use embassy_net::{IpEndpoint, Stack};
use embassy_net::tcp::State;
use embassy_time::{Duration, Timer};
use crate::channel::WriteChannel;
use crate::connection::socket_state::SocketState;
use crate::connection::TcpConnection;
use crate::err::{SocketErr, SocketResult};
use crate::tcp_server::callback::TcpServerCallBack;
pub struct TcpServerRunner<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize, const BUF_SIZE: usize, CB: TcpServerCallBack> {
stack: Stack<'d>,
state: &'d SocketState<N, TX_SZ, RX_SZ, BUF_SIZE>,
port: u16,
socket_timeout: Option<Duration>,
read_timeout: Duration,
pub cb: &'d CB,
}
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize, const BUF_SIZE: usize, CB: TcpServerCallBack> Clone for
TcpServerRunner<'d, N, TX_SZ, RX_SZ, BUF_SIZE, CB> {
#[inline]
fn clone(&self) -> Self {
Self {
stack: self.stack,
state: self.state,
port: self.port,
socket_timeout: self.socket_timeout,
read_timeout: self.read_timeout,
cb: self.cb,
}
}
}
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize, const BUF_SIZE: usize, CB: TcpServerCallBack> Copy for
TcpServerRunner<'d, N, TX_SZ, RX_SZ, BUF_SIZE, CB> {}
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize, const BUF_SIZE: usize, CB: TcpServerCallBack>
TcpServerRunner<'d, N, TX_SZ, RX_SZ, BUF_SIZE, CB> {
#[inline]
pub fn new(stack: Stack<'d>, state: &'d SocketState<N, TX_SZ, RX_SZ, BUF_SIZE>, port: u16, cb: &'d CB) -> Self {
Self { stack, state, port, socket_timeout: None, read_timeout: Duration::from_millis(100), cb }
}
#[inline]
pub fn socket_timeout(&mut self, timeout: Option<Duration>) {
self.socket_timeout = timeout;
}
#[inline]
pub fn read_timeout(&mut self, timeout: Duration) {
self.read_timeout = timeout;
}
pub async fn run<const CN: usize>(&self, wch: &WriteChannel<'_, CN>, t: &mut CB::T) {
loop {
if let Err(e) = self.run_logic(wch, t).await {
self.cb.err(e, t).await;
}
}
}
async fn run_logic<const CN: usize>(&self, wch: &WriteChannel<'_, CN>, t: &mut CB::T) -> SocketResult<()> {
let mut conn = self.try_accept().await?;
let endpoint = conn.socket.remote_endpoint().ok_or_else(SocketErr::no_route)?;
wch.enable().await;
self.cb.conn(endpoint, wch, t).await;
while !self.read_logic(&mut conn, endpoint, wch, t).await {}
wch.disable().await;
self.cb.dis_conn(endpoint, t).await;
Ok(())
}
async fn read_logic<const CN: usize>(
&self,
conn: &mut TcpConnection<'d, N, TX_SZ, RX_SZ, BUF_SIZE>,
endpoint: IpEndpoint,
wch: &WriteChannel<'_, CN>,
t: &mut CB::T) -> bool {
if !conn.socket.can_recv() {
if let Err(e) = self.write_logic(conn, wch).await { self.cb.err(e, t).await; }
return matches!(conn.socket.state(), State::CloseWait|State::Closed);
}
match conn.try_read().await {
Ok(bytes) => {
self.cb.recv(endpoint, bytes, wch, t).await;
false
}
Err(e) => {
self.cb.err(e.into(), t).await;
true
}
}
}
async fn write_logic<const CN: usize>(
&self,
conn: &mut TcpConnection<'d, N, TX_SZ, RX_SZ, BUF_SIZE>,
wch: &WriteChannel<'_, CN>) -> SocketResult<()> {
if wch.is_empty().await {
Timer::after(self.read_timeout).await;
return Ok(());
}
wch.try_tcp_write(conn).await?;
Ok(())
}
async fn try_accept(&self) -> SocketResult<TcpConnection<'d, N, TX_SZ, RX_SZ, BUF_SIZE>> {
self.stack.wait_link_up().await;
self.stack.wait_config_up().await;
let mut conn = TcpConnection::new(self.stack, self.state)?;
conn.socket.set_timeout(self.socket_timeout);
conn.socket.accept(self.port).await?;
Ok(conn)
}
}