refactor close handling
This commit is contained in:
@@ -117,7 +117,3 @@ class AsyncIfc(ABC):
|
|||||||
@abstractmethod
|
@abstractmethod
|
||||||
def prot_set_update_header_cb(self, callback):
|
def prot_set_update_header_cb(self, callback):
|
||||||
pass # pragma: no cover
|
pass # pragma: no cover
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def prot_set_close_header_cb(self, callback):
|
|
||||||
pass # pragma: no cover
|
|
||||||
|
|||||||
@@ -35,14 +35,12 @@ class AsyncIfcImpl(AsyncIfc):
|
|||||||
self.timeout_cb = None
|
self.timeout_cb = None
|
||||||
self.init_new_client_conn_cb = None
|
self.init_new_client_conn_cb = None
|
||||||
self.update_header_cb = None
|
self.update_header_cb = None
|
||||||
self.close_cb = None
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.timeout_cb = None
|
self.timeout_cb = None
|
||||||
self.fwd_fifo.reg_trigger(None)
|
self.fwd_fifo.reg_trigger(None)
|
||||||
self.tx_fifo.reg_trigger(None)
|
self.tx_fifo.reg_trigger(None)
|
||||||
self.rx_fifo.reg_trigger(None)
|
self.rx_fifo.reg_trigger(None)
|
||||||
self.close_cb = None
|
|
||||||
|
|
||||||
def set_node_id(self, value: str):
|
def set_node_id(self, value: str):
|
||||||
self.node_id = value
|
self.node_id = value
|
||||||
@@ -126,9 +124,6 @@ class AsyncIfcImpl(AsyncIfc):
|
|||||||
def prot_set_update_header_cb(self, callback):
|
def prot_set_update_header_cb(self, callback):
|
||||||
self.update_header_cb = callback
|
self.update_header_cb = callback
|
||||||
|
|
||||||
def prot_set_close_header_cb(self, callback):
|
|
||||||
self.close_cb = callback
|
|
||||||
|
|
||||||
|
|
||||||
class StreamPtr():
|
class StreamPtr():
|
||||||
'''Descr StreamPtr'''
|
'''Descr StreamPtr'''
|
||||||
@@ -212,7 +207,6 @@ class AsyncStream(AsyncIfcImpl):
|
|||||||
f'connection timeout ({dead_conn_to}s) '
|
f'connection timeout ({dead_conn_to}s) '
|
||||||
f'for {self.l_addr}')
|
f'for {self.l_addr}')
|
||||||
await self.disc()
|
await self.disc()
|
||||||
self.close()
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
except OSError as error:
|
except OSError as error:
|
||||||
@@ -220,14 +214,12 @@ class AsyncStream(AsyncIfcImpl):
|
|||||||
f'{error} for l{self.l_addr} | '
|
f'{error} for l{self.l_addr} | '
|
||||||
f'r{self.r_addr}')
|
f'r{self.r_addr}')
|
||||||
await self.disc()
|
await self.disc()
|
||||||
self.close()
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
except RuntimeError as error:
|
except RuntimeError as error:
|
||||||
logger.info(f'[{self.node_id}:{self.conn_no}] '
|
logger.info(f'[{self.node_id}:{self.conn_no}] '
|
||||||
f'{error} for {self.l_addr}')
|
f'{error} for {self.l_addr}')
|
||||||
await self.disc()
|
await self.disc()
|
||||||
self.close()
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -251,10 +243,7 @@ class AsyncStream(AsyncIfcImpl):
|
|||||||
|
|
||||||
hint: must be called before releasing the connection instance
|
hint: must be called before releasing the connection instance
|
||||||
"""
|
"""
|
||||||
close_cb = self.close_cb
|
|
||||||
super().close()
|
super().close()
|
||||||
if close_cb:
|
|
||||||
close_cb()
|
|
||||||
self._reader.feed_eof() # abort awaited read
|
self._reader.feed_eof() # abort awaited read
|
||||||
if self._writer.is_closing():
|
if self._writer.is_closing():
|
||||||
return
|
return
|
||||||
@@ -304,21 +293,22 @@ class AsyncStream(AsyncIfcImpl):
|
|||||||
|
|
||||||
except OSError as error:
|
except OSError as error:
|
||||||
if self.remote.stream:
|
if self.remote.stream:
|
||||||
rmt = self.remote.stream
|
rmt = self.remote
|
||||||
self.remote.stream = None
|
logger.error(f'[{rmt.stream.node_id}:{rmt.stream.conn_no}] '
|
||||||
logger.error(f'[{rmt.node_id}:{rmt.conn_no}] Fwd: {error} for '
|
f'Fwd: {error} for '
|
||||||
f'l{rmt._ifc.l_addr} | r{rmt._ifc.r_addr}')
|
f'l{rmt.ifc.l_addr} | r{rmt.ifc.r_addr}')
|
||||||
await rmt._ifc.disc()
|
await rmt.ifc.disc()
|
||||||
rmt._ifc.close()
|
if rmt.ifc.close_cb:
|
||||||
|
rmt.ifc.close_cb()
|
||||||
|
|
||||||
except RuntimeError as error:
|
except RuntimeError as error:
|
||||||
if self.remote.stream:
|
if self.remote.stream:
|
||||||
rmt = self.remote.stream
|
rmt = self.remote
|
||||||
self.remote.stream = None
|
logger.info(f'[{rmt.stream.node_id}:{rmt.stream.conn_no}] '
|
||||||
logger.info(f'[{rmt.node_id}:{rmt.conn_no}] '
|
f'Fwd: {error} for {rmt.ifc.l_addr}')
|
||||||
f'Fwd: {error} for {rmt._ifc.l_addr}')
|
await rmt.ifc.disc()
|
||||||
await rmt._ifc.disc()
|
if rmt.ifc.close_cb:
|
||||||
rmt._ifc.close()
|
rmt.ifc.close_cb()
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
Infos.inc_counter('SW_Exception')
|
Infos.inc_counter('SW_Exception')
|
||||||
@@ -381,46 +371,22 @@ class AsyncStreamServer(AsyncStream):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def close(self) -> None:
|
|
||||||
"""close handler for a no waiting disconnect
|
|
||||||
|
|
||||||
hint: must be called before releasing the connection instance
|
|
||||||
"""
|
|
||||||
logging.info(
|
|
||||||
f'AsyncStreamServer.close() l{self.l_addr} | r{self.r_addr}')
|
|
||||||
self.async_create_remote = None
|
|
||||||
self.async_publ_mqtt = None
|
|
||||||
super().close()
|
|
||||||
|
|
||||||
|
|
||||||
class AsyncStreamClient(AsyncStream):
|
class AsyncStreamClient(AsyncStream):
|
||||||
def __init__(self, reader: StreamReader, writer: StreamWriter,
|
def __init__(self, reader: StreamReader, writer: StreamWriter,
|
||||||
rstream: "StreamPtr") -> None:
|
rstream: "StreamPtr", close_cb) -> None:
|
||||||
AsyncStream.__init__(self, reader, writer, rstream)
|
AsyncStream.__init__(self, reader, writer, rstream)
|
||||||
|
self.close_cb = close_cb
|
||||||
|
|
||||||
async def client_loop(self, _: str) -> None:
|
async def client_loop(self, _: str) -> None:
|
||||||
'''Loop for receiving messages from the TSUN cloud (client-side)'''
|
'''Loop for receiving messages from the TSUN cloud (client-side)'''
|
||||||
logging.info(f'AsynStream.client_loop{self} rem-> {self.remote}')
|
|
||||||
await self.loop()
|
await self.loop()
|
||||||
logger.info(f'[{self.node_id}:{self.conn_no}] '
|
logger.info(f'[{self.node_id}:{self.conn_no}] '
|
||||||
'Client loop stopped for'
|
'Client loop stopped for'
|
||||||
f' l{self.l_addr}')
|
f' l{self.l_addr}')
|
||||||
|
|
||||||
server_ifc = self.remote.ifc
|
if self.close_cb:
|
||||||
|
self.close_cb()
|
||||||
# if the client connection closes, we don't touch the server
|
|
||||||
# connection. Instead we erase the client connection stream,
|
|
||||||
# thus on the next received packet from the inverter, we can
|
|
||||||
# establish a new connection to the TSUN cloud
|
|
||||||
|
|
||||||
if server_ifc and server_ifc.remote.ifc == self:
|
|
||||||
# logging.debug(f'Client l{client_stream.l_addr} refs:'
|
|
||||||
# f' {gc.get_referrers(client_stream)}')
|
|
||||||
# than erase client connection
|
|
||||||
server_ifc.remote.stream = None # erases stream and ifc link
|
|
||||||
|
|
||||||
# erase backlink to inverter
|
|
||||||
self.remote.stream = None
|
|
||||||
|
|
||||||
async def _async_forward(self) -> None:
|
async def _async_forward(self) -> None:
|
||||||
"""forward handler transmits data over the remote connection"""
|
"""forward handler transmits data over the remote connection"""
|
||||||
|
|||||||
@@ -11,6 +11,3 @@ logger = logging.getLogger('conn')
|
|||||||
class ConnectionG3(Talent):
|
class ConnectionG3(Talent):
|
||||||
def __init__(self, addr, ifc, server_side, id_str=b'') -> None:
|
def __init__(self, addr, ifc, server_side, id_str=b'') -> None:
|
||||||
super().__init__(addr, server_side, ifc, id_str)
|
super().__init__(addr, server_side, ifc, id_str)
|
||||||
|
|
||||||
def close(self):
|
|
||||||
super().close()
|
|
||||||
|
|||||||
@@ -33,8 +33,3 @@ class InverterG3(InverterBase):
|
|||||||
async def async_create_remote(self) -> None:
|
async def async_create_remote(self) -> None:
|
||||||
await InverterBase.async_create_remote(
|
await InverterBase.async_create_remote(
|
||||||
self, 'tsun', ConnectionG3)
|
self, 'tsun', ConnectionG3)
|
||||||
|
|
||||||
def close(self) -> None:
|
|
||||||
logging.debug(f'InverterG3.close() {self.addr}')
|
|
||||||
self.local.stream.close()
|
|
||||||
# logging.info(f'Inverter refs: {gc.get_referrers(self)}')
|
|
||||||
|
|||||||
@@ -438,8 +438,8 @@ class Talent(Message):
|
|||||||
result = struct.unpack_from('!q', self.ifc.rx_peek(),
|
result = struct.unpack_from('!q', self.ifc.rx_peek(),
|
||||||
self.header_len)
|
self.header_len)
|
||||||
self.ts_offset = result[0]-ts
|
self.ts_offset = result[0]-ts
|
||||||
if self.remote.stream:
|
if self.ifc.remote.stream:
|
||||||
self.remote.stream.ts_offset = self.ts_offset
|
self.ifc.remote.stream.ts_offset = self.ts_offset
|
||||||
logger.debug(f'tsun-time: {int(result[0]):08x}'
|
logger.debug(f'tsun-time: {int(result[0]):08x}'
|
||||||
f' proxy-time: {ts:08x}'
|
f' proxy-time: {ts:08x}'
|
||||||
f' offset: {self.ts_offset}')
|
f' offset: {self.ts_offset}')
|
||||||
@@ -597,9 +597,8 @@ class Talent(Message):
|
|||||||
self.header_len+self.data_len]
|
self.header_len+self.data_len]
|
||||||
|
|
||||||
if self.ctrl.is_req():
|
if self.ctrl.is_req():
|
||||||
if self.remote.stream.mb.recv_req(data[hdr_len:],
|
rstream = self.ifc.remote.stream
|
||||||
self.remote.stream.
|
if rstream.mb.recv_req(data[hdr_len:], rstream.msg_forward):
|
||||||
msg_forward):
|
|
||||||
self.inc_counter('Modbus_Command')
|
self.inc_counter('Modbus_Command')
|
||||||
else:
|
else:
|
||||||
self.inc_counter('Invalid_Msg_Format')
|
self.inc_counter('Invalid_Msg_Format')
|
||||||
|
|||||||
@@ -12,7 +12,3 @@ class ConnectionG3P(SolarmanV5):
|
|||||||
def __init__(self, addr, ifc, server_side,
|
def __init__(self, addr, ifc, server_side,
|
||||||
client_mode: bool = False) -> None:
|
client_mode: bool = False) -> None:
|
||||||
super().__init__(addr, server_side, client_mode, ifc)
|
super().__init__(addr, server_side, client_mode, ifc)
|
||||||
|
|
||||||
def close(self):
|
|
||||||
super().close()
|
|
||||||
# logger.info(f'AsyncStream refs: {gc.get_referrers(self)}')
|
|
||||||
|
|||||||
@@ -34,8 +34,3 @@ class InverterG3P(InverterBase):
|
|||||||
async def async_create_remote(self) -> None:
|
async def async_create_remote(self) -> None:
|
||||||
await InverterBase.async_create_remote(
|
await InverterBase.async_create_remote(
|
||||||
self, 'solarman', ConnectionG3P)
|
self, 'solarman', ConnectionG3P)
|
||||||
|
|
||||||
def close(self) -> None:
|
|
||||||
logging.debug(f'InverterG3P.close() {self.addr}')
|
|
||||||
self.local.stream.close()
|
|
||||||
# logger.debug (f'Inverter refs: {gc.get_referrers(self)}')
|
|
||||||
|
|||||||
@@ -603,9 +603,9 @@ class SolarmanV5(Message):
|
|||||||
self.forward_at_cmd_resp = True
|
self.forward_at_cmd_resp = True
|
||||||
|
|
||||||
elif ftype == self.MB_RTU_CMD:
|
elif ftype == self.MB_RTU_CMD:
|
||||||
if self.remote.stream.mb.recv_req(data[15:],
|
rstream = self.ifc.remote.stream
|
||||||
self.remote.stream.
|
if rstream.mb.recv_req(data[15:],
|
||||||
__forward_msg):
|
rstream.__forward_msg):
|
||||||
self.inc_counter('Modbus_Command')
|
self.inc_counter('Modbus_Command')
|
||||||
else:
|
else:
|
||||||
logger.error('Invalid Modbus Msg')
|
logger.error('Invalid Modbus Msg')
|
||||||
|
|||||||
@@ -22,6 +22,32 @@ class InverterBase(Inverter):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.__ha_restarts = -1
|
self.__ha_restarts = -1
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc, tb) -> None:
|
||||||
|
logging.info(f'Inverter.__exit__() {self.addr}')
|
||||||
|
self.__del_remote()
|
||||||
|
if self.local.stream:
|
||||||
|
self.local.stream.close()
|
||||||
|
self.local.stream = None
|
||||||
|
|
||||||
|
if self.local.ifc:
|
||||||
|
self.local.ifc.close()
|
||||||
|
self.local.ifc = None
|
||||||
|
|
||||||
|
def __del__(self) -> None:
|
||||||
|
logging.info(f'Inverter.__del__() {self.addr}')
|
||||||
|
|
||||||
|
def __del_remote(self):
|
||||||
|
if self.remote.stream:
|
||||||
|
self.remote.stream.close()
|
||||||
|
self.remote.stream = None
|
||||||
|
|
||||||
|
if self.remote.ifc:
|
||||||
|
self.remote.ifc.close()
|
||||||
|
self.remote.ifc = None
|
||||||
|
|
||||||
async def async_create_remote(self, inv_prot: str, conn_class) -> None:
|
async def async_create_remote(self, inv_prot: str, conn_class) -> None:
|
||||||
'''Establish a client connection to the TSUN cloud'''
|
'''Establish a client connection to the TSUN cloud'''
|
||||||
tsun = Config.get(inv_prot)
|
tsun = Config.get(inv_prot)
|
||||||
@@ -36,8 +62,8 @@ class InverterBase(Inverter):
|
|||||||
logging.info(f'[{stream.node_id}] Connect to {addr}')
|
logging.info(f'[{stream.node_id}] Connect to {addr}')
|
||||||
connect = asyncio.open_connection(host, port)
|
connect = asyncio.open_connection(host, port)
|
||||||
reader, writer = await connect
|
reader, writer = await connect
|
||||||
ifc = AsyncStreamClient(reader, writer,
|
ifc = AsyncStreamClient(
|
||||||
self.local)
|
reader, writer, self.local, self.__del_remote)
|
||||||
|
|
||||||
if hasattr(stream, 'id_str'):
|
if hasattr(stream, 'id_str'):
|
||||||
self.remote.stream = conn_class(
|
self.remote.stream = conn_class(
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class ModbusConn():
|
|||||||
reader, writer = await connection
|
reader, writer = await connection
|
||||||
self.inverter = InverterG3P(reader, writer, self.addr,
|
self.inverter = InverterG3P(reader, writer, self.addr,
|
||||||
client_mode=True)
|
client_mode=True)
|
||||||
|
self.inverter.__enter__()
|
||||||
stream = self.inverter.local.stream
|
stream = self.inverter.local.stream
|
||||||
logging.info(f'[{stream.node_id}:{stream.conn_no}] '
|
logging.info(f'[{stream.node_id}:{stream.conn_no}] '
|
||||||
f'Connected to {self.addr}')
|
f'Connected to {self.addr}')
|
||||||
@@ -37,7 +38,7 @@ class ModbusConn():
|
|||||||
async def __aexit__(self, exc_type, exc, tb):
|
async def __aexit__(self, exc_type, exc, tb):
|
||||||
Infos.dec_counter('Inverter_Cnt')
|
Infos.dec_counter('Inverter_Cnt')
|
||||||
await self.inverter.local.ifc.publish_outstanding_mqtt()
|
await self.inverter.local.ifc.publish_outstanding_mqtt()
|
||||||
self.inverter.close()
|
self.inverter.__exit__(exc_type, exc, tb)
|
||||||
|
|
||||||
|
|
||||||
class ModbusTcp():
|
class ModbusTcp():
|
||||||
|
|||||||
@@ -74,7 +74,8 @@ async def handle_client(reader: StreamReader, writer: StreamWriter, inv_class):
|
|||||||
'''Handles a new incoming connection and starts an async loop'''
|
'''Handles a new incoming connection and starts an async loop'''
|
||||||
|
|
||||||
addr = writer.get_extra_info('peername')
|
addr = writer.get_extra_info('peername')
|
||||||
await inv_class(reader, writer, addr).local.ifc.server_loop()
|
with inv_class(reader, writer, addr) as inv:
|
||||||
|
await inv.local.ifc.server_loop()
|
||||||
|
|
||||||
|
|
||||||
async def handle_shutdown(web_task):
|
async def handle_shutdown(web_task):
|
||||||
|
|||||||
@@ -47,11 +47,6 @@ def patch_conn_init():
|
|||||||
with patch.object(ConnectionG3, '__init__', return_value= None) as conn:
|
with patch.object(ConnectionG3, '__init__', return_value= None) as conn:
|
||||||
yield conn
|
yield conn
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def patch_conn_close():
|
|
||||||
with patch.object(ConnectionG3, 'close') as conn:
|
|
||||||
yield conn
|
|
||||||
|
|
||||||
class FakeReader():
|
class FakeReader():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.on_recv = asyncio.Event()
|
self.on_recv = asyncio.Event()
|
||||||
@@ -104,131 +99,102 @@ def patch_open_connection():
|
|||||||
yield conn
|
yield conn
|
||||||
|
|
||||||
|
|
||||||
def test_method_calls(patch_conn_close):
|
def test_method_calls():
|
||||||
spy2 = patch_conn_close
|
|
||||||
reader = FakeReader()
|
reader = FakeReader()
|
||||||
writer = FakeWriter()
|
writer = FakeWriter()
|
||||||
addr = ('proxy.local', 10000)
|
addr = ('proxy.local', 10000)
|
||||||
inverter = InverterG3(reader, writer, addr)
|
with InverterG3(reader, writer, addr) as inverter:
|
||||||
assert inverter.local.stream
|
assert inverter.local.stream
|
||||||
assert inverter.local.ifc
|
assert inverter.local.ifc
|
||||||
|
|
||||||
inverter.close()
|
|
||||||
spy2.assert_called_once()
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_remote_conn(config_conn, patch_open_connection, patch_conn_close):
|
async def test_remote_conn(config_conn, patch_open_connection):
|
||||||
_ = config_conn
|
_ = config_conn
|
||||||
_ = patch_open_connection
|
_ = patch_open_connection
|
||||||
assert asyncio.get_running_loop()
|
assert asyncio.get_running_loop()
|
||||||
|
|
||||||
spy1 = patch_conn_close
|
with InverterG3(FakeReader(), FakeWriter(), ('proxy.local', 10000)) as inverter:
|
||||||
|
await inverter.async_create_remote()
|
||||||
inverter = InverterG3(FakeReader(), FakeWriter(), ('proxy.local', 10000))
|
await asyncio.sleep(0)
|
||||||
|
assert inverter.remote.stream
|
||||||
await inverter.async_create_remote()
|
|
||||||
await asyncio.sleep(0)
|
|
||||||
assert inverter.remote.stream
|
|
||||||
inverter.close()
|
|
||||||
spy1.assert_called_once()
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_remote_except(config_conn, patch_open_connection, patch_conn_close):
|
async def test_remote_except(config_conn, patch_open_connection):
|
||||||
_ = config_conn
|
_ = config_conn
|
||||||
_ = patch_open_connection
|
_ = patch_open_connection
|
||||||
assert asyncio.get_running_loop()
|
assert asyncio.get_running_loop()
|
||||||
|
|
||||||
spy1 = patch_conn_close
|
|
||||||
|
|
||||||
global test
|
global test
|
||||||
test = TestType.RD_TEST_TIMEOUT
|
test = TestType.RD_TEST_TIMEOUT
|
||||||
|
|
||||||
inverter = InverterG3(FakeReader(), FakeWriter(), ('proxy.local', 10000))
|
with InverterG3(FakeReader(), FakeWriter(), ('proxy.local', 10000)) as inverter:
|
||||||
|
await inverter.async_create_remote()
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
assert inverter.remote.stream==None
|
||||||
|
|
||||||
await inverter.async_create_remote()
|
test = TestType.RD_TEST_EXCEPT
|
||||||
await asyncio.sleep(0)
|
await inverter.async_create_remote()
|
||||||
assert inverter.remote.stream==None
|
await asyncio.sleep(0)
|
||||||
|
assert inverter.remote.stream==None
|
||||||
test = TestType.RD_TEST_EXCEPT
|
|
||||||
await inverter.async_create_remote()
|
|
||||||
await asyncio.sleep(0)
|
|
||||||
assert inverter.remote.stream==None
|
|
||||||
inverter.close()
|
|
||||||
spy1.assert_called_once()
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_mqtt_publish(config_conn, patch_open_connection, patch_conn_close):
|
async def test_mqtt_publish(config_conn, patch_open_connection):
|
||||||
_ = config_conn
|
_ = config_conn
|
||||||
_ = patch_open_connection
|
_ = patch_open_connection
|
||||||
assert asyncio.get_running_loop()
|
assert asyncio.get_running_loop()
|
||||||
|
|
||||||
spy1 = patch_conn_close
|
|
||||||
|
|
||||||
Inverter.class_init()
|
Inverter.class_init()
|
||||||
|
|
||||||
inverter = InverterG3(FakeReader(), FakeWriter(), ('proxy.local', 10000))
|
with InverterG3(FakeReader(), FakeWriter(), ('proxy.local', 10000)) as inverter:
|
||||||
stream = inverter.local.stream
|
stream = inverter.local.stream
|
||||||
await inverter.async_publ_mqtt() # check call with invalid unique_id
|
await inverter.async_publ_mqtt() # check call with invalid unique_id
|
||||||
stream._Talent__set_serial_no(serial_no= "123344")
|
stream._Talent__set_serial_no(serial_no= "123344")
|
||||||
|
|
||||||
stream.new_data['inverter'] = True
|
|
||||||
stream.db.db['inverter'] = {}
|
|
||||||
await inverter.async_publ_mqtt()
|
|
||||||
assert stream.new_data['inverter'] == False
|
|
||||||
|
|
||||||
stream.new_data['env'] = True
|
stream.new_data['inverter'] = True
|
||||||
stream.db.db['env'] = {}
|
stream.db.db['inverter'] = {}
|
||||||
await inverter.async_publ_mqtt()
|
await inverter.async_publ_mqtt()
|
||||||
assert stream.new_data['env'] == False
|
assert stream.new_data['inverter'] == False
|
||||||
|
|
||||||
Infos.new_stat_data['proxy'] = True
|
stream.new_data['env'] = True
|
||||||
await inverter.async_publ_mqtt()
|
stream.db.db['env'] = {}
|
||||||
assert Infos.new_stat_data['proxy'] == False
|
await inverter.async_publ_mqtt()
|
||||||
|
assert stream.new_data['env'] == False
|
||||||
|
|
||||||
inverter.close()
|
Infos.new_stat_data['proxy'] = True
|
||||||
spy1.assert_called_once()
|
await inverter.async_publ_mqtt()
|
||||||
|
assert Infos.new_stat_data['proxy'] == False
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_mqtt_err(config_conn, patch_open_connection, patch_mqtt_err, patch_conn_close):
|
async def test_mqtt_err(config_conn, patch_open_connection, patch_mqtt_err):
|
||||||
_ = config_conn
|
_ = config_conn
|
||||||
_ = patch_open_connection
|
_ = patch_open_connection
|
||||||
_ = patch_mqtt_err
|
_ = patch_mqtt_err
|
||||||
assert asyncio.get_running_loop()
|
assert asyncio.get_running_loop()
|
||||||
|
|
||||||
spy1 = patch_conn_close
|
|
||||||
|
|
||||||
Inverter.class_init()
|
Inverter.class_init()
|
||||||
|
|
||||||
inverter = InverterG3(FakeReader(), FakeWriter(), ('proxy.local', 10000))
|
with InverterG3(FakeReader(), FakeWriter(), ('proxy.local', 10000)) as inverter:
|
||||||
stream = inverter.local.stream
|
stream = inverter.local.stream
|
||||||
stream._Talent__set_serial_no(serial_no= "123344")
|
stream._Talent__set_serial_no(serial_no= "123344")
|
||||||
stream.new_data['inverter'] = True
|
stream.new_data['inverter'] = True
|
||||||
stream.db.db['inverter'] = {}
|
stream.db.db['inverter'] = {}
|
||||||
await inverter.async_publ_mqtt()
|
await inverter.async_publ_mqtt()
|
||||||
assert stream.new_data['inverter'] == True
|
assert stream.new_data['inverter'] == True
|
||||||
|
|
||||||
inverter.close()
|
|
||||||
spy1.assert_called_once()
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_mqtt_except(config_conn, patch_open_connection, patch_mqtt_except, patch_conn_close):
|
async def test_mqtt_except(config_conn, patch_open_connection, patch_mqtt_except):
|
||||||
_ = config_conn
|
_ = config_conn
|
||||||
_ = patch_open_connection
|
_ = patch_open_connection
|
||||||
_ = patch_mqtt_except
|
_ = patch_mqtt_except
|
||||||
assert asyncio.get_running_loop()
|
assert asyncio.get_running_loop()
|
||||||
|
|
||||||
spy1 = patch_conn_close
|
|
||||||
|
|
||||||
Inverter.class_init()
|
Inverter.class_init()
|
||||||
|
|
||||||
inverter = InverterG3(FakeReader(), FakeWriter(), ('proxy.local', 10000))
|
with InverterG3(FakeReader(), FakeWriter(), ('proxy.local', 10000)) as inverter:
|
||||||
stream = inverter.local.stream
|
stream = inverter.local.stream
|
||||||
stream._Talent__set_serial_no(serial_no= "123344")
|
stream._Talent__set_serial_no(serial_no= "123344")
|
||||||
|
|
||||||
stream.new_data['inverter'] = True
|
|
||||||
stream.db.db['inverter'] = {}
|
|
||||||
await inverter.async_publ_mqtt()
|
|
||||||
assert stream.new_data['inverter'] == True
|
|
||||||
|
|
||||||
inverter.close()
|
stream.new_data['inverter'] = True
|
||||||
spy1.assert_called_once()
|
stream.db.db['inverter'] = {}
|
||||||
|
await inverter.async_publ_mqtt()
|
||||||
|
assert stream.new_data['inverter'] == True
|
||||||
|
|||||||
@@ -48,11 +48,6 @@ def patch_conn_init():
|
|||||||
with patch.object(ConnectionG3P, '__init__', return_value= None) as conn:
|
with patch.object(ConnectionG3P, '__init__', return_value= None) as conn:
|
||||||
yield conn
|
yield conn
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def patch_conn_close():
|
|
||||||
with patch.object(ConnectionG3P, 'close') as conn:
|
|
||||||
yield conn
|
|
||||||
|
|
||||||
class FakeReader():
|
class FakeReader():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.on_recv = asyncio.Event()
|
self.on_recv = asyncio.Event()
|
||||||
@@ -104,132 +99,102 @@ def patch_open_connection():
|
|||||||
with patch.object(asyncio, 'open_connection', new_open) as conn:
|
with patch.object(asyncio, 'open_connection', new_open) as conn:
|
||||||
yield conn
|
yield conn
|
||||||
|
|
||||||
|
def test_method_calls():
|
||||||
def test_method_calls(patch_conn_close):
|
|
||||||
spy2 = patch_conn_close
|
|
||||||
reader = FakeReader()
|
reader = FakeReader()
|
||||||
writer = FakeWriter()
|
writer = FakeWriter()
|
||||||
addr = ('proxy.local', 10000)
|
addr = ('proxy.local', 10000)
|
||||||
inverter = InverterG3P(reader, writer, addr, client_mode=False)
|
with InverterG3P(reader, writer, addr, client_mode=False) as inverter:
|
||||||
assert inverter.local.stream
|
assert inverter.local.stream
|
||||||
assert inverter.local.ifc
|
assert inverter.local.ifc
|
||||||
|
|
||||||
inverter.close()
|
|
||||||
spy2.assert_called_once()
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_remote_conn(config_conn, patch_open_connection, patch_conn_close):
|
async def test_remote_conn(config_conn, patch_open_connection):
|
||||||
_ = config_conn
|
_ = config_conn
|
||||||
_ = patch_open_connection
|
_ = patch_open_connection
|
||||||
assert asyncio.get_running_loop()
|
assert asyncio.get_running_loop()
|
||||||
|
|
||||||
spy1 = patch_conn_close
|
with InverterG3P(FakeReader(), FakeWriter(), ('proxy.local', 10000), client_mode=False) as inverter:
|
||||||
|
await inverter.async_create_remote()
|
||||||
inverter = InverterG3P(FakeReader(), FakeWriter(), ('proxy.local', 10000), client_mode=False)
|
await asyncio.sleep(0)
|
||||||
|
assert inverter.remote.stream
|
||||||
await inverter.async_create_remote()
|
|
||||||
await asyncio.sleep(0)
|
|
||||||
assert inverter.remote.stream
|
|
||||||
inverter.close()
|
|
||||||
spy1.assert_called_once()
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_remote_except(config_conn, patch_open_connection, patch_conn_close):
|
async def test_remote_except(config_conn, patch_open_connection):
|
||||||
_ = config_conn
|
_ = config_conn
|
||||||
_ = patch_open_connection
|
_ = patch_open_connection
|
||||||
assert asyncio.get_running_loop()
|
assert asyncio.get_running_loop()
|
||||||
|
|
||||||
spy1 = patch_conn_close
|
|
||||||
|
|
||||||
global test
|
global test
|
||||||
test = TestType.RD_TEST_TIMEOUT
|
test = TestType.RD_TEST_TIMEOUT
|
||||||
|
|
||||||
inverter = InverterG3P(FakeReader(), FakeWriter(), ('proxy.local', 10000), client_mode=False)
|
with InverterG3P(FakeReader(), FakeWriter(), ('proxy.local', 10000), client_mode=False) as inverter:
|
||||||
|
await inverter.async_create_remote()
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
assert inverter.remote.stream==None
|
||||||
|
|
||||||
await inverter.async_create_remote()
|
test = TestType.RD_TEST_EXCEPT
|
||||||
await asyncio.sleep(0)
|
await inverter.async_create_remote()
|
||||||
assert inverter.remote.stream==None
|
await asyncio.sleep(0)
|
||||||
|
assert inverter.remote.stream==None
|
||||||
test = TestType.RD_TEST_EXCEPT
|
|
||||||
await inverter.async_create_remote()
|
|
||||||
await asyncio.sleep(0)
|
|
||||||
assert inverter.remote.stream==None
|
|
||||||
inverter.close()
|
|
||||||
spy1.assert_called_once()
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_mqtt_publish(config_conn, patch_open_connection, patch_conn_close):
|
async def test_mqtt_publish(config_conn, patch_open_connection):
|
||||||
_ = config_conn
|
_ = config_conn
|
||||||
_ = patch_open_connection
|
_ = patch_open_connection
|
||||||
assert asyncio.get_running_loop()
|
assert asyncio.get_running_loop()
|
||||||
|
|
||||||
spy1 = patch_conn_close
|
|
||||||
|
|
||||||
Inverter.class_init()
|
Inverter.class_init()
|
||||||
|
|
||||||
inverter = InverterG3P(FakeReader(), FakeWriter(), ('proxy.local', 10000), client_mode=False)
|
with InverterG3P(FakeReader(), FakeWriter(), ('proxy.local', 10000), client_mode=False) as inverter:
|
||||||
stream = inverter.local.stream
|
stream = inverter.local.stream
|
||||||
await inverter.async_publ_mqtt() # check call with invalid unique_id
|
await inverter.async_publ_mqtt() # check call with invalid unique_id
|
||||||
stream._SolarmanV5__set_serial_no(snr= 123344)
|
stream._SolarmanV5__set_serial_no(snr= 123344)
|
||||||
|
|
||||||
stream.new_data['inverter'] = True
|
|
||||||
stream.db.db['inverter'] = {}
|
|
||||||
await inverter.async_publ_mqtt()
|
|
||||||
assert stream.new_data['inverter'] == False
|
|
||||||
|
|
||||||
stream.new_data['env'] = True
|
stream.new_data['inverter'] = True
|
||||||
stream.db.db['env'] = {}
|
stream.db.db['inverter'] = {}
|
||||||
await inverter.async_publ_mqtt()
|
await inverter.async_publ_mqtt()
|
||||||
assert stream.new_data['env'] == False
|
assert stream.new_data['inverter'] == False
|
||||||
|
|
||||||
Infos.new_stat_data['proxy'] = True
|
stream.new_data['env'] = True
|
||||||
await inverter.async_publ_mqtt()
|
stream.db.db['env'] = {}
|
||||||
assert Infos.new_stat_data['proxy'] == False
|
await inverter.async_publ_mqtt()
|
||||||
|
assert stream.new_data['env'] == False
|
||||||
|
|
||||||
inverter.close()
|
Infos.new_stat_data['proxy'] = True
|
||||||
spy1.assert_called_once()
|
await inverter.async_publ_mqtt()
|
||||||
|
assert Infos.new_stat_data['proxy'] == False
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_mqtt_err(config_conn, patch_open_connection, patch_mqtt_err, patch_conn_close):
|
async def test_mqtt_err(config_conn, patch_open_connection, patch_mqtt_err):
|
||||||
_ = config_conn
|
_ = config_conn
|
||||||
_ = patch_open_connection
|
_ = patch_open_connection
|
||||||
_ = patch_mqtt_err
|
_ = patch_mqtt_err
|
||||||
assert asyncio.get_running_loop()
|
assert asyncio.get_running_loop()
|
||||||
|
|
||||||
spy1 = patch_conn_close
|
|
||||||
|
|
||||||
Inverter.class_init()
|
Inverter.class_init()
|
||||||
|
|
||||||
inverter = InverterG3P(FakeReader(), FakeWriter(), ('proxy.local', 10000), client_mode=False)
|
with InverterG3P(FakeReader(), FakeWriter(), ('proxy.local', 10000), client_mode=False) as inverter:
|
||||||
stream = inverter.local.stream
|
stream = inverter.local.stream
|
||||||
stream._SolarmanV5__set_serial_no(snr= 123344)
|
stream._SolarmanV5__set_serial_no(snr= 123344)
|
||||||
stream.new_data['inverter'] = True
|
stream.new_data['inverter'] = True
|
||||||
stream.db.db['inverter'] = {}
|
stream.db.db['inverter'] = {}
|
||||||
await inverter.async_publ_mqtt()
|
await inverter.async_publ_mqtt()
|
||||||
assert stream.new_data['inverter'] == True
|
assert stream.new_data['inverter'] == True
|
||||||
|
|
||||||
inverter.close()
|
|
||||||
spy1.assert_called_once()
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_mqtt_except(config_conn, patch_open_connection, patch_mqtt_except, patch_conn_close):
|
async def test_mqtt_except(config_conn, patch_open_connection, patch_mqtt_except):
|
||||||
_ = config_conn
|
_ = config_conn
|
||||||
_ = patch_open_connection
|
_ = patch_open_connection
|
||||||
_ = patch_mqtt_except
|
_ = patch_mqtt_except
|
||||||
assert asyncio.get_running_loop()
|
assert asyncio.get_running_loop()
|
||||||
|
|
||||||
spy1 = patch_conn_close
|
|
||||||
|
|
||||||
Inverter.class_init()
|
Inverter.class_init()
|
||||||
|
|
||||||
inverter = InverterG3P(FakeReader(), FakeWriter(), ('proxy.local', 10000), client_mode=False)
|
with InverterG3P(FakeReader(), FakeWriter(), ('proxy.local', 10000), client_mode=False) as inverter:
|
||||||
stream = inverter.local.stream
|
stream = inverter.local.stream
|
||||||
stream._SolarmanV5__set_serial_no(snr= 123344)
|
stream._SolarmanV5__set_serial_no(snr= 123344)
|
||||||
|
|
||||||
stream.new_data['inverter'] = True
|
|
||||||
stream.db.db['inverter'] = {}
|
|
||||||
await inverter.async_publ_mqtt()
|
|
||||||
assert stream.new_data['inverter'] == True
|
|
||||||
|
|
||||||
inverter.close()
|
stream.new_data['inverter'] = True
|
||||||
spy1.assert_called_once()
|
stream.db.db['inverter'] = {}
|
||||||
|
await inverter.async_publ_mqtt()
|
||||||
|
assert stream.new_data['inverter'] == True
|
||||||
|
|||||||
@@ -32,13 +32,17 @@ class Mqtt():
|
|||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
|
|
||||||
|
class FakeIfc(AsyncIfcImpl):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.remote = StreamPtr(None)
|
||||||
|
|
||||||
class MemoryStream(SolarmanV5):
|
class MemoryStream(SolarmanV5):
|
||||||
def __init__(self, msg, chunks = (0,), server_side: bool = True):
|
def __init__(self, msg, chunks = (0,), server_side: bool = True):
|
||||||
_ifc = AsyncIfcImpl()
|
_ifc = FakeIfc()
|
||||||
super().__init__(('test.local', 1234), server_side, client_mode=False, ifc=_ifc)
|
super().__init__(('test.local', 1234), server_side, client_mode=False, ifc=_ifc)
|
||||||
if server_side:
|
if server_side:
|
||||||
self.mb.timeout = 0.4 # overwrite for faster testing
|
self.mb.timeout = 0.4 # overwrite for faster testing
|
||||||
self.remote = StreamPtr(None)
|
|
||||||
self.mb_first_timeout = 0.5
|
self.mb_first_timeout = 0.5
|
||||||
self.mb_timeout = 0.5
|
self.mb_timeout = 0.5
|
||||||
self.sent_pdu = b''
|
self.sent_pdu = b''
|
||||||
@@ -101,8 +105,8 @@ class MemoryStream(SolarmanV5):
|
|||||||
|
|
||||||
def createClientStream(self, msg, chunks = (0,)):
|
def createClientStream(self, msg, chunks = (0,)):
|
||||||
c = MemoryStream(msg, chunks, False)
|
c = MemoryStream(msg, chunks, False)
|
||||||
self.remote.stream = c
|
self.ifc.remote.stream = c
|
||||||
c. remote.stream = self
|
c.ifc.remote.stream = self
|
||||||
return c
|
return c
|
||||||
|
|
||||||
def _SolarmanV5__flush_recv_msg(self) -> None:
|
def _SolarmanV5__flush_recv_msg(self) -> None:
|
||||||
|
|||||||
@@ -16,14 +16,17 @@ Infos.static_init()
|
|||||||
|
|
||||||
tracer = logging.getLogger('tracer')
|
tracer = logging.getLogger('tracer')
|
||||||
|
|
||||||
|
class FakeIfc(AsyncIfcImpl):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.remote = StreamPtr(None)
|
||||||
|
|
||||||
class MemoryStream(Talent):
|
class MemoryStream(Talent):
|
||||||
def __init__(self, msg, chunks = (0,), server_side: bool = True):
|
def __init__(self, msg, chunks = (0,), server_side: bool = True):
|
||||||
self.ifc = AsyncIfcImpl()
|
self.ifc = FakeIfc()
|
||||||
super().__init__(('test.local', 1234), server_side, self.ifc)
|
super().__init__(('test.local', 1234), server_side, self.ifc)
|
||||||
if server_side:
|
if server_side:
|
||||||
self.mb.timeout = 0.4 # overwrite for faster testing
|
self.mb.timeout = 0.4 # overwrite for faster testing
|
||||||
self.remote = StreamPtr(None)
|
|
||||||
self.mb_first_timeout = 0.5
|
self.mb_first_timeout = 0.5
|
||||||
self.mb_timeout = 0.5
|
self.mb_timeout = 0.5
|
||||||
self.sent_pdu = b''
|
self.sent_pdu = b''
|
||||||
@@ -37,7 +40,6 @@ class MemoryStream(Talent):
|
|||||||
self.addr = 'Test: SrvSide'
|
self.addr = 'Test: SrvSide'
|
||||||
self.send_msg_ofs = 0
|
self.send_msg_ofs = 0
|
||||||
self.msg_recvd = []
|
self.msg_recvd = []
|
||||||
self.remote.stream = None
|
|
||||||
|
|
||||||
def write_cb(self):
|
def write_cb(self):
|
||||||
self.sent_pdu = self.ifc.tx_fifo.get()
|
self.sent_pdu = self.ifc.tx_fifo.get()
|
||||||
@@ -73,8 +75,8 @@ class MemoryStream(Talent):
|
|||||||
|
|
||||||
def createClientStream(self, msg, chunks = (0,)):
|
def createClientStream(self, msg, chunks = (0,)):
|
||||||
c = MemoryStream(msg, chunks, False)
|
c = MemoryStream(msg, chunks, False)
|
||||||
self.remote.stream = c
|
self.ifc.remote.stream = c
|
||||||
c. remote.stream = self
|
c.ifc.remote.stream = self
|
||||||
return c
|
return c
|
||||||
|
|
||||||
def _Talent__flush_recv_msg(self) -> None:
|
def _Talent__flush_recv_msg(self) -> None:
|
||||||
@@ -1059,7 +1061,7 @@ def test_msg_time_resp(config_tsun_inv1, msg_time_rsp):
|
|||||||
m = MemoryStream(msg_time_rsp, (0,), False)
|
m = MemoryStream(msg_time_rsp, (0,), False)
|
||||||
s = MemoryStream(b'', (0,), True)
|
s = MemoryStream(b'', (0,), True)
|
||||||
assert s.ts_offset==0
|
assert s.ts_offset==0
|
||||||
m.remote.stream = s
|
m.ifc.remote.stream = s
|
||||||
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
m.db.stat['proxy']['Unknown_Ctrl'] = 0
|
||||||
m.read() # read complete msg, and dispatch msg
|
m.read() # read complete msg, and dispatch msg
|
||||||
assert not m.header_valid # must be invalid, since msg was handled and buffer flushed
|
assert not m.header_valid # must be invalid, since msg was handled and buffer flushed
|
||||||
@@ -1075,7 +1077,7 @@ def test_msg_time_resp(config_tsun_inv1, msg_time_rsp):
|
|||||||
assert m.ifc.fwd_fifo.get()==b''
|
assert m.ifc.fwd_fifo.get()==b''
|
||||||
assert m.ifc.tx_fifo.get()==b''
|
assert m.ifc.tx_fifo.get()==b''
|
||||||
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
assert m.db.stat['proxy']['Unknown_Ctrl'] == 0
|
||||||
m.remote.stream = None
|
m.ifc.remote.stream = None
|
||||||
s.close()
|
s.close()
|
||||||
m.close()
|
m.close()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user