完成框架目录和模块重构;新增核心、单例、工具、加密及框架 SDK;统一任务、相机和 RPC 接口,并增加离线检测、相机标定、备份恢复、版本管理、崩溃守护和心跳监控功能。
762 lines
25 KiB
C++
762 lines
25 KiB
C++
#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_;
|
||
};
|