Common Vision Blox 15.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Friends Modules Pages
GEVServer/Cvb++/QmlGevServer

This example program is located in your CVB installation under %CVB%Tutorial/GEVServer/Cvb++/QmlGevServer.

backend.cpp:

#include "backend.hpp"
#include "QDebug"
#include <cvb/data_type.hpp>
#include <cvb/gevserver/node.hpp>
#include <cvb/gevserver/node_map.hpp>
#include <cvb/gevserver/server.hpp>
#include <cvb/gevserver/stream.hpp>
#include <cvb/size_2d.hpp>
#include <cvb/device_factory.hpp>
#include <cvb/image.hpp>
#include <cvb/ui/image_view_item.hpp>
BackEnd::BackEnd(QQuickView *view, QObject *parent) : QObject(parent), view_{view}
{
// Register view to queue commands over threads
qRegisterMetaType<QWindow::Visibility>();
// open a device
device_ = Cvb::DeviceFactory::Open(Cvb::InstallPath() + CVB_LIT("drivers/CVMock.vin"));
hasRingBuffer_ = !!device_->Stream()->RingBuffer();
if (hasRingBuffer_)
device_->Stream()->RingBuffer()->SetLockMode(Cvb::Driver::RingBufferLockMode::On);
streamHandler_->Create(device_->Stream());
context_ = view->rootContext();
context_->setContextProperty("mainImage", &imageController_);
device_->Stream()->GetSnapshot();
imageController_.Refresh(device_->DeviceImage(), Cvb::UI::AutoRefresh::On);
}
BackEnd::~BackEnd()
{
if (acqThread_)
StopAcq();
if (server_ != Cvb::GevServer::ServerPtr() && server_->State() != Cvb::GevServer::State::Configuration)
server_->Stop();
if (device_->Stream()->IsRunning())
device_->Stream()->Stop();
}
void BackEnd::StartServer(const int &addressIndex, const int &driverTypeIndex)
{
qDebug() << "Starting server on address index " << addressIndex << " and driver type index " << driverTypeIndex;
// Check for recursions
if (startInProgress_)
return;
startInProgress_ = true;
try
{
if (!acqStarted_)
{
// Stop and reset grab button if running
StopGrab();
// Create server
server_ = Cvb::GevServer::Server::CreateWithConstSize(device_->DeviceImage()->Size(),
device_->DeviceImage()->ColorModel(),
device_->DeviceImage()->Plane(0).DataType(),
static_cast<Cvb::GevServer::DriverType>(driverTypeIndex));
AddGenICamFeatures();
// Set custom version/id
server_->SetUserVersion(CVB_LIT("_C++GevServer"));
server_->NodeMap()->SetXmlFileVersion(Cvb::GenApi::NodeMap::GenApiVersion(1, 0, 0));
if (hasRingBuffer_)
server_->Stream()->SetResendBuffersCount(2);
else
server_->Stream()->SetResendBuffersCount(0);
// Start server
auto address = iFaces_.at(addressIndex).IPAddress();
server_->Start(address);
// Start acquisition thread
if (!acqThread_)
StartAcq();
acqStarted_ = true;
qDebug() << "Started server";
}
else
{
if (acqThread_)
StopAcq();
server_->Stop();
acqStarted_ = false;
qDebug() << "Stopped server";
}
}
catch (...)
{
}
startInProgress_ = false;
}
void BackEnd::operator()() noexcept
{
std::unique_lock<std::mutex> guard(mtx_);
device_->Stream()->Start();
while (!stopAcq_)
{
if (grabSwitchSwitched_)
{
if (hasRingBuffer_)
{
auto img = device_->Stream()->Wait<Cvb::RingBufferImage>();
img.Image->Unlock();
}
else
device_->Stream()->Wait();
}
else if (server_ != Cvb::GevServer::ServerPtr() && server_->State() == Cvb::GevServer::State::AcquisitionEnabled &&
!snapSend_)
{
try
{
auto img = device_->Stream()->Wait<Cvb::RingBufferImage>();
if (img.Status == Cvb::WaitStatus::Ok)
{
if (hasRingBuffer_)
{
bufferIndex_ = img.Image->BufferIndex();
imgMap_[bufferIndex_] = img.Image;
server_->Stream()->Send(img.Image, [this](const Cvb::ImagePtr &) {
imgMap_[bufferIndex_]->Unlock();
qDebug() << "unlocked buffer index " << std::to_string(bufferIndex_).c_str();
});
}
else
server_->Stream()->Send(*img.Image.get());
}
}
catch (...)
{
}
}
}
device_->Stream()->Stop();
}
void BackEnd::SwitchGrab(const bool &switched)
{
grabSwitchSwitched_ = switched;
if (grabSwitchSwitched_)
{
if (!acqThread_)
StartAcq();
}
else
{
if (acqThread_)
StopAcq();
}
qDebug() << "Grab switch switched to" << switched;
}
QVariantList BackEnd::ConnectionList()
{
iFaces_.clear();
QVariantList list;
list.clear();
for (auto const &iFace : iFaces_)
{
std::stringstream ss;
ss << iFace.IPAddress() << " (" << iFace.IPv4Mask() << ")";
list.append(std::string(ss.str()).c_str());
}
return list;
}
void BackEnd::LoadDevice(const QString &path)
{
qDebug() << "Loading new device on path " << path;
StopGrab();
try
{
auto tmpDevice = Cvb::DeviceFactory::Open(CVB_LIT(path.toLatin1().toStdString().c_str()));
device_.reset();
device_ = tmpDevice;
device_->Stream()->GetSnapshot();
hasRingBuffer_ = !!device_->Stream()->RingBuffer();
if (hasRingBuffer_)
device_->Stream()->RingBuffer()->SetLockMode(Cvb::Driver::RingBufferLockMode::On);
imageController_.Refresh(device_->DeviceImage(), Cvb::UI::AutoRefresh::On);
}
catch (...)
{
qDebug() << "Failed to load new device";
}
}
void BackEnd::SaveImg(const QString &path)
{
qDebug() << "Saving new file on path " << path;
device_->DeviceImage()->Save(CVB_LIT(path.toLatin1().toStdString().c_str()));
}
void BackEnd::BtnSnap()
{
qDebug() << "Snap image";
StopGrab();
// Snap a single image
device_->Stream()->GetSnapshot();
}
void BackEnd::SwitchEnableSnapSend(const bool &switched)
{
snapSend_ = switched;
if (snapSend_)
StopAcq();
else
StartAcq();
qDebug() << "Enable snap switch switched to" << switched;
}
void BackEnd::BtnSnapSend()
{
if (server_->State() == Cvb::GevServer::State::AcquisitionEnabled)
{
qDebug() << "Snap send";
try
{
auto img = device_->Stream()->GetSnapshot();
if (img.Status == Cvb::WaitStatus::Ok)
if (hasRingBuffer_)
{
server_->Stream()->Send(img.Image, nullptr);
}
else
{
auto img2 = device_->Stream()->GetSnapshot();
if (img2.Status == Cvb::WaitStatus::Ok)
server_->Stream()->Send(*img2.Image);
}
}
catch (...)
{
}
}
else
qDebug() << "Acquisition not enabled on server";
}
void BackEnd::StopGrab()
{
// If grab is currently running, stop it
if (device_->Stream()->IsRunning() && grabSwitchSwitched_)
{
QObject *box = view_->findChild<QObject *>("switchGrab", Qt::FindChildrenRecursively);
if (box)
box->setProperty("checked", false);
}
if (device_->Stream()->IsRunning() && acqStarted_)
{
StopAcq();
}
}
void BackEnd::OnWindowSizeChanged(const Cvb::GevServer::ValueNode & /*value*/)
{
switch (windowStateRegNode_->Value())
{
case 0:
view_->showMinimized();
break;
case 1:
view_->showNormal();
break;
case 2:
view_->showMaximized();
default:
throw std::runtime_error("Invalid window state value");
break;
}
}
void BackEnd::AddGenICamFeatures()
{
auto catNode = Cvb::GevServer::CategoryNode::Create(CVB_LIT("Cust::CustomFeatures"));
server_->NodeMap()->AddNode(catNode);
catNode->SetDisplayName(CVB_LIT("Custom Features"));
catNode->SetToolTip(CVB_LIT("Contains all application defined features."));
auto rootNode = server_->NodeMap()->Node(CVB_LIT("Root"));
rootNode->Add(catNode, Cvb::GevServer::NodeList::Child);
windowStateRegNode_ = Cvb::GevServer::Int32RegNode::Create(CVB_LIT("Cust::WindowStateReg"));
server_->NodeMap()->AddNode(windowStateRegNode_);
windowStateRegNode_->SetVisibility(Cvb::GenApi::Visibility::Invisible);
// No cache because of polling
windowStateRegNode_->SetCacheMode(Cvb::GenApi::CacheMode::NoCache);
// Polling time in ms
windowStateRegNode_->SetPollingTime<float, std::ratio<1, 1000>>(
std::chrono::duration<float, std::ratio<1, 1000>>(333));
windowStateRegNode_->SetValue(1); // View in normal mode
windowStateRegNode_->RegisterEventWrittenUpdated(
[this](const Cvb::GevServer::ValueNode &value) { OnWindowSizeChanged(value); });
auto enumerationNode = Cvb::GevServer::EnumerationNode::Create(CVB_LIT("Cust::WindowState"));
server_->NodeMap()->AddNode(enumerationNode);
enumerationNode->SetDisplayName(CVB_LIT("Window State"));
enumerationNode->SetToolTip(CVB_LIT("Current window state of server application."));
enumerationNode->SetValueConfig<Cvb::GevServer::IntegerBaseNodePtr>(windowStateRegNode_);
auto minimized = Cvb::GevServer::EnumEntryNode::Create(CVB_LIT("Cust::Minimized"));
minimized->SetNumericValue(0);
enumerationNode->Add(minimized, Cvb::GevServer::NodeList::Child);
auto normalNode = Cvb::GevServer::EnumEntryNode::Create(CVB_LIT("Cust::Normal"));
normalNode->SetNumericValue(1); // Must be ascending entry in vector
enumerationNode->Add(normalNode, Cvb::GevServer::NodeList::Child);
auto maxNode = Cvb::GevServer::EnumEntryNode::Create(CVB_LIT("Cust::Maximized"));
maxNode->SetNumericValue(2); // Must be ascending entry in vector
enumerationNode->Add(maxNode, Cvb::GevServer::NodeList::Child);
catNode->Add(enumerationNode, Cvb::GevServer::NodeList::Child);
}
static std::shared_ptr< T > Open(const String &provider, AcquisitionStack acquisitionStack=AcquisitionStack::PreferVin)
static CategoryNodePtr Create(const String &name, const GevServer::Namespace &nameSpace)
static EnumEntryNodePtr Create(const String &name, const GevServer::Namespace &nameSpace)
static EnumerationNodePtr Create(const String &name, const GevServer::Namespace &nameSpace)
static Int32RegNodePtr Create(const String &name, const GevServer::Namespace &nameSpace, const std::int64_t &address)
static ServerPtr CreateWithConstSize(Size2D< int > size, PfncFormat pixelFormat, GevServer::DriverType driverType=GevServer::DriverType::Auto)
Image(Size2D< int > size, int numPlanes=1, DataType dataType=DataType::Int8BppUnsigned())
std::shared_ptr< IntegerBaseNode > IntegerBaseNodePtr
std::shared_ptr< Server > ServerPtr
std::shared_ptr< Image > ImagePtr
String InstallPath()
static std::vector< LogicalNetworkInterface > GetAllAvailable()

backend.hpp:

#ifndef BACKEND_H
#define BACKEND_H
#include "QVariantList"
#include <QObject>
#include <QQmlContext>
#include <QQuickView>
#include <QString>
#include <thread>
#include <cvb/async/single_stream_handler.hpp>
#include <cvb/gevserver/logical_network_interface.hpp>
#include <cvb/ui/image_view_item.hpp>
class BackEnd : public QObject
{
// This macro is required for QObject support. You should get a compiler
// error if you don't include this.
Q_OBJECT
public:
// QObjects are expected to support a parent/child hierarchy
explicit BackEnd(QQuickView *view, QObject *parent = 0);
~BackEnd();
void OnWindowSizeChanged(const Cvb::GevServer::ValueNode &value);
void StartAcq()
{
std::unique_lock<std::mutex> guard(mtx_);
stopAcq_ = false;
acqThread_.reset(new std::thread(std::ref(*this)));
}
void StopAcq()
{
stopAcq_ = true;
if (acqThread_)
{
acqThread_->join();
acqThread_.reset();
}
}
// Acquisition thread
void operator()() noexcept;
signals:
public slots:
void StartServer(const int &addressIndex, const int &driverTypeIndex);
void LoadDevice(const QString &path);
void SaveImg(const QString &path);
void BtnSnap();
void BtnSnapSend();
QVariantList ConnectionList();
void SwitchGrab(const bool &switched);
void SwitchEnableSnapSend(const bool &switched);
private:
void StopGrab();
void AddGenICamFeatures();
QQuickView *view_;
QQmlContext *context_;
// Create a controller object to communicate with QML
Cvb::UI::ImageController imageController_;
Cvb::EventCookie eventCookie_;
// Hold this node for callback
Cvb::GevServer::Int32RegNodePtr windowStateRegNode_;
bool startInProgress_{false};
bool grabSwitchSwitched_{false};
bool acqStarted_{false};
bool snapSend_{false};
bool hasRingBuffer_{false};
int bufferIndex_;
std::map<int, Cvb::RingBufferImagePtr> imgMap_;
std::vector<Cvb::GevServer::LogicalNetworkInterface> iFaces_;
Cvb::DevicePtr device_;
std::atomic<bool> stopAcq_;
std::unique_ptr<std::thread> acqThread_;
std::mutex mtx_;
};
#endif
std::shared_ptr< SingleStreamHandler > SingleStreamHandlerPtr
std::shared_ptr< Device > DevicePtr

main.cpp:

// The QmlGevServer example starts a QML GevServer which can load a device and stream the device image.
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
#include <iostream>
#include <QApplication>
#include <QIcon>
#include <QMetaType>
#include <QQmlContext>
#include <QQuickStyle>
#include <QQuickView>
#include <cvb/image.hpp>
#include "backend.hpp"
int main(int argc, char *argv[])
{
try
{
QQuickStyle::setStyle("Windows");
QQuickStyle::setFallbackStyle("Universal");
QApplication app(argc, argv);
app.setOrganizationName("STEMMER IMAGING");
app.setOrganizationDomain("https://www.stemmer-imaging.com/");
app.setApplicationName("GevServer C++ tutorial");
// Register QML components for an image display
Cvb::UI::ImageViewItem::Register();
QQuickView *view = new QQuickView;
view->setResizeMode(QQuickView::SizeRootObjectToView);
view->setIcon(QIcon(":/qttutorial.png"));
view->resize(640, 390);
view->show();
// Set context property before creating QML file
BackEnd be(view);
view->rootContext()->setContextProperty("backEnd", &be);
// Load main QML file
view->setSource(QUrl::fromLocalFile("../main.qml"));
return app.exec();
}
catch (const std::exception &error)
{
std::cout << error.what() << std::endl;
}
}