use crate::SubDeviceState;
use ethercrab_wire::{EtherCrabWireRead, EtherCrabWireSized};
#[derive(Debug, PartialEq)]
#[non_exhaustive]
pub struct TxRxResponse<const N: usize, T = ()> {
pub working_counter: u16,
pub subdevice_states: heapless::Vec<SubDeviceState, N>,
pub extra: T,
}
bitflags::bitflags! {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct GroupState: u8 {
const NONE = 0x00;
const INIT = 0x01;
const PRE_OP = 0x02;
const SAFE_OP = 0x04;
const OP = 0x08;
}
}
impl EtherCrabWireSized for GroupState {
const PACKED_LEN: usize = 1;
type Buffer = [u8; Self::PACKED_LEN];
fn buffer() -> Self::Buffer {
[0u8; Self::PACKED_LEN]
}
}
impl EtherCrabWireRead for GroupState {
fn unpack_from_slice(buf: &[u8]) -> Result<Self, ethercrab_wire::WireError> {
u8::unpack_from_slice(buf)
.and_then(|value| Self::from_bits(value).ok_or(ethercrab_wire::WireError::InvalidValue))
}
}
impl<const N: usize, T> TxRxResponse<N, T> {
pub fn group_state(&self) -> GroupState {
let bitmap = self
.subdevice_states
.iter()
.fold(0u8, |acc, state| acc | u8::from(*state));
GroupState::from_bits_truncate(bitmap)
}
pub fn group_in_single_state(&self) -> Option<SubDeviceState> {
let state = self.group_state();
if state.bits().count_ones() > 1 {
None
} else {
match state {
GroupState::NONE => Some(SubDeviceState::None),
GroupState::INIT => Some(SubDeviceState::Init),
GroupState::PRE_OP => Some(SubDeviceState::PreOp),
GroupState::SAFE_OP => Some(SubDeviceState::SafeOp),
GroupState::OP => Some(SubDeviceState::Op),
_ => None,
}
}
}
pub fn is_in_state(&self, desired_state: SubDeviceState) -> bool {
let state = self.group_state();
match desired_state {
SubDeviceState::None => state == GroupState::NONE,
SubDeviceState::Init => state == GroupState::INIT,
SubDeviceState::PreOp => state == GroupState::PRE_OP,
SubDeviceState::Bootstrap => false,
SubDeviceState::SafeOp => state == GroupState::SAFE_OP,
SubDeviceState::Op => state == GroupState::OP,
SubDeviceState::Other(n) => state.bits() == n,
}
}
pub fn all_op(&self) -> bool {
self.group_in_single_state()
.filter(|s| *s == SubDeviceState::Op)
.is_some()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_op() {
let all_op = TxRxResponse {
working_counter: 0,
subdevice_states: {
let mut v = heapless::Vec::<_, 3>::new();
let _ = v.push(SubDeviceState::Op);
let _ = v.push(SubDeviceState::Op);
let _ = v.push(SubDeviceState::Op);
v
},
extra: (),
};
let some_op = TxRxResponse {
working_counter: 0,
subdevice_states: {
let mut v = heapless::Vec::<_, 3>::new();
let _ = v.push(SubDeviceState::Op);
let _ = v.push(SubDeviceState::SafeOp);
let _ = v.push(SubDeviceState::Op);
v
},
extra: (),
};
assert!(all_op.is_in_state(SubDeviceState::Op));
assert!(!some_op.is_in_state(SubDeviceState::Op));
}
#[test]
fn none_state() {
let res = TxRxResponse {
working_counter: 0,
subdevice_states: {
let mut v = heapless::Vec::<_, 3>::new();
let _ = v.push(SubDeviceState::Init);
let _ = v.push(SubDeviceState::PreOp);
let _ = v.push(SubDeviceState::PreOp);
v
},
extra: (),
};
assert!(!res.is_in_state(SubDeviceState::SafeOp));
}
}