using System.Collections.Concurrent; using System.IO.Ports; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; namespace sscom_sender { public partial class Form1 : Form { // 串口对象 private SerialPort? serialPort1; private SerialPort? serialPort2; // 数据缓冲区 private StringBuilder buffer1 = new StringBuilder(); private StringBuilder buffer2 = new StringBuilder(); // 数据队列 private ConcurrentQueue dataQueue = new ConcurrentQueue(); // 线程控制 private CancellationTokenSource? uploadCancellationTokenSource; private CancellationTokenSource? logCancellationTokenSource; private Task? uploadTask; private Task? logTask; // HTTP客户端 private readonly HttpClient httpClient = new HttpClient(); // 日志文件路径 private readonly string logFilePath = Path.Combine(Application.StartupPath, "data_log.txt"); // 线程安全的日志队列 private ConcurrentQueue logQueue = new ConcurrentQueue(); public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { // 初始化串口列表 RefreshPortList(); // 设置默认波特率 cmbBaud1.SelectedIndex = 4; // 115200 cmbBaud2.SelectedIndex = 4; // 115200 // 启动日志线程 StartLogThread(); UpdateStatus("系统已启动"); } private void RefreshPortList() { string[] ports = SerialPort.GetPortNames(); cmbPort1.Items.Clear(); cmbPort2.Items.Clear(); foreach (string port in ports) { cmbPort1.Items.Add(port); cmbPort2.Items.Add(port); } if (ports.Length > 0) { cmbPort1.SelectedIndex = 0; if (ports.Length > 1) cmbPort2.SelectedIndex = 1; else cmbPort2.SelectedIndex = 0; } } private void btnConnect1_Click(object sender, EventArgs e) { if (serialPort1?.IsOpen == true) { // 断开连接 serialPort1.Close(); btnConnect1.Text = "连接"; UpdateStatus("串口1已断开"); } else { // 建立连接 try { serialPort1 = new SerialPort( cmbPort1.Text, int.Parse(cmbBaud1.Text), Parity.None, 8, StopBits.One); serialPort1.DataReceived += SerialPort1_DataReceived; serialPort1.Open(); btnConnect1.Text = "断开"; UpdateStatus($"串口1已连接: {cmbPort1.Text}"); } catch (Exception ex) { MessageBox.Show($"串口1连接失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } private void btnConnect2_Click(object sender, EventArgs e) { if (serialPort2?.IsOpen == true) { // 断开连接 serialPort2.Close(); btnConnect2.Text = "连接"; UpdateStatus("串口2已断开"); } else { // 建立连接 try { serialPort2 = new SerialPort( cmbPort2.Text, int.Parse(cmbBaud2.Text), Parity.None, 8, StopBits.One); serialPort2.DataReceived += SerialPort2_DataReceived; serialPort2.Open(); btnConnect2.Text = "断开"; UpdateStatus($"串口2已连接: {cmbPort2.Text}"); } catch (Exception ex) { MessageBox.Show($"串口2连接失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } private void SerialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e) { if (serialPort1?.IsOpen == true) { string data = serialPort1.ReadExisting(); ProcessSerialData(data, 1, buffer1); } } private void SerialPort2_DataReceived(object sender, SerialDataReceivedEventArgs e) { if (serialPort2?.IsOpen == true) { string data = serialPort2.ReadExisting(); ProcessSerialData(data, 2, buffer2); } } private void ProcessSerialData(string data, int portNumber, StringBuilder buffer) { // 将数据添加到缓冲区 buffer.Append(data); // 查找完整的数据包(以换行符结束) string bufferContent = buffer.ToString(); string[] lines = bufferContent.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); if (lines.Length > 0) { // 处理完整的行 for (int i = 0; i < lines.Length - 1; i++) { ProcessCompleteDataLine(lines[i], portNumber); } // 检查最后一行是否完整 if (bufferContent.EndsWith("\r") || bufferContent.EndsWith("\n")) { ProcessCompleteDataLine(lines[lines.Length - 1], portNumber); buffer.Clear(); } else { // 保留不完整的最后一行 buffer.Clear(); buffer.Append(lines[lines.Length - 1]); } } } private void ProcessCompleteDataLine(string line, int portNumber) { if (string.IsNullOrWhiteSpace(line)) return; var dataPacket = new DataPacket { PortNumber = portNumber, RawData = line, Timestamp = DateTime.Now }; // 如果是串口1且数据格式匹配BESTPOS,提取经纬度 if (portNumber == 1 && line.Contains("BESTPOSA")) { var coordinates = ExtractCoordinates(line); if (coordinates != null) { dataPacket.Latitude = coordinates.Value.Latitude; dataPacket.Longitude = coordinates.Value.Longitude; } } // 添加到队列 dataQueue.Enqueue(dataPacket); // 添加到日志显示 string logMessage = $"[{dataPacket.Timestamp:yyyy-MM-dd HH:mm:ss}] 串口{portNumber}: {line}"; if (dataPacket.Latitude.HasValue && dataPacket.Longitude.HasValue) { logMessage += $" [经度: {dataPacket.Longitude:F8}, 纬度: {dataPacket.Latitude:F8}]"; } AddLogMessage(logMessage); } private (double Latitude, double Longitude)? ExtractCoordinates(string data) { try { // BESTPOS数据格式解析 // #BESTPOSA,COM1,14394,98.0,UNKNOWN,1,1699.000,1700908,2,18;INSUFFICIENT_OBS,NONE,纬度,经度,高度,undulation,datum,lat_std,lon_std,hgt_std,stn_id,diff_age,sol_age,#obs,#L1,#L2,reserved,ext_sol_stat,galileo_beidou_sig_mask,gps_glonass_sig_mask*checksum string[] parts = data.Split(','); if (parts.Length >= 14) { if (double.TryParse(parts[11], out double latitude) && double.TryParse(parts[12], out double longitude)) { return (latitude, longitude); } } } catch (Exception ex) { AddLogMessage($"坐标解析错误: {ex.Message}"); } return null; } private void btnStartUpload_Click(object sender, EventArgs e) { if (string.IsNullOrWhiteSpace(txtUrl.Text)) { MessageBox.Show("请输入上传地址", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } uploadCancellationTokenSource = new CancellationTokenSource(); uploadTask = Task.Run(() => UploadWorker(uploadCancellationTokenSource.Token)); btnStartUpload.Enabled = false; btnStopUpload.Enabled = true; UpdateStatus("数据上传已启动"); } private void btnStopUpload_Click(object sender, EventArgs e) { uploadCancellationTokenSource?.Cancel(); btnStartUpload.Enabled = true; btnStopUpload.Enabled = false; UpdateStatus("数据上传已停止"); } private async Task UploadWorker(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { if (dataQueue.TryDequeue(out DataPacket? packet)) { await UploadData(packet); } else { await Task.Delay(100, cancellationToken); // 等待100ms } } catch (OperationCanceledException) { break; } catch (Exception ex) { Invoke(() => AddLogMessage($"上传错误: {ex.Message}")); await Task.Delay(5000, cancellationToken); // 错误后等待5秒 } } } private async Task UploadData(DataPacket packet) { try { var jsonData = JsonSerializer.Serialize(packet); var content = new StringContent(jsonData, Encoding.UTF8, "application/json"); var response = await httpClient.PostAsync(txtUrl.Text, content); if (response.IsSuccessStatusCode) { Invoke(() => AddLogMessage($"数据上传成功: 串口{packet.PortNumber}")); } else { Invoke(() => AddLogMessage($"数据上传失败: {response.StatusCode} - 串口{packet.PortNumber}")); } } catch (Exception ex) { Invoke(() => AddLogMessage($"上传异常: {ex.Message}")); } } private void StartLogThread() { logCancellationTokenSource = new CancellationTokenSource(); logTask = Task.Run(() => LogWorker(logCancellationTokenSource.Token)); } private async Task LogWorker(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { var logsToWrite = new List(); // 批量获取日志 while (logQueue.TryDequeue(out string? logMessage)) { logsToWrite.Add(logMessage); if (logsToWrite.Count >= 10) break; // 批量写入,最多10条 } if (logsToWrite.Count > 0) { await File.AppendAllLinesAsync(logFilePath, logsToWrite, cancellationToken); } else { await Task.Delay(1000, cancellationToken); // 等待1秒 } } catch (OperationCanceledException) { break; } catch (Exception ex) { // 日志写入错误,避免无限循环 Console.WriteLine($"日志写入错误: {ex.Message}"); await Task.Delay(5000, cancellationToken); } } } private void AddLogMessage(string message) { // 添加到界面显示 if (InvokeRequired) { Invoke(() => AddLogMessage(message)); return; } txtLog.AppendText(message + Environment.NewLine); txtLog.SelectionStart = txtLog.Text.Length; txtLog.ScrollToCaret(); // 添加到日志文件队列 logQueue.Enqueue(message); // 限制界面显示的行数 if (txtLog.Lines.Length > 1000) { var lines = txtLog.Lines.Skip(500).ToArray(); txtLog.Text = string.Join(Environment.NewLine, lines); } } private void UpdateStatus(string message) { if (InvokeRequired) { Invoke(() => UpdateStatus(message)); return; } toolStripStatusLabel1.Text = message; AddLogMessage($"[系统] {message}"); } private void btnClearLog_Click(object sender, EventArgs e) { txtLog.Clear(); } private void btnSaveLog_Click(object sender, EventArgs e) { try { using (var saveDialog = new SaveFileDialog()) { saveDialog.Filter = "文本文件|*.txt|所有文件|*.*"; saveDialog.FileName = $"log_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; if (saveDialog.ShowDialog() == DialogResult.OK) { File.WriteAllText(saveDialog.FileName, txtLog.Text); MessageBox.Show("日志保存成功", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } } } catch (Exception ex) { MessageBox.Show($"保存失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { // 停止所有线程 uploadCancellationTokenSource?.Cancel(); logCancellationTokenSource?.Cancel(); // 关闭串口 serialPort1?.Close(); serialPort2?.Close(); // 释放资源 httpClient.Dispose(); } } // 数据包类 public class DataPacket { public int PortNumber { get; set; } public string RawData { get; set; } = string.Empty; public DateTime Timestamp { get; set; } public double? Latitude { get; set; } public double? Longitude { get; set; } } }