CVB++ 14.0
GevServer/QmlGevServer

This example requires Qt5 >= 5.9 setup for building.

You may build it with Ubuntu 18.04's default Qt5 after installing:

1// ---------------------------------------------------------------------------
4// ---------------------------------------------------------------------------
5
6#include <iostream>
7
8#include <QApplication>
9#include <QIcon>
10#include <QMetaType>
11#include <QQmlContext>
12#include <QQuickStyle>
13#include <QQuickView>
14
15#include <cvb/image.hpp>
16
17#include "backend.hpp"
18
19int main(int argc, char *argv[])
20{
21 try
22 {
23 QQuickStyle::setStyle("Windows");
24 QQuickStyle::setFallbackStyle("Universal");
25 QApplication app(argc, argv);
26 app.setOrganizationName("STEMMER IMAGING");
27 app.setOrganizationDomain("https://www.stemmer-imaging.com/");
28 app.setApplicationName("GevServer C++ tutorial");
29
30 // Register QML components for an image display
32
33 QQuickView *view = new QQuickView;
34 view->setResizeMode(QQuickView::SizeRootObjectToView);
35 view->setIcon(QIcon(":/qttutorial.png"));
36
37 view->resize(640, 390);
38 view->show();
39
40 // Set context property before creating QML file
41 BackEnd be(view);
42 view->rootContext()->setContextProperty("backEnd", &be);
43
44 // Load main QML file
45 view->setSource(QUrl::fromLocalFile("../main.qml"));
46
47 return app.exec();
48 }
49 catch (const std::exception &error)
50 {
51 std::cout << error.what() << std::endl;
52 }
53}
static void Register()
Convenience method to register this type in QML.
Definition: image_view_item.hpp:670
void setApplicationName(const QString &application)
void setOrganizationDomain(const QString &orgDomain)
void setOrganizationName(const QString &orgName)
void setContextProperty(const QString &name, QObject *value)
void setResizeMode(QQuickView::ResizeMode)
QQmlContext * rootContext() const const
void setSource(const QUrl &url)
QUrl fromLocalFile(const QString &localFile)
void resize(const QSize &newSize)
void setIcon(const QIcon &icon)
void show()
1import QtQuick 2.3
2import CvbQuick 1.0 as CvbQuick
3import QtQuick.Controls 2.2
4import QtQuick.Layouts 1.3
5import QtQuick.Dialogs 1.2
6import QtQuick.Controls.Universal 2.2
7
8GroupBox {
9 id: gridBox
10 width: 640
11 height: 390
12 Layout.fillWidth: true
13 Layout.fillHeight: true
14
15 property var loadFile: true
16 property var enableSnapSend: false
17
18 Universal.theme: Universal.System
19
20 background: Rectangle {
21 width: parent.width
22 height: parent.height
23 color: Universal.background
24 }
25
26 FileDialog {
27 id: fileDialog
28 modality: Qt.NonModal
29 title: loadFile ? "Select an image or driver to load" : "Save image"
30 selectExisting: loadFile
31 selectFolder: false
32 nameFilters: ["Drivers (*.vin *.emu)", "Image files (*png *.bmp)", "All files (*)"]
33 selectedNameFilter: loadFile ? "Drivers" : "Image files"
34 sidebarVisible: true
35 onAccepted: {
36 var path = fileUrl.toString();
37 // remove prefixed "file:///"
38 path = path.replace(/^(file:\/{3})/,"");
39 // unescape html codes like '%23' for '#'
40 var cleanPath = decodeURIComponent(path);
41
42 loadFile ? backEnd.LoadDevice(path) : backEnd.SaveImg(path)
43 }
44 }
45
46 GridLayout {
47 id: gridLayout
48 rows: 4
49 flow: GridLayout.TopToBottom
50 anchors.fill: parent
51
52 ComboBox
53 {
54 id: connectionCombo
55 enabled: btnStartServer.m_server_disabled
56 Layout.fillWidth: true
57 Layout.columnSpan: 2
58 model: backEnd.ConnectionList()
59 }
60
61 CvbQuick.ImageView
62 {
63 id: imageView
64 image : mainImage
65 Layout.rowSpan: 3
66 Layout.columnSpan: 3
67 Layout.fillHeight: true
68 Layout.fillWidth: true
69 uploadMode : CvbQuick.UploadMode.Viewport
70 }
71
72
73 ComboBox
74 {
75 id: driverTypeCombo
76 enabled: btnStartServer.m_server_disabled
77 Layout.fillWidth: true
78 model:
79 (Qt.platform.os == "windows") ?
80 [
81 qsTr("Socket Driver (loopback)"),
82 qsTr("Filter Driver (default)")
83 ]
84 :
85 [
86 qsTr("Socket Driver")
87 ]
88 }
89
90 Button
91 {
92 objectName: "btnStartServer"
93 id: btnStartServer
94 implicitWidth: btnLoadDevice.width
95 property var m_server_disabled: true
96 text: m_server_disabled ? qsTr("Start Server") : qsTr("Stop Server")
97 onClicked: {
98 m_server_disabled = m_server_disabled ? false : true
99 backEnd.StartServer(connectionCombo.currentIndex, driverTypeCombo.currentIndex)
100 }
101 }
102
103 Column
104 {
105 spacing: 5
106
107 Button
108 {
109 objectName: "btnLoadDevice"
110 id: btnLoadDevice
111 enabled: btnStartServer.m_server_disabled
112 text: qsTr("Load Device")
113 onClicked:
114 {
115 loadFile = true
116 fileDialog.open()
117 }
118
119 }
120
121 Button
122 {
123 objectName: "btnSaveImg"
124 id: btnSaveImg
125 implicitWidth: btnLoadDevice.width
126 text: qsTr("Save Image")
127 onClicked:
128 {
129 loadFile = false
130 fileDialog.open()
131 }
132 }
133
134 Button
135 {
136 objectName: "btnSnap"
137 id: btnSnap
138 implicitWidth: btnLoadDevice.width
139 width: btnSaveImg.width
140 enabled: btnStartServer.m_server_disabled
141 text: qsTr("Snap")
142 onClicked: backEnd.BtnSnap()
143 }
144
145 Switch
146 {
147 objectName: "switchEnableSnapSend"
148 id: switchEnableSnapSend
149 text: qsTr("Enable Snap Send")
150 checked: false
151 enabled: !btnStartServer.m_server_disabled
152 onCheckedChanged:
153 {
154 enableSnapSend = enableSnapSend ? false : true
155 backEnd.SwitchEnableSnapSend(checked)
156 }
157 }
158
159 Button
160 {
161 objectName: "btnSnapSend"
162 id: btnSnapSend
163 implicitWidth: btnLoadDevice.width
164 enabled: !btnStartServer.m_server_disabled && enableSnapSend
165 text: qsTr("Snap Send")
166 onClicked: backEnd.BtnSnapSend()
167 }
168 }
169
170 Switch
171 {
172 objectName: "switchGrab"
173 id: switchGrab
174 text: qsTr("Grab")
175 checked: false
176 enabled: btnStartServer.m_server_disabled
177 onCheckedChanged: backEnd.SwitchGrab(checked)
178 }
179 }
180}
1#include "backend.hpp"
2
3#include "QDebug"
4
5#include <cvb/data_type.hpp>
6#include <cvb/gevserver/node.hpp>
7#include <cvb/gevserver/node_map.hpp>
8#include <cvb/gevserver/server.hpp>
9#include <cvb/gevserver/stream.hpp>
10#include <cvb/size_2d.hpp>
11
12#include <cvb/device_factory.hpp>
13#include <cvb/image.hpp>
14#include <cvb/ui/image_view_item.hpp>
15
16BackEnd::BackEnd(QQuickView *view, QObject *parent) : QObject(parent), view_{view}
17{
18 // Register view to queue commands over threads
19 qRegisterMetaType<QWindow::Visibility>();
20
21
22 // open a device
23 device_ = Cvb::DeviceFactory::Open(Cvb::InstallPath() + CVB_LIT("drivers/CVMock.vin"));
24
25 hasRingBuffer_ = !!device_->Stream()->RingBuffer();
26 if (hasRingBuffer_)
27 device_->Stream()->RingBuffer()->SetLockMode(Cvb::Driver::RingBufferLockMode::On);
28
29 streamHandler_->Create(device_->Stream());
30
31 context_ = view->rootContext();
32 context_->setContextProperty("mainImage", &imageController_);
33 device_->Stream()->GetSnapshot();
34
35 imageController_.Refresh(device_->DeviceImage(), Cvb::UI::AutoRefresh::On);
36}
37
38BackEnd::~BackEnd()
39{
40 if (acqThread_)
41 StopAcq();
42 if (server_ != Cvb::GevServer::ServerPtr() && server_->State() != Cvb::GevServer::State::Configuration)
43 server_->Stop();
44 if (device_->Stream()->IsRunning())
45 device_->Stream()->Stop();
46}
47
48void BackEnd::StartServer(const int &addressIndex, const int &driverTypeIndex)
49{
50 qDebug() << "Starting server on address index " << addressIndex << " and driver type index " << driverTypeIndex;
51
52 // Check for recursions
53 if (startInProgress_)
54 return;
55
56 startInProgress_ = true;
57
58 try
59 {
60 if (!acqStarted_)
61 {
62 // Stop and reset grab button if running
63 StopGrab();
64
65 // Create server
66 server_ = Cvb::GevServer::Server::CreateWithConstSize(device_->DeviceImage()->Size(),
67 device_->DeviceImage()->ColorModel(),
68 device_->DeviceImage()->Plane(0).DataType(),
69 static_cast<Cvb::GevServer::DriverType>(driverTypeIndex));
70
71 AddGenICamFeatures();
72
73 // Set custom version/id
74 server_->SetUserVersion(CVB_LIT("_C++GevServer"));
75 server_->NodeMap()->SetXmlFileVersion(Cvb::GenApi::NodeMap::GenApiVersion(1, 0, 0));
76
77 if (hasRingBuffer_)
78 server_->Stream()->SetResendBuffersCount(2);
79 else
80 server_->Stream()->SetResendBuffersCount(0);
81
82 // Start server
83 auto address = iFaces_.at(addressIndex).IPAddress();
84 server_->Start(address);
85
86 // Start acquisition thread
87 if (!acqThread_)
88 StartAcq();
89
90 acqStarted_ = true;
91 qDebug() << "Started server";
92 }
93 else
94 {
95 if (acqThread_)
96 StopAcq();
97 server_->Stop();
98 acqStarted_ = false;
99 qDebug() << "Stopped server";
100 }
101 }
102 catch (...)
103 {
104 }
105 startInProgress_ = false;
106}
107
108void BackEnd::operator()() noexcept
109{
111 device_->Stream()->Start();
112 while (!stopAcq_)
113 {
114 if (grabSwitchSwitched_)
115 {
116 if (hasRingBuffer_)
117 {
118 auto img = device_->Stream()->Wait<Cvb::RingBufferImage>();
119 img.Image->Unlock();
120 }
121 else
122 device_->Stream()->Wait();
123 }
124 else if (server_ != Cvb::GevServer::ServerPtr() && server_->State() == Cvb::GevServer::State::AcquisitionEnabled &&
125 !snapSend_)
126 {
127 try
128 {
129 auto img = device_->Stream()->Wait<Cvb::RingBufferImage>();
130 if (img.Status == Cvb::WaitStatus::Ok)
131 {
132 if (hasRingBuffer_)
133 {
134 bufferIndex_ = img.Image->BufferIndex();
135 imgMap_[bufferIndex_] = img.Image;
136 server_->Stream()->Send(img.Image, [this](const Cvb::ImagePtr &) {
137 imgMap_[bufferIndex_]->Unlock();
138 qDebug() << "unlocked buffer index " << std::to_string(bufferIndex_).c_str();
139 });
140 }
141 else
142 server_->Stream()->Send(*img.Image.get());
143 }
144 }
145 catch (...)
146 {
147 }
148 }
149 }
150 device_->Stream()->Stop();
151}
152
153void BackEnd::SwitchGrab(const bool &switched)
154{
155 grabSwitchSwitched_ = switched;
156
157 if (grabSwitchSwitched_)
158 {
159 if (!acqThread_)
160 StartAcq();
161 }
162 else
163 {
164 if (acqThread_)
165 StopAcq();
166 }
167 qDebug() << "Grab switch switched to" << switched;
168}
169
170QVariantList BackEnd::ConnectionList()
171{
172 iFaces_.clear();
174
175 QVariantList list;
176 list.clear();
177
178 for (auto const &iFace : iFaces_)
179 {
181 ss << iFace.IPAddress() << " (" << iFace.IPv4Mask() << ")";
182 list.append(std::string(ss.str()).c_str());
183 }
184
185 return list;
186}
187
188void BackEnd::LoadDevice(const QString &path)
189{
190 qDebug() << "Loading new device on path " << path;
191 StopGrab();
192 try
193 {
194 auto tmpDevice = Cvb::DeviceFactory::Open(CVB_LIT(path.toLatin1().toStdString().c_str()));
195 device_.reset();
196 device_ = tmpDevice;
197 device_->Stream()->GetSnapshot();
198
199 hasRingBuffer_ = !!device_->Stream()->RingBuffer();
200 if (hasRingBuffer_)
201 device_->Stream()->RingBuffer()->SetLockMode(Cvb::Driver::RingBufferLockMode::On);
202 imageController_.Refresh(device_->DeviceImage(), Cvb::UI::AutoRefresh::On);
203 }
204 catch (...)
205 {
206 qDebug() << "Failed to load new device";
207 }
208}
209
210void BackEnd::SaveImg(const QString &path)
211{
212 qDebug() << "Saving new file on path " << path;
213 device_->DeviceImage()->Save(CVB_LIT(path.toLatin1().toStdString().c_str()));
214}
215
216void BackEnd::BtnSnap()
217{
218 qDebug() << "Snap image";
219 StopGrab();
220 // Snap a single image
221 device_->Stream()->GetSnapshot();
222}
223
224void BackEnd::SwitchEnableSnapSend(const bool &switched)
225{
226 snapSend_ = switched;
227
228 if (snapSend_)
229 StopAcq();
230 else
231 StartAcq();
232
233 qDebug() << "Enable snap switch switched to" << switched;
234}
235
236void BackEnd::BtnSnapSend()
237{
238 if (server_->State() == Cvb::GevServer::State::AcquisitionEnabled)
239 {
240 qDebug() << "Snap send";
241 try
242 {
243 auto img = device_->Stream()->GetSnapshot();
244 if (img.Status == Cvb::WaitStatus::Ok)
245 if (hasRingBuffer_)
246 {
247 server_->Stream()->Send(img.Image, nullptr);
248 }
249 else
250 {
251 auto img2 = device_->Stream()->GetSnapshot();
252 if (img2.Status == Cvb::WaitStatus::Ok)
253 server_->Stream()->Send(*img2.Image);
254 }
255 }
256 catch (...)
257 {
258 }
259 }
260 else
261 qDebug() << "Acquisition not enabled on server";
262}
263
264void BackEnd::StopGrab()
265{
266 // If grab is currently running, stop it
267 if (device_->Stream()->IsRunning() && grabSwitchSwitched_)
268 {
269 QObject *box = view_->findChild<QObject *>("switchGrab", Qt::FindChildrenRecursively);
270 if (box)
271 box->setProperty("checked", false);
272 }
273 if (device_->Stream()->IsRunning() && acqStarted_)
274 {
275 StopAcq();
276 }
277}
278
279void BackEnd::OnWindowSizeChanged(const Cvb::GevServer::ValueNode & /*value*/)
280{
281 switch (windowStateRegNode_->Value())
282 {
283 case 0:
284 view_->showMinimized();
285 break;
286 case 1:
287 view_->showNormal();
288 break;
289 case 2:
290 view_->showMaximized();
291 default:
292 throw std::runtime_error("Invalid window state value");
293 break;
294 }
295}
296
297void BackEnd::AddGenICamFeatures()
298{
299 auto catNode = Cvb::GevServer::CategoryNode::Create(CVB_LIT("Cust::CustomFeatures"));
300 server_->NodeMap()->AddNode(catNode);
301 catNode->SetDisplayName(CVB_LIT("Custom Features"));
302 catNode->SetToolTip(CVB_LIT("Contains all application defined features."));
303 auto rootNode = server_->NodeMap()->Node(CVB_LIT("Root"));
304 rootNode->Add(catNode, Cvb::GevServer::NodeList::Child);
305
306 windowStateRegNode_ = Cvb::GevServer::Int32RegNode::Create(CVB_LIT("Cust::WindowStateReg"));
307 server_->NodeMap()->AddNode(windowStateRegNode_);
308 windowStateRegNode_->SetVisibility(Cvb::GenApi::Visibility::Invisible);
309 // No cache because of polling
310 windowStateRegNode_->SetCacheMode(Cvb::GenApi::CacheMode::NoCache);
311 // Polling time in ms
312 windowStateRegNode_->SetPollingTime<float, std::ratio<1, 1000>>(
314 windowStateRegNode_->SetValue(1); // View in normal mode
315 windowStateRegNode_->RegisterEventWrittenUpdated(
316 [this](const Cvb::GevServer::ValueNode &value) { OnWindowSizeChanged(value); });
317
318 auto enumerationNode = Cvb::GevServer::EnumerationNode::Create(CVB_LIT("Cust::WindowState"));
319 server_->NodeMap()->AddNode(enumerationNode);
320 enumerationNode->SetDisplayName(CVB_LIT("Window State"));
321 enumerationNode->SetToolTip(CVB_LIT("Current window state of server application."));
322 enumerationNode->SetValueConfig<Cvb::GevServer::IntegerBaseNodePtr>(windowStateRegNode_);
323
324 auto minimized = Cvb::GevServer::EnumEntryNode::Create(CVB_LIT("Cust::Minimized"));
325 minimized->SetNumericValue(0);
326 enumerationNode->Add(minimized, Cvb::GevServer::NodeList::Child);
327
328 auto normalNode = Cvb::GevServer::EnumEntryNode::Create(CVB_LIT("Cust::Normal"));
329 normalNode->SetNumericValue(1); // Must be ascending entry in vector
330 enumerationNode->Add(normalNode, Cvb::GevServer::NodeList::Child);
331
332 auto maxNode = Cvb::GevServer::EnumEntryNode::Create(CVB_LIT("Cust::Maximized"));
333 maxNode->SetNumericValue(2); // Must be ascending entry in vector
334 enumerationNode->Add(maxNode, Cvb::GevServer::NodeList::Child);
335
336 catNode->Add(enumerationNode, Cvb::GevServer::NodeList::Child);
337}
static std::shared_ptr< T > Open(const String &provider, AcquisitionStack acquisitionStack=AcquisitionStack::PreferVin)
Opens a device with the given provider with its default board and port (if applicable).
Definition: decl_device_factory.hpp:50
Stream image that is returned, when the ring buffer interface is available on a device.
Definition: decl_ring_buffer_image.hpp:29
Version information for GenICam related objects.
Definition: decl_node_map.hpp:59
static CategoryNodePtr Create(const String &name, const GevServer::Namespace &nameSpace)
Creates a new CategoryNode with the given name and nameSpace .
Definition: category_node.hpp:38
static EnumEntryNodePtr Create(const String &name, const GevServer::Namespace &nameSpace)
Creates a new EnumEntryNode with the given name and nameSpace .
Definition: enum_entry_node.hpp:40
static EnumerationNodePtr Create(const String &name, const GevServer::Namespace &nameSpace)
Creates a new EnumerationNode with the given name and nameSpace .
Definition: enumeration_node.hpp:41
static Int32RegNodePtr Create(const String &name, const GevServer::Namespace &nameSpace, const std::int64_t &address)
Creates a new int_32_reg_node with the given name and nameSpace .
Definition: int_32_reg_node.hpp:34
static ServerPtr CreateWithConstSize(Size2D< int > size, PfncFormat pixelFormat, GevServer::DriverType driverType=GevServer::DriverType::Auto)
Creates a new Server object with a constant width and height.
Definition: detail_server.hpp:37
Base class for all nodes that have a value.
Definition: value_node.hpp:32
@ Invisible
Node should not be displayed.
@ NoCache
No caching used.
DriverType
GigE Vision driver to use for communication and streaming.
Definition: gevserver.hpp:131
@ Ok
Everything is fine, a new image arrived.
std::string toStdString() const const
T findChild(const QString &name, Qt::FindChildOptions options) const const
bool setProperty(const char *name, const QVariant &value)
QByteArray toLatin1() const const
static std::vector< LogicalNetworkInterface > GetAllAvailable()
Gets all available network interfaces usable by the GevServer.
Definition: logical_network_interface.hpp:38
1#ifndef BACKEND_H
2#define BACKEND_H
3
4#include "QVariantList"
5#include <QObject>
6#include <QQmlContext>
7#include <QQuickView>
8#include <QString>
9
10#include <thread>
11
12#include <cvb/async/single_stream_handler.hpp>
13#include <cvb/gevserver/logical_network_interface.hpp>
14#include <cvb/ui/image_view_item.hpp>
15
16class BackEnd : public QObject
17{
18 // This macro is required for QObject support. You should get a compiler
19 // error if you don't include this.
20 Q_OBJECT
21
22public:
23 // QObjects are expected to support a parent/child hierarchy
24 explicit BackEnd(QQuickView *view, QObject *parent = 0);
25
26 ~BackEnd();
27
28 void OnWindowSizeChanged(const Cvb::GevServer::ValueNode &value);
29
30 void StartAcq()
31 {
33
34 stopAcq_ = false;
35 acqThread_.reset(new std::thread(std::ref(*this)));
36 }
37
38 void StopAcq()
39 {
40 stopAcq_ = true;
41 if (acqThread_)
42 {
43 acqThread_->join();
44 acqThread_.reset();
45 }
46 }
47
48 // Acquisition thread
49 void operator()() noexcept;
50
51signals:
52
53public slots:
54 void StartServer(const int &addressIndex, const int &driverTypeIndex);
55
56 void LoadDevice(const QString &path);
57
58 void SaveImg(const QString &path);
59
60 void BtnSnap();
61
62 void BtnSnapSend();
63
64 QVariantList ConnectionList();
65
66 void SwitchGrab(const bool &switched);
67
68 void SwitchEnableSnapSend(const bool &switched);
69
70private:
71 void StopGrab();
72
73 void AddGenICamFeatures();
74
75 QQuickView *view_;
76 QQmlContext *context_;
77 // Create a controller object to communicate with QML
78 Cvb::UI::ImageController imageController_;
79
80 Cvb::EventCookie eventCookie_;
81 // Hold this node for callback
82 Cvb::GevServer::Int32RegNodePtr windowStateRegNode_;
83
84 bool startInProgress_{false};
85 bool grabSwitchSwitched_{false};
86 bool acqStarted_{false};
87 bool snapSend_{false};
88 bool hasRingBuffer_{false};
89 int bufferIndex_;
90
94 Cvb::DevicePtr device_;
95 Cvb::SingleStreamHandlerPtr streamHandler_;
96
97 std::atomic<bool> stopAcq_;
99 std::mutex mtx_;
100};
101
102#endif
Controller object for the QML image view item.
Definition: image_view_item.hpp:254