How to consolidate a bunch of alarms?

I have a bunch of tags in a folder all set to Critical alarm priority. Operators are getting flooded with alarms from this area on the Alarm Summary and the individual tags are not critical alarms. They really only need to be alerted if something in this area fails and be able to trace out where it came from. My idea is:

  • Set the Alarm Status table in the Alarm Summary to Min Priority = Low
  • Set all the alarms in this area to Diagnostic priority. This should put them in the Alarm History so that they can be found but filter them from the Alarm Summary to reduce the overload. I looked at the editAlarmConfig function to do a bulk edit but since the tagpaths argument would be very long I think I just have to manually set each alarm tag
  • Create a new Expression tag of type Boolean to read all of the tags in the area and set the value to true if any of them are in alarm. I would then configure the alarm priority greater than Diagnostic to show up in the Alarm Summary. That way they know when something in that area has failed.
  • Ideally I would like it to re-alarm (clear the Acknowledged state) if one of the tags in this area goes into alarm, but my understanding is that Ignition has no way to do this. Often there are many tags in the area in alarm (which is another problem) but I would like them to know that while they already have some tags in alarm that a new one has failed. I don’t see a way to do that.

Are there any recommendations on how best to do this? Is my approach a good one?

I think after several hours I have a solution that is not as elegant as I wanted, but it basically works.

  • Create a memory tag for Alarm_Count, type Integer. This will maintain the number of active alarms from the set of tags I am monitoring
  • Create another memory tag for Alarm_Composite, type Boolean. This will be the single tag that will alarm when the tags I am monitoring have new faults so that I am not overloading the alarm journal
  • For the tags I am monitoring, set all their alarm priorities to Diagnostic.
  • Configure the Alarm_Composite for an alarm when Equal to 1. I tried setting a conditional alarm priority with an expression if({[.]Alarm_Count}>2,4,3), but this was not working. Therefore, I left the priority static at High
  • When the monitored tags have new faults, I want Alarm_Composite to go into alarm but when that alarm is acknowledged I want it to clear. The reason is there will almost always be faults and I do not want this tag constantly in alarm. I am able to do this with the Tag Event script for the Alarm Acknowledged Event on Alarm_Composite. Every time this tag’s alarm is acknowledged, the script will run with the following added statement: system.tag.write(“[.]Alarm_Composite",“false”)
  • Now I need a way to set the alarm on Alarm_Composite and maintain a count of the alarms. I do this with a gateway script on tag change. The tags for the change triggers are all of the tags I am monitoring. The script calculates the number of tags that are in fault (their value is “true”) and if the number of faults is equal or greater then I know there are new faults represented, in which case I set Alarm_Composite to true, as follows:

AlarmCount = 0

Alarm_1 = system.tag.read(“Test_Tags/Alarm_1”)
if Alarm_1.value:
AlarmCount=1

if system.tag.read(“Test_Tags/Alarm_2”).value:
AlarmCount=AlarmCount+1

if system.tag.read(“Test_Tags/Alarm_3”).value:
AlarmCount=AlarmCount+1

OldAlarmCount = system.tag.read(“Test_Tags/Alarm_Count”)

if AlarmCount >= OldAlarmCount.value:
system.tag.write(“Test_Tags/Alarm_Composite”,“true”)

system.tag.write(“Test_Tags/Alarm_Count”,AlarmCount)

  • Lastly, I have a display with the Alarm Status Table in it configured with Min Priority set to Low so that all the monitored tags at Diagnostic priority don’t show up. They will show up in the Alarm Journal Table (alarm history) so that the source of the fault can be found.

  • The result is that when a new fault occurs, Alarm_Composite shows up as a High priority alarm in the alarm display. They can also see a count from Alarm_Count of how many faults there are. When they acknowledge the alarm is disappears. They can go to alarm history to see each individual fault.

I found that if I had 3 alarms cleared and 2 new one I would not generate an alarm. I created a bit status tag to retain last scan faults to compare to new faults. This got very messy so I created a TagList and use a for loop. This turned out much better

#This is the list of tags being monitored for faults
TagList = “Test_Tags/Lamp_Alarm_1”,“Test_Tags/Lamp_Alarm_2”,“Test_Tags/Lamp_Alarm_3”
#Initialie the number of active faults
AlarmCount = 0
#Initialize the flag =for the composite alarm to false
AlarmFlag = 0
#Read the status bits from the last scan of faults
AlarmLast = system.tag.read(“Test_Tags/Lamp_Alarm_Last”)
#Initialize the alarm status bit value
AlarmNew = 0
#Initialize the loop index
LoopInd = 0

#For each tag in the list
for TagVal in TagList:
#If the tag is in fault, increase the alarm count and add the status bit
if system.tag.read(TagVal).value:
AlarmCount=AlarmCount+1
AlarmNew = AlarmNew + 2**LoopInd

	#If this is a new fault (previous status bit was off, the composite alarm will be generated
	if not(AlarmLast.value >> LoopInd & 1):
		AlarmFlag = 1	
#Increment loop index for next loop
LoopInd = LoopInd+1

#Set the status bits for faults for the next scan
system.tag.write(“Test_Tags/Lamp_Alarm_Last”,Alarm_New)

#If we detected a new alarm, we generate a compsite alarm
if AlarmFlag:
system.tag.write(“Test_Tags/Lamp_Composite”,“true”)

#Store the count of active faults
system.tag.write(“Test_Tags/Lamp_Alarm_Count”,AlarmCount)

By the way, if there was a RE-ALARM capability in Ignition it would greatly help.

The format of the prior post was a bit goofy, so this is a better posting for copy-paste:

#This is the list of tags being monitored for faults
TagList = "Setting up Alarms/Alarm","Setting up Alarms/Dynamic","Setting up Alarms/Level","Setting up Alarms/SP"
	
#Initialize the number of active faults
AlarmCount = 0
	
#Initialize the flag =for the composite alarm to false
AlarmFlag = 0
	
#Read the status bits from the last scan of faults
AlarmLast = system.tag.read("Test_Tags/Lamp_Alarm_Last")
	
#Initialize the alarm status bit value
AlarmNew = 0
	
#Initialize the loop index
LoopInd = 0
	
#For each tag in the list
for TagVal in TagList:
#If the tag is in fault, increase the alarm count and add the status bit
	if system.tag.read(TagVal).value:
		AlarmCount=AlarmCount+1
		AlarmNew = AlarmNew + 2**LoopInd
	#If this is a new fault (previous status bit was off, the composite alarm will be generated
	if not(AlarmLast.value >> LoopInd & 1):
		AlarmFlag = 1	
#Increment loop index for next loop
LoopInd = LoopInd+1
	
#Set the status bits for faults for the next scan
system.tag.write("Test_Tags/Lamp_Alarm_Last",Alarm_New)
	
#If we detected a new alarm, we generate a compsite alarm
if AlarmFlag:
	system.tag.write("Test_Tags/Lamp_Composite","true")
	
#Store the count of active faults
system.tag.write("Test_Tags/Lamp_Alarm_Count",AlarmCount)

Sorry, had some syntax errors in my last post. I tested this, it works:

#This is the list of tags being monitored for faults
TagList = "Setting up Alarms/Alarm","Setting up Alarms/Dynamic","Setting up Alarms/Level","Setting up Alarms/SP"
	
#Initialize the number of active faults
AlarmCount = 0
	
#Initialize the flag =for the composite alarm to false
AlarmFlag = 0
	
#Read the status bits from the last scan of faults
AlarmLast = system.tag.read("Test_Tags/Lamp_Alarm_Last")
	
#Initialize the alarm status bit value
AlarmNew = 0
	
#Initialize the loop index
LoopInd = 0
	
#For each tag in the list
for TagVal in TagList:
#If the tag is in fault, increase the alarm count and add the status bit
	if system.tag.read(TagVal).value:
		AlarmCount=AlarmCount+1
		AlarmNew = AlarmNew + 2**LoopInd
	#If this is a new fault (previous status bit was off, the composite alarm will be generated
	if not(AlarmLast.value >> LoopInd & 1):
		AlarmFlag = 1	
	#Increment loop index for next loop
	LoopInd = LoopInd+1
	
#Set the status bits for faults for the next scan
system.tag.write("Test_Tags/Lamp_Alarm_Last",AlarmNew)
	
#If we detected a new alarm, we generate a compsite alarm
if AlarmFlag:
	system.tag.write("Test_Tags/Lamp_Composite","true")
	
#Store the count of active faults
system.tag.write("Test_Tags/Lamp_Alarm_Count",AlarmCount)

You should consider restructuring your code to read all the tags at once with system.tag.readAll(), then looping through the results to perform your analysis. BTW, you can edit prior comments to clean up the style and add code formatting – you don’t have to re-post.

Thanks for the recommendation on editing a post

I don’t understand the advantage of system.tag.readAll(). Since I have a limited number of tags I am monitoring in this script, I only need to loop through those tags. What would that code look like?

Something like this, with some list comprehensions for brevity:

tagpaths = ["Test_Tags/Lamp_Alarm_Last"]+["Setting up Alarms/"+x for x in ["Alarm","Dynamic","Level","SP"]]
values = [x.value for x in system.tag.readAll(tagpaths)]
AlarmLast = values[0]
alarms = values[1:]
AlarmNew = 0
for i, av in enumerate(alarms):
    if av:
        ....  yada yada yada ....

writePaths = ["Test_Tags/Lamp_Alarm_"+x for x in ["Last", "Count"]]
writeValues = [Alarm_New, AlarmCount]
if AlarmFlag:
    writePaths.append("Test_Tags/Lamp_Composite")
    writeValues.append(True)
system.tag.writeAll(writePaths, writeValues)

The system.tag.* calls all require serialization in the tag manager, and from client scopes, a round-trip message to the gateway. Both time sinks are mitigated by consolidating operations with the “All” versions.

1 Like

Really appreciate it ptumel. at the risk of making this post really long, I want to post what I think is the “final” version. I added code at the start to create a list of tags.

#Create a list of tags with Lamp_Alarm_Last first, 
#followed by a list of the alarm tags where %d is the index
TagList = ["Test_Tags/Lamp_Alarm_Last"] + ["Test_Tags/Alarm%d/Alarm" % Index for Index in range(1,11)]
#Using readAll is more efficient by having one message to get all tag values 
TagValues = [Tag.value for Tag in system.tag.readAll(TagList)]

#Store locally the status bits from the last scan of faults, which is index 0 in the list of AlmValues
#from the readAll function
AlarmLast = TagValues[0]

#Store locally the values from each alarm tag, which begins with index 1 in the list of AlmValues
#from the readAll function
AlarmValues = TagValues[1:]
	
#Initialize the number of active faults
AlarmCount = 0
	
#Initialize the flag =for the composite alarm to false
AlarmFlag = 0
	
#Initialize the alarm status bit value
AlarmNew = 0
	
#For each tag in the list use enumerate to return the index (LoopInd) and value (AlarmStatus)
for LoopInd, AlarmStatus in enumerate(AlarmValues):
#If the tag is in fault, increase the alarm count and add the status bit
	if AlarmStatus:
		AlarmCount=AlarmCount+1
		AlarmNew = AlarmNew + 2**LoopInd
		#If this is a new fault (previous status bit was off, the composite alarm will be generated
		if not(AlarmLast >> LoopInd & 1):
			AlarmFlag = 1	

#Create a list of paths to the tags we will write to
writePaths = ["Test_Tags/Lamp_Alarm_"+SuffixText for SuffixText in ["Last", "Count"]]
writeValues = [AlarmNew, AlarmCount]
#If there is a new alarm, add the composite tag to the list of tags we will write to
if AlarmFlag:
    writePaths.append("Test_Tags/Lamp_Composite")
    writeValues.append(True)
system.tag.writeAll(writePaths, writeValues)