Files
RCiRoot e3f7733316 V20260829-2.6.1
完成框架目录和模块重构;新增核心、单例、工具、加密及框架 SDK;统一任务、相机和 RPC 接口,并增加离线检测、相机标定、备份恢复、版本管理、崩溃守护和心跳监控功能。
2026-09-20 16:01:09 +08:00

762 lines
25 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include <HalconCpp.h>
#include <HDevEngineCpp.h>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <QDebug>
#include <QReadLocker>
#include <QReadWriteLock>
#include <QString>
#include <QWriteLocker>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cstring>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
class IHalconExecutionGuard
{
public:
virtual ~IHalconExecutionGuard() = default;
};
enum class HalconExecutionTracePhase
{
Started,
Finished,
Failed
};
class IHalconEvaluaterRuntime
{
public:
using reloadCallback = std::function<std::pair<bool, std::string>()>;
virtual ~IHalconEvaluaterRuntime() = default;
virtual void loadAlgoFile(const std::string& filePath) = 0;
virtual bool isInited() const = 0;
virtual bool isReleasing() const = 0;
virtual std::unique_ptr<IHalconExecutionGuard> acquireExecutionGuard() = 0;
virtual int registerReloadCallback(reloadCallback callback) = 0;
virtual void unregisterReloadCallback(int id) = 0;
virtual void submitTime(const std::string& procedureName, double elapsed) = 0;
virtual void recordExecutionTrace(
const std::string& procedureName,
HalconExecutionTracePhase phase)
{
Q_UNUSED(procedureName);
Q_UNUSED(phase);
}
virtual void registerTimeCostViewer(const QString& groupName) = 0;
virtual void reportEvaluaterError(const std::string& message) = 0;
};
class halconHTupleVectorMap
{
public:
HalconCpp::HTupleVector& emplace(
const std::string& key,
const HalconCpp::HTupleVector& value = HalconCpp::HTupleVector{ 0 })
{
auto [it, inserted] = values_.emplace(key, value);
Q_UNUSED(inserted);
return it->second;
}
HalconCpp::HTupleVector& operator[](const std::string& key)
{
auto [it, inserted] = values_.emplace(key, HalconCpp::HTupleVector{ 0 });
Q_UNUSED(inserted);
return it->second;
}
bool contains(const std::string& key) const
{
return values_.find(key) != values_.end();
}
auto begin() { return values_.begin(); }
auto end() { return values_.end(); }
auto begin() const { return values_.begin(); }
auto end() const { return values_.end(); }
private:
std::map<std::string, HalconCpp::HTupleVector> values_;
};
// RAII 模式的 HObject 管理类,负责在析构时自动释放资源,防止内存泄漏。
class HObjectHolder
{
public:
HObjectHolder() = default;
explicit HObjectHolder(const cv::Mat& image) { reset(image); }
explicit HObjectHolder(const HalconCpp::HObject& object) { reset(object); }
HObjectHolder(const HObjectHolder& other)
{
if (other.valid_) {
object_ = other.object_;
valid_ = true;
}
}
HObjectHolder& operator=(const HObjectHolder& other)
{
if (this == &other) {
return *this;
}
if (other.valid_) {
clear();
object_ = other.object_;
valid_ = true;
}
else {
clear();
}
return *this;
}
HObjectHolder(HObjectHolder&& other) noexcept
: object_(std::move(other.object_))
, valid_(other.valid_)
{
other.valid_ = false;
}
HObjectHolder& operator=(HObjectHolder&& other) noexcept
{
if (this != &other) {
clear();
object_ = std::move(other.object_);
valid_ = other.valid_;
other.valid_ = false;
}
return *this;
}
HObjectHolder& operator=(const HalconCpp::HObject& object)
{
reset(object);
return *this;
}
~HObjectHolder() { clear(); }
void reset(const HalconCpp::HObject& object)
{
clear();
object_ = object;
valid_ = true;
}
void reset(const cv::Mat& image)
{
clear();
if (image.empty()) {
return;
}
const cv::Mat continuousImage = image.isContinuous() ? image : image.clone();
const int width = continuousImage.cols;
const int height = continuousImage.rows;
if (continuousImage.type() == CV_8UC1) {
HalconCpp::GenImage1(&object_, "byte", width, height,
reinterpret_cast<Hlong>(continuousImage.data));
}
else if (continuousImage.type() == CV_8UC3) {
std::vector<cv::Mat> channels;
cv::split(continuousImage, channels);
HalconCpp::GenImage3(&object_, "byte", width, height,
reinterpret_cast<Hlong>(channels[2].data),
reinterpret_cast<Hlong>(channels[1].data),
reinterpret_cast<Hlong>(channels[0].data));
}
else if (continuousImage.type() == CV_16UC1) {
HalconCpp::GenImage1(&object_, "uint2", width, height,
reinterpret_cast<Hlong>(continuousImage.data));
}
else if (continuousImage.type() == CV_16SC1) {
HalconCpp::GenImage1(&object_, "int2", width, height,
reinterpret_cast<Hlong>(continuousImage.data));
}
else {
throw std::invalid_argument(
"HObjectHolder only supports CV_8UC1, CV_8UC3, CV_16UC1 and CV_16SC1");
}
valid_ = true;
}
HObjectHolder clone() const
{
HObjectHolder result;
copyTo(result);
return result;
}
void copyTo(HObjectHolder& destination) const
{
if (!valid_) {
destination.clear();
return;
}
destination.clear();
destination.object_ = object_.Clone();
destination.valid_ = true;
}
cv::Mat toMat() const
{
if (!valid_) {
return {};
}
try {
HalconCpp::HTuple channelCount;
HalconCpp::CountChannels(object_, &channelCount);
Hlong width = 0;
Hlong height = 0;
HalconCpp::HString type;
HalconCpp::HImage image(object_);
if (channelCount[0].I() == 1) {
void* source = image.GetImagePointer1(&type, &width, &height);
const int matType = halconImageTypeToCvType(type.Text(), 1);
if (matType < 0) {
return {};
}
cv::Mat result(static_cast<int>(height), static_cast<int>(width), matType);
std::memcpy(result.data, source,
static_cast<std::size_t>(width * height) * CV_ELEM_SIZE(matType));
return result;
}
if (channelCount[0].I() == 3) {
void* red = nullptr;
void* green = nullptr;
void* blue = nullptr;
image.GetImagePointer3(&red, &green, &blue, &type, &width, &height);
const int channelType = halconImageTypeToCvType(type.Text(), 1);
const int matType = halconImageTypeToCvType(type.Text(), 3);
if (channelType < 0 || matType < 0) {
return {};
}
std::vector<cv::Mat> channels(3);
for (auto& channel : channels) {
channel.create(static_cast<int>(height), static_cast<int>(width), channelType);
}
const auto bytes = static_cast<std::size_t>(width * height)
* CV_ELEM_SIZE(channelType);
std::memcpy(channels[2].data, red, bytes);
std::memcpy(channels[1].data, green, bytes);
std::memcpy(channels[0].data, blue, bytes);
cv::Mat result;
cv::merge(channels, result);
return result;
}
}
catch (const HalconCpp::HException& exception) {
qWarning() << "HObjectHolder::toMat HALCON exception:"
<< exception.ErrorMessage().Text();
}
catch (const std::exception& exception) {
qWarning() << "HObjectHolder::toMat exception:" << exception.what();
}
catch (...) {
qWarning() << "HObjectHolder::toMat unknown exception";
}
return {};
}
void clear()
{
if (!valid_) {
return;
}
try {
object_.Clear();
}
catch (...) {
}
valid_ = false;
}
bool empty() const { return !valid_; }
HalconCpp::HObject& hObject() { return object_; }
const HalconCpp::HObject& hObject() const { return object_; }
operator HalconCpp::HObject&() { return object_; }
operator const HalconCpp::HObject&() const { return object_; }
private:
static int halconImageTypeToCvType(const char* type, int channels)
{
const std::string typeName = type == nullptr ? "" : type;
if (typeName == "byte") {
return CV_MAKETYPE(CV_8U, channels);
}
if (typeName == "uint2") {
return CV_MAKETYPE(CV_16U, channels);
}
if (typeName == "int2") {
return CV_MAKETYPE(CV_16S, channels);
}
return -1;
}
HalconCpp::HObject object_;
bool valid_ = false;
};
inline HObjectHolder MatToHObject(const cv::Mat& image)
{
return HObjectHolder(image);
}
inline int halconPhysicalCoreCount()
{
DWORD bufferSize = 0;
if (GetLogicalProcessorInformationEx(
RelationProcessorCore, nullptr, &bufferSize)
|| GetLastError() != ERROR_INSUFFICIENT_BUFFER
|| bufferSize == 0) {
return 1;
}
std::vector<unsigned char> buffer(bufferSize);
DWORD returnedSize = bufferSize;
if (!GetLogicalProcessorInformationEx(
RelationProcessorCore,
reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX>(buffer.data()),
&returnedSize)) {
return 1;
}
int coreCount = 0;
for (DWORD offset = 0; offset < returnedSize;) {
const auto* information = reinterpret_cast<
const SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*>(buffer.data() + offset);
if (information->Relationship == RelationProcessorCore) {
++coreCount;
}
if (information->Size == 0 || information->Size > returnedSize - offset) {
break;
}
offset += information->Size;
}
return coreCount > 0 ? coreCount : 1;
}
inline int halconLogicalCoreCount()
{
const DWORD logicalCount = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
return logicalCount > 0 ? static_cast<int>(logicalCount) : 1;
}
// 优化 HALCON 线程配置:thread_num = 系统逻辑核心数 - 2;
// tsp_thread_num = (系统逻辑核心数 - 2) / 最大并发数量;设置成功后打印 HALCON 中实际生效的值。
static bool halconEngineOptimize(int maxConcurrency)
{
try {
if (maxConcurrency <= 0) {
maxConcurrency = 1;
}
const int threadNum = std::max(1, halconLogicalCoreCount() - 2);
const int tspThreadNum = std::max(1, threadNum / maxConcurrency);
HalconCpp::HSystem::SetSystem(
HalconCpp::HTuple("thread_num"), HalconCpp::HTuple(threadNum));
HalconCpp::HSystem::SetSystem(
HalconCpp::HTuple("tsp_thread_num"), HalconCpp::HTuple(tspThreadNum));
const int actualThreadNum = HalconCpp::HSystem::GetSystem("thread_num").I();
const int actualTspThreadNum = HalconCpp::HSystem::GetSystem("tsp_thread_num").I();
qInfo() << "[halconEngineOptimize] maxConcurrency =" << maxConcurrency
<< ", thread_num =" << actualThreadNum
<< ", tsp_thread_num =" << actualTspThreadNum;
return true;
}
catch (const HalconCpp::HException& exception) {
qWarning() << "[halconEngineOptimize] failed:"
<< QString::fromStdString(exception.ErrorMessage().Text());
return false;
}
catch (const std::exception& exception) {
qWarning() << "[halconEngineOptimize] failed:" << exception.what();
return false;
}
catch (...) {
qWarning() << "[halconEngineOptimize] failed: unknown exception";
return false;
}
}
// 输入输出参数通过 callback 映射,初始化和算法文件重载时校验 procedure 参数。
template <typename Input, typename Output>
class halconEvaluater
{
public:
using inputCallback = std::function<void(
std::map<std::string, HObjectHolder>&,
std::map<std::string, HalconCpp::HTuple>&,
const Input&)>;
using outputCallback = std::function<void(
std::map<std::string, HObjectHolder>&,
std::map<std::string, HalconCpp::HTuple>&,
std::map<std::string, std::vector<HalconCpp::HTuple>>&,
Output&)>;
using failedCallback = std::function<void(
std::map<std::string, HObjectHolder>&,
std::map<std::string, HalconCpp::HTuple>&,
std::map<std::string, std::vector<HalconCpp::HTuple>>&)>;
struct initResult
{
bool result = true;
std::string failedReason;
};
struct halconResult
{
Output result;
double costTime = 0.0;
};
halconEvaluater() = default;
halconEvaluater(const halconEvaluater&) = delete;
halconEvaluater& operator=(const halconEvaluater&) = delete;
~halconEvaluater()
{
QWriteLocker stateLocker(&stateLock_);
if (runtime_ != nullptr && !runtime_->isReleasing() && reloadCallbackId_ >= 0) {
runtime_->unregisterReloadCallback(reloadCallbackId_);
}
reloadCallbackId_ = -1;
runtime_ = nullptr;
}
initResult init(
const std::string& procedureName,
inputCallback input,
outputCallback output,
failedCallback failed,
const std::string& algoFilePath,
IHalconEvaluaterRuntime* runtime)
{
QWriteLocker stateLocker(&stateLock_);
if (initialized_) {
return { false, "重复初始化" };
}
if (runtime == nullptr || runtime->isReleasing()) {
return { false, "halconEngineManager is not available" };
}
runtime_ = runtime;
procedureName_ = procedureName;
inputCallback_ = std::move(input);
outputCallback_ = std::move(output);
failedCallback_ = std::move(failed);
collectParameterNames();
if (!algoFilePath.empty() && !runtime_->isInited()) {
runtime_->loadAlgoFile(algoFilePath);
}
initialized_ = true;
if (runtime_->isInited()) {
auto executionGuard = runtime_->acquireExecutionGuard();
if (!executionGuard) {
initialized_ = false;
return { false, "halconEngineManager is releasing" };
}
const auto selfCheckResult = onSelfCheck();
if (!selfCheckResult.first) {
initialized_ = false;
runtime_->reportEvaluaterError(selfCheckResult.second);
return { false, selfCheckResult.second };
}
}
reloadCallbackId_ = runtime_->registerReloadCallback([this]() {
QWriteLocker reloadLocker(&stateLock_);
initialized_ = false;
const auto result = onSelfCheck();
initialized_ = result.first;
if (!result.first && runtime_ != nullptr) {
runtime_->reportEvaluaterError(result.second);
}
return result;
});
runtime_->registerTimeCostViewer(QString::fromStdString(procedureName_));
return { true, "" };
}
halconResult evaluate(const Input& input)
{
IHalconEvaluaterRuntime* runtime = nullptr;
{
QReadLocker stateLocker(&stateLock_);
runtime = runtime_;
}
if (runtime == nullptr || runtime->isReleasing() || !runtime->isInited()) {
return makeFailedResult("halconEngineManager is not available");
}
auto executionGuard = runtime->acquireExecutionGuard();
if (!executionGuard) {
return makeFailedResult("halconEngineManager is releasing");
}
QReadLocker stateLocker(&stateLock_);
if (runtime != runtime_ || !initialized_) {
return makeFailedResultLocked("函数未初始化或重载后自检失败");
}
try {
std::map<std::string, HObjectHolder> iconicInputs;
std::map<std::string, HalconCpp::HTuple> controlInputs;
inputCallback_(iconicInputs, controlInputs, input);
HDevEngineCpp::HDevProcedure procedure(
const_cast<char*>(procedureName_.c_str()));
HDevEngineCpp::HDevProcedureCall call(procedure);
for (const auto& value : iconicInputs) {
call.SetInputIconicParamObject(value.first.c_str(), value.second.hObject());
}
for (const auto& value : controlInputs) {
call.SetInputCtrlParamTuple(value.first.c_str(), value.second);
}
#if defined(_DEBUG)
runtime->recordExecutionTrace(procedureName_, HalconExecutionTracePhase::Started);
#endif
const auto started = std::chrono::steady_clock::now();
#if defined(_DEBUG)
try {
call.Execute();
}
catch (...) {
runtime->recordExecutionTrace(procedureName_, HalconExecutionTracePhase::Failed);
throw;
}
runtime->recordExecutionTrace(procedureName_, HalconExecutionTracePhase::Finished);
#else
call.Execute();
#endif
const double elapsed = std::chrono::duration<double, std::milli>(
std::chrono::steady_clock::now() - started).count();
runtime->submitTime(procedureName_, elapsed);
std::map<std::string, HObjectHolder> iconicOutputs;
std::map<std::string, HalconCpp::HTuple> controlOutputs;
std::map<std::string, std::vector<HalconCpp::HTuple>> vectorOutputs;
for (const auto& name : iconicOutputNames_) {
iconicOutputs[name].reset(call.GetOutputIconicParamObject(name.c_str()));
}
for (const auto& name : controlOutputNames_) {
controlOutputs[name] = call.GetOutputCtrlParamTuple(name.c_str());
}
for (const auto& name : vectorOutputNames_) {
const HalconCpp::HTupleVector values =
call.GetOutputCtrlParamVector(name.c_str());
for (int index = 0; index < values.Length(); ++index) {
vectorOutputs[name].push_back(values[index].T());
}
}
Output result;
outputCallback_(iconicOutputs, controlOutputs, vectorOutputs, result);
return { std::move(result), elapsed };
}
catch (const HDevEngineCpp::HDevEngineException& exception) {
return makeFailedResultLocked(std::string("HDevEngine异常: ") + exception.Message());
}
catch (const HalconCpp::HException& exception) {
return makeFailedResultLocked(
std::string("Halcon异常: ") + exception.ErrorMessage().Text());
}
catch (const std::exception& exception) {
return makeFailedResultLocked(
std::string("halconEvaluater evaluate exception: ") + exception.what());
}
catch (...) {
return makeFailedResultLocked("halconEvaluater evaluate unknown exception");
}
}
private:
void collectParameterNames()
{
std::map<std::string, HObjectHolder> iconicInputs;
std::map<std::string, HalconCpp::HTuple> controlInputs;
Input input;
inputCallback_(iconicInputs, controlInputs, input);
copyKeys(iconicInputs, iconicInputNames_);
copyKeys(controlInputs, controlInputNames_);
std::map<std::string, HObjectHolder> iconicOutputs;
std::map<std::string, HalconCpp::HTuple> controlOutputs;
std::map<std::string, std::vector<HalconCpp::HTuple>> vectorOutputs;
Output output;
outputCallback_(iconicOutputs, controlOutputs, vectorOutputs, output);
copyKeys(iconicOutputs, iconicOutputNames_);
copyKeys(controlOutputs, controlOutputNames_);
copyKeys(vectorOutputs, vectorOutputNames_);
}
template <typename Map>
static void copyKeys(const Map& values, std::vector<std::string>& names)
{
names.clear();
for (const auto& value : values) {
names.push_back(value.first);
}
}
static std::vector<std::string> tupleNames(const HalconCpp::HTuple& tuple)
{
std::vector<std::string> result;
result.reserve(static_cast<std::size_t>(tuple.Length()));
for (int index = 0; index < tuple.Length(); ++index) {
result.emplace_back(tuple[index].S().Text());
}
return result;
}
static bool sameNames(
std::vector<std::string> expected,
std::vector<std::string> actual)
{
std::sort(expected.begin(), expected.end());
std::sort(actual.begin(), actual.end());
return expected == actual;
}
static bool containsNames(
const std::vector<std::string>& actual,
const std::vector<std::string>& requested)
{
return std::all_of(requested.begin(), requested.end(),
[&actual](const std::string& name) {
return std::find(actual.begin(), actual.end(), name) != actual.end();
});
}
std::pair<bool, std::string> onSelfCheck() const
{
try {
HDevEngineCpp::HDevProcedure procedure(
const_cast<char*>(procedureName_.c_str()));
const auto actualIconicInputs =
tupleNames(procedure.GetInputIconicParamNames());
const auto actualControlInputs =
tupleNames(procedure.GetInputCtrlParamNames());
const auto actualIconicOutputs =
tupleNames(procedure.GetOutputIconicParamNames());
const auto actualControlOutputs =
tupleNames(procedure.GetOutputCtrlParamNames());
if (!sameNames(iconicInputNames_, actualIconicInputs)
|| !sameNames(controlInputNames_, actualControlInputs)) {
return { false, procedureName_ + "输入参数不匹配" };
}
std::vector<std::string> requestedControlOutputs = controlOutputNames_;
requestedControlOutputs.insert(requestedControlOutputs.end(),
vectorOutputNames_.begin(), vectorOutputNames_.end());
if (!containsNames(actualIconicOutputs, iconicOutputNames_)
|| !containsNames(actualControlOutputs, requestedControlOutputs)) {
return { false, procedureName_ + "输出参数不匹配" };
}
std::cout << procedureName_ << "已通过自检" << std::endl;
return { true, "" };
}
catch (const HDevEngineCpp::HDevEngineException& exception) {
return { false, exception.Message() };
}
catch (const HalconCpp::HException& exception) {
return { false, exception.ErrorMessage().Text() };
}
catch (const std::exception& exception) {
return { false, exception.what() };
}
catch (...) {
return { false, "HALCON procedure self-check failed" };
}
}
halconResult makeFailedResult(const std::string& reason)
{
QReadLocker stateLocker(&stateLock_);
return makeFailedResultLocked(reason);
}
halconResult makeFailedResultLocked(const std::string& reason)
{
qCritical() << QString::fromStdString(reason);
if (runtime_ != nullptr && !runtime_->isReleasing()) {
runtime_->reportEvaluaterError(reason);
}
std::map<std::string, HObjectHolder> iconicOutputs;
std::map<std::string, HalconCpp::HTuple> controlOutputs;
std::map<std::string, std::vector<HalconCpp::HTuple>> vectorOutputs;
for (const auto& name : iconicOutputNames_) {
iconicOutputs.emplace(name, HObjectHolder());
}
for (const auto& name : controlOutputNames_) {
controlOutputs.emplace(name, HalconCpp::HTuple());
}
for (const auto& name : vectorOutputNames_) {
vectorOutputs.emplace(name, std::vector<HalconCpp::HTuple>());
}
try {
if (failedCallback_) {
failedCallback_(iconicOutputs, controlOutputs, vectorOutputs);
}
Output output;
outputCallback_(iconicOutputs, controlOutputs, vectorOutputs, output);
return { std::move(output), -1.0 };
}
catch (const std::exception& exception) {
qCritical() << "halconEvaluater failed callback exception:" << exception.what();
}
catch (...) {
qCritical() << "halconEvaluater failed callback unknown exception";
}
return { Output(), -1.0 };
}
std::atomic<bool> initialized_{ false };
int reloadCallbackId_ = -1;
IHalconEvaluaterRuntime* runtime_ = nullptr;
std::string procedureName_;
inputCallback inputCallback_;
outputCallback outputCallback_;
failedCallback failedCallback_;
std::vector<std::string> iconicInputNames_;
std::vector<std::string> controlInputNames_;
std::vector<std::string> iconicOutputNames_;
std::vector<std::string> controlOutputNames_;
std::vector<std::string> vectorOutputNames_;
mutable QReadWriteLock stateLock_;
};