2016-03-23 1 views
0

У меня есть Beaglebone Black, подключаемый к устройствам шины CAN: Аккумулятор.Как использовать несколько ioloop в торнадо и обмениваться данными между ioloop (s)?

Торнадо веб работает на Beaglebone Черный как графический интерфейс.

CAN цикл чтения шины продолжайте чтение данных из CAN шины, чтобы обновить состояние экземпляра батареи

Но как я могу сделать два IOLOOP работать вместе и совместно экземпляр батареи?

enter image description here

Торнадо Веб:

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 шина чтение цикл, код:

#!/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

Если вы используете Торнадо, который способен [WebSockets] (https://en.wikipedia.org/wiki/WebSocket), поэтому пользователь должен отправить команда для получения статуса? Вам просто нужно запустить поток, который будет считывать состояние батареи с указанной частотой и автоматически обновлять браузер через соединение с веб-разъемом. – yegorich

+0

@yegorich Я знаю ваше беспокойство. но это всего лишь пример cmd. У меня также много других команд. Другая причина заключается в том, что данные внутри изменяются гораздо чаще, но графический интерфейс не требуется обновлять с той же частотой. Третья причина заключается в том, что это позволяет другому модулю использовать тот же API для запроса данных на своей собственной частоте. –

ответ

0

Как я вижу, что вы работаете два приложения, так что это будет трудно разделить экземпляр батареи. Первое решение - объединить все функции в одном приложении, так что экземпляр Battery будет просто доступен, но вы столкнетесь с проблемами с обслуживанием HTTP-запросов и обработкой событий сокетов с CAN в одном ioloop.

Итак, вот еще одно решение, держите два приложения, но не пытайтесь разделить экземпляр батареи, просто сделайте http-запрос от CAN-слушателя к вашему графическому интерфейсу.

Например, в файле CAN:

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))) 

Было бы лучше использовать UrlEncode из питона urllib to encode body, если у вас есть байт данных.

В GUI файле:

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

Что делать, если CAN-шина получает 1000 сообщений за одну секунду, будет ли медленным использование метода POST? –

+0

Это своего рода теоретический вопрос. Я не использовал CAN, поэтому проверил википедию и обнаружил, что максимальная скорость 1 Мбит/с, а скорость доступа к локальному хосту ограничена скоростью процессора ([согласно этому] (http://serverfault.com/questions/234223/how -fast-is-127-0-0-1)), поэтому вы не должны сталкиваться с проблемой скорости. В любом случае попробуйте реализовать его и смоделировать массивные входящие данные через CAN, если мое предположение ошибочно, мы можем придерживаться первого решения. – sanddog

+0

Фактически, большая часть IO приходит из шины CAN вместо HTTP-запроса. Во всяком случае, я сначала сделаю симуляцию, спасибо. –