1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
use futures_util::future::select_all;
use tokio::select;
use tokio::time::{Duration,delay_for};
use tokio::sync::watch;
use apigpio::{Pin,Connection,GpioChange,Subscription};
use apigpio::GpioMode::*;
use apigpio::Level::*;
use crate::tasktrack;
use anyhow::Context;
pub trait Debounceable : Copy + Clone + Send + Sync {
type Spec;
fn pins(spec : &Self::Spec) -> &[Pin];
fn interpret(pin_states : &[GpioChange]) -> Option<Self>;
fn delay() -> Duration;
fn description() -> String;
fn equivalent(&self, other : &Self) -> bool;
}
type E = anyhow::Error;
async fn changed_internal<T : Debounceable>
(pins : &mut Vec<Subscription>, vals : &mut Vec<GpioChange>)
-> Result<Option<T>, E>
{
Ok(loop {
let mut futs = Vec::new();
for p in pins.iter_mut() {
let fut = p.recv();
let fut = Box::pin(fut);
futs.push(fut);
}
if let (Some(got), ix, _remain) = select_all(futs).await {
vals[ix] = got;
break <T as Debounceable>::interpret(&vals);
}
})
}
pub async fn new_debouncer
<T : 'static + Debounceable>
(pi : Connection, tt : &mut tasktrack::Tracker,
spec : &T::Spec, initial : Option<T>)
-> Result<watch::Receiver<T>,E>
{
let specs = <T as Debounceable>::pins(spec);
let mut pins = Vec::with_capacity(specs.len());
for &p in specs {
let mut pud = pi.set_pull_up_down(p,Some(H)).await;
if pi.get_mode(p).await? != Input {
pi.set_mode(p,Input).await.context("set mode").context(p)?;
} else {
if let e @ Err(apigpio::Error::Pi(
apigpio::constants::PI_NOT_PERMITTED)
) = pud {
println!("warning: pin {}: failed to set pull-up: {:?}", p, &e);
pud = Ok(());
}
}
pud.context("set pullup").context(p)?;
let sub = pi.notify_subscribe(p,true,false).await?;
pins.push(sub);
}
let mut vals : Vec<_> = pins.iter().map(|p|{ *p.borrow() }).collect();
let initial = match initial {
Some(supplied) => supplied,
None => loop {
let got = changed_internal(&mut pins, &mut vals).await?;
if let Some(good) = got { break good }
},
};
let (osender, oreceiver) = watch::channel(initial);
let duration = T::delay();
tt.spawn(format!("debounce tracker {:}", T::description()), async move {
let mut sent = initial;
'forever : loop {
let stable : Option<T> = 'stabilise : loop {
let mut seen = changed_internal(&mut pins, &mut vals).await?;
'_reread : loop {
select!{
p = changed_internal(&mut pins, &mut vals) => { seen = p? },
_ = delay_for(duration) => { break 'stabilise seen }
}
}
};
if let Some(good) = stable {
if !good.equivalent(&sent) {
if osender.broadcast(good).is_err() { break 'forever }
sent = good;
}
}
}
Ok(())
});
Ok(oreceiver)
}