numat / alicat

Python driver and command line tool for Alicat mass flow controllers.
GNU General Public License v2.0
21 stars 27 forks source link

"OSError: Could not read control point" when calling flow_controller.get() and totalizer #101

Open xins-fys opened 6 months ago

xins-fys commented 6 months ago

Hi, I was simply just logging the "mass_flow" by calling image

It has been continously running for while, but alsway interupped by "OSError: Could not read control point." problem1

It has happened for "reset_totalizer" function, too. Then I used try: asyncio.run(reset_totalizer(MFC2))
except OSError: continue to ignore it, though I am not sure it's the best way to do so. But it bypassed the error.

But now it occurs at "asyncio.run(get_mfc()" that I cannot bypass it.

I read another thread about the same OSError, but on GP firmware. My firmware is 10v09.0-R24.

alexrudd2 commented 6 months ago

Sorry to hear you're having problems, and thanks for the report.

I can think of two possible causes: (1) I know it sounds silly, but are you using a mass flow controller or mass flow meter? If you try to use the MFC code on a meter it will error. (2) Can you successfully connect to any MFCs with your setup? If there's no connection at all, the first message to the device will error (if you suppress _get_control_point that just moves the error to the next message). The problem may be the serial settings.

I read another thread about the same OSError, but on GP firmware. My firmware is 10v09.0-R24.

You are correct, that's unrelated. Good job checking!

alexrudd2 commented 6 months ago

Regarding (2), how is your serial setup? Do you have a physical serial port for COM11 or is it a USB-to-serial adapter?

In either case, are the serial settings (baud, parity, etc) set up correctly? I don't use Windows so I can't give you specific directions, but you should be able to change the settings in Device Manager. Something like the below images (from Google)

image image

You may want to troubleshoot with a serial terminal program. Personally, I use CoolTerm. Good luck!

xins-fys commented 6 months ago

@alexrudd2 Thank you for your reply. To refer your question:
1) It is a mass flow controller. All the command works through a standard serial terminal program if it is a single line command. 2) I wired MFC to a Brainbox Ethernet-Serial server. The MFC has a RS485 port. Then run a virtual COM port on the controlling PC. Baud rate :19200, which is the same as the device setting Data bits: 8 Parity: None Stop bits:1

It doesn't seem to be the problem in serial Actually it has been work for some time. My code is something as below: i = 0 while True: readings_2 = asyncio.run(get_mfc(MFC2)) i = i +1 print(i, readings_2) time.sleep(0.5) It has been continously running until, sometimes interrupted at when i = 200; sometimes at when i = 1000-something, sometime when i = 2000-something. It is unpredictable and I cannot for sure reproduce it everytime. The only information about the interrupt is the screenshot I showed in the first message and error message.

alexrudd2 commented 6 months ago

Thank for for the further details. This is not a problem I've seen before, but I have a guess.

Your code is setting up and tearing down the connection and the asyncio event loop every time you are reading the MFC. This is a lot of unnecessary work (and therefore places to fail). I think it would be better if you setup the asyncio event loop once, and put your polling inside it.

From the asyncio.run() documentation:

This function should be used as a main entry point for asyncio programs, and should ideally only be called once.

StackOverflow has a longer explanation.

Here's a proposed rewrite, based on the README example:

async def get():
    async with FlowController('COM11', 'B') as MFC2:
      while True:
          readings_2 = await MFC2.get())
          i = i +1
          print(i, readings_2)
          asyncio.sleep(0.5)

asyncio.run(get())

Note (1) asyncio.run() is only called once, and therefore keeps the event loop and MFC connection (2) the async with context manager sets up the MFC once, and closes automatically when it exits (3) asyncio.sleep(0.5) instead of time.sleep(), in order to not block the thread.

Please let me know if this approach works for you. :)

alexrudd2 commented 6 months ago

Brainbox Ethernet-Serial server

This driver can read Modbus TCP/IP directly. In other words, you shouldn't need virtual COM port driver. Once you are running, consider switching.

FlowController('COM11', B') -> FlowController('<Brainbox IP>:<port>', 'B')

xins-fys commented 6 months ago

@alexrudd2 It seems very plausible. I will try to implement it into my main loop. But I think it should fix the problem. Thanks a lot for all the help and suggestions.