/* * Author: * Sven Czarnian * Brief: * Implements the EuroScope plug-in definition * Copyright: * 2021 Sven Czarnian * License: * GNU General Public License v3 (GPLv3) */ #include "stdafx.h" #include #include #include #include #include #include #include #include #include "com/ZmqContext.h" #include "PlugIn.h" EXTERN_C IMAGE_DOS_HEADER __ImageBase; using namespace aman; PlugIn::PlugIn() : EuroScopePlugIn::CPlugIn(EuroScopePlugIn::COMPATIBILITY_CODE, PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_DEVELOPER, PLUGIN_COPYRIGHT), m_configuration() { GOOGLE_PROTOBUF_VERIFY_VERSION; 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 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 == BackendNotification::instance().initialize(this->m_configuration)) { this->DisplayUserMessage(PLUGIN_NAME, "ERROR", "Unable to initialize the reporter-connection to the backend", true, true, true, true, true); return; } if (false == BackendReceiver::instance().initialize(this->m_configuration)) { this->DisplayUserMessage(PLUGIN_NAME, "ERROR", "Unable to initialize the scheduling-connection to the backend", true, true, true, true, true); BackendNotification::instance().deinitialize(); return; } } PlugIn::~PlugIn() noexcept { BackendReceiver::instance().deinitialize(); BackendNotification::instance().deinitialize(); ZmqContext::instance().deinitialize(); google::protobuf::ShutdownProtobufLibrary(); } 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(); return this->m_screen.get(); } aman::Aircraft* PlugIn::generateAircraftMessage(const EuroScopePlugIn::CRadarTarget& target) { if (false == target.IsValid() || false == target.GetCorrelatedFlightPlan().IsValid()) return nullptr; 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::generateAircraftReportMessage(const EuroScopePlugIn::CRadarTarget& radarTarget, aman::AircraftReport* report) { 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 */ auto destination = std::string_view(flightPlan.GetFlightPlanData().GetDestination()); if (4 != destination.length()) return; /* filter by distance to destination */ double distanceNM = flightPlan.GetDistanceToDestination(); if (5.0 > distanceNM || 250.0 < distanceNM) 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 == 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()) { auto iafDistance = currentPosition.DistanceTo(iafPosition); /* calculate the distance to the IAF */ double distanceToIaf = 0.0f, lastIafHeading = 100000.0f; for (int i = 1; i < flightPlan.GetPositionPredictions().GetPointsNumber(); ++i) { double distance = flightPlan.GetPositionPredictions().GetPosition(i).DistanceTo(currentPosition); 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(std::round(distanceToIaf))); } report->set_destination(std::string(destination)); report->set_allocated_aircraft(aircraft); report->set_allocated_dynamics(dynamics); report->set_allocated_position(coordinate); /* set the report time */ std::stringstream stream; auto reportTime = std::chrono::utc_clock::now(); stream << std::format("{0:%Y%m%d%H%M%S}", reportTime); report->set_reporttime(String::splitString(stream.str(), ".")[0]); /* send the report */ if (false == BackendNotification::instance().send(update)) this->DisplayUserMessage(PLUGIN_NAME, "ERROR", "Unable to send a new aircraft report update", true, true, true, true, true); }