minimalmodbus icon indicating copy to clipboard operation
minimalmodbus copied to clipboard

FD leak for serial port

Open anatoly-savchenkov opened this issue 4 years ago • 5 comments
trafficstars

I my script I need to re-initialize Instrument from time to time. After couple of months of uptime it failed with 'Too many open files' error. Investigation showed that serial devices were kept open thus causing a leak of file descriptors. Adding a destructor with self.serial.close() call inside solves the problem.

anatoly-savchenkov avatar May 30 '21 07:05 anatoly-savchenkov

Probably close_port_after_each_call can help as workaround.

j123b567 avatar May 31 '21 07:05 j123b567

I also think it should help but I didn't try. It was easier for me to patch my local copy and add a destructor.

anatoly-savchenkov avatar May 31 '21 07:05 anatoly-savchenkov

It is possible to close the serial port using instrument.serial.close()

However, it would be interesting to know what caused the problem in the first place.

pyhys avatar Aug 10 '21 21:08 pyhys

That's a good question. Probably it's a buggy HW, I see Linux loses USB to Modbus converter and I have to reset it (dmesg output): [436791.627904] ftdi_sio ttyUSB1: FTDI USB Serial Device converter now disconnected from ttyUSB1 [436791.636480] ftdi_sio 1-1.1:1.0: device disconnected [436791.840510] usb 1-1.1: reset full-speed USB device number 4 using ehci-pci [436792.074717] ftdi_sio 1-1.1:1.0: FTDI USB Serial Device converter detected [436792.081689] usb 1-1.1: Detected FT232RL [436792.087552] usb 1-1.1: FTDI USB Serial Device converter now attached to ttyUSB2

Right before that happens minimalmodbus throws the following exception: Checksum error in rtu mode: 'ø\x86' instead of '~I' . The response is: '\x00\x03\x04\x00\x06²$ø\x86' (plain response: '\x00\x03\x04\x00\x06²$ø\x86')

Obviously after calling usbreset, FD is no longer valid (but still open which causes the leak) and I need to re-open the port.

Any idea what to try before we conclude it's a HW issue?

anatoly-savchenkov avatar Aug 10 '21 23:08 anatoly-savchenkov

If there is an usb error, the port is recreated, but if the file is opened, then it can't reuse the same name, so e.g. if you are using /dev/ttyUSB1 then the new port /dev/ttyUSB2 is created, so you should close the first one and open the second one.

If you are on the recent linux (or it is easy to obtain UDEV rule to do so), you should find symlink to the serial port in the directory /dev/serial/by-path/ which will point exactly and persistantly acros reboots and usb errors to the same serial converter in the same USB port so you no longer need to solve if it is ttyUSB1 or ttyUSB2.

With this setup (using exclusively /dev/serial/by-path/), I'm using this little hack to solve similar problem like you.

import serial
import minimalmodbus

class SerialAutoOpen(serial.Serial):
    def open(self):
        try:
            super().open()
        except IOError:
            pass

    def close(self):
        try:
            super().close()
        except IOError:
            pass
        self.is_open = False

    def write(self, data):
        self.open()
        try:
            return super().write(data)
        except IOError:
            self.close()
            raise

minimalmodbus.serial.Serial = SerialAutoOpen

instrument = minimalmodbus.Instrument(...)

j123b567 avatar Oct 14 '21 12:10 j123b567