Skip to content

Commit f71402e

Browse files
flogross89BenjozorkBravoMike99
authored
feat(a380x/fms): Secondary flight plans for the A380X (#10429)
* feat: initial commit * fix(fms): some sync fixes * fix(a380/fmc): FMC initialization fixes * feat: request route from avionics wip * fix: rework syncing of segments to sync procedure changes * fix: syncing of runways to msfs * refactor: cleanup * fix: syncing of origin/destination airport/runways * fix: unused import * fix: lint * fix: initialization and race condition fixes * chore: remove debug logs * refactor: draft of refactored airway entry * fix: more sync fixes, batches * refactor: more airways work * refactor: more cleanup, move batch tests * refactor: more airways work, add integration tests * test: add simple rpc client unit tests, remove old integration tests * test: re-enable leg test * feat: route airways * chore: fix copyright * fix: runway conversion and changed property name * feat: enroute legs to EFB * feat: add sync support to DataManager * feat: sync pilot waypoints to sim * feat: import pilot waypoints, send cruise alt * fix(a32nx): don't instantiate FlightPlanAsoboSync in MSFS2024 * fix(a32nx): get 2024 sync running * chore: that's a bit excessive * feat: add auto-import setting to EFB * fix: import * fix: types * fix: simbrief and coroute adapters * chore: lint * fix: test runway designator * fix: mcdu airways * chore: address review comments * chore: address review comments 2 * fix: fix leaked subscription, better property name * chore: remove unused test code * chore: formatting * fix: use new RunwayUtils method * refactor: use better sync client ID RNG * fix: a320 airways * fix: type annotation * fix: after merge fixes * fix: various fixes * refactor: host route sync on FMC * test: fix tests * fix: issues after merge and types * fix: strict error * chore: lint * fix: address review comments * fix enginesWereStarted condition * switch to readonly flight plans * prepare for migration to A380FlightPlanPerformanceData * batch 1 * batch 2 * add SEC INDEX initial * bugfixes * batch 3 * fix: post merge fixes * fix: post merge fixes #2 * some more additions * batch 4 * fix tests, imports * found some more * more strictness * fix #234346 * add another mock implementation * fix strict mode * flight plan creation source * BM's review comments (cherry picked from commit 0439b4a) * WAITING FOR INSERTION IN XXXX message; SEC FPLN source whitespace fix; move CPNY FPLN button code to util * changelog * SEC tab navigation from url * some cpny fpln button changes * merge fixes * holland's comments * try to fix case * try to fix case #2 * draw waypoints in white for SEC FPLN on ND in A380X * extend SEC on ND to three SEC FPLN for A380X * some fixes from QA * remove debug msg & fix console errors * that didn't work * introduce Dummy FMCs, enable flight plan sync on non-master again. * fix INSERT NEXT WPT on first element; fix step climbs for non-active FPLN * fix KCCU events not respecting side; add KCCU input to ND/OANS * add JSdoc * make master always available, and point to the one real FMC * add FlightPlanInterface to props * hook up keyboard to OIT * fix taxi fuel, extra fuel, LW estimation * fix: use pd presel speeds & approach flap setting * fix: make callsign subject. Fix LW & TOW calcs * fix: tropo, crz & taxi fuel errors * fix: mfd perf speeds & taxi fuel defaults * fix: tropo read value should not be pilot entry * chore: sync large-files * fix: wrong stash commit. Fuel and loud route reserve * chore: use common method for RTE RSV * fix: invalidate fuel pred in SEC * fix: disable more preds in MFD PERF * fix: presel speed display. Hide PRED TO in SEC * fix: RTE RSV percentage calc * fix: final time fuel calcs * fix: sec exceptions. flight number not being set for fmgc * fix: change condition of pilot entry reserves * fix: disable some fields if copy of active * fix: colours in fuel & load SEC * fix: only show dest efob amber in primary. Make perf calc speeds white * fix: add alternate & fin res fuel to performance data * fix: change some colors in hold and vert rev * fix: cleanup some classes * fix: merge conflicts * chore: fix strict errors * fix: disable fix info in sec * fix: fpln page not showing dist of SEC * fix: rte rsv pilot entry not visible in SEC * fix: cleanup some more PD. Bring simplified res fuel calcs to SEC too * fix: navigate to active if SEC deleted. Only create SEC on page nav * fix: handle case of no FP in INIT if deleted on other MFD * fix: loaded sec index not being set if does not exist * fix: fqms conflicts * chore: simply getfob * chore: bbk comments part 1 * feat: change sec created to epoch * fix: flags only being set after rpc. ground temperature undefined error * fix: retain flags on sec copy to sec copy. Pipe cost index mode too * chore: fix 32nx build error due to pd gnd temp * fix: convert fqms fob to tons * fix: extra fuel unit error * fix: a32nx cruise temperature error * fix: only set flex if active flightplan * chore: remove non applicable document * fix: retain 0 deg flex setting * fix: fadec not being overriten * fix: selected not updating on & wrong speeds in perf * fix: make vapp consistent with vls etc * fix: vapp not being used by systems --------- Co-authored-by: Benjamin Dupont <4503241+Benjozork@users.noreply.github.com> Co-authored-by: BravoMike99 <119708186+BravoMike99@users.noreply.github.com>
1 parent 28b8b18 commit f71402e

110 files changed

Lines changed: 7533 additions & 3517 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@
100100
1. [A380X/MFD] Fix CPNY F-PLN REQUEST button disabled when airport ICAO not found in failed Simbrief import - @heclak (Heclak)
101101
1. [A380X/RMP] Fix FO RMP integrated lights not working - @heclak (Heclak)
102102
1. [A32NX/FWS] Add `GEAR NOT DOWNLOCKED` master warning - @FozzieHi (fozzie)
103+
1. [A380X/FMS] Add secondary flight plans to A380X - @flogross89 (floridude), @BravoMike99 (bruno_pt99)
104+
1. [A380X/FMS] Allow edition of vnav descent speed - @BravoMike99 (bruno_pt99)
103105

104106
## 0.14.0
105107

fbw-a32nx/src/systems/extras-host/modules/flightplan_sync/FlightPlanAsoboSync.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { ApproachType as MSApproachType } from '../../../../../../fbw-common/src
2929
import { FacilityCache } from '../../../../../../fbw-common/src/systems/navdata/client/backends/Msfs/FacilityCache';
3030
import { NavigationDatabaseService } from '@fmgc/flightplanning/NavigationDatabaseService';
3131
import { NavigationDatabase, NavigationDatabaseBackend } from '@fmgc/NavigationDatabase';
32-
import { A320FlightPlanPerformanceData } from '@fmgc/flightplanning/plans/performance/FlightPlanPerformanceData';
32+
import { A320FlightPlanPerformanceData } from '@fmgc/flightplanning/plans/performance/A320FlightPlanPerformanceData';
3333
import { FlightPlanIndex } from '@fmgc/flightplanning/FlightPlanManager';
3434

3535
export class FlightPlanAsoboSync {

fbw-a32nx/src/systems/fmgc/src/efis/EfisInterface.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ export class EfisInterface {
2424
static readonly A32NX_NUM_LEGS_ON_FPLN_PAGE = 5;
2525

2626
constructor(
27-
private side: EfisSide,
28-
private flightPlanService: FlightPlanService,
27+
private readonly side: EfisSide,
28+
private readonly flightPlanService: FlightPlanService,
2929
private numLegOnFplnPage = EfisInterface.A32NX_NUM_LEGS_ON_FPLN_PAGE,
30+
public readonly numSecondaryFlightPlans: number = 1,
3031
) {}
3132

32-
private isSecRelatedPageOpen: boolean = false;
33+
/** Index of secondary flight plan for which related page is open. Null if no related page for any secondary flight plan is open. */
34+
private secRelatedPageOpenIndex: number | null = null;
3335

3436
readonly planCentre: PlanCentre = {
3537
fpIndex: 0,
@@ -59,9 +61,9 @@ export class EfisInterface {
5961
this.version++;
6062
}
6163

62-
setSecRelatedPageOpen(open: boolean): void {
63-
if (this.isSecRelatedPageOpen !== open) {
64-
this.isSecRelatedPageOpen = open;
64+
setSecRelatedPageOpen(secIndex: number | null): void {
65+
if (this.secRelatedPageOpenIndex !== secIndex) {
66+
this.secRelatedPageOpenIndex = secIndex;
6567
this.version++;
6668
}
6769
}
@@ -74,8 +76,12 @@ export class EfisInterface {
7476
// Don't increment version here, the real one takes a bit of time to update as well
7577
}
7678

77-
shouldTransmitSecondary(): boolean {
78-
return this.isSecRelatedPageOpen;
79+
shouldTransmitSecondary(secondaryIndex: number = 1): boolean {
80+
return this.secRelatedPageOpenIndex === secondaryIndex;
81+
}
82+
83+
shouldTransmitAnySecondary(): boolean {
84+
return this.secRelatedPageOpenIndex !== null;
7985
}
8086

8187
shouldTransmitMissed(planIndex: FlightPlanIndex, isPlanMode: boolean): boolean {

fbw-a32nx/src/systems/fmgc/src/efis/EfisSymbols.ts

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -210,19 +210,28 @@ export class EfisSymbols<T extends number> {
210210
const tempFpVersion = this.flightPlanService.has(FlightPlanIndex.Temporary)
211211
? this.flightPlanService.temporary.version
212212
: -1;
213-
const secFpVersion = this.flightPlanService.has(FlightPlanIndex.FirstSecondary)
214-
? this.flightPlanService.secondary(1).version
215-
: -1;
216213

217214
const activeFPVersionChanged = this.lastFpVersions[FlightPlanIndex.Active] !== activeFpVersion;
218215
const tempFPVersionChanged = this.lastFpVersions[FlightPlanIndex.Temporary] !== tempFpVersion;
219-
const secFPVersionChanged = this.lastFpVersions[FlightPlanIndex.FirstSecondary] !== secFpVersion;
216+
let secFPVersionChanged = false;
217+
for (let i = 0; i < this.efisInterface.numSecondaryFlightPlans; i++) {
218+
const secVersion = this.flightPlanService.has(FlightPlanIndex.FirstSecondary + i)
219+
? this.flightPlanService.secondary(i + 1).version
220+
: -1;
221+
secFPVersionChanged ||= this.lastFpVersions[FlightPlanIndex.FirstSecondary + i] !== secVersion;
222+
}
220223

221224
const fpChanged = activeFPVersionChanged || tempFPVersionChanged || secFPVersionChanged;
222225

223226
this.lastFpVersions[FlightPlanIndex.Active] = activeFpVersion;
224227
this.lastFpVersions[FlightPlanIndex.Temporary] = tempFpVersion;
225-
this.lastFpVersions[FlightPlanIndex.FirstSecondary] = secFpVersion;
228+
for (let i = 0; i < this.efisInterface.numSecondaryFlightPlans; i++) {
229+
this.lastFpVersions[FlightPlanIndex.FirstSecondary + i] = this.flightPlanService.has(
230+
FlightPlanIndex.FirstSecondary + i,
231+
)
232+
? this.flightPlanService.secondary(i + 1).version
233+
: -1;
234+
}
226235

227236
const navaidsChanged = this.lastNavaidVersion !== this.navaidTuner.navaidVersion;
228237
this.lastNavaidVersion = this.navaidTuner.navaidVersion;
@@ -504,40 +513,16 @@ export class EfisSymbols<T extends number> {
504513
}
505514

506515
// SEC
507-
if (
508-
this.flightPlanService.hasSecondary(1) &&
509-
this.guidanceController.hasGeometryForFlightPlan(FlightPlanIndex.FirstSecondary) &&
510-
this.efisInterface.shouldTransmitSecondary()
511-
) {
512-
const symbols = this.getFlightPlanSymbols(
513-
false,
514-
this.flightPlanService.secondary(1),
515-
this.guidanceController.secondaryGeometry,
516-
range,
517-
efisOption,
518-
mode,
519-
this.side,
520-
mapReferencePoint,
521-
mapOrientation,
522-
editArea,
523-
formatConstraintAlt,
524-
formatConstraintSpeed,
525-
);
526-
527-
for (const symbol of symbols) {
528-
upsertSymbol(symbol);
529-
}
530-
531-
// SEC ALTN
516+
for (let secIndex = 1; secIndex <= this.efisInterface.numSecondaryFlightPlans; secIndex++) {
532517
if (
533-
this.flightPlanService.secondary(1).alternateFlightPlan.legCount > 0 &&
534-
this.guidanceController.hasGeometryForFlightPlan(FlightPlanIndex.FirstSecondary) &&
535-
this.efisInterface.shouldTransmitAlternate(FlightPlanIndex.FirstSecondary, mode === EfisNdMode.PLAN)
518+
this.flightPlanService.hasSecondary(secIndex) &&
519+
this.guidanceController.hasGeometryForFlightPlan(FlightPlanIndex.FirstSecondary + secIndex - 1) &&
520+
this.efisInterface.shouldTransmitSecondary(secIndex)
536521
) {
537522
const symbols = this.getFlightPlanSymbols(
538-
true,
539-
this.flightPlanService.secondary(1).alternateFlightPlan,
540-
this.guidanceController.getGeometryForFlightPlan(FlightPlanIndex.FirstSecondary, true),
523+
false,
524+
this.flightPlanService.secondary(secIndex),
525+
this.guidanceController.secondaryGeometry(secIndex),
541526
range,
542527
efisOption,
543528
mode,
@@ -552,6 +537,35 @@ export class EfisSymbols<T extends number> {
552537
for (const symbol of symbols) {
553538
upsertSymbol(symbol);
554539
}
540+
541+
// SEC ALTN
542+
if (
543+
this.flightPlanService.secondary(secIndex).alternateFlightPlan.legCount > 0 &&
544+
this.guidanceController.hasGeometryForFlightPlan(FlightPlanIndex.FirstSecondary + secIndex - 1) &&
545+
this.efisInterface.shouldTransmitAlternate(
546+
FlightPlanIndex.FirstSecondary + secIndex - 1,
547+
mode === EfisNdMode.PLAN,
548+
)
549+
) {
550+
const symbols = this.getFlightPlanSymbols(
551+
true,
552+
this.flightPlanService.secondary(secIndex).alternateFlightPlan,
553+
this.guidanceController.getGeometryForFlightPlan(FlightPlanIndex.FirstSecondary + secIndex - 1, true),
554+
range,
555+
efisOption,
556+
mode,
557+
this.side,
558+
mapReferencePoint,
559+
mapOrientation,
560+
editArea,
561+
formatConstraintAlt,
562+
formatConstraintSpeed,
563+
);
564+
565+
for (const symbol of symbols) {
566+
upsertSymbol(symbol);
567+
}
568+
}
555569
}
556570
}
557571

@@ -753,6 +767,8 @@ export class EfisSymbols<T extends number> {
753767
type |= NdSymbolTypeFlags.CourseReversalRight;
754768
}
755769
direction = MagVar.getLegTrueCourse(leg.definition);
770+
} else if (flightPlan.index >= FlightPlanIndex.FirstSecondary) {
771+
type |= NdSymbolTypeFlags.SecondaryFlightPlan;
756772
}
757773

758774
if (i >= flightPlan.firstMissedApproachLegIndex && !transmitMissed) {

fbw-a32nx/src/systems/fmgc/src/efis/EfisVectors.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ export class EfisVectors {
7676

7777
this.tryProcessFlightPlan(FlightPlanIndex.Active, side, true);
7878
this.tryProcessFlightPlan(FlightPlanIndex.Temporary, side, true);
79-
this.tryProcessFlightPlan(FlightPlanIndex.FirstSecondary, side, true);
79+
80+
for (let i = 1; i <= this.efisInterfaces[side].numSecondaryFlightPlans; i++) {
81+
this.tryProcessFlightPlan(FlightPlanIndex.FirstSecondary + i - 1, side, true);
82+
}
8083

8184
const activeFlightPlanVectors =
8285
this.guidanceController.activeGeometry?.getAllPathVectors(this.guidanceController.activeLegIndex) ?? [];
@@ -93,7 +96,10 @@ export class EfisVectors {
9396
} else {
9497
this.tryProcessFlightPlan(FlightPlanIndex.Active, side);
9598
this.tryProcessFlightPlan(FlightPlanIndex.Temporary, side);
96-
this.tryProcessFlightPlan(FlightPlanIndex.FirstSecondary, side);
99+
100+
for (let i = 1; i <= this.efisInterfaces[side].numSecondaryFlightPlans; i++) {
101+
this.tryProcessFlightPlan(FlightPlanIndex.FirstSecondary + i - 1, side);
102+
}
97103
}
98104
}
99105

@@ -129,7 +135,9 @@ export class EfisVectors {
129135
case FlightPlanIndex.FirstSecondary:
130136
case FlightPlanIndex.Uplink:
131137
default:
132-
this.transmit(null, EfisVectorsGroup.SECONDARY, side);
138+
if (!this.efisInterfaces[side].shouldTransmitAnySecondary()) {
139+
this.transmit(null, EfisVectorsGroup.SECONDARY, side);
140+
}
133141
break;
134142
}
135143

@@ -194,9 +202,9 @@ export class EfisVectors {
194202
this.transmitFlightPlan(plan, side, EfisVectorsGroup.TEMPORARY);
195203
break;
196204
default:
197-
if (this.efisInterfaces[side].shouldTransmitSecondary()) {
205+
if (this.efisInterfaces[side].shouldTransmitSecondary(planIndex - FlightPlanIndex.FirstSecondary + 1)) {
198206
this.transmitFlightPlan(plan, side, EfisVectorsGroup.SECONDARY);
199-
} else {
207+
} else if (!this.efisInterfaces[side].shouldTransmitAnySecondary()) {
200208
this.transmit(null, EfisVectorsGroup.SECONDARY, side);
201209
}
202210
break;

fbw-a32nx/src/systems/fmgc/src/flightplanning/A320AircraftConfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
VnavDescentMode,
1313
} from '@fmgc/flightplanning/AircraftConfigTypes';
1414
import { FlapConf } from '@fmgc/guidance/vnav/common';
15+
import { FpmConfigs } from './FpmConfig';
1516

1617
const lnavConfig: LnavConfig = {
1718
DEFAULT_MIN_PREDICTED_TAS: 160,
@@ -151,4 +152,5 @@ export const A320AircraftConfig: AircraftConfig = {
151152
engineModelParameters: engineModelParams,
152153
flightModelParameters: flightModelParams,
153154
fmSymbolConfig: fmsSymbolConfig,
155+
fpmConfig: FpmConfigs.A320_HONEYWELL_H3,
154156
};

fbw-a32nx/src/systems/fmgc/src/flightplanning/A380AircraftConfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
VnavDescentMode,
1313
} from '@fmgc/flightplanning/AircraftConfigTypes';
1414
import { FlapConf } from '@fmgc/guidance/vnav/common';
15+
import { FpmConfigs } from './FpmConfig';
1516

1617
const lnavConfig: LnavConfig = {
1718
DEFAULT_MIN_PREDICTED_TAS: 160,
@@ -178,4 +179,5 @@ export const A380AircraftConfig: AircraftConfig = {
178179
engineModelParameters: engineModelParams,
179180
flightModelParameters: flightModelParams,
180181
fmSymbolConfig: fmsSymbolConfig,
182+
fpmConfig: FpmConfigs.A380,
181183
};

fbw-a32nx/src/systems/fmgc/src/flightplanning/AircraftConfigTypes.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
// Copyright (c) 2021-2022 FlyByWire Simulations
1+
// Copyright (c) 2021-2026 FlyByWire Simulations
22
//
33
// SPDX-License-Identifier: GPL-3.0
44

55
import { FlapConf } from '@fmgc/guidance/vnav/common';
6+
import { FpmConfig } from './FpmConfig';
67

78
export enum VnavDescentMode {
89
NORMAL,
@@ -16,6 +17,7 @@ export interface AircraftConfig {
1617
engineModelParameters: EngineModelParameters;
1718
flightModelParameters: FlightModelParameters;
1819
fmSymbolConfig: FMSymbolsConfig;
20+
fpmConfig: FpmConfig;
1921
}
2022

2123
export interface VnavConfig {

fbw-a32nx/src/systems/fmgc/src/flightplanning/FlightPlanInterface.integration.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { testFlightPlanService } from './test/TestFlightPlanService';
44
import { NavigationDatabaseService } from './NavigationDatabaseService';
55
import { FlightPlanIndex } from './FlightPlanManager';
66
import { FlightPlanRpcClient } from '@fmgc/flightplanning/rpc/FlightPlanRpcClient';
7-
import { A320FlightPlanPerformanceData } from '@fmgc/flightplanning/plans/performance/FlightPlanPerformanceData';
7+
import { A320FlightPlanPerformanceData } from '@fmgc/flightplanning/plans/performance/A320FlightPlanPerformanceData';
88
import { FlightPlanRpcServer } from '@fmgc/flightplanning/rpc/FlightPlanRpcServer';
99
import { setupTestDatabase } from '@fmgc/flightplanning/test/Database';
1010
import { testEventBus } from '@fmgc/flightplanning/test/TestEventBus';

fbw-a32nx/src/systems/fmgc/src/flightplanning/FlightPlanInterface.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ export interface FlightPlanInterface<P extends FlightPlanPerformanceData = Fligh
5959
*/
6060
secondaryCopyFromActive(index: number, isBeforeEngineStart: boolean): Promise<void>;
6161

62+
/**
63+
* Copies the secondary flight plan into another secondary flight plan
64+
*
65+
* @param from the 1-indexed index of the source secondary flight plan
66+
* @param to the 1-indexed index of the destination secondary flight plan
67+
*/
68+
secondaryCopyFromSecondary(from: number, to: number, isBeforeEngineStart: boolean): Promise<void>;
69+
6270
secondaryDelete(index: number): Promise<void>;
6371

6472
secondaryReset(index: number): Promise<void>;
@@ -290,7 +298,7 @@ export interface FlightPlanInterface<P extends FlightPlanPerformanceData = Fligh
290298
addOrEditManualHold(
291299
atIndex: number,
292300
desiredHold: HoldData,
293-
modifiedHold: HoldData,
301+
modifiedHold: HoldData | undefined,
294302
defaultHold: HoldData,
295303
planIndex: number,
296304
alternate?: boolean,
@@ -334,6 +342,25 @@ export interface FlightPlanInterface<P extends FlightPlanPerformanceData = Fligh
334342
alternate?: boolean,
335343
): Promise<void>;
336344

345+
/**
346+
* Sets whether a fix at an index should be overflown or not.
347+
*
348+
* @param atIndex the index of the leg to start the pending airway entry at
349+
* @param overfly Whether fix should be overflown or not.
350+
* @param planIndex which flight plan to make the change on
351+
* @param alternate whether to edit the plan's alternate flight plan
352+
*/
353+
setOverfly(atIndex: number, overfly: boolean, planIndex: number, alternate: boolean): Promise<void>;
354+
355+
/**
356+
* Toggle whether a fix at an index should be overflown or not.
357+
*
358+
* @param atIndex the index of the leg to start the pending airway entry at
359+
* @param planIndex which flight plan to make the change on
360+
* @param alternate whether to edit the plan's alternate flight plan
361+
*/
362+
toggleOverfly(atIndex: number, planIndex: number, alternate: boolean): Promise<void>;
363+
337364
setFixInfoEntry(index: 1 | 2 | 3 | 4, fixInfo: FixInfoEntry | null, planIndex: number): Promise<void>;
338365

339366
// TODO do not pass in callback (rpc)

0 commit comments

Comments
 (0)