diff --git a/Changelog.txt b/Changelog.txt index 1332840..b5743cb 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,16 @@ +[Changelog v0.14.4] +:gear: **Changed Features / Behaviour** +* FPLN - startup requests are now blocked if no runway is set (both "normal" startup and rwy startup) + * request system refactored - requests and rwy requests share the same type now (distinction is now stored with the flight plan) + * other smaller improvements to the request system (mainly startup request related) +* CONF - improved checking for military SIDs (OID) to validate against the .ese file. As per previous version OIDs have to be written like found in the .ese file (there is no mastering like with normal SIDs) + * added a health check to warn about OIDs in the config that haven't been found in the .ese file + +:lady_beetle: **Fixes** +* FPLN - Squawks are now also displayed if the departure airport is inactive. Only CTR controllers were affected by this (#401) +* FPLN - rwy request indicators shouldn't be overwritten anymore (#406) +* CONF - military SIDs (OIDs) had to be at the end of the config due to missing reset of special OID/SID numbering (#402) + [Changelog v0.14.3.1] >[!NOTE] > This Hotfix is only needed if SIDs without any route (no waypoint after the last ':') is present diff --git a/area.h b/area.h index 78011fb..9cd9b32 100644 --- a/area.h +++ b/area.h @@ -65,7 +65,7 @@ namespace vsid std::vector lines; /** - * @brief Creates a decimal position pair // #refacotr - change to utils.h version + * @brief Creates a decimal position pair // #refactor - change to utils.h version * * @param pos the fpln position as pair of lat/long coordinates */ diff --git a/configparser.cpp b/configparser.cpp index 55fd29f..35e2afa 100644 --- a/configparser.cpp +++ b/configparser.cpp @@ -477,7 +477,7 @@ void vsid::ConfigParser::loadAirportConfig(std::map aptInfo.requests["taxi"] = {}; aptInfo.requests["departure"] = {}; aptInfo.requests["vfr"] = {}; - aptInfo.rwyrequests["rwy startup"] = {}; + aptInfo.rwyrequests["startup"] = {}; // customRules @@ -603,12 +603,12 @@ void vsid::ConfigParser::loadAirportConfig(std::map vsid::tmpSidSettings desSetting; vsid::tmpSidSettings idSetting; - std::string fixedNumber = ""; - // "field level" - iterates over restrictions and sid way points / bases for (auto &sidField : this->parsedConfig.at(icao).at("sids").items()) { + std::string fixedNumber = ""; + if (sidField.key() == "allowDiffNumbers") fieldSetting.allowDiffNumbers = this->parsedConfig.at(icao).at("sids").at(sidField.key()); else if (sidField.key() == "initial") fieldSetting.initial = this->parsedConfig.at(icao).at("sids").at(sidField.key()); else if (sidField.key() == "climbvia") fieldSetting.via = this->parsedConfig.at(icao).at("sids").at(sidField.key()); @@ -703,10 +703,14 @@ void vsid::ConfigParser::loadAirportConfig(std::map else if (!this->isConfigValue(sidField.key())) { // special check for possible military SIDs / OIDs (format: XY12) + if (vsid::utils::lastIsDigit(sidField.key()) && vsid::utils::countDigits(sidField.key()) > 1 && sidField.key().length() > 2) { fieldSetting.base = sidField.key().substr(0, sidField.key().length() - 1); fixedNumber = sidField.key().back(); + + messageHandler->writeMessage("DEBUG", "[" + sidField.key() + "] contained a number - setting as fixed SID number: " + + fixedNumber, vsid::MessageHandler::DebugArea::Conf); } else { diff --git a/constants.h b/constants.h index 295483e..811eb1b 100644 --- a/constants.h +++ b/constants.h @@ -70,6 +70,7 @@ const int MENU_BOTTOM_BAR = 112; // errors const std::string ERROR_FPLN_ATCBLOCK = "E010"; +const std::string ERROR_FPLN_REQSPLIT = "E010-1"; const std::string ERROR_FPLN_SIDWPT = "E011"; const std::string ERROR_FPLN_SPLITWPT = "E012"; const std::string ERROR_FPLN_EXTFUNC = "E013"; diff --git a/display.cpp b/display.cpp index c56d46a..9f4507b 100644 --- a/display.cpp +++ b/display.cpp @@ -152,19 +152,30 @@ void vsid::Display::OnRefresh(HDC hDC, int Phase) this->getZoomLevel() <= sharedPlugin->getConfigParser().getIndicatorDefaultValues().showBelowZoom) { std::string adep = target.GetCorrelatedFlightPlan().GetFlightPlanData().GetOrigin(); - std::string fplnRwy = target.GetCorrelatedFlightPlan().GetFlightPlanData().GetDepartureRwy(); // # refactor - use rwy from actual flight plan (icao block) + std::string fplnRwy = vsid::fplnhelper::getAtcBlock(target.GetCorrelatedFlightPlan()).second; + std::string reqType = fplnInfo.request; + bool isRwyReq = fplnInfo.request.find("rwy") != std::string::npos; - if (fplnInfo.request != "" && adep != "") + if (isRwyReq) + { + try + { + reqType = vsid::utils::split(reqType, ' ').at(1); + } + catch (std::out_of_range&) {} + } + + if (!reqType.empty() && !adep.empty() && sharedPlugin->getActiveApts().contains(adep)) { EuroScopePlugIn::CPosition offsetPos = this->getIndicatorOffset(targetPos, offset, zoomScale, 180.0); POINT offsetPx = this->ConvertCoordFromPositionToPixel(offsetPos); - try + if (!isRwyReq) { for (auto& [type, req] : sharedPlugin->getActiveApts().at(adep).requests) { - if (type != fplnInfo.request) continue; + if (type != reqType) continue; for (std::set>::iterator it = req.begin(); it != req.end(); ++it) { @@ -188,21 +199,23 @@ void vsid::Display::OnRefresh(HDC hDC, int Phase) dc.DrawText(reqPos.c_str(), &area, DT_BOTTOM); } } - + } + else + { for (auto& [type, rwys] : sharedPlugin->getActiveApts().at(adep).rwyrequests) { - if (type != fplnInfo.request) continue; + if (type != reqType) continue; for (auto& [rwy, rwyReq] : rwys) { - if (fplnRwy == "" || fplnRwy != rwy) continue; + if (fplnRwy.empty() || fplnRwy != rwy) continue; for (std::set>::iterator it = rwyReq.begin(); it != rwyReq.end(); ++it) { if (it->first != callsign) continue; size_t pos = std::distance(it, rwyReq.end()); - std::string reqPos = vsid::utils::toupper(type).at(0) + std::to_string(pos); + std::string reqPos = "R" + std::to_string(pos); CRect area; @@ -219,8 +232,7 @@ void vsid::Display::OnRefresh(HDC hDC, int Phase) } } } - } - catch (std::out_of_range) {}; + } } } diff --git a/vSIDPlugin.cpp b/vSIDPlugin.cpp index 97ae174..c81e508 100644 --- a/vSIDPlugin.cpp +++ b/vSIDPlugin.cpp @@ -2380,97 +2380,138 @@ void vsid::VSIDPlugin::OnFunctionCall(int FunctionId, const char * sItemString, if (FunctionId == TAG_FUNC_VSID_REQMENU) { + if (!this->activeAirports.contains(adep)) + { + messageHandler->writeMessage("WARNING", "[" + callsign + "] - Airport " + adep + " not active, can't process request!"); + return; + } if (strlen(sItemString) == 0) { this->OpenPopupList(Area, "REQ", 1); this->AddPopupListElement("No Req", "No Req", TAG_FUNC_VSID_REQMENU, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); - if (this->activeAirports.contains(adep)) + this->AddPopupListElement("Clearance", "Clearance", TAG_FUNC_VSID_REQMENU, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); + this->AddPopupListElement("Startup", "Startup", TAG_FUNC_VSID_REQMENU, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); + this->AddPopupListElement("RWY Startup", "RWY Startup", TAG_FUNC_VSID_REQMENU, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); + this->AddPopupListElement("Pushback", "Pushback", TAG_FUNC_VSID_REQMENU, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); + this->AddPopupListElement("Taxi", "Taxi", TAG_FUNC_VSID_REQMENU, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); + this->AddPopupListElement("Departure", "Departure", TAG_FUNC_VSID_REQMENU, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); + if (fpln.GetFlightPlanData().GetPlanType() == std::string("V")) { - this->AddPopupListElement("Clearance", "Clearance", TAG_FUNC_VSID_REQMENU, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); - this->AddPopupListElement("Startup", "Startup", TAG_FUNC_VSID_REQMENU, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); - this->AddPopupListElement("RWY Startup", "RWY Startup", TAG_FUNC_VSID_REQMENU, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); - this->AddPopupListElement("Pushback", "Pushback", TAG_FUNC_VSID_REQMENU, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); - this->AddPopupListElement("Taxi", "Taxi", TAG_FUNC_VSID_REQMENU, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); - this->AddPopupListElement("Departure", "Departure", TAG_FUNC_VSID_REQMENU, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); - if (fpln.GetFlightPlanData().GetPlanType() == std::string("V")) - { - this->AddPopupListElement("VFR", "VFR", TAG_FUNC_VSID_REQMENU, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); - } + this->AddPopupListElement("VFR", "VFR", TAG_FUNC_VSID_REQMENU, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); } } else if (strlen(sItemString) != 0) { std::string req = vsid::utils::tolower(sItemString); + bool isRwyReq = req.find("rwy") != std::string::npos; + + if (isRwyReq) + { + try + { + req = vsid::utils::split(vsid::utils::tolower(sItemString), ' ').at(1); + } + catch (std::out_of_range&) + { + messageHandler->writeMessage("ERROR", "[" + callsign + "] failed to split RWY from request: " + sItemString); + return; + } + } + + bool isFplRwyReq = this->processed[callsign].request.find("rwy") != std::string::npos; std::string newScratch = ""; long long now = std::chrono::floor(std::chrono::utc_clock::now()).time_since_epoch().count(); - if (this->processed[callsign].request == req) + // check existing requests to preserve request times when switching from norm to rwy request and vice versa + + if (!isRwyReq && !isFplRwyReq && this->processed[callsign].request == req) // all req other than rwq requests { - if (this->activeAirports[adep].requests.contains(req)) return; + messageHandler->writeMessage("DEBUG", "[" + callsign + "] no rwy req and stored req matches current req", vsid::MessageHandler::DebugArea::Req); - if (this->activeAirports[adep].rwyrequests.contains(req)) + if (this->activeAirports[adep].requests.contains(req)) { - for (auto& [rwy, rwyReq] : this->activeAirports[adep].rwyrequests[req]) + for (auto& [reqCallsign, _] : this->activeAirports[adep].requests[req]) { - if (rwy != fplnData.GetDepartureRwy()) continue; - - for (auto& [reqCallsign, reqTime] : rwyReq) + if (reqCallsign == callsign) { - if (reqCallsign == callsign) return; + messageHandler->writeMessage("DEBUG", "[" + callsign + "] already in request list: " + req, vsid::MessageHandler::DebugArea::Req); + return; } } } } - else if (req.find(this->processed[callsign].request) != std::string::npos) + else if (isRwyReq && isFplRwyReq && this->processed[callsign].request.find(req)) // both current and stored req are rwy req and a (partial) match { - if (req.find("rwy") != std::string::npos) + messageHandler->writeMessage("DEBUG", "[" + callsign + "] rwy req and stored req (partial) matches current req", vsid::MessageHandler::DebugArea::Req); + + if (this->activeAirports[adep].rwyrequests.contains(req)) { - try + for (auto& [reqRwy, fp] : this->activeAirports[adep].rwyrequests[req]) { - std::string normReq = vsid::utils::split(req, ' ').at(1); - - if (this->activeAirports[adep].requests.contains(normReq)) + for (auto& [reqCallsign, _] : fp) { - for (auto& [reqCallign, reqTime] : this->activeAirports[adep].requests[normReq]) + if (reqCallsign == callsign) { - if (reqCallign != callsign) continue; - now = reqTime; - break; + messageHandler->writeMessage("DEBUG", "[" + callsign + "] already in request list: " + req + " for rwy: " + reqRwy, + vsid::MessageHandler::DebugArea::Req); + return; } } } - catch (std::out_of_range) {}; } } - else if (this->processed[callsign].request.find(req) != std::string::npos) + else if (!isRwyReq && isFplRwyReq && this->processed[callsign].request.find(req)) { - try + messageHandler->writeMessage("DEBUG", "[" + callsign + "] has rwy request and current non-rwy req (partial) matches", + vsid::MessageHandler::DebugArea::Req); + + if (this->activeAirports[adep].rwyrequests.contains(req)) { bool stop = false; - for (auto& [rwy, rwyReq] : this->activeAirports[adep].rwyrequests.at(this->processed[callsign].request)) + for (auto& [reqRwy, _req] : this->activeAirports[adep].rwyrequests[req]) { - if (rwy != fplnData.GetDepartureRwy()) continue; - - for (auto& [reqCallsign, reqTime] : rwyReq) + for (auto& [reqCallsign, reqTime] : _req) { if (reqCallsign != callsign) continue; now = reqTime; stop = true; - break; } + if (stop) break; } } - catch (std::out_of_range) {}; + } + else if (isRwyReq && !isFplRwyReq && this->processed[callsign].request == req) + { + messageHandler->writeMessage("DEBUG", "[" + callsign + "] has no rwy req, but current req is a rwy req", vsid::MessageHandler::DebugArea::Req); + + if (this->activeAirports[adep].requests.contains(req)) + { + for (auto& [reqCallsign, reqTime] : this->activeAirports[adep].requests[req]) + { + if (reqCallsign != callsign) continue; + + messageHandler->writeMessage("DEBUG", "[" + callsign + "] found in req list: " + req + ". Storing time.", vsid::MessageHandler::DebugArea::Req); + + now = reqTime; + break; + } + } + } + + if (vsid::fplnhelper::getAtcBlock(fpln).second.empty()) + { + messageHandler->writeMessage("WARNING", "[" + callsign + "] no departure runway found in flight plan for request: " + req + ". Not setting the request!"); + return; } newScratch = ".vsid_req_" + std::string(sItemString) + "/" + std::to_string(now); // #refactor - now check for empty string needed - if (newScratch != "") this->addSyncQueue(callsign, newScratch, fpln.GetControllerAssignedData().GetScratchPadString()); + if (!newScratch.empty()) this->addSyncQueue(callsign, newScratch, fpln.GetControllerAssignedData().GetScratchPadString()); } } @@ -3127,65 +3168,36 @@ void vsid::VSIDPlugin::OnGetTagItem(EuroScopePlugIn::CFlightPlan FlightPlan, Eur } } - if (ItemCode == TAG_ITEM_VSID_SQW) + if (ItemCode == TAG_ITEM_VSID_REQ) { - if (auto it = std::find(this->squawkQueue.begin(), this->squawkQueue.end(), callsign); it != this->squawkQueue.end()) + if (this->processed.contains(callsign) && !this->processed[callsign].request.empty()) { - *pRGB = RGB(255, 255, 255); - - if(it == this->squawkQueue.begin()) strcpy_s(sItemString, 16, "NEXT"); - else strcpy_s(sItemString, 16, "STBY"); - - return; // prevent displaying of old squawk until new is set - } - std::string setSquawk = FlightPlan.GetFPTrackPosition().GetSquawk(); - std::string assignedSquawk = FlightPlan.GetControllerAssignedData().GetSquawk(); + + std::string request = this->processed[callsign].request; + bool isFplRwyReq = request.find("rwy") != std::string::npos; - if (setSquawk != assignedSquawk) - { - *pColorCode = EuroScopePlugIn::TAG_COLOR_RGB_DEFINED; - *pRGB = this->configParser.getColor("squawkNotSet"); - } - else - { - if (this->configParser.getColor("squawkSet") == RGB(300, 300, 300)) + if (isFplRwyReq) { - if (FlightPlan.GetState() == EuroScopePlugIn::FLIGHT_PLAN_STATE_NON_CONCERNED) - { - *pColorCode = EuroScopePlugIn::TAG_COLOR_NON_CONCERNED; - } - else if (FlightPlan.GetState() == EuroScopePlugIn::FLIGHT_PLAN_STATE_NOTIFIED) - { - *pColorCode = EuroScopePlugIn::TAG_COLOR_NOTIFIED; - } - else if (FlightPlan.GetState() == EuroScopePlugIn::FLIGHT_PLAN_STATE_ASSUMED) + try { - *pColorCode = EuroScopePlugIn::TAG_COLOR_ASSUMED; + request = vsid::utils::split(request, ' ').at(1); + + messageHandler->removeFplnError(callsign, ERROR_FPLN_REQSPLIT); } - else + catch (std::out_of_range&) { - *pColorCode = EuroScopePlugIn::TAG_COLOR_DEFAULT; + if (!messageHandler->getFplnErrors(callsign).contains(ERROR_FPLN_REQSPLIT)) + { + messageHandler->writeMessage("ERROR", "[" + callsign + "] failed to split stored request (" + request + + ") on tagItem update. Code: " + ERROR_FPLN_REQSPLIT); + messageHandler->addFplnError(callsign, ERROR_FPLN_REQSPLIT); + } } } - else - { - *pColorCode = EuroScopePlugIn::TAG_COLOR_RGB_DEFINED; - *pRGB = this->configParser.getColor("squawkSet"); - } - } - - if (assignedSquawk != "0000" && assignedSquawk != "1234") strcpy_s(sItemString, 16, assignedSquawk.c_str()); - } - - if (ItemCode == TAG_ITEM_VSID_REQ) - { - if (this->processed.contains(callsign) && this->processed[callsign].request != "") - { - std::string& request = this->processed[callsign].request; // check rwy requests first - if(this->activeAirports[adep].rwyrequests.contains(request)) + if(isFplRwyReq && this->activeAirports[adep].rwyrequests.contains(request)) { for (auto& [rwy, rwyreq] : this->activeAirports[adep].rwyrequests[request]) { @@ -3194,7 +3206,7 @@ void vsid::VSIDPlugin::OnGetTagItem(EuroScopePlugIn::CFlightPlan FlightPlan, Eur if (it->first == callsign) { int pos = std::distance(it, rwyreq.end()); - std::string req = vsid::utils::toupper(request).at(0) + std::to_string(pos); + std::string req = "R" + std::to_string(pos); strcpy_s(sItemString, 16, req.c_str()); break; } @@ -3202,7 +3214,7 @@ void vsid::VSIDPlugin::OnGetTagItem(EuroScopePlugIn::CFlightPlan FlightPlan, Eur } } // check normal requests - else if (this->activeAirports[adep].requests.contains(request)) + else if (!isFplRwyReq&& this->activeAirports[adep].requests.contains(request)) { for (std::set>::iterator it = this->activeAirports[adep].requests[request].begin(); it != this->activeAirports[adep].requests[request].end(); ++it) @@ -3387,6 +3399,62 @@ void vsid::VSIDPlugin::OnGetTagItem(EuroScopePlugIn::CFlightPlan FlightPlan, Eur } } } + + + + if (ItemCode == TAG_ITEM_VSID_SQW) + { + if (this->activeAirports.contains(adep)) + { + if (auto it = std::find(this->squawkQueue.begin(), this->squawkQueue.end(), callsign); it != this->squawkQueue.end()) + { + *pRGB = RGB(255, 255, 255); + + if (it == this->squawkQueue.begin()) strcpy_s(sItemString, 16, "NEXT"); + else strcpy_s(sItemString, 16, "STBY"); + + return; // prevent displaying of old squawk until new is set + } + } + + std::string setSquawk = FlightPlan.GetFPTrackPosition().GetSquawk(); + std::string assignedSquawk = FlightPlan.GetControllerAssignedData().GetSquawk(); + + if (setSquawk != assignedSquawk) + { + *pColorCode = EuroScopePlugIn::TAG_COLOR_RGB_DEFINED; + *pRGB = this->configParser.getColor("squawkNotSet"); + } + else + { + if (this->configParser.getColor("squawkSet") == RGB(300, 300, 300)) + { + if (FlightPlan.GetState() == EuroScopePlugIn::FLIGHT_PLAN_STATE_NON_CONCERNED) + { + *pColorCode = EuroScopePlugIn::TAG_COLOR_NON_CONCERNED; + } + else if (FlightPlan.GetState() == EuroScopePlugIn::FLIGHT_PLAN_STATE_NOTIFIED) + { + *pColorCode = EuroScopePlugIn::TAG_COLOR_NOTIFIED; + } + else if (FlightPlan.GetState() == EuroScopePlugIn::FLIGHT_PLAN_STATE_ASSUMED) + { + *pColorCode = EuroScopePlugIn::TAG_COLOR_ASSUMED; + } + else + { + *pColorCode = EuroScopePlugIn::TAG_COLOR_DEFAULT; + } + } + else + { + *pColorCode = EuroScopePlugIn::TAG_COLOR_RGB_DEFINED; + *pRGB = this->configParser.getColor("squawkSet"); + } + } + + if (assignedSquawk != "0000" && assignedSquawk != "1234") strcpy_s(sItemString, 16, assignedSquawk.c_str()); + } } bool vsid::VSIDPlugin::OnCompileCommand(const char* sCommandLine) @@ -4197,7 +4265,7 @@ void vsid::VSIDPlugin::OnFlightPlanFlightPlanDataUpdate(EuroScopePlugIn::CFlight { if (!messageHandler->getFplnErrors(callsign).contains(ERROR_FPLN_AMEND)) { - messageHandler->writeMessage("ERROR", "[" + callsign + "] - Failed to amend flight plan! - #FFDU"); + messageHandler->writeMessage("ERROR", "[" + callsign + "] - Failed to amend flight plan! Code: " + ERROR_FPLN_AMEND); messageHandler->addFplnError(callsign, ERROR_FPLN_AMEND); } @@ -4229,19 +4297,22 @@ void vsid::VSIDPlugin::OnFlightPlanFlightPlanDataUpdate(EuroScopePlugIn::CFlight // update possible rwy requests - if (this->processed[callsign].request != "") + if (!this->processed[callsign].request.empty()) { - std::string fplnRwy = fplnData.GetDepartureRwy(); + std::string fplnRwy = vsid::fplnhelper::getAtcBlock(FlightPlan).second; // update rwy requests directly if a rwy request is stored for the flight plan - if (this->processed[callsign].request.find("rwy") != std::string::npos && fplnRwy != "") + if (this->processed[callsign].request.find("rwy") != std::string::npos && + !fplnRwy.empty() && vsid::utils::contains(this->activeAirports[adep].allRwys, fplnRwy)) { - try + std::string normReq = vsid::utils::split(this->processed[callsign].request, ' ').at(1); + + if (this->activeAirports[adep].rwyrequests.contains(normReq)) { bool stop = false; - for (auto& [rwy, rwyReq] : this->activeAirports[adep].rwyrequests.at(this->processed[callsign].request)) + for (auto& [rwy, rwyReq] : this->activeAirports[adep].rwyrequests[normReq]) { for (std::set>::iterator it = rwyReq.begin(); it != rwyReq.end();) { @@ -4253,7 +4324,7 @@ void vsid::VSIDPlugin::OnFlightPlanFlightPlanDataUpdate(EuroScopePlugIn::CFlight if (rwy != fplnRwy) { - this->activeAirports[adep].rwyrequests.at(this->processed[callsign].request)[fplnRwy].insert({ callsign, it->second }); + this->activeAirports[adep].rwyrequests[normReq][fplnRwy].insert({ callsign, it->second }); rwyReq.erase(it); stop = true; break; @@ -4262,15 +4333,14 @@ void vsid::VSIDPlugin::OnFlightPlanFlightPlanDataUpdate(EuroScopePlugIn::CFlight if (stop) break; } } - catch (std::out_of_range) {}; } // check if a rwy request is available for stored non-rwy request and update the rwy - else if(fplnRwy != "") + else if(!fplnRwy.empty() && vsid::utils::contains(this->activeAirports[adep].allRwys, fplnRwy)) { bool stop = false; for (auto& [type, rwys] : this->activeAirports[adep].rwyrequests) { - if (type.find(this->processed[callsign].request) == std::string::npos) continue; + if (this->processed[callsign].request.find(type) == std::string::npos) continue; for (auto& [rwy, rwyReq] : rwys) { @@ -4284,7 +4354,7 @@ void vsid::VSIDPlugin::OnFlightPlanFlightPlanDataUpdate(EuroScopePlugIn::CFlight if (rwy != fplnRwy) { - this->activeAirports[adep].rwyrequests.at(type)[fplnRwy].insert({ callsign, it->second }); + this->activeAirports[adep].rwyrequests[type][fplnRwy].insert({ callsign, it->second }); rwyReq.erase(it); stop = true; break; @@ -4366,10 +4436,10 @@ void vsid::VSIDPlugin::OnFlightPlanControllerAssignedDataUpdate(EuroScopePlugIn: if (this->spReleased.contains(callsign)) this->updateSPSyncRelease(callsign); } - // set intersection - if (this->processed.contains(callsign) && scratchpad.size() > 0) { + // set intersection + if (scratchpad.size() <= 4 && this->activeAirports.contains(adep)) { if (size_t pos = scratchpad.find("+"); pos != std::string::npos) @@ -4451,6 +4521,19 @@ void vsid::VSIDPlugin::OnFlightPlanControllerAssignedDataUpdate(EuroScopePlugIn: { std::vector req = vsid::utils::split(scratchpad.substr(pos + toFind.size(), scratchpad.size()), '/'); std::string reqType = vsid::utils::tolower(req.at(0)); + bool isRwyReq = reqType.find("rwy") != std::string::npos; + if (isRwyReq) + { + try + { + reqType = vsid::utils::split(reqType, ' ').at(1); + } + catch (std::out_of_range&) + { + messageHandler->writeMessage("ERROR", "[" + callsign + "] failed to split req type (" + reqType + ") in scratch pad update. Stopping setting request!"); + return; + } + } long long reqTime = std::stoll(req.at(1)); // clear all possible requests before setting a new one @@ -4482,23 +4565,22 @@ void vsid::VSIDPlugin::OnFlightPlanControllerAssignedDataUpdate(EuroScopePlugIn: } if (it->first == reqType) { - messageHandler->writeMessage("DEBUG", "[" + callsign + "] setting in requests in: " + + messageHandler->writeMessage("DEBUG", "[" + callsign + "] (equal reqType) setting in requests in: " + it->first, vsid::MessageHandler::DebugArea::Req); it->second.insert({ callsign, reqTime }); - this->processed[callsign].request = reqType; - this->processed[callsign].reqTime = reqTime; - reqActive = true; - } - else if (reqType.find(it->first) != std::string::npos) - { - it->second.insert({ callsign, reqTime }); + + if (!isRwyReq) + { + this->processed[callsign].request = reqType; + this->processed[callsign].reqTime = reqTime; + reqActive = true; + } } } for (auto& [type, rwys] : this->activeAirports[adep].rwyrequests) { - messageHandler->writeMessage("DEBUG", "[" + callsign + "] rwy req type : " + type, vsid::MessageHandler::DebugArea::Dev); for (auto it = rwys.begin(); it != rwys.end(); ++it) { for (std::set>::iterator jt = it->second.begin(); jt != it->second.end();) @@ -4521,21 +4603,21 @@ void vsid::VSIDPlugin::OnFlightPlanControllerAssignedDataUpdate(EuroScopePlugIn: jt = it->second.erase(jt); } } - if (type == reqType && fplnRwy != "") + if (type == reqType && !fplnRwy.empty()) { messageHandler->writeMessage("DEBUG", "[" + callsign + "] setting in rwy requests in: " + type + "/" + std::string(FlightPlan.GetFlightPlanData().GetDepartureRwy()), vsid::MessageHandler::DebugArea::Req); rwys[fplnRwy].insert({ callsign, reqTime }); - this->processed[callsign].request = reqType; - this->processed[callsign].reqTime = reqTime; - reqActive = true; - } - else if (type.find(reqType) != std::string::npos && fplnRwy != "") // #evaluate - if still needed - { - rwys[fplnRwy].insert({ callsign, reqTime }); + + if (isRwyReq) + { + this->processed[callsign].request = "rwy " + reqType; + this->processed[callsign].reqTime = reqTime; + reqActive = true; + } } - else if (type == reqType && fplnRwy == "") + else if (isRwyReq && type == reqType && fplnRwy.empty()) messageHandler->writeMessage("WARNING", "[" + callsign + "] to be set in runway requests, but runway hasn't been set in the flight plan."); } } @@ -4642,14 +4724,18 @@ void vsid::VSIDPlugin::OnFlightPlanControllerAssignedDataUpdate(EuroScopePlugIn: break; } - for (auto& fp : this->activeAirports[adep].requests["rwy startup"]) + for (auto& [rwy, rwyReq] : this->activeAirports[adep].rwyrequests["startup"]) { - if (fp.first != callsign) continue; + for (auto& fp : rwyReq) + { + if (fp.first != callsign) continue; - this->activeAirports[adep].requests["rwy startup"].erase(fp); - this->processed[callsign].request = ""; - this->processed[callsign].reqTime = -1; - break; + this->activeAirports[adep].rwyrequests["startup"][rwy].erase(fp); + this->processed[callsign].request = ""; + this->processed[callsign].reqTime = -1; + break; + } + } } else if (state == "PUSH") @@ -5059,6 +5145,13 @@ void vsid::VSIDPlugin::UpdateActiveAirports() this->configParser.loadAirportConfig(this->activeAirports, this->savedRules, this->savedSettings, this->savedAreas, this->savedRequests, this->savedRwyRequests); messageHandler->writeMessage("DEBUG", "Checking .ese file for SID mastering...", vsid::MessageHandler::DebugArea::Conf); + //************************************ + // temp storage for OID mismatches + // Parameter: > - map of OID to whether it has been matched with a section SID or not + //************************************ + std::map> incompOIDs; + // if there are configured airports check for remaining sid data for(auto §ionSid : this->sectionSids) @@ -5069,7 +5162,12 @@ void vsid::VSIDPlugin::UpdateActiveAirports() { if (sid.base != sectionSid.base) { - // skip unmatching first two char comparison (filter) + + // if OID is skipped due to unmatching bases mark it has incompatible to yield warnings + if (vsid::utils::containsDigit(sid.base) && !incompOIDs[sectionSid.apt].contains(sid.base + sid.number + sid.designator)) + incompOIDs[sectionSid.apt][sid.base + sid.number + sid.designator] = false; + + // skip unmatching first three char comparison (filter) if (sid.base.length() > 2 && sectionSid.base.length() > 2) { if (sid.base[0] != sectionSid.base[0]) continue; @@ -5109,14 +5207,32 @@ void vsid::VSIDPlugin::UpdateActiveAirports() vsid::MessageHandler::DebugArea::Conf); } } - else if (!sid.number.empty() && sid.number == std::string(1, sectionSid.number)) // #dev - debugging only for MIL / OIDs - { - messageHandler->writeMessage("DEBUG", "[" + sid.base + sid.number + sid.designator + - "] matched with section SID [" + sectionSid.base + sectionSid.number + std::string(1, sectionSid.desig.value_or(' ')) + "]", - vsid::MessageHandler::DebugArea::Dev); - } - else if (sid.number != "") // health check for possible errors in .ese config + else if (!sid.number.empty()) // health check for possible errors in .ese config { + if (vsid::utils::containsDigit(sid.base)) + { + if(!incompOIDs[sectionSid.apt].contains(sid.base + sid.number + sid.designator)) + incompOIDs[sectionSid.apt][sid.base + sid.number + sid.designator] = false; + + if (vsid::utils::trim(sid.base + sid.number + sid.designator) == + vsid::utils::trim(sectionSid.base + sectionSid.number + sectionSid.desig.value_or(' '))) + { + messageHandler->writeMessage("DEBUG", "[MIL SID] " + + sid.base + sid.number + sid.designator + " equal: " + + sectionSid.base + sectionSid.number + sectionSid.desig.value_or(' '), vsid::MessageHandler::DebugArea::Conf); + + incompOIDs[sectionSid.apt][sid.base + sid.number + sid.designator] = true; + } + else + { + messageHandler->writeMessage("DEBUG", "[MIL SID] " + + sid.base + sid.number + sid.designator + " NOT equal: " + + sectionSid.base + sectionSid.number + sectionSid.desig.value_or(' '), vsid::MessageHandler::DebugArea::Dev); + } + + continue; + } + int currNumber = -1; try @@ -5139,7 +5255,7 @@ void vsid::VSIDPlugin::UpdateActiveAirports() if (currNumber > newNumber || (currNumber == 1 && newNumber == 9)) { - messageHandler->writeMessage("WARNING", "[" + sectionSid.apt + "] Check your .ese - file for " + sid.base + " ? " + sid.designator + " SID!Already set number : " + + messageHandler->writeMessage("WARNING", "[" + sectionSid.apt + "] Check your .ese - file for " + sid.base + "?" + sid.designator + " SID! Already set number : " + std::to_string(currNumber) + " (ID: " + sid.id + "). Now found additional number: " + std::to_string(newNumber) + " - (Runway: " + sectionSid.rwy + "). Skipping additional number (is lower or before restarting count) due to possible sectore file error!"); } @@ -5221,18 +5337,31 @@ void vsid::VSIDPlugin::UpdateActiveAirports() } } } + } + } - /*if (sid.designator != "") - { - - } - else + for (auto& [apt, oids] : incompOIDs) + { + if (oids.empty()) continue; + + std::ostringstream ss; + ss << "[" << apt << "] Check your config file for the following OIDs that couldn't be mastered: "; + std::string separator = ""; + int mismatchCount = 0; + + for (auto& [oid, matched] : oids) + { + if (!matched) { - if (sectionSid.base != sid.base) continue; - sid.number = 'X'; - messageHandler->writeMessage("DEBUG", "[" + sid.base + "] has no designator but the base could be mastered", vsid::MessageHandler::DebugArea::Conf); - }*/ + ss << separator << oid; + separator = ", "; + mismatchCount++; + } } + + if (mismatchCount > 0) messageHandler->writeMessage("WARNING", ss.str()); + + ss.clear(); } } diff --git a/vSIDPlugin.h b/vSIDPlugin.h index e7ba294..e9e89f5 100644 --- a/vSIDPlugin.h +++ b/vSIDPlugin.h @@ -46,7 +46,7 @@ along with this program. If not, see . namespace vsid { const std::string pluginName = "vSID"; - const std::string pluginVersion = "0.14.3.1"; + const std::string pluginVersion = "0.14.4"; const std::string pluginAuthor = "Gameagle"; const std::string pluginCopyright = "GPL v3"; const std::string pluginViewAviso = "";