BASELINE JSON
{
"sources": [
{
"name": "blocks/FB_SafetyApproval.scl",
"source": "FUNCTION_BLOCK \"FB_SafetyApproval\"\n{ S7_Optimized_Access := 'TRUE' }\nVERSION : 0.1\n VAR_INPUT\n NotHaltOk : Bool; // 急停回路正常(TRUE = 允许)\n ThreePosSwitch : Bool; // 三位置使能开关压下(中间位置)\n ActivitySensor : Bool; // 墙面支架检测到有人操作\n Bit0 : Bool; // BCD 位 0,权值 1\n Bit1 : Bool; // BCD 位 1,权值 2\n Bit2 : Bool; // BCD 位 2,权值 4\n Bit3 : Bool; // BCD 位 3,权值 8\n SwitchRight : Bool; // 右手点动开关\n SwitchLeft : Bool; // 左手点动开关\n LedTakt : Bool; // LED 闪烁节拍\n BcdOffset : Int; // BCD 偏移(工位号)\n END_VAR\n\n VAR_OUTPUT\n LedRed : Bool; // 急停未复位时常亮\n LedGreen : Bool; // 允许操作时亮/闪\n OutRight : Bool; // 右输出\n OutLeft : Bool; // 左输出\n BcdNumber : Int := 0; // 当前 BCD 编号\n LeftChannel : Int := 0; // 当前选中的左工位号(0 = 无)\n RightChannel : Int := 0; // 当前选中的右工位号(0 = 无)\n OutputLeft : Array[1..16] of Bool; // 左工位单路输出\n OutputRight : Array[1..16] of Bool; // 右工位单路输出\n END_VAR\n\n VAR\n Release : Bool := FALSE; // 使能释放锁存\n ThreePosPrev : Bool; // 三位置开关上一周期\n LeftPrev : Bool; // 左开关上一周期\n RightPrev : Bool; // 右开关上一周期\n BcdPrev : Int := 0; // 上一周期 BCD 值\n BcdValue : Int := 0; // 本周期 BCD 值\n OutLeftInt : Bool := FALSE;\n OutRightInt : Bool := FALSE;\n i : Int;\n END_VAR\n\n VAR_TEMP\n ThreePosRising : Bool;\n LeftRising : Bool;\n RightRising : Bool;\n BcdChanged : Bool;\n END_VAR\nBEGIN\n REGION BCD 编号\n // 4 位 BCD 输入加偏移 = 工位号;每一位的权值是 1/2/4/8。\n #BcdValue := #BcdOffset;\n IF #Bit0 THEN #BcdValue := #BcdValue + 1; END_IF;\n IF #Bit1 THEN #BcdValue := #BcdValue + 2; END_IF;\n IF #Bit2 THEN #BcdValue := #BcdValue + 4; END_IF;\n IF #Bit3 THEN #BcdValue := #BcdValue + 8; END_IF;\n #BcdChanged := #BcdValue <> #BcdPrev;\n #BcdPrev := #BcdValue;\n #BcdNumber := #BcdValue;\n END_REGION\n\n REGION 使能释放\n // 三位置开关的上升沿 + 有人 + 急停正常 + 编号没变,才允许操作。\n #ThreePosRising := #ThreePosSwitch AND NOT #ThreePosPrev;\n #ThreePosPrev := #ThreePosSwitch;\n\n // 工位号变化时,已经使能的输出必须先撤销;首次上电 BCD 变化不阻塞使能。\n IF #BcdChanged AND #Release THEN\n #Release := FALSE;\n END_IF;\n\n IF #ThreePosRising AND #ActivitySensor AND #NotHaltOk THEN\n #Release := TRUE;\n END_IF;\n\n // 松开使能开关或急停断开,都必须重新按下使能开关。\n IF NOT #ThreePosSwitch OR NOT #NotHaltOk THEN\n #Release := FALSE;\n END_IF;\n END_REGION\n\n REGION LED 指示\n // 急停不正常:红灯常亮;正常时绿灯按“未使能闪烁 / 已使能常亮”显示。\n // 放在释放逻辑之后,使能生效的同一周期绿灯就常亮。\n #LedRed := NOT #NotHaltOk;\n #LedGreen := #NotHaltOk AND #ActivitySensor AND\n ((NOT #ThreePosSwitch AND #LedTakt) OR (#ThreePosSwitch AND #Release));\n END_REGION\n\n REGION 左右点动输出\n // 左/右都取上升沿,且互锁:另一边按着时不能动。\n #LeftRising := #SwitchLeft AND NOT #LeftPrev;\n #LeftPrev := #SwitchLeft;\n IF #LeftRising AND #Release AND #NotHaltOk AND NOT #SwitchRight THEN\n #OutLeftInt := TRUE;\n ELSIF NOT #SwitchLeft OR NOT #Release THEN\n #OutLeftInt := FALSE;\n END_IF;\n\n #RightRising := #SwitchRight AND NOT #RightPrev;\n #RightPrev := #SwitchRight;\n IF #RightRising AND #Release AND #NotHaltOk AND NOT #SwitchLeft THEN\n #OutRightInt := TRUE;\n ELSIF NOT #SwitchRight OR NOT #Release THEN\n #OutRightInt := FALSE;\n END_IF;\n\n #OutLeft := #OutLeftInt;\n #OutRight := #OutRightInt;\n END_REGION\n\n REGION 单路输出\n // 只有当前工位号对应的那一路输出跟随左/右总输出;1..16 全部赋值。\n #LeftChannel := 0;\n #RightChannel := 0;\n FOR #i := 1 TO 16 DO\n #OutputLeft[#i] := (#BcdNumber = #i) AND #OutLeft;\n #OutputRight[#i] := (#BcdNumber = #i) AND #OutRight;\n IF #OutputLeft[#i] THEN\n #LeftChannel := #i;\n END_IF;\n IF #OutputRight[#i] THEN\n #RightChannel := #i;\n END_IF;\n END_FOR;\n END_REGION\nEND_FUNCTION_BLOCK\n"
}
],
"scenario": {
"docs": [
"FB_SafetyApproval.scl"
],
"block": "FB_SafetyApproval",
"tickMs": 10,
"seed": {
"NotHaltOk": true,
"ThreePosSwitch": false,
"ActivitySensor": true,
"Bit0": true,
"Bit1": false,
"Bit2": false,
"Bit3": false,
"SwitchRight": false,
"SwitchLeft": false,
"LedTakt": true,
"BcdOffset": 0
},
"always": [
"!(OutLeft && OutRight)"
],
"scenarios": [
{
"name": "急停不正常:红灯亮,不允许操作",
"steps": [
{
"set": {
"NotHaltOk": false,
"ThreePosSwitch": true
},
"expect": {
"LedRed": true,
"LedGreen": false,
"OutLeft": false,
"OutRight": false
}
}
]
},
{
"name": "正常上电:绿灯闪烁,按下使能后常亮",
"steps": [
{
"set": {
"NotHaltOk": true,
"ThreePosSwitch": false
},
"expect": {
"LedRed": false,
"LedGreen": true
}
},
{
"set": {
"ThreePosSwitch": true
},
"expect": {
"LedGreen": true,
"BcdNumber": 1
}
},
{
"set": {
"ThreePosSwitch": true
},
"expect": {
"LedGreen": true
}
}
]
},
{
"name": "工位 1 左点动:只有第 1 路输出",
"steps": [
{
"set": {
"ThreePosSwitch": true
}
},
{
"set": {
"SwitchLeft": true
},
"expect": {
"OutLeft": true,
"OutRight": false,
"LeftChannel": 1,
"RightChannel": 0
}
},
{
"set": {
"SwitchLeft": false
},
"expect": {
"OutLeft": false,
"LeftChannel": 0
}
}
]
},
{
"name": "左右互锁:左手按住时右手无效",
"steps": [
{
"set": {
"ThreePosSwitch": true
}
},
{
"set": {
"SwitchLeft": true
},
"expect": {
"OutLeft": true
}
},
{
"set": {
"SwitchRight": true
},
"expect": {
"OutLeft": true,
"OutRight": false
}
},
{
"set": {
"SwitchLeft": false
},
"expect": {
"OutLeft": false
}
},
{
"set": {
"SwitchRight": false
}
},
{
"set": {
"SwitchRight": true
},
"expect": {
"OutRight": true,
"OutLeft": false
}
}
]
},
{
"name": "切换工位号:释放使能,必须重新按下",
"steps": [
{
"set": {
"ThreePosSwitch": true
}
},
{
"set": {
"SwitchLeft": true
},
"expect": {
"OutLeft": true
}
},
{
"set": {
"SwitchLeft": false,
"Bit1": true
},
"expect": {
"BcdNumber": 3,
"OutLeft": false
}
},
{
"set": {
"SwitchLeft": true
},
"expect": {
"OutLeft": false
}
},
{
"set": {
"SwitchLeft": false,
"ThreePosSwitch": false
}
},
{
"set": {
"ThreePosSwitch": true
}
},
{
"set": {
"SwitchLeft": true
},
"expect": {
"OutLeft": true,
"LeftChannel": 3
}
}
]
},
{
"name": "偏移 1 + BCD 15 = 工位 16:第 16 路输出",
"steps": [
{
"set": {
"BcdOffset": 1,
"Bit0": true,
"Bit1": true,
"Bit2": true,
"Bit3": true
}
},
{
"set": {
"ThreePosSwitch": true
},
"expect": {
"BcdNumber": 16
}
},
{
"set": {
"SwitchLeft": true
},
"expect": {
"OutLeft": true,
"LeftChannel": 16
}
}
]
}
]
}
}