Raffson 64d60e5ccf
Datalink support + pydcs update
Support for 2.9.10.4160, though without Iraq, but finally there's some basic support for datalink...
2024-12-16 03:08:40 +01:00

146 lines
5.0 KiB
Python

import re
from typing import Dict, Callable, Union
from PySide6.QtGui import QFocusEvent
from PySide6.QtWidgets import QLineEdit
from dcs.unitpropertydescription import UnitPropertyDescription
from game.ato import Flight
from game.ato.flightmember import FlightMember
from game.radio.datalink import DataLinkKey
class PropertyEditBox(QLineEdit):
def __init__(
self,
flight_member: FlightMember,
prop: UnitPropertyDescription,
flight: Flight,
) -> None:
super().__init__()
self.flight_member = flight_member
self.prop = prop
self.flight = flight
self.setFixedWidth(100)
self.setText(
self.flight_member.properties.get(self.prop.identifier, self.prop.default)
)
self.textChanged.connect(self.on_value_changed)
self._datalink_handlers: Dict[
DataLinkKey,
Union[Callable[[int, str], str], Callable[[int, str, bool], str]],
] = {
DataLinkKey.LINK16: self._link16_input,
DataLinkKey.SADL: self._sadl_input,
DataLinkKey.IDM: self._idm_input,
}
@property
def datalink_type(self) -> DataLinkKey:
return DataLinkKey.from_aircraft_type(self.flight.unit_type)
def focusOutEvent(self, e: QFocusEvent) -> None:
super(PropertyEditBox, self).focusOutEvent(e)
link_type = self.datalink_type
if (handler := self._datalink_handlers.get(link_type)) is not None:
value = handler(0, self.text(), True)
self.setText(value)
def on_value_changed(self, value: str) -> None:
cursor = self.cursorPosition()
link_type = self.datalink_type
if (handler := self._datalink_handlers.get(link_type)) is not None:
value = handler(cursor, value.upper())
if not value and self.prop.identifier in self.flight_member.properties:
del self.flight_member.properties[self.prop.identifier]
else:
self.flight_member.properties[self.prop.identifier] = value
self.setText(value)
self.setCursorPosition(cursor)
if cursor > len(value):
self.setCursorPosition(0)
def _link16_input(self, cursor: int, value: str, focus_lost: bool = False) -> str:
max_input_length = 5
if "VoiceCallsign" in self.prop.identifier:
max_input_length = 2
value = self.common_link16_sadl_logic(
cursor, focus_lost, max_input_length, "STN_L16", value
)
return value
def common_link16_sadl_logic(
self,
cursor: int,
focus_lost: bool,
max_input_length: int,
identifier: str,
value: str,
) -> str:
value = value[:max_input_length]
if not (
("Label" in self.prop.identifier and value.isalpha())
or ("Number" in self.prop.identifier and value.isdigit())
or (identifier == self.prop.identifier and self._is_octal(value))
):
value = self._restore_from_property(cursor)
if focus_lost and 2 < max_input_length > len(value):
# STN_L16 --> prepend with zeroes
value = "0" * (max_input_length - len(value)) + value
return value
def _restore_from_property(self, cursor):
props = self.flight_member.properties
value = props.get(self.prop.identifier, "")[: cursor - 1]
return value
@staticmethod
def _is_octal(value: str) -> bool:
if re.match(r"^[0-7]+$", value):
return True
return False
def _sadl_input(self, cursor: int, value: str, focus_lost: bool = False) -> str:
max_input_length = 4
if "VoiceCallsign" in self.prop.identifier:
max_input_length = 2
value = self.common_link16_sadl_logic(
cursor, focus_lost, max_input_length, "SADL_TN", value
)
return value
def _idm_input(self, cursor: int, value: str, _: bool = False) -> str:
if "TN_IDM_LB" == self.prop.identifier:
value = value[:2] # Originator ID
if len(value) == 1 and not value.isalnum():
return ""
elif len(value) == 2 and not self._valid_2char_idm(value):
value = str(value[0])
else:
value = value[:5] # Ownship CallSign
if not re.match(r"^[A-Z0-9/*\-+.]{,5}$", value):
value = self._restore_from_property(cursor)
return value
@staticmethod
def _valid_2char_idm(value: str) -> bool:
if len(value) != 2:
return False
first = value[0]
second = value[1]
return (
first in ["1", "2"]
and second.isalnum()
or first == "3"
and (re.match(r"^[A-I]$", second) or second.isdigit())
)
def set_flight_member(self, flight_member: FlightMember) -> None:
self.flight_member = flight_member
self.setText(
self.flight_member.properties.get(self.prop.identifier, self.prop.default)
)