In Ignition 8.1.10, when calling system.util.sendMessage, if using scope=G and a value is specified for remoteServers, the message is never received on the remote server if payload is an Ignition type, such as com.inductiveautomation.perspective.gateway.script.PropertyTreeScriptWrapper$ObjectWrapper. I need to first convert the payload to a Python dict before sending the message.
This does not appear to be the case using scope=S if remoteServers is not specified. In that case, the message is sent successfully, even when payload is not a true dict.
Not terribly surprising, since Perspective runs in its own classloader (like all modules). Any scope other than âSâ wonât be able to deserialize a Perspective class instance (deserializing being required for objects passed through a network).
Not a bug, but a side effect of Ignitionâs module isolation.
What is the solution for this in general terms when trying to use Perspective properties as pyDicts?
Iâm trying to send custom parameters as pyDicts but not sure how to convert ââ type into a proper python dictionary type.
EDIT: Seems like just casting it with dict(objectRefHere) did the trick for what i was trying to do.
Has there been any update on this behavior? Casting the object as a dictionary seems to be a decent workaround, but has to be done at every level of a nested list/dictionary object. I'm having to do a bit of type manipulation in perspective before calling message handlers quite often.
We have the code below in a script module that converts Ignition-specific objects to native Python lists and dicts. We pass the Ignition object to the propertyToPython function, and get back Python lists and/or dicts.
def _isDictLikeObject(value):
return 'iteritems' in dir(value)
def _isListLikeObject(value):
return 'append' in dir(value)
def dictLikeObjectToDict(dictLikeObject):
pyDict = {}
for key, item in dict(dictLikeObject).iteritems():
item = propertyToPython(item)
pyDict[key] = item
return pyDict
def listLikeObjectToList(listLikeObject):
pyList = []
for item in list(listLikeObject):
item = propertyToPython(item)
pyList.append(item)
return pyList
def propertyToPython(property):
"""
Accepts a value, object, or array property that might be
encoded as an Ignition ObjectWrapper or ArrayWrapper and
returns the property as Python dicts and lists.
"""
if _isDictLikeObject(property):
property = dictLikeObjectToDict(property)
elif _isListLikeObject(property):
property = listLikeObjectToList(property)
return property
def unwrap(object):
if str(object).startswith('<ArrayWrapper>'):
object = list(object)
object = [unwrap(item) for item in object]
elif str(object).startswith('<ObjectWrapper>'):
object = dict(object)
object = {key: unwrap(value) for key, value in object.items()}
return object
Would prefer to explicitly check for ArrayWrapper and ObjectWrapper types, but not having any luck.
<type 'com.inductiveautomation.perspective.gateway.script.PropertyTreeScriptWrapper$ArrayWrapper'>
<type 'com.inductiveautomation.perspective.gateway.script.PropertyTreeScriptWrapper$ObjectWrapper'>
Thanks for the recommendation. The Ignition Extensions module sounds like a great idea!
Unfortunately, I'm not able to load that module into my environment at the moment.
I have tried python's copy.deepcopy() but it throws an error.
I like this. It looks to be basically the same as our solution, but more concise.
A word of caution, though: An earlier implementation of our solution also checked for specific strings in the type names, similar to what you're doing with <ArrayWrapper> and <ObjectWrapper>, but that broke in an Ignition update at some point when Ignition changed the classes used in certain situations. That's why we settled on checking for the presence of the iteritems and append methods to determine if the object is a list or a dict.
For a more concise and resilient deep copy before serializing, try this.
(Just stumbled on this thread while researching remoteServers and realized I knew a good answer to the question being asked even though it was a bit old... Figured I'd help anyone else who lands here later...)