insight_modbus_client.py
Go to the documentation of this file.
00001 
00002 ########################################################################### 
00003 # This software is graciously provided by HumaRobotics 
00004 # under the Simplified BSD License on
00005 # github: git@www.humarobotics.com:baxter_tasker
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 # Redistribution and use in source and binary forms, with or without 
00014 # modification, are permitted provided that the following conditions are met:
00015 # 
00016 # 1. Redistributions of source code must retain the above copyright notice,
00017 #  this list of conditions and the following disclaimer.
00018 # 
00019 # 2. Redistributions in binary form must reproduce the above copyright notice,
00020 #  this list of conditions and the following disclaimer in the documentation 
00021 #  and/or other materials provided with the distribution.
00022 # 
00023 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00024 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
00025 # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
00026 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
00027 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
00028 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
00029 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
00030 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
00031 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
00032 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
00033 # THE POSSIBILITY OF SUCH DAMAGE. 
00034 # 
00035 # The views and conclusions contained in the software and documentation are 
00036 # those of the authors and should not be interpreted as representing official 
00037 # policies, either expressed or implied, of the FreeBSD Project.
00038 
00039 import rospy
00040 
00041 from modbus.modbus_wrapper_client import ModbusWrapperClient
00042 class InsightModbusClient(ModbusWrapperClient):
00043     """
00044         Wrapper that integrates python modbus into standardized ros msgs.
00045         The wrapper is able to read from and write to a modbus server running on a Cognex Insight 7200.
00046         The corresponding Insight Explorer project should also be contained in this ros package
00047     """
00048     def __init__(self,host,sim=True,rate=50):
00049         """
00050             :param host: Contains the IP adress of the modbus server
00051             :type host: string
00052             :param rate: How often the registers on the modbusserver should be read per second
00053             :type rate: float
00054             :param reset_registers: Defines if the holding registers should be reset to 0 after they have been read. Only possible if they are writeable
00055             :type reset_registers: bool
00056         """
00057         print("Use the appropriate Cognex Insight Project to enable the Modbus Server")
00058         self.sim = sim
00059         ModbusWrapperClient.__init__(self,host,502,rate,reset_registers=False)
00060         # All registers
00061         self.write_start = 50000
00062         self.setReadingRegisters(30010,30127-30010)
00063         # Use only a few registers for writing
00064         self.setWritingRegisters(self.write_start,2)
00065 
00066         # Note: Listener not started as reading is requested manually, but a heartbeat is required to keep the modbus server alive
00067         self.startHeartbeat()
00068         
00069 
00070     def startVisualInspection(self,job_id,timeout=5.0):
00071         """
00072             Sends a modbus request to change the part detection. job_id 0 is reserved for the heartbeat
00073             :param job_id: part id in the insight explorer that should be detected
00074             :type job_id: int
00075             :param timeout: Duration how long this method should wait for a positive response from the part inspection. The default value should be sufficient.
00076             :type timeout: float
00077         """
00078         rospy.loginfo("Setting new cognex job id: %d", job_id) 
00079         self.setOutput(self.write_start,[job_id])            
00080         self.changeTrigger()
00081         rospy.sleep(0.5)            
00082         start_time = rospy.get_time()
00083         return self.__getResponse(job_id,start_time,timeout)
00084     
00085     def __getResponse(self,id,start_time,timeout):
00086         """
00087             Reads the registers from the camera in the correct order:
00088             is alive
00089             job_id
00090             job_pass
00091             job_fail
00092             barcode
00093             
00094             :param job_id: The job from which we expect an answer
00095             :type job_id: int
00096             :param start_time: Contains the time when the request was sent
00097             :type start_time: float
00098             :param timeout: Duration until the latest response of the inspection
00099             :type timeout: float
00100         """
00101         if start_time + timeout < rospy.get_time():
00102             rospy.loginfo("Timeout in getting response from Insight")
00103             return False
00104         tmp = self.readRegisters()
00105         
00106         if tmp is None:
00107             rospy.logwarn("Nothing received from modbus server")
00108             return False
00109         is_alive = tmp[0]
00110         job_id = tmp[1]
00111         job_pass = tmp[2]
00112         job_fail = tmp[3]
00113         string_length = 25
00114         my_string = self.getString(30014,string_length)
00115         barcode = self.getString(30014+string_length,string_length)[:-1]
00116         response = [is_alive, job_id,job_pass,job_fail,my_string,barcode]
00117 #         print "Response for debugging:",response
00118         return self.__evaluateResponse(id,response,start_time,timeout)
00119 
00120     def __evaluateResponse(self,job_id,response,start_time,timeout):
00121         """
00122             Evaluates the response
00123             
00124             :param job_id: The job from which we expect an answer
00125             :type job_id: int
00126             :param response: Contains the response from the modbus registers [is_alive,job_id,job_pass,job_fail,barcode]
00127             :type response: list
00128             :param start_time: Contains the time when the request was sent
00129             :type start_time: float
00130             :param timeout: Duration until the latest response of the inspection
00131             :type timeout: float
00132             :return: Depending on the job if it has passed or failed
00133             :rtype: bool
00134         """
00135         if not response[0]:
00136             return False
00137         elif job_id == 0: # job_id 0 is the is_alive req    
00138             return True
00139         if job_id == response[1]:            
00140             if job_id == 3:
00141                 my_string = response[4]
00142                 if len(my_string) > 0:
00143                     rospy.loginfo(response[4])
00144                     return True
00145             elif job_id == 4: # job id 1 is used for the barcode in the example
00146                 my_string = response[5]
00147                 if len(my_string) > 0:
00148                     rospy.loginfo(response[5])
00149                     return True
00150             elif response[2]:
00151                 return True
00152             # False is returned if the timeout is triggered and not by the fail, that is passed by the camera
00153         rospy.sleep(0.1)
00154         return self.__getResponse(job_id,start_time,timeout)
00155 
00156     def __decodeString(self,ascii):
00157         """    
00158             Converts an ascii number to a string
00159             :param ascii: Integer code of a string encoded in ascii
00160             :type ascii: int
00161             :return: Returns two characters as string
00162             :rtype: string
00163         """
00164         second = ascii%256
00165         first = (ascii-second)/256
00166         return str(chr(first))+str(chr(second)) 
00167                 
00168     def getString(self,start,num_registers):
00169         """
00170             Reads num_registers from start and tries to recover the string that has been send by the modbus server
00171             :param start: Starting address of the modbus register to read the string
00172             :type: int
00173             :param:number of registers to read
00174             :type: int
00175             :return: Returns a string
00176             :rtype: string
00177         """
00178         msg = ""
00179         tmp = self.readRegisters(start,num_registers)
00180         for ascii in tmp:
00181             if ascii == 0:
00182                 continue
00183             msg += self.__decodeString(ascii)
00184         return msg
00185     
00186     def changeTrigger(self):
00187         """
00188             Changes the trigger on the camera to manual and back to continous. This gives the camera some time to update its parameters
00189         """
00190         def reg(letter,number):
00191             """
00192                 Returns an integer for a cell value
00193                 
00194                 Usable triggers on the insight explorer software
00195                 * 0: Camera
00196                 * 1: Continous
00197                 * 2: External
00198                 * 3: Manual
00199                 * 4: Network
00200                 
00201             """
00202             v=ord(letter.lower())-ord('a')
00203             v=v<<10
00204             v+=number
00205             return v
00206         v = reg("A",3)
00207         rospy.sleep(0.1)
00208         self.setOutput(v, [3])
00209         rospy.sleep(0.6)
00210         if self.sim:
00211             self.setOutput(v, [4])
00212         else:
00213             self.setOutput(v, [1])
00214         
00215         
00216     
00217     def startHeartbeat(self):
00218         """
00219             Non blocking call to start the heartbeat loop
00220         """
00221         self.post.__sendHeartbeat()
00222             
00223     
00224     def __sendHeartbeat(self):
00225         """
00226             Sends an is alive signal to the cognex camera. If this is not send, the camera times out 1 hour after the last message in the best case scenario
00227         """
00228         
00229         while not rospy.is_shutdown():
00230             rospy.sleep(5)
00231             self.setOutput(self.write_start+1,0)
00232     
00233 
00234 
00235     


modbus_cognex_insight
Author(s): Sven Bock
autogenerated on Thu Jun 6 2019 17:39:37