Как создать синхронизацию для состояний контроллера в двигателях?

Синхронизацию можно использовать для создания задержки запуска или выключения одного и того же двигателя или для создания задержки включения или выключения между разными двигателями. Эта функция используется для защиты двигателей, избегая приводов с очень короткими интервалами. В аварийных ситуациях двигатели автоматически отключаются (в приоритете по времени), чтобы избежать дальнейшего повреждения.

Запустите мой код, создав подпрограмму для чтения состояний машин, которая сообщит мне, включена она или выключена. Затем создайте подпрограмму, которая считывает машины, находящиеся в состоянии тревоги, после чего я проверил, не находится ли машина в состоянии тревоги. Под этим кодом я создал процедуру, которая управляет двигателями, а затем создал процедуру для срабатывания выходов на двигателях, которые должны быть подключены, и я закончил свой код с помощью else, где она выключает двигатели.


VAR_INPUT

ENABLE      :   BOOL    := FALSE;       (*ENABLES THE BLOCK OPERATION*)

DEV_STS1    :   BOOL    := FALSE;       (*REPRESENTS MOTOR STATUS 1 ON / OFF*)
DEV_STS2    :   BOOL    := FALSE;       (*REPRESENTS MOTOR STATUS 2 ON / OFF*)
DEV_STS3    :   BOOL    := FALSE;       (*REPRESENTS MOTOR STATUS 3 ON / OFF*)
DEV_STS4    :   BOOL    := FALSE;       (*REPRESENTS MOTOR STATUS 4 ON / OFF*)
DEV_STS5    :   BOOL    := FALSE;       (*REPRESENTS MOTOR STATUS 5 ON / OFF*)
DEV_STS6    :   BOOL    := FALSE;       (*REPRESENTS MOTOR STATUS 6 ON / OFF*)

DEV_ALA1    :   BOOL    := FALSE;       (*REPRESENTS MOTOR ALARM CONDITION 1*)
DEV_ALA2    :   BOOL    := FALSE;       (*REPRESENTS MOTOR ALARM CONDITION 2*)
DEV_ALA3    :   BOOL    := FALSE;       (*REPRESENTS MOTOR ALARM CONDITION 3*)
DEV_ALA4    :   BOOL    := FALSE;       (*REPRESENTS MOTOR ALARM CONDITION 4*)
DEV_ALA5    :   BOOL    := FALSE;       (*REPRESENTS MOTOR ALARM CONDITION 5*)
DEV_ALA6    :   BOOL    := FALSE;       (*REPRESENTS MOTOR ALARM CONDITION 6*)

T_MIN_ON    :   REAL    := 0.0;         (*MINIMUM TIME ON ONE SAME MOTOR / RANGE 0.0 ~ 9999.0 **)
T_MIN_OFF   :   REAL    := 0.0;         (*MINIMUM TIME OFF OF SAME MOTOR / RANGE 0.0 ~ 9999.0*)
T_ON_ON     :   REAL    := 0.0;         (*MINIMUM TIME BETWEEN TWO PARTS OF THE SAME MOTOR / RANGE 0.0 ~ 9999.0*)
T_ON_OTHER  :   REAL    := 0.0;         (*TIME BETWEEN TURN ON DIFFERENT MOTORS / RANGE 0.0 ~ 9999.0*)
T_OFF_OTHER :   REAL    := 0.0;         (*TIME BETWEEN TURN OFF DIFFERENT MOTORS / RANGE 0.0 ~ 9999.0*)
END_VAR

VAR_OUTPUT

REQ_DEV1    :   BOOL    := FALSE;       (*STATUS D0 MOTOR 1 (COMPRESSOR) ACCORDING TO THE TIMER LOGIC*)
REQ_DEV2    :   BOOL    := FALSE;       (*STATUS D0 MOTOR 2 (COMPRESSOR) ACCORDING TO THE TIMER LOGIC*)
REQ_DEV3    :   BOOL    := FALSE;       (*STATUS D0 MOTOR 3 (COMPRESSOR) ACCORDING TO THE TIMER LOGIC*)
REQ_DEV4    :   BOOL    := FALSE;       (*STATUS D0 MOTOR 4 (COMPRESSOR) ACCORDING TO THE TIMER LOGIC*)
REQ_DEV5    :   BOOL    := FALSE;       (*STATUS D0 MOTOR 5 (COMPRESSOR) ACCORDING TO THE TIMER LOGIC*)
REQ_DEV6    :   BOOL    := FALSE;       (*STATUS D0 MOTOR 6 (COMPRESSOR) ACCORDING TO THE TIMER LOGIC*)

END_VAR

VAR

DEV_STS     :   ARRAY[1..6] OF BOOL;    (*MOTOR STATUS READING ARRAY*)
DEV_ALA     :   ARRAY[1..6] OF BOOL;    (*ARRAY READING OF MOTORS ALARMS*)  
REQ_DEV     :   ARRAY[1..6] OF BOOL;    (*ARRAY TO MANIPULATE MOTORS STATES*)

FLAG_STS    :   ARRAY[1..6] OF BOOL;    (*ARRAY FOR PREVIOUS STATUS CONTROL OF MOTORS*)

IDX         :   USINT   := 0;           (*GENERIC INDEX TO HANDLE ARRAY*)
DEV_ON      :   USINT   := 0;           (*AMOUNT OF MOTORS MUST BE TURN ON*)

T_ON_INT    :   ARRAY[1..6] OF REAL;    (*INTERNAL TIME ON A SAME MOTOR*)
T_OFF_INT   :   ARRAY[1..6] OF REAL;    (*INTERNAL TIME OFF A SAME MOTOR*)
T_CYCLE     :   ARRAY[1..6] OF REAL;    (*CYCLE TIME OF SAME MOTOR*)

END_VAR

 IF ENABLE THEN
(*==================================================================================*)
                    (*READINGS OF MOTORS STATUS*)
(*==================================================================================*)
    DEV_STS[1] := DEV_STS1;
    DEV_STS[2] := DEV_STS2;
    DEV_STS[3] := DEV_STS3;
    DEV_STS[4] := DEV_STS4;
    DEV_STS[5] := DEV_STS5;
    DEV_STS[6] := DEV_STS6;

(*==================================================================================*)
                (*READINGS OF THE MOTORS ALARM STATUS*)
(*==================================================================================*)
    DEV_ALA[1] := DEV_ALA1;
    DEV_ALA[2] := DEV_ALA2;
    DEV_ALA[3] := DEV_ALA3;
    DEV_ALA[4] := DEV_ALA4;
    DEV_ALA[5] := DEV_ALA5;
    DEV_ALA[6] := DEV_ALA6;

(*==================================================================================*)
                (*CHECK IF ANY MOTOR IS ALARMED*) 
(*==================================================================================*)
    FOR IDX := 0 TO 6 BY 1 DO
        IF DEV_ALA[IDX] = TRUE THEN
            REQ_DEV[IDX] := FALSE;
        END_IF;
    END_FOR;

(*==================================================================================*)
                (*CHECKING WHAT MOTOR SHOULD BE TURN ON*)
(*==================================================================================*)

    FOR IDX := 0 TO 6 BY 1 DO
        IF DEV_STS[IDX] = TRUE THEN
            DEV_ON := DEV_ON + 1;
        END_IF;
    END_FOR;

(*==================================================================================*)
                            (*ACTING A MOTOR*)
(*==================================================================================*)
    FOR IDX := 0 TO 6 DO
        T_CYCLE[IDX] := T_ON_INT[IDX] + T_OFF_INT[IDX];
        IF DEV_STS[IDX] = TRUE AND FLAG_STS[IDX] = FALSE THEN
            IF T_CYCLE[IDX] > T_ON_ON THEN
                IF T_ON_INT[IDX] < T_MIN_OFF THEN
                    REQ_DEV[IDX] := TRUE;
                END_IF;
            END_IF;
        END_IF;

        IF DEV_STS[IDX] = FALSE AND FLAG_STS[IDX] = TRUE THEN
            IF T_ON_INT[IDX] >= T_MIN_ON THEN
                REQ_DEV[IDX] := FALSE;
            END_IF;
        END_IF;

        IF DEV_STS[IDX] = TRUE AND FLAG_STS[IDX] = TRUE THEN
            T_ON_INT[IDX] := T_ON_INT[IDX] + 1.0;
        END_IF;
    END_FOR;


(*==================================================================================*)
                            (*LEADING OUTPUTS*)
(*==================================================================================*)

    REQ_DEV1 := REQ_DEV[1] ;
    REQ_DEV2 := REQ_DEV[2] ;
    REQ_DEV3 := REQ_DEV[3] ;
    REQ_DEV4 := REQ_DEV[4] ;
    REQ_DEV5 := REQ_DEV[5] ;
    REQ_DEV6 := REQ_DEV[6] ;

(*==================================================================================*)
                                (*FLAG*)
(*==================================================================================*)

    FLAG_STS[1] := REQ_DEV1;
    FLAG_STS[2] := REQ_DEV2;
    FLAG_STS[3] := REQ_DEV3;
    FLAG_STS[4] := REQ_DEV4;
    FLAG_STS[5] := REQ_DEV5;
    FLAG_STS[6] := REQ_DEV6;

ELSE

    REQ_DEV1 := FALSE;
    REQ_DEV2 := FALSE;
    REQ_DEV3 := FALSE;
    REQ_DEV4 := FALSE; 
    REQ_DEV5 := FALSE; 
    REQ_DEV6 := FALSE;

END_IF; 


Я еще не тестировал код. Но я обычно использую CFC для тестирования.


person Apach3    schedule 07.06.2019    source источник


Ответы (1)


Для CFC это может быть нормально, но не для ST. В ST вы должны использовать другую концепцию. У меня много вопросов к вашему коду, но позвольте мне показать вам, как я его понял, а вы будете задавать вопросы позже.

Сначала создайте тип.

TYPE MOTOR : STRUCT
        State:  BOOL;    (* State of the motor translated to DO *)
        Task:   BOOL;    (* Do we want to turn this motor off or on *)
        Alarm:  BOOL;    (* Motor alarm *)
        TimerOnMax: TP;  (* Timer to maximum work for motor *)
        TimerOnMin: TP;  (* Timer to maximum work for motor *)
        TimerOff:   TP;  (* Timer for minimum pause between work *)
        TimeOnMax: TIME; (* Maximum time for motor to work *)
        TimeOnMin: TIME; (* Minimum time for motor to work *)
        TimeOff:   TIME; (* Minimum time for motor to rest *)
    END_STRUCT
END_TYPE

Теперь определите глобальные переменные

VAR_GLOBAL
    (* Array of motors to manage *)
    stMotors: ARRAY[1.._MOTORS_NUM] OF MOTOR := [
        _MOTORS_NUM(TimeOnMax := T#1h, TimeOnMin := T#10m, TimeOff := T#30m)
    ];
END_VAR

VAR_GLOBAL CONSTANT
    _MOTORS_NUM: INT := 6; (* Number of motors in array *)
END_VAR

Инициализация может отличаться в зависимости от версии CoDeSys.

Теперь наш функциональный блок

FUNCTION_BLOCK ManageMotors
    VAR_INPUT
        ENABLE: BOOL; (* Enable motor management *)
        M_NUM: INT; (*  Number of motors to be working *)
    END_VAR
    VAR
        iCount: INT; (* Index for circle *)
        iNumOfMotors: INT; (* Number of currently working motors *)
    END_VAR

    IF NOT ENABLE THEN
        actTurnOffAll();
        actApply();
        RETURN;
    END_IF;

    actCountWroking();

    FOR iCount := 1 TO _MOTORS_NUM DO
        (* If motor in alarm state turn it off *)
        IF stMotors[iCount].Alarm AND stMotors[iCount].State THEN
            stMotors[iCount].Task := FALSE;
            iNumOfMotors := iNumOfMotors - 1;
        END_IF;

        (* If motor works longer that allowed time turn it off *)
        IF stMotors[iCount].State AND
            stMotors[iCount].Task AND
            NOT stMotors[iCount].TimerOnMax.Q
        THEN
            stMotors[iCount].Task := FALSE;
            iNumOfMotors := iNumOfMotors - 1;
        END_IF;

        (* If amout of working motors more that allowed number turn one off *)
        IF iNumOfMotors > M_NUM AND
            stMotors[iCount].State AND
            stMotors[iCount].Task AND
            NOT stMotors[iCount].TimerOnMin.Q
        THEN
            stMotors[iCount].Task := FALSE;
            iNumOfMotors := iNumOfMotors - 1;
        END_IF;

        (* If amount of working motors less then required turn one motor on *)
        IF iNumOfMotors < M_NUM AND
            NOT stMotors[iCount].State AND
            NOT stMotors[iCount].Task AND
            NOT stMotors[iCount].TimerOff.Q
        THEN
            stMotors[iCount].Task := TRUE;
            iNumOfMotors := iNumOfMotors + 1;
        END_IF;

        stMotors[iCount].TimerOnMax(
            IN := (stMotors[iCount].Task AND NOT stMotors[iCount].State),
            PT := stMotors[iCount].TimeOnMax
        );

        stMotors[iCount].TimerOnMin(
            IN := (stMotors[iCount].Task AND NOT stMotors[iCount].State),
            PT := stMotors[iCount].TimeOnMin
        );

        stMotors[iCount].TimerOff(
            IN := (NOT stMotors[iCount].Task AND stMotors[iCount].State),
            PT := stMotors[iCount].TimeOff
        );
    END_FOR;

    actApply();

    ACTION actCountWroking:
        iNumOfMotors := 0;
        FOR iCount := 1 TO _MOTORS_NUM DO
            IF stMotors[iCount].State THEN
                iNumOfMotors := iNumOfMotors + 1;
            END_IF;
        END_FOR;
    END_ACTION;

    ACTION actTurnOffAll:
        FOR iCount := 1 TO _MOTORS_NUM DO
            stMotors[iCount].Task := FALSE;
        END_FOR;
    END_ACTION;

    ACTION actApply:
        FOR iCount := 1 TO _MOTORS_NUM DO
            stMotors[iCount].State := stMotors[iCount].Task;
        END_FOR;
    END_ACTION;
END_FUNCTION_BLOCK

Я добавил несколько комментариев, но остальная часть кода должна быть понятной. Я использовал ACTION, поскольку он доступен как в CDS 2.3, так и в CDS 3.5, но если у вас версия 3.5, вы можете вместо этого использовать METHOD.

person Sergey Romanov    schedule 10.06.2019
comment
Так что в основном логика функционирования. Каждый двигатель должен учитывать все времена. Например: когда логика Родицио требует подключения более одного двигателя, они должны включаться по одному, соблюдая время T_ON_OTHER. То же самое касается остановки более чем одного двигателя в случае T_OFF_OTHER. Изображение для примера: drive.google.com/file/d /16Mwc42hX1LBwOyZE9WTWKrazQ-532KnX/ - person Apach3; 11.06.2019
comment
После включения двигателя, даже если вход запрашивает выключение (REQ_DEVn = false), двигатель должен оставаться включенным в течение времени T_MIN_ON. Точно так же, если вход, который был выключен, снова включается, двигатель должен быть выключен к моменту времени T_MIN_OFF перед повторным включением. Изображение для примера: drive.google.com/file/d/1e6BuAlXZH4XQeF0kbIGxQNasYjqFuTrr / - person Apach3; 11.06.2019
comment
Кроме того, движок должен учитывать время между двумя одинаковыми совпадениями, установленное в параметре T_ON_ON. Операция аналогична T_MIN_OFF, но у этого параметра больше время, поэтому надо иметь 2 раза видимо с одной и той же функцией. См.: drive.google.com/file/d/1eIgkR- STKEa2NejKtPLQwUk4Kaw95zfC/ - person Apach3; 11.06.2019
comment
Отсчет времени всегда запускается при моторном событии, включенном или выключенном. Когда один и тот же двигатель включается или выключается или включается другой двигатель, необходимо сравнить время, чтобы проверить состояние выхода. В случае тревоги выход немедленно деактивируется. См. предыдущий пример с эффектом тревоги: drive.google.com/file/d/1OesC4WPZLvwBwaPm0a-beqn6wXLdXWD6/ - person Apach3; 11.06.2019
comment
Итак, отвечая на вопрос о невозможности использования ХФУ. Я использую CFC только для проверочных тестов. На CODESYS я использую другую IDE поэтому на ней не работают некоторые функции CS OpenPCS. В этом видео показано, как я тестирую свои программы на .ST. drive.google.com/file/d/1CQuzkoHAcNZSonjxt_S4TIbppg- i-UDL/ - person Apach3; 12.06.2019
comment
Я понимаю. Фотки посмотреть не удалось, их нет. Но я в целом понял. Концепция все еще правильная. Вы также можете добавить другие таймеры, если вам нужно больше контроля. Основная идея в том, что мой пример содержит все необходимые вам концепции. - person Sergey Romanov; 13.06.2019