def handle_request(self, query, request): """ when a request is received, handle it and returns the response pdu """ request_pdu = "" try: #extract the pdu and the slave id (slave_id, request_pdu) = query.parse_request(request) #get the slave and let him executes the action if slave_id == 0: #broadcast for key in self._slaves: self._slaves[key].handle_request(request_pdu, broadcast=True) return else: slave = self.get_slave(slave_id) response_pdu = slave.handle_request(request_pdu) #make the full response response = query.build_response(response_pdu) return response except Exception as excpt: call_hooks("modbus.Databank.on_error", (self, excpt, request_pdu)) LOGGER.error("handle request failed: " + str(excpt)) #If the request was not handled correctly, return a server error response func_code = 1 if len(request_pdu) > 0: (func_code, ) = struct.unpack(">B", request_pdu[0]) return struct.pack(">BB", func_code+0x80, defines.SLAVE_DEVICE_FAILURE)
def _do_close(self): """Close the connection with the Modbus Slave""" if self._sock: call_hooks("modbus_tcp.TcpMaster.before_close", (self, )) self._sock.close() call_hooks("modbus_tcp.TcpMaster.after_close", (self, )) self._sock = None
def _do_run(self): """main function of the server""" try: #check the status of every socket response = "" request = "" read_bytes = "dummy" while read_bytes: read_bytes = self._serial.read(128) 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 _write_multiple_coils(self, request_pdu): """execute modbus function 15""" call_hooks("modbus.Slave.handle_write_multiple_coils_request", (self, request_pdu)) # get the starting address and the number of items from the request pdu (starting_address, quantity_of_x, byte_count) = struct.unpack(">HHB", request_pdu[1:6]) expected_byte_count = quantity_of_x / 8 if (quantity_of_x % 8) > 0: expected_byte_count += 1 if (quantity_of_x <= 0) or (quantity_of_x > 1968) or (byte_count != expected_byte_count): # maximum allowed size is 1968 coils raise ModbusError(defines.ILLEGAL_DATA_VALUE) # look for the block corresponding to the request block, offset = self._get_block_and_offset(defines.COILS, starting_address, quantity_of_x) count = 0 for i in xrange(byte_count): if count >= quantity_of_x: break fmt = "B" if self.unsigned else "b" (byte_value,) = struct.unpack(">" + fmt, request_pdu[6 + i]) for j in xrange(8): if count >= quantity_of_x: break if byte_value & (1 << j): block[offset + i * 8 + j] = 1 else: block[offset + i * 8 + j] = 0 count += 1 return struct.pack(">HH", starting_address, count)
def _do_open(self): """Connect to the Modbus slave""" if self._sock: self._sock.close() self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.set_timeout(self.get_timeout()) self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) call_hooks("modbus_tcp.TcpMaster.before_connect", (self, )) self._sock.connect((self._host, self._port)) call_hooks("modbus_tcp.TcpMaster.after_connect", (self, ))
def _write_single_register(self, request_pdu): """execute modbus function 6""" call_hooks("modbus.Slave.handle_write_single_register_request", (self, request_pdu)) fmt = "H" if self.unsigned else "h" (data_address, value) = struct.unpack(">H" + fmt, request_pdu[1:5]) block, offset = self._get_block_and_offset(defines.HOLDING_REGISTERS, data_address, 1) block[offset] = value # returns echo of the command return request_pdu[1:]
def _handle(self, request): LOGGER.debug(self.get_log_buffer('-->', request)) query = self._make_query() retval = call_hooks('modbus.Server.before_handle_request', (self, request)) if retval: request = retval response = self._databank.handle_request(query, request) retval = call_hooks('modbus.Server.after_handle_request', (self, response)) if retval: response = retval if response: LOGGER.debug(self.get_log_buffer('<--', response)) return response
def _write_single_coil(self, request_pdu): """execute modbus function 5""" call_hooks("modbus.Slave.handle_write_single_coil_request", (self, request_pdu)) (data_address, value) = struct.unpack(">HH", request_pdu[1:5]) block, offset = self._get_block_and_offset(defines.COILS, data_address, 1) if value == 0: block[offset] = 0 elif value == 0xff00: block[offset] = 1 else: raise ModbusError(defines.ILLEGAL_DATA_VALUE) # returns echo of the command return request_pdu[1:]
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 handle_request(self, query, request): """ when a request is received, handle it and returns the response pdu """ request_pdu = "" try: # extract the pdu and the slave id (slave_id, request_pdu) = query.parse_request(request) # get the slave and let him executes the action if slave_id == 0: # broadcast for key in self._slaves: self._slaves[key].handle_request(request_pdu, broadcast=True) return else: try: slave = self.get_slave(slave_id) except MissingKeyError: if self.error_on_missing_slave: raise else: return "" response_pdu = slave.handle_request(request_pdu) # make the full response response = query.build_response(response_pdu) return response except ModbusInvalidRequestError as excpt: # Request is invalid, do not send any response LOGGER.error("invalid request: " + str(excpt)) return "" except MissingKeyError as excpt: # No slave with this ID in server, do not send any response LOGGER.error("handle request failed: " + str(excpt)) return "" except Exception as excpt: call_hooks("modbus.Databank.on_error", (self, excpt, request_pdu)) LOGGER.error("handle request failed: " + str(excpt)) # If the request was not handled correctly, return a server error response func_code = 1 if len(request_pdu) > 0: (func_code, ) = struct.unpack(">B", request_pdu[0:1]) return struct.pack(">BB", func_code + 0x80, defines.SLAVE_DEVICE_FAILURE)
def handle_request(self, request_pdu, broadcast=False): with self._data_lock: #thread-safe try: retval = call_hooks("modbus.Slave.handle_request", (self, request_pdu)) if retval <> None: return retval # get the function code (function_code, ) = struct.unpack(">B", request_pdu[0]) # check if the function code is valid. If not returns error response if not self._fn_code_map.has_key(function_code): raise modbus.ModbusError(cst.ILLEGAL_FUNCTION) # if read query is broadcasted raises an error cant_be_broadcasted = (cst.READ_COILS, cst.READ_DISCRETE_INPUTS, cst.READ_INPUT_REGISTERS, cst.READ_HOLDING_REGISTERS) if broadcast and (function_code in cant_be_broadcasted): raise modbus.ModbusInvalidRequestError( "Function %d can not be broadcasted" % function_code) #execute the corresponding function response_pdu = self._fn_code_map[function_code](request_pdu) if response_pdu: if broadcast: call_hooks("modbus.Slave.on_handle_broadcast", (self, response_pdu)) LOGGER.debug( "broadcast: %s" % (utils.get_log_buffer("!!", response_pdu))) return "" else: return struct.pack(">B", function_code) + response_pdu raise Exception("No response for function %d" % function_code) except modbus.ModbusError, excpt: LOGGER.debug(str(excpt)) call_hooks("modbus.Slave.on_exception", (self, function_code, excpt)) return struct.pack(">BB", function_code + 128, excpt.get_exception_code())
def handle_request(self, request_pdu, broadcast=False): """ parse the request pdu, makes the corresponding action and returns the response pdu """ # thread-safe with self._data_lock: try: retval = call_hooks("modbus.Slave.handle_request", (self, request_pdu)) if retval is not None: return retval # get the function code (function_code,) = struct.unpack(">B", request_pdu[0]) # check if the function code is valid. If not returns error response if function_code not in self._fn_code_map: raise ModbusError(defines.ILLEGAL_FUNCTION) # if read query is broadcasted raises an error cant_be_broadcasted = ( defines.READ_COILS, defines.READ_DISCRETE_INPUTS, defines.READ_INPUT_REGISTERS, defines.READ_HOLDING_REGISTERS, ) if broadcast and (function_code in cant_be_broadcasted): raise ModbusInvalidRequestError("Function %d can not be broadcasted" % function_code) # execute the corresponding function response_pdu = self._fn_code_map[function_code](request_pdu) if response_pdu: if broadcast: call_hooks("modbus.Slave.on_handle_broadcast", (self, response_pdu)) LOGGER.debug("broadcast: %s", get_log_buffer("!!", response_pdu)) return "" else: return struct.pack(">B", function_code) + response_pdu raise Exception("No response for function %d" % function_code) except ModbusError, excpt: LOGGER.debug(str(excpt)) call_hooks("modbus.Slave.on_exception", (self, function_code, excpt)) return struct.pack(">BB", function_code + 128, excpt.get_exception_code())
def _send(self, request): """Send request to the slave""" retval = call_hooks("modbus_rtu.RtuMaster.before_send", (self, request)) if retval is not None: request = retval self._serial.flushInput() self._serial.flushOutput() self._serial.write(request)
def _send(self, request): """Send request to the slave""" retval = call_hooks("modbus_rtu.RtuMaster.before_send", (self, request)) if retval is not None: request = retval self._serial.reset_input_buffer() self._serial.reset_output_buffer() self._serial.write(request)
def handle_request(self, query, request): request_pdu = '' try: (slave_id, request_pdu) = query.parse_request(request) if slave_id == 0: for key in self._slaves: self._slaves[key].handle_request(request_pdu, broadcast=True) return else: slave = self.get_slave(slave_id) response_pdu = slave.handle_request(request_pdu) response = query.build_response(response_pdu) return response except Exception as e: call_hooks('modbus.Databank.on_error', (self, e, request_pdu)) LOGGER.error('handle_request failed: ' + str(e)) except: LOGGER.error('handle_request failed: unknown exception')
def _write_multiple_registers(self, request_pdu): """execute modbus function 16""" call_hooks("modbus.Slave.handle_write_multiple_registers_request", (self, request_pdu)) # get the starting address and the number of items from the request pdu (starting_address, quantity_of_x, byte_count) = struct.unpack(">HHB", request_pdu[1:6]) if (quantity_of_x <= 0) or (quantity_of_x > 123) or (byte_count != (quantity_of_x * 2)): # maximum allowed size is 123 registers in one reading raise ModbusError(defines.ILLEGAL_DATA_VALUE) # look for the block corresponding to the request block, offset = self._get_block_and_offset(defines.HOLDING_REGISTERS, starting_address, quantity_of_x) count = 0 for i in xrange(quantity_of_x): count += 1 fmt = "H" if self.unsigned else "h" block[offset + i] = struct.unpack(">" + fmt, request_pdu[6 + 2 * i : 8 + 2 * i])[0] return struct.pack(">HH", starting_address, count)
def _write_multiple_registers(self, request_pdu): """execute modbus function 16""" call_hooks("modbus.Slave.handle_write_multiple_registers_request", (self, request_pdu)) # get the starting address and the number of items from the request pdu (starting_address, quantity_of_x, byte_count) = struct.unpack(">HHB", request_pdu[1:6]) if (quantity_of_x <= 0) or (quantity_of_x > 123) or (byte_count != (quantity_of_x * 2)): # maximum allowed size is 123 registers in one reading raise ModbusError(defines.ILLEGAL_DATA_VALUE) # look for the block corresponding to the request block, offset = self._get_block_and_offset(defines.HOLDING_REGISTERS, starting_address, quantity_of_x) count = 0 for i in range(quantity_of_x): count += 1 fmt = "H" if self.unsigned else "h" block[offset+i] = struct.unpack(">"+fmt, request_pdu[6+2*i:8+2*i])[0] return struct.pack(">HH", starting_address, count)
def _send(self, request): """Send request to the slave""" retval = call_hooks("modbus_rtu.RtuMaster.before_send", (self, request)) if retval is not None: request = retval self._serial.flushInput() self._serial.flushOutput() self._serial.write(request) time.sleep(self.get_timeout())
def _handle(self, request): """handle a received sentence""" if self._verbose: LOGGER.debug(get_log_buffer("-->", request)) # gets a query for analyzing the request query = self._make_query() retval = call_hooks("modbus.Server.before_handle_request", (self, request)) if retval: request = retval response = self._databank.handle_request(query, request) retval = call_hooks("modbus.Server.after_handle_request", (self, response)) if retval: response = retval if response and self._verbose: LOGGER.debug(get_log_buffer("<--", response)) 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 _write_multiple_coils(self, request_pdu): """execute modbus function 15""" call_hooks("modbus.Slave.handle_write_multiple_coils_request", (self, request_pdu)) # get the starting address and the number of items from the request pdu (starting_address, quantity_of_x, byte_count) = struct.unpack(">HHB", request_pdu[1:6]) expected_byte_count = quantity_of_x // 8 if (quantity_of_x % 8) > 0: expected_byte_count += 1 if (quantity_of_x <= 0) or (quantity_of_x > 1968) or ( byte_count != expected_byte_count): # maximum allowed size is 1968 coils raise ModbusError(defines.ILLEGAL_DATA_VALUE) # look for the block corresponding to the request block, offset = self._get_block_and_offset(defines.COILS, starting_address, quantity_of_x) count = 0 for i in range(byte_count): if count >= quantity_of_x: break fmt = "B" if self.unsigned else "b" (byte_value, ) = struct.unpack(">" + fmt, request_pdu[6 + i:7 + i]) for j in range(8): if count >= quantity_of_x: break if byte_value & (1 << j): block[offset + i * 8 + j] = 1 else: block[offset + i * 8 + j] = 0 count += 1 return struct.pack(">HH", starting_address, count)
def _send(self, request): """Send request to the slave""" retval = call_hooks("modbus_tcp.TcpMaster.before_send", (self, request)) if retval is not None: request = retval try: flush_socket(self._sock, 3) except Exception as msg: #if we can't flush the socket successfully: a disconnection may happened #try to reconnect LOGGER.error('Error while flushing the socket: {0}'.format(msg)) self._do_open() self._sock.send(request)
def _read_write_multiple_registers(self, request_pdu): """execute modbus function 23""" call_hooks("modbus.Slave.handle_read_write_multiple_registers_request", (self, request_pdu)) # get the starting address and the number of items from the request pdu (starting_read_address, quantity_of_x_to_read, starting_write_address, quantity_of_x_to_write, byte_count_to_write) = struct.unpack(">HHHHB", request_pdu[1:10]) # read part if (quantity_of_x_to_read <= 0) or (quantity_of_x_to_read > 125): # maximum allowed size is 125 registers in one reading LOGGER.debug("quantity_of_x_to_read is %d", quantity_of_x_to_read) raise ModbusError(defines.ILLEGAL_DATA_VALUE) # look for the block corresponding to the request block, offset = self._get_block_and_offset(defines.HOLDING_REGISTERS, starting_read_address, quantity_of_x_to_read) # get the values values = block[offset:offset+quantity_of_x_to_read] # write the response header response = struct.pack(">B", 2 * quantity_of_x_to_read) # add the values of every register on 2 bytes for reg in values: fmt = "H" if self.unsigned else "h" response += struct.pack(">"+fmt, reg) # write part if (quantity_of_x_to_write <= 0) or (quantity_of_x_to_write > 123) or (byte_count_to_write != (quantity_of_x_to_write * 2)): # maximum allowed size is 123 registers in one reading raise ModbusError(defines.ILLEGAL_DATA_VALUE) # look for the block corresponding to the request block, offset = self._get_block_and_offset(defines.HOLDING_REGISTERS, starting_write_address, quantity_of_x_to_write) count = 0 for i in range(quantity_of_x_to_write): count += 1 fmt = "H" if self.unsigned else "h" block[offset+i] = struct.unpack(">"+fmt, request_pdu[10+2*i:12+2*i])[0] return response
def handle_request(self, query, request): request_pdu = "" try: #extract the pdu and the slave id (slave_id, request_pdu) = query.parse_request(request) #get the slave and let him executes the action if slave_id == 0: #broadcast for key in self._slaves: self._slaves[key].handle_request(request_pdu, broadcast=True) return else: slave = self.get_slave(slave_id) response_pdu = slave.handle_request(request_pdu) #make the full response response = query.build_response(response_pdu) return response except Exception, excpt: call_hooks("modbus.Databank.on_error", (self, excpt, request_pdu)) LOGGER.error("handle request failed: " + str(excpt))
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 _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 handle_request(self, query, request): """ when a request is received, handle it and returns the response pdu """ request_pdu = "" try: # extract the pdu and the slave id (slave_id, request_pdu) = query.parse_request(request) # get the slave and let him executes the action if slave_id == 0: # broadcast for key in self._slaves: self._slaves[key].handle_request(request_pdu, broadcast=True) return else: slave = self.get_slave(slave_id) response_pdu = slave.handle_request(request_pdu) # make the full response response = query.build_response(response_pdu) return response except Exception, excpt: call_hooks("modbus.Databank.on_error", (self, excpt, request_pdu)) LOGGER.error("handle request failed: " + str(excpt))
def _send(self, request): """Send request to the slave""" retval = call_hooks("modbus_rtu.RtuMaster.before_send", (self, request)) if retval is not None: request = retval self._serial.reset_input_buffer() self._serial.reset_output_buffer() self._serial.write(request) self._serial.flush() # Read the echo data, and discard it if self.handle_local_echo: self._serial.read(len(request))
def _recv(self, expected_length=-1): """Receive the response from the slave""" response = "" read_bytes = "dummy" while read_bytes: read_bytes = self._serial.read(expected_length if expected_length > 0 else 1) 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 _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 _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 _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 while True: 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 retval = call_hooks("modbus_rtu.RtuMaster.after_recv", (self, response)) if retval is not None: return retval return response
def _do_close(self): """Close the serial port if still opened""" if self._serial.isOpen(): self._serial.close() call_hooks("modbus_rtu.RtuMaster.after_close", (self, ))
def close(self): """close the serial communication""" if self._serial.is_open: call_hooks("modbus_rtu.RtuServer.before_close", (self, )) self._serial.close() call_hooks("modbus_rtu.RtuServer.after_close", (self, ))
def _do_open(self): """Open the given serial port if not already opened""" if not self._serial.is_open: call_hooks("modbus_rtu.RtuMaster.before_open", (self, )) self._serial.open()
def _do_init(self): """initialize the serial connection""" if not self._serial.isOpen(): call_hooks("modbus_rtu.RtuServer.before_open", (self, )) self._serial.open() call_hooks("modbus_rtu.RtuServer.after_open", (self, ))
def _read_input_registers(self, request_pdu): """handle read coils modbus function""" call_hooks("modbus.Slave.handle_read_input_registers_request", (self, request_pdu)) return self._read_registers(defines.ANALOG_INPUTS, request_pdu)
def _read_holding_registers(self, request_pdu): """handle read coils modbus function""" call_hooks("modbus.Slave.handle_read_holding_registers_request", (self, request_pdu)) return self._read_registers(defines.HOLDING_REGISTERS, request_pdu)
def _read_discrete_inputs(self, request_pdu): """handle read discrete inputs modbus function""" call_hooks("modbus.Slave.handle_read_discrete_inputs_request", (self, request_pdu)) return self._read_digital(defines.DISCRETE_INPUTS, request_pdu)
def _read_coils(self, request_pdu): """handle read coils modbus function""" call_hooks("modbus.Slave.handle_read_coils_request", (self, request_pdu)) return self._read_digital(defines.COILS, request_pdu)
def __setitem__(self, item, value): """""" call_hooks("modbus.ModbusBlock.setitem", (self, item, value)) return self._data.__setitem__(item, value)
def close(self): """close the serial communication""" if self._serial.isOpen(): call_hooks("modbus_rtu.RtuServer.before_close", (self, )) self._serial.close() call_hooks("modbus_rtu.RtuServer.after_close", (self, ))
def _do_open(self): """Open the given serial port if not already opened""" if not self._serial.isOpen(): call_hooks("modbus_rtu.RtuMaster.before_open", (self, )) self._serial.open()
def _do_close(self): """Close the serial port if still opened""" if self._serial.is_open: self._serial.close() call_hooks("modbus_rtu.RtuMaster.after_close", (self, )) return True
def _do_init(self): """initialize the serial connection""" if not self._serial.is_open: call_hooks("modbus_rtu.RtuServer.before_open", (self, )) self._serial.open() call_hooks("modbus_rtu.RtuServer.after_open", (self, ))
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 execute( self, slave, function_code, starting_address, quantity_of_x=0, output_value=0, data_format="", expected_length=-1, ): """ Execute a modbus query and returns the data part of the answer as a tuple The returned tuple depends on the query function code. see modbus protocol specification for details data_format makes possible to extract the data like defined in the struct python module documentation """ pdu = "" is_read_function = False nb_of_digits = 0 # open the connection if it is not already done self.open() # Build the modbus pdu and the format of the expected data. # It depends of function code. see modbus specifications for details. if function_code == defines.READ_COILS or function_code == defines.READ_DISCRETE_INPUTS: is_read_function = True pdu = struct.pack(">BHH", function_code, starting_address, quantity_of_x) byte_count = quantity_of_x / 8 if (quantity_of_x % 8) > 0: byte_count += 1 nb_of_digits = quantity_of_x if not data_format: data_format = ">" + (byte_count * "B") if expected_length < 0: # No length was specified and calculated length can be used: # #slave + func + bytcodeLen + bytecode + crc1 + crc2 expected_length = byte_count + 5 elif function_code == defines.READ_INPUT_REGISTERS or function_code == defines.READ_HOLDING_REGISTERS: is_read_function = True pdu = struct.pack(">BHH", function_code, starting_address, quantity_of_x) if not data_format: data_format = ">" + (quantity_of_x * "H") if expected_length < 0: # No length was specified and calculated length can be used: # slave + func + bytcodeLen + bytecode x 2 + crc1 + crc2 expected_length = 2 * quantity_of_x + 5 elif (function_code == defines.WRITE_SINGLE_COIL) or (function_code == defines.WRITE_SINGLE_REGISTER): if function_code == defines.WRITE_SINGLE_COIL: if output_value != 0: output_value = 0xFF00 fmt = ">BH" + ("H" if output_value >= 0 else "h") pdu = struct.pack(fmt, function_code, starting_address, output_value) if not data_format: data_format = ">HH" if expected_length < 0: # No length was specified and calculated length can be used: # slave + func + adress1 + adress2 + value1+value2 + crc1 + crc2 expected_length = 8 elif function_code == defines.WRITE_MULTIPLE_COILS: byte_count = len(output_value) / 8 if (len(output_value) % 8) > 0: byte_count += 1 pdu = struct.pack(">BHHB", function_code, starting_address, len(output_value), byte_count) i, byte_value = 0, 0 for j in output_value: if j > 0: byte_value += pow(2, i) if i == 7: pdu += struct.pack(">B", byte_value) i, byte_value = 0, 0 else: i += 1 if i > 0: pdu += struct.pack(">B", byte_value) if not data_format: data_format = ">HH" if expected_length < 0: # No length was specified and calculated length can be used: # slave + func + adress1 + adress2 + outputQuant1 + outputQuant2 + crc1 + crc2 expected_length = 8 elif function_code == defines.WRITE_MULTIPLE_REGISTERS: byte_count = 2 * len(output_value) pdu = struct.pack(">BHHB", function_code, starting_address, len(output_value), byte_count) for j in output_value: fmt = "H" if j >= 0 else "h" pdu += struct.pack(">" + fmt, j) if not data_format: data_format = ">HH" if expected_length < 0: # No length was specified and calculated length can be used: # slave + func + adress1 + adress2 + outputQuant1 + outputQuant2 + crc1 + crc2 expected_length = 8 elif function_code == defines.READ_EXCEPTION_STATUS: pdu = struct.pack(">B", function_code) data_format = ">B" if expected_length < 0: # No length was specified and calculated length can be used: expected_length = 5 elif function_code == defines.DIAGNOSTIC: # SubFuncCode are in starting_address pdu = struct.pack(">BH", function_code, starting_address) if len(output_value) > 0: for j in output_value: # copy data in pdu pdu += struct.pack(">B", j) if not data_format: data_format = ">" + (len(output_value) * "B") if expected_length < 0: # No length was specified and calculated length can be used: # slave + func + SubFunc1 + SubFunc2 + Data + crc1 + crc2 expected_length = len(output_value) + 6 elif function_code == defines.READ_WRITE_MULTIPLE_REGISTERS: is_read_function = True byte_count = 2 * len(output_value) pdu = struct.pack( ">BHHHHB", function_code, starting_address, quantity_of_x, defines.READ_WRITE_MULTIPLE_REGISTERS, len(output_value), byte_count, ) for j in output_value: fmt = "H" if j >= 0 else "h" # copy data in pdu pdu += struct.pack(">" + fmt, j) if not data_format: data_format = ">" + (quantity_of_x * "H") if expected_length < 0: # No lenght was specified and calculated length can be used: # slave + func + bytcodeLen + bytecode x 2 + crc1 + crc2 expected_length = 2 * quantity_of_x + 5 else: raise ModbusFunctionNotSupportedError("The {0} function code is not supported. ".format(function_code)) # instantiate a query which implements the MAC (TCP or RTU) part of the protocol query = self._make_query() # add the mac part of the protocol to the request request = query.build_request(pdu, slave) # send the request to the slave retval = call_hooks("modbus.Master.before_send", (self, request)) if retval is not None: request = retval if self._verbose: LOGGER.debug(get_log_buffer("-> ", request)) self._send(request) call_hooks("modbus.Master.after_send", (self,)) if slave != 0: # receive the data from the slave response = self._recv(expected_length) retval = call_hooks("modbus.Master.after_recv", (self, response)) if retval is not None: response = retval if self._verbose: LOGGER.debug(get_log_buffer("<- ", response)) # extract the pdu part of the response response_pdu = query.parse_response(response) # analyze the received data (return_code, byte_2) = struct.unpack(">BB", response_pdu[0:2]) if return_code > 0x80: # the slave has returned an error exception_code = byte_2 raise ModbusError(exception_code) else: if is_read_function: # get the values returned by the reading function byte_count = byte_2 data = response_pdu[2:] if byte_count != len(data): # the byte count in the pdu is invalid raise ModbusInvalidResponseError( "Byte count is {0} while actual number of bytes is {1}. ".format(byte_count, len(data)) ) else: # returns what is returned by the slave after a writing function data = response_pdu[1:] # returns the data as a tuple according to the data_format # (calculated based on the function or user-defined) result = struct.unpack(data_format, data) if nb_of_digits > 0: digits = [] for byte_val in result: for i in xrange(8): if len(digits) >= nb_of_digits: break digits.append(byte_val % 2) byte_val = byte_val >> 1 result = tuple(digits) return result