Skip to main content

SocketCan

Struct SocketCan 

Source
pub struct SocketCan { /* private fields */ }
Expand description

A CANopen transport over a Linux SocketCAN interface.

Implementations§

Source§

impl SocketCan

Source

pub fn open(interface: &str) -> Result<Self>

Open the named CAN interface (e.g. "can0" or "vcan0").

Examples found in repository?
examples/vcan_loopback.rs (line 56)
42    pub fn run() -> Result<(), Box<dyn Error>> {
43        let node = NodeId::new(0x10)?;
44
45        // --- Server node: serve an object dictionary on vcan0 in a thread. ---
46        let (ready_tx, ready_rx) = mpsc::channel::<()>();
47        thread::spawn(move || {
48            if let Err(e) = serve(node, ready_tx) {
49                eprintln!("server thread error: {e}");
50            }
51        });
52        // Wait until the server socket is open and listening.
53        ready_rx.recv().map_err(|_| "server failed to start")?;
54
55        // --- Client: talk to the node over vcan0. ---
56        let bus = SocketCan::open(IFACE)?;
57        bus.set_read_timeout(Duration::from_secs(2))?;
58
59        // Expedited read (4-byte object).
60        let device_type = bus.sdo_read(node, Address::new(0x1000, 0), DataType::Unsigned32)?;
61        println!("read  0x1000 device type      = {device_type:?}");
62        assert_eq!(device_type, Value::Unsigned32(0x0004_0192));
63
64        // Expedited write then read-back (2-byte object).
65        bus.sdo_write(node, Address::new(0x1017, 0), Value::Unsigned16(2500))?;
66        let heartbeat = bus.sdo_read(node, Address::new(0x1017, 0), DataType::Unsigned16)?;
67        println!("write 0x1017 heartbeat -> read = {heartbeat:?}");
68        assert_eq!(heartbeat, Value::Unsigned16(2500));
69
70        // 8-byte object forces SEGMENTED transfer over the real bus.
71        let big = Value::Unsigned64(0x0102_0304_0506_0708);
72        bus.sdo_write(node, Address::new(0x2000, 0), big)?;
73        let back = bus.sdo_read(node, Address::new(0x2000, 0), DataType::Unsigned64)?;
74        println!("write 0x2000 (segmented) -> read = {back:?}");
75        assert_eq!(back, big);
76
77        println!("\nvcan0 loopback OK — expedited and segmented SDO round-trips succeeded.");
78        Ok(())
79    }
80
81    /// A minimal device node: build an OD and answer SDO requests addressed to
82    /// this node until the bus goes quiet.
83    fn serve(node: NodeId, ready: mpsc::Sender<()>) -> Result<(), Box<dyn Error>> {
84        let bus = SocketCan::open(IFACE)?;
85        bus.set_read_timeout(Duration::from_secs(3))?;
86
87        let mut od = ObjectDictionary::<8>::new();
88        od.insert(
89            Address::new(0x1000, 0),
90            Entry::constant(Value::Unsigned32(0x0004_0192)),
91        )?;
92        od.insert(Address::new(0x1017, 0), Entry::rw(Value::Unsigned16(1000)))?;
93        od.insert(Address::new(0x2000, 0), Entry::rw(Value::Unsigned64(0)))?;
94
95        let mut server = SdoServer::new(node);
96        ready.send(()).map_err(|_| "client went away")?;
97
98        // Serve until a read times out (the client is done and the bus is idle).
99        while let Ok(frame) = bus.recv() {
100            if frame.cob_id != server.request_cob_id() {
101                continue;
102            }
103            if let Some(response) = server.handle(&mut od, frame.payload()) {
104                bus.send(server.response_cob_id(), &response)?;
105            }
106        }
107        Ok(())
108    }
Source

pub fn set_read_timeout(&self, timeout: Duration) -> Result<()>

Set a read timeout, so SocketCan::recv (and the SDO helpers) fail with a timeout error rather than blocking forever.

Examples found in repository?
examples/vcan_loopback.rs (line 57)
42    pub fn run() -> Result<(), Box<dyn Error>> {
43        let node = NodeId::new(0x10)?;
44
45        // --- Server node: serve an object dictionary on vcan0 in a thread. ---
46        let (ready_tx, ready_rx) = mpsc::channel::<()>();
47        thread::spawn(move || {
48            if let Err(e) = serve(node, ready_tx) {
49                eprintln!("server thread error: {e}");
50            }
51        });
52        // Wait until the server socket is open and listening.
53        ready_rx.recv().map_err(|_| "server failed to start")?;
54
55        // --- Client: talk to the node over vcan0. ---
56        let bus = SocketCan::open(IFACE)?;
57        bus.set_read_timeout(Duration::from_secs(2))?;
58
59        // Expedited read (4-byte object).
60        let device_type = bus.sdo_read(node, Address::new(0x1000, 0), DataType::Unsigned32)?;
61        println!("read  0x1000 device type      = {device_type:?}");
62        assert_eq!(device_type, Value::Unsigned32(0x0004_0192));
63
64        // Expedited write then read-back (2-byte object).
65        bus.sdo_write(node, Address::new(0x1017, 0), Value::Unsigned16(2500))?;
66        let heartbeat = bus.sdo_read(node, Address::new(0x1017, 0), DataType::Unsigned16)?;
67        println!("write 0x1017 heartbeat -> read = {heartbeat:?}");
68        assert_eq!(heartbeat, Value::Unsigned16(2500));
69
70        // 8-byte object forces SEGMENTED transfer over the real bus.
71        let big = Value::Unsigned64(0x0102_0304_0506_0708);
72        bus.sdo_write(node, Address::new(0x2000, 0), big)?;
73        let back = bus.sdo_read(node, Address::new(0x2000, 0), DataType::Unsigned64)?;
74        println!("write 0x2000 (segmented) -> read = {back:?}");
75        assert_eq!(back, big);
76
77        println!("\nvcan0 loopback OK — expedited and segmented SDO round-trips succeeded.");
78        Ok(())
79    }
80
81    /// A minimal device node: build an OD and answer SDO requests addressed to
82    /// this node until the bus goes quiet.
83    fn serve(node: NodeId, ready: mpsc::Sender<()>) -> Result<(), Box<dyn Error>> {
84        let bus = SocketCan::open(IFACE)?;
85        bus.set_read_timeout(Duration::from_secs(3))?;
86
87        let mut od = ObjectDictionary::<8>::new();
88        od.insert(
89            Address::new(0x1000, 0),
90            Entry::constant(Value::Unsigned32(0x0004_0192)),
91        )?;
92        od.insert(Address::new(0x1017, 0), Entry::rw(Value::Unsigned16(1000)))?;
93        od.insert(Address::new(0x2000, 0), Entry::rw(Value::Unsigned64(0)))?;
94
95        let mut server = SdoServer::new(node);
96        ready.send(()).map_err(|_| "client went away")?;
97
98        // Serve until a read times out (the client is done and the bus is idle).
99        while let Ok(frame) = bus.recv() {
100            if frame.cob_id != server.request_cob_id() {
101                continue;
102            }
103            if let Some(response) = server.handle(&mut od, frame.payload()) {
104                bus.send(server.response_cob_id(), &response)?;
105            }
106        }
107        Ok(())
108    }
Source

pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()>

Put the socket into (non-)blocking mode.

Source

pub fn send(&self, cob_id: u16, data: &[u8]) -> Result<()>

Transmit data on COB-ID cob_id as a standard data frame.

Examples found in repository?
examples/vcan_loopback.rs (line 104)
83    fn serve(node: NodeId, ready: mpsc::Sender<()>) -> Result<(), Box<dyn Error>> {
84        let bus = SocketCan::open(IFACE)?;
85        bus.set_read_timeout(Duration::from_secs(3))?;
86
87        let mut od = ObjectDictionary::<8>::new();
88        od.insert(
89            Address::new(0x1000, 0),
90            Entry::constant(Value::Unsigned32(0x0004_0192)),
91        )?;
92        od.insert(Address::new(0x1017, 0), Entry::rw(Value::Unsigned16(1000)))?;
93        od.insert(Address::new(0x2000, 0), Entry::rw(Value::Unsigned64(0)))?;
94
95        let mut server = SdoServer::new(node);
96        ready.send(()).map_err(|_| "client went away")?;
97
98        // Serve until a read times out (the client is done and the bus is idle).
99        while let Ok(frame) = bus.recv() {
100            if frame.cob_id != server.request_cob_id() {
101                continue;
102            }
103            if let Some(response) = server.handle(&mut od, frame.payload()) {
104                bus.send(server.response_cob_id(), &response)?;
105            }
106        }
107        Ok(())
108    }
Source

pub fn recv(&self) -> Result<Received>

Receive the next CANopen data frame, skipping remote and error frames and any frame with a 29-bit extended identifier.

Examples found in repository?
examples/vcan_loopback.rs (line 99)
83    fn serve(node: NodeId, ready: mpsc::Sender<()>) -> Result<(), Box<dyn Error>> {
84        let bus = SocketCan::open(IFACE)?;
85        bus.set_read_timeout(Duration::from_secs(3))?;
86
87        let mut od = ObjectDictionary::<8>::new();
88        od.insert(
89            Address::new(0x1000, 0),
90            Entry::constant(Value::Unsigned32(0x0004_0192)),
91        )?;
92        od.insert(Address::new(0x1017, 0), Entry::rw(Value::Unsigned16(1000)))?;
93        od.insert(Address::new(0x2000, 0), Entry::rw(Value::Unsigned64(0)))?;
94
95        let mut server = SdoServer::new(node);
96        ready.send(()).map_err(|_| "client went away")?;
97
98        // Serve until a read times out (the client is done and the bus is idle).
99        while let Ok(frame) = bus.recv() {
100            if frame.cob_id != server.request_cob_id() {
101                continue;
102            }
103            if let Some(response) = server.handle(&mut od, frame.payload()) {
104                bus.send(server.response_cob_id(), &response)?;
105            }
106        }
107        Ok(())
108    }
Source

pub fn sdo_read( &self, node: NodeId, addr: Address, data_type: DataType, ) -> Result<Value, SdoError>

Read object addr from node, interpreting the result as data_type.

Runs the full SDO upload transaction (expedited or segmented) and returns the value, or an SdoError on abort or I/O failure. Set a read timeout first so an unresponsive node cannot block forever.

Examples found in repository?
examples/vcan_loopback.rs (line 60)
42    pub fn run() -> Result<(), Box<dyn Error>> {
43        let node = NodeId::new(0x10)?;
44
45        // --- Server node: serve an object dictionary on vcan0 in a thread. ---
46        let (ready_tx, ready_rx) = mpsc::channel::<()>();
47        thread::spawn(move || {
48            if let Err(e) = serve(node, ready_tx) {
49                eprintln!("server thread error: {e}");
50            }
51        });
52        // Wait until the server socket is open and listening.
53        ready_rx.recv().map_err(|_| "server failed to start")?;
54
55        // --- Client: talk to the node over vcan0. ---
56        let bus = SocketCan::open(IFACE)?;
57        bus.set_read_timeout(Duration::from_secs(2))?;
58
59        // Expedited read (4-byte object).
60        let device_type = bus.sdo_read(node, Address::new(0x1000, 0), DataType::Unsigned32)?;
61        println!("read  0x1000 device type      = {device_type:?}");
62        assert_eq!(device_type, Value::Unsigned32(0x0004_0192));
63
64        // Expedited write then read-back (2-byte object).
65        bus.sdo_write(node, Address::new(0x1017, 0), Value::Unsigned16(2500))?;
66        let heartbeat = bus.sdo_read(node, Address::new(0x1017, 0), DataType::Unsigned16)?;
67        println!("write 0x1017 heartbeat -> read = {heartbeat:?}");
68        assert_eq!(heartbeat, Value::Unsigned16(2500));
69
70        // 8-byte object forces SEGMENTED transfer over the real bus.
71        let big = Value::Unsigned64(0x0102_0304_0506_0708);
72        bus.sdo_write(node, Address::new(0x2000, 0), big)?;
73        let back = bus.sdo_read(node, Address::new(0x2000, 0), DataType::Unsigned64)?;
74        println!("write 0x2000 (segmented) -> read = {back:?}");
75        assert_eq!(back, big);
76
77        println!("\nvcan0 loopback OK — expedited and segmented SDO round-trips succeeded.");
78        Ok(())
79    }
Source

pub fn sdo_write( &self, node: NodeId, addr: Address, value: Value, ) -> Result<(), SdoError>

Write value to object addr on node.

Runs the full SDO download transaction (expedited or segmented), choosing the transfer type from the value’s size.

Examples found in repository?
examples/vcan_loopback.rs (line 65)
42    pub fn run() -> Result<(), Box<dyn Error>> {
43        let node = NodeId::new(0x10)?;
44
45        // --- Server node: serve an object dictionary on vcan0 in a thread. ---
46        let (ready_tx, ready_rx) = mpsc::channel::<()>();
47        thread::spawn(move || {
48            if let Err(e) = serve(node, ready_tx) {
49                eprintln!("server thread error: {e}");
50            }
51        });
52        // Wait until the server socket is open and listening.
53        ready_rx.recv().map_err(|_| "server failed to start")?;
54
55        // --- Client: talk to the node over vcan0. ---
56        let bus = SocketCan::open(IFACE)?;
57        bus.set_read_timeout(Duration::from_secs(2))?;
58
59        // Expedited read (4-byte object).
60        let device_type = bus.sdo_read(node, Address::new(0x1000, 0), DataType::Unsigned32)?;
61        println!("read  0x1000 device type      = {device_type:?}");
62        assert_eq!(device_type, Value::Unsigned32(0x0004_0192));
63
64        // Expedited write then read-back (2-byte object).
65        bus.sdo_write(node, Address::new(0x1017, 0), Value::Unsigned16(2500))?;
66        let heartbeat = bus.sdo_read(node, Address::new(0x1017, 0), DataType::Unsigned16)?;
67        println!("write 0x1017 heartbeat -> read = {heartbeat:?}");
68        assert_eq!(heartbeat, Value::Unsigned16(2500));
69
70        // 8-byte object forces SEGMENTED transfer over the real bus.
71        let big = Value::Unsigned64(0x0102_0304_0506_0708);
72        bus.sdo_write(node, Address::new(0x2000, 0), big)?;
73        let back = bus.sdo_read(node, Address::new(0x2000, 0), DataType::Unsigned64)?;
74        println!("write 0x2000 (segmented) -> read = {back:?}");
75        assert_eq!(back, big);
76
77        println!("\nvcan0 loopback OK — expedited and segmented SDO round-trips succeeded.");
78        Ok(())
79    }

Trait Implementations§

Source§

impl Debug for SocketCan

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.