00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042 import rospy
00043 try:
00044 from pymodbus.client.sync import ModbusTcpClient
00045 except Exception,e:
00046 print "pymodbus does not seem to be installed.\nInstall it by:\nsudo apt-get install python-pymodbus"
00047 print e
00048 exit()
00049 from std_msgs.msg import Int32MultiArray as HoldingRegister
00050 from post_threading import Post
00051 from threading import Lock
00052
00053 NUM_REGISTERS = 20
00054 ADDRESS_READ_START = 40000
00055 ADDRESS_WRITE_START = 40020
00056
00057 class ModbusWrapperClient():
00058 """
00059 Wrapper that integrates python modbus into standardized ros msgs.
00060 The wrapper is able to read from and write to a standard modbus tcp/ip server.
00061 """
00062 def __init__(self,host,port=502,rate=50,reset_registers=True,sub_topic="modbus_wrapper/output",pub_topic="modbus_wrapper/input"):
00063 """
00064 Use subscribers and publisher to communicate with the modbus server. Check the scripts for example code.
00065 :param host: Contains the IP adress of the modbus server
00066 :type host: string
00067 :param port: The port number, where the modbus server is runnning
00068 :type port: integer
00069 :param rate: How often the registers on the modbusserver should be read per second
00070 :type rate: float
00071 :param reset_registers: Defines if the holding registers should be reset to 0 after they have been read. Only possible if they are writeable
00072 :type reset_registers: bool
00073 """
00074 try:
00075 self.client = ModbusTcpClient(host,port)
00076 except Exception, e:
00077 rospy.logwarn("Could not get a modbus connection to the host modbus. %s", str(e))
00078 raise e
00079 return
00080 self.__rate = rate
00081 self.__reading_delay = 1/rate
00082 self.post = Post(self)
00083
00084 self.__reset_registers = reset_registers
00085 self.__reading_register_start = 0
00086 self.__num_reading_registers = 20
00087
00088 self.__input = HoldingRegister()
00089 self.__input.data = [0 for i in xrange(self.__num_reading_registers )]
00090
00091 self.__writing_registers_start = ADDRESS_WRITE_START
00092 self.__num_writing_registers = 20
00093
00094 self.__output = [None for i in range(self.__num_writing_registers)]
00095
00096 self.__last_output_time = rospy.get_time()
00097 self.__mutex = Lock()
00098
00099 self.__sub = rospy.Subscriber(sub_topic,HoldingRegister,self.__updateModbusOutput,queue_size=500)
00100 self.__pub = rospy.Publisher(pub_topic,HoldingRegister,queue_size=500, latch=True)
00101
00102
00103
00104 rospy.on_shutdown(self.closeConnection)
00105
00106 def startListening(self):
00107 """
00108 Non blocking call for starting the listener for the readable modbus server registers
00109 """
00110
00111 self.post.__updateModbusInput()
00112
00113 def stopListening(self):
00114 """
00115 Stops the listener loop
00116 """
00117 self.stop_listener = True
00118 while not rospy.is_shutdown() and self.listener_stopped is False:
00119 rospy.sleep(0.01)
00120
00121 def setReadingRegisters(self,start,num_registers):
00122 """
00123 Sets the start address of the registers which should be read and their number
00124 :param start: First register that is readable
00125 :type start: int
00126 :param num_registers: Amount of readable registers
00127 :type num_registers: int
00128 """
00129 self.__reading_register_start = start
00130 self.__num_reading_registers = num_registers
00131
00132 def setWritingRegisters(self,start,num_registers):
00133 """
00134 Sets the start address of the registers which are writeable and their number
00135 :param start: First register that is writeable
00136 :type start: int
00137 :param num_registers: Amount of writeable registers
00138 :type num_registers: int
00139 """
00140 self.__writing_registers_start = start
00141 self.__num_writing_registers = num_registers
00142
00143 def getReadingRegisters(self):
00144 """
00145 :return: Returns the first address of the readable registers and the number of registers
00146 :rtype: int,int
00147 """
00148 return self.__reading_register_start,self.__num_reading_registers
00149
00150 def getWritingRegisters(self):
00151 """
00152 :return: Returns the first address of the writeable registers and the number of registers
00153 :rtype: int,int
00154 """
00155 return self.__writing_registers_start,self.__num_writing_registers
00156
00157 def __updateModbusInput(self,delay=0):
00158 """
00159 Loop that is listening to the readable modbus registers and publishes it on a topic
00160 :param delay: The delay time until the loop starts
00161 :type delay: float
00162 """
00163 rospy.sleep(delay)
00164 self.listener_stopped = False
00165 self.stop_listener = False
00166 update = True
00167 while not rospy.is_shutdown() and self.stop_listener is False:
00168 try:
00169 if not rospy.is_shutdown() :
00170 tmp = self.readRegisters()
00171 if tmp is None:
00172 rospy.sleep(2)
00173 continue
00174
00175
00176
00177 if tmp != self.__input.data:
00178 update = True
00179 self.__input.data = tmp
00180 else:
00181 update = False
00182 except Exception,e:
00183 rospy.logwarn("Could not read holding register. %s", str(e))
00184 raise e
00185 rospy.sleep(2)
00186
00187 if update:
00188 if self.__pub.get_num_connections() > 0:
00189 try:
00190 self.__pub.publish(self.__input)
00191 except Exception,e:
00192 rospy.logwarn("Could not publish message. Exception: %s",str(e))
00193 raise e
00194 rospy.Rate(self.__rate).sleep()
00195 self.listener_stopped = True
00196
00197 def __updateModbusOutput(self,msg):
00198 """
00199 Callback from the subscriber to update the writeable modbus registers
00200 :param msg: value of the new registers
00201 :type msg: std_msgs.Int32MultiArray
00202 """
00203 output_changed = False
00204 for index in xrange(self.__num_writing_registers):
00205 if self.__output[index] != msg.data[index]:
00206 output_changed = True
00207 break
00208 if not output_changed:
00209 return
00210 self.__writeRegisters(self.__writing_registers_start,msg.data)
00211
00212 def __writeRegisters(self,address,values):
00213 """
00214 Writes modbus registers
00215 :param address: First address of the values to write
00216 :type address: int
00217 :param values: Values to write
00218 :type values: list
00219 """
00220 with self.__mutex:
00221 try:
00222 if not rospy.is_shutdown() :
00223
00224 self.client.write_registers(address, values)
00225 self.output = values
00226 except Exception, e:
00227 rospy.logwarn("Could not write values %s to address %d. Exception %s",str(values),address, str(e))
00228 raise e
00229
00230 def readRegisters(self,address=None,num_registers=None):
00231 """
00232 Reads modbus registers
00233 :param address: First address of the registers to read
00234 :type address: int
00235 :param num_registers: Amount of registers to read
00236 :type num_registers: int
00237 """
00238 if address is None:
00239 address = self.__reading_register_start
00240 if num_registers is None:
00241 num_registers = self.__num_reading_registers
00242
00243 tmp = None
00244 with self.__mutex:
00245 try:
00246 tmp = self.client.read_holding_registers(address,num_registers).registers
00247 except Exception, e:
00248 rospy.logwarn("Could not read on address %d. Exception: %s",address,str(e))
00249 raise e
00250
00251
00252 if self.__reset_registers:
00253 try:
00254 self.client.write_registers(address, [0 for i in xrange(num_registers)])
00255 except Exception, e:
00256 rospy.logwarn("Could not write to address %d. Exception: %s", address,str(e))
00257 raise e
00258
00259 return tmp
00260
00261 def setOutput(self,address,value,timeout=0):
00262 """
00263 Directly write one register
00264 :param address: The exact register address to write
00265 :type address: int
00266 :param value: What is written in the register
00267 :type value: int
00268 :param timeout: If set, the register is set after this time to 0
00269 :type timeout: float
00270 """
00271 if not type(value) is list:
00272 value = [int(value)]
00273 self.__writeRegisters(address, value)
00274 if timeout > 0:
00275 self.post.__reset(address,timeout)
00276
00277 def __reset(self,address,timeout):
00278 """
00279 Resets a register to 0 after a specific amount of time
00280 :param address: The register address to reset
00281 :type address: int
00282 :param timeout: The delay after the register is reset
00283 :type timeout: float
00284 """
00285 rospy.sleep(timeout)
00286 self.__writeRegisters(address,[0])
00287
00288 def closeConnection(self):
00289 """
00290 Closes the connection to the modbus
00291 """
00292 self.client.close()
00293
00294
00295