1use crate::frame::CanFdFrame;
20use crate::mode::{HomeState, Mode};
21use crate::multiplex::{parse_frame, Subframe, Value, WriteCanData, WriteCombiner};
22use crate::register::Register;
23use crate::resolution::Resolution;
24use crate::scaling;
25
26pub const MAX_EXTRA: usize = 16;
28
29#[non_exhaustive]
52#[derive(Debug, Clone)]
53pub struct QueryFormat {
54 pub mode: Resolution,
55 pub position: Resolution,
56 pub velocity: Resolution,
57 pub torque: Resolution,
58 pub q_current: Resolution,
59 pub d_current: Resolution,
60 pub abs_position: Resolution,
61 pub power: Resolution,
62 pub motor_temperature: Resolution,
63 pub trajectory_complete: Resolution,
64 pub home_state: Resolution,
65 pub voltage: Resolution,
66 pub temperature: Resolution,
67 pub fault: Resolution,
68
69 pub aux1_gpio: Resolution,
70 pub aux2_gpio: Resolution,
71
72 pub aux1_pwm_input_period_us: Resolution,
73 pub aux1_pwm_input_duty_cycle: Resolution,
74 pub aux2_pwm_input_period_us: Resolution,
75 pub aux2_pwm_input_duty_cycle: Resolution,
76
77 pub extra: [(u16, Resolution); MAX_EXTRA],
81}
82
83impl Default for QueryFormat {
84 fn default() -> Self {
85 QueryFormat {
86 mode: Resolution::Int8,
87 position: Resolution::Float,
88 velocity: Resolution::Float,
89 torque: Resolution::Float,
90 q_current: Resolution::Ignore,
91 d_current: Resolution::Ignore,
92 abs_position: Resolution::Ignore,
93 power: Resolution::Ignore,
94 motor_temperature: Resolution::Ignore,
95 trajectory_complete: Resolution::Ignore,
96 home_state: Resolution::Ignore,
97 voltage: Resolution::Int8,
98 temperature: Resolution::Int8,
99 fault: Resolution::Int8,
100 aux1_gpio: Resolution::Ignore,
101 aux2_gpio: Resolution::Ignore,
102 aux1_pwm_input_period_us: Resolution::Ignore,
103 aux1_pwm_input_duty_cycle: Resolution::Ignore,
104 aux2_pwm_input_period_us: Resolution::Ignore,
105 aux2_pwm_input_duty_cycle: Resolution::Ignore,
106 extra: [(u16::MAX, Resolution::Ignore); MAX_EXTRA],
107 }
108 }
109}
110
111impl QueryFormat {
112 pub fn minimal() -> Self {
114 QueryFormat {
115 voltage: Resolution::Ignore,
116 temperature: Resolution::Ignore,
117 fault: Resolution::Ignore,
118 ..Default::default()
119 }
120 }
121
122 pub fn comprehensive() -> Self {
124 QueryFormat {
125 q_current: Resolution::Float,
126 d_current: Resolution::Float,
127 abs_position: Resolution::Float,
128 power: Resolution::Float,
129 motor_temperature: Resolution::Float,
130 trajectory_complete: Resolution::Int8,
131 home_state: Resolution::Int8,
132 ..Default::default()
133 }
134 }
135
136 pub fn extra_count(&self) -> usize {
138 self.extra
139 .iter()
140 .filter(|(_, r)| *r != Resolution::Ignore)
141 .count()
142 }
143
144 pub fn add_extra(&mut self, register: u16, resolution: Resolution) {
146 let slot = self
148 .extra
149 .iter()
150 .position(|(_, r)| *r == Resolution::Ignore);
151 if let Some(idx) = slot {
152 self.extra[idx] = (register, resolution);
153 let count = idx + 1;
154 for i in (1..count).rev() {
156 if self.extra[i].0 < self.extra[i - 1].0 {
157 self.extra.swap(i, i - 1);
158 } else {
159 break;
160 }
161 }
162 }
163 }
164
165 pub fn serialize(&self, frame: &mut CanFdFrame) -> u8 {
167 let mut writer = WriteCanData::new(frame);
168 let mut reply_size: u8 = 0;
169
170 {
172 let resolutions = [
173 self.mode,
174 self.position,
175 self.velocity,
176 self.torque,
177 self.q_current,
178 self.d_current,
179 self.abs_position,
180 self.power,
181 ];
182 let mut combiner = WriteCombiner::new(
183 0x10, Register::Mode.address(),
185 &resolutions,
186 );
187 for _ in 0..resolutions.len() {
188 combiner.maybe_write(&mut writer);
189 }
190 reply_size += combiner.reply_size();
191 }
192
193 {
195 let resolutions = [
196 self.motor_temperature,
197 self.trajectory_complete,
198 self.home_state,
199 self.voltage,
200 self.temperature,
201 self.fault,
202 ];
203 let mut combiner =
204 WriteCombiner::new(0x10, Register::MotorTemperature.address(), &resolutions);
205 for _ in 0..resolutions.len() {
206 combiner.maybe_write(&mut writer);
207 }
208 reply_size += combiner.reply_size();
209 }
210
211 {
213 let resolutions = [self.aux1_gpio, self.aux2_gpio];
214 let mut combiner =
215 WriteCombiner::new(0x10, Register::Aux1GpioStatus.address(), &resolutions);
216 for _ in 0..resolutions.len() {
217 combiner.maybe_write(&mut writer);
218 }
219 reply_size += combiner.reply_size();
220 }
221
222 {
224 let resolutions = [
225 self.aux1_pwm_input_period_us,
226 self.aux1_pwm_input_duty_cycle,
227 self.aux2_pwm_input_period_us,
228 self.aux2_pwm_input_duty_cycle,
229 ];
230 let mut combiner =
231 WriteCombiner::new(0x10, Register::Aux1PwmInputPeriod.address(), &resolutions);
232 for _ in 0..resolutions.len() {
233 combiner.maybe_write(&mut writer);
234 }
235 reply_size += combiner.reply_size();
236 }
237
238 let extra_count = self.extra_count();
241 if extra_count > 0 {
242 const MAX_GROUP_SPAN: usize = 64;
243 let mut group_start = 0;
244
245 while group_start < extra_count {
246 let base_reg = self.extra[group_start].0;
247 let mut group_end = group_start + 1;
248
249 while group_end < extra_count
250 && (self.extra[group_end].0 - base_reg) < MAX_GROUP_SPAN as u16
251 {
252 group_end += 1;
253 }
254
255 let last_reg = self.extra[group_end - 1].0;
256 let span = (last_reg - base_reg + 1) as usize;
257
258 let mut resolutions = [Resolution::Ignore; MAX_GROUP_SPAN];
259 for i in group_start..group_end {
260 let (reg, res) = self.extra[i];
261 resolutions[(reg - base_reg) as usize] = res;
262 }
263
264 let mut combiner = WriteCombiner::new(0x10, base_reg, &resolutions[..span]);
265 for _ in 0..span {
266 combiner.maybe_write(&mut writer);
267 }
268 reply_size += combiner.reply_size();
269
270 group_start = group_end;
271 }
272 }
273
274 reply_size
275 }
276}
277
278#[derive(Debug, Clone, Copy, Default)]
280pub struct ExtraValue {
281 pub register: u16,
283 pub value: f32,
285}
286
287#[non_exhaustive]
304#[derive(Debug, Clone, Default)]
305pub struct QueryResult {
306 pub mode: Mode,
307 pub position: f32,
308 pub velocity: f32,
309 pub torque: f32,
310 pub q_current: f32,
311 pub d_current: f32,
312 pub abs_position: f32,
313 pub power: f32,
314 pub motor_temperature: f32,
315 pub trajectory_complete: bool,
316 pub home_state: HomeState,
317 pub voltage: f32,
318 pub temperature: f32,
319 pub fault: i8,
320
321 pub aux1_gpio: i8,
322 pub aux2_gpio: i8,
323
324 pub aux1_pwm_input_period_us: i32,
325 pub aux1_pwm_input_duty_cycle: f32,
326 pub aux2_pwm_input_period_us: i32,
327 pub aux2_pwm_input_duty_cycle: f32,
328
329 pub extra: [Option<ExtraValue>; MAX_EXTRA],
331}
332
333impl QueryResult {
334 pub fn new() -> Self {
336 QueryResult {
337 mode: Mode::Stopped,
338 position: f32::NAN,
339 velocity: f32::NAN,
340 torque: f32::NAN,
341 q_current: f32::NAN,
342 d_current: f32::NAN,
343 abs_position: f32::NAN,
344 power: f32::NAN,
345 motor_temperature: f32::NAN,
346 trajectory_complete: false,
347 home_state: HomeState::Relative,
348 voltage: f32::NAN,
349 temperature: f32::NAN,
350 fault: 0,
351 aux1_gpio: 0,
352 aux2_gpio: 0,
353 aux1_pwm_input_period_us: 0,
354 aux1_pwm_input_duty_cycle: 0.0,
355 aux2_pwm_input_period_us: 0,
356 aux2_pwm_input_duty_cycle: 0.0,
357 extra: [None; MAX_EXTRA],
358 }
359 }
360
361 pub fn parse(frame: &CanFdFrame) -> Self {
363 Self::parse_data(&frame.data[..frame.size as usize])
364 }
365
366 pub fn parse_data(data: &[u8]) -> Self {
368 let mut result = QueryResult::new();
369
370 for subframe in parse_frame(data) {
371 let (register, value) = match subframe {
372 Subframe::Register {
373 register,
374 value: Some(value),
375 ..
376 } => (register, value),
377 _ => continue,
378 };
379
380 match register {
381 r if r == Register::Mode.address() => {
382 result.mode = Mode::try_from(value.to_i32() as u8).unwrap_or(Mode::Stopped);
383 }
384 r if r == Register::Position.address() => {
385 result.position = value.to_f32(&scaling::POSITION);
386 }
387 r if r == Register::Velocity.address() => {
388 result.velocity = value.to_f32(&scaling::VELOCITY);
389 }
390 r if r == Register::Torque.address() => {
391 result.torque = value.to_f32(&scaling::TORQUE);
392 }
393 r if r == Register::QCurrent.address() => {
394 result.q_current = value.to_f32(&scaling::CURRENT);
395 }
396 r if r == Register::DCurrent.address() => {
397 result.d_current = value.to_f32(&scaling::CURRENT);
398 }
399 r if r == Register::AbsPosition.address() => {
400 result.abs_position = value.to_f32(&scaling::POSITION);
401 }
402 r if r == Register::Power.address() => {
403 result.power = value.to_f32(&scaling::POWER);
404 }
405 r if r == Register::MotorTemperature.address() => {
406 result.motor_temperature = value.to_f32(&scaling::TEMPERATURE);
407 }
408 r if r == Register::TrajectoryComplete.address() => {
409 result.trajectory_complete = value.to_i32() != 0;
410 }
411 r if r == Register::HomeState.address() => {
412 result.home_state =
413 HomeState::try_from(value.to_i32() as u8).unwrap_or(HomeState::Relative);
414 }
415 r if r == Register::Voltage.address() => {
416 result.voltage = value.to_f32(&scaling::VOLTAGE);
417 }
418 r if r == Register::Temperature.address() => {
419 result.temperature = value.to_f32(&scaling::TEMPERATURE);
420 }
421 r if r == Register::Fault.address() => {
422 result.fault = value.to_i32() as i8;
423 }
424 r if r == Register::Aux1GpioStatus.address() => {
425 result.aux1_gpio = value.to_i32() as i8;
426 }
427 r if r == Register::Aux2GpioStatus.address() => {
428 result.aux2_gpio = value.to_i32() as i8;
429 }
430 r if r == Register::Aux1PwmInputPeriod.address() => {
431 result.aux1_pwm_input_period_us = value.to_i32();
432 }
433 r if r == Register::Aux1PwmInputDutyCycle.address() => {
434 result.aux1_pwm_input_duty_cycle = value.to_f32(&scaling::PWM);
435 }
436 r if r == Register::Aux2PwmInputPeriod.address() => {
437 result.aux2_pwm_input_period_us = value.to_i32();
438 }
439 r if r == Register::Aux2PwmInputDutyCycle.address() => {
440 result.aux2_pwm_input_duty_cycle = value.to_f32(&scaling::PWM);
441 }
442 _ => {
443 if let Some(slot) = result.extra.iter().position(|e| e.is_none()) {
444 result.extra[slot] = Some(ExtraValue {
445 register,
446 value: parse_generic(register, value),
447 });
448 } else {
449 debug_assert!(
450 false,
451 "MAX_EXTRA ({}) exceeded, register 0x{:x} dropped",
452 MAX_EXTRA, register
453 );
454 }
455 }
456 }
457 }
458
459 result
460 }
461
462 pub fn get_extra(&self, register: u16) -> Option<f32> {
464 for entry in &self.extra {
465 match entry {
466 Some(ev) if ev.register == register => return Some(ev.value),
467 None => return None,
468 _ => {}
469 }
470 }
471 None
472 }
473}
474
475fn parse_generic(register: u16, value: Value) -> f32 {
477 use crate::scaling;
478
479 let reg = Register::from_address(register);
481
482 let scaling = match reg {
483 Some(Register::Position)
484 | Some(Register::AbsPosition)
485 | Some(Register::CommandPosition)
486 | Some(Register::CommandStopPosition)
487 | Some(Register::CommandStayWithinLowerBound)
488 | Some(Register::CommandStayWithinUpperBound)
489 | Some(Register::ControlPosition)
490 | Some(Register::ControlPositionError)
491 | Some(Register::Encoder0Position)
492 | Some(Register::Encoder1Position)
493 | Some(Register::Encoder2Position) => &scaling::POSITION,
494
495 Some(Register::Velocity)
496 | Some(Register::CommandVelocity)
497 | Some(Register::CommandVelocityLimit)
498 | Some(Register::ControlVelocity)
499 | Some(Register::ControlVelocityError)
500 | Some(Register::Encoder0Velocity)
501 | Some(Register::Encoder1Velocity)
502 | Some(Register::Encoder2Velocity) => &scaling::VELOCITY,
503
504 Some(Register::Torque)
505 | Some(Register::CommandFeedforwardTorque)
506 | Some(Register::CommandPositionMaxTorque)
507 | Some(Register::ControlTorque)
508 | Some(Register::ControlTorqueError)
509 | Some(Register::PositionKp)
510 | Some(Register::PositionKi)
511 | Some(Register::PositionKd)
512 | Some(Register::PositionFeedforward)
513 | Some(Register::PositionCommand) => &scaling::TORQUE,
514
515 Some(Register::QCurrent)
516 | Some(Register::DCurrent)
517 | Some(Register::CommandQCurrent)
518 | Some(Register::CommandDCurrent)
519 | Some(Register::CommandFixedCurrentOverride) => &scaling::CURRENT,
520
521 Some(Register::Voltage)
522 | Some(Register::VoltagePhaseA)
523 | Some(Register::VoltagePhaseB)
524 | Some(Register::VoltagePhaseC)
525 | Some(Register::VFocVoltage)
526 | Some(Register::VoltageDqD)
527 | Some(Register::VoltageDqQ)
528 | Some(Register::CommandFixedVoltageOverride) => &scaling::VOLTAGE,
529
530 Some(Register::Temperature) | Some(Register::MotorTemperature) => &scaling::TEMPERATURE,
531
532 Some(Register::Power) => &scaling::POWER,
533
534 Some(Register::CommandTimeout) | Some(Register::CommandStayWithinTimeout) => &scaling::TIME,
535
536 Some(Register::CommandAccelLimit) => &scaling::ACCELERATION,
537
538 Some(Register::CommandKpScale)
539 | Some(Register::CommandKdScale)
540 | Some(Register::CommandIlimitScale)
541 | Some(Register::PwmPhaseA)
542 | Some(Register::PwmPhaseB)
543 | Some(Register::PwmPhaseC)
544 | Some(Register::Aux1PwmInputDutyCycle)
545 | Some(Register::Aux2PwmInputDutyCycle) => &scaling::PWM,
546
547 _ => &scaling::INT,
548 };
549
550 value.to_f32(scaling)
551}
552
553#[cfg(test)]
554mod tests {
555 use super::*;
556
557 #[test]
558 fn test_query_format_default() {
559 let format = QueryFormat::default();
560 assert_eq!(format.mode, Resolution::Int8);
561 assert_eq!(format.position, Resolution::Float);
562 assert_eq!(format.q_current, Resolution::Ignore);
563 }
564
565 fn bytes(frame: &CanFdFrame) -> &[u8] {
566 &frame.data[..frame.size as usize]
567 }
568
569 #[test]
570 fn test_query_format_serialize() {
571 let format = QueryFormat::default();
572 let mut frame = CanFdFrame::new();
573 let reply_size = format.serialize(&mut frame);
574
575 assert_eq!(reply_size, 22);
576 assert_eq!(bytes(&frame), &[0x11, 0x00, 0x1f, 0x01, 0x13, 0x0d]);
577 }
578
579 #[test]
580 fn test_query_format_serialize_with_extras() {
581 let mut format = QueryFormat::minimal();
582 format.add_extra(0x100, Resolution::Float);
583 format.add_extra(0x102, Resolution::Int16);
584 let mut frame = CanFdFrame::new();
585 let reply_size = format.serialize(&mut frame);
586
587 assert_eq!(reply_size, 29);
588 assert_eq!(
589 bytes(&frame),
590 &[0x11, 0x00, 0x1f, 0x01, 0x1d, 0x80, 0x02, 0x15, 0x82, 0x02]
591 );
592 }
593
594 #[test]
595 fn test_query_format_serialize_with_distant_extras() {
596 let mut format = QueryFormat::minimal();
598 format.add_extra(0x010, Resolution::Int32);
599 format.add_extra(0x100, Resolution::Float);
600 let mut frame = CanFdFrame::new();
601 let reply_size = format.serialize(&mut frame);
602
603 assert_eq!(reply_size, 30);
604 assert_eq!(
605 bytes(&frame),
606 &[0x11, 0x00, 0x1f, 0x01, 0x19, 0x10, 0x1d, 0x80, 0x02]
607 );
608 }
609
610 #[test]
611 fn test_query_result_parse() {
612 let data = [
614 0x21, 0x00, 0x0a, 0x2d, 0x01, 0x00, 0x00, 0x00, 0x3f, ];
621
622 let result = QueryResult::parse_data(&data);
623 assert_eq!(result.mode, Mode::Position);
624 assert!((result.position - 0.5).abs() < 0.001);
625 }
626
627 #[test]
628 fn test_query_result_parse_extras() {
629 let data = [
631 0x21, 0x00, 0x0a, 0x2d, 0x80, 0x02, 0x00, 0x00, 0xc8, 0x42, 0x25, 0x82, 0x02, 0x39, 0x30, ];
641
642 let result = QueryResult::parse_data(&data);
643 assert_eq!(result.mode, Mode::Position);
644 assert!(result.extra[0].is_some());
645 let e0 = result.extra[0].unwrap();
646 assert_eq!(e0.register, 0x100);
647 assert!((e0.value - 100.0).abs() < 0.001);
648 assert!(result.extra[1].is_some());
649 let e1 = result.extra[1].unwrap();
650 assert_eq!(e1.register, 0x102);
651 assert_eq!(e1.value as i32, 12345);
652 assert!(result.extra[2].is_none());
653
654 assert!((result.get_extra(0x100).unwrap() - 100.0).abs() < 0.001);
655 assert_eq!(result.get_extra(0x102).unwrap() as i32, 12345);
656 assert!(result.get_extra(0x999).is_none());
657 }
658}