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.
-- https://mail.python.org/mailman/listinfo/python-list