ssinfo-sender/Form1.cs
copper d21391fb0c 谱仪数据处理逻辑
上传逻辑
2025-06-23 16:33:31 +08:00

593 lines
23 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using uPLibrary.Networking.M2Mqtt;
using uPLibrary.Networking.M2Mqtt.Messages;
using Newtonsoft.Json;
using aiot_paho_csharp;
namespace sscom_sender
{
public partial class Form1 : Form
{
private SerialPort gpsPort;
private SerialPort spectrometerPort;
private MqttClient mqttClient;
private bool isRunning = false;
private string logFileName;
// 数据队列
private ConcurrentQueue<GPSData> gpsDataQueue = new ConcurrentQueue<GPSData>();
private ConcurrentQueue<SpectrometerData> spectrometerDataQueue = new ConcurrentQueue<SpectrometerData>();
private ConcurrentQueue<string> logQueue = new ConcurrentQueue<string>();
// 线程控制
private CancellationTokenSource cancellationTokenSource;
private Task gpsTask;
private Task spectrometerTask;
private Task mqttTask;
private Task logTask;
// 数据缓冲区
private StringBuilder gpsBuffer = new StringBuilder();
private StringBuilder spectrometerBuffer = new StringBuilder();
public Form1()
{
InitializeComponent();
InitializeForm();
}
private void InitializeForm()
{
// 初始化串口列表
RefreshSerialPorts();
// 创建日志文件名
logFileName = Path.Combine(Application.StartupPath, "logs", $"log_{DateTime.Now:yyyyMMdd_HHmmss}.txt");
Directory.CreateDirectory(Path.GetDirectoryName(logFileName));
}
private void RefreshSerialPorts()
{
string[] ports = SerialPort.GetPortNames();
comboBoxGPSPort.Items.Clear();
comboBoxSpectrometerPort.Items.Clear();
foreach (string port in ports)
{
comboBoxGPSPort.Items.Add(port);
comboBoxSpectrometerPort.Items.Add(port);
}
if (ports.Length > 0)
{
comboBoxGPSPort.SelectedIndex = 0;
if (ports.Length > 1)
comboBoxSpectrometerPort.SelectedIndex = 1;
else
comboBoxSpectrometerPort.SelectedIndex = 0;
}
}
private void buttonStartStop_Click(object sender, EventArgs e)
{
if (!isRunning)
{
StartDataCollection();
}
else
{
StopDataCollection();
}
}
private void StartDataCollection()
{
try
{
// 初始化串口
InitializeSerialPorts();
// 启动线程
cancellationTokenSource = new CancellationTokenSource();
var token = cancellationTokenSource.Token;
gpsTask = Task.Run(() => GPSDataReader(token), token);
spectrometerTask = Task.Run(() => SpectrometerDataReader(token), token);
mqttTask = Task.Run(() => MQTTDataSender(token), token);
logTask = Task.Run(() => LogWriter(token), token);
isRunning = true;
buttonStartStop.Text = "停止";
buttonStartStop.BackColor = Color.Red;
LogMessage("数据采集已启动");
}
catch (Exception ex)
{
MessageBox.Show($"启动失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void StopDataCollection()
{
try
{
isRunning = false;
cancellationTokenSource?.Cancel();
// 立即更新UI状态
buttonStartStop.Text = "开始";
buttonStartStop.BackColor = SystemColors.Control;
LogMessage("正在停止数据采集...");
// 异步等待任务完成避免阻塞UI线程
Task.Run(() =>
{
try
{
// 给任务一些时间优雅退出
var tasks = new[] { gpsTask, spectrometerTask, mqttTask, logTask }.Where(t => t != null).ToArray();
if (tasks.Length > 0)
{
Task.WaitAll(tasks, 2000); // 减少等待时间到2秒
}
// 关闭串口
try { gpsPort?.Close(); } catch { }
try { spectrometerPort?.Close(); } catch { }
// 断开MQTT
try { mqttClient?.Disconnect(); } catch { }
// 在UI线程上更新状态
this.Invoke(new Action(() =>
{
LogMessage("数据采集已停止");
}));
}
catch (Exception ex)
{
this.Invoke(new Action(() =>
{
LogMessage($"停止时发生错误: {ex.Message}");
}));
}
});
}
catch (Exception ex)
{
LogMessage($"停止时发生错误: {ex.Message}");
}
}
private void InitializeSerialPorts()
{
// GPS串口
if (comboBoxGPSPort.SelectedItem != null)
{
gpsPort = new SerialPort(comboBoxGPSPort.SelectedItem.ToString(), 115200, Parity.None, 8, StopBits.One);
gpsPort.DataReceived += GPSPort_DataReceived;
gpsPort.Open();
// 发送GPS初始化命令
gpsPort.WriteLine("bestposa com1 1");
}
// 谱仪串口
if (comboBoxSpectrometerPort.SelectedItem != null)
{
spectrometerPort = new SerialPort(comboBoxSpectrometerPort.SelectedItem.ToString(), 115200, Parity.None, 8, StopBits.One);
spectrometerPort.DataReceived += SpectrometerPort_DataReceived;
spectrometerPort.Open();
if (!spectrometerPort.IsOpen) {
LogMessage("谱仪端口打开失败");
return;
}
LogMessage("谱仪端口已打开");
// 发送谱仪开始命令
spectrometerPort.DiscardInBuffer();
spectrometerPort.Write("$start\r\n");
spectrometerPort.ReadTimeout = 1500;
}
}
private void InitializeMQTT()
{
try
{
string productKey = textBoxProductKey.Text;
string topicRoot = textBoxTopicRoot.Text;
string deviceName = textBoxDeviceName.Text;
string deviceSecret = textBoxDeviceSecret.Text;
MqttSign sign = new MqttSign();
sign.calculate(productKey, deviceName, deviceSecret);
string broker = productKey + ".iot-as-mqtt.cn-shanghai.aliyuncs.com";
mqttClient = new MqttClient(broker, 1883, true, null, null, MqttSslProtocols.TLSv1_2);
mqttClient.Connect(sign.getClientid(), sign.getUsername(), sign.getPassword());
LogMessage($"MQTT已连接到: {broker}");
}
catch (Exception ex)
{
LogMessage($"MQTT连接失败: {ex.Message},将继续进行数据采集和日志记录");
mqttClient = null; // 确保mqttClient为null避免后续使用
}
}
private void GPSPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
string data = gpsPort.ReadExisting();
gpsBuffer.Append(data);
// 处理完整的行
string bufferContent = gpsBuffer.ToString();
string[] lines = bufferContent.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
if (bufferContent.EndsWith("\r") || bufferContent.EndsWith("\n"))
{
gpsBuffer.Clear();
foreach (string line in lines)
{
ProcessGPSData(line);
}
}
else
{
gpsBuffer.Clear();
for (int i = 0; i < lines.Length - 1; i++)
{
ProcessGPSData(lines[i]);
}
gpsBuffer.Append(lines[lines.Length - 1]);
}
}
catch (Exception ex)
{
LogMessage($"GPS数据接收错误: {ex.Message}");
}
}
private void SpectrometerPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
string data = spectrometerPort.ReadExisting();
// LogMessage($"谱仪原始数据: {data}");
spectrometerBuffer.Append(data);
// 处理完整的行
string bufferContent = spectrometerBuffer.ToString();
string[] lines = bufferContent.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
if (bufferContent.EndsWith("\r") || bufferContent.EndsWith("\n"))
{
spectrometerBuffer.Clear();
foreach (string line in lines)
{
ProcessSpectrometerData(line);
}
}
else
{
spectrometerBuffer.Clear();
for (int i = 0; i < lines.Length - 1; i++)
{
ProcessSpectrometerData(lines[i]);
}
spectrometerBuffer.Append(lines[lines.Length - 1]);
}
}
catch (Exception ex)
{
LogMessage($"谱仪数据接收错误: {ex.Message}");
}
}
private void ProcessGPSData(string data)
{
LogMessage($"GPS原始数据: {data}");
if (data.StartsWith("#BESTPOSA"))
{
try
{
string[] parts = data.Split(',');
// 查找WGS84标识的位置
// LogMessage($"GPS数据部分数量: {parts.Length}");
int wgs84Index = -1;
for (int i = 0; i < parts.Length; i++)
{
// LogMessage($"GPS数据部分: {parts[i]}");
if (parts[i].Contains("WGS84"))
{
wgs84Index = i;
break;
}
}
// LogMessage($"WGS84索引: {wgs84Index}");
// WGS84后面的两个字段是经纬度
if (wgs84Index >= 0 && parts.Length > wgs84Index + 2)
{
double lat = double.Parse(parts[wgs84Index + 1]);
double lng = double.Parse(parts[wgs84Index + 2]);
GPSData gpsData = new GPSData
{
Latitude = lat,
Longitude = lng,
Timestamp = DateTime.Now
};
gpsDataQueue.Enqueue(gpsData);
LogMessage($"GPS数据: 纬度={lat:F8}, 经度={lng:F8}");
}
}
catch (Exception ex)
{
LogMessage($"GPS数据解析错误: {ex.Message}");
}
}
}
private void ProcessSpectrometerData(string data)
{
LogMessage($"谱仪原始数据: {data}");
if (data.Equals("ok")) {
LogMessage("谱仪已准备好");
return;
}
try
{
string[] parts = data.Split(' ');
// 检查是否为完整的反馈格式16个字段日期 时间 + 14个数值
if (parts.Length == 16)
{
// 验证日期格式 xxxx.xx.xx
if (!System.Text.RegularExpressions.Regex.IsMatch(parts[0], @"^\d{4}\.\d{2}\.\d{2}$"))
return;
// 验证时间格式 xx:xx:xx
if (!System.Text.RegularExpressions.Regex.IsMatch(parts[1], @"^\d{2}:\d{2}:\d{2}$"))
return;
// 解析时间戳 (xxxx.xx.xx xx:xx:xx)
string dateStr = parts[0];
string timeStr = parts[1];
SpectrometerData spectData = new SpectrometerData
{
Timestamp = DateTime.Now, // 使用当前时间,也可以解析返回的时间
RealTime = double.Parse(parts[2]), // aaaaaaa.aa 实时间
DeadTime = double.Parse(parts[3]), // bbbbbbb.bb 死时间
DoseRate = double.Parse(parts[14]), // JJJJJJ.JJ 环境总辐射剂量率值第15个字段
TotalCountRate = double.Parse(parts[15]) // cccccccccc.cc γ谱的全谱计数率第16个字段
};
spectrometerDataQueue.Enqueue(spectData);
LogMessage($"谱仪数据: 时间={dateStr} {timeStr}, 剂量率={spectData.DoseRate:F2} nSv/h, 全谱计数率={spectData.TotalCountRate:F2} cps");
}
}
catch (Exception ex)
{
LogMessage($"谱仪数据解析错误: {ex.Message}, 数据: {data}");
}
}
private async void GPSDataReader(CancellationToken token)
{
while (!token.IsCancellationRequested && isRunning)
{
await Task.Delay(1000, token);
}
}
private async void SpectrometerDataReader(CancellationToken token)
{
while (!token.IsCancellationRequested && isRunning)
{
try
{
LogMessage("尝试读取谱仪数据");
if (spectrometerPort != null && spectrometerPort.IsOpen)
{
// spectrometerPort.DiscardInBuffer();
// spectrometerPort.Write("$start\r\n");
// await Task.Delay(50, token); // 等待刷新完成
// // spectrometerPort.ReadTimeout = 1500;
// // string response = spectrometerPort.ReadLine();
// // LogMessage($"谱仪原始数据: {response}");
// spectrometerPort.DiscardInBuffer();
// spectrometerPort.Write("$start\r");
// await Task.Delay(50, token); // 等待刷新完成
// spectrometerPort.ReadTimeout = 1500;
// response = spectrometerPort.ReadLine();
// LogMessage($"谱仪原始数据: {response}");
// response = spectrometerPort.ReadLine();
// LogMessage($"谱仪原始数据: {response}");
// spectrometerPort.DiscardInBuffer();
// spectrometerPort.Write("$start\r\n\r\n");
// spectrometerPort.ReadTimeout = 1500;
// response = spectrometerPort.ReadLine();
// LogMessage($"谱仪原始数据: {response}");
// await Task.Delay(50, token); // 等待刷新完成
LogMessage("谱仪端口已打开");
// 先发送刷新指令
spectrometerPort.Write("$refresh\r\n");
await Task.Delay(500, token); // 等待刷新完成
// 再发送获取结果指令
spectrometerPort.Write("$getSperesult\r\n");
}
int interval = (int)(numericUpDownRefreshInterval.Value * 1000);
await Task.Delay(interval, token);
}
catch (Exception ex)
{
LogMessage($"谱仪数据读取错误: {ex.Message}");
await Task.Delay(5000, token);
}
}
}
private async void MQTTDataSender(CancellationToken token)
{
// 在数据发送线程中初始化MQTT连接
InitializeMQTT();
while (!token.IsCancellationRequested && isRunning)
{
try
{
if (spectrometerDataQueue.TryDequeue(out SpectrometerData spectData))
{
// 查找最近的GPS数据
GPSData nearestGPS = FindNearestGPSData(spectData.Timestamp);
if (nearestGPS != null)
{
var mqttData = new
{
OptTime = spectData.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff"),
PointLat = nearestGPS.Latitude.ToString("F8"),
PointLng = nearestGPS.Longitude.ToString("F8"),
Value = spectData.DoseRate / 1000000000.0 // 转换为合适的单位
};
string json = JsonConvert.SerializeObject(mqttData);
string topic = $"/{textBoxTopicRoot.Text}/{textBoxDeviceName.Text}/user/update";
if (mqttClient != null && mqttClient.IsConnected)
{
mqttClient.Publish(topic, Encoding.UTF8.GetBytes(json));
LogMessage($"MQTT发送: {json}");
}
else
{
LogMessage($"MQTT未连接数据记录: {json}");
}
}
}
await Task.Delay(100, token);
}
catch (Exception ex)
{
LogMessage($"MQTT发送错误: {ex.Message}");
await Task.Delay(5000, token);
}
}
}
private GPSData FindNearestGPSData(DateTime targetTime)
{
GPSData nearest = null;
TimeSpan minDiff = TimeSpan.MaxValue;
var gpsDataList = gpsDataQueue.ToArray();
foreach (var gpsData in gpsDataList)
{
TimeSpan diff = Math.Abs((gpsData.Timestamp - targetTime).Ticks) == (gpsData.Timestamp - targetTime).Ticks ?
gpsData.Timestamp - targetTime : targetTime - gpsData.Timestamp;
if (diff < minDiff)
{
minDiff = diff;
nearest = gpsData;
}
}
return nearest;
}
private async void LogWriter(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
try
{
if (logQueue.TryDequeue(out string logMessage))
{
await File.AppendAllTextAsync(logFileName, logMessage + Environment.NewLine, token);
}
await Task.Delay(100, token);
}
catch (Exception ex)
{
// 避免日志写入错误导致的无限循环
Console.WriteLine($"日志写入错误: {ex.Message}");
await Task.Delay(1000, token);
}
}
}
private void LogMessage(string message)
{
string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {message}";
// 添加到日志队列
logQueue.Enqueue(logEntry);
// 更新UI
if (textBoxLog.InvokeRequired)
{
textBoxLog.Invoke(new Action(() => {
textBoxLog.AppendText(logEntry + Environment.NewLine);
textBoxLog.ScrollToCaret();
}));
}
else
{
textBoxLog.AppendText(logEntry + Environment.NewLine);
textBoxLog.ScrollToCaret();
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
if (isRunning)
{
StopDataCollection();
}
base.OnFormClosing(e);
}
}
public class GPSData
{
public double Latitude { get; set; }
public double Longitude { get; set; }
public DateTime Timestamp { get; set; }
}
public class SpectrometerData
{
public DateTime Timestamp { get; set; }
public double RealTime { get; set; }
public double DeadTime { get; set; }
public double DoseRate { get; set; }
public double TotalCountRate { get; set; }
}
}