Programatically search through templates?

Hello,

I’m trying to write a script to go through the properties on each template piece so that I can easily audit the scripting code. There’s a ton of template pieces to go through, doing this manually will be tedious and prone to error

Does Ignition have something built-in? Something like system.tag.browseTags but for templates?

Thanks

The following might get you started. You'll have to work out how to deserialize. Seriously unsupported....

If you will add these two functions to my original post that pturmel linked you will have everything needed.


def getTemplateInstance(event, templatePath):
	"""
	Return Template Instance. 
	Pass the path into here.
	"""
	context = getContext(event)
	project_obj = context.getProject()
	templateManager = context.getTemplateManager()
	templateId = templateManager.getId(templatePath)
	return templateManager.getTemplateInstance(templateId)


def getTemplateInstanceProperties(templateInstance):
	""" 
	Returns list of properties on template instance.
	Other cool methods are getShortDescription and getPropertyValue, setPropertyValue etc.
	if you dir(templateInstance) the methods are available.
	"""
	return [str(prop.getName()) for prop in templateInstance.getProperties()]

raymond1982 - Bro. You are awesome. I like that other thread, answered 3 years after the question… good example of why necromancing rules are stupid… it’s not like the problem goes away

Thanks a ton, that takes care of the heavy lifting

No problem man. I have learned a lot on the forum and am trying to contribute on some of the more esoteric items. We use meta programming and reflection/inspection to achieve some of the more dynamic things in our project. It would be nice if these were canned system functions but the ignition devs have a tremendous workload so id rather just do it myself than burden them with a feature req.

Right? I agree. I’ve feature requested a stop/interrupt button for the script console, that seems like a much bigger fish to fry, especially since while this isn’t as simple (at least the first time), it can be done, whereas the other is a gaping hole in the feature set that really needs someone on the Inductive team to fix

https://inductiveautomation.canny.io/ignition-features-and-ideas/p/stopinterrupt-for-script-designer/ Go upvote it if you think it’s something they should do :slight_smile: They say it’s planned but you know how those things work, higher voted items get prescience

Cheers

Follow up question, @raymond1982, is there any way to do this from the script console or without the event object?

yes. This may require some explanation. Instead of using an event object i create a Jframe and grab the projects highest most frame reference and get it that way… I work in console a lot too and this helps. This is a singleton class which makes it nice. Load it at startup and dont worry about getting it again.Heavily unsupported.

from system.util import CLIENT_FLAG, DESIGNER_FLAG
from javax.swing import JFrame
from java.awt.event import WindowEvent

try:
	from com.inductiveautomation.ignition.designer import IgnitionDesigner
except ImportError:
	pass

from com.inductiveautomation.factorypmi.application.runtime import ClientPanel


class ActiveContext(object):
	"""
	Singleton for a stable ignition context reference. 
	NOTE: Mainly for code running where there is no event 
	object to derive context access. i.e. 
	scripts launched automatically or script-console..
	# Has been tested for Designer, 
	# Script Console, Client [Full Screen, Published].
	# We call this as a startup script.
	# Tested on Ignition Version 7.9.3
	# Certian API dependencies may need to be resolved 
	# on versions before or after (7.9.3).
	"""

	def __new__(cls):
		"""
		Hook into the class constructor.
		Adds an class attribute reference to the
		instance.
		"""
		if not hasattr(cls, 'instance'):
			cls.instance = super(ActiveContext, cls).__new__(cls)
			# we only need to establish the context reference 1 time.
			cls.instance.run()
			
		return cls.instance
   
	def run(self):
		"""Get context."""
		print '*************************** Acquiring Project Context ***************************'
		self.valid = True
		self.context = self.resolveContext()

	def isValid(self):
		"""Is Context Valid."""
		return self.valid
	
	def setValid(self):
		"""Set Valid State."""
		self.valid = False if self.valid else True
	
	def resolveDesignerContext(self):
		"""Return designer context."""
		try:
			return IgnitionDesigner.getFrame().getContext()
		except AttributeError:
			pass
		self.isValid()
		
	def resolveClientContext(self):
		"""
		Attempt to resolve client context.
		By creating a frame it prevents the need to
		traverse the object heirarchy. i.e. parent.parent....
		"""
		# acquire project name.
		projName = system.util.getProjectName()
		# Frame title.
		frame_name = 'ThrowAway'
		# Create disposable top level frame object.
		frm = JFrame(frame_name)
		# Adjust close behavior.
		frm.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE)
		# get the windows.
		windows = frm.getWindows()
		# cleanup frame.
		frm.dispose()
		# iterate windows, filter on the title containing the 
		# project name.
		for window in windows:
			try:
				if projName in window.getTitle():
					pane =  window.getContentPane()
					# Compare the content pane instance 
					# to the ClientPanel object.
					if isinstance(pane, ClientPanel):
						if hasattr(pane, 'getClientContext'):
							return pane.getClientContext()
			except AttributeError:
				pass
		self.isValid()	
		
	def resolveContext(self):
		"""Attempt to resolve the Context."""
		current_flag = system.util.getSystemFlags()
		ctx = self.resolveDesignerContext
		# If False assumes the client context.
		if current_flag not in [DESIGNER_FLAG, 3]:
			ctx = self.resolveClientContext
		return ctx()		

	def __call__(self):
		"""Calls to the Instance return the Context."""
		return self.getContext()
	
	def getContext(self):
		"""Return Context."""
		return self.context



ctx = ActiveContext()

def initActiveContext():
	"""Called from startup script"""
	return ctx

We’re going from “seriously unsupported” to “heavily unsupported.” I love it :joy: :joy:

That is absolutely perfect. Now to piece it all together. Great looking code, btw

Thanks!

That’s great, I’ve wanted to get project stuff from the designer script console occasionally, never knew how to do it. I hacked up your script a bit for the designer to get the global project, then I can see scripts, pipelines, etc that I don’t think I could before:

try:
	from com.inductiveautomation.ignition.designer import IgnitionDesigner
except ImportError:
	pass

try:
	cx = IgnitionDesigner.getFrame().getContext()
except AttributeError:
	cx = None

if cx is not None:
	globalProject = cx.getGlobalProject().getProject()

Lets take the unsupported level up a bit though. Can we get the global project, but in Gateway context? Like in a gateway timer script. WITHOUT using SRContext.

To be honest even if knew i wouldn’t post it. The possibility of someone crashing their gateway is very real. That SRContext is heroically unsupported.

So I’m not sure I’m 100% following, but I would expect ctx.getContext() to return the project’s context, right? Instead I’m getting None

Trying to get down to something like ctx.getContext().getTemplateManager()

edit:

I can do
resources = IgnitionDesigner.getFrame().getContext().getProject().getResources() print( filter( getTemplateFilter(), resources) )

But then what’s the point of having the ActiveContext class?

edit:

found the bug, changed

if current_flag not in [DESIGNER_FLAG, 3, 65]:

and .getContext() returns a context. Thanks raymond1982

Template Manager is a singleton which takes the context as an argument in the constructor. By adding the below and using the above code copied into my script console this is what i got. Seems to work fine. You would have needed to edit the code that depended on the event object. If you`re working at this layer you can never be sure how much explanation is needed.

from com.inductiveautomation.factorypmi.application.model import TemplateManager

print TemplateManager(ctx.getContext())
>>> com.inductiveautomation.factorypmi.application.model.TemplateManager@6270e73a

And going away in Ignition 8. :slight_smile: