modbus_wrapper_server.py
Go to the documentation of this file.
00001 
00002 ########################################################################### 
00003 # This software is graciously provided by HumaRobotics 
00004 # under the BSD License on
00005 # github: https://github.com/Humarobotics/modbus_wrapper
00006 # HumaRobotics is a trademark of Generation Robots.
00007 # www.humarobotics.com 
00008 #
00009 # Copyright (c) 2013, Generation Robots.
00010 # All rights reserved.
00011 # www.generationrobots.com
00012 #
00013 # This wrapper package is based on the pymodbus library developed by:
00014 # Galen Collins
00015 # github: https://github.com/bashwork/pymodbus
00016 #   
00017 # Redistribution and use in source and binary forms, with or without 
00018 # modification, are permitted provided that the following conditions are met:
00019 # 
00020 # 1. Redistributions of source code must retain the above copyright notice,
00021 #  this list of conditions and the following disclaimer.
00022 # 
00023 # 2. Redistributions in binary form must reproduce the above copyright notice,
00024 #  this list of conditions and the following disclaimer in the documentation 
00025 #  and/or other materials provided with the distribution.
00026 # 
00027 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00028 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
00029 # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
00030 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
00031 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
00032 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
00033 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
00034 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
00035 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
00036 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
00037 # THE POSSIBILITY OF SUCH DAMAGE. 
00038 # 
00039 # The views and conclusions contained in the software and documentation are 
00040 # those of the authors and should not be interpreted as representing official 
00041 # policies, either expressed or implied, of the FreeBSD Project.
00042 import rospy
00043 from post_threading import Post
00044 from std_msgs.msg import Int32MultiArray as HoldingRegister
00045 
00046 
00047 try:
00048     from pymodbus.server.sync import ModbusSocketFramer, ModbusTcpServer
00049     from pymodbus.device import ModbusDeviceIdentification
00050     from pymodbus.datastore import ModbusSequentialDataBlock
00051     from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
00052 except Exception,e:
00053     print "-- INSTALL DEPENDENCIES -- "
00054     print "sudo apt-get install python-pymodbus"    
00055     print "or from source:"
00056     print "git clone git@github.com:bashwork/pymodbus.git"
00057     print "cd pymodbus"
00058     print "sudo python setup.py install"
00059     print "and"
00060     print "sudo apt-get install python-pyasn1 python-twisted-conch"
00061     print e
00062     exit()
00063 
00064 ADDRESS_WRITE_START = 40001
00065 ADDRESS_READ_START = 40021
00066 
00067 class CustomHoldingRegister(ModbusSequentialDataBlock):
00068     def __init__(self,address,value,sub_topic,pub_topic):
00069         """
00070             Creates a custom holding register to add a publisher and subscriber to the modbus server
00071             
00072             :param address: The starting address of the holding register
00073             :type address: int
00074             :param values: The initial values for the holding registers
00075             :type values: list[int]
00076             :param sub_topic: ROS topic name for the subscriber that updates the modbus registers
00077             :type sub_topic: string
00078             :param pub_topic: ROS topic name for the publisher that publishes a message, once there is something written to the writeable modbus registers
00079             :type pub_topic: string
00080         """
00081         
00082         super(CustomHoldingRegister,self).__init__(address,value)
00083         self.reading_start = ADDRESS_READ_START
00084         self.writing_start = ADDRESS_WRITE_START
00085         self.sub = rospy.Subscriber(sub_topic,HoldingRegister,self.__updateWriteableRegisters,queue_size=500)
00086         self.pub = rospy.Publisher(pub_topic,HoldingRegister,queue_size=500)
00087         
00088         
00089     def setValues(self, address, value):
00090         """ 
00091             Sets the requested values to the holding registers
00092             and publishes they new values on a rostopic 
00093 
00094             :param address: The starting address
00095             :type address: int
00096             :param values: The new values to be set
00097             :type values: list[int]
00098         """
00099 #         print "address",address,"value",value
00100         
00101         
00102         ModbusSequentialDataBlock.setValues(self,address, value)
00103         if address >= self.reading_start:
00104             msg = HoldingRegister()
00105             msg.data = value
00106             self.pub.publish(msg)
00107         
00108     def __updateWriteableRegisters(self,msg):
00109         if len(msg.data) > self.reading_start+1:
00110             rospy.logwarn("Message to long. Shorten it or it will be ignored")
00111         self.setValues(self.writing_start, list(msg.data))
00112         
00113 
00114 
00115 class ModbusWrapperServer():
00116     def __init__(self,port=1234,sub_topic="modbus_server/write_to_registers",pub_topic="modbus_server/read_from_registers"):
00117         """
00118             Creates a Modbus TCP Server object
00119             .. note:: The default port for modbus is 502. This modbus server uses port 1234 by default, otherwise superuser rights are required.
00120             
00121             .. note:: Use "startServer" to start the listener.
00122             
00123             :param port: Port for the modbus TCP server
00124             :type port: int
00125             :param sub_topic: ROS topic name for the subscriber that updates the modbus registers
00126             :type sub_topic: string
00127             :param pub_topic: ROS topic name for the publisher that publishes a message, once there is something written to the writeable modbus registers
00128             :type pub_topic: string
00129             
00130         """
00131         chr = CustomHoldingRegister(ADDRESS_WRITE_START, [17]*100,sub_topic,pub_topic)
00132         self.store = ModbusSlaveContext(
00133             di = ModbusSequentialDataBlock(ADDRESS_WRITE_START, [17]*100),
00134             co = ModbusSequentialDataBlock(ADDRESS_WRITE_START, [17]*100),
00135             hr = chr, 
00136             ir = ModbusSequentialDataBlock(ADDRESS_WRITE_START, [17]*100))
00137         self.context = ModbusServerContext(slaves=self.store, single=True)
00138 
00139         self.identity = ModbusDeviceIdentification()
00140         self.identity.VendorName  = 'Pymodbus'
00141         self.identity.ProductCode = 'PM'
00142         self.identity.VendorUrl   = 'http://github.com/bashwork/pymodbus/'
00143         self.identity.ProductName = 'Pymodbus Server'
00144         self.identity.ModelName   = 'Pymodbus Server'
00145         self.identity.MajorMinorRevision = '1.0'
00146 
00147         self.store.setValues(2,0,[0]*1)
00148         self.post = Post(self)
00149         framer = ModbusSocketFramer
00150         self.server = ModbusTcpServer(self.context, framer, self.identity, address=("0.0.0.0", port))
00151         
00152     def startServer(self):
00153         """
00154             Non blocking call to start the server
00155         """
00156         self.post.__startServer()
00157         rospy.loginfo("Modbus server started")
00158         
00159     def __startServer(self):
00160         self.server.serve_forever()
00161         
00162     def stopServer(self):
00163         """
00164             Closes the server
00165         """
00166         self.server.server_close()
00167         self.server.shutdown()
00168         
00169     def waitForCoilOutput(self,address,timeout=2):
00170         """
00171             Blocks for the timeout in seconds (or forever) until the specified address becomes true. Adapt this to your needs
00172             :param address: Address of the register that wanted to be read.
00173             :type address: int
00174             :param timeout: The time in seconds until the function should return latest.
00175             :type: float/int
00176         """
00177         now = time.time()
00178         while True:
00179             values = self.store.getValues(1,address, 1)
00180             if values[0] is True:
00181                 return True
00182             else:
00183                 if timeout <=0 or now + timeout > time.time():
00184                     time.sleep(1/50)
00185                 else:
00186                     return False
00187     
00188     def setDigitalInput(self,address,values):
00189         """
00190             Writes to the digital input of the modbus server
00191             :param address: Starting address of the values to write
00192             :type: int
00193             :param values: List of values to write
00194             :type list/boolean/int
00195         """
00196         if not values is list:
00197             values = [values]
00198         self.store.setValues(2,address,values)
00199         
00200         


modbus
Author(s): Sven Bock
autogenerated on Thu Jun 6 2019 17:39:35