Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Comment "WAMPER" du code Twisted ?

+2 votes

J'ai réalisé et codé un une boite noire pour enregistrer les paramètres de vol de mon ULM.
Celle-ci repose sur un Arduino Yun, des capteurs Inclinomètres, accéléromètres, Pressions, Température, ...

En bricolant le script de demo Autobahn "serial2ws" je publie les infos des capteurs sur mon iphone/Ipad (super cool !)

Je souhaite ajouter des informations de localisation.
Apres de long jurs de galère et ayant fait le constat que l’implémentation gps de twisted est non fonctionnelle, (de mon point de vue), j'ai codé un script python utilisant gpsd pour lire le gps via le réseau (position, vitesse de fond, ...)

Je n'arrive pas à "l’intégrer" dans dans du WAMP (clone de serial2ws)

Pouvez-vous m'aider en me donnant des pistes ?

Voici le script:

from twisted.internet.protocol import Protocol, ReconnectingClientFactory
from twisted.internet import reactor
from twisted.python import log
import json

host = '127.0.0.1'
port = 2947


class Gps(Protocol):
    def connectionMade(self):
        """
        start gpsd pooling command on loopback using json format
        ?WATCH={"enable":true,"json":true} 
        """

        self.transport.write('?WATCH={"enable":true,"json":true}')

    def dataReceived(self, data):
        """
        data is not pure json and have CRLF ending !
        only TPV is required at this stage
        {"class":"SKY","tag":"GSV","device":"/dev/tty.SLAB_USBtoUART","xdop":0.67,"ydop":0.75,"vdop":1.90,"tdop":1.15,"hdop":1.30,"gdop":2.29,"pdop":2.30,"satellites":[{"PRN":25,"el":79,"az":31,"ss":24,"used":true},{"PRN":29,"el":66,"az":194,"ss":24,"used":true},{"PRN":12,"el":41,"az":79,"ss":17,"used":false},{"PRN":31,"el":40,"az":306,"ss":27,"used":true},{"PRN":14,"el":33,"az":240,"ss":38,"used":true},{"PRN":2,"el":32,"az":71,"ss":25,"used":true},{"PRN":24,"el":14,"az":144,"ss":27,"used":true},{"PRN":8,"el":13,"az":311,"ss":14,"used":false},{"PRN":6,"el":10,"az":31,"ss":0,"used":false}]}
        {"class":"TPV","tag":"RMC","device":"/dev/tty.SLAB_USBtoUART","mode":3,"time":"2015-03-25T18:41:13.766Z","ept":0.005,"lat":48.703760000,"lon":2.035601667,"alt":72.000,"epx":10.042,"epy":11.301,"epv":43.700,"track":164.0000,"speed":0.000,"climb":0.000,"eps":22.60,"epc":87.40}
        """

        tmp = data.rstrip('\r\n')

        if tmp.find('TPV') > 0 :
            gps = json.loads(tmp)
            print 'JSON ', gps


class GpsClientFactory(ReconnectingClientFactory):

    def startedConnecting(self, connector):
        print 'Started to connect.'

    def buildProtocol(self, addr):
        print 'Connected.'
        print 'Resetting reconnection delay'
        self.resetDelay()
        return Gps()

    def clientConnectionLost(self, connector, reason):
        print 'Lost connection.  Reason:', reason
        ReconnectingClientFactory.clientConnectionLost(self, connector, reason)

    def clientConnectionFailed(self, connector, reason):
        print 'Connection failed. Reason:', reason
        ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)


reactor.connectTCP(host, port, GpsClientFactory())


reactor.run()
demandé 27-Mar-2015 par lla4u (126 points)
rouvert 31-Mar-2015 par Sam

Pourrais-tu préciser ce que tu souhaites publier comme informations et comment tu obtiens ces dernières ?

Après ouverture de session et envoi de la commande

'?WATCH={"enable":true,"json":true}' 

gpsd publie toute les secondes:

"""
{"class":"TPV","tag":"RMC","device":"/dev/tty.SLAB_USBtoUART","mode":3,"time":"2015-03-25T18:41:13.766Z","ept":0.005,"lat":48.xxxxxx,"lon":2.xxxxx,"alt":72.000,"epx":10.042,"epy":11.301,"epv":43.700,"track":164.0000,"speed":0.000,"climb":0.000,"eps":22.60,"epc":87.40}
"""

Je souhaite publier Latitude (lat), longitude (lon), speed (speed).
La variable gps contient les informations collectées sur le réseau (json)
Je ne comprend pas comment traiter/integrer GpsClientFactory dans ApplicationSession.

gpsd publie toute les secondes:

Tu veux dire que, toutes les secondes, dataReceived est appelée ?

Tu ne peux pas faire hériter la classe Gps de ApplicationSession ?

Je confirme que dataRecived est appelé toutes les secondes.

Le demon gpsd :
gpsd /dev/tty.SLABUSBtoUART -S 2947
Hornet:gps lla$ ps aux | grep gps
lla 14486 0,0 0,0 2424580 376 s000 R+ 4:44 0:00.00 grep gps
lla 14480 0,0 0,0 2454960 2768 ?? Ss 4:44 0:00.01 gpsd /dev/tty.SLAB
USBtoUART -S 2947
Hornet:gps lla$ netstat -an | grep 2947
tcp6 0 0 ::1.2947 . LISTEN
tcp4 0 0 127.0.0.1.2947 . LISTEN

le champs time ! :
Hornet:gps lla$ python TGPSD.py
Started to connect.
Connected.
Resetting reconnection delay
JSON {u'epx': 22.519, u'epy': 15.359, u'epv': 55.2, u'ept': 0.005, u'lon': 2.xxxxxx, u'eps': 45.04, u'epc': 110.4, u'lat': 48.xxxxxx, u'tag': u'RMC', u'track': 208.3, u'mode': 3, u'time': u'2015-03-28T15:55:05.773Z', u'device': u'/dev/tty.SLABUSBtoUART', u'climb': 0.0, u'alt': 79.1, u'speed': 0.0, u'class': u'TPV'}
JSON {u'epx': 22.519, u'epy': 15.359, u'epv': 55.2, u'ept': 0.005, u'lon': 2.xxxxxx, u'eps': 45.04, u'epc': 110.4, u'lat': 48.xxxxxx, u'tag': u'RMC', u'track': 208.3, u'mode': 3, u'time': u'2015-03-28T15:55:06.773Z', u'device': u'/dev/tty.SLAB
USBtoUART', u'climb': 0.0, u'alt': 79.1, u'speed': 0.0, u'class': u'TPV'}
JSON {u'epx': 22.519, u'epy': 15.359, u'epv': 32.2, u'ept': 0.005, u'lon': 2.xxxxxx, u'eps': 45.04, u'epc': 87.4, u'lat': 48.xxxxxx, u'tag': u'RMC', u'track': 208.3, u'mode': 3, u'time': u'2015-03-28T15:55:07.773Z', u'device': u'/dev/tty.SLABUSBtoUART', u'climb': 0.0, u'alt': 79.1, u'speed': 0.0, u'class': u'TPV'}
JSON {u'epx': 22.519, u'epy': 15.359, u'epv': 32.2, u'ept': 0.005, u'lon': 2.xxxxxx, u'eps': 45.04, u'epc': 64.4, u'lat': 48.xxxxxx, u'tag': u'RMC', u'track': 208.3, u'mode': 3, u'time': u'2015-03-28T15:55:08.773Z', u'device': u'/dev/tty.SLAB
USBtoUART', u'climb': 0.0, u'alt': 79.1, u'speed': 0.0, u'class': u'TPV'}

Si je regarde l'exemple serial2ws proposé, la publication est initiée dans la classe McuProtocol(LineReceiver)

(la frequence actuelle est de 25Hz)
"""
class McuProtocol(LineReceiver):

"""
MCU serial communication protocol.
"""

# need a reference to our WS-MCU gateway factory to dispatch PubSub events
##
def __init__(self, session, debug=False):
    self.debug = debug
    self.session = session

def connectionMade(self):
    print('Serial port connected.')

def lineReceived(self, line):
    if self.debug:
        print("Serial RX: {0}".format(line))

    try:
        # parse data received from MCU
        ##
        data = [x for x in line.split()]
    except ValueError:
        print('Unable to parse value {0}'.format(line))
    else:
        # create payload for WAMP event
        ##
        payload = {u'id': data[0], u'value': data[1]}

        # publish WAMP event to all subscribers on topic
        ##
        self.session.publish(u"com.myapp.mcu.on_analog_value", payload)

"""

D'après la ligne 95, il faut que tu passes ton composant en argument à ta classe recevant les données. Tu pourrais alors :

  • Créer une classe Component héritant de ApplicationSession
  • Exécuter les deux dernières lignes de ton code depuis ton composant, i.e. démarrer ta factory depuis ton composant, en passant le composant en argument à la factory
  • La factory transmet la référence vers le composant WAMP à la classe Gps quand elle l'instancie dans buildProtocol

Je vais gratter la chose.

J'avoue ne pas bien comprendre tes explications.
Je suis nouveau dans le langage Twisted et Wamp voir python.

Pourrais tu me donner le squelette de ce que tu préconises ?
Quelques lignes de code que je puisse comprendre la methode/approche.

Je ne suis pas certain du résultat, n'ayant pas non plus une expérience très fournie avec Twisted, mais tu pourrais faire un truc comme ça :

class Gps(Protocol):

    def __init__(self, session):
        self.session = session

    def dataReceived(self, data):
        tmp = data.rstrip('\r\n')

        if tmp.find('TPV') > 0 :
            gps = json.loads(tmp)
            print 'JSON ', gps
            self.session.publish("com.app.gps", gps)


class GpsClientFactory(ReconnectingClientFactory):

    def __init__(self, session):
        self.session = session

    def startedConnecting(self, connector):
        print 'Started to connect.'

    def buildProtocol(self, addr):
        print 'Connected.'
        print 'Resetting reconnection delay'
        self.resetDelay()
        return Gps(self.session)


class GpsComponent(ApplicationSession):

    def onJoin(self, details):
        reactor.connectTCP(host, port, GpsClientFactory(self))
        reactor.run()

Si ça marche, faut pas fermer :) Faut soit marquer la réponse qui t'as aidé comme "acceptée", soit écrire la réponse toi-même.

Je ne trouve pas comment marquer la réponse (commentaire) comme accepté.
Sam pourrais tu accepter la réponse de Vayel à ma place?

Si si c'est bon, c'est le gros check vert.

2 Réponses

0 votes
 
Meilleure réponse

Je ne suis pas certain du résultat, n'ayant pas non plus une expérience très fournie avec Twisted, mais tu pourrais faire un truc comme ça :

class Gps(Protocol):

    def __init__(self, session):
        self.session = session

    def dataReceived(self, data):
        tmp = data.rstrip('\r\n')

        if tmp.find('TPV') > 0 :
            gps = json.loads(tmp)
            print 'JSON ', gps
            self.session.publish("com.app.gps", gps)


class GpsClientFactory(ReconnectingClientFactory):

    def __init__(self, session):
        self.session = session

    def startedConnecting(self, connector):
        print 'Started to connect.'

    def buildProtocol(self, addr):
        print 'Connected.'
        print 'Resetting reconnection delay'
        self.resetDelay()
        return Gps(self.session)


class GpsComponent(ApplicationSession):

    def onJoin(self, details):
        reactor.connectTCP(host, port, GpsClientFactory(self))
        reactor.run()
répondu 1-Avr-2015 par Vayel (1,058 points)
sélectionné 1-Avr-2015 par lla4u
+1 vote

A mon avis, tu peux juste faire app.run() juste en ressous de ton reactor.connectTCP() et sauter reactor.run() et ça devrait marcher. reactor est un singleton de toute façon.

répondu 30-Mar-2015 par Sam (5,000 points)
...