def change_parameter(self, parm_name, value, affect_slider=False): """改變相機參數 會驗證參數在 setting 所設定的最大最小值,通過後再送給 slave Args: parm_name: 參數名稱 value: 參數數值 """ if self._is_recording: log.error("Can't change parameter due to wrong camera state") return parm = self._parameters[parm_name] result = parm.set(value) if result == 'min': log.error(f'{parm_name} value too small ({value})') elif result == 'max': log.error(f'{parm_name} value too big ({value})') elif result == 'OK': message_manager.send_message( MessageType.CAMERA_PARM, {'camera_parm': (parm_name, parm.get_value())} ) log.debug(f'Parameter <{parm_name}> changes to {value}') ui.dispatch_event( UIEventType.CAMERA_PARAMETER, (parm_name, value, affect_slider) )
def _run(self): while self._running: shot_meta = self._queue.get() if shot_meta is None: break shot_path = shot_meta.get_path() if shot_path is None: continue # 如果 self._file 是空的或者不是所需的檔案路徑,取代掉 if not (self._file is not None and self._file.get_path() == shot_path): if isinstance(self._file, CameraShotFileLoader): self._file.close() self._file = CameraShotFileLoader(shot_path, self._log) camera_image = self._file.load(shot_meta.frame) if camera_image is None: continue message_manager.send_message( MessageType.SHOT_IMAGE, shot_meta.get_parms(), camera_image.convert_jpeg(shot_meta.quality, shot_meta.scale_length))
def retrigger(self): log.info('Retrigger') message_manager.send_message( MessageType.RETRIGGER ) self.stop_capture()
def _request_camera_status(self): message_manager.send_message( MessageType.CAMERA_STATUS, { 'calibrate_frame': self._camera_list[ setting.get_working_camera_ids()[0] ].current_frame } )
def on_entity_event(self, event, entity): """聆聽實體事件 當專案與 Shot 有事件發生時的處理 Args: event: 實體事件 entity: 發生事件的實體 """ # 如果有實體刪除 if event is EntityEvent.REMOVE: # 專案: 同時在 self._projects 刪除並確認是否是 current 來清空 if isinstance(entity, ProjectEntity): log.info('Remove project: {}'.format(entity)) self._projects.remove(entity) ui.dispatch_event(UIEventType.PROJECT_MODIFIED, [*self._projects]) if self.current_project == entity: self.select_project(None) # Shot: 如果是 current 清空,並傳送給 slave 通知刪除檔案 elif isinstance(entity, ShotEntity): log.info('Remove shot: {}'.format(entity)) if self.current_shot == entity: self.select_shot(None) ui.dispatch_event(UIEventType.SHOT_MODIFIED, [*self.current_project.shots]) message_manager.send_message(MessageType.REMOVE_SHOT, {'shot_id': entity.get_id()}) elif isinstance(entity, JobEntity): log.info('Remove job: {}'.format(entity)) if self.current_job == entity: self.select_job(None) ui.dispatch_event(UIEventType.JOB_MODIFIED, [*self.current_shot.jobs]) # 如果有實體創建 elif event is EntityEvent.CREATE: log.info(f'Create {entity.print_name}: {entity}') if isinstance(entity, ShotEntity): ui.dispatch_event(UIEventType.SHOT_MODIFIED, [*self.current_project.shots]) self.select_shot(entity) elif isinstance(entity, JobEntity): ui.dispatch_event(UIEventType.JOB_MODIFIED, [*self.current_shot.jobs]) # 如果有實體更改 elif event is EntityEvent.MODIFY: log.info(f'Modify:\n {entity.print_name}')
def _stop_record(self): """停止運作,將錄製做收尾,並整理錄製報告傳給 master""" report = self._file.get_report() self._file.close() report.update({ 'camera_id': self._shot_meta.camera_id, 'shot_id': self._shot_meta.shot_id }) message_manager.send_message(MessageType.RECORD_REPORT, report)
def _slave_request(self, camera_pixmap): """向 slave 索取指定圖像 藉由 camera_pixmap 的資料去向 slave 索取圖像 Args: camera_pixmap: CameraPixmap """ message_manager.send_message(MessageType.GET_SHOT_IMAGE, camera_pixmap.get_parms())
def _run(self): while self._running: camera_image = self._get_buffer() if camera_image is None: break encoded_data = camera_image.convert_jpeg(**self._encode_parms) message_manager.send_message(MessageType.LIVE_VIEW_IMAGE, {'camera_id': self._camera_id}, encoded_data)
def record(self): """開關錄製 根據目前的 shot 選擇去啟動錄製 如果已在錄製中便關閉,並創建該 shot 的錄製回報蒐集器 """ self._is_recording = not self._is_recording ui.dispatch_event( UIEventType.RECORDING, self._is_recording ) parms = { 'is_start': self._is_recording } shot_id = project_manager.current_shot.get_id() is_cali = project_manager.current_shot.is_cali() # 開啟錄製 if self._is_recording: parms['shot_id'] = shot_id parms['is_cali'] = is_cali log.info('Start recording: {} / {}'.format( project_manager.current_project, project_manager.current_shot )) # 關閉錄製 else: log.info('Stop recording') # 取得這次錄製的相機參數 parameters = {} for name, parm in self._parameters.items(): parameters[name] = parm.get_value() # 建立錄製報告容器 self._report_collector.new_record_report_container( shot_id, parameters ) message_manager.send_message( MessageType.TOGGLE_RECORDING, parms ) if self._is_recording and is_cali: self.record()
def live_view( self, toggle, scale_length=150, close_up=None ): """開關相機預覽 開關指定的相機預覽,同時設定串流品質跟尺寸 Args: camera_ids: [相機ID] quality: 串流品質 scale_length: 最長邊長度 """ if toggle: self._delay.execute( lambda: ( log.info('Toggle LiveView on'), message_manager.send_message( MessageType.TOGGLE_LIVE_VIEW, { 'quality': setting.jpeg.live_view.quality, 'scale_length': scale_length, 'close_up': close_up, 'toggle': toggle } ), ui.dispatch_event( UIEventType.LIVE_VIEW, True ) ) ) else: log.info('Toggle LiveView off') message_manager.send_message( MessageType.TOGGLE_LIVE_VIEW, { 'toggle': toggle } ) ui.dispatch_event( UIEventType.LIVE_VIEW, False )
def submit_shot(self, name, frames, parameters): """到 deadline 放算""" shot = project_manager.current_shot log.info(f'Preparing to submit shot: {shot}') self._report_collector.new_submit_report_container( shot, name, frames, parameters ) # 通知 Slaves 傳輸轉檔 Shot message_manager.send_message( MessageType.SUBMIT_SHOT, { 'shot_id': shot.get_id(), 'job_name': name, 'frames': frames } )
def stop_capture(self, message=None): """停止擷取 主要是用在有 slave 斷線的情況,或者要重置相機擷取的格數 """ for camera in self._camera_list.values(): camera.update_status({'state': CameraState.CLOSE.value}) if self._is_capturing and message is not None: node = message.unpack() log.warning(f'Slave [{node.get_name()}] down, restart cameras') time.sleep(1) message_manager.send_message(MessageType.MASTER_DOWN) self._is_capturing = False ui.dispatch_event( UIEventType.TRIGGER, False )
def _run(self): while self._running: shot_id, job_name, frames, shot_file_paths = self._queue.get() if shot_id is None: break self._log.info( 'Submit shot: ' f'{shot_id} ({frames[0]}-{frames[-1]} [{len(frames)}])') # 創建資料夾 submit_image_path = f'{setting.submit_shot_path}{shot_id}/' os.makedirs(submit_image_path, exist_ok=True) self._log.debug(f'Save to {submit_image_path}') # 取出圖像 for camera_id, shot_file_path in shot_file_paths.items(): file_loader = CameraShotFileLoader(shot_file_path, self._log) # 進度定義 current_count = 0 total_count = len(frames) for frame in frames: camera_image = file_loader.load(frame) if camera_image is not None: image_path = ( f'{submit_image_path}{camera_id}_{frame:06d}.jpg') # 檢查是否有存在的檔案並大小差不多 is_exist = False if os.path.isfile(image_path): exist_size = os.stat(image_path).st_size # 大小超過閥值,略過 if exist_size > setting.bypass_exist_size: is_exist = True # 轉檔與儲存 if not is_exist: jpg_data = camera_image.convert_jpeg( setting.jpeg.submit.quality) with open(image_path, 'wb') as f: f.write(jpg_data) else: self._log.error(f'{camera_id} missing frame {frame}') # 進度整理 current_count += 1 # 傳送進度報告 message_manager.send_message( MessageType.SUBMIT_REPORT, { 'camera_id': camera_id, 'shot_id': shot_id, 'job_name': job_name, 'progress': (current_count, total_count) })
def _report_status(self): message_manager.send_message(MessageType.CAMERA_STATUS, { self._camera_connector.get_id(): self._camera_connector.get_status() })