.
Global variables can be used to get data from a HDevelop procedure while it is running in the HDevEngine. In this article, we explain how this can be done in a C++ project on Windows. The example creates a new C++ console application from scratch and shows how global variables can be used to show log messages from a HDevelop procedure running in the background.
For this project I used Microsoft Visual Studio 2017 Community Edition.
Start Visual Studio, and go to File / New project / Visual C++ / Win32 Console Application
Name: e.g. HDevEngineGlobVar
We assume HALCON 13 is your default installation. If not, you can change the default HALCON version with our tool Start Center for HALCON.
In Solution Explorer, right click on the project "HDevEngineGlobVar" and select Properties in the context menu. Make sure you have selected the same platform in this dialog (e.g. x64) as in the run configuration (as seen in the Visual Studio main window toolbar).
Then in "Configuration Properties / C/C++ / General" set: Additional Include Directories: $(HALCONROOT)\include
In "Configuration Properties / Linker / General" set: Additional Library Directories: $(HALCONROOT)\lib\x64-win64
(or $(HALCONROOT)\lib\x86sse2-win32
if you installed HALCON 13 for 32 bit)
In "Configuration Properties / Linker / Input" add:
Additional Dependencies: halcon.lib
, halconcpp.lib
, hdevenginecpp.lib
Start HDevelop, create an external procedure, e.g. TestProc.hdvp:
** TestProc will run for 5 seconds.
** While running, it will add log messages to GlobalLog
** and dummy data to GlobalTestIconic
** globals
global object GlobalTestIconic
global tuple GlobalLog
GlobalLog := [GlobalLog, 'entered TestProc']
gen_random_region (RegionRandom, 128, 128)
count_seconds (StartSecs)
while (true)
count_seconds (NowSecs)
Ticks := int(NowSecs - StartSecs)
GlobalLog := [GlobalLog, 'Ticks is now: ' + Ticks]
* example for modification of iconic global variable only
concat_obj (GlobalTestIconic, RegionRandom, GlobalTestIconic)
if (Ticks > 5)
GlobalLog := [GlobalLog, 'Ticks > 5, exit while loop']
break
endif
wait_seconds (1)
endwhile
GlobalLog := [GlobalLog, 'finished TestProc']
Result := 42
return ()
We recommend to not use the precompiled headers option: Remove the stdfx.h, stdfx.cpp, and * targetver.h* files from your project In the project properties, go to C/C++ / Precompiled Headers and set Precompiled Header to Not Using Precompiled Headers.
We use the following C++ program. Edit your project's only cpp file HDevEngineGlobVar.cpp accordingly:
#include "halconcpp/HalconCpp.h"
#include "hdevengine/HDevEngineCpp.h"
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
// allow static const initialization of empty object
struct HEmptyObjectClass {
HEmptyObjectClass() { HalconCpp::GenEmptyObj(&hobj); }
operator HalconCpp::HObject() const { return hobj; }
private:
HalconCpp::HObject hobj;
};
static const HEmptyObjectClass EmptyObject;
HDevEngineCpp::HDevEngine engine;
void TestHDevEngineGlobVar() {
using namespace std::chrono_literals;
// will become true when testproc_thread has finished
std::atomic<bool> finished(false);
// load external procedure TestProc
// TestProc will define global variables GlobalLog and
// GlobalTestIconic in HDevEngine
HDevEngineCpp::HDevProcedure proc("TestProc");
engine.SetGlobalCtrlVarTuple("GlobalLog", HalconCpp::HTuple());
engine.SetGlobalIconicVarObject("GlobalTestIconic", EmptyObject);
// run HDevelop procedure TestProc in background thread
std::thread testproc_thread([&proc, &finished] {
try {
HDevEngineCpp::HDevProcedureCall proccall(proc);
proccall.Execute();
auto result = proccall.GetOutputCtrlParamTuple("Result");
std::cout << "Result is " << result.ToString() << std::endl;
finished = true;
}
catch (HDevEngineCpp::HDevEngineException& e) {
auto msg = e.Message();
std::cerr << "HDevEngineException in HDevEngine thread:" << msg << '\n';
}
});
// while TestProc thread is running, show log messages from the
// global variable GlobalLog. GlobalLog is updated in TestProc procedure.
Hlong index_log = 0;
while (!finished) {
auto global_log = engine.GetGlobalCtrlVarTuple("GlobalLog");
// print only GlobalLog entries which had not been printed in last iteration
for (Hlong length = global_log.Length(); index_log < length; ++index_log) {
std::cout << HalconCpp::HTuple(global_log[index_log]).ToString() << std::endl;
}
auto global_test_iconic = engine.GetGlobalIconicVarObject("GlobalTestIconic");
std::cout << "number of objects in GlobalTestIconic: "
<< global_test_iconic.CountObj() << std::endl;
std::this_thread::sleep_for(200ms);
}
testproc_thread.join();
// continue here after TestProc thread and waiting main thread have finished
// ...
}
int main()
{
try {
// TODO: Adapt the following path
engine.AddProcedurePath(R"(P:\project\globvar\procedures)");
TestHDevEngineGlobVar();
}
catch (HDevEngineCpp::HDevEngineException& e) {
auto msg = e.Message();
std::cerr << "HDevEngineException in main thread:" << msg << '\n';
}
do {
std::cout << '\n' << "Press enter to quit.";
} while (std::cin.get() != '\n');
return 0;
}
Note that HDevEngine global variables can only be created by the HDevEngine via scripting code in the loaded scripts or procedures. As far as I know, there is no way to create globals (e.g. before loading single procedures) from C++ code.
The C++ class HEmptyObjectClass and the const static initialized EmptyObject is just a fancy way to make sure that an initialized HALCON empty object is available without having to write code like the following everywhere:
HalconCpp::HObject empty_object;
empty_object.GenEmptyObj();
engine.SetGlobalIconicVarObject("GlobalTestIconic", empty_object);
When the program is run, it runs the HDevelop procedure TestProc in the background, while showing the log output in the main thread. When the TestProc finishes execution, the main thread will continue and in this example the program ends.
Example output:
number of objects in GlobalTestIconic: 0
"entered TestProc"
"Ticks is now: 0"
number of objects in GlobalTestIconic: 1
number of objects in GlobalTestIconic: 1
number of objects in GlobalTestIconic: 1
number of objects in GlobalTestIconic: 1
"Ticks is now: 1"
number of objects in GlobalTestIconic: 2
number of objects in GlobalTestIconic: 2
number of objects in GlobalTestIconic: 2
number of objects in GlobalTestIconic: 2
number of objects in GlobalTestIconic: 2
"Ticks is now: 2"
number of objects in GlobalTestIconic: 3
number of objects in GlobalTestIconic: 3
number of objects in GlobalTestIconic: 3
number of objects in GlobalTestIconic: 3
number of objects in GlobalTestIconic: 3
"Ticks is now: 3"
number of objects in GlobalTestIconic: 4
number of objects in GlobalTestIconic: 4
number of objects in GlobalTestIconic: 4
number of objects in GlobalTestIconic: 4
number of objects in GlobalTestIconic: 4
"Ticks is now: 4"
number of objects in GlobalTestIconic: 5
number of objects in GlobalTestIconic: 5
number of objects in GlobalTestIconic: 5
number of objects in GlobalTestIconic: 5
number of objects in GlobalTestIconic: 5
"Ticks is now: 5"
number of objects in GlobalTestIconic: 6
number of objects in GlobalTestIconic: 6
number of objects in GlobalTestIconic: 6
number of objects in GlobalTestIconic: 6
number of objects in GlobalTestIconic: 6
Result is 42
Press enter to quit.
If you have any questions regarding this topic or and other HALCON related questions please do not hesitate to contact us.