fb6f0182cc
The CDI softkey was in the PFD root row but had no handler — it never cycled the HSI/CDI source (GPS↔VLOC1↔VLOC2). Wire it to write cdiSrc (HSI_source_select_pilot, now writable), cycling GPS→NAV1→NAV2. Also fix a latent demo bug: the 'needs a live sim socket' guard sat above the setDataref/command handlers, so in DEMO (no sim socket) every dataref write (transponder code, baro, AP bugs, CDI) was silently dropped. Reflect writes locally before that guard so cockpit controls respond without a sim. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
190 lines
10 KiB
JavaScript
190 lines
10 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',
|
||
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',
|
||
};
|
||
|
||
// 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)
|
||
};
|
||
|
||
// 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',
|
||
};
|
||
|
||
// 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}`;
|
||
}
|