123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799 |
- /*
- * Author:
- * Sven Czarnian <devel@svcz.de>
- * Brief:
- * Implements the EuroScope plug-in definition
- * Copyright:
- * 2021 Sven Czarnian
- * License:
- * GNU General Public License v3 (GPLv3)
- */
- #include "stdafx.h"
- #include <algorithm>
- #include <cctype>
- #include <gsl/gsl>
- #include <Shlwapi.h>
- #include <Windows.h>
- #include <curl/curl.h>
- #pragma warning(push, 0)
- #include <Eigen/Geometry>
- #pragma warning(pop)
- #include <GeographicLib/Gnomonic.hpp>
- #include <json/json.h>
- #include <aman/com/Backend.h>
- #include <aman/config/CommunicationFileFormat.h>
- #include <aman/config/IdentifierFileFormat.h>
- #include <aman/helper/String.h>
- #include <aman/types/GeoCoordinate.h>
- #include "com/ZmqContext.h"
- #include "PlugIn.h"
- EXTERN_C IMAGE_DOS_HEADER __ImageBase;
- using namespace aman;
- static std::string __receivedAmanData;
- static std::size_t receiveCurl(void* ptr, std::size_t size, std::size_t nmemb, void* stream) {
- std::ignore = stream;
- std::string serverResult = static_cast<char*>(ptr);
- __receivedAmanData += serverResult;
- return size * nmemb;
- }
- PlugIn::PlugIn() :
- EuroScopePlugIn::CPlugIn(EuroScopePlugIn::COMPATIBILITY_CODE,
- PLUGIN_NAME,
- PLUGIN_VERSION,
- PLUGIN_DEVELOPER,
- PLUGIN_COPYRIGHT),
- m_configuration(),
- m_screen(),
- m_updateQueueLock(),
- m_updateQueue(),
- m_inboundsQueueLock(),
- m_inbounds(),
- m_forcedToBackendCallsigns(),
- m_compatible(false),
- m_connectedToNetwork(false),
- m_sweatboxValid(false),
- m_playbackValid(false) {
- GOOGLE_PROTOBUF_VERIFY_VERSION;
- this->RegisterTagItemType("ETA", static_cast<int>(PlugIn::TagItemElement::EstimatedTimeOfArrival));
- this->RegisterTagItemType("PTA", static_cast<int>(PlugIn::TagItemElement::PlannedTimeOfArrival));
- 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->DisplayUserMessage(PLUGIN_NAME, "INFO", (std::string("Loaded ") + PLUGIN_NAME + " " + PLUGIN_VERSION).c_str(), true, true, false, false, false);
- /* get the dll-path */
- char path[MAX_PATH + 1] = { 0 };
- const gsl::span<char, MAX_PATH + 1> span(path);
- GetModuleFileNameA((HINSTANCE)&__ImageBase, span.data(), span.size());
- PathRemoveFileSpecA(span.data());
- std::string dllPath = span.data();
- CommunicationFileFormat comFormat;
- if (false == comFormat.parse(dllPath + "\\AmanCommunication.txt", this->m_configuration) || true == comFormat.errorFound()) {
- this->DisplayUserMessage(PLUGIN_NAME, "ERROR", "Unable to parse AmanCommunication.txt", true, true, true, true, true);
- this->DisplayUserMessage(PLUGIN_NAME, "ERROR", ("AmanCommunication.txt:" + std::to_string(comFormat.errorLine()) + " - " + comFormat.errorMessage()).c_str(), true, true, true, true, true);
- return;
- }
- IdentifierFileFormat identFormat;
- if (false == identFormat.parse(dllPath + "\\AmanIdentity.txt", this->m_configuration) || true == identFormat.errorFound()) {
- this->DisplayUserMessage(PLUGIN_NAME, "ERROR", "Unable to parse AmanIdentity.txt", true, true, true, true, true);
- this->DisplayUserMessage(PLUGIN_NAME, "ERROR", ("AmanIdentity.txt:" + std::to_string(identFormat.errorLine()) + " - " + identFormat.errorMessage()).c_str(), true, true, true, true, true);
- return;
- }
- ZmqContext::instance().initialize();
- if (false == Backend::instance().initialize(this->m_configuration))
- this->DisplayUserMessage(PLUGIN_NAME, "ERROR", "Unable to initialize the reporter-connection to the backend", true, true, true, true, true);
- this->validateBackendData();
- }
- PlugIn::~PlugIn() {
- Backend::instance().deinitialize();
- ZmqContext::instance().deinitialize();
- google::protobuf::ShutdownProtobufLibrary();
- }
- void PlugIn::validateBackendData() {
- if (false == this->m_configuration.valid) {
- this->m_compatible = false;
- return;
- }
- /* set up the URL */
- std::string url;
- if (true == this->m_configuration.httpsProtocol)
- url += "https://";
- else
- url += "http://";
- url += this->m_configuration.address + ":" + std::to_string(this->m_configuration.portRestAPI) + "/aman/airports";
- CURL* curl = curl_easy_init();
- CURLcode result;
- curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
- curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, static_cast<long>(CURL_HTTP_VERSION_1_1));
- curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
- curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receiveCurl);
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, 2L);
- struct curl_slist* headers = nullptr;
- headers = curl_slist_append(headers, "Accept: */*");
- headers = curl_slist_append(headers, "Content-Type: application/json");
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
- __receivedAmanData.clear();
- result = curl_easy_perform(curl);
- this->m_compatible = false;
- if (CURLE_OK != result) {
- MessageBoxA(nullptr, "Unable to receive backend information", "AMAN-Error", MB_OK);
- return;
- }
- /* validate the json data */
- Json::Value root;
- Json::CharReaderBuilder builder;
- std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
- if (false == reader->parse(__receivedAmanData.c_str(), __receivedAmanData.c_str() + __receivedAmanData.length(), &root, nullptr)) {
- MessageBoxA(nullptr, "Received invalid backend data", "AMAN-Error", MB_OK);
- return;
- }
- std::vector<std::string> airports;
- std::string version;
- for (auto it = root.begin(); root.end() != it; ++it) {
- if ("version" == it.key().asString()) {
- version = it->asString();
- }
- else if ("airports" == it.key().asString() && true == it->isArray()) {
- auto airportArray = *it;
- airports.reserve(airportArray.size());
- for (Json::Value::ArrayIndex i = 0; i < airportArray.size(); ++i)
- airports.push_back(airportArray[i].asString());
- }
- }
- /* invalid version or airports info */
- auto entries = String::splitString(version, ".");
- if (3 != entries.size() || 0 == airports.size()) {
- MessageBoxA(nullptr, "Unable to receive backend information", "AMAN-Error", MB_OK);
- return;
- }
- /* incompatible communication version */
- if (PLUGIN_MAJOR_VERSION != std::atoi(gsl::at(entries, 0).c_str()) || PLUGIN_MINOR_VERSION != std::atoi(gsl::at(entries, 1).c_str())) {
- MessageBoxA(nullptr, "Plugin version is outdated. An update is required", "AMAN-Error", MB_OK);
- return;
- }
- std::lock_guard guard(this->m_updateQueueLock);
- this->m_updateQueue.clear();
- for (const auto& airport : std::as_const(airports))
- this->m_updateQueue.insert({ airport, {} });
- this->m_compatible = true;
- this->DisplayUserMessage(PLUGIN_NAME, "INFO", "Reloaded AMAN-configuration", true, true, false, false, false);
- }
- EuroScopePlugIn::CRadarScreen* PlugIn::OnRadarScreenCreated(const char* displayName, bool needsRadarContent, bool geoReferenced,
- bool canBeSaved, bool canBeCreated) {
- std::ignore = needsRadarContent;
- std::ignore = geoReferenced;
- std::ignore = canBeSaved;
- std::ignore = canBeCreated;
- std::ignore = displayName;
- if (nullptr == this->m_screen)
- this->m_screen = std::make_shared<RadarScreen>();
- return this->m_screen.get();
- }
- aman::Aircraft* PlugIn::generateAircraftMessage(const EuroScopePlugIn::CRadarTarget& target) const {
- if (false == target.IsValid() || false == target.GetCorrelatedFlightPlan().IsValid())
- return nullptr;
- const auto flightPlan = target.GetCorrelatedFlightPlan();
- std::string callsign(target.GetCallsign());
- aman::Aircraft* retval = new aman::Aircraft();
- /* fill all available information */
- retval->set_callsign(callsign);
- if (3 <= callsign.length())
- retval->set_airline(callsign.substr(0, 3));
- retval->set_type(flightPlan.GetFlightPlanData().GetAircraftFPType());
- retval->set_wtc(std::to_string(flightPlan.GetFlightPlanData().GetAircraftWtc()));
- /* TODO get recat */
- retval->set_enginecount(flightPlan.GetFlightPlanData().GetEngineNumber());
- switch (flightPlan.GetFlightPlanData().GetEngineType()) {
- case 'P':
- case 'T':
- retval->set_enginetype(aman::Aircraft_EngineType::Aircraft_EngineType_TURBOPROB);
- break;
- case 'E':
- retval->set_enginetype(aman::Aircraft_EngineType::Aircraft_EngineType_ELECTRIC);
- break;
- case 'J':
- retval->set_enginetype(aman::Aircraft_EngineType::Aircraft_EngineType_JET);
- break;
- default:
- retval->set_enginetype(aman::Aircraft_EngineType::Aircraft_EngineType_UNKNOWN);
- break;
- }
- return retval;
- }
- void PlugIn::distanceToPredictedIaf(const EuroScopePlugIn::CRadarTarget& radarTarget, const EuroScopePlugIn::CFlightPlan& flightPlan,
- const EuroScopePlugIn::CPosition& iafPosition, aman::AircraftReport* report) {
- std::string_view direct(flightPlan.GetControllerAssignedData().GetDirectToPointName());
- if (0 != direct.length()) {
- GeoCoordinate directCoordinate;
- /* find the coordinate of the direct waypoint */
- for (int i = 0; i < flightPlan.GetExtractedRoute().GetPointsNumber(); ++i) {
- if (flightPlan.GetExtractedRoute().GetPointName(i) == direct) {
- directCoordinate = GeoCoordinate(static_cast<float>(flightPlan.GetExtractedRoute().GetPointPosition(i).m_Longitude) * degree,
- static_cast<float>(flightPlan.GetExtractedRoute().GetPointPosition(i).m_Latitude) * degree);
- break;
- }
- }
- if (0_m != directCoordinate.distanceTo(GeoCoordinate())) {
- const GeographicLib::Gnomonic projection(GeographicLib::Geodesic::WGS84());
- Eigen::Vector2f currentCartesian, iafCartesian, directCartesian;
- /* convert to Cartesian with IAF as the reference */
- projection.Forward(static_cast<float>(iafPosition.m_Latitude), static_cast<float>(iafPosition.m_Longitude),
- static_cast<float>(radarTarget.GetPosition().GetPosition().m_Latitude),
- static_cast<float>(radarTarget.GetPosition().GetPosition().m_Longitude),
- currentCartesian[0], currentCartesian[1]);
- projection.Forward(static_cast<float>(iafPosition.m_Latitude), static_cast<float>(iafPosition.m_Longitude),
- directCoordinate.latitude().convert(degree),
- directCoordinate.longitude().convert(degree),
- directCartesian[0], directCartesian[1]);
- projection.Forward(static_cast<float>(iafPosition.m_Latitude), static_cast<float>(iafPosition.m_Longitude),
- static_cast<float>(iafPosition.m_Latitude), static_cast<float>(iafPosition.m_Longitude),
- iafCartesian[0], iafCartesian[1]);
- /* project IAF on line between current position and direct */
- const auto direction = (directCartesian - currentCartesian).normalized();
- Eigen::ParametrizedLine<float, 2> line(currentCartesian, direction);
- const auto projectCartesian = line.projection(iafCartesian);
- const auto distanceStart = (currentCartesian - projectCartesian).squaredNorm();
- const auto distanceEnd = (directCartesian - projectCartesian).squaredNorm();
- const auto distanceDirect = (currentCartesian - directCartesian).squaredNorm();
- /* projection of IAF in front of aircraft and not behind direct */
- if (distanceStart <= distanceDirect && distanceEnd <= distanceDirect) {
- EuroScopePlugIn::CPosition projected;
- float lat = 0.0f, lon = 0.0f;
- projection.Reverse(static_cast<float>(iafPosition.m_Latitude), static_cast<float>(iafPosition.m_Longitude),
- projectCartesian[0], projectCartesian[1], lat, lon);
- projected.m_Latitude = lat;
- projected.m_Longitude = lon;
- const auto distanceToProjected = radarTarget.GetPosition().GetPosition().DistanceTo(projected);
- report->set_distancetoiaf(static_cast<int>(std::round(distanceToProjected)));
- }
- else {
- report->set_distancetoiaf(0);
- }
- }
- /* did not find the coordinate -> fallback */
- else {
- report->set_distancetoiaf(0);
- }
- }
- /* in heading mode and passed IAF */
- else {
- report->set_distancetoiaf(0);
- }
- }
- void PlugIn::generateAircraftReportMessage(EuroScopePlugIn::CRadarTarget& radarTarget, aman::AircraftReport* report) {
- const auto flightPlan = radarTarget.GetCorrelatedFlightPlan();
- /* ignore invalid flightplans */
- if (false == flightPlan.IsValid())
- return;
- /* ignore flights that are not tracked by the current controller */
- if (false == flightPlan.GetTrackingControllerIsMe())
- return;
- /* ignore non-IFR flights */
- if (nullptr == flightPlan.GetFlightPlanData().GetPlanType() || 'I' != *flightPlan.GetFlightPlanData().GetPlanType())
- return;
- /* filter invalid destinations */
- const auto destination = std::string_view(flightPlan.GetFlightPlanData().GetDestination());
- if (4 != destination.length())
- return;
- /* filter by airborne identifier (assume a GS>50kn and a big distance to the origin) */
- if (50 > radarTarget.GetGS() || 10.0 > flightPlan.GetDistanceFromOrigin())
- return;
- /* generate protobuf message */
- aman::Aircraft* aircraft = this->generateAircraftMessage(radarTarget);
- if (nullptr == aircraft)
- return;
- aman::Dynamics* dynamics = new aman::Dynamics();
- dynamics->set_altitude(radarTarget.GetPosition().GetFlightLevel());
- dynamics->set_heading(radarTarget.GetPosition().GetReportedHeading());
- dynamics->set_groundspeed(radarTarget.GetPosition().GetReportedGS());
- dynamics->set_verticalspeed(radarTarget.GetVerticalSpeed());
- aman::Coordinate* coordinate = new aman::Coordinate();
- coordinate->set_latitude(radarTarget.GetPosition().GetPosition().m_Latitude);
- coordinate->set_longitude(radarTarget.GetPosition().GetPosition().m_Longitude);
- /* create the report */
- switch (this->ControllerMyself().GetFacility()) {
- case 1:
- case 6:
- report->set_reportedby(aman::AircraftReport::CENTER);
- break;
- case 2:
- report->set_reportedby(aman::AircraftReport::DELIVERY);
- break;
- case 3:
- report->set_reportedby(aman::AircraftReport::GROUND);
- break;
- case 4:
- report->set_reportedby(aman::AircraftReport::TOWER);
- break;
- case 5:
- report->set_reportedby(aman::AircraftReport::APPROACH);
- break;
- default:
- report->set_reportedby(aman::AircraftReport::UNKNOWN);
- break;
- }
- auto currentPosition = radarTarget.GetPosition().GetPosition();
- EuroScopePlugIn::CPosition iafPosition;
- std::string iafName;
- for (auto element = this->SectorFileElementSelectFirst(EuroScopePlugIn::SECTOR_ELEMENT_STAR);
- true == element.IsValid();
- element = this->SectorFileElementSelectNext(element, EuroScopePlugIn::SECTOR_ELEMENT_STAR))
- {
- auto split = String::splitString(element.GetName(), " ");
- /* find the correct star */
- if (0 != split.size() && destination == gsl::at(split, 0)) {
- /* get the IAF */
- EuroScopePlugIn::CPosition position;
- if (true == element.GetPosition(&position, 0)) {
- /* match the waypoints to get the name*/
- for (int i = 0; i < flightPlan.GetExtractedRoute().GetPointsNumber(); ++i) {
- if (1.0f >= flightPlan.GetExtractedRoute().GetPointPosition(i).DistanceTo(position)) {
- iafPosition = flightPlan.GetExtractedRoute().GetPointPosition(i);
- iafName = flightPlan.GetExtractedRoute().GetPointName(i);
- report->set_initialapproachfix(iafName);
- break;
- }
- }
- }
- }
- if (0 != iafName.length())
- break;
- }
- if (0 != iafName.length()) {
- const auto iafDistance = currentPosition.DistanceTo(iafPosition);
- /* calculate the distance to the IAF */
- double distanceToIaf = 0.0f;
- for (int i = 1; i < flightPlan.GetPositionPredictions().GetPointsNumber(); ++i) {
- const double distance = flightPlan.GetPositionPredictions().GetPosition(i).DistanceTo(currentPosition);
- const double headingDelta = std::abs(radarTarget.GetPosition().GetReportedHeading() - flightPlan.GetPositionPredictions().GetPosition(i).DirectionTo(iafPosition));
- /*
- * 1. no direct way to IAF -> some direct given -> stop after lateral passing
- * 2. passed the IAF on a way to a direct
- */
- if ((90.0 < headingDelta && 270.0 > headingDelta) || iafDistance < distance)
- break;
- distanceToIaf += distance;
- currentPosition = flightPlan.GetPositionPredictions().GetPosition(i);
- }
- if (0.01f >= std::abs(distanceToIaf))
- distanceToIaf = iafDistance;
- report->set_distancetoiaf(static_cast<int>(std::round(distanceToIaf)));
- }
- report->set_destination(std::string(destination));
- /* support GrPlugin and TST with the stand association */
- if (nullptr != radarTarget.GetCorrelatedFlightPlan().GetControllerAssignedData().GetFlightStripAnnotation(6)) {
- std::string stand(radarTarget.GetCorrelatedFlightPlan().GetControllerAssignedData().GetFlightStripAnnotation(6));
- auto split = String::splitString(stand, "/");
- if (3 == split.size() && "s" == gsl::at(split, 0) && "s" == gsl::at(split, 2))
- report->set_plannedgate(gsl::at(split, 1));
- }
- report->set_allocated_aircraft(aircraft);
- report->set_allocated_dynamics(dynamics);
- report->set_allocated_position(coordinate);
- /* set the report time */
- std::stringstream stream;
- const auto reportTime = std::chrono::utc_clock::now();
- stream << std::format("{0:%Y%m%d%H%M%S}", reportTime);
- const auto elements = String::splitString(stream.str(), ".");
- report->set_reporttime(gsl::at(elements, 0));
- }
- bool PlugIn::OnCompileCommand(const char* cmdline) {
- std::string message(cmdline);
- bool retval = false;
- #pragma warning(disable: 4244)
- std::transform(message.begin(), message.end(), message.begin(), ::toupper);
- #pragma warning(default: 4244)
- /* no AMAN command */
- if (0 != message.find(".AMAN"))
- return retval;
- 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")) {
- this->m_sweatboxValid = !this->m_sweatboxValid;
- if (true == this->m_sweatboxValid)
- this->m_playbackValid = false;
- retval = true;
- }
- else if (std::string::npos != message.find("PLAYBACK")) {
- this->m_playbackValid = !this->m_playbackValid;
- if (true == this->m_playbackValid)
- this->m_sweatboxValid = false;
- retval = true;
- }
- if (true == retval) {
- if (true == this->m_sweatboxValid)
- this->DisplayUserMessage(PLUGIN_NAME, "INFO", "Sweatbox data is used in AMAN", true, true, false, false, false);
- else
- this->DisplayUserMessage(PLUGIN_NAME, "INFO", "Sweatbox is ignored in AMAN", true, true, false, false, false);
- if (true == this->m_playbackValid)
- this->DisplayUserMessage(PLUGIN_NAME, "INFO", "Playback data is used in AMAN", true, true, false, false, false);
- else
- this->DisplayUserMessage(PLUGIN_NAME, "INFO", "Playback is ignored in AMAN", true, true, false, false, false);
- }
- return retval;
- }
- 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 = false, forced = false;
- const gsl::span<char, 16> tagString(itemString, 16UL);
- this->m_updateQueueLock.lock();
- /* check if the inbound is forced */
- if (0 != this->m_forcedToBackendCallsigns.size()) {
- const 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();
- std::lock_guard guard(this->m_inboundsQueueLock);
- auto it = this->m_inbounds.find(callsign);
- std::string message;
- switch (static_cast<TagItemElement>(itemCode)) {
- case TagItemElement::EstimatedTimeOfArrival:
- {
- if (this->m_inbounds.cend() != it)
- message = UtcTime::timeToString(it->second.eta(), "%H:%M");
- else if (true == forced || true == planExpected)
- message = "??:??";
- break;
- }
- case TagItemElement::PlannedTimeOfArrival:
- {
- if (this->m_inbounds.cend() != it)
- message = UtcTime::timeToString(it->second.pta(), "%H:%M");
- else if (true == forced || true == planExpected)
- message = "??:??";
- break;
- }
- case TagItemElement::DeltaTime:
- {
- if (this->m_inbounds.cend() != it) {
- const auto ttl = static_cast<int>(std::roundf(it->second.timeToLose().convert(second)));
- std::stringstream stream;
- stream << ttl;
- message = stream.str();
- }
- else if (true == forced || true == planExpected) {
- message = "??";
- }
- break;
- }
- case TagItemElement::FixedPlanIndicator:
- if (this->m_inbounds.cend() != it && false == it->second.fixedPlan())
- message = "*";
- else if (this->m_inbounds.cend() == it && (true == forced || true == planExpected))
- message = "*";
- break;
- default:
- break;
- }
- if (0 != message.length()) {
- std::strcpy(itemString, message.c_str());
- *colorCode = EuroScopePlugIn::TAG_COLOR_DEFAULT;
- }
- }
- void PlugIn::OnFunctionCall(int functionId, const char* itemString, POINT pt, RECT area) {
- std::ignore = itemString;
- std::ignore = pt;
- const auto radarTarget = this->RadarTargetSelectASEL();
- if (false == radarTarget.IsValid() || false == radarTarget.GetCorrelatedFlightPlan().IsValid())
- return;
- std::string callsign(radarTarget.GetCallsign());
- switch (static_cast<PlugIn::TagItemFunction>(functionId)) {
- case PlugIn::TagItemFunction::ForceToBackendMenu:
- {
- 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);
- this->AddPopupListElement("Remove from AMAN", "", static_cast<int>(PlugIn::TagItemFunction::ForceToBackendSelect),
- false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false == inList);
- break;
- }
- case PlugIn::TagItemFunction::ForceToBackendSelect:
- {
- 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 (0 == std::strcmp(itemString, "Add to AMAN")) {
- if (this->m_updateQueue.end() != it) {
- auto cIt = std::find(this->m_forcedToBackendCallsigns.cbegin(), this->m_forcedToBackendCallsigns.cend(), callsign);
- if (this->m_forcedToBackendCallsigns.cend() == cIt)
- this->m_forcedToBackendCallsigns.push_back(callsign);
- }
- }
- else {
- if (this->m_updateQueue.end() != it) {
- auto cIt = std::find(this->m_forcedToBackendCallsigns.begin(), this->m_forcedToBackendCallsigns.end(), callsign);
- if (this->m_forcedToBackendCallsigns.cend() != cIt)
- this->m_forcedToBackendCallsigns.erase(cIt);
- }
- }
- break;
- }
- default:
- break;
- }
- }
- 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());
- if (false == radarTarget.GetCorrelatedFlightPlan().GetTrackingControllerIsMe() && this->m_forcedToBackendCallsigns.cend() == forcedIt)
- return;
- std::string destination(radarTarget.GetCorrelatedFlightPlan().GetFlightPlanData().GetDestination());
- #pragma warning(disable: 4244)
- std::transform(destination.begin(), destination.end(), destination.begin(), ::toupper);
- #pragma warning(default: 4244)
- auto it = this->m_updateQueue.find(destination);
- if (this->m_updateQueue.end() != it)
- 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;
- /* validate the correct connection */
- bool validConnection = EuroScopePlugIn::CONNECTION_TYPE_DIRECT == this->GetConnectionType();
- validConnection |= true == this->m_sweatboxValid && EuroScopePlugIn::CONNECTION_TYPE_SWEATBOX == this->GetConnectionType();
- validConnection |= true == this->m_playbackValid && EuroScopePlugIn::CONNECTION_TYPE_PLAYBACK == this->GetConnectionType();
- if (false == validConnection)
- 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());
- 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<aman::AircraftSequence>& 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) {
- 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) {
- /* cleanup the internal data */
- if (EuroScopePlugIn::CONNECTION_TYPE_NO == this->GetConnectionType()) {
- if (true == this->m_connectedToNetwork) {
- std::lock_guard guardUpdate(this->m_updateQueueLock);
- for (auto& airport : this->m_updateQueue)
- airport.second.clear();
- std::lock_guard guardInbound(this->m_inboundsQueueLock);
- this->m_inbounds.clear();
- this->m_connectedToNetwork = false;
- }
- return;
- }
- this->m_connectedToNetwork = true;
- if (false == this->m_compatible || false == Backend::instance().initialized() || 0 != (counter % 10))
- return;
- this->m_updateQueueLock.lock();
- for (auto& airport : this->m_updateQueue) {
- aman::AircraftUpdate update;
- update.set_airport(airport.first);
- if (0 != airport.second.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 report = update.add_reports();
- this->generateAircraftReportMessage(target, report);
- }
- }
- }
- /* send the report and request the current sequence */
- auto sequence = Backend::instance().update(update);
- 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_updateQueueLock.unlock();
- }
|