Alarm statistics perspective

Hi Everyone,

What is the best way of showing alarm statistics?

Looking at other posts, global alarm counters can be shown like this:

count_of_shelved_alarms = len(system.alarm.getShelvedPaths())
ActiveAcked = len(system.alarm.queryStatus(state=["ActiveAcked"]))
ActiveUnacked = len(system.alarm.queryStatus(state=["ActiveUnacked"]))
ClearUnacked = len(system.alarm.queryStatus(state=["ClearUnacked"]))

But what can be done when trying to show individual alarm counters?

alarm_events table can be queried and a COUNT() can be done on the source in a date range. But from there, I’m not sure how to get the property “Label” of the alarm based on the source.

That made me wonder, what is the best approach for statistics using a variety of filters?

Some posts:
How to get total alarm duration and occurrences by alarm (NOV 2015)
How to get total alarm duration and occurrences by alarm (DEC 2015)
Get Alarm Counter
How to pull Associated Alarm Data from the alarm event list generated with system.alarm.queryStatus?
Perspective alarm list with source filtering

Some unanswered posts:
system.alarm.queryStatus vs system.alarm.queryJournal
A friendly guide to count alarms

I would stick with using system.alarm.queryStatus or system.alarm.queryJournal vs querying alarm_events. Also keep in mind alarm_events is the alarm journal data.

You can get the label like this, just add your own filters:
system.alarm.queryStatus()[0].label

edit:
This works also, though i don’t know if one is better or more correct:
system.alarm.queryStatus()[0].getLabel()

Is there a reason?
This:

SELECT FORMAT(eventtime, 'dd/MM/yyyy HH:mm:ss') AS "Event Time",source
FROM alarm_events
WHERE eventtype = 0 AND
eventtime >= :startDate AND eventtime <= :endDate
ORDER BY eventtime DESC

Looks easier than this:

system.alarm.queryJournal(journalName="AlarmLog", startDate=startDate, endDate=endDate,state=['ActiveUnacked'])

With the SQL statement you can get
prov:default:/tag:UDT tag:/alm:Alarm

That's why I was thinking about (somehow) getting the label property out of the SQL results

Querying the table directly only works if you have an database alarm journal configured, and you would have to jump through some not insignificant hoops to get the label. You would need to parse the tag path out of the alarm source, then use system.tag.getConfiguration to get the label. You also cant query active alarms at all that way.
Edit:
That was poorly worded, you cant query the info you see in the alarm status table might be a better way to say it.

I think an alarm journal is necessary to get statistics information. Because it's not about the current state, but looking up at past alarms to count them based on some filters.

After playing around with system.alarm.queryJournal(), I went back to SQL to finish the query that gets the label. I'm not sure it's right but at least I get some results.

SELECT COUNT(ae.eventtime) AS "Trigger count",aed.strvalue
FROM alarm_events AS ae
INNER JOIN alarm_event_data AS aed
ON ae.id = aed.id
WHERE 
ae.eventtype = 0 AND ae.eventtime >= :startDate AND eventtime <= :endDate
AND aed.propname = 'label'
GROUP BY aed.strvalue
ORDER BY COUNT(eventtime) DESC

I will give system.tag.getConfiguration a quick test.

Is the Report Module easier for this task?

1 Like

I don’t think the report module would necessarily be easier, but with it you could create an alarm journal query data source then use a script data source to go through the dataset and perform your calculations. One benefit to this approach is that you can use any type of alarm journal.

Edit:
Are you sure that sql query is doing what you want? I tried it on a testing alarm journal and it yielded zero results. I modified it to see what i was getting.

SELECT COUNT(ae.eventtime) AS "Trigger count",aed.strvalue, aed.propname as propname
FROM alarm_events AS ae
INNER JOIN alarm_event_data AS aed
ON ae.id = aed.id
GROUP BY aed.strvalue
ORDER BY COUNT(eventtime) DESC

Which gave me this:
image

I do get results and it looks good but I have to recheck. How come you got zero results?

In your query you left out the "where" statement, that's ok for the dates. But you would need "propname = label" as there is the alarm text. "eventtype = 0" would be just the activation event.

I'm not sure, I think event type values are from this table:
image

I'm not sure what "dtype" refers to.
Therese are the tablets from the alarm journal.

image

Edit:
I get an error when showing my named query on a perspective table.

Not sure why... it's a common binding. No scripting on it.

Edit 2:
"Invalid Key" disappeared. I'm not sure why it was there in the first place.

Edit 3:
I made a mistake when about the SQL tablets. Here is the documentation:
Alarm Journal

Alarm Events (alarm_events)

Alarm Event Data (alarm_event_data)

I caution you if you’re calling system.alarm.queryStatus from the client. In Vision, these can easily kill client performance. In Perspective, these can easily kill your gateway. Calling this function is expensive and doesn’t scale well.
Consider calling this function once periodically in a script library to pull in all alarms and store the results in a library variable. Then filter that variable and summarise your alarms per area based on this instead.

1 Like

Thank you for the warning.

I was looking at my memory usage working around 800mb-1000mb. Normally it’s 400-500mb.

Probably the increased workload is due to system.alarm.queryStatus.

SQL queries stress the system the same way?

Not sure, i left out the where clause entirely to try to find out. There are absolutely no labels in my data, despite having labels configured in my alarms.

I think this explains the invalid key warning:

That's weird. Try posting some screenshots. Maybe your new labels are not logged or you need to reset your trial for them to be logged.

How people mostly use alarms?
"Name" property is not useful for a dynamic message because it's fixed.
"Display Path" has the default path. I guess a dynamic message can built here but you would miss the path.
"Label" seems appropriate to contain a dynamic message.

Default setup for Alarm Status Table shows Display Path and Source. I don't know what's the intention for it.
Default setup for Alarm Journal Table shows Event ID and Source. Again, I don't know the intention for it.

Out of my head, Dataset is different from Array of Objects. As an example, today I used the Pie Chart binding its "Data" Property. Even though it should accept an Array List, it was able to use a Dataset without any issues.

Anyway, providing some type of statistics is the main topic. I will give it another round of tests tomorrow.

Well in 8.1.5 at least (I think maybe you can display custom alarm fields in the ast now), I use:
Name: the name of the alarm, e.g. Failed to Start
Label: the device description and equip id, e.g. Agitator [T2334_M01]
Display path: the area and parent device if relevant, e.g. Red TF T2334

The alarm source will always contain the path to the tag, so you don't need to worry about replacing display path.

Using this methodology, you can display alarms anywhere with all the info you need.
For example on a device popup, just show name
On a tank detail page, show label and name
On the main alarm page, show all 3

Event id is useless to operators but lets you group different events attached to the same alarm event, e.g. Active, acked, cleared events for a single alarm event

What i would really like to see is all of the alarm info for an alarm event on the one row, so you know when it activated, when it was ackd, and cleared without having to go searching. At least have it an option as sometimes you actually might want to show it more as a timeline as it is now

Fully licensed, and i did try changing the labels and names and saving to see if that made a difference. My previous screen shot is the sum total of every alarm and there is not one instance of a 'label' property.

I am able to get the labels with system.alarm.queryJournal(journalName="AlarmJournal")[0].getLabel() which is why i suggested using that instead of querying directly.

I will give it a try tomorrow. I think it's a good idea.

I have to think about this one. Maybe it's something I would like but I'm not aware of yet.

Are your tables the same as mine?

I actually got some errors out of it. Using a for loop I got an error message... something like "index out of range".
So, I did some debugging with system.perspective.print() and I found that some results were [].
I'm not sure if a should call them Null, None, or empty list.

Edit:
I'll run the code tomorrow and post the error.

They should be the same, default settings on for an external database alarm journal.
And my use of system.alarm.queryJournal was a simple proof of concept I tested in the script console, The manual says the return results are a list of PyAlarmEvent objects for what its worth.

Where can I look up for library variables? A way to store the results to make it available for the clients.

I call the function to populate the library variable with alarm info from an expression tag using runScript. You can only access the value of the variable within the scope from which you call it. For example, you can’t call the function in the tag scope and read the value from the client scope.
Calling from a tag gives access to the value from the tag scope so then I can have other expression tags in various folders that filter and summarise the alarms stored in the library variable based on the tag path they’re located in. Filtering the dictionary is super quick compared with calling the queryStatus function 50 times

Where do you get that version number from?
At this date I can see the latest release is 8.1.16
image

I'm using 8.1.15
image

I've notice that the source path saved in the database has a different format than the one Ignition's use.
From the database:
prov:default:/tag: path here /H:/alm:High Alarm

I was used to the path you get when you right click on a tag:
[default] path here /H/Alarms/High Alarm

I tested some functions with the path from the database:

system.tag.readBlocking(path)
system.tag.getConfiguration(path, False)
system.tag.browse(path)

But I get not result from them. So I was thinking that some string manipulation have to be done to get the path from the database look like the "normal" one.

Breaking it down into steps:

  1. Create a script in the Project Library.
  2. Pick a scope and call it. For example, tag scope.
  3. Other tags will filter on the tag that is calling the function.
  4. Show those results in some Views and filter more if needed.

Nick is running an old version and hasn't upgraded to the newest version.

Most of the system.tag.* functions expect the path as you would get from right clicking on the designer.