@fboes/aerofly-custom-missions 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/.editorconfig +4 -0
  2. package/AGENTS.md +43 -0
  3. package/CHANGELOG.md +19 -13
  4. package/LICENSE.txt +1 -1
  5. package/README.md +6 -1
  6. package/dist/check/TestHelpers.js +13 -0
  7. package/dist/check/TestHelpers.test.js +8 -0
  8. package/dist/dto/AeroflyLocalizedText.test.js +16 -0
  9. package/dist/dto/AeroflyMission.js +12 -2
  10. package/dist/dto/AeroflyMission.test.js +166 -0
  11. package/dist/dto/AeroflyMissionCheckpoint.js +1 -1
  12. package/dist/dto/AeroflyMissionCheckpoint.test.js +41 -0
  13. package/dist/dto/AeroflyMissionConditions.js +3 -3
  14. package/dist/dto/AeroflyMissionConditions.test.js +33 -0
  15. package/dist/dto/AeroflyMissionConditionsCloud.js +2 -2
  16. package/dist/dto/AeroflyMissionConditionsCloud.test.js +25 -0
  17. package/dist/dto/AeroflyMissionTargetPlane.js +1 -1
  18. package/dist/dto/AeroflyMissionTargetPlane.test.js +14 -0
  19. package/dist/dto/AeroflyMissionsList.test.js +16 -0
  20. package/dist/dto-flight/AeroflyFlight.js +94 -0
  21. package/dist/dto-flight/AeroflyFlight.test.js +69 -0
  22. package/dist/dto-flight/AeroflyNavRouteAirports.js +53 -0
  23. package/dist/dto-flight/AeroflyNavRouteBase.js +54 -0
  24. package/dist/dto-flight/AeroflyNavRouteRunway.js +70 -0
  25. package/dist/dto-flight/AeroflyNavRouteTransition.js +75 -0
  26. package/dist/dto-flight/AeroflyNavRouteWaypoint.js +58 -0
  27. package/dist/dto-flight/AeroflyNavigationConfig.js +50 -0
  28. package/dist/dto-flight/AeroflySettingsAircraft.js +28 -0
  29. package/dist/dto-flight/AeroflySettingsCloud.js +103 -0
  30. package/dist/dto-flight/AeroflySettingsCloud.test.js +53 -0
  31. package/dist/dto-flight/AeroflySettingsFlight.js +95 -0
  32. package/dist/dto-flight/AeroflySettingsFlight.test.js +52 -0
  33. package/dist/dto-flight/AeroflySettingsFuelLoad.js +30 -0
  34. package/dist/dto-flight/AeroflySettingsWind.js +74 -0
  35. package/dist/dto-flight/AeroflyTimeUtc.js +84 -0
  36. package/dist/dto-flight/AeroflyWaypoint.js +85 -0
  37. package/dist/index.js +13 -0
  38. package/dist/node/AeroflyBasicTypes.js +61 -0
  39. package/dist/node/AeroflyConfigurationNode.test.js +28 -0
  40. package/dist/node/Convert copy.js +67 -0
  41. package/dist/node/Convert.js +72 -0
  42. package/dist/node/Convert.test.js +50 -0
  43. package/docs/custom_missions_user.tmc +2 -2
  44. package/docs/custom_missions_user.xml +3 -4
  45. package/docs/flight-mcf.md +7 -0
  46. package/docs/flight.mcf +116 -0
  47. package/docs/flight.xml +116 -0
  48. package/package.json +4 -2
  49. package/src/check/TestHelpers.test.ts +10 -0
  50. package/src/check/TestHelpers.ts +16 -0
  51. package/src/dto/AeroflyLocalizedText.test.ts +20 -0
  52. package/src/dto/AeroflyMission.test.ts +185 -0
  53. package/src/dto/AeroflyMission.ts +26 -3
  54. package/src/dto/AeroflyMissionCheckpoint.test.ts +50 -0
  55. package/src/dto/AeroflyMissionCheckpoint.ts +2 -2
  56. package/src/dto/AeroflyMissionConditions.test.ts +38 -0
  57. package/src/dto/AeroflyMissionConditions.ts +4 -4
  58. package/src/dto/AeroflyMissionConditionsCloud.test.ts +29 -0
  59. package/src/dto/AeroflyMissionConditionsCloud.ts +15 -5
  60. package/src/dto/AeroflyMissionTargetPlane.test.ts +15 -0
  61. package/src/dto/AeroflyMissionTargetPlane.ts +2 -2
  62. package/src/dto/AeroflyMissionsList.test.ts +19 -0
  63. package/src/dto-flight/AeroflyFlight.test.ts +87 -0
  64. package/src/dto-flight/AeroflyFlight.ts +180 -0
  65. package/src/dto-flight/AeroflyNavRouteAirports.ts +86 -0
  66. package/src/dto-flight/AeroflyNavRouteBase.ts +109 -0
  67. package/src/dto-flight/AeroflyNavRouteRunway.ts +123 -0
  68. package/src/dto-flight/AeroflyNavRouteTransition.ts +127 -0
  69. package/src/dto-flight/AeroflyNavRouteWaypoint.ts +98 -0
  70. package/src/dto-flight/AeroflyNavigationConfig.ts +79 -0
  71. package/src/dto-flight/AeroflySettingsAircraft.ts +51 -0
  72. package/src/dto-flight/AeroflySettingsCloud.test.ts +68 -0
  73. package/src/dto-flight/AeroflySettingsCloud.ts +137 -0
  74. package/src/dto-flight/AeroflySettingsFlight.test.ts +70 -0
  75. package/src/dto-flight/AeroflySettingsFlight.ts +157 -0
  76. package/src/dto-flight/AeroflySettingsFuelLoad.ts +62 -0
  77. package/src/dto-flight/AeroflySettingsWind.ts +114 -0
  78. package/src/dto-flight/AeroflyTimeUtc.ts +103 -0
  79. package/src/index.ts +23 -0
  80. package/src/node/AeroflyConfigurationNode.test.ts +46 -0
  81. package/src/node/AeroflyConfigurationNode.ts +2 -2
  82. package/src/node/Convert.test.ts +90 -0
  83. package/src/node/Convert.ts +90 -0
  84. package/types/check/TestHelpers.d.ts +3 -0
  85. package/types/check/TestHelpers.d.ts.map +1 -0
  86. package/types/check/TestHelpers.test.d.ts +2 -0
  87. package/types/check/TestHelpers.test.d.ts.map +1 -0
  88. package/types/dto/AeroflyConfigFileSet.d.ts +1 -1
  89. package/types/dto/AeroflyConfiguration.interface.d.ts +1 -1
  90. package/types/dto/AeroflyLocalizedText.d.ts +1 -1
  91. package/types/dto/AeroflyLocalizedText.test.d.ts +2 -0
  92. package/types/dto/AeroflyLocalizedText.test.d.ts.map +1 -0
  93. package/types/dto/AeroflyMission.d.ts +47 -4
  94. package/types/dto/AeroflyMission.d.ts.map +1 -1
  95. package/types/dto/AeroflyMission.test.d.ts +2 -0
  96. package/types/dto/AeroflyMission.test.d.ts.map +1 -0
  97. package/types/dto/AeroflyMissionCheckpoint.d.ts +32 -8
  98. package/types/dto/AeroflyMissionCheckpoint.test.d.ts +2 -0
  99. package/types/dto/AeroflyMissionCheckpoint.test.d.ts.map +1 -0
  100. package/types/dto/AeroflyMissionConditions.d.ts +15 -6
  101. package/types/dto/AeroflyMissionConditions.test.d.ts +2 -0
  102. package/types/dto/AeroflyMissionConditions.test.d.ts.map +1 -0
  103. package/types/dto/AeroflyMissionConditionsCloud.d.ts +4 -4
  104. package/types/dto/AeroflyMissionConditionsCloud.d.ts.map +1 -1
  105. package/types/dto/AeroflyMissionConditionsCloud.test.d.ts +2 -0
  106. package/types/dto/AeroflyMissionConditionsCloud.test.d.ts.map +1 -0
  107. package/types/dto/AeroflyMissionTargetPlane.d.ts +3 -3
  108. package/types/dto/AeroflyMissionTargetPlane.test.d.ts +2 -0
  109. package/types/dto/AeroflyMissionTargetPlane.test.d.ts.map +1 -0
  110. package/types/dto/AeroflyMissionsList.d.ts +1 -1
  111. package/types/dto/AeroflyMissionsList.test.d.ts +2 -0
  112. package/types/dto/AeroflyMissionsList.test.d.ts.map +1 -0
  113. package/types/dto/index.test.d.ts +2 -0
  114. package/types/dto/index.test.d.ts.map +1 -0
  115. package/types/dto-flight/AeroflyFlight.d.ts +103 -0
  116. package/types/dto-flight/AeroflyFlight.d.ts.map +1 -0
  117. package/types/dto-flight/AeroflyFlight.test.d.ts +2 -0
  118. package/types/dto-flight/AeroflyFlight.test.d.ts.map +1 -0
  119. package/types/dto-flight/AeroflyNavRouteAirports.d.ts +55 -0
  120. package/types/dto-flight/AeroflyNavRouteAirports.d.ts.map +1 -0
  121. package/types/dto-flight/AeroflyNavRouteBase.d.ts +65 -0
  122. package/types/dto-flight/AeroflyNavRouteBase.d.ts.map +1 -0
  123. package/types/dto-flight/AeroflyNavRouteRunway.d.ts +54 -0
  124. package/types/dto-flight/AeroflyNavRouteRunway.d.ts.map +1 -0
  125. package/types/dto-flight/AeroflyNavRouteTransition.d.ts +62 -0
  126. package/types/dto-flight/AeroflyNavRouteTransition.d.ts.map +1 -0
  127. package/types/dto-flight/AeroflyNavRouteWaypoint.d.ts +49 -0
  128. package/types/dto-flight/AeroflyNavRouteWaypoint.d.ts.map +1 -0
  129. package/types/dto-flight/AeroflyNavigationConfig.d.ts +38 -0
  130. package/types/dto-flight/AeroflyNavigationConfig.d.ts.map +1 -0
  131. package/types/dto-flight/AeroflySettingsAircraft.d.ts +32 -0
  132. package/types/dto-flight/AeroflySettingsAircraft.d.ts.map +1 -0
  133. package/types/dto-flight/AeroflySettingsCloud.d.ts +45 -0
  134. package/types/dto-flight/AeroflySettingsCloud.d.ts.map +1 -0
  135. package/types/dto-flight/AeroflySettingsCloud.test.d.ts +2 -0
  136. package/types/dto-flight/AeroflySettingsCloud.test.d.ts.map +1 -0
  137. package/types/dto-flight/AeroflySettingsFlight.d.ts +66 -0
  138. package/types/dto-flight/AeroflySettingsFlight.d.ts.map +1 -0
  139. package/types/dto-flight/AeroflySettingsFlight.test.d.ts +2 -0
  140. package/types/dto-flight/AeroflySettingsFlight.test.d.ts.map +1 -0
  141. package/types/dto-flight/AeroflySettingsFuelLoad.d.ts +42 -0
  142. package/types/dto-flight/AeroflySettingsFuelLoad.d.ts.map +1 -0
  143. package/types/dto-flight/AeroflySettingsWind.d.ts +63 -0
  144. package/types/dto-flight/AeroflySettingsWind.d.ts.map +1 -0
  145. package/types/dto-flight/AeroflyTimeUtc.d.ts +47 -0
  146. package/types/dto-flight/AeroflyTimeUtc.d.ts.map +1 -0
  147. package/types/dto-flight/AeroflyWaypoint.d.ts +68 -0
  148. package/types/dto-flight/AeroflyWaypoint.d.ts.map +1 -0
  149. package/types/index.d.ts +20 -1
  150. package/types/index.d.ts.map +1 -1
  151. package/types/index.test.d.ts +1 -1
  152. package/types/node/AeroflyBasicTypes.d.ts +13 -0
  153. package/types/node/AeroflyBasicTypes.d.ts.map +1 -0
  154. package/types/node/AeroflyConfigurationNode.d.ts +14 -4
  155. package/types/node/AeroflyConfigurationNode.d.ts.map +1 -1
  156. package/types/node/AeroflyConfigurationNode.test.d.ts +2 -0
  157. package/types/node/AeroflyConfigurationNode.test.d.ts.map +1 -0
  158. package/types/node/Convert copy.d.ts +15 -0
  159. package/types/node/Convert copy.d.ts.map +1 -0
  160. package/types/node/Convert.d.ts +15 -0
  161. package/types/node/Convert.d.ts.map +1 -0
  162. package/types/node/Convert.test.d.ts +2 -0
  163. package/types/node/Convert.test.d.ts.map +1 -0
  164. package/dist/index.test.js +0 -259
  165. package/src/index.test.ts +0 -298
package/.editorconfig CHANGED
@@ -10,3 +10,7 @@ charset = utf-8
10
10
  indent_style = space
11
11
  indent_size = 4
12
12
  max_line_length = 120
13
+
14
+ [*.md]
15
+ trim_trailing_whitespace = false
16
+ indent_size = 2
package/AGENTS.md ADDED
@@ -0,0 +1,43 @@
1
+ # AGENTS.md
2
+
3
+ ## Project Overview
4
+
5
+ This project provides custom mission support for Aerofly flight simulators. It includes TypeScript source code, DTOs, configuration files, and supporting documentation for mission creation and management.
6
+
7
+ ## Coding Guidelines
8
+
9
+ - **Explicit Class Properties:**
10
+ - All class properties must be explicitly defined in every class. Avoid using implicit or dynamic property assignment.
11
+ - **JSDoc Comments:**
12
+ - Add descriptive JSDoc comments to every class property and method. This improves code readability and enables rich IntelliSense in VS Code.
13
+ - Example:
14
+ ```typescript
15
+ /**
16
+ * @property {string} name The name of the mission
17
+ */
18
+ name: string;
19
+ ```
20
+ - **TypeScript Usage:**
21
+ - Use TypeScript types and interfaces for all data structures.
22
+ - Keep types up to date in the `types/` directory.
23
+
24
+ ## Contribution
25
+
26
+ - Follow the coding guidelines above for all new code and pull requests.
27
+ - See the `README.md` for setup and usage instructions.
28
+
29
+ ## Testing
30
+
31
+ - TypeScript files have corresponding test files with the same filename and an added `.test` before the extension (e.g., `AeroflyMission.ts` and `AeroflyMission.test.ts`).
32
+ - Run all tests using:
33
+ ```sh
34
+ npm test
35
+ ```
36
+ - To run a single test file, specify its name:
37
+ ```sh
38
+ node --test src/dto/AeroflyMission.test.ts
39
+ ```
40
+ - Create new tests by using the `node:test`
41
+ ```typescript
42
+ import { describe, it } from "node:test";
43
+ ```
package/CHANGELOG.md CHANGED
@@ -2,27 +2,33 @@
2
2
 
3
3
  This changelog documents all notable changes to the Aerofly Custom Missions project. Each version entry includes a list of changes, with the most recent version at the top.
4
4
 
5
- ## 1.3.1
5
+ ## [1.4.0]
6
+
7
+ - Changed testing to standard Node.js testing suite
8
+ - Added `fuel_mass` and `payload_mass` properties
9
+ - Added `flight.mcf` / `main.mcf` DTO
10
+
11
+ ## [1.3.1]
6
12
 
7
13
  - Re-introduced spacers between exported missions
8
14
  - Re-introduced commented out nodes for exported missions
9
15
 
10
- ## 1.3.0
16
+ ## [1.3.0]
11
17
 
12
18
  - Changed TMC code generation with nodes
13
19
 
14
- ## 1.2.3
20
+ ## [1.2.3]
15
21
 
16
22
  - Internal restructuring of mission generation logic
17
23
  - Optimized waypoint handling and validation
18
24
  - Improved error handling for mission parsing
19
25
 
20
- ## 1.2.2
26
+ ## [1.2.2]
21
27
 
22
28
  - Added altitude constraint property for waypoints
23
29
  - Improved handling of cloud layers with better validation
24
30
 
25
- ## 1.2.1
31
+ ## [1.2.1]
26
32
 
27
33
  - Changed handling of checkpoints to support missions without checkpoints
28
34
  - Improved file generation for programmatic mission creation
@@ -36,7 +42,7 @@ This changelog documents all notable changes to the Aerofly Custom Missions proj
36
42
  - `before_start`
37
43
  - `pushback`
38
44
 
39
- ## 1.2.0
45
+ ## [1.2.0]
40
46
 
41
47
  - Added new cloud level properties:
42
48
  - `cirrus_cover` for high-altitude cloud coverage
@@ -44,13 +50,13 @@ This changelog documents all notable changes to the Aerofly Custom Missions proj
44
50
  - Added new waypoint property `fly_over` for precise waypoint navigation
45
51
  - Added `finish` property to mark mission completion points
46
52
 
47
- ## 1.1.1
53
+ ## [1.1.1]
48
54
 
49
55
  - Fixed styling issues in mission display
50
56
  - Improved UI consistency across different mission types
51
57
  - Enhanced error message formatting
52
58
 
53
- ## 1.1.0
59
+ ## [1.1.0]
54
60
 
55
61
  - Added new mission metadata properties:
56
62
  - `tags` for mission categorization
@@ -64,31 +70,31 @@ This changelog documents all notable changes to the Aerofly Custom Missions proj
64
70
  - `aerotow` for towed aircraft operations
65
71
  - Improved temperature property with better unit handling
66
72
 
67
- ## 1.0.4
73
+ ## [1.0.4]
68
74
 
69
75
  - Added comprehensive documentation for known issues and workarounds
70
76
  - Added `AeroflyMissionConditions.temperature` property with Celsius support
71
77
  - Improved error handling for weather conditions
72
78
 
73
- ## 1.0.3
79
+ ## [1.0.3]
74
80
 
75
81
  - Enhanced API documentation with examples
76
82
  - Added detailed parameter descriptions
77
83
  - Improved code documentation
78
84
 
79
- ## 1.0.2
85
+ ## [1.0.2]
80
86
 
81
87
  - Added shorthand properties for common mission parameters
82
88
  - Improved property access methods
83
89
  - Enhanced mission validation
84
90
 
85
- ## 1.0.1
91
+ ## [1.0.1]
86
92
 
87
93
  - Added initial API documentation
88
94
  - Improved code comments
89
95
  - Added basic usage examples
90
96
 
91
- ## 1.0.0
97
+ ## [1.0.0]
92
98
 
93
99
  - Initial release of Aerofly Custom Missions
94
100
  - Basic mission creation and editing functionality
package/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright © 2024 Frank Boës
3
+ Copyright © 2024-2026 Frank Boës
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
6
 
package/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  [Aerofly Flight Simulator 4](https://www.aerofly.com/) has a custom missions file `custom_missions_user.tmc` with a very unique format. To help you build this custom missions file, this JavaScript / TypeScript library offers Data Objects to create this file programmatically.
6
6
 
7
+ This simulator also defines flight plans and settings in the `main.mcf`, which is automatically loaded on simulator startup. This library contains a proposal for a `flight.mcf`, which is a stripped-down version of the `main.mcf` which only contains all information necessary for flight set-up. See [the documentation for `flight.mcf`](./docs/flight-mcf.md).
8
+
7
9
  This library is intended to work in modern browsers as well as [Node.js](https://nodejs.org/en).
8
10
 
9
11
  ## Installation
@@ -52,6 +54,9 @@ import {
52
54
 
53
55
  You might want to enable TypeScript type checking by adding `// @ts-check` as your first line in your scripts.
54
56
 
57
+ > [!NOTE]
58
+ > This library also contains classes for building the yet unsupported `flight.mcf`. Please refer to the code on how to use this. See [the documentation for `flight.mcf`](./docs/flight-mcf.md).
59
+
55
60
  ### Basic idea
56
61
 
57
62
  All objects are basic structures needed for the missions list. The constructors tell you which properties are required, and will start with sensible defaults for all other properties.
@@ -60,7 +65,7 @@ All objects can be exported as JSON or as string via the `toString()` methods. E
60
65
 
61
66
  ### Building a missions file
62
67
 
63
- A mission file contains one or multiple missions. Building this file starts with the outer container, wich contains the missions:
68
+ A `custom_missions_user.tmc` mission file contains one or multiple missions. Building this file starts with the outer container, wich contains the missions:
64
69
 
65
70
  ```javascript
66
71
  // Build a missions list
@@ -0,0 +1,13 @@
1
+ import { strict as assert } from "node:assert";
2
+ export const assertValidAeroflyStructure = (aeroflyString) => {
3
+ const openingBrackets = aeroflyString.match(/</g);
4
+ const closingBrackets = aeroflyString.match(/>/g);
5
+ const openingBrackets2 = aeroflyString.match(/\[/g);
6
+ const closingBrackets2 = aeroflyString.match(/\]/g);
7
+ assert.ok(openingBrackets?.length ?? 0 > 0, "Has opening <");
8
+ assert.strictEqual(openingBrackets?.length, closingBrackets?.length, "Number of <> matches");
9
+ assert.strictEqual(openingBrackets2?.length, closingBrackets2?.length, "Number of [] matches");
10
+ };
11
+ export const assertIncludes = (string, includes) => {
12
+ assert.ok(string.includes(includes), `Includes "${includes}"`);
13
+ };
@@ -0,0 +1,8 @@
1
+ import { assertValidAeroflyStructure } from "../check/TestHelpers.js";
2
+ import { describe, it } from "node:test";
3
+ describe("TestHelpers", () => {
4
+ it("should validate Aerofly structure correctly", () => {
5
+ const validString = "<>";
6
+ assertValidAeroflyStructure(validString);
7
+ });
8
+ });
@@ -0,0 +1,16 @@
1
+ import { AeroflyLocalizedText } from "./AeroflyLocalizedText.js";
2
+ import { strict as assert } from "node:assert";
3
+ import { assertValidAeroflyStructure } from "../check/TestHelpers.js";
4
+ import { describe, it } from "node:test";
5
+ describe("AeroflyLocalizedText", () => {
6
+ const localizedText = new AeroflyLocalizedText("de", "Test", "Test2");
7
+ it("should correctly map constructor values", () => {
8
+ assert.deepStrictEqual(localizedText.language, "de");
9
+ assert.deepStrictEqual(localizedText.title, "Test");
10
+ assert.deepStrictEqual(localizedText.description, "Test2");
11
+ assertValidAeroflyStructure(localizedText.toString());
12
+ });
13
+ it("should create a valid Aerofly mission", () => {
14
+ assertValidAeroflyStructure(localizedText.toString());
15
+ });
16
+ });
@@ -23,6 +23,8 @@ export class AeroflyMission {
23
23
  * @param {"cold_and_dark"|"before_start"|"taxi"|"takeoff"|"cruise"|"approach"|"landing"|"winch_launch"|"aerotow"|"pushback"} [additionalAttributes.flightSetting] of aircraft, like "taxi", "cruise"
24
24
  * @param {{name:string,livery:string,icao:string}} [additionalAttributes.aircraft] for this mission
25
25
  * @param {string} [additionalAttributes.callsign] of aircraft, uppercased
26
+ * @param {?number} [additionalAttributes.fuelMass] in kg
27
+ * @param {?number} [additionalAttributes.payloadMass] in kg
26
28
  * @param {object} [additionalAttributes.origin] position of aircraft, as well as name of starting airport. Position does not have match airport.
27
29
  * @param {object} [additionalAttributes.destination] position of aircraft, as well as name of destination airport. Position does not have match airport.
28
30
  * @param {?number} [additionalAttributes.distance] in meters
@@ -36,7 +38,7 @@ export class AeroflyMission {
36
38
  name: "c172",
37
39
  icao: "",
38
40
  livery: "",
39
- }, callsign = "", origin = {
41
+ }, callsign = "", fuelMass = null, payloadMass = null, origin = {
40
42
  icao: "",
41
43
  longitude: 0,
42
44
  latitude: 0,
@@ -60,6 +62,8 @@ export class AeroflyMission {
60
62
  this.flightSetting = flightSetting;
61
63
  this.aircraft = aircraft;
62
64
  this.callsign = callsign;
65
+ this.fuelMass = fuelMass;
66
+ this.payloadMass = payloadMass;
63
67
  this.origin = origin;
64
68
  this.destination = destination;
65
69
  this.distance = distance;
@@ -130,11 +134,17 @@ export class AeroflyMission {
130
134
  }
131
135
  element.appendChild("string8", "flight_setting", this.flightSetting);
132
136
  element.appendChild("string8u", "aircraft_name", this.aircraft.name);
137
+ element.appendChild("stringt8c", "aircraft_icao", this.aircraft.icao);
133
138
  if (this.aircraft.livery) {
134
139
  element.append(new AeroflyConfigurationNodeComment("string8", "aircraft_livery", this.aircraft.livery));
135
140
  }
136
- element.appendChild("stringt8c", "aircraft_icao", this.aircraft.icao);
137
141
  element.appendChild("stringt8c", "callsign", this.callsign);
142
+ if (this.fuelMass !== null) {
143
+ element.append(new AeroflyConfigurationNodeComment("float64", "fuel_mass", this.fuelMass, `kg`));
144
+ }
145
+ if (this.payloadMass !== null) {
146
+ element.append(new AeroflyConfigurationNodeComment("float64", "payload_mass", this.payloadMass, `kg`));
147
+ }
138
148
  element.appendChild("stringt8c", "origin_icao", this.origin.icao);
139
149
  element.appendChild("tmvector2d", "origin_lon_lat", [this.origin.longitude, this.origin.latitude]);
140
150
  element.appendChild("float64", "origin_alt", this.origin.alt, `${Math.ceil(this.origin.alt * feetPerMeter)} ft MSL`);
@@ -0,0 +1,166 @@
1
+ import { AeroflyMission } from "./AeroflyMission.js";
2
+ import { strict as assert } from "node:assert";
3
+ import fs from "node:fs";
4
+ import { assertIncludes, assertValidAeroflyStructure } from "../check/TestHelpers.js";
5
+ import { describe, it } from "node:test";
6
+ import { AeroflyLocalizedText } from "./AeroflyLocalizedText.js";
7
+ import { AeroflyMissionCheckpoint } from "./AeroflyMissionCheckpoint.js";
8
+ import { AeroflyMissionConditions } from "./AeroflyMissionConditions.js";
9
+ import { AeroflyMissionConditionsCloud } from "./AeroflyMissionConditionsCloud.js";
10
+ import { AeroflyMissionsList } from "./AeroflyMissionsList.js";
11
+ import { AeroflyMissionTargetPlane } from "./AeroflyMissionTargetPlane.js";
12
+ describe("AeroflyMission", () => {
13
+ const mission = new AeroflyMission("Title", {
14
+ origin: {
15
+ icao: "XXXX",
16
+ longitude: 0,
17
+ latitude: 0,
18
+ dir: 0,
19
+ alt: 0,
20
+ },
21
+ destination: {
22
+ icao: "XXXX",
23
+ longitude: 0,
24
+ latitude: 0,
25
+ dir: 0,
26
+ alt: 0,
27
+ },
28
+ });
29
+ it("should correctly map constructor values", () => {
30
+ assert.strictEqual(mission.title, "Title");
31
+ });
32
+ it("should create a valid Aerofly mission", () => {
33
+ assertValidAeroflyStructure(mission.toString());
34
+ });
35
+ it("should create a valid XML mission", () => {
36
+ assertValidAeroflyStructure(mission.getElement().toXmlString());
37
+ });
38
+ it("should create AeroflyMissionConditions correctly", () => {
39
+ const conditions = new AeroflyMissionConditions({
40
+ time: new Date(Date.UTC(2024, 5, 14, 13, 15, 38)),
41
+ wind: {
42
+ direction: 190,
43
+ speed: 11,
44
+ gusts: 22,
45
+ },
46
+ turbulenceStrength: 1,
47
+ temperature: 21,
48
+ visibility: 14484.096000000001,
49
+ clouds: [
50
+ AeroflyMissionConditionsCloud.createInFeet(0.1, 5000),
51
+ AeroflyMissionConditionsCloud.createInFeet(0.2, 7500),
52
+ AeroflyMissionConditionsCloud.createInFeet(0.1, 9500),
53
+ ],
54
+ });
55
+ assert.strictEqual(conditions.wind.direction, 190);
56
+ assert.strictEqual(conditions.wind.speed, 11);
57
+ assert.strictEqual(conditions.wind.gusts, 22);
58
+ assert.strictEqual(conditions.turbulenceStrength, 1);
59
+ assert.strictEqual(conditions.temperature, 21);
60
+ assert.strictEqual(conditions.visibility, 14484.096000000001);
61
+ const checkpoints = [
62
+ new AeroflyMissionCheckpoint("KCCR", "origin", -122.057, 37.9897, {
63
+ altitude: 8,
64
+ }),
65
+ new AeroflyMissionCheckpoint("19L", "departure_runway", -122.05504061196366, 37.993168229891225, {
66
+ length: 844.2959729825288,
67
+ }),
68
+ new AeroflyMissionCheckpoint("24", "destination_runway", -70.60730234370952, 41.399093035543366, {
69
+ altitude: 20,
70
+ length: 1677.6191463161874,
71
+ frequency: 108700000,
72
+ }),
73
+ new AeroflyMissionCheckpoint("KMVY", "destination", -70.6139, 41.3934, {
74
+ altitude: 20,
75
+ flyOver: false,
76
+ }),
77
+ ];
78
+ const mission = new AeroflyMission("KCCR #1: Concord / Buchanan Field", {
79
+ checkpoints,
80
+ conditions,
81
+ description: `It is a gusty, clear early morning, and you are 8 NM to the north of the towered airport Concord / Buchanan Field (27ft). As the wind is 11 kts from 190°, the main landing runway is 19L (191° / 844m). Fly the pattern and land safely.
82
+
83
+ - Local tower / CTAF frequency: 123.90
84
+ - Local navigational aids: VOR/DME CCR (117.00) 3.4 NM to the north`,
85
+ flightSetting: "cruise",
86
+ aircraft: {
87
+ name: "c172",
88
+ livery: "default",
89
+ icao: "C172",
90
+ },
91
+ callsign: "N51911",
92
+ origin: {
93
+ icao: "KCCR",
94
+ longitude: -122.0736009331662,
95
+ latitude: 38.122300745843944,
96
+ dir: 174.37511511143452,
97
+ alt: 1066.799965862401,
98
+ },
99
+ destination: {
100
+ icao: "KMVY",
101
+ longitude: -70.6139,
102
+ latitude: 41.3934,
103
+ dir: 221,
104
+ alt: 20,
105
+ },
106
+ });
107
+ const missionList = new AeroflyMissionsList([mission]);
108
+ assert.strictEqual(missionList.missions.length, 1);
109
+ assert.strictEqual(missionList.missions[0].aircraft.name, "c172");
110
+ assert.strictEqual(missionList.missions[0].aircraft.icao, "C172");
111
+ let missionListString = missionList.toString();
112
+ assert.strictEqual(missionListString, missionList.toString());
113
+ assertIncludes(missionListString, "[origin]");
114
+ assertIncludes(missionListString, "[tmmission_definition]");
115
+ assertIncludes(missionListString, "[list_tmmission_checkpoint]");
116
+ assertIncludes(missionListString, "[tmmission_checkpoint]");
117
+ assertIncludes(missionListString, "[departure_runway]");
118
+ assertIncludes(missionListString, "[cloud_cover]");
119
+ assertIncludes(missionListString, "[cirrus_cover]");
120
+ assertIncludes(missionListString, "[tmmission_checkpoint][element][0]");
121
+ assertIncludes(missionListString, "[tmmission_checkpoint][element][1]");
122
+ assertIncludes(missionListString, "[tmmission_checkpoint][element][2]");
123
+ assertIncludes(missionListString, "[tmmission_checkpoint][element][3]");
124
+ assertIncludes(missionListString, "<[float64][length][844.2959729825288]>");
125
+ assert.ok(!missionListString.includes("[tags]"));
126
+ assert.ok(!missionListString.includes("[difficulty]"));
127
+ assert.ok(!missionListString.includes("[is_featured]"));
128
+ assert.ok(!missionListString.includes("[tmmission_definition_localized]"));
129
+ assert.ok(!missionListString.includes("[distance]"));
130
+ assert.ok(!missionListString.includes("[duration]"));
131
+ assert.ok(!missionListString.includes("[alt_cst]"));
132
+ assertValidAeroflyStructure(missionListString);
133
+ //console.log(missionListString);
134
+ mission.difficulty = 1.0;
135
+ mission.isFeatured = true;
136
+ mission.localizedTexts.push(new AeroflyLocalizedText("de", "Landeübung #1", "Probier die Landung"));
137
+ mission.distance = 1400;
138
+ mission.duration = 2 * 60 * 60;
139
+ mission.tags.push("approach");
140
+ mission.tags.push("pattern");
141
+ mission.finish = new AeroflyMissionTargetPlane(0, 1, 2);
142
+ mission.tutorialName = "c172";
143
+ mission.isScheduled = true;
144
+ missionListString = missionList.toString();
145
+ assertIncludes(missionListString, "[tags]");
146
+ assertIncludes(missionListString, "[difficulty]");
147
+ assertIncludes(missionListString, "[is_featured]");
148
+ assertIncludes(missionListString, "true");
149
+ assertIncludes(missionListString, "[tmmission_definition_localized]");
150
+ assertIncludes(missionListString, "Landeübung");
151
+ assertIncludes(missionListString, "[distance]");
152
+ assertIncludes(missionListString, "[duration]");
153
+ assertIncludes(missionListString, "[finish]");
154
+ assertIncludes(missionListString, "[tutorial_name]");
155
+ assertIncludes(missionListString, "[is_scheduled]");
156
+ assertValidAeroflyStructure(missionListString);
157
+ //console.dir(missionList.missions[0], { depth: null });
158
+ //console.log(missionListString);
159
+ //console.log(missionList.getElement().toXmlString());
160
+ const file = new AeroflyMissionsList([mission]);
161
+ const fileContent = file.toString();
162
+ fs.writeFileSync("docs/custom_missions_user.tmc", fileContent);
163
+ const xmlContent = file.toXmlString();
164
+ fs.writeFileSync("docs/custom_missions_user.xml", xmlContent);
165
+ });
166
+ });
@@ -15,7 +15,7 @@ export class AeroflyMissionCheckpoint {
15
15
  * @param {"origin"|"departure_runway"|"departure"|"waypoint"|"arrival"|"approach"|"destination_runway"|"destination"} type Type of checkpoint, like "departure_runway"
16
16
  * @param {number} longitude easting, using the World Geodetic
17
17
  * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
18
- * of decimal degrees; -180..180
18
+ * of decimal degrees; [-180,180]
19
19
  * @param {number} latitude northing, using the World Geodetic
20
20
  * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
21
21
  * of decimal degrees; -90..90
@@ -0,0 +1,41 @@
1
+ import { AeroflyMissionCheckpoint } from "./AeroflyMissionCheckpoint.js";
2
+ import { strict as assert } from "node:assert";
3
+ import { assertValidAeroflyStructure, assertIncludes } from "../check/TestHelpers.js";
4
+ import { describe, it } from "node:test";
5
+ describe("AeroflyMissionCheckpoint", () => {
6
+ it("should correctly map constructor values", () => {
7
+ const checkpoint = new AeroflyMissionCheckpoint("TEST", "waypoint", 15, 20, {
8
+ altitude_feet: 1000,
9
+ flyOver: true,
10
+ length_feet: 500,
11
+ });
12
+ assert.strictEqual(checkpoint.name, "TEST");
13
+ assert.strictEqual(checkpoint.type, "waypoint");
14
+ assert.strictEqual(checkpoint.longitude, 15);
15
+ assert.strictEqual(checkpoint.latitude, 20);
16
+ assert.strictEqual(checkpoint.flyOver, true);
17
+ assert.strictEqual(Math.round(checkpoint.altitude_feet), 1000);
18
+ assert.strictEqual(Math.round(checkpoint.altitude), Math.round(1000 * 0.3048));
19
+ assert.strictEqual(Math.round(checkpoint.length_feet), 500);
20
+ assert.strictEqual(Math.round(checkpoint.length ?? 0), Math.round(500 * 0.3048));
21
+ });
22
+ it("should correctly map values from properties", () => {
23
+ const checkpoint = new AeroflyMissionCheckpoint("TEST", "waypoint", 15, 20);
24
+ let checkpointString = checkpoint.toString();
25
+ assertIncludes(checkpointString, "[type]");
26
+ assertIncludes(checkpointString, "[name]");
27
+ assertIncludes(checkpointString, "[lon_lat]");
28
+ assertIncludes(checkpointString, "15 20");
29
+ assertIncludes(checkpointString, "[altitude]");
30
+ assert.ok(!checkpointString.includes("[fly_over]"));
31
+ assert.ok(!checkpointString.includes("[alt_cst]"));
32
+ checkpoint.altitudeConstraint = false;
33
+ checkpoint.flyOver = false;
34
+ checkpoint.altitude_feet = 1000;
35
+ checkpoint.length_feet = 500;
36
+ checkpointString = checkpoint.toString();
37
+ assertIncludes(checkpointString, "[fly_over]");
38
+ assertIncludes(checkpointString, "[alt_cst]");
39
+ assertValidAeroflyStructure(checkpointString);
40
+ });
41
+ });
@@ -15,8 +15,8 @@ export class AeroflyMissionConditions {
15
15
  * @param {Date} [additionalAttributes.time] of flight plan. Relevant is the UTC part, so
16
16
  * consider setting this date in UTC.
17
17
  * @param {{direction: number, speed: number, gusts: number}} [additionalAttributes.wind] state
18
- * @param {number} [additionalAttributes.turbulenceStrength] 0..1, percentage
19
- * @param {number} [additionalAttributes.thermalStrength] 0..1, percentage
18
+ * @param {number} [additionalAttributes.turbulenceStrength] normalized value [0,1]
19
+ * @param {number} [additionalAttributes.thermalStrength] normalized value [0,1]
20
20
  * @param {number} [additionalAttributes.visibility] in meters
21
21
  * @param {?number} [additionalAttributes.visibility_sm] in statute miles, will overwrite visibility
22
22
  * @param {?number} [additionalAttributes.temperature] in °C, will overwrite thermalStrength
@@ -26,7 +26,7 @@ export class AeroflyMissionConditions {
26
26
  direction: 0,
27
27
  speed: 0,
28
28
  gusts: 0,
29
- }, turbulenceStrength = 0, thermalStrength = 0, visibility = 25000, visibility_sm = 0, temperature = 0, clouds = [], } = {}) {
29
+ }, turbulenceStrength = 0, thermalStrength = 0, visibility = 25_000, visibility_sm = 0, temperature = 0, clouds = [], } = {}) {
30
30
  /**
31
31
  * @property {AeroflyMissionConditionsCloud[]} clouds for the whole flight
32
32
  */
@@ -0,0 +1,33 @@
1
+ import { AeroflyMissionConditions } from "./AeroflyMissionConditions.js";
2
+ import { strict as assert } from "node:assert";
3
+ import { assertValidAeroflyStructure } from "../check/TestHelpers.js";
4
+ import { describe, it } from "node:test";
5
+ describe("AeroflyMissionConditions", () => {
6
+ it("should handle visibility in meters correctly when set via property", () => {
7
+ const conditions = new AeroflyMissionConditions();
8
+ conditions.visibility = 15_000;
9
+ assert.deepStrictEqual(conditions.visibility, 15_000);
10
+ });
11
+ it("should handle visibility in statute miles correctly when set via property", () => {
12
+ const conditions = new AeroflyMissionConditions();
13
+ conditions.visibility_sm = 10;
14
+ assert.notDeepStrictEqual(conditions.visibility, 10);
15
+ assert.deepStrictEqual(Math.round(conditions.visibility_sm), 10);
16
+ assertValidAeroflyStructure(conditions.toString());
17
+ });
18
+ it("should handle visibility in meters correctly when set via constructor", () => {
19
+ const conditions = new AeroflyMissionConditions({
20
+ visibility: 15_000,
21
+ });
22
+ assert.deepStrictEqual(conditions.visibility, 15_000);
23
+ assertValidAeroflyStructure(conditions.toString());
24
+ });
25
+ it("should handle visibility in statute miles correctly when set via constructor", () => {
26
+ const conditions = new AeroflyMissionConditions({
27
+ visibility_sm: 10,
28
+ });
29
+ assert.notDeepStrictEqual(conditions.visibility, 10);
30
+ assert.deepStrictEqual(Math.round(conditions.visibility_sm), 10);
31
+ assertValidAeroflyStructure(conditions.toString());
32
+ });
33
+ });
@@ -10,7 +10,7 @@ import { feetPerMeter } from "./AeroflyMission.js";
10
10
  */
11
11
  export class AeroflyMissionConditionsCloud {
12
12
  /**
13
- * @param {number} cover 0..1, percentage
13
+ * @param {number} cover normalized value [0,1]
14
14
  * @param {number} base altitude in meters AGL
15
15
  */
16
16
  constructor(cover, base) {
@@ -18,7 +18,7 @@ export class AeroflyMissionConditionsCloud {
18
18
  this.base = base;
19
19
  }
20
20
  /**
21
- * @param {number} cover 0..1, percentage
21
+ * @param {number} cover normalized value [0,1]
22
22
  * @param {number} base_feet altitude, but in feet AGL instead of meters AGL
23
23
  * @returns {AeroflyMissionConditionsCloud} self
24
24
  */
@@ -0,0 +1,25 @@
1
+ import { AeroflyMissionConditionsCloud } from "./AeroflyMissionConditionsCloud.js";
2
+ import { strict as assert } from "node:assert";
3
+ import { assertValidAeroflyStructure } from "../check/TestHelpers.js";
4
+ import { describe, it } from "node:test";
5
+ describe("AeroflyMissionConditionsCloud", () => {
6
+ it("should create clouds with indentation", () => {
7
+ const cloud = new AeroflyMissionConditionsCloud(0, 0);
8
+ assert.deepStrictEqual(cloud.cover, 0);
9
+ assert.deepStrictEqual(cloud.base, 0);
10
+ assertValidAeroflyStructure(cloud.toString());
11
+ assertValidAeroflyStructure(cloud.toString(0));
12
+ assertValidAeroflyStructure(cloud.toString(1));
13
+ assertValidAeroflyStructure(cloud.toString(2));
14
+ assertValidAeroflyStructure(cloud.toString(3));
15
+ });
16
+ it("should assign values correctly from constructor and properties", () => {
17
+ const cloud = new AeroflyMissionConditionsCloud(1, 1000);
18
+ assert.deepStrictEqual(cloud.cover, 1);
19
+ assert.deepStrictEqual(cloud.base, 1000);
20
+ cloud.base_feet = 1000;
21
+ assert.notDeepStrictEqual(cloud.base, 1000);
22
+ assert.deepStrictEqual(Math.round(cloud.base_feet), 1000);
23
+ assertValidAeroflyStructure(cloud.toString());
24
+ });
25
+ });
@@ -8,7 +8,7 @@ export class AeroflyMissionTargetPlane {
8
8
  *
9
9
  * @param {number} longitude easting, using the World Geodetic
10
10
  * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
11
- * of decimal degrees; -180..180
11
+ * of decimal degrees; [-180,180]
12
12
  * @param {number}latitude northing, using the World Geodetic
13
13
  * System 1984 (WGS 84) [WGS84] datum, with longitude and latitude units
14
14
  * of decimal degrees; -90..90
@@ -0,0 +1,14 @@
1
+ import { AeroflyMissionTargetPlane } from "../index.js";
2
+ import { assertValidAeroflyStructure } from "../check/TestHelpers.js";
3
+ import { strict as assert } from "node:assert";
4
+ import { describe, it } from "node:test";
5
+ describe("Lots of tests for AeroflyMission and related classes", () => {
6
+ it("should create AeroflyMissionTargetPlane correctly", () => {
7
+ const targetPlane = new AeroflyMissionTargetPlane(0, 1, 2, "Test2");
8
+ assert.deepStrictEqual(targetPlane.longitude, 0);
9
+ assert.deepStrictEqual(targetPlane.latitude, 1);
10
+ assert.deepStrictEqual(targetPlane.dir, 2);
11
+ assert.deepStrictEqual(targetPlane.name, "Test2");
12
+ assertValidAeroflyStructure(targetPlane.toString());
13
+ });
14
+ });
@@ -0,0 +1,16 @@
1
+ import { AeroflyMissionsList } from "./AeroflyMissionsList.js";
2
+ import { strict as assert } from "node:assert";
3
+ import { assertValidAeroflyStructure } from "../check/TestHelpers.js";
4
+ import { describe, it } from "node:test";
5
+ describe("AeroflyMissionsList", () => {
6
+ it("should create valid Aerofly code", () => {
7
+ const missionList = new AeroflyMissionsList();
8
+ assert.deepStrictEqual(missionList.missions.length, 0);
9
+ assertValidAeroflyStructure(missionList.toString());
10
+ });
11
+ it("should create valid XML code", () => {
12
+ const missionList = new AeroflyMissionsList();
13
+ assert.deepStrictEqual(missionList.missions.length, 0);
14
+ assertValidAeroflyStructure(missionList.toXmlString());
15
+ });
16
+ });