b05ffedbc1
Add a switchable cockpit-profile selector (Garmin G1000 / Cessna Citation X / GA steam) and recreate the Citation X Honeywell Primus 2000 avionics line-for- line from the X-Plane Citation X + FMS manuals: - CitPFD: attitude w/ FD command bars, speed tape (Vmo barber-pole, Vfe, low- speed red/amber bands), AOA index, altitude tape + trend, VSI, round HSI with CDI/course pointer + VOR/ADF bearing pointers, radar altimeter, minimums, STD/BARO/CRS/HDG bezel. - CitEICAS: twin FAN%/ITT bar gauges, OIL °C/PSI, FUEL (flow/qty PPH·LBS), ELECTRICAL, HYDRAULICS, slat chevron, STAB trim, FLAPS, CAS message stack, softkeys NORM/FUEL-HYD/ELEC/CTRL-POS/ENG + control-position overlay. - CitMFD: Honeywell heading-up arc map, FMS route (magenta active/white future), TCAS, terrain/WX, range arc, ETE/SAT/TAS/GSPD block, clock + ET/FT timer, V-SPEEDS reference card, MFD-setup overlays (TRAFFIC/TERRAIN/APTS/VOR). - CitAP: HDG/NAV/APP/BC · ALT/VNAV/BANK/STBY · FLC/C-O/VS · pitch wheel · AP/YD/M-TRIM/PFD-SEL, FMA bar + lamps from per-mode *_status datarefs. - CitRMU: COM/NAV active+standby tuning, transponder, ADF, TCAS range/mode, IDENT + Nav Source Selector (NAV1/2/FMS, VOR/ADF/FMS bearing source). Integration: all avionics stream live via the X-Plane Web API (new datarefs for N1/N2/ITT, radar-alt, AOA, hydraulics, trim, flaps/slats/gear, control positions, ADF, mach, yaw-damper); the existing fms-sync.lua drives the Citation's built-in FMS (aircraft-agnostic XPLM FMS SDK). Demo seeds added so every panel renders offline. Verified headless via Playwright (no console errors; G1000/GA profiles unaffected). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
247 lines
14 KiB
JavaScript
247 lines
14 KiB
JavaScript
// Central configuration: which X-Plane datarefs/commands the cockpit needs.
|
||
//
|
||
// These are *universal* datarefs that work on virtually every aircraft.
|
||
// To add a G1000- or aircraft-specific instrument, just add its dataref name
|
||
// here under DATAREFS (read) and/or WRITABLE_DATAREFS / COMMANDS (interact).
|
||
|
||
export const CONFIG = {
|
||
// Where X-Plane's built-in web server listens (on the same PC). X-Plane 12.1.1+.
|
||
xplaneHost: process.env.XPLANE_HOST || 'localhost',
|
||
xplanePort: Number(process.env.XPLANE_PORT || 8086),
|
||
xplaneApiBase: '/api/v3',
|
||
|
||
// Where THIS bridge serves the UI + relays data. 0.0.0.0 => reachable from the LAN.
|
||
bridgeHost: process.env.BRIDGE_HOST || '0.0.0.0',
|
||
bridgePort: Number(process.env.BRIDGE_PORT || 8080),
|
||
|
||
// How often X-Plane pushes value updates (it caps near 10–20 Hz anyway).
|
||
updateHz: Number(process.env.UPDATE_HZ || 20),
|
||
};
|
||
|
||
// Datarefs we SUBSCRIBE to and stream to every client. Keyed by a short alias
|
||
// the frontend uses, so the long sim/... names live in exactly one place.
|
||
export const DATAREFS = {
|
||
// --- primary flight data ---
|
||
airspeed: 'sim/cockpit2/gauges/indicators/airspeed_kts_pilot',
|
||
altitude: 'sim/cockpit2/gauges/indicators/altitude_ft_pilot',
|
||
vspeed: 'sim/cockpit2/gauges/indicators/vvi_fpm_pilot',
|
||
pitch: 'sim/cockpit2/gauges/indicators/pitch_AHARS_deg_pilot',
|
||
roll: 'sim/cockpit2/gauges/indicators/roll_AHARS_deg_pilot',
|
||
heading: 'sim/cockpit2/gauges/indicators/heading_AHARS_deg_mag_pilot',
|
||
slip: 'sim/cockpit2/gauges/indicators/slip_deg',
|
||
gForce: 'sim/flightmodel/forces/g_nrml',
|
||
|
||
// --- position / navigation (for the moving map) ---
|
||
lat: 'sim/flightmodel/position/latitude',
|
||
lon: 'sim/flightmodel/position/longitude',
|
||
groundspeed: 'sim/flightmodel/position/groundspeed', // m/s
|
||
track: 'sim/cockpit2/gauges/indicators/ground_track_mag_pilot', // deg
|
||
gpsDistNm: 'sim/cockpit2/radios/indicators/gps_dme_distance_nm',
|
||
gpsBearing: 'sim/cockpit2/radios/indicators/gps_bearing_deg_mag',
|
||
|
||
// --- engine / misc (handy on an MFD) ---
|
||
fuelTotal: 'sim/cockpit2/fuel/fuel_quantity', // array
|
||
oat: 'sim/cockpit2/temperature/outside_air_temp_degc',
|
||
|
||
// --- G1000 PFD: radios (NAV/COM active + standby) ---
|
||
nav1: 'sim/cockpit2/radios/actuators/nav1_frequency_hz',
|
||
nav1Sb: 'sim/cockpit2/radios/actuators/nav1_standby_frequency_hz',
|
||
nav2: 'sim/cockpit2/radios/actuators/nav2_frequency_hz',
|
||
nav2Sb: 'sim/cockpit2/radios/actuators/nav2_standby_frequency_hz',
|
||
com1: 'sim/cockpit2/radios/actuators/com1_frequency_hz',
|
||
com1Sb: 'sim/cockpit2/radios/actuators/com1_standby_frequency_hz',
|
||
com2: 'sim/cockpit2/radios/actuators/com2_frequency_hz',
|
||
com2Sb: 'sim/cockpit2/radios/actuators/com2_standby_frequency_hz',
|
||
|
||
// --- G1000 PFD: HSI / CDI ---
|
||
obsCrs: 'sim/cockpit2/radios/actuators/nav1_obs_deg_mag_pilot',
|
||
gsDef: 'sim/cockpit/radios/nav1_vdef', // glideslope vertical deflection (dots)
|
||
hsiDef: 'sim/cockpit2/radios/indicators/hsi_hdef_dots_pilot',
|
||
hsiToFrom: 'sim/cockpit2/radios/indicators/hsi_flag_from_to_pilot',
|
||
navBearing: 'sim/cockpit2/radios/indicators/hsi_bearing_deg_mag_pilot',
|
||
|
||
// --- bearing pointers (BRG1/BRG2) + DME + marker beacons ---
|
||
nav1Brg: 'sim/cockpit2/radios/indicators/nav1_bearing_deg_mag',
|
||
nav2Brg: 'sim/cockpit2/radios/indicators/nav2_bearing_deg_mag',
|
||
nav1Dme: 'sim/cockpit2/radios/indicators/nav1_dme_distance_nm',
|
||
nav2Dme: 'sim/cockpit2/radios/indicators/nav2_dme_distance_nm',
|
||
mkrOuter: 'sim/cockpit2/radios/indicators/outer_marker_lit',
|
||
mkrMiddle: 'sim/cockpit2/radios/indicators/middle_marker_lit',
|
||
mkrInner: 'sim/cockpit2/radios/indicators/inner_marker_lit',
|
||
|
||
// --- G1000 UI state (for display sync with the in-sim G1000) ---
|
||
// CDI/HSI source: 0 = NAV1/VLOC1, 1 = NAV2/VLOC2, 2 = GPS (standard dataref).
|
||
cdiSrc: 'sim/cockpit2/radios/actuators/HSI_source_select_pilot',
|
||
// The rest are G1000-internal, so the FlyWithLua companion (ui-sync.lua)
|
||
// publishes them as custom datarefs. Absent until the plugin runs -> the web
|
||
// G1000 just keeps its own local UI state (graceful).
|
||
uiMfdPage: 'glasscockpit/ui/mfd_page', // 0 map, 1 fpl, 2 nrst
|
||
uiMapRange: 'glasscockpit/ui/map_range_nm', // active map range, NM
|
||
uiInset: 'glasscockpit/ui/inset', // PFD inset map on/off (0/1)
|
||
|
||
// --- G1000 PFD: data fields ---
|
||
baro: 'sim/cockpit2/gauges/actuators/barometer_setting_in_hg_pilot',
|
||
tas: 'sim/cockpit2/gauges/indicators/true_airspeed_kts_pilot',
|
||
windSpd: 'sim/cockpit2/gauges/indicators/wind_speed_kts',
|
||
windDir: 'sim/cockpit2/gauges/indicators/wind_heading_deg_mag',
|
||
xpdrCode: 'sim/cockpit2/radios/actuators/transponder_code',
|
||
xpdrMode: 'sim/cockpit2/radios/actuators/transponder_mode',
|
||
fdPitch: 'sim/cockpit2/autopilot/flight_director_pitch_deg',
|
||
fdRoll: 'sim/cockpit2/autopilot/flight_director_roll_deg',
|
||
|
||
// --- G1000 MFD: engine strip (arrays — UI reads index 0/1) ---
|
||
engRpm: 'sim/cockpit2/engine/indicators/engine_speed_rpm',
|
||
fuelFlow: 'sim/cockpit2/engine/indicators/fuel_flow_kg_sec',
|
||
oilTemp: 'sim/cockpit2/engine/indicators/oil_temperature_deg_C',
|
||
oilPress: 'sim/cockpit2/engine/indicators/oil_pressure_psi',
|
||
egt: 'sim/cockpit2/engine/indicators/EGT_deg_C',
|
||
fuelQty: 'sim/cockpit2/fuel/fuel_quantity',
|
||
fuelTot: 'sim/cockpit2/fuel/fuel_totalizer_sum_kg', // pilot-set fuel totalizer (remaining, kg)
|
||
fuelMax: 'sim/aircraft/weight/acf_m_fuel_tot', // max fuel capacity (kg) — for RST FUEL
|
||
volts: 'sim/cockpit2/electrical/bus_volts', // array: [0]=main bus, [1]=essential
|
||
amps: 'sim/cockpit2/electrical/battery_amps', // battery (S) amps
|
||
genAmps: 'sim/cockpit2/electrical/generator_amps', // alternator (M) amps
|
||
engHrs: 'sim/time/total_flight_time_sec', // proxy for engine/tach hours
|
||
|
||
// --- autopilot readouts (live values, so the panel reflects reality) ---
|
||
apState: 'sim/cockpit2/autopilot/autopilot_state', // bitfield of active modes
|
||
apHdgBug: 'sim/cockpit2/autopilot/heading_dial_deg_mag_pilot',
|
||
apAltBug: 'sim/cockpit2/autopilot/altitude_dial_ft_pilot',
|
||
apVsBug: 'sim/cockpit2/autopilot/vvi_dial_fpm',
|
||
apSpdBug: 'sim/cockpit2/autopilot/airspeed_dial_kts_mach',
|
||
apEngaged: 'sim/cockpit2/autopilot/servos_on',
|
||
navHdef: 'sim/cockpit2/radios/indicators/hsi_relative_bearing_vor_pilot',
|
||
|
||
// --- AFCS mode annunciation (the green/white mode strip on a real G1000) ---
|
||
// X-Plane's per-mode status datarefs: 0 = off, 1 = armed, 2 = active/captured.
|
||
// These mean the AFCS bar mirrors the sim exactly, no Lua needed.
|
||
apMode: 'sim/cockpit2/autopilot/autopilot_mode', // 0 off, 1 FD, 2 AP
|
||
hdgStatus: 'sim/cockpit2/autopilot/hdg_status',
|
||
navStatus: 'sim/cockpit2/autopilot/nav_status',
|
||
gpssStatus: 'sim/cockpit2/autopilot/gpss_status',
|
||
aprStatus: 'sim/cockpit2/autopilot/approach_status',
|
||
bcStatus: 'sim/cockpit2/autopilot/backcourse_status',
|
||
altStatus: 'sim/cockpit2/autopilot/alt_hold_status',
|
||
vsStatus: 'sim/cockpit2/autopilot/vvi_status',
|
||
flcStatus: 'sim/cockpit2/autopilot/speed_status',
|
||
gsStatus: 'sim/cockpit2/autopilot/glideslope_status',
|
||
vnavStatus: 'sim/cockpit2/autopilot/vnav_status',
|
||
|
||
// ====================================================================
|
||
// CESSNA CITATION X (model 750) — twin Rolls-Royce AE3007C turbofans.
|
||
// Honeywell Primus 2000 suite: PFD / MFD / EICAS / dual CDU.
|
||
// All of these are universal sim datarefs, streamed live with no Lua;
|
||
// only the FMS flight-plan needs the FlyWithLua bridge (fms-sync.lua).
|
||
// ====================================================================
|
||
// --- engine (arrays index 0 = LH, 1 = RH) ---
|
||
n1: 'sim/cockpit2/engine/indicators/N1_percent', // Fan RPM % (EICAS FAN%)
|
||
n2: 'sim/cockpit2/engine/indicators/N2_percent', // Core RPM % (standby panel)
|
||
itt: 'sim/cockpit2/engine/indicators/ITT_deg_C', // Interstage Turbine Temp °C
|
||
fuelPress: 'sim/cockpit2/engine/indicators/fuel_pressure_psi',
|
||
throttle: 'sim/cockpit2/engine/actuators/throttle_ratio', // per-engine commanded thrust
|
||
// --- Citation PFD extras ---
|
||
radioAlt: 'sim/cockpit2/gauges/indicators/radio_altimeter_height_ft_pilot', // RA (<2500 ft AGL)
|
||
mach: 'sim/cockpit2/gauges/indicators/mach_pilot', // PFD Mach / FLC target
|
||
aoa: 'sim/flightmodel/position/alpha', // angle of attack (deg) → normalised
|
||
adf1Brg: 'sim/cockpit2/radios/indicators/adf1_relative_bearing_deg',
|
||
adf2Brg: 'sim/cockpit2/radios/indicators/adf2_relative_bearing_deg',
|
||
adf1: 'sim/cockpit2/radios/actuators/adf1_frequency_hz',
|
||
adf2: 'sim/cockpit2/radios/actuators/adf2_frequency_hz',
|
||
// --- EICAS systems ---
|
||
hydPress: 'sim/cockpit2/hydraulics/indicators/hydraulic_pressure_psi', // array A/B (PSI)
|
||
elevTrim: 'sim/cockpit2/controls/elevator_trim', // -1..1 → STAB deg
|
||
flapRatio: 'sim/cockpit2/controls/flap_ratio', // 0..1 → SLAT/5/15/FULL
|
||
flapDeploy: 'sim/flightmodel2/controls/flap1_deploy_ratio', // actual trailing-edge flap
|
||
slatRatio: 'sim/flightmodel2/controls/slat_ratio', // leading-edge slat status
|
||
gearHandle: 'sim/cockpit2/controls/gear_handle_down', // 0 up / 1 down
|
||
gearDeploy: 'sim/flightmodel2/gear/deploy_ratio', // array: per-gear 0..1
|
||
speedbrake: 'sim/cockpit2/controls/speedbrake_ratio',
|
||
parkBrake: 'sim/cockpit2/controls/parking_brake_ratio',
|
||
// control-position graphic (CTRL POS page): commanded surface ratios -1..1
|
||
ailDefl: 'sim/cockpit2/controls/yoke_roll_ratio',
|
||
elevDefl: 'sim/cockpit2/controls/yoke_pitch_ratio',
|
||
rudDefl: 'sim/cockpit2/controls/yoke_heading_ratio',
|
||
battVolt: 'sim/cockpit2/electrical/battery_voltage', // array per battery
|
||
battTemp: 'sim/cockpit2/electrical/battery_temp_C', // ELEC page (append)
|
||
// --- yaw damper / mach-trim annunciation (Citation AP YD / M TRIM) ---
|
||
ydOn: 'sim/cockpit2/switches/yaw_damper_on',
|
||
};
|
||
|
||
// Datarefs the frontend may WRITE (e.g. turning the heading bug knob).
|
||
export const WRITABLE_DATAREFS = {
|
||
apHdgBug: 'sim/cockpit2/autopilot/heading_dial_deg_mag_pilot',
|
||
apAltBug: 'sim/cockpit2/autopilot/altitude_dial_ft_pilot',
|
||
apVsBug: 'sim/cockpit2/autopilot/vvi_dial_fpm',
|
||
apSpdBug: 'sim/cockpit2/autopilot/airspeed_dial_kts_mach',
|
||
xpdrMode: 'sim/cockpit2/radios/actuators/transponder_mode', // 0 off,1 stby,2 on,3 alt
|
||
xpdrCode: 'sim/cockpit2/radios/actuators/transponder_code', // 4-digit squawk
|
||
cdiSrc: 'sim/cockpit2/radios/actuators/HSI_source_select_pilot', // 0 NAV1 · 1 NAV2 · 2 GPS (CDI softkey cycles it)
|
||
fuelTot: 'sim/cockpit2/fuel/fuel_totalizer_sum_kg', // SYSTEM → DEC/INC/RST FUEL adjusts the totalizer
|
||
// NRST page: load a selected airport's tower/CTAF into COM standby, or a VOR into NAV standby.
|
||
com1Sb: 'sim/cockpit2/radios/actuators/com1_standby_frequency_hz',
|
||
com2Sb: 'sim/cockpit2/radios/actuators/com2_standby_frequency_hz',
|
||
nav1Sb: 'sim/cockpit2/radios/actuators/nav1_standby_frequency_hz',
|
||
nav2Sb: 'sim/cockpit2/radios/actuators/nav2_standby_frequency_hz',
|
||
};
|
||
|
||
// Commands the frontend may TRIGGER (autopilot mode buttons etc.).
|
||
export const COMMANDS = {
|
||
apToggle: 'sim/autopilot/servos_toggle',
|
||
fdToggle: 'sim/autopilot/fdir_toggle',
|
||
hdg: 'sim/autopilot/heading',
|
||
nav: 'sim/autopilot/NAV',
|
||
apr: 'sim/autopilot/approach',
|
||
altHold: 'sim/autopilot/altitude_hold',
|
||
vs: 'sim/autopilot/vertical_speed',
|
||
flc: 'sim/autopilot/level_change',
|
||
vnav: 'sim/autopilot/vnav',
|
||
backCourse:'sim/autopilot/back_course',
|
||
noseUp: 'sim/autopilot/nose_up',
|
||
noseDown: 'sim/autopilot/nose_down',
|
||
altUp: 'sim/autopilot/altitude_up',
|
||
altDown: 'sim/autopilot/altitude_down',
|
||
hdgUp: 'sim/autopilot/heading_up',
|
||
hdgDown: 'sim/autopilot/heading_down',
|
||
xpdrIdent: 'sim/transponder/transponder_ident',
|
||
|
||
// --- Citation X autopilot (Honeywell Primus) extras ---
|
||
// The mode buttons reuse the universal AP commands above (hdg/nav/apr/bc/
|
||
// altHold/flc/vs/vnav). These add the Citation-specific master functions.
|
||
yawDamper: 'sim/systems/yaw_damper_toggle', // YD button (engages w/ AP too)
|
||
apStby: 'sim/autopilot/control_wheel_steer', // STBY → basic pitch/roll (CWS), drops modes
|
||
spdUp: 'sim/autopilot/airspeed_up', // pitch wheel in FLC = target IAS/Mach
|
||
spdDown: 'sim/autopilot/airspeed_down',
|
||
vsUp: 'sim/autopilot/vertical_speed_up', // pitch wheel in V/S = target fpm
|
||
vsDown: 'sim/autopilot/vertical_speed_down',
|
||
};
|
||
|
||
// Per-radio standby tuning (coarse = MHz, fine = kHz) + active/standby flip.
|
||
// These work regardless of the dataref's frequency units, so the web tuner just
|
||
// fires them — no risky raw frequency writes.
|
||
for (const r of ['nav1', 'nav2', 'com1', 'com2']) {
|
||
COMMANDS[`${r}CoarseUp`] = `sim/radios/stby_${r}_coarse_up`;
|
||
COMMANDS[`${r}CoarseDown`] = `sim/radios/stby_${r}_coarse_down`;
|
||
COMMANDS[`${r}FineUp`] = `sim/radios/stby_${r}_fine_up`;
|
||
COMMANDS[`${r}FineDown`] = `sim/radios/stby_${r}_fine_down`;
|
||
COMMANDS[`${r}Swap`] = `sim/radios/${r}_standby_flip`;
|
||
}
|
||
|
||
// Every clickable G1000 bezel control maps to a real X-Plane command. The PFD
|
||
// is unit n1, the MFD is unit n3 (the default C172 layout). Aliases are
|
||
// prefixed pfd_/mfd_ so the frontend just says e.g. command('mfd_fpl').
|
||
const G1000_KEYS = [
|
||
...Array.from({ length: 12 }, (_, i) => `softkey${i + 1}`),
|
||
'direct', 'menu', 'fpl', 'proc', 'clr', 'ent', 'cursor',
|
||
'fms_outer_up', 'fms_outer_down', 'fms_inner_up', 'fms_inner_down',
|
||
'range_up', 'range_down', 'pan_push', 'pan_up', 'pan_down', 'pan_left', 'pan_right',
|
||
'hdg_up', 'hdg_down', 'hdg_sync',
|
||
'alt_outer_up', 'alt_outer_down', 'alt_inner_up', 'alt_inner_down',
|
||
'crs_up', 'crs_down', 'crs_sync', 'baro_up', 'baro_down',
|
||
'nav_outer_up', 'nav_outer_down', 'nav_inner_up', 'nav_inner_down', 'nav12', 'nvol_up', 'nvol_dn',
|
||
'com_outer_up', 'com_outer_down', 'com_inner_up', 'com_inner_down', 'com12', 'cvol_up', 'cvol_dn',
|
||
'ap', 'fd', 'hdg', 'alt', 'nav', 'vnv', 'apr', 'bc', 'vs', 'flc', 'nose_up', 'nose_down',
|
||
];
|
||
for (const [unit, prefix] of [['n1', 'pfd'], ['n3', 'mfd']]) {
|
||
for (const k of G1000_KEYS) COMMANDS[`${prefix}_${k}`] = `sim/GPS/g1000${unit}_${k}`;
|
||
}
|