zencan_node/object_dict/
object_flags.rs

1use core::cell::UnsafeCell;
2
3use critical_section::Mutex;
4use zencan_common::AtomicCell;
5
6/// A struct used for synchronizing the A/B event flags of all objects, which are used for
7/// triggering PDO events
8#[derive(Debug)]
9pub struct ObjectFlagSync {
10    inner: Mutex<UnsafeCell<ObjectFlagsInner>>,
11}
12
13impl Default for ObjectFlagSync {
14    fn default() -> Self {
15        Self::new()
16    }
17}
18
19#[derive(Clone, Copy, Debug)]
20struct ObjectFlagsInner {
21    /// Indicates which "bank" of flags should be active for setting
22    toggle: bool,
23    /// A global flag that should be set by any object which has set a flag
24    global_flag: bool,
25}
26
27impl ObjectFlagSync {
28    /// Create a new ObjectFlagSync
29    pub const fn new() -> Self {
30        Self {
31            inner: Mutex::new(UnsafeCell::new(ObjectFlagsInner {
32                toggle: false,
33                global_flag: false,
34            })),
35        }
36    }
37
38    /// Toggle the flag and return the global flag
39    pub fn toggle(&self) -> bool {
40        critical_section::with(|cs| {
41            let inner = self.inner.borrow(cs).get();
42            // Safety: This is the only place inner is accessed, and it is in a critical section
43            unsafe {
44                let global = (*inner).global_flag;
45                (*inner).global_flag = false;
46                (*inner).toggle = !(*inner).toggle;
47                global
48            }
49        })
50    }
51
52    /// Get the current value of the flag
53    ///
54    /// `setting` should be true to set the global flag
55    pub fn get_flag(&self, setting: bool) -> bool {
56        critical_section::with(|cs| {
57            let inner = unsafe { &mut (*self.inner.borrow(cs).get()) };
58            inner.global_flag |= setting;
59            inner.toggle
60        })
61    }
62}
63
64/// Stores an event flag for each sub object in an object
65///
66/// PDO transmission can be triggered by events, but PDOs are runtime configurable. An application
67/// needs to be able to signal that an object has changed, and if that object is mapped to a TPDO,
68/// that PDO should be scheduled for transmission.
69///
70/// In order to achieve this in a synchronized way without long critical sections, each object
71/// holds two sets of flags, and they are swapped atomically using a global `ObjectFlagSync` shared by
72/// all `ObjectFlags` instances.
73#[allow(missing_debug_implementations)]
74pub struct ObjectFlags<const N: usize> {
75    sync: &'static ObjectFlagSync,
76    flags0: AtomicCell<[u8; N]>,
77    flags1: AtomicCell<[u8; N]>,
78}
79
80/// Trait for accessing object flags
81pub trait ObjectFlagAccess {
82    /// Set the flag for the specified sub object
83    ///
84    /// The flag is set on the currently active flag set
85    fn set_flag(&self, sub: u8);
86    /// Read the flag for the specified object
87    ///
88    /// The flag is read from the currently inactive flag set, i.e. the flag value from before the
89    /// last sync toggle is returned
90    fn get_flag(&self, sub: u8) -> bool;
91    /// Clear all flags in the currently active flag set
92    fn clear(&self);
93}
94
95impl<const N: usize> ObjectFlags<N> {
96    /// Create a new ObjectFlags
97    pub const fn new(sync: &'static ObjectFlagSync) -> Self {
98        Self {
99            sync,
100            flags0: AtomicCell::new([0; N]),
101            flags1: AtomicCell::new([0; N]),
102        }
103    }
104}
105
106impl<const N: usize> ObjectFlagAccess for ObjectFlags<N> {
107    fn set_flag(&self, sub: u8) {
108        if sub as usize >= N * 8 {
109            return;
110        }
111        let flags = if self.sync.get_flag(true) {
112            &self.flags0
113        } else {
114            &self.flags1
115        };
116        flags
117            .fetch_update(|mut f| {
118                f[sub as usize / 8] |= 1 << (sub & 7);
119                Some(f)
120            })
121            .unwrap();
122    }
123
124    fn get_flag(&self, sub: u8) -> bool {
125        if sub as usize >= N * 8 {
126            return false;
127        }
128        let flags = if self.sync.get_flag(false) {
129            &self.flags1.load()
130        } else {
131            &self.flags0.load()
132        };
133        flags[(sub / 8) as usize] & (1 << (sub & 7)) != 0
134    }
135
136    fn clear(&self) {
137        if self.sync.get_flag(false) {
138            self.flags1.store([0; N]);
139        } else {
140            self.flags0.store([0; N]);
141        }
142    }
143}