Python help needed

This is a legacy forum which is locked for new topics. New topics should be started in one of the other forums under Extending Indigo
Forum rules
This is a legacy forum which is locked for new topics. New topics should be started in one of the other forums under Extending Indigo
User avatar
berkinet
Posts: 3441
Joined: Tue Nov 18, 2008 2:08 pm
Location: Berkeley, CA, USA & Mougins, France

Python help needed

Post by berkinet »

I have a class (ifkit) that handles all communications with a piece of external hardware. The method that starts and manages the communications is startPhidgetThread. While most of the work involves reading the data flow from the hardware and maintaining states of Indigo custom devices, there are times when I need to write to the external hardware.

So far, I have the process working for reading the data. The communication management class is called from plugin.py as:

Code: Select all

def runConcurrentThread(self):
    threadIfkit = threading.Thread(target=self.ifkit.startPhidgetThread, name='ifKit')
    threadIfkit.start()
Now, I want to create a plugin action to "write" to the hardware. There is a function defined in ifkit.startPhidgetThread called setOutput that does what I need. I can create the action and define a callback method in plugin.py called ikfitWrite. But, the problem I am having is getting the callback method to call setOutput and pass the two arguments it requires, or find some other means to execute the code contained in setOutput.

ifkit looks like:

Code: Select all

class ifkit(object):
	########################################
	def __init__(self, plugin):
		self.plugin = plugin
		self.shutdown = False

	def __del__(self):
		pass
					
	######################
	def startPhidgetThread(self):
		indigo.server.log('ifKit thread starting', type="Phidgets Plugin")
	
		def setOutputI(port, value):
		    interfaceKit.setOutputState(port, value)

                def <Additional Communications functions>
                    ...

                while not self.shutdown:
    	 	    time.sleep(0.5)
Any ideas, pointers, suggestions, examples, etc. most appreciated.

EDIT: FWIW, all communications from the hardware are event driven and managed by lower-level interface code provided by the manufacturer. startPhidgetThread also contains callback methods that are invoked by the lower-level interface as needed. That is why I have the endless wait loop at the bottom, waiting for these callbacks, or, my action invocation..
User avatar
jay (support)
Site Admin
Posts: 19232
Joined: Wed Mar 19, 2008 11:52 am
Location: Austin, Texas
Contact:

Re: Python help needed

Post by jay (support) »

I think you'll want to look at the iTunes or EasyDAQ plugins - both use a queue object to pass commands from the plugin to the individual device threads. Then in the device thread's main loop you check for any commands that need to be sent to the hardware and send them.
Jay (Indigo Support)
Twitter | Facebook | LinkedIn
mat
Posts: 776
Joined: Thu Nov 25, 2010 10:48 am
Location: Cambridgeshire - UK

Re: Python help needed

Post by mat »

The serial test plugin uses a queue method too. Very easy to mod.
Late 2018 mini 10.14
User avatar
berkinet
Posts: 3441
Joined: Tue Nov 18, 2008 2:08 pm
Location: Berkeley, CA, USA & Mougins, France

Re: Python help needed

Post by berkinet »

Thanks Jay and Mat. I had looked at the EasyDAQ plugin, but was hoping there was a way to build on what I already have. The current architecture is built on top of a large body of code provided by the Phidgets manufacturer and it is pretty easy to use as it now exists.

I wouldn't think it should be that hard to either call a method defined in a thread, or send a message to a thread to do something. But, if I can't figure out how to do that, I guess I'll take a look at at queues again.
User avatar
jay (support)
Site Admin
Posts: 19232
Joined: Wed Mar 19, 2008 11:52 am
Location: Austin, Texas
Contact:

Re: Python help needed

Post by jay (support) »

Threads require some kind of IPC if you want one thread to talk to another. Queues are the easiest way and they're built-in to Python.
Jay (Indigo Support)
Twitter | Facebook | LinkedIn
mat
Posts: 776
Joined: Thu Nov 25, 2010 10:48 am
Location: Cambridgeshire - UK

Re: Python help needed

Post by mat »

I'm no python expert. Only took my first look last week.

I've moded the serial plugin to remove the queue as my serial device is "input only", and it receives commands faster than a Nintendo player with 16 fingers can push buttons on my iPad.

If you use a queue method, watch CPU usage. I removed all the pauses in the serial test plugin code to send data without pausing between queue reads and 1 processor core sat at 98% for three days before I realised. Beginners mistake I guess.
Late 2018 mini 10.14
mat
Posts: 776
Joined: Thu Nov 25, 2010 10:48 am
Location: Cambridgeshire - UK

Re: Python help needed

Post by mat »

This is what works for me. I don't claim to understand it but just followed the logic. As a really early beginner it works for me, but i'm sure it can be improved, even if I have pasted everything :lol: .

Code: Select all

	def sendSerialCommand(self, devId, command):
		dev = indigo.devices[devId]
		devProps = dev.pluginProps
		portName = devProps.get("serialPort")
		portBaudRate = devProps.get("portBaudRate")
		portParity = devProps.get("portParity")
		portDataBits = int(devProps.get("portDataBits"))
		portStopBits = int(devProps.get("portStopBits"))
		writeCommand = command
                conn = self.plugin.openSerial(dev.name, portName, baudrate=portBaudRate, parity=portParity, bytesize=portDataBits, stopbits=portStopBits, timeout=1, writeTimeout=1)
		sentCount = conn.write(writeCommand)
Late 2018 mini 10.14
User avatar
berkinet
Posts: 3441
Joined: Tue Nov 18, 2008 2:08 pm
Location: Berkeley, CA, USA & Mougins, France

Re: Python help needed

Post by berkinet »

Thanks Mat. It is actually the queueing code that might interest me, and I took a look at an earlier version of your plugin to see what you did - similar to what Jay noted, but a little simpler because of the nature of your plugin. It helped a lot, thanks.

In my case, the communications is all via IP and is all handled by code that was provided, so all I have to do is call their code in a separate thread and wait for events to call functions in that thread. Very simple.

The problem I has was the occasional need to go the other way. I did find out how to send an event to a thread, but that was fairly limited. Though, it might work for you in place of managing a queue. The idea is you send a message to the thread saying an event has taken place. You cannot pass any data. So, the thread would then have to figure out what to do... probably read from some global variable or object. Klugy, and not well suited for multiple threads, but interesting none-the-less. Here is the URL where I could the information. Quite clearly presented.

For my case, it looks like a queue is the way to go, though I am still hoping for some way to directly call the function inside the thread.
mat
Posts: 776
Joined: Thu Nov 25, 2010 10:48 am
Location: Cambridgeshire - UK

Re: Python help needed

Post by mat »

No problem, but for the record, I didn't write the serial plugin, I just hacked it to bits to suit my needs. I can't take credit for the original!

Hope you sort it out.
Late 2018 mini 10.14
User avatar
berkinet
Posts: 3441
Joined: Tue Nov 18, 2008 2:08 pm
Location: Berkeley, CA, USA & Mougins, France

Re: Python help needed

Post by berkinet »

I figured it out :D and it was actually pretty straight forward once I had the right model (which turned out to be a resettable timer).

The class is declared as:
class foo(threading.Thread):

and must have:
def __init__(... ...):
  • threading.Thread.__init__(self)
Then I can declare all the methods I need, like
  • def sendToComms(... ...)
plus the start method
  • def run()
which starts things going, and stays open with a timer loop listening for events from the comms channel

Now, in the main plugin code I can start this thread by
self.myClass = foo(self, ... ...)
self.myClass.start()

and, anytime I need to send something to that thread I can just:
self.myClass.sendToComms(... ...)

No queues, no message passing. Now, In fact, since there will be multiple threads, I save the handles in a dictionary so I can grab the right one and talk to the correct thread. Like:
self.threadDict = {}
self.threadDict[dev.id] = foo(self, ... ...)
self.threadDict[dev.id] .start()

and then...
self.threadDict[dev.id].sendToComms(... ...)
Locked

Return to “Extending Indigo with Plugins and Python”