Skip to main content

autocore_std/fb/beckhoff/
el3356_view.rs

1//! Borrowed view into GlobalMemory for one Beckhoff EL3356 strain-gauge terminal.
2//!
3//! Groups the set of GM variables that map to the EL3356's EtherCAT process
4//! image into a single typed struct with lifetime-bound references. One
5//! `El3356View` is built per-tick via the [`el3356_view!`] macro, then passed
6//! to [`El3356::tick`](crate::fb::beckhoff::El3356::tick).
7//!
8//! # GM naming convention
9//!
10//! For a device with logical prefix `impact`, the control program's
11//! `project.json` should define these linked variables:
12//!
13//! | GM variable            | Type   | Direction | Maps to            |
14//! |------------------------|--------|-----------|--------------------|
15//! | `impact_load`          | `f32`  | input     | scaled load value  |
16//! | `impact_load_steady`   | `bool` | input     | steady-state flag  |
17//! | `impact_load_error`    | `bool` | input     | general error bit  |
18//! | `impact_load_overrange`| `bool` | input     | overrange bit      |
19//! | `impact_tare`          | `bool` | output    | tare command bit   |
20//!
21//! Use the [`el3356_view!`] macro to construct the view at the start of each
22//! control cycle — no hand-written borrowing is required.
23
24/// Borrowed view into GlobalMemory for one EL3356 terminal.
25///
26/// Holds mutable references to output (RxPDO) fields and shared references
27/// to input (TxPDO) fields. Construct via the [`el3356_view!`] macro.
28pub struct El3356View<'a> {
29    /// Tare command bit (output to device).
30    pub tare: &'a mut bool,
31
32    /// Current scaled load (input from device).
33    pub load: &'a f32,
34    /// Steady-state flag (input from device). `true` when the signal has
35    /// been stable within the configured band for long enough.
36    pub load_steady: &'a bool,
37    /// General error flag (input from device).
38    pub load_error: &'a bool,
39    /// Overrange flag (input from device).
40    pub load_overrange: &'a bool,
41}
42
43/// Create an [`El3356View`] by projecting GlobalMemory fields with a common prefix.
44///
45/// Fields must follow the naming convention `{prefix}_load`,
46/// `{prefix}_load_steady`, `{prefix}_load_error`, `{prefix}_load_overrange`,
47/// and `{prefix}_tare`. See the module docs for the full table.
48///
49/// # Example
50///
51/// ```ignore
52/// let mut view = el3356_view!(ctx.gm, impact);
53/// self.load_cell.tick(&mut view, ctx.client);
54/// ```
55#[macro_export]
56macro_rules! el3356_view {
57    ($gm:expr, $prefix:ident) => {
58        paste::paste! {
59            $crate::fb::beckhoff::El3356View {
60                tare:           &mut $gm.[<$prefix _tare>],
61                load:           & $gm.[<$prefix _load>],
62                load_steady:    & $gm.[<$prefix _load_steady>],
63                load_error:     & $gm.[<$prefix _load_error>],
64                load_overrange: & $gm.[<$prefix _load_overrange>],
65            }
66        }
67    };
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    /// Local storage for PDO fields used in tests. Avoids depending on
75    /// auto-generated GlobalMemory field names.
76    #[derive(Default)]
77    pub(crate) struct TestPdo {
78        pub tare: bool,
79        pub load: f32,
80        pub load_steady: bool,
81        pub load_error: bool,
82        pub load_overrange: bool,
83    }
84
85    impl TestPdo {
86        pub(crate) fn view(&mut self) -> El3356View<'_> {
87            El3356View {
88                tare:           &mut self.tare,
89                load:           &self.load,
90                load_steady:    &self.load_steady,
91                load_error:     &self.load_error,
92                load_overrange: &self.load_overrange,
93            }
94        }
95    }
96
97    #[test]
98    fn view_reads_inputs() {
99        let mut pdo = TestPdo { load: 42.5, load_steady: true, ..Default::default() };
100        let view = pdo.view();
101        assert_eq!(*view.load, 42.5);
102        assert!(*view.load_steady);
103        assert!(!*view.load_error);
104    }
105
106    #[test]
107    fn view_writes_tare() {
108        let mut pdo = TestPdo::default();
109        {
110            let mut view = pdo.view();
111            *view.tare = true;
112        }
113        assert!(pdo.tare);
114    }
115}