diff --git a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/AircraftVariable.cpp b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/AircraftVariable.cpp index 477f45b2b92..efa7be4584a 100644 --- a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/AircraftVariable.cpp +++ b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/AircraftVariable.cpp @@ -97,7 +97,7 @@ std::string AircraftVariable::str() const { ss << "AircraftVariable: [" << name << (index ? ":" + std::to_string(index) : ""); ss << ", value: " << (cachedValue.has_value() ? std::to_string(cachedValue.value()) : "N/A"); ss << ", unit: " << unit.name; - ss << ", changed: " << changed; + ss << ", changed: " << hasChanged(); ss << ", dirty: " << dirty; ss << ", timeStamp: " << timeStampSimTime; ss << ", tickStamp: " << tickStamp; diff --git a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/AircraftVariable.h b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/AircraftVariable.h index 3372eec1f94..eb3f7704185 100644 --- a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/AircraftVariable.h +++ b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/AircraftVariable.h @@ -59,7 +59,7 @@ class AircraftVariable : public CacheableVariable { * @param setterEventName The calculator code to write to the variable. */ explicit AircraftVariable( - const std::string &varName, + const std::string varName, int varIndex = 0, std::string setterEventName = "", Unit unit = UNITS.Number, @@ -67,7 +67,7 @@ class AircraftVariable : public CacheableVariable { bool autoWriting = false, FLOAT64 maxAgeTime = 0.0, UINT64 maxAgeTicks = 0) - : CacheableVariable(varName, unit, autoReading, autoWriting, maxAgeTime, maxAgeTicks), + : CacheableVariable(std::move(varName), unit, autoReading, autoWriting, maxAgeTime, maxAgeTicks), index(varIndex), setterEventName(std::move(setterEventName)), setterEvent(nullptr) { dataID = get_aircraft_var_enum(varName.c_str()); diff --git a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/CacheableVariable.cpp b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/CacheableVariable.cpp index cb1d78eb495..f9c6d2a0c3f 100644 --- a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/CacheableVariable.cpp +++ b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/CacheableVariable.cpp @@ -21,17 +21,14 @@ FLOAT64 CacheableVariable::get() const { FLOAT64 CacheableVariable::updateFromSim(FLOAT64 timeStamp, UINT64 tickCounter) { if (cachedValue.has_value() && !needsUpdateFromSim(timeStamp, tickCounter)) { - changed = false; + setChanged(false); LOG_TRACE("CacheableVariable::updateFromSim() - from cache " + this->name + " " + str() ); return cachedValue.value(); } - LOG_TRACE("CacheableVariable::updateFromSim() - read from sim " - + this->name - + " " + str() - ); + LOG_TRACE("CacheableVariable::updateFromSim() - read from sim " + this->name + " " + str()); // update the value from the sim timeStampSimTime = timeStamp; tickStamp = tickCounter; @@ -40,20 +37,12 @@ FLOAT64 CacheableVariable::updateFromSim(FLOAT64 timeStamp, UINT64 tickCounter) FLOAT64 CacheableVariable::readFromSim() { const FLOAT64 fromSim = rawReadFromSim(); - // compare the value from the sim with the cached value - changed = !cachedValue.has_value() + bool changed = skipChangeCheck || !cachedValue.has_value() || !helper::Math::almostEqual(fromSim, cachedValue.value(), epsilon); - - // Handling of "changed" - two options - // 1. new field to remember the last value marked as changed and compare it to the new value - // 2. do not update the cache value and discard the sim read (which is a bit of waste) - // Option 2 has been chosen for now as it is simpler and doesn't need the extra field. - if (changed) { - cachedValue = fromSim; - } - + if (changed) cachedValue = fromSim; dirty = false; + setChanged(changed); return cachedValue.value(); } @@ -61,6 +50,7 @@ void CacheableVariable::set(FLOAT64 value) { if (cachedValue.has_value() && cachedValue.value() == value) { return; } + // TODO: should hasChanged be set to true here? Would call all subscribers' callbacks cachedValue = value; dirty = true; } @@ -79,7 +69,6 @@ void CacheableVariable::setAndWriteToSim(FLOAT64 value) { void CacheableVariable::writeToSim() { if (cachedValue.has_value()) { - changed = false; dirty = false; rawWriteToSim(); return; diff --git a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/CacheableVariable.h b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/CacheableVariable.h index a86367cb472..2ae67fcd5cd 100644 --- a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/CacheableVariable.h +++ b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/CacheableVariable.h @@ -49,11 +49,6 @@ class CacheableVariable : public ManagedDataObjectBase { */ bool dirty = false; - /** - * Flag to indicate if the variable has changed compared to the last read/write from the sim. - */ - bool changed = false; - /** * The epsilon required to change a variable after a read from the sim. This is used to * set the changed flag and cache the new value if it is different by >epsilon from the last @@ -80,13 +75,13 @@ class CacheableVariable : public ManagedDataObjectBase { * @param maxAgeTicks The maximum age of the variable in ticks when using updateDataToSim() */ CacheableVariable( - const std::string &varName, + const std::string varName, const Unit &unit, bool autoRead, bool autoWrite, FLOAT64 maxAgeTime, UINT64 maxAgeTicks) - : ManagedDataObjectBase(varName, autoRead, autoWrite, maxAgeTime, maxAgeTicks), unit(unit) {} + : ManagedDataObjectBase(std::move(varName), autoRead, autoWrite, maxAgeTime, maxAgeTicks), unit(unit) {} public: CacheableVariable() = delete; // no default constructor @@ -225,12 +220,6 @@ class CacheableVariable : public ManagedDataObjectBase { */ void setAsInt64(UINT64 i) { set(static_cast(i)); } - /** - * @return true if the value has changed since the last read from the sim. - */ - [[nodiscard]] - bool hasChanged() const { return changed; } - /** * @return Epsilon used for comparing floating point values. Variables are considered equal if the * difference is smaller than this value. diff --git a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/ClientDataAreaVariable.hpp b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/ClientDataAreaVariable.hpp index 14db56d5da0..c7bb8d3ab5f 100644 --- a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/ClientDataAreaVariable.hpp +++ b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/ClientDataAreaVariable.hpp @@ -47,7 +47,7 @@ */ template class ClientDataAreaVariable : public SimObjectBase { -private: +protected: // The client data area ID SIMCONNECT_CLIENT_DATA_ID clientDataId; @@ -74,22 +74,28 @@ class ClientDataAreaVariable : public SimObjectBase { * @param clientDataDefinitionId A unique ID for the client data definition, specified by the client. * This class only supports one definition per client data area, * the definition given by the template parameter type - * @param requestId Each request for sim object data requires a unique id so the sim can provide the request ID in the response (message - * SIMCONNECT_RECV_ID_SIMOBJECT_DATA). - * @param autoReading Used by external classes to determine if the variable should updated from the sim when a sim update call occurs. - * @param autoWriting Used by external classes to determine if the variable should written to the sim when a sim update call occurs. - * @param maxAgeTime The maximum age of the value in sim time before it is updated from the sim by the requestUpdateFromSim() method. - * @param maxAgeTicks The maximum age of the value in ticks before it is updated from the sim by the requestUpdateFromSim() method. + * @param requestId Each request for sim object data requires a unique id so the sim can provide +* the request ID in the response (message SIMCONNECT_RECV_ID_SIMOBJECT_DATA). + * @param autoReading Used by external classes to determine if the variable should updated from + * the sim when a sim update call occurs. + * @param autoWriting Used by external classes to determine if the variable should written to the + * sim when a sim update call occurs. + * @param maxAgeTime The maximum age of the value in sim time before it is updated from the sim by + * the requestUpdateFromSim() method. + * @param maxAgeTicks The maximum age of the value in ticks before it is updated from the sim by + * the requestUpdateFromSim() method. */ - ClientDataAreaVariable(HANDLE hSimConnect, const std::string &clientDataName, - SIMCONNECT_CLIENT_DATA_ID clientDataId, - SIMCONNECT_CLIENT_DATA_DEFINITION_ID clientDataDefinitionId, - SIMCONNECT_DATA_REQUEST_ID requestId, - bool autoRead = false, - bool autoWrite = false, - FLOAT64 maxAgeTime = 0.0, - UINT64 maxAgeTicks = 0) - : SimObjectBase(hSimConnect, clientDataName, clientDataDefinitionId, requestId, + ClientDataAreaVariable( + HANDLE hSimConnect, + const std::string clientDataName, + SIMCONNECT_CLIENT_DATA_ID clientDataId, + SIMCONNECT_CLIENT_DATA_DEFINITION_ID clientDataDefinitionId, + SIMCONNECT_DATA_REQUEST_ID requestId, + bool autoRead = false, + bool autoWrite = false, + FLOAT64 maxAgeTime = 0.0, + UINT64 maxAgeTicks = 0) + : SimObjectBase(hSimConnect, std::move(clientDataName), clientDataDefinitionId, requestId, autoRead, autoWrite, maxAgeTime, maxAgeTicks), clientDataId(clientDataId) { @@ -113,8 +119,6 @@ class ClientDataAreaVariable : public SimObjectBase { hSimConnect, clientDataDefinitionId, SIMCONNECT_CLIENTDATAOFFSET_AUTO, sizeof(T)))) { LOG_ERROR("ClientDataAreaVariable: Adding to client data definition failed: " + name); } - - setDataChanged(false); } /** @@ -149,8 +153,11 @@ class ClientDataAreaVariable : public SimObjectBase { } [[nodiscard]] bool requestDataFromSim() const override { - if (!SUCCEEDED(SimConnect_RequestClientData(hSimConnect, clientDataId, requestId, dataDefId, SIMCONNECT_CLIENT_DATA_PERIOD_ONCE, - SIMCONNECT_CLIENT_DATA_REQUEST_FLAG_DEFAULT))) { + if (!SUCCEEDED(SimConnect_RequestClientData( + hSimConnect, clientDataId, requestId, dataDefId, + SIMCONNECT_CLIENT_DATA_PERIOD_ONCE, + SIMCONNECT_CLIENT_DATA_REQUEST_FLAG_DEFAULT)) + ) { LOG_ERROR("ClientDataAreaVariable: Requesting client data failed: " + name); return false; } @@ -181,11 +188,11 @@ class ClientDataAreaVariable : public SimObjectBase { */ [[nodiscard]] bool requestPeriodicDataFromSim( SIMCONNECT_CLIENT_DATA_PERIOD period, - SIMCONNECT_CLIENT_DATA_REQUEST_FLAG periodFlags = SIMCONNECT_CLIENT_DATA_REQUEST_FLAG_DEFAULT, + SIMCONNECT_CLIENT_DATA_REQUEST_FLAG periodFlags = SIMCONNECT_CLIENT_DATA_REQUEST_FLAG_DEFAULT, DWORD origin = 0, DWORD interval = 0, DWORD limit = 0 - ) const { + ) const { if (autoRead && period >= SIMCONNECT_CLIENT_DATA_PERIOD_ONCE) { LOG_ERROR("ClientDataAreaVariable: Requested periodic data update from sim is ignored as autoRead is enabled."); return false; @@ -218,19 +225,22 @@ class ClientDataAreaVariable : public SimObjectBase { } void processSimData(const SIMCONNECT_RECV* pData, FLOAT64 simTime, UINT64 tickCounter) override { - LOG_INFO("ClientDataAreaVariable: Received client data: " + name); + LOG_TRACE("ClientDataAreaVariable: Received client data: " + name); const auto pClientData = reinterpret_cast(pData); - SIMPLE_ASSERT(pClientData->dwRequestID == requestId, - "DataDefinitionVariable::processSimData: Request ID mismatch") + + SIMPLE_ASSERT(pClientData->dwRequestID == requestId, + "ClientDataAreaVariable::processSimData: Request ID mismatch: " + name); + // if not required then skip the rather expensive check for change - dataChanged = skipChangeCheck || std::memcmp(&pClientData->dwData, &this->dataStruct, sizeof(T)) != 0; - if (dataChanged) { + if (skipChangeCheck || std::memcmp(&pClientData->dwData, &this->dataStruct, sizeof(T)) != 0) { LOG_TRACE("ClientDataAreaVariable: Data has changed: " + name); std::memcpy(&this->dataStruct, &pClientData->dwData, sizeof(T)); timeStampSimTime = simTime; tickStamp = tickCounter; + setChanged(true); return; } + setChanged(false); LOG_TRACE("ClientDataAreaVariable: Data has not changed: " + name); } @@ -238,13 +248,12 @@ class ClientDataAreaVariable : public SimObjectBase { if (!SUCCEEDED(SimConnect_SetClientData(hSimConnect, clientDataId, dataDefId, SIMCONNECT_CLIENT_DATA_SET_FLAG_DEFAULT, 0, sizeof(T), &this->dataStruct))) { - LOG_ERROR("DataDefinitionVariable: Setting data to sim for " + name + LOG_ERROR("ClientDataAreaVariable: Setting data to sim for " + name + " with dataDefId=" + std::to_string(dataDefId) + " failed!"); return false; } - LOG_TRACE("DataDefinitionVariable: Setting data to sim for " + name + LOG_TRACE("ClientDataAreaVariable: Setting data to sim for " + name + " with dataDefId=" + std::to_string(dataDefId) + " succeeded."); - setDataChanged(false); return true; } @@ -252,18 +261,18 @@ class ClientDataAreaVariable : public SimObjectBase { * Returns a modifiable reference to the data container * @return T& Reference to the data container */ - [[maybe_unused]] [[nodiscard]] T &data() { return dataStruct; } + T &data() { return dataStruct; } /** * Returns a constant reference to the data container * @return std::vector& Reference to the data container */ - [[maybe_unused]] [[nodiscard]] const T &data() const { return dataStruct; } + const T &data() const { return dataStruct; } [[nodiscard]] std::string str() const override { std::stringstream ss; - ss << "DataDefinition[ name=" << getName(); + ss << "ClientDataAreaVariable[ name=" << getName(); ss << ", clientDataId=" << clientDataId; ss << ", dataDefId=" << dataDefId; ss << ", requestId=" << requestId; @@ -271,7 +280,7 @@ class ClientDataAreaVariable : public SimObjectBase { ss << ", timeStamp: " << timeStampSimTime; ss << ", tickStamp: " << tickStamp; ss << ", skipChangeCheck: " << skipChangeCheck; - ss << ", dataChanged: " << dataChanged; + ss << ", dataChanged: " << hasChanged(); ss << ", autoRead: " << autoRead; ss << ", autoWrite: " << autoWrite; ss << ", maxAgeTime: " << maxAgeTime; diff --git a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataDefinitionVariable.hpp b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataDefinitionVariable.hpp index b40567cb606..a7064d4e6b7 100644 --- a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataDefinitionVariable.hpp +++ b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataDefinitionVariable.hpp @@ -99,7 +99,7 @@ class DataDefinitionVariable : public SimObjectBase { */ DataDefinitionVariable( HANDLE hSimConnect, - const std::string &varName, + const std::string varName, const std::vector &dataDefinitions, SIMCONNECT_DATA_DEFINITION_ID dataDefId, SIMCONNECT_DATA_REQUEST_ID requestId, @@ -108,12 +108,9 @@ class DataDefinitionVariable : public SimObjectBase { FLOAT64 maxAgeTime = 0.0, UINT64 maxAgeTicks = 0 ) - : SimObjectBase(hSimConnect, varName, dataDefId, requestId, autoRead, autoWrite, maxAgeTime, maxAgeTicks), + : SimObjectBase(hSimConnect, std::move(varName), dataDefId, requestId, autoRead, autoWrite, maxAgeTime, maxAgeTicks), dataDefinitions(dataDefinitions), dataStruct{} { - SIMPLE_ASSERT(sizeof(T) == dataDefinitions.size() * sizeof(FLOAT64), - "DataDefinitionVariable::processSimData: Struct size mismatch") - for (auto &ddef: dataDefinitions) { std::string fullVarName = ddef.name; if (ddef.index != 0) fullVarName += ":" + std::to_string(ddef.index); @@ -148,8 +145,8 @@ class DataDefinitionVariable : public SimObjectBase { requestId, dataDefId, SIMCONNECT_OBJECT_ID_USER, - SIMCONNECT_PERIOD_ONCE))) { - + SIMCONNECT_PERIOD_ONCE)) + ) { LOG_ERROR("DataDefinitionVariable: Failed to request data from sim: " + name); return false; } @@ -216,19 +213,20 @@ class DataDefinitionVariable : public SimObjectBase { void processSimData(const SIMCONNECT_RECV* pData, FLOAT64 simTime, UINT64 tickCounter) override { LOG_TRACE("DataDefinitionVariable: Received client data: " + name); const auto pSimobjectData = reinterpret_cast(pData); - SIMPLE_ASSERT(sizeof(T) == pSimobjectData->dwDefineCount * sizeof(FLOAT64), - "DataDefinitionVariable::processSimData: Struct size mismatch") - SIMPLE_ASSERT(pSimobjectData->dwRequestID == requestId, - "DataDefinitionVariable::processSimData: Request ID mismatch") + + SIMPLE_ASSERT(pSimobjectData->dwRequestID == requestId, + "DataDefinitionVariable::processSimData: Request ID mismatch: " + name); + // if not required then skip the rather expensive check for change - dataChanged = skipChangeCheck || std::memcmp(&pSimobjectData->dwData, &this->dataStruct, sizeof(T)) != 0; - if (dataChanged) { + if (skipChangeCheck || std::memcmp(&pSimobjectData->dwData, &this->dataStruct, sizeof(T)) != 0) { LOG_TRACE("DataDefinitionVariable: Data has changed: " + name); std::memcpy(&this->dataStruct, &pSimobjectData->dwData, sizeof(T)); timeStampSimTime = simTime; tickStamp = tickCounter; + setChanged(true); return; } + setChanged(false); LOG_TRACE("DataDefinitionVariable: Data has not changed: " + name); }; @@ -240,7 +238,6 @@ class DataDefinitionVariable : public SimObjectBase { + std::to_string(dataDefId) + " failed!"); return false; } - setDataChanged(false); return true; }; @@ -277,7 +274,7 @@ class DataDefinitionVariable : public SimObjectBase { ss << ", timeStamp: " << timeStampSimTime; ss << ", tickStamp: " << tickStamp; ss << ", skipChangeCheck: " << skipChangeCheck; - ss << ", dataChanged: " << dataChanged; + ss << ", dataChanged: " << hasChanged(); ss << ", autoRead: " << autoRead; ss << ", autoWrite: " << autoWrite; ss << ", maxAgeTime: " << maxAgeTime; diff --git a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataManager.cpp b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataManager.cpp index bae839c3db4..71d4875fce5 100644 --- a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataManager.cpp +++ b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataManager.cpp @@ -30,11 +30,11 @@ bool DataManager::preUpdate([[maybe_unused]] sGaugeDrawData* pData) { if (var.second->isAutoRead()) { var.second->updateFromSim(timeStamp, tickCounter); LOG_VERBOSE_BLOCK( - if (tickCounter % 100 == 0) { - std::cout << "DataManager::preUpdate() - auto read named and aircraft: " - << var.second->getName() << " = " << var.second->get() - << std::endl; - }) + if (tickCounter % 100 == 0) { + std::cout << "DataManager::preUpdate() - auto read named and aircraft: " + << var.second->getName() << " = " << var.second->get() + << std::endl; + }) } } @@ -46,11 +46,11 @@ bool DataManager::preUpdate([[maybe_unused]] sGaugeDrawData* pData) { + ddv.second->getName()); } LOG_VERBOSE_BLOCK( - if (tickCounter % 100 == 0) { - std::cout << "DataManager::preUpdate() - auto read simobjects: " - << ddv.second->getName() - << std::endl; - }) + if (tickCounter % 100 == 0) { + std::cout << "DataManager::preUpdate() - auto read simobjects: " + << ddv.second->getName() + << std::endl; + }) } } @@ -82,11 +82,11 @@ bool DataManager::postUpdate([[maybe_unused]] sGaugeDrawData* pData) { if (var.second->isAutoWrite()) { var.second->updateToSim(); LOG_VERBOSE_BLOCK( - if (tickCounter % 100 == 0) { - std::cout << "DataManager::postUpdate() - auto write named and aircraft: " - << var.second->getName() << " = " << var.second->get() - << std::endl; - }) + if (tickCounter % 100 == 0) { + std::cout << "DataManager::postUpdate() - auto write named and aircraft: " + << var.second->getName() << " = " << var.second->get() + << std::endl; + }) } } @@ -98,11 +98,11 @@ bool DataManager::postUpdate([[maybe_unused]] sGaugeDrawData* pData) { + ddv.second->getName()); } LOG_VERBOSE_BLOCK( - if (tickCounter % 100 == 0) { - std::cout << "DataManager::postUpdate() - auto write simobjects" - << ddv.second->getName() - << std::endl; - }) + if (tickCounter % 100 == 0) { + std::cout << "DataManager::postUpdate() - auto write simobjects" + << ddv.second->getName() + << std::endl; + }) } } @@ -159,7 +159,7 @@ void DataManager::processKeyEvent( // + " userdata=" + std::to_string((UINT64) userdata)); } -NamedVariablePtr DataManager::make_named_var(const std::string &varName, +NamedVariablePtr DataManager::make_named_var(const std::string varName, Unit unit, bool autoReading, bool autoWriting, @@ -168,7 +168,7 @@ NamedVariablePtr DataManager::make_named_var(const std::string &varName, // The name needs to contain all the information to identify the variable // and the expected value uniquely. This is because the same variable can be // used in different places with different expected values via Units. - const std::string uniqueName = varName + ":" + unit.name; + const std::string uniqueName{varName + ":" + unit.name}; // Check if variable already exists // Check which update method and frequency to use - if two variables are the same @@ -192,7 +192,7 @@ NamedVariablePtr DataManager::make_named_var(const std::string &varName, // Create new var and store it in the map NamedVariablePtr var - = std::make_shared(varName, unit, autoReading, autoWriting, maxAgeTime, maxAgeTicks); + = std::make_shared(std::move(varName), unit, autoReading, autoWriting, maxAgeTime, maxAgeTicks); variables[uniqueName] = var; @@ -201,9 +201,9 @@ NamedVariablePtr DataManager::make_named_var(const std::string &varName, return var; } -AircraftVariablePtr DataManager::make_aircraft_var(const std::string &varName, +AircraftVariablePtr DataManager::make_aircraft_var(const std::string varName, int index, - const std::string &setterEventName, + const std::string setterEventName, EventPtr setterEvent, Unit unit, bool autoReading, @@ -213,7 +213,7 @@ AircraftVariablePtr DataManager::make_aircraft_var(const std::string &varName, // The name needs to contain all the information to identify the variable // and the expected value uniquely. This is because the same variable can be // used in different places with different expected values via Index and Units. - const std::string uniqueName = varName + ":" + std::to_string(index) + ":" + unit.name; + const std::string uniqueName{varName + ":" + std::to_string(index) + ":" + unit.name}; // Check if variable already exists // Check which update method and frequency to use - if two variables are the same @@ -236,15 +236,14 @@ AircraftVariablePtr DataManager::make_aircraft_var(const std::string &varName, return std::dynamic_pointer_cast(pair->second); } + // Create new var and store it in the map AircraftVariablePtr var; - if (setterEventName.empty()) { - var = setterEventName.empty() ? std::make_shared(varName, index, std::move(setterEvent), unit, autoReading, - autoWriting, maxAgeTime, maxAgeTicks) - : std::make_shared(varName, index, setterEventName, unit, autoReading, autoWriting, - maxAgeTime, maxAgeTicks); - } - + var = setterEventName.empty() + ? std::make_shared(std::move(varName), index, setterEvent, + unit, autoReading, autoWriting, maxAgeTime, maxAgeTicks) + : std::make_shared(std::move(varName), index, std::move(setterEventName), + unit, autoReading, autoWriting, maxAgeTime, maxAgeTicks); variables[uniqueName] = var; LOG_DEBUG("DataManager::make_aircraft_var(): created variable " + var->str()); @@ -252,11 +251,13 @@ AircraftVariablePtr DataManager::make_aircraft_var(const std::string &varName, return var; } -AircraftVariablePtr DataManager::make_simple_aircraft_var(const std::string &varName, - Unit unit, - bool autoReading, - FLOAT64 maxAgeTime, - UINT64 maxAgeTicks) { +AircraftVariablePtr DataManager::make_simple_aircraft_var( + const std::string varName, + Unit unit, + bool autoReading, + FLOAT64 maxAgeTime, + UINT64 maxAgeTicks) { + // The name needs to contain all the information to identify the variable // and the expected value uniquely. This is because the same variable can be // used in different places with different expected values via Index and Units. @@ -280,7 +281,7 @@ AircraftVariablePtr DataManager::make_simple_aircraft_var(const std::string &var } // Create new var and store it in the map - AircraftVariablePtr var = std::make_shared(varName, 0, "", unit, autoReading, false, maxAgeTime, maxAgeTicks); + AircraftVariablePtr var = std::make_shared(std::move(varName), 0, "", unit, autoReading, false, maxAgeTime, maxAgeTicks); variables[uniqueName] = var; @@ -301,7 +302,7 @@ EventPtr DataManager::make_event( } const SIMCONNECT_CLIENT_EVENT_ID id = eventIDGen.getNextId(); - EventPtr event = std::make_shared(hSimConnect, eventName, id, maksEvent); + EventPtr event = std::make_shared(hSimConnect, std::move(eventName), id, maksEvent); events[id] = event; @@ -315,14 +316,8 @@ EventPtr DataManager::make_event( void DataManager::processDispatchMessage(SIMCONNECT_RECV* pRecv, [[maybe_unused]] DWORD* cbData) { switch (pRecv->dwID) { - case SIMCONNECT_RECV_ID_SIMOBJECT_DATA: - // fallthrough + case SIMCONNECT_RECV_ID_SIMOBJECT_DATA: // fallthrough case SIMCONNECT_RECV_ID_CLIENT_DATA: - // We are using the same callback for both SIMOBJECT_DATA and CLIENT_DATA - // as SIMCONNECT_RECV_CLIENT_DATA is a specialization of SIMCONNECT_RECV_SIMOBJECT_DATA, - // and we do not need to distinguish between them here. - // Alternatively, we could have used SIMCONNECT_RECV and then cast to the appropriate type - // later. processSimObjectData(pRecv); break; @@ -385,3 +380,4 @@ void DataManager::processEvent(const SIMCONNECT_RECV_EVENT_EX1* pRecv) { LOG_WARN("DataManager::processEvent() - unknown event id: " + std::to_string(pRecv->uEventID)); } + diff --git a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataManager.h b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataManager.h index d5e9e0fe7f3..3467be28501 100644 --- a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataManager.h +++ b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataManager.h @@ -62,7 +62,7 @@ class DataManager { std::map events{}; // Backreference to the MsfsHandler instance. - MsfsHandler *msfsHandler; + MsfsHandler* msfsHandler; // Handle to the simconnect instance. HANDLE hSimConnect{}; @@ -81,7 +81,7 @@ class DataManager { /** * Creates an instance of the DataManager. */ - explicit DataManager(MsfsHandler *msfsHdl) : msfsHandler(msfsHdl) {} + explicit DataManager(MsfsHandler* msfsHdl) : msfsHandler(msfsHdl) {} DataManager() = delete; // no default constructor DataManager(const DataManager &) = delete; // no copy constructor @@ -151,7 +151,7 @@ class DataManager { * @see Units.h for available units */ NamedVariablePtr make_named_var( - const std::string &varName, + const std::string varName, Unit unit = UNITS.Number, bool autoReading = false, bool autoWriting = false, @@ -175,9 +175,9 @@ class DataManager { * @see Units.h for available units */ AircraftVariablePtr make_aircraft_var( - const std::string &varName, + const std::string varName, int index = 0, - const std::string &setterEventName = "", + const std::string setterEventName = "", EventPtr setterEvent = nullptr, Unit unit = UNITS.Number, bool autoReading = false, @@ -198,7 +198,7 @@ class DataManager { * @see Units.h for available units */ AircraftVariablePtr make_simple_aircraft_var( - const std::string &varName, + const std::string varName, Unit unit = UNITS.Number, bool autoReading = false, FLOAT64 maxAgeTime = 0.0, @@ -217,17 +217,17 @@ class DataManager { */ template std::shared_ptr> make_datadefinition_var( - const std::string &name, - std::vector &dataDefinitions, + const std::string name, + std::vector&dataDefinitions, bool autoReading = false, - bool autoWriting = false, - FLOAT64 maxAgeTime = 0.0, - UINT64 maxAgeTicks = 0) { + bool autoWriting = false, FLOAT64 + maxAgeTime = 0.0, UINT64 + maxAgeTicks = 0) { - std::shared_ptr> var = - std::make_shared>( + std::shared_ptr> + var = std::make_shared> ( hSimConnect, - name, + std::move(name), dataDefinitions, dataDefIDGen.getNextId(), dataReqIDGen.getNextId(), @@ -237,9 +237,7 @@ class DataManager { maxAgeTicks); LOG_DEBUG("DataManager::make_datadefinition_var(): " + name); - simObjects.insert({var->getRequestId(), var}); - return var; } @@ -261,16 +259,16 @@ class DataManager { */ template std::shared_ptr> make_clientdataarea_var( - const std::string &clientDataName, + const std::string clientDataName, bool autoReading = false, bool autoWriting = false, FLOAT64 maxAgeTime = 0.0, UINT64 maxAgeTicks = 0) { - std::shared_ptr> var = - std::make_shared>( + std::shared_ptr > + var = std::make_shared> ( hSimConnect, - clientDataName, + std::move(clientDataName), clientDataIDGen.getNextId(), dataDefIDGen.getNextId(), dataReqIDGen.getNextId(), @@ -280,9 +278,7 @@ class DataManager { maxAgeTicks); LOG_DEBUG("DataManager::make_datadefinition_var(): " + clientDataName); - simObjects.insert({var->getRequestId(), var}); - return var; } diff --git a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataObjectBase.h b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataObjectBase.h index dbc824017f5..ecf06db01e9 100644 --- a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataObjectBase.h +++ b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/DataObjectBase.h @@ -19,6 +19,7 @@ class DataObjectBase { protected: + /** * The name of the variable in the sim */ diff --git a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/Event.cpp b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/Event.cpp index eacd45dd307..7a585612f64 100644 --- a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/Event.cpp +++ b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/Event.cpp @@ -8,7 +8,7 @@ Event::Event( HANDLE hdlSimConnect, const std::string &eventName, DWORD eventClientId, bool maskEvent) - : hSimConnect(hdlSimConnect), eventName(eventName), eventClientID(eventClientId), + : hSimConnect(hdlSimConnect), eventName(std::move(eventName)), eventClientID(eventClientId), maskEvent(maskEvent) { if (!SUCCEEDED(SimConnect_MapClientEventToSimEvent(hSimConnect, eventClientID, eventName.c_str()))) { @@ -58,7 +58,7 @@ void Event::trigger_ex1(DWORD data0, DWORD data1, DWORD data2, DWORD data3, DWOR + std::to_string(data3) + ", " + std::to_string(data4)); } -CallbackID Event::addCallback(const CallbackFunction &callback) { +CallbackID Event::addCallback(const EventCallbackFunction &callback) { const auto id = callbackIdGen.getNextId(); callbacks.insert({id, callback}); if (!isSubscribedToSim) { diff --git a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/Event.h b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/Event.h index 5b2262a7eed..65ea24abb2c 100644 --- a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/Event.h +++ b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/Event.h @@ -23,7 +23,7 @@ typedef uint64_t CallbackID; */ typedef std::function CallbackFunction; + DWORD param4)> EventCallbackFunction; /** * Event class to wrap SimConnect events providing trigger and callback registration. @@ -56,7 +56,7 @@ class Event { /** * Map of callbacks to be called when the event is triggered in the sim. */ - std::map callbacks; + std::map callbacks; /** * Flag to indicate if the event is registered with the sim. @@ -127,7 +127,7 @@ class Event { * @return The ID of the callback required for removing a callback. */ [[nodiscard]] - CallbackID addCallback(const CallbackFunction &callback); + CallbackID addCallback(const EventCallbackFunction &callback); /** * Removes a callback from the event. diff --git a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/ManagedDataObjectBase.h b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/ManagedDataObjectBase.h index 8775e0058ac..19c1f9db90b 100644 --- a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/ManagedDataObjectBase.h +++ b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/ManagedDataObjectBase.h @@ -15,17 +15,53 @@ #include #include "Units.h" +#include "IDGenerator.h" +#include "Callback.h" #include "DataObjectBase.h" +// Used for callback registration to allow removal of callbacks +typedef uint64_t CallbackID; + +/** + * Defines a callback function for an event + * @param number of parameters to use + * @param parameters 0-4 to pass to the callback function + */ +typedef std::function CallbackFunction; + /** * Base class for all managed data objects. * Adds the ability to autoRead, autoWrite variables considering max age based on * time- and tick-stamps. + * Also adds a hasChanged flag and the ability to register callbacks for when + * the variable changes (TODO). */ class ManagedDataObjectBase : public DataObjectBase { +private: + + /** + * Used to generate unique IDs for callbacks. + */ + IDGenerator callbackIdGen{}; + + /** + * Map of callbacks to be called when the event is triggered in the sim. + */ + std::map callbacks; + + // Flag to indicate if the variable has changed compared to the last read/write from the sim. + // Private because it should only be set by the setChanged() method so callbacks from + // listeners can be triggered. + bool changed = false; protected: + /** + * Flag to indicate if the check for data changes should be skipped to save performance when the + * check is not required. + */ + bool skipChangeCheck = false; + /** * Used by external classes to determine if the data should be updated from the sim when * a sim update call occurs. Updates are currently done manually by the external classes. @@ -73,21 +109,64 @@ class ManagedDataObjectBase : public DataObjectBase { * @param maxAgeTicks the maximum age of the value in ticks before it is updated from the sim */ ManagedDataObjectBase( - const std::string &varName, + const std::string varName, bool autoRead, bool autoWrite, FLOAT64 maxAgeTime, UINT64 maxAgeTicks) - : DataObjectBase(varName), autoRead(autoRead), autoWrite(autoWrite), maxAgeTime(maxAgeTime), + : DataObjectBase(std::move(varName)), autoRead(autoRead), autoWrite(autoWrite), maxAgeTime(maxAgeTime), maxAgeTicks(maxAgeTicks) {} ~ManagedDataObjectBase() override = default; + /** + * Sets the changed flag to the given value and triggers the registered callbacks + * if the value has changed. + * TODO: Implement listener registration and callbacks + * @param changed the new value for the changed flag + */ + void setChanged(bool changed) { + this->changed = changed; + if (changed) { + for (const auto &[id, callback]: callbacks) { + callback(); + } + } + } + public: ManagedDataObjectBase() = delete; // no default constructor ManagedDataObjectBase(const ManagedDataObjectBase &) = delete; // no copy constructor ManagedDataObjectBase &operator=(const ManagedDataObjectBase &) = delete; // no copy assignment + /** + * Adds a callback function to be called when the event is triggered in the sim. + * The first callback also registers the event to the sim. + * @param callback + * @return The ID of the callback required for removing a callback. + */ + CallbackID addCallback(const CallbackFunction &callback) { + const auto id = callbackIdGen.getNextId(); + callbacks.insert({id, callback}); + LOG_DEBUG("Added callback to data object " + name + " with callback ID " + std::to_string(id)); + return id; + } + + /** + * Removes a callback from the event. + * The last callback also unregisters the event from the sim. + * @param callbackId The ID receive when adding the callback. + */ + bool removeCallback(CallbackID callbackId) { + if (auto pair = callbacks.find(callbackId); pair != callbacks.end()) { + callbacks.erase(pair); + LOG_DEBUG("Removed callback from data object " + name + " with callback ID " + std::to_string(callbackId)); + return true; + } + LOG_WARN("Failed to remove callback with ID " + std::to_string(callbackId) + " from data object " + str()); + return false; + } + /** * Checks if the variable needs to be updated from the sim based on the given time stamp * and tickCounter.

@@ -104,6 +183,29 @@ class ManagedDataObjectBase : public DataObjectBase { return (timeStampPlusAge < timeStamp && tickStampPlusAge < tickCounter); } + /** + * When this is true every read from the sim will set the changed flag to true + * no matter if the value has changed or not. + * When this is false the changed flag will be set to true only if the value + * has actually changed. + * @return true if the value has changed since the last read from the sim. + */ + [[nodiscard]] + bool hasChanged() const { return this->changed; } + + /** + * @return true if the check for data changes should be skipped to save performance when the check is not required, false otherwise + */ + [[nodiscard]] bool getSkipChangeCheck() const { return skipChangeCheck; } + + /** + * Sets the flag to skip the check for data changes to save performance when + * the check is not required. When this is set every read from the sim will + * set the changed flag to true no matter if the value has changed or not. + * @param changeCheck + */ + void setSkipChangeCheck(bool skipChangeCheck) { this->skipChangeCheck = skipChangeCheck; } + /** * @return true if the variable should be automatically updated from the sim n the DataManagers * postUpdate() method. diff --git a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/NamedVariable.cpp b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/NamedVariable.cpp index d02acee14c0..b71857aa842 100644 --- a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/NamedVariable.cpp +++ b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/NamedVariable.cpp @@ -5,21 +5,10 @@ #include -#include "logging.h" #include "NamedVariable.h" FLOAT64 NamedVariable::rawReadFromSim() { - const FLOAT64 d = get_named_variable_typed_value(dataID, unit.id); - // DEBUG - // if (name == "A32NX_DEBUG_LVAR") { - // LOG_DEBUG("NamedVariable::rawReadFromSim() " - // + this->name - // + " fromSim = " + std::to_string(d) - // + " cached = " + std::to_string(cachedValue.value_or(-999999)) - // + " as " + unit.name - // ); - // } - return d; + return get_named_variable_typed_value(dataID, unit.id); } void NamedVariable::rawWriteToSim() { @@ -31,7 +20,7 @@ std::string NamedVariable::str() const { ss << "NamedVariable: [" << name; ss << ", value: " << (cachedValue.has_value() ? std::to_string(cachedValue.value()) : "N/A"); ss << ", unit: " << unit.name; - ss << ", changed: " << changed; + ss << ", changed: " << hasChanged(); ss << ", dirty: " << dirty; ss << ", timeStamp: " << timeStampSimTime; ss << ", tickStamp: " << tickStamp; diff --git a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/NamedVariable.h b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/NamedVariable.h index ffcf8cb8597..1295ce09c81 100644 --- a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/NamedVariable.h +++ b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/NamedVariable.h @@ -39,7 +39,7 @@ class NamedVariable : public CacheableVariable { * @param maxAgeTicks The maximum age of an auto updated variable in sim ticks. */ explicit NamedVariable( - const std::string &varName, + const std::string& varName, Unit unit = UNITS.Number, bool autoReading = false, bool autoWriting = false, diff --git a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/SimObjectBase.h b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/SimObjectBase.h index bedcbb5970a..e72ed25fa0e 100644 --- a/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/SimObjectBase.h +++ b/fbw-common/src/wasm/extra-backend/MsfsHandler/DataManager/SimObjectBase.h @@ -38,18 +38,6 @@ class SimObjectBase : public ManagedDataObjectBase { */ SIMCONNECT_DATA_REQUEST_ID requestId = 0; - /** - * Flag to indicate if the check for data changes should be skipped to save performance when the - * check is not required. - */ - bool skipChangeCheck = false; - - /** - * Flag to indicate if the data has been changed compared to the corrent data held in the - * datastruct. - */ - bool dataChanged = false; - /** * Creates a new instance of a DataDefinitionVariable. * @param hSimConnect Handle to the SimConnect object. @@ -63,26 +51,16 @@ class SimObjectBase : public ManagedDataObjectBase { */ SimObjectBase( HANDLE hSimConnect, - const std::string &varName, + const std::string varName, DWORD dataDefId, DWORD requestId, bool autoRead = false, bool autoWrite = false, FLOAT64 maxAgeTime = 0.0, UINT64 maxAgeTicks = 0) - : ManagedDataObjectBase(varName, autoRead, autoWrite, maxAgeTime, maxAgeTicks), + : ManagedDataObjectBase(std::move(varName), autoRead, autoWrite, maxAgeTime, maxAgeTicks), hSimConnect(hSimConnect), dataDefId(dataDefId), requestId(requestId) {} - - /** - * Sets the data changed flag after a reading from the sim which is not identical to the current - * data in the datastruct. - * @param hasChanged true if the data has changed, false otherwise - */ - void setDataChanged(bool hasChanged) { - SimObjectBase::dataChanged = hasChanged; - } - public: SimObjectBase() = delete; // no default constructor @@ -141,23 +119,6 @@ class SimObjectBase : public ManagedDataObjectBase { [[nodiscard]] DWORD getRequestId() const { return requestId; } - /** - * @return true if the data is not identical to the current data in the struct, false otherwise - */ - [[nodiscard]] bool hasDataChanged() const { return dataChanged; } - - /** - * @return true if the check for data changes should be skipped to save performance when the check is not required, false otherwise - */ - [[nodiscard]] bool isSkipChangeCheck() const { return skipChangeCheck; } - - /** - * Sets the flag to skip the check for data changes to save performance when the check is not required. - * @param changeCheck - */ - void setSkipChangeCheck(bool changeCheck) { SimObjectBase::skipChangeCheck = changeCheck; } - - }; #endif //FLYBYWIRE_A32NX_SIMOBJECTBASE_H