mirror of
https://github.com/Djeeberjr/fw-anwesenheit.git
synced 2025-10-13 15:06:39 +00:00
fixed webserver not working
- shared store in main - multiple tasks for webserver
This commit is contained in:
parent
8cbdf834a1
commit
082f1faba9
37
src/main.rs
37
src/main.rs
@ -3,11 +3,12 @@
|
|||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
#![feature(impl_trait_in_assoc_type)]
|
#![feature(impl_trait_in_assoc_type)]
|
||||||
|
|
||||||
|
use alloc::rc::Rc;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_net::Stack;
|
use embassy_net::Stack;
|
||||||
use embassy_sync::{
|
use embassy_sync::{
|
||||||
blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex},
|
blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex},
|
||||||
channel::Channel,
|
mutex::Mutex,
|
||||||
pubsub::{
|
pubsub::{
|
||||||
PubSubChannel, Publisher,
|
PubSubChannel, Publisher,
|
||||||
WaitResult::{Lagged, Message},
|
WaitResult::{Lagged, Message},
|
||||||
@ -20,7 +21,11 @@ use esp_hal::{gpio::InputConfig, peripherals};
|
|||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
use static_cell::make_static;
|
use static_cell::make_static;
|
||||||
|
|
||||||
use crate::store::{Date, IDStore, TallyID};
|
use crate::{
|
||||||
|
init::sd_card::SDCardPersistence,
|
||||||
|
store::{Date, IDStore, TallyID},
|
||||||
|
webserver::start_webserver,
|
||||||
|
};
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
@ -28,30 +33,34 @@ mod drivers;
|
|||||||
mod feedback;
|
mod feedback;
|
||||||
mod init;
|
mod init;
|
||||||
mod store;
|
mod store;
|
||||||
//mod webserver;
|
mod webserver;
|
||||||
|
|
||||||
static FEEDBACK_STATE: Signal<CriticalSectionRawMutex, feedback::FeedbackState> = Signal::new();
|
static FEEDBACK_STATE: Signal<CriticalSectionRawMutex, feedback::FeedbackState> = Signal::new();
|
||||||
|
|
||||||
type TallyChannel = PubSubChannel<NoopRawMutex, TallyID, 8, 2, 1>;
|
type TallyChannel = PubSubChannel<NoopRawMutex, TallyID, 8, 2, 1>;
|
||||||
type TallyPublisher = Publisher<'static, NoopRawMutex, TallyID, 8, 2, 1>;
|
type TallyPublisher = Publisher<'static, NoopRawMutex, TallyID, 8, 2, 1>;
|
||||||
|
type UsedStore = IDStore<SDCardPersistence>;
|
||||||
|
|
||||||
#[esp_hal_embassy::main]
|
#[esp_hal_embassy::main]
|
||||||
async fn main(mut spawner: Spawner) {
|
async fn main(mut spawner: Spawner) {
|
||||||
let (uart_device, stack, _i2c, _led, buzzer_gpio, sd_det_gpio, persistence_layer) =
|
let (uart_device, stack, _i2c, _led, buzzer_gpio, sd_det_gpio, persistence_layer) =
|
||||||
init::hardware::hardware_init(&mut spawner).await;
|
init::hardware::hardware_init(&mut spawner).await;
|
||||||
|
|
||||||
wait_for_stack_up(stack).await;
|
|
||||||
|
|
||||||
info!("Starting up...");
|
info!("Starting up...");
|
||||||
|
|
||||||
let chan: &'static mut TallyChannel = make_static!(PubSubChannel::new());
|
|
||||||
|
|
||||||
//start_webserver(&mut spawner, stack);
|
|
||||||
|
|
||||||
let publisher = chan.publisher().unwrap();
|
|
||||||
|
|
||||||
let mut rtc = drivers::rtc::RTCClock::new(_i2c).await;
|
let mut rtc = drivers::rtc::RTCClock::new(_i2c).await;
|
||||||
|
|
||||||
|
let store: UsedStore = IDStore::new_from_storage(persistence_layer).await;
|
||||||
|
let shared_store = Rc::new(Mutex::new(store));
|
||||||
|
|
||||||
|
let chan: &'static mut TallyChannel = make_static!(PubSubChannel::new());
|
||||||
|
let publisher = chan.publisher().unwrap();
|
||||||
|
let mut sub = chan.subscriber().unwrap();
|
||||||
|
|
||||||
|
wait_for_stack_up(stack).await;
|
||||||
|
|
||||||
|
start_webserver(&mut spawner, stack, shared_store.clone());
|
||||||
|
|
||||||
/****************************** Spawning tasks ***********************************/
|
/****************************** Spawning tasks ***********************************/
|
||||||
debug!("spawing NFC reader task...");
|
debug!("spawing NFC reader task...");
|
||||||
spawner.must_spawn(drivers::nfc_reader::rfid_reader_task(
|
spawner.must_spawn(drivers::nfc_reader::rfid_reader_task(
|
||||||
@ -66,13 +75,9 @@ async fn main(mut spawner: Spawner) {
|
|||||||
spawner.must_spawn(sd_detect_task(sd_det_gpio));
|
spawner.must_spawn(sd_detect_task(sd_det_gpio));
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
let mut sub = chan.subscriber().unwrap();
|
|
||||||
|
|
||||||
debug!("everything spawned");
|
debug!("everything spawned");
|
||||||
FEEDBACK_STATE.signal(feedback::FeedbackState::Startup);
|
FEEDBACK_STATE.signal(feedback::FeedbackState::Startup);
|
||||||
|
|
||||||
let mut store = IDStore::new_from_storage(persistence_layer).await;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let wait_result = sub.next_message().await;
|
let wait_result = sub.next_message().await;
|
||||||
match wait_result {
|
match wait_result {
|
||||||
@ -81,7 +86,7 @@ async fn main(mut spawner: Spawner) {
|
|||||||
debug!("Got message: {msg:?}");
|
debug!("Got message: {msg:?}");
|
||||||
|
|
||||||
let day: Date = rtc.get_date().await;
|
let day: Date = rtc.get_date().await;
|
||||||
let added = store.add_id(msg, day).await;
|
let added = shared_store.lock().await.add_id(msg, day).await;
|
||||||
|
|
||||||
if added {
|
if added {
|
||||||
FEEDBACK_STATE.signal(feedback::FeedbackState::Ack);
|
FEEDBACK_STATE.signal(feedback::FeedbackState::Ack);
|
||||||
|
66
src/webserver/api.rs
Normal file
66
src/webserver/api.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use alloc::string::String;
|
||||||
|
use picoserve::{
|
||||||
|
extract::{Json, State},
|
||||||
|
response::{self, IntoResponse},
|
||||||
|
};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
store::{Name, TallyID},
|
||||||
|
webserver::app::AppState,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct NewMapping {
|
||||||
|
id: String,
|
||||||
|
name: Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hex_string_to_tally_id(s: &str) -> Option<TallyID> {
|
||||||
|
let bytes = s.as_bytes();
|
||||||
|
if bytes.len() != 24 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut out = [0u8; 12];
|
||||||
|
for i in 0..12 {
|
||||||
|
let hi = hex_val(bytes[2 * i])?;
|
||||||
|
let lo = hex_val(bytes[2 * i + 1])?;
|
||||||
|
out[i] = (hi << 4) | lo;
|
||||||
|
}
|
||||||
|
Some(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hex_val(b: u8) -> Option<u8> {
|
||||||
|
match b {
|
||||||
|
b'0'..=b'9' => Some(b - b'0'),
|
||||||
|
b'a'..=b'f' => Some(b - b'a' + 10),
|
||||||
|
b'A'..=b'F' => Some(b - b'A' + 10),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #[get("/api/idevent")]
|
||||||
|
* #[get("/api/csv")]
|
||||||
|
* #[get("/api/mapping")]
|
||||||
|
* #[post("/api/mapping", format = "json", data = "<new_mapping>")]
|
||||||
|
* struct NewMapping {
|
||||||
|
* id: String,
|
||||||
|
* name: Name,
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub async fn get_mapping(State(state): State<AppState>) -> impl IntoResponse {
|
||||||
|
let store = state.store.lock().await;
|
||||||
|
response::Json(store.mapping.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn add_mapping(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Json(data): Json<NewMapping>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let mut store = state.store.lock().await;
|
||||||
|
let tally_id = hex_string_to_tally_id(&data.id).unwrap();
|
||||||
|
store.mapping.add_mapping(tally_id, data.name);
|
||||||
|
}
|
31
src/webserver/app.rs
Normal file
31
src/webserver/app.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use alloc::rc::Rc;
|
||||||
|
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex};
|
||||||
|
use picoserve::{AppWithStateBuilder, routing::get};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
webserver::{
|
||||||
|
api::{add_mapping, get_mapping},
|
||||||
|
assets::Assets,
|
||||||
|
}, UsedStore,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AppState {
|
||||||
|
pub store: Rc<Mutex<CriticalSectionRawMutex, UsedStore>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AppProps;
|
||||||
|
|
||||||
|
impl AppWithStateBuilder for AppProps {
|
||||||
|
type State = AppState;
|
||||||
|
type PathRouter = impl picoserve::routing::PathRouter<AppState>;
|
||||||
|
|
||||||
|
fn build_app(self) -> picoserve::Router<Self::PathRouter, AppState> {
|
||||||
|
picoserve::Router::from_service(Assets)
|
||||||
|
.route("/api/mapping", get(get_mapping).post(add_mapping))
|
||||||
|
// .route(
|
||||||
|
// "/api/idevent",
|
||||||
|
// get(move || response::EventStream(Events(self.chan))),
|
||||||
|
// )
|
||||||
|
}
|
||||||
|
}
|
@ -1,54 +1,57 @@
|
|||||||
|
use alloc::rc::Rc;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_net::Stack;
|
use embassy_net::Stack;
|
||||||
|
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, mutex::Mutex};
|
||||||
use embassy_time::Duration;
|
use embassy_time::Duration;
|
||||||
use picoserve::{AppBuilder, AppRouter, routing::get};
|
use picoserve::{AppRouter, AppWithStateBuilder};
|
||||||
use static_cell::make_static;
|
use static_cell::make_static;
|
||||||
|
|
||||||
mod assets;
|
use crate::{
|
||||||
|
UsedStore,
|
||||||
|
init::network::NETWORK_STACK_SIZE,
|
||||||
|
webserver::app::{AppProps, AppState},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn start_webserver(spawner: &mut Spawner, stack: Stack<'static>) {
|
mod assets;
|
||||||
|
// mod sse;
|
||||||
|
mod api;
|
||||||
|
mod app;
|
||||||
|
|
||||||
|
pub fn start_webserver(
|
||||||
|
spawner: &mut Spawner,
|
||||||
|
stack: Stack<'static>,
|
||||||
|
store: Rc<Mutex<CriticalSectionRawMutex, UsedStore>>,
|
||||||
|
) {
|
||||||
let app = make_static!(AppProps.build_app());
|
let app = make_static!(AppProps.build_app());
|
||||||
|
|
||||||
|
let state = make_static!(AppState { store });
|
||||||
|
|
||||||
let config = make_static!(picoserve::Config::new(picoserve::Timeouts {
|
let config = make_static!(picoserve::Config::new(picoserve::Timeouts {
|
||||||
start_read_request: Some(Duration::from_secs(5)),
|
start_read_request: Some(Duration::from_secs(5)),
|
||||||
persistent_start_read_request: Some(Duration::from_secs(1)),
|
persistent_start_read_request: Some(Duration::from_secs(5)),
|
||||||
read_request: Some(Duration::from_secs(1)),
|
read_request: Some(Duration::from_secs(5)),
|
||||||
write: Some(Duration::from_secs(1)),
|
write: Some(Duration::from_secs(5)),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let _ = spawner.spawn(webserver_task(0, stack, app, config));
|
for task_id in 0..NETWORK_STACK_SIZE {
|
||||||
}
|
spawner.must_spawn(webserver_task(task_id, stack, app, config, state));
|
||||||
|
|
||||||
struct AppProps;
|
|
||||||
|
|
||||||
impl AppBuilder for AppProps {
|
|
||||||
type PathRouter = impl picoserve::routing::PathRouter;
|
|
||||||
|
|
||||||
fn build_app(self) -> picoserve::Router<Self::PathRouter> {
|
|
||||||
picoserve::Router::from_service(assets::Assets).route("/api/a", get(async move || "Hello"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task(pool_size = NETWORK_STACK_SIZE)]
|
||||||
async fn webserver_task(
|
async fn webserver_task(
|
||||||
id: usize,
|
task_id: usize,
|
||||||
stack: embassy_net::Stack<'static>,
|
stack: embassy_net::Stack<'static>,
|
||||||
app: &'static AppRouter<AppProps>,
|
app: &'static AppRouter<AppProps>,
|
||||||
config: &'static picoserve::Config<Duration>,
|
config: &'static picoserve::Config<Duration>,
|
||||||
|
state: &'static AppState,
|
||||||
) -> ! {
|
) -> ! {
|
||||||
let mut tcp_rx_buffer = [0u8; 1024];
|
let mut tcp_rx_buffer = [0u8; 1024];
|
||||||
let mut tcp_tx_buffer = [0u8; 1024];
|
let mut tcp_tx_buffer = [0u8; 1024];
|
||||||
let mut http_buffer = [0u8; 2048];
|
let mut http_buffer = [0u8; 2048];
|
||||||
|
|
||||||
picoserve::listen_and_serve(
|
picoserve::Server::new(&app.shared().with_state(state), config, &mut http_buffer)
|
||||||
id,
|
.listen_and_serve(task_id, stack, 80, &mut tcp_rx_buffer, &mut tcp_tx_buffer)
|
||||||
app,
|
.await
|
||||||
config,
|
.into_never()
|
||||||
stack,
|
|
||||||
80,
|
|
||||||
&mut tcp_rx_buffer,
|
|
||||||
&mut tcp_tx_buffer,
|
|
||||||
&mut http_buffer,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
29
src/webserver/sse.rs
Normal file
29
src/webserver/sse.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use embassy_time::{Duration, Timer};
|
||||||
|
use log::warn;
|
||||||
|
use picoserve::response;
|
||||||
|
|
||||||
|
pub struct Events(pub TallySubscriber);
|
||||||
|
|
||||||
|
impl response::sse::EventSource for Events {
|
||||||
|
async fn write_events<W: picoserve::io::Write>(
|
||||||
|
mut self,
|
||||||
|
mut writer: response::sse::EventWriter<W>,
|
||||||
|
) -> Result<(), W::Error> {
|
||||||
|
loop {
|
||||||
|
let timeout = Timer::after(Duration::from_secs(15));
|
||||||
|
let sel = embassy_futures::select::select(self.0.next_message(), timeout);
|
||||||
|
|
||||||
|
match sel.await {
|
||||||
|
embassy_futures::select::Either::First(msg) => match msg {
|
||||||
|
embassy_sync::pubsub::WaitResult::Message(id) => {
|
||||||
|
writer.write_event("msg", id.to_string().as_str()).await?
|
||||||
|
}
|
||||||
|
embassy_sync::pubsub::WaitResult::Lagged(_) => {
|
||||||
|
warn!("SSE subscriber got lagged");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
embassy_futures::select::Either::Second(_) => writer.write_keepalive().await?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user