
简介这份资源是面向工业自动化工程师与WinCC学习者的用户归档实践案例包聚焦西门子WinCC中动作Actions与标准模块Standard Modules的协同使用帮助解决实时数据存储、历史数据检索与归档策略设计等实际问题适合具备一定WinCC基础、希望深入掌握数据管理的中级用户。压缩包为rar格式共290个文件、约36.21MB以250个rpl归档数据文件为主体辅以mdf、ldf数据库文件、dum转储文件、sav与ini配置、pdl画面、xml与cfg配置及少量脚本与说明文件完整还原了一个可参考的项目结构。目前已有1149人学习下载。通过案例中的示例项目与脚本代码读者可理解如何设置归档触发条件、编写模块逻辑、配置归档间隔与保留期限并掌握数据过滤、转换、压缩等标准模块的调用方式从而提升WinCC项目的数据归档效率与故障排查能力。1. WinCC 用户归档到底解决什么问题从一次配方丢失事故说起产线凌晨三点停机操作工换了个配方HMI 上显示写入成功PLC 那边却还是老参数。第二天查原因发现配方数据存在 WinCC 的画面变量里画面一切换、项目一激活变量归零配方自然就没了。这种事故在食品、制药、锂电这些多配方切换的行业里太常见了根子就在于没把配方数据落到数据库里。WinCC 用户归档User Archive就是干这个的它把配方、批次记录、工艺参数这类需要长期保存、按条件查询、能导出报表的数据从易失的画面变量里搬进 SQL Server 的独立数据表由 WinCC 统一管理读写。适合谁用做配方管理、批次追溯、SPC 数据采集的自动化工程师尤其是那些被“配方丢失”“历史数据查不到”折磨过的人。这一章先把用户归档的定位讲清楚后面几章再拆组态步骤、脚本读写和踩坑记录。2. 用户归档和变量归档、报警记录的本质区别选错了后面全是坑2.1 三种归档机制各自管什么数据WinCC 里带“归档”字样的东西有三个变量归档Tag Logging、报警记录Alarm Logging、用户归档User Archive。名字像用途完全不同。变量归档是周期性地把过程值存成时间序列适合画趋势曲线比如反应釜温度每 500ms 采一个点。报警记录存的是报警消息的到达、确认、离开状态带时间戳和消息文本。用户归档不一样它存的是“记录”一条记录包含多个字段字段类型可以是数值、字符串、日期而且这些记录可以被操作员在画面上增删改查。配方就是典型场景一条配方记录里有温度设定值、压力设定值、搅拌时间、配方名称操作员选中某条配方点“下发”PLC 收到全部参数。变量归档做不到这个它只能存单个变量的时间序列没有“记录”的概念也没法在画面上编辑。选型逻辑很简单需要按记录读写、需要操作员编辑、需要导出成表格给质量部门看的用用户归档只需要看趋势的用变量归档只需要记录报警的用报警记录。我见过有人拿变量归档做配方把十个参数当成十个变量分别归档下发的时候按时间戳去对齐结果时间戳差了几毫秒配方就错位了。这种方案不是不能用是维护成本太高后面加一个参数就要改一堆脚本。2.2 用户归档的底层存储结构用户归档在 SQL Server 里对应两张表一张是归档表本身存实际数据另一张是字段定义表存字段名、类型、长度这些元数据。WinCC 通过 UA 控件和脚本函数访问这两张表不直接暴露 SQL 连接给用户。这意味着你不能用外部程序直接往用户归档表里插数据必须走 WinCC 提供的接口。这个限制有人觉得麻烦但换个角度想它保证了数据一致性——字段类型、长度、索引都由 WinCC 维护不会因为外部程序写错类型导致归档损坏。每条用户归档记录有一个隐藏的 ID 字段自增唯一。这个 ID 在脚本里用UA_GetFieldValue读不到但可以用UA_GetRecordID拿到。做批次追溯的时候这个 ID 就是批次号的最佳载体比用时间戳靠谱因为同一秒内可能有多条记录。2.3 组态一个最小可用的用户归档在 WinCC 项目管理器里右键“用户归档”新建一个归档命名比如Recipe_Archive。然后定义字段至少要有RecipeName字符串32 字符、TempSet浮点对应温度设定、PressureSet浮点、MixTime整数秒。字段定义完之后在画面上放一个 UA 控件User Archive Control绑定这个归档。控件会自动生成表格视图操作员可以直接在表格里编辑。组态阶段最容易忽略的是字段的“允许为空”属性。如果某个字段不允许为空但脚本写入时没赋值WinCC 会报错而且报错信息很模糊只说“写入失败”不告诉你哪个字段的问题。我一般把所有字段都设成允许为空然后在脚本里做校验这样出错时能自己控制提示信息。提示用户归档的字段名不要用中文虽然 WinCC 界面支持中文但脚本函数里传中文字段名在某些版本上会乱码尤其是 V7.3 和 V7.4 混用的时候。3. 用 C 脚本和 VBS 读写用户归档从写入配方到下发 PLC3.1 写入一条配方记录UA_Write 的完整参数WinCC 提供两组函数C 脚本的UA_Write和 VBS 的HMIRuntime.UserArchive.Write。C 脚本性能更好适合高频写入VBS 可读性好适合逻辑复杂的场景。先看 C 脚本写入一条配方// 在按钮的鼠标点击事件里执行 // 假设归档名为 Recipe_Archive字段为 RecipeName, TempSet, PressureSet, MixTime #include apdefap.h void OnClick(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName) { // 第一步创建一条新记录返回记录句柄 // 参数1归档名参数2保留传0 long lHandle UA_CreateRecord(Recipe_Archive, 0); if (lHandle 0) { // 创建失败可能是归档不存在或权限问题 printf(创建记录失败\r\n); return; } // 第二步给字段赋值 // 参数记录句柄字段名值 UA_SetFieldValue(lHandle, RecipeName, 配方A); UA_SetFieldValue(lHandle, TempSet, 185.5); UA_SetFieldValue(lHandle, PressureSet, 0.6); UA_SetFieldValue(lHandle, MixTime, 300); // 第三步提交记录写入数据库 // 参数记录句柄0 表示立即写入 if (UA_Write(lHandle, 0) FALSE) { printf(写入归档失败\r\n); } // 第四步释放句柄避免内存泄漏 UA_ReleaseRecord(lHandle); }这段代码的逻辑是“创建-赋值-提交-释放”四步。关键参数说明UA_CreateRecord的第二个参数在大多数场景下传 0它保留给未来的扩展UA_Write的第二个参数控制写入模式传 0 是同步写入传 1 是异步写入。同步写入会阻塞脚本直到数据库返回配方数据量小的时候没问题但如果一次写几百条记录画面会卡。异步写入不阻塞但错误处理更麻烦因为函数返回时数据还没落盘。UA_ReleaseRecord必须调用否则每次写入都会泄漏一个句柄写几百次之后 WinCC 会报“资源不足”。这个坑我踩过当时以为是数据库满了查了半天才发现是句柄没释放。3.2 从归档读取配方并下发到 PLC读取比写入简单但要注意字段类型匹配。假设画面上有一个 UA 控件操作员选中某一行点“下发”按钮脚本要拿到选中行的记录 ID然后读字段值再写到 PLC 变量void OnClick(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName) { // 获取 UA 控件当前选中的记录 ID // 控件名假设为 Control1属性 SelectedRecordID long lRecordID GetPropWord(lpszPictureName, Control1, SelectedRecordID); if (lRecordID 0) { printf(请先选中一条配方\r\n); return; } // 根据记录 ID 打开记录 long lHandle UA_OpenRecord(Recipe_Archive, lRecordID); if (lHandle 0) { printf(打开记录失败\r\n); return; } // 读取字段值注意类型转换 char szName[33]; double dTemp, dPressure; int iMixTime; UA_GetFieldValue(lHandle, RecipeName, szName, 32); UA_GetFieldValue(lHandle, TempSet, dTemp); UA_GetFieldValue(lHandle, PressureSet, dPressure); UA_GetFieldValue(lHandle, MixTime, iMixTime); // 写入 PLC 变量 SetTagDouble(PLC_TempSet, dTemp); SetTagDouble(PLC_PressureSet, dPressure); SetTagLong(PLC_MixTime, iMixTime); UA_ReleaseRecord(lHandle); }这里有个细节UA_GetFieldValue对字符串字段需要传入缓冲区指针和缓冲区长度对数值字段传入变量地址。如果字段类型是浮点但传了int*读出来的值是错的而且不报错。我一般会在组态时把字段类型和 PLC 变量类型列一张对照表写脚本时对着查。3.3 VBS 版本适合做条件查询和批量导出C 脚本适合单条读写但如果要做“查询所有温度大于 180 的配方”或者“导出最近一周的批次记录”VBS 更方便因为可以直接用 SQL 语法。WinCC 的 VBS 接口支持Query方法 在按钮点击事件里执行 Dim ua, rs Set ua HMIRuntime.UserArchive(Recipe_Archive) 查询温度设定大于 180 的配方按配方名排序 Set rs ua.Query(SELECT * FROM Recipe_Archive WHERE TempSet 180 ORDER BY RecipeName) If rs Is Nothing Then HMIRuntime.Trace 查询失败 vbCrLf Exit Sub End If 遍历结果集 Do While Not rs.EOF HMIRuntime.Trace 配方名 rs.Fields(RecipeName).Value _ 温度 rs.Fields(TempSet).Value vbCrLf rs.MoveNext Loop rs.Close Set rs Nothing Set ua NothingVBS 的Query方法底层是 SQL Server 的查询所以支持 WHERE、ORDER BY、GROUP BY 这些子句。但要注意字段名必须和组态时定义的一模一样大小写敏感。另外Query返回的是只读结果集不能通过它修改数据修改还是要用Write方法。注意VBS 的Query在 WinCC V7.3 和 V7.4 上行为有差异。V7.3 返回的Fields集合里数值字段的类型是Variant直接参与算术运算没问题V7.4 返回的是Double但字符串字段多了一个尾部空格。如果做字符串比较记得用Trim。4. 用户归档避坑记录五个让项目翻车的细节4.1 归档名和字段名用了中文或特殊字符现象脚本里UA_CreateRecord(配方归档, 0)返回 0写入失败但 WinCC 日志里没有任何错误信息。原因用户归档的归档名和字段名在底层是 SQL Server 的表名和列名SQL Server 对中文标识符的支持取决于排序规则而且 WinCC 的脚本引擎在传递中文参数时可能编码不一致。解决归档名和字段名一律用英文加下划线比如Recipe_Archive、Temp_Set。如果画面需要显示中文在 UA 控件的列标题里单独设置不要改字段名。4.2 忘记释放记录句柄导致内存泄漏现象项目运行几天后WinCC 画面切换变慢最后报“无法创建记录”。重启 WinCC 后恢复正常但过几天又出现。原因每次UA_CreateRecord或UA_OpenRecord都会占用一个句柄不调用UA_ReleaseRecord就不会释放。WinCC 的句柄池有上限默认是 1024 个。解决在脚本里用try...finally结构C 脚本没有 finally用goto或者把释放放在所有 return 之前。VBS 里可以用On Error Resume Next配合If Not rs Is Nothing Then rs.Close。4.3 字段类型和 PLC 变量类型不匹配现象配方下发后PLC 收到的温度值是 0 或者一个巨大的数。原因用户归档字段定义的是Float但脚本里用UA_GetFieldValue读到了Double变量里然后SetTagDouble写 PLC。如果 PLC 那边是Int类型转换时可能溢出。更隐蔽的是字段定义的是Signed Int但脚本传了浮点数进去WinCC 会截断小数部分不报错。解决组态时列一张表把用户归档字段类型、脚本变量类型、PLC 变量类型三列对齐。每次改字段定义同步改脚本和 PLC 程序。4.4 UA 控件的 SelectedRecordID 在画面切换后归零现象操作员选中一条配方切到另一个画面再切回来再点“下发”脚本报“请先选中一条配方”。原因UA 控件的SelectedRecordID属性是运行时属性画面切换时控件重新初始化选中状态丢失。解决在画面打开事件里用SetPropWord恢复上次选中的记录 ID或者把选中的 ID 存到内部变量里下发时从变量读不依赖控件的选中状态。4.5 归档数据量大了之后查询变慢现象用户归档里存了十万条记录UA 控件加载要十几秒VBS 查询超时。原因用户归档默认没有为所有字段建索引Query走的是全表扫描。而且 UA 控件加载时会一次性拉取所有记录到内存。解决在 SQL Server 里给常用查询字段手动建非聚集索引比如RecipeName和CreateTime。UA 控件设置MaxRecords属性限制显示条数比如 1000 条配合查询条件过滤。如果数据量持续增长定期把旧记录导出到外部数据库然后从用户归档里删除。5. 用用户归档做批次追溯报表一个可复用的导出模板批次追溯的核心需求是给定一个批次号查出这个批次用了哪条配方、每个工艺参数的实际值是多少、操作员是谁、什么时候下的发。用户归档本身只存了配方设定值实际值在变量归档里操作员信息在报警记录或者自定义的登录表里。所以完整的追溯报表需要跨三个数据源查询。我一般这样做在用户归档里加三个字段——BatchID字符串批次号、Operator字符串操作员名、CreateTime日期时间自动填充。写入配方的时候同时把当前登录用户和批次号写进去。然后写一个 VBS 脚本用 ADO 直接连 WinCC 的 SQL Server 数据库把用户归档表、变量归档表、报警记录表 JOIN 起来输出 CSV 文件。 导出批次追溯报表 参数批次号 Sub ExportBatchReport(batchID) Dim conn, rs, sql, fso, file, ts Set conn CreateObject(ADODB.Connection) Set fso CreateObject(Scripting.FileSystemObject) 连接 WinCC 的 SQL Server 数据库 注意数据库名通常是项目名需要根据实际情况修改 conn.ConnectionString ProviderSQLOLEDB;Data Source.\WINCC;Initial CatalogCC_ProjectName;Integrated SecuritySSPI; conn.Open 查询用户归档里的配方设定值 sql SELECT RecipeName, TempSet, PressureSet, MixTime, Operator, CreateTime _ FROM Recipe_Archive WHERE BatchID batchID Set rs conn.Execute(sql) 写入 CSV 文件 Set file fso.CreateTextFile(D:\Reports\ batchID .csv, True) file.WriteLine 配方名,温度设定,压力设定,搅拌时间,操作员,创建时间 Do While Not rs.EOF file.WriteLine rs.Fields(RecipeName).Value , _ rs.Fields(TempSet).Value , _ rs.Fields(PressureSet).Value , _ rs.Fields(MixTime).Value , _ rs.Fields(Operator).Value , _ rs.Fields(CreateTime).Value rs.MoveNext Loop file.Close rs.Close conn.Close Set file Nothing Set rs Nothing Set conn Nothing End Sub这个模板的关键点连接字符串里的Data Source是.\WINCC这是 WinCC 安装时默认的 SQL Server 实例名如果项目用的是远程数据库要改成对应的 IP 和实例名。Initial Catalog是项目名WinCC 会为每个项目建一个独立的数据库名字就是项目名。查询语句里的表名Recipe_Archive就是用户归档的归档名字段名和组态时一致。导出 CSV 之后可以用 Excel 做数据透视表或者导入到 MES 系统里。如果要做成自动报表把这个 Sub 挂到 WinCC 的定时器上每天凌晨跑一次导出前一天的所有批次。提示直接连 SQL Server 查询用户归档表是可行的但要注意 WinCC 的数据库结构在不同版本间有差异。V7.3 的用户归档表名是UA_ArchiveNameV7.4 之后变成了ArchiveName没有UA_前缀。升级项目的时候要改 SQL 语句。最后说一个我自己的习惯每次组态完用户归档先手动写一条测试记录然后用 VBS 的Query查出来确认字段类型和值都对再写正式的读写脚本。这个顺序能省掉很多“写入成功但读出来是空”的排查时间。用户归档这个功能组态不复杂坑都在细节里把字段定义、句柄释放、类型匹配这三件事盯住基本就不会翻车。希望帮到你。本文还有配套的精品资源点击获取