Rule for changing temp and resume rule after power loss

OK, I think a simplified version of the cascading timers approach may work but it might be simpler to use the Cancel Activity Design Pattern.

If I understand your problem (I had guessed you were running a wood drying kiln, drying lumber or blanks for turning?) I think something like the following will work. Note, again I’m just typing this in, it likely has errors.

Preconditions:

  • make sure you have persistence set up with restoreOnStartup

Items:

Number GoalTemp "Goal Temp [%d]" <temperature>
Number ProcessStep "Process Step [%d]"
Switch ProcessTimer { expire="60m,command=OFF" }
String ControlProcess "Control drying process"

Rules:

val targetTemps = newArrayList(34, 35, 36, 37, 38, 39, ...) // enter the target temps for each step of the process

rule "Control the process"
when
    Item ControlProcess received command
then
    switch receivedCommand.toString {

        case "Start": ProcessStep.sendCommand(0)

        case "Pause": {
            Heater.sendCommand(OFF) // turn off the heater
            ProcessTimer.postUpdate(OFF) // cancel the timer
        }

        // Note: Resume will run the current step the full 60 minutes regardless of how much time in the step was run before the pause
        case "Resume":  ProcessStep.sendCommand(ProcessStep.state) // resume the process by sending the current state again

        case "Stop": ProcessStep.sendCommand(-1) // stop the process by sending the end state
    }
end

rule "A step has completed"
when
    Item ProcessTimer received command OFF
then
    val currStep = ProcessStep.state as Number
    val nextStep = currStep + 1

    // if nextStep is the size of the array of target temps we have finished the last step
    if(nextStep == targetTemps.size) ProcessStep.sendCommand(-1)
    else ProcessStep.sendCommand(nextStep)
end

rule "Transition to new step"
when
    Item ProcessStep received command
then
    val currStep = receivedCommand as Number

    // Turn off the heater if we were cancelled, reach the last step, or the curr step is greater than the total number of steps
    if(currStep == -1 || currStep >= numSteps) {
        Heater.sendCommand(OFF)
        ProcessTimer.postUpdate(OFF)
    }

    // Transition to the next step, set the GoalTemp from the array, turn on the heater if it isn't already, and start the timer
    else {
        GoalTemp.sendCommand(targetTemps.get(currStep))
        if(Heater.state != ON) Heater.sendCommand(ON)
        ProcessTimer.sendCommand(ON)
    }
end

rule "Resume the process if it was running when OH went down"
when
    System started
then
    Thread:sleep(1000) // give everything time to be restored from persistence

    // If the Timer was on the process was running, restart the current step
    if(ProcessTimer.state == ON)  ProcessStep.sendCommand(ProcessStep.state)

    // If the process wasn't running, make sure the Heater is OFF
    else Heater.sendCommand(OFF)
end

Sitemap:

    Switch item=ControlProcess mappings=[Start=Start,Pause=Pause,Resume=Resume,Stop=Stop]

This will put a line on your sitemap with four buttons labeled Start, Pause, Resume, and Stop. You can get clever using the visibility Flag to swap out the Pause and Resume buttons based on whether the process is running or not.

While this approach resuses some of the same concepts as the cascading timers, I think it has become simplified enough now to avoid the problems you experienced last time.

You will probably want to add a bunch of log statements to watch it go through the steps while you test it.

I hope it works for you!

1 Like