A record is a collection of fields, possibly of different data types.
Many of the custom types in XOD are some data packed in the record. This way, you can quickly pass grouped values through a single terminal. It also allows you to create specializations for generic nodes and thereby implement polymorphism at the XOD language level.
To simplify the packing and unpacking process without coding a C++ implementation manually, XOD has a marker node xod/patch-nodes/record. When you place this node on the patch with some input terminals and output-self node, XOD automatically generates the code necessary to pack the multiple inputs into a single record. It also creates an unpack patch which can access the record field values.
Grouping of values is useful when you need to pass a bunch of data in the several nodes without insane linking net all over your patches. This guide does not consider examples of such complex programs but gives some use cases of how to pack these data and use it where necessary.
Imagine we want to pack outputs from an accelerometer. It has three data values of type Number, which represent acceleration along X, Y, and Z axes. To do this, we are going to create a new record type:
3d-acceleration.xod/patch-nodes/input-number and give them labels: X, Y, Z.xod/patch-nodes/record marker.xod/patch-nodes/output-self terminal.
Note, the record marker automatically created a @/unpack-3d-acceleration patch, which you can use to get the plain values back out from the record.
As for any other custom type, you can also find out created @/input-3d-acceleration and @/output-3d-acceleration terminal patches in the project browser. So you can encapsulate some logic over your record inside a new patch. To do so, you need to create a new patch and place the @/input-3d-acceleration terminal and the @/unpack-3d-acceleration node and work with it as usual if you want to change some values in the record, place a new @/3d-acceleration node and link it with @/output-3d-acceleration.
Records can be serialized to JSON. JSON is a widespread data interchange format. Most cloud services can work with it. In this way, records make it easy to send data to various services, including XOD Cloud Feeds and especially multiple time series.
The xod/json standard library has an abstract to-json node and specializations for the primitive types. However, when you created a valid record patch, it also automatically creates a specialization for the record, and you can find it in your project with the name @/to-json(3d-acceleration). This patch is a composition of the nodes, and you can open the patch and check out how it works inside. In short, it contains the unpack node, to-json node for each field of the record, and concatenate it into a valid JSON using pin labels as keys in a JSON object.

Records can contain any custom types, including other records, without any limitation on the nesting level. It also makes it possible to send complex data structures in JSON format.

However, most of the custom types have no to-json specializations, so you’ll get a compilation error if you try to serialize a record, which contains, for example, an i2c type. But, if you really need to serialize some custom type you can create to-json specialization manually. It should output a JSON-serialized string.
Some nodes representing hardware devices using a custom type may be defined using the same record mechanism instead of implementing them in C++.
For example, the standard library xod-dev/ds-rtc requires an I2C bus and address of the device. These values pack in the records without any problems. This library uses a record to pass the bus object along with address as a single type, check out how it works.
A record is laid out as a struct with fields of input terminals. Each field has the same type as the terminal and uses its label as the name, prefixed with field_. For example, if you set the label of a terminal to X it will be field_X in the struct.
The C++ implementation of the @/3d-acceleration record patch:
node {
meta {
struct Type {
typeof_X field_X;
typeof_Y field_Y;
typeof_Z field_Z;
};
}
void evaluate(Context ctx) {
Type record;
record.field_X = getValue<input_X>(ctx);
record.field_Y = getValue<input_Y>(ctx);
record.field_Z = getValue<input_Z>(ctx);
emitValue<output_OUT>(ctx, record);
}
}
In case you left terminal labels empty, it will be labeled, as it does with terminals: field_IN1, field_IN2, and so on for the multiple unlabeled terminals. For the single one unlabeled terminal, it will be just field_IN.
The C++ implementation for the @/unpack-3d-acceleration patch:
node {
void evaluate(Context ctx) {
auto record = getValue<output_IN>(ctx);
emitValue<output_X>(ctx, record.field_X);
emitValue<output_Y>(ctx, record.field_Y);
emitValue<output_Z>(ctx, record.field_Z);
}
}
If you want to create your own C++ implementation for some purposes with records on inputs, you can get the record and access its fields the same way as in the implementation of the unpack record patch above.
–
record marker node, the output-self node, and some input terminals.to-json specialization.to-json specialization won’t let you serialize the entire record. However, you can create a missing specialization.struct.