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: .. code-block:: c++ // 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 :class:`karabo::core::Device`. :func:`appendSchema` will add properties to the device, and can be called any number of time. :func:`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 :func:`appendSchema` is called after :func:`updateSchema`, then the parameters from both injections are kept. If :func:`updateSchema` is called with an empty schema, then the device will be reset to its original schema, as defined in :func:`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. .. code-block:: c++ 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 :func:`getFullSchema`: .. code-block:: c++ void MyDevice::update(void) { const Schema schema = this->getFullSchema(); if (schema.has("injectedProperty")) { this->set("injectedProperty"), !this->get("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 :func:`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 :func:`karabo::core::Device::appendSchemaMaxSize` which, given a property path and the new length, will update the schema accordingly. Such a vector: .. code-block:: c++ 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: .. code-block:: c++ 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: .. code-block:: c++ this->appendSchemaMaxSize("node.vector0", 50, false); this->appendSchemaMaxSize("node.vector1", 50, false); this->appendSchemaMaxSize("node.vector2", 50);