Skip to content

Commit 564ffbc

Browse files
committed
wip: rpc
1 parent 770bceb commit 564ffbc

File tree

4 files changed

+173
-68
lines changed

4 files changed

+173
-68
lines changed

Cargo.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/improv-wifi/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ tracing = { version = "0.1.40", features = ["attributes"] }
1818
miette = { version = "7.2.0", optional = true }
1919
bluer = { version = "0.17.1", features = ["bluetoothd"] }
2020
tokio = { version = "1.37.0", features = ["sync", "rt"] }
21+
winnow = "0.6.8"
2122

2223
[features]
2324
miette = ["dep:miette"]

crates/improv-wifi/src/lib.rs

Lines changed: 112 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ use bluer::{
2525
gatt::local::{
2626
characteristic_control, service_control, Application, ApplicationHandle, Characteristic,
2727
CharacteristicControl, CharacteristicNotify, CharacteristicNotifyMethod,
28-
CharacteristicRead, Service, ServiceControl,
28+
CharacteristicRead, CharacteristicWrite, CharacteristicWriteMethod, Service,
29+
ServiceControl,
2930
},
3031
Adapter, Result, Uuid,
3132
};
@@ -37,6 +38,7 @@ use tokio::sync::{
3738
};
3839

3940
mod error;
41+
mod rpc;
4042
mod status;
4143

4244
const SERVICE_UUID: Uuid = Uuid::from_u128(0x00467768_6228_2272_4663_277478268000);
@@ -68,79 +70,60 @@ impl InnerState {
6870
}
6971

7072
#[derive(Clone, Debug)]
71-
pub struct State(Arc<RwLock<InnerState>>);
72-
73-
impl State {
74-
pub fn new(state: InnerState) -> Self {
75-
Self(Arc::new(RwLock::new(state)))
76-
}
73+
pub struct State<T> {
74+
inner: Arc<RwLock<InnerState>>,
75+
status_change_notifier: Sender<()>,
76+
error_change_notifier: Sender<()>,
77+
timeout: Option<Duration>,
78+
handler: T,
79+
}
7780

78-
pub async fn status(&self) -> Status {
79-
self.0.read().await.status
81+
impl<T> State<T>
82+
where
83+
T: WifiConfigurator,
84+
{
85+
async fn status(&self) -> Status {
86+
self.inner.read().await.status
8087
}
8188

82-
pub async fn last_error(&self) -> Option<Error> {
83-
self.0.read().await.last_error
89+
async fn last_error(&self) -> Option<Error> {
90+
self.inner.read().await.last_error
8491
}
8592

86-
pub async fn set_status(&self, new: Status) {
87-
self.0.write().await.status = new;
93+
async fn set_status(&self, new: Status) {
94+
self.inner.write().await.status = new;
8895
}
8996

90-
pub async fn set_last_error(&self, new: Option<Error>) {
91-
self.0.write().await.last_error = new;
97+
async fn set_last_error(&self, new: Option<Error>) {
98+
self.inner.write().await.last_error = new;
9299
}
93-
}
94-
95-
#[derive(Debug)]
96-
pub struct ImprovWifi<T> {
97-
timeout: Option<Duration>,
98-
state: State,
99-
handler: T,
100-
status_change_notifier: Sender<()>,
101-
error_change_notifier: Sender<()>,
102-
app: ApplicationHandle,
103-
service: ServiceControl,
104-
capabilities: CharacteristicControl,
105-
current_state: CharacteristicControl,
106-
error_state: CharacteristicControl,
107-
rpc_command: CharacteristicControl,
108-
rpc_result: CharacteristicControl,
109-
}
110-
111-
pub trait WifiConfigurator {
112-
fn can_authorize() -> bool;
113-
fn can_identify() -> bool;
114-
async fn provision(&mut self) -> std::result::Result<(), Error>;
115-
}
116100

117-
impl<T: WifiConfigurator> ImprovWifi<T> {
118-
async fn modify_status(&mut self, status: Status) {
119-
self.state.set_status(status).await;
101+
async fn modify_status(&self, status: Status) {
102+
self.set_status(status).await;
120103
self.status_change_notifier.send(()).ok();
121104
// TODO: pro-actively write to the client???
122105
}
123106

124-
pub async fn set_error(&mut self, error: Error) {
125-
self.state.set_last_error(Some(error)).await;
107+
pub async fn set_error(&self, error: Error) {
108+
self.set_last_error(Some(error)).await;
126109
self.error_change_notifier.send(()).ok();
127110
// TODO: pro-actively write to the client???
128111
}
129112

130-
pub async fn clear_error(&mut self) {
131-
self.state.set_last_error(None).await;
113+
pub async fn clear_error(&self) {
114+
self.set_last_error(None).await;
132115
self.error_change_notifier.send(()).ok();
133116
// TODO: pro-actively write to the client???
134117
}
135118

136-
pub async fn set_authorized(&mut self) {
137-
if self.state.status().await == Status::AuthorizationRequired {
119+
pub async fn set_authorized(&self) {
120+
if self.status().await == Status::AuthorizationRequired {
138121
self.modify_status(Status::Authorized).await;
139122
}
140123
}
141124

142125
pub async fn provision(&mut self) {
143-
if self.state.status().await != Status::Authorized {
126+
if self.status().await != Status::Authorized {
144127
self.set_error(Error::NotAuthorized).await;
145128
return;
146129
}
@@ -158,19 +141,80 @@ impl<T: WifiConfigurator> ImprovWifi<T> {
158141
self.modify_status(Status::Provisioned).await;
159142
}
160143

144+
#[tracing::instrument(level = "trace", skip(self))]
145+
pub async fn handle_raw_rpc(&self, value: Vec<u8>) {
146+
if let Err(error) = Self::inner_handle_raw_rpc(&self, value).await {
147+
self.set_error(error).await;
148+
} else {
149+
self.clear_error().await;
150+
}
151+
}
152+
153+
async fn inner_handle_raw_rpc(&self, value: Vec<u8>) -> std::result::Result<(), Error> {
154+
let rpc = rpc::Rpc::parse(&value).map_err(|err| {
155+
tracing::error!("Failed to parse RPC: {}", err);
156+
Error::InvalidRPC
157+
})?;
158+
159+
todo!()
160+
}
161+
162+
pub fn set_timeout(&mut self, timeout: Duration) {
163+
if T::can_authorize() {
164+
self.timeout = Some(timeout);
165+
}
166+
}
167+
}
168+
169+
#[derive(Debug)]
170+
pub struct ImprovWifi<T> {
171+
state: State<T>,
172+
app: ApplicationHandle,
173+
service: ServiceControl,
174+
capabilities: CharacteristicControl,
175+
current_state: CharacteristicControl,
176+
error_state: CharacteristicControl,
177+
rpc_command: CharacteristicControl,
178+
rpc_result: CharacteristicControl,
179+
}
180+
181+
pub trait WifiConfigurator: Clone + Send + Sync + 'static {
182+
fn can_authorize() -> bool;
183+
fn can_identify() -> bool;
184+
async fn provision(&mut self) -> std::result::Result<(), Error>;
185+
}
186+
187+
impl<T> ImprovWifi<T>
188+
where
189+
T: WifiConfigurator,
190+
{
191+
pub fn set_timeout(&mut self, timeout: Duration) {
192+
self.state.set_timeout(timeout);
193+
}
194+
161195
pub async fn install(adapter: &Adapter, handler: T) -> Result<Self> {
162196
let initial_status = if T::can_authorize() {
163197
Status::AuthorizationRequired
164198
} else {
165199
Status::Authorized
166200
};
167201

168-
let state = State::new(InnerState {
169-
status: initial_status,
170-
last_error: None,
171-
});
172202
let (status_change_notifier, _) = broadcast_channel(2);
173203
let (error_change_notifier, _) = broadcast_channel(2);
204+
let state = State {
205+
inner: Arc::new(RwLock::new(InnerState {
206+
status: initial_status,
207+
last_error: None,
208+
})),
209+
status_change_notifier: status_change_notifier.clone(),
210+
error_change_notifier: error_change_notifier.clone(),
211+
timeout: if initial_status == Status::AuthorizationRequired {
212+
Some(Duration::from_secs(60))
213+
} else {
214+
None
215+
},
216+
handler,
217+
};
174218

175219
let (service, service_handle) = service_control();
176220
let (capabilities_control, capabilities_handle) = characteristic_control();
@@ -289,6 +333,22 @@ impl<T: WifiConfigurator> ImprovWifi<T> {
289333
},
290334
Characteristic {
291335
uuid: CHARACTERISTIC_UUID_RPC_COMMAND,
336+
write: Some(CharacteristicWrite {
337+
write: true,
338+
write_without_response: true,
339+
method: CharacteristicWriteMethod::Fun(Box::new({
340+
let state = state.clone();
341+
move |value, _req| {
342+
let state = state.clone();
343+
Box::pin(async move {
344+
// not sure if the bluer interface here will stitch writes together, let's ignore that for now
345+
state.handle_raw_rpc(value).await;
346+
Ok(())
347+
})
348+
}
349+
})),
350+
..Default::default()
351+
}),
292352
control_handle: rpc_command_handle,
293353
..Default::default()
294354
},
@@ -305,15 +365,7 @@ impl<T: WifiConfigurator> ImprovWifi<T> {
305365
};
306366

307367
Ok(ImprovWifi {
308-
timeout: if initial_status == Status::AuthorizationRequired {
309-
Some(Duration::from_secs(60))
310-
} else {
311-
None
312-
},
313368
state,
314-
handler,
315-
status_change_notifier,
316-
error_change_notifier,
317369
app: adapter.serve_gatt_application(app).await?,
318370
service,
319371
capabilities: capabilities_control,
@@ -323,10 +375,4 @@ impl<T: WifiConfigurator> ImprovWifi<T> {
323375
rpc_result: rpc_result_control,
324376
})
325377
}
326-
327-
pub fn set_timeout(&mut self, timeout: Duration) {
328-
if T::can_authorize() {
329-
self.timeout = Some(timeout);
330-
}
331-
}
332378
}

crates/improv-wifi/src/rpc.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use winnow::{
2+
binary,
3+
combinator::{alt, fail},
4+
token::take,
5+
PResult, Parser,
6+
};
7+
8+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9+
pub struct Rpc<'data> {
10+
pub command: Command,
11+
pub data: &'data [u8],
12+
}
13+
14+
impl<'a> Rpc<'a> {
15+
fn inner_parse_data(input: &mut &'a [u8]) -> PResult<(&'a [u8], u8)> {
16+
let length = binary::u8(input)?;
17+
let data = take(length).parse_next(input)?;
18+
let checksum = binary::u8(input)?;
19+
Ok((data, checksum))
20+
}
21+
22+
fn parse_next(input: &mut &'a [u8]) -> PResult<Self> {
23+
let command = Command::parse(input)?;
24+
let data = Self::inner_parse_data
25+
.verify_map(|(data, checksum)| {
26+
if checksum == data.iter().fold(0, |acc, &x| acc.wrapping_add(x)) {
27+
Some(data)
28+
} else {
29+
None
30+
}
31+
})
32+
.parse_next(input)?;
33+
34+
Ok(Self { command, data })
35+
}
36+
37+
pub fn parse(input: &'a [u8]) -> Result<Self, String> {
38+
Self::parse_next.parse(input).map_err(|e| e.to_string())
39+
}
40+
}
41+
42+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43+
pub enum Command {
44+
SendWifiSettings = 0x01,
45+
Identify = 0x02,
46+
}
47+
48+
impl Command {
49+
fn parse(input: &mut &[u8]) -> PResult<Self> {
50+
alt((
51+
|input: &mut &[u8]| 0x01.parse_next(input).map(|_| Self::SendWifiSettings),
52+
|input: &mut &[u8]| 0x02.parse_next(input).map(|_| Self::Identify),
53+
fail,
54+
))
55+
.parse_next(input)
56+
}
57+
}

0 commit comments

Comments
 (0)