def test_return_most_recent(self): healths = [ BasicHealthModel( name="test", health_status=HealthStatus.DEAD, health_message="first", ), BasicHealthModel( name="test", health_status=HealthStatus.DEAD, health_message="second", ), ] obs_a = rx.of(healths[0]).pipe(ops.timestamp(scheduler=HistoricalScheduler(1))) obs_b = rx.of(healths[1]).pipe(ops.timestamp(scheduler=HistoricalScheduler(2))) result: BasicHealthModel = None def assign(v): nonlocal result result = v obs_a.pipe(ops.combine_latest(obs_b), most_critical()).subscribe(assign) self.assertEqual(result.health_status, HealthStatus.DEAD) self.assertEqual(result.health_message, "second")
def start(self): if not self.stopped: return super().start() mouse_source, keyboard_source, engagement_source = self.sources initial_keyboard_event = keyboard.KeyboardEvent(keyboard.KEY_UP, 0) initial_mouse_event = mouse.ButtonEvent(event_type=mouse.UP, button=0, time=time.time()) self.subscriptions = [ mouse_source.output.pipe( operators.start_with(initial_mouse_event)).pipe( operators.combine_latest( keyboard_source.output.pipe( operators.start_with(initial_keyboard_event)), engagement_source.output)).pipe( operators.throttle_first(0.1)) # in seconds .subscribe(self.update) ] if self.window is None: self.window = Window(points=self.points_in_buffer, toggle_callback=self.toggle_recording) self.window.show() self.window.activateWindow() self.window.raise_()
def start(self): if not self.stopped: return super().start() head_rotation_vector, voice_present = self.sources # Observable with all data channels merged into one stream obs = head_rotation_vector.output.pipe( operators.combine_latest(voice_present.output)) self.subscriptions = [ obs.subscribe(self.process_state), ]
def submit(gate: str, keys: List[str], ui: Dict[str, QWidget]) -> rx.Observable: if isinstance(gate, str): gate = ui[gate] gate = _unwrap_subject(gate) items = [_unwrap_subject(ui[k]) for k in keys] combined = items[0].pipe(ops.combine_latest(*items[1:]), ops.map(lambda vs: dict(zip(keys, vs)))) return gate.pipe(ops.filter(lambda x: x), ops.with_latest_from(combined), ops.map(lambda x: x[1]))
def _combine_most_critical(*obs: Sequence[rx.Observable]): """ Combines an observable sequence of an observable sequence of BasicHealthModel to an observable sequence of BasicHealthModel with the most critical health status. If there are multiple BasicHealthModel with the same criticality, the most recent item is chosen. :param obs: Sequence[rx.Observable[BasicHealthModel]] """ return rx.pipe( ops.timestamp(), ops.combine_latest(*[x.pipe(ops.timestamp()) for x in obs]), most_critical(), )
def main(): loop = asyncio.get_event_loop() io_scheduler = AsyncIOThreadSafeScheduler(loop=loop) scheduler = ThreadPoolScheduler(multiprocessing.cpu_count()) semaphore = Subject() semaphore_stream = semaphore.pipe( ops.flat_map(lambda _: rx.of(True).pipe( ops.delay(ARGS.block_time, scheduler=scheduler), ops.start_with(False))), ops.start_with(True)) video_stream_observable = rx.using( lambda: VideoStreamDisposable(), lambda d: rx.from_iterable(video_stream_iterable(d.cap))) gated_video_stream = video_stream_observable.pipe( ops.subscribe_on(scheduler), ops.sample(1 / ARGS.fps), # sample frames based on fps ops.combine_latest(semaphore_stream), ops.filter(lambda tup: tup[1]), # proceed only if semaphore allows ops.map(lambda tup: tup[0]) # take only frame ) disposable = gated_video_stream.pipe( ops.filter(has_face), # filter frames without faces ops.map(lambda frame: Image.fromarray( cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))), # map frame to PIL image ops.map(lambda img: img.resize( (640, 360))), # resize image (inference will be faster) ops.observe_on(io_scheduler), ops.map(lambda img: ImageFacesPair(img, analyse_frame(img)) ), # analyse frame for faces ops.filter(lambda img_faces_pair: any([ face.top_prediction.confidence > ARGS.threshold for face in img_faces_pair.faces ])), # proceed only if there is a known face in the frame ops.throttle_first(1), ops.flat_map(unlock_request), # unlock the door ops.do_action( on_next=lambda _: semaphore.on_next(True) ) # trigger semaphore which will block stream for "block-seconds" seconds (doors are unlocked for that long after unlock request) ).subscribe(on_error=lambda e: logger.exception(e)) try: loop.run_forever() except Exception as e: logger.exception(e) logger.info("Smart lock face recognition engine shutdown") disposable.dispose()
def submit(gate: Hashable, keys: List[Hashable], ui: Dict[Hashable, QWidget]) -> rx.Observable: try: gate = ui[gate] except (ValueError, TypeError): pass gate = _unwrap_subject(gate) items = [_unwrap_subject(ui[k]) for k in keys] combined = items[0].pipe( ops.combine_latest(*items[1:]), ops.map(lambda vs: dict(zip(keys, vs))) ) return gate.pipe( ops.filter(lambda x: x), ops.with_latest_from(combined), ops.map(lambda x: x[1]), )
def serve(config, model, data): '''Serves a model This operator serves a model. It loads models received on the model observable, and executes it on each item received on the data observable. The configuration observable must contain a serve section with the following fields: * input_field: The input field name used to run inference. * output_field: The output field name where inference result is set. additionally, a "prepare" field can be set if some data transformation is needed before feeding the model. When not present, the input data is converted to a numpy array Args: config: configuration observable. Returns: An observable of predictions. Each item is a copy of the original datay item, with an additional field. The name of the additional field if the one set in output_field. ''' predict = model.pipe( trace_observable(prefix="model", trace_next_payload=False), ops.map(load_mlflow_model), ops.combine_latest(config), ops.starmap(create_model_predict), ) transforms = config.pipe( trace_observable(prefix="prepare", trace_next_payload=False), ops.map(create_transform_functions)) prediction = data.pipe( rs.ops.with_latest_from(transforms, predict), ops.starmap(infer), ops.filter(lambda i: i is not None), ) return prediction,
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" )))
def test_returns_dead_over_unhealthy(self): healths = [ BasicHealthModel( name="test", health_status=HealthStatus.DEAD, ), BasicHealthModel( name="test", health_status=HealthStatus.UNHEALTHY, ), ] obs_a = rx.of(healths[0]).pipe(ops.timestamp(scheduler=HistoricalScheduler(1))) obs_b = rx.of(healths[1]).pipe(ops.timestamp(scheduler=HistoricalScheduler(2))) result: BasicHealthModel = None def assign(v): nonlocal result result = v obs_a.pipe(ops.combine_latest(obs_b), most_critical()).subscribe(assign) self.assertEqual(result.health_status, HealthStatus.DEAD)
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 create(): return e1.pipe( ops.combine_latest(e2), ops.map(sum), )
def create(): return e1.pipe( ops.combine_latest(e2), ops.map(lambda xy: _raise(ex)), )
op.catch() op.retry() """Utility""" op.delay() op.materialize() op.time_interval() op.timeout() op.timestamp() """Conditional and Boolean""" op.all() op.contains() op.default_if_empty() op.sequence_equal() op.skip_until() op.skip_while() op.take_until() op.take_while() """Connectable""" op.publish() op.ref_count() op.replay() """Combining""" op.combine_latest() op.merge() op.start_with() op.zip()
def run(self, print_output=True, ct: Optional[CancellationToken] = None, poll_interval=10, first_check=0.2, max_time=None, ct_check=0.2) -> rx.Observable: """Starts the MCERD process. Args: print_output: whether MCERD output is also printed to console ct: token that is checked periodically to see if the simulation should be stopped. poll_interval: seconds between each check to see if the simulation process is still running. first_check: seconds until the first time mcerd is polled. max_time: maximum running time in seconds. ct_check: how often cancellation is checked in seconds. Return: observable stream where each item is a dictionary. All dictionaries contain the same keys. """ # Create files necessary to run MCERD self.create_mcerd_files() cmd = self.get_command() ct = ct or CancellationToken() process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=gf.get_bin_dir(), universal_newlines=True) errs = rx.from_iterable(iter(process.stderr.readline, "")) outs = rx.from_iterable(iter(process.stdout.readline, "")) is_running = MCERD.running_check(process, first_check, poll_interval) ct_check = MCERD.cancellation_check(process, ct_check, ct) if max_time is not None: timeout = MCERD.timeout_check(process, max_time, ct) else: timeout = rx.empty() thread_count = multiprocessing.cpu_count() pool_scheduler = ThreadPoolScheduler(thread_count) merged = rx.merge(errs, outs).pipe( ops.subscribe_on(pool_scheduler), MCERD.get_pipeline(self._seed, self._rec_filename, print_output=print_output), ops.combine_latest(rx.merge(is_running, ct_check, timeout)), ops.starmap( lambda x, y: { **x, **y, MCERD.IS_RUNNING: x[MCERD.IS_RUNNING] and y[MCERD.IS_RUNNING] }), ops.take_while(lambda x: x[MCERD.IS_RUNNING], inclusive=True), ) # on_completed does not get called if the take_while condition is # inclusive so this is a quick fix to get the files deleted. # TODO surely there is a way to get the on_completed called? def del_if_not_running(x): if not x[MCERD.IS_RUNNING]: self.delete_unneeded_files() return merged.pipe( ops.do_action(on_next=del_if_not_running, on_error=lambda _: self.delete_unneeded_files(), on_completed=self.delete_unneeded_files))
class Component(Drawable, StyleResolver, MouseEventHandler, EventDispatcher, ContextAware, ReactiveObject): visible: RP[bool] = rv.new_property() parent: RP[Maybe[Container]] = rv.from_value(Nothing) offset: RV[Point] = parent.as_view().map(lambda _, parent: parent.map( lambda p: rx.combine_latest(p.observe("offset"), p.observe("location")) .pipe(ops.map(lambda v: v[0] + v[1]))).or_else_call(lambda: rx.of( Point(0, 0)))).pipe(lambda _: (ops.exclusive(), )) _minimum_size: RP[Dimension] = rv.from_value(Dimension(0, 0)) _preferred_size: RP[Dimension] = rv.from_value(Dimension(0, 0)) minimum_size_override: RP[Maybe[Dimension]] = rv.from_value(Nothing) minimum_size: RV[Dimension] = rv.combine_latest( _minimum_size, minimum_size_override)(ops.pipe(ops.map(lambda v: v[1].value_or(v[0])), ops.distinct_until_changed())) preferred_size_override: RP[Maybe[Dimension]] = rv.from_value( Nothing).pipe(lambda o: (ops.combine_latest(o.observe("minimum_size")), ops.map(lambda t: t[0].map(lambda v: t[1].copy( width=max(v.width, t[1].width), height=max(v.height, t[1].height)))), ops.distinct_until_changed())) preferred_size: RV[Dimension] = rv.combine_latest( _preferred_size, preferred_size_override, minimum_size)(ops.pipe( ops.map(lambda v: (v[1].value_or(v[0]), v[2])), ops.map(lambda v: v[0].copy(width=max(v[0].width, v[1].width), height=max(v[0].height, v[1].height))), ops.distinct_until_changed())) bounds: RP[Bounds] = Bounded.bounds.pipe(lambda o: ( ops.combine_latest(o.observe("minimum_size")), ops.map(lambda v: v[0].copy(width=max(v[0].width, v[1].width), height=max(v[0].height, v[1].height))), ops.start_with(o.preferred_size))) 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) @property def context(self) -> Context: return self._context @property def ui(self) -> ComponentUI: return self._ui @property def look_and_feel(self) -> LookAndFeel: return self.context.look_and_feel def create_ui(self) -> ComponentUI: return self.context.look_and_feel.create_ui(self) def show(self) -> None: # noinspection PyTypeChecker self.visible = True def hide(self) -> None: # noinspection PyTypeChecker self.visible = False @property def valid(self) -> bool: return self._valid # noinspection PyTypeChecker def validate(self, force: bool = False) -> None: if self.visible and (not self.valid or force): self._minimum_size = self.ui.minimum_size(self) self._preferred_size = self.ui.preferred_size(self) self._valid = True self.parent.map(lambda p: p.request_layout()) def invalidate(self) -> None: self._valid = False self.parent.map(lambda p: p.invalidate()) def draw(self, g: Graphics) -> None: if self.visible: g.save() (dx, dy) = self.parent.map(lambda p: p.location).value_or(Point(0, 0)) (cx, cy, cw, ch) = self.ui.clip_bounds(self).tuple g.translate(dx, dy) g.rectangle(cx, cy, cw, ch) g.clip() try: self.draw_component(g) except BaseException as e: self.error_handler(e) g.restore() def draw_component(self, g: Graphics) -> None: self.ui.draw(g, self) def position_of(self, event: PositionalEvent) -> Point: if event is None: raise ValueError("Argument 'event' is required.") return event.position - self.offset @property def inputs(self) -> Mapping[str, Input]: return self.context.inputs @property def parent_dispatcher(self) -> Maybe[EventDispatcher]: # noinspection PyTypeChecker return self.parent def __repr__(self) -> Any: return str({"id": id(self), "type": type(self).__name__})