2016-03-23 12 views
0

Ich habe eine Beaglebone Black Verbindung zu einem CAN-Bus-Geräte: Batterie.Wie man mehrere Ioloop in Tornado benutzt und Daten zwischen den Ioloop (s) teilt?

Ein Tornado Web läuft auf Beaglebone Black als GUI.

CAN-Bus-Leseschleife halten Lesen von Daten von CAN-Bus den Status der Batterie Instanz zu aktualisieren

Aber wie kann ich die beiden IOLOOP machen zusammen arbeiten und Batterie Instanz teilen?

enter image description here

Tornado web:

class Battery(object): 
    status = {} 



class API_Handler(web.RequestHandler): 
    def get(self, dev, cmd): 
     if cmd == 'data': 
      self.write(self.application.battery0.status) 


class Application(web.Application): 
    def __init__(self): 

     self.battery0 = Battery('bat0')  

     routing = [ 
      (r'/api/battery/(data|)', API_Handler), 
     ] 

     settings = { 
      'template_path': os.path.join(os.path.dirname(__file__), "templates"), 
      'static_path': os.path.join(os.path.dirname(__file__), "static"), 
     } 

     web.Application.__init__(self, routing, debug=True, **settings) 


if __name__ == "__main__": 
    import tornado 

    app = Application() 
    app.listen(address='0.0.0.0', port=8888) 
    tornado.ioloop.IOLoop.instance().start() 

CAN-Bus-Leseschleife, Code:

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
import errno 
import functools 
import tornado.ioloop 
import socket 
import struct 


can_frame_fmt = "=IB3x8s" 
can_frame_size = struct.calcsize(can_frame_fmt) 

def build_can_frame(can_id, data): 
    can_dlc = len(data) 
    data = data.ljust(8, b'\x00') 
    return struct.pack(can_frame_fmt, can_id, can_dlc, data) 

def dissect_can_frame(frame): 
    can_id, can_dlc, data = struct.unpack(can_frame_fmt, frame) 
    return (can_id, can_dlc, data[:can_dlc]) 

def connection_ready(sock, fd, events): 
    while True: 
     try: 
      cf, addr = sock.recvfrom(can_frame_size) 
     except socket.error as e: 
      if e.args[0] not in (errno.EWOULDBLOCK, errno.EAGAIN): 
       raise 
      return 
     dissect_can_frame(cf) 


if __name__ == '__main__': 
    sock = socket.socket(socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW) 
    sock.bind(('can0',)) 
    sock.setblocking(0) 

    io_loop = tornado.ioloop.IOLoop.current() 
    callback = functools.partial(connection_ready, sock) 
    io_loop.add_handler(sock.fileno(), callback, io_loop.READ) 
    io_loop.start() 
+0

Wenn Sie Tornado verwenden, das in der Lage ist [WebSockets] (https://en.wikipedia.org/wiki/WebSocket), warum der Benutzer senden hat ein Befehl um einen Status zu bekommen? Sie müssen nur einen Thread starten, der den Batteriestatus mit der angegebenen Häufigkeit liest und den Browser automatisch über eine Websocket-Verbindung aktualisiert. – yegorich

+0

@yegorich Ich kenne Ihre Bedenken. aber das ist nur ein Beispiel für cmd. Ich habe viele andere von cmd auch. Ein weiterer Grund ist, dass die Daten innerhalb von Änderungen viel häufiger sind, aber die GUI nicht als die gleiche Häufigkeit aktualisiert werden muss. Der dritte Grund ist, dass andere Module, die dieselbe API verwenden, Daten in ihrer eigenen Häufigkeit anfordern können. –

Antwort

0

Wie ich Sie laufen zwei Anwendungen sehen können, so wird es schwierig sein, zu teilen Instanz der Batterie. Erste Lösung - Kombinieren Sie alle Funktionen in einer Anwendung, so dass die Batterieinstanz einfach verfügbar ist, aber Sie werden vor Herausforderungen stehen, wenn Sie HTTP-Anforderungen bedienen und Socket-Ereignisse von CAN in einem ioloop verarbeiten müssen.

Also hier ist eine andere Lösung, halten Sie zwei Anwendung, aber nicht versuchen, eine Battery-Instanz zu teilen, nur eine HTTP-Anfrage von CAN-Listener zu Ihrer GUI-App.

Zum Beispiel in CAN-Datei:

from tornado.httpclient import AsyncHTTPClient 

... 

def connection_ready(sock, fd, events): 
    while True: 
     try: 
      cf, addr = sock.recvfrom(can_frame_size) 
     except socket.error as e: 
      if e.args[0] not in (errno.EWOULDBLOCK, errno.EAGAIN): 
       raise 
      return 
     http_client = AsyncHTTPClient() 
     http_client.fetch("http://localhost:8888/update-battery", 
          method="POST", 
          body="can_id={}&can_dlc={}&data={}".format(
           dissect_can_frame(cf))) 

wäre besser urlencode zu verwenden, aus Python urllib to encode body wenn Sie Bytes in Daten haben.

In GUI-Datei:

class Battery_Update_Handler(web.RequestHandler): 
    def post(self): 
     self.application.battery0.status = dict(
      can_id=self.get_argument("can_id", None), 
      can_dlc=self.get_argument("can_dlc", None), 
      data=self.get_argument("data", None)) 


class Application(web.Application): 
    def __init__(self): 

     self.battery0 = Battery('bat0')  

     routing = [ 
      (r'/api/battery/(data|)', API_Handler), 
      (r'/update-battery', Battery_Update_Handler) 
     ] 

     settings = { 
      'template_path': os.path.join(os.path.dirname(__file__), "templates"), 
      'static_path': os.path.join(os.path.dirname(__file__), "static"), 
     } 

     web.Application.__init__(self, routing, debug=True, **settings) 
+0

Was passiert, wenn der CAN-Bus in einer Sekunde 1000 Nachrichten empfängt? Ist es langsam, wenn wir die POST-Methode verwenden? –

+0

Das ist eine theoretische Frage. Ich habe kein CAN benutzt, also habe ich Wikipedia abgecheckt und gefunden, dass es eine maximale Geschwindigkeit von 1Mb/s hat, während die Geschwindigkeit des Zugriffs auf localhost durch die CPU-Geschwindigkeit begrenzt ist ([nach diesem]) (http://serverfault.com/questions/234223/how) -fast-is-127-0-0-1)), daher sollten Sie sich nicht mit Geschwindigkeitsproblemen herumschlagen. Wie auch immer, versuchen Sie es zu implementieren und simulieren Sie massiv eingehende Daten über CAN. Wenn meine Annahme falsch ist, könnten wir bei der ersten Lösung bleiben. – sanddog

+0

Eigentlich kommen die meisten IO von CAN-Bus statt HTTP-Anfrage. Wie auch immer, ich werde zuerst die Simulation machen, Danke. –