Added GetActiveAlarms named query, removed extra scripts in the active alarm table

This commit is contained in:
Salijoghli 2025-06-23 15:25:37 +04:00
parent 76e026896d
commit e9843607db
5 changed files with 188 additions and 56 deletions

View File

@ -488,6 +488,7 @@
],
"custom": {
"priorities": {
"critical": false,
"diagnostic": false,
"high": false,
"low": false,
@ -599,7 +600,7 @@
"component": {
"onActionPerformed": {
"config": {
"script": "\ttry:\n\t # Get table data\n\t data \u003d self.parent.parent.parent.getChild(\"FlexContainer_0\").getChild(\"Table\").props.data\n\t\n\t # CSV header\n\t csv_content \u003d \"Number (ID),Event Timestamp,Duration,Priority,Description,Tag\\n\"\n\t\n\t if data and len(data) \u003e 0:\n\t for row in data:\n\t val \u003d row.get(\"value\", {})\n\t row_data \u003d [\n\t str(val.get(\"NumberID\", \"\")),\n\t str(val.get(\"EventTimestamp\", \"\")),\n\t str(val.get(\"Duration\", \"\")),\n\t str(val.get(\"Priority\", \"\")),\n\t str(val.get(\"Description\", \"\")),\n\t str(val.get(\"Tag\", \"\"))\n\t ]\n\t\n\t # Escape commas for CSV safety\n\t row_data \u003d [field.replace(\",\", \";\") for field in row_data]\n\t csv_content +\u003d \",\".join(row_data) + \"\\n\"\n\t else:\n\t csv_content +\u003d \"No alarms in current view\\n\"\n\t\n\texcept Exception as e:\n\t system.perspective.print(\"Error during CSV export: \" + str(e))\n\t csv_content \u003d \"Error exporting alarm data\\n\"\n\t\n\t# Convert to bytes and trigger download\n\tcsv_bytes \u003d csv_content.encode(\u0027utf-8\u0027)\n\tsystem.perspective.download(\"active_alarms.csv\", csv_bytes)\n "
"script": "\tfrom datetime import datetime\n\ttry:\n\t # Get table data\n\t data \u003d self.parent.parent.parent.getChild(\"FlexContainer_0\").getChild(\"Table\").props.data\n\t \n\t column_order \u003d [\n\t \"ID\",\n\t \"StartTimestamp\", \n\t \"Duration\",\n\t \"Priority\",\n\t \"Location\",\n\t \"Description\",\n\t \"Tag\"\n\t ]\n\t\n\t # CSV header\n\t csv_content \u003d \",\".join(column_order) + \"\\n\"\n\t \n\t def unwrap(v):\n\t\t\tif hasattr(v, \u0027value\u0027):\n\t\t\t\treturn str(v.value)\n\t \t\n\t\t\treturn v\n\t \n\t if data and len(data) \u003e 0:\n\t for item in data:\n\t row_data \u003d []\n\t \n\t for col in column_order:\n\t # Look for the column in the current item\n\t if col in item:\n\t cell \u003d item[col]\n\t # Extract the value from the nested structure\n\t if isinstance(cell, dict) and \"value\" in cell:\n\t raw_value \u003d cell[\"value\"]\n\t else:\n\t raw_value \u003d cell\n\t else:\n\t raw_value \u003d \"\"\n\t \n\t # Process and clean the value\n\t processed_value \u003d unwrap(raw_value).replace(\",\", \";\")\n\t row_data.append(processed_value)\n\t \n\t csv_content +\u003d \",\".join(row_data) + \"\\n\"\n\t else:\n\t csv_content +\u003d \"No alarms in current view\\n\"\n\t\n\texcept Exception as e:\n\t system.perspective.print(\"Export Error: \" + str(e))\n\t csv_content \u003d \"Export failed\\n\"\n\t\n\tcsv_bytes \u003d csv_content.encode(\"utf-8\")\n\tsystem.perspective.download(\"active_alarms.csv\", csv_bytes) \n\t \n\t \n\t \n\t\n#\t if data and len(data) \u003e 0:\n#\t for row in data:\n#\t val \u003d row.get(\"value\", {})\n#\t row_data \u003d [\n#\t str(val.get(\"NumberID\", \"\")),\n#\t str(val.get(\"EventTimestamp\", \"\")),\n#\t str(val.get(\"Duration\", \"\")),\n#\t str(val.get(\"Priority\", \"\")),\n#\t str(val.get(\"Description\", \"\")),\n#\t str(val.get(\"Tag\", \"\"))\n#\t ]\n#\t\n#\t # Escape commas for CSV safety\n#\t row_data \u003d [field.replace(\",\", \";\") for field in row_data]\n#\t csv_content +\u003d \",\".join(row_data) + \"\\n\"\n#\t else:\n#\t csv_content +\u003d \"No alarms in current view\\n\"\n#\t\n#\texcept Exception as e:\n#\t system.perspective.print(\"Error during CSV export: \" + str(e))\n#\t csv_content \u003d \"Error exporting alarm data\\n\"\n#\t\n#\t# Convert to bytes and trigger download\n#\tcsv_bytes \u003d csv_content.encode(\u0027utf-8\u0027)\n#\tsystem.perspective.download(\"active_alarms.csv\", csv_bytes)\n "
},
"scope": "G",
"type": "script"
@ -691,24 +692,6 @@
{
"children": [
{
"custom": {
"rawData": [
{
"style": {
"classes": "Alarms-Styles/Low"
},
"value": {
"Description": "MCM01 Hello world",
"Duration": "00:05:48",
"EventTimestamp": "2025-06-23 13:10:26",
"Location": "MCM01",
"NumberID": 60,
"Priority": "Low",
"Tag": "MCM01.HMI.Beacon_Light"
}
}
]
},
"meta": {
"name": "Table"
},
@ -721,33 +704,34 @@
"config": {
"path": ".../FlexContainer/FlexContainer.custom.priorities"
},
"type": "property"
}
},
"props.data": {
"binding": {
"config": {
"path": "this.custom.rawData"
},
"transforms": [
{
"code": "\t\n\tpriorities \u003d self.custom.priorities if hasattr(self.custom, \"priorities\") else []\n\t\n\tactivePriorities \u003d [k.capitalize() for k, v in priorities.items() if v]\n\t\n\tif not activePriorities:\n\t return value\n\t\n\tfiltered_data \u003d []\n\t\n\tfor row in value:\n\t\tpriority \u003d row[\"value\"][\"Priority\"]\n\t\tif priority in activePriorities:\n\t \t filtered_data.append(row)\n\t\n\treturn filtered_data",
"code": "\t\n\tpriority_to_number \u003d {\n\t \"critical\": 4,\n\t \"high\": 3,\n\t \"medium\": 2,\n\t \"low\": 1,\n\t \"diagnostic\": 0\n\t}\n\t\n\t\n\t# Collect enabled priorities\n\tenabled \u003d [str(priority_to_number[k]) for k, v in value.items() if v]\n\t\n\tresult \u003d \",\".join(enabled)\n\t\n\tif not result:\n\t\treturn \"\"\n\t\n\treturn result\n\t\n",
"type": "script"
}
],
"type": "property"
}
},
"props.query": {
"props.data": {
"binding": {
"config": {
"expression": "now(2000)"
"parameters": {
"priorityList": "{this.custom.priorities}"
},
"polling": {
"enabled": true,
"rate": "3"
},
"queryPath": "GetActiveAlarms"
},
"type": "expr"
},
"onChange": {
"enabled": null,
"script": "\tfrom system import date\n\t\n\ttag_provider \u003d \"[\" + self.session.custom.fc + \"_SCADA_TAG_PROVIDER]\"\n\n\t# Helper: format row for table\n\tdef testRow(eventTimeStamp, duration, location, priority, description, tag, className, numberId):\n\t\treturn {\n\t\t \"value\": {\n\t\t \"NumberID\": numberId,\n\t\t \"EventTimestamp\": eventTimeStamp,\n\t\t \"Duration\": duration,\n\t\t \"Priority\": priority,\n\t\t \"Location\": location,\n\t\t \"Description\": description,\n\t\t \"Tag\": tag,\n\t\t },\n\t\t \"style\": {\n\t\t \"classes\": className,\n\t\t }\n\t\t}\n\t\n\t# Query active alarms\n\tresults \u003d system.alarm.queryStatus(state\u003d[\"ActiveUnacked\", \"ActiveAcked\"])\n\t\n\t# Build rows\n\tdata \u003d []\n\tnumberID \u003d \"\"\n\tlcoation \u003d \"\"\n\tclassName \u003d \"\"\n\tif(results \u003d\u003d0):\n\t\treturn\n\t\n\tfor i, alarm in enumerate(results):\n\t\tactiveTime \u003d alarm.eventTime\n\t\tmyTag \u003d alarm.myTag\n\t\tOPCItemTag \u003d system.tag.read(tag_provider + myTag + \".OpcItemPath\").value\n\t\tif(OPCItemTag):\t\t\n\t\t\tparts \u003d OPCItemTag.split(\".\")\n\t\t\ttag \u003d str(alarm.displayPath) +\".\" + parts[1] + \".\" + parts[2]\n\t\telse:\n\t\t\ttag \u003d \"Unknown tag\"\n\t\t\n\t\ttag_parts \u003d myTag.split(\"/\")\n\t\tif(tag_parts):\n\t\t\tlocation \u003d tag_parts[1] \n\t\telse:\n\t\t\tlocation \u003d \"Unknown Location\"\n\t\t\n\t\t\n\t\ttry:\n\t\t query \u003d system.db.runQuery(\"SELECT id, eventtime FROM alarm_events WHERE eventid \u003d \" + \"\u0027\" + str(alarm.id) + \"\u0027\",\"MariaDB\")\n\t\t if(query):\n\t\t \tnumberID \u003d query[0][0]\n\t\t durationSeconds \u003d date.secondsBetween(activeTime, date.now())\n\t\t durationStr \u003d date.format(date.addSeconds(date.midnight(date.now()), durationSeconds), \"HH:mm:ss\")\n\t\t \n\t\t priority \u003d alarm.priority.toString()\n\t\t if priority \u003d\u003d \"High\":\n\t\t color \u003d \"Alarms-Styles/High\"\n\t\t elif priority \u003d\u003d \"Medium\":\n\t\t className \u003d \"Alarms-Styles/Medium\"\n\t\t elif priority \u003d\u003d \"Low\":\n\t\t className \u003d \"Alarms-Styles/Low\"\n\t\t elif priority \u003d\u003d \"Diagnostic\":\n\t\t className \u003d \"Alarms-Styles/Diagnostic\"\n\t\t else:\n\t\t className \u003d \"Alarms-Styles/NoAlarm\"\n\t\t \n\t\n\t\t data.append(testRow(\n\t\t eventTimeStamp \u003d date.format(activeTime, \"yyyy-MM-dd HH:mm:ss\"),\n\t\t duration \u003d durationStr,\n\t\t location \u003d location,\n\t\t numberId \u003d numberID,\n\t\t priority \u003d priority,\n\t\t description \u003d alarm.myLocation + \" \" + alarm.name,\n\t\t tag \u003d tag,\n\t\t className \u003d className,\n\t\t ))\n\t\texcept:\n\t\t\tpass\n\t\n\tself.custom.rawData \u003d data"
"transforms": [
{
"code": "\n\tfrom system.dataset import toPyDataSet\n\n\tds \u003d toPyDataSet(value)\n\tdata \u003d []\n\n\tcolumn_names \u003d [col for col in ds.columnNames if col !\u003d \"EndTimestamp\"]\n\t\n\n\tfor row in ds:\n\t\tpriority \u003d row[\"Priority\"]\n\n\t\t# Use style class names from Perspective\n\t\tif priority \u003d\u003d \"High\":\n\t\t\tclassName \u003d \"Alarms-Styles/High\"\n\t\telif priority \u003d\u003d \"Medium\":\n\t\t\tclassName \u003d \"Alarms-Styles/Medium\"\n\t\telif priority \u003d\u003d \"Low\":\n\t\t\tclassName \u003d \"Alarms-Styles/Low\"\n\t\telif priority \u003d\u003d \"Diagnostic\":\n\t\t\tclassName \u003d \"Alarms-Styles/Diagnostic\"\n\t\telse:\n\t\t\tclassName \u003d \"Alarms-Styles/NoAlarm\"\n\n\t\t# Apply the style class to all cells in the row\n\t\trow_dict \u003d {\n\t\t\tcol: {\n\t\t\t\t\"value\": row[col],\n\t\t\t\t\"style\": { \"classes\": className }\n\t\t\t} for col in column_names\n\t\t}\n\t\tdata.append(row_dict)\n\n\treturn data",
"type": "script"
}
],
"type": "query"
}
}
},
@ -848,9 +832,9 @@
{
"align": "center",
"boolean": "checkbox",
"dateFormat": "MM/DD/YYYY",
"dateFormat": "MM/DD/YYYY HH:mm:ss",
"editable": false,
"field": "EventTimestamp",
"field": "StartTimestamp",
"filter": {
"boolean": {
"condition": ""
@ -1401,28 +1385,73 @@
"emptyMessage": {
"noData": {
"text": "No Active Alarms"
},
"noFilterResults": {
"text": "No Active Alarms"
}
},
"filter": {
"enabled": true,
"results": {
"data": [
{
"Description": "MCM01 Hello world",
"Duration": "00:03:48",
"EventTimestamp": "2025-06-23 13:10:26",
"Location": "MCM01",
"NumberID": 60,
"Priority": "Low",
"Tag": "MCM01.HMI.Beacon_Light"
}
],
"enabled": true
},
"text": " "
}
},
"selection": {
"mode": "multiple interval"
"data": [
{
"Description": {
"style": {
"classes": "Alarms-Styles/High"
},
"value": "MCM01 - Hello world"
},
"Duration": {
"style": {
"classes": "Alarms-Styles/High"
},
"value": "00:53:47"
},
"ID": {
"style": {
"classes": "Alarms-Styles/High"
},
"value": 103
},
"Location": {
"style": {
"classes": "Alarms-Styles/High"
},
"value": "MCM01"
},
"Priority": {
"style": {
"classes": "Alarms-Styles/High"
},
"value": "High"
},
"StartTimestamp": {
"style": {
"classes": "Alarms-Styles/High"
},
"value": {
"$": [
"ts",
0,
1750677703466
],
"$ts": 1750674476000
}
},
"Tag": {
"style": {
"classes": "Alarms-Styles/High"
},
"value": "MCM01.HMI.Beacon_Light"
}
}
],
"mode": "multiple interval",
"selectedRow": 0
}
},
"type": "ia.display.table"
@ -2923,7 +2952,7 @@
"$": [
"ts",
192,
1750667761263
1750674251724
],
"$ts": 1750435156149
},
@ -2931,7 +2960,7 @@
"$": [
"ts",
192,
1750667761263
1750674251724
],
"$ts": 1750436956149
}
@ -3871,7 +3900,7 @@
"$": [
"ts",
192,
1750667761263
1750674251724
],
"$ts": 1750435156149
},
@ -3879,7 +3908,7 @@
"$": [
"ts",
192,
1750667761263
1750674251724
],
"$ts": 1750436956149
},
@ -4138,7 +4167,6 @@
"contentStyle": {
"classes": "Background-Styles/Grey-Background"
},
"currentTabIndex": 2,
"menuType": "modern",
"style": {
"classes": "Background-Styles/Grey-Background"

View File

@ -0,0 +1,60 @@
WITH Active AS (
SELECT
ae.id,
ae.eventtime,
ae.eventid,
ae.source,
ae.priority,
ae.displaypath,
TIMESTAMPDIFF(SECOND, ae.eventtime, NOW()) AS duration_seconds
FROM alarm_events ae
WHERE ae.eventtype = 0
AND NOT EXISTS (
SELECT 1 FROM alarm_events ae_clear
WHERE ae_clear.eventid = ae.eventid
AND ae_clear.eventtype = 1
)
AND ae.displaypath NOT LIKE '%System Startup%'
AND ae.source NOT LIKE '%System Startup%'
-- Priority filter using FIND_IN_SET for comma-separated values
AND (
:priorityList IS NULL
OR :priorityList = ''
OR FIND_IN_SET(ae.priority, :priorityList) > 0
)
GROUP BY ae.id
),
SingleMyTag AS (
SELECT aed.id, aed.strValue
FROM alarm_event_data aed
WHERE aed.propname = 'myTag'
GROUP BY aed.id
)
SELECT
Active.id AS ID,
Active.eventtime AS StartTimestamp,
NULL AS EndTimestamp, -- no end time since it's still active
CONCAT(
LPAD(FLOOR(Active.duration_seconds / 3600), 2, '0'), ':',
LPAD(FLOOR((Active.duration_seconds % 3600) / 60), 2, '0'), ':',
LPAD(Active.duration_seconds % 60, 2, '0')
) AS Duration,
CONCAT(Active.displaypath, ' - ', SUBSTRING_INDEX(Active.source, ':/alm:', -1)) AS Description,
CASE Active.priority
WHEN 0 THEN 'Diagnostic'
WHEN 1 THEN 'Low'
WHEN 2 THEN 'Medium'
WHEN 3 THEN 'High'
WHEN 4 THEN 'Critical'
ELSE 'Unknown'
END AS Priority,
CONCAT(
Active.displaypath,
'.HMI.',
SUBSTRING_INDEX(aed.strValue, '/', -1)
) AS Tag,
SUBSTRING_INDEX(SUBSTRING_INDEX(aed.strValue, '/', 2), '/', -1) AS Location
FROM Active
LEFT JOIN SingleMyTag aed
ON aed.id = Active.id
ORDER BY Active.eventtime DESC;

View File

@ -0,0 +1,40 @@
{
"scope": "DG",
"version": 2,
"restricted": false,
"overridable": true,
"files": [
"query.sql"
],
"attributes": {
"useMaxReturnSize": false,
"autoBatchEnabled": false,
"fallbackValue": "",
"maxReturnSize": 100,
"cacheUnit": "SEC",
"type": "Query",
"enabled": true,
"cacheAmount": 1,
"cacheEnabled": false,
"database": "MariaDB",
"fallbackEnabled": false,
"lastModificationSignature": "d4eef6a8194fc16fb2061c0ffe057909fb37ddedbe4fd6e4f2416cc6050f6209",
"permissions": [
{
"zone": "default",
"role": ""
}
],
"lastModification": {
"actor": "admin",
"timestamp": "2025-06-23T10:38:45Z"
},
"parameters": [
{
"type": "Parameter",
"identifier": "priorityList",
"sqlType": 7
}
]
}
}

View File

@ -11,6 +11,8 @@ WITH Active AS (
LEFT JOIN alarm_events ae_clear
ON ae.eventid = ae_clear.eventid AND ae_clear.eventtype = 1
WHERE ae.eventtype = 0
AND ae.displaypath NOT LIKE '%System Startup%'
AND ae.source NOT LIKE '%System Startup%'
GROUP BY ae.id -- Ensure one row per alarm
),
SingleMyTag AS (

View File

@ -46,6 +46,8 @@ FROM (
LEFT JOIN alarm_events ae_clear
ON ae.eventid = ae_clear.eventid AND ae_clear.eventtype = 1
WHERE ae.eventtype = 0
AND ae.displaypath NOT LIKE '%System Startup%'
AND ae.source NOT LIKE '%System Startup%'
) AS Active
-- OPC tag path for building .hmi.Tag output