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 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 _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_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 (byte_value, ) = struct.unpack(">B", request_pdu[6+i]) for j in xrange(8): if byte_value & (1 << j): block[offset+i*8+j] = 1 else: block[offset+i*8+j] = 0 if count >= quantity_of_x: break count += 1 return struct.pack(">HH", starting_address, count)
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 byte_value & (1 << j): block[offset+i*8+j] = 1 else: block[offset+i*8+j] = 0 if count >= quantity_of_x: break count += 1 return struct.pack(">HH", starting_address, count)
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 <> None: request = retval response = self._handle(request) #send back the response retval = call_hooks("modbus_rtu.RtuServer.before_write", (self, response)) if retval <> None: response = retval if response: self._serial.write(response) time.sleep(3.5 * self._t0) except Exception, excpt: LOGGER.error("Error while handling request, Exception occurred: %s", excpt) call_hooks("modbus_rtu.RtuServer.on_error", (self, excpt))
def __setitem__(self, r, v): """""" call_hooks("modbus.ModbusBlock.setitem", (self, r, v)) ##We get a copy of the existing values that are about to change, ## implement the changes according to type, then put the new objects ## into the _data object item = self._data.__getitem__(r) try: for cnt in range(len(item)): if str(type(item[cnt])) == "<class 'point.Point'>": item[cnt] = item[cnt].set(v[cnt]) else: item[cnt] = v[cnt] return self._data.__setitem__(r, item) except TypeError: #This happens if item isn't iterable: if str(type(item)) == "<class 'point.Point'>": item = item.set(v) else: #print "BRDEBUG: In __setitem__. About to do the default", type(item) return self._data.__setitem__(r, v) finally: if hasattr(v,'__iter__') and (self.__getitem__(r) == list(v)): pass elif ((str(type(v)) == "<class 'point.Point'>") and self.__getitem__(r) == v.get() ): pass elif self.__getitem__(r) != v: raise Exception("Stored value %s not equal to %s" % (self.__getitem__(r), v) )
def _write_single_register(self, request_pdu): """execute modbus function 6""" call_hooks("modbus.Slave.handle_write_single_register_request", (self, request_pdu)) (data_address, value) = struct.unpack(">HH", request_pdu[1:5]) block, offset = self._get_block_and_offset(defines.HOLDING_REGISTERS, data_address, 1) block[offset] = value return request_pdu[1:] #returns echo of the command
def __getitem__(self, r): """""" call_hooks( "modbus.ModbusBlock.getitem", (self, r, self.block_name)) # Added by zhen.zhang on 2012-08-15 # 注:参数r可能是slice类型或int类型 return self._data.__getitem__(r)
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 return request_pdu[1:] #returns echo of the command
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()) call_hooks("modbus_tcp.TcpMaster.before_connect", (self, )) self._sock.connect((self._host, self._port)) call_hooks("modbus_tcp.TcpMaster.after_connect", (self, ))
def __setitem__(self, r, v): """""" #call_hooks("modbus.ModbusBlock.setitem", (self, r, v)) # Commented by zhen.zhang on 2012-08-14 call_hooks( "modbus.ModbusBlock.setitem", (self, r, v, self.block_name)) # Added by zhen.zhang on 2012-08-14 # 注:参数r可能是slice类型(此时v是list类型)或int类型(此时v是int类型) return self._data.__setitem__(r, v)
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) return request_pdu[1:] #returns echo of the command
def _write_single_coil(self, request_pdu): """execute modbus function 5""" fmt = "H" if self.unsigned else "h" call_hooks("modbus.Slave.handle_write_single_coil_request", (self, request_pdu)) (data_address, value) = struct.unpack(">H"+fmt, 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) return request_pdu[1:] #returns echo of the command
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 _recv(self, expected_length=-1): """Receive the response from the slave""" response = "" read_bytes = "dummy" while read_bytes: read_bytes = self._serial.read(1) #print repr(read_bytes),1111 response += read_bytes if expected_length >= 0 and len( response) >= 2 * expected_length + 1: #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 #response='3A 30 31 30 33 30 34 34 30 38 30 30 30 30 30 33 38 0D 0A' # response=':0103084080000038' retval = call_hooks("modbus_rtu.RtuMaster.after_recv", (self, response)) if retval <> None: return retval print response[1:-2], 1111 return binascii.a2b_hex(response[1:-2])
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 = "" length = 255 while len(response)<length: rcv_byte = self._sock.recv(1) if rcv_byte: response += rcv_byte if len(response) == 6: (tr_id, pr_id, to_be_recv_length) = struct.unpack(">HHH", response) length = to_be_recv_length + 6 else: break retval = call_hooks("modbus_tcp.TcpMaster.after_recv", (self, response)) if retval <> None: return response return response
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 block[offset+i] = struct.unpack(">H", request_pdu[6+2*i:8+2*i])[0] return struct.pack(">HH", starting_address, count)
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 _send(self, request): """Send request to the slave""" retval = call_hooks("modbus_rtu.RtuMaster.before_send", (self, request)) if retval <> None: request = retval self._serial.flushInput() self._serial.flushOutput() self._serial.write(request) time.sleep(3.5 * self._t0)
def _send(self, request): """Send request to the slave""" retval = call_hooks("modbus_tcp.TcpMaster.before_send", (self, request)) if retval <> None: request = retval try: utils.flush_socket(self._sock, 3) except Exception, 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)) raise ModbusNotConnectedError(msg)
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 _send(self, request): """Send request to the slave""" retval = call_hooks("modbus_rtu_over_tcp.RtuOverTcpMaster.before_send", (self, request)) if retval <> None: request = retval try: utils.flush_socket(self._sock, 3) except Exception, 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)) #raise ModbusNotConnectedError(msg) self._do_open();
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 #rxtime = time.time()#BRDEBUG #parse the request if request: retval = call_hooks("modbus_rtu.RtuServer.after_read", (self, request)) if retval <> None: request = retval response = self._handle(request) #send back the response retval = call_hooks("modbus_rtu.RtuServer.before_write", (self, response)) if retval <> None: response = retval if response: self._serial.write(response) #txtime = time.time() #BRDEBUG time.sleep(3.5 * self._t0) #BRDEBUG #self.avg = getattr(self, 'avg', 0) #self.n = getattr(self, 'n', 0) #self.avg = ((self.avg*self.n + (txtime - rxtime))/(self.n+1)) #self.n = self.n+1 #print "BRDEBUG Received request: %.6f"% rxtime #print "BRDEBUG response sent: %.6f"% txtime #print "BRDEBUG Rolling average: %.6f"% self.avg except Exception, 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 = "" read_bytes = "dummy" while read_bytes: read_bytes = self._serial.read(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 <> None: return retval return response
def _send(self, request): """Send request to the slave""" retval = call_hooks("modbus_rtu.RtuMaster.before_send", (self, request)) if retval <> None: request = retval self._serial.flushInput() self._serial.flushOutput() self._serial.setRTS(True) # MJC RS485 hack self._serial.write(request) time.sleep(1 * self._t0) #time.sleep(len(request) * self._t0) self._serial.setRTS(False) # MJC RS485 hack time.sleep(3.5 * self._t0) self._serial.flushInput()
def _recv(self, expected_length=-1, expected_address=None): """Receive the response from the slave. expected_address is used if there is noise or other nastiness on the serial line. It is used to look for the start of a response.""" response = "" # first_byte is made true when we find the exp. addr. in the data stream #If exp_addr isn't specified, we just take the first byte we get. first_byte = False if expected_address else True read_bytes = "dummy" start_time = time.time() while read_bytes: try: #BRFIX read_bytes = self._serial.read(1) if first_byte: pass #We're mid packet -- just keep processing the response elif read_bytes != expected_address: #ignore this byte and keep looking for expected_address if (time.time()-start_time) > self._serial.timeout: #This call should time out return '' continue elif read_bytes == expected_address: first_byte = True pass #keep going with response processing except OSError as error: if error.errno == 11: #This is error EAGAIN-> "Try again later" print ("BRDEBUG: Errno 11 caught. Trying again." + "Response: '%s'" % response) return response + self._recv(expected_length, expected_address) #Try again else: raise error response += read_bytes if expected_length>=0 and len(response)>=expected_length: #if the expected number of byte is received consider that the i #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 <> 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 = "" ##################### Modified by yaoming.lin on 2013-07-09 #################### is_ok = True #read the 5 bytes of the pdu message while (len(response) < 5) and is_ok: new_byte = self._sock.recv(1) if len(new_byte) == 0: is_ok = False else: response += new_byte if is_ok: #read the rest of the request #length = self._get_request_length(request) if ord(response[1]) < 7: # Modified by yaoming.lin on 2015-08-17 length = ord(response[2]) + 5 elif ord(response[1]) < 17: length = 8 else: length = 5 while (len(response) < length) and is_ok: new_byte = self._sock.recv(1) if len(new_byte) == 0: is_ok = False else: response += new_byte ################################################################################ retval = call_hooks("modbus_rtu_over_tcp.RtuOverTcpMaster.after_recv", (self, response)) if retval <> None: return response return response
def _recv(self, expected_length=-1): """Receive the response from the slave""" response = "" read_bytes = self._serial.read(1) while read_bytes: #read_bytes = self._serial.read(1) if len(response) == 0: # MJC RS485 hack, 1st rx byte may be crud throw it away if struct.unpack(">B", read_bytes[0])[0] < 248: response += read_bytes else: 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 read_bytes = self._serial.read(1) retval = call_hooks("modbus_rtu.RtuMaster.after_recv", (self, response)) if retval <> None: return retval return response
def _recv(self, expected_length=-1, expected_address=None): """ 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. Expected address is used for parsing dirty streams in RTU mode. This parameter is not necessary for TCP mode. """ response = "" length = 255 while len(response)<length: rcv_byte = self._sock.recv(1) if rcv_byte: response += rcv_byte if len(response) == 6: (tr_id, pr_id, to_be_recv_length) = struct.unpack(">HHH", response) length = to_be_recv_length + 6 else: break retval = call_hooks("modbus_tcp.TcpMaster.after_recv", (self, response)) if retval <> None: return response return response
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 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 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 _do_run(self): """called in a almost-for-ever loop by the server""" #check the status of every socket inputready, outputready, exceptready = select.select(self._sockets, [], [], 1.0) for sock in inputready: #handle data on each a socket 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 = "" 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 <> 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, 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 <> None: response = retval sock.send(response) except Exception, msg: is_ok = False LOGGER.error("Error while sending on socket %d, Exception occurred: %s", \ sock.fileno(), msg)
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 _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 _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_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)
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, 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 <> None: response = retval sock.send(response) except Exception, msg: is_ok = False LOGGER.error("Error while sending on socket %d, Exception occurred: %s", \ sock.fileno(), msg) except Exception, 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_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 _read_discrete_inputs(self, request_pdu): call_hooks("modbus.Slave.handle_read_discrete_inputs_request", (self, request_pdu)) """handle read coils modbus function""" return self._read_digital(defines.DISCRETE_INPUTS, request_pdu)
def __setitem__(self, r, v): """""" call_hooks("modbus.ModbusBlock.setitem", (self, r, v)) return self._data.__setitem__(r, v)
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)