当前位置: 首页 > news >正文

【避坑实战】C# WinForm 上位机开发:解决串口粘包+LiveCharts卡顿+InfluxDB存储(免费代码+仿真工具)

阅读前提醒,本文完整工程已上传CSDN资源库 (0积分下载!给一键三连呗),包含所有源码、测试代码与详细注释,可直接转到资源库下载测试。

在工业自动化、物联网设备监控等场景中,上位机作为数据采集与可视化的核心工具,承担着连接硬件设备与数据平台的重要角色。本文将通过实战案例,详细讲解如何使用 C# WinForm 开发一套完整的上位机系统,实现从串口数据采集、实时曲线展示,到本地 SQLite 存储与 InfluxDB 时序库持久化的全流程,并提供完整代码与部署指南。

文章目录

    • 一、项目概述与环境搭建
      • 1.1 项目功能清单
      • 1.2 开发环境准备
        • 1.2.1 基础工具
        • 1.2.2 依赖库安装
      • 1.3 项目架构
      • 1.4 核心功能与关键点
    • 二、核心功能实现详解
      • 2.1 串口数据采集模块(SerialReader)
        • 2.1.1 核心代码实现
        • 2.1.2 关键逻辑说明
      • 2.2 实时曲线展示(基于LiveCharts)
        • 2.2.1 图表初始化(MainForm)
        • 2.2.2 动态更新曲线
      • 2.3 本地数据持久化(SQLite)
        • 2.3.1 数据库初始化(LocalStorage)
        • 2.3.2 数据插入实现
      • 2.4 时序数据库集成(InfluxDB)
        • 2.4.1 InfluxDB客户端初始化
        • 2.4.2 数据写入实现
    • 三、界面设计与交互逻辑(MainForm)
      • 3.1 界面布局
      • 3.2 核心交互逻辑
    • 四、代码调试与常见问题解决
      • 4.1 调试准备
      • 4.2 常见问题解决
    • 五、测试效果展示
      • 5.1 运行界面展示
      • 5.2 数据存储验证
    • 六、部署选项
      • 6.1 生成可执行文件
      • 6.2 环境依赖打包
      • 6.3 运行环境要求
    • 七、工程下载与结语

一、项目概述与环境搭建

1.1 项目功能清单

本上位机系统实现以下核心功能:

  • 串口通信:支持动态选择串口、波特率,实时接收传感器数据
  • 数据解析:基于自定义协议解析串口数据(格式:SENSOR,CHx,时间戳,数值
  • 实时可视化:通过折线图动态展示多通道传感器数据
  • 数据持久化:
    • 本地存储:使用 SQLite 保存历史数据
    • 时序存储:集成 InfluxDB 实现高吞吐时序数据存储
  • 状态监控:实时显示连接状态、数据日志与错误信息

1.2 开发环境准备

1.2.1 基础工具
  • 开发IDE:Visual Studio 2019/2022(社区版免费)
  • 框架:.NET Framework 4.8(兼容多数Windows环境)
  • 版本控制:Git(可选,用于代码管理)
1.2.2 依赖库安装

通过 NuGet 包管理器安装以下依赖:

  • LiveCharts.WinForms:用于实时曲线绘制(版本 0.9.7 及以上)
  • Microsoft.Data.Sqlite:SQLite 数据库操作(版本 6.0.0 及以上)
  • SQLitePCL.raw.bundle_e_sqlite3:SQLite 原生依赖(解决跨平台兼容问题)

1.3 项目架构

SerialMonitor/
├─ SerialMonitor.sln
├─ SerialMonitor/ // WinForms 应用
│ ├─ Program.cs
│ ├─ MainForm.cs
│ ├─ MainForm.Designer.cs
│ ├─ SerialReader.cs
│ ├─ DataPoint.cs
│ ├─ InfluxClient.cs
│ └─ LocalStorage.cs // SQLite wrapper
├─ scripts/
│ └─ docker-compose.yml // 启动 InfluxDB + Grafana(可选)
└─ README.md

1.4 核心功能与关键点

**串口读取:**使用 SerialPort.DataReceived 事件或专用后台线程 + BaseStream.ReadAsync 来读取,注意粘包/断包与编码(ASCII/二进制)问题。
**解析协议:**定义简单文本协议(行结束 \n),或实现帧头+长度二进制解析。演示使用文本协议 SENSOR,CH1,123.45\n。
线程安全更新 UI:WinForms 的控件只能在 UI 线程操作,需用 BeginInvoke/Invoke。
**实时绘图:**用 LiveCharts的 WinForms 控件,动态 AddPoint 并滚动显示
**持久化:**本地 SQLite(默认,简单上手)数据库存储

二、核心功能实现详解

2.1 串口数据采集模块(SerialReader)

串口通信是上位机与硬件设备交互的基础,本模块负责串口初始化、数据接收与协议解析。

2.1.1 核心代码实现
// SerialReader.cs 核心片段
public class SerialReader : IDisposable
{private SerialPort _serialPort;private readonly StringBuilder _buffer = new StringBuilder();// 数据接收与错误事件public event Action<DataPoint> OnDataReceived;public event Action<string> OnError;// 初始化串口参数public SerialReader(string portName, int baudRate = 115200,Parity parity = Parity.None, int dataBits = 8,StopBits stopBits = StopBits.One){_serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits){Encoding = Encoding.ASCII,ReadTimeout = 500,WriteTimeout = 500,NewLine = "\n"};_serialPort.DataReceived += SerialPort_DataReceived;_serialPort.ErrorReceived += SerialPort_ErrorReceived;}// 数据接收处理private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e){try{string incomingData = _serialPort.ReadExisting();_buffer.Append(incomingData);ProcessBuffer(); // 解析完整行数据}catch (Exception ex){OnError?.Invoke($"读取数据错误: {ex.Message}");}}// 协议解析(格式:SENSOR,CH1,1622025600,23.45)private void ParseLine(string line){var parts = line.Split(',');if (parts.Length >= 4 && parts[0] == "SENSOR"){if (long.TryParse(parts[2], out long timestamp) &&double.TryParse(parts[3], out double value)){var dataPoint = new DataPoint{Channel = parts[1],Time = DateTimeOffset.FromUnixTimeSeconds(timestamp).DateTime,Value = value};OnDataReceived?.Invoke(dataPoint); // 触发数据接收事件}}}
}
2.1.2 关键逻辑说明
  • 采用事件驱动模式:通过 OnDataReceived 传递解析后的数据,OnError 反馈异常
  • 缓冲区处理:使用 StringBuilder 缓存不完整数据,按换行符分割完整报文
  • 协议解析:严格匹配 SENSOR,CHx,时间戳,数值 格式,确保数据有效性

2.2 实时曲线展示(基于LiveCharts)

实时曲线是数据可视化的核心,通过 LiveCharts 实现多通道数据的动态展示。

2.2.1 图表初始化(MainForm)
// MainForm.cs 图表初始化
private CartesianChart InitializeChart()
{return new CartesianChart{Series = new SeriesCollection(),AxisX = new AxesCollection{new Axis{Title = "时间",LabelFormatter = value => {// 格式化X轴为时间格式(HH:mm:ss)var allTimes = _timeStampsMap.Values.SelectMany(v => v).OrderBy(t => t).ToList();return value < allTimes.Count ? allTimes[(int)value].ToString("HH:mm:ss") : "";}}},AxisY = new AxesCollection{new Axis { Title = "数值", LabelFormatter = value => value.ToString("F2") }},LegendLocation = LegendLocation.Top};
}
2.2.2 动态更新曲线
// 接收数据后更新图表
private void UpdateChart(DataPoint dataPoint)
{// 为新通道创建曲线系列if (!_seriesMap.ContainsKey(dataPoint.Channel)){var color = Color.FromArgb(_random.Next(50, 255), _random.Next(50, 255), _random.Next(50, 255));var series = new LineSeries{Title = dataPoint.Channel,Values = new ChartValues<double>(),Stroke = new SolidColorBrush(Color.FromArgb(color.A, color.R, color.G, color.B)),Fill = Brushes.Transparent};_seriesMap[dataPoint.Channel] = series;_chart.Series.Add(series);}// 添加数据并限制最大点数(防止内存溢出)var lineSeries = _seriesMap[dataPoint.Channel];lineSeries.Values.Add(dataPoint.Value);if (lineSeries.Values.Count > MaxDataPoints){lineSeries.Values.RemoveAt(0); // 移除最旧数据}_chart.Update(); // 刷新图表
}

2.3 本地数据持久化(SQLite)

使用 SQLite 实现本地数据存储,适合离线场景下的历史数据查询。

2.3.1 数据库初始化(LocalStorage)
// LocalStorage.cs 数据库初始化
private void InitializeDatabase()
{using (var connection = new SqliteConnection(_connectionString)){connection.Open();// 创建传感器数据表string createTableSql = @"CREATE TABLE IF NOT EXISTS sensor_data (id INTEGER PRIMARY KEY AUTOINCREMENT,channel TEXT NOT NULL,time TEXT NOT NULL,value REAL NOT NULL,created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP);";using (var command = new SqliteCommand(createTableSql, connection)){command.ExecuteNonQuery();}}
}
2.3.2 数据插入实现
// 插入数据点到SQLite
public void Insert(DataPoint dataPoint)
{using (var connection = new SqliteConnection(_connectionString)){connection.Open();string insertSql = @"INSERT INTO sensor_data (channel, time, value)VALUES (@channel, @time, @value);";using (var command = new SqliteCommand(insertSql, connection)){command.Parameters.AddWithValue("@channel", dataPoint.Channel);command.Parameters.AddWithValue("@time", dataPoint.Time.ToString("o")); // ISO 8601格式command.Parameters.AddWithValue("@value", dataPoint.Value);command.ExecuteNonQuery();}}
}

2.4 时序数据库集成(InfluxDB)

InfluxDB 专为时序数据设计,适合高频率传感器数据的长期存储与分析。

2.4.1 InfluxDB客户端初始化
// InfluxClient.cs 初始化配置
public void Initialize(string url, string token, string org, string bucket)
{_writeUrl = $"{url.TrimEnd('/')}/api/v2/write?org={Uri.EscapeDataString(org)}&bucket={Uri.EscapeDataString(bucket)}&precision=s";_httpClient.DefaultRequestHeaders.Add("Authorization", $"Token {token}");_isInitialized = true;
}
2.4.2 数据写入实现
// 异步写入数据到InfluxDB
public async Task WritePointAsync(DataPoint dataPoint)
{// 构建InfluxDB行协议(measurement,tag=value field=value timestamp)string lineProtocol = $"sensor,channel={EscapeTagValue(dataPoint.Channel)} value={dataPoint.Value} {new DateTimeOffset(dataPoint.Time).ToUnixTimeSeconds()}";var content = new StringContent(lineProtocol, Encoding.UTF8);var response = await _httpClient.PostAsync(_writeUrl, content);if (!response.IsSuccessStatusCode){string errorContent = await response.Content.ReadAsStringAsync();throw new Exception($"写入失败: {response.StatusCode} - {errorContent}");}
}

三、界面设计与交互逻辑(MainForm)

3.1 界面布局

主界面采用上下分割布局:

  • 上半部分:串口配置区(串口选择、波特率、连接按钮)+ 日志显示区
  • 下半部分:实时曲线展示区

核心布局代码:

// MainForm.cs 界面布局
private void SetupUI()
{var splitContainer = new SplitContainer{Dock = DockStyle.Fill,Orientation = Orientation.Horizontal,SplitterDistance = 200 // 上半部分高度};// 上半部分:串口配置与日志var topPanel = splitContainer.Panel1;topPanel.Controls.Add(new FlowLayoutPanel // 串口配置控件{Controls = { portComboBox, baudRateComboBox, startButton, statusLabel }});topPanel.Controls.Add(logTextBox); // 日志文本框// 下半部分:图表splitContainer.Panel2.Controls.Add(_chart);_chart.Dock = DockStyle.Fill;
}

3.2 核心交互逻辑

  • 串口连接/断开:通过 StartButton_Click 事件切换状态
  • 数据流转:串口接收数据 → 解析为 DataPoint → 触发图表更新 + 本地存储 + 时序库存储
// 数据接收后处理流程
private void OnSerialDataReceived(DataPoint dataPoint)
{UpdateLog($"收到数据: {dataPoint.Channel} - {dataPoint.Value}"); // 更新日志UpdateChart(dataPoint); // 更新图表LocalStorage.Instance.Insert(dataPoint); // 本地存储if (InfluxClient.Instance.IsInitialized){Task.Run(() => InfluxClient.Instance.WritePointAsync(dataPoint)); // 异步写入InfluxDB}
}

四、代码调试与常见问题解决

4.1 调试准备

  1. 确保串口设备连接正常(可通过设备管理器确认端口号)
  2. 配置 InfluxDB 连接参数(在 MainForm 初始化时添加配置界面或硬编码测试):
// 示例:在MainForm加载时初始化InfluxClient
private void MainForm_Load(object sender, EventArgs e)
{InfluxClient.Instance.Initialize(url: "http://localhost:8086",token: "你的InfluxDB令牌",org: "sensor_org",bucket: "sensor_data");
}

4.2 常见问题解决

  1. SQLite初始化失败

    • 检查 Program.cs 中是否调用 Batteries.Init();(SQLitePCL 必须初始化)
    • 确保 SQLitePCL.raw.bundle_e_sqlite3 包已正确安装
  2. 串口无法打开

    • 确认端口号正确且未被其他程序占用
    • 检查波特率、校验位等参数与设备匹配
  3. InfluxDB写入失败

    • 验证 urltokenorgbucket 正确性
    • 通过 InfluxDB Web 界面的 “Data” 菜单测试连接

五、测试效果展示

这里我使用了电脑上两个串口直接连接的方式(两个USB转TTL插入电脑,TTL线直连注意TXRX要交叉连接),使用COM3通过软件输出仿真数据,使用CM4接收并显示到界面,存储数据

5.1 运行界面展示

上位机运行主界面,包含串口连接状态、实时曲线与数据日志
主界面和模拟数据输入截图

5.2 数据存储验证

  • 本地SQLite:使用 SQLite Expert Personal 打开 sensor_data.db,查看 sensor_data 表数据
    在这里插入图片描述

六、部署选项

6.1 生成可执行文件

  1. 在 Visual Studio 中右键项目 → “属性” → “生成” → 目标框架选择 .NET Framework 4.7.2
  2. 点击 “生成解决方案”,输出文件位于 bin\Release 目录

6.2 环境依赖打包

  • 必要文件:
    • 生成的 SerialMonitorNetFramework.exe
    • SQLite 依赖:e_sqlite3.dll(自动复制到输出目录)
    • 配置文件:建议创建 app.config 存储 InfluxDB 连接参数

6.3 运行环境要求

  • 操作系统:Windows 7 及以上
  • 安装 .NET Framework 4.8 运行时(下载地址)
  • InfluxDB 服务需提前启动(远程部署时需确保网络可达)

七、工程下载与结语

完整工程已上传CSDN资源库(0积分下载!给一键三连呗) ,包含所有源码、测试代码与详细注释,可直接转到资源库下载测试。

本项目通过模块化设计实现了串口数据采集、实时可视化与多端存储的完整流程,可根据实际需求扩展功能:

  • 增加数据导出(Excel/CSV)
  • 实现报警阈值设置与提醒
  • 支持多设备并发采集

希望本文能为上位机开发初学者提供清晰的实战指引,如有问题欢迎在下方评论区交流。

关注我,每周更新代码实战项目,共同学习进步!

http://www.dtcms.com/a/422913.html

相关文章:

  • 开源 C# 快速开发(十二)进程监控
  • 江协科技 CAN总线入门课程(仲裁)
  • Ubuntu 添加右键“复制全路径”菜单
  • 国企网站建设的意义电影影视网站模板免费下载
  • 网站主页设计模板房地产门户网站
  • 前端核心框架vue之(vuex状态篇4/5)
  • SheetGod:让Excel公式变得简单
  • 地信是“安卓”专业还是“苹果”专业?
  • 视频拼接类产品介绍
  • VSCode上配置Spring Boot环境
  • 线程同步实战指南:从 bug 根源到锁优化的终极之路
  • 中文企业展示网站模板优化wordpress后台速度
  • 做网站不赚钱了wordpress代码编辑
  • 云手机在硬件资源方面的优势
  • 技术深度解析:指纹云手机如何通过设备指纹隔离技术重塑多账号安全管理
  • 中国移动获得手机直连卫星通讯牌照:行业变革的催化剂
  • Chapter9—享元模式
  • 常州网站建设公司案例怎样做企业学校网站
  • 建设网站对企业的重要性企业网站网页设计有哪些
  • SpringBoot结合Vue 播放 m3u8 格式视频
  • 网站推广目标关键词龙岩网站设计找哪家好
  • 【论文阅读】ASPS: Augmented Segment Anything Model for Polyp Segmentation
  • 精读C++20设计模式——结构型设计模式:享元模式
  • FT8430-LRT非隔离5V100MA电源芯片,满足小家电、智能照明、MCU供电需求,替代阻容降压(典型案例,电路图)
  • [论文阅读]Benchmarking Poisoning Attacks against Retrieval-Augmented Generation
  • 精读C++20设计模式:结构型设计模式:装饰器模式
  • (数据结构)链表OJ——刷题练习
  • 怎么做网站源码温州建网站
  • 云服务器做淘客网站苏州网站制作及推广
  • hive启动报错