1use socketcan::{CanSocket, Socket, CanFrame, ExtendedId, EmbeddedFrame, CanError, Id};
2use std::error::Error;
3use std::time::Duration;
4use serde::{Serialize, Deserialize};
5pub struct PowerBoard {
6 socket: CanSocket,
7}
8
9
10fn arbitration_id(target_address: u8, comm_type: u16, reserved: u8) -> Option<ExtendedId> {
15 let arbitration_id = (target_address as u32 & 0xFF)
19 | ((comm_type as u32 & 0x1FFF) << 8)
20 | ((reserved as u32 & 0xFF) << 21);
21
22 ExtendedId::new(arbitration_id)
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct PowerBoardGeneralFrame {
33 pub battery_voltage: f32,
34 pub motor_voltage: f32,
35 pub current: f32,
36 pub fault_raw: u16,
37 pub power_chip_oc: bool,
38 pub power_chip_ot: bool,
39 pub power_chip_sc: bool,
40 pub sampling_oc: bool,
41 pub vbus_ov: bool,
42 pub vbus_uv: bool,
43 pub vmbus_ov: bool,
44 pub vmbus_uv: bool,
45 pub raw_data: Vec<u8>,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct PowerBoardLimbFrame {
55 pub left_leg_power: f32,
56 pub right_leg_power: f32,
57 pub left_arm_power: f32,
58 pub right_arm_power: f32,
59 pub raw_data: Vec<u8>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub enum PowerBoardFrame {
65 General(PowerBoardGeneralFrame), Limbs(PowerBoardLimbFrame), Unknown(u32, Vec<u8>),
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct ControlCommand {
72 pub fan: bool,
73 pub pre_charge: bool,
74 pub motor: bool,
75 pub main: bool,
76 pub restart: bool,
77 pub clear_fault: bool,
78 pub auto_report: bool,
79 pub reserved: bool,
80}
81
82impl PowerBoard {
83 pub fn new(interface: &str) -> Result<Self, Box<dyn Error>> {
84 let socket = CanSocket::open(interface)?;
85 socket.set_read_timeout(Duration::from_millis(100))?;
86 Ok(Self { socket })
87 }
88
89 pub fn configure_board(&self) -> Result<(), Box<dyn Error>> {
91 let command = ControlCommand {
92 fan: false,
93 pre_charge: true,
94 motor: true,
95 main: true,
96 restart: false,
97 clear_fault: false,
98 auto_report: true,
99 reserved: false,
100 };
101 self.send_control_frame(&command)?;
102 Ok(())
103 }
104
105 pub fn configure_and_clear_faults(&self) -> Result<(), Box<dyn Error>> {
106 let command = ControlCommand {
107 fan: false,
108 pre_charge: true,
109 motor: true,
110 main: true,
111 restart: false,
112 clear_fault: true,
113 auto_report: true,
114 reserved: false,
115 };
116 self.send_control_frame(&command)?;
117 Ok(())
118 }
119
120 pub fn send_control_frame(&self, command: &ControlCommand) -> Result<(), Box<dyn Error>> {
132 let target_address = 0xAA; let comm_type = 0x1001; let reserved = 0x00;
135
136 let id = arbitration_id(target_address, comm_type, reserved)
138 .ok_or("Invalid 29-bit ID for control frame")?;
139
140 let data = [command.fan as u8, command.pre_charge as u8, command.motor as u8, command.main as u8, command.restart as u8, command.clear_fault as u8, command.auto_report as u8, command.reserved as u8];
141
142 let frame = CanFrame::new(id, &data)
144 .ok_or("Failed to create 0x1001 control CAN frame")?;
145
146 self.socket.write_frame(&frame)?;
148 tracing::info!("Sent 0x1001 Control Frame with command: {:?}", command);
149 Ok(())
150 }
151
152 pub fn parse_power_board_message(frame: &CanFrame) -> Option<PowerBoardFrame> {
155 let id = frame.id();
157 if !matches!(id, Id::Extended(_)) {
158 return None;
160 }
161
162 let arbitration_id = match id {
164 Id::Extended(ext_id) => ext_id.as_raw(),
165 _ => return None,
166 };
167
168 let target_address = (arbitration_id & 0xFF) as u8;
172 let comm_type = ((arbitration_id >> 8) & 0x1FFF) as u16;
173 let _reserved = ((arbitration_id >> 21) & 0xFF) as u8;
174
175 let data = frame.data();
176 if data.len() < 8 {
177 return None;
179 }
180
181 match comm_type {
183 0x1003 => {
184 Some(PowerBoardFrame::General(parse_status_frame_1003(data)))
186 }
187 0x1004 => {
188 Some(PowerBoardFrame::Limbs(parse_limb_power_frame(data)))
190 }
191 _ => {
192 Some(PowerBoardFrame::Unknown(arbitration_id, data.to_vec()))
194 }
195 }
196 }
197
198 pub fn read_frame(&self) -> Result<Option<PowerBoardFrame>, Box<dyn Error>> {
200 let frame = match self.socket.read_frame() {
201 Ok(f) => f,
202 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
203 return Ok(None);
205 }
206 Err(e) => return Err(Box::new(e)),
207 };
208 Ok(Self::parse_power_board_message(&frame))
209 }
210}
211
212fn parse_status_frame_1003(data: &[u8]) -> PowerBoardGeneralFrame {
214 let battery_voltage_raw = ((data[0] as u16) << 8) | (data[1] as u16);
216 let battery_voltage = battery_voltage_raw as f32 / 100.0;
217
218 let motor_voltage_raw = ((data[2] as u16) << 8) | (data[3] as u16);
220 let motor_voltage = motor_voltage_raw as f32 / 100.0;
221
222 let current_raw = ((data[4] as u16) << 8) | (data[5] as u16);
224 let current = current_raw as f32 / 100.0;
225
226 let fault_raw = ((data[6] as u16) << 8) | (data[7] as u16);
228
229 PowerBoardGeneralFrame {
230 battery_voltage,
231 motor_voltage,
232 current,
233 fault_raw,
234 power_chip_oc: (fault_raw & (1 << 0)) != 0,
235 power_chip_ot: (fault_raw & (1 << 1)) != 0,
236 power_chip_sc: (fault_raw & (1 << 2)) != 0,
237 sampling_oc: (fault_raw & (1 << 3)) != 0,
238 vbus_ov: (fault_raw & (1 << 4)) != 0,
239 vbus_uv: (fault_raw & (1 << 5)) != 0,
240 vmbus_ov: (fault_raw & (1 << 6)) != 0,
241 vmbus_uv: (fault_raw & (1 << 7)) != 0,
242 raw_data: data.to_vec(),
243 }
244}
245
246fn parse_limb_power_frame(data: &[u8]) -> PowerBoardLimbFrame {
248 let left_leg_raw = ((data[0] as u16) << 8) | (data[1] as u16);
249 let left_leg_power = left_leg_raw as f32 / 100.0;
250
251 let right_leg_raw = ((data[2] as u16) << 8) | (data[3] as u16);
252 let right_leg_power = right_leg_raw as f32 / 100.0;
253
254 let left_arm_raw = ((data[4] as u16) << 8) | (data[5] as u16);
255 let left_arm_power = left_arm_raw as f32 / 100.0;
256
257 let right_arm_raw = ((data[6] as u16) << 8) | (data[7] as u16);
258 let right_arm_power = right_arm_raw as f32 / 100.0;
259
260 PowerBoardLimbFrame {
261 left_leg_power,
262 right_leg_power,
263 left_arm_power,
264 right_arm_power,
265 raw_data: data.to_vec(),
266 }
267}