Skip to main content

banc_host/
node.rs

1//! A banc node: any board speaking banc-icd over postcard-rpc USB.
2//!
3//! `Node` wraps discovery (nusb enumeration filtered by the rig config),
4//! the identify handshake, and access to the underlying `HostClient`.
5//! Consumers with their own ICDs call `client()` and use their endpoint
6//! types directly — banc never needs to know them.
7
8use crate::config::AssistantConfig;
9use banc_icd::node::Identity;
10use banc_icd::{IdentifyEndpoint, PROTOCOL_VERSION};
11use postcard_rpc::header::VarSeqKind;
12use postcard_rpc::host_client::HostClient;
13use postcard_rpc::standard_icd::{WireError, ERROR_PATH};
14
15pub struct Node {
16    client: HostClient<WireError>,
17    pub identity: Identity,
18}
19
20impl Node {
21    /// Enumerate USB, match this config entry, connect, and identify.
22    pub async fn connect(cfg: &AssistantConfig) -> anyhow::Result<Node> {
23        let serial = cfg.serial.clone();
24        let product = cfg.product.clone();
25        let name = cfg.name.clone();
26        let client = HostClient::try_new_raw_nusb(
27            move |d| {
28                let serial_ok = serial
29                    .as_deref()
30                    .is_none_or(|want| d.serial_number() == Some(want));
31                let product_ok = product
32                    .as_deref()
33                    .is_none_or(|want| d.product_string() == Some(want));
34                serial_ok && product_ok
35            },
36            ERROR_PATH,
37            8,
38            VarSeqKind::Seq2,
39        )
40        .map_err(|e| anyhow::anyhow!("connecting to node '{name}': {e}"))?;
41
42        let identity = client
43            .send_resp::<IdentifyEndpoint>(&())
44            .await
45            .map_err(|e| anyhow::anyhow!("identify on node '{}': {e:?}", cfg.name))?;
46        anyhow::ensure!(
47            identity.protocol_version == PROTOCOL_VERSION,
48            "node '{}' speaks banc protocol v{}, host expects v{PROTOCOL_VERSION}",
49            cfg.name,
50            identity.protocol_version,
51        );
52        Ok(Node { client, identity })
53    }
54
55    /// The raw postcard-rpc client, for consumer-defined endpoints/topics.
56    pub fn client(&self) -> &HostClient<WireError> {
57        &self.client
58    }
59
60    /// Ask the node to reset itself (firmware replies first, then reboots).
61    pub async fn reset(&self) -> anyhow::Result<()> {
62        self.client
63            .send_resp::<banc_icd::ResetEndpoint>(&())
64            .await
65            .map_err(|e| anyhow::anyhow!("reset: {e:?}"))?;
66        Ok(())
67    }
68}