1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
//!
//! # Canadensis plug-and-play client
//!
//! This library implements the Cyphal plug-and-play node ID allocation protocol.
//!

#![no_std]
#![deny(missing_docs)]

extern crate alloc;

extern crate canadensis;
extern crate canadensis_data_types;
extern crate canadensis_filter_config;
extern crate crc_any;
extern crate heapless;

use canadensis::anonymous::AnonymousPublisher;
use canadensis::core::time::{milliseconds, Clock};
use canadensis::core::transport::{Receiver, Transmitter, Transport};
use canadensis::core::{Priority, SubjectId};
use canadensis::encoding::{Deserialize, Message, Serialize};
use canadensis_data_types::uavcan::pnp::node_id_allocation_data_1_0::{self, NodeIDAllocationData};
use core::convert::TryFrom;
use core::marker::PhantomData;
use crc_any::CRCu64;

/// A plug-and-play allocation client that can be used to find a node ID
pub struct PnpClient<C: Clock, M, T: Transmitter<C>, R: Receiver<C>> {
    /// The unique ID of this node
    unique_id: [u8; 16],
    /// Publisher used to send messages
    publisher: AnonymousPublisher<C, M, T>,
    /// Transmitter used along with the publisher to send messages
    transmitter: T,
    /// Receiver used to receive messages
    receiver: R,
    _message: PhantomData<M>,
}

impl<C, M, T, R, P> PnpClient<C, M, T, R>
where
    C: Clock,
    M: AllocationMessage<P>,
    T: Transmitter<C, Transport = P>,
    R: Receiver<C, Transport = P>,
    P: Transport,
{
    /// Creates a new plug-and-play client
    ///
    /// * `unique_id`: The unique ID of this node
    pub fn new(
        transmitter: T,
        mut receiver: R,
        unique_id: [u8; 16],
        driver: &mut R::Driver,
    ) -> Result<Self, R::Error> {
        receiver.subscribe_message(M::SUBJECT, 9, milliseconds(1000), driver)?;

        Ok(PnpClient {
            unique_id,
            publisher: AnonymousPublisher::new(
                M::SUBJECT,
                Priority::Nominal.into(),
                milliseconds(1000),
            ),
            transmitter,
            receiver,
            _message: PhantomData,
        })
    }

    /// Creates an outgoing node ID allocation message and gives it to the transmitter
    pub fn send_request(&mut self, clock: &mut C, driver: &mut T::Driver) {
        let message = M::with_unique_id(&self.unique_id);
        let status = self
            .publisher
            .send(&message, clock, &mut self.transmitter, driver);
        match status {
            Ok(()) => {}
            Err(_) => panic!("Can't fit transfer into one frame"),
        }
    }

    /// Handles an incoming frame and checks if it provides an ID for this node
    ///
    /// This function returns the node ID if one was assigned.
    pub fn receive(
        &mut self,
        clock: &mut C,
        driver: &mut R::Driver,
    ) -> Result<Option<P::NodeId>, R::Error> {
        if let Some(transfer_in) = self.receiver.receive(clock, driver)? {
            if let Ok(message) = M::deserialize_from_bytes(&transfer_in.payload) {
                if message.matches_unique_id(&self.unique_id) {
                    if let Some(node_id) = message.node_id() {
                        return Ok(Some(node_id));
                    }
                }
            }
        }
        Ok(None)
    }

    /// Returns a reference to the transmitter
    pub fn transmitter(&self) -> &T {
        &self.transmitter
    }
    /// Returns a mutable reference to the transmitter
    pub fn transmitter_mut(&mut self) -> &mut T {
        &mut self.transmitter
    }
    /// Returns a reference to the receiver
    pub fn receiver(&self) -> &R {
        &self.receiver
    }
    /// Returns a mutable reference to the receiver
    pub fn receiver_mut(&mut self) -> &mut R {
        &mut self.receiver
    }
}

/// A node ID allocation message
///
/// This is currently implemented for `uavcan.pnp.NodeIdAllocationData` version 1.0. In the future,
/// it may also be implemented for version 2.0 of that data type.
pub trait AllocationMessage<T: Transport>: Message + Serialize + Deserialize {
    /// The fixed subject ID for this message
    const SUBJECT: SubjectId;

    /// Creates a message with the provided unique ID and no allocated node ID
    ///
    /// The message must fit into one frame of the transport that is being used.
    fn with_unique_id(id: &[u8; 16]) -> Self;

    /// Determines if this message matches the provided unique ID
    fn matches_unique_id(&self, id: &[u8; 16]) -> bool;

    /// Returns the allocated node ID in this message, if one is specified
    fn node_id(&self) -> Option<T::NodeId>;
}

impl<T: Transport> AllocationMessage<T> for NodeIDAllocationData {
    const SUBJECT: SubjectId = node_id_allocation_data_1_0::SUBJECT;

    fn with_unique_id(id: &[u8; 16]) -> Self {
        let id_hash = crc_64we_48_bits(id);
        NodeIDAllocationData {
            unique_id_hash: id_hash,
            allocated_node_id: heapless::Vec::new(),
        }
    }

    fn matches_unique_id(&self, id: &[u8; 16]) -> bool {
        let id_hash = crc_64we_48_bits(id);
        self.unique_id_hash == id_hash
    }

    fn node_id(&self) -> Option<T::NodeId> {
        self.allocated_node_id.iter().next().and_then(|id| {
            // The message may allow a wider range of node IDs than the transport allows.
            // If the ID is too large, return None.
            T::NodeId::try_from(id.value).ok()
        })
    }
}

/// Calculates a CRC-64WE hash of the provided ID and returns the less significant 48 bits of the
/// result
fn crc_64we_48_bits(id: &[u8; 16]) -> u64 {
    let mut crc = CRCu64::crc64we();
    crc.digest(id);
    let value = crc.get_crc();
    value & 0x0000_ffff_ffff_ffff
}