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}