def testCrc16ReturnsDifferentForDifferentStrings(self): """Check that the CRC16 returns a different value if strings are different""" test_strings = ("hello world", "a", "12345678910111213141516", "", "modbus-tk", "www.apidev.fr") for s in test_strings: s1 = to_data(s) s2 = to_data(s + '_') self.assertNotEqual(modbus_tk.utils.calculate_crc(s1), modbus_tk.utils.calculate_crc(s2))
def testBuildRequestWithPdu(self): """Test the mbap returned by building a request with a pdu""" query = modbus_tcp.TcpQuery() for pdu in ["", "a", "a" * 127, "abcdefghi"]: request = query.build_request(to_data(pdu), 0) self.assertEqual( struct.pack(">HHHB" + str(len(pdu)) + "s", query._request_mbap.transaction_id, 0, len(pdu) + 1, 0, to_data(pdu)), request)
def testParseRequest(self): """Test that Modbus Rtu part of the request is understood""" query = modbus_rtu.RtuQuery() i = 0 for pdu in ["", "a", "a" * 127, "abcdefghi"]: request = query.build_request(to_data(pdu), i) (slave, extracted_pdu) = query.parse_request(request) self.assertEqual(extracted_pdu, to_data(pdu)) self.assertEqual(slave, i) i += 1
def testBuildRequestWithPdu(self): """Test the request returned by building a request with a pdu""" query = modbus_rtu.RtuQuery() for i in range(247): for pdu in ["", "a", "a"*127, "abcdefghi"]: request = query.build_request(to_data(pdu), i) expected = struct.pack(">B"+str(len(pdu))+"s", i, to_data(pdu)) expected_crc = crc16_alternative(expected) expected += struct.pack(">H", expected_crc) self.assertEqual(expected, request)
def testBuildResponse(self): """Test that the response of an request is build properly""" query = modbus_rtu.RtuQuery() i = 0 for pdu in ["", "a", "a" * 127, "abcdefghi"]: request = query.build_request(to_data(pdu), i) response = query.build_response(to_data(pdu)) response_pdu = query.parse_response(response) self.assertEqual(to_data(pdu), response_pdu) i += 1
def testParseRequest(self): """Test that Modbus Rtu part of the request is understood""" query = modbus_rtu.RtuQuery() i = 0 for pdu in ["", "a", "a"*127, "abcdefghi"]: request = query.build_request(to_data(pdu), i) (slave, extracted_pdu) = query.parse_request(request) self.assertEqual(extracted_pdu, to_data(pdu)) self.assertEqual(slave, i) i += 1
def testBuildResponse(self): """Test that the response of an request is build properly""" query = modbus_rtu.RtuQuery() i = 0 for pdu in ["", "a", "a"*127, "abcdefghi"]: request = query.build_request(to_data(pdu), i) response = query.build_response(to_data(pdu)) response_pdu = query.parse_response(response) self.assertEqual(to_data(pdu), response_pdu) i += 1
def testParseWrongLengthResponse(self): """Test an error is raised if the length is not ok""" query = modbus_tcp.TcpQuery() pdu = to_data('a') request = query.build_request(pdu, 0) response = struct.pack(">HHHB" + str(len(pdu)) + "s", query._request_mbap.transaction_id, query._request_mbap.protocol_id + 1, len(pdu), query._request_mbap.unit_id, to_data('pdu')) self.assertRaises(modbus_tk.modbus_tcp.ModbusInvalidMbapError, query.parse_response, response)
def testParseRespone(self): """Test that Modbus Rtu part of the response is understood""" query = modbus_rtu.RtuQuery() for i in range(247): for pdu in ["", "a", "a"*127, "abcdefghi"]: request = query.build_request(to_data(pdu), i) response = struct.pack(">B"+str(len(pdu))+"s", i, to_data(pdu)) response_crc = crc16_alternative(response) response += struct.pack(">H", response_crc) extracted = query.parse_response(response) self.assertEqual(extracted, to_data(pdu))
def testParseRespone(self): """Test that Modbus TCP part of the response is understood""" query = modbus_tcp.TcpQuery() for pdu in ["", "a", "a" * 127, "abcdefghi"]: request = query.build_request(to_data(pdu), 0) response = struct.pack(">HHHB" + str(len(pdu)) + "s", query._request_mbap.transaction_id, query._request_mbap.protocol_id, len(pdu) + 1, query._request_mbap.unit_id, to_data(pdu)) extracted = query.parse_response(response) self.assertEqual(extracted, to_data(pdu))
def testParseWrongLengthResponse(self): """Test an error is raised if the length is not ok""" query = modbus_tcp.TcpQuery() pdu = to_data('a') request = query.build_request(pdu, 0) response = struct.pack( ">HHHB"+str(len(pdu))+"s", query._request_mbap.transaction_id, query._request_mbap.protocol_id+1, len(pdu), query._request_mbap.unit_id, to_data('pdu') ) self.assertRaises(modbus_tk.modbus_tcp.ModbusInvalidMbapError, query.parse_response, response)
def testParseRespone(self): """Test that Modbus TCP part of the response is understood""" query = modbus_tcp.TcpQuery() for pdu in ["", "a", "a"*127, "abcdefghi"]: request = query.build_request(to_data(pdu), 0) response = struct.pack( ">HHHB" + str(len(pdu)) + "s", query._request_mbap.transaction_id, query._request_mbap.protocol_id, len(pdu) + 1, query._request_mbap.unit_id, to_data(pdu) ) extracted = query.parse_response(response) self.assertEqual(extracted, to_data(pdu))
def testCrc16ForAllCharValues(self): """Check that the CRC16 is generated properly for all chars""" s = to_data('') for i in range(256): s += struct.pack(">B", i) self.assertEqual(crc16_alternative(s), modbus_tk.utils.calculate_crc(s))
def testBuildRequest(self): """Test the mbap returned by building a request""" query = modbus_tcp.TcpQuery() request = query.build_request(to_data(""), 0) self.assertEqual( struct.pack(">HHHB", query._request_mbap.transaction_id, 0, 1, 0), request)
def _recv(self, expected_length=-1): """Receive the response from the slave""" response = utils.to_data("") start_time = time.time() if self.use_sw_timeout else 0 readed_len = 0 while True: if self._serial.timeout: # serial.read() says if a timeout is set it may return less characters as requested # we should update expected_length by readed_len read_bytes = self._serial.read(expected_length - readed_len if (expected_length - readed_len) > 0 else 1) else: read_bytes = self._serial.read(expected_length if expected_length > 0 else 1) if self.use_sw_timeout: read_duration = time.time() - start_time else: read_duration = 0 if (not read_bytes) or (read_duration > self._serial.timeout): break response += read_bytes if expected_length >= 0 and len(response) >= expected_length: # if the expected number of byte is received consider that the response is done # improve performance by avoiding end-of-response detection by timeout break readed_len += len(read_bytes) retval = call_hooks("modbus_rtu.RtuMaster.after_recv", (self, response)) if retval is not None: return retval return response
def testParseRequestInvalidLength(self): """Test that an error is raised if the length is not valid""" query = modbus_tcp.TcpQuery() i = 0 for pdu in ["", "a", "a" * 127, "abcdefghi"]: request = struct.pack(">HHHB", 0, 0, (len(pdu) + 2), 0) self.assertRaises(modbus_tk.modbus_tcp.ModbusInvalidMbapError, query.parse_request, request + to_data(pdu))
def testBuildRequestWithSlave(self): """Test the mbap returned by building a request with a slave""" query = modbus_tcp.TcpQuery() for i in range(0, 255): request = query.build_request(to_data(""), i) self.assertEqual( struct.pack(">HHHB", query._request_mbap.transaction_id, 0, 1, i), request)
def testCrc16ReturnsAlwaysTheSame(self): """Check that the CRC16 returns the same result for the same value""" test_strings = ("hello world", "a", "12345678910111213141516", "", "modbus-tk", "www.apidev.fr") for s in test_strings: s = to_data(s) self.assertEqual(modbus_tk.utils.calculate_crc(s), modbus_tk.utils.calculate_crc(s))
def testCrc16(self): """Check that the CRC16 is generated properly""" test_strings = ("hello world", "a", "12345678910111213141516", "", "modbus-tk", "www.apidev.fr") for s in test_strings: s = to_data(s) self.assertEqual(crc16_alternative(s), modbus_tk.utils.calculate_crc(s))
def testBuildRequest(self): """Test the string returned by building a request""" query = modbus_rtu.RtuQuery() request = query.build_request(to_data(""), 0) expected = struct.pack(">B", 0) expected_crc = crc16_alternative(expected) expected += struct.pack(">H", expected_crc) self.assertEqual(expected, request)
def testBuildRequestWithSlave(self): """Test the mbap returned by building a request with a slave""" query = modbus_tcp.TcpQuery() for i in range(0, 255): request = query.build_request(to_data(""), i) self.assertEqual( struct.pack(">HHHB", query._request_mbap.transaction_id, 0, 1, i), request )
def testIncIdOfRequest(self): """Check that the transaction id is increased when building the request""" queries = [modbus_tcp.TcpQuery() for i in range(100)] for i in range(len(queries)): queries[i].build_request(to_data(""), 0) for i in range(len(queries)-1): self.assertEqual(queries[i]._request_mbap.transaction_id+1, queries[i+1]._request_mbap.transaction_id)
def testBuildRequestWithSlave(self): """Test the string returned by building a request with a slave""" query = modbus_rtu.RtuQuery() for i in range(0, 256): request = query.build_request(to_data(""), i) expected = struct.pack(">B", i) expected_crc = crc16_alternative(expected) expected += struct.pack(">H", expected_crc) self.assertEqual(expected, request)
def _do_run(self): """main function of the server""" try: # check the status of every socket request = utils.to_data('') if self._block_on_first_byte: # do a blocking read for first byte self._serial.timeout = None try: read_bytes = self._serial.read(1) request += read_bytes except Exception as e: self._serial.close() self._serial.open() self._serial.timeout = self._timeout # Read rest of the request while True: try: read_bytes = self._serial.read(128) if not read_bytes: break except Exception as e: self._serial.close() self._serial.open() break request += read_bytes # parse the request if request: retval = call_hooks("modbus_rtu.RtuServer.after_read", (self, request)) if retval is not None: request = retval response = self._handle(request) # send back the response retval = call_hooks("modbus_rtu.RtuServer.before_write", (self, response)) if retval is not None: response = retval if response: if self._serial.in_waiting > 0: # Most likely master timed out on this request and started a new one # for which we already received atleast 1 byte LOGGER.warning("Not sending response because there is new request pending") else: self._serial.write(response) self._serial.flush() time.sleep(self.get_timeout()) call_hooks("modbus_rtu.RtuServer.after_write", (self, response)) except Exception as excpt: LOGGER.error("Error while handling request, Exception occurred: %s", excpt) call_hooks("modbus_rtu.RtuServer.on_error", (self, excpt))
def testParseWrongCrcResponse(self): """Test an error is raised if wrong transaction id""" query = modbus_rtu.RtuQuery() pdu = to_data("a") request = query.build_request(pdu, 5) response = struct.pack(">B" + str(len(pdu)) + "s", 5, pdu) response_crc = crc16_alternative(response)+1 response += struct.pack(">H", response_crc) self.assertRaises(modbus_tk.modbus.ModbusInvalidResponseError, query.parse_response, response)
def testParseRequestInvalidLength(self): """Test that an error is raised if the length is not valid""" query = modbus_tcp.TcpQuery() i = 0 for pdu in ["", "a", "a"*127, "abcdefghi"]: request = struct.pack(">HHHB", 0, 0, (len(pdu)+2), 0) self.assertRaises( modbus_tk.modbus_tcp.ModbusInvalidMbapError, query.parse_request, request + to_data(pdu) )
def testBuildRequestWithPdu(self): """Test the mbap returned by building a request with a pdu""" query = modbus_tcp.TcpQuery() for pdu in ["", "a", "a"*127, "abcdefghi"]: request = query.build_request(to_data(pdu), 0) self.assertEqual( struct.pack( ">HHHB"+str(len(pdu))+"s", query._request_mbap.transaction_id, 0, len(pdu)+1, 0, to_data(pdu) ), request )
def _recv(self, expected_length=-1): """Receive the response from the slave""" response = to_data('') length = 255 while len(response) < length: rcv_byte = self._sock.recv(1) if rcv_byte: response += rcv_byte if expected_length >= 0 and len(response) >= expected_length: break retval = call_hooks("modbus_rtu_over_tcp.RtuOverTcpMaster.after_recv", (self, response)) if retval is not None: return retval return response
def _recv(self, expected_length=-1): """Receive the response from the slave""" response = utils.to_data("") while True: read_bytes = self._serial.read(expected_length if expected_length > 0 else 1) if not read_bytes: break response += read_bytes if expected_length >= 0 and len(response) >= expected_length: #if the expected number of byte is received consider that the response is done #improve performance by avoiding end-of-response detection by timeout break retval = call_hooks("modbus_rtu.RtuMaster.after_recv", (self, response)) if retval is not None: return retval return response
def _do_run(self): """main function of the server""" try: # check the status of every socket request = utils.to_data('') while True: try: read_bytes = self._serial.read(128) if not read_bytes: break except Exception as e: self._serial.close() self._serial.open() break request += read_bytes # parse the request if request: retval = call_hooks("modbus_rtu.RtuServer.after_read", (self, request)) if retval is not None: request = retval response = self._handle(request) # send back the response retval = call_hooks("modbus_rtu.RtuServer.before_write", (self, response)) if retval is not None: response = retval if response: self._serial.write(response) time.sleep(self.get_timeout()) call_hooks("modbus_rtu.RtuServer.after_write", (self, response)) except Exception as excpt: LOGGER.error( "Error while handling request, Exception occurred: %s", excpt) call_hooks("modbus_rtu.RtuServer.on_error", (self, excpt))
def _recv(self, expected_length=-1): """ Receive the response from the slave Do not take expected_length into account because the length of the response is written in the mbap. Used for RTU only """ response = to_data('') length = 255 while len(response) < length: rcv_byte = self._sock.recv(1) if rcv_byte: response += rcv_byte if len(response) == 6: to_be_recv_length = struct.unpack(">HHH", response)[2] length = to_be_recv_length + 6 else: break retval = call_hooks("modbus_tcp.TcpMaster.after_recv", (self, response)) if retval is not None: return retval return response
def _do_run(self): """main function of the server""" try: #check the status of every socket request = utils.to_data('') while True: try: read_bytes = self._serial.read(128) if not read_bytes: break except Exception as e: self._serial.close() self._serial.open() break request += read_bytes #parse the request if request: retval = call_hooks("modbus_rtu.RtuServer.after_read", (self, request)) if retval is not None: request = retval response = self._handle(request) #send back the response retval = call_hooks("modbus_rtu.RtuServer.before_write", (self, response)) if retval is not None: response = retval if response: self._serial.write(response) time.sleep(self.get_timeout()) except Exception as excpt: LOGGER.error("Error while handling request, Exception occurred: %s", excpt) call_hooks("modbus_rtu.RtuServer.on_error", (self, excpt))
def testParseTooShortRequest(self): """Test an error is raised if the request is too short""" query = modbus_rtu.RtuQuery() for i in range(3): self.assertRaises(modbus_tk.modbus.ModbusInvalidRequestError, query.parse_request, to_data("a" * i))
def _do_run(self): """called in a almost-for-ever loop by the server""" # check the status of every socket inputready = select.select(self._sockets, [], [], 1.0)[0] #print(inputready) # handle data on each a socket for sock in inputready: try: if sock == self._sock: #print (self._sock) # handle the server socket #print ("Receiveeeeeeeeeeeeeeeeeeeee") client, address = self._sock.accept() client.setblocking(0) LOGGER.debug("%s is connected with socket %d...", str(address), client.fileno()) self._sockets.append(client) call_hooks("modbus_tcp.TcpServer.on_connect", (self, client, address)) return else: if len(sock.recv(1, socket.MSG_PEEK)) == 0: # socket is disconnected LOGGER.debug("%d is disconnected" % (sock.fileno())) call_hooks("modbus_tcp.TcpServer.on_disconnect", (self, sock)) sock.close() self._sockets.remove(sock) break # handle all other sockets sock.settimeout(1.0) request = to_data("") is_ok = True # read the 7 bytes of the mbap while (len(request) < 7) and is_ok: new_byte = sock.recv(1) if len(new_byte) == 0: is_ok = False else: request += new_byte retval = call_hooks("modbus_tcp.TcpServer.after_recv", (self, sock, request)) if retval is not None: request = retval if is_ok: # read the rest of the request length = self._get_request_length(request) while (len(request) < (length + 6)) and is_ok: new_byte = sock.recv(1) if len(new_byte) == 0: is_ok = False else: request += new_byte if is_ok: response = "" # parse the request try: response = self._handle(request) except Exception as msg: LOGGER.error( "Error while handling a request, Exception occurred: %s", msg) # send back the response if response: try: retval = call_hooks( "modbus_tcp.TcpServer.before_send", (self, sock, response)) if retval is not None: response = retval sock.send(response) call_hooks("modbus_tcp.TcpServer.after_send", (self, sock, response)) except Exception as msg: is_ok = False LOGGER.error( "Error while sending on socket %d, Exception occurred: %s", sock.fileno(), msg) except Exception as excpt: LOGGER.warning("Error while processing data on socket %d: %s", sock.fileno(), excpt) call_hooks("modbus_tcp.TcpServer.on_error", (self, sock, excpt)) sock.close() self._sockets.remove(sock)
def _do_run(self): """called in a almost-for-ever loop by the server""" # check the status of every socket inputready = select.select(self._sockets, [], [], 1.0)[0] # handle data on each a socket for sock in inputready: try: if sock == self._sock: # handle the server socket client, address = self._sock.accept() client.setblocking(0) LOGGER.info("%s is connected with socket %d...", str(address), client.fileno()) self._sockets.append(client) call_hooks("modbus_tcp.TcpServer.on_connect", (self, client, address)) else: if len(sock.recv(1, socket.MSG_PEEK)) == 0: # socket is disconnected LOGGER.info("%d is disconnected" % (sock.fileno())) call_hooks("modbus_tcp.TcpServer.on_disconnect", (self, sock)) sock.close() self._sockets.remove(sock) break # handle all other sockets sock.settimeout(1.0) request = to_data("") is_ok = True # read the 7 bytes of the mbap while (len(request) < 7) and is_ok: new_byte = sock.recv(1) if len(new_byte) == 0: is_ok = False else: request += new_byte retval = call_hooks("modbus_tcp.TcpServer.after_recv", (self, sock, request)) if retval is not None: request = retval if is_ok: # read the rest of the request length = self._get_request_length(request) while (len(request) < (length + 6)) and is_ok: new_byte = sock.recv(1) if len(new_byte) == 0: is_ok = False else: request += new_byte if is_ok: response = "" # parse the request try: response = self._handle(request) except Exception as msg: LOGGER.error("Error while handling a request, Exception occurred: %s", msg) # send back the response if response: try: retval = call_hooks("modbus_tcp.TcpServer.before_send", (self, sock, response)) if retval is not None: response = retval sock.send(response) call_hooks("modbus_tcp.TcpServer.after_send", (self, sock, response)) except Exception as msg: is_ok = False LOGGER.error( "Error while sending on socket %d, Exception occurred: %s", sock.fileno(), msg ) except Exception as excpt: LOGGER.warning("Error while processing data on socket %d: %s", sock.fileno(), excpt) call_hooks("modbus_tcp.TcpServer.on_error", (self, sock, excpt)) sock.close() self._sockets.remove(sock)
def testBuildRequest(self): """Test the mbap returned by building a request""" query = modbus_tcp.TcpQuery() request = query.build_request(to_data(""), 0) self.assertEqual(struct.pack(">HHHB", query._request_mbap.transaction_id, 0, 1, 0), request)