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}