20260505 Backup

This commit is contained in:
2026-05-05 23:58:14 +00:00
parent a590cd030f
commit 0bc354d095
870 changed files with 30817 additions and 46676 deletions

View File

@@ -0,0 +1,124 @@
def getActivePriorityCount(tagPath, Priority = ['Diagnostic', 'Low','Medium','High','Critical']):
###
# This function returns the number of active alarms of a specificed priority other wise all active alarms
return len(getActiveAlarms(tagPath, Priority, state = ['ActiveUnacked', 'ActiveAcked'], sortByTime = False))
def getActiveCriticalCount(tagPath, Priority = ['Critical']):
###
# This function returns the number of Active Critical Alarms
return len(getActiveAlarms(tagPath, Priority, state = ['ActiveUnacked', 'ActiveAcked'], sortByTime = False))
def getActiveHighCount(tagPath, Priority = ['High']):
###
# This function returns the number of Active High Alarms
return len(getActiveAlarms(tagPath, Priority, state = ['ActiveUnacked', 'ActiveAcked'], sortByTime = False))
def getActiveMediumCount(tagPath, Priority = ['Medium']):
###
# This function returns the number of Active Medium Alarms
return len(getActiveAlarms(tagPath, Priority, state = ['ActiveUnacked', 'ActiveAcked'], sortByTime = False))
def getActiveLowCount(tagPath, Priority = ['Low']):
###
# This function returns the number of Active Low Alarms
return len(getActiveAlarms(tagPath, Priority, state = ['ActiveUnacked', 'ActiveAcked'], sortByTime = False))
def getActiveDiagCount(tagPath, Priority = ['Diagnostic']):
###
# This function returns the number of Active Diagnostic Alarms
return len(getActiveAlarms(tagPath, Priority, state = ['ActiveUnacked', 'ActiveAcked'], sortByTime = False))
def getActiveAlarms(tagPath, priority = ['Diagnostic', 'Low','Medium','High','Critical'], state = ['ActiveUnacked', 'ActiveAcked'], sortByTime = False):
###
# This function grabs a list of active alarms to use in a table
# tagpath - path of device alarms to collect of type string
# priority - list of priority types to collect
# state - a list of states to collect
# sort - option to sort by timestamp
###
alarmList = []
if tagPath:
i = tagPath.find(']')
path = '*{}/*'.format(tagPath[i + 1:])
results = system.alarm.queryStatus(source = [path], priority = priority, state = state)
for each in results:
alarm = {
'Time': each.get('eventTime'),
'Alarm': each.getDisplayPath(),
'Notes': each.getNotes(),
'Priority': str(each.getPriority()),
'Alarm Priority': str(Global.AlmTables.getCustomPriority(str(each.getPriority()))),
'Name': each.getName(),
'id': each.getId(),
'Source': each.getSource(),
'State': each.get('State')
}
alarmList.append(alarm)
if sortByTime:
return sorted(alarmList, key=lambda d: (d['Time'], priorityWeight[d['Priority']]), reverse=True)
else:
priorityWeight = {'Diagnostic': 0, 'Low': 1, 'Medium': 2, 'High': 3, 'Critical': 4}
return sorted(alarmList, key=lambda d: (priorityWeight[d['Priority']], d['Time']), reverse=True)
return alarmList
def getCustomPriority(priority, customList = ['Diagnostic', 'Info', 'Undefined', 'Warning', 'Critical']):
###
# This function converts standard alarm priorities to custom defined priorites
# priority - priority or type string
# customList - list of custom priorites
if priority == 'Diagnostic':
return customList[0]
if priority == 'Low':
return customList[1]
if priority == 'Medium':
return customList[2]
if priority == 'High':
return customList[3]
if priority == 'Critical':
return customList[4]
return 'UNKNOWN'
def printTagAlarmCnts(tagPath):
#Diag##
# This function is used for quickly testing alarm counts for a tagpath
# results printed to console
# this function is purley for testing
count1 = Global.AlmTables.getActiveCriticalCount(tagPath)
count2 = Global.AlmTables.getActiveHighCount(tagPath)
count3 = Global.AlmTables.getActiveMediumCount(tagPath)
count4 = Global.AlmTables.getActiveLowCount(tagPath)
count5 = Global.AlmTables.getActiveDiagCount(tagPath)
print "Alarm total counts for:" + tagPath + " are: \n" + "Critical: " + str(count1) + "\nHigh: " + str(count2) + "\nMedium: " + str(count3) + "\nLow: " + str(count4) + "\nDiag: " + str(count5)
def updateAlarmCount(tagPath):
###
# This function writes the current alarm count back to the tag device
tagList = ['Diagnostic', 'Low','Medium','High','Critical']
for i in tagList:
count = getActivePriorityCount(tagPath, Priority = [i])
tag = tagPath + "/Alarm Data/"+ i + " Count"
#tag = tagPath + "/" + i + " Count"
#system.tag.writeAsync(tag, count, callback)
system.tag.writeAsync(tag, count)
def printThis(text = "Testing"):
system.perspective.print(text)
return text

View File

@@ -7,11 +7,11 @@
"code.py"
],
"attributes": {
"hintScope": 2,
"lastModificationSignature": "0d7cc7d2974a944e1da1eab2282d019d2930240755b62dc0bbcb4586418b1bbf",
"lastModificationSignature": "7aaa342aed62e05b1ae23d5e1f4a38c7d2761efa9975387b2333b425fc7e8b5d",
"hintScope": 7,
"lastModification": {
"actor": "Emmanuel",
"timestamp": "2026-04-15T20:54:16Z"
"timestamp": "2026-05-04T19:01:42Z"
}
}
}

View File

@@ -0,0 +1,373 @@
from datetime import datetime
def applyExclusions(items, datasetTagpath):
"""
Sorts a list of tag dictionary objects.
This function assumes that the UDT structure has a '{rootPath}/Manual Exclusions/{Info|Config|Meta}'. We fail gracefully if not.
Args:
items (List[Dict]): A list of tag dictionaries with the structure: [{tagName: 'Hello World', 'engUnit': 'String'}, ...].
datasetTagpath (String): A tagpath to an L4 UDT Category, i.e "[PHXA2_IG_COMN]System/Device Datasets/RTU/Info".
Returns:
List[Dict]: A device specific dataset with the structure: [{tagName: 'Hello World', 'engUnit': 'String'}, ...].
"""
# parse datasetTagpath
rootPath = datasetTagpath[:datasetTagpath.rindex('/')] # "[PHXA2_IG_COMN]System/Device Datasets/RTU"
category = datasetTagpath[datasetTagpath.rindex('/')+1:] # "Info"
exclusionPath = '{rootPath}/Manual Exclusions/{category}'.format(rootPath=rootPath, category=category)
logger = system.util.getLogger("Device Dataset/itemsExcluded()")
res = []
# Read sort order, convert from unicode to string, and return unchanged items if empty.
data = system.tag.readBlocking(exclusionPath)[0]
excludedTags = list(data.value) if (data and data.value is not None) else []
excludedTags = [str(tag) for tag in excludedTags]
if len(excludedTags) == 0: return items
return list(filter(lambda x: str(x.get('tagName')) not in excludedTags, items))
def getTagInstances(deviceDataset, tagPath, folder=""):
"""
Iterates through a DeviceDataset and returns instances for a Tag Info FlexRepeater.
This function is called by the 'instances' binding in the L4 /Info views.
Args:
deviceDataset (Document): A device specific dataset with the structure: [{tagName: 'Hello World', 'engUnit': 'String'}, ...].
These are stored in [PHXA2_IG_COMN]System/Device Datasets.
tagPath (String): A tagpath to the root of a device, i.e "[PHXA2_IG_COMN]RM2505/PHXA2_RM2505_FCU_4".
folder (String): The folder the tags live in, i.e "/Config" or "/Meta".
Returns:
List[List]: instances to be used in a /Tag Info FlexRepeater.
"""
# Append /Config or /Meta to the tagpath.
tagPath += folder
res = []
for jsonItem in list(deviceDataset):
tagName = jsonItem.get("tagName", "N/A")
dataType = jsonItem.get("engUnit", "N/A")
qualifiedTagpath = "{tagPath}/{tagName}".format(
tagPath=tagPath, tagName=tagName
)
# Create a FlexRepeater instance with the extracted parameters.
res.append({"name": tagName, "dataType": dataType, "tagPath": qualifiedTagpath})
return res
def getSortedTagDictionaries(items, datasetTagpath):
"""
Sorts a list of tag dictionary objects.
This function assumes that the UDT structure has a '{rootPath}/Configuration/{Info|Config|Meta}SortOrder'. We fail gracefully if not.
Args:
items (List[Dict]): A list of tag dictionaries with the structure: [{tagName: 'Hello World', 'engUnit': 'String'}, ...].
datasetTagpath (String): A tagpath to an L4 UDT Category, i.e "[PHXA2_IG_COMN]System/Device Datasets/RTU/Info".
Returns:
List[Dict]: A device specific dataset with the structure: [{tagName: 'Hello World', 'engUnit': 'String'}, ...].
"""
# parse datasetTagpath
rootPath = datasetTagpath[:datasetTagpath.rindex('/')] # "[PHXA2_IG_COMN]System/Device Datasets/RTU"
category = datasetTagpath[datasetTagpath.rindex('/')+1:] # "Info"
sortOrderPath = '{rootPath}/Sorting Orders/{category}'.format(rootPath=rootPath, category=category)
# Read sort order, convert from unicode to string, and return unchanged items if empty.
data = system.tag.readBlocking(sortOrderPath)[0]
sortingOrder = list(data.value) if (data and data.value is not None) else []
sortingOrder = [str(item) for item in sortingOrder]
if len(sortingOrder) == 0: return items
# Sort items based on tag names in sorting order.
sortedItems = sorted(
items,
key=lambda x: (
sortingOrder.index(x['tagName']) if x['tagName'] in sortingOrder else len(sortingOrder) # append unsorted tags
)
)
return sortedItems
def getDiff(previousData, currentData, folder=""):
"""
Get difference between data before generation and after. This is meant to track
manual additions being lost (functionality which will be improved soon^tm).
Args:
previousData (List[Dict]): A device specific dataset with the structure: [{tagName: 'Hello World', 'engUnit': 'String'}, ...].
currentData (List[Dict]): A device specific dataset with the structure: [{tagName: 'Hello World', 'engUnit': 'String'}, ...].
Returns:
String: A timestamped log message detailing tags that existed in previousData and not in currentData.
"""
logMessage = ""
for item in previousInfo:
tagName = item.get("tagName", "")
if tagName is "":
continue
print(
"{} in {}? {}.".format(tagName, currentData, tagName in currentData)
)
logger.info(
"{} in {}? {}.".format(tagName, currentData, tagName in currentData)
)
if tagName not in currentData:
logMessage += "\n{}: {} was removed in {} update.".format(timestamp, folder, item.tagName)
return logMessage
def generateDeviceDataset(deviceTagpath, datasetTagpath, writeToSystem=True, debug=False):
"""
Create a new device dataset.
Args:
deviceTagpath (String): A tagpath to the root of a device, i.e "[PHXA2_IG_COMN]Roof/PHXA2_ROOF_RTU_1".
datasetTagpath (String): A tagpath to an L4 UDT category, i.e "[PHXA2_IG_COMN]System/Device Datasets/RTU/{Info|Config|Meta|Alarms}".
writeToSystem (Boolean)[Optional]: If specified as false, returns the result instead of writing it
to datasetTagpath in the system.
Returns:
List[Dict]: A device specific dataset with the structure: [{tagName: 'Hello World', 'engUnit': 'String'}, ...].
"""
import json
allTagsOutput = []
#deviceName = deviceTagpath[deviceTagpath.index('/')+1:]
if '/' in deviceTagpath:
deviceName = deviceTagpath[deviceTagpath.rindex('/')+1:]
else:
deviceName = deviceTagpath
logger = system.util.getLogger('{} Dataset'.format(deviceName))
# Fetch tag configurations and sort by name
nodes = system.tag.getConfiguration(deviceTagpath, True)
for item in nodes:
if "tags" in item:
sortedTags = sorted(item["tags"], key=lambda x: x["name"])
sortedTags = sorted(
sortedTags,
key=lambda x: (
x.get("engUnit", "misc")
if x.get("engUnit", "misc") != ""
else "misc"
),
)
for tagConfig in sortedTags:
if str(tagConfig["tagType"]) == "AtomicTag":
name = tagConfig.get("name", "")
dataType = tagConfig.get("dataType", "")
tagPath = deviceTagpath + "/" + name
instance = {
"instanceStyle": {"classes": ""},
"instancePosition": {},
"name": name,
"dataType": dataType,
}
# Group based on name containing 'Power', 'Current', or 'Voltage'
allTagsOutput.append(instance)
if debug:
logger.info('allTagsOutput: {}'.format(allTagsOutput))
print('allTagsOutput: {}'.format(allTagsOutput))
### FlexRepeater Binding
bindingOutput = []
for tag in allTagsOutput:
tagName = tag["name"]
tag["tagPath"] = tagPath + "/" + tagName
bindingOutput.append(tag)
### Logan Script
res = []
for item in bindingOutput:
tagPath = item["tagPath"]
tagNameIndex = tagPath.rindex("/") + 1
res.append({"tagName": tagPath[tagNameIndex:], "engUnit": item["dataType"]})
### Excluding
excludedTagDictionaries = applyExclusions(items=res, datasetTagpath=datasetTagpath)
### Sorting
sortedTagDictionaries = getSortedTagDictionaries(items=excludedTagDictionaries, datasetTagpath=datasetTagpath)
if debug:
logger.info('sortedTagDictionaries: {}'.format(sortedTagDictionaries))
logger.info('writing to: {}'.format(datasetTagpath))
print('sortedTagDictionaries: {}'.format(sortedTagDictionaries))
print('writing to: {}'.format(datasetTagpath))
if writeToSystem:
system.tag.writeBlocking(datasetTagpath, [sortedTagDictionaries])
return sortedTagDictionaries
def generateDeviceDatasetTrends(deviceTagpath, datasetTagpath, writeToSystem=True):
return {}
def generateL4(datasetTagpath, writeToSystem=True, debug=False):
"""
Highest level function. Can be called by users.
Generate Info, Config, Meta, and Trends in a L4 UDT. Requires a UDT to exist with a reference tagpath specified.
This function is called by _Manual Refresh in the L4 UDT.
Args:
datasetTagpath (String): A tagpath to an L4 UDT, i.e "[PHXA2_IG_COMN]System/Device Datasets/RTU".
writeToSystem (Boolean)[Optional]: If specified as false, returns the result instead of writing it
to datasetTagpath in the system.
debug (Boolean)[Optional]: Enable verbose logging.
Returns:
Dict{key: val}: A dictionary containing each dataset. Keys are ['info','config','meta','trends'].
"""
timestamp = datetime.now().strftime("[%Y-%m-%d %H:%M:%S]") # Returns "[2025-08-08 08:28:32]".
logger = system.util.getLogger("Device Dataset")
logMessage = ""
if debug:
logger.info("Generating L4 for {}.".format(datasetTagpath))
print(datasetTagpath)
# Get all relevant UDT data.
data = system.tag.readBlocking(
[
datasetTagpath + "/Info",
datasetTagpath + "/Config",
datasetTagpath + "/Meta",
datasetTagpath + "/Trends",
datasetTagpath + "/Alarms",
datasetTagpath + "/_Reference Tagpath",
]
)
# Catch bad tagPaths and non-existent data.
previousInfo = data[0].value if (data and data[0].value is not None) else {}
previousConfig = data[1].value if (data and data[1].value is not None) else {}
previousMeta = data[2].value if (data and data[2].value is not None) else {}
previousTrends = data[3].value if (data and data[3].value is not None) else {}
previousAlarms = data[4].value if (data and data[4].value is not None) else {}
referenceTagpath = data[5].value if (data and data[5].value is not None) else {}
# If no reference device is given, the script explodes. We catch it here.
if referenceTagpath is None or len(referenceTagpath) == 0:
logger.info(
"\n{}: ERROR: Please verify that a reference tagpath is provided.".format(timestamp)
)
logMessage += (
"\n{}: ERROR: Please verify that a reference tagpath is provided.".format(timestamp)
)
system.tag.writeAsync([datasetTagpath + "/_Log Message"], [logMessage])
return # exit early
if debug:
logger.info(
"prevInfo: {}\nprevConfig: {}\n prevTrends: {}\n prevMeta: {}\n refTag: {}".format(
previousInfo,
previousConfig,
previousTrends,
previousMeta,
referenceTagpath,
)
)
print(
"prevInfo: {}\nprevConfig: {}\n prevTrends: {}\n prevMeta: {}\n refTag: {}".format(
previousInfo,
previousConfig,
previousTrends,
previousMeta,
referenceTagpath,
)
)
# Build the dataset for the Info view. Pull each tagName from the dataset to track which manual additions are lost.
currentInfo = Global.DeviceDatasets.generateDeviceDataset(
deviceTagpath="{referenceTagpath}".format(referenceTagpath=referenceTagpath), # DON'T APPEND THIS WITH ANYTHING
datasetTagpath="{datasetTagpath}/Info".format(datasetTagpath=datasetTagpath),
writeToSystem=writeToSystem,
)
# Note manual additions that are lost in the update.
if debug:
currentInfoNames = list(map(lambda x: str(x.get("tagName", "N/A")), currentInfo))
logMessage += Global.DeviceDatasets.getDiff(previousData=previousInfo, currentData=currentInfo, folder="Info")
currentConfig = Global.DeviceDatasets.generateDeviceDataset(
deviceTagpath="{referenceTagpath}/Config".format(referenceTagpath=referenceTagpath),
datasetTagpath="{datasetTagpath}/Config".format(datasetTagpath=datasetTagpath),
writeToSystem=writeToSystem,
)
if debug:
currentConfigNames = list(map(lambda x: str(x.get("tagName", "N/A")), currentConfig))
logMessage += Global.DeviceDatasets.getDiff(previousData=previousConfig, currentData=currentConfigNames, folder="Config")
currentMeta = Global.DeviceDatasets.generateDeviceDataset(
deviceTagpath="{referenceTagpath}/Meta".format(referenceTagpath=referenceTagpath),
datasetTagpath="{datasetTagpath}/Meta".format(datasetTagpath=datasetTagpath),
writeToSystem=True,
)
if debug:
currentMetaNames = list(map(lambda x: str(x.get("tagName", "N/A")), currentMeta))
logMessage += Global.DeviceDatasets.getDiff(previousData=previousMeta, currentData=currentMetaNames, folder="Meta")
currentAlarms = Global.DeviceDatasets.generateDeviceDataset(
deviceTagpath="{referenceTagpath}/Alarms".format(referenceTagpath=referenceTagpath),
datasetTagpath="{datasetTagpath}/Alarms".format(datasetTagpath=datasetTagpath),
writeToSystem=True,
)
if debug:
currentAlarmsNames = list(map(lambda x: str(x.get("tagName", "N/A")), currentAlarms))
logMessage += Global.DeviceDatasets.getDiff(previousData=previousAlarms, currentData=currentAlarmsNames, folder="Alarms")
if len(logMessage) == 0:
logMessage = "{} Successfully generated.".format(timestamp)
# Reset the _Manual Refresh tag back to False.
system.tag.writeAsync([datasetTagpath + "/_Manual Refresh"], [False])
# Write the aggregate log message to _Log Message.
system.tag.writeAsync([datasetTagpath + "/_Log Message"], [logMessage])
return logMessage

View File

@@ -7,11 +7,11 @@
"code.py"
],
"attributes": {
"hintScope": 2,
"lastModificationSignature": "e341a1798bc82f48656303e0c6d816f4f5adf74cac28b4132878419205df89de",
"hintScope": 7,
"lastModificationSignature": "9eb6928e909875055a3f065a11fb7ed868921a79e9ab374e1cdb08972e8e3989",
"lastModification": {
"actor": "Emmanuel",
"timestamp": "2026-04-15T20:54:16Z"
"timestamp": "2026-05-04T19:22:01Z"
}
}
}

View File

@@ -0,0 +1,53 @@
def main(originalDeviceTagpaths, deviceTagpaths, tagsToRead, tagToWrite):
"""
originalDeviceTagpaths: List; a list of qualified tagpaths without their provider remapped.
deviceTagpaths: List; a list of qualified device tagpaths, i.e ['[default]RM2505/PHXA2_RM2505_FCU_4'].
tagsToRead: List; a list of atomic tag names, i.e ['/Meta/EqName', '/Space Temperature', '/System Mode']
tagToWrite: String; the tagPath of where to write the device data, i.e '[PHXA2_IG_COMN]System/VAV' where the tag is a DynamicDeviceSet
"""
# Format the atomic tag name from a full tagpath. i.e:
# "[PHXA2_IG_COMN]RM224/[..]/RTU_01/Space Temperature" -> "spaceTemperature"
def getTagName(tagPath):
name = tagPath.split('/')[-1]
words = name.split()
return words[0].lower() + ''.join(word.capitalize() for word in words[1:])
# Append atomic tag names to each device, returning a fully qualified tagpath.
tagPaths = []
for device in deviceTagpaths:
for tag in tagsToRead:
tagPaths.append(str(device) + str(tag))
tagsPerDevice = len(tagsToRead)
tagData = system.tag.readBlocking(tagPaths)
logger = system.util.getLogger('Tagpaths Script')
# batch read alarm status
activeAlarmsTagpaths = list(map(lambda x: str(x) + '/AlarmSummary/ActiveAlarms', tagPaths)) # Generate tagPaths to ActiveAlarms.
activeAlarmData = system.tag.readBlocking(activeAlarmsTagpaths) # Read tag data.
activeAlarmData = [x.value for x in activeAlarmData] # Get .value from each QualifiedObject.
activeAlarmData = list(map(lambda x: len(x.keys()) != 0, activeAlarmData)) # Check if length > 0, if true, device isAlarmed.
res = []
for batchIndex in range(len(tagPaths) / tagsPerDevice): # read all tags per device
startIndex = batchIndex * tagsPerDevice
endIndex = startIndex + tagsPerDevice
val = {'value': {}, 'style': {}}
# If device is alarmed, highlight red.
val['style'] = {'backgroundColor': '#FF4747', 'color': 'white'} if activeAlarmData[batchIndex] else {}
# Map original tagpath without tag provider reassigned.
# This is necessary for the L4 popup.
originalTagpath = originalDeviceTagpaths[batchIndex]
val['value']['tagPath'] = originalTagpath
# Map tag name to respective tag value.
for i in range(startIndex, endIndex):
tagName = getTagName(tagPaths[i])
tagValue = tagData[i].value
val['value'][tagName] = tagValue
res.append(val)
system.tag.writeBlocking(tagToWrite, system.util.jsonEncode(res))

View File

@@ -7,11 +7,11 @@
"code.py"
],
"attributes": {
"hintScope": 2,
"lastModificationSignature": "a4438c3c50df8495f839d01fc8b494cd7b042d5c1757d91511c0b5f54bd73a5e",
"hintScope": 7,
"lastModificationSignature": "812333a27ba424a63a7bb2138a2c572e69a2111eeab00617fbf1e46cc411e0ab",
"lastModification": {
"actor": "Emmanuel",
"timestamp": "2026-04-15T20:54:16Z"
"timestamp": "2026-05-04T19:01:34Z"
}
}
}

View File

@@ -1,81 +1,43 @@
import pprint, time
Logger = system.util.getLogger("alarm.summary")
provider = "[default]"
def initAlarmSummaries():
""" This function will reset all the alarm summary udt's in the system
"""
allActiveAlarmsTag = []
newValues = []
for t in system.tag.browse(provider, {"tagType":"UdtInstance", "typeId": "AlarmSummary", "recursive":True}):
allActiveAlarmsTag.append(t["fullPath"].toString() +"/ActiveAlarms")
newValues.append({})
def get_severity_style(level):
'''
level: Could be an int (1-4) or a string ("low", "Medium", etc)
'''
# 1. Maping names and values to normalize the input
name_to_level = {
'diagnostic':0, 'low':1, 'medium':2, 'high':3, 'critical':4
}
# 2. Convert a string to compare, if its a string or a number
normalized_level = level
if isinstance(level, basestring):
normalized_level = name_to_level.get(level.lower(), 0)
system.tag.writeAsync(allActiveAlarmsTag, newValues)
def updateAlarmCount(srcPath, priority, state):
pass
# 3. color definition
priorityBGColors = {1:"#7266B7", 2:"#F4B834", 3:"#DE7C33", 4:"#E22028"}
priorityTextColors = {1:"#FAFAFB", 2:"#FAFAFB", 3:"#FAFAFB", 4:"#FAFAFB"}
def calcMaxPriority(levelPath):
""" Pass in the path level to determine the max priority and return the value
levelPath: string of the object level path without the /AlarmSummary/ActiveAlarms
"""
aggAlarmStatus = system.tag.readBlocking([levelPath+"/AlarmSummary/ActiveAlarms"])[0].value.toDict()
# 4. Return an object with the syle
if normalized_level not in priorityBGColors:
return {}
return {
"backgroundColor": priorityBGColors[normalized_level],
"color": priorityTextColors[normalized_level]
}
return determinePriority(aggAlarmStatus)
def determinePriority(tagValue):
""" Use this method to figure out the highest priority level of a tag. Useful with transforms from front-end
Args:
tagValue: dict/document of the expected structure of {srcPath:priority}
Returns:
maxState: int -1 for none, 0-Diag, 1-Low, 2- Med, 3- High, 4-Critical
"""
maxState = -1
for k, v in tagValue.iteritems():
maxState = v if v > maxState else maxState
return maxState
def syncAlarms():
"""
Timer based function to update all of the aggregate alarm tags.
"""
sublog = Logger.createSubLogger("syncAlarms")
start = time.time()
levelAlarmSummary = {} #{"alarmPath":[{srcAlarm:priority}]
for alm in system.alarm.queryStatus(priority=[0,1,2,3,4],state=['ActiveUnacked','ActiveAcked']):
srcPath = alm.getSource().toString()
mainTagPath = srcPath.split("/tag:")[1].split(":/alm")[0]
priorityVal = alm.getPriority().intValue
pathLevelsList = mainTagPath.split("/")
# Because alarms can exist outside of the /Alarms folder in the UDT, we will account for this
almOffset = -1
if "Alarms" not in pathLevelsList:
almOffset= 0
for i in range(1, len(pathLevelsList) + almOffset):
path = provider+"/".join(pathLevelsList[:i])+"/AlarmSummary/ActiveAlarms"
if path not in levelAlarmSummary.keys():
levelAlarmSummary[path]= {}
levelAlarmSummary[path][srcPath] = max([levelAlarmSummary[path].get(srcPath,0), priorityVal ])
# pprint.pprint(levelAlarmSummary)
writeTags, writeVals = [], []
for k, v in levelAlarmSummary.iteritems():
writeTags.append(str(k))
writeVals.append(v)
sublog.debug("%s - %s"%(writeTags, writeVals))
if len(writeTags) == len(writeVals) and len(writeTags) > 0:
system.tag.writeAsync(writeTags, writeVals)
sublog.trace("finished in %s sec"%(time.time() - start))
def getTop3Alarms():
res= []
activeAlarms = system.alarm.queryStatus(state=["ActiveUnacked"])
sortedAlarms = sorted(activeAlarms, key=lambda x: x.getPriority(), reverse=True)
for alm in sortedAlarms[:3]:
prio = str(alm.getPriority())
activeData = alm.getActiveData()
label = activeData.get(EventTime) # O alm.get('label')
res.append({
"value": {
"dispPath": alm.getDisplayPathOrSource(),
"label": alm.get("label")
},
"style": get_severity_style(prio)
})
return res

View File

@@ -7,11 +7,11 @@
"code.py"
],
"attributes": {
"hintScope": 7,
"lastModificationSignature": "36649fc8abff8060b71390e2f49afe477ea8e0096329614f217eda8372941ad9",
"hintScope": 1,
"lastModificationSignature": "3b3bfbd99a44d01714417200bb83d36df2f73238b137abf6b33e2e473e316612",
"lastModification": {
"actor": "Emmanuel",
"timestamp": "2026-04-15T20:54:15Z"
"timestamp": "2026-05-05T21:37:59Z"
}
}
}

View File

@@ -1,22 +0,0 @@
from com.inductiveautomation.ignition.common.alarming.config.CommonAlarmProperties import EventTime
priorityBGColors = {"Diagnostic":"#FFFFFF", "Low":"#7266B7", "Medium":"#F4B834", "High":"#DE7C33", "Critical":"#E22028"}
priorityTextColors = {"Diagnostic":"#222222", "Low":"#FAFAFB", "Medium":"#FAFAFB", "High":"#FAFAFB", "Critical":"#FAFAFB"}
def getTop3Alarms():
# import time
# start= time.time()
res= []
activeAlarms = system.alarm.queryStatus([], ["ActiveUnacked"], ["*"], [], [])
sortedAlarms = system.dataset.toPyDataSet(system.dataset.sort(activeAlarms.getDataset(),3,False))
formatAlarms = [{'eventid':str(alm.EventId), "label":alm.get('label'), "dispPath":alm.getDisplayPathOrSource(), "eventtime":alm.getActiveData().get(EventTime), "priority":str(alm.getPriority())} for alm in activeAlarms]
top3EventIDs = [alm["EventId"] for alm in sortedAlarms[:3]]
for eid in top3EventIDs:
for alm in formatAlarms:
if alm["eventid"] == eid:
res.append({"value":{"dispPath":alm["dispPath"],"label":alm["label"]}, "style":{"backgroundColor":priorityBGColors[alm["priority"]],
"color":priorityTextColors[alm["priority"]]}})
break
# system.perspective.print("alarm update duration %s"%(time.time()- start))
return res

View File

@@ -1,80 +0,0 @@
# Find ALL the devices with a String, set 1 to Enable, 0 to Disable them
def UpdateAllDevices(target):
deviceDataset = system.device.listDevices()
for i in range(0, deviceDataset.getRowCount()):
deviceName = deviceDataset.getValueAt(i, "Name")
if target == 0:
system.device.setDeviceEnabled(deviceName, target)
print "Device: Disabled %s"%(deviceName)
if target == 1:
system.device.setDeviceEnabled(deviceName, target)
print "Device: Enabled %s"%(deviceName)
#Update the devices with a specific string in the name. 1 to enable - 0 to disable
def UpdateDeviceType(taget, deviceType):
deviceDataset = system.device.listDevices()
for i in range(0, deviceDataset.getRowCount()):
deviceName = deviceDataset.getValueAt(i, "Name")
if deviceName.find(deviceType)>-1:
if target == 0:
system.device.setDeviceEnabled(deviceName, target)
print "Device: Disabled %s"%(deviceName)
if target == 1:
system.device.setDeviceEnabled(deviceName, target)
print "Device: Enabled %s"%(deviceName)
def UpdatePanelParams(path, namespace, opc, tagname):
subpaths = [{'name':'/AC Primary Power Fault', 'ext':'.JA_01'},
{'name':'/AC Secondary Power Fault', 'ext':'.JA_02'},
{'name':'/DC Power Supply 1 Fault', 'ext':'.JA_03'},
{'name':'/DC Power Supply 2 Fault', 'ext':'.JA_04'},
{'name':'/High Temperature', 'ext':'.TAH'},
{'name':'/Panel Intrusion', 'ext':'.XA'}]
print "===== Updating Panel Parameters ====="
for subtag in subpaths:
tagpath = str(path) + str(subtag['name'])
tag = system.tag.getConfiguration(tagpath)
name = str(tagname) + str(subtag['ext'])
tag[0]['parameters']['Namespace'] = namespace
tag[0]['parameters']['OPC'] = opc
tag[0]['parameters']['Tag'] = name
system.tag.configure(path, tag, "o")
print "updating: " + str(subtag)
print "===== Panel Parameters Updated ====="
def UpdateUTParams(path, namespace, opc, tagname):
subpaths = [{'name':'/Liquid Level Alarm', 'ext':'.LA'},
{'name':'/Liquid Temperature Alarm High', 'ext':'.TAH'},
{'name':'/Liquid Temperature Alarm High High', 'ext':'.TAHH'},
{'name':'/Pressure Relief Device Alarm', 'ext':'.PRD_ALM'},
{'name':'/Pressure Switch', 'ext':'.PSW'},
{'name':'/Rapid Rise Relay Tripped', 'ext':'.RRR_TRIP'},
{'name':'/Rapid Rise Relay Warning', 'ext':'.RRR_ALM'},
{'name':'/Source 1 Switch', 'ext':'.ZSO01'},
{'name':'/Source 2 Switch', 'ext':'.ZSO02'},
{'name':'/Vacuum Fault Interrupter1 Tripped', 'ext':'.VFI01'},
{'name':'/Vacuum Fault Interrupter2 Tripped', 'ext':'.VFI02'},
{'name':'/Vacuum Switch', 'ext':'.VSW'}]
for subtag in subpaths:
tagpath = str(path) + str(subtag['name'])
tag = system.tag.getConfiguration(tagpath)
name = str(tagname) + str(subtag['ext'])
tag[0]['parameters']['Namespace'] = namespace
tag[0]['parameters']['OPC'] = opc
tag[0]['parameters']['Tag'] = name
system.tag.configure(path, tag, "o")
#def setHistory(UDTNAme):
#parentPath = "[default]_types_/Objects/TEST"
#configs = system.tag.getConfiguration(parentPath, True)
#for folder in configs[0]['tags']:
# UDTName = [folder['name']][0]
# print UDTName
# UDTs = system.tag.getConfiguration(parentPath +'/'+ UDTName, True)
# for tag in UDTs[0]['tags']:
# if str(tag['tagType']) == 'AtomicTag':
# tag['historyEnabled'] = True
# print tag

View File

@@ -0,0 +1,76 @@
# List of enabled gateways,
gtwys = ['[default]']
def get_tagpaths(udt_type):
fltr = {"typeId":udt_type, "tagType":"UdtInstance", "recursive":True}
paths_list = []
for location in equipment.common.gtwys:
results = system.tag.browse(location, fltr)
for result in results:
paths_list.append(result['fullPath'])
return paths_list
def update_tagpaths(data_tagpath, udt_type):
raw_paths = get_tagpaths(udt_type)
paths = [str(p) for p in raw_paths]
document = {'paths':paths}
system.tag.writeBlocking(data_tagpath + '/Paths', document)
def update_data(data_tagpath, sub_paths_map):
'''
config_tag_path: Path to the memory tag that contains the JSON with all the paths
sub_paths_map: Dictionary that maps all the sub_paths with the tag to be used in the table.
'''
logger = system.util.getLogger("DataTableUpdates")
try:
# 1. Read the list of tagpaths.
read_config = system.tag.readBlocking(data_tagpath + '/Paths')
raw_data = read_config[0].value
if not raw_data or 'paths' not in raw_data:
logger.warn("Empty configuration: %s" % data_tagpath)
return
# Order the keys to make sure the reading loop is consistent
paths_list = raw_data.get('paths', [])
col_names = sub_paths_map.keys()
# 2. Build the reading list
read_paths = []
for path in paths_list:
for col in col_names:
read_paths.append(path + sub_paths_map[col])
# 3. Read all the values in one execution
reads = system.tag.readBlocking(read_paths)
# 4. Process the result
data = []
num_cols = len(col_names)
for device_idx, path in enumerate(paths_list):
item = {'device': path.split('/')[-1], 'path': path}
for col_idx, col in enumerate(col_names):
global_idx = (device_idx * num_cols) + col_idx
tag_value = reads[global_idx].value
item[col] = tag_value if tag_value is not None else "0"
data.append(item)
document = {'data':data}
system.tag.writeBlocking(data_tagpath + '/Data', document)
except Exception as e:
logger.error("Fail to update data (%s): %s" % (data_tagpath, str(e)))
def style_table(data_dic):
output = []
for element in data_dic:
try:
severity = int(element.get('Alm', 0 ))
except (TypeError, ValueError):
severity = 0
item = {
"value": element,
"style": {}
}
if severity > 0:
item['style'] = alarm.summary.get_severity_style(severity)
output.append(item)
return output

View File

@@ -0,0 +1,17 @@
{
"scope": "A",
"version": 1,
"restricted": false,
"overridable": true,
"files": [
"code.py"
],
"attributes": {
"hintScope": 7,
"lastModificationSignature": "2082d9f4c43b85e21fc7c8f0a9a964f11dc246f364088a9deb0d76ed98dadb75",
"lastModification": {
"actor": "Emmanuel",
"timestamp": "2026-05-05T21:24:17Z"
}
}
}

View File

@@ -8,10 +8,10 @@
],
"attributes": {
"hintScope": 2,
"lastModificationSignature": "3884853f03c1677b8c792ca6ec847b3304067a5c1b1622618f314a38755d5f6e",
"lastModificationSignature": "1ec497a52d03da78106a8d54b326366024f8493359de30b553b79f53b020cbe1",
"lastModification": {
"actor": "Emmanuel",
"timestamp": "2026-04-15T20:54:16Z"
"timestamp": "2026-05-04T21:52:00Z"
}
}
}

View File

@@ -0,0 +1,50 @@
# Definition of tag location to store tagpaths and updated data.
tagpath = '[default]System/DataTables/DOAS'
udt_type = 'Objects/Air/DOAS'
sub_paths = {
'OAD':'/Outdoor Air Dewpoint',
'BP':'/Building Pressure',
'SAT':'/Supply Air Temperature',
'SAD':'/Supply Air Dewpoint'
}
def update_data(config_tagpath, sub_paths_map):
'''
config_tag_path: Path to the memory tag that contains the JSON with all the paths
sub_paths_map: Dictionary that maps all the sub_paths with the tag to be used in the table.
'''
# 1. Read the list of tagpaths.
raw_data = system.tag.readBlocking(config_tagpath + '/Data')[0].value
paths_list = raw_data.get('paths', [])
if not paths_list:
return []
# Order the keys to make sure the reading loop is consistent
col_names = sub_paths_map.keys()
# 2. Build the reading list
read_paths = []
for path in paths_list:
for col in col_names:
read_paths.append(path + sub_paths_map[col])
# 3. Read all the values in one execution
reads = system.tag.readBlocking(read_paths)
# 4. Process the result
output = []
num_cols = len(col_names)
for device_idx, path in enumerate(paths_list):
item = {"device": path.split('/')[-1]}
for col_idx, col in enumerate(col_names):
global_idx = (device_idx * num_cols) + col_idx
tag_value = reads[global_idx].value
item[col] = tag_value if tag_value is not None else "0"
output.append(item)
return output
def update_tagpaths():
raw_paths = equipment.common.get_tagpaths(udt_type)
paths = [str(p) for p in raw_paths]
document = {'paths':paths}
system.tag.writeBlocking(tagpath + '/Data', document)

View File

@@ -0,0 +1,17 @@
{
"scope": "A",
"version": 1,
"restricted": false,
"overridable": true,
"files": [
"code.py"
],
"attributes": {
"hintScope": 7,
"lastModificationSignature": "4a0f454d3297d1bc7016bba11ad55ad961723511a09a6ccb501176f5fc0f93f1",
"lastModification": {
"actor": "Emmanuel",
"timestamp": "2026-05-05T15:04:19Z"
}
}
}

View File

@@ -1,100 +0,0 @@
def set_DOAS():
tagpaths = ["[Ignition_Common_IO_Gtwy]Roof/SATB1_RF_DOAS01/Config/High Supply Air Temperature Setpoint",
"[Ignition_Common_IO_Gtwy]Roof/SATB1_RF_DOAS01/Control/Schedule Force Occupied",
"[Ignition_Common_IO_Gtwy]Roof/SATB1_RF_DOAS01/Control/Supply Air Dewpoint Setpoint",
"[Ignition_Common_IO_Gtwy]Roof/SATB1_RF_DOAS01/Control/Supply Air Temperature Setpoint",
"[Ignition_Common_IO_Gtwy]Roof/SATB1_RF_DOAS02/Config/High Supply Air Temperature Setpoint",
"[Ignition_Common_IO_Gtwy]Roof/SATB1_RF_DOAS02/Control/Schedule Force Occupied",
"[Ignition_Common_IO_Gtwy]Roof/SATB1_RF_DOAS02/Control/Supply Air Dewpoint Setpoint",
"[Ignition_Common_IO_Gtwy]Roof/SATB1_RF_DOAS02/Control/Supply Air Temperature Setpoint",
"[Ignition_Common_IO_Gtwy]Roof/SATB1_RF_DOAS03/Config/High Supply Air Temperature Setpoint",
"[Ignition_Common_IO_Gtwy]Roof/SATB1_RF_DOAS03/Control/Schedule Force Occupied",
"[Ignition_Common_IO_Gtwy]Roof/SATB1_RF_DOAS03/Control/Supply Air Dewpoint Setpoint",
"[Ignition_Common_IO_Gtwy]Roof/SATB1_RF_DOAS03/Control/Supply Air Temperature Setpoint"
]
data = system.tag.readBlocking(tagpaths)
#print data
idx = 0
w_paths = []
w_values = []
for i in range(0,3):
if data[idx].value == 0:
w_paths.append(tagpaths[idx])
w_values.append(68)
if data[idx+1].value == 0:
w_paths.append(tagpaths[idx+1])
w_values.append(1)
if data[idx+2].value == 0:
w_paths.append(tagpaths[idx+2])
w_values.append(55)
if data[idx+3].value == 0:
w_paths.append(tagpaths[idx+3])
w_values.append(65)
idx += 4
if w_paths:
print "Writing values"
try:
system.tag.writeBlocking(w_paths, w_values)
except:
print "Error"
else:
print "Zeros not found"
def set_FCU():
fcus = ["[Ignition_Common_IO_Gtwy]ER1185/SATB1_HS2_EL1185_FCU01",
"[Ignition_Common_IO_Gtwy]MDF1187/SATB1_HS2_MDF1187_FCU02",
"[Ignition_Common_IO_Gtwy]MDF1187/SATB1_HS2_MDF1187_FCU03",
"[Ignition_Common_IO_Gtwy]ER1160/SATB1_HS2_EL1160_FCU04",
"[Ignition_Common_IO_Gtwy]IDF1174/SATB1_HS2_IDF1174_FCU05",
"[Ignition_Common_IO_Gtwy]IDF1154/SATB1_HS2_IDF1154_FCU06",
"[Ignition_Common_IO_Gtwy]MDF1149/SATB1_HS2_MDF1149_FCU07",
"[Ignition_Common_IO_Gtwy]MDF1149/SATB1_HS2_MDF1149_FCU08",
"[Ignition_Common_IO_Gtwy]FS1285/SATB1_HS2_FS1285_FCU09",
"[Ignition_Common_IO_Gtwy]IDF1286/SATB1_HS2_IDF1286_FCU10",
"[Ignition_Common_IO_Gtwy]IDF1286/SATB1_HS2_IDF1286_FCU11",
"[Ignition_Common_IO_Gtwy]ER1287/SATB1_HS2_EL1287_FCU12",
"[Ignition_Common_IO_Gtwy]ER1284/SATB1_HS2_EL1284_FCU13",
"[Ignition_Common_IO_Gtwy]IDF1274/SATB1_HS2_IDF1274_FCU14",
"[Ignition_Common_IO_Gtwy]IDF1264/SATB1_HS2_IDF1264_FCU15",
"[Ignition_Common_IO_Gtwy]ER1128/SATB1_HS2_EL1128_FCU16",
"[Ignition_Common_IO_Gtwy]MR1125/SATB1_HS2_ME1125_FCU17",
"[Ignition_Common_IO_Gtwy]ER1229/SATB1_HS2_EL1229_FCU18",
"[Ignition_Common_IO_Gtwy]IDF1218/SATB1_HS2_IDF1218_FCU19",
"[Ignition_Common_IO_Gtwy]IDF1122/SATB1_HS2_IDF1122_FCU20",
"[Ignition_Common_IO_Gtwy]FS1232/SATB1_HS2_FS1232_FCU21",
"[Ignition_Common_IO_Gtwy]MR1132/SATB1_HS2_ME1132_FCU22",
"[Ignition_Common_IO_Gtwy]RM1138/SATB1_HS2_RM1138_FCU23",
"[Ignition_Common_IO_Gtwy]RM1138/SATB1_HS2_RM1138_FCU24",
"[Ignition_Common_IO_Gtwy]RM1138/SATB1_HS2_RM1138_FCU25",
"[Ignition_Common_IO_Gtwy]RM1138/SATB1_HS2_RM1138_FCU26",
"[Ignition_Common_IO_Gtwy]RM1219/SATB1_HS2_RM1219_FCU27",
"[Ignition_Common_IO_Gtwy]RM1193/SATB1_HS2_RM1193_FCU28",
"[Ignition_Common_IO_Gtwy]RM1234/SATB1_HS2_RM1234_FCU29"]
tagpaths = []
for fcu in fcus:
tagpaths.append(fcu + "/Config/Space Temperature Cooling Setpoint")
tagpaths.append(fcu + "/Config/Space Temperature Cooling Setpoint DB")
data = system.tag.readBlocking(tagpaths)
#for d in data:
# print d
idx = 0
w_paths = []
w_values = []
for i in range(0,29):
if data[idx].value == 0:
w_paths.append(tagpaths[idx])
w_values.append(74)
if data[idx+1].value == 0:
w_paths.append(tagpaths[idx+1])
w_values.append(1)
idx += 2
if w_paths:
print "writing values"
try:
system.tag.writeBlocking(w_paths, w_values)
except:
print "Error"
else:
print "Zeros not found"

View File

@@ -0,0 +1,22 @@
def handleTimerEvent():
# DOAS update data
tagpath = '[default]System/DataTables/DOAS'
sub_paths = {
'OAD':'/Outdoor Air Dewpoint',
'BP':'/Building Pressure',
'SAT':'/Supply Air Temperature',
'SAD':'/Supply Air Dewpoint',
'Alm':'/AlarmSummary/Highest Active Priority'
}
equipment.common.update_data(tagpath, sub_paths)
# RTU update data
tagpath = '[default]System/DataTables/DOAS'
sub_paths = {
'OAD':'/Outdoor Air Dewpoint',
'BP':'/Building Pressure',
'SAT':'/Supply Air Temperature',
'SAD':'/Supply Air Dewpoint',
'Alm':'/AlarmSummary/Highest Active Priority'
}
equipment.common.update_data(tagpath, sub_paths)

View File

@@ -0,0 +1,20 @@
{
"scope": "G",
"version": 1,
"restricted": false,
"overridable": true,
"files": [
"handleTimerEvent.py"
],
"attributes": {
"sharedThread": true,
"delay": 10000,
"lastModificationSignature": "028eec92dcd85ffca7bd6b750275903fda44afd67a09c1f7bae0c92a7fe642ef",
"fixedDelay": true,
"lastModification": {
"actor": "Emmanuel",
"timestamp": "2026-05-05T21:41:30Z"
},
"enabled": false
}
}