diff --git a/SCADA_PERSPECTIVE_PARENT_PROJECT/com.inductiveautomation.perspective/views/Alarm-Views/RealTime/view.json b/SCADA_PERSPECTIVE_PARENT_PROJECT/com.inductiveautomation.perspective/views/Alarm-Views/RealTime/view.json index c317224..21e0b1a 100644 --- a/SCADA_PERSPECTIVE_PARENT_PROJECT/com.inductiveautomation.perspective/views/Alarm-Views/RealTime/view.json +++ b/SCADA_PERSPECTIVE_PARENT_PROJECT/com.inductiveautomation.perspective/views/Alarm-Views/RealTime/view.json @@ -687,9 +687,9 @@ { "events": { "component": { - "onRowClick": { + "onRowDoubleClick": { "config": { - "script": "\tdata \u003d self.props.selection.data\n\tif not data or len(data) !\u003d 1:\n\t return\n\t\n\trow \u003d data[0]\n\tclickedTagPath \u003d row.get(\"FullTag\", \"\")\n\tMCM \u003d row.get(\"Location\", \"\")\n\t\n\tMCM_Pages_Map \u003d {\n\t\t\"MCM01\": \"Detailed-Views/MCM01 Fluid Inbound Merges 1-4\",\n\t\t\"MCM02\": \"Detailed-Views/MCM02 Fluid Inbound Merges 5-7\",\n\t\t\"MCM03\": \"Detailed-Views/MCM03 Non Con\",\n\t\t\"MCM04\": \"Detailed-Views/MCM04 North Bulk Inbound, Fluid Outbound and Problem Solve\",\n\t\t\"MCM05\": \"Detailed-Views/MCM05 South Bulk Inbound, Fluid Outbound and Problem Solve\",\n\t\t\"MCM06\": \"Detailed-Views/MCM06 Non Con\",\n\t\t\"MCM07\": \"Detailed-Views/MCM07 Bypass\",\n\t}\n\t\n\n\tpage \u003d MCM_Pages_Map.get(MCM)\n\t\n\tif not page:\n\t return\n\t\t\n\tdevice \u003d row.get(\"Device\", \"\")\n\t\n\tif not device or not clickedTagPath:\n\t\treturn\n\n\tpathToDevice \u003d \"\"\n\n#\tcheck for the mcm\n\tif \"MCM\" in device:\n\t\tparts \u003d clickedTagPath.split(\"/\")\n\t\tpathToDevice \u003d \"/\".join(parts[:3])\n\n\telse:\n\t\tindex \u003d clickedTagPath.find(device)\n\t\tif index \u003d\u003d -1:\n\t\t\treturn\n\t\tpathToDevice \u003d clickedTagPath[:index + len(device)]\n\n\tpriority \u003d row.get(\"Priority\", \"\")\n\t\n\tcombined \u003d pathToDevice + \"||\" + priority\n\t\n\t# Navigate to target view, passing the tag to highlight\n\tsystem.perspective.navigate(view \u003d page, params \u003d {\u0027highlightTagPath\u0027: str(combined)})\n\t\n\t\n" + "script": "\n\tmyData \u003d self.props.selection.data\n\n\talarms.alarm_click.handleClick(myData)\n\t\n\t" }, "scope": "G", "type": "script" @@ -1593,37 +1593,37 @@ "style": { "classes": "Alarms-Styles/Diagnostic" }, - "value": "UL1_4_VFD1 - Voltage" + "value": "FL2078_2_VFD1 - Voltage" }, "Device": { "style": { "classes": "Alarms-Styles/Diagnostic" }, - "value": "UL1_4_VFD1" + "value": "FL2078_2_VFD1" }, "Duration": { "style": { "classes": "Alarms-Styles/Diagnostic" }, - "value": "05:42:49" + "value": "07:05:16" }, "FullTag": { "style": { "classes": "Alarms-Styles/Diagnostic" }, - "value": "System/MCM02/Conveyor/UL1_4_VFD1/Voltage" + "value": "System/MCM01/Conveyor/FL2078_2_VFD1/Voltage" }, "ID": { "style": { "classes": "Alarms-Styles/Diagnostic" }, - "value": 5 + "value": 8 }, "Location": { "style": { "classes": "Alarms-Styles/Diagnostic" }, - "value": "MCM02" + "value": "MCM01" }, "Priority": { "style": { @@ -1639,22 +1639,21 @@ "$": [ "ts", 0, - 1750956444058 + 1750962272012 ], - "$ts": 1750936737000 + "$ts": 1750936755000 } }, "Tag": { "style": { "classes": "Alarms-Styles/Diagnostic" }, - "value": "UL1_4_VFD1.HMI.Voltage" + "value": "FL2078_2_VFD1.HMI.Voltage" } } ], - "mode": "multiple interval", - "selectedColumn": "Location", - "selectedRow": 3 + "selectedColumn": "Duration", + "selectedRow": 2 } }, "type": "ia.display.table" @@ -1749,6 +1748,17 @@ { "children": [ { + "events": { + "component": { + "onRowDoubleClick": { + "config": { + "script": "\t\n\tmyData \u003d self.props.selection.data\n\n\talarms.alarm_click.handleClick(myData)" + }, + "scope": "G", + "type": "script" + } + } + }, "meta": { "name": "AlarmsTable" }, @@ -2523,6 +2533,192 @@ "viewPath": "", "visible": true, "width": "" + }, + { + "align": "center", + "boolean": "checkbox", + "dateFormat": "MM/DD/YYYY", + "editable": false, + "field": "FullTag", + "filter": { + "boolean": { + "condition": "" + }, + "date": { + "condition": "", + "value": "" + }, + "enabled": false, + "number": { + "condition": "", + "value": "" + }, + "string": { + "condition": "", + "value": "" + }, + "visible": "on-hover" + }, + "footer": { + "align": "center", + "justify": "left", + "style": { + "classes": "" + }, + "title": "" + }, + "header": { + "align": "center", + "justify": "left", + "style": { + "classes": "" + }, + "title": "" + }, + "justify": "auto", + "nullFormat": { + "includeNullStrings": false, + "nullFormatValue": "", + "strict": false + }, + "number": "value", + "numberFormat": "0,0.##", + "progressBar": { + "bar": { + "color": "", + "style": { + "classes": "" + } + }, + "max": 100, + "min": 0, + "track": { + "color": "", + "style": { + "classes": "" + } + }, + "value": { + "enabled": true, + "format": "0,0.##", + "justify": "center", + "style": { + "classes": "" + } + } + }, + "render": "auto", + "resizable": true, + "sort": "none", + "sortable": true, + "strictWidth": false, + "style": { + "classes": "", + "display": "none" + }, + "toggleSwitch": { + "color": { + "selected": "", + "unselected": "" + } + }, + "viewParams": {}, + "viewPath": "", + "visible": true, + "width": "" + }, + { + "align": "center", + "boolean": "checkbox", + "dateFormat": "MM/DD/YYYY", + "editable": false, + "field": "Device", + "filter": { + "boolean": { + "condition": "" + }, + "date": { + "condition": "", + "value": "" + }, + "enabled": false, + "number": { + "condition": "", + "value": "" + }, + "string": { + "condition": "", + "value": "" + }, + "visible": "on-hover" + }, + "footer": { + "align": "center", + "justify": "left", + "style": { + "classes": "" + }, + "title": "" + }, + "header": { + "align": "center", + "justify": "left", + "style": { + "classes": "" + }, + "title": "" + }, + "justify": "auto", + "nullFormat": { + "includeNullStrings": false, + "nullFormatValue": "", + "strict": false + }, + "number": "value", + "numberFormat": "0,0.##", + "progressBar": { + "bar": { + "color": "", + "style": { + "classes": "" + } + }, + "max": 100, + "min": 0, + "track": { + "color": "", + "style": { + "classes": "" + } + }, + "value": { + "enabled": true, + "format": "0,0.##", + "justify": "center", + "style": { + "classes": "" + } + } + }, + "render": "auto", + "resizable": true, + "sort": "none", + "sortable": true, + "strictWidth": false, + "style": { + "classes": "", + "display": "none" + }, + "toggleSwitch": { + "color": { + "selected": "", + "unselected": "" + } + }, + "viewParams": {}, + "viewPath": "", + "visible": true, + "width": "" } ], "emptyMessage": { @@ -2541,10 +2737,6 @@ }, "pager": { "activeOption": 5 - }, - "selection": { - "enableRowSelection": false, - "mode": "multiple interval" } }, "type": "ia.display.table" @@ -3171,6 +3363,17 @@ "$ts": 1750436956149 } }, + "events": { + "component": { + "onRowDoubleClick": { + "config": { + "script": "\t\n\tmyData \u003d self.props.selection.data\n\n\talarms.alarm_click.handleClick(myData)" + }, + "scope": "G", + "type": "script" + } + } + }, "meta": { "name": "Table" }, @@ -3732,7 +3935,7 @@ "style": { "classes": "" }, - "title": "Severity" + "title": "" }, "justify": "center", "nullFormat": { @@ -3968,6 +4171,192 @@ "viewPath": "", "visible": true, "width": "" + }, + { + "align": "center", + "boolean": "checkbox", + "dateFormat": "MM/DD/YYYY", + "editable": false, + "field": "FullTag", + "filter": { + "boolean": { + "condition": "" + }, + "date": { + "condition": "", + "value": "" + }, + "enabled": false, + "number": { + "condition": "", + "value": "" + }, + "string": { + "condition": "", + "value": "" + }, + "visible": "on-hover" + }, + "footer": { + "align": "center", + "justify": "left", + "style": { + "classes": "" + }, + "title": "" + }, + "header": { + "align": "center", + "justify": "left", + "style": { + "classes": "" + }, + "title": "" + }, + "justify": "auto", + "nullFormat": { + "includeNullStrings": false, + "nullFormatValue": "", + "strict": false + }, + "number": "value", + "numberFormat": "0,0.##", + "progressBar": { + "bar": { + "color": "", + "style": { + "classes": "" + } + }, + "max": 100, + "min": 0, + "track": { + "color": "", + "style": { + "classes": "" + } + }, + "value": { + "enabled": true, + "format": "0,0.##", + "justify": "center", + "style": { + "classes": "" + } + } + }, + "render": "auto", + "resizable": true, + "sort": "none", + "sortable": true, + "strictWidth": false, + "style": { + "classes": "", + "display": "none" + }, + "toggleSwitch": { + "color": { + "selected": "", + "unselected": "" + } + }, + "viewParams": {}, + "viewPath": "", + "visible": true, + "width": "" + }, + { + "align": "center", + "boolean": "checkbox", + "dateFormat": "MM/DD/YYYY", + "editable": false, + "field": "Device", + "filter": { + "boolean": { + "condition": "" + }, + "date": { + "condition": "", + "value": "" + }, + "enabled": false, + "number": { + "condition": "", + "value": "" + }, + "string": { + "condition": "", + "value": "" + }, + "visible": "on-hover" + }, + "footer": { + "align": "center", + "justify": "left", + "style": { + "classes": "" + }, + "title": "" + }, + "header": { + "align": "center", + "justify": "left", + "style": { + "classes": "" + }, + "title": "" + }, + "justify": "auto", + "nullFormat": { + "includeNullStrings": false, + "nullFormatValue": "", + "strict": false + }, + "number": "value", + "numberFormat": "0,0.##", + "progressBar": { + "bar": { + "color": "", + "style": { + "classes": "" + } + }, + "max": 100, + "min": 0, + "track": { + "color": "", + "style": { + "classes": "" + } + }, + "value": { + "enabled": true, + "format": "0,0.##", + "justify": "center", + "style": { + "classes": "" + } + } + }, + "render": "auto", + "resizable": true, + "sort": "none", + "sortable": true, + "strictWidth": false, + "style": { + "classes": "", + "display": "none" + }, + "toggleSwitch": { + "color": { + "selected": "", + "unselected": "" + } + }, + "viewParams": {}, + "viewPath": "", + "visible": true, + "width": "" } ], "emptyMessage": { @@ -3989,7 +4378,22 @@ "bottom": false }, "selection": { - "enableRowSelection": false + "data": [ + { + "Description": "MCM01 - Hello world", + "Device": "MCM01", + "Duration": "06:30:56", + "EndTimestamp": null, + "FullTag": "System/MCM01/MCM01/Beacon_Light", + "ID": 12, + "Location": "MCM01", + "Priority": "High", + "StartTimestamp": "Thu Jun 26 2025 15:57:50 GMT+0400 (Georgia Standard Time)", + "Tag": "MCM01.HMI.Beacon_Light" + } + ], + "selectedColumn": "EndTimestamp", + "selectedRow": 0 }, "style": { "margin": 20 @@ -4381,6 +4785,7 @@ "contentStyle": { "classes": "Background-Styles/Grey-Background" }, + "currentTabIndex": 1, "menuType": "modern", "style": { "classes": "Background-Styles/Grey-Background" diff --git a/SCADA_PERSPECTIVE_PARENT_PROJECT/ignition/named-query/GetAlarms/query.sql b/SCADA_PERSPECTIVE_PARENT_PROJECT/ignition/named-query/GetAlarms/query.sql index 3f3a3e2..b029688 100644 --- a/SCADA_PERSPECTIVE_PARENT_PROJECT/ignition/named-query/GetAlarms/query.sql +++ b/SCADA_PERSPECTIVE_PARENT_PROJECT/ignition/named-query/GetAlarms/query.sql @@ -1,69 +1,71 @@ -WITH Active AS ( - SELECT - ae.id, - ae.eventtime, - ae.eventid, - ae.source, - ae.priority, - ae.displaypath, - TIMESTAMPDIFF(SECOND, ae.eventtime, COALESCE(ae_clear.eventtime, NOW())) AS duration_seconds - FROM alarm_events ae - 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 ( - SELECT aed.id, aed.strValue - FROM alarm_event_data aed - WHERE aed.propname = 'myTag' - GROUP BY aed.id -- Collapse duplicates by id -), -SingleClear AS ( - SELECT eventid, MIN(eventtime) AS eventtime - FROM alarm_events - WHERE eventtype = 1 - GROUP BY eventid -) - -SELECT - Active.id AS ID, - Active.eventtime AS StartTimestamp, - Clear.eventtime AS EndTimestamp, - - 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 SingleClear Clear - ON Active.eventid = Clear.eventid - -LEFT JOIN SingleMyTag aed - ON aed.id = Active.id - -ORDER BY Active.eventtime DESC; +WITH Active AS ( + SELECT + ae.id, + ae.eventtime, + ae.eventid, + ae.source, + ae.priority, + ae.displaypath, + TIMESTAMPDIFF(SECOND, ae.eventtime, COALESCE(ae_clear.eventtime, NOW())) AS duration_seconds + FROM alarm_events ae + 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 ( + SELECT aed.id, aed.strValue + FROM alarm_event_data aed + WHERE aed.propname = 'myTag' + GROUP BY aed.id -- Collapse duplicates by id +), +SingleClear AS ( + SELECT eventid, MIN(eventtime) AS eventtime + FROM alarm_events + WHERE eventtype = 1 + GROUP BY eventid +) + +SELECT + Active.id AS ID, + Active.eventtime AS StartTimestamp, + Clear.eventtime AS EndTimestamp, + + 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 , + aed.strValue AS FullTag, + Active.displaypath as Device + +FROM Active + +LEFT JOIN SingleClear Clear + ON Active.eventid = Clear.eventid + +LEFT JOIN SingleMyTag aed + ON aed.id = Active.id + +ORDER BY Active.eventtime DESC; diff --git a/SCADA_PERSPECTIVE_PARENT_PROJECT/ignition/named-query/GetAlarmsWithCount/query.sql b/SCADA_PERSPECTIVE_PARENT_PROJECT/ignition/named-query/GetAlarmsWithCount/query.sql index fc32678..0cd98a1 100644 --- a/SCADA_PERSPECTIVE_PARENT_PROJECT/ignition/named-query/GetAlarmsWithCount/query.sql +++ b/SCADA_PERSPECTIVE_PARENT_PROJECT/ignition/named-query/GetAlarmsWithCount/query.sql @@ -9,7 +9,7 @@ SELECT '.HMI.', SUBSTRING_INDEX(aed.strValue, '/', -1) ) AS Tag, - + CASE Active.priority WHEN 0 THEN 'Diagnostic' WHEN 1 THEN 'Low' @@ -31,7 +31,11 @@ SELECT ) AS Duration, -- Total number of activations - COUNT(*) AS Count + COUNT(*) AS Count, + + -- Newly added columns + aed.strValue AS FullTag, + Active.displaypath AS Device FROM ( SELECT diff --git a/SCADA_PERSPECTIVE_PARENT_PROJECT/ignition/script-python/alarms/alarm_click/code.py b/SCADA_PERSPECTIVE_PARENT_PROJECT/ignition/script-python/alarms/alarm_click/code.py new file mode 100644 index 0000000..d2c79c8 --- /dev/null +++ b/SCADA_PERSPECTIVE_PARENT_PROJECT/ignition/script-python/alarms/alarm_click/code.py @@ -0,0 +1,49 @@ +def handleClick(data): + if not data or len(data) != 1: + return + + row = data[0] + clickedTagPath = row.get("FullTag", "") + MCM = row.get("Location", "") + + MCM_Pages_Map = { + "MCM01": "Detailed-Views/MCM01 Fluid Inbound Merges 1-4", + "MCM02": "Detailed-Views/MCM02 Fluid Inbound Merges 5-7", + "MCM03": "Detailed-Views/MCM03 Non Con", + "MCM04": "Detailed-Views/MCM04 North Bulk Inbound, Fluid Outbound and Problem Solve", + "MCM05": "Detailed-Views/MCM05 South Bulk Inbound, Fluid Outbound and Problem Solve", + "MCM06": "Detailed-Views/MCM06 Non Con", + "MCM07": "Detailed-Views/MCM07 Bypass", + } + + + page = MCM_Pages_Map.get(MCM) + + if not page: + return + + device = row.get("Device", "") + + if not device or not clickedTagPath: + return + + pathToDevice = "" + + # check for the mcm + if "MCM" in device: + parts = clickedTagPath.split("/") + pathToDevice = "/".join(parts[:3]) + + else: + index = clickedTagPath.find(device) + if index == -1: + return + pathToDevice = clickedTagPath[:index + len(device)] + + priority = row.get("Priority", "") + + #combining with priority + combined = pathToDevice + "||" + priority + + # Navigate to target view, passing the tag to highlight + system.perspective.navigate(view = page, params = {'highlightTagPath': str(combined)}) \ No newline at end of file diff --git a/SCADA_PERSPECTIVE_PARENT_PROJECT/ignition/script-python/alarms/alarm_click/resource.json b/SCADA_PERSPECTIVE_PARENT_PROJECT/ignition/script-python/alarms/alarm_click/resource.json new file mode 100644 index 0000000..4fb4fbb --- /dev/null +++ b/SCADA_PERSPECTIVE_PARENT_PROJECT/ignition/script-python/alarms/alarm_click/resource.json @@ -0,0 +1,17 @@ +{ + "scope": "A", + "version": 1, + "restricted": false, + "overridable": true, + "files": [ + "code.py" + ], + "attributes": { + "lastModification": { + "actor": "admin", + "timestamp": "2025-06-26T18:22:20Z" + }, + "hintScope": 2, + "lastModificationSignature": "b6ff9fb1f8b372d391f1e8e33122b287026821ab30ed938414fca2bd68f451a9" + } +} \ No newline at end of file