diff --git a/ax25/ax25Connection.py b/ax25/ax25Connection.py index 6345a721..745b46d5 100644 --- a/ax25/ax25Connection.py +++ b/ax25/ax25Connection.py @@ -547,7 +547,12 @@ def _ft_queue_handling(self): def ft_reset_timer(self, conn_uid: str): if self.ft_obj is not None: + print(f"ft_resetRNR: conn_uid {conn_uid}") + print(f"ft_resetRNR: rev conn_uid {reverse_uid(conn_uid)}") + print(f"ft_resetRNR: self.uid {self.uid}") if conn_uid != self.uid and reverse_uid(conn_uid) != self.uid: + print(f"ft_resetRNR:SET! ") + self.ft_obj.ft_set_wait_timer() ####################### @@ -589,9 +594,11 @@ def new_link_connection(self, conn): self.my_call_str = digi_call # self.ax25_out_frame.digi_call = str(conn.my_call_str) self.digi_call = digi_call + print("LC 1") self._port_handler.link_connections[str(conn.uid)] = conn, '' else: - self._port_handler.link_connections[str(conn.uid)] = conn, self.my_call_str + print("LC 2") + self._port_handler.link_connections[str(conn.uid)] = conn, conn.my_call_str self.LINK_Connection = conn self.is_link = True @@ -599,23 +606,29 @@ def new_link_connection(self, conn): return True def new_digi_connection(self, conn): - # print(f"Conn newDIGIConn: UID: {conn.uid}") - # logger.debug(f"Conn newDIGIConn: UID: {conn.uid}") + print(f"Conn newDIGIConn: UID: {conn.uid}") + logger.debug(f"Conn newDIGIConn: UID: {conn.uid}") if conn is None: print("Conn ERROR: newDIGIConn: not conn") logger.error("Conn ERROR: newDIGIConn: not conn") return False - if self.uid in self._port_handler.link_connections.keys(): + if self.uid in list(self._port_handler.link_connections.keys()): self.zustand_exec.change_state(4) self.zustand_exec.tx(None) - print("Conn ERROR: newDIGIConn: self.uid in self._port_handler.link_connections") logger.error("Conn ERROR: newDIGIConn: self.uid in self._port_handler.link_connections") + logger.error(f"{self.uid} - {self._port_handler.link_connections.keys()}") return False self.digi_call = str(conn.digi_call) - self._port_handler.link_connections[str(self.uid)] = self, conn.digi_call + self._port_handler.link_connections[str(self.uid)] = self, str(conn.digi_call) self.LINK_Connection = conn self.is_link = True + ############################### + # Del Digi Conn + # self.own_port.delete_digi_conn(conn.uid) + # conn.own_port.delete_digi_conn(self.uid) + + # self.cli = cli.cliMain.NoneCLI(self) # Disable CLI # print("new_digi TX CONN ") return True @@ -647,10 +660,11 @@ def link_disco(self, reconnect=True): self.LINK_Connection.conn_disco() elif reconnect and self.is_link_remote and not self.is_digi: # logger.debug('ReConn') - if hasattr(self.LINK_Connection, 'send_sys_Msg_to_gui'): + if all((hasattr(self.LINK_Connection, 'send_sys_Msg_to_gui'), + hasattr(self.LINK_Connection, 'my_call_str'))): # TODO ?? Why to LINKCONN GUI ? CHANNEL ? - self.LINK_Connection.send_sys_Msg_to_gui(f'*** Reconnected to {self.my_call_str}') - self.send_to_link(f'\r*** Reconnected to {self.my_call_str}\r'.encode('ASCII', 'ignore')) + self.LINK_Connection.send_sys_Msg_to_gui(f'*** Reconnected to {self.LINK_Connection.my_call_str}') + self.send_to_link(f'\r*** Reconnected to {self.LINK_Connection.my_call_str}\r'.encode('ASCII', 'ignore')) """ if self.digi_call: print("Link Disco ----") @@ -1189,31 +1203,31 @@ def accept_connection(self): self._port_handler.accept_new_connection(self) if self.LINK_Connection: self.LINK_Connection.cli.change_cli_state(5) + logger.debug(f"Conn {self.uid}: accept_digi_connection is LINK") # if self.digi_call in self._port_cfg.parm_Digi_calls: if POPT_CFG.get_digi_is_enabled(self.digi_call): + logger.debug(f"Conn {self.uid}: accept_digi_connection") if self.accept_digi_connection(): + logger.debug(f"Conn {self.uid}: accept_digi_connection True") self.is_digi = True return - """ - print(f"Accept Conn UID: {self.uid}") - print(f"Accept Conn digi_call: {self.digi_call}") - print(f"Accept Conn parm_Digi_calls: {self._port_cfg.parm_Digi_calls}") - """ + self.send_to_link( f'\r*** Connected to {self.to_call_str}\r'.encode('ASCII', 'ignore') ) def accept_digi_connection(self): - # print(f'DIGI Conn accept.. {self.uid} ?') + logger.debug(f'DIGI Conn accept.. {self.uid} ?') if not self.LINK_Connection: - # print(f'DIGI Conn accept: No LINK_Connection {self.uid}') - # print(f'DIGI Conn accept: No LINK_Connection {self.LINK_Connection}') + logger.debug(f'DIGI Conn accept: No LINK_Connection {self.uid}') + logger.debug(f'DIGI Conn accept: No LINK_Connection {self.LINK_Connection}') return False - digi_uid = self.LINK_Connection.uid + digi_uid = str(self.LINK_Connection.uid) digi_uid = reverse_uid(digi_uid) link_conn_port = self.LINK_Connection.own_port - return link_conn_port.accept_digi_conn(digi_uid) + digi_accept: bool = link_conn_port.accept_digi_conn(digi_uid) + return digi_accept def insert_new_connection(self): """ Insert connection for handling """ diff --git a/ax25/ax25Digi.py b/ax25/ax25Digi.py index 87651349..7c43f915 100644 --- a/ax25/ax25Digi.py +++ b/ax25/ax25Digi.py @@ -15,7 +15,7 @@ def __init__(self, digi_conf: dict): self._ax25_conf = digi_conf.get('ax25_conf', {}) self._digi_call = digi_conf.get('digi_call', '') self._digi_ssid = digi_conf.get('digi_ssid', 0) - self._rx_conn_uid = self._ax25_conf.get('uid', '') + self._rx_conn_uid = str(self._ax25_conf.get('uid', '')) self._tx_conn_uid = '' self._rx_port = digi_conf.get('rx_port', None) self._tx_port = None @@ -40,18 +40,17 @@ def __init__(self, digi_conf: dict): self._rx_port, self._port_handler )): - logger.warning("DIGI INIT Error !") + logger.error("DIGI INIT Error !") self._state_0_error() - """ - print(f"_ax25_conf: {self._ax25_conf}") - print(f"_digi_call: {self._digi_call}") - print(f"_rx_port: {self._rx_port}") - print(f"_port_handler: {self._port_handler}") - """ + logger.debug(f"_ax25_conf: {self._ax25_conf}") + logger.debug(f"_digi_call: {self._digi_call}") + logger.debug(f"_rx_port: {self._rx_port}") + logger.debug(f"_port_handler: {self._port_handler}") def _init_digi_conn(self, ax25_frame): # print("!!!! _init_digi_conn !!!!") logger.debug("!!!! _init_digi_conn !!!!") + logger.debug(ax25_frame.get_frame_conf()) """ try: self._rx_conn = AX25Conn(ax25_frame, port=self._rx_port) @@ -61,14 +60,14 @@ def _init_digi_conn(self, ax25_frame): self._state_0_error() return """ - self._rx_conn = AX25Conn(ax25_frame, port=self._rx_port) + self._rx_conn = AX25Conn(ax25_frame, port=self._rx_port) ######### self._rx_conn.cli.change_cli_state(5) self._rx_conn.is_link_remote = False self._rx_conn.cli_remote = False self._rx_conn.is_digi = True - self._rx_conn.my_call_str = self._digi_call - self._rx_conn.digi_call = self._digi_call - self._rx_conn_uid = self._rx_conn.uid + self._rx_conn.my_call_str = str(self._digi_call) + self._rx_conn.digi_call = str(self._digi_call) + self._rx_conn_uid = str(self._rx_conn.uid) self._rx_conn.set_station_cfg() """ try: @@ -86,20 +85,20 @@ def _init_digi_conn(self, ax25_frame): if self._conf_digi_auto_port: tx_port_id = -1 else: - tx_port_id = self._rx_port.port_id + tx_port_id = int(self._rx_port.port_id) if self._conf_digi_ssid_port: - tx_port_id = self._digi_ssid + tx_port_id = int(self._digi_ssid) - ax25_frame_conf = ax25_frame.get_frame_conf() # print(f"DIGI INIT: axConf: {ax25_frame_conf}") tx_conn = self._port_handler.new_outgoing_connection( dest_call=self._ax25_conf.get('to_call_str', ''), own_call=self._ax25_conf.get('from_call_str', ''), - via_calls=ax25_frame_conf.get('via_calls_str', []), # Auto lookup in MH if not exclusive Mode + via_calls=self._ax25_conf.get('via_calls_str', []), # Auto lookup in MH if not exclusive Mode port_id=tx_port_id, # -1 Auto lookup in MH list exclusive=True, # True = no lookup in MH list - is_service=True + is_service=True, + # link_conn=self._rx_conn ) if not tx_conn[0]: logger.error(f"Digi-Error _init_digi_conn: {tx_conn[1]}") @@ -113,7 +112,7 @@ def _init_digi_conn(self, ax25_frame): self._tx_conn.is_link_remote = True self._tx_conn.cli_remote = False self._tx_conn.is_digi = True - self._tx_conn.my_call_str = self._digi_call + self._tx_conn.my_call_str = str(self._digi_call) # self._tx_conn.digi_call = self._digi_call self._tx_conn.set_station_cfg() """ @@ -124,20 +123,24 @@ def _init_digi_conn(self, ax25_frame): return """ - self._tx_conn_uid = self._tx_conn.uid + self._tx_conn_uid = str(self._tx_conn.uid) self._tx_port = self._tx_conn.own_port - + # self._rx_port.add_digi_conn(self) ##################################################### logger.debug(f"LinkConn : {self._port_handler.link_connections.items()}") logger.debug(f"LinkConn : txConns: {self._tx_port.connections}") logger.debug(f"LinkConn : txConn UID: {self._tx_conn.uid}") + logger.debug(f"LinkConn : txConn_UID: {self._tx_conn_uid}") logger.debug(f"LinkConn : rxConns: {self._rx_port.connections}") logger.debug(f"LinkConn : rxConn UID: {self._rx_conn.uid}") + logger.debug(f"LinkConn : rxConn_UID: {self._rx_conn_uid}") self._state = 2 def add_rx_conn_cron(self): + logger.debug("LinkConn : ADD Crone ") if not self._rx_conn: + logger.debug("LinkConn : ADD Crone: no rx_conn ") self._state_0_error() - return + return False if self._link_connections(): if self._rx_conn_uid in self._tx_port.connections: logger.debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") @@ -149,7 +152,7 @@ def add_rx_conn_cron(self): logger.debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") # self._tx_port.connections[str(self._tx_conn_uid)] = self._tx_conn self._state_0_error() - return + return False self._rx_port.connections[str(self._rx_conn_uid)] = self._rx_conn self._port_handler.insert_new_connection_PH(self._rx_conn, is_service=True) self._state = 3 @@ -157,19 +160,25 @@ def add_rx_conn_cron(self): logger.debug(f"LinkConn Accept: {self._port_handler.link_connections.items()}") logger.debug(f"RX-State: {self._rx_conn.get_state()}") logger.debug(f"TX-State: {self._tx_conn.get_state()}") - - return + return True + logger.debug("LinkConn : ADD Crone: Fail ") self._state_0_error() + return False def _link_connections(self): + print("_link_connections") if not self._rx_conn or not self._tx_conn: self._state_0_error() logger.error('Digi-Conn_link Error: No tx or rx conn') - return + return False if self._rx_conn.new_digi_connection(self._tx_conn): - logger.debug('Digi-Accept') + logger.debug('Digi-Accept tX-CONN') return True + if self._tx_conn.new_digi_connection(self._rx_conn): + logger.debug('Digi-Accept RX-CONN') + return True + logger.debug("Digi-Not Accept") return False def _state_0_error(self, ax25_frame=None): @@ -181,29 +190,30 @@ def _state_0_error(self, ax25_frame=None): def _state_1_rx(self, ax25_frame): if ax25_frame.ctl_byte.flag == 'SABM': if not self._tx_conn_uid: + print("_state_1_rx: not self._tx_conn_uid") self._init_digi_conn(ax25_frame) else: logger.error('Digi-SABM-RX ERROR') self._state_0_error() # self.crone() # # SABM TX - elif ax25_frame.ctl_byte.flag == 'DISC': + elif ax25_frame.ctl_byte.flag in ['DISC', 'DM']: # self._abort_digi_conn(ax25_frame) logger.debug('DIGI S! DISC RX') if self.is_done(): - # self._rx_port.add_frame_to_digiBuff(ax25_frame) - self._digi_fallback(ax25_frame) + self._rx_port.add_frame_to_digiBuff(ax25_frame) + # self._digi_fallback(ax25_frame) else: # print('DIGI ABORT') self._abort_digi_conn(ax25_frame) else: # MAYBE Fallback to simple Digi Mode ? logger.warning(f'DIGI Not Known Frame: {ax25_frame.ctl_byte.flag}') - self._digi_fallback(ax25_frame) - self._state_0_error() + # self._digi_fallback(ax25_frame) + # self._state_0_error() def _state_2(self, ax25_frame=None): if ax25_frame.ctl_byte.flag == 'SABM': - # print('DIGI INIT SABM RX') + logger.debug('DIGI INIT SABM RX - state 2') self._last_rx = time.time() if self._check_txConn_state(): """ IF AXIP is Faster than other Port """ @@ -214,7 +224,7 @@ def _state_2(self, ax25_frame=None): self._rx_conn.zustand_exec.change_state(5) self._state = 3 - elif ax25_frame.ctl_byte.flag == 'DISC': + elif ax25_frame.ctl_byte.flag in ['DISC', 'DM']: self._abort_digi_conn(ax25_frame) logger.debug('DIGI INIT DISC RX ') @@ -265,10 +275,10 @@ def _UI_digi(self, ax25_frame): if self._conf_digi_auto_port: tx_port_id = -1 else: - tx_port_id = self._rx_port.port_id + tx_port_id = int(self._rx_port.port_id) if self._conf_digi_ssid_port: - tx_port_id = self._digi_ssid + tx_port_id = int(self._digi_ssid) port = self._port_handler.get_port_by_id(tx_port_id) if not port: @@ -281,14 +291,14 @@ def _digi_fallback(self, ax25_frame): print('DIGI Fallback') logger.debug('DIGI Fallback') # self._rx_port.add_frame_to_digiBuff(ax25_frame) - self._UI_digi(ax25_frame) + # self._UI_digi(ax25_frame) def digi_rx_handle(self, ax25_frame): if ax25_frame.ctl_byte.flag == 'UI': self._UI_digi(ax25_frame) return state_exec = self._state_tab.get(self._state, None) - if not callable(state_exec): + if state_exec is None: self._state_0_error() logger.error(f"DIGI-RX ERROR: not callable(state_exec) - STATE: {self._state}") print(f"DIGI-RX ERROR: not callable(state_exec) - STATE: {self._state}") @@ -408,6 +418,9 @@ def _check_buffer_limit_TxConn(self): def get_state(self): return self._state + def get_tx_uid(self): + return str(self._tx_conn_uid) + def _check_txConn_state(self): if not self._tx_conn: logger.debug('DIGI._check_txConn_state : no txConn') diff --git a/ax25/ax25Error.py b/ax25/ax25Error.py index b2bae880..8a50725f 100644 --- a/ax25/ax25Error.py +++ b/ax25/ax25Error.py @@ -1,3 +1,4 @@ +# from cfg.constant import DEBUG_LOG from cfg.logger_config import logger class AX25EncodingERROR(Exception): @@ -10,8 +11,12 @@ def __init__(self, frame=None): logger.error(f'Data: {frame.payload}') # print(f'Data: {frame.payload}') # if DEBUG_LOG: + """ if hasattr(frame, 'get_frame_conf'): - frame_cfg: dict = frame.get_frame_conf() + try: + frame_cfg: dict = frame.get_frame_conf() + except TypeError: + return logger.error('----- AX25-Frame CFG ENC ------') # print('----- AX25-Frame CFG ENC ------') for k, data in frame_cfg.items(): @@ -19,23 +24,25 @@ def __init__(self, frame=None): # print(f'{k}: {data}') # logger.warning(f'Hex: {frame.data.hex()}') + """ class AX25DecodingERROR(Exception): def __init__(self, frame=None): if frame is not None: - logger.error('AX25 Packet Decoding Error !') + logger.warning('AX25 Packet Decoding Error !') if hasattr(frame, 'payload'): # logger.warning(frame.payload) - logger.error(f'Data: {frame.payload}') - - # if DEBUG_LOG: - if hasattr(frame, 'get_frame_conf'): - frame_cfg: dict = frame.get_frame_conf() - logger.error('----- AX25-Frame CFG DEC------') - # print('----- AX25-Frame CFG DEC------') - for k, data in frame_cfg.items(): - logger.error(f'{k}: {data}') - # print(f'{k}: {data}') + logger.warning(f'Data: {frame.payload}') + """ + if DEBUG_LOG: + if hasattr(frame, 'get_frame_conf'): + frame_cfg: dict = frame.get_frame_conf() + logger.debug('----- AX25-Frame CFG DEC------') + # print('----- AX25-Frame CFG DEC------') + for k, data in frame_cfg.items(): + logger.debug(f'{k}: {data}') + # print(f'{k}: {data}') + """ class AX25DeviceERROR(Exception): def __init__(self, e=None, port=None): diff --git a/ax25/ax25FileTransfer.py b/ax25/ax25FileTransfer.py index 5a41cbaf..9dd7775c 100644 --- a/ax25/ax25FileTransfer.py +++ b/ax25/ax25FileTransfer.py @@ -11,28 +11,32 @@ def ft_rx_header_lookup(data: b'', last_pack: b''): data = last_pack + data - # print(f"ft lookup: {data}") - for mode in FT_RX_HEADERS: - if mode in data: - ret = { - FT_RX_HEADERS[0]: is_autobin(data[data.index(mode):]), - FT_RX_HEADERS[1]: is_yapp_SI(data[data.index(mode):]), # SI - # FT_RX_HEADERS[2]: is_yapp_RI(data[data.index(FT_RX_HEADERS[2]):]), # RI - Server Mode - }[mode] - if ret: - # print(f"ft lookup: True") - return ret + lines = data.split(b'\r') + for line in lines: + for mode in FT_RX_HEADERS: + if line.startswith(mode): + ret = { + FT_RX_HEADERS[0]: is_autobin(line[line.index(mode):]), + FT_RX_HEADERS[1]: is_yapp_SI(line[line.index(mode):]), # SI + # FT_RX_HEADERS[2]: is_yapp_RI(data[data.index(FT_RX_HEADERS[2]):]), # RI - Server Mode + }[mode] + if ret: + return ret + return False def is_autobin(header): # if header[-1:] != b'\r': # return False - parts = header[1:-1].split(b'#') - if parts[0] != b'BIN': + parts = header.split(b'#') + if not parts: return False + parts = parts[1:] if len(parts) < 2: return False + if parts[0] != b'BIN': + return False if not parts[1].isdigit(): return False if len(parts) == 2: @@ -46,17 +50,23 @@ def is_autobin(header): ) # return parts[1], 0, 0, '', False new_ft_obj.raw_data_len = int(parts[1]) + return new_ft_obj - elif len(parts) != 5: + if len(parts) != 5: return False if parts[2][:1] != b'|': return False + if not parts[2][1:].isdigit(): return False + if parts[3][:1] != b'$': return False + """ if not parts[3][1:].isdigit(): + print(f"not parts[3][1:].isdigit(): {parts}") return False + """ size = int(parts[1]) crc = int(parts[2][1:]) # datetime_hex = parts[3][1:] @@ -75,14 +85,20 @@ def is_autobin(header): return new_ft_obj -def check_autobin(header): +def check_autobin(header: bytes): # if header[-1:] != b'\r': # return False - parts = header[1:-1].split(b'#') - if parts[0] != b'BIN': + # parts = header[1:-1].split(b'#') + header = header.replace(b'\r', b'') + parts = header.split(b'#') + if not parts: return False + parts = parts[1:] if len(parts) < 2: return False + if parts[0] != b'BIN': + return False + # len if not parts[1].isdigit(): return False if len(parts) == 2: @@ -96,8 +112,10 @@ def check_autobin(header): return False if parts[3][:1] != b'$': return False + """ if not parts[3][1:].isdigit(): return False + """ return True @@ -320,19 +338,40 @@ def ft_crone(self): return self.class_protocol.crone_mode() def ft_rnr(self): + if self.connection is not None: + if not self.param_wait: + if self.connection.is_RNR: + self.connection.unset_RNR() + return + if self.connection.is_RNR: + if self.last_tx < time.time(): + self.connection.unset_RNR() + self.can_rnr = False + return + + if self.can_rnr and self.last_tx > time.time(): + self.connection.set_RNR() + self.ft_set_wait_timer() + return + + """ if self.connection is not None: if not self.param_wait: if self.connection.is_RNR: self.connection.unset_RNR() else: + if not self.connection.is_RNR: if self.can_rnr: self.connection.set_RNR() + print("Conn Set RNR") self.ft_set_wait_timer() else: if self.last_tx < time.time(): self.connection.unset_RNR() self.can_rnr = False + """ + def ft_switch_rnr(self): if self.connection is not None: @@ -488,7 +527,7 @@ def open_file(self): self.file = open(CFG_ft_downloads + self.filename, 'wb') self.e = False return True - except (PermissionError, ValueError): + except (PermissionError, ValueError, FileNotFoundError): return False return False @@ -602,8 +641,8 @@ def init_TX(self): def init_RX(self): self.state_tab = { - 0: self.mode_init_rx, - 1: self.mode_rx_data, + 0: self._mode_init_rx, + 1: self._mode_rx_data, 8: self.exec_abort, 9: self.ft_root.ft_mode_wait_for_end, } @@ -612,6 +651,7 @@ def init_RX(self): self.parm_can_pause = True self.open_file() if self.e: + self.state = 8 self.e = False # self.exec_abort() @@ -626,19 +666,20 @@ def mode_init(self): self.send_data(self.header) self.state = 2 - def mode_init_rx(self): + def _mode_init_rx(self): if self.ft_root.ft_rx_buf: + # print(f'_mode_init_rx - {self.ft_root.ft_rx_buf}') if check_autobin(self.ft_root.ft_rx_buf): self.start = True self.ft_root.ft_rx_buf = b'' self.state = 1 - self.send_data(b'#OK#') + self.send_data(b'#OK#\r') self.start_ft() else: self.e = True self.state = 9 - def mode_rx_data(self): + def _mode_rx_data(self): if not self.check_abort(): if self.ft_root.ft_rx_buf: self.ft_root.can_rnr = True diff --git a/ax25/ax25InitPorts.py b/ax25/ax25InitPorts.py index 00b2fca7..fe04f169 100644 --- a/ax25/ax25InitPorts.py +++ b/ax25/ax25InitPorts.py @@ -7,6 +7,7 @@ # from ax25.ax25RoutingTable import RoutingTable from cfg.popt_config import POPT_CFG from cfg.logger_config import logger, log_book +from fnc.one_wire_fnc import get_1wire_temperature, is_1wire_device from fnc.str_fnc import get_strTab from schedule.popt_sched_tasker import PoPTSchedule_Tasker from sound.popt_sound import SOUND @@ -112,6 +113,11 @@ def __init__(self): self._init_bbs() ####################################################### # Port Handler Tasker (threaded Loop) + logger.info("PH: Tasker Init") + # 1Wire Thread + self._update_1wire_th = None + self._1wire_timer = time.time() + 10 # + 10 Sec, give some time to Init the rest + # self._task_timer_05sec = time.time() + 0.5 self._task_timer_1sec = time.time() + 1 self._task_timer_2sec = time.time() + 2 @@ -156,6 +162,7 @@ def _1sec_task(self): if time.time() > self._task_timer_1sec: self._Sched_task() self._mh_task() + self._tasker_1wire() self._task_timer_1sec = time.time() + 1 def _2sec_task(self): @@ -232,7 +239,6 @@ def check_all_ports_closed(self): return ret def close_popt(self): - # TODO Cleanup / OPT logger.info("PH: Closing PoPT") self.is_running = False logger.info("PH: Closing APRS-Client") @@ -245,6 +251,15 @@ def close_popt(self): self._mh.save_mh_data() logger.info("PH: Saving Port Statistic-Data") self._mh.save_PortStat() + if self._update_1wire_th is not None: + n = 0 + while self._update_1wire_th.is_alive(): + logger.info("PH: Warte auf 1-Wire Thread") + n += 1 + if n > 40: + logger.error("PH: 1-Wire Thread nicht beendet !!") + break + time.sleep(0.5) logger.info("PH: Saving User-DB Data") USER_DB.save_data() logger.info("PH: Closing User-DB") @@ -272,8 +287,8 @@ def close_port(self, port_id: int): time.sleep(0.5) port.close() - - del self.ax25_ports[port_id] + if port_id in self.ax25_ports: + del self.ax25_ports[port_id] del port self.sysmsg_to_gui(get_strTab('port_closed', POPT_CFG.get_guiCFG_language()).format(port_id)) #self.sysmsg_to_gui('Info: Port {} erfolgreich geschlossen.'.format(port_id)) @@ -1028,6 +1043,43 @@ def get_mcast_server(self): return self._mcast_server ############################################################## + # 1Wire TextVars + def _tasker_1wire(self): + if time.time() < self._1wire_timer: + return + if self._update_1wire_th is None: + self._oneWire_thread_run() + return + if self._update_1wire_th.is_alive(): + return + self._oneWire_thread_run() + return + + def _oneWire_thread_run(self): + self._1wire_timer = time.time() + POPT_CFG.get_1wire_loop_timer() + self._update_1wire_th = threading.Thread(target=self._oneWire_task) + self._update_1wire_th.start() + + @staticmethod + def _oneWire_task(): + if not is_1wire_device(): + return + sensor_cfg = POPT_CFG.get_1wire_sensor_cfg() + if not sensor_cfg: + return + for textVar, sens_cfg in sensor_cfg.items(): + sens_cfg: dict + sens_id = sens_cfg.get('device_path', '') + if not sens_id: + continue + try: + sens_cfg['device_value'] = str(get_1wire_temperature(sens_id)[0]) + except IndexError: + logger.warning(f"PH: _oneWire_task IndexError: {textVar}") + logger.warning(f"PH: _oneWire_task IndexError: {sens_cfg}") + continue + # POPT_CFG.set_1wire_sensor_cfg(dict(sensor_cfg)) + ############################################################## # def debug_Connections(self): all_conn = self.get_all_connections(with_null=True) @@ -1037,11 +1089,25 @@ def debug_Connections(self): for ch_id, conn in all_conn.items(): print(f"CH-ID: {ch_id} - UID: {conn.uid} - STATE: {conn.get_state()}") print('ALL LinkConn ------------------') - for link_uid, conn_tpl in all_linkConn.items(): - print(f"LINK-UID: {link_uid} - UID: {conn_tpl[0].uid} - STATE: {conn_tpl[0].get_state()} - LINK: {conn_tpl[1]}") + for link_uid, (conn, link) in all_linkConn.items(): + print(f"LINK-UID: {link_uid} - UID: {conn.uid} - STATE: {conn.get_state()} - LINK: {link}") + print(f"LINK: conn: {conn} link_conn: {conn.LINK_Connection}") + print(f"LINK: link_conn.conn: {conn.LINK_Connection.LINK_Connection} conn: {conn.LINK_Connection.LINK_Connection.LINK_Connection}") print('ALL DIGIConn ------------------') for digi_uid, conn in all_digiConn.items(): - print(f"LINK-UID: {digi_uid} - STATE: {conn.get_state()}") + print(f"LINK-UID: {digi_uid} - STATE: {conn.get_state()} conn: {conn}") + + ####################################################################### + logger.debug("=================Port-Watch-Dog====================") + for port_id, port in self.get_all_ports().items(): + logger.debug(f"Port {port_id}: WD > {time.time() - port.port_w_dog}") + logger.debug(f"Port {port_id}: Loop is running > {port.loop_is_running}") + logger.debug(f"Port {port_id}: Device is running > {port.device_is_running}") + logger.debug(f"Port {port_id}: Device > {port.device}") + if hasattr(port.device, 'readall'): + logger.debug(f"Port {port_id}: readall > {port.device.readall()}") + logger.debug("") + logger.debug("====================================================") PORT_HANDLER = AX25PortHandler() diff --git a/ax25/ax25Kiss.py b/ax25/ax25Kiss.py index a2041cf2..ac2582e5 100644 --- a/ax25/ax25Kiss.py +++ b/ax25/ax25Kiss.py @@ -1,33 +1,33 @@ -from cfg.constant import TNC_KISS_CMD, TNC_KISS_CMD_END +""" +Idea from: +https://github.com/ampledata/kiss +Doc: +http://www.symek.com/pdf/extra.pdf +TODO: MultiKiss ( Direwolf Channels / TNC PORTS) +CH0 Data: \xc0\x00 \x0c\xc0' +CH1 Data: \xc0\x10 \x0c\xc0' +CH2 Data: \xc0\x20 \x0c\xc0' +SMACK: +CH0 Data: \xc0\x80 ' FEND 0x80 DATA DATA ... DATA CRCLOW CRCHIGH FEND +CH1 Data: \xc0\x90 ' +... +NL5VKL-2 +""" +from cfg.constant import TNC_KISS_CMD, TNC_KISS_CMD_END +from cfg.logger_config import logger class Kiss(object): - """ - Idea from: - https://github.com/ampledata/kiss - - TODO: MultiKiss ( Direwolf Channels ) - CH0 Data: \xc0\x00 \x0c\xc0' - CH1 Data: \xc0\x10 \x0c\xc0' - CH2 Data: \xc0\x20 \x0c\xc0' - ... - - """ - def __init__(self, port_cfg: dict): - self.is_enabled = True - # self.port_cfg = port_cfg self.is_enabled = port_cfg.get('parm_kiss_is_on', True) # CFG Flags - self._DATA_FRAME = b'\x00' # Channel 0 - self._RETURN = b'\xFF' - # self.START = b'$0' - self._JHOST0 = bytes.fromhex('11241B404B0D') # jhost0 - DC1+CAN+ESC+'@K' Da fehlt aber noch CR + self._DATA_FRAME = b'\x00' # Channel 0 + self._SMACK_FRAME = b'\x80' # Channel 0 + # self._RETURN = b'\xFF' + # self._JHOST0 = bytes.fromhex('11241B404B0D') # jhost0 - DC1+CAN+ESC+'@K' Da fehlt aber noch CR - # self._START_TNC_DEFAULT = TNC_KISS_CMD # TNC2 KISS MODE b'\x1b@K' self._START_TNC_DEFAULT = port_cfg.get('parm_kiss_init_cmd', TNC_KISS_CMD) # TNC2 KISS MODE b'\x1b@K' - # self._END_TNC_DEFAULT = TNC_KISS_END_CMD # TNC2 KISS MODE b'\x1b@K' self._END_TNC_DEFAULT = port_cfg.get('parm_kiss_end_cmd', TNC_KISS_CMD_END) # TNC2 KISS MODE b'\x1b@K' # ESC & END Flags @@ -35,18 +35,13 @@ def __init__(self, port_cfg: dict): self._FESC = b'\xDB' self._TFEND = b'\xDC' self._TFESC = b'\xDD' - # KISS_ON = 'KISS $0B' - # self.KISS_OFF = b''.join([self._FEND, self._RETURN, self._FEND, self._FEND]) - # ???? self.KISS_OFF = b''.join([self.FEND, self.RETURN, self.FEND]) ???? - # self.txd_frame = lambda: self.TX_DELAY + bytes.fromhex(hex(self.port_cfg.parm_kiss_TXD)[2:]) + self._txd_frame = lambda: b'\xC0\x01' + bytes.fromhex(hex(port_cfg.get('parm_kiss_TXD', 35))[2:].zfill(2)) + b'\xC0' - # self.txd_frame_ch1 = lambda: b'\xC0\x11' + bytes.fromhex(hex(self.port_cfg.parm_kiss_TXD)[2:]) + b'\xC0' self._pers_frame = lambda: b'\xC0\x02' + bytes.fromhex(hex(port_cfg.get('parm_kiss_Pers', 160))[2:].zfill(2)) + b'\xC0' self._slot_frame = lambda: b'\xC0\x03' + bytes.fromhex(hex(port_cfg.get('parm_kiss_Slot', 30))[2:].zfill(2)) + b'\xC0' self._tail_frame = lambda: b'\xC0\x04' + bytes.fromhex(hex(port_cfg.get('parm_kiss_Tail', 15))[2:].zfill(2)) + b'\xC0' self._duplex_frame = lambda: b'\xC0\x05' + bytes.fromhex( str(port_cfg.get('parm_kiss_F_Duplex', 0)).zfill(2)) + b'\xC0' - # self.hw_frame = lambda: b'\x06' + bytes.fromhex(hex(20)[2:]) + b'\xC0' self._kiss_data_frame = lambda inp: self._FEND + self._DATA_FRAME + inp + self._FEND # "FEND is sent as FESC, TFEND" # 0xC0 is sent as 0xDB 0xDC @@ -56,8 +51,6 @@ def __init__(self, port_cfg: dict): # 0xDB is sent as 0xDB 0xDD self._FESC_TFESC = b''.join([self._FESC, self._TFESC]) - # self.set_param = self.build_kiss_param_frame() - def set_all_parameter(self): return b''.join([ self._txd_frame(), @@ -66,8 +59,39 @@ def set_all_parameter(self): self._tail_frame(), self._duplex_frame(), ]) + ###################################################################### + def unknown_kiss_frame(self, inp: bytes): + if not self.is_enabled: + return inp + if not inp.startswith(self._FEND): + logger.warning(f"Kiss: NO KISS Frame > {inp}") + return True + if inp.startswith(self._FEND + self._DATA_FRAME + self._FEND): + logger.warning(f"Kiss: Empty KISS Frame > {inp}") + return True + if inp.startswith(self._FEND + self._DATA_FRAME): + return False + if inp.startswith(self._FEND + self._SMACK_FRAME): + logger.warning(f"Kiss: SMACK-Frame > {inp}") + try: + logger.warning(f"Kiss: SMACK-Frame HEX> {inp.hex()}") + except SyntaxError: + pass + return True + """ + logger.warning(f"Kiss: Unknown Kiss-Frame > {inp}") + try: + logger.warning(f"Kiss: Unknown Kiss-Frame HEX> {inp.hex()}") + except SyntaxError: + pass + return True + """ + return False - def de_kiss(self, inp: b''): + + ############################################################# + # + def de_kiss(self, inp: bytes): """ Code from: https://github.com/ampledata/kiss Escape special codes, per KISS spec. @@ -76,22 +100,24 @@ def de_kiss(self, inp: b''): FESC is then sent as FESC, TFESC." - http://en.wikipedia.org/wiki/KISS_(TNC)#Description """ - if self.is_enabled: - if inp[:2] == self._FEND + self._DATA_FRAME \ - and inp[-1:] == self._FEND \ - and len(inp) > 14: - return inp[2:-1].replace( - self._FESC_TFESC, - self._FESC - ).replace( - self._FESC_TFEND, - self._FEND - ) - else: - return b'' - return inp + if not self.is_enabled: + return inp + # if len(inp) < 15: + if len(inp) < 3: + return None + if not inp.endswith(self._FEND): + return None + if not inp.startswith(self._FEND + self._DATA_FRAME): + return None + return inp[2:-1].replace( + self._FESC_TFESC, + self._FESC + ).replace( + self._FESC_TFEND, + self._FEND + ) - def kiss(self, inp: b''): + def kiss(self, inp: bytes): """ Code from: https://github.com/ampledata/kiss Recover special codes, per KISS spec. @@ -112,6 +138,7 @@ def kiss(self, inp: b''): ) return inp + ############################################################################# def device_kiss_end(self): # return b''.join([self.FEND, self.RETURN, self.FEND]) return self._END_TNC_DEFAULT diff --git a/ax25/ax25Port.py b/ax25/ax25Port.py index ac33d611..9d014794 100644 --- a/ax25/ax25Port.py +++ b/ax25/ax25Port.py @@ -5,6 +5,7 @@ import crcmod from ax25.ax25UI_Pipe import AX25Pipe +from fnc.os_fnc import is_linux crc_x25 = crcmod.predefined.mkCrcFun('x-25') @@ -15,24 +16,26 @@ from ax25.ax25dec_enc import AX25Frame, bytearray2hexstr from cfg.popt_config import POPT_CFG from cfg.logger_config import logger -from fnc.ax25_fnc import reverse_uid +from fnc.ax25_fnc import reverse_uid, is_digipeated_pre_digi from ax25.ax25Error import AX25EncodingERROR, AX25DecodingERROR, AX25DeviceERROR, AX25DeviceFAIL, MCastInitError -from fnc.os_fnc import is_linux +# from fnc.os_fnc import is_linux from fnc.socket_fnc import get_ip_by_hostname +""" if is_linux(): import termios - - +""" class RxBuf: axip_add = '', 0 raw_data = b'' - kiss = b'' + kiss_frame = b'' class AX25Port(object): def __init__(self, port_id: int, port_handler): + self._logTag = f"Port {port_id}: " + self.port_w_dog = time.time() # Debuging self._port_handler = port_handler self.loop_is_running = self._port_handler.is_running self.ende = False @@ -42,7 +45,7 @@ def __init__(self, port_id: int, port_handler): # CONFIG self._port_cfg = dict(POPT_CFG.get_port_CFG_fm_id(port_id)) if not self._port_cfg: - logger.error(f"Port {port_id}: No Config !!!") + logger.error(f"{self._logTag}No Config !!!") raise AX25DeviceFAIL(self) self.kiss = Kiss(self._port_cfg) self._port_param = self._port_cfg.get('parm_PortParm', ('', 0)) @@ -112,7 +115,7 @@ def close(self): # if self.device is not None: # self.device.close() - def rx(self): + def _rx(self): return RxBuf() def tx(self, frame): @@ -190,20 +193,42 @@ def rx_handler(self, ax25_frame): return False def _rx_link_handler(self, ax25_frame): - if reverse_uid(ax25_frame.addr_uid) in self._port_handler.link_connections.keys(): - logger.debug(f"Port rx_link_handler reverse_uid: UID: {ax25_frame.addr_uid}") + ax25_frame_conf = dict(ax25_frame.get_frame_conf()) + uid = str(ax25_frame_conf.get('uid', '')) + """ + if reverse_uid(str(uid)) in self._port_handler.link_connections.keys(): + logger.debug(f"Port rx_link_handler reverse_uid: UID: {uid}") logger.debug(f"Port rx_link_handler reverse_uid: FRAME ctl: {ax25_frame.ctl_byte.flag}") return False - if ax25_frame.addr_uid in self._port_handler.link_connections.keys(): - # print(f"Link-Conn RX: {ax25_frame.addr_uid}") - conn = self._port_handler.link_connections[ax25_frame.addr_uid][0] - link_call = self._port_handler.link_connections[ax25_frame.addr_uid][1] + """ + if uid in self._port_handler.link_connections.keys(): + logger.debug(self._logTag + f"Link-Conn RX: {uid}") + conn = self._port_handler.link_connections[uid][0] + link_call = str(self._port_handler.link_connections[uid][1]) + """ + logger.debug(self._logTag + f"Link-Conn RX link_call: {link_call}") + logger.debug(self._logTag + f"Link-Conn RX is_link_remote: {conn.is_link_remote}") + logger.debug(self._logTag + f"Link-Conn RX digi_call: {conn.digi_call}") + logger.debug(self._logTag + f"Link-Conn RX my_call_str: {conn.my_call_str}") + logger.debug(self._logTag + f"Link-Conn RX self._port_handler.link_connections: {self._port_handler.link_connections}") + logger.debug(self._logTag + f"Link-Conn RX ++: {ax25_frame_conf}") + """ if link_call: - if ax25_frame.digi_check_and_encode(call=link_call, h_bit_enc=True): - conn.handle_rx(ax25_frame=ax25_frame) - return True - conn.handle_rx(ax25_frame=ax25_frame) - return True + # if link_call != ax25_frame.via_calls[-1].call_str: + # return False + + # for call in ax25_frame.via_calls: + # if not ax25_frame.digi_check_and_encode(call=link_call, h_bit_enc=False): + if not is_digipeated_pre_digi(ax25_frame_conf, link_call): + logger.debug(self._logTag + f"Link-Conn RX No DIGI chk --: {ax25_frame_conf}") + return False + # if call.call_str == link_call: + logger.debug(self._logTag + f"Link-Conn RX --: {ax25_frame_conf}") + conn.handle_rx(ax25_frame=ax25_frame) + return True + # if ax25_frame.digi_check_and_encode(call=link_call, h_bit_enc=True): + # conn.handle_rx(ax25_frame=ax25_frame) + # return True return False def _rx_pipe_handler(self, ax25_frame): @@ -269,9 +294,6 @@ def _rx_new_conn_handler(self, ax25_frame): def _rx_digi_handler(self, ax25_frame): if not POPT_CFG.get_digi_CFG(): return False - # if ax25_frame.ctl_byte.flag == 'UI': - # return self._rx_simple_digi(ax25_frame) - # if self.is_smart_digi: return self._rx_managed_digi(ax25_frame) def _rx_simple_digi(self, ax25_frame): @@ -281,50 +303,124 @@ def _rx_simple_digi(self, ax25_frame): if POPT_CFG.get_digi_is_enabled(call.call_str): if ax25_frame.digi_check_and_encode(call=call.call_str, h_bit_enc=True): self._digi_buf.append(ax25_frame) + logger.debug(f"Simple DIGI: {ax25_frame.get_frame_conf()}") # self.set_digi_TXD() return True return False def _rx_managed_digi(self, ax25_frame): self._cleanup_digi_conn() + # get_digi_CFG + digi_conf = dict(POPT_CFG.get_digi_CFG()) + ax25_conf = dict(ax25_frame.get_frame_conf()) + uid = ax25_conf.get('uid', '') + if not uid: + return False + + for call in ax25_frame.via_calls: - digi_conf = dict(POPT_CFG.get_digi_CFG_for_Call(call.call_str)) - if digi_conf.get('digi_enabled', False): - if ax25_frame.digi_check_and_encode(call=call.call_str, h_bit_enc=True): - if digi_conf.get('managed_digi', False): - if ax25_frame.addr_uid not in self._digi_connections.keys(): - ax25_conf = ax25_frame.get_frame_conf() - digi_conf.update(dict( - rx_port=self, - digi_call=str(call.call_str), - digi_ssid=int(call.ssid), - ax25_conf=ax25_conf - )) - # print(digi_conf) - if ax25_frame.ctl_byte.flag == 'UI': - AX25DigiConnection(digi_conf).digi_rx_handle(ax25_frame) - return - - self._digi_connections[str(ax25_frame.addr_uid)] = AX25DigiConnection(digi_conf) - self._digi_connections[str(ax25_frame.addr_uid)].digi_rx_handle(ax25_frame) + if call.call not in digi_conf.keys(): + continue + tmp_cfg = dict(digi_conf.get(call.call, {})) + if not tmp_cfg.get('digi_enabled', False): + continue + # if ax25_frame.digi_check_and_encode(call=call.call_str, h_bit_enc=True): + if is_digipeated_pre_digi(ax25_conf=dict(ax25_conf), call=str(call.call_str)): + if tmp_cfg.get('managed_digi', False): + if uid not in self._digi_connections.keys(): + tmp_cfg.update(dict( + rx_port=self, + digi_call=str(call.call_str), + digi_ssid=int(call.ssid), + ax25_conf=dict(ax25_conf) + )) + if ax25_frame.ctl_byte.flag == 'UI': + logger.debug(self._logTag + "_rx_managed_digi NewUI") + AX25DigiConnection(tmp_cfg).digi_rx_handle(ax25_frame) + return True + # New Digi Conn + if ax25_frame.digi_check_and_encode(call=call.call_str, h_bit_enc=True): + logger.debug(self._logTag + f" NewDigiConn: tmp_cfg {tmp_cfg}") + logger.debug(self._logTag + f" NewDigiConn: digi_conn {self._digi_connections.keys()}") + logger.debug(self._logTag + f" NewDigiConn: conn {self.connections.keys()}") + logger.debug(self._logTag + f" NewDigiConn: ax25_conf {ax25_conf}") + digi_conn = AX25DigiConnection(dict(tmp_cfg)) + digi_conn.digi_rx_handle(ax25_frame) + self._digi_connections[uid] = digi_conn + + return True + logger.error(self._logTag + f" NewDigiConn: not ax25_frame.digi_check_and_encode") + logger.error(self._logTag + f" NewDigiConn: tmp_cfg {tmp_cfg}") + logger.error(self._logTag + f" NewDigiConn: digi_conn {self._digi_connections.keys()}") + logger.error(self._logTag + f" NewDigiConn: conn {self.connections.keys()}") + logger.error(self._logTag + f" NewDigiConn: ax25_conf {ax25_conf}") + return True + + if ax25_frame.digi_check_and_encode(call=call.call_str, h_bit_enc=True): + logger.debug(self._logTag + f" DigiConn: tmp_cfg {tmp_cfg}") + logger.debug(self._logTag + f" DigiConn: digi_conn {self._digi_connections.keys()}") + logger.debug(self._logTag + f" DigiConn: conn {self.connections.keys()}") + logger.debug(self._logTag + f" DigiConn: ax25_conf {ax25_conf}") + self._digi_connections[uid].digi_rx_handle(ax25_frame) return True - self.add_frame_to_digiBuff(ax25_frame) + + logger.error(self._logTag + f" DigiConn: not ax25_frame.digi_check_and_encode") + logger.error(self._logTag + f" DigiConn: tmp_cfg {tmp_cfg}") + logger.error(self._logTag + f" DigiConn: digi_conn {self._digi_connections.keys()}") + logger.error(self._logTag + f" DigiConn: conn {self.connections.keys()}") + logger.error(self._logTag + f" DigiConn: ax25_conf {ax25_conf}") return True - return False + + if ax25_conf.get('uid', '') not in self._digi_connections.keys(): + # logger.debug(self._logTag + f" S-Digi: digi_check_and_encode {ax25_frame.digi_check_and_encode(call=call.call_str, h_bit_enc=True)}") + if ax25_frame.digi_check_and_encode(call=call.call_str, h_bit_enc=True): + logger.debug(self._logTag + f" S-Digi: tmp_cfg {tmp_cfg}") + logger.debug(self._logTag + f" S-Digi: digi_conn {self._digi_connections.keys()}") + logger.debug(self._logTag + f" S-Digi: conn {self.connections.keys()}") + logger.debug(self._logTag + f" S-Digi: ax25_conf {ax25_conf}") + self.add_frame_to_digiBuff(ax25_frame) + return True + logger.error(self._logTag + f" S-Digi: not ax25_frame.digi_check_and_encode") + logger.error(self._logTag + f" S-Digi: tmp_cfg {tmp_cfg}") + logger.error(self._logTag + f" S-Digi: digi_conn {self._digi_connections.keys()}") + logger.error(self._logTag + f" S-Digi: conn {self.connections.keys()}") + logger.error(self._logTag + f" S-Digi: ax25_conf {ax25_conf}") + return True + + return False return False - def accept_digi_conn(self, uid: str): - if uid not in self._digi_connections.keys(): - logger.debug('Port: accept_digi_conn: UID ERROR') - logger.debug(f'Port: accept_digi_conn uid: {uid}') - logger.debug(f'Port: accept_digi_conn keys: {self._digi_connections.keys()}') + def add_digi_conn(self, digi_connection): + tx_uid = str(digi_connection.get_tx_uid()) + if tx_uid in self._digi_connections: + logger.error(self._logTag + f"add_digi_conn - uid in _digi_connections") + logger.error(self._logTag + f"self._digi_connections {self._digi_connections}") + logger.error(self._logTag + f"rx_uid {tx_uid}") return False - self._digi_connections[uid].add_rx_conn_cron() - return True + logger.debug(self._logTag + f"add_digi_conn +++ tx_uid {tx_uid}") + self._digi_connections[tx_uid] = digi_connection + + + def accept_digi_conn(self, uid: str): + """ + if uid in self._digi_connections.keys(): + logger.debug(f'Port: accept_digi_conn: {uid}') + return self._digi_connections[uid].add_rx_conn_cron() + """ + uid = reverse_uid(uid) + if uid in self._digi_connections.keys(): + logger.debug(f'Port: accept_digi_conn reverse: {uid}') + return self._digi_connections[uid].add_rx_conn_cron() + + logger.error('Port: accept_digi_conn: UID ERROR') + logger.error(f'Port: accept_digi_conn uid: {uid}') + logger.error(f'Port: accept_digi_conn keys: {self._digi_connections.keys()}') + return False + # return True def delete_digi_conn(self, uid: str): if uid in self._digi_connections.keys(): - # print(f"DIGI-Conn DEL: {uid}") + print(f"DIGI-Conn DEL: {uid}") del self._digi_connections[uid] def _cleanup_digi_conn(self): @@ -339,7 +435,8 @@ def _digi_task(self): self._cleanup_digi_conn() for uid, digi_conn in list(self._digi_connections.items()): # print(f"DIGI-CONN TASK: {uid}") - digi_conn.digi_crone() + if digi_conn: + digi_conn.digi_crone() def get_digi_conn(self): return self._digi_connections @@ -630,8 +727,7 @@ def _task_Port(self): def _task_connections(self): """ Execute Cronjob on all Connections""" - for k in list(self.connections.keys()): - conn = self.connections.get(k, None) + for uid, conn in dict(self.connections).items(): if conn: conn.exec_cron() @@ -835,8 +931,8 @@ def send_UI_frame(self, def _reset_ft_wait_timer(self, ax25_frame): if ax25_frame.ctl_byte.flag in ['I', 'SABM', 'DM', 'DISC', 'REJ', 'UA', 'UI']: - for k in self.connections.keys(): - self.connections[k].ft_reset_timer(ax25_frame.addr_uid) + for uid, conn in self.connections.items(): + conn.ft_reset_timer(ax25_frame.addr_uid) def _gui_monitor(self, ax25frame, tx: bool = True): if self.monitor_out: @@ -866,7 +962,7 @@ def _tasks(self): while self.loop_is_running: try: ############################################## - buf: RxBuf = self.rx() + buf: RxBuf = self._rx() ############################################## except AX25DeviceERROR: # print(e) @@ -877,10 +973,10 @@ def _tasks(self): # self.close() break if buf is None: - time.sleep(0.05) + # time.sleep(0.05) break if not buf.raw_data: # RX ############ - time.sleep(0.05) + # time.sleep(0.05) break self.set_TXD() self.set_digi_TXD() @@ -889,32 +985,34 @@ def _tasks(self): # Decoding ax25frame.decode_ax25frame(buf.raw_data) except AX25DecodingERROR: - logger.error(f'Port {self.port_id}: decoding: ') - logger.error(f'Port {self.port_id}: org {buf.raw_data}') - logger.error(f'Port {self.port_id}: hex {bytearray2hexstr(buf.raw_data)}') + logger.warning("-------------------------------------------------------------------") + logger.warning(f'Port {self.port_id}: decoding: ') + logger.warning(f'Port {self.port_id}: org {buf.raw_data}') + logger.warning(f'Port {self.port_id}: hex {bytearray2hexstr(buf.raw_data)}') + logger.warning(f'Port {self.port_id}: kiss-org {buf.kiss_frame}') + logger.warning(f'Port {self.port_id}: kiss-hex {bytearray2hexstr(buf.kiss_frame)}') + logger.warning("-------------------------------------------------------------------") break - if ax25frame.validate(): - ax25frame.axip_add = buf.axip_add - # ax25frame.rx_time = datetime.datetime.now() - # setattr(ax25frame, 'rx_time', datetime.datetime.now()) - # ######### RX ############# - if not self._rx_dualPort_handler(ax25_frame=ax25frame): - ax25frame_conf = ax25frame.get_frame_conf() - # Monitor # TODO handling via ax25frame_conf - self._gui_monitor(ax25frame=ax25frame, tx=False) - # MH / Port-Statistic - self._mh_input(ax25frame_conf, tx=False) - # MCast IP Update - if hasattr(self._mcast_server, 'mcast_update_member_ip'): - self._mcast_server.mcast_update_member_ip(ax25frame=ax25frame) - # RX Handler - self.rx_handler(ax25frame) - # MCast - if hasattr(self._mcast_server, 'mcast_rx'): - self._mcast_server.mcast_rx(ax25frame=ax25frame) - - # RX-ECHO - self._rx_echo(ax25_frame=ax25frame) + ######## if ax25frame.validate(): + ax25frame.axip_add = buf.axip_add + # ######### RX ############# + if not self._rx_dualPort_handler(ax25_frame=ax25frame): + ax25frame_conf = ax25frame.get_frame_conf() + # Monitor # TODO handling via ax25frame_conf + self._gui_monitor(ax25frame=ax25frame, tx=False) + # MH / Port-Statistic + self._mh_input(ax25frame_conf, tx=False) + # MCast IP Update + if hasattr(self._mcast_server, 'mcast_update_member_ip'): + self._mcast_server.mcast_update_member_ip(ax25frame=ax25frame) + # RX Handler + self.rx_handler(ax25frame) + # MCast + if hasattr(self._mcast_server, 'mcast_rx'): + self._mcast_server.mcast_rx(ax25frame=ax25frame) + + # RX-ECHO + self._rx_echo(ax25_frame=ax25frame) if self._port_cfg.get('parm_full_duplex', False): break @@ -1008,25 +1106,26 @@ def set_kiss_parm(self): logger.error('{}'.format(e)) raise AX25DeviceFAIL - def rx(self): + def _rx(self): + self.port_w_dog = time.time() try: recv_buff = self.device.recv(999) except socket.timeout: - # self.device.close() - # raise AX25DeviceERROR(e, self) - # raise AX25DeviceERROR return None except OSError as e: raise AX25DeviceERROR(e, self) - ret = RxBuf() if recv_buff: de_kiss_fr = self.kiss.de_kiss(recv_buff) - if de_kiss_fr: - ret.raw_data = de_kiss_fr + if de_kiss_fr is not None: + ret = RxBuf() + ret.raw_data = bytes(de_kiss_fr) + ret.kiss_frame = bytes(recv_buff) return ret + if self.kiss.unknown_kiss_frame(recv_buff): + return None else: - return ret + return None def tx_device(self, frame): try: @@ -1050,17 +1149,13 @@ class KISSSerial(AX25Port): def init(self): if self.loop_is_running: - # print("KISS Serial INIT") logger.info("KISS Serial INIT") - # self.kiss = b'\x00' try: self.device = serial.Serial(self._port_param[0], self._port_param[1], timeout=0.2) self.device_is_running = True except (FileNotFoundError, serial.serialutil.SerialException) as e: - # print('Error. Cant connect to KISS Serial Device {}'.format(self._port_param)) logger.error(f'Port {self.port_id}: Error. Cant connect to KISS Serial Device {self._port_param}') logger.error('{}'.format(e)) - # print('{}'.format(e)) self.close_device() raise AX25DeviceFAIL else: @@ -1071,7 +1166,7 @@ def init(self): # self.device.flush() try: self.device.write(self.kiss.device_kiss_start_1()) - self.device.readall() + logger.info(f"Port {self.port_id}: TNC-MSG: {self.device.readall().decode('UTF-8', 'ignore')}") self.set_kiss_parm() except (FileNotFoundError, serial.serialutil.SerialException, AX25DeviceFAIL) as e: logger.error( @@ -1105,17 +1200,25 @@ def _close_dev(self): if self.kiss.is_enabled: if self.kiss.device_kiss_end(): self.device.write(self.kiss.device_kiss_end()) + time.sleep(1) + """ if is_linux(): try: self.device.flush() except termios.error: - pass + logger.warning(self._logTag + f"Error while closing/flushing: termios.error") + else: + self.device.flush() + """ + time.sleep(1) + if is_linux(): + logger.info(self._logTag + f"TNC - REST: {self.device.readall()}") else: self.device.flush() self.device.close() # self.device_is_running = False - except (FileNotFoundError, serial.serialutil.SerialException, OSError, TypeError): - pass + except (FileNotFoundError, serial.serialutil.SerialException, OSError, TypeError) as e: + logger.warning(self._logTag + f"Error while closing: {e}") self.device_is_running = False def set_kiss_parm(self): @@ -1129,33 +1232,38 @@ def set_kiss_parm(self): self.close_device() raise AX25DeviceFAIL - def rx(self): + def _rx(self): recv_buff = b'' while self.loop_is_running and self.device_is_running: + self.port_w_dog = time.time() try: recv_buff += self.device.read() - except serial.SerialException: # There is no new data from serial port - return RxBuf() + return None except TypeError as e: logger.warning(f'Port {self.port_id}: Serial Device Error {e}') try: # self.init() self._reinit() + return None except AX25DeviceFAIL: self.close_device() logger.error(f"Port {self.port_id}: Reinit Failed !! {self._port_param}") raise AX25DeviceERROR - else: - ret = RxBuf() - if recv_buff: - de_kiss_fr = self.kiss.de_kiss(recv_buff) - if de_kiss_fr: # TODO !!!! flush buffer ? - ret.raw_data = de_kiss_fr - return ret - else: + + if recv_buff: + de_kiss_fr = self.kiss.de_kiss(recv_buff) + if de_kiss_fr is not None: + ret = RxBuf() + ret.raw_data = bytes(de_kiss_fr) + ret.kiss_frame = bytes(recv_buff) return ret + if self.kiss.unknown_kiss_frame(recv_buff): + recv_buff = b'' + + else: + return None def tx_device(self, frame): if self.device is None: @@ -1249,9 +1357,11 @@ def close_device(self): # print("AXIP FINALLY") logger.info(f"Port {self.port_id}: Close AXIP done") - def rx(self): + def _rx(self): + self.port_w_dog = time.time() try: udp_recv = self.device.recvfrom(800) + # self.device.settimeout(0.1) # except socket.error: # raise AX25DeviceERROR @@ -1270,7 +1380,7 @@ def rx(self): ret.axip_add = to_call_ip_addr if calc_crc == crc: ret.raw_data = pack - ret.kiss = b'' + ret.kiss_frame = b'' return ret def tx_device(self, frame, multicast=True): diff --git a/ax25/ax25dec_enc.py b/ax25/ax25dec_enc.py index f8242bb3..fda04acb 100644 --- a/ax25/ax25dec_enc.py +++ b/ax25/ax25dec_enc.py @@ -134,7 +134,7 @@ def validate(self): """ :return: bool """ - if len(self.call) < 2 or len(self.call) > 6: # Calls like CQ or ID + if 2 > len(self.call) > 6: # Calls like CQ or ID # print(f'Call validator: Call length - {self.call}') logger.error(f'Call validator: Call length - {self.call}') return False @@ -143,12 +143,12 @@ def validate(self): logger.error('Call validator: Call.isascii()') return False """ - if self.ssid > 15 or self.ssid < 0: + if 0 > self.ssid > 15: # print(f'Call validator: SSID - {self.ssid}') logger.error(f'Call validator: SSID - {self.ssid}') return False for c in self.call: - if not any([c.isupper(), c.isdigit()]): + if not any((c.isupper(), c.isdigit())): # print(f'Call validator: CAll-Format - {self.call} -') logger.error(f'Call validator: CAll-Format - {self.call} -') return False @@ -303,7 +303,7 @@ def dec_cbyte(self, in_byte): logger.error('C-Byte Error Decoding U Frame ! Unknown C-Byte> ' + str(bi) + ' ' + hex(in_byte)) raise AX25DecodingERROR - def validate(self): + def validate_c(self): if self.hex == 0xff: logger.error('C_Byte validator') return False @@ -451,7 +451,7 @@ def decode(self, in_byte: b''): if bi[2:5] in ['01', '10']: self.ax25_l3(hex(int(in_byte))) - def validate(self): + def validate_pid(self): if self.hex == 0x00: logger.error('PID_Byte validator : {}'.format(self.hex)) return False @@ -543,6 +543,7 @@ def __init__(self, conf=None): self._netrom_cfg = {} def get_frame_conf(self): + return dict( uid=str(self.addr_uid), axip_add=(str(self.axip_add[0]), int(self.axip_add[1])), @@ -709,7 +710,7 @@ def decode_ax25frame(self, hexstr=b''): if not self.validate(): raise AX25DecodingERROR(self) - # self._decode_netrom() + self._decode_netrom() else: raise AX25DecodingERROR(self) @@ -832,13 +833,13 @@ def validate(self): # print('Validate Error: ca.validate') AX25EncodingERROR(self) return False - if not self.ctl_byte.validate(): + if not self.ctl_byte.validate_c(): # print('Validate Error: C_Byte') logger.error('Validate Error: C_Byte') AX25EncodingERROR(self) return False if self.ctl_byte.pid: - if not self.pid_byte.validate(): + if not self.pid_byte.validate_pid(): # print('Validate Error: PID_Byte') logger.error('Validate Error: PID_Byte') AX25EncodingERROR(self) diff --git a/ax25/ax25monitor.py b/ax25/ax25monitor.py index b742795a..8e5d38dd 100644 --- a/ax25/ax25monitor.py +++ b/ax25/ax25monitor.py @@ -1,4 +1,4 @@ -#from ax25.ax25NetRom import NetRom_decode_I, NetRom_decode_UI_mon +from ax25.ax25NetRom import NetRom_decode_I, NetRom_decode_UI_mon # import logging # from ax25.ax25NetRom import NetRom_decode_I from ax25aprs.aprs_dec import format_aprs_f_monitor @@ -26,7 +26,7 @@ def monitor_frame_inp(ax25_frame_conf: dict, port_cfg, decoding='Auto'): rx_time = ax25_frame_conf.get('rx_time') payload_len = ax25_frame_conf.get('payload_len', 0) payload = ax25_frame_conf.get('payload', b'') - # netrom_cfg = ax25_frame_conf.get('netrom_cfg', {}) + netrom_cfg = ax25_frame_conf.get('netrom_cfg', {}) if ctl_flag == 'UI': aprs_data = format_aprs_f_monitor(ax25_frame_conf, own_locator=own_loc) @@ -58,20 +58,20 @@ def monitor_frame_inp(ax25_frame_conf: dict, port_cfg, decoding='Auto'): if int(pid_hex, 16) else '' out_str += ' len={}\n'.format(payload_len) if payload_len else '\n' # ======= Old NetRom - """ + if netrom_cfg: # Net-Rom if ctl_flag == 'UI': data = NetRom_decode_UI_mon(ax25_frame_conf) out_str += data return out_str, aprs_data - """ + # ======= DEV Inp-NetRom/L3-NetRom TODO move decoding call to ax25ecn_dec - """ + if ctl_flag == 'I' and pid_hex == '0xcf': data = NetRom_decode_I(payload) out_str += data return out_str, aprs_data - """ + # if payload: if type(payload) is bytes: if decoding == 'Auto': diff --git a/ax25aprs/aprs_station.py b/ax25aprs/aprs_station.py index 81e2bb5e..490e6b43 100644 --- a/ax25aprs/aprs_station.py +++ b/ax25aprs/aprs_station.py @@ -50,6 +50,7 @@ def __init__(self, load_cfg=True): self.be_tracer_interval = ais_cfg.get('be_tracer_interval', 5) self.be_tracer_port = ais_cfg.get('be_tracer_port', 0) self.be_tracer_station = ais_cfg.get('be_tracer_station', 'NOCALL') + self.be_tracer_via = ais_cfg.get('be_tracer_via', []) self.be_tracer_wide = ais_cfg.get('be_tracer_wide', 1) self.be_tracer_alarm_active = ais_cfg.get('be_tracer_alarm_active', False) self.be_tracer_alarm_range = ais_cfg.get('be_tracer_alarm_range', 50) @@ -130,6 +131,7 @@ def save_conf_to_file(self): ais_cfg['be_tracer_interval'] = int(self.be_tracer_interval) ais_cfg['be_tracer_port'] = int(self.be_tracer_port) ais_cfg['be_tracer_station'] = str(self.be_tracer_station) + ais_cfg['be_tracer_via'] = list(self.be_tracer_via) ais_cfg['be_tracer_wide'] = int(self.be_tracer_wide) ais_cfg['be_tracer_alarm_active'] = bool(self.be_tracer_alarm_active) ais_cfg['be_tracer_alarm_range'] = int(self.be_tracer_alarm_range) @@ -275,6 +277,12 @@ def ais_rx_task(self): blocking=True, immortal=False, raw=False) + # TODO: LogSpam + """ + 2024-12-23 15:37:54,425 - ERROR - aprslib.inet.IS: socket error on recv(): [WinError 10054] Eine vorhandene Verbindung wurde vom Remotehost geschlossen + 2024-12-23 15:37:54,425 - ERROR - aprslib.inet.IS: socket error on recv(): [WinError 10054] Eine vorhandene Verbindung wurde vom Remotehost geschlossen + 2024-12-23 15:37:54,425 - ERROR - aprslib.inet.IS: socket error on recv(): [WinError 10054] Eine vorhandene Verbindung wurde vom Remotehost geschlossen + """ except ValueError: # print("APRS-Consumer ValueError") logger.error("APRS-IS: Consumer ValueError") @@ -709,10 +717,12 @@ def _tracer_build_pack(self): port_id = int(self.be_tracer_port) station_call = str(self.be_tracer_station) wide = f'WIDE{self.be_tracer_wide}-{self.be_tracer_wide}' + path = ','.join(list(self.be_tracer_via) + [wide]) + # dest = APRS_SW_ID if station_call in self._port_handler.get_stat_calls_fm_port(port_id): - add_str = f'{station_call}>{APRS_SW_ID},{wide}:' + add_str = f'{station_call}>{APRS_SW_ID},{path}:' msg = self._tracer_build_msg() aprs_raw = add_str + msg aprs_pack = parse_aprs_fm_aprsframe(aprs_raw) diff --git a/cfg/cfg_fnc.py b/cfg/cfg_fnc.py index 361eddce..7167d42c 100644 --- a/cfg/cfg_fnc.py +++ b/cfg/cfg_fnc.py @@ -47,22 +47,23 @@ def cleanup_obj_dict(inp_dict: dict): def save_to_file(filename: str, data): + old_cfg = load_fm_file(filename) + try: - with open(CFG_data_path + filename, 'wb') as f: - pickle.dump(data, f, 2) + with open(CFG_data_path + filename, 'wb') as file: + pickle.dump(data, file, 2) except FileNotFoundError: - with open(CFG_data_path + filename, 'xb') as f: - pickle.dump(data, f, 2) - except EOFError as e: - # print(f"save_to_file Error: {e}") - logger.error(f"save_to_file Error: {e}") - logger.error(f"save_to_file Error: {data}") - # print(f"save_to_file Error: {data}") - except TypeError as e: + with open(CFG_data_path + filename, 'xb') as file: + pickle.dump(data, file, 2) + except (EOFError, TypeError) as e: # print(f"save_to_file Error: {e}") logger.error(f"save_to_file Error: {e}") logger.error(f"save_to_file Error: {data}") # print(f"save_to_file Error: {data}") + if old_cfg: + logger.info(f"save_to_file Error: Backup old CFG") + save_to_file(filename, old_cfg) + def load_fm_file(filename: str): @@ -76,35 +77,6 @@ def load_fm_file(filename: str): f"CFG: Falsche Version der CFG Datei. Bitte {CFG_data_path + filename} löschen und PoPT neu starten!") raise -""" -def get_all_pipe_cfg(): - stat_cfg_path = CFG_data_path + CFG_usertxt_path - stat_cfg = [x[0] for x in os.walk(stat_cfg_path)] - ret = {} - if len(stat_cfg) > 1: - stat_cfg = stat_cfg[1:] - for folder in stat_cfg: - call = folder.split('/')[-1] - temp = {} - try: - with open(folder + '/stat' + call + '.popt', 'rb') as inp: - temp = pickle.load(inp) - except (FileNotFoundError, EOFError): - pass - except ImportError: - logger.error( - f"Pipe CFG: Falsche Version der CFG Datei. Bitte {folder + '/stat' + call + '.popt'} löschen und PoPT neu starten!") - pass - if temp and call: - loaded_pipe_cfg = temp.get('pipe_cfg', {}) - if loaded_pipe_cfg: - default_pipe_cfg = getNew_pipe_cfg() - for cfg_keys in list(loaded_pipe_cfg.keys()): - default_pipe_cfg[cfg_keys] = loaded_pipe_cfg[cfg_keys] - ret[call] = default_pipe_cfg - return ret -""" - def get_all_stat_CFGs(): stat_cfg_path = CFG_data_path + CFG_usertxt_path stat_cfg = [x[0] for x in os.walk(stat_cfg_path)] @@ -141,19 +113,6 @@ def save_station_CFG_to_file(conf: dict): save_to_file(file, conf) return True -""" -def save_station_to_file(conf): - if conf.stat_parm_Call != getNew_station_cfg().get('stat_parm_Call', ''): - exist_userpath(conf.stat_parm_Call) - file = '{1}{0}/stat{0}.popt'.format(conf.stat_parm_Call, CFG_usertxt_path) - save_station = {} - for att in dir(conf): - if '__' not in att and not callable(getattr(conf, att)): - save_station[att] = getattr(conf, att) - - save_to_file(file, save_station) -""" - def del_user_data(call: str): if not call: @@ -197,6 +156,7 @@ def init_dir_struct(): ############################### # Port CFGs +""" def load_port_cfg_fm_file(port_id: int): file = CFG_data_path + f'port{port_id}.popt' try: @@ -209,6 +169,7 @@ def load_port_cfg_fm_file(port_id: int): logger.error( f"Port CFG: Falsche Version der CFG Datei. Bitte {file} löschen und PoPT neu starten!") raise +""" def load_all_port_cfg_fm_file(): ret = {} diff --git a/cfg/constant.py b/cfg/constant.py index ae8aac10..0162d6d3 100644 --- a/cfg/constant.py +++ b/cfg/constant.py @@ -1,11 +1,12 @@ """ Mach mit, mach nach, -mach besser... +mach besser. """ -VER = '2.113.10' +VER = '2.114.33' + DEBUG_LOG = True -CONSOLE_LOG = False +CONSOLE_LOG = True """ Custom TNC KISSMODE INIT """ TNC_KISS_CMD = b'\x1b@K\r' # Custom Command for setting TNC to Kiss Mode TNC_KISS_CMD_END = b'\xc0\xff\xc0' # Custom Command for stop TNC Kiss Mode @@ -47,7 +48,8 @@ # 'PL': 5, # 'PT': 6, # 'IT': 7, - # '': 8, + # 'RU': 8, + # 'UA': 9, } STATION_TYPS = [ @@ -83,6 +85,7 @@ 'TOP', 'PoPT', 'HSGT', + 'HSGTerm', 'Paxon', ] @@ -112,6 +115,8 @@ 'ASCII', 'LATIN_1', 'UTF-8', + 'KOI8-R', + 'KOI8-U', ) STATION_ID_ENCODING = { @@ -119,7 +124,9 @@ 1: 'ASCII', # TODO Eigentlich ä > ae 2: 'ASCII', # TODO Eigentlich c64 Zeichensatz 3: 'LATIN_1', - 4: 'UTF-8' + 4: 'UTF-8', + 5: 'KOI8-R', + 6: 'KOI8-U', } STATION_ID_ENCODING_REV = { @@ -127,7 +134,9 @@ 'ASCII': 1, # TODO Eigentlich ä > ae # 'ASCII': 2, # TODO Eigentlich c64 Zeichensatz 'LATIN_1': 3, - 'UTF-8': 4 + 'UTF-8': 4, + 'KOI8-R': 5, + 'KOI8-U': 6, } # FT Stuff @@ -167,8 +176,6 @@ DEF_TEXTSIZE = 13 FONT = "Courier" TEXT_SIZE_STATUS = 10 -TXT_BACKGROUND_CLR = 'black' -TXT_OUT_CLR = 'red' TXT_INP_CURSOR_CLR = 'white' STAT_BAR_CLR = 'grey60' STAT_BAR_TXT_CLR = 'black' @@ -224,14 +231,20 @@ b'KISSM\r', b'KISS ON\r', b'KISS ON\rrestart\r', + b'\x11\x18\x1bJHOST1\r', TNC_KISS_CMD ] TNC_KISS_END_CMD = [ b'\xc0\xff\xc0', + b'\xc0\xff\xc0\rrestart\r', + b'KISS OFF\rrestart\r', + b'restart\r', + b'\x11\x18\x1bJHOST0\r', TNC_KISS_CMD_END ] +######################################### POPT_BANNER = '\r$$$$$$$\ $$$$$$\ $$$$$$$\ $$$$$$$$|\r' \ '$$ __$$\ $$ __$$\ $$ __$$\|__$$ __|\r' \ '$$ | $$ |$$ / $$ | $$ | $$ | $$ |\r' \ @@ -318,3 +331,22 @@ 'BEACON', 'APZPOP', ] + + +####################################################### +# 1-Wirer +ONE_WIRE_PATH = '/sys/devices/w1_bus_master1' +ONE_WIRE_MAP = (" PI 3/4 GPIO\n" + "GND(Pin 6/9) - 3.3V(Pin 1) - Data(Pin 7)\n" + " | ________|-- 4,7kOhm --|\n" + " | | |\n" + " |-DS18B20-------------------|\n" + " | | |\n" + " |-DS18B20-------------------|\n" + " | | |\n") + +####################################################### +# GPIO +GPIO_PATH = '/sys/class/gpio' +GPIO_RANGE = (0, 26) + diff --git a/cfg/default_config.py b/cfg/default_config.py index 0a7c524a..0c0510f2 100644 --- a/cfg/default_config.py +++ b/cfg/default_config.py @@ -127,6 +127,7 @@ def getNew_APRS_ais_cfg(): 'be_tracer_interval': 5, 'be_tracer_port': 0, 'be_tracer_station': 'NOCALL', + 'be_tracer_via': [], 'be_tracer_wide': 1, 'be_tracer_alarm_active': False, 'be_tracer_alarm_range': 50, @@ -268,4 +269,18 @@ def getNew_mcast_channel_cfg(channel_id: int): ch_name='Lobby', ch_private=False, ch_members=[], - ) \ No newline at end of file + ) +##################################################### +# 1Wire +def getNew_1wire_cfg(): + return dict( + loop_timer=60, + sensor_cfg={}, + ) + +def getNew_1wire_device_cfg(device_path: str): + return dict( + device_path=str(device_path), + device_value=None, + StringVar='', + ) diff --git a/cfg/popt_config.py b/cfg/popt_config.py index 2db0a9f0..a26f7bcc 100644 --- a/cfg/popt_config.py +++ b/cfg/popt_config.py @@ -1,16 +1,12 @@ from cfg.default_config import getNew_PMS_cfg, getNew_homeBBS_cfg, getNew_maniGUI_parm, \ getNew_APRS_ais_cfg, getNew_MH_cfg, getNew_digi_cfg, getNew_station_cfg, getNew_port_cfg, getNew_mcast_cfg, \ - getNew_mcast_channel_cfg + getNew_mcast_channel_cfg, getNew_1wire_cfg from cfg.constant import CFG_MAIN_data_file, MAX_PORTS from cfg.cfg_fnc import load_fm_file, save_to_file, get_all_stat_CFGs, del_user_data, \ save_station_CFG_to_file, load_all_port_cfg_fm_file, save_all_port_cfg_to_file from cfg.logger_config import logger -def getNew_dict(): - return {} - - class Main_CFG: def __init__(self): logger.info('Main CFG: Init') @@ -34,7 +30,7 @@ def __init__(self): # -- GUI # GUI Main 'gui_main_parm': getNew_maniGUI_parm, - 'gui_channel_vars': getNew_dict, + 'gui_channel_vars': {}, 'gui_pacman': {}, ########################## # -- Beacon @@ -58,6 +54,9 @@ def __init__(self): ########################## # -- MCast CFG 'mcast_cfg': getNew_mcast_cfg, + ########################## + # -- 1Wire CFG + '1wire_cfg': getNew_1wire_cfg, } """ Main CFGs """ self._load_CFG_fm_file() # Other Configs @@ -87,6 +86,8 @@ def __init__(self): logger.info(f'Main CFG: load {conf_k} - Size: {len(conf)} - str_size: {len(str(conf))}') logger.info(f'-------- Loaded CFGs ENDE --------') logger.info('Main CFG: Init complete') + ### DEV ################################################ + # self._config['1wire_cfg'] = getNew_1wire_cfg() #################### @@ -116,8 +117,12 @@ def _update_old_CFGs(self): def _load_CFG_fm_file(self): logger.info(f'Main CFG: Load from {self._config_filename}') # print(f'Main CFG: Load from {self._config_filename}') - config = load_fm_file(self._config_filename) + config: dict = load_fm_file(self._config_filename) if config: + """ + for cfg_name, cfg in config.items(): + logger.debug(f"Main CFG:{cfg_name}> {cfg}") + """ self._config = dict(config) else: logger.warning("Main CFG: MainConfig wasn't found. Generating new Default Configs !! ") @@ -265,26 +270,26 @@ def set_CFG_aprs_ais(self, data: dict): ######################################################## # GUI def get_guiCFG_language(self): - return self._config['gui_main_parm'].get('gui_lang', 0) + return int(self._config['gui_main_parm'].get('gui_lang', 0)) # return LANGUAGE gui_cfg_locator def get_guiCFG_locator(self): - return self._config['gui_main_parm'].get('gui_cfg_locator', '') + return str(self._config['gui_main_parm'].get('gui_cfg_locator', '')) def get_guiCFG_qth(self): - return self._config['gui_main_parm'].get('gui_cfg_qth', '') + return str(self._config['gui_main_parm'].get('gui_cfg_qth', '')) # GUI PARM def load_guiPARM_main(self): - return self._config['gui_main_parm'] + return dict(self._config['gui_main_parm']) def save_guiPARM_main(self, data: dict): - self._config['gui_main_parm'] = data + self._config['gui_main_parm'] = dict(data) def set_guiPARM_main(self, data: dict): if not data: return False - conf_data = self._config.get('gui_main_parm', getNew_maniGUI_parm()) + conf_data = dict(self._config.get('gui_main_parm', getNew_maniGUI_parm())) for conf_k in list(data.keys()): if conf_k in list(conf_data.keys()): # if type(data[conf_k]) is type(conf_k[conf_k]): @@ -296,10 +301,10 @@ def get_guiPARM_main_param_by_key(self, conf_key: str): # Channel Vars def load_guiCH_VARS(self): - return self._config.get('gui_channel_vars', {}) + return dict(self._config.get('gui_channel_vars', {})) def save_guiCH_VARS(self, data: dict): - self._config['gui_channel_vars'] = data + self._config['gui_channel_vars'] = dict(data) # F-Text def get_f_text_fm_id(self, f_id: int): @@ -321,7 +326,7 @@ def set_f_text_f_id(self, f_id: int, text_and_enc: tuple): # Pacman def get_pacman_data(self): - return self._config.get('gui_pacman', {}) + return dict(self._config.get('gui_pacman', {})) def set_pacman_data(self, data: dict): if not data: @@ -331,7 +336,7 @@ def set_pacman_data(self, data: dict): ################################################# # Beacon def get_Beacon_tasks(self): - return self._config.get('beacon_tasks', []) + return list(self._config.get('beacon_tasks', [])) def set_Beacon_tasks(self, data: list): self._config['beacon_tasks'] = list(data) @@ -339,7 +344,7 @@ def set_Beacon_tasks(self, data: list): ################################################# # Dual Port def get_dualPort_CFG(self): - return self._config.get('dualPort_cfg', {}) + return dict(self._config.get('dualPort_cfg', {})) def set_dualPort_CFG(self, cfg: dict): self._config['dualPort_cfg'] = dict(cfg) @@ -347,15 +352,15 @@ def set_dualPort_CFG(self, cfg: dict): ################################################# # DIGI def get_digi_CFG(self): - return self._config.get('digi_cfg', {}) + return dict(self._config.get('digi_cfg', {})) def get_digi_CFG_for_Call(self, call: str): - return self._config.get('digi_cfg', {}).get(call, self.get_digi_default_CFG()) + return dict(self._config.get('digi_cfg', {}).get(call, self.get_digi_default_CFG())) def get_digi_is_enabled(self, call: str): if not call: return False - return self._config.get('digi_cfg', {}).get(call, self.get_digi_default_CFG()).get('digi_enabled', False) + return bool(self._config.get('digi_cfg', {}).get(call, self.get_digi_default_CFG()).get('digi_enabled', False)) @staticmethod def get_digi_default_CFG(): @@ -383,13 +388,13 @@ def del_digi_CFG_fm_call(self, call: str): ########################################### # PIPE def get_pipe_CFG(self): - return self._config.get('pipe_cfgs', {}) + return dict(self._config.get('pipe_cfgs', {})) def get_pipe_CFG_fm_UID(self, call, port_id=-1): cfg_keys = list(self._config.get('pipe_cfgs', {}).keys()) lookup_k = f'{port_id}-{call}' if lookup_k in cfg_keys: - return self._config.get('pipe_cfgs', {}).get(lookup_k, {}) + return dict(self._config.get('pipe_cfgs', {}).get(lookup_k, {})) return {} def del_pipe_CFG_fm_CallPort(self, call, port_id=-1): @@ -424,12 +429,12 @@ def del_pipe_CFG_fm_call(self, call: str): ########################################### # Station def get_stat_CFGs(self): - return self._config.get('stat_cfgs', {}) + return dict(self._config.get('stat_cfgs', {})) def get_stat_CFG_fm_call(self, call): if not call: return {} - return self._config.get('stat_cfgs', {}).get(call, {}) + return dict(self._config.get('stat_cfgs', {}).get(call, {})) def get_stat_CFG_keys(self): return list(self._config.get('stat_cfgs', {})) @@ -457,18 +462,18 @@ def del_stat_CFG_fm_call(self, call): ########################################### # Port def get_port_CFGs(self): - return self._config.get('port_cfgs', {}) + return dict(self._config.get('port_cfgs', {})) def get_port_CFG_fm_id(self, port_id: int): if 0 > port_id > MAX_PORTS - 1: return {} - return self._config.get('port_cfgs', {}).get(port_id, {}) + return dict(self._config.get('port_cfgs', {}).get(port_id, {})) def get_stationCalls_fm_port(self, port_id: int): port_cfg = self.get_port_CFG_fm_id(port_id) if not port_cfg: return [] - return port_cfg.get('parm_StationCalls', []) + return list(port_cfg.get('parm_StationCalls', [])) def set_port_CFG_fm_id(self, port_id: int, port_cfg: dict): @@ -516,7 +521,7 @@ def get_MCast_server_call(self): stat_cfgs = self.get_stat_CFGs() for call, stat_cfg in stat_cfgs.items(): if stat_cfg.get('stat_parm_cli', '') == 'MCAST': - return call + return str(call) return '' def set_MCast_CFG(self, mcast_cfg: dict): @@ -526,5 +531,24 @@ def set_MCast_CFG(self, mcast_cfg: dict): self._config['mcast_cfg'] = dict(mcast_cfg) return True + ########################################### + # 1Wire + def get_1wire_sensor_cfg(self): + return dict(self._config.get('1wire_cfg', {}).get('sensor_cfg', {})) + + def get_1wire_loop_timer(self): + return int(self._config.get('1wire_cfg', {}).get('loop_timer', 60)) + + def set_1wire_sensor_cfg(self, sensor_cfg: dict): + cfg = dict(self._config.get('1wire_cfg', getNew_1wire_cfg())) + cfg['sensor_cfg'] = dict(sensor_cfg) + self._config['1wire_cfg'] = dict(cfg) + + def set_1wire_loop_timer(self, loop_timer: int): + loop_timer = max(30, loop_timer) + cfg = dict(self._config.get('1wire_cfg', getNew_1wire_cfg())) + cfg['loop_timer'] = int(loop_timer) + self._config['1wire_cfg'] = dict(cfg) + POPT_CFG = Main_CFG() diff --git a/cfg/string_tab.py b/cfg/string_tab.py index 6a4fbccc..4774afe5 100644 --- a/cfg/string_tab.py +++ b/cfg/string_tab.py @@ -8,6 +8,7 @@ 6: 'pt' 7: 'it' 8: 'zh' # Not really ;-) +9: '?' Thanks to NL1NOD(Patrick) for the Dutch translations. """ @@ -22,6 +23,7 @@ # PT # IT # ? + # ? 'default_ctext': ( # GER ('\n' @@ -113,6 +115,16 @@ '\n' ' # Letzer Login am: $lastConnDate um: $lastConnTime\n' '\n'), + # ?????? + ('\n' + '-= Hallo $destName, =-\n' + '-= willkommen bei $ownCall ($distance km), =-\n' + '-= auf Terminal-Kanal $channel <> Port $portNr. =-\n' + '-= Das ist Connect Nr. $connNr. =-\n' + '-= $ver - Max-Frame: $parmMaxFrame - Pac-Len: $parmPacLen =-\n' + '\n' + ' # Letzer Login am: $lastConnDate um: $lastConnTime\n' + '\n'), ), 'default_btext': ( @@ -124,6 +136,7 @@ '\n73 de $ownCall ...\n', '\n73 de $ownCall ...\n', '\n73 de $ownCall ...\n', + '\n73 de $ownCall ...\n', '\n73 de $ownCall ...\n'), 'language': ( @@ -135,6 +148,7 @@ '', '', '', + '', ''), 'userdb_add_sysop_ent1': ( @@ -146,6 +160,7 @@ '', '', '', + '', ''), 'userdb_add_sysop_ent2': ( @@ -157,6 +172,7 @@ '', '', '', + '', ''), 'userdb_save_hint': ( @@ -168,6 +184,7 @@ '', '', '', + '', ''), 'userdb_del_hint1': ( @@ -179,6 +196,7 @@ '', '', '', + '', ''), 'userdb_del_hint2': ( @@ -190,6 +208,7 @@ '', '', '', + '', ''), 'prewritewin': ( @@ -201,6 +220,7 @@ '', '', '', + '', ''), 'call_vali_warning_1': ( @@ -212,6 +232,7 @@ '', '', '', + '', ''), 'call_vali_warning_2': ( @@ -223,6 +244,7 @@ '', '', '', + '', ''), 'del_station_hint': ( @@ -234,6 +256,7 @@ '', '', '', + '', ''), 'del_station_warning_1': ( @@ -245,6 +268,7 @@ '', '', '', + '', ''), 'del_station_warning_2': ( @@ -256,6 +280,7 @@ '', '', '', + '', ''), 'del_station_hint_1': ( @@ -267,6 +292,7 @@ '', '', '', + '', ''), 'del_station_hint_2': ( @@ -278,6 +304,7 @@ '', '', '', + '', ''), 'not_all_station_disco_hint_1': ( @@ -289,6 +316,7 @@ '', '', '', + '', ''), 'not_all_station_disco_hint_2': ( @@ -300,6 +328,7 @@ '', '', '', + '', ''), 'all_station_get_disco_hint_1': ( @@ -311,6 +340,7 @@ '', '', '', + '', ''), 'all_station_get_disco_hint_2': ( @@ -322,6 +352,7 @@ '', '', '', + '', ''), 'close_port': ('Info: Versuche Port {} zu schließen.', @@ -332,6 +363,7 @@ '', '', '', + '', ''), 'port_closed': ('Info: Port {} erfolgreich geschlossen.', @@ -342,6 +374,7 @@ '', '', '', + '', ''), 'send_kiss_parm': ('Hinweis: Kiss-Parameter an TNC auf Port {} gesendet..', @@ -352,6 +385,7 @@ '', '', '', + '', ''), 'port_in_use': ('Error: Port {} konnte nicht initialisiert werden. Port wird bereits benutzt.', @@ -362,6 +396,7 @@ '', '', '', + '', ''), 'no_port_typ': ('Hinweis: Kein Port-Typ ausgewählt. Port {}', @@ -372,6 +407,7 @@ '', '', '', + '', ''), 'port_not_init': ('Error: Port {} konnte nicht initialisiert werden.', @@ -382,6 +418,7 @@ '', '', '', + '', ''), 'port_init': ('Info: Port {} erfolgreich initialisiert.', 'Info: Port {} initialized successfully.', @@ -391,6 +428,7 @@ '', '', '', + '', ''), 'setting_saved': ( @@ -402,6 +440,7 @@ '', '', '', + '', ''), 'all_port_reinit': ( @@ -413,6 +452,7 @@ '', '', '', + '', ''), 'port_reinit': ( @@ -424,6 +464,7 @@ '', '', '', + '', ''), 'all_disco1': ( @@ -435,6 +476,7 @@ '', '', '', + '', ''), 'all_disco2': ( 'Es werden alle Stationen disconnected', @@ -445,6 +487,7 @@ '', '', '', + '', ''), 'OK': ('OK', @@ -455,6 +498,7 @@ '', '', '', + '', ''), 'cancel': ('Abbrechen', @@ -465,6 +509,7 @@ '', '', '', + '', ''), 'aborted': ('Abgebrochen', @@ -475,6 +520,7 @@ '', '', '', + '', ''), 'delete': ('Löschen', @@ -485,6 +531,7 @@ '', '', '', + '', ''), 'delete_dx_history': ('DX-History Löschen', @@ -495,6 +542,7 @@ '', '', '', + '', ''), 'del_all': ('Alles Löschen', @@ -505,6 +553,7 @@ '', '', '', + '', ''), 'go': ('Los', @@ -515,6 +564,7 @@ '', '', '', + '', ''), 'close': ('Schließen', @@ -525,6 +575,7 @@ '', '', '', + '', ''), 'save': ('Speichern', @@ -535,6 +586,7 @@ '', '', '', + '', ''), 'send_file': ('Datei senden', @@ -545,6 +597,7 @@ '', '', '', + '', ''), 'file_1': ('Datei', @@ -555,6 +608,7 @@ '', '', '', + '', ''), 'file_2': ('Datei:', @@ -565,6 +619,7 @@ '', '', '', + '', ''), 'locator_calc': ('Locator Rechner', @@ -575,6 +630,7 @@ '', '', '', + '', ''), 'aprs_mon': ('APRS-Server Monitor', @@ -585,6 +641,7 @@ '', '', '', + '', ''), 'protocol': ('Protokoll:', @@ -595,6 +652,7 @@ '', '', '', + '', ''), 'send_if_free': ('Senden wenn Band frei für (sek.):', @@ -605,6 +663,7 @@ '', '', '', + '', ''), 'size': ('Größe:', @@ -615,6 +674,7 @@ '', '', '', + '', ''), 'new': ('Neu', @@ -625,6 +685,7 @@ '', '', '', + '', ''), 'new_port': ('Neuer Port', @@ -635,6 +696,7 @@ '', '', '', + '', ''), 'new_conn': ('Neu Verbindung', @@ -645,6 +707,7 @@ '', '', '', + '', ''), 'disconnect': ('Disconnecten', @@ -655,6 +718,7 @@ '', '', '', + '', ''), 'disconnect_all': ('ALLE disconnecten', @@ -665,6 +729,7 @@ '', '', '', + '', ''), 'disconnect_all_ask': ('Wirklich ALLE Stationen disconnecten ?', @@ -675,6 +740,7 @@ '', '', '', + '', ''), 'wx_window': ('Wetterstationen', @@ -685,6 +751,7 @@ '', '', '', + '', ''), 'quit': ('Quit', @@ -695,6 +762,7 @@ '', '', '', + '', ''), 'connections': ('Verbindungen', @@ -705,6 +773,7 @@ '', '', '', + '', ''), 'copy': ('Kopieren', @@ -715,6 +784,7 @@ '', '', '', + '', ''), 'past': ('Einfügen', @@ -725,6 +795,7 @@ '', '', '', + '', ''), 'past_f_file': ('Aus Datei einfügen', @@ -735,6 +806,7 @@ '', '', '', + '', ''), 'save_to_file': ('In Datei speichern', @@ -745,6 +817,7 @@ '', '', '', + '', ''), 'past_qso_f_file': ('Aus Datei einfügen', @@ -755,6 +828,7 @@ '', '', '', + '', ''), 'save_qso_to_file': ('QSO in Datei speichern', @@ -765,6 +839,7 @@ '', '', '', + '', ''), 'save_mon_to_file': ('Monitor in Datei speichern', @@ -775,6 +850,7 @@ '', '', '', + '', ''), 'clean_qso_win': ('QSO/Vorschreibfenster löschen', @@ -785,6 +861,7 @@ '', '', '', + '', ''), 'clean_all_qso_win': ('Alle QSO/Vorschreibfenster löschen', @@ -795,6 +872,7 @@ '', '', '', + '', ''), 'clean_mon_win': ('Monitor löschen', @@ -805,6 +883,7 @@ '', '', '', + '', ''), 'edit': ('Bearbeiten', @@ -815,6 +894,7 @@ '', '', '', + '', ''), 'statistic': ('Statistik', @@ -825,6 +905,7 @@ '', '', '', + '', ''), 'linkholder': ('Linkhalter', @@ -835,6 +916,7 @@ '', '', '', + '', ''), # 'clean_qso': ('QSO löschen', 'delete QSO', @@ -844,6 +926,7 @@ '', '', '', + '', ''), 'tools': ('Tools', @@ -854,6 +937,7 @@ '', '', '', + '', ''), 'station': ('Station', @@ -864,6 +948,7 @@ '', '', '', + '', ''), 'stations': ('Stationen', @@ -874,6 +959,7 @@ '', '', '', + '', ''), 'port': ('Port', @@ -884,6 +970,7 @@ '', '', '', + '', ''), 'channel': ('Kanal', @@ -894,6 +981,7 @@ '', '', '', + '', ''), 'beacon': ('Baken', @@ -904,6 +992,7 @@ '', '', '', + '', ''), 'sprech': ('Sprachausgabe', @@ -914,6 +1003,7 @@ '', '', '', + '', ''), 'settings': ('Einstellungen', @@ -924,6 +1014,7 @@ '', '', '', + '', ''), 'main_page': ('Hauptseite', @@ -934,6 +1025,7 @@ '', '', '', + '', ''), 'passwords': ('Passwörter', @@ -944,6 +1036,7 @@ '', '', '', + '', ''), 'syspassword': ('Sys-Passwort:', @@ -954,6 +1047,7 @@ '', '', '', + '', ''), 'trys': ('Fake-Versuche:', @@ -964,6 +1058,7 @@ '', '', '', + '', ''), 'fillchars': ('Antwortlänge:', @@ -974,6 +1069,7 @@ '', '', '', + '', ''), 'priv': ('Login', @@ -984,6 +1080,7 @@ '', '', '', + '', ''), 'login_cmd': ('Login Kommando:', @@ -994,6 +1091,7 @@ '', '', '', + '', ''), 'keybind': ('Tastaturbelegung', @@ -1004,6 +1102,7 @@ '', '', '', + '', ''), 'about': ('Über', @@ -1014,6 +1113,7 @@ '', '', '', + '', ''), 'help': ('Hilfe', @@ -1024,6 +1124,7 @@ '', '', '', + '', ''), 'number': ('Anzahl', @@ -1034,6 +1135,7 @@ '', '', '', + '', ''), 'minutes': ('Minuten', @@ -1044,6 +1146,7 @@ '', '', '', + '', ''), 'hours': ('Stunden', @@ -1054,6 +1157,7 @@ '', '', '', + '', ''), 'day': ('Tag', @@ -1064,6 +1168,7 @@ '', '', '', + '', ''), 'month': ('Monat', @@ -1074,6 +1179,7 @@ '', '', '', + '', ''), 'occup': ('Auslastung in %', @@ -1084,6 +1190,7 @@ '', '', '', + '', ''), 'call': ('Call', @@ -1094,6 +1201,7 @@ '', '', '', + '', ''), 'name': ('Name', @@ -1104,6 +1212,7 @@ '', '', '', + '', ''), 'fwd_list': ('Forward Warteschlange', @@ -1114,6 +1223,7 @@ '', '', '', + '', ''), 'fwd_path': ('Forward Routen', @@ -1124,6 +1234,7 @@ '', '', '', + '', ''), 'start_fwd': ('FWD Start', @@ -1134,6 +1245,7 @@ '', '', '', + '', ''), 'start_auto_fwd': ('AutoFWD Start', @@ -1144,6 +1256,7 @@ '', '', '', + '', ''), 'msg_center': ('Nachrichten Center', @@ -1154,6 +1267,7 @@ '', '', '', + '', ''), 'qso_win_color': ('QSO Fenster Farben', @@ -1164,6 +1278,7 @@ '', '', '', + '', ''), 'text_color': ('Text Farben', @@ -1174,6 +1289,7 @@ '', '', '', + '', ''), 'bg_color': ('Hintergrund Farben', @@ -1184,6 +1300,7 @@ '', '', '', + '', ''), 'mon_color': ('Monitor Farben', @@ -1194,6 +1311,7 @@ '', '', '', + '', ''), 'c_text': ('C Text', @@ -1204,6 +1322,7 @@ '', '', '', + '', ''), 'q_text': ('Quit Text', @@ -1214,6 +1333,7 @@ '', '', '', + '', ''), 'i_text': ('Info Text', @@ -1224,6 +1344,7 @@ '', '', '', + '', ''), 'li_text': ('Lang-Info Text', @@ -1234,6 +1355,7 @@ '', '', '', + '', ''), 'news_text': ('News Text', @@ -1244,6 +1366,7 @@ '', '', '', + '', ''), 'aprs_settings': ('APRS-Einstellungen', @@ -1254,6 +1377,7 @@ '', '', '', + '', ''), 'aprs_pn_msg': ('APRS Private Nachrichten', @@ -1264,6 +1388,7 @@ '', '', '', + '', ''), 'pn_msg': ('Private Nachrichten', @@ -1274,6 +1399,7 @@ '', '', '', + '', ''), 'msg': ('Nachricht', @@ -1284,6 +1410,7 @@ '', '', '', + '', ''), 'new_msg': ('Neue Nachricht', @@ -1294,6 +1421,7 @@ '', '', '', + '', ''), 'new_pr_mail': ('Neue PR-Mail', @@ -1304,6 +1432,7 @@ '', '', '', + '', ''), 'stat_settings': ('Station', @@ -1314,6 +1443,7 @@ '', '', '', + '', ''), # 'general_settings': ('Allgemein', @@ -1324,6 +1454,7 @@ '', '', '', + '', ''), 'new_stat': ('Neue Station', @@ -1334,6 +1465,7 @@ '', '', '', + '', ''), 'txt_decoding': ('Umlautumwandlung', @@ -1344,6 +1476,7 @@ '', '', '', + '', ''), 'suc_save': ('Info: Station Einstellungen erfolgreich gespeichert.', @@ -1354,6 +1487,7 @@ '', '', '', + '', ''), 'lob1': ('Lob: Das hast du sehr gut gemacht !!', @@ -1364,6 +1498,7 @@ '', '', '', + '', ''), 'lob2': ('Lob: Das hast du gut gemacht !!', @@ -1374,6 +1509,7 @@ '', '', '', + '', ''), 'lob3': ('Lob: Das war eine gute Entscheidung. Mach weiter so. Das hast du gut gemacht.', @@ -1384,6 +1520,7 @@ '', '', '', + '', ''), # 'lob4': ('Lob: Du hast dir heute noch kein Lob verdient.', @@ -1394,6 +1531,7 @@ '', '', '', + '', ''), 'lob5': ('Es tut mir leid, Dave. Ich fürchte, das kann ich nicht.', @@ -1404,6 +1542,7 @@ '', '', '', + '', ''), 'hin1': ('Hinweis: Der OK Button funktioniert noch !!', @@ -1414,6 +1553,7 @@ '', '', '', + '', ''), 'hin2': ('Hinweis: Knack!! Abgebrochen..', @@ -1424,6 +1564,7 @@ '', '', '', + '', ''), 'from': ('Von', @@ -1434,6 +1575,7 @@ '', '', '', + '', ''), 'to': ('An', @@ -1444,6 +1586,7 @@ '', '', '', + '', ''), 'versatz': ('Versatz', @@ -1454,6 +1597,7 @@ '', '', '', + '', ''), 'intervall': ('Intervall', @@ -1464,6 +1608,7 @@ '', '', '', + '', ''), 'active': ('Aktiviert', @@ -1474,6 +1619,7 @@ '', '', '', + '', ''), 'text_fm_file': ('Text aus Datei', @@ -1484,6 +1630,7 @@ '', '', '', + '', ''), 'beacon_settings': ('Baken', @@ -1494,6 +1641,7 @@ '', '', '', + '', ''), 'pipetool_settings': ('Pipe-Tool Einstellungen', @@ -1504,6 +1652,7 @@ '', '', '', + '', ''), 'new_pipe': ('Neue Pipe', @@ -1514,6 +1663,7 @@ '', '', '', + '', ''), 'new_pipe_fm_connection': ('Pipe auf Verbindung', @@ -1524,6 +1674,7 @@ '', '', '', + '', ''), 'tx_file': ('TX Datei', @@ -1534,6 +1685,7 @@ '', '', '', + '', ''), 'rx_file': ('RX Datei', @@ -1544,6 +1696,7 @@ '', '', '', + '', ''), 'port_cfg_std_parm': ('Standard Parameter. Werden genutzt wenn nirgendwo anders (Station/Client) definiert.', @@ -1554,6 +1707,7 @@ '', '', '', + '', ''), 'port_cfg_psd_txd': ('Pseudo TX-Delay (Wartezeit zwischen TX und RX). Wird nicht als KISS Parameter am TNC gesetzt.', @@ -1564,6 +1718,7 @@ '', '', '', + '', ''), 'port_cfg_pac_len': ('Paket Länge. 1 - 256', @@ -1574,6 +1729,7 @@ '', '', '', + '', ''), 'port_cfg_pac_max': ('Max Paket Anzahl. 1 - 7', @@ -1584,6 +1740,7 @@ '', '', '', + '', ''), 'port_cfg_port_name': ('Port Bezeichnung für MH und Monitor( Max: 4 ):', @@ -1594,6 +1751,7 @@ '', '', '', + '', ''), 'port_cfg_not_init': ('!! Port ist nicht Initialisiert !!', @@ -1604,6 +1762,7 @@ '', '', '', + '', ''), 'new_beacon': ('Neue Bake', @@ -1614,6 +1773,7 @@ '', '', '', + '', ''), 'last_packet': ('letztes Paket', @@ -1624,6 +1784,7 @@ '', '', '', + '', ''), 'scrolling': ('Auto Scrollen', @@ -1634,6 +1795,7 @@ '', '', '', + '', ''), 'msg_box_mh_delete': ('MH-Liste Löschen', @@ -1644,6 +1806,7 @@ '', '', '', + '', ''), 'msg_box_mh_delete_msg': ('Komplette MH-Liste löschen?', @@ -1654,6 +1817,7 @@ '', '', '', + '', ''), 'msg_box_delete_data': ('Daten Löschen', @@ -1664,6 +1828,7 @@ '', '', '', + '', ''), 'msg_box_delete_data_msg': ('Alle Daten löschen?', @@ -1674,6 +1839,7 @@ '', '', '', + '', ''), 'data': ('Daten', @@ -1684,6 +1850,7 @@ '', '', '', + '', ''), 'multicast_warning': ( @@ -1695,6 +1862,7 @@ '', '', '', + '', ''), 'user_db': ( @@ -1706,6 +1874,7 @@ '', '', '', + '', ''), # CLI @@ -1717,6 +1886,7 @@ '', '', '', + '', ''), 'cmd_help_wx': ('Wetterstationen', @@ -1727,6 +1897,7 @@ '', '', '', + '', ''), 'cmd_help_user_db': ('Call DB Abfrage', @@ -1737,6 +1908,7 @@ '', '', '', + '', ''), 'cmd_help_set_name': ('Namen eintragen', @@ -1747,6 +1919,7 @@ '', '', '', + '', ''), 'cmd_help_set_qth': ('QTH eintragen', @@ -1757,6 +1930,7 @@ '', '', '', + '', ''), 'cmd_help_set_loc': ('Locator eintragen', @@ -1767,6 +1941,7 @@ '', '', '', + '', ''), 'cmd_help_set_zip': ('Postleitzahl eintragen', @@ -1777,6 +1952,7 @@ '', '', '', + '', ''), 'cmd_help_set_prmail': ('PR-MAIL Adresse eintragen', @@ -1787,6 +1963,7 @@ '', '', '', + '', ''), 'cmd_help_set_email': ('E-MAIL Adresse eintragen', @@ -1797,6 +1974,7 @@ '', '', '', + '', ''), 'cmd_help_set_http': ('HTTP eintragen', @@ -1807,6 +1985,7 @@ '', '', '', + '', ''), 'cli_no_user_db_ent': (' # Eintag nicht in Benutzer Datenbank vorhanden!', @@ -1817,6 +1996,7 @@ '', '', '', + '', ''), 'cli_name_set': (' # Name eingetragen', @@ -1827,6 +2007,7 @@ '', '', '', + '', ''), 'cli_qth_set': (' # QTH eingetragen', @@ -1837,6 +2018,7 @@ '', '', '', + '', ''), 'cli_loc_set': (' # Locator eingetragen', @@ -1847,6 +2029,7 @@ '', '', '', + '', ''), 'cli_zip_set': (' # Postleitzahl eingetragen', @@ -1857,6 +2040,7 @@ '', '', '', + '', ''), 'cli_prmail_set': (' # PR-Mail Adresse eingetragen', @@ -1867,6 +2051,7 @@ '', '', '', + '', ''), 'cli_email_set': (' # E-Mail Adresse eingetragen', @@ -1877,6 +2062,7 @@ '', '', '', + '', ''), 'cli_http_set': (' # HTTP eingetragen', @@ -1887,6 +2073,7 @@ '', '', '', + '', ''), 'cli_text_encoding_no_param': (' # Bitte ein ä senden. Bsp.: UM ä.\r # Derzeitige Einstellung:', @@ -1897,6 +2084,7 @@ '', '', '', + '', ''), 'cli_text_encoding_error_not_found': (' # Umlaute wurden nicht erkannt !', @@ -1907,6 +2095,7 @@ '', '', '', + '', ''), 'cli_text_encoding_set': (' # Umlaute/Text de/enkodierung erkannt und gesetzt auf:', @@ -1917,6 +2106,7 @@ '', '', '', + '', ''), 'port_overview': ('Port Übersicht', @@ -1927,6 +2117,7 @@ '', '', '', + '', ''), 'cmd_shelp': ('Kurzhilfe', @@ -1937,6 +2128,7 @@ '', '', '', + '', ''), 'time_connected': ('Connect Dauer', @@ -1947,6 +2139,7 @@ '', '', '', + '', ''), 'cmd_not_known': ('Dieses Kommando ist dem System nicht bekannt !', @@ -1957,6 +2150,7 @@ '', '', '', + '', ''), 'auto_text_encoding': ('Automatisch Umlaut Erkennung. ä als Parameter. > UM ä', @@ -1967,6 +2161,7 @@ '', '', '', + '', ''), 'cmd_help_lcstatus': ('Verbundene Terminalkanäle anzeigen (ausführliche Version)', @@ -1977,6 +2172,7 @@ '', '', '', + '', ''), 'cli_no_wx_data': ('Keine Wetterdaten vorhanden.', @@ -1987,6 +2183,7 @@ '', '', '', + '', ''), 'cli_no_data': ('Keine Daten vorhanden.', @@ -1997,6 +2194,7 @@ '', '', '', + '', ''), 'cli_no_tracer_data': ( @@ -2008,6 +2206,7 @@ '', '', '', + '', ''), 'cli_change_language': ('Sprache ändern.', @@ -2018,6 +2217,7 @@ '', '', '', + '', ''), 'cli_lang_set': ('Sprache auf Deutsch geändert.', @@ -2028,6 +2228,7 @@ '', '', '', + '', ''), 'cli_no_lang_param': ('Sprache nicht erkannt! Mögliche Sprachen: ', @@ -2038,6 +2239,7 @@ '', '', '', + '', ''), 'cmd_bell': ('Sysop wird gerufen !!', @@ -2048,6 +2250,7 @@ '', '', '', + '', ''), 'cmd_bell_again': ('Sysop wurde bereits gerufen..', @@ -2058,6 +2261,7 @@ '', '', '', + '', ''), 'cmd_bell_gui_msg': ('verlangt nach dem Sysop !!', @@ -2068,6 +2272,50 @@ '', '', '', + '', + ''), + + 'cmd_c_noCall': ('# Bitte Call eingeben..', + '# Please enter call..', + '# Voer een oproep in..', + '', + '', + '', + '', + '', + '', + ''), + + 'cmd_c_badCall': ('# Ungültiger Ziel Call..', + '# Invalid destination call..', + '# Ongeldige bestemmingsoproep..', + '', + '', + '', + '', + '', + '', + ''), + 'cmd_c_badPort': ('# Ungültige Port Angabe..', + '# Invalid port specification..', + '# Ongeldige poortspecificatie..', + '', + '', + '', + '', + '', + '', + ''), + + 'cmd_c_noPort': ('# Ungültiger Port..', + '# Invalid port..', + '# Ongeldige poort..', + '', + '', + '', + '', + '', + '', ''), ##################################################### @@ -2081,6 +2329,7 @@ '', '', '', + '', ''), 'cmd_help_mcast_ch_info': ('MCast Kanal Info', @@ -2091,6 +2340,7 @@ '', '', '', + '', ''), 'cmd_help_mcast_channels': ('MCast Kanal Übersicht', @@ -2101,6 +2351,7 @@ '', '', '', + '', ''), # 'cmd_help_mcast_set_axip': ('Eigene AXIP Adresse eintragen / anzeigen lassen', @@ -2111,6 +2362,7 @@ '', '', '', + '', ''), 'mcast_new_user_beacon': ( @@ -2123,6 +2375,7 @@ '', '', '', + '', ''), 'mcast_new_user_reg_beacon': ( @@ -2142,6 +2395,8 @@ ''), ('\r' ''), + ('\r' + ''), ('\r' ''), ''), @@ -2156,6 +2411,7 @@ '', '', '', + '', ''), 'mcast_user_left_channel_beacon': ( @@ -2168,5 +2424,6 @@ '', '', '', + '', ''), } \ No newline at end of file diff --git a/cli/StringVARS.py b/cli/StringVARS.py index c7a25d8a..4a5d41b9 100644 --- a/cli/StringVARS.py +++ b/cli/StringVARS.py @@ -1,6 +1,7 @@ import datetime from cfg.constant import VER +from cfg.popt_config import POPT_CFG from fnc.str_fnc import get_timedelta_CLIstr """ @@ -19,6 +20,8 @@ $connNr = Connect Nr $parmMaxFrame = Max Frame Einstellungen - Bake $parmPacLen = Pakete Länge Einstellungen - Bake + + - Raspberry 1Wire Sensoren - Bake """ @@ -213,17 +216,34 @@ def replace_StringVARS(input_string: str, connection=None, user_db=None, ): + if not '$' in input_string: + return input_string if connection: port = connection.own_port if port and not port_handler: port_handler = port.port_get_PH() if port_handler and not user_db: user_db = port_handler.get_userDB() + + # PoPT System for key, fnc in STRING_VARS.items(): - if callable(fnc): - input_string = input_string.replace(key, fnc(port=port, - port_handler=port_handler, - connection=connection, - user_db=user_db)) + if not key in input_string: + continue + if not callable(fnc): + continue + input_string = input_string.replace(key, fnc(port=port, + port_handler=port_handler, + connection=connection, + user_db=user_db)) + # 1-Wire + wire_cfg = POPT_CFG.get_1wire_sensor_cfg() + for key, device_cfg in wire_cfg.items(): + device_cfg: dict + if not key in input_string: + continue + val = device_cfg.get('device_value', 'n/a') + if val is None: + val = 'n/a' + input_string = input_string.replace(key, val) return input_string diff --git a/cli/cliMain.py b/cli/cliMain.py index 7df67ba7..eb827332 100644 --- a/cli/cliMain.py +++ b/cli/cliMain.py @@ -9,7 +9,7 @@ from fnc.file_fnc import get_str_fm_file from fnc.socket_fnc import get_ip_by_hostname from fnc.str_fnc import get_time_delta, find_decoding, get_timedelta_str_fm_sec, get_timedelta_CLIstr, \ - convert_str_to_datetime, zeilenumbruch_lines + convert_str_to_datetime, zeilenumbruch_lines, get_strTab, zeilenumbruch from cfg.string_tab import STR_TABLE from fnc.ax25_fnc import validate_ax25Call from UserDB.UserDBmain import USER_DB @@ -19,9 +19,6 @@ class DefaultCLI(object): cli_name = '' # DON'T CHANGE! service_cli = True - # c_text = '-= Test C-TEXT =-\r\r' - # bye_text = '73 ...\r' - # prompt = '' prefix = b'//' sw_id = '' @@ -79,6 +76,7 @@ def __init__(self, connection): 'QUIT': (1, self._cmd_q, 'Quit'), 'BYE': (1, self._cmd_q, 'Bye'), 'CONNECT': (1, self._cmd_connect, 'Connect'), + 'C!': (2, self._cmd_connect_exclusive, 'Connect Exclusive (No MH-Path-Lookup)'), 'ECHO': (1, self._cmd_echo, 'Echo'), 'PORT': (1, self._cmd_port, 'Ports'), 'MH': (0, self._cmd_mh, 'MYHeard List'), @@ -176,55 +174,42 @@ def change_cli_state(self, state: int): self._state_index = state def is_prefix(self): - # TODO Cleanup !!!! - if self.prefix: - self._input = self._input.replace(b'\n', b'\r') - # self.input = self.input.split(b'\r')[0] - self._input = self._input.split(b'\r') - while self._input: - if self._input[0]: - break - else: - self._input = self._input[1:] - if not self._input: - return False - self._input = self._input[0] - - if self._input[:len(self.prefix)] == self.prefix: - self._parameter = [] - cmd = self._input[len(self.prefix):] - cmd = cmd.split(b' ') - if len(cmd) > 1: - self._input = cmd[1:] - self._parameter = cmd[1:] - else: - self._input = b'' - - cmd = cmd[0] - self._cmd = cmd \ - .upper() \ - .replace(b' ', b'') \ - .replace(b'\r', b'') - # self.input = self.input[len(self.prefix):] - return True - else: - # Message is for User ( Text , Chat ) - return False - # CMD Input for No User Terminals ( Node ... ) - self._parameter = [] - cmd = self._input - cmd = cmd.split(b' ') - if len(cmd) > 1: - self._input = cmd[1:] - self._parameter = cmd[1:] + # Optimized by GROK (x.com) + if not self.prefix: + # Handle case where there is no prefix + self._parameter = [] + cmd_parts = self._input.split(b' ') + self._input = cmd_parts[1:] if len(cmd_parts) > 1 else b'' + self._parameter = self._input + self._cmd = cmd_parts[0].upper().replace(b'\r', b'').replace(b'//', b'') + return False + + # Remove newlines and split by '\r' + lines = self._input.replace(b'\n', b'\r').split(b'\r') + + # Find the first non-empty line + for line in lines: + if line: + self._input = line + break else: - self._input = b'' - cmd = cmd[0] - self._cmd = cmd \ - .upper() \ - .replace(b'\r', b'') \ - .replace(b'//', b'') - + # If no non-empty lines found + return False + + # Check if the input starts with the prefix + if self._input.startswith(self.prefix): + cmd_part = self._input[len(self.prefix):].split(b' ', 1) + self._cmd = cmd_part[0].upper().replace(b'\r', b'') + self._parameter = cmd_part[1:] if len(cmd_part) > 1 else [] + self._input = self._parameter + return True + + # If the prefix does not match, treat as user message + self._parameter = [] + cmd_parts = self._input.split(b' ', 1) + self._cmd = cmd_parts[0].upper().replace(b'\r', b'') + self._parameter = cmd_parts[1:] if len(cmd_parts) > 1 else [] + self._input = self._parameter return False def _load_fm_file(self, filename: str): @@ -420,23 +405,17 @@ def _decode_param(self, defaults=None): tmp.append(tmp_parm) self._parameter = list(tmp) - def _cmd_connect(self): - # print(f'cmd_connect() param: {self.parameter}') + def _cmd_connect(self, exclusive=False): self._decode_param() - # print(f'cmd_connect() param.decode: {self.parameter}') - + lang = self._connection.cli_language if not self._parameter: - ret = '\r # Bitte Call eingeben..\r' - return ret + return f"\r {get_strTab('cmd_c_noCall', lang)}\r" dest_call = str(self._parameter[0]).upper() if not validate_ax25Call(dest_call): - ret = '\r # Ungültiger Ziel Call..\r' - return ret + return f"\r {get_strTab('cmd_c_badCall', lang)}\r" - # port_id = self.own_port.port_id port_id = -1 - # vias = [self._connection.my_call_str] vias = [] port_tr = False if len(self._parameter) > 1: @@ -444,43 +423,35 @@ def _cmd_connect(self): port_tr = True try: port_id = int(self._parameter[-1]) + if port_id not in self._port_handler.get_all_ports().keys(): + return f"\r {get_strTab('cmd_c_noPort', lang)}\r" except ValueError: - ret = '\r # Ungültige Port angabe..\r' - return ret - - if port_id not in self._port_handler.get_all_ports().keys(): - ret = '\r # Ungültiger Port..\r' - return ret - if port_tr: - parm = self._parameter[1:-1] - else: - parm = self._parameter[1:] + return f"\r {get_strTab('cmd_c_badPort', lang)}\r" - for call in parm: - if validate_ax25Call(call.upper()): - vias.append(call.upper()) - else: - break + via_params = self._parameter[1:-1] if port_tr else self._parameter[1:] + vias = [call.upper() for call in via_params if validate_ax25Call(call.upper())] conn = self._port_handler.new_outgoing_connection( - own_call=self._to_call_str, - dest_call=dest_call, - via_calls=vias, - port_id=port_id, - link_conn=self._connection, - # link_call=str(self._connection.my_call_str) - ) + own_call=self._to_call_str, + dest_call=dest_call, + via_calls=vias, + port_id=port_id, + link_conn=self._connection, + exclusive=exclusive + # link_call=str(self._connection.my_call_str) + ) if conn[0]: self._state_index = 4 return conn[1] return f'\r*** Link Busy: {conn[1]}\r' + def _cmd_connect_exclusive(self): + return self._cmd_connect(exclusive=True) + def _cmd_echo(self): # Quit ret = '' - # print(f"Echo Param: {self.parameter}") for el in self._parameter: ret += el.decode(self._encoding[0], self._encoding[1]) + ' ' - # print(f"Echo ret: {ret}") return ret[:-1] + '\r' def _cmd_q(self): # Quit @@ -881,39 +852,47 @@ def _cmd_news(self): def _cmd_user_db_detail(self): if not self._parameter: - max_entry = 20 # TODO: from parameter - _db_list = list(self._user_db.db.keys()) + # max_lines = 20 # TODO: from parameter + db_list = list(self._user_db.db.keys()) header = "\r" \ - f" USER-DB - {len(_db_list)} Calls\r" \ - "------------------------------------\r" + f" USER-DB - {len(db_list)} Calls\r" \ + "-------------------------------------------------------------------------------\r" ent_ret = "" - _db_list.sort() - c = 0 - for call in _db_list: - ent_ret += f"{call}\r" - c += 1 - if c >= max_entry: + db_list.sort() + # c = 0 + # colum_c = 0 + for call in db_list: + ent_ret += f"{call} " + """ + colum_c += 1 + if colum_c > 6: + ent_ret += "\r" + colum_c = 0 + c += 1 + """ + """ + if c >= max_lines: break - ent_ret += "------------------------------------\r\r" + """ + ent_ret = zeilenumbruch(ent_ret) + ent_ret += "\r-------------------------------------------------------------------------------\r\r" return header + ent_ret else: call_str = self._parameter[0].decode(self._encoding[0], self._encoding[1]).upper() - - if validate_ax25Call(call_str): - if call_str in self._user_db.db.keys(): - header = "\r" \ - f"| USER-DB: {call_str}\r" \ - "|-------------------\r" - ent = self._user_db.db[call_str] - ent_ret = "" - for att in dir(ent): - if '__' not in att and \ - att not in self._user_db.not_public_vars: - if getattr(ent, att): - ent_ret += f"| {att.ljust(10)}: {getattr(ent, att)}\r" - - ent_ret += "|-------------------\r\r" - return header + ent_ret + db_ent = self._user_db.get_entry(call_str, add_new=False) + if db_ent: + header = "\r" \ + f"| USER-DB: {call_str}\r" \ + "|-------------------\r" + ent = db_ent + ent_ret = "" + for att in dir(ent): + if '__' not in att and \ + att not in self._user_db.not_public_vars: + if getattr(ent, att): + ent_ret += f"| {att.ljust(10)}: {getattr(ent, att)}\r" + ent_ret += "|-------------------\r\r" + return header + ent_ret return "\r" \ f"{STR_TABLE['cli_no_user_db_ent'][self._connection.cli_language]}" \ diff --git a/doc/AX25_PoPT.odt b/doc/AX25_PoPT.odt index 450b8d46..6e5cb7c2 100644 Binary files a/doc/AX25_PoPT.odt and b/doc/AX25_PoPT.odt differ diff --git a/doc/AX25_PoPT.pdf b/doc/AX25_PoPT.pdf index 0498b880..e961c304 100644 Binary files a/doc/AX25_PoPT.pdf and b/doc/AX25_PoPT.pdf differ diff --git a/doc/AX25_PoPT_en.pdf b/doc/AX25_PoPT_en.pdf index 9c7c871b..82481c2b 100644 Binary files a/doc/AX25_PoPT_en.pdf and b/doc/AX25_PoPT_en.pdf differ diff --git a/doc/AX25_PoPT_nl.pdf b/doc/AX25_PoPT_nl.pdf new file mode 100644 index 00000000..30d3860b Binary files /dev/null and b/doc/AX25_PoPT_nl.pdf differ diff --git a/doc/changelog.txt b/doc/changelog.txt index c515c2b9..a30b982f 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -1,4 +1,41 @@ ########################################################################################################### +# 2.114.x +Fixes: +- Scripterror, wenn "Messagebox" für Sysop Benachrichtigung erscheint +- Bei ""*** Reconnected to" wird call(SSID) der ausgehenden Station angezeigt + und nicht der zu der man connected ist +- AutoBin: Falscher Timestamp Check führt dazu das manche Dateien nicht + angenommen werden. +- AutoBin: Fehlender Zeilenumbruch bei Antwort Paket "#OK#". + !! Danke an Steffen und Ralph fürs Testen und Melden !! +- Port übergreifender weiterconnect über via Call hat für Probleme gesorgt. + (Digi-StopBit der vorherigen Digis wurde nicht korrekt gecheckt) + !! Danke an Lars und Benny fürs Testen und Melden !! + (Es gibt immer noch ein Problem mit dem L3-Digi.. Ich arbeite dran) +- Sporadischer Fehler beim Speichern der popt_main Config +- Unbekannte/Defekte KissFrames werden nicht gefiltert + !! Danke an Patrik !! +- File-Transfer Auto-RNR Mode +- Windows: Probleme beim Schließen des seriellen Ports (PoPT friert ein) + !! Danke an Jan !! +- Port Einstellungen: beim Ändern des COM-Ports wird kein reinit ausgelöst +- Port Einstellungen: beim löschen des "letzten Ports" wird die GUI nicht aktualisiert + +Optimierung: +- APRS-Tracer: Möglichkeit ViaCalls einzugeben hinzugefügt +- Windows: Porteinstellungen: Auswahl der COM Ports zum Dropdown Menü geändert + (Mir war nicht bewusst, dass es Windows Nutzer so schwerfällt COM1, COM2, ..., einzugeben :D) + +Neue Implementierungen / Funktionen: +- Remote Kommando "C!" - Exklusiv Connect ohne MH-Listen Lookup nach Pfad +- Daten aus "1Wire-Sensoren (DS18B20-Temperatur Sensor)" können jetzt als + Textvariable in C-Text/Bake/usw. integriert werden. + (PoPT holt sich die Daten aus "/sys/devices/w1_bus_master1") +- NetRom-Decoder zu Testzwecken aktiviert (experimentell) (Noch nicht vollständig) + + +########################################################################################################### +########################################################################################################### # 2.113.x Fixes: - Windows: Keybindings für F-Texte diff --git a/doc/ideas/idea.txt b/doc/ideas/idea.txt index 2e4ee2be..3f07572d 100644 --- a/doc/ideas/idea.txt +++ b/doc/ideas/idea.txt @@ -27,6 +27,10 @@ Ideas/TODO's : ✓ $parmPacLen = Pakete Länge Einstellungen - Bake ✓ $lastConnDate = Letzter Connect Datum ✓ $lastConnTime = Letzter Connect Zeit + ✓ Raspberry 1Wire Sensoren - Bake + - Raspberry BME280-Wettersensor - Bake + - Raspberry GPIO Zustand - Bake + - CPU-Temp/System-Daten - Bake - 79 Zeichen Zeilenumbruch (Dos-PRG kompatibilität) ✓ PMS @@ -52,15 +56,17 @@ Ideas/TODO's : ✓ Port Configs/Inits aufräumen/optimieren (Thrading) - Port Configs/Inits aufräumen/optimieren (Var OPT/ Code OPT) -- Soundsystem/Sprachausgabe überarbeiten +- Soundsystem überarbeiten + - Sprachausgabe überarbeiten ✓ Haupt GUI überarbeiten - Schluss Prozedur überarbeiten (Disco alle Stationen, Warten bis Ports geschlossen usw.) -✓ Einstellung GUI überarbeiten (OnePager) - ✓ Individuelle Farben (Vorschreib Fenster) - - Individuelle Farben (PMS) - - Individuelle Farben (APRS) - ✓ Sprache auswählbar - ✓ F-Texte + - ttk Styles (AW-Themes) + ✓ Einstellung GUI überarbeiten (OnePager) + ✓ Individuelle Farben (Vorschreib Fenster) + - Individuelle Farben (PMS) + - Individuelle Farben (APRS) + ✓ Sprache auswählbar + ✓ F-Texte ✓ Loop Port oder einfach localhost via AXIP ✓ einfach 127.0.0.1 und eigenen AXIP Port die eigene Station/Node connecten) @@ -119,16 +125,26 @@ Ideas/TODO's : - "Multi-TNC/Multi-Port/Mesh-TNC" mehrere TNC's via KISSTCP zusammenschalten. Siehe Dual-TNC +- SQL System + - Alternative SQL-Sys(MySQL-Server/SQLite) als Spiegelserver + - DB-Tool + ✓ UserDB - UserDB auf SQL-DB umstellen? - Suchfunktion in UserDB GUI - Infos / Ctext usw. in User-DB automatisch abfragen und speichern - User-DB Sync System (Manuell zu triggern bei connect zu anderer PoPT Station) +✓ Baycom Login verfahren + - AutoLogin z.B. BCM +- MD5 Login verfahren + ✓ Logbuch - GUI Ausgabe/Auswertung + - Converse Mode intern - Ping-Pong Converse + - QSO Fenster/Tool - Debugging Tool ✓ PMS (S&F ohne Gedöns) @@ -147,6 +163,10 @@ Ideas/TODO's : - Optional Remote-CMD für Disco u.Ä. - Remote-GPIO Tool für PI (Idee von Lars) + - 1Wire Sensoren lesen + - BME280 Sensor lesen + - GPIO lesen + - GPIO setzen (High/Low, Timer, PWM?) ✓ PoPT-Scheduler ✓ Autoconnect @@ -163,6 +183,9 @@ Ideas/TODO's : ✓ Bin ✓ AutoBin + - Super-Packet ? SP\- anfängt und maximal 80 Zeichen lang ist + (Eine Textzeile plus Umbruch) + ✓ Yapp - Yapp Server Modes - Yapp Resume @@ -179,6 +202,7 @@ Ideas/TODO's : ✓ PortFilter - Call-Filter - U/UI - Frame Filter + - Daten Komprimierung - Daten Verschlüsselung (AES/RSA) ✓ ordentliches Noden System bzw CLI überarbeiten @@ -187,6 +211,7 @@ Ideas/TODO's : - Optional nur als Konsolen Anwendung ohne GUI. - Drucker Ausgabe vom QSO - kompatibel mit Nadeldrucker + - Torrent ähnliches Filesharing via UI Frames (Siehe ?DMR/DRM?) Oder: - BBS TSTHOST? diff --git a/fnc/ax25_fnc.py b/fnc/ax25_fnc.py index 9018a60f..0360327c 100644 --- a/fnc/ax25_fnc.py +++ b/fnc/ax25_fnc.py @@ -101,4 +101,16 @@ def validate_ax25Call(call_str: str): for c in call: if not any((c.isupper(), c.isdigit())): return False - return True \ No newline at end of file + return True + +def is_digipeated_pre_digi(ax25_conf: dict, call: str): + vias = ax25_conf.get('via_calls_str_c_bit', []) + if not vias: + return False + for el in vias: + if el == (call, False): + return True + if not el[1]: + return False + return False + diff --git a/fnc/gpio_fnc.py b/fnc/gpio_fnc.py new file mode 100644 index 00000000..3449251f --- /dev/null +++ b/fnc/gpio_fnc.py @@ -0,0 +1,178 @@ +""" +PoPT GPIO Functions. +Pi GPIO 0-26 + +setup_gpio(pin) +set_gpio_dir(pin, False) - Fasle = Output | True = Input +set_gpio_val(pin, bool) +get_gpio_val(pin) -> bool / None +""" + +import os + +from cfg.constant import GPIO_PATH +from cfg.logger_config import logger +from fnc.os_fnc import is_linux, path_exists + + +def is_gpio_device(): + if not is_linux(): + return False + return path_exists(GPIO_PATH) + +def is_gpio_init(gpio: int): + gpio_path = GPIO_PATH + f"/gpio{gpio}" + return path_exists(gpio_path) + +def close_gpio(gpio: int): + if not is_gpio_device(): + logger.warning(f"GPIO: close_gpio No GPIO Device") + return False + if not is_gpio_init(gpio): + return False + os_cmd = f"echo {gpio} > {GPIO_PATH}/unexport" + try: + ret = os.system(os_cmd) + except TypeError: + logger.error(f"GPIO: close_gpio TypeError: {os_cmd}") + return False + if ret: + logger.error(f"GPIO: close_gpio() 1 > {ret}") + return False + return True + +def set_gpio_dir(gpio: int, gpio_dir_in: bool): + if not is_gpio_device(): + logger.warning(f"GPIO: set_gpio_dir No GPIO Device") + return False + gpio_path = GPIO_PATH + f"/gpio{gpio}" + if not path_exists(gpio_path): + # Init nicht erfolgreich ? gpio Pfad wurde nicht angelegt vom OS + logger.error(f"GPIO: set_gpio_dir() 2 > Path doesn't exists {gpio_path}") + return False + gpio_direction = { + True: 'in', + False: 'out', + }.get(gpio_dir_in, 'out') + os_cmd = f"echo {gpio_direction} > {gpio_path}/direction" + try: + ret = os.system(os_cmd) + except TypeError: + logger.error(f"GPIO: set_gpio_dir TypeError: {os_cmd}") + return False + if ret: + logger.error(f"GPIO: set_gpio_dir() 3 > {ret}") + return False + return True + +def setup_gpio(gpio: int): + if not is_gpio_device(): + logger.warning(f"GPIO: setup_gpio No GPIO Device") + return False + if is_gpio_init(gpio): + # Bereits aktiviert + logger.warning(f"GPIO: setup_gpio() > {gpio} bereits initialisiert.") + return False + os_cmd = f"echo {gpio} > {GPIO_PATH}/export" + try: + ret = os.system(os_cmd) + except TypeError: + logger.error(f"GPIO: setup_gpio TypeError: {os_cmd}") + return False + if ret: + logger.error(f"GPIO: setup_gpio() 1 > {ret}") + return False + gpio_path = GPIO_PATH + f"/gpio{gpio}" + if not path_exists(gpio_path): + # Init nicht erfolgreich ? gpio Pfad wurde nicht angelegt vom OS + logger.error(f"GPIO: setup_gpio() 2 > Path doesn't exists {gpio_path}") + return False + return True + +def get_gpio_dir(gpio: int): + if not is_gpio_device(): + logger.warning(f"GPIO: get_gpio_dir No GPIO Device") + return None + if not is_gpio_init(gpio): + logger.warning(f"GPIO: get_gpio_dir() 1 > GPIO {gpio} nicht initialisiert !") + return None + gpio_path = GPIO_PATH + f"/gpio{gpio}" + os_cmd = f"cat {gpio_path}/direction" + try: + return os.popen(os_cmd).read()[:-1] + except TypeError: + logger.error(f"GPIO: get_gpio_dir TypeError: {os_cmd}") + return None + +def get_gpio_val(gpio: int): + if not is_gpio_device(): + logger.warning(f"GPIO: get_gpio_val No GPIO Device") + return None + if not is_gpio_init(gpio): + logger.warning(f"GPIO: get_gpio_val() 1 > GPIO {gpio} nicht initialisiert !") + return None + gpio_path = GPIO_PATH + f"/gpio{gpio}" + os_cmd = f"cat {gpio_path}/value" + try: + ret = os.popen(os_cmd).read() + except TypeError: + logger.error(f"GPIO: get_gpio_val TypeError: {os_cmd}") + return None + try: + return bool(int(ret)) + except ValueError: + logger.error(f"GPIO: get_gpio_val ValueError os.popen: {ret}") + return None + +def set_gpio_val(gpio: int, value: bool): + if not is_gpio_device(): + logger.warning(f"GPIO: set_gpio_val No GPIO Device") + return False + if not is_gpio_init(gpio): + logger.warning(f"GPIO: set_gpio_val() 1 > GPIO {gpio} nicht initialisiert !") + return False + if get_gpio_dir(gpio) == 'in': + logger.warning(f"GPIO: set_gpio_val() 1 > GPIO {gpio} ist nicht als Ausgang konfiguriert!") + return False + gpio_path = GPIO_PATH + f"/gpio{gpio}" + os_cmd = f"echo {int(value)} > {gpio_path}/value" + try: + ret = os.system(os_cmd) + except TypeError: + logger.error(f"GPIO: set_gpio_val TypeError: {os_cmd}") + return False + if ret: + logger.error(f"GPIO: set_gpio_val() 1 > {ret}") + return False + return True + +""" +for pin in range(0, 27): + set_gpio_val(pin, False) + +for pin in range(0, 27): + setup_gpio(pin) + +for pin in range(0, 27): + set_gpio_dir(pin, False) + +for pin in range(0, 27): + close_gpio(pin) + +for pin in range(0, 27): + is_gpio_init(pin) + +klick = True +while True: + try: + for pin in range(1, 27): + set_gpio_val(pin, klick) + except KeyboardInterrupt: + break + time.sleep(0.5) + klick = not klick + +""" + + + diff --git a/fnc/one_wire_fnc.py b/fnc/one_wire_fnc.py new file mode 100644 index 00000000..659da1a3 --- /dev/null +++ b/fnc/one_wire_fnc.py @@ -0,0 +1,43 @@ +from cfg.constant import ONE_WIRE_PATH +from fnc.file_fnc import get_str_fm_file +from fnc.os_fnc import is_linux, path_exists + + +def is_1wire_device(): + if not is_linux(): + return False + return path_exists(ONE_WIRE_PATH) + +def get_all_1wire_paths(): + wire_dev = get_str_fm_file(ONE_WIRE_PATH + '/w1_master_slaves').split('\n')[:-1] + if not wire_dev: + return [] + return wire_dev + +def get_max_1wire(): + try: + return int(get_str_fm_file(ONE_WIRE_PATH + '/w1_master_max_slave_count').split('\n')[0]) + except (ValueError, IndexError, AttributeError, TypeError): + return 0 + +def get_1wire_timeout(): + try: + return int(get_str_fm_file(ONE_WIRE_PATH + '/w1_master_timeout').split('\n')[0]) + except (ValueError, IndexError, AttributeError, TypeError): + return 0 + + +def get_1wire_temperature(device_path: str): + if not is_1wire_device(): + return () + try: + tempData = int(get_str_fm_file(f'{ONE_WIRE_PATH}/{device_path}/temperature').split('\n')[0]) + except (ValueError, IndexError, AttributeError, TypeError): + return () + + """Source: https://st-page.de/2018/01/20/tutorial-raspberry-pi-temperaturmessung-mit-ds18b20/""" + tempCelsius = round(float(tempData) / 1000, 1) + # tempKelvin = 273 + float(tempData) / 1000 + tempFahrenheit = round(float(tempData) / 1000 * 9.0 / 5.0 + 32.0, 1) + return float(tempCelsius), float(tempFahrenheit) + diff --git a/fnc/os_fnc.py b/fnc/os_fnc.py index ba5f3d45..600b3703 100644 --- a/fnc/os_fnc.py +++ b/fnc/os_fnc.py @@ -9,6 +9,14 @@ def is_linux(): def is_windows(): return 'win' in sys.platform +""" +def is_raspberry(): + if is_windows(): + return False + if os.uname()[1] == 'raspberrypi': + return True + return False +""" def get_root_dir(): return os.getcwd() @@ -16,3 +24,4 @@ def get_root_dir(): def path_exists(path): return os.path.exists(path) + diff --git a/gui/aprs/guiAPRS_be_tracer.py b/gui/aprs/guiAPRS_be_tracer.py index 0a521ae8..94ee51dc 100644 --- a/gui/aprs/guiAPRS_be_tracer.py +++ b/gui/aprs/guiAPRS_be_tracer.py @@ -4,7 +4,11 @@ from ax25.ax25InitPorts import PORT_HANDLER from cfg.constant import CFG_TR_DX_ALARM_BG_CLR +from cfg.popt_config import POPT_CFG from cfg.string_tab import STR_TABLE +from fnc.ax25_fnc import get_list_fm_viaStr + + # from cfg.logger_config import logger @@ -12,7 +16,7 @@ class BeaconTracer(tk.Toplevel): def __init__(self, root_win): tk.Toplevel.__init__(self) self._root_win = root_win - self._lang = self._root_win.language + self._lang = POPT_CFG.get_guiCFG_language() # self._ais_obj = PORT_HANDLER.get_aprs_ais() self.style = self._root_win.style self.geometry(f"1300x" @@ -35,7 +39,7 @@ def __init__(self, root_win): lower_frame = tk.Frame(self) # Selected Info upper_frame.pack(side=tk.TOP, fill=tk.BOTH, pady=10) # upper_frame2.pack(side=tk.TOP, fill=tk.BOTH, pady=10) - middle_frame.pack(side=tk.TOP, fill=tk.BOTH, pady=10) + middle_frame.pack(side=tk.TOP, fill=tk.BOTH, pady=10, expand=True) lower_frame.pack(side=tk.TOP, fill=tk.BOTH, pady=10) ########################## @@ -47,10 +51,10 @@ def __init__(self, root_win): frame_2 = tk.Frame(upper_frame) frame_2.pack(side=tk.TOP, fill=tk.BOTH) frame_2_port = tk.Frame(upper_frame) - frame_2_port.pack(side=tk.LEFT, fill=tk.BOTH, padx=30) + frame_2_port.pack(side=tk.LEFT, fill=tk.BOTH, padx=10) # Port - self._be_port_var = tk.StringVar(frame_2_port) + self._be_port_var = tk.StringVar(self) options = list(PORT_HANDLER.get_all_ports().keys()) if len(options) > PORT_HANDLER.get_aprs_ais().be_tracer_port: self._be_port_var.set(options[PORT_HANDLER.get_aprs_ais().be_tracer_port]) @@ -61,8 +65,8 @@ def __init__(self, root_win): # Station / Call frame_2_stat = tk.Frame(upper_frame) - frame_2_stat.pack(side=tk.LEFT, fill=tk.BOTH, padx=30) - self._be_stat_var = tk.StringVar(frame_2_stat) + frame_2_stat.pack(side=tk.LEFT, fill=tk.BOTH, padx=10) + self._be_stat_var = tk.StringVar(self) # options = list(PORT_HANDLER.ge) options = PORT_HANDLER.get_stat_calls_fm_port(PORT_HANDLER.get_aprs_ais().be_tracer_port) @@ -74,10 +78,19 @@ def __init__(self, root_win): values=options) self._be_stat_opt.pack(side=tk.LEFT, ) + # VIA + frame_2_via = tk.Frame(upper_frame) + frame_2_via.pack(side=tk.LEFT, fill=tk.BOTH, padx=10) + self._be_via_var = tk.StringVar(self) + path = ' '.join(PORT_HANDLER.get_aprs_ais().be_tracer_via) + self._be_via_var.set(path) + tk.Label(frame_2_via, text='via ').pack(side=tk.LEFT, ) + tk.Entry(frame_2_via, textvariable=self._be_via_var, width=25).pack(side=tk.LEFT, ) + # WIDE frame_2_wide = tk.Frame(upper_frame) - frame_2_wide.pack(side=tk.LEFT, fill=tk.BOTH, padx=30) - self._be_wide_var = tk.StringVar(frame_2_wide) + frame_2_wide.pack(side=tk.LEFT, fill=tk.BOTH, padx=10) + self._be_wide_var = tk.StringVar(self) self._be_wide_var.set(str(PORT_HANDLER.get_aprs_ais().be_tracer_wide)) tk.Label(frame_2_wide, text='via WIDE ').pack(side=tk.LEFT, ) @@ -92,8 +105,8 @@ def __init__(self, root_win): # Interval frame_2_interval = tk.Frame(upper_frame) - frame_2_interval.pack(side=tk.LEFT, fill=tk.BOTH, padx=30) - self._be_interval_var = tk.StringVar(frame_2_interval) + frame_2_interval.pack(side=tk.LEFT, fill=tk.BOTH, padx=10) + self._be_interval_var = tk.StringVar(self) self._be_interval_var.set(str(PORT_HANDLER.get_aprs_ais().be_tracer_interval)) tk.Label(frame_2_interval, text='Interval ').pack(side=tk.LEFT, ) @@ -108,8 +121,8 @@ def __init__(self, root_win): # activ Checkbox frame_2_active = tk.Frame(upper_frame) - frame_2_active.pack(side=tk.LEFT, fill=tk.BOTH, padx=30) - self._be_active_var = tk.BooleanVar(frame_2_active) + frame_2_active.pack(side=tk.LEFT, fill=tk.BOTH, padx=10) + self._be_active_var = tk.BooleanVar(self) self._be_active_var.set(PORT_HANDLER.get_aprs_ais().be_tracer_active) tk.Label(frame_2_active, text='Activate ').pack(side=tk.LEFT, ) tk.Checkbutton(frame_2_active, @@ -123,7 +136,7 @@ def __init__(self, root_win): upper_frame, text='SAVE', command=self._save_btn - ).pack(side=tk.LEFT, fill=tk.BOTH, padx=40) + ).pack(side=tk.LEFT, fill=tk.BOTH, padx=20) # Send Button @@ -140,7 +153,7 @@ def __init__(self, root_win): # activ Checkbox frame_21_active = tk.Frame(lower_frame) frame_21_active.pack(side=tk.LEFT, fill=tk.BOTH, padx=30) - self._alarm_active_var = tk.BooleanVar(frame_21_active) + self._alarm_active_var = tk.BooleanVar(self) self._alarm_active_var.set(PORT_HANDLER.get_aprs_ais().be_tracer_alarm_active) tk.Label(frame_21_active, text='Activate ').pack(side=tk.LEFT, ) tk.Checkbutton(frame_21_active, @@ -151,7 +164,7 @@ def __init__(self, root_win): # Alarm Distance frame_21_distance = tk.Frame(lower_frame) frame_21_distance.pack(side=tk.LEFT, fill=tk.BOTH, padx=30) - self._alarm_distance_var = tk.StringVar(frame_21_distance) + self._alarm_distance_var = tk.StringVar(self) self._alarm_distance_var.set(str(PORT_HANDLER.get_aprs_ais().be_tracer_alarm_range)) tk.Label(frame_21_distance, text='Distance ').pack(side=tk.LEFT, ) @@ -167,7 +180,7 @@ def __init__(self, root_win): ########################## # Middle Frame ( Treeview ) tree_Frame = tk.Frame(middle_frame) - tree_Frame.pack(fill=tk.BOTH) + tree_Frame.pack(fill=tk.BOTH, expand=True) tree_Frame.grid_rowconfigure(0, weight=1) tree_Frame.grid_columnconfigure(0, weight=1) self._tree_data = [] @@ -265,6 +278,8 @@ def _save_vars(self): PORT_HANDLER.get_aprs_ais().be_tracer_port = int(self._be_port_var.get()) PORT_HANDLER.get_aprs_ais().be_tracer_station = self._be_stat_var.get() PORT_HANDLER.get_aprs_ais().be_tracer_wide = self._be_wide_var.get() + path = get_list_fm_viaStr(self._be_via_var.get()) + PORT_HANDLER.get_aprs_ais().be_tracer_via = list(path) PORT_HANDLER.get_aprs_ais().be_tracer_interval = int(self._be_interval_var.get()) PORT_HANDLER.get_aprs_ais().be_tracer_active = self._be_active_var.get() PORT_HANDLER.get_aprs_ais().be_tracer_alarm_active = bool(self._alarm_active_var.get()) diff --git a/gui/ft/guiFileTX.py b/gui/ft/guiFileTX.py index 7728d423..94aa0d30 100644 --- a/gui/ft/guiFileTX.py +++ b/gui/ft/guiFileTX.py @@ -112,8 +112,8 @@ def _select_files(self): self.attributes("-topmost", False) # self.root.lower filetypes = ( + ('All files', '*.*'), ('text files', '*.txt'), - ('All files', '*.*') ) filenames = fd.askopenfilenames( diff --git a/gui/guiMain.py b/gui/guiMain.py index d70b8471..25b6bc13 100644 --- a/gui/guiMain.py +++ b/gui/guiMain.py @@ -45,7 +45,7 @@ from gui.guiMsgBoxes import open_file_dialog, save_file_dialog from gui.ft.guiFileTX import FileSend from cfg.constant import FONT, POPT_BANNER, WELCOME_SPEECH, VER, MON_SYS_MSG_CLR_FG, STATION_TYPS, \ - ENCODINGS, TEXT_SIZE_STATUS, TXT_BACKGROUND_CLR, TXT_OUT_CLR, TXT_INP_CURSOR_CLR, \ + ENCODINGS, TEXT_SIZE_STATUS, TXT_INP_CURSOR_CLR, \ STAT_BAR_CLR, STAT_BAR_TXT_CLR, FONT_STAT_BAR, STATUS_BG, PARAM_MAX_MON_LEN, CFG_sound_RX_BEEP, \ SERVICE_CH_START, DEF_STAT_QSO_TX_COL, DEF_STAT_QSO_BG_COL, DEF_STAT_QSO_RX_COL, DEF_PORT_MON_BG_COL, \ DEF_PORT_MON_RX_COL, DEF_PORT_MON_TX_COL, MON_SYS_MSG_CLR_BG, F_KEY_TAB_LINUX, F_KEY_TAB_WIN, DEF_QSO_SYSMSG_FG, \ @@ -346,6 +346,7 @@ def __del__(self): def _destroy_win(self): self.sysMsg_to_monitor("PoPT wird beendet.") + self._Pacman.save_path_data() logger.info('GUI: Closing GUI') for wn in [ self.settings_win, @@ -368,7 +369,6 @@ def _destroy_win(self): wn.destroy() self._quit = True logger.info('GUI: Closing GUI: Save GUI Vars & Parameter.') - self._Pacman.save_path_data() self.save_GUIvars() self._save_parameter() self._save_Channel_Vars() @@ -383,7 +383,7 @@ def save_GUIvars(self): ######################### # GUI-Vars to cfg guiCfg = POPT_CFG.load_guiPARM_main() - guiCfg['gui_lang'] = int(self.language) + # guiCfg['gui_lang'] = int(self.language) guiCfg['gui_cfg_sound'] = bool(self.setting_sound.get()) guiCfg['gui_cfg_beacon'] = bool(self.setting_bake.get()) guiCfg['gui_cfg_rx_echo'] = bool(self.setting_rx_echo.get()) @@ -394,7 +394,10 @@ def save_GUIvars(self): guiCfg['gui_cfg_noty_bell'] = bool(self.setting_noty_bell.get()) guiCfg['gui_cfg_sprech'] = bool(self.setting_sprech.get()) guiCfg['gui_cfg_mon_encoding'] = str(self.setting_mon_encoding.get()) - guiCfg['gui_cfg_rtab_index'] = (self.tabbed_sideFrame.get_tab_index(), self.tabbed_sideFrame2.get_tab_index()) + try: + guiCfg['gui_cfg_rtab_index'] = int(self.tabbed_sideFrame.get_tab_index()), int(self.tabbed_sideFrame2.get_tab_index()) + except (ValueError, tk.TclError): + pass # guiCfg['gui_cfg_locator'] = str(self.own_loc) # guiCfg['gui_cfg_qth'] = str(self.own_qth) POPT_CFG.save_guiPARM_main(guiCfg) @@ -1803,6 +1806,8 @@ def _monitor_task(self): mon_str = mon_out[0] + mon_out[1] else: mon_str = mon_out[0] + if not mon_str.endswith('\n'): + mon_str += '\n' var = tk_filter_bad_chars(mon_str) ind = self._mon_txt.index('end-1c') # TODO Autoscroll @@ -1948,6 +1953,7 @@ def open_MH_win(self): if self.mh_window is None: MHWin(self) self.tabbed_sideFrame.reset_dx_alarm() + self.tabbed_sideFrame2.reset_dx_alarm() ####################################################### """ @@ -2235,7 +2241,7 @@ def _noty_bell(self, ch_id, msg=''): if not msg: msg = f"{conn.to_call_str} {STR_TABLE['cmd_bell_gui_msg'][self.language]}" if messagebox.askokcancel(f"Bell {STR_TABLE['channel'][self.language]} {ch_id}", - msg, parent=self): + msg, parent=self.main_win): if not self._quit: self.switch_channel(ch_id) diff --git a/gui/guiMain_TabbedSideFrame.py b/gui/guiMain_TabbedSideFrame.py index 50ebedec..24d2743b 100644 --- a/gui/guiMain_TabbedSideFrame.py +++ b/gui/guiMain_TabbedSideFrame.py @@ -28,13 +28,13 @@ def __init__(self, main_cl, frame, plot_frame=None, path_frame=None): ) self._tabControl.bind('<>', self.on_ch_stat_change) - tab1_kanal = ttk.Frame(self._tabControl) - tab_connects = ttk.Frame(self._tabControl) - tab2_mh = ttk.Frame(self._tabControl) - tab4_settings = ttk.Frame(self._tabControl) + tab1_kanal = tk.Frame(self._tabControl) + tab_connects = tk.Frame(self._tabControl) + tab2_mh = tk.Frame(self._tabControl) + tab4_settings = tk.Frame(self._tabControl) # self.tab5_ch_links = ttk.Frame(self._tabControl) # TODO - tab6_monitor = ttk.Frame(self._tabControl) - tab7_tracer = ttk.Frame(self._tabControl) + tab6_monitor = tk.Frame(self._tabControl) + tab7_tracer = tk.Frame(self._tabControl) # self._path_plot = None self._tabControl.add(tab1_kanal, text=get_strTab('channel', self._lang)) @@ -142,7 +142,7 @@ def __init__(self, main_cl, frame, plot_frame=None, path_frame=None): parm_y = 225 self._autoscroll_var = tk.BooleanVar(tab1_kanal) - autoscroll = ttk.Checkbutton(tab1_kanal, + autoscroll = tk.Checkbutton(tab1_kanal, text='Autoscroll', variable=self._autoscroll_var, command=self._chk_autoscroll @@ -154,7 +154,7 @@ def __init__(self, main_cl, frame, plot_frame=None, path_frame=None): parm_y = 250 self._cliRemote_var = tk.BooleanVar(tab1_kanal, value=True) - self._cliRemote = ttk.Checkbutton(tab1_kanal, + self._cliRemote = tk.Checkbutton(tab1_kanal, text='CLI/Remote', variable=self._cliRemote_var, state='disabled', @@ -165,7 +165,7 @@ def __init__(self, main_cl, frame, plot_frame=None, path_frame=None): # Link Holder parm_y = 175 # self.link_holder_var = tk.BooleanVar(tab1_kanal) - self._link_holder = ttk.Checkbutton(tab1_kanal, + self._link_holder = tk.Checkbutton(tab1_kanal, text=get_strTab('linkholder', self._lang), variable=self._main_win.link_holder_var, state='disabled', @@ -295,7 +295,7 @@ def __init__(self, main_cl, frame, plot_frame=None, path_frame=None): # self._update_side_mh() self._tree.bind('<>', self._entry_selected) - btn_frame = ttk.Frame(tab2_mh) + btn_frame = tk.Frame(tab2_mh) btn_frame.grid(row=1, column=0, columnspan=2) ttk.Button(btn_frame, text="MH", @@ -315,19 +315,19 @@ def __init__(self, main_cl, frame, plot_frame=None, path_frame=None): ############################################################################# # Global Settings ################################ # Global Sound - ttk.Checkbutton(tab4_settings, + tk.Checkbutton(tab4_settings, text="Sound", variable=self._main_win.setting_sound, command=self._chk_sound ).place(x=10, y=10) # Bell - ttk.Checkbutton(tab4_settings, + tk.Checkbutton(tab4_settings, text="Bell", variable=self._main_win.setting_noty_bell, command=self._main_win.set_noty_bell_active ).place(x=150, y=10) # Global Sprech - sprech_btn = ttk.Checkbutton(tab4_settings, + sprech_btn = tk.Checkbutton(tab4_settings, text=get_strTab('sprech', self._lang), variable=self._main_win.setting_sprech, command=self._chk_sprech_on @@ -336,13 +336,13 @@ def __init__(self, main_cl, frame, plot_frame=None, path_frame=None): if not is_linux(): sprech_btn.configure(state='disabled') # Global Bake - ttk.Checkbutton(tab4_settings, + tk.Checkbutton(tab4_settings, text=get_strTab('beacon', self._lang), variable=self._main_win.setting_bake, command=self._chk_beacon, ).place(x=10, y=60) # DX Alarm > dx_alarm_on - ttk.Checkbutton(tab4_settings, + tk.Checkbutton(tab4_settings, text="Tracer", variable=self._main_win.setting_tracer, command=self._chk_tracer, @@ -352,21 +352,21 @@ def __init__(self, main_cl, frame, plot_frame=None, path_frame=None): True: 'disabled', False: 'normal' }.get(self._main_win.get_tracer(), 'disabled') - self._autotracer_chk_btn = ttk.Checkbutton(tab4_settings, + self._autotracer_chk_btn = tk.Checkbutton(tab4_settings, text="Auto-Tracer", variable=self._main_win.setting_auto_tracer, command=self._chk_auto_tracer, state=auto_tracer_state ) self._autotracer_chk_btn.place(x=10, y=110) - ttk.Checkbutton(tab4_settings, + tk.Checkbutton(tab4_settings, text="DX-Alarm", variable=self._main_win.setting_dx_alarm, command=self._main_win.set_dx_alarm, # state='disabled' ).place(x=10, y=135) # RX ECHO - ttk.Checkbutton(tab4_settings, + tk.Checkbutton(tab4_settings, text="RX-Echo", variable=self._main_win.setting_rx_echo, command=self._set_rx_echo @@ -389,7 +389,7 @@ def __init__(self, main_cl, frame, plot_frame=None, path_frame=None): x = 10 y = 80 # self.cmd_var = tk.BooleanVar(tab6_monitor) - ttk.Checkbutton(tab6_monitor, + tk.Checkbutton(tab6_monitor, variable=self._main_win.mon_cmd_var, text='CMD/RPT').place(x=x, y=y) @@ -894,13 +894,13 @@ def _update_side_trace(self): # self._update_trace_tree() def _format_trace_tree_data(self): - traces = dict(PORT_HANDLER.get_aprs_ais().tracer_traces_get()) + traces: dict = dict(PORT_HANDLER.get_aprs_ais().tracer_traces_get()) if self._trace_tree_data_old != len(str(traces)): self._trace_tree_data_old = len(str(traces)) self._trace_tree_data = [] for k in traces.keys(): - pack = traces[k][-1] - rx_time = pack.get('rx_time', '') + pack: dict = traces[k][-1] + rx_time = pack.get('rx_time', datetime.now()) delta = datetime.now() - rx_time if not delta.days: if rx_time: diff --git a/gui/plots/guiLiveConnPath.py b/gui/plots/guiLiveConnPath.py index 52cbeb77..047b23d1 100644 --- a/gui/plots/guiLiveConnPath.py +++ b/gui/plots/guiLiveConnPath.py @@ -1,3 +1,8 @@ +""" +TODO: + - Port 0 (Monitor) MH-Plot +""" + import tkinter as tk # from matplotlib.backends._backend_tk import NavigationToolbar2Tk @@ -205,4 +210,4 @@ def update_plot_f_ch(self, ch_id): self._update_Graph(ch_id) def save_path_data(self): - POPT_CFG.set_pacman_data(self._path_data) + POPT_CFG.set_pacman_data(dict(self._path_data)) diff --git a/gui/pms/guiBBS_fwd_q.py b/gui/pms/guiBBS_fwd_q.py index 445e4aea..ea27e9c0 100644 --- a/gui/pms/guiBBS_fwd_q.py +++ b/gui/pms/guiBBS_fwd_q.py @@ -122,10 +122,10 @@ def _format_tree_data(self): def _sort_entry(self, col): """ Source: https://stackoverflow.com/questions/1966929/tk-treeview-column-sort """ - _tmp = [(self._tree.set(k, col), k) for k in self._tree.get_children('')] - _tmp.sort(reverse=self._rev_ent) + tmp = [(self._tree.set(k, col), k) for k in self._tree.get_children('')] + tmp.sort(reverse=self._rev_ent) self._rev_ent = not self._rev_ent - for index, (val, k) in enumerate(_tmp): + for index, (val, k) in enumerate(tmp): self._tree.move(k, '', int(index)) def _do_fwd(self): diff --git a/gui/settings/gui1WireSettings.py b/gui/settings/gui1WireSettings.py new file mode 100644 index 00000000..4fbe3668 --- /dev/null +++ b/gui/settings/gui1WireSettings.py @@ -0,0 +1,184 @@ +import threading +import tkinter as tk +from tkinter import ttk + +from cfg.constant import ONE_WIRE_MAP, FONT +from cfg.default_config import getNew_1wire_device_cfg +from cfg.popt_config import POPT_CFG +from fnc.one_wire_fnc import is_1wire_device, get_all_1wire_paths, get_1wire_temperature, get_max_1wire + + +class OneWireSettings(tk.Frame): + def __init__(self, tabctl, root_win=None): + tk.Frame.__init__(self, tabctl) + ################################ + # self._lang = POPT_CFG.get_guiCFG_language() + ################################ + self._rev_ent = False + self._update_th = None + self._sens_cfg: dict = dict(POPT_CFG.get_1wire_sensor_cfg()) + self._read_timer_var = tk.StringVar(self) + self._read_timer_var.set(str(POPT_CFG.get_1wire_loop_timer())) + + self._max_sensor_label = lambda x: f"Max {x} Sensors" + self._max_sensors = int(get_max_1wire()) + self._max_sensor_label_var = tk.StringVar(self, value=self._max_sensor_label(self._max_sensors)) + # self._metric_var = tk.StringVar(self, value='Metrisch') # NEEEEEEE + ################################ + upper_frame = tk.Frame(self) + upper_frame.pack(fill=tk.X) + ################################ + global_opt_frame = tk.Frame(upper_frame) + global_opt_frame.pack(side=tk.LEFT, fill=tk.Y, expand=False) + # + rt_opt_frame = tk.Frame(global_opt_frame) + rt_opt_frame.pack(fill=tk.Y, padx=10, pady=10) + tk.Label(rt_opt_frame, text='Sensor Read Timer in s:').pack(side=tk.LEFT, padx=15) + opt = [x * 30 for x in range(1, 13)] + read_timer_menu = tk.OptionMenu(rt_opt_frame, self._read_timer_var, *opt) + read_timer_menu.pack(side=tk.LEFT, padx=5) + # + """ + metric_opt_frame = tk.Frame(global_opt_frame) + metric_opt_frame.pack(fill=tk.Y, padx=10) + tk.Label(metric_opt_frame, text='Sensor Read Timer in s:').pack(side=tk.LEFT, padx=15) + opt = ['Metrisch', 'Imperiales'] + metric_menu = tk.OptionMenu(metric_opt_frame, self._metric_var, *opt) + metric_menu.pack(side=tk.LEFT, padx=5) + """ + # + tk.Label(global_opt_frame, textvariable=self._max_sensor_label_var).pack() + tk.Button(global_opt_frame, text='Update', command=self._update_sensors).pack(pady=15) + ################################ + wire_frame = tk.Frame(upper_frame) + wire_frame.pack(fill=tk.X, expand=True) + # + tk.Label(wire_frame, text=ONE_WIRE_MAP, font=(FONT, 9)).pack(pady=10) + ################################ + ################################ + lower_frame = tk.Frame(self) + lower_frame.pack(fill=tk.BOTH, expand=True) + ########################################################################################## + # TREE + columns = ( + 'str_var', + 'sens_id', + 'sens_val_c', + # 'sens_val_f', + ) + self._tree = ttk.Treeview(lower_frame, columns=columns, show='headings') + self._tree.heading('str_var', text='TextVAR', command=lambda: self._sort_entry('str_var')) + self._tree.heading('sens_id', text='Sensor ID', command=lambda: self._sort_entry('sens_id')) + self._tree.heading('sens_val_c', text='Temp °C', command=lambda: self._sort_entry('sens_val_c')) + # self._tree.heading('sens_val_f', text='Temp °F', command=lambda: self._sort_entry('sens_val_f')) + self._tree.column("str_var", anchor=tk.W, stretch=tk.YES, width=100) + self._tree.column("sens_id", anchor=tk.W, stretch=tk.YES, width=100) + self._tree.column("sens_val_c", anchor=tk.CENTER, stretch=tk.NO, width=100) + # self._tree.column("sens_val_f", anchor=tk.CENTER, stretch=tk.NO, width=100) + + self._tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + scrollbar = ttk.Scrollbar(lower_frame, orient=tk.VERTICAL, command=self._tree.yview) + scrollbar.pack(side=tk.LEFT, fill=tk.Y, expand=False) + self._tree.configure(yscrollcommand=scrollbar.set) + + self._update_sensors() + + def _sort_entry(self, col): + """ Source: https://stackoverflow.com/questions/1966929/tk-treeview-column-sort """ + tmp = [(self._tree.set(k, col), k) for k in self._tree.get_children('')] + tmp.sort(reverse=self._rev_ent) + self._rev_ent = not self._rev_ent + for index, (val, k) in enumerate(tmp): + self._tree.move(k, '', int(index)) + + def _update_max_sensors(self): + if not is_1wire_device(): + self._max_sensors = 0 + self._max_sensor_label_var.set(value=self._max_sensor_label('n/a')) + return + self._max_sensors = int(get_max_1wire()) + self._max_sensor_label_var.set(value=self._max_sensor_label(self._max_sensors)) + + def _update_sensors(self, event=None): + if self._update_th is None: + self._update_th_run() + return + if self._update_th.is_alive(): + return + self._update_th_run() + return + + def _update_th_run(self): + self._update_max_sensors() + self._update_th = threading.Thread(target=self._update_sensors_th) + self._update_th.start() + + def _update_sensors_th(self): + if not is_1wire_device(): + self._reset_tree_data() + return + sensors_paths: list = get_all_1wire_paths() + if not sensors_paths: + self._reset_tree_data() + return + sens_data = {} + for sens_id in sensors_paths: + sens_data[sens_id]: str = str(get_1wire_temperature(sens_id)[0]) + sensors = list(sens_data.keys()) + # Update / Delete old Sensors + for strVar, cfg in dict(self._sens_cfg).items(): + tmp_id = cfg.get('device_path', '') + if tmp_id in sensors: + cfg['device_value'] = sens_data.get(tmp_id, None) + cfg['StringVar'] = str(strVar) + sensors.remove(tmp_id) + else: + del self._sens_cfg[strVar] + # Add New Sensors + for sens_id in sensors: + n = 1 + while f"$1wire-{n}" in self._sens_cfg: + n += 1 + if n > self._max_sensors: + self._update_tree() + return + strVar = f"$1wire-{n}" + wire_cfg = getNew_1wire_device_cfg(sens_id) + wire_cfg['device_value'] = sens_data.get(sens_id, None) + wire_cfg['StringVar'] = str(strVar) + self._sens_cfg[strVar] = wire_cfg + + self._update_tree() + + def _update_tree(self): + for i in self._tree.get_children(): + self._tree.delete(i) + + for strVar, cfg in dict(self._sens_cfg).items(): + val = (strVar, + cfg.get('device_path', '---'), + cfg.get('device_value', 'n/a')) + self._tree.insert('', tk.END, values=val, ) + + + def _reset_tree_data(self): + self._sens_cfg = {} + for i in self._tree.get_children(): + self._tree.delete(i) + + def save_config(self): + old_sensor_cfg = POPT_CFG.get_1wire_sensor_cfg() + old_timer_cfg = POPT_CFG.get_1wire_loop_timer() + try: + new_timer_cfg = max(30, int(self._read_timer_var.get())) + except ValueError: + new_timer_cfg = 60 + if all(( + old_sensor_cfg == dict(self._sens_cfg), + old_timer_cfg == new_timer_cfg + )): + return False + + POPT_CFG.set_1wire_sensor_cfg(dict(self._sens_cfg)) + POPT_CFG.set_1wire_loop_timer(new_timer_cfg) + return True diff --git a/gui/settings/guiGPIO_Settings.py b/gui/settings/guiGPIO_Settings.py new file mode 100644 index 00000000..425c9929 --- /dev/null +++ b/gui/settings/guiGPIO_Settings.py @@ -0,0 +1,86 @@ +import tkinter as tk +from tkinter import ttk + +from cfg.constant import GPIO_RANGE +from fnc.gpio_fnc import is_gpio_init, get_gpio_dir, get_gpio_val + + +class GPIOSettings(tk.Frame): + def __init__(self, tabctl, root_win=None): + tk.Frame.__init__(self, tabctl) + ################################ + # self._lang = POPT_CFG.get_guiCFG_language() + ################################ + self._rev_ent = False + ################################ + upper_frame = tk.Frame(self) + upper_frame.pack(fill=tk.X) + ################################ + global_opt_frame = tk.Frame(upper_frame) + global_opt_frame.pack(side=tk.LEFT, fill=tk.Y, expand=False) + + tk.Button(global_opt_frame, text='Update', command=self._update_data).pack(pady=15, padx=15) + ################################ + ################################ + lower_frame = tk.Frame(self) + lower_frame.pack(fill=tk.BOTH, expand=True) + ########################################################################################## + # TREE + columns = ( + 'str_var', + 'gpio_id', + 'gpio_dir', + 'gpio_val', + # 'sens_val_f', + ) + self._tree = ttk.Treeview(lower_frame, columns=columns, show='headings') + self._tree.heading('str_var', text='TextVAR', command=lambda: self._sort_entry('str_var')) + self._tree.heading('gpio_id', text='GPIO ID', command=lambda: self._sort_entry('gpio_id')) + self._tree.heading('gpio_dir', text='IN/OUT', command=lambda: self._sort_entry('gpio_dir')) + self._tree.heading('gpio_val', text='Value', command=lambda: self._sort_entry('gpio_val')) + self._tree.column("str_var", anchor=tk.W, stretch=tk.YES, width=100) + self._tree.column("gpio_id", anchor=tk.W, stretch=tk.YES, width=100) + self._tree.column("gpio_dir", anchor=tk.CENTER, stretch=tk.NO, width=100) + self._tree.column("gpio_val", anchor=tk.CENTER, stretch=tk.NO, width=100) + + self._tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) + scrollbar = ttk.Scrollbar(lower_frame, orient=tk.VERTICAL, command=self._tree.yview) + scrollbar.pack(side=tk.LEFT, fill=tk.Y, expand=False) + self._tree.configure(yscrollcommand=scrollbar.set) + + self._update_data() + + def _sort_entry(self, col): + """ Source: https://stackoverflow.com/questions/1966929/tk-treeview-column-sort """ + tmp = [(self._tree.set(k, col), k) for k in self._tree.get_children('')] + tmp.sort(reverse=self._rev_ent) + self._rev_ent = not self._rev_ent + for index, (val, k) in enumerate(tmp): + self._tree.move(k, '', int(index)) + + def _update_data(self, event=None): + for i in self._tree.get_children(): + self._tree.delete(i) + # data = {} + for gpio in range(GPIO_RANGE[0], GPIO_RANGE[1] + 1): + if not is_gpio_init(gpio): + continue + gpio_dir = get_gpio_dir(gpio) + gpio_val = get_gpio_val(gpio) + textVar = f"$gpio-{gpio}" + val = (textVar, + int(gpio), + gpio_dir, + gpio_val) + self._tree.insert('', tk.END, values=val, ) + """ + data[gpio] = dict( + gpio_id=int(gpio), + gpio_dir=str(gpio_dir), + gpio_val=gpio_val, + ) + """ + + + def save_config(self): + return False \ No newline at end of file diff --git a/gui/settings/guiPortSettings.py b/gui/settings/guiPortSettings.py index 96bf6e48..87f93316 100644 --- a/gui/settings/guiPortSettings.py +++ b/gui/settings/guiPortSettings.py @@ -9,7 +9,7 @@ from cfg.logger_config import logger from cfg.popt_config import POPT_CFG from fnc.str_fnc import get_strTab -from gui.guiMsgBoxes import AskMsg, WarningMsg, InfoMsg +from gui.guiMsgBoxes import AskMsg from cfg.cfg_fnc import del_port_data from fnc.os_fnc import is_linux @@ -71,12 +71,15 @@ def __init__(self, main_stt_win, new_settings, tabclt: ttk.Notebook): param_next_line = 0 # param_label = tk.Label(self.tab, text='Port-Parameter:') # param_label.place(x=param_sel_x, y=height - param_sel_y) + self._param1_var = tk.StringVar(self.tab) self._param1_label = tk.Label(self.tab) - self._param1_ent = tk.Entry(self.tab) + self._param1_ent = tk.Entry(self.tab, textvariable=self._param1_var) self._param2_label = tk.Label(self.tab) self._param2_ent = tk.Entry(self.tab) + self._param1_x = int(param_sel_x + 80) + self._param1_y = int(height - param_sel_y + param_next_line) self._param1_label.place(x=param_sel_x, y=height - param_sel_y + param_next_line) - self._param1_ent.place(x=param_sel_x + 80, y=height - param_sel_y + param_next_line) + self._param1_ent.place(x=self._param1_x, y=self._param1_y) self._param2_label.place(x=param_sel_x + 500, y=height - param_sel_y + param_next_line) self._param2_ent.place(x=param_sel_x + 500 + 50, y=height - param_sel_y + param_next_line) self._param1_ent.bind('', self.set_need_reinit) @@ -567,20 +570,25 @@ def _update_port_parameter(self, dummy=None): self._calc_baud.delete(0, tk.END) # self._calc_baud.insert(tk.END, self._port_setting.parm_baud) self._calc_baud.insert(tk.END, self._port_setting.get('parm_baud', new_port_cfg.get('parm_baud', 1200))) + self._param1_label.configure(text='Adresse:') + self._param1_ent.destroy() + self._param1_ent = tk.Entry(self.tab, textvariable=self._param1_var) self._param1_ent.configure(width=28) + self._param1_ent.place(x=self._param1_x, y=self._param1_y) + self._param2_label.configure(text='Port:') self._param2_ent.configure(width=7) self._param1_label.place(x=param_sel_x, y=height - param_sel_y + param_next_line) - self._param1_ent.place(x=param_sel_x + 80, y=height - param_sel_y + param_next_line) + # self._param1_ent.place(x=param_sel_x + 80, y=height - param_sel_y + param_next_line) self._param2_label.place(x=param_sel_x + 500, y=height - param_sel_y + param_next_line) self._param2_ent.place(x=param_sel_x + 500 + 50, y=height - param_sel_y + param_next_line) - self._param1_ent.delete(0, tk.END) + # self._param1_ent.delete(0, tk.END) self._param2_ent.delete(0, tk.END) # if self._port_setting.parm_PortParm[0]: # if self._port_setting.get('parm_PortParm', new_cfg.get('parm_PortParm', ('', 0)))[0]: # self._param1_ent.insert(tk.END, self._port_setting.parm_PortParm[0]) - self._param1_ent.insert(tk.END, self._port_setting.get('parm_PortParm', new_port_cfg.get('parm_PortParm', ('', 0)))[0]) + self._param1_var.set(self._port_setting.get('parm_PortParm', new_port_cfg.get('parm_PortParm', ('', 0)))[0]) # if self._port_setting.parm_PortParm[1]: # if self._port_setting.get('parm_PortParm', new_cfg.get('parm_PortParm', ('', 0)))[1]: # self._param2_ent.insert(tk.END, self._port_setting.parm_PortParm[1]) @@ -629,21 +637,25 @@ def _update_port_parameter(self, dummy=None): self._calc_baud.configure(state="disabled") self._param1_label.configure(text='Adresse:') + self._param1_ent.destroy() + self._param1_ent = tk.Entry(self.tab, textvariable=self._param1_var) self._param1_ent.configure(width=28) + self._param1_ent.place(x=self._param1_x, y=self._param1_y) + self._param2_label.configure(text='Port:') self._param2_ent.configure(width=7) self._param1_label.place(x=param_sel_x, y=height - param_sel_y + param_next_line) - self._param1_ent.place(x=param_sel_x + 80, y=height - param_sel_y + param_next_line) + # self._param1_ent.place(x=param_sel_x + 80, y=height - param_sel_y + param_next_line) self._param2_label.place(x=param_sel_x + 500, y=height - param_sel_y + param_next_line) self._param2_ent.place(x=param_sel_x + 500 + 50, y=height - param_sel_y + param_next_line) - self._param1_ent.delete(0, tk.END) + # self._param1_ent.delete(0, tk.END) self._param2_ent.delete(0, tk.END) if self._port_setting.get('parm_PortParm', new_port_cfg.get('parm_PortParm', ('', 0)))[0]: # self._param1_ent.insert(tk.END, self._port_setting.parm_PortParm[0]) - self._param1_ent.insert(tk.END, self._port_setting.get('parm_PortParm', new_port_cfg.get('parm_PortParm', ('', 0)))[0]) + self._param1_var.set(self._port_setting.get('parm_PortParm', new_port_cfg.get('parm_PortParm', ('', 0)))[0]) else: - self._param1_ent.insert(tk.END, '0.0.0.0') + self._param1_var.set('0.0.0.0') # if self._port_setting.parm_PortParm[1]: if self._port_setting.get('parm_PortParm', new_port_cfg.get('parm_PortParm', ('', 0)))[1]: self._param2_ent.insert(tk.END, self._port_setting.get('parm_PortParm', new_port_cfg.get('parm_PortParm', ('', 0)))[1]) @@ -665,26 +677,36 @@ def _update_port_parameter(self, dummy=None): # self._calc_baud.insert(tk.END, self._port_setting.parm_baud) self._calc_baud.insert(tk.END, self._port_setting.get('parm_baud', new_port_cfg.get('parm_baud', 1200))) # self.calc_baud.configure(state="normal") - self._param1_ent.configure(width=15) self._param1_label.configure(text='Port:') + if is_linux(): + self._param1_ent.destroy() + self._param1_ent = tk.Entry(self.tab, textvariable=self._param1_var) + self._param1_ent.configure(width=15) + self._param1_ent.place(x=self._param1_x, y=self._param1_y) + else: + ser_ports = [f"COM{x}" for x in range(1, 100)] + self._param1_ent.destroy() + self._param1_ent = tk.OptionMenu(self.tab, self._param1_var, *ser_ports) + self._param1_ent.configure(width=7) + self._param1_ent.place(x=self._param1_x, y=self._param1_y) self._param2_label.configure(text='Baud:') self._param2_ent.configure(width=7) self._param1_label.place(x=param_sel_x, y=height - param_sel_y + param_next_line) - self._param1_ent.place(x=param_sel_x + 50, y=height - param_sel_y + param_next_line) + # self._param1_ent.place(x=param_sel_x + 50, y=height - param_sel_y + param_next_line) self._param2_label.place(x=param_sel_x + 250, y=height - param_sel_y + param_next_line) self._param2_ent.place(x=param_sel_x + 250 + 60, y=height - param_sel_y + param_next_line) - self._param1_ent.delete(0, tk.END) + # self._param1_ent.delete(0, tk.END) self._param2_ent.delete(0, tk.END) if self._port_setting.get('parm_PortParm', new_port_cfg.get('parm_PortParm', ('', 0)))[0]: - self._param1_ent.insert(tk.END, self._port_setting.get('parm_PortParm', new_port_cfg.get('parm_PortParm', ('', 0)))[0]) + self._param1_var.set(self._port_setting.get('parm_PortParm', new_port_cfg.get('parm_PortParm', ('', 0)))[0]) else: if is_linux(): - self._param1_ent.insert(tk.END, '/dev/ttyS1') + self._param1_var.set('/dev/ttyS1') else: - self._param1_ent.insert(tk.END, 'com1') + self._param1_var.set('COM1') if self._port_setting.get('parm_PortParm', new_port_cfg.get('parm_PortParm', ('', 0)))[1]: self._param2_ent.insert(tk.END, self._port_setting.get('parm_PortParm', new_port_cfg.get('parm_PortParm', ('', 0)))[1]) @@ -704,16 +726,18 @@ def set_vars_to_cfg(self, event=None): # Port Name self._port_setting['parm_PortName'] = self._prt_name.get() # Port TYpe - old_typ = str(self._port_setting.get('parm_PortTyp', '')) + # old_typ = str(self._port_setting.get('parm_PortTyp', '')) self._port_setting['parm_PortTyp'] = self._port_select_var.get() + """ if all((self._port_setting.get('parm_PortTyp', ''), self._port_setting.get('parm_PortTyp', '') != old_typ)): self._need_reinit = True + """ # Port Parameter try: - tmp_param = (self._param1_ent.get(), int(self._param2_ent.get())) + tmp_param = (self._param1_var.get(), int(self._param2_ent.get())) except ValueError: - tmp_param = (self._param1_ent.get(), 0) + tmp_param = (self._param1_var.get(), 0) self._port_setting['parm_PortParm'] = tmp_param # Pseudo TXD try: @@ -777,9 +801,10 @@ def set_vars_to_cfg(self, event=None): self._port_setting['parm_StationCalls'] = stat_calls self._save_cfg_to_poptCFG() - if all((self._port_setting.get('arm_PortTyp', ''), - old_cfg != self._port_setting)): - self._need_reinit = True + for cfg_k, cfg_val in self._port_setting.items(): + if old_cfg.get(cfg_k, None) != cfg_val: + self._need_reinit = True + return # self._need_reinit = False def need_reinit(self): diff --git a/gui/settings/guiSettingsMain.py b/gui/settings/guiSettingsMain.py index c9679946..4f025683 100644 --- a/gui/settings/guiSettingsMain.py +++ b/gui/settings/guiSettingsMain.py @@ -7,11 +7,15 @@ from tkinter import ttk from cfg.popt_config import POPT_CFG +# from fnc.gpio_fnc import is_gpio_device +from fnc.one_wire_fnc import is_1wire_device from fnc.str_fnc import get_strTab, lob_gen from gui.guiError import PoPTAttributError +from gui.settings.gui1WireSettings import OneWireSettings from gui.settings.guiBeaconSettings import BeaconSettings from gui.settings.guiDigiSettings import DIGI_SettingsWin from gui.settings.guiFTextSettings import FTextSettings +# from gui.settings.guiGPIO_Settings import GPIOSettings from gui.settings.guiGeneralSettings import GeneralSettings from gui.settings.guiMCastSettings import MulticastSettings from gui.settings.guiPortSettings import PortSettingsWin @@ -23,7 +27,7 @@ class SettingsMain(tk.Toplevel): def __init__(self, root_win): tk.Toplevel.__init__(self) win_width = 1200 - win_height = 640 + win_height = 660 self.style = root_win.style self.geometry(f"{win_width}x" f"{win_height}+" @@ -52,6 +56,19 @@ def __init__(self, root_win): 'MCast': MulticastSettings, 'RX-Echo': RxEchoSettings, } + # 1-Wire + if is_1wire_device(): + self._win_tab.update({ + '1-Wire' : OneWireSettings + }) + # GPIO + # if is_gpio_device(): + """ + if True: + self._win_tab.update({ + 'GPIO': GPIOSettings + }) + """ ############################################################### ########################################### diff --git a/gui/settings/guiStationSettings.py b/gui/settings/guiStationSettings.py index c7f83108..21c81943 100644 --- a/gui/settings/guiStationSettings.py +++ b/gui/settings/guiStationSettings.py @@ -800,6 +800,7 @@ def _del_station_btn(self): tab: StatSetTab = self._tab_list[ind] call = tab.call.get() POPT_CFG.del_stat_CFG_fm_call(call=call) # Del Pipe-CFG + # POPT_CFG.del_stat_CFG_fm_call(call=call) # Del Digi-CFG # del_user_data(call) del self._tab_list[ind] self._tabControl.forget(ind)