Am I going about this in the wrong way

I wanted to make a library view that I could quickly drop inside of a tab view and simply add a param to define a WellNumber. I initially tried to find a way to bind a time series chart data to a view.params.WellNumber using a Tag History. But I found no way to do something like this inside of Expressions: {[default]AllenBradley/Controller:Global/W{view.params.WellNumber}_Well_Pressure}. So I made a script in my library which builds all the tags from the param and fills in 8 graphs when a DateTime Input is changed.

That all works great. Where I am now stumped is I would like there to be a Real Time toggle button. If it is true, I’d like to count say 10 seconds and then run the query function and load the data again. I see no way of achieving this repeating function timer though.

Is there a better way of doing this or is this kind of functionality not possible? I would think it was possible because other browser javascript tools have found a way to setup repeating timer functions like ReactQuery.

You should be using the runScript expression to run your library script. The second parameter is pollRate which you can use to make it run repeatedly at a given interval.

1 Like

None of what you are attempting should require a script.

Create an array custom property on the view(make its access private). Each value in the array should be a dictionary with the members tagPath and aggregationMode. For each tag path, add the following expression: "[default]AllenBradley/Controller:Global/W" + {view.params.WellNumber} + "_Well_NeededItemHere".
Adjust NeededItemHere to be whatever tag you want from the well. Doing that should give you an array of objects that are accepted by a tag history binding.

From there, feed that array to the expression of a tag history binding on another custom property, and then bind that historical data to a time series chart. Tag history bindings have a poll rate that is configurable, so you could set it to 10s.

If you had the Integration Toolkit module installed you could have 2 custom properties, baseTagPath and requiredWellMembers, and use those to create the array automatically with an expression of

forEach(
   {view.custom.requiredWellMembers},
   asMap(
       "tagPath",  {view.custom.baseTagPath} + {view.params.wellNumber} + it(),
       "aggregationMode", "MinMax"
   )
)

Additionally, your Ignition tag names should not be the same as your PLC tag path. Rename and organize them in a way that benefits your UI design. Use UDTs and/or folders to group accordingly, regardless of where the tags exist in the PLC.

2 Likes

runScript in a runAction is giving me a NameError: global name 'runScript' is not defined.

runScript is an expression, not a script function.

1 Like

Have you already gone through or at least skimmed IU? I feel like you're laser focused on "I have this one problem and I believe the solution to it is X", but you might need to take a step back and re-evaluate.

4 Likes

Your way sounds like the most logical and makes it as automated as possible so someone else just has to enter baseTagPath and requiredWellMembers like you mentioned.

I’ll try this and report back if I got it to work.

So bind each chart to run a script with inputs for tag built by concat the vision.params with the rest of that tag format, beginningTime, endTime, and the status of real time toggle? This seems less than straight forward. Probably doable though.

I’ve tried a couple of ways. I didn’t see anything on IU that seemed to cover this in the Core. Chatgpt recommended something like ryan.white suggested, but it recommended make the key: value object for each graph each time. So I tried it with a way I came up with. It pretty much works except when I wanted to implement the ability to add polling. Otherwise my method worked great. I could probably change it to an Update button that runs the script.

My view could be dropped into another view and only needed 1 input and would work with all of our tags.

How would I take into account the real time toggle button to change Tag History from Realtime or historical?

Always run in historical mode and bump the span start and end at your desired rate. You can have 2 additional custom properties, one with an expression of now(10000) and one with an expression of addHours({self.view.custom.currentTs}, -6) or similar(adjust to your desired span).

If you want to pause the time, you can tweak the first custom property to be now(if({self.view.custom.enableRealtime}, 10000, 0)). Be warned this will still execute once on transition to stopped.

Feed these two properties to the start and end dates of the tag history binding. It might be worth it to truncate any milliseconds off those two properties to keep start and end times on exact seconds. Something like addMillis({view.custom.key}, -1 * getMillis({view.custom.key})) should work.

1 Like

I’ve gotten pretty close with this solution. The only issue I’ve ran into is that forEach returns a list. So when I go to my timeseries graphs to do a tag history binding, I bind it to 0, 1, 2, 3, etc. I’ve tried a couple of different things. The closest I got was:

forEach(
	{view.params.tagInfo}, 
	asMap(it()["tagEnding"], asMap(
			"tagPath", {view.params.baseTagPath} + {view.params.WellNumber} + "/" + it()["tagEnding"],
			"agg", it()["agg"],
			"type", it()["type"]
		)
	)
)

this results in a list with a [key: {key: value, key: value}], but it’s still a list that’s accessed 0, 1, 2, etc.

I’ve been trying to get tagData: {key: {key: value, key: value}, etc }. I thought the solution might be orderBy, but when I tried:

orderBy(
	{view.params.tagInfo}, 
	asMap(it()["tagEnding"], asMap(
			"tagPath", {view.params.baseTagPath} + {view.params.WellNumber} + "/" + it()["tagEnding"],
			"agg", it()["agg"],
			"type", it()["type"]
		)
	)
)

I get an ExpressionEval error.

orderBy() doesn't construct new mapping objects, it just filters a list into a shorter list. Its arguments after the source data are conditional expressions.

Is there anyway to create an object {key: {key: value, key: value} } or should I just bind my graphs to the list and be careful to never reorder the tag data?

Building on the original example I gave:

asMap(
	asPairs(
		forEach(
			{view.custom.requiredWellMembers},
			asList(
				it(),
				asMap(
				   "tagPath",  {view.custom.baseTagPath} + {view.params.wellNumber} + it(),
				   "aggregationMode", "MinMax"
				)
			)
		)
	)
)

This is assuming you are using the required members as the driving key for the map. Otherwise you'll have to change the first it() after asList to something that will yield a unique key for each tag

Application Result

a source array of

[
  "valueA",
  "valueB",
  "valueC",
  "valueD"
]

yields

{
  "valueB": {
    "tagPath": "tagPathvalueB",
    "aggregationMode": "MinMax"
  },
  "valueA": {
    "tagPath": "tagPathvalueA",
    "aggregationMode": "MinMax"
  },
  "valueD": {
    "tagPath": "tagPathvalueD",
    "aggregationMode": "MinMax"
  },
  "valueC": {
    "tagPath": "tagPathvalueC",
    "aggregationMode": "MinMax"
  }
}
1 Like

Yes that did it. Now I am able to bind my graphs to custom tagData.