diff --git a/ACT/LibMC.xml b/ACT/LibMC.xml
index 3a8ee1dd..edb14ca9 100644
--- a/ACT/LibMC.xml
+++ b/ACT/LibMC.xml
@@ -740,6 +740,7 @@
+
diff --git a/ACT/LibMCData.xml b/ACT/LibMCData.xml
index 922fbba6..dc5583b3 100644
--- a/ACT/LibMCData.xml
+++ b/ACT/LibMCData.xml
@@ -452,6 +452,7 @@
+
@@ -2291,8 +2292,16 @@
+
+
+
+
+
+
+
+
-
+
diff --git a/Framework/HeadersCore/CppDynamic/libmc_dynamic.hpp b/Framework/HeadersCore/CppDynamic/libmc_dynamic.hpp
index 1a480f93..360065b4 100644
--- a/Framework/HeadersCore/CppDynamic/libmc_dynamic.hpp
+++ b/Framework/HeadersCore/CppDynamic/libmc_dynamic.hpp
@@ -809,6 +809,7 @@ class ELibMCException : public std::exception {
case LIBMC_ERROR_MISSINGSESSIONVARIABLENAME: return "MISSINGSESSIONVARIABLENAME";
case LIBMC_ERROR_MISSINGSESSIONVARIABLETYPE: return "MISSINGSESSIONVARIABLETYPE";
case LIBMC_ERROR_INVALIDSESSIONREFERENCE: return "INVALIDSESSIONREFERENCE";
+ case LIBMC_ERROR_INVALIDTELEMETRYSETTING: return "INVALIDTELEMETRYSETTING";
}
return "UNKNOWN";
}
@@ -1452,6 +1453,7 @@ class ELibMCException : public std::exception {
case LIBMC_ERROR_MISSINGSESSIONVARIABLENAME: return "Missing session variable name.";
case LIBMC_ERROR_MISSINGSESSIONVARIABLETYPE: return "Missing session variable type.";
case LIBMC_ERROR_INVALIDSESSIONREFERENCE: return "Invalid session reference in UI expression.";
+ case LIBMC_ERROR_INVALIDTELEMETRYSETTING: return "Invalid telemetry setting.";
}
return "unknown error";
}
diff --git a/Framework/HeadersCore/CppDynamic/libmc_types.hpp b/Framework/HeadersCore/CppDynamic/libmc_types.hpp
index a81eb9ca..de838335 100644
--- a/Framework/HeadersCore/CppDynamic/libmc_types.hpp
+++ b/Framework/HeadersCore/CppDynamic/libmc_types.hpp
@@ -731,6 +731,7 @@ typedef void * LibMC_pvoid;
#define LIBMC_ERROR_MISSINGSESSIONVARIABLENAME 715 /** Missing session variable name. */
#define LIBMC_ERROR_MISSINGSESSIONVARIABLETYPE 716 /** Missing session variable type. */
#define LIBMC_ERROR_INVALIDSESSIONREFERENCE 717 /** Invalid session reference in UI expression. */
+#define LIBMC_ERROR_INVALIDTELEMETRYSETTING 718 /** Invalid telemetry setting. */
/*************************************************************************************************************************
Error strings for LibMC
@@ -1374,6 +1375,7 @@ inline const char * LIBMC_GETERRORSTRING (LibMCResult nErrorCode) {
case LIBMC_ERROR_MISSINGSESSIONVARIABLENAME: return "Missing session variable name.";
case LIBMC_ERROR_MISSINGSESSIONVARIABLETYPE: return "Missing session variable type.";
case LIBMC_ERROR_INVALIDSESSIONREFERENCE: return "Invalid session reference in UI expression.";
+ case LIBMC_ERROR_INVALIDTELEMETRYSETTING: return "Invalid telemetry setting.";
default: return "unknown error";
}
}
diff --git a/Framework/HeadersCore/CppDynamic/libmcdata_dynamic.h b/Framework/HeadersCore/CppDynamic/libmcdata_dynamic.h
index 09ab4e9d..d8b18fdf 100644
--- a/Framework/HeadersCore/CppDynamic/libmcdata_dynamic.h
+++ b/Framework/HeadersCore/CppDynamic/libmcdata_dynamic.h
@@ -3420,6 +3420,24 @@ typedef LibMCDataResult (*PLibMCDataInstallationInformation_GetBaseTempDirectory
*/
typedef LibMCDataResult (*PLibMCDataDataModel_InitialiseDatabasePtr) (LibMCData_DataModel pDataModel, const char * pDataDirectory, LibMCData::eDataBaseType eDataBaseType, const char * pConnectionString);
+/**
+* enables or disables telemetry recording. Must be called before InitialiseDatabase.
+*
+* @param[in] pDataModel - DataModel instance.
+* @param[in] bTelemetryEnabled - Flag if telemetry recording shall be enabled.
+* @return error code or 0 (success)
+*/
+typedef LibMCDataResult (*PLibMCDataDataModel_SetTelemetryEnabledPtr) (LibMCData_DataModel pDataModel, bool bTelemetryEnabled);
+
+/**
+* returns if telemetry recording is enabled.
+*
+* @param[in] pDataModel - DataModel instance.
+* @param[out] pTelemetryEnabled - Flag if telemetry recording is enabled.
+* @return error code or 0 (success)
+*/
+typedef LibMCDataResult (*PLibMCDataDataModel_GetTelemetryEnabledPtr) (LibMCData_DataModel pDataModel, bool * pTelemetryEnabled);
+
/**
* returns the linear data model version.
*
@@ -4021,6 +4039,8 @@ typedef struct {
PLibMCDataInstallationInformation_GetInstallationSecretPtr m_InstallationInformation_GetInstallationSecret;
PLibMCDataInstallationInformation_GetBaseTempDirectoryPtr m_InstallationInformation_GetBaseTempDirectory;
PLibMCDataDataModel_InitialiseDatabasePtr m_DataModel_InitialiseDatabase;
+ PLibMCDataDataModel_SetTelemetryEnabledPtr m_DataModel_SetTelemetryEnabled;
+ PLibMCDataDataModel_GetTelemetryEnabledPtr m_DataModel_GetTelemetryEnabled;
PLibMCDataDataModel_GetDataModelVersionPtr m_DataModel_GetDataModelVersion;
PLibMCDataDataModel_GetInstallationInformationPtr m_DataModel_GetInstallationInformation;
PLibMCDataDataModel_GetInstallationInformationObjectPtr m_DataModel_GetInstallationInformationObject;
diff --git a/Framework/HeadersCore/CppDynamic/libmcdata_dynamic.hpp b/Framework/HeadersCore/CppDynamic/libmcdata_dynamic.hpp
index 8c70f9b0..3eb1bab7 100644
--- a/Framework/HeadersCore/CppDynamic/libmcdata_dynamic.hpp
+++ b/Framework/HeadersCore/CppDynamic/libmcdata_dynamic.hpp
@@ -690,6 +690,7 @@ class ELibMCDataException : public std::exception {
case LIBMCDATA_ERROR_TELEMETRYCHANNELNOTFOUND: return "TELEMETRYCHANNELNOTFOUND";
case LIBMCDATA_ERROR_JOBNAMETOOLONG: return "JOBNAMETOOLONG";
case LIBMCDATA_ERROR_STRINGPARAMETERTOOLONG: return "STRINGPARAMETERTOOLONG";
+ case LIBMCDATA_ERROR_TELEMETRYSETTINGAFTERINITIALISATION: return "TELEMETRYSETTINGAFTERINITIALISATION";
}
return "UNKNOWN";
}
@@ -1079,6 +1080,7 @@ class ELibMCDataException : public std::exception {
case LIBMCDATA_ERROR_TELEMETRYCHANNELNOTFOUND: return "Telemetry channel not found.";
case LIBMCDATA_ERROR_JOBNAMETOOLONG: return "Job name exceeds maximum allowed length";
case LIBMCDATA_ERROR_STRINGPARAMETERTOOLONG: return "String parameter exceeds maximum allowed length";
+ case LIBMCDATA_ERROR_TELEMETRYSETTINGAFTERINITIALISATION: return "Telemetry setting changed after database initialisation";
}
return "unknown error";
}
@@ -2197,6 +2199,8 @@ class CDataModel : public CBase {
}
inline void InitialiseDatabase(const std::string & sDataDirectory, const eDataBaseType eDataBaseType, const std::string & sConnectionString);
+ inline void SetTelemetryEnabled(const bool bTelemetryEnabled);
+ inline bool GetTelemetryEnabled();
inline LibMCData_uint32 GetDataModelVersion();
inline void GetInstallationInformation(std::string & sDEPRECIATEDInstallationUUID, std::string & sDEPRECIATEDInstallationSecret);
inline PInstallationInformation GetInstallationInformationObject();
@@ -2616,6 +2620,8 @@ class CDataModel : public CBase {
pWrapperTable->m_InstallationInformation_GetInstallationSecret = nullptr;
pWrapperTable->m_InstallationInformation_GetBaseTempDirectory = nullptr;
pWrapperTable->m_DataModel_InitialiseDatabase = nullptr;
+ pWrapperTable->m_DataModel_SetTelemetryEnabled = nullptr;
+ pWrapperTable->m_DataModel_GetTelemetryEnabled = nullptr;
pWrapperTable->m_DataModel_GetDataModelVersion = nullptr;
pWrapperTable->m_DataModel_GetInstallationInformation = nullptr;
pWrapperTable->m_DataModel_GetInstallationInformationObject = nullptr;
@@ -5361,6 +5367,24 @@ class CDataModel : public CBase {
if (pWrapperTable->m_DataModel_InitialiseDatabase == nullptr)
return LIBMCDATA_ERROR_COULDNOTFINDLIBRARYEXPORT;
+ #ifdef _WIN32
+ pWrapperTable->m_DataModel_SetTelemetryEnabled = (PLibMCDataDataModel_SetTelemetryEnabledPtr) GetProcAddress(hLibrary, "libmcdata_datamodel_settelemetryenabled");
+ #else // _WIN32
+ pWrapperTable->m_DataModel_SetTelemetryEnabled = (PLibMCDataDataModel_SetTelemetryEnabledPtr) dlsym(hLibrary, "libmcdata_datamodel_settelemetryenabled");
+ dlerror();
+ #endif // _WIN32
+ if (pWrapperTable->m_DataModel_SetTelemetryEnabled == nullptr)
+ return LIBMCDATA_ERROR_COULDNOTFINDLIBRARYEXPORT;
+
+ #ifdef _WIN32
+ pWrapperTable->m_DataModel_GetTelemetryEnabled = (PLibMCDataDataModel_GetTelemetryEnabledPtr) GetProcAddress(hLibrary, "libmcdata_datamodel_gettelemetryenabled");
+ #else // _WIN32
+ pWrapperTable->m_DataModel_GetTelemetryEnabled = (PLibMCDataDataModel_GetTelemetryEnabledPtr) dlsym(hLibrary, "libmcdata_datamodel_gettelemetryenabled");
+ dlerror();
+ #endif // _WIN32
+ if (pWrapperTable->m_DataModel_GetTelemetryEnabled == nullptr)
+ return LIBMCDATA_ERROR_COULDNOTFINDLIBRARYEXPORT;
+
#ifdef _WIN32
pWrapperTable->m_DataModel_GetDataModelVersion = (PLibMCDataDataModel_GetDataModelVersionPtr) GetProcAddress(hLibrary, "libmcdata_datamodel_getdatamodelversion");
#else // _WIN32
@@ -6840,6 +6864,14 @@ class CDataModel : public CBase {
if ( (eLookupError != 0) || (pWrapperTable->m_DataModel_InitialiseDatabase == nullptr) )
return LIBMCDATA_ERROR_COULDNOTFINDLIBRARYEXPORT;
+ eLookupError = (*pLookup)("libmcdata_datamodel_settelemetryenabled", (void**)&(pWrapperTable->m_DataModel_SetTelemetryEnabled));
+ if ( (eLookupError != 0) || (pWrapperTable->m_DataModel_SetTelemetryEnabled == nullptr) )
+ return LIBMCDATA_ERROR_COULDNOTFINDLIBRARYEXPORT;
+
+ eLookupError = (*pLookup)("libmcdata_datamodel_gettelemetryenabled", (void**)&(pWrapperTable->m_DataModel_GetTelemetryEnabled));
+ if ( (eLookupError != 0) || (pWrapperTable->m_DataModel_GetTelemetryEnabled == nullptr) )
+ return LIBMCDATA_ERROR_COULDNOTFINDLIBRARYEXPORT;
+
eLookupError = (*pLookup)("libmcdata_datamodel_getdatamodelversion", (void**)&(pWrapperTable->m_DataModel_GetDataModelVersion));
if ( (eLookupError != 0) || (pWrapperTable->m_DataModel_GetDataModelVersion == nullptr) )
return LIBMCDATA_ERROR_COULDNOTFINDLIBRARYEXPORT;
@@ -11348,6 +11380,27 @@ class CDataModel : public CBase {
CheckError(m_pWrapper->m_WrapperTable.m_DataModel_InitialiseDatabase(m_pHandle, sDataDirectory.c_str(), eDataBaseType, sConnectionString.c_str()));
}
+ /**
+ * CDataModel::SetTelemetryEnabled - enables or disables telemetry recording. Must be called before InitialiseDatabase.
+ * @param[in] bTelemetryEnabled - Flag if telemetry recording shall be enabled.
+ */
+ void CDataModel::SetTelemetryEnabled(const bool bTelemetryEnabled)
+ {
+ CheckError(m_pWrapper->m_WrapperTable.m_DataModel_SetTelemetryEnabled(m_pHandle, bTelemetryEnabled));
+ }
+
+ /**
+ * CDataModel::GetTelemetryEnabled - returns if telemetry recording is enabled.
+ * @return Flag if telemetry recording is enabled.
+ */
+ bool CDataModel::GetTelemetryEnabled()
+ {
+ bool resultTelemetryEnabled = 0;
+ CheckError(m_pWrapper->m_WrapperTable.m_DataModel_GetTelemetryEnabled(m_pHandle, &resultTelemetryEnabled));
+
+ return resultTelemetryEnabled;
+ }
+
/**
* CDataModel::GetDataModelVersion - returns the linear data model version.
* @return Data model version.
diff --git a/Framework/HeadersCore/CppDynamic/libmcdata_types.hpp b/Framework/HeadersCore/CppDynamic/libmcdata_types.hpp
index 19bdf40c..a2d8e0c6 100644
--- a/Framework/HeadersCore/CppDynamic/libmcdata_types.hpp
+++ b/Framework/HeadersCore/CppDynamic/libmcdata_types.hpp
@@ -477,6 +477,7 @@ typedef void * LibMCData_pvoid;
#define LIBMCDATA_ERROR_TELEMETRYCHANNELNOTFOUND 450 /** Telemetry channel not found. */
#define LIBMCDATA_ERROR_JOBNAMETOOLONG 451 /** Job name exceeds maximum allowed length */
#define LIBMCDATA_ERROR_STRINGPARAMETERTOOLONG 452 /** String parameter exceeds maximum allowed length */
+#define LIBMCDATA_ERROR_TELEMETRYSETTINGAFTERINITIALISATION 453 /** Telemetry setting changed after database initialisation */
/*************************************************************************************************************************
Error strings for LibMCData
@@ -866,6 +867,7 @@ inline const char * LIBMCDATA_GETERRORSTRING (LibMCDataResult nErrorCode) {
case LIBMCDATA_ERROR_TELEMETRYCHANNELNOTFOUND: return "Telemetry channel not found.";
case LIBMCDATA_ERROR_JOBNAMETOOLONG: return "Job name exceeds maximum allowed length";
case LIBMCDATA_ERROR_STRINGPARAMETERTOOLONG: return "String parameter exceeds maximum allowed length";
+ case LIBMCDATA_ERROR_TELEMETRYSETTINGAFTERINITIALISATION: return "Telemetry setting changed after database initialisation";
default: return "unknown error";
}
}
diff --git a/Framework/InterfacesCore/libmc_types.hpp b/Framework/InterfacesCore/libmc_types.hpp
index a81eb9ca..de838335 100644
--- a/Framework/InterfacesCore/libmc_types.hpp
+++ b/Framework/InterfacesCore/libmc_types.hpp
@@ -731,6 +731,7 @@ typedef void * LibMC_pvoid;
#define LIBMC_ERROR_MISSINGSESSIONVARIABLENAME 715 /** Missing session variable name. */
#define LIBMC_ERROR_MISSINGSESSIONVARIABLETYPE 716 /** Missing session variable type. */
#define LIBMC_ERROR_INVALIDSESSIONREFERENCE 717 /** Invalid session reference in UI expression. */
+#define LIBMC_ERROR_INVALIDTELEMETRYSETTING 718 /** Invalid telemetry setting. */
/*************************************************************************************************************************
Error strings for LibMC
@@ -1374,6 +1375,7 @@ inline const char * LIBMC_GETERRORSTRING (LibMCResult nErrorCode) {
case LIBMC_ERROR_MISSINGSESSIONVARIABLENAME: return "Missing session variable name.";
case LIBMC_ERROR_MISSINGSESSIONVARIABLETYPE: return "Missing session variable type.";
case LIBMC_ERROR_INVALIDSESSIONREFERENCE: return "Invalid session reference in UI expression.";
+ case LIBMC_ERROR_INVALIDTELEMETRYSETTING: return "Invalid telemetry setting.";
default: return "unknown error";
}
}
diff --git a/Framework/InterfacesCore/libmcdata_abi.hpp b/Framework/InterfacesCore/libmcdata_abi.hpp
index 5c81d31f..98e1f41e 100644
--- a/Framework/InterfacesCore/libmcdata_abi.hpp
+++ b/Framework/InterfacesCore/libmcdata_abi.hpp
@@ -3433,6 +3433,24 @@ LIBMCDATA_DECLSPEC LibMCDataResult libmcdata_installationinformation_getbasetemp
*/
LIBMCDATA_DECLSPEC LibMCDataResult libmcdata_datamodel_initialisedatabase(LibMCData_DataModel pDataModel, const char * pDataDirectory, LibMCData::eDataBaseType eDataBaseType, const char * pConnectionString);
+/**
+* enables or disables telemetry recording. Must be called before InitialiseDatabase.
+*
+* @param[in] pDataModel - DataModel instance.
+* @param[in] bTelemetryEnabled - Flag if telemetry recording shall be enabled.
+* @return error code or 0 (success)
+*/
+LIBMCDATA_DECLSPEC LibMCDataResult libmcdata_datamodel_settelemetryenabled(LibMCData_DataModel pDataModel, bool bTelemetryEnabled);
+
+/**
+* returns if telemetry recording is enabled.
+*
+* @param[in] pDataModel - DataModel instance.
+* @param[out] pTelemetryEnabled - Flag if telemetry recording is enabled.
+* @return error code or 0 (success)
+*/
+LIBMCDATA_DECLSPEC LibMCDataResult libmcdata_datamodel_gettelemetryenabled(LibMCData_DataModel pDataModel, bool * pTelemetryEnabled);
+
/**
* returns the linear data model version.
*
diff --git a/Framework/InterfacesCore/libmcdata_interfaces.hpp b/Framework/InterfacesCore/libmcdata_interfaces.hpp
index 0b7cdcb0..5d52dca6 100644
--- a/Framework/InterfacesCore/libmcdata_interfaces.hpp
+++ b/Framework/InterfacesCore/libmcdata_interfaces.hpp
@@ -2854,6 +2854,18 @@ class IDataModel : public virtual IBase {
*/
virtual void InitialiseDatabase(const std::string & sDataDirectory, const LibMCData::eDataBaseType eDataBaseType, const std::string & sConnectionString) = 0;
+ /**
+ * IDataModel::SetTelemetryEnabled - enables or disables telemetry recording. Must be called before InitialiseDatabase.
+ * @param[in] bTelemetryEnabled - Flag if telemetry recording shall be enabled.
+ */
+ virtual void SetTelemetryEnabled(const bool bTelemetryEnabled) = 0;
+
+ /**
+ * IDataModel::GetTelemetryEnabled - returns if telemetry recording is enabled.
+ * @return Flag if telemetry recording is enabled.
+ */
+ virtual bool GetTelemetryEnabled() = 0;
+
/**
* IDataModel::GetDataModelVersion - returns the linear data model version.
* @return Data model version.
diff --git a/Framework/InterfacesCore/libmcdata_interfacewrapper.cpp b/Framework/InterfacesCore/libmcdata_interfacewrapper.cpp
index 59a379cb..92ad92f6 100644
--- a/Framework/InterfacesCore/libmcdata_interfacewrapper.cpp
+++ b/Framework/InterfacesCore/libmcdata_interfacewrapper.cpp
@@ -10938,6 +10938,56 @@ LibMCDataResult libmcdata_datamodel_initialisedatabase(LibMCData_DataModel pData
}
}
+LibMCDataResult libmcdata_datamodel_settelemetryenabled(LibMCData_DataModel pDataModel, bool bTelemetryEnabled)
+{
+ IBase* pIBaseClass = (IBase *)pDataModel;
+
+ try {
+ IDataModel* pIDataModel = dynamic_cast(pIBaseClass);
+ if (!pIDataModel)
+ throw ELibMCDataInterfaceException(LIBMCDATA_ERROR_INVALIDCAST);
+
+ pIDataModel->SetTelemetryEnabled(bTelemetryEnabled);
+
+ return LIBMCDATA_SUCCESS;
+ }
+ catch (ELibMCDataInterfaceException & Exception) {
+ return handleLibMCDataException(pIBaseClass, Exception);
+ }
+ catch (std::exception & StdException) {
+ return handleStdException(pIBaseClass, StdException);
+ }
+ catch (...) {
+ return handleUnhandledException(pIBaseClass);
+ }
+}
+
+LibMCDataResult libmcdata_datamodel_gettelemetryenabled(LibMCData_DataModel pDataModel, bool * pTelemetryEnabled)
+{
+ IBase* pIBaseClass = (IBase *)pDataModel;
+
+ try {
+ if (pTelemetryEnabled == nullptr)
+ throw ELibMCDataInterfaceException (LIBMCDATA_ERROR_INVALIDPARAM);
+ IDataModel* pIDataModel = dynamic_cast(pIBaseClass);
+ if (!pIDataModel)
+ throw ELibMCDataInterfaceException(LIBMCDATA_ERROR_INVALIDCAST);
+
+ *pTelemetryEnabled = pIDataModel->GetTelemetryEnabled();
+
+ return LIBMCDATA_SUCCESS;
+ }
+ catch (ELibMCDataInterfaceException & Exception) {
+ return handleLibMCDataException(pIBaseClass, Exception);
+ }
+ catch (std::exception & StdException) {
+ return handleStdException(pIBaseClass, StdException);
+ }
+ catch (...) {
+ return handleUnhandledException(pIBaseClass);
+ }
+}
+
LibMCDataResult libmcdata_datamodel_getdatamodelversion(LibMCData_DataModel pDataModel, LibMCData_uint32 * pVersion)
{
IBase* pIBaseClass = (IBase *)pDataModel;
@@ -12307,6 +12357,10 @@ LibMCDataResult LibMCData::Impl::LibMCData_GetProcAddress (const char * pProcNam
*ppProcAddress = (void*) &libmcdata_installationinformation_getbasetempdirectory;
if (sProcName == "libmcdata_datamodel_initialisedatabase")
*ppProcAddress = (void*) &libmcdata_datamodel_initialisedatabase;
+ if (sProcName == "libmcdata_datamodel_settelemetryenabled")
+ *ppProcAddress = (void*) &libmcdata_datamodel_settelemetryenabled;
+ if (sProcName == "libmcdata_datamodel_gettelemetryenabled")
+ *ppProcAddress = (void*) &libmcdata_datamodel_gettelemetryenabled;
if (sProcName == "libmcdata_datamodel_getdatamodelversion")
*ppProcAddress = (void*) &libmcdata_datamodel_getdatamodelversion;
if (sProcName == "libmcdata_datamodel_getinstallationinformation")
diff --git a/Framework/InterfacesCore/libmcdata_types.hpp b/Framework/InterfacesCore/libmcdata_types.hpp
index 19bdf40c..a2d8e0c6 100644
--- a/Framework/InterfacesCore/libmcdata_types.hpp
+++ b/Framework/InterfacesCore/libmcdata_types.hpp
@@ -477,6 +477,7 @@ typedef void * LibMCData_pvoid;
#define LIBMCDATA_ERROR_TELEMETRYCHANNELNOTFOUND 450 /** Telemetry channel not found. */
#define LIBMCDATA_ERROR_JOBNAMETOOLONG 451 /** Job name exceeds maximum allowed length */
#define LIBMCDATA_ERROR_STRINGPARAMETERTOOLONG 452 /** String parameter exceeds maximum allowed length */
+#define LIBMCDATA_ERROR_TELEMETRYSETTINGAFTERINITIALISATION 453 /** Telemetry setting changed after database initialisation */
/*************************************************************************************************************************
Error strings for LibMCData
@@ -866,6 +867,7 @@ inline const char * LIBMCDATA_GETERRORSTRING (LibMCDataResult nErrorCode) {
case LIBMCDATA_ERROR_TELEMETRYCHANNELNOTFOUND: return "Telemetry channel not found.";
case LIBMCDATA_ERROR_JOBNAMETOOLONG: return "Job name exceeds maximum allowed length";
case LIBMCDATA_ERROR_STRINGPARAMETERTOOLONG: return "String parameter exceeds maximum allowed length";
+ case LIBMCDATA_ERROR_TELEMETRYSETTINGAFTERINITIALISATION: return "Telemetry setting changed after database initialisation";
default: return "unknown error";
}
}
diff --git a/Implementation/Core/amc_systemstate.cpp b/Implementation/Core/amc_systemstate.cpp
index 987d9d61..0c14a539 100644
--- a/Implementation/Core/amc_systemstate.cpp
+++ b/Implementation/Core/amc_systemstate.cpp
@@ -93,7 +93,7 @@ namespace AMC {
m_pAccessControl = std::make_shared ();
- auto pTelemetryWriter = std::make_shared (m_pDataModel->CreateTelemetrySession (), m_pGlobalChrono);
+ auto pTelemetryWriter = std::make_shared (m_pDataModel->CreateTelemetrySession (), m_pGlobalChrono, m_pDataModel->GetTelemetryEnabled ());
m_pTelemetryHandler = std::make_shared (pTelemetryWriter);
m_pMeshHandler = std::make_shared();
diff --git a/Implementation/Core/amc_telemetry.cpp b/Implementation/Core/amc_telemetry.cpp
index 183cbc5e..bc84c5a2 100644
--- a/Implementation/Core/amc_telemetry.cpp
+++ b/Implementation/Core/amc_telemetry.cpp
@@ -176,16 +176,18 @@ namespace AMC {
- CTelemetryWriter::CTelemetryWriter(LibMCData::PTelemetrySession pTelemetrySession, AMCCommon::PChrono pGlobalChrono)
+ CTelemetryWriter::CTelemetryWriter(LibMCData::PTelemetrySession pTelemetrySession, AMCCommon::PChrono pGlobalChrono, bool bTelemetryEnabled)
: m_pTelemetrySession(pTelemetrySession), m_nNextMarkerID(1), m_pGlobalChrono (pGlobalChrono),
- m_nChunkIntervalInMicroseconds (TELEMETRY_DEFAULT_CHUNKINTERVAL_MICROSECONDS)
+ m_nChunkIntervalInMicroseconds (TELEMETRY_DEFAULT_CHUNKINTERVAL_MICROSECONDS),
+ m_bTelemetryEnabled (bTelemetryEnabled)
{
if (pGlobalChrono.get () == nullptr)
throw ELibMCInterfaceException(LIBMC_ERROR_INVALIDPARAM);
if (pTelemetrySession.get() == nullptr)
throw ELibMCInterfaceException(LIBMC_ERROR_INVALIDPARAM);
- extendChunksUntil(1);
+ if (m_bTelemetryEnabled)
+ extendChunksUntil(1);
}
CTelemetryWriter::~CTelemetryWriter()
@@ -247,6 +249,22 @@ namespace AMC {
void CTelemetryWriter::writeEntry(const LibMCData::sTelemetryChunkEntry& entry)
{
+ // Telemetry is dropped HERE, at the writer, and deliberately not earlier.
+ //
+ // Do NOT "optimise" this by short-circuiting marker creation (for example with a no-op
+ // CTelemetryMarker). CStateSignalSlot::updateTimingStatistics feeds the live
+ // reactiontime_ / finishtime_ / acknowledgetime_ parameters of every signal from
+ // CTelemetryChannel::getMaxDurationInMicroseconds, which is updated by unregisterMarker
+ // in CTelemetryMarker::finishMarker - independently of this writer. Killing markers
+ // would silently zero those three parameters on every device.
+ //
+ // Dropping at this level is also symmetric: registerOpenInterval and eraseOpenInterval are
+ // both called from CTelemetryDataChunk::writeEntry, so neither runs and no open-interval
+ // state is orphaned. With no chunk ever allocated, archiveOldChunksToDB no-ops and
+ // CJournal::writeTelemetryChunk is never reached.
+ if (!m_bTelemetryEnabled)
+ return;
+
auto pChunk = getOrCreateChunkByTimestamp(entry.m_TimeStamp);
pChunk->writeEntry(entry);
diff --git a/Implementation/Core/amc_telemetry.hpp b/Implementation/Core/amc_telemetry.hpp
index 930d2714..661d362a 100644
--- a/Implementation/Core/amc_telemetry.hpp
+++ b/Implementation/Core/amc_telemetry.hpp
@@ -127,11 +127,15 @@ namespace AMC {
std::atomic m_nNextMarkerID;
+ // If false, writeEntry drops every entry, so no chunk is ever allocated and nothing is
+ // archived to disk. Markers themselves are still created - see writeEntry.
+ bool m_bTelemetryEnabled;
+
void extendChunksUntil(uint64_t nMaxChunkIndexOneBased);
public:
- CTelemetryWriter(LibMCData::PTelemetrySession pTelemetrySession, AMCCommon::PChrono pGlobalChrono);
+ CTelemetryWriter(LibMCData::PTelemetrySession pTelemetrySession, AMCCommon::PChrono pGlobalChrono, bool bTelemetryEnabled = true);
virtual ~CTelemetryWriter();
diff --git a/Implementation/DataModel/amcdata_journal.cpp b/Implementation/DataModel/amcdata_journal.cpp
index 6a7112bf..ad32fc4f 100644
--- a/Implementation/DataModel/amcdata_journal.cpp
+++ b/Implementation/DataModel/amcdata_journal.cpp
@@ -151,9 +151,10 @@ namespace AMCData {
return m_nTotalSize;
}
- CJournal::CJournal(const std::string& sJournalBasePath, const std::string& sJournalName, const std::string& sJournalChunkBaseName, const std::string& sTelemetryChunkBaseName, const std::string& sSessionUUID)
+ CJournal::CJournal(const std::string& sJournalBasePath, const std::string& sJournalName, const std::string& sJournalChunkBaseName, const std::string& sTelemetryChunkBaseName, const std::string& sSessionUUID, bool bTelemetryEnabled)
: m_LogID(1), m_AlertID(1), m_sSessionUUID(AMCCommon::CUtils::normalizeUUIDString(sSessionUUID)),
- m_sJournalBasePath(sJournalBasePath), m_sChunkBaseName (sJournalChunkBaseName), m_sTelemetryChunkBaseName(sTelemetryChunkBaseName)
+ m_sJournalBasePath(sJournalBasePath), m_sChunkBaseName (sJournalChunkBaseName), m_sTelemetryChunkBaseName(sTelemetryChunkBaseName),
+ m_bTelemetryEnabled (bTelemetryEnabled)
{
if (sJournalChunkBaseName.empty())
throw ELibMCDataInterfaceException(LIBMCDATA_ERROR_INVALIDPARAM, "Journal chunk base name is empty");
@@ -296,7 +297,12 @@ namespace AMCData {
pSessionMetricsStatement = nullptr;
m_pCurrentJournalFile = createJournalFile();
- m_pCurrentTelemetryFile = createTelemetryFile();
+
+ // With telemetry disabled, no data file is created at all. The telemetry_* tables above are
+ // still created, so the schema stays uniform and a reader can enumerate the registered
+ // channels of an "off" session.
+ if (m_bTelemetryEnabled)
+ m_pCurrentTelemetryFile = createTelemetryFile();
}
@@ -1052,6 +1058,14 @@ namespace AMCData {
if (nStartTimeStamp > nEndTimeStamp)
throw ELibMCDataInterfaceException(LIBMCDATA_ERROR_INVALIDPARAM);
+ // With telemetry disabled there is no data file to write to. libmc drops entries before they
+ // ever become chunks, so this should be unreachable, but the two libraries are versioned
+ // separately and a flag that reaches one and not the other must not be a null dereference.
+ if (!m_bTelemetryEnabled)
+ return;
+ if (m_pCurrentTelemetryFile.get() == nullptr)
+ return;
+
if (m_pCurrentTelemetryFile->getTotalSize() > getMaxChunkFileSizeQuotaInBytes())
m_pCurrentTelemetryFile = createTelemetryFile();
diff --git a/Implementation/DataModel/amcdata_journal.hpp b/Implementation/DataModel/amcdata_journal.hpp
index 3fef99e0..5d81255d 100644
--- a/Implementation/DataModel/amcdata_journal.hpp
+++ b/Implementation/DataModel/amcdata_journal.hpp
@@ -103,6 +103,10 @@ namespace AMCData {
PActiveJournalFile m_pCurrentTelemetryFile;
+ // If false, no telemetry data file is created and no telemetry entry is ever written.
+ // This is an explicit flag on purpose: an empty m_sTelemetryChunkBaseName stays an error.
+ bool m_bTelemetryEnabled;
+
PActiveJournalFile createTelemetryFile();
public:
@@ -111,7 +115,7 @@ namespace AMCData {
static LibMCData::eAlertLevel convertStringToAlertLevel(const std::string & sValue, bool bFailIfUnknown);
- CJournal(const std::string& sJournalBasePath, const std::string& sJournalName, const std::string& sJournalChunkBaseName, const std::string& sTelemetryChunkBaseName, const std::string & sSessionUUID);
+ CJournal(const std::string& sJournalBasePath, const std::string& sJournalName, const std::string& sJournalChunkBaseName, const std::string& sTelemetryChunkBaseName, const std::string & sSessionUUID, bool bTelemetryEnabled = true);
virtual ~CJournal();
diff --git a/Implementation/LibMCData/libmcdata_datamodel.cpp b/Implementation/LibMCData/libmcdata_datamodel.cpp
index 6c925636..bca018e7 100644
--- a/Implementation/LibMCData/libmcdata_datamodel.cpp
+++ b/Implementation/LibMCData/libmcdata_datamodel.cpp
@@ -75,7 +75,7 @@ using namespace LibMCData::Impl;
**************************************************************************************************************************/
CDataModel::CDataModel()
- : m_eDataBaseType(eDataBaseType::Unknown), m_pLogCallback (nullptr), m_pLogUserData (nullptr)
+ : m_eDataBaseType(eDataBaseType::Unknown), m_pLogCallback (nullptr), m_pLogUserData (nullptr), m_bTelemetryEnabled (true)
{
m_sSessionUUID = AMCCommon::CUtils::createUUID();
@@ -153,7 +153,7 @@ void CDataModel::InitialiseDatabase(const std::string & sDataDirectory, const Li
auto sJournalChunkBaseName = m_pStorageState->getJournalChunkBaseName(m_sTimeFileName);
auto sTelemetryChunkBaseName = m_pStorageState->getTelemetryChunkBaseName(m_sTimeFileName);;
- m_pJournal = std::make_shared (sJournalBasePath, sJournalName, sJournalChunkBaseName, sTelemetryChunkBaseName, m_sSessionUUID);
+ m_pJournal = std::make_shared (sJournalBasePath, sJournalName, sJournalChunkBaseName, sTelemetryChunkBaseName, m_sSessionUUID, m_bTelemetryEnabled);
auto pStatement = m_pSQLHandler->prepareStatement("INSERT INTO journals (uuid, starttime, logfilename, journalfilename, telemetryfilename, logfilepath, journalfilepath, schemaversion, githash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
pStatement->setString(1, m_sSessionUUID);
@@ -169,6 +169,22 @@ void CDataModel::InitialiseDatabase(const std::string & sDataDirectory, const Li
}
+void CDataModel::SetTelemetryEnabled(const bool bTelemetryEnabled)
+{
+ // The flag is latched by InitialiseDatabase, which is where the telemetry data file is
+ // created. Changing it afterwards cannot un-create that file, so fail loudly instead of
+ // silently ignoring the call.
+ if (m_pJournal.get() != nullptr)
+ throw ELibMCDataInterfaceException(LIBMCDATA_ERROR_TELEMETRYSETTINGAFTERINITIALISATION);
+
+ m_bTelemetryEnabled = bTelemetryEnabled;
+}
+
+bool CDataModel::GetTelemetryEnabled()
+{
+ return m_bTelemetryEnabled;
+}
+
LibMCData_uint32 CDataModel::GetDataModelVersion()
{
return AMCData::CDatabaseMigrator::getCurrentSchemaVersion ();
diff --git a/Implementation/LibMCData/libmcdata_datamodel.hpp b/Implementation/LibMCData/libmcdata_datamodel.hpp
index 0f38fa08..f74b54b2 100644
--- a/Implementation/LibMCData/libmcdata_datamodel.hpp
+++ b/Implementation/LibMCData/libmcdata_datamodel.hpp
@@ -76,6 +76,10 @@ class CDataModel : public virtual IDataModel, public virtual CBase {
LibMCData::LogCallback m_pLogCallback;
LibMCData_pvoid m_pLogUserData;
+ // Telemetry recording flag. Defaults to true, so that a caller that never calls
+ // SetTelemetryEnabled gets stock behaviour. Latched in InitialiseDatabase.
+ bool m_bTelemetryEnabled;
+
public:
CDataModel();
@@ -84,6 +88,10 @@ class CDataModel : public virtual IDataModel, public virtual CBase {
void InitialiseDatabase(const std::string & sDataDirectory, const LibMCData::eDataBaseType dataBaseType, const std::string & sConnectionString) override;
+ void SetTelemetryEnabled(const bool bTelemetryEnabled) override;
+
+ bool GetTelemetryEnabled() override;
+
LibMCData_uint32 GetDataModelVersion() override;
// DEPRECIATED! DO NOT USE. Use GetInstallationInformationObject instead!
diff --git a/Implementation/Server/amc_server.cpp b/Implementation/Server/amc_server.cpp
index 96d3fa3f..167abaaa 100644
--- a/Implementation/Server/amc_server.cpp
+++ b/Implementation/Server/amc_server.cpp
@@ -257,6 +257,11 @@ void CServer::executeBlocking(const std::string& sConfigurationFileName)
m_pDataModel->SetBaseTempDirectory(m_pServerConfiguration->getBaseTempDirectory ());
+ // MUST be set before InitialiseDatabase, which is where the telemetry data file is created.
+ if (!m_pServerConfiguration->getTelemetryEnabled())
+ log("Telemetry recording is disabled.");
+ m_pDataModel->SetTelemetryEnabled(m_pServerConfiguration->getTelemetryEnabled());
+
log("Initialising Database...");
m_pDataModel->InitialiseDatabase(m_pServerConfiguration->getDataDirectory(), m_pServerConfiguration->getDataBaseType(), m_pServerConfiguration->getConnectionString());
diff --git a/Implementation/Server/amc_server_configuration.cpp b/Implementation/Server/amc_server_configuration.cpp
index df47579c..10daf809 100644
--- a/Implementation/Server/amc_server_configuration.cpp
+++ b/Implementation/Server/amc_server_configuration.cpp
@@ -97,7 +97,7 @@ std::string CServerLibrary::getResourcePath()
}
CServerConfiguration::CServerConfiguration(const std::string& configurationXMLString, PServerIO pServerIO)
- : m_nPort(0), m_DataBaseType(LibMCData::eDataBaseType::Unknown), m_bUseSSL (false)
+ : m_nPort(0), m_DataBaseType(LibMCData::eDataBaseType::Unknown), m_bUseSSL (false), m_bTelemetryEnabled (true)
{
if (pServerIO.get() == nullptr)
@@ -212,6 +212,21 @@ CServerConfiguration::CServerConfiguration(const std::string& configurationXMLSt
throw LibMC::ELibMCException(LIBMC_ERROR_INVALIDDATABASETYPE, "Invalid database type: " + sDataBaseType);
}
+ // Optional telemetry off-switch. Absent means "on", which is stock behaviour.
+ auto telemetryAttrib = dataNode.attribute("telemetry");
+ if (!telemetryAttrib.empty()) {
+ std::string sTelemetrySetting = telemetryAttrib.as_string();
+ if (sTelemetrySetting == "on") {
+ m_bTelemetryEnabled = true;
+ }
+ else if (sTelemetrySetting == "off") {
+ m_bTelemetryEnabled = false;
+ }
+ else {
+ throw LibMC::ELibMCException(LIBMC_ERROR_INVALIDTELEMETRYSETTING, "Invalid telemetry setting: " + sTelemetrySetting + " (must be \"on\" or \"off\")");
+ }
+ }
+
auto defaultPackageNode = amcNode.child("defaultpackage");
if (defaultPackageNode.empty ())
throw LibMC::ELibMCException(LIBMC_ERROR_DEFAULTPACKAGEMISSING, "Default package missing");
@@ -255,6 +270,11 @@ std::string CServerConfiguration::getConnectionString()
return m_sConnectionString;
}
+bool CServerConfiguration::getTelemetryEnabled()
+{
+ return m_bTelemetryEnabled;
+}
+
std::string CServerConfiguration::getPackageName()
{
return m_sPackageName;
diff --git a/Implementation/Server/amc_server_configuration.hpp b/Implementation/Server/amc_server_configuration.hpp
index a891266c..642e9588 100644
--- a/Implementation/Server/amc_server_configuration.hpp
+++ b/Implementation/Server/amc_server_configuration.hpp
@@ -67,6 +67,7 @@ namespace AMC {
std::string m_sDataDirectory;
LibMCData::eDataBaseType m_DataBaseType;
std::string m_sConnectionString;
+ bool m_bTelemetryEnabled;
std::string m_sPackageName;
std::string m_sPackageCoreClient;
@@ -92,6 +93,7 @@ namespace AMC {
std::string getDataDirectory ();
LibMCData::eDataBaseType getDataBaseType ();
std::string getConnectionString ();
+ bool getTelemetryEnabled ();
std::string getPackageName ();
std::string getPackageCoreClient ();
std::string getPackageAPIDocs ();
diff --git a/Implementation/UnitTest/amc_unittests_signalslot.hpp b/Implementation/UnitTest/amc_unittests_signalslot.hpp
index 1142b28a..48ea7c1c 100644
--- a/Implementation/UnitTest/amc_unittests_signalslot.hpp
+++ b/Implementation/UnitTest/amc_unittests_signalslot.hpp
@@ -47,6 +47,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "common_utils.hpp"
#include "common_chrono.hpp"
#include "amc_telemetry.hpp"
+#include "amc_parametergroup.hpp"
#include "libmcdata_dynamic.hpp"
#include "amc_unittests_libmcdata.hpp"
@@ -57,9 +58,9 @@ namespace AMCUnitTest {
public:
- CDummyRegistry()
+ CDummyRegistry(bool bTelemetryEnabled = true)
{
- initializeTelemetry();
+ initializeTelemetry(bTelemetryEnabled);
}
void registerMessage(const std::string& sMessageUUID, AMC::CStateSignalSlot* pSignalSlot) override
@@ -82,8 +83,13 @@ namespace AMCUnitTest {
return m_pTelemetryHandler->registerChannel(sChannelIdentifier, sChannelDescription, channelType);
}
+ AMCCommon::PChrono getChrono()
+ {
+ return m_pChrono;
+ }
+
private:
- void initializeTelemetry()
+ void initializeTelemetry(bool bTelemetryEnabled)
{
std::string sRootPath = "temp";
if (!AMCCommon::CUtils::fileOrPathExistsOnDisk(sRootPath))
@@ -97,11 +103,12 @@ namespace AMCUnitTest {
m_pDataWrapper = AMCUnitTest::loadLibMCDataInProcess();
m_pDataModel = m_pDataWrapper->CreateDataModelInstance();
+ m_pDataModel->SetTelemetryEnabled(bTelemetryEnabled);
m_pDataModel->InitialiseDatabase(sBasePath, LibMCData::eDataBaseType::SqLite, sDatabaseFile);
m_pTelemetrySession = m_pDataModel->CreateTelemetrySession();
m_pChrono = std::make_shared();
- m_pTelemetryWriter = std::make_shared(m_pTelemetrySession, m_pChrono);
+ m_pTelemetryWriter = std::make_shared(m_pTelemetrySession, m_pChrono, bTelemetryEnabled);
m_pTelemetryHandler = std::make_shared(m_pTelemetryWriter);
}
@@ -137,10 +144,60 @@ class CUnitTestGroup_SignalSlot : public CUnitTestGroup {
registerTest("ParameterResultAccess", "Tests getting parameter and result JSON", eUnitTestCategory::utMandatoryPass, std::bind(&CUnitTestGroup_SignalSlot::test_ParameterResultAccess, this));
registerTest("ClearQueueWorks", "Clears the queue and marks signals as cleared", eUnitTestCategory::utMandatoryPass, std::bind(&CUnitTestGroup_SignalSlot::test_ClearQueueWorks, this));
registerTest("TimeoutAndOverflowTest", "Simulates queue overflow and timeout scenarios", eUnitTestCategory::utOptionalPass, std::bind(&CUnitTestGroup_SignalSlot::test_TimeoutAndOverflowTest, this));
+ registerTest("TimingStatisticsWithTelemetryOn", "Signal timing parameters update with telemetry enabled", eUnitTestCategory::utMandatoryPass, std::bind(&CUnitTestGroup_SignalSlot::test_TimingStatisticsWithTelemetryOn, this));
+ registerTest("TimingStatisticsWithTelemetryOff", "Signal timing parameters still update with telemetry disabled", eUnitTestCategory::utMandatoryPass, std::bind(&CUnitTestGroup_SignalSlot::test_TimingStatisticsWithTelemetryOff, this));
}
private:
+ // Drives a signal through InQueue -> InProcess -> Handled and returns the timing parameters
+ // that CStateSignalSlot::updateTimingStatistics feeds from the telemetry channels.
+ void driveSignalAndReadTimings(bool bTelemetryEnabled, int64_t& nReactionTime, int64_t& nFinishTime)
+ {
+ CDummyRegistry registry(bTelemetryEnabled);
+
+ auto pInformationGroup = std::make_shared("signalinfo", "signal information", registry.getChrono());
+
+ AMC::CStateSignalSlot slot("instance", "signal", {}, {}, 1000, 5000, 3, pInformationGroup, ®istry);
+ std::string uuid = "0f0e0d0c-0b0a-0908-0706-050403020100";
+
+ AMCCommon::CChrono chrono;
+ chrono.sleepMicroseconds(500);
+
+ auto pMessage = slot.addNewInQueueSignalInternal(uuid, "{}", 500, chrono.getElapsedMicroseconds());
+ assertTrue(pMessage != nullptr);
+
+ chrono.sleepMicroseconds(1000);
+ assertTrue(slot.changeSignalPhaseToInProcessInternal(uuid, chrono.getElapsedMicroseconds()));
+
+ chrono.sleepMicroseconds(1000);
+ assertTrue(slot.changeSignalPhaseToHandledInternal(uuid, "{\"result\":true}", chrono.getElapsedMicroseconds()));
+
+ nReactionTime = pInformationGroup->getIntParameterValueByName("reactiontime_signal");
+ nFinishTime = pInformationGroup->getIntParameterValueByName("finishtime_signal");
+ }
+
+ void test_TimingStatisticsWithTelemetryOn() {
+ int64_t nReactionTime = 0;
+ int64_t nFinishTime = 0;
+ driveSignalAndReadTimings(true, nReactionTime, nFinishTime);
+
+ assertTrue(nReactionTime > 0, "reactiontime_ should be updated with telemetry enabled");
+ assertTrue(nFinishTime > 0, "finishtime_ should be updated with telemetry enabled");
+ }
+
+ void test_TimingStatisticsWithTelemetryOff() {
+ // This is the acceptance criterion for the telemetry off-switch: dropping telemetry at the
+ // writer must NOT zero the live signal timing parameters, which are fed from the in-memory
+ // channel statistics rather than from anything written to disk.
+ int64_t nReactionTime = 0;
+ int64_t nFinishTime = 0;
+ driveSignalAndReadTimings(false, nReactionTime, nFinishTime);
+
+ assertTrue(nReactionTime > 0, "reactiontime_ must still be updated with telemetry disabled");
+ assertTrue(nFinishTime > 0, "finishtime_ must still be updated with telemetry disabled");
+ }
+
void test_CreateSignalSlotBasic() {
std::vector params;
std::vector results;
diff --git a/Implementation/UnitTest/amc_unittests_telemetry.hpp b/Implementation/UnitTest/amc_unittests_telemetry.hpp
index 7612798d..73b1d72f 100644
--- a/Implementation/UnitTest/amc_unittests_telemetry.hpp
+++ b/Implementation/UnitTest/amc_unittests_telemetry.hpp
@@ -40,6 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
#include
+#include
namespace AMCUnitTest {
@@ -64,6 +65,13 @@ namespace AMCUnitTest {
registerTest("TypeMappingRoundTrip", "Telemetry channel type mapping round-trip", eUnitTestCategory::utMandatoryPass, std::bind(&CUnitTestGroup_Telemetry::testTypeMappingRoundTrip, this));
registerTest("TypeMappingInvalidThrows", "Telemetry channel type mapping rejects invalid strings", eUnitTestCategory::utMandatoryPass, std::bind(&CUnitTestGroup_Telemetry::testTypeMappingInvalidThrows, this));
registerTest("ChunkArchivePath", "Chunk read-only and archive path works", eUnitTestCategory::utMandatoryPass, std::bind(&CUnitTestGroup_Telemetry::testChunkArchivePath, this));
+ registerTest("TelemetryOnCreatesDataFile", "Telemetry enabled creates a telemetry data file", eUnitTestCategory::utMandatoryPass, std::bind(&CUnitTestGroup_Telemetry::testTelemetryOnCreatesDataFile, this));
+ registerTest("TelemetryOffCreatesNoDataFile", "Telemetry disabled creates no telemetry data file", eUnitTestCategory::utMandatoryPass, std::bind(&CUnitTestGroup_Telemetry::testTelemetryOffCreatesNoDataFile, this));
+ registerTest("TelemetryOffKeepsMarkerStatistics", "Telemetry disabled still updates marker statistics", eUnitTestCategory::utMandatoryPass, std::bind(&CUnitTestGroup_Telemetry::testTelemetryOffKeepsMarkerStatistics, this));
+ registerTest("TelemetryOffScopeDoesNotThrow", "Telemetry disabled scopes complete without throwing", eUnitTestCategory::utMandatoryPass, std::bind(&CUnitTestGroup_Telemetry::testTelemetryOffScopeDoesNotThrow, this));
+ registerTest("TelemetryOffReaderIsEmpty", "Telemetry reader on a disabled session reports channels but no data", eUnitTestCategory::utMandatoryPass, std::bind(&CUnitTestGroup_Telemetry::testTelemetryOffReaderIsEmpty, this));
+ registerTest("TelemetrySettingAfterInitThrows", "Changing the telemetry setting after initialisation fails", eUnitTestCategory::utMandatoryPass, std::bind(&CUnitTestGroup_Telemetry::testTelemetrySettingAfterInitThrows, this));
+ registerTest("TelemetryEnabledDefaultsToOn", "Telemetry defaults to enabled when never set", eUnitTestCategory::utMandatoryPass, std::bind(&CUnitTestGroup_Telemetry::testTelemetryEnabledDefaultsToOn, this));
}
void initializeTests() override {
@@ -78,9 +86,11 @@ namespace AMCUnitTest {
AMCCommon::PChrono m_pChrono;
AMC::PTelemetryWriter m_pWriter;
AMC::PTelemetryHandler m_pHandler;
+ std::string m_sBasePath;
+ std::string m_sJournalUUID;
} sTelemetryFixture;
- sTelemetryFixture createFixture(const std::string& sSuffix)
+ sTelemetryFixture createFixture(const std::string& sSuffix, bool bTelemetryEnabled = true)
{
sTelemetryFixture fixture;
@@ -94,18 +104,47 @@ namespace AMCUnitTest {
std::string sDatabaseFile = sBasePath + "/telemetry.db";
+ fixture.m_sBasePath = sBasePath;
fixture.m_pDataWrapper = AMCUnitTest::loadLibMCDataInProcess();
fixture.m_pDataModel = fixture.m_pDataWrapper->CreateDataModelInstance();
+
+ // MUST happen before InitialiseDatabase, which is where the telemetry file is created.
+ fixture.m_pDataModel->SetTelemetryEnabled(bTelemetryEnabled);
+
fixture.m_pDataModel->InitialiseDatabase(sBasePath, LibMCData::eDataBaseType::SqLite, sDatabaseFile);
fixture.m_pTelemetrySession = fixture.m_pDataModel->CreateTelemetrySession();
+ fixture.m_sJournalUUID = fixture.m_pDataModel->CreateJournalSession()->GetSessionUUID();
fixture.m_pChrono = std::make_shared();
- fixture.m_pWriter = std::make_shared(fixture.m_pTelemetrySession, fixture.m_pChrono);
+ fixture.m_pWriter = std::make_shared(fixture.m_pTelemetrySession, fixture.m_pChrono, bTelemetryEnabled);
fixture.m_pHandler = std::make_shared(fixture.m_pWriter);
return fixture;
}
+ // Each telemetry-mode test needs a directory of its own, so that a data file left behind by
+ // an earlier run cannot be mistaken for one created by this one.
+ sTelemetryFixture createUniqueFixture(const std::string& sSuffix, bool bTelemetryEnabled)
+ {
+ return createFixture(sSuffix + "_" + AMCCommon::CUtils::createUUID(), bTelemetryEnabled);
+ }
+
+ uint32_t countTelemetryDataFiles(const std::string& sBasePath)
+ {
+ uint32_t nCount = 0;
+ for (const auto& entry : std::filesystem::directory_iterator(sBasePath)) {
+ if (!entry.is_regular_file())
+ continue;
+
+ std::string sFileName = entry.path().filename().string();
+ if ((sFileName.rfind("telemetry_", 0) == 0) && (sFileName.size() >= 5)
+ && (sFileName.compare(sFileName.size() - 5, 5, ".data") == 0))
+ nCount++;
+ }
+
+ return nCount;
+ }
+
AMC::PTelemetryChannel createChannel(sTelemetryFixture& fixture, const std::string& sIdentifier)
{
return fixture.m_pHandler->registerChannel(sIdentifier, "channel description", LibMCData::eTelemetryChannelType::CustomMarker);
@@ -373,6 +412,110 @@ namespace AMCUnitTest {
fixture.m_pHandler->archiveOldChunksToDB();
}
+
+ void testTelemetryOnCreatesDataFile()
+ {
+ auto fixture = createUniqueFixture("mode_on", true);
+ assertTrue(fixture.m_pDataModel->GetTelemetryEnabled(), "Telemetry should report as enabled");
+ assertTrue(countTelemetryDataFiles(fixture.m_sBasePath) == 1, "Expected exactly one telemetry data file");
+ }
+
+ void testTelemetryOffCreatesNoDataFile()
+ {
+ auto fixture = createUniqueFixture("mode_off", false);
+ assertFalse(fixture.m_pDataModel->GetTelemetryEnabled(), "Telemetry should report as disabled");
+ assertTrue(countTelemetryDataFiles(fixture.m_sBasePath) == 0, "Expected no telemetry data file at session start");
+
+ // Recording activity must not create one later either.
+ auto pChannel = createChannel(fixture, "off_channel");
+ for (uint32_t nIndex = 0; nIndex < 32; nIndex++) {
+ auto scope = pChannel->startIntervalScope(nIndex);
+ AMCCommon::CChrono::sleepMicroseconds(10);
+ }
+ pChannel->createInstantMarker(7);
+ fixture.m_pHandler->archiveOldChunksToDB();
+
+ assertTrue(countTelemetryDataFiles(fixture.m_sBasePath) == 0, "Expected no telemetry data file after recording activity");
+ }
+
+ void testTelemetryOffKeepsMarkerStatistics()
+ {
+ // This is the behaviour CStateSignalSlot::updateTimingStatistics depends on: the
+ // reactiontime_ / finishtime_ / acknowledgetime_ signal parameters are fed from
+ // getMaxDurationInMicroseconds, which must keep working with telemetry disabled.
+ auto fixture = createUniqueFixture("off_stats", false);
+ auto pChannel = createChannel(fixture, "off_stats_channel");
+
+ auto pMarker = pChannel->startIntervalMarker(42);
+ AMCCommon::CChrono::sleepMicroseconds(500);
+ pMarker->finishMarker();
+
+ assertTrue(pMarker->isFinished(), "Marker should be finished with telemetry disabled");
+ assertTrue(pMarker->getDuration() > 0, "Marker duration should be recorded with telemetry disabled");
+ assertTrue(pChannel->getMaxDurationInMicroseconds() == pMarker->getDuration(), "Channel max duration should track the marker with telemetry disabled");
+ assertTrue(pChannel->getTotalMarkersCreated() == 1, "Marker count should be tracked with telemetry disabled");
+ }
+
+ void testTelemetryOffScopeDoesNotThrow()
+ {
+ auto fixture = createUniqueFixture("off_scope", false);
+ auto pChannel = createChannel(fixture, "off_scope_channel");
+
+ bool thrown = false;
+ try {
+ for (uint32_t nIndex = 0; nIndex < 16; nIndex++) {
+ auto scope = pChannel->startIntervalScope(nIndex);
+ AMCCommon::CChrono::sleepMicroseconds(10);
+ }
+ }
+ catch (...) {
+ thrown = true;
+ }
+
+ assertFalse(thrown, "Telemetry scopes must not throw with telemetry disabled");
+ assertTrue(pChannel->getTotalMarkersCreated() == 16, "All markers should still be created");
+ assertTrue(pChannel->getMaxDurationInMicroseconds() > 0, "Scope durations should still be recorded");
+ }
+
+ void testTelemetryOffReaderIsEmpty()
+ {
+ auto fixture = createUniqueFixture("off_reader", false);
+ auto pChannel = createChannel(fixture, "off_reader_channel");
+ pChannel->createInstantMarker(1);
+ fixture.m_pHandler->archiveOldChunksToDB();
+
+ // Channel registration is deliberately unaffected by the off-switch, so that an "off"
+ // session is still self-describing.
+ auto pReader = fixture.m_pDataModel->CreateTelemetryReader(fixture.m_sJournalUUID);
+ assertTrue(pReader->GetChannelCount() == 1, "Reader should report the registered channel");
+ assertTrue(pReader->GetChunkCount() == 0, "Reader should report no chunks for a disabled session");
+
+ uint32_t nChannelIndex = 0;
+ assertTrue(pReader->FindChannelByIdentifier("off_reader_channel", nChannelIndex), "Reader should find the registered channel");
+ }
+
+ void testTelemetrySettingAfterInitThrows()
+ {
+ auto fixture = createUniqueFixture("set_after_init", true);
+
+ bool thrown = false;
+ try {
+ fixture.m_pDataModel->SetTelemetryEnabled(false);
+ }
+ catch (...) {
+ thrown = true;
+ }
+
+ assertTrue(thrown, "Expected SetTelemetryEnabled after InitialiseDatabase to throw");
+ }
+
+ void testTelemetryEnabledDefaultsToOn()
+ {
+ // A caller that never calls SetTelemetryEnabled must get stock behaviour.
+ auto pDataWrapper = AMCUnitTest::loadLibMCDataInProcess();
+ auto pDataModel = pDataWrapper->CreateDataModelInstance();
+ assertTrue(pDataModel->GetTelemetryEnabled(), "Telemetry should default to enabled");
+ }
};
}