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 60)
46    pub fn run() -> Result<(), Box<dyn Error>> {
47        let node = NodeId::new(0x10)?;
48
49        // --- Server node: serve an object dictionary on vcan0 in a thread. ---
50        let (ready_tx, ready_rx) = mpsc::channel::<()>();
51        thread::spawn(move || {
52            if let Err(e) = serve(node, ready_tx) {
53                eprintln!("server thread error: {e}");
54            }
55        });
56        // Wait until the server socket is open and listening.
57        ready_rx.recv().map_err(|_| "server failed to start")?;
58
59        // --- Client: talk to the node over vcan0. ---
60        let bus = SocketCan::open(IFACE)?;
61        bus.set_read_timeout(Duration::from_secs(2))?;
62
63        // Expedited read (4-byte object).
64        let device_type = bus.sdo_read(node, Address::new(0x1000, 0), DataType::Unsigned32)?;
65        println!("read  0x1000 device type      = {device_type:?}");
66        assert_eq!(device_type, Value::Unsigned32(0x0004_0192));
67
68        // Expedited write then read-back (2-byte object).
69        bus.sdo_write(node, Address::new(0x1017, 0), Value::Unsigned16(2500))?;
70        let heartbeat = bus.sdo_read(node, Address::new(0x1017, 0), DataType::Unsigned16)?;
71        println!("write 0x1017 heartbeat -> read = {heartbeat:?}");
72        assert_eq!(heartbeat, Value::Unsigned16(2500));
73
74        // 8-byte object forces SEGMENTED transfer over the real bus.
75        let big = Value::Unsigned64(0x0102_0304_0506_0708);
76        bus.sdo_write(node, Address::new(0x2000, 0), big)?;
77        let back = bus.sdo_read(node, Address::new(0x2000, 0), DataType::Unsigned64)?;
78        println!("write 0x2000 (segmented) -> read = {back:?}");
79        assert_eq!(back, big);
80
81        // --- PDO: go operational, drive an RPDO in and a SYNC-triggered TPDO
82        //     out. RPDO1 (0x200+node) writes 0x6000/1; TPDO1 (0x180+node) maps
83        //     the same object, so a SYNC echoes back what the RPDO wrote. ---
84        let rpdo1 = 0x200 + node.raw() as u16;
85        let tpdo1 = 0x180 + node.raw() as u16;
86        bus.send_nmt(NmtCommand::StartRemoteNode, node)?; // -> operational
87        bus.send(rpdo1, &[0xCD, 0xAB])?; // RPDO1: 0x6000/1 <- 0xABCD
88        bus.send(SYNC_COB_ID, &[])?; // SYNC: node emits synchronous TPDOs
89
90        let tpdo = recv_cob(&bus, tpdo1)?;
91        println!("RPDO in 0xABCD -> SYNC -> TPDO out = {:02X?}", tpdo.data());
92        assert_eq!(tpdo.data(), &[0xCD, 0xAB]);
93
94        println!(
95            "\nvcan0 loopback OK — SDO (expedited + segmented), NMT, and PDO all round-tripped."
96        );
97        Ok(())
98    }
99
100    /// Receive frames until one arrives on `cob_id`.
101    fn recv_cob(bus: &SocketCan, cob_id: u16) -> Result<Received, Box<dyn Error>> {
102        loop {
103            let frame = bus.recv()?;
104            if frame.cob_id == cob_id {
105                return Ok(frame);
106            }
107        }
108    }
109
110    /// A device node: build an OD, configure a PDO pair, boot, and serve frames
111    /// (SDO, NMT, RPDO) plus SYNC-triggered TPDOs until the bus goes quiet.
112    fn serve(node_id: NodeId, ready: mpsc::Sender<()>) -> Result<(), Box<dyn Error>> {
113        let bus = SocketCan::open(IFACE)?;
114        bus.set_read_timeout(Duration::from_secs(3))?;
115
116        let mut od = ObjectDictionary::<8>::new();
117        od.insert(
118            Address::new(0x1000, 0),
119            Entry::constant(Value::Unsigned32(0x0004_0192)),
120        )?;
121        od.insert(Address::new(0x1017, 0), Entry::rw(Value::Unsigned16(1000)))?;
122        od.insert(Address::new(0x2000, 0), Entry::rw(Value::Unsigned64(0)))?;
123        od.insert(Address::new(0x6000, 1), Entry::rw(Value::Unsigned16(0)))?;
124
125        let mut node = Node::new(node_id, od);
126        // RPDO1 receives into 0x6000/1; TPDO1 transmits it back on SYNC.
127        node.add_rpdo(0x200 + node_id.raw() as u16, mapping(0x6000, 1, 16))?;
128        node.add_tpdo(
129            0x180 + node_id.raw() as u16,
130            mapping(0x6000, 1, 16),
131            TransmissionType::SynchronousAcyclic,
132        )?;
133        node.boot(); // enter pre-operational so SDO is served
134        ready.send(()).map_err(|_| "client went away")?;
135
136        // Serve until a read times out (the client is done and the bus is idle).
137        while let Ok(frame) = bus.recv() {
138            if let Some(tx) = node.on_frame(frame.cob_id, frame.data()) {
139                bus.send(tx.cob_id, tx.data())?;
140            }
141            if frame.cob_id == SYNC_COB_ID {
142                for tx in node.sync_tpdos() {
143                    bus.send(tx.cob_id, tx.data())?;
144                }
145            }
146        }
147        Ok(())
148    }
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 61)
46    pub fn run() -> Result<(), Box<dyn Error>> {
47        let node = NodeId::new(0x10)?;
48
49        // --- Server node: serve an object dictionary on vcan0 in a thread. ---
50        let (ready_tx, ready_rx) = mpsc::channel::<()>();
51        thread::spawn(move || {
52            if let Err(e) = serve(node, ready_tx) {
53                eprintln!("server thread error: {e}");
54            }
55        });
56        // Wait until the server socket is open and listening.
57        ready_rx.recv().map_err(|_| "server failed to start")?;
58
59        // --- Client: talk to the node over vcan0. ---
60        let bus = SocketCan::open(IFACE)?;
61        bus.set_read_timeout(Duration::from_secs(2))?;
62
63        // Expedited read (4-byte object).
64        let device_type = bus.sdo_read(node, Address::new(0x1000, 0), DataType::Unsigned32)?;
65        println!("read  0x1000 device type      = {device_type:?}");
66        assert_eq!(device_type, Value::Unsigned32(0x0004_0192));
67
68        // Expedited write then read-back (2-byte object).
69        bus.sdo_write(node, Address::new(0x1017, 0), Value::Unsigned16(2500))?;
70        let heartbeat = bus.sdo_read(node, Address::new(0x1017, 0), DataType::Unsigned16)?;
71        println!("write 0x1017 heartbeat -> read = {heartbeat:?}");
72        assert_eq!(heartbeat, Value::Unsigned16(2500));
73
74        // 8-byte object forces SEGMENTED transfer over the real bus.
75        let big = Value::Unsigned64(0x0102_0304_0506_0708);
76        bus.sdo_write(node, Address::new(0x2000, 0), big)?;
77        let back = bus.sdo_read(node, Address::new(0x2000, 0), DataType::Unsigned64)?;
78        println!("write 0x2000 (segmented) -> read = {back:?}");
79        assert_eq!(back, big);
80
81        // --- PDO: go operational, drive an RPDO in and a SYNC-triggered TPDO
82        //     out. RPDO1 (0x200+node) writes 0x6000/1; TPDO1 (0x180+node) maps
83        //     the same object, so a SYNC echoes back what the RPDO wrote. ---
84        let rpdo1 = 0x200 + node.raw() as u16;
85        let tpdo1 = 0x180 + node.raw() as u16;
86        bus.send_nmt(NmtCommand::StartRemoteNode, node)?; // -> operational
87        bus.send(rpdo1, &[0xCD, 0xAB])?; // RPDO1: 0x6000/1 <- 0xABCD
88        bus.send(SYNC_COB_ID, &[])?; // SYNC: node emits synchronous TPDOs
89
90        let tpdo = recv_cob(&bus, tpdo1)?;
91        println!("RPDO in 0xABCD -> SYNC -> TPDO out = {:02X?}", tpdo.data());
92        assert_eq!(tpdo.data(), &[0xCD, 0xAB]);
93
94        println!(
95            "\nvcan0 loopback OK — SDO (expedited + segmented), NMT, and PDO all round-tripped."
96        );
97        Ok(())
98    }
99
100    /// Receive frames until one arrives on `cob_id`.
101    fn recv_cob(bus: &SocketCan, cob_id: u16) -> Result<Received, Box<dyn Error>> {
102        loop {
103            let frame = bus.recv()?;
104            if frame.cob_id == cob_id {
105                return Ok(frame);
106            }
107        }
108    }
109
110    /// A device node: build an OD, configure a PDO pair, boot, and serve frames
111    /// (SDO, NMT, RPDO) plus SYNC-triggered TPDOs until the bus goes quiet.
112    fn serve(node_id: NodeId, ready: mpsc::Sender<()>) -> Result<(), Box<dyn Error>> {
113        let bus = SocketCan::open(IFACE)?;
114        bus.set_read_timeout(Duration::from_secs(3))?;
115
116        let mut od = ObjectDictionary::<8>::new();
117        od.insert(
118            Address::new(0x1000, 0),
119            Entry::constant(Value::Unsigned32(0x0004_0192)),
120        )?;
121        od.insert(Address::new(0x1017, 0), Entry::rw(Value::Unsigned16(1000)))?;
122        od.insert(Address::new(0x2000, 0), Entry::rw(Value::Unsigned64(0)))?;
123        od.insert(Address::new(0x6000, 1), Entry::rw(Value::Unsigned16(0)))?;
124
125        let mut node = Node::new(node_id, od);
126        // RPDO1 receives into 0x6000/1; TPDO1 transmits it back on SYNC.
127        node.add_rpdo(0x200 + node_id.raw() as u16, mapping(0x6000, 1, 16))?;
128        node.add_tpdo(
129            0x180 + node_id.raw() as u16,
130            mapping(0x6000, 1, 16),
131            TransmissionType::SynchronousAcyclic,
132        )?;
133        node.boot(); // enter pre-operational so SDO is served
134        ready.send(()).map_err(|_| "client went away")?;
135
136        // Serve until a read times out (the client is done and the bus is idle).
137        while let Ok(frame) = bus.recv() {
138            if let Some(tx) = node.on_frame(frame.cob_id, frame.data()) {
139                bus.send(tx.cob_id, tx.data())?;
140            }
141            if frame.cob_id == SYNC_COB_ID {
142                for tx in node.sync_tpdos() {
143                    bus.send(tx.cob_id, tx.data())?;
144                }
145            }
146        }
147        Ok(())
148    }
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 87)
46    pub fn run() -> Result<(), Box<dyn Error>> {
47        let node = NodeId::new(0x10)?;
48
49        // --- Server node: serve an object dictionary on vcan0 in a thread. ---
50        let (ready_tx, ready_rx) = mpsc::channel::<()>();
51        thread::spawn(move || {
52            if let Err(e) = serve(node, ready_tx) {
53                eprintln!("server thread error: {e}");
54            }
55        });
56        // Wait until the server socket is open and listening.
57        ready_rx.recv().map_err(|_| "server failed to start")?;
58
59        // --- Client: talk to the node over vcan0. ---
60        let bus = SocketCan::open(IFACE)?;
61        bus.set_read_timeout(Duration::from_secs(2))?;
62
63        // Expedited read (4-byte object).
64        let device_type = bus.sdo_read(node, Address::new(0x1000, 0), DataType::Unsigned32)?;
65        println!("read  0x1000 device type      = {device_type:?}");
66        assert_eq!(device_type, Value::Unsigned32(0x0004_0192));
67
68        // Expedited write then read-back (2-byte object).
69        bus.sdo_write(node, Address::new(0x1017, 0), Value::Unsigned16(2500))?;
70        let heartbeat = bus.sdo_read(node, Address::new(0x1017, 0), DataType::Unsigned16)?;
71        println!("write 0x1017 heartbeat -> read = {heartbeat:?}");
72        assert_eq!(heartbeat, Value::Unsigned16(2500));
73
74        // 8-byte object forces SEGMENTED transfer over the real bus.
75        let big = Value::Unsigned64(0x0102_0304_0506_0708);
76        bus.sdo_write(node, Address::new(0x2000, 0), big)?;
77        let back = bus.sdo_read(node, Address::new(0x2000, 0), DataType::Unsigned64)?;
78        println!("write 0x2000 (segmented) -> read = {back:?}");
79        assert_eq!(back, big);
80
81        // --- PDO: go operational, drive an RPDO in and a SYNC-triggered TPDO
82        //     out. RPDO1 (0x200+node) writes 0x6000/1; TPDO1 (0x180+node) maps
83        //     the same object, so a SYNC echoes back what the RPDO wrote. ---
84        let rpdo1 = 0x200 + node.raw() as u16;
85        let tpdo1 = 0x180 + node.raw() as u16;
86        bus.send_nmt(NmtCommand::StartRemoteNode, node)?; // -> operational
87        bus.send(rpdo1, &[0xCD, 0xAB])?; // RPDO1: 0x6000/1 <- 0xABCD
88        bus.send(SYNC_COB_ID, &[])?; // SYNC: node emits synchronous TPDOs
89
90        let tpdo = recv_cob(&bus, tpdo1)?;
91        println!("RPDO in 0xABCD -> SYNC -> TPDO out = {:02X?}", tpdo.data());
92        assert_eq!(tpdo.data(), &[0xCD, 0xAB]);
93
94        println!(
95            "\nvcan0 loopback OK — SDO (expedited + segmented), NMT, and PDO all round-tripped."
96        );
97        Ok(())
98    }
99
100    /// Receive frames until one arrives on `cob_id`.
101    fn recv_cob(bus: &SocketCan, cob_id: u16) -> Result<Received, Box<dyn Error>> {
102        loop {
103            let frame = bus.recv()?;
104            if frame.cob_id == cob_id {
105                return Ok(frame);
106            }
107        }
108    }
109
110    /// A device node: build an OD, configure a PDO pair, boot, and serve frames
111    /// (SDO, NMT, RPDO) plus SYNC-triggered TPDOs until the bus goes quiet.
112    fn serve(node_id: NodeId, ready: mpsc::Sender<()>) -> Result<(), Box<dyn Error>> {
113        let bus = SocketCan::open(IFACE)?;
114        bus.set_read_timeout(Duration::from_secs(3))?;
115
116        let mut od = ObjectDictionary::<8>::new();
117        od.insert(
118            Address::new(0x1000, 0),
119            Entry::constant(Value::Unsigned32(0x0004_0192)),
120        )?;
121        od.insert(Address::new(0x1017, 0), Entry::rw(Value::Unsigned16(1000)))?;
122        od.insert(Address::new(0x2000, 0), Entry::rw(Value::Unsigned64(0)))?;
123        od.insert(Address::new(0x6000, 1), Entry::rw(Value::Unsigned16(0)))?;
124
125        let mut node = Node::new(node_id, od);
126        // RPDO1 receives into 0x6000/1; TPDO1 transmits it back on SYNC.
127        node.add_rpdo(0x200 + node_id.raw() as u16, mapping(0x6000, 1, 16))?;
128        node.add_tpdo(
129            0x180 + node_id.raw() as u16,
130            mapping(0x6000, 1, 16),
131            TransmissionType::SynchronousAcyclic,
132        )?;
133        node.boot(); // enter pre-operational so SDO is served
134        ready.send(()).map_err(|_| "client went away")?;
135
136        // Serve until a read times out (the client is done and the bus is idle).
137        while let Ok(frame) = bus.recv() {
138            if let Some(tx) = node.on_frame(frame.cob_id, frame.data()) {
139                bus.send(tx.cob_id, tx.data())?;
140            }
141            if frame.cob_id == SYNC_COB_ID {
142                for tx in node.sync_tpdos() {
143                    bus.send(tx.cob_id, tx.data())?;
144                }
145            }
146        }
147        Ok(())
148    }
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 103)
101    fn recv_cob(bus: &SocketCan, cob_id: u16) -> Result<Received, Box<dyn Error>> {
102        loop {
103            let frame = bus.recv()?;
104            if frame.cob_id == cob_id {
105                return Ok(frame);
106            }
107        }
108    }
109
110    /// A device node: build an OD, configure a PDO pair, boot, and serve frames
111    /// (SDO, NMT, RPDO) plus SYNC-triggered TPDOs until the bus goes quiet.
112    fn serve(node_id: NodeId, ready: mpsc::Sender<()>) -> Result<(), Box<dyn Error>> {
113        let bus = SocketCan::open(IFACE)?;
114        bus.set_read_timeout(Duration::from_secs(3))?;
115
116        let mut od = ObjectDictionary::<8>::new();
117        od.insert(
118            Address::new(0x1000, 0),
119            Entry::constant(Value::Unsigned32(0x0004_0192)),
120        )?;
121        od.insert(Address::new(0x1017, 0), Entry::rw(Value::Unsigned16(1000)))?;
122        od.insert(Address::new(0x2000, 0), Entry::rw(Value::Unsigned64(0)))?;
123        od.insert(Address::new(0x6000, 1), Entry::rw(Value::Unsigned16(0)))?;
124
125        let mut node = Node::new(node_id, od);
126        // RPDO1 receives into 0x6000/1; TPDO1 transmits it back on SYNC.
127        node.add_rpdo(0x200 + node_id.raw() as u16, mapping(0x6000, 1, 16))?;
128        node.add_tpdo(
129            0x180 + node_id.raw() as u16,
130            mapping(0x6000, 1, 16),
131            TransmissionType::SynchronousAcyclic,
132        )?;
133        node.boot(); // enter pre-operational so SDO is served
134        ready.send(()).map_err(|_| "client went away")?;
135
136        // Serve until a read times out (the client is done and the bus is idle).
137        while let Ok(frame) = bus.recv() {
138            if let Some(tx) = node.on_frame(frame.cob_id, frame.data()) {
139                bus.send(tx.cob_id, tx.data())?;
140            }
141            if frame.cob_id == SYNC_COB_ID {
142                for tx in node.sync_tpdos() {
143                    bus.send(tx.cob_id, tx.data())?;
144                }
145            }
146        }
147        Ok(())
148    }
Source

pub fn send_nmt(&self, command: NmtCommand, target: NodeId) -> Result<()>

Send an NMT node-control command to target on COB-ID 0x000.

Use NodeId::BROADCAST to address every node at once — e.g. send_nmt(NmtCommand::StartRemoteNode, NodeId::BROADCAST).

Examples found in repository?
examples/vcan_loopback.rs (line 86)
46    pub fn run() -> Result<(), Box<dyn Error>> {
47        let node = NodeId::new(0x10)?;
48
49        // --- Server node: serve an object dictionary on vcan0 in a thread. ---
50        let (ready_tx, ready_rx) = mpsc::channel::<()>();
51        thread::spawn(move || {
52            if let Err(e) = serve(node, ready_tx) {
53                eprintln!("server thread error: {e}");
54            }
55        });
56        // Wait until the server socket is open and listening.
57        ready_rx.recv().map_err(|_| "server failed to start")?;
58
59        // --- Client: talk to the node over vcan0. ---
60        let bus = SocketCan::open(IFACE)?;
61        bus.set_read_timeout(Duration::from_secs(2))?;
62
63        // Expedited read (4-byte object).
64        let device_type = bus.sdo_read(node, Address::new(0x1000, 0), DataType::Unsigned32)?;
65        println!("read  0x1000 device type      = {device_type:?}");
66        assert_eq!(device_type, Value::Unsigned32(0x0004_0192));
67
68        // Expedited write then read-back (2-byte object).
69        bus.sdo_write(node, Address::new(0x1017, 0), Value::Unsigned16(2500))?;
70        let heartbeat = bus.sdo_read(node, Address::new(0x1017, 0), DataType::Unsigned16)?;
71        println!("write 0x1017 heartbeat -> read = {heartbeat:?}");
72        assert_eq!(heartbeat, Value::Unsigned16(2500));
73
74        // 8-byte object forces SEGMENTED transfer over the real bus.
75        let big = Value::Unsigned64(0x0102_0304_0506_0708);
76        bus.sdo_write(node, Address::new(0x2000, 0), big)?;
77        let back = bus.sdo_read(node, Address::new(0x2000, 0), DataType::Unsigned64)?;
78        println!("write 0x2000 (segmented) -> read = {back:?}");
79        assert_eq!(back, big);
80
81        // --- PDO: go operational, drive an RPDO in and a SYNC-triggered TPDO
82        //     out. RPDO1 (0x200+node) writes 0x6000/1; TPDO1 (0x180+node) maps
83        //     the same object, so a SYNC echoes back what the RPDO wrote. ---
84        let rpdo1 = 0x200 + node.raw() as u16;
85        let tpdo1 = 0x180 + node.raw() as u16;
86        bus.send_nmt(NmtCommand::StartRemoteNode, node)?; // -> operational
87        bus.send(rpdo1, &[0xCD, 0xAB])?; // RPDO1: 0x6000/1 <- 0xABCD
88        bus.send(SYNC_COB_ID, &[])?; // SYNC: node emits synchronous TPDOs
89
90        let tpdo = recv_cob(&bus, tpdo1)?;
91        println!("RPDO in 0xABCD -> SYNC -> TPDO out = {:02X?}", tpdo.data());
92        assert_eq!(tpdo.data(), &[0xCD, 0xAB]);
93
94        println!(
95            "\nvcan0 loopback OK — SDO (expedited + segmented), NMT, and PDO all round-tripped."
96        );
97        Ok(())
98    }
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 64)
46    pub fn run() -> Result<(), Box<dyn Error>> {
47        let node = NodeId::new(0x10)?;
48
49        // --- Server node: serve an object dictionary on vcan0 in a thread. ---
50        let (ready_tx, ready_rx) = mpsc::channel::<()>();
51        thread::spawn(move || {
52            if let Err(e) = serve(node, ready_tx) {
53                eprintln!("server thread error: {e}");
54            }
55        });
56        // Wait until the server socket is open and listening.
57        ready_rx.recv().map_err(|_| "server failed to start")?;
58
59        // --- Client: talk to the node over vcan0. ---
60        let bus = SocketCan::open(IFACE)?;
61        bus.set_read_timeout(Duration::from_secs(2))?;
62
63        // Expedited read (4-byte object).
64        let device_type = bus.sdo_read(node, Address::new(0x1000, 0), DataType::Unsigned32)?;
65        println!("read  0x1000 device type      = {device_type:?}");
66        assert_eq!(device_type, Value::Unsigned32(0x0004_0192));
67
68        // Expedited write then read-back (2-byte object).
69        bus.sdo_write(node, Address::new(0x1017, 0), Value::Unsigned16(2500))?;
70        let heartbeat = bus.sdo_read(node, Address::new(0x1017, 0), DataType::Unsigned16)?;
71        println!("write 0x1017 heartbeat -> read = {heartbeat:?}");
72        assert_eq!(heartbeat, Value::Unsigned16(2500));
73
74        // 8-byte object forces SEGMENTED transfer over the real bus.
75        let big = Value::Unsigned64(0x0102_0304_0506_0708);
76        bus.sdo_write(node, Address::new(0x2000, 0), big)?;
77        let back = bus.sdo_read(node, Address::new(0x2000, 0), DataType::Unsigned64)?;
78        println!("write 0x2000 (segmented) -> read = {back:?}");
79        assert_eq!(back, big);
80
81        // --- PDO: go operational, drive an RPDO in and a SYNC-triggered TPDO
82        //     out. RPDO1 (0x200+node) writes 0x6000/1; TPDO1 (0x180+node) maps
83        //     the same object, so a SYNC echoes back what the RPDO wrote. ---
84        let rpdo1 = 0x200 + node.raw() as u16;
85        let tpdo1 = 0x180 + node.raw() as u16;
86        bus.send_nmt(NmtCommand::StartRemoteNode, node)?; // -> operational
87        bus.send(rpdo1, &[0xCD, 0xAB])?; // RPDO1: 0x6000/1 <- 0xABCD
88        bus.send(SYNC_COB_ID, &[])?; // SYNC: node emits synchronous TPDOs
89
90        let tpdo = recv_cob(&bus, tpdo1)?;
91        println!("RPDO in 0xABCD -> SYNC -> TPDO out = {:02X?}", tpdo.data());
92        assert_eq!(tpdo.data(), &[0xCD, 0xAB]);
93
94        println!(
95            "\nvcan0 loopback OK — SDO (expedited + segmented), NMT, and PDO all round-tripped."
96        );
97        Ok(())
98    }
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 69)
46    pub fn run() -> Result<(), Box<dyn Error>> {
47        let node = NodeId::new(0x10)?;
48
49        // --- Server node: serve an object dictionary on vcan0 in a thread. ---
50        let (ready_tx, ready_rx) = mpsc::channel::<()>();
51        thread::spawn(move || {
52            if let Err(e) = serve(node, ready_tx) {
53                eprintln!("server thread error: {e}");
54            }
55        });
56        // Wait until the server socket is open and listening.
57        ready_rx.recv().map_err(|_| "server failed to start")?;
58
59        // --- Client: talk to the node over vcan0. ---
60        let bus = SocketCan::open(IFACE)?;
61        bus.set_read_timeout(Duration::from_secs(2))?;
62
63        // Expedited read (4-byte object).
64        let device_type = bus.sdo_read(node, Address::new(0x1000, 0), DataType::Unsigned32)?;
65        println!("read  0x1000 device type      = {device_type:?}");
66        assert_eq!(device_type, Value::Unsigned32(0x0004_0192));
67
68        // Expedited write then read-back (2-byte object).
69        bus.sdo_write(node, Address::new(0x1017, 0), Value::Unsigned16(2500))?;
70        let heartbeat = bus.sdo_read(node, Address::new(0x1017, 0), DataType::Unsigned16)?;
71        println!("write 0x1017 heartbeat -> read = {heartbeat:?}");
72        assert_eq!(heartbeat, Value::Unsigned16(2500));
73
74        // 8-byte object forces SEGMENTED transfer over the real bus.
75        let big = Value::Unsigned64(0x0102_0304_0506_0708);
76        bus.sdo_write(node, Address::new(0x2000, 0), big)?;
77        let back = bus.sdo_read(node, Address::new(0x2000, 0), DataType::Unsigned64)?;
78        println!("write 0x2000 (segmented) -> read = {back:?}");
79        assert_eq!(back, big);
80
81        // --- PDO: go operational, drive an RPDO in and a SYNC-triggered TPDO
82        //     out. RPDO1 (0x200+node) writes 0x6000/1; TPDO1 (0x180+node) maps
83        //     the same object, so a SYNC echoes back what the RPDO wrote. ---
84        let rpdo1 = 0x200 + node.raw() as u16;
85        let tpdo1 = 0x180 + node.raw() as u16;
86        bus.send_nmt(NmtCommand::StartRemoteNode, node)?; // -> operational
87        bus.send(rpdo1, &[0xCD, 0xAB])?; // RPDO1: 0x6000/1 <- 0xABCD
88        bus.send(SYNC_COB_ID, &[])?; // SYNC: node emits synchronous TPDOs
89
90        let tpdo = recv_cob(&bus, tpdo1)?;
91        println!("RPDO in 0xABCD -> SYNC -> TPDO out = {:02X?}", tpdo.data());
92        assert_eq!(tpdo.data(), &[0xCD, 0xAB]);
93
94        println!(
95            "\nvcan0 loopback OK — SDO (expedited + segmented), NMT, and PDO all round-tripped."
96        );
97        Ok(())
98    }

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.