简介这份文档面向使用 WonderWare Intouch 2014 R2 构建 SCADA 系统的工程人员与工控方向学习者聚焦 HMI 与 SQL Server 2012 的数据对接以及用 Excel 搭建报表系统的完整做法。内容从在 SQL Server Management Studio 中配置身份验证方式、新建登录名与数据库表讲起延伸到 Intouch 标记名字典中的内存变量定义、条件脚本编写、SQL 访问管理器的绑定列表设置以及 SQLConnect、SQLInsert、SQLDisconnect 等函数在应用程序脚本中的写法并给出按秒级周期写库、以分钟数判断触发时机的逻辑示例。后半部分说明 Excel 报表模板的控制面板与参数日报页面搭建、VBA 引用配置并附数据库 mdf 与 ldf 文件的备份恢复流程。资源包仅含 1 个 doc 文件约 2.48MB图文对照便于按章节查阅已有 533 人学习适合需要把 Intouch 数据落库并输出报表的现场调试与项目实施参考。1. 半夜两点报表对不上账问题往往不在 Excel值班室最常出现的一种返工是每月月底有人拿着打印出来的报表问“这个班次的产量怎么和现场记的对不上” 屏幕上的实时曲线刷新正常HMI 也没有报警可数据库里就是少了几条记录。这套组合的标题很直白在 Intouch 2014 R2 里把变量写进 SQL Server 2012再用 Excel 报表系统把库里的数据取出来形成日报。它解决的是组态软件、关系型数据库、办公软件三段链路拼接起来的问题适合被安排做报表的自控或仪表工程师也适合想把 SCADA 数据链路弄清楚的学生。三个软件单独看都不难难的是它们之间那几次交接交错了不会报错只会安静地少数据。这一段先说结论这套东西真正的技术含量不在 Excel 画得好看而在“什么时候写、写哪几列、连接什么时候断开”。下面按现场顺序拆。2. 全链路拆解从现场变量到 Excel 报表的四层结构整套系统在运行时有四个独立的执行者它们通过“变量名”和“表列名”这两套约定连接起来。理解这一点后面所有的配置动作才有落点否则你会陷入“页面明明在动数据库里却没有”这种奇怪的矛盾里。2.1 四层结构变量层、调度层、存储层、展示层变量层是 Intouch 的标记名字典。这里既有从 PLC 采集上来的 I/O 变量产量、运行状态、温度也有纯粹在 HMI 内部存在的内存变量连接号、写入标志、计算机名。前者是业务数据后者是“辅助变量”很多现场做失败的项目就是缺了这几个辅助变量脚本写得很漂亮却没有办法判断“这次到底写没写”。调度层是 Intouch 的应用程序脚本与条件脚本。应用程序脚本常驻运行负责连接数据库、关闭数据库条件脚本靠系统时间变量$Second、$Minute周期性触发负责判断“这一刻该不该写入”。之所以把连接放在常驻脚本里而不是每次写数据都重新连接一次是因为建立 SQL 连接的开销远大于插一行数据频繁连断在低配工控机上足以把画面拖卡。存储层是 SQL Server 2012 里的一个库和若干张表。库和表本身很简单关键在登录名和身份验证模式默认安装的实例只开了 Windows 身份验证HMI 那边填了一堆用户名密码却根本连不上这种问题在调试初期最容易反复出现。展示层是一个带宏的 Excel 文件通过 ADO 连到同一个库把一段时间范围内的记录取回来按班次或按小时汇总成“参数日报”。它和 HMI 之间没有直接关系两者只共享数据库这一个中间层这也是这套架构最值得肯定的地方HMI 挂了不影响已经写入数据库的数据报表端挂了也不影响现场监视。层承载物出错时的症状主要定位手段变量层标记名字典、绑定列表值不动、值为 0、名字拼错用“标记查看器”看实时值调度层应用程序脚本、条件脚本只写一次、永远不写、回调卡顿在脚本里插入日志标记计数存储层SQL Server 库表、登录名连接失败、列类型不匹配SSMS 里直接查表展示层Excel 宏文件、VBA查不到当天数据、日期差一天在 VBA 里打印 SQL 语句提示这张表建议打印出来贴在调试工位旁出现问题时先判断属于哪一层再动手。不要一上来就改脚本。2.2 对象清单与命名三个内存标记、一张绑定列表、一张表整个方案里真正需要新造的“基础设施”不多我把它们列清楚你照着建就不会乱。对象类型作用建议初值ConnID连接号内存整型保存SQLConnect返回的连接编号0WSQLS写入标志内存离散标记本条数据是否已写入防重复0假NodeName节点名内存消息存当前计算机名用于区分多台上位机空串TDays 绑定列表SQL 访问管理器对象变量与表列的映射清单—TDays 表SQL Server 数据表存放小时/班次记录—命名上我一般会加前缀辅助变量用c_或x_业务变量用带单位的名字如ProdCount_Pcs。ConnID这种名字在全工程里唯一就行但一定要在名字后面加注释不要只用拼音首字母。当工程超过三台上位机后没有人记得WSQLS是哪四个词的缩写。名字可以改但建议在调试前定稿因为脚本、绑定列表、数据库列名三处都要同步修改改一次要动三个地方。2.3 触发节奏为什么是$Second3和$Minute58原始文档里两个条件脚本分别用$Second3和$Second5触发写入判断用$Minute58。这三个数字不是随意设定的它们共同指向一个目标在整点切换之前把当前累计值保存一份快照。$Second的取值范围是 059每秒钟变化一次。$Second3意味着在第 3 秒这个条件为真。需要注意一个容易被忽略的行为条件为真期间条件脚本有可能被重复执行多次具体次数取决于脚本调度周期和当前系统负载。也就是说这条脚本天然不是“只跑一次”的凡是写库这类有副作用的动作必须自己加一次性锁这就是WSQLS存在的意义。$Minute58给出的是 58 分和 59 分两个整分钟也就是接近 120 秒的时间窗口。为什么要留两分钟而不是卡在$Minute59因为脚本执行、网络传输、SQL 写入都需要时间如果卡在最后一分钟一旦这一分钟里出现卡顿数据就彻底丢了而且不会有任何提示。留出一分钟的余量本质上是用“重复写入风险”去换“漏写风险”而重复可以用WSQLS挡住漏写则无法补救。把这两件事结合起来完整的判断逻辑应该是三层先确认自己是哪台机器再确认是否进入写入窗口最后确认本次窗口内没写过。IF 计算机名 OP01 THEN 第一层只让指定上位机写避免多台重复 IF $Minute 58 THEN 第二层进入整点前的写入窗口 IF WSQLS 0 THEN 第三层本次窗口还没写过 SQLInsert(...) 写入 WSQLS 1; 立锁 ENDIF; ELSE WSQLS 0; 离开窗口后复位为下一个小时准备 ENDIF; ENDIF;这里有个细节值得单独说WSQLS的复位必须放在$Minute 58不成立的那个分支里也就是$Minute变成 0 之后的第一秒。如果你把复位也塞进第三层判断内部就会变成“写一次永远不再写”如果放在窗口内则可能在同一分钟内反复写。复位位置是整个逻辑里最容易放错的一行我见过不止一次因为这一行放错导致整张表一小时只多一条记录。3. 数据落库标记名字典、绑定列表与 SQLConnect/SQLInsert 的落地写法这一章是能直接照着做的部分。顺序不能颠倒先有库和表再有登录名再到 Intouch 里建标记、建绑定列表最后才是脚本。反过来做你会在脚本调试时被迫反复改数据库而数据库一改绑定列表就得重建。3.1 数据库侧身份验证模式、登录名与建表语句先用 SSMS 连上实例。如果这台机器上只有你自己用Windows 身份验证足够但只要 HMI 运行在另一台机器上、或者运行账户和你的登录账户不一致就必须把实例改成“SQL Server 和 Windows 身份验证模式”然后重启数据库服务。这一步在实例属性页的“安全性”里改改完必须重启服务才生效很多人改完不重启就跑回去试连接白折腾半小时。接着新建登录名。在“安全性 → 登录名”上右键新建选“SQL Server 身份验证”设一个密码取消“用户在下次登录时必须更改密码”。然后到“用户映射”页把它映射到目标库勾上db_datareader和db_datawriter两个角色就够了。注意不要用sa去连 HMI。用超级管理员账号能省掉所有权限问题但一旦后续按安全要求统一改密码所有引用它的脚本会同时失效而且你很难回忆起一共有几处引用。给 HMI 单独建一个只读写本库的账号这是血泪经验。表的建立要和绑定列表对应上。下面这张表是我一般会用的结构列名按业务改但RecordTime这一列建议保留-- 小时/班次数据表绑定列表会把变量写进对应的列 CREATE TABLE dbo.TDays ( RecordTime datetime NOT NULL CONSTRAINT DF_TDays_RecordTime DEFAULT (GETDATE()), -- 服务端打点防上位机时钟漂移 LineName nvarchar(32) NOT NULL, -- 产线/机台名对应内存消息变量 NodeName ProdCount int NOT NULL, -- 产量累计值对应内存整型变量 RunState int NOT NULL, -- 运行状态 0/1对应内存离散变量 TagTime datetime NULL -- 可选HMI 侧取的 $Date $Time ); GO CREATE INDEX IX_TDays_RecordTime ON dbo.TDays (RecordTime); GORecordTime用GETDATE()默认值有个关键前提绑定列表里不要包含这一列。SQLInsert是按绑定列表的映射关系插入的没出现在绑定列表里的列要么有默认值要么允许空否则整条插入会失败。失败时的现象是“脚本执行了、HMI 没报错、表里没数据”非常难查这也是我在第 5 章要展开的一个问题。索引建在时间列上是给 Excel 端按日期范围查询用的数据量到了几十万行以后有没有这个索引差别很明显。3.2 标记名字典三个内存标记的类型与初值打开标记名字典逐个建ConnID选内存整型初值 0只读关闭。它保存的是SQLConnect的返回值——连上是个非零编号连不上通常是 0。把这个值在画面上显示出来哪怕只是一个小数字框调试阶段能省下大量时间数字是 0 就说明连接没建立不用再往下猜。WSQLS选内存离散初值关。它是我们的“一次锁”含义是“本次写入窗口已经写过了”。NodeName选内存消息字符串长度给 32 就够计算机名一般不超过 15 个字符。它由GetNodeName函数在运行时填充不要在初值里写死机器名——一旦工程拷贝到另一台机器写死的名字会导致所有写入都被跳过而画面看起来完全正常。顺便把业务变量也确认一下产量这类累计值用内存整型或 I/O 整型运行状态用离散采集上来的字符串用消息。类型这件事在下一节的绑定列表里会再对一遍两处必须一致因为 Intouch 到 SQL 的类型转换不会给你明确的类型不匹配提示。3.3 绑定列表变量名与列名的对齐表打开左侧工具视图里的 SQL 访问管理器选“绑定列表”新建一个名字叫TDays和表名一致纯粹为了自己好记不强制。在里面加行每一行是“Intouch 变量 → SQL 列名 → SQL 数据类型”的三元组。Intouch 变量SQL 列SQL 类型对应标记类型NodeNameLineNamenvarchar(32)内存消息ProdCount_PcsProdCountint内存整型RunStateRunStateint内存离散三个对应关系里最容易出错的是第三个。Intouch 的内存离散在库里如果建成bit多数情况下没问题但如果你在脚本里给它赋过 1/0 以外的东西或者库端是tinyint而你传了 -1插入会静默失败。我一般统一用int存离散多占 3 个字节换来的是绝对不会因为类型边界翻车。LineName这个映射值得多说一句。原始做法是把当前计算机名写进去这样多台上位机写同一张表时可以按机器区分。但计算机名如OP01在报表里不好看值班员想看的是“一号线”。折中的做法是库里存计算机名Excel 端维护一张“机器名 → 产线名”的对照表报表里做一次查找替换。这样现场改机器名不用动数据库改产线名不用动 HMI。3.4 应用程序脚本连接、判据、写入、复位四段式脚本分三处写。第一处是应用程序脚本的“运行时”程序启动时执行一次{ 应用程序脚本 —— 运行时 } SQLConnect( ConnID, providersqloledb;Data SourceSCADA01;Initial CatalogYC_BB; User IDhmi_writer;Password******; );SQLConnect两个参数第一个是接收连接编号的整型变量第二个是连接字符串。字符串里五个部分用分号隔开每项前加等号provider固定写sqloledbData Source是数据库所在计算机名或实例名Initial Catalog是库名User ID和Password是 3.1 里新建的那个专用账号。改工程时要改的就是这四个值其中Data Source最容易忘——从测试机搬到现场机库名对了、账号对了就是计算机名没改然后一直连不上。第二处是“关闭时”脚本{ 应用程序脚本 —— 关闭时 } SQLDisconnect( ConnID );这行不能省。不断开连接数据库端会保留一个会话每次都开不关池子里的连接会被慢慢耗尽最后表现为“运行几天后突然写不进去”。而且这种状态在 HMI 上看不出任何异常。第三处是条件脚本。在“脚本 → 条件”里新建条件表达式填$Second3条件类型选“为真时”{ 条件脚本每小时的整点前写一次快照 } GetNodeName( NodeName, 131 ); { ① 取本机计算机名131 是缓冲区长度 } IF NodeName OP01 THEN { ② 只有指定上位机承担写入防重复 } IF $Minute 58 THEN { ③ 进入整点前两分钟的窗口 } IF WSQLS 0 THEN { ④ 本窗口还没写过才继续 } SQLInsert( ConnID, TDays, TDays ); { ⑤ 按绑定列表插一行 } WSQLS 1; { ⑥ 立锁挡住本窗口内后续触发 } ENDIF; ELSE WSQLS 0; { ⑦ 离开窗口立刻复位为下一小时准备 } ENDIF; ENDIF;逐行说清楚。①GetNodeName的两个参数是接收变量和缓冲区长度131 是个够用的安全值这一步每次触发都执行开销很小。②字符串比较区分大小写OP01和op01不相等现场机器名习惯用大写写脚本时注意别写错。③两分钟窗口的取舍在 2.3 已经讲过。④WSQLS是锁。⑤SQLInsert三个参数连接号、表名、绑定列表名。注意它依赖前面已经SQLConnect成功并拿到编号如果ConnID是 0这一句什么都不会发生也不报错。⑥立锁。⑦复位位置必须在ELSE分支里。写完之后不要急着等整点。把条件临时改成$Second3加上$Minute不判断手动触发一次去 SSMS 里SELECT TOP 10 * FROM dbo.TDays ORDER BY RecordTime DESC看一眼确认数据进去了再把时间条件加回来。这个“先通后准”的顺序能帮你把问题定位在脚本逻辑里而不是连接问题上。4. Excel 报表端用 VBA 把 SQL Server 的班次数据取成日报报表端是一个.xlsm文件里面两个工作表加一段 VBA。它和 HMI 之间没有接口唯一的耦合是数据库表结构所以这部分可以完全脱离现场设备来开发和调试——你甚至可以在自己电脑上装个 SQL Server 表达版手工插几十行测试数据先把报表跑通。4.1 模板骨架控制面板页与参数日报页“控制面板”页只放三样东西报表日期或起止日期、一个“刷新”按钮、一个状态提示单元格。日期用单元格输入按钮绑定宏。状态提示用来显示“查询到 N 条记录”或错误信息没有这个单元格VBA 出错时你只会看到一个没有反应的按钮还以为是宏没启用。“参数日报”页是呈现区表头固定数据区从第 5 行开始往下铺。表头列的顺序不需要和数据库一致VBA 负责搬运和重排这是把“存储结构”和“展示结构”解耦的关键。原始文档里把这两个页面分开是很规范的做法值得照抄。Excel 侧还要做两件事。一是把文件另存为启用宏的工作簿格式.xlsx存 VBA 会直接丢掉代码。二是信任中心里允许宏运行或者把文件放到受信任位置。这两步不做打开文件是一片空白加一条黄色警告条看起来像文件坏了。4.2 VBA 引用与连接对象三项引用加 ADODB进 VBA 编辑器在“工具 → 引用”里勾上三项Visual Basic For Applications、Microsoft Excel 对象库、Microsoft ActiveX Data Objects版本选机器上装的最新的那个。第三项是连数据库用的漏勾的话代码里ADODB.Connection会直接报“用户定义类型未定义”而且这个错误出现在编译期你还以为是语法写错了。连接字符串和 HMI 那边基本一致只是账号不同。报表端只需要读用单独的只读账号更稳Option Explicit 报表端专用只读连接串与 HMI 的写入账号分开便于单独改密码 Private Const CONN_STR As String _ ProviderSQLOLEDB;Data SourceSCADA01;Initial CatalogYC_BB; _ User IDreport_reader;Password******;把连接串写成模块级常量好处是所有查询共用一处换库换账号只改这一行。坏处是密码明文躺在代码里所以这个文件不要随手发给外部人员。更稳妥的做法是把连接串写在一个隐藏工作表里代码去读单元格改密码不用进 VBA 编辑器。4.3 取数代码从 Recordset 到单元格核心是一个查询函数加一段填充逻辑。注意日期条件的写法这里最容易踩区域设置的坑Public Sub RefreshDailyReport() Dim conn As ADODB.Connection Dim rs As ADODB.Recordset Dim sql As String Dim dDate As Date Dim ws As Worksheet dDate ThisWorkbook.Worksheets(控制面板).Range(B3).Value 报表日期 Set ws ThisWorkbook.Worksheets(参数日报) ws.Range(B5:F5000).ClearContents 先清旧数据 Set conn New ADODB.Connection conn.ConnectionTimeout 5 连不上就 5 秒报错不要无限等 conn.Open CONN_STR 用 yyyy-mm-dd 显式格式化避免区域设置把 03/04 解释成 3 月 4 日或 4 月 3 日 sql SELECT RecordTime, LineName, ProdCount, RunState _ FROM dbo.TDays WHERE RecordTime Format$(dDate, yyyy-mm-dd) 00:00:00 _ AND RecordTime Format$(DateAdd(d, 1, dDate), yyyy-mm-dd) 00:00:00 _ ORDER BY RecordTime Set rs New ADODB.Recordset rs.CursorLocation adUseClient 客户端游标断开后仍可遍历 rs.Open sql, conn, adOpenStatic, adLockReadOnly If rs.EOF Then ThisWorkbook.Worksheets(控制面板).Range(B5).Value 该日期无记录 Else ws.Range(B5).CopyFromRecordset rs, 5000 一次最多灌 5000 行 ThisWorkbook.Worksheets(控制面板).Range(B5).Value 共 rs.RecordCount 条 End If rs.Close: Set rs Nothing conn.Close: Set conn Nothing 两个对象都要释放别只关一个 End Sub几个参数值得解释。ConnectionTimeout 5是连接超时默认值太长数据库不可达时按钮会看起来像没有反应。CursorLocation adUseClient让结果集落在客户端后面即使关掉连接也能继续读RecordCount。adOpenStatic adLockReadOnly是只读静态游标对报表这种一次性取数最合适不要用动态游标那会让数据库端一直挂着游标开销。CopyFromRecordset的第二个参数是最大行数给个上限防止某次误查把整个库拉进表格里卡死。ClearContents这一步不能省。因为CopyFromRecordset是从指定单元格开始覆盖写如果这次查到的行数比上次少旧数据会残留在下面报表看起来多了一截而且完全对不上账。4.4 触发与分发按钮、定时、另存为按钮方式最简单在控制面板页放一个形状右键“指定宏”选RefreshDailyReport。值班员每天上班点一下或者交班前点一下。如果要求无人值守就得靠工作簿的Workbook_Open事件或者 Windows 计划任务定时打开文件。Workbook_Open里加一句调用就行但要注意打开文件时如果宏被禁用这个事件也不会跑。所以我一般两者都配开机自启打开文件跑一次同时保留手动按钮做补偿。分发环节有个实际约束报表要给不装数据库客户端的人看。做法是刷新完之后另存一份纯值副本只保留数值不带宏不带连接发出去谁都能打开。这一步用SaveCopyAs或者另存为.xlsx都可以但一定要另存不要在原件上改格式否则下次打开就没有宏了。5. 排查清单这几处翻车都是“看着没问题”下面五条是这套方案里最典型的失败模式。共同点是画面正常、脚本在跑、Excel 也能开就是数据不对。每一条我都按“现象 → 原因 → 解决”写照着对号入座比盲目改脚本快得多。5.1 脚本执行了表里一条记录都没有现象把条件脚本改成每秒触发、临时去掉时间判断HMI 上能看到ConnID有值但 SSMS 里查表始终是空的也没有任何报错。原因基本是两个。一是表里有不允许为空、又没有默认值的列而绑定列表没有覆盖它SQLInsert在数据库端被拒绝Intouch 侧不会弹窗因为这类 SQL 错误默认不往上抛。二是绑定列表里的列名和表里的列名大小写或拼写不一致同样是静默失败。解决办法是把SQLInsert单独拿出来验证在脚本里加一个内存整型计数器每次执行SQLInsert之后自增然后看计数器有没有涨。涨了说明函数被调用了问题在数据库端去 SSMS 里手动执行一条INSERT复现就能看到真实错误。另外把表结构导出来和绑定列表逐列对一遍比在界面上点来点去可靠。5.2 连接号一直是 0怎么改连接串都不行现象画面上显示ConnID 0把Data Source从计算机名改成 IP、从 IP 改成实例名全都不行。原因往往不在连接串本身而在实例的身份验证模式。默认安装只开了 Windows 身份验证你在连接串里写 SQL 账号密码服务端直接拒绝。另一个常见原因是 SQL Server Browser 服务没启动或者 TCP/IP 协议在配置管理器里是禁用的客户端根本找不到实例。解决顺序是固定的先在 SSMS 里用同一个账号密码手动登录一次登不上就先把服务端配置改对登得上再回头查连接串。这一步能把问题一刀切成“服务端”和“客户端”两半省掉大量来回试。改完身份验证模式记得重启数据库服务。5.3 报表日期差一天或者查询结果为空现象明明 15 号有数据选 15 号查出来是空的选 16 号反而查出一部分。原因是日期边界处理。如果用RecordTime 2024-05-15这种写法SQL 会把它当成00:00:00.000只能匹配到零点整那一条。如果用BETWEEN 起 AND 止而止日期没加时分秒同样漏掉止日当天。还有一种情况是操作系统的日期格式是MM/dd/yyyyVBA 里拼出来的字符串被 SQL 按另一种格式解析月份和日期互换。解决办法是统一用“左闭右开”区间[当天 00:00:00, 次日 00:00:00)并且在 VBA 里用Format$(dDate, yyyy-mm-dd)显式格式化不依赖系统区域设置。同时把操作系统短日期格式改成yyyy-MM-dd这一条在原始文档里也专门提到过不是多余的步骤。5.4 数据库附加失败提示找不到日志文件或拒绝访问现象把.mdf和.ldf拷到新服务器在 SSMS 里附加要么报“无法打开物理文件操作系统错误 5拒绝访问”要么报找不到日志文件。前者是权限问题SQL Server 服务跑在一个虚拟账户下对拷过去的目录没有读写权限。把DataBase目录的权限授予该服务账户或者干脆把文件放到实例默认的数据目录下问题就消失。后者是日志文件丢失或拷漏了——只拷了.mdf。日志文件丢失时可以不带日志附加SQL 会重建一个-- 日志文件丢失时只附加数据文件并重建日志 CREATE DATABASE YC_BB ON (FILENAME E:\DataBase\YC_BB.mdf) FOR ATTACH_REBUILD_LOG; GO执行前确认目标库名没被占用且路径是数据库服务能访问到的绝对路径。附加完成后立刻做一次完整备份因为重建出来的日志是空的这个库此时不具备时间点恢复能力。5.5 运行几天后写入突然停止重启 HMI 又好了现象系统连续跑三五天某天开始数据不再进库HMI 画面一切正常重启一次上位机就又好了。这是连接泄漏的典型表现。应用程序脚本的“关闭时”没有写SQLDisconnect或者脚本里做过“断线重连”但只SQLConnect没先SQLDisconnect连接对象越积越多最后数据库端的会话数或客户端句柄耗尽新的连接建立不起来。而ConnID这个变量可能还保留着上一次的值看起来“有连接”实际上早就断了。解决办法是把连接生命周期收拢到一处启动时连一次关闭时断一次中间任何情况下都不重复连。要监控的话在条件脚本里判断ConnID 0时往一个报警标记置位让画面显示“数据库连接异常”而不是等值班员发现报表没数据。SQL Server 侧可以查sys.dm_exec_sessions看这个账号挂了多少会话数字只增不减就是泄漏。6. 二次开发把一次性的组态改成可复制的模板这套东西做完第一套之后第二套的边际成本应该趋近于零。做不到的原因通常不是技术而是把所有参数硬编码在脚本里了。我的改法是三步。第一步参数外置。连接串、写入窗口的分钟数、承担写入的机器名全部提出来放到一组内存消息标记里在画面上开一个隐藏的“工程配置”页去改改完写回标记字典的初值。这样换现场只改配置页不碰脚本。要更彻底一点可以把这几项存到数据库的一张配置表里HMI 启动时读一次Excel 端也读同一张表两边永远一致。第二步触发策略改造。$Minute58这种“按小时估写”的方式本质上是拿时间当触发器适合产量累计值这种连续量。如果业务要的是“每个班次结束记录一次”或者“每次换型记录一次”就别再硬套时间条件改成事件驱动换型信号上升沿触发一次写入配一个WSQLS的变体做去重。这样记录的语义更准也不用担心整点那两分钟里系统正好在报警。改造时保持“判断、立锁、写入、复位”这四段结构不变只换判断条件风险最小。第三步备份与恢复脚本化。附加数据库这件事手动做一次可以做十次一定出错。把CREATE DATABASE ... FOR ATTACH_REBUILD_LOG和后续的完整备份写成一个.sql文件每次恢复时改两行路径执行比在图形界面里点五层菜单稳。同时给报表端加一个“数据截止时间”的显示值班员一眼能看出数据是不是最新的。至于验证方法我固定用三步手工插一条已知数据看报表能否查出来把 HMI 的时间往前调一分钟看是否按预期触发断开网线跑一轮看报警是否亮起。三步都过这套配置才算能用。从那以后我每次接手这类 HMI 加数据库的活儿都会先把“连接号是否非零、绑定列表列数是否等于表列数、日志文件是否随数据文件一起拷走”这三条写在纸上调完逐条打勾绝不上来就改脚本。这套流程救过我很多次。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
基于WebSocket与Disruptor构建高性能协同编辑系统 1. 项目概述在当今的在线协作场景中,实时协同编辑已成为提升团队效率的关键技术。本文将详细介绍如何基于WebSocket和Disruptor无锁队列构建一个高性能的协同编辑系统。这个系统能够实现图片的实时协同编辑,同时解决并发冲突问题,确保所有用户… · 2026/11/4 2:57:20
开发者API资源管理平台:低成本解决方案 1. 项目概述:开发者API资源管理解决方案最近在技术社区发现一个让开发者群体兴奋的工具——某API资源管理平台。这个平台最吸引人的特点是采用"注册即送每日签到"的积分获取机制,开发者可以通过简单的互动行为持续获取API调用额度。作为一个长… · 2026/11/4 2:57:20
动态规划解决序列无重复子序列划分问题 1. 题目背景与核心需求解析这道来自AtCoder Beginner Contest 397的C题,题目编号为ABC397C,全称"Variety Split Easy"。作为ABC竞赛的常规题型,它考察的是选手对基础算法和数据结构应用的掌握程度。题目要求我们处理一个整数序列&a… · 2026/11/4 2:57:20
STM32移植cJSON解析JSON字符串实战:内存配置、解析避坑与压测验证 简介:这份资源面向使用STM32进行嵌入式开发的工程师与学习者,聚焦于在单片机环境中解析和生成JSON格式数据这一常见需求。包内提供经过工程验证的cJSON移植与调用示例,可直接嵌入实际项目,帮助读者解决设备与上位机、云平台之间轻… · 2026/11/4 3:38:29
自动化数据分析流水线:智能体技术与工程实践 1. 项目背景与核心价值在数据驱动的决策时代,传统的数据分析流程往往需要人工介入多个环节——从数据清洗、特征工程到建模分析,再到最终的可视化呈现。这不仅效率低下,还容易因人为因素导致结果偏差。我们团队最近构建的这套自动化流水线&am… · 2026/11/4 3:38:29
音频驱动视频生成:稀疏帧如何让数字人实现无限长对话 打开一段语音,让数字人说出同样的话,30秒内不翻车容易,三分钟不翻车很难。我在本地搭音频驱动视频生成(audio-driven video generation)管线时定的验收线,就是这样一段三分钟连续口播。前30秒效果通常不错&… · 2026/11/4 3:38:29
财富创造的底层逻辑:三层套利模型解析 1. 财富逻辑的底层架构解析财富创造的本质是价值交换过程中的套利行为。这种套利并非简单的投机取巧,而是基于信息差、资源差和能力差形成的系统性优势。我在金融行业从业十年间观察到一个现象:所有可持续的财富积累,本质上都是三层套利模型的… · 2026/11/4 3:38:29
STM32上cJSON移植与JSON解析实战:内存管理与避坑指南 简介:这份资源面向使用STM32进行嵌入式开发的工程师与学习者,聚焦于在单片机工程中集成cJSON库,解决JSON格式数据的解析与生成问题。压缩包共133个文件,约1.76MB,以31个c源文件与31个h头文件为核心,涵盖STM… · 2026/11/4 3:38:29
云手机技术演进与架构优化实践 1. 云手机技术演进全景图云手机技术从2010年左右的萌芽期发展到今天,已经形成了完整的技术栈体系。早期方案主要依赖ARM服务器直通(如华为鲲鹏ARM服务器Android容器化),现在则演变为异构计算架构下的混合技术路线。这个演进过程本… · 2026/11/4 3:37:29
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
AI辅助技术栈评估:提升研发决策效率60% 1. 项目背景与核心价值技术决策一直是研发团队面临的重大挑战。随着技术生态的快速演进,技术栈的选择、维护和迭代变得愈发复杂。我们团队在过去两年里,先后经历了三次重大技术栈迁移,每次都需要投入大量人力进行技术评估和风险分析。直到去年… · 2026/11/4 0:00:17
从生成Token到直接决策:Jev如何重塑AI Agent工程实践 1. 从"生成文字"到"拍板决策":Jev 到底在解决什么问题大多数人第一次接触大模型,脑子里建立的模型是"输入问题、输出文字"。你问它今天天气怎么样,它给你一段描述;你让它写个周报,它给你… · 2026/11/4 0:00:17
32G统一内存跑大模型:内存账、TPS实测与端云分流 想在一台 32G 统一内存的小主机上把大模型跑成"日常可用",这件事我前后折腾了小半年。标题里说的这台 Mac mini M6,是我们这次讨论的目标平台,具体到手规格以实物为准;但下面这套推算方法和实测思路,对任何统… · 2026/11/4 0:00:17
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46