Skip to main content

ados_sdk/context/
network.rs

1//! `ctx.cloud`, `ctx.node` and `ctx.mdns`: the facades that reach past the
2//! plugin's sandbox to the cloud relay, the node facts and the local network.
3
4use std::collections::BTreeMap;
5use std::sync::Arc;
6
7use rmpv::Value;
8
9use crate::client::{ClientError, PluginIpcClient};
10
11/// `ctx.cloud` — this plugin's own cloud stream and cloud records.
12///
13/// Both go through the agent's cloud relay under the plugin's verified id, so a
14/// plugin only ever publishes into its own namespace. The relay being down is
15/// the resting state on an unpaired or offline node: both calls then answer a
16/// `not_available` map rather than an error.
17#[derive(Clone)]
18pub struct CloudClient {
19    pub(super) ipc: Arc<PluginIpcClient>,
20}
21
22impl CloudClient {
23    /// Publish one lossy message on the plugin's cloud stream `stream`. Needs
24    /// `cloud.publish`. See [`PluginIpcClient::cloud_publish`].
25    pub async fn publish(&self, stream: &str, payload: &[u8]) -> Result<Value, ClientError> {
26        self.ipc.cloud_publish(stream, payload).await
27    }
28
29    /// Upsert the record `key` in the plugin's cloud collection `collection`,
30    /// about `device_id` when given (else this node). Needs `cloud.records`.
31    /// See [`PluginIpcClient::cloud_records_put`].
32    pub async fn put_record(
33        &self,
34        collection: &str,
35        key: &str,
36        data: Value,
37        device_id: Option<&str>,
38    ) -> Result<Value, ClientError> {
39        self.ipc
40            .cloud_records_put(collection, key, data, device_id)
41            .await
42    }
43}
44
45/// `ctx.node` — facts about the node the plugin runs on.
46///
47/// The plugin's sandbox hides the agent's sidecars and config, so this is the
48/// sanctioned read of what the agent itself decides on: the profile, the board
49/// and its accelerators, the ground-station role, whether the camera pipeline
50/// is up and the main stream's geometry.
51#[derive(Clone)]
52pub struct NodeClient {
53    pub(super) ipc: Arc<PluginIpcClient>,
54}
55
56impl NodeClient {
57    /// Read the node facts. Needs `node.info.read`. Each fact whose source is
58    /// absent is `None`. See [`PluginIpcClient::node_info`].
59    pub async fn info(&self) -> Result<ados_protocol::node_info::NodeInfo, ClientError> {
60        self.ipc.node_info().await
61    }
62}
63
64/// `ctx.mdns` — find and offer services on the local network.
65///
66/// The plugin's sandbox cannot bind the mDNS port, so the host runs the
67/// responder: `advertise` publishes one of the plugin's declared listen ports
68/// under the node's hostname for as long as this connection lives, and
69/// `browse` collects the instances of one service type.
70#[derive(Clone)]
71pub struct MdnsClient {
72    pub(super) ipc: Arc<PluginIpcClient>,
73}
74
75impl MdnsClient {
76    /// Publish `port` as `service_type` with `txt`. Needs `network.listen`,
77    /// and the port must be a declared listen port of a service that runs on
78    /// this node. See [`PluginIpcClient::mdns_advertise`].
79    pub async fn advertise(
80        &self,
81        service_type: &str,
82        port: u16,
83        txt: &BTreeMap<String, String>,
84    ) -> Result<ados_protocol::plugin_mdns::Advertised, ClientError> {
85        self.ipc.mdns_advertise(service_type, port, txt).await
86    }
87
88    /// Every instance of `service_type` answering within `window`. Needs
89    /// `network.outbound`. See [`PluginIpcClient::mdns_browse`].
90    pub async fn browse(
91        &self,
92        service_type: &str,
93        window: std::time::Duration,
94    ) -> Result<Vec<ados_protocol::plugin_mdns::DiscoveredService>, ClientError> {
95        self.ipc.mdns_browse(service_type, window).await
96    }
97}