发表于:2023/3/14 15:25:41
#0楼
0304 【万泉河】 我把LBP的功能块解耦了
昨天,3月3日,对LBP学习营的学员们进行了西门子LBP应用培训的第五课,也是最后一课:WINCC中的LBP应用和移植。
然而,学员们的学习热情比起前几期已经大幅减退了。 可能是春天到了,各行各业都在如火如荼地忙于生产建设,所以大多同行需要在现场加班加点而抽不开身。 回到家或者住处都要十一点或者更晚,就赶不上培训课程的时间了。
但以我跟个别学员沟通的情况看,学员们都已经普遍地跟不上课程进度了。 都只能把学习素材积累下留待以后有时间时再集中学习了。 所以对实时直播讲座就不在乎了。
然而这些都还是入门的基础,我要做的工作远远不止这些,所以对我来说要赶紧把这一章节结束,我才可以更专心去搞后面的工作。
而其实许多工作已经做过了,是在几个周之前已经在做,这些进展方向,在培训课程中有跟学员们提及。
其中就包含对LBP程序功能块的解耦。
为什么要解耦?
解释这个问题之前,需要先大致介绍下LBP的功能。 与以往所有西门子官方以及各民间组织提供的PLC库函数不同,LBP实现了多触摸屏的高仿弹出窗口设计。
所谓高仿,即其实原本触摸屏是不支持窗口弹出的,或者功能复杂,严重依赖触摸屏的编程功能,不容易实现标准化。
所以LBP就通过在PLC中的逻辑处理,判断到触摸屏上操作某个设备图标时,程序中将此设备的所有参数的运行数据传送到一个供触摸屏通讯的公共数据区,而触摸屏上跳转到设备的相关设置页面,感觉上像是弹出了窗口。而这个弹出窗口与正常电脑上的弹出窗口不同之处仅在于不能同时弹出多个,以及弹出时是满屏的,不能看到运行画面的底图。 这些都不重要,无所谓。
当然,这个功能的实现本身也没什么高难度,许多同行在具体工程项目中有类似需求时,也可以通过繁杂的编程和数据处理实现。然而LBP这里不仅仅实现,而且实现了标准化可重复使用,这就很了不起。
所以LBP中的每一个设备功能块,其实现的功能都是两部分,一部分传统的这个设备的控制逻辑,另一部分则是对触摸屏数据的处理。
传统的设备控制逻辑,所有的库其实都大同小异,甚至每个人也都自己当场编制设计一个简单的库。无非细节功能和画面的精致程度不同而已。 甚至,如果触摸屏数据处理部分可以实现标准化,用一个 标准的程序模块实现, 那么控制逻辑部分随便用一个已有的库功能,也能实现LBP同样的效果。
所以,把这两部分的功能解耦开,对于LBP功能移植到其它平台的PLC,相当重要。在移植过程中,可以不必两部分功能相互干扰,影响移植的实现了。
而实际上,在移植到SMART 200这样的小PLC时,原本的程序模式根本无法实现。首先,触摸屏功能中包含了大量的对数组数据的处理,占用了大量的静态变量和临时变量数据区,对方PLC的程序功能块根本不能容纳这么大的数据量。
其次,LBP功能逻辑中做了太多冗余功能,导致管脚数目太多,以至于在向SMART移植的时候,管脚数量很容易就超出了其允许的最多22个管脚的限制。 那么我就想,如果我用不到那么多功能的话,我何不继续使用以前的功能块,只要接口定义部分和LBP的定义兼容就好了呀!
而至于LBP的接口定义使用了多个UDT,甚至UDT的嵌套,我们还需要对UDT部分进行简化统一,是另外一个方面的话题。
而在实际调试过程中,我不仅仅用自己的功能块实现了和LBP的对接,甚至还按自己喜欢的方式做了触摸屏数据的整理和传送,也实现了同样的功能。
当在移植的过程中遇到系统资源限制无法直接实现的时候,仅保留原接口的定义,保证触摸屏和WINCC的数据接口不变,原画面模板可以直接进行通讯控制,这种移植也是可以接受的。
等于是,最后我们可以只保留将LBP的接口定义作为一种标准,烟台方法基于LBP接口定义来做标准化。
而这一切,都来自于先要对LBP原功能块解耦。
以最普通的MOTOR块LBP_MTR为例,其所有源程序在本文最后。然而其篇幅太长,这里只截取其region描述表:
所有段落中,框出来的两个段落:
REGION: read panel request
REGION: write outputs for Panel
这是为触摸屏数据接口服务的。 而其余的段落则全部都是其正常的控制逻辑。
那么将功能块拆分为2个,其实就是把FB块复制2份,分别叫做L1和L2
L1部分删除2个触摸屏相关段落。
L2部分只保留此2段落,而删除所有其它段落,以及保留一小段重复的段落:
IF "PANELS_NO" > 0 THEN
#tempPanelNo := "PANELS_NO";
ELSE
#tempPanelNo := 1;
END_IF;
然后在L2中,两端程序的中间,建立一个多重背景调用L1,并将输入输出管脚原名字填入。
即完成了原程序块的简单解耦。 这一点技巧,在本人专著《PLC标准化编程原理与方法》中,有重点讲述。虽然技能很简单,门槛不高,然而却是标准化方法中经常用到的非常重要的技能。
当然,程序块解耦分别建立完成后,还可以对各自的变量表中未用到的变量分别删除。 这一点, TIA PORTAL的功能还不够强大,不能自动显示或者用一个指令就查找出来。 需要右键指向每一个变量,列表中可以选择“转至下一个使用点”。而如果这个变量程序中未用到,则命令显示为灰色。则可以删除。
我们现在来看L1程序,如果没有触摸屏,而只有WINCC,那么功能完全可以直接应用,WINCC数据接口直接访问statDataMtr,这是一个UDT数据结构,包含了一个设备的所有控制命令和状态字。
而来看L2程序,逻辑只是几个循环调用,进行数据比较和传送,和MTR类型几乎完全没有关系。 未来解耦更多设备功能块之后,会发现这个部分的程序几乎完全相同。 唯一不同的是与statDataMtr名字相关的部分,需要更改为另外的设备符号对应的名字。 即仅仅数据结构有不同导致。 如果高级语言,这里肯定可以做成一个通用的程序功能块实现。 而在PLC系统中,要做到通用就没那么容易了。 暂时情况下,只能每个L2,都复制一遍,并做细节修改。
LBP_MTR FB2001
REGION header and description
//=============================================================================
// Siemens
// (c)Copyright (2019) All Rights Reserved
//-----------------------------------------------------------------------------
// Library: LBP
// Tested with: S7-1500 CPU 1516-3 PN/DP V2.5
// Engineering: TIA V15
// Restrictions: needs FB "LibBPL_Log15",and data types: "LibBPL_typeMtr..."
// Requirements: no specific - designed for S7-1200/1500
// Functionality: motor control - single speed, single rotating direction
//--------------------------------------------------------------------------------------------
// Change log table:
// Version Date Expert in charge Changes applied
// 01.00.00 18.05.2017 RW First released version
// 01.00.02 15.01.2018 RW renamed to LibBPL, added input indInterlock
// 01.00.03 29.03.2018 RW fix status dword
// 01.01.00 03.04.2018 RW only versioning
// 01.02.00 17.01.2019 MC TIA PORTAL V15 Update with UDTs
// 02.00.00 01.08.2019 JM Release V2 of LBP
//============================================================================================
//
// ------------------- HMI status ---------------------------------------------
// 00 - Off 08 - Trip Error 16 - Ext. Error Flag
// 01 - On 09 - Ext. Error 17 - Timeout Error
// 02 - Auto 10 - Interlock 18 - Plausibility Error
// 03 - Local 11 - Repair 19 - Operating Hours Limit Reached
// 05 - Run Down 13 - Collect Warning 20 - Switch Counter Limit Reached
// 06 - Run Up 14 - Collect Error 21 - Ext. Warning
// 07 - Auto Command 15 - Trip Error Flag
END_REGION
REGION initialization
#tempReset := FALSE;
#tempIntError := w#16#0;
END_REGION
REGION read inputs
#statDataMtr.statusHMI.identName:= #identName;
#statDataMtr.settingsPLC.timeout := #timeout;
IF "PANELS_NO" > 0 THEN // When PANELS_NO=0 then would be compilation error due to wrong array index
#tempPanelNo := "PANELS_NO";
ELSE
#tempPanelNo := 1;
END_IF;
END_REGION
REGION read panel request
IF "PANELS_NO" > 0 THEN
FOR #tempPanelIndex := 1 TO #tempPanelNo DO
#statPanels[#tempPanelIndex] := #panels[#tempPanelIndex].mtr;
#statIdentNames[#tempPanelIndex] := #panels[#tempPanelIndex].identName;
IF #statIdentNames[#tempPanelIndex] <> #statOldIdentNames[#tempPanelIndex] THEN
#statFirstCalls[#tempPanelIndex] := true;
#statOldIdentNames[#tempPanelIndex] := #statIdentNames[#tempPanelIndex];
ELSE
#statFirstCalls[#tempPanelIndex] := false;
END_IF;
END_FOR;
FOR #tempPanelIndex := 1 TO #tempPanelNo DO
IF (NOT #statFirstCalls[#tempPanelIndex])
AND (#statPanels[#tempPanelIndex].settingsHMI <> #statOldPanels[#tempPanelIndex])
AND (#statIdentNames[#tempPanelIndex] = #statDataMtr.statusHMI.identName) THEN
#statDataMtr.settingsHMI := #statOldPanels[#tempPanelIndex]
:= #statPanels[#tempPanelIndex].settingsHMI;
#statFirstCalls[#tempPanelIndex] := false;
END_IF;
END_FOR;
END_IF;
END_REGION
REGION overwrite settings from HMI
IF #statDataMtr.settingsHMI.overwrite.%X0 = FALSE THEN
#tempTimeOut := #timeout;
ELSE
#tempTimeOut := #statDataMtr.settingsHMI.timeout;
END_IF;
END_REGION
REGION repair mode
IF NOT #indRepair AND #statDataMtr.statusHMI.msgStatus.repair THEN
#instFbLog15(codeWrite := #LOG_REPAIR_OFF);
END_IF;
IF #indRepair AND NOT #statDataMtr.statusHMI.msgStatus.repair THEN
#instFbLog15(codeWrite := #LOG_REPAIR_ON);
END_IF;
END_REGION
REGION trip message
IF NOT #indTripOk AND NOT #statDataMtr.statusHMI.msgStatus.errorTrip AND NOT #indRepair THEN
#statDataMtr.statusHMI.msgStatus.errorTrip := TRUE;
#instFbLog15(codeWrite := #LOG_ERR_TRIP);
END_IF;
#statDataMtr.statusHMI.msgStatus.errorFlagTrip := NOT #indTripOk;
END_REGION
REGION external error message
IF #indErr AND NOT #statDataMtr.statusHMI.msgStatus.errorExt AND NOT #indRepair THEN
#statDataMtr.statusHMI.msgStatus.errorExt := TRUE;
#instFbLog15(codeWrite := #LOG_ERR_EXT);
END_IF;
#statDataMtr.statusHMI.msgStatus.errorFlagExt := #indErr;
END_REGION
REGION timeout control
#instTONCtrl(IN := ((#statStatus.ctrl AND NOT #indOn) OR (NOT #statStatus.ctrl AND NOT #indOff) AND NOT #indRepair),
PT := #tempTimeOut);
IF #instTONCtrl.Q AND NOT #statDataMtr.statusHMI.msgStatus.errorTimeout THEN
#instFbLog15(codeWrite := #LOG_ERR_TIMEOUT);
END_IF;
#statDataMtr.statusHMI.msgStatus.errorTimeout := #statDataMtr.statusHMI.msgStatus.errorTimeout OR #instTONCtrl.Q;
END_REGION
REGION plausibility/timeout check
#instTONPlaus(IN := ((#indOff AND #indOn) OR (NOT #indOff AND NOT #indOn) AND NOT #indRepair),
PT := #tempTimeOut);
IF #instTONPlaus.Q AND NOT #statDataMtr.statusHMI.msgStatus.errorPlaus THEN
#instFbLog15(codeWrite := #LOG_ERR_PLAUS);
END_IF;
#statDataMtr.statusHMI.msgStatus.errorPlaus := #instTONPlaus.Q;
END_REGION
REGION collect error
#statDataMtr.statusHMI.msgStatus.collectError := #statDataMtr.statusHMI.msgStatus.errorTrip OR #statDataMtr.statusHMI.msgStatus.errorExt OR #statDataMtr.statusHMI.msgStatus.errorTimeout OR #statDataMtr.statusHMI.msgStatus.errorPlaus;
#collectError := #statDataMtr.statusHMI.msgStatus.collectError;
#statStatus.ctrlMan := #statStatus.ctrlMan AND NOT #statDataMtr.statusHMI.msgStatus.collectError;
END_REGION
REGION collect warning
#statDataMtr.statusHMI.msgStatus.warningExt := #indWarn;
#statDataMtr.statusHMI.msgStatus.collectWarning := #statDataMtr.statusHMI.msgStatus.warningExt;
#collectWarning := #statDataMtr.statusHMI.msgStatus.collectWarning;
END_REGION
REGION collect maintenance
#statDataMtr.statusHMI.msgStatus.collectMaintenance := #statDataMtr.statusHMI.msgStatus.opHoursLimitReached OR #statDataMtr.statusHMI.msgStatus.switchCounterLimitReached;
#collectMaintenance := #statDataMtr.statusHMI.msgStatus.collectMaintenance;
END_REGION
REGION new status of external release
IF NOT #statDataMtr.statusHMI.msgStatus.release AND #indRelease THEN
#instFbLog15(codeWrite := #LOG_RELEASE_ON);
END_IF;
IF #statDataMtr.statusHMI.msgStatus.release AND NOT #indRelease THEN
#instFbLog15(codeWrite := #LOG_RELEASE_OFF);
END_IF;
END_REGION
REGION log interlock
IF #statDataMtr.statusHMI.msgStatus.interlock AND NOT #indInterlock THEN
#instFbLog15(codeWrite := #LOG_INTLCK_OFF);
ELSIF NOT #statDataMtr.statusHMI.msgStatus.interlock AND #indInterlock THEN
#instFbLog15(codeWrite := #LOG_INTLCK_ON);
END_IF;
END_REGION
REGION status detection
#statDataMtr.statusHMI.msgStatus.off := #indOff;
#statDataMtr.statusHMI.msgStatus.on := #indOn;
#statDataMtr.statusHMI.msgStatus.repair := #indRepair;
#statDataMtr.statusHMI.msgStatus.local := #indLocal;
#statDataMtr.statusHMI.msgStatus.release := #indRelease;
#statDataMtr.statusHMI.msgStatus.interlock := #indInterlock;
#statDataMtr.statusHMI.msgStatus.autoCommand := #cmdAutOn;
#statStatus.ctrlAut := #cmdAutOn;
#statStatus.ctrlLoc := #cmdLocOn;
END_REGION
REGION read commands
#instR_TrigAut(CLK := #cmdAut);
#instR_TrigReset(CLK := #cmdReset);
#statCommand.off := #statDataMtr.settingsHMI.cmd.%X0;
#statCommand.on := #statDataMtr.settingsHMI.cmd.%X1;
#statCommand.manual := #statDataMtr.settingsHMI.cmd.%X2;
#statCommand.auto := #statDataMtr.settingsHMI.cmd.%X3 OR (#instR_TrigAut.Q AND NOT #statDataMtr.settingsHMI.overwrite.%X7);
#statCommand.reset := #statDataMtr.settingsHMI.cmd.%X4 OR #instR_TrigReset.Q;
IF #statDataMtr.settingsHMI.overwrite.%X15 THEN
#instFbLog15(codeWrite := #LOG_CHECK);
#statDataMtr.settingsHMI.overwrite.%X15 := FALSE;
END_IF;
END_REGION
REGION clear/#reset commands and errors
#statDataMtr.settingsHMI.cmd := 0;
IF #statCommand.resetRepeat THEN
#statDataMtr.statusHMI.msgStatus.errorExt := #statDataMtr.statusHMI.msgStatus.errorFlagExt;
END_IF;
#statCommand.resetRepeat := #statCommand.reset;
IF #statCommand.reset THEN
#instFbLog15(codeWrite := #LOG_CMD_RESET);
#statDataMtr.statusHMI.msgStatus.errorTrip := #statDataMtr.statusHMI.msgStatus.errorFlagTrip;
#statDataMtr.statusHMI.msgStatus.errorExt := #statDataMtr.statusHMI.msgStatus.errorFlagExt;
#statDataMtr.statusHMI.msgStatus.errorTimeout := false;
#statDataMtr.statusHMI.msgStatus.errorPlaus := false;
#tempReset := TRUE;
END_IF;
#reset := #tempReset;
END_REGION
REGION mode remote automatic/manual
IF #statCommand.auto AND NOT #statDataMtr.statusHMI.msgStatus.local AND NOT #statDataMtr.statusHMI.msgStatus.collectError AND NOT #statDataMtr.statusHMI.msgStatus.auto THEN
#instFbLog15(codeWrite := #LOG_CMD_AUT);
#statDataMtr.statusHMI.msgStatus.auto := true;
END_IF;
IF #statCommand.manual OR #statDataMtr.statusHMI.msgStatus.collectError AND #statDataMtr.statusHMI.msgStatus.auto THEN
#instFbLog15(codeWrite := #LOG_CMD_MAN);
#statDataMtr.statusHMI.msgStatus.auto := false;
END_IF;
#statAuto := #statDataMtr.statusHMI.msgStatus.auto;
#statManual := NOT #statDataMtr.statusHMI.msgStatus.auto AND NOT #statDataMtr.statusHMI.msgStatus.local;
END_REGION
REGION mode local
IF NOT #statLocal AND #statDataMtr.statusHMI.msgStatus.local THEN
#instFbLog15(codeWrite := #LOG_MODE_LOC_ON);
END_IF;
IF #statLocal AND NOT #statDataMtr.statusHMI.msgStatus.local THEN
#instFbLog15(codeWrite := #LOG_MODE_LOC_OFF);
END_IF;
#statLocal := #statDataMtr.statusHMI.msgStatus.local;
// manual commands
IF #statCommand.on THEN
#statStatus.ctrlMan := TRUE;
END_IF;
IF #statCommand.off THEN
#statStatus.ctrlMan := FALSE;
END_IF;
END_REGION
REGION takeover automatic/local cmmand to manual command
IF NOT #statDataMtr.statusHMI.msgStatus.local AND #statDataMtr.statusHMI.msgStatus.auto THEN
#statStatus.ctrlMan := #statStatus.ctrlAut;
END_IF;
IF #statDataMtr.statusHMI.msgStatus.local THEN
#statStatus.ctrlMan := #statStatus.ctrlLoc;
END_IF;
END_REGION
REGION motor control
#tempBool := false;
IF NOT #statDataMtr.statusHMI.msgStatus.collectError AND NOT #statDataMtr.statusHMI.msgStatus.repair THEN
IF #statDataMtr.statusHMI.msgStatus.local THEN
#tempBool := #statStatus.ctrlLoc;
ELSIF #statDataMtr.statusHMI.msgStatus.auto THEN
#tempBool := #statStatus.ctrlAut;
ELSE
#tempBool := #statStatus.ctrlMan;
END_IF;
END_IF;
#tempNoRelease := #tempBool AND NOT #statStatus.ctrl AND NOT #statDataMtr.statusHMI.msgStatus.release;
#instR_TrigNoRelease(CLK := #tempNoRelease);
IF #tempNoRelease THEN
#tempBool := FALSE;
#statStatus.ctrlMan := FALSE;
IF #instR_TrigNoRelease.Q THEN
#instFbLog15(codeWrite := #LOG_NO_RELEASE);
#instFbLog15(codeWrite := #LOG_NO_RELEASE);
END_IF;
END_IF;
#tempBool := #tempBool AND NOT #statDataMtr.statusHMI.msgStatus.interlock;
IF NOT #tempBool AND #statStatus.ctrl THEN
#instFbLog15(codeWrite := #LOG_OFF);
END_IF;
IF #tempBool AND NOT #statStatus.ctrl THEN
#instFbLog15(codeWrite := #LOG_ON);
END_IF;
#statStatus.ctrl := #tempBool;
#on := #statStatus.ctrl;
// reset commands
#statCommand.reset := false;
#statCommand.auto := false;
#statCommand.manual := false;
#statCommand.on := false;
#statCommand.off := false;
END_REGION
REGION maintenance: switching count and operating hours
//统计启停次数和运行时间
REGION read system time
#tempInt := RD_SYS_T(#tempDTL);
IF #tempInt <> 0 THEN
#tempIntError := #tempIntError OR #ERR_RDSYST;
END_IF;
END_REGION
IF NOT #statOnOld AND #indOn THEN
#statDataMtr.settingsHMI.maintenance.switches := #statDataMtr.settingsHMI.maintenance.switches + 1;
END_IF;
#statOnOld := #indOn;
IF #indOn THEN
#tempTime := T_DIFF(IN1 := #tempDTL, IN2 := #instLastTime);
#statOpMsecs := #statOpMsecs + #tempTime;
IF #statOpMsecs > #ONE_HOUR THEN
#statOpMsecs := #statOpMsecs - #ONE_HOUR;
#statDataMtr.settingsHMI.maintenance.opHours := #statDataMtr.settingsHMI.maintenance.opHours + 1;
END_IF;
END_IF;
IF #statDataMtr.settingsHMI.maintenance.switches >= #statDataMtr.settingsHMI.switches AND #statDataMtr.settingsHMI.switches > 0 THEN
#statDataMtr.statusHMI.msgStatus.switchCounterLimitReached := true;
ELSE
#statDataMtr.statusHMI.msgStatus.switchCounterLimitReached := false;
END_IF;
IF (#statDataMtr.settingsHMI.maintenance.opHours >= #statDataMtr.settingsHMI.opHours) AND (#statDataMtr.settingsHMI.opHours > 0) THEN
#statDataMtr.statusHMI.msgStatus.opHoursLimitReached := true;
ELSE
#statDataMtr.statusHMI.msgStatus.opHoursLimitReached := false;
END_IF;
REGION save system time for next cycle
#instLastTime := #tempDTL;
END_REGION
END_REGION
REGION write mode/status
#modeAut := #statDataMtr.statusHMI.msgStatus.auto AND NOT #statDataMtr.statusHMI.msgStatus.local;
#statDataMtr.statusHMI.msgStatus.runDown := NOT #statStatus.ctrl AND NOT #statDataMtr.statusHMI.msgStatus.off;
#statDataMtr.statusHMI.msgStatus.runUp := #statStatus.ctrl AND NOT #statDataMtr.statusHMI.msgStatus.on;
END_REGION
REGION write internal error
#intError := #tempIntError;
#statDataMtr.log15 := #instFbLog15.statData;
END_REGION
REGION write outputs for Panel
IF "PANELS_NO" > 0 THEN
#tempPanel.settingsPLC := #statDataMtr.settingsPLC;
#tempPanel.settingsHMI := #statDataMtr.settingsHMI;
#tempPanel.statusHMI := #statDataMtr.statusHMI;
#tempPanel.alarms1.%X0 := #statDataMtr.statusHMI.msgStatus.off;
#tempPanel.alarms1.%X1 := #statDataMtr.statusHMI.msgStatus.on;
#tempPanel.alarms1.%X2 := #statDataMtr.statusHMI.msgStatus.auto;
#tempPanel.alarms1.%X3 := #statDataMtr.statusHMI.msgStatus.local;
#tempPanel.alarms1.%X4 := #statDataMtr.statusHMI.msgStatus.release;
#tempPanel.alarms1.%X5 := #statDataMtr.statusHMI.msgStatus.runDown;
#tempPanel.alarms1.%X6 := #statDataMtr.statusHMI.msgStatus.runUp;
#tempPanel.alarms1.%X7 := #statDataMtr.statusHMI.msgStatus.errorTrip;
#tempPanel.alarms1.%X8 := #statDataMtr.statusHMI.msgStatus.errorExt;
#tempPanel.alarms1.%X9 := #statDataMtr.statusHMI.msgStatus.interlock;
#tempPanel.alarms1.%X10 := #statDataMtr.statusHMI.msgStatus.repair;
#tempPanel.alarms1.%X11 := #statDataMtr.statusHMI.msgStatus.collectMaintenance;
#tempPanel.alarms1.%X12 := #statDataMtr.statusHMI.msgStatus.collectWarning;
#tempPanel.alarms1.%X13 := #statDataMtr.statusHMI.msgStatus.collectError;
#tempPanel.alarms1.%X14 := #statDataMtr.statusHMI.msgStatus.errorFlagTrip;
#tempPanel.alarms1.%X15 := #statDataMtr.statusHMI.msgStatus.errorFlagExt;
#tempPanel.alarms2.%X0 := #statDataMtr.statusHMI.msgStatus.errorTimeout;
#tempPanel.alarms2.%X1 := #statDataMtr.statusHMI.msgStatus.errorPlaus;
#tempPanel.alarms2.%X2 := #statDataMtr.statusHMI.msgStatus.opHoursLimitReached;
#tempPanel.alarms2.%X3 := #statDataMtr.statusHMI.msgStatus.switchCounterLimitReached;
#tempPanel.alarms2.%X4 := #statDataMtr.statusHMI.msgStatus.warningExt;
#tempPanel.alarms2.%X5 := #statDataMtr.statusHMI.msgStatus.autoCommand;
// Comfort symbols- transfer variables
#statSymbolMtr.identName := #statDataMtr.statusHMI.identName;
#statSymbolMtr.note := #statDataMtr.settingsHMI.note;
#statSymbolMtr.off := #statDataMtr.statusHMI.msgStatus.off;
#statSymbolMtr.on := #statDataMtr.statusHMI.msgStatus.on;
#statSymbolMtr.auto := #statDataMtr.statusHMI.msgStatus.auto;
#statSymbolMtr.local := #statDataMtr.statusHMI.msgStatus.local;
#statSymbolMtr.repair := #statDataMtr.statusHMI.msgStatus.repair;
#statSymbolMtr.collectMaintenance := #statDataMtr.statusHMI.msgStatus.collectMaintenance;
#statSymbolMtr.collectWarning := #statDataMtr.statusHMI.msgStatus.collectWarning;
#statSymbolMtr.collectError := #statDataMtr.statusHMI.msgStatus.collectError;
#statSymbolMtr.errorFlagTrip := #statDataMtr.statusHMI.msgStatus.errorFlagTrip;
#statSymbolMtr.interlock := #statDataMtr.statusHMI.msgStatus.interlock;
#statSymbolMtr.alarmsInfo1 := #statDataMtr.settingsHMI.alarmsInfo1;
#statSymbolMtr.alarmsAck := #statDataMtr.settingsHMI.alarmsAck;
#statSymbolMtr.alarms1 := #tempPanel.alarms1;
#statSymbolMtr.alarms2 := #tempPanel.alarms2;
FOR #tempPanelIndex := 1 TO #tempPanelNo DO
IF (#panels[#tempPanelIndex].identName = #statDataMtr.statusHMI.identName) THEN
#statOldIdentNames[#tempPanelIndex] := #statDataMtr.statusHMI.identName;
#statOldPanels[#tempPanelIndex] := #statDataMtr.settingsHMI;
#panels[#tempPanelIndex].mtr := #tempPanel;
#panels[#tempPanelIndex].log15 := #instFbLog15.statData;
#panels[#tempPanelIndex].opStation := #statDataMtr.settingsHMI.opStation;
#panels[#tempPanelIndex].note := #statDataMtr.settingsHMI.note;
END_IF;
END_FOR;
END_IF;
END_REGION
昨天,3月3日,对LBP学习营的学员们进行了西门子LBP应用培训的第五课,也是最后一课:WINCC中的LBP应用和移植。
然而,学员们的学习热情比起前几期已经大幅减退了。 可能是春天到了,各行各业都在如火如荼地忙于生产建设,所以大多同行需要在现场加班加点而抽不开身。 回到家或者住处都要十一点或者更晚,就赶不上培训课程的时间了。
但以我跟个别学员沟通的情况看,学员们都已经普遍地跟不上课程进度了。 都只能把学习素材积累下留待以后有时间时再集中学习了。 所以对实时直播讲座就不在乎了。
然而这些都还是入门的基础,我要做的工作远远不止这些,所以对我来说要赶紧把这一章节结束,我才可以更专心去搞后面的工作。
而其实许多工作已经做过了,是在几个周之前已经在做,这些进展方向,在培训课程中有跟学员们提及。
其中就包含对LBP程序功能块的解耦。
为什么要解耦?
解释这个问题之前,需要先大致介绍下LBP的功能。 与以往所有西门子官方以及各民间组织提供的PLC库函数不同,LBP实现了多触摸屏的高仿弹出窗口设计。
所谓高仿,即其实原本触摸屏是不支持窗口弹出的,或者功能复杂,严重依赖触摸屏的编程功能,不容易实现标准化。
所以LBP就通过在PLC中的逻辑处理,判断到触摸屏上操作某个设备图标时,程序中将此设备的所有参数的运行数据传送到一个供触摸屏通讯的公共数据区,而触摸屏上跳转到设备的相关设置页面,感觉上像是弹出了窗口。而这个弹出窗口与正常电脑上的弹出窗口不同之处仅在于不能同时弹出多个,以及弹出时是满屏的,不能看到运行画面的底图。 这些都不重要,无所谓。
当然,这个功能的实现本身也没什么高难度,许多同行在具体工程项目中有类似需求时,也可以通过繁杂的编程和数据处理实现。然而LBP这里不仅仅实现,而且实现了标准化可重复使用,这就很了不起。
所以LBP中的每一个设备功能块,其实现的功能都是两部分,一部分传统的这个设备的控制逻辑,另一部分则是对触摸屏数据的处理。
传统的设备控制逻辑,所有的库其实都大同小异,甚至每个人也都自己当场编制设计一个简单的库。无非细节功能和画面的精致程度不同而已。 甚至,如果触摸屏数据处理部分可以实现标准化,用一个 标准的程序模块实现, 那么控制逻辑部分随便用一个已有的库功能,也能实现LBP同样的效果。
所以,把这两部分的功能解耦开,对于LBP功能移植到其它平台的PLC,相当重要。在移植过程中,可以不必两部分功能相互干扰,影响移植的实现了。
而实际上,在移植到SMART 200这样的小PLC时,原本的程序模式根本无法实现。首先,触摸屏功能中包含了大量的对数组数据的处理,占用了大量的静态变量和临时变量数据区,对方PLC的程序功能块根本不能容纳这么大的数据量。
其次,LBP功能逻辑中做了太多冗余功能,导致管脚数目太多,以至于在向SMART移植的时候,管脚数量很容易就超出了其允许的最多22个管脚的限制。 那么我就想,如果我用不到那么多功能的话,我何不继续使用以前的功能块,只要接口定义部分和LBP的定义兼容就好了呀!
而至于LBP的接口定义使用了多个UDT,甚至UDT的嵌套,我们还需要对UDT部分进行简化统一,是另外一个方面的话题。
而在实际调试过程中,我不仅仅用自己的功能块实现了和LBP的对接,甚至还按自己喜欢的方式做了触摸屏数据的整理和传送,也实现了同样的功能。
当在移植的过程中遇到系统资源限制无法直接实现的时候,仅保留原接口的定义,保证触摸屏和WINCC的数据接口不变,原画面模板可以直接进行通讯控制,这种移植也是可以接受的。
等于是,最后我们可以只保留将LBP的接口定义作为一种标准,烟台方法基于LBP接口定义来做标准化。
而这一切,都来自于先要对LBP原功能块解耦。
以最普通的MOTOR块LBP_MTR为例,其所有源程序在本文最后。然而其篇幅太长,这里只截取其region描述表:
所有段落中,框出来的两个段落:
REGION: read panel request
REGION: write outputs for Panel
这是为触摸屏数据接口服务的。 而其余的段落则全部都是其正常的控制逻辑。
那么将功能块拆分为2个,其实就是把FB块复制2份,分别叫做L1和L2
L1部分删除2个触摸屏相关段落。
L2部分只保留此2段落,而删除所有其它段落,以及保留一小段重复的段落:
IF "PANELS_NO" > 0 THEN
#tempPanelNo := "PANELS_NO";
ELSE
#tempPanelNo := 1;
END_IF;
然后在L2中,两端程序的中间,建立一个多重背景调用L1,并将输入输出管脚原名字填入。
即完成了原程序块的简单解耦。 这一点技巧,在本人专著《PLC标准化编程原理与方法》中,有重点讲述。虽然技能很简单,门槛不高,然而却是标准化方法中经常用到的非常重要的技能。
当然,程序块解耦分别建立完成后,还可以对各自的变量表中未用到的变量分别删除。 这一点, TIA PORTAL的功能还不够强大,不能自动显示或者用一个指令就查找出来。 需要右键指向每一个变量,列表中可以选择“转至下一个使用点”。而如果这个变量程序中未用到,则命令显示为灰色。则可以删除。
我们现在来看L1程序,如果没有触摸屏,而只有WINCC,那么功能完全可以直接应用,WINCC数据接口直接访问statDataMtr,这是一个UDT数据结构,包含了一个设备的所有控制命令和状态字。
而来看L2程序,逻辑只是几个循环调用,进行数据比较和传送,和MTR类型几乎完全没有关系。 未来解耦更多设备功能块之后,会发现这个部分的程序几乎完全相同。 唯一不同的是与statDataMtr名字相关的部分,需要更改为另外的设备符号对应的名字。 即仅仅数据结构有不同导致。 如果高级语言,这里肯定可以做成一个通用的程序功能块实现。 而在PLC系统中,要做到通用就没那么容易了。 暂时情况下,只能每个L2,都复制一遍,并做细节修改。
LBP_MTR FB2001
REGION header and description
//=============================================================================
// Siemens
// (c)Copyright (2019) All Rights Reserved
//-----------------------------------------------------------------------------
// Library: LBP
// Tested with: S7-1500 CPU 1516-3 PN/DP V2.5
// Engineering: TIA V15
// Restrictions: needs FB "LibBPL_Log15",and data types: "LibBPL_typeMtr..."
// Requirements: no specific - designed for S7-1200/1500
// Functionality: motor control - single speed, single rotating direction
//--------------------------------------------------------------------------------------------
// Change log table:
// Version Date Expert in charge Changes applied
// 01.00.00 18.05.2017 RW First released version
// 01.00.02 15.01.2018 RW renamed to LibBPL, added input indInterlock
// 01.00.03 29.03.2018 RW fix status dword
// 01.01.00 03.04.2018 RW only versioning
// 01.02.00 17.01.2019 MC TIA PORTAL V15 Update with UDTs
// 02.00.00 01.08.2019 JM Release V2 of LBP
//============================================================================================
//
// ------------------- HMI status ---------------------------------------------
// 00 - Off 08 - Trip Error 16 - Ext. Error Flag
// 01 - On 09 - Ext. Error 17 - Timeout Error
// 02 - Auto 10 - Interlock 18 - Plausibility Error
// 03 - Local 11 - Repair 19 - Operating Hours Limit Reached
// 05 - Run Down 13 - Collect Warning 20 - Switch Counter Limit Reached
// 06 - Run Up 14 - Collect Error 21 - Ext. Warning
// 07 - Auto Command 15 - Trip Error Flag
END_REGION
REGION initialization
#tempReset := FALSE;
#tempIntError := w#16#0;
END_REGION
REGION read inputs
#statDataMtr.statusHMI.identName:= #identName;
#statDataMtr.settingsPLC.timeout := #timeout;
IF "PANELS_NO" > 0 THEN // When PANELS_NO=0 then would be compilation error due to wrong array index
#tempPanelNo := "PANELS_NO";
ELSE
#tempPanelNo := 1;
END_IF;
END_REGION
REGION read panel request
IF "PANELS_NO" > 0 THEN
FOR #tempPanelIndex := 1 TO #tempPanelNo DO
#statPanels[#tempPanelIndex] := #panels[#tempPanelIndex].mtr;
#statIdentNames[#tempPanelIndex] := #panels[#tempPanelIndex].identName;
IF #statIdentNames[#tempPanelIndex] <> #statOldIdentNames[#tempPanelIndex] THEN
#statFirstCalls[#tempPanelIndex] := true;
#statOldIdentNames[#tempPanelIndex] := #statIdentNames[#tempPanelIndex];
ELSE
#statFirstCalls[#tempPanelIndex] := false;
END_IF;
END_FOR;
FOR #tempPanelIndex := 1 TO #tempPanelNo DO
IF (NOT #statFirstCalls[#tempPanelIndex])
AND (#statPanels[#tempPanelIndex].settingsHMI <> #statOldPanels[#tempPanelIndex])
AND (#statIdentNames[#tempPanelIndex] = #statDataMtr.statusHMI.identName) THEN
#statDataMtr.settingsHMI := #statOldPanels[#tempPanelIndex]
:= #statPanels[#tempPanelIndex].settingsHMI;
#statFirstCalls[#tempPanelIndex] := false;
END_IF;
END_FOR;
END_IF;
END_REGION
REGION overwrite settings from HMI
IF #statDataMtr.settingsHMI.overwrite.%X0 = FALSE THEN
#tempTimeOut := #timeout;
ELSE
#tempTimeOut := #statDataMtr.settingsHMI.timeout;
END_IF;
END_REGION
REGION repair mode
IF NOT #indRepair AND #statDataMtr.statusHMI.msgStatus.repair THEN
#instFbLog15(codeWrite := #LOG_REPAIR_OFF);
END_IF;
IF #indRepair AND NOT #statDataMtr.statusHMI.msgStatus.repair THEN
#instFbLog15(codeWrite := #LOG_REPAIR_ON);
END_IF;
END_REGION
REGION trip message
IF NOT #indTripOk AND NOT #statDataMtr.statusHMI.msgStatus.errorTrip AND NOT #indRepair THEN
#statDataMtr.statusHMI.msgStatus.errorTrip := TRUE;
#instFbLog15(codeWrite := #LOG_ERR_TRIP);
END_IF;
#statDataMtr.statusHMI.msgStatus.errorFlagTrip := NOT #indTripOk;
END_REGION
REGION external error message
IF #indErr AND NOT #statDataMtr.statusHMI.msgStatus.errorExt AND NOT #indRepair THEN
#statDataMtr.statusHMI.msgStatus.errorExt := TRUE;
#instFbLog15(codeWrite := #LOG_ERR_EXT);
END_IF;
#statDataMtr.statusHMI.msgStatus.errorFlagExt := #indErr;
END_REGION
REGION timeout control
#instTONCtrl(IN := ((#statStatus.ctrl AND NOT #indOn) OR (NOT #statStatus.ctrl AND NOT #indOff) AND NOT #indRepair),
PT := #tempTimeOut);
IF #instTONCtrl.Q AND NOT #statDataMtr.statusHMI.msgStatus.errorTimeout THEN
#instFbLog15(codeWrite := #LOG_ERR_TIMEOUT);
END_IF;
#statDataMtr.statusHMI.msgStatus.errorTimeout := #statDataMtr.statusHMI.msgStatus.errorTimeout OR #instTONCtrl.Q;
END_REGION
REGION plausibility/timeout check
#instTONPlaus(IN := ((#indOff AND #indOn) OR (NOT #indOff AND NOT #indOn) AND NOT #indRepair),
PT := #tempTimeOut);
IF #instTONPlaus.Q AND NOT #statDataMtr.statusHMI.msgStatus.errorPlaus THEN
#instFbLog15(codeWrite := #LOG_ERR_PLAUS);
END_IF;
#statDataMtr.statusHMI.msgStatus.errorPlaus := #instTONPlaus.Q;
END_REGION
REGION collect error
#statDataMtr.statusHMI.msgStatus.collectError := #statDataMtr.statusHMI.msgStatus.errorTrip OR #statDataMtr.statusHMI.msgStatus.errorExt OR #statDataMtr.statusHMI.msgStatus.errorTimeout OR #statDataMtr.statusHMI.msgStatus.errorPlaus;
#collectError := #statDataMtr.statusHMI.msgStatus.collectError;
#statStatus.ctrlMan := #statStatus.ctrlMan AND NOT #statDataMtr.statusHMI.msgStatus.collectError;
END_REGION
REGION collect warning
#statDataMtr.statusHMI.msgStatus.warningExt := #indWarn;
#statDataMtr.statusHMI.msgStatus.collectWarning := #statDataMtr.statusHMI.msgStatus.warningExt;
#collectWarning := #statDataMtr.statusHMI.msgStatus.collectWarning;
END_REGION
REGION collect maintenance
#statDataMtr.statusHMI.msgStatus.collectMaintenance := #statDataMtr.statusHMI.msgStatus.opHoursLimitReached OR #statDataMtr.statusHMI.msgStatus.switchCounterLimitReached;
#collectMaintenance := #statDataMtr.statusHMI.msgStatus.collectMaintenance;
END_REGION
REGION new status of external release
IF NOT #statDataMtr.statusHMI.msgStatus.release AND #indRelease THEN
#instFbLog15(codeWrite := #LOG_RELEASE_ON);
END_IF;
IF #statDataMtr.statusHMI.msgStatus.release AND NOT #indRelease THEN
#instFbLog15(codeWrite := #LOG_RELEASE_OFF);
END_IF;
END_REGION
REGION log interlock
IF #statDataMtr.statusHMI.msgStatus.interlock AND NOT #indInterlock THEN
#instFbLog15(codeWrite := #LOG_INTLCK_OFF);
ELSIF NOT #statDataMtr.statusHMI.msgStatus.interlock AND #indInterlock THEN
#instFbLog15(codeWrite := #LOG_INTLCK_ON);
END_IF;
END_REGION
REGION status detection
#statDataMtr.statusHMI.msgStatus.off := #indOff;
#statDataMtr.statusHMI.msgStatus.on := #indOn;
#statDataMtr.statusHMI.msgStatus.repair := #indRepair;
#statDataMtr.statusHMI.msgStatus.local := #indLocal;
#statDataMtr.statusHMI.msgStatus.release := #indRelease;
#statDataMtr.statusHMI.msgStatus.interlock := #indInterlock;
#statDataMtr.statusHMI.msgStatus.autoCommand := #cmdAutOn;
#statStatus.ctrlAut := #cmdAutOn;
#statStatus.ctrlLoc := #cmdLocOn;
END_REGION
REGION read commands
#instR_TrigAut(CLK := #cmdAut);
#instR_TrigReset(CLK := #cmdReset);
#statCommand.off := #statDataMtr.settingsHMI.cmd.%X0;
#statCommand.on := #statDataMtr.settingsHMI.cmd.%X1;
#statCommand.manual := #statDataMtr.settingsHMI.cmd.%X2;
#statCommand.auto := #statDataMtr.settingsHMI.cmd.%X3 OR (#instR_TrigAut.Q AND NOT #statDataMtr.settingsHMI.overwrite.%X7);
#statCommand.reset := #statDataMtr.settingsHMI.cmd.%X4 OR #instR_TrigReset.Q;
IF #statDataMtr.settingsHMI.overwrite.%X15 THEN
#instFbLog15(codeWrite := #LOG_CHECK);
#statDataMtr.settingsHMI.overwrite.%X15 := FALSE;
END_IF;
END_REGION
REGION clear/#reset commands and errors
#statDataMtr.settingsHMI.cmd := 0;
IF #statCommand.resetRepeat THEN
#statDataMtr.statusHMI.msgStatus.errorExt := #statDataMtr.statusHMI.msgStatus.errorFlagExt;
END_IF;
#statCommand.resetRepeat := #statCommand.reset;
IF #statCommand.reset THEN
#instFbLog15(codeWrite := #LOG_CMD_RESET);
#statDataMtr.statusHMI.msgStatus.errorTrip := #statDataMtr.statusHMI.msgStatus.errorFlagTrip;
#statDataMtr.statusHMI.msgStatus.errorExt := #statDataMtr.statusHMI.msgStatus.errorFlagExt;
#statDataMtr.statusHMI.msgStatus.errorTimeout := false;
#statDataMtr.statusHMI.msgStatus.errorPlaus := false;
#tempReset := TRUE;
END_IF;
#reset := #tempReset;
END_REGION
REGION mode remote automatic/manual
IF #statCommand.auto AND NOT #statDataMtr.statusHMI.msgStatus.local AND NOT #statDataMtr.statusHMI.msgStatus.collectError AND NOT #statDataMtr.statusHMI.msgStatus.auto THEN
#instFbLog15(codeWrite := #LOG_CMD_AUT);
#statDataMtr.statusHMI.msgStatus.auto := true;
END_IF;
IF #statCommand.manual OR #statDataMtr.statusHMI.msgStatus.collectError AND #statDataMtr.statusHMI.msgStatus.auto THEN
#instFbLog15(codeWrite := #LOG_CMD_MAN);
#statDataMtr.statusHMI.msgStatus.auto := false;
END_IF;
#statAuto := #statDataMtr.statusHMI.msgStatus.auto;
#statManual := NOT #statDataMtr.statusHMI.msgStatus.auto AND NOT #statDataMtr.statusHMI.msgStatus.local;
END_REGION
REGION mode local
IF NOT #statLocal AND #statDataMtr.statusHMI.msgStatus.local THEN
#instFbLog15(codeWrite := #LOG_MODE_LOC_ON);
END_IF;
IF #statLocal AND NOT #statDataMtr.statusHMI.msgStatus.local THEN
#instFbLog15(codeWrite := #LOG_MODE_LOC_OFF);
END_IF;
#statLocal := #statDataMtr.statusHMI.msgStatus.local;
// manual commands
IF #statCommand.on THEN
#statStatus.ctrlMan := TRUE;
END_IF;
IF #statCommand.off THEN
#statStatus.ctrlMan := FALSE;
END_IF;
END_REGION
REGION takeover automatic/local cmmand to manual command
IF NOT #statDataMtr.statusHMI.msgStatus.local AND #statDataMtr.statusHMI.msgStatus.auto THEN
#statStatus.ctrlMan := #statStatus.ctrlAut;
END_IF;
IF #statDataMtr.statusHMI.msgStatus.local THEN
#statStatus.ctrlMan := #statStatus.ctrlLoc;
END_IF;
END_REGION
REGION motor control
#tempBool := false;
IF NOT #statDataMtr.statusHMI.msgStatus.collectError AND NOT #statDataMtr.statusHMI.msgStatus.repair THEN
IF #statDataMtr.statusHMI.msgStatus.local THEN
#tempBool := #statStatus.ctrlLoc;
ELSIF #statDataMtr.statusHMI.msgStatus.auto THEN
#tempBool := #statStatus.ctrlAut;
ELSE
#tempBool := #statStatus.ctrlMan;
END_IF;
END_IF;
#tempNoRelease := #tempBool AND NOT #statStatus.ctrl AND NOT #statDataMtr.statusHMI.msgStatus.release;
#instR_TrigNoRelease(CLK := #tempNoRelease);
IF #tempNoRelease THEN
#tempBool := FALSE;
#statStatus.ctrlMan := FALSE;
IF #instR_TrigNoRelease.Q THEN
#instFbLog15(codeWrite := #LOG_NO_RELEASE);
#instFbLog15(codeWrite := #LOG_NO_RELEASE);
END_IF;
END_IF;
#tempBool := #tempBool AND NOT #statDataMtr.statusHMI.msgStatus.interlock;
IF NOT #tempBool AND #statStatus.ctrl THEN
#instFbLog15(codeWrite := #LOG_OFF);
END_IF;
IF #tempBool AND NOT #statStatus.ctrl THEN
#instFbLog15(codeWrite := #LOG_ON);
END_IF;
#statStatus.ctrl := #tempBool;
#on := #statStatus.ctrl;
// reset commands
#statCommand.reset := false;
#statCommand.auto := false;
#statCommand.manual := false;
#statCommand.on := false;
#statCommand.off := false;
END_REGION
REGION maintenance: switching count and operating hours
//统计启停次数和运行时间
REGION read system time
#tempInt := RD_SYS_T(#tempDTL);
IF #tempInt <> 0 THEN
#tempIntError := #tempIntError OR #ERR_RDSYST;
END_IF;
END_REGION
IF NOT #statOnOld AND #indOn THEN
#statDataMtr.settingsHMI.maintenance.switches := #statDataMtr.settingsHMI.maintenance.switches + 1;
END_IF;
#statOnOld := #indOn;
IF #indOn THEN
#tempTime := T_DIFF(IN1 := #tempDTL, IN2 := #instLastTime);
#statOpMsecs := #statOpMsecs + #tempTime;
IF #statOpMsecs > #ONE_HOUR THEN
#statOpMsecs := #statOpMsecs - #ONE_HOUR;
#statDataMtr.settingsHMI.maintenance.opHours := #statDataMtr.settingsHMI.maintenance.opHours + 1;
END_IF;
END_IF;
IF #statDataMtr.settingsHMI.maintenance.switches >= #statDataMtr.settingsHMI.switches AND #statDataMtr.settingsHMI.switches > 0 THEN
#statDataMtr.statusHMI.msgStatus.switchCounterLimitReached := true;
ELSE
#statDataMtr.statusHMI.msgStatus.switchCounterLimitReached := false;
END_IF;
IF (#statDataMtr.settingsHMI.maintenance.opHours >= #statDataMtr.settingsHMI.opHours) AND (#statDataMtr.settingsHMI.opHours > 0) THEN
#statDataMtr.statusHMI.msgStatus.opHoursLimitReached := true;
ELSE
#statDataMtr.statusHMI.msgStatus.opHoursLimitReached := false;
END_IF;
REGION save system time for next cycle
#instLastTime := #tempDTL;
END_REGION
END_REGION
REGION write mode/status
#modeAut := #statDataMtr.statusHMI.msgStatus.auto AND NOT #statDataMtr.statusHMI.msgStatus.local;
#statDataMtr.statusHMI.msgStatus.runDown := NOT #statStatus.ctrl AND NOT #statDataMtr.statusHMI.msgStatus.off;
#statDataMtr.statusHMI.msgStatus.runUp := #statStatus.ctrl AND NOT #statDataMtr.statusHMI.msgStatus.on;
END_REGION
REGION write internal error
#intError := #tempIntError;
#statDataMtr.log15 := #instFbLog15.statData;
END_REGION
REGION write outputs for Panel
IF "PANELS_NO" > 0 THEN
#tempPanel.settingsPLC := #statDataMtr.settingsPLC;
#tempPanel.settingsHMI := #statDataMtr.settingsHMI;
#tempPanel.statusHMI := #statDataMtr.statusHMI;
#tempPanel.alarms1.%X0 := #statDataMtr.statusHMI.msgStatus.off;
#tempPanel.alarms1.%X1 := #statDataMtr.statusHMI.msgStatus.on;
#tempPanel.alarms1.%X2 := #statDataMtr.statusHMI.msgStatus.auto;
#tempPanel.alarms1.%X3 := #statDataMtr.statusHMI.msgStatus.local;
#tempPanel.alarms1.%X4 := #statDataMtr.statusHMI.msgStatus.release;
#tempPanel.alarms1.%X5 := #statDataMtr.statusHMI.msgStatus.runDown;
#tempPanel.alarms1.%X6 := #statDataMtr.statusHMI.msgStatus.runUp;
#tempPanel.alarms1.%X7 := #statDataMtr.statusHMI.msgStatus.errorTrip;
#tempPanel.alarms1.%X8 := #statDataMtr.statusHMI.msgStatus.errorExt;
#tempPanel.alarms1.%X9 := #statDataMtr.statusHMI.msgStatus.interlock;
#tempPanel.alarms1.%X10 := #statDataMtr.statusHMI.msgStatus.repair;
#tempPanel.alarms1.%X11 := #statDataMtr.statusHMI.msgStatus.collectMaintenance;
#tempPanel.alarms1.%X12 := #statDataMtr.statusHMI.msgStatus.collectWarning;
#tempPanel.alarms1.%X13 := #statDataMtr.statusHMI.msgStatus.collectError;
#tempPanel.alarms1.%X14 := #statDataMtr.statusHMI.msgStatus.errorFlagTrip;
#tempPanel.alarms1.%X15 := #statDataMtr.statusHMI.msgStatus.errorFlagExt;
#tempPanel.alarms2.%X0 := #statDataMtr.statusHMI.msgStatus.errorTimeout;
#tempPanel.alarms2.%X1 := #statDataMtr.statusHMI.msgStatus.errorPlaus;
#tempPanel.alarms2.%X2 := #statDataMtr.statusHMI.msgStatus.opHoursLimitReached;
#tempPanel.alarms2.%X3 := #statDataMtr.statusHMI.msgStatus.switchCounterLimitReached;
#tempPanel.alarms2.%X4 := #statDataMtr.statusHMI.msgStatus.warningExt;
#tempPanel.alarms2.%X5 := #statDataMtr.statusHMI.msgStatus.autoCommand;
// Comfort symbols- transfer variables
#statSymbolMtr.identName := #statDataMtr.statusHMI.identName;
#statSymbolMtr.note := #statDataMtr.settingsHMI.note;
#statSymbolMtr.off := #statDataMtr.statusHMI.msgStatus.off;
#statSymbolMtr.on := #statDataMtr.statusHMI.msgStatus.on;
#statSymbolMtr.auto := #statDataMtr.statusHMI.msgStatus.auto;
#statSymbolMtr.local := #statDataMtr.statusHMI.msgStatus.local;
#statSymbolMtr.repair := #statDataMtr.statusHMI.msgStatus.repair;
#statSymbolMtr.collectMaintenance := #statDataMtr.statusHMI.msgStatus.collectMaintenance;
#statSymbolMtr.collectWarning := #statDataMtr.statusHMI.msgStatus.collectWarning;
#statSymbolMtr.collectError := #statDataMtr.statusHMI.msgStatus.collectError;
#statSymbolMtr.errorFlagTrip := #statDataMtr.statusHMI.msgStatus.errorFlagTrip;
#statSymbolMtr.interlock := #statDataMtr.statusHMI.msgStatus.interlock;
#statSymbolMtr.alarmsInfo1 := #statDataMtr.settingsHMI.alarmsInfo1;
#statSymbolMtr.alarmsAck := #statDataMtr.settingsHMI.alarmsAck;
#statSymbolMtr.alarms1 := #tempPanel.alarms1;
#statSymbolMtr.alarms2 := #tempPanel.alarms2;
FOR #tempPanelIndex := 1 TO #tempPanelNo DO
IF (#panels[#tempPanelIndex].identName = #statDataMtr.statusHMI.identName) THEN
#statOldIdentNames[#tempPanelIndex] := #statDataMtr.statusHMI.identName;
#statOldPanels[#tempPanelIndex] := #statDataMtr.settingsHMI;
#panels[#tempPanelIndex].mtr := #tempPanel;
#panels[#tempPanelIndex].log15 := #instFbLog15.statData;
#panels[#tempPanelIndex].opStation := #statDataMtr.settingsHMI.opStation;
#panels[#tempPanelIndex].note := #statDataMtr.settingsHMI.note;
END_IF;
END_FOR;
END_IF;
END_REGION
PLC标准化编程