1use std::collections::HashMap;
4
5use motor_codec::{BusCapabilities, MotorCodec, MotorTypeId};
6
7use crate::transport::CanBus;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct RouteKey {
12 pub group_name: String,
14 pub motor_index: usize,
16}
17
18pub struct Bus {
24 pub(crate) transport: Box<dyn CanBus>,
25 pub(crate) codec: Box<dyn MotorCodec>,
26 pub(crate) routes: HashMap<u32, RouteKey>,
27}
28
29impl Bus {
30 pub fn new(transport: Box<dyn CanBus>, mut codec: Box<dyn MotorCodec>) -> Self {
33 codec.bind_to_bus(transport.capabilities());
34 Self {
35 transport,
36 codec,
37 routes: HashMap::new(),
38 }
39 }
40
41 pub fn vendor(&self) -> &str {
43 self.codec.vendor_name()
44 }
45
46 pub fn capabilities(&self) -> BusCapabilities {
48 self.transport.capabilities()
49 }
50
51 pub fn codec_supports(&self, mt: MotorTypeId) -> bool {
53 self.codec.supports(mt)
54 }
55
56 pub fn routes(&self) -> &HashMap<u32, RouteKey> {
59 &self.routes
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use std::sync::atomic::{AtomicUsize, Ordering};
66 use std::sync::Arc;
67
68 use motor_codec::{
69 BusCapabilities, CanFrame, CodecError, Command, Event, Limits, MotorCodec, MotorRef,
70 MotorTypeId,
71 };
72
73 use super::*;
74 use crate::transport::MockCanBus;
75
76 pub(crate) struct CountingCodec {
78 pub binds: Arc<AtomicUsize>,
79 pub decodes: Arc<AtomicUsize>,
80 }
81 impl CountingCodec {
82 pub fn new() -> (Self, Arc<AtomicUsize>, Arc<AtomicUsize>) {
83 let b = Arc::new(AtomicUsize::new(0));
84 let d = Arc::new(AtomicUsize::new(0));
85 (
86 Self {
87 binds: b.clone(),
88 decodes: d.clone(),
89 },
90 b,
91 d,
92 )
93 }
94 }
95 impl MotorCodec for CountingCodec {
96 fn vendor_name(&self) -> &'static str {
97 "mock"
98 }
99 fn supports(&self, _: MotorTypeId) -> bool {
100 true
101 }
102 fn limits(&self, _: MotorTypeId) -> Result<Limits, CodecError> {
103 Ok(Limits {
104 p_max: 1.0,
105 v_max: 1.0,
106 t_max: 1.0,
107 })
108 }
109 fn bind_to_bus(&mut self, _: BusCapabilities) {
110 self.binds.fetch_add(1, Ordering::SeqCst);
111 }
112 fn encode_enable(&self, m: MotorRef<'_>) -> Result<CanFrame, CodecError> {
113 CanFrame::classical(m.send_id, &[0xFC])
114 .map_err(|_| CodecError::DecodeFailed { reason: "frame" })
115 }
116 fn encode_disable(&self, m: MotorRef<'_>) -> Result<CanFrame, CodecError> {
117 CanFrame::classical(m.send_id, &[0xFD])
118 .map_err(|_| CodecError::DecodeFailed { reason: "frame" })
119 }
120 fn encode_set_zero(&self, m: MotorRef<'_>) -> Result<CanFrame, CodecError> {
121 CanFrame::classical(m.send_id, &[0xFE])
122 .map_err(|_| CodecError::DecodeFailed { reason: "frame" })
123 }
124 fn encode_command(&self, m: MotorRef<'_>, _: &Command) -> Result<CanFrame, CodecError> {
125 CanFrame::classical(m.send_id, &[0x55])
126 .map_err(|_| CodecError::DecodeFailed { reason: "frame" })
127 }
128 fn decode(&self, frame: &CanFrame) -> Result<Option<Event>, CodecError> {
129 self.decodes.fetch_add(1, Ordering::SeqCst);
130 Ok(Some(Event::State {
131 motor_id: frame.id,
132 q: 0.0,
133 dq: 0.0,
134 tau: 0.0,
135 t_mos: 0,
136 t_rotor: 0,
137 }))
138 }
139 }
140
141 #[test]
142 fn bus_new_calls_bind_to_bus_once() {
143 let (codec, binds, _) = CountingCodec::new();
144 let _bus = Bus::new(Box::new(MockCanBus::new("m")), Box::new(codec));
145 assert_eq!(binds.load(Ordering::SeqCst), 1);
146 }
147
148 #[test]
149 fn bus_exposes_vendor_and_caps() {
150 let (codec, _, _) = CountingCodec::new();
151 let bus = Bus::new(Box::new(MockCanBus::new("m")), Box::new(codec));
152 assert_eq!(bus.vendor(), "mock");
153 assert_eq!(bus.capabilities(), BusCapabilities::classical());
154 }
155}