$search
00001 #!/usr/bin/env python 00002 # Software License Agreement (BSD License) 00003 # 00004 # Copyright (c) 2008, Willow Garage, Inc. 00005 # All rights reserved. 00006 # 00007 # Redistribution and use in source and binary forms, with or without 00008 # modification, are permitted provided that the following conditions 00009 # are met: 00010 # 00011 # * Redistributions of source code must retain the above copyright 00012 # notice, this list of conditions and the following disclaimer. 00013 # * Redistributions in binary form must reproduce the above 00014 # copyright notice, this list of conditions and the following 00015 # disclaimer in the documentation and/or other materials provided 00016 # with the distribution. 00017 # * Neither the name of Willow Garage, Inc. nor the names of its 00018 # contributors may be used to endorse or promote products derived 00019 # from this software without specific prior written permission. 00020 # 00021 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00022 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00023 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00024 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00025 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00026 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00027 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00028 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00029 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00030 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00031 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00032 # POSSIBILITY OF SUCH DAMAGE. 00033 00034 from __future__ import with_statement 00035 import roslib; roslib.load_manifest('pr2_camera_synchronizer') 00036 from dynamic_reconfigure.client import Client as DynamicReconfigureClient 00037 from dynamic_reconfigure.server import Server as DynamicReconfigureServer 00038 from ethercat_trigger_controllers.srv import SetMultiWaveform as SetMultiWaveform 00039 from ethercat_trigger_controllers.srv import SetMultiWaveformRequest as SetMultiWaveformRequest 00040 from ethercat_trigger_controllers.msg import MultiWaveform as MultiWaveform 00041 from ethercat_trigger_controllers.msg import MultiWaveformTransition as MultiWaveformTransition 00042 import pr2_camera_synchronizer.cfg.CameraSynchronizerConfig as Config 00043 import wge100_camera.cfg.WGE100CameraConfig as WGEConfig 00044 00045 import rospy 00046 import time 00047 import math 00048 import threading 00049 import signal 00050 00051 from pr2_camera_synchronizer.cfg import CameraSynchronizerConfig as ConfigType 00052 from pr2_camera_synchronizer.levels import * 00053 from diagnostic_msgs.msg import DiagnosticStatus, KeyValue, DiagnosticArray 00054 00055 ETHERCAT_INTERVAL = 0.001 00056 00057 param_rate = "rate" 00058 param_trig_mode = "trig_mode" 00059 camera_parameters = [ param_rate, param_trig_mode ] 00060 00061 param_proj_rate = "projector_rate" 00062 00063 def roundToEthercat(val): 00064 return ETHERCAT_INTERVAL * round(val / ETHERCAT_INTERVAL) 00065 00066 asynchronous_updaters = [] 00067 def killAsynchronousUpdaters(): 00068 for updater in asynchronous_updaters: 00069 updater.kill() 00070 class AsynchronousUpdater(threading.Thread): 00071 def __init__(self, f, name): 00072 threading.Thread.__init__(self) 00073 self.name = name 00074 self.f = f 00075 self.allargs = None 00076 self.cv = threading.Condition() 00077 asynchronous_updaters.append(self) 00078 self.exiting = False 00079 self.idle = True 00080 self.start() 00081 00082 def update(self, *args, **nargs): 00083 with self.cv: 00084 self.allargs = (args, nargs) 00085 self.cv.notify() 00086 #print "update", self.allargs 00087 00088 def run(self): 00089 #print "run" 00090 while True: 00091 #print "Starting loop on", self.name 00092 with self.cv: 00093 if self.exiting: 00094 break 00095 if self.allargs == None: 00096 #print "start wait" 00097 self.idle = True 00098 self.cv.wait() 00099 self.idle_end_time = rospy.get_time() 00100 self.idle = False 00101 #print "end wait" 00102 allargs = self.allargs 00103 self.allargs = None 00104 if allargs != None: 00105 try: 00106 self.f(*allargs[0], **allargs[1]) 00107 except Exception, e: 00108 rospy.logerr("AsynchronousUpdater failed with exception: %s"%str(e)) 00109 pass 00110 00111 def kill(self): 00112 #print "kill" 00113 #print "Killing", self.name 00114 with self.cv: 00115 self.exiting = True 00116 self.allargs = None 00117 self.cv.notify() 00118 00119 def getStatus(self): # For diagnostics 00120 if self.idle: 00121 return self.name, 0 00122 interval = rospy.get_time() - self.idle_end_time 00123 return self.name, interval 00124 00125 class MultiTriggerController: 00126 def __init__(self, name): 00127 self.period = 0 00128 self.zero_offset = 0 00129 self.clear_waveform() 00130 self.name = name 00131 self.async = AsynchronousUpdater(self.async_update, "Controller "+name) 00132 self.service = None 00133 self.transitions = [] 00134 00135 def clear_waveform(self): 00136 self.transitions = [] 00137 00138 def add_sample(self, time, value, topic): 00139 time = roundToEthercat(time) + 0.5 * ETHERCAT_INTERVAL 00140 self.transitions.append(MultiWaveformTransition(time, value, topic)) 00141 00142 def async_update(self, period, zero_offset, transitions): 00143 try: 00144 if self.service == None: 00145 service_name = self.name+"/set_waveform" 00146 rospy.wait_for_service(service_name) 00147 self.service = rospy.ServiceProxy(service_name, SetMultiWaveform) 00148 #print "Service", service_name, "exists." 00149 00150 #print "Trigger async_update got proxy on", self.name 00151 waveform = MultiWaveform(period, zero_offset, transitions) 00152 #print "Updating waveform ", self.name, waveform 00153 rslt = self.service(waveform) 00154 if not rslt.success: 00155 rospy.logerr("Error setting waveform %s: %s"%(self.name, rslt.status_message)) 00156 #print "Done updating waveform ", self.name 00157 except KeyboardInterrupt: # Handle CTRL+C 00158 print "Aborted trigger update on", self.name 00159 00160 def update(self): 00161 # Run the update using an Asynchronous Updater so that if something 00162 # locks up, the rest of the node can keep working. 00163 #print "Trigger update on", self.name 00164 self.async.update(self.period, self.zero_offset, self.transitions) 00165 00166 class ProsilicaInhibitTriggerController(MultiTriggerController): 00167 def __init__(self, name, param, true_val, false_val): 00168 MultiTriggerController.__init__(self, name) 00169 self.param = param 00170 self.true_val = true_val 00171 self.false_val = false_val 00172 00173 def process_update(self, config, level): 00174 self.period = 1 00175 self.zero_offset = 0 00176 self.clear_waveform() 00177 self.add_sample(0, { True: self.true_val, False: self.false_val}[config[self.param]], '-') 00178 MultiTriggerController.update(self) 00179 00180 class ProjectorTriggerController(MultiTriggerController): 00181 def __init__(self, name, proj): 00182 MultiTriggerController.__init__(self, name) 00183 self.proj = proj 00184 00185 def update(self): 00186 self.period = self.proj.repeat_period 00187 self.zero_offset = self.proj.zero_offset 00188 self.clear_waveform() 00189 self.high_val = 0xf 00190 if (self.proj.needed == False and self.proj.mode == Config.CameraSynchronizer_ProjectorAuto) or \ 00191 self.proj.mode == Config.CameraSynchronizer_ProjectorOff: 00192 self.high_val = 0xe 00193 for i in range(0, len(self.proj.pulse_starts)): 00194 self.add_sample(self.proj.pulse_starts[i], self.high_val, 'on_time') 00195 self.add_sample(self.proj.pulse_ends[i], 0xe, 'off_time') 00196 MultiTriggerController.update(self) 00197 00198 class SingleCameraTriggerController(MultiTriggerController): 00199 def __init__(self, name, camera): 00200 MultiTriggerController.__init__(self, name) 00201 self.camera = camera 00202 00203 def update(self): 00204 if self.camera.reset_cameras: 00205 self.camera_reset() 00206 return 00207 self.clear_waveform() 00208 if not self.camera.ext_trig: 00209 self.period = 1 00210 self.add_sample(0, 0, "-") 00211 else: 00212 self.period = 2 * self.camera.period 00213 self.zero_offset = -self.camera.imager_period 00214 first_pulse_start = self.camera.end_offset 00215 second_pulse_start = self.camera.period + first_pulse_start 00216 first_frame_end = first_pulse_start + self.camera.imager_period 00217 extra_pulse_start = (first_pulse_start + first_frame_end) / 2 00218 trigger_name = "trigger" 00219 self.add_sample(first_pulse_start, 1, trigger_name) 00220 self.add_sample((first_pulse_start + extra_pulse_start) / 2, 0, "-") 00221 self.add_sample(extra_pulse_start, 1, "-") 00222 self.add_sample((extra_pulse_start + first_frame_end) / 2, 0, "-") 00223 self.add_sample(second_pulse_start, 1, trigger_name) 00224 self.add_sample((second_pulse_start + self.period) / 2, 0, "-") 00225 self.camera.trig_rising = True 00226 self.camera.trigger_name = self.name+"/"+trigger_name 00227 00228 #print "About to update trigger", self.name 00229 MultiTriggerController.update(self) 00230 00231 def camera_reset(self): 00232 self.clear_waveform() 00233 self.period = 1.5 00234 self.add_sample(0, 0, "-") 00235 self.add_sample(0.1, 1, "-") 00236 MultiTriggerController.update(self) 00237 00238 class DualCameraTriggerController(SingleCameraTriggerController): 00239 def __init__(self, name, camera1, camera2): 00240 SingleCameraTriggerController.__init__(self, name, None) 00241 self.cameras = [camera1, camera2] 00242 00243 def update(self): 00244 if self.cameras[0].reset_cameras or self.cameras[1].reset_cameras: 00245 self.camera_reset() 00246 return 00247 00248 if self.cameras[0].period != self.cameras[1].period or \ 00249 self.cameras[0].imager_period != self.cameras[1].imager_period: 00250 rospy.logerr("Cameras on same trigger have different period/imager_period settings.") 00251 00252 self.clear_waveform() 00253 if self.cameras[0].end_offset == self.cameras[1].end_offset: 00254 # This works because all cameras have the same imager period. 00255 self.camera = self.cameras[0] 00256 SingleCameraTriggerController.update(self) 00257 self.cameras[1].trigger_name = self.cameras[0].trigger_name 00258 self.cameras[1].trig_rising = self.cameras[0].trig_rising 00259 return 00260 00261 if self.cameras[0].end_offset > self.cameras[1].end_offset: 00262 # Reduction to the case where cameras[0] happens first. 00263 self.cameras.reverse() 00264 00265 # Now we are sure that camera 1 happens strictly before camera 2. 00266 assert self.cameras[0].proj == self.cameras[1].proj 00267 assert self.cameras[0].period == self.cameras[1].period # Will fail if both stereo can be alternate or if they are updated in the wrong order. 00268 00269 self.cameras[0].trig_rising = True 00270 self.cameras[1].trig_rising = False 00271 00272 self.clear_waveform() 00273 self.period = 2 * self.cameras[1].period 00274 self.zero_offset = -self.cameras[0].imager_period 00275 00276 first_rise = self.cameras[0].end_offset 00277 first_fall = self.cameras[1].end_offset 00278 first_rise_end = first_rise + self.cameras[0].imager_period 00279 second_rise = self.cameras[0].end_offset + self.cameras[0].period 00280 second_fall = self.cameras[1].end_offset + self.cameras[1].period 00281 extra_rise = (2 * first_fall + first_rise_end) / 3 00282 extra_fall = (first_fall + 2 * first_rise_end) / 3 00283 00284 trigger_name = [ "trigger_"+camera.name for camera in self.cameras ] 00285 self.add_sample(first_rise, 1, trigger_name[0]) 00286 self.add_sample(first_fall, 0, trigger_name[1]) 00287 self.add_sample(extra_rise, 1, "-") 00288 self.add_sample(extra_fall, 0, "-") 00289 self.add_sample(second_rise, 1, trigger_name[0]) 00290 self.add_sample(second_fall, 0, trigger_name[1]) 00291 for i in range(0,2): 00292 self.cameras[i].trigger_name = self.name+"/"+trigger_name[i] 00293 #print self.cameras[i].trigger_name 00294 00295 #print "About to update trigger", self.name 00296 MultiTriggerController.update(self) 00297 00298 class Projector: 00299 def process_update(self, config, level): 00300 # Produces: 00301 # MEMBERS 00302 # proj_end_offset, alt_end_offset, noproj_end_offset 00303 # pulse_starts, pulse_ends 00304 # repeat_period 00305 # 00306 # PARAMETERS 00307 # projector_rate and projector_pulse_length 00308 #print "Setting needed to false." 00309 self.needed = False # If a camera needs us, the camera will say so. 00310 00311 if level & lvl_projector == 0: 00312 return; # The projector's configuration is unchanged. 00313 00314 base_period = 1.0 / config["projector_rate"] 00315 self.pulse_length = config["projector_pulse_length"] 00316 pulse_shift = config["projector_pulse_shift"] 00317 self.zero_offset = roundToEthercat(config["projector_tweak"]) 00318 self.mode = config["projector_mode"] 00319 00320 base_period = roundToEthercat(base_period) 00321 # Minimum is guard, pulse, guard, offset_pulse 00322 base_period = max(base_period, 4 * ETHERCAT_INTERVAL) 00323 00324 self.repeat_period = 4.0 * base_period 00325 00326 # Pulse can't be so long that it would coalesce with next one. 00327 self.pulse_length = min(self.pulse_length, math.floor(base_period / ETHERCAT_INTERVAL / 2.0 - 1) * ETHERCAT_INTERVAL) 00328 self.pulse_length = math.ceil(self.pulse_length / ETHERCAT_INTERVAL) * ETHERCAT_INTERVAL 00329 00330 min_second_start = base_period 00331 max_second_start = 2 * base_period - 2 * ETHERCAT_INTERVAL - 2 * self.pulse_length 00332 second_start = min_second_start * (1 - pulse_shift) + max_second_start * pulse_shift 00333 second_start = roundToEthercat(second_start) 00334 config["projector_pulse_shift"] = (second_start - min_second_start) / (max_second_start - min_second_start) 00335 00336 self.pulse_starts = [ 00337 0, 00338 second_start, 00339 2 * base_period, 00340 2 * base_period + second_start + self.pulse_length + 2 * ETHERCAT_INTERVAL 00341 ] 00342 self.pulse_ends = [ start + self.pulse_length for start in self.pulse_starts ] 00343 00344 self.proj_end_offset = self.pulse_length + ETHERCAT_INTERVAL 00345 self.alt_end_offset = second_start + self.pulse_length + ETHERCAT_INTERVAL 00346 self.noproj_end_offset = second_start - ETHERCAT_INTERVAL 00347 00348 self.proj_exposure_duration = self.pulse_length + ETHERCAT_INTERVAL * 1.5 00349 self.noproj_max_exposure = second_start - self.pulse_length - ETHERCAT_INTERVAL * 1.5 00350 00351 config["projector_rate"] = 1 / base_period 00352 config["projector_pulse_length"] = self.pulse_length 00353 config["projector_tweak"] = self.zero_offset 00354 00355 class Camera: 00356 def __init__(self, node_name, proj, level, **paramnames): 00357 self.paramnames = paramnames 00358 self.name = node_name 00359 self.level = level 00360 self.proj = proj 00361 self.reconfigure_client = None 00362 self.async = AsynchronousUpdater(self.async_apply_update, "Camera "+node_name) 00363 self.trig_rising = True 00364 00365 def param(self, config, name): 00366 return config[self.paramnames[name]] 00367 00368 def setparam(self, config, name, value): 00369 config[self.paramnames[name]] = value 00370 00371 # Uses the new configuration to compute new camera state. 00372 def process_update(self, config, level): 00373 # Produces: 00374 # MEMBERS 00375 # period, ext_trig, imager_period, end_offset 00376 # 00377 # PARAMETERS 00378 # "param_rate" 00379 self.trigger_name = "not_set" # Will be set by the CameraTriggerController 00380 00381 # Took level checking out as it was causing problems with the projector 00382 # needed flag. 00383 #if self.level & level == 0: 00384 # return # This camera's configuration is unchanged. 00385 00386 self.period = 1.0 / self.param(config, param_rate) 00387 projector_rate = config[param_proj_rate] 00388 self.reset_cameras = config["camera_reset"] 00389 self.ext_trig = True 00390 self.register_set = WGEConfig.WGE100Camera_PrimaryRegisterSet 00391 00392 projector_limits_exposure = True 00393 00394 trig_mode = self.param(config, param_trig_mode) 00395 # Internal triggering. 00396 if trig_mode == Config.CameraSynchronizer_InternalTrigger: 00397 self.ext_trig = False 00398 self.imager_period = self.period 00399 self.end_offset = -1 00400 projector_limits_exposure = False 00401 else: 00402 if self.proj.mode == Config.CameraSynchronizer_ProjectorOff: 00403 trig_mode = Config.CameraSynchronizer_IgnoreProjector 00404 00405 if trig_mode == Config.CameraSynchronizer_AlternateProjector or trig_mode == Config.CameraSynchronizer_WithProjector: 00406 self.proj.needed = True 00407 #print self.name, "setting needed to true." 00408 #else: 00409 #print self.name, "not setting needed to true." 00410 00411 # Set the period 00412 if trig_mode == Config.CameraSynchronizer_AlternateProjector: 00413 n = round(self.period / self.proj.repeat_period - 0.5) 00414 n = max(n, 0) 00415 self.period = (n + 0.5) * self.proj.repeat_period 00416 #print "Case 1", n 00417 elif trig_mode == Config.CameraSynchronizer_IgnoreProjector: 00418 self.period = roundToEthercat(self.period) 00419 else: 00420 n = round(2 * self.period / self.proj.repeat_period - 1) 00421 n = max(n, 0) 00422 self.period = (n + 1) * self.proj.repeat_period / 2.0 00423 #print "Case 2", n, self.period 00424 00425 # Set the end_offset 00426 if trig_mode == Config.CameraSynchronizer_IgnoreProjector: 00427 self.end_offset = 0 00428 projector_limits_exposure = False 00429 if trig_mode == Config.CameraSynchronizer_AlternateProjector: 00430 self.end_offset = self.proj.alt_end_offset 00431 self.register_set = WGEConfig.WGE100Camera_Auto 00432 elif trig_mode == Config.CameraSynchronizer_WithProjector: 00433 self.end_offset = self.proj.proj_end_offset 00434 self.register_set = WGEConfig.WGE100Camera_AlternateRegisterSet 00435 else: 00436 self.end_offset = self.proj.noproj_end_offset 00437 00438 # Pick the imager period 00439 if trig_mode == Config.CameraSynchronizer_IgnoreProjector: 00440 self.imager_period = self.period - ETHERCAT_INTERVAL 00441 else: 00442 self.imager_period = self.proj.repeat_period / 2 - ETHERCAT_INTERVAL 00443 00444 #print "Camera period", self.name, self.period, self.imager_period, self.proj.repeat_period 00445 if projector_limits_exposure: 00446 self.max_exposure = self.proj.noproj_max_exposure 00447 #print "Exposurei projector", self.max_exposure 00448 else: 00449 self.max_exposure = self.imager_period * 0.95 00450 #print "Exposurei imager", self.max_exposure 00451 00452 self.setparam(config, param_rate, 1/self.period) 00453 self.setparam(config, param_trig_mode, trig_mode) 00454 00455 def async_apply_update(self, reconfig_request): 00456 try: 00457 #print "**** Start", self.name 00458 if self.reconfigure_client == None: 00459 #print "**** Making client", self.name 00460 self.reconfigure_client = DynamicReconfigureClient(self.name) 00461 #print "**** Made client", self.name 00462 00463 self.reconfigure_client.update_configuration(reconfig_request) 00464 #print "**** Reconfigured client", self.name 00465 #print "Done updating camera ", self.name 00466 except KeyboardInterrupt: # Handle CTRL+C 00467 print "Aborted camera update on", self.name 00468 00469 def apply_update(self): 00470 reconfig_request = { 00471 "ext_trig" : self.ext_trig, 00472 "trig_rate" : 1.0 / self.period, 00473 "imager_rate" : 1.0 / self.imager_period, 00474 "trig_timestamp_topic" : self.trigger_name, 00475 "rising_edge_trig" : self.trig_rising, 00476 "register_set" : self.register_set, 00477 "auto_exposure_alternate" : False, 00478 "exposure_alternate" : self.proj.proj_exposure_duration, 00479 "max_exposure" : self.max_exposure, 00480 } 00481 #print self.name, reconfig_request 00482 00483 self.async.update(reconfig_request) 00484 00485 # Need to set: 00486 # Global period if synchronized 00487 # Camera 00488 # Frame rate 00489 # Trigger mode 00490 # Exposure alt 00491 # Maximum exposure alt 00492 # Alternate enabled 00493 # Alternate only 00494 # Trigger name 00495 # Trigger 00496 # Full waveform 00497 # Projector 00498 # Full waveform 00499 # Prosilica setting 00500 00501 class CameraSynchronizer: 00502 def __init__(self, forearm=True): 00503 stereo_camera_names = [ "narrow_stereo", "wide_stereo" ] # narrow must be first as it can be alternate, and hence has more period restrictions. 00504 forearm_camera_names = [ "forearm_r", "forearm_l" ] 00505 self.camera_names = stereo_camera_names 00506 if forearm: 00507 self.camera_names = self.camera_names + forearm_camera_names 00508 # Parameter names are pretty symmetric. Build them up automatically. 00509 parameters = [ param_rate, param_trig_mode ] 00510 camera_parameters = dict((name, dict((suffix, name+"_"+suffix) for suffix in parameters)) for name in self.camera_names) 00511 # Some parameters are common to the wide and narrow cameras. Also store 00512 # the node names. 00513 for camera in stereo_camera_names: 00514 camera_parameters[camera][param_rate] = "stereo_rate" 00515 camera_parameters[camera]["node_name"] = camera+"_both" 00516 if forearm: 00517 for camera in forearm_camera_names: 00518 camera_parameters[camera]["node_name"] = camera[-1]+"_forearm_cam" 00519 for i in range(0, len(self.camera_names)): 00520 camera_parameters[self.camera_names[i]]["level"] = 1 << i; # This only works because of the specific levels in the .cfg file. 00521 00522 self.projector = Projector() 00523 self.cameras = dict((name, Camera(proj = self.projector, **camera_parameters[name])) for name in self.camera_names) 00524 self.prosilica_inhibit = ProsilicaInhibitTriggerController('prosilica_inhibit_projector_controller', "prosilica_projector_inhibit", 0x0A, 0x00) 00525 00526 self.controllers = [ 00527 ProjectorTriggerController('projector_trigger', self.projector), 00528 DualCameraTriggerController('head_camera_trigger', *[self.cameras[name] for name in stereo_camera_names])] 00529 if forearm: 00530 self.controllers = self.controllers + [ 00531 SingleCameraTriggerController('r_forearm_cam_trigger', self.cameras["forearm_r"]), 00532 SingleCameraTriggerController('l_forearm_cam_trigger', self.cameras["forearm_l"]), 00533 ] 00534 00535 self.server = DynamicReconfigureServer(ConfigType, self.reconfigure) 00536 00537 @staticmethod 00538 def print_threads(): 00539 threads = threading.enumerate() 00540 n = 0 00541 for t in threads: 00542 if not t.isDaemon() and t != threading.currentThread(): 00543 try: 00544 print t.name 00545 except: 00546 print "Unknown thread ", t 00547 n = n + 1 00548 return n 00549 00550 def kill(self): 00551 print "\nWaiting for all threads to die..." 00552 killAsynchronousUpdaters() 00553 #time.sleep(1) 00554 while self.print_threads() > 0: 00555 print "\nStill waiting for all threads to die..." 00556 time.sleep(1) 00557 print 00558 00559 def reconfigure(self, config, level): 00560 # print "Reconfigure", config 00561 # Reconfigure the projector. 00562 self.projector.process_update(config, level) 00563 self.prosilica_inhibit.process_update(config, level) 00564 # Reconfigure the cameras. 00565 for camera in self.cameras.values(): 00566 camera.process_update(config, level) 00567 #for trig_controller in self.trig_controllers: 00568 # trig_controller.update() 00569 #for camera in self.cameras.keys(): 00570 # camera.update() 00571 for controller in self.controllers: 00572 controller.update(); 00573 for camera in self.cameras.values(): 00574 camera.apply_update() 00575 #print config 00576 self.config = config 00577 return config 00578 00579 def update_diagnostics(self): 00580 da = DiagnosticArray() 00581 ds = DiagnosticStatus() 00582 ds.name = rospy.get_caller_id().lstrip('/') + ": Tasks" 00583 in_progress = 0; 00584 longest_interval = 0; 00585 for updater in list(asynchronous_updaters): 00586 (name, interval) = updater.getStatus() 00587 if interval == 0: 00588 msg = "Idle" 00589 else: 00590 in_progress = in_progress + 1 00591 msg = "Update in progress (%i s)"%interval 00592 longest_interval = max(interval, longest_interval) 00593 ds.values.append(KeyValue(name, msg)) 00594 if in_progress == 0: 00595 ds.message = "Idle" 00596 else: 00597 ds.message = "Updates in progress: %i"%in_progress 00598 if longest_interval > 10: 00599 ds.level = 1 00600 ds.message = "Update is taking too long: %i"%in_progress 00601 ds.hardware_id = "none" 00602 da.status.append(ds) 00603 da.header.stamp = rospy.get_rostime() 00604 self.diagnostic_pub.publish(da) 00605 00606 def spin(self): 00607 self.diagnostic_pub = rospy.Publisher("/diagnostics", DiagnosticArray) 00608 try: 00609 reset_count = 0 00610 rospy.loginfo("Camera synchronizer is running...") 00611 controller_update_count = 0 00612 while not rospy.is_shutdown(): 00613 if self.config['camera_reset'] == True: 00614 reset_count = reset_count + 1 00615 if reset_count > 3: 00616 self.server.update_configuration({'camera_reset' : False}) 00617 else: 00618 reset_count = 0 00619 self.update_diagnostics() 00620 # In case the controllers got restarted, refresh their state. 00621 controller_update_count += 1 00622 if controller_update_count >= 10: 00623 controller_update_count = 0 00624 for controller in self.controllers: 00625 controller.update(); 00626 rospy.sleep(1) 00627 finally: 00628 rospy.signal_shutdown("Main thread exiting") 00629 self.kill() 00630 print "Main thread exiting"