Mercurial > templog
comparison rust/src/fridge.rs @ 632:bde302def78e rust
moving to riker, nowhere near yet
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Thu, 22 Aug 2019 23:59:50 +0800 |
parents | c57821a60e51 |
children | 490e9e15b98c |
comparison
equal
deleted
inserted
replaced
631:c57821a60e51 | 632:bde302def78e |
---|---|
1 #[cfg(target_os = "linux")] | |
2 extern crate sysfs_gpio; | |
3 | |
4 use std; | 1 use std; |
5 use std::io; | 2 |
6 use std::mem; | |
7 use std::error::Error; | |
8 use std::time::{Duration,Instant}; | 3 use std::time::{Duration,Instant}; |
9 | 4 use riker::actors::*; |
10 use futures::{Future,Stream}; | |
11 use futures::sync::{mpsc}; | |
12 | 5 |
13 #[cfg(target_os = "linux")] | 6 #[cfg(target_os = "linux")] |
14 use self::sysfs_gpio::{Direction, Pin}; | 7 use self::sysfs_gpio::{Direction, Pin}; |
15 | 8 |
16 use config::Config; | 9 use super::config::Config; |
17 use params::Params; | 10 use super::params::Params; |
18 use types::*; | 11 use super::types::*; |
19 | 12 |
20 #[derive(Debug)] | 13 #[derive(Debug)] |
21 pub enum Message { | 14 pub struct Reading { |
22 Sensor {wort: Option<f32>, fridge: Option<f32>}, | 15 wort: Option<f32>, |
23 Params (Params), | 16 fridge: Option<f32>, |
24 Tick(u64), | 17 } |
25 } | 18 |
26 | 19 #[derive(Debug)] |
20 pub struct Tick; | |
21 | |
22 | |
23 #[actor(Params, Tick, Reading)] | |
27 pub struct Fridge { | 24 pub struct Fridge { |
28 params: Params, | 25 params: Params, |
29 config: Config, | 26 config: Config, |
30 | 27 |
31 on: bool, | 28 on: bool, |
33 temp_fridge: Option<f32>, | 30 temp_fridge: Option<f32>, |
34 last_off_time: Instant, | 31 last_off_time: Instant, |
35 wort_valid_time: Instant, | 32 wort_valid_time: Instant, |
36 integrator: StepIntegrator, | 33 integrator: StepIntegrator, |
37 control: FridgeControl, | 34 control: FridgeControl, |
38 | 35 } |
39 // Timeouts to wake ourselves up again | 36 |
40 handle: Handle, | 37 impl Actor for Fridge { |
41 timeout_s: mpsc::Sender<u64>, | 38 type Msg = FridgeMsg; |
42 timeout_r: Option<mpsc::Receiver<u64>>, | 39 fn recv(&mut self, |
43 ticker: u64, | 40 ctx: &Context<Self::Msg>, |
44 } | 41 msg: Self::Msg, |
45 | 42 sender: Sender) { |
46 impl Sink for Fridge { | 43 self.receive(ctx, msg, sender); |
47 | 44 // TODO: should we do self.tick(ctx) here instead? |
48 type SinkItem = Message; | 45 } |
49 type SinkError = TemplogError; | 46 |
50 | 47 fn post_start(&mut self, ctx: &Context<Self::Msg>) { |
51 fn start_send(&mut self, msg: Message) | 48 self.tick(ctx); |
52 -> Result<(), Self::SinkError> { | 49 } |
53 self.process_msg(msg); | 50 } |
54 Ok() | 51 |
55 } | 52 impl Receive<Reading> for Fridge { |
56 | 53 type Msg = FridgeMsg; |
57 fn poll_complete(&mut self) -> futures::Poll<(), Self::SinkError> { | 54 fn receive(&mut self, |
58 Ok(futures::Async::Ready(())) | 55 ctx: &Context<Self::Msg>, |
56 r: Reading, | |
57 _sender: Sender) { | |
58 self.temp_wort = r.wort; | |
59 self.temp_fridge = r.fridge; | |
60 | |
61 if self.temp_wort.is_some() { | |
62 self.wort_valid_time = Instant::now(); | |
63 } | |
64 | |
65 self.tick(ctx); | |
66 } | |
67 } | |
68 | |
69 impl Receive<Params> for Fridge { | |
70 type Msg = FridgeMsg; | |
71 fn receive(&mut self, | |
72 ctx: &Context<Self::Msg>, | |
73 p: Params, | |
74 _sender: Sender) { | |
75 self.params = p; | |
76 println!("fridge set_params {:?}", self.params); | |
77 | |
78 self.tick(ctx); | |
79 } | |
80 } | |
81 | |
82 impl Receive<Tick> for Fridge { | |
83 type Msg = FridgeMsg; | |
84 fn receive(&mut self, | |
85 ctx: &Context<Self::Msg>, | |
86 _tick: Tick, | |
87 _sender: Sender) { | |
88 self.tick(ctx); | |
59 } | 89 } |
60 } | 90 } |
61 | 91 |
62 enum FridgeControl { | 92 enum FridgeControl { |
63 #[cfg(target_os = "linux")] | 93 #[cfg(target_os = "linux")] |
64 Gpio(Pin), | 94 Gpio(Pin), |
65 Fake, | 95 Fake, |
66 } | 96 } |
67 | |
68 struct TestControl {} | |
69 | 97 |
70 impl Drop for Fridge { | 98 impl Drop for Fridge { |
71 fn drop(&mut self) { | 99 fn drop(&mut self) { |
72 // safety fridge off | 100 // safety fridge off |
73 self.turn(false); | 101 self.turn(false); |
74 } | 102 } |
75 } | 103 } |
76 | 104 |
77 impl Fridge { | 105 impl Fridge { |
78 pub fn new(config: &Config, nowait: bool, p: Params, handle: &Handle) -> Fridge { | 106 pub fn new(config: &Config, nowait: bool, p: Params) -> Fridge { |
79 let (s, r) = mpsc::channel(1); | |
80 let mut f = Fridge { | 107 let mut f = Fridge { |
81 config: config.clone(), | 108 config: config.clone(), |
82 params: p.clone(), | 109 params: p.clone(), |
83 on: false, | 110 on: false, |
84 temp_wort: None, | 111 temp_wort: None, |
85 temp_fridge: None, | 112 temp_fridge: None, |
86 last_off_time: Instant::now(), | 113 last_off_time: Instant::now(), |
87 wort_valid_time: Instant::now() - Duration::new(config.FRIDGE_WORT_INVALID_TIME, 100), | 114 wort_valid_time: Instant::now() - Duration::new(config.FRIDGE_WORT_INVALID_TIME, 100), |
88 integrator: StepIntegrator::new(Duration::new(p.overshoot_delay, 0)), | 115 integrator: StepIntegrator::new(Duration::new(p.overshoot_delay, 0)), |
89 control: Self::make_control(config), | 116 control: Self::make_control(config), |
90 | |
91 handle: handle.clone(), | |
92 timeout_s: s, | |
93 timeout_r: Some(r), | |
94 ticker: 0, | |
95 }; | 117 }; |
96 | 118 |
97 if nowait { | 119 if nowait { |
98 f.last_off_time -= Duration::new(config.FRIDGE_DELAY, 1); | 120 f.last_off_time -= Duration::new(config.FRIDGE_DELAY, 1); |
99 } | 121 } |
115 #[cfg(not(target_os = "linux"))] | 137 #[cfg(not(target_os = "linux"))] |
116 fn make_control(config: &Config) -> FridgeControl { | 138 fn make_control(config: &Config) -> FridgeControl { |
117 FridgeControl::Fake | 139 FridgeControl::Fake |
118 } | 140 } |
119 | 141 |
120 | |
121 | |
122 fn next_wakeup(&self) -> Duration { | 142 fn next_wakeup(&self) -> Duration { |
123 let millis = 400; | 143 let millis = 400; |
124 let dur = Duration::from_millis(millis); | 144 let dur = Duration::from_millis(millis); |
125 dur | 145 dur |
126 } | 146 } |
127 | 147 |
128 /// The fridge needs to periodically wake itself up, the returned | |
129 /// stream of Tick messages does so. | |
130 /// Examples of wakeups events are | |
131 /// | |
132 /// * overshoot calculation | |
133 /// * minimum fridge-off time | |
134 /// * invalid wort timeout | |
135 /// All specified in next_wakeup() | |
136 pub fn wakeups(&mut self) | |
137 -> Box<Stream<Item=Message, Error=TemplogError>> { | |
138 Box::new(mem::replace(&mut self.timeout_r, None) | |
139 .expect("Fridge::wakeups() can only be called once") | |
140 .map(|v| Message::Tick(v)) | |
141 .map_err(|e| TemplogError::new("wakeups() receive failed"))) | |
142 } | |
143 | 148 |
144 fn turn_off(&mut self) { | 149 fn turn_off(&mut self) { |
145 info!("Turning fridge off"); | 150 info!("Turning fridge off"); |
146 self.turn(false); | 151 self.turn(false); |
147 } | 152 } |
158 FridgeControl::Fake => debug!("fridge turns {}", if on {"on"} else {"off"}), | 163 FridgeControl::Fake => debug!("fridge turns {}", if on {"on"} else {"off"}), |
159 } | 164 } |
160 self.on = on; | 165 self.on = on; |
161 } | 166 } |
162 | 167 |
163 /// Turns the fridge off and on | 168 // Turns the fridge off and on |
164 fn compare_temperatures(&mut self) { | 169 fn compare_temperatures(&mut self) { |
165 let fridge_min = self.params.fridge_setpoint - self.params.fridge_range_lower; | 170 let fridge_min = self.params.fridge_setpoint - self.params.fridge_range_lower; |
166 let fridge_max = self.params.fridge_setpoint - self.params.fridge_range_upper; | 171 let fridge_max = self.params.fridge_setpoint - self.params.fridge_range_upper; |
167 let wort_max = self.params.fridge_setpoint + self.params.fridge_difference; | 172 let wort_max = self.params.fridge_setpoint + self.params.fridge_difference; |
168 let off_time = Instant::now() - self.last_off_time; | 173 let off_time = Instant::now() - self.last_off_time; |
257 } | 262 } |
258 } | 263 } |
259 | 264 |
260 /// Must be called after every state change. Turns the fridge on/off as required and | 265 /// Must be called after every state change. Turns the fridge on/off as required and |
261 /// schedules any future wakeups based on the present (new) state | 266 /// schedules any future wakeups based on the present (new) state |
262 fn tick(&mut self) { | 267 /// Examples of wakeups events are |
268 /// | |
269 /// * overshoot calculation | |
270 /// * minimum fridge-off time | |
271 /// * invalid wort timeout | |
272 /// All specified in next_wakeup() | |
273 fn tick(&mut self, | |
274 ctx: &Context<Self::Msg>) { | |
263 debug!("tick"); | 275 debug!("tick"); |
264 | 276 |
265 self.compare_temperatures(); | 277 self.compare_temperatures(); |
266 self.send_next_timeout(); | 278 |
267 } | 279 // Sets the next self-wakeup timeout |
268 | |
269 /// Sets the next self-wakeup timeout | |
270 fn send_next_timeout(&mut self) { | |
271 let waker = self.timeout_s.clone(); | |
272 let dur = self.next_wakeup(); | 280 let dur = self.next_wakeup(); |
273 debug!("fridge next wakeup {:?}", dur); | 281 ctx.schedule_once(dur, self, None, Tick); |
274 self.ticker += 1; | 282 } |
275 let v = self.ticker; | 283 } |
276 let t = Timeout::new(dur, &self.handle).unwrap() | |
277 .map_err(|_| ()) | |
278 .and_then(move |_| { | |
279 waker.send(v) | |
280 .map_err(|e| { | |
281 warn!("Send error in tick(): {}", e.to_string()); | |
282 () | |
283 }) | |
284 }) | |
285 .map(|_| ()); | |
286 self.handle.spawn(t); | |
287 } | |
288 | |
289 fn process_msg(&mut self, msg: Message) { | |
290 debug!("process_msg {:?}", msg); | |
291 match msg { | |
292 Message::Sensor{wort, fridge} => self.update_sensor(wort, fridge), | |
293 Message::Params(p) => self.update_params(p), | |
294 Message::Tick(v) => if v == self.ticker {self.tick()}, // schedule a timeout if there are none pending | |
295 }; | |
296 } | |
297 | |
298 pub fn update_params(&mut self, p: Params) { | |
299 self.params = p; | |
300 println!("fridge set_params {:?}", self.params); | |
301 | |
302 self.tick(); | |
303 } | |
304 | |
305 pub fn update_sensor(&mut self, wort: Option<f32>, fridge: Option<f32>) { | |
306 self.temp_wort = wort; | |
307 self.temp_fridge = fridge; | |
308 | |
309 if self.temp_wort.is_some() { | |
310 self.wort_valid_time = Instant::now(); | |
311 } | |
312 | |
313 self.tick(); | |
314 } | |
315 | |
316 } |