20260505 Backup
This commit is contained in:
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user