def __init__(self): super(ResultSaver, self).__init__() self._stop = subject.Subject() self.config = config.SettingAccessor(self.config_prefix) self.subjects: Subjects = services.service_provider.SubjectProvider().get_or_create_instance(None) self.saving_scheduler = scheduler.NewThreadScheduler() self.subjects.image_producer.pipe( operators.observe_on(self.saving_scheduler), operators.filter(self.config_enabled_filter("save_images")), operators.take_until(self._stop), ).subscribe(ErrorToConsoleObserver(self.save_image)) self.subjects.detection_result.pipe( operators.observe_on(self.saving_scheduler), operators.filter(self.config_enabled_filter("save_labels")), operators.take_until(self._stop), ).subscribe(ErrorToConsoleObserver(self.save_labels)) self.subjects.add_to_timeline.pipe( operators.observe_on(self.saving_scheduler), operators.filter(self.config_enabled_filter("save_events")), operators.take_until(self._stop), ).subscribe(ErrorToConsoleObserver(self.save_timeline_events)) config.setting_updated_channel.pipe( operators.filter(self._directory_filter), operators.take_until(self._stop), ).subscribe(ErrorToConsoleObserver(lambda x: self.initialize_saving_directory())) self.initialize_saving_directory()
def start(self): if not self.subjects.analyzer_connected.value: if not self.analyzer_not_connected_prompt(): return qt_scheduler = QtScheduler(QtCore) # this sequence is auto-back-pressured observable = self.request_media_file(qt_scheduler).pipe( # list of files operators.observe_on(scheduler.NewThreadScheduler()), # The following sequence will be run on a new thread. operators.flat_map(rx.from_list), # observable of files. self.load_file_operator( ), # When back pressure, this one will block. # observable of frames (AcquiredImage). This operation will be blocked if the downstream is blocked. operators.take_until(self._stop), ).subscribe( ErrorToConsoleObserver(on_next=self.next_image, on_error=self._catch)) self.subjects.analyzer_back_pressure_detected.pipe( operators.take_until(self._stop), ).subscribe( self.notify_back_pressure_changed) super().start()
def configure_subscriptions(self): self.subjects.detection_result.pipe( operators.buffer_with_count(self.config["group_size"]), operators.take_until(self._stop), ).subscribe(ErrorToConsoleObserver(self.process_distribution_data)) self.subjects.sample_image_data.pipe(operators.take_until( self._stop), ).subscribe( ErrorToConsoleObserver(self.render_sample_image))
def enqueue(self, observable: Observable, group: str = 'default-group', retries: int = 0, description: str = None) -> Observable: def log_status(status): logging.debug( str({ 'WorkQueue': str(self), 'group': group, status: description })) log_status('ENQUEUED') output = Subject() errors = Subject() output_finalized = Subject() def handle_error(e, _): log_status('FAILED') errors.on_next(e) return empty() def set_output_finalized(): output_finalized.on_next(True) work = of(True).pipe( do_action(lambda _: log_status('STARTED')), take_until(output_finalized), flat_map(lambda _: observable.pipe( map(lambda value: of({ 'value': value, 'output': output })), retry_with_backoff( retries=retries, description='{}.enqueue(group={}, description={})'.format( self, group, description)), catch(handler=handle_error), take_until(output_finalized), take_until_disposed())), concat(of(of({ 'completed': True, 'output': output }))), finally_action(lambda: log_status('COMPLETED'))) self._queue.on_next({'work': work, 'group': group}) return output.pipe(observe_on(self.request_scheduler), throw_when(errors), take_while(lambda r: not r.get('completed')), map(lambda r: r.get('value')), finally_action(set_output_finalized))
def _initialize_drag(self, ui: FrameUI): offset = rx.merge(self.on_drag_start, self.on_drag).pipe( ops.filter( lambda e: self.draggable and e.button == MouseButton.LEFT), ops.filter(lambda e: ui.allow_drag(self, self.position_of(e))), ops.filter(lambda e: not self.resizable or ui.resize_handle_at( self, e.position) == Nothing), ops.map(lambda e: e.position), ops.pairwise(), ops.map(lambda v: v[1] - v[0]), ops.take_until( self.on_drag_end.pipe( ops.filter(lambda e: e.button == MouseButton.LEFT))), ops.repeat(), ops.take_until(self.on_dispose)) self._drag_listener = offset.subscribe( self.move_by, on_error=self.context.error_handler)
def test_without_bp(self): self.source.pipe( operators.do_action(lambda x: print(f"Producing {x}")), operators.map(self.slow_op), operators.do_action(self.stop), operators.take_until(self._stop), ).run()
def test_with_observe_on(self): self.source.pipe( operators.do_action(lambda x: print(f"Producing {x}")), operators.observe_on(NewThreadScheduler()), operators.map(self.slow_op), operators.do_action(self.stop), operators.take_until(self._stop), ).run()
def test_with_buffer(self): self.source.pipe( operators.do_action(lambda x: print(f"Producing {x}")), bp_operator(BackPressure.BUFFER), operators.map(self.slow_op), operators.do_action(self.stop), operators.take_until(self._stop), ).run()
def event_for(button: MouseButton) -> Observable: return self.on_button_release(button).pipe( ops.map(lambda _: rx.concat( rx.of(self.position), rx.never().pipe( ops.take_until(self.on_button_press(button))))), ops.exclusive(), ops.map(lambda p: MouseUpEvent(self.context, p, button)))
def test_attach_size(self): self.source.pipe( operators.do_action(lambda x: print(f"Producing {x}")), bp_drop_operator_attach_size(3), operators.do_action(print), operators.map(self.slow_op), operators.do_action(self.stop), operators.take_until(self._stop), ).run()
def __init__(self, context: Context, layout: Optional[Layout] = None): super().__init__(context, layout) def on_resolution_change(size: Dimension): self.bounds = Bounds(0, 0, size.width, size.height) context.observe("window_size") \ .pipe(ops.take_until(self.on_dispose)) \ .subscribe(on_resolution_change, on_error=self.error_handler)
def on_drag(self) -> Observable: mouse = MouseInput.input(self) position = mouse.observe("position") return self.on_drag_start.pipe( ops.map(lambda e: position.pipe( ops.skip(1), ops.map(lambda p: DragEvent(self, p, e.button)), ops.take_until(mouse.on_button_release(e.button)))), ops.exclusive())
def __init__(self, context: Context): super().__init__(context) def dispatch(event: PositionalEvent) -> None: self.context.dispatcher_at( event.position).map(lambda d: d.dispatch_event(event)) self._dispatchers = rx.merge(*self.positional_events) \ .pipe(ops.take_until(self.on_dispose)) \ .subscribe(dispatch, on_error=self.error_handler)
def on_drag_leave(self) -> Observable: mouse = MouseInput.input(self) position = mouse.observe("position") from_inside = self.on_mouse_down.pipe( ops.map(lambda e: position.pipe( ops.filter(lambda p: not self.bounds.contains(p - self.offset) ), ops.take(1), ops.map(lambda p: (p, e.button)), ops.take_until(mouse.on_button_release(e.button)))), ops.exclusive()) from_outside = self.on_drag_over.pipe( ops.map(lambda e: self.on_mouse_out.pipe( ops.take(1), ops.map(lambda o: (o.position, e.button)), ops.take_until(mouse.on_button_release(e.button)))), ops.exclusive()) return rx.merge(from_inside, from_outside).pipe( ops.map(lambda e: DragLeaveEvent(self, e[0], e[1])))
def on_drag_start(self) -> Observable: mouse = MouseInput.input(self) position = mouse.observe("position") return self.on_mouse_down.pipe( ops.map(lambda e: position.pipe( ops.take(1), ops.map(lambda _: DragStartEvent(self, e.position, e.button)), ops.take_until(mouse.on_button_release(e.button)))), ops.exclusive())
def start(self): self.fps = self.config["fps"] rx.interval(1 / self.fps).pipe( operators.map(lambda x: self.generate_image()), operators.map( lambda arr: AcquiredImage(arr, datetime.now().timestamp())), operators.take_until(self._stop), ).subscribe(ErrorToConsoleObserver(self.next_image)) self.running = True super().start()
def on_drag_over(self) -> Observable: mouse = MouseInput.input(self) return mouse.on_mouse_down.pipe( ops.filter( lambda e: not self.bounds.contains(e.position - self.offset)), ops.map(lambda e: self.on_mouse_over.pipe( ops.map(lambda o: (o.position, e.button)), ops.take_until(mouse.on_button_release(e.button)))), ops.exclusive(), ops.map(lambda e: DragOverEvent(self, e[0], e[1])))
def start(self): if self.is_running(): self.logger.warning( "GRPC remote inference server is already connected.") return self.inference_comm.connection_chan.pipe( operators.take_until(self._stop)).subscribe( ErrorToConsoleObserver(self.configure_subscriptions)) self.inference_comm.connect_to_grpc_server(self.config["ip"], self.config["port"]) super(RemoteAnalyzer, self).start()
def _initialize_resize(self, ui: FrameUI): mouse = MouseInput.input(self) bounds = self.on_drag_start.pipe( ops.filter( lambda e: self.resizable and e.button == MouseButton.LEFT), ops.map(lambda e: ui.resize_handle_at( self, e.position).map(lambda v: Frame._ResizeState( v, e.position, self.bounds, self.minimum_size))), ops.map(lambda v: v.map(rx.of).value_or(rx.empty())), ops.switch_latest(), ops.map(lambda s: mouse.on_mouse_move.pipe( ops.map(lambda e: self._bounds_for_state(s, e.position)), ops.take_until(mouse.on_button_release(MouseButton.LEFT)))), ops.switch_latest(), ops.take_until(self.on_dispose)) def set_bounds(b: Bounds) -> None: self.bounds = b self._resize_listener = bounds.subscribe( set_bounds, on_error=self.context.error_handler)
def _simex_configure_subscription(self, x=None): self.subjects.image_producer.pipe( operators.observe_on(self.execution_thread), operators.map(lambda acquired_image: acquired_image.image ), # pluck the image array operators.map(lambda im: np.median(im)), operators.buffer_with_count(self.config["buffer_count"]), operators.map(lambda medians: np.mean(medians)), operators.take_until(self._stop) ).subscribe( ErrorToConsoleObserver(lambda t: self.instance.request_port_update( OutputPorts.BRIGHTNESS.value, np.asarray(t, dtype=np.float64)). subscribe(ErrorToConsoleObserver())))
def on_mouse_over(self) -> Observable: position = MouseInput.input(self).observe("position") local_pos = position.pipe(ops.map(lambda p: p - self.offset)) return self.on_mouse_move.pipe( ops.map(lambda e: e.position), ops.map(lambda p: rx.concat( rx.of(p), rx.never().pipe( ops.take_until( local_pos.pipe( ops.filter(lambda l: not self.bounds.contains(l))) )))), ops.exclusive(), ops.map(lambda p: MouseOverEvent(self, p)))
def start(self): # report more image when back pressure self.subjects.image_producer.pipe( operators.observe_on(self.scheduler), operators.combine_latest( self.subjects.analyzer_back_pressure_detected), operators.filter( lambda x: x[1]), # only operate when back pressure operators.buffer_with_time(1.0), # in 1 sec operators.filter(lambda x: len(x) > 3), # more than 3 emission operators.throttle_first(3.0), # report every 3 seconds operators.take_until(self._stop), ).subscribe(self.report_back_pressure_emission) self.subjects.image_producer.pipe( operators.observe_on( self.scheduler), # prevent blocking the upstream subject operators.filter(self.back_pressure_barrier), operators.buffer_with_count(5), bp_drop_report_full(self.subjects.analyzer_back_pressure_detected, 3, 1), operators.take_until(self._stop), ).subscribe(ErrorToConsoleObserver(self.produce_fake_analyze_data)) super(TestAnalyzer, self).start()
def schedule_emit_next_until(until: subject.Subject): stop_emitting = False def _action(sch: rx.typing.Scheduler, state=None): emit_next() def until_on_next(v): nonlocal stop_emitting stop_emitting = True until.pipe(operators.take_until(_stop)).subscribe(until_on_next, scheduler=sch) if not stop_emitting: sch.schedule(_action)
def play_step(self, step, cancel): interval = rx.interval(0.1) interval_steps = rx.just(step).pipe( ops.flat_map(lambda step: interval.pipe(ops.map(lambda _: step)))) step_done = interval_steps.pipe( ops.filter(lambda step: self.player.position() >= step.step_end), ops.do_action( lambda step: self.player.set_position(step.loop_start)), ops.take(1)) loop_done = interval_steps.pipe( ops.filter(lambda step: self.player.position() >= step.loop_end), ops.do_action( lambda step: self.player.set_position(step.loop_start)), ops.take_until(cancel.pipe(ops.skip(1)))) return step_done.pipe(ops.merge(loop_done))
def handleSubscription(msg: RxImpMessage): currentCount = 0 def on_next(next): nonlocal currentCount nextMsg = RxImpMessage(topic=msg.topic, count=currentCount, rx_state=RxImpMessage.STATE_NEXT, payload=json.dumps(next), id=msg.id) currentCount += 1 self._out.on_next(nextMsg) def on_error(error): nonlocal currentCount errorMsg = RxImpMessage(topic=msg.topic, count=currentCount, rx_state=RxImpMessage.STATE_ERROR, payload=json.dumps(error), id=msg.id) currentCount += 1 self._out.on_next(errorMsg) def on_complete(): nonlocal currentCount completeMsg = RxImpMessage( topic=msg.topic, count=currentCount, rx_state=RxImpMessage.STATE_COMPLETE, payload=None, id=msg.id) currentCount += 1 self._out.on_next(completeMsg) handler(json.loads(msg.payload)).pipe( take_until( self._in.pipe( filter(lambda x: x.rx_state == RxImpMessage.STATE_DISPOSE), filter(lambda x: x.id == msg.id), take(1)))).subscribe( on_next=lambda x: on_next(x), on_error=lambda x: on_error(x), on_completed=lambda: on_complete())
def __init__(self, toolkit: Toolkit, look_and_feel: Optional[LookAndFeel] = None, font_options: Optional[FontOptions] = None, window_manager: Optional[WindowManager] = None, error_handler: Optional[ErrorHandler] = None) -> None: if toolkit is None: raise ValueError("Argument 'toolkit' is required.") super().__init__() from alleycat.ui import WindowManager from alleycat.ui.glass import GlassLookAndFeel self._toolkit = toolkit self._look_and_feel = Maybe.from_optional(look_and_feel).or_else_call(lambda: GlassLookAndFeel(toolkit)) self._font_options = Maybe.from_optional(font_options).or_else_call( lambda: FontOptions(antialias=ANTIALIAS_SUBPIXEL, hint_style=HINT_STYLE_FULL)) self._error_handler = Maybe.from_optional(error_handler).value_or(toolkit.error_handler) self._window_manager = Maybe.from_optional(window_manager) \ .or_else_call(lambda: WindowManager(self.error_handler)) inputs = toolkit.create_inputs(self) assert inputs is not None self._inputs = {i.id: i for i in inputs} self._pollers = [i for i in inputs if isinstance(i, EventLoopAware)] # noinspection PyTypeChecker self.surface = self.observe("window_size").pipe(ops.map(self.toolkit.create_surface)) old_surface = self.observe("surface").pipe( ops.pairwise(), ops.map(lambda s: s[0]), ops.take_until(self.on_dispose)) old_surface.subscribe(Surface.finish, on_error=self.error_handler)
def __init__(self, context: Context, visible: bool = True) -> None: if context is None: raise ValueError("Argument 'context' is required.") # noinspection PyTypeChecker self.visible = visible self._context = context self._valid = False self._ui = self.create_ui() assert self._ui is not None super().__init__() self.validate() self.ui \ .on_invalidate(self) \ .pipe(ops.take_until(self.on_dispose)) \ .subscribe(lambda _: self.invalidate(), on_error=self.error_handler)
def downstream_subscribe(observer: rx.core.Observer, sch: rx.typing.Scheduler = None): def emit_next(): if len(buffer): observer.on_next(buffer.pop(0)) else: if _upstream_completed: observer.on_completed() def schedule_emit_next_until(until: subject.Subject): stop_emitting = False def _action(sch: rx.typing.Scheduler, state=None): emit_next() def until_on_next(v): nonlocal stop_emitting stop_emitting = True until.pipe(operators.take_until(_stop)).subscribe(until_on_next, scheduler=sch) if not stop_emitting: sch.schedule(_action) def should_stop_updated(val: bool): if val: # should stop, do nothing until next message pass else: # normal operation # Cannot guarantee that the should_stop will emit every time the value is received. schedule_emit_next_until(should_stop) should_stop.pipe( operators.take_until(_stop)).subscribe(should_stop_updated)
def configure_subscriptions(self, connected): if connected: self.subjects.image_producer.pipe( operators.observe_on(self.feed_scheduler), operators.buffer_with_count(self.batch_size), bp_operator(BackPressure.DROP, 5), operators.take_until(self._stop), ).subscribe(ErrorToConsoleObserver(self.feed_image)) self.inference_comm.back_pressure_chan.pipe( operators.subscribe_on(self.process_scheduler), operators.take_until(self._stop)).subscribe( ErrorToConsoleObserver(self.update_back_pressure_status)) # report error when image source is still producing when back pressuring self.subjects.analyzer_back_pressure_detected.pipe( operators.combine_latest(self.subjects.image_producer), operators.filter(lambda x: x[0]), operators.throttle_first(1.0), operators.take_until(self._stop), ).subscribe( ErrorToConsoleObserver(lambda x: self.logger.warning( "Image is feeding while back-pressure is detected. Please slow down the FPS" ))) self.inference_comm.result_chan.pipe( operators.subscribe_on(self.process_scheduler), operators.take_until(self._stop)).subscribe( ErrorToConsoleObserver(self.result_processing)) self.inference_comm.error_chan.pipe( operators.take_until(self._stop)).subscribe( ErrorToConsoleObserver(lambda err: self.logger.error(err))) self.inference_comm.connection_chan.pipe( operators.take_until(self._stop)).subscribe( ErrorToConsoleObserver(lambda connected: self.logger.info( "GRPC Remote analyzer connected" if connected else "GRPC Remote analyzer disconnected"))) self.inference_comm.stats_chan.take_until(self._stop).subscribe( ErrorToConsoleObserver(lambda x: self.logger.info( f"Processed {x.frame} frames. Average {x.processTime / x.frame} secs" )))
# 위와 다른 점: args 로 생성한다. print('--') # 마블에서 옵저버블 생성 하기 # 타이밍을 제어 할 수 있다. from rx.testing import marbles, TestScheduler scheduler = TestScheduler() ts = 0.1 rx.from_marbles('--(a1)-(b2)---(c3)|', timespan=ts).subscribe(print_value) rx.from_marbles('(a6)---(b5)(c4)|', timespan=ts).subscribe(print_value) time.sleep(2) print('--') # Interval 을 이용한 옵저버블 생성 rx.interval(0.3).pipe(ops.take_until(rx.timer(3))).subscribe(print_value) time.sleep(4) print('--') # 버퍼 print('-- buffer') rx.from_(range(2000)).pipe(ops.buffer( rx.interval(0.001))).subscribe(on_next=lambda buffer: print( '# of items in buffer {}'.format(len(buffer)))) time.sleep(2) print('-- buffer with count') rx.from_(range(10)).pipe(ops.buffer_with_count(3)).subscribe(print_value) print('-- buffer with time') rx.interval(1).pipe(