first commit
This commit is contained in:
+25
@@ -0,0 +1,25 @@
|
|||||||
|
# 编译输出目录
|
||||||
|
/target/
|
||||||
|
|
||||||
|
# 依赖锁文件
|
||||||
|
Cargo.lock
|
||||||
|
|
||||||
|
# 本地环境配置文件
|
||||||
|
.env
|
||||||
|
|
||||||
|
# 编辑器和 IDE 产生的文件
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# 日志文件
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# 临时文件
|
||||||
|
temp/
|
||||||
|
tmp/
|
||||||
|
|
||||||
|
# 覆盖配置文件
|
||||||
|
Cargo.toml.orig
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "VoiletTftp"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tokio = { version = "1.47.1", features = ["full"] }
|
||||||
|
async-tftp = "0.3.6"
|
||||||
|
slint = "1.12.1"
|
||||||
|
rfd = "0.15.4"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
slint-build = "1.12"
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
slint_build::compile("components/Window.slint").unwrap();
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { Button, VerticalBox } from "std-widgets.slint";
|
||||||
|
|
||||||
|
export global Globals {
|
||||||
|
//启动按钮状态
|
||||||
|
in-out property <bool> StartEnable: false;
|
||||||
|
//停止按钮状态
|
||||||
|
in-out property <bool> StopEnable: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export component MainWindow inherits Window {
|
||||||
|
title: "VoileTftpTools";
|
||||||
|
icon: @image-url("../tftp.png");
|
||||||
|
height: 180px;
|
||||||
|
width: 320px;
|
||||||
|
default-font-family: "Segoe UI";
|
||||||
|
default-font-size: 16px;
|
||||||
|
default-font-weight: 700;
|
||||||
|
in-out property <bool> StartEnable <=> Globals.StartEnable;
|
||||||
|
in-out property <bool> StopEnable <=> Globals.StopEnable;
|
||||||
|
//选择路径回调
|
||||||
|
callback BrowserFolder() -> string;
|
||||||
|
property <string> SelectFolder: "";
|
||||||
|
//启动回调
|
||||||
|
callback StartTftp();
|
||||||
|
//停止回调
|
||||||
|
callback StopTftp();
|
||||||
|
//选择路径按钮
|
||||||
|
Rectangle {
|
||||||
|
height: 35px;
|
||||||
|
width: 95px;
|
||||||
|
x: 10px;
|
||||||
|
y: 10px;
|
||||||
|
Button {
|
||||||
|
height: parent.height;
|
||||||
|
width: parent.width;
|
||||||
|
text: "选择路径";
|
||||||
|
clicked => {
|
||||||
|
SelectFolder = BrowserFolder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 35px;
|
||||||
|
width: 95px;
|
||||||
|
x: 110px;
|
||||||
|
y: 10px;
|
||||||
|
Button {
|
||||||
|
height: parent.height;
|
||||||
|
width: parent.width;
|
||||||
|
text: "启动";
|
||||||
|
enabled: StartEnable;
|
||||||
|
clicked => {
|
||||||
|
StartTftp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 35px;
|
||||||
|
width: 95px;
|
||||||
|
x: 210px;
|
||||||
|
y: 10px;
|
||||||
|
Button {
|
||||||
|
height: parent.height;
|
||||||
|
width: parent.width;
|
||||||
|
text: "停止";
|
||||||
|
enabled: StopEnable;
|
||||||
|
clicked => {
|
||||||
|
StopTftp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
x: 13px;
|
||||||
|
y: 55px;
|
||||||
|
text: "路径:";
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
x: 60px;
|
||||||
|
y: 55px;
|
||||||
|
width: 200px;
|
||||||
|
wrap: word-wrap; // 自动换行关键属性
|
||||||
|
text: SelectFolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
0001 ICON "logo.ico"
|
||||||
Binary file not shown.
+107
@@ -0,0 +1,107 @@
|
|||||||
|
#![windows_subsystem = "windows"]
|
||||||
|
use async_tftp::server::TftpServerBuilder;
|
||||||
|
use async_tftp::Result;
|
||||||
|
use rfd::FileDialog;
|
||||||
|
use slint::SharedString;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use tokio::task::{AbortHandle, JoinHandle};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
slint::include_modules!();
|
||||||
|
struct ServerState {
|
||||||
|
abort_handle: Option<AbortHandle>,
|
||||||
|
}
|
||||||
|
async fn start_tftp_server(dir: PathBuf) -> Result<(JoinHandle<()>, AbortHandle)> {
|
||||||
|
let task = tokio::spawn(async move {
|
||||||
|
println!("Starting TFTP server at: {:?}", dir);
|
||||||
|
let tftpd = TftpServerBuilder::with_dir_rw(&dir)
|
||||||
|
.expect("Failed to create server")
|
||||||
|
.build()
|
||||||
|
.await
|
||||||
|
.expect("Failed to bind socket");
|
||||||
|
|
||||||
|
if let Err(e) = tftpd.serve().await {
|
||||||
|
eprintln!("Server error: {}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let abort_handle = task.abort_handle();
|
||||||
|
Ok((task, abort_handle))
|
||||||
|
}
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let ui = MainWindow::new().unwrap();
|
||||||
|
let state = Arc::new(Mutex::new(ServerState { abort_handle: None }));
|
||||||
|
let folder_path = Arc::new(Mutex::new(String::new()));
|
||||||
|
let path_clone = folder_path.clone();
|
||||||
|
let ui_weak = ui.as_weak();
|
||||||
|
|
||||||
|
//选择路径按钮的回调函数
|
||||||
|
ui.on_BrowserFolder(move || {
|
||||||
|
let result = FileDialog::new()
|
||||||
|
.set_directory("/")
|
||||||
|
.pick_folder();
|
||||||
|
let mut path = result.map(|p| p.to_string_lossy().into_owned())
|
||||||
|
.unwrap_or_default();
|
||||||
|
if path == "" {
|
||||||
|
println!("TFTP Dir Empty !");
|
||||||
|
if let Some(ui) = ui_weak.upgrade() {
|
||||||
|
ui.set_StartEnable(false);
|
||||||
|
}
|
||||||
|
path = String::from("");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if let Some(ui) = ui_weak.upgrade() {
|
||||||
|
ui.set_StartEnable(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*path_clone.lock().unwrap() = path.clone(); // 存储到变量
|
||||||
|
SharedString::from(path)
|
||||||
|
});
|
||||||
|
//启动按钮的回调函数
|
||||||
|
let state_clone = state.clone();
|
||||||
|
let path_clone = Arc::clone(&folder_path);
|
||||||
|
let ui_weak = ui.as_weak();
|
||||||
|
ui.on_StartTftp(move || {
|
||||||
|
let dir = PathBuf::from(&*path_clone.lock().unwrap());
|
||||||
|
if dir.iter().count() == 0 {
|
||||||
|
println!("TFTP Dir Empty !");
|
||||||
|
if let Some(ui) = ui_weak.upgrade() {
|
||||||
|
ui.set_StartEnable(true);
|
||||||
|
ui.set_StopEnable(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if let Some(ui) = ui_weak.upgrade() {
|
||||||
|
ui.set_StartEnable(false);
|
||||||
|
ui.set_StopEnable(true);
|
||||||
|
}
|
||||||
|
println!("{:?}",dir);
|
||||||
|
let state = state_clone.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
match start_tftp_server(dir).await {
|
||||||
|
Ok((_handle, abort_handle)) => {
|
||||||
|
println!("Server started");
|
||||||
|
// 存储 abort_handle 以便停止
|
||||||
|
state.lock().unwrap().abort_handle = Some(abort_handle);
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("Error: {}", e),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//停止按钮的回调函数
|
||||||
|
let ui_weak = ui.as_weak();
|
||||||
|
let state_clone = state.clone();
|
||||||
|
ui.on_StopTftp(move || {
|
||||||
|
if let Some(ui) = ui_weak.upgrade() {
|
||||||
|
ui.set_StartEnable(true);
|
||||||
|
ui.set_StopEnable(false);
|
||||||
|
}
|
||||||
|
if let Some(handle) = state_clone.lock().unwrap().abort_handle.take() {
|
||||||
|
handle.abort();
|
||||||
|
println!("Server stopped");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.run().unwrap();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user