Display all scripting code of window and template components

Hello,

Is there a way to see the scripting code of all vision window components and template components? For instance, I need the value for template/button/mouse/mouseClicked/Script Editor, and so on and so forth across all components

My use case: I’m looking at changing database structure and would like to see what all components are making calls to the database so I can plan accordingly and minimize side effects

Thanks!

1 Like

Step 1: Get the context. I think this is familiar.

If the context cant be located in the script console you may have a flag that is odd.
print the system.util.getSystemFlags() and add the resulting integer to [DESIGNER_FLAG, 3] list.

current_flag = system.util.getSystemFlags()
ctx = self.resolveDesignerContext
# If False assumes the client context.
if current_flag not in [DESIGNER_FLAG, 3]:

After ensuring you have the context this should work. Update the path to your context and have fun with the joke.

Step 2:

from com.inductiveautomation.factorypmi.application.binding.action import ActionAdapter


class ScriptFinder:
	
	def __init__(self, context):
		
		self.context = context
		if not hasattr(self.context, 'getProject'):
			raise Exception(self.getInvalidContextError(context))
	
	def getProjectResources(self):
		"""Return all project Resources."""
		return self.context.getProject().getResources()
		
	def getInvalidResourceTypeError(self, resource_type):
		"""Return formatted error message."""
		return 'Invalid Resource Type "%s". Options are: %s || %s.' % (
			resource_type, self.getWindowResourceType(), self.getTemplateResourceType())

	def getInvalidContextError(self, context):
		"""Return formatted error message."""
		return 'Invalid Context Reference "%s". Hint Context must support getProject method' % str(type(context))
		
	def objectSearch(self, fpmiObject, array):
		"""
		NOTES: 
		: This method takes an JComponent
		: and an array reference it will recursivley 
		: walk the component chain accumulating members until 
		: reaching the last container.
		
		Arg(s):
		: [  fpmiObject ]  DataType: JComponent
		:	Example: Jframe, Jdialog, FMPI Root Container,
		: [    array    ]  DataType: list
		:	Example: []
		"""
		try:
			for component in fpmiObject.getComponents():
				array.append(component)
				self.objectSearch(component, array)
		except AttributeError:
			return

	def getAllComponentsForFpmiObject(self, fpmiObject):
		"""
		NOTES: 
		: Method takes a top level jcomponent and calls the
		: getComponents method. If the result is an AttributeError
		: the array is returned. If there are results both the array
		: reference and the component are passed to a recursive function
		: that will go throught the component chain.
		
		Arg(s):
		: [  fpmiObject  ]  DataType: JComponent
		: 	Example: Jframe, Jdialog, FMPI Root Container,
		"""
		components_array = []
		try:
			for component in fpmiObject.getComponents():
				components_array.append(component)
				self.objectSearch(component, components_array)
		except AttributeError:
			pass		
		return components_array
	
	def getProjectResourcesByType(self, resource_type):
		"""
		NOTES:
		: Filter resource array on type.
		Arg(s):
		: [resource_type]  DataType: String
		:	Example: "window", "component-template"
		"""
		return [
			resource for resource in self.getProjectResources() 
			if resource.getResourceType() == resource_type
		]
		
	def getWindowResourceType(self):
		"""Returns windows resource type."""
		return 'window'
	
	def getTemplateResourceType(self):
		"""Returns template resource type."""
		return 'component-template'	
	
	
	def getSerializedReferenceFromResource(self, resource_type):
		"""
		NOTES:
		: Returns a reusable function for the deserialization of 
		: the requested types. Reduces process overhead for iterative
		: resource assessment.
		
		Arg(s):
		: [resource_type]  DataType: String
		:  Example: "window", "component-template"
		"""
	
		deserializer = self.context.createDeserializer()
		deserialize = deserializer.deserialize
		
		def windowInner(resource):
			"""
			NOTES:
			: Inner Function that supports deserialization of window 
			: resources. The window root objects support a different method than
			: templates for retrieving the bytesArray.
			
			Arg(s)
			:  [ resource ]  DataType: <type 'com.inductiveautomation.ignition.common.project.ProjectResource'>
			"""
			code = deserialize(resource.getData()).getRootObjects()[0].getSerializedCode()
			return deserialize(code).getRootObjects()[0]
		
		def templateInner(resource):
			"""
			NOTES:
			: Inner Function that supports deserialization of template
			: resources. The template root objects support a different method than
			: templates for retrieving the bytesArray.
			
			Arg(s)
			:  [ resource ]  DataType: <type 'com.inductiveautomation.ignition.common.project.ProjectResource'>
			:
			"""
			code = deserialize(resource.getData()).getRootObjects()[0].getSerializedBytes()
			return deserialize(code).getRootObjects()[0]
		
		if resource_type == self.getWindowResourceType():
			return windowInner
		elif resource_type == self.getTemplateResourceType():
			return templateInner
		raise Exception(self.getInvalidResourceTypeError(resource_type))
			
	
	def getDeSerializedResources(self, resource_type, resource_array):
		"""
		NOTES:
		: Return an array of deserialized project resoures.
		
		Arg(s)
		: [   resource_type  ]  DataType: String
		:  Example: "window", "component-template"
		: [  resource_array  ]  DataType: list
		:  Example: []
		"""
		deserializer = self.getSerializedReferenceFromResource(resource_type)
		return [deserializer(resource) for resource in resource_array]
	
	
	def getComponentAdapterGetter(self, resource):
		"""
		NOTES:
		: Returns an uncalled method for use in iteration.
		: This method gets all adapteres associated for the
		: component passed in.
		
		Arg(s)
		: [   resource  ]  DataType: <type 'com.inductiveautomation.ignition.common.project.ProjectResource'>
		:  
		"""		
		return resource.getInteractionController().getAllAdaptersForTarget
	
	
	def getValidResourceType(self):
		"""Returns array of valid resource types."""
		return [self.getWindowResourceType(), self.getTemplateResourceType()]
	
	def isResourceTypeValid(self, resource_type):
		"""Checks resource type for validity."""
		return resource_type in self.getValidResourceType()
	
	def getObjectName(self, resource):
		"""Return object name."""
		return str(resource.getTitle() if hasattr(resource, 'getTitle') else resource.getName())
		
	def getActionAdatpersforComponent(self, resource, component):
		"""Returns filtered array of Action Adapters for component."""
		adapterGetter = self.getComponentAdapterGetter(resource)
		return [adapter for adapter in adapterGetter(component) if isinstance(adapter, ActionAdapter)]
	
	
	def getJythonCodeForResourceComponents(self, resource_type):
		"""
		NOTES:
		: Returns a dictionary of resource: {components: {code: ''}}.
		: Process acquires resources of specified type and iterates
		: over all components action adapters.
		
		Arg(s)
		: [   resource_type  ]  DataType: String
		:  Example: "window", "component-template"
		"""	
		# Test validity of type.
		if self.isResourceTypeValid(resource_type):
			resource_component_code_map = {}
			#Acquire resource data for type.
			resources =  self.getProjectResourcesByType(resource_type)
			# Iterate through deserialized resources.
			for resource in self.getDeSerializedResources(resource_type, resources):
				resource_name = self.getObjectName(resource)
				# Get all components.
				components = self.getAllComponentsForFpmiObject(resource)
				if components:
					resource_component_code_map[resource_name] = {}
					#Iterate over component.
					for component in components:
						component_name = self.getObjectName(component)
						# Get adapters for component and iterate filtering on action adapters.
						for adapter in self.getActionAdatpersforComponent(resource, component):
							# Create entry in dictionary if not present.
							if component_name not in resource_component_code_map[resource_name]:
								resource_component_code_map[resource_name][component_name] = {}
							# Get code from action adapter.
							code = adapter.getJythonCode()
							# Get component method associated with the code.
							method_name = adapter.getMethodDescriptor().getName()
							# Update resource map.
							resource_component_code_map[resource_name][component_name][method_name] = code
							
			return resource_component_code_map
		
		raise Exception(self.getInvalidResourceTypeError(resource_type))
		return {}

	def getAllWindowsAllComponentsJythonCode(self):
		""" Returns dictionary of window resource: {components: {code: ''}}."""
		return self.getJythonCodeForResourceComponents(self.getWindowResourceType())

	def getAllTemplatesAllComponentsJythonCode(self):
		""" Returns dictionary of template resource: {components: {code: ''}}."""
		return self.getJythonCodeForResourceComponents(self.getTemplateResourceType())

	def prettyPrint(self, output):
		print system.util.jsonEncode(output, 4)



context = project.Resources.Context.getContext() # <- this must be correct
sf = ScriptFinder(context)

sf.prettyPrint(sf.getAllWindowsAllComponentsJythonCode())

sf.prettyPrint(sf.getAllTemplatesAllComponentsJythonCode())
5 Likes

This does not seem to work with 8.1. Has anybody converted it to 8.1 compatible version?

Most of the code should still be valid, you'll just have to extract the resources from the project differently. Some reference points to start from:

I am trying to do this by not installing additional modules (security concerns that our admins have).
I want to contain all functionality in the global project's script library so that I can just call it from the scripting console.
so far i am able to get the resources like so:

from com.inductiveautomation.ignition.designer import IgnitionDesigner  
from com.inductiveautomation.ignition.common.project import *  
context = IgnitionDesigner.getFrame().getContext()  
resources = context.getProject().getResources()

for r in resources:   
    r.getData()

the thing is that I get a byte array's that I am not sure how to convert to some usable format.

array('b', [31, -117, 8, 0, 0, 0, 0, 0, 0, 0, -51, 84, 93, 111, -37, 32, 20, 125, 110, 

PS. My ultimate goal is to create a script that will iterate over all scripting resources and write them into the fileststem, then show a popup input prompt for a commit message and from there call git to check everything in to the repository so we can version control. Like I said I am limited to scripts as we do not want to install any modules.

The code to deserialize a byte array into a Vision window is in the script posted above; it's unchanged from whatever version that was written in.

You're going to be swimming upstream the entire time you're doing this, and subject to breaking changes every time you update Ignition. I'd really recommend against it.

As of 8.1.25 you can set the project to store Vision windows in human-readable XML. It's not particular friendly to diff, but avoids all of this hacking about with the resource internals.

1 Like

Do you have a good method on how to manage code with version control?
I am also looking into the project export - all script library files are there in plain text, so that would work potentially, but windows' scripts are contained in the binary file.

As of 8.1.25 you can set the project to store Vision windows in human-readable XML. It's not particular friendly to diff, but avoids all of this hacking about with the resource internals.