将包含串行初始化的 python 脚本导入 Flask 应用程序

Importing python script that contain serial initialization into flask app

我想以脚本的形式导入一些函数,我们称它为 controller.py,作为 Web 服务导入到 Flask 应用程序中。让我们调用烧瓶应用程序 api.py.

问题是,在controller.py中,有一个pyserial声明。

controller.py:

import serial

ser = serial.Serial('COM31', 9600,timeout=2)

def serial_function(foo):
    ser.write(foo)
    reply = ser.read()
    return reply

api.py:

from flask import Flask
import controller as cont

app = Flask(__name__)

@app.route('/function/<foo>',methods=['GET'])
def do_function(foo):
    data=cont.serial_function(foo)
    return data

if __name__ == '__main__':
    app.run('0.0.0.0', 80,True)

但是我得到了这个错误:

raise SerialException("could not open port %s: %s" % (self.portstr, ctypes.WinError()))
serial.serialutil.SerialException: could not open port COM31: [Error 5] Access is denied.

好像是Flask反复导入controller.py,串口重新初始化

有什么方法可以实现我上面描述的目标吗?

您的代码的主要问题是串行对象的创建直接在您的模块代码中。这样,每次导入模块时,python 都会 load/interpret 文件,并且通过这样做,将执行在模块根级别找到的所有代码。 (如果您想深入了解,可以参考这个优秀的答案:What does if __name__ == "__main__": do?

此外,在调试模式下,Flask 将启动 2 个进程(一个用于监视源代码,并在更改时重新启动第二个进程,这是真正处理请求的进程),在生产模式下,您可以启动服务器时创建更多的线程或进程,通过这样做,您的模块至少导入两次 => 串行打开冲突。

一个可能的解决方案是从您的模块中删除串行端口的初始化并在您的方法中使用 context manager syntax :

def serial_function(foo):
    with serial.Serial('COM31', 9600,timeout=2) as ser:
        ser.write(foo)
        reply = ser.read()
        return reply

这样,您将在每次读取时打开(和关闭)串行端口。

但是,如果您有多个客户端同时向您的网络服务器发出请求,您仍然需要处理并发访问。

编辑: 如果,正如您在评论中所说,您只需要打开一次串行端口,则需要将其封装在一个特定的对象中(可能使用单例模式),如果尚未打开,该对象将负责打开串行端口:

class SerialProxy:
    def __init__(self):
        self.__serial = None

    def serial_function(self, foo):
        if self.__serial is None:
            self.__serial = serial.Serial('COM31', 9600,timeout=2)
        self.__serial.write(foo)
        reply = self.__serial.read()
        return reply