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:
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¶
Construction performs all setup synchronously:
- opens and authenticates the submaster with
mscb_init(); - reads general information for
address, retrying after a ping if necessary; - reads node uptime;
- reads metadata for every variable and builds the name-to-index map; and
- 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:
Streaming does not refresh the node first. Call read_range() when fresh
values are required.
Refresh variables¶
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¶
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¶
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():
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.