Browse Source

add a menu to force set runways

Sven Czarnian 3 years ago
parent
commit
c3a75bb900
2 changed files with 123 additions and 24 deletions
  1. 99 12
      src/PlugIn.cpp
  2. 24 12
      src/PlugIn.h

+ 99 - 12
src/PlugIn.cpp

@@ -13,6 +13,7 @@
 
 #include <algorithm>
 #include <cctype>
+#include <regex>
 
 #include <gsl/gsl>
 #include <Shlwapi.h>
@@ -71,6 +72,7 @@ PlugIn::PlugIn() :
     this->RegisterTagItemType("Delta time", static_cast<int>(PlugIn::TagItemElement::DeltaTime));
     this->RegisterTagItemType("Fixed plan indicator", static_cast<int>(PlugIn::TagItemElement::FixedPlanIndicator));
     this->RegisterTagItemFunction("Force planning", static_cast<int>(PlugIn::TagItemFunction::ForceToBackendMenu));
+    this->RegisterTagItemFunction("Runway selection", static_cast<int>(PlugIn::TagItemFunction::RunwaySelectMenu));
 
     this->DisplayUserMessage(PLUGIN_NAME, "INFO", (std::string("Loaded ") + PLUGIN_NAME + " " + PLUGIN_VERSION).c_str(), true, true, false, false, false);
 
@@ -207,6 +209,8 @@ EuroScopePlugIn::CRadarScreen* PlugIn::OnRadarScreenCreated(const char* displayN
     if (nullptr == this->m_screen)
         this->m_screen = std::make_shared<RadarScreen>();
 
+    this->OnAirportRunwayActivityChanged();
+
     return this->m_screen.get();
 }
 
@@ -454,6 +458,10 @@ void PlugIn::generateAircraftReportMessage(EuroScopePlugIn::CRadarTarget& radarT
     report->set_allocated_dynamics(dynamics);
     report->set_allocated_position(coordinate);
 
+    auto selectedRwyIt = this->m_selectedRunway.find(radarTarget.GetCallsign());
+    if (this->m_selectedRunway.cend() != selectedRwyIt)
+        report->set_requestedrunway(selectedRwyIt->second);
+
     /* set the report time */
     std::stringstream stream;
     const auto reportTime = std::chrono::utc_clock::now();
@@ -476,8 +484,6 @@ bool PlugIn::OnCompileCommand(const char* cmdline) {
 
     if (std::string::npos != message.find("RELOAD")) {
         this->validateBackendData();
-        this->m_sweatboxValid = false;
-        this->m_playbackValid = false;
         retval = true;
     }
     else if (std::string::npos != message.find("SWEATBOX")) {
@@ -603,9 +609,9 @@ void PlugIn::OnFunctionCall(int functionId, const char* itemString, POINT pt, RE
         this->OpenPopupList(area, "AMAN", 1);
         bool inList = this->m_forcedToBackendCallsigns.cend() != std::find(this->m_forcedToBackendCallsigns.cbegin(), this->m_forcedToBackendCallsigns.cend(), callsign);
         this->AddPopupListElement("Add to AMAN", "", static_cast<int>(PlugIn::TagItemFunction::ForceToBackendSelect),
-                                  false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, true == inList);
+            false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, true == inList);
         this->AddPopupListElement("Remove from AMAN", "", static_cast<int>(PlugIn::TagItemFunction::ForceToBackendSelect),
-                                  false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false == inList);
+            false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false == inList);
         break;
     }
     case PlugIn::TagItemFunction::ForceToBackendSelect:
@@ -633,6 +639,45 @@ void PlugIn::OnFunctionCall(int functionId, const char* itemString, POINT pt, RE
 
         break;
     }
+    case PlugIn::TagItemFunction::RunwaySelectMenu:
+    {
+        std::string destination(radarTarget.GetCorrelatedFlightPlan().GetFlightPlanData().GetDestination());
+        std::transform(destination.begin(), destination.end(), destination.begin(), ::toupper);
+
+        std::lock_guard guard(this->m_updateQueueLock);
+        auto it = this->m_updateQueue.find(destination);
+
+        if (this->m_updateQueue.cend() != it) {
+            this->OpenPopupList(area, "Select runway", 1);
+            bool inList = this->m_selectedRunway.find(callsign) != this->m_selectedRunway.cend();
+            this->AddPopupListElement("Remove", "", static_cast<int>(PlugIn::TagItemFunction::RunwaySelect),
+                                      false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false == inList);
+
+            for (const auto& rwy : std::as_const(it->second.arrivalRunways))
+                this->AddPopupListElement(rwy.c_str(), "", static_cast<int>(PlugIn::TagItemFunction::RunwaySelect));
+        }
+
+        break;
+    }
+    case PlugIn::TagItemFunction::RunwaySelect:
+    {
+        std::lock_guard guard(this->m_updateQueueLock);
+
+        if (0 == std::strcmp(itemString, "Remove")) {
+            auto it = this->m_selectedRunway.find(callsign);
+            if (this->m_selectedRunway.end() != it)
+                this->m_selectedRunway.erase(it);
+        }
+        else {
+            auto it = this->m_selectedRunway.find(callsign);
+            if (this->m_selectedRunway.end() != it)
+                it->second = itemString;
+            else
+                this->m_selectedRunway.insert({ callsign, itemString });
+        }
+
+        break;
+    }
     default:
         break;
     }
@@ -651,8 +696,17 @@ void PlugIn::addUpdateQueue(EuroScopePlugIn::CRadarTarget& radarTarget) {
 #pragma warning(default: 4244)
 
     auto it = this->m_updateQueue.find(destination);
-    if (this->m_updateQueue.end() != it)
-        it->second.push_back(radarTarget.GetCallsign());
+    if (this->m_updateQueue.end() != it) {
+        /* check that an active arrival runway is selected */
+        auto selectedIt = this->m_selectedRunway.find(radarTarget.GetCallsign());
+        if (this->m_selectedRunway.end() != selectedIt) {
+            auto activeRwyIt = std::find(it->second.arrivalRunways.cbegin(), it->second.arrivalRunways.cend(), selectedIt->second);
+            if (it->second.arrivalRunways.cend() == activeRwyIt)
+                this->m_selectedRunway.erase(selectedIt);
+        }
+
+        it->second.inboundUpdates.push_back(radarTarget.GetCallsign());
+    }
 }
 
 void PlugIn::updateInbound(EuroScopePlugIn::CRadarTarget& radarTarget) {
@@ -726,8 +780,15 @@ void PlugIn::updateSequence(std::shared_ptr<aman::AircraftSequence>& sequence) {
             }
         }
 
-        if (false == found)
+        if (false == found) {
+            /* delete the forced runway selection */
+            std::lock_guard guard(this->m_updateQueueLock);
+            auto selected = this->m_selectedRunway.find(it->first);
+            if (this->m_selectedRunway.end() != selected)
+                this->m_selectedRunway.erase(selected);
+
             it = this->m_inbounds.erase(it);
+        }
         else
             ++it;
     }
@@ -764,7 +825,7 @@ void PlugIn::OnTimer(int counter) {
         if (true == this->m_connectedToNetwork) {
             std::lock_guard guardUpdate(this->m_updateQueueLock);
             for (auto& airport : this->m_updateQueue)
-                airport.second.clear();
+                airport.second.inboundUpdates.clear();
 
             std::lock_guard guardInbound(this->m_inboundsQueueLock);
             this->m_inbounds.clear();
@@ -785,10 +846,10 @@ void PlugIn::OnTimer(int counter) {
         aman::AircraftUpdate update;
         update.set_airport(airport.first);
 
-        if (0 != airport.second.size()) {
+        if (0 != airport.second.inboundUpdates.size()) {
             for (auto target = this->RadarTargetSelectFirst(); true == target.IsValid(); target = this->RadarTargetSelectNext(target)) {
-                auto it = std::find(airport.second.begin(), airport.second.end(), target.GetCallsign());
-                if (airport.second.end() != it) {
+                auto it = std::find(airport.second.inboundUpdates.begin(), airport.second.inboundUpdates.end(), target.GetCallsign());
+                if (airport.second.inboundUpdates.end() != it) {
                     auto report = update.add_reports();
                     this->generateAircraftReportMessage(target, report);
                 }
@@ -802,8 +863,34 @@ void PlugIn::OnTimer(int counter) {
         else
             this->updateSequence(sequence);
 
-        airport.second.clear();
+        airport.second.inboundUpdates.clear();
     }
 
     this->m_updateQueueLock.unlock();
 }
+
+void PlugIn::OnAirportRunwayActivityChanged() {
+    std::lock_guard guard(this->m_updateQueueLock);
+
+    /* cleanup the arrival airports */
+    for (auto& airport : this->m_updateQueue)
+        airport.second.arrivalRunways.clear();
+
+    EuroScopePlugIn::CSectorElement rwy;
+    for (rwy = this->SectorFileElementSelectFirst(EuroScopePlugIn::SECTOR_ELEMENT_RUNWAY); true == rwy.IsValid();
+        rwy = this->SectorFileElementSelectNext(rwy, EuroScopePlugIn::SECTOR_ELEMENT_RUNWAY)) {
+        /* remove leading and trailing whitespaces */
+        std::string airport(rwy.GetAirportName());
+        airport = std::regex_replace(airport, std::regex("^ +| +$|( ) +"), "$1");
+
+        if (true == rwy.IsElementActive(false, 0) || true == rwy.IsElementActive(false, 1)) {
+            auto airportData = this->m_updateQueue.find(airport);
+            if (this->m_updateQueue.end() != airportData) {
+                if (true == rwy.IsElementActive(false, 0))
+                    airportData->second.arrivalRunways.push_back(rwy.GetRunwayName(0));
+                if (true == rwy.IsElementActive(false, 1))
+                    airportData->second.arrivalRunways.push_back(rwy.GetRunwayName(1));
+            }
+        }
+    }
+}

+ 24 - 12
src/PlugIn.h

@@ -37,10 +37,17 @@ namespace aman {
          */
         enum class TagItemFunction {
             ForceToBackendMenu   = 0, /**< Opens the menu to force tracks */
-            ForceToBackendSelect = 1  /**< Reacts on controller selection */
+            ForceToBackendSelect = 1, /**< Reacts on controller selection */
+            RunwaySelectMenu     = 2, /**< Opens the runway selection menu */
+            RunwaySelect         = 3  /**< Selects the runway */
         };
 
     private:
+        struct AirportData {
+            std::vector<std::string> arrivalRunways;
+            std::list<std::string>   inboundUpdates;
+        };
+
         enum class TagItemElement {
             EstimatedTimeOfArrival = 0,
             PlannedTimeOfArrival   = 1,
@@ -58,17 +65,18 @@ namespace aman {
         static void distanceToPredictedIaf(const EuroScopePlugIn::CRadarTarget& radarTarget, const EuroScopePlugIn::CFlightPlan& flightPlan,
                                            const EuroScopePlugIn::CPosition& iafPosition, aman::AircraftReport* report);
 
-        Communication                                 m_configuration;
-        std::shared_ptr<RadarScreen>                  m_screen;
-        std::mutex                                    m_updateQueueLock;
-        std::map<std::string, std::list<std::string>> m_updateQueue;
-        std::mutex                                    m_inboundsQueueLock;
-        std::map<std::string, Inbound>                m_inbounds;
-        std::list<std::string>                        m_forcedToBackendCallsigns;
-        bool                                          m_compatible;
-        bool                                          m_connectedToNetwork;
-        bool                                          m_sweatboxValid;
-        bool                                          m_playbackValid;
+        Communication                      m_configuration;
+        std::shared_ptr<RadarScreen>       m_screen;
+        std::mutex                         m_updateQueueLock;
+        std::map<std::string, AirportData> m_updateQueue;
+        std::mutex                         m_inboundsQueueLock;
+        std::map<std::string, Inbound>     m_inbounds;
+        std::map<std::string, std::string> m_selectedRunway;
+        std::list<std::string>             m_forcedToBackendCallsigns;
+        bool                               m_compatible;
+        bool                               m_connectedToNetwork;
+        bool                               m_sweatboxValid;
+        bool                               m_playbackValid;
 
     public:
         /**
@@ -145,5 +153,9 @@ namespace aman {
          * @param[in] flightPlan The disconnected flight plan
          */
         void OnFlightPlanDisconnect(EuroScopePlugIn::CFlightPlan flightPlan) override;
+        /**
+         * @brief Called as soon as a runway configuration changed
+         */
+        void OnAirportRunwayActivityChanged() override;
     };
 }