Using global variables in the HDevEngine

2018-02-14 by Andreas Heindl



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.

Start Microsoft Visual Studio and create new C++ project

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

Setup project to use HALCON 13

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

Create HDevelop procedure

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 ()

Write example C++ code

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;
}

Some explanations

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.

Downloads

HDevEngineGlobVar.cpp

TestProc.hdvp

Questions?

If you have any questions regarding this topic or and other HALCON related questions please do not hesitate to contact us.