This page last changed on May 23, 2013 by aktur.

Hi,

I'm testing PTM 200 from the starter kit with OpenRemote. The problem which I have is that when I press one button twice or more times then the following presses are not propagating. This makes some applications impossible (for example, hotel light switch - switch light on/off from multiply places - where upper buttons always switch lights on and lower buttons switch lights off. Or another example, different action for single press and double press).

For testing this I'm using the following rule:

import java.util.concurrent.*

rule "EnOcean Button Test"
when
  $evt : Event(source=="Button")
then
   execute.command("V_SEC ON",$evt.getValue().toString());
   TimeUnit.MILLISECONDS.sleep(1000);
   execute.command("V_SEC ON","----");
end

Which linked to in-memory command V_SEC STATUS shows which rocker was pressed and after 1s replaces it with "----". The only sequence I can produce is:

ROCKER_BO
----
ROCKER_BI
----
ROCKER_BO
----
etc.

However, for my application I need sequences like this:

ROCKER_BO
----
ROCKER_BO
----
ROCKER_BI
----
ROCKER_BI
etc.

I clearly see in dev.log that commands are received but they are later suppressed in the controller. This gives yet another problem, as controller remembers last state it can become out of sync not only when other switch is used in parallel but also when controller reboots.

I've inspected the rule engine code (RuleEngine::push(EventContext ctx) and it seems that this behavior is intentionally - identical successive events are suppressed.

Posted by rhitz at May 25, 2013 10:08

Very strange. Does this mean that no 'hart beat' event can be used in rules? Like the one sent by STM 250.

On the other hand, how comes that my temperature sensor event get through the rules for Fahrenheit conversion? Is this because temperature each time differs?

Anyway, if this is a case adding button press and button release events as mentioned in the other thread would solve this problem too.

Posted by aktur at May 25, 2013 14:12

That's correct. Only a single event with the same identity (where identity currently is defined by the event's Java object equality) exists in the current model as a fact in the rules knowledgebase.

IIRC this is the best default model given the variety of ways events are generated: a polling protocol can generate large number of identical events – each event in this case does not represent a distinct state change, just a result of polling. In these cases it is most often not desirable to fire the rule each time (and it currently is not) – an exception here would be some sort of graph polling that would want to record the current value/state on a polling interval despite whether there's been a change or not. But these models can be accommodated by including alternative event processor implementations, or parameterizing current one (more on that later).

A proper automation protocol (one that broadcasts/sends updates only on distinct state change events) usually doesn't run foul of this identity policy as most often a distinct state change is one that has a different event identity (which currently means different Java object equality – equality being defined by the combination of event's source and event's value).

The second point to keep in mind is how the stateful knowledgebase behaves – when rules are evaluated and executed, each fact (in our case events) is evaluated. That means that previous event from the same source should be retracted first, before a new event (fact) from the same source is inserted. Otherwise a sequence of ON-OFF-ON with a rule set to trigger on 'ON' state will execute twice on the second 'ON' event, given that there are two matching facts (events) in the working memory. Another consequence is that without a retraction policy of some kind, eventually you'd fill up the working memory with facts and crash.

Final point to keep in mind is that the current rule engine is just one possible kind of event processor. It is even only one kind of possible rule engine with the current identity policy. The RRD data logger is an example of a different event policy – it ignores the event identity and pushes a new value at most once a second. For a polling protocol at 500ms polling frequency it means every second polling value is logged (regardless of value change or not), for a broadcasting/bidirectional protocol it means events are logged when they are sent, although only at most once per second.

Same applies to the current rule engine – a different event identity policy could be implemented. Either by creating (copy/pasting) the existing rule engine event processor to a new class (and configuring it in the event processing chain) or parameterizing the existing identity policy on the current implementation so it can be configured to behave differently:

    try
    {
      if (!knowledgeSession.getObjects().contains(evt))
      {
        if (eventSources.keySet().contains(evt.getSourceID()))
        {
          knowledgeSession.retract(eventSources.get(evt.getSourceID()));

          eventSources.remove(evt.getSourceID());
        }

        FactHandle handle = knowledgeSession.insert(evt);

        eventSources.put(evt.getSourceID(), handle);
      }

      log.trace("Inserted event {0}", evt);
      log.trace("Fact count: " + knowledgeSession.getFactCount());
      
      knowledgeSession.fireAllRules();
    }

You could insert events as facts based on time policy (similar to RRD – same event source/value identity will be accepted and rules evaluated as long as it didn't occur within X seconds), or even object's system identity (not based on equals() implementation). But for both cases you'd need to deal with some kind of retraction policy (garbage collect on timed intervals?) and the fact that you'd have multiple facts in working memory that match the same condition such as 'source = "my switch", value = "on"' which will cause the rule to be evaluated on each event (fact).

HTH!

Posted by juha at May 25, 2013 16:28

In this code I see that the retraction is done for events with the same getSourceID() and there is no value comparison. This would mean that every event from already existing sourceID would be removed, but it does not make sense. Am I missing something?

Posted by aktur at May 25, 2013 17:37

First check against working memory is based on event equality (contains), so if event is EVT(source=123, value=ON) it will proceed if working memory has no facts from that same source or working memory has EVT(source=123, value=OFF) in it. If it is the case of the latter, the 'OFF' fact is removed (based on the source ID lookup to retrieve a fact handle), otherwise new fact is inserted. There's never a case where both ON and OFF from the same source are in the working memory at the same time in this implementation, as in the normal case having both facts would be confusing (unless you're thinking in terms of not-yet-collapsed wave functions .

Posted by juha at May 25, 2013 18:07

The first trace statement however seems to be in the wrong place which can get confusing, should be a step above in the nested block I think.

Posted by juha at May 25, 2013 18:08

OK, I've got it. Indeed I've commented out the first if() and now series the same clicks are coming through. Nevertheless, I think that this piece of code need rethinking. For example, right now is it not quite possible to do what Eric told in the HDTV podcast, i.e. reuse old remote as universal remote. For example how would one select channel 111, which requires to press '1' three times in a row?

Posted by aktur at May 26, 2013 15:24

This is going to be somewhat protocol dependent but one way to go about it would be to create sequential events. Right now IIRC the event API is not yet exposed to the protocol implementers. The plan is to do so however (there's a JIRA task about it somewhere).

This means that the protocol implementation can decide which type of event it creates that's pushed towards the event processing chain. So you can extend the default event model and by subclassing changing the equality rules, for example.

So in this case a sequential event could be one that contains the sensor source, the value and also a sequence identifier.

How a sequence identifier is modified depends on the protocol implementation. In your example, how to determine between 11 and 111 for example. It is likely to be some protocol specific mechanism, for example a timeout on the incoming IR input.

The second part of this is the fact retraction policy. If equality includes a sequence identifier, then in essence three facts (all three presses) are inserted in the rule working memory.

Now then, a rule condition ('when') that is purely based on source or source + event value will get triggered as each fact is inserted (strictly this is not necessary but that's the policy we've implemented now – rules are evaluated on each fact (event) insert). That means once for first '1', again for first '1' and the second '1' (so now total three times) and once again for first '1', the second '1' and finally the third '1' as it is inserted (so now total of 6 times).

However, a sequence event (in the style of Level or Range or Switch event you've seen) can expose the sequence value which allows:


  when $evt : SequenceEvent( source == "foo" value == "press" sequence == "3" ) then // do something
 

And therefore a rule is triggered only on the third event (as the other two are not equal based on sequence identifier).

The fact extraction policy is then that if a sequence event with value = 0 is inserted, all existing events (facts) are first retracted.

Posted by juha at May 27, 2013 11:31
Document generated by Confluence on Jun 05, 2016 09:30