diff --git a/Include/Constants.h b/Include/Constants.h index b146557..a4243de 100644 --- a/Include/Constants.h +++ b/Include/Constants.h @@ -256,6 +256,7 @@ constexpr const char* REMOTE_SIGNATURE = "TwinFan.plugin.XPMP2.Remote"; #define HELP_SET_BASICS "setup/configuration/settings-basics" #define HELP_SET_INPUT_CH "introduction/features/channels" #define HELP_SET_CH_OPENSKY "setup/installation/opensky" +#define HELP_SET_CH_ADSBHUB "setup/installation/adsbhub" #define HELP_SET_CH_ADSBEX "setup/installation/ads-b-exchange" #define HELP_SET_CH_OPENGLIDER "setup/installation/ogn" #define HELP_SET_CH_REALTRAFFIC "setup/installation/realtraffic-connectivity" diff --git a/Include/LTADSBEx.h b/Include/LTADSBEx.h index a285d8b..3b82e72 100644 --- a/Include/LTADSBEx.h +++ b/Include/LTADSBEx.h @@ -46,8 +46,8 @@ #define ADSBEX_RAPIDAPI_25_URL "https://adsbx-flight-sim-traffic.p.rapidapi.com/api/aircraft/json/lat/%f/lon/%f/dist/25/" #define ADSBEX_RAPIDAPI_HOST "X-RapidAPI-Host:adsbx-flight-sim-traffic.p.rapidapi.com" #define ADSBEX_RAPIDAPI_KEY "X-RapidAPI-Key:" -#define ADSBEX_RAPIDAPI_RLIMIT "X-RateLimit-Requests-Limit:" -#define ADSBEX_RAPIDAPI_RREMAIN "X-RateLimit-Requests-Remaining:" +#define ADSBEX_RAPIDAPI_RLIMIT "x-ratelimit-requests-limit:" +#define ADSBEX_RAPIDAPI_RREMAIN "x-ratelimit-requests-remaining:" #define ADSBEX_TOTAL "total" #define ADSBEX_NOW "now" diff --git a/Include/LTChannel.h b/Include/LTChannel.h index 263221c..e6779bf 100644 --- a/Include/LTChannel.h +++ b/Include/LTChannel.h @@ -107,8 +107,6 @@ class LTChannel virtual bool IsEnabled () const; virtual void SetEnable (bool bEnable); virtual std::string GetStatusText () const; ///< return a human-readable staus - virtual std::string GetStatusTextExt () const///< optionally return an extended status - { return std::string(); } virtual int GetNumAcServed () const = 0; ///< how many a/c do we feed? // shall data of this channel be subject to LTFlightData::DataSmoothing? diff --git a/Include/LTForeFlight.h b/Include/LTForeFlight.h index 8ea1526..08da28f 100644 --- a/Include/LTForeFlight.h +++ b/Include/LTForeFlight.h @@ -53,15 +53,8 @@ constexpr std::chrono::milliseconds FF_INTVL = std::chrono::milliseconds( class ForeFlightSender : public LTOutputChannel { protected: - // thread - std::thread thrUdpSender; - volatile bool bStopUdpSender = true; // tells thread to stop - std::mutex ffStopMutex; // supports wake-up and stop synchronization - std::condition_variable ffStopCV; // UDP sender UDPReceiver udpSender; - bool bSendUsersPlane = true; - bool bSendAITraffic = true; // time points last sent something std::chrono::steady_clock::time_point nextGPS; std::chrono::steady_clock::time_point nextAtt; @@ -70,26 +63,16 @@ class ForeFlightSender : public LTOutputChannel public: ForeFlightSender (); - ~ForeFlightSender () override; std::string GetURL (const positionTy&) override { return ""; } // don't need URL, no request/reply // interface called from LTChannel - bool FetchAllData(const positionTy& pos) override; + bool FetchAllData(const positionTy&) override { return false; } bool ProcessFetchedData () override { return true; } - void DoDisabledProcessing() override; - void Close () override; protected: - void Main () override; ///< virtual thread main function - - // Start/Stop - bool StartConnection (); - bool StopConnection (); - // send positions - void udpSend(); // thread's main function - static void udpSendS (ForeFlightSender* me) { me->udpSend(); } + void Main () override; ///< virtual thread main function void SendGPS (const positionTy& pos, double speed_m, double track); // position of user's aircraft void SendAtt (const positionTy& pos, double speed_m, double track); // attitude of user's aircraft void SendAllTraffic (); // other traffic diff --git a/Include/LTRealTraffic.h b/Include/LTRealTraffic.h index 682d3d9..2ed66ec 100644 --- a/Include/LTRealTraffic.h +++ b/Include/LTRealTraffic.h @@ -46,7 +46,7 @@ constexpr size_t RT_NET_BUF_SIZE = 8192; constexpr double RT_VSI_AIRBORNE = 80.0; ///< if VSI is more than this then we assume "airborne" #define MSG_RT_STATUS "RealTraffic network status changed to: %s" -#define MSG_RT_LAST_RCVD " | last: %lds ago" +#define MSG_RT_LAST_RCVD " | last msg %.0fs ago" #define MSG_RT_ADJUST " | historic traffic from %s" #define INFO_RT_REAL_TIME "RealTraffic: Tracking data is real-time again." @@ -211,8 +211,7 @@ class RealTrafficConnection : public LTFlightDataChannel inline rtStatusTy GetStatus () const { return status; } double GetLastRcvdTime () const { return lastReceivedTime; } std::string GetStatusStr () const; - std::string GetStatusText () const override; ///< return a human-readable staus - std::string GetStatusTextExt () const override; + std::string GetStatusText () const override; ///< return a human-readable status inline bool IsConnected () const { return RT_STATUS_CONNECTED_PASSIVELY <= status && status <= RT_STATUS_CONNECTED_FULL; } inline bool IsConnecting () const { return RT_STATUS_STARTING <= status && status <= RT_STATUS_CONNECTED_FULL; } diff --git a/Lib/XPMP2 b/Lib/XPMP2 index d807bf0..14195dc 160000 --- a/Lib/XPMP2 +++ b/Lib/XPMP2 @@ -1 +1 @@ -Subproject commit d807bf01707f4bfd651eb6c3174386cbb376a52e +Subproject commit 14195dc6b0c94f49d9c4b85d6cf52bb17b1ddfb3 diff --git a/Src/InfoListWnd.cpp b/Src/InfoListWnd.cpp index 4b9cf30..ab5f3e0 100644 --- a/Src/InfoListWnd.cpp +++ b/Src/InfoListWnd.cpp @@ -423,12 +423,8 @@ void InfoListWnd::buildInterface() ImGui::TextUnformatted(pCh->ChName()); } // Channel's status - if (ImGui::TableSetColumnIndex(1)) { + if (ImGui::TableSetColumnIndex(1)) ImGui::TextUnformatted(pCh->GetStatusText().c_str()); - const std::string extStatus = pCh->GetStatusTextExt(); - if (!extStatus.empty()) - ImGui::TextUnformatted(extStatus.c_str()); - } ImGui::PopID(); } } diff --git a/Src/LTADSBEx.cpp b/Src/LTADSBEx.cpp index ceb0b95..0909bc9 100644 --- a/Src/LTADSBEx.cpp +++ b/Src/LTADSBEx.cpp @@ -461,9 +461,9 @@ std::string ADSBExchangeConnection::GetStatusText () const std::string s = LTChannel::GetStatusText(); if (IsValid() && IsEnabled() && dataRefs.ADSBExRLimit > 0) { - s += ", "; + s += " | "; s += std::to_string(dataRefs.ADSBExRRemain); - s += " / "; + s += " of "; s += std::to_string(dataRefs.ADSBExRLimit); s += " RAPID API requests left"; } @@ -596,6 +596,12 @@ size_t ADSBExchangeConnection::ReceiveHeader(char *buffer, size_t size, size_t n static size_t lenRLimit = strlen(ADSBEX_RAPIDAPI_RLIMIT); static size_t lenRRemain = strlen(ADSBEX_RAPIDAPI_RREMAIN); char num[50]; + + // Turn buffer content lower case for the first few chars we are to compare + const size_t lenMax = std::min(std::max(lenRLimit,lenRRemain),len); + size_t i = 0; + for (char* p = buffer; i < lenMax; ++i, ++p) + *p = char(std::tolower(*p)); // Limit? if (len > lenRLimit && @@ -606,7 +612,7 @@ size_t ADSBExchangeConnection::ReceiveHeader(char *buffer, size_t size, size_t n num[copyCnt]=0; // zero termination dataRefs.ADSBExRLimit = std::atol(num); } - // Remining? + // Remaining? else if (len > lenRRemain && memcmp(buffer, ADSBEX_RAPIDAPI_RREMAIN, lenRRemain) == 0) { diff --git a/Src/LTADSBHub.cpp b/Src/LTADSBHub.cpp index 504b952..760ade4 100644 --- a/Src/LTADSBHub.cpp +++ b/Src/LTADSBHub.cpp @@ -76,15 +76,15 @@ std::string ADSBHubConnection::GetStatusText () const } else if (isRunning()) { switch (eFormat) { - case FMT_SBS: s += ", receiving SBS format"; break; - case FMT_ComprVRS: s += ", receiving Compressed VRS format"; break; - default: s += ", connecting..."; + case FMT_SBS: s += " | SBS format"; break; + case FMT_ComprVRS: s += " | Compressed VRS format"; break; + default: s += " | connecting..."; } // add time since last data if (lastData != std::chrono::time_point()) { char t[100]; std::chrono::duration diff = std::chrono::steady_clock::now() - lastData; - snprintf(t, sizeof(t), ", last data %.1fs ago", diff.count()); + snprintf(t, sizeof(t), " | last msg %.0fs ago", diff.count()); s += t; } } diff --git a/Src/LTForeFlight.cpp b/Src/LTForeFlight.cpp index e5d65e7..746fb80 100644 --- a/Src/LTForeFlight.cpp +++ b/Src/LTForeFlight.cpp @@ -37,220 +37,139 @@ LTOutputChannel(DR_CHANNEL_FORE_FLIGHT_SENDER, FOREFLIGHT_NAME) urlPopup = FF_CHECK_POPUP; } -ForeFlightSender::~ForeFlightSender() -{ - // make sure everything's cleaned up - StopConnection(); -} - -// if called makes sure the UDP client is up and running -bool ForeFlightSender::FetchAllData(const positionTy&) -{ - // if we are invalid or disabled we should shut down - if (!IsValid() || !IsEnabled()) { - return StopConnection(); - } - - // Update bSendUserPlane/AITraffic - bSendUsersPlane = DataRefs::GetCfgBool(DR_CFG_FF_SEND_USER_PLANE); - bSendAITraffic = DataRefs::GetCfgBool(DR_CFG_FF_SEND_TRAFFIC); - - // make sure we have a socket and a thread - if (!StartConnection()) - return false; - - return true; -} - - -void ForeFlightSender::DoDisabledProcessing() -{ - // make sure everything's cleaned up - StopConnection(); -} - -void ForeFlightSender::Close () -{ - // make sure everything's cleaned up - StopConnection(); -} - - -// virtual thread main function -void ForeFlightSender::Main () -{ - // This is a communication thread's main function, set thread's name and C locale - ThreadSettings TS ("LT_ForeFlight", LC_ALL_MASK); -} - - -// Start/Stop the thread, which sends the data -bool ForeFlightSender::StartConnection () -{ - bStopUdpSender = false; - if (!thrUdpSender.joinable()) - thrUdpSender = std::thread (udpSendS, this); - return true; -} - -bool ForeFlightSender::StopConnection () -{ - // is there a main thread running? -> stop it and wait for it to return - if ( thrUdpSender.joinable() ) - { - // stop the main thread - bStopUdpSender = true; // the message is: Stop! - ffStopCV.notify_all(); // wake up the thread for stop - thrUdpSender.join(); // wait for thread to finish - - thrUdpSender = std::thread(); - } - return true; -} - // // MARK: Send information // // thread main function -void ForeFlightSender::udpSend() +void ForeFlightSender::Main () { // This is a communication thread's main function, set thread's name and C locale ThreadSettings TS ("LT_ForeFlight", LC_ALL_MASK); - - // - // *** open the UDP socket *** - // - if (!udpSender.isOpen()) - { - // open the UDP socket with broadcast permission - int port = DataRefs::GetCfgInt(DR_CFG_FF_SEND_PORT); - try { - udpSender.Open (FF_LOCALHOST, port, FF_NET_BUF_SIZE, 0, true); - LOG_MSG(logINFO, MSG_FF_OPENED); - } - catch (std::runtime_error& e) { - // exception...can only really happen in UDPReceiver::Open - LOG_MSG(logERR, ERR_UDP_SOCKET_CREAT, ChName(), - FF_LOCALHOST, port, - e.what()); - } - } - // now - do we have a socket now? - if (!udpSender.isOpen()) { - // no: invalidate the channel - SetValid(false, true); - return; - } - - // - // *** Loop for sending data *** - // - // We place 20ms pause between any two broadcasts in order not to overtax - // the network. Increases reliability. The logic is then as follows: - // The loop keeps running till stop. - // We check if it is time for ATT or GPS data and send that if so. - // If not we continue with the traffic planes. - // Once we reach the end of all traffic we won't start again - // before reaching the proper time. - // Between any two broadcasts there is 20ms break. - // - LTFlightData::FDKeyTy lastKey; // last traffic sent out - while ( !bStopUdpSender ) - { - bool bDidSendSomething = false; - - // now - std::chrono::time_point now = - std::chrono::steady_clock::now(); + // Top-Level Exception Handling + const int port = DataRefs::GetCfgInt(DR_CFG_FF_SEND_PORT); + try { + // open the UDP socket with broadcast permission + udpSender.Open (FF_LOCALHOST, port, FF_NET_BUF_SIZE, 0, true); + LOG_MSG(logINFO, MSG_FF_OPENED); - // send user's plane at all? - if (bSendUsersPlane) { - double airSpeed_m = 0.0; - double track = 0.0; - positionTy pos; - - // time for GPS? - if (now >= nextGPS) - { - pos = dataRefs.GetUsersPlanePos(airSpeed_m, track); - SendGPS(pos, airSpeed_m, track); - nextGPS = now + FF_INTVL_GPS; - bDidSendSomething = true; - } + // + // *** Loop for sending data *** + // + // We place 20ms pause between any two broadcasts in order not to overtax + // the network. Increases reliability. The logic is then as follows: + // The loop keeps running till stop. + // We check if it is time for ATT or GPS data and send that if so. + // If not we continue with the traffic planes. + // Once we reach the end of all traffic we won't start again + // before reaching the proper time. + // Between any two broadcasts there is 20ms break. + // + LTFlightData::FDKeyTy lastKey; // last traffic sent out + while ( shallRun() && udpSender.isOpen() ) + { + const bool bSendUsersPlane = DataRefs::GetCfgBool(DR_CFG_FF_SEND_USER_PLANE); + const bool bSendAITraffic = DataRefs::GetCfgBool(DR_CFG_FF_SEND_TRAFFIC); + bool bDidSendSomething = false; - // time for ATT? - if (now >= nextAtt) - { - if (!pos.isNormal()) + // now + std::chrono::time_point now = + std::chrono::steady_clock::now(); + + // send user's plane at all? + if (bSendUsersPlane) { + double airSpeed_m = 0.0; + double track = 0.0; + positionTy pos; + + // time for GPS? + if (now >= nextGPS) + { pos = dataRefs.GetUsersPlanePos(airSpeed_m, track); - SendAtt(pos, airSpeed_m, track); - nextAtt = now + FF_INTVL_ATT; - bDidSendSomething = true; + SendGPS(pos, airSpeed_m, track); + nextGPS = now + FF_INTVL_GPS; + bDidSendSomething = true; + } + + // time for ATT? + if (now >= nextAtt) + { + if (!pos.isNormal()) + pos = dataRefs.GetUsersPlanePos(airSpeed_m, track); + SendAtt(pos, airSpeed_m, track); + nextAtt = now + FF_INTVL_ATT; + bDidSendSomething = true; + } } - } - - // send traffic at all? - // not yet send GPS/ATT? - // time to send some traffic? - if (bSendAITraffic && !bDidSendSomething && - now >= nextTraffic) - { - // from here on access to fdMap guarded by a mutex - std::unique_lock lock (mapFdMutex, std::try_to_lock); - if (lock) { - if (!mapFd.empty()) { - // just starting with a new round? - if (lastKey == LTFlightData::FDKeyTy()) - lastStartOfTraffic = now; - - // next key to send? (shall have an actual a/c) - mapLTFlightDataTy::const_iterator mapIter; - for (mapIter = mapFd.upper_bound(lastKey); - mapIter != mapFd.cend() && !mapIter->second.hasAc(); - mapIter++); - - // something left? - if (mapIter != mapFd.cend()) { - // send that plane's info - SendTraffic(mapIter->second); - // wake up soon again for the rest - lastKey = mapIter->first; - nextTraffic = now + FF_INTVL; + + // send traffic at all? + // not yet send GPS/ATT? + // time to send some traffic? + if (bSendAITraffic && !bDidSendSomething && + now >= nextTraffic) + { + // from here on access to fdMap guarded by a mutex + std::unique_lock lock (mapFdMutex, std::try_to_lock); + if (lock) { + if (!mapFd.empty()) { + // just starting with a new round? + if (lastKey == LTFlightData::FDKeyTy()) + lastStartOfTraffic = now; + + // next key to send? (shall have an actual a/c) + mapLTFlightDataTy::const_iterator mapIter; + for (mapIter = mapFd.upper_bound(lastKey); + mapIter != mapFd.cend() && !mapIter->second.hasAc(); + mapIter++); + + // something left? + if (mapIter != mapFd.cend()) { + // send that plane's info + SendTraffic(mapIter->second); + // wake up soon again for the rest + lastKey = mapIter->first; + nextTraffic = now + FF_INTVL; + } + else { + // we're done with one round, start over + lastKey = LTFlightData::FDKeyTy(); + nextTraffic = lastStartOfTraffic + std::chrono::seconds(DataRefs::GetCfgInt(DR_CFG_FF_SEND_TRAFFIC_INTVL)); + } } else { - // we're done with one round, start over + // map's empty, so we are done lastKey = LTFlightData::FDKeyTy(); nextTraffic = lastStartOfTraffic + std::chrono::seconds(DataRefs::GetCfgInt(DR_CFG_FF_SEND_TRAFFIC_INTVL)); } + } else { + // wake up soon again for the rest + nextTraffic = now + FF_INTVL; } - else { - // map's empty, so we are done - lastKey = LTFlightData::FDKeyTy(); - nextTraffic = lastStartOfTraffic + std::chrono::seconds(DataRefs::GetCfgInt(DR_CFG_FF_SEND_TRAFFIC_INTVL)); - } - } else { - // wake up soon again for the rest - nextTraffic = now + FF_INTVL; } - } - - // sleep until time or if woken up for termination - // by condition variable trigger - if (!bStopUdpSender) - { - std::chrono::time_point nextWakeup = - bSendUsersPlane && bSendAITraffic ? std::min({nextGPS, nextAtt, nextTraffic}) : - bSendUsersPlane && !bSendAITraffic ? std::min(nextGPS, nextAtt) : - nextTraffic; - std::unique_lock lk(ffStopMutex); - ffStopCV.wait_until(lk, nextWakeup, - [this]{return bStopUdpSender;}); - lk.unlock(); + // sleep until time or if woken up for termination + // by condition variable trigger + if (shallRun()) + { + std::chrono::time_point nextWakeup = + bSendUsersPlane && bSendAITraffic ? std::min({nextGPS, nextAtt, nextTraffic}) : + bSendUsersPlane && !bSendAITraffic ? std::min(nextGPS, nextAtt) : + nextTraffic; + + std::unique_lock lk(FDThreadSynchMutex); + FDThreadSynchCV.wait_until(lk, nextWakeup, + [this]{return !shallRun();}); + } } } + catch (std::runtime_error& e) { + // exception...can only really happen in UDPReceiver::Open + LOG_MSG(logERR, ERR_UDP_SOCKET_CREAT, ChName(), + FF_LOCALHOST, port, + e.what()); + IncErrCnt(); + } // // *** close the UDP socket *** diff --git a/Src/LTOpenGlider.cpp b/Src/LTOpenGlider.cpp index 281453d..c60ce68 100644 --- a/Src/LTOpenGlider.cpp +++ b/Src/LTOpenGlider.cpp @@ -319,7 +319,7 @@ std::string OpenGliderConnection::GetStatusText () const // if we have a latest APRS timestamp then we add age of last APRS message if (!std::isnan(aprsLastData)) { char buf[50]; - snprintf(buf, sizeof(buf), ", last message %.1fs ago", + snprintf(buf, sizeof(buf), " | last msg %.0fs ago", dataRefs.GetMiscNetwTime() - aprsLastData); s += buf; } diff --git a/Src/LTOpenSky.cpp b/Src/LTOpenSky.cpp index 4244f78..24b5e61 100644 --- a/Src/LTOpenSky.cpp +++ b/Src/LTOpenSky.cpp @@ -324,7 +324,7 @@ std::string OpenSkyConnection::GetStatusText () const if (IsValid()) { if (dataRefs.OpenSkyRRemain < LONG_MAX) { - s += ", "; + s += " | "; s += std::to_string(dataRefs.OpenSkyRRemain); s += " requests left today"; } diff --git a/Src/LTRealTraffic.cpp b/Src/LTRealTraffic.cpp index d7afdc8..d43dfa4 100644 --- a/Src/LTRealTraffic.cpp +++ b/Src/LTRealTraffic.cpp @@ -213,25 +213,15 @@ std::string RealTrafficConnection::GetStatusText () const return GetStatusStr(); // An active source of tracking data...for how many aircraft? - return LTChannel::GetStatusText(); -} - -std::string RealTrafficConnection::GetStatusTextExt() const -{ - // Any extended status only if we are connected in any way - if (!IsEnabled() || - status < RT_STATUS_CONNECTED_PASSIVELY || - status > RT_STATUS_CONNECTED_FULL) - return std::string(); - - // Turn the RealTraffic status into text - std::string s (GetStatusStr()); - + std::string s = LTChannel::GetStatusText(); + // Add extended information specifically on RealTraffic connection status + s += " | "; + s += GetStatusStr(); if (IsConnected() && lastReceivedTime > 0.0) { char sIntvl[100]; // add when the last msg was received - long intvl = std::lround(dataRefs.GetSimTime() - lastReceivedTime); - snprintf(sIntvl,sizeof(sIntvl),MSG_RT_LAST_RCVD,intvl); + snprintf(sIntvl,sizeof(sIntvl),MSG_RT_LAST_RCVD, + dataRefs.GetSimTime() - lastReceivedTime); s += sIntvl; // if receiving historic traffic say so if (tsAdjust > 1.0) { diff --git a/Src/SettingsUI.cpp b/Src/SettingsUI.cpp index 0ca228a..f4ed477 100644 --- a/Src/SettingsUI.cpp +++ b/Src/SettingsUI.cpp @@ -306,7 +306,7 @@ void LTSettingsUI::buildInterface() // OpenSky's connection status details if (ImGui::FilteredLabel("Connection Status", sFilter)) { if (pOpenSkyCh) { - ImGui::TextUnformatted(pOpenSkyCh->GetStatusText().c_str()); + ImGui::TextWrapped("%s", pOpenSkyCh->GetStatusText().c_str()); } else { ImGui::TextUnformatted("Off"); } @@ -317,23 +317,34 @@ void LTSettingsUI::buildInterface() } // --- ADSBHub --- - const bool bWasADSBHubEnabled = dataRefs.IsChannelEnabled(DR_CHANNEL_ADSB_HUB); - ImGui::Indent(); - ImGui::FilteredCfgCheckbox("ADSBHub", sFilter, DR_CHANNEL_ADSB_HUB, - "Connect to ADSBHub for tracking data, requires feeder setup", - false); // don't move to next cell yet - if (ImGui::MatchesFilter("ADSBHub", sFilter)) { - ImGui::SameLine(); - ImGui::ButtonURL(ICON_FA_EXTERNAL_LINK_SQUARE_ALT " " ADSBHUB_CHECK_NAME, ADSBHUB_CHECK_URL, ADSBHUB_CHECK_POPUP); - ImGui::TableNextCell(); + if (ImGui::TreeNodeCbxLinkHelp("ADSBHub", nCol, + DR_CHANNEL_ADSB_HUB, "Connect to ADSBHub for tracking data, requires feeder setup", + ICON_FA_EXTERNAL_LINK_SQUARE_ALT " " ADSBHUB_CHECK_NAME, + ADSBHUB_CHECK_URL, + ADSBHUB_CHECK_POPUP, + HELP_SET_CH_ADSBHUB, "Open Help on ADSBHub in Browser", + sFilter, nOpCl)) + { + const bool bWasADSBHubEnabled = dataRefs.IsChannelEnabled(DR_CHANNEL_ADSB_HUB); + + // If ADSBHub has just been enabled then, as a courtesy, + // we also make sure that OpenSky Master data is enabled + if (!bWasADSBHubEnabled && dataRefs.IsChannelEnabled(DR_CHANNEL_ADSB_HUB)) + dataRefs.SetChannelEnabled(DR_CHANNEL_OPEN_SKY_AC_MASTERDATA, true); + + // ADSBHub's connection status details + if (ImGui::FilteredLabel("Connection Status", sFilter)) { + if (const LTChannel* pADSBHubCh = LTFlightDataGetCh(DR_CHANNEL_ADSB_HUB)) { + ImGui::TextWrapped("%s", pADSBHubCh->GetStatusText().c_str()); + } else { + ImGui::TextUnformatted("Off"); + } + ImGui::TableNextCell(); + } + + if (!*sFilter) ImGui::TreePop(); } - ImGui::Unindent(); - // If ADSBHub has just been enabled then, as a courtesy, - // we also make sure that OpenSky Master data is enabled - if (!bWasADSBHubEnabled && dataRefs.IsChannelEnabled(DR_CHANNEL_ADSB_HUB)) - dataRefs.SetChannelEnabled(DR_CHANNEL_OPEN_SKY_AC_MASTERDATA, true); - // --- ADS-B Exchange --- if (ImGui::TreeNodeCbxLinkHelp("ADS-B Exchange", nCol, // we offer the enable checkbox only when an API key is defined @@ -406,16 +417,21 @@ void LTSettingsUI::buildInterface() else if (eADSBExKeyTest == ADSBX_KEY_FAILED) ImGui::TextUnformatted(ICON_FA_EXCLAMATION_TRIANGLE " Key test failed!"); - // If available, show information on remaining RAPID API data usage - if (dataRefs.ADSBExRLimit || dataRefs.ADSBExRRemain) - { - ImGui::Text("%ld / %ld RAPID API requests left", - dataRefs.ADSBExRRemain, dataRefs.ADSBExRLimit); - } - ImGui::TableNextCell(); } + // ADSBEx's connection status details (only if there is an API key defined) + if (!dataRefs.GetADSBExAPIKey().empty() && + ImGui::FilteredLabel("Connection Status", sFilter)) + { + if (const LTChannel* pADSBExbCh = LTFlightDataGetCh(DR_CHANNEL_ADSB_EXCHANGE_ONLINE)) { + ImGui::TextWrapped("%s", pADSBExbCh->GetStatusText().c_str()); + } else { + ImGui::TextUnformatted("Off"); + } + ImGui::TableNextCell(); + } + if (!*sFilter) ImGui::TreePop(); } @@ -428,6 +444,16 @@ void LTSettingsUI::buildInterface() HELP_SET_CH_OPENGLIDER, "Open Help on Open Glider Network in Browser", sFilter, nOpCl)) { + // OGN's connection status details + if (ImGui::FilteredLabel("Connection Status", sFilter)) { + if (const LTChannel* pOGNCh = LTFlightDataGetCh(DR_CHANNEL_OPEN_GLIDER_NET)) { + ImGui::TextWrapped("%s", pOGNCh->GetStatusText().c_str()); + } else { + ImGui::TextUnformatted("Off"); + } + ImGui::TableNextCell(); + } + if (!*sFilter) { ImGui::TextUnformatted("Flarm A/c Types"); ImGui::TableNextCell(); @@ -508,10 +534,7 @@ void LTSettingsUI::buildInterface() if (ImGui::FilteredLabel("Connection Status", sFilter)) { const LTChannel* pRTCh = LTFlightDataGetCh(DR_CHANNEL_REAL_TRAFFIC_ONLINE); if (pRTCh) { - ImGui::TextUnformatted(pRTCh->GetStatusText().c_str()); - const std::string extStatus = pRTCh->GetStatusTextExt(); - if (!extStatus.empty()) - ImGui::TextUnformatted(extStatus.c_str()); + ImGui::TextWrapped("%s", pRTCh->GetStatusText().c_str()); } else { ImGui::TextUnformatted("Off"); } @@ -530,11 +553,6 @@ void LTSettingsUI::buildInterface() if (!*sFilter) ImGui::TreePop(); } - // If RealTraffic has just been enabled then, as a courtesy, - // we also make sure that OpenSky Master data is enabled - if (!bWasRTEnabled && dataRefs.IsChannelEnabled(DR_CHANNEL_REAL_TRAFFIC_ONLINE)) - dataRefs.SetChannelEnabled(DR_CHANNEL_OPEN_SKY_AC_MASTERDATA, true); - // --- FSCharter --- if (ImGui::TreeNodeCbxLinkHelp(FSC_NAME, nCol, DR_CHANNEL_FSCHARTER, @@ -601,7 +619,7 @@ void LTSettingsUI::buildInterface() // FSCharter's connection status details if (ImGui::FilteredLabel("Connection Status", sFilter)) { if (pFSCCh) { - ImGui::TextUnformatted(pFSCCh->GetStatusText().c_str()); + ImGui::TextWrapped("%s", pFSCCh->GetStatusText().c_str()); } else { ImGui::TextUnformatted("Off"); } @@ -638,7 +656,7 @@ void LTSettingsUI::buildInterface() } if (!*sFilter) { ImGui::TreePop(); ImGui::Spacing(); } - } // --- Input Channels --- + } // --- Output Channels --- // MARK: --- Aircraft Labels --- if (ImGui::TreeNodeHelp("Aircraft Labels", nCol,