Compare commits
3 commits
ea1f1a3358
...
93861bb7ce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93861bb7ce | ||
|
|
f65547870e | ||
|
|
40f6c0dffd |
7 changed files with 84 additions and 18 deletions
17
.zed/settings.json
Normal file
17
.zed/settings.json
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"lsp": {
|
||||
"rust-analyzer": {
|
||||
"initialization_options": {
|
||||
"linkedProjects": [
|
||||
"device-state/Cargo.toml",
|
||||
"esp32/Cargo.toml",
|
||||
"pico/Cargo.toml",
|
||||
"simulator/Cargo.toml"
|
||||
],
|
||||
"check": {
|
||||
"workspace": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ type DeviceSocketMessage =
|
|||
type DeviceProxyEvent =
|
||||
| PartySocketEvent
|
||||
| { type: "device_connect_required" }
|
||||
| { type: "device_connected" };
|
||||
| { type: "device_connected"; username: string };
|
||||
|
||||
type DeviceQuizResponsePayload = {
|
||||
QuizResponse: number;
|
||||
|
|
@ -125,12 +125,24 @@ async function syncDeviceConnectionStatus(deviceId: string) {
|
|||
return;
|
||||
}
|
||||
|
||||
console.log("[device-socket] device claimed", deviceId, device.userId);
|
||||
const userRecord = await db.query.user.findFirst({
|
||||
where: { id: device.userId },
|
||||
});
|
||||
const username = userRecord?.name ?? device.userId;
|
||||
console.log(
|
||||
"[device-socket] device claimed",
|
||||
deviceId,
|
||||
device.userId,
|
||||
username,
|
||||
);
|
||||
await db
|
||||
.update(deviceConnection)
|
||||
.set({ lastSeen: new Date() })
|
||||
.where(eq(deviceConnection.id, deviceId));
|
||||
sendDeviceEvent(deviceId, { type: "device_connected" });
|
||||
sendDeviceEvent(deviceId, {
|
||||
type: "device_connected",
|
||||
username,
|
||||
});
|
||||
}
|
||||
|
||||
export async function claimDeviceForUser(deviceId: string, userId: string) {
|
||||
|
|
@ -296,7 +308,10 @@ export const deviceClaimApp = new Elysia()
|
|||
"/:deviceId/connect",
|
||||
async ({ user, params }) => {
|
||||
await claimDeviceForUser(params.deviceId, user.id);
|
||||
sendDeviceEvent(params.deviceId, { type: "device_connected" });
|
||||
sendDeviceEvent(params.deviceId, {
|
||||
type: "device_connected",
|
||||
username: user.name,
|
||||
});
|
||||
return { ok: true, deviceId: params.deviceId, userId: user.id };
|
||||
},
|
||||
{ auth: true },
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ type ErrorEvent = {
|
|||
|
||||
type DeviceLifecycleEvent =
|
||||
| { type: "device_connect_required" }
|
||||
| { type: "device_connected" };
|
||||
| { type: "device_connected"; username: string };
|
||||
|
||||
type PartySocketEvent =
|
||||
| PartyStatusEvent
|
||||
|
|
@ -262,7 +262,7 @@ function handleApiMessage(e: MessageEvent) {
|
|||
|
||||
if (event.type === "device_connected") {
|
||||
console.log("Writing waiting-for-party", message.deviceId);
|
||||
writeProxyOutput(socket, "WaitingForParty");
|
||||
writeProxyOutput(socket, { WaitingForParty: event.username });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ pub struct QuestionDataNet<'a> {
|
|||
#[derive(Deserialize)]
|
||||
pub enum ProxyOutput<'a> {
|
||||
ConnectPrompt(&'a str),
|
||||
WaitingForParty,
|
||||
WaitingForParty(&'a str),
|
||||
Question(QuestionDataNet<'a>),
|
||||
Results,
|
||||
Error(&'a str),
|
||||
|
|
@ -87,6 +87,7 @@ impl WheelData {
|
|||
pub struct DeviceState {
|
||||
view: ViewState,
|
||||
device_id: Option<OwnedStr<64>>,
|
||||
connected_user_name: Option<OwnedStr<64>>,
|
||||
question: Option<QuestionData>,
|
||||
wheel: WheelData,
|
||||
last_index: usize,
|
||||
|
|
@ -98,6 +99,7 @@ impl DeviceState {
|
|||
Self {
|
||||
view: ViewState::Loading,
|
||||
device_id: None,
|
||||
connected_user_name: None,
|
||||
question: None,
|
||||
wheel: WheelData::empty(),
|
||||
last_index: 0,
|
||||
|
|
@ -107,6 +109,7 @@ impl DeviceState {
|
|||
|
||||
pub fn reset(&mut self) {
|
||||
self.view = ViewState::Loading;
|
||||
self.connected_user_name = None;
|
||||
self.question = None;
|
||||
self.wheel = WheelData::empty();
|
||||
self.last_index = 0;
|
||||
|
|
@ -114,6 +117,7 @@ impl DeviceState {
|
|||
}
|
||||
|
||||
pub fn reconnecting(&mut self) {
|
||||
self.connected_user_name = None;
|
||||
self.question = None;
|
||||
self.wheel = WheelData::empty();
|
||||
self.view = ViewState::Reconnecting;
|
||||
|
|
@ -144,7 +148,14 @@ impl DeviceState {
|
|||
self.question = None;
|
||||
self.view = ViewState::ConnectPrompt;
|
||||
}
|
||||
ProxyOutput::WaitingForParty => {
|
||||
ProxyOutput::WaitingForParty(user_name) => {
|
||||
let mut owned_user_name = OwnedStr::new();
|
||||
for char in user_name.chars() {
|
||||
if owned_user_name.try_push(char).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.connected_user_name = Some(owned_user_name);
|
||||
self.question = None;
|
||||
self.view = ViewState::WaitingForParty;
|
||||
}
|
||||
|
|
@ -215,9 +226,16 @@ impl DeviceState {
|
|||
}
|
||||
|
||||
if self.view == ViewState::WaitingForParty {
|
||||
let user_name = self.connected_user_name.as_ref()?;
|
||||
let mut display_name: OwnedStr<16> = OwnedStr::new();
|
||||
for char in user_name.as_str().chars() {
|
||||
if display_name.try_push(char).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Some((
|
||||
OwnedStr::from_str("Connected").unwrap(),
|
||||
OwnedStr::from_str("Waiting party").unwrap(),
|
||||
display_name,
|
||||
OwnedStr::from_str("Awaiting party").unwrap(),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -227,8 +245,10 @@ impl DeviceState {
|
|||
|
||||
let question = self.question.as_ref()?;
|
||||
let title_line = if question.text.len() > 16 {
|
||||
self.title_offset %= question.text.len() - 16;
|
||||
OwnedStr::from_str(&question.text[self.title_offset..self.title_offset + 16]).unwrap()
|
||||
// overscroll, should show spaces after the end
|
||||
self.title_offset %= question.text.len() - 31;
|
||||
let end = usize::min(self.title_offset + 16, question.text.len());
|
||||
OwnedStr::from_str(&question.text[self.title_offset..end]).unwrap()
|
||||
} else {
|
||||
OwnedStr::from_str(&question.text).unwrap()
|
||||
};
|
||||
|
|
@ -407,15 +427,15 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn parses_and_renders_waiting_for_party() {
|
||||
let data = parse_proxy_output(r#""WaitingForParty""#).unwrap();
|
||||
let data = parse_proxy_output(r#"{"WaitingForParty":"User"}"#).unwrap();
|
||||
let mut state = DeviceState::new();
|
||||
|
||||
state.apply_proxy_output(data);
|
||||
|
||||
assert_eq!(state.view_state(), ViewState::WaitingForParty);
|
||||
let (line1, line2) = state.render_lines().unwrap();
|
||||
assert_eq!(line1.as_str(), "Connected");
|
||||
assert_eq!(line2.as_str(), "Waiting party");
|
||||
assert_eq!(line1.as_str(), "User");
|
||||
assert_eq!(line2.as_str(), "Awaiting party");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::string::String;
|
||||
use device_state::DeviceState;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_futures::select::{Either, select};
|
||||
|
|
@ -133,8 +135,9 @@ pub async fn tcp_write_loop(
|
|||
#[embassy_executor::task]
|
||||
pub async fn main_loop() {
|
||||
println!("Main loop started");
|
||||
let mut last = (String::new(), String::new());
|
||||
loop {
|
||||
embassy_time::Timer::after_millis(50).await;
|
||||
embassy_time::Timer::after_millis(100).await;
|
||||
let mut state = STATE.lock().await;
|
||||
state.tick();
|
||||
let lines = state.render_lines();
|
||||
|
|
@ -143,7 +146,15 @@ pub async fn main_loop() {
|
|||
let Some((title_line, second_line)) = lines else {
|
||||
continue;
|
||||
};
|
||||
println!("lcd: {} {}", title_line, second_line);
|
||||
let rendered = (
|
||||
title_line.as_str().to_owned(),
|
||||
second_line.as_str().to_owned(),
|
||||
);
|
||||
if rendered == last {
|
||||
continue;
|
||||
}
|
||||
last = rendered;
|
||||
println!("lcd1: {}\nlcd2: {}", title_line, second_line);
|
||||
screen::overwrite_lcd(&title_line, &second_line).await;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use esp_radio::wifi::sta::StationConfig;
|
|||
use esp_radio::wifi::{Config, ControllerConfig, scan::ScanConfig};
|
||||
|
||||
use crate::screen::overwrite_lcd;
|
||||
use crate::{WIFI_NETWORK, WIFI_PASSWORD};
|
||||
use crate::{WIFI_NETWORK, WIFI_PASSWORD, main_loop};
|
||||
use crate::{buffer::wait_for_config, tcp_read_loop, tcp_write_loop};
|
||||
use crate::{reconnecting_state, reset_state};
|
||||
|
||||
|
|
@ -68,6 +68,7 @@ pub async fn network_setup_task(
|
|||
seed,
|
||||
);
|
||||
spawner.spawn(net_task(runner).expect("spawn net task"));
|
||||
spawner.spawn(main_loop().expect("spawn main loop"));
|
||||
|
||||
loop {
|
||||
loop {
|
||||
|
|
|
|||
|
|
@ -89,7 +89,9 @@ pub async fn lcd_display_task(config: LcdConfig) {
|
|||
for byte in &buffer.line1 {
|
||||
lcd.write(*byte);
|
||||
}
|
||||
Timer::after_micros(500).await;
|
||||
lcd.set_position(0, 1);
|
||||
Timer::after_micros(500).await;
|
||||
for byte in &buffer.line2 {
|
||||
lcd.write(*byte);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue