#[derive(Clone, Copy, Debug, Default)]
pub struct RawControlWord(pub u16);
#[derive(Clone, Copy, Debug, Default)]
pub struct RawStatusWord(pub u16);
pub trait Cia402Control {
fn raw(&self) -> u16;
fn raw_mut(&mut self) -> &mut u16;
fn set_switch_on(&mut self, v: bool) {
self.set_bit(0, v);
}
fn set_enable_voltage(&mut self, v: bool) {
self.set_bit(1, v);
}
fn set_quick_stop(&mut self, v: bool) {
self.set_bit(2, v);
}
fn set_enable_operation(&mut self, v: bool) {
self.set_bit(3, v);
}
fn set_fault_reset(&mut self, v: bool) {
self.set_bit(7, v);
}
fn cmd_shutdown(&mut self) {
let w = self.raw_mut();
*w = (*w & !0x008F) | 0x0006; }
fn cmd_switch_on(&mut self) {
let w = self.raw_mut();
*w = (*w & !0x008F) | 0x0007; }
fn cmd_enable_operation(&mut self) {
let w = self.raw_mut();
*w = (*w & !0x008F) | 0x000F; }
fn cmd_disable_operation(&mut self) {
let w = self.raw_mut();
*w = (*w & !0x008F) | 0x0007; }
fn cmd_disable_voltage(&mut self) {
let w = self.raw_mut();
*w &= !0x0082; }
fn cmd_quick_stop(&mut self) {
let w = self.raw_mut();
*w = (*w & !0x0086) | 0x0002; }
fn cmd_fault_reset(&mut self) {
let w = self.raw_mut();
*w |= 0x0080; }
fn cmd_clear_fault_reset(&mut self) {
self.set_bit(7, false);
}
fn set_bit(&mut self, bit: u8, v: bool) {
let w = self.raw_mut();
if v {
*w |= 1 << bit;
} else {
*w &= !(1 << bit);
}
}
}
pub trait Cia402Status {
fn raw(&self) -> u16;
fn ready_to_switch_on(&self) -> bool {
self.raw() & (1 << 0) != 0
}
fn switched_on(&self) -> bool {
self.raw() & (1 << 1) != 0
}
fn operation_enabled(&self) -> bool {
self.raw() & (1 << 2) != 0
}
fn fault(&self) -> bool {
self.raw() & (1 << 3) != 0
}
fn voltage_enabled(&self) -> bool {
self.raw() & (1 << 4) != 0
}
fn quick_stop_active(&self) -> bool {
self.raw() & (1 << 5) != 0
}
fn switch_on_disabled(&self) -> bool {
self.raw() & (1 << 6) != 0
}
fn warning(&self) -> bool {
self.raw() & (1 << 7) != 0
}
fn remote(&self) -> bool {
self.raw() & (1 << 9) != 0
}
fn target_reached(&self) -> bool {
self.raw() & (1 << 10) != 0
}
fn state(&self) -> Cia402State {
let w = self.raw();
if w & 0x006F == 0x0027 { return Cia402State::OperationEnabled; }
if w & 0x006F == 0x0023 { return Cia402State::SwitchedOn; }
if w & 0x006F == 0x0021 { return Cia402State::ReadyToSwitchOn; }
if w & 0x006F == 0x0007 { return Cia402State::QuickStopActive; }
if w & 0x004F == 0x000F { return Cia402State::FaultReactionActive; }
if w & 0x004F == 0x0008 { return Cia402State::Fault; }
if w & 0x004F == 0x0040 { return Cia402State::SwitchOnDisabled; }
if w & 0x004F == 0x0000 { return Cia402State::NotReadyToSwitchOn; }
Cia402State::Unknown
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Cia402State {
NotReadyToSwitchOn,
SwitchOnDisabled,
ReadyToSwitchOn,
SwitchedOn,
OperationEnabled,
QuickStopActive,
FaultReactionActive,
Fault,
Unknown,
}
impl std::fmt::Display for Cia402State {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NotReadyToSwitchOn => write!(f, "Not Ready to Switch On"),
Self::SwitchOnDisabled => write!(f, "Switch On Disabled"),
Self::ReadyToSwitchOn => write!(f, "Ready to Switch On"),
Self::SwitchedOn => write!(f, "Switched On"),
Self::OperationEnabled => write!(f, "Operation Enabled"),
Self::QuickStopActive => write!(f, "Quick Stop Active"),
Self::FaultReactionActive => write!(f, "Fault Reaction Active"),
Self::Fault => write!(f, "Fault"),
Self::Unknown => write!(f, "Unknown"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i8)]
pub enum ModesOfOperation {
ProfilePosition = 1,
ProfileVelocity = 3,
Homing = 6,
InterpolatedPosition = 7,
CyclicSynchronousPosition = 8,
CyclicSynchronousVelocity = 9,
CyclicSynchronousTorque = 10,
}
impl ModesOfOperation {
pub fn from_i8(v: i8) -> Option<Self> {
match v {
1 => Some(Self::ProfilePosition),
3 => Some(Self::ProfileVelocity),
6 => Some(Self::Homing),
7 => Some(Self::InterpolatedPosition),
8 => Some(Self::CyclicSynchronousPosition),
9 => Some(Self::CyclicSynchronousVelocity),
10 => Some(Self::CyclicSynchronousTorque),
_ => None,
}
}
pub fn as_i8(self) -> i8 {
self as i8
}
}
impl std::fmt::Display for ModesOfOperation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ProfilePosition => write!(f, "Profile Position (PP)"),
Self::ProfileVelocity => write!(f, "Profile Velocity (PV)"),
Self::Homing => write!(f, "Homing"),
Self::InterpolatedPosition => write!(f, "Interpolated Position (IP)"),
Self::CyclicSynchronousPosition => write!(f, "Cyclic Synchronous Position (CSP)"),
Self::CyclicSynchronousVelocity => write!(f, "Cyclic Synchronous Velocity (CSV)"),
Self::CyclicSynchronousTorque => write!(f, "Cyclic Synchronous Torque (CST)"),
}
}
}
impl Cia402Control for RawControlWord {
fn raw(&self) -> u16 {
self.0
}
fn raw_mut(&mut self) -> &mut u16 {
&mut self.0
}
}
impl Cia402Status for RawStatusWord {
fn raw(&self) -> u16 {
self.0
}
}
pub trait PpControl: Cia402Control {
fn set_new_set_point(&mut self, v: bool) {
self.set_bit(4, v);
}
fn set_change_set_immediately(&mut self, v: bool) {
self.set_bit(5, v);
}
fn set_relative(&mut self, v: bool) {
self.set_bit(6, v);
}
fn set_halt(&mut self, v: bool) {
self.set_bit(8, v);
}
}
pub trait PpStatus: Cia402Status {
fn pp_target_reached(&self) -> bool {
self.raw() & (1 << 10) != 0
}
fn internal_limit(&self) -> bool {
self.raw() & (1 << 11) != 0
}
fn set_point_acknowledge(&self) -> bool {
self.raw() & (1 << 12) != 0
}
fn following_error(&self) -> bool {
self.raw() & (1 << 13) != 0
}
}
pub trait PvControl: Cia402Control {
fn set_halt(&mut self, v: bool) {
self.set_bit(8, v);
}
}
pub trait PvStatus: Cia402Status {
fn pv_target_reached(&self) -> bool {
self.raw() & (1 << 10) != 0
}
fn pv_internal_limit(&self) -> bool {
self.raw() & (1 << 11) != 0
}
fn speed_is_zero(&self) -> bool {
self.raw() & (1 << 12) != 0
}
fn max_slippage_error(&self) -> bool {
self.raw() & (1 << 13) != 0
}
}
pub trait HomingControl: Cia402Control {
fn set_homing_start(&mut self, v: bool) {
self.set_bit(4, v);
}
fn set_halt(&mut self, v: bool) {
self.set_bit(8, v);
}
}
pub trait HomingStatus: Cia402Status {
fn homing_target_reached(&self) -> bool {
self.raw() & (1 << 10) != 0
}
fn homing_attained(&self) -> bool {
self.raw() & (1 << 12) != 0
}
fn homing_error(&self) -> bool {
self.raw() & (1 << 13) != 0
}
}
impl PpControl for RawControlWord {}
impl PpStatus for RawStatusWord {}
impl PvControl for RawControlWord {}
impl PvStatus for RawStatusWord {}
impl HomingControl for RawControlWord {}
impl HomingStatus for RawStatusWord {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_state_decoding() {
assert_eq!(RawStatusWord(0x0000).state(), Cia402State::NotReadyToSwitchOn);
assert_eq!(RawStatusWord(0x0040).state(), Cia402State::SwitchOnDisabled);
assert_eq!(RawStatusWord(0x0021).state(), Cia402State::ReadyToSwitchOn);
assert_eq!(RawStatusWord(0x0023).state(), Cia402State::SwitchedOn);
assert_eq!(RawStatusWord(0x0027).state(), Cia402State::OperationEnabled);
assert_eq!(RawStatusWord(0x0007).state(), Cia402State::QuickStopActive);
assert_eq!(RawStatusWord(0x000F).state(), Cia402State::FaultReactionActive);
assert_eq!(RawStatusWord(0x0008).state(), Cia402State::Fault);
}
#[test]
fn test_state_decoding_bit5_dont_care() {
assert_eq!(RawStatusWord(0x0020).state(), Cia402State::NotReadyToSwitchOn);
assert_eq!(RawStatusWord(0x0060).state(), Cia402State::SwitchOnDisabled);
assert_eq!(RawStatusWord(0x002F).state(), Cia402State::FaultReactionActive);
assert_eq!(RawStatusWord(0x0028).state(), Cia402State::Fault);
}
#[test]
fn test_state_decoding_ignores_high_bits() {
assert_eq!(RawStatusWord(0xFF27).state(), Cia402State::OperationEnabled);
assert_eq!(RawStatusWord(0x8040).state(), Cia402State::SwitchOnDisabled);
}
#[test]
fn test_cmd_shutdown() {
let mut cw = RawControlWord(0xFF00);
cw.cmd_shutdown();
assert_eq!(cw.0, 0xFF06);
}
#[test]
fn test_cmd_enable_operation() {
let mut cw = RawControlWord(0x0000);
cw.cmd_enable_operation();
assert_eq!(cw.0, 0x000F);
}
#[test]
fn test_cmd_fault_reset() {
let mut cw = RawControlWord(0x0000);
cw.cmd_fault_reset();
assert!(cw.0 & 0x0080 != 0); }
#[test]
fn test_modes_of_operation_roundtrip() {
for mode in [
ModesOfOperation::ProfilePosition,
ModesOfOperation::ProfileVelocity,
ModesOfOperation::Homing,
] {
assert_eq!(ModesOfOperation::from_i8(mode.as_i8()), Some(mode));
}
assert_eq!(ModesOfOperation::from_i8(99), None);
}
}