Device Scenes ============= Karabo provides a protocol for devices to share predefined scenes. These allows the author of a device to provide what they think are a good starting point. Moreover, these are easily accessible from the topology panel in the GUI: .. image:: images/default_scenes.png A default scene can also be accessed by double-clicking on a device. This section shows how to enable your device to have builtin scenes. Implementing this functionality requires the creation of a scene in the scene editor, its conversion to a C++ `ostringstream`, and adding the `requestScene` slot to the device. From Scene To Header File ------------------------- Begin by drawing an adequate scene in the GUI's scene editor, and save it locally on your computer as SVG (right-click on scene -> *Save to File*). SVG is XML-based, and thus can be easily edited. Convert the SVG to a header file `scenes.hh` with a function. Thus the original SVG: .. code-block:: xml Becomes: .. code-block:: c++ #include std::string getControls(const std::string& instanceId) { std::ostringstream ret; // SVG header ret << "" << "" // Start button label << "" << "" // Start button itself << "" // Stop button label << "" << "" // Stop button itself << "" // SVG footer << ""; return ret.str(); } Add this file to your project. Providing The Scene From Your Device ------------------------------------ Add a read-only `VECTOR_STRING_ELEMENT` property called `availableScenes` to your expected parameters, and register and implement the `requestScene` slot. This is a predefined slot, which allows various actors to understand the scene protocol. The slot takes a Hash `params` and lets the device reply with a Hash containing the origin, its datatype (`deviceScene`), and the scene serialized as xml: .. code-block:: c++ #include "scenes.hh" using namespace karabo::util; // Define the list of scenes void MyDevice::expectedParameters(karabo::util::Schema& expected) { VECTOR_STRING_ELEMENT(expected).key("availableScenes") .setSpecialDisplayType(KARABO_SCHEMA_DISPLAY_TYPE_SCENES) .readOnly().initialValue(std::vector{"controls"}) .commit(); } // Register in the constructor that we have this functionality MyDevice::MyDevice(const karabo::util::Hash& config) : karabo::core::Device<>(config) { KARABO_SLOT(requestScene, karabo::util::Hash); } // The function that provides the scene void MyDevice::requestScene(const karabo::util::Hash& params) { Hash result("type", "deviceScene", "origin", this->getInstanceId()); Hash& payload = result.bindReference("payload"); payload.set("success", true); payload.set("name", "controls"); payload.set("data", getControls()); this->reply(result); } Providing Several Scenes ------------------------ Would you want to provide several scenes (e.g., simple overview and control scene), you can define several functions in `scenes.hh`, and modify `requestScene` to check `name` in `params`: .. code-block:: c++ #include "scenes.hh" using namespace karabo::util; // Define the list of scenes void MyDevice::expectedParameters(karabo::util::Schema& expected) { VECTOR_STRING_ELEMENT(expected).key("availableScenes") .setSpecialDisplayType(KARABO_SCHEMA_DISPLAY_TYPE_SCENES) .readOnly().initialValue(std::vector{"overview", "controls"}) .commit(); } // Register in the constructor that we have this functionality MyDevice::MyDevice(const karabo::util::Hash& config) : karabo::core::Device<>(config) { KARABO_SLOT(requestScene, karabo::util::Hash); } // The function that provides the scene void MyDevice::requestScene(const karabo::util::Hash& params) { Hash result("type", "deviceScene", "origin", this->getInstanceId()); Hash& payload = result.bindReference("payload"); payload.set("success", false); const std::string& which = param.get("name"); if ("overview" == which) { payload.set("name", "overview"); payload.set("data", getOverview(this->getInstanceId())); payload.set("success", true); } else if ("controls" == which) { payload.set("name", "controls"); payload.set("data", getControls(this->getInstanceId())); payload.set("success", true); } else { KARABO_LOG_ERROR << "Scene '" << which << "' was requested, but we don't have any"; } this->reply(result); } .. note:: There is the convention that the default scene (of your choice) should be first in the `availableScenes` list: this will be the one served when double-clicking, for instance. Linking To Other Devices Scenes ------------------------------- The following applies whether you want to link to another of your scenes or to another device's scene. Let's say that you want to add links in your `overview` scene to your `controls` scene. Create the `overview` scene in Karabo, and add a link to `controls` by dragging `availableScenes` from the configuration editor, then changing the widget to `Device Scene Link`, then do `Configure Link`, and select the `controls` scene. Export the scene to SVG, convert it to a C++ function as shown above. If you want to link to another device, make the function accept another `remoteInstanceId` parameter, and point to that device: .. code-block:: c++ std::string sceneWithLink(const std::string& instanceId, const std::string& remoteInstanceId) { std::ostringstream ret; // SVG header ret << "" << "" // Link to other device's controls scene << "" // SVG footer << ""; return ret.str(); } Reference Implementations ------------------------- DataGenerator_: provides two scenes, overview and controls with links, as described here Beckhoff_: More complex possibilites are used here. KEP21_: definition of the scene protocol .. _DataGenerator: https://git.xfel.eu/karaboDevices/DataGenerator .. _Beckhoff: https://git.xfel.eu/karaboDevices/Beckhoff .. _KEP21: https://git.xfel.eu/Karabo/enhancement-proposals/blob/kep21/keps/kep-0021.rst