简介:这份资源是面向工业自动化工程师与WinCC学习者的用户归档实践案例包,聚焦西门子WinCC中动作(Actions)与标准模块(Standard Modules)的协同使用,帮助解决实时数据存储、历史归档与后期分析等实际问题。压缩包共290个文件,约36.21MB,以rpl归档数据文件为主,辅以mdf、ldf数据库文件、dum、sav、ini配置与脚本文件,以及pdl画面、xml、cfg等工程文件,基本覆盖WinCC项目运行与归档所需的各类组件。目前已有1153人学习下载,说明该案例在同类教程中具备一定参考价值。通过案例中的示例项目与脚本代码,读者可了解归档触发条件设置、数据过滤与压缩模块的调用方式,以及归档间隔、保留期限和存储介质的选择思路,从而掌握构建完整用户归档系统的关键环节,提升WinCC项目中的数据管理效率与故障排查能力。
1. 从一次产线数据追溯翻车说起:WinCC 用户归档到底能干什么
去年帮一个做食品灌装线的朋友排查问题,他们的配方参数在 HMI 上被操作工改过,但事后谁也说不清是哪一班、哪个账号、改前改后各是多少。翻遍报警记录只有“参数已修改”四个字,具体数值一个没留。这种场景下,WinCC 自带的用户归档(User Archive)就是干这个的——它把配方、批次、工艺参数这类结构化数据按行存进数据库,支持运行时读写、按条件查询、导出 CSV,还能和趋势控件、报表编辑器联动。和报警归档、变量记录归档不同,用户归档存的是“业务数据”而不是“过程数据”,字段和表结构完全由你定义。适合谁?做产线追溯、配方管理、批次报表的自动化工程师,尤其是那些不想额外上 MES、又需要把关键数据落库的场合。下面这份案例包,我按“建表→组态→脚本读写→排错”拆了一遍,照着走能跑通。
2. 用户归档的组态逻辑:从字段设计到归档变量绑定
2.1 先想清楚表结构,再动手建归档
用户归档在 WinCC 里叫 User Archive,本质是在 SQL Server(或 WinCC 自带的 Sybase 实例)里建一张自定义表。组态入口在 WinCC 项目管理器的“用户归档”节点,右键新建归档,然后定义字段。字段类型支持数值、字符串、日期时间、布尔等,每个字段要指定长度和是否作为主键。我一般会按业务查询维度来设计:比如配方追溯场景,至少要有“批次号(字符串,主键)”“配方名(字符串)”“参数1~N(浮点)”“修改人(字符串)”“修改时间(日期时间)”。这里有个容易忽略的点——字段名不要用中文,虽然编辑器允许,但后续脚本读写和 SQL 查询时容易出编码问题,血泪经验是统一用英文加下划线。
建完字段后,需要把归档和 WinCC 变量关联起来。用户归档本身不直接绑定变量,而是通过“归档变量”做映射。在归档属性里添加变量,指定哪个 WinCC 变量对应哪个字段。这一步决定了你后面是用脚本写还是用变量自动写。如果只是做配方下发,通常用脚本按需写;如果要做连续记录,可以启用“变量触发”方式,当触发变量变化时自动写一行。注意触发方式选“上升沿”还是“电平”,选错了会重复写或者漏写。
2.2 归档变量的读写模式与权限控制
用户归档的读写有两种路径:一种是通过 WinCC 的 User Archive 控件在画面上直接显示和编辑,另一种是通过 C 脚本或 VBS 调用 API。控件方式适合操作工手动录入,脚本方式适合后台自动记录。权限控制要在 WinCC 用户管理里配,给不同角色分配“归档读”“归档写”“归档删除”权限。我见过一个翻车案例:操作工能直接删归档记录,结果把不合格批次的数据删了,追溯直接断链。所以生产环境里,删除权限只给管理员,普通操作工只给读和写。
另外,归档的存储位置要提前规划。默认放在 WinCC 项目目录下的 SQL 实例里,数据量大了会影响项目打开速度。常见做法是定期把历史数据导出到外部数据库或 CSV,然后在归档里做清理。清理策略可以按时间或按记录数,在归档属性里设“最大记录数”或“自动删除旧记录”。但注意,自动删除是不可逆的,如果没做备份,删了就真没了。我一般会先配一个定时脚本,每天凌晨把前一天的数据导出到网络盘,再让归档自动清理。
2.3 用 C 脚本写入一条归档记录的完整流程
下面这段代码是往用户归档里写一行配方修改记录的典型写法。假设归档名是“RecipeLog”,字段有 BatchNo、RecipeName、Param1、ModifiedBy、ModifiedTime。
// WinCC C 脚本:写入用户归档 #include "apdefap.h" void WriteRecipeLog(char* batchNo, char* recipeName, double param1, char* user) { // 打开用户归档,第一个参数是归档名,第二个是打开模式 // UA_OPEN_WRITE 表示以写模式打开 if (UA_OpenArchive("RecipeLog", UA_OPEN_WRITE) == FALSE) { // 打开失败,可能是归档名写错或权限不足 printf("Open archive failed!\r\n"); return; } // 设置字段值,字段名必须和组态时完全一致 UA_SetFieldValue("BatchNo", batchNo); UA_SetFieldValue("RecipeName", recipeName); UA_SetFieldValue("Param1", param1); UA_SetFieldValue("ModifiedBy", user); // 时间字段可以用 WinCC 的时间函数获取当前系统时间 // 这里用 UA_SetFieldValue 直接传字符串,格式要匹配字段类型 char timeStr[32]; sprintf(timeStr, "%04d-%02d-%02d %02d:%02d:%02d", GetYear(), GetMonth(), GetDay(), GetHour(), GetMinute(), GetSecond()); UA_SetFieldValue("ModifiedTime", timeStr); // 提交写入,这一步才真正落库 if (UA_WriteRecord() == FALSE) { printf("Write record failed!\r\n"); } // 关闭归档,释放句柄 UA_CloseArchive(); }逻辑说明:UA_OpenArchive 打开归档后,UA_SetFieldValue 逐个字段赋值,UA_WriteRecord 提交。参数说明:batchNo 是批次号字符串,长度别超过字段定义;param1 是浮点参数,直接传 double;时间字段我习惯用字符串拼接,因为 WinCC 的日期时间字段对格式敏感,用系统函数拼成“YYYY-MM-DD HH:MM:SS”最稳。注意每次写完必须 UA_CloseArchive,否则句柄泄漏,跑几天后归档就打不开了。这个坑我踩过,当时产线跑了三天突然写不进去,重启才恢复,后来查出来就是没关归档。
3. 查询、导出与报表联动:让归档数据真正能用起来
3.1 按条件查询归档记录的脚本写法
写入只是第一步,能查出来才有价值。用户归档的查询用 UA_OpenArchive 配合 UA_SetFilter 和 UA_Query。比如要查某个批次号的所有记录:
// 查询指定批次号的归档记录 void QueryByBatch(char* batchNo) { if (UA_OpenArchive("RecipeLog", UA_OPEN_READ) == FALSE) { printf("Open archive failed!\r\n"); return; } // 设置过滤条件,类似 SQL 的 WHERE // 格式:字段名 操作符 值,字符串值要用单引号 char filter[256]; sprintf(filter, "BatchNo = '%s'", batchNo); UA_SetFilter(filter); // 执行查询,返回记录数 int count = UA_Query(); if (count <= 0) { printf("No record found!\r\n"); UA_CloseArchive(); return; } // 遍历结果集 for (int i = 0; i < count; i++) { // 移动到第 i 条记录 UA_MoveToRecord(i); // 读取字段值 char recipeName[64]; double param1; UA_GetFieldValue("RecipeName", recipeName); UA_GetFieldValue("Param1", ¶m1); printf("Record %d: Recipe=%s, Param1=%.2f\r\n", i, recipeName, param1); } UA_CloseArchive(); }逻辑说明:UA_SetFilter 支持 =、>、<、LIKE 等操作符,字符串值必须用单引号包起来,数值不用。UA_Query 返回记录数,然后 UA_MoveToRecord 定位到某一行,再用 UA_GetFieldValue 取值。参数说明:filter 字符串长度别超过 255,太复杂条件建议拆成多次查询。注意 UA_GetFieldValue 对字符串要传字符数组地址,对数值要传变量地址,类型不匹配会读到乱码。我一般会在查询前先判断归档是否已打开,避免重复打开导致句柄冲突。
3.2 导出 CSV 与报表编辑器的配合
用户归档自带导出功能,在归档控件上右键就有“导出”菜单,但那是手动操作。要做自动导出,还是得用脚本。WinCC 提供了 UA_Export 函数,可以把查询结果直接写成 CSV 文件。常见做法是每天凌晨用全局脚本触发一次导出,文件名带日期,存到指定目录。导出后可以用 Excel 或报表工具做二次分析。
如果要用 WinCC 报表编辑器出正式报表,需要先把用户归档配置成报表的数据源。在报表编辑器里新建一个“用户归档”类型的报表,选择归档名和字段,然后拖到布局里。注意报表里的字段顺序要和归档字段一致,否则会错位。另外报表打印时如果数据量大,建议先查询再打印,不要直接打印整个归档,否则可能卡死。我一般会在报表前面加一个查询按钮,操作工选好批次再打印。
3.3 和趋势控件联动的边界
用户归档的数据能不能上趋势?可以,但有限制。趋势控件原生支持的是变量记录归档,用户归档需要通过脚本把数据读出来再塞给趋势控件,或者用在线表格控件显示。常见做法是用在线表格(Online Trend Control 的表格模式)绑定用户归档,但只能显示,不能缩放和游标联动。如果一定要趋势图,我一般会先把归档数据导出到 CSV,再用外部工具画图,或者用 WinCC 的 User Archive Control 自带的表格视图凑合看。别指望用户归档能像变量归档那样直接拖进趋势控件,这是两个不同的归档体系,硬绑会翻车。
4. 避坑与排查:用户归档最常见的五个翻车点
4.1 归档打不开,报“无法访问数据库”
现象:脚本里 UA_OpenArchive 一直返回 FALSE,画面上的归档控件也显示空白。原因:WinCC 项目复制或迁移后,SQL 实例名变了,或者当前 Windows 用户没有数据库访问权限。解决:先检查 WinCC 项目管理器里“用户归档”节点能否正常打开,如果打不开,用 WinCC 自带的“SQL Server 配置管理器”确认实例运行状态。权限问题的话,把当前用户加到 SQL 的 sysadmin 角色里,或者重新运行 WinCC 的“归档配置”工具修复连接。
4.2 写入成功但查不到记录
现象:UA_WriteRecord 返回 TRUE,但查询时记录数为 0。原因:最常见的是字段类型不匹配,比如往数值字段写了字符串,写入时没报错但实际存进去的是 NULL。另一个原因是查询过滤条件写错了,比如字符串值没加单引号。解决:先用不带过滤条件的 UA_Query 查全部记录,确认数据是否真的写进去了。如果写进去了但过滤查不到,检查过滤字符串的格式。如果是类型问题,在组态里把字段类型和脚本传值类型对齐,数值字段就传数值,别传字符串。
4.3 归档记录数暴涨,项目打开越来越慢
现象:运行几周后,WinCC 项目打开要几分钟,归档控件加载特别慢。原因:用户归档默认不限制记录数,数据一直堆在 SQL 里,项目启动时要加载全部记录。解决:在归档属性里设置“最大记录数”,比如保留最近 10000 条,超出后自动删除最旧的。同时配一个定时导出脚本,每天把数据备份到外部 CSV。注意自动删除是按记录数从旧到新删,不是按时间,所以如果写入频率不均匀,可能删掉的是昨天的数据而保留了上个月的。更稳妥的做法是按时间清理,用脚本定期执行 DELETE 语句。
4.4 多用户同时写入导致冲突
现象:两个操作站同时往同一个归档写记录,偶尔有一边的数据丢失。原因:用户归档的写入不是事务级的,多个客户端同时 UA_OpenArchive 写模式打开时,后打开的会阻塞先打开的。解决:常见做法是加一个全局互斥变量,写之前先申请,写完释放。或者把写入操作集中到一台服务器上,客户端通过变量触发服务器脚本写入。如果产线节拍快,建议用消息队列的方式缓冲,别让多个客户端直接抢归档。
4.5 导出 CSV 中文乱码
现象:UA_Export 导出的 CSV 用 Excel 打开,中文字段全是乱码。原因:WinCC 默认导出编码是 ANSI,而 Excel 默认按 UTF-8 或 GBK 解析,对不上就乱码。解决:导出后不要直接双击打开,用 Excel 的“数据→从文本导入”,手动选编码为 ANSI 或 GBK。或者在脚本里导出后,用外部工具转一次编码。我一般会在导出脚本里加一步,调用系统命令把 CSV 转成 UTF-8 with BOM,这样 Excel 直接打开就不乱码了。
5. 进阶技巧:用全局脚本做归档自动清理与远程备份
5.1 定时清理的脚本实现
自动清理不能只靠归档属性的“最大记录数”,因为那个是按条数删,不按时间。更可控的方式是用全局脚本每天执行一次 DELETE。WinCC 的全局脚本可以调用 SQL 命令,但直接操作数据库有风险,建议用 UA 的 API 逐条删除。下面这段是删除 30 天前记录的思路:
// 删除 30 天前的归档记录 void CleanOldRecords() { if (UA_OpenArchive("RecipeLog", UA_OPEN_READ | UA_OPEN_WRITE) == FALSE) return; // 计算 30 天前的日期字符串 char cutoff[32]; // 这里用 WinCC 时间函数,实际项目中要处理月份天数差异 sprintf(cutoff, "%04d-%02d-%02d 00:00:00", GetYear(), GetMonth(), GetDay() - 30); // 设置过滤条件,查早于 cutoff 的记录 char filter[256]; sprintf(filter, "ModifiedTime < '%s'", cutoff); UA_SetFilter(filter); int count = UA_Query(); if (count > 0) { // 从后往前删,避免索引变化 for (int i = count - 1; i >= 0; i--) { UA_MoveToRecord(i); UA_DeleteRecord(); } } UA_CloseArchive(); }逻辑说明:先按时间过滤查出旧记录,然后从后往前逐条删除。参数说明:cutoff 的计算要小心月份边界,比如 3 月 1 日减 30 天会变成 2 月负数,实际项目中建议用系统时间函数做日期运算,别手动拼。UA_DeleteRecord 删的是当前定位的记录,删完要重新查询确认。注意删除操作不可逆,执行前一定先导出备份。
5.2 远程备份到网络盘
备份脚本的核心是把 CSV 写到网络共享目录。WinCC 运行账户需要有网络盘的写权限,否则会失败。常见做法是在脚本里用文件操作函数打开网络路径,比如\\192.168.1.100\backup\recipe_20250101.csv。注意路径要用双反斜杠,而且运行账户要提前映射好网络驱动器,或者在脚本里用完整 UNC 路径。如果网络不通,脚本会阻塞,建议加超时判断。我一般会在备份前先写一个测试文件,确认网络盘可写再导正式数据。
5.3 验证归档是否正常工作的三个检查点
第一,看归档控件的记录数是否在增长,如果一直不变,说明写入没成功。第二,用 UA_Query 查最近一条记录的时间戳,确认和当前时间差不超过一个采集周期。第三,定期打开 SQL Server 管理工具,直接查归档表的数据量,和 WinCC 界面对比。这三个检查点我每次交付前都会走一遍,尤其是第三个,能发现界面显示正常但底层没落库的玄学问题。从那以后我每次配完用户归档,都强制走一遍“写入→查询→导出→清理”全流程,确认每个环节都通再交给产线。希望帮到你。
本文还有配套的精品资源,点击获取