def _handle(self, request): """handle a received sentence""" if self._verbose: LOGGER.debug(utils.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(utils.get_log_buffer("<--", response)) return response
def handle_request(self, request_pdu, broadcast=False): """ parse the request pdu, makes the corresponding action and returns the response pdu """ 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 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" % (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 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 """ 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 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" % (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 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 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 lenght was specified and calculated length can be used: expected_length = byte_count + 5 #slave + func + bytcodeLen + bytecode + crc1 + crc2 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 lenght was specified and calculated length can be used: expected_length = 2*quantity_of_x + 5 #slave + func + bytcodeLen + bytecode x 2 + crc1 + crc2 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 pdu = struct.pack(">BHH", function_code, starting_address, output_value) if not data_format: data_format = ">HH" if expected_length < 0: #No lenght was specified and calculated length can be used: expected_length = 8 #slave + func + adress1 + adress2 + value1+value2 + crc1 + crc2 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 lenght was specified and calculated length can be used: expected_length = 8 #slave + func + adress1 + adress2 + outputQuant1 + outputQuant2 + crc1 + crc2 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: pdu += struct.pack(">H", j) if not data_format: data_format = ">HH" if expected_length < 0: #No lenght was specified and calculated length can be used: expected_length = 8 #slave + func + adress1 + adress2 + outputQuant1 + outputQuant2 + crc1 + crc2 else: raise ModbusFunctionNotSupportedError("The %d function code is not supported. " % (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 <> None: request = retval if self._verbose: LOGGER.debug(utils.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 <> None: response = retval if self._verbose: LOGGER.debug(utils.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 %d while actual number of bytes is %d. " \ % (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
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 lenght was specified and calculated length can be used: expected_length = byte_count + 5 #slave + func + bytcodeLen + bytecode + crc1 + crc2 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 lenght was specified and calculated length can be used: expected_length = 2 * quantity_of_x + 5 #slave + func + bytcodeLen + bytecode x 2 + crc1 + crc2 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 pdu = struct.pack(">BHH", function_code, starting_address, output_value) if not data_format: data_format = ">HH" if expected_length < 0: #No lenght was specified and calculated length can be used: expected_length = 8 #slave + func + adress1 + adress2 + value1+value2 + crc1 + crc2 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 lenght was specified and calculated length can be used: expected_length = 8 #slave + func + adress1 + adress2 + outputQuant1 + outputQuant2 + crc1 + crc2 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: pdu += struct.pack(">H", j) if not data_format: data_format = ">HH" if expected_length < 0: #No lenght was specified and calculated length can be used: expected_length = 8 #slave + func + adress1 + adress2 + outputQuant1 + outputQuant2 + crc1 + crc2 else: raise ModbusFunctionNotSupportedError( "The %d function code is not supported. " % (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 <> None: request = retval if self._verbose: LOGGER.debug(utils.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 <> None: response = retval if self._verbose: LOGGER.debug(utils.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 %d while actual number of bytes is %d. " \ % (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