modified the script codes, now correctly displaying the states of devices, modified the device mapping and showing all the releated devices for the conveyors and chutes. added new tags for the VFD and showign falted info and possible solutions in the docked vfd view
|
After Width: | Height: | Size: 19 KiB |
@ -1,308 +0,0 @@
|
||||
import os, json, sys
|
||||
|
||||
global_device_mapping = {}
|
||||
|
||||
def build_device_mapping(full_tag_path):
|
||||
"""
|
||||
Builds global_device_mapping for devices that:
|
||||
- Belong to the same PLC (index 1)
|
||||
- Are children of the clicked device (start with clicked_name + "_")
|
||||
"""
|
||||
global global_device_mapping
|
||||
global_device_mapping.clear()
|
||||
|
||||
|
||||
|
||||
try:
|
||||
# Parse PLC and clicked device
|
||||
path_parts = full_tag_path.split("/")
|
||||
plc_name = path_parts[1] if len(path_parts) > 1 else path_parts[0]
|
||||
clicked_name = path_parts[-1] if len(path_parts) > 0 else ""
|
||||
if "_VFD" in clicked_name:
|
||||
idx = clicked_name.find("_VFD")
|
||||
if idx != -1:
|
||||
clicked_name = clicked_name[:idx]
|
||||
|
||||
project_name = system.util.getProjectName()
|
||||
base_path = (
|
||||
os.getcwd().replace("\\", "/")
|
||||
+ "/data/projects/"
|
||||
+ project_name
|
||||
+ "/com.inductiveautomation.perspective/Views/autStand/Detailed_Views/MCM-Views"
|
||||
)
|
||||
|
||||
if not os.path.exists(base_path):
|
||||
system.perspective.print("Path not found: " + base_path)
|
||||
return {}
|
||||
|
||||
# loop through all view folders
|
||||
for view_folder in os.listdir(base_path):
|
||||
json_file = os.path.join(base_path, view_folder, "view.json")
|
||||
if not os.path.isfile(json_file):
|
||||
continue
|
||||
|
||||
try:
|
||||
with open(json_file, "r") as fh:
|
||||
view_json = json.load(fh)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
# go one level deeper: root -> children[0] (coordinateContainer) -> its children
|
||||
root_children = (view_json.get("root") or {}).get("children") or []
|
||||
if not root_children:
|
||||
continue
|
||||
|
||||
container = root_children[0]
|
||||
children = container.get("children") or []
|
||||
|
||||
for child in children:
|
||||
props = child.get("props") or {}
|
||||
params = props.get("params") or {}
|
||||
tag_props = params.get("tagProps")
|
||||
|
||||
if isinstance(tag_props, list) and len(tag_props) > 0:
|
||||
tag_prop = str(tag_props[0])
|
||||
parts = tag_prop.split("/")
|
||||
|
||||
if len(parts) > 1 and parts[1] == plc_name:
|
||||
|
||||
dev_name = parts[-1]
|
||||
if len(parts) > 3 and parts[-2] == clicked_name:
|
||||
dev_name = clicked_name + "_" + parts[-1]
|
||||
system.perspective.print(dev_name)
|
||||
|
||||
# ONLY include devices that are children of clicked_name
|
||||
else:
|
||||
dev_name = parts[-1]
|
||||
prefix = clicked_name + "_"
|
||||
|
||||
if dev_name.startswith(prefix) or (len(parts) > 3 and parts[-2] == clicked_name):
|
||||
global_device_mapping[dev_name] = {
|
||||
"tagPath": tag_prop,
|
||||
"zone": view_folder
|
||||
}
|
||||
|
||||
return global_device_mapping
|
||||
|
||||
except Exception as e:
|
||||
whid = "unknown"
|
||||
try:
|
||||
whid = system.tag.readBlocking("Configuration/FC")[0].value
|
||||
except:
|
||||
pass
|
||||
logger = system.util.getLogger("%s-build_device_mapping" % whid)
|
||||
exc_type, exc_obj, tb = sys.exc_info()
|
||||
logger.error("Error at line %s: %s" % (tb.tb_lineno, exc_obj))
|
||||
return {}
|
||||
|
||||
def build_device_table(self):
|
||||
"""
|
||||
Converts global_device_mapping into a list of dictionaries:
|
||||
Keys: Device, Status
|
||||
Reads each tag value, falls back to 'Unknown' if error/null.
|
||||
"""
|
||||
rows = []
|
||||
state_mappings = {
|
||||
0: "Closed",
|
||||
1: "Actuated",
|
||||
2: "Communication Faulted",
|
||||
3: "Conveyor Running In Maintenance Mode",
|
||||
4: "Disabled",
|
||||
5: "Disconnected",
|
||||
6: "Stopped",
|
||||
7: "Enabled Not Running",
|
||||
8: "Encoder Fault",
|
||||
9: "Energy Management",
|
||||
10: "ESTOP Was Actuated",
|
||||
11: "EStopped",
|
||||
12: "EStopped Locally",
|
||||
13: "Extended Faulted",
|
||||
14: "Full",
|
||||
15: "Gaylord Start Pressed",
|
||||
16: "Jam Fault",
|
||||
17: "Jammed",
|
||||
18: "Loading Allowed",
|
||||
19: "Loading Not Allowed",
|
||||
20: "Low Air Pressure Fault Was Present",
|
||||
21: "Maintenance Mode",
|
||||
22: "Conveyor Stopped In Maintenance Mode",
|
||||
23: "Motor Faulted",
|
||||
24: "Motor Was Faulted",
|
||||
25: "Normal",
|
||||
26: "Off Inactive",
|
||||
27: "Open",
|
||||
28: "PLC Ready To Run",
|
||||
29: "Package Release Pressed",
|
||||
30: "Power Branch Was Faulted",
|
||||
31: "Pressed",
|
||||
32: "Ready To Receive",
|
||||
33: "Running",
|
||||
34: "Started",
|
||||
35: "Stopped",
|
||||
36: "System Started",
|
||||
37: "Unknown",
|
||||
38: "VFD Fault",
|
||||
39: "Conveyor Running In Power Saving Mode",
|
||||
40: "Conveyor Jogging In Maintenance Mode",
|
||||
41: "VFD Reset Required",
|
||||
42: "Jam Reset Push Button Pressed",
|
||||
43: "Start Push Button Pressed",
|
||||
44: "Stop Push Button Pressed",
|
||||
45: "No Container",
|
||||
46: "Ready To Be Enabled",
|
||||
47: "Half Full",
|
||||
48: "Enabled",
|
||||
49: "Tipper Faulted"
|
||||
}
|
||||
|
||||
try:
|
||||
for dev_name, info in global_device_mapping.items():
|
||||
tagPath = info.get("tagPath", "")
|
||||
status_value = ""
|
||||
provider = "[" + self.session.custom.fc + "_SCADA_TAG_PROVIDER]"
|
||||
path = provider + tagPath + "/STATE"
|
||||
|
||||
if tagPath:
|
||||
try:
|
||||
result = system.tag.readBlocking([path])[0]
|
||||
status_value = state_mappings.get(result.value, "Unknown")
|
||||
except:
|
||||
status_value = "Unknown"
|
||||
|
||||
# Append as dictionary
|
||||
rows.append({
|
||||
'Device': dev_name,
|
||||
'Status': status_value
|
||||
})
|
||||
|
||||
return rows
|
||||
|
||||
except Exception as e:
|
||||
system.perspective.print("Error building device table: %s" % e)
|
||||
return [] # Return empty list on error
|
||||
|
||||
|
||||
def getAllTags(self, tagPath, section="all"):
|
||||
"""
|
||||
Reads all tags under a UDT instance (recursively) and returns a list of dictionaries.
|
||||
|
||||
Supports:
|
||||
- VFD (Drive folder)
|
||||
- Conveyor (skips Drive)
|
||||
- Chute (root + PE/PRX/EN tags)
|
||||
- Single Photoeyes (PE1/PE2)
|
||||
- Single Prox Sensors (PRX1/PRX2)
|
||||
- Enable buttons (EN_Color, EN_State, EN_Priority)
|
||||
- Tracking Photoeyes (TPE, handles both folder- and struct-style UDTs)
|
||||
"""
|
||||
|
||||
rows = []
|
||||
|
||||
try:
|
||||
providerPath = "[" + self.session.custom.fc + "_SCADA_TAG_PROVIDER]"
|
||||
driveFolderName = "Drive"
|
||||
|
||||
|
||||
# === Utility: read a single atomic tag ===
|
||||
def readSingleTag(path, prefix=""):
|
||||
try:
|
||||
result = system.tag.readBlocking([providerPath + path])[0]
|
||||
value = str(result.value) if result.quality.isGood() else "Unknown"
|
||||
except:
|
||||
value = "Unknown"
|
||||
|
||||
displayName = prefix + path.split("/")[-1] if prefix else path.split("/")[-1]
|
||||
rows.append({
|
||||
"Name": displayName,
|
||||
"OPC Path": path,
|
||||
"Value": value
|
||||
})
|
||||
|
||||
# === Utility: recursive browse ===
|
||||
def browseRecursive(basePath, prefix=""):
|
||||
children = system.tag.browse(providerPath + basePath).getResults()
|
||||
|
||||
for child in children:
|
||||
tagType = str(child.get("tagType", ""))
|
||||
name = str(child.get("name", ""))
|
||||
fullPath = str(child.get("fullPath", ""))
|
||||
|
||||
if fullPath.startswith("[") and "]" in fullPath:
|
||||
fullPath = fullPath.split("]", 1)[1]
|
||||
|
||||
# --- Conveyor filter (skip Drive folder) ---
|
||||
if section == "conveyor" and name == driveFolderName:
|
||||
continue
|
||||
|
||||
if tagType == "Folder":
|
||||
newPrefix = prefix + name + "/" if prefix else name + "/"
|
||||
browseRecursive(basePath + "/" + name, newPrefix)
|
||||
elif tagType == "AtomicTag":
|
||||
readSingleTag(fullPath, prefix)
|
||||
|
||||
# === MAIN ENTRY POINT ===
|
||||
if section == "vfd":
|
||||
# Browse only inside Drive folder
|
||||
drivePath = tagPath + "/" + driveFolderName
|
||||
browseRecursive(drivePath)
|
||||
|
||||
elif tagPath.upper().endswith("/EN"):
|
||||
# --- Handle flat EN_ tags ---
|
||||
parentPath = "/".join(tagPath.split("/")[:-1])
|
||||
children = system.tag.browse(providerPath + parentPath).getResults()
|
||||
|
||||
for child in children:
|
||||
tagType = str(child.get("tagType", ""))
|
||||
name = str(child.get("name", ""))
|
||||
if tagType == "AtomicTag" and name.upper().startswith("EN_"):
|
||||
fullPath = str(child.get("fullPath", ""))
|
||||
if fullPath.startswith("[") and "]" in fullPath:
|
||||
fullPath = fullPath.split("]", 1)[1]
|
||||
readSingleTag(fullPath)
|
||||
|
||||
elif tagPath.upper().endswith(("PE1", "PE2", "PRX1", "PRX2")) and "/TPE/" not in tagPath.upper():
|
||||
# --- Single sensors ---
|
||||
readSingleTag(tagPath)
|
||||
|
||||
else:
|
||||
# --- Default path ---
|
||||
browseResult = system.tag.browse(providerPath + tagPath).getResults()
|
||||
|
||||
if not browseResult:
|
||||
# Possibly a struct-style UDT (like some TPEs)
|
||||
system.perspective.print("Empty browse for {}, checking struct value...".format(tagPath))
|
||||
try:
|
||||
result = system.tag.readBlocking([providerPath + tagPath])[0]
|
||||
value = result.value
|
||||
|
||||
# If we got a STRUCT, expand it into sub-rows
|
||||
if isinstance(value, dict):
|
||||
system.perspective.print("Detected STRUCT value, expanding {}".format(tagPath))
|
||||
|
||||
def flattenStruct(struct, base=""):
|
||||
for k, v in struct.items():
|
||||
newName = base + "/" + k if base else k
|
||||
if isinstance(v, dict):
|
||||
flattenStruct(v, newName)
|
||||
else:
|
||||
rows.append({
|
||||
"Name": newName,
|
||||
"OPC Path": tagPath + "/" + newName,
|
||||
"Value": str(v)
|
||||
})
|
||||
|
||||
flattenStruct(value)
|
||||
else:
|
||||
# Not a struct, just read it normally
|
||||
readSingleTag(tagPath)
|
||||
except Exception as ex:
|
||||
system.perspective.print("Fallback read failed for {}: {}".format(tagPath, ex))
|
||||
else:
|
||||
# Normal case — browse folder/UDT structure
|
||||
browseRecursive(tagPath)
|
||||
|
||||
return rows
|
||||
|
||||
except Exception as e:
|
||||
system.perspective.print("Error in getAllTags: {}".format(e))
|
||||
return []
|
||||
@ -3,7 +3,7 @@
|
||||
"color": "#000000",
|
||||
"deviceName": "S03_1_JR1",
|
||||
"priority": "No Active Alarms",
|
||||
"state": "Offline"
|
||||
"state": "Actuated"
|
||||
},
|
||||
"params": {
|
||||
"demoColor": -1,
|
||||
@ -71,232 +71,6 @@
|
||||
"persistent": true
|
||||
},
|
||||
"custom.state": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"fallbackDelay": 2.5,
|
||||
"mode": "indirect",
|
||||
"references": {
|
||||
"0": "{view.params.tagProps[0]}",
|
||||
"fc": "{session.custom.fc}"
|
||||
},
|
||||
"tagPath": "[{fc}_SCADA_TAG_PROVIDER]{0}/State"
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"expression": "coalesce({value},-1)",
|
||||
"type": "expression"
|
||||
},
|
||||
{
|
||||
"fallback": "Offline",
|
||||
"inputType": "scalar",
|
||||
"mappings": [
|
||||
{
|
||||
"input": 0,
|
||||
"output": "Closed"
|
||||
},
|
||||
{
|
||||
"input": 1,
|
||||
"output": "Actuated"
|
||||
},
|
||||
{
|
||||
"input": 2,
|
||||
"output": "Communication Faulted"
|
||||
},
|
||||
{
|
||||
"input": 3,
|
||||
"output": "Conveyor Running In Maintenance Mode"
|
||||
},
|
||||
{
|
||||
"input": 4,
|
||||
"output": "Disabled"
|
||||
},
|
||||
{
|
||||
"input": 5,
|
||||
"output": "Disconnected"
|
||||
},
|
||||
{
|
||||
"input": 6,
|
||||
"output": "Stopped"
|
||||
},
|
||||
{
|
||||
"input": 7,
|
||||
"output": "Enabled Not Running"
|
||||
},
|
||||
{
|
||||
"input": 8,
|
||||
"output": "Encoder Fault"
|
||||
},
|
||||
{
|
||||
"input": 9,
|
||||
"output": "Energy Management"
|
||||
},
|
||||
{
|
||||
"input": 10,
|
||||
"output": "ESTOP Was Actuated"
|
||||
},
|
||||
{
|
||||
"input": 11,
|
||||
"output": "EStopped"
|
||||
},
|
||||
{
|
||||
"input": 12,
|
||||
"output": "EStopped Locally"
|
||||
},
|
||||
{
|
||||
"input": 13,
|
||||
"output": "Extended Faulted"
|
||||
},
|
||||
{
|
||||
"input": 14,
|
||||
"output": "Full"
|
||||
},
|
||||
{
|
||||
"input": 15,
|
||||
"output": "Gaylord Start Pressed"
|
||||
},
|
||||
{
|
||||
"input": 16,
|
||||
"output": "Jam Fault"
|
||||
},
|
||||
{
|
||||
"input": 17,
|
||||
"output": "Jammed"
|
||||
},
|
||||
{
|
||||
"input": 18,
|
||||
"output": "Loading Allowed"
|
||||
},
|
||||
{
|
||||
"input": 19,
|
||||
"output": "Loading Not Allowed"
|
||||
},
|
||||
{
|
||||
"input": 20,
|
||||
"output": "Low Air Pressure Fault Was Present"
|
||||
},
|
||||
{
|
||||
"input": 21,
|
||||
"output": "Maintenance Mode"
|
||||
},
|
||||
{
|
||||
"input": 22,
|
||||
"output": "Conveyor Stopped In Maintenance Mode"
|
||||
},
|
||||
{
|
||||
"input": 23,
|
||||
"output": "Motor Faulted"
|
||||
},
|
||||
{
|
||||
"input": 24,
|
||||
"output": "Motor Was Faulted"
|
||||
},
|
||||
{
|
||||
"input": 25,
|
||||
"output": "Normal"
|
||||
},
|
||||
{
|
||||
"input": 26,
|
||||
"output": "Off Inactive"
|
||||
},
|
||||
{
|
||||
"input": 27,
|
||||
"output": "Open"
|
||||
},
|
||||
{
|
||||
"input": 28,
|
||||
"output": "PLC Ready To Run"
|
||||
},
|
||||
{
|
||||
"input": 29,
|
||||
"output": "Package Release Pressed"
|
||||
},
|
||||
{
|
||||
"input": 30,
|
||||
"output": "Power Branch Was Faulted"
|
||||
},
|
||||
{
|
||||
"input": 31,
|
||||
"output": "Pressed"
|
||||
},
|
||||
{
|
||||
"input": 32,
|
||||
"output": "Ready To Receive"
|
||||
},
|
||||
{
|
||||
"input": 33,
|
||||
"output": "Running"
|
||||
},
|
||||
{
|
||||
"input": 34,
|
||||
"output": "Started"
|
||||
},
|
||||
{
|
||||
"input": 35,
|
||||
"output": "Stopped"
|
||||
},
|
||||
{
|
||||
"input": 36,
|
||||
"output": "System Started"
|
||||
},
|
||||
{
|
||||
"input": 37,
|
||||
"output": "Unknown"
|
||||
},
|
||||
{
|
||||
"input": 38,
|
||||
"output": "VFD Fault"
|
||||
},
|
||||
{
|
||||
"input": 39,
|
||||
"output": "Conveyor Running In Power Saving Mode"
|
||||
},
|
||||
{
|
||||
"input": 40,
|
||||
"output": "Conveyor Jogging In Maintenance Mode"
|
||||
},
|
||||
{
|
||||
"input": 41,
|
||||
"output": "VFD Reset Required"
|
||||
},
|
||||
{
|
||||
"input": 42,
|
||||
"output": "Jam Reset Push Button Pressed"
|
||||
},
|
||||
{
|
||||
"input": 43,
|
||||
"output": "Start Push Button Pressed"
|
||||
},
|
||||
{
|
||||
"input": 44,
|
||||
"output": "Stop Push Button Pressed"
|
||||
},
|
||||
{
|
||||
"input": 45,
|
||||
"output": "No Container"
|
||||
},
|
||||
{
|
||||
"input": 46,
|
||||
"output": "Ready To Be Enabled"
|
||||
},
|
||||
{
|
||||
"input": 47,
|
||||
"output": "Half Full"
|
||||
},
|
||||
{
|
||||
"input": 48,
|
||||
"output": "Enabled"
|
||||
},
|
||||
{
|
||||
"input": 49,
|
||||
"output": "Tipper Faulted"
|
||||
}
|
||||
],
|
||||
"outputType": "scalar",
|
||||
"type": "map"
|
||||
}
|
||||
],
|
||||
"type": "tag"
|
||||
},
|
||||
"persistent": true
|
||||
},
|
||||
"custom.view": {
|
||||
@ -307,6 +81,10 @@
|
||||
"persistent": true
|
||||
},
|
||||
"params.tagProps": {
|
||||
"onChange": {
|
||||
"enabled": null,
|
||||
"script": "\ttagPath \u003d currentValue.value[0].value #I know this looks ugly\t\t\t\n\tstatus \u003d autStand.devices.get_single_device_status(self, tagPath)\n\tself.view.custom.state \u003d status\n\n"
|
||||
},
|
||||
"paramDirection": "input",
|
||||
"persistent": true
|
||||
},
|
||||
@ -594,7 +594,7 @@
|
||||
"dom": {
|
||||
"onClick": {
|
||||
"config": {
|
||||
"script": "\t#create devices and tags lists for the conveyor\n\tprops \u003d self.view.params.tagProps[0]\n\tautStand.devices.build_device_mapping(props)\n\tdevice_table_dataset \u003d autStand.devices.build_device_table(self)\n\ttags_table_dataset \u003d autStand.devices.getAllTags(self, props)\n\tsystem.perspective.openDock(\u0027Docked-East-Conv\u0027,params\u003d{\u0027tagProps\u0027:self.view.params.tagProps, \"devices\": device_table_dataset, \"tags\":tags_table_dataset})"
|
||||
"script": "\t#create devices and tags lists for the conveyor\n\tprops \u003d self.view.params.tagProps[0]\n\tautStand.devices.build_device_mapping(props)\n\tdevice_table_dataset \u003d autStand.devices.build_device_table(self)\n\ttags_table_dataset \u003d autStand.devices.getAllTags(self, props, section \u003d \"conveyor\")\n\tsystem.perspective.openDock(\u0027Docked-East-Conv\u0027,params\u003d{\u0027tagProps\u0027:self.view.params.tagProps, \"devices\": device_table_dataset, \"tags\":tags_table_dataset})"
|
||||
},
|
||||
"scope": "G",
|
||||
"type": "script"
|
||||
@ -443,7 +443,7 @@
|
||||
"basis": "32px"
|
||||
},
|
||||
"props": {
|
||||
"text": 20,
|
||||
"text": 6,
|
||||
"textStyle": {
|
||||
"fontSize": 10,
|
||||
"textAlign": "center"
|
||||
@ -472,7 +472,7 @@
|
||||
"basis": "32px"
|
||||
},
|
||||
"props": {
|
||||
"text": 2,
|
||||
"text": 1,
|
||||
"textStyle": {
|
||||
"fontSize": 10,
|
||||
"textAlign": "center"
|
||||
@ -501,7 +501,7 @@
|
||||
"basis": "32px"
|
||||
},
|
||||
"props": {
|
||||
"text": 5,
|
||||
"text": 2,
|
||||
"textStyle": {
|
||||
"fontSize": 10,
|
||||
"textAlign": "center"
|
||||
@ -559,7 +559,7 @@
|
||||
"basis": "32px"
|
||||
},
|
||||
"props": {
|
||||
"text": 27,
|
||||
"text": 9,
|
||||
"textStyle": {
|
||||
"fontSize": 10,
|
||||
"textAlign": "center"
|
||||
@ -625,9 +625,9 @@
|
||||
"counts": {
|
||||
"Critical": 0,
|
||||
"Diagnostic": 0,
|
||||
"High": 20,
|
||||
"Low": 5,
|
||||
"Medium": 2
|
||||
"High": 6,
|
||||
"Low": 2,
|
||||
"Medium": 1
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 118 KiB |
@ -0,0 +1,77 @@
|
||||
{
|
||||
"custom": {},
|
||||
"params": {
|
||||
"text": "Provide for a sufficient cooling of the device."
|
||||
},
|
||||
"propConfig": {
|
||||
"params.text": {
|
||||
"paramDirection": "input",
|
||||
"persistent": true
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"defaultSize": {
|
||||
"height": 50,
|
||||
"width": 300
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"children": [
|
||||
{
|
||||
"meta": {
|
||||
"name": "Icon"
|
||||
},
|
||||
"position": {
|
||||
"basis": "20px",
|
||||
"shrink": 0
|
||||
},
|
||||
"props": {
|
||||
"path": "material/brightness_1",
|
||||
"style": {
|
||||
"color": "black",
|
||||
"marginLeft": 4,
|
||||
"marginTop": 4
|
||||
}
|
||||
},
|
||||
"type": "ia.display.icon"
|
||||
},
|
||||
{
|
||||
"meta": {
|
||||
"name": "Label"
|
||||
},
|
||||
"position": {
|
||||
"grow": 1
|
||||
},
|
||||
"propConfig": {
|
||||
"props.text": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"path": "view.params.text"
|
||||
},
|
||||
"type": "property"
|
||||
}
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"textStyle": {
|
||||
"color": "black",
|
||||
"fontSize": "18px"
|
||||
}
|
||||
},
|
||||
"type": "ia.display.label"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "root"
|
||||
},
|
||||
"props": {
|
||||
"alignContent": "center",
|
||||
"alignItems": "flex-start",
|
||||
"justify": "space-evenly",
|
||||
"style": {
|
||||
"gap": 7
|
||||
}
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
"color": "#000000",
|
||||
"deviceName": "S03_CH115_EN",
|
||||
"priority": "No Active Alarms",
|
||||
"state": "Offline"
|
||||
"state": "Closed"
|
||||
},
|
||||
"params": {
|
||||
"demoColor": -1,
|
||||
@ -3,7 +3,7 @@
|
||||
"beacon": 0,
|
||||
"flashingColor": "#808080",
|
||||
"solidColor": "#FF8C00",
|
||||
"state": "Offline"
|
||||
"state": "OFF"
|
||||
},
|
||||
"params": {
|
||||
"demoColor": "",
|
||||
@ -3,7 +3,7 @@
|
||||
"PLC": "MCM01",
|
||||
"color": "#C2C2C2",
|
||||
"showTags": true,
|
||||
"state": "Offline"
|
||||
"state": "Closed"
|
||||
},
|
||||
"params": {
|
||||
"devices": [],
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"custom": {
|
||||
"color": "#000",
|
||||
"color": "#f9050d",
|
||||
"modifiedTag": "System/MCM01/VFD/UL14_1_VFD1",
|
||||
"priority": "No Active Alarms"
|
||||
},
|
||||
@ -3,12 +3,54 @@
|
||||
"PLC": "MCM01",
|
||||
"amperage": 0,
|
||||
"device": "UL15_1_VFD1",
|
||||
"faultDescription": "",
|
||||
"faultProbableCause": [
|
||||
{
|
||||
"instancePosition": {},
|
||||
"instanceStyle": {},
|
||||
"text": "Ambient temperature too high."
|
||||
},
|
||||
{
|
||||
"instancePosition": {},
|
||||
"instanceStyle": {},
|
||||
"text": "Fan or ventilation slots are polluted."
|
||||
},
|
||||
{
|
||||
"instancePosition": {},
|
||||
"instanceStyle": {},
|
||||
"text": "Fan is defective."
|
||||
},
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value"
|
||||
],
|
||||
"faultRemedy": [
|
||||
{
|
||||
"instancePosition": {},
|
||||
"instanceStyle": {},
|
||||
"text": "Provide for a sufficient cooling of the device."
|
||||
},
|
||||
{
|
||||
"instancePosition": {},
|
||||
"instanceStyle": {},
|
||||
"text": "Clean fan and ventilation slots."
|
||||
},
|
||||
{
|
||||
"instancePosition": {},
|
||||
"instanceStyle": {},
|
||||
"text": "If required, replace fan."
|
||||
},
|
||||
"value",
|
||||
"value",
|
||||
"value"
|
||||
],
|
||||
"fpm": 0,
|
||||
"frequency": 0,
|
||||
"lastFaultCode": 0,
|
||||
"maintance_mode": false,
|
||||
"showTags": true,
|
||||
"state": "Offline",
|
||||
"state": "FAULTED/DISCONNECTED",
|
||||
"statusCode": 0,
|
||||
"voltage": 0
|
||||
},
|
||||
@ -79,6 +121,77 @@
|
||||
},
|
||||
"persistent": true
|
||||
},
|
||||
"custom.faultDescription": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"fallbackDelay": 2.5,
|
||||
"mode": "indirect",
|
||||
"references": {
|
||||
"0": "{view.params.tagProps[0]}",
|
||||
"fc": "{session.custom.fc}"
|
||||
},
|
||||
"tagPath": "[{fc}_SCADA_TAG_PROVIDER]{0}/Fault/Fault_Description"
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"expression": "coalesce({value},\"\")",
|
||||
"type": "expression"
|
||||
}
|
||||
],
|
||||
"type": "tag"
|
||||
},
|
||||
"persistent": true
|
||||
},
|
||||
"custom.faultProbableCause": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"fallbackDelay": 2.5,
|
||||
"mode": "indirect",
|
||||
"references": {
|
||||
"0": "{view.params.tagProps[0]}",
|
||||
"fc": "{session.custom.fc}"
|
||||
},
|
||||
"tagPath": "[{fc}_SCADA_TAG_PROVIDER]{0}/Fault/Fault_Probable_Cause"
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"expression": "coalesce({value},\"\")",
|
||||
"type": "expression"
|
||||
},
|
||||
{
|
||||
"code": "\t# Input: a string like \"Ambient temperature too high. || Fan or ventilation slots are polluted. || Fan is defective.\"\n\t# Output: a list of instance dicts for the repeater\n\t\n\titems \u003d []\n\t\n\ttry:\n\t text \u003d str(value).strip()\n\t if text:\n\t # Split by \"||\" and clean up\n\t parts \u003d [p.strip() for p in text.split(\"||\") if p.strip()]\n\t \n\t # Build instance dictionaries\n\t for p in parts:\n\t items.append({\n\t \"instanceStyle\": {},\n\t \"instancePosition\": {},\n\t \"text\": p\n\t })\n\texcept Exception as e:\n\t system.perspective.print(\"FaultItem repeater transform error: \" + str(e))\n\t items \u003d []\n\t\n\treturn items",
|
||||
"type": "script"
|
||||
}
|
||||
],
|
||||
"type": "tag"
|
||||
},
|
||||
"persistent": true
|
||||
},
|
||||
"custom.faultRemedy": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"fallbackDelay": 2.5,
|
||||
"mode": "indirect",
|
||||
"references": {
|
||||
"0": "{view.params.tagProps[0]}",
|
||||
"fc": "{session.custom.fc}"
|
||||
},
|
||||
"tagPath": "[{fc}_SCADA_TAG_PROVIDER]{0}/Fault/Fault_Remedy"
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"expression": "coalesce({value},\"\")",
|
||||
"type": "expression"
|
||||
},
|
||||
{
|
||||
"code": "\t# Input: a string like \"Ambient temperature too high. || Fan or ventilation slots are polluted. || Fan is defective.\"\n\t# Output: a list of instance dicts for the repeater\n\t\n\titems \u003d []\n\t\n\ttry:\n\t text \u003d str(value).strip()\n\t if text:\n\t # Split by \"||\" and clean up\n\t parts \u003d [p.strip() for p in text.split(\"||\") if p.strip()]\n\t \n\t # Build instance dictionaries\n\t for p in parts:\n\t items.append({\n\t \"instanceStyle\": {},\n\t \"instancePosition\": {},\n\t \"text\": p\n\t })\n\texcept Exception as e:\n\t system.perspective.print(\"FaultItem repeater transform error: \" + str(e))\n\t items \u003d []\n\t\n\treturn items",
|
||||
"type": "script"
|
||||
}
|
||||
],
|
||||
"type": "tag"
|
||||
},
|
||||
"persistent": true
|
||||
},
|
||||
"custom.fpm": {
|
||||
"binding": {
|
||||
"config": {
|
||||
@ -274,6 +387,7 @@
|
||||
},
|
||||
"props": {
|
||||
"defaultSize": {
|
||||
"height": 1080,
|
||||
"width": 600
|
||||
}
|
||||
},
|
||||
@ -935,7 +1049,7 @@
|
||||
"height": 161,
|
||||
"width": 98,
|
||||
"x": 97.65,
|
||||
"y": 505.33000000000004
|
||||
"y": 455.33
|
||||
},
|
||||
"propConfig": {
|
||||
"props.style.backgroundColor": {
|
||||
@ -975,7 +1089,7 @@
|
||||
"height": 92,
|
||||
"width": 314,
|
||||
"x": 205.65,
|
||||
"y": 505.30999999999995
|
||||
"y": 455.31
|
||||
},
|
||||
"propConfig": {
|
||||
"props.style.backgroundColor": {
|
||||
@ -1015,7 +1129,7 @@
|
||||
"height": 64,
|
||||
"width": 98,
|
||||
"x": 205.296,
|
||||
"y": 602.3
|
||||
"y": 552.3
|
||||
},
|
||||
"propConfig": {
|
||||
"props.enabled": {
|
||||
@ -1063,7 +1177,7 @@
|
||||
"height": 64,
|
||||
"width": 98,
|
||||
"x": 314.65,
|
||||
"y": 602.29
|
||||
"y": 552.29
|
||||
},
|
||||
"propConfig": {
|
||||
"props.enabled": {
|
||||
@ -1111,7 +1225,7 @@
|
||||
"height": 64,
|
||||
"width": 98,
|
||||
"x": 421.296,
|
||||
"y": 602.249
|
||||
"y": 552.249
|
||||
},
|
||||
"propConfig": {
|
||||
"props.enabled": {
|
||||
@ -1259,6 +1373,231 @@
|
||||
"y": 381.49
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"meta": {
|
||||
"name": "Label"
|
||||
},
|
||||
"position": {
|
||||
"basis": "32px"
|
||||
},
|
||||
"propConfig": {
|
||||
"props.text": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"expression": "\"LENZE FAULTED : \" + {view.custom.faultDescription}"
|
||||
},
|
||||
"type": "expr"
|
||||
}
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"style": {
|
||||
"color": "black",
|
||||
"paddingLeft": "20px"
|
||||
}
|
||||
},
|
||||
"type": "ia.display.label"
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"meta": {
|
||||
"name": "Label"
|
||||
},
|
||||
"position": {
|
||||
"basis": "33px",
|
||||
"shrink": 0
|
||||
},
|
||||
"props": {
|
||||
"style": {
|
||||
"background": "#d9d9d9",
|
||||
"color": "#000000"
|
||||
},
|
||||
"text": "Cause",
|
||||
"textStyle": {
|
||||
"textAlign": "center"
|
||||
}
|
||||
},
|
||||
"type": "ia.display.label"
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"meta": {
|
||||
"name": "FlexRepeater"
|
||||
},
|
||||
"position": {
|
||||
"basis": "100%"
|
||||
},
|
||||
"propConfig": {
|
||||
"props.instances": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"path": "view.custom.faultProbableCause"
|
||||
},
|
||||
"type": "property"
|
||||
}
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"direction": "column",
|
||||
"path": "autStand/Equipment/VFD-Views/FaultItem",
|
||||
"style": {
|
||||
"overflowX": "hidden"
|
||||
},
|
||||
"useDefaultViewWidth": false
|
||||
},
|
||||
"type": "ia.display.flex-repeater"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "FlexContainer"
|
||||
},
|
||||
"position": {
|
||||
"basis": "200px",
|
||||
"grow": 1
|
||||
},
|
||||
"props": {
|
||||
"direction": "column",
|
||||
"style": {
|
||||
"borderRight": "solid black 0.5px",
|
||||
"overflow": "visible"
|
||||
}
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "FlexContainer"
|
||||
},
|
||||
"position": {
|
||||
"basis": "277px"
|
||||
},
|
||||
"props": {
|
||||
"direction": "column"
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"meta": {
|
||||
"name": "Label"
|
||||
},
|
||||
"position": {
|
||||
"basis": "33px",
|
||||
"shrink": 0
|
||||
},
|
||||
"props": {
|
||||
"style": {
|
||||
"background": "#d9d9d9",
|
||||
"color": "#000000"
|
||||
},
|
||||
"text": "Remedy",
|
||||
"textStyle": {
|
||||
"textAlign": "center"
|
||||
}
|
||||
},
|
||||
"type": "ia.display.label"
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"meta": {
|
||||
"name": "FlexRepeater"
|
||||
},
|
||||
"position": {
|
||||
"basis": "100%"
|
||||
},
|
||||
"propConfig": {
|
||||
"props.instances": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"path": "view.custom.faultRemedy"
|
||||
},
|
||||
"type": "property"
|
||||
}
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"direction": "column",
|
||||
"path": "autStand/Equipment/VFD-Views/FaultItem",
|
||||
"style": {
|
||||
"overflowX": "hidden"
|
||||
},
|
||||
"useDefaultViewWidth": false
|
||||
},
|
||||
"type": "ia.display.flex-repeater"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "FlexContainer"
|
||||
},
|
||||
"position": {
|
||||
"basis": "200px",
|
||||
"grow": 1
|
||||
},
|
||||
"props": {
|
||||
"direction": "column",
|
||||
"style": {
|
||||
"overflow": "visible"
|
||||
}
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "FlexContainer_0"
|
||||
},
|
||||
"position": {
|
||||
"basis": "277px"
|
||||
},
|
||||
"props": {
|
||||
"direction": "column",
|
||||
"style": {
|
||||
"borderLeft": "solid black 0.5px"
|
||||
}
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "FlexContainer_8"
|
||||
},
|
||||
"position": {
|
||||
"grow": 1
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "FlexContainer_2"
|
||||
},
|
||||
"position": {
|
||||
"height": 300,
|
||||
"width": 508,
|
||||
"x": 42.33000093823242,
|
||||
"y": 642.33
|
||||
},
|
||||
"propConfig": {
|
||||
"meta.visible": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"expression": "{view.custom.faultDescription} !\u003d \"\""
|
||||
},
|
||||
"type": "expr"
|
||||
}
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"direction": "column"
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
@ -1,8 +1,8 @@
|
||||
{
|
||||
"custom": {
|
||||
"color": "#000000",
|
||||
"color": "#AAAAAA",
|
||||
"deviceName": "S03_CH101_PRX1",
|
||||
"state": "Offline"
|
||||
"state": "INACTIVE"
|
||||
},
|
||||
"params": {
|
||||
"demoColor": -1,
|
||||
@ -571,7 +571,7 @@
|
||||
"dom": {
|
||||
"onClick": {
|
||||
"config": {
|
||||
"script": "\t#create devices and tags lists for the conveyor\n\tprops \u003d self.view.params.tagProps[0]\n\tautStand.devices.build_device_mapping(props)\n\tdevice_table_dataset \u003d autStand.devices.build_device_table(self)\n\ttags_table_dataset \u003d autStand.devices.getAllTags(self, props)\n\tsystem.perspective.openDock(\u0027Docked-East-Conv\u0027,params\u003d{\u0027tagProps\u0027:self.view.params.tagProps, \"devices\": device_table_dataset, \"tags\":tags_table_dataset})"
|
||||
"script": "\t#create devices and tags lists for the conveyor\n\tprops \u003d self.view.params.tagProps[0]\n\tautStand.devices.build_device_mapping(props)\n\tdevice_table_dataset \u003d autStand.devices.build_device_table(self)\n\ttags_table_dataset \u003d autStand.devices.getAllTags(self, props, section \u003d \"conveyor\")\n\tsystem.perspective.openDock(\u0027Docked-East-Conv\u0027,params\u003d{\u0027tagProps\u0027:self.view.params.tagProps, \"devices\": device_table_dataset, \"tags\":tags_table_dataset})"
|
||||
},
|
||||
"scope": "G",
|
||||
"type": "script"
|
||||
|
Before Width: | Height: | Size: 108 KiB |
@ -4,7 +4,7 @@
|
||||
"$": [
|
||||
"ds",
|
||||
192,
|
||||
1762166893089
|
||||
1762422327752
|
||||
],
|
||||
"$columns": [
|
||||
{
|
||||
@ -31,11 +31,11 @@
|
||||
},
|
||||
{
|
||||
"data": [
|
||||
20,
|
||||
5,
|
||||
6,
|
||||
2,
|
||||
36,
|
||||
3
|
||||
1,
|
||||
32,
|
||||
4
|
||||
],
|
||||
"name": "Count",
|
||||
"type": "Long"
|
||||
|
After Width: | Height: | Size: 42 KiB |
@ -3,16 +3,16 @@
|
||||
"counts": {
|
||||
"Critical": 0,
|
||||
"Diagnostic": 0,
|
||||
"High": 20,
|
||||
"Low": 5,
|
||||
"Medium": 2,
|
||||
"Total": 27
|
||||
"High": 6,
|
||||
"Low": 2,
|
||||
"Medium": 1,
|
||||
"Total": 9
|
||||
},
|
||||
"totalAlarms": {
|
||||
"$": [
|
||||
"ds",
|
||||
192,
|
||||
1762166890980
|
||||
1762422326706
|
||||
],
|
||||
"$columns": [
|
||||
{
|
||||
@ -39,11 +39,11 @@
|
||||
},
|
||||
{
|
||||
"data": [
|
||||
20,
|
||||
5,
|
||||
6,
|
||||
2,
|
||||
36,
|
||||
3
|
||||
1,
|
||||
32,
|
||||
4
|
||||
],
|
||||
"name": "Count",
|
||||
"type": "Long"
|
||||
|
Before Width: | Height: | Size: 20 KiB |
@ -4,7 +4,7 @@
|
||||
"isHighlited": false,
|
||||
"overlayColor": "#ffffff",
|
||||
"priority": "No Active Alarms",
|
||||
"state": "Offline"
|
||||
"state": "BLOCKED"
|
||||
},
|
||||
"params": {
|
||||
"demoColor": -1,
|
||||
|
Before Width: | Height: | Size: 35 KiB |
@ -0,0 +1,111 @@
|
||||
{
|
||||
"custom": {},
|
||||
"params": {},
|
||||
"props": {
|
||||
"defaultSize": {
|
||||
"height": 920
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"children": [
|
||||
{
|
||||
"meta": {
|
||||
"name": "PDFViewer"
|
||||
},
|
||||
"position": {
|
||||
"height": 1,
|
||||
"width": 1
|
||||
},
|
||||
"propConfig": {
|
||||
"props.page": {
|
||||
"onChange": {
|
||||
"enabled": null,
|
||||
"script": "\tmaxCount \u003d self.props.pageCount\n\tcurrentPage \u003d currentValue.value\n\tif currentPage \u003c 1:\n\t\tself.props.page \u003d maxCount\n\t\n\tif currentPage \u003e maxCount:\n\t\tself.props.page \u003d 1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"page": 1,
|
||||
"pageCount": 32,
|
||||
"source": "\\Description of Operations - Amazon BNA8.pdf",
|
||||
"style": {
|
||||
"overflow": "hidden"
|
||||
}
|
||||
},
|
||||
"type": "ia.display.pdf-viewer"
|
||||
},
|
||||
{
|
||||
"events": {
|
||||
"dom": {
|
||||
"onClick": {
|
||||
"config": {
|
||||
"script": "\tself.getSibling(\"PDFViewer\").props.page -\u003d1"
|
||||
},
|
||||
"scope": "G",
|
||||
"type": "script"
|
||||
}
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"name": "Icon_0",
|
||||
"tooltip": {
|
||||
"enabled": true,
|
||||
"text": "Previous Page"
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"height": 0.043,
|
||||
"width": 0.0587,
|
||||
"x": 0.4217,
|
||||
"y": 0.9451
|
||||
},
|
||||
"props": {
|
||||
"path": "material/arrow_back",
|
||||
"style": {
|
||||
"cursor": "pointer"
|
||||
}
|
||||
},
|
||||
"type": "ia.display.icon"
|
||||
},
|
||||
{
|
||||
"events": {
|
||||
"dom": {
|
||||
"onClick": {
|
||||
"config": {
|
||||
"script": "\tself.getSibling(\"PDFViewer\").props.page +\u003d1"
|
||||
},
|
||||
"scope": "G",
|
||||
"type": "script"
|
||||
}
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"name": "Icon_1",
|
||||
"tooltip": {
|
||||
"text": "Next Page"
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"height": 0.043,
|
||||
"width": 0.0587,
|
||||
"x": 0.5439,
|
||||
"y": 0.9451
|
||||
},
|
||||
"props": {
|
||||
"path": "material/arrow_forward",
|
||||
"style": {
|
||||
"cursor": "painter"
|
||||
}
|
||||
},
|
||||
"type": "ia.display.icon"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "root"
|
||||
},
|
||||
"props": {
|
||||
"mode": "percent"
|
||||
},
|
||||
"type": "ia.container.coord"
|
||||
}
|
||||
}
|
||||
@ -3562,7 +3562,7 @@
|
||||
"height": 0.0231,
|
||||
"width": 0.013,
|
||||
"x": 0.0721,
|
||||
"y": 0.4037
|
||||
"y": 0.3893
|
||||
},
|
||||
"props": {
|
||||
"params": {
|
||||
@ -10069,7 +10069,7 @@
|
||||
},
|
||||
{
|
||||
"meta": {
|
||||
"name": "PS3_12_TPE1"
|
||||
"name": "PS3_12_TPE2"
|
||||
},
|
||||
"position": {
|
||||
"height": 0.0231,
|
||||
@ -10083,7 +10083,7 @@
|
||||
"props": {
|
||||
"params": {
|
||||
"tagProps": [
|
||||
"System/MCM02/PE/TPE/PS3_12_TPE1",
|
||||
"System/MCM02/PE/TPE/PS3_12_TPE2",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
@ -10321,6 +10321,78 @@
|
||||
}
|
||||
},
|
||||
"type": "ia.display.view"
|
||||
},
|
||||
{
|
||||
"meta": {
|
||||
"name": "PS3_13_ENW2"
|
||||
},
|
||||
"position": {
|
||||
"height": 0.0231,
|
||||
"width": 0.0131,
|
||||
"x": 0.0488,
|
||||
"y": 0.4583
|
||||
},
|
||||
"props": {
|
||||
"params": {
|
||||
"tagProps": [
|
||||
"System/MCM02/ENCODER/ENW/PS3_12_ENW1",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
"path": "autStand/Equipment/Encoder",
|
||||
"style": {
|
||||
"borderRadius": "50%",
|
||||
"classes": "hover",
|
||||
"overflow": "visible"
|
||||
}
|
||||
},
|
||||
"type": "ia.display.view"
|
||||
},
|
||||
{
|
||||
"meta": {
|
||||
"name": "PS3_12_TPE1"
|
||||
},
|
||||
"position": {
|
||||
"height": 0.0231,
|
||||
"rotate": {
|
||||
"angle": "90deg"
|
||||
},
|
||||
"width": 0.0531,
|
||||
"x": 0.0543,
|
||||
"y": 0.4473
|
||||
},
|
||||
"props": {
|
||||
"params": {
|
||||
"tagProps": [
|
||||
"System/MCM02/PE/TPE/PS3_12_TPE1",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
"path": "autStand/Equipment/Photoeye_Chute",
|
||||
"style": {
|
||||
"classes": "hover-90",
|
||||
"overflow": "visible"
|
||||
},
|
||||
"useDefaultViewHeight": true,
|
||||
"useDefaultViewWidth": true
|
||||
},
|
||||
"type": "ia.display.view"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
@ -4,7 +4,7 @@
|
||||
"divertingLeft": false,
|
||||
"divertingRight": false,
|
||||
"priority": "No Active Alarms",
|
||||
"state": "Offline"
|
||||
"state": "Closed"
|
||||
},
|
||||
"params": {
|
||||
"demoColor": 0,
|
||||
@ -574,7 +574,7 @@
|
||||
"dom": {
|
||||
"onClick": {
|
||||
"config": {
|
||||
"script": "\t#create devices and tags lists for the conveyor\n\tprops \u003d self.view.params.tagProps[0]\n\tautStand.devices.build_device_mapping(props)\n\tdevice_table_dataset \u003d autStand.devices.build_device_table(self)\n\ttags_table_dataset \u003d autStand.devices.getAllTags(self, props)\n\tsystem.perspective.openDock(\u0027Docked-East-Conv\u0027,params\u003d{\u0027tagProps\u0027:self.view.params.tagProps, \"devices\": device_table_dataset, \"tags\":tags_table_dataset})"
|
||||
"script": "\t#create devices and tags lists for the conveyor\n\tprops \u003d self.view.params.tagProps[0]\n\tautStand.devices.build_device_mapping(props)\n\tdevice_table_dataset \u003d autStand.devices.build_device_table(self)\n\ttags_table_dataset \u003d autStand.devices.getAllTags(self, props, section \u003d \"conveyor\")\n\tsystem.perspective.openDock(\u0027Docked-East-Conv\u0027,params\u003d{\u0027tagProps\u0027:self.view.params.tagProps, \"devices\": device_table_dataset, \"tags\":tags_table_dataset})"
|
||||
},
|
||||
"scope": "G",
|
||||
"type": "script"
|
||||
@ -0,0 +1,542 @@
|
||||
import os, json, sys
|
||||
|
||||
# ======================================================
|
||||
# Helper Function: State Resolver
|
||||
# ======================================================
|
||||
def get_device_state(value, tagPath):
|
||||
up = tagPath.upper()
|
||||
|
||||
# === Base state dictionary ===
|
||||
state_mappings = {
|
||||
0: "Closed",
|
||||
1: "Actuated",
|
||||
2: "Communication Faulted",
|
||||
3: "Conveyor Running In Maintenance Mode",
|
||||
4: "Disabled",
|
||||
5: "Disconnected",
|
||||
6: "Stopped",
|
||||
7: "Enabled Not Running",
|
||||
8: "Encoder Fault",
|
||||
9: "Energy Management",
|
||||
10: "ESTOP Was Actuated",
|
||||
11: "EStopped",
|
||||
12: "EStopped Locally",
|
||||
13: "Extended Faulted",
|
||||
14: "Full",
|
||||
15: "Gaylord Start Pressed",
|
||||
16: "Jam Fault",
|
||||
17: "Jammed",
|
||||
18: "Loading Allowed",
|
||||
19: "Loading Not Allowed",
|
||||
20: "Low Air Pressure Fault Was Present",
|
||||
21: "Maintenance Mode",
|
||||
22: "Conveyor Stopped In Maintenance Mode",
|
||||
23: "Motor Faulted",
|
||||
24: "Motor Was Faulted",
|
||||
25: "Normal",
|
||||
26: "Off Inactive",
|
||||
27: "Open",
|
||||
28: "PLC Ready To Run",
|
||||
29: "Package Release Pressed",
|
||||
30: "Power Branch Was Faulted",
|
||||
31: "Pressed",
|
||||
32: "Ready To Receive",
|
||||
33: "Running",
|
||||
34: "Started",
|
||||
35: "Stopped",
|
||||
36: "System Started",
|
||||
37: "Unknown",
|
||||
38: "VFD Fault",
|
||||
39: "Conveyor Running In Power Saving Mode",
|
||||
40: "Conveyor Jogging In Maintenance Mode",
|
||||
41: "VFD Reset Required",
|
||||
42: "Jam Reset Push Button Pressed",
|
||||
43: "Start Push Button Pressed",
|
||||
44: "Stop Push Button Pressed",
|
||||
45: "No Container",
|
||||
46: "Ready To Be Enabled",
|
||||
47: "Half Full",
|
||||
48: "Enabled",
|
||||
49: "Tipper Faulted",
|
||||
50: "OK",
|
||||
51: "Disconnected",
|
||||
52: "Faulted",
|
||||
53: "Faulted/Disconnect",
|
||||
54: "Diverting"
|
||||
}
|
||||
|
||||
# === TPE (Tracking Photoeye) ===
|
||||
if "/TPE/" in up:
|
||||
if value == 0:
|
||||
return "Blocked"
|
||||
elif value == 27:
|
||||
return "Clear"
|
||||
elif value == 17:
|
||||
return "Jammed"
|
||||
else:
|
||||
return state_mappings.get(value, "Unknown")
|
||||
|
||||
# === Single Photoeyes (PE1, PE2) ===
|
||||
if up.endswith(("PE1", "PE2")):
|
||||
if not value:
|
||||
return "Clear"
|
||||
else:
|
||||
return "Blocked"
|
||||
|
||||
# === Prox Sensors (PRX1, PRX2) ===
|
||||
if up.endswith(("PRX1", "PRX2")):
|
||||
if not value:
|
||||
return "Inactive"
|
||||
else:
|
||||
return "Actuated"
|
||||
|
||||
# === Beacons (BCN) ===
|
||||
if "/BEACON" in up:
|
||||
if value == 0:
|
||||
return "Off"
|
||||
elif value == 1:
|
||||
return "Cleared / Reset Required"
|
||||
else:
|
||||
return "Active"
|
||||
|
||||
# === Default ===
|
||||
return state_mappings.get(value, "Unknown")
|
||||
|
||||
|
||||
# ======================================================
|
||||
# Helper Function: Read One Device (multi or single state)
|
||||
# ======================================================
|
||||
def read_device_status(tagPath, provider, dev_name):
|
||||
"""
|
||||
Reads the appropriate state tag(s) for a given device and returns
|
||||
a list of {Device, Status} dictionaries.
|
||||
Handles multi-state (SS), VFD, PRX, PE, EN, etc.
|
||||
"""
|
||||
rows = []
|
||||
try:
|
||||
up = tagPath.upper()
|
||||
|
||||
# === Case 0: SS (Start/Stop Station) ===
|
||||
if up.endswith("SS") or "/SS/" in up:
|
||||
for sub in ("Start", "Stop"):
|
||||
sub_path = provider + tagPath + "/" + sub + "/State"
|
||||
try:
|
||||
result = system.tag.readBlocking([sub_path])[0]
|
||||
if result.quality.isGood():
|
||||
status_value = get_device_state(result.value, tagPath)
|
||||
else:
|
||||
status_value = "Unknown"
|
||||
except:
|
||||
status_value = "Unknown"
|
||||
|
||||
rows.append({
|
||||
"Device": "{} ({})".format(dev_name, sub),
|
||||
"Status": status_value
|
||||
})
|
||||
return rows # handled fully
|
||||
|
||||
# === Case 1: VFD / Conveyor ===
|
||||
if "/VFD/" in up:
|
||||
path = provider + tagPath + "/Drive/Lenze"
|
||||
|
||||
# === Case 2: Chute sensors (PE / PRX) ===
|
||||
elif up.endswith(("PE1", "PE2", "PRX1", "PRX2")) and "/TPE/" not in up:
|
||||
path = provider + tagPath
|
||||
|
||||
# === Case 3: Chute EN ===
|
||||
elif up.endswith("EN") and "/CHUTE/" in up:
|
||||
path = provider + tagPath + "_State"
|
||||
|
||||
# === Case 4: Default ===
|
||||
else:
|
||||
path = provider + tagPath + "/State"
|
||||
|
||||
try:
|
||||
result = system.tag.readBlocking([path])[0]
|
||||
if result.quality.isGood():
|
||||
status_value = get_device_state(result.value, tagPath)
|
||||
else:
|
||||
status_value = "Offline"
|
||||
except:
|
||||
status_value = "Offline"
|
||||
|
||||
rows.append({
|
||||
"Device": dev_name,
|
||||
"Status": status_value
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
system.perspective.print("Error reading device status for %s: %s" % (dev_name, e))
|
||||
|
||||
return rows
|
||||
|
||||
|
||||
# ======================================================
|
||||
# Helper Function: Single Device Reader (for Docked Device View)
|
||||
# ======================================================
|
||||
def get_single_device_status(self, tagPath):
|
||||
"""
|
||||
Reads a single device tag (used for docked device views).
|
||||
Returns a single readable status string (e.g. "Running", "Blocked", etc.)
|
||||
"""
|
||||
try:
|
||||
up = tagPath.upper()
|
||||
provider = "[" + self.session.custom.fc + "_SCADA_TAG_PROVIDER]"
|
||||
|
||||
if up.endswith("SS") or "/SS/" in up:
|
||||
states = []
|
||||
for sub in ("Start", "Stop"):
|
||||
sub_path = provider + tagPath + "/" + sub + "/State"
|
||||
try:
|
||||
result = system.tag.readBlocking([sub_path])[0]
|
||||
if result.quality.isGood():
|
||||
states.append("{}: {}".format(sub, get_device_state(result.value, tagPath)))
|
||||
else:
|
||||
states.append("{}: Unknown".format(sub))
|
||||
except:
|
||||
states.append("{}: Unknown".format(sub))
|
||||
return " | ".join(states)
|
||||
|
||||
# === VFD ===
|
||||
if "/VFD/" in up:
|
||||
path = provider + tagPath + "/Drive/Lenze"
|
||||
# === Sensors ===
|
||||
elif up.endswith(("PE1", "PE2", "PRX1", "PRX2")) and "/TPE/" not in up:
|
||||
path = provider + tagPath
|
||||
# === EN ===
|
||||
elif up.endswith("EN") and "/CHUTE/" in up:
|
||||
path = provider + tagPath + "_State"
|
||||
else:
|
||||
path = provider + tagPath + "/State"
|
||||
|
||||
result = system.tag.readBlocking([path])[0]
|
||||
if result.quality.isGood():
|
||||
return get_device_state(result.value, tagPath)
|
||||
else:
|
||||
return "Unknown"
|
||||
|
||||
except Exception as e:
|
||||
system.perspective.print("Error reading single device status for %s: %s" % (tagPath, e))
|
||||
return "Unknown"
|
||||
|
||||
|
||||
# ======================================================
|
||||
# Device Mapping Builder
|
||||
# ======================================================
|
||||
global_device_mapping = {}
|
||||
|
||||
def build_device_mapping(full_tag_path):
|
||||
"""
|
||||
Builds global_device_mapping for devices under the same PLC and parent device.
|
||||
Adds support for:
|
||||
- Chute FIOM devices (e.g. S03_CH109_FIOM_1 when clicking S03_CH109)
|
||||
- Shared JR and PE devices used by multiple chutes (e.g. S03_1_JR1, S03_1_LRPE1)
|
||||
"""
|
||||
system.perspective.print(full_tag_path)
|
||||
global global_device_mapping
|
||||
global_device_mapping.clear()
|
||||
|
||||
try:
|
||||
path_parts = full_tag_path.split("/")
|
||||
plc_name = path_parts[1] if len(path_parts) > 1 else path_parts[0]
|
||||
clicked_name = path_parts[-1] if len(path_parts) > 0 else ""
|
||||
|
||||
# --- Clean clicked name ---
|
||||
if "_VFD" in clicked_name:
|
||||
clicked_name = clicked_name.split("_VFD")[0]
|
||||
|
||||
project_name = system.util.getProjectName()
|
||||
base_path = (
|
||||
os.getcwd().replace("\\", "/")
|
||||
+ "/data/projects/"
|
||||
+ project_name
|
||||
+ "/com.inductiveautomation.perspective/Views/autStand/Detailed_Views/MCM-Views"
|
||||
)
|
||||
|
||||
if not os.path.exists(base_path):
|
||||
system.perspective.print("Path not found: " + base_path)
|
||||
return {}
|
||||
|
||||
# --- Detect if this is a Chute ---
|
||||
is_chute = "/CHUTE/" in full_tag_path.upper()
|
||||
|
||||
for view_folder in os.listdir(base_path):
|
||||
json_file = os.path.join(base_path, view_folder, "view.json")
|
||||
if not os.path.isfile(json_file):
|
||||
continue
|
||||
|
||||
try:
|
||||
with open(json_file, "r") as fh:
|
||||
view_json = json.load(fh)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
root_children = (view_json.get("root") or {}).get("children") or []
|
||||
if not root_children:
|
||||
continue
|
||||
|
||||
container = root_children[0]
|
||||
children = container.get("children") or []
|
||||
|
||||
for child in children:
|
||||
props = child.get("props") or {}
|
||||
params = props.get("params") or {}
|
||||
tag_props = params.get("tagProps")
|
||||
|
||||
if isinstance(tag_props, list) and len(tag_props) > 0:
|
||||
tag_prop = str(tag_props[0])
|
||||
parts = tag_prop.split("/")
|
||||
|
||||
if len(parts) > 1 and parts[1] == plc_name:
|
||||
dev_name = parts[-1]
|
||||
if len(parts) > 3 and parts[-2] == clicked_name:
|
||||
dev_name = clicked_name + "_" + parts[-1]
|
||||
else:
|
||||
dev_name = parts[-1]
|
||||
|
||||
prefix = clicked_name + "_"
|
||||
|
||||
# === 🟢 NEW: Chute FIOM match ===
|
||||
if is_chute and dev_name.startswith(clicked_name + "_"):
|
||||
global_device_mapping[dev_name] = {
|
||||
"tagPath": tag_prop,
|
||||
"zone": view_folder
|
||||
}
|
||||
continue
|
||||
|
||||
# === Default inclusion ===
|
||||
if dev_name.startswith(prefix) or (len(parts) > 3 and parts[-2] == clicked_name):
|
||||
global_device_mapping[dev_name] = {
|
||||
"tagPath": tag_prop,
|
||||
"zone": view_folder
|
||||
}
|
||||
|
||||
# === Special Case: JR Buttons ===
|
||||
elif "/JR/" in tag_prop.upper():
|
||||
try:
|
||||
jr_parts = tag_prop.split("/JR/")
|
||||
if len(jr_parts) > 1:
|
||||
sub_path = jr_parts[1]
|
||||
if sub_path.startswith(clicked_name + "_JR"):
|
||||
dev_name = sub_path.split("/")[0]
|
||||
global_device_mapping[dev_name] = {
|
||||
"tagPath": tag_prop,
|
||||
"zone": view_folder
|
||||
}
|
||||
except:
|
||||
pass
|
||||
|
||||
shared_jr_pe_map = {
|
||||
"S03_CH101": ["S03_1_JR1", "S03_1_LRPE1"],
|
||||
"S03_CH103": ["S03_1_JR1", "S03_1_LRPE1"],
|
||||
"S03_CH105": ["S03_1_JR1", "S03_1_LRPE1"],
|
||||
"S03_CH107": ["S03_1_JR3", "S03_1_LRPE3"],
|
||||
"S03_CH108": ["S03_1_JR4", "S03_1_LRPE4"],
|
||||
"S03_CH109": ["S03_1_JR3", "S03_1_LRPE3"],
|
||||
"S03_CH110": ["S03_1_JR4", "S03_1_LRPE4"],
|
||||
"S03_CH111": ["S03_1_JR3", "S03_1_LRPE3"],
|
||||
"S03_CH112": ["S03_1_JR2", "S03_1_LRPE2"],
|
||||
"S03_CH113": ["S03_1_JR5", "S03_1_LRPE5"],
|
||||
"S03_CH114": ["S03_1_JR6", "S03_1_LRPE6"],
|
||||
"S03_CH115": ["S03_1_JR5", "S03_1_LRPE5"],
|
||||
"S03_CH116": ["S03_1_JR6", "S03_1_LRPE6"],
|
||||
"S03_CH117": ["S03_1_JR5", "S03_1_LRPE5"],
|
||||
"S03_CH118": ["S03_1_JR6", "S03_1_LRPE6"],
|
||||
"S03_CH119": ["S03_1_JR7", "S03_1_LRPE7"],
|
||||
"S03_CH120": ["S03_1_JR8", "S03_1_LRPE8"],
|
||||
"S03_CH121": ["S03_1_JR7", "S03_1_LRPE7"],
|
||||
"S03_CH122": ["S03_1_JR8", "S03_1_LRPE8"],
|
||||
"S03_CH123": ["S03_1_JR7", "S03_1_LRPE7"],
|
||||
"S03_CH124": ["S03_1_JR8", "S03_1_LRPE8"],
|
||||
}
|
||||
shared_fiom_map = {
|
||||
"NCS1_1": ["S03_1_FIOM_5", "S03_1_FIOM_9", "S03_1_FIOM_1", "S03_1_FIOM_2","S03_1_FIOM_3","S03_1_FIOM_4", "S03_1_FIOM_6","S03_1_FIOM_7", "S03_1_FIOM_8"],
|
||||
}
|
||||
|
||||
if clicked_name in shared_jr_pe_map:
|
||||
extra_devices = shared_jr_pe_map[clicked_name]
|
||||
for dev in extra_devices:
|
||||
try:
|
||||
# Base tag (for PE)
|
||||
base_tag = "System/MCM02/Station/Chute_JR/" + dev
|
||||
# JR subtag (for JR button)
|
||||
jr_tag = base_tag + "/JR" if dev.endswith("JR1") else base_tag
|
||||
|
||||
for tag_candidate in [base_tag, jr_tag]:
|
||||
global_device_mapping[dev] = {
|
||||
"tagPath": tag_candidate,
|
||||
"zone": "Chute_JR"
|
||||
}
|
||||
except Exception as ex:
|
||||
system.perspective.print("Error adding JR/PE for {}: {}".format(clicked_name, ex))
|
||||
|
||||
if clicked_name in shared_fiom_map:
|
||||
for dev in shared_fiom_map[clicked_name]:
|
||||
tag_path = "System/{}/IO_Block/FIO/{}".format(plc_name, dev)
|
||||
global_device_mapping[dev] = {
|
||||
"tagPath": tag_path,
|
||||
"zone": "FIO"
|
||||
}
|
||||
|
||||
return global_device_mapping
|
||||
|
||||
except Exception as e:
|
||||
whid = "unknown"
|
||||
try:
|
||||
whid = system.tag.readBlocking("Configuration/FC")[0].value
|
||||
except:
|
||||
pass
|
||||
logger = system.util.getLogger("%s-build_device_mapping" % whid)
|
||||
exc_type, exc_obj, tb = sys.exc_info()
|
||||
logger.error("Error at line %s: %s" % (tb.tb_lineno, exc_obj))
|
||||
return {}
|
||||
|
||||
|
||||
|
||||
|
||||
# ======================================================
|
||||
# Device Table Builder
|
||||
# ======================================================
|
||||
def build_device_table(self):
|
||||
rows = []
|
||||
try:
|
||||
for dev_name, info in global_device_mapping.items():
|
||||
tagPath = info.get("tagPath", "")
|
||||
if not tagPath:
|
||||
continue
|
||||
|
||||
provider = "[" + self.session.custom.fc + "_SCADA_TAG_PROVIDER]"
|
||||
rows.extend(read_device_status(tagPath, provider, dev_name))
|
||||
|
||||
return rows
|
||||
|
||||
except Exception as e:
|
||||
system.perspective.print("Error building device table: %s" % e)
|
||||
return []
|
||||
|
||||
# ======================================================
|
||||
# Get All Tags for Clicked Device
|
||||
# ======================================================
|
||||
def getAllTags(self, tagPath, section="all"):
|
||||
"""
|
||||
Reads all tags under a UDT instance (recursively)
|
||||
and returns a list of dictionaries:
|
||||
[
|
||||
{"Name": "State", "OPC Path": "System/MCM01/...", "Value": "Running"},
|
||||
...
|
||||
]
|
||||
"""
|
||||
rows = []
|
||||
|
||||
try:
|
||||
providerPath = "[" + self.session.custom.fc + "_SCADA_TAG_PROVIDER]"
|
||||
driveFolderName = "Drive"
|
||||
|
||||
# === Utility: read a single atomic tag ===
|
||||
def readSingleTag(path, prefix=""):
|
||||
try:
|
||||
result = system.tag.readBlocking([providerPath + path])[0]
|
||||
value = str(result.value) if result.quality.isGood() else "Unknown"
|
||||
except:
|
||||
value = "Unknown"
|
||||
|
||||
displayName = prefix + path.split("/")[-1] if prefix else path.split("/")[-1]
|
||||
rows.append({
|
||||
"Name": displayName,
|
||||
"OPC Path": path,
|
||||
"Value": value
|
||||
})
|
||||
|
||||
# === Utility: recursive browse ===
|
||||
def browseRecursive(basePath, prefix=""):
|
||||
children = system.tag.browse(providerPath + basePath).getResults()
|
||||
for child in children:
|
||||
tagType = str(child.get("tagType", ""))
|
||||
name = str(child.get("name", ""))
|
||||
fullPath = str(child.get("fullPath", ""))
|
||||
|
||||
if fullPath.startswith("[") and "]" in fullPath:
|
||||
fullPath = fullPath.split("]", 1)[1]
|
||||
|
||||
# --- Conveyor filter (skip Drive folder) ---
|
||||
if section == "conveyor" and name == driveFolderName:
|
||||
continue
|
||||
|
||||
if tagType == "Folder":
|
||||
# --- Skip JR subfolder if current device is LRPE ---
|
||||
if name.upper() == "JR" and "_JR" in basePath.upper():
|
||||
continue
|
||||
newPrefix = prefix + name + "/" if prefix else name + "/"
|
||||
browseRecursive(basePath + "/" + name, newPrefix)
|
||||
elif tagType == "AtomicTag":
|
||||
readSingleTag(fullPath, prefix)
|
||||
|
||||
# === MAIN ENTRY POINT ===
|
||||
|
||||
# --- Case 1: VFD ---
|
||||
if section == "vfd":
|
||||
drivePath = tagPath + "/" + driveFolderName
|
||||
browseRecursive(drivePath)
|
||||
|
||||
# --- Case 2: Flat EN_ tags (Chutes) ---
|
||||
elif tagPath.upper().endswith("/EN"):
|
||||
parentPath = "/".join(tagPath.split("/")[:-1])
|
||||
children = system.tag.browse(providerPath + parentPath).getResults()
|
||||
for child in children:
|
||||
tagType = str(child.get("tagType", ""))
|
||||
name = str(child.get("name", ""))
|
||||
if tagType == "AtomicTag" and name.upper().startswith("EN_"):
|
||||
fullPath = str(child.get("fullPath", ""))
|
||||
if fullPath.startswith("[") and "]" in fullPath:
|
||||
fullPath = fullPath.split("]", 1)[1]
|
||||
readSingleTag(fullPath)
|
||||
|
||||
# --- Case 3: Single Sensors (PE/PRX) ---
|
||||
elif tagPath.upper().endswith(("PE1", "PE2", "PRX1", "PRX2")) and "/TPE/" not in tagPath.upper():
|
||||
readSingleTag(tagPath)
|
||||
|
||||
# --- Case 4: Default / Fallback ---
|
||||
else:
|
||||
browseResult = system.tag.browse(providerPath + tagPath).getResults()
|
||||
|
||||
if not browseResult:
|
||||
# Possibly a struct-style UDT (like some TPEs)
|
||||
system.perspective.print("Empty browse for {}, checking struct value...".format(tagPath))
|
||||
try:
|
||||
result = system.tag.readBlocking([providerPath + tagPath])[0]
|
||||
value = result.value
|
||||
|
||||
# === Expand STRUCT ===
|
||||
if isinstance(value, dict):
|
||||
system.perspective.print("Detected STRUCT value, expanding {}".format(tagPath))
|
||||
|
||||
def flattenStruct(struct, base=""):
|
||||
for k, v in struct.items():
|
||||
newName = base + "/" + k if base else k
|
||||
if isinstance(v, dict):
|
||||
flattenStruct(v, newName)
|
||||
else:
|
||||
rows.append({
|
||||
"Name": newName,
|
||||
"OPC Path": tagPath + "/" + newName,
|
||||
"Value": str(v)
|
||||
})
|
||||
|
||||
flattenStruct(value)
|
||||
|
||||
else:
|
||||
# Not a struct, read normally
|
||||
readSingleTag(tagPath)
|
||||
|
||||
except Exception as ex:
|
||||
system.perspective.print("Fallback read failed for {}: {}".format(tagPath, ex))
|
||||
|
||||
else:
|
||||
# Normal browse case
|
||||
browseRecursive(tagPath)
|
||||
|
||||
return rows
|
||||
|
||||
except Exception as e:
|
||||
system.perspective.print("Error in getAllTags: {}".format(e))
|
||||
return []
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-04T15:20:44Z"
|
||||
"timestamp": "2025-11-05T14:27:38Z"
|
||||
},
|
||||
"lastModificationSignature": "7aefd1d334d282d738eaf97f9245f751ec78e796123dc60f1c7cf6f0df39e039"
|
||||
"lastModificationSignature": "de4d878ca08fcb3581ac8deb45ba0df2b6cfa9e6e54a588bef5c7089f40a3524"
|
||||
}
|
||||
}
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T10:10:15Z"
|
||||
"timestamp": "2025-11-06T09:07:51Z"
|
||||
},
|
||||
"lastModificationSignature": "93ca78bdef1d5678a8d5ec0bdb7adb2d459d940aa32e842ff0e0bedb902aad35"
|
||||
"lastModificationSignature": "69538fe7e0bc707101c3e0349c42569d245b26f2d01e548dc6d6409daded6bad"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 118 KiB |
@ -3562,7 +3562,7 @@
|
||||
"height": 0.0231,
|
||||
"width": 0.013,
|
||||
"x": 0.0721,
|
||||
"y": 0.4037
|
||||
"y": 0.3893
|
||||
},
|
||||
"props": {
|
||||
"params": {
|
||||
@ -10069,7 +10069,7 @@
|
||||
},
|
||||
{
|
||||
"meta": {
|
||||
"name": "PS3_12_TPE1"
|
||||
"name": "PS3_12_TPE2"
|
||||
},
|
||||
"position": {
|
||||
"height": 0.0231,
|
||||
@ -10083,7 +10083,7 @@
|
||||
"props": {
|
||||
"params": {
|
||||
"tagProps": [
|
||||
"System/MCM02/PE/TPE/PS3_12_TPE1",
|
||||
"System/MCM02/PE/TPE/PS3_12_TPE2",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
@ -10321,6 +10321,78 @@
|
||||
}
|
||||
},
|
||||
"type": "ia.display.view"
|
||||
},
|
||||
{
|
||||
"meta": {
|
||||
"name": "PS3_13_ENW2"
|
||||
},
|
||||
"position": {
|
||||
"height": 0.0231,
|
||||
"width": 0.0131,
|
||||
"x": 0.0488,
|
||||
"y": 0.4583
|
||||
},
|
||||
"props": {
|
||||
"params": {
|
||||
"tagProps": [
|
||||
"System/MCM02/ENCODER/ENW/PS3_12_ENW1",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
"path": "autStand/Equipment/Encoder",
|
||||
"style": {
|
||||
"borderRadius": "50%",
|
||||
"classes": "hover",
|
||||
"overflow": "visible"
|
||||
}
|
||||
},
|
||||
"type": "ia.display.view"
|
||||
},
|
||||
{
|
||||
"meta": {
|
||||
"name": "PS3_12_TPE1"
|
||||
},
|
||||
"position": {
|
||||
"height": 0.0231,
|
||||
"rotate": {
|
||||
"angle": "90deg"
|
||||
},
|
||||
"width": 0.0531,
|
||||
"x": 0.0543,
|
||||
"y": 0.4473
|
||||
},
|
||||
"props": {
|
||||
"params": {
|
||||
"tagProps": [
|
||||
"System/MCM02/PE/TPE/PS3_12_TPE1",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
"path": "autStand/Equipment/Photoeye_Chute",
|
||||
"style": {
|
||||
"classes": "hover-90",
|
||||
"overflow": "visible"
|
||||
},
|
||||
"useDefaultViewHeight": true,
|
||||
"useDefaultViewWidth": true
|
||||
},
|
||||
"type": "ia.display.view"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T10:19:56Z"
|
||||
"timestamp": "2025-11-06T07:10:44Z"
|
||||
},
|
||||
"lastModificationSignature": "43d1be0664c71a5e4d3d4aabd47c7de302c43a5c16b73253ee224b331c75f8b0"
|
||||
"lastModificationSignature": "3ec9d44b3da00172f7274ea7375a30869247ebe90d8164e8382a4589a7b8c543"
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
"PLC": "MCM01",
|
||||
"color": "#C2C2C2",
|
||||
"showTags": true,
|
||||
"state": "Offline"
|
||||
"state": "Closed"
|
||||
},
|
||||
"params": {
|
||||
"devices": [],
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T10:10:15Z"
|
||||
"timestamp": "2025-11-06T06:30:46Z"
|
||||
},
|
||||
"lastModificationSignature": "b2d0d45ff1c80965505c780bd723721a4160fa8eb9dd6d8e8b303967a53cb7a3"
|
||||
"lastModificationSignature": "d10ceafdc901b383a0a4411bd8d28abd097f520027017a0482d5ec2e2ac1ed94"
|
||||
}
|
||||
}
|
||||
@ -71,232 +71,6 @@
|
||||
"persistent": true
|
||||
},
|
||||
"custom.state": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"fallbackDelay": 2.5,
|
||||
"mode": "indirect",
|
||||
"references": {
|
||||
"0": "{view.params.tagProps[0]}",
|
||||
"fc": "{session.custom.fc}"
|
||||
},
|
||||
"tagPath": "[{fc}_SCADA_TAG_PROVIDER]{0}/State"
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"expression": "coalesce({value},-1)",
|
||||
"type": "expression"
|
||||
},
|
||||
{
|
||||
"fallback": "Offline",
|
||||
"inputType": "scalar",
|
||||
"mappings": [
|
||||
{
|
||||
"input": 0,
|
||||
"output": "Closed"
|
||||
},
|
||||
{
|
||||
"input": 1,
|
||||
"output": "Actuated"
|
||||
},
|
||||
{
|
||||
"input": 2,
|
||||
"output": "Communication Faulted"
|
||||
},
|
||||
{
|
||||
"input": 3,
|
||||
"output": "Conveyor Running In Maintenance Mode"
|
||||
},
|
||||
{
|
||||
"input": 4,
|
||||
"output": "Disabled"
|
||||
},
|
||||
{
|
||||
"input": 5,
|
||||
"output": "Disconnected"
|
||||
},
|
||||
{
|
||||
"input": 6,
|
||||
"output": "Stopped"
|
||||
},
|
||||
{
|
||||
"input": 7,
|
||||
"output": "Enabled Not Running"
|
||||
},
|
||||
{
|
||||
"input": 8,
|
||||
"output": "Encoder Fault"
|
||||
},
|
||||
{
|
||||
"input": 9,
|
||||
"output": "Energy Management"
|
||||
},
|
||||
{
|
||||
"input": 10,
|
||||
"output": "ESTOP Was Actuated"
|
||||
},
|
||||
{
|
||||
"input": 11,
|
||||
"output": "EStopped"
|
||||
},
|
||||
{
|
||||
"input": 12,
|
||||
"output": "EStopped Locally"
|
||||
},
|
||||
{
|
||||
"input": 13,
|
||||
"output": "Extended Faulted"
|
||||
},
|
||||
{
|
||||
"input": 14,
|
||||
"output": "Full"
|
||||
},
|
||||
{
|
||||
"input": 15,
|
||||
"output": "Gaylord Start Pressed"
|
||||
},
|
||||
{
|
||||
"input": 16,
|
||||
"output": "Jam Fault"
|
||||
},
|
||||
{
|
||||
"input": 17,
|
||||
"output": "Jammed"
|
||||
},
|
||||
{
|
||||
"input": 18,
|
||||
"output": "Loading Allowed"
|
||||
},
|
||||
{
|
||||
"input": 19,
|
||||
"output": "Loading Not Allowed"
|
||||
},
|
||||
{
|
||||
"input": 20,
|
||||
"output": "Low Air Pressure Fault Was Present"
|
||||
},
|
||||
{
|
||||
"input": 21,
|
||||
"output": "Maintenance Mode"
|
||||
},
|
||||
{
|
||||
"input": 22,
|
||||
"output": "Conveyor Stopped In Maintenance Mode"
|
||||
},
|
||||
{
|
||||
"input": 23,
|
||||
"output": "Motor Faulted"
|
||||
},
|
||||
{
|
||||
"input": 24,
|
||||
"output": "Motor Was Faulted"
|
||||
},
|
||||
{
|
||||
"input": 25,
|
||||
"output": "Normal"
|
||||
},
|
||||
{
|
||||
"input": 26,
|
||||
"output": "Off Inactive"
|
||||
},
|
||||
{
|
||||
"input": 27,
|
||||
"output": "Open"
|
||||
},
|
||||
{
|
||||
"input": 28,
|
||||
"output": "PLC Ready To Run"
|
||||
},
|
||||
{
|
||||
"input": 29,
|
||||
"output": "Package Release Pressed"
|
||||
},
|
||||
{
|
||||
"input": 30,
|
||||
"output": "Power Branch Was Faulted"
|
||||
},
|
||||
{
|
||||
"input": 31,
|
||||
"output": "Pressed"
|
||||
},
|
||||
{
|
||||
"input": 32,
|
||||
"output": "Ready To Receive"
|
||||
},
|
||||
{
|
||||
"input": 33,
|
||||
"output": "Running"
|
||||
},
|
||||
{
|
||||
"input": 34,
|
||||
"output": "Started"
|
||||
},
|
||||
{
|
||||
"input": 35,
|
||||
"output": "Stopped"
|
||||
},
|
||||
{
|
||||
"input": 36,
|
||||
"output": "System Started"
|
||||
},
|
||||
{
|
||||
"input": 37,
|
||||
"output": "Unknown"
|
||||
},
|
||||
{
|
||||
"input": 38,
|
||||
"output": "VFD Fault"
|
||||
},
|
||||
{
|
||||
"input": 39,
|
||||
"output": "Conveyor Running In Power Saving Mode"
|
||||
},
|
||||
{
|
||||
"input": 40,
|
||||
"output": "Conveyor Jogging In Maintenance Mode"
|
||||
},
|
||||
{
|
||||
"input": 41,
|
||||
"output": "VFD Reset Required"
|
||||
},
|
||||
{
|
||||
"input": 42,
|
||||
"output": "Jam Reset Push Button Pressed"
|
||||
},
|
||||
{
|
||||
"input": 43,
|
||||
"output": "Start Push Button Pressed"
|
||||
},
|
||||
{
|
||||
"input": 44,
|
||||
"output": "Stop Push Button Pressed"
|
||||
},
|
||||
{
|
||||
"input": 45,
|
||||
"output": "No Container"
|
||||
},
|
||||
{
|
||||
"input": 46,
|
||||
"output": "Ready To Be Enabled"
|
||||
},
|
||||
{
|
||||
"input": 47,
|
||||
"output": "Half Full"
|
||||
},
|
||||
{
|
||||
"input": 48,
|
||||
"output": "Enabled"
|
||||
},
|
||||
{
|
||||
"input": 49,
|
||||
"output": "Tipper Faulted"
|
||||
}
|
||||
],
|
||||
"outputType": "scalar",
|
||||
"type": "map"
|
||||
}
|
||||
],
|
||||
"type": "tag"
|
||||
},
|
||||
"persistent": true
|
||||
},
|
||||
"custom.view": {
|
||||
@ -307,6 +81,10 @@
|
||||
"persistent": true
|
||||
},
|
||||
"params.tagProps": {
|
||||
"onChange": {
|
||||
"enabled": null,
|
||||
"script": "\ttagPath \u003d currentValue.value[0].value #I know this looks ugly\t\t\t\n\tstatus \u003d autStand.devices.get_single_device_status(self, tagPath)\n\tself.view.custom.state \u003d status\n\n"
|
||||
},
|
||||
"paramDirection": "input",
|
||||
"persistent": true
|
||||
},
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T10:06:34Z"
|
||||
"timestamp": "2025-11-06T10:48:24Z"
|
||||
},
|
||||
"lastModificationSignature": "4ecba60a0ac1422450b0014b8931ec222fa721d33dace06608f36c9d4237d607"
|
||||
"lastModificationSignature": "15f70498ff0d20fac7dd1f3668d7659faf171f84d8964bd1bb19f3a9e6cc0bb3"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 42 KiB |
@ -3,12 +3,54 @@
|
||||
"PLC": "MCM01",
|
||||
"amperage": 0,
|
||||
"device": "UL15_1_VFD1",
|
||||
"faultDescription": "",
|
||||
"faultProbableCause": [
|
||||
{
|
||||
"instancePosition": {},
|
||||
"instanceStyle": {},
|
||||
"text": "Ambient temperature too high."
|
||||
},
|
||||
{
|
||||
"instancePosition": {},
|
||||
"instanceStyle": {},
|
||||
"text": "Fan or ventilation slots are polluted."
|
||||
},
|
||||
{
|
||||
"instancePosition": {},
|
||||
"instanceStyle": {},
|
||||
"text": "Fan is defective."
|
||||
},
|
||||
"value",
|
||||
"value",
|
||||
"value",
|
||||
"value"
|
||||
],
|
||||
"faultRemedy": [
|
||||
{
|
||||
"instancePosition": {},
|
||||
"instanceStyle": {},
|
||||
"text": "Provide for a sufficient cooling of the device."
|
||||
},
|
||||
{
|
||||
"instancePosition": {},
|
||||
"instanceStyle": {},
|
||||
"text": "Clean fan and ventilation slots."
|
||||
},
|
||||
{
|
||||
"instancePosition": {},
|
||||
"instanceStyle": {},
|
||||
"text": "If required, replace fan."
|
||||
},
|
||||
"value",
|
||||
"value",
|
||||
"value"
|
||||
],
|
||||
"fpm": 0,
|
||||
"frequency": 0,
|
||||
"lastFaultCode": 0,
|
||||
"maintance_mode": false,
|
||||
"showTags": true,
|
||||
"state": "Offline",
|
||||
"state": "FAULTED/DISCONNECTED",
|
||||
"statusCode": 0,
|
||||
"voltage": 0
|
||||
},
|
||||
@ -79,6 +121,77 @@
|
||||
},
|
||||
"persistent": true
|
||||
},
|
||||
"custom.faultDescription": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"fallbackDelay": 2.5,
|
||||
"mode": "indirect",
|
||||
"references": {
|
||||
"0": "{view.params.tagProps[0]}",
|
||||
"fc": "{session.custom.fc}"
|
||||
},
|
||||
"tagPath": "[{fc}_SCADA_TAG_PROVIDER]{0}/Fault/Fault_Description"
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"expression": "coalesce({value},\"\")",
|
||||
"type": "expression"
|
||||
}
|
||||
],
|
||||
"type": "tag"
|
||||
},
|
||||
"persistent": true
|
||||
},
|
||||
"custom.faultProbableCause": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"fallbackDelay": 2.5,
|
||||
"mode": "indirect",
|
||||
"references": {
|
||||
"0": "{view.params.tagProps[0]}",
|
||||
"fc": "{session.custom.fc}"
|
||||
},
|
||||
"tagPath": "[{fc}_SCADA_TAG_PROVIDER]{0}/Fault/Fault_Probable_Cause"
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"expression": "coalesce({value},\"\")",
|
||||
"type": "expression"
|
||||
},
|
||||
{
|
||||
"code": "\t# Input: a string like \"Ambient temperature too high. || Fan or ventilation slots are polluted. || Fan is defective.\"\n\t# Output: a list of instance dicts for the repeater\n\t\n\titems \u003d []\n\t\n\ttry:\n\t text \u003d str(value).strip()\n\t if text:\n\t # Split by \"||\" and clean up\n\t parts \u003d [p.strip() for p in text.split(\"||\") if p.strip()]\n\t \n\t # Build instance dictionaries\n\t for p in parts:\n\t items.append({\n\t \"instanceStyle\": {},\n\t \"instancePosition\": {},\n\t \"text\": p\n\t })\n\texcept Exception as e:\n\t system.perspective.print(\"FaultItem repeater transform error: \" + str(e))\n\t items \u003d []\n\t\n\treturn items",
|
||||
"type": "script"
|
||||
}
|
||||
],
|
||||
"type": "tag"
|
||||
},
|
||||
"persistent": true
|
||||
},
|
||||
"custom.faultRemedy": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"fallbackDelay": 2.5,
|
||||
"mode": "indirect",
|
||||
"references": {
|
||||
"0": "{view.params.tagProps[0]}",
|
||||
"fc": "{session.custom.fc}"
|
||||
},
|
||||
"tagPath": "[{fc}_SCADA_TAG_PROVIDER]{0}/Fault/Fault_Remedy"
|
||||
},
|
||||
"transforms": [
|
||||
{
|
||||
"expression": "coalesce({value},\"\")",
|
||||
"type": "expression"
|
||||
},
|
||||
{
|
||||
"code": "\t# Input: a string like \"Ambient temperature too high. || Fan or ventilation slots are polluted. || Fan is defective.\"\n\t# Output: a list of instance dicts for the repeater\n\t\n\titems \u003d []\n\t\n\ttry:\n\t text \u003d str(value).strip()\n\t if text:\n\t # Split by \"||\" and clean up\n\t parts \u003d [p.strip() for p in text.split(\"||\") if p.strip()]\n\t \n\t # Build instance dictionaries\n\t for p in parts:\n\t items.append({\n\t \"instanceStyle\": {},\n\t \"instancePosition\": {},\n\t \"text\": p\n\t })\n\texcept Exception as e:\n\t system.perspective.print(\"FaultItem repeater transform error: \" + str(e))\n\t items \u003d []\n\t\n\treturn items",
|
||||
"type": "script"
|
||||
}
|
||||
],
|
||||
"type": "tag"
|
||||
},
|
||||
"persistent": true
|
||||
},
|
||||
"custom.fpm": {
|
||||
"binding": {
|
||||
"config": {
|
||||
@ -274,6 +387,7 @@
|
||||
},
|
||||
"props": {
|
||||
"defaultSize": {
|
||||
"height": 1080,
|
||||
"width": 600
|
||||
}
|
||||
},
|
||||
@ -935,7 +1049,7 @@
|
||||
"height": 161,
|
||||
"width": 98,
|
||||
"x": 97.65,
|
||||
"y": 505.33000000000004
|
||||
"y": 455.33
|
||||
},
|
||||
"propConfig": {
|
||||
"props.style.backgroundColor": {
|
||||
@ -975,7 +1089,7 @@
|
||||
"height": 92,
|
||||
"width": 314,
|
||||
"x": 205.65,
|
||||
"y": 505.30999999999995
|
||||
"y": 455.31
|
||||
},
|
||||
"propConfig": {
|
||||
"props.style.backgroundColor": {
|
||||
@ -1015,7 +1129,7 @@
|
||||
"height": 64,
|
||||
"width": 98,
|
||||
"x": 205.296,
|
||||
"y": 602.3
|
||||
"y": 552.3
|
||||
},
|
||||
"propConfig": {
|
||||
"props.enabled": {
|
||||
@ -1063,7 +1177,7 @@
|
||||
"height": 64,
|
||||
"width": 98,
|
||||
"x": 314.65,
|
||||
"y": 602.29
|
||||
"y": 552.29
|
||||
},
|
||||
"propConfig": {
|
||||
"props.enabled": {
|
||||
@ -1111,7 +1225,7 @@
|
||||
"height": 64,
|
||||
"width": 98,
|
||||
"x": 421.296,
|
||||
"y": 602.249
|
||||
"y": 552.249
|
||||
},
|
||||
"propConfig": {
|
||||
"props.enabled": {
|
||||
@ -1259,6 +1373,231 @@
|
||||
"y": 381.49
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"meta": {
|
||||
"name": "Label"
|
||||
},
|
||||
"position": {
|
||||
"basis": "32px"
|
||||
},
|
||||
"propConfig": {
|
||||
"props.text": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"expression": "\"LENZE FAULTED : \" + {view.custom.faultDescription}"
|
||||
},
|
||||
"type": "expr"
|
||||
}
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"style": {
|
||||
"color": "black",
|
||||
"paddingLeft": "20px"
|
||||
}
|
||||
},
|
||||
"type": "ia.display.label"
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"meta": {
|
||||
"name": "Label"
|
||||
},
|
||||
"position": {
|
||||
"basis": "33px",
|
||||
"shrink": 0
|
||||
},
|
||||
"props": {
|
||||
"style": {
|
||||
"background": "#d9d9d9",
|
||||
"color": "#000000"
|
||||
},
|
||||
"text": "Cause",
|
||||
"textStyle": {
|
||||
"textAlign": "center"
|
||||
}
|
||||
},
|
||||
"type": "ia.display.label"
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"meta": {
|
||||
"name": "FlexRepeater"
|
||||
},
|
||||
"position": {
|
||||
"basis": "100%"
|
||||
},
|
||||
"propConfig": {
|
||||
"props.instances": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"path": "view.custom.faultProbableCause"
|
||||
},
|
||||
"type": "property"
|
||||
}
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"direction": "column",
|
||||
"path": "autStand/Equipment/VFD-Views/FaultItem",
|
||||
"style": {
|
||||
"overflowX": "hidden"
|
||||
},
|
||||
"useDefaultViewWidth": false
|
||||
},
|
||||
"type": "ia.display.flex-repeater"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "FlexContainer"
|
||||
},
|
||||
"position": {
|
||||
"basis": "200px",
|
||||
"grow": 1
|
||||
},
|
||||
"props": {
|
||||
"direction": "column",
|
||||
"style": {
|
||||
"borderRight": "solid black 0.5px",
|
||||
"overflow": "visible"
|
||||
}
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "FlexContainer"
|
||||
},
|
||||
"position": {
|
||||
"basis": "277px"
|
||||
},
|
||||
"props": {
|
||||
"direction": "column"
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"meta": {
|
||||
"name": "Label"
|
||||
},
|
||||
"position": {
|
||||
"basis": "33px",
|
||||
"shrink": 0
|
||||
},
|
||||
"props": {
|
||||
"style": {
|
||||
"background": "#d9d9d9",
|
||||
"color": "#000000"
|
||||
},
|
||||
"text": "Remedy",
|
||||
"textStyle": {
|
||||
"textAlign": "center"
|
||||
}
|
||||
},
|
||||
"type": "ia.display.label"
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"meta": {
|
||||
"name": "FlexRepeater"
|
||||
},
|
||||
"position": {
|
||||
"basis": "100%"
|
||||
},
|
||||
"propConfig": {
|
||||
"props.instances": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"path": "view.custom.faultRemedy"
|
||||
},
|
||||
"type": "property"
|
||||
}
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"direction": "column",
|
||||
"path": "autStand/Equipment/VFD-Views/FaultItem",
|
||||
"style": {
|
||||
"overflowX": "hidden"
|
||||
},
|
||||
"useDefaultViewWidth": false
|
||||
},
|
||||
"type": "ia.display.flex-repeater"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "FlexContainer"
|
||||
},
|
||||
"position": {
|
||||
"basis": "200px",
|
||||
"grow": 1
|
||||
},
|
||||
"props": {
|
||||
"direction": "column",
|
||||
"style": {
|
||||
"overflow": "visible"
|
||||
}
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "FlexContainer_0"
|
||||
},
|
||||
"position": {
|
||||
"basis": "277px"
|
||||
},
|
||||
"props": {
|
||||
"direction": "column",
|
||||
"style": {
|
||||
"borderLeft": "solid black 0.5px"
|
||||
}
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "FlexContainer_8"
|
||||
},
|
||||
"position": {
|
||||
"grow": 1
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "FlexContainer_2"
|
||||
},
|
||||
"position": {
|
||||
"height": 300,
|
||||
"width": 508,
|
||||
"x": 42.33000093823242,
|
||||
"y": 642.33
|
||||
},
|
||||
"propConfig": {
|
||||
"meta.visible": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"expression": "{view.custom.faultDescription} !\u003d \"\""
|
||||
},
|
||||
"type": "expr"
|
||||
}
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"direction": "column"
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
|
||||
@ -9,8 +9,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T10:03:20Z"
|
||||
"timestamp": "2025-11-05T10:58:14Z"
|
||||
},
|
||||
"lastModificationSignature": "a45fc778e3455652a14c8b77b8c627633086bef4b0984bff774dc9ebdf86e057"
|
||||
"lastModificationSignature": "71bfb1fcb21970bcad2d212abf101e99ef7649061deac3f5f19a56201c32172e"
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
"beacon": 0,
|
||||
"flashingColor": "#808080",
|
||||
"solidColor": "#FF8C00",
|
||||
"state": "Offline"
|
||||
"state": "OFF"
|
||||
},
|
||||
"params": {
|
||||
"demoColor": "",
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T10:09:30Z"
|
||||
"timestamp": "2025-11-05T10:58:25Z"
|
||||
},
|
||||
"lastModificationSignature": "202804489dea1be0a5952a0a0668d47e8996b586217d8ad06b5d35301a270f64"
|
||||
"lastModificationSignature": "e589663e9d0f7b86bd955fa5f896c4baf8f4a5aac6054a89087b606e53e849e4"
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
"color": "#000000",
|
||||
"deviceName": "S03_1_JR1",
|
||||
"priority": "No Active Alarms",
|
||||
"state": "Offline"
|
||||
"state": "Actuated"
|
||||
},
|
||||
"params": {
|
||||
"demoColor": -1,
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T10:03:48Z"
|
||||
"timestamp": "2025-11-05T10:58:14Z"
|
||||
},
|
||||
"lastModificationSignature": "c0cad2fb0a077cee13c61f73d4e2698972da4e4f0919e1dc8186d33ccb57d204"
|
||||
"lastModificationSignature": "0d465364c45191827e9b2d189ed92b7134de40f76d8b257abb57acb6ce25c269"
|
||||
}
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
"color": "#000000",
|
||||
"deviceName": "S03_CH115_EN",
|
||||
"priority": "No Active Alarms",
|
||||
"state": "Offline"
|
||||
"state": "Closed"
|
||||
},
|
||||
"params": {
|
||||
"demoColor": -1,
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-03T10:48:14Z"
|
||||
"timestamp": "2025-11-06T09:45:29Z"
|
||||
},
|
||||
"lastModificationSignature": "e4adadbb2ccaa998f6711c2f1835a11716490fb19c8407ca298fb9f3f7f4ae71"
|
||||
"lastModificationSignature": "a7f4a48b3b1044a614c495a7abbb5fc64daa1bd6fa748696d30ff0d6c61e4409"
|
||||
}
|
||||
}
|
||||
@ -3,16 +3,16 @@
|
||||
"counts": {
|
||||
"Critical": 0,
|
||||
"Diagnostic": 0,
|
||||
"High": 20,
|
||||
"Low": 5,
|
||||
"Medium": 2,
|
||||
"Total": 27
|
||||
"High": 6,
|
||||
"Low": 2,
|
||||
"Medium": 1,
|
||||
"Total": 9
|
||||
},
|
||||
"totalAlarms": {
|
||||
"$": [
|
||||
"ds",
|
||||
192,
|
||||
1762166890980
|
||||
1762422326706
|
||||
],
|
||||
"$columns": [
|
||||
{
|
||||
@ -39,11 +39,11 @@
|
||||
},
|
||||
{
|
||||
"data": [
|
||||
20,
|
||||
5,
|
||||
6,
|
||||
2,
|
||||
36,
|
||||
3
|
||||
1,
|
||||
32,
|
||||
4
|
||||
],
|
||||
"name": "Count",
|
||||
"type": "Long"
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-03T10:48:13Z"
|
||||
"timestamp": "2025-11-06T09:45:28Z"
|
||||
},
|
||||
"lastModificationSignature": "cf6029c178a1897c4e3745df0737345d13ee88bf22deb4eba98b83aba636794d"
|
||||
"lastModificationSignature": "caef4a008da4d1c2473f73bb35520e0a7f394b4a25cb5947098e0dd928cf5dee"
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@
|
||||
"$": [
|
||||
"ds",
|
||||
192,
|
||||
1762166893089
|
||||
1762422327752
|
||||
],
|
||||
"$columns": [
|
||||
{
|
||||
@ -31,11 +31,11 @@
|
||||
},
|
||||
{
|
||||
"data": [
|
||||
20,
|
||||
5,
|
||||
6,
|
||||
2,
|
||||
36,
|
||||
3
|
||||
1,
|
||||
32,
|
||||
4
|
||||
],
|
||||
"name": "Count",
|
||||
"type": "Long"
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-03T10:45:14Z"
|
||||
"timestamp": "2025-11-06T09:28:00Z"
|
||||
},
|
||||
"lastModificationSignature": "905f499557e14f9a6ba14b18a8f3a8a99087be92b0ad57a907532cd840a2efea"
|
||||
"lastModificationSignature": "33a2d01b5e1831e82442481afd4cfaa270b9e4136809c9c6711b8d1272c54167"
|
||||
}
|
||||
}
|
||||
@ -443,7 +443,7 @@
|
||||
"basis": "32px"
|
||||
},
|
||||
"props": {
|
||||
"text": 20,
|
||||
"text": 6,
|
||||
"textStyle": {
|
||||
"fontSize": 10,
|
||||
"textAlign": "center"
|
||||
@ -472,7 +472,7 @@
|
||||
"basis": "32px"
|
||||
},
|
||||
"props": {
|
||||
"text": 2,
|
||||
"text": 1,
|
||||
"textStyle": {
|
||||
"fontSize": 10,
|
||||
"textAlign": "center"
|
||||
@ -501,7 +501,7 @@
|
||||
"basis": "32px"
|
||||
},
|
||||
"props": {
|
||||
"text": 5,
|
||||
"text": 2,
|
||||
"textStyle": {
|
||||
"fontSize": 10,
|
||||
"textAlign": "center"
|
||||
@ -559,7 +559,7 @@
|
||||
"basis": "32px"
|
||||
},
|
||||
"props": {
|
||||
"text": 27,
|
||||
"text": 9,
|
||||
"textStyle": {
|
||||
"fontSize": 10,
|
||||
"textAlign": "center"
|
||||
@ -625,9 +625,9 @@
|
||||
"counts": {
|
||||
"Critical": 0,
|
||||
"Diagnostic": 0,
|
||||
"High": 20,
|
||||
"Low": 5,
|
||||
"Medium": 2
|
||||
"High": 6,
|
||||
"Low": 2,
|
||||
"Medium": 1
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T10:04:41Z"
|
||||
"timestamp": "2025-11-06T05:53:32Z"
|
||||
},
|
||||
"lastModificationSignature": "c3053fd47ba6060f90c82b968094d2ad57a40bc60e1a63de3439b5bca745bda2"
|
||||
"lastModificationSignature": "09a417e495e942ce0d88518e9bbbe1e80703111c87e5b1c709b329fb4aecb0a9"
|
||||
}
|
||||
}
|
||||
@ -571,7 +571,7 @@
|
||||
"dom": {
|
||||
"onClick": {
|
||||
"config": {
|
||||
"script": "\t#create devices and tags lists for the conveyor\n\tprops \u003d self.view.params.tagProps[0]\n\tautStand.devices.build_device_mapping(props)\n\tdevice_table_dataset \u003d autStand.devices.build_device_table(self)\n\ttags_table_dataset \u003d autStand.devices.getAllTags(self, props)\n\tsystem.perspective.openDock(\u0027Docked-East-Conv\u0027,params\u003d{\u0027tagProps\u0027:self.view.params.tagProps, \"devices\": device_table_dataset, \"tags\":tags_table_dataset})"
|
||||
"script": "\t#create devices and tags lists for the conveyor\n\tprops \u003d self.view.params.tagProps[0]\n\tautStand.devices.build_device_mapping(props)\n\tdevice_table_dataset \u003d autStand.devices.build_device_table(self)\n\ttags_table_dataset \u003d autStand.devices.getAllTags(self, props, section \u003d \"conveyor\")\n\tsystem.perspective.openDock(\u0027Docked-East-Conv\u0027,params\u003d{\u0027tagProps\u0027:self.view.params.tagProps, \"devices\": device_table_dataset, \"tags\":tags_table_dataset})"
|
||||
},
|
||||
"scope": "G",
|
||||
"type": "script"
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T10:05:09Z"
|
||||
"timestamp": "2025-11-06T05:53:39Z"
|
||||
},
|
||||
"lastModificationSignature": "11f6963d22782647abcea15e4264ecba09836177042468b2d2cb434715c4ae1e"
|
||||
"lastModificationSignature": "922086cbe69562d7e45d7d20f33be2d8f41991e47ae7e6a280195f0e92b4d23a"
|
||||
}
|
||||
}
|
||||
@ -594,7 +594,7 @@
|
||||
"dom": {
|
||||
"onClick": {
|
||||
"config": {
|
||||
"script": "\t#create devices and tags lists for the conveyor\n\tprops \u003d self.view.params.tagProps[0]\n\tautStand.devices.build_device_mapping(props)\n\tdevice_table_dataset \u003d autStand.devices.build_device_table(self)\n\ttags_table_dataset \u003d autStand.devices.getAllTags(self, props)\n\tsystem.perspective.openDock(\u0027Docked-East-Conv\u0027,params\u003d{\u0027tagProps\u0027:self.view.params.tagProps, \"devices\": device_table_dataset, \"tags\":tags_table_dataset})"
|
||||
"script": "\t#create devices and tags lists for the conveyor\n\tprops \u003d self.view.params.tagProps[0]\n\tautStand.devices.build_device_mapping(props)\n\tdevice_table_dataset \u003d autStand.devices.build_device_table(self)\n\ttags_table_dataset \u003d autStand.devices.getAllTags(self, props, section \u003d \"conveyor\")\n\tsystem.perspective.openDock(\u0027Docked-East-Conv\u0027,params\u003d{\u0027tagProps\u0027:self.view.params.tagProps, \"devices\": device_table_dataset, \"tags\":tags_table_dataset})"
|
||||
},
|
||||
"scope": "G",
|
||||
"type": "script"
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T10:05:46Z"
|
||||
"timestamp": "2025-11-06T05:53:46Z"
|
||||
},
|
||||
"lastModificationSignature": "66e4286fd9730511928ddcf22341e9cf8d36aac0f861b6a32011597ee1e93490"
|
||||
"lastModificationSignature": "41eb3b4d3418906754d43553be1845cdd7ccd9841a13e983b4f290d6e6247e8a"
|
||||
}
|
||||
}
|
||||
@ -574,7 +574,7 @@
|
||||
"dom": {
|
||||
"onClick": {
|
||||
"config": {
|
||||
"script": "\t#create devices and tags lists for the conveyor\n\tprops \u003d self.view.params.tagProps[0]\n\tautStand.devices.build_device_mapping(props)\n\tdevice_table_dataset \u003d autStand.devices.build_device_table(self)\n\ttags_table_dataset \u003d autStand.devices.getAllTags(self, props)\n\tsystem.perspective.openDock(\u0027Docked-East-Conv\u0027,params\u003d{\u0027tagProps\u0027:self.view.params.tagProps, \"devices\": device_table_dataset, \"tags\":tags_table_dataset})"
|
||||
"script": "\t#create devices and tags lists for the conveyor\n\tprops \u003d self.view.params.tagProps[0]\n\tautStand.devices.build_device_mapping(props)\n\tdevice_table_dataset \u003d autStand.devices.build_device_table(self)\n\ttags_table_dataset \u003d autStand.devices.getAllTags(self, props, section \u003d \"conveyor\")\n\tsystem.perspective.openDock(\u0027Docked-East-Conv\u0027,params\u003d{\u0027tagProps\u0027:self.view.params.tagProps, \"devices\": device_table_dataset, \"tags\":tags_table_dataset})"
|
||||
},
|
||||
"scope": "G",
|
||||
"type": "script"
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T09:56:58Z"
|
||||
"timestamp": "2025-11-05T16:03:12Z"
|
||||
},
|
||||
"lastModificationSignature": "47cb76182cad0ebbd030abdd3997bfc6c5031f7b3111d9378e0ec295e06995f3"
|
||||
"lastModificationSignature": "af732729fae7c59783c445521081d73eb607e4723fe1070097c979d25a5dd1e1"
|
||||
}
|
||||
}
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T09:56:37Z"
|
||||
"timestamp": "2025-11-05T11:04:37Z"
|
||||
},
|
||||
"lastModificationSignature": "73ef999335872207376b4932914dc91ce58ac2e266a384a16d2ed0679307a363"
|
||||
"lastModificationSignature": "2967f9a672eda98f9b5149d0d6037ce4870a5c4458a77de30f302c5d94a0cb81"
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@
|
||||
"isHighlited": false,
|
||||
"overlayColor": "#ffffff",
|
||||
"priority": "No Active Alarms",
|
||||
"state": "Offline"
|
||||
"state": "BLOCKED"
|
||||
},
|
||||
"params": {
|
||||
"demoColor": -1,
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T09:55:47Z"
|
||||
"timestamp": "2025-11-05T11:02:12Z"
|
||||
},
|
||||
"lastModificationSignature": "e499dac6c15ef5769b713f487a901f89ddded7480c634bec50783348ccd686ad"
|
||||
"lastModificationSignature": "e62e71b2c3c31d9b02ac9ae66a4e5a4858c85d9bb78bdc1beb5ca56be7eb8a90"
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
{
|
||||
"custom": {
|
||||
"color": "#000000",
|
||||
"color": "#AAAAAA",
|
||||
"deviceName": "S03_CH101_PRX1",
|
||||
"state": "Offline"
|
||||
"state": "INACTIVE"
|
||||
},
|
||||
"params": {
|
||||
"demoColor": -1,
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T09:50:17Z"
|
||||
"timestamp": "2025-11-05T10:58:14Z"
|
||||
},
|
||||
"lastModificationSignature": "00481db9366a0ae0a8ca6bea529f685857c34990822f5aa2f60b42c12400148b"
|
||||
"lastModificationSignature": "e439249b233a0322cf68a4b7c22d5871ff1adf0d75c7b265d487f4f564ff9009"
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@
|
||||
"divertingLeft": false,
|
||||
"divertingRight": false,
|
||||
"priority": "No Active Alarms",
|
||||
"state": "Offline"
|
||||
"state": "Closed"
|
||||
},
|
||||
"params": {
|
||||
"demoColor": 0,
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
{
|
||||
"scope": "G",
|
||||
"version": 1,
|
||||
"restricted": false,
|
||||
"overridable": true,
|
||||
"files": [
|
||||
"view.json",
|
||||
"thumbnail.png"
|
||||
],
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-06T10:41:41Z"
|
||||
},
|
||||
"lastModificationSignature": "94ada3882d5e031e352abc0f63fdb45b43f2491a46298cbcded7dcb4468f6367"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1,77 @@
|
||||
{
|
||||
"custom": {},
|
||||
"params": {
|
||||
"text": "Provide for a sufficient cooling of the device."
|
||||
},
|
||||
"propConfig": {
|
||||
"params.text": {
|
||||
"paramDirection": "input",
|
||||
"persistent": true
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"defaultSize": {
|
||||
"height": 50,
|
||||
"width": 300
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"children": [
|
||||
{
|
||||
"meta": {
|
||||
"name": "Icon"
|
||||
},
|
||||
"position": {
|
||||
"basis": "20px",
|
||||
"shrink": 0
|
||||
},
|
||||
"props": {
|
||||
"path": "material/brightness_1",
|
||||
"style": {
|
||||
"color": "black",
|
||||
"marginLeft": 4,
|
||||
"marginTop": 4
|
||||
}
|
||||
},
|
||||
"type": "ia.display.icon"
|
||||
},
|
||||
{
|
||||
"meta": {
|
||||
"name": "Label"
|
||||
},
|
||||
"position": {
|
||||
"grow": 1
|
||||
},
|
||||
"propConfig": {
|
||||
"props.text": {
|
||||
"binding": {
|
||||
"config": {
|
||||
"path": "view.params.text"
|
||||
},
|
||||
"type": "property"
|
||||
}
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"textStyle": {
|
||||
"color": "black",
|
||||
"fontSize": "18px"
|
||||
}
|
||||
},
|
||||
"type": "ia.display.label"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"name": "root"
|
||||
},
|
||||
"props": {
|
||||
"alignContent": "center",
|
||||
"alignItems": "flex-start",
|
||||
"justify": "space-evenly",
|
||||
"style": {
|
||||
"gap": 7
|
||||
}
|
||||
},
|
||||
"type": "ia.container.flex"
|
||||
}
|
||||
}
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T09:49:59Z"
|
||||
"timestamp": "2025-11-06T09:34:59Z"
|
||||
},
|
||||
"lastModificationSignature": "eb4e651526b151add3fe79e7c2cc150331f570ba01f258f74f9960ce062abc28"
|
||||
"lastModificationSignature": "a03070e098038bd2aa62a5692f809f523c8cdbc858d38157421e0a19292a0a79"
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"custom": {
|
||||
"color": "#000",
|
||||
"color": "#f9050d",
|
||||
"modifiedTag": "System/MCM01/VFD/UL14_1_VFD1",
|
||||
"priority": "No Active Alarms"
|
||||
},
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-03T07:30:59Z"
|
||||
"timestamp": "2025-11-06T09:10:53Z"
|
||||
},
|
||||
"lastModificationSignature": "6a0fecdc30b481e27569e24c81a59df066b326c50e14d901e82490fe84307fed"
|
||||
"lastModificationSignature": "f3c965334dd56490910cdc0493c176ee8dd58074925b58c32317a373fa271897"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 19 KiB |
@ -3,7 +3,7 @@
|
||||
"params": {},
|
||||
"props": {
|
||||
"defaultSize": {
|
||||
"height": 1000
|
||||
"height": 920
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
|
||||
@ -1,308 +1,542 @@
|
||||
import os, json, sys
|
||||
|
||||
# ======================================================
|
||||
# Helper Function: State Resolver
|
||||
# ======================================================
|
||||
def get_device_state(value, tagPath):
|
||||
up = tagPath.upper()
|
||||
|
||||
# === Base state dictionary ===
|
||||
state_mappings = {
|
||||
0: "Closed",
|
||||
1: "Actuated",
|
||||
2: "Communication Faulted",
|
||||
3: "Conveyor Running In Maintenance Mode",
|
||||
4: "Disabled",
|
||||
5: "Disconnected",
|
||||
6: "Stopped",
|
||||
7: "Enabled Not Running",
|
||||
8: "Encoder Fault",
|
||||
9: "Energy Management",
|
||||
10: "ESTOP Was Actuated",
|
||||
11: "EStopped",
|
||||
12: "EStopped Locally",
|
||||
13: "Extended Faulted",
|
||||
14: "Full",
|
||||
15: "Gaylord Start Pressed",
|
||||
16: "Jam Fault",
|
||||
17: "Jammed",
|
||||
18: "Loading Allowed",
|
||||
19: "Loading Not Allowed",
|
||||
20: "Low Air Pressure Fault Was Present",
|
||||
21: "Maintenance Mode",
|
||||
22: "Conveyor Stopped In Maintenance Mode",
|
||||
23: "Motor Faulted",
|
||||
24: "Motor Was Faulted",
|
||||
25: "Normal",
|
||||
26: "Off Inactive",
|
||||
27: "Open",
|
||||
28: "PLC Ready To Run",
|
||||
29: "Package Release Pressed",
|
||||
30: "Power Branch Was Faulted",
|
||||
31: "Pressed",
|
||||
32: "Ready To Receive",
|
||||
33: "Running",
|
||||
34: "Started",
|
||||
35: "Stopped",
|
||||
36: "System Started",
|
||||
37: "Unknown",
|
||||
38: "VFD Fault",
|
||||
39: "Conveyor Running In Power Saving Mode",
|
||||
40: "Conveyor Jogging In Maintenance Mode",
|
||||
41: "VFD Reset Required",
|
||||
42: "Jam Reset Push Button Pressed",
|
||||
43: "Start Push Button Pressed",
|
||||
44: "Stop Push Button Pressed",
|
||||
45: "No Container",
|
||||
46: "Ready To Be Enabled",
|
||||
47: "Half Full",
|
||||
48: "Enabled",
|
||||
49: "Tipper Faulted",
|
||||
50: "OK",
|
||||
51: "Disconnected",
|
||||
52: "Faulted",
|
||||
53: "Faulted/Disconnect",
|
||||
54: "Diverting"
|
||||
}
|
||||
|
||||
# === TPE (Tracking Photoeye) ===
|
||||
if "/TPE/" in up:
|
||||
if value == 0:
|
||||
return "Blocked"
|
||||
elif value == 27:
|
||||
return "Clear"
|
||||
elif value == 17:
|
||||
return "Jammed"
|
||||
else:
|
||||
return state_mappings.get(value, "Unknown")
|
||||
|
||||
# === Single Photoeyes (PE1, PE2) ===
|
||||
if up.endswith(("PE1", "PE2")):
|
||||
if not value:
|
||||
return "Clear"
|
||||
else:
|
||||
return "Blocked"
|
||||
|
||||
# === Prox Sensors (PRX1, PRX2) ===
|
||||
if up.endswith(("PRX1", "PRX2")):
|
||||
if not value:
|
||||
return "Inactive"
|
||||
else:
|
||||
return "Actuated"
|
||||
|
||||
# === Beacons (BCN) ===
|
||||
if "/BEACON" in up:
|
||||
if value == 0:
|
||||
return "Off"
|
||||
elif value == 1:
|
||||
return "Cleared / Reset Required"
|
||||
else:
|
||||
return "Active"
|
||||
|
||||
# === Default ===
|
||||
return state_mappings.get(value, "Unknown")
|
||||
|
||||
|
||||
# ======================================================
|
||||
# Helper Function: Read One Device (multi or single state)
|
||||
# ======================================================
|
||||
def read_device_status(tagPath, provider, dev_name):
|
||||
"""
|
||||
Reads the appropriate state tag(s) for a given device and returns
|
||||
a list of {Device, Status} dictionaries.
|
||||
Handles multi-state (SS), VFD, PRX, PE, EN, etc.
|
||||
"""
|
||||
rows = []
|
||||
try:
|
||||
up = tagPath.upper()
|
||||
|
||||
# === Case 0: SS (Start/Stop Station) ===
|
||||
if up.endswith("SS") or "/SS/" in up:
|
||||
for sub in ("Start", "Stop"):
|
||||
sub_path = provider + tagPath + "/" + sub + "/State"
|
||||
try:
|
||||
result = system.tag.readBlocking([sub_path])[0]
|
||||
if result.quality.isGood():
|
||||
status_value = get_device_state(result.value, tagPath)
|
||||
else:
|
||||
status_value = "Unknown"
|
||||
except:
|
||||
status_value = "Unknown"
|
||||
|
||||
rows.append({
|
||||
"Device": "{} ({})".format(dev_name, sub),
|
||||
"Status": status_value
|
||||
})
|
||||
return rows # handled fully
|
||||
|
||||
# === Case 1: VFD / Conveyor ===
|
||||
if "/VFD/" in up:
|
||||
path = provider + tagPath + "/Drive/Lenze"
|
||||
|
||||
# === Case 2: Chute sensors (PE / PRX) ===
|
||||
elif up.endswith(("PE1", "PE2", "PRX1", "PRX2")) and "/TPE/" not in up:
|
||||
path = provider + tagPath
|
||||
|
||||
# === Case 3: Chute EN ===
|
||||
elif up.endswith("EN") and "/CHUTE/" in up:
|
||||
path = provider + tagPath + "_State"
|
||||
|
||||
# === Case 4: Default ===
|
||||
else:
|
||||
path = provider + tagPath + "/State"
|
||||
|
||||
try:
|
||||
result = system.tag.readBlocking([path])[0]
|
||||
if result.quality.isGood():
|
||||
status_value = get_device_state(result.value, tagPath)
|
||||
else:
|
||||
status_value = "Offline"
|
||||
except:
|
||||
status_value = "Offline"
|
||||
|
||||
rows.append({
|
||||
"Device": dev_name,
|
||||
"Status": status_value
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
system.perspective.print("Error reading device status for %s: %s" % (dev_name, e))
|
||||
|
||||
return rows
|
||||
|
||||
|
||||
# ======================================================
|
||||
# Helper Function: Single Device Reader (for Docked Device View)
|
||||
# ======================================================
|
||||
def get_single_device_status(self, tagPath):
|
||||
"""
|
||||
Reads a single device tag (used for docked device views).
|
||||
Returns a single readable status string (e.g. "Running", "Blocked", etc.)
|
||||
"""
|
||||
try:
|
||||
up = tagPath.upper()
|
||||
provider = "[" + self.session.custom.fc + "_SCADA_TAG_PROVIDER]"
|
||||
|
||||
if up.endswith("SS") or "/SS/" in up:
|
||||
states = []
|
||||
for sub in ("Start", "Stop"):
|
||||
sub_path = provider + tagPath + "/" + sub + "/State"
|
||||
try:
|
||||
result = system.tag.readBlocking([sub_path])[0]
|
||||
if result.quality.isGood():
|
||||
states.append("{}: {}".format(sub, get_device_state(result.value, tagPath)))
|
||||
else:
|
||||
states.append("{}: Unknown".format(sub))
|
||||
except:
|
||||
states.append("{}: Unknown".format(sub))
|
||||
return " | ".join(states)
|
||||
|
||||
# === VFD ===
|
||||
if "/VFD/" in up:
|
||||
path = provider + tagPath + "/Drive/Lenze"
|
||||
# === Sensors ===
|
||||
elif up.endswith(("PE1", "PE2", "PRX1", "PRX2")) and "/TPE/" not in up:
|
||||
path = provider + tagPath
|
||||
# === EN ===
|
||||
elif up.endswith("EN") and "/CHUTE/" in up:
|
||||
path = provider + tagPath + "_State"
|
||||
else:
|
||||
path = provider + tagPath + "/State"
|
||||
|
||||
result = system.tag.readBlocking([path])[0]
|
||||
if result.quality.isGood():
|
||||
return get_device_state(result.value, tagPath)
|
||||
else:
|
||||
return "Unknown"
|
||||
|
||||
except Exception as e:
|
||||
system.perspective.print("Error reading single device status for %s: %s" % (tagPath, e))
|
||||
return "Unknown"
|
||||
|
||||
|
||||
# ======================================================
|
||||
# Device Mapping Builder
|
||||
# ======================================================
|
||||
global_device_mapping = {}
|
||||
|
||||
def build_device_mapping(full_tag_path):
|
||||
"""
|
||||
Builds global_device_mapping for devices that:
|
||||
- Belong to the same PLC (index 1)
|
||||
- Are children of the clicked device (start with clicked_name + "_")
|
||||
"""
|
||||
global global_device_mapping
|
||||
global_device_mapping.clear()
|
||||
|
||||
|
||||
"""
|
||||
Builds global_device_mapping for devices under the same PLC and parent device.
|
||||
Adds support for:
|
||||
- Chute FIOM devices (e.g. S03_CH109_FIOM_1 when clicking S03_CH109)
|
||||
- Shared JR and PE devices used by multiple chutes (e.g. S03_1_JR1, S03_1_LRPE1)
|
||||
"""
|
||||
system.perspective.print(full_tag_path)
|
||||
global global_device_mapping
|
||||
global_device_mapping.clear()
|
||||
|
||||
try:
|
||||
# Parse PLC and clicked device
|
||||
path_parts = full_tag_path.split("/")
|
||||
plc_name = path_parts[1] if len(path_parts) > 1 else path_parts[0]
|
||||
clicked_name = path_parts[-1] if len(path_parts) > 0 else ""
|
||||
if "_VFD" in clicked_name:
|
||||
idx = clicked_name.find("_VFD")
|
||||
if idx != -1:
|
||||
clicked_name = clicked_name[:idx]
|
||||
try:
|
||||
path_parts = full_tag_path.split("/")
|
||||
plc_name = path_parts[1] if len(path_parts) > 1 else path_parts[0]
|
||||
clicked_name = path_parts[-1] if len(path_parts) > 0 else ""
|
||||
|
||||
project_name = system.util.getProjectName()
|
||||
base_path = (
|
||||
os.getcwd().replace("\\", "/")
|
||||
+ "/data/projects/"
|
||||
+ project_name
|
||||
+ "/com.inductiveautomation.perspective/Views/autStand/Detailed_Views/MCM-Views"
|
||||
)
|
||||
# --- Clean clicked name ---
|
||||
if "_VFD" in clicked_name:
|
||||
clicked_name = clicked_name.split("_VFD")[0]
|
||||
|
||||
if not os.path.exists(base_path):
|
||||
system.perspective.print("Path not found: " + base_path)
|
||||
return {}
|
||||
project_name = system.util.getProjectName()
|
||||
base_path = (
|
||||
os.getcwd().replace("\\", "/")
|
||||
+ "/data/projects/"
|
||||
+ project_name
|
||||
+ "/com.inductiveautomation.perspective/Views/autStand/Detailed_Views/MCM-Views"
|
||||
)
|
||||
|
||||
# loop through all view folders
|
||||
for view_folder in os.listdir(base_path):
|
||||
json_file = os.path.join(base_path, view_folder, "view.json")
|
||||
if not os.path.isfile(json_file):
|
||||
continue
|
||||
if not os.path.exists(base_path):
|
||||
system.perspective.print("Path not found: " + base_path)
|
||||
return {}
|
||||
|
||||
try:
|
||||
with open(json_file, "r") as fh:
|
||||
view_json = json.load(fh)
|
||||
except Exception:
|
||||
continue
|
||||
# --- Detect if this is a Chute ---
|
||||
is_chute = "/CHUTE/" in full_tag_path.upper()
|
||||
|
||||
# go one level deeper: root -> children[0] (coordinateContainer) -> its children
|
||||
root_children = (view_json.get("root") or {}).get("children") or []
|
||||
if not root_children:
|
||||
continue
|
||||
for view_folder in os.listdir(base_path):
|
||||
json_file = os.path.join(base_path, view_folder, "view.json")
|
||||
if not os.path.isfile(json_file):
|
||||
continue
|
||||
|
||||
container = root_children[0]
|
||||
children = container.get("children") or []
|
||||
try:
|
||||
with open(json_file, "r") as fh:
|
||||
view_json = json.load(fh)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
for child in children:
|
||||
props = child.get("props") or {}
|
||||
params = props.get("params") or {}
|
||||
tag_props = params.get("tagProps")
|
||||
root_children = (view_json.get("root") or {}).get("children") or []
|
||||
if not root_children:
|
||||
continue
|
||||
|
||||
if isinstance(tag_props, list) and len(tag_props) > 0:
|
||||
tag_prop = str(tag_props[0])
|
||||
parts = tag_prop.split("/")
|
||||
container = root_children[0]
|
||||
children = container.get("children") or []
|
||||
|
||||
if len(parts) > 1 and parts[1] == plc_name:
|
||||
|
||||
dev_name = parts[-1]
|
||||
if len(parts) > 3 and parts[-2] == clicked_name:
|
||||
dev_name = clicked_name + "_" + parts[-1]
|
||||
system.perspective.print(dev_name)
|
||||
for child in children:
|
||||
props = child.get("props") or {}
|
||||
params = props.get("params") or {}
|
||||
tag_props = params.get("tagProps")
|
||||
|
||||
# ONLY include devices that are children of clicked_name
|
||||
else:
|
||||
dev_name = parts[-1]
|
||||
prefix = clicked_name + "_"
|
||||
|
||||
if dev_name.startswith(prefix) or (len(parts) > 3 and parts[-2] == clicked_name):
|
||||
global_device_mapping[dev_name] = {
|
||||
"tagPath": tag_prop,
|
||||
"zone": view_folder
|
||||
}
|
||||
|
||||
return global_device_mapping
|
||||
if isinstance(tag_props, list) and len(tag_props) > 0:
|
||||
tag_prop = str(tag_props[0])
|
||||
parts = tag_prop.split("/")
|
||||
|
||||
except Exception as e:
|
||||
whid = "unknown"
|
||||
try:
|
||||
whid = system.tag.readBlocking("Configuration/FC")[0].value
|
||||
except:
|
||||
pass
|
||||
logger = system.util.getLogger("%s-build_device_mapping" % whid)
|
||||
exc_type, exc_obj, tb = sys.exc_info()
|
||||
logger.error("Error at line %s: %s" % (tb.tb_lineno, exc_obj))
|
||||
return {}
|
||||
if len(parts) > 1 and parts[1] == plc_name:
|
||||
dev_name = parts[-1]
|
||||
if len(parts) > 3 and parts[-2] == clicked_name:
|
||||
dev_name = clicked_name + "_" + parts[-1]
|
||||
else:
|
||||
dev_name = parts[-1]
|
||||
|
||||
prefix = clicked_name + "_"
|
||||
|
||||
# === 🟢 NEW: Chute FIOM match ===
|
||||
if is_chute and dev_name.startswith(clicked_name + "_"):
|
||||
global_device_mapping[dev_name] = {
|
||||
"tagPath": tag_prop,
|
||||
"zone": view_folder
|
||||
}
|
||||
continue
|
||||
|
||||
# === Default inclusion ===
|
||||
if dev_name.startswith(prefix) or (len(parts) > 3 and parts[-2] == clicked_name):
|
||||
global_device_mapping[dev_name] = {
|
||||
"tagPath": tag_prop,
|
||||
"zone": view_folder
|
||||
}
|
||||
|
||||
# === Special Case: JR Buttons ===
|
||||
elif "/JR/" in tag_prop.upper():
|
||||
try:
|
||||
jr_parts = tag_prop.split("/JR/")
|
||||
if len(jr_parts) > 1:
|
||||
sub_path = jr_parts[1]
|
||||
if sub_path.startswith(clicked_name + "_JR"):
|
||||
dev_name = sub_path.split("/")[0]
|
||||
global_device_mapping[dev_name] = {
|
||||
"tagPath": tag_prop,
|
||||
"zone": view_folder
|
||||
}
|
||||
except:
|
||||
pass
|
||||
|
||||
shared_jr_pe_map = {
|
||||
"S03_CH101": ["S03_1_JR1", "S03_1_LRPE1"],
|
||||
"S03_CH103": ["S03_1_JR1", "S03_1_LRPE1"],
|
||||
"S03_CH105": ["S03_1_JR1", "S03_1_LRPE1"],
|
||||
"S03_CH107": ["S03_1_JR3", "S03_1_LRPE3"],
|
||||
"S03_CH108": ["S03_1_JR4", "S03_1_LRPE4"],
|
||||
"S03_CH109": ["S03_1_JR3", "S03_1_LRPE3"],
|
||||
"S03_CH110": ["S03_1_JR4", "S03_1_LRPE4"],
|
||||
"S03_CH111": ["S03_1_JR3", "S03_1_LRPE3"],
|
||||
"S03_CH112": ["S03_1_JR2", "S03_1_LRPE2"],
|
||||
"S03_CH113": ["S03_1_JR5", "S03_1_LRPE5"],
|
||||
"S03_CH114": ["S03_1_JR6", "S03_1_LRPE6"],
|
||||
"S03_CH115": ["S03_1_JR5", "S03_1_LRPE5"],
|
||||
"S03_CH116": ["S03_1_JR6", "S03_1_LRPE6"],
|
||||
"S03_CH117": ["S03_1_JR5", "S03_1_LRPE5"],
|
||||
"S03_CH118": ["S03_1_JR6", "S03_1_LRPE6"],
|
||||
"S03_CH119": ["S03_1_JR7", "S03_1_LRPE7"],
|
||||
"S03_CH120": ["S03_1_JR8", "S03_1_LRPE8"],
|
||||
"S03_CH121": ["S03_1_JR7", "S03_1_LRPE7"],
|
||||
"S03_CH122": ["S03_1_JR8", "S03_1_LRPE8"],
|
||||
"S03_CH123": ["S03_1_JR7", "S03_1_LRPE7"],
|
||||
"S03_CH124": ["S03_1_JR8", "S03_1_LRPE8"],
|
||||
}
|
||||
shared_fiom_map = {
|
||||
"NCS1_1": ["S03_1_FIOM_5", "S03_1_FIOM_9", "S03_1_FIOM_1", "S03_1_FIOM_2","S03_1_FIOM_3","S03_1_FIOM_4", "S03_1_FIOM_6","S03_1_FIOM_7", "S03_1_FIOM_8"],
|
||||
}
|
||||
|
||||
if clicked_name in shared_jr_pe_map:
|
||||
extra_devices = shared_jr_pe_map[clicked_name]
|
||||
for dev in extra_devices:
|
||||
try:
|
||||
# Base tag (for PE)
|
||||
base_tag = "System/MCM02/Station/Chute_JR/" + dev
|
||||
# JR subtag (for JR button)
|
||||
jr_tag = base_tag + "/JR" if dev.endswith("JR1") else base_tag
|
||||
|
||||
for tag_candidate in [base_tag, jr_tag]:
|
||||
global_device_mapping[dev] = {
|
||||
"tagPath": tag_candidate,
|
||||
"zone": "Chute_JR"
|
||||
}
|
||||
except Exception as ex:
|
||||
system.perspective.print("Error adding JR/PE for {}: {}".format(clicked_name, ex))
|
||||
|
||||
if clicked_name in shared_fiom_map:
|
||||
for dev in shared_fiom_map[clicked_name]:
|
||||
tag_path = "System/{}/IO_Block/FIO/{}".format(plc_name, dev)
|
||||
global_device_mapping[dev] = {
|
||||
"tagPath": tag_path,
|
||||
"zone": "FIO"
|
||||
}
|
||||
|
||||
return global_device_mapping
|
||||
|
||||
except Exception as e:
|
||||
whid = "unknown"
|
||||
try:
|
||||
whid = system.tag.readBlocking("Configuration/FC")[0].value
|
||||
except:
|
||||
pass
|
||||
logger = system.util.getLogger("%s-build_device_mapping" % whid)
|
||||
exc_type, exc_obj, tb = sys.exc_info()
|
||||
logger.error("Error at line %s: %s" % (tb.tb_lineno, exc_obj))
|
||||
return {}
|
||||
|
||||
|
||||
|
||||
|
||||
# ======================================================
|
||||
# Device Table Builder
|
||||
# ======================================================
|
||||
def build_device_table(self):
|
||||
"""
|
||||
Converts global_device_mapping into a list of dictionaries:
|
||||
Keys: Device, Status
|
||||
Reads each tag value, falls back to 'Unknown' if error/null.
|
||||
"""
|
||||
rows = []
|
||||
state_mappings = {
|
||||
0: "Closed",
|
||||
1: "Actuated",
|
||||
2: "Communication Faulted",
|
||||
3: "Conveyor Running In Maintenance Mode",
|
||||
4: "Disabled",
|
||||
5: "Disconnected",
|
||||
6: "Stopped",
|
||||
7: "Enabled Not Running",
|
||||
8: "Encoder Fault",
|
||||
9: "Energy Management",
|
||||
10: "ESTOP Was Actuated",
|
||||
11: "EStopped",
|
||||
12: "EStopped Locally",
|
||||
13: "Extended Faulted",
|
||||
14: "Full",
|
||||
15: "Gaylord Start Pressed",
|
||||
16: "Jam Fault",
|
||||
17: "Jammed",
|
||||
18: "Loading Allowed",
|
||||
19: "Loading Not Allowed",
|
||||
20: "Low Air Pressure Fault Was Present",
|
||||
21: "Maintenance Mode",
|
||||
22: "Conveyor Stopped In Maintenance Mode",
|
||||
23: "Motor Faulted",
|
||||
24: "Motor Was Faulted",
|
||||
25: "Normal",
|
||||
26: "Off Inactive",
|
||||
27: "Open",
|
||||
28: "PLC Ready To Run",
|
||||
29: "Package Release Pressed",
|
||||
30: "Power Branch Was Faulted",
|
||||
31: "Pressed",
|
||||
32: "Ready To Receive",
|
||||
33: "Running",
|
||||
34: "Started",
|
||||
35: "Stopped",
|
||||
36: "System Started",
|
||||
37: "Unknown",
|
||||
38: "VFD Fault",
|
||||
39: "Conveyor Running In Power Saving Mode",
|
||||
40: "Conveyor Jogging In Maintenance Mode",
|
||||
41: "VFD Reset Required",
|
||||
42: "Jam Reset Push Button Pressed",
|
||||
43: "Start Push Button Pressed",
|
||||
44: "Stop Push Button Pressed",
|
||||
45: "No Container",
|
||||
46: "Ready To Be Enabled",
|
||||
47: "Half Full",
|
||||
48: "Enabled",
|
||||
49: "Tipper Faulted"
|
||||
}
|
||||
rows = []
|
||||
try:
|
||||
for dev_name, info in global_device_mapping.items():
|
||||
tagPath = info.get("tagPath", "")
|
||||
if not tagPath:
|
||||
continue
|
||||
|
||||
try:
|
||||
for dev_name, info in global_device_mapping.items():
|
||||
tagPath = info.get("tagPath", "")
|
||||
status_value = ""
|
||||
provider = "[" + self.session.custom.fc + "_SCADA_TAG_PROVIDER]"
|
||||
path = provider + tagPath + "/STATE"
|
||||
|
||||
if tagPath:
|
||||
try:
|
||||
result = system.tag.readBlocking([path])[0]
|
||||
status_value = state_mappings.get(result.value, "Unknown")
|
||||
except:
|
||||
status_value = "Unknown"
|
||||
provider = "[" + self.session.custom.fc + "_SCADA_TAG_PROVIDER]"
|
||||
rows.extend(read_device_status(tagPath, provider, dev_name))
|
||||
|
||||
# Append as dictionary
|
||||
rows.append({
|
||||
'Device': dev_name,
|
||||
'Status': status_value
|
||||
})
|
||||
|
||||
return rows
|
||||
|
||||
except Exception as e:
|
||||
system.perspective.print("Error building device table: %s" % e)
|
||||
return [] # Return empty list on error
|
||||
return rows
|
||||
|
||||
except Exception as e:
|
||||
system.perspective.print("Error building device table: %s" % e)
|
||||
return []
|
||||
|
||||
# ======================================================
|
||||
# Get All Tags for Clicked Device
|
||||
# ======================================================
|
||||
def getAllTags(self, tagPath, section="all"):
|
||||
"""
|
||||
Reads all tags under a UDT instance (recursively) and returns a list of dictionaries.
|
||||
"""
|
||||
Reads all tags under a UDT instance (recursively)
|
||||
and returns a list of dictionaries:
|
||||
[
|
||||
{"Name": "State", "OPC Path": "System/MCM01/...", "Value": "Running"},
|
||||
...
|
||||
]
|
||||
"""
|
||||
rows = []
|
||||
|
||||
Supports:
|
||||
- VFD (Drive folder)
|
||||
- Conveyor (skips Drive)
|
||||
- Chute (root + PE/PRX/EN tags)
|
||||
- Single Photoeyes (PE1/PE2)
|
||||
- Single Prox Sensors (PRX1/PRX2)
|
||||
- Enable buttons (EN_Color, EN_State, EN_Priority)
|
||||
- Tracking Photoeyes (TPE, handles both folder- and struct-style UDTs)
|
||||
"""
|
||||
try:
|
||||
providerPath = "[" + self.session.custom.fc + "_SCADA_TAG_PROVIDER]"
|
||||
driveFolderName = "Drive"
|
||||
|
||||
rows = []
|
||||
# === Utility: read a single atomic tag ===
|
||||
def readSingleTag(path, prefix=""):
|
||||
try:
|
||||
result = system.tag.readBlocking([providerPath + path])[0]
|
||||
value = str(result.value) if result.quality.isGood() else "Unknown"
|
||||
except:
|
||||
value = "Unknown"
|
||||
|
||||
try:
|
||||
providerPath = "[" + self.session.custom.fc + "_SCADA_TAG_PROVIDER]"
|
||||
driveFolderName = "Drive"
|
||||
displayName = prefix + path.split("/")[-1] if prefix else path.split("/")[-1]
|
||||
rows.append({
|
||||
"Name": displayName,
|
||||
"OPC Path": path,
|
||||
"Value": value
|
||||
})
|
||||
|
||||
# === Utility: recursive browse ===
|
||||
def browseRecursive(basePath, prefix=""):
|
||||
children = system.tag.browse(providerPath + basePath).getResults()
|
||||
for child in children:
|
||||
tagType = str(child.get("tagType", ""))
|
||||
name = str(child.get("name", ""))
|
||||
fullPath = str(child.get("fullPath", ""))
|
||||
|
||||
# === Utility: read a single atomic tag ===
|
||||
def readSingleTag(path, prefix=""):
|
||||
try:
|
||||
result = system.tag.readBlocking([providerPath + path])[0]
|
||||
value = str(result.value) if result.quality.isGood() else "Unknown"
|
||||
except:
|
||||
value = "Unknown"
|
||||
if fullPath.startswith("[") and "]" in fullPath:
|
||||
fullPath = fullPath.split("]", 1)[1]
|
||||
|
||||
displayName = prefix + path.split("/")[-1] if prefix else path.split("/")[-1]
|
||||
rows.append({
|
||||
"Name": displayName,
|
||||
"OPC Path": path,
|
||||
"Value": value
|
||||
})
|
||||
# --- Conveyor filter (skip Drive folder) ---
|
||||
if section == "conveyor" and name == driveFolderName:
|
||||
continue
|
||||
|
||||
# === Utility: recursive browse ===
|
||||
def browseRecursive(basePath, prefix=""):
|
||||
children = system.tag.browse(providerPath + basePath).getResults()
|
||||
if tagType == "Folder":
|
||||
# --- Skip JR subfolder if current device is LRPE ---
|
||||
if name.upper() == "JR" and "_JR" in basePath.upper():
|
||||
continue
|
||||
newPrefix = prefix + name + "/" if prefix else name + "/"
|
||||
browseRecursive(basePath + "/" + name, newPrefix)
|
||||
elif tagType == "AtomicTag":
|
||||
readSingleTag(fullPath, prefix)
|
||||
|
||||
for child in children:
|
||||
tagType = str(child.get("tagType", ""))
|
||||
name = str(child.get("name", ""))
|
||||
fullPath = str(child.get("fullPath", ""))
|
||||
# === MAIN ENTRY POINT ===
|
||||
|
||||
if fullPath.startswith("[") and "]" in fullPath:
|
||||
fullPath = fullPath.split("]", 1)[1]
|
||||
# --- Case 1: VFD ---
|
||||
if section == "vfd":
|
||||
drivePath = tagPath + "/" + driveFolderName
|
||||
browseRecursive(drivePath)
|
||||
|
||||
# --- Conveyor filter (skip Drive folder) ---
|
||||
if section == "conveyor" and name == driveFolderName:
|
||||
continue
|
||||
# --- Case 2: Flat EN_ tags (Chutes) ---
|
||||
elif tagPath.upper().endswith("/EN"):
|
||||
parentPath = "/".join(tagPath.split("/")[:-1])
|
||||
children = system.tag.browse(providerPath + parentPath).getResults()
|
||||
for child in children:
|
||||
tagType = str(child.get("tagType", ""))
|
||||
name = str(child.get("name", ""))
|
||||
if tagType == "AtomicTag" and name.upper().startswith("EN_"):
|
||||
fullPath = str(child.get("fullPath", ""))
|
||||
if fullPath.startswith("[") and "]" in fullPath:
|
||||
fullPath = fullPath.split("]", 1)[1]
|
||||
readSingleTag(fullPath)
|
||||
|
||||
if tagType == "Folder":
|
||||
newPrefix = prefix + name + "/" if prefix else name + "/"
|
||||
browseRecursive(basePath + "/" + name, newPrefix)
|
||||
elif tagType == "AtomicTag":
|
||||
readSingleTag(fullPath, prefix)
|
||||
# --- Case 3: Single Sensors (PE/PRX) ---
|
||||
elif tagPath.upper().endswith(("PE1", "PE2", "PRX1", "PRX2")) and "/TPE/" not in tagPath.upper():
|
||||
readSingleTag(tagPath)
|
||||
|
||||
# === MAIN ENTRY POINT ===
|
||||
if section == "vfd":
|
||||
# Browse only inside Drive folder
|
||||
drivePath = tagPath + "/" + driveFolderName
|
||||
browseRecursive(drivePath)
|
||||
# --- Case 4: Default / Fallback ---
|
||||
else:
|
||||
browseResult = system.tag.browse(providerPath + tagPath).getResults()
|
||||
|
||||
elif tagPath.upper().endswith("/EN"):
|
||||
# --- Handle flat EN_ tags ---
|
||||
parentPath = "/".join(tagPath.split("/")[:-1])
|
||||
children = system.tag.browse(providerPath + parentPath).getResults()
|
||||
if not browseResult:
|
||||
# Possibly a struct-style UDT (like some TPEs)
|
||||
system.perspective.print("Empty browse for {}, checking struct value...".format(tagPath))
|
||||
try:
|
||||
result = system.tag.readBlocking([providerPath + tagPath])[0]
|
||||
value = result.value
|
||||
|
||||
for child in children:
|
||||
tagType = str(child.get("tagType", ""))
|
||||
name = str(child.get("name", ""))
|
||||
if tagType == "AtomicTag" and name.upper().startswith("EN_"):
|
||||
fullPath = str(child.get("fullPath", ""))
|
||||
if fullPath.startswith("[") and "]" in fullPath:
|
||||
fullPath = fullPath.split("]", 1)[1]
|
||||
readSingleTag(fullPath)
|
||||
# === Expand STRUCT ===
|
||||
if isinstance(value, dict):
|
||||
system.perspective.print("Detected STRUCT value, expanding {}".format(tagPath))
|
||||
|
||||
elif tagPath.upper().endswith(("PE1", "PE2", "PRX1", "PRX2")) and "/TPE/" not in tagPath.upper():
|
||||
# --- Single sensors ---
|
||||
readSingleTag(tagPath)
|
||||
def flattenStruct(struct, base=""):
|
||||
for k, v in struct.items():
|
||||
newName = base + "/" + k if base else k
|
||||
if isinstance(v, dict):
|
||||
flattenStruct(v, newName)
|
||||
else:
|
||||
rows.append({
|
||||
"Name": newName,
|
||||
"OPC Path": tagPath + "/" + newName,
|
||||
"Value": str(v)
|
||||
})
|
||||
|
||||
else:
|
||||
# --- Default path ---
|
||||
browseResult = system.tag.browse(providerPath + tagPath).getResults()
|
||||
flattenStruct(value)
|
||||
|
||||
if not browseResult:
|
||||
# Possibly a struct-style UDT (like some TPEs)
|
||||
system.perspective.print("Empty browse for {}, checking struct value...".format(tagPath))
|
||||
try:
|
||||
result = system.tag.readBlocking([providerPath + tagPath])[0]
|
||||
value = result.value
|
||||
else:
|
||||
# Not a struct, read normally
|
||||
readSingleTag(tagPath)
|
||||
|
||||
# If we got a STRUCT, expand it into sub-rows
|
||||
if isinstance(value, dict):
|
||||
system.perspective.print("Detected STRUCT value, expanding {}".format(tagPath))
|
||||
except Exception as ex:
|
||||
system.perspective.print("Fallback read failed for {}: {}".format(tagPath, ex))
|
||||
|
||||
def flattenStruct(struct, base=""):
|
||||
for k, v in struct.items():
|
||||
newName = base + "/" + k if base else k
|
||||
if isinstance(v, dict):
|
||||
flattenStruct(v, newName)
|
||||
else:
|
||||
rows.append({
|
||||
"Name": newName,
|
||||
"OPC Path": tagPath + "/" + newName,
|
||||
"Value": str(v)
|
||||
})
|
||||
else:
|
||||
# Normal browse case
|
||||
browseRecursive(tagPath)
|
||||
|
||||
flattenStruct(value)
|
||||
else:
|
||||
# Not a struct, just read it normally
|
||||
readSingleTag(tagPath)
|
||||
except Exception as ex:
|
||||
system.perspective.print("Fallback read failed for {}: {}".format(tagPath, ex))
|
||||
else:
|
||||
# Normal case — browse folder/UDT structure
|
||||
browseRecursive(tagPath)
|
||||
return rows
|
||||
|
||||
return rows
|
||||
|
||||
except Exception as e:
|
||||
system.perspective.print("Error in getAllTags: {}".format(e))
|
||||
return []
|
||||
except Exception as e:
|
||||
system.perspective.print("Error in getAllTags: {}".format(e))
|
||||
return []
|
||||
|
||||
@ -9,9 +9,9 @@
|
||||
"attributes": {
|
||||
"lastModification": {
|
||||
"actor": "admin",
|
||||
"timestamp": "2025-11-05T10:10:15Z"
|
||||
"timestamp": "2025-11-06T09:07:05Z"
|
||||
},
|
||||
"hintScope": 2,
|
||||
"lastModificationSignature": "509aec4ea42ad044944f9f2f53d5e610bd5fdb0ed852c6d392f7a94816fa6cd9"
|
||||
"lastModificationSignature": "1aab25cf2caca967d0c6fc4a2cbc161a07f54ef8faf7c2675b94d53bb2e289b9"
|
||||
}
|
||||
}
|
||||