拓冰建站拓冰建站
首页 / 资讯中心 / 正文

WinCC C脚本执行边界与报警/画面/报表函数选型

简介这是一份面向工控自动化工程师与WinCC组态开发人员的C脚本函数速查资料针对WinCC脚本编写中函数名繁多、参数含义易混淆、缺乏中文参考等问题帮助读者快速定位标准函数的用途与调用方式。整包仅1个pdf文件约58KB篇幅精炼便于随查随用内容以WinCC C脚本标准函数库为主线按Alarm报警控件等函数组分类整理逐条给出函数原型、功能简述与典型调用示例例如AcknowledgeMessage用于按消息号确认报警、AXC_OnBtnMsgFirst用于跳转报警控件首条消息并说明lpszPictureName、pszMsgWin等参数对应的画面名与控件对象名。读者可据此掌握报警控件工具栏定制、消息确认与归档切换、蜂鸣器确认、消息锁定与解锁、报警回路触发等常见操作的脚本写法减少查阅英文手册的时间适合作为日常开发中的案头工具书也便于排查脚本参数填写错误。目前已有2411人学习下载。1. WinCC C 脚本不是 VBS 的备胎它的执行边界决定选型现场遇到过这样的画面报警控件上的“首条”“末条”“组确认”按钮用 VBS 写出来只能去点控件内部接口绕一大圈还不稳定换成 C 脚本一行AXC_OnBtnMsgFirst(gs_alarm_00,Control1)就完事。这不是 VBS 不行而是 C 脚本在 WinCC 里握着一条 VBS 拿不到的通路——报警控件、表格控件、打印作业这些对象的内部 API 只对 C 开放。所以选型判断其实很朴素凡是涉及报警控件的行选择、过滤、确认、锁定、打印涉及 WinCC 表格/趋势控件的行列信息读取涉及打印作业的启动与错误回读走 C纯粹的变量读写、画面切换、控件属性赋值VBS 写起来更省心。而 C 脚本真正的门槛不在语法在于你得先把函数分成 Alarm、Graphics、Report、TagLog 这几组记住每组的参数约定尤其是“画面名 控件对象名”这两个位置参数几乎贯穿全部按钮函数。搞混一次函数就静默返回 FALSE。2. Alarm 组报警控件按钮函数与 AXC_SetFilter 过滤结构Alarm 组是整份文档里体量最大的一组二十多个函数命名几乎都是AXC_开头。它们的共同特征是同一种签名两个char*参数第一个是承载报警控件的画面名第二个是控件在该画面里的对象名。把这两个参数理解成“定位一个控件实例的坐标”后面所有按钮函数就都顺了。2.1 AXC_ 按钮函数的统一签名与调用约定看一组常见的按钮动作映射它们做的事和工具栏上那个按钮完全一致函数动作典型调用AXC_OnBtnMsgFirst跳到消息列表第一条AXC_OnBtnMsgFirst(gs_alarm_00,Control1)AXC_OnBtnMsgLast跳到消息列表最后一条AXC_OnBtnMsgLast(gs_alarm_00,Control1)AXC_OnBtnMsgNext下一条消息AXC_OnBtnMsgNext(gs_alarm_00,Control1)AXC_OnBtnMsgPrev上一条消息AXC_OnBtnMsgPrev(gs_alarm_00,Control1)AXC_OnBtnSinglAckn确认当前选中消息AXC_OnBtnSinglAckn(gs_alarm_00,Control1)AXC_OnBtnVisibleAckn组确认可见消息AXC_OnBtnVisibleAckn(gs_alarm_00,Control1)AXC_OnBtnHornAckn确认蜂鸣器信号AXC_OnBtnHornAckn(gs_alarm_00,Control1)AXC_OnBtnComment显示所选消息注释AXC_OnBtnComment(gs_alarm_00,Control1)AXC_OnBtnLock打开设置锁定对话框AXC_OnBtnLock(gs_alarm_00,Control1)AXC_OnBtnScroll激活/取消滚动AXC_OnBtnScroll(gs_alarm_00,Control1)// 标准写法先接返回值再决定是否提示 // 参数一组态了报警控件的画面名注意不带 .pdl 扩展名 // 参数二报警控件在画面里的对象名属性面板里能看到 BOOL bRet AXC_OnBtnMsgFirst(gs_alarm_00, Control1); if (!bRet) { // 返回 FALSE 的绝大多数原因是画面名或对象名写错 // 也可能是当前画面根本没打开控件实例不存在 SetTagBit(bAlarmNavFail, 1); }需要提醒的是AXC_OnBtnScroll是切换语义而不是单向动作按钮第一次按下开启滚动再按一次关闭。如果你在按钮的“按下”和“释放”两个事件里都挂了它等于按一次触发两回滚动看起来毫无反应。2.2 AcknowledgeMessage由变量驱动的确认按钮函数是给人点的AcknowledgeMessage是给逻辑用的。它只吃一个DWORD参数——消息编号返回值是void所以无法从返回值判断成没成功。// 从内部变量读出上位机算好的待确认消息号 // U08i_MsgNr 是 Word 类型GetTagWord 取回后隐式转 DWORD DWORD dwNr GetTagWord(U08i_MsgNr); // 确认该编号对应的那条消息 AcknowledgeMessage(dwNr);这里的变量类型要对齐。如果消息号超过 65535用 Word 就截断了应该换GetTagDWord。另外这个函数不做可见性判断消息即使已经被过滤掉、不在当前窗口里编号对得上照样确认成功。要做“只确认当前可见”的语义还是得回到AXC_OnBtnVisibleAckn。2.3 MSG_FILTER_STRUCT 的过滤器布局AXC_SetFilter是这组里唯一需要搭结构体的函数签名是画面名 对象名 LPMSG_FILTER_STRUCTLPCMN_ERROR。结构体字段多但真正要动的就三块过滤器名、dwFilter位掩码、掩码对应的数据域。BOOL ret; MSG_FILTER_STRUCT Filter; CMN_ERROR Error; // 必须清零。结构体里混着上一次的脏数据会导致过滤结果莫名其妙 memset(Filter, 0, sizeof(MSG_FILTER_STRUCT)); // 过滤器名称用于标识这一套过滤设置 strcpy(Filter.szFilterName, Control1); // dwFilter 是位掩码把要启用的条件按位或起来 Filter.dwFilter MSG_FILTER_NR_FROM | MSG_FILTER_NR_TO; // 只筛消息号 2 这一条起止号填成一样 Filter.dwMsgNr[0] 2; // 起始消息号 Filter.dwMsgNr[1] 2; // 结束消息号 // 第四个参数是错误结构失败时可以从里面读错误码和错误文本 ret AXC_SetFilter(gs_alarm_00, Control1, Filter, Error);dwFilter的可选常数和它们要求你填的域是一一对应的对不上的条件会被忽略掩码常数需要填充的域含义MSG_FILTER_DATE_FROM/_TOst[0]/st[1]日期起止MSG_FILTER_TIME_FROM/_TOst[0]/st[1]时间起止MSG_FILTER_NR_FROM/_TOdwMsgNr[0]/[1]消息号区间MSG_FILTER_CLASSdwMsgClass消息等级位码MSG_FILTER_STATEdwMsgState消息状态位码MSG_FILTER_AG_FROM/_TOwAGNr[0]/[1]AG 号区间MSG_FILTER_AGSUB_FROM/_TOwAGSubNr[0]/[1]AG 子号区间MSG_FILTER_TEXTszTB/dwTB文本块内容与激活位码MSG_FILTER_PVALUEdPValue/dwPValue过程值区间与激活位码MSG_FILTER_COUNTER_FROM/_TOdwMsgCounter[0]/[1]内部消息计数器区间日期时间用的是SYSTEMTIME。文档里特意点了一句要取当前时间用GetLocalTime别用GetSystemTime两者差着一个时区。做“最近 8 小时”这类过滤时用错函数会把窗口整体平移掉几个小时现象是消息明明在就是筛不出来。清空过滤器的方式是把结构体清零后直接传进去不放任何条件memset(Filter, 0, sizeof(MSG_FILTER_STRUCT)); // 结构体全 0 且 dwFilter 为 0等于取消全部过滤条件 AXC_SetFilter(gs_alarm_00, Control1, Filter, Error);2.4 过滤器排错时看什么过滤不生效按这个顺序查dwFilter掩码里有没有把对应条件或进去需要填的域是不是只填了一半比如只写了 FROM 没写 TO过滤器名和控件对象名是不是同一套memset有没有漏掉。还有一个隐蔽点dwQuickSelect这类为扩展保留的字段必须保持 0随手填值会让整个过滤器行为异常。3. Graphics 组画面层级与变量链接的字符串取用Graphics 组解决的是另一类问题——脚本运行的时候我怎么知道当前在哪个画面、属性上挂了什么变量、上一层画面窗口叫什么。这类信息在组态阶段是静态的在运行阶段只能靠函数反查。3.1 GetLinkedVariable 反查属性上的变量名签名是画面名 对象名 属性名返回一个char*。它不返回变量值返回的是变量名本身。char* pszVarName NULL; char szVarName[_MAX_PATH 1]; // 查“静态文本 6”的“可见性”属性上挂了哪个变量 // 属性名要和属性面板里一致字符串大小写敏感 pszVarName GetLinkedVariable(gs_stand_graph_00, Static Text 6, Visible); // 返回 NULL 说明该属性是静态设置的没有链接变量这一步必须判空 if (pszVarName ! NULL) { // 把返回指针的内容拷进自己的缓冲区最多 _MAX_PATH 个字符 strncpy(szVarName, pszVarName, _MAX_PATH); szVarName[_MAX_PATH] \0; // strncpy 不保证补终止符手动收尾 }返回的指针指向 WinCC 内部缓冲区不要长期持有也不要试图去改它指向的内容。拷进自己的数组再处理是稳妥做法。属性名这一项比想象中灵活几何属性里的X、Y、Width、Visible、BackColor都可以传想知道某个动态对话框挂在哪个变量上时这个函数比翻组态快得多。3.2 画面层级三兄弟GetLocalPicture、GetParentPicture、GetParentPictureWindow经常被混用区别在返回的是画面还是画面窗口// 当前脚本所在画面的名称不带 .pdl char* pszLocal GetLocalPicture(lpszPictureName); // 上一层画面的名称。画面窗口里嵌画面时才有意义 char* pszParent GetParentPicture(lpszPictureName); // 上一层“画面窗口”对象的名称注意返回的不是画面 char* pszParentWin GetParentPictureWindow(lpszPictureName);层级关系用文档里的例子最清楚Picture_1上有画面窗口Picture Window_1里面调Picture_2Picture_2上有Picture Window_2里面调Picture_3。调用位置GetParentPictureGetParentPictureWindow从Picture_3调用Picture_2Picture Window_2从Picture_2调用Picture_1Picture Window_1从Picture_1调用无上层返回需判空无上层返回需判空这个区分在做弹窗时非常关键。弹窗关一次就打不开、或者关掉的是整个主画面十有八九是把画面窗口名当成画面名传给了OpenPicture或者关闭函数。OpenPicture接的是画面名想操作那个窗口得先用GetParentPictureWindow拿到窗口名。3.3 嵌套调用时的字符串缓冲区三个反查函数都返回指针都建议拷进_MAX_PATH 1的数组。_MAX_PATH的长度在不同系统上不一致写死 260 在多数场合能用但用宏更稳。拷贝时记得手动补终止符strncpy在源串长度等于或超过目标容量时不会补\0后续拼接或比较就会越界读到栈上的垃圾。char szPic[_MAX_PATH 1]; char* p GetParentPicture(lpszPictureName); if (p ! NULL) { // 留一个字节给终止符避免 strncpy 不补 \0 的坑 strncpy(szPic, p, _MAX_PATH - 1); szPic[_MAX_PATH - 1] \0; }Registry2是这一组里比较特殊的一个用来维护一对字符串的列表适合做轻量的状态寄存。它不落盘重启就没了别拿它当持久化存储用。4. Report 与 TagLog打印作业与表格控件行列读取做报表导出和打印的时候C 脚本的作用是把已经组态好的打印作业拉起来而不是自己排版。这也是 WinCC 报表教程里绕不开的一段作业名字对不上预览窗口一闪而过。4.1 ReportJob、RPTJobPreview 与 RPTJobPrint三个入口用途分工明确函数参数行为ReportJob作业名 方法名按方法名决定预览还是打印RPTJobPreview作业名只启动预览RPTJobPrint作业名直接送打印输出// 启动指定打印作业的预览窗口 // 参数是打印作业在报表编辑器里定义好的名称区分大小写 RPTJobPreview(Text library (compact)); // 送到打印机 RPTJobPrint(Text library (compact));作业名里带前缀的是系统内置作业自定义作业直接用你起的名字。名字写错不会崩溃函数返回 FALSE预览窗口根本不出来——这种现象在现场常被误判成打印机驱动问题其实先查作业名更快。4.2 CMN_ERROR 与 RptShowErrorRptShowError接收失败函数名和一个CMN_ERRORA*把错误描述取出来。它是RPTJobPrint和RPTJobPreview的内部组成通常不需要单独调用但你自己封装打印逻辑时可以拿它做提示。// 结构体里 dwError1..dwError5 按 API 约定存放错误码 // 没有特别约定时错误码就在 dwError1 // szErrorText 是随语言变化的错误文本缓冲区 typedef struct CMNERRORSTRUCT { DWORD dwError1, dwError2, dwError3, dwError4, dwError5; TCHAR szErrorText[MAX_ERROR_LEN]; } CMNERROR;判断打印失败时优先看dwError1需要给人看的描述再取szErrorText。这两者不总是一致文本依赖资源文件的语言版本同一个错误码在不同语言包下文案不同做日志时只记错误码更稳。4.3 TagLog表格控件的行列数读取TagLog 组里可用性最高的是三个计数函数用来把在线表格控件的规模读出来。趋势控件那边多数人习惯用 VBS 调属性但行列统计这类需要走控件内部接口的用 C 更直接。char text[16]; long int columns; long int rows; // 传控件对象名即可不需要画面名 columns TlgGetNumberOfColumns(TableControl_01); rows TlgGetNumberOfRows(TableControl_01); // long 转字符串注意缓冲区留足 sprintf(text, %d, (int)columns); // 把结果写到画面的静态文本上 SetText(lpszPictureName, Static Text 5, text);sprintf的格式串里用%d配int更保险long在 64 位环境下可能被解释成 32 位值本身不大时看不出问题数据量大起来就会显示成负数。缓冲区给 16 字节够放五位数的列数和终止符别用char text[5]这种刚好卡住的写法。TlgGetColumnPosition返回列指针当前位置对应的列索引用处是做“当前停在第几列”的显示。它和上面两个计数函数一样参数都是控件对象名传错时返回 0 或者 -1要做区分就得结合当前实际列数一起看。4.4 行列统计脚本的触发时机这三个函数取的是运行时快照放在画面打开事件里只跑一次表格还没填充完就读到 0。常规做法是挂在表格控件的刷新事件或者一个周期触发里配合一个判断避免重复写值// 只在行数发生变化时更新显示减少不必要的画面重绘 static long int lastRows -1; long int rows TlgGetNumberOfRows(TableControl_01); if (rows ! lastRows) { char text[16]; sprintf(text, %d, (int)rows); SetText(lpszPictureName, Static Text 5, text); lastRows rows; }注意静态变量在 C 脚本里的生命周期和脚本触发方式有关周期触发同一次执行上下文里它才保留值。跨触发方式的持久状态还是老老实实用内部变量。5. 循环脚本触发、标准函数被覆盖与运行验证最后落到两个最容易翻车的地方函数改了丢了以及脚本到底有没有在跑。5.1 标准函数被安装覆盖的规避文档里那句提醒分量很重——WinCC 安装会重写已修改的标准函数改过的内容直接丢失。所以别在标准函数本体上改代码。正确姿势是在项目函数里写自己的实现标准函数里只留一行调用或者干脆完全不碰标准函数把它当成只读的参考实现。// 不要改这个标准函数本体新建一个项目函数 MyAckAll 承接逻辑 // 标准函数只保留转发安装覆盖后损失也只是一行 void MyAckAll(char* pszPicture, char* pszControl) { AXC_OnBtnVisibleAckn(pszPicture, pszControl); }自定义工具栏的按钮事件里挂MyAckAll升级或修复安装之后重新补一行转发就恢复了逻辑主体在项目函数里始终安全。5.2 循环脚本的触发方式选择周期执行的脚本触发方式决定它跑得多勤。1 秒周期适合做画面上的实时数值显示报警计数这类不需要高频刷新的用 5 秒或事件触发更省资源。挂在全局脚本里按周期跑的函数要注意它拿到的lpszPictureName未必是当前可见画面——全局脚本的上下文和画面脚本不一样涉及画面名的地方建议用GetLocalPicture反查而不是假定某个固定值。5.3 验证脚本真的执行了调试 C 脚本比 VBS 麻烦一点没有即时窗口。常用的验证路径有三条一是往内部变量写值再在变量管理里看实时值变不变二是用静态文本临时显示一个中间结果跑通了再撤掉三是在函数返回值上做判断失败时写一个错误标志变量画面角落放个小指示。// 用一个内部变量做执行探针跑一次加一 DWORD n GetTagDWord(dbg_CallCount); n; SetTagDWord(dbg_CallCount, n); // 后面再接真正的业务调用用返回值判断是否成功 BOOL bRet AXC_OnBtnMsgNext(gs_alarm_00, Control1); SetTagBit(dbg_LastCallOK, bRet ? 1 : 0);计数器不动说明脚本没被触发去查触发方式和事件挂载计数器在动但dbg_LastCallOK恒为 0说明触发没问题是画面名或控件对象名对不上。这个二分法能省掉大量反复改代码的时间。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门