PLC报警消息处理

在TIA Protal中,大致有以下几种方式可以获取报警信息:

  • HMI侧组态报警
  • Program_alarm
  • Prodiag

第一种方式基本上是最常用和最简单的,基本步骤是先创建报警变量再组态报警文本,由HMI定时去轮询变量,通过监控变量值的变化触发。优点是配置简单,但是效率比较低,工作量比较大(以前项目上我会新建一个报警excel来处理,每次检查报警表的时候就是我头最大的时候,特别是涉及到中英切换报警翻译的时候)。
第二种方式就是在程序中调用Program_Alarm实现报警推送,PLC中变量值的变化来推送报警消息。好处是处理在PLC侧,所以和HMI的通信负载比较低,另外一方面在PLC侧编程可以一定概率的实现自动化推送,客观降低工作量,增加效率。缺点是对设备有要求,PLC1200就不用想了,它只支持1500;另外报警文本存PLC内部的话其实还是很占PLC数据工作存储器资源的,数据量大了之后PLC可能吃不消(直接导致工作存储器不够用)。
第三种方式是使用Prodig技术,博图从V14开始就集成了prodiag功能了(它还有很多很好玩的应用,以后我可能单开一篇来写),这种技术极大的提升了编程效率,但是呢,依旧只支持1500。
所以1200的用户,还是老老实实用第一种方法吧。

1. HMI侧组态报警

以前我遇到很多非标项目,小项目很多人喜欢一个报警条目占用一个bool。最后传给HMI侧的时候HMI变量表里面就传了一大堆bool量的报警信息,这是非常占用HMI的Tag点位的。所以我这里用word来表示最多16条报警(为什么可以这么做,自己体会一下),这样的好处一是报警集中(毕竟一个或者多个word可以单独组合某一个设备单元的报警集合),二是传一个word给HMI只会被看作一个Tag,但是信息量1:16,简直遥遥领先..
废话不多说,开始演示吧。

  • 自己做一个Bool2Word的块,目的是收集在程序里做的报警
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #FB_BoolToWord_Instance_Alarm1(In_Alarm00 := "_HolderLoadStation".HoistCylinder.Error_Initial,
    In_Alarm01 := "_HolderLoadStation".HoistCylinder.Error_Target,
    In_Alarm02 := "_HolderLoadStation".HoistCylinder.Error_Pg,
    In_Alarm03 := "_HolderLoadStation".GripCylinder.Error_Initial,
    In_Alarm04 := "_HolderLoadStation".GripCylinder.Error_Target,
    In_Alarm05 := "_HolderLoadStation".GripCylinder.Error_Pg,
    In_Alarm06 := FALSE,
    In_Alarm07 := FALSE,
    In_Alarm08 := FALSE,
    In_Alarm09 := FALSE,
    In_Alarm10 := FALSE,
    In_Alarm11 := FALSE,
    In_Alarm12 := FALSE,
    In_Alarm13 := FALSE,
    In_Alarm14 := FALSE,
    In_Alarm15 := FALSE,
    Out_Alarm => "_AlarmDB".HolderLoadStationAlarm1);

    .这是外部管脚,具体报警怎么来的取决于你报警逻辑怎么写的

程序变量接口定义:

  • In_Alarmxx:Input,bool
  • Alarm:Static,word
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    REGION 输入映射
    #Alarm.%X0 := #In_Alarm00;
    #Alarm.%X1 := #In_Alarm01;
    #Alarm.%X2 := #In_Alarm02;
    #Alarm.%X3 := #In_Alarm03;
    #Alarm.%X4 := #In_Alarm04;
    #Alarm.%X5 := #In_Alarm05;
    #Alarm.%X6 := #In_Alarm06;
    #Alarm.%X7 := #In_Alarm07;
    #Alarm.%X8 := #In_Alarm08;
    #Alarm.%X9 := #In_Alarm09;
    #Alarm.%X10 := #In_Alarm10;
    #Alarm.%X11 := #In_Alarm11;
    #Alarm.%X12 := #In_Alarm12;
    #Alarm.%X13 := #In_Alarm13;
    #Alarm.%X14 := #In_Alarm14;
    #Alarm.%X15 := #In_Alarm15;
    END_REGION

    .这是内部程序,为了直观,我把报警一个一个赋值给了Word对应的bit.
    .其实有一种叫做AT的指令,可以在变量定义的时候直接把Array[0..15] of bool映射给一个Word。这样可以做到零代码映射,但是对FB有要求(FB不能被优化,想想为什么),感兴趣的朋友可以去玩一下。

完事之后再去HMI的HMI报警里面,把你定义好的报警文本和报警变量对应上,Ctrl+C/Ctrl+V吧。

.报警文本自己手动定义。你写个Hello World都行
.报警类别需要定义,不同的报警类别在HMI的报警控件中有不一样的处理显示规则
.触发变量和触发位配合使用,一个Word有16个位
触发器地址根据你的触发变量和触发位自动填入。

最后去画面上做出你的报警控件来,HMI选择报警视图。拽到画面上就OK。

.在HMI里面,选择记录,再选择报警记录,新增一个报警日志,设置名字记录数存储位置。然后你就得到一个历史记录报警。
.选中报警视图当前报警状态,然后你就得到一个实时报警记录。

  • 至此,第一种方法就已经实现了,但是在开始第二种方法之前我对一种方法做了一个更深的探索,用来提高我写报警文本的效率(甚至可以说我压根就不想手动写报警文本)。
    这里依旧简单记录一下:
    我在上面的Bool2Word块里面加了一坨新的程序段,如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    REGION 数值变化记忆
    // Statement section REGION
    IF #In_FirstScan THEN
    #AlarmMemory := #Alarm;
    END_IF;
    #plus(CLK:=(#AlarmMemory<>#Alarm));
    IF #plus.Q THEN
    #ValueChange := TRUE;
    #AlarmMemory := #Alarm;
    END_IF;
    END_REGION

    REGION 读取变量符号名做为报警文本
    // Statement section REGION
    IF #In_FirstScan OR TRUE = #ValueChange THEN
    #AlarmText[0] := GetSymbolName(variable := #In_Alarm00, size := 0);
    #AlarmText[1] := GetSymbolName(variable := #In_Alarm01, size := 0);
    #AlarmText[2] := GetSymbolName(variable := #In_Alarm02, size := 0);
    #AlarmText[3] := GetSymbolName(variable := #In_Alarm03, size := 0);
    #AlarmText[4] := GetSymbolName(variable := #In_Alarm04, size := 0);
    #AlarmText[5] := GetSymbolName(variable := #In_Alarm05, size := 0);
    #AlarmText[6] := GetSymbolName(variable := #In_Alarm06, size := 0);
    #AlarmText[7] := GetSymbolName(variable := #In_Alarm07, size := 0);
    #AlarmText[8] := GetSymbolName(variable := #In_Alarm08, size := 0);
    #AlarmText[9] := GetSymbolName(variable := #In_Alarm09, size := 0);
    #AlarmText[10] := GetSymbolName(variable := #In_Alarm10, size := 0);
    #AlarmText[11] := GetSymbolName(variable := #In_Alarm11, size := 0);
    #AlarmText[12] := GetSymbolName(variable := #In_Alarm12, size := 0);
    #AlarmText[13] := GetSymbolName(variable := #In_Alarm13, size := 0);
    #AlarmText[14] := GetSymbolName(variable := #In_Alarm14, size := 0);
    #AlarmText[15] := GetSymbolName(variable := #In_Alarm15, size := 0);
    #ValueChange := FALSE;
    END_IF;
    END_REGION

    .GetSymbolName具体怎么用,可以按Ctrl+F1,博图帮助系统会告诉你答案。

把你In_Alarm所连接的变量符号名改成的报警文本内容(你可以选择在DB内建Struct或者引用UDT,让你的符号名更有层级)。如图:

现在回到HMI侧,在HMI变量表新建你的AlarmDB_WordAlarmDB_Text(就是在PLC程序里面建立的报警字和对应报警文本数组,数组格式我定义成Array[0..15] of WString[60]);
找到你想处理的AlarmDB_Word对应的离散量报警,在对应报警文本的地方,先双击进入文本编辑框内,再右键,选择插入变量域,弹出过程子窗体,选择需要连接的变量,最后确认。如图:

.当然你也可以选连接文本列表,这又是其他玩法了,感兴趣可以玩一玩。

至此,我们就实现了用符号名来代替我们要手动键入的报警文本,是不是很神奇。
但是你反过头来再来想想,这个柔性报警有什么缺点:还记得我使用了GetSymbolName吗,最后输出要求的格式是WSTRING,这是个什么概念我们来算一算,一个string[60]占用62个byte长度。那么一个WString[60]就要占用124个byte长度,我们一个报警word一共产生了Array[0..15] of WString[60]也就是124*16=1984个byte,接近2KB,如果多用几个就更多了,回头看一下我们的PLC数据存储器才多大,我用的1215也就125KB工作寄存器(代码+数据)。

上面的题外话说多了,那么下面我们接着做Program_Alram。

2. Program_Alarm

这个操作方法比较简单,简单描述就行。

  • 如图,在FB里面建立一个(或多个)和报警相关的参数

  • 在FB代码编辑界面引用Program_Alarm

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //增加报警块
    #Program_Alarm(SIG:=#statAlarm.trigger,
    TIMESTAMP:=_ldt_in_,
    SD_1:=#statAlarm.text,
    SD_2:=_variant_in_,
    SD_3:=_variant_in_,
    SD_4:=_variant_in_,
    SD_5:=_variant_in_,
    SD_6:=_variant_in_,
    SD_7:=_variant_in_,
    SD_8:=_variant_in_,
    SD_9:=_variant_in_,
    SD_10:=_variant_in_,
    Error=>_bool_out_,
    Status=>_word_out_);

    .我只简单填写了trigger和一个报警text,其他更多的信息可以按Ctrl+F1获取博图帮助。
    .正式使用场景因为每一条报警都需要调用一次Program_Alarm,所以最好使用多重实例,在循环迭代(for循环遍历多重实例构成的数组)中调用来最大程度减少程序量;

.在PLC的PLC监控的报警中选择报警,填入报警文本(这是HMI弹出报警页面的的报警内容),填入信息文本(这是点入报警文本之后的详细帮助信息。)
.在HMI上拖拽一个报警视图出来,把确认信息和这里的对应好

就这么简单就可以自动生成一个Program_alarm了,记得要去HMI侧拖一个报警视图出来并选择相关确认信息,这一步工作还是要做的。
它的问题还是会占用一部分数据寄存器的内存,但是当我们使用1500的时候,就算1511应该也是1MB的工作寄存器起步了,所以影响应该还好。
再次提醒,这个块仅支持1500。

3. Prodiag

  • prodiag的基本使用方法其实很容易,这里先对基本使用做一个简易过程描述。

在1500的PLC里面,对你想使用的的变量右键(不一定非的是DB,对于FB的基本数据类型IO等等一样可以右键),我这里在FB里面新建了一个静态变量。如图:

.新建后,右边监控列会出现一个监控的小图标,如图不想监控了,右键这个小图标选择删除就行。

点完后的界面如下:

.监控类型可以选择操作数互锁动作,还有一些其他的。(比如当你选择互锁之后,就会多出一个条件让你填写),我们就拿操作数为例,右边可以选择触发器,下面可以选择延时监控类别,等等很多,有兴趣可以自己玩一玩。
.报警文本的格式是
<类别> : <监控类型> : <ProDiag FB 的名称> : <监控 ID> : <实例名称> : <参数名称> : <变量地址> : <变量名称> : <变量注释>,注意,这意味着它甚至可以把你的变量符号变量地址变量注释当作报警文本的一部分输出出来。
.详细文本域用来帮你添加你想添加的详细文本内容。

做完上面的之后,我们的选择触发器是FALSE,意味着当该变量为假的时候PLC就会给HMI推送一条报警信息,这甚至不需要去HMI界面做任何的配置。因为它的技术是PLC推送技术,不需要HMI去轮询或者监控的。

  • 在一些稍微大一点的项目中,可能会专门建立一个Prodiag_FB来做专门的监控管理。

在这种情况下,监控内容就具有了更多的结构化属性,也更利于程序分类。
如下:

.选择添加新块,语言选择PRODIAG(含IDB),就可以建立一个ProdiagFB,图是建立完成并监控了一个数据后的样子。
.具体什么时候该建立什么样的监控PRODIAG(含IDB),监控的内容应该是什么,监控类别的分类应该怎么划分。这就是项目上用来做结构化和标准化分类所需要考虑的了,这里就不详细谈论了。

4. 总结

虽然笔者对西门子PLC报警应用的总结虽然就到这了,但技术在不断的发展和更新,很多新技术值得去尝试,还需要不断的去学习总结才行。


PLC报警消息处理
http://example.com/2024/07/21/PLC报警消息处理/
作者
xiao cuncun
发布于
2024年7月21日
许可协议