Skip to content

Commit 70c5124

Browse files
committed
update requirements.txt, add DCS IPC listener, TelemManager refactoring, some code cleanups, fix some pylint warnings
1 parent 1ffa78f commit 70c5124

12 files changed

Lines changed: 608 additions & 350 deletions

.pylintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ const-naming-style=UPPER_CASE
177177
docstring-min-length=-1
178178

179179
# Naming style matching correct function names.
180-
function-naming-style=snake_case
180+
function-naming-style=snake_case,camelCase
181181

182182
# Regular expression matching correct function names. Overrides function-
183183
# naming-style. If left empty, function names will be checked with the set

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pyqt6
22
configobj
33
pysimconnect @ https://github.com/walmis/pysimconnect/archive/refs/heads/master.zip
4+
libipc_ctypes @ https://github.com/walmis/libipc_ctypes/archive/refs/heads/master.zip
45
stransi
56
libusb1
67
pygetwindow

telemffb/globals.py

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#
1818

1919

20-
from typing import TYPE_CHECKING, Dict
20+
from typing import TYPE_CHECKING, Dict, Optional, Any
2121
if TYPE_CHECKING:
2222
# from PyQt5.QtCore import QSettings
2323
from .LogWindow import LogWindow
@@ -29,61 +29,61 @@
2929
from telemffb.MainWindow import MainWindow
3030
from subprocess import Popen
3131
from telemffb.CmdLineArgs import CmdLineArgs
32+
from telemffb.ConfiguratorDialog import ConfiguratorDialog
3233

34+
# Application state
3335
is_exe: bool = False
36+
args : 'CmdLineArgs'
3437

35-
settings_mgr : 'SettingsWindow' = None
36-
userconfig_rootpath = None
37-
userconfig_path = None
38-
defaults_path = None
39-
defaults_path = None
40-
current_vpconf_profile = None
41-
42-
# main window instance
43-
main_window : 'MainWindow' = None
44-
45-
device_type : str = None
46-
device_usbpid : str = None
47-
device_usbvidpid : str = None # "FFFF:2055"
48-
device_ident : str = None #Joystick, Pedals, etc.. as set in configurator
49-
50-
launched_instances : Dict[str, 'ChildPopen'] = {}
51-
instance_dev_dict : dict = {}
52-
master_instance : bool = False
53-
ipc_instance : 'IPCNetworkThread' = None
54-
child_instance : bool = None
55-
active_buttons: list = []
56-
master_buttons: list = []
57-
child_buttons: dict = {}
58-
59-
force_reload_aircraft_trigger: bool = False
60-
61-
current_device_config_scope: str = None # add current device config scope to globals for tracking across telemffb modules
62-
63-
# systems settings
64-
system_settings : 'SystemSettings' = None
65-
66-
#parsed startup arguments
67-
args : 'CmdLineArgs' = None
68-
69-
# telemetry manager instance
70-
telem_manager : 'TelemManager' = None
71-
72-
# configurator gains read at startup
73-
startup_configurator_gains = None # Gain object direct from 'device.get_gains'. Gains get read at TelemFFB startup fallback baseline values.
74-
vpconf_configurator_gains = None # Gain object direct from 'device.get_gains'. Updated every time a configurator profile is pushed to the device to use as revert data
75-
current_configurator_gains = None # Gain settings table set by gain override dialog. Updated when gains set/saved in dialog or read from config
76-
gain_override_dialog = None
77-
78-
sim_listeners : 'SimListenerManager' = None
79-
80-
log_window : 'LogWindow' = None
81-
82-
vpf_logo: str = ":/image/vpforcelogo.png"
38+
# Version and build configuration
8339
release_version : bool = False
8440
release_version_str: str = "Vx.x.x"
8541
dev_build : bool = False # when True, build versions will use 'dev_build_str' as the version string and will not look for updates
8642
dev_userconfig: bool = True # will use/create userconfig.xml in root when True (dev_build must also be true)
8743
dev_build_str: str = "DEV_BUILD"
44+
vpf_logo: str = ":/image/vpforcelogo.png"
45+
46+
# UI components
47+
main_window : 'MainWindow'
48+
settings_mgr : 'SettingsWindow'
49+
log_window : 'LogWindow'
50+
useDarkMode : bool = False
51+
52+
# Configuration paths and profiles
53+
userconfig_rootpath : str = ""
54+
userconfig_path : str = ""
55+
defaults_path : str = ""
56+
current_vpconf_profile : Optional[str] = None
57+
current_device_config_scope: Optional[str] = None # add current device config scope to globals for tracking across telemffb modules
58+
59+
# Device information
60+
device_type : str = ""
61+
device_usbpid : str
62+
device_usbvidpid : str # "FFFF:2055"
63+
device_ident : str #Joystick, Pedals, etc.. as set in configurator
64+
65+
# Gain management
66+
startup_configurator_gains: Optional[Any] = None # Gain object direct from 'device.get_gains'. Gains get read at TelemFFB startup fallback baseline values.
67+
vpconf_configurator_gains: Optional[Any] = None # Gain object direct from 'device.get_gains'. Updated every time a configurator profile is pushed to the device to use as revert data
68+
current_configurator_gains: Optional[Any] = None # Gain settings table set by gain override dialog. Updated when gains set/saved in dialog or read from config
69+
gain_override_dialog: 'ConfiguratorDialog'
70+
71+
# Instance management
72+
launched_instances : Dict[str, 'ChildPopen'] = {}
73+
instance_dev_dict : Dict[str, Any] = {}
74+
master_instance : bool = False
75+
child_instance : bool = False
76+
ipc_instance : 'IPCNetworkThread'
77+
78+
# Button management
79+
active_buttons: list[Any] = []
80+
master_buttons: list[Any] = []
81+
child_buttons: Dict[str, Any] = {}
82+
83+
# System components
84+
system_settings : 'SystemSettings'
85+
telem_manager : 'TelemManager'
86+
sim_listeners : 'SimListenerManager'
8887

89-
useDarkMode : bool = False
88+
# Triggers and flags
89+
force_reload_aircraft_trigger: bool = False

telemffb/sim/aircraft_base.py

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@
3232

3333
# by accessing effects dict directly new effects will be automatically allocated
3434
# example: effects["myUniqueName"]
35-
effects: Dict[str, HapticEffect] = utils.Dispenser(HapticEffect)
35+
effects: utils.Dispenser = utils.Dispenser(HapticEffect)
3636

3737
# Highpass filter dispenser
38-
HPFs: Dict[str, utils.HighPassFilter] = utils.Dispenser(utils.HighPassFilter)
38+
HPFs: utils.Dispenser = utils.Dispenser(utils.HighPassFilter)
3939

4040
# Lowpass filter dispenser
41-
LPFs: Dict[str, utils.LowPassFilter] = utils.Dispenser(utils.LowPassFilter)
41+
LPFs: utils.Dispenser = utils.Dispenser(utils.LowPassFilter)
4242

4343
perftracker = utils.PerformanceTracker()
4444

@@ -332,9 +332,6 @@ def __init__(self, name: str, **kwargs):
332332

333333
self.friction_effect_overridden: bool = False
334334

335-
self.friction_effect_overridden: bool = False
336-
337-
self.friction_effect_overridden: bool = False
338335

339336
def step_value_over_time(self, key, value, timeframe_ms, dst_val, floatpoint=False):
340337
'''
@@ -399,6 +396,14 @@ def step_value_over_time(self, key, value, timeframe_ms, dst_val, floatpoint=Fal
399396
return data['value']
400397

401398
def apply_settings(self, settings_dict):
399+
"""Apply settings from a configuration dictionary to the aircraft instance.
400+
401+
Args:
402+
settings_dict (dict): Dictionary containing configuration key-value pairs.
403+
Keys should match aircraft attribute names.
404+
405+
Logs warnings for unknown parameters and info for each applied setting.
406+
"""
402407
for k, v in settings_dict.items():
403408
if k in ["type"]: continue
404409
if getattr(self, k, None) is None and k != 'vpconf' and 'dummy' not in k and 'command_runner' not in k:
@@ -408,6 +413,17 @@ def apply_settings(self, settings_dict):
408413
setattr(self, k, v)
409414

410415
def has_changed(self, item: str, delta_ms=0, data=None) -> bool:
416+
"""Check if a telemetry data item has changed since last call.
417+
418+
Args:
419+
item (str): Name of the telemetry data item to check
420+
delta_ms (int, optional): Time window in milliseconds to consider as "recently changed". Defaults to 0.
421+
data (dict, optional): Telemetry data dictionary to use. Defaults to self._telem_data.
422+
423+
Returns:
424+
bool: True if the item changed, False otherwise.
425+
If the item changed, returns a tuple (prev_val, new_val) instead.
426+
"""
411427
if data == None:
412428
data = self._telem_data
413429

@@ -430,21 +446,46 @@ def has_changed(self, item: str, delta_ms=0, data=None) -> bool:
430446
return False
431447

432448
def flag_error(self, message):
449+
"""Flag an error message for display in the UI.
450+
451+
Args:
452+
message (str): Error message to display
453+
"""
433454
dev = self.telem_data.get('FFBType', 'joystick').capitalize()
434455
self.telem_data['error'] = message
435456
if not master_instance:
436457
self._ipc_telem['error'] = f"{dev}: {message}"
437458

438459
def is_joystick(self):
460+
"""Check if the current FFB device is a joystick.
461+
462+
Returns:
463+
bool: True if device is a joystick, False otherwise
464+
"""
439465
return self._telem_data.get("FFBType", "joystick") == "joystick"
440466

441467
def is_pedals(self):
468+
"""Check if the current FFB device is pedals.
469+
470+
Returns:
471+
bool: True if device is pedals, False otherwise
472+
"""
442473
return self._telem_data.get("FFBType") == "pedals"
443474

444475
def is_collective(self):
476+
"""Check if the current FFB device is a collective.
477+
478+
Returns:
479+
bool: True if device is a collective, False otherwise
480+
"""
445481
return self._telem_data.get("FFBType") == "collective"
446482

447483
def is_trimwheel(self):
484+
"""Check if the current FFB device is a trim wheel.
485+
486+
Returns:
487+
bool: True if device is a trim wheel, False otherwise
488+
"""
448489
return self._telem_data.get("FFBType") == "trimwheel"
449490

450491

@@ -479,23 +520,47 @@ def anything_has_changed(self, item: str, value, delta_ms=0):
479520
return False
480521

481522
def _sim_is_msfs(self, *unused):
523+
"""Check if the current simulator is Microsoft Flight Simulator.
524+
525+
Returns:
526+
int: 1 if MSFS, 0 otherwise
527+
"""
482528
if self._telem_data.get("src") == "MSFS":
483529
return 1
484530
else:
485531
return 0
486532

487533
def _sim_is_xplane(self):
534+
"""Check if the current simulator is X-Plane.
535+
536+
Returns:
537+
bool: True if X-Plane, False otherwise
538+
"""
488539
if self._telem_data.get('src') == "XPLANE":
489540
return True
490541
else:
491542
return False
492543

493544
def _sim_is_dcs(self, *unused):
545+
"""Check if the current simulator is DCS World.
546+
547+
Returns:
548+
int: 1 if DCS, 0 otherwise
549+
"""
494550
if self._telem_data.get("src") == "DCS":
495551
return 1
496552
else:
497553
return 0
554+
498555
def _sim_is(self, sim, *unused):
556+
"""Check if the current simulator matches the specified name.
557+
558+
Args:
559+
sim (str): Simulator name to check against
560+
561+
Returns:
562+
int: 1 if matches, 0 otherwise
563+
"""
499564
if self._telem_data.get('src') == sim:
500565
return 1
501566
else:
@@ -569,6 +634,15 @@ def _update_runway_rumble(self, telem_data):
569634
effects.dispose("runway1")
570635

571636
def new_gforce_effect(self, telem_data):
637+
"""Apply new G-force effects based on aircraft acceleration.
638+
639+
Generates force feedback effects that vary with G-forces experienced by the aircraft.
640+
The effect strength is modulated by stick deflection and can handle both positive
641+
and negative G-forces if configured.
642+
643+
Args:
644+
telem_data (dict): Telemetry data containing acceleration information
645+
"""
572646
if not self.is_joystick() or not self.new_gforce_effect_enable or not self.gforce_effect_master:
573647
effects.dispose("new_gforce")
574648
return
@@ -1960,13 +2034,14 @@ def set_deadzone(self):
19602034

19612035

19622036

1963-
def on_event(self):
2037+
def on_event(self, event, *args):
19642038
pass
19652039

19662040
def on_timeout(self): # override me
19672041
logging.info("Telemetry Timeout, stopping effects")
19682042
# effects.foreach(lambda e: e.stop())
19692043
for key, effect in effects.dict.items():
2044+
effect: HapticEffect
19702045
if self.keep_forces_on_pause:
19712046
if effect.effect_type in [EFFECT_SPRING, EFFECT_DAMPER, EFFECT_INERTIA, EFFECT_FRICTION, EFFECT_SPRING_ADJUSTER]:
19722047
continue

telemffb/sim/aircrafts_dcs.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
from telemffb.hw.ffb_rhino import (EFFECT_SINE, EFFECT_SQUARE, EFFECT_TRIANGLE, EFFECT_SAWTOOTHUP, EFFECT_SAWTOOTHDOWN, HapticEffect)
4747

4848
from telemffb.sim.aircraft_base import AircraftBase, LPFs, effects, perftracker
49-
49+
from telemffb.telem.DcsIpcThread import DcsIpcThread
5050
#unit conversions (to m/s)
5151
knots = 0.514444
5252
kmh = 1.0/3.6
@@ -231,10 +231,7 @@ def on_timeout(self):
231231

232232
def send_commands(self, cmds):
233233
cmds = "\n".join(cmds)
234-
if not getattr(self, "_socket", None):
235-
self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
236-
237-
self._socket.sendto(bytes(cmds, "utf-8"), ("127.0.0.1", 34381))
234+
DcsIpcThread.send_commands(cmds)
238235

239236
def _update_damage(self, telem_data):
240237
if not self.damage_effect_enabled: return

0 commit comments

Comments
 (0)