Howto talk:Implementing a simple GCA system

From FlightGear wiki
Revision as of 14:22, 23 September 2017 by Hooray (talk | contribs) (https://forum.flightgear.org/viewtopic.php?f=30&t=32882&p=319259#p319259)
Jump to navigation Jump to search
###
#
# UI code starts here
#
###

var configureGCA = func( gcaObject ) {

var (width,height) = (320,450);
var title = 'GCA Dialog ';

var window = canvas.Window.new([width,height],"dialog").set('title',title);

window.del = func()
{
print("Cleaning up window:",title,"\n");
call(canvas.Window.del, [], me);
};

# adding a canvas to the new window and setting up background colors/transparency
var myCanvas = window.createCanvas().set("background", canvas.style.getColor("bg_color"));

# creating the top-level/root group which will contain all other elements/group
var root = myCanvas.createGroup();

# create a new layout
var myLayout = canvas.VBoxLayout.new();
# assign it to the Canvas
myCanvas.setLayout(myLayout);

var setupWidgetTooltip = func(widget, tooltip) {
 widget._view._root.addEventListener("mouseover", func gui.popupTip(tooltip) );
} # setupWidgetTooltip


var setupLabeledInput = func(root, layout, input) {


var label = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0}); 
var unit_suffix = sprintf(" (%s):", input.unit);
label.setText(input.text~unit_suffix);
layout.addItem(label);

var field = canvas.gui.widgets.LineEdit.new(root, canvas.style, {});
layout.addItem(field);
field.setText(input.default_value);

if (input.focus)
field.setFocus();

setupWidgetTooltip(widget:field, tooltip: input.tooltip);

var el = field._view._root;
el.addEventListener("keypress", func (e) {

# colorize valid/invalid inputs
var color = (validationHelpers[input.validate]( field.text() ) == 0) ? [0,1,0] : [1,0,0];
field._view._root.setColorFill(color);

});
    

return field; # return to caller
} # setupLabeledInput()

var validationHelpers = {

_internalState: {},

'AircraftRoot': func(input) {
var root = props.getNode(input);
if (root == nil) return 1; # error

var required_props = ['altitude-ft', 'longitude-deg', 'latitude-deg'];

foreach(var rp; required_props) {
 if (root.getNode(rp) == nil) return STATUS.FAILURE;
} # foreach

return STATUS.SUCCESS; # valid root node
},

'Airport': func(input) {
var match = airportinfo(input);
if (match == nil or typeof(match) != 'ghost') return STATUS.FAILURE; 
validationHelpers._internalState.airport = match; 
return STATUS.SUCCESS;

},

'Runway': func(input) {

var runways = validationHelpers._internalState.airport.runways;
if (typeof(runways)!="hash" or !size(keys(runways))) return STATUS.FAILURE;
if (runways[input] == nil) return STATUS.FAILURE;

return STATUS.SUCCESS;
},

'FinalApproach': func(input) {
return 0;
},

'Glidepath': func(input) {
return 0;
},

'SafetySlope': func(input) {
return 0;
}, 

'DecisionHeight': func(input) {
return 0;
},

'TouchdownOffset': func(input) {
return 0;
}, 

'TransmissionInterval': func(input) {
return 0;
}, 

'TransmissionProperty': func(input) {
return 0;
}, 

}; # validationHelpers;


var inputs = [
{text: 'Position root', default_value:'/position', focus:1, callback:GCAController.setPosition, tooltip:'property path to position node', validate: 'AircraftRoot', convert:nil, unit: 'property path'},
{text: 'Airport', default_value:'KSFO', focus:0, callback:GCAController.setAirport, tooltip:'ICAO ID, e.g. KSFO', validate: 'Airport', convert:nil, unit:'ICAO'},
{text: 'Runway', default_value:'28R', focus:0, callback:GCAController.setRunway, tooltip:'runway identifier, e.g. 28L', validate: 'Runway', convert:nil, unit:'rwy'},
{text: 'Final Approach', default_value:'10.00', focus:0, callback:GCAController.setFinalApproach, tooltip:'length of final approach leg', validate: 'FinalApproach', convert:nil, unit:'nm'},

{text: 'Glidepath', default_value:'3.00', focus:0, callback:GCAController.setGlidepath, tooltip:'glidepath in degrees, e.g. 3', validate: 'Glidepath', convert:nil, unit:'degrees'},
{text: 'Safety Slope', default_value:'2.00', focus:0, callback:GCAController.setSafetySlope, tooltip:'safety slope in degrees', validate: 'SafetySlope', convert:nil, unit:'degrees'},

{text: 'Decision Height', default_value:'200.00', focus:0, callback:GCAController.setDecisionHeight, tooltip:'decision height (vertical offset)', validate: 'DecisionHeight', convert:nil, unit:'ft'},
{text: 'Touchdown Offset', default_value:'0.00', focus:0, callback:GCAController.setTouchdownOffset, tooltip:'touchdown offset', validate: 'TouchdownOffset', convert:nil, unit:'m'},
{text: 'Transmission interval', default_value:'5.00', focus:0, callback:GCAController.setTransmissionInterval, tooltip:'Controller/timer resolution', validate: 'TransmissionInterval', convert:nil, unit:'secs'},
{text: 'Transmission channel', default_value:'/sim/messages/approach', focus:0, callback:GCAController.setTransmissionChannel, tooltip:'property to use for transmissions. For example: /sim/multiplay/chat or /sim/sound/voices/approach', validate: 'TransmissionProperty', convert:nil, unit:'property'},
]; # input fields

foreach(var input; inputs) {
 # TODO: pass input hash 
 input.widget = setupLabeledInput(root, myLayout, input);
}

var validateFields = func() {
foreach(var f; inputs) {

 var result = validationHelpers[f.validate] ( f.widget.text() );
 if (result == STATUS.FAILURE) {

canvas.MessageBox.critical(
  "Validation error",
  "Error validating "~f.text,
  cb = nil,
  buttons = canvas.MessageBox.Ok
); # MessageBox


 } # error handling
} # foreach
return STATUS.SUCCESS; # all validations passed
} # validateFields()


###
# global stuff
#

var gcaRunning = 0;
# var myGCA = nil;

var toggleFields = func(enabled) {
foreach(var i; inputs) {
 i.widget.setEnabled(enabled);
}
}


var buildCGA = func() {

# create new object
# myGCA = GCAController.new();

foreach(var field; inputs) {
var value = field.widget.text();
# var converted_value = typeof(field.convert != nil) ? field.convert(value) : value;
# call vector
print(field.callback);
# http://wiki.flightgear.org/Nasal_library#call.28.29
call(GCAController[field.callback], [value], gcaObject );

}

# myGCA.start();
gcaObject.start();

}

var toggleGCA = func() {

if (gcaRunning) {
gcaObject.stop();
gcaRunning = 0;
toggleFields(!gcaRunning); # set editable
return;
}

if (!gcaRunning and validateFields()==0) {
gcaRunning = 1;
toggleFields(!gcaRunning); # set readonly
buildCGA();
return;
}

} # toggleGCA()


var button = canvas.gui.widgets.Button.new(root, canvas.style, {})
	.setText("Start/Stop")
	.setFixedSize(75, 25)
	.listen("clicked", toggleGCA);

setupWidgetTooltip(widget:button, tooltip: "toggle GCA on/off");
myLayout.addItem(button);

}; # configureGCA();