Merge branch 'dev-0.9.0' of https://github.com/s-allius/tsun-gen3-proxy into s-allius/issue56
This commit is contained in:
@@ -31,10 +31,12 @@ def test_default_config():
|
||||
assert True
|
||||
except:
|
||||
assert False
|
||||
assert validated == {'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'}, 'inverters': {'allow_all': True, 'R170000000000001': {'node_id': '', 'monitor_sn': 0, 'suggested_area': ''}, 'Y170000000000001': {'monitor_sn': 2000000000, 'node_id': '', 'suggested_area': ''}}}
|
||||
assert validated == {'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}}, 'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'}, 'inverters': {'allow_all': True, 'R170000000000001': {'node_id': '', 'monitor_sn': 0, 'suggested_area': ''}, 'Y170000000000001': {'monitor_sn': 2000000000, 'node_id': '', 'suggested_area': ''}}}
|
||||
|
||||
def test_full_config():
|
||||
cnf = {'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005},
|
||||
'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []},
|
||||
'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}},
|
||||
'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000},
|
||||
'mqtt': {'host': 'mqtt', 'port': 1883, 'user': '', 'passwd': ''},
|
||||
'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'},
|
||||
@@ -46,10 +48,12 @@ def test_full_config():
|
||||
assert True
|
||||
except:
|
||||
assert False
|
||||
assert validated == {'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'}, 'inverters': {'allow_all': True, 'R170000000000001': {'node_id': '', 'monitor_sn': 0, 'pv1': {'manufacturer': 'man1','type': 'type1'},'pv2': {'manufacturer': 'man2','type': 'type2'},'pv3': {'manufacturer': 'man3','type': 'type3'}, 'suggested_area': ''}, 'Y170000000000001': {'monitor_sn': 2000000000, 'node_id': '', 'suggested_area': ''}}}
|
||||
assert validated == {'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}}, 'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'}, 'inverters': {'allow_all': True, 'R170000000000001': {'node_id': '', 'monitor_sn': 0, 'pv1': {'manufacturer': 'man1','type': 'type1'},'pv2': {'manufacturer': 'man2','type': 'type2'},'pv3': {'manufacturer': 'man3','type': 'type3'}, 'suggested_area': ''}, 'Y170000000000001': {'monitor_sn': 2000000000, 'node_id': '', 'suggested_area': ''}}}
|
||||
|
||||
def test_mininum_config():
|
||||
cnf = {'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005},
|
||||
'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+']},
|
||||
'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE']}}},
|
||||
'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000},
|
||||
'mqtt': {'host': 'mqtt', 'port': 1883, 'user': '', 'passwd': ''},
|
||||
'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'},
|
||||
@@ -62,7 +66,7 @@ def test_mininum_config():
|
||||
assert True
|
||||
except:
|
||||
assert False
|
||||
assert validated == {'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'}, 'inverters': {'allow_all': True, 'R170000000000001': {'node_id': '', 'monitor_sn': 0, 'suggested_area': ''}}}
|
||||
assert validated == {'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}}, 'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'}, 'inverters': {'allow_all': True, 'R170000000000001': {'node_id': '', 'monitor_sn': 0, 'suggested_area': ''}}}
|
||||
|
||||
def test_read_empty():
|
||||
cnf = {}
|
||||
@@ -70,7 +74,7 @@ def test_read_empty():
|
||||
err = TstConfig.read('app/config/')
|
||||
assert err == None
|
||||
cnf = TstConfig.get()
|
||||
assert cnf == {'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'monitor_sn': 0, 'node_id': ''}, 'Y170000000000001': {'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': ''}}}
|
||||
assert cnf == {'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}}, 'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'monitor_sn': 0, 'node_id': ''}, 'Y170000000000001': {'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': ''}}}
|
||||
|
||||
defcnf = TstConfig.def_config.get('solarman')
|
||||
assert defcnf == {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000}
|
||||
@@ -92,7 +96,7 @@ def test_read_cnf1():
|
||||
err = TstConfig.read('app/config/')
|
||||
assert err == None
|
||||
cnf = TstConfig.get()
|
||||
assert cnf == {'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': False, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'monitor_sn': 0, 'node_id': ''}, 'Y170000000000001': {'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': ''}}}
|
||||
assert cnf == {'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}}, 'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': False, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'monitor_sn': 0, 'node_id': ''}, 'Y170000000000001': {'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': ''}}}
|
||||
cnf = TstConfig.get('solarman')
|
||||
assert cnf == {'enabled': False, 'host': 'iot.talent-monitoring.com', 'port': 10000}
|
||||
defcnf = TstConfig.def_config.get('solarman')
|
||||
@@ -105,7 +109,7 @@ def test_read_cnf2():
|
||||
err = TstConfig.read('app/config/')
|
||||
assert err == None
|
||||
cnf = TstConfig.get()
|
||||
assert cnf == {'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'monitor_sn': 0, 'node_id': ''}, 'Y170000000000001': {'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': ''}}}
|
||||
assert cnf == {'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}}, 'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 10000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'monitor_sn': 0, 'node_id': ''}, 'Y170000000000001': {'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': ''}}}
|
||||
assert True == TstConfig.is_default('solarman')
|
||||
|
||||
def test_read_cnf3():
|
||||
@@ -122,7 +126,7 @@ def test_read_cnf4():
|
||||
err = TstConfig.read('app/config/')
|
||||
assert err == None
|
||||
cnf = TstConfig.get()
|
||||
assert cnf == {'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 5000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'monitor_sn': 0, 'node_id': ''}, 'Y170000000000001': {'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': ''}}}
|
||||
assert cnf == {'gen3plus': {'at_acl': {'mqtt': {'allow': ['AT+'], 'block': []}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE'], 'block': []}}}, 'tsun': {'enabled': True, 'host': 'logger.talent-monitoring.com', 'port': 5005}, 'solarman': {'enabled': True, 'host': 'iot.talent-monitoring.com', 'port': 5000}, 'mqtt': {'host': 'mqtt', 'port': 1883, 'user': None, 'passwd': None}, 'ha': {'auto_conf_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant', 'entity_prefix': 'tsun', 'proxy_node_id': 'proxy', 'proxy_unique_id': 'P170000000000001'}, 'inverters': {'allow_all': True, 'R170000000000001': {'suggested_area': '', 'monitor_sn': 0, 'node_id': ''}, 'Y170000000000001': {'monitor_sn': 2000000000, 'suggested_area': '', 'node_id': ''}}}
|
||||
assert False == TstConfig.is_default('solarman')
|
||||
|
||||
def test_read_cnf5():
|
||||
|
||||
@@ -17,13 +17,13 @@ def test_statistic_counter():
|
||||
assert val == None or val == 0
|
||||
|
||||
i.static_init() # initialize counter
|
||||
assert json.dumps(i.stat) == json.dumps({"proxy": {"Inverter_Cnt": 0, "Unknown_SNR": 0, "Unknown_Msg": 0, "Invalid_Data_Type": 0, "Internal_Error": 0,"Unknown_Ctrl": 0, "OTA_Start_Msg": 0, "SW_Exception": 0, "Invalid_Msg_Format": 0, "AT_Command": 0}})
|
||||
assert json.dumps(i.stat) == json.dumps({"proxy": {"Inverter_Cnt": 0, "Unknown_SNR": 0, "Unknown_Msg": 0, "Invalid_Data_Type": 0, "Internal_Error": 0,"Unknown_Ctrl": 0, "OTA_Start_Msg": 0, "SW_Exception": 0, "Invalid_Msg_Format": 0, "AT_Command": 0, "AT_Command_Blocked": 0, "Modbus_Command": 0}})
|
||||
|
||||
val = i.dev_value(Register.INVERTER_CNT) # valid and initiliazed addr
|
||||
assert val == 0
|
||||
|
||||
i.inc_counter('Inverter_Cnt')
|
||||
assert json.dumps(i.stat) == json.dumps({"proxy": {"Inverter_Cnt": 1, "Unknown_SNR": 0, "Unknown_Msg": 0, "Invalid_Data_Type": 0, "Internal_Error": 0,"Unknown_Ctrl": 0, "OTA_Start_Msg": 0, "SW_Exception": 0, "Invalid_Msg_Format": 0, "AT_Command": 0}})
|
||||
assert json.dumps(i.stat) == json.dumps({"proxy": {"Inverter_Cnt": 1, "Unknown_SNR": 0, "Unknown_Msg": 0, "Invalid_Data_Type": 0, "Internal_Error": 0,"Unknown_Ctrl": 0, "OTA_Start_Msg": 0, "SW_Exception": 0, "Invalid_Msg_Format": 0, "AT_Command": 0, "AT_Command_Blocked": 0, "Modbus_Command": 0}})
|
||||
val = i.dev_value(Register.INVERTER_CNT)
|
||||
assert val == 1
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ def test_parse_4110(DeviceData: bytes):
|
||||
pass
|
||||
|
||||
assert json.dumps(i.db) == json.dumps({
|
||||
'controller': {"Data_Up_Interval": 300, "Collect_Interval": 1, "Heartbeat_Interval": 120, "Signal_Strength": 100, "IP_Adress": "192.168.80.49"},
|
||||
'controller': {"Data_Up_Interval": 300, "Collect_Interval": 1, "Heartbeat_Interval": 120, "Signal_Strength": 100, "IP_Address": "192.168.80.49"},
|
||||
'collector': {"Chip_Model": "LSW5BLE_17_02B0_1.05", "Collector_Fw_Version": "V1.1.00.0B"},
|
||||
})
|
||||
|
||||
@@ -83,7 +83,7 @@ def test_parse_4210(InverterData: bytes):
|
||||
|
||||
assert json.dumps(i.db) == json.dumps({
|
||||
"controller": {"Power_On_Time": 2051},
|
||||
"inverter": {"Serial_Number": "Y17E00000000000E", "Version": "v4.0.10", "Rated_Power": 600, "Max_Designed_Power": 2000, "No_Inputs": 4},
|
||||
"inverter": {"Serial_Number": "Y17E00000000000E", "Version": "V4.0.10", "Rated_Power": 600, "Max_Designed_Power": 2000, "No_Inputs": 4},
|
||||
"env": {"Inverter_Status": 1, "Inverter_Temp": 14},
|
||||
"grid": {"Voltage": 224.8, "Current": 0.73, "Frequency": 50.05, "Output_Power": 165.8},
|
||||
"input": {"pv1": {"Voltage": 35.3, "Current": 1.68, "Power": 59.6, "Daily_Generation": 0.04, "Total_Generation": 30.76},
|
||||
|
||||
380
app/tests/test_modbus.py
Normal file
380
app/tests/test_modbus.py
Normal file
@@ -0,0 +1,380 @@
|
||||
# test_with_pytest.py
|
||||
import pytest
|
||||
import asyncio
|
||||
from app.src.modbus import Modbus
|
||||
from app.src.infos import Infos, Register
|
||||
|
||||
pytest_plugins = ('pytest_asyncio',)
|
||||
pytestmark = pytest.mark.asyncio(scope="module")
|
||||
|
||||
class TestHelper(Modbus):
|
||||
def __init__(self):
|
||||
super().__init__(self.send_cb)
|
||||
self.db = Infos()
|
||||
self.pdu = None
|
||||
self.send_calls = 0
|
||||
self.recv_responses = 0
|
||||
def send_cb(self, pdu: bytearray, log_lvl: int, state: str):
|
||||
self.pdu = pdu
|
||||
self.send_calls += 1
|
||||
def resp_handler(self):
|
||||
self.recv_responses += 1
|
||||
|
||||
def test_modbus_crc():
|
||||
'''Check CRC-16 calculation'''
|
||||
mb = Modbus(None)
|
||||
assert 0x0b02 == mb._Modbus__calc_crc(b'\x01\x06\x20\x08\x00\x04')
|
||||
assert 0 == mb._Modbus__calc_crc(b'\x01\x06\x20\x08\x00\x04\x02\x0b')
|
||||
assert mb._Modbus__check_crc(b'\x01\x06\x20\x08\x00\x04\x02\x0b')
|
||||
|
||||
assert 0xc803 == mb._Modbus__calc_crc(b'\x01\x06\x20\x08\x00\x00')
|
||||
assert 0 == mb._Modbus__calc_crc(b'\x01\x06\x20\x08\x00\x00\x03\xc8')
|
||||
assert mb._Modbus__check_crc(b'\x01\x06\x20\x08\x00\x00\x03\xc8')
|
||||
|
||||
assert 0x5c75 == mb._Modbus__calc_crc(b'\x01\x03\x08\x01\x2c\x00\x2c\x02\x2c\x2c\x46')
|
||||
|
||||
def test_build_modbus_pdu():
|
||||
'''Check building and sending a MODBUS RTU'''
|
||||
mb = TestHelper()
|
||||
mb.build_msg(1,6,0x2000,0x12)
|
||||
assert mb.pdu == b'\x01\x06\x20\x00\x00\x12\x02\x07'
|
||||
assert mb._Modbus__check_crc(mb.pdu)
|
||||
assert mb.last_addr == 1
|
||||
assert mb.last_fcode == 6
|
||||
assert mb.last_reg == 0x2000
|
||||
assert mb.last_len == 18
|
||||
assert mb.err == 0
|
||||
|
||||
def test_recv_req():
|
||||
'''Receive a valid request, which must transmitted'''
|
||||
mb = TestHelper()
|
||||
assert mb.recv_req(b'\x01\x06\x20\x00\x00\x12\x02\x07')
|
||||
assert mb.last_fcode == 6
|
||||
assert mb.last_reg == 0x2000
|
||||
assert mb.last_len == 0x12
|
||||
assert mb.err == 0
|
||||
|
||||
def test_recv_req_crc_err():
|
||||
'''Receive a request with invalid CRC, which must be dropped'''
|
||||
mb = TestHelper()
|
||||
assert not mb.recv_req(b'\x01\x06\x20\x00\x00\x12\x02\x08')
|
||||
assert mb.pdu == None
|
||||
assert mb.last_fcode == 0
|
||||
assert mb.last_reg == 0
|
||||
assert mb.last_len == 0
|
||||
assert mb.err == 1
|
||||
|
||||
def test_recv_resp_crc_err():
|
||||
'''Receive a response with invalid CRC, which must be dropped'''
|
||||
mb = TestHelper()
|
||||
# simulate a transmitted request
|
||||
mb.req_pend = True
|
||||
mb.last_addr = 1
|
||||
mb.last_fcode = 3
|
||||
mb.last_reg == 0x300e
|
||||
mb.last_len == 2
|
||||
# check matching response, but with CRC error
|
||||
call = 0
|
||||
for key, update, val in mb.recv_resp(mb.db, b'\x01\x03\x04\x01\x2c\x00\x46\xbb\xf3', 'test'):
|
||||
call += 1
|
||||
assert mb.err == 1
|
||||
assert 0 == call
|
||||
assert mb.req_pend == True
|
||||
# cleanup queue
|
||||
mb._Modbus__stop_timer()
|
||||
assert not mb.req_pend
|
||||
|
||||
def test_recv_resp_invalid_addr():
|
||||
'''Receive a response with wrong server addr, which must be dropped'''
|
||||
mb = TestHelper()
|
||||
mb.req_pend = True
|
||||
# simulate a transmitted request
|
||||
mb.last_addr = 1
|
||||
mb.last_fcode = 3
|
||||
mb.last_reg == 0x300e
|
||||
mb.last_len == 2
|
||||
|
||||
# check not matching response, with wrong server addr
|
||||
call = 0
|
||||
for key, update in mb.recv_resp(mb.db, b'\x02\x03\x04\x01\x2c\x00\x46\x88\xf4', 'test'):
|
||||
call += 1
|
||||
assert mb.err == 2
|
||||
assert 0 == call
|
||||
assert mb.req_pend == True
|
||||
assert mb.que.qsize() == 0
|
||||
|
||||
# cleanup queue
|
||||
mb._Modbus__stop_timer()
|
||||
assert not mb.req_pend
|
||||
|
||||
def test_recv_recv_fcode():
|
||||
'''Receive a response with wrong function code, which must be dropped'''
|
||||
mb = TestHelper()
|
||||
mb.build_msg(1,4,0x300e,2)
|
||||
assert mb.que.qsize() == 0
|
||||
assert mb.req_pend
|
||||
|
||||
# check not matching response, with wrong function code
|
||||
call = 0
|
||||
for key, update, val in mb.recv_resp(mb.db, b'\x01\x03\x04\x01\x2c\x00\x46\xbb\xf4', 'test'):
|
||||
call += 1
|
||||
|
||||
assert mb.err == 3
|
||||
assert 0 == call
|
||||
assert mb.req_pend == True
|
||||
assert mb.que.qsize() == 0
|
||||
|
||||
# cleanup queue
|
||||
mb._Modbus__stop_timer()
|
||||
assert not mb.req_pend
|
||||
|
||||
def test_recv_resp_len():
|
||||
'''Receive a response with wrong data length, which must be dropped'''
|
||||
mb = TestHelper()
|
||||
mb.build_msg(1,3,0x300e,3)
|
||||
assert mb.que.qsize() == 0
|
||||
assert mb.req_pend
|
||||
assert mb.last_len == 3
|
||||
|
||||
# check not matching response, with wrong data length
|
||||
call = 0
|
||||
for key, update, _ in mb.recv_resp(mb.db, b'\x01\x03\x04\x01\x2c\x00\x46\xbb\xf4', 'test'):
|
||||
call += 1
|
||||
|
||||
assert mb.err == 4
|
||||
assert 0 == call
|
||||
assert mb.req_pend == True
|
||||
assert mb.que.qsize() == 0
|
||||
|
||||
# cleanup queue
|
||||
mb._Modbus__stop_timer()
|
||||
assert not mb.req_pend
|
||||
|
||||
def test_recv_unexpect_resp():
|
||||
'''Receive a response when we havb't sent a request'''
|
||||
mb = TestHelper()
|
||||
assert not mb.req_pend
|
||||
|
||||
# check unexpected response, which must be dropped
|
||||
call = 0
|
||||
for key, update, val in mb.recv_resp(mb.db, b'\x01\x03\x04\x01\x2c\x00\x46\xbb\xf4', 'test'):
|
||||
call += 1
|
||||
|
||||
assert mb.err == 5
|
||||
assert 0 == call
|
||||
assert mb.req_pend == False
|
||||
assert mb.que.qsize() == 0
|
||||
|
||||
def test_parse_resp():
|
||||
'''Receive matching response and parse the values'''
|
||||
mb = TestHelper()
|
||||
mb.build_msg(1,3,0x3007,6)
|
||||
assert mb.que.qsize() == 0
|
||||
assert mb.req_pend
|
||||
|
||||
call = 0
|
||||
exp_result = ['V0.0.212', 4.4, 0.7, 0.7, 30]
|
||||
for key, update, val in mb.recv_resp(mb.db, b'\x01\x03\x0c\x01\x2c\x00\x2c\x00\x2c\x00\x46\x00\x46\x00\x46\x32\xc8', 'test'):
|
||||
if key == 'grid':
|
||||
assert update == True
|
||||
elif key == 'inverter':
|
||||
assert update == True
|
||||
elif key == 'env':
|
||||
assert update == True
|
||||
else:
|
||||
assert False
|
||||
assert exp_result[call] == val
|
||||
call += 1
|
||||
assert 0 == mb.err
|
||||
assert 5 == call
|
||||
assert mb.que.qsize() == 0
|
||||
assert not mb.req_pend
|
||||
|
||||
def test_queue():
|
||||
mb = TestHelper()
|
||||
mb.build_msg(1,3,0x3022,4)
|
||||
assert mb.que.qsize() == 0
|
||||
assert mb.req_pend
|
||||
|
||||
assert mb.send_calls == 1
|
||||
assert mb.pdu == b'\x01\x030"\x00\x04\xeb\x03'
|
||||
mb.pdu = None
|
||||
assert mb.send_calls == 1
|
||||
assert mb.pdu == None
|
||||
|
||||
assert mb.que.qsize() == 0
|
||||
|
||||
# cleanup queue
|
||||
mb._Modbus__stop_timer()
|
||||
assert not mb.req_pend
|
||||
|
||||
def test_queue2():
|
||||
'''Check queue handling for build_msg() calls'''
|
||||
mb = TestHelper()
|
||||
mb.build_msg(1,3,0x3007,6)
|
||||
mb.build_msg(1,6,0x2008,4)
|
||||
assert mb.que.qsize() == 1
|
||||
assert mb.req_pend
|
||||
mb.build_msg(1,3,0x3007,6)
|
||||
assert mb.que.qsize() == 2
|
||||
assert mb.req_pend
|
||||
|
||||
assert mb.send_calls == 1
|
||||
assert mb.pdu == b'\x01\x030\x07\x00\x06{\t'
|
||||
call = 0
|
||||
exp_result = ['V0.0.212', 4.4, 0.7, 0.7, 30]
|
||||
for key, update, val in mb.recv_resp(mb.db, b'\x01\x03\x0c\x01\x2c\x00\x2c\x00\x2c\x00\x46\x00\x46\x00\x46\x32\xc8', 'test'):
|
||||
if key == 'grid':
|
||||
assert update == True
|
||||
elif key == 'inverter':
|
||||
assert update == True
|
||||
elif key == 'env':
|
||||
assert update == True
|
||||
else:
|
||||
assert False
|
||||
assert exp_result[call] == val
|
||||
call += 1
|
||||
assert 0 == mb.err
|
||||
assert 5 == call
|
||||
|
||||
assert mb.que.qsize() == 1
|
||||
assert mb.send_calls == 2
|
||||
assert mb.pdu == b'\x01\x06\x20\x08\x00\x04\x02\x0b'
|
||||
|
||||
for key, update, val in mb.recv_resp(mb.db, b'\x01\x06\x20\x08\x00\x04\x02\x0b', 'test'):
|
||||
pass
|
||||
|
||||
assert mb.que.qsize() == 0
|
||||
assert mb.send_calls == 3
|
||||
assert mb.pdu == b'\x01\x030\x07\x00\x06{\t'
|
||||
call = 0
|
||||
for key, update, val in mb.recv_resp(mb.db, b'\x01\x03\x0c\x01\x2c\x00\x2c\x00\x2c\x00\x46\x00\x46\x00\x46\x32\xc8', 'test'):
|
||||
call += 1
|
||||
assert 0 == mb.err
|
||||
assert 5 == call
|
||||
|
||||
assert mb.que.qsize() == 0
|
||||
assert not mb.req_pend
|
||||
|
||||
def test_queue3():
|
||||
'''Check queue handling for recv_req() calls'''
|
||||
mb = TestHelper()
|
||||
assert mb.recv_req(b'\x01\x03\x30\x07\x00\x06{\t', mb.resp_handler)
|
||||
assert mb.recv_req(b'\x01\x06\x20\x08\x00\x04\x02\x0b', mb.resp_handler)
|
||||
assert mb.que.qsize() == 1
|
||||
assert mb.req_pend
|
||||
assert mb.recv_req(b'\x01\x03\x30\x07\x00\x06{\t')
|
||||
assert mb.que.qsize() == 2
|
||||
assert mb.req_pend
|
||||
|
||||
assert mb.send_calls == 1
|
||||
assert mb.pdu == b'\x01\x030\x07\x00\x06{\t'
|
||||
assert mb.recv_responses == 0
|
||||
|
||||
call = 0
|
||||
exp_result = ['V0.0.212', 4.4, 0.7, 0.7, 30]
|
||||
for key, update, val in mb.recv_resp(mb.db, b'\x01\x03\x0c\x01\x2c\x00\x2c\x00\x2c\x00\x46\x00\x46\x00\x46\x32\xc8', 'test'):
|
||||
if key == 'grid':
|
||||
assert update == True
|
||||
elif key == 'inverter':
|
||||
assert update == True
|
||||
elif key == 'env':
|
||||
assert update == True
|
||||
else:
|
||||
assert False
|
||||
assert exp_result[call] == val
|
||||
call += 1
|
||||
assert 0 == mb.err
|
||||
assert 5 == call
|
||||
assert mb.recv_responses == 1
|
||||
|
||||
assert mb.que.qsize() == 1
|
||||
assert mb.send_calls == 2
|
||||
assert mb.pdu == b'\x01\x06\x20\x08\x00\x04\x02\x0b'
|
||||
|
||||
for key, update, val in mb.recv_resp(mb.db, b'\x01\x06\x20\x08\x00\x04\x02\x0b', 'test'):
|
||||
pass
|
||||
assert 0 == mb.err
|
||||
assert mb.recv_responses == 2
|
||||
|
||||
assert mb.que.qsize() == 0
|
||||
assert mb.send_calls == 3
|
||||
assert mb.pdu == b'\x01\x030\x07\x00\x06{\t'
|
||||
call = 0
|
||||
for key, update, val in mb.recv_resp(mb.db, b'\x01\x03\x0c\x01\x2c\x00\x2c\x00\x2c\x00\x46\x00\x46\x00\x46\x32\xc8', 'test'):
|
||||
call += 1
|
||||
assert 0 == mb.err
|
||||
assert mb.recv_responses == 2
|
||||
assert 5 == call
|
||||
|
||||
|
||||
assert mb.que.qsize() == 0
|
||||
assert not mb.req_pend
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_timeout():
|
||||
'''Test MODBUS response timeout and RTU retransmitting'''
|
||||
assert asyncio.get_running_loop()
|
||||
mb = TestHelper()
|
||||
mb.max_retries = 2
|
||||
mb.timeout = 0.1 # 100ms timeout for fast testing, expect a time resolution of at least 10ms
|
||||
assert asyncio.get_running_loop() == mb.loop
|
||||
mb.build_msg(1,3,0x3007,6)
|
||||
mb.build_msg(1,6,0x2008,4)
|
||||
|
||||
assert mb.que.qsize() == 1
|
||||
assert mb.req_pend
|
||||
assert mb.retry_cnt == 0
|
||||
assert mb.send_calls == 1
|
||||
assert mb.pdu == b'\x01\x030\x07\x00\x06{\t'
|
||||
|
||||
mb.pdu = None
|
||||
await asyncio.sleep(0.11) # wait for first timeout and retransmittion
|
||||
assert mb.que.qsize() == 1
|
||||
assert mb.req_pend
|
||||
assert mb.retry_cnt == 1
|
||||
assert mb.send_calls == 2
|
||||
assert mb.pdu == b'\x01\x030\x07\x00\x06{\t'
|
||||
|
||||
mb.pdu = None
|
||||
await asyncio.sleep(0.11) # wait for second timeout and retransmittion
|
||||
assert mb.que.qsize() == 1
|
||||
assert mb.req_pend
|
||||
assert mb.retry_cnt == 2
|
||||
assert mb.send_calls == 3
|
||||
assert mb.pdu == b'\x01\x030\x07\x00\x06{\t'
|
||||
|
||||
mb.pdu = None
|
||||
await asyncio.sleep(0.11) # wait for third timeout and next pdu
|
||||
assert mb.que.qsize() == 0
|
||||
assert mb.req_pend
|
||||
assert mb.retry_cnt == 0
|
||||
assert mb.send_calls == 4
|
||||
assert mb.pdu == b'\x01\x06\x20\x08\x00\x04\x02\x0b'
|
||||
|
||||
mb.max_retries = 0 # next pdu without retranmsission
|
||||
await asyncio.sleep(0.11) # wait for fourth timout
|
||||
assert mb.que.qsize() == 0
|
||||
assert not mb.req_pend
|
||||
assert mb.retry_cnt == 0
|
||||
assert mb.send_calls == 4
|
||||
|
||||
# assert mb.counter == {}
|
||||
|
||||
def test_recv_unknown_data():
|
||||
'''Receive a response with an unknwon register'''
|
||||
mb = TestHelper()
|
||||
assert 0x9000 not in mb.map
|
||||
mb.map[0x9000] = {'reg': Register.TEST_REG1, 'fmt': '!H', 'ratio': 1}
|
||||
|
||||
mb.build_msg(1,3,0x9000,2)
|
||||
|
||||
# check matching response, but with CRC error
|
||||
call = 0
|
||||
for key, update, val in mb.recv_resp(mb.db, b'\x01\x03\x04\x01\x2c\x00\x46\xbb\xf4', 'test'):
|
||||
call += 1
|
||||
assert mb.err == 0
|
||||
assert 0 == call
|
||||
assert not mb.req_pend
|
||||
|
||||
del mb.map[0x9000]
|
||||
@@ -1,10 +1,15 @@
|
||||
import pytest
|
||||
import struct
|
||||
import time
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from app.src.gen3plus.solarman_v5 import SolarmanV5
|
||||
from app.src.config import Config
|
||||
from app.src.infos import Infos, Register
|
||||
from app.src.modbus import Modbus
|
||||
|
||||
|
||||
pytest_plugins = ('pytest_asyncio',)
|
||||
|
||||
# initialize the proxy statistics
|
||||
Infos.static_init()
|
||||
@@ -12,9 +17,19 @@ Infos.static_init()
|
||||
timestamp = int(time.time()) # 1712861197
|
||||
heartbeat = 60
|
||||
|
||||
class Writer():
|
||||
def __init__(self):
|
||||
self.sent_pdu = b''
|
||||
|
||||
def write(self, pdu: bytearray):
|
||||
self.sent_pdu = pdu
|
||||
|
||||
class MemoryStream(SolarmanV5):
|
||||
def __init__(self, msg, chunks = (0,), server_side: bool = True):
|
||||
super().__init__(server_side)
|
||||
if server_side:
|
||||
self.mb.timeout = 1 # overwrite for faster testing
|
||||
self.writer = Writer()
|
||||
self.__msg = msg
|
||||
self.__msg_len = len(msg)
|
||||
self.__chunks = chunks
|
||||
@@ -24,13 +39,16 @@ class MemoryStream(SolarmanV5):
|
||||
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.entity_prfx = ''
|
||||
self.at_acl = {'mqtt': {'allow': ['AT+'], 'block': ['AT+WEBU']}, 'tsun': {'allow': ['AT+Z', 'AT+UPURL', 'AT+SUPDATE', 'AT+TIME'], 'block': ['AT+WEBU']}}
|
||||
|
||||
def _timestamp(self):
|
||||
return timestamp
|
||||
|
||||
def _heartbeat(self) -> int:
|
||||
return heartbeat
|
||||
|
||||
|
||||
def append_msg(self, msg):
|
||||
self.__msg += msg
|
||||
@@ -54,6 +72,16 @@ class MemoryStream(SolarmanV5):
|
||||
pass
|
||||
return copied_bytes
|
||||
|
||||
async def async_write(self, headline=''):
|
||||
if self.test_exception_async_write:
|
||||
raise RuntimeError("Peer closed.")
|
||||
|
||||
def createClientStream(self, msg, chunks = (0,)):
|
||||
c = MemoryStream(msg, chunks, False)
|
||||
self.remoteStream = c
|
||||
c. remoteStream = self
|
||||
return c
|
||||
|
||||
def _SolarmanV5__flush_recv_msg(self) -> None:
|
||||
super()._SolarmanV5__flush_recv_msg()
|
||||
self.msg_count += 1
|
||||
@@ -308,6 +336,72 @@ def InverterIndMsg2000(): # 0x4210 rated Power 2000W
|
||||
msg += b'\x15'
|
||||
return msg
|
||||
|
||||
@pytest.fixture
|
||||
def InverterIndMsg800(): # 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 InverterIndMsg_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 InverterRspMsg(): # 0x1210
|
||||
msg = b'\xa5\x0a\x00\x10\x12\x02\02' +get_sn() +b'\x01\x01'
|
||||
@@ -317,6 +411,15 @@ def InverterRspMsg(): # 0x1210
|
||||
msg += b'\x15'
|
||||
return msg
|
||||
|
||||
@pytest.fixture
|
||||
def InverterRspMsg_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 UnknownMsg(): # 0x5110
|
||||
msg = b'\xa5\x0a\x00\x10\x51\x10\x84' +get_sn() +b'\x01\x01\x69\x6f\x09'
|
||||
@@ -346,7 +449,7 @@ def SyncStartRspMsg(): # 0x1310
|
||||
|
||||
@pytest.fixture
|
||||
def SyncStartFwdMsg(): # 0x4310
|
||||
msg = b'\xa5\x2f\x00\x10\x43\x0e\x0d' +get_sn() +b'\x81\x7a\x0b\x2e\x32'
|
||||
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'
|
||||
@@ -357,16 +460,25 @@ def SyncStartFwdMsg(): # 0x4310
|
||||
|
||||
@pytest.fixture
|
||||
def AtCommandIndMsg(): # 0x4510
|
||||
msg = b'\xa5\x27\x00\x10\x45\x02\x01' +get_sn() +b'\x01\x02\x00'
|
||||
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 AtCommandIndMsgBlock(): # 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 AtCommandRspMsg(): # 0x1510
|
||||
msg = b'\xa5\x0a\x00\x10\x15\x02\x02' +get_sn() +b'\x01\x01'
|
||||
msg = b'\xa5\x0a\x00\x10\x15\x03\x03' +get_sn() +b'\x01\x01'
|
||||
msg += total()
|
||||
msg += hb()
|
||||
msg += correct_checksum(msg)
|
||||
@@ -410,6 +522,72 @@ def SyncEndRspMsg(): # 0x1810
|
||||
msg += b'\x15'
|
||||
return msg
|
||||
|
||||
@pytest.fixture
|
||||
def MsgModbusCmd():
|
||||
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 MsgModbusCmdCrcErr():
|
||||
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 MsgModbusRsp(): # 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 MsgModbusInvalid(): # 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 MsgUnknownCmd():
|
||||
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 MsgUnknownCmdRsp(): # 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 ConfigTsunAllowAll():
|
||||
Config.config = {'solarman':{'enabled': True}, 'inverters':{'allow_all':True}}
|
||||
@@ -693,6 +871,52 @@ def test_read_two_messages(ConfigTsunAllowAll, DeviceIndMsg, DeviceRspMsg, Inver
|
||||
assert m._send_buffer==b''
|
||||
m.close()
|
||||
|
||||
def test_read_two_messages2(ConfigTsunAllowAll, InverterIndMsg, InverterIndMsg_81, InverterRspMsg, InverterRspMsg_81):
|
||||
ConfigTsunAllowAll
|
||||
m = MemoryStream(InverterIndMsg, (0,))
|
||||
m.append_msg(InverterIndMsg_81)
|
||||
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 m.time_ofs == 0x33e447a0
|
||||
assert str(m.seq) == '02:02'
|
||||
assert m.data_len == 0x199
|
||||
assert m.msg_count == 1
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
assert m._forward_buffer==InverterIndMsg
|
||||
assert m._send_buffer==InverterRspMsg
|
||||
|
||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||
m._init_new_client_conn()
|
||||
assert m._send_buffer==b''
|
||||
assert m._recv_buffer==InverterIndMsg_81
|
||||
|
||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||
m._forward_buffer = bytearray(0) # clear forward buffer for next test
|
||||
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.control == 0x4210
|
||||
assert m.time_ofs == 0x33e447a0
|
||||
assert str(m.seq) == '03:03'
|
||||
assert m.data_len == 0x199
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
assert m._forward_buffer==InverterIndMsg_81
|
||||
assert m._send_buffer==InverterRspMsg_81
|
||||
|
||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||
m._init_new_client_conn()
|
||||
assert m._send_buffer==b''
|
||||
m.close()
|
||||
|
||||
def test_unkown_message(ConfigTsunInv1, UnknownMsg):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(UnknownMsg, (0,))
|
||||
@@ -725,7 +949,7 @@ def test_device_rsp(ConfigTsunInv1, DeviceRspMsg):
|
||||
assert m.data_len == 0x0a
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m._forward_buffer==b'' # DeviceRspMsg
|
||||
assert m._forward_buffer==b''
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
m.close()
|
||||
|
||||
@@ -743,7 +967,7 @@ def test_inverter_rsp(ConfigTsunInv1, InverterRspMsg):
|
||||
assert m.data_len == 0x0a
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m._forward_buffer==b'' # InverterRspMsg
|
||||
assert m._forward_buffer==b''
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
m.close()
|
||||
|
||||
@@ -779,7 +1003,7 @@ def test_heartbeat_rsp(ConfigTsunInv1, HeartbeatRspMsg):
|
||||
assert m.data_len == 0x0a
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m._forward_buffer==b'' # HeartbeatRspMsg
|
||||
assert m._forward_buffer==b''
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
m.close()
|
||||
|
||||
@@ -800,6 +1024,7 @@ def test_sync_start_ind(ConfigTsunInv1, SyncStartIndMsg, SyncStartRspMsg, SyncSt
|
||||
assert m._forward_buffer==SyncStartIndMsg
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
|
||||
m.seq.server_side = False # simulate forawding to TSUN cloud
|
||||
m._update_header(m._forward_buffer)
|
||||
assert str(m.seq) == '0d:0e' # value after forwarding indication
|
||||
assert m._forward_buffer==SyncStartFwdMsg
|
||||
@@ -820,7 +1045,7 @@ def test_sync_start_rsp(ConfigTsunInv1, SyncStartRspMsg):
|
||||
assert m.data_len == 0x0a
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m._forward_buffer==b'' # HeartbeatRspMsg
|
||||
assert m._forward_buffer==b''
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
m.close()
|
||||
|
||||
@@ -856,29 +1081,10 @@ def test_sync_end_rsp(ConfigTsunInv1, SyncEndRspMsg):
|
||||
assert m.data_len == 0x0a
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m._forward_buffer==b'' # HeartbeatRspMsg
|
||||
assert m._forward_buffer==b''
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
m.close()
|
||||
|
||||
def test_at_command_ind(ConfigTsunInv1, AtCommandIndMsg, AtCommandRspMsg):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(AtCommandIndMsg, (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 == 0x4510
|
||||
assert str(m.seq) == '02:02'
|
||||
assert m.data_len == 39
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==AtCommandRspMsg
|
||||
assert m._forward_buffer==AtCommandIndMsg
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
assert m.db.stat['proxy']['AT_Command'] == 1
|
||||
m.close()
|
||||
|
||||
def test_build_modell_600(ConfigTsunAllowAll, InverterIndMsg):
|
||||
ConfigTsunAllowAll
|
||||
m = MemoryStream(InverterIndMsg, (0,))
|
||||
@@ -931,6 +1137,18 @@ def test_build_modell_2000(ConfigTsunAllowAll, InverterIndMsg2000):
|
||||
assert 'TSOL-MS2000' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0)
|
||||
m.close()
|
||||
|
||||
def test_build_modell_800(ConfigTsunAllowAll, InverterIndMsg800):
|
||||
ConfigTsunAllowAll
|
||||
m = MemoryStream(InverterIndMsg800, (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 'TSOL-MSxx00' == m.db.get_db_value(Register.EQUIPMENT_MODEL, 0)
|
||||
m.close()
|
||||
|
||||
def test_build_logger_modell(ConfigTsunAllowAll, DeviceIndMsg):
|
||||
ConfigTsunAllowAll
|
||||
m = MemoryStream(DeviceIndMsg, (0,))
|
||||
@@ -942,21 +1160,505 @@ def test_build_logger_modell(ConfigTsunAllowAll, DeviceIndMsg):
|
||||
assert 'V1.1.00.0B' == m.db.get_db_value(Register.COLLECTOR_FW_VERSION, 0).rstrip('\00')
|
||||
m.close()
|
||||
|
||||
def test_AT_cmd(ConfigTsunAllowAll, DeviceIndMsg, DeviceRspMsg, AtCommandIndMsg):
|
||||
ConfigTsunAllowAll
|
||||
def test_msg_iterator():
|
||||
m1 = SolarmanV5(server_side=True)
|
||||
m2 = SolarmanV5(server_side=True)
|
||||
m3 = SolarmanV5(server_side=True)
|
||||
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
|
||||
|
||||
def test_proxy_counter():
|
||||
m = SolarmanV5(server_side=True)
|
||||
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(ConfigTsunInv1, DeviceIndMsg, DeviceRspMsg, InverterIndMsg, InverterRspMsg, MsgModbusCmd):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(DeviceIndMsg, (0,), True)
|
||||
m.append_msg(InverterIndMsg)
|
||||
m.read()
|
||||
assert m.control == 0x4110
|
||||
assert str(m.seq) == '01:01'
|
||||
assert m._recv_buffer==b''
|
||||
assert m._recv_buffer==InverterIndMsg # unhandled next message
|
||||
assert m._send_buffer==DeviceRspMsg
|
||||
assert m._forward_buffer==DeviceIndMsg
|
||||
|
||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||
m._forward_buffer = bytearray(0) # clear send buffer for next test
|
||||
await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG)
|
||||
assert m._recv_buffer==InverterIndMsg # unhandled next message
|
||||
assert 0 == m.send_msg_ofs
|
||||
assert m._forward_buffer == b''
|
||||
assert m.writer.sent_pdu == b'' # modbus command must be ignore, cause connection is still not up
|
||||
assert m._send_buffer == b'' # modbus command must be ignore, cause connection is still not up
|
||||
|
||||
m.read()
|
||||
assert m.control == 0x4210
|
||||
assert str(m.seq) == '02:02'
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==InverterRspMsg
|
||||
assert m._forward_buffer==InverterIndMsg
|
||||
|
||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||
m._forward_buffer = bytearray(0) # clear send buffer for next test
|
||||
await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG)
|
||||
assert 0 == m.send_msg_ofs
|
||||
assert m._forward_buffer == b''
|
||||
assert m.writer.sent_pdu == MsgModbusCmd
|
||||
assert m._send_buffer == b''
|
||||
|
||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||
m.test_exception_async_write = True
|
||||
await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG)
|
||||
assert 0 == m.send_msg_ofs
|
||||
assert m._forward_buffer == b''
|
||||
assert m._send_buffer == b''
|
||||
m.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_AT_cmd(ConfigTsunAllowAll, DeviceIndMsg, DeviceRspMsg, InverterIndMsg, InverterRspMsg, AtCommandIndMsg):
|
||||
ConfigTsunAllowAll
|
||||
m = MemoryStream(DeviceIndMsg, (0,), True)
|
||||
m.append_msg(InverterIndMsg)
|
||||
m.read()
|
||||
assert m.control == 0x4110
|
||||
assert str(m.seq) == '01:01'
|
||||
assert m._recv_buffer==InverterIndMsg # unhandled next message
|
||||
assert m._send_buffer==DeviceRspMsg
|
||||
assert m._forward_buffer==DeviceIndMsg
|
||||
|
||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||
m._forward_buffer = bytearray(0) # clear send buffer for next test
|
||||
await m.send_at_cmd('AT+TIME=214028,1,60,120')
|
||||
assert m._recv_buffer==InverterIndMsg # unhandled next message
|
||||
assert m._send_buffer==b''
|
||||
assert m._forward_buffer==b''
|
||||
assert str(m.seq) == '01:01'
|
||||
|
||||
m.read()
|
||||
assert m.control == 0x4210
|
||||
assert str(m.seq) == '02:02'
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==InverterRspMsg
|
||||
assert m._forward_buffer==InverterIndMsg
|
||||
|
||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||
m._forward_buffer = bytearray(0) # clear send buffer for next test
|
||||
m.send_at_cmd('AT+TIME=214028,1,60,120')
|
||||
await m.send_at_cmd('AT+TIME=214028,1,60,120')
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==AtCommandIndMsg
|
||||
assert m._forward_buffer==b''
|
||||
assert str(m.seq) == '01:02'
|
||||
assert str(m.seq) == '02:03'
|
||||
|
||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||
m.test_exception_async_write = True
|
||||
await m.send_at_cmd('AT+TIME=214028,1,60,120')
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m._forward_buffer==b''
|
||||
assert str(m.seq) == '02:04'
|
||||
assert m.forward_at_cmd_resp == False
|
||||
m.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_AT_cmd_blocked(ConfigTsunAllowAll, DeviceIndMsg, DeviceRspMsg, InverterIndMsg, InverterRspMsg, AtCommandIndMsg):
|
||||
ConfigTsunAllowAll
|
||||
m = MemoryStream(DeviceIndMsg, (0,), True)
|
||||
m.append_msg(InverterIndMsg)
|
||||
m.read()
|
||||
assert m.control == 0x4110
|
||||
assert str(m.seq) == '01:01'
|
||||
assert m._recv_buffer==InverterIndMsg # unhandled next message
|
||||
assert m._send_buffer==DeviceRspMsg
|
||||
assert m._forward_buffer==DeviceIndMsg
|
||||
|
||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||
m._forward_buffer = bytearray(0) # clear send buffer for next test
|
||||
await m.send_at_cmd('AT+WEBU')
|
||||
assert m._recv_buffer==InverterIndMsg # unhandled next message
|
||||
assert m._send_buffer==b''
|
||||
assert m._forward_buffer==b''
|
||||
assert str(m.seq) == '01:01'
|
||||
|
||||
m.read()
|
||||
assert m.control == 0x4210
|
||||
assert str(m.seq) == '02:02'
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==InverterRspMsg
|
||||
assert m._forward_buffer==InverterIndMsg
|
||||
|
||||
m._send_buffer = bytearray(0) # clear send buffer for next test
|
||||
m._forward_buffer = bytearray(0) # clear send buffer for next test
|
||||
await m.send_at_cmd('AT+WEBU')
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m._forward_buffer==b''
|
||||
assert str(m.seq) == '02:02'
|
||||
assert m.forward_at_cmd_resp == False
|
||||
m.close()
|
||||
|
||||
def test_AT_cmd_ind(ConfigTsunInv1, AtCommandIndMsg):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(AtCommandIndMsg, (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.unique_id == '2070233889'
|
||||
assert m.control == 0x4510
|
||||
assert str(m.seq) == '03:02'
|
||||
assert m.data_len == 39
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m._forward_buffer==AtCommandIndMsg
|
||||
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.close()
|
||||
|
||||
def test_AT_cmd_ind_block(ConfigTsunInv1, AtCommandIndMsgBlock):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(AtCommandIndMsgBlock, (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.unique_id == '2070233889'
|
||||
assert m.control == 0x4510
|
||||
assert str(m.seq) == '03:02'
|
||||
assert m.data_len == 23
|
||||
assert m._recv_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m._forward_buffer==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
|
||||
m.close()
|
||||
|
||||
def test_msg_at_command_rsp1(ConfigTsunInv1, AtCommandRspMsg):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(AtCommandRspMsg)
|
||||
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
||||
m.db.stat['proxy']['Modbus_Command'] = 0
|
||||
m.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==10
|
||||
assert m._forward_buffer==AtCommandRspMsg
|
||||
assert m._send_buffer==b''
|
||||
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
||||
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
||||
m.close()
|
||||
|
||||
def test_msg_at_command_rsp2(ConfigTsunInv1, AtCommandRspMsg):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(AtCommandRspMsg)
|
||||
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
||||
m.db.stat['proxy']['Modbus_Command'] = 0
|
||||
m.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==10
|
||||
assert m._forward_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
||||
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
||||
m.close()
|
||||
|
||||
def test_msg_modbus_req(ConfigTsunInv1, MsgModbusCmd):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(b'')
|
||||
c = m.createClientStream(MsgModbusCmd)
|
||||
|
||||
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._forward_buffer==MsgModbusCmd
|
||||
assert c._send_buffer==b''
|
||||
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()
|
||||
|
||||
def test_msg_modbus_req2(ConfigTsunInv1, MsgModbusCmdCrcErr):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(b'')
|
||||
c = m.createClientStream(MsgModbusCmdCrcErr)
|
||||
|
||||
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._forward_buffer==MsgModbusCmdCrcErr
|
||||
assert c._send_buffer==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()
|
||||
|
||||
def test_msg_unknown_cmd_req(ConfigTsunInv1, MsgUnknownCmd):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(MsgUnknownCmd, (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._forward_buffer==MsgUnknownCmd
|
||||
assert m._send_buffer==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()
|
||||
|
||||
def test_msg_modbus_rsp1(ConfigTsunInv1, MsgModbusRsp):
|
||||
'''Modbus response without a valid Modbus request must be dropped'''
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(MsgModbusRsp)
|
||||
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._forward_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
||||
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
||||
m.close()
|
||||
|
||||
def test_msg_modbus_rsp2(ConfigTsunInv1, MsgModbusRsp):
|
||||
'''Modbus response with a valid Modbus request must be forwarded'''
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(MsgModbusRsp)
|
||||
m.append_msg(MsgModbusRsp)
|
||||
|
||||
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
|
||||
# assert m.db.db == {'inverter': {'Manufacturer': 'TSUN', 'Equipment_Model': 'TSOL-MSxx00'}}
|
||||
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._forward_buffer==MsgModbusRsp
|
||||
assert m._send_buffer==b''
|
||||
# assert m.db.db == {'inverter': {'Version': 'V5.1.09', 'Rated_Power': 300}, 'grid': {'Voltage': 225.9, 'Current': 0.41, 'Frequency': 49.99, 'Output_Power': 94.8}, 'env': {'Inverter_Temp': 22}, 'input': {'pv1': {'Voltage': 0.8, 'Current': 0.0, 'Power': 0.0}, 'pv2': {'Voltage': 34.5, 'Current': 2.89, 'Power': 99.8}, 'pv3': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}, 'pv4': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}}}
|
||||
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.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._forward_buffer==MsgModbusRsp
|
||||
assert m._send_buffer==b''
|
||||
# assert m.db.db == {'inverter': {'Version': 'V5.1.09', 'Rated_Power': 300}, 'grid': {'Voltage': 225.9, 'Current': 0.41, 'Frequency': 49.99, 'Output_Power': 94.8}, 'env': {'Inverter_Temp': 22}, 'input': {'pv1': {'Voltage': 0.8, 'Current': 0.0, 'Power': 0.0}, 'pv2': {'Voltage': 34.5, 'Current': 2.89, 'Power': 99.8}, 'pv3': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}, 'pv4': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}}}
|
||||
assert m.db.get_db_value(Register.VERSION) == 'V4.0.10'
|
||||
assert m.new_data['inverter'] == False
|
||||
|
||||
m.close()
|
||||
|
||||
def test_msg_modbus_rsp3(ConfigTsunInv1, MsgModbusRsp):
|
||||
'''Modbus response with a valid Modbus request must be forwarded'''
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(MsgModbusRsp)
|
||||
m.append_msg(MsgModbusRsp)
|
||||
|
||||
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
|
||||
# assert m.db.db == {'inverter': {'Manufacturer': 'TSUN', 'Equipment_Model': 'TSOL-MSxx00'}}
|
||||
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._forward_buffer==MsgModbusRsp
|
||||
assert m._send_buffer==b''
|
||||
# assert m.db.db == {'inverter': {'Version': 'V5.1.09', 'Rated_Power': 300}, 'grid': {'Voltage': 225.9, 'Current': 0.41, 'Frequency': 49.99, 'Output_Power': 94.8}, 'env': {'Inverter_Temp': 22}, 'input': {'pv1': {'Voltage': 0.8, 'Current': 0.0, 'Power': 0.0}, 'pv2': {'Voltage': 34.5, 'Current': 2.89, 'Power': 99.8}, 'pv3': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}, 'pv4': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}}}
|
||||
assert m.db.get_db_value(Register.VERSION) == 'V4.0.10'
|
||||
assert m.new_data['inverter'] == True
|
||||
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 == 5
|
||||
assert m.msg_count == 2
|
||||
assert m._forward_buffer==MsgModbusRsp
|
||||
assert m._send_buffer==b''
|
||||
# assert m.db.db == {'inverter': {'Version': 'V5.1.09', 'Rated_Power': 300}, 'grid': {'Voltage': 225.9, 'Current': 0.41, 'Frequency': 49.99, 'Output_Power': 94.8}, 'env': {'Inverter_Temp': 22}, 'input': {'pv1': {'Voltage': 0.8, 'Current': 0.0, 'Power': 0.0}, 'pv2': {'Voltage': 34.5, 'Current': 2.89, 'Power': 99.8}, 'pv3': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}, 'pv4': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}}}
|
||||
assert m.db.get_db_value(Register.VERSION) == 'V4.0.10'
|
||||
assert m.new_data['inverter'] == False
|
||||
|
||||
m.close()
|
||||
|
||||
def test_msg_unknown_rsp(ConfigTsunInv1, MsgUnknownCmdRsp):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(MsgUnknownCmdRsp)
|
||||
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._forward_buffer==MsgUnknownCmdRsp
|
||||
assert m._send_buffer==b''
|
||||
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
||||
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
||||
m.close()
|
||||
|
||||
def test_msg_modbus_invalid(ConfigTsunInv1, MsgModbusInvalid):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(MsgModbusInvalid, (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._forward_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
||||
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
||||
m.close()
|
||||
|
||||
def test_msg_modbus_fragment(ConfigTsunInv1, MsgModbusRsp):
|
||||
ConfigTsunInv1
|
||||
# receive more bytes than expected (7 bytes from the next msg)
|
||||
m = MemoryStream(MsgModbusRsp+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._forward_buffer==MsgModbusRsp
|
||||
assert m._send_buffer == 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()
|
||||
'''
|
||||
def test_zombie_conn(ConfigTsunInv1, MsgInverterInd):
|
||||
ConfigTsunInv1
|
||||
tracer.setLevel(logging.DEBUG)
|
||||
m1 = MemoryStream(MsgInverterInd, (0,))
|
||||
m2 = MemoryStream(MsgInverterInd, (0,))
|
||||
m3 = MemoryStream(MsgInverterInd, (0,))
|
||||
assert m1.state == m1.STATE_INIT
|
||||
assert m2.state == m2.STATE_INIT
|
||||
assert m3.state == m3.STATE_INIT
|
||||
m1.read() # read complete msg, and set unique_id
|
||||
assert m1.state == m1.STATE_INIT
|
||||
assert m2.state == m2.STATE_INIT
|
||||
assert m3.state == m3.STATE_INIT
|
||||
m2.read() # read complete msg, and set unique_id
|
||||
assert m1.state == m1.STATE_CLOSED
|
||||
assert m2.state == m2.STATE_INIT
|
||||
assert m3.state == m3.STATE_INIT
|
||||
m3.read() # read complete msg, and set unique_id
|
||||
assert m1.state == m1.STATE_CLOSED
|
||||
assert m2.state == m2.STATE_CLOSED
|
||||
assert m3.state == m3.STATE_INIT
|
||||
m1.close()
|
||||
m2.close()
|
||||
m3.close()
|
||||
'''
|
||||
@@ -2,16 +2,31 @@
|
||||
import pytest, logging
|
||||
from app.src.gen3.talent import Talent, Control
|
||||
from app.src.config import Config
|
||||
from app.src.infos import Infos
|
||||
from app.src.infos import Infos, Register
|
||||
from app.src.modbus import Modbus
|
||||
|
||||
|
||||
pytest_plugins = ('pytest_asyncio',)
|
||||
|
||||
# initialize the proxy statistics
|
||||
Infos.static_init()
|
||||
|
||||
tracer = logging.getLogger('tracer')
|
||||
|
||||
|
||||
|
||||
class Writer():
|
||||
def __init__(self):
|
||||
self.sent_pdu = b''
|
||||
|
||||
def write(self, pdu: bytearray):
|
||||
self.sent_pdu = pdu
|
||||
|
||||
class MemoryStream(Talent):
|
||||
def __init__(self, msg, chunks = (0,), server_side: bool = True):
|
||||
super().__init__(server_side)
|
||||
if server_side:
|
||||
self.mb.timeout = 1 # overwrite for faster testing
|
||||
self.writer = Writer()
|
||||
self.__msg = msg
|
||||
self.__msg_len = len(msg)
|
||||
self.__chunks = chunks
|
||||
@@ -19,6 +34,8 @@ class MemoryStream(Talent):
|
||||
self.__chunk_idx = 0
|
||||
self.msg_count = 0
|
||||
self.addr = 'Test: SrvSide'
|
||||
self.send_msg_ofs = 0
|
||||
self.test_exception_async_write = False
|
||||
|
||||
def append_msg(self, msg):
|
||||
self.__msg += msg
|
||||
@@ -46,11 +63,22 @@ class MemoryStream(Talent):
|
||||
# return 1700260990000
|
||||
return 1691246944000
|
||||
|
||||
def createClientStream(self, msg, chunks = (0,)):
|
||||
c = MemoryStream(msg, chunks, False)
|
||||
self.remoteStream = c
|
||||
c. remoteStream = self
|
||||
return c
|
||||
|
||||
def _Talent__flush_recv_msg(self) -> None:
|
||||
super()._Talent__flush_recv_msg()
|
||||
self.msg_count += 1
|
||||
return
|
||||
|
||||
async def async_write(self, headline=''):
|
||||
if self.test_exception_async_write:
|
||||
raise RuntimeError("Peer closed.")
|
||||
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def MsgContactInfo(): # Contact Info message
|
||||
@@ -191,6 +219,42 @@ def MsgOtaAck(): # Over the air update rewuest from tsun cloud
|
||||
def MsgOtaInvalid(): # Get Time Request message
|
||||
return b'\x00\x00\x00\x14\x10R170000000000001\x99\x13\x01'
|
||||
|
||||
@pytest.fixture
|
||||
def MsgModbusCmd():
|
||||
msg = b'\x00\x00\x00\x20\x10R170000000000001'
|
||||
msg += b'\x70\x77\x00\x01\xa3\x28\x08\x01\x06\x20\x08'
|
||||
msg += b'\x00\x00\x03\xc8'
|
||||
return msg
|
||||
|
||||
@pytest.fixture
|
||||
def MsgModbusCmdCrcErr():
|
||||
msg = b'\x00\x00\x00\x20\x10R170000000000001'
|
||||
msg += b'\x70\x77\x00\x01\xa3\x28\x08\x01\x06\x20\x08'
|
||||
msg += b'\x00\x00\x04\xc8'
|
||||
return msg
|
||||
|
||||
@pytest.fixture
|
||||
def MsgModbusRsp():
|
||||
msg = b'\x00\x00\x00\x20\x10R170000000000001'
|
||||
msg += b'\x91\x77\x17\x18\x19\x1a\x08\x01\x06\x20\x08'
|
||||
msg += b'\x00\x00\x03\xc8'
|
||||
return msg
|
||||
|
||||
@pytest.fixture
|
||||
def MsgModbusInv():
|
||||
msg = b'\x00\x00\x00\x20\x10R170000000000001'
|
||||
msg += b'\x99\x77\x17\x18\x19\x1a\x08\x01\x06\x20\x08'
|
||||
msg += b'\x00\x00\x03\xc8'
|
||||
return msg
|
||||
|
||||
@pytest.fixture
|
||||
def MsgModbusResp20():
|
||||
msg = b'\x00\x00\x00\x45\x10R170000000000001'
|
||||
msg += b'\x91\x77\x17\x18\x19\x1a\x2d\x01\x03\x28\x51'
|
||||
msg += b'\x09\x08\xd3\x00\x29\x13\x87\x00\x3e\x00\x00\x01\x2c\x03\xb4\x00'
|
||||
msg += b'\x08\x00\x00\x00\x00\x01\x59\x01\x21\x03\xe6\x00\x00\x00\x00\x00'
|
||||
msg += b'\x00\x00\x00\x00\x00\x00\x00\xdb\x6b'
|
||||
return msg
|
||||
|
||||
def test_read_message(MsgContactInfo):
|
||||
m = MemoryStream(MsgContactInfo, (0,))
|
||||
@@ -757,10 +821,16 @@ def test_msg_unknown(ConfigTsunInv1, MsgUnknown):
|
||||
m.close()
|
||||
|
||||
def test_ctrl_byte():
|
||||
c = Control(0x70)
|
||||
assert not c.is_ind()
|
||||
assert not c.is_resp()
|
||||
assert c.is_req()
|
||||
c = Control(0x91)
|
||||
assert not c.is_req()
|
||||
assert c.is_ind()
|
||||
assert not c.is_resp()
|
||||
c = Control(0x99)
|
||||
assert not c.is_req()
|
||||
assert not c.is_ind()
|
||||
assert c.is_resp()
|
||||
|
||||
@@ -786,19 +856,355 @@ def test_msg_iterator():
|
||||
assert test2 == 1
|
||||
|
||||
def test_proxy_counter():
|
||||
m = Talent(server_side=True)
|
||||
# m = MemoryStream(b'')
|
||||
# m.close()
|
||||
Infos.stat['proxy']['Modbus_Command'] = 1
|
||||
|
||||
m = MemoryStream(b'')
|
||||
m.id_str = b"R170000000000001"
|
||||
c = m.createClientStream(b'')
|
||||
|
||||
assert m.new_data == {}
|
||||
m.db.stat['proxy']['Unknown_Msg'] = 0
|
||||
c.db.stat['proxy']['Unknown_Msg'] = 0
|
||||
Infos.new_stat_data['proxy'] = False
|
||||
|
||||
m.inc_counter('Unknown_Msg')
|
||||
m.close()
|
||||
m = MemoryStream(b'')
|
||||
|
||||
assert m.new_data == {}
|
||||
assert Infos.new_stat_data == {'proxy': True}
|
||||
assert m.db.new_stat_data == {'proxy': True}
|
||||
assert c.db.new_stat_data == {'proxy': True}
|
||||
assert 1 == m.db.stat['proxy']['Unknown_Msg']
|
||||
assert 1 == c.db.stat['proxy']['Unknown_Msg']
|
||||
Infos.new_stat_data['proxy'] = False
|
||||
|
||||
c.inc_counter('Unknown_Msg')
|
||||
assert m.new_data == {}
|
||||
assert Infos.new_stat_data == {'proxy': True}
|
||||
assert m.db.new_stat_data == {'proxy': True}
|
||||
assert c.db.new_stat_data == {'proxy': True}
|
||||
assert 2 == m.db.stat['proxy']['Unknown_Msg']
|
||||
assert 2 == c.db.stat['proxy']['Unknown_Msg']
|
||||
Infos.new_stat_data['proxy'] = False
|
||||
|
||||
c.inc_counter('Modbus_Command')
|
||||
assert m.new_data == {}
|
||||
assert Infos.new_stat_data == {'proxy': True}
|
||||
assert m.db.new_stat_data == {'proxy': True}
|
||||
assert c.db.new_stat_data == {'proxy': True}
|
||||
assert 2 == m.db.stat['proxy']['Modbus_Command']
|
||||
assert 2 == c.db.stat['proxy']['Modbus_Command']
|
||||
|
||||
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']
|
||||
assert 1 == m.db.stat['proxy']['Unknown_Msg']
|
||||
m.close()
|
||||
|
||||
def test_msg_modbus_req(ConfigTsunInv1, MsgModbusCmd):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(b'')
|
||||
m.id_str = b"R170000000000001"
|
||||
m.state = m.STATE_UP
|
||||
|
||||
c = m.createClientStream(MsgModbusCmd)
|
||||
|
||||
m.db.stat['proxy']['Unknown_Ctrl'] = 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.id_str == b"R170000000000001"
|
||||
assert c.unique_id == 'R170000000000001'
|
||||
assert int(c.ctrl)==112
|
||||
assert c.msg_id==119
|
||||
assert c.header_len==23
|
||||
assert c.data_len==13
|
||||
assert c._forward_buffer==b''
|
||||
assert c._send_buffer==b''
|
||||
assert m.id_str == b"R170000000000001"
|
||||
assert m._forward_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m.writer.sent_pdu == MsgModbusCmd
|
||||
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
||||
assert m.db.stat['proxy']['Modbus_Command'] == 1
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
m.close()
|
||||
|
||||
def test_msg_modbus_req2(ConfigTsunInv1, MsgModbusCmd):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(b'')
|
||||
m.id_str = b"R170000000000001"
|
||||
|
||||
c = m.createClientStream(MsgModbusCmd)
|
||||
|
||||
m.db.stat['proxy']['Unknown_Ctrl'] = 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.id_str == b"R170000000000001"
|
||||
assert c.unique_id == 'R170000000000001'
|
||||
assert int(c.ctrl)==112
|
||||
assert c.msg_id==119
|
||||
assert c.header_len==23
|
||||
assert c.data_len==13
|
||||
assert c._forward_buffer==b''
|
||||
assert c._send_buffer==b''
|
||||
assert m.id_str == b"R170000000000001"
|
||||
assert m._forward_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m.writer.sent_pdu == b''
|
||||
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
||||
assert m.db.stat['proxy']['Modbus_Command'] == 1
|
||||
assert m.db.stat['proxy']['Invalid_Msg_Format'] == 0
|
||||
m.close()
|
||||
|
||||
def test_msg_modbus_req3(ConfigTsunInv1, MsgModbusCmdCrcErr):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(b'')
|
||||
m.id_str = b"R170000000000001"
|
||||
c = m.createClientStream(MsgModbusCmdCrcErr)
|
||||
|
||||
m.db.stat['proxy']['Unknown_Ctrl'] = 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.id_str == b"R170000000000001"
|
||||
assert c.unique_id == 'R170000000000001'
|
||||
assert int(c.ctrl)==112
|
||||
assert c.msg_id==119
|
||||
assert c.header_len==23
|
||||
assert c.data_len==13
|
||||
assert c._forward_buffer==b''
|
||||
assert c._send_buffer==b''
|
||||
assert m._forward_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m.writer.sent_pdu ==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'] == 1
|
||||
m.close()
|
||||
|
||||
def test_msg_modbus_rsp1(ConfigTsunInv1, MsgModbusRsp):
|
||||
'''Modbus response without a valid Modbus request must be dropped'''
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(MsgModbusRsp)
|
||||
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.id_str == b"R170000000000001"
|
||||
assert m.unique_id == 'R170000000000001'
|
||||
assert int(m.ctrl)==145
|
||||
assert m.msg_id==119
|
||||
assert m.header_len==23
|
||||
assert m.data_len==13
|
||||
assert m._forward_buffer==b''
|
||||
assert m._send_buffer==b''
|
||||
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
||||
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
||||
m.close()
|
||||
|
||||
def test_msg_modbus_rsp2(ConfigTsunInv1, MsgModbusResp20):
|
||||
'''Modbus response with a valid Modbus request must be forwarded'''
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(MsgModbusResp20)
|
||||
m.append_msg(MsgModbusResp20)
|
||||
|
||||
m.mb.rsp_handler = m.msg_forward
|
||||
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
|
||||
|
||||
assert m.db.db == {}
|
||||
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._forward_buffer==MsgModbusResp20
|
||||
assert m._send_buffer==b''
|
||||
assert m.db.db == {'inverter': {'Version': 'V5.1.09', 'Rated_Power': 300}, 'grid': {'Voltage': 225.9, 'Current': 0.41, 'Frequency': 49.99, 'Output_Power': 94.8}, 'env': {'Inverter_Temp': 22}, 'input': {'pv1': {'Voltage': 0.8, 'Current': 0.0, 'Power': 0.0}, 'pv2': {'Voltage': 34.5, 'Current': 2.89, 'Power': 99.8}, 'pv3': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}, 'pv4': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}}}
|
||||
assert m.db.get_db_value(Register.VERSION) == 'V5.1.09'
|
||||
assert m.new_data['inverter'] == True
|
||||
|
||||
m.new_data['inverter'] = False
|
||||
m.mb.req_pend = 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.mb.err == 0
|
||||
assert m.msg_count == 2
|
||||
assert m._forward_buffer==MsgModbusResp20
|
||||
assert m._send_buffer==b''
|
||||
assert m.db.db == {'inverter': {'Version': 'V5.1.09', 'Rated_Power': 300}, 'grid': {'Voltage': 225.9, 'Current': 0.41, 'Frequency': 49.99, 'Output_Power': 94.8}, 'env': {'Inverter_Temp': 22}, 'input': {'pv1': {'Voltage': 0.8, 'Current': 0.0, 'Power': 0.0}, 'pv2': {'Voltage': 34.5, 'Current': 2.89, 'Power': 99.8}, 'pv3': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}, 'pv4': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}}}
|
||||
assert m.db.get_db_value(Register.VERSION) == 'V5.1.09'
|
||||
assert m.new_data['inverter'] == False
|
||||
|
||||
m.close()
|
||||
|
||||
def test_msg_modbus_rsp3(ConfigTsunInv1, MsgModbusResp20):
|
||||
'''Modbus response with a valid Modbus request must be forwarded'''
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(MsgModbusResp20)
|
||||
m.append_msg(MsgModbusResp20)
|
||||
|
||||
m.mb.rsp_handler = m.msg_forward
|
||||
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
|
||||
|
||||
assert m.db.db == {}
|
||||
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._forward_buffer==MsgModbusResp20
|
||||
assert m._send_buffer==b''
|
||||
assert m.db.db == {'inverter': {'Version': 'V5.1.09', 'Rated_Power': 300}, 'grid': {'Voltage': 225.9, 'Current': 0.41, 'Frequency': 49.99, 'Output_Power': 94.8}, 'env': {'Inverter_Temp': 22}, 'input': {'pv1': {'Voltage': 0.8, 'Current': 0.0, 'Power': 0.0}, 'pv2': {'Voltage': 34.5, 'Current': 2.89, 'Power': 99.8}, 'pv3': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}, 'pv4': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}}}
|
||||
assert m.db.get_db_value(Register.VERSION) == 'V5.1.09'
|
||||
assert m.new_data['inverter'] == True
|
||||
m.new_data['inverter'] = False
|
||||
assert m.mb.req_pend == 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 == 5
|
||||
assert m.msg_count == 2
|
||||
assert m._forward_buffer==MsgModbusResp20
|
||||
assert m._send_buffer==b''
|
||||
assert m.db.db == {'inverter': {'Version': 'V5.1.09', 'Rated_Power': 300}, 'grid': {'Voltage': 225.9, 'Current': 0.41, 'Frequency': 49.99, 'Output_Power': 94.8}, 'env': {'Inverter_Temp': 22}, 'input': {'pv1': {'Voltage': 0.8, 'Current': 0.0, 'Power': 0.0}, 'pv2': {'Voltage': 34.5, 'Current': 2.89, 'Power': 99.8}, 'pv3': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}, 'pv4': {'Voltage': 0.0, 'Current': 0.0, 'Power': 0.0}}}
|
||||
assert m.db.get_db_value(Register.VERSION) == 'V5.1.09'
|
||||
assert m.new_data['inverter'] == False
|
||||
|
||||
m.close()
|
||||
|
||||
def test_msg_modbus_invalid(ConfigTsunInv1, MsgModbusInv):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(MsgModbusInv, (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.id_str == b"R170000000000001"
|
||||
assert m.unique_id == 'R170000000000001'
|
||||
assert int(m.ctrl)==153
|
||||
assert m.msg_id==119
|
||||
assert m.header_len==23
|
||||
assert m.data_len==13
|
||||
assert m._forward_buffer==MsgModbusInv
|
||||
assert m._send_buffer==b''
|
||||
assert m.db.stat['proxy']['Unknown_Ctrl'] == 1
|
||||
assert m.db.stat['proxy']['Modbus_Command'] == 0
|
||||
m.close()
|
||||
|
||||
def test_msg_modbus_fragment(ConfigTsunInv1, MsgModbusResp20):
|
||||
ConfigTsunInv1
|
||||
# receive more bytes than expected (7 bytes from the next msg)
|
||||
m = MemoryStream(MsgModbusResp20+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.msg_forward
|
||||
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.id_str == b"R170000000000001"
|
||||
assert m.unique_id == 'R170000000000001'
|
||||
assert int(m.ctrl) == 0x91
|
||||
assert m.msg_id == 119
|
||||
assert m.header_len == 23
|
||||
assert m.data_len == 50
|
||||
assert m._forward_buffer==MsgModbusResp20
|
||||
assert m._send_buffer == 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_msg_build_modbus_req(ConfigTsunInv1, MsgModbusCmd):
|
||||
ConfigTsunInv1
|
||||
m = MemoryStream(b'', (0,), True)
|
||||
m.id_str = b"R170000000000001"
|
||||
await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG)
|
||||
assert 0 == m.send_msg_ofs
|
||||
assert m._forward_buffer == b''
|
||||
assert m._send_buffer == b''
|
||||
assert m.writer.sent_pdu == b''
|
||||
|
||||
m.state = m.STATE_UP
|
||||
await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG)
|
||||
assert 0 == m.send_msg_ofs
|
||||
assert m._forward_buffer == b''
|
||||
assert m._send_buffer == b''
|
||||
assert m.writer.sent_pdu == MsgModbusCmd
|
||||
|
||||
m.writer.sent_pdu = bytearray(0) # clear send buffer for next test
|
||||
m.test_exception_async_write = True
|
||||
await m.send_modbus_cmd(Modbus.WRITE_SINGLE_REG, 0x2008, 0, logging.DEBUG)
|
||||
assert 0 == m.send_msg_ofs
|
||||
assert m._forward_buffer == b''
|
||||
assert m._send_buffer == b''
|
||||
assert m.writer.sent_pdu == b''
|
||||
m.close()
|
||||
'''
|
||||
def test_zombie_conn(ConfigTsunInv1, MsgInverterInd):
|
||||
ConfigTsunInv1
|
||||
tracer.setLevel(logging.DEBUG)
|
||||
start_val = MemoryStream._RefNo
|
||||
|
||||
m1 = MemoryStream(MsgInverterInd, (0,))
|
||||
assert MemoryStream._RefNo == 1 + start_val
|
||||
assert m1.RefNo == 1 + start_val
|
||||
m2 = MemoryStream(MsgInverterInd, (0,))
|
||||
assert MemoryStream._RefNo == 2 + start_val
|
||||
assert m2.RefNo == 2 + start_val
|
||||
m3 = MemoryStream(MsgInverterInd, (0,))
|
||||
assert MemoryStream._RefNo == 3 + start_val
|
||||
assert m3.RefNo == 3 + start_val
|
||||
assert m1.state == m1.STATE_INIT
|
||||
assert m2.state == m2.STATE_INIT
|
||||
assert m3.state == m3.STATE_INIT
|
||||
m1.read() # read complete msg, and set unique_id
|
||||
assert m1.state == m1.STATE_UP
|
||||
assert m2.state == m2.STATE_INIT
|
||||
assert m3.state == m3.STATE_INIT
|
||||
m2.read() # read complete msg, and set unique_id
|
||||
assert m1.state == m1.STATE_CLOSED
|
||||
assert m2.state == m2.STATE_UP
|
||||
assert m3.state == m3.STATE_INIT
|
||||
m3.read() # read complete msg, and set unique_id
|
||||
assert m1.state == m1.STATE_CLOSED
|
||||
assert m2.state == m2.STATE_CLOSED
|
||||
assert m3.state == m3.STATE_UP
|
||||
m1.close()
|
||||
m2.close()
|
||||
m3.close()
|
||||
'''
|
||||
Reference in New Issue
Block a user