1 contributor
# vim: set foldmethod=marker foldmarker={{{,}}} :
var softkeysClass = {
new : func (device) {
var m = { parents: [ softkeysClass ] };
m.device = device;
m.path = [];
m.colored = {};
return m;
},
SoftKey : func (n, a) {
# released key not yet managed
if (a == 1)
return;
var key = me.device.display.screenElements[sprintf("SoftKey%02i-text",n)].get('text');
if (key == '' or key == nil)
return;
var path = keyMap[me.device.role];
foreach(var p; me.path) {
if (contains(path, p))
path = path[p];
else
break;
}
var bindings = me.bindings[me.device.role];
foreach(var p; me.path) {
if (contains(bindings, p))
bindings = bindings[p];
else
break;
}
if (contains(path, key)) {
append(me.path, key);
if (contains(bindings, key))
if (contains(bindings[key], 'hook'))
call(bindings[key].hook, [], me);
me.device.display.updateSoftKeys();
}
elsif (contains(bindings, key)) {
call(bindings[key], [], me);
}
elsif (key == 'BACK') {
pop(me.path);
me.device.display.updateSoftKeys();
}
else {
var list_path = '';
foreach(var p; me.path) list_path ~= p ~ '/';
print(me.device.role ~ ':' ~ list_path ~ key ~ ' : not yet implemented');
}
},
bindings : {
PFD : {
INSET: {
OFF: func {
me.device.map.setVisible(0);
me.device.display.screenElements['PFD-Map-bg'].hide();
},
TRAFFIC: func {
me.device.data.tcas = ! me.device.data.tcas;
if (me.device.data.tcas)
me.colored['INSETTRAFFIC'] = me.device.data.tcas;
else
delete(me.colored, 'INSETTRAFFIC');
me.device.display.updateSoftKeys();
me.device.map.layers.tcas.setVisible(me.device.data.tcas);
},
hook : func {
me.device.display.screenElements['PFD-Map-bg'].show();
me.device.map.setVisible(1);
me.device.map.update();
},
},
PFD: {
'AOA/WIND' : {
AOA : {
'AOA ON' : func {
if (me.device.data['aoa-auto'])
return;
me.device.data.aoa = ! me.device.data.aoa;
foreach (var e; ['AOA', 'AOA-needle', 'AOA-text'])
me.device.display.screenElements[e]
.setVisible(me.device.data.aoa);
me.device.display.screenElements['AOA-approach']
.setVisible(me.device.data.aoa and contains(data, 'approach-aoa'));
me.device.display.updateAOA();
me.device.display.setSoftKeyColor(5 ,me.device.data.aoa);
if (me.device.data.aoa)
me.colored['PFDAOA/WINDAOAAOA ON'] = 1;
else
delete(me.colored, 'PFDAOA/WINDAOAAOA ON');
},
'AOA AUTO' : func {
if (me.device.data.aoa)
return;
me.device.data['aoa-auto'] = ! me.device.data['aoa-auto'];
me.device.display.setSoftKeyColor(6 ,me.device.data['aoa-auto']);
if (me.device.data['aoa-auto']) {
me.colored['PFDAOA/WINDAOAAOA AUTO'] = 1;
if (!contains(me.device.timers, 'aoa'))
me.device.timers.aoa = maketimer(1,
func {
var v = getprop('/gear/gear/position-norm') == 1
and getprop('/surfaces-positions/flap-pos-norm') != 0;
foreach (var e; ['AOA', 'AOA-needle', 'AOA-text'])
me.device.display.screenElements[e]
.setVisible(v);
me.device.display.screenElements['AOA-approach']
.setVisible(v and contains(data, 'approach-aoa'));
}, me);
me.device.timers.aoa.start();
}
else {
delete(me.colored, 'PFDAOA/WINDAOAAOA AUTO');
me.device.timers.aoa.stop();
me.device.data.aoa = 0;
me.device.display.screenElements['AOA']
.hide();
}
},
hook : func {
if (contains(data,'approach-aoa'))
me.device.display.screenElements['AOA-approach']
.setRotation(-data['approach-aoa']/data['stall-aoa']*math.pi);
},
},
WIND : {
OPTN1 : func {
me.device.display._winddata_optn = 1;
me.device.display.screenElements['WindData'].show();
me.device.display.screenElements['WindData-OPTN1'].show();
me.device.display.screenElements['WindData-OPTN1-HDG'].show();
me.device.display.screenElements['WindData-OPTN2'].hide();
me.device.display.updateWindData();
me.device.display.setSoftKeyColor(2, 1);
me.colored['PFDAOA/WINDWINDOPTN1'] = 1;
me.device.display.setSoftKeyColor(3, 0);
delete(me.colored, 'PFDAOA/WINDWINDOPTN2');
},
OPTN2 : func {
me.device.display._winddata_optn = 2;
me.device.display.screenElements['WindData'].show();
me.device.display.screenElements['WindData-OPTN1'].hide();
me.device.display.screenElements['WindData-OPTN2'].show();
me.device.display.screenElements['WindData-OPTN2-symbol'].show();
me.device.display.screenElements['WindData-OPTN2-headwind'].show();
me.device.display.screenElements['WindData-OPTN2-crosswind'].show();
me.device.display.updateWindData();
me.device.display.setSoftKeyColor(2, 0);
delete(me.colored, 'PFDAOA/WINDWINDOPTN1');
me.device.display.setSoftKeyColor(3, 1);
me.colored['PFDAOA/WINDWINDOPTN2'] = 1;
},
OFF : func {
me.device.display._winddata_optn = 0;
me.device.display.screenElements['WindData'].hide();
me.device.display.screenElements['WindData-OPTN1'].hide();
me.device.display.screenElements['WindData-OPTN2'].hide();
me.device.display.setSoftKeyColor(2, 0);
delete(me.colored, 'PFDAOA/WINDWINDOPTN1');
me.device.display.setSoftKeyColor(3, 0);
delete(me.colored, 'PFDAOA/WINDWINDOPTN2');
},
},
},
BRG1 : func (brg = 1){
var source = 'brg' ~ brg ~ '-source';
var list = ['NAV' ~ brg, 'GPS', 'ADF', 'OFF'];
var index = std.Vector
.new(list)
.index(radios.getNode(source).getValue());
var next = (index == size(list) -1) ? 0 : index + 1;
radios.getNode(source).setValue(list[next]);
if (list[next] != 'OFF') {
me.device.display.setSoftKeyColor(brg == 1 ? 4 : 6, 1);
me.colored['PFDBRG' ~ brg] = 1;
}
else {
me.device.display.setSoftKeyColor(brg == 1 ? 4 : 6, 0);
delete(me.colored, 'PFDBRG' ~ brg);
}
},
BRG2 : func {
call(me.bindings.PFD.PFD.BRG1, [ 2 ], me);
},
'STD BARO' : func {
setprop('/instrumentation/altimeter/setting-inhg', 29.92);
me.device.display.updateBARO();
pop(me.path);
me.device.display.updateSoftKeys();
},
IN : func {
me.device.display._baro_unit = 'inhg';
me.device.display.updateBARO();
},
HPA : func {
me.device.display._baro_unit = 'hpa';
me.device.display.updateBARO();
},
},
XPDR: {
STBY : func {
setprop('/instrumentation/transponder/ident', 0);
setprop('/instrumentation/transponder/knob-mode', 1);
setprop('/instrumentation/zkv1000/radio/xpdr-mode', 'STBY');
me.device.display.updateXPDR();
},
ON : func {
setprop('/instrumentation/transponder/ident', 1);
setprop('/instrumentation/transponder/knob-mode', 4);
setprop('/instrumentation/zkv1000/radio/xpdr-mode', 'ON');
me.device.display.updateXPDR();
},
ALT : func {
setprop('/instrumentation/transponder/ident', 1);
setprop('/instrumentation/transponder/knob-mode', 5);
setprop('/instrumentation/zkv1000/radio/xpdr-mode', 'ALT');
me.device.display.updateXPDR();
},
VFR : func {
setprop('/instrumentation/transponder/id-code', '1200');
me.device.display.updateXPDR();
},
IDENT : func {
call(me.bindings.PFD.IDENT, [], me);
},
CODE : {
'0' : func (n = 0) {
if (getprop('/instrumentation/zkv1000/radios/xpdr-tuning-fms-method'))
return;
me.device.display.timers2.softkeys_inactivity.stop();
me.bindings.PFD.XPDR.CODE.inactivity.restart(me.device.display.softkeys_inactivity_delay);
# disable FMS knob entering method
me.device.knobs.FmsInner = void;
# When entering the code, the next softkey in sequence
# must be pressed within 10 seconds, or the entry is cancelled
# and restored to the previous code
if (!contains(me.bindings.PFD.XPDR.CODE, 'on_change_inactivity')) {
me.bindings.PFD.XPDR.CODE.on_change_inactivity = maketimer(10,
func {
setprop('/instrumentation/zkv1000/radios/xpdr-tuning-digit', 3);
me.device.knobs.FmsInner = me.device.knobs.XPDRCodeSetDigits;
me.device.knobs.FmsOuter = me.device.knobs.XPDRCodeNextDigits;
call(me.bindings.PFD.XPDR.CODE.restore, [], me);
});
me.bindings.PFD.XPDR.CODE.on_change_inactivity.singleShot = 1;
me.bindings.PFD.XPDR.CODE.on_change_inactivity.start();
}
else
me.bindings.PFD.XPDR.CODE.on_change_inactivity.restart(10);
var tuning = radios.getNode('xpdr-tuning-digit');
var d = tuning.getValue();
setprop('/instrumentation/transponder/inputs/digit[' ~ d ~ ']', n);
if (d == 1) {
if (!contains(me.bindings.PFD.XPDR.CODE, 'on_change_auto_validation'))
me.bindings.PFD.XPDR.CODE.on_change_auto_validation = maketimer(5,
func call(me.bindings.PFD.IDENT, [], me));
me.bindings.PFD.XPDR.CODE.on_change_auto_validation.singleShot = 1;
me.bindings.PFD.XPDR.CODE.on_change_auto_validation.start();
}
else {
d -= 1;
tuning.setValue(d);
}
me.device.display.updateXPDR();
},
'1' : func {
call(me.bindings.PFD.XPDR.CODE['0'], [ 1 ], me);
},
'2' : func {
call(me.bindings.PFD.XPDR.CODE['0'], [ 2 ], me);
},
'3' : func {
call(me.bindings.PFD.XPDR.CODE['0'], [ 3 ], me);
},
'4' : func {
call(me.bindings.PFD.XPDR.CODE['0'], [ 4 ], me);
},
'5' : func {
call(me.bindings.PFD.XPDR.CODE['0'], [ 5 ], me);
},
'6' : func {
call(me.bindings.PFD.XPDR.CODE['0'], [ 6 ], me);
},
'7' : func {
call(me.bindings.PFD.XPDR.CODE['0'], [ 7 ], me);
},
IDENT: func {
me.bindings.PFD.XPDR.CODE.inactivity.restart(me.device.display.softkeys_inactivity_delay);
me.device.display.timers2.softkeys_inactivity.restart(me.device.display.softkeys_inactivity_delay);
call(me.bindings.PFD.IDENT, [], me);
},
BKSP: func {
if (getprop('/instrumentation/zkv1000/radios/xpdr-tuning-fms-method'))
return;
if (contains(me.bindings.PFD.XPDR.CODE, 'on_change_inactivity'))
me.bindings.PFD.XPDR.CODE.on_change_inactivity.restart(10);
if (contains(me.bindings.PFD.XPDR.CODE, 'on_change_auto_validation'))
me.bindings.PFD.XPDR.CODE.on_change_auto_validation.stop();
var tuning = radios.getNode('xpdr-tuning-digit');
var d = tuning.getValue();
if (d < 3) {
d += 1;
tuning.setValue(d);
}
me.device.display.updateXPDR();
},
BACK : func (inactive = 0) {
call(me.bindings.PFD.XPDR.CODE.restore, [], me);
pop(me.path);
call(me.bindings.PFD.XPDR.CODE.exit, [me.path], me);
},
restore : func {
setprop('/instrumentation/transponder/id-code',
sprintf('%s', getprop('/instrumentation/zkv1000/radios/xpdr-backup-code')));
me.device.display.updateXPDR();
},
exit : func (p) {
if (contains(me.bindings.PFD.XPDR.CODE, 'inactivity')) # does not exists if IDENT pressed from top-level
me.bindings.PFD.XPDR.CODE.inactivity.stop();
radios.removeChild('xpdr-tuning-digit', 0);
radios.removeChild('xpdr-backup-code', 0);
radios.removeChild('xpdr-tuning-fms-method', 0);
me.path = p;
me.device.display.updateXPDR();
me.device.display.updateSoftKeys();
me.device.knobs.FmsInner = void;
me.device.knobs.FmsOuter = void;
me.device.display.timers2.softkeys_inactivity.restart(me.device.display.softkeys_inactivity_delay);
},
hook : func {
# this level has its own timer as we may need to revert changes, and got different timers
me.device.display.timers2.softkeys_inactivity.stop();
me.bindings.PFD.XPDR.CODE.inactivity = maketimer(
me.device.display.softkeys_inactivity_delay,
func call(me.bindings.PFD.XPDR.CODE.BACK, [], me));
me.bindings.PFD.XPDR.CODE.inactivity.singleShot = 1;
me.bindings.PFD.XPDR.CODE.inactivity.start();
var tuning = getprop('/instrument/zkv1000/radios/xpdr-tuning-digit');
if (tuning == nil) {
radios.getNode('xpdr-tuning-digit', 1).setValue(3);
radios.getNode('xpdr-backup-code', 1).setValue(getprop('/instrumentation/transponder/id-code'));
radios.getNode('xpdr-tuning-fms-method', 1).setValue(0);
me.device.display.updateXPDR();
}
me.device.knobs.FmsInner = me.device.knobs.XPDRCodeSetDigits;
me.device.knobs.FmsOuter = me.device.knobs.XPDRCodeNextDigits;
},
},
},
IDENT : func {
if (getprop('/instrumentation/zkv1000/radio/xpdr-mode') == 'STBY')
return;
setprop('/instrumentation/transponder/ident', 1);
me.bindings.PFD.XPDR.ident = maketimer(18,
func {
setprop('/instrumentation/transponder/ident', 0);
me.device.display.updateXPDR();
});
me.bindings.PFD.XPDR.ident.singleShot = 1;
me.bindings.PFD.XPDR.ident.start();
call(me.bindings.PFD.XPDR.CODE.exit, [], me);
},
CDI : func {
var list = ['OFF'];
if (getprop('/instrumentation/gps/route-distance-nm') != nil)
append(list, 'GPS');
if (getprop('/instrumentation/nav/in-range') != nil)
append(list, 'NAV1');
if (getprop('/instrumentation/nav[1]/in-range') != nil)
append(list, 'NAV2');
var index = std.Vector
.new(list)
.index(cdi.getNode('source').getValue());
var next = (index == size(list) -1) ? 0 : index + 1;
cdi.getNode('source').setValue(list[next]);
CDIfromSOURCE(list[next]);
me.device.display.updateCDI();
},
'TMR/REF' : func {
if (!contains(me.device.windows.state, 'TMR/REF')) {
var Vspeed_visiblity = func (id, selected, Vspeed)
me.device.data[Vspeed ~ '-visible'] =
me.device.windows.state[id].objects[selected].text
==
me.device.windows.state[id].objects[selected].choices[0];
var GenericTimer = func (id, selected) {
var action = me.device.windows.state[id].objects[selected].text;
if (action == 'START?') {
me.device.data.TMRrevert = 0;
me.device.data.TMRlast = getprop('/sim/time/elapsed-sec') - 1;
me.device.data.TMRreset = me.device.windows.state[id].objects[selected - 2].text;
me.device.data.TMRtimer = maketimer(1, func {
var (hh, mm, ss) = split(':',
me.device.windows.state[id].objects[selected - 2].text);
var direction = -1;
if ((me.device.windows.state[id].objects[selected - 1].text
==
me.device.windows.state[id].objects[selected - 1].choices[0])
or me.device.data.TMRrevert)
direction = 1;
var now = getprop('/sim/time/elapsed-sec');
var dt = int(now - me.device.data.TMRlast) * direction;
me.device.data.TMRlast = now;
var val = HMS(hh, mm, ss, dt);
me.device.windows.state[id].objects[selected - 2].text = val;
me.device.windows.window[id ~ '-' ~ (selected -2)]
.setText(val);
if (val == '00:00:00' and direction == -1)
me.device.data.TMRrevert = 1;
}, me);
me.device.data.TMRtimer.start();
action = 'STOP?';
}
elsif (action == 'STOP?') {
me.device.data.TMRtimer.stop();
action = 'RESET?';
}
elsif (action == 'RESET?') {
action = 'START?';
if ((me.device.windows.state[id].objects[selected - 1].text
==
me.device.windows.state[id].objects[selected - 1].choices[1])
and !me.device.data.TMRrevert)
var val = me.device.data.TMRreset;
else
var val = '00:00:00';
me.device.windows.state[id].objects[selected - 2].text = val;
me.device.windows.window[id ~ '-' ~ (selected -2)]
.setText(val);
}
me.device.windows.window[me.device.windows.selected]
.setText(action);
me.device.windows.state[id].objects[selected].text = action;
};
me.device.windows.draw(
'TMR/REF',
{x: 720, y: 535, w: 300, l: 5, sep: 3},
[ # objects infos
{text: 'REFERENCES', type: 'title'},
{type: 'separator'},
{text: 'TIMER', type: 'normal'},
{text: '00:00:00', type: 'selected|time', },
{text: ' UP >', type: 'editable', choices: [' UP >', '<DOWN ']},
{text: 'START?', type: 'editable|end-of-line', callback: func (id, selected) GenericTimer(id, selected)},
{type: 'separator'},
{text: sprintf('Vx %3iKT', alerts.getNode('Vx').getValue()), type: 'normal', scrollgroup:0},
{text: me.device.data['Vx-visible'] ? ' ON >' : '< OFF ', type: 'editable|immediate|end-of-line', choices: [' ON >', '< OFF '], scrollgroup:0, callback: func (id, selected) Vspeed_visiblity(id, selected, 'Vx')},
{text: sprintf('Vy %3iKT', alerts.getNode('Vy').getValue()), type: 'normal', scrollgroup:1},
{text: me.device.data['Vy-visible'] ? ' ON >' : '< OFF ', type: 'editable|immediate|end-of-line', choices: [' ON >', '< OFF '], scrollgroup:1, callback: func (id, selected) Vspeed_visiblity(id, selected, 'Vy')},
{text: sprintf('Vr %3iKT', alerts.getNode('Vr').getValue()), type: 'normal', scrollgroup:2},
{text: me.device.data['Vr-visible'] ? ' ON >' : '< OFF ', type: 'editable|immediate|end-of-line', choices: [' ON >', '< OFF '], scrollgroup:2, callback: func (id, selected) Vspeed_visiblity(id, selected, 'Vr')},
{text: sprintf('Vglide %3iKT', alerts.getNode('Vglide').getValue()), type: 'normal', scrollgroup:3},
{text: me.device.data['Vglide-visible'] ? ' ON >' : '< OFF ', type: 'editable|immediate|end-of-line', choices: [' ON >', '< OFF '], scrollgroup:3, callback: func (id, selected) Vspeed_visiblity(id, selected, 'Vglide')},
{type: 'separator'},
{text: 'MINIMUMS', type: 'normal'},
{text: ' OFF >', type: 'editable', choices: [' OFF >', '< BARO >','<TEMP COMP'], callback: func},
{text: ' 1000FT', type: 'editable', format: '% 5iFT', factor: 100, callback: func},
],
{ # scrolling info, see menu.nas
lines : 3,
columns : 2,
}
);
me.device.knobs.FmsInner = me.device.knobs.MenuSettings;
me.device.knobs.FmsOuter = me.device.knobs.NavigateMenu;
me.device.buttons.ENT = me.device.buttons.ValidateTMRREF;
me.device.buttons.FMS = me.device.buttons.ValidateTMRREF;
me.device.buttons.CLR = me.device.buttons.ClearTMRREF;
}
else {
me.device.buttons.ClearTMRREF();
}
},
},
MFD : {
ENGINE: {
FUEL: {
UNDO: func {
pop(me.path);
me.device.display.updateSoftKeys();
},
ENTER: func {
pop(me.path);
me.device.display.updateSoftKeys();
},
},
ENGINE: func {
me.path = [];
me.device.display.updateSoftKeys();
},
},
CHKLIST : {
EXIT: func {
me.path = [];
me.device.display.updateSoftKeys();
},
},
MAP: {
TRAFFIC: func {
me.device.data.tcas = ! me.device.data.tcas;
if (me.device.data.tcas)
me.colored['MAPTRAFFIC'] = me.device.data.tcas;
else
delete(me.colored, 'MAPTRAFFIC');
me.device.display.updateSoftKeys();
me.device.map.layers.tcas.setVisible(me.device.data.tcas);
},
},
},
},
};
var keyMap = {
# softkeys map for PFD and MFD {{{1
# PFD {{{2
PFD : {
first : 1,
texts : ['INSET', 'SENSOR', 'PFD', 'OBS', 'CDI', 'DME', 'XPDR', 'IDENT', 'TMR/REF', 'NRST' ],
INSET : {
texts : ['OFF', 'DCLTR', 'WXLGND', 'TRAFFIC', 'TOPO', 'TERRAIN', 'STRMSCP', 'NEXRAD-C', 'XM LTNG', 'METAR'],
},
SENSOR : {
first : 2,
texts : [ 'ADC1', 'ADC2', '', 'AHRS1', 'AHRS2'],
},
PFD : {
texts : [ 'SYN VIS', 'DFLTS', 'AOA/WIND', 'DME', 'BRG1', 'HSI FMT', 'BRG2', '', 'ALT UNIT', 'STD BARO' ],
'SYN VIS' : {
texts : [ 'PATHWAY', 'SYN TERR', 'HR2NHDG', 'APTSIGNS', 'FPM'],
},
'AOA/WIND' : {
first : 4,
texts : ['AOA', 'WIND'],
AOA : {
first : 5,
texts : ['AOA ON', 'AOA AUTO'],
},
WIND : {
first : 2,
texts : ['OPTN1', 'OPTN2', '', 'OFF'],
},
},
'HSI FMT' : {
first : 6,
texts : ['360 HSI', 'ARC HSI'],
},
'ALT UNIT' : {
first : 5,
texts : ['METERS', '', 'IN', 'HPA'],
},
},
XPDR : {
first : 2,
texts : ['STBY', 'ON', 'ALT', '', 'VFR', 'CODE', 'IDENT'],
CODE : {
texts : ['0', '1', '2', '3', '4', '5', '6', '7', 'IDENT', 'BKSP'],
},
},
},
#}}}2
# MFD {{{2
MFD : {
texts : ['ENGINE', '', 'MAP', '', '', '', '', '', '', 'DCLTR', 'SHW CHRT', 'CHKLIST'],
MAP : {
texts: ['TRAFFIC', 'PROFILE', 'TOPO', 'TERRAIN', 'AIRWAYS', 'STRMSCP','NEXRAD-C', 'XM LTNG', 'METAR', 'LEGEND', 'BACK'],
},
CHKLIST : {
texts : ['ENGINE', '', '', '', '', 'DONE', '', '', '', '', 'EXIT', 'EMERGCY'],
},
ENGINE : {
texts : ['ENGINE', 'ANTI-ICE', '', 'DCLTR', '', 'ASSIST', '', '', '', '', 'FUEL'],
'ANTI-ICE' : {
texts : ['LEFT', 'AUTO', 'RIGHT', '', '', '', '', '', '', '', '', 'BACK'],
},
FUEL : {
first : 1,
texts : ['FULL', 'TABS', '', '', '', '', '', '', '', 'UNDO', 'ENTER'],
},
},
},
#}}}2
};
if (data['stall-aoa'] == 9999)
keyMap.PFD.PFD['AOA/WIND'].texts = ['', 'WIND'];
#}}}1