canopen_host/
transport.rs1use std::fmt;
21use std::io;
22use std::time::Duration;
23
24use embedded_can::Frame as _;
25use socketcan::{CanFrame, CanSocket, Socket};
26
27use canopen_rs::datatypes::{DataType, Value};
28use canopen_rs::object_dictionary::Address;
29use canopen_rs::sdo::{SdoClient, SdoEvent};
30use canopen_rs::transport::{cob_id, frame_from};
31use canopen_rs::types::NodeId;
32
33#[derive(Debug, Clone)]
35pub struct Received {
36 pub cob_id: u16,
38 data: [u8; 8],
39 len: usize,
40}
41
42impl Received {
43 pub fn data(&self) -> &[u8] {
45 &self.data[..self.len]
46 }
47
48 pub fn payload(&self) -> &[u8; 8] {
51 &self.data
52 }
53}
54
55#[derive(Debug)]
57pub enum SdoError {
58 Io(io::Error),
60 Aborted(u32),
62 NoValue,
64}
65
66impl fmt::Display for SdoError {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 match self {
69 SdoError::Io(e) => write!(f, "SDO transport I/O error: {e}"),
70 SdoError::Aborted(code) => write!(f, "SDO transfer aborted (code {code:#010x})"),
71 SdoError::NoValue => f.write_str("SDO transfer completed without a value"),
72 }
73 }
74}
75
76impl std::error::Error for SdoError {
77 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
78 match self {
79 SdoError::Io(e) => Some(e),
80 _ => None,
81 }
82 }
83}
84
85impl From<io::Error> for SdoError {
86 fn from(e: io::Error) -> Self {
87 SdoError::Io(e)
88 }
89}
90
91#[derive(Debug)]
93pub struct SocketCan {
94 socket: CanSocket,
95}
96
97impl SocketCan {
98 pub fn open(interface: &str) -> io::Result<Self> {
100 Ok(Self {
101 socket: CanSocket::open(interface)?,
102 })
103 }
104
105 pub fn set_read_timeout(&self, timeout: Duration) -> io::Result<()> {
108 self.socket.set_read_timeout(timeout)
109 }
110
111 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
113 self.socket.set_nonblocking(nonblocking)
114 }
115
116 pub fn send(&self, cob_id: u16, data: &[u8]) -> io::Result<()> {
118 let frame: CanFrame = frame_from(cob_id, data).ok_or_else(|| {
119 io::Error::new(
120 io::ErrorKind::InvalidInput,
121 "invalid COB-ID or over-long data",
122 )
123 })?;
124 self.socket.write_frame(&frame)
125 }
126
127 pub fn recv(&self) -> io::Result<Received> {
130 loop {
131 let frame = self.socket.read_frame()?;
132 if !frame.is_data_frame() {
133 continue;
134 }
135 let Some(cob) = cob_id(&frame) else { continue };
136 let bytes = frame.data();
137 let mut data = [0u8; 8];
138 data[..bytes.len()].copy_from_slice(bytes);
139 return Ok(Received {
140 cob_id: cob,
141 data,
142 len: bytes.len(),
143 });
144 }
145 }
146
147 fn recv_on(&self, cob_id: u16) -> io::Result<Received> {
149 loop {
150 let frame = self.recv()?;
151 if frame.cob_id == cob_id {
152 return Ok(frame);
153 }
154 }
155 }
156
157 pub fn sdo_read(
163 &self,
164 node: NodeId,
165 addr: Address,
166 data_type: DataType,
167 ) -> Result<Value, SdoError> {
168 let mut client = SdoClient::new(node);
169 let mut request = client.read(addr, data_type);
170 loop {
171 self.send(client.request_cob_id(), &request)?;
172 let reply = self.recv_on(client.response_cob_id())?;
173 match client.on_response(reply.payload()) {
174 SdoEvent::Send(next) => request = next,
175 SdoEvent::Complete(value) => return value.ok_or(SdoError::NoValue),
176 SdoEvent::Aborted(code) => return Err(SdoError::Aborted(code)),
177 }
178 }
179 }
180
181 pub fn sdo_write(&self, node: NodeId, addr: Address, value: Value) -> Result<(), SdoError> {
186 let mut client = SdoClient::new(node);
187 let mut request = client.write(addr, value);
188 loop {
189 self.send(client.request_cob_id(), &request)?;
190 let reply = self.recv_on(client.response_cob_id())?;
191 match client.on_response(reply.payload()) {
192 SdoEvent::Send(next) => request = next,
193 SdoEvent::Complete(_) => return Ok(()),
194 SdoEvent::Aborted(code) => return Err(SdoError::Aborted(code)),
195 }
196 }
197 }
198}