* set no of pv modules for MS800 GEN3PLUS inverters * fix unit test * increase test coverage * change the PV module handling - in default we set the number of modules now to two. So with the first data from the inverter we only register two modules. After we determine the inverter module, the number can increase to four and more PV modules will be registered. With the default value of 4, we register always 4 modules and can't reduce the number of areas when we detect that the inverter only supoorts two PV modules
2608 lines
106 KiB
Python
Executable File
2608 lines
106 KiB
Python
Executable File
import pytest
|
|
import struct
|
|
import time
|
|
import asyncio
|
|
import logging
|
|
import random
|
|
from asyncio import StreamReader, StreamWriter
|
|
from math import isclose
|
|
|
|
from async_stream import AsyncIfcImpl, StreamPtr
|
|
from gen3plus.solarman_v5 import SolarmanV5, SolarmanBase
|
|
from cnf.config import Config
|
|
from infos import Infos, Register
|
|
from modbus import Modbus
|
|
from messages import State, Message
|
|
from proxy import Proxy
|
|
from test_inverter_g3p import FakeReader, FakeWriter, patch_open_connection
|
|
from inverter_base import InverterBase
|
|
from test_modbus_tcp import test_port, test_hostname
|
|
|
|
|
|
|
|
|
|
pytest_plugins = ('pytest_asyncio',)
|
|
|
|
# initialize the proxy statistics
|
|
Infos.static_init()
|
|
|
|
timestamp = int(time.time()) # 1712861197
|
|
heartbeat = 60
|
|
|
|
|
|
class Mqtt():
|
|
def __init__(self):
|
|
self.clear()
|
|
def clear(self):
|
|
self.key = ''
|
|
self.data = ''
|
|
|
|
async def publish(self, key, data):
|
|
self.key = key
|
|
self.data = data
|
|
|
|
|
|
class FakeIfc(AsyncIfcImpl):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.remote = StreamPtr(None)
|
|
|
|
async def create_remote(self):
|
|
await asyncio.sleep(0)
|
|
|
|
class FakeInverter():
|
|
def __init__(self):
|
|
self.forward_at_cmd_resp = False
|
|
|
|
class MemoryStream(SolarmanV5):
|
|
def __init__(self, msg, chunks = (0,), server_side: bool = True, inverter=FakeInverter()):
|
|
_ifc = FakeIfc()
|
|
super().__init__(inverter, ('test.local', 1234), _ifc, server_side, client_mode=False)
|
|
if server_side:
|
|
self.mb.timeout = 0.4 # overwrite for faster testing
|
|
self.mb_first_timeout = 0.5
|
|
self.mb_timeout = 0.5
|
|
self.sent_pdu = b''
|
|
self.ifc.tx_fifo.reg_trigger(self.write_cb)
|
|
self.__msg = msg
|
|
self.__msg_len = len(msg)
|
|
self.__chunks = chunks
|
|
self.__offs = 0
|
|
self.__chunk_idx = 0
|
|
self.msg_count = 0
|
|
self.addr = 'Test: SrvSide'
|
|
self.db.stat['proxy']['Invalid_Msg_Format'] = 0
|
|
self.db.stat['proxy']['AT_Command'] = 0
|
|
self.db.stat['proxy']['AT_Command_Blocked'] = 0
|
|
self.test_exception_async_write = False
|
|
self.at_acl = {'mqtt': {'allow': ['AT+'], 'block': ['AT+WEBU']}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE', 'AT+TIME'], 'block': ['AT+WEBU']}}
|
|
self.key = ''
|
|
self.data = ''
|
|
self.msg_recvd = []
|
|
|
|
|
|
def write_cb(self):
|
|
if self.test_exception_async_write:
|
|
raise RuntimeError("Peer closed.")
|
|
self.sent_pdu = self.ifc.tx_fifo.get()
|
|
|
|
def _timestamp(self):
|
|
return timestamp
|
|
|
|
def _heartbeat(self) -> int:
|
|
return heartbeat
|
|
|
|
def append_msg(self, msg):
|
|
self.__msg += msg
|
|
self.__msg_len += len(msg)
|
|
self.__chunk_idx = 0
|
|
|
|
def publish_mqtt(self, key, data):
|
|
Proxy.mqtt.key = key
|
|
Proxy.mqtt.data = data
|
|
|
|
def _read(self) -> int:
|
|
copied_bytes = 0
|
|
try:
|
|
if (self.__offs < self.__msg_len):
|
|
chunk_len = self.__chunks[self.__chunk_idx]
|
|
self.__chunk_idx += 1
|
|
if chunk_len!=0:
|
|
self.ifc.rx_fifo += self.__msg[self.__offs:chunk_len]
|
|
copied_bytes = chunk_len - self.__offs
|
|
self.__offs = chunk_len
|
|
else:
|
|
self.ifc.rx_fifo += self.__msg[self.__offs:]
|
|
copied_bytes = self.__msg_len - self.__offs
|
|
self.__offs = self.__msg_len
|
|
except Exception:
|
|
pass # ignore exceptions here
|
|
return copied_bytes
|
|
|
|
def createClientStream(self, msg, chunks = (0,)):
|
|
c = MemoryStream(msg, chunks, False)
|
|
self.ifc.remote.stream = c
|
|
c.ifc.remote.stream = self
|
|
return c
|
|
|
|
def _SolarmanBase__flush_recv_msg(self) -> None:
|
|
self.msg_recvd.append(
|
|
{
|
|
'control': self.control,
|
|
'seq': str(self.seq),
|
|
'data_len': self.data_len
|
|
}
|
|
)
|
|
super()._SolarmanBase__flush_recv_msg()
|
|
self.msg_count += 1
|
|
|
|
|
|
def get_sn() -> bytes:
|
|
return b'\x21\x43\x65\x7b'
|
|
|
|
def get_sn_int() -> int:
|
|
return 2070233889
|
|
|
|
def get_dcu_sn() -> bytes:
|
|
return b'\x20\x43\x65\x7b'
|
|
|
|
def get_dcu_sn_int() -> int:
|
|
return 2070233888
|
|
|
|
def get_inv_no() -> bytes:
|
|
return b'T170000000000001'
|
|
|
|
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)
|
|
|
|
def incorrect_checksum(buf):
|
|
checksum = (sum(buf[1:])+1) & 0xff
|
|
return checksum.to_bytes(length=1)
|
|
|
|
@pytest.fixture(scope="session")
|
|
def str_test_ip():
|
|
ip = ".".join(str(random.randint(1, 254)) for _ in range(4))
|
|
print(f'random_ip: {ip}')
|
|
return ip
|
|
|
|
@pytest.fixture
|
|
def device_ind_msg(): # 0x4110
|
|
msg = b'\xa5\xd4\x00\x10\x41\x00\x01' +get_sn() +b'\x02\xba\xd2\x00\x00'
|
|
msg += b'\x19\x00\x00\x00\x00\x00\x00\x00\x05\x3c\x78\x01\x64\x01\x4c\x53'
|
|
msg += b'\x57\x35\x42\x4c\x45\x5f\x31\x37\x5f\x30\x32\x42\x30\x5f\x31\x2e'
|
|
msg += b'\x30\x35\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x40\x2a\x8f\x4f\x51\x54\x31\x39\x32\x2e'
|
|
msg += b'\x31\x36\x38\x2e\x38\x30\x2e\x34\x39\x00\x00\x00\x0f\x00\x01\xb0'
|
|
msg += b'\x02\x0f\x00\xff\x56\x31\x2e\x31\x2e\x30\x30\x2e\x30\x42\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xfe\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x41\x6c\x6c\x69\x75\x73\x2d\x48\x6f'
|
|
msg += b'\x6d\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def device_rsp_msg(): # 0x1110
|
|
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
|
|
|
|
@pytest.fixture
|
|
def device_ind_msg2(): # 0x4110
|
|
msg = b'\xa5\xd4\x00\x10\x41\x02\x03' +get_sn() +b'\x02\xba\xd2\x00\x00'
|
|
msg += b'\x19\x00\x00\x00\x00\x00\x00\x00\x05\x3c\x78\x01\x64\x01\x4c\x53'
|
|
msg += b'\x57\x35\x42\x4c\x45\x5f\x31\x37\x5f\x30\x32\x42\x30\x5f\x31\x2e'
|
|
msg += b'\x30\x35\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x40\x2a\x8f\x4f\x51\x54\x31\x39\x32\x2e'
|
|
msg += b'\x31\x36\x38\x2e\x38\x30\x2e\x34\x39\x00\x00\x00\x0f\x00\x01\xb0'
|
|
msg += b'\x02\x0f\x00\xff\x56\x31\x2e\x31\x2e\x30\x30\x2e\x30\x42\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xfe\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x41\x6c\x6c\x69\x75\x73\x2d\x48\x6f'
|
|
msg += b'\x6d\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def device_rsp_msg2(): # 0x1110
|
|
msg = b'\xa5\x0a\x00\x10\x11\x03\x03' +get_sn() +b'\x02\x01'
|
|
msg += total()
|
|
msg += hb()
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def invalid_start_byte(): # 0x4110
|
|
msg = b'\xa4\xd4\x00\x10\x41\x00\x01' +get_sn() +b'\x02\xba\xd2\x00\x00'
|
|
msg += b'\x19\x00\x00\x00\x00\x00\x00\x00\x05\x3c\x78\x01\x64\x01\x4c\x53'
|
|
msg += b'\x57\x35\x42\x4c\x45\x5f\x31\x37\x5f\x30\x32\x42\x30\x5f\x31\x2e'
|
|
msg += b'\x30\x35\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x40\x2a\x8f\x4f\x51\x54\x31\x39\x32\x2e'
|
|
msg += b'\x31\x36\x38\x2e\x38\x30\x2e\x34\x39\x00\x00\x00\x0f\x00\x01\xb0'
|
|
msg += b'\x02\x0f\x00\xff\x56\x31\x2e\x31\x2e\x30\x30\x2e\x30\x42\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xfe\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x41\x6c\x6c\x69\x75\x73\x2d\x48\x6f'
|
|
msg += b'\x6d\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def invalid_stop_byte(): # 0x4110
|
|
msg = b'\xa5\xd4\x00\x10\x41\x00\x01' +get_sn() +b'\x02\xba\xd2\x00\x00'
|
|
msg += b'\x19\x00\x00\x00\x00\x00\x00\x00\x05\x3c\x78\x01\x64\x01\x4c\x53'
|
|
msg += b'\x57\x35\x42\x4c\x45\x5f\x31\x37\x5f\x30\x32\x42\x30\x5f\x31\x2e'
|
|
msg += b'\x30\x35\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x40\x2a\x8f\x4f\x51\x54\x31\x39\x32\x2e'
|
|
msg += b'\x31\x36\x38\x2e\x38\x30\x2e\x34\x39\x00\x00\x00\x0f\x00\x01\xb0'
|
|
msg += b'\x02\x0f\x00\xff\x56\x31\x2e\x31\x2e\x30\x30\x2e\x30\x42\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xfe\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x41\x6c\x6c\x69\x75\x73\x2d\x48\x6f'
|
|
msg += b'\x6d\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x14'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def invalid_checksum(): # 0x4110
|
|
msg = b'\xa5\xd4\x00\x10\x41\x00\x01' +get_sn() +b'\x02\xba\xd2\x00\x00'
|
|
msg += b'\x19\x00\x00\x00\x00\x00\x00\x00\x05\x3c\x78\x01\x64\x01\x4c\x53'
|
|
msg += b'\x57\x35\x42\x4c\x45\x5f\x31\x37\x5f\x30\x32\x42\x30\x5f\x31\x2e'
|
|
msg += b'\x30\x35\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x40\x2a\x8f\x4f\x51\x54\x31\x39\x32\x2e'
|
|
msg += b'\x31\x36\x38\x2e\x38\x30\x2e\x34\x39\x00\x00\x00\x0f\x00\x01\xb0'
|
|
msg += b'\x02\x0f\x00\xff\x56\x31\x2e\x31\x2e\x30\x30\x2e\x30\x42\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xfe\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x41\x6c\x6c\x69\x75\x73\x2d\x48\x6f'
|
|
msg += b'\x6d\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += incorrect_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def inverter_ind_msg(): # 0x4210
|
|
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'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x40\x10\x08\xc8\x00\x49\x13\x8d\x00\x36\x00\x00\x02\x58\x06\x7a'
|
|
msg += b'\x01\x61\x00\xa8\x02\x54\x01\x5a\x00\x8a\x01\xe4\x01\x5a\x00\xbd'
|
|
msg += b'\x02\x8f\x00\x11\x00\x01\x00\x00\x00\x0b\x00\x00\x27\x98\x00\x04'
|
|
msg += b'\x00\x00\x0c\x04\x00\x03\x00\x00\x0a\xe7\x00\x05\x00\x00\x0c\x75'
|
|
msg += b'\x00\x00\x00\x00\x06\x16\x02\x00\x00\x00\x55\xaa\x00\x01\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\xff\xff\x07\xd0\x00\x03\x04\x00\x04\x00\x04\x00'
|
|
msg += b'\x04\x00\x00\x01\xff\xff\x00\x01\x00\x06\x00\x68\x00\x68\x05\x00'
|
|
msg += b'\x09\xcd\x07\xb6\x13\x9c\x13\x24\x00\x01\x07\xae\x04\x0f\x00\x41'
|
|
msg += b'\x00\x0f\x0a\x64\x0a\x64\x00\x06\x00\x06\x09\xf6\x12\x8c\x12\x8c'
|
|
msg += b'\x00\x10\x00\x10\x14\x52\x14\x52\x00\x10\x00\x10\x01\x51\x00\x05'
|
|
msg += b'\x04\x00\x00\x01\x13\x9c\x0f\xa0\x00\x4e\x00\x66\x03\xe8\x04\x00'
|
|
msg += b'\x09\xce\x07\xa8\x13\x9c\x13\x26\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x04\x00\x04\x00\x00\x00\x00\x00\xff\xff\x00\x00'
|
|
msg += b'\x00\x00\x00\x00'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def inverter_ind_msg1600(): # 0x4210 rated Power 1600W
|
|
msg = b'\xa5\x99\x01\x10\x42\xe6\x9e' +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'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x40\x10\x08\xc8\x00\x49\x13\x8d\x00\x36\x00\x00\x06\x40\x06\x7a'
|
|
msg += b'\x01\x61\x00\xa8\x02\x54\x01\x5a\x00\x8a\x01\xe4\x01\x5a\x00\xbd'
|
|
msg += b'\x02\x8f\x00\x11\x00\x01\x00\x00\x00\x0b\x00\x00\x27\x98\x00\x04'
|
|
msg += b'\x00\x00\x0c\x04\x00\x03\x00\x00\x0a\xe7\x00\x05\x00\x00\x0c\x75'
|
|
msg += b'\x00\x00\x00\x00\x06\x16\x02\x00\x00\x00\x55\xaa\x00\x01\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\xff\xff\x06\x40\x00\x03\x04\x00\x04\x00\x04\x00'
|
|
msg += b'\x04\x00\x00\x01\xff\xff\x00\x01\x00\x06\x00\x68\x00\x68\x05\x00'
|
|
msg += b'\x09\xcd\x07\xb6\x13\x9c\x13\x24\x00\x01\x07\xae\x04\x0f\x00\x41'
|
|
msg += b'\x00\x0f\x0a\x64\x0a\x64\x00\x06\x00\x06\x09\xf6\x12\x8c\x12\x8c'
|
|
msg += b'\x00\x10\x00\x10\x14\x52\x14\x52\x00\x10\x00\x10\x01\x51\x00\x05'
|
|
msg += b'\x04\x00\x00\x01\x13\x9c\x0f\xa0\x00\x4e\x00\x66\x03\xe8\x04\x00'
|
|
msg += b'\x09\xce\x07\xa8\x13\x9c\x13\x26\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x04\x00\x04\x00\x00\x00\x00\x00\xff\xff\x00\x00'
|
|
msg += b'\x00\x00\x00\x00'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def inverter_ind_msg1800(): # 0x4210 rated Power 1800W
|
|
msg = b'\xa5\x99\x01\x10\x42\xe6\x9e' +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'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x40\x10\x08\xc8\x00\x49\x13\x8d\x00\x36\x00\x00\x07\x08\x06\x7a'
|
|
msg += b'\x01\x61\x00\xa8\x02\x54\x01\x5a\x00\x8a\x01\xe4\x01\x5a\x00\xbd'
|
|
msg += b'\x02\x8f\x00\x11\x00\x01\x00\x00\x00\x0b\x00\x00\x27\x98\x00\x04'
|
|
msg += b'\x00\x00\x0c\x04\x00\x03\x00\x00\x0a\xe7\x00\x05\x00\x00\x0c\x75'
|
|
msg += b'\x00\x00\x00\x00\x06\x16\x02\x00\x00\x00\x55\xaa\x00\x01\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\xff\xff\x07\x08\x00\x03\x04\x00\x04\x00\x04\x00'
|
|
msg += b'\x04\x00\x00\x01\xff\xff\x00\x01\x00\x06\x00\x68\x00\x68\x05\x00'
|
|
msg += b'\x09\xcd\x07\xb6\x13\x9c\x13\x24\x00\x01\x07\xae\x04\x0f\x00\x41'
|
|
msg += b'\x00\x0f\x0a\x64\x0a\x64\x00\x06\x00\x06\x09\xf6\x12\x8c\x12\x8c'
|
|
msg += b'\x00\x10\x00\x10\x14\x52\x14\x52\x00\x10\x00\x10\x01\x51\x00\x05'
|
|
msg += b'\x04\x00\x00\x01\x13\x9c\x0f\xa0\x00\x4e\x00\x66\x03\xe8\x04\x00'
|
|
msg += b'\x09\xce\x07\xa8\x13\x9c\x13\x26\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x04\x00\x04\x00\x00\x00\x00\x00\xff\xff\x00\x00'
|
|
msg += b'\x00\x00\x00\x00'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def inverter_ind_msg2000(): # 0x4210 rated Power 2000W
|
|
msg = b'\xa5\x99\x01\x10\x42\xe6\x9e' +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'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x40\x10\x08\xc8\x00\x49\x13\x8d\x00\x36\x00\x00\x07\xd0\x06\x7a'
|
|
msg += b'\x01\x61\x00\xa8\x02\x54\x01\x5a\x00\x8a\x01\xe4\x01\x5a\x00\xbd'
|
|
msg += b'\x02\x8f\x00\x11\x00\x01\x00\x00\x00\x0b\x00\x00\x27\x98\x00\x04'
|
|
msg += b'\x00\x00\x0c\x04\x00\x03\x00\x00\x0a\xe7\x00\x05\x00\x00\x0c\x75'
|
|
msg += b'\x00\x00\x00\x00\x06\x16\x02\x00\x00\x00\x55\xaa\x00\x01\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\xff\xff\x07\xd0\x00\x03\x04\x00\x04\x00\x04\x00'
|
|
msg += b'\x04\x00\x00\x01\xff\xff\x00\x01\x00\x06\x00\x68\x00\x68\x05\x00'
|
|
msg += b'\x09\xcd\x07\xb6\x13\x9c\x13\x24\x00\x01\x07\xae\x04\x0f\x00\x41'
|
|
msg += b'\x00\x0f\x0a\x64\x0a\x64\x00\x06\x00\x06\x09\xf6\x12\x8c\x12\x8c'
|
|
msg += b'\x00\x10\x00\x10\x14\x52\x14\x52\x00\x10\x00\x10\x01\x51\x00\x05'
|
|
msg += b'\x04\x00\x00\x01\x13\x9c\x0f\xa0\x00\x4e\x00\x66\x03\xe8\x04\x00'
|
|
msg += b'\x09\xce\x07\xa8\x13\x9c\x13\x26\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x04\x00\x04\x00\x00\x00\x00\x00\xff\xff\x00\x00'
|
|
msg += b'\x00\x00\x00\x00'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def inverter_ind_msg800(): # 0x4210 rated Power 800W
|
|
msg = b'\xa5\x99\x01\x10\x42\xe6\x9e' +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'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x40\x10\x08\xc8\x00\x49\x13\x8d\x00\x36\x00\x00\x03\x20\x06\x7a'
|
|
msg += b'\x01\x61\x00\xa8\x02\x54\x01\x5a\x00\x8a\x01\xe4\x01\x5a\x00\xbd'
|
|
msg += b'\x02\x8f\x00\x11\x00\x01\x00\x00\x00\x0b\x00\x00\x27\x98\x00\x04'
|
|
msg += b'\x00\x00\x0c\x04\x00\x03\x00\x00\x0a\xe7\x00\x05\x00\x00\x0c\x75'
|
|
msg += b'\x00\x00\x00\x00\x06\x16\x02\x00\x00\x00\x55\xaa\x00\x01\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\xff\xff\x03\x20\x00\x03\x04\x00\x04\x00\x04\x00'
|
|
msg += b'\x04\x00\x00\x01\xff\xff\x00\x01\x00\x06\x00\x68\x00\x68\x05\x00'
|
|
msg += b'\x09\xcd\x07\xb6\x13\x9c\x13\x24\x00\x01\x07\xae\x04\x0f\x00\x41'
|
|
msg += b'\x00\x0f\x0a\x64\x0a\x64\x00\x06\x00\x06\x09\xf6\x12\x8c\x12\x8c'
|
|
msg += b'\x00\x10\x00\x10\x14\x52\x14\x52\x00\x10\x00\x10\x01\x51\x00\x05'
|
|
msg += b'\x04\x00\x00\x01\x13\x9c\x0f\xa0\x00\x4e\x00\x66\x03\xe8\x04\x00'
|
|
msg += b'\x09\xce\x07\xa8\x13\x9c\x13\x26\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x04\x00\x04\x00\x00\x00\x00\x00\xff\xff\x00\x00'
|
|
msg += b'\x00\x00\x00\x00'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def inverter_ind_msg900(): # 0x4210 rated Power 900W
|
|
msg = b'\xa5\x99\x01\x10\x42\xe6\x9e' +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'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x40\x10\x08\xc8\x00\x49\x13\x8d\x00\x36\x00\x00\x03\x84\x06\x7a'
|
|
msg += b'\x01\x61\x00\xa8\x02\x54\x01\x5a\x00\x8a\x01\xe4\x01\x5a\x00\xbd'
|
|
msg += b'\x02\x8f\x00\x11\x00\x01\x00\x00\x00\x0b\x00\x00\x27\x98\x00\x04'
|
|
msg += b'\x00\x00\x0c\x04\x00\x03\x00\x00\x0a\xe7\x00\x05\x00\x00\x0c\x75'
|
|
msg += b'\x00\x00\x00\x00\x06\x16\x02\x00\x00\x00\x55\xaa\x00\x01\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\xff\xff\x03\x84\x00\x03\x04\x00\x04\x00\x04\x00'
|
|
msg += b'\x04\x00\x00\x01\xff\xff\x00\x01\x00\x06\x00\x68\x00\x68\x05\x00'
|
|
msg += b'\x09\xcd\x07\xb6\x13\x9c\x13\x24\x00\x01\x07\xae\x04\x0f\x00\x41'
|
|
msg += b'\x00\x0f\x0a\x64\x0a\x64\x00\x06\x00\x06\x09\xf6\x12\x8c\x12\x8c'
|
|
msg += b'\x00\x10\x00\x10\x14\x52\x14\x52\x00\x10\x00\x10\x01\x51\x00\x05'
|
|
msg += b'\x04\x00\x00\x01\x13\x9c\x0f\xa0\x00\x4e\x00\x66\x03\xe8\x04\x00'
|
|
msg += b'\x09\xce\x07\xa8\x13\x9c\x13\x26\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x04\x00\x04\x00\x00\x00\x00\x00\xff\xff\x00\x00'
|
|
msg += b'\x00\x00\x00\x00'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def inverter_ind_msg_81(): # 0x4210 fcode 0x81
|
|
msg = b'\xa5\x99\x01\x10\x42\x02\x03' +get_sn() +b'\x81\xb0\x02\xbc\xc8'
|
|
msg += b'\x24\x32\x6c\x1f\x00\x00\xa0\x07\x04\x03\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'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x40\x10\x08\xc8\x00\x49\x13\x8d\x00\x36\x00\x00\x02\x58\x06\x7a'
|
|
msg += b'\x01\x61\x00\xa8\x02\x54\x01\x5a\x00\x8a\x01\xe4\x01\x5a\x00\xbd'
|
|
msg += b'\x02\x8f\x00\x11\x00\x01\x00\x00\x00\x0b\x00\x00\x27\x98\x00\x04'
|
|
msg += b'\x00\x00\x0c\x04\x00\x03\x00\x00\x0a\xe7\x00\x05\x00\x00\x0c\x75'
|
|
msg += b'\x00\x00\x00\x00\x06\x16\x02\x00\x00\x00\x55\xaa\x00\x01\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\xff\xff\x07\xd0\x00\x03\x04\x00\x04\x00\x04\x00'
|
|
msg += b'\x04\x00\x00\x01\xff\xff\x00\x01\x00\x06\x00\x68\x00\x68\x05\x00'
|
|
msg += b'\x09\xcd\x07\xb6\x13\x9c\x13\x24\x00\x01\x07\xae\x04\x0f\x00\x41'
|
|
msg += b'\x00\x0f\x0a\x64\x0a\x64\x00\x06\x00\x06\x09\xf6\x12\x8c\x12\x8c'
|
|
msg += b'\x00\x10\x00\x10\x14\x52\x14\x52\x00\x10\x00\x10\x01\x51\x00\x05'
|
|
msg += b'\x04\x00\x00\x01\x13\x9c\x0f\xa0\x00\x4e\x00\x66\x03\xe8\x04\x00'
|
|
msg += b'\x09\xce\x07\xa8\x13\x9c\x13\x26\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x04\x00\x04\x00\x00\x00\x00\x00\xff\xff\x00\x00'
|
|
msg += b'\x00\x00\x00\x00'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def inverter_rsp_msg(): # 0x1210
|
|
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
|
|
|
|
@pytest.fixture
|
|
def inverter_rsp_msg_81(): # 0x1210 fcode 0x81
|
|
msg = b'\xa5\x0a\x00\x10\x12\x03\03' +get_sn() +b'\x81\x01'
|
|
msg += total()
|
|
msg += hb()
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def unknown_msg(): # 0x5110
|
|
msg = b'\xa5\x0a\x00\x10\x51\x10\x84' +get_sn() +b'\x01\x01\x69\x6f\x09'
|
|
msg += b'\x66\x78\x00\x00\x00'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def sync_start_ind_msg(): # 0x4310
|
|
msg = b'\xa5\x2f\x00\x10\x43\x0c\x0d' +get_sn() +b'\x81\x7a\x0b\x2e\x32'
|
|
msg += b'\x39\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x41\x6c\x6c\x69\x75\x73'
|
|
msg += b'\x2d\x48\x6f\x6d\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x61\x01'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def sync_start_rsp_msg(): # 0x1310
|
|
msg = b'\xa5\x0a\x00\x10\x13\x0d\x0d' +get_sn() +b'\x81\x01'
|
|
msg += total()
|
|
msg += hb()
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def sync_start_fwd_msg(): # 0x4310
|
|
msg = b'\xa5\x2f\x00\x10\x43\x0d\x0e' +get_sn() +b'\x81\x7a\x0b\x2e\x32'
|
|
msg += b'\x39\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x41\x6c\x6c\x69\x75\x73'
|
|
msg += b'\x2d\x48\x6f\x6d\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x61\x01'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
|
|
@pytest.fixture
|
|
def at_command_ind_msg(): # 0x4510
|
|
msg = b'\xa5\x27\x00\x10\x45\x03\x02' +get_sn() +b'\x01\x02\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'AT+TIME=214028,1,60,120\r'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def at_command_ind_msg_block(): # 0x4510
|
|
msg = b'\xa5\x17\x00\x10\x45\x03\x02' +get_sn() +b'\x01\x02\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'AT+WEBU\r'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def at_command_rsp_msg(): # 0x1510
|
|
msg = b'\xa5\x11\x00\x10\x15\x03\x03' +get_sn() +b'\x01\x01'
|
|
msg += total()
|
|
msg += hb()
|
|
msg += b'\x00\x00\x00\x00+ok'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def at_command_interim_rsp_msg(): # 0x0510
|
|
msg = b'\xa5\x25\x00\x10\x05\x03\x03' +get_sn() +b'\x08\x01'
|
|
msg += total()
|
|
msg += hb()
|
|
msg += b'\x00\x00\x00\x00+ok=10\x2c'
|
|
msg += b'start download\x0d\x0a'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def heartbeat_ind_msg(): # 0x4710
|
|
msg = b'\xa5\x01\x00\x10\x47\x10\x84' +get_sn()
|
|
msg += b'\x00'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def heartbeat_rsp_msg(): # 0x1710
|
|
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
|
|
|
|
@pytest.fixture
|
|
def sync_end_ind_msg(): # 0x4810
|
|
msg = b'\xa5\x3c\x00\x10\x48\x06\x07' +get_sn() +b'\x01\xa5\x3c\x2e\x32'
|
|
msg += b'\x2c\x00\x00\x00\xc1\x01\xec\x33\x01\x05\x2c\xff\xff\xff\xff\xff'
|
|
msg += b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
|
|
msg += b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'
|
|
msg += b'\xff\xff\xff\xff\xff\xff\xff'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def sync_end_rsp_msg(): # 0x1810
|
|
msg = b'\xa5\x0a\x00\x10\x18\x07\x07' +get_sn() +b'\x01\x01'
|
|
msg += total()
|
|
msg += hb()
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def msg_modbus_cmd():
|
|
msg = b'\xa5\x17\x00\x10\x45\x03\x02' +get_sn() +b'\x02\xb0\x02'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x06\x20\x08'
|
|
msg += b'\x00\x00\x03\xc8'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def msg_modbus_cmd_fwd():
|
|
msg = b'\xa5\x17\x00\x10\x45\x01\x00' +get_sn() +b'\x02\xb0\x02'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x06\x20\x08'
|
|
msg += b'\x00\x00\x03\xc8'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def msg_modbus_cmd_seq():
|
|
msg = b'\xa5\x17\x00\x10\x45\x03\x02' +get_sn() +b'\x05\x26\x30\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x06\x01\x00\x01'
|
|
msg += b'\x03\xe8'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def msg_modbus_cmd_crc_err():
|
|
msg = b'\xa5\x17\x00\x10\x45\x03\x02' +get_sn() +b'\x02\xb0\x02'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x06\x20\x08'
|
|
msg += b'\x00\x00\x04\xc8'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def msg_modbus_rsp(): # 0x1510
|
|
msg = b'\xa5\x3b\x00\x10\x15\x03\x03' +get_sn() +b'\x02\x01'
|
|
msg += total()
|
|
msg += hb()
|
|
msg += b'\x0a\xe2\xfa\x33\x01\x03\x28\x40\x10\x08\xd8'
|
|
msg += b'\x00\x00\x13\x87\x00\x31\x00\x68\x02\x58\x00\x00\x01\x53\x00\x02'
|
|
msg += b'\x00\x00\x01\x52\x00\x02\x00\x00\x01\x53\x00\x03\x00\x00\x00\x04'
|
|
msg += b'\x00\x01\x00\x00\x6c\x68'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def msg_modbus_interim_rsp(): # 0x0510
|
|
msg = b'\xa5\x3b\x00\x10\x15\x03\x03' +get_sn() +b'\x02\x01'
|
|
msg += total()
|
|
msg += hb()
|
|
msg += b'\x0a\xe2\xfa\x33\x01\x03\x28\x40\x10\x08\xd8'
|
|
msg += b'\x00\x00\x13\x87\x00\x31\x00\x68\x02\x58\x00\x00\x01\x53\x00\x02'
|
|
msg += b'\x00\x00\x01\x52\x00\x02\x00\x00\x01\x53\x00\x03\x00\x00\x00\x04'
|
|
msg += b'\x00\x01\x00\x00\x6c\x68'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def msg_modbus_rsp_inv_id2(): # 0x1510
|
|
msg = b'\xa5\x3b\x00\x10\x15\x03\x03' +get_sn() +b'\x02\x01'
|
|
msg += total()
|
|
msg += hb()
|
|
msg += b'\x0a\xe2\xfa\x33\x02\x03\x28\x40\x10\x08\xd8'
|
|
msg += b'\x00\x00\x13\x87\x00\x31\x00\x68\x02\x58\x00\x00\x01\x53\x00\x02'
|
|
msg += b'\x00\x00\x01\x52\x00\x02\x00\x00\x01\x53\x00\x03\x00\x00\x00\x04'
|
|
msg += b'\x00\x01\x00\x00\x2a\xaa'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def msg_modbus_invalid(): # 0x1510
|
|
msg = b'\xa5\x3b\x00\x10\x15\x03\x03' +get_sn() +b'\x02\x00'
|
|
msg += total()
|
|
msg += hb()
|
|
msg += b'\x0a\xe2\xfa\x33\x01\x03\x28\x40\x10\x08\xd8'
|
|
msg += b'\x00\x00\x13\x87\x00\x31\x00\x68\x02\x58\x00\x00\x01\x53\x00\x02'
|
|
msg += b'\x00\x00\x01\x52\x00\x02\x00\x00\x01\x53\x00\x03\x00\x00\x00\x04'
|
|
msg += b'\x00\x01\x00\x00\x6c\x68'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def msg_unknown_cmd():
|
|
msg = b'\xa5\x17\x00\x10\x45\x03\x02' +get_sn() +b'\x03\xb0\x02'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x06\x20\x08'
|
|
msg += b'\x00\x00\x03\xc8'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def msg_unknown_cmd_rsp(): # 0x1510
|
|
msg = b'\xa5\x3b\x00\x10\x15\x03\x03' +get_sn() +b'\x03\x01'
|
|
msg += total()
|
|
msg += hb()
|
|
msg += b'\x0a\xe2\xfa\x33\x01\x03\x28\x40\x10\x08\xd8'
|
|
msg += b'\x00\x00\x13\x87\x00\x31\x00\x68\x02\x58\x00\x00\x01\x53\x00\x02'
|
|
msg += b'\x00\x00\x01\x52\x00\x02\x00\x00\x01\x53\x00\x03\x00\x00\x00\x04'
|
|
msg += b'\x00\x01\x00\x00\x6c\x68'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def dcu_modbus_rsp(): # 0x1510
|
|
msg = b'\xa5\x6d\x00\x10\x15\x03\x03' +get_dcu_sn() +b'\x02\x01'
|
|
msg += total()
|
|
msg += hb()
|
|
msg += b'\x4d\x0d\x84\x34\x01\x03\x5a\x34\x31\x30\x31'
|
|
msg += b'\x32\x34\x30\x37\x30\x31\x34\x39\x30\x33\x31\x34\x00\x32\x00\x00'
|
|
msg += b'\x00\x32\x00\x00\x00\x00\x10\x7b\x00\x02\x00\x02\x14\x9b\xfe\xfd'
|
|
msg += b'\x25\x28\x0c\xe1\x0c\xde\x0c\xe1\x0c\xe1\x0c\xe0\x0c\xe1\x0c\xe3'
|
|
msg += b'\x0c\xdf\x0c\xe0\x0c\xe2\x0c\xe1\x0c\xe1\x0c\xe2\x0c\xe2\x0c\xe3'
|
|
msg += b'\x0c\xdf\x00\x14\x00\x14\x00\x13\x0f\x94\x01\x4a\x00\x01\x00\x15'
|
|
msg += b'\x00\x00\x02\x05\x02\x01\x14\xab'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def dcu_dev_ind_msg(): # 0x4110
|
|
msg = b'\xa5\x3a\x01\x10\x41\x91\x01' +get_dcu_sn() +b'\x02\xc6\xde\x2d\x32'
|
|
msg += b'\x27\x00\x00\x00\x00\x00\x00\x00\x05\x3c\x78\x01\x5c\x01\x4c\x53'
|
|
msg += b'\x57\x35\x5f\x30\x31\x5f\x33\x30\x32\x36\x5f\x4e\x53\x5f\x30\x35'
|
|
msg += b'\x5f\x30\x31\x2e\x30\x30\x2e\x30\x30\x2e\x30\x30\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\xd4\x27\x87\x12\xad\xc0\x31\x39\x32\x2e'
|
|
msg += b'\x31\x36\x38\x2e\x39\x2e\x31\x34\x00\x00\x00\x00\x01\x00\x01\x26'
|
|
msg += b'\x30\x0f\x00\xff\x56\x31\x2e\x31\x2e\x30\x30\x2e\x30\x42\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xfe\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x7a\x75\x68\x61\x75\x73\x65\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x08\x01\x01\x01\x00\x00\x00\x00'
|
|
msg += b'\x00\x00\x00\x00\x01'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def dcu_dev_rsp_msg(): # 0x1110
|
|
msg = b'\xa5\x0a\x00\x10\x11\x92\x01' +get_dcu_sn() +b'\x02\x01'
|
|
msg += total()
|
|
msg += hb()
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def dcu_data_ind_msg(): # 0x4210
|
|
msg = b'\xa5\x6f\x00\x10\x42\x92\x02' +get_dcu_sn() +b'\x01\x26\x30\xc7\xde'
|
|
msg += b'\x2d\x32\x28\x00\x00\x00\x84\x17\x79\x35\x01\x00\x4c\x12\x00\x00'
|
|
msg += b'\x34\x31\x30\x31\x32\x34\x30\x37\x30\x31\x34\x39\x30\x33\x31\x34'
|
|
msg += b'\x0d\x3a\x00\x00\x0d\x2c\x00\x00\x00\x00\x08\x20\x00\x00\x00\x00'
|
|
msg += b'\x14\x0e\xff\xfe\x03\xe8\x0c\x89\x0c\x89\x0c\x89\x0c\x8a\x0c\x89'
|
|
msg += b'\x0c\x89\x0c\x8a\x0c\x89\x0c\x89\x0c\x8a\x0c\x8a\x0c\x89\x0c\x89'
|
|
msg += b'\x0c\x89\x0c\x89\x0c\x88\x00\x0f\x00\x0f\x00\x0f\x00\x0e\x00\x00'
|
|
msg += b'\x00\x00\x00\x0f\x00\x00\x02\x05\x02\x01'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def dcu_data_rsp_msg(): # 0x1210
|
|
msg = b'\xa5\x0a\x00\x10\x12\x93\x02' +get_dcu_sn() +b'\x01\x01'
|
|
msg += total()
|
|
msg += hb()
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def dcu_command_ind_msg(): # 0x4510
|
|
msg = b'\xa5\x17\x00\x10\x45\x94\x02' +get_dcu_sn() +b'\x05\x26\x30'
|
|
msg += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
msg += b'\x01\x01\x06\x01\x00\x01\x03\xe8'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def dcu_command_rsp_msg(): # 0x1510
|
|
msg = b'\xa5\x11\x00\x10\x15\x94\x03' +get_dcu_sn() +b'\x05\x01'
|
|
msg += total()
|
|
msg += hb()
|
|
msg += b'\x00\x00\x00\x00'
|
|
msg += b'\x01\x01\x01'
|
|
msg += correct_checksum(msg)
|
|
msg += b'\x15'
|
|
return msg
|
|
|
|
@pytest.fixture
|
|
def config_tsun_allow_all():
|
|
Config.act_config = {
|
|
'ha':{
|
|
'auto_conf_prefix': 'homeassistant',
|
|
'discovery_prefix': 'homeassistant',
|
|
'entity_prefix': 'tsun',
|
|
'proxy_node_id': 'test_1',
|
|
'proxy_unique_id': ''
|
|
},
|
|
'solarman':{'enabled': True}, 'inverters':{'allow_all':True}}
|
|
Proxy.class_init()
|
|
Proxy.mqtt = Mqtt() # set dummy mqtt instance
|
|
|
|
@pytest.fixture
|
|
def config_no_tsun_inv1():
|
|
Config.act_config = {'solarman':{'enabled': False},'inverters':{'Y170000000000001':{'monitor_sn': 2070233889, 'node_id':'inv1/', 'modbus_polling': True, 'suggested_area':'roof', 'sensor_list': 688}}}
|
|
|
|
@pytest.fixture
|
|
def config_tsun_inv1():
|
|
Config.act_config = {
|
|
'ha':{
|
|
'auto_conf_prefix': 'homeassistant',
|
|
'discovery_prefix': 'homeassistant',
|
|
'entity_prefix': 'tsun',
|
|
'proxy_node_id': 'test_1',
|
|
'proxy_unique_id': ''
|
|
},
|
|
'solarman':{'enabled': True, 'host': 'test_cloud.local', 'port': 1234},'inverters':{'Y170000000000001':{'monitor_sn': 2070233889, 'node_id':'inv1/', 'modbus_polling': True, 'suggested_area':'roof', 'sensor_list': 0}}}
|
|
Proxy.class_init()
|
|
Proxy.mqtt = Mqtt()
|
|
|
|
@pytest.fixture
|
|
def config_tsun_scan():
|
|
Config.act_config = {'solarman':{'enabled': True},'inverters':{'Y170000000000001':{'monitor_sn': 2070233889, 'node_id':'inv1/', 'modbus_polling': True, 'modbus_scanning': {'start': 0xffc0, 'step': 0x40, 'bytes':20}, 'suggested_area':'roof', 'sensor_list': 0}}}
|
|
|
|
@pytest.fixture
|
|
def config_tsun_scan_dcu():
|
|
Config.act_config = {'solarman':{'enabled': True},'inverters':{'4100000000000001':{'monitor_sn': 2070233888, 'node_id':'inv1/', 'modbus_polling': True, 'modbus_scanning': {'start': 0x0000, 'step': 0x100, 'bytes':0x2d}, 'client_mode': {'host': '192.168.1.1.'}, 'suggested_area':'roof', 'sensor_list': 0}}}
|
|
|
|
@pytest.fixture
|
|
def config_tsun_dcu1():
|
|
Config.act_config = {
|
|
'ha':{
|
|
'auto_conf_prefix': 'homeassistant',
|
|
'discovery_prefix': 'homeassistant',
|
|
'entity_prefix': 'tsun',
|
|
'proxy_node_id': 'test_1',
|
|
'proxy_unique_id': ''
|
|
},
|
|
'solarman':{'enabled': True, 'host': 'test_cloud.local', 'port': 1234},'batteries':{'4100000000000001':{'monitor_sn': 2070233888, 'node_id':'inv1/', 'modbus_polling': True, 'suggested_area':'roof', 'sensor_list': 0}}}
|
|
Proxy.class_init()
|
|
Proxy.mqtt = Mqtt()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_message(device_ind_msg):
|
|
Config.act_config = {'solarman':{'enabled': True}}
|
|
m = MemoryStream(device_ind_msg, (0,))
|
|
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 == 1
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == None
|
|
assert m.control == 0x4110
|
|
assert str(m.seq) == '01:00'
|
|
assert m.data_len == 0xd4
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_invalid_start_byte(invalid_start_byte, device_ind_msg):
|
|
# received a message with wrong start byte plus an valid message
|
|
# the complete receive buffer must be cleared to
|
|
# find the next valid message
|
|
Config.act_config = {'solarman':{'enabled': True}}
|
|
m = MemoryStream(invalid_start_byte, (0,))
|
|
m.append_msg(device_ind_msg)
|
|
m.read() # read complete msg, and dispatch msg
|
|
assert not m.header_valid # must be invalid, since start byte is wrong
|
|
assert m.msg_count == 0
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == 0
|
|
assert m.control == 0x4110
|
|
assert str(m.seq) == '01:00'
|
|
assert m.data_len == 0xd4
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_invalid_stop_byte(invalid_stop_byte):
|
|
# received a message with wrong stop byte
|
|
# the complete receive buffer must be cleared to
|
|
# find the next valid message
|
|
Config.act_config = {'solarman':{'enabled': True}}
|
|
m = MemoryStream(invalid_stop_byte, (0,))
|
|
m.read() # read complete msg, and dispatch msg
|
|
assert not m.header_valid # must be invalid, since start byte is wrong
|
|
assert m.msg_count == 1 # msg flush was called
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == 0
|
|
assert m.control == 0x4110
|
|
assert str(m.seq) == '01:00'
|
|
assert m.data_len == 0xd4
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_invalid_stop_byte2(invalid_stop_byte, device_ind_msg):
|
|
# received a message with wrong stop byte plus an valid message
|
|
# only the first message must be discarded
|
|
Config.act_config = {'solarman':{'enabled': True}}
|
|
m = MemoryStream(invalid_stop_byte, (0,))
|
|
m.append_msg(device_ind_msg)
|
|
|
|
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
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.msg_recvd[0]['control']==0x4110
|
|
assert m.msg_recvd[0]['seq']=='01:00'
|
|
assert m.msg_recvd[0]['data_len']==0xd4
|
|
assert m.msg_recvd[1]['control']==0x4110
|
|
assert m.msg_recvd[1]['seq']=='01:00'
|
|
assert m.msg_recvd[1]['data_len']==0xd4
|
|
|
|
assert m.unique_id == None
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_invalid_stop_start_byte(invalid_stop_byte, invalid_start_byte):
|
|
# received a message with wrong stop byte plus an invalid message
|
|
# with fron start byte
|
|
# the complete receive buffer must be cleared to
|
|
# find the next valid message
|
|
Config.act_config = {'solarman':{'enabled': True}}
|
|
m = MemoryStream(invalid_stop_byte, (0,))
|
|
m.append_msg(invalid_start_byte)
|
|
m.read() # read complete msg, and dispatch msg
|
|
assert not m.header_valid # must be invalid, since start byte is wrong
|
|
assert m.msg_count == 1 # msg flush was called
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == 0
|
|
assert m.control == 0x4110
|
|
assert str(m.seq) == '01:00'
|
|
assert m.data_len == 0xd4
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_invalid_checksum(invalid_checksum, device_ind_msg):
|
|
# received a message with wrong checksum plus an valid message
|
|
# only the first message must be discarded
|
|
Config.act_config = {'solarman':{'enabled': True}}
|
|
m = MemoryStream(invalid_checksum, (0,))
|
|
m.append_msg(device_ind_msg)
|
|
|
|
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
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == None
|
|
assert m.msg_recvd[0]['control']==0x4110
|
|
assert m.msg_recvd[0]['seq']=='01:00'
|
|
assert m.msg_recvd[0]['data_len']==0xd4
|
|
assert m.msg_recvd[1]['control']==0x4110
|
|
assert m.msg_recvd[1]['seq']=='01:00'
|
|
assert m.msg_recvd[1]['data_len']==0xd4
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_message_twice(config_no_tsun_inv1, device_ind_msg, device_rsp_msg):
|
|
_ = config_no_tsun_inv1
|
|
m = MemoryStream(device_ind_msg, (0,))
|
|
m.append_msg(device_ind_msg)
|
|
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
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == '2070233889'
|
|
assert m.msg_recvd[0]['control']==0x4110
|
|
assert m.msg_recvd[0]['seq']=='01:01'
|
|
assert m.msg_recvd[0]['data_len']==0xd4
|
|
assert m.msg_recvd[1]['control']==0x4110
|
|
assert m.msg_recvd[1]['seq']=='01:01'
|
|
assert m.msg_recvd[1]['data_len']==0xd4
|
|
assert m.ifc.tx_fifo.get()==device_rsp_msg+device_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_message_in_chunks(device_ind_msg):
|
|
Config.act_config = {'solarman':{'enabled': True}}
|
|
m = MemoryStream(device_ind_msg, (4,11,0))
|
|
m.read() # read 4 bytes, header incomplere
|
|
assert not m.header_valid # must be invalid, since header not complete
|
|
assert m.msg_count == 0
|
|
m.read() # read missing bytes for complete header
|
|
assert m.header_valid # must be valid, since header is complete but not the msg
|
|
assert m.msg_count == 0
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == 0 # should be None ?
|
|
assert m.control == 0x4110
|
|
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
|
|
assert not m.header_valid # must be invalid, since msg was handled and buffer flushed
|
|
assert m.msg_count == 1
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_message_in_chunks2(my_loop, config_tsun_inv1, device_ind_msg):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(device_ind_msg, (4,10,0))
|
|
m.read() # read 4 bytes, header incomplere
|
|
assert not m.header_valid
|
|
assert m.msg_count == 0
|
|
m.read() # read 6 more bytes, header incomplere
|
|
assert not m.header_valid
|
|
assert m.msg_count == 0
|
|
m.read() # read rest of message
|
|
assert not m.header_valid # must be invalid, since msg was handled and buffer flushed
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == '2070233889'
|
|
assert m.control == 0x4110
|
|
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
|
|
m.read() # read rest of message
|
|
assert m.msg_count == 1
|
|
assert not m.header_valid # must be invalid, since msg was handled and buffer flushed
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_two_messages(my_loop, config_tsun_allow_all, device_ind_msg, device_rsp_msg, inverter_ind_msg, inverter_rsp_msg):
|
|
_ = config_tsun_allow_all
|
|
m = MemoryStream(device_ind_msg, (0,))
|
|
m.append_msg(inverter_ind_msg)
|
|
assert 0 == m.sensor_list
|
|
m._init_new_client_conn()
|
|
m.read() # read complete msg, and dispatch msg
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
assert not m.header_valid # must be invalid, since msg was handled and buffer flushed
|
|
assert m.msg_count == 2
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == '2070233889'
|
|
assert m.msg_recvd[0]['control']==0x4110
|
|
assert m.msg_recvd[0]['seq']=='01:01'
|
|
assert m.msg_recvd[0]['data_len']==0xd4
|
|
assert m.msg_recvd[1]['control']==0x4210
|
|
assert m.msg_recvd[1]['seq']=='02:02'
|
|
assert m.msg_recvd[1]['data_len']==0x199
|
|
assert '02b0' == m.db.get_db_value(Register.SENSOR_LIST, None)
|
|
assert 0x02b0 == m.sensor_list
|
|
assert m.ifc.fwd_fifo.get()==device_ind_msg+inverter_ind_msg
|
|
assert m.ifc.tx_fifo.get()==device_rsp_msg+inverter_rsp_msg
|
|
|
|
m._init_new_client_conn()
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_two_messages2(my_loop, config_tsun_allow_all, inverter_ind_msg, inverter_ind_msg_81, inverter_rsp_msg, inverter_rsp_msg_81):
|
|
_ = config_tsun_allow_all
|
|
m = MemoryStream(inverter_ind_msg, (0,))
|
|
m.append_msg(inverter_ind_msg_81)
|
|
m.read() # read complete msg, and dispatch msg
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
assert not m.header_valid # must be invalid, since msg was handled and buffer flushed
|
|
assert m.msg_count == 2
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == '2070233889'
|
|
assert m.msg_recvd[0]['control']==0x4210
|
|
assert m.msg_recvd[0]['seq']=='02:02'
|
|
assert m.msg_recvd[0]['data_len']==0x199
|
|
assert m.msg_recvd[1]['control']==0x4210
|
|
assert m.msg_recvd[1]['seq']=='03:03'
|
|
assert m.msg_recvd[1]['data_len']==0x199
|
|
assert m.time_ofs == 0x33e447a0
|
|
assert m.ifc.fwd_fifo.get()==inverter_ind_msg+inverter_ind_msg_81
|
|
assert m.ifc.tx_fifo.get()==inverter_rsp_msg+inverter_rsp_msg_81
|
|
|
|
m._init_new_client_conn()
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_two_messages3(my_loop, config_tsun_allow_all, device_ind_msg2, device_rsp_msg2, inverter_ind_msg, inverter_rsp_msg):
|
|
# test device message received after the inverter masg
|
|
_ = config_tsun_allow_all
|
|
m = MemoryStream(inverter_ind_msg, (0,))
|
|
m.append_msg(device_ind_msg2)
|
|
assert 0 == m.sensor_list
|
|
m._init_new_client_conn()
|
|
m.read() # read complete msg, and dispatch msg
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
assert not m.header_valid # must be invalid, since msg was handled and buffer flushed
|
|
assert m.msg_count == 2
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == '2070233889'
|
|
assert m.msg_recvd[0]['control']==0x4210
|
|
assert m.msg_recvd[0]['seq']=='02:02'
|
|
assert m.msg_recvd[0]['data_len']==0x199
|
|
assert m.msg_recvd[1]['control']==0x4110
|
|
assert m.msg_recvd[1]['seq']=='03:03'
|
|
assert m.msg_recvd[1]['data_len']==0xd4
|
|
assert '02b0' == m.db.get_db_value(Register.SENSOR_LIST, None)
|
|
assert 0x02b0 == m.sensor_list
|
|
assert m.ifc.fwd_fifo.get()==inverter_ind_msg+device_ind_msg2
|
|
assert m.ifc.tx_fifo.get()==inverter_rsp_msg+device_rsp_msg2
|
|
|
|
m._init_new_client_conn()
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_read_two_messages4(my_loop, config_tsun_dcu1, dcu_dev_ind_msg, dcu_dev_rsp_msg, dcu_data_ind_msg, dcu_data_rsp_msg):
|
|
_ = config_tsun_dcu1
|
|
m = MemoryStream(dcu_dev_ind_msg, (0,))
|
|
m.append_msg(dcu_data_ind_msg)
|
|
assert 0 == m.sensor_list
|
|
m._init_new_client_conn()
|
|
m.read() # read complete msg, and dispatch msg
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
assert not m.header_valid # must be invalid, since msg was handled and buffer flushed
|
|
assert m.msg_count == 2
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233888
|
|
assert m.unique_id == '2070233888'
|
|
assert m.msg_recvd[0]['control']==0x4110
|
|
assert m.msg_recvd[0]['seq']=='01:92'
|
|
assert m.msg_recvd[0]['data_len']==314
|
|
assert m.msg_recvd[1]['control']==0x4210
|
|
assert m.msg_recvd[1]['seq']=='02:93'
|
|
assert m.msg_recvd[1]['data_len']==111
|
|
assert '3026' == m.db.get_db_value(Register.SENSOR_LIST, None)
|
|
assert 0x3026 == m.sensor_list
|
|
assert m.ifc.fwd_fifo.get()==dcu_dev_ind_msg+dcu_data_ind_msg
|
|
assert m.ifc.tx_fifo.get()==dcu_dev_rsp_msg+dcu_data_rsp_msg
|
|
|
|
m._init_new_client_conn()
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_unkown_frame_code(my_loop, config_tsun_inv1, inverter_ind_msg_81, inverter_rsp_msg_81):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(inverter_ind_msg_81, (0,))
|
|
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 == 1
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == '2070233889'
|
|
assert m.control == 0x4210
|
|
assert str(m.seq) == '03:03'
|
|
assert m.data_len == 0x199
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==inverter_rsp_msg_81
|
|
assert m.ifc.fwd_fifo.get()==inverter_ind_msg_81
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_unkown_message(my_loop, config_tsun_inv1, unknown_msg):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(unknown_msg, (0,))
|
|
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 == 1
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == '2070233889'
|
|
assert m.control == 0x5110
|
|
assert str(m.seq) == '84:10'
|
|
assert m.data_len == 0x0a
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==unknown_msg
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_device_rsp(my_loop, config_tsun_inv1, device_rsp_msg):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(device_rsp_msg, (0,), False)
|
|
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 == 1
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == '2070233889'
|
|
assert m.control == 0x1110
|
|
assert str(m.seq) == '01:01'
|
|
assert m.data_len == 0x0a
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_inverter_rsp(my_loop, config_tsun_inv1, inverter_rsp_msg):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(inverter_rsp_msg, (0,), False)
|
|
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 == 1
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == '2070233889'
|
|
assert m.control == 0x1210
|
|
assert str(m.seq) == '02:02'
|
|
assert m.data_len == 0x0a
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_heartbeat_ind(my_loop, config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp_msg):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(heartbeat_ind_msg, (0,))
|
|
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 == 1
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.control == 0x4710
|
|
assert str(m.seq) == '84:11' # value after sending response
|
|
assert m.data_len == 0x01
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==heartbeat_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==heartbeat_ind_msg
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_heartbeat_ind2(my_loop, config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp_msg):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(heartbeat_ind_msg, (0,))
|
|
m.no_forwarding = True
|
|
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 == 1
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.control == 0x4710
|
|
assert str(m.seq) == '84:11' # value after sending response
|
|
assert m.data_len == 0x01
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==heartbeat_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_heartbeat_rsp(my_loop, config_tsun_inv1, heartbeat_rsp_msg):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(heartbeat_rsp_msg, (0,), False)
|
|
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 == 1
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == '2070233889'
|
|
assert m.control == 0x1710
|
|
assert str(m.seq) == '11:84' # value after sending response
|
|
assert m.data_len == 0x0a
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sync_start_ind(my_loop, config_tsun_inv1, sync_start_ind_msg, sync_start_rsp_msg, sync_start_fwd_msg):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(sync_start_ind_msg, (0,))
|
|
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 == 1
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.control == 0x4310
|
|
assert str(m.seq) == '0d:0d' # value after sending response
|
|
assert m.data_len == 47
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==sync_start_rsp_msg
|
|
assert m.ifc.fwd_fifo.peek()==sync_start_ind_msg
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
|
|
m.seq.server_side = False # simulate forawding to TSUN cloud
|
|
m._SolarmanBase__update_header(m.ifc.fwd_fifo.peek())
|
|
assert str(m.seq) == '0d:0e' # value after forwarding indication
|
|
assert m.ifc.fwd_fifo.get()==sync_start_fwd_msg
|
|
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sync_start_rsp(my_loop, config_tsun_inv1, sync_start_rsp_msg):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(sync_start_rsp_msg, (0,), False)
|
|
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 == 1
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == '2070233889'
|
|
assert m.control == 0x1310
|
|
assert str(m.seq) == '0d:0d' # value after sending response
|
|
assert m.data_len == 0x0a
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sync_end_ind(my_loop, config_tsun_inv1, sync_end_ind_msg, sync_end_rsp_msg):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(sync_end_ind_msg, (0,))
|
|
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 == 1
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.control == 0x4810
|
|
assert str(m.seq) == '07:07' # value after sending response
|
|
assert m.data_len == 60
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==sync_end_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==sync_end_ind_msg
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sync_end_rsp(my_loop, config_tsun_inv1, sync_end_rsp_msg):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(sync_end_rsp_msg, (0,), False)
|
|
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 == 1
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.unique_id == '2070233889'
|
|
assert m.control == 0x1810
|
|
assert str(m.seq) == '07:07' # value after sending response
|
|
assert m.data_len == 0x0a
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_build_modell_600(my_loop, config_tsun_allow_all, inverter_ind_msg):
|
|
_ = config_tsun_allow_all
|
|
m = MemoryStream(inverter_ind_msg, (0,))
|
|
assert 0 == m.sensor_list
|
|
assert 0 == m.db.get_db_value(Register.MAX_DESIGNED_POWER, 0)
|
|
assert None == m.db.get_db_value(Register.RATED_POWER, None)
|
|
assert None == m.db.get_db_value(Register.INVERTER_TEMP, None)
|
|
m.read() # read complete msg, and dispatch msg
|
|
assert 2000 == m.db.get_db_value(Register.MAX_DESIGNED_POWER, 0)
|
|
assert 600 == m.db.get_db_value(Register.RATED_POWER, 0)
|
|
assert 4 == m.db.get_db_value(Register.NO_INPUTS, 0)
|
|
assert 'TSOL-MS2000(600)' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0)
|
|
assert '02b0' == m.db.get_db_value(Register.SENSOR_LIST, None)
|
|
assert 0 == m.sensor_list # must not been set by an inverter data ind
|
|
|
|
m.ifc.tx_clear() # clear send buffer for next test
|
|
m._init_new_client_conn()
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_build_modell_1600(my_loop, config_tsun_allow_all, inverter_ind_msg1600):
|
|
_ = config_tsun_allow_all
|
|
m = MemoryStream(inverter_ind_msg1600, (0,))
|
|
assert 0 == m.db.get_db_value(Register.MAX_DESIGNED_POWER, 0)
|
|
assert None == m.db.get_db_value(Register.RATED_POWER, None)
|
|
assert None == m.db.get_db_value(Register.INVERTER_TEMP, None)
|
|
m.read() # read complete msg, and dispatch msg
|
|
assert 1600 == m.db.get_db_value(Register.MAX_DESIGNED_POWER, 0)
|
|
assert 1600 == m.db.get_db_value(Register.RATED_POWER, 0)
|
|
assert 4 == m.db.get_db_value(Register.NO_INPUTS, 0)
|
|
assert 'TSOL-MS1600' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0)
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_build_modell_1800(my_loop, config_tsun_allow_all, inverter_ind_msg1800):
|
|
_ = config_tsun_allow_all
|
|
m = MemoryStream(inverter_ind_msg1800, (0,))
|
|
assert 0 == m.db.get_db_value(Register.MAX_DESIGNED_POWER, 0)
|
|
assert None == m.db.get_db_value(Register.RATED_POWER, None)
|
|
assert None == m.db.get_db_value(Register.INVERTER_TEMP, None)
|
|
m.read() # read complete msg, and dispatch msg
|
|
assert 1800 == m.db.get_db_value(Register.MAX_DESIGNED_POWER, 0)
|
|
assert 1800 == m.db.get_db_value(Register.RATED_POWER, 0)
|
|
assert 4 == m.db.get_db_value(Register.NO_INPUTS, 0)
|
|
assert 'TSOL-MS1800' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0)
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_build_modell_2000(my_loop, config_tsun_allow_all, inverter_ind_msg2000):
|
|
_ = config_tsun_allow_all
|
|
m = MemoryStream(inverter_ind_msg2000, (0,))
|
|
assert 0 == m.db.get_db_value(Register.MAX_DESIGNED_POWER, 0)
|
|
assert None == m.db.get_db_value(Register.RATED_POWER, None)
|
|
assert None == m.db.get_db_value(Register.INVERTER_TEMP, None)
|
|
m.read() # read complete msg, and dispatch msg
|
|
assert 2000 == m.db.get_db_value(Register.MAX_DESIGNED_POWER, 0)
|
|
assert 2000 == m.db.get_db_value(Register.RATED_POWER, 0)
|
|
assert 4 == m.db.get_db_value(Register.NO_INPUTS, 0)
|
|
assert 'TSOL-MS2000' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0)
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_build_modell_800(my_loop, config_tsun_allow_all, inverter_ind_msg800):
|
|
_ = config_tsun_allow_all
|
|
m = MemoryStream(inverter_ind_msg800, (0,))
|
|
assert 0 == m.db.get_db_value(Register.MAX_DESIGNED_POWER, 0)
|
|
assert None == m.db.get_db_value(Register.RATED_POWER, None)
|
|
assert None == m.db.get_db_value(Register.INVERTER_TEMP, None)
|
|
m.read() # read complete msg, and dispatch msg
|
|
assert 800 == m.db.get_db_value(Register.MAX_DESIGNED_POWER, 0)
|
|
assert 800 == m.db.get_db_value(Register.RATED_POWER, 0)
|
|
assert 2 == m.db.get_db_value(Register.NO_INPUTS, 0)
|
|
assert 'TSOL-MS800' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0)
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_build_modell_900(my_loop, config_tsun_allow_all, inverter_ind_msg900):
|
|
_ = config_tsun_allow_all
|
|
m = MemoryStream(inverter_ind_msg900, (0,))
|
|
assert 0 == m.db.get_db_value(Register.MAX_DESIGNED_POWER, 0)
|
|
assert None == m.db.get_db_value(Register.RATED_POWER, None)
|
|
assert None == m.db.get_db_value(Register.INVERTER_TEMP, None)
|
|
m.read() # read complete msg, and dispatch msg
|
|
assert 900 == m.db.get_db_value(Register.MAX_DESIGNED_POWER, 0)
|
|
assert 900 == m.db.get_db_value(Register.RATED_POWER, 0)
|
|
assert 2 == m.db.get_db_value(Register.NO_INPUTS, 0)
|
|
assert 'TSOL-MSxx00' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0)
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_build_logger_modell(my_loop, config_tsun_allow_all, device_ind_msg):
|
|
_ = config_tsun_allow_all
|
|
m = MemoryStream(device_ind_msg, (0,))
|
|
assert 0 == m.db.get_db_value(Register.COLLECTOR_FW_VERSION, 0)
|
|
assert 'IGEN TECH' == m.db.get_db_value(Register.CHIP_TYPE, None)
|
|
assert None == m.db.get_db_value(Register.CHIP_MODEL, None)
|
|
m.read() # read complete msg, and dispatch msg
|
|
assert 'LSW5BLE_17_02B0_1.05' == m.db.get_db_value(Register.CHIP_MODEL, 0)
|
|
assert 'V1.1.00.0B' == m.db.get_db_value(Register.COLLECTOR_FW_VERSION, 0).rstrip('\00')
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_msg_iterator(my_loop, config_tsun_inv1):
|
|
Message._registry.clear()
|
|
m1 = SolarmanV5(None, ('test1.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False)
|
|
m2 = SolarmanV5(None, ('test2.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False)
|
|
m3 = SolarmanV5(None, ('test3.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False)
|
|
m3.close()
|
|
del m3
|
|
test1 = 0
|
|
test2 = 0
|
|
for key in SolarmanV5:
|
|
if key == m1:
|
|
test1+=1
|
|
elif key == m2:
|
|
test2+=1
|
|
elif type(key) != SolarmanV5:
|
|
continue
|
|
else:
|
|
assert False
|
|
assert test1 == 1
|
|
assert test2 == 1
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_proxy_counter(my_loop, config_tsun_inv1):
|
|
m = SolarmanV5(None, ('test.local', 1234), ifc=AsyncIfcImpl(), server_side=True, client_mode=False)
|
|
assert m.new_data == {}
|
|
m.db.stat['proxy']['Unknown_Msg'] = 0
|
|
Infos.new_stat_data['proxy'] = False
|
|
|
|
m.inc_counter('Unknown_Msg')
|
|
assert m.new_data == {}
|
|
assert Infos.new_stat_data == {'proxy': True}
|
|
assert 1 == m.db.stat['proxy']['Unknown_Msg']
|
|
|
|
Infos.new_stat_data['proxy'] = False
|
|
m.dec_counter('Unknown_Msg')
|
|
assert m.new_data == {}
|
|
assert Infos.new_stat_data == {'proxy': True}
|
|
assert 0 == m.db.stat['proxy']['Unknown_Msg']
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_msg_build_modbus_req(my_loop, config_tsun_inv1, device_ind_msg, device_rsp_msg, inverter_ind_msg, inverter_rsp_msg, msg_modbus_cmd):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(device_ind_msg, (0,), True)
|
|
m.read()
|
|
assert m.control == 0x4110
|
|
assert str(m.seq) == '01:01'
|
|
assert m.ifc.tx_fifo.get()==device_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==device_ind_msg
|
|
|
|
await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG)
|
|
assert 0 == m.send_msg_ofs
|
|
assert m.ifc.fwd_fifo.get() == b''
|
|
assert m.sent_pdu == b'' # modbus command must be ignore, cause connection is still not up
|
|
assert m.ifc.tx_fifo.get() == b'' # modbus command must be ignore, cause connection is still not up
|
|
|
|
m.append_msg(inverter_ind_msg)
|
|
m.read()
|
|
assert m.control == 0x4210
|
|
assert str(m.seq) == '02:02'
|
|
assert m.msg_recvd[0]['control']==0x4110
|
|
assert m.msg_recvd[0]['seq']=='01:01'
|
|
assert m.msg_recvd[1]['control']==0x4210
|
|
assert m.msg_recvd[1]['seq']=='02:02'
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==inverter_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==inverter_ind_msg
|
|
|
|
await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG)
|
|
assert 0 == m.send_msg_ofs
|
|
assert m.ifc.fwd_fifo.get() == b''
|
|
assert m.sent_pdu == msg_modbus_cmd
|
|
assert m.ifc.tx_fifo.get()== b''
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_at_cmd(my_loop, config_tsun_allow_all, device_ind_msg, device_rsp_msg, inverter_ind_msg, inverter_rsp_msg, at_command_ind_msg, at_command_rsp_msg):
|
|
_ = config_tsun_allow_all
|
|
m = MemoryStream(device_ind_msg, (0,), True)
|
|
m.read() # read device ind
|
|
assert m.control == 0x4110
|
|
assert str(m.seq) == '01:01'
|
|
assert m.ifc.tx_fifo.get()==device_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==device_ind_msg
|
|
|
|
await m.send_at_cmd('AT+TIME=214028,1,60,120')
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.sent_pdu == b''
|
|
assert str(m.seq) == '01:01'
|
|
assert Proxy.mqtt.key == ''
|
|
assert Proxy.mqtt.data == ""
|
|
|
|
m.append_msg(inverter_ind_msg)
|
|
m.read() # read inverter ind
|
|
assert m.control == 0x4210
|
|
assert str(m.seq) == '02:02'
|
|
assert m.ifc.tx_fifo.get()==inverter_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==inverter_ind_msg
|
|
|
|
await m.send_at_cmd('AT+TIME=214028,1,60,120')
|
|
assert m.ifc.fwd_fifo.get() == b''
|
|
assert m.ifc.tx_fifo.get()== b''
|
|
assert m.sent_pdu == at_command_ind_msg
|
|
m.sent_pdu = bytearray()
|
|
|
|
assert str(m.seq) == '02:03'
|
|
assert Proxy.mqtt.key == ''
|
|
assert Proxy.mqtt.data == ""
|
|
|
|
m.append_msg(at_command_rsp_msg)
|
|
m.read() # read at resp
|
|
assert m.control == 0x1510
|
|
assert str(m.seq) == '03:03'
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert Proxy.mqtt.key == 'tsun/at_resp'
|
|
assert Proxy.mqtt.data == "+ok"
|
|
Proxy.mqtt.clear() # clear last test result
|
|
|
|
m.sent_pdu = bytearray()
|
|
m.test_exception_async_write = True
|
|
await m.send_at_cmd('AT+TIME=214028,1,60,120')
|
|
assert m.sent_pdu == b''
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.sent_pdu == b''
|
|
assert str(m.seq) == '03:04'
|
|
assert m.inverter.forward_at_cmd_resp == False
|
|
assert Proxy.mqtt.key == ''
|
|
assert Proxy.mqtt.data == ""
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_at_cmd_blocked(my_loop, config_tsun_allow_all, device_ind_msg, device_rsp_msg, inverter_ind_msg, inverter_rsp_msg, at_command_ind_msg):
|
|
_ = config_tsun_allow_all
|
|
m = MemoryStream(device_ind_msg, (0,), True)
|
|
m.read()
|
|
assert m.control == 0x4110
|
|
assert str(m.seq) == '01:01'
|
|
assert m.ifc.tx_fifo.get()==device_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==device_ind_msg
|
|
|
|
await m.send_at_cmd('AT+WEBU')
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert str(m.seq) == '01:01'
|
|
assert Proxy.mqtt.key == ''
|
|
assert Proxy.mqtt.data == ""
|
|
|
|
m.append_msg(inverter_ind_msg)
|
|
m.read()
|
|
assert m.control == 0x4210
|
|
assert str(m.seq) == '02:02'
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==inverter_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==inverter_ind_msg
|
|
|
|
await m.send_at_cmd('AT+WEBU')
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert str(m.seq) == '02:02'
|
|
assert m.inverter.forward_at_cmd_resp == False
|
|
assert Proxy.mqtt.key == 'tsun/at_resp'
|
|
assert Proxy.mqtt.data == "'AT+WEBU' is forbidden"
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_at_cmd_ind(my_loop, config_tsun_inv1, at_command_ind_msg, at_command_rsp_msg):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(at_command_ind_msg, (0,), False)
|
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
|
m.db.stat['proxy']['AT_Command'] = 0
|
|
m.db.stat['proxy']['AT_Command_Blocked'] = 0
|
|
m.db.stat['proxy']['Modbus_Command'] = 0
|
|
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 == 1
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.control == 0x4510
|
|
assert str(m.seq) == '03:02'
|
|
assert m.data_len == 39
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==at_command_ind_msg
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
assert m.db.stat['proxy']['AT_Command'] == 1
|
|
assert m.db.stat['proxy']['AT_Command_Blocked'] == 0
|
|
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
|
|
|
m.append_msg(at_command_rsp_msg)
|
|
m.read() # read at resp
|
|
assert m.control == 0x1510
|
|
assert str(m.seq) == '03:03'
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==at_command_rsp_msg
|
|
assert Proxy.mqtt.key == ''
|
|
assert Proxy.mqtt.data == ""
|
|
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_at_cmd_ind_block(my_loop, config_tsun_inv1, at_command_ind_msg_block):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(at_command_ind_msg_block, (0,), False)
|
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
|
m.db.stat['proxy']['AT_Command'] = 0
|
|
m.db.stat['proxy']['AT_Command_Blocked'] = 0
|
|
m.db.stat['proxy']['Modbus_Command'] = 0
|
|
m.inverter.forward_at_cmd_resp = False
|
|
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 == 1
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.control == 0x4510
|
|
assert str(m.seq) == '03:02'
|
|
assert m.data_len == 23
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
assert m.db.stat['proxy']['AT_Command'] == 0
|
|
assert m.db.stat['proxy']['AT_Command_Blocked'] == 1
|
|
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
|
assert m.inverter.forward_at_cmd_resp == False
|
|
assert Proxy.mqtt.key == ''
|
|
assert Proxy.mqtt.data == ""
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_msg_at_command_rsp1(my_loop, config_tsun_inv1, at_command_rsp_msg):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(at_command_rsp_msg)
|
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
|
m.db.stat['proxy']['Modbus_Command'] = 0
|
|
m.inverter.forward_at_cmd_resp = True
|
|
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 == 1
|
|
assert m.control == 0x1510
|
|
assert str(m.seq) == '03:03'
|
|
assert m.header_len==11
|
|
assert m.data_len==17
|
|
assert m.ifc.fwd_fifo.get()==at_command_rsp_msg
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
|
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_msg_at_command_rsp2(my_loop, config_tsun_inv1, at_command_rsp_msg):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(at_command_rsp_msg)
|
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
|
m.db.stat['proxy']['Modbus_Command'] = 0
|
|
m.inverter.forward_at_cmd_resp = False
|
|
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 == 1
|
|
assert m.control == 0x1510
|
|
assert str(m.seq) == '03:03'
|
|
assert m.header_len==11
|
|
assert m.data_len==17
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
|
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
|
assert Proxy.mqtt.key == 'tsun/inv1/at_resp'
|
|
assert Proxy.mqtt.data == "+ok"
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_msg_at_command_rsp3(my_loop, config_tsun_inv1, at_command_interim_rsp_msg):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(at_command_interim_rsp_msg)
|
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
|
m.db.stat['proxy']['Modbus_Command'] = 0
|
|
m.db.stat['proxy']['Invalid_Msg_Format'] = 0
|
|
m.db.stat['proxy']['Unknown_Msg'] = 0
|
|
m.inverter.forward_at_cmd_resp = True
|
|
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 == 1
|
|
assert m.control == 0x0510
|
|
assert str(m.seq) == '03:03'
|
|
assert m.header_len==11
|
|
assert m.data_len==37
|
|
assert m.ifc.fwd_fifo.get()==at_command_interim_rsp_msg
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
|
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
assert m.db.stat['proxy']['Unknown_Msg'] == 0
|
|
assert Proxy.mqtt.key == ''
|
|
assert Proxy.mqtt.data == ""
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_msg_modbus_req(my_loop, config_tsun_inv1, msg_modbus_cmd, msg_modbus_cmd_fwd):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(b'')
|
|
m.snr = get_sn_int()
|
|
m.sensor_list = 0x2b0
|
|
m.state = State.up
|
|
c = m.createClientStream(msg_modbus_cmd)
|
|
|
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
|
m.db.stat['proxy']['AT_Command'] = 0
|
|
m.db.stat['proxy']['Modbus_Command'] = 0
|
|
m.db.stat['proxy']['Invalid_Msg_Format'] = 0
|
|
c.read() # read complete msg, and dispatch msg
|
|
assert not c.header_valid # must be invalid, since msg was handled and buffer flushed
|
|
assert c.msg_count == 1
|
|
assert c.control == 0x4510
|
|
assert str(c.seq) == '03:02'
|
|
assert c.header_len==11
|
|
assert c.data_len==23
|
|
assert c.ifc.fwd_fifo.get()==b''
|
|
assert c.ifc.tx_fifo.get()==b''
|
|
assert m.sent_pdu == msg_modbus_cmd_fwd
|
|
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
|
assert m.db.stat['proxy']['AT_Command'] == 0
|
|
assert m.db.stat['proxy']['Modbus_Command'] == 1
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_msg_modbus_req_seq(my_loop, config_tsun_inv1, msg_modbus_cmd_seq):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(b'')
|
|
m.snr = get_sn_int()
|
|
m.sensor_list = 0x2b0
|
|
m.state = State.up
|
|
c = m.createClientStream(msg_modbus_cmd_seq)
|
|
|
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
|
m.db.stat['proxy']['AT_Command'] = 0
|
|
m.db.stat['proxy']['Modbus_Command'] = 0
|
|
m.db.stat['proxy']['Invalid_Msg_Format'] = 0
|
|
c.read() # read complete msg, and dispatch msg
|
|
assert not c.header_valid # must be invalid, since msg was handled and buffer flushed
|
|
assert c.msg_count == 1
|
|
assert c.control == 0x4510
|
|
assert str(c.seq) == '03:02'
|
|
assert c.header_len==11
|
|
assert c.data_len==23
|
|
assert c.ifc.fwd_fifo.get()==msg_modbus_cmd_seq
|
|
assert c.ifc.tx_fifo.get()==b''
|
|
assert m.sent_pdu == b''
|
|
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
|
assert m.db.stat['proxy']['AT_Command'] == 0
|
|
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_msg_modbus_req2(my_loop, config_tsun_inv1, msg_modbus_cmd_crc_err):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(b'')
|
|
m.snr = get_sn_int()
|
|
m.state = State.up
|
|
c = m.createClientStream(msg_modbus_cmd_crc_err)
|
|
|
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
|
m.db.stat['proxy']['AT_Command'] = 0
|
|
m.db.stat['proxy']['Modbus_Command'] = 0
|
|
m.db.stat['proxy']['Invalid_Msg_Format'] = 0
|
|
c.read() # read complete msg, and dispatch msg
|
|
assert not c.header_valid # must be invalid, since msg was handled and buffer flushed
|
|
assert c.msg_count == 1
|
|
assert c.control == 0x4510
|
|
assert str(c.seq) == '03:02'
|
|
assert c.header_len==11
|
|
assert c.data_len==23
|
|
assert c.ifc.fwd_fifo.get()==b''
|
|
assert c.ifc.tx_fifo.get()==b''
|
|
assert m.sent_pdu==b''
|
|
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
|
assert m.db.stat['proxy']['AT_Command'] == 0
|
|
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 1
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_msg_unknown_cmd_req(my_loop, config_tsun_inv1, msg_unknown_cmd):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(msg_unknown_cmd, (0,), False)
|
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
|
m.db.stat['proxy']['AT_Command'] = 0
|
|
m.db.stat['proxy']['Modbus_Command'] = 0
|
|
m.db.stat['proxy']['Invalid_Msg_Format'] = 0
|
|
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 == 1
|
|
assert m.control == 0x4510
|
|
assert str(m.seq) == '03:02'
|
|
assert m.header_len==11
|
|
assert m.data_len==23
|
|
assert m.ifc.fwd_fifo.get()==msg_unknown_cmd
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
|
assert m.db.stat['proxy']['AT_Command'] == 0
|
|
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_msg_modbus_rsp1(my_loop, config_tsun_inv1, msg_modbus_rsp):
|
|
'''Modbus response without a valid Modbus request must be dropped'''
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(msg_modbus_rsp)
|
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
|
m.db.stat['proxy']['Modbus_Command'] = 0
|
|
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 == 1
|
|
assert m.control == 0x1510
|
|
assert str(m.seq) == '03:03'
|
|
assert m.header_len==11
|
|
assert m.data_len==59
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
|
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_msg_modbus_rsp2(my_loop, config_tsun_inv1, msg_modbus_rsp):
|
|
'''Modbus response with a valid Modbus request must be forwarded'''
|
|
_ = config_tsun_inv1 # setup config structure
|
|
m = MemoryStream(msg_modbus_rsp)
|
|
|
|
m.mb.rsp_handler = m._SolarmanV5__forward_msg
|
|
m.mb.last_addr = 1
|
|
m.mb.last_fcode = 3
|
|
m.mb.last_len = 20
|
|
m.mb.last_reg = 0x3008
|
|
m.mb.req_pend = True
|
|
m.mb.err = 0
|
|
m.new_data['inverter'] = False
|
|
|
|
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.mb.err == 0
|
|
assert m.msg_count == 1
|
|
assert m.ifc.fwd_fifo.get()==msg_modbus_rsp
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.db.get_db_value(Register.VERSION) == 'V4.0.10'
|
|
assert m.new_data['inverter'] == True
|
|
m.new_data['inverter'] = False
|
|
|
|
m.mb.req_pend = True
|
|
m.append_msg(msg_modbus_rsp)
|
|
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.mb.err == 0
|
|
assert m.msg_count == 2
|
|
assert m.ifc.fwd_fifo.get()==msg_modbus_rsp
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.db.get_db_value(Register.VERSION) == 'V4.0.10'
|
|
assert m.new_data['inverter'] == False
|
|
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_msg_modbus_rsp3(my_loop, config_tsun_inv1, msg_modbus_rsp):
|
|
'''Modbus response with a valid Modbus request must be forwarded'''
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(msg_modbus_rsp)
|
|
|
|
m.mb.rsp_handler = m._SolarmanV5__forward_msg
|
|
m.mb.last_addr = 1
|
|
m.mb.last_fcode = 3
|
|
m.mb.last_len = 20
|
|
m.mb.last_reg = 0x3008
|
|
m.mb.req_pend = True
|
|
m.mb.err = 0
|
|
m.new_data['inverter'] = False
|
|
|
|
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.mb.err == 0
|
|
assert m.msg_count == 1
|
|
assert m.ifc.fwd_fifo.get()==msg_modbus_rsp
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.db.get_db_value(Register.VERSION) == 'V4.0.10'
|
|
assert m.new_data['inverter'] == True
|
|
m.new_data['inverter'] = False
|
|
|
|
m.append_msg(msg_modbus_rsp)
|
|
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.mb.err == 5
|
|
assert m.msg_count == 2
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.db.get_db_value(Register.VERSION) == 'V4.0.10'
|
|
assert m.new_data['inverter'] == False
|
|
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_msg_unknown_rsp(my_loop, config_tsun_inv1, msg_unknown_cmd_rsp):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(msg_unknown_cmd_rsp)
|
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
|
m.db.stat['proxy']['Modbus_Command'] = 0
|
|
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 == 1
|
|
assert m.control == 0x1510
|
|
assert str(m.seq) == '03:03'
|
|
assert m.header_len==11
|
|
assert m.data_len==59
|
|
assert m.ifc.fwd_fifo.get()==msg_unknown_cmd_rsp
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
|
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_msg_modbus_invalid(my_loop, config_tsun_inv1, msg_modbus_invalid):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(msg_modbus_invalid, (0,), False)
|
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
|
m.db.stat['proxy']['Modbus_Command'] = 0
|
|
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 == 1
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
|
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_msg_modbus_fragment(my_loop, config_tsun_inv1, msg_modbus_rsp):
|
|
_ = config_tsun_inv1
|
|
# receive more bytes than expected (7 bytes from the next msg)
|
|
m = MemoryStream(msg_modbus_rsp+b'\x00\x00\x00\x45\x10\x52\x31', (0,))
|
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
|
m.db.stat['proxy']['Modbus_Command'] = 0
|
|
m.mb.rsp_handler = m._SolarmanV5__forward_msg
|
|
m.mb.last_addr = 1
|
|
m.mb.last_fcode = 3
|
|
m.mb.last_len = 20
|
|
m.mb.last_reg = 0x3008
|
|
m.mb.req_pend = True
|
|
m.mb.err = 0
|
|
|
|
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 == 1
|
|
assert m.ifc.fwd_fifo.get()==msg_modbus_rsp
|
|
assert m.ifc.tx_fifo.get()== b''
|
|
assert m.mb.err == 0
|
|
assert m.modbus_elms == 20-1 # register 0x300d is unknown, so one value can't be mapped
|
|
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
|
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_modbus_polling(my_loop, config_tsun_inv1, heartbeat_ind_msg, heartbeat_rsp_msg):
|
|
_ = config_tsun_inv1
|
|
assert asyncio.get_running_loop()
|
|
m = MemoryStream(heartbeat_ind_msg, (0,))
|
|
assert asyncio.get_running_loop() == m.mb_timer.loop
|
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
|
assert m.mb_timer.tim == None
|
|
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 == 1
|
|
assert m.header_len==11
|
|
assert m.snr == 2070233889
|
|
assert m.control == 0x4710
|
|
assert str(m.seq) == '84:11' # value after sending response
|
|
assert m.data_len == 0x01
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==heartbeat_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==heartbeat_ind_msg
|
|
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
|
|
assert m.state == State.up
|
|
assert isclose(m.mb_timeout, 0.5)
|
|
assert next(m.mb_timer.exp_count) == 0
|
|
|
|
await asyncio.sleep(0.5)
|
|
assert m.sent_pdu==bytearray(b'\xa5\x17\x00\x10E\x12\x84!Ce{\x02\xb0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x03\x30\x00\x000J\xde\x86\x15')
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
|
|
await asyncio.sleep(0.5)
|
|
assert m.sent_pdu==bytearray(b'\xa5\x17\x00\x10E\x13\x84!Ce{\x02\xb0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x03\x30\x00\x000J\xde\x87\x15')
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
m.state = State.closed
|
|
m.sent_pdu = bytearray()
|
|
await asyncio.sleep(0.5)
|
|
assert m.sent_pdu==bytearray(b'')
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert next(m.mb_timer.exp_count) == 4
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_modbus_scaning(config_tsun_scan, heartbeat_ind_msg, heartbeat_rsp_msg, msg_modbus_rsp, msg_modbus_rsp_inv_id2):
|
|
_ = config_tsun_scan
|
|
assert asyncio.get_running_loop()
|
|
|
|
m = MemoryStream(heartbeat_ind_msg, (0x15,0x56,0))
|
|
m.append_msg(msg_modbus_rsp)
|
|
m.append_msg(msg_modbus_rsp_inv_id2)
|
|
assert m.mb_scan == False
|
|
assert asyncio.get_running_loop() == m.mb_timer.loop
|
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
|
assert m.mb_timer.tim == None
|
|
m.read() # read complete msg, and dispatch msg
|
|
assert m.mb_scan == True
|
|
assert m.mb_start_reg == 0xff80
|
|
assert m.mb_step == 0x40
|
|
assert m.mb_bytes == 0x14
|
|
assert asyncio.get_running_loop() == m.mb_timer.loop
|
|
|
|
assert not m.header_valid # must be invalid, since msg was handled and buffer flushed
|
|
assert m.msg_count == 1
|
|
assert m.snr == 2070233889
|
|
assert m.control == 0x4710
|
|
|
|
assert m.msg_recvd[0]['control']==0x4710
|
|
assert m.msg_recvd[0]['seq']=='84:11'
|
|
assert m.msg_recvd[0]['data_len']==0x1
|
|
|
|
assert m.ifc.tx_fifo.get()==heartbeat_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==heartbeat_ind_msg
|
|
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
|
|
|
m.ifc.tx_clear() # clear send buffer for next test
|
|
assert isclose(m.mb_timeout, 0.5)
|
|
assert next(m.mb_timer.exp_count) == 0
|
|
|
|
await asyncio.sleep(0.5)
|
|
assert m.sent_pdu==b'\xa5\x17\x00\x10E\x12\x84!Ce{\x02\xb0\x02\x00\x00\x00\x00\x00\x00' \
|
|
b'\x00\x00\x00\x00\x00\x00\x01\x03\xff\xc0\x00\x14\x75\xed\x33\x15'
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
|
|
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
|
|
assert m.msg_recvd[1]['control']==0x1510
|
|
assert m.msg_recvd[1]['seq']=='03:03'
|
|
assert m.msg_recvd[1]['data_len']==0x3b
|
|
assert m.mb.last_addr == 1
|
|
assert m.mb.last_fcode == 3
|
|
assert m.mb.last_reg == 0xffc0 # mb_start_reg + mb_step
|
|
assert m.mb.last_len == 20
|
|
assert m.mb.err == 0
|
|
|
|
await asyncio.sleep(0.5)
|
|
assert m.sent_pdu==b'\xa5\x17\x00\x10E\x04\x03!Ce{\x02\xb0\x02\x00\x00\x00\x00\x00\x00' \
|
|
b'\x00\x00\x00\x00\x00\x00\x02\x03\x00\x00\x00\x14\x45\xf6\xbf\x15'
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
|
|
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 == 3
|
|
assert m.msg_recvd[2]['control']==0x1510
|
|
assert m.msg_recvd[2]['seq']=='03:03'
|
|
assert m.msg_recvd[2]['data_len']==0x3b
|
|
assert m.mb.last_addr == 2
|
|
assert m.mb.last_fcode == 3
|
|
assert m.mb.last_reg == 0x0000 # mb_start_reg + mb_step
|
|
assert m.mb.last_len == 20
|
|
assert m.mb.err == 0
|
|
|
|
assert next(m.mb_timer.exp_count) == 3
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_start_client_mode(my_loop, config_tsun_inv1, str_test_ip):
|
|
_ = config_tsun_inv1
|
|
assert asyncio.get_running_loop()
|
|
m = MemoryStream(b'')
|
|
assert m.state == State.init
|
|
assert m.no_forwarding == False
|
|
assert m.mb_timer.tim == None
|
|
assert asyncio.get_running_loop() == m.mb_timer.loop
|
|
await m.send_start_cmd(get_sn_int(), str_test_ip, False, m.mb_first_timeout)
|
|
assert m.sent_pdu==bytearray(b'\xa5\x17\x00\x10E\x01\x00!Ce{\x02\xb0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x030\x00\x000J\xde\xf1\x15')
|
|
assert m.db.get_db_value(Register.IP_ADDRESS) == str_test_ip
|
|
assert isclose(m.db.get_db_value(Register.POLLING_INTERVAL), 0.5)
|
|
assert m.db.get_db_value(Register.HEARTBEAT_INTERVAL) == 120
|
|
|
|
assert m.state == State.up
|
|
assert m.no_forwarding == True
|
|
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert isclose(m.mb_timeout, 0.5)
|
|
assert next(m.mb_timer.exp_count) == 0
|
|
|
|
await asyncio.sleep(0.5)
|
|
assert m.sent_pdu==bytearray(b'\xa5\x17\x00\x10E\x02\x00!Ce{\x02\xb0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x030\x00\x000J\xde\xf2\x15')
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
|
|
await asyncio.sleep(0.5)
|
|
assert m.sent_pdu==bytearray(b'\xa5\x17\x00\x10E\x03\x00!Ce{\x02\xb0\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x030\x00\x000J\xde\xf3\x15')
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert next(m.mb_timer.exp_count) == 3
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_start_client_mode_scan(config_tsun_scan_dcu, str_test_ip, dcu_modbus_rsp):
|
|
_ = config_tsun_scan_dcu
|
|
assert asyncio.get_running_loop()
|
|
m = MemoryStream(dcu_modbus_rsp, (131,0,))
|
|
m.append_msg(dcu_modbus_rsp)
|
|
assert m.state == State.init
|
|
assert m.no_forwarding == False
|
|
assert m.mb_timer.tim == None
|
|
assert asyncio.get_running_loop() == m.mb_timer.loop
|
|
await m.send_start_cmd(get_dcu_sn_int(), str_test_ip, False, m.mb_first_timeout)
|
|
assert m.mb_start_reg == 0x0000
|
|
assert m.mb_step == 0x100
|
|
assert m.mb_bytes == 0x2d
|
|
|
|
assert m.sent_pdu==bytearray(b'\xa5\x17\x00\x10E\x01\x00 Ce{\x02&0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00-\x85\xd7\x95\x15')
|
|
assert m.mb_scan == True
|
|
m.mb_step = 0
|
|
assert m.db.get_db_value(Register.IP_ADDRESS) == str_test_ip
|
|
assert isclose(m.db.get_db_value(Register.POLLING_INTERVAL), 0.5)
|
|
assert m.db.get_db_value(Register.HEARTBEAT_INTERVAL) == 120
|
|
|
|
assert m.state == State.up
|
|
assert m.no_forwarding == True
|
|
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert isclose(m.mb_timeout, 0.5)
|
|
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
|
|
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 == 1
|
|
assert m.msg_recvd[0]['control']==0x1510
|
|
assert m.msg_recvd[0]['seq']=='03:03'
|
|
assert m.msg_recvd[0]['data_len']==109
|
|
assert m.mb.last_addr == 1
|
|
assert m.mb.last_fcode == 3
|
|
assert m.mb.last_reg == 0x0000 # mb_start_reg + mb_step
|
|
assert m.mb.last_len == 45
|
|
assert m.mb.err == 0
|
|
|
|
assert isclose(m.db.get_db_value(Register.BATT_PWR, None), -136.6225)
|
|
assert isclose(m.db.get_db_value(Register.BATT_OUT_PWR, None), 131.604)
|
|
assert isclose(m.db.get_db_value(Register.BATT_PV_PWR, None), 0.0)
|
|
assert m.new_data['batterie'] == True
|
|
m.new_data['batterie'] = False
|
|
|
|
await asyncio.sleep(0.5)
|
|
assert m.sent_pdu==bytearray(b'\xa5\x17\x00\x10E\x04\x03 Ce{\x02&0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00-\x85\xd7\x9b\x15')
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
|
|
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
|
|
assert m.msg_recvd[1]['control']==0x1510
|
|
assert m.msg_recvd[1]['seq']=='03:03'
|
|
assert m.msg_recvd[1]['data_len']==109
|
|
assert m.mb.last_addr == 1
|
|
assert m.mb.last_fcode == 3
|
|
assert m.mb.last_reg == 0x0000 # mb_start_reg + mb_step
|
|
assert m.mb.last_len == 45
|
|
assert m.mb.err == 0
|
|
|
|
assert isclose(m.db.get_db_value(Register.BATT_PWR, None), -136.6225)
|
|
assert isclose(m.db.get_db_value(Register.BATT_OUT_PWR, None), 131.604)
|
|
assert isclose(m.db.get_db_value(Register.BATT_PV_PWR, None), 0.0)
|
|
assert m.new_data['batterie'] == False
|
|
|
|
assert next(m.mb_timer.exp_count) == 1
|
|
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_timeout(my_loop, config_tsun_inv1):
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(b'')
|
|
assert m.state == State.init
|
|
assert SolarmanV5.MAX_START_TIME == m._timeout()
|
|
m.state = State.up
|
|
m.modbus_polling = True
|
|
assert SolarmanV5.MAX_INV_IDLE_TIME == m._timeout()
|
|
m.modbus_polling = False
|
|
assert SolarmanV5.MAX_DEF_IDLE_TIME == m._timeout()
|
|
m.state = State.closed
|
|
m.close()
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_fnc_dispatch(my_loop, config_tsun_inv1):
|
|
def msg():
|
|
return
|
|
|
|
_ = config_tsun_inv1
|
|
m = MemoryStream(b'')
|
|
m.switch[1] = msg
|
|
m.switch[2] = "msg"
|
|
|
|
_obj, _str = m.get_fnc_handler(1)
|
|
assert _obj == msg
|
|
assert _str == "'msg'"
|
|
|
|
_obj, _str = m.get_fnc_handler(2)
|
|
assert _obj == m.msg_unknown
|
|
assert _str == "'msg'"
|
|
|
|
_obj, _str = m.get_fnc_handler(3)
|
|
assert _obj == m.msg_unknown
|
|
assert _str == "'msg_unknown'"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_timestamp(my_loop, config_tsun_inv1):
|
|
m = MemoryStream(b'')
|
|
ts = m._timestamp()
|
|
ts_emu = m._emu_timestamp()
|
|
assert ts == ts_emu + 24*60*60
|
|
|
|
class MemoryStream2(MemoryStream):
|
|
def __init__(self, inverter, addr, ifc,
|
|
server_side: bool, client_mode: bool):
|
|
super().__init__(b'', inverter=inverter)
|
|
|
|
|
|
class InverterTest(InverterBase):
|
|
def __init__(self, reader: StreamReader, writer: StreamWriter,
|
|
client_mode: bool = False):
|
|
remote_prot = None
|
|
super().__init__(reader, writer, 'solarman',
|
|
MemoryStream2, client_mode, remote_prot)
|
|
|
|
def forward(self, src, dst) -> 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(my_loop, 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(my_loop, 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"
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_dcu_cmd(my_loop, config_tsun_allow_all, dcu_dev_ind_msg, dcu_dev_rsp_msg, dcu_data_ind_msg, dcu_data_rsp_msg, dcu_command_ind_msg, dcu_command_rsp_msg):
|
|
'''test dcu_power command fpr a DCU device with sensor 0x3026'''
|
|
_ = config_tsun_allow_all
|
|
m = MemoryStream(dcu_dev_ind_msg, (0,), True)
|
|
m.read() # read device ind
|
|
assert m.control == 0x4110
|
|
assert str(m.seq) == '01:92'
|
|
assert m.ifc.tx_fifo.get()==dcu_dev_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==dcu_dev_ind_msg
|
|
|
|
m.send_dcu_cmd(b'\x01\x01\x06\x01\x00\x01\x03\xe8')
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.sent_pdu == b''
|
|
assert str(m.seq) == '01:92'
|
|
assert Proxy.mqtt.key == ''
|
|
assert Proxy.mqtt.data == ""
|
|
|
|
m.append_msg(dcu_data_ind_msg)
|
|
m.read() # read inverter ind
|
|
assert m.control == 0x4210
|
|
assert str(m.seq) == '02:93'
|
|
assert m.ifc.tx_fifo.get()==dcu_data_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==dcu_data_ind_msg
|
|
|
|
m.send_dcu_cmd(b'\x01\x01\x06\x01\x00\x01\x03\xe8')
|
|
assert m.ifc.fwd_fifo.get() == b''
|
|
assert m.ifc.tx_fifo.get()== b''
|
|
assert m.sent_pdu == dcu_command_ind_msg
|
|
m.sent_pdu = bytearray()
|
|
|
|
assert str(m.seq) == '02:94'
|
|
assert Proxy.mqtt.key == ''
|
|
assert Proxy.mqtt.data == ""
|
|
|
|
m.append_msg(dcu_command_rsp_msg)
|
|
m.read() # read at resp
|
|
assert m.control == 0x1510
|
|
assert str(m.seq) == '03:94'
|
|
assert m.ifc.rx_get()==b''
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert Proxy.mqtt.key == 'tsun/dcu_resp'
|
|
assert Proxy.mqtt.data == "+ok"
|
|
Proxy.mqtt.clear() # clear last test result
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_dcu_cmd_not_supported(my_loop, config_tsun_allow_all, device_ind_msg, device_rsp_msg, inverter_ind_msg, inverter_rsp_msg):
|
|
'''test that an inverter don't accept the dcu_power command'''
|
|
_ = config_tsun_allow_all
|
|
m = MemoryStream(device_ind_msg, (0,), True)
|
|
m.read() # read device ind
|
|
assert m.control == 0x4110
|
|
assert str(m.seq) == '01:01'
|
|
assert m.ifc.tx_fifo.get()==device_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==device_ind_msg
|
|
|
|
m.send_dcu_cmd(b'\x01\x01\x06\x01\x00\x01\x03\xe8')
|
|
assert m.ifc.tx_fifo.get()==b''
|
|
assert m.ifc.fwd_fifo.get()==b''
|
|
assert m.sent_pdu == b''
|
|
assert str(m.seq) == '01:01'
|
|
assert Proxy.mqtt.key == ''
|
|
assert Proxy.mqtt.data == ""
|
|
|
|
m.append_msg(inverter_ind_msg)
|
|
m.read() # read inverter ind
|
|
assert m.control == 0x4210
|
|
assert str(m.seq) == '02:02'
|
|
assert m.ifc.tx_fifo.get()==inverter_rsp_msg
|
|
assert m.ifc.fwd_fifo.get()==inverter_ind_msg
|
|
|
|
m.send_dcu_cmd(b'\x01\x01\x06\x01\x00\x01\x03\xe8')
|
|
assert m.ifc.fwd_fifo.get() == b''
|
|
assert m.ifc.tx_fifo.get()== b''
|
|
assert m.sent_pdu == b''
|
|
Proxy.mqtt.clear() # clear last test result
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_proxy_dcu_cmd(my_loop, config_tsun_dcu1, patch_open_connection, dcu_command_ind_msg, dcu_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']['DCU_Command'] = 0
|
|
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_dcu_cmd_resp = False
|
|
r.append_msg(dcu_command_ind_msg)
|
|
r.read() # read complete msg, and dispatch msg
|
|
assert inverter.forward_dcu_cmd_resp
|
|
inverter.forward(r,l)
|
|
|
|
assert l.ifc.tx_fifo.get()==dcu_command_ind_msg
|
|
|
|
assert l.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
|
assert l.db.stat['proxy']['DCU_Command'] == 1
|
|
assert l.db.stat['proxy']['AT_Command'] == 0
|
|
assert l.db.stat['proxy']['AT_Command_Blocked'] == 0
|
|
assert l.db.stat['proxy']['Modbus_Command'] == 0
|
|
assert 2 == l.db.get_db_value(Register.NO_INPUTS, 0)
|
|
|
|
l.append_msg(dcu_command_rsp_msg)
|
|
l.read() # read at resp
|
|
assert l.ifc.fwd_fifo.peek()==dcu_command_rsp_msg
|
|
inverter.forward(l,r)
|
|
assert r.ifc.tx_fifo.get()==dcu_command_rsp_msg
|
|
|
|
assert Proxy.mqtt.key == ''
|
|
assert Proxy.mqtt.data == ""
|
|
|