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.display.MapTiles.setVisible(0); me.device.display.MapNavaids.setVisible(0); me.device.display.screenElements['PFD-Map-bg'].hide(); }, hook : func { me.device.display.screenElements['PFD-Map-bg'].show(); me.device.display.MapTiles.setVisible(1); me.device.display.MapNavaids.setVisible(1); me.device.display.MapTiles.update(); me.device.display.MapNavaids.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 >', '' : '< 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 >','