Getting Started with Susi

This page will help you get started with Susi. You'll be up and running in a few minutes!

1440

First you will have to get a working copy of Susi. You can get it either via one of the prebuild packages from the release branches of our Github repository, or you can build Susi on your own.

Get Susi

To clone the whole repository and checkout the bleeding edge branch do this:

git clone --recursive https://github.com/webvariants/susi

Now we will go on to compile the core of susi, the susi server. We will call the directory where you cloned the repository to $SUSI.

Build Susi

OK, lets create a build directory and init it with cmake:

mkdir ~/susi-build
cd ~/susi-build
cmake $SUSI
make -j4
sudo make install
sudo ldconfig

Start the susi-core server

The main component of SUSI is its core-server. This is the server which does all event dispatching, and serves as a communication base for all other components.
You need to specify a valid TLS key / certificate pair to let the server start. To create a slef signed certificate run the following command:

openssl req -nodes -x509 -newkey rsa:2048 -keyout server_key.pem -out server_cert.pem -days 36500

After this, you can start the susi-core server:

susi-core --key server_key.pem --cert server_cert.pem --port 4000

This starts the susi-core server accepting TLS-connection on port 4000

Start one or more services

Once your core-server is started, you can start other components which connects to it.
One example would be, that you start the susi-duktape component, a server side javascript interpreter.
To do so, create a Javascript source file. This could look like this:

susi.registerProcessor('.*', function (evt) {
	console.debug('in processor');
	evt.payload = {};
	susi.ack(evt);
});

susi.registerProcessor('foo', function (evt) {
	console.debug('in foo proc 1');
	evt.payload.a = 'foo';
	susi.ack(evt);
});

susi.registerProcessor('foo', function (evt) {
	console.debug('in foo proc 2');
	evt.payload.b = 'bar';
	susi.dismiss(evt);
});

susi.registerProcessor('foo', function (evt) {
	console.debug('in foo proc 3 (should never be called)');
	evt.payload.c = 'baz';
	susi.ack(evt);
});

susi.registerConsumer('foo', function (evt) {
	console.log('consumer:', evt.payload);
});

susi.publish({ topic: 'foo' }, function (evt) {
	console.log('finish:', evt.payload);
});

Place this file somewhere in your filesystem with the name susi-sample.js.
You can use the same key/certificate pair you used to start the server, but you can create another pair:

openssl req -nodes -x509 -newkey rsa:2048 -keyout duktape_key.pem -out duktape_cert.pem -days 36500

Now its time to start susi-duktape.

susi-duktape --src susi-sample.js --addr localhost --port 4000 --key duktape_key.pem --cert duktape_cert.pem 
> started Susi::duktape engine and loaded source.js
> 2015-08-25T09:35:31.934Z DBG susi-js: source.js:2: in processor
> 2015-08-25T09:35:31.934Z DBG susi-js: source.js:8: in foo proc 1
> 2015-08-25T09:35:31.934Z DBG susi-js: source.js:14: in foo proc 2
> 2015-08-25T09:35:31.934Z INF susi-js: consumer: {a:"foo",b:"bar"}
> 2015-08-25T09:35:31.935Z INF susi-js: finish: {a:"foo",b:"bar"}

If you see an output like this, everything's fine :)
If you have a look at the supplied JS sources, you can see how SUSI works.

Understand the code

There are 5 essential actions you need to know about:

  • registerProcessor()
    • This attaches an active event handler to a specific topic.
    • All active event handlers run sequentially in the order they are declared.
  • registerConsumer()
    • This attaches an passive event handler to a specific topic
    • All passive event handlers run after all active event handlers finished.
  • publish()
    • This publishes an event.
    • The event is firstly processed by all processors
    • After all processors finished, the consumers for this topic are called
  • ack()
    • This needs to be called when an processor finished.
    • It tells susi, to continue with the event processing.
  • dismiss()
    • This can also be called if a processor finished
    • It tells SUSI to stop the event processing -> no active handlers will be called after this
    • It will NOT stop passive handlers or the finish callback from being called

As you see in the example, we register four processors one consumer and call publish:

susi.registerProcessor('foo', function (evt) {
	console.debug('in foo proc 1');
	evt.payload.a = 'foo';
	susi.ack(evt);
});

This is the second processor. It matches all events with the topic 'foo'.
It attaches the string 'foo' to the payload field 'a'. After this, it acknoledges the event back to susi.

susi.registerProcessor('foo', function (evt) {
	console.debug('in foo proc 2');
	evt.payload.b = 'bar';
	susi.dismiss(evt);
});

This is the third processor. It also matches all events with the topic 'foo'.
Notice that we call dismiss() in the end of the callback. This prevents all later declared processors to be called.
Especially fourth processor we declared after this, will NOT be called.

susi.registerProcessor('foo', function (evt) {
	console.debug('in foo proc 3 (should never be called)');
	evt.payload.c = 'baz';
	susi.ack(evt);
});

This is the fourth processor. It will never be called due to the dismiss() statement in the third processor.

susi.registerConsumer('foo', function (evt) {
	console.log('consumer:', evt.payload);
});

Here we declared a Consumer / passive event handler.
It gets called after all processors, which are interested in this event, finished.
In the callback we simply log the event payload to stdout.

susi.publish({ topic: 'foo' }, function (evt) {
	console.log('finish:', evt.payload);
});

Now after the setup of all those processors and consumers, we can finally publish an event!
publish() takes the event as first parameter. All events MUST have a topic field. Additionally they can have a payload field
which can contain arbitary data. As a second argument you can specify a finish callback. This is somewhat a one-time-consumer.
It gets called after all processors for this event finished, but gets immediatly deleted afterwards.