diff --git a/.editorconfig b/.editorconfig index 677ee245..f90f6abb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,5 +10,5 @@ indent_style = space [*.md] trim_trailing_whitespace = false -[*.{json,yaml,yml}] +[*.{json,toml,yaml,yml}] indent_size = 2 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..f0f92877 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,12 @@ +# Default code owners unless overridden below +* @MorpheusXAUT @IamLuisAUT + +# Explicitly specify code owners for GitHub config, such as workflows or CODEOWNERS, and other relevant files +/.github/ @MorpheusXAUT @IamLuisAUT +/docs/ @MorpheusXAUT @IamLuisAUT +LICENSE* @MorpheusXAUT @IamLuisAUT +README.md @MorpheusXAUT @IamLuisAUT +CONTRIBUTING.md @MorpheusXAUT @IamLuisAUT + +# Dataset owners +/dataset/LO/ @MorpheusXAUT @IamLuisAUT diff --git a/.github/airac-cycles.yaml b/.github/airac-cycles.yml similarity index 100% rename from .github/airac-cycles.yaml rename to .github/airac-cycles.yml diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..b7f469cc --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,40 @@ +# Dataset Changes + +Thank you for your contribution to the `vacs` dataset. + +> [!WARNING] +> Please make sure your dataset is in the correct `dataset/{FIR}/` directory, otherwise it will not be picked up by our validations and release process. + +If you're contributing configuration for a new FIR, please add a small paragraph about your affiliation to the respective FIR to this PR description. We'll be glad to accept your contribution, but would like to ensure our dataset's origin is transparent. +After the first PR was merged, we'll add you to the CODEOWNERS file for that FIR, so that future changes can be reviewed by you. + +If you're contributing for an existing FIR, we'll wait for feedback from the current CODEOWNER(s) before merging. + +_You may just delete these paragraphs from your PR description, they're just here to remind you of our contribution guidelines._ + +## Auto-Formatting + +This repository uses [autofix.ci](https://autofix.ci) to automatically ensure consistent code formatting using our [prettier](https://prettier.io) configuration. +The `autofix-ci` bot will automatically push formatting fixes to your branch if needed. + +> [!IMPORTANT] +> Please **enable "Allow edits by maintainers"** so that the bot can push commits to your PR branch. + +Look for the **"Allow edits by maintainers"** checkbox when creating the PR (below the description box or in the right sidebar). Please ensure this checkbox is checked before submitting the PR. + +The `autofix-ci` bot will automatically push formatting fixes to your branch if needed (commits will have the message `chore(dataset): format with prettier`). + +
+If you prefer not to enable this setting: + +Make sure your dataset changes are formatted properly. +If your editor supports `prettier`, formatting should happen automatically. + +**Malformed dataset files will automatically be rejected by CI.** + +Alternatively, you can run our formatter locally: + +1. Install [Node.js](https://nodejs.org/en/download) +2. Run `npm install` to install `prettier` and the required plugins +3. Run `npm run format` to format your dataset changes +
diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml new file mode 100644 index 00000000..b06b7527 --- /dev/null +++ b/.github/workflows/autofix.yml @@ -0,0 +1,40 @@ +name: autofix.ci +on: + pull_request: + paths: + - "dataset/**" + - ".github/workflows/autofix.yml" + push: + branches: ["main"] + paths: + - "dataset/**" + - ".github/workflows/autofix.yml" + +permissions: + contents: read + +jobs: + autofix: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Setup Node (LTS) + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 + with: + node-version: "lts/*" + cache: "npm" + cache-dependency-path: | + package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Format dataset + run: npm run format + + - uses: autofix-ci/action@7a166d7532b277f34e16238930461bf77f9d7ed8 # v1.3.3 + with: + commit-message: "chore(dataset): format with prettier" diff --git a/.github/workflows/ci-dataset.yml b/.github/workflows/ci-dataset.yml index d9543732..0017c6d2 100644 --- a/.github/workflows/ci-dataset.yml +++ b/.github/workflows/ci-dataset.yml @@ -22,13 +22,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Rust toolchain uses: dtolnay/rust-toolchain@stable - name: Cache cargo - uses: Swatinem/rust-cache@v2.8.2 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: shared-key: "linux-x86_64" workspaces: "tools -> target" diff --git a/.github/workflows/ci-rust.yml b/.github/workflows/ci-rust.yml index 8c313afc..5a7b5438 100644 --- a/.github/workflows/ci-rust.yml +++ b/.github/workflows/ci-rust.yml @@ -43,13 +43,13 @@ jobs: continue-on-error: ${{ matrix.checks == 'advisories' }} steps: - name: Checkout - uses: actions/checkout@v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - name: cargo deny - uses: EmbarkStudios/cargo-deny-action@v2.0.15 + uses: EmbarkStudios/cargo-deny-action@3fd3802e88374d3fe9159b834c7714ec57d6c979 # v2.0.15 with: manifest-path: "tools/Cargo.toml" command: check ${{ matrix.checks }} @@ -60,20 +60,20 @@ jobs: continue-on-error: true steps: - name: Checkout - uses: actions/checkout@v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - name: cargo machete - uses: bnjbvr/cargo-machete@v0.9.1 + uses: bnjbvr/cargo-machete@7959c845782fed02ee69303126d4a12d64f1db18 # v0.9.1 check: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Rust toolchain uses: dtolnay/rust-toolchain@stable @@ -81,7 +81,7 @@ jobs: components: clippy, rustfmt - name: Cache cargo - uses: Swatinem/rust-cache@v2.8.2 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: shared-key: "linux-x86_64" workspaces: "tools -> target" diff --git a/.github/workflows/release-tools.yml b/.github/workflows/release-tools.yml index c3319745..7d1884e9 100644 --- a/.github/workflows/release-tools.yml +++ b/.github/workflows/release-tools.yml @@ -37,7 +37,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 ref: ${{ inputs.ref }} @@ -91,12 +91,12 @@ jobs: release_body: ${{ steps.git-cliff.outputs.content }} steps: - name: Checkout - uses: actions/checkout@v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - name: Generate a changelog - uses: orhun/git-cliff-action@v4.7.0 + uses: orhun/git-cliff-action@e16f179f0be49ecdfe63753837f20b9531642772 # v4.7.0 id: git-cliff with: config: tools/cliff.toml @@ -126,7 +126,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false @@ -137,7 +137,7 @@ jobs: targets: ${{ matrix.target }} - name: Cache cargo - uses: Swatinem/rust-cache@v2.8.2 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: shared-key: ${{ matrix.platform }} workspaces: "tools -> target" @@ -161,7 +161,7 @@ jobs: mv "target/${{ matrix.target }}/release/${{ steps.vars.outputs.binary_name }}" "target/${{ matrix.target }}/release/${{ steps.vars.outputs.release_binary_name }}" - name: Upload binary artifact - uses: actions/upload-artifact@v6.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: vacs-data-${{ matrix.platform }} if-no-files-found: error @@ -171,7 +171,7 @@ jobs: - name: Attest build provenance if: ${{ !github.event.repository.private || github.event.repository.owner.type == 'Organization' }} - uses: actions/attest-build-provenance@v3.2.0 + uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3.2.0 with: subject-path: | tools/target/${{ matrix.target }}/release/${{ steps.vars.outputs.release_binary_name }} @@ -184,7 +184,7 @@ jobs: contents: write steps: - name: Download binaries - uses: actions/download-artifact@v7.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 #v7.0.0 with: path: dist pattern: vacs-data-* @@ -199,7 +199,7 @@ jobs: done - name: Create/update GitHub release - uses: softprops/action-gh-release@v2.5.0 + uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 with: tag_name: ${{ needs.prep.outputs.tag }} name: ${{ needs.prep.outputs.release_name }} diff --git a/.github/workflows/tag-airac.yml b/.github/workflows/tag-airac.yml index e1d05b82..5f4d4a07 100644 --- a/.github/workflows/tag-airac.yml +++ b/.github/workflows/tag-airac.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check for AIRAC cycle id: check diff --git a/.gitignore b/.gitignore index 83f22a2c..60f5be93 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,jetbrains,windows,linux,macos -# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,jetbrains,windows,linux,macos +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,jetbrains,windows,linux,macos,node +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,jetbrains,windows,linux,macos,node ### JetBrains ### # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider @@ -162,6 +162,146 @@ Temporary Items # iCloud generated files *.icloud +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + ### VisualStudioCode ### .vscode/* !.vscode/settings.json @@ -207,4 +347,4 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,jetbrains,windows,linux,macos +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,jetbrains,windows,linux,macos,node diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml new file mode 100644 index 00000000..cb4d9aeb --- /dev/null +++ b/.idea/jsonSchemas.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/prettier.xml b/.idea/prettier.xml new file mode 100644 index 00000000..0b9f16d4 --- /dev/null +++ b/.idea/prettier.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..6a5598cc --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +*.rs +**/Cargo.toml +**/Cargo.lock +**/target/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..76ad26d3 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,17 @@ +{ + "plugins": ["prettier-plugin-toml"], + "overrides": [ + { + "files": "*.json", + "options": { "parser": "json" } + }, + { + "files": "*.toml", + "options": { "parser": "toml" } + }, + { + "files": "*.md", + "options": { "parser": "markdown" } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 93fbc00e..b075f3be 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,21 +1,15 @@ { "json.schemas": [ { - "fileMatch": [ - "dataset/**/profiles/*.json" - ], + "fileMatch": ["dataset/**/profiles/*.json"], "url": "./docs/schemas/profiles.schema.json" }, { - "fileMatch": [ - "dataset/**/positions.json" - ], + "fileMatch": ["dataset/**/positions.json"], "url": "./docs/schemas/positions.schema.json" }, { - "fileMatch": [ - "dataset/**/stations.json" - ], + "fileMatch": ["dataset/**/stations.json"], "url": "./docs/schemas/stations.schema.json" } ] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a596b6f8..71fb46b3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,71 @@ +## Contributing to the Dataset + +Thank you for your interest in contributing to the `vacs` dataset! + +### Directory Structure + +All dataset contributions must be placed in the correct directory: + +``` +dataset/{FIR}/ +``` + +Where `{FIR}` is the two/four letter Flight Information Region code (e.g., `LO` for Austria, `EDMM` for Munich, etc.). + +> [!IMPORTANT] +> Files outside the correct directory structure will not be picked up by our validation and release process. + +### Auto-Formatting + +This repository uses [autofix.ci](https://autofix.ci) with [Prettier](https://prettier.io) to ensure consistent code formatting. + +When you create a pull request, please **enable "Allow edits by maintainers"**. This allows the autofix.ci bot to automatically push formatting fixes to your PR branch. + +#### Setting up local formatting (optional) + +If you prefer to format files locally before pushing: + +```bash +# Install dependencies (one-time) +npm install + +# Format all dataset files +npm run format + +# Check formatting without modifying +npm run format:check +``` + +If your editor supports Prettier, formatting can happen automatically on save. See our editor configuration in `.vscode/settings.json` for VS Code setup. + +### New FIR Contributions + +If you're contributing configuration for a **new FIR**: + +1. Add your dataset files to `dataset/{FIR}/` +2. In your pull request description, include a brief paragraph about your affiliation to the FIR +3. After your first PR is merged, we'll add you to the `CODEOWNERS` file for that FIR +4. Future changes to your FIR will be routed to you for review + +### Existing FIR Contributions + +If you're contributing to an **existing FIR**: + +- Your PR will be reviewed by the current CODEOWNER(s) for that FIR +- We'll wait for their feedback before merging + +### Validation + +All contributions are automatically validated for: + +- Valid JSON/TOML syntax +- Proper formatting (via Prettier) +- Schema compliance (structure, required fields, data types) + +If validation fails, you'll receive feedback in your pull request with specific error messages. + +--- + ## License for dataset Any contribution intentionally submitted for inclusion in the dataset provided by the `vacs-data` project by you shall be licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) license. diff --git a/README.md b/README.md index 30ce7aff..a309c9df 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,8 @@ We welcome contributions from FIR staff and community members! Please refer to t 1. [Fork the repository](https://github.com/MorpheusXAUT/vacs-data/fork). 2. Make your changes in your FIR's directory (or create it if it doesn't exist). 3. Validate your changes using the provided JSON schemas or the `vacs-data` tool. -4. [Submit a Pull Request](https://github.com/MorpheusXAUT/vacs-data/compare). +4. [Submit a Pull Request](https://github.com/MorpheusXAUT/vacs-data/compare). Please make sure to enable "Allow edits by maintainers" so that the autofix.ci bot can automatically push formatting fixes to your PR branch. +5. Make sure to check our [contribution guidelines](CONTRIBUTING.md) for more information. ## License diff --git a/dataset/LO/profiles/LOVV.json b/dataset/LO/profiles/LOVV.json index 084b14fe..9e50f1e4 100644 --- a/dataset/LO/profiles/LOVV.json +++ b/dataset/LO/profiles/LOVV.json @@ -483,7 +483,44 @@ }, { "label": ["CWP"], - "size": 6.25 + "size": 6.25, + "page": { + "rows": 6, + "client_page": { + "include": [ + "LO*", + "EDMM*", + "EDUU*", + "EDDM*", + "ADR*", + "EUC*", + "LD*", + "LH*", + "LIM*", + "LIP*", + "LJ*", + "LK*", + "LS*", + "LZ*" + ], + "exclude": ["LON*"], + "priority": [ + "LO*_FMP", + "*_FMP", + "LO*_CTR", + "*_CTR", + "LOWW*_APP", + "LO*_APP", + "*_APP", + "LOWW*_TWR", + "LO*_TWR", + "*_TWR", + "LO*_GND", + "*_GND", + "LO*" + ] + } + } } ] } diff --git a/dataset/LO/profiles/LOWW.json b/dataset/LO/profiles/LOWW.json index 656ec1e2..8c52b126 100644 --- a/dataset/LO/profiles/LOWW.json +++ b/dataset/LO/profiles/LOWW.json @@ -8,59 +8,30 @@ "rows": 6, "keys": [ { - "label": [ - "PRA", - "LW", - "EC" - ] + "label": ["PRA", "LW", "EC"] }, { - "label": [ - "PRA", - "LS", - "EC" - ] + "label": ["PRA", "LS", "EC"] }, { - "label": [ - "PRA", - "TB", - "EC" - ] + "label": ["PRA", "TB", "EC"] }, { - "label": [ - "PRA", - "KV", - "EC" - ] + "label": ["PRA", "KV", "EC"] }, { - "label": [ - "APP", - "VD1" - ], + "label": ["APP", "VD1"], "station_id": "LOWW_D_APP" }, { - "label": [ - "TWR 1" - ], + "label": ["TWR 1"], "station_id": "LOWW_TWR" }, { - "label": [ - "BRA", - "LW", - "EC" - ] + "label": ["BRA", "LW", "EC"] }, { - "label": [ - "BRA", - "APP", - "EC" - ] + "label": ["BRA", "APP", "EC"] }, { "label": [] @@ -69,138 +40,77 @@ "label": [] }, { - "label": [ - "APP", - "VD2" - ], + "label": ["APP", "VD2"], "station_id": "LOWW_F_APP" }, { - "label": [ - "TWR 2" - ], + "label": ["TWR 2"], "station_id": "LOWW_E_TWR" }, { - "label": [ - "BUD", - "128.10", - "EC" - ] + "label": ["BUD", "128.10", "EC"] }, { - "label": [ - "BUD", - "133.20", - "EC" - ] + "label": ["BUD", "133.20", "EC"] }, { - "label": [ - "MCC", - "TAU", - "EC" - ] + "label": ["MCC", "TAU", "EC"] }, { "label": [] }, { - "label": [ - "APP", - "TFI", - "EC" - ], + "label": ["APP", "TFI", "EC"], "station_id": "LOWW_I_APP" }, { "label": [] }, { - "label": [ - "ACC", - "N1", - "EC" - ], + "label": ["ACC", "N1", "EC"], "station_id": "LOVV_N1" }, { "label": [] }, { - "label": [ - "LOWL", - "APP", - "EC" - ], + "label": ["LOWL", "APP", "EC"], "station_id": "LOWL_APP" }, { - "label": [ - "APP", - "VN-EC", - "118775" - ], + "label": ["APP", "VN-EC", "118775"], "station_id": "LOWW_N_APP" }, { - "label": [ - "APP", - "VB-EC", - "134675" - ], + "label": ["APP", "VB-EC", "134675"], "station_id": "LOWW_APP" }, { - "label": [ - "TWR 1" - ], + "label": ["TWR 1"], "station_id": "LOWW_TWR" }, { - "label": [ - "ACC", - "E1", - "EC" - ], + "label": ["ACC", "E1", "EC"], "station_id": "LOVV_E1" }, { - "label": [ - "ACC", - "S1", - "EC" - ], + "label": ["ACC", "S1", "EC"], "station_id": "LOVV_S1" }, { - "label": [ - "LOWG", - "APP", - "EC" - ], + "label": ["LOWG", "APP", "EC"], "station_id": "LOWG_APP" }, { - "label": [ - "APP", - "VM-EC", - "125175" - ], + "label": ["APP", "VM-EC", "125175"], "station_id": "LOWW_M_APP" }, { - "label": [ - "APP", - "VP-EC", - "129050" - ], + "label": ["APP", "VP-EC", "129050"], "station_id": "LOWW_P_APP" }, { - "label": [ - "TWR 2" - ], + "label": ["TWR 2"], "station_id": "LOWW_E_TWR" } ] @@ -212,210 +122,104 @@ "rows": 6, "keys": [ { - "label": [ - "PRA", - "LW", - "PLC" - ] + "label": ["PRA", "LW", "PLC"] }, { - "label": [ - "PRA", - "LS", - "PLC" - ] + "label": ["PRA", "LS", "PLC"] }, { - "label": [ - "PRA", - "TB", - "PLC" - ] + "label": ["PRA", "TB", "PLC"] }, { - "label": [ - "PRA", - "KV", - "PLC" - ] + "label": ["PRA", "KV", "PLC"] }, { - "label": [ - "APP", - "SAP" - ] + "label": ["APP", "SAP"] }, { - "label": [ - "ACC", - "N PLC", - "Cont." - ], + "label": ["ACC", "N PLC", "Cont."], "station_id": "LOVV_N1" }, { - "label": [ - "BRA", - "LW", - "PLC" - ] + "label": ["BRA", "LW", "PLC"] }, { - "label": [ - "BRA", - "APP", - "PLC" - ] + "label": ["BRA", "APP", "PLC"] }, { - "label": [ - "BRA", - "FDT1" - ] + "label": ["BRA", "FDT1"] }, { - "label": [ - "ACC", - "FDU", - "NOT" - ] + "label": ["ACC", "FDU", "NOT"] }, { - "label": [ - "FDU 1", - "TCH" - ] + "label": ["FDU 1", "TCH"] }, { - "label": [ - "ACC", - "S PLC", - "Cont." - ], + "label": ["ACC", "S PLC", "Cont."], "station_id": "LOVV_S1" }, { - "label": [ - "BUD", - "128.10", - "PLC" - ] + "label": ["BUD", "128.10", "PLC"] }, { - "label": [ - "BUD", - "133.20", - "PLC" - ] + "label": ["BUD", "133.20", "PLC"] }, { - "label": [ - "MCC", - "TAU", - "PLC" - ] + "label": ["MCC", "TAU", "PLC"] }, { - "label": [ - "LOXZ", - "APP" - ], + "label": ["LOXZ", "APP"], "station_id": "LOXZ_APP" }, { - "label": [ - "APP", - "TFI", - "PLC" - ], + "label": ["APP", "TFI", "PLC"], "station_id": "LOWW_I_APP" }, { - "label": [ - "ACC", - "Rs. PLC", - "Cont." - ] + "label": ["ACC", "Rs. PLC", "Cont."] }, { - "label": [ - "ACC", - "N1", - "PLC" - ], + "label": ["ACC", "N1", "PLC"], "station_id": "LOVV_N1" }, { "label": [] }, { - "label": [ - "LOWL", - "APP", - "PLC" - ], + "label": ["LOWL", "APP", "PLC"], "station_id": "LOWL_APP" }, { - "label": [ - "APP", - "VN-PLC", - "118775" - ], + "label": ["APP", "VN-PLC", "118775"], "station_id": "LOWW_N_APP" }, { - "label": [ - "APP", - "VB-PLC", - "134675" - ], + "label": ["APP", "VB-PLC", "134675"], "station_id": "LOWW_APP" }, { - "label": [ - "TCO" - ], + "label": ["TCO"], "station_id": "LOWW_TWR" }, { - "label": [ - "ACC", - "E1", - "PLC" - ], + "label": ["ACC", "E1", "PLC"], "station_id": "LOVV_E1" }, { - "label": [ - "ACC", - "S1", - "PLC" - ], + "label": ["ACC", "S1", "PLC"], "station_id": "LOVV_S1" }, { - "label": [ - "LOWG", - "APP", - "PLC" - ], + "label": ["LOWG", "APP", "PLC"], "station_id": "LOWG_APP" }, { - "label": [ - "APP", - "VM-PLC", - "125175" - ], + "label": ["APP", "VM-PLC", "125175"], "station_id": "LOWW_M_APP" }, { - "label": [ - "APP", - "VP-PLC", - "129050" - ], + "label": ["APP", "VP-PLC", "129050"], "station_id": "LOWW_P_APP" }, { @@ -428,14 +232,152 @@ "label": "TFI", "page": { "rows": 6, - "keys": [] + "keys": [ + { + "label": ["LOAG"] + }, + { + "label": ["LOAV"] + }, + { + "label": ["PRA", "FIC", "WEST"] + }, + { + "label": ["APP", "SAP"] + }, + { + "label": ["FIC", "N", "EC"], + "station_id": "LOVV_I_CTR" + }, + { + "label": ["FIC", "S", "EC"], + "station_id": "LOVV_I_CTR" + }, + { + "label": ["LOAU"] + }, + { + "label": ["LOAN"] + }, + { + "label": ["PRA", "FIC", "EAST"] + }, + { + "label": ["ARO"] + }, + { + "label": ["FIC", "N", "PLC"], + "station_id": "LOVV_I_CTR" + }, + { + "label": ["FIC", "S", "PLC"], + "station_id": "LOVV_I_CTR" + }, + { + "label": ["LOXT"], + "station_id": "LOXT_TWR" + }, + { + "label": ["LOXN", "TWR"] + }, + { + "label": ["PRA", "FIC", "MOR"] + }, + { + "label": ["PRA", "TB", "PLC"] + }, + { + "label": ["APP", "TFI", "EC"], + "station_id": "LOWW_I_APP" + }, + { + "label": ["TCO"], + "station_id": "LOWW_TWR" + }, + { + "label": ["MIL", "INFO", "PLC"], + "station_id": "LOVV_M_CTR" + }, + { + "label": ["BRA", "FIC", "PLC"] + }, + { + "label": ["BRA", "APP", "PLC"] + }, + { + "label": ["BRA", "TWR", "PLC"] + }, + { + "label": ["APP", "VN-PLC", "118775"], + "station_id": "LOWW_N_APP" + }, + { + "label": ["APP", "VB-PLC", "134675"], + "station_id": "LOWW_APP" + }, + { + "label": ["MIL", "CTR"], + "station_id": "LOVV_M_CTR" + }, + { + "label": ["LHPR"] + }, + { + "label": ["BUD", "FIC"] + }, + { + "label": ["LOXZ", "APP"], + "station_id": "LOXZ_APP" + }, + { + "label": ["APP", "VM-PLC", "125175"], + "station_id": "LOWW_M_APP" + }, + { + "label": ["APP", "VP-PLC", "129050"], + "station_id": "LOWW_P_APP" + } + ] } }, { - "label": "SAP", + "label": "CWP", "page": { "rows": 6, - "keys": [] + "client_page": { + "include": [ + "LO*", + "EDMM*", + "EDUU*", + "EDDM*", + "ADR*", + "EUC*", + "LD*", + "LH*", + "LIM*", + "LIP*", + "LJ*", + "LK*", + "LS*", + "LZ*" + ], + "exclude": ["LON*"], + "priority": [ + "LO*_FMP", + "*_FMP", + "LO*_CTR", + "*_CTR", + "LOWW*_APP", + "LO*_APP", + "*_APP", + "LOWW*_TWR", + "LO*_TWR", + "*_TWR", + "LO*_GND", + "*_GND", + "LO*" + ] + } } } ] diff --git a/dataset/LO/stations.json b/dataset/LO/stations.json index a1cac8ec..9ca2df17 100644 --- a/dataset/LO/stations.json +++ b/dataset/LO/stations.json @@ -417,6 +417,10 @@ "id": "LOWW_I_APP", "controlled_by": ["LOWW_I_APP", "LOVV_I_CTR"] }, + { + "id": "LOVV_M_CTR", + "controlled_by": ["LOVV_M_CTR"] + }, { "id": "LOXT_APP", "controlled_by": [ diff --git a/dataset/LO/stations.toml b/dataset/LO/stations.toml index 2ad02893..da9c522f 100644 --- a/dataset/LO/stations.toml +++ b/dataset/LO/stations.toml @@ -1,16 +1,16 @@ [[stations]] id = "LOVV_B1" controlled_by = [ - "LOVV_B_CTR", - "LOVV_BC_CTR", - "LOVV_W_CTR", - "LOVV_N_CTR", - "LOVV_CTR", - "LOVV_C_CTR", - "LOVV_BU_CTR", - "LOVV_WU_CTR", - "LOVV_NU_CTR", - "LOVV_U_CTR", + "LOVV_B_CTR", + "LOVV_BC_CTR", + "LOVV_W_CTR", + "LOVV_N_CTR", + "LOVV_CTR", + "LOVV_C_CTR", + "LOVV_BU_CTR", + "LOVV_WU_CTR", + "LOVV_NU_CTR", + "LOVV_U_CTR", ] [[stations]] @@ -36,28 +36,28 @@ parent_id = "LOVV_B7" [[stations]] id = "LOVV_B7" controlled_by = [ - "LOVV_BU_CTR", - "LOVV_WU_CTR", - "LOVV_NU_CTR", - "LOVV_U_CTR", - "LOVV_B_CTR", - "LOVV_BC_CTR", - "LOVV_W_CTR", - "LOVV_N_CTR", - "LOVV_CTR", - "LOVV_C_CTR", + "LOVV_BU_CTR", + "LOVV_WU_CTR", + "LOVV_NU_CTR", + "LOVV_U_CTR", + "LOVV_B_CTR", + "LOVV_BC_CTR", + "LOVV_W_CTR", + "LOVV_N_CTR", + "LOVV_CTR", + "LOVV_C_CTR", ] [[stations]] id = "LOVV_E1" controlled_by = [ - "LOVV_E_CTR", - "LOVV_N_CTR", - "LOVV_CTR", - "LOVV_C_CTR", - "LOVV_EU_CTR", - "LOVV_NU_CTR", - "LOVV_U_CTR", + "LOVV_E_CTR", + "LOVV_N_CTR", + "LOVV_CTR", + "LOVV_C_CTR", + "LOVV_EU_CTR", + "LOVV_NU_CTR", + "LOVV_U_CTR", ] [[stations]] @@ -83,26 +83,26 @@ parent_id = "LOVV_E7" [[stations]] id = "LOVV_E7" controlled_by = [ - "LOVV_EU_CTR", - "LOVV_NU_CTR", - "LOVV_U_CTR", - "LOVV_E_CTR", - "LOVV_N_CTR", - "LOVV_CTR", - "LOVV_C_CTR", + "LOVV_EU_CTR", + "LOVV_NU_CTR", + "LOVV_U_CTR", + "LOVV_E_CTR", + "LOVV_N_CTR", + "LOVV_CTR", + "LOVV_C_CTR", ] [[stations]] id = "LOVV_N1" controlled_by = [ - "LOVV_F_CTR", - "LOVV_N_CTR", - "LOVV_E_CTR", - "LOVV_CTR", - "LOVV_C_CTR", - "LOVV_NU_CTR", - "LOVV_EU_CTR", - "LOVV_U_CTR", + "LOVV_F_CTR", + "LOVV_N_CTR", + "LOVV_E_CTR", + "LOVV_CTR", + "LOVV_C_CTR", + "LOVV_NU_CTR", + "LOVV_EU_CTR", + "LOVV_U_CTR", ] [[stations]] @@ -128,28 +128,28 @@ parent_id = "LOVV_N7" [[stations]] id = "LOVV_N7" controlled_by = [ - "LOVV_NU_CTR", - "LOVV_EU_CTR", - "LOVV_U_CTR", - "LOVV_N_CTR", - "LOVV_E_CTR", - "LOVV_CTR", - "LOVV_C_CTR", + "LOVV_NU_CTR", + "LOVV_EU_CTR", + "LOVV_U_CTR", + "LOVV_N_CTR", + "LOVV_E_CTR", + "LOVV_CTR", + "LOVV_C_CTR", ] [[stations]] id = "LOVV_S1" controlled_by = [ - "LOVV_S_CTR", - "LOVV_B_CTR", - "LOVV_BC_CTR", - "LOVV_E_CTR", - "LOVV_CTR", - "LOVV_C_CTR", - "LOVV_SU_CTR", - "LOVV_BU_CTR", - "LOVV_EU_CTR", - "LOVV_U_CTR", + "LOVV_S_CTR", + "LOVV_B_CTR", + "LOVV_BC_CTR", + "LOVV_E_CTR", + "LOVV_CTR", + "LOVV_C_CTR", + "LOVV_SU_CTR", + "LOVV_BU_CTR", + "LOVV_EU_CTR", + "LOVV_U_CTR", ] [[stations]] @@ -175,31 +175,31 @@ parent_id = "LOVV_S7" [[stations]] id = "LOVV_S7" controlled_by = [ - "LOVV_SU_CTR", - "LOVV_BU_CTR", - "LOVV_EU_CTR", - "LOVV_U_CTR", - "LOVV_S_CTR", - "LOVV_B_CTR", - "LOVV_BC_CTR", - "LOVV_E_CTR", - "LOVV_CTR", - "LOVV_C_CTR", + "LOVV_SU_CTR", + "LOVV_BU_CTR", + "LOVV_EU_CTR", + "LOVV_U_CTR", + "LOVV_S_CTR", + "LOVV_B_CTR", + "LOVV_BC_CTR", + "LOVV_E_CTR", + "LOVV_CTR", + "LOVV_C_CTR", ] [[stations]] id = "LOVV_W1" controlled_by = [ - "LOVV_W_CTR", - "LOVV_S_CTR", - "LOVV_B_CTR", - "LOVV_BC_CTR", - "LOVV_CTR", - "LOVV_C_CTR", - "LOVV_WU_CTR", - "LOVV_SU_CTR", - "LOVV_BU_CTR", - "LOVV_U_CTR", + "LOVV_W_CTR", + "LOVV_S_CTR", + "LOVV_B_CTR", + "LOVV_BC_CTR", + "LOVV_CTR", + "LOVV_C_CTR", + "LOVV_WU_CTR", + "LOVV_SU_CTR", + "LOVV_BU_CTR", + "LOVV_U_CTR", ] [[stations]] @@ -225,37 +225,41 @@ parent_id = "LOVV_W7" [[stations]] id = "LOVV_W7" controlled_by = [ - "LOVV_WU_CTR", - "LOVV_SU_CTR", - "LOVV_BU_CTR", - "LOVV_U_CTR", - "LOVV_W_CTR", - "LOVV_S_CTR", - "LOVV_B_CTR", - "LOVV_BC_CTR", - "LOVV_CTR", - "LOVV_C_CTR", + "LOVV_WU_CTR", + "LOVV_SU_CTR", + "LOVV_BU_CTR", + "LOVV_U_CTR", + "LOVV_W_CTR", + "LOVV_S_CTR", + "LOVV_B_CTR", + "LOVV_BC_CTR", + "LOVV_CTR", + "LOVV_C_CTR", ] [[stations]] id = "LOVV_I_CTR" controlled_by = ["LOVV_I_CTR"] +[[stations]] +id = "LOVV_M_CTR" +controlled_by = ["LOVV_M_CTR"] + [[stations]] id = "LOWG_APP" controlled_by = [ - "LOWG_APP", - "LOVV_S_APP", - "LOVV_L_CTR", - "LOVV_S_CTR", - "LOVV_B_CTR", - "LOVV_BC_CTR", - "LOVV_E_CTR", - "LOVV_CTR", - "LOVV_C_CTR", - "LOVV_SU_CTR", - "LOVV_BU_CTR", - "LOVV_EU_CTR", + "LOWG_APP", + "LOVV_S_APP", + "LOVV_L_CTR", + "LOVV_S_CTR", + "LOVV_B_CTR", + "LOVV_BC_CTR", + "LOVV_E_CTR", + "LOVV_CTR", + "LOVV_C_CTR", + "LOVV_SU_CTR", + "LOVV_BU_CTR", + "LOVV_EU_CTR", ] [[stations]] @@ -286,34 +290,34 @@ controlled_by = ["LOWI_F_APP", "LOWI_E_APP", "LOWI_S_APP"] [[stations]] id = "LOWI_APP" controlled_by = [ - "LOWI_APP", - "LOWI_E_APP", - "LOWI_S_APP", - "LOVV_L_CTR", - "LOVV_W_CTR", - "LOVV_S_CTR", - "LOVV_B_CTR", - "LOVV_CTR", - "LOVV_WU_CTR", - "LOVV_SU_CTR", - "LOVV_BU_CTR", + "LOWI_APP", + "LOWI_E_APP", + "LOWI_S_APP", + "LOVV_L_CTR", + "LOVV_W_CTR", + "LOVV_S_CTR", + "LOVV_B_CTR", + "LOVV_CTR", + "LOVV_WU_CTR", + "LOVV_SU_CTR", + "LOVV_BU_CTR", ] [[stations]] id = "LOWK_APP" controlled_by = [ - "LOWK_APP", - "LOVV_S_APP", - "LOVV_L_CTR", - "LOVV_W_CTR", - "LOVV_S_CTR", - "LOVV_B_CTR", - "LOVV_BC_CTR", - "LOVV_CTR", - "LOVV_C_CTR", - "LOVV_WU_CTR", - "LOVV_SU_CTR", - "LOVV_BU_CTR", + "LOWK_APP", + "LOVV_S_APP", + "LOVV_L_CTR", + "LOVV_W_CTR", + "LOVV_S_CTR", + "LOVV_B_CTR", + "LOVV_BC_CTR", + "LOVV_CTR", + "LOVV_C_CTR", + "LOVV_WU_CTR", + "LOVV_SU_CTR", + "LOVV_BU_CTR", ] [[stations]] @@ -324,15 +328,15 @@ controlled_by = ["LOWK_TWR"] [[stations]] id = "LOWL_APP" controlled_by = [ - "LOWL_APP", - "LOVV_N_APP", - "LOVV_L_CTR", - "LOVV_N_CTR", - "LOVV_E_CTR", - "LOVV_CTR", - "LOVV_C_CTR", - "LOVV_NU_CTR", - "LOVV_EU_CTR", + "LOWL_APP", + "LOVV_N_APP", + "LOVV_L_CTR", + "LOVV_N_CTR", + "LOVV_E_CTR", + "LOVV_CTR", + "LOVV_C_CTR", + "LOVV_NU_CTR", + "LOVV_EU_CTR", ] [[stations]] @@ -343,18 +347,18 @@ controlled_by = ["LOWL_TWR"] [[stations]] id = "LOWS_APP" controlled_by = [ - "LOWS_APP", - "LOVV_N_APP", - "LOVV_L_CTR", - "LOVV_B_CTR", - "LOVV_BC_CTR", - "LOVV_W_CTR", - "LOVV_N_CTR", - "LOVV_CTR", - "LOVV_C_CTR", - "LOVV_BU_CTR", - "LOVV_WU_CTR", - "LOVV_NU_CTR", + "LOWS_APP", + "LOVV_N_APP", + "LOVV_L_CTR", + "LOVV_B_CTR", + "LOVV_BC_CTR", + "LOVV_W_CTR", + "LOVV_N_CTR", + "LOVV_CTR", + "LOVV_C_CTR", + "LOVV_BU_CTR", + "LOVV_WU_CTR", + "LOVV_NU_CTR", ] [[stations]] @@ -375,17 +379,17 @@ controlled_by = ["LOWW_E_TWR", "LOWW_TWR"] [[stations]] id = "LOWW_APP" controlled_by = [ - "LOWW_APP", - "LOWW_P_APP", - "LOWW_N_APP", - "LOWW_M_APP", - "LOVV_L_CTR", - "LOVV_E_CTR", - "LOVV_N_CTR", - "LOVV_CTR", - "LOVV_C_CTR", - "LOVV_EU_CTR", - "LOVV_NU_CTR", + "LOWW_APP", + "LOWW_P_APP", + "LOWW_N_APP", + "LOWW_M_APP", + "LOVV_L_CTR", + "LOVV_E_CTR", + "LOVV_N_CTR", + "LOVV_CTR", + "LOVV_C_CTR", + "LOVV_EU_CTR", + "LOVV_NU_CTR", ] [[stations]] @@ -418,19 +422,19 @@ controlled_by = ["LOWW_I_APP", "LOVV_I_CTR"] [[stations]] id = "LOXT_APP" controlled_by = [ - "LOXT_APP", - "LOVV_M_CTR", - "LOWW_N_APP", - "LOWW_M_APP", - "LOWW_APP", - "LOWW_P_APP", - "LOVV_L_CTR", - "LOVV_E_CTR", - "LOVV_N_CTR", - "LOVV_CTR", - "LOVV_C_CTR", - "LOVV_EU_CTR", - "LOVV_NU_CTR", + "LOXT_APP", + "LOVV_M_CTR", + "LOWW_N_APP", + "LOWW_M_APP", + "LOWW_APP", + "LOWW_P_APP", + "LOVV_L_CTR", + "LOVV_E_CTR", + "LOVV_N_CTR", + "LOVV_CTR", + "LOVV_C_CTR", + "LOVV_EU_CTR", + "LOVV_NU_CTR", ] [[stations]] @@ -441,23 +445,23 @@ controlled_by = ["LOXT_TWR"] [[stations]] id = "LOXZ_APP" controlled_by = [ - "LOXZ_APP", - "LOVV_M_CTR", - "LOWG_APP", - "LOWK_APP", - "LOVV_S_APP", - "LOVV_L_CTR", - "LOVV_S_CTR", - "LOVV_W_CTR", - "LOVV_B_CTR", - "LOVV_BC_CTR", - "LOVV_E_CTR", - "LOVV_CTR", - "LOVV_C_CTR", - "LOVV_SU_CTR", - "LOVV_WU_CTR", - "LOVV_BU_CTR", - "LOVV_EU_CTR", + "LOXZ_APP", + "LOVV_M_CTR", + "LOWG_APP", + "LOWK_APP", + "LOVV_S_APP", + "LOVV_L_CTR", + "LOVV_S_CTR", + "LOVV_W_CTR", + "LOVV_B_CTR", + "LOVV_BC_CTR", + "LOVV_E_CTR", + "LOVV_CTR", + "LOVV_C_CTR", + "LOVV_SU_CTR", + "LOVV_WU_CTR", + "LOVV_BU_CTR", + "LOVV_EU_CTR", ] [[stations]] diff --git a/dataset/VH/positions.json b/dataset/VH/positions.json new file mode 100644 index 00000000..c912061b --- /dev/null +++ b/dataset/VH/positions.json @@ -0,0 +1,452 @@ +{ + "positions": [ + { + "id": "VHHH_DEL", + "prefixes": ["VHHH"], + "frequency": "122.150", + "facility_type": "DEL", + "profile": "VHHK" + }, + { + "id": "VHHX_DEL", + "prefixes": ["VHHX"], + "frequency": "123.225", + "facility_type": "DEL", + "profile": "VHHK" + }, + { + "id": "VMMC_DEL", + "prefixes": ["VMMC"], + "frequency": "121.975", + "facility_type": "DEL", + "profile": "VHHK" + }, + { + "id": "VHHH_1_GND", + "prefixes": ["VHHH"], + "frequency": "121.600", + "facility_type": "GND", + "profile": "VHHK" + }, + { + "id": "VHHH_2_GND", + "prefixes": ["VHHH"], + "frequency": "122.550", + "facility_type": "GND", + "profile": "VHHK" + }, + { + "id": "VHHH_3_GND", + "prefixes": ["VHHH"], + "frequency": "121.875", + "facility_type": "GND", + "profile": "VHHK" + }, + { + "id": "VHHH_4_GND", + "prefixes": ["VHHH"], + "frequency": "122.600", + "facility_type": "GND", + "profile": "VHHK" + }, + { + "id": "VHHH_5_GND", + "prefixes": ["VHHH"], + "frequency": "122.125", + "facility_type": "GND", + "profile": "VHHK" + }, + { + "id": "VHHX_GND", + "prefixes": ["VHHX"], + "frequency": "121.925", + "facility_type": "GND", + "profile": "VHHK" + }, + { + "id": "VMMC_GND", + "prefixes": ["VMMC"], + "frequency": "121.725", + "facility_type": "GND", + "profile": "VHHK" + }, + { + "id": "VHHH_S_TWR", + "prefixes": ["VHHH"], + "frequency": "118.400", + "facility_type": "TWR", + "profile": "VHHK" + }, + { + "id": "VHHH_C_TWR", + "prefixes": ["VHHH"], + "frequency": "118.200", + "facility_type": "TWR", + "profile": "VHHK" + }, + { + "id": "VHHH_N_TWR", + "prefixes": ["VHHH"], + "frequency": "118.700", + "facility_type": "TWR", + "profile": "VHHK" + }, + { + "id": "VHHH_Z_TWR", + "prefixes": ["VHHH"], + "frequency": "120.600", + "facility_type": "TWR", + "profile": "VHHK" + }, + { + "id": "VHHH_F_TWR", + "prefixes": ["VHHH"], + "frequency": "121.000", + "facility_type": "TWR", + "profile": "VHHK" + }, + { + "id": "VHHX_TWR", + "prefixes": ["VHHX"], + "frequency": "124.650", + "facility_type": "TWR", + "profile": "VHHK" + }, + { + "id": "VMMC_TWR", + "prefixes": ["VMMC"], + "frequency": "118.000", + "facility_type": "TWR", + "profile": "VHHK" + }, + { + "id": "VHHH_APP", + "prefixes": ["VHHH"], + "frequency": "119.100", + "facility_type": "APP", + "profile": "VHHK" + }, + { + "id": "VHHH_F_APP", + "prefixes": ["VHHH"], + "frequency": "119.500", + "facility_type": "APP", + "profile": "VHHK" + }, + { + "id": "VHHH_P_APP", + "prefixes": ["VHHH"], + "frequency": "119.350", + "facility_type": "APP", + "profile": "VHHK" + }, + { + "id": "VHHH_N_DEP", + "prefixes": ["VHHH"], + "frequency": "123.800", + "facility_type": "DEP", + "profile": "VHHK" + }, + { + "id": "VHHH_S_DEP", + "prefixes": ["VHHH"], + "frequency": "122.000", + "facility_type": "DEP", + "profile": "VHHK" + }, + { + "id": "VHHH_H_DEP", + "prefixes": ["VHHH"], + "frequency": "122.650", + "facility_type": "DEP", + "profile": "VHHK" + }, + { + "id": "VHHH_W_APP", + "prefixes": ["VHHH"], + "frequency": "127.550", + "facility_type": "APP", + "profile": "VHHK" + }, + { + "id": "VHHH_L_APP", + "prefixes": ["VHHH"], + "frequency": "125.175", + "facility_type": "APP", + "profile": "VHHK" + }, + { + "id": "VMMC_APP", + "prefixes": ["VMMC"], + "frequency": "123.950", + "facility_type": "APP", + "profile": "VHHK" + }, + { + "id": "VHHH_S_APP", + "prefixes": ["VHHH"], + "frequency": "126.300", + "facility_type": "APP", + "profile": "VHHK" + }, + { + "id": "VHHH_C_DEP", + "prefixes": ["VHHH"], + "frequency": "123.475", + "facility_type": "DEP", + "profile": "VHHK" + }, + { + "id": "VHHH_E_APP", + "prefixes": ["VHHH"], + "frequency": "126.500", + "facility_type": "APP", + "profile": "VHHK" + }, + { + "id": "VHHH_E_DEP", + "prefixes": ["VHHH"], + "frequency": "133.825", + "facility_type": "APP", + "profile": "VHHK" + }, + { + "id": "VHHK_W_CTR", + "prefixes": ["HKG"], + "frequency": "127.100", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "VHHK_V_CTR", + "prefixes": ["HKG"], + "frequency": "125.325", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "VHHK_D_CTR", + "prefixes": ["HKG"], + "frequency": "122.950", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "VHHK_S_CTR", + "prefixes": ["HKG"], + "frequency": "132.150", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "VHHK_Z_CTR", + "prefixes": ["HKG"], + "frequency": "133.700", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "VHHK_C_CTR", + "prefixes": ["HKG"], + "frequency": "128.750", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "VHHK_E_CTR", + "prefixes": ["HKG"], + "frequency": "118.925", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "VHHK_K_CTR", + "prefixes": ["HKG"], + "frequency": "121.300", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "VHHK_U_CTR", + "prefixes": ["HKG"], + "frequency": "132.525", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "VHHK_FMP", + "prefixes": ["HKG"], + "frequency": "199.995", + "facility_type": "FMP", + "profile": "VHHK" + }, + { + "id": "RCAA_W_CTR", + "prefixes": ["TPE"], + "frequency": "126.700", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "RCAA_L_CTR", + "prefixes": ["TPE"], + "frequency": "125.500", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "RCAA_N_CTR", + "prefixes": ["TPE"], + "frequency": "123.600", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "RCAA_E_CTR", + "prefixes": ["TPE"], + "frequency": "127.900", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "RCAA_S_CTR", + "prefixes": ["TPE"], + "frequency": "129.100", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "ASEA_FSS", + "prefixes": ["ASEA"], + "frequency": "135.350", + "facility_type": "FSS", + "profile": "VHHK" + }, + { + "id": "RPHI_1_CTR", + "prefixes": ["MNL"], + "frequency": "127.500", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "RPHI_N_CTR", + "prefixes": ["MNL"], + "frequency": "126.575", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "RPHI_CTR", + "prefixes": ["MNL"], + "frequency": "119.300", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "ZBBB_FSS", + "prefixes": ["PRC"], + "frequency": "133.075", + "facility_type": "FSS", + "profile": "VHHK" + }, + { + "id": "ZSSS_S_CTR", + "prefixes": ["ZSSS"], + "frequency": "120.900", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "ZSSS_CTR", + "prefixes": ["ZSSS"], + "frequency": "120.950", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "ZSHA_CTR", + "prefixes": ["ZSHA"], + "frequency": "124.550", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "ZSAM_CTR", + "prefixes": ["ZSAM"], + "frequency": "125.700", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "ZJSA_CTR", + "prefixes": ["ZJSA"], + "frequency": "120.500", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "ZJSY_L_CTR", + "prefixes": ["ZJSY"], + "frequency": "133.200", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "ZJSY_O_CTR", + "prefixes": ["ZJSY"], + "frequency": "130.200", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "ZGZU_CTR", + "prefixes": ["ZGZU"], + "frequency": "132.750", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "ZGNN_CTR", + "prefixes": ["ZGNN"], + "frequency": "132.700", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "ZGZJ_APP", + "prefixes": ["ZGZJ"], + "frequency": "120.875", + "facility_type": "APP", + "profile": "VHHK" + }, + { + "id": "ZGOW_APP", + "prefixes": ["ZGOW"], + "frequency": "120.650", + "facility_type": "APP", + "profile": "VHHK" + }, + { + "id": "ZGJD_APP", + "prefixes": ["ZGJD"], + "frequency": "120.350", + "facility_type": "APP", + "profile": "VHHK" + }, + { + "id": "ZGGG_CTR", + "prefixes": ["ZGGG"], + "frequency": "128.350", + "facility_type": "CTR", + "profile": "VHHK" + }, + { + "id": "ZGGG_APP", + "prefixes": ["ZGGG"], + "frequency": "126.550", + "facility_type": "APP", + "profile": "VHHK" + } + ] +} diff --git a/dataset/VH/profiles/VHHK.json b/dataset/VH/profiles/VHHK.json new file mode 100644 index 00000000..98677675 --- /dev/null +++ b/dataset/VH/profiles/VHHK.json @@ -0,0 +1,294 @@ +{ + "id": "VHHK", + "type": "Tabbed", + "tabs": [ + { + "label": ["S-TWR", "+NAT"], + "page": { + "rows": 7, + "keys": [ + { + "label": ["N-CDC"], + "station_id": "VHHH_DEL" + }, + { + "label": ["S-GMC1"], + "station_id": "VHHH_1_GND" + }, + { + "label": ["S-GMC2"], + "station_id": "VHHH_2_GND" + }, + { + "label": ["S-GMC3"], + "station_id": "VHHH_3_GND" + }, + { + "label": ["N-GMC4"], + "station_id": "VHHH_4_GND" + }, + { + "label": ["N-GMC5"], + "station_id": "VHHH_5_GND" + }, + { + "label": ["S-AMS"], + "station_id": "VHHH_S_TWR" + }, + { + "label": ["N-AMM"], + "station_id": "VHHH_C_TWR" + }, + { + "label": ["N-AMN"], + "station_id": "VHHH_N_TWR" + }, + { + "label": ["N-ZNC"], + "station_id": "VHHH_Z_TWR" + }, + { + "label": ["N-FIS"], + "station_id": "VHHH_F_TWR" + } + ] + } + }, + { + "label": "E-ATCC", + "page": { + "rows": 7, + "keys": [ + { + "label": ["E-FLC"], + "station_id": "VHHK_FMP" + }, + { + "label": ["E-APP"], + "station_id": "VHHH_APP" + }, + { + "label": ["E-FAD"], + "station_id": "VHHH_F_APP" + }, + { + "label": ["E-PAR"], + "station_id": "VHHH_P_APP" + }, + { + "label": ["E-DEN"], + "station_id": "VHHH_N_DEP" + }, + { + "label": ["E-DES"], + "station_id": "VHHH_S_DEP" + }, + { + "label": ["E-DEH"], + "station_id": "VHHH_H_DEP" + }, + { + "label": ["E-TMW"], + "station_id": "VHHH_W_APP" + }, + { + "label": ["E-TML"], + "station_id": "VHHH_L_APP" + }, + { + "label": ["E-MCU"], + "station_id": "VMMC_APP" + }, + { + "label": ["E-TMS"], + "station_id": "VHHH_S_APP" + }, + { + "label": ["E-TDC"], + "station_id": "VHHH_C_DEP" + }, + { + "label": ["E-TME"], + "station_id": "VHHH_E_APP" + }, + { + "label": ["E-TDE"], + "station_id": "VHHH_E_DEP" + }, + { + "label": ["E-TRW"], + "station_id": "VHHK_W_CTR" + }, + { + "label": ["E-TRV"], + "station_id": "VHHK_V_CTR" + }, + { + "label": ["E-TRD"], + "station_id": "VHHK_D_CTR" + }, + { + "label": ["E-TRS"], + "station_id": "VHHK_S_CTR" + }, + { + "label": ["E-TRZ"], + "station_id": "VHHK_Z_CTR" + }, + { + "label": ["E-TRC"], + "station_id": "VHHK_C_CTR" + }, + { + "label": ["E-TRE"], + "station_id": "VHHK_E_CTR" + }, + { + "label": ["E-TRK"], + "station_id": "VHHK_K_CTR" + }, + { + "label": ["E-TRU"], + "station_id": "VHHK_U_CTR" + } + ] + } + }, + { + "label": ["K-TWR", "+VMMC"], + "page": { + "rows": 7, + "keys": [ + { + "label": ["K-CDC"], + "station_id": "VHHX_DEL" + }, + { + "label": ["K-GMC"], + "station_id": "VHHX_GND" + }, + { + "label": ["K-AMC"], + "station_id": "VHHX_TWR" + }, + { + "label": ["VMMC_DEL"], + "station_id": "VMMC_DEL" + }, + { + "label": ["VMMC_GND"], + "station_id": "VMMC_GND" + }, + { + "label": ["VMMC_TWR"], + "station_id": "VMMC_TWR" + } + ] + } + }, + { + "label": "EXT", + "page": { + "rows": 7, + "keys": [ + { + "label": ["PEW"], + "station_id": "RCAA_W_CTR" + }, + { + "label": ["PEL"], + "station_id": "RCAA_L_CTR" + }, + { + "label": ["PEE"], + "station_id": "RCAA_E_CTR" + }, + { + "label": ["PEN"], + "station_id": "RCAA_N_CTR" + }, + { + "label": ["PES"], + "station_id": "RCAA_S_CTR" + }, + { + "label": ["SEA"], + "station_id": "ASEA_FSS" + }, + { + "label": ["MN1"], + "station_id": "RPHI_1_CTR" + }, + { + "label": ["MNN"], + "station_id": "RPHI_N_CTR" + }, + { + "label": ["MNL"], + "station_id": "RPHI_CTR" + }, + { + "label": ["PRC"], + "station_id": "ZBBB_FSS" + }, + { + "label": ["SSS"], + "station_id": "ZSSS_S_CTR" + }, + { + "label": ["SHS"], + "station_id": "ZSSS_CTR" + }, + { + "label": ["SHA"], + "station_id": "ZSHA_CTR" + }, + { + "label": ["SAM"], + "station_id": "ZSAM_CTR" + }, + { + "label": ["JSA"], + "station_id": "ZJSA_CTR" + }, + { + "label": ["SYL"], + "station_id": "ZJSY_L_CTR" + }, + { + "label": ["SYO"], + "station_id": "ZJSY_O_CTR" + }, + { + "label": ["GZU"], + "station_id": "ZGZU_CTR" + }, + { + "label": ["GNN"], + "station_id": "ZGNN_CTR" + }, + { + "label": ["GZJ"], + "station_id": "ZGZJ_APP" + }, + { + "label": ["SWA"], + "station_id": "ZGOW_APP" + }, + { + "label": ["ZUH"], + "station_id": "ZGJD_APP" + }, + { + "label": ["GGG"], + "station_id": "ZGGG_CTR" + }, + { + "label": ["GAP"], + "station_id": "ZGGG_APP" + } + ] + } + } + ] +} diff --git a/dataset/VH/stations.json b/dataset/VH/stations.json new file mode 100644 index 00000000..6e3a473b --- /dev/null +++ b/dataset/VH/stations.json @@ -0,0 +1,319 @@ +{ + "stations": [ + { + "id": "VHHH_DEL", + "parent_id": "VHHH_1_GND", + "controlled_by": ["VHHH_DEL"] + }, + { + "id": "VHHX_DEL", + "parent_id": "VHHX_GND", + "controlled_by": ["VHHX_DEL"] + }, + { + "id": "VMMC_DEL", + "parent_id": "VMMC_GND", + "controlled_by": ["VMMC_DEL"] + }, + { + "id": "VHHH_1_GND", + "parent_id": "VHHH_S_TWR", + "controlled_by": ["VHHH_1_GND"] + }, + { + "id": "VHHH_2_GND", + "parent_id": "VHHH_1_GND", + "controlled_by": ["VHHH_2_GND"] + }, + { + "id": "VHHH_3_GND", + "parent_id": "VHHH_1_GND", + "controlled_by": ["VHHH_3_GND"] + }, + { + "id": "VHHH_4_GND", + "parent_id": "VHHH_2_GND", + "controlled_by": ["VHHH_4_GND"] + }, + { + "id": "VHHH_5_GND", + "parent_id": "VHHH_4_GND", + "controlled_by": ["VHHH_5_GND"] + }, + { + "id": "VHHX_GND", + "parent_id": "VHHX_TWR", + "controlled_by": ["VHHX_GND"] + }, + { + "id": "VMMC_GND", + "parent_id": "VMMC_TWR", + "controlled_by": ["VMMC_GND"] + }, + { + "id": "VHHH_S_TWR", + "parent_id": "VHHH_APP", + "controlled_by": ["VHHH_S_TWR", "VHHH_C_TWR", "VHHH_N_TWR"] + }, + { + "id": "VHHH_C_TWR", + "parent_id": "VHHH_S_TWR", + "controlled_by": ["VHHH_C_TWR", "VHHH_N_TWR", "VHHH_S_TWR"] + }, + { + "id": "VHHH_N_TWR", + "parent_id": "VHHH_S_TWR", + "controlled_by": ["VHHH_N_TWR", "VHHH_C_TWR", "VHHH_S_TWR"] + }, + { + "id": "VHHH_Z_TWR", + "parent_id": "VHHH_S_TWR", + "controlled_by": ["VHHH_Z_TWR", "VHHH_N_TWR", "VHHH_C_TWR", "VHHH_S_TWR"] + }, + { + "id": "VHHH_F_TWR", + "parent_id": "VHHH_Z_TWR", + "controlled_by": ["VHHH_F_TWR"] + }, + { + "id": "VHHX_TWR", + "parent_id": "VHHH_APP", + "controlled_by": ["VHHX_TWR"] + }, + { + "id": "VMMC_TWR", + "parent_id": "VMMC_APP", + "controlled_by": ["VMMC_TWR"] + }, + { + "id": "VHHH_APP", + "parent_id": "VHHK_W_CTR", + "controlled_by": ["VHHH_APP"] + }, + { + "id": "VHHH_F_APP", + "parent_id": "VHHH_APP", + "controlled_by": ["VHHH_F_APP"] + }, + { + "id": "VHHH_P_APP", + "parent_id": "VHHH_APP", + "controlled_by": ["VHHH_P_APP"] + }, + { + "id": "VHHH_N_DEP", + "parent_id": "VHHH_APP", + "controlled_by": ["VHHH_N_DEP"] + }, + { + "id": "VHHH_S_DEP", + "parent_id": "VHHH_N_DEP", + "controlled_by": ["VHHH_S_DEP"] + }, + { + "id": "VHHH_H_DEP", + "parent_id": "VHHH_N_DEP", + "controlled_by": ["VHHH_H_DEP"] + }, + { + "id": "VHHH_W_APP", + "parent_id": "VHHH_APP", + "controlled_by": ["VHHH_W_APP", "VHHK_W_CTR"] + }, + { + "id": "VHHH_L_APP", + "parent_id": "VHHH_W_APP", + "controlled_by": ["VHHH_L_APP"] + }, + { + "id": "VMMC_APP", + "parent_id": "VHHH_W_APP", + "controlled_by": ["VMMC_APP"] + }, + { + "id": "VHHH_S_APP", + "parent_id": "VHHH_W_APP", + "controlled_by": ["VHHH_S_APP"] + }, + { + "id": "VHHH_C_DEP", + "parent_id": "VHHH_S_APP", + "controlled_by": ["VHHH_C_DEP"] + }, + { + "id": "VHHH_E_APP", + "parent_id": "VHHH_W_APP", + "controlled_by": ["VHHH_E_APP"] + }, + { + "id": "VHHH_E_DEP", + "parent_id": "VHHH_E_APP", + "controlled_by": ["VHHH_E_DEP"] + }, + { + "id": "VHHK_W_CTR", + "controlled_by": ["VHHK_W_CTR"] + }, + { + "id": "VHHK_V_CTR", + "parent_id": "VHHK_W_CTR", + "controlled_by": ["VHHK_V_CTR"] + }, + { + "id": "VHHK_D_CTR", + "parent_id": "VHHK_V_CTR", + "controlled_by": ["VHHK_D_CTR"] + }, + { + "id": "VHHK_S_CTR", + "parent_id": "VHHK_W_CTR", + "controlled_by": ["VHHK_S_CTR"] + }, + { + "id": "VHHK_Z_CTR", + "parent_id": "VHHK_S_CTR", + "controlled_by": ["VHHK_Z_CTR"] + }, + { + "id": "VHHK_C_CTR", + "parent_id": "VHHK_S_CTR", + "controlled_by": ["VHHK_C_CTR"] + }, + { + "id": "VHHK_E_CTR", + "parent_id": "VHHK_W_CTR", + "controlled_by": ["VHHK_E_CTR"] + }, + { + "id": "VHHK_K_CTR", + "parent_id": "VHHK_E_CTR", + "controlled_by": ["VHHK_K_CTR"] + }, + { + "id": "VHHK_U_CTR", + "parent_id": "VHHK_W_CTR", + "controlled_by": ["VHHK_U_CTR"] + }, + { + "id": "VHHK_FMP", + "parent_id": "VHHH_W_APP", + "controlled_by": ["VHHK_FMP"] + }, + { + "id": "RCAA_W_CTR", + "controlled_by": ["RCAA_W_CTR"] + }, + { + "id": "RCAA_E_CTR", + "parent_id": "RCAA_W_CTR", + "controlled_by": ["RCAA_E_CTR"] + }, + { + "id": "RCAA_S_CTR", + "parent_id": "RCAA_E_CTR", + "controlled_by": ["RCAA_S_CTR"] + }, + { + "id": "RCAA_L_CTR", + "controlled_by": ["RCAA_L_CTR", "RCAA_W_CTR"] + }, + { + "id": "RCAA_N_CTR", + "controlled_by": ["RCAA_N_CTR", "RCAA_L_CTR", "RCAA_W_CTR"] + }, + { + "id": "ASEA_FSS", + "controlled_by": ["ASEA_FSS"] + }, + { + "id": "RPHI_1_CTR", + "parent_id": "RPHI_N_CTR", + "controlled_by": ["RPHI_1_CTR"] + }, + { + "id": "RPHI_N_CTR", + "parent_id": "RPHI_CTR", + "controlled_by": ["RPHI_N_CTR"] + }, + { + "id": "RPHI_CTR", + "controlled_by": ["RPHI_CTR"] + }, + { + "id": "ZBBB_FSS", + "controlled_by": ["ZBBB_FSS"] + }, + { + "id": "ZSSS_S_CTR", + "parent_id": "ZSSS_CTR", + "controlled_by": ["ZSSS_S_CTR"] + }, + { + "id": "ZSSS_CTR", + "parent_id": "ZSHA_CTR", + "controlled_by": ["ZSSS_CTR"] + }, + { + "id": "ZSHA_CTR", + "controlled_by": ["ZSHA_CTR"] + }, + { + "id": "ZSAM_CTR", + "parent_id": "ZSHA_CTR", + "controlled_by": ["ZSAM_CTR"] + }, + { + "id": "ZJSA_CTR", + "controlled_by": ["ZJSA_CTR"] + }, + { + "id": "ZJSY_L_CTR", + "parent_id": "ZJSA_CTR", + "controlled_by": ["ZJSY_L_CTR"] + }, + { + "id": "ZJSY_O_CTR", + "parent_id": "ZJSA_CTR", + "controlled_by": ["ZJSY_O_CTR"] + }, + { + "id": "ZGZU_CTR", + "controlled_by": ["ZGZU_CTR"] + }, + { + "id": "ZGNN_CTR", + "parent_id": "ZGZU_CTR", + "controlled_by": ["ZGNN_CTR"] + }, + { + "id": "ZGZJ_APP", + "parent_id": "ZGNN_CTR", + "controlled_by": ["ZGZJ_APP"] + }, + { + "id": "ZGOW_APP", + "controlled_by": [ + "ZGOW_APP", + "ZGGG_CTR", + "ZGZU_CTR", + "ZSAM_CTR", + "ZSHA_CTR" + ] + }, + { + "id": "ZGJD_APP", + "parent_id": "ZGGG_CTR", + "controlled_by": ["ZGJD_APP"] + }, + { + "id": "ZGGG_CTR", + "parent_id": "ZGZU_CTR", + "controlled_by": ["ZGGG_CTR"] + }, + { + "id": "ZGGG_APP", + "parent_id": "ZGGG_CTR", + "controlled_by": ["ZGGG_APP"] + } + ] +} diff --git a/docs/dataset/positions.md b/docs/dataset/positions.md index 994d7414..c26202db 100644 --- a/docs/dataset/positions.md +++ b/docs/dataset/positions.md @@ -57,7 +57,7 @@ Each position entry maps a VATSIM controller login to a vacs position. | `prefixes` | Array of strings | Yes | Callsign prefixes used to match VATSIM logins when the callsign doesn't exactly match the `id` (e.g., `["LOWW"]`). | | `frequency` | String | Yes | Primary frequency in `XXX.XXX` format (e.g., `118.700`). | | `facility_type` | String | Yes | VATSIM facility type. Must be one of the facility types listed below. | -| `profile_id` | String | No | Optional ID of the default profile to load for this position. | +| `profile_id` | String | No | Optional ID of the profile to load for this position. | ## Validation Rules @@ -171,7 +171,7 @@ What this means in practice: ### Position without a profile -This example shows a position that does not load a default profile. +This example shows a position that does not load a profile. ```toml [[positions]] @@ -184,7 +184,7 @@ facility_type = "DEL" What this means in practice: - The `profile_id` field is optional and can be omitted -- Controllers logging into this position will not have a default profile automatically loaded +- Controllers logging into this position will not have a profile automatically loaded - Positions without profiles associated will only receive a basic view showing connected users (and their VATSIM callsign) and **will not show up as an online station for other controllers in vacs** ### Prefix matching with non-standard callsigns diff --git a/docs/dataset/profiles.md b/docs/dataset/profiles.md index 7ad8c26c..b1483621 100644 --- a/docs/dataset/profiles.md +++ b/docs/dataset/profiles.md @@ -25,8 +25,9 @@ Each profile file must contain a single profile object. There are two types of p - **Direct Access Key**: A button that allows calling a specific station - **Station ID**: Reference to a station defined in the stations configuration - **Tab**: A page in a tabbed profile containing a grid of keys -- **Geo Node**: A building block in a geo profile (container, button, or divider) +- **Geo Node**: A building block in a geo profile (container, button, or divider). See [Geo Profile Components](#geo-profile-components). - **Container**: A layout element that groups and arranges child nodes +- **Client Page**: A specialized page displaying a list of all online clients, filtered and prioritized based on the client page configuration ## Profile Types @@ -34,6 +35,8 @@ Each profile file must contain a single profile object. There are two types of p A tabbed profile organizes keys into multiple tabs, each containing a grid of direct access keys. +![Tabbed Profile Example](../images/tabbed.png "Tabbed Profile Example") + **Required fields:** - `id` (string): Unique profile identifier @@ -64,12 +67,14 @@ A tabbed profile organizes keys into multiple tabs, each containing a grid of di A geo profile uses a flexible container-based layout system, allowing for custom geometric arrangements of buttons and dividers. +![Geo Profile Example](../images/geo_page.png "Geo Profile Example") + **Required fields:** - `id` (string): Unique profile identifier - `type` (string): Must be `"Geo"` - `direction` (string): Flex direction (`"row"` or `"col"`) -- `children` (array): One or more geo nodes (containers, buttons, or dividers) +- `children` (array): One or more [geo nodes](#geonode) (containers, buttons, or dividers) **Structure:** @@ -93,61 +98,100 @@ A geo profile uses a flexible container-based layout system, allowing for custom | `id` | String | Yes | Unique profile identifier. Must start with the FIR's country code (e.g., `LOWW`, `LOVV`). | | `type` | String | Yes | Profile type. Must be either `"Tabbed"` or `"Geo"`. | -## Tabbed Profile Components +## Shared Profile Components -### Tab +### DirectAccessPage -Represents a single tab in a tabbed profile. +Defines a grid layout of direct access keys. -| Field | Type | Required | Description | -| :------ | :--------------- | :------- | :------------------------------------ | -| `label` | String | Yes | Tab name displayed in the interface. | -| `page` | DirectAccessPage | Yes | The page containing the grid of keys. | +| Field | Type | Required | Description | +| :------------ | :--------------------------------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------ | +| `rows` | Integer | Yes | Number of rows in the grid (minimum 1). | +| `keys` | Array of [DirectAccessKey](#directaccesskey) | No | Array of keys to display in the grid. Mutually exclusive with `client_page`. One of `keys` or `client_page` must be set. | +| `client_page` | [ClientPageConfig](#client-page-configuration) | No | Configuration for a dynamic client list page. Mutually exclusive with `keys`. One of `keys` or `client_page` must be set. | -### DirectAccessPage +### Client Page Configuration -Defines a grid layout of direct access keys. +A client page displays a dynamic list of online clients instead of a static grid of keys. + +| Field | Type | Required | Description | +| :------------ | :------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `include` | Array of Strings | No | List of callsign patterns to include. Supports glob syntax (e.g., `"LO*"`, `"*_APP"`). If empty, all clients are eligible. | +| `exclude` | Array of Strings | No | List of callsign patterns to exclude. Clients matching these patterns are never shown. Supports glob syntax. | +| `priority` | Array of Strings | No | Ordered list of callsign patterns to determine sort priority. Earlier patterns have higher priority. Default: `["*_FMP", "*_CTR", "*_APP", "*_TWR", "*_GND"]`. | +| `frequencies` | FrequencyDisplayMode | No | Controls frequency display on keys. | +| `grouping` | ClientGroupMode | No | Controls how keys are grouped. | + +**Valid `frequencies` values:** -| Field | Type | Required | Description | -| :----- | :----------------------- | :------- | :-------------------------------------- | -| `rows` | Integer | Yes | Number of rows in the grid (minimum 1). | -| `keys` | Array of DirectAccessKey | Yes | Array of keys to display in the grid. | +- `"ShowAll"` (default), `"HideAll"` + +**Valid `grouping` values:** + +- `"None"`: Do not group +- `"Fir"`: Group by first 2 chars +- `"Icao"`: Group by first 4 chars +- `"FirAndIcao"` (default): Group by FIR then ICAO code ### DirectAccessKey Represents a single callable button. -| Field | Type | Required | Description | -| :----------- | :--------------- | :------- | :------------------------------------------------------------------------------ | -| `label` | Array of strings | Yes | Multi-line label (up to 3 lines). Can be empty array for blank keys. | -| `station_id` | String | No | Station ID to call when pressed. If omitted, key is displayed but not callable. | +| Field | Type | Required | Description | +| :----------- | :------------------------------------ | :------- | :------------------------------------------------------------------------------------------------------------------------ | +| `label` | String or Array of strings | Yes | Multi-line label (up to 3 lines). Can be empty string or empty array for blank keys. | +| `station_id` | String | No | Station ID to call when pressed. Mutually exclusive with `page`. If neither is specified, the button will be disabled. | +| `page` | [DirectAccessPage](#directaccesspage) | No | Subpage to open when pressed. Mutually exclusive with `station_id`. If neither is specified, the button will be disabled. | + +## Tabbed Profile Components + +### Tab + +Represents a single tab in a tabbed profile. + +| Field | Type | Required | Description | +| :------ | :------------------------------------ | :------- | :--------------------------------------------------------------------------- | +| `label` | String or Array of strings | Yes | Multi-line tab name (1-3 lines, cannot be empty) displayed in the interface. | +| `page` | [DirectAccessPage](#directaccesspage) | Yes | The page containing the grid of keys. | ## Geo Profile Components +### GeoNode + +A `GeoNode` is one of the following: + +- [GeoPageContainer](#geopagecontainer) +- [GeoPageButton](#geopagebutton) +- [GeoPageDivider](#geopagedivider) + ### GeoPageContainer A layout container that arranges child nodes using flexbox. -| Field | Type | Required | Description | -| :---------------- | :---------------- | :------- | :---------------------------------------------- | -| `direction` | String | Yes | Flex direction: `"row"` or `"col"`. | -| `children` | Array of GeoNodes | Yes | Child nodes (containers, buttons, or dividers). | -| `height` | String | No | Container height (e.g., `"100%"`, `"20rem"`). | -| `width` | String | No | Container width (e.g., `"100%"`, `"20rem"`). | -| `padding` | Number | No | Padding on all sides (≥ 0). | -| `padding_left` | Number | No | Left padding (≥ 0). | -| `padding_right` | Number | No | Right padding (≥ 0). | -| `padding_top` | Number | No | Top padding (≥ 0). | -| `padding_bottom` | Number | No | Bottom padding (≥ 0). | -| `gap` | Number | No | Gap between child elements (≥ 0). | -| `justify_content` | String | No | Flexbox justify-content property. | -| `align_items` | String | No | Flexbox align-items property. | - -**Valid `justify_content` values:** +| Field | Type | Required | Description | +| :---------------- | :---------------------------- | :------- | :---------------------------------------------- | +| `direction` | FlexDirection | Yes | Flex direction. | +| `children` | Array of [GeoNodes](#geonode) | Yes | Child nodes (containers, buttons, or dividers). | +| `height` | String | No | Container height (e.g., `"100%"`, `"20rem"`). | +| `width` | String | No | Container width (e.g., `"100%"`, `"20rem"`). | +| `padding` | Number | No | Padding on all sides (≥ 0). | +| `padding_left` | Number | No | Left padding (≥ 0). | +| `padding_right` | Number | No | Right padding (≥ 0). | +| `padding_top` | Number | No | Top padding (≥ 0). | +| `padding_bottom` | Number | No | Bottom padding (≥ 0). | +| `gap` | Number | No | Gap between child elements (≥ 0). | +| `justify_content` | JustifyContent | No | Flexbox justify-content property. | +| `align_items` | AlignItems | No | Flexbox align-items property. | + +**Valid `FlexDirection` values:** + +- `"row"`, `"col"` + +**Valid `JustifyContent` values:** - `"flex-start"`, `"flex-end"`, `"center"`, `"space-between"`, `"space-around"`, `"space-evenly"` -**Valid `align_items` values:** +**Valid `AlignItems` values:** - `"flex-start"`, `"flex-end"`, `"center"`, `"stretch"`, `"baseline"` @@ -155,11 +199,11 @@ A layout container that arranges child nodes using flexbox. A clickable button that can trigger a direct access page or call a station. -| Field | Type | Required | Description | -| :------ | :--------------- | :------- | :--------------------------------------------------------- | -| `label` | Array of strings | Yes | Multi-line label (1-3 lines, cannot be empty). | -| `size` | Number | Yes | Button size (> 0). Controls relative sizing in the layout. | -| `page` | DirectAccessPage | No | Optional nested page to display when button is pressed. | +| Field | Type | Required | Description | +| :------ | :------------------------------------ | :------- | :--------------------------------------------------------- | +| `label` | String or Array of strings | Yes | Multi-line label (1-3 lines, cannot be empty). | +| `size` | Number | Yes | Button size (> 0). Controls relative sizing in the layout. | +| `page` | [DirectAccessPage](#directaccesspage) | No | Optional nested page to display when button is pressed. | ### GeoPageDivider @@ -188,6 +232,7 @@ The following validation rules apply: - All size values (`height`, `width`) must match pattern `^\d+(%|rem)$` - All numeric values (padding, gap, size, thickness) must be appropriate to their type (non-negative or positive) - Labels: + - Can be provided as a single string or an array of strings - Can have up to 3 lines - Geo button labels must have at least 1 line - Direct access key labels can be empty (for blank keys) @@ -205,10 +250,17 @@ When configuring profiles, watch out for these issues: ## Examples +You can find simple examples for different types of profiles below. + +For a more comprehensive [Geo Profile example](#geo-profile), see the [`LOVV` profile](../../dataset/LO/profiles/LOVV.json). +For a more comprehensive [Tabbed Profile example](#tabbed-profile), see the [`LOWW` profile](../../dataset/LO/profiles/LOWW.json). + ### Simple tabbed profile This example defines a basic tabbed profile with one tab containing a 4-row grid of keys. +![Simple tabbed profile example](../images/example_simple_tabbed.png "Simple tabbed profile example") + ```json { "id": "LOWW", @@ -253,6 +305,8 @@ What this means in practice: This example shows a tabbed profile with multiple tabs for different operational areas. +![Multi-tab profile example](../images/example_multi_tabbed.png "Multi-tab profile example") + ```json { "id": "LOWW", @@ -309,6 +363,8 @@ What this means in practice: This example demonstrates a simple geo profile using a vertical container layout. +![Basic Geo Profile example](../images/example_basic_geo.png "Basic Geo Profile example") + ```json { "id": "LOVV", @@ -321,10 +377,11 @@ This example demonstrates a simple geo profile using a vertical container layout { "direction": "row", "gap": 8, + "padding": 2, "children": [ { "label": ["LOVV", "N5"], - "size": 1, + "size": 10, "page": { "rows": 2, "keys": [ @@ -341,7 +398,7 @@ This example demonstrates a simple geo profile using a vertical container layout }, { "label": ["LOVV", "S5"], - "size": 1, + "size": 10, "page": { "rows": 2, "keys": [ @@ -366,6 +423,112 @@ What this means in practice: - The profile uses a geometric layout instead of fixed tabs - The root container is a vertical column (`"col"`) that fills the full available space -- Inside is a horizontal row (`"row"`) containing two equally-sized buttons (`size: 1`) +- Inside is a horizontal row (`"row"`) containing two equally sized buttons (`size: 10`) - Each button opens a nested direct access page when pressed - The nested pages contain grids of station keys for different sectors + +### Subpage definition + +This example shows a profile with a key that opens another page (subpage) instead of calling a station. + +```json +{ + "id": "LOWW", + "type": "Tabbed", + "tabs": [ + { + "label": "EC", + "page": { + "rows": 4, + "keys": [ + { + "label": ["Other", "Sectors"], + "page": { + "rows": 4, + "keys": [ + { + "label": ["LOVV", "W_CTR"], + "station_id": "LOVV_W_CTR" + }, + { + "label": ["LOVV", "S_CTR"], + "station_id": "LOVV_S_CTR" + } + ] + } + } + ] + } + } + ] +} +``` + +What this means in practice: + +- The profile contains one tab with a single key +- The key is labeled "Other Sectors" +- When pressed, it replaces the current grid with a new 4-row grid +- The new grid contains keys for `LOVV_W_CTR` and `LOVV_S_CTR` +- This allows creating hierarchical menus of stations + +### Client page definition + +This example shows a profile with a page that displays a dynamic list of online clients. + +```json +{ + "id": "LOWW", + "type": "Tabbed", + "tabs": [ + { + "label": "CWP", + "page": { + "rows": 6, + "client_page": { + "include": ["LO*", "ED*"], + "exclude": ["LON*", "*_GND"], + "grouping": "Fir", + "priority": ["*_CTR", "*_APP"] + } + } + } + ] +} +``` + +What this means in practice: + +- The profile contains a tab that shows a client list +- The page will automatically populate with online clients +- Only callsigns starting with `LO` or `ED` are included +- Callsigns starting with `LON` as well as all Ground positions are excluded +- Clients are grouped by their FIR (first 2 letters of callsign) +- Centers and Approach units are shown first in the list + +## Component Visuals + +### Direct Access Page states + +![Direct Access Page states](../images/direct_access_page.png "Direct Access Page states") + +The screenshot above shows a direct access page with three different states: + +- **Station online, covered by own position**: The DA key is active with grey text (e.g., `380 E6 PLC`) +- **Station online, covered by different position**: The DA key is active with black text (e.g., `APP VB PLN`) +- **Station defined, but currently not covered by any position**: The DA key is inactive with black text (e.g., `APP VD1 PLN`) +- **Station not defined**: The DA key is inactive with grey text (see [Tabbed Profile](#tabbed-profile), e.g., `PRA LW EC`) + +### Client Page + +![Client Page Grouping - FIR](../images/client_page_fir.png "Client Page Grouping - FIR") + +Client page grouped by FIR (two letters) + +![Client Page Grouping - ICAO](../images/client_page_icao.png "Client Page Grouping - ICAO") + +Client page grouped by ICAO (four letters) + +![Client Page](../images/client_page.png "Client Page") + +List of clients prioritized and displayed as per [Client Page Configuration](#client-page-configuration) diff --git a/docs/dataset/stations.md b/docs/dataset/stations.md index 6905c795..6709735c 100644 --- a/docs/dataset/stations.md +++ b/docs/dataset/stations.md @@ -193,13 +193,13 @@ parent_id = "LOVV_N7" [[stations]] id = "LOVV_N7" controlled_by = [ - "LOVV_NU_CTR", - "LOVV_EU_CTR", - "LOVV_U_CTR", - "LOVV_N_CTR", - "LOVV_E_CTR", - "LOVV_CTR", - "LOVV_C_CTR", + "LOVV_NU_CTR", + "LOVV_EU_CTR", + "LOVV_U_CTR", + "LOVV_N_CTR", + "LOVV_E_CTR", + "LOVV_CTR", + "LOVV_C_CTR", ] ``` diff --git a/docs/images/client_page.png b/docs/images/client_page.png new file mode 100644 index 00000000..e6a3df66 Binary files /dev/null and b/docs/images/client_page.png differ diff --git a/docs/images/client_page_fir.png b/docs/images/client_page_fir.png new file mode 100644 index 00000000..35100b09 Binary files /dev/null and b/docs/images/client_page_fir.png differ diff --git a/docs/images/client_page_icao.png b/docs/images/client_page_icao.png new file mode 100644 index 00000000..d5291c6d Binary files /dev/null and b/docs/images/client_page_icao.png differ diff --git a/docs/images/direct_access_page.png b/docs/images/direct_access_page.png new file mode 100644 index 00000000..4081eb07 Binary files /dev/null and b/docs/images/direct_access_page.png differ diff --git a/docs/images/example_basic_geo.png b/docs/images/example_basic_geo.png new file mode 100644 index 00000000..966b8e87 Binary files /dev/null and b/docs/images/example_basic_geo.png differ diff --git a/docs/images/example_multi_tabbed.png b/docs/images/example_multi_tabbed.png new file mode 100644 index 00000000..bfedcafa Binary files /dev/null and b/docs/images/example_multi_tabbed.png differ diff --git a/docs/images/example_simple_tabbed.png b/docs/images/example_simple_tabbed.png new file mode 100644 index 00000000..4af575d2 Binary files /dev/null and b/docs/images/example_simple_tabbed.png differ diff --git a/docs/images/geo_page.png b/docs/images/geo_page.png new file mode 100644 index 00000000..74487b16 Binary files /dev/null and b/docs/images/geo_page.png differ diff --git a/docs/images/tabbed.png b/docs/images/tabbed.png new file mode 100644 index 00000000..6cffbb1a Binary files /dev/null and b/docs/images/tabbed.png differ diff --git a/docs/schemas/profiles.schema.json b/docs/schemas/profiles.schema.json index 31799884..543fee60 100644 --- a/docs/schemas/profiles.schema.json +++ b/docs/schemas/profiles.schema.json @@ -26,6 +26,10 @@ "type": "string" } }, + "LabelStringOrLines": { + "description": "Label that can be either a single string or an array of strings (lines).", + "oneOf": [{ "type": "string" }, { "$ref": "#/$defs/LabelLines" }] + }, "FlexDirection": { "type": "string", "enum": ["row", "col"] @@ -62,19 +66,56 @@ "type": "number", "exclusiveMinimum": 0 }, + "FrequencyDisplayMode": { + "type": "string", + "enum": ["ShowAll", "HideAll"], + "default": "ShowAll" + }, + "ClientGroupMode": { + "type": "string", + "enum": ["None", "Fir", "Icao", "FirAndIcao"], + "default": "FirAndIcao" + }, + "ClientPageConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "include": { + "type": "array", + "items": { "type": "string" }, + "default": [] + }, + "exclude": { + "type": "array", + "items": { "type": "string" }, + "default": [] + }, + "priority": { + "type": "array", + "items": { "type": "string" }, + "default": ["*_FMP", "*_CTR", "*_APP", "*_TWR", "*_GND"] + }, + "frequencies": { "$ref": "#/$defs/FrequencyDisplayMode" }, + "grouping": { "$ref": "#/$defs/ClientGroupMode" } + } + }, "DirectAccessKey": { "type": "object", "additionalProperties": false, "required": ["label"], "properties": { - "label": { "$ref": "#/$defs/LabelLines" }, - "station_id": { "$ref": "#/$defs/StationId" } + "label": { "$ref": "#/$defs/LabelStringOrLines" }, + "station_id": { "$ref": "#/$defs/StationId" }, + "page": { "$ref": "#/$defs/DirectAccessPage" } + }, + "not": { + "required": ["station_id", "page"] } }, "DirectAccessPage": { "type": "object", "additionalProperties": false, - "required": ["rows", "keys"], + "required": ["rows"], "properties": { "rows": { "type": "integer", @@ -83,7 +124,12 @@ "keys": { "type": "array", "items": { "$ref": "#/$defs/DirectAccessKey" } - } + }, + "client_page": { "$ref": "#/$defs/ClientPageConfig" } + }, + "oneOf": [{ "required": ["keys"] }, { "required": ["client_page"] }], + "not": { + "required": ["keys", "client_page"] } }, "Tab": { @@ -91,7 +137,19 @@ "additionalProperties": false, "required": ["label", "page"], "properties": { - "label": { "type": "string", "minLength": 1 }, + "label": { + "allOf": [ + { "$ref": "#/$defs/LabelStringOrLines" }, + { + "if": { "type": "array" }, + "then": { "minItems": 1 } + }, + { + "if": { "type": "string" }, + "then": { "minLength": 1 } + } + ] + }, "page": { "$ref": "#/$defs/DirectAccessPage" } } }, @@ -102,7 +160,17 @@ "properties": { "label": { "description": "Geo page button labels must be non-empty and cannot exceed 3 lines.", - "allOf": [{ "$ref": "#/$defs/LabelLines" }, { "minItems": 1 }] + "allOf": [ + { "$ref": "#/$defs/LabelStringOrLines" }, + { + "if": { "type": "array" }, + "then": { "minItems": 1 } + }, + { + "if": { "type": "string" }, + "then": { "minLength": 1 } + } + ] }, "size": { "$ref": "#/$defs/PositiveNumber" diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..ff929efb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,69 @@ +{ + "name": "vacs-data", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vacs-data", + "version": "0.0.0", + "devDependencies": { + "prettier": "^3.8.1", + "prettier-plugin-toml": "^2.0.6" + } + }, + "node_modules/@taplo/core": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@taplo/core/-/core-0.2.0.tgz", + "integrity": "sha512-r8bl54Zj1In3QLkiW/ex694bVzpPJ9EhwqT9xkcUVODnVUGirdB1JTsmiIv0o1uwqZiwhi8xNnTOQBRQCpizrQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@taplo/lib": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@taplo/lib/-/lib-0.5.0.tgz", + "integrity": "sha512-+xIqpQXJco3T+VGaTTwmhxLa51qpkQxCjRwezjFZgr+l21ExlywJFcDfTrNmL6lG6tqb0h8GyJKO3UPGPtSCWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@taplo/core": "^0.2.0" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-toml": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/prettier-plugin-toml/-/prettier-plugin-toml-2.0.6.tgz", + "integrity": "sha512-12N/wBuHa9jd/KVy9pRP20NMKxQfQLMseQCt66lIbLaPLItvGUcSIryE1eZZMJ7loSws6Ig3M2Elc2EreNh76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@taplo/lib": "^0.5.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + }, + "peerDependencies": { + "prettier": "^3.0.3" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..557c0c89 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "vacs-data", + "version": "0.0.0", + "private": "true", + "description": "vacs Data Repository", + "scripts": { + "format": "prettier --write \"dataset/**/*.{json,toml}\"", + "format:check": "prettier --check \"dataset/**/*.{json,toml}\"", + "format:all": "prettier --write .", + "format:all:check": "prettier --check ." + }, + "devDependencies": { + "prettier": "^3.8.1", + "prettier-plugin-toml": "^2.0.6" + } +} diff --git a/tools/Cargo.lock b/tools/Cargo.lock index e43677ad..51fa5769 100644 --- a/tools/Cargo.lock +++ b/tools/Cargo.lock @@ -75,9 +75,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "clap" -version = "4.5.56" +version = "4.5.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75ca66430e33a14957acc24c5077b503e7d374151b2b4b3a10c83b4ceb4be0e" +checksum = "6899ea499e3fb9305a65d5ebf6e3d2248c5fab291f300ad0a704fbe142eae31a" dependencies = [ "clap_builder", "clap_derive", @@ -85,9 +85,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.56" +version = "4.5.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793207c7fa6300a0608d1080b858e5fdbe713cdc1c8db9fb17777d8a13e63df0" +checksum = "7b12c8b680195a62a8364d16b8447b01b6c2c8f9aaf68bee653be34d4245e238" dependencies = [ "anstream", "anstyle", @@ -232,9 +232,9 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "once_cell" @@ -280,9 +280,9 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -292,9 +292,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -303,9 +303,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" [[package]] name = "rustversion" @@ -411,7 +411,22 @@ dependencies = [ "indexmap", "serde_core", "serde_spanned", - "toml_datetime", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml" +version = "1.0.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1d7e18e3dd1d31e0ee5e863a8091ffec2fcc271636586042452b656a22c8ee1" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime 1.0.0+spec-1.1.0", "toml_parser", "toml_writer", "winnow", @@ -426,11 +441,20 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_datetime" +version = "1.0.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.0.7+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "247eaa3197818b831697600aadf81514e577e0cba5eab10f7e064e78ae154df1" dependencies = [ "winnow", ] @@ -505,7 +529,7 @@ dependencies = [ [[package]] name = "vacs-data-cli" -version = "0.1.0" +version = "0.4.0" dependencies = [ "clap", "vacs-data-diagnostics", @@ -515,21 +539,21 @@ dependencies = [ [[package]] name = "vacs-data-diagnostics" -version = "0.1.0" +version = "0.4.0" dependencies = [ "console", ] [[package]] name = "vacs-data-importer" -version = "0.1.0" +version = "0.4.0" dependencies = [ "console", "encoding_rs", "encoding_rs_io", "serde", "serde_json", - "toml", + "toml 1.0.0+spec-1.1.0", "vacs-data-diagnostics", "vacs-protocol", "vacs-vatsim", @@ -537,7 +561,7 @@ dependencies = [ [[package]] name = "vacs-data-validator" -version = "0.1.0" +version = "0.4.0" dependencies = [ "console", "vacs-data-diagnostics", @@ -547,7 +571,7 @@ dependencies = [ [[package]] name = "vacs-protocol" version = "1.1.0" -source = "git+https://github.com/MorpheusXAUT/vacs?branch=v2#fc588ebd0689f4e04e471b449e466594092fa452" +source = "git+https://github.com/MorpheusXAUT/vacs?branch=v2#5336e4d2af83de97c031c7f9ff0ba95c7b241866" dependencies = [ "serde", "serde_json", @@ -557,13 +581,13 @@ dependencies = [ [[package]] name = "vacs-vatsim" version = "0.2.1" -source = "git+https://github.com/MorpheusXAUT/vacs?branch=v2#fc588ebd0689f4e04e471b449e466594092fa452" +source = "git+https://github.com/MorpheusXAUT/vacs?branch=v2#5336e4d2af83de97c031c7f9ff0ba95c7b241866" dependencies = [ "regex", "serde", "serde_json", "thiserror", - "toml", + "toml 0.9.11+spec-1.1.0", "tracing", "vacs-protocol", ] @@ -651,6 +675,6 @@ checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "zmij" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1966f8ac2c1f76987d69a74d0e0f929241c10e78136434e3be70ff7f58f64214" +checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445" diff --git a/tools/Cargo.toml b/tools/Cargo.toml index 0a950691..f5be0634 100644 --- a/tools/Cargo.toml +++ b/tools/Cargo.toml @@ -3,7 +3,7 @@ resolver = "3" members = ["cli", "diagnostics", "importer", "validator"] [workspace.package] -version = "0.1.0" +version = "0.4.0" edition = "2024" license = "Apache-2.0 OR MIT" repository = "https://github.com/MorpheusXAUT/vacs-data" @@ -17,7 +17,7 @@ encoding_rs_io = "0.1.7" serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.149" thiserror = "2.0.18" -toml = { version = "0.9.11", features = ["serde"] } +toml = { version = "1.0.0", features = ["serde"] } tracing = "0.1.44" tracing-subscriber = { version = "0.3.22", features = ["env-filter", "json"] } vacs-data-diagnostics = { path = "./diagnostics" } diff --git a/tools/cli/src/cli.rs b/tools/cli/src/cli.rs index ae5ea070..674493c2 100644 --- a/tools/cli/src/cli.rs +++ b/tools/cli/src/cli.rs @@ -13,7 +13,7 @@ pub struct Cli { #[arg(short, long)] pub quiet: bool, - /// Logging output format + /// Logging output format. Supported: human, github #[arg(long, default_value_t = LogFormat::Human)] pub log_format: LogFormat, @@ -28,9 +28,10 @@ pub struct Cli { #[derive(Debug, Subcommand)] pub enum Command { /// Run validations on the whole dataset + #[command(arg_required_else_help = true)] Validate { - /// Dataset root to validate (positional). Defaults to repo dataset/ if found, else "." - #[arg(value_name = "INPUT")] + /// Dataset root to validate (positional). + #[arg(value_name = "INPUT", required_unless_present = "input")] input_pos: Option, /// Dataset root to validate @@ -48,13 +49,14 @@ pub enum Command { #[derive(Debug, Subcommand)] pub enum ImportCommand { /// Import data from the VATglasses project, converting it to vacs dataset format + #[command(arg_required_else_help = true)] Vatglasses { /// Input JSON file (positional) - #[arg(value_name = "INPUT")] + #[arg(value_name = "INPUT", required_unless_present = "input")] input_pos: Option, /// Output directory (positional) - #[arg(value_name = "OUTPUT")] + #[arg(value_name = "OUTPUT", required_unless_present = "output")] output_pos: Option, /// Input JSON file @@ -65,6 +67,10 @@ pub enum ImportCommand { #[arg(short, long)] output: Option, + /// Format to use for output files. Supported: toml, json + #[arg(short, long, default_value_t = vacs_data_importer::OutputFormat::Toml)] + format: vacs_data_importer::OutputFormat, + /// Overwrite existing files #[arg(long, conflicts_with = "merge")] overwrite: bool, @@ -75,13 +81,14 @@ pub enum ImportCommand { }, /// Import data from an EuroScope sectorfile, converting it to vacs dataset format + #[command(arg_required_else_help = true)] Euroscope { /// Input JSON file (positional) - #[arg(value_name = "INPUT")] + #[arg(value_name = "INPUT", required_unless_present = "input")] input_pos: Option, /// Output directory (positional) - #[arg(value_name = "OUTPUT")] + #[arg(value_name = "OUTPUT", required_unless_present = "output")] output_pos: Option, /// Input JSON file @@ -92,6 +99,10 @@ pub enum ImportCommand { #[arg(short, long)] output: Option, + /// Format to use for output files. Supported: toml, json + #[arg(short, long, default_value_t = vacs_data_importer::OutputFormat::Toml)] + format: vacs_data_importer::OutputFormat, + /// Prefixes to filter positions by #[arg(short, long, value_name = "PREFIX")] prefixes: Option>, diff --git a/tools/cli/src/main.rs b/tools/cli/src/main.rs index e3c83f93..87f03377 100644 --- a/tools/cli/src/main.rs +++ b/tools/cli/src/main.rs @@ -2,7 +2,6 @@ mod cli; use crate::cli::{Cli, Command, ImportCommand}; use clap::Parser; -use vacs_data_diagnostics::log; pub fn main() { let cli = Cli::parse(); @@ -10,10 +9,7 @@ pub fn main() { match cli.cmd { Command::Validate { input_pos, input } => { - let Some(input) = input.or(input_pos) else { - log::error("Missing input path. Either provide INPUT or use --i/--input."); - std::process::exit(2); - }; + let input = input.or(input_pos).unwrap(); if vacs_data_validator::validate(&input).is_err() { std::process::exit(1); @@ -28,18 +24,15 @@ pub fn main() { output, overwrite, merge, + format, }, } => { - let Some(input) = input.or(input_pos) else { - log::error("Missing input path. Either provide INPUT or use --i/--input."); - std::process::exit(2); - }; - let Some(output) = output.or(output_pos) else { - log::error("Missing output path. Either provide OUTPUT or use --o/--output."); - std::process::exit(2); - }; + let input = input.or(input_pos).unwrap(); + let output = output.or(output_pos).unwrap(); - if vacs_data_importer::vatglasses::parse(&input, &output, overwrite, merge).is_err() { + if vacs_data_importer::vatglasses::parse(&input, &output, overwrite, merge, format) + .is_err() + { std::process::exit(1); } } @@ -53,20 +46,17 @@ pub fn main() { prefixes, overwrite, merge, + format, }, } => { - let Some(input) = input.or(input_pos) else { - log::error("Missing input path. Either provide INPUT or use --i/--input."); - std::process::exit(2); - }; - let Some(output) = output.or(output_pos) else { - log::error("Missing output path. Either provide OUTPUT or use --o/--output."); - std::process::exit(2); - }; + let input = input.or(input_pos).unwrap(); + let output = output.or(output_pos).unwrap(); let prefixes = prefixes.unwrap_or_default(); - if vacs_data_importer::euroscope::parse(&input, &output, &prefixes, overwrite, merge) - .is_err() + if vacs_data_importer::euroscope::parse( + &input, &output, &prefixes, overwrite, merge, format, + ) + .is_err() { std::process::exit(1); } diff --git a/tools/deny.toml b/tools/deny.toml index 4eb8a1a7..af451cfc 100644 --- a/tools/deny.toml +++ b/tools/deny.toml @@ -4,8 +4,12 @@ all-features = true [licenses] allow = ["MIT", "Apache-2.0"] exceptions = [ - { allow = ["BSD-3-Clause"], crate = "encoding_rs" }, - { allow = ["Unicode-3.0"], crate = "unicode-ident" }, + { allow = [ + "BSD-3-Clause", + ], crate = "encoding_rs" }, + { allow = [ + "Unicode-3.0", + ], crate = "unicode-ident" }, ] [licenses.private] @@ -19,6 +23,4 @@ allow-wildcard-paths = true [sources] unknown-registry = "deny" unknown-git = "deny" -allow-git = [ - "https://github.com/MorpheusXAUT/vacs" -] +allow-git = ["https://github.com/MorpheusXAUT/vacs"] diff --git a/tools/importer/src/euroscope.rs b/tools/importer/src/euroscope.rs index fc208214..8356ed36 100644 --- a/tools/importer/src/euroscope.rs +++ b/tools/importer/src/euroscope.rs @@ -15,6 +15,7 @@ pub fn parse( prefixes: &[String], overwrite: bool, merge: bool, + format: crate::OutputFormat, ) -> Result<(), Box> { log::info(format_args!( "Parsing EuroScope sectorfile data from {input:?} to {output:?}" @@ -23,8 +24,14 @@ pub fn parse( crate::check_input_exists(input)?; crate::ensure_output_directory(output)?; - let output_positions = - crate::check_output_file(output, "positions.toml", "Positions", overwrite, merge)?; + let ext = format.ext(); + let output_positions = crate::check_output_file( + output, + &format!("positions.{ext}"), + "Positions", + overwrite, + merge, + )?; let file = match std::fs::File::open(input) { Ok(f) => f, @@ -104,13 +111,14 @@ pub fn parse( .then_with(|| a.id.cmp(&b.id)) }); - let serialized_positions = match toml::to_string_pretty(&PositionConfigFile { positions }) { - Ok(s) => s, - Err(err) => { - log::error(format_args!("Failed to serialize positions: {err:?}")); - return Err(err.into()); - } - }; + let serialized_positions = + match crate::format::serialize(&PositionConfigFile { positions }, format) { + Ok(s) => s, + Err(err) => { + log::error(format_args!("Failed to serialize positions: {err:?}")); + return Err(err); + } + }; crate::write_output_file(&output_positions, &serialized_positions, "Positions")?; diff --git a/tools/importer/src/format.rs b/tools/importer/src/format.rs new file mode 100644 index 00000000..70d4ba74 --- /dev/null +++ b/tools/importer/src/format.rs @@ -0,0 +1,62 @@ +use serde::Serialize; +use std::fmt; +use std::str::FromStr; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum OutputFormat { + #[default] + Toml, + Json, +} + +impl OutputFormat { + #[must_use] + pub const fn variants() -> &'static [&'static str] { + &["toml", "json"] + } +} + +impl fmt::Display for OutputFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + OutputFormat::Toml => write!(f, "toml"), + OutputFormat::Json => write!(f, "json"), + } + } +} + +impl FromStr for OutputFormat { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_ascii_lowercase().as_str() { + "toml" => Ok(OutputFormat::Toml), + "json" => Ok(OutputFormat::Json), + _ => Err(format!( + "Invalid output format: {}. Supported formats: {}", + s, + Self::variants().join(", ") + )), + } + } +} + +impl OutputFormat { + #[must_use] + pub const fn ext(&self) -> &'static str { + match self { + OutputFormat::Toml => "toml", + OutputFormat::Json => "json", + } + } +} + +pub fn serialize( + value: &T, + format: OutputFormat, +) -> Result> { + match format { + OutputFormat::Toml => Ok(toml::to_string_pretty(value)?), + OutputFormat::Json => Ok(serde_json::to_string_pretty(value)?), + } +} diff --git a/tools/importer/src/lib.rs b/tools/importer/src/lib.rs index ea7ad78e..f493aaee 100644 --- a/tools/importer/src/lib.rs +++ b/tools/importer/src/lib.rs @@ -1,6 +1,9 @@ pub mod euroscope; +pub mod format; pub mod vatglasses; +pub use format::OutputFormat; + use std::path::{Path, PathBuf}; use vacs_data_diagnostics::log; diff --git a/tools/importer/src/vatglasses.rs b/tools/importer/src/vatglasses.rs index f40a9d24..f63ea7bc 100644 --- a/tools/importer/src/vatglasses.rs +++ b/tools/importer/src/vatglasses.rs @@ -13,6 +13,7 @@ pub fn parse( output: &PathBuf, overwrite: bool, merge: bool, + format: crate::OutputFormat, ) -> Result<(), Box> { log::info(format_args!( "Parsing VATglasses data from {input:?} to {output:?}" @@ -21,11 +22,22 @@ pub fn parse( crate::check_input_exists(input)?; crate::ensure_output_directory(output)?; - let output_stations = - crate::check_output_file(output, "stations.toml", "Stations", overwrite, merge)?; - - let output_positions = - crate::check_output_file(output, "positions.toml", "Positions", overwrite, merge)?; + let ext = format.ext(); + let output_stations = crate::check_output_file( + output, + &format!("stations.{ext}"), + "Stations", + overwrite, + merge, + )?; + + let output_positions = crate::check_output_file( + output, + &format!("positions.{ext}"), + "Positions", + overwrite, + merge, + )?; let file = match std::fs::File::open(input) { Ok(f) => f, @@ -82,11 +94,11 @@ pub fn parse( stations.stations.sort_by(|a, b| a.id.cmp(&b.id)); - let serialized_stations = match toml::to_string_pretty(&stations) { + let serialized_stations = match crate::format::serialize(&stations, format) { Ok(s) => s, Err(err) => { log::error(format_args!("Failed to serialize stations: {err:?}")); - return Err(err.into()); + return Err(err); } }; @@ -132,11 +144,11 @@ pub fn parse( .then_with(|| a.id.cmp(&b.id)) }); - let serialized_positions = match toml::to_string_pretty(&positions) { + let serialized_positions = match crate::format::serialize(&positions, format) { Ok(s) => s, Err(err) => { log::error(format_args!("Failed to serialize positions: {err:?}")); - return Err(err.into()); + return Err(err); } };