Please ensure Javascript is enabled for purposes of website accessibility
Powered by Zoomin Software. For more details please contactZoomin

AVEVA Enterprise SCADA

Extend the generation of stateless alarms to non-baseline tables

Extend the generation of stateless alarms to non-baseline tables

  • Last UpdatedApr 24, 2025
  • 4 minute read

To extend the generation of stateless alarms to non-baseline tables

  1. Create a new registered function called ProcessIndefiniteRTNAlarm.

    This function issues the stateless alarm against the child point. It checks the alarm inhibits and the current flashing state of the child record to determine how to add the alarm against the record.

    The following example is the code for the ProcessIndefiniteRTNAlarm function where alarm suppression is extended to rate points:

    /*****************************************************************************

    * Function: " RATE_processIndefiniteRTNAlarm"

    ***

    ** Purpose: Generate an indefinite RTN alarm and event for rate point

    ** This code is only called once the first time a point enters an indefinite alarm suppression state.

    **

    ** Output: Returns true if it added an alarm.

    **

    */

    static oas_boolean_t RATE_processIndefiniteRTNAlarm (

    slot ptnum,

    unsigned short prevChildState,

    oas_boolean_t bPointAlreadyLocked)

    {

    almsumMsg almMsg = { 0 };

    char *palmMsg = NULL;

    char *almCategory = NULL;

    oas_boolean_t persistAlm = OAS_FALSE;

    oas_boolean_t flash = OAS_FALSE;

    oas_boolean_t inAlm = OAS_FALSE;

    sev_enum severity = SEV_ENUM_LOW;

    oas_boolean_t isManualEntry = OAS_FALSE;

    double currate = 0;

    void *vpRec = NULL;

    void *vpFlag = NULL;

    oas_boolean_t bAddedNewFlashingIndefRTNAlarm = OAS_FALSE;

    alminh_types highestPrioritySup = ALMINH_ALM; /* alarm suppression type */

    RealTimeTable rtdbTable("rate");

    RealTimeRecord rtdbRec(rtdbTable, ptnum);

    RealTimeDoubleField fieldObj(rtdbTable, "currate");

    RealTimeField rtfFlag("rate", "flag");

    RealTimeEnumType pointtypeEnum("pointtype_enum");

    if (rtdbTable.Lock(REQ_SAFE_READ, LOCK_TIMEOUT) == OAS_FALSE)

    {

    cpu_debug(CPU_DEBUG_ERROR, " RATE_processIndefiniteRTNAlarm (): Failed to lock realtime table %s. Indefinite RTN alarm not generated",

    (char*)dbpnam(DATABASE_RATE));

    return OAS_FALSE;

    }

    if (bPointAlreadyLocked == OAS_TRUE)

    {

    // Another process already had a record lock on this point.

    // We only need to attach to the record.

    rtdbRec.AttachInsteadOfLock(REQ_SAFE_WRITE); // PARENT_ALMSUP - change to safe write

    }

    else

    {

    // Get a new lock on the record

    if (rtdbRec.Lock(REQ_SAFE_WRITE, ptnum, LOCK_TIMEOUT) == OAS_FALSE) // PARENT_ALMSUP - change to safe write

    {

    rtdbTable.UnLock();

    return OAS_FALSE;

    }

    }

    rtdbRec.ReadDouble(fieldObj, currate);

    rtdbRec.GetRecordPointer(vpRec);

    rtdbRec.GetPointerToField(rtfFlag, vpFlag);

    if (rtdbRec.ReadUChar("pointtype") == pointtypeEnum.GetEnumValueForConstant("PT_MANUALENTRY"))

    {

    isManualEntry = OAS_TRUE;

    }

    else

    {

    isManualEntry = OAS_FALSE;

    }

    palmMsg = &almMsg[0];

    // Find how the alarm should be generated.

    hilowstate almState = HILOW_OK; // The alarm state is HILOW_OK.

    getHiloAlarmState("rate", rtdbRec.ReadString("name").Get(), "", currate, rtdbRec.ReadString("runits").Get(),

    static_cast<flags*>(vpFlag), almState, &palmMsg, &almCategory, &persistAlm, &flash,

    &inAlm, OAS_TRUE, isManualEntry);

    // Find out the current state of the point.

    almsumMsg tempstring = { 0 };

    oas_boolean_t tempflash;

    oas_boolean_t tempinalarm;

    oas_boolean_t alarmexists = getAlarmInfoAndFlashState("rate", rtdbRec.ReadString("name").Get(), "", almCategory, tempstring, &tempflash, &tempinalarm);

    internaltime currentTimeStamp = { 0 };

    osvtim(&currentTimeStamp);

    // Localize the almsuppression specific RTN alarm message

    genfmtmsg(KRN_ALMSUP_INDEF_RTN, almMsg, sizeof(almMsg));

    // Now generate RTN alarm and event

    severity = krpsev(rtdbRec.ReadInt("flag.bmsg") + HILOW_OK);

    // Issue indefinite RTN if we came from abnormal state (not HILOW_OK)

    // OR

    // issue RTN if we came from normal, but there is an unacknowledged normal alarm we need to replace with indefinite RTN alarm

    cpu_debug(CPU_DEBUG_VERB, "ProcessRateRTNAlarm: point: %s, prev_inAlm: %d, flag.alminh: %d, tempflash: %d, tempinalarm: %d, alarmexists: %d",

    rtdbRec.ReadString("name").Get(), (prevChildState != HILOW_OK), rtdbRec.ReadUChar("flag.alminh"), tempflash, tempinalarm, alarmexists);

    // Don't need to replace/remove an alarm if it doesn't exist

    if (alarmexists == TRUE)

    {

    if (((prevChildState != HILOW_OK) && !rtdbRec.ReadUChar("flag.alminh")) ||

    ((prevChildState == HILOW_OK) && tempflash && !tempinalarm && !rtdbRec.ReadUChar("flag.alminh")))

    {

    // We don't want the indefinite RTN alarms to be persistent (ignore Sustain COS setting)

    persistAlm = OAS_FALSE;

    // If the previous existing alarm is not flashing, then we remove the alarm instead of adding a replacement indefRTN alarm.

    // (Calling addDBalarm with persistAlm is false and flashing is false is the same as deleting an alarm.)

    if (tempflash == OAS_FALSE)

    {

    flash = OAS_FALSE;

    }

    if (flash == OAS_TRUE)

    {

    // If the point was in manual mode, or flag.clrinh, flash might have been set false separately from the if statement just above.

    bAddedNewFlashingIndefRTNAlarm = OAS_TRUE;

    }

    if (!addDBalarm((char*)dbpnam(DATABASE_RATE), DATABASE_RATE, rtdbRec.ReadString("name").Get(), ptnum, "", rtdbRec.ReadInt("rtu"),

    rtdbRec.ReadString("description").Get(), rtdbRec.ReadInt("group"), almCategory, almMsg, vpRec, static_cast<flags*>(vpFlag),

    persistAlm, flash, severity, 0, OAS_FALSE,

    &currentTimeStamp, getHostName(), rtdbRec.ReadInt("dataset"), OAS_TRUE))

    {

    cpu_debug(CPU_DEBUG_ERROR, "GenerateIndefRTNAlarm(): Failed to generate indefinite RTN alarm for table %s, record %s",

    (char*)dbpnam(DATABASE_RATE), rtdbRec.ReadString("name").Get());

    }

    }

    // Add an event, even if we removed the alarm instead of replaced it.

    if (!rtdbRec.ReadUChar("flag.cevinh"))

    {

    EventSpoolObj anaEvent;

    if (!persistAlm && !flash)

    {

    //non-persistent non flashing alarms are inhibited

    highestPrioritySup = getHigherPrioritySuppression(

    highestPrioritySup, ALMINH_INH);

    }

    EVENTSPOOLER_SEND_ALARM_FILTER(anaEvent, OPEVENT_TELEM_ALARM, DATABASE_ALM,

    rtdbRec.ReadUChar("flag.testmode"), highestPrioritySup, get_spooler(rtdbRec.ReadInt("group"), SPL_MSG), &currentTimeStamp,

    "rate", rtdbRec.ReadString("name").Get(), rtdbRec.ReadString("description").Get(), rtdbRec.ReadInt("rtu"), rtdbRec.ReadInt("group"),

    almMsg, NULL, NULL, severity, COLOR_GREEN, rtdbRec.ReadInt("dataset"), OAS_TRUE,

    DATABASE_RATE, ptnum, almCategory, HILOW_OK);

    }

    }

    if (bPointAlreadyLocked == OAS_TRUE)

    {

    rtdbRec.DetachInsteadOfUnLock();

    }

    else

    {

    rtdbRec.UnLock();

    }

    rtdbTable.UnLock();

    return bAddedNewFlashingIndefRTNAlarm;

    }

  2. Update the alarm suppression registration call with the name of the new function.

    Notes: If you do not register the function when the configuration setting is enabled, an error message is printed to the oaserrlog.log similar to the following when indefinite parent alarming is triggered against a child point: Attempting to call an unregistered ProcessIndefiniteRTNAlarm function for table:<table_num_of_table>.

    The system logs this message because it does not expect to call the function unless Indefinite Parent Alarm Suppression is enabled. This can result in unpredictable alarming behavior in the system. The message is only printed to the log once, since printing the log with every krunch would produce too many messages and result in an unreadable log.

    The following example displays the code for a registration call where the alarm suppression is extended to rate points. The RATE_processIndefiniteRTNAlarm function name has been added:

    //------------------------------------------------------------------------------

    //Function: "ALMSUP_Register"

    //

    // Purpose: Alarm suppression runtime support Registration routine

    //

    extern "C" LIBAPP void

    ALMSUP_Register(

    AlmSuppressionSupport *pAlmSupport)

    {

    pAlmSupport->Register(DATABASE_RATE,

    RATE_ConvertStateStringToNumb,

    RATE_ConvertStateNumbToString,

    RATE_GetStateNumbFromRecord,

    RATE_RegenUnsuppressedAlarm,

    RATE_processIndefiniteRTNAlarm);

    }

  3. Place both methods in an assembly within %DNADataRoot%\RealTime\bin.

  4. Create a JSON file in %DNADataRoot%\RealTime\config\settings that will notify RealTime to load your assembly when starting the service.

    The JSON file should take the following format:

    {

    "RealTime": {

    "Extensions": {

    "Alarm": {

    "assemblyName.dll": true

    }

    }

    }

    }

  5. Restart the RealTime service.