串行连接中断时 "During handling of the above exception, another exception occurred" 的无限循环

Infinite loop of "During handling of the above exception, another exception occurred" when serial connection interrupted

我有一个 Arduino,它使用 pySerialTransfer 库通过串口与我的 Mac 正常通信,运行持续数小时。然后会出现某种类型的串行中断 - 虽然我一直无法确定一夜之间发生这种情况的原因,但只需从笔记本电脑上拔下 Arduino USB 电缆,我就可以很容易地重现该行为。我笔记本电脑上的 python 代码继续 运行,但进入了这个无限错误循环:

Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/pySerialTransfer/pySerialTransfer.py", line 257, in send
    self.connection.write(stack)
  File "/usr/local/lib/python3.7/site-packages/serial/serialposix.py", line 571, in write
    raise SerialException('write failed: {}'.format(e))
serial.serialutil.SerialException: write failed: [Errno 6] Device not configured
SENT (12 byte struct): (0, -55.836434114277004, 31.732435543849192)
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/serial/serialposix.py", line 537, in write
    n = os.write(self.fd, d)
OSError: [Errno 6] Device not configured

During handling of the above exception, another exception occurred:

我似乎无法在 python 代码中使用任何 try / except 块捕获异常,因此我可以检测到这一点并重新启动或尝试重新连接。下面是我当前的 python 代码——感谢任何指点!谢谢。

#!/usr/bin/python3

import sys
import time

import arduino
import messageboard

VERBOSE = arduino.VERBOSE
SN = '75835343130351802272'  # arduino uno serial

SIMULATE_ARDUINO = arduino.SIMULATE_ARDUINO
if '-s' in sys.argv:
  SIMULATE_ARDUINO = True

COMMAND_DELAY_TIME = 1  # send a command to servos every n seconds


def main():
  servos = arduino.OpenArduino(sn=SN)
  format_string = '<lff'
  format_byte_size = arduino.SizeOf(format_string)
  azimuth=90

  while True:

    if not servos and not SIMULATE_ARDUINO:
      servos = arduino.OpenArduino(sn=SN)
      if VERBOSE:
        print('Reopening connection')

    if servos or SIMULATE_ARDUINO:
      if azimuth == 90:
        azimuth = 85
      else:
        azimuth = 90
      values = (0, azimuth, 0)

      arduino.StuffObject(servos, values, format_string, format_byte_size)
      if not SIMULATE_ARDUINO:
        servos.send(format_byte_size)


    time.sleep(COMMAND_DELAY_TIME)


if __name__ == "__main__":
  main()

我开始寻找终止 运行 过长的函数调用的方法,潜在的解决方案是使用 StopIt and multiprocessing。但是那些失败了,因为看起来实际发生的事情似乎是 pySerialTransfer 正在产生一个单独的线程来处理串行通信,然后将控制权返回给调用函数,但保持那个单独的线程处于活动状态,并且那个单独的线程是卡在无限错误循环。所以我无法通过终止 long-运行ning 函数调用的解决方案来检测条件。正如评论线程中提到的那样,也没有将异常传递给调用者,因此 try-except 子句也失败了。

但是,我可以检测到的一件事是记录了一个到 stderr 的错误,因此如果我可以重定向它并在那里检测到一个新错误,那么这将使我走上正确的道路。输入 this guidance,这使我转向了这个解决方案:

#!/usr/bin/python3

import sys
import time
import io

import arduino
import messageboard
import contextlib

VERBOSE = arduino.VERBOSE
SN = '75835343130351802272'  # arduino uno serial

SIMULATE_ARDUINO = arduino.SIMULATE_ARDUINO
if '-s' in sys.argv:
  SIMULATE_ARDUINO = True

COMMAND_DELAY_TIME = 1  # send a command to servos every n seconds


def main():
  servos = arduino.OpenArduino(sn=SN)
  format_string = '<lff'
  format_byte_size = arduino.SizeOf(format_string)
  azimuth=90

  while True:

    if not servos and not SIMULATE_ARDUINO:
      servos = arduino.OpenArduino(sn=SN)
      if VERBOSE:
        print('Reopening connection')

    if servos or SIMULATE_ARDUINO:
      if azimuth == 90:
        azimuth = 85
      else:
        azimuth = 90
      values = (0, azimuth, 0)

      # Detects any exceptions that print to stderr but are not raised to caller
      with io.StringIO() as buf, contextlib.redirect_stderr(buf):
        arduino.StuffObject(servos, values, format_string, format_byte_size)
        if not SIMULATE_ARDUINO:
          servos.send(format_byte_size)

        # if there's an exception, probably the connection failed; clear the connection
        # so that it can be reopened at top of loop
        if buf.getvalue():
          servos = None

    time.sleep(COMMAND_DELAY_TIME)


if __name__ == "__main__":
  main()

当我手动拔下 USB 电缆连接到 arduino 然后再将其重新插入时,这会导致以下 stdio 输出,在测试期间是 2x。

$ python3 arduino_servo_test.py 
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
Reopening connection
Reopening connection
Reopening connection
Reopening connection
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
Reopening connection
Reopening connection
Reopening connection
Reopening connection
Reopening connection
Reopening connection
Reopening connection
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)
SENT (12 byte struct): (0, 85, 0)
SENT (12 byte struct): (0, 90, 0)

问题已解决 - 也许不是最优雅的解决方案,但它似乎有效。