def __init__(self): # 狀態 self._is_recording = False # 錄製中 self._is_live_view = False # 預覽中 self._is_capturing = False self._camera_list = self._build_camera_proxies() # 相機 proxy 列表 self._parameters = self._build_parameters() # 相機可控參數 self._report_collector = CameraReportCollector() # 相機報告蒐集 self._ui_status_sender = Repeater(self._send_ui_status, 0.1, True) self._camera_status_requester = Repeater( self._request_camera_status, 0.1, True ) self._delay = DelayExecutor(0.1) self._save_meta = None # 綁定 UI ui.dispatch_event( UIEventType.UI_CONNECT, { 'camera': self, 'project': project_manager } ) self.load_parameters('Default')
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 submit(self, name, frames, parameters={}): job = self.create_job(name, frames, parameters, self.is_cali()) # Deadline integration log.info(f'Deadline submit shot: {self}') deadline_ids = submit_deadline(self._parent.name, self, job) if deadline_ids is None: log.error('Deadline submit server error!') job.remove() return log.info(f'Deadline submit done: {self}') ui.dispatch_event( UIEventType.NOTIFICATION, { 'title': f'[{self.name}] Submit Success', 'description': (f'Shot [{self.name}] submitted with {len(frames)} frames.') }) if self.state != 2: self.update({'state': 2}) job.update({'deadline_ids': deadline_ids})
def check_deadline_server(self): check_result = check_deadline_server() if check_result != '': ui.dispatch_event(UIEventType.NOTIFICATION, { 'title': 'Deadline Connection Error', 'description': check_result }) ui.dispatch_event(UIEventType.DEADLINE_STATUS, check_result == '')
def select_job(self, job): if job: log.info(f'Select job: {job}') if job.get_id() not in self._selected_jobs: self._selected_jobs[job.get_id()] = job else: log.info('Select job: Empty') self.current_job = job ui.dispatch_event(UIEventType.JOB_SELECTED, job)
def update_cali_list(self): calis = get_calibrations() result = [] for cali in calis: name = f'{cali["name"]} - '\ f'{cali["_id"].generation_time:%m/%d %H:%M}' value = (str(cali['_id']), cali['deadline_ids'][-1]) result.append((name, value)) ui.dispatch_event(UIEventType.CALI_LIST, result)
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 _import_report(self, report): """匯入報告""" from master.ui import ui progress = report['progress'] self._progress_list[report['camera_id']] = progress[0] self._complete_check_list[ report['camera_id']] = progress[0] == progress[1] ui.dispatch_event(UIEventType.TICK_SUBMIT, sum(self._progress_list.values()))
def __init__(self): super().__init__() self._queue = Queue() self._cache = {} self._delay = DelayExecutor() self._multi_executor = MultiExecutor(self) # 綁定 UI ui.dispatch_event(UIEventType.UI_CONNECT, {'resolve': self}) self.start()
def create_project(self, name): """創建專案 Args: name: 專案名稱 """ project = ProjectEntity({'name': name}, self.on_entity_event) self._projects.insert(0, project) ui.dispatch_event(UIEventType.PROJECT_MODIFIED, [*self._projects]) return project
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 select_shot(self, shot): """選擇 Shot 選擇 Shot 後,會在 self._selected_shots 新增以便之後快速存取 """ if shot: log.info(f'Select shot: {shot}') if shot.get_id() not in self._selected_shots: self._selected_shots[shot.get_id()] = shot else: log.info('Select shot: Empty') self.current_shot = shot ui.dispatch_event(UIEventType.SHOT_SELECTED, shot)
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 trigger(self): """觸發相機擷取""" log.info('Trigger camera capture') hardware_trigger.trigger() # 觸發後,將相機參數做統一設定 for name, parm in self._parameters.items(): self.change_parameter( name, parm.get_value() ) ui.dispatch_event( UIEventType.TRIGGER, True ) self._is_capturing = True
def __init__(self): self.current_project = None # 目前選擇的專案 self.current_shot = None # 目前選擇的 Shot self.current_job = None self._projects = self._load_projects() # 專案,從 database 讀取 ui.dispatch_event(UIEventType.PROJECTS_INITIALIZED, list(self._projects)) """ 選擇過的 Shot,主要是為了在找 Shot 時可以比較快的找到 """ self._selected_shots = {} self._selected_jobs = {} self._selected_projects = {} # 自動選擇最近的 project 跟 shot if len(self._projects) > 0: self.select_project(self._projects[0])
def send_ui(self, camera_pixmap, save=False): """將 camera_pixmap 傳送給 UI Args: camera_pixmap: CameraPixmap """ if camera_pixmap.is_state(): ui.dispatch_event(UIEventType.CAMERA_STATE, camera_pixmap.to_state()) else: ui.dispatch_event( UIEventType.CAMERA_PIXMAP, camera_pixmap.to_payload(ui.get_state('Focus'), ui.get_state('caching'), save)) # 如果是 shot 圖像便存起來 if save and camera_pixmap.is_shot(): self.add_task(CameraLibraryTask.IMPORT, camera_pixmap)
def _send_ui_status(self): status = { 'bias': self._get_bias(), 'slaves': message_manager.get_nodes_count(), 'frames': -1, 'cache_size': project_manager.get_all_cache_size() } if self._is_recording: status['frames'] = min( [ camera.record_frames_count for camera in self._camera_list.values() ] ) ui.dispatch_event( UIEventType.UI_STATUS, status )
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 select_project(self, project): """選擇專案 選擇專案,如果專案已有 Shot,會選擇最新的 Shot """ self.current_project = project if project.get_id() not in self._selected_projects: self._selected_projects[project.get_id()] = project if project: log.info(f'Select project: {project}') if len(project.shots) != 0: self.select_shot(project.shots[0]) else: self.select_shot(None) else: log.info('Select project: Empty') if self.current_shot is not None: self.select_shot(None) ui.dispatch_event(UIEventType.PROJECT_SELECTED, project)
def _send_payload(self, payload): ui.dispatch_event(UIEventType.RESOLVE_GEOMETRY, payload)
def ui_tick_export(self): ui.dispatch_event(UIEventType.TICK_EXPORT)