如何修复我的 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