Для 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
.