def readMBinputs(clientIP, coil, number=1): from pymodbus.client.sync import ModbusTcpClient, ConnectionException client = ModbusTcpClient(clientIP) values = [] try: rawresult = client.read_discrete_inputs(coil, number) except ConnectionException: # print('we were unable to connect to the host') statuscode = 7 else: # print(rawresult) try: resultregisters = rawresult.bits[0:number] except AttributeError: statuscode = rawresult.exception_code else: statuscode = 0 values = resultregisters client.close() result = { 'message': messagefrommbstatuscode(statuscode), 'statuscode': statuscode, 'values': values } return result
def read_discrete_inputs(self, command): parser = argument_parser() command = 'read_discrete_inputs ' + command spec = parser.parse_args(command.split()) response = _ModbusClient.read_discrete_inputs( self, spec.address, spec.count, unit=spec.unit_id) return response
class Client: def __init__(self): self.client = None self.handle = None self.ip = '127.0.0.1' self.port = 502 def setup(self, config): self.handle = config self.ip = config['ipv4']['value'] self.port = config['port']['value'] self.client = ModbusTcpClient(self.ip, self.port) def execute(self, fc, addr, length=1, values=None): result = None if fc == 1: temp = self.client.read_coils(addr, length) result = [] for i in range(temp.byte_count): t2 = temp.bits[i * 16:(i + 1) * 16] result.append(''.join([str(int(x)) for x in t2])) elif fc == 2: temp = self.client.read_discrete_inputs(addr, length) result = [] for i in range(temp.byte_count): t2 = temp.bits[i * 16:(i + 1) * 16] result.append(''.join([str(int(x)) for x in t2])) elif fc == 3: temp = self.client.read_holding_registers(addr, length).registers result = ['{0:016b}'.format(x) for x in temp] elif fc == 4: temp = self.client.read_input_registers(addr, length).registers result = ['{0:016b}'.format(x) for x in temp] elif fc == 5: if values: self.client.write_coil(addr, values[0]) elif fc == 6: if values: self.client.write_register(addr, values[0]) elif fc == 15: if values: self.client.write_coils(addr, values) elif fc == 16: if values: self.client.write_registers(addr, values) return result def update_config(self, conf): self.ip = conf['ipv4']['value'] self.port = conf['port']['value'] self.client = ModbusTcpClient(self.ip, self.port) self.handle = conf def connect(self): return self.client.connect()
def post(self): query = self.reqparse.parse_args() client = ModbusClient(query['ip'], query['port']) client.connect() data = None start_address = query['start_address'] count = query['count'] if query['type_prefix'] == ModbusTypePrefix.DISCRETE_INPUT.value: data = client.read_discrete_inputs(start_address, count, unit=1) elif query['type_prefix'] == ModbusTypePrefix.COIL.value: data = client.read_coils(start_address, count, unit=1) elif query['type_prefix'] == ModbusTypePrefix.INPUT_REGISTER.value: data = client.read_input_registers(start_address, count, unit=1) elif query['type_prefix'] == ModbusTypePrefix.HOLDING_REGISTER.value: data = client.read_holding_registers(start_address, count, unit=1) client.close() result = [] print(data.registers) # if hasattr(data, 'bits'): # d = data.bits # else: # d = data.registers decoder = BinaryPayloadDecoder.fromRegisters(data.registers, byteorder=Endian.Big, wordorder=Endian.Big) # decoder.reset() # decoded = { # 'string': decoder.decode_string(8), # 'float': decoder.decode_32bit_float(), # '16uint': decoder.decode_16bit_uint(), # 'ignored': decoder.skip_bytes(2), # '8int': decoder.decode_8bit_int(), # 'bits': decoder.decode_bits(), # } e = decoder.decode_16bit_int() i = 0 while e is not False: try: print e result.append({'address': i + start_address, 'value': e}) i += 1 e = decoder.decode_16bit_int() except: e = False # for i, v in enumerate(d): # result.append({'address': i+start_address, 'value': v}) reg_fields = {'address': fields.Integer, 'value': fields.Integer} return {'registers': [marshal(reg, reg_fields) for reg in result]}
class ChattySensor(): def __init__(self, BIT_NUMBER=0, RESOLUTION=3): self.BIT_NUMBER = BIT_NUMBER self.client = ModbusTcpClient('127.0.0.1') self.encoded_message = [] self.last_value = '' self.delayed_time = 0 self.data = '' self.index = 0 self.RESOLUTION = RESOLUTION def get_data(self): return self.data def next_bit(self): self.index = self.index + 1 self.index = self.index % len(COVERT_MESSAGE) def get_measure(self): if random.randint(0, 3) == 0: return True result = self.client.read_discrete_inputs(self.BIT_NUMBER, 1) current_value = result.bits[0] if self.last_value != '' and self.last_value == True and current_value == False: current_time = int( str(time.time()).split('.')[1][:self.RESOLUTION]) if int(COVERT_MESSAGE[self.index]) == current_time % 2: delta_time = int(str( time.time()).split('.')[1][:3]) - self.delayed_time self.data = "\t".join([ "Chatty Sensor:", str(current_time), COVERT_MESSAGE[self.index], str(delta_time) ]) self.encoded_message.append( str(time.time()).split('.')[1][:self.RESOLUTION]) self.last_value = current_value else: current_value = self.last_value self.delayed_time = int(str(time.time()).split('.')[1][:3]) else: self.delayed_time = int(str(time.time()).split('.')[1][:3]) self.last_value = current_value return current_value def __del__(self): self.client.close()
def readMBinputs(clientIP, coil, number=1): from pymodbus.client.sync import ModbusTcpClient, ConnectionException client = ModbusTcpClient(clientIP) values = [] try: rawresult = client.read_discrete_inputs(coil, number) except ConnectionException: # print('we were unable to connect to the host') statuscode = 7 else: # print(rawresult) try: resultregisters = rawresult.bits[0:number] except AttributeError: statuscode = rawresult.exception_code else: statuscode = 0 values = resultregisters client.close() result = {'message': messagefrommbstatuscode(statuscode), 'statuscode': statuscode, 'values':values} return result
def activate(self): rospy.init_node('niryo_node', anonymous=False) while not rospy.is_shutdown(): if (self.activate_plc == "Activated"): rospy.loginfo( "Niryo will do other action, PLC will be deactivated") self.pub.publish("plc_deactivate") else: rospy.loginfo( "PLC hasn't been activated, Niryo will activate PLC") self.pub.publish("Activate_plc") client = ModbusClient("192.168.1.7", port=502) client.connect() UNIT = 0x1 print("Escritura de salidas") rq = client.write_coil(0, [True] * 8, unit=UNIT) rr = client.read_coils(0, 8, unit=UNIT) print("Las salidas Q0.0-Q0.7 ", rr.bits) print("Leer entradas") rr = client.read_discrete_inputs(0, 8, unit=UNIT) print("Las entradas I0.0-I0.7 son ", rr.bits) print("Escritura de un HR") rq = client.write_register(0, 15, unit=UNIT) rr = client.read_holding_registers(0, 1, unit=UNIT) print("HR0 = ", rr.registers) print("Escritura de varios HR") rq = client.write_registers(1, [35] * 10, unit=UNIT) rr = client.read_holding_registers(1, 10, unit=UNIT) print("HR0 = ", rr.registers) print("Escritura de varios HR") rq = client.write_registers(11, [43] * 5, unit=UNIT) rr = client.read_holding_registers(0, 15, unit=UNIT) print("HR0 = ", rr.registers) client.close() rospy.sleep(1)
from pymodbus.client.sync import ModbusTcpClient as ModbusClient client = ModbusClient('127.0.0.1', 5020) print(client) client.connect() #holding registers: rr = client.read_holding_registers(0, 2, unit=1) #read AO A = rr.registers print(A) #discrete inputs: rr = client.read_discrete_inputs( 0, 7, unit=1) #will reply 1 byte --> 8 bits被數到的打開遮罩,沒數到的被遮罩 B = rr.bits print(B) #coil read: rr = client.read_coils(0, 8, unit=1) #被數到的打開遮罩,沒數到的被遮罩 C = rr.bits print(C) #input registers: rr = client.read_input_registers(0, 2, unit=1) #read AI D = rr.registers print(D) print('==== AI ====') #-- FC04: read multi-input registers (3xxxx), for AI #read_input_registers(start_addr, count, unit=sid) rr = client.read_input_registers(0, 1, unit=1) #read AI print(rr, "value=", rr.registers) rr = client.read_input_registers(0, 2, unit=1) #read AI print(rr, "value=", rr.registers)
def run_sync_client(): # ------------------------------------------------------------------------# # choose the client you want # ------------------------------------------------------------------------# # make sure to start an implementation to hit against. For this # you can use an existing device, the reference implementation in the tools # directory, or start a pymodbus server. # # If you use the UDP or TCP clients, you can override the framer being used # to use a custom implementation (say RTU over TCP). By default they use # the socket framer:: # # client = ModbusClient('localhost', port=5020, framer=ModbusRtuFramer) # # It should be noted that you can supply an ipv4 or an ipv6 host address # for both the UDP and TCP clients. # # There are also other options that can be set on the client that controls # how transactions are performed. The current ones are: # # * retries - Specify how many retries to allow per transaction (default=3) # * retry_on_empty - Is an empty response a retry (default = False) # * source_address - Specifies the TCP source address to bind to # * strict - Applicable only for Modbus RTU clients. # Adheres to modbus protocol for timing restrictions # (default = True). # Setting this to False would disable the inter char timeout # restriction (t1.5) for Modbus RTU # # # Here is an example of using these options:: # # client = ModbusClient('localhost', retries=3, retry_on_empty=True) # ------------------------------------------------------------------------# client = ModbusClient('localhost', port=5020) # from pymodbus.transaction import ModbusRtuFramer # client = ModbusClient('localhost', port=5020, framer=ModbusRtuFramer) # client = ModbusClient(method='binary', port='/dev/ptyp0', timeout=1) # client = ModbusClient(method='ascii', port='/dev/ptyp0', timeout=1) # client = ModbusClient(method='rtu', port='/dev/ptyp0', timeout=1, # baudrate=9600) client.connect() # ------------------------------------------------------------------------# # specify slave to query # ------------------------------------------------------------------------# # The slave to query is specified in an optional parameter for each # individual request. This can be done by specifying the `unit` parameter # which defaults to `0x00` # ----------------------------------------------------------------------- # log.debug("Reading Coils") rr = client.read_coils(1, 1, unit=UNIT) log.debug(rr) # ----------------------------------------------------------------------- # # example requests # ----------------------------------------------------------------------- # # simply call the methods that you would like to use. An example session # is displayed below along with some assert checks. Note that some modbus # implementations differentiate holding/input discrete/coils and as such # you will not be able to write to these, therefore the starting values # are not known to these tests. Furthermore, some use the same memory # blocks for the two sets, so a change to one is a change to the other. # Keep both of these cases in mind when testing as the following will # _only_ pass with the supplied asynchronous modbus server (script supplied). # ----------------------------------------------------------------------- # log.debug("Write to a Coil and read back") rq = client.write_coil(0, True, unit=UNIT) rr = client.read_coils(0, 1, unit=UNIT) assert(not rq.isError()) # test that we are not an error assert(rr.bits[0] == True) # test the expected value log.debug("Write to multiple coils and read back- test 1") rq = client.write_coils(1, [True]*8, unit=UNIT) assert(not rq.isError()) # test that we are not an error rr = client.read_coils(1, 21, unit=UNIT) assert(not rr.isError()) # test that we are not an error resp = [True]*21 # If the returned output quantity is not a multiple of eight, # the remaining bits in the final data byte will be padded with zeros # (toward the high order end of the byte). resp.extend([False]*3) assert(rr.bits == resp) # test the expected value log.debug("Write to multiple coils and read back - test 2") rq = client.write_coils(1, [False]*8, unit=UNIT) rr = client.read_coils(1, 8, unit=UNIT) assert(not rq.isError()) # test that we are not an error assert(rr.bits == [False]*8) # test the expected value log.debug("Read discrete inputs") rr = client.read_discrete_inputs(0, 8, unit=UNIT) assert(not rq.isError()) # test that we are not an error log.debug("Write to a holding register and read back") rq = client.write_register(1, 10, unit=UNIT) rr = client.read_holding_registers(1, 1, unit=UNIT) assert(not rq.isError()) # test that we are not an error assert(rr.registers[0] == 10) # test the expected value log.debug("Write to multiple holding registers and read back") rq = client.write_registers(1, [10]*8, unit=UNIT) rr = client.read_holding_registers(1, 8, unit=UNIT) assert(not rq.isError()) # test that we are not an error assert(rr.registers == [10]*8) # test the expected value log.debug("Read input registers") rr = client.read_input_registers(1, 8, unit=UNIT) assert(not rq.isError()) # test that we are not an error arguments = { 'read_address': 1, 'read_count': 8, 'write_address': 1, 'write_registers': [20]*8, } log.debug("Read write registeres simulataneously") rq = client.readwrite_registers(unit=UNIT, **arguments) rr = client.read_holding_registers(1, 8, unit=UNIT) assert(not rq.isError()) # test that we are not an error assert(rq.registers == [20]*8) # test the expected value assert(rr.registers == [20]*8) # test the expected value # ----------------------------------------------------------------------- # # close the client # ----------------------------------------------------------------------- # client.close()
class dataValidation(threading.Thread): """docstring for dataValidation""" def __init__(self): super(dataValidation, self).__init__() self.threadID = 2 self.name = 'data_validation' self.stoprequest = threading.Event() self.begintest = False self.testrunning = False self.modbusclient = ModbusTcpClient(host='172.28.0.41', port=502) def run(self): start_test_data = {} current_data = {} while not self.stoprequest.isSet(): logger.info('%s' % time.strftime('%Y-%m-%d %H:%M:%S')) if self.testrunning: logger.info('Test is in-process') else: logger.info('Waiting for test to start') try: device_data = getJsonData('http://172.28.0.1/latest_data.json') except requests.exceptions.ConnectionError: logger.error('cannot connect to the site server, check all cable connections.') # environmental data try: licor_constant = -74.73 * 1000 current_data['irradiance'] = device_data['alpha']['env']['IRRA-1']['irradiance_1'] * licor_constant cell_temp = {'A': 1.129241e-3, 'B': 2.341077e-4, 'C': 8.775468e-8} cell_temp['v_ratio'] = 5 / device_data['alpha']['env']['cts-1']['cell_temp_01'] cell_temp['resistance'] = 24900/ (cell_temp['v_ratio'] - 1) cell_temp['temperature'] = (1 / (cell_temp['A'] + (cell_temp['B'] * math.log(cell_temp['resistance'])) + (cell_temp['C'] * pow(math.log(cell_temp['resistance']), 3)))) - 273.15 current_data['cell_temp'] = cell_temp['temperature'] logger.debug('%s: %s' % (device_data['alpha']['env']['ESI-1']['identifier'], device_data['alpha']['env']['ESI-1']['serial_number'])) logger.debug('irradiance: %.2f W/m2' % (current_data['irradiance'])) logger.debug('cell temperature: %.2f Degrees C' % (current_data['cell_temp'])) if self.begintest: start_test_data['irradiance'] = current_data['irradiance'] start_test_data['cell_temp'] = current_data['cell_temp'] if self.testrunning: logger.info('prev_irradiance: %.2f' % (start_test_data['irradiance'])) logger.info('prev_cell_temp: %.2f' % (start_test_data['cell_temp'])) logger.info('curr_irradiance: %.2f' % (current_data['irradiance'])) logger.info('curr_cell_temp: %.2f' % (current_data['cell_temp'])) if self.testrunning: # check env data if (-25 < (start_test_data['irradiance'] - current_data['irradiance']) < 25): logger.info('irradiance value within acceptable limits') logger.debug('difference is: %.2f' % (start_test_data['irradiance'] - current_data['irradiance'])) else: logger.error('irradiance value outside of acceptable limits') logger.debug('difference is: %.2f' % (start_test_data['irradiance'] - current_data['irradiance'])) if (-5 < (start_test_data['cell_temp'] - current_data['cell_temp']) < 5): logger.info('cell temperature value within acceptable limits') logger.debug('difference is: %.2f' % (start_test_data['cell_temp'] - current_data['cell_temp'])) else: logger.error('cell temperature value outside of acceptable limits') logger.debug('difference is: %.2f' % (start_test_data['cell_temp'] - current_data['cell_temp'])) except KeyError: logger.error('ENV data unavailable') # MBS-1 data try: mbs_data = [] for i in range(1, 11): mbs_data.append(device_data['alpha']['mbs1']['MBS-1']['value%02d' % (i)]) logger.debug('%s data: %s' % (device_data['alpha']['mbs1']['MBS-1']['identifier'], mbs_data)) # for i in range(1, 11): # logger.debug('%s data: %s' % (device_data['alpha']['mbs1']['MBS-1']['identifier'], device_data['alpha']['mbs1']['MBS-1']['value%02d' % (i)])) if self.testrunning: # check modbus data if checkModbusData('MBS-1', device_data['alpha']['mbs1']['MBS-1']): logger.info('MBS-1: Modbus Data VALID') else: logger.error('MBS-1: Modbus Data INVALID') except KeyError: logger.error('MBS-1 data unavailable') # MBS-2 data try: mbs_data = [] for i in range(1, 11): mbs_data.append(device_data['alpha']['mbs2']['MBS-2']['value%02d' % (i)]) logger.debug('%s data: %s' % (device_data['alpha']['mbs2']['MBS-2']['identifier'], mbs_data)) # for i in range(1, 11): # logger.debug('%s data: %s' % (device_data['alpha']['mbs2']['MBS-2']['identifier'], device_data['alpha']['mbs2']['MBS-2']['value%02d' % (i)])) if self.testrunning: if checkModbusData('MBS-2', device_data['alpha']['mbs2']['MBS-2']): logger.info('MBS-2: Modbus Data VALID') else: logger.error('MBS-2: Modbus Data INVALID') except KeyError: logger.error('MBS-2 data unavailable') #sixnet data try: # try: if not self.modbusclient.connect(): logger.error('failing to connect to sixnet Ethernet switch for Modbus query') sixnet_link_status = self.modbusclient.read_discrete_inputs(0, 8, unit=11) sixnet_pwr_status = self.modbusclient.read_discrete_inputs(29, 3, unit=11) if self.begintest: start_test_data['link_bits'] = sixnet_link_status.bits start_test_data['status_bits'] = sixnet_pwr_status.bits if self.testrunning: logger.debug('prev_link_bits: %s' % (start_test_data['link_bits'])) logger.debug('prev_status_bits: %s' % (start_test_data['status_bits'])) logger.debug('curr_link_bits: %s' % (sixnet_link_status.bits)) logger.debug('curr_status_bits: %s' % (sixnet_pwr_status.bits)) if self.testrunning: if start_test_data['link_bits'] != sixnet_link_status.bits: logger.error('sixnet "link" status has changed') elif start_test_data['status_bits'] != sixnet_pwr_status.bits: logger.error('sixnet "OK" status has changed') else: logger.info('sixnet switch status is good') # except KeyError: # logger.error('sixnet data unavailable') except pymodbus.exceptions.ConnectionException: logger.error('Sixnet Modbus query failed') if self.begintest: self.begintest = False self.testrunning = True time.sleep(5) os.system('clear') self.modbusclient.close() def join(self, timeout=None): if self.testrunning: logger.info('Stopping Test...') self.testrunning = False self.stoprequest.set() super(dataValidation, self).join(timeout) def starttest(self, timeout=None): self.begintest = True def stoptest(self, timeout=None): self.testrunning = False
numInputs = 10 startAddress = 1000 slaveID = 1 result = client.read_input_registers(startAddress, numInputs, slaveID) for i in range(0, len(totusTemps)): print totusTemps[i] + " = " + str( result.getRegister(i) / 10.0) + "\xb0C" # scaling is 10 # read alarms totusAlarms = ["ALARM/System/HL/State", "ALARM/System/HHLL/State"] numInputs = 2 startAddress = 100 slaveID = 1 result = client.read_discrete_inputs(startAddress, numInputs, slaveID) for i in range(0, len(totusAlarms)): print totusAlarms[i] + " = " + str(result.getBit(i)) # read DGA float32 gases totusDGA = [ "DGA/SourceA/CH4", "DGA/SourceA/C2H6", "DGA/SourceA/C2H4", "DGA/SourceA/C2H2", "DGA/SourceA/CO", "DGA/SourceA/CO2", "DGA/SourceA/O2", "DGA/SourceA/N2", "DGA/SourceA/H2", "DGA/SourceA/H2O", "DGA/SourceA/TDCG", "DGA/SourceA/THC" ] numInputs = 12 startAddress = 2200 slaveID = 1
def operate(address, port, request, operation): # fields of the form start = request.form['address'] value = request.form['value'] unitId = request.form['unitId'] # connection to the slave port = int(port) client = ModbusTcpClient(address, port) succeed = client.connect() html_template = operation + ".html" # no connection if not succeed: return render_template( html_template, address=address, port=port, form=request.form, error= "Connection has failed. Please, check IP address and port on URL or go back to homepage." ) # operation to launch if operation == 'read-discrete-inputs': result = client.read_discrete_inputs(address=int(start), count=int(value), unit=int(unitId)) elif operation == 'read-coils': result = client.read_coils(address=int(start), count=int(value), unit=int(unitId)) elif operation == 'read-holding-registers': result = client.read_holding_registers(address=int(start), count=int(value), unit=int(unitId)) elif operation == 'read-input-registers': result = client.read_input_registers(address=int(start), count=int(value), unit=int(unitId)) elif operation == 'write-coil': result = client.write_coil(int(start), int(value), unit=int(unitId)) elif operation == 'write-holding-register': result = client.write_register(int(start), int(value), unit=int(unitId)) client.close() # error in operation if result.isError(): return render_template(html_template, address=address, port=port, form=request.form, error=result) # display results else: # op : read discrete values if operation in ["read-discrete-inputs", "read-coils"]: visual_result = [] for b in result.bits: if b: visual_result.append('On') else: visual_result.append('Off') flash(str(visual_result)) # op : read registers elif operation in ["read-holding-registers", "read-input-registers"]: flash(str(result.registers)) # op : write values elif operation in ['write-coil', 'write-holding-register']: flash("Success!") return render_template(html_template, form=request.form, address=address, port=port)
class ModbusWrapperClient(): def __init__(self, modbusUnitAddress, maxRegsRead, modbusTimeout, endian="little"): self.client = None self.modbusAddress = modbusUnitAddress self.bufferStart = 0 self.bufferEnd = 0 self.data_buffer = None self.maxRegsRead = maxRegsRead self.timeout = modbusTimeout if endian == 'auto': self.endian = Endian.Auto elif endian == 'little': self.endian = Endian.Little else: self.endian = Endian.Big self.isConnected = False self.validaddresses = None self.validaddresses_write = None def openConnectionSerial(self, modbusSerialPort, modbusMethon, modbusByte, modbusStopBits, modbusParity, modbusBaudrate, modbusTimeout): self.client = ModbusSerialClient(method=modbusMethon, port=modbusSerialPort, stopbits=modbusStopBits, bytesize=modbusByte, parity=modbusParity, baudrate=modbusBaudrate, timeout=modbusTimeout) self.tryReconnect() def openConnectionTCP(self, modbusHost, modbusPort): self.client = ModbusTcpClient(modbusHost, modbusPort) self.tryReconnect() def closeConnection(self): if self.isConnected is True: self.client.close() def tryReconnect(self): retry = MODBUS_CONNECTIONRETRY + 1 for i in range(1, retry): if self.isConnected is False: self.isConnected = self.client.connect() break log.debug("riconessione %s/%s" % (i, retry)) def load_valid_addresses(self, lista=None): log.debug("load_valid_addresses") self.validaddresses = lista def check_address(self, address): if self.validaddresses is None: return True ret = True if (address in self.validaddresses) else False return ret def readRegisters(self, address, count, mb_type='uint16', mb_funcall=3, force=False): if self.isConnected is False: self.tryReconnect() tmp = None if (self.check_address(address) is True) or (force is True): try: if mb_funcall == 1: # Read Coil Status (FC=01) result = self.client.read_coils(address, count=count, unit=self.modbusAddress) tmp = result.bits elif mb_funcall == 2: # Read Dscrete Input (FC=02) result = self.client.read_discrete_inputs( address, count=count, unit=self.modbusAddress) tmp = result.bits elif mb_funcall == 3: # Read Holding Registers (FC=03) result = self.client.read_holding_registers( address, count=count, unit=self.modbusAddress) if result != None: tmp = result.registers elif mb_funcall == 4: # Read Input Registers (FC=04) result = self.client.read_input_registers( address, count=count, unit=self.modbusAddress) #tmp = result.bits if result != None: tmp = result.registers #log.debug("out: %s" % tmp) else: log.debug("Function call not supported: %s" % mb_funcall) result = None tmp = result except Exception as e: log.exception(e) return tmp def check_address_write(self, address): if self.validaddresses_write is None: return True ret = True if (address in self.validaddresses_write) else False return ret def writeRegisters(self, address, value, mb_funcall=5, force=False, skip_encode=False): # Refer to "libmodbus" C library: http://libmodbus.org/documentation/ # log.info('writeRegisters(address="%s", value="%s", mb_funcall="%s"' % (address, value, mb_funcall)) if self.isConnected is False: self.tryReconnect() result = None if (self.check_address_write(address) is True) or (force is True): try: if mb_funcall == 5: # Single Coil (FC=05) => modbus_write_bit result = self.client.write_coil(address, value, unit=self.modbusAddress) elif mb_funcall == 6: # Single Register (FC=06) result = self.client.write_register( address, value, unit=self.modbusAddress, skip_encode=skip_encode) elif mb_funcall == 15: # Multiple Coils (FC=15) => modbus_write_bits result = self.client.write_coils(address, value, unit=self.modbusAddress) elif mb_funcall == 16: # Multiple Registers (FC=16) result = self.client.write_registers( address, value, unit=self.modbusAddress, skip_encode=skip_encode) else: log.warn("Function call not supported: %s" % mb_funcall) except Exception as e: log.exception(e) return result def encode_field(self, value, mb_type='unit16'): builder = BinaryPayloadBuilder(endian=self.endian) if mb_type == 'bit' or mb_type == 'bits': builder.add_bits(value) elif mb_type == 'uint8': builder.add_8bit_uint(value) elif mb_type == 'uint16': builder.add_16bit_uint(value) elif mb_type == 'uint32': builder.add_32bit_uint(value) elif mb_type == 'uint64': builder.add_64bit_uint(value) elif mb_type == 'int8': builder.add_8bit_int(value) elif mb_type == 'int16': builder.add_16bit_int(value) elif mb_type == 'int32': builder.add_32bit_int(value) elif mb_type == 'int64': builder.add_64bit_int(value) elif mb_type == 'float32': builder.add_32bit_float(value) elif mb_type == 'float64': builder.add_64bit_float(value) elif mb_type == 'string' or mb_type == 'str': builder.add_string(value) else: log.warn('Not supported DataType: "%s"' % mb_type) return builder.build() def readRegistersAndDecode(self, registri, counter, mb_type='uint16', mb_funcall=3, force=False): tmp = None if (self.check_address(registri) is True) or (force is True): ret = self.readRegisters(registri, counter, mb_type, mb_funcall, force) if ret is not None: tmp = self.decode(ret, counter, mb_type, mb_funcall) return tmp def decode(self, raw, size, mb_type, mb_funcall=3): log.debug('decode param (raw=%s, size=%s, mb_type=%s, mb_funcall=%s)' % (raw, size, mb_type, mb_funcall)) if mb_funcall == 1: # Read Coil Status (FC=01) log.debug("decoder FC1 (raw: %s)" % raw) decoder = BinaryPayloadDecoder.fromCoils(raw, endian=self.endian) elif mb_funcall == 2: # Read Discrete Input (FC=02) log.debug("decoder FC2 (raw: %s)" % raw) decoder = BinaryPayloadDecoder(raw, endian=self.endian) elif mb_funcall == 3: # Read Holding Registers (FC=03) log.debug("decoder FC3 (raw: %s)" % raw) decoder = BinaryPayloadDecoder.fromRegisters(raw, endian=self.endian) elif mb_funcall == 4: # Read Input Registers (FC=04) log.debug("decoder stub FC4 (raw: %s)" % raw) decoder = BinaryPayloadDecoder(raw, endian=self.endian) else: log.debug("Function call not supported: %s" % mb_funcall) decoder = None result = "" if mb_type == 'bitmap': if size == 1: mb_type = 'int8' elif size == 2: mb_type = 'int16' elif size == 2: mb_type = 'int32' elif size == 4: mb_type = 'int64' if decoder is None: log.debug("decode none") result = raw else: try: if mb_type == 'string' or mb_type == 'utf8': result = decoder.decode_string(size) #elif mb_type == 'bitmap': # result = decoder.decode_string(size) elif mb_type == 'datetime': result = decoder.decode_string(size) elif mb_type == 'uint8': result = int(decoder.decode_8bit_uint()) elif mb_type == 'int8': result = int(decoder.decode_8bit_int()) elif mb_type == 'uint16': result = int(decoder.decode_16bit_uint()) elif mb_type == 'int16': result = int(decoder.decode_16bit_int()) elif mb_type == 'uint32': result = int(decoder.decode_32bit_uint()) elif mb_type == 'int32': result = int(decoder.decode_32bit_int()) elif mb_type == 'uint64': result = int(decoder.decode_64bit_uint()) elif mb_type == 'int64': result = int(decoder.decode_64bit_int()) elif mb_type == 'float32' or mb_type == 'float': result = float(decoder.decode_32bit_float()) elif mb_type == 'float64': result = float(decoder.decode_64bit_float()) elif mb_type == 'bit': result = int(decoder.decode_bits()) elif mb_type == 'bool': result = bool(raw[0]) elif mb_type == 'raw': result = raw[0] else: result = raw except ValueError as e: log.exception(e) result = raw return result def read1(self, startreg, mb_type, mb_funcall=3): return self.readRegisters(startreg, 1, mb_type, mb_funcall) def read2(self, startreg, mb_type, mb_funcall=3): return self.readRegisters(startreg, 2, mb_type, mb_funcall) def read3(self, startreg, mb_type, mb_funcall=3): return self.readRegisters(startreg, 3, mb_type, mb_funcall) def read4(self, startreg, mb_type, mb_funcall=3): return self.readRegisters(startreg, 4, mb_type, mb_funcall) def buffer_print(self): if not self.bufferReady(): log.debug('BUFFER empty ---') else: text = 'BUFFER [%s-%s]: ' % (self.bufferStart, self.bufferEnd) i = self.bufferStart for item in self.data_buffer: text += "%s(%s) " % (i, item) i += 1 log.debug(text) def bufferedReadRegisters(self, startreg, counter, mb_type='uint16', mb_funcall=3): log.debug( 'bufferedReadRegisters param (startreg=%s, counter=%s, mb_type=%s, mb_funcall=%s)' % (startreg, counter, mb_type, mb_funcall)) valido = False offset = self.maxRegsRead while (offset >= 0) and (valido != True): valido = self.check_address(startreg + offset) if valido is True: self.data_buffer = self.readRegisters(startreg, offset, mb_type, mb_funcall) if self.data_buffer != None: self.bufferStart = startreg self.bufferEnd = startreg + len(self.data_buffer) - 1 offset -= 1 self.buffer_print() return self.bufferReady() def bufferReady(self): return True if (self.data_buffer is not None) else False def bufferCleanup(self): if self.bufferReady(): self.data_buffer = None def inBuffer(self, startreg, conteggio): if not self.bufferReady(): return False return True if ((startreg >= self.bufferStart) and ((startreg + conteggio) <= self.bufferEnd)) else False def cachedRead(self, startreg, counter, mb_type='uint16', mb_funcall=3): log.debug( 'cachedRead param (startreg=%s, counter=%s, mb_type=%s, mb_funcall=%s)' % (startreg, counter, mb_type, mb_funcall)) if not self.bufferReady(): self.bufferedReadRegisters(startreg, counter, mb_type, mb_funcall) if not self.inBuffer(startreg, counter): self.bufferedReadRegisters(startreg, counter, mb_type, mb_funcall) regs = [] i = 0 while i < counter: regs.append(self.data_buffer[startreg - self.bufferStart + i]) i += 1 return self.decode(regs, counter, mb_type, mb_funcall) def cachedRead1(self, startreg, mb_type='uint16', mb_funcall=3): if not self.bufferReady(): self.bufferedReadRegisters(startreg, 1, mb_type, mb_funcall) if not self.inBuffer(startreg, 1): self.bufferedReadRegisters(startreg, 1, mb_type, mb_funcall) return self.decode(self.data_buffer[startreg - self.bufferStart], 1, mb_type) def cachedRead2(self, startreg, mb_type='uint16', mb_funcall=3): if not self.bufferReady(): self.bufferedReadRegisters(startreg, 2, mb_type, mb_funcall) if not self.inBuffer(startreg, 2): self.bufferedReadRegisters(startreg, 2, mb_type, mb_funcall) regs = [] regs.append(self.data_buffer[startreg - self.bufferStart]) regs.append(self.data_buffer[startreg - self.bufferStart + 1]) return self.decode(regs, 2, mb_type) def cachedRead3(self, startreg, mb_type='uint16', mb_funcall=3): if not self.bufferReady(): self.bufferedReadRegisters(startreg, 3, mb_type, mb_funcall) if not self.inBuffer(startreg, 3): self.bufferedReadRegisters(startreg, 3, mb_type, mb_funcall) regs = [] regs.append(self.data_buffer[startreg - self.bufferStart]) regs.append(self.data_buffer[startreg - self.bufferStart + 1]) regs.append(self.data_buffer[startreg - self.bufferStart + 2]) return self.decode(regs, 3, mb_type) def cachedRead4(self, startreg, mb_type='uint16', mb_funcall=3): if not self.bufferReady(): self.bufferedReadRegisters(startreg, 4, mb_type, mb_funcall) if not self.inBuffer(startreg, 4): self.bufferedReadRegisters(startreg, 4, mb_type, mb_funcall) regs = [] regs.append(self.data_buffer[startreg - self.bufferStart]) regs.append(self.data_buffer[startreg - self.bufferStart + 1]) regs.append(self.data_buffer[startreg - self.bufferStart + 2]) regs.append(self.data_buffer[startreg - self.bufferStart + 3]) return self.decode(regs, 4, mb_type)
def testReadDiscreteInputs(self): client = ModbusTcpClient("localhost", 502) result = client.read_discrete_inputs(1, 10, unit=1) self.assertIsNotNone(result) client.close()
def run_sync_client(): # ------------------------------------------------------------------------# # choose the client you want # ------------------------------------------------------------------------# # make sure to start an implementation to hit against. For this # you can use an existing device, the reference implementation in the tools # directory, or start a pymodbus server. # # If you use the UDP or TCP clients, you can override the framer being used # to use a custom implementation (say RTU over TCP). By default they use # the socket framer:: # # client = ModbusClient('localhost', port=5020, framer=ModbusRtuFramer) # # It should be noted that you can supply an ipv4 or an ipv6 host address # for both the UDP and TCP clients. # # There are also other options that can be set on the client that controls # how transactions are performed. The current ones are: # # * retries - Specify how many retries to allow per transaction (default=3) # * retry_on_empty - Is an empty response a retry (default = False) # * source_address - Specifies the TCP source address to bind to # # Here is an example of using these options:: # # client = ModbusClient('localhost', retries=3, retry_on_empty=True) # ------------------------------------------------------------------------# client = ModbusClient('localhost', port=5020) # client = ModbusClient(method='ascii', port='/dev/pts/2', timeout=1) # client = ModbusClient(method='rtu', port='/dev/ttyp0', timeout=1) client.connect() # ------------------------------------------------------------------------# # specify slave to query # ------------------------------------------------------------------------# # The slave to query is specified in an optional parameter for each # individual request. This can be done by specifying the `unit` parameter # which defaults to `0x00` # ----------------------------------------------------------------------- # log.debug("Reading Coils") rr = client.read_coils(1, 1, unit=0x01) print(rr) # ----------------------------------------------------------------------- # # example requests # ----------------------------------------------------------------------- # # simply call the methods that you would like to use. An example session # is displayed below along with some assert checks. Note that some modbus # implementations differentiate holding/input discrete/coils and as such # you will not be able to write to these, therefore the starting values # are not known to these tests. Furthermore, some use the same memory # blocks for the two sets, so a change to one is a change to the other. # Keep both of these cases in mind when testing as the following will # _only_ pass with the supplied async modbus server (script supplied). # ----------------------------------------------------------------------- # log.debug("Write to a Coil and read back") rq = client.write_coil(0, True, unit=UNIT) rr = client.read_coils(0, 1, unit=UNIT) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.bits[0] == True) # test the expected value log.debug("Write to multiple coils and read back- test 1") rq = client.write_coils(1, [True]*8, unit=UNIT) assert(rq.function_code < 0x80) # test that we are not an error rr = client.read_coils(1, 21, unit=UNIT) assert(rr.function_code < 0x80) # test that we are not an error resp = [True]*21 # If the returned output quantity is not a multiple of eight, # the remaining bits in the final data byte will be padded with zeros # (toward the high order end of the byte). resp.extend([False]*3) assert(rr.bits == resp) # test the expected value log.debug("Write to multiple coils and read back - test 2") rq = client.write_coils(1, [False]*8, unit=UNIT) rr = client.read_coils(1, 8, unit=UNIT) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.bits == [False]*8) # test the expected value log.debug("Read discrete inputs") rr = client.read_discrete_inputs(0, 8, unit=UNIT) assert(rq.function_code < 0x80) # test that we are not an error log.debug("Write to a holding register and read back") rq = client.write_register(1, 10, unit=UNIT) rr = client.read_holding_registers(1, 1, unit=UNIT) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.registers[0] == 10) # test the expected value log.debug("Write to multiple holding registers and read back") rq = client.write_registers(1, [10]*8, unit=UNIT) rr = client.read_holding_registers(1, 8, unit=UNIT) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.registers == [10]*8) # test the expected value log.debug("Read input registers") rr = client.read_input_registers(1, 8, unit=UNIT) assert(rq.function_code < 0x80) # test that we are not an error arguments = { 'read_address': 1, 'read_count': 8, 'write_address': 1, 'write_registers': [20]*8, } log.debug("Read write registeres simulataneously") rq = client.readwrite_registers(unit=UNIT, **arguments) rr = client.read_holding_registers(1, 8, unit=UNIT) assert(rq.function_code < 0x80) # test that we are not an error assert(rq.registers == [20]*8) # test the expected value assert(rr.registers == [20]*8) # test the expected value # ----------------------------------------------------------------------- # # close the client # ----------------------------------------------------------------------- # client.close()
if args.op == 'w': if args.fun == 'hr': rq = client.write_register(args.adr, args.val, unit=args.id) elif args.fun == 'co': rq = client.write_coil(args.adr, args.val, unit=args.id) print "Write function return code : " + str(rq.function_code) else : if args.fun == 'hr': rr = client.read_holding_registers(args.adr, args.qty, unit=args.id) if rr != None: results = rr.registers elif args.fun == 'ri': rr = client.read_input_registers(args.adr, args.qty, unit=args.id) if rr != None: results = rr.registers elif args.fun == 'di': rr = client.read_discrete_inputs(args.adr, args.qty, unit=args.id) if rr != None: results = rr.bits elif args.fun == 'co': rr = client.read_coils(args.adr, args.qty, unit=args.id) if rr != None: results = rr.bits if results is not None: print "[SUCCESS] value:" print " " + ",".join(hex(n) for n in results) else: print("[ERROR]") client.close() except: print("Unknown Exception") raise
def youbot_modbus_server_service(): global station_states, button_states, modbus_data_validity connection_error_count = 0 rospy.init_node('youbot_modbus_server') # Grab all of the parameters from the launch file mb_server_ip = rospy.get_param("~modbus_server_ip") mb_server_port = rospy.get_param("~modbus_server_port") mb_station_status_topic = rospy.get_param("~station_status_topic") mb_button_status_topic = rospy.get_param("~button_status_topic") plc_polling_rate = rospy.Rate(rospy.get_param("~plc_polling_rate_hz")) # Update the array sizes station_states = [False] * plc_num_stations button_states = [False] * (plc_num_buttons * 2) # Create two services for nodes to call and get current sensor & button states s = rospy.Service(mb_station_status_topic, YoubotModbusSensorMsg, handle_station_request) t = rospy.Service(mb_button_status_topic, YoubotModbusButtonMsg, handle_button_request) # Attempt to connect to the PLC Modbus server try: # Connect to the Modbus Server client = ModbusClient(mb_server_ip, mb_server_port) if client.connect() == False: raise # We failed to connect to the PLC Modbus server. Report error and shutdown. except: except_desc = "Could not initiate connection with Modbus server" rospy.logerr(except_desc) rospy.signal_shutdown(except_desc) # We suceeded, so report the connection and continue else: rospy.loginfo("Youbot Modbus service successfully connected to: " + str(mb_server_ip) + ":" + str(mb_server_port)) rospy.loginfo("Awaiting ROS service calls...") # Main loop for polling the Modbus Server on the PLC - plc_polling_rate param is in launch file while not rospy.is_shutdown(): # Request data from the PLC Modbus server try: # Get the current station values (every two values is a ball/done pair) returned_station_states = client.read_discrete_inputs(0x8000, plc_num_stations*2) # Get the current button values (every two values is a button/indicator pair) (buttons follow stations in PLC addresses) returned_button_states = client.read_discrete_inputs((0x8000+(plc_num_stations*2)), plc_num_buttons*2) # Raise an exception if the server did not respond with the message type we were expecting if type(returned_station_states) != bit_read_message.ReadDiscreteInputsResponse or \ type(returned_button_states ) != bit_read_message.ReadDiscreteInputsResponse: raise # If the data was invalid on a previous iteration, we need to report the transition if modbus_data_validity == False: connection_error_count = 0 modbus_data_validity = True rospy.loginfo("PLC communication has been restored. Data validity: " + str(modbus_data_validity)) # We did not get a reply from the PLC Modbus server, so count it and report a warning message. except exceptions.ConnectionException: modbus_data_validity = False connection_error_count += 1 except_desc = "PLC did not respond to data request. Data validity: " + str(modbus_data_validity) rospy.logwarn(except_desc) # The PLC Modbus server did not respond with the type of message we were expecting. except: except_desc = "An exception was raised while attempting to read from the Modbus server" rospy.logerr(except_desc) rospy.signal_shutdown(except_desc) # Data is GOOD, so let's process it. else: # Report to all service calls that the data is GOOD modbus_data_validity = True # We got a good response, so update the arrays update_sensor_array(returned_station_states, plc_num_stations) update_button_array(returned_button_states, plc_num_buttons) # If we have not heard back from the PLC Modbus server in # (connection_error_count * (1/plc_polling_rate) seconds, so we should shutdown. if connection_error_count >= 10: except_desc = "Could not communicate with PLC. Error count exceeded: "+ str(connection_error_count) + " failures" rospy.logerr(except_desc) rospy.signal_shutdown(except_desc) # Wait until the next Modbus poll plc_polling_rate.sleep()
# ! You need to launch the server first ! from pymodbus.client.sync import ModbusTcpClient import time print "--- START" client = ModbusTcpClient('localhost', port=5020) client.connect() print "Connected to modbus server" rr = client.read_input_registers(400, 9) print rr.registers rr = client.read_discrete_inputs(100, 6) print rr.bits # Set digital IO mode - output client.write_coil(0, False) client.write_coil(3, False) # Set digital IO state client.write_coil(100, True) client.write_coil(103, False) time.sleep(0.1) rr = client.read_discrete_inputs(100, 6) print rr.bits
class Modbus_Driver(object): def __init__(self, config_file, config_section=None): # Use a config section if the config file is being shared with other # parts of a project. if (config_section==None): modbus_section = 'modbus' with open(config_file) as f: # use safe_load instead load for security reasons modbusConfig = yaml.safe_load(f) self.MODBUS_TYPE = modbusConfig[modbus_section]['modbus_type'] # Check to see if unit id is a list, if it is then set flag that it is a # list self.UNIT_ID = modbusConfig[modbus_section]['UNIT_ID'] if isinstance(self.UNIT_ID, list): self.UNIT_ID_LIST = self.UNIT_ID #Set default UNIT_ID as first UNIT_ID in list self.UNIT_ID = self.UNIT_ID_LIST[0] else: # Make a unit id list from the non-list definition for compatibility # reasons of previous configs. This also eliminates the possibility # of error in calling get_data_all_devices() on a config with a non # list definition self.UNIT_ID_LIST = [] self.UNIT_ID_LIST.append(self.UNIT_ID) # Start logging if enabled in config self.LOGGING_FLAG = modbusConfig[modbus_section]['enable_logging'] if self.LOGGING_FLAG == True: #Start client logging for trouble shooting logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) # Start appropriate client based on the type specified in the config if self.MODBUS_TYPE == 'serial': print('serial') self.METHOD = modbusConfig[modbus_section]['method'] self.SERIAL_PORT = modbusConfig[modbus_section]['serial_port'] self.STOPBITS = modbusConfig[modbus_section]['stopbits'] self.BYTESIZE = modbusConfig[modbus_section]['bytesize'] self.PARITY = modbusConfig[modbus_section]['parity'] self.BAUDRATE = modbusConfig[modbus_section]['baudrate'] elif self.MODBUS_TYPE == 'tcp': self.IP_ADDRESS = modbusConfig[modbus_section]['ip'] self.PORT = modbusConfig[modbus_section]['tcp_port'] else: print("Invalid modbus type") exit() # Set the byte order as big or little endian if modbusConfig[modbus_section]['byte_order'] == 'big': self.BYTE_ORDER = Endian.Big elif modbusConfig[modbus_section]['byte_order'] == 'little': self.BYTE_ORDER = Endian.Little else: print("invalid byte order") # change to except later exit() # Set the word order as big or little endian if modbusConfig[modbus_section]['word_order'] == 'big': print("big") self.WORD_ORDER = Endian.Big elif modbusConfig[modbus_section]['word_order'] == 'little': self.WORD_ORDER = Endian.Little else: print("invalid byte order") # change to except later exit() # Read in all registers specified in the YAML config self.coil_register_dict = modbusConfig[modbus_section]['coil_registers'] self.discrete_register_dict = modbusConfig[modbus_section]['discrete_registers'] self.holding_register_dict = modbusConfig[modbus_section]['holding_registers'] self.input_register_dict = modbusConfig[modbus_section]['input_registers'] # Apply register offset if specified self.OFFSET_REGISTERS = modbusConfig[modbus_section]['OFFSET_REGISTERS'] for key in self.holding_register_dict: self.holding_register_dict[key][0] -= self.OFFSET_REGISTERS def initialize_modbus(self): """ initalize correct client according to type specified in config: 'tcp' or 'serial' """ if self.MODBUS_TYPE == 'serial': self.client= ModbusSerialClient(method = self.METHOD, port=self.SERIAL_PORT,stopbits = self.STOPBITS, bytesize = self.BYTESIZE, parity = self.PARITY, baudrate= self.BAUDRATE) connection = self.client.connect() if self.MODBUS_TYPE == 'tcp': self.client = ModbusTcpClient(self.IP_ADDRESS,port=self.PORT) def write_single_register(self,register,value, unit=None): """ :param register: address of reigster to write :param value: Unsigned short :returns: Status of write """ if (unit is None): unit = self.UNIT_ID response = self.client.write_register(register,value,unit) return response def write_register(self,register_name,value, unit=None): """ :param register_name: register key from holding register dictionary generated by yaml config :param value: value to write to register :returns: -- Nothing """ if (unit is None): unit = self.UNIT_ID builder = BinaryPayloadBuilder(byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) if (self.holding_register_dict[register_name][1] == '8int'): builder.add_8bit_int(value) elif (self.holding_register_dict[register_name][1] == '8uint'): builder.add_8bit_uint(value) elif (self.holding_register_dict[register_name][1] == '16int'): builder.add_16bit_int(value) elif (self.holding_register_dict[register_name][1] == '16uint'): builder.add_16bit_uint(value) elif (self.holding_register_dict[register_name][1] == '32int'): builder.add_32bit_int(value) elif (self.holding_register_dict[register_name][1] == '32uint'): builder.add_32bit_uint(value) elif (self.holding_register_dict[register_name][1] == '32float'): builder.add_32bit_float(value) elif (self.holding_register_dict[register_name][1] == '64int'): builder.add_64bit_int(value) elif (self.holding_register_dict[register_name][1] == '64uint'): builder.add_64bit_uint(value) elif (self.holding_register_dict[register_name][1] == '64float'): builder.add_64bit_float(value) else: print("Bad type") exit() payload = builder.build() self.client.write_registers(self.holding_register_dict[register_name][0], payload, skip_encode=True, unit = self.UNIT_ID) def write_coil(self,register,value, unit=None): """ :param register_name: register key from holding register dictionary generated by yaml config :param value: value to write to register :returns: """ if (unit is None): unit = self.UNIT_ID response = self.client.write_coil(register,value,unit) return response def read_coil(self,register, unit=None): """ :param register: coil register address to read :returns: value stored in coil register """ if (unit is None): unit = self.UNIT_ID rr = self.client.read_coils(register, 1, unit=unit) return rr.bits[0] def read_discrete(self,register,unit=None): """ :param register: discrete register address to read :returns: value stored in coil register """ if (unit is None): unit = self.UNIT_ID rr = self.client.read_discrete_inputs(register, count=1,unit=unit) return rr.bits[0] def read_register_raw(self,register,length, unit=None): """ :param register: base holding register address to read :param length: amount of registers to read to encompass all of the data necessary for the type :returns: A deferred response handle """ if (unit is None): unit = self.UNIT_ID response = self.client.read_holding_registers(register,length,unit= self.UNIT_ID) return response def read_input_raw(self,register,length, unit=None): """ :param register: base input register address to read :param length: amount of registers to read to encompass all of the data necessary for the type :returns: A deferred response handle """ if (unit is None): unit = self.UNIT_ID response = self.client.read_input_registers(register,length,unit= self.UNIT_ID) return response #TODO ADD unit id here! and to decode input register def decode_register(self,register,type, unit=None): """ :param register: holding register address to retrieve :param type: type to interpret the registers retrieved as :returns: data in the type specified Based on the type provided, this function retrieves the values contained in the register address specfied plus the amount necessary to encompass the the type. For example, if 32int is specified with an address of 200 the registers accessed would be 200 and 201. The types accepted are listed in the table below along with their length | Type | Length (registers) | | ------------- |:------------------:| | ignore | 1 | | 8int | 1 | | 8uint | 1 | | 16int | 1 | | 16uint | 1 | | 32int | 2 | | 32uint | 2 | | 32float | 2 | | 64int | 4 | | 64uint | 4 | | 64float | 4 | """ if (unit is None): unit = self.UNIT_ID #omitting string for now since it requires a specified length if type == '8int': rr = self.read_register_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_8bit_int() elif type == '8uint': rr = self.read_register_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_8bit_uint() elif type == '16int': rr = self.read_register_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_16bit_int() elif type == '16uint': rr = self.read_register_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_16bit_uint() elif type == '32int': rr = self.read_register_raw(register,2,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_32bit_int() elif type == '32uint': rr = self.read_register_raw(register,2,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_32bit_uint() elif type == '32float': rr = self.read_register_raw(register,2,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_32bit_float() elif type == '64int': rr = self.read_register_raw(register,4,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_64bit_int() elif type == '64uint': rr = self.read_register_raw(register,4,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_64bit_uint() elif type == 'ignore': rr = self.read_register_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.skip_bytes(8) elif type == '64float': rr = self.read_register_raw(register,4,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_64bit_float() else: print("Wrong type specified") exit() return output def decode_input_register(self,register,type, unit=None): """ :param register: input register address to retrieve :param type: type to interpret the registers retrieved as :returns: data in the type specified Based on the type provided, this function retrieves the values contained in the register address specfied plus the amount necessary to encompass the the type. For example, if 32int is specified with an address of 200 the registers accessed would be 200 and 201. The types accepted are listed in the table below along with their length | Type | Length (registers) | | ------------- |:------------------:| | ignore | 1 | | 8int | 1 | | 8uint | 1 | | 16int | 1 | | 16uint | 1 | | 32int | 2 | | 32uint | 2 | | 32float | 2 | | 64int | 4 | | 64uint | 4 | | 64float | 4 | """ if (unit is None): unit = self.UNIT_ID #omitting string for now since it requires a specified length if type == '8int': rr = self.read_input_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_8bit_int() elif type == '8uint': rr = self.read_input_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_8bit_uint() elif type == '16int': rr = self.read_input_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_16bit_int() elif type == '16uint': rr = self.read_input_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_16bit_uint() elif type == '32int': rr = self.read_input_raw(register,2,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_32bit_int() elif type == '32uint': rr = self.read_input_raw(register,2,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_32bit_uint() elif type == '32float': rr = self.read_input_raw(register,2,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_32bit_float() elif type == '64int': rr = self.read_input_raw(register,4,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_64bit_int() elif type == '64uint': rr = self.read_input_raw(register,4,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_64bit_uint() elif type == 'ignore': rr = self.read_input_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.skip_bytes(8) elif type == '64float': rr = self.read_input_raw(register,4,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_64bit_float() else: print("Wrong type specified") exit() return output def get_data(self,unit=None): """ :returns: Dictionary containing the value retrieved for each register contained in the YAML config file, register names cannot be repeated or the register will be overwritten """ if unit is None: unit = self.UNIT_ID output = {} for key in self.coil_register_dict: #print(key) output[key] = self.read_coil(self.coil_register_dict[key][0],unit) for key in self.discrete_register_dict: #print(key) output[key] = self.read_discrete(self.discrete_register_dict[key][0],unit) for key in self.holding_register_dict: #print(key) output[key] = self.decode_register(self.holding_register_dict[key][0],self.holding_register_dict[key][1],unit) for key in self.input_register_dict: #print(key) output[key] = self.decode_input_register(self.input_register_dict[key][0],self.input_register_dict[key][1],unit) return output def get_data_all_devices(self): reg_data_dict = {} cnt = 1 for dev_id in self.UNIT_ID_LIST: new_key = str(self.UNIT_ID_LIST[dev_id]) if str(self.UNIT_ID_LIST[dev_id]) in reg_data_dict: new_key = new_key + '_' + str(cnt) cnt += 1 reg_data_dict[new_key] = self.get_data(self.UNIT_ID_LIST[dev_id]) return reg_data_dict def kill_modbus(self): """ Closes connection with Modbus Slave """ self.client.close()
class clientthreads(threading.Thread): def __init__(self, vnic, ipaddr, port): threading.Thread.__init__(self) self.ipaddr = ipaddr # ip address self.port = port # port address self.vnic = vnic # virtual nic self.mode = "" # server or client self.state = "" # up or down self.dest = "" # destination address for client self.clientstop = threading.Event() self.server = "" self.client = "" self.framer = "" self.vnicm = "" self.runtime= 0 self.delayr = random.uniform(0,5) self.delayw = random.uniform(0,60) self.firstdelay = 0 self.pstart= "" def run(self): self.client = ModbusTcpClient(self.dest, self.port, source_address=(self.ipaddr, 0), retries=1, retry_on_empty=True) if(self.mode=="read"): self.clientintr() elif(self.mode=="write"): self.clientintw() else: print "wrong mode specified" def clientintr(self): # instantiate server stuff while(not self.clientstop.is_set()): if(time.time() - self.pstart > self.runtime): print "stopping" break if(self.firstdelay < 1): print "Start RDelay is: " + str(self.delayr) time.sleep(self.delayr) self.firstdelay = 1 print "Starting Reads" self.clientreads() print "\n\r-----read-----\n\r" print self.dest print time.time() - self.pstart print "------------------\n\r" def clientintw(self): # instantiate server stuff while(not self.clientstop.is_set()): if(time.time() - self.pstart > self.runtime): print "stopping" break if(self.firstdelay < 1): print "Start WDelay is: " + str(self.delayw) time.sleep(self.delayw) self.firstdelay = 1 print "Starting Writes" self.clientwrites() print "\n\r-----write----\n\r" print self.dest print time.time() - self.pstart print "------------------\n\r" def clientreads(self): self.client.read_coils(1, 10) self.client.read_discrete_inputs(1, 10) self.client.read_holding_registers(1, 10) self.client.read_input_registers(1, 10) time.sleep(5) def clientwrites(self): self.client.write_coil(1, True) self.client.write_register(1, 3) self.client.write_coils(1, [True]*10) self.client.write_registers(1, [3]*10) time.sleep(60) def alloc(self): # Allocate ip address if (validateIP(self.ipaddr, self.vnicm)): cmdargs = [self.vnic, self.ipaddr] subprocess.call(["ifconfig"] + cmdargs) else: return 0 def dealloc(self): # De-allocate ip address cmdargs = [self.vnic] subprocess.call(["ifconfig"] + cmdargs + ["down"]) def stop(self): self.clientstop.set() return
#!/usr/bin/env python3 #from pymodbus.constants import Endian from pymodbus.payload import BinaryPayloadDecoder from pymodbus.client.sync import ModbusTcpClient client = ModbusTcpClient('127.0.0.1', port=5020) client.connect() di = client.read_discrete_inputs(address=0x000, count=8) co = client.read_coils(address=0x100, count=8) hr = client.read_holding_registers(address=0x200, count=4) ir = client.read_input_registers(address=0x300, count=1) print(di.bits) print(co.bits) #print(hr.registers) decoder = BinaryPayloadDecoder.fromRegisters(hr.registers) decoded_string = decoder.decode_string(8) print(decoded_string) #print(ir.registers) decoder = BinaryPayloadDecoder.fromRegisters(ir.registers) decoded_8bit_int = decoder.decode_8bit_int() print(decoded_8bit_int)
clientTCP.close() times() else: a = 0 elif function_code == '02': clientTCP = ModbusTcpClient(AddrTCP) connection = clientTCP.connect() #print(clientTCP.timeout) if connection == True: times() print( '--------------------------------------------------FC02----------------------------------------------------------' ) Request = clientTCP.read_discrete_inputs(0x00C4, 0x0016, unit=0x11) resp = reversePacket('FC02', Request) print('') print( '*********************************************************' ) print('Resultado comparado com Modbus RTU:') print(PacketCompare('FC02', sglobal, resp)) print('') print('Resposta do Modbus TCP:') print(Request) print('') print('Valor do Modbus TCP: ' + resp) print( '*********************************************************'
class MainWindow(QtWidgets.QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.setWindowTitle("PLC Timing Charts") self.main_widget = QtWidgets.QWidget(self) self.setCentralWidget(self.main_widget) # Create the maptlotlib FigureCanvas object, # which defines a single set of axes as self.axes. self.left_in = MplCanvas(self) # self.left_out = MplCanvas(self) # self.right_in = MplCanvas(self) # self.right_out = MplCanvas(self) self.plc_client = ModbusTcpClient("10.24.0.2") self.left_in.fkth_sensor.add = 0x400 self.left_in.fkth_purger.add = 0x401 self.left_in.tr_sensor.add = 0x402 self.left_in.tr_purger.add = 0x403 self.timing_layout = QtWidgets.QGridLayout(self.main_widget) self.timing_layout.addWidget(self.left_in, *(0, 0)) # self.timing_layout.addWidget(self.left_out, *(0, 1)) # self.timing_layout.addWidget(self.right_in, *(1, 0)) # self.timing_layout.addWidget(self.right_out, *(1, 1)) n_samples = np.linspace(0, 499, 100) for _side in [self.left_in]: # , self.left_out, self.right_in, self.right_out]: for _plot in [ _side.fkth_sensor, _side.fkth_purger, _side.tr_sensor, _side.tr_purger, ]: _plot.xdata = [(n_samples[i]) for i in range(len(n_samples))] _plot.ydata = [(0) for i in range(len(n_samples))] _plot.plot_ref = None self.update_plot() self.showMaximized() # dataAcquire = threading.Thread(name="data loop", target=self.acquire_signal) # dataAcquire.start() # Setup a timer to trigger the redraw by calling update_plot. acquire = threading.Thread(target=self.acquire_signal, daemon=True) # plotting = threading.Thread(target=self.update_plot, daemon=True) acquire.start() # plotting.start() # acquire.join() # plotting.join() self.timer = QtCore.QTimer() self.timer.setInterval(25) # self.timer.timeout.connect(self.acquire_signal) self.timer.timeout.connect(self.update_plot) self.timer.start() def acquire_signal(self): while True: start = time.time() for _side in [ self.left_in ]: # , self.left_out, self.right_in, self.right_out]: for _plot in [ _side.fkth_sensor, _side.fkth_purger, _side.tr_sensor, _side.tr_purger, ]: _plot.response = int( (self.plc_client.read_discrete_inputs(_plot.add)).bits[0] == True ) _plot.ydata = _plot.ydata[1:] + [_plot.response] elapsed_time = time.time() - start print(elapsed_time) def update_plot(self): # Drop off the first y element, append a new one. for _side in [self.left_in]: # , self.left_out, self.right_in, self.right_out]: # for _plot in [ # _side.fkth_sensor, # _side.fkth_purger, # _side.tr_sensor, # _side.tr_purger, # ]: # _plot.response = int( # (self.plc_client.read_discrete_inputs(_plot.add)).bits[0] == True # ) # Note: we no longer need to clear the axis. # First time we have no plot reference, so do a normal plot. # .plot returns a list of line <reference>s, as we're # only getting one we can take the first element. # Then once we have a reference, we can use it to update the data for that line. if _side.fkth_sensor.plot_ref is None: _side.fkth_sensor.plot_refs = _side.fkth_sensor.plot( _side.fkth_sensor.xdata, _side.fkth_sensor.ydata, "b", drawstyle="steps-mid", ) _side.fkth_sensor.plot_ref = _side.fkth_sensor.plot_refs[0] else: _side.fkth_sensor.plot_ref.set_ydata(_side.fkth_sensor.ydata) if _side.fkth_purger.plot_ref is None: _side.fkth_purger.plot_refs = _side.fkth_purger.plot( _side.fkth_purger.xdata, _side.fkth_purger.ydata, "r", drawstyle="steps-mid", ) _side.fkth_purger.plot_ref = _side.fkth_purger.plot_refs[0] else: _side.fkth_purger.plot_ref.set_ydata(_side.fkth_purger.ydata) if _side.tr_sensor.plot_ref is None: _side.tr_sensor.plot_refs = _side.tr_sensor.plot( _side.tr_sensor.xdata, _side.tr_sensor.ydata, "c", drawstyle="steps-mid", ) _side.tr_sensor.plot_ref = _side.tr_sensor.plot_refs[0] else: _side.tr_sensor.plot_ref.set_ydata(_side.tr_sensor.ydata) if _side.tr_purger.plot_ref is None: _side.tr_purger.plot_refs = _side.tr_purger.plot( _side.tr_purger.xdata, _side.tr_purger.ydata, "m", drawstyle="steps-mid", ) _side.tr_purger.plot_ref = _side.tr_purger.plot_refs[0] else: _side.tr_purger.plot_ref.set_ydata(_side.tr_purger.ydata) # Trigger the canvas to update and redraw. _side.draw()
class ModbusTCPPlcConnector: """ Instantiates the connection to a PLC via modbus/TCP. """ def __init__(self, plc_ip_address, timeout=0): """ :param plc_ip_address: The IP address of the PLC. """ self.modbus_client = ModbusTcpClient(plc_ip_address, timeout=timeout) self.modbus_client.connect() # Map of motor controls to modbus/TCP bits. self.motor_controls = { "control-motor-up": int(ModbusTCPRegisters.ControlMotorUp), "control-motor-down": int(ModbusTCPRegisters.ControlMotorDown), "control-motor-left": int(ModbusTCPRegisters.ControlMotorLeft), "control-motor-right": int(ModbusTCPRegisters.ControlMotorRight) } # The proper names for the motor controls. self.motor_names = ["motorUp", "motorDown", "motorLeft", "motorRight"] # The proper names for the sensors. self.sensor_names = [ "topSensor", "bottomSensor", "leftSensor", "rightSensor" ] def is_connected(self): """ Is the client connected? :return: True is modbus client is connected """ return self.modbus_client.is_socket_open() def get_values(self): """ This function queries the state of the sensors and motor controls. :return: A dictionary of the state of the motor controls and the state of the sensors. """ # Should the HMI not be connected to the PLC return all values as false so that while being in the disconnected # state, all sensors and motors are shown as off sensors = dict( zip(self.sensor_names, ["true"] * len(self.sensor_names))) motors = dict(zip(self.motor_names, ["true"] * len(self.motor_names))) if self.is_connected(): try: # Read the four bits of motor state from the PLC over modbus/TCP. motor_values = self.modbus_client.read_coils( int(ModbusTCPRegisters.PLCInputs), 4).bits[:4] # Read the four bits of sensors state from the PLC over modbus/TCP. sensor_values = self.modbus_client.read_discrete_inputs( int(ModbusTCPRegisters.PLCInputs)).bits[:4] # Separate the limit switches of the punching arm from the light switches limit_switches = sensor_values[:2] # Separate the light switches from the limit switches of the punching arm. light_sensors = sensor_values[2:4] # The values of the motor manual values must be inverted to fit our logic. # The values are converted to strings. corrected_motor_values = [ str(not value).lower() for value in motor_values ] # The values are converted to strings. light_sensor_values = [ str(value).lower() for value in light_sensors ] # The values are converted to strings. limit_switch_values = [ str(value).lower() for value in limit_switches ] # Join the sensor values again. joined_sensor_values = limit_switch_values + light_sensor_values # Join the proper sensor names as keys with the corresponding sensor values into a dictionary. sensors = dict(zip(self.sensor_names, joined_sensor_values)) # Join the proper motor manual names as keys with the corresponding motor manual values into a dictionary. motors = dict(zip(self.motor_names, corrected_motor_values)) except: pass # Return the dictionary that contains the current motor manual values as well as sensor values. return {**sensors, **motors} def set_order(self, count): """ Set an positive numeric value as amount of times the process is to be executed. :param count: Positive integer. :return: The PLC connectors response, so it can be use if of interest. """ if self.is_connected(): try: self.modbus_client.write_register( int(ModbusTCPRegisters.Orders), count) except: pass def set_application_state(self, state): """ Set the current application state. :param state: :return: The PLC connectors response, so it can be use if of interest. """ state_value = application.Disconnected().modbus_value if self.is_connected(): try: state_value = self.modbus_client.write_register( int(ModbusTCPRegisters.HmiApplicationState), state.modbus_value) except: pass return state_value def set_motor(self, motor, motor_state): """ Turn a motor of the process manually on or off. :param motor: Which motor is to be set. :param motor_state: On or Off. :return: The PLC connectors response, so it can be use if of interest. """ register = self.motor_controls[motor] state_value = application.Disconnected().modbus_value if self.is_connected(): try: state_value = self.modbus_client.write_register( register, int(motor_state)) except: pass return state_value def set_reset(self): """ Initiate the reset signal to get the PLC out of the emergency stop state. :return: The PLC connectors response, so it can be use if of interest. """ state_value = application.Disconnected().modbus_value if self.is_connected(): try: state_value = self.modbus_client.write_register( int(ModbusTCPRegisters.Reset), 1) except: pass return state_value def get_orders(self): """ Retrieve the amount of currently placed orders. :return: The order count. """ state_value = application.Disconnected().modbus_value if self.is_connected(): try: state_value = self.modbus_client.read_holding_registers( int(ModbusTCPRegisters.Orders), 1).registers[0] except: pass return state_value def get_process_state(self): """ Query the process state. :return: A dictionary containing the current process state. """ state_value = process.PendingState().modbus_value if self.is_connected(): try: state_value = \ self.modbus_client.read_holding_registers(int(ModbusTCPRegisters.ProcessState), 2).registers[0] except: pass return state_value def get_application_state(self): """ Query the application state. :return: A dictionary containing the current application state. """ state_value = application.Disconnected().modbus_value if self.is_connected(): try: state_value = \ self.modbus_client.read_holding_registers(int(ModbusTCPRegisters.PlcApplicationState), 1).registers[ 0] except: pass return state_value
#res = client.write_registers(0,[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],unit=1) for i, j in enumerate("001001001100"): client.write_coil(886 + i, int(j), unit=junction) #for i in range(880,890): # print(i) # client.write_coil(i,1,unit=junction) # time.sleep(5) hold_reg = client.read_holding_registers(0, 99, unit=junction) print("Holding Register:", ''.join([chr(x) for x in (hold_reg.registers)])) print(hold_reg.registers) input_reg = client.read_input_registers(0, 99, unit=junction) print("Input Register:", input_reg.registers) discrete_inputs = client.read_discrete_inputs(0, 99, unit=junction) print("Discrete Input:", discrete_inputs.bits) coils = client.read_coils(0, 100, unit=junction) print("Coils:", [int(x) for x in coils.bits]) client.close() #Junction 1 starts at coil 571 we want 001001001100 #Junction 2 starts at coil 1920 we want 100001001001 #Junction 4 starts at coil 1266 we want 001001001100 #Junction 6 starts at coil 886 we want 001001001100
# If the returned output quantity is not a multiple of eight, # the remaining bits in the final data byte will be padded with zeros # (toward the high order end of the byte). resp.extend([False]*3) assert(rr.bits == resp) # test the expected value log.debug("Write to multiple coils and read back - test 2") rq = client.write_coils(1, [False]*8, unit=1) rr = client.read_coils(1, 8, unit=1) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.bits == [False]*8) # test the expected value log.debug("Read discrete inputs") rr = client.read_discrete_inputs(0, 8, unit=1) assert(rq.function_code < 0x80) # test that we are not an error log.debug("Write to a holding register and read back") rq = client.write_register(1, 10, unit=1) rr = client.read_holding_registers(1, 1, unit=1) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.registers[0] == 10) # test the expected value log.debug("Write to multiple holding registers and read back") rq = client.write_registers(1, [10]*8, unit=1) rr = client.read_holding_registers(1, 8, unit=1) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.registers == [10]*8) # test the expected value log.debug("Read input registers")
class PLC(): timeSpan=0 connected=False prev_res=0 def __init__(self, ip): #ip should be 10.3.0.2 self.client = ModbusTcpClient(ip) self.connected = self.client.connect() self.clearFlags() #self.setDefaultPurgingTime() def changeIP(self, ip): if self.connected: self.client.close() print("Modbus TCP connection closed") self.client = ModbusTcpClient(ip) self.connected = self.client.connect() if self.connected: self.clearFlags() return self.connected def clearFlags(self): if self.connected: self.client.write_coils(GLOVE_PRESENT_ADDR, (0,0,0,0,0,0,0,0,0)) #Clear all flags self.client.write_register(FURS_ON_TIME,5)#half second def waitNextGloveFlicker(self): if self.connected: for i in range(50): result = self.client.read_discrete_inputs(GLOVE_PRESENT_ADDR,1) if(self.prev_res==0 and result.bits[0]>0): #Check for rising edge self.prev_res=result.registers[0] return 1 self.prev_res=result.registers[0] time.sleep(0.02) print("Production Line Stopped") return 0 print("No PLC Connection") time.sleep(0.5) #Do not wait next glove if no connection, return -1 return -1 def waitNextGlove(self): if self.connected: for i in range(500): if not self.connected: break try: result = self.client.read_discrete_inputs(GLOVE_PRESENT_ADDR,1) if result.bits[0]: self.client.write_coil(GLOVE_PRESENT_ADDR, False) time.sleep(0.03) ##Displacement delay return 1 time.sleep(0.002) except: print("Changing PLC IP Address") print("Production Line Stopped") return 0 time.sleep(1) print("No PLC Connection") #Do not wait next glove if no connection, return 0 return -1 def readSensors(self): if self.connected: try: result = self.client.read_discrete_inputs(GLOVE_PRESENT_ADDR,4) for side, bit in enumerate(result.bits): if bit: self.client.write_coil(GLOVE_PRESENT_ADDR+side, False) return result.bits except: print("Changing PLC IP Address") print("No PLC Connection") #Do not wait next glove if no connection, return 0 return -1 def purgeGlove32(self, line): if self.connected: #activate purger by setting M1, will be cleared by PLC self.client.write_register(PURGER_ADDR+line, 1) time.sleep(0.5) self.client.write_register(PURGER_ADDR+line, 0) def purgeGlove(self, line): if self.connected: #activate purger by setting M1, will be cleared by PLC self.client.write_coil(PURGER_ADDR+line, True) def setPurgeDelay_100ms(self,line,val): if self.connected: self.client.write_register(PURGE_DELAY_ADDR+line,val) def setPurgeDuration_100ms(self,line,val): if self.connected: self.client.write_register(PURGING_DURATION_ADDR+line,val) def setPurgeInterval_100ms(self,line,val): if self.connected: self.client.write_register(PURGE_INTERVAL_ADDR+line,val) def setDefaultPurgingTime(self): if self.connected: for i in range(4): self.setPurgeDuration_100ms(i,8) self.setPurgeInterval_100ms(i,3) def isFormerAnchor(self): return self.readNClearFlag(FORMER_ANCHOR_ADDR) #Former Anchor Flag M11 def readRasmAnchor(self,side): return self.readNClearFlag(RASM_ANCHOR_ADDR+side) #RASM Anchor Flag M4~M7 def readNClearFlag(self, addr): if self.connected: try: result = self.client.read_discrete_inputs(addr,1) if result.bits[0]: self.client.write_coil(addr, False) return 1 #read and cleared else: return 0 #no flag except AttributeError: print("Anchor Checking no reading because lost PLC connection") return -1 else: return -1 #no connection def setDualBinFlap(self,side,val): if self.connected: if val: self.client.write_coil(DUAL_BIN_FLAP_ON+side, True) else: self.client.write_coil(DUAL_BIN_FLAP_OFF+side, True) def setFlipDuration_100ms(self,val): if self.connected: self.client.write_register(FLIP_DURATION_ADDR,val) def activateFurs(self,side): if self.connected: self.client.write_coil(FURS_ADDR+side, True) def close(self): self.client.close() print("Modbus TCP connection closed")
if args.type == 'HR': hr_read = client.read_holding_registers(args.offset, count=args.count) assert(hr_read.function_code < 0x80) print(hr_read.registers[0:args.count]) # NOTE: read_holding_registers elif args.type == 'IR': ir_read = client.read_input_registers(args.offset, count=args.count) assert(ir_read.function_code < 0x80) print(ir_read.registers[0:args.count]) # NOTE: read_discrete_inputs elif args.type == 'DI': di_read = client.read_discrete_inputs(args.offset, count=args.count) assert(di_read.function_code < 0x80) print(di_read.bits) # NOTE: read_discrete_inputs elif args.type == 'CO': co_read = client.read_coils(args.offset, count=args.count) assert(co_read.function_code < 0x80) print(co_read.bits) client.close()
from time import sleep from pymodbus.client.sync import ModbusTcpClient inputState = [False] * 32 outputState = [False] * 32 clientDI_1 = ModbusTcpClient('192.168.82.77') clientDO_1 = ModbusTcpClient('192.168.82.76') clientDO_1.write_coils(1, inputState) while not sleep(0.05): try: result = clientDI_1.read_discrete_inputs(1, 32) activeButtonsIndexes = [i for i, x in enumerate(result.bits) if x] for index in activeButtonsIndexes: if inputState[index] != result.bits[index]: print(f"Button pressed {index}") outputState[index] = not outputState[index] inputState = result.bits clientDO_1.write_coils(1, outputState) clientDI_1.close() clientDO_1.close() except: print("error, resetting")
def acquire_signal(data_io1, data_io2, data_io3, data_io4, n_samples): io_1 = IO() io_2 = IO() io_3 = IO() io_4 = IO() io_1.ydata = [(0) for i in range(len(n_samples))] io_2.ydata = [(0) for i in range(len(n_samples))] io_3.ydata = [(0) for i in range(len(n_samples))] io_4.ydata = [(0) for i in range(len(n_samples))] try: with open("./plc.json") as f: plc = json.load(f) ip = plc["ipAddress"] io_1.typ, io_1.bit = get_bit(plc["Ports"]["IOport1"]) io_2.typ, io_2.bit = get_bit(plc["Ports"]["IOport2"]) io_3.typ, io_3.bit = get_bit(plc["Ports"]["IOport3"]) io_4.typ, io_4.bit = get_bit(plc["Ports"]["IOport4"]) plc_client = ModbusTcpClient(ip) while True: start = time.time() input_data = plc_client.read_discrete_inputs(1024, 32) output_data = plc_client.read_discrete_inputs(1280, 32) for _io in [io_1, io_2, io_3, io_4]: if _io.typ == "X": _io.response = int(input_data.bits[_io.bit] == True) elif _io.typ == "Y": _io.response = int(output_data.bits[_io.bit] == True) else: io_1.response = -2 _io.ydata = _io.ydata[1:] + [_io.response] _io.log_val = str(f"{_io.typ}{_io.bit}:{_io.response}") data_io1.send(io_1.ydata) data_io2.send(io_2.ydata) data_io3.send(io_3.ydata) data_io4.send(io_4.ydata) elapsed_time = time.time() - start remaining_time = 0.05 - elapsed_time err = "No error" if remaining_time > 0: time.sleep(remaining_time) else: err = "Warning: Acquisition rate is above 50ms" print(err) msg = str(f"Current acquisition rate is: {elapsed_time}seconds") logger( [ datetime.datetime.now().strftime("%H:%M:%S.%f"), msg, err, io_1.log_val, io_2.log_val, io_3.log_val, io_4.log_val, ] ) except: err = f"Unable to connect to PLC. Please confirm if PLC address is {ip}" logger( [datetime.datetime.now().strftime("%H:%M:%S.%f"), "inf", err, 0, 0, 0, 0] ) print(err)
class ModbusTCP(SmartPlugin): ALLOW_MULTIINSTANCE = False PLUGIN_VERSION = "1.0.0" #################################################################################################### # # # # # #################################################################################################### def __init__(self, smarthome, host, timeout=10, port=502, cycle=5, byteorder='big', wordorder='little'): ##Initialdaten aus Plugin.conf self._sh = smarthome self._items = {} self.timeout = float(timeout) self._device = str(host) self.port = int(port) self.cycle = int(cycle) self._unit = 0x1 self.logger = logging.getLogger(__name__) ###################################### self._adresses = [] self.fail = False #1: bool 0...1 #5: 8-bit signed value 0...255 #5.001: 8-bit unsigned value 0...100 #6: 8-bit signed value -128...127 #7: 16-bit unsigned value 0...65535 #8: 16-bit signed value -32768...32767 #9: floating point -671088,64 - 670760,96 if byteorder == 'big': self.byteorder = Endian.Big else: self.byteorder = Endian.Little if wordorder == 'big': self.wordorder = Endian.Big else: self.wordorder = Endian.Little if cycle: self._sh.scheduler.add('ModbusTCP: read cycle', self._read, prio=5, cycle=self.cycle) self.connect() self._lock = threading.Lock() def run(self): ##plugin starten self.alive = True def stop(self): ##plugin stoppen self.alive = False #self.client.disconnect() self.client.close() self.logger.info('ModbusTCP: Stopped!') def parse_logic(self, logic): ##nicht benoetigt pass def connect(self): try: #if self.device is not None: self.client = ModbusClient(self._device, self.port) #self.client.connect() #self.connected = True self.logger.info('ModbusTCP: Connected to {0}'.format( self._device)) self.fail = False except Exception as e: if self.fail == False: self.logger.error( 'ModbusTCP: Could not connect to : {0}, {1]'.format( self._device, e)) self.fail = True #################################################################################################### #Items beim start überprüfen, auf modbus_on = 1 #################################################################################################### def parse_item(self, item): if self.has_iattr(item.conf, 'modbus_function'): mfunction = item.conf['modbus_function'] #1 Read Coil Status 1bit #2 Read Input Status 16bit #3 Read Holding Registers 16bit #4 Read Input Registers 16bit #5 Force Single Coil 1bit #6 Preset Single Register 16bit if mfunction not in ['1', '2', '3', '4', '5', '6']: self.logger.warning( "ModbusTCP: This is not a valid Modbus function.{0} {1}.". format(item, mfunction)) pass if self.has_iattr(item.conf, 'modbus_read'): madress = item.conf['modbus_read'] self.logger.debug( "ModbusTCP: {0} - read from {2} and mfunction {1}".format( item, mfunction, madress)) self._adresses.append({ 'mfunction': int(mfunction), 'madress': int(madress), 'item': item }) return self.update_item if self.has_iattr(item.conf, 'modbus_write'): madress = item.conf['modbus_write'] mfunction = item.conf['modbus_function'] self.logger.debug( "ModbusTCP: {0} - write to {1} and mfunction{2}".format( item, madress, mfunction)) return self.update_item #else: # self.logger.warning("ModbusTCP: Modbus Function is mandatory!{0}".format(item)) # return None #################################################################################################### ##Item has changed, called by SmarthomeNG #################################################################################################### def update_item(self, item, caller=None, source=None, dest=None): if caller != 'ModbusTCP': value = int(item()) mfunction = int(item.conf['modbus_function']) if self.has_iattr(item.conf, 'modbus_write'): madress = int(item.conf['modbus_write']) try: #if self.client._connected: if mfunction == 5: #write_single_coil(bit_addr, bit_value) val = self.client.write_coil(madress, value, unit=self._unit) self.logger.debug( 'MODBUSTCP: Write on {0} with function{1} value {2}' .format(madress, mfunction, value)) elif mfunction == 6: #write_single_register(reg_addr, reg_value) val = self.client.write_register(madress, value, unit=self._unit) self.logger.debug( 'MODBUSTCP: Write on {0} with function{1} value {2}' .format(madress, mfunction, value)) elif mfunction == 15: #write_multiple_coils(reg_addr, reg_value) bitarray = self.client.get_bits_from_int( value, val_size=16, unit=self._unit) val = self.client.write_multiple_coils(madress, bitarray, unit=self._unit) elif mfunction == 16: #write_multiple_registers(regs_addr, regs_value) val = self.client.write_multiple_registers( madress, value, unit=self._unit) #if val.function_code > 0x80: # self.logger.error('MODBUSTCP: Could not write on {0} with function{1}, because Errorcode{2}'.format(madress, mfunction, val)) #else: # self.logger.error('MODBUSTCP: Client failure, not connected') except Exception as e: self.logger.error( 'MODBUSTCP: ERROR; Could not write an OutWord, because {}' .format(e)) #################################################################################################### #Read all Items, who are in self._adresses, called by Scheduler #################################################################################################### def _read(self): self._lock.acquire() try: #if self.client._connected: for adress in self._adresses: #self.logger.debug('MODBUSTCP: {0}'.format(adress)) item = adress['item'] mfunction = adress['mfunction'] madress = adress['madress'] #type = item.conf['type'] if self.has_iattr(item.conf, 'modbus_format'): mformat = item.conf['modbus_format'] else: mformat = 'raw' if mfunction == 1: #Read Coil Status => Status der Ausgänge des MODBUS TCP Servers! ,bitorientiert val = self.client.read_coils(madress, 1, unit=self._unit) val = val.bits[0] self.logger.debug( 'MODBUSTCP: Read from adress {0}, function {1}, item {2} = {3}' .format(madress, mfunction, item, val)) # #Read Input Status 0> STatus der Eingänge elif mfunction == 2: #Read Discrete Inputs => Status der Eingänge des MODBUS TCP Servers!, bitorientiert # #read_discrete_inputs(bit_addr, bit_nb=1) val = self.client.read_discrete_inputs(madress, 1, unit=self._unit) val = val.bits[0] self.logger.debug( 'MODBUSTCP: Read from adress {0}, function {1}, item {2} = {3}' .format(madress, mfunction, item, val)) elif mfunction == 3: #Read Holding Registers => Status der Merker des MODBUS TCP Servers!, byteoriertiert # #read_holding_registers(reg_addr, reg_nb=1) #pass val = self.client.read_holding_registers(madress, 1, unit=self._unit) #val = val.registers[0] elif mfunction == 4: #Read INput Registers => Status der Eingänge des MODBUS TCP Servers!, byteoriertiert # #read_input_registers(reg_addr, reg_nb=1) if '8' in mformat: size = 1 value = self.client.read_input_registers( madress, size, unit=self._unit) decoder = BinaryPayloadDecoder.fromRegisters( value.registers, byteorder=self.byteorder, wordorder=self.wordorder) if 'uint' in mformat: val = decoder.decode_8bit_uint() else: val = decoder.decode_8bit_int() elif 'bits' in mformat: value = self.client.read_input_registers( madress, size, unit=self._unit) decoder = BinaryPayloadDecoder.fromRegisters( value.registers, byteorder=self.byteorder, wordorder=self.wordorder) val = decoder.decode_bits() elif '16' in mformat: size = 1 value = self.client.read_input_registers( madress, size, unit=self._unit) decoder = BinaryPayloadDecoder.fromRegisters( value.registers, byteorder=self.byteorder, wordorder=self.wordorder) if 'uint' in mformat: val = decoder.decode_16bit_uint() else: val = decoder.decode_16bit_int() elif '32' in mformat: size = 2 value = self.client.read_input_registers( madress, size, unit=self._unit) decoder = BinaryPayloadDecoder.fromRegisters( value.registers, byteorder=self.byteorder, wordorder=self.wordorder) if 'uint' in mformat: val = decoder.decode_32bit_uint() else: val = decoder.decode_32bit_int() elif '64' in mformat: size = 4 value = self.client.read_input_registers( madress, size, unit=self._unit) decoder = BinaryPayloadDecoder.fromRegisters( value.registers, byteorder=self.byteorder, wordorder=self.wordorder) if 'uint' in mformat: val = decoder.decode_64bit_uint() else: val = decoder.decode_64bit_int() elif 'raw' in mformat: value = self.client.read_input_registers( madress, size, unit=self._unit) decoder = BinaryPayloadDecoder.fromRegisters( value.registers, byteorder=self.byteorder, wordorder=self.wordorder) val = decoder.skip_bytes(8) else: val = 0 #decoded = { # 'string': decoder.decode_string(8), # 'bits': decoder.decode_bits(), #'8int': decoder.decode_8bit_int(), # '8uint': decoder.decode_8bit_uint(), #'16int': decoder.decode_16bit_int(), # '16uint': decoder.decode_16bit_uint(), #'32int': decoder.decode_32bit_int(), # '32uint': decoder.decode_32bit_uint(), # '32float': decoder.decode_32bit_float(), # '64int': decoder.decode_64bit_int(), # '64uint': decoder.decode_64bit_uint(), # 'raw': decoder.skip_bytes(8), # '64float': decoder.decode_64bit_float(), #} if value == None: self.logger.error( 'MODBUSTCP: Could not read from {0} with function {1}'. format(madress, mfunction)) else: #val = decoded[mformat] self.logger.debug( 'MODBUSTCP: Read from adress {0}, with size {1},function {2} item {3} = {4}' .format(madress, size, mfunction, item, val)) item(val, 'ModbusTCP') except Exception as e: self.logger.error( 'MODBUSTCP: Could not read from adress {0} with function {1}, because {2}' .format(madress, mfunction, e)) finally: self._lock.release() #################################################################################################### #wandelt str in binary ohne führendes 0b #und invertiert auf wunsch das Ergebnis! # # # #################################################################################################### def toBinary2(self, n, invert=1): ## byte = '{:08b}'.format(n) i = int(16) - len(byte) ausgabe = "" for x in range(0, i): ausgabe = "0" + ausgabe ausgabe = ausgabe + byte if invert == 1: #invertieren ausgabe = ausgabe[::-1] return ausgabe def toBinary(self, n, invert=0): ## byte = '{:08b}'.format(n) if invert == 1: #invertieren byte = byte[::-1] ausgabe = [] if byte == '00000000': ausgabe.append('00000000') ausgabe.append('00000000') elif byte < '256': ausgabe.append(byte[0:8]) ausgabe.append('00000000') else: ausgabe.append(byte[0:8]) ausgabe.append(byte[8:17]) return ausgabe def de5001(self, value): #8bit 0-100 auf 0-255 normieren if value > 255: value = 255 elif value < 0: value = 0 return int(round(value * 2.55)) def en5001(self, value): #8bit auf 0-100 normieren if value > 255: value = 255 elif value < 0: value = 0 return (round((value / 2.55), 2)) #################################################################################################### #Helper FUnction to split modbus register #################################################################################################### def split_ga(self, ga): if '|' in ga: type, dst = ga.split('|') if type in self.types.keys(): area = self.types[type] else: area = 'DB' else: area = 'DB' s7area = areas[area] #split db\byte\bit ret_s7_num = re.findall(r'\d+', ga) if area == 'DB': #db = 3 [] if len(ret_s7_num) > 2: #dbnum, byte,bit = ret_s7_num dbnum = int(ret_s7_num[0]) byte = int(ret_s7_num[1]) bit = int(ret_s7_num[2]) else: dbnum = int(ret_s7_num[0]) byte = int(ret_s7_num[1]) bit = 0 else: # other only 2 dbnum = 0 if len(ret_s7_num) > 1: byte = int(ret_s7_num[0]) bit = int(ret_s7_num[1]) else: byte = int(ret_s7_num[0]) bit = 0 self.logger.debug("S7: split_ga {0}".format([s7area, dbnum, byte, bit])) return [s7area, dbnum, byte, bit]
class Modbus_Driver(object): def __init__(self, config_file, config_section='modbus', **kwargs): # Use a config section if the config file is being shared with other # parts of a project. **kwargs can contain a variable amount of if (isinstance(config_file,str)): with open(config_file) as f: modbusConfig = yaml.safe_load(f) else: modbusConfig = config_file modbus_section = config_section self.BYTE_ORDER_DICT = {} self.WORD_ORDER_DICT = {} self.input_register_dict = {} self.holding_register_dict = {} self.coil_register_dict = {} self.discrete_register_dict = {} self.input_registers = {} self.holding_registers = {} self.coil_registers = {} self.discrete_registers = {} self.MODBUS_TYPE = modbusConfig[modbus_section]['modbus_type'] # Check to see if unit id is a list, if it is then set flag that it is a # list self.UNIT_ID = modbusConfig[modbus_section]['UNIT_ID'] if isinstance(self.UNIT_ID, list): self.UNIT_ID_LIST = self.UNIT_ID #Set default UNIT_ID as first UNIT_ID in list self.UNIT_ID = int(self.UNIT_ID_LIST[0]) else: # Make a unit id list from the non-list definition for compatibility # reasons of previous configs. This also eliminates the possibility # of error in calling get_data_all_devices() on a config with a non # list definition self.UNIT_ID_LIST = [] self.UNIT_ID_LIST.append(self.UNIT_ID) # Start logging if enabled in config self.LOGGING_FLAG = modbusConfig[modbus_section]['enable_logging'] if self.LOGGING_FLAG == False: #Start client logging for trouble shooting logging.basicConfig() log = logging.getLogger() log.setLevel(logging.ERROR) # Start appropriate client based on the type specified in the config if self.MODBUS_TYPE == 'serial': self.METHOD = modbusConfig[modbus_section]['method'] self.SERIAL_PORT = modbusConfig[modbus_section]['serial_port'] self.STOPBITS = modbusConfig[modbus_section]['stopbits'] self.BYTESIZE = modbusConfig[modbus_section]['bytesize'] self.PARITY = modbusConfig[modbus_section]['parity'] self.BAUDRATE = modbusConfig[modbus_section]['baudrate'] elif self.MODBUS_TYPE == 'tcp': self.IP_ADDRESS = modbusConfig[modbus_section]['ip'] self.PORT = modbusConfig[modbus_section]['port'] else: print("Invalid modbus type") exit # Set the byte order as big or little endian if modbusConfig[modbus_section]['byte_order'] == 'big': self.BYTE_ORDER = Endian.Big self.BYTE_ORDER_DICT[self.UNIT_ID] = Endian.Big elif modbusConfig[modbus_section]['byte_order'] == 'little': self.BYTE_ORDER = Endian.Little self.BYTE_ORDER_DICT[self.UNIT_ID] = Endian.Little else: print("invalid byte order") # change to except later exit() # Set the word order as big or little endian if modbusConfig[modbus_section]['word_order'] == 'big': self.WORD_ORDER = Endian.Big self.WORD_ORDER_DICT[self.UNIT_ID] = Endian.Big elif modbusConfig[modbus_section]['word_order'] == 'little': self.WORD_ORDER = Endian.Little self.WORD_ORDER_DICT[self.UNIT_ID] = Endian.Little else: print("invalid byte order") # change to except later exit() # Read in all registers specified in the YAML config self.coil_register_dict = modbusConfig[modbus_section]['coil_registers'] self.discrete_register_dict = modbusConfig[modbus_section]['discrete_registers'] self.holding_register_dict = modbusConfig[modbus_section]['holding_registers'] self.input_register_dict = modbusConfig[modbus_section]['input_registers'] self.coil_registers[self.UNIT_ID] = self.coil_register_dict self.discrete_registers[self.UNIT_ID] = self.discrete_register_dict self.holding_registers[self.UNIT_ID] = self.holding_register_dict self.input_registers[self.UNIT_ID] = self.input_register_dict #print(self.holding_registers) # Add single device that is either specified in config_section parameter # or a single device config file for current_device in self.UNIT_ID_LIST: self.coil_registers[current_device] = self.coil_register_dict self.discrete_registers[current_device] = self.discrete_register_dict self.holding_registers[current_device] = self.holding_register_dict self.input_registers[current_device] = self.input_register_dict # Set the byte order as big or little endian if modbusConfig[modbus_section]['byte_order'] == 'big': self.BYTE_ORDER_DICT[current_device] = Endian.Big elif modbusConfig[modbus_section]['byte_order'] == 'little': self.BYTE_ORDER_DICT[current_device] = Endian.Little else: print("invalid byte order") # change to except later exit() # Set the word order as big or little endian if modbusConfig[modbus_section]['word_order'] == 'big': self.WORD_ORDER_DICT[current_device] = Endian.Big elif modbusConfig[modbus_section]['word_order'] == 'little': self.WORD_ORDER_DICT[current_device] = Endian.Little else: print("invalid word order") # change to except later exit() # Apply register offset if specified self.OFFSET_REGISTERS = modbusConfig[modbus_section]['OFFSET_REGISTERS'] for key in self.holding_register_dict: self.holding_register_dict[key][0] -= self.OFFSET_REGISTERS # Add devices that were specified with **kwargs for device_name, modbus_section in kwargs.items(): # The Device ID is used as the key in a dictionary for all settings # that could potentially differ between devices. Since all of the # functions already have been updated to take in a UNIT_ID this # can be used to retrieve the appropriate setting for the device. # TODO Handle case where the config section has a list of the same # device. current_device = modbusConfig[modbus_section]['UNIT_ID'] #print(type(current_device)) # TODO make this a for loop for each ID #current_device = current_device[0] self.UNIT_ID_LIST.append(int(current_device)) if modbusConfig[modbus_section]['byte_order'] == 'big': self.BYTE_ORDER_DICT[current_device] = Endian.Big elif modbusConfig[modbus_section]['byte_order'] == 'little': self.BYTE_ORDER_DICT[current_device] = Endian.Little # Set the word order as big or little endian if modbusConfig[modbus_section]['word_order'] == 'big': self.WORD_ORDER_DICT[current_device] = Endian.Big elif modbusConfig[modbus_section]['word_order'] == 'little': self.WORD_ORDER_DICT[current_device] = Endian.Little else: print("invalid word order") # change to except later exit() # Read in all registers specified in the YAML config self.coil_register_dict = modbusConfig[modbus_section]['coil_registers'] self.discrete_register_dict = modbusConfig[modbus_section]['discrete_registers'] self.holding_register_dict = modbusConfig[modbus_section]['holding_registers'] self.input_register_dict = modbusConfig[modbus_section]['input_registers'] self.coil_registers[current_device] = self.coil_register_dict self.discrete_registers[current_device] = self.discrete_register_dict self.holding_registers[current_device] = self.holding_register_dict self.input_registers[current_device] = self.input_register_dict #print(self.holding_register_dict) ''' # Read in all registers specified in the YAML config self.coil_registers[current_device] = modbusConfig[modbus_section]['coil_registers'] self.discrete_registers[current_device] = modbusConfig[modbus_section]['discrete_registers'] self.holding_registers[current_device] = modbusConfig[modbus_section]['holding_registers'] self.input_register_dict[current_device] = modbusConfig[modbus_section]['input_registers'] ''' # Apply register offset if specified # TODO fix this for one device as well as multiple """ self.OFFSET_REGISTERS_DICT[current_device] = modbusConfig[modbus_section]['OFFSET_REGISTERS'] for key in self.holding_register_dict: self.holding_register_dict[key][0] -= self.OFFSET_REGISTERS """ #print(self.holding_registers) def initialize_modbus(self): """ initalize correct client according to type specified in config: 'tcp' or 'serial' """ if self.MODBUS_TYPE == 'serial': self.client= ModbusSerialClient( method = self.METHOD, port = self.SERIAL_PORT, stopbits = self.STOPBITS, bytesize = self.BYTESIZE, parity = self.PARITY, baudrate = self.BAUDRATE ) connection = self.client.connect() if self.MODBUS_TYPE == 'tcp': self.client = ModbusTcpClient(self.IP_ADDRESS,port=self.PORT) ''' rr = self.read_register_raw(0x601,1,247) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) output = decoder.decode_16bit_int() print(output) ''' #rr = self.read_register_raw(1001,2,7) '''decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) ''' def reconnect(self): try: self.client.close() finally: self.initialize_modbus() def write_single_register(self,register,value, unit=None): """ :param register: address of reigster to write :param value: Unsigned short :returns: Status of write """ if (unit is None): unit = self.UNIT_ID response = self.client.write_register(register,value,unit) return response def write_data(self,register,value): response = self.client.write_register(register,value,unit= self.UNIT_ID) return response def write_register(self,register_name,value, unit=None): """ :param register_name: register key from holding register dictionary generated by yaml config :param value: value to write to register :returns: -- Nothing """ # TODO add the ability to discern which settings will be appropriate for # the device that is being written to if (unit is None): unit = self.UNIT_ID ''' builder = BinaryPayloadBuilder(byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER_DICT[unit]) ''' builder = BinaryPayloadBuilder(byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) # This will change depending on the device that is being connected # potentially so it has to be correleated to the device ID if (self.holding_register_dict[register_name][1] == '8int'): builder.add_8bit_int(value) elif (self.holding_register_dict[register_name][1] == '8uint'): builder.add_8bit_uint(value) elif (self.holding_register_dict[register_name][1] == '16int'): builder.add_16bit_int(value) elif (self.holding_register_dict[register_name][1] == '16uint'): builder.add_16bit_uint(value) elif (self.holding_register_dict[register_name][1] == '32int'): builder.add_32bit_int(value) elif (self.holding_register_dict[register_name][1] == '32uint'): builder.add_32bit_uint(value) elif (self.holding_register_dict[register_name][1] == '32float'): builder.add_32bit_float(value) elif (self.holding_register_dict[register_name][1] == '64int'): builder.add_64bit_int(value) elif (self.holding_register_dict[register_name][1] == '64uint'): builder.add_64bit_uint(value) elif (self.holding_register_dict[register_name][1] == '64float'): builder.add_64bit_float(value) else: print("Bad type") exit() payload = builder.build() self.client.write_registers(self.holding_register_dict[register_name][0], payload, skip_encode=True, unit = self.UNIT_ID) def write_coil(self,register,value, unit=None): """ :param register_name: register key from holding register dictionary generated by yaml config :param value: value to write to register :returns: """ # TODO mention what type the value needs to be for value if (unit is None): unit = self.UNIT_ID response = self.client.write_coil(register,value,unit) return response def read_coil(self,register, unit=None): """ :param register: coil register address to read :returns: value stored in coil register """ # TODO mention what type the value needs to be for value if (unit is None): unit = self.UNIT_ID rr = self.client.read_coils(register, 1, unit=unit) return rr.bits[0] def read_discrete(self,register,unit=None): """ :param register: discrete register address to read :returns: value stored in coil register """ if (unit is None): unit = self.UNIT_ID rr = self.client.read_discrete_inputs(register, count=1,unit=unit) return rr.bits[0] def read_register_raw(self,register,length, unit=None): """ :param register: base holding register address to read :param length: amount of registers to read to encompass all of the data necessary for the type :returns: A deferred response handle """ if (unit is None): unit = self.UNIT_ID response = self.client.read_holding_registers(register,length,unit=unit) return response def read_input_raw(self,register,length, unit=None): """ :param register: base input register address to read :param length: amount of registers to read to encompass all of the data necessary for the type :returns: A deferred response handle """ if (unit is None): unit = self.UNIT_ID response = self.client.read_input_registers(register,length,unit=unit) return response def decode_register(self,register,type, unit=None): #print(unit) #print(type(unit)) """ :param register: holding register address to retrieve :param type: type to interpret the registers retrieved as :returns: data in the type specified Based on the type provided, this function retrieves the values contained in the register address specfied plus the amount necessary to encompass the the type. For example, if 32int is specified with an address of 200 the registers accessed would be 200 and 201. The types accepted are listed in the table below along with their length | Type | Length (registers) | | ------------- |:------------------:| | ignore | 1 | | 8int | 1 | | 8uint | 1 | | 16int | 1 | | 16uint | 1 | | 32int | 2 | | 32uint | 2 | | 32float | 2 | | 64int | 4 | | 64uint | 4 | | 64float | 4 | """ if (unit is None): unit = self.UNIT_ID #omitting string for now since it requires a specified length if type == '8int': rr = self.read_register_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_8bit_int() elif type == '8uint': rr = self.read_register_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_8bit_uint() elif type == '16int': rr = self.read_register_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_16bit_int() elif type == '16uint': rr = self.read_register_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_16bit_uint() elif type == '32int': rr = self.read_register_raw(register,2,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_32bit_int() elif type == '32uint': rr = self.read_register_raw(register,2,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_32bit_uint() elif type == '32float': rr = self.read_register_raw(register,2,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_32bit_float() elif type == '64int': rr = self.read_register_raw(register,4,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_64bit_int() elif type == '64uint': rr = self.read_register_raw(register,4,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_64bit_uint() elif type == 'ignore': rr = self.read_register_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.skip_bytes(8) elif type == '64float': rr = self.read_register_raw(register,4,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_64bit_float() else: print("Wrong type specified") exit() return output def decode_input_register(self,register,type, unit=None): """ :param register: input register address to retrieve :param type: type to interpret the registers retrieved as :returns: data in the type specified Based on the type provided, this function retrieves the values contained in the register address specfied plus the amount necessary to encompass the the type. For example, if 32int is specified with an address of 200 the registers accessed would be 200 and 201. The types accepted are listed in the table below along with their length | Type | Length (registers) | | ------------- |:------------------:| | ignore | 1 | | 8int | 1 | | 8uint | 1 | | 16int | 1 | | 16uint | 1 | | 32int | 2 | | 32uint | 2 | | 32float | 2 | | 64int | 4 | | 64uint | 4 | | 64float | 4 | """ if (unit is None): unit = self.UNIT_ID #omitting string for now since it requires a specified length if type == '8int': rr = self.read_input_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_8bit_int() elif type == '8uint': rr = self.read_input_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit][unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_8bit_uint() elif type == '16int': rr = self.read_input_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_16bit_int() elif type == '16uint': rr = self.read_input_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_16bit_uint() elif type == '32int': rr = self.read_input_raw(register,2,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_32bit_int() elif type == '32uint': rr = self.read_input_raw(register,2,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_32bit_uint() elif type == '32float': rr = self.read_input_raw(register,2,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_32bit_float() elif type == '64int': rr = self.read_input_raw(register,4,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_64bit_int() elif type == '64uint': rr = self.read_input_raw(register,4,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_64bit_uint() elif type == 'ignore': rr = self.read_input_raw(register,1,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.skip_bytes(8) elif type == '64float': rr = self.read_input_raw(register,4,unit) decoder = BinaryPayloadDecoder.fromRegisters( rr.registers, byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) output = decoder.decode_64bit_float() else: print("Wrong type specified") exit() return output def read_register(self,register_name): response = self.decode_register(self.holding_register_dict[register_name][0], self.holding_register_dict[register_name][1]) return response def read_input_raw(self,register_name): response = self.decode_input_register(self.holding_register_dict[register_name][0], self.holding_register_dict[register_name][1]) return response def get_data(self,unit=None): """ :returns: Dictionary containing the value retrieved for each register contained in the YAML config file, register names cannot be repeated or the register will be overwritten """ output = {} if unit is None: unit = self.UNIT_ID for key in self.coil_registers[unit]: output[key] = self.read_coil(self.coil_registers[unit][key][0],unit) for key in self.discrete_registers[unit]: output[key] = self.read_discrete(self.discrete_registers[unit][key][0],unit) for key in self.input_registers[unit]: output[key] = self.decode_input_register(self.input_registers[unit][key][0],self.input_registers[unit][key][1],unit) for key in self.holding_registers[unit]: if (len(self.holding_registers[unit][key]) == 3): # Check Read/Write Flag if (self.holding_registers[unit][key][2].find('R') != -1): output[key] = self.decode_register(self.holding_registers[unit][key][0],self.holding_registers[unit][key][1],unit) else: # Register list does not contain a Read/Write Flag assume R output[key] = self.decode_register(self.holding_registers[unit][key][0],self.holding_registers[unit][key][1],unit) return output def get_data_all_devices(self): reg_data_dict = {} cnt = 1 for dev_id in self.UNIT_ID_LIST: new_key = str(dev_id) if str(dev_id) in reg_data_dict: new_key = new_key + '_' + str(cnt) cnt += 1 reg_data_dict[new_key] = self.get_data(dev_id) return reg_data_dict def kill_modbus(self): """ Closes connection with Modbus Slave """ self.client.close()
# blocks for the two sets, so a change to one is a change to the other. # Keep both of these cases in mind when testing as the following will # _only_ pass with the supplied async modbus server (script supplied). #---------------------------------------------------------------------------# rq = client.write_coil(1, True) rr = client.read_coils(1,1) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.bits[0] == True) # test the expected value rq = client.write_coils(1, [True]*8) rr = client.read_coils(1,8) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.bits == [True]*8) # test the expected value rq = client.write_coils(1, [False]*8) rr = client.read_discrete_inputs(1,8) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.bits == [True]*8) # test the expected value rq = client.write_register(1, 10) rr = client.read_holding_registers(1,1) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.registers[0] == 10) # test the expected value rq = client.write_registers(1, [10]*8) rr = client.read_input_registers(1,8) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.registers == [17]*8) # test the expected value arguments = { 'read_address': 1,
class VisualizerApp(Ui_MainWindow): def __init__(self, main_window): self.setupUi(main_window) self.connect_slots() self.init_poll_table() self.update_poll_table_column_headers() self.configure_modbus_client() self.connected = False def connect_slots(self): self.singlePollPushButton.clicked.connect(self.single_poll) self.startRegisterSpinBox.valueChanged.connect( self.update_poll_table_column_headers) def init_poll_table(self): """ Initialize the table with QTableWidgetItem objects that are empty strings. """ num_rows = self.pollTable.rowCount() num_cols = self.pollTable.columnCount() for j in range(num_cols): for i in range(num_rows): self.pollTable.setItem(i, j, QTableWidgetItem("")) def update_poll_table_column_headers(self): self.clear_poll_table() # Avoids confusion start = self.startRegisterSpinBox.value() num_cols = self.pollTable.columnCount() num_rows = self.pollTable.rowCount() for i in range(num_cols): self.pollTable.horizontalHeaderItem(i).setText( str(start + i * num_rows)) def clear_poll_table(self): num_rows = self.pollTable.rowCount() num_cols = self.pollTable.columnCount() for j in range(num_cols): for i in range(num_rows): self.pollTable.item(i, j).setText("") def write_poll_table(self, data): self.clear_poll_table() num_rows = self.pollTable.rowCount() cur_col = 0 for i, datum in enumerate(data): self.pollTable.item(i % 10, cur_col).setText(str(datum)) #self.pollTable.setItem(i % 10, cur_col, QTableWidgetItem(str(datum))) if (i + 1) % num_rows == 0: cur_col += 1 def configure_modbus_client(self): tcp_mode = self.tcpRadioButton.isChecked() rtu_mode = self.rtuRadioButton.isChecked() if rtu_mode: pass elif tcp_mode: host = self.tcpHostLineEdit.text() port = self.tcpPortLineEdit.text() self.client = ModbusTcpClient(host, port) self.connected = self.client.connect() if not self.connected: self.write_console("Could not connect.") else: self.write_console("Connection Successful.") def single_poll(self): data = self.poll_modbus_data() self.write_poll_table(data) if data: self.write_console("Poll Successful") def poll_modbus_data(self): self.configure_modbus_client() if not self.connected: return [] start = self.startRegisterSpinBox.value() length = self.numberOfRegistersSpinBox.value() register_type = self.registerTypeComboBox.currentText() if register_type == "Coils": rr = self.client.read_coils(start, length) data = rr.bits[:length] elif register_type == "Discrete Inputs": rr = self.client.read_discrete_inputs(start, length) data = rr.bits[:length] elif register_type == "Input Registers": rr = self.client.read_input_registers(start, length) data = rr.registers elif register_type == "Holding Registers": rr = self.client.read_holding_registers(start, length) data = rr.registers else: self.write_console("Unknown Register Type.") data = [] return data def write_console(self, msg): self.consoleLineEdit.setText(msg) def exit(self): QApplication.quit()
# If the returned output quantity is not a multiple of eight, # the remaining bits in the final data byte will be padded with zeros # (toward the high order end of the byte). resp.extend([False] * 3) assert (rr.bits == resp) # test the expected value log.debug("Write to multiple coils and read back - test 2") rq = client.write_coils(1, [False] * 8, unit=1) rr = client.read_coils(1, 8, unit=1) assert (rq.function_code < 0x80) # test that we are not an error assert (rr.bits == [False] * 8) # test the expected value log.debug("Read discrete inputs") rr = client.read_discrete_inputs(0, 8, unit=1) assert (rq.function_code < 0x80) # test that we are not an error log.debug("Write to a holding register and read back") rq = client.write_register(1, 10, unit=1) rr = client.read_holding_registers(1, 1, unit=1) assert (rq.function_code < 0x80) # test that we are not an error assert (rr.registers[0] == 10) # test the expected value log.debug("Write to multiple holding registers and read back") rq = client.write_registers(1, [10] * 8, unit=1) rr = client.read_holding_registers(1, 8, unit=1) assert (rq.function_code < 0x80) # test that we are not an error assert (rr.registers == [10] * 8) # test the expected value log.debug("Read input registers")
assert (rq.function_code < 0x80) # test that we are not an error assert (rr.bits[0] == True) # test the expected value print rr.bits rq = client.write_coils(1, [True] * 8) rr = client.read_coils(1, 8) assert (rq.function_code < 0x80) # test that we are not an error assert (rr.bits == [True] * 8) # test the expected value print rr.bits rq = client.write_coils(1, [False] * 8) rr = client.read_coils(1, 8) assert (rq.function_code < 0x80) # test that we are not an error print rr.bits assert (rr.bits == [False] * 8) # test the expected value ''' client.read_discrete_inputs(address, count, unit) rq = client.write_coils(1, [False]*8) rr = client.read_discrete_inputs(1,8) print "discrete 1", rr.bits rq = client.write_coils(2, [False]*8) rr = client.read_discrete_inputs(2,8) print "discrete 2", rr.bits rq = client.write_coils(0, [False]*8) rr = client.read_discrete_inputs(0,8) print "discrete 0", rr.bits assert(rq.function_code < 0x80) # test that we are not an error assert(rr.bits == [False]*8) # test the expected value ''' rq = client.write_register(1, 10) rr = client.read_holding_registers(1, 1)
elif args.mode == 'r': # NOTE: read_holding_registers if args.type == 'HR': hr_read = client.read_holding_registers(args.offset, count=args.count) assert (hr_read.function_code < 0x80) print(hr_read.registers[0:args.count]) # NOTE: read_holding_registers elif args.type == 'IR': ir_read = client.read_input_registers(args.offset, count=args.count) assert (ir_read.function_code < 0x80) print(ir_read.registers[0:args.count]) # NOTE: read_discrete_inputs elif args.type == 'DI': di_read = client.read_discrete_inputs(args.offset, count=args.count) assert (di_read.function_code < 0x80) print(di_read.bits) # NOTE: read_discrete_inputs elif args.type == 'CO': co_read = client.read_coils(args.offset, count=args.count) assert (co_read.function_code < 0x80) print(co_read.bits) client.close()
for i in range(0, len(totusTemps)): print totusTemps[i] + " = " + str(result.getRegister(i)/10.0) + "\xb0C"# scaling is 10 # read alarms totusAlarms = [ "ALARM/System/HL/State", "ALARM/System/HHLL/State" ] numInputs = 2 startAddress = 100 slaveID = 1 result = client.read_discrete_inputs(startAddress, numInputs, slaveID) for i in range(0, len(totusAlarms)): print totusAlarms[i] + " = " + str(result.getBit(i)) # read DGA float32 gases totusDGA = [ "DGA/SourceA/CH4", "DGA/SourceA/C2H6", "DGA/SourceA/C2H4", "DGA/SourceA/C2H2", "DGA/SourceA/CO", "DGA/SourceA/CO2", "DGA/SourceA/O2",