# Set one tag value to another tag's value on valueChanged event

**URL:** <https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682>\
**Category:** Ignition\
**Tags:** ignition81, perspective, scripting\
**Created:** [July 20, 2023, 8:35pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682 "2023-07-20T20:35:17Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ian.sebryk](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ian.sebryk/32/49319_2.png) [@ian.sebryk](https://forum.inductiveautomation.com/u/ian.sebryk)\
**Post date:** [July 20, 2023, 8:35pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/1 "2023-07-20T20:35:17Z")

</div>

we have a dataset tag whose value is from an MQTT broker. power goes out, value lost. we need to set a stored value and re-write it to the tag when it comes back online. we ALSO need to set it when the user changes the value manually. so i am thinking of targeting the `onValueChange` value event on the 'stored' dataset to force a write back to the MQTT tag:

```python
def valueChanged(blah-blah): # i copied baseline composition from existing stuff
    if '[.]Online': #<--- the tag at the 'base' level of the Tag list
        '[.]../../silenceTime'.value = currentValue #<--- both this tag and 
            # the tag this is bound to are at the same nested Tag list level

```

is this a sound strategy or is there a better way?

EDIT: i learned some stuff and am rethinking this. instead of scripting either of the two tags, i am going to script the 'Online' tag's 'valueChange' event instead. which is actually more accurate. so currently, it looks like this:

```python
def valueChanged(blah-blah):
    if not currentValue: #<--- this is a boolean: online --> yes/no
        '[.]../../silenceTime'.value = '[.]../../silenceTimeStored'.value

```

so when the unit comes back online, boom; data gets written across.

is this good or is there a better way yet?

---

<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:** [July 20, 2023, 9:23pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/2 "2023-07-20T21:23:59Z")

</div>

Be sure to filter against the `initialChange` flag, or this script will execute anytime the project is saved (or for any other event that causes a resubscription).

Also, `currentValue` is a `qualifiedValue` so you'll want to check the value property otherwise, this would actually execute on every value change.

```python
def valueChange(blah-blah):
    if not any(initialChange,currentValue.value):
        system.tag.writeAsync(['[.]../../silenceTime'],system.tag.readBlocking(['[.]../../silenceTimeStored'])[0].value)

```

---

<div class="post-metadata">

**Author:** ![ian.sebryk](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ian.sebryk/32/49319_2.png) [@ian.sebryk](https://forum.inductiveautomation.com/u/ian.sebryk)\
**Post date:** [July 20, 2023, 9:37pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/3 "2023-07-20T21:37:05Z")

</div>

okay. excellent! thank you! quick final question: why is `initialChange` on it's own, but `currentValue` requires `.value` appended?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [July 20, 2023, 9:38pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/4 "2023-07-20T21:38:58Z")

</div>

`initialChange` is a bare boolean. `currentValue` is a `QualifiedValue` with `value`, `quality`, and `timestamp` properties.

---

<div class="post-metadata">

**Author:** ![ian.sebryk](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ian.sebryk/32/49319_2.png) [@ian.sebryk](https://forum.inductiveautomation.com/u/ian.sebryk)\
**Post date:** [July 20, 2023, 9:42pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/5 "2023-07-20T21:42:51Z")

</div>

ah. gotcha. thank you. 🙂 and, again, for my OCD brain: i **cannot** directly address a value, aka `'[.]Online'.value`, i have to use `system.tag.read(or)readBlocking('[.]Online')[0]).value`

EDIT: okay. i flight-tested this as best i could. here's the structure:  
 ![Screenshot 2023-07-20 154436](https://us1.discourse-cdn.com/inductiveautomation/original/3X/0/8/0806c880808b5eb465b5d12f94a972fd7305ed61.png)

i placed the `valueChanged` script from @lrose on the Online tag, and another on the silenceTimeStored tag. when i change either of the two values... nothing. what is going on?

Online `valueChanged script:

```python
def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):
	#
	# when the panel comes back online, write out the values to the MQTT Broker
	#
	if currentValue.value or not initialChange:
		print('-------------> online!')
		system.tag.writeAsync(['[.]silenceTime'], system.tag.readBlocking(['[.]../../silenceTimeStored'])[0].value)
	else:
		print('offline! <-------------')

```

the silenceTimerStored `valueChanged` script:

```python
def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):
	if readBlocking(['[.]../../silenceTimeStored'])[0].value:
		system.tag.writeAsync(['[.]silenceTime'], system.tag.readBlocking(['[.]../../silenceTimeStored'])[0].value), system.tag.readBlocking(['[.]../../silenceTimeStored'])[0].value)
	else:
		system.tag.writeAsync(['[.]silenceTime'],'OFFLINE') #<--- for testing feedback purposes

```

---

<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:** [July 20, 2023, 9:46pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/6 "2023-07-20T21:46:38Z")

</div>

In a script, you can not directly address a value.

This: `'[.]Online'` is just a string.

Technically, `system.tag.read()` will work but it's deprecated.

There are two other functions you can use to read tag values depending on if you need it to block or not.

`system.tag.readBlocking()`  
`system.tag.readAsync()`

And they return a `List` of `qualifiedValues` so you must index into them as such and access the value property.

This post will give you a quick history lesson on this, if you're interested.

> [@Python List Comprehensions Help](https://forum.inductiveautomation.com/t/python-list-comprehensions-help/56621/43):
>
> I think I can help clarify this a little, although, this is just my outsider understanding of the issue and why I think the change was a good one. In 7.9 there were 6 functions that worked with the tag system system.tag.read() system.tag.readAll() system.tag.write() system.tag.writeAll() system.tag.writeSyncronous() system.tag.writeAllSyncronous() This was confusing for some users who expected similarly named functions to behave the same. For instance, system.tag.write() is an \*asynchronou…

---

<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:** [July 20, 2023, 9:53pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/7 "2023-07-20T21:53:27Z")

</div>

Where are you looking for the print out?

---

<div class="post-metadata">

**Author:** ![ian.sebryk](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ian.sebryk/32/49319_2.png) [@ian.sebryk](https://forum.inductiveautomation.com/u/ian.sebryk)\
**Post date:** [July 20, 2023, 9:54pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/8 "2023-07-20T21:54:37Z")

</div>

Output Console in Designer. but that's the least of my concerns. the scripts aren't firing on value change. period. i should see those changes in the tag browser.

---

<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:** [July 20, 2023, 9:55pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/9 "2023-07-20T21:55:55Z")

</div>

Tag change scripts execute in the gateway scope. Print will output to the Wrapper log.

---

<div class="post-metadata">

**Author:** ![ian.sebryk](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ian.sebryk/32/49319_2.png) [@ian.sebryk](https://forum.inductiveautomation.com/u/ian.sebryk)\
**Post date:** [July 20, 2023, 9:57pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/10 "2023-07-20T21:57:19Z")

</div>

i also have to stop editing my earlier post with updated content. that's an old habit from another forum. sorry! 😛

---

<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:** [July 20, 2023, 9:57pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/11 "2023-07-20T21:57:24Z")

</div>

> [@ian.sebryk](#):
>
> i should see those changes in the tag browser

I agree, unless something isn't the value that you think it is. If you look in the wrapper log and see `'offline! <-------------'` that will give you some info.

---

<div class="post-metadata">

**Author:** ![ian.sebryk](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ian.sebryk/32/49319_2.png) [@ian.sebryk](https://forum.inductiveautomation.com/u/ian.sebryk)\
**Post date:** [July 20, 2023, 9:57pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/12 "2023-07-20T21:57:56Z")

</div>

hell, at this point, i'll take _any_ value! 🙂 as long as it changes. then work from there.

---

<div class="post-metadata">

**Author:** ![ian.sebryk](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ian.sebryk/32/49319_2.png) [@ian.sebryk](https://forum.inductiveautomation.com/u/ian.sebryk)\
**Post date:** [July 20, 2023, 10:00pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/13 "2023-07-20T22:00:37Z")

</div>

i have to punch out, but let's pick this up tomorrow. i really need to nail this down. for work and for sanity. 😃 thanks for your help so far.

---

<div class="post-metadata">

**Author:** ![WillMT10](https://avatars.discourse-cdn.com/v4/letter/w/7feea3/32.png) [@WillMT10](https://forum.inductiveautomation.com/u/WillMT10)\
**Post date:** [July 20, 2023, 11:54pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/14 "2023-07-20T23:54:00Z")

</div>

> [@ian.sebryk](#):
>
> `system.tag.writeAsync(['[.]silenceTime'], system.tag.readBlocking(['[.]../../silenceTimeStored'])[0].value)`

Based on your screenshot, your relative tag paths are incorrect. You're showing the tags are at the same nesting level within the UDT yet you have `[.]silenceTime` and `[.]../../silenceTimeStored`. Should be:

```auto
system.tag.writeAsync(['[.]silenceTime'], system.tag.readBlocking(['[.]silenceTimeStored'])[0].value)

```

Is the `silenceTime` memory tag an integer or string datatype? If it's an integer, this line will fail:

> [@ian.sebryk](#):
>
> `system.tag.writeAsync(['[.]silenceTime'],'OFFLINE')`

Looks like you accidentally called `system.tag.readBlocking(['[.]../../silenceTimeStored'])[0].value)` twice in your `silenceTimeStored` `valueChanged` script:

> [@ian.sebryk](#):
>
> `system.tag.writeAsync(['[.]silenceTime'], system.tag.readBlocking(['[.]../../silenceTimeStored'])[0].value), system.tag.readBlocking(['[.]../../silenceTimeStored'])[0].value)`

---

<div class="post-metadata">

**Author:** ![ian.sebryk](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ian.sebryk/32/49319_2.png) [@ian.sebryk](https://forum.inductiveautomation.com/u/ian.sebryk)\
**Post date:** [July 24, 2023, 4:16pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/15 "2023-07-24T16:16:47Z")

</div>

the copy/paste monkeys did me no favours, for sure. i was cobbling this together from three sources. regardless, you're correct: the pathing was wrong; they were all on the same level.

also: i keep forgetting the 'Java' part of Jython: strictly typed vars. yes: `OFFLINE` will not work. thanks for that catch.

so thank you for fixing all my slapdash errors. 👍 having corrected them, this is now chugging as desired. 💯

---

<div class="post-metadata">

**Author:** ![WillMT10](https://avatars.discourse-cdn.com/v4/letter/w/7feea3/32.png) [@WillMT10](https://forum.inductiveautomation.com/u/WillMT10)\
**Post date:** [July 24, 2023, 4:45pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/16 "2023-07-24T16:45:50Z")

</div>

> [@ian.sebryk](#):
>
> `if readBlocking(['[.]../../silenceTimeStored'])[0].value:`

I noticed this one after the others I sent and didn't want to pile on all at once, but you're also missing `system.tag.` for calling the `readBlocking()` function. (Couldn't _not_ say something...had to scratch that itch 😅)

---

<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:** [July 24, 2023, 5:46pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/17 "2023-07-24T17:46:08Z")

</div>

I will also say, that (especially with tag value change scripts) you should always read (and write for that matter) as many tags as possible in a single go. The `readBlocking()` accepts and returns a list for exactly this reason. It is exponentially faster to read multiple tags at once then to execute multiple reads.

The reason is that all tag valueChange events execute in a deditcated thread pool of size 3, IIRC. This means that long running scripts have the opportunity to exhaust the thread pool, leading to missed events. @pturmel recommends that any tag valueChange script execute in \< 100ms, ideally in the tens of ms, I believe. This is why generally it is recommended to utilize Gateway Tag Change Events which do not have this limitation.

I use this script to put all of the tags into a dictionary for simple readable access.

```auto
def createTabValueDict(tagPaths,parentPath):
    return {tagName[len(parentPath):]:qValue.value for tagName,qValue in zip(tagPaths,systemtag.readBlocking(tagPaths))}

```

I use this script to write the values back.

```auto
def writeTagValueDict(values,parentPath):
    parentPath = parentPath if parentPath[-1:] == '/' else parentPath + '/'
    writePaths, writeValues = [list(x) for x in zip(*values.items())]
    longWritePaths = [parentPath + path for path in writePaths]'
    return system.tag.writeBlocking(longWritePaths,writeValues)

```

Utilize them like this:

```auto
tagValues = createTagValueDict(['path/to/tag1','path/to/tag2'],'path/to/')

tag1Value = tagValues['tag1']
tagValues['tag2'] = 'new value'

writeTagValueDict(tagValues,'path/to/')

```

---

<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:** [July 24, 2023, 7:37pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/18 "2023-07-24T19:37:08Z")

</div>

> [@lrose](#):
>
> @pturmel recommends that any tag valueChange script execute in \< 100ms, ideally in the tens of ms

Close.

- I recommend that **all** events, gateway and UI, unless carefully set up to only handle **one** somewhat rare condition, execute in an _average_ of 100ms or less. An occasional excursion is generally tolerable. Humans are particularly sensitive to responses greater than 0.1 second.

- I recommend that **tag** events, defined on a tag, not in a project, run in **single digit milliseconds** , and **no excursions**. If a large system (tens of thousands of tags with scripts), even less than that, or use the setting in `ignition.conf` to change the thread pool.

This means no database operations in **tag** events. NONE. And no network operations--no web API calls and no access to **network file systems**. (People forget that one.) No **blocking** tag reads to remote tag providers, and no **blocking** OPC tag writes or blocking tag writes to remote tag providers.

---

<div class="post-metadata">

**Author:** ![ian.sebryk](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ian.sebryk/32/49319_2.png) [@ian.sebryk](https://forum.inductiveautomation.com/u/ian.sebryk)\
**Post date:** [July 24, 2023, 7:58pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/19 "2023-07-24T19:58:09Z")

</div>

@pturmel how would i clock those calls? it could explain some of the bog we get...

besides `time.time()`... 😃 unless that's literally the answer...

---

<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:** [July 24, 2023, 8:06pm UTC](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682/20 "2023-07-24T20:06:26Z")

</div>

Somewhere in a project script module that is included in your Gateway Scripting Project, include:

```auto
from java.lang import System

```

That makes it possible, in your tag events, to use something like this:

```auto
def valueChanged(....):
	start_ts = gwscript.System.nanoTime()
	.....
	... Do work
	.....
	nanos = gwscript.System.nanoTime() - start_ts
	if nanos > 10000000L:
		system.util.getLogger("TagEventExcursions").warnf("Tag Event *somename* Overtime! %dns", nanos)

```

Then filter (or grep) your gateway log to see these.

Java's [System.nanoTime()](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/System.html#nanoTime()) is made specifically for this kind of duty--efficient, and tightly synchronized across multiple cores in a system.

[Next page](https://forum.inductiveautomation.com/t/set-one-tag-value-to-another-tags-value-on-valuechanged-event/76682.md?page=2)
