如何修复我的 PLC 设备队列中的错误
How to fix a bug on my queue of devices in PLC
我正在尝试在 .st 中创建一个简单的队列,其中包含 6 个设备,这些设备应该按队列顺序打开和关闭,只有可用的设备应该被连接。例如,我用 6 个可用设备进行了测试,然后我一个接一个地不可用,但最后一个始终不会在输出处关闭并使程序停止。
我使用 infoteam 的 OpenPCS IDE。
VAR_INPUT
ENABLE : BOOL ;
STATE_DEVICE1 : BOOL ;
STATE_DEVICE2 : BOOL ;
STATE_DEVICE3 : BOOL ;
STATE_DEVICE4 : BOOL ;
STATE_DEVICE5 : BOOL ;
STATE_DEVICE6 : BOOL ;
NUMBER_DEVICES : USINT ;
POWER_REQUEST : USINT ;
END_VAR
VAR_OUTPUT
REQUEST_DEVICE1 : BOOL ;
REQUEST_DEVICE2 : BOOL ;
REQUEST_DEVICE3 : BOOL ;
REQUEST_DEVICE4 : BOOL ;
REQUEST_DEVICE5 : BOOL ;
REQUEST_DEVICE6 : BOOL ;
END_VAR
VAR
STATE_DEVICES_ARR : ARRAY[1..6] OF BOOL ;
REQUEST_DEVICES_ARR : ARRAY[1..6] OF BOOL ;
NUMBER_DEVICES_STATE : USINT ;
NUM_DEV_REAL : USINT ;
NUM_DEV_ON : USINT ;
DEVICES_TO_ON : USINT ;
DEVICES_TO_OFF : USINT ;
P_ON : USINT := 0 ;
P_OFF : USINT := 0 ;
COUNT : USINT ;
END_VAR
IF ENABLE = TRUE THEN
STATE_DEVICES_ARR[1] := STATE_DEVICE1;
STATE_DEVICES_ARR[2] := STATE_DEVICE2;
STATE_DEVICES_ARR[3] := STATE_DEVICE3;
STATE_DEVICES_ARR[4] := STATE_DEVICE4;
STATE_DEVICES_ARR[5] := STATE_DEVICE5;
STATE_DEVICES_ARR[6] := STATE_DEVICE6;
NUM_DEV_ON := 0;
FOR COUNT := 1 TO 6 DO
IF STATE_DEVICES_ARR[COUNT] = FALSE THEN
REQUEST_DEVICES_ARR[COUNT] := FALSE;
END_IF;
IF STATE_DEVICES_ARR[COUNT] = TRUE THEN
NUMBER_DEVICES_STATE := NUMBER_DEVICES_STATE + 1;
END_IF;
IF REQUEST_DEVICES_ARR[COUNT] = TRUE THEN
DEVICES_TO_ON := DEVICES_TO_ON + 1;
END_IF;
END_FOR;
IF POWER_REQUEST > NUM_DEV_ON THEN
DEVICES_TO_ON := POWER_REQUEST-NUM_DEV_ON;
DEVICES_TO_OFF := 0;
END_IF;
IF POWER_REQUEST < NUM_DEV_ON THEN
DEVICES_TO_ON := 0;
DEVICES_TO_OFF := NUM_DEV_ON-POWER_REQUEST;
END_IF;
IF POWER_REQUEST = NUM_DEV_ON THEN
DEVICES_TO_ON := 0;
DEVICES_TO_OFF := 0;
END_IF;
IF NUMBER_DEVICES_STATE = 0 THEN
DEVICES_TO_ON := 0;
END_IF;
(*===============================================================================================================*)
(*switches the devices on or off according to FIFO logic.*)
(*===============================================================================================================*)
IF DEVICES_TO_ON > 0 THEN (* check if a device was requested to connect*)
WHILE DEVICES_TO_ON > 0 DO (* as long as there are devices to be connected *)
P_ON := P_ON + 1; (* increase the "pointer" connect devices *)
IF P_ON > 6 THEN (* check if the pointer position is at the end of the device queue *)
P_ON :=1; (* if it is at the end, it returns to the start *)
END_IF;
IF STATE_DEVICES_ARR[P_ON] = TRUE THEN (* check if the device is available to be connected *)
REQUEST_DEVICES_ARR[P_ON] := TRUE; (* connect the device of position P_ON *)
DEVICES_TO_ON := DEVICES_TO_ON-1; (* decrements the number of devices to be connected *)
END_IF;
END_WHILE;
END_IF;
IF DEVICES_TO_OFF > 0 THEN (* check if you are asked to disconnect from some device *)
WHILE DEVICES_TO_OFF > 0 DO (* as long as there are devices to be switched off *)
P_OFF := P_OFF + 1; (* increments the "pointer" to turn off devices *)
IF P_OFF > 6 THEN (* check if the pointer position is at the end of the device queue *)
P_OFF :=1; (* check if the pointer position is at the end of the device queue *)
END_IF;
IF STATE_DEVICES_ARR[P_OFF] = TRUE THEN (* check if the device is available to be switched off *)
REQUEST_DEVICES_ARR[P_OFF] := FALSE; (* disconnect device from position P_OFF *)
DEVICES_TO_OFF := DEVICES_TO_OFF-1; (* decrements the number of devices to be disconnected *)
END_IF;
END_WHILE;
END_IF;
(* I THINK THE BUG WAS HERE *)
REQUEST_DEVICE1 := REQUEST_DEVICES_ARR[1];
REQUEST_DEVICE2 := REQUEST_DEVICES_ARR[2];
REQUEST_DEVICE3 := REQUEST_DEVICES_ARR[3];
REQUEST_DEVICE4 := REQUEST_DEVICES_ARR[4];
REQUEST_DEVICE5 := REQUEST_DEVICES_ARR[5];
REQUEST_DEVICE6 := REQUEST_DEVICES_ARR[6];
END_IF;
IF ENABLE = FALSE THEN
REQUEST_DEVICE1 := FALSE;
REQUEST_DEVICE2 := FALSE;
REQUEST_DEVICE3 := FALSE;
REQUEST_DEVICE4 := FALSE;
REQUEST_DEVICE5 := FALSE;
REQUEST_DEVICE6 := FALSE;
END_IF;
;
There are many things to improve in your code. For instance:
IF REQUEST_DEVICES_ARR[COUNT] = TRUE THEN
DEVICES_TO_ON := DEVICES_TO_ON + 1;
END_IF;
这是毫无意义的,因为在它之后您立即覆盖了 DEVICES_TO_ON
并且不使用它。那你为什么要设置它?
或者你这样做
IF POWER_REQUEST > NUM_DEV_ON THEN
DEVICES_TO_ON := POWER_REQUEST-NUM_DEV_ON;
DEVICES_TO_OFF := 0;
END_IF;
但在你设置 NUM_DEV_ON
之前无处可去。
或者您有一个输入变量 NUMBER_DEVICES
但在代码中没有用到。
但一般来说,您选择了错误的方法来解决问题。
所以,首先,你必须创建一个类型
TYPE MY_DEVICE: STRUCT
Available: BOOL; (* If a device is available *)
State: BOOL; (* Current device state *)
Queue: BOOL; (* What to do with device *)
END_STRUCT
END_TYPE
然后设置全局变量
VAR_GLOBAL
garDevices: ARARY[1.._DEVICE_NUM] OF MY_DEVICE; (* Comment *)
END_VAR
VAR_GLOBAL CONSTANT
_DEVICE_NUM: USINT := 6; (* Comment *)
END_VAR
这样,您只需更改 _DEVICE_NUM
常量即可更改设备数量,而无需更改其余代码。
现在你的函数
FUNCTION QUEUE_DEVICES: BOOL
VAR_INPUT
ENABLE : BOOL;
POWER_REQUEST : USINT;
END_VAR
VAR
iDeviceOnOff: INT;
usiCount: USINT;
usiCountOnDevices: USINT;
END_VAR
(* If not enabled, set all devices to turn off and quite function *)
IF NOT ENABLE THEN
FOR usiCount TO _DEVICE_NUM DO
garDevices[usiCount].Queue := FALSE;
END_FOR;
RETURN;
END_IF;
(* Count how many devices is on already *)
usiCountOnDevices := 0;
FOR usiCount := 1 TO _DEVICE_NUM DO
IF garDevices[usiCount].State THEN
usiCountOnDevices := usiCountOnDevices + 1;
END_IF;
END_FOR;
(* Find the difference between power request and power on.
Might be negative or positive *)
iDeviceOnOff := POWER_REQUEST - usiCountOnDevices;
FOR usiCount := 1 TO _DEVICE_NUM DO
(* If device is not available for turning on or off
continue to the other device *)
IF garDevices[usiCount].Available THEN
(* if iDeviceOnOff is positive, then we have to turn on devices *)
IF iDeviceOnOff > 0 AND NOT garDevices[usiCount].Queue THEN
garDevices[usiCount].Queue := TRUE;
iDeviceOnOff := iDeviceOnOff - 1;
END_IF;
(* if iDeviceOnOff is negative we have to turn OFF devices *)
IF iDeviceOnOff < 0 AND garDevices[usiCount].Queue THEN
garDevices[usiCount].Queue := FALSE;
iDeviceOnOff := iDeviceOnOff + 1;
END_IF;
(* If iDeviceOnOff is 0 means balance is reached *)
IF iDeviceOnOff = 0 THEN
EXIT;
END_IF;
END_IF;
END_FOR;
END_FUNCTION
然后,您可以在函数的末尾添加一些其他测试。例如.
IF iDeviceOnOff > 0 THEN
_ERROR: = 'More power requested than available devices';
END_IF;
IF iDeviceOnOff < 0 THEN
_ERROR: = 'There is a power excess';
END_IF;
在 PLC 中编程与在应用程序运行一次的普通 OS 应用程序中有点不同。在我看来你有很多 while 和 for 循环,我认为这些不是必需的。
在我看来,您的程序可以从状态机的使用中获益。您还可以使用枚举类型来提高状态机的可读性。这是一个简单的概念,用于控制 PLC 中的事件序列。简单状态机示例:
PROGRAM MAIN
VAR
bPizzaToCook : BOOL;
bPizzaCooking : BOOL;
bPizzaCooked : BOOL;
bLoadPizza : BOOL;
bUnloadPizza : BOOL;
fb_t_CookTimer : TON;
iPizzasCooked : UDINT;
bBuzzer : BOOL;
iPizzaState : DINT;
sPizzaState : STRING;
END_VAR
IF bPizzaToCook AND NOT bPizzaCooking THEN
//simulates conveyor moving pizza into oven
bPizzaToCook := FALSE;
bPizzaCooking := TRUE;
END_IF
IF bLoadPizza THEN //pizza loaded onto conveyor
bLoadPizza := FALSE;
bPizzaToCook := TRUE;
END_IF
IF bUnloadPizza THEN //pizza unloaded off of conveyor
bUnloadPizza := FALSE;
bPizzaCooked := FALSE;
END_IF
CASE iOvenState OF
0 : //wait for pizza to cook
sPizzaState := ‘Waiting for pizza…’;
IF bPizzaCooking THEN
iPizzaState := 10;
END_IF
10: //cook the pizza (start the timer)
sPizzaState := ‘Baking Pizza…’
fb_t_CookTimer(INT := TRUE, PT := T#10s);
IF fb_t_CookTimer.Q THEN
fb_t_CookTimer(IN := FALSE);
iPizzaState := 20;
END_IF
20: //is there space to move pizza out of oven?
IF NOT bPizzaCooked THEN
bPizzaCooking := FALSE;
bPizzaCooked := TRUE;
bBuzzer := FALSE;
iPizzaState := 30;
ELSE //pizza burning
sPizzaState := ‘BURNING’;
bBuzzer := TRUE;
END_IF
30://pizza ready on conveyor
iPizzasCooked := iPizzasCooked + 1;
iPizzaState := 0; //reset state
ELSE
sPizzaState := ‘Invalid State!’;
END_CASE
还有很多其他状态机示例。这个来自https://www.youtube.com/watch?v=XmcXRZXPRWs
我正在尝试在 .st 中创建一个简单的队列,其中包含 6 个设备,这些设备应该按队列顺序打开和关闭,只有可用的设备应该被连接。例如,我用 6 个可用设备进行了测试,然后我一个接一个地不可用,但最后一个始终不会在输出处关闭并使程序停止。 我使用 infoteam 的 OpenPCS IDE。
VAR_INPUT
ENABLE : BOOL ;
STATE_DEVICE1 : BOOL ;
STATE_DEVICE2 : BOOL ;
STATE_DEVICE3 : BOOL ;
STATE_DEVICE4 : BOOL ;
STATE_DEVICE5 : BOOL ;
STATE_DEVICE6 : BOOL ;
NUMBER_DEVICES : USINT ;
POWER_REQUEST : USINT ;
END_VAR
VAR_OUTPUT
REQUEST_DEVICE1 : BOOL ;
REQUEST_DEVICE2 : BOOL ;
REQUEST_DEVICE3 : BOOL ;
REQUEST_DEVICE4 : BOOL ;
REQUEST_DEVICE5 : BOOL ;
REQUEST_DEVICE6 : BOOL ;
END_VAR
VAR
STATE_DEVICES_ARR : ARRAY[1..6] OF BOOL ;
REQUEST_DEVICES_ARR : ARRAY[1..6] OF BOOL ;
NUMBER_DEVICES_STATE : USINT ;
NUM_DEV_REAL : USINT ;
NUM_DEV_ON : USINT ;
DEVICES_TO_ON : USINT ;
DEVICES_TO_OFF : USINT ;
P_ON : USINT := 0 ;
P_OFF : USINT := 0 ;
COUNT : USINT ;
END_VAR
IF ENABLE = TRUE THEN
STATE_DEVICES_ARR[1] := STATE_DEVICE1;
STATE_DEVICES_ARR[2] := STATE_DEVICE2;
STATE_DEVICES_ARR[3] := STATE_DEVICE3;
STATE_DEVICES_ARR[4] := STATE_DEVICE4;
STATE_DEVICES_ARR[5] := STATE_DEVICE5;
STATE_DEVICES_ARR[6] := STATE_DEVICE6;
NUM_DEV_ON := 0;
FOR COUNT := 1 TO 6 DO
IF STATE_DEVICES_ARR[COUNT] = FALSE THEN
REQUEST_DEVICES_ARR[COUNT] := FALSE;
END_IF;
IF STATE_DEVICES_ARR[COUNT] = TRUE THEN
NUMBER_DEVICES_STATE := NUMBER_DEVICES_STATE + 1;
END_IF;
IF REQUEST_DEVICES_ARR[COUNT] = TRUE THEN
DEVICES_TO_ON := DEVICES_TO_ON + 1;
END_IF;
END_FOR;
IF POWER_REQUEST > NUM_DEV_ON THEN
DEVICES_TO_ON := POWER_REQUEST-NUM_DEV_ON;
DEVICES_TO_OFF := 0;
END_IF;
IF POWER_REQUEST < NUM_DEV_ON THEN
DEVICES_TO_ON := 0;
DEVICES_TO_OFF := NUM_DEV_ON-POWER_REQUEST;
END_IF;
IF POWER_REQUEST = NUM_DEV_ON THEN
DEVICES_TO_ON := 0;
DEVICES_TO_OFF := 0;
END_IF;
IF NUMBER_DEVICES_STATE = 0 THEN
DEVICES_TO_ON := 0;
END_IF;
(*===============================================================================================================*)
(*switches the devices on or off according to FIFO logic.*)
(*===============================================================================================================*)
IF DEVICES_TO_ON > 0 THEN (* check if a device was requested to connect*)
WHILE DEVICES_TO_ON > 0 DO (* as long as there are devices to be connected *)
P_ON := P_ON + 1; (* increase the "pointer" connect devices *)
IF P_ON > 6 THEN (* check if the pointer position is at the end of the device queue *)
P_ON :=1; (* if it is at the end, it returns to the start *)
END_IF;
IF STATE_DEVICES_ARR[P_ON] = TRUE THEN (* check if the device is available to be connected *)
REQUEST_DEVICES_ARR[P_ON] := TRUE; (* connect the device of position P_ON *)
DEVICES_TO_ON := DEVICES_TO_ON-1; (* decrements the number of devices to be connected *)
END_IF;
END_WHILE;
END_IF;
IF DEVICES_TO_OFF > 0 THEN (* check if you are asked to disconnect from some device *)
WHILE DEVICES_TO_OFF > 0 DO (* as long as there are devices to be switched off *)
P_OFF := P_OFF + 1; (* increments the "pointer" to turn off devices *)
IF P_OFF > 6 THEN (* check if the pointer position is at the end of the device queue *)
P_OFF :=1; (* check if the pointer position is at the end of the device queue *)
END_IF;
IF STATE_DEVICES_ARR[P_OFF] = TRUE THEN (* check if the device is available to be switched off *)
REQUEST_DEVICES_ARR[P_OFF] := FALSE; (* disconnect device from position P_OFF *)
DEVICES_TO_OFF := DEVICES_TO_OFF-1; (* decrements the number of devices to be disconnected *)
END_IF;
END_WHILE;
END_IF;
(* I THINK THE BUG WAS HERE *)
REQUEST_DEVICE1 := REQUEST_DEVICES_ARR[1];
REQUEST_DEVICE2 := REQUEST_DEVICES_ARR[2];
REQUEST_DEVICE3 := REQUEST_DEVICES_ARR[3];
REQUEST_DEVICE4 := REQUEST_DEVICES_ARR[4];
REQUEST_DEVICE5 := REQUEST_DEVICES_ARR[5];
REQUEST_DEVICE6 := REQUEST_DEVICES_ARR[6];
END_IF;
IF ENABLE = FALSE THEN
REQUEST_DEVICE1 := FALSE;
REQUEST_DEVICE2 := FALSE;
REQUEST_DEVICE3 := FALSE;
REQUEST_DEVICE4 := FALSE;
REQUEST_DEVICE5 := FALSE;
REQUEST_DEVICE6 := FALSE;
END_IF;
;
There are many things to improve in your code. For instance:
IF REQUEST_DEVICES_ARR[COUNT] = TRUE THEN
DEVICES_TO_ON := DEVICES_TO_ON + 1;
END_IF;
这是毫无意义的,因为在它之后您立即覆盖了 DEVICES_TO_ON
并且不使用它。那你为什么要设置它?
或者你这样做
IF POWER_REQUEST > NUM_DEV_ON THEN
DEVICES_TO_ON := POWER_REQUEST-NUM_DEV_ON;
DEVICES_TO_OFF := 0;
END_IF;
但在你设置 NUM_DEV_ON
之前无处可去。
或者您有一个输入变量 NUMBER_DEVICES
但在代码中没有用到。
但一般来说,您选择了错误的方法来解决问题。
所以,首先,你必须创建一个类型
TYPE MY_DEVICE: STRUCT
Available: BOOL; (* If a device is available *)
State: BOOL; (* Current device state *)
Queue: BOOL; (* What to do with device *)
END_STRUCT
END_TYPE
然后设置全局变量
VAR_GLOBAL
garDevices: ARARY[1.._DEVICE_NUM] OF MY_DEVICE; (* Comment *)
END_VAR
VAR_GLOBAL CONSTANT
_DEVICE_NUM: USINT := 6; (* Comment *)
END_VAR
这样,您只需更改 _DEVICE_NUM
常量即可更改设备数量,而无需更改其余代码。
现在你的函数
FUNCTION QUEUE_DEVICES: BOOL
VAR_INPUT
ENABLE : BOOL;
POWER_REQUEST : USINT;
END_VAR
VAR
iDeviceOnOff: INT;
usiCount: USINT;
usiCountOnDevices: USINT;
END_VAR
(* If not enabled, set all devices to turn off and quite function *)
IF NOT ENABLE THEN
FOR usiCount TO _DEVICE_NUM DO
garDevices[usiCount].Queue := FALSE;
END_FOR;
RETURN;
END_IF;
(* Count how many devices is on already *)
usiCountOnDevices := 0;
FOR usiCount := 1 TO _DEVICE_NUM DO
IF garDevices[usiCount].State THEN
usiCountOnDevices := usiCountOnDevices + 1;
END_IF;
END_FOR;
(* Find the difference between power request and power on.
Might be negative or positive *)
iDeviceOnOff := POWER_REQUEST - usiCountOnDevices;
FOR usiCount := 1 TO _DEVICE_NUM DO
(* If device is not available for turning on or off
continue to the other device *)
IF garDevices[usiCount].Available THEN
(* if iDeviceOnOff is positive, then we have to turn on devices *)
IF iDeviceOnOff > 0 AND NOT garDevices[usiCount].Queue THEN
garDevices[usiCount].Queue := TRUE;
iDeviceOnOff := iDeviceOnOff - 1;
END_IF;
(* if iDeviceOnOff is negative we have to turn OFF devices *)
IF iDeviceOnOff < 0 AND garDevices[usiCount].Queue THEN
garDevices[usiCount].Queue := FALSE;
iDeviceOnOff := iDeviceOnOff + 1;
END_IF;
(* If iDeviceOnOff is 0 means balance is reached *)
IF iDeviceOnOff = 0 THEN
EXIT;
END_IF;
END_IF;
END_FOR;
END_FUNCTION
然后,您可以在函数的末尾添加一些其他测试。例如.
IF iDeviceOnOff > 0 THEN
_ERROR: = 'More power requested than available devices';
END_IF;
IF iDeviceOnOff < 0 THEN
_ERROR: = 'There is a power excess';
END_IF;
在 PLC 中编程与在应用程序运行一次的普通 OS 应用程序中有点不同。在我看来你有很多 while 和 for 循环,我认为这些不是必需的。
在我看来,您的程序可以从状态机的使用中获益。您还可以使用枚举类型来提高状态机的可读性。这是一个简单的概念,用于控制 PLC 中的事件序列。简单状态机示例:
PROGRAM MAIN
VAR
bPizzaToCook : BOOL;
bPizzaCooking : BOOL;
bPizzaCooked : BOOL;
bLoadPizza : BOOL;
bUnloadPizza : BOOL;
fb_t_CookTimer : TON;
iPizzasCooked : UDINT;
bBuzzer : BOOL;
iPizzaState : DINT;
sPizzaState : STRING;
END_VAR
IF bPizzaToCook AND NOT bPizzaCooking THEN
//simulates conveyor moving pizza into oven
bPizzaToCook := FALSE;
bPizzaCooking := TRUE;
END_IF
IF bLoadPizza THEN //pizza loaded onto conveyor
bLoadPizza := FALSE;
bPizzaToCook := TRUE;
END_IF
IF bUnloadPizza THEN //pizza unloaded off of conveyor
bUnloadPizza := FALSE;
bPizzaCooked := FALSE;
END_IF
CASE iOvenState OF
0 : //wait for pizza to cook
sPizzaState := ‘Waiting for pizza…’;
IF bPizzaCooking THEN
iPizzaState := 10;
END_IF
10: //cook the pizza (start the timer)
sPizzaState := ‘Baking Pizza…’
fb_t_CookTimer(INT := TRUE, PT := T#10s);
IF fb_t_CookTimer.Q THEN
fb_t_CookTimer(IN := FALSE);
iPizzaState := 20;
END_IF
20: //is there space to move pizza out of oven?
IF NOT bPizzaCooked THEN
bPizzaCooking := FALSE;
bPizzaCooked := TRUE;
bBuzzer := FALSE;
iPizzaState := 30;
ELSE //pizza burning
sPizzaState := ‘BURNING’;
bBuzzer := TRUE;
END_IF
30://pizza ready on conveyor
iPizzasCooked := iPizzasCooked + 1;
iPizzaState := 0; //reset state
ELSE
sPizzaState := ‘Invalid State!’;
END_CASE
还有很多其他状态机示例。这个来自https://www.youtube.com/watch?v=XmcXRZXPRWs