comparison rust/src/fridge.rs @ 621:8136a6b99866 rust

make it compile again
author Matt Johnston <matt@ucc.asn.au>
date Sat, 15 Apr 2017 23:39:23 +0800
parents 8fda564cc46f
children efcbe0d3afd6
comparison
equal deleted inserted replaced
620:8fda564cc46f 621:8136a6b99866
1 extern crate futures; 1 extern crate futures;
2 extern crate tokio_core; 2 extern crate tokio_core;
3 #[cfg(linux)]
3 extern crate sysfs_gpio; 4 extern crate sysfs_gpio;
4 5
5 use std; 6 use std;
6 use std::io; 7 use std::io;
7 use std::mem; 8 use std::mem;
9 use std::time::{Duration,Instant}; 10 use std::time::{Duration,Instant};
10 11
11 use futures::{Future,future,Sink,Stream}; 12 use futures::{Future,future,Sink,Stream};
12 use tokio_core::reactor::{Timeout,Handle}; 13 use tokio_core::reactor::{Timeout,Handle};
13 use futures::sync::{mpsc}; 14 use futures::sync::{mpsc};
15
16 #[cfg(target_os = "linux")]
14 use sysfs_gpio::{Direction, Pin}; 17 use sysfs_gpio::{Direction, Pin};
15 18
16 use config::Config; 19 use config::Config;
17 use params::Params; 20 use params::Params;
18 use types::*; 21 use types::*;
32 temp_wort: Option<f32>, 35 temp_wort: Option<f32>,
33 temp_fridge: Option<f32>, 36 temp_fridge: Option<f32>,
34 last_off_time: Instant, 37 last_off_time: Instant,
35 wort_valid_time: Instant, 38 wort_valid_time: Instant,
36 integrator: StepIntegrator, 39 integrator: StepIntegrator,
37 gpio: Pin, 40 control: FridgeControl,
38 41
39 // Timeouts to wake ourselves up again 42 // Timeouts to wake ourselves up again
40 handle: Handle, 43 handle: Handle,
41 timeout_s: mpsc::Sender<u64>, 44 timeout_s: mpsc::Sender<u64>,
42 timeout_r: Option<mpsc::Receiver<u64>>, 45 timeout_r: Option<mpsc::Receiver<u64>>,
57 fn poll_complete(&mut self) -> futures::Poll<(), Self::SinkError> { 60 fn poll_complete(&mut self) -> futures::Poll<(), Self::SinkError> {
58 Ok(futures::Async::Ready(())) 61 Ok(futures::Async::Ready(()))
59 } 62 }
60 } 63 }
61 64
65 enum FridgeControl {
66 #[cfg(target_os = "linux")]
67 Gpio(Pin),
68 Fake,
69 }
70
71 struct TestControl {}
72
62 impl Drop for Fridge { 73 impl Drop for Fridge {
63 fn drop(&mut self) { 74 fn drop(&mut self) {
64 // safety fridge off 75 // safety fridge off
65 self.gpio.set_state(0); 76 self.turn(false);
66 } 77 }
67 } 78 }
68 79
69 impl Fridge { 80 impl Fridge {
70 pub fn new(config: &Config, nowait: bool, p: Params, handle: &Handle) -> Fridge { 81 pub fn new(config: &Config, nowait: bool, p: Params, handle: &Handle) -> Fridge {
75 on: false, 86 on: false,
76 temp_wort: None, 87 temp_wort: None,
77 temp_fridge: None, 88 temp_fridge: None,
78 last_off_time: Instant::now(), 89 last_off_time: Instant::now(),
79 wort_valid_time: Instant::now() - Duration::new(config.FRIDGE_WORT_INVALID_TIME, 100), 90 wort_valid_time: Instant::now() - Duration::new(config.FRIDGE_WORT_INVALID_TIME, 100),
80 integrator: StepIntegrator::new(p.overshoot_delay), 91 integrator: StepIntegrator::new(Duration::new(p.overshoot_delay, 0)),
81 gpio: Pin::new(config.FRIDGE_GPIO_PIN), 92 control: Self::make_control(config),
82 93
83 handle: handle.clone(), 94 handle: handle.clone(),
84 timeout_s: s, 95 timeout_s: s,
85 timeout_r: Some(r), 96 timeout_r: Some(r),
86 ticker: 0, 97 ticker: 0,
88 99
89 if nowait { 100 if nowait {
90 f.last_off_time -= Duration::new(config.FRIDGE_DELAY, 1); 101 f.last_off_time -= Duration::new(config.FRIDGE_DELAY, 1);
91 } 102 }
92 103
104 f.tick();
105
106 f
107 }
108
109 #[cfg(target_os = "linux")]
110 fn make_control(config: &Config) -> FridgeControl {
111 let mut pin = Pin(config.FRIDGE_GPIO_PIN);
93 // XXX better error handling? 112 // XXX better error handling?
94 f.gpio.export().expect("Exporting fridge gpio failed"); 113 pin.export().expect("Exporting fridge gpio failed");
95 f.gpio.set_direction(Direction::Low).expect("Fridge gpio direction failed"); 114 pin.set_direction(Direction::Low).expect("Fridge gpio direction failed");
96 115 FridgeControl::Gpio(pin)
97 f.tick(); 116 }
98 117
99 f 118 #[cfg(not(target_os = "linux"))]
100 } 119 fn make_control(config: &Config) -> FridgeControl {
120 FridgeControl::Fake
121 }
122
123
101 124
102 fn next_wakeup(&self) -> Duration { 125 fn next_wakeup(&self) -> Duration {
103 let millis = 400; 126 let millis = 400;
104 let dur = Duration::from_millis(millis); 127 let dur = Duration::from_millis(millis);
105 dur 128 dur
121 .map_err(|e| TemplogError::new("wakeups() receive failed")) 144 .map_err(|e| TemplogError::new("wakeups() receive failed"))
122 .boxed() 145 .boxed()
123 } 146 }
124 147
125 fn turn_off(&mut self) { 148 fn turn_off(&mut self) {
126 log!("Turning fridge off"); 149 info!("Turning fridge off");
127 self.turn(false); 150 self.turn(false);
128 } 151 }
129 152
130 fn turn_on(&mut self) { 153 fn turn_on(&mut self) {
131 log!("Turning fridge on"); 154 info!("Turning fridge on");
132 self.turn(true); 155 self.turn(true);
133 } 156 }
134 157
135 fn turn(&mut self, on: bool) { 158 fn turn(&mut self, on: bool) {
159 match self.control {
160 #[cfg(target_os = "linux")]
161 Gpio(pin) => pin.set_value(on as u8),
162 FridgeControl::Fake => debug!("fridge turns {}", if on {"on"} else {"off"}),
163 }
136 self.on = on; 164 self.on = on;
137 self.gpio.set_value(on as u8)
138 } 165 }
139 166
140 /// Turns the fridge off and on 167 /// Turns the fridge off and on
141 fn compare_temperatures(&mut self) { 168 fn compare_temperatures(&mut self) {
142 let fridge_min = self.params.fridge_setpoint - self.params.fridge_range_lower; 169 let fridge_min = self.params.fridge_setpoint - self.params.fridge_range_lower;
143 let fridge_max = self.params.fridge_setpoint - self.params.fridge_range_upper; 170 let fridge_max = self.params.fridge_setpoint - self.params.fridge_range_upper;
144 let wort_max = self.params.fridge_setpoint + self.params.fridge_difference; 171 let wort_max = self.params.fridge_setpoint + self.params.fridge_difference;
145 let off_time = Instant::now() - self.last_off_time; 172 let off_time = Instant::now() - self.last_off_time;
146 173
147 // Or elsewhere? 174 // Or elsewhere?
148 self.integrator.set_limit(self.params.overshoot_delay); 175 self.integrator.set_limit(Duration::new(self.params.overshoot_delay, 0));
149 176
150 // Safety to avoid bad things happening to the fridge motor (?) 177 // Safety to avoid bad things happening to the fridge motor (?)
151 // When it turns off don't start up again for at least FRIDGE_DELAY 178 // When it turns off don't start up again for at least FRIDGE_DELAY
152 if !self.on && off_time < self.config.FRIDGE_DELAY { 179 if !self.on && off_time < Duration::new(self.config.FRIDGE_DELAY, 0) {
153 log!("fridge skipping, too early"); 180 info!("fridge skipping, too early");
154 return; 181 return;
155 } 182 }
156 183
157 if self.params.disabled { 184 if self.params.disabled {
158 if self.on { 185 if self.on {
159 log!("Disabled, turning fridge off"); 186 info!("Disabled, turning fridge off");
160 self.turn_off(); 187 self.turn_off();
161 } 188 }
162 return; 189 return;
163 } 190 }
164 191
165 // handle broken wort sensor 192 // handle broken wort sensor
166 if self.temp_wort.is_none() { 193 if self.temp_wort.is_none() {
167 let invalid_time = Instant::now() - self.wort_valid_time; 194 let invalid_time = Instant::now() - self.wort_valid_time;
168 warn!("Invalid wort sensor for {:?} secs", invalid_time); 195 warn!("Invalid wort sensor for {:?} secs", invalid_time);
169 if invalid_time < self.config.FRIDGE_WORT_INVALID_TIME { 196 if invalid_time < Duration::new(self.config.FRIDGE_WORT_INVALID_TIME, 0) {
170 warn!("Has only been invalid for {:?}, waiting", invalid_time); 197 warn!("Has only been invalid for {:?}, waiting", invalid_time);
171 return; 198 return;
172 } 199 }
173 } 200 }
174 201
177 } 204 }
178 205
179 if self.on { 206 if self.on {
180 debug!("fridge is on"); 207 debug!("fridge is on");
181 let on_time = self.integrator.integrate().as_secs() as f32; 208 let on_time = self.integrator.integrate().as_secs() as f32;
182 let on_ratio = on_time / params.overshoot_delay as f32; 209 let on_ratio = on_time / self.params.overshoot_delay as f32;
183 210
184 overshoot = params.overshoot_factor as f32 * on_ratio; 211 let overshoot = self.params.overshoot_factor as f32 * on_ratio;
185 debug!("on_time {}, overshoot {}", on_percent, overshoot); 212 debug!("on_percent {}, overshoot {}", on_ratio * 100.0, overshoot);
186 213
187 let mut turn_off = false; 214 let mut turn_off = false;
188 if let Some(t) = self.temp_wort && !params.nowort { 215 if self.temp_wort.is_some() && !self.params.nowort {
216 let t = self.temp_wort.unwrap();
189 // use the wort temperature 217 // use the wort temperature
190 if t - overshoot < params.fridge_setpoint { 218 if t - overshoot < self.params.fridge_setpoint {
191 log!("wort has cooled enough, {temp}º (overshoot {overshoot}º = {factor} × {percent}%)", 219 info!("wort has cooled enough, {temp}º (overshoot {overshoot}º = {factor} × {percent}%)",
192 temp = t, overshoot = overshoot, 220 temp = t, overshoot = overshoot,
193 factor = params.overshoot_factor, 221 factor = self.params.overshoot_factor,
194 percent = on_ratio*100); 222 percent = on_ratio*100.0);
195 turn_off = true; 223 turn_off = true;
196 } 224 }
197 } else let if Some(t) = self.temp_fridge { 225 } else if let Some(t) = self.temp_fridge {
198 // use the fridge temperature 226 // use the fridge temperature
199 if t < fridge_min { 227 if t < fridge_min {
200 warn!("fridge off fallback, fridge {}, min {}", t, fridge_min); 228 warn!("fridge off fallback, fridge {}, min {}", t, fridge_min);
201 if self.temp_wort.is_none() { 229 if self.temp_wort.is_none() {
202 W("wort has been invalid for {:?}", Instant::now() - self.wort_valid_time); 230 warn!("wort has been invalid for {:?}", Instant::now() - self.wort_valid_time);
203 } 231 }
204 turn_off = true; 232 turn_off = true;
205 } 233 }
206 } 234 }
207 if turn_off { 235 if turn_off {
209 } 237 }
210 } else { 238 } else {
211 debug!("fridge is off. fridge {:?} max {:?}. wort {:?} max {:?}", 239 debug!("fridge is off. fridge {:?} max {:?}. wort {:?} max {:?}",
212 self.temp_fridge, fridge_max, self.temp_wort, wort_max); 240 self.temp_fridge, fridge_max, self.temp_wort, wort_max);
213 let mut turn_on = false; 241 let mut turn_on = false;
214 if let Some(t) = self.temp_wort && !params.nowort { 242 if self.temp_wort.is_some() && !self.params.nowort {
215 // use the wort temperature 243 // use the wort temperature
244 let t = self.temp_wort.unwrap();
216 if t >= wort_max { 245 if t >= wort_max {
217 log!("Wort is too hot {}°, max {}°", t, wort_max); 246 info!("Wort is too hot {}°, max {}°", t, wort_max);
218 turn_on = true; 247 turn_on = true;
219 } 248 }
220 } 249 }
221 250
222 if let Some(t) = self.temp_fridge { 251 if let Some(t) = self.temp_fridge {
223 if t >= fridge_max { 252 if t >= fridge_max {
224 warn!("fridge too hot fallback, fridge {}°, max {}°", t, fridge_max) 253 warn!("fridge too hot fallback, fridge {}°, max {}°", t, fridge_max);
225 turn_on = true; 254 turn_on = true;
226 } 255 }
227 } 256 }
228 257
229 if turn_on { 258 if turn_on {
235 /// Must be called after every state change. Turns the fridge on/off as required and 264 /// Must be called after every state change. Turns the fridge on/off as required and
236 /// schedules any future wakeups based on the present (new) state 265 /// schedules any future wakeups based on the present (new) state
237 fn tick(&mut self) { 266 fn tick(&mut self) {
238 debug!("tick"); 267 debug!("tick");
239 268
240 self.compare_temperatures() 269 self.compare_temperatures();
241 self.send_next_timeout(); 270 self.send_next_timeout();
242 } 271 }
243 272
244 /// Sets the next self-wakeup timeout 273 /// Sets the next self-wakeup timeout
245 fn send_next_timeout(&mut self) { 274 fn send_next_timeout(&mut self) {