NDS3
1.0.0
API reference manual
|
This section will guide you through the development of a simple Device Support based on the NDS3 framework
Here we assume that you already installed the NDS3 library and the NDS3 control system layer of your choice.
Device Supports for the NDS3 framework must declare the device functionalities using a tree-like structure: the tree structure can be composed by:
A node can contain children nodes or PVs. There are several kind of nodes:
The responsability of defining the device structure falls on the constructor of the class that the developer decides to use as representation of the device: when a specific device support is needed then NDS3 will allocate and construct the class that represents the device and expects that the constructor of the class will specify all the nodes and PVs that are needed.
PVs, or Process Variables, are variables that are visible from both the Device Support and from the clients connected to the control system.
NDS3 differentiates between input PVs and output PVs;
For instance, a device representing a temperature sensor will have one input PV that it will update regularly with the detected temperature, while a power supply could have an output PV that set the desidered output voltage.
NDS3 provides 2 kinds of input and output PVs:
All the input PVs also support the push model, which allows the Device Support to forcefully push data to the Control System at any moment.
The first device that we are going to build is a really simple thermometer: It has a single input PV which supplies the temperature in Kelvin degrees.
The structure of the termomether device is really simple: it provides a root node (a Port, so it can communicate with the Control System) that represents the device and an input PV that the control system will read to get the current temperature.
Let's put this in C++ form:
What did we do with this code?
The constructor takes three parameters passed by NDS3 when it allocates the device:
We allocate a Port as root node: a Port node holds a reference to the underlying control system. PVs can be added only to Ports or to nodes that have a Port up in the hierarchy.
The variable rootNode contains a shared pointer to the acual implementation of the Port: when we will register rootNode with the Control System then NDS will take care of storing the shared pointer internally and therefore the Port will continue to exists even after rootNode goes out of scope.
After we declare the root node then we call nds::Node::addChild() on it to add a child component, in our case a PVDelegateIn that handles double floating point values. PVDelegateIN PVs are not able to store the value internally but rely on a function that is called when the control system wants to read the value. In our case we provide a callback to getTemperature() which always return 10 degrees Kelvin.
Finally we call initialize() which registers the root node and its children with the Control System.
If we are running our Device Support using EPICS as control system then the following PV will be available:
rootNodeName-Temperature
Issuing a dbpf rootNodeName-Temperature.PROC 1 will cause the framework to call getTemperature() and fill the PV with the value 10.
The Makefile for a NDS3 device is quite straightforward: it does not depend on EPICS or any other Control System you may be using and you just have to make sure that it includes the shared library nds3 and specify the flag NDS3_DLL (tells the compiler that the NDS3 is being linked dynamically).
An example for our thermometer:
This makefile will generate the shared module libthermometer.so. Now you can load the device into your NDS3 control system of choice and run it.
On EPICS you will use the command to load the shared module and the command to allocate and construct the device.
For instance:
epics> ndsLoadDriver path/to/libthermometer.so epics> ndsCreateDevice Thermometer testDevice epics> iocInit epics> dbl testDevice-Temperature epics> dbpf testDevice-Temperature.PROC 1 epics> dbgf testDevice-Temperature 10