# Dynamic Array/Dataset Tag to determine max value

**URL:** <https://forum.inductiveautomation.com/t/dynamic-array-dataset-tag-to-determine-max-value/99670>\
**Category:** Ignition\
**Created:** [December 11, 2024, 12:11am UTC](https://forum.inductiveautomation.com/t/dynamic-array-dataset-tag-to-determine-max-value/99670 "2024-12-11T00:11:55Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![oceandweller](https://avatars.discourse-cdn.com/v4/letter/o/4bbf92/32.png) [@oceandweller](https://forum.inductiveautomation.com/u/oceandweller)\
**Post date:** [December 11, 2024, 12:11am UTC](https://forum.inductiveautomation.com/t/dynamic-array-dataset-tag-to-determine-max-value/99670/1 "2024-12-11T00:11:55Z")

</div>

I have a situation where I need to alter a list based on separate bool tags to determine max/min values. Ex. Tags: (PIT1, PIT2, PIT3) floats; (EN1, EN2, EN3) bools.  
If EN1 and EN3 are true then add PIT1 and PIT3 into a list that is used with Max and Min function. I have tried adding rows to a dataset tag, which is triggered with a value change any of the EN tags, but it doesn't seem to work right. I get one tag to copy and thats it. Any suggestions? New path I can take?

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [December 11, 2024, 1:20am UTC](https://forum.inductiveautomation.com/t/dynamic-array-dataset-tag-to-determine-max-value/99670/2 "2024-12-11T01:20:51Z")

</div>

> [@oceandweller](#):
>
> I get one tag to copy and thats it.

Sounds like a race condition, where multiple EN bits are coming on in the same time frame and the last write to the dataset tag is winning.

Are the PIT tags' values constantly changing and you are trying to pull the value the entire time that the EN tag is on for that pit?

If you are allowed to add modules to your system, I would install Automation Pros' [Integration Toolkit](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/179). Its completely free and adds a lot of functionality, including iteration in expressions.

This toolkit has a `tags()` expression function that accepts a list or dataset of tag paths and will poll that list at a set rate and return all the values of the tags in a single dataset. It accepts dynamically building the source tag list so it may do what you are looking for.

We are doing something similar to you but we are using it for monitoring voltages. We have a driving numeric value that tells an expression how many voltages we want to monitor and the expression builds the list of tag paths for the `tags()` function. We then look at the highest and the average of the returned dataset.

You would have 1 memory tag containing the paths to all the EN bits and their associated PIT tags, and 3 expression tags, all of which would be a dataset type. One expression tag would use `tags` to monitor all the values from the EN bits, one to build the list of value (PIT) tags to monitor and another that monitors all the selected PIT tags.

Your first tag would be a dataset with two string columns, "enableTagPath and valueTagPath".

```python
"#NAMES"
"enableTagPath","valueTagPath"
"#TYPES"
"str","str"
"#ROWS","1"
"[provider]Path/To/Some/EN/Bit","[provider]Path/To/Some/PIT/Value"

```

Your second tag (1st expression tag) would have an expression of

```python
unionAll(
	asMap("path", "str", "value", "b"),
	tags(
		unionAll(
			asMap("path", "str"),
			forEach(
				{Path/to/memory/tag},
				asMap("path", it()[0])
			)
		)
	)
)

```

Your third tag (second expression tag) would have an expression of

```python
unionAll(
	asMap("path", "str"),
	forEach(
		where(
			forEach(
				{path/to/expression/tag/one},
				if(
					it()[1] = 1,
					lookup(
						{path/to/memory/tag},
						it()[0], Null,
						"enableTagPath",
						"valueTagPath"
						), Null
					)
				), !isNull(it())
			),
		asMap("path", it())
	)
)

```

Your final expression tag would have an expression of

```python
unionAll(
	asMap("path", "str", "value", "f"),
	if(
		len({path/to/second/expression/tag}) > 0,
		tags({path/to/second/expression/tag}), asList()
	)
)

```

This would be the tag that you do all of your min/max/average calculations on.

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [December 11, 2024, 12:51pm UTC](https://forum.inductiveautomation.com/t/dynamic-array-dataset-tag-to-determine-max-value/99670/3 "2024-12-11T12:51:40Z")

</div>

> [@oceandweller](#):
>
> add PIT1 and PIT3 into a list that is used with Max and Min function.

I'm not sure exactly what this means, so a little explanation on what you're attempting to accomplish and what your true end goal is would be helpful.

If you can add the Integration Toolkit, then you should absolutely go that way. If not, then a script like this would build the list you're looking for.

```python
tagPaths = ['PIT1','PIT2','PIT3','EN1','EN2','EN3']
qvList = system.tag.readBlocking(tagPaths)
floats = qvList[:len(qvList)/2]
enables = qvList[len(qvList)/2:]

minMaxList = [qv.value for i,qv in enumerate(floats) if enables[i].value]

```

EDIT: Corrected Missing syntax in script.

---

<div class="post-metadata">

**Author:** ![oceandweller](https://avatars.discourse-cdn.com/v4/letter/o/4bbf92/32.png) [@oceandweller](https://forum.inductiveautomation.com/u/oceandweller)\
**Post date:** [December 11, 2024, 6:42pm UTC](https://forum.inductiveautomation.com/t/dynamic-array-dataset-tag-to-determine-max-value/99670/4 "2024-12-11T18:42:00Z")

</div>

> [@ryan.white](#):
>
> Are the PIT tags' values constantly changing and you are trying to pull the value the entire time that the EN tag is on for that pit?

Yes, the values of the PIT are constantly changing. Thats the real importance to this. Ex. Batch 1 is run and PIT1-3 are used, so EN1-3 are true, then PIT1-3 will be monitored. By looking at the min and max of those PITs the operators know nothing is lagging behind. If PIT2 drops way down (noticed by a big difference between min and max) then the operators can save the batch, if caught in time.

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [December 11, 2024, 7:23pm UTC](https://forum.inductiveautomation.com/t/dynamic-array-dataset-tag-to-determine-max-value/99670/5 "2024-12-11T19:23:38Z")

</div>

> [@oceandweller](#):
>
> Yes, the values of the PIT are constantly changing. Thats the real importance to this.

Okay, in that case my suggestion using the Integration Toolkit will work, as it pulls in the values at a set rate.

If you aren't able to add that module, then you would need to set up a gateway timer script to execute the script from lrose at a set rate (1s or 5s would probably be fast enough).

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [December 11, 2024, 7:27pm UTC](https://forum.inductiveautomation.com/t/dynamic-array-dataset-tag-to-determine-max-value/99670/6 "2024-12-11T19:27:08Z")

</div>

> [@oceandweller](#):
>
> can save the batch, if caught in time.

Consider creating an expression tag that compares the delta between min and max to a setpoint or limit of some sort and has an alarm configured on it. That way you can make it more noticeable to the operator.

---

<div class="post-metadata">

**Author:** ![oceandweller](https://avatars.discourse-cdn.com/v4/letter/o/4bbf92/32.png) [@oceandweller](https://forum.inductiveautomation.com/u/oceandweller)\
**Post date:** [December 11, 2024, 7:35pm UTC](https://forum.inductiveautomation.com/t/dynamic-array-dataset-tag-to-determine-max-value/99670/7 "2024-12-11T19:35:26Z")

</div>

End goal is to analyze enabled instruments. Its a recipe situation, so each run has different instruments enabled. We need to make sure the instruments stay close in value, so operators watch min vs max values of enabled instruments.

---

<div class="post-metadata">

**Author:** ![oceandweller](https://avatars.discourse-cdn.com/v4/letter/o/4bbf92/32.png) [@oceandweller](https://forum.inductiveautomation.com/u/oceandweller)\
**Post date:** [December 11, 2024, 7:56pm UTC](https://forum.inductiveautomation.com/t/dynamic-array-dataset-tag-to-determine-max-value/99670/8 "2024-12-11T19:56:01Z")

</div>

I'm working on the script, from lrose, to see if I can see correct values and I'm getting "object is not iterable" error on the last line. Not sure. I assume its indicating the list is not iterable.

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [December 11, 2024, 8:04pm UTC](https://forum.inductiveautomation.com/t/dynamic-array-dataset-tag-to-determine-max-value/99670/9 "2024-12-11T20:04:26Z")

</div>

> [@lrose](#):
>
> `minMaxList = [qv.value for i,qv in floats if enables[i]]`

change that to

```python
minMaxList = [qv.value for i,qv in enumerate(floats) if enables[i].value]

```

Edit: Added `.value` to `enables[i]` so it actually pulls the boolean value, not the qualified value object.

> [@oceandweller](#):
>
> I assume its indicating the list is not iterable.

It was trying to iterate through a `qualifiedValue` object, which obviously does not allow this.

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [December 11, 2024, 8:15pm UTC](https://forum.inductiveautomation.com/t/dynamic-array-dataset-tag-to-determine-max-value/99670/10 "2024-12-11T20:15:10Z")

</div>

Oops. Corrected original script.

Thanks,

---

<div class="post-metadata">

**Author:** ![oceandweller](https://avatars.discourse-cdn.com/v4/letter/o/4bbf92/32.png) [@oceandweller](https://forum.inductiveautomation.com/u/oceandweller)\
**Post date:** [December 11, 2024, 8:46pm UTC](https://forum.inductiveautomation.com/t/dynamic-array-dataset-tag-to-determine-max-value/99670/11 "2024-12-11T20:46:50Z")

</div>

Great, thanks for working that out!

---

<div class="post-metadata">

**Author:** ![oceandweller](https://avatars.discourse-cdn.com/v4/letter/o/4bbf92/32.png) [@oceandweller](https://forum.inductiveautomation.com/u/oceandweller)\
**Post date:** [December 11, 2024, 8:47pm UTC](https://forum.inductiveautomation.com/t/dynamic-array-dataset-tag-to-determine-max-value/99670/12 "2024-12-11T20:47:27Z")

</div>

Thanks for the ideas. Its been very appreciated.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [December 11, 2024, 10:44pm UTC](https://forum.inductiveautomation.com/t/dynamic-array-dataset-tag-to-determine-max-value/99670/13 "2024-12-11T22:44:40Z")

</div>

> [@ryan.white](#):
>
> as it pulls in the values at a set rate.

That's not quite right, fwiw. The `tags()` function accepts updates from the tag subscriptions it sets up, and holds them briefly so that other updates coming back-to-back will yield a single expression re-execution. That default is 5ms, but can be up to 1 second. If deliberately set to zero, then the tags are read without subscribing, and only when something **else** makes the expression execute again.
