mts75 Geschrieben May 29, 2015 at 14:15 Geschrieben May 29, 2015 at 14:15 Hallo Zusammen, mir fehlen aktuell gute Ideen, wie ich von den Hello-World-Beispielen bei den einzelnen Bricks zu einem komplexeren Programm komme, weil mir das bevorzugte asynchrone Eventhandling ständig wild im Programm herumspringt und Multithreading-Fragen aufwirft. Leider habe ich keine Beispiele für komplexere Programme auf den Seiten von TF oder hier im Forum gefunden. Um die Probleme überhaupt diskutieren zu können, hier mal ein konkretes Beispiel: Steuerung einer fiktive Hardware, ein 3-achsiger Roboter-Arm: 2 x Schrittmotoren jeweils am Stepper-Brick für den Arm 1 x ein einfacher Motor am DC-Brick zur Drehung der Bodenplatte Der normale Motor hat eine Art Taktimpuls (z.B. Lichtschranke) zum "mitzählen" von Impulsen am IO16-Bricklet zur Positionserkennung. 1 x Ein Taster am IO16, um einen Bewegungsablauf zu starten. (Es geht mir hier nur um die Programmierung und nicht um die Frage, ob die Hardware sinnvoll aufgebaut ist) Um einfache Bewegungsfolgen laufen zu lassen, brauche ich wohl eine Komfort-Methode in der Form "geheZuPosition(x,y,z)", die erst zurückkehrt, wenn die jeweilige Position vollständig erreicht ist. Darin muss nach Start der drei Motoren auf die folgenden 3 'Events' gewartet werden: Stepper 1 ist angekommen, Stepper 2 ist angekommen, (geht ja Dank der Bricks weitesgehend automatisch) IO16-Zähler hat die richtige Anzahl Impulse gezählt, so das auch die Position erreicht ist, um den DC-Motor abzuschalten. Die einzelnen, kurzen Demo-Codestrecken der Bricks würden nun jeweils "Position-Erreicht"-Listener an die Stepper und ein "Impuls wurde gezählt" Event an das IO16 zu binden, um nicht ständig die Hardware "pollen" zu müssen und damit Last auf den beteiligen Bussen zu sparen. (USB, Wifi etc.) Das IO16 hat nun die Doppelaufgabe -> Zunächst Warten auf Tastendruck / dann Warten auf nächsten Zählimpuls zur Positionsbestimmung und vielleicht weitere Aufgaben. Wie verknüpfe ich nun temporär solche Events dynamisch zu einer größeren Gesamtaufgabe? Ich würde vermutlich 3 globale Variablen PositionX/Y/Z-erreicht einführen (in Java Volatile wegen Multithreading Problematik) und in den asynchronen Event-Listenern nur diesen Status pflegen. Dann könnte ich den Haupt-Thread in der Methode mit Thread.sleep periodisch schlafen legen und von Zeit zu Zeit prüfen, ob alle Variablen true geworden sind, sie für's nächste Mal zurücksetzen und dann die Methode verlassen. Aber ich hätte ggf. einen Zeitverlust bis zum Aufwachend es Main-Threads, auch wenn ich jetzt schon die BUS-Last schone. Vor dem eigentlichen Lauf muss ich ja auch noch auf den Tastendruck warten, der im gleichen Listener vom IO16 behandelt werden könnte, wie das Zählevent, aber auch da ist die Frage, wohin in der Zwischenzeit mit dem Main-Thread. Wenn ich das alles mit statischen Variablen in einer Klasse baue, mag es funktionieren, passt aber so garnicht gut zu meinem persönlichen Anspruch an eleganten und robusten Code ... ;-) Die Erfahrung aus prozeduralen Programmiersprachen mit Nutzung vieler globalen Variablen zeigt, dass man da irgendwann nicht mehr durchblickt, wenn das Projekt wächst, weil immer schlechter vorhersagbar ist, welche Variable zu welchem Zeitpunkt welchen Zustand hat und von wo dieser zuletzt kam. Nun die Fragen an die Profis, wie eine solche Steuerung 'schön' aufgebaut werden könnte. Teilt Ihr solche Logik sinnvoll auf mehrere Klassen auf? Wie vermeidet man die ganzen "globalen" oder sogar statischen Variablen/Zustände, falls möglich? Wie pausiert oder beschäftigt Ihr den Main-Thread, während Ihr auf Events wartet? Nehmt ihr einen großen Listener pro Brick oder viele kleine, die je nach aktueller Aufgabe kurz gebunden und sofort wieder entfernt werden? Ich bin für jeden Denkanstoß dankbar ... ;-) Zitieren
remotecontrol Geschrieben May 30, 2015 at 08:21 Geschrieben May 30, 2015 at 08:21 Hier gibt es eine Menge verschiedener Möglichkeiten ... Ich würde Jedes Brick/Bricklet softwareseitig an ein Objekt binden. Das Objekt wird über die Listener mit dem aktuellen Hardware-Zustand aktualisiert und kennt zu jeder Zeit den Zustand der Hardware. D.h. Statusabfragen von außen fragen dann nur diese Objekte ab und gehen nicht direkt auf die TF-API / Hardware.Wenn eine neue Position gesetzt werden soll, muss die Methode im Main-Thread den aktuellen Zustand der Objekte erstmal prüfen und testen, ob die neue Position gesetzt werden kann (oder schon erreicht oder Abstand innerhalb der minimalen Bewegung, ...)Nach Senden von Positionsänderungen über die an die Bricks gebundenen Objekte könnte die Methode einfach wait(timeout) nutzen.Die an die Hardware gebundenen Objekte müssen ein Synchronisations-Objekt des Main-Thread kennen und könnten bei jeder Positionsänderung per notify() den Main-Thread aufwecken.Die Methode im Main-Thread muss dann aber nach jedem Wake-Up kurz prüfen, ob die Position erreicht ist.In Summe sollte der wait() nie endlos warten für den Fall, dass aus irgendeinem Grunde die Position nicht erreicht wird.Alternativ könnten die Brick-Objekte den notify() auch nur dann auslösen, wenn die gewünschte Position erreicht ist. Dann muss der main-Thread nicht so oft prüfen, ob die Position korrekt erreicht ist.Letzteres geht einfach, indem die Positionsänderung aus dem Main auch über das eigene Brick-Objekt geht (welches dann intern erst die TF-API nutzt), damit weiss das Objekt ja, welche Ziel-Position erreicht werden soll und könnte den notify erst auslösen, wenn diese Zielposition erreicht ist (für jenen Teil der Steuerung, wo das Erreichen der Zielposition nicht direkt per TF-API-Callback mitgeteilt wird). Das mal als Versuch, einen möglichen Ansatz zu beschreiben. Zitieren
mts75 Geschrieben May 31, 2015 at 00:09 Autor Geschrieben May 31, 2015 at 00:09 Vielen Dank zunächst, dass du über das Problem so ausführlich nachgedacht hast ... Ich werde den Wait-Notify-Ansatz mal austesten und berichten. 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.