diff --git a/app/docu/proxy_3.svg b/app/docu/proxy_3.svg index 8be4969..aadb6fe 100644 --- a/app/docu/proxy_3.svg +++ b/app/docu/proxy_3.svg @@ -4,11 +4,11 @@ - + G - + A0 @@ -47,78 +47,79 @@ A2 - -InverterG3P - -addr -remote:StreamPtr -local:StreamPtr - -create_remote() -close() + +InverterG3P + +addr +forward_at_cmd_resp +remote:StreamPtr +local:StreamPtr + +create_remote() +close() A3 - -local:StreamPtr + +local:StreamPtr A2->A3 - - - + + + A4 - -remote:StreamPtr + +remote:StreamPtr A2->A4 - - - + + + A8 - -AsyncStreamServer - -create_remote - -<async>server_loop() -<async>_async_forward() -<async>publish_outstanding_mqtt() -close() + +AsyncStreamServer + +create_remote + +<async>server_loop() +<async>_async_forward() +<async>publish_outstanding_mqtt() +close() A3->A8 - - - + + + A9 - -AsyncStreamClient - - -<async>client_loop() -<async>_async_forward()) + +AsyncStreamClient + + +<async>client_loop() +<async>_async_forward()) A4->A9 - - -0..1 + + +0..1 @@ -149,138 +150,139 @@ A6 - -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 A5->A6 - - + + A7 - -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() A6->A7 - - + + A7->A8 - - + + A7->A9 - - + + A10 - -SolarmanV5 - -conn_no -addr -control -serial -snr -db:InfosG3P -switch - -msg_unknown() -healthy() -close() + +SolarmanV5 + +conn_no +addr +inverter:InverterG3P +control +serial +snr +db:InfosG3P +switch + +msg_unknown() +healthy() +close() A10->A3 - - - + + + A10->A4 - - -0..1 + + +0..1 A12 - -InfosG3P - -client_mode:bool - -ha_confs() -parse() -calc() -build() + +InfosG3P + +client_mode:bool + +ha_confs() +parse() +calc() +build() A10->A12 - - + + A11 - -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 A11->A12 - - + + @@ -324,8 +326,8 @@ A13->A10 - - + + diff --git a/app/docu/proxy_3.yuml b/app/docu/proxy_3.yuml index 3703658..4602240 100644 --- a/app/docu/proxy_3.yuml +++ b/app/docu/proxy_3.yuml @@ -5,7 +5,7 @@ [note: Example of instantiation for a GEN3PLUS inverter!{bg:cornsilk}] [<>||__iter__()] -[InverterG3P|addr;remote:StreamPtr;local:StreamPtr|create_remote();;close()] +[InverterG3P|addr;forward_at_cmd_resp;remote:StreamPtr;local:StreamPtr|create_remote();;close()] [InverterG3P]++->[local:StreamPtr] [InverterG3P]++->[remote:StreamPtr] @@ -19,7 +19,7 @@ [AsyncStream]^[AsyncStreamServer] [AsyncStream]^[AsyncStreamClient] -[SolarmanV5|conn_no;addr;;control;serial;snr;db:InfosG3P;switch|msg_unknown();;healthy();close()] +[SolarmanV5|conn_no;addr;inverter:InverterG3P;control;serial;snr;db:InfosG3P;switch|msg_unknown();;healthy();close()] [SolarmanV5]<-++[local:StreamPtr] [local:StreamPtr]++->[AsyncStreamServer] [SolarmanV5]<-0..1[remote:StreamPtr] diff --git a/app/src/gen3/talent.py b/app/src/gen3/talent.py index e799a36..292dfdf 100644 --- a/app/src/gen3/talent.py +++ b/app/src/gen3/talent.py @@ -34,10 +34,11 @@ class Control: class Talent(Message): TXT_UNKNOWN_CTRL = 'Unknown Ctrl' - def __init__(self, addr, ifc: "AsyncIfc", server_side: bool, + def __init__(self, inverter, addr, ifc: "AsyncIfc", server_side: bool, client_mode: bool = False, id_str=b''): super().__init__('G3', ifc, server_side, self.send_modbus_cb, mb_timeout=15) + _ = inverter 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) diff --git a/app/src/gen3plus/infos_g3p.py b/app/src/gen3plus/infos_g3p.py index 1cbbc18..fd2c97c 100644 --- a/app/src/gen3plus/infos_g3p.py +++ b/app/src/gen3plus/infos_g3p.py @@ -193,13 +193,12 @@ class RegisterSel: class InfosG3P(Infos): - __slots__ = ('client_mode', 'forward_at_cmd_resp') + __slots__ = ('client_mode') def __init__(self, client_mode: bool): super().__init__() self.client_mode = client_mode # shared value between both inverter connections - self.forward_at_cmd_resp = False '''Flag if response for the last at command must be send to the cloud. False: send result only to the MQTT broker, cause the AT+ command diff --git a/app/src/gen3plus/inverter_g3p.py b/app/src/gen3plus/inverter_g3p.py index f3680c9..2461d46 100644 --- a/app/src/gen3plus/inverter_g3p.py +++ b/app/src/gen3plus/inverter_g3p.py @@ -9,6 +9,7 @@ class InverterG3P(InverterBase): def __init__(self, reader: StreamReader, writer: StreamWriter, client_mode: bool = False): remote_prot = None + self.forward_at_cmd_resp = False if client_mode: remote_prot = SolarmanEmu super().__init__(reader, writer, 'solarman', diff --git a/app/src/gen3plus/solarman_emu.py b/app/src/gen3plus/solarman_emu.py index 7462388..93bb874 100644 --- a/app/src/gen3plus/solarman_emu.py +++ b/app/src/gen3plus/solarman_emu.py @@ -10,11 +10,12 @@ logger = logging.getLogger('msg') class SolarmanEmu(SolarmanBase): - def __init__(self, addr, ifc: "AsyncIfc", + def __init__(self, inverter, addr, ifc: "AsyncIfc", server_side: bool, client_mode: bool): super().__init__(addr, ifc, server_side=False, _send_modbus_cb=None, mb_timeout=8) + _ = inverter logging.debug('SolarmanEmu.init()') self.db = ifc.remote.stream.db self.snr = ifc.remote.stream.snr diff --git a/app/src/gen3plus/solarman_v5.py b/app/src/gen3plus/solarman_v5.py index 3fb2205..7c8b997 100644 --- a/app/src/gen3plus/solarman_v5.py +++ b/app/src/gen3plus/solarman_v5.py @@ -253,11 +253,12 @@ class SolarmanV5(SolarmanBase): HDR_FMT = ' we get a memory leak + self.inverter = None self.switch.clear() self.log_lvl.clear() super().close() @@ -518,7 +520,7 @@ class SolarmanV5(SolarmanBase): await Proxy.mqtt.publish(f'{Proxy.entity_prfx}{node_id}{key}', data_json) # noqa: E501 return - self.db.forward_at_cmd_resp = False + self.inverter.forward_at_cmd_resp = False self._build_header(0x4510) self.ifc.tx_add(struct.pack(f' None: + """forward handler transmits data over the remote connection""" + # dst.ifc.update_header_cb(src.fwd_fifo.peek()) + + dst.ifc.tx_add(src.ifc.fwd_fifo.get()) + + +@pytest.mark.asyncio +async def test_proxy_at_cmd(config_tsun_inv1, patch_open_connection, at_command_ind_msg, at_command_rsp_msg): + _ = config_tsun_inv1 + _ = patch_open_connection + assert asyncio.get_running_loop() + + with InverterTest(FakeReader(), FakeWriter(), client_mode=False) as inverter: + await inverter.create_remote() + await asyncio.sleep(0) + r = inverter.remote.stream + l = inverter.local.stream + + l.db.stat['proxy']['AT_Command'] = 0 + l.db.stat['proxy']['Unknown_Ctrl'] = 0 + l.db.stat['proxy']['AT_Command_Blocked'] = 0 + l.db.stat['proxy']['Modbus_Command'] = 0 + inverter.forward_at_cmd_resp = False + r.append_msg(at_command_ind_msg) + r.read() # read complete msg, and dispatch msg + assert inverter.forward_at_cmd_resp + inverter.forward(r,l) + + assert l.ifc.tx_fifo.get()==at_command_ind_msg + + assert l.db.stat['proxy']['Invalid_Msg_Format'] == 0 + assert l.db.stat['proxy']['AT_Command'] == 1 + assert l.db.stat['proxy']['AT_Command_Blocked'] == 0 + assert l.db.stat['proxy']['Modbus_Command'] == 0 + + l.append_msg(at_command_rsp_msg) + l.read() # read at resp + assert l.ifc.fwd_fifo.peek()==at_command_rsp_msg + inverter.forward(l,r) + assert r.ifc.tx_fifo.get()==at_command_rsp_msg + + assert Proxy.mqtt.key == '' + assert Proxy.mqtt.data == "" + +@pytest.mark.asyncio +async def test_proxy_at_blocked(config_tsun_inv1, patch_open_connection, at_command_ind_msg_block, at_command_rsp_msg): + _ = config_tsun_inv1 + _ = patch_open_connection + assert asyncio.get_running_loop() + + with InverterTest(FakeReader(), FakeWriter(), client_mode=False) as inverter: + await inverter.create_remote() + await asyncio.sleep(0) + r = inverter.remote.stream + l = inverter.local.stream + + l.db.stat['proxy']['AT_Command'] = 0 + l.db.stat['proxy']['Unknown_Ctrl'] = 0 + l.db.stat['proxy']['AT_Command_Blocked'] = 0 + l.db.stat['proxy']['Modbus_Command'] = 0 + inverter.forward_at_cmd_resp = False + r.append_msg(at_command_ind_msg_block) + r.read() # read complete msg, and dispatch msg + assert not inverter.forward_at_cmd_resp + inverter.forward(r,l) + + assert l.ifc.tx_fifo.get()==b'' + + assert l.db.stat['proxy']['Invalid_Msg_Format'] == 0 + assert l.db.stat['proxy']['AT_Command'] == 0 + assert l.db.stat['proxy']['AT_Command_Blocked'] == 1 + assert l.db.stat['proxy']['Modbus_Command'] == 0 + + l.append_msg(at_command_rsp_msg) + l.read() # read at resp + assert l.ifc.fwd_fifo.peek()==b'' + inverter.forward(l,r) + assert r.ifc.tx_fifo.get()==b'' + + assert Proxy.mqtt.key == 'tsun/inv1/at_resp' + assert Proxy.mqtt.data == "+ok" diff --git a/app/tests/test_solarman_emu.py b/app/tests/test_solarman_emu.py index 41e0e48..a62fbdc 100644 --- a/app/tests/test_solarman_emu.py +++ b/app/tests/test_solarman_emu.py @@ -6,7 +6,7 @@ from gen3plus.solarman_v5 import SolarmanV5, SolarmanBase from gen3plus.solarman_emu import SolarmanEmu from infos import Infos, Register -from test_solarman import FakeIfc, MemoryStream, get_sn_int, get_sn, correct_checksum, config_tsun_inv1, msg_modbus_rsp +from test_solarman import FakeIfc, FakeInverter, MemoryStream, get_sn_int, get_sn, correct_checksum, config_tsun_inv1, msg_modbus_rsp from test_infos_g3p import str_test_ip, bytes_test_ip timestamp = 0x3224c8bc @@ -19,10 +19,10 @@ class InvStream(MemoryStream): return timestamp class CldStream(SolarmanEmu): - def __init__(self, inv: InvStream): + def __init__(self, inv: InvStream, inverter=FakeInverter()): _ifc = FakeIfc() _ifc.remote.stream = inv - super().__init__(('test.local', 1234), _ifc, server_side=False, client_mode=False) + super().__init__(inverter, ('test.local', 1234), _ifc, server_side=False, client_mode=False) self.__msg = b'' self.__msg_len = 0 self.__offs = 0 diff --git a/app/tests/test_talent.py b/app/tests/test_talent.py index 2e7a4f0..225c38e 100644 --- a/app/tests/test_talent.py +++ b/app/tests/test_talent.py @@ -25,7 +25,7 @@ class FakeIfc(AsyncIfcImpl): class MemoryStream(Talent): def __init__(self, msg, chunks = (0,), server_side: bool = True): self.ifc = FakeIfc() - super().__init__(('test.local', 1234), self.ifc, server_side) + super().__init__(None, ('test.local', 1234), self.ifc, server_side) if server_side: self.mb.timeout = 0.4 # overwrite for faster testing self.mb_first_timeout = 0.5 @@ -2026,9 +2026,9 @@ def test_ctrl_byte(): def test_msg_iterator(): - m1 = Talent(('test1.local', 1234), ifc=AsyncIfcImpl(), server_side=True) - m2 = Talent(('test2.local', 1234), ifc=AsyncIfcImpl(), server_side=True) - m3 = Talent(('test3.local', 1234), ifc=AsyncIfcImpl(), server_side=True) + m1 = Talent(None, ('test1.local', 1234), ifc=AsyncIfcImpl(), server_side=True) + m2 = Talent(None, ('test2.local', 1234), ifc=AsyncIfcImpl(), server_side=True) + m3 = Talent(None, ('test3.local', 1234), ifc=AsyncIfcImpl(), server_side=True) m3.close() del m3 test1 = 0