Alarming from within a module

I am needing to create alarms via multiple modules that we are developing for our Ignition projects. Thus far, I have been able to add a journal entry to the Alarm Journal from the module’s Gateway Context and the alarm reports back in the module that it is Active, Unacknowledged. However, that alarm doesn’t show up as a System alarm in the Alarm Journal or the System’s Gateway alarm tags.

What am I missing?

  1. Get GatewayContext.getAlarmJournal
  2. Create AlarmEvent and EventData
  3. Call AlarmEvent.active(EventData)
  4. Call AlarmJournal.storeEvent(AlarmEvent)

I may have messed up some of the PseudoCode, but any help would be appreciated.

1 Like

welh the pseudocode is kinda lacking to help xd
store event also requires transition state, maybe thats why idk. you’ll have to show some more code

I found the code once I got in the office

public void raiseAlarm(String displayPath, AlarmPriority alarmPriority, AlarmStateTransition alarmStateTransition, String eventMessage, Integer eventValue) {
        try {
            var addr = InetAddress.getLocalHost();
            String hostname = addr.getHostName();

            QualifiedPath.Builder qualifiedPathBuilder = new QualifiedPath.Builder();
            qualifiedPathBuilder.setAlarm("ATOM Alarm");
            qualifiedPathBuilder.setEvent(eventMessage);
            qualifiedPathBuilder.setMachine(hostname);
            qualifiedPathBuilder.setProvider("LIFT");
            qualifiedPathBuilder.setSource("Dematic ATOM Module");
            qualifiedPathBuilder.setUser("ATOM");
            QualifiedPath qualifiedPath = qualifiedPathBuilder.build();

            AlarmJournalManager journalManager = gatewayContext.getAlarmManager().getJournalManager();
            BasicAlarmEvent alarmEvent = new BasicAlarmEvent(qualifiedPath, displayPath, alarmPriority);
            DateTime alarmDate = DateTime.now();
            BasicPropertySet basicPropertySet = new BasicPropertySet();
            basicPropertySet.set(CommonAlarmProperties.EventState, alarmStateTransition);
            basicPropertySet.set(CommonAlarmProperties.EventValue, eventValue);
            basicPropertySet.set(CommonAlarmProperties.Priority, alarmPriority);
            basicPropertySet.set(CommonAlarmProperties.Label, eventMessage);
            switch (alarmStateTransition) {
                case Ack:
                    basicPropertySet.set(CommonAlarmProperties.AckUserName, "ATOM");
                    basicPropertySet.set(CommonAlarmProperties.AckTime, alarmDate.toDate());
                    basicPropertySet.set(CommonAlarmProperties.IsAcked, true);
                    alarmEvent.acknowledge(new EventData(basicPropertySet));
                case Active:
                    basicPropertySet.set(CommonAlarmProperties.ActiveTime, alarmDate.toDate());
                    basicPropertySet.set(CommonAlarmProperties.IsActive, true);
                    alarmEvent.active(new EventData(basicPropertySet));
                    break;
                case Clear:
                    basicPropertySet.set(CommonAlarmProperties.ClearTime, alarmDate.toDate());
                    basicPropertySet.set(CommonAlarmProperties.IsClear, true);
                    alarmEvent.clear(new EventData(basicPropertySet));
                    break;
                case Finished:
                    break;
            }
            journalManager.storeEvent(alarmEvent, alarmStateTransition);
        }
        catch (Exception ex) {
            logger.error("Error Raising Alarm", ex);
            try { SystemLog.log(ex); } catch (Exception lex) { logger.error("Failed to log to SystemLog", lex); }
        }
    }

If you want it to be a system event, then you just set com.inductiveautomation.ignition.common.alarming.config.CommonAlarmProperties#IsSystemEvent on the property set.

I tried the following code and setting isSystemFlag didn’t seem to do much either. It is possible I used it incorrectly because I am not sure where I was supposed to set that property value.

public void raiseAlarm(String alarmName, String source, String user, String displayPath, AlarmPriority alarmPriority, AlarmStateTransition alarmStateTransition, String eventMessage, Integer eventValue) {
        try {
            var addr = InetAddress.getLocalHost();
            String hostname = addr.getHostName();

            QualifiedPath.Builder qualifiedPathBuilder = new QualifiedPath.Builder();
            qualifiedPathBuilder.setAlarm(alarmName.replace(' ', '_'));
            //qualifiedPathBuilder.setEvent(eventMessage);
            //qualifiedPathBuilder.setMachine(hostname);
            qualifiedPathBuilder.setProvider("LIFT");
            //qualifiedPathBuilder.setSource(source);
            //qualifiedPathBuilder.setUser(user);
            qualifiedPathBuilder.setTag("testAlarm");
            QualifiedPath qualifiedPath = qualifiedPathBuilder.build();

            AlarmJournalManager journalManager = liftGatewayContext.getAlarmManager().getJournalManager();
            BasicAlarmEvent alarmEvent = new BasicAlarmEvent(qualifiedPath, displayPath, alarmPriority);
            UnfilteredEventData eventData;
            BasicPropertySet psb = new BasicPropertySet();
            //psb.set(CommonAlarmProperties.Enabled, true);
            DateTime alarmDate = DateTime.now();
            switch (alarmStateTransition) {
                case Ack:
                    psb.set(CommonAlarmProperties.EventValue, eventValue);
                    psb.set(CommonAlarmProperties.Label, eventMessage);
                    eventData = new UnfilteredEventData(alarmDate.getMillis(), psb);
                    //alarmEvent.set(CommonAlarmProperties.IsSystemEvent, true);
                    alarmEvent.acknowledge(eventData);
                    break;
                case Active:
                    psb.set(CommonAlarmProperties.EventValue, eventValue);
                    psb.set(CommonAlarmProperties.Label, eventMessage);
                    eventData = new UnfilteredEventData(alarmDate.getMillis(), psb);
                    //alarmEvent.set(CommonAlarmProperties.IsSystemEvent, true);
                    alarmEvent.active(eventData);
                    break;
                case Clear:
                    psb.set(CommonAlarmProperties.EventValue, eventValue);
                    psb.set(CommonAlarmProperties.Label, eventMessage);
                    eventData = new UnfilteredEventData(alarmDate.getMillis(), psb);
                    //alarmEvent.set(CommonAlarmProperties.IsSystemEvent, true);
                    alarmEvent.clear(eventData);
                    break;
                case Finished:
                    break;
            }
            journalManager.storeEvent(alarmEvent, alarmStateTransition);
        }
        catch (Exception ex) {
            logger.error("Error Raising Alarm", ex);
            try { SystemLog.log(ex); } catch (Exception lex) { logger.error("Failed to log to SystemLog", lex); }
        }
    }

The image shows 2 alarms

  1. This is the one generated from the script above. However, the System tag for Active, Unacknowledged alarms does not change values ([System]Gateway/Alarming/Active and Unacked).
  2. This alarm is from a tag generated alarm we have configured in one of our UDTs. It seems like I am missing something to tell the gateway there is a new active alarm.

Thank you for the help

Still trying to work through this based on the SDK Programmer Guide and the JavaDocs, but struggling with a few things.

The SDK guide says

Most alarms in Ignition are defined on tags. However, it is possible for modules to generate their own alarms. All alarm evaluation is handled by the AlarmManager, you simply give it the definition of an alarm through AlarmManager.registerAlarm(...), and it provides you with an AlarmEvaluator that you update from time to time with the current value.

However, registerAlarm takes 4 parameters, and we're not sure how to construct them to raise a simple alarm.

Here's what I see in the JavaDocs.

AlarmEvaluator registerAlarm​(QualifiedPath sourcePath, PropertyValueSource parentProperties, AlarmConfiguration configuration, ExpressionParseContext expressionContext)

Registers the configuration of an alarm for a specific source, also providing the context for references in bound alarm properties. The result is an AlarmEvaluator which will be used by the source to evaluate values. When the source is done or removed, it should release the AlarmExecutor to unregister it from the system.

So let's say we want to raise a simple alarm that shows the user that a bad thing happened whenever we encounter a condition (hypothetically, we can use as an example, an exception is encountered during a critical operation).

What would we need need to pass in as a QualifiedPath, PropertyValueSource and ExpressionParseContext when we're wanting to raise the alarm on something in memory instead of on a tag?

Once we have an AlarmEvaluator, how do we update it from time to time?

I don’t think what you’re ultimately looking for is currently possible.
registerAlarm is going to give you back an object that you can manually provide specific values to, and then based on the supplied alarm configuration, the AlarmEvaluator will choose whether or not it’s going to actually trigger an alarm. It’s not a true ‘fire off an alarm using the rest of the subsystem’ mechanism.

Is there another approach you would recommend?

The best we could come up with under current constraints would be to create and destroy tags just for the sake of raising alarms, which feels hacky.

I was looking over the platform services document and found the section on Alarming. Can someone provide an example on how to use it?

https://docs.inductiveautomation.com/display/SE/Working+with+Platform+Services

Should we use an AlarmListener implementation or register to the AlarmManager and use an AlarmExecutor?

We basically want to show an active alarm when some of our functions either have an exception or a “bad” state.

We appreciate any input.