Project

General

Profile

MOOC Beta Features

These are the changes in the current beta of MOOC (v5.0).

Dropping VxWorks 5.x Support

This version of MOOC only supports the VxWorks 6.x kernels.

Split headers

At some point in the distant past (pre-3.0?), we removed mooc.h and replaced it with mooc++.h -- even though there was nothing "++" about it1. We'd like to start incorporating C++ features for C++ programmers so this version of MOOC is going through some re-organization. C programmers should go back to using mooc.h and C++ programmers should use mooc++.h. Due to complex, start-up issues in MOOC, the C++ API is provided as a separate VxWorks module (dev_api.out) which needs to be loaded after MOOC is running. This restriction will eventually be removed.

UPDATE: mooc++.h can now be included by C code. It's slightly more efficient to include mooc.h explicitly, but if you don't want to change your code, it'll still compile.

Packed Structures are Gone

There were still uses of -fpack-struct in MOOC. This release tries to remove all uses that are visible to MOOC programmers. For this reason, front-ends using MOOC 5.0 need to be completely recompiled with the 5.0 header.

Minimal use of printf in MOOC

Previous versions of MOOC used printf to report internal conditions. If no one was connected to the system, these messages would go to the console, which is attached to a 9600 baud serial port. If too much was getting printed, VxWorks would block the task until the output buffer would empty which could block other real-time processes. In this release, all internal uses of printf have been replaced with calls to logMsg.

C++ Templates to Simplify Writing Drivers

We're adding code to help C++ programmers reduce the amount of boilerplate code that has to be written. Every MOOC driver has to check the length and offset parameters for every request and then correctly interpret the buffers containing or receiving the data. Add to this the confusion caused by ACNET byte ordering and you have quite a few places to make a mistake. The C++ code is designed to handle these petty details for the programmer so they can focus on writing code that interfaces with their hardware. This version of MOOC introduces two templated classes, SettingProxy<> and ReadingProxy<>, to make this easier.

It should be noted that these templates will throw an exception for errors. The examples will show try/catch blocks in the MOOC methods. Until the core of MOOC is converted to C++, we'll have to catch thrown exceptions and convert them to ACNET statuses.

Setting Proxy

For a setting method, the setting value is in a buffer pointed to by a field in the request structure. We can use the SettingProxy<> to verify the request and give us access to the setting value. For instance, if our setting is an unsigned 16-bit value:

static STATUS devSetting(short, RS_REQ const* req, void*, uint16_t* hw_reg)
{
    try {
        MOOC::SettingProxy<uint16_t> setting(req);

        *hw_reg = setting;
    }
    catch (STATUS const& v) {
        return v;
    }
    catch (std::exception const&) {
        return ERR_DEVICEERROR;
    }
    return NOERR;
}

When the proxy object is created, it makes sure the length and offset fields of the request structure are valid for accessing a uint16_t value. If not, they throw the appropriate ACNET status, which we catch and return to MOOC. If the parameters are good, the object is created. This template defines a typecast operator of the type of its parameter so, in this example, proxy is automatically converted during the assignment to the dereferenced pointer.

What if we want to receive a floating point? Easily done (it's unlikely there's hardware that accepts a float directly, so the example passes the setting to a function):

static void useSetting(float val);

static STATUS devSetting(short, RS_REQ const* req, void*, void*)
{
    try {
        MOOC::SettingProxy<float> setting(req);

        useSetting(setting);
    }
    catch (STATUS const& v) {
        return v;
    }
    catch (std::exception const&) {
        return ERR_DEVICEERROR;
    }
    return NOERR;
}

The float proxy handles the byte swapping and alignment issues for you so you can focus on using the value instead of getting the value.

The setting proxy supports uint16_t, int16_t, uint32_t, int32_t and float types. There are also specialized templates which recognize and support arrays:

static float junk[4] = { 0 };

static STATUS devSetting(short, RS_REQ const* req, void*, void*)
{
    try {
        MOOC::SettingProxy<float[4]> setting(req);

        for (size_t ii = 0; ii < setting.total(); ++ii)
            junk[setting.offset() + ii] = setting[ii];
    }
    catch (STATUS const& v) {
        return v;
    }
    catch (std::exception const&) {
        return ERR_DEVICEERROR;
    }
    return NOERR;
}

Specifying array notation in the template parameter enables array notation with the proxy. The junk array receives the incoming settings to show how to pull settings from the request. The loop queries the proxy for the size of the setting because the incoming setting may be a subset of the total parameter (i.e. our method can handle at most 4 floats, which the request is validated against.) The request could just send us 2 floats, so we use proxy.total() to find out how many values were actually sent. Array proxies support the subscript operator, so accessing the value is natural.

It should be noted that the setting proxy doesn't allow assignment. A compile time error will be generated if you accidentally try to change the value of the setting.

It should also be noted that only the subscript operator should be used to access array elements. DO NOT TAKE THE ADDRESS OF AN ELEMENT AND ADVANCE THROUGH THE ARRAY USING IT -- YOU WILL BE DISAPPOINTED WITH THE RESULTS. Due to big-endian/little-endian issues, bytes need to be swapped when sending or receiving from the network. These proxy objects hide these details, but saving a pointer to the buffer exposes your code to the underlying layout.

Reading Proxy

Reading Proxy objects are just as easy to use. In addition to passing the request structure to the object's constructor, you also pass the return buffer. Read proxies only support assignment; you can't read back values you've written (that's not something that's done anyways.) So a device that returns pi would look like this:

static STATUS devReading(short, RS_REQ const* req, void* rpyBuf, void*)
{
    try {
        MOOC::ReadingProxy<float> reading(req, rpyBuf);

        reading = M_PI;
    }
    catch (STATUS const& v) {
        return v;
    }
    catch (std::exception const&) {
        return ERR_DEVICEERROR;
    }
    return NOERR;
}

Here's an example of reading an array device:

static uint16_t readJunk[4] = {1, 2, 3, 4};

STATUS devReading3(short, RS_REQ const* req, void* rpyBuf, void*)
{
    try {
        MOOC::ReadingProxy<uint16_t[4]> reading(req, rpyBuf);

        for (size_t ii = 0; ii < proxy.total(); ++ii)
            reading[ii] = readJunk[reading.offset() + ii];
        return NOERR;
    }
    catch (STATUS const& v) {
        return v;
    }
    catch (std::exception const&) {
        return ERR_DEVICEERROR;
    }
}

1 I was told the “++” was added when the MOOC header started pulling in the ACNET headers.