Format Shelved Alarms Table

How can I change what columns are displayed in the manage shelved alarms popup on the alarm status table? Right now, the only data that is given is the path, who shelved it, and for how long. The path is not very useful to operators who are looking at the shelved alarms.

Will this require a script on a button to open a custom table?

Since there have been no replies as of yet, I've created a dataset tag that runs this script to get the shelved alarms.

def getShelvedAlarmInfo():
	'''
		Get dataset of currently shevled alarms. Includes these columns:
			|	Shelved Path	|	Alarm Path	|	Display Path	|	Label	|	Expiration	|
			
		Args:
			n/a
			
		Returns:
			dataset	(dataset)	:	Dataset of currently shelved alarm tag data
	'''
	#get the list of paths for currently shelved alarms
	shelvedPaths = system.alarm.getShelvedPaths()
    
    #dataset headers
	headers = ['Shelved Path', 'Alarm Path', 'Display Path', 'Label', 'Expiration']

	#initialize blank data list for dataset creatoin
	data = []
    
    if len(shelvedPaths) > 0:
	
        #initialize empty lists for paths and expirations
        almPaths = []
        almExp = []
        
        for alm in shelvedPaths:
            almPaths.append(str(alm.path))
            almExp.append(system.date.format(alm.expiration, "yyyy-MMM-dd h:mm a" ))
	
		#initialize empty lists for alarm paths, labels, and display paths
		fullAlmPaths = []
		almLabelPaths = []
		almDisplayPathPaths = []
		
		#for each path in the list of alarm paths
		for almPath in almPaths:
			
			#spit at the ':/' character
			almPathParts = almPath.split(':/')
			
			#create dict to map key:value pairs
			almPartsDict = {}
			for part in almPathParts:
				key, value = part.split(':', 1)
				almPartsDict[key] = value
			
			#get values for provider, tag, and alarm keys
			prov = almPartsDict.get('prov','')
			tag = almPartsDict.get('tag','')
			alarm = almPartsDict.get('alm','')
			
			#create the full path to the alarm from the provider, tag path, and alarm name
			fullAlmPath = '[' + prov + ']' + tag + '/Alarms/' + alarm
			
			#append each to the corresponding list of properties
			fullAlmPaths.append(fullAlmPath)
			almLabelPaths.append(fullAlmPath+'.Label')
			almDisplayPathPaths.append(fullAlmPath+'.DisplayPath')
			
		#combine label and display path tag lists for a single read and extract the value from the qualified value
		almInfo = [tag.value for tag in system.tag.readBlocking(almLabelPaths + almDisplayPathPaths)]
		
		#for each path in the list of alarm paths
		for i in range(0, len(almPaths)):
			#add row to the data list for the dataset
			data.append([almPaths[i], fullAlmPaths[i], almInfo[i], almInfo[i+len(almPaths)], almExp[i]])
		
	#create and return dataset
	dataset = system.dataset.toDataSet(headers, data)
		
	return dataset

I have a button to hide/show the shelved alarm table. It also initially sorts and assigns the dataset from the tag when the table is opened.

Added a custom string property to hold the selected alarm's path

if({Root Container.Container.Shelved_Alarms.selectedRow} >= 0,

{Root Container.Container.Shelved_Alarms.data}[{Root Container.Container.Shelved_Alarms.selectedRow},'Shelved Path'], '')

And a button to unshelve the selected alarm.

Seems odd that at the very least there's no config options for the shelved alarms table. Ideally, the shelved alarms table columns would match the active alarms table.

It looks like you've done the hard part, which is assembling the dataset. I would recommend adding a return after your getShelvedPaths call, in case it returns None, so the rest of the code isn't uselessly processed.

If you wanted to put your custom table directly in the alarm status component, you probably could. You would just have to remove the JTable from the shelving panel and put yours in its place. You could even add a listener to the shelving button to do this when the popup holder panel is opened. It would take a little work though, and I don't know how far you want to go.

Here are the scripts you would need to directly access the internal shelving panel, table, and its launch button:

# This will only be found if the shelving table is open.
def getShelvingPanel(statusTable):
	for component in statusTable.components:
		if 'ShelfManagementPanel' in component.__class__.__name__:
			return component
		elif getShelvingPanel(component) is not None:
			return getShelvingPanel(component)

# This is the JTable that lives in the shelving panel
# ...It only exists when the shelving panel exists and is visible
def getShelvingTable(shelvingPanel):
	for component in shelvingPanel.components:
		if 'JTable' in component.__class__.__name__:
			return component
		elif getShelvingTable(component) is not None:
			return getShelvingTable(component)

# This is the button at the bottom left of the alarm status table that launches the shelving panel
def getShelvingButton(statusTable):
	for component in statusTable.components:
		if 'JideToggleButton' in component.__class__.__name__ and component.toolTipText == 'Manage shelved alarms':
			return component
		elif getShelvingPanel(component) is not None:
			return getShelvingPanel(component)


statusTable = event.source.parent.getComponent('Alarm Status Table')
shelvingPanel = getShelvingPanel(statusTable)
shelvingTable = getShelvingTable(shelvingPanel) if shelvingPanel is not None else None
shelvingButton = getShelvingButton(statusTable)

Ya, I just disabled the manage shelved alarms button on the alarm status table, overlaid my table on the bottom of the alarm status table, and now use external buttons to show/hide the custom table and unshelve the selected alarm.

Would my if len(shelvedPaths) > 0 not suffice to skip processing of the useless code?

I would write it like this, so the rest of the code doesn't have to be indented:

	shelvedPaths = system.alarm.getShelvedPaths()
	if not shelvedPaths:
		return

fair, but I'd probably still opt to return an empty dataset, for consistency's sake. I only did it that way so that the single call to system.dataset.toDataset() is always what creates the dataset

When I run your script when no alarms are shelved, it fails on this line:

almInfo = [tag.value for tag in system.tag.readBlocking(almLabelPaths + almDisplayPathPaths)]

...because almPaths doesn't evaluate to None when there are no shelved paths.

Perhaps it would be better to change if almPaths is not None: to if almPaths:

That way any falsyness will skip that section of code, but the function will still return the dataset you are wanting.

Works fine for me (8.1.38) - I had already moved the check before that inline for loop

image

I see. I copied your code before this edit was made:
image

That's why it was breaking when I ran it.

1 Like

And actually, I found that an empty list is not None, hence the replacement with len()

An empty list will evaluate as False though, so you don't need to specify its length or check to see if it's null.

1 Like