use super::{Display, Frame, From, ParseError};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(PartialEq, Debug, Display, From, Clone)]
#[repr(align(8))]
pub enum ControlPanel {
Buttons(Buttons),
Warmers(Warmers),
Knobs(Knobs),
}
impl TryFrom<Frame> for ControlPanel {
type Error = ParseError;
fn try_from(frame: Frame) -> Result<Self, Self::Error> {
match frame.id() {
0x2d3 => Ok(ControlPanel::Buttons(frame.try_into()?)),
0x2d4 => Ok(ControlPanel::Warmers(frame.try_into()?)),
0x273 => Ok(ControlPanel::Knobs(frame.try_into()?)),
_ => Err(ParseError::Id { frame }),
}
}
}
bitflags::bitflags! {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Display)]
pub struct Buttons: u64 {
const TRACTION_CONTROL = 0x07000000_00000001; const RADIO_POWER = 0x07000000_00000040; const AC = 0x07000000_00000100; const RECIRCULATION = 0x07000000_00000200; const VENT_MODE = 0x07000000_00000800; const HVAC_POWER = 0x07000000_00010000; const AUTO = 0x07000000_00020000; const DRIVER_TEMP_UP = 0x07000000_00040000; const DRIVER_TEMP_DOWN = 0x07000000_00080000; const PASSENGER_TEMP_UP = 0x07000000_00100000; const PASSENGER_TEMP_DOWN = 0x07000000_00200000; const REAR_DEFROSTER = 0x07000000_00400000; const FRONT_DEFROSTER = 0x07000000_00800000; const MUTE = 0x07000100_00000000; const SCREEN = 0x07002000_00000000; const ESS_MAX_REGEN = 0x07240000_00000000; }
}
impl TryFrom<Frame> for Buttons {
type Error = ParseError;
fn try_from(frame: Frame) -> Result<Self, Self::Error> {
const ID: u32 = 0x2d3;
const LEN: usize = 8;
if frame.id() != ID {
return Err(ParseError::Id { frame });
}
let data: [u8; LEN] = match frame.data().try_into() {
Ok(data) => data,
Err(_) => {
return Err(ParseError::Len {
frame,
expected: LEN,
})
}
};
match Self::from_bits(u64::from_be_bytes(data)) {
Some(flags) => Ok(flags),
None => Err(ParseError::Data {
frame,
detail: format!("A bit was set for `{}` that doesn't correspond to a flag: {:?}", stringify!(Buttons), &data),
}),
}
}
}
bitflags::bitflags! {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Display)]
#[repr(align(4))]
pub struct Warmers:u16 {
const DRIVER_BUTT = 0x0001; const PASSENGER_BUTT = 0x0010; const STEERING_WHEEL = 0x4000; }
}
impl TryFrom<Frame> for Warmers {
type Error = ParseError;
fn try_from(frame: Frame) -> Result<Self, Self::Error> {
const ID: u32 = 0x2d4;
const LEN: usize = 8;
if frame.id() != ID {
return Err(ParseError::Id { frame });
}
let data: [u8; LEN] = match frame.data().try_into() {
Ok(data) => data,
Err(_) => {
return Err(ParseError::Len {
frame,
expected: LEN,
})
}
};
match Self::from_bits(u16::from_be_bytes([data[1], data[2]])) {
Some(flags) => Ok(flags),
None => Err(ParseError::Data {
frame,
detail: format!("A bit was set for `{}` that doesn't correspond to a flag: {:?}", stringify!(Warmers), &data),
}),
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(PartialEq, Debug, Display, Clone)]
#[repr(align(4))]
pub enum Knobs {
FanDown,
FanUp,
}
impl TryFrom<Frame> for Knobs {
type Error = ParseError;
fn try_from(frame: Frame) -> Result<Self, Self::Error> {
const ID: u32 = 0x273;
const LEN: usize = 8;
if frame.id() != ID {
return Err(ParseError::Id { frame });
}
let data: [u8; LEN] = match frame.data().try_into() {
Ok(data) => data,
Err(_) => {
return Err(ParseError::Len {
frame,
expected: LEN,
})
}
};
match u64::from_be_bytes(data) {
0x00000A0000000000 => Ok(Knobs::FanDown), 0x0000050000000000 => Ok(Knobs::FanUp), 0x0000090000000000 => Ok(Knobs::FanUp), _ => Err(ParseError::Data {
frame,
detail: format!("Unrecognized value for `Knobs` ({:X}). Please report this.", u64::from_be_bytes(data)),
}),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hvac_radio_ess() {
let data = Buttons::TRACTION_CONTROL
.union(Buttons::MUTE)
.bits
.to_be_bytes();
let frame = Frame::from_id_data_len(0x2d3, data, 8).unwrap();
let parsed = Buttons::try_from(frame).unwrap();
assert!(parsed.contains(Buttons::TRACTION_CONTROL));
assert!(parsed.contains(Buttons::MUTE));
}
#[test]
fn test_warmers() {
let bytes = Warmers::DRIVER_BUTT.bits.to_be_bytes();
let frame = Frame::from_id_data_len(
0x2d4,
[0, bytes[0], bytes[1], 0, 0, 0, 0, 0],
8,
)
.unwrap();
let parsed = Warmers::try_from(frame).unwrap();
assert_eq!(parsed, Warmers::DRIVER_BUTT);
}
#[test]
fn test_buttons() {
let frame = Frame::from_id_data_len(
0x2d3,
Buttons::TRACTION_CONTROL
.union(Buttons::MUTE)
.bits
.to_be_bytes(),
8,
)
.unwrap();
if let ControlPanel::Buttons(pressed) =
ControlPanel::try_from(frame).unwrap()
{
assert!(pressed.contains(Buttons::TRACTION_CONTROL));
assert!(pressed.contains(Buttons::MUTE));
} else {
panic!("Buttons::try_from(frame: Frame) parsed incorrect id.");
}
let bytes = Warmers::DRIVER_BUTT.bits.to_be_bytes();
let frame = Frame::from_id_data_len(
0x2d4,
[0, bytes[0], bytes[1], 0, 0, 0, 0, 0],
8,
)
.unwrap();
if let ControlPanel::Warmers(pressed) =
ControlPanel::try_from(frame).unwrap()
{
assert!(pressed.contains(Warmers::DRIVER_BUTT));
assert!(!pressed.contains(Warmers::PASSENGER_BUTT));
} else {
panic!("Buttons::try_from(frame: Frame) parsed incorrect id.");
}
}
}