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")