FreeDATA/freedata_server/rigctld.py

396 lines
14 KiB
Python
Raw Normal View History

import socket
import structlog
2024-02-10 20:28:07 +00:00
import helpers
import threading
2022-05-09 01:27:24 +00:00
class radio:
"""rigctld (hamlib) communication class"""
2022-06-01 00:35:35 +00:00
log = structlog.get_logger("radio (rigctld)")
2024-02-10 20:28:07 +00:00
def __init__(self, config, states, hostname="localhost", port=4532, timeout=5):
self.hostname = hostname
self.port = port
2024-01-12 14:51:23 +00:00
self.timeout = timeout
self.states = states
2024-02-10 20:28:07 +00:00
self.config = config
2024-01-12 14:51:23 +00:00
self.connection = None
self.connected = False
self.await_response = threading.Event()
self.await_response.set()
2024-01-12 14:51:23 +00:00
self.parameters = {
'frequency': '---',
'mode': '---',
'alc': '---',
'strength': '---',
'bandwidth': '---',
'rf': '---',
2024-04-26 09:54:24 +00:00
'ptt': False, # Initial PTT state is set to False,
'tuner': False,
'swr': '---'
2024-01-12 14:51:23 +00:00
}
2024-02-10 20:28:07 +00:00
# start rigctld...
2024-02-11 09:00:57 +00:00
if self.config["RADIO"]["control"] in ["rigctld_bundle"]:
2024-02-11 08:17:52 +00:00
self.start_service()
2024-02-10 20:28:07 +00:00
2024-01-12 14:51:23 +00:00
# connect to radio
self.connect()
def connect(self):
try:
self.connection = socket.create_connection((self.hostname, self.port), timeout=self.timeout)
self.connected = True
2024-04-26 18:50:22 +00:00
self.states.set_radio("radio_status", True)
2024-01-12 14:51:23 +00:00
self.log.info(f"[RIGCTLD] Connected to rigctld at {self.hostname}:{self.port}")
except Exception as err:
self.log.warning(f"[RIGCTLD] Failed to connect to rigctld: {err}")
self.connected = False
2024-04-26 18:50:22 +00:00
self.states.set_radio("radio_status", False)
2024-01-12 14:51:23 +00:00
def disconnect(self):
self.connected = False
if self.connection:
self.connection.close()
2024-01-12 14:51:23 +00:00
del self.connection
self.connection = None
2024-04-26 18:50:22 +00:00
self.states.set_radio("radio_status", False)
2024-01-12 14:51:23 +00:00
self.parameters = {
'frequency': '---',
'mode': '---',
'alc': '---',
'strength': '---',
'bandwidth': '---',
'rf': '---',
2024-04-26 09:54:24 +00:00
'ptt': False, # Initial PTT state is set to False,
'tuner': False,
'swr': '---'
2024-01-12 14:51:23 +00:00
}
def send_command(self, command) -> str:
if self.connected:
# wait if we have another command awaiting its response...
2024-02-07 21:23:49 +00:00
# we need to set a timeout for avoiding a blocking state
self.await_response.wait(timeout=1)
2024-01-12 14:51:23 +00:00
try:
self.await_response = threading.Event()
2024-01-12 14:51:23 +00:00
self.connection.sendall(command.encode('utf-8') + b"\n")
response = self.connection.recv(1024)
self.await_response.set()
2024-02-22 09:26:18 +00:00
stripped_result = response.decode('utf-8').strip()
if 'RPRT' in stripped_result:
return None
return stripped_result
2024-01-12 14:51:23 +00:00
except Exception as err:
self.log.warning(f"[RIGCTLD] Error sending command [{command}] to rigctld: {err}")
2024-01-12 14:51:23 +00:00
self.connected = False
return ""
2022-11-19 08:51:48 +00:00
2024-01-12 14:51:23 +00:00
def set_ptt(self, state):
"""Set the PTT (Push-to-Talk) state.
Args:
2024-01-12 14:51:23 +00:00
state (bool): True to enable PTT, False to disable.
Returns:
2024-01-12 14:51:23 +00:00
bool: True if the PTT state was set successfully, False otherwise.
"""
2024-01-12 14:51:23 +00:00
if self.connected:
try:
if state:
self.send_command('T 1') # Enable PTT
else:
self.send_command('T 0') # Disable PTT
self.parameters['ptt'] = state # Update PTT state in parameters
return True
except Exception as err:
self.log.warning(f"[RIGCTLD] Error setting PTT state: {err}")
self.connected = False
return False
2022-05-09 00:41:49 +00:00
2024-01-12 14:51:23 +00:00
def set_mode(self, mode):
"""Set the mode.
2023-01-04 07:33:25 +00:00
Args:
2024-01-12 14:51:23 +00:00
mode (str): The mode to set.
2023-01-04 07:33:25 +00:00
2024-01-12 14:51:23 +00:00
Returns:
bool: True if the mode was set successfully, False otherwise.
2023-01-04 07:33:25 +00:00
"""
2024-01-12 14:51:23 +00:00
if self.connected:
try:
command = f"M {mode} 0"
2024-01-12 14:51:23 +00:00
self.send_command(command)
self.parameters['mode'] = mode
return True
except Exception as err:
self.log.warning(f"[RIGCTLD] Error setting mode: {err}")
self.connected = False
return False
2024-01-12 14:51:23 +00:00
def set_frequency(self, frequency):
"""Set the frequency.
Args:
2024-01-12 14:51:23 +00:00
frequency (str): The frequency to set.
2024-01-12 14:51:23 +00:00
Returns:
bool: True if the frequency was set successfully, False otherwise.
"""
2024-01-12 14:51:23 +00:00
if self.connected:
try:
2024-01-12 14:51:23 +00:00
command = f"F {frequency}"
self.send_command(command)
self.parameters['frequency'] = frequency
return True
except Exception as err:
self.log.warning(f"[RIGCTLD] Error setting frequency: {err}")
self.connected = False
return False
2022-05-09 00:41:49 +00:00
2024-01-12 14:51:23 +00:00
def set_bandwidth(self, bandwidth):
"""Set the bandwidth.
Args:
2024-01-12 14:51:23 +00:00
bandwidth (str): The bandwidth to set.
Returns:
2024-01-12 14:51:23 +00:00
bool: True if the bandwidth was set successfully, False otherwise.
"""
2024-01-12 14:51:23 +00:00
if self.connected:
try:
command = f"M {self.parameters['mode']} {bandwidth}"
self.send_command(command)
self.parameters['bandwidth'] = bandwidth
return True
except Exception as err:
self.log.warning(f"[RIGCTLD] Error setting bandwidth: {err}")
self.connected = False
return False
2024-04-26 09:54:24 +00:00
def set_rf_level(self, rf):
2024-01-12 14:51:23 +00:00
"""Set the RF.
Args:
2024-01-12 14:51:23 +00:00
rf (str): The RF to set.
Returns:
2024-01-12 14:51:23 +00:00
bool: True if the RF was set successfully, False otherwise.
"""
2024-01-12 14:51:23 +00:00
if self.connected:
try:
command = f"L RFPOWER {rf/100}" #RF RFPOWER --> RFPOWER == IC705
2024-01-12 14:51:23 +00:00
self.send_command(command)
self.parameters['rf'] = rf
return True
except Exception as err:
self.log.warning(f"[RIGCTLD] Error setting RF: {err}")
self.connected = False
return False
2023-01-04 19:12:03 +00:00
2024-04-26 09:54:24 +00:00
def set_tuner(self, state):
"""Set the TUNER state.
Args:
state (bool): True to enable PTT, False to disable.
Returns:
bool: True if the PTT state was set successfully, False otherwise.
"""
if self.connected:
try:
if state:
self.send_command('U TUNER 1') # Enable PTT
else:
self.send_command('U TUNER 0') # Disable PTT
self.parameters['tuner'] = state # Update PTT state in parameters
return True
except Exception as err:
self.log.warning(f"[RIGCTLD] Error setting TUNER state: {err}")
self.connected = False
return False
def get_tuner(self):
"""Set the TUNER state.
Args:
state (bool): True to enable PTT, False to disable.
Returns:
bool: True if the PTT state was set successfully, False otherwise.
"""
if self.connected:
try:
result = self.send_command('u TUNER')
2024-04-26 18:41:20 +00:00
if int(result) == 1:
2024-04-26 18:37:41 +00:00
state = True
else:
state = False
2024-04-26 09:54:24 +00:00
self.parameters['tuner'] = state # Update TUNER state in parameters
return True
except Exception as err:
self.log.warning(f"[RIGCTLD] Error setting TUNER state: {err}")
self.connected = False
return False
2024-01-12 14:51:23 +00:00
def get_parameters(self):
if not self.connected:
self.connect()
2023-01-04 19:12:03 +00:00
2024-01-12 14:51:23 +00:00
if self.connected:
2024-02-22 09:26:18 +00:00
self.get_frequency()
self.get_mode_bandwidth()
self.get_alc()
self.get_strength()
self.get_rf()
2024-04-26 09:54:24 +00:00
self.get_tuner()
self.get_swr()
2024-02-22 09:26:18 +00:00
return self.parameters
def get_frequency(self):
try:
frequency_response = self.send_command('f')
self.parameters['frequency'] = frequency_response if frequency_response is not None else 'err'
except Exception as e:
self.log.warning(f"Error getting frequency: {e}")
self.parameters['frequency'] = 'err'
def get_mode_bandwidth(self):
try:
response = self.send_command('m')
if response is not None:
response = response.strip()
mode, bandwidth = response.split('\n', 1)
else:
mode = 'err'
bandwidth = 'err'
2024-02-22 09:26:18 +00:00
except ValueError:
mode = 'err'
bandwidth = 'err'
except Exception as e:
self.log.warning(f"Error getting mode and bandwidth: {e}")
mode = 'err'
bandwidth = 'err'
finally:
2024-01-12 14:51:23 +00:00
self.parameters['mode'] = mode
self.parameters['bandwidth'] = bandwidth
2023-01-04 19:12:03 +00:00
2024-02-22 09:26:18 +00:00
def get_alc(self):
try:
alc_response = self.send_command('l ALC')
self.parameters['alc'] = alc_response if alc_response is not None else 'err'
except Exception as e:
self.log.warning(f"Error getting ALC: {e}")
self.parameters['alc'] = 'err'
2024-01-12 14:51:23 +00:00
2024-02-22 09:26:18 +00:00
def get_strength(self):
try:
strength_response = self.send_command('l STRENGTH')
self.parameters['strength'] = strength_response if strength_response is not None else 'err'
except Exception as e:
self.log.warning(f"Error getting strength: {e}")
self.parameters['strength'] = 'err'
def get_rf(self):
try:
rf_response = self.send_command('l RFPOWER')
if rf_response is not None:
self.parameters['rf'] = int(float(rf_response) * 100)
else:
self.parameters['rf'] = 'err'
except ValueError:
self.parameters['rf'] = 'err'
except Exception as e:
self.log.warning(f"Error getting RF power: {e}")
self.parameters['rf'] = 'err'
2024-02-10 20:28:07 +00:00
2024-04-26 09:54:24 +00:00
def get_swr(self):
try:
rf_response = self.send_command('l SWR')
if rf_response is not None:
self.parameters['swr'] = rf_response
else:
self.parameters['swr'] = 'err'
except ValueError:
self.parameters['swr'] = 'err'
except Exception as e:
self.log.warning(f"Error getting SWR: {e}")
self.parameters['swr'] = 'err'
2024-02-10 20:28:07 +00:00
def start_service(self):
binary_name = "rigctld"
2024-02-22 09:49:30 +00:00
binary_paths = helpers.find_binary_paths(binary_name, search_system_wide=True)
2024-02-10 20:28:07 +00:00
additional_args = self.format_rigctld_args()
2024-02-22 09:49:30 +00:00
if binary_paths:
for binary_path in binary_paths:
try:
self.log.info(f"Attempting to start rigctld using binary found at: {binary_path}")
helpers.kill_and_execute(binary_path, additional_args)
self.log.info("Successfully executed rigctld.")
break # Exit the loop after successful execution
except Exception as e:
pass
# let's keep this hidden for the user to avoid confusion
# self.log.warning(f"Failed to start rigctld with binary at {binary_path}: {e}")
else:
self.log.warning("Failed to start rigctld with all found binaries.", binaries=binary_paths)
2024-02-10 20:28:07 +00:00
else:
self.log.warning("Rigctld binary not found.")
2024-02-22 09:49:30 +00:00
2024-02-10 20:28:07 +00:00
def format_rigctld_args(self):
config = self.config['RADIO'] # Accessing the 'RADIO' section of the INI file
config_rigctld = self.config['RIGCTLD'] # Accessing the 'RIGCTLD' section of the INI file for custom args
2024-02-10 20:28:07 +00:00
args = []
# Helper function to check if the value should be ignored
def should_ignore(value):
2024-02-11 07:48:49 +00:00
return value in ['ignore', 0]
2024-02-10 20:28:07 +00:00
# Model ID, Serial Port, and Speed
2024-02-14 15:29:18 +00:00
if not should_ignore(config.get('model_id')):
2024-02-10 20:28:07 +00:00
args += ['-m', str(config['model_id'])]
2024-02-14 15:29:18 +00:00
if not should_ignore(config.get('serial_port')):
2024-02-10 20:28:07 +00:00
args += ['-r', config['serial_port']]
2024-02-14 15:29:18 +00:00
if not should_ignore(config.get('serial_speed')):
2024-02-10 20:28:07 +00:00
args += ['-s', str(config['serial_speed'])]
# PTT Port and Type
2024-02-14 15:29:18 +00:00
if not should_ignore(config.get('ptt_port')):
2024-03-17 11:04:58 +00:00
args += ['-p', config['ptt_port']]
2024-02-14 15:29:18 +00:00
if not should_ignore(config.get('ptt_type')):
2024-03-17 11:04:58 +00:00
args += ['-P', config['ptt_type']]
2024-02-10 20:28:07 +00:00
# Serial DCD and DTR
2024-02-14 15:29:18 +00:00
if not should_ignore(config.get('serial_dcd')):
2024-03-17 11:04:58 +00:00
args += ['-D', config['serial_dcd']]
2024-02-14 15:29:18 +00:00
if not should_ignore(config.get('serial_dtr')):
2024-02-14 15:20:00 +00:00
args += ['--set-conf', f'dtr_state={config["serial_dtr"]}']
2024-02-10 20:28:07 +00:00
2024-02-14 15:20:00 +00:00
# Handling Data Bits and Stop Bits
2024-02-14 15:29:18 +00:00
if not should_ignore(config.get('data_bits')):
2024-02-14 15:20:00 +00:00
args += ['--set-conf', f'data_bits={config["data_bits"]}']
2024-02-14 15:29:18 +00:00
if not should_ignore(config.get('stop_bits')):
2024-02-10 20:28:07 +00:00
args += ['--set-conf', f'stop_bits={config["stop_bits"]}']
2024-02-14 15:20:00 +00:00
# Fixme #rts_state
2024-02-17 16:03:59 +00:00
# if not should_ignore(config.get('rts_state')):
# args += ['--set-conf', f'stop_bits={config["rts_state"]}']
2024-02-14 15:20:00 +00:00
# Handle custom arguments for rigctld
# Custom args are split via ' ' so python doesn't add extranaeous quotes on windows
args += config_rigctld["arguments"].split(" ")
2024-03-17 11:04:58 +00:00
print("Hamlib args ==>" + str(args))
2024-02-10 20:28:07 +00:00
return args