Search code examples
droolsdrools-fusion

before or after within variable window?


I am trying to design a rule system where the rules themselves can be external configured, using a rule configuration object. Specifically, I want to configure externally to the DRL rule definition what the minimum time between rule firings for a particular type of rule should be.

My approach so far is to insert the rule configuration as a fact ValueRuleSpec:

rule "Any value detected"
when
    $r : ValueRuleSpec(mode == ValueRuleSpecMode.ANY(), $devices: devices)
    $e : SensorEvent(deviceId memberOf $devices) from entry-point FMSensorEvents
    not Activation(ruleSpec == $r, cause == $e)
then
    insert(new Activation($r, $e));
end

The $r ValueRuleSpec object has a property triggerEvery that contains the minimum number of seconds between activations. I know that this can be done statically by testing for the absence of an Activation object that is inside a specific range before $e using something like:

not Activation(this before[60s, 0s] $e)

How could I do this with a configurable time window, using the $r.triggerEvery property as the number of seconds?


Solution

  • Answering my own question based on the advice of laune.

    The before keyword's behavior is described in the manual as:

    $eventA : EventA( this before[ 3m30s, 4m ] $eventB ) 
    

    The previous pattern will match if and only if the temporal distance between the time when $eventA finished and the time when $eventB started is between ( 3 minutes and 30 seconds ) and ( 4 minutes ). In other words:

    3m30s <= $eventB.startTimestamp - $eventA.endTimeStamp <= 4m
    

    Looking up the source code for the before evaluator we can see the same.

     @Override
     protected boolean evaluate(long rightTS, long leftTS) {
         long dist = leftTS - rightTS;
         return this.getOperator().isNegated() ^ (dist >= this.initRange && dist <= this.finalRange);
     }
    

    Based on this I've modified my code accordingly, and it seems to work correctly now:

    rule "Any value detected"
    when
        $r : ValueRuleSpec(mode == ValueRuleSpecMode.ANY(), $devices: devices)
        $e : SensorEvent(deviceId memberOf $devices) from entry-point FMSensorEvents
        not Activation(ruleSpec == $r, cause == $e)
        // no activation within past triggerEvery seconds for same device
        not Activation(
            ruleSpec == $r, 
            deviceId == $e.deviceId, 
            start.time > ($e.start.time - ($r.triggerEvery * 1000))
        )
    then
        insert(new Activation($r, $e));
    end