Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

VIATRA/CEP/Examples

< VIATRA‎ | CEP
Revision as of 19:21, 14 January 2015 by Davidi.inf.mit.bme.hu (Talk | contribs)

The following demonstrations highlight the essentials of the VIATRA-CEP framework using a simple example introduced by Martin Fowler, commonly referred as the state machine DSL, presented in his DSL book. The sources are available on GitHub. To run them, the environment must be set up first.

Example #1: Simple event processing

We are about to design a security system which controls secret compartments in a real estate. The system is rather old-school. For example, one would open a drawer first then turn on the light and knock on the wall twice to open a compartment. For that purpose, sensors are installed on the significant points of the real estate which emit events if something interesting happens; and a central controller deals with the events. The essentials of complex event modeling and processing are obvious even in this plain simple example:

  • we would like to depict the interesting happenings as atomic events (e.g. a knocking on the wall) and
  • complex events (e.g. a series of events which would open a secret compartment), then
  • define actions to be executed based on identified complex event patterns (e.g. to open a secret compartment), and finally,
  • employ some sort of a central reasoning system to process atomic events and identify complex event patterns.

Modeling atomic events

We follow the conventions of the original example, i.e. identify atomic events using four-character IDs. We have different sensors on stock: a door sensor (DO), a drawer sensor (DW), a light swich sensor (LI) and a wall sensor (WL). The door is either in a closed (DOCL) or open (DOOP) state. On every state change, an event is triggered with the given four-character ID. The same applies for the drawer (DWOP, DWCL). Similarly, the light can be turned on (LION) or off (LIOF). Finally, every knock on the wall triggers a separate event (WLKC). The outlined events will serve as the atomic events in our example.

Let's define the above atomic events using VEPL, i.e. the VIATRA-CEP Event Processing Language.

AtomicEvent DOCL(){}
AtomicEvent DOOP(){}
AtomicEvent DWCL(){}
AtomicEvent DWOP(){}
AtomicEvent LIOP(){}
AtomicEvent LIOF(){}
AtomicEvent WLKC(){}

The complete source is available here. (For the sake of simplicity, the events do not feature parameters, nor any special features, hence the empty parentheses and braces. For details see the language overview.)

Modeling complex event patterns

Setting up the security system is a twofold task: (i) we have to deploy the mentioned sensors, and (ii) we have to configure them. The latter on means that we have to define the complex event patterns to depict the "security codes" of the customer; and we also have to define the actions triggered when these complex event patterns are identified.

Let's say, the first secret compartment will open (SC1O) when the following sequence of events is observed: the door opens, the lights go on and the drawer is opened. Using VEPL, this complex event looks like this:

ComplexEvent SC1O(){
     definition: DOOP -> LION -> DWOP 
}

The pattern begins with the keyword ComplexEvent, then a unique name follows. Again, the parentheses are empty because we do not use parameters in our patterns. To see a parameterized example, jump here. The definition features a very straightforward operator, the followed by operator, denoted by an arrow (->). The complex event will be observed if all the required atomic events occur in this order.

We want to allow the second compartment to be opened by two knocks on the wall:

ComplexEvent SC2O(){
     definition: WLKC{2}
}

Here, the {2} after the atomic event denotes its required multiplicity. It would be equivalent to write it in this form:

ComplexEvent SC2O(){
     definition: WLKC -> WLKC
}

Finally, let's say the third secret compartment can be opened in two ways. One either has to turn the light on and subsequently off within a second, or has to open and then subsequently close the drawer twice within two seconds.

ComplexEvent SC3O(){
     definition: ((LION -> LIOF)[1000] OR (DWOP -> DWCL){2}[2000])
}

The definition is actually a combination of two complex event patterns, connected with an or-clause (OR). The first part (on the left side of the OR operator) defines the case when the light is turned on and off again within a second. Timing criteria can be defined between brackets: the number will define an upper limit for the pattern it follows to appear within. Timing and multiplicity criteria can be combined, as the second part of the definition shows it.

Actions

To actually open the secret compartments when the appropriate complex events are observed, we define actions in which we activate an appropriate actuator. The logic of the actuator is pretty simple, we only have to invoke its openCompartment method with the appropriate compartment number, then we will see a message about the appropriate compartment being opened:

public class CompartmentActuator {
     public static void openCompartment(int compartmentNumber) {
          System.out.println(String.format("Opening compartment #%d.", compartmentNumber));
     }
}

The actual method invocation is placed into an block called rule:

Rule openSC1{
     events: SC1O
     action{
          CompartmentActuator::openCompartment(1)
     }
}

In the above rule, we invoke the actuator with the compartment number "1", since the action will be triggered by the complex event SC1O, which describes the required pattern to open compartment #1. The action block is defined using Xbase syntax. [ref?] The other two rules look pretty similar, only the required event and the parameter of the actuator is changed.

Putting things into motion

Finally, we have to set up the system by reusing the modeled events, event patterns and actions. We will demonstrate this with a set of simple JUnit tests. The full source can be found here.

First, we have to instantiate a CEP engine from the framework.

CEPEngine engine = CEPEngine.newEngine();

Then we have to configure the engine with the required rules. To keep this example simple, let's just put rule openSC1 there.

engine.addRule(CepFactory.getInstance().createOpenSC1());

The syntax is pretty straightforward. The CepFactory is a generated factory class which offers factory methods for every defined event, event pattern and rule.

Now we can forward events to the engine. To do so, we need an event stream, which can be acquired from the instantiated engine.

EventStream eventStream = engine.getStreamManager().newEventStream();

Finally, we will forward the three atomic events required for the SC1O complex event pattern, in the right order, using the previously seen factory class.

eventStream.push(CepFactory.getInstance().createDOOP_Event());
eventStream.push(CepFactory.getInstance().createLION_Event());
eventStream.push(CepFactory.getInstance().createDWOP_Event());

Running the test will now give the expected result, that is, the complex event pattern will be recognized.

Opening compartment #1.

Parameterized event patterns

event patterns can be augmented with parameters evaluated at run time. This enables defining even more complex event patterns. Parameters are typed and to this end, the whole JVM type hierarchy is available.

Let's modify the example and say that there are multiple drawers in the room and in order to open the third compartment the same drawer should be opened and closed twice. Complex event pattern SC3O tries do depict this case, but it will fall short as it does not specify that the same drawer should be manipulated. To overcome this, let's introduce an ID parameter in the atomic event pattern:

AtomicEvent DWCL_2(id:String){}
AtomicEvent DWOP_2(id:String){}

The two patterns are the counterparts of the DWCL and DWOP patterns, only with parameters. Now the SC3O pattern can be extended as well with a parameter and this parameter can be used to specify that we want to manipulate the same drawer:

ComplexEvent SC3O_2(drawerId:String){
     definition: ((LION -> LIOF)[1000] OR (DWOP_2(drawerId)->DWCL_2(drawerId)){2}[2000])
}

Let's rewrite the example application. After instantiating an Event class, it's ID should be also defined:

DWOP_2_Event dwop_Event = CepFactory.getInstance().createDWOP_2_Event();
dwop_Event.setId("drawer#1");
DWCL_2_Event dwcl_Event = CepFactory.getInstance().createDWCL_2_Event();
dwcl_Event.setId("drawer#1");

Now pushing these events to the event stream in the desired sequence, we'll get the expected result.

getEventStream().push(dwop_Event);
getEventStream().push(dwcl_Event);
getEventStream().push(dwop_Event);
getEventStream().push(dwcl_Event);

The full source can be found here.

Example #2: Reasoning over non-materialized models

One of the great features of VIATRA-CEP is the tight integration with EMF-IncQuery, an incremental query engine for EMF models, which enables defining event patterns essentially describing elementary or compound changes in EMF models.

//TODO

Back to the top