using_perspective_broker
Différences
Ci-dessous, les différences entre deux révisions de la page.
| Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente | ||
| using_perspective_broker [2022/11/29 09:04] – [Exemple 2] serge | using_perspective_broker [2022/12/16 10:42] (Version actuelle) – [Using Perspective Broker] serge | ||
|---|---|---|---|
| Ligne 1: | Ligne 1: | ||
| ======Using Perspective Broker====== | ======Using Perspective Broker====== | ||
| + | {{ : | ||
| + | |||
| + | {{ ::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' | ||
| + | * les exemples ne respectent pas le PEP 8, c'est imbriqué au possible | ||
| </ | </ | ||
| <WRAP center round box 60% > | <WRAP center round box 60% > | ||
| * **{{tagpage> | * **{{tagpage> | ||
| - | * **[[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:// | ||
| </ | </ | ||
| 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: | + | **Passez votre chemin**, cela revient à expliquer la factorisation à un élève de 5ème, en lui parlant de nombres complexes et de cosinus hyperbolique: |
| + | 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 70: | Ligne 85: | ||
| ====Explications==== | ====Explications==== | ||
| - | =====Exemple 2===== | + | =====Exemple 2: Accès aux objets du serveur depuis le client===== |
| * **[[https:// | * **[[https:// | ||
| - | 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' | + | 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' |
| 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(" | ||
| + | - remote_checkTwo avec self.oneRef.callRemote(" | ||
| + | |||
| + | """ | ||
| + | |||
| from twisted.internet import reactor | from twisted.internet import reactor | ||
| from twisted.spread import pb | from twisted.spread import pb | ||
| - | class Two(pb.Referenceable): | + | |
| - | | + | class ObjetAccessibleParLeClient(pb.Referenceable): |
| - | print(" | + | """ |
| - | + | mais il ne fait rien dans cet exemple | |
| - | class One(pb.Root): | + | L' |
| - | def __init__(self, | + | "" |
| - | | + | |
| - | | + | |
| - | def remote_getTwo(self): | + | class ObjetPrincipal(pb.Root): |
| - | """ | + | def __init__(self, |
| - | self.oneRef.callRemote(" | + | self.client_accessible = client_accessible |
| - | print(" | + | |
| - | return self.two | + | def remote_getObject(self): |
| - | def remote_checkTwo(self, | + | """ |
| - | """ | + | self.oneRef.callRemote(" |
| - | self.oneRef.callRemote(" | + | |
| - | | + | print(f" |
| - | print(" | + | return self.client_accessible |
| - | print(" | + | |
| - | if self.two == new_two: | + | def remote_checkObject(self, |
| - | print(" | + | """ |
| - | two = Two() | + | self.oneRef.callRemote(" |
| - | 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 | ||
| + | print(" | ||
| + | |||
| + | # un object auquel le client pourra accéder | ||
| + | client_accessible | ||
| + | |||
| + | # L' | ||
| + | root_obj = ObjetPrincipal(client_accessible) | ||
| reactor.listenTCP(8800, | reactor.listenTCP(8800, | ||
| 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 " | ||
| + | - remote_checkObject avec callRemote sur " | ||
| + | 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: | ||
| - | """ | + | """ |
| - | 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 | + | |
| + | self.root_obj_client | ||
| def step1(self, obj): | def step1(self, obj): | ||
| - | print(" | + | print(f"step1: |
| - | self.oneRef | + | self.root_obj_client |
| - | print(" | + | print(" |
| - | self.oneRef.callRemote(" | + | self.root_obj_client.callRemote(" |
| - | def step2(self, | + | |
| - | print(" | + | def step2(self, |
| - | print(" | + | print(f"Got object |
| - | print(" | + | print(f"Giving |
| - | self.oneRef.callRemote(" | + | self.root_obj_client.callRemote(" |
| main() | main() | ||
| </ | </ | ||
| ====Explications==== | ====Explications==== | ||
| - | Le serveur donne une Two()instance au client, qui renvoie ensuite la référence au serveur. Le serveur compare les " | + | Le serveur donne une Two()instance au client, qui renvoie ensuite la référence au serveur. Le serveur compare les " |
| + | Quelques autres techniques sont démontrées dans pb2client.py. La première est que les rappels sont ajoutés avec .addCallback au lieu de .addCallbacks. Comme vous pouvez le voir dans la documentation Deferred.addCallback , il s'agit d'un formulaire simplifié qui n' | ||
| + | N' | ||
| + | =====Exemple 3: Accès aux objets coté client===== | ||
| + | * **[[https:// | ||
| - | Quelques autres techniques sont démontrées dans pb2client.py. La première | + | Tout ce qui est référençable peut être transmis sur le fil, dans les deux sens . Le " |
| - | + | ====Code==== | |
| - | N'oubliez pas que le client peut vous rendre toute référence à distance que vous lui avez donnée. Ne basez pas votre serveur | + | One est l'objet principal du serveur\\ |
| - | =====Exemple 3===== | + | Two est l'objet du client |
| + | <file python pb3server.py> | ||
| + | from twisted.internet import reactor | ||
| + | from twisted.spread import pb | ||
| + | class One(pb.Root): | ||
| + | def remote_takeTwo(self, two): | ||
| + | """ | ||
| + | print(" | ||
| + | print(" | ||
| + | two.callRemote(" | ||
| + | reactor.listenTCP(8800, pb.PBServerFactory(One())) | ||
| + | reactor.run() | ||
| + | </ | ||
| + | <file python pb3client.py> | ||
| + | from twisted.internet import reactor | ||
| + | from twisted.spread import pb | ||
| + | class Two(pb.Referenceable): | ||
| + | def remote_print(self, | ||
| + | print(" | ||
| + | def main(): | ||
| + | two = Two() | ||
| + | factory = pb.PBClientFactory() | ||
| + | reactor.connectTCP(" | ||
| + | def1 = factory.getRootObject() | ||
| + | def1.addCallback(got_obj, | ||
| + | reactor.run() | ||
| + | def got_obj(obj, | ||
| + | print(" | ||
| + | print(" | ||
| + | obj.callRemote(" | ||
| + | main() | ||
| + | </ | ||
| + | ====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' | ||
| + | =====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' | ||
| ====Code==== | ====Code==== | ||
| - | <file python | + | <file python |
| + | """ | ||
| + | Serveur: Echange bidirectionnel serveur client | ||
| + | """ | ||
| + | from twisted.internet import reactor | ||
| + | from twisted.spread import pb | ||
| + | class ObjetAccessibleParLeClient(pb.Referenceable): | ||
| + | """ | ||
| + | mais il ne fait rien dans cet exemple | ||
| + | L' | ||
| + | """ | ||
| + | pass | ||
| + | |||
| + | class ObjetPrincipal(pb.Root): | ||
| + | def __init__(self, | ||
| + | self.client_accessible = client_accessible | ||
| + | def remote_getObject(self): | ||
| + | """ | ||
| + | self.oneRef.callRemote(" | ||
| + | """ | ||
| + | print(f" | ||
| + | return self.client_accessible | ||
| + | def remote_checkObject(self, | ||
| + | """ | ||
| + | self.oneRef.callRemote(" | ||
| + | """ | ||
| + | print(f" | ||
| + | print(f" | ||
| + | if self.client_accessible == new_client_accessible: | ||
| + | print(" | ||
| + | def remote_takeTwo(self, | ||
| + | """ | ||
| + | print(f" | ||
| + | print(" | ||
| + | two.callRemote(" | ||
| + | |||
| + | # un object auquel le client pourra accéder | ||
| + | client_accessible = ObjetAccessibleParLeClient() | ||
| + | # L' | ||
| + | root_obj = ObjetPrincipal(client_accessible) | ||
| + | reactor.listenTCP(8800, | ||
| + | reactor.run() | ||
| </ | </ | ||
| - | <file python | + | <file python |
| + | """ | ||
| + | Client: Echange bidirectionnel serveur client | ||
| + | """ | ||
| + | from twisted.internet import reactor | ||
| + | from twisted.spread import pb | ||
| + | class Two(pb.Referenceable): | ||
| + | def remote_print(self, | ||
| + | print(f" | ||
| + | print(arg) | ||
| + | |||
| + | class Foo: | ||
| + | """ | ||
| + | def __init__(self): | ||
| + | """ | ||
| + | self.root_obj_client = None | ||
| + | def step1(self, obj): | ||
| + | self.root_obj_client = obj | ||
| + | obj_server = self.root_obj_client.callRemote(" | ||
| + | print(f" | ||
| + | obj_server.addCallback(self.step2) | ||
| + | def step2(self, client_accessible): | ||
| + | print(f" | ||
| + | print(f" | ||
| + | self.root_obj_client.callRemote(" | ||
| + | |||
| + | def got_obj(obj, | ||
| + | print(" | ||
| + | print(f" | ||
| + | obj.callRemote(" | ||
| + | def main(): | ||
| + | foo = Foo() | ||
| + | two = Two() | ||
| + | factory = pb.PBClientFactory() | ||
| + | reactor.connectTCP(" | ||
| + | factory.getRootObject().addCallback(foo.step1) | ||
| + | def1 = factory.getRootObject() | ||
| + | def1.addCallback(got_obj, | ||
| + | reactor.run() | ||
| + | main() | ||
| + | </ | ||
| + | =====Exemple 5: Un vrai client/ | ||
| + | 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): | ||
| + | """ | ||
| + | Il simule un player avec: | ||
| + | - une position de lecture du fichier musical à self.position | ||
| + | - un track à lire imposé par le client | ||
| + | L' | ||
| + | """ | ||
| + | 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, | ||
| + | """ | ||
| + | self.track = track | ||
| + | print(f" | ||
| + | |||
| + | class ObjetPrincipal(pb.Root): | ||
| + | def __init__(self, | ||
| + | self.player = player | ||
| + | |||
| + | def remote_getPlayerSimulator(self): | ||
| + | print(f" | ||
| + | return self.player | ||
| + | |||
| + | def remote_getPosition(self): | ||
| + | p = self.player.position | ||
| + | print(f" | ||
| + | return p | ||
| + | |||
| + | # un object auquel le client pourra accéder | ||
| + | player = PlayerSimulator() | ||
| + | |||
| + | # L' | ||
| + | root_obj = ObjetPrincipal(player) | ||
| + | |||
| + | reactor.listenTCP(8800, | ||
| + | reactor.run() | ||
| + | </ | ||
| + | |||
| + | <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: | ||
| + | """ | ||
| + | |||
| + | def __init__(self): | ||
| + | """ | ||
| + | self.root_obj_client = None | ||
| + | self.track = 1 | ||
| + | |||
| + | def on_start(self, | ||
| + | print(f" | ||
| + | self.root_obj_client = obj | ||
| + | # Appel du Player sur le serveur et ajout du callback | ||
| + | self.root_obj_client.callRemote(" | ||
| + | |||
| + | 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(" | ||
| + | |||
| + | # Changement de track tous les 10 | ||
| + | if n % 10 == 2: | ||
| + | self.track += 1 | ||
| + | self.root_obj_client.callRemote(" | ||
| + | sleep(0.3) | ||
| + | |||
| + | def toto(self, args): | ||
| + | print(" | ||
| + | print(f" | ||
| + | args.callRemote(" | ||
| + | |||
| + | |||
| + | def print_position(self, | ||
| + | print(f" | ||
| + | |||
| + | |||
| + | def main(): | ||
| + | clt = Client() | ||
| + | factory = pb.PBClientFactory() | ||
| + | reactor.connectTCP(" | ||
| + | factory.getRootObject().addCallback(clt.on_start) | ||
| + | reactor.run() | ||
| + | |||
| + | main() | ||
| + | </ | ||
| + | |||
| + | =====Transfert d'une image ou d'un json===== | ||
| + | On peut transférer n' | ||
| + | <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(' | ||
| + | img = cv2.resize(self.img, | ||
| + | encode_param=[int(cv2.IMWRITE_JPEG_QUALITY), | ||
| + | result, imgencode = cv2.imencode(' | ||
| + | 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(" | ||
| + | return self.mi.stringData | ||
| + | |||
| + | # L' | ||
| + | root_obj = ObjetPrincipal() | ||
| + | |||
| + | reactor.listenTCP(8800, | ||
| + | reactor.run() | ||
| + | </ | ||
| + | |||
| + | <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, | ||
| + | image = np.asarray(bytearray(stringData), | ||
| + | image = cv2.imdecode(image, | ||
| + | print(image.shape) | ||
| + | cv2.imshow(' | ||
| + | cv2.waitKey(0) | ||
| + | cv2.destroyAllWindows() | ||
| + | |||
| + | class RemoteGetImage(pb.RemoteCopy): | ||
| + | pass | ||
| + | |||
| + | pb.setUnjellyableForClass(GetImage, | ||
| + | |||
| + | class Client: | ||
| + | """ | ||
| + | def __init__(self): | ||
| + | """ | ||
| + | self.root_obj_client = None | ||
| + | self.gi = GetImage() | ||
| + | def on_start(self, | ||
| + | print(f" | ||
| + | self.root_obj_client = obj | ||
| + | # Transfert d'une image | ||
| + | self.root_obj_client.callRemote(" | ||
| + | | ||
| + | def main(): | ||
| + | clt = Client() | ||
| + | factory = pb.PBClientFactory() | ||
| + | reactor.connectTCP(" | ||
| + | factory.getRootObject().addCallback(clt.on_start) | ||
| + | reactor.run() | ||
| + | |||
| + | main() | ||
| </ | </ | ||
| - | ====Explications==== | ||
| + | =====Page suivante: Passing Complex Types===== | ||
| + | * **[[pb_copyable_passing_complex_types|PB Copyable Passing Complex Types]]** | ||
| {{tag> | {{tag> | ||
using_perspective_broker.1669712643.txt.gz · Dernière modification : de serge
