class FSScanProcessor(FSScanProcessorInterface): def __init__(self, config, settings, eventmanager, imageprocessor, hardwarecontroller, calibration): super(FSScanProcessorInterface, self).__init__(self, config, settings, eventmanager, imageprocessor, hardwarecontroller, calibration) self.settings = settings self.config = config self._logger = logging.getLogger(__name__) self.eventmanager = eventmanager.instance self.calibration = calibration self.hardwareController = hardwarecontroller self.image_processor = imageprocessor self._prefix = None self._resolution = 16 self._number_of_pictures = 0 self._total = 0 self._laser_positions = 1 self._progress = 0 self._is_color_scan = True self.point_cloud = None self.image_task_q = multiprocessing.Queue(self.config.process_numbers * 2) self.current_position = 0 self._stop_scan = False self._current_laser_position = 1 self._starttime = 0 self.utils = FSSystem() self.semaphore = multiprocessing.BoundedSemaphore() self.event_q = self.eventmanager.get_event_q() self._worker_pool = None self._scan_brightness = self.settings.camera.brightness self._scan_contrast = self.settings.camera.contrast self._scan_saturation = self.settings.camera.saturation self.eventmanager.subscribe(FSEvents.ON_IMAGE_PROCESSED, self.image_processed) self._logger.info("Laser Scan Processor initilized..." + str(self)) def on_receive(self, event): if event[FSEvents.COMMAND] == FSScanProcessorCommand.START: self.start_scan() if event[FSEvents.COMMAND] == FSScanProcessorCommand.STOP: self.stop_scan() if event[FSEvents.COMMAND] == FSScanProcessorCommand.SETTINGS_MODE_ON: self.settings_mode_on() if event[FSEvents.COMMAND] == FSScanProcessorCommand.SETTINGS_MODE_OFF: self.settings_mode_off() if event[ FSEvents. COMMAND] == FSScanProcessorCommand._SCAN_NEXT_TEXTURE_POSITION: self.scan_next_texture_position() if event[FSEvents. COMMAND] == FSScanProcessorCommand._SCAN_NEXT_OBJECT_POSITION: self.scan_next_object_position() if event[FSEvents. COMMAND] == FSScanProcessorCommand.NOTIFY_HARDWARE_STATE: self.send_hardware_state_notification() if event[FSEvents.COMMAND] == FSScanProcessorCommand.UPDATE_SETTINGS: self.update_settings(event['SETTINGS']) if event[FSEvents.COMMAND] == FSScanProcessorCommand.UPDATE_CONFIG: self.update_settings(event['CONFIG']) if event[FSEvents.COMMAND] == FSScanProcessorCommand.GET_HARDWARE_INFO: return self.hardwareController.get_firmware_version() if event[FSEvents. COMMAND] == FSScanProcessorCommand.GET_ADJUSTMENT_STREAM: return self.create_adjustment_stream() if event[FSEvents.COMMAND] == FSScanProcessorCommand.GET_LASER_STREAM: return self.create_laser_stream() if event[ FSEvents.COMMAND] == FSScanProcessorCommand.GET_TEXTURE_STREAM: return self.create_texture_stream() if event[FSEvents. COMMAND] == FSScanProcessorCommand.GET_CALIBRATION_STREAM: return self.create_calibration_stream() if event[FSEvents.COMMAND] == FSScanProcessorCommand.START_CALIBRATION: return self.start_calibration() if event[FSEvents.COMMAND] == FSScanProcessorCommand.STOP_CALIBRATION: return self.stop_calibration() if event[FSEvents. COMMAND] == FSScanProcessorCommand.NOTIFY_IF_NOT_CALIBRATED: return self.notify_if_is_not_calibrated() if event[ FSEvents. COMMAND] == FSScanProcessorCommand.CALL_HARDWARE_TEST_FUNCTION: device = event['DEVICE_TEST'] self.hardwareController.call_test_function(device) def call_hardware_test_function(self, function): self.hardwareController.call_test_function(function) def notify_if_is_not_calibrated(self): self._logger.debug(self.config.calibration.camera_matrix) is_calibrated = not (self.config.calibration.laser_planes[0]['normal'] == []) self._logger.debug("FabScan is calibrated: " + str(is_calibrated)) if not is_calibrated: message = {"message": "SCANNER_NOT_CALIBRATED", "level": "warn"} self.eventmanager.broadcast_client_message( FSEvents.ON_INFO_MESSAGE, message) def create_texture_stream(self): try: image = self.hardwareController.get_picture() #image = self.image_processor.get_texture_stream_frame(image) return image except Exception as e: #self._logger.error(e) pass def create_adjustment_stream(self): try: image = self.hardwareController.get_picture() image = self.image_processor.get_adjustment_stream_frame(image) return image except Exception as e: pass def create_calibration_stream(self): try: image = self.hardwareController.get_picture() image = self.image_processor.get_calibration_stream_frame(image) return image except Exception as e: # images are dropped this cateched exception.. no error hanlder needed here. pass def create_laser_stream(self): try: image = self.hardwareController.get_picture() return image except Exception as e: # images are dropped this cateched exception.. no error hanlder needed here. pass def update_settings(self, settings): try: self.settings.update(settings) #FIXME: Only change Color Settings when values changed. self.hardwareController.led.on(self.settings.led.red, self.settings.led.green, self.settings.led.blue) except Exception as e: # images are dropped this cateched exception.. no error hanlder needed here. pass def update_config(self, config): try: self.config.update(config) except Exception as e: pass def start_calibration(self): self.hardwareController.settings_mode_off() time.sleep(0.5) self.calibration.start() def stop_calibration(self): self.calibration.stop() def send_hardware_state_notification(self): self._logger.debug("Checking Hardware connections") if not self.hardwareController.arduino_is_connected(): message = {"message": "NO_SERIAL_CONNECTION", "level": "error"} self.eventmanager.broadcast_client_message( FSEvents.ON_INFO_MESSAGE, message) if not self.hardwareController.camera_is_connected(): message = {"message": "NO_CAMERA_CONNECTION", "level": "error"} self.eventmanager.broadcast_client_message( FSEvents.ON_INFO_MESSAGE, message) def settings_mode_on(self): self._logger.debug("FSScanProcessor:settings_mode_on") #message = { # "message": "SETTINGS_MODE_ON", # "level": "info" #} #self.eventmanager.broadcast_client_message(FSEvents.ON_INFO_MESSAGE, message) self.hardwareController.settings_mode_on() def settings_mode_off(self): #message = { # "message": "SETTINGS_MODE_OFF", # "level": "info" #} #self.eventmanager.broadcast_client_message(FSEvents.ON_INFO_MESSAGE, message) self.hardwareController.settings_mode_off() def start_scan(self): self.settings_mode_off() self._logger.info("Scan started") self._stop_scan = False if self._worker_pool is None: self._worker_pool = FSImageWorkerPool(self.image_task_q, self.event_q) self.hardwareController.turntable.enable_motors() self.hardwareController.start_camera_stream(mode="default") time.sleep(1.5) self._resolution = int(self.settings.resolution) self._laser_positions = int(self.settings.laser_positions) self._is_color_scan = bool(self.settings.color) self._number_of_pictures = old_div(self.config.turntable.steps, int(self.settings.resolution)) self.current_position = 0 self._starttime = self.get_time_stamp() # TODO: rename prefix to scan_id self._prefix = datetime.fromtimestamp( time.time()).strftime('%Y%m%d-%H%M%S') self.point_cloud = FSPointCloud(color=self._is_color_scan) if not (self.config.calibration.laser_planes[0]['normal'] == []) and self.actor_ref.is_alive(): if self._is_color_scan: self._total = self._number_of_pictures * 2 * self.config.laser.numbers self.actor_ref.tell({ FSEvents.COMMAND: FSScanProcessorCommand._SCAN_NEXT_TEXTURE_POSITION }) else: self._total = self._number_of_pictures * self.config.laser.numbers self.actor_ref.tell({ FSEvents.COMMAND: FSScanProcessorCommand._SCAN_NEXT_OBJECT_POSITION }) else: self._logger.debug("FabScan is not calibrated scan canceled") message = {"message": "SCANNER_NOT_CALIBRATED", "level": "warn"} self.eventmanager.broadcast_client_message( FSEvents.ON_INFO_MESSAGE, message) event = FSEvent() event.command = 'STOP' self.eventmanager.publish(FSEvents.COMMAND, event) def init_texture_scan(self): message = {"message": "SCANNING_TEXTURE", "level": "info"} self.eventmanager.broadcast_client_message(FSEvents.ON_INFO_MESSAGE, message) self._worker_pool.create(self.config.process_numbers) self._scan_brightness = self.settings.camera.brightness self._scan_contrast = self.settings.camera.contrast self._scan_saturation = self.settings.camera.saturation #self.settings.camera.brightness = 50 #self.settings.camera.contrast = 0 #self.settings.camera.saturation = 0 self.hardwareController.led.on(self.config.texture_illumination, self.config.texture_illumination, self.config.texture_illumination) self.hardwareController.camera.device.flush_stream() def finish_texture_scan(self): self._logger.info("Finishing texture scan.") self.current_position = 0 self.hardwareController.camera.device.flush_stream() self.hardwareController.led.off() self.settings.camera.brightness = self._scan_brightness self.settings.camera.contrast = self._scan_contrast self.settings.camera.saturation = self._scan_saturation def scan_next_texture_position(self): if not self._stop_scan: if self.current_position <= self._number_of_pictures and self.actor_ref.is_alive( ): if self.current_position == 0: self.init_texture_scan() color_image = self.hardwareController.scan_at_position( self._resolution, color=True) task = ImageTask(color_image, self._prefix, self.current_position, self._number_of_pictures, task_type="PROCESS_COLOR_IMAGE") self.image_task_q.put(task, True) #self._logger.debug("Color Progress %i of %i : " % (self.current_position, self._number_of_pictures)) self.current_position += 1 if self.actor_ref.is_alive(): self.actor_ref.tell({ FSEvents.COMMAND: FSScanProcessorCommand._SCAN_NEXT_TEXTURE_POSITION }) else: while not self.image_task_q.empty(): # wait until texture scan stream is ready. time.sleep(0.1) self.finish_texture_scan() if self.actor_ref.is_alive(): self.actor_ref.tell({ FSEvents.COMMAND: FSScanProcessorCommand._SCAN_NEXT_OBJECT_POSITION }) def init_object_scan(self): self._logger.info("Started object scan initialisation") message = {"message": "SCANNING_OBJECT", "level": "info"} self.eventmanager.broadcast_client_message(FSEvents.ON_INFO_MESSAGE, message) self.current_position = 0 self._laser_positions = self.settings.laser_positions # wait for ending of texture stream self.hardwareController.led.on(self.settings.led.red, self.settings.led.green, self.settings.led.blue) self.hardwareController.laser.on() self.hardwareController.camera.device.flush_stream() time.sleep(2) if not self._worker_pool.workers_active(): self._worker_pool.create(self.config.process_numbers) def finish_object_scan(self): self._logger.info("Finishing object scan.") self._worker_pool.kill() def scan_next_object_position(self): if not self._stop_scan: if self.current_position <= self._number_of_pictures and self.actor_ref.is_alive( ): if self.current_position == 0: self.init_object_scan() laser_image = self.hardwareController.scan_at_position( self._resolution) task = ImageTask(laser_image, self._prefix, self.current_position, self._number_of_pictures) self.image_task_q.put(task) self._logger.debug( "Laser Progress: %i of %i at laser position %i" % (self.current_position, self._number_of_pictures, self._current_laser_position)) self.current_position += 1 if self.actor_ref.is_alive(): self.actor_ref.tell({ FSEvents.COMMAND: FSScanProcessorCommand._SCAN_NEXT_OBJECT_POSITION }) else: self.finish_object_scan() def on_laser_detection_failed(self): self._logger.info("Send laser detection failed message to frontend") message = {"message": "NO_LASER_FOUND", "level": "warn"} self.eventmanager.broadcast_client_message(FSEvents.ON_INFO_MESSAGE, message) self.settings_mode_on() # on stop pykka actor def on_stop(self): self._worker_pool.clear_task_queue() self._worker_pool.kill() self.hardwareController.stop_camera_stream() self.hardwareController.turntable.stop_turning() self.hardwareController.led.off() self.hardwareController.laser.off(0) self.hardwareController.laser.off(1) def stop_scan(self): self._stop_scan = True self._worker_pool.kill() self._starttime = 0 self.utils.delete_scan(self._prefix) self.reset_scanner_state() self._logger.info("Scan stoped") self.hardwareController.stop_camera_stream() message = {"message": "SCAN_CANCELED", "level": "info"} self.eventmanager.broadcast_client_message(FSEvents.ON_INFO_MESSAGE, message) def image_processed(self, eventmanager, event): points = [] scan_state = 'texture_scan' if event['image_type'] == 'depth': scan_state = 'object_scan' point_cloud = list( zip(event['point_cloud'][0], event['point_cloud'][1], event['point_cloud'][2], event['texture'][0], event['texture'][1], event['texture'][2])) self.append_points(point_cloud) for index, point in enumerate(point_cloud): new_point = dict() new_point['x'] = str(point[0]) new_point['y'] = str(point[2]) new_point['z'] = str(point[1]) new_point['r'] = str(point[5]) new_point['g'] = str(point[4]) new_point['b'] = str(point[3]) points.append(new_point) self.semaphore.acquire() self._progress += 1 self.semaphore.release() message = { "points": points, "progress": self._progress, "resolution": self._total, "starttime": self._starttime, "timestamp": self.get_time_stamp(), "state": scan_state } self.eventmanager.broadcast_client_message(FSEvents.ON_NEW_PROGRESS, message) if self._progress == self._total: while not self.image_task_q.empty(): #wait until the last image is processed and send to the client. time.sleep(0.1) self.scan_complete() def scan_complete(self): end_time = self.get_time_stamp() duration = old_div(int(end_time - self._starttime), 1000) self._logger.debug("Time Total: %i sec." % (duration, )) self._starttime = 0 self._logger.info( "Scan complete writing pointcloud files with %i points." % (self.point_cloud.get_size(), )) self.point_cloud.saveAsFile(self._prefix) settings_filename = self.config.folders.scans + self._prefix + "/" + self._prefix + ".fab" self.settings.saveAsFile(settings_filename) message = { "message": "SAVING_POINT_CLOUD", "scan_id": self._prefix, "level": "info" } self.eventmanager.broadcast_client_message(FSEvents.ON_INFO_MESSAGE, message) self.utils.delete_image_folders(self._prefix) self.reset_scanner_state() event = FSEvent() event.command = 'COMPLETE' self.eventmanager.publish(FSEvents.COMMAND, event) message = { "message": "SCAN_COMPLETE", "scan_id": self._prefix, "level": "success" } self.eventmanager.broadcast_client_message(FSEvents.ON_INFO_MESSAGE, message) self.hardwareController.stop_camera_stream() def append_points(self, point_cloud_set): if self.point_cloud: self.point_cloud.append_points(point_cloud_set) #self.point_cloud.append_texture(texture_set) def get_resolution(self): return self.settings.resolution def get_number_of_pictures(self): return self._number_of_pictures def get_folder_name(self): return self._prefix def reset_scanner_state(self): self._logger.info("Reseting scanner states ... ") self.hardwareController.camera.device.flush_stream() self.hardwareController.laser.off() self.hardwareController.led.off() self.hardwareController.turntable.disable_motors() self._progress = 0 self.current_position = 0 self._number_of_pictures = 0 self._total = 0 self._starttime = 0 self.point_cloud = None def get_time_stamp(self): return old_div(int(datetime.now().strftime("%s%f")), 1000)
class FSScanProcessor(FSScanProcessorInterface): def __init__(self, config, settings, eventmanager, imageprocessor, hardwarecontroller, calibration): super(FSScanProcessorInterface, self).__init__(self, config, settings, eventmanager, imageprocessor, hardwarecontroller, calibration) #asyncio.set_event_loop(asyncio.new_event_loop()) self.settings = settings self.config = config self._logger = logging.getLogger(__name__) self.eventmanager = eventmanager.instance self.calibration = calibration self._worker_pool = None self.hardwareController = hardwarecontroller self.image_processor = imageprocessor self._prefix = None self._resolution = 16 self._number_of_pictures = 0 self._total = 1 self._progress = 1 self._is_color_scan = True self.point_clouds = [] self.both_cloud = [] self.current_position = 0 self._stop_scan = False self._current_laser_position = 1 self._starttime = 0 self._additional_worker_number = 1 self.texture_lock_event = threading.Event() self.texture_lock_event.set() self.utils = FSSystem() self._scan_brightness = self.settings.file.camera.brightness self._scan_contrast = self.settings.file.camera.contrast self._scan_saturation = self.settings.file.camera.saturation self._logger.info("Laser Scan Processor initilized.") # prevent deadlocks when opencv tbb is not available cv_build_info = cv2.getBuildInformation() # fallback to one worker. if not "TBB" in cv_build_info: self._logger.warning( "OpenCV does not support TBB. Falling back to single processing." ) self.config.file.process_numbers = 1 def on_receive(self, event): if event[FSEvents.COMMAND] == FSScanProcessorCommand.START: self.start_scan() if event[FSEvents.COMMAND] == FSScanProcessorCommand.STOP: self.stop_scan() if event[FSEvents.COMMAND] == FSScanProcessorCommand.SETTINGS_MODE_ON: self.settings_mode_on() if event[FSEvents.COMMAND] == FSScanProcessorCommand.SETTINGS_MODE_OFF: self.settings_mode_off() if event[ FSEvents. COMMAND] == FSScanProcessorCommand._SCAN_NEXT_TEXTURE_POSITION: self.scan_next_texture_position() if event[FSEvents. COMMAND] == FSScanProcessorCommand._SCAN_NEXT_OBJECT_POSITION: self.scan_next_object_position() if event[FSEvents. COMMAND] == FSScanProcessorCommand.NOTIFY_HARDWARE_STATE: self.send_hardware_state_notification() if event[FSEvents.COMMAND] == FSScanProcessorCommand.UPDATE_SETTINGS: self.update_settings(event['SETTINGS']) if event[FSEvents.COMMAND] == FSScanProcessorCommand.UPDATE_CONFIG: self.update_settings(event['CONFIG']) if event[FSEvents.COMMAND] == FSScanProcessorCommand.GET_HARDWARE_INFO: return self.hardwareController.get_firmware_version() if event[FSEvents. COMMAND] == FSScanProcessorCommand.GET_ADJUSTMENT_STREAM: return self.create_adjustment_stream() if event[FSEvents.COMMAND] == FSScanProcessorCommand.GET_LASER_STREAM: return self.create_laser_stream() if event[ FSEvents.COMMAND] == FSScanProcessorCommand.GET_TEXTURE_STREAM: return self.create_texture_stream() if event[FSEvents. COMMAND] == FSScanProcessorCommand.GET_SETTINGS_STREAM: return self.create_settings_stream() if event[FSEvents. COMMAND] == FSScanProcessorCommand.GET_CALIBRATION_STREAM: return self.create_calibration_stream() if event[FSEvents.COMMAND] == FSScanProcessorCommand.START_CALIBRATION: return self.start_calibration() if event[FSEvents.COMMAND] == FSScanProcessorCommand.STOP_CALIBRATION: return self.stop_calibration() if event[FSEvents. COMMAND] == FSScanProcessorCommand.NOTIFY_IF_NOT_CALIBRATED: return self.notify_if_is_not_calibrated() if event[ FSEvents. COMMAND] == FSScanProcessorCommand.CALL_HARDWARE_TEST_FUNCTION: device = event['DEVICE_TEST'] self.hardwareController.call_test_function(device) if event[FSEvents.COMMAND] == FSScanProcessorCommand.CONFIG_MODE_ON: self.config_mode_on() if event[FSEvents.COMMAND] == FSScanProcessorCommand.CONFIG_MODE_OFF: self.config_mode_off() if event[FSEvents.COMMAND] == FSScanProcessorCommand.IMAGE_PROCESSED: self.image_processed(event['RESULT']) def config_mode_on(self): self.hardwareController.start_camera_stream('alignment') def config_mode_off(self): self.hardwareController.stop_camera_stream() for i in range(self.config.file.laser.numbers): self.hardwareController.laser.off(i) self.hardwareController.led.off() self.hardwareController.turntable.stop_turning() def call_hardware_test_function(self, function): self.hardwareController.call_test_function(function) def scanner_is_calibrated(self): correct_plane_number = len(self.config.file.calibration.laser_planes ) == self.config.file.laser.numbers distance_is_set = True for i in range(self.config.file.laser.numbers - 1): plane = self.config.file.calibration.laser_planes[i] if (plane['distance'] is None) or (plane['distance'] == 0): distance_is_set = False break is_calibrated = correct_plane_number and distance_is_set return is_calibrated def notify_if_is_not_calibrated(self): try: self._logger.debug(self.config.file.calibration.camera_matrix) is_calibrated = self.scanner_is_calibrated() self._logger.debug( "FabScan is calibrated: {0}".format(is_calibrated)) if not is_calibrated: message = { "message": "SCANNER_NOT_CALIBRATED", "level": "warn" } self._logger.debug("Clients informaed") event = FSEvent() event.command = "STOP" self.eventmanager.publish(FSEvents.COMMAND, event) self.eventmanager.broadcast_client_message( FSEvents.ON_INFO_MESSAGE, message) return except Exception as e: self._logger.exception(e) def create_texture_stream(self): try: image = self.hardwareController.get_picture() image = self.image_processor.get_texture_stream_frame(image) return image except Exception as e: #self._logger.error(e) pass def create_settings_stream(self): try: image = self.hardwareController.get_picture() image = self.image_processor.get_settings_stream_frame(image) return image except Exception as e: #self._logger.error(e) pass def create_adjustment_stream(self): try: image = self.hardwareController.get_picture() image = self.image_processor.get_adjustment_stream_frame(image) return image except Exception as e: pass def create_calibration_stream(self): try: image = self.hardwareController.get_picture() image = self.image_processor.get_calibration_stream_frame(image) return image except Exception as e: # images are dropped this cateched exception.. no error hanlder needed here. pass def create_laser_stream(self): try: image = self.hardwareController.get_picture() return image except Exception as e: #self._logger.error("Error while grabbing laser Frame: " + str(e)) # images are dropped this cateched exception.. no error hanlder needed here. pass def update_settings(self, settings): try: self.settings.update(settings) #FIXME: Only change Color Settings when values changed. self.hardwareController.led.on(self.settings.file.led.red, self.settings.file.led.green, self.settings.file.led.blue) except Exception as e: self._logger.exception("Updating Settings failed: {0}".format(e)) pass def update_config(self, config): try: self.config.file.update(config) except Exception as e: pass def start_calibration(self): self.hardwareController.settings_mode_off() self.calibration.start() def stop_calibration(self): self.calibration.stop() def send_hardware_state_notification(self): self._logger.debug("Checking Hardware connections") if not self.hardwareController.arduino_is_connected(): message = {"message": "NO_SERIAL_CONNECTION", "level": "error"} self.eventmanager.broadcast_client_message( FSEvents.ON_INFO_MESSAGE, message) if not self.hardwareController.camera_is_connected(): message = {"message": "NO_CAMERA_CONNECTION", "level": "error"} self.eventmanager.broadcast_client_message( FSEvents.ON_INFO_MESSAGE, message) def settings_mode_on(self): self.hardwareController.settings_mode_on() def settings_mode_off(self): self.hardwareController.settings_mode_off() ## general start sequence def start_scan(self): self.settings_mode_off() self._logger.info("Scan started") self._stop_scan = False self.hardwareController.turntable.enable_motors() for i in range(int(self.config.file.laser.numbers)): self.hardwareController.laser.off(i) self._resolution = int(self.settings.file.resolution) self._is_color_scan = bool(self.settings.file.color) self._number_of_pictures = int(self.config.file.turntable.steps // self._resolution) self.current_position = 0 self._starttime = self.get_time_stamp() # TODO: rename prefix to scan_id self._prefix = datetime.fromtimestamp( time.time()).strftime('%Y%m%d-%H%M%S') # initialize pointcloud actors... self.point_clouds = [] #self.point_clouds = [FSPointCloud(config=self.config, color=self._is_color_scan) for _ in range(self.config.file.laser.numbers)] for laser_index in range(self.config.file.laser.numbers): self.point_clouds.append( FSPointCloud(config=self.config, color=self._is_color_scan, filename=self._prefix, postfix=laser_index, binary=False)) if self.config.file.laser.numbers > 1: self.both_cloud = FSPointCloud(config=self.config, color=self._is_color_scan, filename=self._prefix, postfix='both', binary=False) if self.scanner_is_calibrated() and self.actor_ref.is_alive(): if self._is_color_scan: self._total = ( self._number_of_pictures * self.config.file.laser.numbers) + self._number_of_pictures self.actor_ref.tell({ FSEvents.COMMAND: FSScanProcessorCommand._SCAN_NEXT_TEXTURE_POSITION }) else: self._total = self._number_of_pictures * self.config.file.laser.numbers self.actor_ref.tell({ FSEvents.COMMAND: FSScanProcessorCommand._SCAN_NEXT_OBJECT_POSITION }) else: self._logger.debug("FabScan is not calibrated scan canceled") self.actor_ref.tell({ FSEvents.COMMAND: FSScanProcessorCommand.NOTIFY_IF_NOT_CALIBRATED }) ## texture callbacks def init_texture_scan(self): message = {"message": "SCANNING_TEXTURE", "level": "info"} if self._worker_pool is None or not self._worker_pool.is_alive(): self._worker_pool = FSImageWorkerPool.start( scanprocessor=self.actor_ref) if self._worker_pool.is_alive(): self._logger.debug("Adding some workers to Pool.") self._worker_pool.tell({ FSEvents.COMMAND: FSSWorkerPoolCommand.CREATE, 'NUMBER_OF_WORKERS': self._additional_worker_number }) self.eventmanager.broadcast_client_message(FSEvents.ON_INFO_MESSAGE, message) self._scan_brightness = self.settings.file.camera.brightness self._scan_contrast = self.settings.file.camera.contrast self._scan_saturation = self.settings.file.camera.saturation self.hardwareController.led.on(self.config.file.texture_illumination, self.config.file.texture_illumination, self.config.file.texture_illumination) self.hardwareController.start_camera_stream(mode="default") # wait until camera is settled time.sleep(1) self.hardwareController.camera.device.flush_stream() def scan_next_texture_position(self): if not self._stop_scan: try: if self.current_position < self._number_of_pictures and self.actor_ref.is_alive( ): flush = False if self.current_position == 0: flush = True self.init_texture_scan() color_image = self.hardwareController.get_picture( flush=flush) color_image = self.image_processor.decode_image( color_image) self.hardwareController.move_to_next_position( steps=self._resolution, speed=800) task = ImageTask(color_image, self._prefix, self.current_position, self._number_of_pictures, task_type="PROCESS_COLOR_IMAGE") self._worker_pool.tell({ FSEvents.COMMAND: FSSWorkerPoolCommand.ADD_TASK, 'TASK': task }) color_image = None self.current_position += 1 if self.actor_ref.is_alive(): time.sleep(0.1) #self.self.texture_lock_event.clear() self.actor_ref.tell({ FSEvents.COMMAND: FSScanProcessorCommand._SCAN_NEXT_TEXTURE_POSITION }) else: self._logger.error("Worker Pool died.") self.stop_scan() else: self.finish_texture_scan() if self.actor_ref.is_alive(): self.actor_ref.tell({ FSEvents.COMMAND: FSScanProcessorCommand._SCAN_NEXT_OBJECT_POSITION }) else: self._logger.error("Worker Pool died.") self.stop_scan() except Exception as e: self._logger.exception("Scan Processor Error: {0}".format(e)) def finish_texture_scan(self): self._logger.info("Finishing texture scan.") self.current_position = 0 self.hardwareController.led.off() self.settings.file.camera.brightness = self._scan_brightness self.settings.file.camera.contrast = self._scan_contrast self.settings.file.camera.saturation = self._scan_saturation ## object scan callbacks def init_object_scan(self): self.hardwareController.start_camera_stream() if self._worker_pool is None or not self._worker_pool.is_alive(): self._worker_pool = FSImageWorkerPool.start( scanprocessor=self.actor_ref) self._logger.info("Started object scan initialisation") if self._is_color_scan: self._additional_worker_number = 3 else: self._additional_worker_number = 4 self._logger.debug("Adding some workers to pool.") self._worker_pool.tell({ FSEvents.COMMAND: FSSWorkerPoolCommand.CREATE, 'NUMBER_OF_WORKERS': self._additional_worker_number }) message = {"message": "SCANNING_OBJECT", "level": "info"} self.eventmanager.broadcast_client_message(FSEvents.ON_INFO_MESSAGE, message) self.current_position = 0 if self.config.file.laser.interleaved == "False": self.hardwareController.led.off() self.hardwareController.camera.device.flush_stream() def scan_next_object_position(self): if not self._stop_scan: if self.current_position <= self._number_of_pictures and self.actor_ref.is_alive( ): if self.current_position == 0: self.init_object_scan() self._logger.debug('Start creating Task.') for laser_index in range(self.config.file.laser.numbers): laser_image = self.hardwareController.get_image_at_position( index=laser_index) task = ImageTask(laser_image, self._prefix, self.current_position, self._number_of_pictures, index=laser_index) self._worker_pool.tell({ FSEvents.COMMAND: FSSWorkerPoolCommand.ADD_TASK, 'TASK': task }) self.current_position += 1 self.hardwareController.move_to_next_position( steps=self._resolution, speed=800) self._logger.debug('New Image Task created.') if self.actor_ref.is_alive(): self.actor_ref.tell({ FSEvents.COMMAND: FSScanProcessorCommand._SCAN_NEXT_OBJECT_POSITION }) else: self._logger.error("Worker Pool died.") self.stop_scan() self._logger.debug('End creating Task.') def on_laser_detection_failed(self): self._logger.info("Send laser detection failed message to frontend") message = {"message": "NO_LASER_FOUND", "level": "warn"} self.eventmanager.broadcast_client_message(FSEvents.ON_INFO_MESSAGE, message) self.settings_mode_on() # pykka actor stop event def on_stop(self): self.stop_scan() self.hardwareController.destroy_camera_device() self.finishFiles() self.hardwareController.turntable.stop_turning() self.hardwareController.led.off() for laser_index in range(self.config.file.laser.numbers): self.hardwareController.laser.off(laser_index) # on stop command by user def stop_scan(self): self._stop_scan = True self._starttime = 0 self.finishFiles() if self._prefix: self.utils.delete_folder( str(self.config.file.folders.scans) + '/' + str(self._prefix)) self.reset_scanner_state() self._logger.info("Scan stoped") self.hardwareController.stop_camera_stream() message = {"message": "SCAN_CANCELED", "level": "info"} self.eventmanager.broadcast_client_message(FSEvents.ON_INFO_MESSAGE, message) def clear_and_stop_worker_pool(self): # clear queue if self._worker_pool and self._worker_pool.is_alive(): self._logger.debug("Stopping worker Pool.") self._worker_pool.stop() def image_processed(self, result): if not self._stop_scan: if self._progress <= self._total and self.actor_ref.is_alive(): points = [] if not 'laser_index' in list(result.keys()): result['laser_index'] = -1 try: scan_state = 'texture_scan' if result['image_type'] == 'depth' and result[ 'point_cloud'] is not None: scan_state = 'object_scan' point_cloud = zip(result['point_cloud'][0], result['point_cloud'][1], result['point_cloud'][2], result['texture'][0], result['texture'][1], result['texture'][2]) for x, y, z, b, g, r in point_cloud: new_point = { "x": str(x), "y": str(z), "z": str(y), "r": str(r), "g": str(g), "b": str(b) } points.append(new_point) self.append_points(( x, y, z, r, g, b, ), result['laser_index']) # result = None except Exception as err: self._logger.warning( "Image processing Failure: {0}".format(err)) message = { "laser_index": result['laser_index'], "points": points, "progress": self._progress, "resolution": self._total, "starttime": self._starttime, "timestamp": self.get_time_stamp(), "state": scan_state } self.eventmanager.broadcast_client_message( FSEvents.ON_NEW_PROGRESS, message) message = None result = None self._logger.debug("Step {0} of {1}".format( self._progress, self._total)) self._progress += 1 if self._progress - 1 == self._total: self.scan_complete() def scan_complete(self): self._worker_pool.tell({FSEvents.COMMAND: FSSWorkerPoolCommand.KILL}) end_time = self.get_time_stamp() duration = int((end_time - self._starttime) // 1000) self._logger.debug("Time Total: {0} sec.".format(duration)) if len(self.point_clouds) == self.config.file.laser.numbers: self._logger.info("Scan complete writing pointcloud.") self._logger.debug( "Number of PointClouds (for each laser one): {0}".format( len(self.point_clouds))) self.finishFiles() settings_filename = self.config.file.folders.scans + self._prefix + "/" + self._prefix + ".fab" self.settings.save_json(settings_filename) #if bool(self.config.file.keep_raw_images): # self.utils.zipdir(str(self._prefix)) self.utils.delete_image_folders(self._prefix) self.reset_scanner_state() event = FSEvent() event.command = 'COMPLETE' self.eventmanager.publish(FSEvents.COMMAND, event) message = { "message": "SCAN_COMPLETE", "scan_id": self._prefix, "level": "success" } self.eventmanager.broadcast_client_message(FSEvents.ON_INFO_MESSAGE, message) self.hardwareController.stop_camera_stream() def append_points(self, points, index): if len(self.point_clouds) > 0: self.point_clouds[index].append_points(points) if len(self.point_clouds) > 1: self.both_cloud.append_points(points) def finishFiles(self): try: for laser_index in range(self.config.file.laser.numbers): if self.point_clouds and len( self.point_clouds ) > 0 and self.point_clouds[laser_index]: self.point_clouds[laser_index].closeFile() self.point_clouds[laser_index] = None if self.config.file.laser.numbers > 1: if self.both_cloud: self.both_cloud.closeFile() self.both_cloud = None except IOError as e: #TODO: Call stop scan function if this fails to release the scan process self._logger.exception( "Closing PointCloud files failed: {0}".format(e)) #self.scan_failed() def scan_failed(self): message = { "message": "SCAN_FAILED_STOPPING", "scan_id": self._prefix, "level": "error" } self.eventmanager.broadcast_client_message(FSEvents.ON_INFO_MESSAGE, message) self.stop_scan() def get_resolution(self): return self._resolution def get_number_of_pictures(self): return self._number_of_pictures def get_folder_name(self): return self._prefix def reset_scanner_state(self): self._logger.info("Reseting scanner states ... ") self.hardwareController.stop_camera_stream() for i in range(self.config.file.laser.numbers): self.hardwareController.laser.off(i) self.hardwareController.led.off() self.hardwareController.turntable.disable_motors() self.clear_and_stop_worker_pool() self._progress = 1 self.current_position = 0 self._number_of_pictures = 0 self._total = 0 self._starttime = 0 def get_time_stamp(self): return int(datetime.now().strftime("%s%f")) / 1000