ttt backend api kinda works
This commit is contained in:
parent
3782686d4b
commit
5a9ac5c436
@ -1,3 +1,6 @@
|
|||||||
|
use heapless::Vec;
|
||||||
|
use pico_website::unwrap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
apps::Content,
|
apps::Content,
|
||||||
socket::{HttpRequestType, HttpResCode},
|
socket::{HttpRequestType, HttpResCode},
|
||||||
@ -22,17 +25,50 @@ impl App for IndexApp {
|
|||||||
"html",
|
"html",
|
||||||
Some(include_str!("./index.html").into()),
|
Some(include_str!("./index.html").into()),
|
||||||
),
|
),
|
||||||
"/ttt" => (
|
path => {
|
||||||
HttpResCode::Ok,
|
let (path, args) = path.split_once('?').unwrap_or((path, ""));
|
||||||
"html",
|
let mut team = None;
|
||||||
Some(include_str!("ttt.html").into()),
|
for arg in args.split('&') {
|
||||||
),
|
match arg.split_once('=') {
|
||||||
"/ttt.js" => (
|
Some(("team", "0")) => team = Some("0"),
|
||||||
HttpResCode::Ok,
|
Some(("team", "1")) => team = Some("1"),
|
||||||
"javascript",
|
_ => {}
|
||||||
Some(include_str!("ttt.js").into()),
|
}
|
||||||
),
|
}
|
||||||
_ => (HttpResCode::NotFound, "", None),
|
if path == "/ttt" {
|
||||||
|
let Some(team) = team else {
|
||||||
|
return (HttpResCode::BadRequest, "", None);
|
||||||
|
};
|
||||||
|
let html = include_str!("ttt.html");
|
||||||
|
let mut content = Vec::new();
|
||||||
|
let r: Result<(), &str> = try {
|
||||||
|
let (html1, html2) = html.split_once("/ttt.js").ok_or("")?;
|
||||||
|
|
||||||
|
content.push(html1)?;
|
||||||
|
content.push("/ttt.js?team=")?;
|
||||||
|
content.push(team)?;
|
||||||
|
content.push(html2)?;
|
||||||
|
};
|
||||||
|
unwrap(r).await;
|
||||||
|
(HttpResCode::Ok, "html", Some(Content(content)))
|
||||||
|
} else if path == "/ttt.js" {
|
||||||
|
let Some(team) = team else {
|
||||||
|
return (HttpResCode::BadRequest, "", None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut content = Vec::new();
|
||||||
|
let r: Result<(), &str> = try {
|
||||||
|
content.push("const team = ")?;
|
||||||
|
content.push(team)?;
|
||||||
|
content.push(";\n")?;
|
||||||
|
content.push(include_str!("ttt.js"))?;
|
||||||
|
};
|
||||||
|
unwrap(r).await;
|
||||||
|
(HttpResCode::Ok, "javascript", Some(Content(content)))
|
||||||
|
} else {
|
||||||
|
(HttpResCode::NotFound, "", None)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
<h3 id="team"></h3>
|
<h3 id="team"></h3>
|
||||||
<!-- <div class="cell" style="background-color:"></div> -->
|
<!-- <div class="cell" style="background-color:"></div> -->
|
||||||
<div id="grid">
|
<div id="grid">
|
||||||
<div class="cell" id="cell0"></div>
|
<!-- <div class="cell" id="cell0"></div>
|
||||||
<div class="cell" id="cell1"></div>
|
<div class="cell" id="cell1"></div>
|
||||||
<div class="cell" id="cell2"></div>
|
<div class="cell" id="cell2"></div>
|
||||||
<div class="cell" id="cell3"></div>
|
<div class="cell" id="cell3"></div>
|
||||||
@ -38,7 +38,7 @@
|
|||||||
<div class="cell" id="cell6"></div>
|
<div class="cell" id="cell6"></div>
|
||||||
<div class="cell" id="cell7"></div>
|
<div class="cell" id="cell7"></div>
|
||||||
<div class="cell" id="cell8"></div>
|
<div class="cell" id="cell8"></div>
|
||||||
<div class="cell" id="cell9"></div>
|
<div class="cell" id="cell9"></div> -->
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,12 +1,22 @@
|
|||||||
const team = 0;
|
//const team = 0;
|
||||||
const teamName = "blue";
|
if (team != 0 && team != 1) {
|
||||||
const color = "dodgerblue";
|
throw "team is not 0 or 1! team=" + team;
|
||||||
const otherColor = "firebrick";
|
}
|
||||||
|
let teamName = "blue";
|
||||||
|
let color = "dodgerblue";
|
||||||
|
let otherColor = "firebrick";
|
||||||
|
let port = "8080";
|
||||||
|
if (team === 1) {
|
||||||
|
teamName = "red";
|
||||||
|
color = "firebrick";
|
||||||
|
otherColor = "dodgerblue";
|
||||||
|
port = "8081";
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById("team").innerHTML =
|
document.getElementById("team").innerHTML =
|
||||||
'Team : <span style="color:' + color + '">' + teamName + "</span>";
|
'Team : <span style="color:' + color + '">' + teamName + "</span>";
|
||||||
|
|
||||||
const ws = new WebSocket("ws://192.254.0.2:8080/" + teamName);
|
const ws = new WebSocket("ws://192.254.0.2:" + port + "/" + teamName);
|
||||||
|
|
||||||
ws.onmessage = (event) => {
|
ws.onmessage = (event) => {
|
||||||
console.log(event.data);
|
console.log(event.data);
|
||||||
|
168
src/apps/ttt.rs
168
src/apps/ttt.rs
@ -1,10 +1,11 @@
|
|||||||
use core::fmt::Write;
|
use core::str::from_utf8_unchecked;
|
||||||
use core::{ops::Not, sync::atomic::Ordering};
|
use core::{ops::Not, sync::atomic::Ordering};
|
||||||
|
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
|
||||||
|
use embassy_sync::mutex::Mutex;
|
||||||
use embassy_time::{Duration, Instant, Timer};
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
use heapless::{String, Vec};
|
use heapless::{String, Vec};
|
||||||
use log::info;
|
use log::{info, warn};
|
||||||
use pico_website::unwrap;
|
use pico_website::{unwrap, unwrap_opt};
|
||||||
use portable_atomic::{AtomicBool, AtomicU32};
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::apps::Content;
|
use crate::apps::Content;
|
||||||
@ -13,61 +14,75 @@ use crate::socket::{HttpRequestType, HttpResCode};
|
|||||||
|
|
||||||
use super::App;
|
use super::App;
|
||||||
|
|
||||||
static TURN: AtomicBool = AtomicBool::new(false);
|
|
||||||
// bits [0; 8] : player zero board / bits [9; 17] : player one board / is_ended [18] / is_draw [19] / winner [20]: 0=blue 1=green / current_turn [21]: 0=blue 1=green
|
// bits [0; 8] : player zero board / bits [9; 17] : player one board / is_ended [18] / is_draw [19] / winner [20]: 0=blue 1=green / current_turn [21]: 0=blue 1=green
|
||||||
static BOARD: AtomicU32 = AtomicU32::new(0b01000000_001000000);
|
|
||||||
|
#[derive(Debug, Serialize, Clone, PartialEq, Eq, Default)]
|
||||||
|
struct Game {
|
||||||
|
board: [Option<Team>; 9],
|
||||||
|
turn: Option<Team>,
|
||||||
|
winner: Option<Team>,
|
||||||
|
}
|
||||||
|
static GAME: Mutex<ThreadModeRawMutex, Game> = Mutex::new(Game {
|
||||||
|
board: [None; 9],
|
||||||
|
turn: Some(Team::Zero),
|
||||||
|
winner: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// {"board"=[null,null,null,null,null,null,null,null,null],"turn"=null,"winner":null}
|
||||||
|
|
||||||
pub struct TttApp {
|
pub struct TttApp {
|
||||||
team: Team,
|
team: Team,
|
||||||
last_board: u32,
|
last_game: Game,
|
||||||
end: Option<(Instant, Option<Team>)>,
|
end: Option<(Instant, Option<Team>)>,
|
||||||
|
json_buf: [u8; 128],
|
||||||
}
|
}
|
||||||
impl TttApp {
|
impl TttApp {
|
||||||
pub fn new(team: Team) -> Self {
|
pub fn new(team: Team) -> Self {
|
||||||
Self {
|
Self {
|
||||||
team,
|
team,
|
||||||
last_board: 0,
|
last_game: Game::default(),
|
||||||
end: None,
|
end: None,
|
||||||
|
json_buf: [0; 128],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_ended(&self, board: u32) -> (bool, Option<Team>) {
|
// pub fn is_ended(&self, board: u32) -> (bool, Option<Team>) {
|
||||||
if let Some((_, t)) = self.end {
|
// if let Some((_, t)) = self.end {
|
||||||
return (true, t);
|
// return (true, t);
|
||||||
}
|
// }
|
||||||
for (t, m) in [(Team::Zero, 0), (Team::One, 9)] {
|
// for (t, m) in [(Team::Zero, 0), (Team::One, 9)] {
|
||||||
for w in [
|
// for w in [
|
||||||
0b111000000,
|
// 0b111000000,
|
||||||
0b000111000,
|
// 0b000111000,
|
||||||
0b000000111,
|
// 0b000000111,
|
||||||
0b100100100,
|
// 0b100100100,
|
||||||
0b010010010,
|
// 0b010010010,
|
||||||
0b001001001,
|
// 0b001001001,
|
||||||
0b100010001,
|
// 0b100010001,
|
||||||
0b001010100,
|
// 0b001010100,
|
||||||
] {
|
// ] {
|
||||||
if board & (w << m) == (w << m) {
|
// if board & (w << m) == (w << m) {
|
||||||
return (true, Some(t));
|
// return (true, Some(t));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if ((board | (board >> 9)) & 0b111111111) == 0b111111111 {
|
// if ((board | (board >> 9)) & 0b111111111) == 0b111111111 {
|
||||||
return (true, None);
|
// return (true, None);
|
||||||
}
|
// }
|
||||||
(false, None)
|
// (false, None)
|
||||||
}
|
// }
|
||||||
pub fn update_end_state(&mut self, board: &mut u32) {
|
// pub fn update_end_state(&mut self, board: &mut u32) {
|
||||||
if let Some((i, _)) = self.end {
|
// if let Some((i, _)) = self.end {
|
||||||
if i + Duration::from_secs(7) < Instant::now() {
|
// if i + Duration::from_secs(7) < Instant::now() {
|
||||||
self.end = None;
|
// self.end = None;
|
||||||
BOARD.store(0, Ordering::Release);
|
// // BOARD.store(0, Ordering::Release);
|
||||||
*board = 0;
|
// *board = 0;
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
if let (true, t) = self.is_ended(*board) {
|
// if let (true, t) = self.is_ended(*board) {
|
||||||
self.end = Some((Instant::now(), t));
|
// self.end = Some((Instant::now(), t));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App for TttApp {
|
impl App for TttApp {
|
||||||
@ -97,36 +112,59 @@ impl App for TttApp {
|
|||||||
_path: &str,
|
_path: &str,
|
||||||
mut ws: Ws<'a, BUF_SIZE, RES_HEAD_BUF_SIZE>,
|
mut ws: Ws<'a, BUF_SIZE, RES_HEAD_BUF_SIZE>,
|
||||||
) {
|
) {
|
||||||
|
Timer::after_millis(500).await;
|
||||||
let r: Result<(), ()> = try {
|
let r: Result<(), ()> = try {
|
||||||
loop {
|
loop {
|
||||||
let board = BOARD.load(Ordering::Acquire);
|
Timer::after_millis(1).await;
|
||||||
ws.send(WsMsg::Text(
|
let Ok(mut game) = GAME.try_lock() else {
|
||||||
&serde_json_core::to_string::<_, 40>(&ServerMsg {
|
info!("locked");
|
||||||
board,
|
continue;
|
||||||
turn: Some(Team::Zero),
|
};
|
||||||
winner: None,
|
// match GAME.try_lock() ;
|
||||||
})
|
if self.last_game != *game {
|
||||||
.unwrap(),
|
// let json = unwrap(serde_json_core::to_string::<Game, 128>(&game)).await;
|
||||||
))
|
let n = unwrap(serde_json_core::to_slice(&(*game), &mut self.json_buf)).await;
|
||||||
.await?;
|
let json =
|
||||||
|
unsafe { from_utf8_unchecked(&unwrap_opt(self.json_buf.get(..n)).await) };
|
||||||
|
info!("{:?}", json);
|
||||||
|
ws.send(WsMsg::Text(json)).await?;
|
||||||
|
self.last_game = game.clone();
|
||||||
|
}
|
||||||
|
if ws.last_msg.elapsed() >= Duration::from_secs(5) {
|
||||||
|
ws.send(WsMsg::Ping(&[])).await?;
|
||||||
|
info!("ping");
|
||||||
|
}
|
||||||
while let Some(r) = ws.rcv().await? {
|
while let Some(r) = ws.rcv().await? {
|
||||||
info!("{:?}", r);
|
info!("{:?}", r);
|
||||||
|
if let WsMsg::Bytes([c]) = r {
|
||||||
|
let c = *c;
|
||||||
|
info!("c={}", c);
|
||||||
|
if c > 8 {}
|
||||||
|
match game.board.get_mut(c as usize) {
|
||||||
|
None => {
|
||||||
|
warn!("Cell played is too big!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Some(Some(_)) => {
|
||||||
|
warn!("Cell is already taken!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Some(cell) => {
|
||||||
|
*cell = Some(self.team);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info!("{:#?}", game);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer::after_secs(1).await;
|
// Timer::after_secs(1).await;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
info!("{:?}", r);
|
warn!("error: {:?}", r);
|
||||||
|
Timer::after_micros(100).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
struct ServerMsg {
|
|
||||||
board: u32,
|
|
||||||
turn: Option<Team>,
|
|
||||||
winner: Option<Team>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Team {
|
pub enum Team {
|
||||||
Zero = 0,
|
Zero = 0,
|
||||||
|
@ -11,7 +11,7 @@ use embassy_net::{
|
|||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use pico_website::unwrap;
|
use pico_website::{unwrap, unwrap_opt};
|
||||||
|
|
||||||
#[embassy_executor::task(pool_size = 1)]
|
#[embassy_executor::task(pool_size = 1)]
|
||||||
pub async fn dhcp_server(stack: Stack<'static>) {
|
pub async fn dhcp_server(stack: Stack<'static>) {
|
||||||
@ -38,7 +38,8 @@ pub async fn dhcp_server(stack: Stack<'static>) {
|
|||||||
loop {
|
loop {
|
||||||
let (n, _) = unwrap(socket.recv_from(&mut buf).await).await;
|
let (n, _) = unwrap(socket.recv_from(&mut buf).await).await;
|
||||||
|
|
||||||
let msg = unwrap(dhcpv4::Message::new(&buf[..n])).await;
|
let msg = unwrap_opt(buf.get(..n)).await;
|
||||||
|
let msg = unwrap(dhcpv4::Message::new(&msg)).await;
|
||||||
|
|
||||||
let msg_type = unwrap(v4_options!(msg; MessageType required)).await;
|
let msg_type = unwrap(v4_options!(msg; MessageType required)).await;
|
||||||
let mut rapid_commit = false;
|
let mut rapid_commit = false;
|
||||||
|
@ -5,7 +5,7 @@ use embassy_net::{
|
|||||||
};
|
};
|
||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use pico_website::unwrap;
|
use pico_website::{unwrap, unwrap_opt};
|
||||||
|
|
||||||
#[embassy_executor::task(pool_size = 1)]
|
#[embassy_executor::task(pool_size = 1)]
|
||||||
pub async fn dns_server(stack: Stack<'static>) {
|
pub async fn dns_server(stack: Stack<'static>) {
|
||||||
@ -30,7 +30,8 @@ pub async fn dns_server(stack: Stack<'static>) {
|
|||||||
Timer::after_secs(0).await;
|
Timer::after_secs(0).await;
|
||||||
let (n, meta) = unwrap(socket.recv_from(&mut buf).await).await;
|
let (n, meta) = unwrap(socket.recv_from(&mut buf).await).await;
|
||||||
|
|
||||||
let msg = match dnsparse::Message::parse(&mut buf[..n]) {
|
let msg = unwrap_opt(buf.get_mut(..n)).await;
|
||||||
|
let msg = match dnsparse::Message::parse(msg) {
|
||||||
Ok(msg) => msg,
|
Ok(msg) => msg,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Dns: Error while parsing DNS message : {:#?}", e);
|
warn!("Dns: Error while parsing DNS message : {:#?}", e);
|
||||||
|
19
src/lib.rs
19
src/lib.rs
@ -1,6 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use core::fmt::Debug;
|
use core::{fmt::Debug, panic::PanicInfo};
|
||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
@ -13,3 +13,20 @@ pub async fn unwrap<T, E: Debug>(res: Result<T, E>) -> T {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub async fn unwrap_opt<T>(opt: Option<T>) -> T {
|
||||||
|
unwrap(opt.ok_or(())).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn assert(condition: bool) {
|
||||||
|
if !condition {
|
||||||
|
let err: Result<(), &str> = Err("assert failed");
|
||||||
|
unwrap(err).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doesn't work
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
info!("PANIC: {}", info);
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
15
src/main.rs
15
src/main.rs
@ -1,6 +1,12 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![allow(async_fn_in_trait)]
|
#![allow(async_fn_in_trait)]
|
||||||
|
#![deny(
|
||||||
|
// clippy::unwrap_used,
|
||||||
|
// clippy::expect_used,
|
||||||
|
clippy::panic,
|
||||||
|
clippy::indexing_slicing
|
||||||
|
)]
|
||||||
#![feature(impl_trait_in_assoc_type)]
|
#![feature(impl_trait_in_assoc_type)]
|
||||||
#![feature(slice_split_once)]
|
#![feature(slice_split_once)]
|
||||||
#![feature(try_blocks)]
|
#![feature(try_blocks)]
|
||||||
@ -10,6 +16,8 @@
|
|||||||
use core::net::Ipv4Addr;
|
use core::net::Ipv4Addr;
|
||||||
|
|
||||||
use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi};
|
use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi};
|
||||||
|
use defmt::warn;
|
||||||
|
use defmt_rtt as _;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_net::{Config, StackResources};
|
use embassy_net::{Config, StackResources};
|
||||||
use embassy_rp::bind_interrupts;
|
use embassy_rp::bind_interrupts;
|
||||||
@ -23,7 +31,6 @@ use log::info;
|
|||||||
use pico_website::unwrap;
|
use pico_website::unwrap;
|
||||||
use rand_core::RngCore;
|
use rand_core::RngCore;
|
||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
|
||||||
|
|
||||||
#[cfg(feature = "dhcp")]
|
#[cfg(feature = "dhcp")]
|
||||||
mod dhcp;
|
mod dhcp;
|
||||||
@ -175,6 +182,12 @@ async fn main(spawner: Spawner) {
|
|||||||
.address
|
.address
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
// embassy_time::Timer::after_secs(5).await;
|
||||||
|
// info!("test0");
|
||||||
|
|
||||||
|
// embassy_time::Timer::after_secs(3).await;
|
||||||
|
|
||||||
|
// panic!("test");
|
||||||
|
|
||||||
#[cfg(feature = "dhcp")]
|
#[cfg(feature = "dhcp")]
|
||||||
unwrap(spawner.spawn(dhcp::dhcp_server(stack))).await;
|
unwrap(spawner.spawn(dhcp::dhcp_server(stack))).await;
|
||||||
|
@ -6,6 +6,7 @@ use embassy_time::{Duration, Timer};
|
|||||||
use embedded_io_async::Write as _;
|
use embedded_io_async::Write as _;
|
||||||
use heapless::{String, Vec};
|
use heapless::{String, Vec};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
use pico_website::{unwrap, unwrap_opt};
|
||||||
use sha1::{Digest, Sha1};
|
use sha1::{Digest, Sha1};
|
||||||
|
|
||||||
use crate::apps::Content;
|
use crate::apps::Content;
|
||||||
@ -71,7 +72,8 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl apps:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
head_buf.clear();
|
head_buf.clear();
|
||||||
let (headers, content) = match from_utf8(&buf[..n]) {
|
let msg = unwrap_opt(buf.get(..n)).await;
|
||||||
|
let (headers, content) = match from_utf8(msg) {
|
||||||
Ok(b) => match b.split_once("\r\n\r\n") {
|
Ok(b) => match b.split_once("\r\n\r\n") {
|
||||||
Some(t) => t,
|
Some(t) => t,
|
||||||
None => (b, ""),
|
None => (b, ""),
|
||||||
@ -82,7 +84,8 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl apps:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
info!("\n{:?}\n", headers);
|
// info!("\n{:?}\n", headers);
|
||||||
|
// Timer::after_micros(100).await;
|
||||||
|
|
||||||
let mut hl = headers.lines();
|
let mut hl = headers.lines();
|
||||||
let (request_type, path) = match hl.next() {
|
let (request_type, path) = match hl.next() {
|
||||||
@ -144,12 +147,13 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl apps:
|
|||||||
host,
|
host,
|
||||||
path,
|
path,
|
||||||
);
|
);
|
||||||
Timer::after_secs(0).await;
|
Timer::after_micros(100).await;
|
||||||
|
|
||||||
head_buf.clear();
|
head_buf.clear();
|
||||||
let res_content: Result<Option<Content>, core::fmt::Error> = try {
|
let res_content: Result<Option<Content>, core::fmt::Error> = try {
|
||||||
if ws_handshake {
|
if ws_handshake {
|
||||||
if !app.accept_ws(path) {
|
if !app.accept_ws(path) {
|
||||||
|
warn!("No ws there!");
|
||||||
write!(
|
write!(
|
||||||
&mut head_buf,
|
&mut head_buf,
|
||||||
"{}\r\n\r\n",
|
"{}\r\n\r\n",
|
||||||
@ -206,7 +210,7 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl apps:
|
|||||||
Content-Length: {}\r\n",
|
Content-Length: {}\r\n",
|
||||||
res_type,
|
res_type,
|
||||||
c.len()
|
c.len()
|
||||||
)?
|
)?;
|
||||||
}
|
}
|
||||||
write!(&mut head_buf, "\r\n\r\n")?;
|
write!(&mut head_buf, "\r\n\r\n")?;
|
||||||
res_content
|
res_content
|
||||||
@ -221,7 +225,8 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl apps:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
info!("\n{}\n", from_utf8(&head_buf).unwrap());
|
info!("\n{}\n", unwrap(from_utf8(&head_buf)).await);
|
||||||
|
Timer::after_micros(1000).await;
|
||||||
|
|
||||||
let w: Result<(), embassy_net::tcp::Error> = try {
|
let w: Result<(), embassy_net::tcp::Error> = try {
|
||||||
socket.write_all(&head_buf).await?;
|
socket.write_all(&head_buf).await?;
|
||||||
@ -229,7 +234,6 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl apps:
|
|||||||
for s in c.0.iter() {
|
for s in c.0.iter() {
|
||||||
socket.write_all(s.as_bytes()).await?;
|
socket.write_all(s.as_bytes()).await?;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -239,11 +243,13 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl apps:
|
|||||||
};
|
};
|
||||||
|
|
||||||
if ws_handshake {
|
if ws_handshake {
|
||||||
ws_path = Some(String::from_str(path).unwrap());
|
ws_path = Some(unwrap(String::from_str(path)).await);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(path) = ws_path {
|
if let Some(path) = ws_path {
|
||||||
|
info!("handle ws");
|
||||||
|
Timer::after_micros(200).await;
|
||||||
app.handle_ws(
|
app.handle_ws(
|
||||||
&path,
|
&path,
|
||||||
Ws::new(&mut socket, &mut buf, &mut head_buf, app.socket_name()),
|
Ws::new(&mut socket, &mut buf, &mut head_buf, app.socket_name()),
|
||||||
@ -291,11 +297,12 @@ impl Into<&str> for HttpResCode {
|
|||||||
|
|
||||||
async fn compute_ws_accept(key: &str) -> Result<String<28>, EncodeSliceError> {
|
async fn compute_ws_accept(key: &str) -> Result<String<28>, EncodeSliceError> {
|
||||||
let mut res = Vec::<u8, 28>::new();
|
let mut res = Vec::<u8, 28>::new();
|
||||||
|
Timer::after_micros(10).await;
|
||||||
res.extend_from_slice(&[0; 28]).unwrap();
|
res.extend_from_slice(&[0; 28]).unwrap();
|
||||||
let mut hasher = Sha1::new();
|
let mut hasher = Sha1::new();
|
||||||
hasher.update(key.as_bytes());
|
hasher.update(key.as_bytes());
|
||||||
hasher.update(b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
|
hasher.update(b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
|
||||||
let hash = hasher.finalize();
|
let hash = hasher.finalize();
|
||||||
BASE64_STANDARD.encode_slice(hash, &mut res)?;
|
BASE64_STANDARD.encode_slice(hash, &mut res)?;
|
||||||
Ok(String::from_utf8(res).unwrap())
|
Ok(unwrap(String::from_utf8(res)).await)
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ use embassy_time::{Instant, Timer};
|
|||||||
use embedded_io_async::{ErrorType, ReadReady, Write};
|
use embedded_io_async::{ErrorType, ReadReady, Write};
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
use pico_website::{assert, unwrap, unwrap_opt};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum WsMsg<'a> {
|
pub enum WsMsg<'a> {
|
||||||
@ -38,7 +39,6 @@ impl WsMsg<'_> {
|
|||||||
struct WsRx<'a, const BUF_SIZE: usize> {
|
struct WsRx<'a, const BUF_SIZE: usize> {
|
||||||
socket: TcpReader<'a>,
|
socket: TcpReader<'a>,
|
||||||
buf: &'a mut [u8; BUF_SIZE],
|
buf: &'a mut [u8; BUF_SIZE],
|
||||||
last_msg: Instant,
|
|
||||||
msg_in_buf: Option<(usize, usize)>, // (start, length)
|
msg_in_buf: Option<(usize, usize)>, // (start, length)
|
||||||
}
|
}
|
||||||
struct WsTx<'a, const HEAD_BUF_SIZE: usize> {
|
struct WsTx<'a, const HEAD_BUF_SIZE: usize> {
|
||||||
@ -48,15 +48,17 @@ struct WsTx<'a, const HEAD_BUF_SIZE: usize> {
|
|||||||
impl<'a, const HEAD_BUF_SIZE: usize> WsTx<'a, HEAD_BUF_SIZE> {
|
impl<'a, const HEAD_BUF_SIZE: usize> WsTx<'a, HEAD_BUF_SIZE> {
|
||||||
pub async fn send<'m>(&mut self, msg: WsMsg<'m>) -> Result<(), ()> {
|
pub async fn send<'m>(&mut self, msg: WsMsg<'m>) -> Result<(), ()> {
|
||||||
self.head_buf.clear();
|
self.head_buf.clear();
|
||||||
self.head_buf.push(0b1000_0000 | msg.code()).unwrap();
|
unwrap(self.head_buf.push(0b1000_0000 | msg.code())).await;
|
||||||
if msg.len() < 126 {
|
if msg.len() < 126 {
|
||||||
self.head_buf.push(msg.len() as u8).unwrap();
|
unwrap(self.head_buf.push(msg.len() as u8)).await;
|
||||||
} else {
|
} else {
|
||||||
self.head_buf.push(0b0111_1110).unwrap();
|
unwrap(self.head_buf.push(0b0111_1110)).await;
|
||||||
|
unwrap(
|
||||||
self.head_buf
|
self.head_buf
|
||||||
.extend_from_slice(&(msg.len() as u16).to_le_bytes())
|
.extend_from_slice(&(msg.len() as u16).to_le_bytes()),
|
||||||
.unwrap();
|
)
|
||||||
self.head_buf.extend_from_slice(msg.as_bytes()).unwrap();
|
.await;
|
||||||
|
// self.head_buf.extend_from_slice(msg.as_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
let w: Result<(), <TcpSocket<'_> as ErrorType>::Error> = try {
|
let w: Result<(), <TcpSocket<'_> as ErrorType>::Error> = try {
|
||||||
self.socket.write_all(&self.head_buf).await?;
|
self.socket.write_all(&self.head_buf).await?;
|
||||||
@ -72,6 +74,7 @@ impl<'a, const HEAD_BUF_SIZE: usize> WsTx<'a, HEAD_BUF_SIZE> {
|
|||||||
pub struct Ws<'a, const BUF_SIZE: usize = 1024, const RES_HEAD_BUF_SIZE: usize = 256> {
|
pub struct Ws<'a, const BUF_SIZE: usize = 1024, const RES_HEAD_BUF_SIZE: usize = 256> {
|
||||||
rx: WsRx<'a, BUF_SIZE>,
|
rx: WsRx<'a, BUF_SIZE>,
|
||||||
tx: WsTx<'a, RES_HEAD_BUF_SIZE>,
|
tx: WsTx<'a, RES_HEAD_BUF_SIZE>,
|
||||||
|
pub last_msg: Instant,
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
}
|
}
|
||||||
impl<'a, const BUF_SIZE: usize, const HEAD_BUF_SIZE: usize> Ws<'a, BUF_SIZE, HEAD_BUF_SIZE> {
|
impl<'a, const BUF_SIZE: usize, const HEAD_BUF_SIZE: usize> Ws<'a, BUF_SIZE, HEAD_BUF_SIZE> {
|
||||||
@ -86,13 +89,13 @@ impl<'a, const BUF_SIZE: usize, const HEAD_BUF_SIZE: usize> Ws<'a, BUF_SIZE, HEA
|
|||||||
rx: WsRx {
|
rx: WsRx {
|
||||||
socket: rx,
|
socket: rx,
|
||||||
buf,
|
buf,
|
||||||
last_msg: Instant::MIN,
|
|
||||||
msg_in_buf: None,
|
msg_in_buf: None,
|
||||||
},
|
},
|
||||||
tx: WsTx {
|
tx: WsTx {
|
||||||
socket: tx,
|
socket: tx,
|
||||||
head_buf,
|
head_buf,
|
||||||
},
|
},
|
||||||
|
last_msg: Instant::MIN,
|
||||||
name,
|
name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,8 +103,9 @@ impl<'a, const BUF_SIZE: usize, const HEAD_BUF_SIZE: usize> Ws<'a, BUF_SIZE, HEA
|
|||||||
pub async fn rcv(&mut self) -> Result<Option<WsMsg>, ()> {
|
pub async fn rcv(&mut self) -> Result<Option<WsMsg>, ()> {
|
||||||
let n = match self.rx.msg_in_buf.take() {
|
let n = match self.rx.msg_in_buf.take() {
|
||||||
Some(n) => {
|
Some(n) => {
|
||||||
|
assert(n.0 + n.1 <= self.rx.buf.len()).await;
|
||||||
self.rx.buf.copy_within(n.0..n.0 + n.1, 0);
|
self.rx.buf.copy_within(n.0..n.0 + n.1, 0);
|
||||||
if self.rx.socket.read_ready().unwrap() {
|
if unwrap(self.rx.socket.read_ready()).await {
|
||||||
let n_rcv = match self.rx.socket.read(&mut self.rx.buf[n.1..]).await {
|
let n_rcv = match self.rx.socket.read(&mut self.rx.buf[n.1..]).await {
|
||||||
Ok(0) => {
|
Ok(0) => {
|
||||||
info!("read EOF");
|
info!("read EOF");
|
||||||
@ -119,7 +123,7 @@ impl<'a, const BUF_SIZE: usize, const HEAD_BUF_SIZE: usize> Ws<'a, BUF_SIZE, HEA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if self.rx.socket.read_ready().unwrap() {
|
if unwrap(self.rx.socket.read_ready()).await {
|
||||||
match self.rx.socket.read(self.rx.buf).await {
|
match self.rx.socket.read(self.rx.buf).await {
|
||||||
Ok(0) => {
|
Ok(0) => {
|
||||||
info!("read EOF");
|
info!("read EOF");
|
||||||
@ -177,7 +181,7 @@ impl<'a, const BUF_SIZE: usize, const HEAD_BUF_SIZE: usize> Ws<'a, BUF_SIZE, HEA
|
|||||||
.iter_mut()
|
.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
*x ^= mask_key[i & 0xff];
|
*x ^= unwrap_opt(mask_key.get(i & 0xff)).await;
|
||||||
}
|
}
|
||||||
if n_after_length + 4 + (length as usize) < n {
|
if n_after_length + 4 + (length as usize) < n {
|
||||||
self.rx.msg_in_buf = Some((
|
self.rx.msg_in_buf = Some((
|
||||||
@ -199,7 +203,7 @@ impl<'a, const BUF_SIZE: usize, const HEAD_BUF_SIZE: usize> Ws<'a, BUF_SIZE, HEA
|
|||||||
}
|
}
|
||||||
&self.rx.buf[n_after_length..n_after_length + length as usize]
|
&self.rx.buf[n_after_length..n_after_length + length as usize]
|
||||||
};
|
};
|
||||||
self.rx.last_msg = Instant::now();
|
self.last_msg = Instant::now();
|
||||||
match self.rx.buf[0] & 0b0000_1111 {
|
match self.rx.buf[0] & 0b0000_1111 {
|
||||||
// Text message
|
// Text message
|
||||||
1 => {
|
1 => {
|
||||||
@ -222,6 +226,8 @@ impl<'a, const BUF_SIZE: usize, const HEAD_BUF_SIZE: usize> Ws<'a, BUF_SIZE, HEA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn send(&mut self, msg: WsMsg<'_>) -> Result<(), ()> {
|
pub async fn send(&mut self, msg: WsMsg<'_>) -> Result<(), ()> {
|
||||||
self.tx.send(msg).await
|
self.tx.send(msg).await?;
|
||||||
|
self.last_msg = Instant::now();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user