Files
ANSCORE/modules/ANSODEngine/ANSCUSTOMPY.cpp

758 lines
29 KiB
C++
Raw Normal View History

2026-03-28 16:54:11 +11:00
#include "ANSCUSTOMPY.h"
#include <filesystem>
#include <json.hpp>
using json = nlohmann::json;
namespace fs = std::filesystem;
namespace py = pybind11;
namespace {
using ThreadLocalModelMap = std::unordered_map<int, std::shared_ptr<ANSCENTER::ModelWrapper>>;
ThreadLocalModelMap& GetThreadLocalState() {
thread_local ThreadLocalModelMap threadState;
return threadState;
}
}
std::string CustomParametersToJson(const ANSCENTER::Params& params) {
json root;
// ROI_Config
for (const auto& cfg : params.ROI_Config) {
json j;
j["Rectangle"] = cfg.Rectangle;
j["Polygon"] = cfg.Polygon;
j["Line"] = cfg.Line;
j["MinItems"] = cfg.MinItems;
j["MaxItems"] = cfg.MaxItems;
j["Name"] = cfg.Name;
j["ROI-Match"] = cfg.ROIMatch;
root["ROI_Config"].push_back(j);
}
// ROI_Options
for (const auto& opt : params.ROI_Options) {
root["ROI_Options"].push_back(opt);
}
// Parameters
for (const auto& param : params.Parameters) {
json j;
j["Name"] = param.Name;
j["DataType"] = param.DataType;
j["NoOfdecimals"] = param.NoOfDecimals;
j["MaxValue"] = param.MaxValue;
j["MinValue"] = param.MinValue;
j["StartValue"] = param.StartValue;
j["ListItems"] = param.ListItems;
j["DefaultValue"] = param.DefaultValue;
j["Value"] = param.Value;
root["Parameters"].push_back(j);
}
// ROI_Values
for (const auto& rv : params.ROI_Values) {
json j;
j["ROI-Match"] = rv.ROIMatch;
for (const auto& pt : rv.ROIPoints) {
j["ROIPoints"].push_back({ {"x", pt.x}, {"y", pt.y} });
}
j["Option"] = rv.Option;
j["Name"] = rv.Name;
j["OriginalImageSize"] = rv.OriginalImageSize;
root["ROI_Values"].push_back(j);
}
// Convert to compact JSON string (no whitespace)
std::string jsonString = root.dump(); // dump() without indent = compact
return jsonString;
}
namespace ANSCENTER
{
std::once_flag PythonRuntime::initFlag;
std::unique_ptr<PythonRuntime> PythonRuntime::instance = nullptr;
std::atomic<bool> PythonRuntime::_isInitialized{ false };
std::atomic<bool> PythonRuntime::shuttingDown{ false };
std::mutex PythonRuntime::_instanceMutex;
std::mutex ANSCUSTOMPY::_globalInstancesMutex;
std::unordered_set<ANSCUSTOMPY*> ANSCUSTOMPY::_globalInstances;
std::atomic<int> ANSCUSTOMPY::globalInstanceCounter{ 1 };
// Declare GetPythonRuntime as a friend function of PythonRuntime
PythonRuntime& GetPythonRuntime(const std::wstring& pythonHome);
PythonRuntime& GetPythonRuntime(const std::wstring& pythonHome) {
std::call_once(PythonRuntime::initFlag, [&]() {
std::lock_guard<std::mutex> lock(PythonRuntime::_instanceMutex);
PythonRuntime::instance = std::unique_ptr<PythonRuntime>(new PythonRuntime(pythonHome));
});
return *PythonRuntime::instance;
}
PythonRuntime::PythonRuntime(const std::wstring& home) : pythonHome(home) {
InitializeInterpreter();
}
bool PythonRuntime::IsInitialized() {
return _isInitialized.load(std::memory_order_acquire) && Py_IsInitialized();
}
void PythonRuntime::InitializeInterpreter() {
if (!pythonHome.empty()) {
#pragma warning(push)
#pragma warning(disable : 4996)
Py_SetPythonHome(pythonHome.c_str());
#pragma warning(pop)
}
py::initialize_interpreter();
_isInitialized.store(true, std::memory_order_release);
PatchPythonStreamsSafe();
}
void PythonRuntime::PatchPythonStreamsSafe() {
try {
py::exec(R"(
import sys, io
class DummyStream(io.StringIO):
def write(self, txt): pass
def flush(self): pass
if sys.stdout is None or not hasattr(sys.stdout, "write"):
sys.stdout = DummyStream()
if sys.stderr is None or not hasattr(sys.stderr, "write"):
sys.stderr = DummyStream()
)");
}
catch (const py::error_already_set& e) {
std::cerr << "[PatchPythonStreamsSafe] Python error: " << e.what() << std::endl;
}
}
void PythonRuntime::AddToSysPath(const std::string& path) {
if (!IsInitialized()) {
std::cerr << "[PythonRuntime::AddToSysPath] Interpreter not initialized!\n";
return;
}
py::gil_scoped_acquire gil;
py::module_ sys = py::module_::import("sys");
sys.attr("path").attr("insert")(0, path);
}
void PythonRuntime::Shutdown() {
std::lock_guard<std::mutex> lock(_instanceMutex);
if (shuttingDown.exchange(true)) return;
if (_isInitialized.load(std::memory_order_acquire)) {
try {
py::gil_scoped_acquire gil;
py::module::import("gc").attr("collect")();
}
catch (...) {
std::cerr << "[PythonRuntime::Shutdown] GC failed (ignored)." << std::endl;
}
try {
py::finalize_interpreter();
}
catch (...) {
std::cerr << "[PythonRuntime::Shutdown] finalize_interpreter failed." << std::endl;
}
_isInitialized.store(false, std::memory_order_release);
}
instance.reset();
}
/// <summary>
/// /// Initializes the ANSCUSTOMPY model with the given parameters.
/// </summary>
ANSCUSTOMPY::ANSCUSTOMPY() {
_instanceId = globalInstanceCounter.fetch_add(1, std::memory_order_relaxed);
_destroyed = false;
_isInitialized = false;
}
ANSCUSTOMPY::~ANSCUSTOMPY() {
try {
if (PythonRuntime::IsInitialized())
Destroy();
}
catch (...) {
std::cerr << "[~ANSCUSTOMPY] Exception during destruction." << std::endl;
}
}
void ANSCUSTOMPY::RegisterSelf() {
{
std::lock_guard<std::mutex> lock(_globalInstancesMutex);
_globalInstances.insert(this);
}
auto& threadState = GetThreadLocalState();
if (!_wrapper || !_wrapper->modelFactory) return;
if (threadState.find(_instanceId) == threadState.end()) {
if (_wrapper && _wrapper->modelFactory) {
py::gil_scoped_acquire gil;
auto newWrapper = std::make_shared<ModelWrapper>();
newWrapper->modelFactory = _wrapper->modelFactory;
newWrapper->map[0] = std::make_shared<py::object>(_wrapper->modelFactory());
threadState[_instanceId] = newWrapper;
}
}
}
void ANSCUSTOMPY::UnregisterSelf() {
std::lock_guard<std::mutex> lock(_globalInstancesMutex);
_globalInstances.erase(this);
}
void ANSCUSTOMPY::ClearThreadState() {
auto& threadState = GetThreadLocalState();
auto it = threadState.find(_instanceId);
if (it != threadState.end()) {
if (PythonRuntime::IsInitialized()) {
py::gil_scoped_acquire gil;
it->second->map.clear();
}
else {
it->second->map.clear();
}
threadState.erase(it);
}
}
bool ANSCUSTOMPY::EnsureModelReady(const std::string& methodName) {
if (!_isInitialized || _destroyed || !PythonRuntime::IsInitialized()) {
return false;
}
//auto& threadState = GetThreadLocalState();
//if (threadState.find(_instanceId) == threadState.end()) {
// if (_wrapper && _wrapper->modelFactory) {
// py::gil_scoped_acquire gil;
// auto newWrapper = std::make_shared<ModelWrapper>();
// newWrapper->modelFactory = _wrapper->modelFactory;
// newWrapper->map[0] = std::make_shared<py::object>(_wrapper->modelFactory());
// threadState[_instanceId] = newWrapper;
// }
// else {
// return false;
// }
//}
return true;
}
py::object ANSCUSTOMPY::GetThreadLocalModel() {
auto& threadState = GetThreadLocalState();
auto it = threadState.find(_instanceId);
if (_destroyed || it == threadState.end() || !it->second) {
return py::none();
}
auto& modelMap = it->second->map;
auto modelIt = modelMap.find(0);
if (modelIt == modelMap.end()) {
py::gil_scoped_acquire gil;
auto obj = it->second->modelFactory();
if (obj.is_none()) return py::none();
auto wrapped = std::make_shared<py::object>(obj);
modelMap[0] = wrapped;
return *wrapped;
}
return *modelIt->second;
}
bool ANSCUSTOMPY::Destroy() {
//std::lock_guard<std::recursive_mutex> lock(_mutex);
if (_destroyed) return true;
_destroyed = true;
ClearThreadState();
UnregisterSelf();
return true;
}
void ANSCUSTOMPY::SafeShutdownAll(bool waitForShutdown) {
std::unordered_set<ANSCUSTOMPY*> instancesCopy;
{
std::lock_guard<std::mutex> lock(_globalInstancesMutex);
instancesCopy = _globalInstances;
}
for (ANSCUSTOMPY* instance : instancesCopy) {
if (instance) {
try {
instance->ClearThreadState();
instance->Destroy();
}
catch (...) {
std::cerr << "[SafeShutdownAll] Destroy failed." << std::endl;
}
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(300));
auto shutdownFunc = []() {
try {
std::this_thread::sleep_for(std::chrono::milliseconds(300));
int retry = 0;
while (!ANSCUSTOMPY::_globalInstances.empty() && retry++ < 100) {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
if (PythonRuntime::IsInitialized()) {
py::gil_scoped_acquire gil;
py::module::import("gc").attr("collect")();
PythonRuntime::Shutdown();
}
}
catch (...) {
std::cerr << "[SafeShutdownAll] Shutdown failed." << std::endl;
}
};
if (waitForShutdown) shutdownFunc();
else std::thread(shutdownFunc).detach();
}
bool ANSCUSTOMPY::InitializePythonModel(const std::string& moduleName, const std::string& fullPath) {
if (!PythonRuntime::IsInitialized()) {
_logger.LogError("[InitializePythonModel] Python error:\n", "Python interpreter is not initialized.", __FILE__, __LINE__);
return false;
}
py::gil_scoped_acquire gil;
try {
py::exec("import logging; logging.getLogger('ultralytics').setLevel(logging.CRITICAL)", py::globals());
py::dict locals;
std::ostringstream code;
code << "import importlib.util\n"
<< "spec = importlib.util.spec_from_file_location('" << moduleName << "', r'" << fullPath << "')\n"
<< "mod = importlib.util.module_from_spec(spec)\n"
<< "spec.loader.exec_module(mod)\n";
py::exec(code.str(), py::globals(), locals);
py::object mod = locals["mod"];
py::object modelClass = mod.attr("ANSModel");
auto& threadState = GetThreadLocalState();
auto& wrapper = threadState[_instanceId]; // ensure per-thread-per-instance
if (!wrapper) wrapper = std::make_shared<ModelWrapper>();
wrapper->modelFactory = [modelClass]() {
py::gil_scoped_acquire gil;
py::object instance = modelClass();
return instance;};
_wrapper = wrapper; // Store for main thread access too
return true;
}
catch (const py::error_already_set& e) {
PyErr_Print();
_logger.LogFatal("InitializePythonModel", std::string("Python error: ") + e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSCUSTOMPY::Initialize(std::string licenseKey, ModelConfig modelConfig,
const std::string& modelZipFilePath, const std::string& modelZipPassword,
std::string& labelMap) {
//std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
// Step 1: Base model init
bool result = ANSODBase::Initialize(licenseKey, modelConfig, modelZipFilePath, modelZipPassword, labelMap);
if (!result) return false;
_modelConfig = modelConfig;
_modelConfig.inpHeight = 640;
_modelConfig.inpWidth = 640;
if (_modelConfig.modelMNSThreshold < 0.2) _modelConfig.modelMNSThreshold = 0.5;
if (_modelConfig.modelConfThreshold < 0.2) _modelConfig.modelConfThreshold = 0.5;
// Step 2: Validate model folder
if (_modelFolder.empty()) {
_logger.LogError("ANSCUSTOMPY::Initialize", "_modelFolder is not set.", __FILE__, __LINE__);
return false;
}
// Step 3: Set Python Home
std::wstring pythonHome;
const fs::path sharedPython = "C:/ProgramData/ANSCENTER/Python311";
const fs::path sharedDll = sharedPython / "python311.dll";
if (fs::exists(sharedDll)) {
pythonHome = sharedPython.wstring();
}
else {
pythonHome = (fs::path(_modelFolder) / "python_env").wstring();
}
// Step 4: Initialize Python and sys.path
auto& runtime = GetPythonRuntime(pythonHome);
runtime.AddToSysPath(_modelFolder);
// Step 5: Load Python model class from unique module
//_scriptPath = _modelFolder + "/ansinfer.py";
_scriptPath = fs::path(_modelFolder).append("ansinfer.py").string();
_moduleName = "ansinfer_" + std::to_string(_instanceId);
if (!InitializePythonModel(_moduleName, _scriptPath)) {
_logger.LogError("ANSCUSTOMPY::Initialize", "Failed to load Python model.", __FILE__, __LINE__);
return false;
}
// Step 6: Load class names
_classFilePath = CreateFilePath(_modelFolder, "classes.names");
std::ifstream isValidFileName(_classFilePath);
if (!isValidFileName) {
_logger.LogDebug("ANSCUSTOMPY::LoadModel. Load classes from string", _classFilePath, __FILE__, __LINE__);
LoadClassesFromString();
}
else {
_logger.LogDebug("ANSCUSTOMPY::LoadModel. Load classes from file", _classFilePath, __FILE__, __LINE__);
LoadClassesFromFile();
}
labelMap.clear();
if (!_classes.empty())
labelMap = VectorToCommaSeparatedString(_classes);
_destroyed = false;
_isInitialized = true;
RegisterSelf();
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSCUSTOMPY::Initialize", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSCUSTOMPY::LoadModel(const std::string& modelZipFilePath, const std::string& modelZipPassword) {
//std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
// Step 0: Assign unique instance ID
if (!ANSODBase::LoadModel(modelZipFilePath, modelZipPassword))
return false;
if (_modelFolder.empty()) {
_logger.LogError("ANSCUSTOMPY::LoadModel", "_modelFolder is not set.", __FILE__, __LINE__);
return false;
}
// Step 3: Set Python home
std::wstring pythonHome;
const fs::path sharedPython = "C:/ProgramData/ANSCENTER/Python311";
const fs::path sharedDll = sharedPython / "python311.dll";
if (fs::exists(sharedDll)) {
pythonHome = sharedPython.wstring();
}
else {
pythonHome = (fs::path(_modelFolder) / "python_env").wstring();
}
// Step 4: Initialize Python and sys.path
auto& runtime = GetPythonRuntime(pythonHome);
runtime.AddToSysPath(_modelFolder);
// Step 5: Load Python model class from unique module
//_scriptPath = _modelFolder + "/ansinfer.py";
_scriptPath = fs::path(_modelFolder).append("ansinfer.py").string();
_moduleName = "ansinfer_" + std::to_string(_instanceId);
if (!InitializePythonModel(_moduleName, _scriptPath)) {
_logger.LogError("ANSCUSTOMPY::LoadModel", "Failed to load Python model.", __FILE__, __LINE__);
return false;
}
_destroyed = false;
_isInitialized = true;
RegisterSelf();
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSCUSTOMPY::LoadModel", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSCUSTOMPY::LoadModelFromFolder(std::string licenseKey, ModelConfig modelConfig,
std::string modelName, std::string className,
const std::string& modelFolder,
std::string& labelMap)
{
//std::lock_guard<std::recursive_mutex> lock(_mutex);
try {
// Step 0: Assign unique instance ID early
if (!ANSODBase::LoadModelFromFolder(licenseKey, modelConfig, modelName, className, modelFolder, labelMap))
return false;
_modelConfig = modelConfig;
_modelConfig.inpHeight = 640;
_modelConfig.inpWidth = 640;
if (_modelConfig.modelMNSThreshold < 0.2) _modelConfig.modelMNSThreshold = 0.5;
if (_modelConfig.modelConfThreshold < 0.2) _modelConfig.modelConfThreshold = 0.5;
// Step 2: Validate model folder
if (_modelFolder.empty()) {
_logger.LogError("ANSCUSTOMPY::Initialize", "_modelFolder is not set.", __FILE__, __LINE__);
return false;
}
// Step 3: Set Python home
std::wstring pythonHome;
const fs::path sharedPython = "C:/ProgramData/ANSCENTER/Python311";
const fs::path sharedDll = sharedPython / "python311.dll";
if (fs::exists(sharedDll)) {
pythonHome = sharedPython.wstring();
}
else {
pythonHome = (fs::path(_modelFolder) / "python_env").wstring();
}
// Step 4: Initialize Python and sys.path
auto& runtime = GetPythonRuntime(pythonHome);
runtime.AddToSysPath(_modelFolder);
// Step 5: Load Python model class from unique module
//_scriptPath = _modelFolder + "/ansinfer.py";
_scriptPath = fs::path(_modelFolder).append("ansinfer.py").string();
_moduleName = "ansinfer_" + std::to_string(_instanceId);
if (!InitializePythonModel(_moduleName, _scriptPath)) {
_logger.LogError("ANSCUSTOMPY::Initialize", "Failed to load Python model.", __FILE__, __LINE__);
return false;
}
// Step 6: Load class labels
_classFilePath = CreateFilePath(_modelFolder, "classes.names");
std::ifstream isValidFileName(_classFilePath);
if (!isValidFileName) {
_logger.LogDebug("ANSCUSTOMPY::LoadModel", "Load classes from string", __FILE__, __LINE__);
LoadClassesFromString();
}
else {
_logger.LogDebug("ANSCUSTOMPY::LoadModel", "Load classes from file", __FILE__, __LINE__);
LoadClassesFromFile();
}
labelMap.clear();
if (!_classes.empty())
labelMap = VectorToCommaSeparatedString(_classes);
_destroyed = false;
_isInitialized = true;
RegisterSelf();
return true;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSCUSTOMPY::LoadModelFromFolder", e.what(), __FILE__, __LINE__);
return false;
}
}
std::vector<Object> ANSCUSTOMPY::RunInference(const cv::Mat& input) {
return RunInference(input, "CustomPyCam");
}
std::vector<Object> ANSCUSTOMPY::RunInference(const cv::Mat& input, const std::string& camera_id) {
std::vector<Object> results;
if (!_isInitialized) {
_logger.LogError("ANSCUSTOMPY::RunInference", "Model is not initialized.", __FILE__, __LINE__);
return results;
}
if (!_licenseValid) {
_logger.LogError("ANSCUSTOMPY::RunInference", "Invalid License", __FILE__, __LINE__);
return results;
}
if (input.empty()) {
_logger.LogError("ANSCUSTOMPY::RunInference", "Input image is empty.", __FILE__, __LINE__);
return results;
}
try {
std::string jsonStr = RunPythonInferenceFromMat(input);
if (!jsonStr.empty()) {
results = ANSUtilityHelper::GetDetectionsFromString(jsonStr);
}
else {
_logger.LogWarn("ANSCUSTOMPY::RunInference", "Empty JSON result from Python inference.", __FILE__, __LINE__);
}
}
catch (const std::exception& e) {
_logger.LogFatal("ANSCUSTOMPY::RunInference", e.what(), __FILE__, __LINE__);
}
return results;
}
std::string ANSCUSTOMPY::RunPythonInferenceFromMat(const cv::Mat& image) {
if (!EnsureModelReady("RunPythonInferenceFromMat"))
return "";
if (image.empty())
return "";
try {
py::object model = GetThreadLocalModel();
if (model.is_none()) {
_logger.LogError("ANSCUSTOMPY::RunPythonInferenceFromMat", "Model is None.", __FILE__, __LINE__);
return "";
}
py::gil_scoped_acquire gil;
if (!py::hasattr(model, "run_inference")) {
_logger.LogError("ANSCUSTOMPY::RunPythonInferenceFromMat", "Model has no 'run_inference' method.", __FILE__, __LINE__);
return "";
}
py::bytes image_bytes(reinterpret_cast<const char*>(image.data), image.total() * image.elemSize());
py::object result = model.attr("run_inference")(image_bytes, image.cols, image.rows, image.channels());
return result.cast<std::string>();
}
catch (const py::error_already_set& e) {
PyErr_Print();
_logger.LogFatal("RunPythonInferenceFromMat", e.what(), __FILE__, __LINE__);
return "";
}
catch (const std::exception& e) {
_logger.LogFatal("RunPythonInferenceFromMat", e.what(), __FILE__, __LINE__);
return "";
}
}
bool ANSCUSTOMPY::OptimizeModel(bool fp16, std::string& optimizedModelFolder) {
if (!EnsureModelReady("OptimizeModel")) return false;
try {
py::object model = GetThreadLocalModel();
if (model.is_none()) {
_logger.LogError("ANSCUSTOMPY::OptimizeModel", "Model is None.", __FILE__, __LINE__);
return false;
}
py::gil_scoped_acquire gil;
if (!py::hasattr(model, "model_optimize")) {
_logger.LogError("ANSCUSTOMPY::OptimizeModel", "Python model has no method 'model_optimize'.", __FILE__, __LINE__);
return false;
}
py::object result;
try {
result = model.attr("model_optimize")(fp16);
}
catch (const py::error_already_set& e) {
PyErr_Print();
_logger.LogFatal("ANSCUSTOMPY::OptimizeModel", std::string("Exception in 'model_optimize': ") + e.what(), __FILE__, __LINE__);
return false;
}
if (result.is_none()) {
_logger.LogError("ANSCUSTOMPY::OptimizeModel", "Python 'model_optimize' returned None.", __FILE__, __LINE__);
return false;
}
if (!py::isinstance<py::bool_>(result)) {
_logger.LogError("ANSCUSTOMPY::OptimizeModel", "Unexpected return type from 'model_optimize'. Expected bool.", __FILE__, __LINE__);
return false;
}
bool success = result.cast<bool>();
if (success) {
optimizedModelFolder = fs::path(_scriptPath).parent_path().string();
}
return success;
}
catch (const py::error_already_set& e) {
PyErr_Print();
_logger.LogFatal("ANSCUSTOMPY::OptimizeModel", e.what(), __FILE__, __LINE__);
return false;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSCUSTOMPY::OptimizeModel", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSCUSTOMPY::ConfigureParameters(Params& param) {
if (!EnsureModelReady("ConfigureParamaters")) return false;
try {
py::object model = GetThreadLocalModel();
if (model.is_none()) {
_logger.LogError("ANSCUSTOMPY::ConfigureParamaters", "Model is None.", __FILE__, __LINE__);
return false;
}
py::gil_scoped_acquire gil;
if (!py::hasattr(model, "configureParameters")) {
_logger.LogError("ANSCUSTOMPY::ConfigureParamaters", "Python model missing 'configureParameters' method.", __FILE__, __LINE__);
return false;
}
py::object result;
try {
result = model.attr("configureParameters")();
}
catch (const py::error_already_set& e) {
PyErr_Print();
_logger.LogFatal("ANSCUSTOMPY::ConfigureParamaters", std::string("Exception in 'configureParameters': ") + e.what(), __FILE__, __LINE__);
return false;
}
if (result.is_none()) {
_logger.LogError("ANSCUSTOMPY::ConfigureParamaters", "Returned None from Python method.", __FILE__, __LINE__);
return false;
}
if (!py::isinstance<py::str>(result)) {
_logger.LogError("ANSCUSTOMPY::ConfigureParamaters", "Expected Python string return from 'configureParameters'.", __FILE__, __LINE__);
return false;
}
std::string jsonStr = result.cast<std::string>();
if (jsonStr.empty()) {
_logger.LogError("ANSCUSTOMPY::ConfigureParamaters", "Empty JSON string returned from Python.", __FILE__, __LINE__);
return false;
}
param = ANSUtilityHelper::ParseCustomParameters(jsonStr);
return true;
}
catch (const py::error_already_set& e) {
PyErr_Print();
_logger.LogFatal("ANSCUSTOMPY::ConfigureParamaters", e.what(), __FILE__, __LINE__);
return false;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSCUSTOMPY::ConfigureParamaters", e.what(), __FILE__, __LINE__);
return false;
}
}
bool ANSCUSTOMPY::SetParameters(const Params& param) {
if (!EnsureModelReady("SetParamaters")) return false;
try {
std::string jsonStr = CustomParametersToJson(param);
if (jsonStr.empty()) {
_logger.LogError("ANSCUSTOMPY::SetParamaters", "Serialized parameter string is empty.", __FILE__, __LINE__);
return false;
}
py::object model = GetThreadLocalModel();
if (model.is_none()) {
_logger.LogError("ANSCUSTOMPY::SetParamaters", "Model is None.", __FILE__, __LINE__);
return false;
}
py::gil_scoped_acquire gil;
if (!py::hasattr(model, "setParameters")) {
_logger.LogError("ANSCUSTOMPY::SetParamaters", "Python model has no method 'setParameters'.", __FILE__, __LINE__);
return false;
}
py::object result;
try {
result = model.attr("setParameters")(jsonStr);
}
catch (const py::error_already_set& e) {
PyErr_Print();
_logger.LogFatal("ANSCUSTOMPY::SetParamaters", std::string("Exception in 'setParameters': ") + e.what(), __FILE__, __LINE__);
return false;
}
if (result.is_none()) {
_logger.LogError("ANSCUSTOMPY::SetParamaters", "Python method returned None.", __FILE__, __LINE__);
return false;
}
if (!py::isinstance<py::bool_>(result)) {
_logger.LogError("ANSCUSTOMPY::SetParamaters", "Python method returned non-boolean type.", __FILE__, __LINE__);
return false;
}
return result.cast<bool>();
}
catch (const py::error_already_set& e) {
PyErr_Print();
_logger.LogFatal("ANSCUSTOMPY::SetParamaters", std::string("Python error: ") + e.what(), __FILE__, __LINE__);
return false;
}
catch (const std::exception& e) {
_logger.LogFatal("ANSCUSTOMPY::SetParamaters", e.what(), __FILE__, __LINE__);
return false;
}
}
}