def handleTimerEvent(): # --------------------------------------- # Gateway Timer Script - Active Alarms Table # --------------------------------------- # # Runs every 3 seconds to query active alarms and populate a dataset table. # # Features: # - Queries runtime alarms (ActiveUnacked, ActiveAcked) from alarm provider # - Joins with alarm_events database table to get DB IDs and event timestamps # - Filters alarms by minimum priority level (read from tag) # - Calculates alarm duration from DB event time # - Excludes System Startup/Shutdown alarms # - Optional age filter to hide old ghost alarms # # Output Dataset Columns: # - NumberID: Database ID from alarm_events table # - EventTimestamp: Formatted timestamp from alarm_events # - Duration: Calculated duration (HH:MM:SS) # - Priority: Alarm priority text (Diagnostic/Low/Medium/High/Critical) # - Location: Custom alarm property (myLocation) # - Description: Formatted from display path and source # - Tag: Custom alarm property (myTag) # - Style: CSS style class based on priority # import system TAG_TABLE = "[TPA8_SCADA_TAG_PROVIDER]System/Queries/Alarms/ActiveAlarmsTable" PROVIDER = "TPA8_SCADA_TAG_PROVIDER" TAG_MIN_PRIO = "[TPA8_SCADA_TAG_PROVIDER]System/Queries/Alarms/Priority" DB = "MariaDB" logger = system.util.getLogger("TPA8_ActiveAlarms") MAX_AGE_HOURS = 0 def priority_text_and_style(p_raw): if p_raw is None: text = "Unknown" else: text = str(p_raw) p = text.lower() if p in ("high", "critical"): style = "Alarms-Styles/High" elif p == "medium": style = "Alarms-Styles/Medium" elif p == "low": style = "Alarms-Styles/Low" elif p == "diagnostic": style = "Alarms-Styles/Diagnostic" else: style = "Alarms-Styles/NoAlarm" return text, style def priority_level(p_text): p = (p_text or "").lower() if p == "diagnostic": return 0 if p == "low": return 1 if p == "medium": return 2 if p in ("high", "critical"): return 3 return 0 try: try: min_val = system.tag.readBlocking([TAG_MIN_PRIO])[0].value try: min_prio_level = int(min_val) except: min_prio_level = 0 except: min_prio_level = 0 if min_prio_level < 0: min_prio_level = 0 if min_prio_level > 3: min_prio_level = 3 headers_table = ["NumberID", "EventTimestamp", "Duration", "Priority", "Location", "Description", "Tag", "Style"] events = system.alarm.queryStatus( state=["ActiveUnacked", "ActiveAcked"], provider=[PROVIDER], includeShelved=False, includeSystem=False ) if not events: ds_empty_table = system.dataset.toDataSet(headers_table, []) system.tag.writeBlocking([TAG_TABLE], [ds_empty_table]) now = system.date.now() max_age_msec = MAX_AGE_HOURS * 60L * 60L * 1000L if MAX_AGE_HOURS > 0 else None eventIdSet = set() for ev in events: try: eid = str(ev.id) except: eid = None if eid: eventIdSet.add(eid) idMap = {} if eventIdSet: try: eventIds = list(eventIdSet) placeholders = ",".join(["?"] * len(eventIds)) sql_ids = """ SELECT eventid, MIN(id) AS dbId, MIN(eventtime) AS eventtime FROM alarm_events WHERE eventid IN (%s) AND eventtype = 0 GROUP BY eventid """ % placeholders ds_ids = system.db.runPrepQuery(sql_ids, eventIds, DB) for row in ds_ids: try: eid = str(row["eventid"]) dbId = row["dbId"] evT = row["eventtime"] if dbId is not None and evT is not None: idMap[eid] = (dbId, evT) except: pass except Exception, exId: logger.error("ActiveAlarms: DB id map error: %s" % exId) idMap = {} rows_table = [] for ev in events: try: try: eid_str = str(ev.id) except: eid_str = None if not eid_str: continue info = idMap.get(eid_str) if not info: continue dbId, evTime = info srcObj = ev.getSource() dpObj = ev.getDisplayPath() src = str(srcObj) if srcObj is not None else "" dp = str(dpObj) if dpObj is not None else "" txt = src + " " + dp if "System Startup" in txt or "System Shutdown" in txt: continue try: loc = ev.getOrElse("myLocation", "") except: loc = "" if loc is None: loc = "" location = str(loc) try: tagVal = ev.getOrElse("myTag", "") except: tagVal = "" if tagVal is None: tagVal = "" tagPath = str(tagVal) try: p_raw = ev.getOrElse("priority", None) except: p_raw = None if p_raw is None: try: p_raw = ev.getPriority() except: p_raw = None priorityText, styleClass = priority_text_and_style(p_raw) level = priority_level(priorityText) if level < min_prio_level: continue try: diff_ms = system.date.toMillis(now) - system.date.toMillis(evTime) except: diff_ms = 0 if diff_ms < 0: diff_ms = -diff_ms if max_age_msec is not None and diff_ms > max_age_msec: continue total_sec = diff_ms / 1000 hours = total_sec // 3600 minutes = (total_sec % 3600) // 60 seconds = total_sec % 60 duration_str = "%02d:%02d:%02d" % (hours, minutes, seconds) event_ts_str = system.date.format(evTime, "yyyy-MM-dd HH:mm:ss") if ":/alm:" in src: desc_suffix = src.split(":/alm:", 1)[1] else: desc_suffix = src description = (dp.replace("_", "-") + " " + desc_suffix).strip() try: num_id = int(dbId) except: continue rows_table.append([ num_id, event_ts_str, duration_str, priorityText, location, description, tagPath, styleClass ]) except Exception, rowErr: try: alarm_id = str(ev.id) except: alarm_id = "unknown" logger.error("ActiveAlarms row error for %s: %s" % (alarm_id, rowErr)) ds_table = system.dataset.toDataSet(headers_table, rows_table) system.tag.writeBlocking([TAG_TABLE], [ds_table]) except Exception, e: logger.error("ActiveAlarms update failed: %s" % e)