Last leg types
This commit is contained in:
parent
c5cd3c7a0e
commit
2bdb7e78c4
17
README.md
17
README.md
@ -346,7 +346,7 @@ LFPV 27 PB2V SID (Cycle 2507, ID 10395)
|
||||
- `NavDist` is in **nmi**. `NavDist` is geodesic.
|
||||
|
||||
|
||||
## Holding mandatory (HA)
|
||||
## Holding mandatory (altitude end) (HA)
|
||||
|
||||
### Example
|
||||
|
||||
@ -420,7 +420,7 @@ My guess is that `Distance` always is a distance. This would match with the exam
|
||||
The Leg terminates after the hold is exited ath the hold entry fix.
|
||||
|
||||
|
||||
## Holding mandatory (HM)
|
||||
## Holding mandatory (manual end) (HM)
|
||||
|
||||
### Example
|
||||
|
||||
@ -493,8 +493,11 @@ FAUP VDM35 APP (Cycle 2507, ID 67790)
|
||||
### Instructions
|
||||
|
||||
- From the fix identified by (`WptID`, `WptLat`, `WptLon`) *or*
|
||||
(`NavID`, `NavLat`, `NavLon`, `NavDist`, `NavBear`), fly `Course` for `Distance`.
|
||||
- Turn `TurnDir` until aligned with succeeding leg course.
|
||||
(`NavID`, `NavLat`, `NavLon`, `NavDist`, `NavBear`), fly direct to succeeding leg fix.
|
||||
- Turn `TurnDir` onto `Course`.
|
||||
- Fly for 1 minute.
|
||||
- Turn 180°.
|
||||
- Fly until intercepting course of succeeding leg.
|
||||
- `Alt` shall be the altitude during the procedure.
|
||||
|
||||
### Units
|
||||
@ -505,8 +508,10 @@ FAUP VDM35 APP (Cycle 2507, ID 67790)
|
||||
|
||||
### Notes
|
||||
|
||||
This needs to look into the succeeding leg in order to calculate the intercept point.
|
||||
This intercept point then becomes the origin fix of the succeeding leg.
|
||||
This needs to look into the succeeding leg in order to fly the first segment and get the course
|
||||
to intercept after the reversal.
|
||||
The database does not specify how long the outbound leg shall be.
|
||||
1 minute is assumed here (googled it, seemed a common one).
|
||||
|
||||
|
||||
## Radius to Fix (RF)
|
||||
|
||||
@ -11,7 +11,11 @@ import { TerminatorsFA } from './terminators/FA';
|
||||
import { TerminatorsFC } from './terminators/FC';
|
||||
import { TerminatorsFD } from './terminators/FD';
|
||||
import { TerminatorsFM } from './terminators/FM';
|
||||
import { TerminatorsHA } from './terminators/HA';
|
||||
import { TerminatorsHF } from './terminators/HF';
|
||||
import { TerminatorsHM } from './terminators/HM';
|
||||
import { TerminatorsIF } from './terminators/IF';
|
||||
import { TerminatorsPI } from './terminators/PI';
|
||||
import { TerminatorsRF } from './terminators/RF';
|
||||
import { TerminatorsTF } from './terminators/TF';
|
||||
import { TerminatorsVA } from './terminators/VA';
|
||||
@ -135,7 +139,10 @@ class Parser {
|
||||
|
||||
// Main
|
||||
for (let index = 0; index < procedure.length; index++) {
|
||||
console.log('Processing leg with ID', procedure[index].ID);
|
||||
|
||||
const leg = procedure[index];
|
||||
leg.Alt = leg.IsMAP ? String(runway.Elevation + 200).padStart(5, '0') : leg.Alt;
|
||||
const previousFix = navFixes.at(-1)!;
|
||||
legOptions.isMAP ||= previousFix.IsMAP ?? false;
|
||||
const waypoint = this.waypoints.find(({ ID }) => ID === leg.WptID);
|
||||
@ -208,11 +215,21 @@ class Parser {
|
||||
navFixes.at(-1)!.isFlyOver = true;
|
||||
break;
|
||||
}
|
||||
case 'HA':
|
||||
case 'HF':
|
||||
case 'HM':
|
||||
console.error('Unknown TrackCode', leg.TrackCode);
|
||||
case 'HA': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsHA(leg as HATerminalEntry, previousFix);
|
||||
update(fixToAdd, lineToAdd, { ...legOptions });
|
||||
break;
|
||||
}
|
||||
case 'HF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsHF(leg as HFTerminalEntry, previousFix);
|
||||
update(fixToAdd, lineToAdd, { ...legOptions });
|
||||
break;
|
||||
}
|
||||
case 'HM': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsHM(leg as HMTerminalEntry, previousFix);
|
||||
update(fixToAdd, lineToAdd, { ...legOptions });
|
||||
break;
|
||||
}
|
||||
case 'IF': {
|
||||
const fixToAdd = TerminatorsIF(leg as RFTerminalEntry, waypoint);
|
||||
// Only Runway, replace
|
||||
@ -222,9 +239,16 @@ class Parser {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'PI':
|
||||
console.error('Unknown TrackCode', leg.TrackCode);
|
||||
case 'PI': {
|
||||
const nextLeg = procedure[index + 1] as CFTerminalEntry;
|
||||
const _waypoint = this.waypoints.find(({ ID }) => ID === nextLeg.WptID);
|
||||
const [fixToAdd, lineToAdd] = TerminatorsPI(leg as PITerminalEntry, nextLeg, previousFix, lastCourse, [
|
||||
waypoint,
|
||||
_waypoint,
|
||||
]);
|
||||
update(fixToAdd, lineToAdd, { ...legOptions });
|
||||
break;
|
||||
}
|
||||
case 'RF': {
|
||||
const [fixToAdd, lineToAdd] = TerminatorsRF(leg as RFTerminalEntry, previousFix, lastCourse, waypoint);
|
||||
if (fixToAdd) {
|
||||
|
||||
@ -80,7 +80,7 @@ export const TerminatorsFC = (
|
||||
}
|
||||
|
||||
const targetFix: NavFix = {
|
||||
...computeDestinationPoint(arcEnd, leg.Distance.toMetre(), lastCourse),
|
||||
...computeDestinationPoint(arcEnd, leg.Distance.toMetre(), leg.Course.toTrue(refFix)),
|
||||
name: leg.Distance.toString(),
|
||||
isFlyOver: true,
|
||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||
|
||||
88
browser/src/parser/terminators/HA.ts
Normal file
88
browser/src/parser/terminators/HA.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import Parser from '../parser';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||
|
||||
// NOTE: Distance is interpreted as distance and not time
|
||||
export const TerminatorsHA = (leg: HATerminalEntry, previousFix: NavFix): [NavFix?, LineSegment[]?] => {
|
||||
const refFix = {
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
};
|
||||
const speed = computeSpeed(leg, previousFix);
|
||||
const turnRate = computeTurnRate(speed, Parser.AC_BANK);
|
||||
const inboundCrs = leg.Course.toTrue(refFix);
|
||||
const outboundCrs = inboundCrs.reciprocalCourse();
|
||||
|
||||
const line: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];
|
||||
|
||||
// Generate top arc
|
||||
let currentCrs = inboundCrs;
|
||||
while (!currentCrs.equal(outboundCrs)) {
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (outboundCrs - currentCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (currentCrs - outboundCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
currentCrs
|
||||
);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
}
|
||||
|
||||
const outboundStart = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
leg.Distance.toMetre(),
|
||||
outboundCrs
|
||||
);
|
||||
line.push([outboundStart.longitude, outboundStart.latitude]);
|
||||
|
||||
// Generate bottom arc
|
||||
currentCrs = outboundCrs;
|
||||
while (!currentCrs.equal(inboundCrs)) {
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (inboundCrs - currentCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (currentCrs - inboundCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
currentCrs
|
||||
);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
}
|
||||
|
||||
line.push([refFix.longitude, refFix.latitude]);
|
||||
|
||||
return [refFix, line];
|
||||
};
|
||||
88
browser/src/parser/terminators/HF.ts
Normal file
88
browser/src/parser/terminators/HF.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import Parser from '../parser';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||
|
||||
// NOTE: Distance is interpreted as distance and not time
|
||||
export const TerminatorsHF = (leg: HFTerminalEntry, previousFix: NavFix): [NavFix?, LineSegment[]?] => {
|
||||
const refFix = {
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
};
|
||||
const speed = computeSpeed(leg, previousFix);
|
||||
const turnRate = computeTurnRate(speed, Parser.AC_BANK);
|
||||
const inboundCrs = leg.Course.toTrue(refFix);
|
||||
const outboundCrs = inboundCrs.reciprocalCourse();
|
||||
|
||||
const line: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];
|
||||
|
||||
// Generate top arc
|
||||
let currentCrs = inboundCrs;
|
||||
while (!currentCrs.equal(outboundCrs)) {
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (outboundCrs - currentCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (currentCrs - outboundCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
currentCrs
|
||||
);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
}
|
||||
|
||||
const outboundStart = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
leg.Distance.toMetre(),
|
||||
outboundCrs
|
||||
);
|
||||
line.push([outboundStart.longitude, outboundStart.latitude]);
|
||||
|
||||
// Generate bottom arc
|
||||
currentCrs = outboundCrs;
|
||||
while (!currentCrs.equal(inboundCrs)) {
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (inboundCrs - currentCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (currentCrs - inboundCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
currentCrs
|
||||
);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
}
|
||||
|
||||
line.push([refFix.longitude, refFix.latitude]);
|
||||
|
||||
return [refFix, line];
|
||||
};
|
||||
88
browser/src/parser/terminators/HM.ts
Normal file
88
browser/src/parser/terminators/HM.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import Parser from '../parser';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||
|
||||
// NOTE: Distance is interpreted as distance and not time
|
||||
export const TerminatorsHM = (leg: HMTerminalEntry, previousFix: NavFix): [NavFix?, LineSegment[]?] => {
|
||||
const refFix = {
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
};
|
||||
const speed = computeSpeed(leg, previousFix);
|
||||
const turnRate = computeTurnRate(speed, Parser.AC_BANK);
|
||||
const inboundCrs = leg.Course.toTrue(refFix);
|
||||
const outboundCrs = inboundCrs.reciprocalCourse();
|
||||
|
||||
const line: LineSegment[] = [[previousFix.longitude, previousFix.latitude]];
|
||||
|
||||
// Generate top arc
|
||||
let currentCrs = inboundCrs;
|
||||
while (!currentCrs.equal(outboundCrs)) {
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (outboundCrs - currentCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (currentCrs - outboundCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
currentCrs
|
||||
);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
}
|
||||
|
||||
const outboundStart = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
leg.Distance.toMetre(),
|
||||
outboundCrs
|
||||
);
|
||||
line.push([outboundStart.longitude, outboundStart.latitude]);
|
||||
|
||||
// Generate bottom arc
|
||||
currentCrs = outboundCrs;
|
||||
while (!currentCrs.equal(inboundCrs)) {
|
||||
let time = 0;
|
||||
if (leg.TurnDir === 'R') {
|
||||
const delta = (inboundCrs - currentCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs + increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
} else {
|
||||
const delta = (currentCrs - inboundCrs).normaliseDegrees();
|
||||
const increment = delta < 0.1 ? delta : 0.1;
|
||||
currentCrs = (currentCrs - increment).normaliseDegrees();
|
||||
time = increment / turnRate;
|
||||
}
|
||||
|
||||
const arcFix = computeDestinationPoint(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
((speed / 3600) * time).toMetre(),
|
||||
currentCrs
|
||||
);
|
||||
|
||||
line.push([arcFix.longitude, arcFix.latitude]);
|
||||
}
|
||||
|
||||
line.push([refFix.longitude, refFix.latitude]);
|
||||
|
||||
return [refFix, line];
|
||||
};
|
||||
76
browser/src/parser/terminators/PI.ts
Normal file
76
browser/src/parser/terminators/PI.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
|
||||
import Parser from '../parser';
|
||||
import { generatePerformanceArc } from '../pathGenerators/generatePerformanceArc';
|
||||
import { computeIntersection } from '../utils/computeIntersection';
|
||||
import { computeSpeed } from '../utils/computeSpeed';
|
||||
import { computeTurnRate } from '../utils/computeTurnRate';
|
||||
|
||||
export const TerminatorsPI = (
|
||||
leg: PITerminalEntry,
|
||||
nextLeg: CFTerminalEntry, // As per NG docs in the TrmLegTypes.json
|
||||
previousFix: NavFix,
|
||||
lastCourse: number,
|
||||
waypoints: [Waypoint?, Waypoint?]
|
||||
): [NavFix?, LineSegment[]?] => {
|
||||
const speed = computeSpeed(leg, previousFix);
|
||||
const turnRate = computeTurnRate(speed, Parser.AC_BANK);
|
||||
|
||||
const originFix: NavFix = {
|
||||
latitude: leg.WptLat,
|
||||
longitude: leg.WptLon,
|
||||
name: waypoints[0]?.Ident ?? undefined,
|
||||
isFlyOver: leg.IsFlyOver,
|
||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
const endFix: NavFix = {
|
||||
latitude: nextLeg.WptLat,
|
||||
longitude: nextLeg.WptLon,
|
||||
name: waypoints[1]?.Ident ?? undefined,
|
||||
isFlyOver: leg.IsFlyOver,
|
||||
altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
|
||||
speed: speed,
|
||||
speedConstraint: leg.SpeedLimit,
|
||||
altitudeConstraint: leg.Alt,
|
||||
IsFAF: leg.IsFAF,
|
||||
IsMAP: leg.IsMAP,
|
||||
};
|
||||
const outboundCrs = leg.Course.toTrue(endFix);
|
||||
const interceptCrs = nextLeg.Course.toTrue(endFix);
|
||||
|
||||
const line: LineSegment[] = [
|
||||
[originFix.longitude, originFix.latitude],
|
||||
[endFix.longitude, endFix.latitude],
|
||||
];
|
||||
|
||||
// Outbound end
|
||||
const outEnd = computeDestinationPoint(
|
||||
endFix,
|
||||
((speed / 3600) * 60).toMetre(), // 1min leg
|
||||
outboundCrs
|
||||
);
|
||||
line.push([outEnd.longitude, outEnd.latitude]);
|
||||
|
||||
// Arc
|
||||
line.push(...generatePerformanceArc(outboundCrs.reciprocalCourse(), outboundCrs, outEnd, speed, leg.TurnDir));
|
||||
|
||||
// Intercept
|
||||
const interceptFix = computeIntersection(
|
||||
{
|
||||
latitude: line.at(-1)![1],
|
||||
longitude: line.at(-1)![0],
|
||||
},
|
||||
outboundCrs.reciprocalCourse(),
|
||||
endFix,
|
||||
interceptCrs.reciprocalCourse()
|
||||
);
|
||||
|
||||
line.push([interceptFix!.longitude, interceptFix!.latitude]);
|
||||
line.push([endFix!.longitude, endFix!.latitude]);
|
||||
|
||||
return [undefined, line];
|
||||
};
|
||||
6
browser/src/types/terminators/HA.d.ts
vendored
Normal file
6
browser/src/types/terminators/HA.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
export declare global {
|
||||
type HATerminalEntry = Required<
|
||||
Pick<TerminalEntry, 'WptID' | 'WptLat' | 'WptLon' | 'TurnDir' | 'Course' | 'Distance' | 'Alt'>
|
||||
> &
|
||||
TerminalEntry;
|
||||
}
|
||||
6
browser/src/types/terminators/HF.d.ts
vendored
Normal file
6
browser/src/types/terminators/HF.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
export declare global {
|
||||
type HFTerminalEntry = Required<
|
||||
Pick<TerminalEntry, 'WptID' | 'WptLat' | 'WptLon' | 'TurnDir' | 'Course' | 'Distance'>
|
||||
> &
|
||||
TerminalEntry;
|
||||
}
|
||||
6
browser/src/types/terminators/HM.d.ts
vendored
Normal file
6
browser/src/types/terminators/HM.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
export declare global {
|
||||
type HMTerminalEntry = Required<
|
||||
Pick<TerminalEntry, 'WptID' | 'WptLat' | 'WptLon' | 'TurnDir' | 'Course' | 'Distance'>
|
||||
> &
|
||||
TerminalEntry;
|
||||
}
|
||||
19
browser/src/types/terminators/PI.d.ts
vendored
Normal file
19
browser/src/types/terminators/PI.d.ts
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
export declare global {
|
||||
type PITerminalEntry = Required<
|
||||
Pick<
|
||||
TerminalEntry,
|
||||
| 'WptID'
|
||||
| 'WptLat'
|
||||
| 'WptLon'
|
||||
| 'NavID'
|
||||
| 'NavLat'
|
||||
| 'NavLon'
|
||||
| 'NavBear'
|
||||
| 'NavDist'
|
||||
| 'Course'
|
||||
| 'Distance'
|
||||
| 'Alt'
|
||||
>
|
||||
> &
|
||||
TerminalEntry;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user