Ignition Edge - Writing a Tag Value to An OPC tag

Hello,

I need to send year, month, day, hours, minutes, seconds to a modbus gateway devcie (protocal converter). The data needs to be sent as seperate registers for each element.

I have created a series of expression tags that generate the individual elements using the "get*" Expression funtion.

My problem is trying to write the individual expression tags to their associated OPC tags. I am slowly coming up to speed on scripting in general but I am kind of stuck.

Simply put, I am trying to write a script generated tag value to a seperate OPC tag assigned to a 16UINT holding register. I know I can script it but I was hoping that someone would have a go by or something I could follow that was representative of the "normal" way of accomplishing this task.

Also, I have tried the value change script in the expression tags but I may not have been formatting my script correctly.

Please show what you have tried. Post your code, highlight it then press the </> button. This will let us take a closer look at what you have, and guide you to a better answer. :slight_smile:

That said I will post how I go about it.

  • One tag called Now that has the expression now()
  • value changed script. Not the use of currentValue.value. currentValue and previousValue are qualified values that have three parts to it: value, quality, and timestamp. This can be a bit of a stumbling block to new users.
def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):
	
	now = currentValue.value
	
	# Make a list of the tags you wish to write to.
	timeTags = ['[Test]Time Parts/Year',
	            '[Test]Time Parts/Month',
	            '[Test]Time Parts/Day',
	            '[Test]Time Parts/Hour',
	            '[Test]Time Parts/Minute',
	            '[Test]Time Parts/Second'
	           ]

	# Make a list of values to write
	timeValues = [now.year + 1900,
	              now.month + 1,
	              now.date,
	              now.hours,
	              now.minutes,
	              now.seconds
	             ]

	# Write the values to the tags
	system.tag.writeBlocking(timeTags, timeValues)

image

1 Like

Here is my work:

STEP #1

I created a series of expression tags for BMS_Health. CurentDate, CurrentSecond, CurrentMinute, CurrentHour, CurrentDay, CurrentMonth & CurrentYear. I also created a tag for a togglng heart beat bit called SCADA_HEARTBEAT.

Capture

In each of "Current" tags listed above I created the following expression:

get*(now())

where the * is the value I needed for the tag (second, minute, hour, etc...). For the tag CurrentDate, I used the following exprpession:

{[System]Gateway/CurrentDateTime.value}

STEP #2

In order to generate the toggling 1/0 heartbeat bit, I created the following Value Event, Value Changed script in the CurrentSecond tag:

def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):
	
		
	toggleBit = system.date.getSecond(system.date.now())%2
	tagPaths = "[edge]BMS_Health/000_SCADA_HEARTBEAT.value"
	tagValues = [toggleBit]
	system.tag.writeBlocking(tagPaths, tagValues)

This takes the current seconds, multiples by modulus 2, then writes the result to variable toggleBit. The rest I copied out of an example in the manuals.

This appeared to work fine as the heartbeat toggles between 0 and 1 in onse second intervals.

STEP #3

I then attempted to apply a similar function to the Value Event, Value Changed script in the CurrentYear tag as follows:

def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):

		tagPaths = "[edge]BMS_Health/007_SCADA_YEAR.value"
		tagValues = getYear(now())
		system.tag.writeBlocking(tagPaths, tagValues)

This did not work as the 007_SCADA_YEAR tag does not change.

This is the point at which I tried to understand exactly what is happening in the Value Change/Quality Changed scripting tool but became pretty lost as the manuals were not detailed enough for someone with little experience with this function. Case in point:

I get that we are defining a funtion called "valueChanged" with the arguments (tag, tagPath, previousValue, currentValue, initialChange, missedEvents) but I dont understand how the system knows what value we assign to the argument currentValue because we haven't specified the value. Also, when you and the manual state that " currentValue.value. currentValue and previousValue are qualified values that have three parts to it: value, quality, and timestamp" I have a difficult time understanding what that means and what use is it to me.

Admittedly, I understood that writes to multiple tags are possible but since I couldn't get it to work with one tag let alone multiple, I wanted to keep things simple. As inefficient as the coding is for now.

Hopefully this clarifies my thought processes a little.

No worries. When time permits, go through the free training videos at Inductive University. It covers a lot of topics.

It looks like you are trying to use expression language in a Jython script. They're not the same thing, so let's take a step back, and I'll be pedantic for a bit. :wink:

The currentValue is exactly what is soundls like: the current value of the tag when it detects a change. previousValue is what it was before it changed.
EDIT: These are automatically assigned when the event fires.

The three parts of a qualified value:

  • value: the data associated with the qualified value. In most cases this is the only thing you'll be interested in (the date, of seconds, or minutes, etc.)
  • quality: how good the data is. For instance, you can read an OPC tag and get the last value, but the quality is bad because you've lost connection to the device.
  • timestamp: when the tag changed.

So, your current script can change to:

def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):

	tagPaths = ["[edge]BMS_Health/007_SCADA_YEAR"]
	tagValues = [currentValue.value]
	system.tag.writeBlocking(tagPaths, tagValues)

Note that, whie you could forego the brackets (the brackets denote a list of values) EDIT: for single tags and values, it's good to get into the habit of always including them. You only have to remember one way of doing it, and it always works.

Hope this helps clear up some things.

2 Likes

@JordanCClark

Update #1:

I replicated your suggestion including the creation of new memory tags for the date elements and a new expression tag for resloving the current date.

Capture

The Expression for the Date tag:

now()

I then created the following Value Event/Value Changed Script in the Date tag:


def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):

	now = currentValue.value
			
	# Make a list of the tags you wish to write to.
	timeTags = ['[edge]BMS_Health/Year',
		        '[edge]BMS_Health/Month',
		        '[edge]BMS_Health/Day',
		        '[edge]BMS_Health/Hour',
		        '[edge]BMS_Health/Minute',
		        '[edge]BMS_Health/Second',
		        ]
		
	# Make a list of values to write
	timeValues = [now.year + 1900,
		          now.month + 1,
		          now.date,
		          now.hours,
		          now.minutes,
		          now.seconds
		          ]
		
	# Write the values to the tags
	system.tag.writeBlocking(timeTags, timeValues)	

With this I was able to make your solution work as expected based on your response. I wasn't that far off in my method although I used individual expresion tags for my date element tags which is inefficient at best.

I have the following follow up questions:

QUESTION #1

When I created the expression tag for current date, I used:

{[System]Gateway/CurrentDateTime.value}

When you created a similar tag, you used:

now()

They both seem to produce the same result. What is the difference? Is one prefered over the other?

QUESTION #2

Having successfully written the Value Change script to write to MEMORY tags, I next needed to get it to write to OPC tags. So I created the following OPC tags:

Capture

and I made the followig modification to the Value Change script in my Date Expression tag:

def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):

	now = currentValue.value
			
	# Make a list of the tags you wish to write to.
#	timeTags = ['[edge]BMS_Health/Year',
#		        '[edge]BMS_Health/Month',
#		        '[edge]BMS_Health/Day',
#		        '[edge]BMS_Health/Hour',
#		        '[edge]BMS_Health/Minute',
#		        '[edge]BMS_Health/Second',

	timeTags = ['[edge]006_SCADA_YEAR',
		        '[edge]005_SCADA_MONTH',
		        '[edge]004_SCADA_DAY',		        
		        '[edge]003_SCADA_HOUR',
		        '[edge]002_SCADA_MINUTE',		        		        
		        '[edge]001_SCADA_SECOND',
		        '[edge]000_SCADA_HEARTBEAT',
		        ]
		
	# Make a list of values to write
	timeValues = [now.year + 1900,
		          now.month + 1,
		          now.date,
		          now.hours,
		          now.minutes,
		          now.seconds,
		          now.seconds % 2
		          ]
		
	# Write the values to the tags
	system.tag.writeBlocking(timeTags, timeValues)	

This results in the required date elements being successfully written to the OPC tags and I can see that the values get updated in the modbus registers of my modbus gateway.

My question, is this the best way of doing this? Or am I missing a more efficient method? Should I be writing to the memory tags first and then write their values to the OPC tags?

QUESTION #3 (really a statement)

I misunderstod the way the modbus gateway manufacturer intended the heart beat bit to work. Their actual intention is for the SCADA to be setting the heartbeat bit to "1" and then the modbus gateway will reset the value to "0" about a second after it see the bit go high. SCADA is supposed to monitor the value of the register and check to see if the bit goes low. If it doesn't, then SCADA is supposed to assume communication with the gateway is lost. So I'm going to have to investigate how to handle this now.

I wouldn't call it that. I would say helpful instead. The way I think and the way the manual is written do not line up sometimes so if some pedanticism fills in the gaps then I'll happily listen.

Just easier for me to remember. Since this is running on the gateway, it amounts to the same thing. If it was running on, for instance, a vision client, it would be the time of the clock on wherever the client is running. [System]Gateway/CurrentDateTime will always assure you're using the gateway time.

Yes, IMO. All of the tags get processed, and will successfully change back if you manually change one of the OPC values. It also gives you one spot to maintain your code. There's no need to write to memory tags first. I just did that as an example, as I don't have a device here to play with.

1 Like

Understood, it's relative but since I always want to reference the gateway time for these values, using the full reference is just good form in this case.

I wasnt sure if I should becasue I saw this in the manual:

"The state of memory tags are stored inside the gateway's internal database. This allows the tag and its value to be retained across Gateway restarts."

But I guess, in my particular instance it doesnt really matter as the tags will just get updated on a gateway restart anyway.

Thanks for all the help!

1 Like