Gast piwo Geschrieben February 7, 2019 at 00:19 Geschrieben February 7, 2019 at 00:19 sorry liebe gemeinde, aber es ist eol v. python2.x und wer macht sich nicht da gedanken über python3 ;-) beim drüberfliegen über die ipconn-library habe ich mehr fragen als antworten : da werden prober-threads, callback-threads usw. wie ein feuerwerk geballert. schön. da seh ich queues, kontexte, wasauchimmer - grossartig diesen code von einem async* aus zu bändigen : mahlzeit wie habt ihr euch das so vorgestellt ? wie soll dieses feuerwerk unter einer python-3 eventloop ansteuerbar sein aber bitte keine kryptischen no-na-du-depp-meldungen. ein klares design bitte. ODER MUSS ICH JETZT ALLE MEINE TF-HARDWARE VERSCHROTTEN WEIL ICH MIR DIE HÄNDE BRECHE MIT EINER STATE-OF-THE-ART-IMPLEMENTIERUNG von standard-vorgängen mit asyncio ??????? lg wp Zitieren
Gast piwo Geschrieben February 7, 2019 at 00:54 Geschrieben February 7, 2019 at 00:54 ... und damits nicht sagen könnts, da tut einer bloss blöd daherreden : https://www.usenix.org/system/files/login/articles/login_spring16_11_beazley.pdf damit kann ja vielleicht eure resistenz (bzw. unwillen, bzw. widerstand da tausende zeilen python incl. brickd, logger, mqtt-bridge usw. usw. usw. neu zu kodieren) etwas "weicher" machen. ja, ich hab verständnis. nein. mit wegschauen geht : GAR NIX Zitieren
Gast piwo Geschrieben February 7, 2019 at 01:14 Geschrieben February 7, 2019 at 01:14 ... und damit es nicht heisst, da ist ein obergscheiter am nölen : https://realpython.com/python-concurrency/ ich hab ein problem - asyncio integration von tinker* aber auch ihr habt ein problem mit der momentanen architektur unter solchen voraussetzungen. ich wünsche mir konstruktive und passende lösungen .... lg wp Zitieren
jgmischke Geschrieben February 7, 2019 at 07:24 Geschrieben February 7, 2019 at 07:24 Hallo piwo, wenn ich mir die Liste der Programmiersprachen ansehe, die TF so unterstützt empfinde ich dein "ich will aber" sehr komisch. Wenn dir Teile der Implementierung nicht gefallen könntest du ja es besser machen, zumal du dich wohl gut damit auskennst. So wie ich die TF Jungs kenne wird es sicher bald ne Lösung geben. Also wo liegt das Problem?? Zitieren
borg Geschrieben February 7, 2019 at 10:50 Geschrieben February 7, 2019 at 10:50 @piwo: Dass die Python Bindings Threads nutzen ist ja eigentlich erstmal ein internes Implementierungsdetail. Hast du mal ein konkretes Beispiel wo du asyncio extern verwendest aber die aktuellen Python Bindings nicht nutzen kannst? Zitieren
Gast piwo Geschrieben February 7, 2019 at 11:09 Geschrieben February 7, 2019 at 11:09 ** implementierungsdetail ** frage : wie verhält sich das implementierungsdetail wenn ich es aus coroutinen heraus verwenden muss SÄMTLICHE callbacks kann ich m.m.n. schmeissen - oder wie könnte man die in die eventloop einbinden oder gebt mal eine implementierungsempfehlung unter verwendung von co-routinen an bitte ... w Zitieren
Gast piwo Geschrieben February 7, 2019 at 11:15 Geschrieben February 7, 2019 at 11:15 @jgmischke wir sind hier bei einem kommerziellen anbieter von hardware und nicht bei einem wald-und-wiesen-opensource ein minimum an zeitgemässer api ist keine frechheit, sondern ein grundrecht hast schon einmal in die python-implementierung von ipconnection reingesehen ? da gehen mir die kommentare aus ... lg Zitieren
borg Geschrieben February 7, 2019 at 11:18 Geschrieben February 7, 2019 at 11:18 Eine gängige Möglichkeit Callbacks in eine Eventloop einzubinden ist im Callback die Daten in eine Queue zu packen und im Eventloop die Queue auszulesen. Im Brick Viewer nutzen wir zum Beispiel eine Queue um die Daten des Tinkerforge Systems in die Qt-Eventloop zu bekommen ohne das die GUI blockiert wird: https://github.com/Tinkerforge/brickv/blob/master/src/brickv/async_call.py Zitieren
Gast piwo Geschrieben February 7, 2019 at 11:53 Geschrieben February 7, 2019 at 11:53 lieber borg ich habe jetzt jahrelang prozedural und mit threading mich herumgeschlagen und das nur um eine ipconn am leben zu erhalten und einen stack auszulesen bzw. mich mit events versorgen zu lassen. das design das ihr vorgebt erzeugt ganz schlimme und unlesbare/unwartbare monolithen. man sehe sich nur euren code für den mqtt-proxy an.... wenn das die spitze der fahnenstange bzgl "generische programmierung/strukturen" ist, dann mahlzeit. ich brauche ein flexibles producer/consumer-system auf basis von python3 und coroutinen/tasks. wie ich DAS mache, das ist meine sache. aber was mir da entgegensteht ist weniger die API sondern deren implementierung. also bitte macht euch mal gedanken über alles was mit ASYNC/AWAIT anfängt ... - wie sind client-calls zu wrappen - wie sind callbackfunktionen zu wrappen lg Zitieren
jgmischke Geschrieben February 7, 2019 at 12:43 Geschrieben February 7, 2019 at 12:43 <<< wir sind hier bei einem kommerziellen anbieter von hardware und nicht bei einem wald-und-wiesen-opensource >>> Genau, dieser Anbieter unterstützt fast 20(!) APIs aller gängigen Sprachen und das mit ein paar Programmierern. TF ist nicht MS und muss mit den Resourcen sicherlich vorsichtig umgehen. Wenn ich mir etwa den C-Code von TF ansehe, kann ich nichts ungewöhnliches feststellen, wohl deswegen weil C natürlich hardwarenahe Programmierung sehr gut unterstützt. Vielleicht wäre ein Umstieg auf C das richtige für dich. Zitieren
borg Geschrieben February 7, 2019 at 13:35 Geschrieben February 7, 2019 at 13:35 man sehe sich nur euren code für den mqtt-proxy an.... wenn das die spitze der fahnenstange bzgl "generische programmierung/strukturen" ist, dann mahlzeit. Du meinst die neuen MQTT Bindings die noch in der Beta sind? Der Code ist zu 100% generiert und in der Tat nicht zum durchlesen gedacht. Allerdings ist dort ja nun wirklich MQTT die Schnittstelle und der Python-Code im Hintergrund für den Nutzer nicht relevant. - wie sind client-calls zu wrappen - wie sind callbackfunktionen zu wrappen Du meinst sowas? #!/usr/bin/env python3 # -*- coding: utf-8 -*- HOST = "localhost" PORT = 4223 UID = "GKp" # Change XYZ to the UID of your Air Quality Bricklet import asyncio from concurrent.futures import ThreadPoolExecutor from tinkerforge.ip_connection import IPConnection from tinkerforge.bricklet_air_quality import BrickletAirQuality import time _executor = ThreadPoolExecutor(10) async def tf_async_call(name, func, delay): def _non_async_delayed_call(func, delay): time.sleep(delay) return func() value = await asyncio.get_event_loop().run_in_executor(_executor, lambda: _non_async_delayed_call(func, delay)) print('{0}: {1}'.format(name, value)) async def async_main(aq): call_temperature = tf_async_call('Temperature', aq.get_temperature, 2) call_humidity = tf_async_call('Humidity', aq.get_humidity, 1) task1 = asyncio.ensure_future(call_temperature) task2 = asyncio.get_event_loop().create_task(call_humidity) print('Do something useful here while humidity and temperature are gathered...') start = time.time() while start + 2 > time.time(): await asyncio.sleep(0.1) print(time.time()) await asyncio.wait([task1, task2]) print('Done!') if __name__ == "__main__": ipcon = IPConnection() # Create IP connection aq = BrickletAirQuality(UID, ipcon) # Create device object ipcon.connect(HOST, PORT) # Connect to brickd # Don't use device before ipcon is connected loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.ensure_future(async_main(aq))) ipcon.disconnect() Edit: Hab das gerade nochmal ein bisschen aufgebohrt, damit man sieht das ich wirklich async calls machen kann während der getter-Aufruf läuft Zitieren
Gast piwo Geschrieben February 7, 2019 at 16:33 Geschrieben February 7, 2019 at 16:33 async def tf_async_call(name, func, delay): await asyncio.sleep(delay) # Add some artifical wait time async def tf_async_call(name, func, delay): def _non_async_delayed_call(func, delay): time.sleep(delay) def test(func, delay): time.sleep(delay) wozu all die delays ? wir wollen ja nicht umsonst warten in einem async/await-umfeld ... ich pflege bei allen bricklet-initialisierungen ein br_obj.set_response_expected_all(True) aufzurufen, in der hoffentlich nicht irrigen meinung, dass damit ein optimiertes antwortzeitverhalten erzielt wird ... noch dazu ein (sync) time.sleep() statt eines (async) asyncio.sleep() ... es erfolgt KEIN scheduling "anderer" coroutinen - aber genau das ist ja das ziel - für sowas brauch ich keine coroutinen ... rationale bitte ein tragfähiges design und keine ad-hoc-lösungen lg wp Zitieren
Gast piwo Geschrieben February 7, 2019 at 16:41 Geschrieben February 7, 2019 at 16:41 bezüglich mqtt-proxy : ich bin schon dazu übergegangen, den "normalen" d.h. nicht den BETA mqtt-proxy für queuing in einem async-umfeld zu "durchleuchten". bin noch auf keinen verifizierbaren grünen zweig gekommen. absicht dabei ist : statt topics in paho zu injizieren diese in eine async-queue zu füttern und vice-versa ich wäre ja schon zufrieden, wenn ich diesen (universellen) moloch als seperaten thread weit weg von den "normalen" coroutinen bedienen könnte. einen thread pro stack. der soll machen was er will, hauptsache, er passt in ein "echtes" asyncio-umfeld. got the idea ? Zitieren
borg Geschrieben February 7, 2019 at 16:43 Geschrieben February 7, 2019 at 16:43 Die ganzen delays sind da zu Demonstrationszwecken, damit die non-async Funktionen viel Zeit benötigen. Mit dieser Schleife hier while start + 2 > time.time(): await asyncio.sleep(0.1) print(time.time()) Zeige ich dann das ich während die Tinkerforge Getter aufgerufen werden weitere async Funktionen aufrufen kann, was ja Sinn der Sache ist. Hier eine vereinfachte Version: #!/usr/bin/env python3 # -*- coding: utf-8 -*- HOST = "localhost" PORT = 4223 UID = "GKp" # Change XYZ to the UID of your Air Quality Bricklet import asyncio from concurrent.futures import ThreadPoolExecutor from tinkerforge.ip_connection import IPConnection from tinkerforge.bricklet_air_quality import BrickletAirQuality import time _executor = ThreadPoolExecutor(10) async def tf_async_call(name, func): value = await asyncio.get_event_loop().run_in_executor(_executor, func) print('{0}: {1}'.format(name, value)) async def async_main(aq): task1 = asyncio.ensure_future(tf_async_call('Temperature', aq.get_temperature)) task2 = asyncio.ensure_future(tf_async_call('Humidity', aq.get_humidity)) # Here your async code await asyncio.wait([task1, task2]) print('Done!') if __name__ == "__main__": ipcon = IPConnection() # Create IP connection aq = BrickletAirQuality(UID, ipcon) # Create device object ipcon.connect(HOST, PORT) # Connect to brickd # Don't use device before ipcon is connected loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.ensure_future(async_main(aq))) ipcon.disconnect() Edit: Deine Idee mit den MQTT Bindings zu benutzen um damit Python asyncio zu machen hab ich leider nicht verstanden, aber was spricht dagegen den Code oben in einem asyncio-Umfeld einzusetzen? Zitieren
Gast piwo Geschrieben February 7, 2019 at 16:48 Geschrieben February 7, 2019 at 16:48 Edit: Hab das gerade nochmal ein bisschen aufgebohrt, damit man sieht das ich wirklich async calls machen kann während der getter-Aufruf läuft bitte sieh all das nicht als quälerei ... vielleicht bin ich ja der erste (trottel), aber diese dinge kommen sowieso bald auf euch zu ... ich kann dir ja mal eine "old-fashioned" implementierung mailen, wo ich rein durch (rekursives, irrsinniges) __init__ eine stackumgebung definiert habe und dann eine alarmanlagenüberwachung "soft-pluggable" gemacht habe : wenn die objekte "richtig" definiert wurden, ist das ding losgerattert für BELIEBIGE STACKS ;-) aber ich hab im moment von solchen zaubereien die nase voll und will in ein NEUES "soft-configurable" design wechseln auf basis von asyncio und python3 - die "alten dinger" zahlen sich heutzutage in den "neuen zeiten" nicht mehr aus. python2 und prozedurale zaubereien sind für io-bound tasks VORBEI .... echt .... ;-) Zitieren
Gast piwo Geschrieben February 7, 2019 at 16:59 Geschrieben February 7, 2019 at 16:59 corr: "optimiertes antwortzeitverhalten" : wenn ich eine setter-function absetze, will ich als paranoider mensch auch, dass ich nicht nochmals einen getter nachwerfen, ob das auch so passt ... d.h. will nicht nachher womögich über exceptions stolpern, die "so nicht ausgemacht sind" ;-) wenn ein "return" returned, dann sollte er auch indizieren, dass alles passt ... auch bei den settern ob das wirklich SO der fall ist, weiss ich nach jahren noch immer nicht, bzw. GLAUBE, dass das dann auch "passt" please enlighten me ;-) lg wp Zitieren
borg Geschrieben February 7, 2019 at 17:02 Geschrieben February 7, 2019 at 17:02 Wenn du set_response_expected_all(True) aufrufst wird für alle Aufrufe (auch für Setter) eine Nachricht vom Brick/Bricklet zurück zum PC gesendet und wenn diese nicht ankommt bekommst du eine TimeoutException. Zitieren
Gast piwo Geschrieben February 7, 2019 at 17:26 Geschrieben February 7, 2019 at 17:26 ok. asyncio getters werd ich implementieren ... callbacks werd ich : a) vergessen (ist kein drama ; ich bevorzuge sowieso totale kontrolle nach "meinen vorstellungen" ;-) b) entgegen euren empfehlungen ".... callbacks sind IMMER zu bevorzugen ..." NICHT IMPLEMENTIEREN c) in eine awaitable Queue zwingen : ok. und wie ? da muss man ja die callbacks irgendwie für coroutinen tauglich machen ... ad c) ich fürchte, das geht nicht "ohne weiteres". dein beispiel mit dem injizieren in die QT-loop ist zwar schlüssig, setzt aber die verwendung von den brickv-(client)-imports voraus. ich hoffe, diese unterscheiden sich nicht allzusehr von den "normalen" (client)-python-bindings ... ( -> am besten durch NULL DIFFERENCE ;-) GENERELL : ich fürchte mich nicht vor separaten threads, die es in einem co-routinen-tauglichen umfelt zu initialisiern gibt ... threading.local-kontexte are your friend ;-) ziel sollte halt sein, dass irgendein x-beliebiger brickd (vor allem auf red-brick) oder ein x-beliebiger tinkerforge-stack mit wlan-extension-v1,v2 via tcp-bindings angesteuert werden kann in einem co-routinen-tauglichen umfeld ... welche helper oder wrapper oder leck-mich-am-... ich da brauche ist mir EGAL. am ende sollen asyncio-tasks konstruierbar sein, die auch entsprechend yielden und nicht in der nase bohren (= pfui teufel ;-) ) ... Zitieren
Gast piwo Geschrieben February 7, 2019 at 17:28 Geschrieben February 7, 2019 at 17:28 ... na da sieht man, wie gut das ist : hatte noch niemals eine TimeoutException über jahre ;-) ... vielleicht hat auch keiner die gehandelt ;-) ;-) Zitieren
borg Geschrieben February 7, 2019 at 17:42 Geschrieben February 7, 2019 at 17:42 ich hoffe, diese unterscheiden sich nicht allzusehr von den "normalen" (client)-python-bindings ... ( -> am besten durch NULL DIFFERENCE ;-) Der aktuelle Brick Viewer nutzt immer die aktuell veröffentlichten Python-Bindings. Hier das Beispiel nochmal mit zusätzlichem Callback: #!/usr/bin/env python3 # -*- coding: utf-8 -*- HOST = "localhost" PORT = 4223 UID = "GKp" # Change XYZ to the UID of your Air Quality Bricklet import asyncio from concurrent.futures import ThreadPoolExecutor from tinkerforge.ip_connection import IPConnection from tinkerforge.bricklet_air_quality import BrickletAirQuality import time _executor = ThreadPoolExecutor(10) _air_pressure_queue = asyncio.Queue() def cb_air_pressure(air_pressure, loop): asyncio.run_coroutine_threadsafe(_air_pressure_queue.put(air_pressure), loop) async def tf_async_call(name, func): value = await asyncio.get_event_loop().run_in_executor(_executor, func) print('{0}: {1}'.format(name, value)) async def async_main(aq): task1 = asyncio.ensure_future(tf_async_call('Temperature', aq.get_temperature)) task2 = asyncio.ensure_future(tf_async_call('Humidity', aq.get_humidity)) air_pressure = await _air_pressure_queue.get() print('Air Pressure: {0}'.format(air_pressure)) # Here your async code await asyncio.wait([task1, task2]) print('Done!') if __name__ == "__main__": ipcon = IPConnection() # Create IP connection aq = BrickletAirQuality(UID, ipcon) # Create device object ipcon.connect(HOST, PORT) # Connect to brickd # Don't use device before ipcon is connected loop = asyncio.get_event_loop() aq.register_callback(aq.CALLBACK_AIR_PRESSURE, lambda x: cb_air_pressure(x, loop)) aq.set_air_pressure_callback_configuration(100, False, 'x', 0, 0) loop.run_until_complete(asyncio.ensure_future(async_main(aq))) ipcon.disconnect() Consolen-Ausgabe: olaf@pc2:~/build20/python$ ./async_example2.py Air Pressure: 99750 Temperature: 2478 Humidity: 2707 Done! Zitieren
Gast piwo Geschrieben February 8, 2019 at 02:03 Geschrieben February 8, 2019 at 02:03 d.h. pro bricklet eine Queue und 10 workers ? oder gibt es da noch optimierungen ? sorry, aber ich habe meistens 12-16 bricklets pro stack ;-) Zitieren
borg Geschrieben February 8, 2019 at 08:39 Geschrieben February 8, 2019 at 08:39 Das hängt davon ab wie du dein Programm gestaltest und was mit den Werten gemacht werden muss und wie oft du Daten abfragst etc. Der ThreadPoolExecutor kann natürlich von allen Bricklets genutzt werden. Wahrscheinlich reichen da auch 2-4 Worker für beliebig viele Bricklets. Statt einer Queue pro Bricklet kannst du natürlich auch eine Queue für alle verwenden und neben den Daten den Messwert-Typ mit in die Queue packen. Zitieren
Gast piwo Geschrieben February 8, 2019 at 18:55 Geschrieben February 8, 2019 at 18:55 ... gibts das obige beispiel auch mit argumenten bei den calls des *** tf_async_call *** dass callbacks direkt aufgerufen werden und in die queue einspeisen ist irgendwo klar. aber wie geht man mit argumenten bzw. dem handling von argumenten von settern in deiner implementierung um ? async def tf_async_call(name, func, *args): ... run_in_executor(_executor, func,*args) oder mit partials ? lgw Zitieren
borg Geschrieben February 8, 2019 at 19:46 Geschrieben February 8, 2019 at 19:46 Die Argumente kannst du mit functools.partial umsetzen, ist auch so in der Python-Doku so dokumentiert: https://docs.python.org/3/library/asyncio-eventloop.html Rückgabewerte in Python asyncio kann man ganz allgemein mit Futures machen: https://docs.python.org/3/library/asyncio-future.html#asyncio.Future In dem Beispiel würdest du das Future (future = asyncio.Future()) mit bei dem tf_async_call übergeben und dann da wo aktuell das print ist future.set_result(value) aufrufen und an der Stelle wo du es benutzen willst kannst du dann result = await future machen. Hier gibt es ein Beispiel dafür: https://pymotw.com/3/asyncio/futures.html In dem Beispiel oben kannst du in tf_async_call aber auch einfach den Wert zurückgeben und nach dem await asyncio.wait([task1, task2]) kannst du task1.result() und task2.result() aufrufen. Zitieren
Gast piwo Geschrieben February 8, 2019 at 20:55 Geschrieben February 8, 2019 at 20:55 ... ich möchte die low-level routinen vermeiden ... d.h. keine futures, sondern ** tasks ** die ge-gathered werden möglicherweise mit einem loop-argument falls ich mehrere loops instanziere, und mit return_exceptions=True/False die auswirkungen von Exceptions auf das canceln von concurrent tasks bzw. das handling in der routine, die die tasks scheduled bin noch heftig am studieren ;-) jedenfalls soll das design sowas ähnliches wie invariante ergebnisse (d.h. alles oder nichts) über alle rekursiven task-ketten liefern ich habe keine lust, mich mit problemen "unten" einzeln durchzukämpfen... entweder alle operationen laufen, oder der ganze stack (und auch alle von seinen operationen abhägigen komponenten werden gecancelt oder auch restarted ... d.h. der stack ist in meinem design producer/consumer für sämtliche darübergelagerten tasks, die im wesentlichen aber nur ein "wiring" bzw. "mapping" von daten vom stack machen. alles was vom stack kommt ("sensor") oder in diesen injiziert wird ("actor") ist als gesamtheit zu sehen. als "kontrakt" zwischen "stack" und "rest". die state-machine dieser "beziehung" kennt nur 2 zustände des stacks : RUNNING oder FAILED. nach N mal FAILED in einem floating fenster gibts dann einen sys.exit(1) mit entsprechendem alarm per sms oder so ... eigentlich nichts wirklich unübliches, denke ich. und ich denke so ein design mit "minimalem coding" ist durchaus im interesse der allgemeinheit .... lgw Zitieren
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.