@@ -44,7 +44,7 @@ If you use a Pi-hole, you can also store the host entry in the Pi-hole.
|
||||
- supports TSUN GEN3 inverters: TSOL-MS800, MS700, MS600, MS400, MS350 and MS300
|
||||
- `MQTT` support
|
||||
- `Home-Assistant` auto-discovery support
|
||||
- Self-sufficient island operation without internet (for TSUN GEN3 PLUS inverters in preparation)
|
||||
- Self-sufficient island operation without internet
|
||||
- runs in a non-root Docker Container
|
||||
|
||||
## Home Assistant Screenshots
|
||||
|
||||
@@ -117,6 +117,7 @@ class AsyncStream():
|
||||
await self.remoteStream.__async_write()
|
||||
|
||||
if self.remoteStream:
|
||||
self.remoteStream._update_header(self._forward_buffer)
|
||||
hex_dump_memory(logging.INFO,
|
||||
f'Forward to {self.remoteStream.addr}:',
|
||||
self._forward_buffer,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import struct
|
||||
# import json
|
||||
import logging
|
||||
# import time
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
if __name__ == "app.src.gen3plus.solarman_v5":
|
||||
@@ -19,6 +19,32 @@ else: # pragma: no cover
|
||||
logger = logging.getLogger('msg')
|
||||
|
||||
|
||||
class Sequence():
|
||||
def __init__(self, server_side: bool):
|
||||
self.rcv_idx = 0
|
||||
self.snd_idx = 0
|
||||
self.server_side = server_side
|
||||
|
||||
def set_recv(self, val: int):
|
||||
if self.server_side:
|
||||
self.rcv_idx = val >> 8
|
||||
self.snd_idx = val & 0xff
|
||||
else:
|
||||
self.rcv_idx = val & 0xff
|
||||
self.snd_idx = val >> 8
|
||||
|
||||
def get_send(self):
|
||||
self.snd_idx += 1
|
||||
self.snd_idx &= 0xff
|
||||
if self.server_side:
|
||||
return (self.rcv_idx << 8) | self.snd_idx
|
||||
else:
|
||||
return (self.snd_idx << 8) | self.rcv_idx
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.rcv_idx:02x}:{self.snd_idx:02x}'
|
||||
|
||||
|
||||
class SolarmanV5(Message):
|
||||
|
||||
def __init__(self, server_side: bool):
|
||||
@@ -26,7 +52,7 @@ class SolarmanV5(Message):
|
||||
|
||||
self.header_len = 11 # overwrite construcor in class Message
|
||||
self.control = 0
|
||||
self.serial = 0
|
||||
self.seq = Sequence(server_side)
|
||||
self.snr = 0
|
||||
self.db = InfosG3P()
|
||||
self.switch = {
|
||||
@@ -160,6 +186,13 @@ class SolarmanV5(Message):
|
||||
type += 'S'
|
||||
return switch.get(type, '???')
|
||||
|
||||
def _timestamp(self): # pragma: no cover
|
||||
# utc as epoche
|
||||
return int(time.time())
|
||||
|
||||
def _heartbeat(self) -> int:
|
||||
return 60
|
||||
|
||||
def __parse_header(self, buf: bytes, buf_len: int) -> None:
|
||||
|
||||
if (buf_len < self.header_len): # enough bytes for complete header?
|
||||
@@ -168,10 +201,10 @@ class SolarmanV5(Message):
|
||||
result = struct.unpack_from('<BHHHL', buf, 0)
|
||||
|
||||
# store parsed header values in the class
|
||||
start = result[0] # len of complete message
|
||||
start = result[0] # start byte
|
||||
self.data_len = result[1] # len of variable id string
|
||||
self.control = result[2]
|
||||
self.serial = result[3]
|
||||
self.seq.set_recv(result[3])
|
||||
self.snr = result[4]
|
||||
|
||||
if start != 0xA5:
|
||||
@@ -194,6 +227,7 @@ class SolarmanV5(Message):
|
||||
self._recv_buffer = bytearray()
|
||||
|
||||
return False
|
||||
|
||||
check = sum(buf[1:buf_len-2]) & 0xff
|
||||
if check != crc:
|
||||
self.inc_counter('Invalid_Msg_Format')
|
||||
@@ -204,6 +238,36 @@ class SolarmanV5(Message):
|
||||
|
||||
return True
|
||||
|
||||
def __build_header(self, ctrl) -> None:
|
||||
'''build header for new transmit message'''
|
||||
self.send_msg_ofs = len(self._send_buffer)
|
||||
|
||||
self._send_buffer += 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
|
||||
|
||||
def _update_header(self, _forward_buffer):
|
||||
'''update header for message before forwarding,
|
||||
set sequence and checksum'''
|
||||
_len = len(_forward_buffer)
|
||||
struct.pack_into('<H', _forward_buffer, 1,
|
||||
_len-13)
|
||||
struct.pack_into('<H', _forward_buffer, 5,
|
||||
self.seq.get_send())
|
||||
|
||||
check = sum(_forward_buffer[1:_len-2]) & 0xff
|
||||
struct.pack_into('<B', _forward_buffer, _len-2, check)
|
||||
|
||||
def __dispatch_msg(self) -> None:
|
||||
fnc = self.switch.get(self.control, self.msg_unknown)
|
||||
if self.unique_id:
|
||||
@@ -220,41 +284,7 @@ class SolarmanV5(Message):
|
||||
self._recv_buffer = self._recv_buffer[(self.header_len +
|
||||
self.data_len+2):]
|
||||
self.header_valid = False
|
||||
'''
|
||||
def modbus(self, data):
|
||||
POLY = 0xA001
|
||||
|
||||
crc = 0xFFFF
|
||||
for byte in data:
|
||||
crc ^= byte
|
||||
for _ in range(8):
|
||||
crc = ((crc >> 1) ^ POLY
|
||||
if (crc & 0x0001)
|
||||
else crc >> 1)
|
||||
return crc
|
||||
|
||||
def validate_modbus_crc(self, frame):
|
||||
# Calculate crc with all but the last 2 bytes of
|
||||
# the frame (they contain the crc)
|
||||
calc_crc = 0xFFFF
|
||||
for pos in frame[:-2]:
|
||||
calc_crc ^= pos
|
||||
for i in range(8):
|
||||
if (calc_crc & 1) != 0:
|
||||
calc_crc >>= 1
|
||||
calc_crc ^= 0xA001 # bitwise 'or' with modbus magic
|
||||
# number (0xa001 == bitwise
|
||||
# reverse of 0x8005)
|
||||
else:
|
||||
calc_crc >>= 1
|
||||
|
||||
# Compare calculated crc with the one supplied in the frame....
|
||||
frame_crc, = struct.unpack('<H', frame[-2:])
|
||||
if calc_crc == frame_crc:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
'''
|
||||
'''
|
||||
Message handler methods
|
||||
'''
|
||||
@@ -280,6 +310,11 @@ class SolarmanV5(Message):
|
||||
|
||||
self.__process_data(ftype)
|
||||
self.forward(self._recv_buffer, self.header_len+self.data_len+2)
|
||||
self.__build_header(0x1110)
|
||||
self._send_buffer += struct.pack('<BBLL', ftype, 1,
|
||||
self._timestamp(),
|
||||
self._heartbeat())
|
||||
self.__finish_send_msg()
|
||||
|
||||
def msg_dev_rsp(self):
|
||||
self.msg_response()
|
||||
@@ -299,9 +334,13 @@ class SolarmanV5(Message):
|
||||
dt = datetime.fromtimestamp(total)
|
||||
logger.info(f'ts: {dt.strftime("%Y-%m-%d %H:%M:%S")}')
|
||||
|
||||
ftype &= 0x7f # mask bit 7 (0x80)
|
||||
self.__process_data(ftype)
|
||||
self.__process_data(ftype & 0x7f) # mask bit 7 (0x80)
|
||||
self.forward(self._recv_buffer, self.header_len+self.data_len+2)
|
||||
self.__build_header(0x1210)
|
||||
self._send_buffer += struct.pack('<BBLL', ftype, 1,
|
||||
self._timestamp(),
|
||||
self._heartbeat())
|
||||
self.__finish_send_msg()
|
||||
|
||||
def __process_data(self, ftype):
|
||||
inv_update = False
|
||||
@@ -343,6 +382,11 @@ class SolarmanV5(Message):
|
||||
|
||||
def msg_hbeat_ind(self):
|
||||
self.forward(self._recv_buffer, self.header_len+self.data_len+2)
|
||||
self.__build_header(0x1710)
|
||||
self._send_buffer += struct.pack('<BBLL', 0, 1,
|
||||
self._timestamp(),
|
||||
self._heartbeat())
|
||||
self.__finish_send_msg()
|
||||
|
||||
def msg_hbeat_rsp(self):
|
||||
self.msg_response()
|
||||
@@ -359,7 +403,6 @@ class SolarmanV5(Message):
|
||||
|
||||
dt = datetime.fromtimestamp(ts)
|
||||
logger.info(f'ts: {dt.strftime("%Y-%m-%d %H:%M:%S")}')
|
||||
self.forward(self._recv_buffer, self.header_len+self.data_len+2)
|
||||
|
||||
def at_command_ind(self):
|
||||
self.inc_counter('AT_Command')
|
||||
|
||||
@@ -74,6 +74,10 @@ 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'''
|
||||
return
|
||||
|
||||
'''
|
||||
Our puplic methods
|
||||
'''
|
||||
|
||||
@@ -95,7 +95,7 @@ class Mqtt(metaclass=Singleton):
|
||||
logger_mqtt.info(
|
||||
f"Connection lost; Reconnecting in {interval}"
|
||||
" seconds ...")
|
||||
|
||||
|
||||
await asyncio.sleep(interval)
|
||||
except asyncio.CancelledError:
|
||||
logger_mqtt.debug("MQTT task cancelled")
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import pytest, json
|
||||
import pytest
|
||||
import struct
|
||||
import time
|
||||
from datetime import datetime
|
||||
from app.src.gen3plus.solarman_v5 import SolarmanV5
|
||||
from app.src.config import Config
|
||||
from app.src.infos import Infos, Register
|
||||
@@ -6,6 +9,9 @@ from app.src.infos import Infos, Register
|
||||
# initialize the proxy statistics
|
||||
Infos.static_init()
|
||||
|
||||
timestamp = int(time.time()) # 1712861197
|
||||
heartbeat = 60
|
||||
|
||||
class MemoryStream(SolarmanV5):
|
||||
def __init__(self, msg, chunks = (0,), server_side: bool = True):
|
||||
super().__init__(server_side)
|
||||
@@ -19,6 +25,12 @@ class MemoryStream(SolarmanV5):
|
||||
self.db.stat['proxy']['Invalid_Msg_Format'] = 0
|
||||
self.db.stat['proxy']['AT_Command'] = 0
|
||||
|
||||
def _timestamp(self):
|
||||
return timestamp
|
||||
|
||||
def _heartbeat(self) -> int:
|
||||
return heartbeat
|
||||
|
||||
|
||||
def append_msg(self, msg):
|
||||
self.__msg += msg
|
||||
@@ -42,9 +54,6 @@ class MemoryStream(SolarmanV5):
|
||||
pass
|
||||
return copied_bytes
|
||||
|
||||
def _timestamp(self):
|
||||
return 1700260990000
|
||||
|
||||
def _SolarmanV5__flush_recv_msg(self) -> None:
|
||||
super()._SolarmanV5__flush_recv_msg()
|
||||
self.msg_count += 1
|
||||
@@ -60,6 +69,16 @@ def get_inv_no() -> bytes:
|
||||
def get_invalid_sn():
|
||||
return b'R170000000000002'
|
||||
|
||||
def total():
|
||||
ts = timestamp
|
||||
# convert int to little-endian bytes
|
||||
return struct.pack('<L',ts)
|
||||
|
||||
def hb():
|
||||
hb = heartbeat
|
||||
# convert int to little-endian bytes
|
||||
return struct.pack('<L',hb)
|
||||
|
||||
def correct_checksum(buf):
|
||||
checksum = sum(buf[1:]) & 0xff
|
||||
return checksum.to_bytes(length=1)
|
||||
@@ -90,8 +109,9 @@ def DeviceIndMsg(): # 0x4110
|
||||
|
||||
@pytest.fixture
|
||||
def DeviceRspMsg(): # 0x1110
|
||||
msg = b'\xa5\x0a\x00\x10\x11\x10\x84' +get_sn() +b'\x01\x01\x69\x6f\x09'
|
||||
msg += b'\x66\x78\x00\x00\x00'
|
||||
msg = b'\xa5\x0a\x00\x10\x11\x01\x01' +get_sn() +b'\x02\x01'
|
||||
msg += total()
|
||||
msg += hb()
|
||||
msg += correct_checksum(msg)
|
||||
msg += b'\x15'
|
||||
return msg
|
||||
@@ -158,7 +178,7 @@ def InvalidChecksum(): # 0x4110
|
||||
|
||||
@pytest.fixture
|
||||
def InverterIndMsg(): # 0x4210
|
||||
msg = b'\xa5\x99\x01\x10\x42\xe6\x9e' +get_sn() +b'\x01\xb0\x02\xbc\xc8'
|
||||
msg = b'\xa5\x99\x01\x10\x42\x01\x02' +get_sn() +b'\x01\xb0\x02\xbc\xc8'
|
||||
msg += b'\x24\x32\x6c\x1f\x00\x00\xa0\x47\xe4\x33\x01\x00\x03\x08\x00\x00'
|
||||
msg += b'\x59\x31\x37\x45\x37\x41\x30\x46\x30\x31\x30\x42\x30\x31\x33\x45'
|
||||
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
@@ -290,8 +310,9 @@ def InverterIndMsg2000(): # 0x4210 rated Power 2000W
|
||||
|
||||
@pytest.fixture
|
||||
def InverterRspMsg(): # 0x1210
|
||||
msg = b'\xa5\x0a\x00\x10\x12\x10\x84' +get_sn() +b'\x01\x01\x69\x6f\x09'
|
||||
msg += b'\x66\x78\x00\x00\x00'
|
||||
msg = b'\xa5\x0a\x00\x10\x12\x02\02' +get_sn() +b'\x01\x01'
|
||||
msg += total()
|
||||
msg += hb()
|
||||
msg += correct_checksum(msg)
|
||||
msg += b'\x15'
|
||||
return msg
|
||||
@@ -314,8 +335,9 @@ def HeartbeatIndMsg(): # 0x4710
|
||||
|
||||
@pytest.fixture
|
||||
def HeartbeatRspMsg(): # 0x1710
|
||||
msg = b'\xa5\x0a\x00\x10\x17\x10\x84' +get_sn() +b'\x00\x01\x22\x71\x09'
|
||||
msg += b'\x66\x78\x00\x00\x00'
|
||||
msg = b'\xa5\x0a\x00\x10\x17\x11\x84' +get_sn() +b'\x00\x01'
|
||||
msg += total()
|
||||
msg += hb()
|
||||
msg += correct_checksum(msg)
|
||||
msg += b'\x15'
|
||||
return msg
|
||||
@@ -349,7 +371,7 @@ def test_read_message(DeviceIndMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == None
|
||||
assert m.control == 0x4110
|
||||
assert m.serial == 0x0100
|
||||
assert str(m.seq) == '01:00'
|
||||
assert m.data_len == 0xd4
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
@@ -370,7 +392,7 @@ def test_invalid_start_byte(InvalidStartByte, DeviceIndMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == 0
|
||||
assert m.control == 0x4110
|
||||
assert m.serial == 0x0100
|
||||
assert str(m.seq) == '01:00'
|
||||
assert m.data_len == 0xd4
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
@@ -390,7 +412,7 @@ def test_invalid_stop_byte(InvalidStopByte):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == 0
|
||||
assert m.control == 0x4110
|
||||
assert m.serial == 0x0100
|
||||
assert str(m.seq) == '01:00'
|
||||
assert m.data_len == 0xd4
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
@@ -410,7 +432,7 @@ def test_invalid_stop_byte2(InvalidStopByte, DeviceIndMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == 0
|
||||
assert m.control == 0x4110
|
||||
assert m.serial == 0x0100
|
||||
assert str(m.seq) == '01:00'
|
||||
assert m.data_len == 0xd4
|
||||
assert m._recv_buffer==DeviceIndMsg
|
||||
assert m._send_buffer==b''
|
||||
@@ -424,7 +446,7 @@ def test_invalid_stop_byte2(InvalidStopByte, DeviceIndMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == None
|
||||
assert m.control == 0x4110
|
||||
assert m.serial == 0x0100
|
||||
assert str(m.seq) == '01:00'
|
||||
assert m.data_len == 0xd4
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
@@ -446,7 +468,7 @@ def test_invalid_stop_start_byte(InvalidStopByte, InvalidStartByte):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == 0
|
||||
assert m.control == 0x4110
|
||||
assert m.serial == 0x0100
|
||||
assert str(m.seq) == '01:00'
|
||||
assert m.data_len == 0xd4
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
@@ -466,7 +488,7 @@ def test_invalid_checksum(InvalidChecksum, DeviceIndMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == 0
|
||||
assert m.control == 0x4110
|
||||
assert m.serial == 0x0100
|
||||
assert str(m.seq) == '01:00'
|
||||
assert m.data_len == 0xd4
|
||||
assert m._recv_buffer==DeviceIndMsg
|
||||
assert m._send_buffer==b''
|
||||
@@ -480,7 +502,7 @@ def test_invalid_checksum(InvalidChecksum, DeviceIndMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == None
|
||||
assert m.control == 0x4110
|
||||
assert m.serial == 0x0100
|
||||
assert str(m.seq) == '01:00'
|
||||
assert m.data_len == 0xd4
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
@@ -488,7 +510,7 @@ def test_invalid_checksum(InvalidChecksum, DeviceIndMsg):
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1
|
||||
m.close()
|
||||
|
||||
def test_read_message_twice(ConfigNoTsunInv1, DeviceIndMsg):
|
||||
def test_read_message_twice(ConfigNoTsunInv1, DeviceIndMsg, DeviceRspMsg):
|
||||
ConfigNoTsunInv1
|
||||
m = MemoryStream(DeviceIndMsg, (0,))
|
||||
m.append_msg(DeviceIndMsg)
|
||||
@@ -499,10 +521,13 @@ def test_read_message_twice(ConfigNoTsunInv1, DeviceIndMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == '2070233889'
|
||||
assert m.control == 0x4110
|
||||
assert m.serial == 0x0100
|
||||
assert str(m.seq) == '01:01'
|
||||
assert m.data_len == 0xd4
|
||||
assert m._send_buffer==DeviceRspMsg
|
||||
assert m._forward_buffer==b''
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
|
||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||
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 == 2
|
||||
@@ -510,8 +535,9 @@ def test_read_message_twice(ConfigNoTsunInv1, DeviceIndMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == '2070233889'
|
||||
assert m.control == 0x4110
|
||||
assert m.serial == 0x0100
|
||||
assert str(m.seq) == '01:01'
|
||||
assert m.data_len == 0xd4
|
||||
assert m._send_buffer==DeviceRspMsg
|
||||
assert m._forward_buffer==b''
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
m.close()
|
||||
@@ -528,7 +554,7 @@ def test_read_message_in_chunks(DeviceIndMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == 0 # should be None ?
|
||||
assert m.control == 0x4110
|
||||
assert m.serial == 0x0100
|
||||
assert str(m.seq) == '01:00'
|
||||
assert m.data_len == 0xd4
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
m.read() # read rest of message
|
||||
@@ -552,7 +578,7 @@ def test_read_message_in_chunks2(ConfigTsunInv1, DeviceIndMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == '2070233889'
|
||||
assert m.control == 0x4110
|
||||
assert m.serial == 0x0100
|
||||
assert str(m.seq) == '01:01'
|
||||
assert m.data_len == 0xd4
|
||||
assert m.msg_count == 1
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
@@ -563,7 +589,7 @@ def test_read_message_in_chunks2(ConfigTsunInv1, DeviceIndMsg):
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
m.close()
|
||||
|
||||
def test_read_two_messages(ConfigTsunAllowAll, DeviceIndMsg, InverterIndMsg):
|
||||
def test_read_two_messages(ConfigTsunAllowAll, DeviceIndMsg, DeviceRspMsg, InverterIndMsg, InverterRspMsg):
|
||||
ConfigTsunAllowAll
|
||||
m = MemoryStream(DeviceIndMsg, (0,))
|
||||
m.append_msg(InverterIndMsg)
|
||||
@@ -574,14 +600,13 @@ def test_read_two_messages(ConfigTsunAllowAll, DeviceIndMsg, InverterIndMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == '2070233889'
|
||||
assert m.control == 0x4110
|
||||
assert m.serial == 0x0100
|
||||
assert str(m.seq) == '01:01'
|
||||
assert m.data_len == 0xd4
|
||||
assert m.msg_count == 1
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
assert m._forward_buffer==DeviceIndMsg
|
||||
assert m._send_buffer==b''
|
||||
# assert m._send_buffer==MsgContactResp
|
||||
|
||||
assert m._send_buffer==DeviceRspMsg
|
||||
|
||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||
m._init_new_client_conn()
|
||||
assert m._send_buffer==b''
|
||||
@@ -597,11 +622,11 @@ def test_read_two_messages(ConfigTsunAllowAll, DeviceIndMsg, InverterIndMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == '2070233889'
|
||||
assert m.control == 0x4210
|
||||
assert m.serial == 0x9ee6
|
||||
assert str(m.seq) == '02:02'
|
||||
assert m.data_len == 0x199
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
assert m._forward_buffer==InverterIndMsg
|
||||
assert m._send_buffer==b''
|
||||
assert m._send_buffer==InverterRspMsg
|
||||
|
||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||
m._init_new_client_conn()
|
||||
@@ -618,7 +643,7 @@ def test_unkown_message(ConfigTsunInv1, UnknownMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == '2070233889'
|
||||
assert m.control == 0x5110
|
||||
assert m.serial == 0x8410
|
||||
assert str(m.seq) == '84:10'
|
||||
assert m.data_len == 0x0a
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
@@ -636,11 +661,11 @@ def test_device_rsp(ConfigTsunInv1, DeviceRspMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == '2070233889'
|
||||
assert m.control == 0x1110
|
||||
assert m.serial == 0x8410
|
||||
assert str(m.seq) == '01:01'
|
||||
assert m.data_len == 0x0a
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m._forward_buffer==DeviceRspMsg
|
||||
assert m._forward_buffer==b'' # DeviceRspMsg
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
m.close()
|
||||
|
||||
@@ -654,15 +679,15 @@ def test_inverter_rsp(ConfigTsunInv1, InverterRspMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == '2070233889'
|
||||
assert m.control == 0x1210
|
||||
assert m.serial == 0x8410
|
||||
assert str(m.seq) == '02:02'
|
||||
assert m.data_len == 0x0a
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m._forward_buffer==InverterRspMsg
|
||||
assert m._forward_buffer==b'' # InverterRspMsg
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
m.close()
|
||||
|
||||
def test_heartbeat_ind(ConfigTsunInv1, HeartbeatIndMsg):
|
||||
def test_heartbeat_ind(ConfigTsunInv1, HeartbeatIndMsg, HeartbeatRspMsg):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(HeartbeatIndMsg, (0,))
|
||||
m.read() # read complete msg, and dispatch msg
|
||||
@@ -672,10 +697,10 @@ def test_heartbeat_ind(ConfigTsunInv1, HeartbeatIndMsg):
|
||||
assert m.snr == 2070233889
|
||||
# assert m.unique_id == '2070233889'
|
||||
assert m.control == 0x4710
|
||||
assert m.serial == 0x8410
|
||||
assert str(m.seq) == '84:11' # value after sending response
|
||||
assert m.data_len == 0x01
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m._send_buffer==HeartbeatRspMsg
|
||||
assert m._forward_buffer==HeartbeatIndMsg
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
m.close()
|
||||
@@ -690,11 +715,11 @@ def test_heartbeat_rsp(ConfigTsunInv1, HeartbeatRspMsg):
|
||||
assert m.snr == 2070233889
|
||||
assert m.unique_id == '2070233889'
|
||||
assert m.control == 0x1710
|
||||
assert m.serial == 0x8410
|
||||
assert str(m.seq) == '11:84' # value after sending response
|
||||
assert m.data_len == 0x0a
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m._forward_buffer==HeartbeatRspMsg
|
||||
assert m._forward_buffer==b'' # HeartbeatRspMsg
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
m.close()
|
||||
|
||||
@@ -708,7 +733,7 @@ def test_at_command_ind(ConfigTsunInv1, AtCommandIndMsg):
|
||||
assert m.snr == 2070233889
|
||||
# assert m.unique_id == '2070233889'
|
||||
assert m.control == 0x4510
|
||||
assert m.serial == 0x8410
|
||||
assert str(m.seq) == '84:10'
|
||||
assert m.data_len == 0x01
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
|
||||
Reference in New Issue
Block a user