├── .github └── ISSUE_TEMPLATE │ ├── add-airport-config.md │ ├── bug_report.md │ ├── feature_request.md │ └── fix-airport-config.md ├── .gitignore ├── DelHel.sln ├── DelHel ├── CDelHel.cpp ├── CDelHel.h ├── DelHel.cpp ├── DelHel.def ├── DelHel.h ├── DelHel.rc ├── DelHel.vcxproj ├── DelHel.vcxproj.filters ├── RadarScreen.cpp ├── RadarScreen.h ├── Resource.h ├── airport.h ├── constants.h ├── flightplan.cpp ├── flightplan.h ├── framework.h ├── helpers.cpp ├── helpers.h ├── pch.cpp ├── pch.h ├── res │ └── DelHel.rc2 ├── route_entry.cpp ├── route_entry.h ├── routing.cpp ├── routing.h ├── rwy_config.h ├── sid.h ├── targetver.h └── validation.h ├── LICENSE ├── README.md ├── airports.json ├── customconfigs.json ├── include ├── EuroScope │ └── EuroScopePlugIn.h ├── nlohmann │ └── json.hpp └── semver │ └── semver.hpp ├── lib └── EuroScope │ └── EuroScopePlugInDll.lib ├── routing.json └── version.txt /.github/ISSUE_TEMPLATE/add-airport-config.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Add airport config 3 | about: Add a new airport config entry 4 | title: '' 5 | labels: airport config, enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **ICAO code of airport to be added** 11 | What airport would you like to add to the default airport config? 12 | 13 | **Relevant procedures/documents** 14 | - List of links to procedures or documents regarding the airport config to be added 15 | 16 | **Other relevant information** 17 | Anything else you might be relevant. 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us fix issues 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior/taken shortly before the issue appeared: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. '.....' happened 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Logs** 27 | If applicable, add logs to help analyze your problem. 28 | 29 | **EuroScope (please complete the following information):** 30 | - Version: [e.g. 3.2.1.25] 31 | - [relevant settings] 32 | - ... 33 | 34 | **EuroScope plugins:** 35 | - DelHel version: [e.g. 0.0.1] 36 | - [relevant plugins] 37 | - ... 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this plugin 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/fix-airport-config.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Fix airport config 3 | about: Fix an incorrect existing airport config entry 4 | title: '' 5 | labels: airport config, bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **ICAO code of airport to be fixed** 11 | What airport would you like to report an issue for? 12 | 13 | **Incorrect data** 14 | - List of incorrect data for the airport config 15 | 16 | **Relevant procedures/documents** 17 | - List of links to procedures or documents regarding the airport config to be fixed 18 | 19 | **Other relevant information** 20 | Anything else you might be relevant. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # EuroScope files generated during debugging 7 | DelHel/euroscope_sector_providers.txt 8 | DelHel/ipaddr.txt 9 | DelHel/SectorFileProviderDescriptor.txt 10 | DelHel/version.txt 11 | 12 | # User-specific files 13 | *.rsuser 14 | *.suo 15 | *.user 16 | *.userosscache 17 | *.sln.docstates 18 | 19 | # User-specific files (MonoDevelop/Xamarin Studio) 20 | *.userprefs 21 | 22 | # Mono auto generated files 23 | mono_crash.* 24 | 25 | # Build results 26 | [Dd]ebug/ 27 | [Dd]ebugPublic/ 28 | [Rr]elease/ 29 | [Rr]eleases/ 30 | x64/ 31 | x86/ 32 | [Aa][Rr][Mm]/ 33 | [Aa][Rr][Mm]64/ 34 | bld/ 35 | [Bb]in/ 36 | [Oo]bj/ 37 | [Ll]og/ 38 | [Ll]ogs/ 39 | 40 | # Visual Studio 2015/2017 cache/options directory 41 | .vs/ 42 | # Uncomment if you have tasks that create the project's static files in wwwroot 43 | #wwwroot/ 44 | 45 | # Visual Studio 2017 auto generated files 46 | Generated\ Files/ 47 | 48 | # MSTest test Results 49 | [Tt]est[Rr]esult*/ 50 | [Bb]uild[Ll]og.* 51 | 52 | # NUnit 53 | *.VisualState.xml 54 | TestResult.xml 55 | nunit-*.xml 56 | 57 | # Build Results of an ATL Project 58 | [Dd]ebugPS/ 59 | [Rr]eleasePS/ 60 | dlldata.c 61 | 62 | # Benchmark Results 63 | BenchmarkDotNet.Artifacts/ 64 | 65 | # .NET Core 66 | project.lock.json 67 | project.fragment.lock.json 68 | artifacts/ 69 | 70 | # StyleCop 71 | StyleCopReport.xml 72 | 73 | # Files built by Visual Studio 74 | *_i.c 75 | *_p.c 76 | *_h.h 77 | *.ilk 78 | *.meta 79 | *.obj 80 | *.iobj 81 | *.pch 82 | *.pdb 83 | *.ipdb 84 | *.pgc 85 | *.pgd 86 | *.rsp 87 | *.sbr 88 | *.tlb 89 | *.tli 90 | *.tlh 91 | *.tmp 92 | *.tmp_proj 93 | *_wpftmp.csproj 94 | *.log 95 | *.vspscc 96 | *.vssscc 97 | .builds 98 | *.pidb 99 | *.svclog 100 | *.scc 101 | 102 | # Chutzpah Test files 103 | _Chutzpah* 104 | 105 | # Visual C++ cache files 106 | ipch/ 107 | *.aps 108 | *.ncb 109 | *.opendb 110 | *.opensdf 111 | *.sdf 112 | *.cachefile 113 | *.VC.db 114 | *.VC.VC.opendb 115 | 116 | # Visual Studio profiler 117 | *.psess 118 | *.vsp 119 | *.vspx 120 | *.sap 121 | 122 | # Visual Studio Trace Files 123 | *.e2e 124 | 125 | # TFS 2012 Local Workspace 126 | $tf/ 127 | 128 | # Guidance Automation Toolkit 129 | *.gpState 130 | 131 | # ReSharper is a .NET coding add-in 132 | _ReSharper*/ 133 | *.[Rr]e[Ss]harper 134 | *.DotSettings.user 135 | 136 | # TeamCity is a build add-in 137 | _TeamCity* 138 | 139 | # DotCover is a Code Coverage Tool 140 | *.dotCover 141 | 142 | # AxoCover is a Code Coverage Tool 143 | .axoCover/* 144 | !.axoCover/settings.json 145 | 146 | # Visual Studio code coverage results 147 | *.coverage 148 | *.coveragexml 149 | 150 | # NCrunch 151 | _NCrunch_* 152 | .*crunch*.local.xml 153 | nCrunchTemp_* 154 | 155 | # MightyMoose 156 | *.mm.* 157 | AutoTest.Net/ 158 | 159 | # Web workbench (sass) 160 | .sass-cache/ 161 | 162 | # Installshield output folder 163 | [Ee]xpress/ 164 | 165 | # DocProject is a documentation generator add-in 166 | DocProject/buildhelp/ 167 | DocProject/Help/*.HxT 168 | DocProject/Help/*.HxC 169 | DocProject/Help/*.hhc 170 | DocProject/Help/*.hhk 171 | DocProject/Help/*.hhp 172 | DocProject/Help/Html2 173 | DocProject/Help/html 174 | 175 | # Click-Once directory 176 | publish/ 177 | 178 | # Publish Web Output 179 | *.[Pp]ublish.xml 180 | *.azurePubxml 181 | # Note: Comment the next line if you want to checkin your web deploy settings, 182 | # but database connection strings (with potential passwords) will be unencrypted 183 | *.pubxml 184 | *.publishproj 185 | 186 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 187 | # checkin your Azure Web App publish settings, but sensitive information contained 188 | # in these scripts will be unencrypted 189 | PublishScripts/ 190 | 191 | # NuGet Packages 192 | *.nupkg 193 | # NuGet Symbol Packages 194 | *.snupkg 195 | # The packages folder can be ignored because of Package Restore 196 | **/[Pp]ackages/* 197 | # except build/, which is used as an MSBuild target. 198 | !**/[Pp]ackages/build/ 199 | # Uncomment if necessary however generally it will be regenerated when needed 200 | #!**/[Pp]ackages/repositories.config 201 | # NuGet v3's project.json files produces more ignorable files 202 | *.nuget.props 203 | *.nuget.targets 204 | 205 | # Microsoft Azure Build Output 206 | csx/ 207 | *.build.csdef 208 | 209 | # Microsoft Azure Emulator 210 | ecf/ 211 | rcf/ 212 | 213 | # Windows Store app package directories and files 214 | AppPackages/ 215 | BundleArtifacts/ 216 | Package.StoreAssociation.xml 217 | _pkginfo.txt 218 | *.appx 219 | *.appxbundle 220 | *.appxupload 221 | 222 | # Visual Studio cache files 223 | # files ending in .cache can be ignored 224 | *.[Cc]ache 225 | # but keep track of directories ending in .cache 226 | !?*.[Cc]ache/ 227 | 228 | # Others 229 | ClientBin/ 230 | ~$* 231 | *~ 232 | *.dbmdl 233 | *.dbproj.schemaview 234 | *.jfm 235 | *.pfx 236 | *.publishsettings 237 | orleans.codegen.cs 238 | 239 | # Including strong name files can present a security risk 240 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 241 | #*.snk 242 | 243 | # Since there are multiple workflows, uncomment next line to ignore bower_components 244 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 245 | #bower_components/ 246 | 247 | # RIA/Silverlight projects 248 | Generated_Code/ 249 | 250 | # Backup & report files from converting an old project file 251 | # to a newer Visual Studio version. Backup files are not needed, 252 | # because we have git ;-) 253 | _UpgradeReport_Files/ 254 | Backup*/ 255 | UpgradeLog*.XML 256 | UpgradeLog*.htm 257 | ServiceFabricBackup/ 258 | *.rptproj.bak 259 | 260 | # SQL Server files 261 | *.mdf 262 | *.ldf 263 | *.ndf 264 | 265 | # Business Intelligence projects 266 | *.rdl.data 267 | *.bim.layout 268 | *.bim_*.settings 269 | *.rptproj.rsuser 270 | *- [Bb]ackup.rdl 271 | *- [Bb]ackup ([0-9]).rdl 272 | *- [Bb]ackup ([0-9][0-9]).rdl 273 | 274 | # Microsoft Fakes 275 | FakesAssemblies/ 276 | 277 | # GhostDoc plugin setting file 278 | *.GhostDoc.xml 279 | 280 | # Node.js Tools for Visual Studio 281 | .ntvs_analysis.dat 282 | node_modules/ 283 | 284 | # Visual Studio 6 build log 285 | *.plg 286 | 287 | # Visual Studio 6 workspace options file 288 | *.opt 289 | 290 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 291 | *.vbw 292 | 293 | # Visual Studio LightSwitch build output 294 | **/*.HTMLClient/GeneratedArtifacts 295 | **/*.DesktopClient/GeneratedArtifacts 296 | **/*.DesktopClient/ModelManifest.xml 297 | **/*.Server/GeneratedArtifacts 298 | **/*.Server/ModelManifest.xml 299 | _Pvt_Extensions 300 | 301 | # Paket dependency manager 302 | .paket/paket.exe 303 | paket-files/ 304 | 305 | # FAKE - F# Make 306 | .fake/ 307 | 308 | # CodeRush personal settings 309 | .cr/personal 310 | 311 | # Python Tools for Visual Studio (PTVS) 312 | __pycache__/ 313 | *.pyc 314 | 315 | # Cake - Uncomment if you are using it 316 | # tools/** 317 | # !tools/packages.config 318 | 319 | # Tabs Studio 320 | *.tss 321 | 322 | # Telerik's JustMock configuration file 323 | *.jmconfig 324 | 325 | # BizTalk build output 326 | *.btp.cs 327 | *.btm.cs 328 | *.odx.cs 329 | *.xsd.cs 330 | 331 | # OpenCover UI analysis results 332 | OpenCover/ 333 | 334 | # Azure Stream Analytics local run output 335 | ASALocalRun/ 336 | 337 | # MSBuild Binary and Structured Log 338 | *.binlog 339 | 340 | # NVidia Nsight GPU debugger configuration file 341 | *.nvuser 342 | 343 | # MFractors (Xamarin productivity tool) working folder 344 | .mfractor/ 345 | 346 | # Local History for Visual Studio 347 | .localhistory/ 348 | 349 | # BeatPulse healthcheck temp database 350 | healthchecksdb 351 | 352 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 353 | MigrationBackup/ 354 | 355 | # Ionide (cross platform F# VS Code tools) working folder 356 | .ionide/ 357 | -------------------------------------------------------------------------------- /DelHel.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30204.135 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DelHel", "DelHel\DelHel.vcxproj", "{B66473EF-F312-4E6C-A5F4-64EAC53169AF}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {B66473EF-F312-4E6C-A5F4-64EAC53169AF}.Debug|x64.ActiveCfg = Debug|x64 17 | {B66473EF-F312-4E6C-A5F4-64EAC53169AF}.Debug|x64.Build.0 = Debug|x64 18 | {B66473EF-F312-4E6C-A5F4-64EAC53169AF}.Debug|x86.ActiveCfg = Debug|Win32 19 | {B66473EF-F312-4E6C-A5F4-64EAC53169AF}.Debug|x86.Build.0 = Debug|Win32 20 | {B66473EF-F312-4E6C-A5F4-64EAC53169AF}.Release|x64.ActiveCfg = Release|x64 21 | {B66473EF-F312-4E6C-A5F4-64EAC53169AF}.Release|x64.Build.0 = Release|x64 22 | {B66473EF-F312-4E6C-A5F4-64EAC53169AF}.Release|x86.ActiveCfg = Release|Win32 23 | {B66473EF-F312-4E6C-A5F4-64EAC53169AF}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {81D5458C-91F3-4960-956B-F674914F1FEF} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /DelHel/CDelHel.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "CDelHel.h" 4 | 5 | CDelHel* pPlugin; 6 | 7 | CDelHel::CDelHel() : EuroScopePlugIn::CPlugIn( 8 | EuroScopePlugIn::COMPATIBILITY_CODE, 9 | PLUGIN_NAME, 10 | PLUGIN_VERSION, 11 | PLUGIN_AUTHOR, 12 | PLUGIN_LICENSE 13 | ) 14 | { 15 | std::ostringstream msg; 16 | msg << "Version " << PLUGIN_VERSION << " loaded."; 17 | 18 | this->LogMessage(msg.str()); 19 | 20 | this->RegisterTagItemType("Flightplan Validation", TAG_ITEM_FP_VALIDATION); 21 | this->RegisterTagItemFunction("Validation menu", TAG_FUNC_VALIDATION_MENU); 22 | this->RegisterTagItemFunction("Process FPL", TAG_FUNC_PROCESS_FP); 23 | this->RegisterTagItemFunction("Process FPL (non-NAP)", TAG_FUNC_PROCESS_FP_NON_NAP); 24 | this->RegisterTagItemFunction("Process FPL (NAP)", TAG_FUNC_PROCESS_FP_NAP); 25 | 26 | this->RegisterDisplayType(PLUGIN_NAME, false, false, false, false); 27 | 28 | this->debug = false; 29 | this->updateCheck = false; 30 | this->assignNap = false; 31 | this->autoProcess = false; 32 | this->warnRFLBelowCFL = false; 33 | this->logMinMaxRFL = false; 34 | this->checkMinMaxRFL = false; 35 | this->flashOnMessage = false; 36 | this->topSkyAvailable = false; 37 | this->ccamsAvailable = false; 38 | this->preferTopSkySquawkAssignment = false; 39 | this->customRunwayConfig = ""; 40 | 41 | this->LoadSettings(); 42 | this->CheckLoadedPlugins(); 43 | 44 | this->ReadAirportConfig(); 45 | this->ReadRoutingConfig(); 46 | this->ReadCustomConfigs(); 47 | 48 | if (this->updateCheck) { 49 | this->latestVersion = std::async(FetchLatestVersion); 50 | } 51 | } 52 | 53 | CDelHel::~CDelHel() 54 | { 55 | } 56 | 57 | bool CDelHel::OnCompileCommand(const char* sCommandLine) 58 | { 59 | std::vector args = split(sCommandLine); 60 | 61 | if (starts_with(args[0], ".delhel")) { 62 | if (args.size() == 1) { 63 | std::ostringstream msg; 64 | msg << "Version " << PLUGIN_VERSION << " loaded. Available commands: auto, debug, nap, reload, reset, update, rflblw, logminmaxrfl, minmaxrfl, flash, ccams, rwycfg"; 65 | 66 | this->LogMessage(msg.str()); 67 | 68 | return true; 69 | } 70 | 71 | if (args[1] == "debug") { 72 | if (this->debug) { 73 | this->LogMessage("Disabling debug mode", "Debug"); 74 | } 75 | else { 76 | this->LogMessage("Enabling debug mode", "Debug"); 77 | } 78 | 79 | this->debug = !this->debug; 80 | 81 | this->SaveSettings(); 82 | 83 | return true; 84 | } 85 | else if (args[1] == "update") { 86 | if (this->updateCheck) { 87 | this->LogMessage("Disabling update check", "Update"); 88 | } 89 | else { 90 | this->LogMessage("Enabling update check", "Update"); 91 | } 92 | 93 | this->updateCheck = !this->updateCheck; 94 | 95 | this->SaveSettings(); 96 | 97 | return true; 98 | } 99 | else if (args[1] == "nap") { 100 | if (this->assignNap) { 101 | this->LogMessage("No longer assigning NAP SIDs", "Config"); 102 | } 103 | else { 104 | this->LogMessage("Assigning NAP SIDs", "Config"); 105 | } 106 | 107 | this->assignNap = !this->assignNap; 108 | 109 | this->SaveSettings(); 110 | 111 | return true; 112 | } 113 | else if (args[1] == "auto") { 114 | if (this->autoProcess) { 115 | this->LogMessage("No longer automatically processing flightplans", "Config"); 116 | } 117 | else { 118 | this->LogMessage("Automatically processing flightplans", "Config"); 119 | } 120 | 121 | this->autoProcess = !this->autoProcess; 122 | 123 | return true; 124 | } 125 | else if (args[1] == "reload") { 126 | this->LogMessage("Reloading airport config", "Config"); 127 | 128 | this->airports.clear(); 129 | this->ReadAirportConfig(); 130 | this->ReadRoutingConfig(); 131 | 132 | return true; 133 | } 134 | else if (args[1] == "reset") { 135 | this->LogMessage("Resetting plugin state", "Config"); 136 | 137 | this->autoProcess = false; 138 | this->processed.clear(); 139 | this->airports.clear(); 140 | this->ReadAirportConfig(); 141 | this->ReadRoutingConfig(); 142 | 143 | return true; 144 | } 145 | else if (args[1] == "rflblw") { 146 | if (this->warnRFLBelowCFL) { 147 | this->LogMessage("No longer displaying warnings for RFLs below inital CFLs for SIDs", "Config"); 148 | } 149 | else { 150 | this->LogMessage("Displaying warnings for RFLs below inital CFLs for SIDs", "Config"); 151 | } 152 | 153 | this->warnRFLBelowCFL = !this->warnRFLBelowCFL; 154 | 155 | this->SaveSettings(); 156 | 157 | return true; 158 | } 159 | else if (args[1] == "logminmaxrfl") { 160 | if (this->logMinMaxRFL) { 161 | this->LogMessage("No longer logging min and max RFLs for predefined routings", "Config"); 162 | } 163 | else { 164 | this->LogMessage("Logging min and max RFLs for predefined routings", "Config"); 165 | } 166 | 167 | this->logMinMaxRFL = !this->logMinMaxRFL; 168 | 169 | this->SaveSettings(); 170 | 171 | return true; 172 | } 173 | else if (args[1] == "minmaxrfl") { 174 | if (this->checkMinMaxRFL) { 175 | this->LogMessage("No longer checking min and max RFLs for predefined routings", "Config"); 176 | } 177 | else { 178 | this->LogMessage("Checking min and max RFLs for predefined routings", "Config"); 179 | } 180 | 181 | this->checkMinMaxRFL = !this->checkMinMaxRFL; 182 | 183 | this->SaveSettings(); 184 | 185 | return true; 186 | } 187 | else if (args[1] == "flash") { 188 | if (this->flashOnMessage) { 189 | this->LogMessage("No longer flashing on DelHel message", "Config"); 190 | } 191 | else { 192 | this->LogMessage("Flashing on DelHel message", "Config"); 193 | } 194 | 195 | this->flashOnMessage = !this->flashOnMessage; 196 | 197 | this->SaveSettings(); 198 | 199 | return true; 200 | } 201 | else if (args[1] == "prefertopsky") { 202 | if (this->preferTopSkySquawkAssignment) { 203 | this->LogMessage("No longer preferring TopSky squawk assignment, will use CCAMS if loaded", "Config"); 204 | } 205 | else { 206 | this->LogMessage("Preferring TopSky squawk assignment, even if CCAMS is loaded", "Config"); 207 | } 208 | 209 | this->preferTopSkySquawkAssignment = !this->preferTopSkySquawkAssignment; 210 | 211 | this->SaveSettings(); 212 | this->CheckLoadedPlugins(); 213 | 214 | return true; 215 | } 216 | else if (args[1] == "rwycfg") 217 | { 218 | if (args.size() < 3) 219 | { 220 | std::ostringstream msg; 221 | msg << "Select custom runway config to apply. Currently active: " << (this->customRunwayConfig.empty() ? "none" : this->customRunwayConfig) << ". Available values : none(disables custom config)"; 222 | for (auto& [name, config] : this->runwayConfigs) 223 | { 224 | msg << ", " << name; 225 | } 226 | this->LogMessage(msg.str(), "Config"); 227 | return true; 228 | } 229 | 230 | std::string cfg = args[2]; 231 | to_upper(cfg); 232 | if (cfg == "NONE") 233 | { 234 | this->LogMessage("No longer using custom runway config", "Config"); 235 | this->customRunwayConfig = ""; 236 | 237 | return true; 238 | } 239 | 240 | auto config = this->runwayConfigs.find(cfg); 241 | if (config == this->runwayConfigs.end()) 242 | { 243 | this->LogMessage("Invalid custom runway config specified", "Config"); 244 | } 245 | else 246 | { 247 | this->LogMessage("Switched to custom runway config: " + cfg, "Config"); 248 | this->customRunwayConfig = cfg; 249 | } 250 | 251 | return true; 252 | } 253 | } 254 | 255 | return false; 256 | } 257 | 258 | void CDelHel::OnGetTagItem(EuroScopePlugIn::CFlightPlan FlightPlan, EuroScopePlugIn::CRadarTarget RadarTarget, int ItemCode, int TagData, char sItemString[16], int* pColorCode, COLORREF* pRGB, double* pFontSize) 259 | { 260 | if (!FlightPlan.IsValid()) { 261 | return; 262 | } 263 | 264 | switch (ItemCode) { 265 | case TAG_ITEM_FP_VALIDATION: 266 | validation res = this->ProcessFlightPlan(FlightPlan, this->assignNap, true); 267 | 268 | if (res.valid && std::find(this->processed.begin(), this->processed.end(), FlightPlan.GetCallsign()) != this->processed.end()) { 269 | if (res.tag.empty()) { 270 | strcpy_s(sItemString, 16, "OK"); 271 | } 272 | else 273 | { 274 | strcpy_s(sItemString, 16, res.tag.c_str()); 275 | } 276 | 277 | *pColorCode = EuroScopePlugIn::TAG_COLOR_RGB_DEFINED; 278 | 279 | if (res.color == TAG_COLOR_NONE) { 280 | *pRGB = TAG_COLOR_GREEN; 281 | } 282 | else { 283 | *pRGB = res.color; 284 | } 285 | } 286 | else 287 | { 288 | strcpy_s(sItemString, 16, res.tag.c_str()); 289 | 290 | if (res.color != TAG_COLOR_NONE) { 291 | *pColorCode = EuroScopePlugIn::TAG_COLOR_RGB_DEFINED; 292 | *pRGB = res.color; 293 | } 294 | } 295 | 296 | break; 297 | } 298 | } 299 | 300 | void CDelHel::OnFunctionCall(int FunctionId, const char* sItemString, POINT Pt, RECT Area) 301 | { 302 | EuroScopePlugIn::CFlightPlan fp = this->FlightPlanSelectASEL(); 303 | if (!fp.IsValid()) { 304 | return; 305 | } 306 | 307 | switch (FunctionId) { 308 | case TAG_FUNC_VALIDATION_MENU: 309 | this->OpenPopupList(Area, "Validation", 1); 310 | this->AddPopupListElement("Process FPL (NAP)", NULL, TAG_FUNC_PROCESS_FP_NAP, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); 311 | this->AddPopupListElement("Process FPL (non-NAP)", NULL, TAG_FUNC_PROCESS_FP_NON_NAP, false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false, false); 312 | break; 313 | case TAG_FUNC_PROCESS_FP: 314 | this->ProcessFlightPlan(fp, this->assignNap); 315 | break; 316 | case TAG_FUNC_PROCESS_FP_NAP: 317 | this->ProcessFlightPlan(fp, true); 318 | break; 319 | case TAG_FUNC_PROCESS_FP_NON_NAP: 320 | this->ProcessFlightPlan(fp, false); 321 | } 322 | } 323 | 324 | void CDelHel::OnTimer(int Counter) 325 | { 326 | if (this->updateCheck && this->latestVersion.valid() && this->latestVersion.wait_for(0ms) == std::future_status::ready) { 327 | this->CheckForUpdate(); 328 | } 329 | if (this->autoProcess && Counter % 5 == 0) { 330 | this->AutoProcessFlightPlans(); 331 | } 332 | } 333 | 334 | void CDelHel::OnFlightPlanDisconnect(EuroScopePlugIn::CFlightPlan FlightPlan) 335 | { 336 | this->processed.erase(std::remove(this->processed.begin(), this->processed.end(), FlightPlan.GetCallsign()), this->processed.end()); 337 | } 338 | 339 | void CDelHel::OnAirportRunwayActivityChanged() 340 | { 341 | this->UpdateActiveAirports(); 342 | } 343 | 344 | EuroScopePlugIn::CRadarScreen* CDelHel::OnRadarScreenCreated(const char* sDisplayName, bool NeedRadarContent, bool GeoReferenced, bool CanBeSaved, bool CanBeCreated) 345 | { 346 | this->radarScreen = new RadarScreen(); 347 | return this->radarScreen; 348 | } 349 | 350 | void CDelHel::LoadSettings() 351 | { 352 | const char* settings = this->GetDataFromSettings(PLUGIN_NAME); 353 | if (settings) { 354 | std::vector splitSettings = split(settings, SETTINGS_DELIMITER); 355 | 356 | if (splitSettings.size() < 8) { 357 | this->LogMessage("Invalid saved settings found, reverting to default."); 358 | 359 | this->SaveSettings(); 360 | 361 | return; 362 | } 363 | 364 | std::istringstream(splitSettings[0]) >> this->debug; 365 | std::istringstream(splitSettings[1]) >> this->updateCheck; 366 | std::istringstream(splitSettings[2]) >> this->assignNap; 367 | std::istringstream(splitSettings[3]) >> this->warnRFLBelowCFL; 368 | std::istringstream(splitSettings[4]) >> this->logMinMaxRFL; 369 | std::istringstream(splitSettings[5]) >> this->checkMinMaxRFL; 370 | std::istringstream(splitSettings[6]) >> this->flashOnMessage; 371 | std::istringstream(splitSettings[7]) >> this->preferTopSkySquawkAssignment; 372 | 373 | this->LogDebugMessage("Successfully loaded settings."); 374 | } 375 | else { 376 | this->LogMessage("No saved settings found, using defaults."); 377 | } 378 | } 379 | 380 | void CDelHel::SaveSettings() 381 | { 382 | std::ostringstream ss; 383 | ss << this->debug << SETTINGS_DELIMITER 384 | << this->updateCheck << SETTINGS_DELIMITER 385 | << this->assignNap << SETTINGS_DELIMITER 386 | << this->warnRFLBelowCFL << SETTINGS_DELIMITER 387 | << this->logMinMaxRFL << SETTINGS_DELIMITER 388 | << this->checkMinMaxRFL << SETTINGS_DELIMITER 389 | << this->flashOnMessage << SETTINGS_DELIMITER 390 | << this->preferTopSkySquawkAssignment; 391 | 392 | this->SaveDataToSettings(PLUGIN_NAME, "DelHel settings", ss.str().c_str()); 393 | } 394 | 395 | void CDelHel::ReadCustomConfigs() 396 | { 397 | json j; 398 | 399 | try { 400 | std::filesystem::path base2(GetPluginDirectory()); 401 | base2.append("customconfigs.json"); 402 | 403 | // Custom runway config file is optional, skip reading if it doesn't exist 404 | if (!std::filesystem::exists(base2)) 405 | { 406 | this->LogDebugMessage("No custom runway config file found, skipping loading.", "Config"); 407 | return; 408 | } 409 | 410 | std::ifstream ifs(base2.c_str()); 411 | 412 | j = json::parse(ifs); 413 | } 414 | catch (std::exception e) 415 | { 416 | this->LogMessage("Failed to read custom runway configs json. Error: " + std::string(e.what()), "Config"); 417 | return; 418 | } 419 | 420 | for (auto& [configName, jconfig] : j.items()) 421 | { 422 | std::string configUpper = configName; 423 | to_upper(configUpper); 424 | std::string def = jconfig.value("def", ""); 425 | if (def == "") 426 | { 427 | this->LogMessage("Missing default runway for \"" + configUpper + "\".", "Config"); 428 | continue; 429 | } 430 | rwy_config c{ 431 | def 432 | }; 433 | 434 | json sids; 435 | try 436 | { 437 | sids = jconfig.at("sids"); 438 | } 439 | catch (std::exception e) 440 | { 441 | this->LogMessage("Failed to get SIDs for runway config \"" + configUpper + "\". Error: " + std::string(e.what()), "Config"); 442 | continue; 443 | } 444 | 445 | for (auto& [wp, jwp] : sids.items()) 446 | { 447 | rwy_config_sid cs{ 448 | wp 449 | }; 450 | 451 | json rwys; 452 | try 453 | { 454 | rwys = jwp.at("rwys"); 455 | } 456 | catch (std::exception e) 457 | { 458 | this->LogMessage("Failed to get RWYs for WP \"" + wp + "\" in \"" + configUpper + "\". Error: " + std::string(e.what()), "Config"); 459 | continue; 460 | } 461 | 462 | for (auto& [rwy, jrwy] : rwys.items()) 463 | { 464 | int prio = jrwy.value("prio", 0); 465 | cs.rwyPrio.emplace(rwy, prio); 466 | } 467 | 468 | c.sids.emplace(wp, cs); 469 | } 470 | 471 | this->runwayConfigs.emplace(configUpper, c); 472 | } 473 | 474 | this->LogDebugMessage("Loaded " + std::to_string(this->runwayConfigs.size()) + " custom runway config(s).", "Config"); 475 | } 476 | 477 | void CDelHel::ReadRoutingConfig() 478 | { 479 | json j; 480 | 481 | try { 482 | std::filesystem::path base2(GetPluginDirectory()); 483 | base2.append("routing.json"); 484 | 485 | std::ifstream ifs(base2.c_str()); 486 | 487 | j = json::parse(ifs); 488 | } 489 | catch (std::exception e) 490 | { 491 | this->LogMessage("Failed to read routing config. Error: " + std::string(e.what()), "Config"); 492 | return; 493 | } 494 | 495 | for (auto itair = this->airports.begin(); itair != this->airports.end(); itair++) { 496 | 497 | for (auto& obj : j.items()) { //iterator on outer json object -> key = departure icao 498 | if (obj.key() == itair->second.icao) { //if departure icao has already been read in by AirportConfig 499 | 500 | try { 501 | for (auto& el : obj.value()["entry"].items()) { //iterator on routes items 502 | 503 | for (auto& in_el : el.value()["routes"].items()) { 504 | 505 | routing ro{ 506 | obj.key(), 507 | in_el.value()["icao"], 508 | in_el.value()["maxlvl"], 509 | in_el.value()["minlvl"], 510 | {} 511 | }; 512 | 513 | ro.waypts.push_back(el.value()["name"]); // add entry-point = SID exit as first waypoint of route 514 | 515 | for (auto& inner_el : in_el.value()["waypoints"].items()) { // add route waypoints 516 | ro.waypts.push_back(inner_el.value()); 517 | } 518 | 519 | itair->second.validroutes.push_back(ro); //add routing to airports 520 | 521 | std::string check = "ADEP:" + ro.adep + "-ADEST:" + ro.adest + "-MAX:" + std::to_string(ro.maxlvl) + "-MIN:" + std::to_string(ro.minlvl) + "-#wpts:" + std::to_string(ro.waypts.size()); 522 | this->LogDebugMessage("New Routing added: " + check, "Config"); 523 | } 524 | } 525 | } 526 | catch (std::exception e) { 527 | this->LogMessage("Failed to read routing config for " + itair->second.icao + "| Error: " + std::string(e.what()), "Config"); 528 | return; 529 | } 530 | this->LogDebugMessage("Routing for departure Airport " + itair->second.icao + " has been added.", "Config"); 531 | } 532 | 533 | } 534 | } 535 | } 536 | 537 | void CDelHel::ReadAirportConfig() 538 | { 539 | json j; 540 | try { 541 | std::filesystem::path base(GetPluginDirectory()); 542 | base.append("airports.json"); 543 | 544 | std::ifstream ifs(base.c_str()); 545 | 546 | j = json::parse(ifs); 547 | } 548 | catch (std::exception e) 549 | { 550 | this->LogMessage("Failed to read airport config. Error: " + std::string(e.what()), "Config"); 551 | return; 552 | } 553 | 554 | for (auto& [icao, jap] : j.items()) { 555 | airport ap{ 556 | icao, // icao 557 | jap.value("elevation", 0), // elevation 558 | false, // active 559 | }; 560 | 561 | json jss; 562 | try { 563 | jss = jap.at("sids"); 564 | } 565 | catch (std::exception e) 566 | { 567 | this->LogMessage("Failed to get SIDs for airport \"" + icao + "\". Error: " + std::string(e.what()), "Config"); 568 | continue; 569 | } 570 | 571 | for (auto& [wp, js] : jss.items()) { 572 | sid s{ 573 | wp, // wp 574 | js.value("cfl", 0) // cfl 575 | }; 576 | 577 | json jrwys; 578 | try { 579 | jrwys = js.at("rwys"); 580 | } 581 | catch (std::exception e) 582 | { 583 | this->LogMessage("Failed to get RWYs for SID \"" + wp + "\" for airport \"" + icao + "\". Error: " + std::string(e.what()), "Config"); 584 | continue; 585 | } 586 | 587 | std::ostringstream rrs; 588 | rrs << icao << "\\/("; 589 | for (auto it = jrwys.items().begin(); it != jrwys.items().end(); ++it) { 590 | sidinfo si{ 591 | it.key(), // rwy 592 | it.value().value("dep", ""), // dep 593 | it.value().value("nap", ""), // nap 594 | it.value().value("prio", 0) // prio 595 | }; 596 | 597 | s.rwys.emplace(si.rwy, si); 598 | ap.rwys.emplace(si.rwy, false); 599 | 600 | rrs << si.rwy; 601 | if (std::next(it) != jrwys.items().end()) { 602 | rrs << '|'; 603 | } 604 | } 605 | rrs << ')'; 606 | 607 | ap.rwy_regex = std::regex(rrs.str(), std::regex_constants::ECMAScript); 608 | 609 | ap.sids.emplace(wp, s); 610 | } 611 | 612 | this->airports.emplace(icao, ap); 613 | } 614 | 615 | this->UpdateActiveAirports(); 616 | } 617 | 618 | void CDelHel::UpdateActiveAirports() 619 | { 620 | this->SelectActiveSectorfile(); 621 | for (auto sfe = this->SectorFileElementSelectFirst(EuroScopePlugIn::SECTOR_ELEMENT_RUNWAY); sfe.IsValid(); sfe = this->SectorFileElementSelectNext(sfe, EuroScopePlugIn::SECTOR_ELEMENT_RUNWAY)) { 622 | std::string ap = trim(sfe.GetAirportName()); 623 | to_upper(ap); 624 | 625 | auto ait = this->airports.find(ap); 626 | if (ait == this->airports.end()) { 627 | continue; 628 | } 629 | 630 | std::string rwy = trim(sfe.GetRunwayName(0)); 631 | to_upper(rwy); 632 | 633 | auto rit = ait->second.rwys.find(rwy); 634 | if (rit != ait->second.rwys.end()) { 635 | rit->second = sfe.IsElementActive(true, 0); 636 | } 637 | 638 | rwy = trim(sfe.GetRunwayName(1)); 639 | to_upper(rwy); 640 | 641 | rit = ait->second.rwys.find(rwy); 642 | if (rit != ait->second.rwys.end()) { 643 | rit->second = sfe.IsElementActive(true, 1); 644 | } 645 | 646 | if (!ait->second.active) { 647 | ait->second.active = sfe.IsElementActive(true, 0) || sfe.IsElementActive(true, 1); 648 | } 649 | } 650 | } 651 | 652 | 653 | validation CDelHel::ProcessFlightPlan(EuroScopePlugIn::CFlightPlan& fp, bool nap, bool validateOnly) 654 | { 655 | validation res{ 656 | true, // valid 657 | "", // tag 658 | TAG_COLOR_NONE // color 659 | }; 660 | std::string cs = fp.GetCallsign(); 661 | 662 | if (!validateOnly) { 663 | if (nap) { 664 | this->LogDebugMessage("Processing flightplan using noise abatement procedures", cs); 665 | } 666 | else { 667 | this->LogDebugMessage("Processing flightplan", cs); 668 | } 669 | } 670 | 671 | EuroScopePlugIn::CFlightPlanData fpd = fp.GetFlightPlanData(); 672 | 673 | std::string dep = fpd.GetOrigin(); 674 | to_upper(dep); 675 | 676 | std::string arr = fpd.GetDestination(); 677 | to_upper(arr); 678 | 679 | auto ait = this->airports.find(dep); 680 | if (ait == this->airports.end()) { 681 | if (!validateOnly) { 682 | this->LogDebugMessage("Failed to process flightplan, did not find departure airport \"" + dep + "\" in airport config", cs); 683 | } 684 | 685 | res.valid = false; 686 | res.tag = "ADEP"; 687 | res.color = TAG_COLOR_RED; 688 | 689 | return res; 690 | } 691 | 692 | airport ap = ait->second; 693 | EuroScopePlugIn::CFlightPlanControllerAssignedData cad = fp.GetControllerAssignedData(); 694 | 695 | if (strcmp(fpd.GetPlanType(), "V") == 0 || strcmp(fpd.GetPlanType(), "Z") == 0) { 696 | if (!validateOnly) { 697 | if (!cad.SetClearedAltitude(round_to_closest(ap.elevation + VFR_TRAFFIC_PATTERN_ALTITUDE, 500))) { 698 | this->LogMessage("Failed to process VFR flightplan, cannot set cleared flightlevel", cs); 699 | return res; 700 | } 701 | 702 | if (this->radarScreen != nullptr && this->ccamsAvailable && !this->preferTopSkySquawkAssignment) { 703 | this->radarScreen->StartTagFunction(cs.c_str(), nullptr, 0, cs.c_str(), CCAMS_PLUGIN_NAME, CCAMS_TAG_FUNC_ASSIGN_SQUAWK_VFR, POINT(), RECT()); 704 | this->LogDebugMessage("Triggered automatic VFR squawk assignment via CCAMS", cs); 705 | } 706 | else { 707 | // TopSky doesn't have a dedicated VFR squawk assignment function. Force hardcoded VFR squawk assignment 708 | if (!cad.SetSquawk(VFR_SQUAWK)) { 709 | this->LogDebugMessage("Failed to set VFR squawk", cs); 710 | } 711 | } 712 | 713 | this->LogDebugMessage("Skipping processing of VFR flightplan route", cs); 714 | 715 | // Add to list of processed flightplans if not added by auto-processing already 716 | this->IsFlightPlanProcessed(fp); 717 | } 718 | 719 | res.tag = "VFR"; 720 | 721 | return res; 722 | } 723 | 724 | std::vector route = split(fpd.GetRoute()); 725 | sid sid; 726 | 727 | auto rit = route.begin(); 728 | while (rit != route.end()) { 729 | if (std::regex_search(*rit, ap.rwy_regex)) { 730 | ++rit; 731 | res.tag = "RWY"; 732 | continue; 733 | } 734 | 735 | std::map::iterator sit; 736 | std::smatch m; 737 | if (std::regex_search(*rit, m, REGEX_SPEED_LEVEL_GROUP)) { 738 | // Try to match waypoint of speed/level group in case SID fix already has one assigned 739 | sit = ap.sids.find(m[1]); 740 | } 741 | else { 742 | // If no other matchers above yield a result, try to match full route part 743 | sit = ap.sids.find(*rit); 744 | } 745 | 746 | if (sit != ap.sids.end()) { 747 | sid = sit->second; 748 | break; 749 | } 750 | 751 | rit = route.erase(rit); 752 | } 753 | 754 | if (sid.wp == "" || route.size() == 0) { 755 | if (!validateOnly) { 756 | this->LogMessage("Invalid flightplan, no valid SID waypoint found in route", cs); 757 | } 758 | 759 | res.valid = false; 760 | res.tag = "SID"; 761 | res.color = TAG_COLOR_RED; 762 | 763 | return res; 764 | } 765 | 766 | if (validateOnly) { 767 | if (this->warnRFLBelowCFL && fp.GetFinalAltitude() < sid.cfl) { 768 | res.tag = "RFL"; 769 | res.color = TAG_COLOR_ORANGE; 770 | 771 | return res; 772 | } 773 | 774 | int cfl = cad.GetClearedAltitude(); 775 | // If CFL == RFL, EuroScope returns a CFL of 0 and the RFL value should be consulted. Additionally, CFL 1 and 2 indicate ILS and visual approach clearances respectively. 776 | if (cfl < 3) { 777 | // If the RFL is not adapted or confirmed by the controller, cad.GetFinalAltitude() will also return 0. As a last source of CFL info, we need to consider the filed RFL. 778 | cfl = cad.GetFinalAltitude(); 779 | if (cfl < 3) { 780 | cfl = fp.GetFinalAltitude(); 781 | } 782 | } 783 | 784 | // Display a warning if the CFL does not match the initial CFL assigned to the SID. No warning is shown if the RFL is below the CFL for the SID as pilots might request a lower initial climb. 785 | if (cfl != sid.cfl && (cfl != fp.GetFinalAltitude() || fp.GetFinalAltitude() >= sid.cfl)) { 786 | res.valid = false; 787 | res.tag = "CFL"; 788 | 789 | return res; 790 | } 791 | } 792 | else { 793 | if (!fpd.SetRoute(join(route).c_str())) { 794 | this->LogMessage("Failed to process flightplan, cannot set cleaned route", cs); 795 | return res; 796 | } 797 | 798 | if (!fpd.AmendFlightPlan()) { 799 | this->LogMessage("Failed to process flightplan, cannot amend flightplan after setting cleaned route", cs); 800 | return res; 801 | } 802 | 803 | std::map::iterator sit{}; 804 | std::string rwy = fpd.GetDepartureRwy(); 805 | if (rwy == "" || !this->customRunwayConfig.empty()) { 806 | this->LogDebugMessage("No runway assigned, or override active, attempting to pick first active runway for SID", cs); 807 | 808 | // SIDs can have a priority assigned per runway, allowing for "hierarchy" depending on runway config (as currently possible in ES sectorfiles). 809 | // If no priority is assigned, the default of 0 will be used and the first active runway will be picked. 810 | int prio = -1; 811 | for (auto [r, active] : ap.rwys) { 812 | if (active) { 813 | this->LogDebugMessage("Checking active runway " + r, cs); 814 | auto s = sid.rwys.find(r); 815 | if (s != sid.rwys.end()) { 816 | if (!this->customRunwayConfig.empty()) 817 | { 818 | this->LogDebugMessage("Custom config " + this->customRunwayConfig + " active, check for custom config of SID wp: " + sid.wp, cs); 819 | if (!this->runwayConfigs[this->customRunwayConfig].sids.empty()) { 820 | auto customSid = this->runwayConfigs[this->customRunwayConfig].sids.find(sid.wp); 821 | if (customSid != this->runwayConfigs[this->customRunwayConfig].sids.end()) 822 | { 823 | this->LogDebugMessage("Found SID wp " + customSid->first, cs); 824 | if (!customSid->second.rwyPrio.empty()) { 825 | auto customRwy = customSid->second.rwyPrio.find(r); 826 | if (customRwy != customSid->second.rwyPrio.end()) 827 | { 828 | this->LogDebugMessage("Found SID rwy " + customRwy->first, cs); 829 | if (customRwy->second > prio) 830 | { 831 | this->LogDebugMessage("Found and applied custom RWY priority override for config " + this->customRunwayConfig, cs); 832 | rwy = r; 833 | prio = customRwy->second; 834 | } 835 | } 836 | } 837 | } 838 | } 839 | 840 | if (this->runwayConfigs[this->customRunwayConfig].def == r && 1 > prio) 841 | { 842 | this->LogDebugMessage("Found and applied custom default-RWY priority override for config " + this->customRunwayConfig, cs); 843 | rwy = r; 844 | prio = 1; 845 | } 846 | } 847 | else if (rwy == "" && s->second.prio > prio) { 848 | rwy = r; 849 | prio = sit->second.prio; 850 | } 851 | } 852 | } 853 | } 854 | 855 | if (rwy == "") { 856 | this->LogMessage("Failed to process flightplan, no runway assigned", cs); 857 | 858 | res.valid = false; 859 | res.tag = "RWY"; 860 | res.color = TAG_COLOR_RED; 861 | 862 | return res; 863 | } 864 | 865 | // TODO display warning once "valid" tag override below is fixed 866 | /*res.tag = "SID"; 867 | res.color = TAG_COLOR_GREEN;*/ 868 | } 869 | 870 | sit = sid.rwys.find(rwy); 871 | 872 | if (sit == sid.rwys.end()) { 873 | this->LogMessage("Invalid flightplan, no matching SID found for runway", cs); 874 | 875 | res.valid = false; 876 | res.tag = "SID"; 877 | res.color = TAG_COLOR_RED; 878 | 879 | return res; 880 | } 881 | 882 | sidinfo sidinfo = sit->second; 883 | 884 | std::ostringstream sssid; 885 | if (nap && sidinfo.nap != "") { 886 | this->LogDebugMessage("--> Assigned sid/rwy: " + sidinfo.nap + "/" + rwy, cs); 887 | sssid << sidinfo.nap; 888 | } 889 | else { 890 | this->LogDebugMessage("--> Assigned sid/rwy: " + sidinfo.dep + "/" + rwy, cs); 891 | sssid << sidinfo.dep; 892 | } 893 | sssid << "/" << rwy; 894 | 895 | 896 | route.insert(route.begin(), sssid.str()); 897 | 898 | if (!fpd.SetRoute(join(route).c_str())) { 899 | this->LogMessage("Failed to process flightplan, cannot set route including SID", cs); 900 | return res; 901 | } 902 | 903 | if (!fpd.AmendFlightPlan()) { 904 | this->LogMessage("Failed to process flightplan, cannot amend flightplan after setting route including SID", cs); 905 | return res; 906 | } 907 | 908 | int cfl = sid.cfl; 909 | if (fp.GetFinalAltitude() < sid.cfl) { 910 | this->LogDebugMessage("Flightplan has RFL below initial CFL for SID, setting RFL", cs); 911 | 912 | cfl = fp.GetFinalAltitude(); 913 | } 914 | 915 | if (!cad.SetClearedAltitude(cfl)) { 916 | this->LogMessage("Failed to process flightplan, cannot set cleared flightlevel", cs); 917 | return res; 918 | } 919 | 920 | if (this->radarScreen == nullptr) { 921 | this->LogDebugMessage("Radar screen not initialised, cannot trigger automatic squawk assignment via TopSky or CCAMS", cs); 922 | } 923 | else { 924 | if (this->preferTopSkySquawkAssignment && this->topSkyAvailable) { 925 | this->radarScreen->StartTagFunction(cs.c_str(), nullptr, 0, cs.c_str(), TOPSKY_PLUGIN_NAME, TOPSKY_TAG_FUNC_ASSIGN_SQUAWK, POINT(), RECT()); 926 | this->LogDebugMessage("Triggered automatic squawk assignment via TopSky", cs); 927 | } 928 | else if (this->ccamsAvailable) { 929 | this->radarScreen->StartTagFunction(cs.c_str(), nullptr, 0, cs.c_str(), CCAMS_PLUGIN_NAME, CCAMS_TAG_FUNC_ASSIGN_SQUAWK_AUTO, POINT(), RECT()); 930 | this->LogDebugMessage("Triggered automatic squawk assignment via CCAMS", cs); 931 | } 932 | else if (this->topSkyAvailable) { 933 | this->radarScreen->StartTagFunction(cs.c_str(), nullptr, 0, cs.c_str(), TOPSKY_PLUGIN_NAME, TOPSKY_TAG_FUNC_ASSIGN_SQUAWK, POINT(), RECT()); 934 | this->LogDebugMessage("Triggered automatic squawk assignment via TopSky", cs); 935 | } 936 | else { 937 | this->LogDebugMessage("Neither TopSky nor CCAMS are loaded, cannot trigger automatic squawk assignment", cs); 938 | } 939 | } 940 | 941 | this->LogDebugMessage("Successfully processed flightplan", cs); 942 | 943 | // Add to list of processed flightplans if not added by auto-processing already 944 | this->IsFlightPlanProcessed(fp); 945 | } 946 | 947 | 948 | if (ap.validroutes.size() != 0) { 949 | 950 | flightplan fpl = flightplan(fp.GetCallsign(), fp.GetExtractedRoute(), fpd.GetRoute()); // create fp for route validation 951 | 952 | bool routecheck = false; 953 | int count = 0; 954 | for (auto vait = ap.validroutes.begin(); vait != ap.validroutes.end(); ++vait) { 955 | 956 | routecheck = false; 957 | auto selsidit = vait->waypts.begin(); 958 | 959 | if (*selsidit == sid.wp) { 960 | if (vait->waypts.size() > 1) { 961 | try { 962 | 963 | 964 | count = 0; //counter to disregard previous found waypoints in fpl 965 | for (auto wyprouit = vait->waypts.begin(); wyprouit != vait->waypts.end(); ++wyprouit) { 966 | for (auto wypfpl = fpl.route.begin() + count; wypfpl != fpl.route.end(); ++wypfpl) { 967 | 968 | if (wypfpl->airway && wypfpl->name.rfind(*wyprouit) == 0) { // check if waypoint name is part of the airway (e.g. SID) 969 | 970 | routecheck = true; 971 | ++count; 972 | break; 973 | } 974 | if (*wyprouit == wypfpl->name) { 975 | routecheck = true; 976 | ++count; 977 | break; 978 | } 979 | else { 980 | routecheck = false; 981 | } 982 | ++count; 983 | } 984 | if (!routecheck) { 985 | break; 986 | } 987 | 988 | } 989 | } 990 | catch (std::exception e) { 991 | this->LogDebugMessage("Error, No Routing", cs); 992 | } 993 | } 994 | else { 995 | routecheck = true; 996 | } 997 | if (routecheck && vait->adest == arr) { //check specified destinations like LOWI, LOWS, etc. 998 | 999 | if (this->checkMinMaxRFL && ((cad.GetFinalAltitude() == 0 && fpd.GetFinalAltitude() > vait->maxlvl * 100) || cad.GetFinalAltitude() > vait->maxlvl * 100)) { 1000 | 1001 | res.valid = false; 1002 | res.tag = "MAX"; 1003 | res.color = TAG_COLOR_ORANGE; 1004 | 1005 | if (!validateOnly) { 1006 | std::ostringstream msg; 1007 | msg << "Flights from " << dep << " to " << arr << " via " << sid.wp << " have a maximum FL of " << vait->maxlvl; 1008 | 1009 | if (this->logMinMaxRFL) { 1010 | this->LogMessage(msg.str(), cs); 1011 | } 1012 | else { 1013 | this->LogDebugMessage(msg.str(), cs); 1014 | } 1015 | } 1016 | 1017 | return res; 1018 | } 1019 | if (this->checkMinMaxRFL && ((cad.GetFinalAltitude() == 0 && fpd.GetFinalAltitude() < vait->minlvl * 100) || (cad.GetFinalAltitude() != 0 && cad.GetFinalAltitude() < vait->minlvl * 100))) { 1020 | 1021 | res.valid = false; 1022 | res.tag = "MIN"; 1023 | res.color = TAG_COLOR_ORANGE; 1024 | 1025 | if (!validateOnly) { 1026 | std::ostringstream msg; 1027 | msg << "Flights from " << dep << " to " << arr << " via " << sid.wp << " have a minimum FL of " << vait->minlvl; 1028 | 1029 | if (this->logMinMaxRFL) { 1030 | this->LogMessage(msg.str(), cs); 1031 | } 1032 | else { 1033 | this->LogDebugMessage(msg.str(), cs); 1034 | } 1035 | } 1036 | 1037 | return res; 1038 | } 1039 | 1040 | //case all correct 1041 | res.valid = true; 1042 | res.tag = ""; 1043 | res.color = TAG_COLOR_NONE; 1044 | 1045 | return res; 1046 | 1047 | } 1048 | else if (routecheck && vait->adest != arr && vait->adest == "") { // check for non specified destinations 1049 | if (this->checkMinMaxRFL && ((cad.GetFinalAltitude() == 0 && fpd.GetFinalAltitude() > vait->maxlvl * 100) || cad.GetFinalAltitude() > vait->maxlvl * 100)) { 1050 | 1051 | res.valid = false; 1052 | res.tag = "MAX"; 1053 | res.color = TAG_COLOR_ORANGE; 1054 | 1055 | if (!validateOnly) { 1056 | std::ostringstream msg; 1057 | msg << "Flights from " << dep << " via " << sid.wp << " have a maximum FL of " << vait->maxlvl; 1058 | 1059 | if (this->logMinMaxRFL) { 1060 | this->LogMessage(msg.str(), cs); 1061 | } 1062 | else { 1063 | this->LogDebugMessage(msg.str(), cs); 1064 | } 1065 | } 1066 | 1067 | break; 1068 | } 1069 | if (this->checkMinMaxRFL && ((cad.GetFinalAltitude() == 0 && fpd.GetFinalAltitude() < vait->minlvl * 100) || (cad.GetFinalAltitude() != 0 && cad.GetFinalAltitude() < vait->minlvl * 100))) { 1070 | 1071 | res.valid = false; 1072 | res.tag = "MIN"; 1073 | res.color = TAG_COLOR_ORANGE; 1074 | 1075 | if (!validateOnly) { 1076 | std::ostringstream msg; 1077 | msg << "Flights from " << dep << " via " << sid.wp << " have a minimum FL of " << vait->minlvl; 1078 | 1079 | if (this->logMinMaxRFL) { 1080 | this->LogMessage(msg.str(), cs); 1081 | } 1082 | else { 1083 | this->LogDebugMessage(msg.str(), cs); 1084 | } 1085 | } 1086 | 1087 | break; 1088 | } 1089 | 1090 | //case all correct 1091 | res.valid = true; 1092 | res.tag = ""; 1093 | res.color = TAG_COLOR_NONE; 1094 | 1095 | return res; 1096 | 1097 | } 1098 | else if (this->CheckFlightPlanProcessed(fp)) { 1099 | res.valid = false; 1100 | res.tag = "INV"; 1101 | res.color = TAG_COLOR_ORANGE; 1102 | 1103 | continue; 1104 | } 1105 | else { 1106 | res.valid = false; 1107 | res.tag = ""; 1108 | res.color = TAG_COLOR_NONE; 1109 | continue; 1110 | } 1111 | } 1112 | } 1113 | } 1114 | return res; 1115 | } 1116 | 1117 | bool CDelHel::CheckFlightPlanProcessed(const EuroScopePlugIn::CFlightPlan& fp) 1118 | { 1119 | std::string cs = fp.GetCallsign(); 1120 | 1121 | if (std::find(this->processed.begin(), this->processed.end(), cs) != this->processed.end()) { 1122 | return true; 1123 | } 1124 | return false; 1125 | } 1126 | 1127 | bool CDelHel::IsFlightPlanProcessed(const EuroScopePlugIn::CFlightPlan& fp) 1128 | { 1129 | std::string cs = fp.GetCallsign(); 1130 | 1131 | if (std::find(this->processed.begin(), this->processed.end(), cs) != this->processed.end()) { 1132 | return true; 1133 | } 1134 | 1135 | this->processed.push_back(cs); 1136 | return false; 1137 | } 1138 | 1139 | void CDelHel::AutoProcessFlightPlans() 1140 | { 1141 | for (EuroScopePlugIn::CRadarTarget rt = this->RadarTargetSelectFirst(); rt.IsValid(); rt = this->RadarTargetSelectNext(rt)) { 1142 | EuroScopePlugIn::CRadarTargetPositionData pos = rt.GetPosition(); 1143 | // Skip auto-processing if aircraft is not on the ground (currently using flightlevel threshold) 1144 | // TODO better option for finding aircraft on ground 1145 | if (!pos.IsValid() || pos.GetFlightLevel() > AUTO_ASSIGN_MIN_FL) { 1146 | continue; 1147 | } 1148 | 1149 | EuroScopePlugIn::CFlightPlan fp = rt.GetCorrelatedFlightPlan(); 1150 | // Skip auto-processing if aircraft is tracked (with exception of aircraft tracked by current controller) 1151 | if (!fp.IsValid() || (strcmp(fp.GetTrackingControllerId(), "") != 0 && !fp.GetTrackingControllerIsMe())) { 1152 | continue; 1153 | } 1154 | 1155 | std::string dep = fp.GetFlightPlanData().GetOrigin(); 1156 | to_upper(dep); 1157 | 1158 | std::string arr = fp.GetFlightPlanData().GetDestination(); 1159 | to_upper(arr); 1160 | 1161 | // Skip auto-processing for aircraft without a valid flightplan (no departure/destination airport) 1162 | if (dep == "" || arr == "") { 1163 | continue; 1164 | } 1165 | 1166 | auto ait = this->airports.find(dep); 1167 | // Skip auto-processing of departures not available in the airport config 1168 | if (ait == this->airports.end()) { 1169 | continue; 1170 | } 1171 | // Skip auto-processing of airports currently not set as active in EuroScope 1172 | if (!ait->second.active) { 1173 | continue; 1174 | } 1175 | 1176 | if (this->IsFlightPlanProcessed(fp)) { 1177 | continue; 1178 | } 1179 | 1180 | this->ProcessFlightPlan(fp, this->assignNap); 1181 | } 1182 | } 1183 | 1184 | void CDelHel::LogMessage(std::string message) 1185 | { 1186 | this->DisplayUserMessage("Message", PLUGIN_NAME, message.c_str(), true, true, true, false, false); 1187 | } 1188 | 1189 | void CDelHel::LogMessage(std::string message, std::string type) 1190 | { 1191 | this->DisplayUserMessage(PLUGIN_NAME, type.c_str(), message.c_str(), true, true, true, this->flashOnMessage, false); 1192 | } 1193 | 1194 | void CDelHel::LogDebugMessage(std::string message) 1195 | { 1196 | if (this->debug) { 1197 | this->LogMessage(message); 1198 | } 1199 | } 1200 | 1201 | void CDelHel::LogDebugMessage(std::string message, std::string type) 1202 | { 1203 | if (this->debug) { 1204 | this->LogMessage(message, type); 1205 | } 1206 | } 1207 | 1208 | void CDelHel::CheckForUpdate() 1209 | { 1210 | try 1211 | { 1212 | semver::version latest{ this->latestVersion.get() }; 1213 | semver::version current{ PLUGIN_VERSION }; 1214 | 1215 | if (latest > current) { 1216 | std::ostringstream ss; 1217 | ss << "A new version (" << latest << ") of " << PLUGIN_NAME << " is available, download it at " << PLUGIN_LATEST_DOWNLOAD_URL; 1218 | 1219 | this->LogMessage(ss.str(), "Update"); 1220 | } 1221 | } 1222 | catch (std::exception& e) 1223 | { 1224 | MessageBox(NULL, e.what(), PLUGIN_NAME, MB_OK | MB_ICONERROR); 1225 | } 1226 | 1227 | this->latestVersion = std::future(); 1228 | } 1229 | 1230 | void CDelHel::CheckLoadedPlugins() 1231 | { 1232 | this->topSkyAvailable = false; 1233 | this->ccamsAvailable = false; 1234 | 1235 | HMODULE hMods[1024]; 1236 | HANDLE hProcess; 1237 | DWORD cbNeeded; 1238 | unsigned int i; 1239 | 1240 | hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId()); 1241 | if (hProcess == NULL) { 1242 | this->LogDebugMessage("Failed to check loaded plugins"); 1243 | return; 1244 | } 1245 | 1246 | if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) { 1247 | for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) { 1248 | TCHAR szModName[MAX_PATH]; 1249 | if (GetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR))) { 1250 | std::string moduleName = szModName; 1251 | size_t pos = moduleName.find_last_of("\\"); 1252 | if (pos != std::string::npos) { 1253 | moduleName = moduleName.substr(pos + 1); 1254 | } 1255 | 1256 | if (moduleName == TOPSKY_DLL_NAME) { 1257 | this->topSkyAvailable = true; 1258 | this->LogDebugMessage("Found TopSky plugin", "Config"); 1259 | } 1260 | else if (moduleName == CCAMS_DLL_NAME) { 1261 | this->ccamsAvailable = true; 1262 | this->LogDebugMessage("Found CCAMS plugin", "Config"); 1263 | } 1264 | } 1265 | } 1266 | } 1267 | 1268 | CloseHandle(hProcess); 1269 | } 1270 | 1271 | void __declspec (dllexport) EuroScopePlugInInit(EuroScopePlugIn::CPlugIn** ppPlugInInstance) 1272 | { 1273 | *ppPlugInInstance = pPlugin = new CDelHel(); 1274 | } 1275 | 1276 | void __declspec (dllexport) EuroScopePlugInExit(void) 1277 | { 1278 | delete pPlugin; 1279 | } -------------------------------------------------------------------------------- /DelHel/CDelHel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "EuroScope/EuroScopePlugIn.h" 14 | #include "semver/semver.hpp" 15 | #include "nlohmann/json.hpp" 16 | 17 | #include "constants.h" 18 | #include "helpers.h" 19 | #include "airport.h" 20 | #include "validation.h" 21 | #include "flightplan.h" 22 | #include "sid.h" 23 | #include "rwy_config.h" 24 | #include "RadarScreen.h" 25 | 26 | using json = nlohmann::json; 27 | using namespace std::chrono_literals; 28 | 29 | class CDelHel : public EuroScopePlugIn::CPlugIn 30 | { 31 | public: 32 | CDelHel(); 33 | virtual ~CDelHel(); 34 | 35 | bool OnCompileCommand(const char* sCommandLine); 36 | void OnGetTagItem(EuroScopePlugIn::CFlightPlan FlightPlan, EuroScopePlugIn::CRadarTarget RadarTarget, int ItemCode, int TagData, char sItemString[16], int* pColorCode, COLORREF* pRGB, double* pFontSize); 37 | void OnFunctionCall(int FunctionId, const char* sItemString, POINT Pt, RECT Area); 38 | void OnTimer(int Counter); 39 | void OnFlightPlanDisconnect(EuroScopePlugIn::CFlightPlan FlightPlan); 40 | void OnAirportRunwayActivityChanged(); 41 | EuroScopePlugIn::CRadarScreen* OnRadarScreenCreated(const char* sDisplayName, bool NeedRadarContent, bool GeoReferenced, bool CanBeSaved, bool CanBeCreated); 42 | 43 | private: 44 | bool debug; 45 | bool updateCheck; 46 | bool assignNap; 47 | bool autoProcess; 48 | bool warnRFLBelowCFL; 49 | bool logMinMaxRFL; 50 | bool checkMinMaxRFL; 51 | bool flashOnMessage; 52 | bool topSkyAvailable; 53 | bool ccamsAvailable; 54 | bool preferTopSkySquawkAssignment; 55 | bool customConfigActive; 56 | std::string customRunwayConfig; 57 | std::future latestVersion; 58 | std::map airports; 59 | std::vector processed; 60 | std::map runwayConfigs; 61 | RadarScreen* radarScreen; 62 | 63 | void LoadSettings(); 64 | void SaveSettings(); 65 | void ReadRoutingConfig(); 66 | void ReadAirportConfig(); 67 | void ReadCustomConfigs(); 68 | void UpdateActiveAirports(); 69 | 70 | validation ProcessFlightPlan(EuroScopePlugIn::CFlightPlan& fp, bool nap, bool validateOnly = false); 71 | bool CDelHel::CheckFlightPlanProcessed(const EuroScopePlugIn::CFlightPlan& fp); 72 | bool IsFlightPlanProcessed(const EuroScopePlugIn::CFlightPlan& fp); 73 | void AutoProcessFlightPlans(); 74 | 75 | void LogMessage(std::string message); 76 | void LogMessage(std::string message, std::string handler); 77 | void LogDebugMessage(std::string message); 78 | void LogDebugMessage(std::string message, std::string type); 79 | 80 | void CheckForUpdate(); 81 | void CheckLoadedPlugins(); 82 | }; 83 | 84 | -------------------------------------------------------------------------------- /DelHel/DelHel.cpp: -------------------------------------------------------------------------------- 1 | // DelHel.cpp : Defines the initialization routines for the DLL. 2 | // 3 | 4 | #include "pch.h" 5 | #include "framework.h" 6 | #include "DelHel.h" 7 | 8 | #ifdef _DEBUG 9 | #define new DEBUG_NEW 10 | #endif 11 | 12 | // 13 | //TODO: If this DLL is dynamically linked against the MFC DLLs, 14 | // any functions exported from this DLL which call into 15 | // MFC must have the AFX_MANAGE_STATE macro added at the 16 | // very beginning of the function. 17 | // 18 | // For example: 19 | // 20 | // extern "C" BOOL PASCAL EXPORT ExportedFunction() 21 | // { 22 | // AFX_MANAGE_STATE(AfxGetStaticModuleState()); 23 | // // normal function body here 24 | // } 25 | // 26 | // It is very important that this macro appear in each 27 | // function, prior to any calls into MFC. This means that 28 | // it must appear as the first statement within the 29 | // function, even before any object variable declarations 30 | // as their constructors may generate calls into the MFC 31 | // DLL. 32 | // 33 | // Please see MFC Technical Notes 33 and 58 for additional 34 | // details. 35 | // 36 | 37 | // CDelHelApp 38 | 39 | BEGIN_MESSAGE_MAP(CDelHelApp, CWinApp) 40 | END_MESSAGE_MAP() 41 | 42 | 43 | // CDelHelApp construction 44 | 45 | CDelHelApp::CDelHelApp() 46 | { 47 | // TODO: add construction code here, 48 | // Place all significant initialization in InitInstance 49 | } 50 | 51 | 52 | // The one and only CDelHelApp object 53 | 54 | CDelHelApp theApp; 55 | 56 | 57 | // CDelHelApp initialization 58 | 59 | BOOL CDelHelApp::InitInstance() 60 | { 61 | CWinApp::InitInstance(); 62 | 63 | return TRUE; 64 | } 65 | -------------------------------------------------------------------------------- /DelHel/DelHel.def: -------------------------------------------------------------------------------- 1 | ; DelHel.def : Declares the module parameters for the DLL. 2 | 3 | LIBRARY 4 | 5 | EXPORTS 6 | ; Explicit exports can go here 7 | -------------------------------------------------------------------------------- /DelHel/DelHel.h: -------------------------------------------------------------------------------- 1 | // DelHel.h : main header file for the DelHel DLL 2 | // 3 | 4 | #pragma once 5 | 6 | #ifndef __AFXWIN_H__ 7 | #error "include 'pch.h' before including this file for PCH" 8 | #endif 9 | 10 | #include "resource.h" // main symbols 11 | 12 | 13 | // CDelHelApp 14 | // See DelHel.cpp for the implementation of this class 15 | // 16 | 17 | class CDelHelApp : public CWinApp 18 | { 19 | public: 20 | CDelHelApp(); 21 | 22 | // Overrides 23 | public: 24 | virtual BOOL InitInstance(); 25 | 26 | DECLARE_MESSAGE_MAP() 27 | }; 28 | -------------------------------------------------------------------------------- /DelHel/DelHel.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorpheusXAUT/DelHel/7c574eedd1dbc14e04a888557aa4eaeb4f03bf13/DelHel/DelHel.rc -------------------------------------------------------------------------------- /DelHel/DelHel.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | {B66473EF-F312-4E6C-A5F4-64EAC53169AF} 24 | MFCDLLProj 25 | DelHel 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v143 33 | MultiByte 34 | Dynamic 35 | 36 | 37 | DynamicLibrary 38 | false 39 | v143 40 | MultiByte 41 | Dynamic 42 | 43 | 44 | DynamicLibrary 45 | true 46 | v143 47 | MultiByte 48 | Dynamic 49 | 50 | 51 | DynamicLibrary 52 | false 53 | v143 54 | true 55 | MultiByte 56 | Dynamic 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | true 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | false 87 | 88 | 89 | 90 | Use 91 | Level3 92 | true 93 | WIN32;_WINDOWS;_DEBUG;_USRDLL;%(PreprocessorDefinitions) 94 | pch.h 95 | $(SolutionDir)include;%(AdditionalIncludeDirectories) 96 | false 97 | stdcpp17 98 | 99 | 100 | Windows 101 | .\DelHel.def 102 | $(SolutionDir)lib\EuroScope;%(AdditionalLibraryDirectories) 103 | EuroScopePlugInDll.lib;%(AdditionalDependencies) 104 | 105 | 106 | false 107 | _DEBUG;%(PreprocessorDefinitions) 108 | 109 | 110 | 0x0009 111 | _DEBUG;%(PreprocessorDefinitions) 112 | $(IntDir);%(AdditionalIncludeDirectories) 113 | 114 | 115 | 116 | 117 | Use 118 | Level3 119 | true 120 | _WINDOWS;_DEBUG;_USRDLL;%(PreprocessorDefinitions) 121 | pch.h 122 | $(SolutionDir)include;%(AdditionalIncludeDirectories) 123 | false 124 | stdcpp17 125 | 126 | 127 | Windows 128 | .\DelHel.def 129 | $(SolutionDir)lib\EuroScope;%(AdditionalLibraryDirectories) 130 | EuroScopePlugInDll.lib;%(AdditionalDependencies) 131 | 132 | 133 | false 134 | _DEBUG;%(PreprocessorDefinitions) 135 | 136 | 137 | 0x0409 138 | _DEBUG;%(PreprocessorDefinitions) 139 | $(IntDir);%(AdditionalIncludeDirectories) 140 | 141 | 142 | 143 | 144 | Use 145 | Level3 146 | true 147 | true 148 | true 149 | WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) 150 | pch.h 151 | $(SolutionDir)include;%(AdditionalIncludeDirectories) 152 | false 153 | stdcpp17 154 | 155 | 156 | Windows 157 | true 158 | true 159 | .\DelHel.def 160 | $(SolutionDir)lib\EuroScope;%(AdditionalLibraryDirectories) 161 | EuroScopePlugInDll.lib;%(AdditionalDependencies) 162 | 163 | 164 | false 165 | NDEBUG;%(PreprocessorDefinitions) 166 | 167 | 168 | 0x0009 169 | NDEBUG;%(PreprocessorDefinitions) 170 | $(IntDir);%(AdditionalIncludeDirectories) 171 | 172 | 173 | 174 | 175 | Use 176 | Level3 177 | true 178 | true 179 | true 180 | _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) 181 | pch.h 182 | $(SolutionDir)include;%(AdditionalIncludeDirectories) 183 | false 184 | stdcpp17 185 | 186 | 187 | Windows 188 | true 189 | true 190 | .\DelHel.def 191 | $(SolutionDir)lib\EuroScope;%(AdditionalLibraryDirectories) 192 | EuroScopePlugInDll.lib;%(AdditionalDependencies) 193 | 194 | 195 | false 196 | NDEBUG;%(PreprocessorDefinitions) 197 | 198 | 199 | 0x0409 200 | NDEBUG;%(PreprocessorDefinitions) 201 | $(IntDir);%(AdditionalIncludeDirectories) 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | Create 211 | Create 212 | Create 213 | Create 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | Document 222 | 223 | 224 | Document 225 | 226 | 227 | Document 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /DelHel/DelHel.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | 44 | 45 | Source Files 46 | 47 | 48 | Resource Files 49 | 50 | 51 | 52 | 53 | Header Files 54 | 55 | 56 | Header Files 57 | 58 | 59 | Header Files 60 | 61 | 62 | Header Files 63 | 64 | 65 | Header Files 66 | 67 | 68 | Header Files 69 | 70 | 71 | Header Files 72 | 73 | 74 | Header Files 75 | 76 | 77 | Header Files 78 | 79 | 80 | Header Files 81 | 82 | 83 | Header Files 84 | 85 | 86 | Header Files 87 | 88 | 89 | Header Files 90 | 91 | 92 | Header Files 93 | 94 | 95 | Header Files 96 | 97 | 98 | Header Files 99 | 100 | 101 | 102 | 103 | Resource Files 104 | 105 | 106 | 107 | 108 | Resource Files 109 | 110 | 111 | 112 | 113 | Resource Files 114 | 115 | 116 | Resource Files 117 | 118 | 119 | Resource Files 120 | 121 | 122 | -------------------------------------------------------------------------------- /DelHel/RadarScreen.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "RadarScreen.h" 3 | 4 | RadarScreen::RadarScreen() : EuroScopePlugIn::CRadarScreen() 5 | { 6 | 7 | } 8 | 9 | RadarScreen::~RadarScreen() 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /DelHel/RadarScreen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EuroScope/EuroScopePlugIn.h" 4 | 5 | class RadarScreen : public EuroScopePlugIn::CRadarScreen 6 | { 7 | public: 8 | RadarScreen(); 9 | virtual ~RadarScreen(); 10 | 11 | inline void OnAsrContentToBeClosed() { delete this; } 12 | }; -------------------------------------------------------------------------------- /DelHel/Resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by DelHel.rc 4 | // 5 | 6 | // Next default values for new objects 7 | // 8 | #ifdef APSTUDIO_INVOKED 9 | #ifndef APSTUDIO_READONLY_SYMBOLS 10 | 11 | #define _APS_NEXT_RESOURCE_VALUE 1000 12 | #define _APS_NEXT_CONTROL_VALUE 1000 13 | #define _APS_NEXT_SYMED_VALUE 1000 14 | #define _APS_NEXT_COMMAND_VALUE 32771 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /DelHel/airport.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "sid.h" 9 | #include "routing.h" 10 | 11 | struct airport { 12 | std::string icao; 13 | int elevation; 14 | bool active; 15 | std::map sids; 16 | std::map rwys; 17 | std::vector validroutes; 18 | std::regex rwy_regex; 19 | }; -------------------------------------------------------------------------------- /DelHel/constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define PLUGIN_NAME "DelHel" 6 | #define PLUGIN_VERSION "0.3.0" 7 | #define PLUGIN_AUTHOR "Nick Mueller et al." 8 | #define PLUGIN_LICENSE "(c) 2020-2025, MIT License" 9 | #define PLUGIN_LATEST_VERSION_URL "https://raw.githubusercontent.com/MorpheusXAUT/DelHel/master/version.txt" 10 | #define PLUGIN_LATEST_DOWNLOAD_URL "https://github.com/MorpheusXAUT/DelHel/releases/latest" 11 | 12 | const char SETTINGS_DELIMITER = '|'; 13 | 14 | const int TAG_ITEM_FP_VALIDATION = 1; 15 | 16 | const int TAG_FUNC_VALIDATION_MENU = 100; 17 | const int TAG_FUNC_PROCESS_FP = 101; 18 | const int TAG_FUNC_PROCESS_FP_NAP = 102; 19 | const int TAG_FUNC_PROCESS_FP_NON_NAP = 103; 20 | 21 | constexpr auto TOPSKY_PLUGIN_NAME = "TopSky plugin"; 22 | constexpr auto TOPSKY_DLL_NAME = "TopSky.dll"; 23 | const int TOPSKY_TAG_FUNC_ASSIGN_SQUAWK = 667; 24 | 25 | constexpr auto CCAMS_PLUGIN_NAME = "CCAMS"; 26 | constexpr auto CCAMS_DLL_NAME = "CCAMS.dll"; 27 | const int CCAMS_TAG_FUNC_ASSIGN_SQUAWK_AUTO = 871; 28 | const int CCAMS_TAG_FUNC_ASSIGN_SQUAWK_VFR = 873; 29 | 30 | const COLORREF TAG_COLOR_NONE = 0; 31 | const COLORREF TAG_COLOR_RED = RGB(200, 0, 0); 32 | const COLORREF TAG_COLOR_ORANGE = RGB(255, 165, 0); 33 | const COLORREF TAG_COLOR_GREEN = RGB(0, 200, 0); 34 | 35 | const std::regex REGEX_SPEED_LEVEL_GROUP = std::regex("([\\w\\d]*)\\/((?:N|K)\\d{4})((?:F\\d{3})|(?:S\\d{4})|(?:A\\d{3})|(?:M\\d{4})|(?:VFR))", std::regex_constants::ECMAScript); 36 | 37 | const int AUTO_ASSIGN_MIN_FL = 5000; 38 | const int VFR_TRAFFIC_PATTERN_ALTITUDE = 1000; 39 | constexpr auto VFR_SQUAWK = "7000"; -------------------------------------------------------------------------------- /DelHel/flightplan.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "flightplan.h" 4 | 5 | flightplan::flightplan(std::string callsign, const EuroScopePlugIn::CFlightPlanExtractedRoute& route, std::string rawRoute) : callsign(callsign), direction(0.0), distance(0.0) 6 | { 7 | this->ParseRoute(route, rawRoute); 8 | } 9 | 10 | 11 | flightplan::flightplan(const char* callsign, const EuroScopePlugIn::CFlightPlanExtractedRoute& route, const char* rawRoute) : flightplan(std::string(callsign), route, std::string(rawRoute)) 12 | { 13 | } 14 | 15 | void flightplan::ParseRoute(const EuroScopePlugIn::CFlightPlanExtractedRoute& route, std::string rawRoute) 16 | { 17 | this->route.clear(); 18 | this->direction = 0.0; 19 | this->distance = 0.0; 20 | 21 | EuroScopePlugIn::CPosition lastPos; 22 | EuroScopePlugIn::CPosition rePos; 23 | std::string airway; 24 | std::vector wps; 25 | int numPoints = route.GetPointsNumber(); 26 | 27 | for (int i = 0; i < numPoints; i++) 28 | { 29 | std::string name = route.GetPointName(i); 30 | 31 | EuroScopePlugIn::CPosition pos = route.GetPointPosition(i); 32 | 33 | double dir = 0.0; 34 | double dist = 0.0; 35 | if (i > 0) { 36 | dir = pos.DirectionTo(lastPos); 37 | dist = pos.DistanceTo(lastPos); 38 | } 39 | 40 | lastPos = pos; 41 | 42 | std::string aw = route.GetPointAirwayName(i); 43 | if (aw.size() > 0) { 44 | if (airway.size() == 0) { 45 | airway = aw; 46 | rePos = route.GetPointPosition(i); 47 | } 48 | else if (airway != aw) { 49 | double reDir = 0.0; 50 | double reDist = 0.0; 51 | if (i > 0) { 52 | EuroScopePlugIn::CPosition p = route.GetPointPosition(i - 1); 53 | 54 | reDir = rePos.DirectionTo(p); 55 | reDist = rePos.DistanceTo(p); 56 | } 57 | 58 | route_entry re(airway, reDir, reDist); 59 | re.airway = true; 60 | re.waypoints = wps; 61 | 62 | this->route.push_back(re); 63 | 64 | airway = aw; 65 | rePos = route.GetPointPosition(i); 66 | wps.clear(); 67 | } 68 | 69 | wps.push_back(route_entry(name, dir, dist)); 70 | } 71 | else { 72 | if (airway.size() > 0) { 73 | double reDir = 0.0; 74 | double reDist = 0.0; 75 | if (i > 0) { 76 | EuroScopePlugIn::CPosition p = route.GetPointPosition(i - 1); 77 | 78 | reDir = rePos.DirectionTo(p); 79 | reDist = rePos.DistanceTo(p); 80 | } 81 | 82 | route_entry re(airway, reDir, reDist); 83 | re.airway = true; 84 | re.waypoints = wps; 85 | 86 | this->route.push_back(re); 87 | 88 | airway.clear(); 89 | wps.clear(); 90 | } 91 | 92 | this->route.push_back(route_entry(name, dir, dist)); 93 | } 94 | } 95 | 96 | if (numPoints >= 2) { 97 | this->direction = route.GetPointPosition(0).DirectionTo(route.GetPointPosition(numPoints - 1)); 98 | this->distance = route.GetPointPosition(0).DistanceTo(route.GetPointPosition(numPoints - 1)); 99 | } 100 | } 101 | 102 | std::ostream& operator<<(std::ostream& os, const flightplan& fp) 103 | { 104 | os << "Flight(" << fp.callsign << "): "; 105 | 106 | for (int i = 0; i < fp.route.size(); i++) { 107 | os << fp.route[i]; 108 | if (i < fp.route.size() - 1) { 109 | os << ";"; 110 | } 111 | } 112 | 113 | return os; 114 | } 115 | -------------------------------------------------------------------------------- /DelHel/flightplan.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "EuroScope/EuroScopePlugIn.h" 9 | 10 | #include "route_entry.h" 11 | 12 | class flightplan 13 | { 14 | public: 15 | std::string callsign; 16 | std::vector route; 17 | double direction; 18 | double distance; 19 | 20 | flightplan(std::string callsign, const EuroScopePlugIn::CFlightPlanExtractedRoute& route, std::string rawRoute); 21 | flightplan(const char* callsign, const EuroScopePlugIn::CFlightPlanExtractedRoute& route, const char* rawRoute); 22 | 23 | void ParseRoute(const EuroScopePlugIn::CFlightPlanExtractedRoute& route, std::string rawRoute); 24 | 25 | friend std::ostream& operator<<(std::ostream& os, const flightplan& fp); 26 | }; 27 | 28 | -------------------------------------------------------------------------------- /DelHel/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef VC_EXTRALEAN 4 | #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers 5 | #endif 6 | 7 | #include "targetver.h" 8 | 9 | #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit 10 | 11 | #include // MFC core and standard components 12 | #include // MFC extensions 13 | 14 | #ifndef _AFX_NO_OLE_SUPPORT 15 | #include // MFC OLE classes 16 | #include // MFC OLE dialog classes 17 | #include // MFC Automation classes 18 | #endif // _AFX_NO_OLE_SUPPORT 19 | 20 | #ifndef _AFX_NO_DB_SUPPORT 21 | #include // MFC ODBC database classes 22 | #endif // _AFX_NO_DB_SUPPORT 23 | 24 | #ifndef _AFX_NO_DAO_SUPPORT 25 | #include // MFC DAO database classes 26 | #endif // _AFX_NO_DAO_SUPPORT 27 | 28 | #ifndef _AFX_NO_OLE_SUPPORT 29 | #include // MFC support for Internet Explorer 4 Common Controls 30 | #endif 31 | #ifndef _AFX_NO_AFXCMN_SUPPORT 32 | #include // MFC support for Windows Common Controls 33 | #endif // _AFX_NO_AFXCMN_SUPPORT 34 | 35 | 36 | -------------------------------------------------------------------------------- /DelHel/helpers.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "helpers.h" 4 | 5 | std::string FetchLatestVersion() 6 | { 7 | std::ostringstream agent; 8 | agent << PLUGIN_NAME << '/' << PLUGIN_VERSION; 9 | 10 | HINTERNET init = InternetOpen(agent.str().c_str(), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); 11 | if (!init) { 12 | throw error{ "Connection failed. Error: " + std::to_string(GetLastError()) }; 13 | } 14 | 15 | HINTERNET open = InternetOpenUrl(init, PLUGIN_LATEST_VERSION_URL, NULL, 0, INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_RELOAD, 0); 16 | if (!open) { 17 | InternetCloseHandle(init); 18 | throw error{ "Failed to load URL. Error: " + std::to_string(GetLastError()) }; 19 | } 20 | 21 | char data[256]; 22 | DWORD read; 23 | std::string version; 24 | 25 | while (InternetReadFile(open, data, 256, &read) && read) { 26 | version.append(data, read); 27 | } 28 | 29 | InternetCloseHandle(init); 30 | InternetCloseHandle(open); 31 | 32 | return version; 33 | } -------------------------------------------------------------------------------- /DelHel/helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "constants.h" 10 | 11 | extern "C" IMAGE_DOS_HEADER __ImageBase; 12 | 13 | std::string FetchLatestVersion(); 14 | 15 | class delhelexception : public std::exception 16 | { 17 | public: 18 | explicit delhelexception(std::string& what) : std::exception{ what.c_str() } {} 19 | virtual inline const long icon() const = 0; 20 | inline void whatMessageBox() 21 | { 22 | MessageBox(NULL, this->what(), PLUGIN_NAME, MB_OK | icon()); 23 | } 24 | }; 25 | 26 | class error : public delhelexception 27 | { 28 | public: 29 | explicit error(std::string& what) : delhelexception{ what } {} 30 | inline const long icon() const 31 | { 32 | return MB_ICONERROR; 33 | } 34 | }; 35 | 36 | class warning : public delhelexception 37 | { 38 | public: 39 | explicit warning(std::string& what) : delhelexception{ what } {} 40 | inline const long icon() const 41 | { 42 | return MB_ICONWARNING; 43 | } 44 | }; 45 | 46 | class information : public delhelexception 47 | { 48 | public: 49 | explicit information(std::string& what) : delhelexception{ what } {} 50 | inline const long icon() const 51 | { 52 | return MB_ICONINFORMATION; 53 | } 54 | }; 55 | 56 | inline std::string GetPluginDirectory() 57 | { 58 | char buf[MAX_PATH] = { 0 }; 59 | GetModuleFileName(HINSTANCE(&__ImageBase), buf, MAX_PATH); 60 | 61 | std::string::size_type pos = std::string(buf).find_last_of("\\/"); 62 | 63 | return std::string(buf).substr(0, pos); 64 | } 65 | 66 | inline std::vector split(const std::string& s, char delim = ' ') 67 | { 68 | std::istringstream ss(s); 69 | std::string item; 70 | std::vector res; 71 | 72 | while (std::getline(ss, item, delim)) { 73 | res.push_back(item); 74 | } 75 | 76 | return res; 77 | } 78 | 79 | inline std::string join(const std::vector& s, const char delim = ' ') 80 | { 81 | std::ostringstream ss; 82 | std::copy(s.begin(), s.end(), std::ostream_iterator(ss, &delim)); 83 | return ss.str(); 84 | } 85 | 86 | inline bool starts_with(const std::string& str, const std::string& pre) 87 | { 88 | return str.rfind(pre, 0) == 0; 89 | } 90 | 91 | inline void to_upper(std::string& str) 92 | { 93 | std::transform(str.begin(), str.end(), str.begin(), 94 | [](unsigned char c) -> unsigned char { return std::toupper(c); }); 95 | } 96 | 97 | inline int round_to_closest(int num, int closest) 98 | { 99 | return ((num + closest / 2) / closest) * closest; 100 | } 101 | 102 | inline std::string trim(const std::string& str, const std::string& charset = " \t") 103 | { 104 | size_t begin = str.find_first_not_of(charset); 105 | if (begin == std::string::npos) { 106 | // Empty string or only characters from charset provided 107 | return ""; 108 | } 109 | 110 | size_t end = str.find_last_not_of(charset); 111 | 112 | return str.substr(begin, end - begin + 1); 113 | } -------------------------------------------------------------------------------- /DelHel/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /DelHel/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: This is a precompiled header file. 2 | // Files listed below are compiled only once, improving build performance for future builds. 3 | // This also affects IntelliSense performance, including code completion and many code browsing features. 4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds. 5 | // Do not add files here that you will be updating frequently as this negates the performance advantage. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // add headers that you want to pre-compile here 11 | #include "framework.h" 12 | 13 | #endif //PCH_H 14 | -------------------------------------------------------------------------------- /DelHel/res/DelHel.rc2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorpheusXAUT/DelHel/7c574eedd1dbc14e04a888557aa4eaeb4f03bf13/DelHel/res/DelHel.rc2 -------------------------------------------------------------------------------- /DelHel/route_entry.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "route_entry.h" 4 | 5 | route_entry::route_entry() : name(""), airway(false), direction(0.0), distance(0.0), rfl(0), speed(0) 6 | { 7 | } 8 | 9 | route_entry::route_entry(std::string name) : name(name), airway(false), direction(0.0), distance(0.0), rfl(0), speed(0) 10 | { 11 | } 12 | 13 | route_entry::route_entry(std::string name, double direction, double distance) : name(name), airway(false), direction(direction), distance(distance), rfl(0), speed(0) 14 | { 15 | } 16 | 17 | route_entry::route_entry(std::string name, int rfl, int speed) : name(name), airway(false), direction(0.0), distance(0.0), rfl(rfl), speed(speed) 18 | { 19 | } 20 | 21 | std::ostream& operator<<(std::ostream& os, const route_entry& re) 22 | { 23 | os << re.name; 24 | 25 | if (re.airway) { 26 | os << "[aw]"; 27 | } 28 | 29 | if (re.waypoints.size() > 0) { 30 | os << "("; 31 | for (int i = 0; i < re.waypoints.size(); i++) { 32 | os << re.waypoints[i]; 33 | if (i < re.waypoints.size() - 1) { 34 | os << ","; 35 | } 36 | } 37 | os << ")"; 38 | } 39 | 40 | return os; 41 | } 42 | -------------------------------------------------------------------------------- /DelHel/route_entry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class route_entry 8 | { 9 | public: 10 | std::string name; 11 | std::vector waypoints; 12 | bool airway; 13 | double direction; 14 | double distance; 15 | int rfl; 16 | int speed; 17 | 18 | route_entry(); 19 | route_entry(std::string name); 20 | route_entry(std::string name, double direction, double distance); 21 | route_entry(std::string name, int rfl, int speed); 22 | 23 | friend std::ostream& operator<<(std::ostream& os, const route_entry& re); 24 | }; 25 | 26 | -------------------------------------------------------------------------------- /DelHel/routing.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "routing.h" 4 | 5 | std::ostream& operator<<(std::ostream& os, const routing& rt) 6 | { 7 | os << "Routing(" << rt.adep; 8 | if (rt.adest.size() > 0) { 9 | os << "->" << rt.adest << ","; 10 | } 11 | else { 12 | os << ","; 13 | } 14 | 15 | os << rt.minlvl << " 4 | #include 5 | #include 6 | 7 | #include "route_entry.h" 8 | 9 | struct routing { 10 | std::string adep; 11 | std::string adest; 12 | int maxlvl{}; 13 | int minlvl{}; 14 | std::vector waypts; 15 | 16 | friend std::ostream& operator<<(std::ostream& os, const routing& rt); 17 | }; -------------------------------------------------------------------------------- /DelHel/rwy_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct rwy_config_sid 7 | { 8 | std::string wp; 9 | std::map rwyPrio; 10 | }; 11 | 12 | struct rwy_config 13 | { 14 | std::string def; 15 | std::map sids; 16 | }; -------------------------------------------------------------------------------- /DelHel/sid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct sidinfo { 7 | std::string rwy; 8 | std::string dep; 9 | std::string nap; 10 | int prio{}; 11 | }; 12 | 13 | struct sid { 14 | std::string wp; 15 | int cfl{}; 16 | std::map rwys; 17 | }; -------------------------------------------------------------------------------- /DelHel/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /DelHel/validation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct validation { 6 | bool valid; 7 | std::string tag; 8 | COLORREF color; 9 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Nick Müller 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DelHel 2 | 3 | `DelHel` helps VATSIM controllers by automating repetitive delivery duties in EuroScope. 4 | 5 | The plugin performs validation of FPLs (**f**light **pl**ans), sets the appropriate SID (**s**tandard **i**nstrument **d**eparture) depending on runway config and changes the cleared flight level to the initial value of the respective SID. If available, NAPs (**n**oise **a**batement **p**rocedures) will be respected during SID assignment. 6 | 7 | ## Table of Contents 8 | 9 | - [Getting started](#getting-started) 10 | - [Prerequisites](#prerequisites) 11 | - [Installation](#installation) 12 | - [Usage](#usage) 13 | - [Basics](#basics) 14 | - [Tag items](#tag-items) 15 | - [Tag functions](#tag-functions) 16 | - [Chat commands](#chat-commands) 17 | - [Airport config](#airport-config) 18 | - [Custom runway configs](#custom-runway-configs) 19 | - [Contributing](#contributing) 20 | - [Development setup](#development-setup) 21 | - [License](#license) 22 | 23 | ## Getting started 24 | 25 | ### Prerequisites 26 | 27 | Since `DelHel` was developed as an EuroScope plugin, it requires a working installation [EuroScope](https://euroscope.hu/). The initial development was started using EuroScope version [`v3.2.1.25`](https://www.euroscope.hu/wp/2020/06/28/v3-2-1-25/), although the plugin should most likely also work fine with previous and later versions. As development continues, compatibility to the latest **beta** versions of EuroScope will be maintained as long as possible and feasible. 28 | 29 | ### Installation 30 | 31 | 1. Download the latest release (`DelHel.zip`) of `DelHel` from the [**Releases**](https://github.com/MorpheusXAUT/DelHel/releases/latest) section of this repository 32 | 2. Extract `DelHel.dll` and `airports.json` and place them **in the same folder** (most likely somewhere inside your EuroScope sectorfile/profile setup, where other plugins are already set up) 33 | 3. Start EuroScope and open the **Plug-ins** dialog in the settings menu (**OTHER SET**) 34 | ![Plug-ins dialog](https://i.imgur.com/SrVtRp9.png) 35 | 4. **Load** the plugin by selecting the `DelHel.dll` you extracted and ensure the proper version is displayed 36 | ![Load plugin](https://i.imgur.com/y6koC4g.png) 37 | `DelHel` will also confirm successful initialisation by logging its version to the **Messages** chat: 38 | `[08:34:10] DelHel: Version 0.0.1 loaded.` 39 | 5. Close the plugin dialog and open the departure list columns setup dialog (small **S** at the left side of your departure list) 40 | ![Departure list columns setup dialog](https://i.imgur.com/MvFYkkh.png) 41 | 6. (_Optional_) Add the **Flightplan Validation** column to your departure list by clicking **Add Item** and selecting the `DelHel / Flightplan Validation` **Tag Item type**. Pick a **Header name** and set a **Width** of 4 or greater. This column will display warnings and the status of each flightplan processed by DelHel, but is not strictly required for the plugin to function 42 | 7. Assign the `DelHel / Process FPL` action as the **Left button** or **Right button** action of any of your tag items as desired. Triggering this function processes the selected flightplan using the default settings of `DelHel` (described in more detail in the [Process FPL](#process-fpl) section below) 43 | 8. (_Optional_) Assign the `DelHel / Validation menu` action as the **Left button** or **Right button** action of any of your tag items as desired. Triggering this function opens the flightplan validation menu, allowing for more fine-grained processing of the selected flightplan (described in more detail in the [Validation Menu](#validation-menu) section below) 44 | 9. Close the departure list settings by clicking **OK** 45 | 46 | ## Usage 47 | 48 | ### Basics 49 | 50 | By default, `DelHel` only performs some basic validations of flightplans, displaying warnings for potential issues found for each aircraft in your departure list via the [`Flightplan Validation`](#flightplan-validation) tag item. Additionally, flightplan processing and more detailed validations can be triggered manually using the [`Process FPL`](#process-fpl) tag functions. 51 | 52 | At the moment, `DelHel` supports the following validations and processing: 53 | 54 | - SID validation: ensures FPL has a valid SID fix for the departure airport and runway config as its first waypoint. Upon processing a flightplan, the calculated SID will be confirmed (added to the filed route in the FPL) so any changes to runway configs or different controller setups should have no effect. 55 | - NAP assignment: if available (and [enabled](#toggle-assignment-of-nap-sids)), noise abatement procedure SIDs will be assigned as appropriate based on runway config. 56 | - CFL (**c**leared **f**light **l**evel) validation: verifies correct CFL is set for selected SID. Processing a FPL automatically sets the correct CFL for the calculated SID. 57 | - RWY (**r**un**w**a**y**) validation: displays a warning if a runway assignment has been found in the flightplan as this might influence SID selection. Should no RWY be selected by EuroScope automatically (e.g. the filed SID fix does not have a SID for the active RWYs), `DelHel` will attempt to match the SIDs defined for all RWYs set as active in EuroScope to find an alternative valid departures (as defined in the `airports.json` file). 58 | - Flightplan cleanup: when processing a flightplan, some cleanup will be performed, removing any additional information the pilot might have included before the SID fix (e.g. a SID filed by the pilot), only leaving a valid speed/level group or a runway designation included. This prevents SID assignments filed by pilots from selecting an incorrect runway or procedure by accident. 59 | - INV (**Inv**alid routing): verifies that the filed flightplan contains a valid routing according to the maintained `routing.json` file. Those routes can e.g. be the SAXFRA compulsory routings or special Vatsim Event routings. Each route contains at least an entry point (= last waypoint of the SID) and optional waypoints. If waypoints are maintained, the flightplan has to contain those waypoints in identical sequence in order to pass the routing validation. "DCT"/"DIRECT"/Speed-Altitude-Blocks in the FP are omitted. 60 | 61 | Flightplans can be processed manually using the [`Process FPL`](#process-fpl) [tag function](#tag-functions) or automatically by toggling the [automatic processing](#toggle-automatic-processing) setting on. 62 | 63 | ### Tag items 64 | 65 | Tag items are used to display information about flightplans in aircraft lists, such as the departure or arrival list. 66 | At the moment, `DelHel` only adds one (optional) tag item to EuroScope: 67 | 68 | #### Flightplan Validation 69 | 70 | The `Flightplan Validation` tag item displays the result of the flightplan validation performed by `DelHel` for each aircraft. It contains any warnings or errors the plugin encounters while checking the FPL and is also the suggested column to interact with the `DelHel` [tag functions](#tag-functions). 71 | To save space, only one indication will be displayed at a time in the order they are encountered during processing, so you might see multiple warnings for a single flightplan while handling it. 72 | 73 | The following indications are available: 74 | 75 | ##### `ADEP` 76 | 77 | Error, displayed in red. 78 | The plugin did not find the departure airport of the aircraft in its [airport config](#airport-config) and thus cannot perform any validations or processing. This might indicate an incorrectly filed departure ICAO code or an airport currently not supported by `DelHel`. 79 | 80 | ##### `CFL` 81 | 82 | Info, no/default color. 83 | Indicates the current CFL set does not equal the initial CFL defined for the SID assigned. This most likely indicates the flightplan has not been processed yet or the aircraft was assigned a cleared flight level that deviates from the default. 84 | 85 | ##### `RFL` 86 | 87 | Caution, orange color. 88 | Indicates the RFL filed in the FPL is below the initial CFL defined for the SID assigned. While this does not always indicate an error, caution is advised so minimum altitudes or other restrictions are not violated. 89 | This warning is disabled by default and can be toggled using the [Toggle RFL below inital CFL](#toggle-rfl-below-initial-cfl-check) chat command. 90 | 91 | ##### `RWY` 92 | 93 | Info, no/default color. 94 | Indicates a runway designation (e.g. `LOWW/16`) has been found in the flightplan route. This is most likely the result of a manual runway assignment by a controller (via the TopSky plugin), but could also have been filed by a pilot, thus reminding you to ensure the correct RWY (and therefore SID) has been assigned. 95 | 96 | Info, green color. 97 | Indicates a runway designation (e.g. `LOWW/16`) has been found in the flightplan route of an already processed FPL. This is most likely the result of a manual runway assignment by a controller (via the TopSky plugin), but could also have been filed by a pilot, thus reminding you to ensure the correct RWY (and therefore SID) has been assigned. 98 | 99 | Error, displayed in red. 100 | Indicates an error with the runway config, as no departure runway could be assigned by EuroScope. Ensure you have at least one departure runway set as active in your EuroScope runway dialog. Alternatively this could indicate an error in the FPL route as no suitable RWY could be found. 101 | 102 | ##### `SID` 103 | 104 | Error, displayed in red. 105 | Indicates no valid SID fix has been found in the flightplan, the pilot has either filed an invalid FPL route without a proper departure waypoint set or the route is malformed and no proper fix could be detected. Open the flightplan and check the route, adapting it manually if required, or ask the pilot to file a new flightplan with a proper route. 106 | 107 | ##### `VFR` 108 | 109 | Info, no/default color. 110 | Indicates pilot filed a VFR flightplan, so little to no validations can be performed. This just serves as an additional reminder about VFR flights. 111 | 112 | Info, green color. 113 | Indicates pilot filed a processed VFR flightplan, so little to no validations can be performed. This just serves as an additional reminder about VFR flights. 114 | 115 | ##### `MIN` 116 | 117 | Caution, orange color. 118 | Indicates, that the Requested Flight Level (RFL) is below the minima for the filed route. This warning will only be displayed if [checking of min and max RFLs](#toggle-checking-of-min-and-max-rfls) is enabled (default off). 119 | The altitudes are maintained in the routing.json and can be disregarded by the controller if necessary (e.g. depeding on active/inactive military areas along the route). 120 | 121 | ##### `MAX` 122 | 123 | Caution, orange color. 124 | Indicates, that the Requested Flight Level (RFL) is above the maxima for the filed route. This warning will only be displayed if [checking of min and max RFLs](#toggle-checking-of-min-and-max-rfls) is enabled (default off). 125 | The altitudes are maintained in the routing.json and can be disregarded by the controller if necessary (e.g. depeding on active/inactive military areas along the route). 126 | 127 | ##### `INV` 128 | 129 | Caution, orange color. 130 | Indicates, that the filed route is not valid according to the maintained routings (routing.json). Those routings are e.g. the compulsory SAXRFRA routings for LOWW-departures or in case of a Vatsim Event. 131 | Indication becomes active as soon as the flightplan has been processed (SID is set and a valid SID exit point is in the FP). 132 | 133 | ##### `OK` 134 | 135 | Info, green color. 136 | Indicates a flightplan has been processed and no validation errors or warnings have been found. 137 | 138 | ### Tag functions 139 | 140 | Tag functions are used to trigger plugin functionality via a flightplan tag in aircraft lists, such as the departure or arrival list. 141 | At the moment, `DelHel` adds several functions for processing FPLs which can be added as an action to any tag item desirable (although using them with the [`Flightplan Validation`](#flightplan-validation) tag item is recommended): 142 | 143 | #### Process FPL 144 | 145 | Processes the selected flightplan using the default global plugin settings, checking for a valid SID fix (as defined in the [airport config](#airport-config)) while cleaning up the route (removing everything filed before the SID fix aside from speed/level groups and runway designations). 146 | Based on the SID fix and selected runway config, an appropriate SID (or NAP SID, if available/[enabled](#toggle-assignment-of-nap-sids)) will be picked from the airport config and the respective CFL assigned. 147 | Since VFR flightplans contain no SID and often have just a very basic (if any) route available, the number of validations and automatic changes available is quite limited. At the moment of writing, `DelHel` will only assign a default altitude (standard traffic pattern altitude, airport elevation + 1000ft, rounded to the nearest 500ft) for a VFR flightplan and skip all other processing. 148 | Note: using this function does **not** assign a squawk since TopSky does not expose its squawk logic and re-implementing the exact same assignment logic is outside the scope of this project. 149 | This is the default action of `DelHel` as it allows the most flexible and comfortable processing of FPLs and is thus suggested as a left-click action for the [`Flightplan Validation`](#flightplan-validation) tag item. 150 | 151 | #### Process FPL (NAP) 152 | 153 | Processes the selected flightplan as described in [Process FPL](#process-fpl) above, however forces using noise abatement procedures (where available), even if the global NAP assignment has been toggled off. 154 | This action is also available in the [`Validation Menu`](#validation-menu) tag function by default and allows for separate processing of selected flightplans without changing the global NAP setting. 155 | 156 | #### Process FPL (non-NAP) 157 | 158 | Processes the selected flightplan as described in [Process FPL](#process-fpl) above, however forces using "regular" (non-NAP) SIDs, even if the global NAP assignment has been toggled on. 159 | This action is also available in the [`Validation Menu`](#validation-menu) tag function by default and allows for separate processing of selected flightplans without changing the global NAP setting. 160 | 161 | #### Validation Menu 162 | 163 | Opens a selection menu containing advanced options regarding flightplan validation, currently only the [`Process FPL (NAP)`](#process-fpl-nap) and [`Process FPL (non-NAP)`](#process-fpl-non-nap) functions. As more functionality is added, additional entries will be included in this menu. 164 | This is the default secondary action of `DelHel` as it allows more fine-grained processing of FPLs and is thus suggested as the right-click action for the [`Flightplan Validation`](#flightplan-validation) tag item. 165 | 166 | ### Chat commands 167 | 168 | Chat commands allow more fine-grained control of `DelHel`'s behavior and settings not available via UI elements. Every chat command is prefixed with `.delhel` and can be entered in every chat channel available. Executing `.delhel` without any additional commands prints the version loaded and a list of commands available. 169 | 170 | #### Toggle automatic processing 171 | 172 | `.delhel auto` 173 | 174 | Toggles automatic processing of flightplans. 175 | 176 | Once enabled, `DelHel` will automatically process flightplans roughly every 5 seconds using the default processing settings as described in [Process FPL](#process-fpl) above. The plugin will only consider aircraft below a certain altitude threshold (current hard-coded to 5000ft) as already airborne pilots will most likely not receive a new clearance anyways. 177 | **Attention**: use this setting with caution, there is currently no limit to active airports or regard taken for controllers "below" you - `DelHel` will process flightplans for all aircraft it can "see" and has a valid departure [airport config](#airport-config) for. 178 | 179 | This setting will always reset to its disabled state on every startup to avoid issues after connecting to a new session. 180 | 181 | #### Toggle debug logging 182 | 183 | `.delhel debug` 184 | 185 | Toggles debug logging, displaying more messages about the internal state and flightplan processing. 186 | 187 | This setting will be saved to the EuroScope settings upon exit. 188 | 189 | #### Toggle assignment of NAP SIDs 190 | 191 | `.delhel nap` 192 | 193 | Toggles automatic assignment of NAP SIDs for the [default processing](#process-fpl) functionality. 194 | 195 | This setting will be saved to the EuroScope settings upon exit. 196 | 197 | #### Reload airport config 198 | 199 | `.delhel reload` 200 | 201 | Reloads the [airport config](#airport-config) from disk, allowing for a new config to be loaded without having to completely restart EuroScope or unloading/reloading the plugin. 202 | Note: this does not reset the processed state for the [automatic processing](#toggle-automatic-processing) features, so aircraft previously handled will not received an updated SID based on the new config. To reset the list of processed aircraft and handle every flightplan again, see [Reset plugin state](#reset-plugin-state). 203 | 204 | #### Reset plugin state 205 | 206 | `.delhel reset` 207 | 208 | Resets the plugin state to its default values (respecting the saved settings from the EuroScope config), disabling [automatic processing](#toggle-automatic-processing), reloading the [airport config](#airport-config) and clearing the list of processed aircraft. 209 | 210 | #### Toggle update checks 211 | 212 | `.delhel update` 213 | 214 | Toggles the plugin update check upon EuroScope startup. 215 | 216 | If enabled, `DelHel` will check this repository for newer releases of the plugin, displaying a message should an update be available. 217 | 218 | This setting will be saved to the EuroScope settings upon exit. 219 | 220 | #### Toggle RFL below initial CFL check 221 | 222 | `.delhel rflblw` 223 | 224 | Toggles the flightplan check for RFLs below the initial CFL for SIDs. 225 | 226 | If enabled, `DelHel` will check the RFL of a flightplan and display a [caution](#rfl) if the filed final level is below the initial CFL for the SID assigned. 227 | If disabled (default setting), `DelHel` will display no indication and silently assign the lower RFL as the CFL while processing. 228 | 229 | This setting will be saved to the EuroScope settings upon exit. 230 | 231 | #### Toggle logging of min and max RFLs 232 | 233 | `.delhel logminmaxrfl` 234 | 235 | Toggles logging of min and max RFLs for predefined routings during flightplan processing. 236 | 237 | If enabled, `DelHel` will display the min/max value triggering a warning during flightplan processing, informing you about the limit defined in the routing config. 238 | If disabled (default setting), `DelHel` will only display the min/max RFL values if the [debug mode](#toggle-debug-logging) is enabled. 239 | 240 | This setting will be saved to the EuroScope settings upon exit. 241 | 242 | #### Toggle checking of min and max RFLs 243 | 244 | `.delhel minmaxrfl` 245 | 246 | Toggles checking of min and max RFLs for predefined routings during flightplan processing. 247 | 248 | If enabled, `DelHel` will verify the min/max value of a predefined route during flightplan processing, displaying a warning if the filed RFL is below or above the limit defined in the routing config. 249 | If disabled (default setting), `DelHel` will not display the [`MIN`](#min)/[`MAX`](#max) warnings. 250 | 251 | This setting will be saved to the EuroScope settings upon exit. 252 | 253 | #### Toggle flashing of DelHel messages 254 | 255 | `.delhel flash` 256 | 257 | Toggles flashing of unread message indicator for messages in the DelHel group. Note that, once disabled, all `DelHel` messages will continue to flash until you have restarted EuroScope (saving your plugin settings). This unfortunately seems to be a EuroScope limitation we cannot work around. 258 | 259 | If enabled, messages sent to the `DelHel` group will have a flashing unread indiciator. 260 | If disabled (default setting), unread messages will only light up once, solidly (only applies after EuroScope has been restarted). 261 | 262 | This setting will be saved to the EuroScope settings upon exit. 263 | 264 | #### Toggle preferred TopSky squawk assignment 265 | 266 | `.delhel prefertopsky` 267 | 268 | Toggles automatic squawk assignment preferring TopSky, even if the CCAMS plugin is loaded. 269 | 270 | If enabled, the TopSky plugin will be used for automatic squawk assignments, even if CCAMS might be loaded. If no TopSky plugin was found, `DelHel` will try to use CCAMS instead, if available. 271 | If disabled (default setting), `DelHel` will check whether the CCAMS plugin is loaded. If it wasn't found, `DelHel` will attempt to use TopSky for squawk assignment. 272 | 273 | This setting will be saved to the EuroScope settings upon exit. 274 | 275 | #### Set custom runway configuration 276 | 277 | `.delhel rwycfg ` 278 | 279 | Enables custom arunway config, allowing custom runway overrides for SID waypoints when multiple active departure runways are being used. 280 | 281 | If enabled, the plugin will override runway assignments according to the custom config in the `customconfigs.json` file. 282 | 283 | This settings will not be saved and is for the current session only. 284 | 285 | `.delhel rwycfg none` 286 | 287 | Setting the custom runway configuation `none` disables the use of custom configs, runways will be assigned according to the sector file or priorities from the `airports.json` file. 288 | 289 | All entries are case-insensitive. 290 | 291 | ### Airport config 292 | 293 | `DelHel` uses its airport config, stored in the `airports.json` file in the same directory as the `DelHel.dll` plugin DLL, to retrieve most of its configuration for validations and flightplan processing. 294 | This repository contains the default airport config for `DelHel`, although you might choose to adapt it to your vACC's needs and remove entries that are not relevant to you. Please keep in mind the same structure must still be provided as the plugin will fail to parse its config otherwise. 295 | 296 | The `airports.json` file is a [JSON](https://www.json.org/) file containing a top-level object with the departure airport's ICAO code in uppercase as keys and `Airport` objects as values. 297 | 298 | #### `Airport` object 299 | 300 | | Key | Type | Description | Required | 301 | | --------- | -------- | --------------------------------------------------------------------------------------------- | -------- | 302 | | elevation | `int` | Airport elevation in feet (ft) | Yes | 303 | | sids | `object` | Object with SID fixes as keys and `SID` objects as values, contains SIDs available at airport | Yes | 304 | 305 | #### `SID` object 306 | 307 | | Key | Type | Description | Required | 308 | | ---- | -------- | ---------------------------------------------------------------------------------------- | -------- | 309 | | cfl | `int` | Initial CFL for SID in feet (ft) | Yes | 310 | | rwys | `object` | Object with RWYs as keys and `RWY` objects as values, contains RWYs SID is available for | Yes | 311 | 312 | #### `RWY` object 313 | 314 | | Key | Type | Description | Required | 315 | | ---- | -------- | ---------------------------------------------------------------------------------------- | -------- | 316 | | dep | `string` | Full name of SID for RWY | Yes | 317 | | nap | `string` | Full name of NAP for RWY (if available) | No | 318 | | prio | `int` | Priority of RWY for SID if no RWY is assigned by EuroScope (higher number = higher prio) | No | 319 | 320 | ### Routing config 321 | 322 | All mandatory routings are stored in the `routing.json`file in the same directory as the `DelHel.dll` plugin. Within this file, you can specify routings with optional waypoints, and the corresponding altitudes (min/max cruise altitude for this route). 323 | 324 | Please make sure to follow the file structure, and checking the edited file with a [validator](https://jsonformatter.curiousconcept.com/). 325 | 326 | Root object contains the departure ICAO code in (4-letter) uppercase as key and a `entry` object as value. 327 | 328 | #### `entry` object 329 | 330 | | Key | Type | Description | Required | 331 | | ------ | -------- | --------------------------------------------------------------------------- | -------- | 332 | | name | `string` | Describes the route entry waypoint, which is equal to the last SID-waypoint | Yes | 333 | | routes | `array` | Storage for more specified routes, e.g. destinations, waypoints, etc. | Yes | 334 | 335 | #### `routes` array 336 | 337 | The routes array is filled with route objects including the following data: 338 | 339 | | Key | Type | Description | Required | 340 | | --------- | -------- | --------------------------------------------------------------------------------- | -------- | 341 | | icao | `string` | Describes the route entry waypoint, which is equal to the last SID-waypoint | Yes | 342 | | maxlvl | `int` | Maximum allowed cruise altitude in flightlevel (= feet/100) for this route | Yes | 343 | | minlvl | `int` | Minimum allowed cruise altitude in flightlevel (= feet/100) for this route | Yes | 344 | | waypoints | `array` | Enter all succeeding waypoints after the SID as strings, optional, can left blank | No | 345 | 346 | ### Custom runway configs 347 | 348 | Optionally allows you to define custom departure runway assignments for specific SID waypoints - when using multiple departure runways. Each config has a name and a default runway. Different runway assignments can then be added for specific waypoints. All runways are assigned a priority that ulimately decides how they are assigned. All runways are initially assigned 0. The config default runway is set to 1. For the SID specific one's assign priorities of at least 2 or more depending on config. All this config is saved in a file named `customconfigs.json` that needs to be placed in the same folder as the `DelHel.dll` plugin. 349 | 350 | The root objects are the name of the config, followed by a config object as value. 351 | 352 | #### `config` object 353 | 354 | | Key | Type | Description | Required | 355 | | ---- | ------------ | --------------------------------------------------------------------- | -------- | 356 | | def | `string` | Default runway to assign of SID waypoint not on the `sids` list. | Yes | 357 | | sids | `collection` | Collection of child-elements containing SID-waypoint specific config. | Yes | 358 | 359 | #### `sids` collection 360 | 361 | Each SID element has the RNAV-WAYPOINT as the key, followed by the runway configs as value. 362 | 363 | | Key | Type | Description | Required | 364 | | ---- | ------------ | -------------------------------------------------------------- | -------- | 365 | | rwys | `collection` | Collection of child-elements containing runway configurations. | Yes | 366 | 367 | #### `rwys` collection 368 | 369 | Each runway uses it's number as the key and containing only one setting for the priority within. 370 | 371 | | Key | Type | Description | Required | 372 | | ---- | ----- | -------------------------------------------------------------------------------- | -------- | 373 | | prio | `int` | Priority for assiging this runway, should be >= 2 to be higher than the default. | Yes | 374 | 375 | ## Contributing 376 | 377 | If you have a suggestion for the project or encountered an error, please open an [issue](https://github.com/MorpheusXAUT/DelHel/issues) on GitHub. Please provide a summary of your idea or problem, optionally with some logs or screenshots and ways to replicate for the latter. 378 | The current development state as well as a rough collection of ideas can also be found in the `DelHel` [project board](https://github.com/MorpheusXAUT/DelHel/projects/1) on GitHub. 379 | 380 | [Pull requests](https://github.com/MorpheusXAUT/DelHel/pulls) are highly welcome, feel free to extend the plugin's functionality as you see fit and submit a request to this repository to make your changes available to everyone. Please keep in mind this plugin attempts to provide features in a relatively generic way so it can be used by vACCs with different needs - try refraining from "hard-coding" any features that might just apply to a specific airport or vACC. 381 | 382 | ### Development setup 383 | 384 | `DelHel` currently has no external development dependencies aside [Visual Studio](https://visualstudio.microsoft.com/vs/). Initial development started using Visual Studio 2019, although later versions should most likely remain compatible. 385 | 386 | To allow for debugging, the project has been configured to launch EuroScope as its debug command. Since your installation path of EuroScope will most likely be different, you **must** set an environment variable `EUROSCOPE_ROOT` to the **directory** EuroScope is installed in (**not** the actual `EuroScope.exe` executable), for instance `E:\EuroScope`. 387 | Note: triggering a breakpoint seems to cause both EuroScope and Visual Studio to freak out, resulting in high resource usage and slugging mouse movements, thus only being of limited usefulnes. **NEVER** debug your EuroScope plugin using a live connection as halting EuroScope apparently messes with the VATSIM data feed under certain circumstances. 388 | 389 | `DelHel` is compiled using Windows SDK Version 10.0 with a platform toolset for Visual Studio 2019 (v142) using the ISO C++17 Standard. 390 | 391 | This repository contains all external dependencies used by the project in their respective `include` and `lib` folders: 392 | 393 | - `EuroScope`: EuroScope plugin library 394 | - `nlohmann/json`: [JSON for Modern C++](https://github.com/nlohmann/json/) ([v3.9.1](https://github.com/nlohmann/json/releases/tag/v3.9.1), [MIT License](https://github.com/nlohmann/json/blob/develop/LICENSE.MIT)), used for parsing the airport config JSON 395 | - `semver`: [Semantic Versioning C++](https://github.com/Neargye/semver) ([v0.2.2](https://github.com/Neargye/semver/releases/tag/v0.2.2), [MIT License](https://github.com/Neargye/semver/blob/master/LICENSE)), used for version comparison of update check 396 | 397 | ## License 398 | 399 | [MIT License](LICENSE) 400 | -------------------------------------------------------------------------------- /airports.json: -------------------------------------------------------------------------------- 1 | { 2 | "LOWW": { 3 | "elevation": 600, 4 | "sids": { 5 | "ADAMA": { 6 | "cfl": 5000, 7 | "rwys": { 8 | "11": { 9 | "dep": "ADAMA1A", 10 | "prio": 1 11 | }, 12 | "16": { 13 | "dep": "ADAMA1B", 14 | "prio": 3 15 | }, 16 | "29": { 17 | "dep": "ADAMA2C", 18 | "nap": "ASPI2C", 19 | "prio": 2 20 | }, 21 | "34": { 22 | "dep": "ADAMA1D", 23 | "prio": 4 24 | } 25 | } 26 | }, 27 | "ARSIN": { 28 | "cfl": 5000, 29 | "rwys": { 30 | "11": { 31 | "dep": "ARSIN1A", 32 | "prio": 2 33 | }, 34 | "16": { 35 | "dep": "ARSIN1B", 36 | "prio": 4 37 | }, 38 | "29": { 39 | "dep": "ARSIN1C", 40 | "nap": "EWUK1C", 41 | "prio": 3 42 | }, 43 | "34": { 44 | "dep": "ARSIN1D", 45 | "prio": 1 46 | } 47 | } 48 | }, 49 | "BUWUT": { 50 | "cfl": 5000, 51 | "rwys": { 52 | "11": { 53 | "dep": "BUWUT1A", 54 | "prio": 2 55 | }, 56 | "16": { 57 | "dep": "BUWUT1B", 58 | "prio": 1 59 | }, 60 | "29": { 61 | "dep": "BUWUT1C", 62 | "nap": "UMSU3C", 63 | "prio": 3 64 | }, 65 | "34": { 66 | "dep": "BUWUT1D", 67 | "prio": 4 68 | } 69 | } 70 | }, 71 | "DITIS": { 72 | "cfl": 5000, 73 | "rwys": { 74 | "11": { 75 | "dep": "BUWUT1A", 76 | "prio": 2 77 | }, 78 | "16": { 79 | "dep": "BUWUT1B", 80 | "prio": 1 81 | }, 82 | "29": { 83 | "dep": "BUWUT1C", 84 | "nap": "UMSU3C", 85 | "prio": 3 86 | }, 87 | "34": { 88 | "dep": "BUWUT1D", 89 | "prio": 4 90 | } 91 | } 92 | }, 93 | "KOXER": { 94 | "cfl": 5000, 95 | "rwys": { 96 | "11": { 97 | "dep": "KOXER1A", 98 | "prio": 1 99 | }, 100 | "16": { 101 | "dep": "KOXER1B", 102 | "prio": 3 103 | }, 104 | "29": { 105 | "dep": "KOXER1C", 106 | "nap": "AGMI2C", 107 | "prio": 2 108 | }, 109 | "34": { 110 | "dep": "KOXER1D", 111 | "prio": 4 112 | } 113 | } 114 | }, 115 | "LANUX": { 116 | "cfl": 5000, 117 | "rwys": { 118 | "11": { 119 | "dep": "LANUX3A", 120 | "prio": 2 121 | }, 122 | "16": { 123 | "dep": "LANUX5B", 124 | "prio": 1 125 | }, 126 | "29": { 127 | "dep": "LANUX2C", 128 | "nap": "UNGU2C", 129 | "prio": 3 130 | }, 131 | "34": { 132 | "dep": "LANUX6D", 133 | "prio": 4 134 | } 135 | } 136 | }, 137 | "LEDVA": { 138 | "cfl": 5000, 139 | "rwys": { 140 | "11": { 141 | "dep": "LEDVA3A", 142 | "prio": 2 143 | }, 144 | "16": { 145 | "dep": "LEDVA2B", 146 | "prio": 3 147 | }, 148 | "29": { 149 | "dep": "LEDVA3C", 150 | "nap": "VABG2C", 151 | "prio": 1 152 | }, 153 | "34": { 154 | "dep": "LEDVA4D", 155 | "prio": 4 156 | } 157 | } 158 | }, 159 | "LUGEM": { 160 | "cfl": 5000, 161 | "rwys": { 162 | "11": { 163 | "dep": "LUGEM1A", 164 | "nap": "OSMO1A", 165 | "prio": 3 166 | }, 167 | "16": { 168 | "dep": "LUGEM2B", 169 | "prio": 2 170 | }, 171 | "29": { 172 | "dep": "LUGEM1C", 173 | "nap": "OSMO2C", 174 | "prio": 4 175 | }, 176 | "34": { 177 | "dep": "LUGEM1D", 178 | "nap": "OSMO2D", 179 | "prio": 1 180 | } 181 | } 182 | }, 183 | "MEDIX": { 184 | "cfl": 5000, 185 | "rwys": { 186 | "11": { 187 | "dep": "MEDIX1A", 188 | "prio": 3 189 | }, 190 | "16": { 191 | "dep": "MEDIX2B", 192 | "prio": 2 193 | }, 194 | "29": { 195 | "dep": "MEDIX1C", 196 | "nap": "OTGA2C", 197 | "prio": 4 198 | }, 199 | "34": { 200 | "dep": "MEDIX1D", 201 | "nap": "OTGA2D", 202 | "prio": 1 203 | } 204 | } 205 | }, 206 | "OSPEN": { 207 | "cfl": 5000, 208 | "rwys": { 209 | "11": { 210 | "dep": "OSPEN2A", 211 | "nap": "IMVO3A", 212 | "prio": 3 213 | }, 214 | "16": { 215 | "dep": "OSPEN5B", 216 | "prio": 2 217 | }, 218 | "29": { 219 | "dep": "OSPEN4C", 220 | "nap": "IMVO3C", 221 | "prio": 4 222 | }, 223 | "34": { 224 | "dep": "OSPEN3D", 225 | "nap": "IMVO3D", 226 | "prio": 1 227 | } 228 | } 229 | }, 230 | "RUPET": { 231 | "cfl": 5000, 232 | "rwys": { 233 | "11": { 234 | "dep": "RUPET1A", 235 | "nap": "IRGO1A", 236 | "prio": 3 237 | }, 238 | "16": { 239 | "dep": "RUPET2B", 240 | "prio": 2 241 | }, 242 | "29": { 243 | "dep": "RUPET2C", 244 | "nap": "IRGO2C", 245 | "prio": 4 246 | }, 247 | "34": { 248 | "dep": "RUPET1D", 249 | "nap": "IRGO2D", 250 | "prio": 1 251 | } 252 | } 253 | }, 254 | "SNU": { 255 | "cfl": 5000, 256 | "rwys": { 257 | "11": { 258 | "dep": "SNU2A", 259 | "prio": 3 260 | }, 261 | "16": { 262 | "dep": "SNU4B", 263 | "prio": 2 264 | }, 265 | "29": { 266 | "dep": "SNU2C", 267 | "prio": 4 268 | }, 269 | "34": { 270 | "dep": "SNU2D", 271 | "prio": 1 272 | } 273 | } 274 | }, 275 | "SOVIL": { 276 | "cfl": 5000, 277 | "rwys": { 278 | "11": { 279 | "dep": "SOVIL1A", 280 | "nap": "ODSU1A", 281 | "prio": 3 282 | }, 283 | "16": { 284 | "dep": "SOVIL2B", 285 | "prio": 2 286 | }, 287 | "29": { 288 | "dep": "SOVIL1C", 289 | "nap": "ODSU2C", 290 | "prio": 4 291 | }, 292 | "34": { 293 | "dep": "SOVIL1D", 294 | "nap": "ODSU2D", 295 | "prio": 1 296 | } 297 | } 298 | }, 299 | "STEIN": { 300 | "cfl": 5000, 301 | "rwys": { 302 | "11": { 303 | "dep": "STEIN2A", 304 | "prio": 2 305 | }, 306 | "16": { 307 | "dep": "STEIN4B", 308 | "prio": 4 309 | }, 310 | "29": { 311 | "dep": "STEIN3C", 312 | "nap": "EMKO3C", 313 | "prio": 3 314 | }, 315 | "34": { 316 | "dep": "STEIN3D", 317 | "nap": "EMKO2D", 318 | "prio": 1 319 | } 320 | } 321 | } 322 | } 323 | }, 324 | "LOWI": { 325 | "elevation": 1907, 326 | "sids": { 327 | "ADILO": { 328 | "cfl": 16000, 329 | "rwys": { 330 | "08": { 331 | "dep": "ADILO2J" 332 | } 333 | } 334 | }, 335 | "BRENO": { 336 | "cfl": 16000, 337 | "rwys": { 338 | "08": { 339 | "dep": "BRENO2J" 340 | }, 341 | "26": { 342 | "dep": "BRENO3H" 343 | } 344 | } 345 | }, 346 | "KOGOL": { 347 | "cfl": 12000, 348 | "rwys": { 349 | "08": { 350 | "dep": "KOGOL3J" 351 | }, 352 | "26": { 353 | "dep": "KOGOL4H" 354 | } 355 | } 356 | }, 357 | "KPT": { 358 | "cfl": 16000, 359 | "rwys": { 360 | "08": { 361 | "dep": "KPT5J", 362 | "prio": 2 363 | }, 364 | "26": { 365 | "dep": "MOGTI3H", 366 | "prio": 1 367 | } 368 | } 369 | }, 370 | "MOGTI": { 371 | "cfl": 16000, 372 | "rwys": { 373 | "08": { 374 | "dep": "KPT5J", 375 | "prio": 1 376 | }, 377 | "26": { 378 | "dep": "MOGTI3H", 379 | "prio": 2 380 | } 381 | } 382 | }, 383 | "OBEDI": { 384 | "cfl": 12000, 385 | "rwys": { 386 | "08": { 387 | "dep": "OBEDI3J" 388 | }, 389 | "26": { 390 | "dep": "OBEDI4H" 391 | } 392 | } 393 | }, 394 | "RTT": { 395 | "cfl": 12000, 396 | "rwys": { 397 | "08": { 398 | "dep": "RTT2Q" 399 | }, 400 | "26": { 401 | "dep": "RTT1R" 402 | } 403 | } 404 | }, 405 | "UNKEN": { 406 | "cfl": 12000, 407 | "rwys": { 408 | "08": { 409 | "dep": "UNKEN2J" 410 | }, 411 | "26": { 412 | "dep": "UNKEN3H" 413 | } 414 | } 415 | } 416 | } 417 | }, 418 | "LOWS": { 419 | "elevation": 1420, 420 | "sids": { 421 | "DETSA": { 422 | "cfl": 10000, 423 | "rwys": { 424 | "15": { 425 | "dep": "DETSA2B" 426 | }, 427 | "33": { 428 | "dep": "DETSA1A" 429 | } 430 | } 431 | }, 432 | "INROM": { 433 | "cfl": 6000, 434 | "rwys": { 435 | "15": { 436 | "dep": "INROM1B" 437 | }, 438 | "33": { 439 | "dep": "INROM1A" 440 | } 441 | } 442 | }, 443 | "NEMAL": { 444 | "cfl": 6000, 445 | "rwys": { 446 | "15": { 447 | "dep": "NEMAL3B" 448 | }, 449 | "33": { 450 | "dep": "NEMAL3A" 451 | } 452 | } 453 | }, 454 | "RTT": { 455 | "cfl": 10000, 456 | "rwys": { 457 | "15": { 458 | "dep": "RTT4B" 459 | }, 460 | "33": { 461 | "dep": "RTT3A" 462 | } 463 | } 464 | }, 465 | "SBG": { 466 | "cfl": 6000, 467 | "rwys": { 468 | "33": { 469 | "dep": "SBG2A" 470 | } 471 | } 472 | }, 473 | "SIMBA": { 474 | "cfl": 6000, 475 | "rwys": { 476 | "15": { 477 | "dep": "INROM1B" 478 | }, 479 | "33": { 480 | "dep": "INROM1A" 481 | } 482 | } 483 | }, 484 | "TITIG": { 485 | "cfl": 6000, 486 | "rwys": { 487 | "15": { 488 | "dep": "TITIG3B" 489 | }, 490 | "33": { 491 | "dep": "TITIG2A" 492 | } 493 | } 494 | }, 495 | "TRAUN": { 496 | "cfl": 8000, 497 | "rwys": { 498 | "15": { 499 | "dep": "TRAUN3B" 500 | }, 501 | "33": { 502 | "dep": "TRAUN2A" 503 | } 504 | } 505 | }, 506 | "VERDA": { 507 | "cfl": 10000, 508 | "rwys": { 509 | "15": { 510 | "dep": "VERDA2B" 511 | }, 512 | "33": { 513 | "dep": "VERDA1A" 514 | } 515 | } 516 | } 517 | } 518 | }, 519 | "LOWL": { 520 | "elevation": 980, 521 | "sids": { 522 | "LIDSI": { 523 | "cfl": 6000, 524 | "rwys": { 525 | "08": { 526 | "dep": "LIDSI1E" 527 | }, 528 | "26": { 529 | "dep": "LIDSI1W" 530 | } 531 | } 532 | }, 533 | "LIMRA": { 534 | "cfl": 8000, 535 | "rwys": { 536 | "08": { 537 | "dep": "LIMRA1E" 538 | }, 539 | "26": { 540 | "dep": "LIMRA1W" 541 | } 542 | } 543 | }, 544 | "LNZ": { 545 | "cfl": 4000, 546 | "rwys": { 547 | "08": { 548 | "dep": "LNZ2T" 549 | }, 550 | "26": { 551 | "dep": "LNZ2L" 552 | } 553 | } 554 | }, 555 | "PEROL": { 556 | "cfl": 6000, 557 | "rwys": { 558 | "08": { 559 | "dep": "PEROL1E" 560 | }, 561 | "26": { 562 | "dep": "PEROL1W" 563 | } 564 | } 565 | }, 566 | "PETEN": { 567 | "cfl": 6000, 568 | "rwys": { 569 | "08": { 570 | "dep": "PETEN1E" 571 | }, 572 | "26": { 573 | "dep": "PETEN1W" 574 | } 575 | } 576 | } 577 | } 578 | }, 579 | "LOWG": { 580 | "elevation": 1120, 581 | "sids": { 582 | "ABIRI": { 583 | "cfl": 16000, 584 | "rwys": { 585 | "16C": { 586 | "dep": "ABIRI4G" 587 | }, 588 | "34C": { 589 | "dep": "ABIRI5U" 590 | } 591 | } 592 | }, 593 | "GBG": { 594 | "cfl": 16000, 595 | "rwys": { 596 | "16C": { 597 | "dep": "GBG7X" 598 | }, 599 | "34C": { 600 | "dep": "GBG5Y" 601 | } 602 | } 603 | }, 604 | "GOLVA": { 605 | "cfl": 12000, 606 | "rwys": { 607 | "16C": { 608 | "dep": "GOLVA5G" 609 | }, 610 | "34C": { 611 | "dep": "GOLVA4U" 612 | } 613 | } 614 | }, 615 | "GOTAR": { 616 | "cfl": 12000, 617 | "rwys": { 618 | "16C": { 619 | "dep": "GOTAR5G" 620 | }, 621 | "34C": { 622 | "dep": "GOTAR5U" 623 | } 624 | } 625 | }, 626 | "GRZ": { 627 | "cfl": 16000, 628 | "rwys": { 629 | "16C": { 630 | "dep": "GRZ3W" 631 | }, 632 | "34C": { 633 | "dep": "GRZ4Y" 634 | } 635 | } 636 | }, 637 | "MILGO": { 638 | "cfl": 16000, 639 | "rwys": { 640 | "16C": { 641 | "dep": "MILGO6G" 642 | }, 643 | "34C": { 644 | "dep": "MILGO5U" 645 | } 646 | } 647 | }, 648 | "MUREG": { 649 | "cfl": 16000, 650 | "rwys": { 651 | "16C": { 652 | "dep": "MUREG5G" 653 | }, 654 | "34C": { 655 | "dep": "MUREG4U" 656 | } 657 | } 658 | }, 659 | "RADLY": { 660 | "cfl": 16000, 661 | "rwys": { 662 | "16C": { 663 | "dep": "RADLY5G" 664 | }, 665 | "34C": { 666 | "dep": "RADLY6U" 667 | } 668 | } 669 | }, 670 | "ROPAG": { 671 | "cfl": 16000, 672 | "rwys": { 673 | "16C": { 674 | "dep": "ROPAG4G" 675 | }, 676 | "34C": { 677 | "dep": "ROPAG4U" 678 | } 679 | } 680 | } 681 | } 682 | }, 683 | "LOWK": { 684 | "elevation": 1472, 685 | "sids": { 686 | "ABIRI": { 687 | "cfl": 16000, 688 | "rwys": { 689 | "10L": { 690 | "dep": "ABIRI1L" 691 | }, 692 | "28R": { 693 | "dep": "ABIRI1R" 694 | } 695 | } 696 | }, 697 | "BERTA": { 698 | "cfl": 15000, 699 | "rwys": { 700 | "10L": { 701 | "dep": "BERTA1L" 702 | }, 703 | "28R": { 704 | "dep": "BERTA1R" 705 | } 706 | } 707 | }, 708 | "INGID": { 709 | "cfl": 16000, 710 | "rwys": { 711 | "10L": { 712 | "dep": "INGID2L" 713 | }, 714 | "28R": { 715 | "dep": "INGID2R" 716 | } 717 | } 718 | }, 719 | "KFT": { 720 | "cfl": 16000, 721 | "rwys": { 722 | "10L": { 723 | "dep": "KFT1L" 724 | }, 725 | "28R": { 726 | "dep": "KFT2R" 727 | } 728 | } 729 | }, 730 | "KLAGY": { 731 | "cfl": 15000, 732 | "rwys": { 733 | "10L": { 734 | "dep": "KLAGY1L" 735 | }, 736 | "28R": { 737 | "dep": "KLAGY1R" 738 | } 739 | } 740 | }, 741 | "REKTI": { 742 | "cfl": 15000, 743 | "rwys": { 744 | "10L": { 745 | "dep": "REKTI1L" 746 | }, 747 | "28R": { 748 | "dep": "REKTI1R" 749 | } 750 | } 751 | }, 752 | "VILAK": { 753 | "cfl": 16000, 754 | "rwys": { 755 | "10L": { 756 | "dep": "VILAK1L" 757 | }, 758 | "28R": { 759 | "dep": "VILAK1R" 760 | } 761 | } 762 | } 763 | } 764 | }, 765 | "LOAN": { 766 | "elevation": 896, 767 | "sids": { 768 | "GESGI": { 769 | "cfl": 3000, 770 | "rwys": { 771 | "09": { 772 | "dep": "GESGI1A" 773 | }, 774 | "27": { 775 | "dep": "GESGI1A" 776 | } 777 | } 778 | } 779 | } 780 | }, 781 | "LOAV": { 782 | "elevation": 767, 783 | "sids": { 784 | "MOVOS": { 785 | "cfl": 3000, 786 | "rwys": { 787 | "09": { 788 | "dep": "MOVOS1A" 789 | }, 790 | "27": { 791 | "dep": "MOVOS1A" 792 | } 793 | } 794 | } 795 | } 796 | }, 797 | "LOIJ": { 798 | "elevation": 2198, 799 | "sids": { 800 | "ERKIR": { 801 | "cfl": 9000, 802 | "rwys": { 803 | "13": { 804 | "dep": "ERKIR1G" 805 | }, 806 | "31": { 807 | "dep": "ERKIR1G" 808 | } 809 | } 810 | } 811 | } 812 | }, 813 | "LOWZ": { 814 | "elevation": 2470, 815 | "sids": { 816 | "NANIT": { 817 | "cfl": 10000, 818 | "rwys": { 819 | "08": { 820 | "dep": "NANIT1G" 821 | }, 822 | "26": { 823 | "dep": "NANIT1G" 824 | } 825 | } 826 | } 827 | } 828 | } 829 | } -------------------------------------------------------------------------------- /customconfigs.json: -------------------------------------------------------------------------------- 1 | { 2 | "F1": { 3 | "def": "29", 4 | "sids": { 5 | "KOXER": { 6 | "rwys": { 7 | "16": { 8 | "prio": 2 9 | } 10 | } 11 | }, 12 | "ADAMA": { 13 | "rwys": { 14 | "16": { 15 | "prio": 2 16 | } 17 | } 18 | }, 19 | "LEDVA": { 20 | "rwys": { 21 | "16": { 22 | "prio": 2 23 | } 24 | } 25 | }, 26 | "STEIN": { 27 | "rwys": { 28 | "16": { 29 | "prio": 2 30 | } 31 | } 32 | }, 33 | "ARSIN": { 34 | "rwys": { 35 | "16": { 36 | "prio": 2 37 | } 38 | } 39 | } 40 | } 41 | }, 42 | "F2": { 43 | "def": "29", 44 | "sids": { 45 | "KOXER": { 46 | "rwys": { 47 | "16": { 48 | "prio": 2 49 | } 50 | } 51 | }, 52 | "ADAMA": { 53 | "rwys": { 54 | "16": { 55 | "prio": 2 56 | } 57 | } 58 | }, 59 | "LEDVA": { 60 | "rwys": { 61 | "16": { 62 | "prio": 2 63 | } 64 | } 65 | } 66 | } 67 | }, 68 | "G1": { 69 | "def": "29", 70 | "sids": { 71 | "KOXER": { 72 | "rwys": { 73 | "34": { 74 | "prio": 2 75 | } 76 | } 77 | }, 78 | "ADAMA": { 79 | "rwys": { 80 | "34": { 81 | "prio": 2 82 | } 83 | } 84 | }, 85 | "LEDVA": { 86 | "rwys": { 87 | "34": { 88 | "prio": 2 89 | } 90 | } 91 | }, 92 | "LANUX": { 93 | "rwys": { 94 | "34": { 95 | "prio": 2 96 | } 97 | } 98 | }, 99 | "BUWUT": { 100 | "rwys": { 101 | "34": { 102 | "prio": 2 103 | } 104 | } 105 | } 106 | } 107 | }, 108 | "G2": { 109 | "def": "29", 110 | "sids": { 111 | "KOXER": { 112 | "rwys": { 113 | "34": { 114 | "prio": 2 115 | } 116 | } 117 | }, 118 | "ADAMA": { 119 | "rwys": { 120 | "34": { 121 | "prio": 2 122 | } 123 | } 124 | }, 125 | "LEDVA": { 126 | "rwys": { 127 | "34": { 128 | "prio": 2 129 | } 130 | } 131 | }, 132 | "LANUX": { 133 | "rwys": { 134 | "34": { 135 | "prio": 2 136 | } 137 | } 138 | } 139 | } 140 | }, 141 | "G3": { 142 | "def": "29", 143 | "sids": { 144 | "KOXER": { 145 | "rwys": { 146 | "34": { 147 | "prio": 2 148 | } 149 | } 150 | }, 151 | "ADAMA": { 152 | "rwys": { 153 | "34": { 154 | "prio": 2 155 | } 156 | } 157 | }, 158 | "LEDVA": { 159 | "rwys": { 160 | "34": { 161 | "prio": 2 162 | } 163 | } 164 | } 165 | } 166 | }, 167 | "G4": { 168 | "def": "29", 169 | "sids": { 170 | "KOXER": { 171 | "rwys": { 172 | "34": { 173 | "prio": 2 174 | } 175 | } 176 | }, 177 | "ADAMA": { 178 | "rwys": { 179 | "34": { 180 | "prio": 2 181 | } 182 | } 183 | } 184 | } 185 | } 186 | } -------------------------------------------------------------------------------- /include/semver/semver.hpp: -------------------------------------------------------------------------------- 1 | // _____ _ _ 2 | // / ____| | | (_) 3 | // | (___ ___ _ __ ___ __ _ _ __ | |_ _ ___ 4 | // \___ \ / _ \ '_ ` _ \ / _` | '_ \| __| |/ __| 5 | // ____) | __/ | | | | | (_| | | | | |_| | (__ 6 | // |_____/ \___|_| |_| |_|\__,_|_| |_|\__|_|\___| 7 | // __ __ _ _ _____ 8 | // \ \ / / (_) (_) / ____|_ _ 9 | // \ \ / /__ _ __ ___ _ ___ _ __ _ _ __ __ _ | | _| |_ _| |_ 10 | // \ \/ / _ \ '__/ __| |/ _ \| '_ \| | '_ \ / _` | | | |_ _|_ _| 11 | // \ / __/ | \__ \ | (_) | | | | | | | | (_| | | |____|_| |_| 12 | // \/ \___|_| |___/_|\___/|_| |_|_|_| |_|\__, | \_____| 13 | // https://github.com/Neargye/semver __/ | 14 | // version 0.2.2 |___/ 15 | // 16 | // Licensed under the MIT License . 17 | // SPDX-License-Identifier: MIT 18 | // Copyright (c) 2018 - 2019 Daniil Goncharov . 19 | // 20 | // Permission is hereby granted, free of charge, to any person obtaining a copy 21 | // of this software and associated documentation files (the "Software"), to deal 22 | // in the Software without restriction, including without limitation the rights 23 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 | // copies of the Software, and to permit persons to whom the Software is 25 | // furnished to do so, subject to the following conditions: 26 | // 27 | // The above copyright notice and this permission notice shall be included in all 28 | // copies or substantial portions of the Software. 29 | // 30 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 36 | // SOFTWARE. 37 | 38 | #ifndef NEARGYE_SEMANTIC_VERSIONING_HPP 39 | #define NEARGYE_SEMANTIC_VERSIONING_HPP 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #if __has_include() 49 | #include 50 | #else 51 | #include 52 | #endif 53 | 54 | // Allow to disable exceptions. 55 | #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(SEMVER_NOEXCEPTION) 56 | # include 57 | # define __SEMVER_THROW(exception) throw exception 58 | #else 59 | # include 60 | # define __SEMVER_THROW(exception) std::abort() 61 | #endif 62 | 63 | #if defined(__clang__) 64 | # pragma clang diagnostic push 65 | # pragma clang diagnostic ignored "-Wmissing-braces" // Ignore warning: suggest braces around initialization of subobject [-Wmissing-braces] 'return {first, std::errc::invalid_argument};'. 66 | #endif 67 | 68 | namespace semver { 69 | 70 | enum struct prerelease : std::uint8_t { 71 | alpha = 0, 72 | beta = 1, 73 | rc = 2, 74 | none = 3 75 | }; 76 | 77 | // Max version string length = 3() + 1(.) + 3() + 1(.) + 3() + 1(-) + 5() + 1(.) + 3() = 21. 78 | inline constexpr std::size_t max_version_string_length = 21; 79 | 80 | namespace detail { 81 | 82 | #if __has_include() 83 | struct from_chars_result : std::from_chars_result { 84 | [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } 85 | }; 86 | 87 | struct to_chars_result : std::to_chars_result { 88 | [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } 89 | }; 90 | #else 91 | struct from_chars_result { 92 | const char* ptr; 93 | std::errc ec; 94 | 95 | [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } 96 | }; 97 | 98 | struct to_chars_result { 99 | char* ptr; 100 | std::errc ec; 101 | 102 | [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc{}; } 103 | }; 104 | #endif 105 | 106 | inline constexpr std::string_view alpha = {"-alpha", 6}; 107 | inline constexpr std::string_view beta = {"-beta", 5}; 108 | inline constexpr std::string_view rc = {"-rc", 3}; 109 | 110 | // Min version string length = 1() + 1(.) + 1() + 1(.) + 1() = 5. 111 | inline constexpr auto min_version_string_length = 5; 112 | 113 | constexpr char to_lower(char c) noexcept { 114 | return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; 115 | } 116 | 117 | constexpr bool is_digit(char c) noexcept { 118 | return c >= '0' && c <= '9'; 119 | } 120 | 121 | constexpr std::uint8_t to_digit(char c) noexcept { 122 | return c - '0'; 123 | } 124 | 125 | constexpr std::uint8_t length(std::uint8_t x) noexcept { 126 | return x < 10 ? 1 : (x < 100 ? 2 : 3); 127 | } 128 | 129 | constexpr std::uint8_t length(prerelease t) noexcept { 130 | if (t == prerelease::alpha) { 131 | return 5; 132 | } else if (t == prerelease::beta) { 133 | return 4; 134 | } else if(t == prerelease::rc) { 135 | return 2; 136 | } 137 | 138 | return 0; 139 | } 140 | 141 | constexpr bool equals(const char* first, const char* last, std::string_view str) noexcept { 142 | for (std::size_t i = 0; first != last && i < str.length(); ++i, ++first) { 143 | if (to_lower(*first) != to_lower(str[i])) { 144 | return false; 145 | } 146 | } 147 | 148 | return true; 149 | } 150 | 151 | constexpr char* to_chars(char* str, std::uint8_t x, bool dot = true) noexcept { 152 | do { 153 | *(--str) = static_cast('0' + (x % 10)); 154 | x /= 10; 155 | } while (x != 0); 156 | 157 | if (dot) { 158 | *(--str) = '.'; 159 | } 160 | 161 | return str; 162 | } 163 | 164 | constexpr char* to_chars(char* str, prerelease t) noexcept { 165 | const auto p = t == prerelease::alpha 166 | ? alpha 167 | : t == prerelease::beta 168 | ? beta 169 | : t == prerelease::rc 170 | ? rc 171 | : std::string_view{}; 172 | 173 | for (auto it = p.rbegin(); it != p.rend(); ++it) { 174 | *(--str) = *it; 175 | } 176 | 177 | return str; 178 | } 179 | 180 | constexpr const char* from_chars(const char* first, const char* last, std::uint8_t& d) noexcept { 181 | if (first != last && is_digit(*first)) { 182 | std::int32_t t = 0; 183 | for (; first != last && is_digit(*first); ++first) { 184 | t = t * 10 + to_digit(*first); 185 | } 186 | if (t <= (std::numeric_limits::max)()) { 187 | d = static_cast(t); 188 | return first; 189 | } 190 | } 191 | 192 | return nullptr; 193 | } 194 | 195 | constexpr const char* from_chars(const char* first, const char* last, prerelease& p) noexcept { 196 | if (equals(first, last, alpha)) { 197 | p = prerelease::alpha; 198 | return first + alpha.length(); 199 | } else if (equals(first, last, beta)) { 200 | p = prerelease::beta; 201 | return first + beta.length(); 202 | } else if (equals(first, last, rc)) { 203 | p = prerelease::rc; 204 | return first + rc.length(); 205 | } 206 | 207 | return nullptr; 208 | } 209 | 210 | constexpr bool check_delimiter(const char* first, const char* last, char d) noexcept { 211 | return first != last && first != nullptr && *first == d; 212 | } 213 | 214 | } // namespace semver::detail 215 | 216 | struct version { 217 | std::uint8_t major = 0; 218 | std::uint8_t minor = 1; 219 | std::uint8_t patch = 0; 220 | prerelease prerelease_type = prerelease::none; 221 | std::uint8_t prerelease_number = 0; 222 | 223 | constexpr version(std::uint8_t major, 224 | std::uint8_t minor, 225 | std::uint8_t patch, 226 | prerelease prerelease_type = prerelease::none, 227 | std::uint8_t prerelease_number = 0) noexcept 228 | : major{major}, 229 | minor{minor}, 230 | patch{patch}, 231 | prerelease_type{prerelease_type}, 232 | prerelease_number{prerelease_type == prerelease::none ? static_cast(0) : prerelease_number} { 233 | } 234 | 235 | constexpr version(std::string_view str) : version(0, 0, 0, prerelease::none, 0) { 236 | from_string(str); 237 | } 238 | 239 | constexpr version() = default; // https://semver.org/#how-should-i-deal-with-revisions-in-the-0yz-initial-development-phase 240 | 241 | constexpr version(const version&) = default; 242 | 243 | constexpr version(version&&) = default; 244 | 245 | ~version() = default; 246 | 247 | constexpr version& operator=(const version&) = default; 248 | 249 | constexpr version& operator=(version&&) = default; 250 | 251 | [[nodiscard]] constexpr detail::from_chars_result from_chars(const char* first, const char* last) noexcept { 252 | if (first == nullptr || last == nullptr || (last - first) < detail::min_version_string_length) { 253 | return {first, std::errc::invalid_argument}; 254 | } 255 | 256 | auto next = first; 257 | if (next = detail::from_chars(next, last, major); detail::check_delimiter(next, last, '.')) { 258 | if (next = detail::from_chars(++next, last, minor); detail::check_delimiter(next, last, '.')) { 259 | if (next = detail::from_chars(++next, last, patch); next == last) { 260 | prerelease_type = prerelease::none; 261 | prerelease_number = 0; 262 | return {next, std::errc{}}; 263 | } else if (detail::check_delimiter(next, last, '-')) { 264 | if (next = detail::from_chars(next, last, prerelease_type); next == last) { 265 | prerelease_number = 0; 266 | return {next, std::errc{}}; 267 | } else if (detail::check_delimiter(next, last, '.')) { 268 | if (next = detail::from_chars(++next, last, prerelease_number); next == last) { 269 | return {next, std::errc{}}; 270 | } 271 | } 272 | } 273 | } 274 | } 275 | 276 | return {first, std::errc::invalid_argument}; 277 | } 278 | 279 | [[nodiscard]] constexpr detail::to_chars_result to_chars(char* first, char* last) const noexcept { 280 | const auto length = chars_length(); 281 | if (first == nullptr || last == nullptr || (last - first) < length) { 282 | return {last, std::errc::value_too_large}; 283 | } 284 | 285 | auto next = first + length; 286 | if (prerelease_type != prerelease::none) { 287 | if (prerelease_number != 0) { 288 | next = detail::to_chars(next, prerelease_number); 289 | } 290 | next = detail::to_chars(next, prerelease_type); 291 | } 292 | next = detail::to_chars(next, patch); 293 | next = detail::to_chars(next, minor); 294 | next = detail::to_chars(next, major, false); 295 | 296 | return {first + length, std::errc{}}; 297 | } 298 | 299 | [[nodiscard]] constexpr bool from_string_noexcept(std::string_view str) noexcept { 300 | return from_chars(str.data(), str.data() + str.length()); 301 | } 302 | 303 | constexpr version& from_string(std::string_view str) { 304 | if (!from_string_noexcept(str)) { 305 | __SEMVER_THROW(std::invalid_argument{"semver::version::from_string() invalid version."}); 306 | } 307 | 308 | return *this; 309 | } 310 | 311 | [[nodiscard]] std::string to_string() const { 312 | auto str = std::string(chars_length(), '\0'); 313 | if (!to_chars(str.data(), str.data() + str.length())) { 314 | __SEMVER_THROW(std::invalid_argument{"semver::version::to_string() invalid version."}); 315 | } 316 | 317 | return str; 318 | } 319 | 320 | [[nodiscard]] constexpr int compare(const version& other) const noexcept { 321 | if (major != other.major) { 322 | return major - other.major; 323 | } 324 | 325 | if (minor != other.minor) { 326 | return minor - other.minor; 327 | } 328 | 329 | if (patch != other.patch) { 330 | return patch - other.patch; 331 | } 332 | 333 | if (prerelease_type != other.prerelease_type) { 334 | return static_cast(prerelease_type) - static_cast(other.prerelease_type); 335 | } 336 | 337 | if (prerelease_number != other.prerelease_number) { 338 | return prerelease_number - other.prerelease_number; 339 | } 340 | 341 | return 0; 342 | } 343 | 344 | private: 345 | constexpr std::uint8_t chars_length() const noexcept { 346 | // () + 1(.) + () + 1(.) + () 347 | std::uint8_t length = detail::length(major) + detail::length(minor) + detail::length(patch) + 2; 348 | if (prerelease_type != prerelease::none) { 349 | // + 1(-) + () 350 | length += detail::length(prerelease_type) + 1; 351 | if (prerelease_number != 0) { 352 | // + 1(.) + () 353 | length += detail::length(prerelease_number) + 1; 354 | } 355 | } 356 | return length; 357 | } 358 | }; 359 | 360 | [[nodiscard]] constexpr bool operator==(const version& lhs, const version& rhs) noexcept { 361 | return lhs.compare(rhs) == 0; 362 | } 363 | 364 | [[nodiscard]] constexpr bool operator!=(const version& lhs, const version& rhs) noexcept { 365 | return lhs.compare(rhs) != 0; 366 | } 367 | 368 | [[nodiscard]] constexpr bool operator>(const version& lhs, const version& rhs) noexcept { 369 | return lhs.compare(rhs) > 0; 370 | } 371 | 372 | [[nodiscard]] constexpr bool operator>=(const version& lhs, const version& rhs) noexcept { 373 | return lhs.compare(rhs) >= 0; 374 | } 375 | 376 | [[nodiscard]] constexpr bool operator<(const version& lhs, const version& rhs) noexcept { 377 | return lhs.compare(rhs) < 0; 378 | } 379 | 380 | [[nodiscard]] constexpr bool operator<=(const version& lhs, const version& rhs) noexcept { 381 | return lhs.compare(rhs) <= 0; 382 | } 383 | 384 | [[nodiscard]] constexpr version operator""_version(const char* str, std::size_t length) { 385 | return version{std::string_view{str, length}}; 386 | } 387 | 388 | [[nodiscard]] constexpr detail::from_chars_result from_chars(const char* first, const char* last, version& v) noexcept { 389 | return v.from_chars(first, last); 390 | } 391 | 392 | [[nodiscard]] constexpr detail::to_chars_result to_chars(char* first, char* last, const version& v) noexcept { 393 | return v.to_chars(first, last); 394 | } 395 | 396 | [[nodiscard]] constexpr std::optional from_string_noexcept(std::string_view str) noexcept { 397 | if (auto v = version{0, 0, 0}; v.from_string_noexcept(str)) { 398 | return v; 399 | } 400 | 401 | return std::nullopt; 402 | } 403 | 404 | [[nodiscard]] constexpr version from_string(std::string_view str) { 405 | return version{str}; 406 | } 407 | 408 | [[nodiscard]] inline std::string to_string(const version& v) { 409 | return v.to_string(); 410 | } 411 | 412 | template 413 | inline std::basic_ostream& operator<<(std::basic_ostream& os, const version& v) { 414 | for (const auto c : v.to_string()) { 415 | os.put(c); 416 | } 417 | 418 | return os; 419 | } 420 | 421 | } // namespace semver 422 | 423 | #if defined(__clang__) 424 | # pragma clang diagnostic pop 425 | #endif 426 | 427 | #endif // NEARGYE_SEMANTIC_VERSIONING_HPP 428 | -------------------------------------------------------------------------------- /lib/EuroScope/EuroScopePlugInDll.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MorpheusXAUT/DelHel/7c574eedd1dbc14e04a888557aa4eaeb4f03bf13/lib/EuroScope/EuroScopePlugInDll.lib -------------------------------------------------------------------------------- /routing.json: -------------------------------------------------------------------------------- 1 | { 2 | "LOWW": { 3 | "entry": [ 4 | { 5 | "name": "SOVIL", 6 | "routes": [ 7 | { 8 | "icao": "LOWL", 9 | "maxlvl": 165, 10 | "minlvl": 60, 11 | "waypoints": [ 12 | "SITNI" 13 | ] 14 | }, 15 | { 16 | "icao": "LOWS", 17 | "maxlvl": 165, 18 | "minlvl": 60, 19 | "waypoints": [ 20 | "SITNI", 21 | "BAGSI", 22 | "MATIG" 23 | ] 24 | }, 25 | { 26 | "icao": "LOWI", 27 | "maxlvl": 225, 28 | "minlvl": 100, 29 | "waypoints": [ 30 | "SITNI", 31 | "BAGSI", 32 | "MATIG", 33 | "SBG" 34 | ] 35 | }, 36 | { 37 | "icao": "LOWI", 38 | "maxlvl": 305, 39 | "minlvl": 100, 40 | "waypoints": [ 41 | "SITNI", 42 | "BAGSI", 43 | "MATIG", 44 | "NANIT" 45 | ] 46 | }, 47 | { 48 | "icao": "LOWI", 49 | "maxlvl": 305, 50 | "minlvl": 110, 51 | "waypoints": [ 52 | "SITNI", 53 | "BAGSI", 54 | "NANIT" 55 | ] 56 | }, 57 | { 58 | "icao": "EDDM", 59 | "maxlvl": 305, 60 | "minlvl": 60, 61 | "waypoints": [ 62 | "SITNI", 63 | "BAGSI", 64 | "MATIG", 65 | "AMADI" 66 | ] 67 | }, 68 | { 69 | "icao": "", 70 | "maxlvl": 660, 71 | "minlvl": 60, 72 | "waypoints": [ 73 | "SITNI", 74 | "BAGSI", 75 | "MATIG", 76 | "AMADI" 77 | ] 78 | }, 79 | { 80 | "icao": "", 81 | "maxlvl": 660, 82 | "minlvl": 100, 83 | "waypoints": [ 84 | "SITNI", 85 | "BAGSI", 86 | "MATIG", 87 | "TITIG" 88 | ] 89 | }, 90 | { 91 | "icao": "", 92 | "maxlvl": 660, 93 | "minlvl": 100, 94 | "waypoints": [ 95 | "SITNI", 96 | "BAGSI", 97 | "MATIG", 98 | "TRAUN" 99 | ] 100 | }, 101 | { 102 | "icao": "", 103 | "maxlvl": 660, 104 | "minlvl": 100, 105 | "waypoints": [ 106 | "SITNI", 107 | "BAGSI", 108 | "NANIT" 109 | ] 110 | } 111 | ] 112 | }, 113 | { 114 | "name": "RUPET", 115 | "routes": [ 116 | { 117 | "icao": "LOWG", 118 | "maxlvl": 145, 119 | "minlvl": 80, 120 | "waypoints": [] 121 | }, 122 | { 123 | "icao": "LOWK", 124 | "maxlvl": 160, 125 | "minlvl": 95, 126 | "waypoints": [ 127 | "ABIRI" 128 | ] 129 | }, 130 | { 131 | "icao": "", 132 | "maxlvl": 660, 133 | "minlvl": 135, 134 | "waypoints": [ 135 | "GRZ" 136 | ] 137 | } 138 | ] 139 | }, 140 | { 141 | "name": "LUGEM", 142 | "routes": [ 143 | { 144 | "icao": "EDDS", 145 | "maxlvl": 345, 146 | "minlvl": 60, 147 | "waypoints": [ 148 | "SUBEN" 149 | ] 150 | }, 151 | { 152 | "icao": "EDDS", 153 | "maxlvl": 345, 154 | "minlvl": 60, 155 | "waypoints": [ 156 | "KIRDI" 157 | ] 158 | }, 159 | { 160 | "icao": "", 161 | "maxlvl": 660, 162 | "minlvl": 60, 163 | "waypoints": [ 164 | "KIRDI" 165 | ] 166 | }, 167 | { 168 | "icao": "", 169 | "maxlvl": 660, 170 | "minlvl": 60, 171 | "waypoints": [ 172 | "SUBEN" 173 | ] 174 | } 175 | ] 176 | }, 177 | { 178 | "name": "OSPEN", 179 | "routes": [ 180 | { 181 | "icao": "LOWI", 182 | "maxlvl": 305, 183 | "minlvl": 145, 184 | "waypoints": [ 185 | "ABRUK", 186 | "SETAL", 187 | "NANIT" 188 | ] 189 | }, 190 | { 191 | "icao": "", 192 | "maxlvl": 660, 193 | "minlvl": 145, 194 | "waypoints": [ 195 | "ABRUK", 196 | "SETAL", 197 | "ERKIR" 198 | ] 199 | }, 200 | { 201 | "icao": "", 202 | "maxlvl": 660, 203 | "minlvl": 145, 204 | "waypoints": [ 205 | "ABRUK", 206 | "SETAL", 207 | "UMVEG" 208 | ] 209 | }, 210 | { 211 | "icao": "", 212 | "maxlvl": 660, 213 | "minlvl": 145, 214 | "waypoints": [ 215 | "ABRUK", 216 | "SETAL", 217 | "DETSA" 218 | ] 219 | }, 220 | { 221 | "icao": "", 222 | "maxlvl": 660, 223 | "minlvl": 145, 224 | "waypoints": [ 225 | "ABRUK", 226 | "SETAL", 227 | "URAVA" 228 | ] 229 | } 230 | ] 231 | }, 232 | { 233 | "name": "MEDIX", 234 | "routes": [ 235 | { 236 | "icao": "", 237 | "maxlvl": 660, 238 | "minlvl": 60, 239 | "waypoints": [ 240 | "RENKA" 241 | ] 242 | }, 243 | { 244 | "icao": "", 245 | "maxlvl": 660, 246 | "minlvl": 60, 247 | "waypoints": [ 248 | "LAMSI" 249 | ] 250 | } 251 | ] 252 | }, 253 | { 254 | "name": "BUWUT", 255 | "routes": [ 256 | { 257 | "icao": "", 258 | "maxlvl": 660, 259 | "minlvl": 100, 260 | "waypoints": [ 261 | "DITIS" 262 | ] 263 | } 264 | ] 265 | }, 266 | { 267 | "name": "LANUX", 268 | "routes": [ 269 | { 270 | "icao": "LKPR", 271 | "maxlvl": 185, 272 | "minlvl": 95, 273 | "waypoints": [] 274 | }, 275 | { 276 | "icao": "", 277 | "maxlvl": 660, 278 | "minlvl": 95, 279 | "waypoints": [] 280 | } 281 | ] 282 | }, 283 | { 284 | "name": "LEDVA", 285 | "routes": [ 286 | { 287 | "icao": "LKTB", 288 | "maxlvl": 125, 289 | "minlvl": 95, 290 | "waypoints": [] 291 | }, 292 | { 293 | "icao": "", 294 | "maxlvl": 660, 295 | "minlvl": 95, 296 | "waypoints": [] 297 | } 298 | ] 299 | }, 300 | { 301 | "name": "KOXER", 302 | "routes": [ 303 | { 304 | "icao": "LZIB", 305 | "maxlvl": 75, 306 | "minlvl": 50, 307 | "waypoints": [] 308 | }, 309 | { 310 | "icao": "", 311 | "maxlvl": 660, 312 | "minlvl": 100, 313 | "waypoints": [] 314 | } 315 | ] 316 | }, 317 | { 318 | "name": "ADAMA", 319 | "routes": [ 320 | { 321 | "icao": "", 322 | "maxlvl": 660, 323 | "minlvl": 50, 324 | "waypoints": [] 325 | } 326 | ] 327 | }, 328 | { 329 | "name": "ARSIN", 330 | "routes": [ 331 | { 332 | "icao": "", 333 | "maxlvl": 660, 334 | "minlvl": 95, 335 | "waypoints": [] 336 | } 337 | ] 338 | }, 339 | { 340 | "name": "STEIN", 341 | "routes": [ 342 | { 343 | "icao": "", 344 | "maxlvl": 660, 345 | "minlvl": 95, 346 | "waypoints": [] 347 | } 348 | ] 349 | }, 350 | { 351 | "name": "SNU", 352 | "routes": [ 353 | { 354 | "icao": "", 355 | "maxlvl": 100, 356 | "minlvl": 95, 357 | "waypoints": [ 358 | "STO" 359 | ] 360 | }, 361 | { 362 | "icao": "", 363 | "maxlvl": 100, 364 | "minlvl": 95, 365 | "waypoints": [ 366 | "GBG" 367 | ] 368 | }, 369 | { 370 | "icao": "", 371 | "maxlvl": 660, 372 | "minlvl": 100, 373 | "waypoints": [] 374 | } 375 | ] 376 | } 377 | ] 378 | }, 379 | "LOWI": { 380 | "entry": [ 381 | { 382 | "name": "UNKEN", 383 | "routes": [ 384 | { 385 | "icao": "LOWW", 386 | "maxlvl": 295, 387 | "minlvl": 100, 388 | "waypoints": [] 389 | }, 390 | { 391 | "icao": "LOWS", 392 | "maxlvl": 125, 393 | "minlvl": 100, 394 | "waypoints": [] 395 | }, 396 | { 397 | "icao": "LOWL", 398 | "maxlvl": 165, 399 | "minlvl": 100, 400 | "waypoints": [] 401 | }, 402 | { 403 | "icao": "", 404 | "maxlvl": 660, 405 | "minlvl": 100, 406 | "waypoints": [] 407 | } 408 | ] 409 | }, 410 | { 411 | "name": "RTT", 412 | "routes": [ 413 | { 414 | "icao": "LOWW", 415 | "maxlvl": 295, 416 | "minlvl": 100, 417 | "waypoints": [ 418 | "NEMAL" 419 | ] 420 | }, 421 | { 422 | "icao": "LOWS", 423 | "maxlvl": 125, 424 | "minlvl": 100, 425 | "waypoints": [] 426 | }, 427 | { 428 | "icao": "LOWL", 429 | "maxlvl": 165, 430 | "minlvl": 100, 431 | "waypoints": [] 432 | }, 433 | { 434 | "icao": "", 435 | "maxlvl": 660, 436 | "minlvl": 100, 437 | "waypoints": [] 438 | } 439 | ] 440 | }, 441 | { 442 | "name": "OBEDI", 443 | "routes": [ 444 | { 445 | "icao": "", 446 | "maxlvl": 660, 447 | "minlvl": 145, 448 | "waypoints": [] 449 | } 450 | ] 451 | }, 452 | { 453 | "name": "KOGOL", 454 | "routes": [ 455 | { 456 | "icao": "", 457 | "maxlvl": 660, 458 | "minlvl": 100, 459 | "waypoints": [] 460 | } 461 | ] 462 | }, 463 | { 464 | "name": "BRENO", 465 | "routes": [ 466 | { 467 | "icao": "", 468 | "maxlvl": 660, 469 | "minlvl": 160, 470 | "waypoints": [] 471 | } 472 | ] 473 | }, 474 | { 475 | "name": "ADILO", 476 | "routes": [ 477 | { 478 | "icao": "", 479 | "maxlvl": 660, 480 | "minlvl": 165, 481 | "waypoints": [] 482 | } 483 | ] 484 | }, 485 | { 486 | "name": "KPT", 487 | "routes": [ 488 | { 489 | "icao": "", 490 | "maxlvl": 660, 491 | "minlvl": 165, 492 | "waypoints": [] 493 | } 494 | ] 495 | } 496 | ] 497 | }, 498 | "LOWG": { 499 | "entry": [ 500 | { 501 | "name": "MILGO", 502 | "routes": [ 503 | { 504 | "icao": "EDDM", 505 | "maxlvl": 305, 506 | "minlvl": 110, 507 | "waypoints": [ 508 | "MILGO", 509 | "VATET", 510 | "REDBU" 511 | ] 512 | }, 513 | { 514 | "icao": "LOWL", 515 | "maxlvl": 165, 516 | "minlvl": 110, 517 | "waypoints": [ 518 | "MILGO", 519 | "LIMRA" 520 | ] 521 | }, 522 | { 523 | "icao": "", 524 | "maxlvl": 660, 525 | "minlvl": 110, 526 | "waypoints": [] 527 | } 528 | ] 529 | }, 530 | { 531 | "name": "ROPAG", 532 | "routes": [ 533 | { 534 | "icao": "LOWW", 535 | "maxlvl": 145, 536 | "minlvl": 70, 537 | "waypoints": [ 538 | "NIGSI" 539 | ] 540 | }, 541 | { 542 | "icao": "", 543 | "maxlvl": 660, 544 | "minlvl": 70, 545 | "waypoints": [] 546 | } 547 | ] 548 | }, 549 | { 550 | "name": "GOTAR", 551 | "routes": [ 552 | { 553 | "icao": "", 554 | "maxlvl": 660, 555 | "minlvl": 70, 556 | "waypoints": [] 557 | } 558 | ] 559 | }, 560 | { 561 | "name": "GBG", 562 | "routes": [ 563 | { 564 | "icao": "", 565 | "maxlvl": 660, 566 | "minlvl": 50, 567 | "waypoints": [] 568 | } 569 | ] 570 | }, 571 | { 572 | "name": "MUREG", 573 | "routes": [ 574 | { 575 | "icao": "", 576 | "maxlvl": 660, 577 | "minlvl": 125, 578 | "waypoints": [] 579 | } 580 | ] 581 | }, 582 | { 583 | "name": "GOLVA", 584 | "routes": [ 585 | { 586 | "icao": "", 587 | "maxlvl": 660, 588 | "minlvl": 125, 589 | "waypoints": [] 590 | } 591 | ] 592 | }, 593 | { 594 | "name": "RADLY", 595 | "routes": [ 596 | { 597 | "icao": "", 598 | "maxlvl": 660, 599 | "minlvl": 85, 600 | "waypoints": [] 601 | } 602 | ] 603 | }, 604 | { 605 | "name": "ABIRI", 606 | "routes": [ 607 | { 608 | "icao": "LOWK", 609 | "maxlvl": 125, 610 | "minlvl": 90, 611 | "waypoints": [] 612 | }, 613 | { 614 | "icao": "", 615 | "maxlvl": 660, 616 | "minlvl": 90, 617 | "waypoints": [] 618 | } 619 | ] 620 | } 621 | ] 622 | }, 623 | "LOWK": { 624 | "entry": [ 625 | { 626 | "name": "INGID", 627 | "routes": [ 628 | { 629 | "icao": "EDDM", 630 | "maxlvl": 245, 631 | "minlvl": 60, 632 | "waypoints": [ 633 | "RASTA", 634 | "REDBU" 635 | ] 636 | }, 637 | { 638 | "icao": "LOWL", 639 | "maxlvl": 165, 640 | "minlvl": 60, 641 | "waypoints": [] 642 | }, 643 | { 644 | "icao": "", 645 | "maxlvl": 660, 646 | "minlvl": 60, 647 | "waypoints": [] 648 | } 649 | ] 650 | }, 651 | { 652 | "name": "ABIRI", 653 | "routes": [ 654 | { 655 | "icao": "LOWW", 656 | "maxlvl": 165, 657 | "minlvl": 60, 658 | "waypoints": [ 659 | "NIGSI" 660 | ] 661 | }, 662 | { 663 | "icao": "LOWL", 664 | "maxlvl": 165, 665 | "minlvl": 60, 666 | "waypoints": [] 667 | }, 668 | { 669 | "icao": "LOWG", 670 | "maxlvl": 125, 671 | "minlvl": 60, 672 | "waypoints": [] 673 | }, 674 | { 675 | "icao": "", 676 | "maxlvl": 660, 677 | "minlvl": 60, 678 | "waypoints": [] 679 | } 680 | ] 681 | }, 682 | { 683 | "name": "KLAGY", 684 | "routes": [ 685 | { 686 | "icao": "", 687 | "maxlvl": 660, 688 | "minlvl": 60, 689 | "waypoints": [] 690 | } 691 | ] 692 | }, 693 | { 694 | "name": "BERTA", 695 | "routes": [ 696 | { 697 | "icao": "", 698 | "maxlvl": 660, 699 | "minlvl": 60, 700 | "waypoints": [] 701 | } 702 | ] 703 | }, 704 | { 705 | "name": "REKTI", 706 | "routes": [ 707 | { 708 | "icao": "", 709 | "maxlvl": 660, 710 | "minlvl": 60, 711 | "waypoints": [] 712 | } 713 | ] 714 | }, 715 | { 716 | "name": "VILAK", 717 | "routes": [ 718 | { 719 | "icao": "", 720 | "maxlvl": 660, 721 | "minlvl": 60, 722 | "waypoints": [] 723 | } 724 | ] 725 | }, 726 | { 727 | "name": "KFT", 728 | "routes": [ 729 | { 730 | "icao": "", 731 | "maxlvl": 660, 732 | "minlvl": 60, 733 | "waypoints": [] 734 | } 735 | ] 736 | } 737 | ] 738 | }, 739 | "LOWL": { 740 | "entry": [ 741 | { 742 | "name": "PETEN", 743 | "routes": [ 744 | { 745 | "icao": "", 746 | "maxlvl": 660, 747 | "minlvl": 60, 748 | "waypoints": [] 749 | } 750 | ] 751 | }, 752 | { 753 | "name": "PEROL", 754 | "routes": [ 755 | { 756 | "icao": "LOWW", 757 | "maxlvl": 165, 758 | "minlvl": 60, 759 | "waypoints": [] 760 | }, 761 | { 762 | "icao": "", 763 | "maxlvl": 660, 764 | "minlvl": 60, 765 | "waypoints": [] 766 | } 767 | ] 768 | }, 769 | { 770 | "name": "LIMRA", 771 | "routes": [ 772 | { 773 | "icao": "LOWG", 774 | "maxlvl": 165, 775 | "minlvl": 60, 776 | "waypoints": [ 777 | "LEOBE" 778 | ] 779 | }, 780 | { 781 | "icao": "LOWK", 782 | "maxlvl": 165, 783 | "minlvl": 60, 784 | "waypoints": [] 785 | }, 786 | { 787 | "icao": "", 788 | "maxlvl": 660, 789 | "minlvl": 60, 790 | "waypoints": [] 791 | } 792 | ] 793 | }, 794 | { 795 | "name": "LIDSI", 796 | "routes": [ 797 | { 798 | "icao": "LOWI", 799 | "maxlvl": 165, 800 | "minlvl": 60, 801 | "waypoints": [] 802 | }, 803 | { 804 | "icao": "LOWS", 805 | "maxlvl": 125, 806 | "minlvl": 60, 807 | "waypoints": [ 808 | "MATIG" 809 | ] 810 | }, 811 | { 812 | "icao": "EDDM", 813 | "maxlvl": 125, 814 | "minlvl": 60, 815 | "waypoints": [ 816 | "MATIG", 817 | "AMADI" 818 | ] 819 | }, 820 | { 821 | "icao": "", 822 | "maxlvl": 660, 823 | "minlvl": 60, 824 | "waypoints": [] 825 | } 826 | ] 827 | }, 828 | { 829 | "name": "LNZ", 830 | "routes": [ 831 | { 832 | "icao": "", 833 | "maxlvl": 95, 834 | "minlvl": 60, 835 | "waypoints": [ 836 | "J21" 837 | ] 838 | }, 839 | { 840 | "icao": "", 841 | "maxlvl": 660, 842 | "minlvl": 60, 843 | "waypoints": [] 844 | } 845 | ] 846 | } 847 | ] 848 | }, 849 | "LOWS": { 850 | "entry": [ 851 | { 852 | "name": "INROM", 853 | "routes": [ 854 | { 855 | "icao": "LOWW", 856 | "maxlvl": 165, 857 | "minlvl": 60, 858 | "waypoints": [ 859 | "MASUR" 860 | ] 861 | }, 862 | { 863 | "icao": "", 864 | "maxlvl": 660, 865 | "minlvl": 60, 866 | "waypoints": [] 867 | } 868 | ] 869 | }, 870 | { 871 | "name": "NEMAL", 872 | "routes": [ 873 | { 874 | "icao": "LOWW", 875 | "maxlvl": 165, 876 | "minlvl": 80, 877 | "waypoints": [ 878 | "MASUR" 879 | ] 880 | }, 881 | { 882 | "icao": "LOWL", 883 | "maxlvl": 125, 884 | "minlvl": 80, 885 | "waypoints": [] 886 | }, 887 | { 888 | "icao": "", 889 | "maxlvl": 660, 890 | "minlvl": 80, 891 | "waypoints": [] 892 | } 893 | ] 894 | }, 895 | { 896 | "name": "VERDA", 897 | "routes": [ 898 | { 899 | "icao": "", 900 | "maxlvl": 660, 901 | "minlvl": 60, 902 | "waypoints": [] 903 | } 904 | ] 905 | }, 906 | { 907 | "name": "DETSA", 908 | "routes": [ 909 | { 910 | "icao": "", 911 | "maxlvl": 660, 912 | "minlvl": 145, 913 | "waypoints": [] 914 | } 915 | ] 916 | }, 917 | { 918 | "name": "RTT", 919 | "routes": [ 920 | { 921 | "icao": "", 922 | "maxlvl": 125, 923 | "minlvl": 100, 924 | "waypoints": [] 925 | }, 926 | { 927 | "icao": "", 928 | "maxlvl": 660, 929 | "minlvl": 100, 930 | "waypoints": [] 931 | } 932 | ] 933 | }, 934 | { 935 | "name": "TRAUN", 936 | "routes": [ 937 | { 938 | "icao": "", 939 | "maxlvl": 660, 940 | "minlvl": 60, 941 | "waypoints": [] 942 | } 943 | ] 944 | }, 945 | { 946 | "name": "TITIG", 947 | "routes": [ 948 | { 949 | "icao": "EDDM", 950 | "maxlvl": 125, 951 | "minlvl": 60, 952 | "waypoints": [] 953 | }, 954 | { 955 | "icao": "", 956 | "maxlvl": 660, 957 | "minlvl": 60, 958 | "waypoints": [] 959 | } 960 | ] 961 | }, 962 | { 963 | "name": "SBG", 964 | "routes": [ 965 | { 966 | "icao": "LOWL", 967 | "maxlvl": 125, 968 | "minlvl": 60, 969 | "waypoints": [] 970 | }, 971 | { 972 | "icao": "", 973 | "maxlvl": 95, 974 | "minlvl": 60, 975 | "waypoints": [ 976 | "J21" 977 | ] 978 | }, 979 | { 980 | "icao": "", 981 | "maxlvl": 660, 982 | "minlvl": 60, 983 | "waypoints": [] 984 | } 985 | ] 986 | } 987 | ] 988 | } 989 | } -------------------------------------------------------------------------------- /version.txt: -------------------------------------------------------------------------------- 1 | 0.3.0 --------------------------------------------------------------------------------