Compare commits

..

No commits in common. "23d03920aed1f5012fe2625217f607cbb26a8cd1" and "3782686d4bbb41bfd8f91be6b198b4021aff4e40" have entirely different histories.

10 changed files with 131 additions and 315 deletions

View File

@ -1,6 +1,3 @@
use heapless::Vec;
use pico_website::unwrap;
use crate::{ use crate::{
apps::Content, apps::Content,
socket::{HttpRequestType, HttpResCode}, socket::{HttpRequestType, HttpResCode},
@ -25,50 +22,17 @@ impl App for IndexApp {
"html", "html",
Some(include_str!("./index.html").into()), Some(include_str!("./index.html").into()),
), ),
path => { "/ttt" => (
let (path, args) = path.split_once('?').unwrap_or((path, "")); HttpResCode::Ok,
let mut team = None; "html",
for arg in args.split('&') { Some(include_str!("ttt.html").into()),
match arg.split_once('=') { ),
Some(("team", "0")) => team = Some("0"), "/ttt.js" => (
Some(("team", "1")) => team = Some("1"), HttpResCode::Ok,
_ => {} "javascript",
} Some(include_str!("ttt.js").into()),
} ),
if path == "/ttt" { _ => (HttpResCode::NotFound, "", None),
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)
}
}
} }
} }
} }

View File

@ -27,7 +27,18 @@
<body> <body>
<h1>TicTacToe</h1> <h1>TicTacToe</h1>
<h3 id="team"></h3> <h3 id="team"></h3>
<h3 id="winner"></h3> <!-- <div class="cell" style="background-color:"></div> -->
<div id="grid"></div> <div id="grid">
<div class="cell" id="cell0"></div>
<div class="cell" id="cell1"></div>
<div class="cell" id="cell2"></div>
<div class="cell" id="cell3"></div>
<div class="cell" id="cell4"></div>
<div class="cell" id="cell5"></div>
<div class="cell" id="cell6"></div>
<div class="cell" id="cell7"></div>
<div class="cell" id="cell8"></div>
<div class="cell" id="cell9"></div>
</div>
</body> </body>
</html> </html>

View File

@ -1,30 +1,12 @@
//const team = 0; const team = 0;
if (team != 0 && team != 1) { const teamName = "blue";
throw "team is not 0 or 1! team=" + team; const color = "dodgerblue";
} const otherColor = "firebrick";
const teams = [
{
name: "blue",
color: "dodgerblue",
port: "8080",
},
{
name: "red",
color: "firebrick",
port: "8081",
},
];
document.getElementById("team").innerHTML = document.getElementById("team").innerHTML =
'Team : <span style="color:' + 'Team : <span style="color:' + color + '">' + teamName + "</span>";
teams[team].color +
'">' +
teams[team].name +
"</span>";
const ws = new WebSocket( const ws = new WebSocket("ws://192.254.0.2:8080/" + teamName);
"ws://192.254.0.2:" + teams[team].port + "/" + teams[team].name,
);
ws.onmessage = (event) => { ws.onmessage = (event) => {
console.log(event.data); console.log(event.data);
@ -32,7 +14,12 @@ ws.onmessage = (event) => {
let msg = JSON.parse(event.data); let msg = JSON.parse(event.data);
let cells = []; let cells = [];
for (let i = 0; i < 9; i++) { for (let i = 0; i < 9; i++) {
let owner = msg.board[i]; let owner = null;
if (((msg.board >> (17 - i)) & 1) == 1) {
owner = 0;
} else if (((msg.board >> (8 - i)) & 1) == 1) {
owner = 1;
}
let tagName; let tagName;
if (msg.turn == team && owner === null) { if (msg.turn == team && owner === null) {
@ -61,19 +48,5 @@ ws.onmessage = (event) => {
cells.push(cell); cells.push(cell);
} }
document.getElementById("grid").replaceChildren(...cells); document.getElementById("grid").replaceChildren(...cells);
if (msg.turn == null) {
if (msg.winner == null) {
document.getElementById("winner").innerHTML = "Draw!";
} else {
document.getElementById("winner").innerHTML =
'Winner : <span style="color:' +
teams[msg.winner].color +
'">' +
teams[msg.winner].name +
"</span>";
}
} else {
document.getElementById("winner").innerHTML = "";
}
} }
}; };

View File

@ -1,11 +1,10 @@
use core::str::from_utf8_unchecked; use core::fmt::Write;
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, warn}; use log::info;
use pico_website::{unwrap, unwrap_opt}; use pico_website::unwrap;
use portable_atomic::{AtomicBool, AtomicU32};
use serde::Serialize; use serde::Serialize;
use crate::apps::Content; use crate::apps::Content;
@ -14,112 +13,61 @@ 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)]
struct Game {
board: [Option<Team>; 9],
turn: Option<Team>,
winner: Option<Team>,
}
impl Game {
const fn default() -> Self {
Game {
board: [None; 9],
turn: Some(Team::Zero),
winner: None,
}
}
fn check_end(&mut self) -> bool {
for [a, b, c] in [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
] {
if let Some(t) = self.board[a] {
if self.board[b] == Some(t) && self.board[c] == Some(t) {
self.winner = Some(t);
self.turn = None;
return true;
}
}
}
if self.board.iter().all(|c| c.is_some()) {
self.winner = None;
self.turn = None;
return true;
}
false
}
}
static GAME: Mutex<ThreadModeRawMutex, Game> = Mutex::new(Game::default());
// {"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_game: Game, last_board: u32,
/// Only one socket manages the end, this can be None even when it's the end end: Option<(Instant, Option<Team>)>,
end: Option<Instant>,
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_game: Game { last_board: 0,
board: [None; 9],
turn: None,
winner: None,
},
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 {
@ -149,73 +97,38 @@ 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 {
Timer::after_millis(1).await; let board = BOARD.load(Ordering::Acquire);
let Ok(mut game) = GAME.try_lock() else { ws.send(WsMsg::Text(
info!("locked"); &serde_json_core::to_string::<_, 40>(&ServerMsg {
continue; board,
}; turn: Some(Team::Zero),
// match GAME.try_lock() ; winner: None,
if self.last_game != *game { })
// let json = unwrap(serde_json_core::to_string::<Game, 128>(&game)).await; .unwrap(),
let n = unwrap(serde_json_core::to_slice(&(*game), &mut self.json_buf)).await; ))
let json = .await?;
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");
}
if self.end.map(|e| e.elapsed()).unwrap_or_default() > Duration::from_secs(5) {
self.end = None;
*game = Game {
turn: Some(!game.winner.unwrap_or_default()),
..Game::default()
};
}
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 as usize;
info!("c={}", c);
if c >= game.board.len() {
warn!("Cell played is too big!");
return;
}
if game.board[c].is_some() {
warn!("Cell is already taken!");
return;
}
if game.turn == Some(self.team) {
game.board[c] = Some(self.team);
game.turn = Some(!self.team);
if game.check_end() {
self.end = Some(Instant::now());
}
} else {
warn!("It's not your turn!");
return;
}
info!("{:#?}", game);
}
} }
// Timer::after_secs(1).await; Timer::after_secs(1).await;
} }
}; };
warn!("error: {:?}", r); info!("{:?}", r);
Timer::after_micros(100).await;
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] #[derive(Debug, Serialize)]
struct ServerMsg {
board: u32,
turn: Option<Team>,
winner: Option<Team>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Team { pub enum Team {
#[default]
Zero = 0, Zero = 0,
One = 1, One = 1,
} }

View File

@ -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, unwrap_opt}; use pico_website::unwrap;
#[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,8 +38,7 @@ 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_opt(buf.get(..n)).await; let msg = unwrap(dhcpv4::Message::new(&buf[..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;

View File

@ -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, unwrap_opt}; use pico_website::unwrap;
#[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,8 +30,7 @@ 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 = unwrap_opt(buf.get_mut(..n)).await; let msg = match dnsparse::Message::parse(&mut buf[..n]) {
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);

View File

@ -1,6 +1,6 @@
#![no_std] #![no_std]
use core::{fmt::Debug, panic::PanicInfo}; use core::fmt::Debug;
use embassy_time::Timer; use embassy_time::Timer;
use log::info; use log::info;
@ -13,20 +13,3 @@ 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 {}
}

View File

@ -1,12 +1,6 @@
#![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)]
@ -16,8 +10,6 @@
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;
@ -31,6 +23,7 @@ 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;
@ -182,12 +175,6 @@ 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;

View File

@ -6,7 +6,6 @@ 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;
@ -72,8 +71,7 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl apps:
} }
}; };
head_buf.clear(); head_buf.clear();
let msg = unwrap_opt(buf.get(..n)).await; let (headers, content) = match from_utf8(&buf[..n]) {
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, ""),
@ -84,8 +82,7 @@ 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() {
@ -147,13 +144,12 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl apps:
host, host,
path, path,
); );
Timer::after_micros(100).await; Timer::after_secs(0).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",
@ -210,7 +206,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
@ -225,8 +221,7 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl apps:
} }
}; };
info!("\n{}\n", unwrap(from_utf8(&head_buf)).await); info!("\n{}\n", from_utf8(&head_buf).unwrap());
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?;
@ -234,6 +229,7 @@ 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 {
} }
}; };
@ -243,13 +239,11 @@ pub async fn listen_task(stack: embassy_net::Stack<'static>, mut app: impl apps:
}; };
if ws_handshake { if ws_handshake {
ws_path = Some(unwrap(String::from_str(path)).await); ws_path = Some(String::from_str(path).unwrap());
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()),
@ -297,12 +291,11 @@ 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(unwrap(String::from_utf8(res)).await) Ok(String::from_utf8(res).unwrap())
} }

View File

@ -5,7 +5,6 @@ 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> {
@ -39,6 +38,7 @@ 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,17 +48,15 @@ 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();
unwrap(self.head_buf.push(0b1000_0000 | msg.code())).await; self.head_buf.push(0b1000_0000 | msg.code()).unwrap();
if msg.len() < 126 { if msg.len() < 126 {
unwrap(self.head_buf.push(msg.len() as u8)).await; self.head_buf.push(msg.len() as u8).unwrap();
} else { } else {
unwrap(self.head_buf.push(0b0111_1110)).await; self.head_buf.push(0b0111_1110).unwrap();
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?;
@ -74,7 +72,6 @@ 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> {
@ -89,13 +86,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,
} }
} }
@ -103,9 +100,8 @@ 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 unwrap(self.rx.socket.read_ready()).await { if self.rx.socket.read_ready().unwrap() {
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");
@ -123,7 +119,7 @@ impl<'a, const BUF_SIZE: usize, const HEAD_BUF_SIZE: usize> Ws<'a, BUF_SIZE, HEA
} }
} }
None => { None => {
if unwrap(self.rx.socket.read_ready()).await { if self.rx.socket.read_ready().unwrap() {
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");
@ -181,7 +177,7 @@ impl<'a, const BUF_SIZE: usize, const HEAD_BUF_SIZE: usize> Ws<'a, BUF_SIZE, HEA
.iter_mut() .iter_mut()
.enumerate() .enumerate()
{ {
*x ^= unwrap_opt(mask_key.get(i & 0xff)).await; *x ^= mask_key[i & 0xff];
} }
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((
@ -203,7 +199,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.last_msg = Instant::now(); self.rx.last_msg = Instant::now();
match self.rx.buf[0] & 0b0000_1111 { match self.rx.buf[0] & 0b0000_1111 {
// Text message // Text message
1 => { 1 => {
@ -226,8 +222,6 @@ 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(())
} }
} }