Asyncio (or something better) for control of a vacuumsystem/components.

S

Shishir

Dear All,

I apologise in advance a) if this is not the proper list for asking the
question, b) the length of the post.

I am writing a software to control and monitor a vacuum
furnace+attachments. It has a few mass flow controllers, a butterfly valve,
a labjack unit for analog/digital outputs etc. They use RS485, RS232 and
USB to communicate with the computer and follow special protocols for
commands and response. The response time to execute commands can be from 5
ms to 1 s.

To achieve this, I thought of a server which reads commands on a network
connections, parses the command and calls methods of appropriate device
classes, which then use the corresponding channel protocol to execute the
command. The response provided by the devices (flow controllers, valve) is
sent back on the network connection.

As there can be multiple clients (e.g. for monitoring from several
computers), and some commands can take long, the server should not block
when getting a command executed.

I wanted to use asyncio to achieve this non-blocking behaviour, i.e. when
the server calls the method of device classes, a callback is installed and
the method returns immediately. When the response is available from the
device, the callback writes the response to the client's connection.

Some (pseudo)code:

class server(Protocol):
def connection_made(self, transport): self.transport = transport
def data_received(self, data):
device, command, args = parse(data)
result = yield from device.command(args)
self.transport.write(result)

class device1():
self.channel = rs232()
@asyncio.coroutine
def command1(args):
r = self.execute(self.channel, 'Cmd1', args) # this can take a while
return r

# ++ get_event_loop code for running the server forever

This doesn't work as I expected. For example, I expected that when command1
is awaiting response from the device, the server can receive another
command2 on the same network connection, and get that executed (say on
another device). If the response of this command2 is available earlier, it
will be written to the network connection. Instead, the data_received call
for first command doesn't return till the command1 is completed. If the
execute() call is replaced by asyncio.sleep(r) it works as I expect.

The problem is that self.execute() blocks and the asyncio framework has no
way to know how to reschedule it or bypass it. This can be avoided if I
depended on I/O from a file descriptor, on which I can apply
poll()/select(). But the channel handler that I have is more generic
(rs232() in code above).

My question then is: is there a good way to handle the situation?

I would like to have following architecture:

A server sits on the computer which is connected to the devices via
channels (rs232, rs485, usb etc.). A client initiates a network connection
to the server and starts sending commands which are numbered. Server asks
the device classes for a response of execution of these commands on the
devices. The responses it receives (and dispatches to the client) from
device classes are not in same sequence, but that doesn't matter as they
are numbered by the client. There can be several clients. I also assume
that execution of each command on the devices is atomic, i.e. when the
device class connects to the device on later's channel, the channel is not
relinquished till the device provides a response. To achieve this, the
device classes obtain a lock on the channel they need before talking to the
device (and release it after they are done).


I have looked around the web but couldn't find an easy+flexible framework
to do what I have outlined here. Our lab is using a control panel written
in labview, which I believe does things sequentially (i.e. by blocking). It
works alright, but error handling is poor (crashes on errors and resets the
furnace state on a restart!). It is also done in Labview, is not portable
or can be addressed over network.


I have written/tested python code to control the following if anyone is
interested (will put up on bitbucket.org once I am done with the above
server):
T3Bi throttle valve controller from MKS (RS232 based)
GF40 MFCs from Brooks Instruments (RS485 based)
CMC & CMX gauges from Brooks Instruments (RS485, but currently controlled
by T3Bi)
1179A MFCs from MKS (which is controlled via analog output from a LabJack
U6)
SMC pneumatic switches (which are controlled via digital ports on LabJack
U6 + PS12VDC).
The LabJack parts use labjackpython library.

Thanks a lot for reading,
sid.
 
M

Marko Rauhamaa

Shishir said:
The problem is that self.execute() blocks and the asyncio framework
has no way to know how to reschedule it or bypass it. This can be
avoided if I depended on I/O from a file descriptor, on which I can
apply poll()/select(). But the channel handler that I have is more
generic (rs232() in code above).

Deep down asyncio depends on file descriptors. If your thingy is another
kind of object, it needs to go into its own process or thread.
A server sits on the computer which is connected to the devices via
channels (rs232, rs485, usb etc.). [...]

Sounds about right.
I have looked around the web but couldn't find an easy+flexible
framework to do what I have outlined here.

Asyncio + subprocess should be all you need for a framework. However, some
system programming *will* be required.


Marko
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,740
Latest member
AdolphBig6

Latest Threads

Top