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::nmt::{encode_command, NMT_COMMAND_COB_ID};
29use canopen_rs::object_dictionary::Address;
30use canopen_rs::sdo::{SdoClient, SdoEvent};
31use canopen_rs::transport::{cob_id, frame_from};
32use canopen_rs::types::NodeId;
33use canopen_rs::NmtCommand;
34
35#[derive(Debug, Clone)]
37pub struct Received {
38 pub cob_id: u16,
40 data: [u8; 8],
41 len: usize,
42}
43
44impl Received {
45 pub fn data(&self) -> &[u8] {
47 &self.data[..self.len]
48 }
49
50 pub fn payload(&self) -> &[u8; 8] {
53 &self.data
54 }
55}
56
57#[derive(Debug)]
59pub enum SdoError {
60 Io(io::Error),
62 Aborted(u32),
64 NoValue,
66}
67
68impl fmt::Display for SdoError {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 match self {
71 SdoError::Io(e) => write!(f, "SDO transport I/O error: {e}"),
72 SdoError::Aborted(code) => write!(f, "SDO transfer aborted (code {code:#010x})"),
73 SdoError::NoValue => f.write_str("SDO transfer completed without a value"),
74 }
75 }
76}
77
78impl std::error::Error for SdoError {
79 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
80 match self {
81 SdoError::Io(e) => Some(e),
82 _ => None,
83 }
84 }
85}
86
87impl From<io::Error> for SdoError {
88 fn from(e: io::Error) -> Self {
89 SdoError::Io(e)
90 }
91}
92
93#[derive(Debug)]
95pub struct SocketCan {
96 socket: CanSocket,
97}
98
99impl SocketCan {
100 pub fn open(interface: &str) -> io::Result<Self> {
102 Ok(Self {
103 socket: CanSocket::open(interface)?,
104 })
105 }
106
107 pub fn set_read_timeout(&self, timeout: Duration) -> io::Result<()> {
110 self.socket.set_read_timeout(timeout)
111 }
112
113 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
115 self.socket.set_nonblocking(nonblocking)
116 }
117
118 pub fn send(&self, cob_id: u16, data: &[u8]) -> io::Result<()> {
120 let frame: CanFrame = frame_from(cob_id, data).ok_or_else(|| {
121 io::Error::new(
122 io::ErrorKind::InvalidInput,
123 "invalid COB-ID or over-long data",
124 )
125 })?;
126 self.socket.write_frame(&frame)
127 }
128
129 pub fn recv(&self) -> io::Result<Received> {
132 loop {
133 let frame = self.socket.read_frame()?;
134 if !frame.is_data_frame() {
135 continue;
136 }
137 let Some(cob) = cob_id(&frame) else { continue };
138 let bytes = frame.data();
139 let mut data = [0u8; 8];
140 data[..bytes.len()].copy_from_slice(bytes);
141 return Ok(Received {
142 cob_id: cob,
143 data,
144 len: bytes.len(),
145 });
146 }
147 }
148
149 pub fn send_nmt(&self, command: NmtCommand, target: NodeId) -> io::Result<()> {
154 self.send(NMT_COMMAND_COB_ID, &encode_command(command, target))
155 }
156
157 fn recv_on(&self, cob_id: u16) -> io::Result<Received> {
159 loop {
160 let frame = self.recv()?;
161 if frame.cob_id == cob_id {
162 return Ok(frame);
163 }
164 }
165 }
166
167 pub fn sdo_read(
173 &self,
174 node: NodeId,
175 addr: Address,
176 data_type: DataType,
177 ) -> Result<Value, SdoError> {
178 let mut client = SdoClient::new(node);
179 let mut request = client.read(addr, data_type);
180 loop {
181 self.send(client.request_cob_id(), &request)?;
182 let reply = self.recv_on(client.response_cob_id())?;
183 match client.on_response(reply.payload()) {
184 SdoEvent::Send(next) => request = next,
185 SdoEvent::Complete(value) => return value.ok_or(SdoError::NoValue),
186 SdoEvent::Aborted(code) => return Err(SdoError::Aborted(code)),
187 }
188 }
189 }
190
191 pub fn sdo_write(&self, node: NodeId, addr: Address, value: Value) -> Result<(), SdoError> {
196 let mut client = SdoClient::new(node);
197 let mut request = client.write(addr, value);
198 loop {
199 self.send(client.request_cob_id(), &request)?;
200 let reply = self.recv_on(client.response_cob_id())?;
201 match client.on_response(reply.payload()) {
202 SdoEvent::Send(next) => request = next,
203 SdoEvent::Complete(_) => return Ok(()),
204 SdoEvent::Aborted(code) => return Err(SdoError::Aborted(code)),
205 }
206 }
207 }
208}