Dev 0.11 (#200)
* Code Cleanup (#158) * print coverage report * create sonar-project property file * install all py dependencies in one step * code cleanup * reduce cognitive complexity * do not build on *.yml changes * optimise versionstring handling (#159) - Reading the version string from the image updates it even if the image is re-pulled without re-deployment * fix linter warning * exclude *.pyi filese * ignore some rules for tests * cleanup (#160) * Sonar qube 3 (#163) fix SonarQube warnings in modbus.py * Sonar qube 3 (#164) * fix SonarQube warnings * Sonar qube 3 (#165) * cleanup * Add support for TSUN Titan inverter Fixes #161 * fix SonarQube warnings * fix error * rename field "config" * SonarQube reads flake8 output * don't stop on flake8 errors * flake8 scan only app/src for SonarQube * update flake8 run * ignore flake8 C901 * cleanup * fix linter warnings * ignore changed *.yml files * read sensor list solarman data packets * catch 'No route to' error and log only in debug mode * fix unit tests * add sensor_list configuration * adapt unit tests * fix SonarQube warnings * Sonar qube 3 (#166) * add unittests for mqtt.py * add mock * move test requirements into a file * fix unit tests * fix formating * initial version * fix SonarQube warning * Sonar qube 4 (#169) * add unit test for inverter.py * fix SonarQube warning * Sonar qube 5 (#170) * fix SonarLints warnings * use random IP adresses for unit tests * Docker: The description ist missing (#171) Fixes #167 * S allius/issue167 (#172) * cleanup * Sonar qube 6 (#174) * test class ModbusConn * Sonar qube 3 (#178) * add more unit tests * GEN3: don't crash on overwritten msg in the receive buffer * improve test coverage und reduce test delays * reduce cognitive complexity * fix merge * fix merge conflikt * fix merge conflict * S allius/issue182 (#183) * GEN3: After inverter firmware update the 'Unknown Msg Type' increases continuously Fixes #182 * add support for Controller serial no and MAC * test hardening * GEN3: add support for new messages of version 3 firmwares * bump libraries to latest versions - bump aiomqtt to version 2.3.0 - bump aiohttp to version 3.10.5 * improve test coverage * reduce cognective complexity * fix target preview * remove dubbled fixtures * increase test coverage * Update README.md (#185) update badges * S allius/issue186 (#187) * Parse more values in Server Mode Fixes #186 * read OUTPUT_COEFFICIENT and MAC_ADDR in SrvMode * fix unit test * increase test coverage * S allius/issue186 (#188) * increase test coverage * update changelog * add dokumentation * change default config * Update README.md (#189) Config file is now foldable * GEN3: Invalid Contact Info Msg (#192) Fixes #191 * Refactoring async stream (#194) * GEN3: Invalid Contact Info Msg Fixes #191 * introduce ifc with FIFOs * add object factory * use AsyncIfc class with FIFO * declare more methods as classmethods * - refactoring - remove _forward_buffer - make async_write private * remove _forward_buffer * refactoring * avoid mqtt handling for invalid serial numbers * add two more callbacks * FIX update_header_cb handling * split AsyncStream in two classes * split ConnectionG3(P) in server and client class * update class diagramm * refactor server creation * remove duplicated imports * reduce code duplication * move StremPtr instances into Inverter class * resolution of connection classes - remove ConnectionG3Client - remove ConnectionG3Server - remove ConnectionG3PClient - remove ConnectionG3PServer * fix server connections * fix client loop closing * don't overwrite self.remote in constructor * update class diagramm * fixes - fixes null pointer accesses - initalize AsyncStreamClient with proper StreamPtr instance * add close callback * refactor close handling * remove connection classes * move more code into InverterBase class * remove test_inverter_base.py * add abstract inverter interface class * initial commit * fix sonar qube warnings * rename class Inverter into Proxy * fix typo * move class InverterIfc into a separate file * add more testcases * use ProtocolIfc class * add unit tests for AsyncStream class * icrease test coverage * reduce cognitive complexity * increase test coverage * increase tes coverage * simplify heartbeat handler * remove obsolete tx_get method * add more unittests * update changelog * remove __del__ method for proper gc runs * check releasing of ModbusConn instances * call garbage collector to release unreachable objs * decrease ref counter after the with block * S allius/issue196 (#198) * fix healthcheck - on infrastructure with IPv6 support localhost might be resolved to an IPv6 adress. Since the proxy only support IPv4 for now, we replace localhost by 127.0.0.1, to fix this * merge from main
This commit is contained in:
@@ -1,47 +0,0 @@
|
||||
import logging
|
||||
from asyncio import StreamReader, StreamWriter
|
||||
|
||||
if __name__ == "app.src.gen3plus.connection_g3p":
|
||||
from app.src.async_stream import AsyncStream
|
||||
from app.src.gen3plus.solarman_v5 import SolarmanV5
|
||||
else: # pragma: no cover
|
||||
from async_stream import AsyncStream
|
||||
from gen3plus.solarman_v5 import SolarmanV5
|
||||
|
||||
logger = logging.getLogger('conn')
|
||||
|
||||
|
||||
class ConnectionG3P(AsyncStream, SolarmanV5):
|
||||
|
||||
def __init__(self, reader: StreamReader, writer: StreamWriter,
|
||||
addr, remote_stream: 'ConnectionG3P',
|
||||
server_side: bool,
|
||||
client_mode: bool) -> None:
|
||||
AsyncStream.__init__(self, reader, writer, addr)
|
||||
SolarmanV5.__init__(self, server_side, client_mode)
|
||||
|
||||
self.remote_stream: 'ConnectionG3P' = remote_stream
|
||||
|
||||
'''
|
||||
Our puplic methods
|
||||
'''
|
||||
def close(self):
|
||||
AsyncStream.close(self)
|
||||
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
|
||||
|
||||
async def async_publ_mqtt(self) -> None:
|
||||
pass # virtual interface # pragma: no cover
|
||||
|
||||
def healthy(self) -> bool:
|
||||
logger.debug('ConnectionG3P healthy()')
|
||||
return AsyncStream.healthy(self)
|
||||
|
||||
'''
|
||||
Our private methods
|
||||
'''
|
||||
def __del__(self):
|
||||
super().__del__()
|
||||
@@ -1,140 +1,15 @@
|
||||
import logging
|
||||
import traceback
|
||||
import json
|
||||
import asyncio
|
||||
from asyncio import StreamReader, StreamWriter
|
||||
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.infos import Infos
|
||||
from app.src.inverter_base import InverterBase
|
||||
from app.src.gen3plus.solarman_v5 import SolarmanV5
|
||||
else: # pragma: no cover
|
||||
from config import Config
|
||||
from inverter import Inverter
|
||||
from gen3plus.connection_g3p import ConnectionG3P
|
||||
from infos import Infos
|
||||
from inverter_base import InverterBase
|
||||
from gen3plus.solarman_v5 import SolarmanV5
|
||||
|
||||
|
||||
logger_mqtt = logging.getLogger('mqtt')
|
||||
|
||||
|
||||
class InverterG3P(Inverter, ConnectionG3P):
|
||||
'''class Inverter is a derivation of an Async_Stream
|
||||
|
||||
The class has some class method for managing common resources like a
|
||||
connection to the MQTT broker or proxy error counter which are common
|
||||
for all inverter connection
|
||||
|
||||
Instances of the class are connections to an inverter and can have an
|
||||
optional link to an remote connection to the TSUN cloud. A remote
|
||||
connection dies with the inverter connection.
|
||||
|
||||
class methods:
|
||||
class_init(): initialize the common resources of the proxy (MQTT
|
||||
broker, Proxy DB, etc). Must be called before the
|
||||
first inverter instance can be created
|
||||
class_close(): release the common resources of the proxy. Should not
|
||||
be called before any instances of the class are
|
||||
destroyed
|
||||
|
||||
methods:
|
||||
server_loop(addr): Async loop method for receiving messages from the
|
||||
inverter (server-side)
|
||||
client_loop(addr): Async loop method for receiving messages from the
|
||||
TSUN cloud (client-side)
|
||||
async_create_remote(): Establish a client connection to the TSUN cloud
|
||||
async_publ_mqtt(): Publish data to MQTT broker
|
||||
close(): Release method which must be called before a instance can be
|
||||
destroyed
|
||||
'''
|
||||
|
||||
def __init__(self, reader: StreamReader, writer: StreamWriter, addr,
|
||||
class InverterG3P(InverterBase):
|
||||
def __init__(self, reader: StreamReader, writer: StreamWriter,
|
||||
client_mode: bool = False):
|
||||
super().__init__(reader, writer, addr, None,
|
||||
server_side=True, client_mode=client_mode)
|
||||
self.__ha_restarts = -1
|
||||
|
||||
async def async_create_remote(self) -> None:
|
||||
'''Establish a client connection to the TSUN cloud'''
|
||||
tsun = Config.get('solarman')
|
||||
host = tsun['host']
|
||||
port = tsun['port']
|
||||
addr = (host, port)
|
||||
|
||||
try:
|
||||
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)
|
||||
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))
|
||||
|
||||
except (ConnectionRefusedError, TimeoutError) as error:
|
||||
logging.info(f'{error}')
|
||||
except Exception:
|
||||
self.inc_counter('SW_Exception')
|
||||
logging.error(
|
||||
f"Inverter: Exception for {addr}:\n"
|
||||
f"{traceback.format_exc()}")
|
||||
|
||||
async def async_publ_mqtt(self) -> None:
|
||||
'''publish data to MQTT broker'''
|
||||
# check if new inverter or collector infos are available or when the
|
||||
# home assistant has changed the status back to online
|
||||
try:
|
||||
if (('inverter' in self.new_data and self.new_data['inverter'])
|
||||
or ('collector' in self.new_data and
|
||||
self.new_data['collector'])
|
||||
or self.mqtt.ha_restarts != self.__ha_restarts):
|
||||
await self._register_proxy_stat_home_assistant()
|
||||
await self.__register_home_assistant()
|
||||
self.__ha_restarts = self.mqtt.ha_restarts
|
||||
|
||||
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)
|
||||
|
||||
except MqttCodeError as error:
|
||||
logging.error(f'Mqtt except: {error}')
|
||||
except Exception:
|
||||
self.inc_counter('SW_Exception')
|
||||
logging.error(
|
||||
f"Inverter: Exception:\n"
|
||||
f"{traceback.format_exc()}")
|
||||
|
||||
async def __async_publ_mqtt_packet(self, key):
|
||||
db = self.db.db
|
||||
if key in db and self.new_data[key]:
|
||||
data_json = json.dumps(db[key])
|
||||
node_id = self.node_id
|
||||
logger_mqtt.debug(f'{key}: {data_json}')
|
||||
await self.mqtt.publish(f'{self.entity_prfx}{node_id}{key}', data_json) # noqa: E501
|
||||
self.new_data[key] = False
|
||||
|
||||
async def __register_home_assistant(self) -> None:
|
||||
'''register all our topics at home assistant'''
|
||||
for data_json, component, node_id, id in self.db.ha_confs(
|
||||
self.entity_prfx, self.node_id, self.unique_id,
|
||||
self.sug_area):
|
||||
logger_mqtt.debug(f"MQTT Register: cmp:'{component}'"
|
||||
f" node_id:'{node_id}' {data_json}")
|
||||
await self.mqtt.publish(f"{self.discovery_prfx}{component}"
|
||||
f"/{node_id}{id}/config", data_json)
|
||||
|
||||
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}')
|
||||
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__()
|
||||
super().__init__(reader, writer, 'solarman',
|
||||
SolarmanV5, client_mode)
|
||||
|
||||
@@ -5,6 +5,7 @@ import asyncio
|
||||
from datetime import datetime
|
||||
|
||||
if __name__ == "app.src.gen3plus.solarman_v5":
|
||||
from app.src.async_ifc import AsyncIfc
|
||||
from app.src.messages import hex_dump_memory, Message, State
|
||||
from app.src.modbus import Modbus
|
||||
from app.src.my_timer import Timer
|
||||
@@ -12,6 +13,7 @@ if __name__ == "app.src.gen3plus.solarman_v5":
|
||||
from app.src.gen3plus.infos_g3p import InfosG3P
|
||||
from app.src.infos import Register
|
||||
else: # pragma: no cover
|
||||
from async_ifc import AsyncIfc
|
||||
from messages import hex_dump_memory, Message, State
|
||||
from config import Config
|
||||
from modbus import Modbus
|
||||
@@ -60,9 +62,17 @@ class SolarmanV5(Message):
|
||||
HDR_FMT = '<BLLL'
|
||||
'''format string for packing of the header'''
|
||||
|
||||
def __init__(self, server_side: bool, client_mode: bool):
|
||||
def __init__(self, addr, ifc: "AsyncIfc",
|
||||
server_side: bool, client_mode: bool):
|
||||
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.addr = addr
|
||||
self.ifc = ifc
|
||||
self.conn_no = ifc.get_conn_no()
|
||||
self.header_len = 11 # overwrite construcor in class Message
|
||||
self.control = 0
|
||||
self.seq = Sequence(server_side)
|
||||
@@ -160,6 +170,11 @@ class SolarmanV5(Message):
|
||||
self.log_lvl.clear()
|
||||
self.state = State.closed
|
||||
self.mb_timer.close()
|
||||
self.ifc.rx_set_cb(None)
|
||||
self.ifc.prot_set_timeout_cb(None)
|
||||
self.ifc.prot_set_init_new_client_conn_cb(None)
|
||||
self.ifc.prot_set_update_header_cb(None)
|
||||
self.ifc = None
|
||||
super().close()
|
||||
|
||||
async def send_start_cmd(self, snr: int, host: str,
|
||||
@@ -230,9 +245,10 @@ class SolarmanV5(Message):
|
||||
self._read()
|
||||
while True:
|
||||
if not self.header_valid:
|
||||
self.__parse_header(self._recv_buffer, len(self._recv_buffer))
|
||||
self.__parse_header(self.ifc.rx_peek(),
|
||||
self.ifc.rx_len())
|
||||
|
||||
if self.header_valid and len(self._recv_buffer) >= \
|
||||
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()
|
||||
@@ -243,10 +259,10 @@ class SolarmanV5(Message):
|
||||
log_lvl = self.log_lvl.get(self.control, logging.WARNING)
|
||||
if callable(log_lvl):
|
||||
log_lvl = log_lvl()
|
||||
hex_dump_memory(log_lvl, f'Received from {self.addr}:',
|
||||
self._recv_buffer, self.header_len +
|
||||
self.data_len+2)
|
||||
if self.__trailer_is_ok(self._recv_buffer, self.header_len
|
||||
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.rx_peek(), self.header_len
|
||||
+ self.data_len + 2):
|
||||
if self.state == State.init:
|
||||
self.state = State.received
|
||||
@@ -259,9 +275,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.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') +
|
||||
@@ -317,7 +332,7 @@ class SolarmanV5(Message):
|
||||
|
||||
self.inc_counter('Invalid_Msg_Format')
|
||||
# erase broken recv buffer
|
||||
self._recv_buffer = bytearray()
|
||||
self.ifc.rx_clear()
|
||||
return
|
||||
self.header_valid = True
|
||||
|
||||
@@ -329,11 +344,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._recv_buffer) > (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._recv_buffer = bytearray()
|
||||
self.ifc.rx_clear()
|
||||
|
||||
return False
|
||||
|
||||
@@ -349,21 +364,22 @@ class SolarmanV5(Message):
|
||||
|
||||
def __build_header(self, ctrl) -> None:
|
||||
'''build header for new transmit message'''
|
||||
self.send_msg_ofs = len(self._send_buffer)
|
||||
self.send_msg_ofs = self.ifc.tx_len()
|
||||
|
||||
self._send_buffer += struct.pack(
|
||||
'<BHHHL', 0xA5, 0, ctrl, self.seq.get_send(), self.snr)
|
||||
self.ifc.tx_add(struct.pack(
|
||||
'<BHHHL', 0xA5, 0, ctrl, self.seq.get_send(), self.snr))
|
||||
fnc = self.switch.get(ctrl, self.msg_unknown)
|
||||
logger.info(self.__flow_str(self.server_side, 'tx') +
|
||||
f' Ctl: {int(ctrl):#04x} Msg: {fnc.__name__!r}')
|
||||
|
||||
def __finish_send_msg(self) -> None:
|
||||
'''finish the transmit message, set lenght and checksum'''
|
||||
_len = len(self._send_buffer) - self.send_msg_ofs
|
||||
struct.pack_into('<H', self._send_buffer, self.send_msg_ofs+1, _len-11)
|
||||
check = sum(self._send_buffer[self.send_msg_ofs+1:self.send_msg_ofs +
|
||||
_len]) & 0xff
|
||||
self._send_buffer += struct.pack('<BB', check, 0x15) # crc & stop
|
||||
_len = self.ifc.tx_len() - self.send_msg_ofs
|
||||
struct.pack_into('<H', self.ifc.tx_peek(), self.send_msg_ofs+1,
|
||||
_len-11)
|
||||
check = sum(self.ifc.tx_peek()[
|
||||
self.send_msg_ofs+1:self.send_msg_ofs + _len]) & 0xff
|
||||
self.ifc.tx_add(struct.pack('<BB', check, 0x15)) # crc & stop
|
||||
|
||||
def _update_header(self, _forward_buffer):
|
||||
'''update header for message before forwarding,
|
||||
@@ -394,15 +410,14 @@ class SolarmanV5(Message):
|
||||
f' Msg: {fnc.__name__!r}')
|
||||
|
||||
def __flush_recv_msg(self) -> None:
|
||||
self._recv_buffer = self._recv_buffer[(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._send_buffer += struct.pack('<BBLL', ftype, ack,
|
||||
self._timestamp(),
|
||||
self._heartbeat())
|
||||
self.ifc.tx_add(struct.pack('<BBLL', ftype, ack,
|
||||
self._timestamp(),
|
||||
self._heartbeat()))
|
||||
self.__finish_send_msg()
|
||||
|
||||
def send_modbus_cb(self, pdu: bytearray, log_lvl: int, state: str):
|
||||
@@ -411,14 +426,12 @@ class SolarmanV5(Message):
|
||||
' cause the state is not UP anymore')
|
||||
return
|
||||
self.__build_header(0x4510)
|
||||
self._send_buffer += struct.pack('<BHLLL', self.MB_RTU_CMD,
|
||||
self.sensor_list, 0, 0, 0)
|
||||
self._send_buffer += pdu
|
||||
self.ifc.tx_add(struct.pack('<BHLLL', self.MB_RTU_CMD,
|
||||
self.sensor_list, 0, 0, 0))
|
||||
self.ifc.tx_add(pdu)
|
||||
self.__finish_send_msg()
|
||||
hex_dump_memory(log_lvl, f'Send Modbus {state}:{self.addr}:',
|
||||
self._send_buffer, len(self._send_buffer))
|
||||
self.writer.write(self._send_buffer)
|
||||
self._send_buffer = bytearray(0) # self._send_buffer[sent:]
|
||||
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:
|
||||
@@ -460,17 +473,18 @@ class SolarmanV5(Message):
|
||||
|
||||
self.forward_at_cmd_resp = False
|
||||
self.__build_header(0x4510)
|
||||
self._send_buffer += struct.pack(f'<BHLLL{len(at_cmd)}sc', self.AT_CMD,
|
||||
0x0002, 0, 0, 0,
|
||||
at_cmd.encode('utf-8'), b'\r')
|
||||
self.ifc.tx_add(struct.pack(f'<BHLLL{len(at_cmd)}sc', self.AT_CMD,
|
||||
0x0002, 0, 0, 0,
|
||||
at_cmd.encode('utf-8'), b'\r'))
|
||||
self.__finish_send_msg()
|
||||
self.ifc.tx_log(logging.INFO, 'Send AT Command:')
|
||||
try:
|
||||
await self.async_write('Send AT Command:')
|
||||
self.ifc.tx_flush()
|
||||
except Exception:
|
||||
self._send_buffer = bytearray(0)
|
||||
self.ifc.tx_clear()
|
||||
|
||||
def __forward_msg(self):
|
||||
self.forward(self._recv_buffer, self.header_len+self.data_len+2)
|
||||
self.forward(self.ifc.rx_peek(), self.header_len+self.data_len+2)
|
||||
|
||||
def __build_model_name(self):
|
||||
db = self.db
|
||||
@@ -491,7 +505,7 @@ class SolarmanV5(Message):
|
||||
def __process_data(self, ftype, ts):
|
||||
inv_update = False
|
||||
msg_type = self.control >> 8
|
||||
for key, update in self.db.parse(self._recv_buffer, 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':
|
||||
@@ -510,7 +524,7 @@ class SolarmanV5(Message):
|
||||
self.__forward_msg()
|
||||
|
||||
def msg_dev_ind(self):
|
||||
data = self._recv_buffer[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]
|
||||
@@ -531,7 +545,7 @@ class SolarmanV5(Message):
|
||||
self.__send_ack_rsp(0x1110, ftype)
|
||||
|
||||
def msg_data_ind(self):
|
||||
data = self._recv_buffer
|
||||
data = self.ifc.rx_peek()
|
||||
result = struct.unpack_from('<BHLLLHL', data, self.header_len)
|
||||
ftype = result[0] # 1 or 0x81
|
||||
sensor = result[1]
|
||||
@@ -559,7 +573,7 @@ class SolarmanV5(Message):
|
||||
self.new_state_up()
|
||||
|
||||
def msg_sync_start(self):
|
||||
data = self._recv_buffer[self.header_len:]
|
||||
data = self.ifc.rx_peek()[self.header_len:]
|
||||
result = struct.unpack_from(self.HDR_FMT, data, 0)
|
||||
ftype = result[0]
|
||||
total = result[1]
|
||||
@@ -572,8 +586,8 @@ class SolarmanV5(Message):
|
||||
self.__send_ack_rsp(0x1310, ftype)
|
||||
|
||||
def msg_command_req(self):
|
||||
data = self._recv_buffer[self.header_len:
|
||||
self.header_len+self.data_len]
|
||||
data = self.ifc.rx_peek()[self.header_len:
|
||||
self.header_len+self.data_len]
|
||||
result = struct.unpack_from('<B', data, 0)
|
||||
ftype = result[0]
|
||||
if ftype == self.AT_CMD:
|
||||
@@ -585,9 +599,9 @@ class SolarmanV5(Message):
|
||||
self.forward_at_cmd_resp = True
|
||||
|
||||
elif ftype == self.MB_RTU_CMD:
|
||||
if self.remote_stream.mb.recv_req(data[15:],
|
||||
self.remote_stream.
|
||||
__forward_msg):
|
||||
rstream = self.ifc.remote.stream
|
||||
if rstream.mb.recv_req(data[15:],
|
||||
rstream.__forward_msg):
|
||||
self.inc_counter('Modbus_Command')
|
||||
else:
|
||||
logger.error('Invalid Modbus Msg')
|
||||
@@ -601,7 +615,7 @@ class SolarmanV5(Message):
|
||||
self.mqtt.publish(key, data))
|
||||
|
||||
def get_cmd_rsp_log_lvl(self) -> int:
|
||||
ftype = self._recv_buffer[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
|
||||
@@ -613,8 +627,8 @@ class SolarmanV5(Message):
|
||||
return logging.WARNING
|
||||
|
||||
def msg_command_rsp(self):
|
||||
data = self._recv_buffer[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:
|
||||
@@ -650,7 +664,7 @@ class SolarmanV5(Message):
|
||||
self.__build_model_name()
|
||||
|
||||
def msg_hbeat_ind(self):
|
||||
data = self._recv_buffer[self.header_len:]
|
||||
data = self.ifc.rx_peek()[self.header_len:]
|
||||
result = struct.unpack_from('<B', data, 0)
|
||||
ftype = result[0]
|
||||
|
||||
@@ -659,7 +673,7 @@ class SolarmanV5(Message):
|
||||
self.new_state_up()
|
||||
|
||||
def msg_sync_end(self):
|
||||
data = self._recv_buffer[self.header_len:]
|
||||
data = self.ifc.rx_peek()[self.header_len:]
|
||||
result = struct.unpack_from(self.HDR_FMT, data, 0)
|
||||
ftype = result[0]
|
||||
total = result[1]
|
||||
@@ -672,7 +686,7 @@ class SolarmanV5(Message):
|
||||
self.__send_ack_rsp(0x1810, ftype)
|
||||
|
||||
def msg_response(self):
|
||||
data = self._recv_buffer[self.header_len:]
|
||||
data = self.ifc.rx_peek()[self.header_len:]
|
||||
result = struct.unpack_from('<BBLL', data, 0)
|
||||
ftype = result[0] # always 2
|
||||
valid = result[1] == 1 # status
|
||||
|
||||
Reference in New Issue
Block a user