diff --git a/src/PlugIn.cpp b/src/PlugIn.cpp index b21c6db..56354a4 100644 --- a/src/PlugIn.cpp +++ b/src/PlugIn.cpp @@ -53,6 +53,8 @@ PlugIn::PlugIn() : m_screen(), m_updateQueueLock(), m_updateQueue(), + m_inboundsQueueLock(), + m_inbounds(), m_forcedToBackendCallsigns(), m_compatible(false) { GOOGLE_PROTOBUF_VERIFY_VERSION; @@ -391,6 +393,69 @@ bool PlugIn::OnCompileCommand(const char* cmdline) { return false; } +void PlugIn::OnGetTagItem(EuroScopePlugIn::CFlightPlan flightPlan, EuroScopePlugIn::CRadarTarget radarTarget, + int itemCode, int tagData, char itemString[16], int* colorCode, COLORREF* rgb, + double* fontSize) { + std::ignore = tagData; + std::ignore = rgb; + std::ignore = fontSize; + + std::string callsign(radarTarget.GetCallsign()); + std::string destination(flightPlan.GetFlightPlanData().GetDestination()); +#pragma warning(disable: 4244) + std::transform(destination.begin(), destination.end(), destination.begin(), ::toupper); +#pragma warning(default: 4244) + + bool planExpected, forced; + + this->m_updateQueueLock.lock(); + + /* check if the inbound is forced */ + auto cIt = std::find(this->m_forcedToBackendCallsigns.cbegin(), this->m_forcedToBackendCallsigns.cend(), callsign); + forced = this->m_forcedToBackendCallsigns.cend() != cIt; + + /* check if the inbound is expected to be planned */ + planExpected = this->m_updateQueue.cend() != this->m_updateQueue.find(destination); + + this->m_updateQueueLock.unlock(); + + if (TagItemElement::DeltaTime == static_cast(itemCode)) { + if (true == this->m_compatible) { + std::lock_guard guard(this->m_inboundsQueueLock); + auto it = this->m_inbounds.find(callsign); + + /* inbound expected, but not available */ + if (this->m_inbounds.cend() == it && (true == forced || true == planExpected)) { + std::strcpy(itemString, "??:??"); + *colorCode = EuroScopePlugIn::TAG_COLOR_INFORMATION; + } + /* inbound is available */ + else if (this->m_inbounds.cend() != it) { + const auto ttl = static_cast(std::roundf(it->second.timeToLose().convert(second))); + const auto minutes = ttl / 60; + const auto seconds = std::abs(ttl) % 60; + + std::stringstream stream; + stream << std::setw(2) << std::setfill('0') << minutes << ":" + << std::setw(2) << std::setfill('0') << seconds; + std::strcpy(itemString, stream.str().c_str()); + + if (false == it->second.fixedPlan()) + *colorCode = EuroScopePlugIn::TAG_COLOR_INFORMATION; + else + *colorCode = EuroScopePlugIn::TAG_COLOR_DEFAULT; + } + else { + itemString[0] = '\0'; + } + } + else { + std::strcpy(itemString, "N/A"); + *colorCode = EuroScopePlugIn::TAG_COLOR_DEFAULT; + } + } +} + void PlugIn::OnFunctionCall(int functionId, const char* itemString, POINT pt, RECT area) { std::ignore = itemString; std::ignore = pt; @@ -423,11 +488,7 @@ void PlugIn::OnFunctionCall(int functionId, const char* itemString, POINT pt, RE } } -void PlugIn::OnRadarTargetPositionUpdate(EuroScopePlugIn::CRadarTarget radarTarget) { - /* do nothing if the reporter is not initialized and ignore invalid targets */ - if (false == this->m_compatible || false == Backend::instance().initialized() || false == radarTarget.IsValid()) - return; - +void PlugIn::addUpdateQueue(EuroScopePlugIn::CRadarTarget& radarTarget) { std::lock_guard guard(this->m_updateQueueLock); auto forcedIt = std::find(this->m_forcedToBackendCallsigns.cbegin(), this->m_forcedToBackendCallsigns.cend(), radarTarget.GetCallsign()); @@ -444,13 +505,103 @@ void PlugIn::OnRadarTargetPositionUpdate(EuroScopePlugIn::CRadarTarget radarTarg it->second.push_back(radarTarget.GetCallsign()); } +void PlugIn::updateInbound(EuroScopePlugIn::CRadarTarget& radarTarget) { + std::lock_guard guard(this->m_inboundsQueueLock); + auto it = this->m_inbounds.find(radarTarget.GetCallsign()); + if (this->m_inbounds.end() != it) + it->second.update(radarTarget); +} + +void PlugIn::updateInbound(EuroScopePlugIn::CFlightPlan& plan) { + std::lock_guard guard(this->m_inboundsQueueLock); + auto it = this->m_inbounds.find(plan.GetCorrelatedRadarTarget().GetCallsign()); + if (this->m_inbounds.end() != it) + it->second.update(plan); +} + +void PlugIn::OnRadarTargetPositionUpdate(EuroScopePlugIn::CRadarTarget radarTarget) { + /* do nothing if the reporter is not initialized and ignore invalid targets */ + if (false == this->m_compatible || false == Backend::instance().initialized() || false == radarTarget.IsValid()) + return; + + this->addUpdateQueue(radarTarget); + this->updateInbound(radarTarget); +} + +void PlugIn::OnFlightPlanControllerAssignedDataUpdate(EuroScopePlugIn::CFlightPlan flightPlan, int type) { + /* handle only relevant changes */ + if (EuroScopePlugIn::CTR_DATA_TYPE_HEADING != type && EuroScopePlugIn::CTR_DATA_TYPE_DIRECT_TO != type) + return; + + if (false == flightPlan.GetCorrelatedRadarTarget().IsValid()) + return; + + this->updateInbound(flightPlan); +} + void PlugIn::OnFlightPlanDisconnect(EuroScopePlugIn::CFlightPlan flightPlan) { std::string callsign(flightPlan.GetCorrelatedRadarTarget().GetCallsign()); - std::lock_guard guard(this->m_updateQueueLock); + this->m_updateQueueLock.lock(); auto it = std::find(this->m_forcedToBackendCallsigns.begin(), this->m_forcedToBackendCallsigns.end(), callsign); if (this->m_forcedToBackendCallsigns.end() != it) this->m_forcedToBackendCallsigns.erase(it); + this->m_updateQueueLock.unlock(); + + this->m_inboundsQueueLock.lock(); + auto inIt = this->m_inbounds.find(flightPlan.GetCorrelatedRadarTarget().GetCallsign()); + if (this->m_inbounds.end() != inIt) + this->m_inbounds.erase(inIt); + this->m_inboundsQueueLock.unlock(); +} + +void PlugIn::updateSequence(std::shared_ptr& sequence) { + /* cleanup the inbound list */ + this->m_inboundsQueueLock.lock(); + for (auto it = this->m_inbounds.begin(); this->m_inbounds.end() != it;) { + bool found = false; + + /* check if the inbound is part of the server */ + for (auto i = 0; i < sequence->sequence_size(); ++i) { + if (sequence->sequence(i).callsign() == it->first) { + found = true; + break; + } + } + + if (false == found) + it = this->m_inbounds.erase(it); + else + ++it; + } + this->m_inboundsQueueLock.unlock(); + + /* update the inbound list */ + for (auto i = 0; i < sequence->sequence_size(); ++i) { + const auto& inbound = sequence->sequence(i); + EuroScopePlugIn::CRadarTarget target; + bool found = false; + + for (target = this->RadarTargetSelectFirst(); true == target.IsValid(); target = this->RadarTargetSelectNext(target)) { + if (target.GetCallsign() == inbound.callsign()) { + found = true; + break; + } + } + + if (true == found) { + auto msg = inbound.callsign() + ": PTA: " + inbound.waypoints(inbound.waypoints_size() - 1).pta(); + this->DisplayUserMessage("Test", "Test", msg.c_str(), true, false, false, false, false); + + this->m_inboundsQueueLock.lock(); + auto it = this->m_inbounds.find(inbound.callsign()); + if (this->m_inbounds.end() != it) + it->second.update(target, inbound, sequence->winddata()); + else + this->m_inbounds.insert({ inbound.callsign(), Inbound(target, inbound, sequence->winddata()) }); + this->m_inboundsQueueLock.unlock(); + } + } } void PlugIn::OnTimer(int counter) { @@ -472,16 +623,17 @@ void PlugIn::OnTimer(int counter) { } } - /* send the report */ if (true == inserted) { + /* send the report and request the current sequence */ auto sequence = Backend::instance().update(update); - if (nullptr != sequence) + if (nullptr == sequence) this->DisplayUserMessage(PLUGIN_NAME, "ERROR", "Unable to send a new aircraft report update", true, true, true, true, true); + else + this->updateSequence(sequence); } airport.second.clear(); } - this->m_updateQueue.clear(); this->m_updateQueueLock.unlock(); } diff --git a/src/PlugIn.h b/src/PlugIn.h index 67b6ecf..3abd726 100644 --- a/src/PlugIn.h +++ b/src/PlugIn.h @@ -15,6 +15,7 @@ #pragma warning(pop) #include +#include #pragma warning(push, 0) #include @@ -46,11 +47,17 @@ namespace aman { void validateBackendData(); aman::Aircraft* generateAircraftMessage(EuroScopePlugIn::CRadarTarget& target); void generateAircraftReportMessage(EuroScopePlugIn::CRadarTarget& radarTarget, aman::AircraftReport* report); + void addUpdateQueue(EuroScopePlugIn::CRadarTarget& radarTarget); + void updateInbound(EuroScopePlugIn::CRadarTarget& radarTarget); + void updateInbound(EuroScopePlugIn::CFlightPlan& plan); + void updateSequence(std::shared_ptr& sequence); Communication m_configuration; std::shared_ptr m_screen; std::mutex m_updateQueueLock; std::map> m_updateQueue; + std::mutex m_inboundsQueueLock; + std::map m_inbounds; std::list m_forcedToBackendCallsigns; bool m_compatible; @@ -94,11 +101,31 @@ namespace aman { * @param[in] area The clicked area */ void OnFunctionCall(int functionId, const char* itemString, POINT pt, RECT area) override; + /** + * @brief Called as soon as a tag entry needs to be updated + * @param[in] flightPlan The requested flight plan + * @param[in] radarTarget The corresponding RADAR target + * @param[in] itemCode The requested item + * @param[in] tagData The requested tag data + * @param[in] itemString The buffer to store the data + * @param[in] colorCode The color code of the entry + * @param[in] rgb The colorization of the entry + * @param[in] fontSize The size of the text + */ + void OnGetTagItem(EuroScopePlugIn::CFlightPlan flightPlan, EuroScopePlugIn::CRadarTarget radarTarget, + int itemCode, int tagData, char itemString[16], int* colorCode, COLORREF* rgb, + double* fontSize) override; /** * @brief Called every second * @param[in] counter The counter that indicates the seconds */ void OnTimer(int counter) override; + /** + * @brief Called as soon as a controller updated the flight plan + * @param[in] flightPlan The updated flight plan + * @param[in] type The changed information + */ + void OnFlightPlanControllerAssignedDataUpdate(EuroScopePlugIn::CFlightPlan flightPlan, int type) override; /** * @brief Called as soon as a radar target position is updated * @param[in] radarTarget The updated radar target