@fboes/aerofly-custom-missions 1.3.1 → 1.6.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 +24 -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 +100 -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 +187 -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 +107 -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,38 @@
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.5.0]
6
+
7
+ - Added clouds elements getter
8
+ - Fixed spelling error for `configuration`, which changes the API
9
+
10
+ ## [1.4.0]
11
+
12
+ - Changed testing to standard Node.js testing suite
13
+ - Added `fuel_mass` and `payload_mass` properties
14
+ - Added `flight.mcf` / `main.mcf` DTO
15
+
16
+ ## [1.3.1]
6
17
 
7
18
  - Re-introduced spacers between exported missions
8
19
  - Re-introduced commented out nodes for exported missions
9
20
 
10
- ## 1.3.0
21
+ ## [1.3.0]
11
22
 
12
23
  - Changed TMC code generation with nodes
13
24
 
14
- ## 1.2.3
25
+ ## [1.2.3]
15
26
 
16
27
  - Internal restructuring of mission generation logic
17
28
  - Optimized waypoint handling and validation
18
29
  - Improved error handling for mission parsing
19
30
 
20
- ## 1.2.2
31
+ ## [1.2.2]
21
32
 
22
33
  - Added altitude constraint property for waypoints
23
34
  - Improved handling of cloud layers with better validation
24
35
 
25
- ## 1.2.1
36
+ ## [1.2.1]
26
37
 
27
38
  - Changed handling of checkpoints to support missions without checkpoints
28
39
  - Improved file generation for programmatic mission creation
@@ -36,7 +47,7 @@ This changelog documents all notable changes to the Aerofly Custom Missions proj
36
47
  - `before_start`
37
48
  - `pushback`
38
49
 
39
- ## 1.2.0
50
+ ## [1.2.0]
40
51
 
41
52
  - Added new cloud level properties:
42
53
  - `cirrus_cover` for high-altitude cloud coverage
@@ -44,13 +55,13 @@ This changelog documents all notable changes to the Aerofly Custom Missions proj
44
55
  - Added new waypoint property `fly_over` for precise waypoint navigation
45
56
  - Added `finish` property to mark mission completion points
46
57
 
47
- ## 1.1.1
58
+ ## [1.1.1]
48
59
 
49
60
  - Fixed styling issues in mission display
50
61
  - Improved UI consistency across different mission types
51
62
  - Enhanced error message formatting
52
63
 
53
- ## 1.1.0
64
+ ## [1.1.0]
54
65
 
55
66
  - Added new mission metadata properties:
56
67
  - `tags` for mission categorization
@@ -64,31 +75,31 @@ This changelog documents all notable changes to the Aerofly Custom Missions proj
64
75
  - `aerotow` for towed aircraft operations
65
76
  - Improved temperature property with better unit handling
66
77
 
67
- ## 1.0.4
78
+ ## [1.0.4]
68
79
 
69
80
  - Added comprehensive documentation for known issues and workarounds
70
81
  - Added `AeroflyMissionConditions.temperature` property with Celsius support
71
82
  - Improved error handling for weather conditions
72
83
 
73
- ## 1.0.3
84
+ ## [1.0.3]
74
85
 
75
86
  - Enhanced API documentation with examples
76
87
  - Added detailed parameter descriptions
77
88
  - Improved code documentation
78
89
 
79
- ## 1.0.2
90
+ ## [1.0.2]
80
91
 
81
92
  - Added shorthand properties for common mission parameters
82
93
  - Improved property access methods
83
94
  - Enhanced mission validation
84
95
 
85
- ## 1.0.1
96
+ ## [1.0.1]
86
97
 
87
98
  - Added initial API documentation
88
99
  - Improved code comments
89
100
  - Added basic usage examples
90
101
 
91
- ## 1.0.0
102
+ ## [1.0.0]
92
103
 
93
104
  - Initial release of Aerofly Custom Missions
94
105
  - 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
+ });