diff --git a/CHANGELOG.md b/CHANGELOG.md index 000d2aa..f64b4ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- Fixed [#33](https://github.com/s-allius/tsun-gen3-proxy/issues/33) - Fixed detection of the connected inputs/MPPTs - Preparation for overwriting received data - home assistant: diff --git a/app/src/messages.py b/app/src/messages.py index 5fa8ce4..82e702f 100644 --- a/app/src/messages.py +++ b/app/src/messages.py @@ -83,6 +83,7 @@ class Message(metaclass=IterRegistry): self.unique_id = 0 self.node_id = '' self.sug_area = '' + self.await_conn_resp_cnt = 0 self.id_str = id_str self.contact_name = b'' self.contact_mail = b'' @@ -182,6 +183,7 @@ class Message(metaclass=IterRegistry): def _init_new_client_conn(self, contact_name, contact_mail) -> None: logger.info(f'name: {contact_name} mail: {contact_mail}') self.msg_id = 0 + self.await_conn_resp_cnt += 1 self.__build_header(0x91) self._send_buffer += struct.pack(f'!{len(contact_name)+1}p' f'{len(contact_mail)+1}p', @@ -282,23 +284,27 @@ class Message(metaclass=IterRegistry): ''' def msg_contact_info(self): if self.ctrl.is_ind(): - self.__build_header(0x99) - self._send_buffer += b'\x01' - self.__finish_send_msg() - self.__process_contact_info() + if self.server_side and self.__process_contact_info(): + self.__build_header(0x91) + self._send_buffer += b'\x01' + self.__finish_send_msg() # don't forward this contact info here, we will build one # when the remote connection is established + elif self.await_conn_resp_cnt > 0: + self.await_conn_resp_cnt -= 1 + else: + self.forward(self._recv_buffer, self.header_len+self.data_len) return - elif self.ctrl.is_resp(): - return # ignore received response from tsun else: + logger.warning('Unknown Ctrl') self.inc_counter('Unknown_Ctrl') self.forward(self._recv_buffer, self.header_len+self.data_len) - def __process_contact_info(self): + def __process_contact_info(self) -> bool: result = struct.unpack_from('!B', self._recv_buffer, self.header_len) name_len = result[0] - + if self.data_len < name_len+2: + return False result = struct.unpack_from(f'!{name_len+1}pB', self._recv_buffer, self.header_len) self.contact_name = result[0] @@ -309,33 +315,34 @@ class Message(metaclass=IterRegistry): self.header_len+name_len+1) self.contact_mail = result[0] logger.info(f'mail: {self.contact_mail}') + return True def msg_get_time(self): tsun = Config.get('tsun') if tsun['enabled']: - if self.ctrl.is_resp(): - ts = self._timestamp() - result = struct.unpack_from('!q', self._recv_buffer, - self.header_len) - logger.debug(f'tsun-time: {result[0]:08x}' - f' proxy-time: {ts:08x}') - elif not self.ctrl.is_ind(): + if self.ctrl.is_ind(): + if self.data_len >= 8: + ts = self._timestamp() + result = struct.unpack_from('!q', self._recv_buffer, + self.header_len) + logger.debug(f'tsun-time: {result[0]:08x}' + f' proxy-time: {ts:08x}') + else: + logger.warning('Unknown Ctrl') self.inc_counter('Unknown_Ctrl') self.forward(self._recv_buffer, self.header_len+self.data_len) else: if self.ctrl.is_ind(): - ts = self._timestamp() - logger.debug(f'time: {ts:08x}') + if self.data_len == 0: + ts = self._timestamp() + logger.debug(f'time: {ts:08x}') - self.__build_header(0x99) - self._send_buffer += struct.pack('!q', ts) - self.__finish_send_msg() + self.__build_header(0x91) + self._send_buffer += struct.pack('!q', ts) + self.__finish_send_msg() - elif self.ctrl.is_resp(): - result = struct.unpack_from('!q', self._recv_buffer, - self.header_len) - logger.debug(f'tsun-time: {result[0]:08x}') else: + logger.warning('Unknown Ctrl') self.inc_counter('Unknown_Ctrl') def parse_msg_header(self): @@ -366,6 +373,7 @@ class Message(metaclass=IterRegistry): elif self.ctrl.is_resp(): return # ignore received response else: + logger.warning('Unknown Ctrl') self.inc_counter('Unknown_Ctrl') self.forward(self._recv_buffer, self.header_len+self.data_len) @@ -380,6 +388,7 @@ class Message(metaclass=IterRegistry): elif self.ctrl.is_resp(): return # ignore received response else: + logger.warning('Unknown Ctrl') self.inc_counter('Unknown_Ctrl') self.forward(self._recv_buffer, self.header_len+self.data_len) @@ -398,6 +407,7 @@ class Message(metaclass=IterRegistry): elif self.ctrl.is_ind(): pass else: + logger.warning('Unknown Ctrl') self.inc_counter('Unknown_Ctrl') self.forward(self._recv_buffer, self.header_len+self.data_len) diff --git a/app/tests/test_messages.py b/app/tests/test_messages.py index 2968a3d..582d562 100644 --- a/app/tests/test_messages.py +++ b/app/tests/test_messages.py @@ -67,11 +67,11 @@ def Msg2ContactInfo(): # two Contact Info messages @pytest.fixture def MsgContactResp(): # Contact Response message - return b'\x00\x00\x00\x14\x10R170000000000001\x99\x00\x01' + return b'\x00\x00\x00\x14\x10R170000000000001\x91\x00\x01' @pytest.fixture def MsgContactResp2(): # Contact Response message - return b'\x00\x00\x00\x14\x10R170000000000002\x99\x00\x01' + return b'\x00\x00\x00\x14\x10R170000000000002\x91\x00\x01' @pytest.fixture def MsgContactInvalid(): # Contact Response message @@ -83,7 +83,7 @@ def MsgGetTime(): # Get Time Request message @pytest.fixture def MsgTimeResp(): # Get Time Resonse message - return b'\x00\x00\x00\x1b\x10R170000000000001\x99\x22\x00\x00\x01\x89\xc6\x63\x4d\x80' + return b'\x00\x00\x00\x1b\x10R170000000000001\x91\x22\x00\x00\x01\x89\xc6\x63\x4d\x80' @pytest.fixture def MsgTimeInvalid(): # Get Time Request message @@ -316,13 +316,15 @@ def test_read_two_messages(ConfigTsunAllowAll, Msg2ContactInfo,MsgContactResp,Ms def test_msg_contact_resp(ConfigTsunInv1, MsgContactResp): ConfigTsunInv1 m = MemoryStream(MsgContactResp, (0,), False) + m.await_conn_resp_cnt = 1 m.db.stat['proxy']['Unknown_Ctrl'] = 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.await_conn_resp_cnt == 0 assert m.id_str == b"R170000000000001" assert m.unique_id == 'R170000000000001' - assert int(m.ctrl)==153 + assert int(m.ctrl)==145 assert m.msg_id==0 assert m.header_len==23 assert m.data_len==1 @@ -331,6 +333,46 @@ def test_msg_contact_resp(ConfigTsunInv1, MsgContactResp): assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() +def test_msg_contact_resp_2(ConfigTsunInv1, MsgContactResp): + ConfigTsunInv1 + m = MemoryStream(MsgContactResp, (0,), False) + m.await_conn_resp_cnt = 0 + m.db.stat['proxy']['Unknown_Ctrl'] = 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.await_conn_resp_cnt == 0 + assert m.id_str == b"R170000000000001" + assert m.unique_id == 'R170000000000001' + assert int(m.ctrl)==145 + assert m.msg_id==0 + assert m.header_len==23 + assert m.data_len==1 + assert m._forward_buffer==MsgContactResp + assert m._send_buffer==b'' + assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 + m.close() + +def test_msg_contact_resp_3(ConfigTsunInv1, MsgContactResp): + ConfigTsunInv1 + m = MemoryStream(MsgContactResp, (0,), True) + m.await_conn_resp_cnt = 0 + m.db.stat['proxy']['Unknown_Ctrl'] = 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.await_conn_resp_cnt == 0 + assert m.id_str == b"R170000000000001" + assert m.unique_id == 'R170000000000001' + assert int(m.ctrl)==145 + assert m.msg_id==0 + assert m.header_len==23 + assert m.data_len==1 + assert m._forward_buffer==MsgContactResp + assert m._send_buffer==b'' + assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 + m.close() + def test_msg_contact_invalid(ConfigTsunInv1, MsgContactInvalid): ConfigTsunInv1 m = MemoryStream(MsgContactInvalid, (0,)) @@ -381,7 +423,7 @@ def test_msg_get_time_autark(ConfigNoTsunInv1, MsgGetTime): assert m.header_len==23 assert m.data_len==0 assert m._forward_buffer==b'' - assert m._send_buffer==b'\x00\x00\x00\x1b\x10R170000000000001\x99"\x00\x00\x01\x8b\xdfs\xcc0' + assert m._send_buffer==b'\x00\x00\x00\x1b\x10R170000000000001\x91"\x00\x00\x01\x8b\xdfs\xcc0' assert m.db.stat['proxy']['Unknown_Ctrl'] == 0 m.close() @@ -394,7 +436,7 @@ def test_msg_time_resp(ConfigTsunInv1, MsgTimeResp): assert m.msg_count == 1 assert m.id_str == b"R170000000000001" assert m.unique_id == 'R170000000000001' - assert int(m.ctrl)==153 + assert int(m.ctrl)==145 assert m.msg_id==34 assert m.header_len==23 assert m.data_len==8 @@ -412,7 +454,7 @@ def test_msg_time_resp_autark(ConfigNoTsunInv1, MsgTimeResp): assert m.msg_count == 1 assert m.id_str == b"R170000000000001" assert m.unique_id == 'R170000000000001' - assert int(m.ctrl)==153 + assert int(m.ctrl)==145 assert m.msg_id==34 assert m.header_len==23 assert m.data_len==8 diff --git a/system_tests/test_tcp_socket.py b/system_tests/test_tcp_socket.py index f2d0adb..f559c00 100644 --- a/system_tests/test_tcp_socket.py +++ b/system_tests/test_tcp_socket.py @@ -20,7 +20,7 @@ def MsgContactInfo(): # Contact Info message @pytest.fixture def MsgContactResp(): # Contact Response message - return b'\x00\x00\x00\x14\x10'+get_sn()+b'\x99\x00\x01' + return b'\x00\x00\x00\x14\x10'+get_sn()+b'\x91\x00\x01' @pytest.fixture def MsgContactInfo2(): # Contact Info message @@ -28,7 +28,7 @@ def MsgContactInfo2(): # Contact Info message @pytest.fixture def MsgContactResp2(): # Contact Response message - return b'\x00\x00\x00\x14\x10'+get_invalid_sn()+b'\x99\x00\x01' + return b'\x00\x00\x00\x14\x10'+get_invalid_sn()+b'\x91\x00\x01' @pytest.fixture def MsgTimeStampReq(): # Get Time Request message @@ -199,7 +199,7 @@ def test_send_contact_resp(ClientConnection, MsgContactResp): except TimeoutError: assert True else: - assert data =='' + assert data == b'' def test_send_ctrl_data(ClientConnection, MsgTimeStampReq, MsgTimeStampResp, MsgContollerInd): s = ClientConnection