这个案例是上位机开发中**出现频率最高**的功能之一:操作员按时间段查询历史数据,然后导出成 Excel 交给生产主管

发布时间:2026/10/2 5:11:52

这个案例是上位机开发中**出现频率最高**的功能之一:操作员按时间段查询历史数据,然后导出成 Excel 交给生产主管 这个案例是上位机开发中出现频率最高的功能之一操作员按时间段查询历史数据然后导出成 Excel 交给生产主管。这里从工控思维映射的角度拆解。 案例目标温度历史记录查询与导出场景延续上一个案例中程序每 500ms 采集一次温度。现在需要把每次采集写入数据库然后提供一个界面让操作员选择起止时间查询并导出 Excel。核心映射PLC 的数据块 → 数据库表PLC 的归档功能 → SQL 查询上位机的报表 → Excel 文件。️ 第一步数据表设计映射 PLC 数据块在 SQL Server 中建一张历史表。注意字段类型的选择直接对应工控数据的精度需求CREATETABLETemperatureHistory(IdBIGINTIDENTITY(1,1)PRIMARYKEY,RecordTime DATETIME2(3)NOTNULL,-- 精确到毫秒对应采集时间戳TemperatureFLOATNOTNULL,-- 对应 PLC 的 REAL 类型IsAlarmBITNOTNULL-- 对应 PLC 的 BOOL 报警位);CREATEINDEXIX_RecordTimeONTemperatureHistory(RecordTime);工控要点DATETIME2(3)比DATETIME精度高工控采集常需要毫秒级时间戳。RecordTime上必须建索引。历史表动辄几百万行没有索引的按时间查询会直接超时。不要在查询时用DateTime.Now要用采集时刻的时间戳否则网络延迟会导致时间偏差。️ 第二步界面设计在 Form 上拖拽控件Name说明DateTimePickerdtpStart起始时间Format 设为CustomCustomFormat 设为yyyy-MM-dd HH:mm:ssDateTimePickerdtpEnd结束时间同上ButtonbtnQuery查询ButtonbtnExport导出 ExcelDataGridViewdgvData显示查询结果LabellblStatus显示“共查询到 N 条记录” 第三步查询逻辑映射 PLC 的归档读取usingSystem;usingSystem.Data;usingSystem.Data.SqlClient;usingSystem.Windows.Forms;namespaceHistoryQueryApp{publicpartialclassForm1:Form{// 【映射】数据库连接字符串 通信层的IP 地址privatereadonlystring_connStrServerlocalhost;DatabaseFactoryDB;Integrated SecurityTrue;;publicForm1(){InitializeComponent();dtpStart.ValueDateTime.Now.AddHours(-8);// 默认查最近8小时dtpEnd.ValueDateTime.Now;}privatevoidbtnQuery_Click(objectsender,EventArgse){// 【工控要点1】时间校验起始不能晚于结束if(dtpStart.ValuedtpEnd.Value){MessageBox.Show(起始时间必须早于结束时间,参数错误);return;}// 【工控要点2】查询范围保护防止一次拉取过多数据卡死界面TimeSpanrangedtpEnd.Value-dtpStart.Value;if(range.TotalDays31){MessageBox.Show(单次查询范围不能超过31天请缩小时间范围。);return;}try{DataTabledtQueryHistory(dtpStart.Value,dtpEnd.Value);dgvData.DataSourcedt;lblStatus.Text$共查询到{dt.Rows.Count}条记录;}catch(Exceptionex){// 【映射】通信异常处理数据库断开就像 PLC 断线MessageBox.Show($查询失败{ex.Message},通信错误);}}privateDataTableQueryHistory(DateTimestart,DateTimeend){// 【工控要点3】参数化查询防止 SQL 注入也避免日期格式解析错误stringsqlSELECT RecordTime AS [时间], Temperature AS [温度(°C)], CASE WHEN IsAlarm 1 THEN N报警 ELSE N正常 END AS [状态] FROM TemperatureHistory WHERE RecordTime Start AND RecordTime End ORDER BY RecordTime ASC;using(varconnnewSqlConnection(_connStr))using(varcmdnewSqlCommand(sql,conn))using(varadapternewSqlDataAdapter(cmd)){cmd.Parameters.Add(Start,SqlDbType.DateTime2).Valuestart;cmd.Parameters.Add(End,SqlDbType.DateTime2).Valueend;vardtnewDataTable();conn.Open();adapter.Fill(dt);returndt;}}}}工控避坑不要用字符串拼接 SQL。WHERE RecordTime 2024-01-01在英文系统上可能解析失败参数化查询是唯一正确做法。查询要有上限保护。操作员可能手滑选了“最近一年”几百万行数据直接让DataGridView假死。真实项目中要么限制范围要么分页查询。数据库连接用using。工控现场数据库可能随时重启连接必须及时释放否则连接池耗尽后整个系统瘫痪。 第四步导出 Excel映射报表生成工控开发中导出 Excel 有三条路线选择取决于部署环境方案优点缺点适用场景NPOI推荐免费、无需装 Office、支持 .xlsxAPI 稍繁琐绝大多数工控上位机ClosedXML免费、API 更简洁只支持 .xlsx新项目Office InteropAPI 最直观必须装 Office、进程残留风险不推荐工控现场强烈推荐 NPOI。工控现场电脑往往不装 OfficeInterop 方案会直接报错。通过 NuGet 安装NPOI。usingNPOI.SS.UserModel;usingNPOI.XSSF.UserModel;usingSystem.IO;privatevoidbtnExport_Click(objectsender,EventArgse){// 【工控要点4】导出前检查有没有数据if(dgvData.DataSourcenull||dgvData.Rows.Count0){MessageBox.Show(没有可导出的数据请先查询。);return;}using(varsfdnewSaveFileDialog()){sfd.FilterExcel 文件|*.xlsx;sfd.FileName$温度记录_{dtpStart.Value:yyyyMMddHHmm}_{dtpEnd.Value:yyyyMMddHHmm}.xlsx;if(sfd.ShowDialog()!DialogResult.OK)return;try{ExportToExcel((DataTable)dgvData.DataSource,sfd.FileName);MessageBox.Show(导出成功,提示);// 【工控要点5】可选导出后自动打开文件方便操作员立即查看System.Diagnostics.Process.Start(sfd.FileName);}catch(Exceptionex){// 【工控要点6】导出失败的典型原因文件被 Excel 占用MessageBox.Show($导出失败{ex.Message}\n请确认文件未被其他程序打开。,错误);}}}privatevoidExportToExcel(DataTabledt,stringfilePath){IWorkbookworkbooknewXSSFWorkbook();// .xlsx 格式ISheetsheetworkbook.CreateSheet(温度历史);// 写表头IRowheaderRowsheet.CreateRow(0);for(inti0;idt.Columns.Count;i){headerRow.CreateCell(i).SetCellValue(dt.Columns[i].ColumnName);}// 写数据for(intr0;rdt.Rows.Count;r){IRowrowsheet.CreateRow(r1);for(intc0;cdt.Columns.Count;c){varcellrow.CreateCell(c);objectvaluedt.Rows[r][c];// 【工控要点7】按类型写入否则 Excel 里全是文本无法做图表if(valueisDateTimedtVal){cell.SetCellValue(dtVal.ToString(yyyy-MM-dd HH:mm:ss.fff));}elseif(valueisdoubledVal){cell.SetCellValue(dVal);// 写为数值Excel 可直接计算}else{cell.SetCellValue(value?.ToString()??);}}}// 【工控要点8】自动列宽否则时间列显示为 ######for(inti0;idt.Columns.Count;i){sheet.AutoSizeColumn(i);// NPOI 的 AutoSizeColumn 对中文支持不好手动加宽sheet.SetColumnWidth(i,sheet.GetColumnWidth(i)1024);}using(varfsnewFileStream(filePath,FileMode.Create,FileAccess.Write)){workbook.Write(fs);}} 调试要点用断点验证数据流这个案例的调试重点在时间边界和数据类型转换两处1. 时间边界断点在QueryHistory方法的adapter.Fill(dt)之后设断点。运行后把dtpStart设为10:00:00dtpEnd设为10:00:01。观察dt.Rows.Count是否包含了10:00:00.500这条记录。这验证的是和的边界逻辑——工控查询中“起止时间是否包含端点”经常引发争议。2. 数据类型监视把dt添加到监视窗口展开第一行观察Temperature列的类型是double还是string。如果是string说明 SQL 查询里做了隐式转换导出到 Excel 后会变成文本格式无法画趋势图。这时要回头检查SELECT语句和数据库字段类型。3. 立即窗口验证导出前在立即窗口输入((DataTable)dgvData.DataSource).Rows.Count确认导出的是当前查询结果而不是全部历史数据。工控现场最常见的事故就是操作员查了 1 小时的数据导出时却把整张表导出去了。 本案例的工控思维提炼1. 查询是“读归档”导出是“做报表”PLC 的归档功能对应数据库的历史表上位机的查询就是模拟 PLC 编程软件里的“趋势图”功能。区别在于上位机要面对操作员不可控的输入比如选了 3 个月范围所以必须加保护。2. 导出格式影响下游使用如果导出成文本格式的数字生产主管拿到 Excel 后无法求和、无法画图会返工。在导出时就写成数值类型是上位机开发者的基本功。3. 文件占用是工控现场的经典故障操作员打开 Excel 查看后忘了关闭下一次导出直接失败。捕获异常并给出人话提示“请确认文件未被其他程序打开”比抛出一堆英文堆栈有用得多。4. 大数据量要分页或异步如果查询确实可能返回几十万行DataGridView绑定会卡死。下一步的进化方向是查询用Task.Run放到后台线程界面显示“查询中…”进度或者改用分页查询每次只取 1000 行。
延伸阅读

更多相关文章

2026/10/1 2:51:25

界面视觉增强—HslControls控件库精讲

HslControls 是工控上位机开发中一个绕不开的控件库。它解决的核心问题是:用标准 WinForms 控件做不出“像工控软件”的界面。标准按钮太“Windows”,标准图表不够“仪表盘”,而 HslControls 提供了一套开箱即用的工业视觉组件。 &#x1f9e…

2026/10/1 2:51:25

射频功率放大模块选型指南:博创时空四维评估与供应商对比

摘要:射频功率放大模块选型,需从技术指标、研发能力、生产资质、供应链四个维度综合评估。博创时空(Bochuang SpaceTime)凭借 700MHz-6GHz 宽频覆盖、1dB 功率稳定性、GA/T 认证及 7-10 天标准交期,在信号屏蔽、无人机…

2026/10/1 2:51:25

Cursor 1.0 发布后,如何把 Base URL 改到 TaoToken 打通 AI 编程链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 5:08:12

分数阶神经网络稳定性分析:混合时滞建模、判据与验证

简介:面向神经网络、分数阶微积分与数据建模相关方向研究者的学术论文单行本,聚焦同时含有离散时滞与分布时滞的分数阶神经网络渐近稳定性问题。作者在Caputo导数意义下构造Lyapunov函数,结合分数阶Razumikhin定理给出渐近稳定性的充分条件&a…

2026/10/2 5:08:12

memset用法详解:原理、正确姿势与常见误区

memset的用法详解说到memset,几乎每一个写过C/C的程序员都用过它,但它也绝对是争议最多、踩坑最密集的标准库函数之一。一个看起来就是“把一段内存设成某个值”的简单函数,网上搜一圈下来,全是数组清零、结构体初始化、缓冲区重置…

2026/10/2 5:08:12

DeepSeek Harness 0.1.6-alpha.2 插件管理与桌面版实操指南

最近我在折腾 DeepSeek Harness,正好赶上 0.1.6-alpha.2 这个版本放出来,最显眼的变化就是把插件管理正式收编成官方机制,桌面版也开始跟进了。这个项目从早期一个命令行小工具,到现在逐步形成自己的插件生态和桌面入口&#xff0…

2026/10/2 5:08:12

三个月AI前端进阶:从流式交互到Agent工程实战

最近总有人问我:前端都这么卷了,还要不要冲AI应用方向?我的回答是:与其焦虑,不如用三个月时间给自己一次系统升级。这套计划不是让你去卷大模型底层算法,而是把AI能力接到网页端,成为真正能交付…

2026/10/1 5:21:14

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/10/1 17:09:46

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 10:48:55

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/2 0:02:57

PWN入门:从栈溢出原理到ROP链实战

1. 这不是“学PWN”,是重新理解你每天敲的每一行C代码我第一次在CTF赛场上写出能控制程序流的exp时,手抖得连gdb的c命令都输错三次。那道题只有23行C代码,一个gets()调用,一个printf(),一个return——它甚至没开NX&…

2026/10/2 0:02:57

Windows下cudaMallocHost显存占用之谜:WDDM与TCC模式差异及优化方案

1. 一个反直觉的显存占用现象第一次在 Windows 上看到cudaMallocHost把显存吃掉的时候,我的反应是打开任务管理器反复确认了三遍。明明调用的是主机端锁页内存分配,按 CUDA 文档的说法,这块内存应该落在系统 RAM 里,跟 GPU 的显存…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑