Schema Injection¶
A schema injection is a modification of a device schema, to bring further properties visible outside or to update attributes of existing properties (such as the maximum size of a vector). Any number and types of properties can be injected, whether parameters or slots.
In this example, we will define a slot that adds a boolean injectedProperty.
Begin by defining a slot that will call the extend function:
// Register the slot in the device schema
void MyDevice::expectedParameters(Schema& expected) {
SLOT_ELEMENT(expected).key("extend")
.displayedName("Extend")
.description("Extend the schema and introduce a new boolean property")
.commit();
}
// Register the slot in the constructor
MyDevice::MyDevice(const karabo::util::Hash& config) {
KARABO_SLOT(extend);
}
There are two methods to injecting properties: appendSchema and updateSchema,
both inherited from karabo::core::Device
.
appendSchema()
will add properties to the device, and can be called any
number of time.
updateSchema()
will take the the original schema of the device, and add
the new one to the device. However, it will discard what has been previously
appended or updated!
Nonetheless, if appendSchema()
is called after updateSchema()
, then
the parameters from both injections are kept.
If updateSchema()
is called with an empty schema, then the device will be
reset to its original schema, as defined in MyDevice::expectedParameters()
.
This works, as internally, a device keeps three schemas: its static schema, as defined in expectedParameters, the injected schema, which is modified on injections, and the full schema, the combination of both which is exposed to the rest of the ecosystem.
void MyDevice::extend(void) {
// Define a new Schema object
Schema schema;
// Then populate that new Schema
BOOL_ELEMENT(schema).key("injectedProperty")
.displayedName("Hello")
.description("A fresh property")
.daqPolicy(DAQPolicy::OMIT)
.readOnly().initialValue(true)
.commit();
// Finally, append the schema to our existing device
this->appendSchema(schema);
// Or
// this->updateSchema(schema);
}
Once a device has been modified, either of these log message will be given:
INFO MyDevice : Schema appended
INFO MyDevice : Schema updated
Re-injecting a property, such as injectedProperty, will keep its current value if possible. However, with different types, an error will be raised if the value cannot be cast e.g. going from UINT32_ELEMENT to INT16_ELEMENT with a value out of bound.
Check Whether A Property Exists¶
If your device has an update loop, you can either use flags to check
whether schema injection has already been done, or use getFullSchema()
:
void MyDevice::update(void) {
const Schema schema = this->getFullSchema();
if (schema.has("injectedProperty")) {
this->set("injectedProperty"), !this->get<bool>("injectedProperty"));
}
}
Injected Properties and DAQ¶
Injected Properties and the DAQ need some ground rules in order to record these properties correctly.
In order for the DAQ to record injected properties, the DAQ needs to request the
updated schema again, using the Run Controller’s applyConfiguration()
slot.
This can be prone to operator errors, and therefore it is recommended that only properties injected at instantiation to be recorded.
However, a common need for Schema updates is to specify the maxSize attribute fo vector or table elements, as the DAQ only supports fixed length arrays, of which the size has to be predefined.
For that, there is the special function karabo::core::Device::appendSchemaMaxSize()
which, given a property path and the new length, will update the schema
accordingly.
Such a vector:
void MyDevice::expectedParameters(Schema& expected) {
NODE_ELEMENT(expected).key("node").commit();
VECTOR_UINT32_ELEMENT(expected).key("node.vector")
.displayedName("Vector")
.readOnly()
.maxSize(5)
.commit();
}
Can be resized as follows:
this->appendSchemaMaxSize("node.vector", 50);
If several updates are made, then it is recommended to set emitFlag to false for all vectors apart of the last one. Only a single update will be then sent:
this->appendSchemaMaxSize("node.vector0", 50, false);
this->appendSchemaMaxSize("node.vector1", 50, false);
this->appendSchemaMaxSize("node.vector2", 50);