OH 3 Rule time problem with loop

Hello,

I have a strange probleme. I would like to switch devices in a decent order depending on a trigger. In principle it works fine however if all Trigger Switches are activated for some reason the switch schalter 2 goes on and off a couple times before trigger 3 starts. I looks like that OH 3 is having a time problem to work through the while loop. What can I do or where is my mistake.

Thanks for your help !

//Switch Test_Scheife “Test_Scheife” [Equipment]

//Switch Test_Trigger1 “Test_Trigger1” [Equipment]

//Switch Test_Trigger2 “Test_Trigger2” [Equipment]

//Switch Test_Trigger3 “Test_Trigger3” [Equipment]

//Switch Test_Schalter1 “Test_Schalter1” [Equipment]

//Switch Test_Schalter2 “Test_Schalter2” [Equipment]

//Switch Test_Schalter3 “Test_Schalter3” [Equipment]

var Timer ttest1

var Timer ttest2

var Timer ttest3

rule “Bewaesserung_in_a_row”

when Item Test_Scheife changed to “ON”

then

ttest1 = null

ttest2 = null

ttest3 = null

var i = 1

while ( (i) < 4 ) {

    if(Test_Trigger1.state==OFF && i==1) i = i+1

    if(Test_Trigger1.state==ON && i==1){ Test_Schalter1.sendCommand("ON")

    ttest1 = createTimer(now.plusSeconds(10), [ |

    Test_Trigger1.sendCommand("OFF")

    createTimer(now.plusSeconds(1), [ | Test_Schalter1.sendCommand("OFF")])  

                                    ]) }                                            

    if(Test_Trigger2.state==OFF && i==2) i = i+1

    if(Test_Trigger2.state==ON && i==2){ Test_Schalter2.sendCommand("ON")

    ttest2 = createTimer(now.plusSeconds(10), [ |

    Test_Trigger2.sendCommand("OFF")

    createTimer(now.plusSeconds(1), [ | Test_Schalter2.sendCommand("OFF")])

                                    ]) }

    if(Test_Trigger3.state==OFF && i==3) i = i+1

    if(Test_Trigger3.state==ON && i==3){ Test_Schalter3.sendCommand("ON")

    ttest3 = createTimer(now.plusSeconds(10), [ |

    Test_Trigger3.sendCommand("OFF")

    createTimer(now.plusSeconds(1), [ | Test_Schalter3.sendCommand("OFF")])

                                    ]) }

    }

Test_Scheife.sendCommand("OFF")

end

Please use code fences.

```
code goes here
```

Use indentation to show what code is part of what context. Instead of:

createTimer(now.plusSeconds(1), [ |
TestTrigger1.sendCommand("OFF")

use indentation to show the code that is part of that timer.

createTimer(now.plusSeconds(1), [ |
    TestTrigger1.sendCommand("OFF")
    // indent every line until the closing "])"

The same goes for if statements. Pretty much every time you have an opening block (i.e. [ or {) all the following lines should be indented until the closing ] or }.

Even for this relatively simple rule it’s really hard to tell what code is in what context.

If you have more than one line in a context, always start those lines on the next line, not on the same line. Instead of

if(Test_Trigger1.state==ON && i==1){ Test_Schalter1.sendCommand("ON")

use

if(Test_Trigger1.state==ON && i==1){ 
    Test_Schalter1.sendCommand("ON")

Why all of these formatting suggestions? Because as written this code is exceptionally difficult to figure out what it does. I can’t even tell where your ttest1 timer ends.

Your code will create hundreds of thousands of timers (even in the short time of ten seconds for each timer) as the while loop, well… loops forever, in a millisecond. and as the first condition is not true for the first ten seconds (Test_Trigger1 is ON) but the second is, it will turn ON Test_Schalter1 and create a timer, then set the var ttest1 to the pointer for that timer, loop the while, condition 1 is false, but condition two is still true, so it will create another (additional!) timer aand set the var to the new pointer. at this moment, the pointer of the first timer gets lost, but the timer is still there, and it’s active.
Same is for all other timers, a few seconds later.

So, at least you have to check if ttest1 is null before creating the timer

//Switch Test_Scheife “Test_Scheife” [Equipment]
//Switch Test_Trigger1 “Test_Trigger1” [Equipment]
//Switch Test_Trigger2 “Test_Trigger2” [Equipment]
//Switch Test_Trigger3 “Test_Trigger3” [Equipment]
//Switch Test_Schalter1 “Test_Schalter1” [Equipment]
//Switch Test_Schalter2 “Test_Schalter2” [Equipment]
//Switch Test_Schalter3 “Test_Schalter3” [Equipment]

var Timer ttest1
var Timer ttest2
var Timer ttest3

rule “Bewaesserung in a row”
when
    Item Test_Scheife changed to ON
then
    ttest1 = null
    ttest2 = null
    ttest3 = null
    var i = 1
    while (i < 4) {
        if(Test_Trigger1.state==OFF && i==1)
            i = i + 1
        if(Test_Trigger1.state==ON && i==1 && ttest1 === null) {
            Test_Schalter1.sendCommand(ON)
            ttest1 = createTimer(now.plusSeconds(10), [ |
                Test_Trigger1.sendCommand(OFF)
                createTimer(now.plusSeconds(1), [ | 
                    Test_Schalter1.sendCommand(OFF)
                ])
            ])
        }
        if(Test_Trigger2.state==OFF && i==2)
            i = i + 1
        if(Test_Trigger2.state==ON && i==2 && ttest2 === null) {
            Test_Schalter2.sendCommand(ON)
            ttest2 = createTimer(now.plusSeconds(10), [ |
                Test_Trigger2.sendCommand(OFF)
                createTimer(now.plusSeconds(1), [ |
                    Test_Schalter2.sendCommand(OFF)
                ])
            ])
        }
        if(Test_Trigger3.state==OFF && i==3)
            i = i + 1
        if(Test_Trigger3.state==ON && i==3 && ttest3 === null) {
            Test_Schalter3.sendCommand(ON)
            ttest3 = createTimer(now.plusSeconds(10), [ |
                Test_Trigger3.sendCommand(OFF)
                createTimer(now.plusSeconds(1), [ |
                    Test_Schalter3.sendCommand(OFF)
                ])
            ])
        }
    }
    Test_Scheife.sendCommand(OFF)
end

A more elegant solution without while and only one timer:

//Switch Test_Scheife “Test_Scheife” [Equipment]
//Switch Test_Trigger1 “Test_Trigger1” [Equipment]
//Switch Test_Trigger2 “Test_Trigger2” [Equipment]
//Switch Test_Trigger3 “Test_Trigger3” [Equipment]
//Switch Test_Schalter1 “Test_Schalter1” [Equipment]
//Switch Test_Schalter2 “Test_Schalter2” [Equipment]
//Switch Test_Schalter3 “Test_Schalter3” [Equipment]

var Timer   tSprinkler = null                             // timer for sprinkler
var Integer iSprinkler = 0                                // counter (like the i in the old while)
val Long lTenMillis = 10000000                            // 10.000.000 Nanos = 10 Milliseconds

rule “Bewässerung in a row”
when
    Item Test_Scheife changed to ON
then
    tSprinkler?.cancel                                    // cancel existing timer
    iSprinkler = 0                                        // set counter to 0
    tSprinkler = createTimer(now.plusNanos(nTenMillis),[| // create timer and start almost immediately
        var sSwitch1 = OFF                                // new state for Switch 1
        var sSwitch2 = OFF
        var sSwitch3 = OFF
        var Long lResched = nTenMillis                    // default time for reschedule
        val Long lElevenSecs = 11000000000                // 11.000.000.000 Nanos = 11 Seconds

        iSprinkler ++                                     // add 1
        switch(iSprinkler) {                              // split to different cases
            case 1: {                                     // counter 1
                if(Test_Trigger1.state == ON) {           // if Triggersswitch ON
                    Test_Trigger1.postUpdate(OFF)         // set to OFF
                    sSwitch1 = ON                         // change new state for Switch 1
                    lResched = lElevenSecs                // change reschedule time
                }
            }
            case 2: {                                     // counter 2
                if(Test_Trigger2.state == ON) {
                    Test_Trigger2.postUpdate(OFF)
                    sSwitch2 = ON
                    lResched = lElevenSecs
                }
            }
            case 3: {                                     // counter 3
                if(Test_Trigger3.state == ON) {
                    Test_Trigger3.postUpdate(OFF)
                    sSwitch3 = ON
                    lResched = lElevenSecs
                }
            }
            default: {                                    // every other value (4 and above...)
                lResched = 0                              // set reschedule to 0
                Test_Scheife.postUpdate(OFF)              // switch off
            }
        }
        if(Test_Schalter1.state != sSwitch1)              // bring Switch to correct position
            Test_Schalter1.sendCommand(sSwitch1.toString)

        if(Test_Schalter2.state != sSwitch2)
            Test_Schalter2.sendCommand(sSwitch2.toString)

        if(Test_Schalter3.state != sSwitch3)
            Test_Schalter3.sendCommand(sSwitch3.toString)

        if(lResched != 0)                                 // if reschedule other than 0, reschedule timer
            tSprinkler.reschedule(now.plusNanos(lResched))
    ])
end

In fact, the code could be reduced remarkably by using Group Items.

The idea is, to set a default OFF for every switch. If counter is at the correct position and corresponding trigger is activ, deactivate trigger but set Value to ON
In addition, the default reschedule time for the timer is 10 millis (has to be set as nanos for openHAB3)
If a switch is set to ON, the reschedule time will be changed to the ON duration.

As a last step, every switch is turned to the correct position and the timer is rescheduled.

Thanks to the vars some code can be omitted.

:slight_smile:

Couldn*t resist…

You don*t even need a counter at all…

// Group gSprinkler_Enable
// Group gSprinkler
//Switch Sprinkler_Start    "Start Sprinkler"                        [Equipment]
//Switch Sprinkler_Enable_1 "Enable Sprinkler 1" (gSprinkler_Enable) [Equipment]
//Switch Sprinkler_Enable_2 "Enable Sprinkler 2" (gSprinkler_Enable) [Equipment]
//Switch Sprinkler_Enable_3 "Enable Sprinkler 3" (gSprinkler_Enable) [Equipment]
//Switch Sprinkler_1        "Sprinkler 1"        (gSprinkler)        [Equipment]
//Switch Sprinkler_2        "Sprinkler 2"        (gSprinkler)        [Equipment]
//Switch Sprinkler_3        "Sprinkler 3"        (gSprinkler)        [Equipment]

var Timer   tSprinkler = null                             // timer for sprinkler

rule “Bewässerung in a row”
when
    Item Sprinkler_Start changed to ON
then
    tSprinkler?.cancel
    iSprinkler = 0
    tSprinkler = createTimer(now.plusSeconds(1),[|
        var nResched = 11
        if(gSprinkler_Enable.members.filter[i|i.state == ON].size == 0)
            nResched = 0
        val myEnable = gSprinkler_Enable.members.filter[i|i.state == ON].sortBy[name].head // first enabled
        val strCurrent = if(myEnable !== null) myEnable.name.split("_").get(2) else "end"
        gSprinkler.members.filter[ i |
            !i.name.endsWith(strCurrent) && i.state != OFF                         // all which should be off and aren't
        ].forEach[ j | j.sendCommand(OFF)]                                         // switch off

        gSprinkler.members.filter[ i |
            i.name.endsWith(strCurrent) && i.state != ON                           // the one which should be on and isn't
        ].head.sendCommand(ON)                                                     // switch on

        if(myEnable !== null)
            myEnable.postUpdate(OFF)

        if(nResched != 0)                                                              // if reschedule other than 0, reschedule timer
            tSprinkler.reschedule(now.plusSeconds(nResched))
    ])
end

As you can see, it’s even more compact, thanks to no counter…
Within the timer, the list of Enable Switches is filtered to these which are ON. the first is taken as Item myEnable (in alphabetical order…) If there is none (so size of list is 0), resched is set to 0
Then the digit is extracted (last part of the item name). If there is none, String is set to “end” (there are no group members which name ends with “end”)
Each Switch which has another digit in its name and is on is witched off
The one Switch which has the digit in its name is switched on (if not already on)
The current Enable Switch is set to OFF, if there is any.
If at least one Enable switch was ON at start of timer execution, the timer will be rescheduled.

EDIT: forgot .members for group functions…