diff --git a/Drivers/ScanLabSMC/ACT/LibMCDriver_ScanLabSMC.xml b/Drivers/ScanLabSMC/ACT/LibMCDriver_ScanLabSMC.xml index 4eefb4b7..5693c607 100644 --- a/Drivers/ScanLabSMC/ACT/LibMCDriver_ScanLabSMC.xml +++ b/Drivers/ScanLabSMC/ACT/LibMCDriver_ScanLabSMC.xml @@ -326,6 +326,14 @@ Custom implementation + + + + + + + + diff --git a/Drivers/ScanLabSMC/CMakeLists.txt b/Drivers/ScanLabSMC/CMakeLists.txt index a85fccd5..ab468edd 100644 --- a/Drivers/ScanLabSMC/CMakeLists.txt +++ b/Drivers/ScanLabSMC/CMakeLists.txt @@ -39,6 +39,12 @@ set (DRIVERPROJECT ScanLabSMC) include (../CMakeDriverCommon.txt) +set_property(TARGET libmcdriver_scanlabsmc PROPERTY CXX_STANDARD 17) +set_property(TARGET libmcdriver_scanlabsmc PROPERTY CXX_STANDARD_REQUIRED ON) + +get_target_property(_std libmcdriver_scanlabsmc CXX_STANDARD) +message(STATUS "libmcdriver_scanlabsmc uses C++ standard: ${_std}") + ########################################################################################## ### Add custom code below ########################################################################################## diff --git a/Drivers/ScanLabSMC/Headers/CppDynamic/libmcdriver_scanlabsmc_dynamic.h b/Drivers/ScanLabSMC/Headers/CppDynamic/libmcdriver_scanlabsmc_dynamic.h index 33754374..e11190ed 100644 --- a/Drivers/ScanLabSMC/Headers/CppDynamic/libmcdriver_scanlabsmc_dynamic.h +++ b/Drivers/ScanLabSMC/Headers/CppDynamic/libmcdriver_scanlabsmc_dynamic.h @@ -252,6 +252,24 @@ typedef LibMCDriver_ScanLabSMCResult (*PLibMCDriver_ScanLabSMCSMCJob_StopExecuti */ typedef LibMCDriver_ScanLabSMCResult (*PLibMCDriver_ScanLabSMCSMCJob_LoadSimulationDataPtr) (LibMCDriver_ScanLabSMC_SMCJob pSMCJob, LibMCEnv_DataTable pSimulationDataTable); +/** +* Reads the SMC Simulation data into a data table. +* +* @param[in] pSMCJob - SMCJob instance. +* @param[in] pSimulationDataTable - Data table object to read the simulation into. +* @return error code or 0 (success) +*/ +typedef LibMCDriver_ScanLabSMCResult (*PLibMCDriver_ScanLabSMCSMCJob_LoadSimulationData_SMC_v1Ptr) (LibMCDriver_ScanLabSMC_SMCJob pSMCJob, LibMCEnv_DataTable pSimulationDataTable); + +/** +* Reads the SMC Log Record data into a data table. +* +* @param[in] pSMCJob - SMCJob instance. +* @param[in] pLogRecordDataTable - Data table object to read the simulation into. +* @return error code or 0 (success) +*/ +typedef LibMCDriver_ScanLabSMCResult (*PLibMCDriver_ScanLabSMCSMCJob_LoadLogRecordDataPtr) (LibMCDriver_ScanLabSMC_SMCJob pSMCJob, LibMCEnv_DataTable pLogRecordDataTable); + /** * Returns a characteristic value of a job. * @@ -856,6 +874,8 @@ typedef struct { PLibMCDriver_ScanLabSMCSMCJob_WaitForExecutionPtr m_SMCJob_WaitForExecution; PLibMCDriver_ScanLabSMCSMCJob_StopExecutionPtr m_SMCJob_StopExecution; PLibMCDriver_ScanLabSMCSMCJob_LoadSimulationDataPtr m_SMCJob_LoadSimulationData; + PLibMCDriver_ScanLabSMCSMCJob_LoadSimulationData_SMC_v1Ptr m_SMCJob_LoadSimulationData_SMC_v1; + PLibMCDriver_ScanLabSMCSMCJob_LoadLogRecordDataPtr m_SMCJob_LoadLogRecordData; PLibMCDriver_ScanLabSMCSMCJob_GetJobCharacteristicPtr m_SMCJob_GetJobCharacteristic; PLibMCDriver_ScanLabSMCSMCJob_GetJobDurationPtr m_SMCJob_GetJobDuration; PLibMCDriver_ScanLabSMCSMCConfiguration_SetDynamicViolationReactionPtr m_SMCConfiguration_SetDynamicViolationReaction; diff --git a/Drivers/ScanLabSMC/Headers/CppDynamic/libmcdriver_scanlabsmc_dynamic.hpp b/Drivers/ScanLabSMC/Headers/CppDynamic/libmcdriver_scanlabsmc_dynamic.hpp index b86a6efb..c57f169f 100644 --- a/Drivers/ScanLabSMC/Headers/CppDynamic/libmcdriver_scanlabsmc_dynamic.hpp +++ b/Drivers/ScanLabSMC/Headers/CppDynamic/libmcdriver_scanlabsmc_dynamic.hpp @@ -544,6 +544,8 @@ class CSMCJob : public CBase { inline void WaitForExecution(const LibMCDriver_ScanLabSMC_uint32 nTimeOutInMilliseconds); inline void StopExecution(); inline void LoadSimulationData(classParam pSimulationDataTable); + inline void LoadSimulationData_SMC_v1(classParam pSimulationDataTable); + inline void LoadLogRecordData(classParam pLogRecordDataTable); inline LibMCDriver_ScanLabSMC_double GetJobCharacteristic(const eJobCharacteristic eValueType); inline LibMCDriver_ScanLabSMC_double GetJobDuration(); }; @@ -786,6 +788,8 @@ class CDriver_ScanLabSMC : public CDriver { pWrapperTable->m_SMCJob_WaitForExecution = nullptr; pWrapperTable->m_SMCJob_StopExecution = nullptr; pWrapperTable->m_SMCJob_LoadSimulationData = nullptr; + pWrapperTable->m_SMCJob_LoadSimulationData_SMC_v1 = nullptr; + pWrapperTable->m_SMCJob_LoadLogRecordData = nullptr; pWrapperTable->m_SMCJob_GetJobCharacteristic = nullptr; pWrapperTable->m_SMCJob_GetJobDuration = nullptr; pWrapperTable->m_SMCConfiguration_SetDynamicViolationReaction = nullptr; @@ -1065,6 +1069,24 @@ class CDriver_ScanLabSMC : public CDriver { if (pWrapperTable->m_SMCJob_LoadSimulationData == nullptr) return LIBMCDRIVER_SCANLABSMC_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 + pWrapperTable->m_SMCJob_LoadSimulationData_SMC_v1 = (PLibMCDriver_ScanLabSMCSMCJob_LoadSimulationData_SMC_v1Ptr) GetProcAddress(hLibrary, "libmcdriver_scanlabsmc_smcjob_loadsimulationdata_smc_v1"); + #else // _WIN32 + pWrapperTable->m_SMCJob_LoadSimulationData_SMC_v1 = (PLibMCDriver_ScanLabSMCSMCJob_LoadSimulationData_SMC_v1Ptr) dlsym(hLibrary, "libmcdriver_scanlabsmc_smcjob_loadsimulationdata_smc_v1"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_SMCJob_LoadSimulationData_SMC_v1 == nullptr) + return LIBMCDRIVER_SCANLABSMC_ERROR_COULDNOTFINDLIBRARYEXPORT; + + #ifdef _WIN32 + pWrapperTable->m_SMCJob_LoadLogRecordData = (PLibMCDriver_ScanLabSMCSMCJob_LoadLogRecordDataPtr) GetProcAddress(hLibrary, "libmcdriver_scanlabsmc_smcjob_loadlogrecorddata"); + #else // _WIN32 + pWrapperTable->m_SMCJob_LoadLogRecordData = (PLibMCDriver_ScanLabSMCSMCJob_LoadLogRecordDataPtr) dlsym(hLibrary, "libmcdriver_scanlabsmc_smcjob_loadlogrecorddata"); + dlerror(); + #endif // _WIN32 + if (pWrapperTable->m_SMCJob_LoadLogRecordData == nullptr) + return LIBMCDRIVER_SCANLABSMC_ERROR_COULDNOTFINDLIBRARYEXPORT; + #ifdef _WIN32 pWrapperTable->m_SMCJob_GetJobCharacteristic = (PLibMCDriver_ScanLabSMCSMCJob_GetJobCharacteristicPtr) GetProcAddress(hLibrary, "libmcdriver_scanlabsmc_smcjob_getjobcharacteristic"); #else // _WIN32 @@ -1679,6 +1701,14 @@ class CDriver_ScanLabSMC : public CDriver { if ( (eLookupError != 0) || (pWrapperTable->m_SMCJob_LoadSimulationData == nullptr) ) return LIBMCDRIVER_SCANLABSMC_ERROR_COULDNOTFINDLIBRARYEXPORT; + eLookupError = (*pLookup)("libmcdriver_scanlabsmc_smcjob_loadsimulationdata_smc_v1", (void**)&(pWrapperTable->m_SMCJob_LoadSimulationData_SMC_v1)); + if ( (eLookupError != 0) || (pWrapperTable->m_SMCJob_LoadSimulationData_SMC_v1 == nullptr) ) + return LIBMCDRIVER_SCANLABSMC_ERROR_COULDNOTFINDLIBRARYEXPORT; + + eLookupError = (*pLookup)("libmcdriver_scanlabsmc_smcjob_loadlogrecorddata", (void**)&(pWrapperTable->m_SMCJob_LoadLogRecordData)); + if ( (eLookupError != 0) || (pWrapperTable->m_SMCJob_LoadLogRecordData == nullptr) ) + return LIBMCDRIVER_SCANLABSMC_ERROR_COULDNOTFINDLIBRARYEXPORT; + eLookupError = (*pLookup)("libmcdriver_scanlabsmc_smcjob_getjobcharacteristic", (void**)&(pWrapperTable->m_SMCJob_GetJobCharacteristic)); if ( (eLookupError != 0) || (pWrapperTable->m_SMCJob_GetJobCharacteristic == nullptr) ) return LIBMCDRIVER_SCANLABSMC_ERROR_COULDNOTFINDLIBRARYEXPORT; @@ -2147,6 +2177,26 @@ class CDriver_ScanLabSMC : public CDriver { CheckError(m_pWrapper->m_WrapperTable.m_SMCJob_LoadSimulationData(m_pHandle, hSimulationDataTable)); } + /** + * CSMCJob::LoadSimulationData_SMC_v1 - Reads the SMC Simulation data into a data table. + * @param[in] pSimulationDataTable - Data table object to read the simulation into. + */ + void CSMCJob::LoadSimulationData_SMC_v1(classParam pSimulationDataTable) + { + LibMCEnvHandle hSimulationDataTable = pSimulationDataTable.GetHandle(); + CheckError(m_pWrapper->m_WrapperTable.m_SMCJob_LoadSimulationData_SMC_v1(m_pHandle, hSimulationDataTable)); + } + + /** + * CSMCJob::LoadLogRecordData - Reads the SMC Log Record data into a data table. + * @param[in] pLogRecordDataTable - Data table object to read the simulation into. + */ + void CSMCJob::LoadLogRecordData(classParam pLogRecordDataTable) + { + LibMCEnvHandle hLogRecordDataTable = pLogRecordDataTable.GetHandle(); + CheckError(m_pWrapper->m_WrapperTable.m_SMCJob_LoadLogRecordData(m_pHandle, hLogRecordDataTable)); + } + /** * CSMCJob::GetJobCharacteristic - Returns a characteristic value of a job. * @param[in] eValueType - Type of job diff --git a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_sdk.cpp b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_sdk.cpp index a687ecfe..4b48de29 100644 --- a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_sdk.cpp +++ b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_sdk.cpp @@ -166,6 +166,10 @@ CScanLabSMCSDK::CScanLabSMCSDK(const std::string& sDLLNameUTF8, const std::strin this->slsc_cfg_get_blend_mode = (PScanLabSMCPtr_slsc_cfg_get_blend_mode)_loadScanLabSMCAddress(hLibrary, "slsc_cfg_get_blend_mode"); this->slsc_cfg_set_blend_mode = (PScanLabSMCPtr_slsc_cfg_set_blend_mode)_loadScanLabSMCAddress(hLibrary, "slsc_cfg_set_blend_mode"); + this->slsc_job_start_record = (PScanLabSMCPtr_slsc_job_start_record)_loadScanLabSMCAddress(hLibrary, "slsc_job_start_record"); + this->slsc_job_stop_record = (PScanLabSMCPtr_slsc_job_stop_record)_loadScanLabSMCAddress(hLibrary, "slsc_job_stop_record"); + this->slsc_ctrl_log_record = (PScanLabSMCPtr_slsc_ctrl_log_record)_loadScanLabSMCAddress(hLibrary, "slsc_ctrl_log_record"); + m_LibraryHandle = (void*) hLibrary; } @@ -281,7 +285,9 @@ void CScanLabSMCSDK::resetFunctionPtrs() slsc_cfg_get_blend_mode = nullptr; slsc_cfg_set_blend_mode = nullptr; - + slsc_job_start_record = nullptr; + slsc_job_stop_record = nullptr; + slsc_ctrl_log_record = nullptr; } diff --git a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_sdk.hpp b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_sdk.hpp index 4c43bdf4..7a4a9db5 100644 --- a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_sdk.hpp +++ b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_sdk.hpp @@ -176,6 +176,23 @@ namespace LibMCDriver_ScanLabSMC { }; + enum class slsc_RecordSet : uint8_t + { + slsc_RecordSet_HeadAPosition = 0, + slsc_RecordSet_HeadBPosition = 1, + slsc_RecordSet_LaserSwitches = 2, + slsc_RecordSet_SetPositions = 3, + slsc_RecordSet_ActPositions = 4, + slsc_RecordSet_Empty = 5, + }; + + enum class slsc_TransformationStep : uint8_t + { + slsc_TransformationStep_Workspace = 0, + slsc_TransformationStep_Aligned = 1, + slsc_TransformationStep_Corrected = 2, + slsc_TransformationStep_Rtc = 3, + }; typedef struct _slsc_PolylineOptions slsc_PolylineOptions; typedef struct _slsc_VersionInfo slsc_VersionInfo; @@ -215,6 +232,10 @@ namespace LibMCDriver_ScanLabSMC { typedef slscReturnValue(SCANLABSMC_CALLINGCONVENTION* PScanLabSMCPtr_slsc_cfg_get_blend_mode) (size_t Handle, slsc_BlendModes* BlendMode); typedef slscReturnValue(SCANLABSMC_CALLINGCONVENTION* PScanLabSMCPtr_slsc_cfg_set_blend_mode) (size_t Handle, slsc_BlendModes BlendMode); + typedef slscReturnValue(SCANLABSMC_CALLINGCONVENTION* PScanLabSMCPtr_slsc_job_start_record) (size_t Handle, slsc_RecordSet RecordSetA, slsc_RecordSet RecordSetB); + typedef slscReturnValue(SCANLABSMC_CALLINGCONVENTION* PScanLabSMCPtr_slsc_job_stop_record) (size_t Handle); + typedef slscReturnValue(SCANLABSMC_CALLINGCONVENTION* PScanLabSMCPtr_slsc_ctrl_log_record) (size_t Handle, const char* DatasetPath, slsc_TransformationStep Step); + class CScanLabSMCSDK_DLLDirectoryCache { private: #ifdef _WIN32 @@ -270,6 +291,9 @@ namespace LibMCDriver_ScanLabSMC { PScanLabSMCPtr_slsc_job_para_disable slsc_job_para_disable = nullptr; PScanLabSMCPtr_slsc_job_para_line slsc_job_para_line = nullptr; PScanLabSMCPtr_slsc_job_multi_para_line slsc_job_multi_para_line = nullptr; + PScanLabSMCPtr_slsc_job_start_record slsc_job_start_record = nullptr; + PScanLabSMCPtr_slsc_job_stop_record slsc_job_stop_record = nullptr; + PScanLabSMCPtr_slsc_ctrl_log_record slsc_ctrl_log_record = nullptr; CScanLabSMCSDK(const std::string & sDLLNameUTF8, const std::string& sDLLDirectoryUTF8); ~CScanLabSMCSDK(); diff --git a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcconfiguration.cpp b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcconfiguration.cpp index 2fe63179..c7c0e083 100644 --- a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcconfiguration.cpp +++ b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcconfiguration.cpp @@ -255,6 +255,8 @@ void CSMCConfiguration::SetFirmwareResources(const std::string& sFirmwareDataRes if (sFirmwareDataResource.empty()) throw ELibMCDriver_ScanLabSMCInterfaceException(LIBMCDRIVER_SCANLABSMC_ERROR_EMPTYRTCFIRMWARERESOURCENAME); + m_sFirmwareDataResource = sFirmwareDataResource; + if (m_pDriverEnvironment->MachineHasResourceData(sFirmwareDataResource)) { m_pDriverEnvironment->RetrieveMachineResourceData(sFirmwareDataResource, m_FirmwareData); } @@ -316,7 +318,7 @@ std::string CSMCConfiguration::buildConfigurationXML(LibMCEnv::CWorkingDirectory newCorrectionFile = pWorkingDirectory->StoreCustomDataInTempFile("ct5", m_CorrectionFileData); pWorkingDirectory->StoreCustomData("RTC6RBF.rbf", m_FPGAData); - pWorkingDirectory->StoreCustomData("RTC6ETH.out", m_FirmwareData); + pWorkingDirectory->StoreCustomData(m_sFirmwareDataResource + ".out", m_FirmwareData); pWorkingDirectory->StoreCustomData("RTC6DAT.dat", m_AuxiliaryData); std::string sBaseDirectoryPath = pWorkingDirectory->GetAbsoluteFilePath(); @@ -389,6 +391,7 @@ std::string CSMCConfiguration::buildConfigurationXML(LibMCEnv::CWorkingDirectory nodesToCopyFromTemplate.push_back("KinematicsList"); nodesToCopyFromTemplate.push_back("LaserConfig"); nodesToCopyFromTemplate.push_back("IOConfig"); + nodesToCopyFromTemplate.push_back("SystemConfig"); break; default: diff --git a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcconfiguration.hpp b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcconfiguration.hpp index 275ec7df..b33982c4 100644 --- a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcconfiguration.hpp +++ b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcconfiguration.hpp @@ -66,7 +66,10 @@ class CSMCConfiguration : public virtual ISMCConfiguration, public virtual CBase uint32_t m_nSerialNumber; std::vector m_CorrectionFileData; + + std::string m_sFirmwareDataResource; std::vector m_FirmwareData; + std::vector m_FPGAData; std::vector m_AuxiliaryData; std::string m_sIPAddress; diff --git a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smccsvparser.cpp b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smccsvparser.cpp new file mode 100644 index 00000000..4336eb71 --- /dev/null +++ b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smccsvparser.cpp @@ -0,0 +1,491 @@ +#include "libmcdriver_scanlabsmc_smccsvparser.hpp" + +#include +#include +#include +#include +#include // for std::tolower +#include + +using namespace LibMCDriver_ScanLabSMC::Impl; + +CSMCCSVParser::CSMCCSVParser(const std::string& filename, char delimiter) + : m_delimiter(delimiter) +{ + std::ifstream file(filename, std::ios::binary); + if (!file) { + throw std::runtime_error("Failed to open file: " + filename); + } + + file.seekg(0, std::ios::end); + std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); + + m_fileBuff.resize(static_cast(size)); + if (!file.read(m_fileBuff.data(), size)) { + throw std::runtime_error("Failed to read file content"); + } +} + +void CSMCCSVParser::Parse(const std::vector& field_bindings) +{ + const char* data = m_fileBuff.data(); + const char* end = data + m_fileBuff.size(); + + const char* lineStart = data; + const char* ptr = data; + + double timestamp = 0.0; + + size_t prevTsIdx = 0; + size_t currTsIdx = 0; + + std::vector parsers; + std::vector extenders; + std::vector interpolators; + + void* ts_target = nullptr; + + for (size_t i = 0; i < field_bindings.size(); ++i) { + if (field_bindings[i].meta.type == FieldParserType::Timestamp) + ts_target = field_bindings[i].target; + else { + parsers.push_back(GetParser(field_bindings[i].meta.type)); + extenders.push_back(GetExtender(field_bindings[i].meta.type)); + interpolators.push_back(GetInterpolator(field_bindings[i].meta.type)); + } + } + + while (ptr < end) { + const char* lineEnd = nullptr; + bool isLineEnd = false; + + if (*ptr == '\r') { + if ((ptr + 1 < end) && *(ptr + 1) == '\n') { + lineEnd = ptr; + ptr += 2; + } + else { + lineEnd = ptr; + ptr += 1; + } + isLineEnd = true; + } + else if (*ptr == '\n') { + lineEnd = ptr; + ptr += 1; + isLineEnd = true; + } + else if (ptr == end - 1) { + lineEnd = ptr + 1; + ++ptr; + isLineEnd = true; + } + else { + ++ptr; + } + + if (isLineEnd) { + size_t lineLength = lineEnd - lineStart; + + if (lineLength > 0 && *lineStart == '#') { + ParseComment(lineStart, lineLength); + } + else if (lineLength > 0) { + const char* fieldStart = lineStart; + size_t fieldLength = 0; + size_t parserIndex = 0; + + PushTimestamp(ts_target, timestamp); + + prevTsIdx = currTsIdx; + + auto vec_ts = static_cast*>(ts_target); + if (vec_ts) + currTsIdx = vec_ts->size() - 1; + + timestamp += CSMCCSVParser::TimestampDefaultInc; + + const char* p = lineStart; + while (p <= lineEnd && parserIndex < parsers.size()) { + if (p == lineEnd || *p == m_delimiter) { + fieldLength = p - fieldStart; + + if (parsers[parserIndex] && field_bindings[parserIndex].target) { + parsers[parserIndex](fieldStart, fieldLength, field_bindings[parserIndex].target, ts_target); + } + + ++parserIndex; + fieldStart = p + 1; + } + ++p; + } + + if (currTsIdx != prevTsIdx) { + parserIndex = 0; + while (parserIndex < extenders.size()) { + if (interpolators[parserIndex] && field_bindings[parserIndex].target) { + interpolators[parserIndex](prevTsIdx, currTsIdx, field_bindings[parserIndex].target, ts_target); + } + ++parserIndex; + } + } + + parserIndex = 0; + while (parserIndex < extenders.size()) { + if (extenders[parserIndex] && field_bindings[parserIndex].target) { + extenders[parserIndex](field_bindings[parserIndex].target, ts_target); + } + ++parserIndex; + } + + ++m_rowCount; + } + + lineStart = ptr; + } + } +} + +void CSMCCSVParser::ParseComment(const char* lineStart, size_t length) +{ + // Handle comment lines starting with '#' (optional) + // Example: logging or ignoring + // std::string_view comment(lineStart, length); + // std::cout << "Comment: " << comment << std::endl; +} + +void CSMCCSVParser::PushTimestamp(void* ts_target, double ts) +{ + auto* vec = static_cast*>(ts_target); + vec->push_back(ts); +} + +double CSMCCSVParser::LastTimestamp(void* ts_target) +{ + auto* vec = static_cast*>(ts_target); + double ts = 0.0; + if (vec->size()) + ts = vec->back(); + return ts; +} + +void CSMCCSVParser::ParseTimestamp(const char* data, size_t length, void* target, void* ts_target) +{ + // Parse the timestamp as a Unix timestamp or in another format + std::string timestampStr(data, length); + + // If parsing Unix timestamp (integer) + try { + long timestamp = std::stol(timestampStr); + *reinterpret_cast(target) = static_cast(timestamp); + } + catch (const std::exception& e) { + // Handle invalid format if needed + *reinterpret_cast(target) = 0; // or throw an exception + } +} + +template +void CSMCCSVParser::ParseValue(const char* data, size_t length, void* target, void* ts_target) +{ + auto* vec = static_cast*>(target); + T value = 0; + + // Skip leading whitespace + while (length > 0 && std::isspace(static_cast(*data))) { + ++data; + --length; + } + + if constexpr (std::is_same_v || std::is_same_v) + { + auto [ptr, ec] = std::from_chars(data, data + length, value); + vec->push_back((ec == std::errc()) ? value : 0); + } + else if constexpr (std::is_same_v) + { + try { + // Convert to std::string to support + or - and decimal parsing via stod + std::string str(data, length); + value = std::stod(str); + vec->push_back(value); + } + catch (...) { + vec->push_back(0.0); // fallback on failure + } + } + else + { + throw std::runtime_error("ParseValue: Unsupported type"); + } +} + +template +void CSMCCSVParser::ExtendVector(void* target, void* ts_target) +{ + auto* vec = static_cast*>(target); + auto* vec_ts = static_cast*>(ts_target); + + auto diff = vec_ts->size() - vec->size(); + if (diff > 0 && !vec->empty()) { + const T copy = vec->back(); + for (size_t i = 0; i < diff; ++i) + vec->push_back(copy); + } +} + +template +void CSMCCSVParser::InterpolateVector(size_t idx_from, size_t idx_to, void* target, void* ts_target) +{ + if (idx_from + 1 == idx_to) + return; // nothing to interpolate + + auto* position = static_cast*>(target); + auto* timestamp = static_cast*>(ts_target); + + if (idx_from >= timestamp->size() || idx_to >= timestamp->size() || + idx_from >= position->size() || idx_to >= position->size()) { + throw std::out_of_range("Index out of range in Interpolate."); + } + + double t0 = timestamp->at(idx_from); + double t1 = timestamp->at(idx_to); + + T x0 = position->at(idx_from); + T x1 = position->at(idx_to); + + for (size_t i = idx_from + 1; i < idx_to; ++i) + { + double target_time = timestamp->at(i); + double t = (target_time - t0) / (t1 - t0); + + T interpolated; + if constexpr (std::is_floating_point::value) { + interpolated = static_cast(x0 + t * (x1 - x0)); + } + else { + interpolated = static_cast(std::round(x0 + t * (x1 - x0))); + } + + position->at(i) = interpolated; + } +} + +void CSMCCSVParser::ParseBool(const char* data, size_t length, void* target, void* ts_target) +{ + auto* vec = static_cast*>(target); + bool value = false; + + if (length == 1) { + value = (data[0] == '1' || std::tolower(data[0]) == 't'); + } + else { + std::string_view sv(data, length); + if (sv == "true" || sv == "True" || sv == "TRUE" || + sv == "yes" || sv == "Yes" || sv == "YES") { + value = true; + } + } + + vec->push_back(value); +} + +void CSMCCSVParser::ParseString(const char* data, size_t length, void* target, void* ts_target) +{ + auto* vec = static_cast*>(target); + vec->emplace_back(data, length); +} + +void CSMCCSVParser::ParseLaserSignal(const char* data, size_t length, void* target, void* ts_target) +{ + + auto* vec = static_cast*>(target); + + std::vector sub_cycles; + + //std::cout << "-----------------" << std::endl; + //std::cout << std::string(data, length) << std::endl; + + ParseLaserSignal_Internal(data, length, (void*)&sub_cycles, nullptr); + vec->push_back(sub_cycles[0].Toggle); + //std::cout << "init " << sub_cycles[0].Toggle << " at " << CSMCCSVParser::FormatDouble(sub_cycles[0].TimeOffset) << std::endl; + + if (sub_cycles.size() > 1) + { + double ts = LastTimestamp(ts_target); + + for (int i = 1; i < sub_cycles.size(); ++i) + { + vec->push_back(sub_cycles[i].Toggle); + + if (sub_cycles.size() > 1) + CSMCCSVParser::PushTimestamp(ts_target, ts + sub_cycles[i].TimeOffset); + + //std::cout << "toggle to " << sub_cycles[i].Toggle << " at " << CSMCCSVParser::FormatDouble(ts) << " + " << CSMCCSVParser::FormatDouble(sub_cycles[i].TimeOffset) << std::endl; + } + } + + //std::cout << "-----------------" << std::endl; +} + +CSMCCSVParser::ParserFunc CSMCCSVParser::GetParser(FieldParserType type) +{ + switch (type) { + case FieldParserType::None: + return nullptr; + case FieldParserType::Timestamp: + return nullptr; //return &CSMCCSVParser::ParseTimestamp; + case FieldParserType::Int: + return static_cast(&CSMCCSVParser::ParseValue); //return &CSMCCSVParser::ParseInt32; + case FieldParserType::UInt32: + return static_cast(&CSMCCSVParser::ParseValue); //return &CSMCCSVParser::ParseUInt32; + case FieldParserType::Double: + return static_cast(&CSMCCSVParser::ParseValue); // return &CSMCCSVParser::ParseDouble; + case FieldParserType::Bool: + return &CSMCCSVParser::ParseBool; + case FieldParserType::String: + return &CSMCCSVParser::ParseString; + case FieldParserType::LaserSignal: + return &CSMCCSVParser::ParseLaserSignal; + default: + throw std::invalid_argument("CSMCCSVParser::GetParser - unknown FieldParserType"); + } +} + +CSMCCSVParser::ExtenderFunc CSMCCSVParser::GetExtender(FieldParserType type) { + switch (type) { + case FieldParserType::None: + return nullptr; + case FieldParserType::Timestamp: + return nullptr; //return &CSMCCSVParser::ParseTimestamp; + case FieldParserType::Int: + return static_cast(&CSMCCSVParser::ExtendVector); + case FieldParserType::UInt32: + return static_cast(&CSMCCSVParser::ExtendVector); + case FieldParserType::Double: + return static_cast(&CSMCCSVParser::ExtendVector); + case FieldParserType::Bool: + return static_cast(&CSMCCSVParser::ExtendVector); + case FieldParserType::String: + return static_cast(&CSMCCSVParser::ExtendVector); + case FieldParserType::LaserSignal: + return nullptr; + default: + throw std::invalid_argument("CSMCCSVParser::GetParser - unknown FieldParserType"); + } +} + +CSMCCSVParser::InterpolatorFunc CSMCCSVParser::GetInterpolator(FieldParserType type) +{ + switch (type) { + case FieldParserType::None: + return nullptr; + case FieldParserType::Timestamp: + return nullptr; + case FieldParserType::Int: + return static_cast(&CSMCCSVParser::InterpolateVector); + case FieldParserType::UInt32: + return static_cast(&CSMCCSVParser::InterpolateVector); + case FieldParserType::Double: + return static_cast(&CSMCCSVParser::InterpolateVector); + case FieldParserType::Bool: + return nullptr; + case FieldParserType::String: + return nullptr; + case FieldParserType::LaserSignal: + return nullptr; + default: + throw std::invalid_argument("CSMCCSVParser::GetParser - unknown FieldParserType"); + } +} + +void CSMCCSVParser::ParseLaserSignal_Internal(const char* data, size_t length, void* target, void* ts_target) +{ + if (!target || !data || length == 0) + return; + + auto* outVec = static_cast*>(target); + outVec->clear(); + + const char* ptr = data; + const char* end = data + length; + + // Step 1: Parse LaserActive + const char* token_start = ptr; + while (ptr < end && *ptr != '_') ++ptr; + bool laserActive = (ptr - token_start == 1 && token_start[0] == '1'); + + if (!laserActive) { + outVec->push_back({ false, 0.0 }); + return; + } + + // Move to next token (InitToggleState) + if (ptr < end && *ptr == '_') ++ptr; + + // Step 2: Parse InitToggleState + token_start = ptr; + while (ptr < end && *ptr != '_') ++ptr; + bool initState = false; + + if (ptr - token_start == 1 && token_start[0] == '1') initState = true; + else if (ptr - token_start == 1 && token_start[0] == '0') initState = false; + else return; // invalid InitToggleState + + outVec->push_back({ initState, 0.0 }); + + // Move to first time offset + if (ptr < end && *ptr == '_') + ++ptr; + else + return; + + SubCycle sub_cycle; + sub_cycle.Toggle = !initState; + + while (ptr <= end) { + // Step 5.1: Parse next token (time offset or empty) + token_start = ptr; + while (ptr < end && *ptr != '_') ++ptr; + size_t token_len = ptr - token_start; + + if (token_len == 0) { + sub_cycle.TimeOffset = 0.0; + } + else { + std::vector temp; + //CSMCCSVParser::ParseDouble(token_start, token_len, &temp, ts_target); + CSMCCSVParser::ParseValue(token_start, token_len, &temp, ts_target); + sub_cycle.TimeOffset = temp.empty() ? 0.0 : temp.back(); + } + + outVec->push_back(sub_cycle); + + // Step 5.2: Flip toggle state + sub_cycle.Toggle = !sub_cycle.Toggle; + + if (ptr < end && *ptr == '_') ++ptr; + else break; + } + + // If no time offsets, still add initial with offset = 0 + if (outVec->empty()) { + sub_cycle.TimeOffset = 0.0; + outVec->push_back(sub_cycle); + } +} + +std::string CSMCCSVParser::FormatDouble(double value) +{ + char buffer[40]; // safe size + std::snprintf(buffer, sizeof(buffer), "%+.9f", value); + + // Ensure "-0.000000000;" is preserved correctly (for -0.0) + if (value == 0.0 && std::signbit(value)) { + // replace first character with '-' if signbit is set + buffer[0] = '-'; + } + + return std::string(buffer); +} \ No newline at end of file diff --git a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smccsvparser.hpp b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smccsvparser.hpp new file mode 100644 index 00000000..8cc0debb --- /dev/null +++ b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smccsvparser.hpp @@ -0,0 +1,334 @@ +#ifndef __LIBMCDRIVER_SCANLABSMC_SMCCSVPARSER +#define __LIBMCDRIVER_SCANLABSMC_SMCCSVPARSER + +#include +#include +#include + + + +namespace LibMCDriver_ScanLabSMC { +namespace Impl { + + /** + * @brief A fast single-pass CSV parser that loads the file into memory + * and parses fields without copying using pointer slicing. + */ + class CSMCCSVParser { + public: + + /** + * @brief Enumeration of supported field parser types. + * Used for mapping column definitions to parser functions. + */ + enum class FieldParserType { + /** + * Skips this field during parsing. Equivalent to nullptr in targets. + * Useful for ignoring unused CSV columns. + */ + None, + /** + * Parses a field as a timestamp using CSMCCSVParser::ParseDouble. + * Expected format: "+0.000000000", "-12.345678901" + * Typically represents absolute or relative time in seconds. + */ + Timestamp, + + /** + * Parses a field as signed integer using CSMCCSVParser::ParseInt. + * Expects input like: "42", "-7", "0" + */ + Int, + + /** + * Parses a field as unsigned 32-bit integer using CSMCCSVParser::ParseUInt32. + * Expects input like: "123", "0", "4294967295" + */ + UInt32, + + /** + * Parses a field as double using CSMCCSVParser::ParseDouble. + * Expects fixed-point or scientific notation: "3.14", "+0.0", "-1.23e-5" + */ + Double, + + /** + * Parses a field as boolean using CSMCCSVParser::ParseBool. + * Accepts "1", "0", "true", "false", "yes", "no" + */ + Bool, + + /** + * Parses a field as std::string using CSMCCSVParser::ParseString. + * Accepts arbitrary UTF-8 text. + */ + String, + + /** + * Parses a field as a laser signal descriptor using CSMCCSVParser::ParseLaserSignal. + * Expects compound format: "1_1_+2.0_+3.5_+8.1" + */ + LaserSignal + }; + + /** + * @brief Defines the processing steps that are applied to a field after parsing from CSV. + * + * Each field may undergo one or more of the following processing steps: + * - Nop: No post-processing is applied. The parsed value is stored as-is. + * - Extend: If a field has fewer entries than timestamps, the last known value is extended. + * - Interpolate: Missing values between known data points are interpolated linearly. + */ + enum FieldProcessingStep : uint8_t { + /** + * @brief No additional processing; values are stored as parsed. + */ + Nop = 0, + + /** + * @brief Repeat the last known value to align vector length with timestamps. + */ + Extend = 1, + + /** + * @brief Perform linear interpolation between known values to fill in gaps. + */ + Interpolate = 2 + }; + + + + /** + * @brief Metadata describing how a specific CSV field should be interpreted and processed. + * + * This structure pairs a field's parser type with the processing steps that should + * be applied after parsing. Used to define the parsing behavior for each column. + */ + struct FieldMetadata { + /** + * @brief Defines how the field should be parsed (e.g., as double, int, bool, etc.). + */ + FieldParserType type; + /** + * @brief Specifies post-processing steps such as Extend or Interpolate. + */ + uint8_t processing; + }; + + /** + * @brief Associates metadata with a pointer to the destination buffer for parsed data. + * + * This structure maps a single CSV field to both its interpretation rules (metadata) + * and the memory location where the parsed values should be stored. + */ + struct FieldBinding { + /** + * @brief Describes how the field should be parsed and post-processed. + */ + FieldMetadata meta; + + /** + * @brief Pointer to the target container where parsed values are appended. + * + * This is typically a pointer to a std::vector. + */ + void* target; + }; + + + /** + * @brief Type alias for parser function pointer. + * The function receives: + * - pointer to character buffer (not null-terminated), + * - length of the field, + * - pointer to target vector. + */ + using ParserFunc = std::function; + + /** + * @brief Function type used to extend a data vector after parsing. + * + * This function is responsible for duplicating the last known value + * to ensure the vector matches the length of the timestamp vector. + * + * @param target Pointer to the target data vector (e.g., std::vector). + * @param ts_target Pointer to the timestamp vector (std::vector). + */ + using ExtenderFunc = std::function; + + + /** + * @brief Function type used to interpolate values between two known timestamp indices. + * + * This function is applied when a field supports interpolation between sparse data points. + * It modifies the target vector in-place to insert estimated values using linear interpolation. + * + * @param idx_from Index of the first known timestamp. + * @param idx_to Index of the second known timestamp. + * @param target Pointer to the target data vector (e.g., std::vector). + * @param ts_target Pointer to the timestamp vector (std::vector). + */ + using InterpolatorFunc = std::function; + + /** + * @brief Constructs the parser and loads the entire file into memory. + * @param filename Path to the CSV file. + * @param delimiter Delimiter character used in the CSV (e.g. ',', ';'). + */ + CSMCCSVParser(const std::string& filename, char delimiter); + + /** + * @brief Starts parsing the CSV file using user-provided parsers and target vectors. + * @param field_bindings A vector of binding between fields metadata and destination targe containers + */ + void Parse(const std::vector& field_bindings); + + // Static parsing functions for specific data types + + static constexpr size_t TimestampDefaultInc = 10; + + /** + * @brief Adds a timestamp to the timestamp target vector. + * @param ts_target Pointer to the timestamp vector (std::vector*). + * @param ts Timestamp value to be added. + */ + static void PushTimestamp(void* ts_target, double ts); + + /** + * @brief Returns the last timestamp from the timestamp target vector. + * @param ts_target Pointer to the timestamp vector (std::vector*). + * @return The last timestamp value or 0.0 if the vector is empty. + */ + static double LastTimestamp(void* ts_target); + + + /** + * @brief Parses a timestamp string and stores it in the provided target. + * @param data Pointer to the character buffer. + * @param length Length of the buffer. + * @param target Pointer to the output target (std::time_t*). + * @param ts_target Optional pointer to the timestamp vector. + */ + static void ParseTimestamp(const char* data, size_t length, void* target, void* ts_target); + + /** + * @brief Parses a boolean value (e.g. '1', 'true') and stores it in the target vector. + * @param data Pointer to the character buffer. + * @param length Length of the buffer. + * @param target Pointer to the output vector (std::vector*). + * @param ts_target Optional pointer to the timestamp vector. + */ + static void ParseBool(const char* data, size_t length, void* target, void* ts_target); + + /** + * @brief Parses a string and stores it in the target vector. + * @param data Pointer to the character buffer. + * @param length Length of the buffer. + * @param target Pointer to the output vector (std::vector*). + * @param ts_target Optional pointer to the timestamp vector. + */ + static void ParseString(const char* data, size_t length, void* target, void* ts_target); + + /** + * @brief Generic value parser for numeric types (int, uint32_t, double). + * @tparam T The numeric type to parse. + * @param data Pointer to the character buffer. + * @param length Length of the buffer. + * @param target Pointer to the output vector (std::vector*). + * @param ts_target Optional pointer to the timestamp vector. + */ + template + static void ParseValue(const char* data, size_t length, void* target, void* ts_target); + + /** + * @brief Fills the vector with repeated last value to match the length of the timestamp vector. + * @tparam T The value type. + * @param target Pointer to the output vector (std::vector*). + * @param ts_target Pointer to the timestamp vector (std::vector*). + */ + template + static void ExtendVector(void* target, void* ts_target); + + /** + * @brief Interpolates values between two known points in time. + * @tparam T The value type. + * @param idx_from Start index in the timestamp vector. + * @param idx_to End index in the timestamp vector. + * @param target Pointer to the output vector (std::vector*). + * @param ts_target Pointer to the timestamp vector (std::vector*). + */ + template + static void InterpolateVector(size_t idx_from, size_t idx_to, void* target, void* ts_target); + + /** + * @brief Parses a complex laser signal structure and stores toggle states in the target vector. + * @param data Pointer to the character buffer. + * @param length Length of the buffer. + * @param target Pointer to the output vector (std::vector*). + * @param ts_target Pointer to the timestamp vector (std::vector*). + */ + static void ParseLaserSignal(const char* data, size_t length, void* target, void* ts_target); + + + /** + * @brief Formats a double with fixed precision and explicit sign. + * @param value Double value to format. + * @return Formatted string representation of the double. + */ + static std::string FormatDouble(double value); + + private: + /** + * @brief Handles comment lines (starting with '#'). + * This function can be customized to log, ignore or store comments. + */ + void ParseComment(const char* lineStart, size_t length); + + /** + * @brief Retrieves the appropriate parser function for a given field type. + * @param type The type of the field to be parsed. + * @return A function pointer to the corresponding parser. + */ + static CSMCCSVParser::ParserFunc GetParser(FieldParserType type); + + /** + * @brief Retrieves the extender function for the given field type. + * @param type The type of the field to extend. + * @return A function pointer to the corresponding extender. + */ + static CSMCCSVParser::ExtenderFunc GetExtender(FieldParserType type); + + /** + * @brief Retrieves the interpolation function for the given field type. + * @param type The type of the field to interpolate. + * @return A function pointer to the corresponding interpolator. + */ + static CSMCCSVParser::InterpolatorFunc GetInterpolator(FieldParserType type); + + /** + * @brief Represents a laser toggle sub-cycle with timing. + */ + struct SubCycle { + bool Toggle; ///< Toggle state (on/off). + double TimeOffset; ///< Time offset from the base timestamp. + }; + + /** + * @brief Internal parser for complex LaserSignal field with sub-cycle toggle encoding. + * @param data Pointer to the input character buffer. + * @param length Length of the buffer. + * @param target Pointer to the output vector of SubCycle. + * @param ts_target Pointer to the timestamp vector. + */ + static void ParseLaserSignal_Internal(const char* data, size_t length, void* target, void* ts_target); + + std::vector m_fileBuff; ///< Raw file contents held in memory + char m_delimiter; ///< CSV field delimiter character + size_t m_rowCount = 0; ///< Number of parsed data rows + }; + +} //namespace Impl +} //namespace LibMCDriver_ScanLabSMC + + +#endif // __LIBMCDRIVER_SCANLABSMC_SMCCSVPARSER \ No newline at end of file diff --git a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjob.cpp b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjob.cpp index 1295c8b1..e8a16804 100644 --- a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjob.cpp +++ b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjob.cpp @@ -132,6 +132,16 @@ void CSMCJob::LoadSimulationData(LibMCEnv::PDataTable pSimulationDataTable) m_pJobInstance->ReadSimulationFile(pSimulationDataTable); } +void CSMCJob::LoadSimulationData_SMC_v1(LibMCEnv::PDataTable pSimulationDataTable) +{ + m_pJobInstance->ReadSimulationFile_SMC_v1(pSimulationDataTable); +} + +void CSMCJob::LoadLogRecordData(LibMCEnv::PDataTable pLogRecordDataTable) +{ + m_pJobInstance->ReadLogRecordFile(pLogRecordDataTable); +} + LibMCDriver_ScanLabSMC_double CSMCJob::GetJobCharacteristic(const LibMCDriver_ScanLabSMC::eJobCharacteristic eValueType) { return m_pJobInstance->GetJobCharacteristic (eValueType); diff --git a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjob.hpp b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjob.hpp index acc75d6b..849edd0e 100644 --- a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjob.hpp +++ b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjob.hpp @@ -96,6 +96,10 @@ class CSMCJob : public virtual ISMCJob, public virtual CBase { void LoadSimulationData(LibMCEnv::PDataTable pSimulationDataTable) override; + void LoadSimulationData_SMC_v1(LibMCEnv::PDataTable pSimulationDataTable) override; + + void LoadLogRecordData(LibMCEnv::PDataTable pLogRecordDataTable) override; + LibMCDriver_ScanLabSMC_double GetJobCharacteristic(const LibMCDriver_ScanLabSMC::eJobCharacteristic eValueType) override; LibMCDriver_ScanLabSMC_double GetJobDuration() override; diff --git a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjobinstance.cpp b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjobinstance.cpp index 882424ea..0f2ca2cb 100644 --- a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjobinstance.cpp +++ b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjobinstance.cpp @@ -35,6 +35,7 @@ Abstract: This is a stub class definition of CSMCJob #include "libmcdriver_scanlabsmc_interfaceexception.hpp" #include "libmcdriver_scanlabsmc_sdk.hpp" #include "libmcdriver_scanlabsmc_smcsimulationparser.hpp" +#include "libmcdriver_scanlabsmc_smccsvparser.hpp" // Include custom headers here. #define SCANLABSMC_MICROSTEPSPERSECOND 100000 @@ -84,7 +85,12 @@ CSMCJobInstance::CSMCJobInstance(PSMCContextHandle pContextHandle, double dStart } // */ m_pSDK->checkError(contextHandle, m_pSDK->slsc_job_begin(contextHandle, &m_JobID)); + + + slsc_RecordSet eRecordSetA = slsc_RecordSet::slsc_RecordSet_SetPositions; + slsc_RecordSet eRecordSetB = slsc_RecordSet::slsc_RecordSet_LaserSwitches; + m_pSDK->checkError(contextHandle, m_pSDK->slsc_job_start_record(contextHandle, eRecordSetA, eRecordSetB)); } CSMCJobInstance::~CSMCJobInstance() @@ -104,6 +110,8 @@ void CSMCJobInstance::Finalize() auto contextHandle = m_pContextHandle->getHandle(); + m_pSDK->checkError(contextHandle, m_pSDK->slsc_job_stop_record(contextHandle)); + m_pSDK->checkError(contextHandle, m_pSDK->slsc_job_end(contextHandle)); m_bIsFinalized = true; } @@ -698,6 +706,144 @@ void CSMCJobInstance::ReadSimulationFile(LibMCEnv::PDataTable pDataTable) m_bHasJobDuration = true; } +void CSMCJobInstance::ReadSimulationFile_SMC_v1(LibMCEnv::PDataTable pDataTable) +{ + if (pDataTable.get() == nullptr) + throw ELibMCDriver_ScanLabSMCInterfaceException(LIBMCDRIVER_SCANLABSMC_ERROR_INVALIDPARAM); + + auto contextHandle = m_pContextHandle->getHandle(); + + std::vector buffer; + buffer.resize(16384); + m_pSDK->checkError(contextHandle, m_pSDK->slsc_ctrl_get_simulation_filename(contextHandle, m_JobID, buffer.data(), buffer.size())); + buffer.at(buffer.size() - 1) = 0; + + std::string sSimulationFileName(buffer.data()); + + std::string sSimulationDirectory = m_pWorkingDirectory->GetAbsoluteFilePath() + "/"; + if (!m_sSimulationSubDirectory.empty()) + sSimulationDirectory += m_sSimulationSubDirectory + "/"; + + CSMCCSVParser parser(sSimulationDirectory + sSimulationFileName, ';'); + + std::vector scanheadX; // DisplacedX_Galvo_1 + std::vector scanheadY; // DisplacedY_Galvo_1 + std::vector laserSignal; // LaserSignal + std::vector laserToggle; // LaserToggle + std::vector activeChannel0; // ActiveChannel0 + std::vector activeChannel1; // ActiveChannel1 + std::vector cmdCount; // CommandCount + std::vector triggerSignal; // TriggerSignal + std::vector dummy; // + std::vector timestampValues; // + + std::vector bindings = { + {{CSMCCSVParser::FieldParserType::Double, CSMCCSVParser::FieldProcessingStep::Extend | CSMCCSVParser::FieldProcessingStep::Interpolate }, &scanheadX}, + {{CSMCCSVParser::FieldParserType::Double, CSMCCSVParser::FieldProcessingStep::Extend | CSMCCSVParser::FieldProcessingStep::Interpolate }, &scanheadY}, + {{CSMCCSVParser::FieldParserType::LaserSignal,CSMCCSVParser::FieldProcessingStep::Nop}, &laserSignal}, + {{CSMCCSVParser::FieldParserType::Bool,CSMCCSVParser::FieldProcessingStep::Nop}, &laserToggle}, + {{CSMCCSVParser::FieldParserType::None,CSMCCSVParser::FieldProcessingStep::Nop}, nullptr}, + {{CSMCCSVParser::FieldParserType::None,CSMCCSVParser::FieldProcessingStep::Nop}, nullptr}, + {{CSMCCSVParser::FieldParserType::Int,CSMCCSVParser::FieldProcessingStep::Nop}, &cmdCount}, + {{CSMCCSVParser::FieldParserType::None,CSMCCSVParser::FieldProcessingStep::Nop}, nullptr}, + {{CSMCCSVParser::FieldParserType::None,CSMCCSVParser::FieldProcessingStep::Nop}, nullptr}, + {{CSMCCSVParser::FieldParserType::Timestamp,CSMCCSVParser::FieldProcessingStep::Nop}, ×tampValues} + }; + + parser.Parse(bindings); + +#if NOT_IMPLEMENTED + const auto& headers = parser.getHeader(); + const auto& types = parser.getColumnTypes(); + const auto& rows = parser.getRows(); +#endif + + pDataTable->AddColumn("timestamp", "Timestamp", LibMCEnv::eDataTableColumnType::DoubleColumn); + pDataTable->AddColumn("x", "X", LibMCEnv::eDataTableColumnType::DoubleColumn); + pDataTable->AddColumn("y", "Y", LibMCEnv::eDataTableColumnType::DoubleColumn); + pDataTable->AddColumn("laseron", "LaserOn", LibMCEnv::eDataTableColumnType::Int32Column); + pDataTable->AddColumn("active1", "Active Channel 1", LibMCEnv::eDataTableColumnType::DoubleColumn); + pDataTable->AddColumn("active2", "Active Channel 2", LibMCEnv::eDataTableColumnType::DoubleColumn); + pDataTable->AddColumn("cmdindex", "Command Index", LibMCEnv::eDataTableColumnType::Int32Column); + + m_dJobDuration = (double)timestampValues.size() / (double)SCANLABSMC_MICROSTEPSPERSECOND; + m_bHasJobDuration = true; + + pDataTable->SetDoubleColumnValues("timestamp", timestampValues); + timestampValues.resize(0); + + pDataTable->SetDoubleColumnValues("x", scanheadX); + scanheadX.resize(0); + + pDataTable->SetDoubleColumnValues("y", scanheadY); + scanheadY.resize(0); +} + +void CSMCJobInstance::ReadLogRecordFile(LibMCEnv::PDataTable pDataTable) +{ + if (pDataTable.get() == nullptr) + throw ELibMCDriver_ScanLabSMCInterfaceException(LIBMCDRIVER_SCANLABSMC_ERROR_INVALIDPARAM); + + auto contextHandle = m_pContextHandle->getHandle(); + + auto pLogRecordFile = m_pWorkingDirectory->AddManagedTempFile("csv"); + + //if(!pLogRecordFile->FileExists()) + // throw ELibMCDriver_ScanLabSMCInterfaceException(LIBMCDRIVER_SCANLABSMC_ERROR_INVALIDPARAM); + + auto sLogRecordAbsoluteFileName = pLogRecordFile->GetAbsoluteFileName(); + + //auto eTransformationStep = slsc_TransformationStep::slsc_TransformationStep_Rtc; // needs scaling and rotation + auto eTransformationStep = slsc_TransformationStep::slsc_TransformationStep_Corrected; + + m_pSDK->slsc_ctrl_log_record(m_pContextHandle->getHandle(), sLogRecordAbsoluteFileName.c_str(), eTransformationStep); + + //----------------------------------- + + CSMCCSVParser parser(sLogRecordAbsoluteFileName, ';'); + + std::vector timestampValues; + std::vector scanheadX; + std::vector scanheadY; + std::vector laserSignal; + + std::vector bindings = { + {{CSMCCSVParser::FieldParserType::Double, CSMCCSVParser::FieldProcessingStep::Extend | CSMCCSVParser::FieldProcessingStep::Interpolate }, &scanheadX}, + {{CSMCCSVParser::FieldParserType::Double, CSMCCSVParser::FieldProcessingStep::Extend | CSMCCSVParser::FieldProcessingStep::Interpolate }, &scanheadY}, + {{CSMCCSVParser::FieldParserType::LaserSignal,CSMCCSVParser::FieldProcessingStep::Nop}, &laserSignal}, + {{CSMCCSVParser::FieldParserType::Timestamp,CSMCCSVParser::FieldProcessingStep::Nop}, ×tampValues} + }; + + auto start = std::chrono::high_resolution_clock::now(); + + parser.Parse(bindings); + +#if NOT_IMPLEMENTED + const auto& headers = parser.getHeader(); + const auto& types = parser.getColumnTypes(); + const auto& rows = parser.getRows(); +#endif + + pDataTable->AddColumn("timestamp", "Timestamp", LibMCEnv::eDataTableColumnType::DoubleColumn); + pDataTable->AddColumn("x", "X", LibMCEnv::eDataTableColumnType::DoubleColumn); + pDataTable->AddColumn("y", "Y", LibMCEnv::eDataTableColumnType::DoubleColumn); + pDataTable->AddColumn("laseron", "LaserOn", LibMCEnv::eDataTableColumnType::Int32Column); + pDataTable->AddColumn("active1", "Active Channel 1", LibMCEnv::eDataTableColumnType::DoubleColumn); + pDataTable->AddColumn("active2", "Active Channel 2", LibMCEnv::eDataTableColumnType::DoubleColumn); + pDataTable->AddColumn("cmdindex", "Command Index", LibMCEnv::eDataTableColumnType::Int32Column); + + m_dJobDuration = (double)timestampValues.size() / (double)SCANLABSMC_MICROSTEPSPERSECOND; + m_bHasJobDuration = true; + + pDataTable->SetDoubleColumnValues("timestamp", timestampValues); + timestampValues.resize(0); + + pDataTable->SetDoubleColumnValues("x", scanheadX); + scanheadX.resize(0); + + pDataTable->SetDoubleColumnValues("y", scanheadY); + scanheadY.resize(0); +} void CSMCJobInstance::AddLayerToList(LibMCEnv::PToolpathLayer pLayer) { diff --git a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjobinstance.hpp b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjobinstance.hpp index f1dcbaa4..9b7348aa 100644 --- a/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjobinstance.hpp +++ b/Drivers/ScanLabSMC/Implementation/libmcdriver_scanlabsmc_smcjobinstance.hpp @@ -98,7 +98,11 @@ class CSMCJobInstance { void AddLayerToList(LibMCEnv::PToolpathLayer pLayer); - void ReadSimulationFile (LibMCEnv::PDataTable pDataTable); + void ReadSimulationFile(LibMCEnv::PDataTable pDataTable); + + void ReadSimulationFile_SMC_v1(LibMCEnv::PDataTable pDataTable); + + void ReadLogRecordFile(LibMCEnv::PDataTable pDataTable); double GetJobCharacteristic(const LibMCDriver_ScanLabSMC::eJobCharacteristic eValueType); diff --git a/Drivers/ScanLabSMC/Interfaces/libmcdriver_scanlabsmc_abi.hpp b/Drivers/ScanLabSMC/Interfaces/libmcdriver_scanlabsmc_abi.hpp index 5c551adb..b0cb9176 100644 --- a/Drivers/ScanLabSMC/Interfaces/libmcdriver_scanlabsmc_abi.hpp +++ b/Drivers/ScanLabSMC/Interfaces/libmcdriver_scanlabsmc_abi.hpp @@ -265,6 +265,24 @@ LIBMCDRIVER_SCANLABSMC_DECLSPEC LibMCDriver_ScanLabSMCResult libmcdriver_scanlab */ LIBMCDRIVER_SCANLABSMC_DECLSPEC LibMCDriver_ScanLabSMCResult libmcdriver_scanlabsmc_smcjob_loadsimulationdata(LibMCDriver_ScanLabSMC_SMCJob pSMCJob, LibMCEnv_DataTable pSimulationDataTable); +/** +* Reads the SMC Simulation data into a data table. +* +* @param[in] pSMCJob - SMCJob instance. +* @param[in] pSimulationDataTable - Data table object to read the simulation into. +* @return error code or 0 (success) +*/ +LIBMCDRIVER_SCANLABSMC_DECLSPEC LibMCDriver_ScanLabSMCResult libmcdriver_scanlabsmc_smcjob_loadsimulationdata_smc_v1(LibMCDriver_ScanLabSMC_SMCJob pSMCJob, LibMCEnv_DataTable pSimulationDataTable); + +/** +* Reads the SMC Log Record data into a data table. +* +* @param[in] pSMCJob - SMCJob instance. +* @param[in] pLogRecordDataTable - Data table object to read the simulation into. +* @return error code or 0 (success) +*/ +LIBMCDRIVER_SCANLABSMC_DECLSPEC LibMCDriver_ScanLabSMCResult libmcdriver_scanlabsmc_smcjob_loadlogrecorddata(LibMCDriver_ScanLabSMC_SMCJob pSMCJob, LibMCEnv_DataTable pLogRecordDataTable); + /** * Returns a characteristic value of a job. * diff --git a/Drivers/ScanLabSMC/Interfaces/libmcdriver_scanlabsmc_interfaces.hpp b/Drivers/ScanLabSMC/Interfaces/libmcdriver_scanlabsmc_interfaces.hpp index 0da4bb34..fd0301a9 100644 --- a/Drivers/ScanLabSMC/Interfaces/libmcdriver_scanlabsmc_interfaces.hpp +++ b/Drivers/ScanLabSMC/Interfaces/libmcdriver_scanlabsmc_interfaces.hpp @@ -435,6 +435,18 @@ class ISMCJob : public virtual IBase { */ virtual void LoadSimulationData(LibMCEnv::PDataTable pSimulationDataTable) = 0; + /** + * ISMCJob::LoadSimulationData_SMC_v1 - Reads the SMC Simulation data into a data table. + * @param[in] pSimulationDataTable - Data table object to read the simulation into. + */ + virtual void LoadSimulationData_SMC_v1(LibMCEnv::PDataTable pSimulationDataTable) = 0; + + /** + * ISMCJob::LoadLogRecordData - Reads the SMC Log Record data into a data table. + * @param[in] pLogRecordDataTable - Data table object to read the simulation into. + */ + virtual void LoadLogRecordData(LibMCEnv::PDataTable pLogRecordDataTable) = 0; + /** * ISMCJob::GetJobCharacteristic - Returns a characteristic value of a job. * @param[in] eValueType - Type of job diff --git a/Drivers/ScanLabSMC/Interfaces/libmcdriver_scanlabsmc_interfacewrapper.cpp b/Drivers/ScanLabSMC/Interfaces/libmcdriver_scanlabsmc_interfacewrapper.cpp index 4a287526..5db66160 100644 --- a/Drivers/ScanLabSMC/Interfaces/libmcdriver_scanlabsmc_interfacewrapper.cpp +++ b/Drivers/ScanLabSMC/Interfaces/libmcdriver_scanlabsmc_interfacewrapper.cpp @@ -653,6 +653,64 @@ LibMCDriver_ScanLabSMCResult libmcdriver_scanlabsmc_smcjob_loadsimulationdata(Li } } +LibMCDriver_ScanLabSMCResult libmcdriver_scanlabsmc_smcjob_loadsimulationdata_smc_v1(LibMCDriver_ScanLabSMC_SMCJob pSMCJob, LibMCEnv_DataTable pSimulationDataTable) +{ + IBase* pIBaseClass = (IBase *)pSMCJob; + + try { + LibMCEnv::PDataTable pISimulationDataTable = std::make_shared(CWrapper::sPLibMCEnvWrapper.get(), pSimulationDataTable); + CWrapper::sPLibMCEnvWrapper->AcquireInstance(pISimulationDataTable.get()); + if (!pISimulationDataTable) + throw ELibMCDriver_ScanLabSMCInterfaceException (LIBMCDRIVER_SCANLABSMC_ERROR_INVALIDCAST); + + ISMCJob* pISMCJob = dynamic_cast(pIBaseClass); + if (!pISMCJob) + throw ELibMCDriver_ScanLabSMCInterfaceException(LIBMCDRIVER_SCANLABSMC_ERROR_INVALIDCAST); + + pISMCJob->LoadSimulationData_SMC_v1(pISimulationDataTable); + + return LIBMCDRIVER_SCANLABSMC_SUCCESS; + } + catch (ELibMCDriver_ScanLabSMCInterfaceException & Exception) { + return handleLibMCDriver_ScanLabSMCException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + +LibMCDriver_ScanLabSMCResult libmcdriver_scanlabsmc_smcjob_loadlogrecorddata(LibMCDriver_ScanLabSMC_SMCJob pSMCJob, LibMCEnv_DataTable pLogRecordDataTable) +{ + IBase* pIBaseClass = (IBase *)pSMCJob; + + try { + LibMCEnv::PDataTable pILogRecordDataTable = std::make_shared(CWrapper::sPLibMCEnvWrapper.get(), pLogRecordDataTable); + CWrapper::sPLibMCEnvWrapper->AcquireInstance(pILogRecordDataTable.get()); + if (!pILogRecordDataTable) + throw ELibMCDriver_ScanLabSMCInterfaceException (LIBMCDRIVER_SCANLABSMC_ERROR_INVALIDCAST); + + ISMCJob* pISMCJob = dynamic_cast(pIBaseClass); + if (!pISMCJob) + throw ELibMCDriver_ScanLabSMCInterfaceException(LIBMCDRIVER_SCANLABSMC_ERROR_INVALIDCAST); + + pISMCJob->LoadLogRecordData(pILogRecordDataTable); + + return LIBMCDRIVER_SCANLABSMC_SUCCESS; + } + catch (ELibMCDriver_ScanLabSMCInterfaceException & Exception) { + return handleLibMCDriver_ScanLabSMCException(pIBaseClass, Exception); + } + catch (std::exception & StdException) { + return handleStdException(pIBaseClass, StdException); + } + catch (...) { + return handleUnhandledException(pIBaseClass); + } +} + LibMCDriver_ScanLabSMCResult libmcdriver_scanlabsmc_smcjob_getjobcharacteristic(LibMCDriver_ScanLabSMC_SMCJob pSMCJob, eLibMCDriver_ScanLabSMCJobCharacteristic eValueType, LibMCDriver_ScanLabSMC_double * pValue) { IBase* pIBaseClass = (IBase *)pSMCJob; @@ -2197,6 +2255,10 @@ LibMCDriver_ScanLabSMCResult LibMCDriver_ScanLabSMC::Impl::LibMCDriver_ScanLabSM *ppProcAddress = (void*) &libmcdriver_scanlabsmc_smcjob_stopexecution; if (sProcName == "libmcdriver_scanlabsmc_smcjob_loadsimulationdata") *ppProcAddress = (void*) &libmcdriver_scanlabsmc_smcjob_loadsimulationdata; + if (sProcName == "libmcdriver_scanlabsmc_smcjob_loadsimulationdata_smc_v1") + *ppProcAddress = (void*) &libmcdriver_scanlabsmc_smcjob_loadsimulationdata_smc_v1; + if (sProcName == "libmcdriver_scanlabsmc_smcjob_loadlogrecorddata") + *ppProcAddress = (void*) &libmcdriver_scanlabsmc_smcjob_loadlogrecorddata; if (sProcName == "libmcdriver_scanlabsmc_smcjob_getjobcharacteristic") *ppProcAddress = (void*) &libmcdriver_scanlabsmc_smcjob_getjobcharacteristic; if (sProcName == "libmcdriver_scanlabsmc_smcjob_getjobduration")