2023-11-15 22:34:26 +00:00
|
|
|
import time
|
2023-11-22 20:54:50 +00:00
|
|
|
import threading
|
2023-12-01 16:02:42 +00:00
|
|
|
import numpy as np
|
2023-11-22 17:05:31 +00:00
|
|
|
class StateManager:
|
2023-11-11 19:01:15 +00:00
|
|
|
def __init__(self, statequeue):
|
2023-11-17 09:22:45 +00:00
|
|
|
|
|
|
|
# state related settings
|
2023-11-11 19:01:15 +00:00
|
|
|
self.statequeue = statequeue
|
2023-11-14 18:26:11 +00:00
|
|
|
self.newstate = None
|
2024-04-26 13:30:56 +00:00
|
|
|
self.newradio = None
|
2023-11-17 09:22:45 +00:00
|
|
|
self.last = time.time()
|
|
|
|
|
2024-04-18 09:04:25 +00:00
|
|
|
# freedata_server related states
|
2023-11-17 09:22:45 +00:00
|
|
|
# not every state is needed to publish, yet
|
|
|
|
# TODO can we reduce them?
|
2023-11-11 19:01:15 +00:00
|
|
|
self.channel_busy_slot = [False, False, False, False, False]
|
2023-12-30 20:47:16 +00:00
|
|
|
self.channel_busy_event = threading.Event()
|
|
|
|
self.channel_busy_condition_traffic = threading.Event()
|
|
|
|
self.channel_busy_condition_codec2 = threading.Event()
|
|
|
|
|
2023-11-12 18:56:15 +00:00
|
|
|
self.is_modem_running = False
|
2024-02-02 18:37:02 +00:00
|
|
|
|
|
|
|
self.is_modem_busy = threading.Event()
|
|
|
|
self.setARQ(False)
|
|
|
|
|
2023-11-12 22:22:53 +00:00
|
|
|
self.is_beacon_running = False
|
2024-04-13 08:56:11 +00:00
|
|
|
self.is_away_from_key = False
|
2023-11-22 20:54:50 +00:00
|
|
|
|
|
|
|
# If true, any wait() call is blocking
|
|
|
|
self.transmitting_event = threading.Event()
|
|
|
|
self.setTransmitting(False)
|
|
|
|
|
2023-11-13 17:11:55 +00:00
|
|
|
self.audio_dbfs = 0
|
2023-11-17 09:58:07 +00:00
|
|
|
self.dxcallsign: bytes = b"ZZ9YY-0"
|
|
|
|
self.dxgrid: bytes = b"------"
|
2023-12-01 15:39:52 +00:00
|
|
|
|
2024-02-02 18:37:02 +00:00
|
|
|
self.heard_stations = []
|
2023-12-01 15:39:52 +00:00
|
|
|
self.activities_list = {}
|
2023-11-12 22:22:53 +00:00
|
|
|
|
2023-12-05 14:40:04 +00:00
|
|
|
self.arq_iss_sessions = {}
|
|
|
|
self.arq_irs_sessions = {}
|
2023-11-17 21:45:10 +00:00
|
|
|
|
2024-03-09 09:47:27 +00:00
|
|
|
self.p2p_connection_sessions = {}
|
|
|
|
|
2024-02-02 18:37:02 +00:00
|
|
|
#self.mesh_routing_table = []
|
2023-11-17 22:33:30 +00:00
|
|
|
|
2023-11-17 09:37:50 +00:00
|
|
|
self.radio_frequency = 0
|
|
|
|
self.radio_mode = None
|
|
|
|
self.radio_bandwidth = 0
|
2024-01-12 19:21:22 +00:00
|
|
|
self.radio_rf_level = 0
|
2024-01-12 19:34:07 +00:00
|
|
|
self.s_meter_strength = 0
|
2024-04-26 09:54:24 +00:00
|
|
|
self.radio_tuner = False
|
|
|
|
self.radio_swr = 0
|
2023-11-18 22:50:40 +00:00
|
|
|
# Set rig control status regardless or rig control method
|
|
|
|
self.radio_status = False
|
2023-11-17 09:37:50 +00:00
|
|
|
|
2024-04-26 19:14:53 +00:00
|
|
|
def sendState(self):
|
2023-11-26 11:45:51 +00:00
|
|
|
currentState = self.get_state_event(False)
|
2023-11-17 21:35:52 +00:00
|
|
|
self.statequeue.put(currentState)
|
|
|
|
return currentState
|
|
|
|
|
2024-04-26 19:14:53 +00:00
|
|
|
def sendStateUpdate(self, state):
|
2024-04-26 19:06:21 +00:00
|
|
|
self.statequeue.put(state)
|
2023-11-12 22:22:53 +00:00
|
|
|
|
2023-11-11 19:01:15 +00:00
|
|
|
def set(self, key, value):
|
|
|
|
setattr(self, key, value)
|
2023-11-18 22:50:40 +00:00
|
|
|
#print(f"State ==> Setting {key} to value {value}")
|
2023-11-14 18:26:11 +00:00
|
|
|
# only process data if changed
|
2023-11-26 11:45:51 +00:00
|
|
|
new_state = self.get_state_event(True)
|
2023-11-17 01:39:04 +00:00
|
|
|
if new_state != self.newstate:
|
2023-11-14 18:26:11 +00:00
|
|
|
self.newstate = new_state
|
2024-04-26 19:06:21 +00:00
|
|
|
self.sendStateUpdate(new_state)
|
2023-11-28 22:29:01 +00:00
|
|
|
|
2024-04-26 13:15:23 +00:00
|
|
|
def set_radio(self, key, value):
|
|
|
|
setattr(self, key, value)
|
|
|
|
#print(f"State ==> Setting {key} to value {value}")
|
|
|
|
# only process data if changed
|
|
|
|
new_radio = self.get_radio_event(True)
|
2024-04-26 13:30:56 +00:00
|
|
|
if new_radio != self.newradio:
|
|
|
|
self.newradio = new_radio
|
2024-04-26 19:06:21 +00:00
|
|
|
self.sendStateUpdate(new_radio)
|
2024-04-26 13:15:23 +00:00
|
|
|
|
2023-11-28 22:29:01 +00:00
|
|
|
def set_channel_slot_busy(self, array):
|
|
|
|
for i in range(0,len(array),1):
|
|
|
|
if not array[i] == self.channel_busy_slot[i]:
|
|
|
|
self.channel_busy_slot = array
|
|
|
|
self.newstate = self.get_state_event(True)
|
2024-04-26 19:06:21 +00:00
|
|
|
self.sendStateUpdate(self.newstate)
|
2023-11-28 22:29:01 +00:00
|
|
|
continue
|
|
|
|
|
2023-11-26 11:45:51 +00:00
|
|
|
def get_state_event(self, isChangedState):
|
2023-11-17 21:35:52 +00:00
|
|
|
msgtype = "state-change"
|
|
|
|
if (not isChangedState):
|
|
|
|
msgtype = "state"
|
2023-11-11 19:01:15 +00:00
|
|
|
|
2023-11-26 11:45:51 +00:00
|
|
|
return {
|
2024-01-04 14:46:58 +00:00
|
|
|
"type": msgtype,
|
2023-11-12 18:56:15 +00:00
|
|
|
"is_modem_running": self.is_modem_running,
|
2023-11-12 22:22:53 +00:00
|
|
|
"is_beacon_running": self.is_beacon_running,
|
2024-04-13 08:56:11 +00:00
|
|
|
"is_away_from_key": self.is_away_from_key,
|
2023-11-18 22:50:40 +00:00
|
|
|
"radio_status": self.radio_status,
|
2023-11-28 22:29:01 +00:00
|
|
|
"channel_busy_slot": self.channel_busy_slot,
|
2024-05-11 11:30:48 +00:00
|
|
|
"is_codec2_traffic": self.is_receiving_codec2_signal(),
|
2023-11-28 22:46:55 +00:00
|
|
|
"audio_dbfs": self.audio_dbfs,
|
2023-12-01 15:39:52 +00:00
|
|
|
"activities": self.activities_list,
|
2024-02-02 18:37:02 +00:00
|
|
|
"is_modem_busy" : self.getARQ()
|
2023-11-26 11:45:51 +00:00
|
|
|
}
|
2024-04-26 13:15:23 +00:00
|
|
|
|
|
|
|
def get_radio_event(self, isChangedState):
|
2024-04-26 13:20:37 +00:00
|
|
|
msgtype = "radio-change"
|
2024-04-26 13:15:23 +00:00
|
|
|
if (not isChangedState):
|
|
|
|
msgtype = "radio"
|
|
|
|
|
|
|
|
return {
|
|
|
|
"type": msgtype,
|
|
|
|
"radio_status": self.radio_status,
|
|
|
|
"radio_frequency": self.radio_frequency,
|
|
|
|
"radio_mode": self.radio_mode,
|
|
|
|
"s_meter_strength": self.s_meter_strength,
|
2024-04-26 13:20:37 +00:00
|
|
|
"radio_swr" : self.radio_swr,
|
|
|
|
"radio_tuner": self.radio_tuner,
|
2024-04-26 13:15:23 +00:00
|
|
|
}
|
2023-11-22 20:54:50 +00:00
|
|
|
|
|
|
|
# .wait() blocks until the event is set
|
|
|
|
def isTransmitting(self):
|
|
|
|
return not self.transmitting_event.is_set()
|
|
|
|
|
|
|
|
# .wait() blocks until the event is set
|
|
|
|
def setTransmitting(self, transmitting: bool):
|
|
|
|
if transmitting:
|
|
|
|
self.transmitting_event.clear()
|
|
|
|
else:
|
|
|
|
self.transmitting_event.set()
|
|
|
|
|
2024-02-02 18:37:02 +00:00
|
|
|
def setARQ(self, busy):
|
|
|
|
if busy:
|
|
|
|
self.is_modem_busy.clear()
|
|
|
|
else:
|
|
|
|
self.is_modem_busy.set()
|
|
|
|
|
|
|
|
def getARQ(self):
|
|
|
|
return not self.is_modem_busy.is_set()
|
|
|
|
|
2023-11-22 20:54:50 +00:00
|
|
|
def waitForTransmission(self):
|
|
|
|
self.transmitting_event.wait()
|
2023-11-23 22:10:53 +00:00
|
|
|
|
2023-12-30 21:44:18 +00:00
|
|
|
def waitForChannelBusy(self):
|
|
|
|
self.channel_busy_event.wait(2)
|
|
|
|
|
2023-12-05 14:40:04 +00:00
|
|
|
def register_arq_iss_session(self, session):
|
|
|
|
if session.id in self.arq_iss_sessions:
|
2024-01-04 20:44:59 +00:00
|
|
|
return False
|
2023-12-05 14:40:04 +00:00
|
|
|
self.arq_iss_sessions[session.id] = session
|
2024-01-04 20:44:59 +00:00
|
|
|
return True
|
2023-12-05 14:40:04 +00:00
|
|
|
|
2023-12-12 19:46:22 +00:00
|
|
|
def register_arq_irs_session(self, session):
|
2023-12-05 14:40:04 +00:00
|
|
|
if session.id in self.arq_irs_sessions:
|
2024-01-04 20:44:59 +00:00
|
|
|
return False
|
2023-12-05 14:40:04 +00:00
|
|
|
self.arq_irs_sessions[session.id] = session
|
2024-01-04 20:44:59 +00:00
|
|
|
return True
|
2023-12-05 14:40:04 +00:00
|
|
|
|
2024-01-15 10:16:33 +00:00
|
|
|
def check_if_running_arq_session(self, irs=False):
|
|
|
|
sessions = self.arq_irs_sessions if irs else self.arq_iss_sessions
|
2024-01-15 15:04:11 +00:00
|
|
|
|
|
|
|
for session_id in sessions:
|
|
|
|
# do a session cleanup of outdated sessions before
|
|
|
|
if sessions[session_id].is_session_outdated():
|
|
|
|
print(f"session cleanup.....{session_id}")
|
|
|
|
if irs:
|
|
|
|
self.remove_arq_irs_session(session_id)
|
|
|
|
else:
|
|
|
|
self.remove_arq_iss_session(session_id)
|
2024-01-17 10:06:33 +00:00
|
|
|
|
|
|
|
# check again if session id exists in session because of cleanup
|
|
|
|
if session_id in sessions and sessions[session_id].state.name not in ['ENDED', 'ABORTED', 'FAILED']:
|
2024-01-15 15:04:11 +00:00
|
|
|
print(f"[State Manager] running session...[{session_id}]")
|
2024-01-15 10:16:33 +00:00
|
|
|
return True
|
2024-01-17 10:06:33 +00:00
|
|
|
return False
|
2024-01-15 10:16:33 +00:00
|
|
|
return False
|
|
|
|
|
2023-12-05 14:40:04 +00:00
|
|
|
def get_arq_iss_session(self, id):
|
|
|
|
if id not in self.arq_iss_sessions:
|
2023-12-24 12:27:24 +00:00
|
|
|
#raise RuntimeError(f"ARQ ISS Session '{id}' not found!")
|
|
|
|
# DJ2LS: WIP We need to find a better way of handling this
|
2023-12-24 12:29:54 +00:00
|
|
|
pass
|
2023-12-05 18:12:21 +00:00
|
|
|
return self.arq_iss_sessions[id]
|
2023-12-05 14:40:04 +00:00
|
|
|
|
|
|
|
def get_arq_irs_session(self, id):
|
|
|
|
if id not in self.arq_irs_sessions:
|
2023-12-24 12:27:24 +00:00
|
|
|
#raise RuntimeError(f"ARQ IRS Session '{id}' not found!")
|
|
|
|
# DJ2LS: WIP We need to find a better way of handling this
|
2023-12-24 12:29:54 +00:00
|
|
|
pass
|
2023-12-05 18:12:21 +00:00
|
|
|
return self.arq_irs_sessions[id]
|
2023-12-05 14:40:04 +00:00
|
|
|
|
|
|
|
def remove_arq_iss_session(self, id):
|
2024-01-15 15:04:11 +00:00
|
|
|
if id in self.arq_iss_sessions:
|
|
|
|
del self.arq_iss_sessions[id]
|
2023-12-05 14:40:04 +00:00
|
|
|
|
|
|
|
def remove_arq_irs_session(self, id):
|
2024-01-15 15:04:11 +00:00
|
|
|
if id in self.arq_irs_sessions:
|
|
|
|
del self.arq_irs_sessions[id]
|
2023-11-23 22:10:53 +00:00
|
|
|
|
2023-12-01 15:39:52 +00:00
|
|
|
def add_activity(self, activity_data):
|
|
|
|
# Generate a random 8-byte string as hex
|
|
|
|
activity_id = np.random.bytes(8).hex()
|
|
|
|
|
|
|
|
# if timestamp not provided, add it here
|
|
|
|
if 'timestamp' not in activity_data:
|
|
|
|
activity_data['timestamp'] = int(time.time())
|
|
|
|
|
|
|
|
# if frequency not provided, add it here
|
|
|
|
if 'frequency' not in activity_data:
|
|
|
|
activity_data['frequency'] = self.radio_frequency
|
|
|
|
self.activities_list[activity_id] = activity_data
|
2024-04-26 19:06:21 +00:00
|
|
|
self.sendStateUpdate(self.newstate)
|
2023-12-30 20:47:16 +00:00
|
|
|
|
|
|
|
def calculate_channel_busy_state(self):
|
|
|
|
if self.channel_busy_condition_traffic.is_set() and self.channel_busy_condition_codec2.is_set():
|
|
|
|
self.channel_busy_event.set()
|
|
|
|
else:
|
|
|
|
self.channel_busy_event = threading.Event()
|
|
|
|
|
|
|
|
def set_channel_busy_condition_traffic(self, busy):
|
|
|
|
if not busy:
|
|
|
|
self.channel_busy_condition_traffic.set()
|
|
|
|
else:
|
|
|
|
self.channel_busy_condition_traffic = threading.Event()
|
|
|
|
self.calculate_channel_busy_state()
|
|
|
|
|
|
|
|
def set_channel_busy_condition_codec2(self, traffic):
|
|
|
|
if not traffic:
|
|
|
|
self.channel_busy_condition_codec2.set()
|
|
|
|
else:
|
|
|
|
self.channel_busy_condition_codec2 = threading.Event()
|
|
|
|
self.calculate_channel_busy_state()
|
2024-01-12 15:29:22 +00:00
|
|
|
|
2024-05-11 11:30:48 +00:00
|
|
|
def is_receiving_codec2_signal(self):
|
|
|
|
return not self.channel_busy_condition_codec2.is_set()
|
|
|
|
|
2024-01-12 15:29:22 +00:00
|
|
|
def get_radio_status(self):
|
|
|
|
return {
|
|
|
|
"radio_status": self.radio_status,
|
|
|
|
"radio_frequency": self.radio_frequency,
|
|
|
|
"radio_mode": self.radio_mode,
|
2024-01-12 19:34:07 +00:00
|
|
|
"radio_rf_level": self.radio_rf_level,
|
|
|
|
"s_meter_strength": self.s_meter_strength,
|
2024-04-26 09:54:24 +00:00
|
|
|
"radio_swr": self.radio_swr,
|
|
|
|
"radio_tuner": self.radio_tuner
|
2024-02-02 18:37:02 +00:00
|
|
|
}
|
2024-03-09 09:47:27 +00:00
|
|
|
|
|
|
|
def register_p2p_connection_session(self, session):
|
|
|
|
if session.session_id in self.p2p_connection_sessions:
|
2024-03-19 12:19:13 +00:00
|
|
|
print("session already registered...")
|
2024-03-09 09:47:27 +00:00
|
|
|
return False
|
|
|
|
self.p2p_connection_sessions[session.session_id] = session
|
|
|
|
return True
|
|
|
|
|
|
|
|
def get_p2p_connection_session(self, id):
|
|
|
|
if id not in self.p2p_connection_sessions:
|
|
|
|
pass
|
|
|
|
return self.p2p_connection_sessions[id]
|