Mehrere Befehle an Serielle Schnittstelle mit Verarbeitung der Antworten

Begonnen von abc2006, 25 Februar 2019, 01:14:04

Vorheriges Thema - Nächstes Thema

abc2006

Hi,

ich stehe gerade ein bisschen auf dem Schlauch, könnte durchaus sein, dass ich den Wald vor lauter Bäumen nicht sehe.

Mein Problem ist folgendes: Ich schreibe an einem Modul, welches Befehle an die serielle Schnittstelle sendet und die Antworten darauf verarbeiten soll.
Mein Problem ist, dass ich nicht weiss, wie ich das logisch umsetze.

Der erste Ansatz war, ich schreibe eine sub, in der ich nacheinander die Befehle sende, mit etwas pause, und die ReadFn liest die Antworten aus und ordnet diese den Abfragen zu.

Allerdings habe ich hier den Eindruck, dass die ReadFn nicht aufgerufen wird, während eine andere Funktion noch läuft. Zumindest gehen bei mir alle Abfragen raus und dann kommen geballt die Antworten.

Ich bin mir ebenfalls weiterhin sicher, dass auch andere Module vor diesem Problem stehen... Allerdings habe ich es nicht geschafft, eines zu finden, in dessen Quellcode ich eine Lösung erkannt habe.

Bitte gebt mir einen Tipp, wie ich das sinnvoll! löse... Bis dahin probiere ich weiter, mein momentaner Ansatz wäre, dass die ReadFn am Ende die Abfrage des nächsten Parameters auslöst...

Danke für eure Mühe,
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

CoolTux

Ist es denn relevant welche Antwort zu welchen Sendebefehl es gibt. Kannst Du es nicht anhand der Antwort an sich zu ordnen?

Ansonsten kannst Du natürlich auch die Write Befehle in eine queue schicken und einzeln jeweils nach erhalt der Antwort abarbeiten lassen.

Beispiel wäre das TeslaPowerWall Modul. Alle Writes kommen in ein Array und dann wird das erste losgeschickt. Erst nach erhalt der Antwort wird das nächste aus dem Array geholt und los geschickt und so weiter.


Grüße
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

rudolfkoenig

Zusaetzlich: eine Pause im Modul zu machen ist keine gute Idee, weil FHEM Single-Threaded ist.
D.h. es gibt nur eine Ausfuehrungsreihenfolge, wenn/solange diese blockiert, blockiert die komplette Anwendung.

abc2006

Zitat von: CoolTux am 25 Februar 2019, 08:03:03
Ist es denn relevant welche Antwort zu welchen Sendebefehl es gibt. Kannst Du es nicht anhand der Antwort an sich zu ordnen?

Ja, ist leider relevant. Die Antwort beinhaltet lediglich die Werte. Ich könnte natürlich anhand der länge der Antwort raten, welche Abfrage es war.. aber ich weiss nicht, ob vielleicht mehrere Abfragen mit der gleichen Antwort-Länge kommen .. hab noch nicht alle Befehle implementiert...


ZitatAnsonsten kannst Du natürlich auch die Write Befehle in eine queue schicken und einzeln jeweils nach erhalt der Antwort abarbeiten lassen.

Das war auch meine Idee, ja. Aber, da muss ich mal Rudi reinzitieren:

Zitat
eine Pause im Modul zu machen ist keine gute Idee [...] solange diese blockiert, blockiert die komplette Anwendung.
Hab ich dann gemerkt bzw. war mir das Ansatzweise vorher klar. Deshalb gibts ja die BlockingCall...

ZitatBeispiel wäre das TeslaPowerWall Modul. Alle Writes kommen in ein Array und dann wird das erste losgeschickt. Erst nach erhalt der Antwort wird das nächste aus dem Array geholt und los geschickt und so weiter.

Das schau ich mir mal an. Alle Module, die ich gefunden hatte (und deren Inhalt für mich verständlich war), haben mit TCP gearbeitet...

Danke!
Stephan

So. Hab mir mal das TeslaPowerWall angeschaut.
Falls ich das richtig verstanden habe:
Nach jedem Interval packst du alle Pfade aus %paths in die actionQueue falls diese leer ist und rufst dann getData auf. Das holt sich immer ein Element aus der Liste und fragt es ab.

Der Unterschied zu meinem vorgehen wäre, dass ich ja nicht mit der HTTP-utils arbeite, die die Daten direkt in der callback-fn zurückliefert, sondern ich habe die DevIO_SimpleWrite, die mir die Abfrage wegschickt, und bin dann abhängig von Fhem, welches mir die ReadFn aufruft, die dann den Wert
verarbeitet.

Idee:
Ich könnte natürlich in der ReadFn den Versand der nächsten Nachricht anstoßen - glaube ich.
Im einfachsten Fall einfach mit InternalTimer(gettimeofday()+$interval...); und dann wird halt alle Intervall-Sekunden der nächste Parameter abgefragt.

Im nächsten Schritt könnte ich dann vielleicht so eine Liste implementieren und so lange weitere Abfragen aufrufen, bis die Liste leer ist, und erst dann das Interval abwarten.

Wäre das eine gute Lösung? Oder schlägt da jeder, der Ahnung hat, die Hände über dem Kopf zusammen?
Das Problem mit dem blockieren hätte ich ja dann auch kaum, da ich ja nicht auf die Antwort warte, sondern informiert werde (ReadFn), wenn diese eintrifft ...

Ich probier das mal... Danke!

Grüße,
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

CoolTux

Du hast das schon korrekt erkannt.
Du schiebst alle writes in eine Array Queue. Dann rufst Du write auf und holst Dir mit shift das erste Array Element. Nun muss auf jeden Fall ReadFn aufgerufen werden. Sollte ja hoffentlich durch die Antwort Daten erfolgen.
Und wenn dann die ReadFn aufgerufen wird sorgst Du am Ende der ReadFn dafür das die write Funktion mit dem nächsten Array Element als Option aufgerufen wird. Das machst Du wieder mit shift. Und so weiter.
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

abc2006

So. Soweit, sogut. Danke eurer Hilfe :).
Hab jetzt ne Version, die *funktioniert* :

https://github.com/abc2006/pvfhem/blob/master/FHEM/15_effekta.pm

Allerdings hab ich jetzt das Problem, dass das Endgerät manchmal nicht antwortet. Dadurch wird meine Schleife nicht weiter bearbeitet (weil ich auf eine Antwort warte, die nicht kommt).

Wie würde man das sauber lösen? Im Prinzip hätte ich folgende Idee:
a) Ich baue das um, dass ich alle $interval sekunden die Queue leere (sicherstelle, dass sie leer ist), neu fülle und die Anfrage wieder los schicke. Nach dem Motto "udp".

b) Ich bräuchte einen Timeout, der mir sagt "du hast jetzt seit 10 Sekunden keine Antwort erhalten, versuchs nochmal. Dazu könnte ich einen neuen InternalTimer starten, der prüft, ob innerhalb der gegebenen Zeitspanne was passiert ist. Nachteil wäre, dass ich zwei Timer laufen hab... -> doppelte "Last".

Welchen Ansatz würdet ihr bevorzugen, und warum?

Danke und Grüße,
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

Beta-User

Hmm, du könntest dir auch die letzte Abfrage merken, und dann nochmal schicken, wenn innerhalb des timeouts keine Antwort kam. Evtl. hilft es auch, den timeout dann zu verlängern (bis zu einem Maximalwert), es macht keinen großen Sinn zu versuchen, ein Gerät zu erreichen, das dauerhaft nicht mehr antwortet. Kommt aber darauf an; wenn die Anfrage an sich "kaputt" ist, und deswegen keine Antwort kommt, ist ein Reset besser.

Noch eine allgemeine Frage: kann es vorkommen, dass eine neuere Anweisung eine ältere überschreibt? Dann sollte auch die ältere aus der loop genommen werden.

(sowas ähnliches ist für ACK-Messages in MYSENSORS drin: da wird der timeout immer um eine Sekunde verlängert nach jedem erfolglosen Versuch; und nur die jeweils neueste Anweisung an eine bestimmte "Adresse" wird in der Queue gehalten).
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

abc2006

Zitat von: Beta-User am 05 März 2019, 14:34:01
wenn die Anfrage an sich "kaputt" ist, und deswegen keine Antwort kommt, ist ein Reset besser.
Ich weiss leider nicht, warum das Gerät manchmal nicht antwortet. Da das ganze ja seriell läuft, hab ich da auch keine Kontrollmöglichkeiten - naja, zumindest sind mir - stand jetzt - keine bekannt.
Die Abfragen funktionieren ja meistens - und ich ändere nichts daran.
Sind bisher auch wirklich nur "abfragen", mit dem Parameter setzen hab ich mich noch nicht beschäftigt.
Zitat
Noch eine allgemeine Frage: kann es vorkommen, dass eine neuere Anweisung eine ältere überschreibt? Dann sollte auch die ältere aus der loop genommen werden.
Da ich nur Messwerte hole, ist das eigentlich unkritisch. Deshalb die Idee mit dem Reset. Dadurch passt natürlich das update-Intervall der Readings nicht mehr 100-prozentig.

Zitat
(sowas ähnliches ist für ACK-Messages in MYSENSORS drin: da wird der timeout immer um eine Sekunde verlängert nach jedem erfolglosen Versuch; und nur die jeweils neueste Anweisung an eine bestimmte "Adresse" wird in der Queue gehalten).
hmmmmm .... schau ich mir mal an.
Danke,
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

Beta-User

Na ja, wenn du DevIO verwendest, müßte doch jede eingehende Nachricht recht einfach ins Log zu schreiben gehen, oder?
Einfach vor der Weiterverarbeitung einen entsprechenden log3-Befehl schreiben, dann kannst du hinterher via verbose-Angabe am Device festlegen, wie gesprächig das sein soll.

Und für ausgehende Nachrichten dto.
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

abc2006

Achso - ja, das habe ich durchaus bereits umgesetzt. und die Abfragen werden laut DevIO gesendet - es kommt aber es kommt - laut DevIO - keine Antwort.
Grüße,
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

Beta-User

Und woher liest du dann die Antwort (bzw. überhaupt Daten vom Zielgerät). Müßte doch eine ReadFn geben, oder?!?
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

abc2006

ja, die gibt es. Wenn aber keine Antwort kommt, wird diese auch nicht aufgerufen - was wie oben geschildert mein Problem ist.
Grüße,
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

Beta-User

auf die Schnelle: mit dem Senden den Sendebefehl merken, timer starten, der den Befehl nochmal sendet, wenn nichts gelesen wurde. Kommt zwischendurch was, den timer abbrechen...
Kommt n-Mal nichts zurück: Reset
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

abc2006

Ich habe mir mal ein paar Zeilen aus dem httpmod-modul ausgeliehen ;)

Danke für deine Hilfe, momentan scheint es zu funktionieren. Ich lass es mal laufen und schau mal, wie lange ;)

Danke euch allen,
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX

abc2006

So, bisher läuft es klaglos und ich hab mein Script weiter aufgebohrt.
Dabei bin ich über das Thema CRC-Checksummen gefallen.
ich habe mittlerweile herausgefunden, dass ich eine
CRC-16/XMODEM -Checksumme benötige.

In den meisten Modulen sind Zeilen, die darauf schließen lassen, dass der Author es selbst programmiert hat, wenige verlangen zusätzliche Module...

Was davon ist "best practice" ?

Grüße,
Stephan
FHEM nightly auf Intel Atom (lubuntu) mit VDSL 50000 ;-)
Nutze zur Zeit OneWire und KNX