ados_sdk/client/cloud.rs
1//! Cloud publish, records and offload advertisement calls.
2
3use super::*;
4
5impl PluginIpcClient {
6 /// Publish one message on this plugin's own cloud stream.
7 ///
8 /// The host forwards `{stream, payload}` to the cloud relay under the
9 /// caller's verified plugin id, which publishes it at QoS 0 on
10 /// `ados/{device_id}/plugin/{plugin_id}/{stream}`. `stream` must match
11 /// `[a-z0-9][a-z0-9._-]{0,63}` and `payload` is opaque bytes of at most
12 /// [`ados_protocol::cloud_publish::MAX_PAYLOAD`]. The stream
13 /// [`ados_protocol::cloud_publish::VISION_DETECTIONS_STREAM`] instead lands
14 /// on the core detection topic and additionally needs
15 /// `vision.detection.publish`. Gated on `cloud.publish`.
16 ///
17 /// The reply is `{ok: true}` once the relay queued the message (the lane is
18 /// lossy, so this is not broker delivery), or a `not_available` map while
19 /// the relay is down.
20 pub async fn cloud_publish(&self, stream: &str, payload: &[u8]) -> Result<Value, ClientError> {
21 let args = Value::Map(vec![
22 (Value::from("stream"), Value::from(stream)),
23 (Value::from("payload"), Value::Binary(payload.to_vec())),
24 ]);
25 Ok(self
26 .send_request("cloud.publish", "cloud.publish", args)
27 .await?
28 .args)
29 }
30
31 /// Upsert one record `{collection, key, data}` into this plugin's own cloud
32 /// records in the operator's account.
33 ///
34 /// `collection` must match `[a-z0-9_.-]{1,64}`, `key` is 1..=256 UTF-8
35 /// bytes, and `data` must be JSON-representable (the host re-encodes it as
36 /// JSON, at most [`ados_protocol::cloud_publish::MAX_PAYLOAD`]). The record's
37 /// subject is `device_id` when given (e.g. the drone a workstation wrote a
38 /// job for), otherwise this node. Gated on `cloud.records`.
39 ///
40 /// The reply is `{ok: true}` only once the cloud accepted the write, so this
41 /// call waits past the relay's record bound rather than the default request
42 /// timeout; a `not_available` map comes back while the relay is down.
43 pub async fn cloud_records_put(
44 &self,
45 collection: &str,
46 key: &str,
47 data: Value,
48 device_id: Option<&str>,
49 ) -> Result<Value, ClientError> {
50 let mut args = vec![
51 (Value::from("collection"), Value::from(collection)),
52 (Value::from("key"), Value::from(key)),
53 (Value::from("data"), data),
54 ];
55 if let Some(device_id) = device_id {
56 args.push((Value::from("device_id"), Value::from(device_id)));
57 }
58 let args = Value::Map(args);
59 let timeout = self
60 .request_timeout
61 .max(ados_protocol::cloud_publish::RECORD_REPLY_TIMEOUT + RECORD_REPLY_SLACK);
62 Ok(self
63 .send_request_within("cloud.records.put", "cloud.records", args, timeout)
64 .await?
65 .args)
66 }
67
68 /// Report the perception-offload link this plugin holds.
69 ///
70 /// The host writes it as the offload-link sidecar, which goes stale
71 /// [`ados_protocol::offload_link::OFFLOAD_LINK_STALE_MS`] (20 s) after the
72 /// last write, so a plugin keeps an active link counted by re-advertising at
73 /// least every ~10 s, and advertises `paired: false` as soon as the link
74 /// drops rather than waiting out the window. The optional strings must be
75 /// 1..=256 printable bytes. The host stamps `paired` only when `target`
76 /// accepts a TCP connection, and refuses the call while another plugin's
77 /// link is fresh. Gated on `vision.offload.advertise`. The reply is
78 /// `{ok: true, paired: <as stamped>}`.
79 pub async fn offload_advertise(
80 &self,
81 advert: &OffloadAdvertisement,
82 ) -> Result<Value, ClientError> {
83 let opt = |v: &Option<String>| v.as_deref().map_or(Value::Nil, Value::from);
84 let args = Value::Map(vec![
85 (Value::from("paired"), Value::Boolean(advert.paired)),
86 (
87 Value::from("bearer_acceptable"),
88 Value::Boolean(advert.bearer_acceptable),
89 ),
90 (Value::from("target"), opt(&advert.target)),
91 (Value::from("device_id"), opt(&advert.device_id)),
92 (Value::from("model_id"), opt(&advert.model_id)),
93 ]);
94 Ok(self
95 .send_request("offload.advertise", "vision.offload.advertise", args)
96 .await?
97 .args)
98 }
99}