use crate::crc::Crc7;
use crate::result::{Error, Result};
use crate::{lib_bitfield, response};
mod voltage_accepted;
pub use voltage_accepted::*;
const COMMAND_INDEX: u8 = 0b00_1000;
lib_bitfield! {
pub R7(MSB0 [u8; 6]): u8 {
start_bit: 47;
direction: 46;
raw_command_index: 45, 40;
pub pcie_12v_support: 21;
pub pcie: 20;
raw_voltage_accepted: 19, 16;
pub echo_check: 15, 8;
raw_crc: 7, 1;
end_bit: 0;
}
}
response! {
R7 {
response_mode: Sd,
}
}
impl R7 {
pub const LEN: usize = 6;
pub const DEFAULT: [u8; Self::LEN] = [COMMAND_INDEX, 0, 0, 0x1, 0, 0x41];
pub const fn new() -> Self {
Self(Self::DEFAULT)
}
pub const fn command_index(&self) -> u8 {
self.raw_command_index()
}
pub const fn voltage_accepted(&self) -> Result<VoltageAccepted> {
VoltageAccepted::from_raw(self.raw_voltage_accepted())
}
pub fn set_voltage_accepted(&mut self, voltage_accepted: VoltageAccepted) {
self.set_raw_voltage_accepted(voltage_accepted.into_raw());
}
pub const fn crc(&self) -> Crc7 {
Crc7::from_bits(self.raw_crc())
}
pub fn calculate_crc(&mut self) -> Crc7 {
let crc = Crc7::calculate(self.0[..Self::LEN - 1].as_ref());
self.set_raw_crc(crc.bits());
crc
}
pub const fn try_from_bytes(val: &[u8]) -> Result<Self> {
match val.len() {
len if len < Self::LEN => Err(Error::invalid_length(len, Self::LEN)),
_ => {
let crc = Crc7::calculate(&[val[0], val[1], val[2], val[3], val[4]]);
match R7([val[0], val[1], val[2], val[3], val[4], val[5]]) {
r7 if r7.start_bit() => Err(Error::invalid_field_variant("r7::start_bit", 1)),
r7 if r7.direction() => Err(Error::invalid_field_variant("r7::direction", 1)),
r7 if r7.command_index() != COMMAND_INDEX => Err(Error::invalid_field_variant(
"r7::command_index",
r7.command_index() as usize,
)),
r7 if r7.voltage_accepted().is_err() => Err(Error::invalid_field_variant(
"r7::voltage_accepted",
r7.raw_voltage_accepted() as usize,
)),
r7 if r7.raw_crc() != crc.bits() => {
Err(Error::invalid_crc7(r7.raw_crc(), crc.bits()))
}
r7 if !r7.end_bit() => Err(Error::invalid_field_variant("r7::end_bit", 0)),
r7 => Ok(r7),
}
}
}
}
}
impl Default for R7 {
fn default() -> Self {
Self::new()
}
}
impl From<R7> for [u8; R7::LEN] {
fn from(val: R7) -> Self {
val.bytes()
}
}
impl TryFrom<&[u8]> for R7 {
type Error = Error;
fn try_from(val: &[u8]) -> Result<Self> {
Self::try_from_bytes(val)
}
}
impl<const N: usize> TryFrom<[u8; N]> for R7 {
type Error = Error;
fn try_from(val: [u8; N]) -> Result<Self> {
Self::try_from_bytes(val.as_ref())
}
}
impl<const N: usize> TryFrom<&[u8; N]> for R7 {
type Error = Error;
fn try_from(val: &[u8; N]) -> Result<Self> {
Self::try_from_bytes(val.as_ref())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_field;
use crate::util::raw_with_crc;
#[test]
fn test_fields() {
let mut r7 = R7::new();
assert!(!r7.pcie());
assert!(!r7.pcie_12v_support());
test_field!(r7, pcie { bit: 20, [u8; R7::LEN] });
test_field!(r7, pcie_12v_support { bit: 21, [u8; R7::LEN] });
assert_eq!(r7.voltage_accepted(), Ok(VoltageAccepted::new()));
assert_eq!(r7.echo_check(), 0);
(0..=u8::MAX).zip(0..=u8::MAX).for_each(|(flags, echo)| {
let (raw, _crc) = raw_with_crc([COMMAND_INDEX, 0, 0, flags, echo, 0]);
let raw_va = flags & 0xf;
match VoltageAccepted::from_raw(raw_va) {
Ok(va) => {
r7 = R7(raw);
assert_eq!(R7::try_from_bytes(raw.as_ref()), Ok(r7));
assert_eq!(R7::try_from(raw), Ok(r7));
assert_eq!(r7.bytes(), raw);
test_field!(r7, pcie { bit: 20, [u8; R7::LEN] });
test_field!(r7, pcie_12v_support { bit: 21, [u8; R7::LEN] });
assert_eq!(r7.voltage_accepted(), Ok(va));
r7.set_voltage_accepted(VoltageAccepted::new());
assert_eq!(r7.voltage_accepted(), Ok(VoltageAccepted::new()));
r7.set_voltage_accepted(va);
assert_eq!(r7.voltage_accepted(), Ok(va));
assert_eq!(r7.echo_check(), echo);
r7.set_echo_check(0);
assert_eq!(r7.echo_check(), 0);
r7.set_echo_check(echo);
assert_eq!(r7.echo_check(), echo);
}
Err(_) => {
assert_eq!(
R7::try_from_bytes(raw.as_ref()),
Err(Error::invalid_field_variant(
"r7::voltage_accepted",
raw_va as usize
))
);
assert_eq!(
R7::try_from(raw),
Err(Error::invalid_field_variant(
"r7::voltage_accepted",
raw_va as usize
))
);
}
}
});
}
}