在自动化设备项目中,经常会遇到这样一类需求:
PLC负责设备控制,上位机负责读取生产信息,并将每一个产品的生产数据长期保存。
例如记录:
产品QR
生产时间
当前产品CT
刀具使用次数
产品结果
设备状态
同时还要求:
数据保存5年以上;
能够按日期查询;
每月自动归档;
每天自动生成Excel报表;
电脑异常关机以后不能轻易丢数据。
如果数据量很小,直接写Excel当然可以。
但如果设备24小时生产,并且持续运行几年,直接把Excel当数据库使用并不是一个好的方案。
本文整理一套我目前比较推荐的:
C#
+
WinForms
+
Siemens S7
+
SQLite
+
EPPlus
生产数据采集软件架构。
1. 为什么不建议PLC数据直接写Excel
很多项目一开始会写成:
PLC
↓
C#
↓
Excel
产品生产一次就:
WriteExcel();
短时间没有问题。
但运行时间长以后会遇到:
文件越来越大;
Excel占用;
写文件异常;
断电风险;
查询速度下降;
多人读取;
文件损坏;
历史数据管理困难。
所以比较合理的架构应该是:
PLC
↓
Data Acquisition
↓
Production Record
↓
SQLite
↓
Excel Export
SQLite负责:
数据。
Excel负责:
报表。
不要让Excel承担数据库的工作。
2. 软件整体架构
我会把程序拆成:
ProductionDataSystem
│
├─ UI
│ ├─ MainForm
│ ├─ HistoryForm
│ └─ SettingForm
│
├─ PLC
│ ├─ PlcService
│ └─ PlcSnapshot
│
├─ Production
│ ├─ ProductionService
│ └─ ProductionRecord
│
├─ Data
│ ├─ SqliteRepository
│ └─ DatabaseManager
│
├─ Export
│ └─ ExcelExportService
│
└─ Infrastructure
├─ Logger
├─ Config
└─ Scheduler
MainForm不直接:
读PLC
写数据库
生成Excel
MainForm只负责显示。
3. PLC通讯使用独立Service
假设使用 HslCommunication 连接 Siemens PLC。
不要在:
timer1_Tick
里面写几十个PLC读取。
可以封装:
public class PlcSnapshot
{
public bool IsConnected { get; set; }
public bool Trigger { get; set; }
public string QRCode { get; set; }
public int CT { get; set; }
public int CutterCount { get; set; }
}
PLC Service负责不断刷新:
PLC
↓
PlcService
↓
PlcSnapshot
其他模块只读取Snapshot。
这样PLC通讯和业务逻辑不会耦合在一起。
4. 为什么需要Snapshot
如果业务代码分别读取:
QR
CT
刀具次数
有可能出现:
读取QR的时候PLC还是产品A;
读取CT的时候PLC已经进入产品B。
最终会产生一条错误记录。
所以应该尽量让一组生产数据属于同一次采集。
例如:
public class PlcSnapshot
{
public DateTime Time { get; set; }
public string QRCode { get; set; }
public double CycleTime { get; set; }
public int CutterCount { get; set; }
}
生产完成时将整个Snapshot提交给数据层。
5. 产品触发一定要做沿检测
PLC里面可能有:
ProductionComplete = 1
如果上位机每100ms读取一次:
1
1
1
1
1
如果每次看到1都保存一次,就会生成多条重复数据。
应该检测:
0 → 1
例如:
private bool _lastTrigger;
private void CheckTrigger(bool current)
{
if (current && !_lastTrigger)
{
CreateProductionRecord();
}
_lastTrigger = current;
}
这也是工业通讯中非常常见的问题。
6. 数据库表设计
例如:
CREATE TABLE ProductionRecord
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
QRCode TEXT NOT NULL,
ProductionTime TEXT NOT NULL,
CycleTime REAL,
CutterCount INTEGER,
Result INTEGER,
CreatedTime TEXT NOT NULL
);
建议至少对:
ProductionTime
QRCode
建立索引。
因为以后最常见的查询就是:
查询某一天
查询某一个QR
7. 为什么可以按月建立数据库
假设设备每10秒生产一个产品:
8640条 / 天
一年大约:
315万条
五年就是千万级数据。
这种情况下我更喜欢按月归档,例如:
Data
│
├─ 2026
│ ├─ Production_202601.db
│ ├─ Production_202602.db
│ ├─ Production_202603.db
│ └─ ...
│
├─ 2027
└─ 2028
这样有几个好处。
单个数据库文件不会无限增大。
备份方便。
历史文件可以直接压缩。
数据库损坏影响范围有限。
查询某个月的数据也比较直接。
8. DatabaseManager负责自动切换数据库
软件运行时根据当前时间判断:
yyyyMM
例如:
202609
目标数据库:
Production_202609.db
如果文件不存在:
创建数据库
创建表
创建索引
如果存在:
直接打开
跨月以后自动切换。
生产业务层完全不需要关心:
现在应该写哪个数据库。
9. 每天生成Excel,但不要在0点重新读取PLC
Excel的数据来源应该是数据库。
例如:
2026-09-08
结束以后:
SQLite
↓
Query 2026-09-08
↓
EPPlus
↓
2026-09-08.xlsx
生成:
Report
│
└─ 2026
└─ 09
├─ 2026-09-01.xlsx
├─ 2026-09-02.xlsx
└─ 2026-09-03.xlsx
这样即使Excel生成失败:
原始数据依然在数据库里面。
下一次重新生成即可。
10. Excel建议包含哪些字段
例如:
| 序号 | QR码 | 生产时间 | CT | 刀具次数 | 结果 |
|---|---|---|---|---|---|
| 1 | ABC001 | 2026-09-09 08:10:01 | 7.82 | 10231 | OK |
| 2 | ABC002 | 2026-09-09 08:10:09 | 7.91 | 10232 | OK |
| 3 | ABC003 | 2026-09-09 08:10:17 | 8.03 | 10233 | NG |
后续还可以增加:
班次
机型
报警码
操作员
设备编号
配方
工位
11. 软件启动时一定要补报表
假设设备晚上:
23:50
突然断电。
第二天:
08:00
软件重新启动。
如果报表逻辑只写:
每天00:00生成Excel
那么昨天的Excel永远不会生成。
所以软件启动时应该检查:
数据库是否存在昨天的数据
昨天Excel是否存在
如果:
有数据库
无Excel
就自动补生成。
12. 写数据库和生成Excel不要阻塞PLC线程
推荐的数据流:
PLC Thread
↓
ProductionRecord
↓
DatabaseQueue
↓
DatabaseWorker
↓
SQLite
而不是:
PLC Thread
↓
SQLite
↓
Excel
↓
PLC Thread
通讯线程最重要的是:
保持稳定。
不要让磁盘IO影响PLC心跳和读取周期。
13. 建议给每条生产记录增加唯一ID
除了数据库自增ID之外,还可以生成:
MachineId
+
Time
+
Sequence
例如:
LINE01-20260909150312852-000018
这样以后:
PLC;
MES;
数据库;
Excel;
日志
都可以通过同一个编号关联。
出现问题以后追溯非常方便。
14. 日志应该记录什么
例如:
2026-09-09 08:10:01
PLC Connected
Product Trigger RisingEdge
QR = ABC001
CT = 7.82
CutterCount = 10231
Database Insert Success
RecordId = 182039
异常时:
PLC Read Failed
Database Insert Failed
Excel Export Failed
生产数据软件最怕的不是报错。
而是:
出错以后不知道什么时候开始出错,也不知道丢了多少数据。
15. 数据保存5年以上真正需要考虑什么
很多人一看到"保存5年数据",首先想到的是:
硬盘容量够不够。
其实纯文本生产数据占用非常小。
真正需要考虑的是:
数据库索引;
备份;
文件损坏;
程序异常;
跨月归档;
Excel重新生成;
数据重复;
时间同步;
查询性能。
如果设计合理,即使保存几年数据,对普通工业电脑来说压力也并不大。
16. 最终的数据链路
最终整个系统可以理解为:
Siemens PLC
│
↓
PlcService
│
↓
PlcSnapshot
│
Rising Edge
│
↓
ProductionService
│
↓
ProductionRecord
│ │
↓ ↓
SQLite Logger
│
↓
ExcelExportService
│
↓
Daily Excel
这样即使后续增加:
MES上传;
扫码枪;
多个PLC;
多条生产线;
云端数据库;
统计图表
也不需要推翻整个程序。
总结
生产数据采集软件看起来并不复杂。
无非就是:
PLC读数据
保存
生成Excel
但如果要求设备24小时运行,并且数据保存5年以上,那么就应该在项目一开始考虑:
通讯解耦;
数据一致性;
防重复;
数据库归档;
异常恢复;
补报表;
日志;
备份。
我的理解是:
工业软件最重要的能力不是"正常情况下能运行",而是"异常发生以后仍然知道发生了什么,并且能够恢复"。
后续如果有时间,我会继续整理完整的:
HslCommunication Siemens S7封装
SQLite生产数据库管理
EPPlus日报自动生成
WinForms历史数据查询
生产数据统计图表
把这一套生产数据采集软件完整拆成一个系列。