268 lines
10 KiB
C++
268 lines
10 KiB
C++
/*
|
|
* 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 <gsl/gsl>
|
|
#include <Shlwapi.h>
|
|
#include <Windows.h>
|
|
|
|
#include <aman/com/BackendReceiver.h>
|
|
#include <aman/com/BackendNotification.h>
|
|
#include <aman/config/CommunicationFileFormat.h>
|
|
#include <aman/config/IdentifierFileFormat.h>
|
|
#include <aman/helper/String.h>
|
|
|
|
#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<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 == 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<RadarScreen>();
|
|
|
|
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<int>(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);
|
|
}
|