diff --git a/include/ConfigManager.h b/include/ConfigManager.h index 516ab20..ff7ed40 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -76,6 +76,10 @@ class ConfigManager { static bool hasPropBrake(int motorIndex); static const int MAX_MOTORS = 8; // Maximum possible motors in X-Plane static void configureMotorBrakes(const CSimpleIniA& ini); + static std::vector getAirframeLists(); + static std::string getActiveAirframeName(); + static void setActiveAirframeName(const std::string& airframeName); + static std::string getAirframeByIndex(int index); diff --git a/src/ConfigManager.cpp b/src/ConfigManager.cpp index af11986..916e3aa 100644 --- a/src/ConfigManager.cpp +++ b/src/ConfigManager.cpp @@ -460,4 +460,105 @@ std::vector ConfigManager::parseMotorIndices(const std::string& indicesStr) } } return indices; -} \ No newline at end of file +} + + +/** + * @brief Retrieves a list of airframe configuration names from the config.ini file. + * + * This function reads the config.ini file and extracts the names of all sections, + * which represent different airframe configurations. It uses the SimpleIni library + * to parse the INI file and collect the section names. Each section name is assumed + * to correspond to a unique airframe configuration. + * + * The function is static and can be called without an instance of ConfigManager. + * It utilizes the getConfigFilePath method to resolve the path to the config.ini file. + * + * Usage example: + * std::vector airframes = ConfigManager::getAirframeLists(); + * for (const auto& airframe : airframes) { + * // Process each airframe name + * } + * + * @return A vector of strings, each representing the name of an airframe configuration. + * Returns an empty vector if the config.ini file cannot be read or if there are no sections. + */ +std::vector ConfigManager::getAirframeLists() { + CSimpleIniA ini; + ini.SetUnicode(); + ini.LoadFile(getConfigFilePath().c_str()); + + CSimpleIniA::TNamesDepend sections; + ini.GetAllSections(sections); + + std::vector airframeNames; + for (const auto& section : sections) { + // Check and ignore the default section which does not represent an airframe. + if (std::string(section.pItem) != "") { + airframeNames.push_back(section.pItem); + } + } + + return airframeNames; +} + + + +/** + * @brief Retrieves the name of the currently active airframe configuration. + * + * This function reads the 'config.ini' file to obtain the name of the active airframe configuration. + * It returns the value of the 'config_name' parameter, which indicates the currently selected airframe. + * + * @return The name of the active airframe configuration. Returns an empty string if not specified. + */ +std::string ConfigManager::getActiveAirframeName() { + CSimpleIniA ini; + ini.SetUnicode(); + ini.LoadFile(getConfigFilePath().c_str()); + return ini.GetValue("", "config_name", ""); +} + +/** + * @brief Sets the active airframe configuration to the specified name. + * + * This function updates the 'config.ini' file to set the active airframe configuration. + * It first checks if the specified airframe name exists in the config file. If it does, + * the 'config_name' parameter is updated with the new airframe name. The updated configuration + * is then saved back to the 'config.ini' file. + * + * @param airframeName The name of the airframe configuration to be set as active. + */ +void ConfigManager::setActiveAirframeName(const std::string& airframeName) { + CSimpleIniA ini; + ini.SetUnicode(); + ini.LoadFile(getConfigFilePath().c_str()); + + // Check if the airframe name exists in the config + CSimpleIniA::TNamesDepend sections; + ini.GetAllSections(sections); + bool exists = std::any_of(sections.begin(), sections.end(), [&](const CSimpleIniA::Entry& entry) { + return entry.pItem == airframeName; + }); + + if (exists) { + ini.SetValue("", "config_name", airframeName.c_str()); + ini.SaveFile(getConfigFilePath().c_str()); + XPLMDebugString("px4xplane: Config File Updated."); + + } + else { + XPLMDebugString(("px4xplane: Airframe name not found in config: " + airframeName + "\n").c_str()); + } +} + + +std::string ConfigManager::getAirframeByIndex(int index) { + // Retrieve the list of airframes and return the one at the given index + std::vector airframes = getAirframeLists(); + if (index >= 0 && index < airframes.size()) { + return airframes[index]; + } + return ""; // Return an empty string if the index is out of range +} + diff --git a/src/px4xplane.cpp b/src/px4xplane.cpp index 64ee793..92194a9 100644 --- a/src/px4xplane.cpp +++ b/src/px4xplane.cpp @@ -35,6 +35,10 @@ static XPLMWindowID g_window; static XPLMMenuID g_menu_id; +static XPLMMenuID airframesMenu; // Global variable for the airframes submenu +static int g_airframesMenuItemIndex; // Global variable to store the index of the airframes submenu + + // Global variable to hold our command reference static XPLMCommandRef toggleEnableCmd; @@ -53,6 +57,14 @@ void updateMenuItems(); float MyFlightLoopCallback(float inElapsedSinceLastCall, float inElapsedTimeSinceLastFlightLoop, int inCounter, void* inRefcon); +// Debugging function - Logs a message to the X-Plane log +void debugLog(const char* message) { + XPLMDebugString("px4xplane: "); + XPLMDebugString(message); + XPLMDebugString("\n"); +} + + int drawHeader(int l, int t, float col_white[]) { char header[512]; @@ -164,6 +176,7 @@ PLUGIN_API int XPluginStart( XPLMRegisterFlightLoopCallback(MyFlightLoopCallback, -1.0f, NULL); + debugLog("Plugin started successfully"); return 1; @@ -206,18 +219,61 @@ void draw_px4xplane(XPLMWindowID in_window_id, void* in_refcon) { drawFooter(l, b, col_white); } +// Function to refresh the airframes submenu to indicate the active airframe. +void refreshAirframesMenu() { + // Clear the current submenu items. + XPLMClearAllMenuItems(airframesMenu); + + // Repopulate the submenu with updated airframe names and active status. + std::vector airframeNames = ConfigManager::getAirframeLists(); + for (const std::string& name : airframeNames) { + // Append each airframe to the submenu and mark the active one. + XPLMAppendMenuItem(airframesMenu, (name + (name == ConfigManager::getActiveAirframeName() ? " *" : "")).c_str(), (void*)new std::string(name), 1); + } +} + + +void menu_handler(void* in_menu_ref, void* in_item_ref) { + //TODO: Remember here there is a problem. handler cant find when we click on airframes. now I did a hack and put that in else when it is not part of main list menu. we should fix this later. + debugLog("Menu handler called"); + if (in_menu_ref == (void*)g_menu_id) { + if ((int)(intptr_t)in_item_ref == g_airframesMenuItemIndex) { + debugLog("Airframes submenu selected"); + // The submenu itself was clicked; specific handling if needed + } + else { + // Handling other main menu items + if (in_item_ref == (void*)0) { + XPLMSetWindowIsVisible(g_window, 1); + debugLog("Show Data menu item selected"); + } + else if (in_item_ref == (void*)1) { + toggleEnable(); + debugLog("Toggle enable menu item selected"); + } + } + } + else { + // Handling airframes submenu interactions + debugLog("Entered airframe submenu handler"); + + int index = (int)(intptr_t)in_item_ref; + std::vector airframeNames = ConfigManager::getAirframeLists(); + if (index >= 0 && index < airframeNames.size()) { + const std::string& selectedAirframe = airframeNames[index]; + debugLog(("Airframe selected: " + selectedAirframe).c_str()); -void menu_handler(XPLMMenuID in_menu, void* in_item_ref) { - if (in_item_ref == (void*)0) { // Show Window item - XPLMSetWindowIsVisible(g_window, 1); - } - else if (in_item_ref == (void*)1) { // Toggle enable - toggleEnable(); - } + ConfigManager::setActiveAirframeName(selectedAirframe); + refreshAirframesMenu(); + } + } + } + + void toggleEnable() { XPLMDebugString("px4xplane: toggleEnable() called.\n"); if (ConnectionManager::isConnected()) { @@ -267,26 +323,64 @@ std::vector getDataRefFloatArray(const char* dataRefName) { - void create_menu() { + debugLog("Creating plugin menu"); + int menu_container_idx = XPLMAppendMenuItem(XPLMFindPluginsMenu(), "PX4 X-Plane", NULL, 1); g_menu_id = XPLMCreateMenu("px4xplane", XPLMFindPluginsMenu(), menu_container_idx, menu_handler, NULL); - XPLMAppendMenuItem(g_menu_id, "Show Data", (void*)0, 1); + // Save the index of the airframes submenu in the main menu + g_airframesMenuItemIndex = XPLMAppendMenuItem(g_menu_id, "Airframes", NULL, 1); + airframesMenu = XPLMCreateMenu("Airframes", g_menu_id, g_airframesMenuItemIndex, menu_handler, NULL); + + + debugLog("Airframes submenu created"); + std::vector airframeNames = ConfigManager::getAirframeLists(); + std::string activeAirframe = ConfigManager::getActiveAirframeName(); + for (size_t i = 0; i < airframeNames.size(); ++i) { + const std::string& name = airframeNames[i]; + std::string menuItemName = name; + if (name == activeAirframe) { + menuItemName += " *"; + } + XPLMAppendMenuItem(airframesMenu, menuItemName.c_str(), (void*)(intptr_t)i, 1); + } + + XPLMAppendMenuItem(g_menu_id, "Show Data", (void*)0, 1); toggleEnableCmd = XPLMCreateCommand("px4xplane/toggleEnable", "Toggle enable/disable state"); XPLMRegisterCommandHandler(toggleEnableCmd, toggleEnableHandler, 1, (void*)0); XPLMAppendMenuSeparator(g_menu_id); + XPLMAppendMenuItemWithCommand(g_menu_id, "Connect to SITL", toggleEnableCmd); + XPLMAppendMenuItemWithCommand(g_menu_id, "Disconnect from SITL", toggleEnableCmd); - XPLMAppendMenuItemWithCommand(g_menu_id, "Connect to SITL", toggleEnableCmd); // Update this dynamically based on connection state - XPLMAppendMenuItemWithCommand(g_menu_id, "Disconnect from SITL", toggleEnableCmd); // Update this dynamically based on connection state - updateMenuItems(); // Update menu items after toggling connection + updateMenuItems(); + debugLog("Menu created successfully"); } + void updateMenuItems() { - XPLMClearAllMenuItems(g_menu_id); // Clear all existing items - XPLMAppendMenuItem(g_menu_id, "Show Settings", (void*)0, 1); + XPLMClearAllMenuItems(g_menu_id); + + // Recreate the main menu items + // Important: Update the airframesMenuItemIndex to reflect the new index after clearing + g_airframesMenuItemIndex = XPLMAppendMenuItem(g_menu_id, "Airframes", NULL, 1); + airframesMenu = XPLMCreateMenu("Airframes", g_menu_id, g_airframesMenuItemIndex, menu_handler, NULL); + + std::vector airframeNames = ConfigManager::getAirframeLists(); + std::string activeAirframe = ConfigManager::getActiveAirframeName(); + for (size_t i = 0; i < airframeNames.size(); ++i) { + const std::string& name = airframeNames[i]; + std::string menuItemName = name; + if (name == activeAirframe) { + menuItemName += " *"; + } + XPLMAppendMenuItem(airframesMenu, menuItemName.c_str(), (void*)(intptr_t)i, 1); + } + + // Recreate the remaining main menu items + XPLMAppendMenuItem(g_menu_id, "Show Data", (void*)0, 1); XPLMAppendMenuSeparator(g_menu_id); if (ConnectionManager::isConnected()) { XPLMAppendMenuItemWithCommand(g_menu_id, "Disconnect from SITL", toggleEnableCmd); @@ -297,6 +391,15 @@ void updateMenuItems() { } +// Placeholder function for handling airframe selection commands. +void handleAirframeSelection(const std::string& airframeName) { + // TODO: Implement the logic for when a user selects an airframe from the "Airframes" submenu. + debugLog(("Airframe selected: " + airframeName).c_str()); + // Perform necessary actions to activate the selected airframe. +} + + + // Constants for update frequencies (in seconds) constexpr float SENSOR_UPDATE_PERIOD = 1.0f / 100.0f; // 100 Hz for sensor data constexpr float GPS_UPDATE_PERIOD = 1.0f / 10.0f; // 10 Hz for GPS data @@ -393,6 +496,7 @@ PLUGIN_API void XPluginStop(void) { if (ConnectionManager::isConnected()) { toggleEnable(); } + XPLMUnregisterFlightLoopCallback(MyFlightLoopCallback, NULL); #if IBM ConnectionManager::cleanupWinSock();