From 6c55b9803502a646226bb6e036dcc32a271354b9 Mon Sep 17 00:00:00 2001 From: fly Date: Tue, 14 Apr 2020 19:05:54 +0000 Subject: [PATCH] Added maintainance dialog Signed-off-by: fly --- A320-main.xml | 8 + gui/dialogs/maintainance.nas | 648 +++++++++++++++++++++++++++++++++++ gui/dialogs/maintainance.svg | 366 ++++++++++++++++++++ 3 files changed, 1022 insertions(+) create mode 100644 gui/dialogs/maintainance.nas create mode 100644 gui/dialogs/maintainance.svg diff --git a/A320-main.xml b/A320-main.xml index 349962d0..d40f72f8 100644 --- a/A320-main.xml +++ b/A320-main.xml @@ -340,6 +340,13 @@ + + + + nasal + + + @@ -3990,6 +3997,7 @@ Aircraft/A320-family/gui/dialogs/refuel.nas Aircraft/A320-family/gui/dialogs/rmp.nas + Aircraft/A320-family/gui/dialogs/maintainance.nas Aircraft/A320-family/Nasal/Systems/save.nas diff --git a/gui/dialogs/maintainance.nas b/gui/dialogs/maintainance.nas new file mode 100644 index 00000000..2bfb77dc --- /dev/null +++ b/gui/dialogs/maintainance.nas @@ -0,0 +1,648 @@ + +#Font Mapper +var font_mapper = func(family, weight) { + return "Orbitron/Orbitron-Bold.ttf"; +}; + + + +var clamp = func(value,min=0.0,max=0.0){ + if(value < min) {value = min;} + if(value > max) {value = max;} + return value; +} + +var MyWindow = { + # Constructor + # + # @param size ([width, height]) + new: func(size, type = nil, id = nil) + { + var ghost = canvas._newWindowGhost(id); + var m = { + parents: [MyWindow, canvas.PropertyElement, ghost], + _node: props.wrapNode(ghost._node_ghost) + }; + + m.setInt("size[0]", size[0]); + m.setInt("size[1]", size[1]); + + # TODO better default position + m.move(0,0); + + # arg = [child, listener_node, mode, is_child_event] + setlistener(m._node, func m._propCallback(arg[0], arg[2]), 0, 2); + if( type ) + m.set("type", type); + + m._isOpen = 1; + return m; + }, + # Destructor + del: func + { + me._node.remove(); + me._node = nil; + + if( me["_canvas"] != nil ) + { + me._canvas.del(); + me._canvas = nil; + } + me._isOpen = 0; + }, + # Create the canvas to be used for this Window + # + # @return The new canvas + createCanvas: func() + { + var size = [ + me.get("size[0]"), + me.get("size[1]") + ]; + + me._canvas = canvas.new({ + size: [2 * size[0], 2 * size[1]], + view: size, + placement: { + type: "window", + id: me.get("id") + } + }); + + me._canvas.addEventListener("mousedown", func me.raise()); + return me._canvas; + }, + # Set an existing canvas to be used for this Window + setCanvas: func(canvas_) + { + if( !isa(canvas_, canvas.Canvas) ) + return debug.warn("Not a canvas.Canvas"); + + canvas_.addPlacement({type: "window", index: me._node.getIndex()}); + me['_canvas'] = canvas_; + }, + # Get the displayed canvas + getCanvas: func() + { + return me['_canvas']; + }, + getCanvasDecoration: func() + { + return canvas.wrapCanvas(me._getCanvasDecoration()); + }, + setPosition: func(x, y) + { + me.setInt("tf/t[0]", x); + me.setInt("tf/t[1]", y); + }, + move: func(x, y) + { + me.setInt("tf/t[0]", me.get("tf/t[0]", 10) + x); + me.setInt("tf/t[1]", me.get("tf/t[1]", 30) + y); + }, + # Raise to top of window stack + raise: func() + { + # on writing the z-index the window always is moved to the top of all other + # windows with the same z-index. + me.setInt("z-index", me.get("z-index", 0)); + }, +# private: + _propCallback: func(child, mode) + { + if( !me._node.equals(child.getParent()) ) + return; + var name = child.getName(); + + # support for CSS like position: absolute; with right and/or bottom margin + if( name == "right" ) + me._handlePositionAbsolute(child, mode, name, 0); + else if( name == "bottom" ) + me._handlePositionAbsolute(child, mode, name, 1); + + # update decoration on type change + else if( name == "type" ) + { + if( mode == 0 ) + settimer(func me._updateDecoration(), 0); + } + }, + _handlePositionAbsolute: func(child, mode, name, index) + { + # mode + # -1 child removed + # 0 value changed + # 1 child added + + if( mode == 0 ) + me._updatePos(index, name); + else if( mode == 1 ) + me["_listener_" ~ name] = [ + setlistener + ( + "/sim/gui/canvas/size[" ~ index ~ "]", + func me._updatePos(index, name) + ), + setlistener + ( + me._node.getNode("size[" ~ index ~ "]"), + func me._updatePos(index, name) + ) + ]; + else if( mode == -1 ) + for(var i = 0; i < 2; i += 1) + removelistener(me["_listener_" ~ name][i]); + }, + _updatePos: func(index, name) + { + me.setInt + ( + "tf/t[" ~ index ~ "]", + getprop("/sim/gui/canvas/size[" ~ index ~ "]") + - me.get(name) + - me.get("size[" ~ index ~ "]") + ); + }, + _onClose : func(){ + me.del(); + }, + _updateDecoration: func() + { + var border_radius = 9; + me.set("decoration-border", "25 1 1"); + me.set("shadow-inset", int((1 - math.cos(45 * D2R)) * border_radius + 0.5)); + me.set("shadow-radius", 5); + me.setBool("update", 1); + + var canvas_deco = me.getCanvasDecoration(); + canvas_deco.addEventListener("mousedown", func me.raise()); + canvas_deco.set("blend-source-rgb", "src-alpha"); + canvas_deco.set("blend-destination-rgb", "one-minus-src-alpha"); + canvas_deco.set("blend-source-alpha", "one"); + canvas_deco.set("blend-destination-alpha", "one"); + + var group_deco = canvas_deco.getGroup("decoration"); + var title_bar = group_deco.createChild("group", "title_bar"); + title_bar + .rect( 0, 0, + me.get("size[0]"), + me.get("size[1]"), #25, + {"border-top-radius": border_radius} ) + .setColorFill(0.25,0.24,0.22) + .setStrokeLineWidth(0); + + var style_dir = "gui/styles/AmbianceClassic/decoration/"; + + # close icon + var x = 10; + var y = 3; + var w = 19; + var h = 19; + var ico = title_bar.createChild("image", "icon-close") + .set("file", style_dir ~ "close_focused_normal.png") + .setTranslation(x,y); + ico.addEventListener("click", func me._onClose()); + ico.addEventListener("mouseover", func ico.set("file", style_dir ~ "close_focused_prelight.png")); + ico.addEventListener("mousedown", func ico.set("file", style_dir ~ "close_focused_pressed.png")); + ico.addEventListener("mouseout", func ico.set("file", style_dir ~ "close_focused_normal.png")); + + # title + me._title = title_bar.createChild("text", "title") + .set("alignment", "left-center") + .set("character-size", 14) + .set("font", "Orbitron/Orbitron-Bold.ttf") + .setTranslation( int(x + 1.5 * w + 0.5), + int(y + 0.5 * h + 0.5) ); + + var title = me.get("title", "Canvas Dialog"); + me._node.getNode("title", 1).alias(me._title._node.getPath() ~ "/text"); + me.set("title", title); + + title_bar.addEventListener("drag", func(e) { + if( !ico.equals(e.target) ) + me.move(e.deltaX, e.deltaY); + }); + } +}; + +var COLOR = {}; +COLOR["Red"] = "rgb(244,28,33)"; +COLOR["Black"] = "#000000"; + + +var SvgWidget = { + new: func(dialog,canvasGroup,name){ + var m = {parents:[SvgWidget]}; + m._class = "SvgWidget"; + m._dialog = dialog; + m._listeners = []; + m._name = name; + m._group = canvasGroup; + return m; + }, + removeListeners :func(){ + foreach(l;me._listeners){ + removelistener(l); + } + me._listeners = []; + }, + setListeners : func(instance) { + + }, + init : func(instance=me){ + + }, + deinit : func(){ + me.removeListeners(); + }, + +}; + +var BatteryWidget = { + new: func(dialog,canvasGroup,name){ + var m = {parents:[BatteryWidget,SvgWidget.new(dialog,canvasGroup,name)]}; + m._class = "BatteryWidget"; + m._name = name; + + if(m._name=="Front"){ + m._nLevelPct = props.globals.initNode("/systems/electrical/battery-charge-percent-front",0.0,"DOUBLE"); + }else{ + m._nLevelPct = props.globals.initNode("/systems/electrical/battery-charge-percent-back",0.0,"DOUBLE"); + } + + m._fraction = m._nLevelPct.getValue(); + m._capacity = 10.5; #10.5 kWh (per pack) + + m._cFrame = m._group.getElementById(m._name~"_Frame"); + m._cFrameV = m._group.getElementById(m._name~"_Frame_Vis"); + m._cLevel = m._group.getElementById(m._name~"_Charge_Level"); + m._cDataLevel = m._group.getElementById(m._name~"_Data_Level"); + m._cDataAbs = m._group.getElementById(m._name~"_Data_Abs"); + + m._cDataLevel.setText(sprintf("%3d",m._fraction*100)~" %"); + m._cDataAbs.setText(sprintf("%3.1f",m._fraction*m._capacity)~" kWh"); + + m._left = m._cFrame.get("coord[0]"); + m._right = m._cFrame.get("coord[2]"); + m._width = m._right - m._left; + return m; + }, + setListeners : func(instance) { + append(me._listeners, setlistener(me._nLevelPct,func(n){me._onChargeLevelChange(n);},1,0) ); + + me._cFrameV.addEventListener("drag",func(e){me._onChargeInputChange(e);}); + me._cLevel.addEventListener("drag",func(e){me._onChargeInputChange(e);}); + me._cFrameV.addEventListener("wheel",func(e){me._onChargeInputChange(e);}); + me._cLevel.addEventListener("wheel",func(e){me._onChargeInputChange(e);}); + }, + init : func(instance=me){ + me.setListeners(instance); + }, + deinit : func(){ + me.removeListeners(); + }, + _onChargeLevelChange : func(n){ + me._fraction = n.getValue(); + + me._cDataLevel.setText(sprintf("%3d",me._fraction*100)~" %"); + me._cDataAbs.setText(sprintf("%3.1f",me._fraction*me._capacity)~" kWh"); + + me._cLevel.set("coord[2]", me._left + (me._width * me._fraction)); + + }, + _onChargeInputChange : func(e){ + var newFraction = 0; + if(e.type == "wheel"){ + newFraction = me._fraction + (e.deltaY/me._width); + }else{ + newFraction = me._fraction + (e.deltaX/me._width); + } + newFraction = clamp(newFraction,0.0,1.0); + me._nLevelPct.setValue(newFraction); + + }, +}; + + +var WeightWidget = { + new: func(dialog,canvasGroup,name,widgets){ + var m = {parents:[WeightWidget,SvgWidget.new(dialog,canvasGroup,name)]}; + m._class = "WeightWidget"; + m._widget = {}; + + foreach(w;keys(widgets)){ + if(widgets[w] != nil){ + if(widgets[w]._class == "PayloadWidget"){ + m._widget[w] = widgets[w]; + } + } + } + + m._cWeightGrossKg = m._group.getElementById("Weight_Gross_Kg"); + m._cWeightGrossLbs = m._group.getElementById("Weight_Gross_Lbs"); + m._cWeightWarning = m._group.getElementById("Weight_Warning"); + m._cWeightPilotKg = m._group.getElementById("Weight_Pilot_Kg"); + m._cWeightPilotLbs = m._group.getElementById("Weight_Pilot_Lbs"); + m._cWeightCopilotKg = m._group.getElementById("Weight_Copilot_Kg"); + m._cWeightCopilotLbs = m._group.getElementById("Weight_Copilot_Lbs"); + m._cWeightBaggageKg = m._group.getElementById("Weight_Baggage_Kg"); + m._cWeightBaggageLbs = m._group.getElementById("Weight_Baggage_Lbs"); + + m._cCenterGravityX = m._group.getElementById("Center_Gravity_X"); + m._cCenterGravityXWarning = m._group.getElementById("Center_Gravity_Warn"); + + m._nCGx = props.globals.initNode("/fdm/jsbsim/inertia/cg-x-mm-rp",0.0,"DOUBLE"); #calculated CG distance to reference point, set via system in Systems/dialogs.xml + m._nGross = props.globals.initNode("/fdm/jsbsim/inertia/weight-lbs"); + m._nPilot = props.globals.initNode("/payload/weight[0]/weight-lb"); + m._nCopilot = props.globals.initNode("/payload/weight[1]/weight-lb"); + m._nBaggage = props.globals.initNode("/payload/weight[2]/weight-lb"); + m._nTakeoff = props.globals.initNode("/limits/mtow-lbs"); + + + m._cgX = 0; + m._pilot = 0; + m._copilot = 0; + m._baggage = 0; + m._gross = 0; + m._takeoff = 0; + + m._takeoff = m._nTakeoff.getValue(); + + return m; + }, + setListeners : func(instance) { + append(me._listeners, setlistener(maintainance._nGrossWeight,func(n){me._onGrossWeightChange(n);},1,0) ); + append(me._listeners, setlistener(me._nCGx,func(n){me._onCGChange(n);},1,0) ); + + }, + init : func(instance=me){ + me.setListeners(instance); + }, + deinit : func(){ + me.removeListeners(); + }, + _onGrossWeightChange : func(n){ + + me._gross = me._nGross.getValue(); + me._cWeightGrossKg.setText(sprintf("%5d",me._gross/KG2LB)); + me._cWeightGrossLbs.setText(sprintf("%4d",me._gross)); + + me._pilot = me._nPilot.getValue(); + me._cWeightPilotKg.setText(sprintf("%5d",me._pilot/KG2LB)); + me._cWeightPilotLbs.setText(sprintf("%4d",me._pilot)); + + me._copilot = me._nCopilot.getValue(); + me._cWeightCopilotKg.setText(sprintf("%5d",me._copilot/KG2LB)); + me._cWeightCopilotLbs.setText(sprintf("%4d",me._copilot)); + + me._baggage = me._nBaggage.getValue(); + me._cWeightBaggageKg.setText(sprintf("%5d",me._baggage/KG2LB)); + me._cWeightBaggageLbs.setText(sprintf("%4d",me._baggage)); + + + if (me._gross > me._takeoff){ + me._cWeightWarning.show(); + me._cWeightGrossKg.setColor(COLOR["Red"]); + me._cWeightGrossLbs.setColor(COLOR["Red"]); + }else{ + me._cWeightWarning.hide(); + me._cWeightGrossKg.setColor(COLOR["Black"]); + me._cWeightGrossLbs.setColor(COLOR["Black"]); + } + + + }, + _onCGChange : func(n){ + + me._cgX = me._nCGx.getValue(); + + me._cCenterGravityX.setTranslation((me._cgX-200),0); + + if(me._cgX>195 and me._cgX<368){ + me._cCenterGravityXWarning.hide(); + }else{ + me._cCenterGravityXWarning.show(); + } + + }, + + + +}; + +var PayloadWidget = { + new: func(dialog,canvasGroup,name,index){ + var m = {parents:[PayloadWidget,SvgWidget.new(dialog,canvasGroup,name)]}; + m._class = "PayloadWidget"; + m._index = index; + + #debug.dump(m._listCategoryKeys); + + m._nRoot = props.globals.getNode("/payload/weight["~m._index~"]"); + m._nLable = m._nRoot.initNode("name","","STRING"); + + ### HACK : listener on /payload/weight[0]/weight-lb not working + ### two props one for fdm(weight-lb) one for dialog(nt-weight-lb) listener + m._nWeightFdm = m._nRoot.initNode("weight-lb",0.0,"DOUBLE"); + m._weight = m._nWeightFdm.getValue(); # lbs + m._nWeight = m._nRoot.initNode("nt-weight-lb",m._weight,"DOUBLE"); + + m._nCapacity = m._nRoot.initNode("max-lb",0.0,"DOUBLE"); + + m._capacity = m._nCapacity.getValue(); + m._fraction = m._weight / m._capacity; + + m._cFrame = m._group.getElementById(m._name~"_Frame"); + m._cFrame_Pick = m._group.getElementById(m._name~"_Frame_Pick"); + m._cLevel = m._group.getElementById(m._name~"_Level"); + m._cLBS = m._group.getElementById(m._name~"_Lbs"); + m._cKG = m._group.getElementById(m._name~"_Kg"); + + m._cLBS.setText(sprintf("%3.0f",m._weight)); + m._cKG.setText(sprintf("%3.0f",m._weight)); + + + m._left = m._cFrame.get("coord[0]"); + m._right = m._cFrame.get("coord[2]"); + m._width = m._right - m._left; + + return m; + }, + setListeners : func(instance) { + ### FIXME : use one property remove the HACK + append(me._listeners, setlistener(me._nWeight,func(n){me._onWeightChange(n);},1,0) ); + + me._cFrame_Pick.addEventListener("drag",func(e){me._onInputChange(e);}); + me._cLevel.addEventListener("drag",func(e){me._onInputChange(e);}); + me._cFrame_Pick.addEventListener("wheel",func(e){me._onInputChange(e);}); + me._cLevel.addEventListener("wheel",func(e){me._onInputChange(e);}); + + + + }, + init : func(instance=me){ + me.setListeners(instance); + }, + deinit : func(){ + me.removeListeners(); + }, + setWeight : func(weight){ + me._weight = weight; + + ### HACK : set two props + me._nWeight.setValue(me._weight); + me._nWeightFdm.setValue(me._weight); + + }, + _onWeightChange : func(n){ + me._weight = me._nWeight.getValue(); + #print("PayloadWidget._onWeightChange() ... "~me._weight); + + me._fraction = me._weight / me._capacity; + + me._cLBS.setText(sprintf("%3.0f",me._weight)); + me._cKG.setText(sprintf("%3.0f",me._weight/KG2LB)); + + me._cLevel.set("coord[2]", me._left + (me._width * me._fraction)); + }, + _onInputChange : func(e){ + var newFraction =0; + if(e.type == "wheel"){ + newFraction = me._fraction + (e.deltaY/me._width); + }else{ + newFraction = me._fraction + (e.deltaX/me._width); + } + newFraction = clamp(newFraction,0.0,1.0); + me._weight = me._capacity * newFraction; + + me.setWeight(me._weight); + + }, +}; + + +var maintainanceClass = { + new : func(){ + var m = {parents:[maintainanceClass]}; + m._nRoot = props.globals.initNode("/alphaelectro/dialog/config"); + + m._nGrossWeight = props.globals.initNode("/fdm/jsbsim/inertia/nt-weight-lbs",0.0,"DOUBLE"); #listener on weight-lbs not possible, set via system in Systems/fuelpayload.xml + + m._name = "Maintainance"; + m._title = "Maintainance"; + m._fdmdata = { + grosswgt : "/fdm/jsbsim/inertia/weight-lbs", + payload : "/payload", + cg : "/fdm/jsbsim/inertia/cg-x-in", + }; + + + m._listeners = []; + m._dlg = nil; + m._canvas = nil; + + m._isOpen = 0; + m._isNotInitialized = 1; + + m._widget = { + Front : nil, + Rear : nil, + Pilot : nil, + Copilot : nil, + Baggage : nil, + weight : nil, + }; + + + return m; + }, + toggle : func(){ + if(me._dlg != nil){ + if (me._dlg._isOpen){ + me.close(); + }else{ + me.open(); + } + }else{ + me.open(); + } + }, + close : func(){ + me._dlg.del(); + me._dlg = nil; + }, + removeListeners :func(){ + foreach(l;me._listeners){ + removelistener(l); + } + me._listeners = []; + }, + setListeners : func(instance) { + + + }, + _onClose : func(){ + me.removeListeners(); + me._dlg.del(); + + foreach(widget;keys(me._widget)){ + if(me._widget[widget] != nil){ + me._widget[widget].deinit(); + me._widget[widget] = nil; + } + } + + }, + open : func(){ + if(getprop("/gear/gear[1]/wow") == 1){ + me.openBAP(); + }else{ + screen.log.write("Maintainance dialog not available in air!"); + } + + }, + openBAP : func(){ + + + me._dlg = MyWindow.new([1024, 512], "dialog"); + me._dlg._onClose = func(){ + maintainance._onClose(); + } + me._dlg.set("title", "Maintainance"); + me._dlg.move(100,100); + + + me._canvas = me._dlg.createCanvas(); + me._canvas.set("background", "#c5c5c5ff"); + + me._group = me._canvas.createGroup(); + + canvas.parsesvg(me._group, "Aircraft/A320-family/gui/dialogs/maintainance.svg",{"font-mapper": font_mapper}); + + + + me._widget.Front = BatteryWidget.new(me,me._group,"Front"); + me._widget.Rear = BatteryWidget.new(me,me._group,"Rear"); + + me._widget.Pilot = PayloadWidget.new(me,me._group,"Pilot",0); + me._widget.Copilot = PayloadWidget.new(me,me._group,"Copilot",1); + me._widget.Baggage = PayloadWidget.new(me,me._group,"Baggage",2); + + me._widget.weight = WeightWidget.new(me,me._group,"Weight",me._widget); + + foreach(widget;keys(me._widget)){ + if(me._widget[widget] != nil){ + me._widget[widget].init(); + } + } + + + }, + _onNotifyChange : func(n){ + + }, + +}; + +var maintainance = maintainanceClass.new(); diff --git a/gui/dialogs/maintainance.svg b/gui/dialogs/maintainance.svg new file mode 100644 index 00000000..0f457360 --- /dev/null +++ b/gui/dialogs/maintainance.svg @@ -0,0 +1,366 @@ + + + + + + + + + + image/svg+xml + + + + + + + + A320 Maintainance + + + + Front Battery Pack + + + + SOC + 000 + 000 + + + + + + + + + + + + + + + + + + + + + + + + + +