I'd like to build a simple monitor. Who listens for events in a time window and executes a listener. But the listener is not executed. I have a feeling the Apama Engine doesn't support that.
From documentation: Temporal operations are a common requirement for CEP. For example, we may want to put a time limit on our expressions. The within operator specifies a time in seconds within which the expression must complete, starting from when a constituent template is first activated. The below expression is complete when we receive both a MyEvent and a MyOtherEvent with the appropriate fields inside of 15.0 seconds. To clarify, in this expression the timer starts when either the MyEvent or MyOtherEvent is matched:
on all MyEvent(id="important") and MyOtherEvent(id="important") within 15.0
//Eventdefinition
&FLUSHING(1)
&SETTIME("2019-04-03T14:07:00.0+01:00")
&TIME("2019-04-03T14:07:01.0+01:00") // sec 1
TestEvent("START","1")
&TIME("2019-04-03T14:07:02.0+01:00") // sec 2
TestEvent("BODY_ID","1")
&TIME("2019-04-03T14:07:03.0+01:00") // sec 3
TestEvent("BODY_TYPE","1")
&TIME("2019-04-03T14:07:20.0+01:00") // sec 4
TestEvent("BODY_MODELL","1")
//EPL monitor Rule
on all TestEvent(tag= "BODY_ID") as test
and TestEvent(tag = "START") as test1 within(5.0)
{
log "test" + test.tag at INFO; //never called!
}
-----EDIT------
Another solution is this one, but not beautiful! And you can't access the details of the events.
on all (
TestEvent(tag = "START") as e1
or TestEvent(tag= "BODY_ID") as e2
or TestEvent(tag= "BODY_TYPE") as e3
or TestEvent(tag= "BODY_MODELL") as e4
) -> (//followd by random tag
TestEvent(tag = "START")
or TestEvent(tag = "BODY_ID")
or TestEvent(tag = "BODY_TYPE")
or TestEvent(tag = "BODY_MODELL")
) within(3.0) -> (//followd by random tag
TestEvent(tag = "START")
or TestEvent(tag = "BODY_ID")
or TestEvent(tag = "BODY_TYPE")
or TestEvent(tag = "BODY_MODELL")
) within(3.0) -> (//followd by random tag
TestEvent(tag = "START")
or TestEvent(tag = "BODY_ID")
or TestEvent(tag = "BODY_TYPE")
or TestEvent(tag = "BODY_MODELL")
) within(3.0) {
//Problem: No e1, e2,e3,e4 are not accessible...
}
Why the listener doesn't trigger
When you run the code an event listener is created that attempts to match the sequence of events based on the operators you choose. So
on A() within(10.0) listenerAction();
After the correlator sets up this event listener, the event listener must detect an A event within 10 seconds. If no A event is detected within 10 seconds, the event expression becomes permanently false and the correlator subsequently terminates the event listener.
With your expression the 'within' is essentially behaving as above.
on all TestEvent(tag= "BODY_ID") as test
and TestEvent(tag = "START") as test1 within(5.0)
{
log "test" + test.tag at INFO; //never called!
}
The and
operator will be false if either operand evaluates to false. That triggers the removal of the Event Listener.
In this case the within
will be false if the timeout expires without an event being received. The within
is actually with reference to the creation of the event listener because the and
has no concept of order or timings. This means that the evaluation returns permanently false
and the all
doesn't recreate the event template because it will never be true and neither will the and
.
If you try to use brackets to force the 'within' to apply to the second event only the same result applies (the timeout is still from listener creation).
If you remove the within
and ignore timings then the and
works as you expect, either order will trigger the event body. However, there is a side effect that may not be desired, if you had a sequence of events like:
A
B
A
You would trigger the body twice, A+B and B+A because of how the event listener behaves.
Solution
he simplest way to achieve what you want is to use the 'follows' operator and within. Because you want to receive the events in either order we need to use the or
operator and specify both with a within
.
on all ( ( TestEvent(tag= "BODY_ID") as test
-> TestEvent(tag = "START") as test1 within(5.0))
or
( TestEvent(tag= "START") as test
-> TestEvent(tag = "BODY_ID") as test1 within(5.0)) )
{
log "test" + test.tag at INFO; //never called!
}
When you create the event listener it doesn't evaluate (start the timer) on the right hand side of the '-> ' until either a START or BODY_ID is received. If no event comes in before the timer expires then the listener will terminate as before but it isn't now permanently false and so the 'all' recreates the event listener and waits for the first event to arrive.
See this for more details : listening for event patterns within a set time
Alternative
An Alternative would be to use streams as I mentioned below.
I have looked a little into and this works, but I am not completely sure if this is what you need. There may be a better way to set up the streams so that they do what you need
from t1 in all TestEvent () select t1.tag as tag1
from t2 in all TestEvent ()
within (5.0) select t2.tag as tag2 {
if( ( tag1 = "BODY_ID" and tag2 = "START" ) or
( tag1 = "START" and tag2 = "BODY_ID" ) ) {
log "t1 " + tag1 + " t2 " + tag2 at INFO;
}
}
Checkout the sections around here in the docs : Stream network