Skip to content

C++ API

include/mscbxx.h provides a header-only C++17 façade over the C MSCB library. It is useful when an application operates on one node and wants typed variable access without manually calling mscb_info_variable(), decoding byte widths, or managing name-to-index mappings.

The API lives in namespace midas and has two public classes:

Class Represents
midas::mscb A connection to one node through one submaster
midas::u_mscb One cached node variable and its read/write conversion logic

mscbxx.h contains the class implementation, but the application must still link against the mscb library because the classes call the C functions in mscb.cxx.

Simple example

#include <iostream>
#include <mscbxx.h>

int main()
{
    try {
        // Connect to node 10 through an Ethernet submaster.
        midas::mscb node("192.0.2.40", 10);

        std::cout << "Connected to " << node.get_node_name() << '\n';

        // Refresh all variables, then access one by name.
        if (node.read_range() != MSCB_SUCCESS) {
            std::cerr << "Could not read node variables\n";
            return 1;
        }

        const int input_index = node.idx("In0");
        const float input = node[input_index].get<float>();
        std::cout << "In0 = " << input
                  << ' ' << node.get_unit_short(input_index) << '\n';

        // Assignment immediately writes the value to the node.
        // Replace DAC0 with a writable variable available on your node.
        node["DAC0"] = 1.25f;
    }
    catch (const std::exception &e) {
        std::cerr << e.what() << '\n';
        return 1;
    }

    return 0;
}

Variable names and types are device-specific. Query or print the node before using the example names:

std::cout << node << std::endl;

Building

The repository defines the CMake target midas::mscb. When MSCB is included with add_subdirectory(), a program can link it as follows:

cmake_minimum_required(VERSION 3.10)
project(mscbxx_example LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_subdirectory(path/to/mscb)
add_executable(mscbxx_example example.cxx)
find_package(Threads REQUIRED)
target_link_libraries(mscbxx_example PRIVATE midas::mscb Threads::Threads)

if(WIN32)
    target_link_libraries(mscbxx_example PRIVATE wsock32 ws2_32)
endif()

The target supplies the MSCB include directories. When linking a separately built library, add include/ to the compiler's include path and link mscb plus the platform libraries required by that build.

midas::mscb

Constructor

mscb(std::string submaster, int address,
     std::string password = "", int debug = 0);

Construction performs all setup synchronously:

  1. opens and authenticates the submaster with mscb_init();
  2. reads general information for address, retrying after a ping if necessary;
  3. reads node uptime;
  4. reads metadata for every variable and builds the name-to-index map; and
  5. calls read_range() to populate the variable cache.

Connection, authentication, protocol-version, and missing-node errors throw mexception, which derives from std::runtime_error. Catching std::exception is therefore sufficient. The exception text includes the source location and, on supported systems, a stack trace.

Node information

std::string get_node_name();
std::string get_submaster();
int get_node_address();
int get_protocol_version();
int get_n_variables();

These values are cached during construction. Streaming the node prints the submaster, node and group addresses, protocol version, revision, uptime, and all cached variables:

std::cout << node << std::endl;

Streaming does not refresh the node first. Call read_range() when fresh values are required.

Refresh variables

int read_range(int first = 0, int last = 0);

Reads an inclusive range with mscb_read_range() and updates the corresponding u_mscb cache entries. With the default arguments (0, 0), it reads every variable. The return value is an MSCB_* status.

The implementation uses a 1024-byte local receive buffer, so the packed range must fit within 1024 bytes. Because (0, 0) means “all,” this method cannot be used to request only variable zero; use the C API when that distinction is needed.

Access by index or name

u_mscb &operator[](int index);
u_mscb &operator[](std::string variable_name);
int idx(const std::string &variable_name);
std::vector<u_mscb> vec(int first, int last);

operator[] returns a reference to the cached variable. Integer access checks the range. Name access calls idx(), which throws when a name is not present. Names are case-sensitive. The current map construction terminates names at eight characters, so named access should use the first eight characters reported for a variable.

vec() returns copies of an inclusive range of cached variables. It performs no bounds checking; prefer checked operator[] access unless the indexes have already been validated.

Units

std::string get_unit(int index);
std::string get_unit_short(int index);

Returns the variable's metric prefix and unit using the long or abbreviated unit tables. Both methods validate the index and throw on an invalid value.

Retry controls

void set_max_retry(int count);
void set_max_eth_retry(int count);

set_max_retry() changes the library-wide MSCB operation retry count. set_max_eth_retry() changes the Ethernet retry count for this object's descriptor.

midas::u_mscb

A u_mscb stores the descriptor, node address, variable index, inferred type, width, and most recently read or assigned value. Applications normally obtain one from mscb::operator[] rather than constructing it directly.

Its public low-level operations are:

u_mscb(int fd, int address, int index, int tid, int size);
int get_tid();
void set_string(char *value);
void set_data(const unsigned char *data);
int write();

set_string() and set_data() update only the cache. write() sends the cached value. These methods are used by midas::mscb; direct construction is rarely necessary because callers would have to reproduce its metadata-to-type mapping themselves.

Reading cached values

template<typename T> T get();
void get(std::string &value);
template<typename T> operator T();
operator std::string();
int get_tid();

get<T>() and the conversion operator convert the cached value to the requested C++ type. They do not contact the node. The string overload formats numeric and Boolean variables and returns string variables unchanged.

Examples:

node.read_range(0, 7);

float temperature = node[2].get<float>();
int state = node[3];
std::string label = node["Label"];
std::cout << node[2] << '\n';

An incompatible conversion throws mexception.

Writing through assignment

Assignments update the cache and immediately call mscb_write():

node[5] = 1.234f;
node["Enabled"] = true;
node["Label"] = std::string("ready");

Overloads exist for 8-, 16-, and 32-bit signed and unsigned integers, bool, float, double, C strings, and std::string. Numeric assignments convert to the target variable's inferred type and can narrow the supplied value.

The current u_mscb::write() returns 1 after calling mscb_write() and does not propagate the underlying write status. Use the C API directly when the application must confirm the acknowledgement or distinguish write failures.

Type inference

The constructor maps MSCB metadata to MIDAS type identifiers:

Variable metadata Type identifier
Unit is UNIT_STRING TID_STRING
MSCBF_FLOAT is set TID_FLOAT
Signed, width 1/2/4 TID_INT8 / TID_INT16 / TID_INT32
Unsigned, width 1/2/4 TID_UINT8 / TID_UINT16 / TID_UINT32

The header also defines TID_BOOL, TID_DOUBLE, and deprecated aliases such as TID_BYTE, TID_WORD, and TID_DWORD. Automatic node discovery does not map 3-byte or 8-byte integer variables to a usable u_mscb type. Use the C API for variable layouts outside the table above.

Lifetime and concurrency

The current midas::mscb class has no explicit close() method and no destructor that calls mscb_exit(). It is best suited to an object that lives for the duration of the process. Applications that repeatedly create and destroy connections, or that require deterministic connection cleanup, should manage the descriptor through the C API.

The underlying C library serializes access to a descriptor, but cached values inside midas::mscb and u_mscb are not independently synchronized. Protect a shared C++ object when several application threads may refresh or assign its variables concurrently.