-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
use a finite state machine to handle state changes
- Loading branch information
Showing
6 changed files
with
292 additions
and
114 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this | ||
// file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
use crate::{ | ||
fsm::{Fsm, FsmState, StateMap}, | ||
notify::{send, NotifyNotification}, | ||
Notification, | ||
}; | ||
use std::{collections::HashMap, hash::Hash}; | ||
|
||
struct ChargingState; | ||
struct DischargingState; | ||
struct FullState; | ||
struct LowState; | ||
struct CriticalState; | ||
|
||
#[derive(Hash, Eq, PartialEq)] | ||
pub enum State { | ||
Charging, | ||
Discharging, | ||
Full, | ||
Low, | ||
Critical, | ||
} | ||
|
||
pub struct Data<'data> { | ||
pub current_level: u32, | ||
pub status: String, | ||
pub low_level: u32, | ||
pub critical_level: u32, | ||
pub critical: Option<&'data Notification>, | ||
pub low: Option<&'data Notification>, | ||
pub full: Option<&'data Notification>, | ||
pub charging: Option<&'data Notification>, | ||
pub discharging: Option<&'data Notification>, | ||
pub notification: *mut NotifyNotification, | ||
} | ||
|
||
impl<'data> FsmState<State, Data<'data>> for ChargingState { | ||
fn enter(&mut self, data: &mut Data) { | ||
if let Some(n) = data.charging { | ||
send(data.notification, n); | ||
} | ||
} | ||
|
||
fn next_state(&self, data: &mut Data) -> Option<State> { | ||
match (data.status.as_str(), data.current_level) { | ||
("Full", _) => Some(State::Full), | ||
("Discharging", l) if l <= data.critical_level => Some(State::Critical), | ||
("Discharging", l) if l <= data.low_level => Some(State::Low), | ||
("Discharging", _) => Some(State::Discharging), | ||
_ => None, | ||
} | ||
} | ||
|
||
fn exit(&mut self, _data: &mut Data) {} | ||
} | ||
|
||
impl<'data> FsmState<State, Data<'data>> for DischargingState { | ||
fn enter(&mut self, data: &mut Data) { | ||
if let Some(n) = data.discharging { | ||
send(data.notification, n); | ||
} | ||
} | ||
|
||
fn next_state(&self, data: &mut Data) -> Option<State> { | ||
match (data.status.as_str(), data.current_level) { | ||
("Charging", _) => Some(State::Charging), | ||
("Full", _) => Some(State::Full), // add this just in case | ||
("Discharging", l) if l <= data.critical_level => Some(State::Critical), | ||
("Discharging", l) if l <= data.low_level => Some(State::Low), | ||
_ => None, | ||
} | ||
} | ||
|
||
fn exit(&mut self, _data: &mut Data) {} | ||
} | ||
|
||
impl<'data> FsmState<State, Data<'data>> for FullState { | ||
fn enter(&mut self, data: &mut Data) { | ||
if let Some(n) = data.full { | ||
send(data.notification, n); | ||
} | ||
} | ||
|
||
fn next_state(&self, data: &mut Data) -> Option<State> { | ||
match data.status.as_str() { | ||
"Charging" => Some(State::Charging), | ||
"Discharging" => Some(State::Discharging), | ||
_ => None, | ||
} | ||
} | ||
|
||
fn exit(&mut self, _data: &mut Data) {} | ||
} | ||
|
||
impl<'data> FsmState<State, Data<'data>> for LowState { | ||
fn enter(&mut self, data: &mut Data) { | ||
if let Some(n) = data.low { | ||
send(data.notification, n); | ||
} | ||
} | ||
|
||
fn next_state(&self, data: &mut Data) -> Option<State> { | ||
match (data.status.as_str(), data.current_level) { | ||
("Charging", _) => Some(State::Charging), | ||
("Discharging", l) if l <= data.critical_level => Some(State::Critical), | ||
_ => None, | ||
} | ||
} | ||
|
||
fn exit(&mut self, _data: &mut Data) {} | ||
} | ||
|
||
impl<'data> FsmState<State, Data<'data>> for CriticalState { | ||
fn enter(&mut self, data: &mut Data) { | ||
if let Some(n) = data.critical { | ||
send(data.notification, n); | ||
} | ||
} | ||
|
||
fn next_state(&self, data: &mut Data) -> Option<State> { | ||
if data.status == "Charging" { | ||
return Some(State::Charging); | ||
} | ||
None | ||
} | ||
|
||
fn exit(&mut self, _data: &mut Data) {} | ||
} | ||
|
||
pub fn create_fsm<'data>() -> Fsm<State, Data<'data>> { | ||
let mut states: StateMap<State, Data> = HashMap::new(); | ||
states.insert(State::Full, Box::new(FullState)); | ||
states.insert(State::Charging, Box::new(ChargingState)); | ||
states.insert(State::Discharging, Box::new(DischargingState)); | ||
states.insert(State::Low, Box::new(LowState)); | ||
states.insert(State::Critical, Box::new(CriticalState)); | ||
Fsm::new(State::Discharging, states) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this | ||
// file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
||
use std::{collections::HashMap, hash::Hash}; | ||
|
||
pub type StateMap<K, D> = HashMap<K, Box<dyn FsmState<K, D>>>; | ||
|
||
pub struct Fsm<K, D> | ||
where | ||
K: Eq + Hash, | ||
{ | ||
current_state: K, | ||
states: StateMap<K, D>, | ||
} | ||
|
||
impl<K, D> Fsm<K, D> | ||
where | ||
K: Eq + Hash, | ||
{ | ||
pub fn new(init_state: K, states: StateMap<K, D>) -> Self { | ||
Fsm { | ||
current_state: init_state, | ||
states, | ||
} | ||
} | ||
|
||
fn set_state(&mut self, new_state: K, data: &mut D) { | ||
self.states.get_mut(&self.current_state).unwrap().exit(data); | ||
self.states.get_mut(&new_state).unwrap().enter(data); | ||
self.current_state = new_state; | ||
} | ||
|
||
pub fn shift(&mut self, data: &mut D) { | ||
if let Some(next_state) = self | ||
.states | ||
.get_mut(&self.current_state) | ||
.unwrap() | ||
.next_state(data) | ||
{ | ||
self.set_state(next_state, data); | ||
} | ||
} | ||
} | ||
|
||
pub trait FsmState<K, D> | ||
where | ||
K: Eq + Hash, | ||
{ | ||
fn enter(&mut self, data: &mut D); | ||
fn next_state(&self, data: &mut D) -> Option<K>; | ||
fn exit(&mut self, data: &mut D); | ||
} |
Oops, something went wrong.