Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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 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):
        """
            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)