#include "ANSANNHUB.h" #include #ifndef NOMINMAX #define NOMINMAX #endif #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include #include #include #include #include // --- Refcounted handle registry infrastructure --- static std::unordered_map& ANNHUBHandleRegistry() { static std::unordered_map s; return s; } static std::mutex& ANNHUBHandleRegistryMutex() { static std::mutex m; return m; } static std::condition_variable& ANNHUBHandleRegistryCV() { static std::condition_variable cv; return cv; } static void RegisterANNHUBHandle(ANSCENTER::ANNHUBAPI* h) { std::lock_guard lk(ANNHUBHandleRegistryMutex()); ANNHUBHandleRegistry()[h] = 1; } static ANSCENTER::ANNHUBAPI* AcquireANNHUBHandle(ANSCENTER::ANNHUBAPI* h) { std::lock_guard lk(ANNHUBHandleRegistryMutex()); auto it = ANNHUBHandleRegistry().find(h); if (it == ANNHUBHandleRegistry().end()) return nullptr; it->second++; return h; } static bool ReleaseANNHUBHandleRef(ANSCENTER::ANNHUBAPI* h) { std::lock_guard lk(ANNHUBHandleRegistryMutex()); auto it = ANNHUBHandleRegistry().find(h); if (it == ANNHUBHandleRegistry().end()) return false; it->second--; if (it->second <= 0) { ANNHUBHandleRegistry().erase(it); ANNHUBHandleRegistryCV().notify_all(); return true; } ANNHUBHandleRegistryCV().notify_all(); return false; } static bool UnregisterANNHUBHandle(ANSCENTER::ANNHUBAPI* h) { std::unique_lock lk(ANNHUBHandleRegistryMutex()); auto it = ANNHUBHandleRegistry().find(h); if (it == ANNHUBHandleRegistry().end()) return false; it->second--; if (!ANNHUBHandleRegistryCV().wait_for(lk, std::chrono::seconds(30), [&]() { auto it2 = ANNHUBHandleRegistry().find(h); return it2 == ANNHUBHandleRegistry().end() || it2->second <= 0; })) { OutputDebugStringA("WARNING: UnregisterANNHUBHandle timed out after 30s waiting for refcount to reach zero.\n"); } ANNHUBHandleRegistry().erase(h); return true; } class ANNHUBHandleGuard { ANSCENTER::ANNHUBAPI* engine; public: explicit ANNHUBHandleGuard(ANSCENTER::ANNHUBAPI* e) : engine(e) {} ~ANNHUBHandleGuard() { if (engine) ReleaseANNHUBHandleRef(engine); } ANNHUBHandleGuard(const ANNHUBHandleGuard&) = delete; ANNHUBHandleGuard& operator=(const ANNHUBHandleGuard&) = delete; explicit operator bool() const { return engine != nullptr; } ANSCENTER::ANNHUBAPI* get() const { return engine; } }; // --- End handle registry infrastructure --- static bool ansannhubLicenceValid = false; // Global once_flag to protect license checking static std::once_flag ansannhubLicenseOnceFlag; namespace ANSCENTER { void ReLu(std::vector& iVal, std::vector& oVal) { int dim = iVal.size(); oVal.resize(dim); for (int i = 0; i < dim; i++) { if (iVal[i] >= 0) oVal[i] = iVal[i]; else oVal[i] = 0; } } void LogSig(std::vector& iVal, std::vector& oVal) { int dim = iVal.size(); oVal.resize(dim); for (int i = 0; i < dim; i++) { oVal[i] = 1 / (1 + exp(-iVal[i])); } } void TanSig(std::vector& iVal, std::vector& oVal) { int dim = iVal.size(); oVal.resize(dim); for (int i = 0; i < dim; i++) { oVal[i] = 2 / (1 + exp(-2 * iVal[i])) - 1; } } void PureLin(std::vector& iVal, std::vector& oVal) { oVal = iVal; } void SoftMax(std::vector& iVal, std::vector& oVal) { double softmaxWeight = 0; int dim = iVal.size(); oVal.resize(dim); if (dim == 1) { oVal[0] = 1 / (1 + exp(-iVal[0])); } else { for (int i = 0; i < dim; i++) { softmaxWeight = softmaxWeight + exp(iVal[i]); } for (int i = 0; i < dim; i++) { oVal[i] = exp(iVal[i]) / softmaxWeight; } } } void ActivationFunction(std::vector& iVal, std::vector& oVal, int mode) { switch (mode) { case 0: PureLin(iVal, oVal); break; case 1: ReLu(iVal, oVal); break; case 2: LogSig(iVal, oVal); break; case 3: TanSig(iVal, oVal); break; case 4: SoftMax(iVal, oVal); break; default: TanSig(iVal, oVal); break; } } static void VerifyGlobalANSANNHUBLicense(const std::string& licenseKey) { try { ansannhubLicenceValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(licenseKey, 1000, "ANNHUB-LV");//Default productId=1000 } catch (std::exception& e) { //this->_logger.LogFatal("ANSOPENCV::CheckLicense. Error:", e.what(), __FILE__, __LINE__); } } void ANNHUBAPI::CheckLicense() { //try { // _licenseValid = ANSCENTER::ANSLicenseHelper::LicenseVerification(_licenseKey, 1000, "ANNHUB-LV");//Default productId=1000 //} //catch (std::exception& e) { // //this->_logger.LogFatal("ANSOPENCV::CheckLicense. Error:", e.what(), __FILE__, __LINE__); //} try { // Check once globally std::call_once(ansannhubLicenseOnceFlag, [this]() { VerifyGlobalANSANNHUBLicense(_licenseKey); }); // Update this instance's local license flag _licenseValid = ansannhubLicenceValid; } catch (const std::exception& e) { //this->_logger.LogFatal("ANNHUBAPI::CheckLicense. Error:", e.what(), __FILE__, __LINE__); } } ANNHUBAPI::ANNHUBAPI() { // Control creation isCreated = 0; // Structrural parameters nInputNodes = 1; nHiddenNodes = 10; nOutputNodes = 1; hiddenActivation = 2; // default =2 outputActivation = 2; // default =2; dataNormalisationModeInput = 0; dataNormalisationModeOutput = 0; } ANNHUBAPI::~ANNHUBAPI() noexcept { try { Destroy(); } catch (...) {} } void ANNHUBAPI::Destroy() { if (isCreated != 0) { FreeNeuralNetwork(); } } void ANNHUBAPI::FreeNeuralNetwork() { try { // Clear vectors IW.clear(); LW.clear(); nInput.clear(); nOutput.clear(); Ib.clear(); Lb.clear(); xminInput.clear(); xmaxInput.clear(); xminOutput.clear(); xmaxOutput.clear(); // Reset the flag indicating whether the network is created isCreated = 0; } catch (const std::exception& e) { //this->_logger.LogFatal("ANNHUBAPI::FreeNeuralNetwork. Error:", e.what(), __FILE__, __LINE__); } } /* (x[i]-xmin[i]) (ymax-ymin) x[i] = ---------------- * +ymin (xmax[i]-xmin[i]) */ void ANNHUBAPI::PreProcessing(std::vector& Input) { switch (dataNormalisationModeInput) { case 0: // linear break; case 1: // mapminmax for (int i = 0; i < nInputNodes; i++) { if (xmaxInput[i] != xminInput[i]) { Input[i] = (Input[i] - xminInput[i]) * (ymaxInput - yminInput) / (xmaxInput[i] - xminInput[i]) + yminInput; } } break; default:// minmaxmap for (int i = 0; i < nInputNodes; i++) { if (xmaxInput[i] != xminInput[i]) { Input[i] = (Input[i] - xminInput[i]) * (ymaxInput - yminInput) / (xmaxInput[i] - xminInput[i]) + yminInput; } } break; } } void ANNHUBAPI::PostProcessing(std::vector& Output) { switch (dataNormalisationModeOutput) { case 0: // linear break; case 1: for (int i = 0; i < nOutputNodes; i++) { if (ymaxOutput != yminOutput) { Output[i] = (Output[i] - yminOutput) * (xmaxOutput[i] - xminOutput[i]) / (ymaxOutput - yminOutput) + xminOutput[i]; } } break; default: for (int i = 0; i < nOutputNodes; i++) { if (ymaxOutput != yminOutput) { Output[i] = (Output[i] - yminOutput) * (xmaxOutput[i] - xminOutput[i]) / (ymaxOutput - yminOutput) + xminOutput[i]; } } break; } } int ANNHUBAPI::ImportANNFromFile(std::string filename) { try { FILE* fp; int err = fopen_s(&fp, filename.c_str(), "r"); // r: Opens for reading. if (err != 0) return -2; float value, randNum; //0. Check if it the correct type for C language fscanf_s(fp, "%f", &value); if (static_cast(value) != 1) return -1; // Assume that the type C is 0 //1. Load random number fscanf_s(fp, "%f", &randNum); //2. Structure (IDs) int trainingEngine, hlAct, olAct, costFunct, prePro, postPro, evalModel; fscanf_s(fp, "%f", &value); trainingEngine = static_cast(value); fscanf_s(fp, "%f", &value); hlAct = static_cast(value); fscanf_s(fp, "%f", &value); olAct = static_cast(value); fscanf_s(fp, "%f", &value); costFunct = static_cast(value); fscanf_s(fp, "%f", &value); prePro = static_cast(value); fscanf_s(fp, "%f", &value); postPro = static_cast(value); fscanf_s(fp, "%f", &value); evalModel = static_cast(value); //2.1 Activation function hiddenActivation = hlAct - 10; outputActivation = olAct - 10; //2.2 Data Processing dataNormalisationModeInput = prePro - 1000; dataNormalisationModeOutput = postPro - 1000; // 3. Load neural network structure and min max inputs value for pre-post processing int ipNodes, hdNodes, opNodes; fscanf_s(fp, "%f", &value); ipNodes = static_cast(value); fscanf_s(fp, "%f", &value); hdNodes = static_cast(value); fscanf_s(fp, "%f", &value); opNodes = static_cast(value); Create(ipNodes, hdNodes, opNodes); // 4. Load Input-Hidden weights (extraction formula) for (int j = 0; j < nInputNodes; j++) { for (int i = 0; i < nHiddenNodes; i++) { fscanf_s(fp, "%f", &value); IW[i][j] = value - randNum; } } // 4.1. Load bias Hidden weights for (int i = 0; i < nHiddenNodes; i++) { fscanf_s(fp, "%f", &value); Ib[i] = value - randNum; } // 4.2. Load Hidden_Output weights for (int j = 0; j < nHiddenNodes; j++) { for (int i = 0; i < nOutputNodes ; i++) { fscanf_s(fp, "%f", &value); LW[i][j] = value - randNum; } } // 4.3. Load bias Output weights for (int i = 0; i < nOutputNodes; i++) { fscanf_s(fp, "%f", &value); Lb[i] = value - randNum; } // 5. For Pre-processing if (dataNormalisationModeInput >= 0) { // Range settings fscanf_s(fp, "%f", &value); yminInput = value - randNum; fscanf_s(fp, "%f", &value); ymaxInput = value - randNum; // Min and max for (int i = 0; i < nInputNodes; i++) { fscanf_s(fp, "%f", &value); xminInput[i] = value - randNum; } for (int i = 0; i < nInputNodes; i++) { fscanf_s(fp, "%f", &value); xmaxInput[i] = value - randNum; } } // 6. For Post-processing if (dataNormalisationModeOutput >= 0) { // Range settings fscanf_s(fp, "%f", &value); yminOutput = value - randNum; fscanf_s(fp, "%f", &value); ymaxOutput = value - randNum; for (int i = 0; i < nOutputNodes; i++) { fscanf_s(fp, "%f", &value); xminOutput[i] = value - randNum; } for (int i = 0; i < nOutputNodes; i++) { fscanf_s(fp, "%f", &value); xmaxOutput[i] = value - randNum; } } fclose(fp); return 0; } catch (std::exception& e) { //this->_logger.LogFatal("ANNHUBAPI::ImportANNFromFile. Error:", e.what(), __FILE__, __LINE__); return -1; } } void ANNHUBAPI::Create(int inputNodes, int hiddenNodes, int outputNodes) { try { if (isCreated != 0) { FreeNeuralNetwork(); } nInputNodes = inputNodes; nHiddenNodes = hiddenNodes; nOutputNodes = outputNodes; nInput.resize(inputNodes); nOutput.resize(outputNodes); IW.resize(hiddenNodes, std::vector(inputNodes)); LW.resize(outputNodes, std::vector(hiddenNodes)); Ib.resize(hiddenNodes); Lb.resize(outputNodes); xminInput.resize(inputNodes); xmaxInput.resize(inputNodes); xminOutput.resize(outputNodes); xmaxOutput.resize(outputNodes); isCreated = 1; } catch (std::exception& e) { //this->_logger.LogFatal("ANNHUBAPI::Create. Error:", e.what(), __FILE__, __LINE__); } } bool ANNHUBAPI::Init(std::string licenseKey, std::string modelFilePath) { try { _licenseKey = licenseKey; _modelFilePath = modelFilePath; CheckLicense(); if (_licenseValid) { int result = ImportANNFromFile(_modelFilePath); if (result == 0) return true; else return false; } else { return false; } } catch (std::exception& e) { //this->_logger.LogFatal("ANNHUBAPI::Init. Error:", e.what(), __FILE__, __LINE__); return false; } } std::vector ANNHUBAPI::Inference(std::vector ip) { try { int i, j; std::vector a1(nHiddenNodes), n1(nHiddenNodes), n2(nOutputNodes); if (!_licenseValid) return {}; // Invalid license if (isCreated == 0) return {}; // Need to check the input size as well, return {} if it fails PreProcessing(ip); //1. Calculate n1 for (i = 0; i < nHiddenNodes; i++) { n1[i] = 0; for (j = 0; j < nInputNodes; j++) { if (std::isnan(IW[i][j]) || std::isnan(ip[j])) { continue; } n1[i] = n1[i] + IW[i][j] * ip[j]; } n1[i] = n1[i] + Ib[i]; } ActivationFunction(n1, a1, hiddenActivation); // 3. Calculate n2 for (i = 0; i < nOutputNodes; i++) { n2[i] = 0; for (j = 0; j < nHiddenNodes; j++) { n2[i] = n2[i] + LW[i][j] * a1[j]; } n2[i] = n2[i] + Lb[i]; } ActivationFunction(n2, nOutput, outputActivation); PostProcessing(nOutput); return nOutput; } catch (std::exception& e) { //this->_logger.LogFatal("ANNHUBAPI::Inference. Error:", e.what(), __FILE__, __LINE__); return {}; } } } extern "C" __declspec(dllexport) int CreateANNHUBHandle(ANSCENTER::ANNHUBAPI * *Handle, const char* licenseKey, const char* modelFilePath) { if (!Handle || !licenseKey || !modelFilePath) return -1; try { if (*Handle) { if (UnregisterANNHUBHandle(*Handle)) { (*Handle)->Destroy(); delete *Handle; } *Handle = nullptr; } auto ptr = std::make_unique(); bool result = ptr->Init(licenseKey, modelFilePath); if (result) { *Handle = ptr.release(); RegisterANNHUBHandle(*Handle); return 1; } *Handle = nullptr; return 0; } catch (...) { return 0; } } static int ReleaseANNHUBHandle_Impl(ANSCENTER::ANNHUBAPI** Handle) { if (Handle == nullptr || *Handle == nullptr) return -1; UnregisterANNHUBHandle(*Handle); (*Handle)->Destroy(); delete *Handle; *Handle = nullptr; return 0; } extern "C" __declspec(dllexport) int ReleaseANNHUBHandle(ANSCENTER::ANNHUBAPI * *Handle) { __try { return ReleaseANNHUBHandle_Impl(Handle); } __except (EXCEPTION_EXECUTE_HANDLER) { if (Handle) *Handle = nullptr; return 0; } } extern "C" __declspec(dllexport) int ANNHUB_Inference(ANSCENTER::ANNHUBAPI * *pHandle, double* inputArray, int inputSize, double** outputArray) { // Check for null pointers if (pHandle == nullptr || *pHandle == nullptr || inputArray == nullptr || outputArray == nullptr || inputSize <= 0) { return -1; // Invalid arguments } ANNHUBHandleGuard guard(AcquireANNHUBHandle(*pHandle)); if (!guard) return -1; try { // Convert inputArray to std::vector std::vector inputVector(inputArray, inputArray + inputSize); // Call Inference std::vector inferenceResult = guard.get()->Inference(inputVector); int resultSize = inferenceResult.size(); // Allocate memory for the output array *outputArray = new double[resultSize]; if (*outputArray == nullptr) return -2; // Memory allocation failed // Copy the inference result to the output array std::copy(inferenceResult.begin(), inferenceResult.end(), *outputArray); return 0; // Success } catch (const std::exception& e) { // Log the exception message if you have a logging system return -5; // Error code for exception in Inference } } extern "C" __declspec(dllexport) int ANNHUB_InferenceLV(ANSCENTER::ANNHUBAPI * *pHandle, double* inputArray, int inputSize, LStrHandle outputHandle) { // Check for null pointers if (pHandle == nullptr || *pHandle == nullptr || inputArray == nullptr || outputHandle == nullptr || inputSize <= 0) { return 0; // Invalid arguments } ANNHUBHandleGuard guard(AcquireANNHUBHandle(*pHandle)); if (!guard) return 0; try { // Convert inputArray to std::vector std::vector inputVector(inputArray, inputArray + inputSize); // Call Inference std::vector inferenceResult = guard.get()->Inference(inputVector); int resultSize = inferenceResult.size(); if (resultSize <= 0) return 0; // Error code for empty inference result std::stringstream ss; for (size_t i = 0; i < resultSize; ++i) { if (i == resultSize - 1) ss << inferenceResult[i]; else ss << inferenceResult[i]<<";"; } std::string st= ss.str(); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputHandle, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputHandle)->cnt = size; memcpy((*outputHandle)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (const std::exception& e) { // Log the exception message if you have a logging system return 0; // Error code for exception in Inference } } // --- V2 entry points: accept handle by value (uint64_t) to avoid LabVIEW buffer reuse bug --- extern "C" __declspec(dllexport) int ANNHUB_Inference_V2(uint64_t handleVal, double* inputArray, int inputSize, double** outputArray) { auto* _v2h = reinterpret_cast(handleVal); if (!_v2h) return -1; if (inputArray == nullptr || outputArray == nullptr || inputSize <= 0) return -1; ANNHUBHandleGuard guard(AcquireANNHUBHandle(_v2h)); if (!guard) return -1; try { std::vector inputVector(inputArray, inputArray + inputSize); std::vector inferenceResult = guard.get()->Inference(inputVector); int resultSize = inferenceResult.size(); *outputArray = new double[resultSize]; if (*outputArray == nullptr) return -2; std::copy(inferenceResult.begin(), inferenceResult.end(), *outputArray); return 0; } catch (const std::exception& e) { return -5; } } extern "C" __declspec(dllexport) int ANNHUB_InferenceLV_V2(uint64_t handleVal, double* inputArray, int inputSize, LStrHandle outputHandle) { auto* _v2h = reinterpret_cast(handleVal); if (!_v2h) return 0; if (inputArray == nullptr || outputHandle == nullptr || inputSize <= 0) return 0; ANNHUBHandleGuard guard(AcquireANNHUBHandle(_v2h)); if (!guard) return 0; try { std::vector inputVector(inputArray, inputArray + inputSize); std::vector inferenceResult = guard.get()->Inference(inputVector); int resultSize = inferenceResult.size(); if (resultSize <= 0) return 0; std::stringstream ss; for (size_t i = 0; i < resultSize; ++i) { if (i == resultSize - 1) ss << inferenceResult[i]; else ss << inferenceResult[i] << ";"; } std::string st = ss.str(); int size = st.length(); if (size > 0) { MgErr error; error = DSSetHandleSize(outputHandle, sizeof(int32) + size * sizeof(uChar)); if (error == noErr) { (*outputHandle)->cnt = size; memcpy((*outputHandle)->str, st.c_str(), size); return 1; } else return 0; } else return 0; } catch (const std::exception& e) { return 0; } }