pyserial读取烧瓶中的串口(可能使用gevent)

时间:2012-12-24 15:09:01

标签: python flask pyserial server-sent-events

我正在构建一个网络服务器,需要读取(并继续阅读)正在运行的机器的串口。
目的是能够读取条形码扫描仪,并使用服务器发送的事件来更新带有读取条形码的浏览器。

我正在使用烧瓶来做这件事。我浏览过,有些实现只需要烧瓶,有人说我需要像Gevent这样的异步库,还有一些人甚至说我需要Gevent和Redis或RabbitMQ之类的某种队列。

我尝试将我的代码基于stackoverflow here上的一个非常简单的示例。我主要工作,但我遇到了一些问题;

  • 在Chrome中,添加了一个跨域错误 Access-Control-Allow-Origin标题我可以让它在FireFox中工作, 但Chrome仍无效。只有FF支持才是正确的 SSE跨源?我需要它来支持CORS,因为浏览器会 需要从单独的机器加载条形码数据。
  • 在每条消息之后,浏览器会在控制台中显示条形码,但是 它然后关闭连接,只在大约3后再打开它 秒。看来这起源于Flask,它给了我数据 然后停下来。
  • 另外,我想知道这将如何在负载下执行。我的意思是,烧瓶 保持文本/事件流mimetype的连接打开。如果 多个客户连接,一段时间后不会阻塞烧瓶,因为 所有的连接都会饱和吗?

我的代码如下(为清晰起见缩短了)

服务器端:

from flask import Flask
import flask
import serial

app = Flask(__name__)
app.debug = True

def event_barcode():
    ser = serial.Serial()
    ser.port = 0
    ser.baudrate = 9600
    ser.bytesize = 8
    ser.parity = serial.PARITY_NONE
    ser.stopbits = serial.STOPBITS_ONE
    ser.open()
    s = ser.read(7)
    yield 'data: %s\n\n' % s

@app.route('/barcode')
def barcode():
    newresponse = flask.Response(event_barcode(), mimetype="text/event-stream")
    newresponse.headers.add('Access-Control-Allow-Origin', '*')
    return newresponse

if __name__ == '__main__':
    app.run(port=8080, threaded=True)

客户端:

    <!DOCTYPE HTML>
<html>
<head>
    <meta http-equiv=Content-Type content="text/html; charset=utf-8">
    <title>TEST</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js" type="text/javascript" charset="utf-8"></script>
    <script>

        $(document).ready(function(){
            if (!!window.EventSource) {
                console.log('SSE supported.');
                var source = new EventSource('http://localhost:8080/barcode');

                source.addEventListener('message', function(e) {
                  console.log(e.data);
                }, false);

                source.addEventListener('open', function(e) {
                  console.log('Connection was opened.');
                }, false);

                source.addEventListener('error', function(e) {
                  if (e.readyState == EventSource.CLOSED) {
                    console.log('Connection was closed.');
                  }
                }, false);

            } else {
                console.log('SSE notsupported.');
            }
        });

    </script>
</head>

<body>

</body>
</html>

我在这里看到的更多信息: http://www.socketubs.net/2012/10/28/Websocket_with_flask_and_gevent/ http://sdiehl.github.com/gevent-tutorial/#chat-server

我希望有人可以解决我的问题,并且可能指出我的解决方案,针对跨源和3秒延迟问题。

感谢。

2 个答案:

答案 0 :(得分:4)

以下是一些可能有用的要点(我的意思是发布基于'django-sse'的'flask-sse'之类的东西):

https://gist.github.com/3680055

https://gist.github.com/3687523

也很有用 - https://github.com/jkbr/chat/blob/master/app.py

'RedisSseStream'类使用redis作为后端在线程之间进行通信(尽管gevent可以这样做吗?),并且'监听'redis发布事件。

虽然'PeriodicSseStream'不需要redis,但它不能在烧瓶线程之间进行通信,即使用来自另一个响应的信息;没有像redis这样的东西,单独的线程(流和服务于另一个用户的线程)无法通信。

正如Janus所说,生成器只返回一个结果 - 它必须产生多个,并且在这种情况下它必须被包含在一个循环中,在每次串行轮询后无限地产生;您还需要决定限制轮询的内容,是否受时间限制(定期轮询)或其他内容(例如,如果已经花了一段时间来读取串口)?

我真的不太了解sse的性能,或者支持它的程度如何(以及跨域),但是如果你考虑socket.io,你可以使用this来改进网络套接字performance?

答案 1 :(得分:3)

回答我自己的问题

  1. 目前看来确实只有Firefox支持SSS的CORS - &gt; article
  2. 在Janus Troelsen的帮助下,我想出了如何保持 连接打开并在整个线路上发送几个条形码(参见代码 下文)
  3. 性能方面似乎我只能建立一个连接,但是 这可能是因为我只有一个串口,后续 连接无法再打开串口。我认为它会起作用 从烧瓶,但有socketio和gevents的东西将会出现 更好,因为它更适合这项工作。有一个有趣的 文章here
  4. 代码:

    import flask
    import serial
    from time import sleep
    
    app = flask.Flask(__name__)
    app.debug = True
    
    def event_barcode():
        messageid = 0
        ser = serial.Serial()
        ser.port = 0
        ser.baudrate = 9600
        ser.bytesize = 8
        ser.parity = serial.PARITY_NONE
        ser.stopbits = serial.STOPBITS_ONE
        ser.timeout = 0
        try:
            ser.open()
        except serial.SerialException, e:
             yield 'event:error\n' + 'data:' + 'Serial port error({0}): {1}\n\n'.format(e.errno, e.strerror)
             messageid = messageid + 1
        str_list = []
        while True:
            sleep(0.01)
            nextchar = ser.read()
            if nextchar:
                str_list.append(nextchar)
            else:
                if len(str_list) > 0:
                    yield 'id:' + str(messageid) + '\n' + 'data:' + ''.join(str_list) + '\n\n'
                    messageid = messageid + 1
                    str_list = []
    
    @app.route('/barcode')
    def barcode():
        newresponse = flask.Response(event_barcode(), mimetype="text/event-stream")
        newresponse.headers.add('Access-Control-Allow-Origin', '*')
        newresponse.headers.add('Cache-Control', 'no-cache')
        return newresponse
    
    if __name__ == '__main__':
        app.run(port=8080, threaded=True)
    

    因为我想支持多种浏览器,所以SSE现在不适合我。我将研究websockets并尝试从中开始工作。