Power Chart Operator Experience

Using the Perspective Power Chart for the first time. I like the ease of adding the database and all the tags show up. However, all the tags in the tag browser are named using equipment info (a little cryptic for the operator).

Best-case-scenario I would be able to set the display name for the tag browser somehow. At the very least, I need to be able to change the name of the pen from the historical tag selected.

How might I go about changing the names displayed for the tags? Most of the tags are UDTs with a "Name" parameter that could be particularly useful, but all of the UDTs have the same inner tags (of course). So, adding 4 different tags as pens with the same name "output" makes it rather difficult to know what's what and manually editing the pen names every time is quite tedious.

Would be nice if there was something like an onPenAdded() script so I could update the name of the added pen with the correct name from the UDT.

I see the pens/0/data/source property is the historical tag path. Perhaps this could be converted to the tag browser path to get the name from the UDT and use a change script on the pens object.

This isn't exactly what you are asking for, but my solution to this issue was to create the pens JSON object with a script where I decided what to have the label set as and lots of other parameters. I resorted to completely disabling the tag browser on the power chart as I had a similar issue where the tag names themselves were cryptic and not readable for the everyday operator. What I would do is run this script whenever an operator clicked on a value on a given view and then when they opened the power chart the pens that would be used would be the 'currentTrends' session object.

def addTrend(tagPath, currentTrends, label):
	tagPath = tagPath.lower()
	SplitPath = tagPath.split("/")
	tagName = SplitPath[-1]
	
	count = 0
	for i in range(0, len(currentTrends)):
		 if currentTrends[i].name[:-3] == label:
		 	count += 1
			
	interpolation = "curveStepAfter"
	breakLine = True
	axis = ""
	if tagName in ['active']:
		breakLine = False
		axis = "Default_1"
	elif tagName in ['sp', 'position_manual', 'flow_target', 'speed_max_sp', 'speed_min_sp', 'lvl_start_sp', 'lvl_stop_sp', 'low_flow_alarm_sp']:
		breakLine = False
	
	label += " #" + str(count)
	
	colors = ['#4363d8', '#f58231', '#800000', '#b391d9', '#000075', '#a9a9a9', '#edd115', '#000000']
	
	if len(currentTrends) < len(colors):
		color = colors[len(currentTrends)]
	else:
		color = "#%06x" % random.randint(0, 0xA0A0A0)

	jsonObject = {
	  "name": label,
	  "visible": True,
	  "enabled": True,
	  "selectable": True,
	  "axis": axis,
	  "plot": 0,
	  "display": {
	    "type": "line",
	    "interpolation": interpolation,
	    "breakLine": breakLine,
	    "radius": 3,
	    "styles": {
	      "normal": {
	        "stroke": {
	          "color": color,
	          "width": 1,
	          "opacity": 0.8,
	          "dashArray": 0
	        },
	        "fill": {
	          "color": color,
	          "opacity": 0.8
	        }
	      },
	      "highlighted": {
	        "stroke": {
	          "color": color,
	          "width": 1,
	          "opacity": 1,
	          "dashArray": 0
	        },
	        "fill": {
	          "color": color,
	          "opacity": 1
	        }
	      },
	      "selected": {
	        "stroke": {
	          "color": color,
	          "width": 1,
	          "opacity": 1,
	          "dashArray": 0
	        },
	        "fill": {
	          "color": color,
	          "opacity": 1
	        }
	      },
	      "muted": {
	        "stroke": {
	          "color": color,
	          "width": 1,
	          "opacity": 0.4,
	          "dashArray": 0
	        },
	        "fill": {
	          "color": color,
	          "opacity": 0.4
	        }
	      }
	    }
	  },
	  "data": {
	    "source": "histprov:...:default:/tag:"+tagPath.lower(),
	    "aggregateMode": "default"
	  }
	}
	currentTrends.append(jsonObject)
	return currentTrends

How you decide to name your tags labels is up to you. Here I have some easy translations, but I could just as easily pull a 'name' tag like you are suggesting.

Having trouble figuring out example what this statement does.
Care to enlighten me?

It indexes to a specific hex value in the colors list based off of the length of currentTrends. currentTrends I guess is intended to be analogous to the number of pens. If the number of trends(pens) is greater than the number of fixed colors provided then it generates a random color between black and what ever color 0xA0A0A0 is.

Do you want to change the name of the tag in the tag browser or the displayed name on the power chart?

Yes, as Irose mentioned, it just assigns a fixed hex color value to a pen from the list. the colors in the list are just preset colors that all have good contrast so they would graph well together. For instances where the number of pens is greater than the size of the list of colors that I have hardcoded here, it will create new random colors.

Any idea how many pens can be configured before it starts taking too many resources? I've got 158 tags being trended currently. Would it be too much to have all of the pens on the power chart?

I would say that is way too many pens to have on a single plot and still make since of them, and multiple sub plots would probably look pretty crazy even with 10 pens per plot.

Performance wise, you would have the potential to crush the gateway. It wouldn't take much for the number of points to grow out of control.

We have several users that routinely requests a full days worth of data for 15-20 pens and they have complained of it "being" slow, but I remind them how much data their really asking for and that staves them off for a bit.

When I said "on the power chart" I should've been more clear. Not displayed all at once (I agree this would be quite ridiculous), I meant selectable at the bottom of the chart.

I don't think that the data is actually processed unless the pen is actively being charted, so other than needing to (potentially) scroll through the list, I don't know of a reason this would really impact performance.

Perhaps it's where the script was placed. It was on the chart's onStartup event. That seemed to bog down the gateway quite a bit. I'm guessing it should be in the view's onStartup event.

It slows down my preview view significantly when all of the pens are added, whether the script is done on the view startup or the chart startup.

This is my script to get all pens that have history enabled. Each tag that has history enabled has a custom string property Trend_Label

def getTrendPens():
	data = system.db.runNamedQuery("getHistoryTags")
		
	if data is not None:
		historyTagPaths = data.getColumnAsList(0)
		
		trendLabelPaths = [historyTag+".Trend_Label" for historyTag in historyTagPaths]
	
		
		trendLabels_qv = system.tag.readBlocking(trendLabelPaths)
	
		trendLabels = [label.value for label in trendLabels_qv]
		
		colors = ["#66FFFF","#B266FF","#FF3333", "#66FF66", "#000099", "#FF66B2", "#CCCC00", "#000000", "#606060" ]
		
		pens = []
		
		for i, pen in enumerate(trendLabels):
			if pen is not None:
				color = colors[i % len(colors)]
				#print pen
				#print historyTagPaths[i]
				jsonObject = {
						
								  "name": pen,
								  "visible": False,
								  "enabled": True,
								  "selectable": True,
								  "axis": "",
								  "plot": 0,
								  "display": {
								    "type": "line",
								    "interpolation": "curveLinear",
								    "breakLine": True,
								    "radius": 3,
								    "styles": {
								      "normal": {
								        "stroke": {
								          "color": color,
								          "width": 1,
								          "opacity": 0.8,
								          "dashArray": 0
								        },
								        "fill": {
								          "color": color,
								          "opacity": 0.8
								        }
								      },
								      "highlighted": {
								        "stroke": {
								          "color": color,
								          "width": 1,
								          "opacity": 1,
								          "dashArray": 0
								        },
								        "fill": {
								          "color": color,
								          "opacity": 1
								        }
								      },
								      "selected": {
								        "stroke": {
								          "color": color,
								          "width": 1,
								          "opacity": 1,
								          "dashArray": 0
								        },
								        "fill": {
								          "color": color,
								          "opacity": 1
								        }
								      },
								      "muted": {
								        "stroke": {
								          "color": color,
								          "width": 1,
								          "opacity": 0.4,
								          "dashArray": 0
								        },
								        "fill": {
								          "color": color,
								          "opacity": 0.4
								        }
								      }
								    }
								  },
								  "data": {
								    "source": "histprov:MSSQLSERVER1:/drv:ignition-scada-pc:default:/tag:"+historyTagPaths[i],
								    "aggregateMode": "default"
								  }
						
							}
				pens.append(jsonObject)
				
	return pens

I don't think the script takes that long to run (it executes pretty much immediately in the script console), I think it just has to do with the way the power chart is handling it.

When I hover over the chart after adding the pens, the value for all of the pens in the x-trace shows up as if the data was being displayed.

Ideally I would like to change it in the power chart's tag browser. But I'd settle for hiding the browser and just adding all the pens as selectable pens at the bottom of the chart, provided that it doesn't cause any other issues. At the moment, that approach is way too slow.

As far as I know there isn’t any “easy” access to the browser in the component.

I’m not in a place where I can do any testing, in a few days I might be able to get back around to this. Sorry I can’t provide more timely assistance.

This is what I have come up with:

  1. Use an expression tag to hold a dataset of the history tag source paths and the friendly names (custom tag prop) via an expression binding that calls a script to get all tags with history enabled (and the custom tag prop).
  2. Bind this with a script transform to the options array of a dropdown (multi-select enabled).
  3. Use a change script on the dropdown value prop to populate a custom array prop (value_pens) of the pen JSON objects from the dataset.
  4. Bind the power chart pens prop to the custom dropdown prop.
  5. Hide the power table's tag browser and pen browser.

  1. Expression tag and script:
runScript("trendPens.getTrendList()")
def getAllTrendPens():
	data = system.db.runNamedQuery("getHistoryTags")
		
	if data is not None:
		historyTagPaths = data.getColumnAsList(0)
		
		trendLabelPaths = [historyTag+".Trend_Label" for historyTag in historyTagPaths]
	
		
		trendLabels_qv = system.tag.readBlocking(trendLabelPaths)
	
		trendLabels = [label.value for label in trendLabels_qv]
		
		colors = ["#66FFFF","#B266FF","#FF3333", "#66FF66", "#000099", "#FF66B2", "#CCCC00", "#000000", "#606060" ]
		
		pens = []
		
		for i, pen in enumerate(trendLabels):
			if pen is not None:
				color = colors[i % len(colors)]
				#print pen
				#print historyTagPaths[i]
				jsonObject = {
						
								  "name": pen,
								  "visible": True,
								  "enabled": True,
								  "selectable": True,
								  "axis": "",
								  "plot": 0,
								  "display": {
								    "type": "line",
								    "interpolation": "curveLinear",
								    "breakLine": True,
								    "radius": 3,
								    "styles": {
								      "normal": {
								        "stroke": {
								          "color": color,
								          "width": 1,
								          "opacity": 0.8,
								          "dashArray": 0
								        },
								        "fill": {
								          "color": color,
								          "opacity": 0.8
								        }
								      },
								      "highlighted": {
								        "stroke": {
								          "color": color,
								          "width": 1,
								          "opacity": 1,
								          "dashArray": 0
								        },
								        "fill": {
								          "color": color,
								          "opacity": 1
								        }
								      },
								      "selected": {
								        "stroke": {
								          "color": color,
								          "width": 1,
								          "opacity": 1,
								          "dashArray": 0
								        },
								        "fill": {
								          "color": color,
								          "opacity": 1
								        }
								      },
								      "muted": {
								        "stroke": {
								          "color": color,
								          "width": 1,
								          "opacity": 0.4,
								          "dashArray": 0
								        },
								        "fill": {
								          "color": color,
								          "opacity": 0.4
								        }
								      }
								    }
								  },
								  "data": {
								    "source": "histprov:MSSQLSERVER1:/drv:ignition-scada-pc:default:/tag:"+historyTagPaths[i],
								    "aggregateMode": "default"
								  }
						
							}
				pens.append(system.util.jsonEncode(jsonObject))
				
	return pens
	
def getTrendList():

	headers = ["Name", "Pen"]

	pens = trendPens.getAllTrendPens()
	
	names = [system.util.jsonDecode(pen)["name"] for pen in pens]
	
	data = [[name,pen] for name, pen in zip(names, pens)]
		
	dataset = system.dataset.toDataSet(headers, data)
	
	return dataset
  1. Tag binding transform:
def transform(self, value, quality, timestamp):
	data = system.dataset.toPyDataSet(value)
	
	options = [{"label":row["Name"], "pen": row["Pen"]} for row in data]
		
	sortedData = sorted(options, key=lambda d: d['label'])
	
	return [dict(item, **{'value':index}) for index, item in enumerate(sortedData)]
  1. Value change script:
def valueChanged(self, previousValue, currentValue, origin, missedEvents):
	if currentValue.value is None:
		self.custom.value_pens = None
	else:
		pens = [system.util.jsonDecode(self.props.options[value.value].pen) for value in currentValue.value]
			
		self.custom.value_pens = pens

Now I just need to figure out why my dropdown modal popup won't allow me to scroll...

1 Like