Overview

The empty Process you've created doesn't generate any output. We can see this if we modify the test script to view the outputs it generates.

M Source Code (against 995)
... % run brahms [out, rep] = brahms(sys, exe);
% show output
disp(out)

Running this script as it stands, you'll get the following.

Matlab Console
>> test sml_log object: 1-by-1

That is, an empty log object is generated, because no Process in the System generated any outputs to fill this structure.

Notes
  • A log object, sml_log, is a read-on-demand interface to the actual log, which may in some cases be very large. It behaves exactly like a Matlab struct array, except that it only reads its contents in from disk when required. You can convert it to a Matlab struct array by calling, for example, out = struct(out).

Adding an output port

The Process can create as many outputs as it likes whilst it is servicing EVENT_INIT_CONNECT. This event is fired multiple times, and you should create output ports as early as possible (on the first of these calls that you can). The only reason to not create an output on the very first call is if you need to see some of your inputs before you know what sort of output to create. We don't, so we're just going to create an output port on our first call.

We'll create the port during EVENT_INIT_CONNECT, and store it for later use, so we'll have to also add a state variable to store it. We're going to add an output of data type std/data/numeric. This data type will serve most purposes - it can represent any N-dimensional array of simple numeric data (its reference page gives much more detail). Add code to your process source file as follows.

C++ Source Code (against 1199)
// include the binding #include "brahms-1199.h"
// alias data namespace to something briefer
namespace numeric = std_2009_data_numeric_0;
C++ Source Code (against 1199)
/* Private member data of the process goes here. You can use this to store your parameters and process state between calls to event(). */
// an (numeric) output port
numeric::Output output;
C++ Source Code (against 1199)
// on first call if (event->flags & F_FIRST_CALL) {
// create one output
output.create(hComponent);
}

Viewing the output log

If you run the test script as it stands, you'll get output like this.

Matlab Console
>> test process: [sml_log]

The Process has now created an output, so the output structure gets a field named after the process. Currently, and unimaginatively, our Process is called "process".

Let's make a couple of changes to the test script. First, let's call our process something more meaningful. We're going to make a model of a very simple neuron, so let's call it "neuron". Second, let's get a look at what outputs it actually created, by looking deeper into that output structure.

M Source Code (against 995)
% add process state = []; fS = 10; cls = 'dev/quickstart/1199';
sys = sys.addprocess('neuron', cls, fS, state);
M Source Code (against 995)
% show output disp(out)
disp(out.neuron)
Matlab Console
>> test neuron: [sml_log] out0001: [0x10 double]

OK - seems we've generated an output, and it's named "out0001". Bit of a crap name, that.

Naming the output port

Naming an output port is straightforward. Modify your source file as follows.

C++ Source Code (against 1199)
{ // create one output
output.setName("out");
output.create(hComponent); }
Matlab Console
>> test neuron: [sml_log] out: [0x10 double]

OK - that's a much better name. Pretty generic, I suppose, but it'll certainly do for now. It's a numeric output because we created it as std/data/numeric. But why is it 0x10? And why is it double type?

Setting the output structure

Examining the test script, we have added the neuron process with a sample rate of 10Hz (fS), and we have asked the execution to run for one second (exe.stop). Hence, we get 10 samples back - the last dimension of the output log of a std/data/numeric is sample number. And because we didn't set the structure of the data object on our new output Port, it was left with its default structure, which is a zero-size array of DOUBLE type elements. Hence, 0x10 and double.

To give the output object the size we want, we can't ask the Framework, because the Framework doesn't know anything about the internals of Components. Instead, we call a function on the interface offered by the data object itself. Since the vast majority of data handling will involve std/data/numeric, you may become quite familiar with this interface. It's detailed at std/data/numeric, but there's no need to go and look at it now - we're going to see the key calls in this section anyway.

Make the modifications below to set the output data structure to be double, real (not complex), and a two-element vector. Don't worry too much about the syntax of this call at this stage.

C++ Source Code (against 1199)
// create one output output.setName("out"); output.create(hComponent);
// structure it
output.setStructure(TYPE_REAL | TYPE_DOUBLE, Dims(2));
Matlab Console
>> test neuron: [sml_log] out: [2x10 double]

OK - Now we've got 10 samples of a two-element vector, which is what we intended.

Viewing output data

Now let's have a look at the actual values in the output log. Modify your test script as follows, to view the actual content of the output log.

M Source Code (against 995)
% show output
disp(out)
disp(out.neuron)
disp(out.neuron.out)
Matlab Console
>> test 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Unsurprisingly, the output is filled with zeroes. If you want to know why, see F_ZERO, and remember that we aren't writing any data to these outputs.

Writing output data

We haven't got anything much to put in the output yet, but let's write something in there just to see how it's done.

We write data to our outputs when we receive EVENT_RUN_SERVICE.

C++ Source Code (against 1199)
case EVENT_RUN_SERVICE: {
// get a pointer to the port's contents (actual numeric data)
DOUBLE* p = (DOUBLE*) output.getContent();
 
// it's got two elements, so let's write them both
p[0] = 42;
p[1] = time->now;
// ok return C_OK; }
Matlab Console
>> test 42 42 42 42 42 42 42 42 42 42 0 1 2 3 4 5 6 7 8 9

OK - now we're writing data to our output object. On each successive call to EVENT_RUN_SERVICE (we receive 10 such calls) we write two items of data (42 and the current Base Clock time). If you want to know exactly why you see the numbers 0 to 9, read Clocks then Servicing. The Framework is logging these two-element vectors as they are generated, and it collates them into the log that is returned to us when BRAHMS exits, seen above.

Where do I go from here?

If you need more control over the Numeric Type of the output, see the documentation for std/data/numeric. If you need to generate spiking outputs, you can use the std/data/spikes Data Class. If you need an entirely new output type, it is possible to create one, but this is poorly documented at the moment, so we hope you can make do with std/data/numeric and std/data/spikes, for now.

In addition, you can create output Sets, if required, but this is not something you should adopt unless there is a real need - most processes don't need Sets. See Using Sets under Advanced Topics.

To find out more about how you can interact with your outputs, review the documentation for SystemMLInterface.