Active and Unacked Tag Doesn't Match Alarm Table

I have an alarm table that shows alarms that are all active and acked.
I have an expression tag that is the system tag for the active and unacked alarm count.

The table shows no active and unacked tags (minimum priority is diagnostic) but the tag counts 6 active and unacked tags.

I also have 10 shelved alarms. Do shelved alarms retain there unacked status if shelved before being acked? If so, how do I require that alarms be acked before shelving, or automatically ack the alarm if it's being shelved?

I'll have to do something with the onShelve extension function. Where can I find more info on the format of "alarms"?

Tried: system.alarm.acknowledge(alarms) and that did not work.

Printing to the console, alarms looks like this:

[{Source: 'prov:Area_01:/tag:Alarms/Odor_Control_Heat_Trace_ALM/ALM_IND:/alm:Alarm', Display Path: 'HDWK', UUID: 'b837ed7b-eb76-4ba8-b308-b394b4922243', Current State: 'Active, Acknowledged', Priority: 'Medium', Active Data: {ackPipeline=Alarms, setpointA=1.0, eventValue=true, activePipeline=Alarms, clearPipeline=Alarms, name=Alarm, eventTime=Wed Aug 28 11:22:35 CDT 2024, label=Odor Control Panel Heat Trace, priority=Medium, displayPath=HDWK, enabled=true}, Clear Data: null, Ack Data: {ackPipeline=Alarms, setpointA=1.0, ackUser=usr-prov:Users:/usr:admin, activePipeline=Alarms, clearPipeline=Alarms, name=Alarm, eventTime=Wed Aug 28 11:23:08 CDT 2024, label=Odor Control Panel Heat Trace, priority=Medium, displayPath=HDWK, enabled=true}, Runtime Data: {isShelved=false, shelfUser=usr-prov:Users:/usr:admin, shelveExpiration=null}}]

Trying to print the UUID from the alarms object, assuming that's the ID I need for system.alarm.acknowledge():

print(alarm['UUID'] for alarm in alarms)

I get <generator object <genexpr> at 0x4>

Try:

for alarm in alarms:
   print alarm.id

Thanks. Issue was two-fold. I needed to use .id and I needed to print the results individually.

Easy enough now:

ids = []
	if alarms:
		for alarm in alarms:
			ids.append(str(alarm.id))
			
		system.alarm.acknowledge(ids, '')

Golf:

if alarms:
    system.alarm.acknowledge([str(alarm.id) for alarm in alarms], "")
2 Likes

You don't need the if alarms check, assuming it's checking if it's empty.
If you iterate through an empty list, then nothing will happen, no error there.
If it can be null, that's a different story, but in this case I'd suggest making this explicit:

if alarms is not None:
    ids = [str(alarm.id) for alarm in alarms]
else:
    ids = []

or, to golf it further:

ids = [str(alarm) for alarm in alarms or []]
2 Likes

This list is straight from the alarm table, so I don't think it will ever be null. But an extra check won't hurt anything.

I'll take readability over golf, considering I'm the only SCADA guy at my empolyer that knows let alone likes text-based languages.

I'd argue that a proper comprehension is way more readable than explicit loops and appends...

List comprehension is one of the new things that I had to learn with more Python exposure. I like them now, but with limited exposure I did not like it, initially. It can be alot to read if you're not used to reading the 'list comprehension sentences"

Eh, while I generally agree with you I've become aware of a whole category of folks who prefer imperative loops (whether from genuine preference or a dogged insistence that X language worked that way, so should Python).

I've also soured somewhat on comprehensions in general after having seen the light of functional operations and method chaining, e.g. Java streams, Kotlin collection extension methods, Linq in C#, etc. Python just goes out of its way to make those awkward to use, especially in Python 2's syntax.

Bonus, extra silly:
:-1:

ids = [str(alarm) for alarm in alarms or []]

:+1:

ids = map(lambda a: str(a.id), alarms)

It saves six whole characters! :laughing:

2 Likes

Save 6 characters, confuse 100's of beginner programmers

1 Like

And no comprehension involved ! What more could you possibly ask for ?

2 Likes

Here's a prime example of where list comprehension can go overboard, especially if a newish Python dev is reading it:

if not areas:
		tagsToWrite = [tag + suffix for tag_list in area_tags.values() for tag in tag_list]
	else:
		tagsToWrite = [tag + suffix for area,tag_list in area_tags.items() if area in areas for tag in tag_list]

I wrote it, I know it works, but I have to reread it a couple times every time I come back to it.

I digress...

One must be careful to ensure that you actually stream through these, instead of constructing then discarding intermediate lists. Another gotcha for casual programmers.

1 Like

Assuming areas can't be null, you can remove the checks and keep the second comprehension only. Same deal as the alarms earlier.

I don't think it's overboard, it's a pretty simple nested comprehension. The condition inside makes it a bit funky, but...
You can make things easier by decomposing it on several lines:

tagsToWrite = [
	tag + suffix
	for area, tag_list in area_tags.items() if area in areas
	for tag in tag_list
]

But you can usually make things cleaner with itertools.

2 Likes

Hey, digression's fun.

I'm a big proponent of using whitespace to make things like that more clear:

if not areas:
    tagsToWrite = [
        tag + suffix 
        for tag_list in area_tags.values()
        for tag in tag_list
    ]
else:
    tagsToWrite = [
        tag + suffix
        for area, tag_list in area_tags.items()
        if area in areas
        for tag in tag_list
    ]

But I would probably also extract the filtering portion from the joining logic, so you've got a statement that returns tag_list separate from your statement that returns tagsToWrite