Exemple #1
0
    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')
Exemple #2
0
    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)
            )
Exemple #3
0
    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})
Exemple #4
0
    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 == '')
Exemple #5
0
    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)
Exemple #6
0
    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)
Exemple #7
0
    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}')
Exemple #8
0
    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()))
Exemple #9
0
    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()
Exemple #10
0
    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
Exemple #11
0
    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()
Exemple #12
0
    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)
Exemple #13
0
    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
            )
Exemple #14
0
    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
Exemple #15
0
    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])
Exemple #16
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)
Exemple #17
0
    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
        )
Exemple #18
0
    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
        )
Exemple #19
0
    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)
Exemple #20
0
 def _send_payload(self, payload):
     ui.dispatch_event(UIEventType.RESOLVE_GEOMETRY, payload)
Exemple #21
0
 def ui_tick_export(self):
     ui.dispatch_event(UIEventType.TICK_EXPORT)