Reputation: 731
Imagine that we have independent boolean variables that can occur independently. Now, if at least one of the variables occurs for a certain period of time, an alert will be activated. The common solution is that we use a timer for each variable.
Is there an optimal solution that can only be used with a single timer?
Example for 2 variables with 1 second as passed time:
VAR
Timer1,Timer2:TON;
bVar1,bVar2:BOOL;
tSetDelay:TIME:=T#1S;
Alarm:BOOL;
END_VAR
Timer1(IN:=bVar1,PT:=tSetDelay);
Timer2(IN:=bVar2,PT:=tSetDelay);
IF Timer1.Q RO Timer2.Q THEN
Alarm:=TRUE;
END_IF
If we use OR
it won't be true
Timer(IN:=bVar1 OR bVar2,PT:=tSetDelay);
cause the vars may have overlap in the same tSetDelay
time, it means that they may happen less than the delay, but the timer output be true.
In this example, we have only 2 variables, but if we have many more variables it will be more important to find a better solution.
Upvotes: 0
Views: 329
Reputation: 3080
You cannot do this without individual timers. Here is how I would approach this.
Set global constant and variables
VAR_GLOBAL
glbEvents: ARRAY[1..c_EventsNum] OF stMyEvents; (* Events array *)
END_VAR
VAR_GLOBAL CONSTANT
c_EventsNum: INT := 3; (* Number of events *)
END_VAR
Now you can map glbEvents[1].State
to inputs of PLC
Define new structure
TYPE stMyEvents : STRUCT
State : BOOL; (* Event current state *)
StateM : BOOL; (* Memmory of state in previouse cycle to get the trigger *)
Timer: TON := (PT := T#1S); (* Timer *)
END_STRUCT
END_TYPE
Create function
FUNCTION ProcessEvents : BOOL
VAR
iCount: INT; (* Counter *)
END_VAR
FOR iCount := 1 TO c_EventsNum DO
glbEvents[iCount].Timer(IN := glbEvents[iCount].State);
IF glbEvents[iCount].Timer.Q THEN
ProcessEvents := TRUE;
EXIT;
END_IF;
END_FOR;
END_FUNCTION
Implementation
PROGRAM PLC_PRG
VAR
xAlarm: BOOL; (* Alarm *)
END_VAR
IF ProcessEvents() THEN
// Alarm happened
END_IF;
END_PROGRAM
With this approach although you do not have 1 Timer, you have certain level of abstraction that makes it more flexible to support and modify.
But if you absolutely do not want to have so many TON timers, you can create your own timer. It will be single timer in one FB
VAR_GLOBAL
glbEvents: ARRAY[1..c_EventsNum] OF stMyEvents; (* Events array *)
END_VAR
VAR_GLOBAL CONSTANT
c_EventsNum: INT := 3; (* Number of events *)
END_VAR
TYPE stMyEvents : STRUCT
State : BOOL; (* Event current state *)
StateM : BOOL; (* Memmory of state in previouse cycle to get the trigger *)
TimeM: TIME; (* Memmory of event start time *)
END_STRUCT
END_TYPE
FUNCTION_BLOCK ProcessEvents
VAR
iCount: INT; (* Counter *)
END_VAR
VAR_OUTPUT
Q: BOOL; (* Impulse if alarm *)
END_VAR
Q := FALSE;
FOR iCount := 1 TO c_EventsNum DO
(* Get raising edge and save the timer *)
IF glbEvents[iCount].State AND NOT glbEvents[iCount].StateM THEN
glbEvents[iCount].TimeM := TIME();
END_IF;
glbEvents[iCount].StateM := glbEvents[iCount].State;
(* If event is low reset timer *)
IF NOT glbEvents[iCount].State THEN
glbEvents[iCount].TimeM := T#0S;
END_IF;
(* if more than a second make impuls on Q *)
IF (glbEvents[iCount].TimeM > T#0S) AND ((TIME() - glbEvents[iCount].TimeM) >= T#1S) THEN
Q := TRUE;
glbEvents[iCount].TimeM := T#0S;
END_IF;
END_FOR;
END_FUNCTION_BLOCK
PROGRAM PLC_PRG
VAR
fbeventProcess: ProcessEvents; (* function block *)
END_VAR
fbeventProcess();
IF fbeventProcess.Q THEN
// Alarm happened
END_IF;
END_PROGRAM
Upvotes: 1
Reputation: 1018
It it not possible to manage this with a single timer. Given the that you want to ensure that the triggering source is a single variable (without any logical interference from additional variables), each variable must be compared against it own history (timer).
Recommended path forward would be to build an Alarm function block that handles the majority of the logic here in a consistent manner. An example of this kind of function block is below:
VAR_INPUT
monitor : ARRAY [ 0..7 ] OF POINTER TO BOOL;
duration : TIME;
END_VAR
VAR_OUTPUT
alarm : BOOL;
END_VAR
VAR
timers : ARRAY [ 0..7 ] OF TON;
END_VAR
VAR_TEMP
i : UDINT;
END_VAR
alarm := false;
FOR i := 0 TO 7 BY 1 DO
// Only run process if linked boolean
IF monitor[ i ] <> 0 THEN
timers[ i ]( in := monitor[ i ]^,
pt := duration );
alarm := timers[ i ].Q OR alarm;
END_IF
END_FOR
VAR
// Alarm flags
flag_1, flag_2, flag_3
: BOOL;
// Pointers to alarm flags
flag_array : ARRAY [ 0..7 ] OF POINTER TO BOOL
:=[ ADR( flag_1 ),
ADR( flag_2 ),
ADR( flag_3 ) ];
// Monitoring flags
monitor : fb_AlarmMonitor
:=( monitor := flag_array,
duration := T#10S );
END_VAR
monitor();
Upvotes: 1