Outils pour utilisateurs

Outils du site


using_perspective_broker

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentesRévision précédente
Prochaine révision
Révision précédente
using_perspective_broker [2022/11/29 09:13] – [Exemple 3] sergeusing_perspective_broker [2022/12/16 10:42] (Version actuelle) – [Using Perspective Broker] serge
Ligne 1: Ligne 1:
 ======Using Perspective Broker====== ======Using Perspective Broker======
 +{{ :media_01:apkv_20.svg |}}
 +
 +{{ ::labo.svg |}}
 +
 <WRAP center round info centeralign 60%> <WRAP center round info centeralign 60%>
-**Niveau Pro et intergalactique !**+**Niveau Pro et intergalactique !**\\ 
 +Cette API: 
 +  * a entre 15 et 20 ans 
 +  * n'est que vaguement maintenu 
 +  * un exemple au moins ne marche pas 
 +  * le tranfert de datas simples marche 
 +  * le transfert d'objets complexes ne marche pas 
 +  * les exemples ne respectent pas le PEP 8, c'est imbriqué au possible
 </WRAP> </WRAP>
  
 <WRAP center round box 60% > <WRAP center round box 60% >
   * **{{tagpage>perspectivebroker|Toutes les pages Twisted Perspective Broker}}**   * **{{tagpage>perspectivebroker|Toutes les pages Twisted Perspective Broker}}**
-  * **[[perspective_broker|Page précédente]]**+  * **[[perspective_broker|Page précédente]]** Python: Twisted Perspective Broker 
 +  * **[[managing_clients_of_perspectives|Page suivante]]** Managing Clients of Perspectives 
 +  * **[[https://github.com/sergeLabo/PB|Les fichiers sources]]**
 </WRAP> </WRAP>
  
Ligne 13: Ligne 26:
  
 =====Exemple simple===== =====Exemple simple=====
-**Passez votre chemin**, cela revient à expliquer la factorisation à un élève de 5ème, en lui parlant de nombres complexes et de cosinus hyperbolique: pourquoi utiliser des "lambda" qui ne sont pas pythoniques ???? m(+**Passez votre chemin**, cela revient à expliquer la factorisation à un élève de 5ème, en lui parlant de nombres complexes et de cosinus hyperbolique: pourquoi utiliser des "lambda" qui ne sont pas pythoniques ???? m( et en plus gérer les erreurs !\\ 
 +Tout simplement pour avoir un exemple court, malheureusement comme c'est court, on y comprends rien, pour un Hello World, c'est raté.\\ 
 +Pas grave, nous allons comprendre la suite ! 8-)
  
  
Ligne 75: Ligne 90:
 Si votre serveur donne une référence(à un objet) à un client, puis que ce client renvoie la référence au serveur, le serveur se retrouvera avec le même objet qu'il a donné à l'origine. La couche de sérialisation surveille le retour des identifiants de référence et les transforme en objets réels. Vous devez rester conscient de l'emplacement de l'objet : s'il est de votre côté, vous effectuez des appels de méthode réels. Si c'est de l'autre côté, vous faites des .callRemote()(appel à distance).\\ Si votre serveur donne une référence(à un objet) à un client, puis que ce client renvoie la référence au serveur, le serveur se retrouvera avec le même objet qu'il a donné à l'origine. La couche de sérialisation surveille le retour des identifiants de référence et les transforme en objets réels. Vous devez rester conscient de l'emplacement de l'objet : s'il est de votre côté, vous effectuez des appels de méthode réels. Si c'est de l'autre côté, vous faites des .callRemote()(appel à distance).\\
 Cela crée 2 univers identiques. Les objets sont les mêmes mais pas au mêmes endroits: un sur le serveur, un sur le client. Cela crée 2 univers identiques. Les objets sont les mêmes mais pas au mêmes endroits: un sur le serveur, un sur le client.
 +
 +En renommant astucieusement les variables, c'est beaucoup plus clair !!
 ====Code==== ====Code====
 <file python pb2server.py> <file python pb2server.py>
 +"""
 +Accès aux objets de ce serveur depuis le client.
 +Cet exemple montre que c'est bien les mêmes, mais les univers parallèles créent
 +un peu de trouble.
 +
 +Le client vient accéder ici client_accessible = ObjetAccessibleParLeClient()
 +
 +Le client appelle
 +    - remote_getTwo avec self.oneRef.callRemote("getTwo").addCallback(self.step2)
 +    - remote_checkTwo avec self.oneRef.callRemote("checkTwo", two)
 +
 +"""
 +
 from twisted.internet import reactor from twisted.internet import reactor
 from twisted.spread import pb from twisted.spread import pb
-class Two(pb.Referenceable): + 
-    def remote_print(self, arg): +class ObjetAccessibleParLeClient(pb.Referenceable): 
-        print("class Two dans serveur: two.print was given", arg) +    """Cette class permet de créer un objet pour en avoir un à partager, 
-         +    mais il ne fait rien dans cet exemple 
-class One(pb.Root): +    L'objet accessible par le client est client_accessible 
-    def __init__(self, two): +    """ 
-        # pb.Root.__init__(self)   # pb.Root doesn't implement __init__ +    pass 
-        self.two = two + 
-    def remote_getTwo(self): +class ObjetPrincipal(pb.Root): 
-        """Méthode appelée par getTwo chez le client avec +    def __init__(self, client_accessible): 
-        self.oneRef.callRemote("getTwo").addCallback(self.step2)""" +        self.client_accessible = client_accessible 
-        print("One.getTwo(), returning my two called", self.two) + 
-        return self.two +    def remote_getObject(self):  # ancien remote_getTwo 
-    def remote_checkTwo(self, new_two): +        """Méthode appelée par "getTwo" chez le client avec 
-        """Méthode appelée par checkTwo chez le client +        self.oneRef.callRemote("getTwo").addCallback(self.step2) 
-        self.oneRef.callRemote("checkTwo", two)""" +        """ 
-        print("appelé par quoi ?") +        print(f"One.getTwo(), returning my client_accessible called", self.client_accessible) 
-        print("One.checkTwo(): comparing my two", self.two) +        return self.client_accessible 
-        print("One.checkTwo(): against your two", new_two) + 
-        if self.two == new_two: +    def remote_checkObject(self, new_client_accessible):  # ancien remote_checkTwo 
-            print("One.checkTwo(): our twos are the same") +        """Méthode appelée par "checkTwo" chez le client 
-two = Two() +        self.oneRef.callRemote("checkTwo", client_accessible) 
-root_obj = One(two)+        """ 
 +        print(f"My client_accessible is: {self.client_accessible}") 
 +        print(f"Le client_accessible is: {new_client_accessible}") 
 +        if self.client_accessible == new_client_accessible: 
 +            print("client_accessible are the same") 
 + 
 +# un object auquel le client pourra accéder 
 +client_accessible = ObjetAccessibleParLeClient() 
 + 
 +# L'objet principal réseau qui tourne 
 +root_obj = ObjetPrincipal(client_accessible) 
 reactor.listenTCP(8800, pb.PBServerFactory(root_obj)) reactor.listenTCP(8800, pb.PBServerFactory(root_obj))
 reactor.run() reactor.run()
Ligne 107: Ligne 148:
  
 <file python pb2client.py> <file python pb2client.py>
 +"""
 +Accès aux objets du serveur depuis le client
 +Cet exemple montre que c'est bien les mêmes, mais les univers parallèles créent
 +un peu de trouble.
 +
 +Ce client accède à:
 +    - remote_getObject avec callRemote sur "getObject"
 +    - remote_checkObject avec callRemote sur "checkObject"
 +du serveur.
 +"""
 from twisted.internet import reactor from twisted.internet import reactor
 from twisted.spread import pb from twisted.spread import pb
 +
 +
 def main(): def main():
     foo = Foo()     foo = Foo()
Ligne 117: Ligne 170:
  
 class Foo: class Foo:
-    """Garder les globals autour commence à devenir moche, nous utilisons donc  +    """Permet de créer l'objet principal client qui tourne"""
-    une classe simple à la place. Au lieu d'accrocher une fonction à la suivante,  +
-    nous accrochons une méthode à la suivante."""+
     def __init__(self):     def __init__(self):
-        self.oneRef = None+        """self.root_obj_client sera l'objet principal du serveur root_obj""" 
 +        self.root_obj_client = None 
     def step1(self, obj):     def step1(self, obj):
-        print("got one object:", obj) +        print(f"step1: got client_accessible object from server = {obj}") 
-        self.oneRef = obj +        self.root_obj_client = obj 
-        print("asking it to getTwo") +        print("Appel de getObject sur le serveur et ajout du callback") 
-        self.oneRef.callRemote("getTwo").addCallback(self.step2) +        self.root_obj_client.callRemote("getObject").addCallback(self.step2) 
-    def step2(self, two): + 
-        print("got two object:", two) +    def step2(self, client_accessible): 
-        print("giving it back to one") +        print(f"Got object from server: {client_accessible}") 
-        print("one is", self.oneRef) +        print(f"Giving it back to server: {self.root_obj_client}") 
-        self.oneRef.callRemote("checkTwo", two)+        self.root_obj_client.callRemote("checkObject", client_accessible) 
 main() main()
 </file> </file>
Ligne 144: Ligne 198:
 Tout ce qui est référençable peut être transmis sur le fil, dans les deux sens . Le "client" peut donner une référence au "serveur", puis le serveur peut utiliser .callRemote() pour invoquer des méthodes côté client. Cela brouille la distinction entre « client » et « serveur » : la seule vraie différence est qui initie la connexion TCP d'origine ; après c'est tout symétrique. Tout ce qui est référençable peut être transmis sur le fil, dans les deux sens . Le "client" peut donner une référence au "serveur", puis le serveur peut utiliser .callRemote() pour invoquer des méthodes côté client. Cela brouille la distinction entre « client » et « serveur » : la seule vraie différence est qui initie la connexion TCP d'origine ; après c'est tout symétrique.
 ====Code==== ====Code====
 +One est l'objet principal du serveur\\
 +Two est l'objet du client
 <file python pb3server.py> <file python pb3server.py>
 from twisted.internet import reactor from twisted.internet import reactor
Ligne 177: Ligne 233:
 </file> </file>
 ====Explications==== ====Explications====
 +Dans cet exemple, le client donne une référence à son propre objet au serveur. Le serveur appelle ensuite une méthode distante sur l'objet côté client.
 +
 +=====Exemple 4: Echanges bidirectionnels=====
 +Cet exemple est trop confus, il est le mélange de 2 et 3. Il est une étape intermédiaire avec l'exemple 5.
 +====Code====
 +<file python pb4server.py>
 +"""
 +Serveur: Echange bidirectionnel serveur client
 +"""
 +from twisted.internet import reactor
 +from twisted.spread import pb
 +
 +class ObjetAccessibleParLeClient(pb.Referenceable):
 +    """Cette class permet de créer un objet pour en avoir un à partager,
 +    mais il ne fait rien dans cet exemple
 +    L'objet accessible par le client est client_accessible
 +    """
 +    pass
 +
 +class ObjetPrincipal(pb.Root):
 +    def __init__(self, client_accessible):
 +        self.client_accessible = client_accessible
 +    def remote_getObject(self):  # ancien remote_getTwo
 +        """Méthode appelée par "getObject" chez le client avec
 +            self.oneRef.callRemote("getObject").addCallback(self.step2)
 +        """
 +        print(f"Le client a reçu: {self.client_accessible}")
 +        return self.client_accessible
 +    def remote_checkObject(self, new_client_accessible):  # ancien remote_checkTwo
 +        """Méthode appelée par "checkTwo" chez le client
 +        self.oneRef.callRemote("checkTwo", client_accessible)
 +        """
 +        print(f"Mon client_accessible est: {self.client_accessible}")
 +        print(f"Le client_accessible est:  {new_client_accessible}")
 +        if self.client_accessible == new_client_accessible:
 +            print("Les client_accessible sont les mêmes")
 +    def remote_takeTwo(self, two):
 +        """takeTwo"""
 +        print(f"Le serveur à reçu du client un Two: {two}")
 +        print("Le serveur demande au client: print(12)")
 +        two.callRemote("print", 12)
 +
 +# un object auquel le client pourra accéder
 +client_accessible = ObjetAccessibleParLeClient()
 +# L'objet principal réseau qui tourne
 +root_obj = ObjetPrincipal(client_accessible)
 +reactor.listenTCP(8800, pb.PBServerFactory(root_obj))
 +reactor.run()
 +</file>
 +
 +<file python pb4client.py>
 +"""
 +Client: Echange bidirectionnel serveur client
 +"""
 +from twisted.internet import reactor
 +from twisted.spread import pb
 +
 +class Two(pb.Referenceable):
 +    def remote_print(self, arg):
 +        print(f"Two.print() appelé par le serveur avec l'argument: {arg}")
 +        print(arg)
 +
 +class Foo:
 +    """Permet de créer l'objet principal client qui tourne"""
 +    def __init__(self):
 +        """self.root_obj_client sera l'objet principal du serveur root_obj"""
 +        self.root_obj_client = None
 +    def step1(self, obj):
 +        self.root_obj_client = obj
 +        obj_server = self.root_obj_client.callRemote("getObject")
 +        print(f"Le client a accès à: {obj_server}")
 +        obj_server.addCallback(self.step2)
 +    def step2(self, client_accessible):
 +        print(f"Got object from server: {client_accessible}")
 +        print(f"Giving it back to server: {self.root_obj_client}")
 +        self.root_obj_client.callRemote("checkObject", client_accessible)
 +
 +def got_obj(obj, two):
 +    print("J'ai eu l'objet:", obj)
 +    print(f"Je lui donne le mien {two}")
 +    obj.callRemote("takeTwo", two)
 +def main():
 +    foo = Foo()
 +    two = Two()
 +    factory = pb.PBClientFactory()
 +    reactor.connectTCP("localhost", 8800, factory)
 +    factory.getRootObject().addCallback(foo.step1)
 +    def1 = factory.getRootObject()
 +    def1.addCallback(got_obj, two)  # hands our 'two' to the callback
 +    reactor.run()
 +main()
 +</file>
 +=====Exemple 5: Un vrai client/serveur=====
 +Le serveur tourne.\\
 +Dans une boucle, le client récupére des infos sur le serveur, et lui envoie lui aussi des valeurs.
 +
 +<file python pb5server.py>
 +
 +from time import sleep
 +from threading import Thread
 +
 +from twisted.internet import reactor
 +from twisted.spread import pb
 +
 +class PlayerSimulator(pb.Referenceable):
 +    """Cette class permet de créer un objet à partager.
 +    Il simule un player avec:
 +        - une position de lecture du fichier musical à self.position
 +        - un track à lire imposé par le client
 +    L'objet accessible par le client est player
 +    """
 +    def __init__(self):
 +        self.track = 1
 +        self.position = 0
 +        Thread(target=self.thread_update_position).start()
 +
 +    def thread_update_position(self):
 +        n = 0
 +        while n < 1000:
 +            n += 1
 +            self.position += 1
 +            sleep(1)
 +
 +    def remote_newTrack(self, track):
 +        """New Track imposé par le client"""
 +        self.track = track
 +        print(f"Nouveau track demandé par le client {self.track}")
 +
 +class ObjetPrincipal(pb.Root):
 +    def __init__(self, player):
 +        self.player = player
 +
 +    def remote_getPlayerSimulator(self):
 +        print(f"Le client  demande self.player, je lui retourne")
 +        return self.player
 +
 +    def remote_getPosition(self):
 +        p = self.player.position
 +        print(f"Le serveur envoie au client la position {p}")
 +        return p
 +
 +# un object auquel le client pourra accéder
 +player = PlayerSimulator()
 +
 +# L'objet principal réseau qui tourne
 +root_obj = ObjetPrincipal(player)
 +
 +reactor.listenTCP(8800, pb.PBServerFactory(root_obj))
 +reactor.run()
 +</file>
 +
 +<file python pb5client.py>
 +from time import sleep
 +from threading import Thread
 +
 +from twisted.internet import reactor
 +# Interaction réseau en TCP
 +from twisted.spread import pb  # Perspetive Broker de Twisted
 +
 +class Client:
 +    """Permet de créer l'objet principal client qui tourne"""
 +
 +    def __init__(self):
 +        """self.root_obj_client sera l'objet principal du serveur root_obj"""
 +        self.root_obj_client = None
 +        self.track = 1
 +
 +    def on_start(self, obj):
 +        print(f"on_start: got client_accessible object from server = {obj}")
 +        self.root_obj_client = obj
 +        # Appel du Player sur le serveur et ajout du callback
 +        self.root_obj_client.callRemote("getPlayerSimulator").addCallback(self.run)
 +
 +    def run(self, args):
 +        Thread(target=self.thread_run).start()
 +
 +    def thread_run(self):
 +        n = 0
 +        while n < 500:
 +            # Position
 +            n += 1
 +            self.root_obj_client.callRemote("getPosition").addCallback(self.print_position)
 +
 +            # Changement de track tous les 10
 +            if n % 10 == 2:
 +                self.track += 1
 +                self.root_obj_client.callRemote("getPlayerSimulator").addCallback(self.toto)
 +            sleep(0.3)
 +
 +    def toto(self, args):
 +        print("args:", args)
 +        print(f"Envoi du track: {self.track}")
 +        args.callRemote("newTrack", self.track)
 +
 +
 +    def print_position(self, position):
 +        print(f"Le client a reçu: {position}")
 +
 +
 +def main():
 +    clt = Client()
 +    factory = pb.PBClientFactory()
 +    reactor.connectTCP("localhost", 8800, factory)
 +    factory.getRootObject().addCallback(clt.on_start)
 +    reactor.run()
 +
 +main()
 +</file>
 +
 +=====Transfert d'une image ou d'un json=====
 +On peut transférer n'import quoi mais il faut toujours convertir ses datas en bytes (octets) avant de les envoyer. Et les décoder à la réception.
 +<file python >
 +from time import sleep
 +from threading import Thread
 +
 +import numpy as np
 +import cv2
 +
 +from twisted.internet import reactor
 +from twisted.spread import pb
 +
 +class MyImage:
 +    def __init__(self):
 +        self.img = cv2.imread('./covers/Fasokan.jpg')
 +        img = cv2.resize(self.img, (512, 512))
 +        encode_param=[int(cv2.IMWRITE_JPEG_QUALITY), 90]
 +        result, imgencode = cv2.imencode('.jpg', img, encode_param)
 +        data = np.array(imgencode)
 +        print(data.shape)
 +        self.stringData = data.tobytes()
 +
 +
 +class ObjetPrincipal(pb.Root):
 +    def __init__(self):
 +        self.mi = MyImage()
 +    def remote_image(self):
 +        print("Le serveur envoie une image")
 +        return self.mi.stringData
 +
 +# L'objet principal réseau qui tourne
 +root_obj = ObjetPrincipal()
 +
 +reactor.listenTCP(8800, pb.PBServerFactory(root_obj))
 +reactor.run()
 +</file>
 +
 +<file python >
 +import numpy as np
 +import cv2
 +import base64
 +from twisted.internet import reactor
 +from twisted.spread import pb
 +
 +
 +class GetImage(pb.Copyable):
 +    def get_image(self, stringData):
 +        image = np.asarray(bytearray(stringData), dtype="uint8")
 +        image = cv2.imdecode(image, cv2.IMREAD_COLOR)
 +        print(image.shape)
 +        cv2.imshow('SERVER', image)
 +        cv2.waitKey(0)
 +        cv2.destroyAllWindows()
 +
 +class RemoteGetImage(pb.RemoteCopy):
 +    pass
 +
 +pb.setUnjellyableForClass(GetImage, RemoteGetImage)
 +
 +class Client:
 +    """Permet de créer l'objet principal client qui tourne"""
 +    def __init__(self):
 +        """self.root_obj_client sera l'objet principal du serveur root_obj"""
 +        self.root_obj_client = None
 +        self.gi = GetImage()
 +    def on_start(self, obj):
 +        print(f"on_start: got client_accessible object from server = {obj}")
 +        self.root_obj_client = obj
 +        # Transfert d'une image
 +        self.root_obj_client.callRemote("image").addCallback(self.gi.get_image)
 +        
 +def main():
 +    clt = Client()
 +    factory = pb.PBClientFactory()
 +    reactor.connectTCP("localhost", 8800, factory)
 +    factory.getRootObject().addCallback(clt.on_start)
 +    reactor.run()
 +
 +main()
 +</file>
  
 +=====Page suivante: Passing Complex Types=====
 +  * **[[pb_copyable_passing_complex_types|PB Copyable Passing Complex Types]]**
  
 {{tag>python sb intergalactique twisted perspectivebroker}} {{tag>python sb intergalactique twisted perspectivebroker}}
using_perspective_broker.1669713210.txt.gz · Dernière modification : de serge