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 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
00061 self.write_start = 50000
00062 self.setReadingRegisters(30010,30127-30010)
00063
00064 self.setWritingRegisters(self.write_start,2)
00065
00066
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
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:
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:
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
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