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.

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!


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

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: @paul-griffith 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.

):

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