embedded_multi_page_hmi/
lib.rs

1//! # Embedded Multi-Page HMI
2//!
3//! The embedded multi-page HMI combines a resource constraint display output
4//! and a constraint fixed button input by an interaction model.
5//!
6//! ## Input
7//!
8//! Input is limited to a small set of buttons.
9//!
10//! Depending on the amount of buttons several interaction models are predominant.
11//! The buttons get a semantic meaning, and allow for certain interaction
12//! models. The more buttons there are the more convenient the interaction model
13//! is.
14//!
15//! | Button | Semantics | Assigned activity |
16//! | ------ | --------- |------------------ |
17//! | first  | action    | activate, confirm, trigger, modify, ... |
18//! | second | next      | select the next item |
19//! | third  | previous  | select the previous item in list|
20//! | fourth | back      | navigate to the previous position |
21//! | fifth  | home      | go to home page, reset  |
22//!
23//! A rotary knop can be modelled as three buttons (action, next, previous).
24//!
25//! ## Output
26//!
27//! ### Display
28//!
29//! The output is on one single display. The display can be
30//!
31//! * alphanumerical,
32//! * or graphical display.
33//!
34//! ### Pages
35//!
36//! The output is organized in pages.
37//! Exactly one page is displayed at a time on the display.
38//!
39//! Every page has a lifetime.
40//!
41//! | Page     | Meaning |
42//! | -------- | ------- |
43//! | Home     | Is Mandatory; Is the fallback Page, Start point for all navigation |
44//! | Startup  | Optional; Shown during init; no interaction; replaced by Home  |
45//! | Shutdown | Optional; Shown during de-init; no interaction |
46//!
47//! Pages have the following properties:
48//!
49//! * Can handle input interactions.
50//!   * this can be used to capture input e.g. of numbers, flags
51//!     that get delegated to a data model underneath
52//! * Have a lifetime: How long are they displayed w/o input
53//!   * If the lifetime is over, automatically the home page or next page is activated.
54//! * Pages can have dynamic content (like current time, temperature, etc) that is updated
55//!   on a regular base
56//!
57//! ## Interaction Models
58//!
59//! Between pages can be navigated, triggered by an interaction or
60//! automatically triggered by events (from timer or value change)
61//!
62//! ### One Button - Sequence of Pages
63//!
64//! * The `action` interaction activates the next page.
65//! * Inside the pages no activity is possible
66//!
67//! ### Two Button/ Three Button - Sequence of Pages
68//!
69//! More than one button input allows inter-page interaction.
70//!
71//! Three button interaction is like two button interaction, except that
72//! `previous` is a shortcut for iterating with `next` through a looped list of
73//! items.
74//!
75//! There are information pages and setting pages.
76//!
77//! **Information pages**:
78//!   * purely display (dynamic) information
79//!   * do not allow for internal interaction
80//!
81//! **Setting pages**:
82//!   * Allow to select items or enter values
83//!
84//! * The `next` interaction activates the next info page.
85//! * The `action` interaction activates the setting page(s).
86//! * Inside the info pages no activity is possible
87//! * Inside the setting page(s) it is possible to
88//!   * select items with `next` interaction
89//!   * activate items with `action` interaction
90//!   * *Go back to home (info) page* could be item to select and activate
91
92// later on this should be a no_std to run on embedded - still we need a Box type
93// that is not available easily  on no_std
94// #![no_std]
95
96// use heapless::pool::Box;
97// #![feature(alloc)]  // only works with nightly compiler versions
98// use alloc::boxed::Box;
99
100#![allow(clippy::type_complexity)]
101
102/// Possible Interactions derived from the input
103#[derive(Debug, Clone, Copy)]
104pub enum Interaction {
105    /// Primary HMI event to trigger some action e.g. go to next page
106    Action,
107    /// Primary HMI event to e.g. go to next page
108    Next,
109    /// Primary HMI event to e.g. go to previous page
110    Previous,
111    /// Primary HMI event to e.g. go to one page up
112    Back,
113    /// Event to go to home page.
114    /// Could be a primary HMI event or a generated event.
115    Home,
116}
117
118/// Page navigation events dispatched by pagemanager
119#[derive(Debug, Clone, Copy, PartialEq)]
120pub enum PageNavigation {
121    /// Start the HMI.
122    SystemStart,
123    /// Stop the HMI.
124    SystemStop,
125    /// Stay at the active page and initiate an update.
126    Update,
127    /// Navigate to the left page.
128    Left,
129    /// Navigate to the right page.
130    Right,
131    /// Navigate one page up.
132    Up,
133    /// Navigate down the n-th subpage. Start counting with one.
134    NthSubpage(usize),
135    /// Event to go to home page.
136    Home,
137}
138
139/// Any error a page update my run into
140#[derive(Debug, Clone)]
141pub struct PageError;
142
143/// Data structures that implement the Page trait are Pages and can be handled
144/// by the PageManager type
145///
146/// Args
147/// * `display_driver` - The display to render the page content
148/// * `
149pub trait PageInterface<D>: PageInteractionInterface {
150    /// Force updating the page content on the display
151    fn display(&self, display_driver: &mut D);
152}
153
154/// Data structures that implement the Page trait are Pages and can be handled
155/// by the PageManager type
156///
157pub trait PageBaseInterface {
158    /// Trigger a page-internal update and causes page-internal state modification
159    ///
160    /// Is called by `PageManager`.
161    /// Handles Page Lifetime management
162    ///
163    /// Args:
164    ///     title_of_subpages: Iterator to titles of subpages (Optional)
165    ///
166    /// Returns:
167    ///     `Ok(<PageNavigation>)` - In case update is went well, to indicate the which page
168    ///         to navigate to next.
169    ///     `Error` - Indicate an error. (Note: Could be on purpose to force a controlled
170    ///         gui process shutdown)
171    fn update<'a>(
172        &mut self,
173        _title_of_subpages: Option<Box<dyn Iterator<Item = &'a str> + 'a>>,
174    ) -> Result<PageNavigation, PageError> {
175        Ok(PageNavigation::Update)
176    }
177
178    /// Every page has a title - default is empty &str
179    fn title(&self) -> &str {
180        ""
181    }
182}
183
184/// A page is responsible to implement user interaction
185///
186/// User interaction can lead to page content modification or to a navigation to
187/// another page.
188pub trait PageInteractionInterface: PageBaseInterface {
189    /// Handle page interaction
190    ///
191    /// Args:
192    ///
193    /// * `interaction` - One of the user interactions
194    ///
195    /// Returns:
196    ///
197    /// * `PageNavigation` - Advice to the page manager of where to navigate to.
198    ///   If PageNavigation::Update is returned, no navigation is processed.
199    fn dispatch(&mut self, interaction: Interaction) -> PageNavigation {
200        match interaction {
201            Interaction::Action => PageNavigation::Update,
202            Interaction::Back => PageNavigation::Up,
203            Interaction::Home => PageNavigation::Home,
204            Interaction::Next => PageNavigation::Left,
205            Interaction::Previous => PageNavigation::Right,
206        }
207    }
208}
209
210mod lifetime;
211pub mod page;
212mod page_manager;
213mod setting;
214
215// Re-exports
216#[allow(unused_imports)]
217pub use lifetime::PageLifetime;
218#[allow(unused_imports)]
219pub use page_manager::PageManager;
220#[allow(unused_imports)]
221pub use setting::{CellSetting, Setting};