Worker Code Examples

C++ Language Examples

Here is a simple example of a C++ language xyz worker whose:

  • Initial run condition is the default (condition == NULL, usecs == 0, run when all ports are ready, no timeout),

  • initialize, start and release methods are not needed or implemented.

  • One input port (in), and one output port (out)

  • One protocol operation Op1 on input, which is an array of 100 “shorts”.

  • Two protocol operations Op2 and Op3 on output, a sequence of shorts.

  • One simple property, called center_frequency, of type float.

The OPS for the in port would be (in the shortarray-prot.xml file):

<protocol>
    <operation name='op1'>
        <argument name='shorts' type='short' arraylength='100'/>
    </operation>
</protocol>

The OPS for the output port would be (in the shortseq-prot.xml file)

<protocol>
    <operation name='op2'>
        <argument name='shorts' type='short' sequencelength='0'/>
    </operation>
    <operation name='op3'/>
</protocol>

Note the op3 operation has no arguments, indicating that op3 messages will have an opcode indicated, but no data (message length of zero). The OCS for the component would be (in the xyz-spec.xml file):

<componentSpec>
    <property name='center_frequency' type='float' initial='true'/>
    <port name='in' protocol='shortarray'/>
    <port name='out' protocol='shortseq'/>
</componentSpec>

The OWD would be (in the file xyz.xml):

<RccWorker spec="xyz-spec" language='c++'/>

The worker would have automatically generated types and structures like this (based on OCS and OWD), and put in a file called xyz-worker.hh:

#include <RCC_Worker.h>
namespace XyzWorkerTypes {
    struct Properties {              // structure for properties
    // internal stuff here
    const RCCFloat center_frequency; // const: not set to volatile
    };
    class XyzWorkerBase {            // class inherited by worker
    // internal stuff here, including port members
    };
}

The actual code for the C++worker would look like this (in the file xyz.cc):

#include "xyz-worker.hh" // include generated declarations

using namespace OCPI::RCC; // access to RCC data types and constants
using namespace XyzWorkerTypes; // for generated types

struct XyzWorker : public XyzWorkerBase { // struct avoids "public"
    // In constructor, set output operation to be a constant,
    // since it is the only one used, but not the default (0)
    XyzWorker() {
        out.setDefaultOpCode(ProtOp2_OPERATION);
    }
    void
    compute(int16_t *in, int16_t *out, size_t n, float freq) { ... }
    // Define run method to call the "compute" function, reading from
    // input buffer, writing to output, applying current value of the
    // centerFrequency" property
    RCCResult
    run(RCCBoolean /* timedout */) {
        // Do computation from in.op0.shorts(), using freq, put results
        // in out.op2.shorts().
        out.op2().shorts().resize(in.op0().shorts().size());
        compute(in.op0().shorts().data(), out.op2().shorts().data(),
            in.op0().shorts().size(),
            properties().center_frequency);
        // Ask container to send output and get new input/output buffers
        return RCC_ADVANCE;
    }
}; // end of worker class

Worker Using the State-machine Style

This illustrates a finite state machine coding style in a C++ worker that maintains an internal state to simulate blocking on a two-way remote call until the response is received and processing can continue. It accepts data on its in port, performs computation in-place (in the input buffer), then sends this buffer to an out port, and starts awaiting a response from a response input port. When the response arrives, it sends the response to an other output port, and reverts to its original condition, waiting for input.

#include "xyz-worker.hh" // include generated declarations

using namespace OCPI::RCC; // for RCC data types and constants
using namespace XyzWorkerTypes; // for worker's generated types

struct XyzWorker : public XyzWorkerBase {
    // Define two different run conditions to represent two states
    // These could be static and initialized outside the class too.
    RCCRunCondition m_awaitingInput, m_awaitingResponse;
    XyzWorker()
    : m_awaitingInput(1 << XYZ_IN, RCC_NO_PORTS),
      m_waitingResponse(1 << XYZ_RESPONSE, RCC_NO_PORTS) {
      setRunCondition(&m_awaitingInput);
    }
    RCCResult
    run(RCCBoolean /* timedout */) {
        // Use run condition as state indicator
        if (getRunCondition() == &m_awaitingInput) {
            // do computation based in in.op0().shorts(), and frequency;
            // put results back in same buffer (in-place)
            compute(in.op0().shorts().data(), in.op0().shorts().data(),
                    in.op0().shorts().size(),
                    properties().center_frequency);
            // Input port is advanced by taking buffer away from it.
            out.send(in); // send input to output, same op and length,
            setRunCondition(&m_awaitingResponse); // await response
        } else {
            other.send(response);           // got response, send to other
            setRunCondition(&awaitingInput);// change state, await input
        }
        return RCC_OK;
    }
}; // end of worker class

C Language Examples

Here is a simple example of a C-language xyz worker whose:

  • Initial run condition is the default (condition == NULL, usecs == 0, run when all ports are ready, no timeout),

  • start and release methods are not needed or implemented.

  • One input port named in, and one output port named out.

  • One protocol operation Op1 on input (i.e. can ignore “operation”), which is an array of 100 “shorts”.

  • Two protocol operations Op2, and Op3 on output

  • One simple property, called center_frequency, of type float.

The worker has automatically generated types and structures like this (based on OCS and OWD), generated in a file called xyz_Worker.h:

#include "RCC_Worker.h"
typedef struct { /* structure for defined properties */
    RCCFloat center_frequency;
} XyzProperties;
typedef struct { // structure for message for operation Op1
    int16_t ishorts[100];
} XyzInOp1;

typedef struct { // structure for message for operation Op2
    int16_t oshorts[100];
} XyzOutOp2;

typedef enum { // port ordinals
    XYZ_IN,
    XYZ_OUT
} XyzPort;

typedef enum { // operation ordinals
    XYZ_OUT_OP2,
    XYZ_OUT_OP3
} XyzOutOperation;

typedef enum { // operation ordinals
    XYZ_IN_OP1
} XyzInOperation;

The actual code for the worker is below.

Worker Using the Default Run Condition

This C language worker uses the default run condition and thus has no checking for port readiness, but does set a default operation for output messages. It always produces an output message for every input message, so the run method can simply return RCC_ADVANCE.

#include "Xyz_Worker.h"

/* Define the initialize method, setting output operation to be a */
/* constant, since it is the only one.*/
static RCCResult
initialize(RCCWorker *w) {
    w->ports[XYZ_OUT].output.u.operation = XYZ_OUT_OP2;
    return RCC_OK;
}

/* Define run method to call the "compute" function, reading from */
/* input buffer, writing to output, applying current value of the */
/* "center frequency" property.*/
static RCCResult
run(RCCWorker *w, RCCBoolean timedout, RCCBoolean *newRunCondition)
{
    XyzProperties *p = w->properties;
    XyzInOp1 *in = w->ports[XYZ_IN].current.data;
    XyzOutOp2 *out = w->ports[XYZ_OUT].current.data;

    /* Do computation based in ishorts, and frequency put results */
    /* in oshorts. Extern is here simply for readability. */
    extern void compute(int16_t *, int16_t *, float);

    compute(in->ishorts, out->oshorts, p->center_frequency);
    /* Ask container to get new input and output buffers */
    return RCC_ADVANCE;
}

Other than the compute function, the above example compiles to use less than 120 bytes on an ARM processor.

Worker Using the State-machine Style

This worker illustrates a finite state machine coding style in a worker that maintains an internal state to simulate blocking on a two-way remote call until the response is received and processing can continue.

#include "Xyz_Worker.h"
/* Define two different run conditions to represent two states */
static uint32_t
    state1Ports[] = {1 << XYZ_IN, RCC_NO_PORTS},
    state2Ports[] = {1 << XYZ_COMPUTE_REPLY_IN, RCC_NO_PORTS};
static RCCRunCondition
    awaitingInput = {state1Ports},
    awaitingResponse = {state2Ports};

/* Define start method, set run condition, which is also state. */
static RCCResult
initialize(RCCWorker *this) {
    this->runCondition = &awaitingInput;
    return RCC_OK;
}
/* Define run method to call the local "compute" function
 * reading from input buffer, applying current value of the
 * "center frequency" property, followed by remote two way
 * "process" operation, finally writing result to output. */
static RCCResult
run(RCCWorker *this, RCCBoolean timedout, RCCBoolean *newRunCondition) {
    RCCContainer *c = &this->container;

    /* Use run condition as state indicator */
    if (this->runCondition == &awaitingInput) {
        RCCPort
            *inPort = &this->ports[XYZ_IN],
            *computeOut = &this->ports[XYZ_COMPUTE_REQUEST_OUT];
        XyzInOp1 *in = inPort->current.data;
        XyzProperties *p = this->properties;

        /* do some computation based in ishorts, and frequency;
         * put results back in same buffer (in-place) */
        extern void compute(int16_t *, int16_t *, float);
        compute(in->ishorts, in->ishorts, p->center_frequency);

        /* Call Process op on user port - buffer ownership passes back
         * to container. Input port is advanced by taking buffer away
         * from it. */
        c->send(computeOut, &inPort->current, XYZ_COMPUTE_PROCESS,
                inPort->input.length);     /* length of message */
        this->runCondition = &awaitingResponse; /* update state */
    } else {
        RCCPort *computeIn = &this->ports[XYZ_COMPUTE_REPLY_IN];
        RCCPort *otherOut = &this->ports[XYZ_OUT];

        if (computeIn->input.u.exception == 0)
            c->send(otherOut, &computeIn->current, XYZ_OUT_OP2,
                    computeIn->input.length);
        else
            c->advance(computeIn, 0);
            this->runCondition = &awaitingInput; /* update state */
    }
    *newRunCondition = RCC_TRUE; /* to container: new run condition */
    return RCC_OK;
}
/* continued on next page */

Other than the compute function, the above example compiles to use less than 330 bytes on a Pentium processor.