S allius/issue334 (#335)

* move forward_at_cmd_resp into InfosG3P class

- the variable is shared between the two connections
of an inverter. One is for the TSUN cloud and the
other for the device.

* use inverter class to share values between
the two protocol instances of a proxy
- move forward_at_cmd_resp into class InverterG3P
- store inverter ptr in Solarman_V5 instances
- add inverter ptr to all constructurs of protocols
- adapt doku and unit tests-
- add integration tests for AT+ commands which
  check the forwarding from and to the TSUN cloud

* adapt and improve the unit tests
- fix node_id declaration, which always has a / at
  the end. See config grammar for this rule
- set global var test to default after test run
This commit is contained in:
Stefan Allius
2025-04-05 14:37:52 +02:00
committed by GitHub
parent 4988a29a34
commit 6974672ba0
16 changed files with 912 additions and 739 deletions

View File

@@ -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)

View File

@@ -8,6 +8,15 @@ from gen3plus.solarman_emu import SolarmanEmu
class InverterG3P(InverterBase):
def __init__(self, reader: StreamReader, writer: StreamWriter,
client_mode: bool = False):
# 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
came from there
True: send response packet to the cloud, cause the AT+ command
came from the cloud'''
remote_prot = None
if client_mode:
remote_prot = SolarmanEmu

View File

@@ -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

View File

@@ -253,13 +253,13 @@ class SolarmanV5(SolarmanBase):
HDR_FMT = '<BLLL'
'''format string for packing of the header'''
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, self.send_modbus_cb,
mb_timeout=8)
self.inverter = inverter
self.db = InfosG3P(client_mode)
self.forward_at_cmd_resp = False
self.no_forwarding = False
'''not allowed to connect to TSUN cloud by connection type'''
self.establish_inv_emu = False
@@ -335,6 +335,7 @@ class SolarmanV5(SolarmanBase):
# we have references to methods of this class in self.switch
# so we have to erase self.switch, otherwise this instance can't be
# deallocated by the garbage collector ==> we get a memory leak
self.inverter = None
self.switch.clear()
self.log_lvl.clear()
super().close()
@@ -519,7 +520,7 @@ class SolarmanV5(SolarmanBase):
await Proxy.mqtt.publish(f'{Proxy.entity_prfx}{node_id}{key}', data_json) # noqa: E501
return
self.forward_at_cmd_resp = False
self.inverter.forward_at_cmd_resp = False
self._build_header(0x4510)
self.ifc.tx_add(struct.pack(f'<BHLLL{len(at_cmd)}sc', self.AT_CMD,
0x0002, 0, 0, 0,
@@ -644,7 +645,7 @@ class SolarmanV5(SolarmanBase):
self.inc_counter('AT_Command_Blocked')
return
self.inc_counter('AT_Command')
self.forward_at_cmd_resp = True
self.inverter.forward_at_cmd_resp = True
elif ftype == self.MB_RTU_CMD:
rstream = self.ifc.remote.stream
@@ -664,8 +665,9 @@ class SolarmanV5(SolarmanBase):
def get_cmd_rsp_log_lvl(self) -> int:
ftype = self.ifc.rx_peek()[self.header_len]
if ftype == self.AT_CMD:
if self.forward_at_cmd_resp:
if ftype == self.AT_CMD or \
ftype == self.AT_CMD_RSP:
if self.inverter.forward_at_cmd_resp:
return logging.INFO
return logging.DEBUG
elif ftype == self.MB_RTU_CMD \
@@ -680,7 +682,7 @@ class SolarmanV5(SolarmanBase):
ftype = data[0]
if ftype == self.AT_CMD or \
ftype == self.AT_CMD_RSP:
if not self.forward_at_cmd_resp:
if not self.inverter.forward_at_cmd_resp:
data_json = data[14:].decode("utf-8")
node_id = self.node_id
key = 'at_resp'

View File

@@ -41,7 +41,7 @@ class InverterBase(InverterIfc, Proxy):
self.remote)
self.local = StreamPtr(
prot_class(self.addr, ifc, True, client_mode), ifc
prot_class(self, self.addr, ifc, True, client_mode), ifc
)
def __enter__(self):
@@ -122,11 +122,11 @@ class InverterBase(InverterIfc, Proxy):
self.remote.ifc = ifc
if hasattr(stream, 'id_str'):
self.remote.stream = self.prot_class(
addr, ifc, server_side=False,
self, addr, ifc, server_side=False,
client_mode=False, id_str=stream.id_str)
else:
self.remote.stream = self.prot_class(
addr, ifc, server_side=False,
self, addr, ifc, server_side=False,
client_mode=False)
logging.info(f'[{self.remote.stream.node_id}:'