Merge branch 'main' of https://github.com/s-allius/tsun-gen3-proxy into s-allius/issue415
This commit is contained in:
@@ -109,7 +109,7 @@ def test_default_db():
|
||||
i = InfosG3P(client_mode=False)
|
||||
|
||||
assert json.dumps(i.db) == json.dumps({
|
||||
"inverter": {"Manufacturer": "TSUN", "Equipment_Model": "TSOL-MSxx00", "No_Inputs": 4},
|
||||
"inverter": {"Manufacturer": "TSUN", "Equipment_Model": "TSOL-MSxx00", "No_Inputs": 2},
|
||||
"collector": {"Chip_Type": "IGEN TECH"},
|
||||
})
|
||||
|
||||
@@ -271,7 +271,7 @@ def test_build_ha_conf1():
|
||||
elif id == 'inv_count_456':
|
||||
assert False
|
||||
|
||||
assert tests==7
|
||||
assert tests==5
|
||||
|
||||
def test_build_ha_conf2():
|
||||
i = InfosG3P(client_mode=False)
|
||||
@@ -346,7 +346,7 @@ def test_build_ha_conf3():
|
||||
elif id == 'inv_count_456':
|
||||
assert False
|
||||
|
||||
assert tests==7
|
||||
assert tests==5
|
||||
|
||||
def test_build_ha_conf4():
|
||||
i = InfosG3P(client_mode=True)
|
||||
|
||||
@@ -4,6 +4,10 @@ import logging
|
||||
import os
|
||||
from mock import patch
|
||||
from server import app, Server, ProxyState, HypercornLogHndl
|
||||
from inverter_base import InverterBase
|
||||
from gen3.talent import Talent
|
||||
|
||||
from test_inverter_base import FakeReader, FakeWriter
|
||||
|
||||
pytest_plugins = ('pytest_asyncio',)
|
||||
|
||||
@@ -108,20 +112,20 @@ class TestServerClass:
|
||||
assert logging.getLogger('hypercorn.access').level == logging.INFO
|
||||
assert logging.getLogger('hypercorn.error').level == logging.INFO
|
||||
|
||||
os.environ["LOG_LVL"] = "WARN"
|
||||
s.parse_args(['--log_backups', '3'])
|
||||
s.init_logging_system()
|
||||
assert s.log_backups == 3
|
||||
assert s.log_level == logging.WARNING
|
||||
assert logging.handlers.log_backups == 3
|
||||
assert logging.getLogger().level == s.log_level
|
||||
assert logging.getLogger('msg').level == s.log_level
|
||||
assert logging.getLogger('conn').level == s.log_level
|
||||
assert logging.getLogger('data').level == s.log_level
|
||||
assert logging.getLogger('tracer').level == s.log_level
|
||||
assert logging.getLogger('asyncio').level == s.log_level
|
||||
assert logging.getLogger('hypercorn.access').level == logging.INFO
|
||||
assert logging.getLogger('hypercorn.error').level == logging.INFO
|
||||
with patch.dict(os.environ, {'LOG_LVL': 'WARN'}):
|
||||
s.parse_args(['--log_backups', '3'])
|
||||
s.init_logging_system()
|
||||
assert s.log_backups == 3
|
||||
assert s.log_level == logging.WARNING
|
||||
assert logging.handlers.log_backups == 3
|
||||
assert logging.getLogger().level == s.log_level
|
||||
assert logging.getLogger('msg').level == s.log_level
|
||||
assert logging.getLogger('conn').level == s.log_level
|
||||
assert logging.getLogger('data').level == s.log_level
|
||||
assert logging.getLogger('tracer').level == s.log_level
|
||||
assert logging.getLogger('asyncio').level == s.log_level
|
||||
assert logging.getLogger('hypercorn.access').level == logging.INFO
|
||||
assert logging.getLogger('hypercorn.error').level == logging.INFO
|
||||
|
||||
def test_build_config_error(self, caplog):
|
||||
s = self.FakeServer()
|
||||
@@ -202,17 +206,81 @@ class TestApp:
|
||||
@pytest.mark.asyncio
|
||||
async def test_healthy(self):
|
||||
"""Test the healthy route."""
|
||||
reader = FakeReader()
|
||||
writer = FakeWriter()
|
||||
|
||||
ProxyState.set_up(False)
|
||||
client = app.test_client()
|
||||
response = await client.get('/-/healthy')
|
||||
assert response.status_code == 200
|
||||
result = await response.get_data()
|
||||
assert result == b"I'm fine"
|
||||
with InverterBase(reader, writer, 'tsun', Talent):
|
||||
ProxyState.set_up(False)
|
||||
client = app.test_client()
|
||||
response = await client.get('/-/healthy')
|
||||
assert response.status_code == 200
|
||||
result = await response.get_data()
|
||||
assert result == b"I'm fine"
|
||||
|
||||
ProxyState.set_up(True)
|
||||
response = await client.get('/-/healthy')
|
||||
assert response.status_code == 200
|
||||
result = await response.get_data()
|
||||
assert result == b"I'm fine"
|
||||
ProxyState.set_up(True)
|
||||
response = await client.get('/-/healthy')
|
||||
assert response.status_code == 200
|
||||
result = await response.get_data()
|
||||
assert result == b"I'm fine"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_unhealthy(self, monkeypatch, caplog):
|
||||
"""Test the healthy route."""
|
||||
def result_false(self):
|
||||
return False
|
||||
|
||||
LOGGER = logging.getLogger("mqtt")
|
||||
LOGGER.propagate = True
|
||||
LOGGER.setLevel(logging.INFO)
|
||||
|
||||
monkeypatch.setattr(InverterBase, "healthy", result_false)
|
||||
InverterBase._registry.clear()
|
||||
reader = FakeReader()
|
||||
writer = FakeWriter()
|
||||
|
||||
with caplog.at_level(logging.INFO) and InverterBase(reader, writer, 'tsun', Talent):
|
||||
ProxyState.set_up(False)
|
||||
client = app.test_client()
|
||||
response = await client.get('/-/healthy')
|
||||
assert response.status_code == 200
|
||||
result = await response.get_data()
|
||||
assert result == b"I'm fine"
|
||||
assert "" == caplog.text
|
||||
|
||||
ProxyState.set_up(True)
|
||||
response = await client.get('/-/healthy')
|
||||
assert response.status_code == 503
|
||||
result = await response.get_data()
|
||||
assert result == b"I have a problem"
|
||||
assert "" == caplog.text
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_healthy_exception(self, monkeypatch, caplog):
|
||||
"""Test the healthy route."""
|
||||
def result_except(self):
|
||||
raise ValueError
|
||||
|
||||
LOGGER = logging.getLogger("mqtt")
|
||||
LOGGER.propagate = True
|
||||
LOGGER.setLevel(logging.INFO)
|
||||
|
||||
monkeypatch.setattr(InverterBase, "healthy", result_except)
|
||||
InverterBase._registry.clear()
|
||||
reader = FakeReader()
|
||||
writer = FakeWriter()
|
||||
|
||||
with caplog.at_level(logging.INFO) and InverterBase(reader, writer, 'tsun', Talent):
|
||||
ProxyState.set_up(False)
|
||||
client = app.test_client()
|
||||
response = await client.get('/-/healthy')
|
||||
assert response.status_code == 200
|
||||
result = await response.get_data()
|
||||
assert result == b"I'm fine"
|
||||
assert "" == caplog.text
|
||||
|
||||
ProxyState.set_up(True)
|
||||
response = await client.get('/-/healthy')
|
||||
assert response.status_code == 200
|
||||
result = await response.get_data()
|
||||
assert result == b"I'm fine"
|
||||
assert "Exception:" in caplog.text
|
||||
|
||||
@@ -462,6 +462,39 @@ def inverter_ind_msg800(): # 0x4210 rated Power 800W
|
||||
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'
|
||||
@@ -676,6 +709,19 @@ def msg_modbus_rsp(): # 0x1510
|
||||
msg += b'\x15'
|
||||
return msg
|
||||
|
||||
@pytest.fixture
|
||||
def msg_modbus_rsp_mb_4(): # 0x1510, MODBUS Type:4
|
||||
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\x04\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\x9e\xa4'
|
||||
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'
|
||||
@@ -1435,6 +1481,7 @@ async def test_build_modell_600(my_loop, config_tsun_allow_all, inverter_ind_msg
|
||||
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
|
||||
@@ -1454,6 +1501,7 @@ async def test_build_modell_1600(my_loop, config_tsun_allow_all, inverter_ind_ms
|
||||
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()
|
||||
|
||||
@@ -1467,6 +1515,7 @@ async def test_build_modell_1800(my_loop, config_tsun_allow_all, inverter_ind_ms
|
||||
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()
|
||||
|
||||
@@ -1480,6 +1529,7 @@ async def test_build_modell_2000(my_loop, config_tsun_allow_all, inverter_ind_ms
|
||||
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()
|
||||
|
||||
@@ -1493,6 +1543,21 @@ async def test_build_modell_800(my_loop, config_tsun_allow_all, inverter_ind_msg
|
||||
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()
|
||||
|
||||
@@ -2189,6 +2254,61 @@ async def test_modbus_scaning(config_tsun_scan, heartbeat_ind_msg, heartbeat_rsp
|
||||
assert next(m.mb_timer.exp_count) == 3
|
||||
m.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_modbus_scaning_inv_rsp(config_tsun_scan, heartbeat_ind_msg, heartbeat_rsp_msg, msg_modbus_rsp_mb_4):
|
||||
_ = config_tsun_scan
|
||||
assert asyncio.get_running_loop()
|
||||
|
||||
m = MemoryStream(heartbeat_ind_msg, (0x15,0x56,0))
|
||||
m.append_msg(msg_modbus_rsp_mb_4)
|
||||
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 == 3
|
||||
|
||||
assert next(m.mb_timer.exp_count) == 2
|
||||
m.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_client_mode(my_loop, config_tsun_inv1, str_test_ip):
|
||||
_ = config_tsun_inv1
|
||||
|
||||
Reference in New Issue
Block a user