feat: 前端显示 mdns 搜索到的板子连接信息。
This commit is contained in:
parent
e5527ce3c3
commit
f6e3257670
@ -13,6 +13,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@solidjs/router": "^0.8.2",
|
"@solidjs/router": "^0.8.2",
|
||||||
"@tauri-apps/api": "^1.2.0",
|
"@tauri-apps/api": "^1.2.0",
|
||||||
|
"debug": "^4.3.4",
|
||||||
"solid-icons": "^1.0.4",
|
"solid-icons": "^1.0.4",
|
||||||
"solid-js": "^1.4.7",
|
"solid-js": "^1.4.7",
|
||||||
"solid-tippy": "^0.2.1",
|
"solid-tippy": "^0.2.1",
|
||||||
@ -20,6 +21,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^1.2.2",
|
"@tauri-apps/cli": "^1.2.2",
|
||||||
|
"@types/debug": "^4.1.7",
|
||||||
"@types/node": "^18.7.10",
|
"@types/node": "^18.7.10",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
|
@ -7,6 +7,9 @@ dependencies:
|
|||||||
'@tauri-apps/api':
|
'@tauri-apps/api':
|
||||||
specifier: ^1.2.0
|
specifier: ^1.2.0
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
|
debug:
|
||||||
|
specifier: ^4.3.4
|
||||||
|
version: 4.3.4
|
||||||
solid-icons:
|
solid-icons:
|
||||||
specifier: ^1.0.4
|
specifier: ^1.0.4
|
||||||
version: 1.0.4(solid-js@1.6.14)
|
version: 1.0.4(solid-js@1.6.14)
|
||||||
@ -24,6 +27,9 @@ devDependencies:
|
|||||||
'@tauri-apps/cli':
|
'@tauri-apps/cli':
|
||||||
specifier: ^1.2.2
|
specifier: ^1.2.2
|
||||||
version: 1.2.3
|
version: 1.2.3
|
||||||
|
'@types/debug':
|
||||||
|
specifier: ^4.1.7
|
||||||
|
version: 4.1.7
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^18.7.10
|
specifier: ^18.7.10
|
||||||
version: 18.15.3
|
version: 18.15.3
|
||||||
@ -766,6 +772,16 @@ packages:
|
|||||||
'@babel/types': 7.21.3
|
'@babel/types': 7.21.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/debug@4.1.7:
|
||||||
|
resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==}
|
||||||
|
dependencies:
|
||||||
|
'@types/ms': 0.7.31
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@types/ms@0.7.31:
|
||||||
|
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/node@18.15.3:
|
/@types/node@18.15.3:
|
||||||
resolution: {integrity: sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==}
|
resolution: {integrity: sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -939,7 +955,6 @@ packages:
|
|||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.2
|
ms: 2.1.2
|
||||||
dev: true
|
|
||||||
|
|
||||||
/defined@1.0.1:
|
/defined@1.0.1:
|
||||||
resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==}
|
resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==}
|
||||||
@ -1175,7 +1190,6 @@ packages:
|
|||||||
|
|
||||||
/ms@2.1.2:
|
/ms@2.1.2:
|
||||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/nanoid@3.3.4:
|
/nanoid@3.3.4:
|
||||||
resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
|
resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
|
||||||
|
@ -11,7 +11,7 @@ mod screenshot_manager;
|
|||||||
use ambient_light::{Border, ColorCalibration, LedStripConfig, LedStripConfigGroup};
|
use ambient_light::{Border, ColorCalibration, LedStripConfig, LedStripConfigGroup};
|
||||||
use display_info::DisplayInfo;
|
use display_info::DisplayInfo;
|
||||||
use paris::{error, info, warn};
|
use paris::{error, info, warn};
|
||||||
use rpc::{MqttRpc, UdpRpc};
|
use rpc::{BoardInfo, MqttRpc, UdpRpc};
|
||||||
use screenshot::Screenshot;
|
use screenshot::Screenshot;
|
||||||
use screenshot_manager::ScreenshotManager;
|
use screenshot_manager::ScreenshotManager;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -188,6 +188,21 @@ async fn read_config() -> ambient_light::LedStripConfigGroup {
|
|||||||
config_manager.configs().await
|
config_manager.configs().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn get_boards() -> Result<Vec<BoardInfo>, String> {
|
||||||
|
let udp_rpc = UdpRpc::global().await;
|
||||||
|
|
||||||
|
if let Err(e) = udp_rpc {
|
||||||
|
return Err(format!("can not ping: {}", e));
|
||||||
|
}
|
||||||
|
|
||||||
|
let udp_rpc = udp_rpc.as_ref().unwrap();
|
||||||
|
|
||||||
|
let boards = udp_rpc.get_boards().await;
|
||||||
|
let boards = boards.into_iter().collect::<Vec<_>>();
|
||||||
|
Ok(boards)
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
@ -200,8 +215,6 @@ async fn main() {
|
|||||||
|
|
||||||
let _mqtt = MqttRpc::global().await;
|
let _mqtt = MqttRpc::global().await;
|
||||||
|
|
||||||
let _udp = UdpRpc::global().await;
|
|
||||||
|
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
greet,
|
greet,
|
||||||
@ -216,6 +229,7 @@ async fn main() {
|
|||||||
reverse_led_strip_part,
|
reverse_led_strip_part,
|
||||||
set_color_calibration,
|
set_color_calibration,
|
||||||
read_config,
|
read_config,
|
||||||
|
get_boards,
|
||||||
])
|
])
|
||||||
.register_uri_scheme_protocol("ambient-light", move |_app, request| {
|
.register_uri_scheme_protocol("ambient-light", move |_app, request| {
|
||||||
let response = ResponseBuilder::new().header("Access-Control-Allow-Origin", "*");
|
let response = ResponseBuilder::new().header("Access-Control-Allow-Origin", "*");
|
||||||
@ -400,6 +414,33 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let app_handle = app.handle().clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
match UdpRpc::global().await {
|
||||||
|
Ok(udp_rpc) => {
|
||||||
|
let mut receiver = udp_rpc.clone_boards_change_receiver().await;
|
||||||
|
loop {
|
||||||
|
if let Err(err) = receiver.changed().await {
|
||||||
|
error!("boards change receiver changed error: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let boards = receiver.borrow().clone();
|
||||||
|
|
||||||
|
let boards = boards.into_iter().collect::<Vec<_>>();
|
||||||
|
|
||||||
|
app_handle.emit_all("boards_changed", boards).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("udp rpc error: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use std::net::{Ipv4Addr};
|
use std::net::{Ipv4Addr};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||||
pub struct BoardInfo {
|
pub struct BoardInfo {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub address: Ipv4Addr,
|
pub address: Ipv4Addr,
|
||||||
|
@ -4,7 +4,7 @@ use mdns_sd::{ServiceDaemon, ServiceEvent};
|
|||||||
use paris::{error, info, warn};
|
use paris::{error, info, warn};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
net::UdpSocket,
|
net::UdpSocket,
|
||||||
sync::{Mutex, OnceCell},
|
sync::{watch, Mutex, OnceCell},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::BoardInfo;
|
use super::BoardInfo;
|
||||||
@ -13,6 +13,8 @@ use super::BoardInfo;
|
|||||||
pub struct UdpRpc {
|
pub struct UdpRpc {
|
||||||
socket: Arc<Mutex<UdpSocket>>,
|
socket: Arc<Mutex<UdpSocket>>,
|
||||||
boards: Arc<Mutex<HashSet<BoardInfo>>>,
|
boards: Arc<Mutex<HashSet<BoardInfo>>>,
|
||||||
|
boards_change_sender: Arc<Mutex<watch::Sender<HashSet<BoardInfo>>>>,
|
||||||
|
boards_change_receiver: Arc<Mutex<watch::Receiver<HashSet<BoardInfo>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UdpRpc {
|
impl UdpRpc {
|
||||||
@ -32,7 +34,15 @@ impl UdpRpc {
|
|||||||
let socket = UdpSocket::bind("0.0.0.0:0").await?;
|
let socket = UdpSocket::bind("0.0.0.0:0").await?;
|
||||||
let socket = Arc::new(Mutex::new(socket));
|
let socket = Arc::new(Mutex::new(socket));
|
||||||
let boards = Arc::new(Mutex::new(HashSet::new()));
|
let boards = Arc::new(Mutex::new(HashSet::new()));
|
||||||
Ok(Self { socket, boards })
|
let (boards_change_sender, boards_change_receiver) = watch::channel(HashSet::new());
|
||||||
|
let boards_change_sender = Arc::new(Mutex::new(boards_change_sender));
|
||||||
|
let boards_change_receiver = Arc::new(Mutex::new(boards_change_receiver));
|
||||||
|
Ok(Self {
|
||||||
|
socket,
|
||||||
|
boards,
|
||||||
|
boards_change_sender,
|
||||||
|
boards_change_receiver,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn initialize(&self) {
|
async fn initialize(&self) {
|
||||||
@ -52,12 +62,10 @@ impl UdpRpc {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn search_boards(&self) -> anyhow::Result<()> {
|
async fn search_boards(&self) -> anyhow::Result<()> {
|
||||||
|
let service_type = "_ambient_light._udp.local.";
|
||||||
let mdns = ServiceDaemon::new()?;
|
let mdns = ServiceDaemon::new()?;
|
||||||
let shared_self = Arc::new(Mutex::new(self.clone()));
|
let shared_self = Arc::new(Mutex::new(self.clone()));
|
||||||
|
|
||||||
let service_type = "_ambient_light._udp.local.";
|
|
||||||
|
|
||||||
let receiver = mdns.browse(&service_type).map_err(|e| {
|
let receiver = mdns.browse(&service_type).map_err(|e| {
|
||||||
warn!("Failed to browse for {:?}: {:?}", service_type, e);
|
warn!("Failed to browse for {:?}: {:?}", service_type, e);
|
||||||
e
|
e
|
||||||
@ -87,6 +95,9 @@ impl UdpRpc {
|
|||||||
if boards.insert(board.clone()) {
|
if boards.insert(board.clone()) {
|
||||||
info!("added board {:?}", board);
|
info!("added board {:?}", board);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let sender = self.boards_change_sender.clone().lock_owned().await;
|
||||||
|
sender.send(boards.clone())?;
|
||||||
}
|
}
|
||||||
other_event => {
|
other_event => {
|
||||||
warn!("{:?}", &other_event);
|
warn!("{:?}", &other_event);
|
||||||
@ -96,4 +107,16 @@ impl UdpRpc {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn clone_boards_change_receiver(
|
||||||
|
&self,
|
||||||
|
) -> watch::Receiver<HashSet<BoardInfo>> {
|
||||||
|
let boards_change_receiver = self.boards_change_receiver.clone().lock_owned().await;
|
||||||
|
boards_change_receiver.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_boards(&self) -> HashSet<BoardInfo> {
|
||||||
|
let boards = self.boards.clone().lock_owned().await;
|
||||||
|
boards.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import { createEffect } from 'solid-js';
|
|||||||
import { invoke } from '@tauri-apps/api';
|
import { invoke } from '@tauri-apps/api';
|
||||||
import { setLedStripStore } from './stores/led-strip.store';
|
import { setLedStripStore } from './stores/led-strip.store';
|
||||||
import { LedStripConfigContainer } from './models/led-strip-config';
|
import { LedStripConfigContainer } from './models/led-strip-config';
|
||||||
|
import { InfoIndex } from './components/info/info-index';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
@ -21,10 +22,12 @@ function App() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
|
<a href="/info">基本信息</a>
|
||||||
<a href="/led-strips-configuration">灯条配置</a>
|
<a href="/led-strips-configuration">灯条配置</a>
|
||||||
<a href="/white-balance">白平衡</a>
|
<a href="/white-balance">白平衡</a>
|
||||||
</div>
|
</div>
|
||||||
<Routes>
|
<Routes>
|
||||||
|
<Route path="/info" component={InfoIndex} />
|
||||||
<Route path="/led-strips-configuration" component={LedStripConfiguration} />
|
<Route path="/led-strips-configuration" component={LedStripConfiguration} />
|
||||||
<Route path="/white-balance" component={WhiteBalance} />
|
<Route path="/white-balance" component={WhiteBalance} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
52
src/components/info/board-index.tsx
Normal file
52
src/components/info/board-index.tsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { Component, For, createEffect, createSignal } from 'solid-js';
|
||||||
|
import { BoardInfo } from '../../models/board-info.model';
|
||||||
|
import { listen } from '@tauri-apps/api/event';
|
||||||
|
import debug from 'debug';
|
||||||
|
import { invoke } from '@tauri-apps/api';
|
||||||
|
|
||||||
|
const logger = debug('app:components:info:board-index');
|
||||||
|
|
||||||
|
export const BoardIndex: Component = () => {
|
||||||
|
const [boards, setBoards] = createSignal<BoardInfo[]>([]);
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const unlisten = listen<BoardInfo[]>('boards_changed', (ev) => {
|
||||||
|
logger('boards_changed', ev);
|
||||||
|
setBoards(ev.payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
invoke<BoardInfo[]>('get_boards').then((boards) => {
|
||||||
|
logger('get_boards', boards);
|
||||||
|
setBoards(boards);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unlisten.then((unlisten) => unlisten());
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<ol class="grid sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 p-2 gap-2">
|
||||||
|
<For each={boards()}>
|
||||||
|
{(board, index) => (
|
||||||
|
<li class="p-2 rounded shadow bg-slate-50 text-gray-800 relative border-2 border-slate-50 hover:border-sky-300 focus:border-sky-300 transition">
|
||||||
|
<dl class="flex">
|
||||||
|
<dt class="w-20">host</dt>
|
||||||
|
<dd class="flex-auto">{board.name}</dd>
|
||||||
|
</dl>
|
||||||
|
<dl class="flex">
|
||||||
|
<dt class="w-20">Ip Addr</dt>
|
||||||
|
<dd class="flex-auto font-mono">{board.address}</dd>
|
||||||
|
</dl>
|
||||||
|
<dl class="flex">
|
||||||
|
<dt class="w-20">Port</dt>
|
||||||
|
<dd class="flex-auto font-mono">{board.port}</dd>
|
||||||
|
</dl>
|
||||||
|
<span class="absolute left-2 -top-3 bg-sky-300 text-white px-1 py-0.5 text-xs rounded-sm font-mono">
|
||||||
|
#{index() + 1}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</ol>
|
||||||
|
);
|
||||||
|
};
|
10
src/components/info/info-index.tsx
Normal file
10
src/components/info/info-index.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { Component } from 'solid-js';
|
||||||
|
import { BoardIndex } from './board-index';
|
||||||
|
|
||||||
|
export const InfoIndex: Component = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<BoardIndex />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
5
src/models/board-info.model.ts
Normal file
5
src/models/board-info.model.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export type BoardInfo = {
|
||||||
|
name: string;
|
||||||
|
address: string;
|
||||||
|
port: number;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user