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.

In 1262, we create the port, name the port and set its data type and structure all in the same call. So, we're going to name the output port out, set the data type to std/data/numeric, and set its structure to DOUBLE/REAL/2 (real, DOUBLE-type, two-element vector).

Py Source Code (against 1262)
# on first call if input['event']['flags'] & F_FIRST_CALL:
# create output
persist['hOutput'] = brahms.operation(persist['self'], \
OPERATION_ADD_PORT, '', 'std/2009/data/numeric', \
'DOUBLE/REAL/2', 'out')

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/1262';
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] out: [2x10 double]

OK - seems we've generated an output, and it's named "out", and of the required size (a two-element vector) and type (real double). But why is it 2x10?

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. Hence, 2x10.

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. In BRAHMS, we can't write output to a "port", however - we can only write output into the data object that is present on the port. But in 1262, the bindings resolve Port to Data for us, so we can just call OPERATION_SET_CONTENT on the Port to write the data.

Py Source Code (against 1262)
# switch on event type elif input['event']['type'] == EVENT_RUN_SERVICE:
# write output
brahms.operation(persist['self'], OPERATION_SET_CONTENT, \
persist['hOutput'], \
numpy.array([42, input['time']['now']], numpy.float64))
# ok output['event']['response'] = C_OK
Notes
  • We use the numpy.float64 argument to make sure we pass our data for this DOUBLE-type output in DOUBLE format (input.time.now is UINT64).
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.