|
|
@@ -7,182 +7,138 @@ use serialport; |
|
|
|
use actix_web::{web, App, HttpServer, HttpResponse}; |
|
|
|
use actix_cors::Cors; |
|
|
|
use std::fs; |
|
|
|
use futures_util::{SinkExt, StreamExt}; |
|
|
|
use tokio::net::TcpListener; |
|
|
|
use tokio_tungstenite::accept_async; |
|
|
|
use tokio::sync::broadcast; |
|
|
|
use std::path::PathBuf; |
|
|
|
use std::sync::Mutex; |
|
|
|
use once_cell::sync::Lazy; |
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize)] |
|
|
|
struct WeightData { |
|
|
|
weight: String, |
|
|
|
timestamp: u64, |
|
|
|
is_read: bool, |
|
|
|
} |
|
|
|
|
|
|
|
#[derive(Serialize)] |
|
|
|
struct MacResponse { |
|
|
|
mac_address: String, |
|
|
|
} |
|
|
|
|
|
|
|
#[derive(Serialize)] |
|
|
|
struct ScaleTypeResponse { |
|
|
|
scale_type: String, |
|
|
|
} |
|
|
|
|
|
|
|
#[derive(Deserialize)] |
|
|
|
#[derive(Serialize, Deserialize)] |
|
|
|
struct Config { |
|
|
|
scale_type: String, |
|
|
|
serial_port: String, |
|
|
|
baud_rate: u32, |
|
|
|
} |
|
|
|
|
|
|
|
// 读取配置文件 |
|
|
|
fn read_config() -> Result<Config> { |
|
|
|
let config_path = "config.json"; |
|
|
|
let content = fs::read_to_string(config_path) |
|
|
|
.with_context(|| format!("无法读取配置文件 {}", config_path))?; |
|
|
|
let config: Config = serde_json::from_str(&content) |
|
|
|
.with_context(|| format!("配置文件格式错误 {}", config_path))?; |
|
|
|
let config_str = fs::read_to_string("config.json")?; |
|
|
|
let config: Config = serde_json::from_str(&config_str)?; |
|
|
|
Ok(config) |
|
|
|
} |
|
|
|
|
|
|
|
fn get_mac_address() -> String { |
|
|
|
match read_config() { |
|
|
|
Ok(config) => config.scale_type, |
|
|
|
Err(_) => String::from("Unknown") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn get_scale_type() -> String { |
|
|
|
match read_config() { |
|
|
|
Ok(config) => config.scale_type, |
|
|
|
Err(_) => String::from("Unknown") |
|
|
|
} |
|
|
|
} |
|
|
|
// 使用全局静态变量来存储重量数据 |
|
|
|
static WEIGHT_DATA: Lazy<Mutex<WeightData>> = Lazy::new(|| { |
|
|
|
Mutex::new(WeightData { |
|
|
|
weight: "0".to_string(), |
|
|
|
timestamp: 0, |
|
|
|
is_read: false, |
|
|
|
}) |
|
|
|
}); |
|
|
|
|
|
|
|
// HTTP处理函数 |
|
|
|
// 获取MAC地址的HTTP处理函数 |
|
|
|
async fn get_mac() -> HttpResponse { |
|
|
|
let mac = get_mac_address(); |
|
|
|
println!("HTTP请求:获取MAC地址 = {}", mac); |
|
|
|
let response = MacResponse { |
|
|
|
mac_address: mac, |
|
|
|
}; |
|
|
|
let response = serde_json::json!({ |
|
|
|
"mac": "00:11:22:33:44:55" // 这里替换为实际的MAC地址获取逻辑 |
|
|
|
}); |
|
|
|
HttpResponse::Ok().json(response) |
|
|
|
} |
|
|
|
|
|
|
|
// 新增的HTTP处理函数 |
|
|
|
// 获取秤类型的HTTP处理函数 |
|
|
|
async fn get_scale() -> HttpResponse { |
|
|
|
let scale_type = get_scale_type(); |
|
|
|
println!("HTTP请求:获取天平类型 = {}", scale_type); |
|
|
|
let response = ScaleTypeResponse { |
|
|
|
scale_type, |
|
|
|
let config = match read_config() { |
|
|
|
Ok(config) => config, |
|
|
|
Err(_) => return HttpResponse::InternalServerError().finish(), |
|
|
|
}; |
|
|
|
|
|
|
|
let response = serde_json::json!({ |
|
|
|
"scale_type": config.scale_type |
|
|
|
}); |
|
|
|
HttpResponse::Ok().json(response) |
|
|
|
} |
|
|
|
|
|
|
|
// WebSocket测试客户端函数 |
|
|
|
async fn test_websocket_client() -> Result<()> { |
|
|
|
println!("开始测试WebSocket客户端..."); |
|
|
|
|
|
|
|
// 连接到WebSocket服务器 |
|
|
|
println!("正在连接到WebSocket服务器..."); |
|
|
|
let (ws_stream, _) = tokio_tungstenite::connect_async("ws://127.0.0.1:8081") |
|
|
|
.await |
|
|
|
.context("无法连接到WebSocket服务器")?; |
|
|
|
println!("已成功连接到WebSocket服务器"); |
|
|
|
|
|
|
|
let (_, mut read) = ws_stream.split(); |
|
|
|
|
|
|
|
// 持续接收消息 |
|
|
|
println!("等待接收数据..."); |
|
|
|
while let Some(message) = read.next().await { |
|
|
|
match message { |
|
|
|
Ok(msg) => { |
|
|
|
if let Ok(text) = msg.into_text() { |
|
|
|
println!("收到原始数据: {}", text); |
|
|
|
// 尝试解析JSON数据 |
|
|
|
match serde_json::from_str::<WeightData>(&text) { |
|
|
|
Ok(weight_data) => { |
|
|
|
println!("收到重量数据: {} (时间戳: {})", weight_data.weight, weight_data.timestamp); |
|
|
|
} |
|
|
|
Err(e) => { |
|
|
|
println!("JSON解析错误: {}", e); |
|
|
|
println!("原始数据: {}", text); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
Err(e) => { |
|
|
|
eprintln!("WebSocket接收错误: {}", e); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
// 获取重量数据的HTTP处理函数 |
|
|
|
async fn get_weight() -> HttpResponse { |
|
|
|
let mut weight_data = WEIGHT_DATA.lock().unwrap(); |
|
|
|
if weight_data.is_read { |
|
|
|
// 如果数据已读,返回 null |
|
|
|
HttpResponse::Ok().json(serde_json::json!({"weight": null})) |
|
|
|
} else { |
|
|
|
// 标记为已读并返回数据 |
|
|
|
weight_data.is_read = true; |
|
|
|
HttpResponse::Ok().json(&*weight_data) |
|
|
|
} |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
|
|
|
|
// 模拟串口数据发送 |
|
|
|
async fn simulate_serial_data() -> Result<()> { |
|
|
|
println!("开始模拟串口数据发送..."); |
|
|
|
// 串口数据处理函数 |
|
|
|
async fn run_serial() -> Result<()> { |
|
|
|
// 读取配置文件 |
|
|
|
let config = read_config()?; |
|
|
|
|
|
|
|
// 创建一个TCP连接到WebSocket服务器 |
|
|
|
println!("正在连接到WebSocket服务器..."); |
|
|
|
let (ws_stream, _) = tokio_tungstenite::connect_async("ws://127.0.0.1:8081") |
|
|
|
.await |
|
|
|
.context("无法连接到WebSocket服务器")?; |
|
|
|
println!("已成功连接到WebSocket服务器"); |
|
|
|
|
|
|
|
let (mut write, _) = ws_stream.split(); |
|
|
|
// 打开配置的串口 |
|
|
|
println!("正在尝试打开串口 {}...", config.serial_port); |
|
|
|
let mut port = serialport::new(&config.serial_port, config.baud_rate) |
|
|
|
.timeout(Duration::from_millis(1000)) |
|
|
|
.open() |
|
|
|
.with_context(|| format!("无法打开串口 {}", config.serial_port))?; |
|
|
|
|
|
|
|
println!("串口打开成功"); |
|
|
|
|
|
|
|
// 模拟每秒发送一个随机重量数据 |
|
|
|
let mut interval = tokio::time::interval(Duration::from_secs(1)); |
|
|
|
let mut counter = 0; |
|
|
|
let mut buf = [0u8; 1024]; |
|
|
|
let mut accumulated_data = String::new(); |
|
|
|
|
|
|
|
println!("开始发送模拟数据 (按Ctrl+C退出)..."); |
|
|
|
loop { |
|
|
|
interval.tick().await; |
|
|
|
counter += 1; |
|
|
|
|
|
|
|
// 生成一个模拟的重量值(10.5 - 20.5之间的随机数) |
|
|
|
let weight = format!("{:.1}", 10.5 + (counter % 10) as f32); |
|
|
|
|
|
|
|
// 创建数据结构 |
|
|
|
let weight_data = WeightData { |
|
|
|
weight: weight.clone(), |
|
|
|
timestamp: std::time::SystemTime::now() |
|
|
|
.duration_since(std::time::UNIX_EPOCH) |
|
|
|
.unwrap() |
|
|
|
.as_secs(), |
|
|
|
}; |
|
|
|
|
|
|
|
// 序列化并发送数据 |
|
|
|
if let Ok(json_data) = serde_json::to_string(&weight_data) { |
|
|
|
println!("发送模拟数据: {} (JSON: {})", weight, json_data); |
|
|
|
if let Err(e) = write.send(tokio_tungstenite::tungstenite::Message::Text(json_data)).await { |
|
|
|
eprintln!("发送数据错误: {}", e); |
|
|
|
break; |
|
|
|
match port.read(&mut buf) { |
|
|
|
Ok(bytes_read) if bytes_read > 0 => { |
|
|
|
// 将读取的数据转换为字符串并添加到累积的数据中 |
|
|
|
if let Ok(data) = String::from_utf8(buf[..bytes_read].to_vec()) { |
|
|
|
accumulated_data.push_str(&data); |
|
|
|
|
|
|
|
// 检查是否有完整的行 |
|
|
|
if accumulated_data.contains('\n') { |
|
|
|
// 处理所有完整的行 |
|
|
|
for line in accumulated_data.lines() { |
|
|
|
let trimmed_data = line.trim(); |
|
|
|
|
|
|
|
if !trimmed_data.is_empty() { |
|
|
|
println!("收到串口数据: {}", trimmed_data); |
|
|
|
|
|
|
|
// 更新内存中的数据(直接覆盖) |
|
|
|
let mut weight_data = WEIGHT_DATA.lock().unwrap(); |
|
|
|
weight_data.weight = trimmed_data.to_string(); |
|
|
|
weight_data.timestamp = std::time::SystemTime::now() |
|
|
|
.duration_since(std::time::UNIX_EPOCH) |
|
|
|
.unwrap() |
|
|
|
.as_secs(); |
|
|
|
weight_data.is_read = false; // 新数据未读 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 清除已处理的数据 |
|
|
|
accumulated_data.clear(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
Err(ref e) if e.kind() == io::ErrorKind::TimedOut => { |
|
|
|
// 超时是正常的,继续尝试读取 |
|
|
|
continue; |
|
|
|
} |
|
|
|
println!("数据发送成功"); |
|
|
|
Err(e) => { |
|
|
|
eprintln!("串口读取错误: {}", e); |
|
|
|
return Err(e.into()); |
|
|
|
} |
|
|
|
_ => {} |
|
|
|
} |
|
|
|
|
|
|
|
// 等待一小段时间 |
|
|
|
tokio::time::sleep(Duration::from_secs(1)).await; |
|
|
|
} |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
|
|
|
|
#[actix_web::main] |
|
|
|
async fn main() -> Result<()> { |
|
|
|
// 检查命令行参数 |
|
|
|
let args: Vec<String> = std::env::args().collect(); |
|
|
|
if args.len() > 1 { |
|
|
|
match args[1].as_str() { |
|
|
|
"--test-client" => return test_websocket_client().await, |
|
|
|
"--simulate" => return simulate_serial_data().await, |
|
|
|
_ => {} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
println!("程序启动..."); |
|
|
|
|
|
|
|
// 启动HTTP服务器 |
|
|
@@ -200,175 +156,23 @@ async fn main() -> Result<()> { |
|
|
|
App::new() |
|
|
|
.wrap(cors) // 添加CORS中间件 |
|
|
|
.route("/mac", web::get().to(get_mac)) |
|
|
|
.route("/scale", web::get().to(get_scale)) // 新增的路由 |
|
|
|
.route("/scale", web::get().to(get_scale)) |
|
|
|
.route("/weight", web::get().to(get_weight)) |
|
|
|
}) |
|
|
|
.bind(("0.0.0.0", 8080))? |
|
|
|
.run(); |
|
|
|
|
|
|
|
println!("HTTP服务器已启动,监听在 http://127.0.0.1:8080"); |
|
|
|
|
|
|
|
// 在新线程中运行WebSocket和串口服务 |
|
|
|
let _websocket_handle = tokio::spawn(async { |
|
|
|
if let Err(e) = run_websocket_and_serial().await { |
|
|
|
eprintln!("WebSocket/串口服务错误: {}", e); |
|
|
|
// 在新线程中运行串口服务 |
|
|
|
let _serial_handle = tokio::spawn(async { |
|
|
|
if let Err(e) = run_serial().await { |
|
|
|
eprintln!("串口服务错误: {}", e); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 等待HTTP服务器结束 |
|
|
|
server.await?; |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
} |
|
|
|
|
|
|
|
// WebSocket和串口处理函数 |
|
|
|
async fn run_websocket_and_serial() -> Result<()> { |
|
|
|
// 读取配置 |
|
|
|
let config = read_config()?; |
|
|
|
|
|
|
|
// 创建一个广播通道,用于向所有WebSocket客户端发送数据 |
|
|
|
let (tx, _rx) = broadcast::channel::<String>(100); |
|
|
|
|
|
|
|
// 启动WebSocket服务器 |
|
|
|
let listener = TcpListener::bind("127.0.0.1:8081").await?; |
|
|
|
println!("WebSocket服务器已启动,监听在 ws://127.0.0.1:8081"); |
|
|
|
|
|
|
|
// 在单独的任务中处理串口数据 |
|
|
|
let tx_serial = tx.clone(); |
|
|
|
let _serial_handle = tokio::spawn(async move { |
|
|
|
// 打开配置的串口 |
|
|
|
println!("正在尝试打开串口 {}...", config.serial_port); |
|
|
|
let mut port = serialport::new(&config.serial_port, config.baud_rate) |
|
|
|
.timeout(Duration::from_millis(1000)) |
|
|
|
.open() |
|
|
|
.with_context(|| format!("无法打开串口 {}", config.serial_port))?; |
|
|
|
|
|
|
|
println!("成功打开串口 {}!", config.serial_port); |
|
|
|
println!("正在读取数据... (按 Ctrl+C 退出)"); |
|
|
|
|
|
|
|
// 增加缓冲区大小并使用String来累积数据 |
|
|
|
let mut serial_buf: Vec<u8> = vec![0; 1024]; |
|
|
|
let mut accumulated_data = String::new(); |
|
|
|
|
|
|
|
loop { |
|
|
|
match port.read(serial_buf.as_mut_slice()) { |
|
|
|
Ok(t) => { |
|
|
|
if t > 0 { |
|
|
|
// 将新数据添加到累积的字符串中 |
|
|
|
if let Ok(data) = String::from_utf8(serial_buf[..t].to_vec()) { |
|
|
|
accumulated_data.push_str(&data); |
|
|
|
|
|
|
|
// 检查是否有完整的数据行(以换行符或回车符结束) |
|
|
|
if accumulated_data.contains('\n') || accumulated_data.contains('\r') { |
|
|
|
// 处理累积的数据 |
|
|
|
let lines: Vec<&str> = accumulated_data |
|
|
|
.split(|c| c == '\n' || c == '\r') |
|
|
|
.filter(|s| !s.is_empty()) |
|
|
|
.collect(); |
|
|
|
|
|
|
|
for line in lines { |
|
|
|
let trimmed_data = line.trim(); |
|
|
|
if !trimmed_data.is_empty() { |
|
|
|
println!("收到串口数据: {}", trimmed_data); |
|
|
|
|
|
|
|
// 创建带时间戳的数据结构 |
|
|
|
let weight_data = WeightData { |
|
|
|
weight: trimmed_data.to_string(), |
|
|
|
timestamp: std::time::SystemTime::now() |
|
|
|
.duration_since(std::time::UNIX_EPOCH) |
|
|
|
.unwrap() |
|
|
|
.as_secs(), |
|
|
|
}; |
|
|
|
|
|
|
|
// 序列化数据 |
|
|
|
if let Ok(json_data) = serde_json::to_string(&weight_data) { |
|
|
|
println!("发送串口数据到WebSocket: {}", json_data); |
|
|
|
// 发送数据到所有WebSocket客户端 |
|
|
|
if let Err(e) = tx_serial.send(json_data) { |
|
|
|
eprintln!("发送数据到广播通道失败: {}", e); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 清除已处理的数据 |
|
|
|
accumulated_data.clear(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
Err(ref e) if e.kind() == io::ErrorKind::TimedOut => { |
|
|
|
// 超时是正常的,继续尝试读取 |
|
|
|
continue; |
|
|
|
} |
|
|
|
Err(e) => { |
|
|
|
eprintln!("串口读取错误: {}", e); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
Ok::<_, anyhow::Error>(()) |
|
|
|
}); |
|
|
|
|
|
|
|
// 处理WebSocket连接 |
|
|
|
while let Ok((stream, addr)) = listener.accept().await { |
|
|
|
println!("新的WebSocket连接: {}", addr); |
|
|
|
let ws_stream = accept_async(stream).await.expect("Failed to accept WebSocket"); |
|
|
|
let tx = tx.clone(); |
|
|
|
|
|
|
|
// 为每个WebSocket连接创建一个新任务 |
|
|
|
tokio::spawn(async move { |
|
|
|
println!("开始处理WebSocket连接: {}", addr); |
|
|
|
let (mut ws_sender, mut ws_receiver) = ws_stream.split(); |
|
|
|
let mut rx = tx.subscribe(); |
|
|
|
|
|
|
|
// 创建两个任务:一个用于接收消息,一个用于发送消息 |
|
|
|
let mut send_task = tokio::spawn(async move { |
|
|
|
println!("[{}] 等待接收广播数据...", addr); |
|
|
|
while let Ok(msg) = rx.recv().await { |
|
|
|
println!("[{}] 准备发送数据: {}", addr, msg); |
|
|
|
if let Err(e) = ws_sender.send(tokio_tungstenite::tungstenite::Message::Text(msg.clone())).await { |
|
|
|
eprintln!("[{}] 发送WebSocket消息失败: {}", addr, e); |
|
|
|
break; |
|
|
|
} |
|
|
|
println!("[{}] 成功发送数据", addr); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
let tx_clone = tx.clone(); |
|
|
|
let mut recv_task = tokio::spawn(async move { |
|
|
|
println!("[{}] 开始监听WebSocket消息...", addr); |
|
|
|
while let Some(result) = ws_receiver.next().await { |
|
|
|
match result { |
|
|
|
Ok(msg) => { |
|
|
|
if let Ok(text) = msg.into_text() { |
|
|
|
println!("[{}] 收到WebSocket消息: {}", addr, text); |
|
|
|
// 将收到的消息广播给所有客户端 |
|
|
|
if let Err(e) = tx_clone.send(text.clone()) { |
|
|
|
eprintln!("[{}] 广播消息失败: {}", addr, e); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
Err(e) => { |
|
|
|
eprintln!("[{}] WebSocket接收错误: {}", addr, e); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// 等待任意一个任务结束 |
|
|
|
tokio::select! { |
|
|
|
_ = (&mut send_task) => { |
|
|
|
println!("[{}] 发送任务结束", addr); |
|
|
|
} |
|
|
|
_ = (&mut recv_task) => { |
|
|
|
println!("[{}] 接收任务结束", addr); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
println!("[{}] WebSocket连接结束", addr); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
Ok(()) |
|
|
|
} |