embedded_multi_page_hmi/
page_manager.rs

1use super::*;
2use core::mem;
3
4/// The PageManager is responsible for switching among pages while
5/// pages do not know about other pages.
6/// The PageManager also dispatches events and updates the current page.
7///
8/// Only a "home" page is mandatory. Any other pages are optional.
9/// Startup and Shutdown pages are purely information pages and not
10/// activated only by SystemStartup and SystemShutdown events.
11///
12/// h2. Example
13///
14/// ```ignore
15/// let mut input = SomeInput(); // artificial code
16/// let display = SomeDisplay::new(); // artificial code
17/// let home = HomePage::new("!!! This is the home page !!!");
18///
19/// let mut m = PageManager::new(display, Box::new(home));
20/// // Optional startup page has a mandatory lifetime.
21/// let startup = StartupPage::new("Welcome message", 8);
22/// m.register_startup(Box::new(startup));
23/// // Optional Shutdown page has a mandatory lifetime.
24/// let shutdown = ShutdownPage::new("Bye bye message", 10);
25/// m.register_shutdown(Box::new(shutdown));
26/// // Additional pages reachable by next button
27/// // A predefined Information text page with lifetime
28/// let page_one = TextPage::new(
29///     BasicPage::new("First", Some(PageLifetime::new(PageNavigation::Left, 6))),
30///     "First Information Page with 3 seconds lifetime; moving to next page",
31/// );
32/// m.register(Box::new(page_one));
33///
34/// // Enter the event loop
35/// let mut navigation = m.dispatch(PageNavigation::SystemStart).unwrap();
36/// loop {
37///     match input.next() {
38///          None => m.dispatch(navigation),
39///          Some(interaction) => m.dispatch_interaction(interaction),
40///      }
41/// }
42/// ```
43
44// h2. Implementation Note
45//
46// There is only one page active at a time, that dispatches events
47// (stored in page variable). other pages are activate by updating links
48// (in respective directions).
49//
50// * Tree structures of pages are modeled by left, right, up and down links.
51// * Tree root is where both up and left links are empty.
52// * From the active page, all other pages can be navigated to
53//
54// h3. Example Structure
55//
56// ```ignore
57//  a-------------------b-----------c
58//  |                   |
59//  d-----e-----f       o-p
60//  |     |     |
61//  g-h-i j-k-l m-n
62// ```
63// * `a`- is root page
64// * `b, c` - are pages on the same level like `a` reachable via right link of `a`
65// * `d, e, f`- are sub-pages of `a` reachable via down link of `a`
66//
67pub struct PageManager<'a, D> {
68    display: D,
69    page: Box<dyn PageInterface<D> + 'a>,
70    left: Link<Box<dyn PageInterface<D> + 'a>>,
71    right: Link<Box<dyn PageInterface<D> + 'a>>,
72    up: Link<Box<dyn PageInterface<D> + 'a>>,
73    down: Link<Box<dyn PageInterface<D> + 'a>>,
74    startup: Option<Box<dyn PageInterface<D> + 'a>>,
75    shutdown: Option<Box<dyn PageInterface<D> + 'a>>,
76    state: PageManagerState,
77}
78
79type Link<T> = Option<Box<Node<T>>>;
80
81struct Node<T> {
82    page: T,
83    left: Link<T>,
84    right: Link<T>,
85    down: Link<T>,
86    up: Link<T>,
87}
88
89enum PageManagerState {
90    Startup,
91    Operational,
92    Shutdown,
93}
94
95impl<'a, D> PageManager<'a, D> {
96    /// PageManager Constructor
97    ///
98    /// Arguments
99    ///
100    /// * `display`: The display data structure where all output is rendered to
101    ///   The display data structure and the logic attached makes the rendered
102    ///   output appear on some output facility viewable by a human.
103    /// * `home`: The "home" page. There must be at least one page. Other pages
104    ///    are added by register_* calls.
105    pub fn new(display: D, home: Box<dyn PageInterface<D> + 'a>) -> Self {
106        PageManager::<D> {
107            display,
108            page: home,
109            left: None,
110            right: None,
111            up: None,
112            down: None,
113            startup: None,
114            shutdown: None,
115            state: PageManagerState::Startup,
116        }
117    }
118
119    /// Update the content of the active page on the display
120    ///
121    /// Potentially initiate a page change before displaying, since the
122    /// update responsibility is the responsibility of the specific active page
123    pub fn update(&mut self) -> Result<(), PageError> {
124        // menu pages need submenu titles
125        let iter = Box::new(SubPageIterator {
126            left: self.down.as_deref(),
127        });
128        let navigation = self.page.update(Some(Box::new(iter.map(|p| p.title()))))?;
129
130        // in case the page requires another page to navigate this needs to be performed
131        if navigation != PageNavigation::Update {
132            self.dispatch(navigation)?;
133        }
134
135        self.page.display(&mut self.display);
136        Ok(())
137    }
138
139    /// Register a new page
140    ///
141    /// The page is registered in the "left" direction of the
142    /// active page. The registered page will be the new active page.
143    ///
144    /// Arguments
145    ///
146    /// * `page` - The page to be registered and activated.
147    pub fn register(&mut self, page: Box<dyn PageInterface<D> + 'a>) {
148        self.push_left(page, None, None);
149        self.activate_left();
150    }
151
152    /// Register a new sub page
153    ///
154    /// The page is registered in the "down" direction of the
155    /// active page. The registered page will be the new active page.
156    ///
157    /// Arguments
158    ///
159    /// * `page`: - The page to be registered and activated.
160    pub fn register_sub(&mut self, page: Box<dyn PageInterface<D> + 'a>) {
161        self.push_down(page, None, None);
162        self.activate_down();
163    }
164
165    /// Register a startup page
166    ///
167    /// There can be just one startup page. Multiple calls to this function
168    /// overwrite the previously set startup page.
169    ///
170    /// Arguments
171    ///
172    /// * `page`: - The page that should serve for startup.
173    pub fn register_startup(&mut self, page: Box<dyn PageInterface<D> + 'a>) {
174        self.startup = Some(page);
175    }
176
177    /// Register a shutdown page
178    ///
179    /// There can be just one shutdown page. Multiple calls to this function
180    /// overwrite the previously set shutdown page.
181    ///
182    /// Arguments
183    ///
184    /// * `page`: - The page that should serve for startup.
185    pub fn register_shutdown(&mut self, page: Box<dyn PageInterface<D> + 'a>) {
186        self.shutdown = Some(page);
187    }
188
189    fn push_left(
190        &mut self,
191        page: Box<dyn PageInterface<D> + 'a>,
192        up: Link<Box<dyn PageInterface<D> + 'a>>,
193        down: Link<Box<dyn PageInterface<D> + 'a>>,
194    ) {
195        let new_node = Box::new(Node {
196            page,
197            left: self.left.take(),
198            right: None,
199            down,
200            up,
201        });
202        self.left = Some(new_node);
203    }
204
205    fn push_right(
206        &mut self,
207        page: Box<dyn PageInterface<D> + 'a>,
208        up: Link<Box<dyn PageInterface<D> + 'a>>,
209        down: Link<Box<dyn PageInterface<D> + 'a>>,
210    ) {
211        let new_node = Box::new(Node {
212            page,
213            left: None,
214            right: self.right.take(),
215            up,
216            down,
217        });
218        self.right = Some(new_node);
219    }
220
221    fn pop_left(
222        &mut self,
223    ) -> Option<(
224        Box<dyn PageInterface<D> + 'a>,
225        Link<Box<dyn PageInterface<D> + 'a>>,
226        Link<Box<dyn PageInterface<D> + 'a>>,
227    )> {
228        self.left.take().map(|node| {
229            let mut node = node;
230            self.left = node.left;
231            (node.page, node.up.take(), node.down.take())
232        })
233    }
234
235    fn pop_right(
236        &mut self,
237    ) -> Option<(
238        Box<dyn PageInterface<D> + 'a>,
239        Link<Box<dyn PageInterface<D> + 'a>>,
240        Link<Box<dyn PageInterface<D> + 'a>>,
241    )> {
242        self.right.take().map(|node| {
243            let mut node = node;
244            self.right = node.right;
245            (node.page, node.up.take(), node.down.take())
246        })
247    }
248
249    /// Navigate to the left page
250    /// If there is no left page it returns false and activate page is unchanged
251    fn activate_left(&mut self) -> bool {
252        match self.pop_left() {
253            None => false,
254            Some((page, up, down)) => {
255                let page = mem::replace(&mut self.page, page);
256                let new_up = self.up.take();
257                let new_down = self.down.take();
258                self.push_right(page, new_up, new_down);
259                self.up = up;
260                self.down = down;
261                true
262            }
263        }
264    }
265
266    /// Navigate to the right page
267    /// If there is no right page it returns false and activate page is unchanged
268    fn activate_right(&mut self) -> bool {
269        match self.pop_right() {
270            None => false,
271            Some((page, up, down)) => {
272                let page = mem::replace(&mut self.page, page);
273                let new_up = self.up.take();
274                let new_down = self.down.take();
275                self.push_left(page, new_up, new_down);
276                self.up = up;
277                self.down = down;
278                true
279            }
280        }
281    }
282
283    fn activate_most_right(&mut self) {
284        while self.activate_right() {}
285    }
286
287    fn push_down(
288        &mut self,
289        page: Box<dyn PageInterface<D> + 'a>,
290        left: Link<Box<dyn PageInterface<D> + 'a>>,
291        right: Link<Box<dyn PageInterface<D> + 'a>>,
292    ) {
293        let new_node = Box::new(Node {
294            page,
295            up: None,
296            down: self.down.take(),
297            left,
298            right,
299        });
300        self.down = Some(new_node);
301    }
302
303    fn push_up(
304        &mut self,
305        page: Box<dyn PageInterface<D> + 'a>,
306        left: Link<Box<dyn PageInterface<D> + 'a>>,
307        right: Link<Box<dyn PageInterface<D> + 'a>>,
308    ) {
309        let new_node = Box::new(Node {
310            page,
311            up: self.up.take(),
312            down: None,
313            left,
314            right,
315        });
316        self.up = Some(new_node);
317    }
318
319    fn pop_down(
320        &mut self,
321    ) -> Option<(
322        Box<dyn PageInterface<D> + 'a>,
323        Link<Box<dyn PageInterface<D> + 'a>>,
324        Link<Box<dyn PageInterface<D> + 'a>>,
325    )> {
326        self.down.take().map(|node| {
327            let mut node = node;
328            self.down = node.down;
329            (node.page, node.left.take(), node.right.take())
330        })
331    }
332
333    fn pop_up(
334        &mut self,
335    ) -> Option<(
336        Box<dyn PageInterface<D> + 'a>,
337        Link<Box<dyn PageInterface<D> + 'a>>,
338        Link<Box<dyn PageInterface<D> + 'a>>,
339    )> {
340        self.up.take().map(|node| {
341            let mut node = node;
342            self.up = node.up;
343            (node.page, node.left.take(), node.right.take())
344        })
345    }
346
347    fn activate_down(&mut self) -> bool {
348        match self.pop_down() {
349            None => false,
350            Some((page, left, right)) => {
351                let page = mem::replace(&mut self.page, page);
352                let new_left = self.left.take();
353                let new_right = self.right.take();
354                self.push_up(page, new_left, new_right);
355                self.left = left;
356                self.right = right;
357                true
358            }
359        }
360    }
361
362    fn activate_up(&mut self) -> bool {
363        self.activate_most_right();
364        match self.pop_up() {
365            None => false,
366            Some((page, left, right)) => {
367                let page = mem::replace(&mut self.page, page);
368                let new_left = self.left.take();
369                let new_right = self.right.take();
370                self.push_down(page, new_left, new_right);
371                self.left = left;
372                self.right = right;
373                true
374            }
375        }
376    }
377
378    fn activate_home(&mut self) {
379        while self.activate_up() {}
380        self.activate_most_right();
381    }
382
383    /// Dispatch an interaction event
384    ///
385    /// Let the active page process the interaction event and eventually turn
386    /// The interaction event into a executed page navigation
387    ///
388    /// Arguments
389    ///
390    /// * `interaction`: - The interaction event to dispatch
391
392    pub fn dispatch_interaction(
393        &mut self,
394        interaction: Interaction,
395    ) -> Result<PageNavigation, PageError> {
396        let navigation = match self.state {
397            PageManagerState::Startup => match &mut self.startup {
398                None => self.page.dispatch(interaction),
399                Some(x) => x.dispatch(interaction),
400            },
401            PageManagerState::Operational => self.page.dispatch(interaction),
402            PageManagerState::Shutdown => match &mut self.shutdown {
403                None => self.page.dispatch(interaction),
404                Some(x) => x.dispatch(interaction),
405            },
406        };
407        self.dispatch(navigation)
408    }
409
410    /// Dispatch a navigation event
411    ///
412    /// The event can cause a change of the active page or
413    /// lead to an update of the active page content.
414    ///
415    /// Arguments
416    ///
417    /// * `navigation`: - The navigation event to dispatch
418    pub fn dispatch(&mut self, navigation: PageNavigation) -> Result<PageNavigation, PageError> {
419        let mut navigation = navigation;
420        match navigation {
421            PageNavigation::SystemStart => {
422                self.activate_home(); // reset the ordinary page structure to home in case there is no startup page
423                match &mut self.startup {
424                    Some(page) => {
425                        navigation = page.update(None)?;
426                        page.display(&mut self.display);
427                    }
428                    None => (),
429                }
430            }
431            PageNavigation::SystemStop => match &mut self.shutdown {
432                Some(page) => {
433                    navigation = page.update(None)?;
434                    page.display(&mut self.display);
435                }
436                None => (),
437            },
438            PageNavigation::Left => {
439                // when navigating left, we turn around at the end; in case there is no previous navigation
440                if !self.activate_left() {
441                    self.activate_most_right();
442                }
443                self.update()?;
444                navigation = PageNavigation::Update;
445            }
446            PageNavigation::Right => {
447                self.activate_right();
448                self.update()?;
449                navigation = PageNavigation::Update;
450            }
451            PageNavigation::Home => {
452                self.activate_home();
453                self.update()?;
454                navigation = PageNavigation::Update;
455            }
456            PageNavigation::Up => {
457                self.activate_up();
458                self.update()?;
459                navigation = PageNavigation::Update;
460            }
461            PageNavigation::NthSubpage(index) => {
462                self.activate_down();
463                let mut index: usize = index;
464                while index > 1 {
465                    self.activate_left();
466                    index -= 1;
467                }
468                self.update()?;
469                navigation = PageNavigation::Update;
470            }
471            PageNavigation::Update => {
472                self.update()?;
473            }
474        };
475
476        // update the internal state for Correct HMI interaction update
477        match navigation {
478            PageNavigation::SystemStart => self.state = PageManagerState::Startup,
479            PageNavigation::SystemStop => self.state = PageManagerState::Shutdown,
480            _ => self.state = PageManagerState::Operational,
481        }
482
483        Ok(navigation)
484    }
485}
486
487impl<'a, D> Drop for PageManager<'a, D> {
488    /// TODO - update to remove everything
489    fn drop(&mut self) {
490        // forward list
491        let mut cur_horizontal = self.left.take();
492        while let Some(mut boxed_node) = cur_horizontal {
493            cur_horizontal = boxed_node.left.take();
494        }
495        // backward list
496        let mut cur_horizontal = self.right.take();
497        while let Some(mut boxed_node) = cur_horizontal {
498            cur_horizontal = boxed_node.left.take();
499        }
500    }
501}
502
503pub struct SubPageIterator<'a, P> {
504    left: Option<&'a Node<P>>,
505}
506
507impl<'a, D> PageManager<'a, D> {
508    pub fn sub_iter(&self) -> SubPageIterator<Box<dyn PageInterface<D> + 'a>> {
509        SubPageIterator {
510            left: self.down.as_deref(),
511        }
512    }
513}
514
515impl<'a, D> Iterator for SubPageIterator<'a, Box<dyn PageInterface<D> + 'a>> {
516    type Item = &'a Box<dyn PageInterface<D> + 'a>;
517    fn next(&mut self) -> Option<Self::Item> {
518        self.left.map(|node| {
519            self.left = node.left.as_deref();
520            &node.page
521        })
522    }
523}
524
525#[cfg(test)]
526mod tests;