From 0b437cf3bce10fa89cc56bf94cae153bdd541b73 Mon Sep 17 00:00:00 2001 From: Stefan Allius Date: Sun, 22 Sep 2024 14:59:18 +0200 Subject: [PATCH 01/11] - refactoring - remove _forward_buffer - make async_write private --- app/src/async_stream.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/app/src/async_stream.py b/app/src/async_stream.py index c30cd7c..d8a584a 100644 --- a/app/src/async_stream.py +++ b/app/src/async_stream.py @@ -8,10 +8,10 @@ from itertools import count if __name__ == "app.src.async_stream": from app.src.async_ifc import AsyncIfc - from app.src.messages import hex_dump_memory, State + from app.src.messages import State else: # pragma: no cover from async_ifc import AsyncIfc - from messages import hex_dump_memory, State + from messages import State import gc @@ -122,7 +122,7 @@ class AsyncStream(): dead_conn_to) if self.unique_id: - await self.async_write() + await self.__async_write() await self.__async_forward() await self.async_publ_mqtt() @@ -156,7 +156,7 @@ class AsyncStream(): f"{traceback.format_exc()}") await asyncio.sleep(0) # be cooperative to other task - async def async_write(self, headline: str = 'Transmit to ') -> None: + async def __async_write(self, headline: str = 'Transmit to ') -> None: """Async write handler to transmit the send_buffer""" if len(self.ifc.write) > 0: self.ifc.write.logging(logging.INFO, f'{headline}{self.addr}:') @@ -212,24 +212,21 @@ class AsyncStream(): async def __async_forward(self) -> None: """forward handler transmits data over the remote connection""" - if not self._forward_buffer: + if len(self.ifc.forward) == 0: return try: if not self.remote_stream: await self.async_create_remote() if self.remote_stream: if self.remote_stream._init_new_client_conn(): - await self.remote_stream.async_write() + await self.remote_stream.__async_write() if self.remote_stream: - self.remote_stream._update_header(self._forward_buffer) - hex_dump_memory(logging.INFO, - f'Forward to {self.remote_stream.addr}:', - self._forward_buffer, - len(self._forward_buffer)) - self.remote_stream._writer.write(self._forward_buffer) + self.remote_stream._update_header(self.ifc.forward.peek()) + self.ifc.forward.logging(logging.INFO, 'Forward to ' + f'{self.remote_stream.addr}:') + self.remote_stream._writer.write(self.ifc.forward.get()) await self.remote_stream._writer.drain() - self._forward_buffer = bytearray(0) except OSError as error: if self.remote_stream: From eea725b8dabed461582bfd5503afc162989b93ae Mon Sep 17 00:00:00 2001 From: Stefan Allius Date: Sun, 22 Sep 2024 15:00:53 +0200 Subject: [PATCH 02/11] remove _forward_buffer --- app/src/gen3/talent.py | 15 ++- app/src/gen3plus/solarman_v5.py | 8 +- app/src/messages.py | 1 - app/tests/test_solarman.py | 136 ++++++++++++------------- app/tests/test_talent.py | 175 +++++++++++++++----------------- 5 files changed, 154 insertions(+), 181 deletions(-) diff --git a/app/src/gen3/talent.py b/app/src/gen3/talent.py index 9c900fd..87c8a21 100644 --- a/app/src/gen3/talent.py +++ b/app/src/gen3/talent.py @@ -6,7 +6,7 @@ from tzlocal import get_localzone if __name__ == "app.src.gen3.talent": from app.src.async_ifc import AsyncIfc - from app.src.messages import hex_dump_memory, Message, State + 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 @@ -14,7 +14,7 @@ if __name__ == "app.src.gen3.talent": from app.src.infos import Register else: # pragma: no cover from async_ifc import AsyncIfc - from messages import hex_dump_memory, Message, State + from messages import Message, State from modbus import Modbus from my_timer import Timer from config import Config @@ -171,9 +171,8 @@ class Talent(Message): if tsun['enabled']: buflen = self.header_len+self.data_len buffer = self.ifc.read.peek(buflen) - self._forward_buffer += buffer - hex_dump_memory(logging.DEBUG, 'Store for forwarding:', - buffer, buflen) + self.ifc.forward += buffer + self.ifc.forward.logging(logging.DEBUG, 'Store for forwarding:') fnc = self.switch.get(self.msg_id, self.msg_unknown) logger.info(self.__flow_str(self.server_side, 'forwrd') + @@ -187,10 +186,8 @@ class Talent(Message): if tsun['enabled']: _len = len(buffer) struct.pack_into('!l', buffer, 0, _len-4) - buflen = _len - self._forward_buffer += buffer - hex_dump_memory(logging.INFO, 'Store for forwarding:', - buffer, buflen) + self.ifc.forward += buffer + self.ifc.forward.logging(logging.INFO, 'Store for forwarding:') fnc = self.switch.get(self.msg_id, self.msg_unknown) logger.info(self.__flow_str(self.server_side, 'forwrd') + diff --git a/app/src/gen3plus/solarman_v5.py b/app/src/gen3plus/solarman_v5.py index 031cd9d..2129fae 100644 --- a/app/src/gen3plus/solarman_v5.py +++ b/app/src/gen3plus/solarman_v5.py @@ -264,9 +264,8 @@ class SolarmanV5(Message): return tsun = Config.get('solarman') if tsun['enabled']: - self._forward_buffer += buffer[:buflen] - hex_dump_memory(logging.DEBUG, 'Store for forwarding:', - buffer, buflen) + self.ifc.forward += buffer[:buflen] + self.ifc.forward.logging(logging.DEBUG, 'Store for forwarding:') fnc = self.switch.get(self.control, self.msg_unknown) logger.info(self.__flow_str(self.server_side, 'forwrd') + @@ -467,8 +466,9 @@ class SolarmanV5(Message): 0x0002, 0, 0, 0, at_cmd.encode('utf-8'), b'\r') self.__finish_send_msg() + self.ifc.write.logging(logging.INFO, 'Send AT Command:') try: - await self.async_write('Send AT Command:') + self.ifc.write() except Exception: self.ifc.write.clear() diff --git a/app/src/messages.py b/app/src/messages.py index cf094af..eef1dff 100644 --- a/app/src/messages.py +++ b/app/src/messages.py @@ -107,7 +107,6 @@ class Message(metaclass=IterRegistry): self.unique_id = 0 self.node_id = '' # will be overwritten in the child class's __init__ self.sug_area = '' - self._forward_buffer = bytearray(0) self.new_data = {} self.state = State.init self.shutdown_started = False diff --git a/app/tests/test_solarman.py b/app/tests/test_solarman.py index e5296f5..68a3d23 100644 --- a/app/tests/test_solarman.py +++ b/app/tests/test_solarman.py @@ -61,6 +61,8 @@ class MemoryStream(SolarmanV5): self.msg_recvd = [] def write_cb(self): + if self.test_exception_async_write: + raise RuntimeError("Peer closed.") self.sent_pdu = self.ifc.write.get() def _timestamp(self): @@ -96,10 +98,6 @@ class MemoryStream(SolarmanV5): pass # ignore exceptions here return copied_bytes - async def async_write(self, headline=''): - if self.test_exception_async_write: - raise RuntimeError("Peer closed.") - def createClientStream(self, msg, chunks = (0,)): c = MemoryStream(msg, chunks, False) self.remote_stream = c @@ -691,7 +689,7 @@ def test_read_message(device_ind_msg): assert m.data_len == 0xd4 assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -712,7 +710,7 @@ def test_invalid_start_byte(invalid_start_byte, device_ind_msg): assert m.data_len == 0xd4 assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 m.close() @@ -732,7 +730,7 @@ def test_invalid_stop_byte(invalid_stop_byte): assert m.data_len == 0xd4 assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 m.close() @@ -757,7 +755,7 @@ def test_invalid_stop_byte2(invalid_stop_byte, device_ind_msg): assert m.unique_id == None assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 m.close() @@ -779,7 +777,7 @@ def test_invalid_stop_start_byte(invalid_stop_byte, invalid_start_byte): assert m.data_len == 0xd4 assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 m.close() @@ -803,7 +801,7 @@ def test_invalid_checksum(invalid_checksum, device_ind_msg): assert m.msg_recvd[1]['data_len']==0xd4 assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 m.close() @@ -824,7 +822,7 @@ def test_read_message_twice(config_no_tsun_inv1, device_ind_msg, device_rsp_msg) assert m.msg_recvd[1]['seq']=='01:01' assert m.msg_recvd[1]['data_len']==0xd4 assert m.ifc.write.get()==device_rsp_msg+device_rsp_msg - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -895,7 +893,7 @@ def test_read_two_messages(config_tsun_allow_all, device_ind_msg, device_rsp_msg assert m.msg_recvd[1]['data_len']==0x199 assert '02b0' == m.db.get_db_value(Register.SENSOR_LIST, None) assert 0x02b0 == m.sensor_list - assert m._forward_buffer==device_ind_msg+inverter_ind_msg + assert m.ifc.forward.get()==device_ind_msg+inverter_ind_msg assert m.ifc.write.get()==device_rsp_msg+inverter_rsp_msg m._init_new_client_conn() @@ -920,7 +918,7 @@ def test_read_two_messages2(config_tsun_allow_all, inverter_ind_msg, inverter_in assert m.msg_recvd[1]['seq']=='03:03' assert m.msg_recvd[1]['data_len']==0x199 assert m.time_ofs == 0x33e447a0 - assert m._forward_buffer==inverter_ind_msg+inverter_ind_msg_81 + assert m.ifc.forward.get()==inverter_ind_msg+inverter_ind_msg_81 assert m.ifc.write.get()==inverter_rsp_msg+inverter_rsp_msg_81 m._init_new_client_conn() @@ -949,7 +947,7 @@ def test_read_two_messages3(config_tsun_allow_all, device_ind_msg2, device_rsp_m assert m.msg_recvd[1]['data_len']==0xd4 assert '02b0' == m.db.get_db_value(Register.SENSOR_LIST, None) assert 0x02b0 == m.sensor_list - assert m._forward_buffer==inverter_ind_msg+device_ind_msg2 + assert m.ifc.forward.get()==inverter_ind_msg+device_ind_msg2 assert m.ifc.write.get()==inverter_rsp_msg+device_rsp_msg2 m._init_new_client_conn() @@ -970,7 +968,7 @@ def test_unkown_frame_code(config_tsun_inv1, inverter_ind_msg_81, inverter_rsp_m assert m.data_len == 0x199 assert m.ifc.read.get()==b'' assert m.ifc.write.get()==inverter_rsp_msg_81 - assert m._forward_buffer==inverter_ind_msg_81 + assert m.ifc.forward.get()==inverter_ind_msg_81 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -988,7 +986,7 @@ def test_unkown_message(config_tsun_inv1, unknown_msg): assert m.data_len == 0x0a assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==unknown_msg + assert m.ifc.forward.get()==unknown_msg assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1006,7 +1004,7 @@ def test_device_rsp(config_tsun_inv1, device_rsp_msg): assert m.data_len == 0x0a assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1024,7 +1022,7 @@ def test_inverter_rsp(config_tsun_inv1, inverter_rsp_msg): assert m.data_len == 0x0a assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1041,7 +1039,7 @@ def test_heartbeat_ind(config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp_msg): assert m.data_len == 0x01 assert m.ifc.read.get()==b'' assert m.ifc.write.get()==heartbeat_rsp_msg - assert m._forward_buffer==heartbeat_ind_msg + assert m.ifc.forward.get()==heartbeat_ind_msg assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1059,7 +1057,7 @@ def test_heartbeat_ind2(config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp_msg): assert m.data_len == 0x01 assert m.ifc.read.get()==b'' assert m.ifc.write.get()==heartbeat_rsp_msg - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1077,7 +1075,7 @@ def test_heartbeat_rsp(config_tsun_inv1, heartbeat_rsp_msg): assert m.data_len == 0x0a assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1094,13 +1092,13 @@ def test_sync_start_ind(config_tsun_inv1, sync_start_ind_msg, sync_start_rsp_msg assert m.data_len == 47 assert m.ifc.read.get()==b'' assert m.ifc.write.get()==sync_start_rsp_msg - assert m._forward_buffer==sync_start_ind_msg + assert m.ifc.forward.peek()==sync_start_ind_msg assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.seq.server_side = False # simulate forawding to TSUN cloud - m._update_header(m._forward_buffer) + m._update_header(m.ifc.forward.peek()) assert str(m.seq) == '0d:0e' # value after forwarding indication - assert m._forward_buffer==sync_start_fwd_msg + assert m.ifc.forward.get()==sync_start_fwd_msg m.close() @@ -1118,7 +1116,7 @@ def test_sync_start_rsp(config_tsun_inv1, sync_start_rsp_msg): assert m.data_len == 0x0a assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1135,7 +1133,7 @@ def test_sync_end_ind(config_tsun_inv1, sync_end_ind_msg, sync_end_rsp_msg): assert m.data_len == 60 assert m.ifc.read.get()==b'' assert m.ifc.write.get()==sync_end_rsp_msg - assert m._forward_buffer==sync_end_ind_msg + assert m.ifc.forward.get()==sync_end_ind_msg assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1153,7 +1151,7 @@ def test_sync_end_rsp(config_tsun_inv1, sync_end_rsp_msg): assert m.data_len == 0x0a assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1282,13 +1280,11 @@ async def test_msg_build_modbus_req(config_tsun_inv1, device_ind_msg, device_rsp assert m.control == 0x4110 assert str(m.seq) == '01:01' assert m.ifc.write.get()==device_rsp_msg - assert m._forward_buffer==device_ind_msg + assert m.ifc.forward.get()==device_ind_msg - m.ifc.write.clear() # clear send buffer for next test - m._forward_buffer = bytearray(0) # clear send buffer for next test await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG) assert 0 == m.send_msg_ofs - assert m._forward_buffer == b'' + assert m.ifc.forward.get() == b'' assert m.sent_pdu == b'' # modbus command must be ignore, cause connection is still not up assert m.ifc.write.get() == b'' # modbus command must be ignore, cause connection is still not up @@ -1302,20 +1298,13 @@ async def test_msg_build_modbus_req(config_tsun_inv1, device_ind_msg, device_rsp assert m.msg_recvd[1]['seq']=='02:02' assert m.ifc.read.get()==b'' assert m.ifc.write.get()==inverter_rsp_msg - assert m._forward_buffer==inverter_ind_msg + assert m.ifc.forward.get()==inverter_ind_msg - m._forward_buffer = bytearray(0) # clear send buffer for next test await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG) assert 0 == m.send_msg_ofs - assert m._forward_buffer == b'' + assert m.ifc.forward.get() == b'' assert m.sent_pdu == msg_modbus_cmd assert m.ifc.write.get()== b'' - - m.test_exception_async_write = True - await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG) - assert 0 == m.send_msg_ofs - assert m._forward_buffer == b'' - assert m.ifc.write.get() == b'' m.close() @pytest.mark.asyncio @@ -1326,12 +1315,12 @@ async def test_at_cmd(config_tsun_allow_all, device_ind_msg, device_rsp_msg, inv assert m.control == 0x4110 assert str(m.seq) == '01:01' assert m.ifc.write.get()==device_rsp_msg - assert m._forward_buffer==device_ind_msg + assert m.ifc.forward.get()==device_ind_msg - m._forward_buffer = bytearray(0) # clear send buffer for next test await m.send_at_cmd('AT+TIME=214028,1,60,120') assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' + assert m.sent_pdu == b'' assert str(m.seq) == '01:01' assert m.mqtt.key == '' assert m.mqtt.data == "" @@ -1341,12 +1330,14 @@ async def test_at_cmd(config_tsun_allow_all, device_ind_msg, device_rsp_msg, inv assert m.control == 0x4210 assert str(m.seq) == '02:02' assert m.ifc.write.get()==inverter_rsp_msg - assert m._forward_buffer==inverter_ind_msg + assert m.ifc.forward.get()==inverter_ind_msg - m._forward_buffer = bytearray(0) # clear send buffer for next test await m.send_at_cmd('AT+TIME=214028,1,60,120') - assert m.ifc.write.get()==at_command_ind_msg - assert m._forward_buffer==b'' + assert m.ifc.forward.get() == b'' + assert m.ifc.write.get()== b'' + assert m.sent_pdu == at_command_ind_msg + m.sent_pdu = bytearray() + assert str(m.seq) == '02:03' assert m.mqtt.key == '' assert m.mqtt.data == "" @@ -1357,15 +1348,18 @@ async def test_at_cmd(config_tsun_allow_all, device_ind_msg, device_rsp_msg, inv assert str(m.seq) == '03:03' assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.key == 'at_resp' assert m.data == "+ok" + m.sent_pdu = bytearray() m.test_exception_async_write = True await m.send_at_cmd('AT+TIME=214028,1,60,120') + assert m.sent_pdu == b'' assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' + assert m.sent_pdu == b'' assert str(m.seq) == '03:04' assert m.forward_at_cmd_resp == False assert m.mqtt.key == '' @@ -1380,12 +1374,11 @@ async def test_at_cmd_blocked(config_tsun_allow_all, device_ind_msg, device_rsp_ assert m.control == 0x4110 assert str(m.seq) == '01:01' assert m.ifc.write.get()==device_rsp_msg - assert m._forward_buffer==device_ind_msg + assert m.ifc.forward.get()==device_ind_msg - m._forward_buffer = bytearray(0) # clear send buffer for next test await m.send_at_cmd('AT+WEBU') assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert str(m.seq) == '01:01' assert m.mqtt.key == '' assert m.mqtt.data == "" @@ -1396,13 +1389,12 @@ async def test_at_cmd_blocked(config_tsun_allow_all, device_ind_msg, device_rsp_ assert str(m.seq) == '02:02' assert m.ifc.read.get()==b'' assert m.ifc.write.get()==inverter_rsp_msg - assert m._forward_buffer==inverter_ind_msg + assert m.ifc.forward.get()==inverter_ind_msg - m._forward_buffer = bytearray(0) # clear send buffer for next test await m.send_at_cmd('AT+WEBU') assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert str(m.seq) == '02:02' assert m.forward_at_cmd_resp == False assert m.mqtt.key == 'at_resp' @@ -1426,7 +1418,7 @@ def test_at_cmd_ind(config_tsun_inv1, at_command_ind_msg): assert m.data_len == 39 assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==at_command_ind_msg + assert m.ifc.forward.get()==at_command_ind_msg assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['AT_Command'] == 1 assert m.db.stat['proxy']['AT_Command_Blocked'] == 0 @@ -1450,7 +1442,7 @@ def test_at_cmd_ind_block(config_tsun_inv1, at_command_ind_msg_block): assert m.data_len == 23 assert m.ifc.read.get()==b'' assert m.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['AT_Command'] == 0 assert m.db.stat['proxy']['AT_Command_Blocked'] == 1 @@ -1470,7 +1462,7 @@ def test_msg_at_command_rsp1(config_tsun_inv1, at_command_rsp_msg): assert str(m.seq) == '03:03' assert m.header_len==11 assert m.data_len==17 - assert m._forward_buffer==at_command_rsp_msg + assert m.ifc.forward.get()==at_command_rsp_msg assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 @@ -1489,7 +1481,7 @@ def test_msg_at_command_rsp2(config_tsun_inv1, at_command_rsp_msg): assert str(m.seq) == '03:03' assert m.header_len==11 assert m.data_len==17 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 @@ -1514,7 +1506,7 @@ def test_msg_modbus_req(config_tsun_inv1, msg_modbus_cmd, msg_modbus_cmd_fwd): assert str(c.seq) == '03:02' assert c.header_len==11 assert c.data_len==23 - assert c._forward_buffer==b'' + assert c.ifc.forward.get()==b'' assert c.ifc.write.get()==b'' assert m.sent_pdu == msg_modbus_cmd_fwd assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 @@ -1541,7 +1533,7 @@ def test_msg_modbus_req2(config_tsun_inv1, msg_modbus_cmd_crc_err): assert str(c.seq) == '03:02' assert c.header_len==11 assert c.data_len==23 - assert c._forward_buffer==b'' + assert c.ifc.forward.get()==b'' assert c.ifc.write.get()==b'' assert m.sent_pdu==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 @@ -1564,7 +1556,7 @@ def test_msg_unknown_cmd_req(config_tsun_inv1, msg_unknown_cmd): assert str(m.seq) == '03:02' assert m.header_len==11 assert m.data_len==23 - assert m._forward_buffer==msg_unknown_cmd + assert m.ifc.forward.get()==msg_unknown_cmd assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['AT_Command'] == 0 @@ -1585,7 +1577,7 @@ def test_msg_modbus_rsp1(config_tsun_inv1, msg_modbus_rsp): assert str(m.seq) == '03:03' assert m.header_len==11 assert m.data_len==59 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 @@ -1609,20 +1601,19 @@ def test_msg_modbus_rsp2(config_tsun_inv1, msg_modbus_rsp): assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.mb.err == 0 assert m.msg_count == 1 - assert m._forward_buffer==msg_modbus_rsp + assert m.ifc.forward.get()==msg_modbus_rsp assert m.ifc.write.get()==b'' assert m.db.get_db_value(Register.VERSION) == 'V4.0.10' assert m.new_data['inverter'] == True m.new_data['inverter'] = False m.mb.req_pend = True - m._forward_buffer = bytearray() m.append_msg(msg_modbus_rsp) m.read() # read complete msg, and dispatch msg assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.mb.err == 0 assert m.msg_count == 2 - assert m._forward_buffer==msg_modbus_rsp + assert m.ifc.forward.get()==msg_modbus_rsp assert m.ifc.write.get()==b'' assert m.db.get_db_value(Register.VERSION) == 'V4.0.10' assert m.new_data['inverter'] == False @@ -1647,19 +1638,18 @@ def test_msg_modbus_rsp3(config_tsun_inv1, msg_modbus_rsp): assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.mb.err == 0 assert m.msg_count == 1 - assert m._forward_buffer==msg_modbus_rsp + assert m.ifc.forward.get()==msg_modbus_rsp assert m.ifc.write.get()==b'' assert m.db.get_db_value(Register.VERSION) == 'V4.0.10' assert m.new_data['inverter'] == True m.new_data['inverter'] = False - m._forward_buffer = bytearray() m.append_msg(msg_modbus_rsp) m.read() # read complete msg, and dispatch msg assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.mb.err == 5 assert m.msg_count == 2 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.db.get_db_value(Register.VERSION) == 'V4.0.10' assert m.new_data['inverter'] == False @@ -1678,7 +1668,7 @@ def test_msg_unknown_rsp(config_tsun_inv1, msg_unknown_cmd_rsp): assert str(m.seq) == '03:03' assert m.header_len==11 assert m.data_len==59 - assert m._forward_buffer==msg_unknown_cmd_rsp + assert m.ifc.forward.get()==msg_unknown_cmd_rsp assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 @@ -1692,7 +1682,7 @@ def test_msg_modbus_invalid(config_tsun_inv1, msg_modbus_invalid): m.read() # read complete msg, and dispatch msg assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.msg_count == 1 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 @@ -1715,7 +1705,7 @@ def test_msg_modbus_fragment(config_tsun_inv1, msg_modbus_rsp): m.read() # read complete msg, and dispatch msg assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.msg_count == 1 - assert m._forward_buffer==msg_modbus_rsp + assert m.ifc.forward.get()==msg_modbus_rsp assert m.ifc.write.get()== b'' assert m.mb.err == 0 assert m.modbus_elms == 20-1 # register 0x300d is unknown, so one value can't be mapped @@ -1741,7 +1731,7 @@ async def test_modbus_polling(config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp assert m.data_len == 0x01 assert m.ifc.read.get()==b'' assert m.ifc.write.get()==heartbeat_rsp_msg - assert m._forward_buffer==heartbeat_ind_msg + assert m.ifc.forward.get()==heartbeat_ind_msg assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.state == State.up diff --git a/app/tests/test_talent.py b/app/tests/test_talent.py index 35e4a1f..4bea5db 100644 --- a/app/tests/test_talent.py +++ b/app/tests/test_talent.py @@ -34,7 +34,6 @@ class MemoryStream(Talent): self.msg_count = 0 self.addr = 'Test: SrvSide' self.send_msg_ofs = 0 - self.test_exception_async_write = False self.msg_recvd = [] self.remote_stream = None @@ -90,10 +89,6 @@ class MemoryStream(Talent): self.msg_count += 1 - async def async_write(self, headline=''): - if self.test_exception_async_write: - raise RuntimeError("Peer closed.") - @pytest.fixture @@ -746,7 +741,7 @@ def test_read_message(msg_contact_info): assert m.msg_id==0 assert m.header_len==23 assert m.data_len==25 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' m.close() def test_read_message_twice(config_no_tsun_inv1, msg_inverter_ind): @@ -766,7 +761,7 @@ def test_read_message_twice(config_no_tsun_inv1, msg_inverter_ind): assert m.msg_recvd[1]['data_len']==120 assert m.id_str == b"R170000000000001" assert m.unique_id == 'R170000000000001' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' m.close() def test_read_message_long_id(msg_contact_info_long_id): @@ -851,7 +846,7 @@ def test_read_two_messages(config_tsun_allow_all, msg2_contact_info,msg_contact_ assert m.msg_recvd[1]['msg_id']==0 assert m.msg_recvd[1]['header_len']==23 assert m.msg_recvd[1]['data_len']==25 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==msg_contact_rsp + msg_contact_rsp2 assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 @@ -876,7 +871,7 @@ def test_conttact_req(config_tsun_allow_all, msg_contact_info, msg_contact_rsp): assert m.msg_id==0 assert m.header_len==23 assert m.data_len==25 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==msg_contact_rsp m.close() @@ -894,7 +889,7 @@ def test_contact_broken_req(config_tsun_allow_all, msg_contact_info_broken, msg_ assert m.msg_id==0 assert m.header_len==23 assert m.data_len==23 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==msg_contact_rsp m.close() @@ -912,7 +907,7 @@ def test_conttact_req(config_tsun_allow_all, msg_contact_info, msg_contact_rsp): assert m.msg_id==0 assert m.header_len==23 assert m.data_len==25 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==msg_contact_rsp m.close() @@ -930,7 +925,7 @@ def test_contact_broken_req(config_tsun_allow_all, msg_contact_info_broken, msg_ assert m.msg_id==0 assert m.header_len==23 assert m.data_len==23 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==msg_contact_rsp m.close() @@ -949,7 +944,7 @@ def test_msg_contact_resp(config_tsun_inv1, msg_contact_rsp): assert m.msg_id==0 assert m.header_len==23 assert m.data_len==1 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -969,7 +964,7 @@ def test_msg_contact_resp_2(config_tsun_inv1, msg_contact_rsp): assert m.msg_id==0 assert m.header_len==23 assert m.data_len==1 - assert m._forward_buffer==msg_contact_rsp + assert m.ifc.forward.get()==msg_contact_rsp assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -989,7 +984,7 @@ def test_msg_contact_resp_3(config_tsun_inv1, msg_contact_rsp): assert m.msg_id==0 assert m.header_len==23 assert m.data_len==1 - assert m._forward_buffer==msg_contact_rsp + assert m.ifc.forward.get()==msg_contact_rsp assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1007,7 +1002,7 @@ def test_msg_contact_invalid(config_tsun_inv1, msg_contact_invalid): assert m.msg_id==0 assert m.header_len==23 assert m.data_len==1 - assert m._forward_buffer==msg_contact_invalid + assert m.ifc.forward.get()==msg_contact_invalid assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 m.close() @@ -1028,7 +1023,7 @@ def test_msg_get_time(config_tsun_inv1, msg_get_time): assert m.ts_offset==0 assert m.data_len==0 assert m.state==State.pend - assert m._forward_buffer==msg_get_time + assert m.ifc.forward.get()==msg_get_time assert m.ifc.write.get()==b'\x00\x00\x00\x1b\x10R170000000000001\x91"\x00\x00\x01\x89\xc6,_\x00' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1049,7 +1044,7 @@ def test_msg_get_time_autark(config_no_tsun_inv1, msg_get_time): assert m.ts_offset==0 assert m.data_len==0 assert m.state==State.received - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==bytearray(b'\x00\x00\x00\x1b\x10R170000000000001\x91"\x00\x00\x01\x89\xc6,_\x00') assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1073,7 +1068,7 @@ def test_msg_time_resp(config_tsun_inv1, msg_time_rsp): assert m.ts_offset==3600000 assert s.ts_offset==3600000 assert m.data_len==8 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.remote_stream = None @@ -1094,7 +1089,7 @@ def test_msg_time_resp_autark(config_no_tsun_inv1, msg_time_rsp): assert m.header_len==23 assert m.ts_offset==3600000 assert m.data_len==8 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1113,7 +1108,7 @@ def test_msg_time_inv_resp(config_tsun_inv1, msg_time_rsp_inv): assert m.header_len==23 assert m.ts_offset==0 assert m.data_len==4 - assert m._forward_buffer==msg_time_rsp_inv + assert m.ifc.forward.get()==msg_time_rsp_inv assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1132,7 +1127,7 @@ def test_msg_time_invalid(config_tsun_inv1, msg_time_invalid): assert m.header_len==23 assert m.ts_offset==0 assert m.data_len==0 - assert m._forward_buffer==msg_time_invalid + assert m.ifc.forward.get()==msg_time_invalid assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 m.close() @@ -1151,7 +1146,7 @@ def test_msg_time_invalid_autark(config_no_tsun_inv1, msg_time_invalid): assert m.ts_offset==0 assert m.header_len==23 assert m.data_len==0 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 m.close() @@ -1175,7 +1170,7 @@ def test_msg_act_time(config_no_modbus_poll, msg_act_time, msg_act_time_ack): assert m.header_len==23 assert m.data_len==9 assert m.state == State.up - assert m._forward_buffer==msg_act_time + assert m.ifc.forward.get()==msg_act_time assert m.ifc.write.get()==msg_act_time_ack assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert 125 == m.db.get_db_value(Register.POLLING_INTERVAL, 0) @@ -1199,7 +1194,7 @@ def test_msg_act_time2(config_tsun_inv1, msg_act_time, msg_act_time_ack): assert m.ts_offset==0 assert m.header_len==23 assert m.data_len==9 - assert m._forward_buffer==msg_act_time + assert m.ifc.forward.get()==msg_act_time assert m.ifc.write.get()==msg_act_time_ack assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert 123 == m.db.get_db_value(Register.POLLING_INTERVAL, 0) @@ -1220,7 +1215,7 @@ def test_msg_act_time_ofs(config_tsun_inv1, msg_act_time, msg_act_time_ofs, msg_ assert m.ts_offset==3600 assert m.header_len==23 assert m.data_len==9 - assert m._forward_buffer==msg_act_time_ofs + assert m.ifc.forward.get()==msg_act_time_ofs assert m.ifc.write.get()==msg_act_time_ack assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1240,7 +1235,7 @@ def test_msg_act_time_ofs2(config_tsun_inv1, msg_act_time, msg_act_time_ofs, msg assert m.ts_offset==-3600 assert m.header_len==23 assert m.data_len==9 - assert m._forward_buffer==msg_act_time + assert m.ifc.forward.get()==msg_act_time assert m.ifc.write.get()==msg_act_time_ack assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1260,7 +1255,7 @@ def test_msg_act_time_autark(config_no_tsun_inv1, msg_act_time, msg_act_time_ack assert m.ts_offset==0 assert m.header_len==23 assert m.data_len==9 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==msg_act_time_ack assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1278,7 +1273,7 @@ def test_msg_act_time_ack(config_tsun_inv1, msg_act_time_ack): assert m.msg_id==153 assert m.header_len==23 assert m.data_len==1 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1296,7 +1291,7 @@ def test_msg_act_time_cmd(config_tsun_inv1, msg_act_time_cmd): assert m.msg_id==153 assert m.header_len==23 assert m.data_len==1 - assert m._forward_buffer==msg_act_time_cmd + assert m.ifc.forward.get()==msg_act_time_cmd assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 m.close() @@ -1314,7 +1309,7 @@ def test_msg_act_time_inv(config_tsun_inv1, msg_act_time_inv): assert m.msg_id==153 assert m.header_len==23 assert m.data_len==8 - assert m._forward_buffer==msg_act_time_inv + assert m.ifc.forward.get()==msg_act_time_inv assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1333,11 +1328,11 @@ def test_msg_cntrl_ind(config_tsun_inv1, msg_controller_ind, msg_controller_ind_ assert m.header_len==23 assert m.data_len==284 m.ts_offset = 0 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_controller_ind + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.peek()==msg_controller_ind m.ts_offset = -4096 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_controller_ind_ts_offs + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.get()==msg_controller_ind_ts_offs assert m.ifc.write.get()==msg_controller_ack assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1355,7 +1350,7 @@ def test_msg_cntrl_ack(config_tsun_inv1, msg_controller_ack): assert m.msg_id==113 assert m.header_len==23 assert m.data_len==1 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1374,11 +1369,11 @@ def test_msg_cntrl_invalid(config_tsun_inv1, msg_controller_invalid): assert m.header_len==23 assert m.data_len==1 m.ts_offset = 0 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_controller_invalid + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.peek()==msg_controller_invalid m.ts_offset = -4096 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_controller_invalid + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.get()==msg_controller_invalid assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 m.close() @@ -1398,11 +1393,11 @@ def test_msg_inv_ind(config_tsun_inv1, msg_inverter_ind, msg_inverter_ind_ts_off assert m.header_len==23 assert m.data_len==120 m.ts_offset = 0 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_inverter_ind + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.peek()==msg_inverter_ind m.ts_offset = +256 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_inverter_ind_ts_offs + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.get()==msg_inverter_ind_ts_offs assert m.ifc.write.get()==msg_inverter_ack assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1425,8 +1420,8 @@ def test_msg_inv_ind1(config_tsun_inv1, msg_inverter_ind2, msg_inverter_ind_ts_o assert m.header_len==23 assert m.data_len==1263 m.ts_offset = 0 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_inverter_ind2 + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.get()==msg_inverter_ind2 assert m.ifc.write.get()==msg_inverter_ack assert m.db.get_db_value(Register.TS_GRID) == 1691243349 m.close() @@ -1449,8 +1444,8 @@ def test_msg_inv_ind2(config_tsun_inv1, msg_inverter_ind_new, msg_inverter_ind_t assert m.header_len==23 assert m.data_len==1165 m.ts_offset = 0 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_inverter_ind_new + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.get()==msg_inverter_ind_new assert m.ifc.write.get()==msg_inverter_ack assert m.db.get_db_value(Register.INVERTER_STATUS) == None assert m.db.get_db_value(Register.TS_GRID) == None @@ -1477,8 +1472,8 @@ def test_msg_inv_ind3(config_tsun_inv1, msg_inverter_ind_0w, msg_inverter_ack): assert m.header_len==23 assert m.data_len==1263 m.ts_offset = 0 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_inverter_ind_0w + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.get()==msg_inverter_ind_0w assert m.ifc.write.get()==msg_inverter_ack assert m.db.get_db_value(Register.INVERTER_STATUS) == 1 assert isclose(m.db.db['grid']['Output_Power'], 0.5) @@ -1501,7 +1496,7 @@ def test_msg_inv_ack(config_tsun_inv1, msg_inverter_ack): assert m.msg_id==4 assert m.header_len==23 assert m.data_len==1 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1520,11 +1515,11 @@ def test_msg_inv_invalid(config_tsun_inv1, msg_inverter_invalid): assert m.header_len==23 assert m.data_len==1 m.ts_offset = 0 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_inverter_invalid + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.peek()==msg_inverter_invalid m.ts_offset = 256 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_inverter_invalid + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.get()==msg_inverter_invalid assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 m.close() @@ -1544,11 +1539,11 @@ def test_msg_ota_req(config_tsun_inv1, msg_ota_req): assert m.header_len==23 assert m.data_len==259 m.ts_offset = 0 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_ota_req + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.peek()==msg_ota_req m.ts_offset = 4096 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_ota_req + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.get()==msg_ota_req assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['OTA_Start_Msg'] == 1 @@ -1571,11 +1566,11 @@ def test_msg_ota_ack(config_tsun_inv1, msg_ota_ack): assert m.header_len==23 assert m.data_len==1 m.ts_offset = 0 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_ota_ack + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.peek()==msg_ota_ack m.ts_offset = 256 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_ota_ack + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.get()==msg_ota_ack assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['OTA_Start_Msg'] == 0 @@ -1596,11 +1591,11 @@ def test_msg_ota_invalid(config_tsun_inv1, msg_ota_invalid): assert m.header_len==23 assert m.data_len==1 m.ts_offset = 0 - m._update_header(m._forward_buffer) - assert m._forward_buffer==msg_ota_invalid + m._update_header(m.ifc.forward.peek()) + assert m.ifc.forward.peek()==msg_ota_invalid m.ts_offset = 4096 - assert m._forward_buffer==msg_ota_invalid - m._update_header(m._forward_buffer) + assert m.ifc.forward.get()==msg_ota_invalid + m._update_header(m.ifc.forward.peek()) assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 assert m.db.stat['proxy']['OTA_Start_Msg'] == 0 @@ -1619,7 +1614,7 @@ def test_msg_unknown(config_tsun_inv1, msg_unknown): assert m.msg_id==23 assert m.header_len==23 assert m.data_len==4 - assert m._forward_buffer==msg_unknown + assert m.ifc.forward.get()==msg_unknown assert m.ifc.write.get()==b'' assert 1 == m.db.stat['proxy']['Unknown_Msg'] m.close() @@ -1744,10 +1739,10 @@ def test_msg_modbus_req(config_tsun_inv1, msg_modbus_cmd): assert c.msg_id==119 assert c.header_len==23 assert c.data_len==13 - assert c._forward_buffer==b'' + assert c.ifc.forward.get()==b'' assert c.ifc.write.get()==b'' assert m.id_str == b"R170000000000001" - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.sent_pdu == msg_modbus_cmd assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 @@ -1774,10 +1769,10 @@ def test_msg_modbus_req2(config_tsun_inv1, msg_modbus_cmd): assert c.msg_id==119 assert c.header_len==23 assert c.data_len==13 - assert c._forward_buffer==b'' + assert c.ifc.forward.get()==b'' assert c.ifc.write.get()==b'' assert m.id_str == b"R170000000000001" - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.sent_pdu == b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 @@ -1803,9 +1798,9 @@ def test_msg_modbus_req3(config_tsun_inv1, msg_modbus_cmd_crc_err): assert c.msg_id==119 assert c.header_len==23 assert c.data_len==13 - assert c._forward_buffer==b'' + assert c.ifc.forward.get()==b'' assert c.ifc.write.get()==b'' - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.sent_pdu ==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 @@ -1828,7 +1823,7 @@ def test_msg_modbus_rsp1(config_tsun_inv1, msg_modbus_rsp): assert m.msg_id==119 assert m.header_len==23 assert m.data_len==13 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 @@ -1850,7 +1845,7 @@ def test_msg_modbus_cloud_rsp(config_tsun_inv1, msg_modbus_rsp): assert m.msg_id==119 assert m.header_len==23 assert m.data_len==13 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Msg'] == 1 assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 @@ -1878,7 +1873,7 @@ def test_msg_modbus_rsp2(config_tsun_inv1, msg_modbus_rsp20): assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.mb.err == 5 assert m.msg_count == 2 - assert m._forward_buffer==msg_modbus_rsp20 + assert m.ifc.forward.get()==msg_modbus_rsp20 assert m.ifc.write.get()==b'' assert m.db.db == {'collector': {'Serial_Number': 'R170000000000001'}, 'inverter': {'Version': 'V5.1.09', 'Rated_Power': 300}, 'grid': {'Timestamp': m._utc(), 'Voltage': 225.9, 'Current': 0.41, 'Frequency': 49.99, 'Output_Power': 94.8}, 'env': {'Inverter_Temp': 22}, 'input': {'Timestamp': m._utc(), 'pv1': {'Voltage': 0.8, 'Current': 0.0, 'Power': 0.0}, 'pv2': {'Voltage': 34.5, 'Current': 2.89, 'Power': 99.8}, 'pv3': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}, 'pv4': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}}} assert m.db.get_db_value(Register.VERSION) == 'V5.1.09' @@ -1908,7 +1903,7 @@ def test_msg_modbus_rsp3(config_tsun_inv1, msg_modbus_rsp21): assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.mb.err == 5 assert m.msg_count == 2 - assert m._forward_buffer==msg_modbus_rsp21 + assert m.ifc.forward.get()==msg_modbus_rsp21 assert m.ifc.write.get()==b'' assert m.db.db == {'collector': {'Serial_Number': 'R170000000000001'}, 'inverter': {'Version': 'V5.1.0E', 'Rated_Power': 300}, 'grid': {'Timestamp': m._utc(), 'Voltage': 225.9, 'Current': 0.41, 'Frequency': 49.99, 'Output_Power': 94.8}, 'env': {'Inverter_Temp': 22}, 'input': {'Timestamp': m._utc(), 'pv1': {'Voltage': 0.8, 'Current': 0.0, 'Power': 0.0}, 'pv2': {'Voltage': 34.5, 'Current': 2.89, 'Power': 99.8}, 'pv3': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}, 'pv4': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}}} assert m.db.get_db_value(Register.VERSION) == 'V5.1.0E' @@ -1937,7 +1932,7 @@ def test_msg_modbus_rsp4(config_tsun_inv1, msg_modbus_rsp21): assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.mb.err == 0 assert m.msg_count == 1 - assert m._forward_buffer==msg_modbus_rsp21 + assert m.ifc.forward.get()==msg_modbus_rsp21 assert m.modbus_elms == 19 assert m.ifc.write.get()==b'' assert m.db.db == db_values @@ -1962,7 +1957,7 @@ def test_msg_modbus_rsp_new(config_tsun_inv1, msg_modbus_rsp20_new): assert m.msg_id==135 assert m.header_len==23 assert m.data_len==107 - assert m._forward_buffer==b'' + assert m.ifc.forward.get()==b'' assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 @@ -1982,7 +1977,7 @@ def test_msg_modbus_invalid(config_tsun_inv1, msg_modbus_inv): assert m.msg_id==119 assert m.header_len==23 assert m.data_len==13 - assert m._forward_buffer==msg_modbus_inv + assert m.ifc.forward.get()==msg_modbus_inv assert m.ifc.write.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 assert m.db.stat['proxy']['Modbus_Command'] == 0 @@ -2011,7 +2006,7 @@ def test_msg_modbus_fragment(config_tsun_inv1, msg_modbus_rsp20): assert m.msg_id == 119 assert m.header_len == 23 assert m.data_len == 50 - assert m._forward_buffer==msg_modbus_rsp20 + assert m.ifc.forward.get()==msg_modbus_rsp20 assert m.ifc.write.get() == b'' assert m.mb.err == 0 assert m.modbus_elms == 20-1 # register 0x300d is unknown, so one value can't be mapped @@ -2026,24 +2021,16 @@ async def test_msg_build_modbus_req(config_tsun_inv1, msg_modbus_cmd): m.id_str = b"R170000000000001" await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG) assert 0 == m.send_msg_ofs - assert m._forward_buffer == b'' + assert m.ifc.forward.get() == b'' assert m.ifc.write.get() == b'' assert m.sent_pdu == b'' m.state = State.up await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG) assert 0 == m.send_msg_ofs - assert m._forward_buffer == b'' + assert m.ifc.forward.get() == b'' assert m.ifc.write.get() == b'' assert m.sent_pdu == msg_modbus_cmd - - m.sent_pdu = bytearray(0) # clear send buffer for next test - m.test_exception_async_write = True - await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG) - assert 0 == m.send_msg_ofs - assert m._forward_buffer == b'' - assert m.ifc.write.get() == b'' - assert m.sent_pdu == b'' m.close() def test_modbus_no_polling(config_no_modbus_poll, msg_get_time): @@ -2061,7 +2048,7 @@ def test_modbus_no_polling(config_no_modbus_poll, msg_get_time): assert m.header_len==23 assert m.ts_offset==0 assert m.data_len==0 - assert m._forward_buffer==msg_get_time + assert m.ifc.forward.get()==msg_get_time assert m.ifc.write.get()==b'\x00\x00\x00\x1b\x10R170000000000001\x91"\x00\x00\x01\x89\xc6,_\x00' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -2085,7 +2072,7 @@ async def test_modbus_polling(config_tsun_inv1, msg_inverter_ind): assert m.header_len==23 assert m.ts_offset==0 assert m.data_len==120 - assert m._forward_buffer==msg_inverter_ind + assert m.ifc.forward.get()==msg_inverter_ind assert m.ifc.write.get()==b'\x00\x00\x00\x14\x10R170000000000001\x99\x04\x01' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 From 89c2c11ed9d1d2243b073a2040df6c42b569cf9d Mon Sep 17 00:00:00 2001 From: Stefan Allius Date: Tue, 24 Sep 2024 21:10:58 +0200 Subject: [PATCH 03/11] refactoring --- tsun.code-workspace | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tsun.code-workspace diff --git a/tsun.code-workspace b/tsun.code-workspace new file mode 100644 index 0000000..0bb7570 --- /dev/null +++ b/tsun.code-workspace @@ -0,0 +1,11 @@ +{ + "folders": [ + { + "path": "." + }, + { + "path": "../wiki" + } + ], + "settings": {} +} \ No newline at end of file From d5c369e5fea2e447dc99e921b46bcc37038b9eee Mon Sep 17 00:00:00 2001 From: Stefan Allius Date: Tue, 24 Sep 2024 21:12:51 +0200 Subject: [PATCH 04/11] refactoring --- app/proxy.svg | 745 +++++++++++++++-------------- app/proxy.yuml | 53 +- app/src/async_ifc.py | 118 ++++- app/src/async_stream.py | 220 ++++++--- app/src/gen3/connection_g3.py | 30 +- app/src/gen3/inverter_g3.py | 15 +- app/src/gen3/talent.py | 106 ++-- app/src/gen3plus/connection_g3p.py | 31 +- app/src/gen3plus/inverter_g3p.py | 15 +- app/src/gen3plus/solarman_v5.py | 100 ++-- app/src/infos.py | 2 + app/src/messages.py | 27 +- app/src/modbus_tcp.py | 13 +- app/src/server.py | 4 +- app/tests/test_connection_g3.py | 14 +- app/tests/test_connection_g3p.py | 15 +- app/tests/test_inverter.py | 3 +- app/tests/test_inverter_g3.py | 6 +- app/tests/test_inverter_g3p.py | 6 +- app/tests/test_modbus_tcp.py | 19 +- app/tests/test_mqtt.py | 11 +- app/tests/test_solarman.py | 318 ++++++------ app/tests/test_talent.py | 328 +++++++------ 23 files changed, 1257 insertions(+), 942 deletions(-) diff --git a/app/proxy.svg b/app/proxy.svg index 32caab7..aa7a73b 100644 --- a/app/proxy.svg +++ b/app/proxy.svg @@ -4,408 +4,455 @@ - - + + G - + A0 - - - -You can stick notes -on diagrams too! + + + +You can stick notes +on diagrams too! A1 - -Singleton + +Mqtt +<<Singleton>> + +<static>ha_restarts +<static>__client +<static>__cb_MqttIsUp + +<async>publish() +<async>close() A2 - -Mqtt - -<static>ha_restarts -<static>__client -<static>__cb_MqttIsUp - -<async>publish() -<async>close() + +Inverter + +cls.db_stat +cls.entity_prfx +cls.discovery_prfx +cls.proxy_node_id +cls.proxy_unique_id +cls.mqtt:Mqtt + - + A1->A2 - - - - - -A11 - -Inverter - -cls.db_stat -cls.entity_prfx -cls.discovery_prfx -cls.proxy_node_id -cls.proxy_unique_id -cls.mqtt:Mqtt - - - - -A2->A11 - + + + A3 - -Modbus - -que -snd_handler -rsp_handler -timeout -max_retires -last_xxx -err -retry_cnt -req_pend -tim - -build_msg() -recv_req() -recv_resp() -close() + +InverterG3 + +__ha_restarts + +async_create_remote() +async_publ_mqtt() +close() + + + +A2->A3 + + A4 - -IterRegistry - - -__iter__ + +InverterG3P + +__ha_restarts + +async_create_remote( +)async_publ_mqtt() +close() + + + +A2->A4 + + A5 - -Message - -server_side:bool -header_valid:bool -header_len:unsigned -data_len:unsigned -unique_id -node_id -sug_area -_recv_buffer:bytearray -_send_buffer:bytearray -_forward_buffer:bytearray -db:Infos -new_data:list -state - -_read():void<abstract> -close():void -inc_counter():void -dec_counter():void - - - -A4->A5 - - + +IterRegistry + + +__iter__ A6 - -Talent - -await_conn_resp_cnt -id_str -contact_name -contact_mail -db:InfosG3 -mb:Modbus -switch - -msg_contact_info() -msg_ota_update() -msg_get_time() -msg_collector_data() -msg_inverter_data() -msg_unknown() -close() + +Message + +server_side:bool +header_valid:bool +header_len:unsigned +data_len:unsigned +unique_id +node_id +sug_area +_recv_buffer:bytearray +_send_buffer:bytearray +_forward_buffer:bytearray +db:Infos +new_data:list +state + +_read():void<abstract> +close():void +inc_counter():void +dec_counter():void - -A5->A6 - - - - - -A7 - -SolarmanV5 - -control -serial -snr -db:InfosG3P -mb:Modbus -switch - -msg_unknown() -close() - - -A5->A7 - - - - - -A6->A3 - - -1 -has - - - -A8 - -ConnectionG3 - -remote_stream:ConnectionG3 - -healthy() -close() - - - -A6->A8 - - - - - -A7->A3 - - -1 -has - - - -A9 - -ConnectionG3P - -remote_stream:ConnectionG3P - -healthy() -close() - - - -A7->A9 - - - - - -A8->A8 - - -0..1 -has - - - -A12 - -InverterG3 - -__ha_restarts - -async_create_remote() -async_publ_mqtt() -close() - - - -A8->A12 - - - - - -A9->A9 - - -0..1 -has - - - -A13 - -InverterG3P - -__ha_restarts - -async_create_remote( -)async_publ_mqtt() -close() - - - -A9->A13 - - - - - -A10 - -AsyncStream - -reader -writer -addr -r_addr -l_addr - -<async>server_loop() -<async>client_loop() -<async>loop -disc() -close() -__async_read() -async_write() -__async_forward() - - - -A10->A8 - - - - - -A10->A9 - - - - - -A11->A12 - - - - - -A11->A13 - - - - - -A14 - -Infos - -stat -new_stat_data -info_dev - -static_init() -dev_value() -inc_counter() -dec_counter() -ha_proxy_conf -ha_conf -ha_remove -update_db -set_db_def_value -get_db_value -ignore_this_device +A5->A6 + + A15 - -InfosG3 - - -ha_confs() -parse() + +Talent + +await_conn_resp_cnt +id_str +contact_name +contact_mail +db:InfosG3 +mb:Modbus +switch + +msg_contact_info() +msg_ota_update() +msg_get_time() +msg_collector_data() +msg_inverter_data() +msg_unknown() +close() - - -A14->A15 - - + + +A6->A15 + + A16 - -InfosG3P - - -ha_confs() -parse() + +SolarmanV5 + +control +serial +snr +db:InfosG3P +mb:Modbus +switch + +msg_unknown() +close() - - -A14->A16 - - + + +A6->A16 + + - - -A15->A6 - - + + +A7 + +<<AsyncIfc>> + + +set_node_id() +get_conn_no() +tx_add() +tx_flush() +tx_get() +tx_peek() +tx_log() +tx_clear() +tx_len() +fwd_add() +fwd_flush() +fwd_log() +fwd_clear() +rx_get() +rx_peek() +rx_log() +rx_clear() +rx_len() +rx_set_cb() +prot_set_timeout_cb() + + + +A8 + +AsyncIfcImpl + +fwd_fifo:ByteFifo +tx_fifo:ByteFifo +rx_fifo:ByteFifo +conn_no:Count +node_id +timeout_cb + + + +A7->A8 + + + + + +A9 + +AsyncStream + + + +A8->A9 + + + + + +A10 + +ConnectionG3 + +remote.stream:ConnectionG3 + +healthy() +close() + + + +A10->A3 + + + + + +A10->A9 + + + +1 + + + +A10->A10 + + +0..1 +has + + + +A11 + +ConnectionG3P + +remote.stream:ConnectionG3P + +healthy() +close() + + + +A11->A4 + + + + + +A11->A9 + + + +1 + + + +A11->A11 + + +0..1 +has + + + +A12 + +Infos + +stat +new_stat_data +info_dev + +static_init() +dev_value() +inc_counter() +dec_counter() +ha_proxy_conf +ha_conf +ha_remove +update_db +set_db_def_value +get_db_value +ignore_this_device + + + +A13 + +InfosG3 + + +ha_confs() +parse() + + + +A12->A13 + + + + + +A14 + +InfosG3P + + +ha_confs() +parse() + + + +A12->A14 + + + + + +A15->A7 + + +use + + + +A15->A10 + + + + + +A15->A13 + + - + A16->A7 - - + + +use + + + +A16->A11 + + + + + +A16->A14 + + A17 - -ModbusConn - -host -port -addr -stream:InverterG3P - + +Modbus + +que +snd_handler +rsp_handler +timeout +max_retires +last_xxx +err +retry_cnt +req_pend +tim + +build_msg() +recv_req() +recv_resp() +close() - - -A17->A13 - - -1 -has + + +A17->A15 + + +has +1 + + + +A17->A16 + + +has +1 + + + +A18 + +ModbusConn + +host +port +addr +stream:InverterG3P + + + + +A18->A4 + + +1 +has diff --git a/app/proxy.yuml b/app/proxy.yuml index c629d35..9b1b136 100644 --- a/app/proxy.yuml +++ b/app/proxy.yuml @@ -3,28 +3,49 @@ // {generate:true} [note: You can stick notes on diagrams too!{bg:cornsilk}] -[Singleton]^[Mqtt|ha_restarts;__client;__cb_MqttIsUp|publish();close()] -[Modbus|que;;snd_handler;rsp_handler;timeout;max_retires;last_xxx;err;retry_cnt;req_pend;tim|build_msg();recv_req();recv_resp();close()] -[IterRegistry||__iter__]^[Message|server_side:bool;header_valid:bool;header_len:unsigned;data_len:unsigned;unique_id;node_id;sug_area;_recv_buffer:bytearray;_send_buffer:bytearray;_forward_buffer:bytearray;db:Infos;new_data:list;state|_read():void;close():void;inc_counter():void;dec_counter():void] -[Message]^[Talent|await_conn_resp_cnt;id_str;contact_name;contact_mail;db:InfosG3;mb:Modbus;switch|msg_contact_info();msg_ota_update();msg_get_time();msg_collector_data();msg_inverter_data();msg_unknown();;close()] -[Message]^[SolarmanV5|control;serial;snr;db:InfosG3P;mb:Modbus;switch|msg_unknown();;close()] -[Talent]^[ConnectionG3|remote_stream:ConnectionG3|healthy();close()] -[Talent]has-1>[Modbus] -[SolarmanV5]^[ConnectionG3P|remote_stream:ConnectionG3P|healthy();close()] -[SolarmanV5]has-1>[Modbus] -[AsyncStream|reader;writer;addr;r_addr;l_addr|server_loop();client_loop();loop;disc();close();;__async_read();async_write();__async_forward()]^[ConnectionG3] -[AsyncStream]^[ConnectionG3P] -[Inverter|cls.db_stat;cls.entity_prfx;cls.discovery_prfx;cls.proxy_node_id;cls.proxy_unique_id;cls.mqtt:Mqtt|]^[InverterG3|__ha_restarts|async_create_remote();async_publ_mqtt();;close()] + +[Mqtt;<>|ha_restarts;__client;__cb_MqttIsUp|publish();close()] +[Inverter|cls.db_stat;cls.entity_prfx;cls.discovery_prfx;cls.proxy_node_id;cls.proxy_unique_id;cls.mqtt:Mqtt|] +[Inverter]^[InverterG3|__ha_restarts|async_create_remote();async_publ_mqtt();;close()] [Inverter]^[InverterG3P|__ha_restarts|async_create_remote(;)async_publ_mqtt();close()] -[Mqtt]-[Inverter] +[Mqtt;<>]<-++[Inverter] + +[IterRegistry||__iter__]^[Message|server_side:bool;header_valid:bool;header_len:unsigned;data_len:unsigned;unique_id;node_id;sug_area;_recv_buffer:bytearray;_send_buffer:bytearray;_forward_buffer:bytearray;db:Infos;new_data:list;state|_read():void;close():void;inc_counter():void;dec_counter():void] + +[<>||set_node_id();get_conn_no();;tx_add();tx_flush();tx_get();tx_peek();tx_log();tx_clear();tx_len();;fwd_add();fwd_flush();fwd_log();fwd_clear();rx_get();rx_peek();rx_log();rx_clear();rx_len();rx_set_cb();;prot_set_timeout_cb()] +[AsyncIfcImpl|fwd_fifo:ByteFifo;tx_fifo:ByteFifo;rx_fifo:ByteFifo;conn_no:Count;node_id;timeout_cb] +[<>]^-.-[AsyncIfcImpl] +[AsyncIfcImpl]<-[AsyncStream] +[AsyncStream|reader;writer;addr;r_addr;l_addr|server_loop();client_loop();loop;disc();close();;__async_read();async_write();__async_forward()] + +[ConnectionG3|remote.stream:ConnectionG3|healthy();close()] [ConnectionG3]^[InverterG3] [ConnectionG3]has-0..1>[ConnectionG3] +[ConnectionG3]++-1>[AsyncStream] +[ConnectionG3P|remote.stream:ConnectionG3P|healthy();close()] [ConnectionG3P]^[InverterG3P] [ConnectionG3P]has-0..1>[ConnectionG3P] +[ConnectionG3P]++-1>[AsyncStream] -[Infos|stat;new_stat_data;info_dev|static_init();dev_value();inc_counter();dec_counter();ha_proxy_conf;ha_conf;ha_remove;update_db;set_db_def_value;get_db_value;ignore_this_device]^[InfosG3||ha_confs();parse()] +[Infos|stat;new_stat_data;info_dev|static_init();dev_value();inc_counter();dec_counter();ha_proxy_conf;ha_conf;ha_remove;update_db;set_db_def_value;get_db_value;ignore_this_device] +[Infos]^[InfosG3||ha_confs();parse()] [Infos]^[InfosG3P||ha_confs();parse()] -[InfosG3P]->[SolarmanV5] -[InfosG3]->[Talent] + +[Talent|await_conn_resp_cnt;id_str;contact_name;contact_mail;db:InfosG3;mb:Modbus;switch|msg_contact_info();msg_ota_update();msg_get_time();msg_collector_data();msg_inverter_data();msg_unknown();;close()] +[Talent]^[ConnectionG3] +[Talent]use->[<>] +[Talent]->[InfosG3] +[SolarmanV5|control;serial;snr;db:InfosG3P;mb:Modbus;switch|msg_unknown();;close()] +[SolarmanV5]^[ConnectionG3P] +[SolarmanV5]use->[<>] +[SolarmanV5]->[InfosG3P] + +[Message]^[Talent] +[Message]^[SolarmanV5] + +[Modbus|que;;snd_handler;rsp_handler;timeout;max_retires;last_xxx;err;retry_cnt;req_pend;tim|build_msg();recv_req();recv_resp();close()] +[Modbus]<1-has[SolarmanV5] +[Modbus]<1-has[Talent] + [ModbusConn|host;port;addr;stream:InverterG3P;|]has-1>[InverterG3P] diff --git a/app/src/async_ifc.py b/app/src/async_ifc.py index b0dde65..333fb06 100644 --- a/app/src/async_ifc.py +++ b/app/src/async_ifc.py @@ -1,11 +1,111 @@ -if __name__ == "app.src.async_ifc": - from app.src.byte_fifo import ByteFifo -else: # pragma: no cover - from byte_fifo import ByteFifo +from abc import ABC, abstractmethod -class AsyncIfc(): - def __init__(self): - self.read = ByteFifo() - self.write = ByteFifo() - self.forward = ByteFifo() +class AsyncIfc(ABC): + @abstractmethod + def get_conn_no(self): + pass # pragma: no cover + + @abstractmethod + def set_node_id(self, value: str): + pass # pragma: no cover + + # + # TX - QUEUE + # + @abstractmethod + def tx_add(self, data: bytearray): + ''' add data to transmit queue''' + pass # pragma: no cover + + @abstractmethod + def tx_flush(self): + ''' send transmit queue and clears it''' + pass # pragma: no cover + + @abstractmethod + def tx_get(self, size: int = None) -> bytearray: + '''removes size numbers of bytes and return them''' + pass # pragma: no cover + + @abstractmethod + def tx_peek(self, size: int = None) -> bytearray: + '''returns size numbers of byte without removing them''' + pass # pragma: no cover + + @abstractmethod + def tx_log(self, level, info): + ''' log the transmit queue''' + pass # pragma: no cover + + @abstractmethod + def tx_clear(self): + ''' clear transmit queue''' + pass # pragma: no cover + + @abstractmethod + def tx_len(self): + ''' get numner of bytes in the transmit queue''' + pass # pragma: no cover + + # + # FORWARD - QUEUE + # + @abstractmethod + def fwd_add(self, data: bytearray): + ''' add data to forward queue''' + pass # pragma: no cover + + @abstractmethod + def fwd_flush(self): + ''' send forward queue and clears it''' + pass # pragma: no cover + + @abstractmethod + def fwd_log(self, level, info): + ''' log the forward queue''' + pass # pragma: no cover + + @abstractmethod + def fwd_clear(self): + ''' clear forward queue''' + pass # pragma: no cover + + # + # RX - QUEUE + # + @abstractmethod + def rx_get(self, size: int = None) -> bytearray: + '''removes size numbers of bytes and return them''' + pass # pragma: no cover + + @abstractmethod + def rx_peek(self, size: int = None) -> bytearray: + '''returns size numbers of byte without removing them''' + pass # pragma: no cover + + @abstractmethod + def rx_log(self, level, info): + ''' logs the receive queue''' + pass # pragma: no cover + + @abstractmethod + def rx_clear(self): + ''' clear receive queue''' + pass # pragma: no cover + + @abstractmethod + def rx_len(self): + ''' get numner of bytes in the receive queue''' + pass # pragma: no cover + + @abstractmethod + def rx_set_cb(self, callback): + pass # pragma: no cover + + # + # Protocol Callbacks + # + @abstractmethod + def prot_set_timeout_cb(self, callback): + pass # pragma: no cover diff --git a/app/src/async_stream.py b/app/src/async_stream.py index d8a584a..83e3426 100644 --- a/app/src/async_stream.py +++ b/app/src/async_stream.py @@ -7,19 +7,114 @@ from typing import Self from itertools import count if __name__ == "app.src.async_stream": + from app.src.byte_fifo import ByteFifo from app.src.async_ifc import AsyncIfc - from app.src.messages import State + from app.src.infos import Infos else: # pragma: no cover + from byte_fifo import ByteFifo from async_ifc import AsyncIfc - from messages import State + from infos import Infos import gc logger = logging.getLogger('conn') -class AsyncStream(): +class AsyncIfcImpl(AsyncIfc): _ids = count(0) + + def __init__(self) -> None: + logger.debug('AsyncIfcImpl.__init__') + self.fwd_fifo = ByteFifo() + self.tx_fifo = ByteFifo() + self.rx_fifo = ByteFifo() + self.conn_no = next(self._ids) + self.node_id = '' + self.timeout_cb = None + + def set_node_id(self, value: str): + self.node_id = value + + def get_conn_no(self): + return self.conn_no + + def tx_add(self, data: bytearray): + ''' add data to transmit queue''' + self.tx_fifo += data + + def tx_flush(self): + ''' send transmit queue and clears it''' + self.tx_fifo() + + def tx_get(self, size: int = None) -> bytearray: + '''removes size numbers of bytes and return them''' + return self.tx_fifo.get(size) + + def tx_peek(self, size: int = None) -> bytearray: + '''returns size numbers of byte without removing them''' + return self.tx_fifo.peek(size) + + def tx_log(self, level, info): + ''' log the transmit queue''' + self.tx_fifo.logging(level, info) + + def tx_clear(self): + ''' clear transmit queue''' + self.tx_fifo.clear() + + def tx_len(self): + ''' get numner of bytes in the transmit queue''' + return len(self.tx_fifo) + + def fwd_add(self, data: bytearray): + ''' add data to forward queue''' + self.fwd_fifo += data + + def fwd_flush(self): + ''' send forward queue and clears it''' + self.fwd_fifo() + + def fwd_log(self, level, info): + ''' log the forward queue''' + self.fwd_fifo.logging(level, info) + + def fwd_clear(self): + ''' clear forward queue''' + self.fwd_fifo.clear() + + def rx_get(self, size: int = None) -> bytearray: + '''removes size numbers of bytes and return them''' + return self.rx_fifo.get(size) + + def rx_peek(self, size: int = None) -> bytearray: + '''returns size numbers of byte without removing them''' + return self.rx_fifo.peek(size) + + def rx_log(self, level, info): + ''' logs the receive queue''' + self.rx_fifo.logging(level, info) + + def rx_clear(self): + ''' clear receive queue''' + self.rx_fifo.clear() + + def rx_len(self): + ''' get numner of bytes in the receive queue''' + return len(self.rx_fifo) + + def rx_set_cb(self, callback): + self.rx_fifo.reg_trigger(callback) + + def prot_set_timeout_cb(self, callback): + self.timeout_cb = callback + + +class StreamPtr(): + def __init__(self, stream): + self.stream = stream + + +class AsyncStream(AsyncIfcImpl): MAX_PROC_TIME = 2 '''maximum processing time for a received msg in sec''' MAX_START_TIME = 400 @@ -30,31 +125,31 @@ class AsyncStream(): '''maximum default time without a received msg in sec''' def __init__(self, reader: StreamReader, writer: StreamWriter, - addr, ifc: "AsyncIfc") -> None: + addr, async_publ_mqtt, async_create_remote, + rstream: "StreamPtr") -> None: + AsyncIfcImpl.__init__(self) + logger.debug('AsyncStream.__init__') - ifc.write.reg_trigger(self.__write_cb) - self.ifc = ifc + + self.remote = rstream + self.tx_fifo.reg_trigger(self.__write_cb) + self.async_create_remote = async_create_remote self._reader = reader self._writer = writer self.addr = addr self.r_addr = '' self.l_addr = '' - self.conn_no = next(self._ids) self.proc_start = None # start processing start timestamp self.proc_max = 0 + self.async_publ_mqtt = async_publ_mqtt def __write_cb(self): - self._writer.write(self.ifc.write.get()) + self._writer.write(self.tx_fifo.get()) def __timeout(self) -> int: - if self.state == State.init or self.state == State.received: - to = self.MAX_START_TIME - elif self.state == State.up and \ - self.server_side and self.modbus_polling: - to = self.MAX_INV_IDLE_TIME - else: - to = self.MAX_DEF_IDLE_TIME - return to + if self.timeout_cb is callable: + return self.timeout_cb + return 360 async def publish_outstanding_mqtt(self): '''Publish all outstanding MQTT topics''' @@ -69,25 +164,25 @@ class AsyncStream(): '''Loop for receiving messages from the inverter (server-side)''' logger.info(f'[{self.node_id}:{self.conn_no}] ' f'Accept connection from {addr}') - self.inc_counter('Inverter_Cnt') + Infos.inc_counter('Inverter_Cnt') await self.publish_outstanding_mqtt() await self.loop() - self.dec_counter('Inverter_Cnt') + Infos.dec_counter('Inverter_Cnt') await self.publish_outstanding_mqtt() logger.info(f'[{self.node_id}:{self.conn_no}] Server loop stopped for' f' r{self.r_addr}') # if the server connection closes, we also have to disconnect # the connection to te TSUN cloud - if self.remote_stream: + if self.remote.stream: logger.info(f'[{self.node_id}:{self.conn_no}] disc client ' - f'connection: [{self.remote_stream.node_id}:' - f'{self.remote_stream.conn_no}]') - await self.remote_stream.disc() + f'connection: [{self.remote.stream.node_id}:' + f'{self.remote.stream.conn_no}]') + await self.remote.stream._ifc.disc() async def client_loop(self, _: str) -> None: '''Loop for receiving messages from the TSUN cloud (client-side)''' - client_stream = await self.remote_stream.loop() + client_stream = await self.remote.stream._ifc.loop() logger.info(f'[{client_stream.node_id}:{client_stream.conn_no}] ' 'Client loop stopped for' f' l{client_stream.l_addr}') @@ -98,13 +193,13 @@ class AsyncStream(): # establish a new connection to the TSUN cloud # erase backlink to inverter - client_stream.remote_stream = None + client_stream.remote.stream = None - if self.remote_stream == client_stream: + if self.remote.stream == client_stream: # logging.debug(f'Client l{client_stream.l_addr} refs:' # f' {gc.get_referrers(client_stream)}') # than erase client connection - self.remote_stream = None + self.remote.stream = None async def loop(self) -> Self: """Async loop handler for precessing all received messages""" @@ -121,10 +216,10 @@ class AsyncStream(): await asyncio.wait_for(self.__async_read(), dead_conn_to) - if self.unique_id: - await self.__async_write() - await self.__async_forward() - await self.async_publ_mqtt() + # if self.unique_id: + await self.__async_write() + await self.__async_forward() + await self.async_publ_mqtt() except asyncio.TimeoutError: logger.warning(f'[{self.node_id}:{self.conn_no}] Dead ' @@ -150,7 +245,7 @@ class AsyncStream(): return self except Exception: - self.inc_counter('SW_Exception') + Infos.inc_counter('SW_Exception') logger.error( f"Exception for {self.addr}:\n" f"{traceback.format_exc()}") @@ -158,9 +253,9 @@ class AsyncStream(): async def __async_write(self, headline: str = 'Transmit to ') -> None: """Async write handler to transmit the send_buffer""" - if len(self.ifc.write) > 0: - self.ifc.write.logging(logging.INFO, f'{headline}{self.addr}:') - self._writer.write(self.ifc.write.get()) + if len(self.tx_fifo) > 0: + self.tx_fifo.logging(logging.INFO, f'{headline}{self.addr}:') + self._writer.write(self.tx_fifo.get()) await self._writer.drain() async def disc(self) -> None: @@ -176,18 +271,19 @@ class AsyncStream(): hint: must be called before releasing the connection instance """ + self.tx_fifo.reg_trigger(None) + self.async_create_remote = None self._reader.feed_eof() # abort awaited read if self._writer.is_closing(): return logger.debug(f'AsyncStream.close() l{self.l_addr} | r{self.r_addr}') - self.ifc.write.reg_trigger(None) self._writer.close() def healthy(self) -> bool: elapsed = 0 if self.proc_start is not None: elapsed = time.time() - self.proc_start - if self.state == State.closed or elapsed > self.MAX_PROC_TIME: + if elapsed > self.MAX_PROC_TIME: logging.debug(f'[{self.node_id}:{self.conn_no}:' f'{type(self).__name__}]' f' act:{round(1000*elapsed)}ms' @@ -203,8 +299,8 @@ class AsyncStream(): data = await self._reader.read(4096) if data: self.proc_start = time.time() - self.ifc.read += data - wait = self.ifc.read() # call read in parent class + self.rx_fifo += data + wait = self.rx_fifo() # call read in parent class if wait > 0: await asyncio.sleep(wait) else: @@ -212,42 +308,42 @@ class AsyncStream(): async def __async_forward(self) -> None: """forward handler transmits data over the remote connection""" - if len(self.ifc.forward) == 0: + if len(self.fwd_fifo) == 0: return try: - if not self.remote_stream: + if not self.remote.stream: await self.async_create_remote() - if self.remote_stream: - if self.remote_stream._init_new_client_conn(): - await self.remote_stream.__async_write() + if self.remote.stream: + if self.remote.stream._init_new_client_conn(): + await self.remote.stream._ifc.__async_write() - if self.remote_stream: - self.remote_stream._update_header(self.ifc.forward.peek()) - self.ifc.forward.logging(logging.INFO, 'Forward to ' - f'{self.remote_stream.addr}:') - self.remote_stream._writer.write(self.ifc.forward.get()) - await self.remote_stream._writer.drain() + if self.remote.stream: + self.remote.stream._update_header(self.fwd_fifo.peek()) + self.fwd_fifo.logging(logging.INFO, 'Forward to ' + f'{self.remote.stream.addr}:') + self.remote.stream._ifc._writer.write(self.fwd_fifo.get()) + await self.remote.stream._ifc._writer.drain() except OSError as error: - if self.remote_stream: - rmt = self.remote_stream - self.remote_stream = None + if self.remote.stream: + rmt = self.remote.stream + self.remote.stream = None logger.error(f'[{rmt.node_id}:{rmt.conn_no}] Fwd: {error} for ' - f'l{rmt.l_addr} | r{rmt.r_addr}') - await rmt.disc() - rmt.close() + f'l{rmt._ifc.l_addr} | r{rmt._ifc.r_addr}') + await rmt._ifc.disc() + rmt._ifc.close() except RuntimeError as error: - if self.remote_stream: - rmt = self.remote_stream - self.remote_stream = None + if self.remote.stream: + rmt = self.remote.stream + self.remote.stream = None logger.info(f'[{rmt.node_id}:{rmt.conn_no}] ' - f'Fwd: {error} for {rmt.l_addr}') - await rmt.disc() - rmt.close() + f'Fwd: {error} for {rmt._ifc.l_addr}') + await rmt._ifc.disc() + rmt._ifc.close() except Exception: - self.inc_counter('SW_Exception') + Infos.inc_counter('SW_Exception') logger.error( f"Fwd Exception for {self.addr}:\n" f"{traceback.format_exc()}") diff --git a/app/src/gen3/connection_g3.py b/app/src/gen3/connection_g3.py index c1f9914..8633cfc 100644 --- a/app/src/gen3/connection_g3.py +++ b/app/src/gen3/connection_g3.py @@ -2,33 +2,35 @@ import logging from asyncio import StreamReader, StreamWriter if __name__ == "app.src.gen3.connection_g3": - from app.src.async_ifc import AsyncIfc - from app.src.async_stream import AsyncStream + from app.src.async_stream import AsyncStream, StreamPtr from app.src.gen3.talent import Talent else: # pragma: no cover - from async_ifc import AsyncIfc - from async_stream import AsyncStream + from async_stream import AsyncStream, StreamPtr from gen3.talent import Talent logger = logging.getLogger('conn') -class ConnectionG3(AsyncStream, Talent): +class ConnectionG3(Talent): def __init__(self, reader: StreamReader, writer: StreamWriter, - addr, remote_stream: 'ConnectionG3', server_side: bool, + addr, rstream: 'ConnectionG3', server_side: bool, id_str=b'') -> None: - self._ifc = AsyncIfc() - AsyncStream.__init__(self, reader, writer, addr, self._ifc) + self.remote = StreamPtr(rstream) + self._ifc = AsyncStream(reader, writer, addr, + self.async_publ_mqtt, + self.async_create_remote, + self.remote) Talent.__init__(self, server_side, self._ifc, id_str) - self.remote_stream: 'ConnectionG3' = remote_stream + self.conn_no = self._ifc.get_conn_no() + self.addr = addr ''' Our puplic methods ''' def close(self): - AsyncStream.close(self) + self._ifc.close() Talent.close(self) # logger.info(f'AsyncStream refs: {gc.get_referrers(self)}') @@ -40,10 +42,4 @@ class ConnectionG3(AsyncStream, Talent): def healthy(self) -> bool: logger.debug('ConnectionG3 healthy()') - return AsyncStream.healthy(self) - - ''' - Our private methods - ''' - def __del__(self): - super().__del__() + return self._ifc.healthy() diff --git a/app/src/gen3/inverter_g3.py b/app/src/gen3/inverter_g3.py index c365286..c913c28 100644 --- a/app/src/gen3/inverter_g3.py +++ b/app/src/gen3/inverter_g3.py @@ -53,6 +53,7 @@ class InverterG3(Inverter, ConnectionG3): def __init__(self, reader: StreamReader, writer: StreamWriter, addr): super().__init__(reader, writer, addr, None, True) self.__ha_restarts = -1 + self.addr = addr async def async_create_remote(self) -> None: '''Establish a client connection to the TSUN cloud''' @@ -65,12 +66,12 @@ class InverterG3(Inverter, ConnectionG3): logging.info(f'[{self.node_id}] Connect to {addr}') connect = asyncio.open_connection(host, port) reader, writer = await connect - self.remote_stream = ConnectionG3(reader, writer, addr, self, + self.remote.stream = ConnectionG3(reader, writer, addr, self, False, self.id_str) - logging.info(f'[{self.remote_stream.node_id}:' - f'{self.remote_stream.conn_no}] ' + logging.info(f'[{self.remote.stream.node_id}:' + f'{self.remote.stream.conn_no}] ' f'Connected to {addr}') - asyncio.create_task(self.client_loop(addr)) + asyncio.create_task(self._ifc.client_loop(addr)) except (ConnectionRefusedError, TimeoutError) as error: logging.info(f'{error}') @@ -128,10 +129,6 @@ class InverterG3(Inverter, ConnectionG3): self.db.reg_clr_at_midnight(f'{self.entity_prfx}{self.node_id}') def close(self) -> None: - logging.debug(f'InverterG3.close() l{self.l_addr} | r{self.r_addr}') + logging.debug(f'InverterG3.close() {self.addr}') super().close() # call close handler in the parent class # logging.info(f'Inverter refs: {gc.get_referrers(self)}') - - def __del__(self): - logging.debug("InverterG3.__del__") - super().__del__() diff --git a/app/src/gen3/talent.py b/app/src/gen3/talent.py index 87c8a21..4fd24a3 100644 --- a/app/src/gen3/talent.py +++ b/app/src/gen3/talent.py @@ -48,7 +48,8 @@ class Talent(Message): def __init__(self, server_side: bool, ifc: "AsyncIfc", id_str=b''): super().__init__(server_side, self.send_modbus_cb, mb_timeout=15) - ifc.read.reg_trigger(self.read) + ifc.rx_set_cb(self.read) + ifc.prot_set_timeout_cb(self._timeout) self.ifc = ifc self.await_conn_resp_cnt = 0 self.id_str = id_str @@ -107,7 +108,8 @@ class Talent(Message): self.log_lvl.clear() self.state = State.closed self.mb_timer.close() - self.ifc.read.reg_trigger(None) + self.ifc.rx_set_cb(None) + self.ifc.prot_set_timeout_cb(None) super().close() def __set_serial_no(self, serial_no: str): @@ -143,10 +145,10 @@ class Talent(Message): self._read() while True: if not self.header_valid: - self.__parse_header(self.ifc.read.peek(), len(self.ifc.read)) + self.__parse_header(self.ifc.rx_peek(), self.ifc.rx_len()) if self.header_valid and \ - len(self.ifc.read) >= (self.header_len + self.data_len): + self.ifc.rx_len() >= (self.header_len + self.data_len): if self.state == State.init: self.state = State.received # received 1st package @@ -154,10 +156,10 @@ class Talent(Message): if callable(log_lvl): log_lvl = log_lvl() - self.ifc.read.logging(log_lvl, f'Received from {self.addr}:' - f' BufLen: {len(self.ifc.read)}' - f' HdrLen: {self.header_len}' - f' DtaLen: {self.data_len}') + self.ifc.rx_log(log_lvl, f'Received from {self.addr}:' + f' BufLen: {self.ifc.rx_len()}' + f' HdrLen: {self.header_len}' + f' DtaLen: {self.data_len}') self.__set_serial_no(self.id_str.decode("utf-8")) self.__dispatch_msg() @@ -170,9 +172,9 @@ class Talent(Message): tsun = Config.get('tsun') if tsun['enabled']: buflen = self.header_len+self.data_len - buffer = self.ifc.read.peek(buflen) - self.ifc.forward += buffer - self.ifc.forward.logging(logging.DEBUG, 'Store for forwarding:') + buffer = self.ifc.rx_peek(buflen) + self.ifc.fwd_add(buffer) + self.ifc.fwd_log(logging.DEBUG, 'Store for forwarding:') fnc = self.switch.get(self.msg_id, self.msg_unknown) logger.info(self.__flow_str(self.server_side, 'forwrd') + @@ -181,18 +183,18 @@ class Talent(Message): def forward_snd(self) -> None: '''add the build send msg to the forwarding queue''' tsun = Config.get('tsun') - rest = self.ifc.write.get(self.send_msg_ofs) - buffer = self.ifc.write.get(len(self.ifc.write)) + rest = self.ifc.tx_get(self.send_msg_ofs) + buffer = self.ifc.tx_get() if tsun['enabled']: _len = len(buffer) struct.pack_into('!l', buffer, 0, _len-4) - self.ifc.forward += buffer - self.ifc.forward.logging(logging.INFO, 'Store for forwarding:') + self.ifc.fwd_add(buffer) + self.ifc.fwd_log(logging.INFO, 'Store for forwarding:') fnc = self.switch.get(self.msg_id, self.msg_unknown) logger.info(self.__flow_str(self.server_side, 'forwrd') + f' Ctl: {int(self.ctrl):#02x} Msg: {fnc.__name__!r}') - self.ifc.write += rest + self.ifc.tx_add(rest) def send_modbus_cb(self, modbus_pdu: bytearray, log_lvl: int, state: str): if self.state != State.up: @@ -201,13 +203,13 @@ class Talent(Message): return self.__build_header(0x70, 0x77) - self.ifc.write += b'\x00\x01\xa3\x28' # magic ? - self.ifc.write += struct.pack('!B', len(modbus_pdu)) - self.ifc.write += modbus_pdu + self.ifc.tx_add(b'\x00\x01\xa3\x28') # magic ? + self.ifc.tx_add(struct.pack('!B', len(modbus_pdu))) + self.ifc.tx_add(modbus_pdu) self.__finish_send_msg() - self.ifc.write.logging(log_lvl, f'Send Modbus {state}:{self.addr}:') - self.ifc.write() + 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: @@ -235,9 +237,9 @@ class Talent(Message): self.msg_id = 0 self.await_conn_resp_cnt += 1 self.__build_header(0x91) - self.ifc.write += struct.pack(f'!{len(contact_name)+1}p' - f'{len(contact_mail)+1}p', - contact_name, contact_mail) + self.ifc.tx_add(struct.pack(f'!{len(contact_name)+1}p' + f'{len(contact_mail)+1}p', + contact_name, contact_mail)) self.__finish_send_msg() return True @@ -321,7 +323,7 @@ class Talent(Message): self.inc_counter('Invalid_Msg_Format') # erase broken recv buffer - self.ifc.read.clear() + self.ifc.rx_clear() return hdr_len = 5+id_len+2 @@ -342,16 +344,16 @@ class Talent(Message): def __build_header(self, ctrl, msg_id=None) -> None: if not msg_id: msg_id = self.msg_id - self.send_msg_ofs = len(self.ifc.write) - self.ifc.write += struct.pack(f'!l{len(self.id_str)+1}pBB', - 0, self.id_str, ctrl, msg_id) + self.send_msg_ofs = self.ifc.tx_len() + self.ifc.tx_add(struct.pack(f'!l{len(self.id_str)+1}pBB', + 0, self.id_str, ctrl, msg_id)) fnc = self.switch.get(msg_id, self.msg_unknown) logger.info(self.__flow_str(self.server_side, 'tx') + f' Ctl: {int(ctrl):#02x} Msg: {fnc.__name__!r}') def __finish_send_msg(self) -> None: - _len = len(self.ifc.write) - self.send_msg_ofs - struct.pack_into('!l', self.ifc.write.peek(), self.send_msg_ofs, + _len = self.ifc.tx_len() - self.send_msg_ofs + struct.pack_into('!l', self.ifc.tx_peek(), self.send_msg_ofs, _len-4) def __dispatch_msg(self) -> None: @@ -366,7 +368,7 @@ class Talent(Message): f' Ctl: {int(self.ctrl):#02x} Msg: {fnc.__name__!r}') def __flush_recv_msg(self) -> None: - self.ifc.read.get(self.header_len+self.data_len) + self.ifc.rx_get(self.header_len+self.data_len) self.header_valid = False ''' @@ -376,7 +378,7 @@ class Talent(Message): if self.ctrl.is_ind(): if self.server_side and self.__process_contact_info(): self.__build_header(0x91) - self.ifc.write += b'\x01' + self.ifc.tx_add(b'\x01') self.__finish_send_msg() # don't forward this contact info here, we will build one # when the remote connection is established @@ -390,7 +392,7 @@ class Talent(Message): self.forward() def __process_contact_info(self) -> bool: - buf = self.ifc.read.peek() + buf = self.ifc.rx_peek() result = struct.unpack_from('!B', buf, self.header_len) name_len = result[0] if self.data_len == 1: # this is a response withone status byte @@ -417,16 +419,16 @@ class Talent(Message): ts = self._timestamp() logger.debug(f'time: {ts:08x}') self.__build_header(0x91) - self.ifc.write += struct.pack('!q', ts) + self.ifc.tx_add(struct.pack('!q', ts)) self.__finish_send_msg() elif self.data_len >= 8: ts = self._timestamp() - result = struct.unpack_from('!q', self.ifc.read.peek(), + result = struct.unpack_from('!q', self.ifc.rx_peek(), self.header_len) self.ts_offset = result[0]-ts - if self.remote_stream: - self.remote_stream.ts_offset = self.ts_offset + if self.remote.stream: + self.remote.stream.ts_offset = self.ts_offset logger.debug(f'tsun-time: {int(result[0]):08x}' f' proxy-time: {ts:08x}' f' offset: {self.ts_offset}') @@ -446,10 +448,10 @@ class Talent(Message): self.db.set_db_def_value(Register.POLLING_INTERVAL, self.mb_timeout) self.__build_header(0x99) - self.ifc.write += b'\x02' + self.ifc.tx_add(b'\x02') self.__finish_send_msg() - result = struct.unpack_from('!Bq', self.ifc.read.peek(), + result = struct.unpack_from('!Bq', self.ifc.rx_peek(), self.header_len) resp_code = result[0] ts = result[1]+self.ts_offset @@ -457,11 +459,11 @@ class Talent(Message): f' tsun-time: {ts:08x}' f' offset: {self.ts_offset}') self.__build_header(0x91) - self.ifc.write += struct.pack('!Bq', resp_code, ts) + self.ifc.tx_add(struct.pack('!Bq', resp_code, ts)) self.forward_snd() return elif self.ctrl.is_resp(): - result = struct.unpack_from('!B', self.ifc.read.peek(), + result = struct.unpack_from('!B', self.ifc.rx_peek(), self.header_len) resp_code = result[0] logging.debug(f'TimeActRespCode: {resp_code}') @@ -473,7 +475,7 @@ class Talent(Message): self.forward() def parse_msg_header(self): - result = struct.unpack_from('!lB', self.ifc.read.peek(), + result = struct.unpack_from('!lB', self.ifc.rx_peek(), self.header_len) data_id = result[0] # len of complete message @@ -482,7 +484,7 @@ class Talent(Message): msg_hdr_len = 5+id_len+9 - result = struct.unpack_from(f'!{id_len+1}pBq', self.ifc.read.peek(), + result = struct.unpack_from(f'!{id_len+1}pBq', self.ifc.rx_peek(), self.header_len + 4) timestamp = result[2] @@ -495,7 +497,7 @@ class Talent(Message): def msg_collector_data(self): if self.ctrl.is_ind(): self.__build_header(0x99) - self.ifc.write += b'\x01' + self.ifc.tx_add(b'\x01') self.__finish_send_msg() self.__process_data() @@ -510,7 +512,7 @@ class Talent(Message): def msg_inverter_data(self): if self.ctrl.is_ind(): self.__build_header(0x99) - self.ifc.write += b'\x01' + self.ifc.tx_add(b'\x01') self.__finish_send_msg() self.__process_data() self.state = State.up # allow MODBUS cmds @@ -530,7 +532,7 @@ class Talent(Message): def __process_data(self): msg_hdr_len, ts = self.parse_msg_header() - for key, update in self.db.parse(self.ifc.read.peek(), self.header_len + for key, update in self.db.parse(self.ifc.rx_peek(), self.header_len + msg_hdr_len, self.node_id): if update: self._set_mqtt_timestamp(key, self._utcfromts(ts)) @@ -550,7 +552,7 @@ class Talent(Message): msg_hdr_len = 5 - result = struct.unpack_from('!lBB', self.ifc.read.peek(), + result = struct.unpack_from('!lBB', self.ifc.rx_peek(), self.header_len) modbus_len = result[1] return msg_hdr_len, modbus_len @@ -559,7 +561,7 @@ class Talent(Message): msg_hdr_len = 6 - result = struct.unpack_from('!lBBB', self.ifc.read.peek(), + result = struct.unpack_from('!lBBB', self.ifc.rx_peek(), self.header_len) modbus_len = result[2] return msg_hdr_len, modbus_len @@ -580,12 +582,12 @@ class Talent(Message): self.__msg_modbus(hdr_len) def __msg_modbus(self, hdr_len): - data = self.ifc.read.peek()[self.header_len: - self.header_len+self.data_len] + data = self.ifc.rx_peek()[self.header_len: + self.header_len+self.data_len] if self.ctrl.is_req(): - if self.remote_stream.mb.recv_req(data[hdr_len:], - self.remote_stream. + if self.remote.stream.mb.recv_req(data[hdr_len:], + self.remote.stream. msg_forward): self.inc_counter('Modbus_Command') else: diff --git a/app/src/gen3plus/connection_g3p.py b/app/src/gen3plus/connection_g3p.py index 735058d..a816c25 100644 --- a/app/src/gen3plus/connection_g3p.py +++ b/app/src/gen3plus/connection_g3p.py @@ -2,34 +2,37 @@ import logging from asyncio import StreamReader, StreamWriter if __name__ == "app.src.gen3plus.connection_g3p": - from app.src.async_ifc import AsyncIfc - from app.src.async_stream import AsyncStream + from app.src.async_stream import AsyncStream, StreamPtr from app.src.gen3plus.solarman_v5 import SolarmanV5 else: # pragma: no cover - from async_ifc import AsyncIfc - from async_stream import AsyncStream + from async_stream import AsyncStream, StreamPtr from gen3plus.solarman_v5 import SolarmanV5 logger = logging.getLogger('conn') -class ConnectionG3P(AsyncStream, SolarmanV5): +class ConnectionG3P(SolarmanV5): def __init__(self, reader: StreamReader, writer: StreamWriter, - addr, remote_stream: 'ConnectionG3P', + addr, rstream: 'ConnectionG3P', server_side: bool, client_mode: bool) -> None: - self._ifc = AsyncIfc() - AsyncStream.__init__(self, reader, writer, addr, self._ifc) + + self.remote = StreamPtr(rstream) + self._ifc = AsyncStream(reader, writer, addr, + self.async_publ_mqtt, + self.async_create_remote, + self.remote) SolarmanV5.__init__(self, server_side, client_mode, self._ifc) - self.remote_stream: 'ConnectionG3P' = remote_stream + self.conn_no = self._ifc.get_conn_no() + self.addr = addr ''' Our puplic methods ''' def close(self): - AsyncStream.close(self) + self._ifc.close() SolarmanV5.close(self) # logger.info(f'AsyncStream refs: {gc.get_referrers(self)}') @@ -41,10 +44,4 @@ class ConnectionG3P(AsyncStream, SolarmanV5): def healthy(self) -> bool: logger.debug('ConnectionG3P healthy()') - return AsyncStream.healthy(self) - - ''' - Our private methods - ''' - def __del__(self): - super().__del__() + return self._ifc.healthy() diff --git a/app/src/gen3plus/inverter_g3p.py b/app/src/gen3plus/inverter_g3p.py index d9bf0f2..6be2f8c 100644 --- a/app/src/gen3plus/inverter_g3p.py +++ b/app/src/gen3plus/inverter_g3p.py @@ -55,6 +55,7 @@ class InverterG3P(Inverter, ConnectionG3P): super().__init__(reader, writer, addr, None, server_side=True, client_mode=client_mode) self.__ha_restarts = -1 + self.addr = addr async def async_create_remote(self) -> None: '''Establish a client connection to the TSUN cloud''' @@ -67,13 +68,13 @@ class InverterG3P(Inverter, ConnectionG3P): logging.info(f'[{self.node_id}] Connect to {addr}') connect = asyncio.open_connection(host, port) reader, writer = await connect - self.remote_stream = ConnectionG3P(reader, writer, addr, self, + self.remote.stream = ConnectionG3P(reader, writer, addr, self, server_side=False, client_mode=False) - logging.info(f'[{self.remote_stream.node_id}:' - f'{self.remote_stream.conn_no}] ' + logging.info(f'[{self.remote.stream.node_id}:' + f'{self.remote.stream.conn_no}] ' f'Connected to {addr}') - asyncio.create_task(self.client_loop(addr)) + asyncio.create_task(self._ifc.client_loop(addr)) except (ConnectionRefusedError, TimeoutError) as error: logging.info(f'{error}') @@ -131,10 +132,6 @@ class InverterG3P(Inverter, ConnectionG3P): self.db.reg_clr_at_midnight(f'{self.entity_prfx}{self.node_id}') def close(self) -> None: - logging.debug(f'InverterG3P.close() l{self.l_addr} | r{self.r_addr}') + logging.debug(f'InverterG3P.close() {self.addr}') super().close() # call close handler in the parent class # logger.debug (f'Inverter refs: {gc.get_referrers(self)}') - - def __del__(self): - logging.debug("InverterG3P.__del__") - super().__del__() diff --git a/app/src/gen3plus/solarman_v5.py b/app/src/gen3plus/solarman_v5.py index 2129fae..7f0caf2 100644 --- a/app/src/gen3plus/solarman_v5.py +++ b/app/src/gen3plus/solarman_v5.py @@ -64,7 +64,8 @@ class SolarmanV5(Message): def __init__(self, server_side: bool, client_mode: bool, ifc: "AsyncIfc"): super().__init__(server_side, self.send_modbus_cb, mb_timeout=8) - ifc.read.reg_trigger(self.read) + ifc.rx_set_cb(self.read) + ifc.prot_set_timeout_cb(self._timeout) self.ifc = ifc self.header_len = 11 # overwrite construcor in class Message self.control = 0 @@ -163,7 +164,8 @@ class SolarmanV5(Message): self.log_lvl.clear() self.state = State.closed self.mb_timer.close() - self.ifc.read.reg_trigger(None) + self.ifc.rx_set_cb(None) + self.ifc.prot_set_timeout_cb(None) super().close() async def send_start_cmd(self, snr: int, host: str, @@ -234,10 +236,10 @@ class SolarmanV5(Message): self._read() while True: if not self.header_valid: - self.__parse_header(self.ifc.read.peek(), - len(self.ifc.read)) + self.__parse_header(self.ifc.rx_peek(), + self.ifc.rx_len()) - if self.header_valid and len(self.ifc.read) >= \ + if self.header_valid and self.ifc.rx_len() >= \ (self.header_len + self.data_len+2): self.__process_complete_received_msg() self.__flush_recv_msg() @@ -248,10 +250,10 @@ class SolarmanV5(Message): log_lvl = self.log_lvl.get(self.control, logging.WARNING) if callable(log_lvl): log_lvl = log_lvl() - self.ifc.read.logging(log_lvl, f'Received from {self.addr}:') + self.ifc.rx_log(log_lvl, f'Received from {self.addr}:') # self._recv_buffer, self.header_len + # self.data_len+2) - if self.__trailer_is_ok(self.ifc.read.peek(), self.header_len + if self.__trailer_is_ok(self.ifc.rx_peek(), self.header_len + self.data_len + 2): if self.state == State.init: self.state = State.received @@ -264,8 +266,8 @@ class SolarmanV5(Message): return tsun = Config.get('solarman') if tsun['enabled']: - self.ifc.forward += buffer[:buflen] - self.ifc.forward.logging(logging.DEBUG, 'Store for forwarding:') + self.ifc.fwd_add(buffer[:buflen]) + self.ifc.fwd_log(logging.DEBUG, 'Store for forwarding:') fnc = self.switch.get(self.control, self.msg_unknown) logger.info(self.__flow_str(self.server_side, 'forwrd') + @@ -321,7 +323,7 @@ class SolarmanV5(Message): self.inc_counter('Invalid_Msg_Format') # erase broken recv buffer - self.ifc.read.clear() + self.ifc.rx_clear() return self.header_valid = True @@ -333,11 +335,11 @@ class SolarmanV5(Message): 'Drop packet w invalid stop byte from ' f'{self.addr}:', buf, buf_len) self.inc_counter('Invalid_Msg_Format') - if len(self.ifc.read) > (self.data_len+13): + if self.ifc.rx_len() > (self.data_len+13): next_start = buf[self.data_len+13] if next_start != 0xa5: # erase broken recv buffer - self.ifc.read.clear() + self.ifc.rx_clear() return False @@ -353,22 +355,22 @@ class SolarmanV5(Message): def __build_header(self, ctrl) -> None: '''build header for new transmit message''' - self.send_msg_ofs = len(self.ifc.write) + self.send_msg_ofs = self.ifc.tx_len() - self.ifc.write += struct.pack( - ' None: '''finish the transmit message, set lenght and checksum''' - _len = len(self.ifc.write) - self.send_msg_ofs - struct.pack_into(' None: - self.ifc.read.get(self.header_len + self.data_len+2) + self.ifc.rx_get(self.header_len + self.data_len+2) self.header_valid = False def __send_ack_rsp(self, msgtype, ftype, ack=1): self.__build_header(msgtype) - self.ifc.write += struct.pack(' None: if self.state != State.up: @@ -462,18 +464,18 @@ class SolarmanV5(Message): self.forward_at_cmd_resp = False self.__build_header(0x4510) - self.ifc.write += struct.pack(f'> 8 - for key, update in self.db.parse(self.ifc.read.peek(), msg_type, ftype, + for key, update in self.db.parse(self.ifc.rx_peek(), msg_type, ftype, self.node_id): if update: if key == 'inverter': @@ -513,7 +515,7 @@ class SolarmanV5(Message): self.__forward_msg() def msg_dev_ind(self): - data = self.ifc.read.peek()[self.header_len:] + data = self.ifc.rx_peek()[self.header_len:] result = struct.unpack_from(self.HDR_FMT, data, 0) ftype = result[0] # always 2 total = result[1] @@ -534,7 +536,7 @@ class SolarmanV5(Message): self.__send_ack_rsp(0x1110, ftype) def msg_data_ind(self): - data = self.ifc.read.peek() + data = self.ifc.rx_peek() result = struct.unpack_from(' int: - ftype = self.ifc.read.peek()[self.header_len] + ftype = self.ifc.rx_peek()[self.header_len] if ftype == self.AT_CMD: if self.forward_at_cmd_resp: return logging.INFO @@ -616,8 +618,8 @@ class SolarmanV5(Message): return logging.WARNING def msg_command_rsp(self): - data = self.ifc.read.peek()[self.header_len: - self.header_len+self.data_len] + data = self.ifc.rx_peek()[self.header_len: + self.header_len+self.data_len] ftype = data[0] if ftype == self.AT_CMD: if not self.forward_at_cmd_resp: @@ -653,7 +655,7 @@ class SolarmanV5(Message): self.__build_model_name() def msg_hbeat_ind(self): - data = self.ifc.read.peek()[self.header_len:] + data = self.ifc.rx_peek()[self.header_len:] result = struct.unpack_from(' None: '''dec proxy statistic counter''' db_dict = cls.stat['proxy'] db_dict[counter] -= 1 + cls.new_stat_data['proxy'] = True def ha_proxy_confs(self, ha_prfx: str, node_id: str, snr: str) \ -> Generator[tuple[str, str, str, str], None, None]: diff --git a/app/src/messages.py b/app/src/messages.py index eef1dff..3a6f0e2 100644 --- a/app/src/messages.py +++ b/app/src/messages.py @@ -90,6 +90,12 @@ class State(Enum): class Message(metaclass=IterRegistry): _registry = [] + MAX_START_TIME = 400 + '''maximum time without a received msg in sec''' + MAX_INV_IDLE_TIME = 120 + '''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''' def __init__(self, server_side: bool, send_modbus_cb: Callable[[bytes, int, str], None], mb_timeout: int): @@ -105,12 +111,21 @@ class Message(metaclass=IterRegistry): self.header_len = 0 self.data_len = 0 self.unique_id = 0 - self.node_id = '' # will be overwritten in the child class's __init__ + self._node_id = '' self.sug_area = '' self.new_data = {} self.state = State.init self.shutdown_started = False + @property + def node_id(self): + return self._node_id + + @node_id.setter + def node_id(self, value): + self._node_id = value + self.ifc.set_node_id(value) + ''' Empty methods, that have to be implemented in any child class which don't use asyncio @@ -138,6 +153,16 @@ class Message(metaclass=IterRegistry): # logger.info(f'update: key: {key} ts:{tstr}' self.db.set_db_def_value(info_id, round(ts)) + def _timeout(self) -> int: + if self.state == State.init or self.state == State.received: + to = self.MAX_START_TIME + elif self.state == State.up and \ + self.server_side and self.modbus_polling: + to = self.MAX_INV_IDLE_TIME + else: + to = self.MAX_DEF_IDLE_TIME + return to + ''' Our puplic methods ''' diff --git a/app/src/modbus_tcp.py b/app/src/modbus_tcp.py index 429710b..ab7057f 100644 --- a/app/src/modbus_tcp.py +++ b/app/src/modbus_tcp.py @@ -5,9 +5,11 @@ import asyncio if __name__ == "app.src.modbus_tcp": from app.src.config import Config from app.src.gen3plus.inverter_g3p import InverterG3P + from app.src.infos import Infos else: # pragma: no cover from config import Config from gen3plus.inverter_g3p import InverterG3P + from infos import Infos logger = logging.getLogger('conn') @@ -27,13 +29,14 @@ class ModbusConn(): client_mode=True) logging.info(f'[{self.stream.node_id}:{self.stream.conn_no}] ' f'Connected to {self.addr}') - self.stream.inc_counter('Inverter_Cnt') - await self.stream.publish_outstanding_mqtt() + Infos.inc_counter('Inverter_Cnt') + await self.stream._ifc.publish_outstanding_mqtt() return self.stream async def __aexit__(self, exc_type, exc, tb): - self.stream.dec_counter('Inverter_Cnt') - await self.stream.publish_outstanding_mqtt() + Infos.dec_counter('Inverter_Cnt') + await self.stream._ifc.publish_outstanding_mqtt() + self.stream.close() class ModbusTcp(): @@ -60,7 +63,7 @@ class ModbusTcp(): try: async with ModbusConn(host, port) as stream: await stream.send_start_cmd(snr, host) - await stream.loop() + await stream._ifc.loop() logger.info(f'[{stream.node_id}:{stream.conn_no}] ' f'Connection closed - Shutdown: ' f'{stream.shutdown_started}') diff --git a/app/src/server.py b/app/src/server.py index 95cc715..f1648ba 100644 --- a/app/src/server.py +++ b/app/src/server.py @@ -74,14 +74,14 @@ async def handle_client(reader: StreamReader, writer: StreamWriter): '''Handles a new incoming connection and starts an async loop''' addr = writer.get_extra_info('peername') - await InverterG3(reader, writer, addr).server_loop(addr) + await InverterG3(reader, writer, addr)._ifc.server_loop(addr) async def handle_client_v2(reader: StreamReader, writer: StreamWriter): '''Handles a new incoming connection and starts an async loop''' addr = writer.get_extra_info('peername') - await InverterG3P(reader, writer, addr).server_loop(addr) + await InverterG3P(reader, writer, addr)._ifc.server_loop(addr) async def handle_shutdown(web_task): diff --git a/app/tests/test_connection_g3.py b/app/tests/test_connection_g3.py index bb2b025..b7282e7 100644 --- a/app/tests/test_connection_g3.py +++ b/app/tests/test_connection_g3.py @@ -2,8 +2,9 @@ import pytest import asyncio +from itertools import count from mock import patch -from app.src.async_stream import AsyncStream +from app.src.async_stream import AsyncStream, AsyncIfcImpl from app.src.gen3.connection_g3 import ConnectionG3 from app.src.gen3.talent import Talent @@ -60,19 +61,19 @@ class FakeWriter(): -def test_method_calls(patch_async_init, patch_talent_init, patch_healthy, patch_async_close, patch_talent_close): - spy1 = patch_async_init +def test_method_calls(patch_talent_init, patch_healthy, patch_async_close, patch_talent_close): + AsyncIfcImpl._ids = count(5) spy2 = patch_talent_init spy3 = patch_healthy spy4 = patch_async_close spy5 = patch_talent_close reader = FakeReader() - writer = FakeWriter() + writer = FakeWriter() id_str = "id_string" addr = ('proxy.local', 10000) conn = ConnectionG3(reader, writer, addr, - remote_stream= None, server_side=True, id_str=id_str) - spy1.assert_called_once_with(conn, reader, writer, addr, conn._ifc) + rstream= None, server_side=True, id_str=id_str) + assert 5 == conn._ifc.get_conn_no() spy2.assert_called_once_with(conn, True, conn._ifc, id_str) conn.healthy() @@ -81,4 +82,3 @@ def test_method_calls(patch_async_init, patch_talent_init, patch_healthy, patch_ conn.close() spy4.assert_called_once() spy5.assert_called_once() - diff --git a/app/tests/test_connection_g3p.py b/app/tests/test_connection_g3p.py index 0a56398..60aac30 100644 --- a/app/tests/test_connection_g3p.py +++ b/app/tests/test_connection_g3p.py @@ -2,15 +2,16 @@ import pytest import asyncio +from itertools import count from mock import patch from app.src.singleton import Singleton -from app.src.async_stream import AsyncStream +from app.src.async_stream import AsyncStream, AsyncIfcImpl from app.src.gen3plus.connection_g3p import ConnectionG3P from app.src.gen3plus.solarman_v5 import SolarmanV5 @pytest.fixture def patch_async_init(): - with patch.object(AsyncStream, '__init__') as conn: + with patch.object(AsyncStream, '__init__', return_value= None) as conn: yield conn @pytest.fixture @@ -66,18 +67,18 @@ class FakeWriter(): -def test_method_calls(patch_async_init, patch_solarman_init, patch_healthy, patch_async_close, patch_solarman_close): - spy1 = patch_async_init +def test_method_calls(patch_solarman_init, patch_healthy, patch_async_close, patch_solarman_close): + AsyncIfcImpl._ids = count(5) spy2 = patch_solarman_init spy3 = patch_healthy spy4 = patch_async_close spy5 = patch_solarman_close reader = FakeReader() - writer = FakeWriter() + writer = FakeWriter() addr = ('proxy.local', 10000) conn = ConnectionG3P(reader, writer, addr, - remote_stream= None, server_side=True, client_mode=False) - spy1.assert_called_once_with(conn, reader, writer, addr, conn._ifc) + rstream= None, server_side=True, client_mode=False) + assert 5 == conn._ifc.get_conn_no() spy2.assert_called_once_with(conn, True, False, conn._ifc) conn.healthy() diff --git a/app/tests/test_inverter.py b/app/tests/test_inverter.py index 40b23bf..c59ff38 100644 --- a/app/tests/test_inverter.py +++ b/app/tests/test_inverter.py @@ -18,7 +18,7 @@ pytest_plugins = ('pytest_asyncio',) @pytest.fixture(scope="module", autouse=True) def module_init(): def new_init(cls, cb_mqtt_is_up): - cb_mqtt_is_up() + pass # empty test methos Singleton._instances.clear() with patch.object(Mqtt, '__init__', new_init): @@ -69,6 +69,7 @@ async def test_inverter_cb(config_conn): assert 'homeassistant/' == Inverter.discovery_prfx assert 'tsun/' == Inverter.entity_prfx assert 'test_1/' == Inverter.proxy_node_id + await Inverter._cb_mqtt_is_up() spy.assert_called_once() @pytest.mark.asyncio diff --git a/app/tests/test_inverter_g3.py b/app/tests/test_inverter_g3.py index 017e897..e87aeb9 100644 --- a/app/tests/test_inverter_g3.py +++ b/app/tests/test_inverter_g3.py @@ -132,7 +132,7 @@ async def test_remote_conn(config_conn, patch_open_connection, patch_conn_close) await inverter.async_create_remote() await asyncio.sleep(0) - assert inverter.remote_stream + assert inverter.remote.stream inverter.close() spy1.assert_called_once() @@ -151,12 +151,12 @@ async def test_remote_except(config_conn, patch_open_connection, patch_conn_clos await inverter.async_create_remote() await asyncio.sleep(0) - assert inverter.remote_stream==None + assert inverter.remote.stream==None test = TestType.RD_TEST_EXCEPT await inverter.async_create_remote() await asyncio.sleep(0) - assert inverter.remote_stream==None + assert inverter.remote.stream==None inverter.close() spy1.assert_called_once() diff --git a/app/tests/test_inverter_g3p.py b/app/tests/test_inverter_g3p.py index 07d1160..1dd344c 100644 --- a/app/tests/test_inverter_g3p.py +++ b/app/tests/test_inverter_g3p.py @@ -133,7 +133,7 @@ async def test_remote_conn(config_conn, patch_open_connection, patch_conn_close) await inverter.async_create_remote() await asyncio.sleep(0) - assert inverter.remote_stream + assert inverter.remote.stream inverter.close() spy1.assert_called_once() @@ -152,12 +152,12 @@ async def test_remote_except(config_conn, patch_open_connection, patch_conn_clos await inverter.async_create_remote() await asyncio.sleep(0) - assert inverter.remote_stream==None + assert inverter.remote.stream==None test = TestType.RD_TEST_EXCEPT await inverter.async_create_remote() await asyncio.sleep(0) - assert inverter.remote_stream==None + assert inverter.remote.stream==None inverter.close() spy1.assert_called_once() diff --git a/app/tests/test_modbus_tcp.py b/app/tests/test_modbus_tcp.py index 0fa7438..3ef8c9e 100644 --- a/app/tests/test_modbus_tcp.py +++ b/app/tests/test_modbus_tcp.py @@ -157,8 +157,8 @@ async def test_modbus_conn(patch_open): async with ModbusConn('test.local', 1234) as stream: assert stream.node_id == 'G3P' assert stream.addr == ('test.local', 1234) - assert type(stream._reader) is FakeReader - assert type(stream._writer) is FakeWriter + assert type(stream._ifc._reader) is FakeReader + assert type(stream._ifc._writer) is FakeWriter assert Infos.stat['proxy']['Inverter_Cnt'] == 1 assert Infos.stat['proxy']['Inverter_Cnt'] == 0 @@ -209,7 +209,7 @@ async def test_modbus_cnf2(config_conn, patch_no_mqtt, patch_open): test += 1 assert Infos.stat['proxy']['Inverter_Cnt'] == 1 m.shutdown_started = True - m._reader.on_recv.set() + m._ifc._reader.on_recv.set() del m assert 1 == test @@ -236,13 +236,13 @@ async def test_modbus_cnf3(config_conn, patch_no_mqtt, patch_open): test += 1 if test == 1: m.shutdown_started = False - m._reader.on_recv.set() + m.ifc._reader.on_recv.set() await asyncio.sleep(0.1) assert m.state == State.closed await asyncio.sleep(0.1) else: m.shutdown_started = True - m._reader.on_recv.set() + m.ifc._reader.on_recv.set() del m assert 2 == test @@ -269,13 +269,14 @@ async def test_mqtt_err(config_conn, patch_mqtt_err, patch_open): test += 1 if test == 1: m.shutdown_started = False - m._reader.on_recv.set() + m._ifc._reader.on_recv.set() await asyncio.sleep(0.1) assert m.state == State.closed await asyncio.sleep(0.1) + await asyncio.sleep(0.1) else: m.shutdown_started = True - m._reader.on_recv.set() + m._ifc._reader.on_recv.set() del m await asyncio.sleep(0.01) @@ -301,13 +302,13 @@ async def test_mqtt_except(config_conn, patch_mqtt_except, patch_open): test += 1 if test == 1: m.shutdown_started = False - m._reader.on_recv.set() + m._ifc._reader.on_recv.set() await asyncio.sleep(0.1) assert m.state == State.closed await asyncio.sleep(0.1) else: m.shutdown_started = True - m._reader.on_recv.set() + m._ifc._reader.on_recv.set() del m await asyncio.sleep(0.01) diff --git a/app/tests/test_mqtt.py b/app/tests/test_mqtt.py index eda7d5e..5ce2ea5 100644 --- a/app/tests/test_mqtt.py +++ b/app/tests/test_mqtt.py @@ -5,7 +5,7 @@ import aiomqtt import logging from mock import patch, Mock -from app.src.async_ifc import AsyncIfc +from app.src.async_stream import AsyncIfcImpl from app.src.singleton import Singleton from app.src.mqtt import Mqtt from app.src.modbus import Modbus @@ -45,8 +45,7 @@ def config_no_conn(test_port): @pytest.fixture def spy_at_cmd(): - ifc = AsyncIfc() - conn = SolarmanV5(server_side=True, client_mode= False, ifc=ifc) + conn = SolarmanV5(server_side=True, client_mode= False, ifc=AsyncIfcImpl()) conn.node_id = 'inv_2/' with patch.object(conn, 'send_at_cmd', wraps=conn.send_at_cmd) as wrapped_conn: yield wrapped_conn @@ -54,8 +53,7 @@ def spy_at_cmd(): @pytest.fixture def spy_modbus_cmd(): - ifc = AsyncIfc() - conn = SolarmanV5(server_side=True, client_mode= False, ifc=ifc) + conn = SolarmanV5(server_side=True, client_mode= False, ifc=AsyncIfcImpl()) conn.node_id = 'inv_1/' with patch.object(conn, 'send_modbus_cmd', wraps=conn.send_modbus_cmd) as wrapped_conn: yield wrapped_conn @@ -63,8 +61,7 @@ def spy_modbus_cmd(): @pytest.fixture def spy_modbus_cmd_client(): - ifc = AsyncIfc() - conn = SolarmanV5(server_side=False, client_mode= False, ifc=ifc) + conn = SolarmanV5(server_side=False, client_mode= False, ifc=AsyncIfcImpl()) conn.node_id = 'inv_1/' with patch.object(conn, 'send_modbus_cmd', wraps=conn.send_modbus_cmd) as wrapped_conn: yield wrapped_conn diff --git a/app/tests/test_solarman.py b/app/tests/test_solarman.py index 68a3d23..41a8101 100644 --- a/app/tests/test_solarman.py +++ b/app/tests/test_solarman.py @@ -5,7 +5,7 @@ import asyncio import logging import random from math import isclose -from app.src.async_ifc import AsyncIfc +from app.src.async_stream import AsyncIfcImpl, StreamPtr from app.src.gen3plus.solarman_v5 import SolarmanV5 from app.src.config import Config from app.src.infos import Infos, Register @@ -34,14 +34,15 @@ class Mqtt(): class MemoryStream(SolarmanV5): def __init__(self, msg, chunks = (0,), server_side: bool = True): - _ifc = AsyncIfc() + _ifc = AsyncIfcImpl() super().__init__(server_side, client_mode=False, ifc=_ifc) if server_side: self.mb.timeout = 0.4 # overwrite for faster testing + self.remote = StreamPtr(None) self.mb_first_timeout = 0.5 self.mb_timeout = 0.5 self.sent_pdu = b'' - self.ifc.write.reg_trigger(self.write_cb) + self.ifc.tx_fifo.reg_trigger(self.write_cb) self.mqtt = Mqtt() self.__msg = msg self.__msg_len = len(msg) @@ -63,7 +64,7 @@ class MemoryStream(SolarmanV5): def write_cb(self): if self.test_exception_async_write: raise RuntimeError("Peer closed.") - self.sent_pdu = self.ifc.write.get() + self.sent_pdu = self.ifc.tx_fifo.get() def _timestamp(self): return timestamp @@ -87,11 +88,11 @@ class MemoryStream(SolarmanV5): chunk_len = self.__chunks[self.__chunk_idx] self.__chunk_idx += 1 if chunk_len!=0: - self.ifc.read += self.__msg[self.__offs:chunk_len] + self.ifc.rx_fifo += self.__msg[self.__offs:chunk_len] copied_bytes = chunk_len - self.__offs self.__offs = chunk_len else: - self.ifc.read += self.__msg[self.__offs:] + self.ifc.rx_fifo += self.__msg[self.__offs:] copied_bytes = self.__msg_len - self.__offs self.__offs = self.__msg_len except Exception: @@ -100,8 +101,8 @@ class MemoryStream(SolarmanV5): def createClientStream(self, msg, chunks = (0,)): c = MemoryStream(msg, chunks, False) - self.remote_stream = c - c. remote_stream = self + self.remote.stream = c + c. remote.stream = self return c def _SolarmanV5__flush_recv_msg(self) -> None: @@ -687,9 +688,9 @@ def test_read_message(device_ind_msg): assert m.control == 0x4110 assert str(m.seq) == '01:00' assert m.data_len == 0xd4 - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -708,9 +709,9 @@ def test_invalid_start_byte(invalid_start_byte, device_ind_msg): assert m.control == 0x4110 assert str(m.seq) == '01:00' assert m.data_len == 0xd4 - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 m.close() @@ -728,9 +729,9 @@ def test_invalid_stop_byte(invalid_stop_byte): assert m.control == 0x4110 assert str(m.seq) == '01:00' assert m.data_len == 0xd4 - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 m.close() @@ -753,9 +754,9 @@ def test_invalid_stop_byte2(invalid_stop_byte, device_ind_msg): assert m.msg_recvd[1]['data_len']==0xd4 assert m.unique_id == None - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 m.close() @@ -775,9 +776,9 @@ def test_invalid_stop_start_byte(invalid_stop_byte, invalid_start_byte): assert m.control == 0x4110 assert str(m.seq) == '01:00' assert m.data_len == 0xd4 - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 m.close() @@ -799,9 +800,9 @@ def test_invalid_checksum(invalid_checksum, device_ind_msg): assert m.msg_recvd[1]['control']==0x4110 assert m.msg_recvd[1]['seq']=='01:00' assert m.msg_recvd[1]['data_len']==0xd4 - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1 m.close() @@ -821,8 +822,8 @@ def test_read_message_twice(config_no_tsun_inv1, device_ind_msg, device_rsp_msg) assert m.msg_recvd[1]['control']==0x4110 assert m.msg_recvd[1]['seq']=='01:01' assert m.msg_recvd[1]['data_len']==0xd4 - assert m.ifc.write.get()==device_rsp_msg+device_rsp_msg - assert m.ifc.forward.get()==b'' + assert m.ifc.tx_fifo.get()==device_rsp_msg+device_rsp_msg + assert m.ifc.fwd_fifo.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -893,11 +894,11 @@ def test_read_two_messages(config_tsun_allow_all, device_ind_msg, device_rsp_msg assert m.msg_recvd[1]['data_len']==0x199 assert '02b0' == m.db.get_db_value(Register.SENSOR_LIST, None) assert 0x02b0 == m.sensor_list - assert m.ifc.forward.get()==device_ind_msg+inverter_ind_msg - assert m.ifc.write.get()==device_rsp_msg+inverter_rsp_msg + assert m.ifc.fwd_fifo.get()==device_ind_msg+inverter_ind_msg + assert m.ifc.tx_fifo.get()==device_rsp_msg+inverter_rsp_msg m._init_new_client_conn() - assert m.ifc.write.get()==b'' + assert m.ifc.tx_fifo.get()==b'' m.close() def test_read_two_messages2(config_tsun_allow_all, inverter_ind_msg, inverter_ind_msg_81, inverter_rsp_msg, inverter_rsp_msg_81): @@ -918,11 +919,11 @@ def test_read_two_messages2(config_tsun_allow_all, inverter_ind_msg, inverter_in assert m.msg_recvd[1]['seq']=='03:03' assert m.msg_recvd[1]['data_len']==0x199 assert m.time_ofs == 0x33e447a0 - assert m.ifc.forward.get()==inverter_ind_msg+inverter_ind_msg_81 - assert m.ifc.write.get()==inverter_rsp_msg+inverter_rsp_msg_81 + assert m.ifc.fwd_fifo.get()==inverter_ind_msg+inverter_ind_msg_81 + assert m.ifc.tx_fifo.get()==inverter_rsp_msg+inverter_rsp_msg_81 m._init_new_client_conn() - assert m.ifc.write.get()==b'' + assert m.ifc.tx_fifo.get()==b'' m.close() def test_read_two_messages3(config_tsun_allow_all, device_ind_msg2, device_rsp_msg2, inverter_ind_msg, inverter_rsp_msg): @@ -947,11 +948,11 @@ def test_read_two_messages3(config_tsun_allow_all, device_ind_msg2, device_rsp_m assert m.msg_recvd[1]['data_len']==0xd4 assert '02b0' == m.db.get_db_value(Register.SENSOR_LIST, None) assert 0x02b0 == m.sensor_list - assert m.ifc.forward.get()==inverter_ind_msg+device_ind_msg2 - assert m.ifc.write.get()==inverter_rsp_msg+device_rsp_msg2 + assert m.ifc.fwd_fifo.get()==inverter_ind_msg+device_ind_msg2 + assert m.ifc.tx_fifo.get()==inverter_rsp_msg+device_rsp_msg2 m._init_new_client_conn() - assert m.ifc.write.get()==b'' + assert m.ifc.tx_fifo.get()==b'' m.close() def test_unkown_frame_code(config_tsun_inv1, inverter_ind_msg_81, inverter_rsp_msg_81): @@ -966,9 +967,9 @@ def test_unkown_frame_code(config_tsun_inv1, inverter_ind_msg_81, inverter_rsp_m assert m.control == 0x4210 assert str(m.seq) == '03:03' assert m.data_len == 0x199 - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==inverter_rsp_msg_81 - assert m.ifc.forward.get()==inverter_ind_msg_81 + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==inverter_rsp_msg_81 + assert m.ifc.fwd_fifo.get()==inverter_ind_msg_81 assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -984,9 +985,9 @@ def test_unkown_message(config_tsun_inv1, unknown_msg): assert m.control == 0x5110 assert str(m.seq) == '84:10' assert m.data_len == 0x0a - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==unknown_msg + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==unknown_msg assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1002,9 +1003,9 @@ def test_device_rsp(config_tsun_inv1, device_rsp_msg): assert m.control == 0x1110 assert str(m.seq) == '01:01' assert m.data_len == 0x0a - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1020,9 +1021,9 @@ def test_inverter_rsp(config_tsun_inv1, inverter_rsp_msg): assert m.control == 0x1210 assert str(m.seq) == '02:02' assert m.data_len == 0x0a - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1037,9 +1038,9 @@ def test_heartbeat_ind(config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp_msg): assert m.control == 0x4710 assert str(m.seq) == '84:11' # value after sending response assert m.data_len == 0x01 - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==heartbeat_rsp_msg - assert m.ifc.forward.get()==heartbeat_ind_msg + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==heartbeat_rsp_msg + assert m.ifc.fwd_fifo.get()==heartbeat_ind_msg assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1055,9 +1056,9 @@ def test_heartbeat_ind2(config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp_msg): assert m.control == 0x4710 assert str(m.seq) == '84:11' # value after sending response assert m.data_len == 0x01 - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==heartbeat_rsp_msg - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==heartbeat_rsp_msg + assert m.ifc.fwd_fifo.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1073,9 +1074,9 @@ def test_heartbeat_rsp(config_tsun_inv1, heartbeat_rsp_msg): assert m.control == 0x1710 assert str(m.seq) == '11:84' # value after sending response assert m.data_len == 0x0a - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1090,15 +1091,15 @@ def test_sync_start_ind(config_tsun_inv1, sync_start_ind_msg, sync_start_rsp_msg assert m.control == 0x4310 assert str(m.seq) == '0d:0d' # value after sending response assert m.data_len == 47 - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==sync_start_rsp_msg - assert m.ifc.forward.peek()==sync_start_ind_msg + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==sync_start_rsp_msg + assert m.ifc.fwd_fifo.peek()==sync_start_ind_msg assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.seq.server_side = False # simulate forawding to TSUN cloud - m._update_header(m.ifc.forward.peek()) + m._update_header(m.ifc.fwd_fifo.peek()) assert str(m.seq) == '0d:0e' # value after forwarding indication - assert m.ifc.forward.get()==sync_start_fwd_msg + assert m.ifc.fwd_fifo.get()==sync_start_fwd_msg m.close() @@ -1114,9 +1115,9 @@ def test_sync_start_rsp(config_tsun_inv1, sync_start_rsp_msg): assert m.control == 0x1310 assert str(m.seq) == '0d:0d' # value after sending response assert m.data_len == 0x0a - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1131,9 +1132,9 @@ def test_sync_end_ind(config_tsun_inv1, sync_end_ind_msg, sync_end_rsp_msg): assert m.control == 0x4810 assert str(m.seq) == '07:07' # value after sending response assert m.data_len == 60 - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==sync_end_rsp_msg - assert m.ifc.forward.get()==sync_end_ind_msg + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==sync_end_rsp_msg + assert m.ifc.fwd_fifo.get()==sync_end_ind_msg assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1149,9 +1150,9 @@ def test_sync_end_rsp(config_tsun_inv1, sync_end_rsp_msg): assert m.control == 0x1810 assert str(m.seq) == '07:07' # value after sending response assert m.data_len == 0x0a - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 m.close() @@ -1169,9 +1170,9 @@ def test_build_modell_600(config_tsun_allow_all, inverter_ind_msg): assert '02b0' == m.db.get_db_value(Register.SENSOR_LIST, None) assert 0 == m.sensor_list # must not been set by an inverter data ind - m.ifc.write.clear() # clear send buffer for next test + m.ifc.tx_clear() # clear send buffer for next test m._init_new_client_conn() - assert m.ifc.write.get()==b'' + assert m.ifc.tx_fifo.get()==b'' m.close() def test_build_modell_1600(config_tsun_allow_all, inverter_ind_msg1600): @@ -1235,9 +1236,9 @@ def test_build_logger_modell(config_tsun_allow_all, device_ind_msg): def test_msg_iterator(): Message._registry.clear() - m1 = SolarmanV5(server_side=True, client_mode=False, ifc=AsyncIfc()) - m2 = SolarmanV5(server_side=True, client_mode=False, ifc=AsyncIfc()) - m3 = SolarmanV5(server_side=True, client_mode=False, ifc=AsyncIfc()) + m1 = SolarmanV5(server_side=True, client_mode=False, ifc=AsyncIfcImpl()) + m2 = SolarmanV5(server_side=True, client_mode=False, ifc=AsyncIfcImpl()) + m3 = SolarmanV5(server_side=True, client_mode=False, ifc=AsyncIfcImpl()) m3.close() del m3 test1 = 0 @@ -1255,7 +1256,7 @@ def test_msg_iterator(): assert test2 == 1 def test_proxy_counter(): - m = SolarmanV5(server_side=True, client_mode=False, ifc=AsyncIfc()) + m = SolarmanV5(server_side=True, client_mode=False, ifc=AsyncIfcImpl()) assert m.new_data == {} m.db.stat['proxy']['Unknown_Msg'] = 0 Infos.new_stat_data['proxy'] = False @@ -1279,14 +1280,14 @@ async def test_msg_build_modbus_req(config_tsun_inv1, device_ind_msg, device_rsp m.read() assert m.control == 0x4110 assert str(m.seq) == '01:01' - assert m.ifc.write.get()==device_rsp_msg - assert m.ifc.forward.get()==device_ind_msg + assert m.ifc.tx_fifo.get()==device_rsp_msg + assert m.ifc.fwd_fifo.get()==device_ind_msg await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG) assert 0 == m.send_msg_ofs - assert m.ifc.forward.get() == b'' + assert m.ifc.fwd_fifo.get() == b'' assert m.sent_pdu == b'' # modbus command must be ignore, cause connection is still not up - assert m.ifc.write.get() == b'' # modbus command must be ignore, cause connection is still not up + assert m.ifc.tx_fifo.get() == b'' # modbus command must be ignore, cause connection is still not up m.append_msg(inverter_ind_msg) m.read() @@ -1296,15 +1297,15 @@ async def test_msg_build_modbus_req(config_tsun_inv1, device_ind_msg, device_rsp assert m.msg_recvd[0]['seq']=='01:01' assert m.msg_recvd[1]['control']==0x4210 assert m.msg_recvd[1]['seq']=='02:02' - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==inverter_rsp_msg - assert m.ifc.forward.get()==inverter_ind_msg + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==inverter_rsp_msg + assert m.ifc.fwd_fifo.get()==inverter_ind_msg await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG) assert 0 == m.send_msg_ofs - assert m.ifc.forward.get() == b'' + assert m.ifc.fwd_fifo.get() == b'' assert m.sent_pdu == msg_modbus_cmd - assert m.ifc.write.get()== b'' + assert m.ifc.tx_fifo.get()== b'' m.close() @pytest.mark.asyncio @@ -1314,12 +1315,12 @@ async def test_at_cmd(config_tsun_allow_all, device_ind_msg, device_rsp_msg, inv m.read() # read device ind assert m.control == 0x4110 assert str(m.seq) == '01:01' - assert m.ifc.write.get()==device_rsp_msg - assert m.ifc.forward.get()==device_ind_msg + assert m.ifc.tx_fifo.get()==device_rsp_msg + assert m.ifc.fwd_fifo.get()==device_ind_msg await m.send_at_cmd('AT+TIME=214028,1,60,120') - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert m.sent_pdu == b'' assert str(m.seq) == '01:01' assert m.mqtt.key == '' @@ -1329,12 +1330,12 @@ async def test_at_cmd(config_tsun_allow_all, device_ind_msg, device_rsp_msg, inv m.read() # read inverter ind assert m.control == 0x4210 assert str(m.seq) == '02:02' - assert m.ifc.write.get()==inverter_rsp_msg - assert m.ifc.forward.get()==inverter_ind_msg + assert m.ifc.tx_fifo.get()==inverter_rsp_msg + assert m.ifc.fwd_fifo.get()==inverter_ind_msg await m.send_at_cmd('AT+TIME=214028,1,60,120') - assert m.ifc.forward.get() == b'' - assert m.ifc.write.get()== b'' + assert m.ifc.fwd_fifo.get() == b'' + assert m.ifc.tx_fifo.get()== b'' assert m.sent_pdu == at_command_ind_msg m.sent_pdu = bytearray() @@ -1346,9 +1347,9 @@ async def test_at_cmd(config_tsun_allow_all, device_ind_msg, device_rsp_msg, inv m.read() # read at resp assert m.control == 0x1510 assert str(m.seq) == '03:03' - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert m.key == 'at_resp' assert m.data == "+ok" @@ -1356,9 +1357,9 @@ async def test_at_cmd(config_tsun_allow_all, device_ind_msg, device_rsp_msg, inv m.test_exception_async_write = True await m.send_at_cmd('AT+TIME=214028,1,60,120') assert m.sent_pdu == b'' - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert m.sent_pdu == b'' assert str(m.seq) == '03:04' assert m.forward_at_cmd_resp == False @@ -1373,12 +1374,12 @@ async def test_at_cmd_blocked(config_tsun_allow_all, device_ind_msg, device_rsp_ m.read() assert m.control == 0x4110 assert str(m.seq) == '01:01' - assert m.ifc.write.get()==device_rsp_msg - assert m.ifc.forward.get()==device_ind_msg + assert m.ifc.tx_fifo.get()==device_rsp_msg + assert m.ifc.fwd_fifo.get()==device_ind_msg await m.send_at_cmd('AT+WEBU') - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert str(m.seq) == '01:01' assert m.mqtt.key == '' assert m.mqtt.data == "" @@ -1387,14 +1388,14 @@ async def test_at_cmd_blocked(config_tsun_allow_all, device_ind_msg, device_rsp_ m.read() assert m.control == 0x4210 assert str(m.seq) == '02:02' - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==inverter_rsp_msg - assert m.ifc.forward.get()==inverter_ind_msg + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==inverter_rsp_msg + assert m.ifc.fwd_fifo.get()==inverter_ind_msg await m.send_at_cmd('AT+WEBU') - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert str(m.seq) == '02:02' assert m.forward_at_cmd_resp == False assert m.mqtt.key == 'at_resp' @@ -1416,9 +1417,9 @@ def test_at_cmd_ind(config_tsun_inv1, at_command_ind_msg): assert m.control == 0x4510 assert str(m.seq) == '03:02' assert m.data_len == 39 - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==at_command_ind_msg + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==at_command_ind_msg assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['AT_Command'] == 1 assert m.db.stat['proxy']['AT_Command_Blocked'] == 0 @@ -1440,9 +1441,9 @@ def test_at_cmd_ind_block(config_tsun_inv1, at_command_ind_msg_block): assert m.control == 0x4510 assert str(m.seq) == '03:02' assert m.data_len == 23 - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.db.stat['proxy']['AT_Command'] == 0 assert m.db.stat['proxy']['AT_Command_Blocked'] == 1 @@ -1462,8 +1463,8 @@ def test_msg_at_command_rsp1(config_tsun_inv1, at_command_rsp_msg): assert str(m.seq) == '03:03' assert m.header_len==11 assert m.data_len==17 - assert m.ifc.forward.get()==at_command_rsp_msg - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==at_command_rsp_msg + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 m.close() @@ -1481,8 +1482,8 @@ def test_msg_at_command_rsp2(config_tsun_inv1, at_command_rsp_msg): assert str(m.seq) == '03:03' assert m.header_len==11 assert m.data_len==17 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 m.close() @@ -1506,8 +1507,8 @@ def test_msg_modbus_req(config_tsun_inv1, msg_modbus_cmd, msg_modbus_cmd_fwd): assert str(c.seq) == '03:02' assert c.header_len==11 assert c.data_len==23 - assert c.ifc.forward.get()==b'' - assert c.ifc.write.get()==b'' + assert c.ifc.fwd_fifo.get()==b'' + assert c.ifc.tx_fifo.get()==b'' assert m.sent_pdu == msg_modbus_cmd_fwd assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['AT_Command'] == 0 @@ -1533,8 +1534,8 @@ def test_msg_modbus_req2(config_tsun_inv1, msg_modbus_cmd_crc_err): assert str(c.seq) == '03:02' assert c.header_len==11 assert c.data_len==23 - assert c.ifc.forward.get()==b'' - assert c.ifc.write.get()==b'' + assert c.ifc.fwd_fifo.get()==b'' + assert c.ifc.tx_fifo.get()==b'' assert m.sent_pdu==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['AT_Command'] == 0 @@ -1556,8 +1557,8 @@ def test_msg_unknown_cmd_req(config_tsun_inv1, msg_unknown_cmd): assert str(m.seq) == '03:02' assert m.header_len==11 assert m.data_len==23 - assert m.ifc.forward.get()==msg_unknown_cmd - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_unknown_cmd + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['AT_Command'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 @@ -1577,8 +1578,8 @@ def test_msg_modbus_rsp1(config_tsun_inv1, msg_modbus_rsp): assert str(m.seq) == '03:03' assert m.header_len==11 assert m.data_len==59 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 m.close() @@ -1601,8 +1602,8 @@ def test_msg_modbus_rsp2(config_tsun_inv1, msg_modbus_rsp): assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.mb.err == 0 assert m.msg_count == 1 - assert m.ifc.forward.get()==msg_modbus_rsp - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_modbus_rsp + assert m.ifc.tx_fifo.get()==b'' assert m.db.get_db_value(Register.VERSION) == 'V4.0.10' assert m.new_data['inverter'] == True m.new_data['inverter'] = False @@ -1613,8 +1614,8 @@ def test_msg_modbus_rsp2(config_tsun_inv1, msg_modbus_rsp): assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.mb.err == 0 assert m.msg_count == 2 - assert m.ifc.forward.get()==msg_modbus_rsp - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_modbus_rsp + assert m.ifc.tx_fifo.get()==b'' assert m.db.get_db_value(Register.VERSION) == 'V4.0.10' assert m.new_data['inverter'] == False @@ -1638,8 +1639,8 @@ def test_msg_modbus_rsp3(config_tsun_inv1, msg_modbus_rsp): assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.mb.err == 0 assert m.msg_count == 1 - assert m.ifc.forward.get()==msg_modbus_rsp - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_modbus_rsp + assert m.ifc.tx_fifo.get()==b'' assert m.db.get_db_value(Register.VERSION) == 'V4.0.10' assert m.new_data['inverter'] == True m.new_data['inverter'] = False @@ -1649,8 +1650,8 @@ def test_msg_modbus_rsp3(config_tsun_inv1, msg_modbus_rsp): assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.mb.err == 5 assert m.msg_count == 2 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.db.get_db_value(Register.VERSION) == 'V4.0.10' assert m.new_data['inverter'] == False @@ -1668,8 +1669,8 @@ def test_msg_unknown_rsp(config_tsun_inv1, msg_unknown_cmd_rsp): assert str(m.seq) == '03:03' assert m.header_len==11 assert m.data_len==59 - assert m.ifc.forward.get()==msg_unknown_cmd_rsp - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_unknown_cmd_rsp + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 m.close() @@ -1682,8 +1683,8 @@ def test_msg_modbus_invalid(config_tsun_inv1, msg_modbus_invalid): m.read() # read complete msg, and dispatch msg assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.msg_count == 1 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 m.close() @@ -1705,8 +1706,8 @@ def test_msg_modbus_fragment(config_tsun_inv1, msg_modbus_rsp): m.read() # read complete msg, and dispatch msg assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.msg_count == 1 - assert m.ifc.forward.get()==msg_modbus_rsp - assert m.ifc.write.get()== b'' + assert m.ifc.fwd_fifo.get()==msg_modbus_rsp + assert m.ifc.tx_fifo.get()== b'' assert m.mb.err == 0 assert m.modbus_elms == 20-1 # register 0x300d is unknown, so one value can't be mapped assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 @@ -1729,9 +1730,9 @@ async def test_modbus_polling(config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp assert m.control == 0x4710 assert str(m.seq) == '84:11' # value after sending response assert m.data_len == 0x01 - assert m.ifc.read.get()==b'' - assert m.ifc.write.get()==heartbeat_rsp_msg - assert m.ifc.forward.get()==heartbeat_ind_msg + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==heartbeat_rsp_msg + assert m.ifc.fwd_fifo.get()==heartbeat_ind_msg assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0 assert m.state == State.up @@ -1740,16 +1741,16 @@ async def test_modbus_polling(config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp await asyncio.sleep(0.5) assert m.sent_pdu==bytearray(b'\xa5\x17\x00\x10E\x12\x84!Ce{\x02\xb0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x03\x30\x00\x000J\xde\x86\x15') - assert m.ifc.write.get()==b'' + assert m.ifc.tx_fifo.get()==b'' await asyncio.sleep(0.5) assert m.sent_pdu==bytearray(b'\xa5\x17\x00\x10E\x13\x84!Ce{\x02\xb0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x03\x30\x00\x000J\xde\x87\x15') - assert m.ifc.write.get()==b'' + assert m.ifc.tx_fifo.get()==b'' m.state = State.closed m.sent_pdu = bytearray() await asyncio.sleep(0.5) assert m.sent_pdu==bytearray(b'') - assert m.ifc.write.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert next(m.mb_timer.exp_count) == 4 m.close() @@ -1771,16 +1772,29 @@ async def test_start_client_mode(config_tsun_inv1, str_test_ip): assert m.state == State.up assert m.no_forwarding == True - assert m.ifc.write.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert isclose(m.mb_timeout, 0.5) assert next(m.mb_timer.exp_count) == 0 await asyncio.sleep(0.5) assert m.sent_pdu==bytearray(b'\xa5\x17\x00\x10E\x02\x00!Ce{\x02\xb0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x030\x00\x000J\xde\xf2\x15') - assert m.ifc.write.get()==b'' + assert m.ifc.tx_fifo.get()==b'' await asyncio.sleep(0.5) assert m.sent_pdu==bytearray(b'\xa5\x17\x00\x10E\x03\x00!Ce{\x02\xb0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x030\x00\x000J\xde\xf3\x15') - assert m.ifc.write.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert next(m.mb_timer.exp_count) == 3 m.close() + +def test_timeout(config_tsun_inv1): + _ = config_tsun_inv1 + m = MemoryStream(b'') + assert m.state == State.init + assert SolarmanV5.MAX_START_TIME == m._timeout() + m.state = State.up + m.modbus_polling = True + assert SolarmanV5.MAX_INV_IDLE_TIME == m._timeout() + m.modbus_polling = False + assert SolarmanV5.MAX_DEF_IDLE_TIME == m._timeout() + m.state = State.closed + m.close() diff --git a/app/tests/test_talent.py b/app/tests/test_talent.py index 4bea5db..7ef01b1 100644 --- a/app/tests/test_talent.py +++ b/app/tests/test_talent.py @@ -1,7 +1,7 @@ # test_with_pytest.py import pytest, logging, asyncio from math import isclose -from app.src.async_ifc import AsyncIfc +from app.src.async_stream import AsyncIfcImpl, StreamPtr from app.src.gen3.talent import Talent, Control from app.src.config import Config from app.src.infos import Infos, Register @@ -19,13 +19,15 @@ tracer = logging.getLogger('tracer') class MemoryStream(Talent): def __init__(self, msg, chunks = (0,), server_side: bool = True): - super().__init__(server_side, AsyncIfc()) + self.ifc = AsyncIfcImpl() + super().__init__(server_side, self.ifc) if server_side: self.mb.timeout = 0.4 # overwrite for faster testing + self.remote = StreamPtr(None) self.mb_first_timeout = 0.5 self.mb_timeout = 0.5 self.sent_pdu = b'' - self.ifc.write.reg_trigger(self.write_cb) + self.ifc.tx_fifo.reg_trigger(self.write_cb) self.__msg = msg self.__msg_len = len(msg) self.__chunks = chunks @@ -35,10 +37,10 @@ class MemoryStream(Talent): self.addr = 'Test: SrvSide' self.send_msg_ofs = 0 self.msg_recvd = [] - self.remote_stream = None + self.remote.stream = None def write_cb(self): - self.sent_pdu = self.ifc.write.get() + self.sent_pdu = self.ifc.tx_fifo.get() def append_msg(self, msg): @@ -52,11 +54,11 @@ class MemoryStream(Talent): chunk_len = self.__chunks[self.__chunk_idx] self.__chunk_idx += 1 if chunk_len!=0: - self.ifc.read += self.__msg[self.__offs:chunk_len] + self.ifc.rx_fifo += self.__msg[self.__offs:chunk_len] copied_bytes = chunk_len - self.__offs self.__offs = chunk_len else: - self.ifc.read += self.__msg[self.__offs:] + self.ifc.rx_fifo += self.__msg[self.__offs:] copied_bytes = self.__msg_len - self.__offs self.__offs = self.__msg_len except Exception: @@ -71,8 +73,8 @@ class MemoryStream(Talent): def createClientStream(self, msg, chunks = (0,)): c = MemoryStream(msg, chunks, False) - self.remote_stream = c - c. remote_stream = self + self.remote.stream = c + c. remote.stream = self return c def _Talent__flush_recv_msg(self) -> None: @@ -741,7 +743,9 @@ def test_read_message(msg_contact_info): assert m.msg_id==0 assert m.header_len==23 assert m.data_len==25 - assert m.ifc.forward.get()==b'' + assert m.ifc.rx_get()==b'' + assert m.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' m.close() def test_read_message_twice(config_no_tsun_inv1, msg_inverter_ind): @@ -761,7 +765,7 @@ def test_read_message_twice(config_no_tsun_inv1, msg_inverter_ind): assert m.msg_recvd[1]['data_len']==120 assert m.id_str == b"R170000000000001" assert m.unique_id == 'R170000000000001' - assert m.ifc.forward.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' m.close() def test_read_message_long_id(msg_contact_info_long_id): @@ -846,15 +850,15 @@ def test_read_two_messages(config_tsun_allow_all, msg2_contact_info,msg_contact_ assert m.msg_recvd[1]['msg_id']==0 assert m.msg_recvd[1]['header_len']==23 assert m.msg_recvd[1]['data_len']==25 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==msg_contact_rsp + msg_contact_rsp2 + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==msg_contact_rsp + msg_contact_rsp2 assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 - m.ifc.write.clear() # clear send buffer for next test + m.ifc.tx_clear() # clear send buffer for next test m.contact_name = b'solarhub' m.contact_mail = b'solarhub@123456' m._init_new_client_conn() - assert m.ifc.write.get()==b'\x00\x00\x00,\x10R170000000000002\x91\x00\x08solarhub\x0fsolarhub@123456' + assert m.ifc.tx_fifo.get()==b'\x00\x00\x00,\x10R170000000000002\x91\x00\x08solarhub\x0fsolarhub@123456' m.close() def test_conttact_req(config_tsun_allow_all, msg_contact_info, msg_contact_rsp): @@ -871,8 +875,8 @@ def test_conttact_req(config_tsun_allow_all, msg_contact_info, msg_contact_rsp): assert m.msg_id==0 assert m.header_len==23 assert m.data_len==25 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==msg_contact_rsp + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==msg_contact_rsp m.close() def test_contact_broken_req(config_tsun_allow_all, msg_contact_info_broken, msg_contact_rsp): @@ -889,8 +893,8 @@ def test_contact_broken_req(config_tsun_allow_all, msg_contact_info_broken, msg_ assert m.msg_id==0 assert m.header_len==23 assert m.data_len==23 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==msg_contact_rsp + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==msg_contact_rsp m.close() def test_conttact_req(config_tsun_allow_all, msg_contact_info, msg_contact_rsp): @@ -907,8 +911,8 @@ def test_conttact_req(config_tsun_allow_all, msg_contact_info, msg_contact_rsp): assert m.msg_id==0 assert m.header_len==23 assert m.data_len==25 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==msg_contact_rsp + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==msg_contact_rsp m.close() def test_contact_broken_req(config_tsun_allow_all, msg_contact_info_broken, msg_contact_rsp): @@ -925,8 +929,8 @@ def test_contact_broken_req(config_tsun_allow_all, msg_contact_info_broken, msg_ assert m.msg_id==0 assert m.header_len==23 assert m.data_len==23 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==msg_contact_rsp + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==msg_contact_rsp m.close() def test_msg_contact_resp(config_tsun_inv1, msg_contact_rsp): @@ -944,8 +948,8 @@ def test_msg_contact_resp(config_tsun_inv1, msg_contact_rsp): assert m.msg_id==0 assert m.header_len==23 assert m.data_len==1 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -964,8 +968,8 @@ def test_msg_contact_resp_2(config_tsun_inv1, msg_contact_rsp): assert m.msg_id==0 assert m.header_len==23 assert m.data_len==1 - assert m.ifc.forward.get()==msg_contact_rsp - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_contact_rsp + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -984,8 +988,8 @@ def test_msg_contact_resp_3(config_tsun_inv1, msg_contact_rsp): assert m.msg_id==0 assert m.header_len==23 assert m.data_len==1 - assert m.ifc.forward.get()==msg_contact_rsp - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_contact_rsp + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1002,8 +1006,8 @@ def test_msg_contact_invalid(config_tsun_inv1, msg_contact_invalid): assert m.msg_id==0 assert m.header_len==23 assert m.data_len==1 - assert m.ifc.forward.get()==msg_contact_invalid - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_contact_invalid + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 m.close() @@ -1023,8 +1027,8 @@ def test_msg_get_time(config_tsun_inv1, msg_get_time): assert m.ts_offset==0 assert m.data_len==0 assert m.state==State.pend - assert m.ifc.forward.get()==msg_get_time - assert m.ifc.write.get()==b'\x00\x00\x00\x1b\x10R170000000000001\x91"\x00\x00\x01\x89\xc6,_\x00' + assert m.ifc.fwd_fifo.get()==msg_get_time + assert m.ifc.tx_fifo.get()==b'\x00\x00\x00\x1b\x10R170000000000001\x91"\x00\x00\x01\x89\xc6,_\x00' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1044,8 +1048,8 @@ def test_msg_get_time_autark(config_no_tsun_inv1, msg_get_time): assert m.ts_offset==0 assert m.data_len==0 assert m.state==State.received - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==bytearray(b'\x00\x00\x00\x1b\x10R170000000000001\x91"\x00\x00\x01\x89\xc6,_\x00') + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==bytearray(b'\x00\x00\x00\x1b\x10R170000000000001\x91"\x00\x00\x01\x89\xc6,_\x00') assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1055,7 +1059,7 @@ def test_msg_time_resp(config_tsun_inv1, msg_time_rsp): m = MemoryStream(msg_time_rsp, (0,), False) s = MemoryStream(b'', (0,), True) assert s.ts_offset==0 - m.remote_stream = s + m.remote.stream = s m.db.stat['proxy']['Unknown_Ctrl'] = 0 m.read() # read complete msg, and dispatch msg assert not m.header_valid # must be invalid, since msg was handled and buffer flushed @@ -1068,10 +1072,10 @@ def test_msg_time_resp(config_tsun_inv1, msg_time_rsp): assert m.ts_offset==3600000 assert s.ts_offset==3600000 assert m.data_len==8 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 - m.remote_stream = None + m.remote.stream = None s.close() m.close() @@ -1089,8 +1093,8 @@ def test_msg_time_resp_autark(config_no_tsun_inv1, msg_time_rsp): assert m.header_len==23 assert m.ts_offset==3600000 assert m.data_len==8 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1108,8 +1112,8 @@ def test_msg_time_inv_resp(config_tsun_inv1, msg_time_rsp_inv): assert m.header_len==23 assert m.ts_offset==0 assert m.data_len==4 - assert m.ifc.forward.get()==msg_time_rsp_inv - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_time_rsp_inv + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1127,8 +1131,8 @@ def test_msg_time_invalid(config_tsun_inv1, msg_time_invalid): assert m.header_len==23 assert m.ts_offset==0 assert m.data_len==0 - assert m.ifc.forward.get()==msg_time_invalid - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_time_invalid + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 m.close() @@ -1146,8 +1150,8 @@ def test_msg_time_invalid_autark(config_no_tsun_inv1, msg_time_invalid): assert m.ts_offset==0 assert m.header_len==23 assert m.data_len==0 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 m.close() @@ -1170,8 +1174,8 @@ def test_msg_act_time(config_no_modbus_poll, msg_act_time, msg_act_time_ack): assert m.header_len==23 assert m.data_len==9 assert m.state == State.up - assert m.ifc.forward.get()==msg_act_time - assert m.ifc.write.get()==msg_act_time_ack + assert m.ifc.fwd_fifo.get()==msg_act_time + assert m.ifc.tx_fifo.get()==msg_act_time_ack assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert 125 == m.db.get_db_value(Register.POLLING_INTERVAL, 0) m.close() @@ -1194,8 +1198,8 @@ def test_msg_act_time2(config_tsun_inv1, msg_act_time, msg_act_time_ack): assert m.ts_offset==0 assert m.header_len==23 assert m.data_len==9 - assert m.ifc.forward.get()==msg_act_time - assert m.ifc.write.get()==msg_act_time_ack + assert m.ifc.fwd_fifo.get()==msg_act_time + assert m.ifc.tx_fifo.get()==msg_act_time_ack assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert 123 == m.db.get_db_value(Register.POLLING_INTERVAL, 0) m.close() @@ -1215,8 +1219,8 @@ def test_msg_act_time_ofs(config_tsun_inv1, msg_act_time, msg_act_time_ofs, msg_ assert m.ts_offset==3600 assert m.header_len==23 assert m.data_len==9 - assert m.ifc.forward.get()==msg_act_time_ofs - assert m.ifc.write.get()==msg_act_time_ack + assert m.ifc.fwd_fifo.get()==msg_act_time_ofs + assert m.ifc.tx_fifo.get()==msg_act_time_ack assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1235,8 +1239,8 @@ def test_msg_act_time_ofs2(config_tsun_inv1, msg_act_time, msg_act_time_ofs, msg assert m.ts_offset==-3600 assert m.header_len==23 assert m.data_len==9 - assert m.ifc.forward.get()==msg_act_time - assert m.ifc.write.get()==msg_act_time_ack + assert m.ifc.fwd_fifo.get()==msg_act_time + assert m.ifc.tx_fifo.get()==msg_act_time_ack assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1255,8 +1259,8 @@ def test_msg_act_time_autark(config_no_tsun_inv1, msg_act_time, msg_act_time_ack assert m.ts_offset==0 assert m.header_len==23 assert m.data_len==9 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==msg_act_time_ack + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==msg_act_time_ack assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1273,8 +1277,8 @@ def test_msg_act_time_ack(config_tsun_inv1, msg_act_time_ack): assert m.msg_id==153 assert m.header_len==23 assert m.data_len==1 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1291,8 +1295,8 @@ def test_msg_act_time_cmd(config_tsun_inv1, msg_act_time_cmd): assert m.msg_id==153 assert m.header_len==23 assert m.data_len==1 - assert m.ifc.forward.get()==msg_act_time_cmd - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_act_time_cmd + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 m.close() @@ -1309,8 +1313,8 @@ def test_msg_act_time_inv(config_tsun_inv1, msg_act_time_inv): assert m.msg_id==153 assert m.header_len==23 assert m.data_len==8 - assert m.ifc.forward.get()==msg_act_time_inv - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_act_time_inv + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1328,12 +1332,12 @@ def test_msg_cntrl_ind(config_tsun_inv1, msg_controller_ind, msg_controller_ind_ assert m.header_len==23 assert m.data_len==284 m.ts_offset = 0 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.peek()==msg_controller_ind + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.peek()==msg_controller_ind m.ts_offset = -4096 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.get()==msg_controller_ind_ts_offs - assert m.ifc.write.get()==msg_controller_ack + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.get()==msg_controller_ind_ts_offs + assert m.ifc.tx_fifo.get()==msg_controller_ack assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1350,8 +1354,8 @@ def test_msg_cntrl_ack(config_tsun_inv1, msg_controller_ack): assert m.msg_id==113 assert m.header_len==23 assert m.data_len==1 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1369,12 +1373,12 @@ def test_msg_cntrl_invalid(config_tsun_inv1, msg_controller_invalid): assert m.header_len==23 assert m.data_len==1 m.ts_offset = 0 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.peek()==msg_controller_invalid + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.peek()==msg_controller_invalid m.ts_offset = -4096 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.get()==msg_controller_invalid - assert m.ifc.write.get()==b'' + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.get()==msg_controller_invalid + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 m.close() @@ -1393,12 +1397,12 @@ def test_msg_inv_ind(config_tsun_inv1, msg_inverter_ind, msg_inverter_ind_ts_off assert m.header_len==23 assert m.data_len==120 m.ts_offset = 0 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.peek()==msg_inverter_ind + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.peek()==msg_inverter_ind m.ts_offset = +256 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.get()==msg_inverter_ind_ts_offs - assert m.ifc.write.get()==msg_inverter_ack + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.get()==msg_inverter_ind_ts_offs + assert m.ifc.tx_fifo.get()==msg_inverter_ack assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1420,9 +1424,9 @@ def test_msg_inv_ind1(config_tsun_inv1, msg_inverter_ind2, msg_inverter_ind_ts_o assert m.header_len==23 assert m.data_len==1263 m.ts_offset = 0 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.get()==msg_inverter_ind2 - assert m.ifc.write.get()==msg_inverter_ack + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.get()==msg_inverter_ind2 + assert m.ifc.tx_fifo.get()==msg_inverter_ack assert m.db.get_db_value(Register.TS_GRID) == 1691243349 m.close() @@ -1444,9 +1448,9 @@ def test_msg_inv_ind2(config_tsun_inv1, msg_inverter_ind_new, msg_inverter_ind_t assert m.header_len==23 assert m.data_len==1165 m.ts_offset = 0 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.get()==msg_inverter_ind_new - assert m.ifc.write.get()==msg_inverter_ack + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.get()==msg_inverter_ind_new + assert m.ifc.tx_fifo.get()==msg_inverter_ack assert m.db.get_db_value(Register.INVERTER_STATUS) == None assert m.db.get_db_value(Register.TS_GRID) == None m.db.db['grid'] = {'Output_Power': 100} @@ -1472,9 +1476,9 @@ def test_msg_inv_ind3(config_tsun_inv1, msg_inverter_ind_0w, msg_inverter_ack): assert m.header_len==23 assert m.data_len==1263 m.ts_offset = 0 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.get()==msg_inverter_ind_0w - assert m.ifc.write.get()==msg_inverter_ack + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.get()==msg_inverter_ind_0w + assert m.ifc.tx_fifo.get()==msg_inverter_ack assert m.db.get_db_value(Register.INVERTER_STATUS) == 1 assert isclose(m.db.db['grid']['Output_Power'], 0.5) m.close() @@ -1496,8 +1500,8 @@ def test_msg_inv_ack(config_tsun_inv1, msg_inverter_ack): assert m.msg_id==4 assert m.header_len==23 assert m.data_len==1 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -1515,12 +1519,12 @@ def test_msg_inv_invalid(config_tsun_inv1, msg_inverter_invalid): assert m.header_len==23 assert m.data_len==1 m.ts_offset = 0 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.peek()==msg_inverter_invalid + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.peek()==msg_inverter_invalid m.ts_offset = 256 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.get()==msg_inverter_invalid - assert m.ifc.write.get()==b'' + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.get()==msg_inverter_invalid + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 m.close() @@ -1539,12 +1543,12 @@ def test_msg_ota_req(config_tsun_inv1, msg_ota_req): assert m.header_len==23 assert m.data_len==259 m.ts_offset = 0 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.peek()==msg_ota_req + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.peek()==msg_ota_req m.ts_offset = 4096 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.get()==msg_ota_req - assert m.ifc.write.get()==b'' + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.get()==msg_ota_req + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['OTA_Start_Msg'] == 1 m.close() @@ -1566,12 +1570,12 @@ def test_msg_ota_ack(config_tsun_inv1, msg_ota_ack): assert m.header_len==23 assert m.data_len==1 m.ts_offset = 0 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.peek()==msg_ota_ack + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.peek()==msg_ota_ack m.ts_offset = 256 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.get()==msg_ota_ack - assert m.ifc.write.get()==b'' + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.get()==msg_ota_ack + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['OTA_Start_Msg'] == 0 m.close() @@ -1591,12 +1595,12 @@ def test_msg_ota_invalid(config_tsun_inv1, msg_ota_invalid): assert m.header_len==23 assert m.data_len==1 m.ts_offset = 0 - m._update_header(m.ifc.forward.peek()) - assert m.ifc.forward.peek()==msg_ota_invalid + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.fwd_fifo.peek()==msg_ota_invalid m.ts_offset = 4096 - assert m.ifc.forward.get()==msg_ota_invalid - m._update_header(m.ifc.forward.peek()) - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_ota_invalid + m._update_header(m.ifc.fwd_fifo.peek()) + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 assert m.db.stat['proxy']['OTA_Start_Msg'] == 0 m.close() @@ -1614,8 +1618,8 @@ def test_msg_unknown(config_tsun_inv1, msg_unknown): assert m.msg_id==23 assert m.header_len==23 assert m.data_len==4 - assert m.ifc.forward.get()==msg_unknown - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_unknown + assert m.ifc.tx_fifo.get()==b'' assert 1 == m.db.stat['proxy']['Unknown_Msg'] m.close() @@ -1635,9 +1639,9 @@ def test_ctrl_byte(): def test_msg_iterator(): - m1 = Talent(server_side=True, ifc=AsyncIfc()) - m2 = Talent(server_side=True, ifc=AsyncIfc()) - m3 = Talent(server_side=True, ifc=AsyncIfc()) + m1 = Talent(server_side=True, ifc=AsyncIfcImpl()) + m2 = Talent(server_side=True, ifc=AsyncIfcImpl()) + m3 = Talent(server_side=True, ifc=AsyncIfcImpl()) m3.close() del m3 test1 = 0 @@ -1739,11 +1743,11 @@ def test_msg_modbus_req(config_tsun_inv1, msg_modbus_cmd): assert c.msg_id==119 assert c.header_len==23 assert c.data_len==13 - assert c.ifc.forward.get()==b'' - assert c.ifc.write.get()==b'' + assert c.ifc.fwd_fifo.get()==b'' + assert c.ifc.tx_fifo.get()==b'' assert m.id_str == b"R170000000000001" - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.sent_pdu == msg_modbus_cmd assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 1 @@ -1769,11 +1773,11 @@ def test_msg_modbus_req2(config_tsun_inv1, msg_modbus_cmd): assert c.msg_id==119 assert c.header_len==23 assert c.data_len==13 - assert c.ifc.forward.get()==b'' - assert c.ifc.write.get()==b'' + assert c.ifc.fwd_fifo.get()==b'' + assert c.ifc.tx_fifo.get()==b'' assert m.id_str == b"R170000000000001" - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.sent_pdu == b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 1 @@ -1798,10 +1802,10 @@ def test_msg_modbus_req3(config_tsun_inv1, msg_modbus_cmd_crc_err): assert c.msg_id==119 assert c.header_len==23 assert c.data_len==13 - assert c.ifc.forward.get()==b'' - assert c.ifc.write.get()==b'' - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert c.ifc.fwd_fifo.get()==b'' + assert c.ifc.tx_fifo.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.sent_pdu ==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 @@ -1823,8 +1827,8 @@ def test_msg_modbus_rsp1(config_tsun_inv1, msg_modbus_rsp): assert m.msg_id==119 assert m.header_len==23 assert m.data_len==13 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 m.close() @@ -1845,8 +1849,8 @@ def test_msg_modbus_cloud_rsp(config_tsun_inv1, msg_modbus_rsp): assert m.msg_id==119 assert m.header_len==23 assert m.data_len==13 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Msg'] == 1 assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 @@ -1873,8 +1877,8 @@ def test_msg_modbus_rsp2(config_tsun_inv1, msg_modbus_rsp20): assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.mb.err == 5 assert m.msg_count == 2 - assert m.ifc.forward.get()==msg_modbus_rsp20 - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_modbus_rsp20 + assert m.ifc.tx_fifo.get()==b'' assert m.db.db == {'collector': {'Serial_Number': 'R170000000000001'}, 'inverter': {'Version': 'V5.1.09', 'Rated_Power': 300}, 'grid': {'Timestamp': m._utc(), 'Voltage': 225.9, 'Current': 0.41, 'Frequency': 49.99, 'Output_Power': 94.8}, 'env': {'Inverter_Temp': 22}, 'input': {'Timestamp': m._utc(), 'pv1': {'Voltage': 0.8, 'Current': 0.0, 'Power': 0.0}, 'pv2': {'Voltage': 34.5, 'Current': 2.89, 'Power': 99.8}, 'pv3': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}, 'pv4': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}}} assert m.db.get_db_value(Register.VERSION) == 'V5.1.09' assert m.db.get_db_value(Register.TS_GRID) == m._utc() @@ -1903,8 +1907,8 @@ def test_msg_modbus_rsp3(config_tsun_inv1, msg_modbus_rsp21): assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.mb.err == 5 assert m.msg_count == 2 - assert m.ifc.forward.get()==msg_modbus_rsp21 - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_modbus_rsp21 + assert m.ifc.tx_fifo.get()==b'' assert m.db.db == {'collector': {'Serial_Number': 'R170000000000001'}, 'inverter': {'Version': 'V5.1.0E', 'Rated_Power': 300}, 'grid': {'Timestamp': m._utc(), 'Voltage': 225.9, 'Current': 0.41, 'Frequency': 49.99, 'Output_Power': 94.8}, 'env': {'Inverter_Temp': 22}, 'input': {'Timestamp': m._utc(), 'pv1': {'Voltage': 0.8, 'Current': 0.0, 'Power': 0.0}, 'pv2': {'Voltage': 34.5, 'Current': 2.89, 'Power': 99.8}, 'pv3': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}, 'pv4': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}}} assert m.db.get_db_value(Register.VERSION) == 'V5.1.0E' assert m.db.get_db_value(Register.TS_GRID) == m._utc() @@ -1932,9 +1936,9 @@ def test_msg_modbus_rsp4(config_tsun_inv1, msg_modbus_rsp21): assert not m.header_valid # must be invalid, since msg was handled and buffer flushed assert m.mb.err == 0 assert m.msg_count == 1 - assert m.ifc.forward.get()==msg_modbus_rsp21 + assert m.ifc.fwd_fifo.get()==msg_modbus_rsp21 assert m.modbus_elms == 19 - assert m.ifc.write.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.db.db == db_values assert m.db.get_db_value(Register.VERSION) == 'V5.1.0E' assert m.db.get_db_value(Register.TS_GRID) == m._utc() @@ -1957,8 +1961,8 @@ def test_msg_modbus_rsp_new(config_tsun_inv1, msg_modbus_rsp20_new): assert m.msg_id==135 assert m.header_len==23 assert m.data_len==107 - assert m.ifc.forward.get()==b'' - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 assert m.db.stat['proxy']['Modbus_Command'] == 0 m.close() @@ -1977,8 +1981,8 @@ def test_msg_modbus_invalid(config_tsun_inv1, msg_modbus_inv): assert m.msg_id==119 assert m.header_len==23 assert m.data_len==13 - assert m.ifc.forward.get()==msg_modbus_inv - assert m.ifc.write.get()==b'' + assert m.ifc.fwd_fifo.get()==msg_modbus_inv + assert m.ifc.tx_fifo.get()==b'' assert m.db.stat['proxy']['Unknown_Ctrl'] == 1 assert m.db.stat['proxy']['Modbus_Command'] == 0 m.close() @@ -2006,8 +2010,8 @@ def test_msg_modbus_fragment(config_tsun_inv1, msg_modbus_rsp20): assert m.msg_id == 119 assert m.header_len == 23 assert m.data_len == 50 - assert m.ifc.forward.get()==msg_modbus_rsp20 - assert m.ifc.write.get() == b'' + assert m.ifc.fwd_fifo.get()==msg_modbus_rsp20 + assert m.ifc.tx_fifo.get() == b'' assert m.mb.err == 0 assert m.modbus_elms == 20-1 # register 0x300d is unknown, so one value can't be mapped assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 @@ -2021,15 +2025,15 @@ async def test_msg_build_modbus_req(config_tsun_inv1, msg_modbus_cmd): m.id_str = b"R170000000000001" await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG) assert 0 == m.send_msg_ofs - assert m.ifc.forward.get() == b'' - assert m.ifc.write.get() == b'' + assert m.ifc.fwd_fifo.get() == b'' + assert m.ifc.tx_fifo.get() == b'' assert m.sent_pdu == b'' m.state = State.up await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG) assert 0 == m.send_msg_ofs - assert m.ifc.forward.get() == b'' - assert m.ifc.write.get() == b'' + assert m.ifc.fwd_fifo.get() == b'' + assert m.ifc.tx_fifo.get() == b'' assert m.sent_pdu == msg_modbus_cmd m.close() @@ -2048,8 +2052,8 @@ def test_modbus_no_polling(config_no_modbus_poll, msg_get_time): assert m.header_len==23 assert m.ts_offset==0 assert m.data_len==0 - assert m.ifc.forward.get()==msg_get_time - assert m.ifc.write.get()==b'\x00\x00\x00\x1b\x10R170000000000001\x91"\x00\x00\x01\x89\xc6,_\x00' + assert m.ifc.fwd_fifo.get()==msg_get_time + assert m.ifc.tx_fifo.get()==b'\x00\x00\x00\x1b\x10R170000000000001\x91"\x00\x00\x01\x89\xc6,_\x00' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -2072,25 +2076,25 @@ async def test_modbus_polling(config_tsun_inv1, msg_inverter_ind): assert m.header_len==23 assert m.ts_offset==0 assert m.data_len==120 - assert m.ifc.forward.get()==msg_inverter_ind - assert m.ifc.write.get()==b'\x00\x00\x00\x14\x10R170000000000001\x99\x04\x01' + assert m.ifc.fwd_fifo.get()==msg_inverter_ind + assert m.ifc.tx_fifo.get()==b'\x00\x00\x00\x14\x10R170000000000001\x99\x04\x01' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 - m.ifc.write.clear() # clear send buffer for next test + m.ifc.tx_clear() # clear send buffer for next test assert isclose(m.mb_timeout, 0.5) assert next(m.mb_timer.exp_count) == 0 await asyncio.sleep(0.5) assert m.sent_pdu==b'\x00\x00\x00 \x10R170000000000001pw\x00\x01\xa3(\x08\x01\x030\x00\x000J\xde' - assert m.ifc.write.get()==b'' + assert m.ifc.tx_fifo.get()==b'' await asyncio.sleep(0.5) assert m.sent_pdu==b'\x00\x00\x00 \x10R170000000000001pw\x00\x01\xa3(\x08\x01\x030\x00\x000J\xde' - assert m.ifc.write.get()==b'' + assert m.ifc.tx_fifo.get()==b'' await asyncio.sleep(0.5) assert m.sent_pdu==b'\x00\x00\x00 \x10R170000000000001pw\x00\x01\xa3(\x08\x01\x03\x20\x00\x00`N"' - assert m.ifc.write.get()==b'' + assert m.ifc.tx_fifo.get()==b'' assert next(m.mb_timer.exp_count) == 4 m.close() @@ -2133,3 +2137,15 @@ def test_multiiple_recv_buf(config_tsun_allow_all, multiple_recv_buf): assert m.db.stat['proxy']['Invalid_Data_Type'] == 1 m.close() + +def test_timeout(config_tsun_inv1): + _ = config_tsun_inv1 + m = MemoryStream(b'') + assert m.state == State.init + assert Talent.MAX_START_TIME == m._timeout() + m.state = State.up + m.modbus_polling = True + assert Talent.MAX_INV_IDLE_TIME == m._timeout() + m.modbus_polling = False + assert Talent.MAX_DEF_IDLE_TIME == m._timeout() + m.close() From 37c977c2fe316d150fa37c82d66b9ea3a7041c5f Mon Sep 17 00:00:00 2001 From: Stefan Allius Date: Wed, 25 Sep 2024 00:11:39 +0200 Subject: [PATCH 05/11] avoid mqtt handling for invalid serial numbers --- app/src/gen3/inverter_g3.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/gen3/inverter_g3.py b/app/src/gen3/inverter_g3.py index c913c28..7d21fff 100644 --- a/app/src/gen3/inverter_g3.py +++ b/app/src/gen3/inverter_g3.py @@ -118,6 +118,8 @@ class InverterG3(Inverter, ConnectionG3): async def __register_home_assistant(self) -> None: '''register all our topics at home assistant''' + if not self.unique_id: + return for data_json, component, node_id, id in self.db.ha_confs( self.entity_prfx, self.node_id, self.unique_id, self.sug_area): From bc54944077c3a31a145e3edbef4108f1d5ebb9a3 Mon Sep 17 00:00:00 2001 From: Stefan Allius Date: Wed, 25 Sep 2024 22:41:01 +0200 Subject: [PATCH 06/11] add two more callbacks --- app/src/async_ifc.py | 8 +++++ app/src/async_stream.py | 62 +++++++++++++++++++++++++-------- app/src/gen3/talent.py | 4 +++ app/src/gen3plus/solarman_v5.py | 5 +++ app/src/messages.py | 4 --- 5 files changed, 65 insertions(+), 18 deletions(-) diff --git a/app/src/async_ifc.py b/app/src/async_ifc.py index 333fb06..144f270 100644 --- a/app/src/async_ifc.py +++ b/app/src/async_ifc.py @@ -109,3 +109,11 @@ class AsyncIfc(ABC): @abstractmethod def prot_set_timeout_cb(self, callback): pass # pragma: no cover + + @abstractmethod + def prot_set_init_new_client_conn_cb(self, callback): + pass # pragma: no cover + + @abstractmethod + def prot_set_update_header_cb(self, callback): + pass # pragma: no cover diff --git a/app/src/async_stream.py b/app/src/async_stream.py index 83e3426..0cc449c 100644 --- a/app/src/async_stream.py +++ b/app/src/async_stream.py @@ -31,6 +31,16 @@ class AsyncIfcImpl(AsyncIfc): self.conn_no = next(self._ids) self.node_id = '' self.timeout_cb = None + self.init_new_client_conn_cb = None + self.update_header_cb = None + + def close(self): + self.timeout_cb = None + self.init_new_client_conn_cb = None + self.update_header_cb = None + self.fwd_fifo.reg_trigger(None) + self.tx_fifo.reg_trigger(None) + self.rx_fifo.reg_trigger(None) def set_node_id(self, value: str): self.node_id = value @@ -108,10 +118,33 @@ class AsyncIfcImpl(AsyncIfc): def prot_set_timeout_cb(self, callback): self.timeout_cb = callback + def prot_set_init_new_client_conn_cb(self, callback): + self.init_new_client_conn_cb = callback + + def prot_set_update_header_cb(self, callback): + self.update_header_cb = callback + class StreamPtr(): - def __init__(self, stream): - self.stream = stream + '''Descr StreamPtr''' + def __init__(self, _stream): + self.stream = _stream + + @property + def ifc(self): + return self._ifc + + @property + def stream(self): + return self._stream + + @stream.setter + def stream(self, value): + self._stream = value + if value: + self._ifc = value.ifc + else: + self._ifc = None class AsyncStream(AsyncIfcImpl): @@ -176,13 +209,13 @@ class AsyncStream(AsyncIfcImpl): # the connection to te TSUN cloud if self.remote.stream: logger.info(f'[{self.node_id}:{self.conn_no}] disc client ' - f'connection: [{self.remote.stream.node_id}:' - f'{self.remote.stream.conn_no}]') - await self.remote.stream._ifc.disc() + f'connection: [{self.remote.ifc.node_id}:' + f'{self.remote.ifc.conn_no}]') + await self.remote.ifc.disc() async def client_loop(self, _: str) -> None: '''Loop for receiving messages from the TSUN cloud (client-side)''' - client_stream = await self.remote.stream._ifc.loop() + client_stream = await self.remote.ifc.loop() logger.info(f'[{client_stream.node_id}:{client_stream.conn_no}] ' 'Client loop stopped for' f' l{client_stream.l_addr}') @@ -271,8 +304,8 @@ class AsyncStream(AsyncIfcImpl): hint: must be called before releasing the connection instance """ - self.tx_fifo.reg_trigger(None) self.async_create_remote = None + super().close() self._reader.feed_eof() # abort awaited read if self._writer.is_closing(): return @@ -314,15 +347,16 @@ class AsyncStream(AsyncIfcImpl): if not self.remote.stream: await self.async_create_remote() if self.remote.stream: - if self.remote.stream._init_new_client_conn(): - await self.remote.stream._ifc.__async_write() + if self.remote.ifc.init_new_client_conn_cb(): + await self.remote.ifc.__async_write() if self.remote.stream: - self.remote.stream._update_header(self.fwd_fifo.peek()) - self.fwd_fifo.logging(logging.INFO, 'Forward to ' - f'{self.remote.stream.addr}:') - self.remote.stream._ifc._writer.write(self.fwd_fifo.get()) - await self.remote.stream._ifc._writer.drain() + if self.remote.ifc.update_header_cb is callable: + self.remote.ifc.update_header_cb(self.fwd_fifo.peek()) + self.fwd_fifo.logging(logging.INFO, 'Forward to ' + f'{self.remote.ifc.addr}:') + self.remote.ifc._writer.write(self.fwd_fifo.get()) + await self.remote.ifc._writer.drain() except OSError as error: if self.remote.stream: diff --git a/app/src/gen3/talent.py b/app/src/gen3/talent.py index 4fd24a3..b662576 100644 --- a/app/src/gen3/talent.py +++ b/app/src/gen3/talent.py @@ -50,6 +50,8 @@ class Talent(Message): super().__init__(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.ifc = ifc self.await_conn_resp_cnt = 0 self.id_str = id_str @@ -110,6 +112,8 @@ class Talent(Message): 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) super().close() def __set_serial_no(self, serial_no: str): diff --git a/app/src/gen3plus/solarman_v5.py b/app/src/gen3plus/solarman_v5.py index 7f0caf2..3b0de54 100644 --- a/app/src/gen3plus/solarman_v5.py +++ b/app/src/gen3plus/solarman_v5.py @@ -66,6 +66,9 @@ class SolarmanV5(Message): super().__init__(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.ifc = ifc self.header_len = 11 # overwrite construcor in class Message self.control = 0 @@ -166,6 +169,8 @@ class SolarmanV5(Message): 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) super().close() async def send_start_cmd(self, snr: int, host: str, diff --git a/app/src/messages.py b/app/src/messages.py index 3a6f0e2..0fe66b9 100644 --- a/app/src/messages.py +++ b/app/src/messages.py @@ -134,10 +134,6 @@ class Message(metaclass=IterRegistry): # to our _recv_buffer return # pragma: no cover - def _update_header(self, _forward_buffer): - '''callback for updating the header of the forward buffer''' - pass # pragma: no cover - def _set_mqtt_timestamp(self, key, ts: float | None): if key not in self.new_data or \ not self.new_data[key]: From 209956865bebb9903e53450b5182aa7174c7686c Mon Sep 17 00:00:00 2001 From: Stefan Allius Date: Thu, 26 Sep 2024 00:11:11 +0200 Subject: [PATCH 07/11] add two more callbacks --- app/src/async_stream.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/src/async_stream.py b/app/src/async_stream.py index 0cc449c..1e1ac84 100644 --- a/app/src/async_stream.py +++ b/app/src/async_stream.py @@ -36,8 +36,6 @@ class AsyncIfcImpl(AsyncIfc): def close(self): self.timeout_cb = None - self.init_new_client_conn_cb = None - self.update_header_cb = None self.fwd_fifo.reg_trigger(None) self.tx_fifo.reg_trigger(None) self.rx_fifo.reg_trigger(None) @@ -351,12 +349,11 @@ class AsyncStream(AsyncIfcImpl): await self.remote.ifc.__async_write() if self.remote.stream: - if self.remote.ifc.update_header_cb is callable: - self.remote.ifc.update_header_cb(self.fwd_fifo.peek()) - self.fwd_fifo.logging(logging.INFO, 'Forward to ' - f'{self.remote.ifc.addr}:') - self.remote.ifc._writer.write(self.fwd_fifo.get()) - await self.remote.ifc._writer.drain() + self.remote.ifc.update_header_cb(self.fwd_fifo.peek()) + self.fwd_fifo.logging(logging.INFO, 'Forward to ' + f'{self.remote.ifc.addr}:') + self.remote.ifc._writer.write(self.fwd_fifo.get()) + await self.remote.ifc._writer.drain() except OSError as error: if self.remote.stream: From b6761e75171a5ae9879bfc497cc1d97d3346d89f Mon Sep 17 00:00:00 2001 From: Stefan Allius Date: Thu, 26 Sep 2024 00:14:51 +0200 Subject: [PATCH 08/11] FIX update_header_cb handling From 73526b7dc6f88f7709f6d671cafe4f3909159748 Mon Sep 17 00:00:00 2001 From: Stefan Allius Date: Thu, 26 Sep 2024 23:04:11 +0200 Subject: [PATCH 09/11] split AsyncStream in two classes --- app/proxy.svg | 855 ++++++++++++++++------------- app/proxy.yuml | 13 +- app/src/async_stream.py | 190 ++++--- app/src/gen3/connection_g3.py | 18 +- app/src/gen3/inverter_g3.py | 8 +- app/src/gen3plus/connection_g3p.py | 19 +- app/src/gen3plus/inverter_g3p.py | 7 +- app/src/object_factory.py | 13 - app/tests/test_inverter_g3.py | 1 + app/tests/test_inverter_g3p.py | 1 + 10 files changed, 620 insertions(+), 505 deletions(-) delete mode 100644 app/src/object_factory.py diff --git a/app/proxy.svg b/app/proxy.svg index aa7a73b..b79e92e 100644 --- a/app/proxy.svg +++ b/app/proxy.svg @@ -4,455 +4,520 @@ - - + + G - + A0 - - - -You can stick notes -on diagrams too! + + + +You can stick notes +on diagrams too! A1 - -Mqtt -<<Singleton>> - -<static>ha_restarts -<static>__client -<static>__cb_MqttIsUp - -<async>publish() -<async>close() + +Mqtt +<<Singleton>> + +<static>ha_restarts +<static>__client +<static>__cb_MqttIsUp + +<async>publish() +<async>close() A2 - -Inverter - -cls.db_stat -cls.entity_prfx -cls.discovery_prfx -cls.proxy_node_id -cls.proxy_unique_id -cls.mqtt:Mqtt - + +Inverter + +cls.db_stat +cls.entity_prfx +cls.discovery_prfx +cls.proxy_node_id +cls.proxy_unique_id +cls.mqtt:Mqtt + A1->A2 - - - + + + A3 - -InverterG3 - -__ha_restarts - -async_create_remote() -async_publ_mqtt() -close() + +InverterG3 + +__ha_restarts + +async_create_remote() +async_publ_mqtt() +close() A2->A3 - - + + A4 - -InverterG3P - -__ha_restarts - -async_create_remote( -)async_publ_mqtt() -close() + +InverterG3P + +__ha_restarts + +async_create_remote( +)async_publ_mqtt() +close() A2->A4 - - + + A5 - -IterRegistry - - -__iter__ + +IterRegistry + + +__iter__ A6 - -Message - -server_side:bool -header_valid:bool -header_len:unsigned -data_len:unsigned -unique_id -node_id -sug_area -_recv_buffer:bytearray -_send_buffer:bytearray -_forward_buffer:bytearray -db:Infos -new_data:list -state - -_read():void<abstract> -close():void -inc_counter():void -dec_counter():void + +Message + +server_side:bool +header_valid:bool +header_len:unsigned +data_len:unsigned +unique_id +node_id +sug_area +_recv_buffer:bytearray +_send_buffer:bytearray +_forward_buffer:bytearray +db:Infos +new_data:list +state + +_read():void<abstract> +close():void +inc_counter():void +dec_counter():void A5->A6 - - - - - -A15 - -Talent - -await_conn_resp_cnt -id_str -contact_name -contact_mail -db:InfosG3 -mb:Modbus -switch - -msg_contact_info() -msg_ota_update() -msg_get_time() -msg_collector_data() -msg_inverter_data() -msg_unknown() -close() - - - -A6->A15 - - - - - -A16 - -SolarmanV5 - -control -serial -snr -db:InfosG3P -mb:Modbus -switch - -msg_unknown() -close() - - - -A6->A16 - - - - - -A7 - -<<AsyncIfc>> - - -set_node_id() -get_conn_no() -tx_add() -tx_flush() -tx_get() -tx_peek() -tx_log() -tx_clear() -tx_len() -fwd_add() -fwd_flush() -fwd_log() -fwd_clear() -rx_get() -rx_peek() -rx_log() -rx_clear() -rx_len() -rx_set_cb() -prot_set_timeout_cb() - - - -A8 - -AsyncIfcImpl - -fwd_fifo:ByteFifo -tx_fifo:ByteFifo -rx_fifo:ByteFifo -conn_no:Count -node_id -timeout_cb - - - -A7->A8 - - - - - -A9 - -AsyncStream - - - -A8->A9 - - - - - -A10 - -ConnectionG3 - -remote.stream:ConnectionG3 - -healthy() -close() - - - -A10->A3 - - - - - -A10->A9 - - - -1 - - - -A10->A10 - - -0..1 -has - - - -A11 - -ConnectionG3P - -remote.stream:ConnectionG3P - -healthy() -close() - - - -A11->A4 - - - - - -A11->A9 - - - -1 - - - -A11->A11 - - -0..1 -has - - - -A12 - -Infos - -stat -new_stat_data -info_dev - -static_init() -dev_value() -inc_counter() -dec_counter() -ha_proxy_conf -ha_conf -ha_remove -update_db -set_db_def_value -get_db_value -ignore_this_device - - - -A13 - -InfosG3 - - -ha_confs() -parse() - - - -A12->A13 - - - - - -A14 - -InfosG3P - - -ha_confs() -parse() - - - -A12->A14 - - - - - -A15->A7 - - -use - - - -A15->A10 - - - - - -A15->A13 - - - - - -A16->A7 - - -use - - - -A16->A11 - - - - - -A16->A14 - - + + A17 - -Modbus - -que -snd_handler -rsp_handler -timeout -max_retires -last_xxx -err -retry_cnt -req_pend -tim - -build_msg() -recv_req() -recv_resp() -close() + +Talent + +await_conn_resp_cnt +id_str +contact_name +contact_mail +db:InfosG3 +mb:Modbus +switch + +msg_contact_info() +msg_ota_update() +msg_get_time() +msg_collector_data() +msg_inverter_data() +msg_unknown() +close() - - -A17->A15 - - -has -1 - - - -A17->A16 - - -has -1 + + +A6->A17 + + A18 - -ModbusConn - -host -port -addr -stream:InverterG3P - + +SolarmanV5 + +control +serial +snr +db:InfosG3P +mb:Modbus +switch + +msg_unknown() +close() - - -A18->A4 - - -1 -has + + +A6->A18 + + + + + +A7 + +<<AsyncIfc>> + + +set_node_id() +get_conn_no() +tx_add() +tx_flush() +tx_get() +tx_peek() +tx_log() +tx_clear() +tx_len() +fwd_add() +fwd_flush() +fwd_log() +fwd_clear() +rx_get() +rx_peek() +rx_log() +rx_clear() +rx_len() +rx_set_cb() +prot_set_timeout_cb() + + + +A8 + +AsyncIfcImpl + +fwd_fifo:ByteFifo +tx_fifo:ByteFifo +rx_fifo:ByteFifo +conn_no:Count +node_id +timeout_cb + + + +A7->A8 + + + + + +A9 + +AsyncStream + +reader +writer +addr +r_addr +l_addr + +<async>loop +disc() +close() +healthy() +__async_read() +__async_write() +__async_forward() + + + +A8->A9 + + + + + +A10 + +AsyncStreamServer + +async_create_remote + +<async>server_loop() +<async>_async_forward() +<async>publish_outstanding_mqtt() +close() + + + +A9->A10 + + + + + +A11 + +AsyncStreamClient + + +<async>client_loop() +<async>_async_forward()) + + + +A9->A11 + + + + + +A12 + +ConnectionG3 + +remote.stream:ConnectionG3 + +healthy() +close() + + + +A12->A3 + + + + + +A12->A10 + + + +0..1 + + + +A12->A11 + + + +0..1 + + + +A12->A12 + + +0..1 +has + + + +A13 + +ConnectionG3P + +remote.stream:ConnectionG3P + +healthy() +close() + + + +A13->A4 + + + + + +A13->A10 + + + +0..1 + + + +A13->A11 + + + +0..1 + + + +A13->A13 + + +0..1 +has + + + +A14 + +Infos + +stat +new_stat_data +info_dev + +static_init() +dev_value() +inc_counter() +dec_counter() +ha_proxy_conf +ha_conf +ha_remove +update_db +set_db_def_value +get_db_value +ignore_this_device + + + +A15 + +InfosG3 + + +ha_confs() +parse() + + + +A14->A15 + + + + + +A16 + +InfosG3P + + +ha_confs() +parse() + + + +A14->A16 + + + + + +A17->A7 + + +use + + + +A17->A12 + + + + + +A17->A15 + + + + + +A18->A7 + + +use + + + +A18->A13 + + + + + +A18->A16 + + + + + +A19 + +Modbus + +que +snd_handler +rsp_handler +timeout +max_retires +last_xxx +err +retry_cnt +req_pend +tim + +build_msg() +recv_req() +recv_resp() +close() + + + +A19->A17 + + +has +1 + + + +A19->A18 + + +has +1 + + + +A20 + +ModbusConn + +host +port +addr +stream:InverterG3P + + + + +A20->A4 + + +1 +has diff --git a/app/proxy.yuml b/app/proxy.yuml index 9b1b136..b9f25e4 100644 --- a/app/proxy.yuml +++ b/app/proxy.yuml @@ -14,18 +14,25 @@ [<>||set_node_id();get_conn_no();;tx_add();tx_flush();tx_get();tx_peek();tx_log();tx_clear();tx_len();;fwd_add();fwd_flush();fwd_log();fwd_clear();rx_get();rx_peek();rx_log();rx_clear();rx_len();rx_set_cb();;prot_set_timeout_cb()] [AsyncIfcImpl|fwd_fifo:ByteFifo;tx_fifo:ByteFifo;rx_fifo:ByteFifo;conn_no:Count;node_id;timeout_cb] +[AsyncStream|reader;writer;addr;r_addr;l_addr|;loop;disc();close();healthy();;__async_read();__async_write();__async_forward()] +[AsyncStreamServer|async_create_remote|server_loop();_async_forward();publish_outstanding_mqtt();close()] +[AsyncStreamClient||client_loop();_async_forward())] [<>]^-.-[AsyncIfcImpl] [AsyncIfcImpl]<-[AsyncStream] -[AsyncStream|reader;writer;addr;r_addr;l_addr|server_loop();client_loop();loop;disc();close();;__async_read();async_write();__async_forward()] +[AsyncStream]<-[AsyncStreamServer] +[AsyncStream]<-[AsyncStreamClient] + [ConnectionG3|remote.stream:ConnectionG3|healthy();close()] [ConnectionG3]^[InverterG3] [ConnectionG3]has-0..1>[ConnectionG3] -[ConnectionG3]++-1>[AsyncStream] +[ConnectionG3]++-0..1>[AsyncStreamClient] +[ConnectionG3]++-0..1>[AsyncStreamServer] [ConnectionG3P|remote.stream:ConnectionG3P|healthy();close()] [ConnectionG3P]^[InverterG3P] [ConnectionG3P]has-0..1>[ConnectionG3P] -[ConnectionG3P]++-1>[AsyncStream] +[ConnectionG3P]++-0..1>[AsyncStreamClient] +[ConnectionG3P]++-0..1>[AsyncStreamServer] [Infos|stat;new_stat_data;info_dev|static_init();dev_value();inc_counter();dec_counter();ha_proxy_conf;ha_conf;ha_remove;update_db;set_db_def_value;get_db_value;ignore_this_device] [Infos]^[InfosG3||ha_confs();parse()] diff --git a/app/src/async_stream.py b/app/src/async_stream.py index 1e1ac84..a0c8e6a 100644 --- a/app/src/async_stream.py +++ b/app/src/async_stream.py @@ -7,10 +7,12 @@ from typing import Self from itertools import count if __name__ == "app.src.async_stream": + from app.src.inverter import Inverter from app.src.byte_fifo import ByteFifo from app.src.async_ifc import AsyncIfc from app.src.infos import Infos else: # pragma: no cover + from inverter import Inverter from byte_fifo import ByteFifo from async_ifc import AsyncIfc from infos import Infos @@ -156,15 +158,13 @@ class AsyncStream(AsyncIfcImpl): '''maximum default time without a received msg in sec''' def __init__(self, reader: StreamReader, writer: StreamWriter, - addr, async_publ_mqtt, async_create_remote, - rstream: "StreamPtr") -> None: + addr, rstream: "StreamPtr") -> None: AsyncIfcImpl.__init__(self) logger.debug('AsyncStream.__init__') self.remote = rstream self.tx_fifo.reg_trigger(self.__write_cb) - self.async_create_remote = async_create_remote self._reader = reader self._writer = writer self.addr = addr @@ -172,7 +172,7 @@ class AsyncStream(AsyncIfcImpl): self.l_addr = '' self.proc_start = None # start processing start timestamp self.proc_max = 0 - self.async_publ_mqtt = async_publ_mqtt + self.async_publ_mqtt = None # will be set AsyncStreamServer only def __write_cb(self): self._writer.write(self.tx_fifo.get()) @@ -182,56 +182,6 @@ class AsyncStream(AsyncIfcImpl): return self.timeout_cb return 360 - async def publish_outstanding_mqtt(self): - '''Publish all outstanding MQTT topics''' - try: - if self.unique_id: - await self.async_publ_mqtt() - await self._async_publ_mqtt_proxy_stat('proxy') - except Exception: - pass - - async def server_loop(self, addr: str) -> None: - '''Loop for receiving messages from the inverter (server-side)''' - logger.info(f'[{self.node_id}:{self.conn_no}] ' - f'Accept connection from {addr}') - Infos.inc_counter('Inverter_Cnt') - await self.publish_outstanding_mqtt() - await self.loop() - Infos.dec_counter('Inverter_Cnt') - await self.publish_outstanding_mqtt() - logger.info(f'[{self.node_id}:{self.conn_no}] Server loop stopped for' - f' r{self.r_addr}') - - # if the server connection closes, we also have to disconnect - # the connection to te TSUN cloud - if self.remote.stream: - logger.info(f'[{self.node_id}:{self.conn_no}] disc client ' - f'connection: [{self.remote.ifc.node_id}:' - f'{self.remote.ifc.conn_no}]') - await self.remote.ifc.disc() - - async def client_loop(self, _: str) -> None: - '''Loop for receiving messages from the TSUN cloud (client-side)''' - client_stream = await self.remote.ifc.loop() - logger.info(f'[{client_stream.node_id}:{client_stream.conn_no}] ' - 'Client loop stopped for' - f' l{client_stream.l_addr}') - - # if the client connection closes, we don't touch the server - # connection. Instead we erase the client connection stream, - # thus on the next received packet from the inverter, we can - # establish a new connection to the TSUN cloud - - # erase backlink to inverter - client_stream.remote.stream = None - - if self.remote.stream == client_stream: - # logging.debug(f'Client l{client_stream.l_addr} refs:' - # f' {gc.get_referrers(client_stream)}') - # than erase client connection - self.remote.stream = None - async def loop(self) -> Self: """Async loop handler for precessing all received messages""" self.r_addr = self._writer.get_extra_info('peername') @@ -247,10 +197,10 @@ class AsyncStream(AsyncIfcImpl): await asyncio.wait_for(self.__async_read(), dead_conn_to) - # if self.unique_id: await self.__async_write() await self.__async_forward() - await self.async_publ_mqtt() + if self.async_publ_mqtt: + await self.async_publ_mqtt() except asyncio.TimeoutError: logger.warning(f'[{self.node_id}:{self.conn_no}] Dead ' @@ -282,13 +232,6 @@ class AsyncStream(AsyncIfcImpl): f"{traceback.format_exc()}") await asyncio.sleep(0) # be cooperative to other task - async def __async_write(self, headline: str = 'Transmit to ') -> None: - """Async write handler to transmit the send_buffer""" - if len(self.tx_fifo) > 0: - self.tx_fifo.logging(logging.INFO, f'{headline}{self.addr}:') - self._writer.write(self.tx_fifo.get()) - await self._writer.drain() - async def disc(self) -> None: """Async disc handler for graceful disconnect""" if self._writer.is_closing(): @@ -302,7 +245,6 @@ class AsyncStream(AsyncIfcImpl): hint: must be called before releasing the connection instance """ - self.async_create_remote = None super().close() self._reader.feed_eof() # abort awaited read if self._writer.is_closing(): @@ -337,23 +279,19 @@ class AsyncStream(AsyncIfcImpl): else: raise RuntimeError("Peer closed.") + async def __async_write(self, headline: str = 'Transmit to ') -> None: + """Async write handler to transmit the send_buffer""" + if len(self.tx_fifo) > 0: + self.tx_fifo.logging(logging.INFO, f'{headline}{self.addr}:') + self._writer.write(self.tx_fifo.get()) + await self._writer.drain() + async def __async_forward(self) -> None: """forward handler transmits data over the remote connection""" if len(self.fwd_fifo) == 0: return try: - if not self.remote.stream: - await self.async_create_remote() - if self.remote.stream: - if self.remote.ifc.init_new_client_conn_cb(): - await self.remote.ifc.__async_write() - - if self.remote.stream: - self.remote.ifc.update_header_cb(self.fwd_fifo.peek()) - self.fwd_fifo.logging(logging.INFO, 'Forward to ' - f'{self.remote.ifc.addr}:') - self.remote.ifc._writer.write(self.fwd_fifo.get()) - await self.remote.ifc._writer.drain() + await self._async_forward() except OSError as error: if self.remote.stream: @@ -382,3 +320,103 @@ class AsyncStream(AsyncIfcImpl): def __del__(self): logger.debug( f"AsyncStream.__del__ l{self.l_addr} | r{self.r_addr}") + + +class AsyncStreamServer(AsyncStream): + def __init__(self, reader: StreamReader, writer: StreamWriter, + addr, async_publ_mqtt, async_create_remote, + rstream: "StreamPtr") -> None: + AsyncStream.__init__(self, reader, writer, addr, + rstream) + self.async_create_remote = async_create_remote + self.async_publ_mqtt = async_publ_mqtt + + async def server_loop(self, addr: str) -> None: + '''Loop for receiving messages from the inverter (server-side)''' + logger.info(f'[{self.node_id}:{self.conn_no}] ' + f'Accept connection from {addr}') + Infos.inc_counter('Inverter_Cnt') + await self.publish_outstanding_mqtt() + await self.loop() + Infos.dec_counter('Inverter_Cnt') + await self.publish_outstanding_mqtt() + logger.info(f'[{self.node_id}:{self.conn_no}] Server loop stopped for' + f' r{self.r_addr}') + + # if the server connection closes, we also have to disconnect + # the connection to te TSUN cloud + if self.remote.stream: + logger.info(f'[{self.node_id}:{self.conn_no}] disc client ' + f'connection: [{self.remote.ifc.node_id}:' + f'{self.remote.ifc.conn_no}]') + await self.remote.ifc.disc() + + async def _async_forward(self) -> None: + """forward handler transmits data over the remote connection""" + if not self.remote.stream: + await self.async_create_remote() + if self.remote.stream and \ + self.remote.ifc.init_new_client_conn_cb(): + await self.remote.ifc._AsyncStream__async_write() + if self.remote.stream: + self.remote.ifc.update_header_cb(self.fwd_fifo.peek()) + self.fwd_fifo.logging(logging.INFO, 'Forward to ' + f'{self.remote.ifc.addr}:') + self.remote.ifc._writer.write(self.fwd_fifo.get()) + await self.remote.ifc._writer.drain() + + async def publish_outstanding_mqtt(self): + '''Publish all outstanding MQTT topics''' + try: + await self.async_publ_mqtt() + await Inverter._async_publ_mqtt_proxy_stat('proxy') + except Exception: + pass + + def close(self) -> None: + """close handler for a no waiting disconnect + + hint: must be called before releasing the connection instance + """ + self.async_create_remote = None + self.async_publ_mqtt = None + super().close() + + +class AsyncStreamClient(AsyncStream): + def __init__(self, reader: StreamReader, writer: StreamWriter, + addr, rstream: "StreamPtr") -> None: + AsyncStream.__init__(self, reader, writer, addr, + rstream) + + async def client_loop(self, _: str) -> None: + '''Loop for receiving messages from the TSUN cloud (client-side)''' + await self.loop() + logger.info(f'[{self.node_id}:{self.conn_no}] ' + 'Client loop stopped for' + f' l{self.l_addr}') + + server_stream = self.remote.stream + + # if the client connection closes, we don't touch the server + # connection. Instead we erase the client connection stream, + # thus on the next received packet from the inverter, we can + # establish a new connection to the TSUN cloud + + if server_stream.remote.ifc == self: + # logging.debug(f'Client l{client_stream.l_addr} refs:' + # f' {gc.get_referrers(client_stream)}') + # than erase client connection + server_stream.remote.stream = None # erases stream and ifc link + + # erase backlink to inverter + self.remote.stream = None + + async def _async_forward(self) -> None: + """forward handler transmits data over the remote connection""" + if self.remote.stream: + self.remote.ifc.update_header_cb(self.fwd_fifo.peek()) + self.fwd_fifo.logging(logging.INFO, 'Forward to ' + f'{self.remote.ifc.addr}:') + self.remote.ifc._writer.write(self.fwd_fifo.get()) + await self.remote.ifc._writer.drain() diff --git a/app/src/gen3/connection_g3.py b/app/src/gen3/connection_g3.py index 8633cfc..933c35b 100644 --- a/app/src/gen3/connection_g3.py +++ b/app/src/gen3/connection_g3.py @@ -2,10 +2,12 @@ import logging from asyncio import StreamReader, StreamWriter if __name__ == "app.src.gen3.connection_g3": - from app.src.async_stream import AsyncStream, StreamPtr + from app.src.async_stream import AsyncStreamServer + from app.src.async_stream import AsyncStreamClient, StreamPtr from app.src.gen3.talent import Talent else: # pragma: no cover - from async_stream import AsyncStream, StreamPtr + from async_stream import AsyncStreamServer + from async_stream import AsyncStreamClient, StreamPtr from gen3.talent import Talent logger = logging.getLogger('conn') @@ -17,10 +19,14 @@ class ConnectionG3(Talent): addr, rstream: 'ConnectionG3', server_side: bool, id_str=b'') -> None: self.remote = StreamPtr(rstream) - self._ifc = AsyncStream(reader, writer, addr, - self.async_publ_mqtt, - self.async_create_remote, - self.remote) + if server_side: + self._ifc = AsyncStreamServer(reader, writer, addr, + self.async_publ_mqtt, + self.async_create_remote, + self.remote) + else: + self._ifc = AsyncStreamClient(reader, writer, addr, + self.remote) Talent.__init__(self, server_side, self._ifc, id_str) self.conn_no = self._ifc.get_conn_no() diff --git a/app/src/gen3/inverter_g3.py b/app/src/gen3/inverter_g3.py index 7d21fff..48c70ad 100644 --- a/app/src/gen3/inverter_g3.py +++ b/app/src/gen3/inverter_g3.py @@ -71,7 +71,7 @@ class InverterG3(Inverter, ConnectionG3): logging.info(f'[{self.remote.stream.node_id}:' f'{self.remote.stream.conn_no}] ' f'Connected to {addr}') - asyncio.create_task(self._ifc.client_loop(addr)) + asyncio.create_task(self.remote.ifc.client_loop(addr)) except (ConnectionRefusedError, TimeoutError) as error: logging.info(f'{error}') @@ -83,6 +83,8 @@ class InverterG3(Inverter, ConnectionG3): async def async_publ_mqtt(self) -> None: '''publish data to MQTT broker''' + if not self.unique_id: + return # check if new inverter or collector infos are available or when the # home assistant has changed the status back to online try: @@ -97,7 +99,7 @@ class InverterG3(Inverter, ConnectionG3): for key in self.new_data: await self.__async_publ_mqtt_packet(key) for key in Infos.new_stat_data: - await self._async_publ_mqtt_proxy_stat(key) + await Inverter._async_publ_mqtt_proxy_stat(key) except MqttCodeError as error: logging.error(f'Mqtt except: {error}') @@ -118,8 +120,6 @@ class InverterG3(Inverter, ConnectionG3): async def __register_home_assistant(self) -> None: '''register all our topics at home assistant''' - if not self.unique_id: - return for data_json, component, node_id, id in self.db.ha_confs( self.entity_prfx, self.node_id, self.unique_id, self.sug_area): diff --git a/app/src/gen3plus/connection_g3p.py b/app/src/gen3plus/connection_g3p.py index a816c25..8e486ae 100644 --- a/app/src/gen3plus/connection_g3p.py +++ b/app/src/gen3plus/connection_g3p.py @@ -2,10 +2,12 @@ import logging from asyncio import StreamReader, StreamWriter if __name__ == "app.src.gen3plus.connection_g3p": - from app.src.async_stream import AsyncStream, StreamPtr + from app.src.async_stream import AsyncStreamServer + from app.src.async_stream import AsyncStreamClient, StreamPtr from app.src.gen3plus.solarman_v5 import SolarmanV5 else: # pragma: no cover - from async_stream import AsyncStream, StreamPtr + from async_stream import AsyncStreamServer + from async_stream import AsyncStreamClient, StreamPtr from gen3plus.solarman_v5 import SolarmanV5 logger = logging.getLogger('conn') @@ -19,10 +21,15 @@ class ConnectionG3P(SolarmanV5): client_mode: bool) -> None: self.remote = StreamPtr(rstream) - self._ifc = AsyncStream(reader, writer, addr, - self.async_publ_mqtt, - self.async_create_remote, - self.remote) + if server_side: + self._ifc = AsyncStreamServer(reader, writer, addr, + self.async_publ_mqtt, + self.async_create_remote, + self.remote) + else: + self._ifc = AsyncStreamClient(reader, writer, addr, + self.remote) + SolarmanV5.__init__(self, server_side, client_mode, self._ifc) self.conn_no = self._ifc.get_conn_no() diff --git a/app/src/gen3plus/inverter_g3p.py b/app/src/gen3plus/inverter_g3p.py index 6be2f8c..be32fe1 100644 --- a/app/src/gen3plus/inverter_g3p.py +++ b/app/src/gen3plus/inverter_g3p.py @@ -74,7 +74,7 @@ class InverterG3P(Inverter, ConnectionG3P): logging.info(f'[{self.remote.stream.node_id}:' f'{self.remote.stream.conn_no}] ' f'Connected to {addr}') - asyncio.create_task(self._ifc.client_loop(addr)) + asyncio.create_task(self.remote.ifc.client_loop(addr)) except (ConnectionRefusedError, TimeoutError) as error: logging.info(f'{error}') @@ -86,6 +86,9 @@ class InverterG3P(Inverter, ConnectionG3P): async def async_publ_mqtt(self) -> None: '''publish data to MQTT broker''' + if not self.unique_id: + return + # check if new inverter or collector infos are available or when the # home assistant has changed the status back to online try: @@ -100,7 +103,7 @@ class InverterG3P(Inverter, ConnectionG3P): for key in self.new_data: await self.__async_publ_mqtt_packet(key) for key in Infos.new_stat_data: - await self._async_publ_mqtt_proxy_stat(key) + await Inverter._async_publ_mqtt_proxy_stat(key) except MqttCodeError as error: logging.error(f'Mqtt except: {error}') diff --git a/app/src/object_factory.py b/app/src/object_factory.py deleted file mode 100644 index 7dd20cb..0000000 --- a/app/src/object_factory.py +++ /dev/null @@ -1,13 +0,0 @@ - -class ObjectFactory: - def __init__(self): - self._builders = {} - - def register_builder(self, key, builder): - self._builders[key] = builder - - def create(self, key, **kwargs): - builder = self._builders.get(key) - if not builder: - raise ValueError(key) - return builder(**kwargs) diff --git a/app/tests/test_inverter_g3.py b/app/tests/test_inverter_g3.py index e87aeb9..762659a 100644 --- a/app/tests/test_inverter_g3.py +++ b/app/tests/test_inverter_g3.py @@ -171,6 +171,7 @@ async def test_mqtt_publish(config_conn, patch_open_connection, patch_conn_close Inverter.class_init() inverter = InverterG3(FakeReader(), FakeWriter(), ('proxy.local', 10000)) + await inverter.async_publ_mqtt() # check call with invalid unique_id inverter._Talent__set_serial_no(serial_no= "123344") inverter.new_data['inverter'] = True diff --git a/app/tests/test_inverter_g3p.py b/app/tests/test_inverter_g3p.py index 1dd344c..26e50ae 100644 --- a/app/tests/test_inverter_g3p.py +++ b/app/tests/test_inverter_g3p.py @@ -172,6 +172,7 @@ async def test_mqtt_publish(config_conn, patch_open_connection, patch_conn_close Inverter.class_init() inverter = InverterG3P(FakeReader(), FakeWriter(), ('proxy.local', 10000), client_mode=False) + await inverter.async_publ_mqtt() # check call with invalid unique_id inverter._SolarmanV5__set_serial_no(snr= 123344) inverter.new_data['inverter'] = True From aa7c1832ef61a5cf62c3bce0c9d79ae721ffdc1c Mon Sep 17 00:00:00 2001 From: Stefan Allius Date: Fri, 27 Sep 2024 00:47:44 +0200 Subject: [PATCH 10/11] split ConnectionG3(P) in server and client class --- app/proxy.svg | 811 +++++++++++++++-------------- app/proxy.yuml | 28 +- app/src/gen3/connection_g3.py | 62 ++- app/src/gen3/inverter_g3.py | 14 +- app/src/gen3plus/connection_g3p.py | 65 +-- app/src/gen3plus/inverter_g3p.py | 15 +- app/tests/test_connection_g3.py | 6 +- app/tests/test_connection_g3p.py | 6 +- app/tests/test_inverter_g3.py | 8 +- app/tests/test_inverter_g3p.py | 8 +- 10 files changed, 548 insertions(+), 475 deletions(-) diff --git a/app/proxy.svg b/app/proxy.svg index b79e92e..240812f 100644 --- a/app/proxy.svg +++ b/app/proxy.svg @@ -4,504 +4,547 @@ - + G - + A0 - - - -You can stick notes -on diagrams too! + + + +You can stick notes +on diagrams too! A1 - -Mqtt -<<Singleton>> - -<static>ha_restarts -<static>__client -<static>__cb_MqttIsUp - -<async>publish() -<async>close() + +Mqtt +<<Singleton>> + +<static>ha_restarts +<static>__client +<static>__cb_MqttIsUp + +<async>publish() +<async>close() A2 - -Inverter - -cls.db_stat -cls.entity_prfx -cls.discovery_prfx -cls.proxy_node_id -cls.proxy_unique_id -cls.mqtt:Mqtt - + +Inverter + +cls.db_stat +cls.entity_prfx +cls.discovery_prfx +cls.proxy_node_id +cls.proxy_unique_id +cls.mqtt:Mqtt + A1->A2 - - - + + + A3 - -InverterG3 - -__ha_restarts - -async_create_remote() -async_publ_mqtt() -close() + +InverterG3 + +__ha_restarts + +async_create_remote() +async_publ_mqtt() +close() A2->A3 - - + + A4 - -InverterG3P - -__ha_restarts - -async_create_remote( -)async_publ_mqtt() -close() + +InverterG3P + +__ha_restarts + +async_create_remote( +)async_publ_mqtt() +close() A2->A4 - - + + A5 - -IterRegistry - - -__iter__ + +IterRegistry + + +__iter__ A6 - -Message - -server_side:bool -header_valid:bool -header_len:unsigned -data_len:unsigned -unique_id -node_id -sug_area -_recv_buffer:bytearray -_send_buffer:bytearray -_forward_buffer:bytearray -db:Infos -new_data:list -state - -_read():void<abstract> -close():void -inc_counter():void -dec_counter():void + +Message + +server_side:bool +header_valid:bool +header_len:unsigned +data_len:unsigned +unique_id +node_id +sug_area +_recv_buffer:bytearray +_send_buffer:bytearray +_forward_buffer:bytearray +db:Infos +new_data:list +state + +_read():void<abstract> +close():void +inc_counter():void +dec_counter():void A5->A6 - - + + - - -A17 - -Talent - -await_conn_resp_cnt -id_str -contact_name -contact_mail -db:InfosG3 -mb:Modbus -switch - -msg_contact_info() -msg_ota_update() -msg_get_time() -msg_collector_data() -msg_inverter_data() -msg_unknown() -close() + + +A21 + +Talent + +await_conn_resp_cnt +id_str +contact_name +contact_mail +db:InfosG3 +mb:Modbus +switch + +msg_contact_info() +msg_ota_update() +msg_get_time() +msg_collector_data() +msg_inverter_data() +msg_unknown() +close() - - -A6->A17 - - + + +A6->A21 + + - - -A18 - -SolarmanV5 - -control -serial -snr -db:InfosG3P -mb:Modbus -switch - -msg_unknown() -close() + + +A22 + +SolarmanV5 + +control +serial +snr +db:InfosG3P +mb:Modbus +switch + +msg_unknown() +close() - - -A6->A18 - - + + +A6->A22 + + A7 - -<<AsyncIfc>> - - -set_node_id() -get_conn_no() -tx_add() -tx_flush() -tx_get() -tx_peek() -tx_log() -tx_clear() -tx_len() -fwd_add() -fwd_flush() -fwd_log() -fwd_clear() -rx_get() -rx_peek() -rx_log() -rx_clear() -rx_len() -rx_set_cb() -prot_set_timeout_cb() + +<<AsyncIfc>> + + +set_node_id() +get_conn_no() +tx_add() +tx_flush() +tx_get() +tx_peek() +tx_log() +tx_clear() +tx_len() +fwd_add() +fwd_flush() +fwd_log() +fwd_clear() +rx_get() +rx_peek() +rx_log() +rx_clear() +rx_len() +rx_set_cb() +prot_set_timeout_cb() A8 - -AsyncIfcImpl - -fwd_fifo:ByteFifo -tx_fifo:ByteFifo -rx_fifo:ByteFifo -conn_no:Count -node_id -timeout_cb + +AsyncIfcImpl + +fwd_fifo:ByteFifo +tx_fifo:ByteFifo +rx_fifo:ByteFifo +conn_no:Count +node_id +timeout_cb A7->A8 - - + + A9 - -AsyncStream - -reader -writer -addr -r_addr -l_addr - -<async>loop -disc() -close() -healthy() -__async_read() -__async_write() -__async_forward() + +AsyncStream + +reader +writer +addr +r_addr +l_addr + +<async>loop +disc() +close() +healthy() +__async_read() +__async_write() +__async_forward() A8->A9 - - + + A10 - -AsyncStreamServer - -async_create_remote - -<async>server_loop() -<async>_async_forward() -<async>publish_outstanding_mqtt() -close() + +AsyncStreamServer + +async_create_remote + +<async>server_loop() +<async>_async_forward() +<async>publish_outstanding_mqtt() +close() A9->A10 - - + + A11 - -AsyncStreamClient - - -<async>client_loop() -<async>_async_forward()) + +AsyncStreamClient + + +<async>client_loop() +<async>_async_forward()) A9->A11 - - + + A12 - -ConnectionG3 - -remote.stream:ConnectionG3 - -healthy() -close() - - - -A12->A3 - - - - - -A12->A10 - - - -0..1 - - - -A12->A11 - - - -0..1 - - - -A12->A12 - - -0..1 -has + +ConnectionG3 + +remote.stream:ConnectionG3 + +healthy() A13 - -ConnectionG3P - -remote.stream:ConnectionG3P - -healthy() -close() + +ConnectionG3Client - - -A13->A4 - - - - - -A13->A10 - - - -0..1 - - - -A13->A11 - - - -0..1 - - - -A13->A13 - - -0..1 -has + + +A12->A13 + + A14 - -Infos - -stat -new_stat_data -info_dev - -static_init() -dev_value() -inc_counter() -dec_counter() -ha_proxy_conf -ha_conf -ha_remove -update_db -set_db_def_value -get_db_value -ignore_this_device + +ConnectionG3Server + + + +A12->A14 + + + + + +A13->A3 + + + + + +A13->A11 + + + +1 + + + +A14->A3 + + + + + +A14->A10 + + + +1 A15 - -InfosG3 - - -ha_confs() -parse() - - - -A14->A15 - - + +ConnectionG3P + +remote.stream:ConnectionG3P + +healthy() +close() A16 - -InfosG3P - - -ha_confs() -parse() + +ConnectionG3PClient - + + +A15->A16 + + + + + +A17 + +ConnectionG3PServer + + + +A15->A17 + + + + + +A16->A4 + + + + -A14->A16 - - +A16->A11 + + + +1 - - -A17->A7 - - -use - - + -A17->A12 - - +A17->A4 + + - - -A17->A15 - - + + +A17->A10 + + + +1 - - -A18->A7 - - -use - - - -A18->A13 - - - - - -A18->A16 - - + + +A18 + +Infos + +stat +new_stat_data +info_dev + +static_init() +dev_value() +inc_counter() +dec_counter() +ha_proxy_conf +ha_conf +ha_remove +update_db +set_db_def_value +get_db_value +ignore_this_device A19 - -Modbus - -que -snd_handler -rsp_handler -timeout -max_retires -last_xxx -err -retry_cnt -req_pend -tim - -build_msg() -recv_req() -recv_resp() -close() + +InfosG3 + + +ha_confs() +parse() - - -A19->A17 - - -has -1 - - - -A19->A18 - - -has -1 + + +A18->A19 + + A20 + +InfosG3P + + +ha_confs() +parse() + + + +A18->A20 + + + + + +A21->A7 + + +use + + + +A21->A12 + + + + + +A21->A19 + + + + + +A22->A7 + + +use + + + +A22->A15 + + + + + +A22->A20 + + + + + +A23 + +Modbus + +que +snd_handler +rsp_handler +timeout +max_retires +last_xxx +err +retry_cnt +req_pend +tim + +build_msg() +recv_req() +recv_resp() +close() + + + +A23->A21 + + +has +1 + + + +A23->A22 + + +has +1 + + + +A24 ModbusConn @@ -511,13 +554,13 @@ stream:InverterG3P - - -A20->A4 - - -1 -has + + +A24->A4 + + +1 +has diff --git a/app/proxy.yuml b/app/proxy.yuml index b9f25e4..af14779 100644 --- a/app/proxy.yuml +++ b/app/proxy.yuml @@ -23,16 +23,26 @@ [AsyncStream]<-[AsyncStreamClient] -[ConnectionG3|remote.stream:ConnectionG3|healthy();close()] -[ConnectionG3]^[InverterG3] -[ConnectionG3]has-0..1>[ConnectionG3] -[ConnectionG3]++-0..1>[AsyncStreamClient] -[ConnectionG3]++-0..1>[AsyncStreamServer] +[ConnectionG3|remote.stream:ConnectionG3|healthy()] +[ConnectionG3]^[ConnectionG3Client] +[ConnectionG3]^[ConnectionG3Server] +[ConnectionG3Client|remote.stream:ConnectionG3|close()] +[ConnectionG3Client]^[InverterG3] +[ConnectionG3Client]++-1>[AsyncStreamClient] +[ConnectionG3Server|remote.stream:ConnectionG3|;close()] +[ConnectionG3Server]^[InverterG3] +[ConnectionG3Server]++-1>[AsyncStreamServer] + [ConnectionG3P|remote.stream:ConnectionG3P|healthy();close()] -[ConnectionG3P]^[InverterG3P] -[ConnectionG3P]has-0..1>[ConnectionG3P] -[ConnectionG3P]++-0..1>[AsyncStreamClient] -[ConnectionG3P]++-0..1>[AsyncStreamServer] +[ConnectionG3P]^[ConnectionG3PClient] +[ConnectionG3P]^[ConnectionG3PServer] + +[ConnectionG3PClient|remote.stream:ConnectionG3P|close()] +[ConnectionG3PClient]^[InverterG3P] +[ConnectionG3PClient]++-1>[AsyncStreamClient] +[ConnectionG3PServer|remote.stream:ConnectionG3P|;close()] +[ConnectionG3PServer]^[InverterG3P] +[ConnectionG3PServer]++-1>[AsyncStreamServer] [Infos|stat;new_stat_data;info_dev|static_init();dev_value();inc_counter();dec_counter();ha_proxy_conf;ha_conf;ha_remove;update_db;set_db_def_value;get_db_value;ignore_this_device] [Infos]^[InfosG3||ha_confs();parse()] diff --git a/app/src/gen3/connection_g3.py b/app/src/gen3/connection_g3.py index 933c35b..8e431c4 100644 --- a/app/src/gen3/connection_g3.py +++ b/app/src/gen3/connection_g3.py @@ -14,32 +14,6 @@ logger = logging.getLogger('conn') class ConnectionG3(Talent): - - def __init__(self, reader: StreamReader, writer: StreamWriter, - addr, rstream: 'ConnectionG3', server_side: bool, - id_str=b'') -> None: - self.remote = StreamPtr(rstream) - if server_side: - self._ifc = AsyncStreamServer(reader, writer, addr, - self.async_publ_mqtt, - self.async_create_remote, - self.remote) - else: - self._ifc = AsyncStreamClient(reader, writer, addr, - self.remote) - Talent.__init__(self, server_side, self._ifc, id_str) - - self.conn_no = self._ifc.get_conn_no() - self.addr = addr - - ''' - Our puplic methods - ''' - def close(self): - self._ifc.close() - Talent.close(self) - # logger.info(f'AsyncStream refs: {gc.get_referrers(self)}') - async def async_create_remote(self) -> None: pass # virtual interface # pragma: no cover @@ -49,3 +23,39 @@ class ConnectionG3(Talent): def healthy(self) -> bool: logger.debug('ConnectionG3 healthy()') return self._ifc.healthy() + + def close(self): + self._ifc.close() + Talent.close(self) + # logger.info(f'AsyncStream refs: {gc.get_referrers(self)}') + + +class ConnectionG3Server(ConnectionG3): + + def __init__(self, reader: StreamReader, writer: StreamWriter, + addr, rstream: 'ConnectionG3Client', + id_str=b'') -> None: + + server_side = True + self.remote = StreamPtr(rstream) + self._ifc = AsyncStreamServer(reader, writer, addr, + self.async_publ_mqtt, + self.async_create_remote, + self.remote) + self.conn_no = self._ifc.get_conn_no() + self.addr = addr + Talent.__init__(self, server_side, self._ifc, id_str) + + +class ConnectionG3Client(ConnectionG3): + + def __init__(self, reader: StreamReader, writer: StreamWriter, + addr, rstream: 'ConnectionG3Server', + id_str=b'') -> None: + server_side = False + self.remote = StreamPtr(rstream) + self._ifc = AsyncStreamClient(reader, writer, addr, + self.remote) + self.conn_no = self._ifc.get_conn_no() + self.addr = addr + Talent.__init__(self, server_side, self._ifc, id_str) diff --git a/app/src/gen3/inverter_g3.py b/app/src/gen3/inverter_g3.py index 48c70ad..f2483e7 100644 --- a/app/src/gen3/inverter_g3.py +++ b/app/src/gen3/inverter_g3.py @@ -8,19 +8,21 @@ from aiomqtt import MqttCodeError if __name__ == "app.src.gen3.inverter_g3": from app.src.config import Config from app.src.inverter import Inverter - from app.src.gen3.connection_g3 import ConnectionG3 + from app.src.gen3.connection_g3 import ConnectionG3Server + from app.src.gen3.connection_g3 import ConnectionG3Client from app.src.infos import Infos else: # pragma: no cover from config import Config from inverter import Inverter - from gen3.connection_g3 import ConnectionG3 + from gen3.connection_g3 import ConnectionG3Server + from gen3.connection_g3 import ConnectionG3Client from infos import Infos logger_mqtt = logging.getLogger('mqtt') -class InverterG3(Inverter, ConnectionG3): +class InverterG3(Inverter, ConnectionG3Server): '''class Inverter is a derivation of an Async_Stream The class has some class method for managing common resources like a @@ -51,7 +53,7 @@ class InverterG3(Inverter, ConnectionG3): ''' def __init__(self, reader: StreamReader, writer: StreamWriter, addr): - super().__init__(reader, writer, addr, None, True) + super().__init__(reader, writer, addr, None) self.__ha_restarts = -1 self.addr = addr @@ -66,8 +68,8 @@ class InverterG3(Inverter, ConnectionG3): logging.info(f'[{self.node_id}] Connect to {addr}') connect = asyncio.open_connection(host, port) reader, writer = await connect - self.remote.stream = ConnectionG3(reader, writer, addr, self, - False, self.id_str) + self.remote.stream = ConnectionG3Client(reader, writer, addr, self, + self.id_str) logging.info(f'[{self.remote.stream.node_id}:' f'{self.remote.stream.conn_no}] ' f'Connected to {addr}') diff --git a/app/src/gen3plus/connection_g3p.py b/app/src/gen3plus/connection_g3p.py index 8e486ae..2fcf1bb 100644 --- a/app/src/gen3plus/connection_g3p.py +++ b/app/src/gen3plus/connection_g3p.py @@ -14,35 +14,6 @@ logger = logging.getLogger('conn') class ConnectionG3P(SolarmanV5): - - def __init__(self, reader: StreamReader, writer: StreamWriter, - addr, rstream: 'ConnectionG3P', - server_side: bool, - client_mode: bool) -> None: - - self.remote = StreamPtr(rstream) - if server_side: - self._ifc = AsyncStreamServer(reader, writer, addr, - self.async_publ_mqtt, - self.async_create_remote, - self.remote) - else: - self._ifc = AsyncStreamClient(reader, writer, addr, - self.remote) - - SolarmanV5.__init__(self, server_side, client_mode, self._ifc) - - self.conn_no = self._ifc.get_conn_no() - self.addr = addr - - ''' - Our puplic methods - ''' - def close(self): - self._ifc.close() - SolarmanV5.close(self) - # logger.info(f'AsyncStream refs: {gc.get_referrers(self)}') - async def async_create_remote(self) -> None: pass # virtual interface # pragma: no cover @@ -52,3 +23,39 @@ class ConnectionG3P(SolarmanV5): def healthy(self) -> bool: logger.debug('ConnectionG3P healthy()') return self._ifc.healthy() + + def close(self): + self._ifc.close() + SolarmanV5.close(self) + # logger.info(f'AsyncStream refs: {gc.get_referrers(self)}') + + +class ConnectionG3PServer(ConnectionG3P): + + def __init__(self, reader: StreamReader, writer: StreamWriter, + addr, rstream: 'ConnectionG3PClient', + client_mode: bool) -> None: + + server_side = True + self.remote = StreamPtr(rstream) + self._ifc = AsyncStreamServer(reader, writer, addr, + self.async_publ_mqtt, + self.async_create_remote, + self.remote) + self.conn_no = self._ifc.get_conn_no() + self.addr = addr + SolarmanV5.__init__(self, server_side, client_mode, self._ifc) + + +class ConnectionG3PClient(ConnectionG3P): + + def __init__(self, reader: StreamReader, writer: StreamWriter, + addr, rstream: 'ConnectionG3PServer') -> None: + + server_side = False + client_mode = False + self.remote = StreamPtr(rstream) + self._ifc = AsyncStreamClient(reader, writer, addr, self.remote) + self.conn_no = self._ifc.get_conn_no() + self.addr = addr + SolarmanV5.__init__(self, server_side, client_mode, self._ifc) diff --git a/app/src/gen3plus/inverter_g3p.py b/app/src/gen3plus/inverter_g3p.py index be32fe1..2c9bca7 100644 --- a/app/src/gen3plus/inverter_g3p.py +++ b/app/src/gen3plus/inverter_g3p.py @@ -8,19 +8,21 @@ from aiomqtt import MqttCodeError if __name__ == "app.src.gen3plus.inverter_g3p": from app.src.config import Config from app.src.inverter import Inverter - from app.src.gen3plus.connection_g3p import ConnectionG3P + from app.src.gen3plus.connection_g3p import ConnectionG3PServer + from app.src.gen3plus.connection_g3p import ConnectionG3PClient from app.src.infos import Infos else: # pragma: no cover from config import Config from inverter import Inverter - from gen3plus.connection_g3p import ConnectionG3P + from gen3plus.connection_g3p import ConnectionG3PServer + from gen3plus.connection_g3p import ConnectionG3PClient from infos import Infos logger_mqtt = logging.getLogger('mqtt') -class InverterG3P(Inverter, ConnectionG3P): +class InverterG3P(Inverter, ConnectionG3PServer): '''class Inverter is a derivation of an Async_Stream The class has some class method for managing common resources like a @@ -53,7 +55,7 @@ class InverterG3P(Inverter, ConnectionG3P): def __init__(self, reader: StreamReader, writer: StreamWriter, addr, client_mode: bool = False): super().__init__(reader, writer, addr, None, - server_side=True, client_mode=client_mode) + client_mode=client_mode) self.__ha_restarts = -1 self.addr = addr @@ -68,9 +70,8 @@ class InverterG3P(Inverter, ConnectionG3P): logging.info(f'[{self.node_id}] Connect to {addr}') connect = asyncio.open_connection(host, port) reader, writer = await connect - self.remote.stream = ConnectionG3P(reader, writer, addr, self, - server_side=False, - client_mode=False) + self.remote.stream = ConnectionG3PClient(reader, writer, + addr, self) logging.info(f'[{self.remote.stream.node_id}:' f'{self.remote.stream.conn_no}] ' f'Connected to {addr}') diff --git a/app/tests/test_connection_g3.py b/app/tests/test_connection_g3.py index b7282e7..e182560 100644 --- a/app/tests/test_connection_g3.py +++ b/app/tests/test_connection_g3.py @@ -5,7 +5,7 @@ import asyncio from itertools import count from mock import patch from app.src.async_stream import AsyncStream, AsyncIfcImpl -from app.src.gen3.connection_g3 import ConnectionG3 +from app.src.gen3.connection_g3 import ConnectionG3Server from app.src.gen3.talent import Talent @pytest.fixture @@ -71,8 +71,8 @@ def test_method_calls(patch_talent_init, patch_healthy, patch_async_close, patch writer = FakeWriter() id_str = "id_string" addr = ('proxy.local', 10000) - conn = ConnectionG3(reader, writer, addr, - rstream= None, server_side=True, id_str=id_str) + conn = ConnectionG3Server(reader, writer, addr, + rstream= None, id_str=id_str) assert 5 == conn._ifc.get_conn_no() spy2.assert_called_once_with(conn, True, conn._ifc, id_str) conn.healthy() diff --git a/app/tests/test_connection_g3p.py b/app/tests/test_connection_g3p.py index 60aac30..85ed802 100644 --- a/app/tests/test_connection_g3p.py +++ b/app/tests/test_connection_g3p.py @@ -6,7 +6,7 @@ from itertools import count from mock import patch from app.src.singleton import Singleton from app.src.async_stream import AsyncStream, AsyncIfcImpl -from app.src.gen3plus.connection_g3p import ConnectionG3P +from app.src.gen3plus.connection_g3p import ConnectionG3PServer from app.src.gen3plus.solarman_v5 import SolarmanV5 @pytest.fixture @@ -76,8 +76,8 @@ def test_method_calls(patch_solarman_init, patch_healthy, patch_async_close, pat reader = FakeReader() writer = FakeWriter() addr = ('proxy.local', 10000) - conn = ConnectionG3P(reader, writer, addr, - rstream= None, server_side=True, client_mode=False) + conn = ConnectionG3PServer(reader, writer, addr, + rstream= None, client_mode=False) assert 5 == conn._ifc.get_conn_no() spy2.assert_called_once_with(conn, True, False, conn._ifc) conn.healthy() diff --git a/app/tests/test_inverter_g3.py b/app/tests/test_inverter_g3.py index 762659a..b4bec04 100644 --- a/app/tests/test_inverter_g3.py +++ b/app/tests/test_inverter_g3.py @@ -8,7 +8,7 @@ from app.src.infos import Infos from app.src.config import Config from app.src.inverter import Inverter from app.src.singleton import Singleton -from app.src.gen3.connection_g3 import ConnectionG3 +from app.src.gen3.connection_g3 import ConnectionG3Server from app.src.gen3.inverter_g3 import InverterG3 from app.tests.test_modbus_tcp import patch_mqtt_err, patch_mqtt_except, test_port, test_hostname @@ -44,12 +44,12 @@ def module_init(): @pytest.fixture def patch_conn_init(): - with patch.object(ConnectionG3, '__init__', return_value= None) as conn: + with patch.object(ConnectionG3Server, '__init__', return_value= None) as conn: yield conn @pytest.fixture def patch_conn_close(): - with patch.object(ConnectionG3, 'close') as conn: + with patch.object(ConnectionG3Server, 'close') as conn: yield conn class FakeReader(): @@ -115,7 +115,7 @@ def test_method_calls(patch_conn_init, patch_conn_close): inverter.r_addr = '' spy1.assert_called_once() - spy1.assert_called_once_with(reader, writer, addr, None, True) + spy1.assert_called_once_with(reader, writer, addr, None) inverter.close() spy2.assert_called_once() diff --git a/app/tests/test_inverter_g3p.py b/app/tests/test_inverter_g3p.py index 26e50ae..0a4946e 100644 --- a/app/tests/test_inverter_g3p.py +++ b/app/tests/test_inverter_g3p.py @@ -8,7 +8,7 @@ from app.src.infos import Infos from app.src.config import Config from app.src.inverter import Inverter from app.src.singleton import Singleton -from app.src.gen3plus.connection_g3p import ConnectionG3P +from app.src.gen3plus.connection_g3p import ConnectionG3PServer from app.src.gen3plus.inverter_g3p import InverterG3P from app.tests.test_modbus_tcp import patch_mqtt_err, patch_mqtt_except, test_port, test_hostname @@ -45,12 +45,12 @@ def module_init(): @pytest.fixture def patch_conn_init(): - with patch.object(ConnectionG3P, '__init__', return_value= None) as conn: + with patch.object(ConnectionG3PServer, '__init__', return_value= None) as conn: yield conn @pytest.fixture def patch_conn_close(): - with patch.object(ConnectionG3P, 'close') as conn: + with patch.object(ConnectionG3PServer, 'close') as conn: yield conn class FakeReader(): @@ -116,7 +116,7 @@ def test_method_calls(patch_conn_init, patch_conn_close): inverter.r_addr = '' spy1.assert_called_once() - spy1.assert_called_once_with(reader, writer, addr, None, server_side=True, client_mode=False) + spy1.assert_called_once_with(reader, writer, addr, None, client_mode=False) inverter.close() spy2.assert_called_once() From 7b6810cb466605b0c7ceda87943463c58ca82197 Mon Sep 17 00:00:00 2001 From: Stefan Allius Date: Fri, 27 Sep 2024 19:25:40 +0200 Subject: [PATCH 11/11] update class diagramm --- app/proxy.svg | 664 +++++++++++++++++++++++++------------------------ app/proxy.yuml | 13 +- 2 files changed, 346 insertions(+), 331 deletions(-) diff --git a/app/proxy.svg b/app/proxy.svg index 240812f..b2ef8ba 100644 --- a/app/proxy.svg +++ b/app/proxy.svg @@ -4,543 +4,559 @@ - + G - + A0 - - - -You can stick notes -on diagrams too! + + + +You can stick notes +on diagrams too! A1 - -Mqtt -<<Singleton>> - -<static>ha_restarts -<static>__client -<static>__cb_MqttIsUp - -<async>publish() -<async>close() + +Mqtt +<<Singleton>> + +<static>ha_restarts +<static>__client +<static>__cb_MqttIsUp + +<async>publish() +<async>close() A2 - -Inverter - -cls.db_stat -cls.entity_prfx -cls.discovery_prfx -cls.proxy_node_id -cls.proxy_unique_id -cls.mqtt:Mqtt - + +Inverter + +cls.db_stat +cls.entity_prfx +cls.discovery_prfx +cls.proxy_node_id +cls.proxy_unique_id +cls.mqtt:Mqtt + A1->A2 - - - + + + A3 - -InverterG3 - -__ha_restarts - -async_create_remote() -async_publ_mqtt() -close() + +InverterG3 + +__ha_restarts + +async_create_remote() +async_publ_mqtt() +close() A2->A3 - - + + A4 - -InverterG3P - -__ha_restarts - -async_create_remote( -)async_publ_mqtt() -close() + +InverterG3P + +__ha_restarts + +async_create_remote( +)async_publ_mqtt() +close() A2->A4 - - + + A5 - -IterRegistry - - -__iter__ + +IterRegistry + + +__iter__ A6 - -Message - -server_side:bool -header_valid:bool -header_len:unsigned -data_len:unsigned -unique_id -node_id -sug_area -_recv_buffer:bytearray -_send_buffer:bytearray -_forward_buffer:bytearray -db:Infos -new_data:list -state - -_read():void<abstract> -close():void -inc_counter():void -dec_counter():void + +Message + +server_side:bool +header_valid:bool +header_len:unsigned +data_len:unsigned +unique_id +node_id +sug_area +_recv_buffer:bytearray +_send_buffer:bytearray +_forward_buffer:bytearray +db:Infos +new_data:list +state + +_read():void<abstract> +close():void +inc_counter():void +dec_counter():void A5->A6 - - + + A21 - -Talent - -await_conn_resp_cnt -id_str -contact_name -contact_mail -db:InfosG3 -mb:Modbus -switch - -msg_contact_info() -msg_ota_update() -msg_get_time() -msg_collector_data() -msg_inverter_data() -msg_unknown() -close() + +Talent + +await_conn_resp_cnt +id_str +contact_name +contact_mail +db:InfosG3 +mb:Modbus +switch + +msg_contact_info() +msg_ota_update() +msg_get_time() +msg_collector_data() +msg_inverter_data() +msg_unknown() +close() A6->A21 - - + + A22 - -SolarmanV5 - -control -serial -snr -db:InfosG3P -mb:Modbus -switch - -msg_unknown() -close() + +SolarmanV5 + +control +serial +snr +db:InfosG3P +mb:Modbus +switch + +msg_unknown() +close() A6->A22 - - + + A7 - -<<AsyncIfc>> - - -set_node_id() -get_conn_no() -tx_add() -tx_flush() -tx_get() -tx_peek() -tx_log() -tx_clear() -tx_len() -fwd_add() -fwd_flush() -fwd_log() -fwd_clear() -rx_get() -rx_peek() -rx_log() -rx_clear() -rx_len() -rx_set_cb() -prot_set_timeout_cb() + +<<AsyncIfc>> + + +set_node_id() +get_conn_no() +tx_add() +tx_flush() +tx_get() +tx_peek() +tx_log() +tx_clear() +tx_len() +fwd_add() +fwd_flush() +fwd_log() +fwd_clear() +rx_get() +rx_peek() +rx_log() +rx_clear() +rx_len() +rx_set_cb() +prot_set_timeout_cb() A8 - -AsyncIfcImpl - -fwd_fifo:ByteFifo -tx_fifo:ByteFifo -rx_fifo:ByteFifo -conn_no:Count -node_id -timeout_cb + +AsyncIfcImpl + +fwd_fifo:ByteFifo +tx_fifo:ByteFifo +rx_fifo:ByteFifo +conn_no:Count +node_id +timeout_cb A7->A8 - - + + A9 - -AsyncStream - -reader -writer -addr -r_addr -l_addr - -<async>loop -disc() -close() -healthy() -__async_read() -__async_write() -__async_forward() + +AsyncStream + +reader +writer +addr +r_addr +l_addr + +<async>loop +disc() +close() +healthy() +__async_read() +__async_write() +__async_forward() A8->A9 - - + + A10 - -AsyncStreamServer - -async_create_remote - -<async>server_loop() -<async>_async_forward() -<async>publish_outstanding_mqtt() -close() + +AsyncStreamServer + +async_create_remote + +<async>server_loop() +<async>_async_forward() +<async>publish_outstanding_mqtt() +close() A9->A10 - - + + A11 - -AsyncStreamClient - - -<async>client_loop() -<async>_async_forward()) + +AsyncStreamClient + + +<async>client_loop() +<async>_async_forward()) A9->A11 - - + + A12 - -ConnectionG3 - -remote.stream:ConnectionG3 - -healthy() + +ConnectionG3 + +remote.stream:ConnectionG3 + +healthy() A13 - -ConnectionG3Client + +ConnectionG3Client + +remote.stream:ConnectionG3 + +close() A12->A13 - - + + A14 - -ConnectionG3Server + +ConnectionG3Server + +remote.stream:ConnectionG3 + +close() A12->A14 - - - - - -A13->A3 - - + + A13->A11 - - - -1 + + + +1 + + + +A13->A14 + + A14->A3 - - + + A14->A10 - - - -1 + + + +1 A15 - -ConnectionG3P - -remote.stream:ConnectionG3P - -healthy() -close() + +ConnectionG3P + +remote.stream:ConnectionG3P + +healthy() +close() A16 - -ConnectionG3PClient + +ConnectionG3PClient + +remote.stream:ConnectionG3P + +close() A15->A16 - - + + A17 - -ConnectionG3PServer + +ConnectionG3PServer + +remote.stream:ConnectionG3P + +close() A15->A17 - - - - - -A16->A4 - - + + A16->A11 - - - -1 + + + +1 + + + +A16->A17 + + A17->A4 - - + + A17->A10 - - - -1 + + + +1 A18 - -Infos - -stat -new_stat_data -info_dev - -static_init() -dev_value() -inc_counter() -dec_counter() -ha_proxy_conf -ha_conf -ha_remove -update_db -set_db_def_value -get_db_value -ignore_this_device + +Infos + +stat +new_stat_data +info_dev + +static_init() +dev_value() +inc_counter() +dec_counter() +ha_proxy_conf +ha_conf +ha_remove +update_db +set_db_def_value +get_db_value +ignore_this_device A19 - -InfosG3 - - -ha_confs() -parse() + +InfosG3 + + +ha_confs() +parse() A18->A19 - - + + A20 - -InfosG3P - - -ha_confs() -parse() + +InfosG3P + + +ha_confs() +parse() A18->A20 - - + + A21->A7 - - -use + + +use A21->A12 - - + + A21->A19 - - + + A22->A7 - - -use + + +use A22->A15 - - + + A22->A20 - - + + A23 - -Modbus - -que -snd_handler -rsp_handler -timeout -max_retires -last_xxx -err -retry_cnt -req_pend -tim - -build_msg() -recv_req() -recv_resp() -close() + +Modbus + +que +snd_handler +rsp_handler +timeout +max_retires +last_xxx +err +retry_cnt +req_pend +tim + +build_msg() +recv_req() +recv_resp() +close() A23->A21 - - -has -1 + + +has +1 A23->A22 - - -has -1 + + +has +1 @@ -557,10 +573,10 @@ A24->A4 - - -1 -has + + +1 +has diff --git a/app/proxy.yuml b/app/proxy.yuml index af14779..c750340 100644 --- a/app/proxy.yuml +++ b/app/proxy.yuml @@ -24,23 +24,22 @@ [ConnectionG3|remote.stream:ConnectionG3|healthy()] +[ConnectionG3Client|remote.stream:ConnectionG3|close()] +[ConnectionG3Server|remote.stream:ConnectionG3|;close()] [ConnectionG3]^[ConnectionG3Client] [ConnectionG3]^[ConnectionG3Server] -[ConnectionG3Client|remote.stream:ConnectionG3|close()] -[ConnectionG3Client]^[InverterG3] +[ConnectionG3Client]<-[ConnectionG3Server] [ConnectionG3Client]++-1>[AsyncStreamClient] -[ConnectionG3Server|remote.stream:ConnectionG3|;close()] [ConnectionG3Server]^[InverterG3] [ConnectionG3Server]++-1>[AsyncStreamServer] [ConnectionG3P|remote.stream:ConnectionG3P|healthy();close()] +[ConnectionG3PClient|remote.stream:ConnectionG3P|close()] +[ConnectionG3PServer|remote.stream:ConnectionG3P|;close()] [ConnectionG3P]^[ConnectionG3PClient] [ConnectionG3P]^[ConnectionG3PServer] - -[ConnectionG3PClient|remote.stream:ConnectionG3P|close()] -[ConnectionG3PClient]^[InverterG3P] +[ConnectionG3PClient]<-[ConnectionG3PServer] [ConnectionG3PClient]++-1>[AsyncStreamClient] -[ConnectionG3PServer|remote.stream:ConnectionG3P|;close()] [ConnectionG3PServer]^[InverterG3P] [ConnectionG3PServer]++-1>[AsyncStreamServer]