C# WinForms + 西门子S7 + SQLite + EPPlus生产数据采集系统架构设计

在自动化设备项目中,经常会遇到这样一类需求:

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历史数据查询

生产数据统计图表

把这一套生产数据采集软件完整拆成一个系列。

相关推荐
粉色大象2 小时前
handdrawn‑architecture‑video:开源SVG架构图转手绘4K动画视频|本地AI Agent Skill
人工智能·ai·ai作画·系统架构·开源·aigc·音视频
Lost of 程序猿2 小时前
船岸数据同步链路实战:SendFlag、单调版本号与断网三天后的续传
后端·c#·asp.net
微三云 - 廖会灵 (私域系统开发)6 小时前
私域团购系统架构设计:五级定价、自动结算与三级合规的技术实现
系统架构
木木学AI12 小时前
2026主流大模型电话机器人系统解析:技术架构、业务执行与企业落地实践
架构·系统架构·机器人
诚心呈意共享外卖配送系统12 小时前
中秋礼品配送峰值场景下,自建同城配送平台的技术选型与架构实践
架构·系统架构·创业创新
m0_5873830017 小时前
折扣卡CPS系统源码的技术架构与实战开发指南
人工智能·小程序·数据挖掘·系统架构·需求分析
cjp56020 小时前
012.UG二次开发,UG管道服务器与WPF管道通讯,传输JSON数据
c#
jiushidt21 小时前
ArcGIS Pro Add‑in 打包流程详解
arcgis·c#·.net·arcgispro
geovindu2 天前
CSharp: Observer Pattern
开发语言·后端·观察者模式·设计模式·c#·.netcore·行为模式