use super::axis_view::AxisView;
use super::cia402::{
Cia402Control, Cia402Status, Cia402State,
PpControl, PpStatus,
HomingControl, HomingStatus,
RawControlWord, RawStatusWord,
ModesOfOperation,
};
pub use crate::ethercat::teknic::view::HomingProgress;
#[derive(Debug, Clone, Default)]
pub struct Cia402PpSnapshot {
pub control_word: u16,
pub target_position: i32,
pub profile_velocity: u32,
pub profile_acceleration: u32,
pub profile_deceleration: u32,
pub modes_of_operation: i8,
pub status_word: u16,
pub position_actual: i32,
pub velocity_actual: i32,
pub torque_actual: i16,
pub modes_of_operation_display: i8,
pub positive_limit: bool,
pub negative_limit: bool,
pub home_sensor: bool,
pub error_code: u16,
}
impl Cia402PpSnapshot {
pub fn pp_control(&self) -> RawControlWord {
RawControlWord(self.control_word)
}
pub fn pp_status(&self) -> RawStatusWord {
RawStatusWord(self.status_word)
}
pub fn set_pp_control(&mut self, cw: RawControlWord) {
self.control_word = cw.raw();
}
pub fn state(&self) -> Cia402State {
self.pp_status().state()
}
pub fn cmd_shutdown(&mut self) {
let mut cw = self.pp_control();
cw.cmd_shutdown();
self.set_pp_control(cw);
}
pub fn cmd_switch_on(&mut self) {
let mut cw = self.pp_control();
cw.cmd_switch_on();
self.set_pp_control(cw);
}
pub fn cmd_enable_operation(&mut self) {
let mut cw = self.pp_control();
cw.cmd_enable_operation();
self.set_pp_control(cw);
}
pub fn cmd_disable_operation(&mut self) {
let mut cw = self.pp_control();
cw.cmd_disable_operation();
self.set_pp_control(cw);
}
pub fn cmd_disable_voltage(&mut self) {
let mut cw = self.pp_control();
cw.cmd_disable_voltage();
self.set_pp_control(cw);
}
pub fn cmd_quick_stop(&mut self) {
let mut cw = self.pp_control();
cw.cmd_quick_stop();
self.set_pp_control(cw);
}
pub fn cmd_fault_reset(&mut self) {
let mut cw = self.pp_control();
cw.cmd_fault_reset();
self.set_pp_control(cw);
}
pub fn cmd_clear_fault_reset(&mut self) {
let mut cw = self.pp_control();
cw.cmd_clear_fault_reset();
self.set_pp_control(cw);
}
pub fn ensure_pp_mode(&mut self) {
self.modes_of_operation = ModesOfOperation::ProfilePosition.as_i8();
}
pub fn set_target(&mut self, position: i32, velocity: u32, accel: u32, decel: u32) {
self.target_position = position;
self.profile_velocity = velocity;
self.profile_acceleration = accel;
self.profile_deceleration = decel;
}
pub fn trigger_move(&mut self) {
let mut cw = self.pp_control();
cw.set_new_set_point(true);
self.set_pp_control(cw);
}
pub fn ack_set_point(&mut self) {
let mut cw = self.pp_control();
cw.set_new_set_point(false);
self.set_pp_control(cw);
}
pub fn set_halt(&mut self, v: bool) {
let mut cw = self.pp_control();
PpControl::set_halt(&mut cw, v);
self.set_pp_control(cw);
}
pub fn set_relative(&mut self, v: bool) {
let mut cw = self.pp_control();
cw.set_relative(v);
self.set_pp_control(cw);
}
pub fn target_reached(&self) -> bool {
self.pp_status().pp_target_reached()
}
pub fn set_point_acknowledged(&self) -> bool {
self.pp_status().set_point_acknowledge()
}
pub fn following_error(&self) -> bool {
self.pp_status().following_error()
}
pub fn internal_limit(&self) -> bool {
self.pp_status().internal_limit()
}
pub fn warning(&self) -> bool {
self.pp_status().warning()
}
pub fn is_faulted(&self) -> bool {
matches!(self.state(), Cia402State::Fault | Cia402State::FaultReactionActive)
}
pub fn ensure_homing_mode(&mut self) {
self.modes_of_operation = ModesOfOperation::Homing.as_i8();
}
pub fn trigger_homing(&mut self) {
let mut cw = RawControlWord(self.control_word);
cw.set_homing_start(true);
self.control_word = cw.raw();
}
pub fn clear_homing_start(&mut self) {
let mut cw = RawControlWord(self.control_word);
cw.set_homing_start(false);
self.control_word = cw.raw();
}
pub fn homing_progress(&self) -> HomingProgress {
let sw = RawStatusWord(self.status_word);
let attained = sw.homing_attained();
let reached = sw.homing_target_reached();
let error = sw.homing_error();
if error {
HomingProgress::Error
} else if attained && reached {
HomingProgress::Complete
} else if attained {
HomingProgress::Attained
} else if reached {
HomingProgress::Idle
} else {
HomingProgress::InProgress
}
}
pub fn position(&self) -> i32 {
self.position_actual
}
pub fn velocity(&self) -> i32 {
self.velocity_actual
}
pub fn torque(&self) -> i16 {
self.torque_actual
}
pub fn current_mode(&self) -> Option<ModesOfOperation> {
ModesOfOperation::from_i8(self.modes_of_operation_display)
}
}
impl AxisView for Cia402PpSnapshot {
fn control_word(&self) -> u16 { self.control_word }
fn set_control_word(&mut self, word: u16) { self.control_word = word; }
fn status_word(&self) -> u16 { self.status_word }
fn set_target_position(&mut self, pos: i32) { self.target_position = pos; }
fn set_profile_velocity(&mut self, vel: u32) { self.profile_velocity = vel; }
fn set_profile_acceleration(&mut self, accel: u32) { self.profile_acceleration = accel; }
fn set_profile_deceleration(&mut self, decel: u32) { self.profile_deceleration = decel; }
fn set_modes_of_operation(&mut self, mode: i8) { self.modes_of_operation = mode; }
fn modes_of_operation_display(&self) -> i8 { self.modes_of_operation_display }
fn position_actual(&self) -> i32 { self.position_actual }
fn velocity_actual(&self) -> i32 { self.velocity_actual }
fn positive_limit_active(&self) -> bool { self.positive_limit }
fn negative_limit_active(&self) -> bool { self.negative_limit }
fn home_sensor_active(&self) -> bool { self.home_sensor }
fn error_code(&self) -> u16 { self.error_code }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_snapshot_default() {
let s = Cia402PpSnapshot::default();
assert_eq!(s.control_word, 0);
assert_eq!(s.status_word, 0);
assert_eq!(s.target_position, 0);
}
#[test]
fn test_snapshot_state_machine() {
let mut s = Cia402PpSnapshot { status_word: 0x0040, ..Default::default() };
assert_eq!(s.state(), Cia402State::SwitchOnDisabled);
s.cmd_shutdown();
assert_eq!(s.control_word & 0x008F, 0x0006);
s.cmd_enable_operation();
assert_eq!(s.control_word & 0x008F, 0x000F);
}
#[test]
fn test_snapshot_pp_motion() {
let mut s = Cia402PpSnapshot::default();
s.cmd_enable_operation();
s.set_target(50_000, 10_000, 2_000, 2_000);
s.trigger_move();
assert_eq!(s.target_position, 50_000);
assert_eq!(s.profile_velocity, 10_000);
assert!(s.control_word & (1 << 4) != 0);
s.ack_set_point();
assert!(s.control_word & (1 << 4) == 0);
}
#[test]
fn test_snapshot_homing() {
let mut s = Cia402PpSnapshot::default();
s.ensure_homing_mode();
assert_eq!(s.modes_of_operation, 6);
s.cmd_enable_operation();
s.trigger_homing();
assert!(s.control_word & (1 << 4) != 0);
s.clear_homing_start();
assert!(s.control_word & (1 << 4) == 0);
}
#[test]
fn test_snapshot_homing_progress() {
let s = Cia402PpSnapshot { status_word: 0x1427, ..Default::default() };
assert_eq!(s.homing_progress(), HomingProgress::Complete);
let s = Cia402PpSnapshot { status_word: 0x2027, ..Default::default() };
assert_eq!(s.homing_progress(), HomingProgress::Error);
let s = Cia402PpSnapshot { status_word: 0x0027, ..Default::default() };
assert_eq!(s.homing_progress(), HomingProgress::InProgress);
}
#[test]
fn test_snapshot_axis_view() {
let mut s = Cia402PpSnapshot {
status_word: 0x0027,
position_actual: 5000,
velocity_actual: 1000,
modes_of_operation_display: 1,
..Default::default()
};
assert_eq!(AxisView::status_word(&s), 0x0027);
assert_eq!(AxisView::position_actual(&s), 5000);
assert_eq!(AxisView::velocity_actual(&s), 1000);
AxisView::set_control_word(&mut s, 0x000F);
assert_eq!(s.control_word, 0x000F);
AxisView::set_target_position(&mut s, 10_000);
assert_eq!(s.target_position, 10_000);
}
#[test]
fn test_snapshot_error_status() {
let s = Cia402PpSnapshot { status_word: 0x0008, ..Default::default() };
assert!(s.is_faulted());
let s = Cia402PpSnapshot { status_word: 0x2027, ..Default::default() };
assert!(s.following_error());
let s = Cia402PpSnapshot { status_word: 0x0827, ..Default::default() };
assert!(s.internal_limit());
}
}