diff --git a/app/src/gen3/talent.py b/app/src/gen3/talent.py index 0081742..57f875c 100644 --- a/app/src/gen3/talent.py +++ b/app/src/gen3/talent.py @@ -8,7 +8,6 @@ if __name__ == "app.src.gen3.talent": from app.src.async_ifc import AsyncIfc from app.src.messages import Message, State from app.src.modbus import Modbus - from app.src.my_timer import Timer from app.src.config import Config from app.src.gen3.infos_g3 import InfosG3 from app.src.infos import Register @@ -16,7 +15,6 @@ else: # pragma: no cover from async_ifc import AsyncIfc from messages import Message, State from modbus import Modbus - from my_timer import Timer from config import Config from gen3.infos_g3 import InfosG3 from infos import Register @@ -42,19 +40,18 @@ class Control: class Talent(Message): - MB_START_TIMEOUT = 40 - MB_REGULAR_TIMEOUT = 60 TXT_UNKNOWN_CTRL = 'Unknown Ctrl' def __init__(self, addr, ifc: "AsyncIfc", server_side: bool, client_mode: bool = False, id_str=b''): - super().__init__(server_side, self.send_modbus_cb, mb_timeout=15) + super().__init__('G3', ifc, server_side, self.send_modbus_cb, + mb_timeout=15) ifc.rx_set_cb(self.read) ifc.prot_set_timeout_cb(self._timeout) ifc.prot_set_init_new_client_conn_cb(self._init_new_client_conn) ifc.prot_set_update_header_cb(self._update_header) + self.addr = addr - self.ifc = ifc self.conn_no = ifc.get_conn_no() self.await_conn_resp_cnt = 0 self.id_str = id_str @@ -86,38 +83,17 @@ class Talent(Message): 0x87: self.get_modbus_log_lvl, 0x04: logging.INFO, } - self.modbus_elms = 0 # for unit tests - self.node_id = 'G3' # will be overwritten in __set_serial_no - self.mb_timer = Timer(self.mb_timout_cb, self.node_id) - self.mb_timeout = self.MB_REGULAR_TIMEOUT - self.mb_first_timeout = self.MB_START_TIMEOUT - self.modbus_polling = False ''' Our puplic methods ''' def close(self) -> None: logging.debug('Talent.close()') - if self.server_side: - # set inverter state to offline, if output power is very low - logging.debug('close power: ' - f'{self.db.get_db_value(Register.OUTPUT_POWER, -1)}') - if self.db.get_db_value(Register.OUTPUT_POWER, 999) < 2: - self.db.set_db_def_value(Register.INVERTER_STATUS, 0) - self.new_data['env'] = True - # we have references to methods of this class in self.switch # so we have to erase self.switch, otherwise this instance can't be # deallocated by the garbage collector ==> we get a memory leak self.switch.clear() self.log_lvl.clear() - self.state = State.closed - self.mb_timer.close() - self.ifc.rx_set_cb(None) - self.ifc.prot_set_timeout_cb(None) - self.ifc.prot_set_init_new_client_conn_cb(None) - self.ifc.prot_set_update_header_cb(None) - self.ifc = None super().close() def __set_serial_no(self, serial_no: str): @@ -205,16 +181,6 @@ class Talent(Message): self.ifc.tx_log(log_lvl, f'Send Modbus {state}:{self.addr}:') self.ifc.tx_flush() - def _send_modbus_cmd(self, func, addr, val, log_lvl) -> None: - if self.state != State.up: - logger.log(log_lvl, f'[{self.node_id}] ignore MODBUS cmd,' - ' as the state is not UP') - return - self.mb.build_msg(Modbus.INV_ADDR, func, addr, val, log_lvl) - - async def send_modbus_cmd(self, func, addr, val, log_lvl) -> None: - self._send_modbus_cmd(func, addr, val, log_lvl) - def mb_timout_cb(self, exp_cnt): self.mb_timer.start(self.mb_timeout) diff --git a/app/src/gen3plus/solarman_v5.py b/app/src/gen3plus/solarman_v5.py index acc86b7..e481582 100644 --- a/app/src/gen3plus/solarman_v5.py +++ b/app/src/gen3plus/solarman_v5.py @@ -8,7 +8,6 @@ if __name__ == "app.src.gen3plus.solarman_v5": from app.src.async_ifc import AsyncIfc from app.src.messages import hex_dump_memory, Message, State from app.src.modbus import Modbus - from app.src.my_timer import Timer from app.src.config import Config from app.src.gen3plus.infos_g3p import InfosG3P from app.src.infos import Register @@ -17,7 +16,6 @@ else: # pragma: no cover from messages import hex_dump_memory, Message, State from config import Config from modbus import Modbus - from my_timer import Timer from gen3plus.infos_g3p import InfosG3P from infos import Register @@ -53,9 +51,6 @@ class Sequence(): class SolarmanV5(Message): AT_CMD = 1 MB_RTU_CMD = 2 - MB_START_TIMEOUT = 40 - '''start delay for Modbus polling in server mode''' - MB_REGULAR_TIMEOUT = 20 '''regular Modbus polling time in server mode''' MB_CLIENT_DATA_UP = 10 '''Data up time in client mode''' @@ -64,14 +59,14 @@ class SolarmanV5(Message): def __init__(self, addr, ifc: "AsyncIfc", server_side: bool, client_mode: bool): - super().__init__(server_side, self.send_modbus_cb, mb_timeout=8) + super().__init__('G3P', ifc, server_side, self.send_modbus_cb, + mb_timeout=8) ifc.rx_set_cb(self.read) ifc.prot_set_timeout_cb(self._timeout) ifc.prot_set_init_new_client_conn_cb(self._init_new_client_conn) ifc.prot_set_update_header_cb(self._update_header) self.addr = addr - self.ifc = ifc self.conn_no = ifc.get_conn_no() self.header_len = 11 # overwrite construcor in class Message self.control = 0 @@ -136,18 +131,11 @@ class SolarmanV5(Message): 0x4510: logging.INFO, # from server 0x1510: self.get_cmd_rsp_log_lvl, } - self.modbus_elms = 0 # for unit tests g3p_cnf = Config.get('gen3plus') if 'at_acl' in g3p_cnf: # pragma: no cover self.at_acl = g3p_cnf['at_acl'] - self.node_id = 'G3P' # will be overwritten in __set_serial_no - self.mb_timer = Timer(self.mb_timout_cb, self.node_id) - self.mb_timeout = self.MB_REGULAR_TIMEOUT - self.mb_first_timeout = self.MB_START_TIMEOUT - '''timer value for next Modbus polling request''' - self.modbus_polling = False self.sensor_list = 0x0000 self.mb_start_reg = 0x2b01 self.mb_inv_no = 3 @@ -157,26 +145,11 @@ class SolarmanV5(Message): ''' def close(self) -> None: logging.debug('Solarman.close()') - if self.server_side: - # set inverter state to offline, if output power is very low - logging.debug('close power: ' - f'{self.db.get_db_value(Register.OUTPUT_POWER, -1)}') - if self.db.get_db_value(Register.OUTPUT_POWER, 999) < 2: - self.db.set_db_def_value(Register.INVERTER_STATUS, 0) - self.new_data['env'] = True - # we have references to methods of this class in self.switch # so we have to erase self.switch, otherwise this instance can't be # deallocated by the garbage collector ==> we get a memory leak self.switch.clear() self.log_lvl.clear() - self.state = State.closed - self.mb_timer.close() - self.ifc.rx_set_cb(None) - self.ifc.prot_set_timeout_cb(None) - self.ifc.prot_set_init_new_client_conn_cb(None) - self.ifc.prot_set_update_header_cb(None) - self.ifc = None super().close() async def send_start_cmd(self, snr: int, host: str, @@ -452,16 +425,6 @@ class SolarmanV5(Message): self.ifc.tx_log(log_lvl, f'Send Modbus {state}:{self.addr}:') self.ifc.tx_flush() - def _send_modbus_cmd(self, mb_no, func, addr, val, log_lvl) -> None: - if self.state != State.up: - logger.log(log_lvl, f'[{self.node_id}] ignore MODBUS cmd,' - ' as the state is not UP') - return - self.mb.build_msg(mb_no, func, addr, val, log_lvl) - - async def send_modbus_cmd(self, func, addr, val, log_lvl) -> None: - self._send_modbus_cmd(Modbus.INV_ADDR, func, addr, val, log_lvl) - def mb_timout_cb(self, exp_cnt): self.mb_timer.start(self.mb_timeout) if self.sensor_list != 0: # 0x02b0 diff --git a/app/src/messages.py b/app/src/messages.py index bbff315..ae68bf6 100644 --- a/app/src/messages.py +++ b/app/src/messages.py @@ -5,13 +5,17 @@ from enum import Enum if __name__ == "app.src.messages": + from app.src.async_ifc import AsyncIfc from app.src.protocol_ifc import ProtocolIfc from app.src.infos import Infos, Register from app.src.modbus import Modbus + from app.src.my_timer import Timer else: # pragma: no cover + from async_ifc import AsyncIfc from protocol_ifc import ProtocolIfc from infos import Infos, Register from modbus import Modbus + from my_timer import Timer logger = logging.getLogger('msg') @@ -89,9 +93,14 @@ class Message(ProtocolIfc): '''maximum time without a received msg from the inverter in sec''' MAX_DEF_IDLE_TIME = 360 '''maximum default time without a received msg in sec''' + MB_START_TIMEOUT = 40 + '''start delay for Modbus polling in server mode''' + MB_REGULAR_TIMEOUT = 20 + '''regular Modbus polling time in server mode''' - def __init__(self, server_side: bool, send_modbus_cb: - Callable[[bytes, int, str], None], mb_timeout: int): + def __init__(self, node_id, ifc: "AsyncIfc", server_side: bool, + send_modbus_cb: Callable[[bytes, int, str], None], + mb_timeout: int): self._registry.append(weakref.ref(self)) self.server_side = server_side @@ -99,16 +108,22 @@ class Message(ProtocolIfc): self.mb = Modbus(send_modbus_cb, mb_timeout) else: self.mb = None - + self.ifc = ifc + self.node_id = node_id self.header_valid = False self.header_len = 0 self.data_len = 0 self.unique_id = 0 - self._node_id = '' self.sug_area = '' self.new_data = {} self.state = State.init self.shutdown_started = False + self.modbus_elms = 0 # for unit tests + self.mb_timer = Timer(self.mb_timout_cb, self.node_id) + self.mb_timeout = self.MB_REGULAR_TIMEOUT + self.mb_first_timeout = self.MB_START_TIMEOUT + '''timer value for next Modbus polling request''' + self.modbus_polling = False @property def node_id(self): @@ -152,10 +167,35 @@ class Message(ProtocolIfc): to = self.MAX_DEF_IDLE_TIME return to + def _send_modbus_cmd(self, mb_no, func, addr, val, log_lvl) -> None: + if self.state != State.up: + logger.log(log_lvl, f'[{self.node_id}] ignore MODBUS cmd,' + ' as the state is not UP') + return + self.mb.build_msg(mb_no, func, addr, val, log_lvl) + + async def send_modbus_cmd(self, func, addr, val, log_lvl) -> None: + self._send_modbus_cmd(Modbus.INV_ADDR, func, addr, val, log_lvl) + ''' Our puplic methods ''' def close(self) -> None: + if self.server_side: + # set inverter state to offline, if output power is very low + logging.debug('close power: ' + f'{self.db.get_db_value(Register.OUTPUT_POWER, -1)}') + if self.db.get_db_value(Register.OUTPUT_POWER, 999) < 2: + self.db.set_db_def_value(Register.INVERTER_STATUS, 0) + self.new_data['env'] = True + self.state = State.closed + self.mb_timer.close() + self.ifc.rx_set_cb(None) + self.ifc.prot_set_timeout_cb(None) + self.ifc.prot_set_init_new_client_conn_cb(None) + self.ifc.prot_set_update_header_cb(None) + self.ifc = None + if self.mb: self.mb.close() self.mb = None diff --git a/app/tests/test_async_stream.py b/app/tests/test_async_stream.py index d7dcf12..d1d5911 100644 --- a/app/tests/test_async_stream.py +++ b/app/tests/test_async_stream.py @@ -17,10 +17,13 @@ pytest_plugins = ('pytest_asyncio',) Infos.static_init() class FakeProto(Message): - def __init__(self, server_side): - super().__init__(server_side, None, 10) + def __init__(self, ifc, server_side): + super().__init__('G3F', ifc, server_side, None, 10) self.conn_no = 0 + def mb_timout_cb(self, exp_cnt): + pass # empty callback + def fake_reader_fwd(): reader = FakeReader() reader.test = FakeReader.RD_TEST_13_BYTES @@ -349,7 +352,7 @@ def create_remote(remote, test_type, with_close_hdr:bool = False): FakeReader(), FakeWriter(), StreamPtr(None), close_hndl) remote.ifc.prot_set_update_header_cb(update_hdr) remote.ifc.prot_set_init_new_client_conn_cb(callback) - remote.stream = FakeProto(False) + remote.stream = FakeProto(remote.ifc, False) @pytest.mark.asyncio async def test_forward():