diff --git a/gui/src/components/settings_hamlib.vue b/gui/src/components/settings_hamlib.vue index dc83a29b..da8ae52e 100644 --- a/gui/src/components/settings_hamlib.vue +++ b/gui/src/components/settings_hamlib.vue @@ -357,7 +357,7 @@ import { serialDeviceOptions } from "../js/deviceFormHelper"; @change="onChange" v-model.number="settings.remote.RADIO.serial_speed" > - + @@ -380,7 +380,7 @@ import { serialDeviceOptions } from "../js/deviceFormHelper"; @change="onChange" v-model.number="settings.remote.RADIO.data_bits" > - + @@ -395,7 +395,7 @@ import { serialDeviceOptions } from "../js/deviceFormHelper"; @change="onChange" v-model.number="settings.remote.RADIO.stop_bits" > - + diff --git a/modem/helpers.py b/modem/helpers.py index bfdb28f8..8ee0cc8f 100644 --- a/modem/helpers.py +++ b/modem/helpers.py @@ -15,7 +15,9 @@ import hmac import os import sys from pathlib import Path - +import platform +import subprocess +import psutil log = structlog.get_logger("helpers") @@ -701,9 +703,54 @@ def set_flag(byte, flag_name, value, flag_dict): position = flag_dict[flag_name] return set_bit(byte, position, value) + def get_flag(byte, flag_name, flag_dict): """Get the value of the flag from the byte according to the flag dictionary.""" if flag_name not in flag_dict: raise ValueError(f"Unknown flag name: {flag_name}") position = flag_dict[flag_name] return get_bit(byte, position) + + +def find_binary_path(binary_name="rigctld"): + """ + Search for a binary within the current working directory and its subdirectories, + adjusting the binary name for the operating system. + + :param binary_name: The base name of the binary to search for, without extension. + :return: The full path to the binary if found, otherwise None. + """ + # Adjust binary name for Windows + if platform.system() == 'Windows': + binary_name += ".exe" + + root_path = os.getcwd() # Get the current working directory + for dirpath, dirnames, filenames in os.walk(root_path): + if binary_name in filenames: + return os.path.join(dirpath, binary_name) + return None + + +def kill_and_execute(binary_path, additional_args=None): + """ + Kills any running instances of the binary across Linux, macOS, and Windows, then starts a new one non-blocking. + + :param binary_path: The full path to the binary to execute. + :param additional_args: A list of additional arguments to pass to the binary. + :return: subprocess.Popen object of the started process + """ + # Kill any existing instances of the binary + for proc in psutil.process_iter(attrs=['pid', 'name', 'cmdline']): + try: + cmdline = proc.info['cmdline'] + # Ensure cmdline is iterable and not None + if cmdline and binary_path in ' '.join(cmdline): + proc.kill() + print(f"Killed running instance with PID: {proc.info['pid']}") + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + pass # Process no longer exists or no permission to kill + + # Execute the binary with additional arguments non-blocking + command = [binary_path] + (additional_args if additional_args else []) + process = subprocess.Popen(command) + return process \ No newline at end of file diff --git a/modem/radio_manager.py b/modem/radio_manager.py index 5f0c214a..eabdf291 100644 --- a/modem/radio_manager.py +++ b/modem/radio_manager.py @@ -21,7 +21,7 @@ class RadioManager: def _init_rig_control(self): # Check how we want to control the radio if self.radiocontrol == "rigctld": - self.radio = rigctld.radio(self.state_manager, hostname=self.rigctld_ip,port=self.rigctld_port) + self.radio = rigctld.radio(self.config, self.state_manager, hostname=self.rigctld_ip,port=self.rigctld_port) elif self.radiocontrol == "tci": raise NotImplementedError # self.radio = self.tci_module diff --git a/modem/rigctld.py b/modem/rigctld.py index 4e3a9110..2fa82d0b 100644 --- a/modem/rigctld.py +++ b/modem/rigctld.py @@ -1,6 +1,6 @@ import socket import structlog -import time +import helpers import threading class radio: @@ -8,11 +8,12 @@ class radio: log = structlog.get_logger("radio (rigctld)") - def __init__(self, states, hostname="localhost", port=4532, timeout=5): + def __init__(self, config, states, hostname="localhost", port=4532, timeout=5): self.hostname = hostname self.port = port self.timeout = timeout self.states = states + self.config = config self.connection = None self.connected = False @@ -29,6 +30,9 @@ class radio: 'ptt': False # Initial PTT state is set to False } + # start rigctld... + self.start_service() + # connect to radio self.connect() @@ -201,3 +205,49 @@ class radio: """Return the latest fetched parameters.""" return self.parameters + + def start_service(self): + binary_name = "rigctld" + binary_path = helpers.find_binary_path(binary_name) + additional_args = self.format_rigctld_args() + if binary_path: + self.log.info(f"Rigctld binary found at: {binary_path}") + helpers.kill_and_execute(binary_path, additional_args) + self.log.info(f"Executed rigctld...") + else: + self.log.warning("Rigctld binary not found.") + + def format_rigctld_args(self): + config = self.config['RADIO'] # Accessing the 'RADIO' section of the INI file + args = [] + + # Helper function to check if the value should be ignored + def should_ignore(value): + return value == 'ignore' or value == 0 + + # Model ID, Serial Port, and Speed + if not should_ignore(config.get('model_id', "0")): + args += ['-m', str(config['model_id'])] + if not should_ignore(config.get('serial_port', "0")): + args += ['-r', config['serial_port']] + if not should_ignore(config.get('serial_speed', "0")): + args += ['-s', str(config['serial_speed'])] + + # PTT Port and Type + if not should_ignore(config.get('ptt_port', "0")): + args += ['--ptt-port', config['ptt_port']] + if not should_ignore(config.get('ptt_type', "0")): + args += ['--ptt-type', config['ptt_type']] + + # Serial DCD and DTR + if not should_ignore(config.get('serial_dcd', "0")): + args += ['--set-dcd', config['serial_dcd']] + if not should_ignore(config.get('serial_dtr', "0")): + args += ['--set-dtr', config['serial_dtr']] + + # Handling Stop Bits with the corrected --set-conf syntax + if not should_ignore(config.get('stop_bits', "0")): + args += ['--set-conf', f'stop_bits={config["stop_bits"]}'] + + return args +