door/server/src/main.rs
2024-12-06 01:20:30 -05:00

220 lines
6.1 KiB
Rust

use std::{sync::Arc, time::Duration};
use axum::{extract::State, http::StatusCode, response::Html, routing::{get, post}, serve, Form, Router};
use common::{Name, Request};
use postcard::to_allocvec;
use tokio::{fs, io::{AsyncReadExt, AsyncWriteExt}, net::TcpStream, sync::{RwLock, Semaphore}, time::{self, sleep, Instant}};
use anyhow::{Ok, Result};
use tracing::{error, info, trace, Level};
use tracing_subscriber::{filter, layer::{Filter, SubscriberExt}, util::SubscriberInitExt, Layer};
const FISH: &'static str = "169.254.2.1:1234";
#[derive(Clone)]
struct AppState {
fish: Arc<Semaphore>,
last_badge: Arc<RwLock<(u64,Instant)>>,
}
#[tokio::main]
async fn main() -> Result<()>{
let log = fs::OpenOptions::new().append(true).create(true).open("john.log").await?;
let (non_blocking, _guard) = tracing_appender::non_blocking(log.into_std().await);
let filter = filter::Targets::new()
.with_target("server", Level::INFO);
let stdout = tracing_subscriber::fmt::layer()
.compact()
.with_file(true)
.with_target(true);
let log = tracing_subscriber::fmt::layer()
.compact()
.with_file(false)
.with_target(true)
.with_writer(non_blocking)
.with_filter(filter);
let reg = tracing_subscriber::registry()
.with(stdout)
.with(log);
reg.try_init()?;
let fish = Arc::new(Semaphore::new(1));
let last_badge = Arc::new(RwLock::new((0u64,Instant::now())));
info!("starting");
let app: Router = Router::new()
.route("/", get(root))
.route("/phil", get(phil))
.route("/recent", get(recent_badge))
.route("/setLast", post(set_last))
//.route("/map", post(set_badge))
.with_state(AppState {
fish: fish.clone(),
last_badge: last_badge.clone()
});
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
tokio::spawn(async {serve(listener, app).await.unwrap()});
let fish_ = fish.clone();
tokio::spawn(async move {
loop {
match get_recent_badge(fish_.clone()).await {
Result::Ok(badge) => {
*last_badge.write().await = (badge, Instant::now());
},
Err(e) => {
trace!("badge read fail: {e}");
}
}
sleep(Duration::from_secs(1)).await;
}
});
{
let _permit = fish.acquire().await?;
let mut conn = TcpStream::connect(FISH).await?;
let req = Request::RecentBadge;
conn.write_all(&to_allocvec(&req)?).await?;
conn.flush().await?;
let badge = conn.read_u64().await?;
info!("read: {badge}");
if badge != 0 {
let req = Request::SetBadge(common::Name::Evan, badge);
conn.write_all(&to_allocvec(&req)?).await?;
conn.flush().await?;
}
let req = Request::SetVolume(30);
conn.write_all(&to_allocvec(&req)?).await?;
}
loop {
sleep(Duration::from_secs(2)).await;
}
}
async fn get_recent_badge(fish: Arc<Semaphore>) -> Result<u64> {
let _permit = fish.acquire().await?;
let mut conn = TcpStream::connect(FISH).await?;
let req = Request::RecentBadge;
conn.write_all(&to_allocvec(&req)?).await?;
conn.flush().await?;
let badge = conn.read_u64().await?;
if badge != 0 {
info!("new badge: {badge:#0x}");
Ok(badge)
} else {
Err(anyhow::anyhow!("no badge present"))
}
}
async fn root(state: State<AppState>) -> Html<String> {
let (last_badge, time) = *state.last_badge.read().await;
let time = time.elapsed();
let mut page = format!(r#"
<p>
most recent badge {last_badge:#0x}<br>
{time:?} ago
</p>
<h1>set last badge</h1>
"#);
for name in [
"Tess",
"Amaia",
"Prueky",
"David",
"Nathaniel",
"Thia",
"Michael",
"Zoey",
] {
page.push_str(&format!(r#"
<form action="setLast" method="post" id="myForm">
<button style="font-size:40px;" type="submit" name="name", value="{name}">{name}</button>
</form>
"#));
}
Html::from(page)
}
#[derive(serde::Deserialize)]
struct NameForm {
name: String,
}
async fn set_last(
state: State<AppState>,
Form(input): Form<NameForm>) -> Html<String> {
let name_e = match input.name.as_str() {
"Tess" => Name::Tess,
"Amaia" => Name::Amaia,
"Prueky" => Name::Prueky,
"David" => Name::David,
"Nathaniel" => Name::Nathaniel,
"Thia" => Name::Thia,
"Michael" => Name::Michael,
"Zoey" => Name::Zoey,
_ => Name::Unknown,
};
let (last_badge, _) = *state.last_badge.read().await;
info!("setting {} ({name_e:?}) to {last_badge:#0x}", input.name);
let _fish = state.fish.acquire().await.unwrap();
let mut conn = TcpStream::connect(FISH).await.unwrap();
if last_badge == 0 {
return Html::from("<h1>failed, no badge scanned</h1>".to_string());
}
let req = Request::SetBadge(name_e, last_badge);
conn.write_all(&to_allocvec(&req).unwrap()).await.unwrap();
conn.flush().await.unwrap();
Html::from(format!("<p>{last_badge:#0x} is now {}</p></br><a href=\"/\">back</a>", input.name))
}
async fn recent_badge(state: State<AppState>) -> String {
let (last_badge, time) = *state.last_badge.read().await;
format!("id: {last_badge:#0x}, scanned {:?} ago", time.elapsed())
}
async fn phil(state: State<AppState>) -> String {
let _fish = state.fish.acquire().await.unwrap();
let (last_badge, time) = *state.last_badge.read().await;
info!("id: {last_badge:#0x}, scanned {:?} ago", time.elapsed());
let mut conn = TcpStream::connect(FISH).await.unwrap();
if last_badge != 0 {
let req = Request::SetBadge(common::Name::Phil, last_badge);
conn.write_all(&to_allocvec(&req).unwrap()).await.unwrap();
conn.flush().await.unwrap();
}
String::from(format!("{last_badge:#0x} is now phil"))
}