diff --git a/src-tauri/src/rpc/board_info.rs b/src-tauri/src/rpc/board_info.rs index f2ec2f1..73d7833 100644 --- a/src-tauri/src/rpc/board_info.rs +++ b/src-tauri/src/rpc/board_info.rs @@ -1,15 +1,23 @@ use std::{net::Ipv4Addr, time::Duration}; -use paris::warn; +use paris::{warn, info}; use serde::{Deserialize, Serialize}; use tokio::{net::UdpSocket, time::timeout}; -#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)] +pub enum BoardConnectStatus { + Connected, + Connecting(u8), + Disconnected, + Unknown, +} + +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct BoardInfo { pub host: String, pub address: Ipv4Addr, pub port: u16, - pub is_online: bool, + pub connect_status: BoardConnectStatus, pub checked_at: Option, pub ttl: Option, } @@ -20,7 +28,7 @@ impl BoardInfo { host, address, port, - is_online: false, + connect_status: BoardConnectStatus::Unknown, checked_at: None, ttl: None, } @@ -36,20 +44,39 @@ impl BoardInfo { let mut buf = [0u8; 1]; let recv_future = socket.recv(&mut buf); - match timeout(Duration::from_secs(5), recv_future).await { + match timeout(Duration::from_secs(1), recv_future).await { Ok(_) => { let ttl = instant.elapsed(); - log::info!("buf: {:?}", buf); if buf == [1] { - self.is_online = true; + self.connect_status = BoardConnectStatus::Connected; } else { - self.is_online = false; + if let BoardConnectStatus::Connecting(retry) = self.connect_status { + if retry < 10 { + self.connect_status = BoardConnectStatus::Connecting(retry + 1); + info!("reconnect: {}", retry + 1); + } else { + self.connect_status = BoardConnectStatus::Disconnected; + warn!("board Disconnected: bad pong."); + } + } else if self.connect_status != BoardConnectStatus::Disconnected { + self.connect_status = BoardConnectStatus::Connecting(1); + } } self.ttl = Some(ttl.as_millis()); } Err(_) => { - warn!("timeout"); - self.is_online = false; + if let BoardConnectStatus::Connecting(retry) = self.connect_status { + if retry < 10 { + self.connect_status = BoardConnectStatus::Connecting(retry + 1); + info!("reconnect: {}", retry + 1); + } else { + self.connect_status = BoardConnectStatus::Disconnected; + warn!("board Disconnected: timeout"); + } + } else if self.connect_status != BoardConnectStatus::Disconnected { + self.connect_status = BoardConnectStatus::Connecting(1); + } + self.ttl = None; } } diff --git a/src-tauri/src/rpc/udp.rs b/src-tauri/src/rpc/udp.rs index 4ed99e2..7f83257 100644 --- a/src-tauri/src/rpc/udp.rs +++ b/src-tauri/src/rpc/udp.rs @@ -1,4 +1,9 @@ -use std::{collections::HashSet, sync::Arc, time::Duration}; +use std::{ + collections::{HashMap, HashSet}, + net::Ipv4Addr, + sync::Arc, + time::Duration, +}; use futures::future::join_all; use mdns_sd::{ServiceDaemon, ServiceEvent}; @@ -8,12 +13,12 @@ use tokio::{ sync::{watch, OnceCell, RwLock}, }; -use super::BoardInfo; +use super::{BoardConnectStatus, BoardInfo}; #[derive(Debug, Clone)] pub struct UdpRpc { socket: Arc, - boards: Arc>>, + boards: Arc>>, boards_change_sender: Arc>>, } @@ -33,7 +38,7 @@ impl UdpRpc { async fn new() -> anyhow::Result { let socket = UdpSocket::bind("0.0.0.0:0").await?; let socket = Arc::new(socket); - let boards = Arc::new(RwLock::new(HashSet::new())); + let boards = Arc::new(RwLock::new(HashMap::new())); let (boards_change_sender, _) = watch::channel(Vec::new()); let boards_change_sender = Arc::new(boards_change_sender); @@ -109,11 +114,11 @@ impl UdpRpc { info.get_port(), ); - if boards.insert(board.clone()) { + if boards.insert(board.address, board.clone()).is_some() { info!("added board {:?}", board); } - let tx_boards = boards.iter().cloned().collect(); + let tx_boards = boards.values().cloned().collect(); drop(boards); sender.send(tx_boards)?; @@ -133,8 +138,8 @@ impl UdpRpc { } pub async fn get_boards(&self) -> Vec { - let boards: tokio::sync::RwLockReadGuard> = self.boards.read().await; - boards.iter().cloned().collect() + let boards = self.boards.read().await; + boards.values().cloned().collect() } pub async fn send_to_all(&self, buff: &Vec) -> anyhow::Result<()> { @@ -160,29 +165,30 @@ impl UdpRpc { } pub async fn check_boards(&self) { + let mut interval = tokio::time::interval(Duration::from_secs(1)); loop { - tokio::time::sleep(Duration::from_secs(1)).await; - - let mut boards = self.get_boards().await; + let mut boards = self.boards.clone().write_owned().await; if boards.is_empty() { info!("no boards found"); continue; } - for board in &mut boards { + for (_, board) in boards.iter_mut() { if let Err(err) = board.check().await { error!("failed to check board {}: {:?}", board.host, err); } } + let board_vec = boards.values().cloned().collect::>(); + drop(boards); + let board_change_sender = self.boards_change_sender.clone(); - if let Err(err) = board_change_sender.send(boards) { + if let Err(err) = board_change_sender.send(board_vec) { error!("failed to send board change: {:?}", err); - } else { - info!("send"); } drop(board_change_sender); + interval.tick().await; } } } diff --git a/src/components/info/board-info-panel.tsx b/src/components/info/board-info-panel.tsx index 56fb949..d3b5a7e 100644 --- a/src/components/info/board-info-panel.tsx +++ b/src/components/info/board-info-panel.tsx @@ -16,7 +16,7 @@ const Item: ParentComponent = (props) => { export const BoardInfoPanel: Component<{ board: BoardInfo }> = (props) => { const ttl = createMemo(() => { - if (!props.board.is_online) { + if (props.board.connect_status !== 'Connected') { return '--'; } @@ -31,6 +31,16 @@ export const BoardInfoPanel: Component<{ board: BoardInfo }> = (props) => { ); }); + const connectStatus = createMemo(() => { + if (typeof props.board.connect_status === 'string') { + return props.board.connect_status; + } + + if ('Connecting' in props.board.connect_status) { + return `Connecting (${props.board.connect_status.Connecting.toFixed(0)})`; + } + }); + return (
{props.board.host} @@ -41,7 +51,7 @@ export const BoardInfoPanel: Component<{ board: BoardInfo }> = (props) => { {props.board.port} - {props.board.is_online ? 'Online' : 'Offline'} + {connectStatus()} {ttl()}
diff --git a/src/models/board-info.model.ts b/src/models/board-info.model.ts index 6cb261f..e43e186 100644 --- a/src/models/board-info.model.ts +++ b/src/models/board-info.model.ts @@ -3,6 +3,6 @@ export type BoardInfo = { address: string; port: number; ttl: number; - is_online: boolean; + connect_status: 'Connected' | 'Disconnected' | { Connecting: number }; checked_at: Date; };