Positions Bestimmung an Hand der Laufzeit rule

Für welche Projekte verwendet Ihr OpenHAB? Was habt Ihr automatisiert? Stellt eure Projekte hier vor.

Moderatoren: Cyrelian, seppy

Antworten
mad-mike
Beiträge: 491
Registriert: 6. Jan 2021 18:05
Answers: 3

Positions Bestimmung an Hand der Laufzeit rule

Beitrag von mad-mike »

Moin zusammen.

Ich suche schon etwas länger, aber irgendwie wurde ich nicht fündig.

Vielleicht hat auch einer einen Link, oder eine Vorlage für mein Vorhaben.

Es muss doch möglich sein, eine rule zu erstellen, womit man eine Position von einem Rollo, oder Motor bestimmen kann.

Ich denke dabei erstmal ganz einfach: Rollo fährt, rule zählt Zeit, rechnet % schreibt in Proxy Item.

Mir ist bekannt, das die Zeiten für hoch und runter Lauf nie 100% sind. Drum dachte ich weiter: bei Befehl in die Endlage:

Einfach Motor 10 Sekunden länger bestromen um neuen 0 Punkt zu generieren.

Und ein setpoint, könnte dann auch den Rollo in eine bestimmte Position fahren.


z.b. ist es bei lcn nicht so einfach eine % Steuerung zu realisieren, aber eventuell kann man hier eine rule Lösung anstreben.

Auch könnte man hier eventuell ein Relais Board verwenden, um die Motoren hoch und runter laufen zulassen.

Wisst ihr was ich meine? Jemand eine Idee, oder sogar schon um gesetzt?
Gruss mad-mike

openHABian 4.3.5 auf Raspberry Pi 4 Mod. b (8GB) ;)

Benutzeravatar
udo1toni
Beiträge: 15249
Registriert: 11. Apr 2018 18:05
Answers: 243
Wohnort: Darmstadt

Re: Positions Bestimmung an Hand der Laufzeit rule

Beitrag von udo1toni »

Ich hatte so was mal vor Jahren im Einsatz. Das Ganze ist aber recht kritisch - Du darfst nie vergessen, dass openHAB kein Echtzeitsystem ist. Mehr noch, es ist asynchron designed. Das ist an mancher Stelle sehr praktisch, für diese Anwendung aber eher hinderlich, da man keine qualifizierten Aussagen über Laufzeiten machen kann.
Dennoch ein Beispielcode:

Code: Alles auswählen

var Long lStart = null                                                    // Speicher für Laufzeitmessung
var Integer iRollCommand = null                                           // Zwischenspeicher für Befehl

val Integer iUpTime   = 15000                                             // Gesamtlaufzeit von geschlossen nach offen
val Integer iDownTime = 13000                                             // Gesamtlaufzeit von offen nach geschlossen

rule "Rollo"
when
    Item MyRollo received command                                         // Befehl empfangen
then
    var Integer iProzent = (MyRollo.state as Number).intValue             // aktuelle gespeicherte Position
    switch(receivedCommand) {                                             // Welcher Befehl
        case STOP : {
            if(lStart === null) return;                                   // falls was schief gegangen ist, raus
            val Laufzeit = now.toInstant.toEpochMilli - lStart            // Differenz Start zu Stopp in ms
            if(iRollCommand == 1) {                                       // Falls Richtung hoch war
                iProzent = iProzent - (Laufzeit/iUpTime)*100              // ziehe Anteil der Laufzeit entsprechend von Prozent ab
                if(iProzent < 0) iProzent = 0                             // falls unter 0, setze 0
            } else if(iRollCommand == -1) {                               // Falls Richtung runter war
                iProzent = iProzent + (Laufzeit/iDownTime)*100            // addiere Anteil der Laufzeit
                if(iProzent > 100) iProzent = 100                         // Falls über 100, setze 100
            }
            logInfo("rollo",
                "Rollo gestoppt. Laufzeit {} ms, neue Position {} %",
                Laufzeit,iProzent)
            MyRollo.postUpdate(iProzent)                                  // melde neue Position
            lStart = null                                                 // leeren!
        }
        case UP : {
            lStart = now.toInstant.toEpochMilli                           // Setze Startzeit
            iRollCommand = 1                                              // Setze Richtung
        }
        case DOWN : {
            lStart = now.toInstant.toEpochMilli                           // Setze Startzeit
            iRollCommand = -1                                             // Setze Richtung
        }
        default : {                                                       // Positionsfahrt
            val Integer iSoll = (receivedCommand as Number).intValue      // Setze Sollposition
            val Integer iMyTime = 0                                       // Benötigte Fahrzeit
            if(iSoll < iProzent) {                                        // Falls Fahrtrichtung hoch
                iRollCommand = 1                                          // Setze Richtung
                iMyTime = (iProzent-iSoll)/100*iUpTime                    // bestimme benötigte Zeit
            }
            if(iSoll > iProzent) {                                        // Falls Fahrtrichtung runter
                iRollCommand = -1                                         // Setze Richtung
                iMyTime = (iSoll-iProzent)/100*iDownTime                  // bestimme benötigte Zeit
            }
            if(iMyTime > 0) {                                             // Falls Fahrtzeit > 0
                createTimer(now.plusNanos(1000000*iMyTime),[|             // Erzeuge Timer (ohne Zugriff!)
                    MyRollo.sendCommand(STOP)                             // Sende Stop
                ])
                MyRollo.sendCommand(if(iRollCommand == 1) UP else DOWN)   // Sende Befehl UP oder DOWN
            }
        }
    }
end
Sieht schlimm aus...
Erst mal die globalen Variablen und Konstanten:
lStart ist Zwischenspeicher für einen Unix Zeitstempel (Anzahl Millisekunden seit 1.1.1970,00:00:00.000 Uhr) Hier merkt sich die Rule, wann ein Befehl UP oder DOWN empfangen wurde
iRollCommand dient zum Merken der Fahrtrichtung

Die beiden Konstanten iUpTime und iDownTime geben die exakte Verfahrdauer von oben nach unten bzw. von unten nach oben an. Die beiden Zeiten weichen voneinander ab, weil der Motor beim Aufwickeln des Rollladens mehr Kraft benötigt.

Lokale Variablen und Konstanten:
iProzent: Die Position des Ladens
Laufzeit: Die Fahrtdauer in Millisekunden
iSoll: neue Sollposition
iMyTime: Errechnete Fahrzeit zum Erreichen der neuen Sollposition



Die Rule reagiert auf empfangene Befehle. Da die Position an verschiedenen Stellen gebraucht wird, wird zunächst die aktuell gespeicherte Position in iProzent gesichert.

Nun gibt es vier verschiedene Möglichkeiten:
1. Der Befehl lautete STOP
2. Der Befehl lautetet UP
3. der Befehl lautete DOWN
4. etwas Anderes :)

Im Fall 2 oder 3 müssen der aktuelle Zeitpunkt und die gewünschte Fahrtrichtung gesichert werden.
Im Fall 1 muss zunächst geprüft werden, ob es einen gültigen Wert in lStart gibt - falls nicht, muss die Rule abgebrochen werden. Ist dieser Test überstanden, können wir gefahrlos die Laufzeit bestimmen, indem wir lStart vom aktuellen Unix Zeitstempel abziehen.
Falls die Richtung aufwärts war, ergibt sich die neue Position aus der Differenz der alten Position minus der Laufzeit, geteilt durch die Gesamtlaufzeit * 100. Lief der Motor länger als nötig, um auf Position 0 zu kommen, ergibt sich dabei ein negativer Wert, den wir abfangen müssen, also falls iProzent kleiner 0, setze iProzent auf 0.
Das gleiche gilt sinngemäß für die Abwärtsbewegung, nur dass hier der prozentuale Anteil addiert wird und bei Überschreiten der 100 die 100 als Maximalwert gesetzt wird.
Der logInfo() Befehl sieht etwas komisch aus :) ich wollte damit die Kommentar-Einrückungen etwas minimieren...

Für Fall 4, "etwas Anderes" bleibt eigentlich nur noch eine Zahl als Befehl übrig, denn andere Befehle kennt ein Rollershutter Item nicht.
Die Zahl ist also die neue Sollposition.
Nun müssen wir die benötigte Fahrdauer bestimmen. Diese ist natürlich abhängig von der Fahrtrichtung, die sich wiederum daraus ergibt, ob die neue Position kleiner oder größer als die aktuelle Position ist.
Dabei gilt, dass die Differenz aus den beiden Positionen den prozentualen Verfahrweg ergibt, also z.B. von 40 auf 50 wären 10. Das muss man dann durch 100 teilen und mit der Gesamtfahrzeit in Fahrtrichtung multiplizieren, um die nötige Fahrzeit zu ermitteln, natürlich wieder für beide Fahrtrichtungen getrennt :)
Ergibt sich eine Fahrtzeit über 0 (es könnte ja auch eine Positionsfahrt auf die aktuelle Position angefordert werden), so wird nun also der Timer für den Stop-Befehl gestartet und anschließend der Befehl UP bzw. DOWN gesendet (womit sich die Rule nun selbst triggert!)

Ich hoffe, dass die Theorie hinter der Rule klar wird :lol:

Ich hab bestimmt noch irgendwo Fehler drin ;) wie immer...

Nächstes Level ist dann, diese Rule so zu generalisieren, dass sie mit Gruppen funktioniert (wobei das bei Rollläden kritisch werden könnte - eine Rule kann seit 3.0 nur noch einmal zeitgleich ausgeführt werden, Rollläden werden aber gerne in Gruppen verfahren...)
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet

mad-mike
Beiträge: 491
Registriert: 6. Jan 2021 18:05
Answers: 3

Re: Positions Bestimmung an Hand der Laufzeit rule

Beitrag von mad-mike »

Danke dafür.

Ich habe dies mal einfach versucht und ein Proxy Item "MyRollo" erstellt... (rollershutter)

Nch dem Speichern der Rule:

Info:

Code: Alles auswählen

2023-02-15 19:47:31.243 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'rollo.rules', using it anyway:

Assignment to final variable

Assignment to final variable

Constant condition is always false.

und beim ausführen des Rollo schalters:

Code: Alles auswählen

2023-02-15 19:41:13.584 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'rollo-1' failed: An error occurred during the script execution: Could not invoke method: org.eclipse.xtext.xbase.lib.IntegerExtensions.operator_lessThan(int,int) on instance: null in rollo
Im Log sehe ich, das der Rollo wechselt von 0 - direkt auf 100 und umgedreht von 100 - 0.

ich kann ihn auch setzen mit dem

Das Log aus der Rule wird nicht angezeigt...

Und die Fehlermeldung wird immer beim befehl ""Stop"" ausgeführt.
Gruss mad-mike

openHABian 4.3.5 auf Raspberry Pi 4 Mod. b (8GB) ;)

Antworten