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