Is there a way to access the project properties in python to get things such as the default database name or the polling base rate, even if it’s read-only?
Yea, sure. All you need is the client or designer context.
However, the way that you get the context in Python scripting is a bit hacky and unsupported. That’s why I just made a feature request to provide supported functions for getting the client/designer/gateway context: inductiveautomation.com/forum/vi … 71&t=11563
Here’s how you can get a client or designer context so you can get the default database name and polling rate etc. in Python. (yay Python!!!).
Throw this in an event handler somewhere:
def getContext(event):
import system
comp = event.source
if system.util.getSystemFlags() & system.util.DESIGNER_FLAG:
from com.inductiveautomation.ignition.designer import IgnitionDesigner
while not isinstance(comp,IgnitionDesigner):
comp = comp.parent
context = comp.getContext()
else:
from com.inductiveautomation.factorypmi.application.runtime import ClientPanel
while not isinstance(comp,ClientPanel):
comp = comp.parent
context = comp.getClientContext()
return context
context = getContext(event)
defaultDatasourceName = context.getDefaultDatasourceName()
pollRate = context.getTagPollRate()
print defaultDatasourceName, pollRate
By the way I look forward to meeting you at the next Ignition conference.
Nick
Thanks, Nick. It was one of your most recent posts which inspired my question. It’ll mark it as good when I get a chance to test it.
We had a need for context access from the script console and other spots where we couldnt use an event object. This is what we use…
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)
return cls.instance
def __init__(self):
"""Get context."""
self.context = self.resolveContext()
self.valid = True
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 != DESIGNER_FLAG:
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
def initActiveContext():
"""Called from startup script"""
ActiveContext()
def acquireContext():
"""Returns context reference."""
active_context = ActiveContext()
if active_context.isValid():
return ActiveContext().getContext()
That’s very interesting, thank you. I was wondering, do you know of a way to get the GatewayContext from a gateway script? Like gateway timer script, startup script, tag change etc.
Unsupported.....
from com.inductiveautomation.ignition.gateway import SRContext
ctx = SRContext.get()
And very dangerous - if you go down this road, take frequent gateway backups, because it's very possible to permanently break things.
Yeah, I should have mentioned I was aware of SRContext from the forums here but noticed its nowhere to be seen in the documentation, and I have heard about problems with it before. Given the impact something like that could have on our gateways, I’m staying far, far, far away from that.
The safer way to get access to the gateway context to perform high-level operations is to write your own module - if you’re already comfortable doing this high-level scripting, it’s not that much of a leap to module development.
I thought that’d be the best way - I’m thinking about making that leap some time, but the right opportunity to pour the time into that hasn’t been there just yet. As for this particular issue, its more of a “would be cool if I could do this”, not a “pour many hours into making a new module” type thing.
Debatable. {my bold} If you are using the documented GatewayContext methods, the returned objects are identical via module context and SRContext. I would expect the danger to be in the extra functionality in the SRContext, but you must use reflection to explore that. If there's danger in the documented GatewayContext (and I would suggest there is), writing a module instead of using SRContext won't mitigate it. And it is really easy to crash a gateway with a sloppily-coded module.
I said "debatable" because learning the module life cycle in enough detail to avoid such crashes is good preparation for intelligent access to such resources via scripting. Anyways, support for anything done with Module SDK documentation has always been explicitly disavowed by IA.