Make test code more clean (#149)

* cleanup
This commit is contained in:
Stefan Allius
2024-08-09 00:02:01 +02:00
committed by GitHub
parent 24ece4fece
commit 155b6d4e67
14 changed files with 551 additions and 683 deletions

View File

@@ -188,7 +188,7 @@
<polygon fill="none" stroke="#000000" points="410.5,-330 410.5,-362 560.5,-362 560.5,-330 410.5,-330"/> <polygon fill="none" stroke="#000000" points="410.5,-330 410.5,-362 560.5,-362 560.5,-330 410.5,-330"/>
<text text-anchor="start" x="453.5455" y="-343" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">ConnectionG3</text> <text text-anchor="start" x="453.5455" y="-343" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">ConnectionG3</text>
<polygon fill="none" stroke="#000000" points="410.5,-298 410.5,-330 560.5,-330 560.5,-298 410.5,-298"/> <polygon fill="none" stroke="#000000" points="410.5,-298 410.5,-330 560.5,-330 560.5,-298 410.5,-298"/>
<text text-anchor="start" x="420.487" y="-311" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">remoteStream:ConnectionG3</text> <text text-anchor="start" x="420.487" y="-311" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">remote_stream:ConnectionG3</text>
<polygon fill="none" stroke="#000000" points="410.5,-254 410.5,-298 560.5,-298 560.5,-254 410.5,-254"/> <polygon fill="none" stroke="#000000" points="410.5,-254 410.5,-298 560.5,-298 560.5,-254 410.5,-254"/>
<text text-anchor="start" x="466.054" y="-279" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">healthy()</text> <text text-anchor="start" x="466.054" y="-279" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">healthy()</text>
<text text-anchor="start" x="470.5025" y="-267" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text> <text text-anchor="start" x="470.5025" y="-267" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>
@@ -213,7 +213,7 @@
<polygon fill="none" stroke="#000000" points="125.5,-330 125.5,-362 281.5,-362 281.5,-330 125.5,-330"/> <polygon fill="none" stroke="#000000" points="125.5,-330 125.5,-362 281.5,-362 281.5,-330 125.5,-330"/>
<text text-anchor="start" x="168.211" y="-343" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">ConnectionG3P</text> <text text-anchor="start" x="168.211" y="-343" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">ConnectionG3P</text>
<polygon fill="none" stroke="#000000" points="125.5,-298 125.5,-330 281.5,-330 281.5,-298 125.5,-298"/> <polygon fill="none" stroke="#000000" points="125.5,-298 125.5,-330 281.5,-330 281.5,-298 125.5,-298"/>
<text text-anchor="start" x="135.1525" y="-311" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">remoteStream:ConnectionG3P</text> <text text-anchor="start" x="135.1525" y="-311" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">remote_stream:ConnectionG3P</text>
<polygon fill="none" stroke="#000000" points="125.5,-254 125.5,-298 281.5,-298 281.5,-254 125.5,-254"/> <polygon fill="none" stroke="#000000" points="125.5,-254 125.5,-298 281.5,-298 281.5,-254 125.5,-254"/>
<text text-anchor="start" x="184.054" y="-279" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">healthy()</text> <text text-anchor="start" x="184.054" y="-279" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">healthy()</text>
<text text-anchor="start" x="188.5025" y="-267" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text> <text text-anchor="start" x="188.5025" y="-267" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">close()</text>

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -8,9 +8,9 @@
[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<abstract>;close():void;inc_counter():void;dec_counter():void] [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<abstract>;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]^[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()] [Message]^[SolarmanV5|control;serial;snr;db:InfosG3P;mb:Modbus;switch|msg_unknown();;close()]
[Talent]^[ConnectionG3|remoteStream:ConnectionG3|healthy();close()] [Talent]^[ConnectionG3|remote_stream:ConnectionG3|healthy();close()]
[Talent]has-1>[Modbus] [Talent]has-1>[Modbus]
[SolarmanV5]^[ConnectionG3P|remoteStream:ConnectionG3P|healthy();close()] [SolarmanV5]^[ConnectionG3P|remote_stream:ConnectionG3P|healthy();close()]
[SolarmanV5]has-1>[Modbus] [SolarmanV5]has-1>[Modbus]
[AsyncStream|reader;writer;addr;r_addr;l_addr|<async>server_loop();<async>client_loop();<async>loop;disc();close();;__async_read();async_write();__async_forward()]^[ConnectionG3] [AsyncStream|reader;writer;addr;r_addr;l_addr|<async>server_loop();<async>client_loop();<async>loop;disc();close();;__async_read();async_write();__async_forward()]^[ConnectionG3]
[AsyncStream]^[ConnectionG3P] [AsyncStream]^[ConnectionG3P]

View File

@@ -67,18 +67,18 @@ class AsyncStream():
# if the server connection closes, we also have to disconnect # if the server connection closes, we also have to disconnect
# the connection to te TSUN cloud # the connection to te TSUN cloud
if self.remoteStream: if self.remote_stream:
logger.info(f'[{self.node_id}:{self.conn_no}] disc client ' logger.info(f'[{self.node_id}:{self.conn_no}] disc client '
f'connection: [{self.remoteStream.node_id}:' f'connection: [{self.remote_stream.node_id}:'
f'{self.remoteStream.conn_no}]') f'{self.remote_stream.conn_no}]')
await self.remoteStream.disc() await self.remote_stream.disc()
async def client_loop(self, addr: str) -> None: async def client_loop(self, _: str) -> None:
'''Loop for receiving messages from the TSUN cloud (client-side)''' '''Loop for receiving messages from the TSUN cloud (client-side)'''
clientStream = await self.remoteStream.loop() client_stream = await self.remote_stream.loop()
logger.info(f'[{clientStream.node_id}:{clientStream.conn_no}] ' logger.info(f'[{client_stream.node_id}:{client_stream.conn_no}] '
'Client loop stopped for' 'Client loop stopped for'
f' l{clientStream.l_addr}') f' l{client_stream.l_addr}')
# if the client connection closes, we don't touch the server # if the client connection closes, we don't touch the server
# connection. Instead we erase the client connection stream, # connection. Instead we erase the client connection stream,
@@ -86,13 +86,13 @@ class AsyncStream():
# establish a new connection to the TSUN cloud # establish a new connection to the TSUN cloud
# erase backlink to inverter # erase backlink to inverter
clientStream.remoteStream = None client_stream.remote_stream = None
if self.remoteStream == clientStream: if self.remote_stream == client_stream:
# logging.debug(f'Client l{clientStream.l_addr} refs:' # logging.debug(f'Client l{client_stream.l_addr} refs:'
# f' {gc.get_referrers(clientStream)}') # f' {gc.get_referrers(client_stream)}')
# than erase client connection # than erase client connection
self.remoteStream = None self.remote_stream = None
async def loop(self) -> Self: async def loop(self) -> Self:
"""Async loop handler for precessing all received messages""" """Async loop handler for precessing all received messages"""
@@ -203,35 +203,35 @@ class AsyncStream():
if not self._forward_buffer: if not self._forward_buffer:
return return
try: try:
if not self.remoteStream: if not self.remote_stream:
await self.async_create_remote() await self.async_create_remote()
if self.remoteStream: if self.remote_stream:
if self.remoteStream._init_new_client_conn(): if self.remote_stream._init_new_client_conn():
await self.remoteStream.async_write() await self.remote_stream.async_write()
if self.remoteStream: if self.remote_stream:
self.remoteStream._update_header(self._forward_buffer) self.remote_stream._update_header(self._forward_buffer)
hex_dump_memory(logging.INFO, hex_dump_memory(logging.INFO,
f'Forward to {self.remoteStream.addr}:', f'Forward to {self.remote_stream.addr}:',
self._forward_buffer, self._forward_buffer,
len(self._forward_buffer)) len(self._forward_buffer))
self.remoteStream.writer.write(self._forward_buffer) self.remote_stream.writer.write(self._forward_buffer)
await self.remoteStream.writer.drain() await self.remote_stream.writer.drain()
self._forward_buffer = bytearray(0) self._forward_buffer = bytearray(0)
except OSError as error: except OSError as error:
if self.remoteStream: if self.remote_stream:
rmt = self.remoteStream rmt = self.remote_stream
self.remoteStream = None self.remote_stream = None
logger.error(f'[{rmt.node_id}:{rmt.conn_no}] Fwd: {error} for ' logger.error(f'[{rmt.node_id}:{rmt.conn_no}] Fwd: {error} for '
f'l{rmt.l_addr} | r{rmt.r_addr}') f'l{rmt.l_addr} | r{rmt.r_addr}')
await rmt.disc() await rmt.disc()
rmt.close() rmt.close()
except RuntimeError as error: except RuntimeError as error:
if self.remoteStream: if self.remote_stream:
rmt = self.remoteStream rmt = self.remote_stream
self.remoteStream = None self.remote_stream = None
logger.info(f'[{rmt.node_id}:{rmt.conn_no}] ' logger.info(f'[{rmt.node_id}:{rmt.conn_no}] '
f'Fwd: {error} for {rmt.l_addr}') f'Fwd: {error} for {rmt.l_addr}')
await rmt.disc() await rmt.disc()

View File

@@ -1,5 +1,4 @@
import logging import logging
# import gc
from asyncio import StreamReader, StreamWriter from asyncio import StreamReader, StreamWriter
from async_stream import AsyncStream from async_stream import AsyncStream
from gen3.talent import Talent from gen3.talent import Talent
@@ -15,7 +14,7 @@ class ConnectionG3(AsyncStream, Talent):
AsyncStream.__init__(self, reader, writer, addr) AsyncStream.__init__(self, reader, writer, addr)
Talent.__init__(self, server_side, id_str) Talent.__init__(self, server_side, id_str)
self.remoteStream: 'ConnectionG3' = remote_stream self.remote_stream: 'ConnectionG3' = remote_stream
''' '''
Our puplic methods Our puplic methods
@@ -26,10 +25,10 @@ class ConnectionG3(AsyncStream, Talent):
# logger.info(f'AsyncStream refs: {gc.get_referrers(self)}') # logger.info(f'AsyncStream refs: {gc.get_referrers(self)}')
async def async_create_remote(self) -> None: async def async_create_remote(self) -> None:
pass pass # virtual interface
async def async_publ_mqtt(self) -> None: async def async_publ_mqtt(self) -> None:
pass pass # virtual interface
def healthy(self) -> bool: def healthy(self) -> bool:
logger.debug('ConnectionG3 healthy()') logger.debug('ConnectionG3 healthy()')

View File

@@ -9,9 +9,7 @@ from gen3.connection_g3 import ConnectionG3
from aiomqtt import MqttCodeError from aiomqtt import MqttCodeError
from infos import Infos from infos import Infos
# import gc
# logger = logging.getLogger('conn')
logger_mqtt = logging.getLogger('mqtt') logger_mqtt = logging.getLogger('mqtt')
@@ -60,10 +58,10 @@ class InverterG3(Inverter, ConnectionG3):
logging.info(f'[{self.node_id}] Connect to {addr}') logging.info(f'[{self.node_id}] Connect to {addr}')
connect = asyncio.open_connection(host, port) connect = asyncio.open_connection(host, port)
reader, writer = await connect reader, writer = await connect
self.remoteStream = ConnectionG3(reader, writer, addr, self, self.remote_stream = ConnectionG3(reader, writer, addr, self,
False, self.id_str) False, self.id_str)
logging.info(f'[{self.remoteStream.node_id}:' logging.info(f'[{self.remote_stream.node_id}:'
f'{self.remoteStream.conn_no}] ' f'{self.remote_stream.conn_no}] '
f'Connected to {addr}') f'Connected to {addr}')
asyncio.create_task(self.client_loop(addr)) asyncio.create_task(self.client_loop(addr))

View File

@@ -1,6 +1,5 @@
import struct import struct
import logging import logging
import time
import pytz import pytz
from datetime import datetime from datetime import datetime
from tzlocal import get_localzone from tzlocal import get_localzone
@@ -169,7 +168,6 @@ class Talent(Message):
fnc = self.switch.get(self.msg_id, self.msg_unknown) fnc = self.switch.get(self.msg_id, self.msg_unknown)
logger.info(self.__flow_str(self.server_side, 'forwrd') + logger.info(self.__flow_str(self.server_side, 'forwrd') +
f' Ctl: {int(self.ctrl):#02x} Msg: {fnc.__name__!r}') f' Ctl: {int(self.ctrl):#02x} Msg: {fnc.__name__!r}')
return
def send_modbus_cb(self, modbus_pdu: bytearray, log_lvl: int, state: str): def send_modbus_cb(self, modbus_pdu: bytearray, log_lvl: int, state: str):
if self.state != State.up: if self.state != State.up:
@@ -242,12 +240,8 @@ class Talent(Message):
def _timestamp(self): # pragma: no cover def _timestamp(self): # pragma: no cover
'''returns timestamp fo the inverter as localtime '''returns timestamp fo the inverter as localtime
since 1.1.1970 in msec''' since 1.1.1970 in msec'''
if False: # convert localtime in epoche
# utc as epoche ts = (datetime.now() - datetime(1970, 1, 1)).total_seconds()
ts = time.time()
else:
# convert localtime in epoche
ts = (datetime.now() - datetime(1970, 1, 1)).total_seconds()
return round(ts*1000) return round(ts*1000)
def _utcfromts(self, ts: float): def _utcfromts(self, ts: float):
@@ -297,7 +291,7 @@ class Talent(Message):
if (buf_len < 5): # enough bytes to read len and id_len? if (buf_len < 5): # enough bytes to read len and id_len?
return return
result = struct.unpack_from('!lB', buf, 0) result = struct.unpack_from('!lB', buf, 0)
len = result[0] # len of complete message msg_len = result[0] # len of complete message
id_len = result[1] # len of variable id string id_len = result[1] # len of variable id string
hdr_len = 5+id_len+2 hdr_len = 5+id_len+2
@@ -311,10 +305,9 @@ class Talent(Message):
self.id_str = result[0] self.id_str = result[0]
self.ctrl = Control(result[1]) self.ctrl = Control(result[1])
self.msg_id = result[2] self.msg_id = result[2]
self.data_len = len-id_len-3 self.data_len = msg_len-id_len-3
self.header_len = hdr_len self.header_len = hdr_len
self.header_valid = True self.header_valid = True
return
def __build_header(self, ctrl, msg_id=None) -> None: def __build_header(self, ctrl, msg_id=None) -> None:
if not msg_id: if not msg_id:
@@ -360,7 +353,6 @@ class Talent(Message):
self.await_conn_resp_cnt -= 1 self.await_conn_resp_cnt -= 1
else: else:
self.forward() self.forward()
return
else: else:
logger.warning('Unknown Ctrl') logger.warning('Unknown Ctrl')
self.inc_counter('Unknown_Ctrl') self.inc_counter('Unknown_Ctrl')
@@ -477,7 +469,7 @@ class Talent(Message):
if self.ctrl.is_req(): if self.ctrl.is_req():
self.inc_counter('OTA_Start_Msg') self.inc_counter('OTA_Start_Msg')
elif self.ctrl.is_ind(): elif self.ctrl.is_ind():
pass pass # Ok, nothing to do
else: else:
logger.warning('Unknown Ctrl') logger.warning('Unknown Ctrl')
self.inc_counter('Unknown_Ctrl') self.inc_counter('Unknown_Ctrl')
@@ -490,27 +482,24 @@ class Talent(Message):
result = struct.unpack_from('!lBB', self._recv_buffer, result = struct.unpack_from('!lBB', self._recv_buffer,
self.header_len) self.header_len)
modbus_len = result[1] modbus_len = result[1]
# logger.debug(f'Ref: {result[0]}')
# logger.debug(f'Modbus MsgLen: {modbus_len} Func:{result[2]}')
return msg_hdr_len, modbus_len return msg_hdr_len, modbus_len
def get_modbus_log_lvl(self) -> int: def get_modbus_log_lvl(self) -> int:
if self.ctrl.is_req(): if self.ctrl.is_req():
return logging.INFO return logging.INFO
elif self.ctrl.is_ind(): elif self.ctrl.is_ind() and self.server_side:
if self.server_side: return self.mb.last_log_lvl
return self.mb.last_log_lvl
return logging.WARNING return logging.WARNING
def msg_modbus(self): def msg_modbus(self):
hdr_len, modbus_len = self.parse_modbus_header() hdr_len, _ = self.parse_modbus_header()
data = self._recv_buffer[self.header_len: data = self._recv_buffer[self.header_len:
self.header_len+self.data_len] self.header_len+self.data_len]
if self.ctrl.is_req(): if self.ctrl.is_req():
if self.remoteStream.mb.recv_req(data[hdr_len:], if self.remote_stream.mb.recv_req(data[hdr_len:],
self.remoteStream. self.remote_stream.
msg_forward): msg_forward):
self.inc_counter('Modbus_Command') self.inc_counter('Modbus_Command')
else: else:
self.inc_counter('Invalid_Msg_Format') self.inc_counter('Invalid_Msg_Format')

View File

@@ -1,5 +1,4 @@
import logging import logging
# import gc
from asyncio import StreamReader, StreamWriter from asyncio import StreamReader, StreamWriter
from async_stream import AsyncStream from async_stream import AsyncStream
from gen3plus.solarman_v5 import SolarmanV5 from gen3plus.solarman_v5 import SolarmanV5
@@ -16,7 +15,7 @@ class ConnectionG3P(AsyncStream, SolarmanV5):
AsyncStream.__init__(self, reader, writer, addr) AsyncStream.__init__(self, reader, writer, addr)
SolarmanV5.__init__(self, server_side, client_mode) SolarmanV5.__init__(self, server_side, client_mode)
self.remoteStream: 'ConnectionG3P' = remote_stream self.remote_stream: 'ConnectionG3P' = remote_stream
''' '''
Our puplic methods Our puplic methods
@@ -27,10 +26,10 @@ class ConnectionG3P(AsyncStream, SolarmanV5):
# logger.info(f'AsyncStream refs: {gc.get_referrers(self)}') # logger.info(f'AsyncStream refs: {gc.get_referrers(self)}')
async def async_create_remote(self) -> None: async def async_create_remote(self) -> None:
pass pass # virtual interface
async def async_publ_mqtt(self) -> None: async def async_publ_mqtt(self) -> None:
pass pass # virtual interface
def healthy(self) -> bool: def healthy(self) -> bool:
logger.debug('ConnectionG3P healthy()') logger.debug('ConnectionG3P healthy()')

View File

@@ -9,9 +9,7 @@ from gen3plus.connection_g3p import ConnectionG3P
from aiomqtt import MqttCodeError from aiomqtt import MqttCodeError
from infos import Infos from infos import Infos
# import gc
# logger = logging.getLogger('conn')
logger_mqtt = logging.getLogger('mqtt') logger_mqtt = logging.getLogger('mqtt')
@@ -62,11 +60,11 @@ class InverterG3P(Inverter, ConnectionG3P):
logging.info(f'[{self.node_id}] Connect to {addr}') logging.info(f'[{self.node_id}] Connect to {addr}')
connect = asyncio.open_connection(host, port) connect = asyncio.open_connection(host, port)
reader, writer = await connect reader, writer = await connect
self.remoteStream = ConnectionG3P(reader, writer, addr, self, self.remote_stream = ConnectionG3P(reader, writer, addr, self,
server_side=False, server_side=False,
client_mode=False) client_mode=False)
logging.info(f'[{self.remoteStream.node_id}:' logging.info(f'[{self.remote_stream.node_id}:'
f'{self.remoteStream.conn_no}] ' f'{self.remote_stream.conn_no}] '
f'Connected to {addr}') f'Connected to {addr}')
asyncio.create_task(self.client_loop(addr)) asyncio.create_task(self.client_loop(addr))

View File

@@ -1,5 +1,4 @@
import struct import struct
# import json
import logging import logging
import time import time
import asyncio import asyncio
@@ -19,7 +18,6 @@ else: # pragma: no cover
from my_timer import Timer from my_timer import Timer
from gen3plus.infos_g3p import InfosG3P from gen3plus.infos_g3p import InfosG3P
from infos import Register from infos import Register
# import traceback
logger = logging.getLogger('msg') logger = logging.getLogger('msg')
@@ -258,7 +256,6 @@ class SolarmanV5(Message):
logger.info(self.__flow_str(self.server_side, 'forwrd') + logger.info(self.__flow_str(self.server_side, 'forwrd') +
f' Ctl: {int(self.control):#04x}' f' Ctl: {int(self.control):#04x}'
f' Msg: {fnc.__name__!r}') f' Msg: {fnc.__name__!r}')
return
def _init_new_client_conn(self) -> bool: def _init_new_client_conn(self) -> bool:
return False return False
@@ -312,7 +309,6 @@ class SolarmanV5(Message):
self._recv_buffer = bytearray() self._recv_buffer = bytearray()
return return
self.header_valid = True self.header_valid = True
return
def __trailer_is_ok(self, buf: bytes, buf_len: int) -> bool: def __trailer_is_ok(self, buf: bytes, buf_len: int) -> bool:
crc = buf[self.data_len+11] crc = buf[self.data_len+11]
@@ -436,15 +432,15 @@ class SolarmanV5(Message):
return not cmd.startswith(tuple(self.at_acl[connection]['allow'])) or \ return not cmd.startswith(tuple(self.at_acl[connection]['allow'])) or \
cmd.startswith(tuple(self.at_acl[connection]['block'])) cmd.startswith(tuple(self.at_acl[connection]['block']))
async def send_at_cmd(self, AT_cmd: str) -> None: async def send_at_cmd(self, at_cmd: str) -> None:
if self.state != State.up: if self.state != State.up:
logger.warning(f'[{self.node_id}] ignore AT+ cmd,' logger.warning(f'[{self.node_id}] ignore AT+ cmd,'
' as the state is not UP') ' as the state is not UP')
return return
AT_cmd = AT_cmd.strip() at_cmd = at_cmd.strip()
if self.at_cmd_forbidden(cmd=AT_cmd, connection='mqtt'): if self.at_cmd_forbidden(cmd=at_cmd, connection='mqtt'):
data_json = f'\'{AT_cmd}\' is forbidden' data_json = f'\'{at_cmd}\' is forbidden'
node_id = self.node_id node_id = self.node_id
key = 'at_resp' key = 'at_resp'
logger.info(f'{key}: {data_json}') logger.info(f'{key}: {data_json}')
@@ -453,8 +449,8 @@ class SolarmanV5(Message):
self.forward_at_cmd_resp = False self.forward_at_cmd_resp = False
self.__build_header(0x4510) self.__build_header(0x4510)
self._send_buffer += struct.pack(f'<BHLLL{len(AT_cmd)}sc', self.AT_CMD, self._send_buffer += struct.pack(f'<BHLLL{len(at_cmd)}sc', self.AT_CMD,
2, 0, 0, 0, AT_cmd.encode('utf-8'), 2, 0, 0, 0, at_cmd.encode('utf-8'),
b'\r') b'\r')
self.__finish_send_msg() self.__finish_send_msg()
try: try:
@@ -467,19 +463,19 @@ class SolarmanV5(Message):
def __build_model_name(self): def __build_model_name(self):
db = self.db db = self.db
MaxPow = db.get_db_value(Register.MAX_DESIGNED_POWER, 0) max_pow = db.get_db_value(Register.MAX_DESIGNED_POWER, 0)
Rated = db.get_db_value(Register.RATED_POWER, 0) rated = db.get_db_value(Register.RATED_POWER, 0)
Model = None model = None
if MaxPow == 2000: if max_pow == 2000:
if Rated == 800 or Rated == 600: if rated == 800 or rated == 600:
Model = f'TSOL-MS{MaxPow}({Rated})' model = f'TSOL-MS{max_pow}({rated})'
else: else:
Model = f'TSOL-MS{MaxPow}' model = f'TSOL-MS{max_pow}'
elif MaxPow == 1800 or MaxPow == 1600: elif max_pow == 1800 or max_pow == 1600:
Model = f'TSOL-MS{MaxPow}' model = f'TSOL-MS{max_pow}'
if Model: if model:
logger.info(f'Model: {Model}') logger.info(f'Model: {model}')
self.db.set_db_def_value(Register.EQUIPMENT_MODEL, Model) self.db.set_db_def_value(Register.EQUIPMENT_MODEL, model)
def __process_data(self, ftype, ts): def __process_data(self, ftype, ts):
inv_update = False inv_update = False
@@ -564,17 +560,17 @@ class SolarmanV5(Message):
result = struct.unpack_from('<B', data, 0) result = struct.unpack_from('<B', data, 0)
ftype = result[0] ftype = result[0]
if ftype == self.AT_CMD: if ftype == self.AT_CMD:
AT_cmd = data[15:].decode() at_cmd = data[15:].decode()
if self.at_cmd_forbidden(cmd=AT_cmd, connection='tsun'): if self.at_cmd_forbidden(cmd=at_cmd, connection='tsun'):
self.inc_counter('AT_Command_Blocked') self.inc_counter('AT_Command_Blocked')
return return
self.inc_counter('AT_Command') self.inc_counter('AT_Command')
self.forward_at_cmd_resp = True self.forward_at_cmd_resp = True
elif ftype == self.MB_RTU_CMD: elif ftype == self.MB_RTU_CMD:
if self.remoteStream.mb.recv_req(data[15:], if self.remote_stream.mb.recv_req(data[15:],
self.remoteStream. self.remote_stream.
__forward_msg): __forward_msg):
self.inc_counter('Modbus_Command') self.inc_counter('Modbus_Command')
else: else:
logger.error('Invalid Modbus Msg') logger.error('Invalid Modbus Msg')
@@ -593,9 +589,9 @@ class SolarmanV5(Message):
if self.forward_at_cmd_resp: if self.forward_at_cmd_resp:
return logging.INFO return logging.INFO
return logging.DEBUG return logging.DEBUG
elif ftype == self.MB_RTU_CMD: elif ftype == self.MB_RTU_CMD \
if self.server_side: and self.server_side:
return self.mb.last_log_lvl return self.mb.last_log_lvl
return logging.WARNING return logging.WARNING

View File

@@ -130,16 +130,16 @@ class ClrAtMidnight:
return return
prfx += f'{keys[0]}' prfx += f'{keys[0]}'
dict = cls.db db_dict = cls.db
if prfx not in dict: if prfx not in db_dict:
dict[prfx] = {} db_dict[prfx] = {}
dict = dict[prfx] db_dict = db_dict[prfx]
for key in keys[1:-1]: for key in keys[1:-1]:
if key not in dict: if key not in db_dict:
dict[key] = {} db_dict[key] = {}
dict = dict[key] db_dict = db_dict[key]
dict[keys[-1]] = 0 db_dict[keys[-1]] = 0
@classmethod @classmethod
def elm(cls) -> Generator[tuple[str, dict], None, None]: def elm(cls) -> Generator[tuple[str, dict], None, None]:
@@ -333,7 +333,7 @@ class Infos:
def info_defs(self) -> dict: def info_defs(self) -> dict:
return self.__info_defs return self.__info_defs
def dev_value(self, idx: str | int) -> str | int | float | None: def dev_value(self, idx: str | int) -> str | int | float | dict | None:
'''returns the stored device value from our database '''returns the stored device value from our database
idx:int ==> lookup the value in the database and return it as str, idx:int ==> lookup the value in the database and return it as str,
@@ -346,29 +346,29 @@ class Infos:
elif idx in self.info_defs: elif idx in self.info_defs:
row = self.info_defs[idx] row = self.info_defs[idx]
if 'singleton' in row and row['singleton']: if 'singleton' in row and row['singleton']:
dict = self.stat db_dict = self.stat
else: else:
dict = self.db db_dict = self.db
keys = row['name'] keys = row['name']
for key in keys: for key in keys:
if key not in dict: if key not in db_dict:
return None # value not found in the database return None # value not found in the database
dict = dict[key] db_dict = db_dict[key]
return dict # value of the reqeusted entry return db_dict # value of the reqeusted entry
return None # unknwon idx, not in info_defs return None # unknwon idx, not in info_defs
def inc_counter(self, counter: str) -> None: def inc_counter(self, counter: str) -> None:
'''inc proxy statistic counter''' '''inc proxy statistic counter'''
dict = self.stat['proxy'] db_dict = self.stat['proxy']
dict[counter] += 1 db_dict[counter] += 1
def dec_counter(self, counter: str) -> None: def dec_counter(self, counter: str) -> None:
'''dec proxy statistic counter''' '''dec proxy statistic counter'''
dict = self.stat['proxy'] db_dict = self.stat['proxy']
dict[counter] -= 1 db_dict[counter] -= 1
def ha_proxy_confs(self, ha_prfx: str, node_id: str, snr: str) \ def ha_proxy_confs(self, ha_prfx: str, node_id: str, snr: str) \
-> Generator[tuple[str, str, str, str], None, None]: -> Generator[tuple[str, str, str, str], None, None]:
@@ -525,9 +525,8 @@ class Infos:
return None return None
row = self.info_defs[key] row = self.info_defs[key]
if 'singleton' in row: if 'singleton' in row and row['singleton']:
if row['singleton']: return None
return None
# check if we have details for home assistant # check if we have details for home assistant
if 'ha' in row: if 'ha' in row:
@@ -542,7 +541,7 @@ class Infos:
return json.dumps(attr), component, node_id, uniq_id return json.dumps(attr), component, node_id, uniq_id
return None return None
def _key_obj(self, id: Register) -> list: def _key_obj(self, id: Register) -> tuple:
d = self.info_defs.get(id, {'name': None, 'level': logging.DEBUG, d = self.info_defs.get(id, {'name': None, 'level': logging.DEBUG,
'unit': ''}) 'unit': ''})
if 'ha' in d and 'must_incr' in d['ha']: if 'ha' in d and 'must_incr' in d['ha']:
@@ -554,21 +553,21 @@ class Infos:
def update_db(self, keys: list, must_incr: bool, result): def update_db(self, keys: list, must_incr: bool, result):
name = '' name = ''
dict = self.db db_dict = self.db
for key in keys[:-1]: for key in keys[:-1]:
if key not in dict: if key not in db_dict:
dict[key] = {} db_dict[key] = {}
dict = dict[key] db_dict = db_dict[key]
name += key + '.' name += key + '.'
if keys[-1] not in dict: if keys[-1] not in db_dict:
update = (not must_incr or result > 0) update = (not must_incr or result > 0)
else: else:
if must_incr: if must_incr:
update = dict[keys[-1]] < result update = db_dict[keys[-1]] < result
else: else:
update = dict[keys[-1]] != result update = db_dict[keys[-1]] != result
if update: if update:
dict[keys[-1]] = result db_dict[keys[-1]] = result
name += keys[-1] name += keys[-1]
return name, update return name, update
@@ -622,13 +621,13 @@ class Infos:
return True return True
if 'gte' in dep: if 'gte' in dep:
return not value >= dep['gte'] return value < dep['gte']
elif 'less_eq' in dep: elif 'less_eq' in dep:
return not value <= dep['less_eq'] return value > dep['less_eq']
return True return True
def set_pv_module_details(self, inv: dict) -> None: def set_pv_module_details(self, inv: dict) -> None:
map = {'pv1': {'manufacturer': Register.PV1_MANUFACTURER, 'model': Register.PV1_MODEL}, # noqa: E501 pvs = {'pv1': {'manufacturer': Register.PV1_MANUFACTURER, 'model': Register.PV1_MODEL}, # noqa: E501
'pv2': {'manufacturer': Register.PV2_MANUFACTURER, 'model': Register.PV2_MODEL}, # noqa: E501 'pv2': {'manufacturer': Register.PV2_MANUFACTURER, 'model': Register.PV2_MODEL}, # noqa: E501
'pv3': {'manufacturer': Register.PV3_MANUFACTURER, 'model': Register.PV3_MODEL}, # noqa: E501 'pv3': {'manufacturer': Register.PV3_MANUFACTURER, 'model': Register.PV3_MODEL}, # noqa: E501
'pv4': {'manufacturer': Register.PV4_MANUFACTURER, 'model': Register.PV4_MODEL}, # noqa: E501 'pv4': {'manufacturer': Register.PV4_MANUFACTURER, 'model': Register.PV4_MODEL}, # noqa: E501
@@ -636,7 +635,7 @@ class Infos:
'pv6': {'manufacturer': Register.PV6_MANUFACTURER, 'model': Register.PV6_MODEL} # noqa: E501 'pv6': {'manufacturer': Register.PV6_MANUFACTURER, 'model': Register.PV6_MODEL} # noqa: E501
} }
for key, reg in map.items(): for key, reg in pvs.items():
if key in inv: if key in inv:
if 'manufacturer' in inv[key]: if 'manufacturer' in inv[key]:
self.set_db_def_value(reg['manufacturer'], self.set_db_def_value(reg['manufacturer'],

View File

@@ -103,11 +103,10 @@ class Message(metaclass=IterRegistry):
def _update_header(self, _forward_buffer): def _update_header(self, _forward_buffer):
'''callback for updating the header of the forward buffer''' '''callback for updating the header of the forward buffer'''
return # pragma: no cover pass # pragma: no cover
def _set_mqtt_timestamp(self, key, ts: float | None): def _set_mqtt_timestamp(self, key, ts: float | None):
if type(ts) is not None and \ if key not in self.new_data or \
key not in self.new_data or \
not self.new_data[key]: not self.new_data[key]:
if key == 'grid': if key == 'grid':
info_id = Register.TS_GRID info_id = Register.TS_GRID
@@ -128,7 +127,7 @@ class Message(metaclass=IterRegistry):
if self.mb: if self.mb:
self.mb.close() self.mb.close()
self.mb = None self.mb = None
pass # pragma: no cover # pragma: no cover
def inc_counter(self, counter: str) -> None: def inc_counter(self, counter: str) -> None:
self.db.inc_counter(counter) self.db.inc_counter(counter)

View File

@@ -246,7 +246,7 @@ def test_queue2():
assert mb.pdu == b'\x01\x06\x20\x08\x00\x04\x02\x0b' assert mb.pdu == b'\x01\x06\x20\x08\x00\x04\x02\x0b'
for key, update, val in mb.recv_resp(mb.db, b'\x01\x06\x20\x08\x00\x04\x02\x0b', 'test'): for key, update, val in mb.recv_resp(mb.db, b'\x01\x06\x20\x08\x00\x04\x02\x0b', 'test'):
pass pass # call generator mb.recv_resp()
assert mb.que.qsize() == 0 assert mb.que.qsize() == 0
assert mb.send_calls == 3 assert mb.send_calls == 3

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff