news 2026/10/1 6:10:23

WinCC用户归档实战:从配方丢失到批次追溯的完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WinCC用户归档实战:从配方丢失到批次追溯的完整指南

简介:这份资源是面向工业自动化工程师与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 读写用户归档:从写入配方到下发 PLC

3.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 = Nothing

VBS 的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 = "Provider=SQLOLEDB;Data Source=.\WINCC;Initial Catalog=CC_ProjectName;Integrated Security=SSPI;" 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_ArchiveName,V7.4 之后变成了ArchiveName,没有UA_前缀。升级项目的时候要改 SQL 语句。

最后说一个我自己的习惯:每次组态完用户归档,先手动写一条测试记录,然后用 VBS 的Query查出来,确认字段类型和值都对,再写正式的读写脚本。这个顺序能省掉很多“写入成功但读出来是空”的排查时间。用户归档这个功能,组态不复杂,坑都在细节里,把字段定义、句柄释放、类型匹配这三件事盯住,基本就不会翻车。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 6:08:43

Linux进程通信IPC实战:管道、信号与消息队列选型指南

1. 项目概述&#xff1a;为什么进程通信是Linux系统里最常被低估的“底层呼吸”你有没有遇到过这样的场景&#xff1a;写了个Python脚本监控日志&#xff0c;想让它一发现错误就立刻通知另一个告警服务&#xff1b;或者在嵌入式设备上&#xff0c;主控程序要实时把传感器数据传…

作者头像 李华
网站建设 2026/10/1 6:08:43

甲状腺结节超声图像分类数据集实战:多类别识别与模型训练

简介&#xff1a;面向甲状腺结节分类任务的专业医疗AI数据集&#xff0c;适合计算机视觉与医疗健康方向的研究者、竞赛团队及原型开发使用。数据来源于真实临床场景&#xff0c;覆盖广泛年龄层与病理特征&#xff0c;包含结节性甲状腺肿与正常两类&#xff0c;全部图像经专业医…

作者头像 李华
网站建设 2026/10/1 6:07:11

AI短剧生成平台实战:从脚本到成片的pipeline搭建与调优

简介&#xff1a;面向AI视频创作者与短剧开发者的全栈源码包&#xff0c;解决从一句话创意到成片输出的完整短剧制作难题。基于大语言模型解析剧本并自动提取角色、场景与分镜&#xff0c;配合AI绘图生成角色形象和场景背景&#xff0c;再通过图生视频、TTS配音与FFmpeg合成&am…

作者头像 李华
网站建设 2026/10/1 6:07:11

AI工程从零搭建:RAG应用与工程化实践完整指南

ai-engineering-from-scratch 这个项目名&#xff0c;乍一看像是某个 GitHub 上的学习清单&#xff0c;点进去无非是资源链接的堆叠。但我在把整条学习路径完整走了一遍之后想说的是&#xff1a;从零开始做 AI 工程&#xff0c;真正难的不是“没有资料”&#xff0c;而是“每一…

作者头像 李华
网站建设 2026/10/1 6:07:11

谷歌ARTEMIS:大模型驱动的移动端AI自动化框架详解与实战

如果你最近在刷 AI Agent 方向的内容&#xff0c;ARTEMIS 这个名字应该早就不陌生了。谷歌开源的移动端 AI 自动化框架&#xff0c;主打让 AI 助手像人一样操作手机。我把它从仓库里拉下来、跑通、又折腾了几个小任务之后&#xff0c;最大的感受是&#xff1a;这玩意的思路和传…

作者头像 李华
网站建设 2026/10/1 6:05:45

24GB显存塞进4路32K上下文:KV Cache与量化实战指南

前阵子帮团队把一套基于 8B 开源模型的服务化推理部署到一张 RTX 4090 上&#xff0c;显存就 24 GiB&#xff0c;任务要求说起来很简单&#xff1a;模型权重得装进去&#xff0c;同时还要服务 4 路并发请求&#xff0c;每一路都完整支持 32K 上下文。我一开始觉得这配置很宽裕—…

作者头像 李华