Seite 1 von 1

Lambda Function will kein Ergebnis liefern

Verfasst: 2. Jul 2023 15:51
von MISPEZI
Hallo zusammen,
bin im OH3 unterwegs und bekomme ein Unterprogramm einfach nicht hin:
Aufgabe soll sein anhand der Stelligkeit von Eingaben entweder eine führende "0" im Ausgabestring vorzusetzen oder nicht.
Dafür habe ich einen kleinen Test geschrieben - der so leider nicht funktioniert.
Hier sind die Rules dazu:

Code: Alles auswählen

// Sonderrules zu Testen mit S14
 // immer nur einmalige Ausführung um bestimmte Zustände herzustellen
var Number va_h = 7 
var Number va_m = 30
//var String Ausgabe

// Lambda für Ausgabe richtigstellen
val Functions$Function3< Number, Number, GenericItem >
    Daten_umsetzter = [
        In_h,
        In_m,
        Status |
        val Number h = ((In_h))
        val Number m = ((In_m))
        if (m < 10) {
            var Ausg = String::format("%.0f:0%.0f", h, m)
       }
        else {
            var Ausg = String::format("%.0f:%.0f", h, m)
        }
        postUpdate(Status, new StringType(Ausg))
]




rule "1 - Systemstart"
    when 
        System started
    then 
     MyTestSwitch.sendCommand(OFF)
end    
rule "2 - Einschalten"
    when 
        Item MyTestSwitch changed to ON
    then    
    // Grundwerte setzten 
        MyTest_in_h.postUpdate(7) 
        MyTest_in_m.postUpdate(30)
        MyTest_in_tog.postUpdate("15")
        va_h =((MyTest_in_h.state as DecimalType).floatValue)
        va_m =((MyTest_in_m.state as DecimalType).floatValue)
        Daten_umsetzter.apply( va_h, va_m, MyTest_in_tog )
//        Ausgabe = String::format("%.0f:%.0f",va_h,va_m)
        logInfo("Tester", "Werte festgelegt")
//        MyTest_in_tog.postUpdate(Ausgabe)
        

end 
die Fehlermeldung im Log lautet:
[ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'S14_testrules-2' failed: The name 'Ausg' cannot be resolved to an item or type; line 21, column 43, length 4 in S14_testrules
Er kann also die letzte Zeile nicht zuordnen obwohl ich den String mitgegeben habe.
Anmerkung meinerseits die Definition mit
Function3
ist falsch, es müsste
Function2
heißen - aber dann wird über die Anzahl der Argumente gemeckert.

Weiß jemand wie die Anweisung richtig heißt? Danke für eure Mühe

Re: Lambda Function will kein Ergebnis liefern

Verfasst: 3. Jul 2023 02:24
von udo1toni
Dein Problem ist, dass die Variable Ausg dort, wo Du auf sie zugreifst, nicht existiert.
Detailansicht:

Code: Alles auswählen

        else {
            var Ausg = String::format("%.0f:%.0f", h, m)
         // Ebene 2
        }
        postUpdate(Status, new StringType(Ausg))
     // Ebene 1
Du definierst die Variable auf Ebene 2 (die sich unterhalb von Ebene 1 befindet) und versuchst auf Ebene 1 auf sie zuzugreifen.

Ansonstenfürchte ich, dass Du von vornherein unglücklich abgebogen bist - es sei denn, das Ganze ist nur der Versuch, sich mit der DSL vertraut zu machen, aber selbst dann...
Die DSL kennt keine Unterprogramme. Selbst die von Dir verwendete "Funktion" (und Du brauchst tatsächlich Function3, denn Du übergibst drei Parameter, Stunde, Minute und das Item) ist nur ein Notbehelf. Meistens kann man ein typisches openHAB Programm deutlich eleganter und einfacher ohne Funktionen und Unterprogramme schreiben.

Deine Ausgabe von Stunde und Minute wäre einfacher so zu erledigen:

Code: Alles auswählen

 var Ausg = String::format("%d:%02d", h, m)
%02d bedeutet: stelle die Ganzzahl mit mindestens zwei Stellen dar. Ist die Zahl nur einstellig, fülle mit 0 auf.

Re: Lambda Function will kein Ergebnis liefern

Verfasst: 3. Jul 2023 12:04
von MISPEZI
Danke für die Schnelle Antwort.
Es ist wie immer im Leben. Ja, es sollte schon dazu dienen zu lernen, wie man mit "Unterprogrammen" in der DSL arbeitet. Und ja, man kommt auf solche Ideen, wenn man die einfachen Lösungen mit kennt oder findet.
Danke nochmal

Re: Lambda Function will kein Ergebnis liefern

Verfasst: 3. Jul 2023 14:36
von udo1toni
Leider gibt es keine gute Dokumentation der DSL (also vor allem in der Art eines Best Practice), aber platt gesagt ist die Sprache für die angenommene Verwendung fast perfekt designt.
Man schreibt keine Programme (damit geht es mal los), sondern Rules (oder Regeln).
Eine Regel läuft nicht, sie wird abgearbeitet (mithin sollte eine Rule nicht so entworfen sein, dass sie irgendwelche Schleifen dreht, weil ihr gerade langweilig ist)
Eine Regel reagiert auf ein (oder mehrere) Ereignis(se). Wann immer das Ereignis auftritt, wird die Rule aufgerufen und abgearbeitet.

Die Rule kümmert sich zu 99% um Items. Sie wertet also die Status von einem, oder mehreren Items aus und bestimmt daraus die Status anderer Items oder auch Befehle, die an Items gesendet werden.

Da es sich bei Items um Objekte handelt, gibt es Methoden zur Manipulation der Items, und wenn wir z.B. von Group Items sprechen, so kann man sich über die Eigenschaft .members eine Liste aller in der Gruppe zusammengefassten Items ausgeben lassen. Die Liste ist wieder ein Objekt und kann entsprechend gefiltert und sortiert werden, man kann die Größe der Liste bestimmen oder auch auf ganz bestimmte Elemente der Liste zugreifen - z.B. auf das letzte oder das erste Element eines Filterergebnisses. Oder man durchläuft alle Elemente der Liste mit .forEach(), um z.B. an alle Items einer Gruppe einen bestimmten Befehl zu senden - zusammen mit der Triggerquelle Member of (die stellt in der Rule dann ein Objekt zur Verfügung, welches das auslösende Item enthält) kann man z.B. eine einzige Rule schreiben, die sich aber um beliebig viele Items des selben Typs kümmern kann.

Beispiel: Ich habe knx Raumtemperaturregler (RTR). Die Betriebsart (Nachtabsenkung, Komfort, Frostschutz...) wird über eine Zahl gesetzt, die Rückmeldung erfolgt aber über eine andere Zahl (weil dort noch weitere Daten enhalten sind, z.B. ob gerade Kühlbetrieb oder Heizbetrieb aktiv ist)
Ich muss also für jeden RTR zwei Items vorsehen, das eine zum Senden, das andere zum Empfangen der Betriebsart, denn ich möchte natürlich in openHAB auch eine Änderung der Betriebsart sehen, wenn ich das am RTR selbst bediene. In der Oberfläche will ich aus verständlichen Gründen keine zwei Items haben, also brauche ich eine Rule, welche den Itemstatus des einen Items passend zum gelieferten Wert setzt. Ich habe neun RTR, brauche aber nur eine einzige Rule:

Code: Alles auswählen

rule "Betriebsart RTR"
 when
    Member of gHeat_Mode changed
 then
    var Integer newMode 
    val mode = (triggeringItem.state as DecimalType).toBigDecimal.toBigInteger
    val iName = triggeringItem.name.split("_").get(0).toString
    logDebug("rtr","Name is: {}, Mode is: {}",iName,mode)
    switch (mode) {
        case mode.testBit(0) : newMode = 1
        case mode.testBit(2) : newMode = 3
        case mode.testBit(3) : newMode = 4
        default              : newMode = 2
    }
    var myItem = gHeat_Set.members.filter[ f | f.name.startsWith(iName) ].head
    var Integer oldMode = 0
    if(myItem.state instanceof Number) oldMode = (myItem.state as Number).intValue
    if(oldMode != newMode) {
        logDebug("rtr","Name is: {}, oldMode is: {}, newMode is: {}",myItem.name,oldMode,newMode)
        myItem.postUpdate(newMode)
    }    
end
Die Rule triggert, wenn über ein Item der Gruppe gHeat_Mode ein geänderter Status gemeldet wird.
Daraufhin bestimmt die Rule den neuen Status anhand des Bitmusters (so ist das in der Rückmeldung codiert)
Anschließend sucht die Rule anhand des Namens des Items, welches die Rule getriggert hat das passende Gegenstück heraus und setzt dieses auf den errechneten Status.

Mit einem Unterprogramm würde man für jedes Itempaar eine Rule anlegen und von dort das Unterprogramm aufrufen, welches den Wert errechnet - umständlich. Ich brauche nur diese eine Rule, und wenn mir einfällt, 10 weitere Räume hinzuzubauen (mit eigenem Heizkreis), so lege ich einfach je zwei weitere Items an und füge sie den beiden Gruppen hinzu. Alles andere geschieht vollautomatisch, ohne eine einzige zusätzliche Zeile Code.

Unabhängig davon, ob man den Code oben jetzt direkt versteht ;) denke ich, wird klar, dass man mit der DSL extrem effizient programmieren kann :)