feat: 增强连接状态。
This commit is contained in:
parent
6c90a5e655
commit
82d4adfe0f
@ -1,15 +1,23 @@
|
|||||||
use std::{net::Ipv4Addr, time::Duration};
|
use std::{net::Ipv4Addr, time::Duration};
|
||||||
|
|
||||||
use paris::warn;
|
use paris::{warn, info};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::{net::UdpSocket, time::timeout};
|
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 struct BoardInfo {
|
||||||
pub host: String,
|
pub host: String,
|
||||||
pub address: Ipv4Addr,
|
pub address: Ipv4Addr,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub is_online: bool,
|
pub connect_status: BoardConnectStatus,
|
||||||
pub checked_at: Option<std::time::SystemTime>,
|
pub checked_at: Option<std::time::SystemTime>,
|
||||||
pub ttl: Option<u128>,
|
pub ttl: Option<u128>,
|
||||||
}
|
}
|
||||||
@ -20,7 +28,7 @@ impl BoardInfo {
|
|||||||
host,
|
host,
|
||||||
address,
|
address,
|
||||||
port,
|
port,
|
||||||
is_online: false,
|
connect_status: BoardConnectStatus::Unknown,
|
||||||
checked_at: None,
|
checked_at: None,
|
||||||
ttl: None,
|
ttl: None,
|
||||||
}
|
}
|
||||||
@ -36,20 +44,39 @@ impl BoardInfo {
|
|||||||
let mut buf = [0u8; 1];
|
let mut buf = [0u8; 1];
|
||||||
let recv_future = socket.recv(&mut buf);
|
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(_) => {
|
Ok(_) => {
|
||||||
let ttl = instant.elapsed();
|
let ttl = instant.elapsed();
|
||||||
log::info!("buf: {:?}", buf);
|
|
||||||
if buf == [1] {
|
if buf == [1] {
|
||||||
self.is_online = true;
|
self.connect_status = BoardConnectStatus::Connected;
|
||||||
} else {
|
} 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());
|
self.ttl = Some(ttl.as_millis());
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
warn!("timeout");
|
if let BoardConnectStatus::Connecting(retry) = self.connect_status {
|
||||||
self.is_online = false;
|
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;
|
self.ttl = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 futures::future::join_all;
|
||||||
use mdns_sd::{ServiceDaemon, ServiceEvent};
|
use mdns_sd::{ServiceDaemon, ServiceEvent};
|
||||||
@ -8,12 +13,12 @@ use tokio::{
|
|||||||
sync::{watch, OnceCell, RwLock},
|
sync::{watch, OnceCell, RwLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::BoardInfo;
|
use super::{BoardConnectStatus, BoardInfo};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct UdpRpc {
|
pub struct UdpRpc {
|
||||||
socket: Arc<UdpSocket>,
|
socket: Arc<UdpSocket>,
|
||||||
boards: Arc<RwLock<HashSet<BoardInfo>>>,
|
boards: Arc<RwLock<HashMap<Ipv4Addr, BoardInfo>>>,
|
||||||
boards_change_sender: Arc<watch::Sender<Vec<BoardInfo>>>,
|
boards_change_sender: Arc<watch::Sender<Vec<BoardInfo>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +38,7 @@ impl UdpRpc {
|
|||||||
async fn new() -> anyhow::Result<Self> {
|
async fn new() -> anyhow::Result<Self> {
|
||||||
let socket = UdpSocket::bind("0.0.0.0:0").await?;
|
let socket = UdpSocket::bind("0.0.0.0:0").await?;
|
||||||
let socket = Arc::new(socket);
|
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, _) = watch::channel(Vec::new());
|
||||||
let boards_change_sender = Arc::new(boards_change_sender);
|
let boards_change_sender = Arc::new(boards_change_sender);
|
||||||
|
|
||||||
@ -109,11 +114,11 @@ impl UdpRpc {
|
|||||||
info.get_port(),
|
info.get_port(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if boards.insert(board.clone()) {
|
if boards.insert(board.address, board.clone()).is_some() {
|
||||||
info!("added board {:?}", board);
|
info!("added board {:?}", board);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tx_boards = boards.iter().cloned().collect();
|
let tx_boards = boards.values().cloned().collect();
|
||||||
drop(boards);
|
drop(boards);
|
||||||
|
|
||||||
sender.send(tx_boards)?;
|
sender.send(tx_boards)?;
|
||||||
@ -133,8 +138,8 @@ impl UdpRpc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_boards(&self) -> Vec<BoardInfo> {
|
pub async fn get_boards(&self) -> Vec<BoardInfo> {
|
||||||
let boards: tokio::sync::RwLockReadGuard<HashSet<BoardInfo>> = self.boards.read().await;
|
let boards = self.boards.read().await;
|
||||||
boards.iter().cloned().collect()
|
boards.values().cloned().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_to_all(&self, buff: &Vec<u8>) -> anyhow::Result<()> {
|
pub async fn send_to_all(&self, buff: &Vec<u8>) -> anyhow::Result<()> {
|
||||||
@ -160,29 +165,30 @@ impl UdpRpc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn check_boards(&self) {
|
pub async fn check_boards(&self) {
|
||||||
|
let mut interval = tokio::time::interval(Duration::from_secs(1));
|
||||||
loop {
|
loop {
|
||||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
let mut boards = self.boards.clone().write_owned().await;
|
||||||
|
|
||||||
let mut boards = self.get_boards().await;
|
|
||||||
|
|
||||||
if boards.is_empty() {
|
if boards.is_empty() {
|
||||||
info!("no boards found");
|
info!("no boards found");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for board in &mut boards {
|
for (_, board) in boards.iter_mut() {
|
||||||
if let Err(err) = board.check().await {
|
if let Err(err) = board.check().await {
|
||||||
error!("failed to check board {}: {:?}", board.host, err);
|
error!("failed to check board {}: {:?}", board.host, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let board_vec = boards.values().cloned().collect::<Vec<_>>();
|
||||||
|
drop(boards);
|
||||||
|
|
||||||
let board_change_sender = self.boards_change_sender.clone();
|
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);
|
error!("failed to send board change: {:?}", err);
|
||||||
} else {
|
|
||||||
info!("send");
|
|
||||||
}
|
}
|
||||||
drop(board_change_sender);
|
drop(board_change_sender);
|
||||||
|
interval.tick().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ const Item: ParentComponent<ItemProps> = (props) => {
|
|||||||
|
|
||||||
export const BoardInfoPanel: Component<{ board: BoardInfo }> = (props) => {
|
export const BoardInfoPanel: Component<{ board: BoardInfo }> = (props) => {
|
||||||
const ttl = createMemo(() => {
|
const ttl = createMemo(() => {
|
||||||
if (!props.board.is_online) {
|
if (props.board.connect_status !== 'Connected') {
|
||||||
return '--';
|
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 (
|
return (
|
||||||
<section class="p-2 rounded shadow">
|
<section class="p-2 rounded shadow">
|
||||||
<Item label="Host">{props.board.host}</Item>
|
<Item label="Host">{props.board.host}</Item>
|
||||||
@ -41,7 +51,7 @@ export const BoardInfoPanel: Component<{ board: BoardInfo }> = (props) => {
|
|||||||
<span class="font-mono">{props.board.port}</span>
|
<span class="font-mono">{props.board.port}</span>
|
||||||
</Item>
|
</Item>
|
||||||
<Item label="Status">
|
<Item label="Status">
|
||||||
<span class="font-mono">{props.board.is_online ? 'Online' : 'Offline'}</span>
|
<span class="font-mono">{connectStatus()}</span>
|
||||||
</Item>
|
</Item>
|
||||||
<Item label="TTL">{ttl()}</Item>
|
<Item label="TTL">{ttl()}</Item>
|
||||||
</section>
|
</section>
|
||||||
|
@ -3,6 +3,6 @@ export type BoardInfo = {
|
|||||||
address: string;
|
address: string;
|
||||||
port: number;
|
port: number;
|
||||||
ttl: number;
|
ttl: number;
|
||||||
is_online: boolean;
|
connect_status: 'Connected' | 'Disconnected' | { Connecting: number };
|
||||||
checked_at: Date;
|
checked_at: Date;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user