Displaying Recorded Tag Values Into Tables With Scripting

It's entirely possible.

I'd do it a bit differently than what you had in mind though:

  1. have a section with device/tests selection, and a button to start the tests
  2. in script, run the tests and build a dataset
  3. assign this dataset to a table in a different section. Maybe even in a popup or different window.

So as the script runs, it will snapshot the results into a dataset which will then be displayed in a table.

I guess I can use some if statements in the script to handle which devices are active. How will the table handle it if I want to run the script multiple times? Would I need to have multiple datasets or would it just override the existing one?

You will either need to store the original dataset in a custom property (or another place) or it will be overwritten. You will need to overwrite the tables dataset for it to display in the table.

I'd first build a list of devices and tests to be used, then there's no if needed. Just loop through your lists.

If you want to save the results of multiple runs, you could make several datasets, though I'm not sure of what's the best way of doing that in vision.
Or, you could extend your dataset, and use only parts of it for display, based on some input (maybe a dropdown ?), using system.dataset.filterColumns in scripts or columnRearrange in expressions for example.

So this is what I have so far for one iteration of the script.

In the large middle portion is where I want to read the values from the tags listed out in the columns and then write them to the corresponding row of a dataset but the User Manual pages are not super clear on how to create the initial dataset.

It seems that once I create the first dataset, any time a set a value of that dataset to a new value it will just create a new dataset and overwrite the original one. What I can't determine is if I need to create a tag that the dataset will write to in order to bind that dataset to a table.

I also assume I will need to write a lines in the script for each cell that will look at the value in the tag and then write that value into the corresponding cell.

Ok so I figured out how to create a Dataset Memory tag which I then bound to the table. Now the question is just how to get those values all written to that dataset and only for the tags that are currently present and enabled.

I'm also looking at this table from the dataset and I'm thinking I might need a separate configuration area where I choose which columns and rows to include in my main dataset.

What I'm envisioning now is a page where you check TRUE or FALSE on the list of devices that you need in each column and row then then write those specific values to the columns and rows on the dataset which is shown in the table.

Then you run the test itself which will write values to the intersecting cells of the rows and columns of the dataset generated by the previous page.

I'm also seeing that I will need to reverse my values in the intersecting cells. When the value of the BOOL tag is FALSE, I want the cell to have a filled in checkbox or an X so that it will match the reference spreadsheet.

I think my main issue is not understanding the syntax enough to make the scripts do what I want them to do.

Do you want this data to be recorded to be able to report on? If so, youll want to record it into a database table instead

Ps don't read or write single tags at a time, pad lists into the read/write function to execute on them all at once, it's a lot more efficient

1 Like

A few pointers:

  • You don't need to work with a dataset the whole time. You can use basic python lists, and when you're done build a dataset from those. It's much easier.
  • Try to avoid reading/writing tags one at a time. Batch it instead. As much as possible, build a list of tags to read, then read everything. Build a list of tags to write, then write everything.
tagspaths = [
	"tag/path/1",
    "tag/path/2",
    "tag/path/3",
]

values = [qval.value for qval in system.tag.readBlocking(tagspaths)]
# or
val1, va2, val3 = [qval.value for qval in system.tag.readBlocking(tagspaths)]

values = [1, 2, 3]
system.tag.writeBlocking(tagspaths, values)
  • Don't post screenshot of code, post the code and format it with the preformatted text tool (or directly surround it with triple backticks). You can even specify what language it is to enable syntax highliting:
    ```python
    # code
    ```
  • Why did you write var1 = value = tagRead() ? You're not using that value variable anywhere.
  • Why are you storing the dataset in a tag ?
2 Likes

fwiw, perhaps overkill, but.. I use a class to add tags to write to. It handles write failures by raising an exception which is one of my main reasons for using it. It makes it super simple to add tag/value pairs though and then to write them all with error handling.

"""
Example usage:
tag_writer = TagWriter()
# add tags and the values to write into the object, one-by-one
tag_writer.add('Path/To/Tag1', False)
tag_writer.add('Path/To/Tag2', 'Simon Says')

# or
# add tags and the values to write into the object when first instatiated
tag_writer = TagWriter(['Path/To/Tag1', 'Path/To/Tag2'], [False, 'Simon Says'])

# add tagpath/value lists after creating
tag_write.add(['Path/To/Tag3', 'Path/To/Tag4'], [1,5])

# write the stored values to the tagpaths
tag_writer.write()
"""

class TagWriteException(Exception):
	pass

class TagWriter:
	""" Simplifies writing to multiple tagpaths by providing an object to add tagpath(s) and their value(s) to write, and later write them all at once. """
	def __init__(self, tagpaths=None, values=None):
		if tagpaths is None:
			self.tag_paths_to_write = []
			self.tag_vals_to_write = []
		else:
			self.tag_paths_to_write = tagpaths
			self.tag_vals_to_write = values
	
		
	def add(self, tagpaths, values):
		if isinstance(tagpaths, (list, tuple)):
			self.tag_paths_to_write.extend(tagpaths)
			self.tag_vals_to_write.extend(values)
		elif isinstance(tagpaths, (unicode, str)):
			self.tag_paths_to_write.append(tagpaths)
			self.tag_vals_to_write.append(values)
		else:
			raise IllegalArgumentException('Invalid args passed. These must either be lists, tuples, or single string and value.')
	
	
	def __str__(self):
		return '\r\n'.join(['{} -> {}'.format(tagpath, value) for tagpath, value in zip(self.tag_paths_to_write, self.tag_vals_to_write)])
	
	
	def __repr__(self):
		return 'TagWriter({}, {})'.format(self.tag_paths_to_write, self.tag_vals_to_write)
	
	
	def write(self, removeSuccessful=False):
		results = system.tag.writeBlocking(self.tag_paths_to_write, self.tag_vals_to_write)
		
		if removeSuccessful:
			remove_indices = set(i for i, result in enumerate(results) if result.good)
			self.tag_paths_to_write = [tagpath for i, tagpath in enumerate(self.tag_paths_to_write) if i not in remove_indices]
			self.tag_vals_to_write = [tagpath for i, tagpath in enumerate(self.tag_vals_to_write) if i not in remove_indices]
			
		if any(not result.good for result in results):
			raise TagWriteException('Failed to write to tag(s) with results:\r\n{}'.format('\r\n'.join(['"{}" ==> {}'.format(tagpath, result) for tagpath, result in zip(self.tag_paths_to_write, results) if not result.good])))
		else:
			True

8 Likes

Do we know enough about XY to update the thread title to be more descriptive yet?

That's a nice way to do that. removeSuccessful so that you can attempt a .write again on only the faulty tags?

I have to ask though what is this doing

else:
    True

Absolutely nothing ;p

I'm not a big fan of classes, so if you're after something 'similar' with functions... I use something like this:

def write_tags(paths, values):
	qvals = system.tag.writeBlocking(paths, values)
	failed_write_paths = [path for path, qval in zip(paths, qvals) if qval.bad]
	if failed_write_paths:
		log("failed to write to {}".format(failed_write_paths))
	return failed_write_paths

And just build the lists instead of adding to a TagWriter

1 Like
  • Don't post screenshot of code, post the code and format it with the preformatted text tool (or directly surround it with triple backticks). You can even specify what language it is to enable syntax highliting:

Noted, thanks.

  • Why did you write var1 = value = tagRead() ? You're not using that value variable anywhere.

I didn't put the line in there but at the end of the test it will need to write the value back below the set point in order to do a reset.

  • Why are you storing the dataset in a tag ?

Just because it was the only way I was able to figure it out offhand. I figured it would be easier to create a dataset tag and manually enter my rows and columns, then just edit the intersecting cells with the script.

I couldn't see where to link a dataset to the table in any other way. The IU video on scripting and datasets was not super informative.

I would use a SQL database that we have set up in the office; however, the guys in the field moving around from site to site do not have access to any databases so I need to try to do it all self-contained in the Vision client.

So the potential issue I see with this is that I don't want to manually assign the values to the tags. I want to record into the table the values that the tags currently are showing from my logic.

I still don't see why you need 2 variables for this. Especially when you're overwriting one without using it before.

Can you clarify what happens/what do you want to happen when the tests are ran several times ?

So currently, an operator will go to a device's window on the HMI and put the device into "Commissioning Mode" which allows him to override the process value coming from a transmitter in order to simulate values and induce shutdowns and alarms.

The operator will manually enter that value into the HMI, trigger a shutdown, navigate to several other pages to view if the correct parts of the site are shutdown and that the correct permissives have been pulled.

Assuming everything worked as intended, the operator will then go back to the device page, enter a value to bring the process value back into the clear where it doesn't trip any alarms or shutdowns, then press a Master Reset button. The operator will then reopen any necessary valves or start processes back from the HMI and move on to the next device to test.

This process takes a long time when you are considering that there are close to 50 devices to test as well as a minimum 15 processes and permissives to check for each shut down. Some site have fewer devices and processes while some have more.

I am trying to automate this entire process so that an operator can simply pull up Vision, enter in which devices he wants to test, and push the button that will do that entire process for him.

It will put the device into commissioning mode, drive the value above the shutdown set point, record and display the current values for all the appropriate tags that represent the permissive and processes he needs to verify the status of, then bring the value for the device back down into normal operating range, reset, then reopen the site.

Then it will do the same for the rest of the devices.

I'm not aiming for the script to work quickly as this will all still take time because we are dealing with physical devices and valves, etc.

The main goal of this is to reduce the amount of time the operator has to spend manually inputting values and navigating screens in the HMI, which will reduce the downtime for the site.

I already have scripts that drive the values for the devices up then down based on the shutdown setpoints, conduct a reset, then reopen the site.

The missing piece is recording all the relevant tag values and displaying them in one table on the screen in between raising and lowering the device values.

That's a good description, but I was asking about this:

What do you want to happen if the script is ran multiple times ?

I already answered that question myself earlier. I was asking if the table would update with new values with the script was run a second time.