From 3dfb0b9aff34494dc433e147f37a535564b19905 Mon Sep 17 00:00:00 2001 From: Wei Shuai Date: Thu, 14 Mar 2024 15:10:54 +0800 Subject: [PATCH 1/2] Avoid duplicated reading RPN code execution across multiple clients While multiple clients Add(MF.SimVars.Add.xxxxRPNcodexxx) the exact same SimVars to WASM, From MSFS2020 perspective, it is unnecessary to execute the same RPNcode several times in each frame. This is an enhancement change to reduce FPS impact WASM will smartly execute duplicated SimVars only once in each frame. introduce new data structure: //RPN code execution for reading values in every frame struct ReadRPNCode { std::string Code; int RetType; //RetType: 0:float 1:integer 2:string std::vector SimVars; std::vector StringSimVars; }; -- This patch is stable because we have 1000+ customers who have been using these changes over a year --- src/Sources/Code/Module.cpp | 182 ++++++++++++++++++++++++++---------- 1 file changed, 131 insertions(+), 51 deletions(-) diff --git a/src/Sources/Code/Module.cpp b/src/Sources/Code/Module.cpp index afe5a3e..16d6281 100644 --- a/src/Sources/Code/Module.cpp +++ b/src/Sources/Code/Module.cpp @@ -44,19 +44,22 @@ uint16_t MOBIFLIGHT_MAX_VARS_PER_FRAME = 30; // due to the maximum client-data-array-size (SIMCONNECT_CLIENTDATA_MAX_SIZE) of 8kB! constexpr uint16_t MOBIFLIGHT_STRING_SIMVAR_VALUE_MAX_LEN = 128; +//declare struct Client +struct Client; + // data struct for dynamically registered SimVars struct SimVar { int ID; int Offset; - std::string Name; float Value; + struct Client * client; }; struct StringSimVar { int ID; int Offset; - std::string Name; std::string Value; + struct Client * client; }; // data struct for client accessing SimVars @@ -81,12 +84,23 @@ struct Client { // This is an optimization to be able to re-use already defined data definition IDs & request IDs // after resetting registered SimVars uint16_t MaxClientDataDefinition = 0; - // Runtime Rolling CLient Data reading Index - //std::vector::iterator RollingClientDataReadIndex; - uint16_t RollingClientDataReadIndex; +}; + +// Runtime Rolling CLient Data reading Index +uint16_t RollingDataReadIndex = 0; +//RPN code execution for reading values in every frame +struct ReadRPNCode { + std::string Code; + //RetType: 0:float 1:integer 2:string + int RetType; + std::vector SimVars; + std::vector StringSimVars; }; +std::vector RPNCodelist; + + // The list of currently registered clients std::vector RegisteredClients; @@ -255,10 +269,10 @@ void WriteSimVar(StringSimVar& simVar, Client* client) ); if (hr != S_OK) { - fprintf(stderr, "MobiFlight[%s]: Error on Setting String Client Data. %lu, SimVar: %s (String-ID: %ul)\n", client->Name.c_str(), hr, simVar.Name.c_str(), simVar.ID); + fprintf(stderr, "MobiFlight[%s]: Error on Setting String Client Data. %lu, SimVar: (String-ID: %ul)\n", client->Name.c_str(), hr, simVar.ID); } #if _DEBUG - std::cout << "MobiFlight[" << client->Name.c_str() << "]: Written String-SimVar " << simVar.Name.c_str(); + std::cout << "MobiFlight[" << client->Name.c_str() << "]: Written String-SimVar"; std::cout << " with String-ID " << simVar.ID << " has value " << simVar.Value.c_str() << std::endl; #endif } @@ -276,27 +290,49 @@ void WriteSimVar(SimVar& simVar, Client* client) { ); if (hr != S_OK) { - fprintf(stderr, "MobiFlight[%s]: Error on Setting Client Data. %lu, SimVar: %s (ID: %u)", client->Name.c_str(), hr, simVar.Name.c_str(), simVar.ID); + fprintf(stderr, "MobiFlight[%s]: Error on Setting Client Data. %lu, SimVar: (ID: %u)", client->Name.c_str(), hr, simVar.ID); } #if _DEBUG - std::cout << "MobiFlight[" << client->Name.c_str() << "]: Written SimVar " << simVar.Name.c_str(); + std::cout << "MobiFlight[" << client->Name.c_str() << "]: Written SimVar"; std::cout << " with ID " << simVar.ID << " has value " << simVar.Value << std::endl; #endif } +//check whether SimVar has already registered in SimVar list +ReadRPNCode* IsDuplicatedSimVar(const std::string code) { + for (auto &rpn : RPNCodelist) { + if (rpn.Code == code) { + return &rpn; + } + } + return nullptr; +} + // Register a single Float-SimVar and send the current value to SimConnect Clients void RegisterFloatSimVar(const std::string code, Client* client) { std::vector* SimVars = &(client->SimVars); std::vector* StringSimVars = &(client->StringSimVars); SimVar newSimVar; + ReadRPNCode* pdupRpn = IsDuplicatedSimVar(code); HRESULT hr; - newSimVar.Name = code; newSimVar.ID = SimVars->size() + client->DataDefinitionIdSimVarsStart; newSimVar.Offset = SimVars->size() * (sizeof(float)); newSimVar.Value = 0.0F; + newSimVar.client = client; SimVars->push_back(newSimVar); + //duplicated SimVar + if (pdupRpn) { + pdupRpn->SimVars.push_back(newSimVar); + } else { + ReadRPNCode rpnCode; + rpnCode.Code = code; + rpnCode.RetType = 0;//hardcoded type id + rpnCode.SimVars.push_back(newSimVar); + RPNCodelist.push_back(rpnCode); + } + if (client->MaxClientDataDefinition < (SimVars->size() + StringSimVars->size())) { hr = SimConnect_AddToClientDataDefinition( g_hSimConnect, @@ -307,11 +343,11 @@ void RegisterFloatSimVar(const std::string code, Client* client) { ); if (hr != S_OK) { - fprintf(stderr, "MobiFlight[%s]: Error on adding Client Data \"%s\" with ID: %u, Offset: %u and Size: %lu\n", client->Name.c_str(), newSimVar.Name.c_str(), newSimVar.ID, newSimVar.Offset, sizeof(float)); + fprintf(stderr, "MobiFlight[%s]: Error on adding Client Data \"%s\" with ID: %u, Offset: %u and Size: %lu\n", client->Name.c_str(), code.c_str(), newSimVar.ID, newSimVar.Offset, sizeof(float)); } #if _DEBUG else { - std::cout << "MobiFlight[" << client->Name.c_str() << "]: Added SimVar > " << newSimVar.Name.c_str(); + std::cout << "MobiFlight[" << client->Name.c_str() << "]: Added SimVar > " << code.c_str(); std::cout << " with ID: " << newSimVar.ID << ", Offset: " << newSimVar.Offset << " and Size: " << sizeof(float) << std::endl; } std::cout << "MobiFlight[" << client->Name.c_str() << "]: RegisterFloatSimVar SimVars Size: " << SimVars->size() << std::endl; @@ -324,7 +360,7 @@ void RegisterFloatSimVar(const std::string code, Client* client) { newSimVar.Value = floatVal; WriteSimVar(newSimVar, client); #if _DEBUG - std::cout << "MobiFlight[" << client->Name.c_str() << "]: RegisterFloatSimVar > " << newSimVar.Name.c_str(); + std::cout << "MobiFlight[" << client->Name.c_str() << "]: RegisterFloatSimVar > " << code.c_str(); std::cout << " ID [" << newSimVar.ID << "] : Offset(" << newSimVar.Offset << ") : Value(" << newSimVar.Value << ")" << std::endl; #endif } @@ -334,14 +370,26 @@ void RegisterStringSimVar(const std::string code, Client* client) { std::vector* SimVars = &(client->SimVars); std::vector* StringSimVars = &(client->StringSimVars); StringSimVar newStringSimVar; + ReadRPNCode* pdupRpn = IsDuplicatedSimVar(code); HRESULT hr; - newStringSimVar.Name = code; newStringSimVar.ID = StringSimVars->size() + client->DataDefinitionIdStringVarsStart; newStringSimVar.Offset = StringSimVars->size() * MOBIFLIGHT_STRING_SIMVAR_VALUE_MAX_LEN; newStringSimVar.Value.empty(); + newStringSimVar.client = client; StringSimVars->push_back(newStringSimVar); + //duplicated SimVar + if (pdupRpn) { + pdupRpn->StringSimVars.push_back(newStringSimVar); + } else { + ReadRPNCode rpnCode; + rpnCode.Code = code; + rpnCode.RetType = 2;//hardcoded type id + rpnCode.StringSimVars.push_back(newStringSimVar); + RPNCodelist.push_back(rpnCode); + } + if (client->MaxClientDataDefinition < (SimVars->size() + StringSimVars->size())) { hr = SimConnect_AddToClientDataDefinition( g_hSimConnect, @@ -352,11 +400,11 @@ void RegisterStringSimVar(const std::string code, Client* client) { ); if (hr != S_OK) { - fprintf(stderr, "MobiFlight[%s]: Error on adding Client Data \"%s\" with String-ID: %u, String-Offset: %u and Size: %u\n", client->Name.c_str(), newStringSimVar.Name.c_str(), newStringSimVar.ID, newStringSimVar.Offset, MOBIFLIGHT_STRING_SIMVAR_VALUE_MAX_LEN); + fprintf(stderr, "MobiFlight[%s]: Error on adding Client Data \"%s\" with String-ID: %u, String-Offset: %u and Size: %u\n", client->Name.c_str(), code.c_str(), newStringSimVar.ID, newStringSimVar.Offset, MOBIFLIGHT_STRING_SIMVAR_VALUE_MAX_LEN); } #if _DEBUG else { - std::cout << "MobiFlight[" << client->Name.c_str() << "]: Added String-SimVar > " << newStringSimVar.Name.c_str(); + std::cout << "MobiFlight[" << client->Name.c_str() << "]: Added String-SimVar > " << code.c_str(); std::cout << " with String-ID: " << newStringSimVar.ID << ", String-Offset: " << newStringSimVar.Offset << " and Size: " << MOBIFLIGHT_STRING_SIMVAR_VALUE_MAX_LEN << std::endl; } std::cout << "MobiFlight[" << client->Name.c_str() << "]: RegisterStringSimVar StringSimVars Size: " << StringSimVars->size() << std::endl; @@ -369,7 +417,7 @@ void RegisterStringSimVar(const std::string code, Client* client) { newStringSimVar.Value = std::string(charVal, strnlen(charVal, MOBIFLIGHT_STRING_SIMVAR_VALUE_MAX_LEN)); WriteSimVar(newStringSimVar, client); #if _DEBUG - std::cout << "MobiFlight[" << client->Name.c_str() << "]: RegisterStringSimVar > " << newStringSimVar.Name.c_str(); + std::cout << "MobiFlight[" << client->Name.c_str() << "]: RegisterStringSimVar > " << code.c_str(); std::cout << " ID [" << newStringSimVar.ID << "] : Offset(" << newStringSimVar.Offset << ") : Value(" << newStringSimVar.Value << ")" << std::endl; #endif } @@ -391,66 +439,99 @@ void ClearSimVars(Client* client) { WriteSimVar(simVar, client); } client->StringSimVars.clear(); + // clear RNP code list + for (std::vector::iterator rit = RPNCodelist.begin(); rit != RPNCodelist.end();) { + for (std::vector::iterator it = (*rit).StringSimVars.begin(); it != (*rit).StringSimVars.end();) { + if ((*it).client == client) { + it = (*rit).StringSimVars.erase(it); + } else { + ++it; + } + } + for (std::vector::iterator sit = (*rit).SimVars.begin(); sit != (*rit).SimVars.end();) { + if ((*sit).client == client) { + sit = (*rit).SimVars.erase(sit); + } else { + ++sit; + } + } + //remove empty RNP code + if ((*rit).StringSimVars.empty() && (*rit).SimVars.empty()) { + rit = RPNCodelist.erase(rit); + } else { + ++rit; + } + } std::cout << "MobiFlight[" << client->Name.c_str() << "]: Cleared SimVar tracking." << std::endl; - //client->RollingClientDataReadIndex = client->SimVars.begin(); - client->RollingClientDataReadIndex = 0; + //client->RollingDataReadIndex = client->SimVars.begin(); + RollingDataReadIndex = 0; } + // Read a single SimVar and send the current value to SimConnect Clients (overloaded for float SimVars) -void ReadSimVar(SimVar &simVar, Client* client) { +void ReadSimVarFloat(ReadRPNCode &rpn) { FLOAT64 floatVal = 0; - execute_calculator_code(std::string(simVar.Name).c_str(), &floatVal, nullptr, nullptr); + execute_calculator_code(std::string(rpn.Code).c_str(), &floatVal, nullptr, nullptr); - if (simVar.Value == floatVal) return; - simVar.Value = floatVal; + for (auto& simVar : rpn.SimVars) { + if ((simVar.Value > floatVal) && (simVar.Value - floatVal < 0.00001F)) { + continue; + } else if ((simVar.Value < floatVal) && (floatVal - simVar.Value < 0.00001F)) { + continue; + } + simVar.Value = floatVal; - WriteSimVar(simVar, client); + WriteSimVar(simVar, simVar.client); #if _DEBUG - std::cout << "MobiFlight[" << client->Name.c_str() << "]: SimVar " << simVar.Name.c_str(); - std::cout << " with ID " << simVar.ID << " has value " << simVar.Value << std::endl; + std::cout << "MobiFlight[" << simVar.client->Name.c_str() << "]: SimVar " << rpn.Code.c_str(); + std::cout << " with ID " << simVar.ID << " has value " << simVar.Value << std::endl; #endif + } } // Read a single SimVar and send the current value to SimConnect Clients (overloaded for string SimVars) -void ReadSimVar(StringSimVar &simVar, Client* client) { +void ReadSimVarString(ReadRPNCode &rpn) { PCSTRINGZ charVal = nullptr; - execute_calculator_code(std::string(simVar.Name).c_str(), nullptr, nullptr, &charVal); + execute_calculator_code(std::string(rpn.Code).c_str(), nullptr, nullptr, &charVal); std::string stringVal = std::string(charVal, strnlen(charVal, MOBIFLIGHT_STRING_SIMVAR_VALUE_MAX_LEN)); - if (simVar.Value == stringVal) return; - simVar.Value = stringVal; - WriteSimVar(simVar, client); + for (auto& simVar : rpn.StringSimVars) { + if (simVar.Value == stringVal) continue; + simVar.Value = stringVal; + + WriteSimVar(simVar, simVar.client); #if _DEBUG - std::cout << "MobiFlight[" << client->Name.c_str() << "]: StringSimVar " << simVar.Name.c_str(); - std::cout << " with ID " << simVar.ID << " has value " << simVar.Value << std::endl; + std::cout << "MobiFlight[" << simVar.client->Name.c_str() << "]: StringSimVar " << rpn.Code.c_str(); + std::cout << " with ID " << simVar.ID << " has value " << simVar.Value << std::endl; #endif + } +} + +// Read a single SimVar and send the current value to SimConnect Clients (overloaded for float SimVars) +void ReadSimVar(ReadRPNCode &rpn) { + if (rpn.RetType == 0) { + ReadSimVarFloat(rpn); + } else if (rpn.RetType == 2) { + ReadSimVarString(rpn); + } } // Read all dynamically registered SimVars void ReadSimVars() { - for (auto& client : RegisteredClients) { - std::vector* SimVars = &(client->SimVars); - std::vector* StringSimVars = &(client->StringSimVars); + int totalSimVars = RPNCodelist.size(); + int maxVarsPerFrame = (totalSimVars < MOBIFLIGHT_MAX_VARS_PER_FRAME) ? totalSimVars : MOBIFLIGHT_MAX_VARS_PER_FRAME; - int totalSimVars = SimVars->size() + StringSimVars->size(); - int maxVarsPerFrame = (totalSimVars < MOBIFLIGHT_MAX_VARS_PER_FRAME) ? totalSimVars : MOBIFLIGHT_MAX_VARS_PER_FRAME; + for (int i=0; i < maxVarsPerFrame; ++i) { + ReadSimVar(RPNCodelist.at(RollingDataReadIndex)); - for (int i=0; i < maxVarsPerFrame; ++i) { - if(client->RollingClientDataReadIndex < SimVars->size() ) { - ReadSimVar(SimVars->at(client->RollingClientDataReadIndex), client); - } - else { - ReadSimVar(StringSimVars->at(client->RollingClientDataReadIndex - SimVars->size()), client); - } - client->RollingClientDataReadIndex++; - if (client->RollingClientDataReadIndex >= totalSimVars) - client->RollingClientDataReadIndex = 0; - } + RollingDataReadIndex++; + if (RollingDataReadIndex >= totalSimVars) + RollingDataReadIndex = 0; } } @@ -544,8 +625,7 @@ Client* RegisterNewClient(const std::string clientName) { newClient->DataDefinitionIDStringCommand = newClient->DataDefinitionIDStringResponse + 1; newClient->SimVars = std::vector(); newClient->StringSimVars = std::vector(); - //newClient->RollingClientDataReadIndex = newClient->SimVars.begin(); - newClient->RollingClientDataReadIndex = 0; + RollingDataReadIndex = 0; newClient->DataDefinitionIdSimVarsStart = SIMVAR_OFFSET + (newClient->ID * (CLIENT_DATA_DEF_ID_SIMVAR_RANGE + CLIENT_DATA_DEF_ID_STRINGVAR_RANGE)); newClient->DataDefinitionIdStringVarsStart = newClient->DataDefinitionIdSimVarsStart + CLIENT_DATA_DEF_ID_SIMVAR_RANGE; From 0bf059493bdb24151847938cefa0b94758f502c5 Mon Sep 17 00:00:00 2001 From: Wei Shuai Date: Tue, 25 Nov 2025 07:49:29 +0800 Subject: [PATCH 2/2] extend command MF.LVars.List MF.LVars.List.Start I_FCU_EFIS1_ARPT;I_ECAM_COND;... MF.LVars.List.Cont I_ASP2_INT_REC;I_ASP4_VOICE;... MF.LVars.List.Cont I_ASP2_PA_REC;I_ASP4_CAB_SEND;... MF.LVars.List.End --- README.md | 2 +- src/Sources/Code/Module.cpp | 55 +++++++++++++++++++++++++++---------- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index a9f2995..6af1fc4 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ The default channels for the MobiFlight client are auto created on startup. Each | Command (string)| Responses (string) | LVars value (float/string) | | ----------- | ----------- | ---------| | ```MF.Ping```| ```MF.Pong```| -| ```MF.LVars.List``` | ```MF.LVars.List.Start```
```A32NX_AUTOPILOT_1_ACTIVE```
```A32NX_AUTOPILOT_HEADING_SELECTED```
```...```
```MF.LVars.List.End```| +| ```MF.LVars.List``` | ```MF.LVars.List.Start```
```I_FCU_EFIS1_ARPT;I_ECAM_COND;...```
```MF.LVars.List.Cont```

```I_ASP2_INT_REC;I_ASP4_VOICE;...```
```MF.LVars.List.Cont```

```I_ASP2_PA_REC;I_ASP4_CAB_SEND;...```
```MF.LVars.List.End```| | ```MF.SimVars.Add.(A:GROUND ALTITUDE,Meters)``` || ```e.g. 1455.23 (float)``` | | ```MF.SimVars.AddString.(A:GPS WP NEXT ID,String)``` || ```e.g. EDDS (string)``` | | ```MF.SimVars.Clear``` ||| diff --git a/src/Sources/Code/Module.cpp b/src/Sources/Code/Module.cpp index 16d6281..84c87b5 100644 --- a/src/Sources/Code/Module.cpp +++ b/src/Sources/Code/Module.cpp @@ -84,6 +84,9 @@ struct Client { // This is an optimization to be able to re-use already defined data definition IDs & request IDs // after resetting registered SimVars uint16_t MaxClientDataDefinition = 0; + + //Runtime Rolling Client LVARS List Reading Index + uint16_t RollingLvarsListReadIndex = 0; }; // Runtime Rolling CLient Data reading Index @@ -236,22 +239,38 @@ void SendNewClientResponse(Client* client, Client* nc) { // List all available LVars for the currently loaded flight // and send them to the SimConnect client -void ListLVars(Client* client) { - lVarList.clear(); +int ListLVars(Client* client) { + int i; + std::string buffer; + buffer.reserve(MOBIFLIGHT_MESSAGE_SIZE); - for (int i = 0; i != 1000; i++) { + for (i = client->RollingLvarsListReadIndex; i < 10000; i++) { const char * lVarName = get_name_of_named_variable(i); - if (lVarName == NULLPTR) break; - lVarList.push_back(std::string(lVarName)); + if (lVarName == NULLPTR) { + break; + } + std::string str(lVarName); + // +1 means plus string seperator ';' + int buffer_size_next = buffer.size() + str.size() + 1; + if (buffer_size_next < MOBIFLIGHT_MESSAGE_SIZE - 1) { + lVarList.push_back(str); + buffer += str; + buffer += ";"; + } else { + break; + } } + client->RollingLvarsListReadIndex = i; - std::sort(lVarList.begin(), lVarList.end()); - - for (const auto& lVar : lVarList) { - SendResponse(lVar.c_str(), client); + if (buffer.size() > 0) { + SendResponse(buffer.c_str(), client); #if _DEBUG - std::cout << "MobiFlight[" << client->Name.c_str() << "]: Available LVar > " << lVar.c_str() << std::endl; + std::cout << "MobiFlight[" << client->Name.c_str() << "]: Available LVar > " << buffer.c_str() << std::endl; #endif + return buffer.size(); + } else { + client->RollingLvarsListReadIndex = 0; + return 0; } } @@ -473,7 +492,7 @@ void ClearSimVars(Client* client) { void ReadSimVarFloat(ReadRPNCode &rpn) { FLOAT64 floatVal = 0; - execute_calculator_code(std::string(rpn.Code).c_str(), &floatVal, nullptr, nullptr); + execute_calculator_code(rpn.Code.c_str(), &floatVal, nullptr, nullptr); for (auto& simVar : rpn.SimVars) { if ((simVar.Value > floatVal) && (simVar.Value - floatVal < 0.00001F)) { @@ -497,6 +516,7 @@ void ReadSimVarString(ReadRPNCode &rpn) { PCSTRINGZ charVal = nullptr; execute_calculator_code(std::string(rpn.Code).c_str(), nullptr, nullptr, &charVal); + std::string stringVal = std::string(charVal, strnlen(charVal, MOBIFLIGHT_STRING_SIMVAR_VALUE_MAX_LEN)); for (auto& simVar : rpn.StringSimVars) { @@ -692,6 +712,7 @@ extern "C" MSFS_CALLBACK void module_init(void) Client* client = RegisterNewClient(std::string(MOBIFLIGHT_CLIENT_DATA_NAME)); RegisterEvents(); ListLVars(client); + client->RollingLvarsListReadIndex = 0; std::cout << "MobiFlight: Max Message size is " << MOBIFLIGHT_MESSAGE_SIZE << std::endl; std::cout << "MobiFlight: Module Init Complete.Version: " << version << std::endl; @@ -739,11 +760,15 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV* pData, DWORD cbData, void* pContex } else if (str == "MF.LVars.List") { - SendResponse("MF.LVars.List.Start", client); - ListLVars(client); - SendResponse("MF.LVars.List.End", client); + if(client->RollingLvarsListReadIndex == 0) { + SendResponse("MF.LVars.List.Start", client); + } + if (ListLVars(client) > 0) { + SendResponse("MF.LVars.List.Cont", client); + } else { + SendResponse("MF.LVars.List.End", client); + } break; - } else if (str == "MF.Version.Get") {