Skip to main content

pamoja_core/
adapt.rs

1//! Adapters that reshape what a sensor reads and what an actuator is told.
2//!
3//! A driver's reading is whatever the part measures, often several channels at once,
4//! and a driver's command is whatever the part accepts. The pieces that consume a
5//! sensor or drive an actuator usually want one plain value: a controller takes a
6//! single `f32`, a profile drives a `bool`. [`Map`] and [`MapCommand`] bridge the two
7//! without a wrapper type per part: [`Sensor::map`] selects or converts each reading
8//! as it is taken, and [`Actuator::map_command`] converts each command before it
9//! reaches the part.
10
11use core::marker::PhantomData;
12
13use crate::device::{Actuator, Sensor};
14use crate::error::Result;
15
16/// A sensor whose readings pass through a function before they are returned.
17///
18/// Created by [`Sensor::map`]. The function runs on every read, so it can select one
19/// channel of a multi-channel measurement, convert a unit, or fold several fields into
20/// one value.
21///
22/// # Examples
23///
24/// ```
25/// use pamoja_core::{Result, Sensor};
26///
27/// struct Climate;
28/// impl Sensor for Climate {
29///     type Reading = (f32, f32);
30///     async fn read(&mut self) -> Result<(f32, f32)> {
31///         Ok((21.5, 48.0))
32///     }
33/// }
34///
35/// # async fn run() -> Result<()> {
36/// let mut humidity = Climate.map(|(_, humidity)| humidity);
37/// assert_eq!(humidity.read().await?, 48.0);
38/// # Ok(())
39/// # }
40/// ```
41pub struct Map<S, F> {
42    inner: S,
43    select: F,
44}
45
46impl<S, F> Map<S, F> {
47    /// Wraps `inner` so each reading passes through `select`.
48    ///
49    /// # Arguments
50    ///
51    /// * `inner` - the sensor being adapted.
52    /// * `select` - the function applied to each reading.
53    ///
54    /// # Returns
55    ///
56    /// The adapted sensor.
57    pub fn new(inner: S, select: F) -> Map<S, F> {
58        Map { inner, select }
59    }
60
61    /// Borrows the wrapped sensor.
62    ///
63    /// # Returns
64    ///
65    /// The sensor underneath the adapter.
66    pub fn inner(&self) -> &S {
67        &self.inner
68    }
69
70    /// Mutably borrows the wrapped sensor.
71    ///
72    /// # Returns
73    ///
74    /// The sensor underneath the adapter.
75    pub fn inner_mut(&mut self) -> &mut S {
76        &mut self.inner
77    }
78
79    /// Unwraps the sensor, discarding the adapter.
80    ///
81    /// # Returns
82    ///
83    /// The sensor underneath the adapter.
84    pub fn into_inner(self) -> S {
85        self.inner
86    }
87}
88
89impl<S, F, T> Sensor for Map<S, F>
90where
91    S: Sensor + Send,
92    F: FnMut(S::Reading) -> T + Send,
93{
94    type Reading = T;
95
96    async fn read(&mut self) -> Result<T> {
97        let reading = self.inner.read().await?;
98        Ok((self.select)(reading))
99    }
100}
101
102/// An actuator whose commands are converted before they reach the part.
103///
104/// Created by [`Actuator::map_command`]. The function runs on every command, so a part
105/// that takes a pulse width or a duty can be driven by a `bool`, an angle, or a
106/// percentage, whichever the caller has.
107///
108/// # Examples
109///
110/// ```
111/// use pamoja_core::{Actuator, Result};
112///
113/// struct Pump {
114///     duty: u8,
115/// }
116/// impl Actuator for Pump {
117///     type Command = u8;
118///     async fn apply(&mut self, duty: u8) -> Result<()> {
119///         self.duty = duty;
120///         Ok(())
121///     }
122/// }
123///
124/// # async fn run() -> Result<()> {
125/// // A profile drives the pump as on or off; on means full speed.
126/// let mut switch = Pump { duty: 0 }.map_command(|on: bool| if on { 100 } else { 0 });
127/// switch.apply(true).await?;
128/// assert_eq!(switch.inner().duty, 100);
129/// # Ok(())
130/// # }
131/// ```
132pub struct MapCommand<A, F, C> {
133    inner: A,
134    convert: F,
135    command: PhantomData<fn(C)>,
136}
137
138impl<A, F, C> MapCommand<A, F, C> {
139    /// Wraps `inner` so each command passes through `convert` first.
140    ///
141    /// # Arguments
142    ///
143    /// * `inner` - the actuator being adapted.
144    /// * `convert` - the function turning the caller's command into the part's.
145    ///
146    /// # Returns
147    ///
148    /// The adapted actuator.
149    pub fn new(inner: A, convert: F) -> MapCommand<A, F, C> {
150        MapCommand {
151            inner,
152            convert,
153            command: PhantomData,
154        }
155    }
156
157    /// Borrows the wrapped actuator.
158    ///
159    /// # Returns
160    ///
161    /// The actuator underneath the adapter.
162    pub fn inner(&self) -> &A {
163        &self.inner
164    }
165
166    /// Mutably borrows the wrapped actuator.
167    ///
168    /// # Returns
169    ///
170    /// The actuator underneath the adapter.
171    pub fn inner_mut(&mut self) -> &mut A {
172        &mut self.inner
173    }
174
175    /// Unwraps the actuator, discarding the adapter.
176    ///
177    /// # Returns
178    ///
179    /// The actuator underneath the adapter.
180    pub fn into_inner(self) -> A {
181        self.inner
182    }
183}
184
185impl<A, F, C> Actuator for MapCommand<A, F, C>
186where
187    A: Actuator + Send,
188    F: FnMut(C) -> A::Command + Send,
189    C: Send,
190{
191    type Command = C;
192
193    async fn apply(&mut self, command: C) -> Result<()> {
194        let converted = (self.convert)(command);
195        self.inner.apply(converted).await
196    }
197}