0

如果我没有正确解释这一点,请帮助我纠正我的问题。我的问题可能与 Java 计时器有关,也可能与一般问题解决有关。

亲爱的读者,您不需要了解什么是 OpenHAB,什么是 OpenHAB 规则,或者实际上 MQTT 做了什么。但无论如何我都会使用这些术语来设置我的问题的场景。

在 OpenHAB 中,我有一个规则来响应发布到 MQTT 主题的消息。当我调暗调光器时,会向代理发送一连串“关闭”的 MQTT 消息。每条消息都会触发一个 OpenHAB 规则,该规则读取光的当前值并减去 1,然后将其写回。

为了确保规则不会在不同的线程中同时触发(从而防止灯光以正确的速率变暗),我的一位同事建议我像这样添加lock.lock

rule "ArduinoBedroomVector"
    when
        Item Bedroomvector received update
    then
        lock.lock()
        try {
          // rules here
          var Number lightcircuit1level = BedroomCeilingLight.state as DecimalType
          switch(Bedroomvector.state) {
            case "light_1_up" : {
              lightcircuit1level = lightcircuit1level + 3
              if(lightcircuit1level>100) lightcircuit1level = 100
              sendCommand(BedroomCeilingLight, lightcircuit1level);
            }
            case "light_1_down" : {
              lightcircuit1level = lightcircuit1level -3
              if(lightcircuit1level<0) lightcircuit1level = 0
              sendCommand(BedroomCeilingLight, lightcircuit1level);
            }
          }
        }
        finally {
          lock.unlock()
        }
    end

这是一种享受。

现在我的执行器不会错过“关闭”消息。

然而,由于执行器需要一点时间来响应每个单独的消息(它运行在 433MHz 射频传输上,每条射频消息消息需要 0.5 秒才能发送),所以到执行器的调光命令正在排队。

所以我需要介绍一种方法来检查规则是否在最后(例如 0.6 秒)内运行。如果是,则增加该值,但不发送命令。如果没有,则增加值并最终发送命令。

例如,这意味着我可以连续上下调暗灯光,只要我不停下来,灯光水平就不会改变。然后,一旦我决定停止调高或调低,亮度水平就会被最终确定和设置。

更好的是一个定时“规则”,它可以让我不断改变水平,但只根据最新水平每 0.5 秒设置一次光水平。

我确定这只是创建一个在运行规则时检查的计时器的情况,但我无法理解何时应该创建和检查计时器。这对很多人来说可能是非常明显的。

4

1 回答 1

0

不要对自己太苛刻,这不是显而易见的。

我的方法是保留最近一次执行规则的时间戳,并在调用 sendCommand 之前添加一个 if 语句来检查是否至少经过了 0.6 秒。

但是,有一个边缘情况,如果最后一次收到 a 命令是在 0.6 秒之前,则永远不会发送新值,因此我们需要设置一个计时器来发布最后一个值。但是我们需要清理计时器,这样如果我们在计时器设置但尚未触发后收到新命令,它就不会触发。边缘情况很混乱。

var lastExec = now.millis
var Timer timer = null

rule "ArduinoBedroomVector"
when
    Item Bedroomvector received update
then
    lock.lock()
    try {
      // rules here
      var Number lightcircuit1level = BedroomCeilingLight.state as DecimalType
      switch(Bedroomvector.state) {
        case "light_1_up" : {
          lightcircuit1level = lightcircuit1level + 3
          if(lightcircuit1level>100) lightcircuit1level = 100
          // Wait to send the new value
        }
        case "light_1_down" : {
          lightcircuit1level = lightcircuit1level -3
          if(lightcircuit1level<0) lightcircuit1level = 0
          // wait to send the new value
        }
      }

      // if more than .6 seconds have passed since the last time the value was sent
      if((now.millis - lastExec) > 600){
        // cancel the timer if one is already set
        if(timer != null) {
          timer.cancel
          timer = null
        }
        sendCommand(BedroomCeilingLight, lightcircuit1level)
        lastExec = now.millis
      }
      // its too soon to send the update, set a timer
      else {
        // cancel the timer if one is already set
        if(timer != null) {
          timer.cancel
          timer = null
        }
        // set a timer to go off in what is left of the .6 secs since the last update
        var t = now.plusMillis(600) - lastExec
        timer = createTimer(now.plusMillis(t), [|
          sendCommand(BedroomCeilingLight, lightcircuit1level)
          lastExec = now.millis 
          // beware, there could be a race condition here as the timer
          // will execute outside of the lock. If the rule executes 
          // at the same time as the timer the most recent value of
          // lastExec may be overwritten with an older value. It should 
          // happen very rarely though and may not be a problem.
        ]
      }
    }
    finally {
      lock.unlock()
    }
end
于 2015-06-11T16:10:19.337 回答