Bienvenue sur IndexError.

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

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

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

Accès simultané à périphérique ('i2c', 'modbus') avec plusieurs instances WAMP (asyncio)

0 votes

J'automatise une machine industrielle basé sur Crossbar.io et Autobahn.ws
J'ai plusieurs fichiers indépendants connectés à Crossbar.io, un représentant le ventilateur, l'autre le contact 1, le troisième le lecteur de thermocouples...

Le problème c'est qu'ils doivent passer par le même canal de communication (i2c ou modbus).
Il arrive par moment que j'ai un accès simultané, ce qui pose problème.

Quelle est la meilleur solution pour résoudre ce problème (j'ai regarder asyncio.Lock(), mais cela fonctionne uniquement dans un fichier).

J'ai aussi fait une pile pour ordonner la communication, qui communique avec un push et un pull, mais ça alourdi beaucoup le code (exemple ci-dessous).

class PistonGazo(ApplicationSession):
""" Run piston gazo """
topic = "gazo.pistonGazo"
topicRun = "gazo.pistonGazo.run"
priorityRun = 5

cmdPiston = 'off'

def __init__(self, config):
    ApplicationSession.__init__(self)
    self.config = config
    self.pistons = I2CBus(BUS, 0x21)

@asyncio.coroutine
def onJoin(self, details):
    print('Session {} attached'.format(self.__class__.__name__))
    try:
        yield from self.register(self.runLoop, self.topicRun)
        print("Procedure registered : {}".format(self.topicRun))
    except Exception as e:
        print('Could not register procedure: {}'.format(e))
    try:
        yield from self.register(self.startPiston, self.topic)
        print("Procedure registered : {}".format(self.topic))
    except Exception as e:
        print('Could not register procedure: {}'.format(e))

    while True:
        # TODO: Add loop function hereo
        self.addStack()
        # delay of loop
        yield from asyncio.sleep(DELAY_WHILE)

def startPiston(self, cmd):
    self.cmdPiston = cmd

def addStack(self):
    """Put into loopStack for execution when all bus are free. Take placei"""
    self.call("gazo.stack.add", self.topicRun, self.priorityRun)

def removeStack(self):
    """Send to loopStack that execution are finished and free the place """
    self.call("gazo.stack.remove", self.topicRun)

def runLoop(self):
    """It's the main code that execute every time"""
    if self.cmdPiston == 'on':
        self.pistons.write(0x41, [0x01])
        self.cmdPiston = ''
        print('on')
    elif self.cmdPiston == 'off':
        self.pistons.write(0x41, [0x00])
        self.cmdPiston = ''
        print('off')
    self.removeStack()

J'aurai préféré l'équivalent du Lock avec une pause dans le programme (avant la communication) tant que le périphérique est occupé.

Merci

demandé 8-Jul-2016 par Oryx_r (108 points)
edité 8-Jul-2016 par yoch

1 Réponse

0 votes

(je ne saisis pas bien ce que tu veux dire par "fichier indépendant")

Sans entrer dans toutes les considérations techniques de ton probleme, dans ce type de cas on utilise un composant chargé (et lui seul) d'utiliser le canal d’écriture. Il reçoit les messages a écrire a partir d'une file d'attente, ainsi tes divers accès ne vont pas se marcher sur les pieds. Les trucs a base de Lock, c'est juste une plaie a gérer...

Sinon, je ne comprend pas grand chose a ton code, en particulier ce que addStack et removeStack sont censés faire, donc je ne pourrais pas commenter. Mais c'est plutôt mauvais signe que le code soit si peu clair...

répondu 8-Jul-2016 par yoch (2,312 points)
edité 8-Jul-2016 par yoch

Merci pour la réponse, je sens bien qu'il y a un problème, du coup je réfléchi à changer le tout.

J'ai le code suivant qui gère la pile :

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import asyncio
import time

from autobahn.asyncio.wamp import ApplicationSession
from autobahn.asyncio.wamp import ApplicationRunner

from stackFifoLifo import StackFifo

REALM = "altie"
HOST = "127.0.0.1"
PORT = 9000

class LoopStack(ApplicationSession):
    """ Ordonate executions of programs """
    topic = ""

    def __init__(self, config):
        ApplicationSession.__init__(self)
        self.config = config

        self.loopStack = StackFifo()
        self.startWatchDog = 0
        self.delayWatchDog = 3

    @asyncio.coroutine
    def onJoin(self, details):
        print('Session {} attached'.format(self.__class__.__name__))
        # TODO: Add suscribtion or RPC here
        try:
            yield from self.register(self.addToStack, 'gazo.stack.add')
            print("Procedure registered : {}".format('gazo.stack.add'))
        except Exception as e:
            print('Could not register procedure: {}'.format(e))
        try:
            yield from self.register(self.removeFromStack, 'gazo.stack.remove')
            print("Procedure registered : {}".format('gazo.stack.remove'))
        except Exception as e:
            print('Could not register procedure: {}'.format(e))

        while True:
            # TODO: Add loop function here
            timeNow = time.time()
            if self.topic == "" and not self.loopStack.emptyStack():
                self.startWatchDog = timeNow
                print(self.loopStack.copyStack())
                self.topic, *args = self.loopStack.element()
                try:
                    self.call(self.topic, *args)
                except Exception as e:
                    print('Error RPC: ' + self.topic)
                    print('Exception: {}'.format(e))
            elif self.topic != "" and timeNow - self.startWatchDog > self.delayWatchDog:
                self.removeFromStack(self.topic)
                # delay of loop
            yield from asyncio.sleep(0.00001)

    def addToStack(self, topic, priority, *args):
        self.loopStack.stack((topic, *args))

    def removeFromStack(self, topic):
        try:
            self.loopStack.unstack()
        except ValueError as e:
            print("Error: not unstack ({})".format(e))
        self.topic = ""

runner = ApplicationRunner(
    url="ws://" + HOST + ":" + str(PORT) + "/ws",
    realm=REALM,
)

if __name__ == "__main__":
    runner.run(LoopStack)

Le addStack ajoute ma fonction à la pile et il attend, une fois que le code peut s'exécuter, la procedure loopStack est appelé et à la fin de cette procédure, removeStack est appelé pour libérer la pile.

Sinon, je pense effectivement utiliser un seul composant qui gère le canal d'écriture et je pensais utiliser redis pour communiquer avec les autres composants.

...