system.tag.writeAsync() requires callback to be named "callBack" in 8.0.14

Whenever I try to execute a callback with a name other than callBack (I cant even use callback) it fails showing that the global name is not defined.

Is this a bug, or is there a reason that the callback function specifically needs to be named and typed that way?

i.e. This works:

tagPath = "[Testing]Lines/Icing/Equipment/Mixer/State"

class tagWriter():
	def __init__(self, tagPath):
		self.state = not system.tag.read(tagPath).value

	def callBack(self, aSyncReturn):
		print 'Tag write successful!'

	def tagWrite(self):
		system.tag.writeAsync([tagPath], [state], callBack)

writer = tagWriter(tagPath)
writer.tagWrite()

But this does not

tagPath = "[Testing]Lines/Icing/Equipment/Mixer/State"

class tagWriter():
	def __init__(self, tagPath):
		self.state = not system.tag.read(tagPath).value

	def callback(self, aSyncReturn):
		print 'Tag write successful!'

	def tagWrite(self):
		system.tag.writeAsync([tagPath], [state], callback)

writer = tagWriter(tagPath)
writer.tagWrite()

Explicitly I need a completely different name for the callback, because its on a class that may have a handful of different callbacks after tag writes.

I think you already had a callBack variable in scope - were you testing in the script console? As is, neither of your examples will actually run - tagPath, state, and callback are not defined in the scope of the tagWrite function.
I was able to get your example working with some modifications:

tagPath = "[default]alarm"

class tagWriter():
	def __init__(self, tagPath):
		self.tagPath = tagPath
		self.state = not system.tag.read(tagPath).value

	@staticmethod
	def callback(aSyncReturn):
		print 'Tag write successful!'

	def tagWrite(self):
		system.tag.writeAsync([self.tagPath], [self.state], tagWriter.callback)

writer = tagWriter(tagPath)
writer.tagWrite()

Unfortunately, the writeAsync callback argument isn’t defined very well - trying anything fancy will probably lead to java.lang.ClassCastException: Cannot coerce value '<type>' into type: class org.python.core.PyFunction. There’s not really any great ways around that, until/unless we patch the method internally. That’s why I had to use the @staticmethod decorator above - even calling self.callback isn’t a PyFunction, it’s a PyClassMethod or something similar, so the strict cast we have in our code won’t work.

If I copy the text from my first one, it will run in the script console for, even with a fresh designer launch? I dont see the "Tag Write Successful" in the script console though, I see it in the regular console. I presumed this is because its executing in a separate thread associated with the designer instead of the script console.

In regards to the rest of that, I can rename it with the change you made, however it loses the references to self in the callback function, which is not optimal as I actually need to reference some self variables in the real version of this script. In my real script library I just have it temporarily changed to callBack instead of eventComplete which is what I really need.

Is there any reason the above script runs for me and not you? Try setting your global scripting project to the one you're using, thats the only difference I could think of. Which I would assume is why the error that I see when using callback instead of callBack is the following:
NameError: global name 'callback' is not defined

I know this is a bit of a workaround, but if you define the callBack function within the tagWrite definition, you can call your eventComplete method from that callBack with the references to self still intact.

2 Likes

This does feel like a bit of a workaround, but I recognize that it may just be a stipulation of the writeAsync function as paul mentioned above.

This works perfectly though, thank you!

1 Like


This is what I get. Do you have a project library called state? This is what I would expect - state is unqualified when it’s referenced. tagPath happens to work in tagWrite because you defined it before you create your class - rewriting your sample to not define a tagPath variable then throws an exception about tagPath not existing:

Fixing that, then, leads to "callBack is not defined":

Again, all expected behavior - this is all Python, and nothing to do with writeAsync at this point.
The global scripting project can’t be a factor here, since we’re inside the scope of a particular project.

@KMuldoon’s suggestion is a good one - you can even improve it a bit using lambda:

class tagWriter():
	def __init__(self, tagPath):
		self.tagPath = tagPath
		self.state = not system.tag.read(tagPath).value

	def callBack(self, aSyncReturn):
		print aSyncReturn, 'Tag write successful!'

	def tagWrite(self):
		system.tag.writeAsync([self.tagPath], [self.state], lambda r: self.callBack(r))

writer = tagWriter("[default]alarm")
writer.tagWrite()
1 Like

Interestingly I don't have a project library called state either...?

At this point with y'all's suggestions its working, just weird that I am seeing a different behavior than you are.

Weirdly mine also works without tagPath defined as well?

class tagWriter():
	def __init__(self, tagPath):
		self.state = not system.tag.read(tagPath).value

	def callBack(aSyncReturn):
		print 'Tag write successful!'

	def tagWrite(self):
		system.tag.writeAsync([tagPath], [state], callBack)
writer = tagWriter("[Testing]Lines/Icing/Equipment/Mixer/State")
writer.tagWrite()

EDIT: @PGriffith I PM'd you a copy of the project if it helps

Oy! Please just use the standard PyObject.isCallable() and PyObject.__call__() / PyObject._jcall() methods. Those take care of all of the permutations. There's no excuse for not accepting self.someCallback.

):

2 Likes

Indeed, that's exactly what I suggested in the bug ticket I filed this morning :slight_smile:

2 Likes