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

1 Like

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

4 Likes

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()]

3 Likes

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

3 Likes

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.

1 Like

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
2 Likes

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!

1 Like

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.

1 Like

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

1 Like

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:

2 Likes