Hello,
You may be able to use Pac Display and the built in alarm notification functions such as ALARM, and ALARM CLEAR. Then have control trigger the alarm TAG variable every x seconds causing Display to send an ALARM NOTIFICATION variable back to the strategy and the strategy resets both variables to original state and starts the process over again.
As far as checking if a brains or controller is communicating, taking it a step further here we not only check that but also if the strategy is running using this method based on using the scratchpads:
Our setup consists of 10 R1’s, S1, S2 with one being a dedicated scratchpad data transfer unit receiving all data and then sending it to the other units.
There is one controller status table that is shared by all with each having a desginated table element that is constantally incrumented every time a good handshake occurs and a watchdog timer is also being reset. If there is a loss of com or the strategy is stopped alarms, bells and whistles let everyone know.
Here is the script that would be in each controller(Variables and table names would be different along with s/pad IP). It first checks the data transfer scratchpad then through it checks the others.
//CHECK IF SCRATCH PAD CONTROLLER STRATEGY IS INCRUMENTING THUS RUNNING; IF GOOD RESET TIMER WHICH IS USED TO CHK IF BELOW 30 SECS THEN OK.
//SCRATCH PAD =1 ESD=2 ALK =3 DIST=4 MCC=5 C5000=6 R1301=7 300TF=8 EMEG EVAC=9 CX801=10
STATUS_GET_DATA_XFER_SCRATCH_PAD = GetIoUnitScratchPadINT32TABLE(SCRATCH_PAD_FROM_DATA_XFER_10_10_10_33,25,0,0, CX801_TO_FROM_DATA_XFER_CONTROLLER_STAT);
STATUS_SCRATCH_PAD_IS_ALIVE_CURRENT = CX801_TO_FROM_DATA_XFER_CONTROLLER_STAT[1];
IF (STATUS_SCRATCH_PAD_IS_ALIVE_CURRENT <> STATUS_SCRATCH_PAD_IS_ALIVE_PREV) THEN
STATUS_SCRATCH_PAD_IS_ALIVE_PREV = STATUS_SCRATCH_PAD_IS_ALIVE_CURRENT;
StartTimer(TIMER_WDOG_SCRATCH_PAD_STRATEGY_RUNNING);
ENDIF
IF (TIMER_WDOG_SCRATCH_PAD_STRATEGY_RUNNING < 30) THEN
SETVARIABLETRUE(STATUS_SCRATCH_PAD_STRATEGY_RUNNING);
ELSE
SETVARIABLEFALSE(STATUS_SCRATCH_PAD_STRATEGY_RUNNING);
ENDIF
//IF SCRATCH PAD STRAT IS RUNNING THEN CHK ALL OTHER CONTROLLERS IN TABLE SHOULD BE TRUE
//IF NOT RUNNING SET ALL FALSE
IF (ISVARIABLETRUE(STATUS_SCRATCH_PAD_STRATEGY_RUNNING)) THEN
STATUS_ESD_STRATEGY_RUNNING = CX801_TO_FROM_DATA_XFER_CONTROLLER_STAT[2];
STATUS_ALK_STRATEGY_RUNNING = CX801_TO_FROM_DATA_XFER_CONTROLLER_STAT[3];
STATUS_MCC1_RACK1_STRATEGY_RUNNING = CX801_TO_FROM_DATA_XFER_CONTROLLER_STAT[5];
//SEND OUT IS ALIVE INT
IncrementVariable(STATUS_TOGGLE_CX801_IS_ALIVE);
CX801_TO_FROM_DATA_XFER_CONTROLLER_STAT[10]= STATUS_TOGGLE_CX801_IS_ALIVE;
SetIoUnitScratchPadINT32TABLE(SCRATCH_PAD_ALK_CX801_R1_10_10_10_25, 1,10,10, CX801_TO_FROM_DATA_XFER_CONTROLLER_STAT);
ELSE
SETVARIABLEFALSE(STATUS_ESD_STRATEGY_RUNNING);
SETVARIABLEFALSE(STATUS_ALK_STRATEGY_RUNNING);
SETVARIABLEFALSE(STATUS_MCC1_RACK1_STRATEGY_RUNNING);
ENDIF
Then In the Scratch Pad Data Transfer Controller I check the others and send the data for them to see
//CHECK OTHER CONTROLLERS ARE RUNNING. IF STRATEGY IS INCRUMENTING THUS RUNNING; IF GOOD RESET TIMER WHICH IS USED TO CHK IF BELOW 30 SECS THEN OK.
//DATA XFER SCRATCH PAD =1 ESD=2 ALK =3 DIST=4 MCC=5 C5000=6 R1301=7 300TF=8 EMEG EVAC=9 CX801=10
STATUS_GET_ESD_SCRATCH_PAD = GetIoUnitScratchPadINT32Table(SCRATCH_PAD_FROM_ESD_RTU1_10_10_10_13,1,2,2, DATA_XFER_TO_FROM_CONTROLLER_STAT);
STATUS_ESD_IS_ALIVE_CURRENT = DATA_XFER_TO_FROM_CONTROLLER_STAT[2];
IF (STATUS_ESD_IS_ALIVE_CURRENT <> STATUS_ESD_IS_ALIVE_PREV) THEN
STATUS_ESD_IS_ALIVE_PREV = STATUS_ESD_IS_ALIVE_CURRENT;
StartTimer(TIMER_WDOG_ESD_STRATEGY_RUNNING);
ENDIF
IF (TIMER_WDOG_ESD_STRATEGY_RUNNING > 30) THEN
DATA_XFER_TO_FROM_CONTROLLER_STAT[2] =0;
ENDIF
STATUS_GET_ALK_SCRATCH_PAD = GetIoUnitScratchPadINT32Table(SCRATCH_PAD_FROM_ALK_MISTIC1_10_10_10_9,1,3,3, DATA_XFER_TO_FROM_CONTROLLER_STAT);
STATUS_ALK_IS_ALIVE_CURRENT = DATA_XFER_TO_FROM_CONTROLLER_STAT[3];
IF (STATUS_ALK_IS_ALIVE_CURRENT <> STATUS_ALK_IS_ALIVE_PREV) THEN
STATUS_ALK_IS_ALIVE_PREV = STATUS_ALK_IS_ALIVE_CURRENT;
StartTimer(TIMER_WDOG_ALK_STRATEGY_RUNNING);
ENDIF
IF (TIMER_WDOG_ALK_STRATEGY_RUNNING > 30) THEN
DATA_XFER_TO_FROM_CONTROLLER_STAT[3] =0;
ENDIF
STATUS_GET_DIST_SCRATCH_PAD = GetIoUnitScratchPadINT32Table(SCRATCH_PAD_FROM_DIST_MISTIC2_10_10_10_11,1,4,4, DATA_XFER_TO_FROM_CONTROLLER_STAT);
STATUS_DIST_IS_ALIVE_CURRENT = DATA_XFER_TO_FROM_CONTROLLER_STAT[4];
IF (STATUS_DIST_IS_ALIVE_CURRENT <> STATUS_DIST_IS_ALIVE_PREV) THEN
STATUS_DIST_IS_ALIVE_PREV = STATUS_DIST_IS_ALIVE_CURRENT;
StartTimer(TIMER_WDOG_DIST_STRATEGY_RUNNING);
ENDIF
IF (TIMER_WDOG_DIST_STRATEGY_RUNNING > 30) THEN
DATA_XFER_TO_FROM_CONTROLLER_STAT[4] =0;
ENDIF
STATUS_GET_MCC1_RK1_SCRATCH_PAD = GetIoUnitScratchPadINT32Table(SCRATCH_PAD_FROM_MCC1_RACK1_10_10_10_28,1,5,5, DATA_XFER_TO_FROM_CONTROLLER_STAT);
STATUS_MCC1_RK1_IS_ALIVE_CURRENT = DATA_XFER_TO_FROM_CONTROLLER_STAT[5];
IF (STATUS_MCC1_RK1_IS_ALIVE_CURRENT <> STATUS_MCC1_RK1_IS_ALIVE_PREV) THEN
STATUS_MCC1_RK1_IS_ALIVE_PREV = STATUS_MCC1_RK1_IS_ALIVE_CURRENT;
StartTimer(TIMER_WDOG_MCC1_RK1_STRATEGY_RUNNING);
ENDIF
IF (TIMER_WDOG_MCC1_RK1_STRATEGY_RUNNING > 30) THEN
DATA_XFER_TO_FROM_CONTROLLER_STAT[5] =0;
ENDIF
STATUS_GET_C5000_SCRATCH_PAD = GetIoUnitScratchPadINT32Table(SCRATCH_PAD_FROM_C5000_10_10_10_21,1,6,6, DATA_XFER_TO_FROM_CONTROLLER_STAT);
STATUS_C5000_IS_ALIVE_CURRENT = DATA_XFER_TO_FROM_CONTROLLER_STAT[6];
IF (STATUS_C5000_IS_ALIVE_CURRENT <> STATUS_C5000_IS_ALIVE_PREV) THEN
STATUS_C5000_IS_ALIVE_PREV = STATUS_C5000_IS_ALIVE_CURRENT;
StartTimer(TIMER_WDOG_C5000_STRATEGY_RUNNING);
ENDIF
IF (TIMER_WDOG_C5000_STRATEGY_RUNNING > 30) THEN
DATA_XFER_TO_FROM_CONTROLLER_STAT[6] =0;
ENDIF
STATUS_GET_R1301_SCRATCH_PAD = GetIoUnitScratchPadINT32Table(SCRATCH_PAD_FROM_R1301_10_10_10_19,1,7,7, DATA_XFER_TO_FROM_CONTROLLER_STAT);
STATUS_R1301_IS_ALIVE_CURRENT = DATA_XFER_TO_FROM_CONTROLLER_STAT[7];
IF (STATUS_R1301_IS_ALIVE_CURRENT <> STATUS_R1301_IS_ALIVE_PREV) THEN
STATUS_R1301_IS_ALIVE_PREV = STATUS_R1301_IS_ALIVE_CURRENT;
StartTimer(TIMER_WDOG_R1301_STRATEGY_RUNNING);
ENDIF
IF (TIMER_WDOG_R1301_STRATEGY_RUNNING > 30) THEN
DATA_XFER_TO_FROM_CONTROLLER_STAT[7] =0;
ENDIF
STATUS_GET_TT_RC_SCRATCH_PAD = GetIoUnitScratchPadINT32Table(SCRATCH_PAD_FROM_TT_RC_10_10_10_7,1,8,8, DATA_XFER_TO_FROM_CONTROLLER_STAT);
STATUS_TT_RC_IS_ALIVE_CURRENT = DATA_XFER_TO_FROM_CONTROLLER_STAT[8];
IF (STATUS_TT_RC_IS_ALIVE_CURRENT <> STATUS_TT_RC_IS_ALIVE_PREV) THEN
STATUS_TT_RC_IS_ALIVE_PREV = STATUS_TT_RC_IS_ALIVE_CURRENT;
StartTimer(TIMER_WDOG_TT_RC_STRATEGY_RUNNING);
ENDIF
IF (TIMER_WDOG_TT_RC_STRATEGY_RUNNING > 30) THEN
DATA_XFER_TO_FROM_CONTROLLER_STAT[8] =0;
ENDIF
STATUS_GET_EMEG_EVAC_SCRATCH_PAD = GetIoUnitScratchPadINT32Table(SCRATCH_PAD_FROM_EMEG_EVAC_R1_10_10_10_23,1,9,9, DATA_XFER_TO_FROM_CONTROLLER_STAT);
STATUS_EMEG_EVAC_IS_ALIVE_CURRENT = DATA_XFER_TO_FROM_CONTROLLER_STAT[9];
IF (STATUS_EMEG_EVAC_IS_ALIVE_CURRENT <> STATUS_EMEG_EVAC_IS_ALIVE_PREV) THEN
STATUS_EMEG_EVAC_IS_ALIVE_PREV = STATUS_EMEG_EVAC_IS_ALIVE_CURRENT;
StartTimer(TIMER_WDOG_EMEG_EVAC_STRATEGY_RUNNING);
ENDIF
IF (TIMER_WDOG_EMEG_EVAC_STRATEGY_RUNNING > 30) THEN
DATA_XFER_TO_FROM_CONTROLLER_STAT[9] =0;
ENDIF
STATUS_GET_CX801_SCRATCH_PAD = GetIoUnitScratchPadINT32Table(SCRATCH_PAD_FROM_ALK_CX801_R1_10_10_10_25,1,10,10, DATA_XFER_TO_FROM_CONTROLLER_STAT);
STATUS_CX801_IS_ALIVE_CURRENT = DATA_XFER_TO_FROM_CONTROLLER_STAT[10];
IF (STATUS_CX801_IS_ALIVE_CURRENT <> STATUS_CX801_IS_ALIVE_PREV) THEN
STATUS_CX801_IS_ALIVE_PREV = STATUS_CX801_IS_ALIVE_CURRENT;
StartTimer(TIMER_WDOG_CX801_STRATEGY_RUNNING);
ENDIF
IF (TIMER_WDOG_CX801_STRATEGY_RUNNING > 30) THEN
DATA_XFER_TO_FROM_CONTROLLER_STAT[10] =0;
ENDIF
/////////////////////////////////////////////////////////SEND OUT COMPILED TABLE OF CONTROLLER STAT
//SEND OUT TO ALL OTHERS ALL CONTROLLER STATUS INCLUDING DATA TRANSFER CONTROLLER IS RUNNING
IncrementVariable(STATUS_TOGGLE_DATA_XFER_IS_ALIVE);
DATA_XFER_TO_FROM_CONTROLLER_STAT[1]= STATUS_TOGGLE_DATA_XFER_IS_ALIVE;
STATUS_SET_DATA_XFER_SCRATCH_PAD = SetIoUnitScratchPadINT32Table(SCRATCH_PAD_DATA_XFER_10_10_10_33, 25,0,0, DATA_XFER_TO_FROM_CONTROLLER_STAT);
A lot of this is repetative for all the controllers we have and you do not need a dedicated controller to function just as a scratch pad data transfer but we are quite large and method reduced our loop times.