This example shows a fictional multi-channel oscilloscope. When the acquisition is triggered on a channel then the channel pushes a sinusoidal wave to the control system.
The example shows how to create the channels programmatically (useful when you have a large number of channel that offer the same functionality).
The Makefile for the sample is the following:
CXX = g++
CXXFLAGS = -std=c++0x -Wall -Wextra -pedantic -fPIC -pthread -DNDS3_DLL
# Flags passed to gcc during linking
LINK = -shared -fPIC -Wl,-as-needed
# Name of the nds library
TARGET = liboscilloscopemultichannel.so
# Additional linker libraries
LIBS = -lnds3
# Source code files used in this project
SRCS = oscilloscopeMultiChannel.cpp
OBJS = $(SRCS:.cpp=.o)
# Rules for building
$(TARGET): $(OBJS)
$(CXX) $(LINK) -o $@ $^ $(LIBS)
.PHONY: clean
clean:
$(RM) $(TARGET) $(OBJS)
The macro NDS_DEFINE_DRIVER tells that the Oscilloscope class is the one that must be allocated and initialized when the control system wants to use our "Oscilloscope" device.
The constructor of our oscilloscope creates a root node for the device and names it using the parameter device passed by the control system. It is not mandatory to call the root node using the device parameter.
The root node is declared as a Port: this means that it holds an interface with the control system.
We could have declared the root node as a simple Node and each channel as a Port: in this case each channel would keep an interface with the control system and all its callback would be executed on separate threads (one per channel).
The Channel class is just a helper that add the acquisition nodes to the root one and keep few references to the channel's PVs and to the channel's acquisition node. For fewer number of channels we could have implemented this functionality directly in the Oscillocope class.
#include <functional>
#include <sstream>
#include <math.h>
#include <unistd.h>
class Channel;
class Oscilloscope
{
public:
private:
std::vector<std::shared_ptr<Channel> > m_channels;
};
class Channel
{
public:
Channel(
const std::string& name,
nds::Node& parentNode);
void switchOn();
void switchOff();
void start();
void stop();
void recover();
void acquisitionLoop();
volatile bool m_bStopAcquisition;
};
{
for(size_t numChannel(0); numChannel != 16; ++numChannel)
{
std::ostringstream channelName;
channelName << "Ch" << numChannel;
m_channels.push_back(std::make_shared<Channel>(channelName.str(), rootNode));
}
rootNode.initialize(this, factory);
}
Channel::Channel(
const std::string &name,
nds::Node &parentNode)
{
100,
std::bind(&Channel::switchOn, this),
std::bind(&Channel::switchOff, this),
std::bind(&Channel::start, this),
std::bind(&Channel::stop, this),
std::bind(&Channel::recover, this),
std::bind(&Channel::allowChange, this,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3));
}
void Channel::switchOn()
{
}
void Channel::switchOff()
{
}
void Channel::start()
{
m_bStopAcquisition = false;
m_acquisitionThread = m_acquisition.runInThread("Acquisition", std::bind(&Channel::acquisitionLoop, this));
}
void Channel::stop()
{
m_bStopAcquisition = true;
m_acquisitionThread.join();
}
void Channel::recover()
{
}
{
return true;
}
void Channel::acquisitionLoop()
{
std::vector<std::int32_t> outputData(m_acquisition.getMaxElements());
std::int64_t angle(0);
while(!m_bStopAcquisition)
{
size_t maxAmplitude = m_amplitude.getValue();
for(size_t scanVector(0); scanVector != outputData.size(); ++scanVector)
{
outputData[scanVector] = (double)maxAmplitude * sin((double)(angle++) / 10.0f);
}
m_acquisition.push(m_acquisition.getTimestamp(), outputData);
::usleep(100000);
}
}