class LoggingContextReader(object): def __init__(self, output): """ Initialize a new instance of the logger :param output: The output file to save to """ self.output = output self.context = ModbusSlaveContext( di=ModbusSequentialDataBlock.create(), co=ModbusSequentialDataBlock.create(), hr=ModbusSequentialDataBlock.create(), ir=ModbusSequentialDataBlock.create()) def write(self, response): """ Handle the next modbus response :param response: The response to process """ log.info("Read Data: %s" % str(response)) fx, address, values = response self.context.setValues(fx, address, values) def finalize(self): with open(self.output, "wb") as handle: pickle.dump(self.context, handle)
class LoggingContextReader(object): def __init__(self, output): """ Initialize a new instance of the logger :param output: The output file to save to """ self.output = output self.context = ModbusSlaveContext( di = ModbusSequentialDataBlock.create(), co = ModbusSequentialDataBlock.create(), hr = ModbusSequentialDataBlock.create(), ir = ModbusSequentialDataBlock.create()) def write(self, response): """ Handle the next modbus response :param response: The response to process """ log.info("Read Data: %s" % str(response)) fx, address, values = response self.context.setValues(fx, address, values) def finalize(self): with open(self.output, "w") as handle: pickle.dump(self.context, handle)
class VirtualModbusTCPServer: def __init__(self, port): self.port = port self.block = ModbusSequentialDataBlock.create() self.store = ModbusSlaveContext(ir=self.block) self.context = ModbusServerContext(slaves=self.store, single=True) self.scheduler = BackgroundScheduler() params = { 'hour': os.environ.get("VIRTUAL_SERVER_UPDATE_CRON_HOUR_TRIGGER"), 'minute': os.environ.get("VIRTUAL_SERVER_UPDATE_CRON_MINUTE_TRIGGER"), 'second': os.environ.get("VIRTUAL_SERVER_UPDATE_CRON_SECOND_TRIGGER"), } self.main_job = self.scheduler.add_job(**params, func=self.set_datastore, trigger='cron') def set_datastore(self): FUNCTION_CODE = 0x04 ADDRESS = 0x00 builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Big) builder.add_32bit_float(3.14) self.store.setValues(FUNCTION_CODE, ADDRESS, builder.to_registers()) def start(self): self.scheduler.start() StartTcpServer(self.context, address=("0.0.0.0", self.port))
def test_decode(self): test_store = ModbusSlaveContext(t=ModbusSequentialDataBlock.create()) test_env = {'fields': {'scale_8_int': {'reg_type': CO, 'address': 1, 'encode': {'e_type': SCALE, 's_factor': 10, 'd_type': INT8} }, 'scale_8_uint': {'reg_type': CO, 'address': 4, 'encode': {'e_type': SCALE, 's_factor': 10, 'd_type': UINT8} }, 'scale_16_int': {'reg_type': CO, 'address': 6, 'encode': {'e_type': SCALE, 's_factor': 100, 'd_type': INT16} }, 'scale_16_uint': {'reg_type': CO, 'address': 8, 'encode': {'e_type': SCALE, 's_factor': 100, 'd_type': UINT16} }}, 'byte_order': '<', 'word_order': '<', 'default_scaling_factor': 1000} test_fields = test_env['fields'] test_handler = PayloadHandler(test_env, test_store) test_builder = BinaryPayloadBuilder('<', '<') test_builder.reset() test_builder.add_8bit_int(100) test_store.setValues(1, 1, test_builder.to_registers()) self.assertEqual(10, test_handler.decode(1, 1, test_fields['scale_8_int']['encode'])) test_builder.reset() test_builder.add_8bit_uint(100) test_store.setValues(1, 4, test_builder.to_registers()) self.assertEqual(10, test_handler.decode(1, 4, test_fields['scale_8_uint']['encode'])) test_builder.reset() test_builder.add_16bit_int(1350) test_store.setValues(1, 6, test_builder.to_registers()) self.assertEqual(13.5, test_handler.decode(1, 6, test_fields['scale_16_int']['encode'])) test_builder.reset() test_builder.add_16bit_uint(3287) test_store.setValues(1, 8, test_builder.to_registers()) self.assertEqual(32.87, test_handler.decode(1, 8, test_fields['scale_16_uint']['encode'])) test_builder.reset()
def run_server(): store = ModbusSlaveContext(di=ModbusSequentialDataBlock(0, [0] * 10000), co=ModbusSequentialDataBlock(0, [0] * 10000), hr=ModbusSequentialDataBlock(0, [0] * 10000), ir=ModbusSequentialDataBlock(0, [0] * 10000)) context = ModbusServerContext(slaves=store, single=True) identity = ModbusDeviceIdentification() identity.VendorName = 'Pymodbus' identity.ProductCode = 'PM' # identity.VendorUrl = 'http://github.com/riptideio/pymodbus/' identity.ProductName = 'Pymodbus Server' identity.ModelName = 'Pymodbus Server' identity.MajorMinorRevision = '1.5' #----------------------------------------------------- print("debug started\n\n") print(dir(store)) store.setValues(3, 53, [30000]) print("\n\ndebug finished") #----------------------------------------------------- print("Listening on {}:{}".format(HOST, PORT)) StartTcpServer(context, identity=identity, address=(HOST, PORT))
def test_no_e_type(self): test_store = ModbusSlaveContext(t=ModbusSequentialDataBlock.create()) test_env = {'fields': {'8_int': {'reg_type': CO, 'address': 1, 'encode': {'d_type': INT8} }, '8_uint': {'reg_type': CO, 'address': 2, 'encode': {'d_type': UINT8} }, '16_int': {'reg_type': CO, 'address': 3, 'encode': {'d_type': INT16} }, '16_uint': {'reg_type': CO, 'address': 4, 'encode': {'d_type': UINT16} }, '32_int': {'reg_type': CO, 'address': 5, 'encode': {'d_type': INT32} }, '32_uint': {'reg_type': CO, 'address': 6, 'encode': {'d_type': UINT32} }}, 'byte_order': '<', 'word_order': '<', 'default_scaling_factor': 1000} test_fields = test_env['fields'] test_handler = PayloadHandler(test_env, test_store) test_store.setValues(1, 1, test_handler.encode(1, test_fields['8_int']['encode'])) self.assertEqual(1, test_handler.decode(1, 1, test_fields['8_int']['encode'])) test_store.setValues(1, 2, test_handler.encode(10, test_fields['8_uint']['encode'])) self.assertEqual(10, test_handler.decode(1, 2, test_fields['8_uint']['encode'])) test_store.setValues(1, 3, test_handler.encode(12, test_fields['16_int']['encode'])) self.assertEqual(12, test_handler.decode(1, 3, test_fields['16_int']['encode'])) test_store.setValues(1, 4, test_handler.encode(32, test_fields['16_uint']['encode'])) self.assertEqual(32, test_handler.decode(1, 4, test_fields['16_uint']['encode'])) test_store.setValues(1, 5, test_handler.encode(1000, test_fields['32_int']['encode'])) self.assertEqual(1000, test_handler.decode(1, 5, test_fields['32_int']['encode'])) test_store.setValues(1, 6, test_handler.encode(63, test_fields['32_uint']['encode'])) self.assertEqual(63, test_handler.decode(1, 6, test_fields['32_uint']['encode']))
def test_combo(self): test_store = ModbusSlaveContext(t=ModbusSequentialDataBlock.create()) test_env = {'fields': {'scale_8_int': {'reg_type': CO, 'address': 1, 'encode': {'e_type': SCALE, 's_factor': 100, 'd_type': INT8} }, 'scale_8_uint': {'reg_type': CO, 'address': 2, 'encode': {'e_type': SCALE, 's_factor': 10, 'd_type': UINT8} }, 'scale_16_int': {'reg_type': CO, 'address': 3, 'encode': {'e_type': SCALE, 's_factor': 100, 'd_type': INT16} }, 'scale_16_uint': {'reg_type': CO, 'address': 4, 'encode': {'e_type': SCALE, 's_factor': 100, 'd_type': UINT16} }, 'scale_32_int': {'reg_type': CO, 'address': 5, 'encode': {'e_type': SCALE, 'd_type': INT32} }, 'scale_32_uint': {'reg_type': CO, 'address': 6, 'encode': {'e_type': SCALE, 'd_type': UINT32} }, 'scale_32_float': {'reg_type': CO, 'address': 7, 'encode': {'e_type': SCALE, 's_factor': 10, 'd_type': FLOAT32} }, 'comb_32_float': {'reg_type': CO, 'address': 8, 'encode': {'e_type': COMB, 'd_type': FLOAT32} }}, 'byte_order': '<', 'word_order': '<', 'default_scaling_factor': 1000} test_fields = test_env['fields'] test_handler = PayloadHandler(test_env, test_store) test_store.setValues(1, 1, test_handler.encode(1, test_fields['scale_8_int']['encode'])) self.assertEqual(1, test_handler.decode(1, 1, test_fields['scale_8_int']['encode'])) test_store.setValues(1, 2, test_handler.encode(10, test_fields['scale_8_uint']['encode'])) self.assertEqual(10, test_handler.decode(1, 2, test_fields['scale_8_uint']['encode'])) test_store.setValues(1, 3, test_handler.encode(12.5, test_fields['scale_16_int']['encode'])) self.assertEqual(12.5, test_handler.decode(1, 3, test_fields['scale_16_int']['encode'])) test_store.setValues(1, 4, test_handler.encode(32.991, test_fields['scale_16_uint']['encode'])) self.assertEqual(32.99, test_handler.decode(1, 4, test_fields['scale_16_uint']['encode'])) test_store.setValues(1, 5, test_handler.encode(1000, test_fields['scale_32_int']['encode'])) self.assertEqual(1000, test_handler.decode(1, 5, test_fields['scale_32_int']['encode'])) test_store.setValues(1, 6, test_handler.encode(63.2, test_fields['scale_32_uint']['encode'])) self.assertEqual(63.2, test_handler.decode(1, 6, test_fields['scale_32_uint']['encode'])) test_store.setValues(1, 7, test_handler.encode(1.333, test_fields['scale_32_float']['encode'])) self.assertEqual(1.3, test_handler.decode(1, 7, test_fields['scale_32_float']['encode'])) test_store.setValues(1, 8, test_handler.encode(789.4375, test_fields['comb_32_float']['encode'])) self.assertEqual(789.4375, test_handler.decode(1, 8, test_fields['comb_32_float']['encode']))
block_1601.add_16bit_int(10000) # power/energy adjustment l1 block_1601.add_16bit_int(10000) # power/energy adjustment l2 block_1601.add_16bit_int(10000) # power/energy adjustment l3 block_1601.add_16bit_int(-1000) # ct phase angle adjustment l1 block_1601.add_16bit_int(-1000) # ct phase angle adjustment l2 block_1601.add_16bit_int(-1000) # ct phase angle adjustment l3 block_1601.add_16bit_int(1500) # minimum power reading block_1601.add_16bit_int( confparser[meter].getint("phase_offset", fallback=default_config["meters"] ["phase_offset"])) # phase offset block_1601.add_16bit_int(0) # reset energy block_1601.add_16bit_int(0) # reset demand block_1601.add_16bit_int(20000) # current scale block_1601.add_16bit_int(0) # io pin mode slave_ctx.setValues(3, 1600, block_1601.to_registers()) block_1651 = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) block_1651.add_16bit_int(0) # apply config block_1651.add_16bit_int(address) # modbus address block_1651.add_16bit_int(4) # baud rate block_1651.add_16bit_int(0) # parity mode block_1651.add_16bit_int(0) # modbus mode block_1651.add_16bit_int(5) # message delay slave_ctx.setValues(3, 1650, block_1651.to_registers()) block_1701 = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) block_1701.add_32bit_int(confparser[meter].getint( "serial_number",
class ModbusWrapperServer(): def __init__(self, port=1234, sub_topic="modbus_server/write_to_registers", pub_topic="modbus_server/read_from_registers"): """ Creates a Modbus TCP Server object .. note:: The default port for modbus is 502. This modbus server uses port 1234 by default, otherwise superuser rights are required. .. note:: Use "startServer" to start the listener. :param port: Port for the modbus TCP server :type port: int :param sub_topic: ROS topic name for the subscriber that updates the modbus registers :type sub_topic: string :param pub_topic: ROS topic name for the publisher that publishes a message, once there is something written to the writeable modbus registers :type pub_topic: string """ chr = CustomHoldingRegister(ADDRESS_WRITE_START, [17] * 100, sub_topic, pub_topic) self.store = ModbusSlaveContext( di=ModbusSequentialDataBlock(ADDRESS_WRITE_START, [17] * 100), co=ModbusSequentialDataBlock(ADDRESS_WRITE_START, [17] * 100), hr=chr, ir=ModbusSequentialDataBlock(ADDRESS_WRITE_START, [17] * 100)) self.context = ModbusServerContext(slaves=self.store, single=True) self.identity = ModbusDeviceIdentification() self.identity.VendorName = 'Pymodbus' self.identity.ProductCode = 'PM' self.identity.VendorUrl = 'http://github.com/bashwork/pymodbus/' self.identity.ProductName = 'Pymodbus Server' self.identity.ModelName = 'Pymodbus Server' self.identity.MajorMinorRevision = '1.0' self.store.setValues(2, 0, [0] * 1) self.post = Post(self) framer = ModbusSocketFramer self.server = ModbusTcpServer(self.context, framer, self.identity, address=("0.0.0.0", port)) def startServer(self): """ Non blocking call to start the server """ self.post.__startServer() rospy.loginfo("Modbus server started") def __startServer(self): self.server.serve_forever() def stopServer(self): """ Closes the server """ self.server.server_close() self.server.shutdown() def waitForCoilOutput(self, address, timeout=2): """ Blocks for the timeout in seconds (or forever) until the specified address becomes true. Adapt this to your needs :param address: Address of the register that wanted to be read. :type address: int :param timeout: The time in seconds until the function should return latest. :type: float/int """ now = time.time() while True: values = self.store.getValues(1, address, 1) if values[0] is True: return True else: if timeout <= 0 or now + timeout > time.time(): time.sleep(1 / 50) else: return False def setDigitalInput(self, address, values): """ Writes to the digital input of the modbus server :param address: Starting address of the values to write :type: int :param values: List of values to write :type list/boolean/int """ if not values is list: values = [values] self.store.setValues(2, address, values)
def run_server(): store = ModbusSlaveContext(di=ModbusSequentialDataBlock(0, [0] * 10000), co=ModbusSequentialDataBlock(0, [0] * 10000), hr=ModbusSequentialDataBlock(0, [0] * 10000), ir=ModbusSequentialDataBlock(0, [0] * 10000)) context = ModbusServerContext(slaves=store, single=True) identity = ModbusDeviceIdentification() identity.VendorName = 'Pymodbus' identity.ProductCode = 'PM' # identity.VendorUrl = 'http://github.com/riptideio/pymodbus/' identity.ProductName = 'Pymodbus Server' identity.ModelName = 'Pymodbus Server' identity.MajorMinorRevision = '1.5' #----------------------------------------------------- # print("debug started\n\n") # print(dir(store)) store.setValues(3, 53, [30000]) store.setValues(3, 32, [14373]) store.setValues(3, 33, [28911]) store.setValues(3, 61, [59626]) store.setValues(3, 26, [22288]) store.setValues(3, 89, [59530]) store.setValues(3, 90, [22198]) store.setValues(3, 2, [27306]) store.setValues(3, 10, [24285]) store.setValues(3, 52, [9160]) store.setValues(3, 32, [8707]) store.setValues(3, 74, [41718]) store.setValues(3, 78, [40553]) store.setValues(3, 93, [44444]) store.setValues(3, 65, [45136]) store.setValues(3, 24, [39191]) store.setValues(2, 1, [1]) store.setValues(2, 12, [1]) store.setValues(2, 101, [1]) store.setValues(2, 8, [1]) store.setValues(2, 94, [1]) store.setValues(2, 23, [1]) store.setValues(2, 34, [1]) store.setValues(2, 65, [1]) store.setValues(2, 66, [1]) store.setValues(2, 67, [1]) store.setValues(2, 97, [1]) store.setValues(2, 99, [1]) store.setValues(2, 14, [1]) store.setValues(2, 76, [1]) store.setValues(1, 1, [1]) store.setValues(1, 11, [1]) store.setValues(1, 21, [1]) store.setValues(1, 31, [1]) store.setValues(1, 41, [1]) store.setValues(1, 42, [1]) store.setValues(1, 101, [1]) store.setValues(1, 129, [1]) store.setValues(1, 259, [1]) store.setValues(1, 111, [1]) store.setValues(1, 192, [1]) store.setValues(4, 0, [22]) store.setValues(4, 19, [69]) store.setValues(4, 40, [99]) store.setValues(4, 51, [14]) store.setValues(4, 17, [35]) store.setValues(4, 28, [24]) store.setValues(4, 54, [31]) store.setValues(4, 7, [84]) store.setValues(4, 12, [31]) store.setValues(4, 43, [7]) store.setValues(4, 53, [54]) store.setValues(4, 2, [69]) store.setValues(4, 35, [38]) store.setValues(4, 53, [50]) store.setValues(4, 17, [99]) # print("\n\ndebug finished") #----------------------------------------------------- print("Listening on {}:{}".format(HOST, PORT)) StartTcpServer(context, identity=identity, address=(HOST, PORT))
class plcSlave(): def __init__(self, plcIP, plcPort, initialRegisters, plcName): self.plcIP=plcIP self.plcPort=plcPort self.dataStore = 0 self.context = 0 self.identity = 0 self.initialRegisters = initialRegisters self.plcName = plcName self.run_async_server(address=(self.plcIP,self.plcPort)) def run_async_server(self, address): # ----------------------------------------------------------------------- # # initialize your data store # ----------------------------------------------------------------------- # # The datastores only respond to the addresses that they are initialized to # Therefore, if you initialize a DataBlock to addresses from 0x00 to 0xFF, # a request to 0x100 will respond with an invalid address exception. # This is because many devices exhibit this kind of behavior (but not all) # # block = ModbusSequentialDataBlock(0x00, [0]*0xff) # # Continuing, you can choose to use a sequential or a sparse DataBlock in # your data context. The difference is that the sequential has no gaps in # the data while the sparse can. Once again, there are devices that exhibit # both forms of behavior:: # # block = ModbusSparseDataBlock({0x00: 0, 0x05: 1}) # block = ModbusSequentialDataBlock(0x00, [0]*5) # # Alternately, you can use the factory methods to initialize the DataBlocks # or simply do not pass them to have them initialized to 0x00 on the full # address range:: # # store = ModbusSlaveContext(di = ModbusSequentialDataBlock.create()) # store = ModbusSlaveContext() # # Finally, you are allowed to use the same DataBlock reference for every # table or you you may use a seperate DataBlock for each table. # This depends if you would like functions to be able to access and modify # the same data or not:: # # block = ModbusSequentialDataBlock(0x00, [0]*0xff) # store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block) # # The server then makes use of a server context that allows the server to # respond with different slave contexts for different unit ids. By default # it will return the same context for every unit id supplied (broadcast # mode). # However, this can be overloaded by setting the single flag to False # and then supplying a dictionary of unit id to context mapping:: # # slaves = { # 0x01: ModbusSlaveContext(...), # 0x02: ModbusSlaveContext(...), # 0x03: ModbusSlaveContext(...), # } # context = ModbusServerContext(slaves=slaves, single=False) # # The slave context can also be initialized in zero_mode which means that a # request to address(0-7) will map to the address (0-7). The default is # False which is based on section 4.4 of the specification, so address(0-7) # will map to (1-8):: # # store = ModbusSlaveContext(..., zero_mode=True) # ----------------------------------------------------------------------- # self.dataStore = ModbusSlaveContext( di=ModbusSequentialDataBlock(0, [17]*100), co=ModbusSequentialDataBlock(0, [17]*100), hr=ModbusSequentialDataBlock(0, [17]*100), ir=ModbusSequentialDataBlock(0, [17]*100)) self.context = ModbusServerContext(slaves=self.dataStore, single=True) # ----------------------------------------------------------------------- # # initialize the server information # ----------------------------------------------------------------------- # # If you don't set this or any fields, they are defaulted to empty strings. # ----------------------------------------------------------------------- # self.identity = ModbusDeviceIdentification() self.identity.VendorName = 'Pymodbus' self.identity.ProductCode = 'PM' self.identity.VendorUrl = 'http://github.com/bashwork/pymodbus/' self.identity.ProductName = 'Pymodbus Server' self.identity.ModelName = 'Pymodbus Server' self.identity.MajorMinorRevision = '1.0' # ----------------------------------------------------------------------- # # run the server (listener) # ----------------------------------------------------------------------- # framer = ModbusSocketFramer factory = ModbusServerFactory(self.context, framer, self.identity) reactor.listenTCP(address[1], factory, interface=address[0]) def start(self): coil_list = 1 holdregister = 3 self.setCoils(0x00,[1,0,0,0,0,0,0,0,0,0,0,0,0]) self.setHoldingRegisters(0x00,self.initialRegisters) threading.Thread(target = self.operateLightTimer).start() def getHoldingRegisters(self, startByte, bytesCount): return self.dataStore.getValues(3, startByte,count=bytesCount) def getCoils(self, startBit, bitCount): return self.dataStore.getValues(1,startBit,count=bitCount) def setHoldingRegisters(self,address,values): self.dataStore.setValues(3,address,values) def setCoils(self, address, values): self.dataStore.setValues(1,address,values) def changeLightColor(self,northsouth, color): registers = self.getHoldingRegisters(0x00,4) if northsouth == 1: registers[0] = color registers[2] = color else: registers[1] = color registers[3] = color self.dataStore.setValues(3,0x00,registers) def operateLightTimer(self): ################################# # coils[0] = 1 - machine initialization (move to normal operations) # coils[1] = 1 - machine moved to normal operations # coils[2] = 1 - east/west side is green or yellow # coils[3] = 1 - north/south side is green yellow # coils[4] = 1 - put the machine into maintenancemode (ALL FLASHING RED LIGHTS) # coils[5] = 1 - machine moved to mainteance mode # coils[6] = 1 - puts the machine into TEST Mode (ALL LIGHTS ON) # coils[7] = 1 - machine moved to TEST mode # holdingRegister[0,2] - north/south light state 0 = red, 1 = yellow, 2 = green, 3 = ALL OFF, 4 = ALL ON # holdingRegister[1,3] - east/west light state # holdingRegister [4] - timer/counter # holdingRegisters [5,6,7] - timer values that cause an increment in logic ################ coils = self.getCoils(0,10) #this is the main operation loop while True: coils = self.getCoils(0,10) regs = self.getHoldingRegisters(0,10) if coils[0] == 1: #initialize NORMAL OPS print("The traffic light at: " + str(self.plcName) + " is starting") self.setCoils(0x00,[0,1,1,0,0,0,0,0,0]) self.setHoldingRegisters(0x00,[0,2,0,2,0,10,12,14]) elif coils[4] == 1: #move into MAINTENANCE print("The traffic light at: " + str(self.plcName) + " is going into MAINTENANCE") self.setCoils(0x00,[0,0,0,0,0,1,0,0,0]) self.setHoldingRegisters(0x00,[0,0,0,0,0]) elif coils[6] == 1: #move into TEST print("The traffic light at: " + str(self.plcName) + " is going into TEST mode") self.setCoils(0x00,[0,0,0,0,0,0,0,1,0]) self.setHoldingRegisters(0x00,[0,0,0,0,0]) counter = regs[4] print(self.plcName + "....Coils: " + str(coils[0:10]) +"\t\tRegisters: " + str(regs[0:10])) if coils[1] == True: ##standard traffic light operation self.setHoldingRegisters(4,[counter + 1]) if counter == regs[5]: #change the green to yellow sys.stdout.write("X") sys.stdout.flush() if coils[2] == 1: self.changeLightColor(0, 1) #change the east west side to yellow if coils[3] == 1: self.changeLightColor(1,1) #change the north/south side to yellow elif counter == regs[6]: #everyone goes to red sys.stdout.write("X") sys.stdout.flush() self.changeLightColor(1,0) self.changeLightColor(0,0) elif counter == regs[7]: #change the red light to green (for the favored side) sys.stdout.write("X") sys.stdout.flush() if coils[2] == 1: #east/west was green, change it to red self.setCoils(0x02,[0,1]) self.changeLightColor(1,2) elif coils[3] == 1: #north/south was green, change it to red self.setCoils(0x02,[1,0]) self.changeLightColor(0,2) self.setHoldingRegisters(0x04,[0]) else: sys.stdout.write(".") sys.stdout.flush() time.sleep(1) elif coils[5] == True: ##maintenance if regs[0] == 0: #displaying a red light, make it flash self.setHoldingRegisters(0,[3,3,3,3]) elif regs[0] == 3: #blank light, change to red self.setHoldingRegisters(0,[0,0,0,0]) else: self.setHoldingRegisters(0,[3,3,3,3]) time.sleep(1) elif coils[7] == True: #test mode if regs[0] == 3: #displaying a red light, make it flash self.setHoldingRegisters(0,[4,4,4,4]) elif regs[0] == 4: #blank light, change to red self.setHoldingRegisters(0,[3,3,3,3]) else: self.setHoldingRegisters(0,[3,3,3,3]) time.sleep(1)
class ModbusWrapperServer(): def __init__(self,port=1234,sub_topic="modbus_server/write_to_registers",pub_topic="modbus_server/read_from_registers"): """ Creates a Modbus TCP Server object .. note:: The default port for modbus is 502. This modbus server uses port 1234 by default, otherwise superuser rights are required. .. note:: Use "startServer" to start the listener. :param port: Port for the modbus TCP server :type port: int :param sub_topic: ROS topic name for the subscriber that updates the modbus registers :type sub_topic: string :param pub_topic: ROS topic name for the publisher that publishes a message, once there is something written to the writeable modbus registers :type pub_topic: string """ chr = CustomHoldingRegister(ADDRESS_WRITE_START, [17]*100,sub_topic,pub_topic) self.store = ModbusSlaveContext( di = ModbusSequentialDataBlock(ADDRESS_WRITE_START, [17]*100), co = ModbusSequentialDataBlock(ADDRESS_WRITE_START, [17]*100), hr = chr, ir = ModbusSequentialDataBlock(ADDRESS_WRITE_START, [17]*100)) self.context = ModbusServerContext(slaves=self.store, single=True) self.identity = ModbusDeviceIdentification() self.identity.VendorName = 'Pymodbus' self.identity.ProductCode = 'PM' self.identity.VendorUrl = 'http://github.com/bashwork/pymodbus/' self.identity.ProductName = 'Pymodbus Server' self.identity.ModelName = 'Pymodbus Server' self.identity.MajorMinorRevision = '1.0' self.store.setValues(2,0,[0]*1) self.post = Post(self) framer = ModbusSocketFramer self.server = ModbusTcpServer(self.context, framer, self.identity, address=("0.0.0.0", port)) def startServer(self): """ Non blocking call to start the server """ self.post.__startServer() rospy.loginfo("Modbus server started") def __startServer(self): self.server.serve_forever() def stopServer(self): """ Closes the server """ self.server.server_close() self.server.shutdown() def waitForCoilOutput(self,address,timeout=2): """ Blocks for the timeout in seconds (or forever) until the specified address becomes true. Adapt this to your needs :param address: Address of the register that wanted to be read. :type address: int :param timeout: The time in seconds until the function should return latest. :type: float/int """ now = time.time() while True: values = self.store.getValues(1,address, 1) if values[0] is True: return True else: if timeout <=0 or now + timeout > time.time(): time.sleep(1/50) else: return False def setDigitalInput(self,address,values): """ Writes to the digital input of the modbus server :param address: Starting address of the values to write :type: int :param values: List of values to write :type list/boolean/int """ if not values is list: values = [values] self.store.setValues(2,address,values)
class ModbusWrapperServer(): def __init__(self,port=1234): """ Creates a Modbus TCP Server object .. note:: The default port for modbus is 502. This modbus server uses port 1234 by default, otherwise superuser rights are required. .. note:: Use "startServer" to start the listener. :param port: Port for the modbus TCP server :type port: int """ # chr = CustomHoldingRegister(ADDRESS_WRITE_START, [16]*100) # cdi = CustomHoldingRegister(ADDRESS_WRITE_START, [16]*100) self.onShutdown=False; self.state_changed = Signal() cco = CustomHoldingRegister(ADDRESS_WRITE_START, [16]*100) self.store = ModbusSlaveContext( di = ModbusSequentialDataBlock(ADDRESS_WRITE_START, [16]*100), co = cco, hr = ModbusSequentialDataBlock(ADDRESS_WRITE_START, [16]*100), ir = ModbusSequentialDataBlock(ADDRESS_WRITE_START, [16]*100)) self.context = ModbusServerContext(slaves=self.store, single=True) self.identity = ModbusDeviceIdentification() self.identity.VendorName = 'Pymodbus' self.identity.ProductCode = 'PM' self.identity.VendorUrl = 'http://github.com/bashwork/pymodbus/' self.identity.ProductName = 'Pymodbus Server' self.identity.ModelName = 'Pymodbus Server' self.identity.MajorMinorRevision = '1.0' self.store.setValues(2,0,[0]*1) self.post = Post(self) framer = ModbusSocketFramer self.server = ModbusTcpServer(self.context, framer, self.identity, address=("0.0.0.0", port)) def startServer(self): """ Non blocking call to start the server """ self.post._startServer() def _startServer(self): print "Modbus server started" self.server.serve_forever() def stopServer(self): """ Closes the server """ self.onShutdown = True self.server.server_close() self.server.shutdown() def waitForCoilOutput(self,address,timeout=2): now = time.time() while not self.onShutdown: values = self.store.getValues(1,address, 1) if values[0] is True: return True else: if timeout > 0 and now + timeout > time.time(): time.sleep(1/50) else: return False def stateChangeListener(self,address): self.post.__stateChangeListener(address) def __stateChangeListener(self,address): old_state = 0; while not self.onShutdown: value = self.store.getValues(1,address, 1)[0] if old_state != value: old_state = value self.state_changed(address,value) #print "state has changed" time.sleep(1/50) def setDigitalInput(self,address,values): if not values is list: values = [values] self.store.setValues(2,address,values) def triggerInput(self,address,timeout=0.5): self.setDigitalInput(address, True) self.post.resetInput(address, timeout) def resetInput(self,address,timeout): time.sleep(timeout) self.setDigitalInput(address, False)