Renaming Vision Template Parameters Removes Corresponding Template Instance Parameter Values

I am trying to rename some template parameters on a Vision template that has many instances of it in my project. When I rename the parameter, however, all of the parameter values and bindings that were associated with the renamed parameter are lost.

Is there any way to prevent this behavior from happening?

No. To do what you're describing automatically, editing a template would have to 'reach out' to every single window in your project, open it up, check whether it has an instance of that template, then rename the property and binding (and assume that it succeeds). On large projects, that could take many minutes and would absolutely kill designer performance as we open literally hundreds of windows.

2 Likes

Thanks for the reply!

With that limitation in mind, do you know of any good ways to accomplish renaming a template parameter and somehow restoring the bindings?

Is there a way to do this via the underlying xml that is copied when you copy a Vision component or have you heard of any tools created by others to help with this type of task?

I am trying to avoid needing to manually transfer the parameters and bindings of each template instance, if possible, as that would likely take me days…

In theory, you could do this via XML editing. If you're on a new enough 8.1 version, change the project properties to store window configuration in XML format, then open -> save every window in your project. The XML format is not documented and not particularly human readable, but if you're lucky and your "from" parameter name you're trying to replace is unique in the window you could use a programmatic find/replace on all the files to make changes in bulk.

Take lots of backups before you start, because it's quite easy to break the XML representation in such a way that you cannot open the resource in the designer at all anymore.

1 Like

I would manipulate each window's interaction controller object with a designer script. (Use the SDK javadocs for guidance.) Not trivial, but likely easier than XML manipulation.

(Have the script close the windows after manipulation to keep the memory usage down.)

2 Likes

That's definitely another way to go.

I can't remember, but there's a chance Find & Replace might be able to help here as well. Not sure if it allows renaming custom properties.

1 Like

That you pturmel and paul-griffith for the help!

I will dig into the interaction controller suggestion for a bit and will report back here with any questions or success I come up with.

I was not personally able to get find & replace to accomplish this, but I could have been using the wrong approach.

Find and Replace certainly finds all instances of the custom property on the template and in windows.

Select Templates, all and Windows, all

This is probably a silly mistake on my part caused by my lack of familiarity with the Ignition SDK.

I am trying to test the InteractionController interface in a designer script and keep getting an error stating that I am not providing enough arguments to the getPropertyAdapter method even though the javadocs makes it seem like the getPropertyAdapter method only takes 2 arguments.

from com.inductiveautomation.factorypmi.application.binding import InteractionController

# Get window object
vision_window = system.gui.getWindow("Stations/TEST")

# Find the template instance
template_instance = vision_window.getRootContainer().getComponent("ExampleComponent")

# Modify a template parameter
InteractionController.getPropertyAdapter(template_instance, "Title")

This code yields the following error:

Traceback (most recent call last):
  File "<input>", line 10, in <module>
TypeError: getPropertyAdapter(): expected 3 args; got 2

Am I missing something?

It’s been awhile since I’ve done stuff with the interactionController but I believe you need to get a reference to it with interactionController = vision_window.getInteractionController() then call the getPropertyAdapter method on that reference

1 Like

That did the trick, thank you very much! I will keep experimenting with the template parameter renaming and will report back!

1 Like

To expand on the 'why' of this:
Java has both static and instance methods (since everything is contained in a class).
Jython allows you to call instance methods of a class using a similar calling convention to Python, by providing your own "self" argument (Java would call it this).

That is, these two do the same thing:

instance = vision_window.getInteractionController()
InteractionController.getPropertyAdapter(instance, template_instance, "Title")
instance = vision_window.getInteractionController().getPropertyAdapter(template_instance, "Title")

But the missing "self" is why you get apparently confusing TypeErrors, where the method claims it expects more parameters than the Javadoc shows.

1 Like

Except you don't need an import at all for the latter syntax.

2 Likes

Thanks again for more great replies!

I almost have a working solution in place (which I will share when finished) however, I am missing one last piece of the puzzle.

After I move the parameter bindings from the old template to the new template, I can see that the parameters are bound but the bindings do not seem to have been activated yet. I have to click on each parameter’s binding editor dialog button to get the actual bound value to refresh.

The new bindings are also lost if I copy/paste the new template unless I open the binding dialog of each new binding as described above.

I was trying to use the com.inductiveautomation.factorypmi.application.binding.DefaultInteractionController class’ connectAdapters() method to remedy this but it does not seem to do what I am needing.

It seems like I am not fully implementing the new bindings correctly, but I am struggling to find the correct methods in the javadocs.

Here is what I have so far:

# Define old template path
oldTemplatePath = str("OldTemplates/ExampleTemplate")

# Define new template path
newTemplatePath = str("NewTemplates/ExampleTemplate")

# Define old / new template parameters
parameterTransferList = [
{"OLD":"Old_P1", "NEW":"New_P1"},
{"OLD":"Old_P2", "NEW":"New_P2"},
{"OLD":"Old_P3", "NEW":"New_P3"},
{"OLD":"Old_P4", "NEW":"New_P4"}
]

# Define bindings to be deleted
parameterDeleteList = [
"Old_P5",
"Old_P6"
]

# Get scripting objects
window = system.gui.getParentWindow(event)
container = event.source.parent
interactionController = window.getInteractionController()
context = interactionController.getContext()

# Loop through all components in parent container
for component in container.components:
	
	try:
		templatePath = component.templatePath
	except:
		templatePath = None
	
	# Modify ActuatorComponent template instances
	if templatePath == oldTemplatePath:
		
		# Transfer old parameters to new parameters
		for parameter in parameterTransferList:
			
			binding = interactionController.getPropertyAdapter(component, parameter["OLD"])
			
			if binding is not None:
				interactionController.setPropertyAdapter(component, parameter["NEW"], binding)
				interactionController.removePropertyAdapter(component, parameter["OLD"])
				
			else:
				value = component.getPropertyValue(parameter["OLD"])
				component.setPropertyValue(parameter["NEW"], value)
		
		# Delete unused bindings
		for parameter in parameterDeleteList:
			
			binding = interactionController.getPropertyAdapter(component, parameter)
			
			if binding is not None:
				interactionController.removePropertyAdapter(component, parameter)
				
			else:
				pass
		
		# Change component template path
		component.templatePath = newTemplatePath
		
	else:
		pass

# Refresh bindings
interactionController.connectAdapters(context)

Anyone have any suggestions?

Thanks again for you suggestion of using the InteractionController class. Does the code in my post above follow the idea you were presenting? Can you see what I am doing wrong in correctly moving the adapters from the old template to the new template?

I'd try to alter the binding to point at the new parameter, not try to replace it. (Bindings have to start up with the component, and there are complex nuances there.)

Ah, good suggestion!

I replaced the commented lines of code with the uncommented line and that fixed the copy/paste issue I mentioned above, and the template now acts like it is bound to the tags I am using.

binding = interactionController.getPropertyAdapter(component, parameter["OLD"])
				
if binding is not None:
	#interactionController.setPropertyAdapter(component, parameter["NEW"], binding)
	#interactionController.removePropertyAdapter(component, parameter["OLD"])
	binding.setTargetPropertyName(parameter["NEW"])

The issue I am seeing now is that the Vision Property Editor does not show that the parameters are bound unless I cut/paste the template instance. Once it is pasted, the parameters show that they are bound.

I assume I am missing another step to complete the alteration process.

I apologize for my ignorance using these classes, this is my first time digging into the SDK classes.

Also, is there a better place to look for documentation on these classes than the javadocs? It does not seem like there is much information on what the methods do or how to use them in the javadocs.

Try closing and re-opening the window. (Component startup nuances, I suspect.)

You are completely off into the unsupported weeds. Sorry. If the javadoc doesn't help, your only further tool is reflection. (See the reflection tools in my Integration Toolkit.)

2 Likes

Closing/Opening the window also does not fix the issue, only copy/paste works so far.

I also tried restarting the designer and that also did not make a difference, unfortunately.

I compared the copied XML data before and after I pasted the component and there are certainly differences. @paul-griffith Do you know if the copy/paste function is doing some automatic repairs/substitutions that I can handle in my scripting to fix this issue?