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