Skip to main content

ados_sdk/client/
vision.rs

1//! Vision subscription, model, inference and detection calls.
2
3use super::*;
4
5impl PluginIpcClient {
6    /// Register a callback for `vision.deliver` frame pushes, keyed by the
7    /// camera id the subscriber filtered on (`None` ⇒ every camera, the
8    /// wildcard key). The reader loop decodes each delivered descriptor and
9    /// invokes the callbacks whose key is the descriptor's `camera_id` or the
10    /// wildcard, passing the envelope `args` map (which carries the
11    /// `descriptor` binary). Unlike events, `vision.deliver` carries no `topic`,
12    /// so it routes on the decoded descriptor's camera id, not the event map.
13    pub fn register_vision_callback(&self, camera_id: Option<&str>, callback: EventCallback) {
14        let key = camera_id.unwrap_or(VISION_ANY_CAMERA);
15        register_callback(&self.vision_callbacks, key, callback);
16    }
17
18    /// Register a callback for `vision.deliver_detection` pushes.
19    ///
20    /// `camera_id` of `None` receives every camera's batches. The engine fans
21    /// all cameras out on one stream, so the filter is applied here rather than
22    /// at the engine — the same posture the Python client takes.
23    pub fn register_detection_callback(&self, camera_id: Option<&str>, callback: EventCallback) {
24        let key = camera_id.unwrap_or(VISION_ANY_CAMERA);
25        register_callback(&self.detection_callbacks, key, callback);
26    }
27}
28
29impl PluginIpcClient {
30    /// Arm the host's detection-batch push stream for this connection.
31    pub async fn vision_subscribe_detections(
32        &self,
33        camera_id: Option<&str>,
34    ) -> Result<Value, ClientError> {
35        let args = Value::Map(vec![(
36            Value::from("camera_id"),
37            Value::from(camera_id.unwrap_or("")),
38        )]);
39        Ok(self
40            .send_request(
41                ados_protocol::framebus::methods::SUBSCRIBE_DETECTIONS,
42                "vision.detection.subscribe",
43                args,
44            )
45            .await?
46            .args)
47    }
48
49    /// Designate the tracker's locked target, overriding its auto-lock. Only
50    /// the target's box, label and confidence cross; the box is required. The
51    /// required cap is `vision.track.designate`.
52    pub async fn vision_designate_track(
53        &self,
54        camera_id: &str,
55        target: &ados_protocol::framebus::Detection,
56    ) -> Result<Value, ClientError> {
57        let args =
58            vision_rpc::designate_track_args(camera_id, target).map_err(vision_args_error)?;
59        Ok(self
60            .send_request(
61                ados_protocol::framebus::methods::DESIGNATE_TRACK,
62                "vision.track.designate",
63                args,
64            )
65            .await?
66            .args)
67    }
68
69    /// Read this plugin's resolved model-delivery status. Sends
70    /// [`framebus::methods::READ_MODEL`](ados_protocol::framebus::methods::READ_MODEL) (gated on `vision.model.read`); the host
71    /// returns `{models: [ {state, model_id, runtime, path, reason}, ... ]}` off
72    /// this plugin's install record. No args.
73    pub async fn vision_read_model(&self) -> Result<Value, ClientError> {
74        Ok(self
75            .send_request(
76                ados_protocol::framebus::methods::READ_MODEL,
77                "vision.model.read",
78                Value::Map(vec![]),
79            )
80            .await?
81            .args)
82    }
83}
84
85impl PluginIpcClient {
86    /// Subscribe to vision frame descriptors. The host starts (or widens) the
87    /// engine's frame stream toward this plugin and then delivers descriptors
88    /// as `vision.deliver` events. `camera_id` of `None` requests every
89    /// camera's frames; `Some(id)` narrows the stream at the engine. The
90    /// required cap is `vision.frame.read`. The caller registers the matching
91    /// frame callback via [`register_vision_callback`](Self::register_vision_callback);
92    /// `vision.deliver` pushes carry the descriptor directly (no topic), so they
93    /// do not ride the `event.subscribe` path.
94    pub async fn vision_subscribe_frames(
95        &self,
96        camera_id: Option<&str>,
97    ) -> Result<Value, ClientError> {
98        let mut entries = Vec::new();
99        if let Some(id) = camera_id {
100            entries.push((Value::from("camera_id"), Value::from(id)));
101        }
102        Ok(self
103            .send_request(
104                ados_protocol::framebus::methods::SUBSCRIBE_FRAMES,
105                "vision.frame.read",
106                Value::Map(entries),
107            )
108            .await?
109            .args)
110    }
111
112    /// Register an inference model. The required cap is
113    /// `vision.model.register`.
114    pub async fn vision_register_model(
115        &self,
116        model: &ados_protocol::framebus::ModelMetadata,
117    ) -> Result<Value, ClientError> {
118        let args = vision_rpc::register_model_args(model).map_err(vision_args_error)?;
119        Ok(self
120            .send_request(
121                ados_protocol::framebus::methods::REGISTER_MODEL,
122                "vision.model.register",
123                args,
124            )
125            .await?
126            .args)
127    }
128
129    /// Run a registered model against one frame, named by the descriptor the
130    /// engine published for it, on the shared backend and return the engine's
131    /// reply (decode it with [`vision_rpc::decode_infer_reply`]). The required
132    /// cap is `vision.model.register`.
133    pub async fn vision_infer(
134        &self,
135        model_id: &str,
136        frame: &ados_protocol::framebus::FrameDescriptor,
137    ) -> Result<Value, ClientError> {
138        let args = vision_rpc::infer_args(model_id, frame).map_err(vision_args_error)?;
139        Ok(self
140            .send_request(
141                ados_protocol::framebus::methods::INFER,
142                "vision.model.register",
143                args,
144            )
145            .await?
146            .args)
147    }
148
149    /// Publish a detection batch on `vision.detection`. The required cap is
150    /// `vision.detection.publish`.
151    pub async fn vision_publish_detection(
152        &self,
153        batch: &ados_protocol::framebus::DetectionBatch,
154    ) -> Result<Value, ClientError> {
155        let args = vision_rpc::publish_detection_args(batch).map_err(vision_args_error)?;
156        Ok(self
157            .send_request(
158                ados_protocol::framebus::methods::PUBLISH_DETECTION,
159                "vision.detection.publish",
160                args,
161            )
162            .await?
163            .args)
164    }
165}