首页 > 解决方案 > 将包含串行初始化的 python 脚本导入烧瓶应用程序

问题描述

我想以脚本的形式将一些函数(我们称之为 controller.py)导入到烧瓶应用程序中作为 Web 服务。我们将烧瓶应用程序称为 api.py。

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

控制器.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,串口重新初始化。

有什么方法可以实现如上所述的我想要做的事情吗?

标签: pythonflaskpyserial

解决方案


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

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

一个可能的解决方案是从您的模块中删除串行端口的初始化,并在您的方法中使用上下文管理器语法

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

推荐阅读