diff --git a/A320-main.xml b/A320-main.xml index 78516fdd..4af259c7 100644 --- a/A320-main.xml +++ b/A320-main.xml @@ -647,6 +647,7 @@ + 0 diff --git a/Models/Instruments/IESI/IESI.nas b/Models/Instruments/IESI/IESI.nas index 85258f17..33076dcd 100644 --- a/Models/Instruments/IESI/IESI.nas +++ b/Models/Instruments/IESI/IESI.nas @@ -162,7 +162,7 @@ var canvas_IESI = { return; } - if (_IESITime + 90 >= notification.elapsed_seconds) { + if (_IESITime + 90 >= notification.elapsedTime) { me["IESI"].hide(); me["IESI_Init"].show(); return; @@ -191,7 +191,7 @@ var canvas_IESI = { # todo transient max 0.2s # todo 20W power consumption if (notification.attReset == 1) { - if (notification.iesiInit and _IESITime + 90 >= notification.elapsed_seconds) { + if (notification.iesiInit and _IESITime + 90 >= notification.elapsedTime) { _fast = 1; } else { _fast = 0; @@ -199,19 +199,19 @@ var canvas_IESI = { iesi_init.setBoolValue(0); } - if (notification.dcEss >= 25 or (notification.dcHot1 >= 25 and notification.airspeed >= 50 and notification.elapsed_seconds >= 5)) { + if (notification.dcEss >= 25 or (notification.dcHot1 >= 25 and notification.airspeed >= 50 and notification.elapsedTime >= 5)) { _showIESI = 1; if (notification.acconfig != 1 and notification.iesiInit != 1) { iesi_init.setBoolValue(1); if (_fast) { - _IESITime = notification.elapsed_seconds - 80; + _IESITime = notification.elapsedTime - 80; _fast = 0; } else { - _IESITime = notification.elapsed_seconds; + _IESITime = notification.elapsedTime; } } else if (notification.acconfig == 1 and notification.iesiInit != 1) { iesi_init.setBoolValue(1); - _IESITime = notification.elapsed_seconds - 87; + _IESITime = notification.elapsedTime - 87; } } else { _showIESI = 0; @@ -230,24 +230,24 @@ var IESIRecipient = { new: func(_ident) { - var IESIRecipient = emesary.Recipient.new(_ident); - IESIRecipient.MainScreen = nil; - IESIRecipient.Receive = func(notification) + var NewIESIRecipient = emesary.Recipient.new(_ident); + NewIESIRecipient.MainScreen = nil; + NewIESIRecipient.Receive = func(notification) { if (notification.NotificationType == "FrameNotification") { - if (IESIRecipient.MainScreen == nil) { - IESIRecipient.MainScreen = canvas_IESI.new("Aircraft/A320-family/Models/Instruments/IESI/res/iesi.svg", "A320 IESI"); + if (NewIESIRecipient.MainScreen == nil) { + NewIESIRecipient.MainScreen = canvas_IESI.new("Aircraft/A320-family/Models/Instruments/IESI/res/iesi.svg", "A320 IESI"); } #if (!math.mod(notifications.frameNotification.FrameCount,2)){ - IESIRecipient.MainScreen.update(notification); + NewIESIRecipient.MainScreen.update(notification); #} return emesary.Transmitter.ReceiptStatus_OK; } return emesary.Transmitter.ReceiptStatus_NotProcessed; }; - return IESIRecipient; + return NewIESIRecipient; }, }; diff --git a/Nasal/ECAM/ECAM-main.nas b/Nasal/ECAM/ECAM-main.nas index 535544b3..89a09b66 100644 --- a/Nasal/ECAM/ECAM-main.nas +++ b/Nasal/ECAM/ECAM-main.nas @@ -5,17 +5,12 @@ var ap_active = 0; var athr_active = 0; -var phase = 0; var aileron = 0; var elevator = 0; -var stateL = 0; -var stateR = 0; var engModeSel = 0; var APUMaster = 0; var APURPM = 0; -var elapsedSec = 0; var gearDown = 0; -var agl = 0; var apOffTime = 0; var athrOffTime = 0; var apWarnNode = 0; @@ -23,165 +18,128 @@ var athrWarnNode = 0; var engStrtTimeSw = 0; var engStrtTime = 0; var page = 0; -var apuLight = 0; -var bleedLight = 0; -var condLight = 0; -var doorLight = 0; -var elecLight = 0; -var engLight = 0; -var fctlLight = 0; -var fuelLight = 0; -var hydLight = 0; -var pressLight = 0; -var stsLight = 0; -var wheelLight = 0; -var clrLight = 0; var ECAM = { _cachePage: "", init: func() { setprop("/systems/gear/landing-gear-warning-light", 0); page = props.globals.initNode("/ECAM/Lower/page", "door", "STRING"); - apuLight = props.globals.initNode("/ECAM/Lower/light/apu", 0, "BOOL"); - bleedLight = props.globals.initNode("/ECAM/Lower/light/bleed", 0, "BOOL"); - condLight = props.globals.initNode("/ECAM/Lower/light/cond", 0, "BOOL"); - doorLight = props.globals.initNode("/ECAM/Lower/light/door", 0, "BOOL"); - elecLight = props.globals.initNode("/ECAM/Lower/light/elec", 0, "BOOL"); - engLight = props.globals.initNode("/ECAM/Lower/light/eng", 0, "BOOL"); - fctlLight = props.globals.initNode("/ECAM/Lower/light/fctl", 0, "BOOL"); - fuelLight = props.globals.initNode("/ECAM/Lower/light/fuel", 0, "BOOL"); - hydLight = props.globals.initNode("/ECAM/Lower/light/hyd", 0, "BOOL"); - pressLight = props.globals.initNode("/ECAM/Lower/light/press", 0, "BOOL"); - stsLight = props.globals.initNode("/ECAM/Lower/light/sts", 0, "BOOL"); - wheelLight = props.globals.initNode("/ECAM/Lower/light/wheel", 0, "BOOL"); - clrLight = props.globals.initNode("/ECAM/Lower/light/clr", 0, "BOOL"); - phase = props.globals.initNode("/ECAM/warning-phase", 0, "INT"); - apOffTime = props.globals.initNode("/ECAM/ap-off-time", 0, "INT"); - athrOffTime = props.globals.initNode("/ECAM/athr-off-time", 0, "INT"); + apOffTime = props.globals.initNode("/ECAM/warnings/ap-off-time", 0, "INT"); + athrOffTime = props.globals.initNode("/ECAM/warnings/athr-off-time", 0, "INT"); engStrtTimeSw = props.globals.initNode("/ECAM/engine-start-time-switch", 0, "BOOL"); engStrtTime = props.globals.initNode("/ECAM/engine-start-time", 0.0, "DOUBLE"); apWarnNode = props.globals.initNode("/it-autoflight/output/ap-warning", 0, "INT"); athrWarnNode = props.globals.initNode("/it-autoflight/output/athr-warning", 0, "INT"); me.reset(); }, - reset: func() { - setprop("ECAM/msg/line1", ""); - setprop("ECAM/msg/line2", ""); - setprop("ECAM/msg/line3", ""); - setprop("ECAM/msg/line4", ""); - setprop("ECAM/msg/line5", ""); - setprop("ECAM/msg/line6", ""); - setprop("ECAM/msg/line7", ""); - setprop("ECAM/msg/line8", ""); - setprop("ECAM/msg/linec1", "w"); - setprop("ECAM/msg/linec2", "w"); - setprop("ECAM/msg/linec3", "w"); - setprop("ECAM/msg/linec4", "w"); - setprop("ECAM/msg/linec5", "w"); - setprop("ECAM/msg/linec6", "w"); - setprop("ECAM/msg/linec7", "w"); - setprop("ECAM/msg/linec8", "w"); - setprop("ECAM/rightmsg/line1", ""); - setprop("ECAM/rightmsg/line2", ""); - setprop("ECAM/rightmsg/line3", ""); - setprop("ECAM/rightmsg/line4", ""); - setprop("ECAM/rightmsg/line5", ""); - setprop("ECAM/rightmsg/line6", ""); - setprop("ECAM/rightmsg/line7", ""); - setprop("ECAM/rightmsg/line8", ""); - setprop("ECAM/rightmsg/linec1", "w"); - setprop("ECAM/rightmsg/linec2", "w"); - setprop("ECAM/rightmsg/linec3", "w"); - setprop("ECAM/rightmsg/linec4", "w"); - setprop("ECAM/rightmsg/linec5", "w"); - setprop("ECAM/rightmsg/linec6", "w"); - setprop("ECAM/rightmsg/linec7", "w"); - setprop("ECAM/rightmsg/linec8", "w"); - - page.setValue("door"); - apuLight.setValue(0); - bleedLight.setValue(0); - condLight.setValue(0); - doorLight.setValue(0); - elecLight.setValue(0); - engLight.setValue(0); - fctlLight.setValue(0); - fuelLight.setValue(0); - hydLight.setValue(0); - pressLight.setValue(0); - stsLight.setValue(0); - wheelLight.setValue(0); - clrLight.setValue(0); - }, - loop: func() { - stateL = pts.Engines.Engine.state[0].getValue(); - stateR = pts.Engines.Engine.state[1].getValue(); - wow = pts.Gear.wow[0].getValue(); - elapsedTime = pts.Sim.Time.elapsedSec.getValue(); - - if (stateL != 3 or stateR != 3) { - if (engStrtTimeSw.getBoolValue()) { + update_items: [ + props.UpdateManager.FromHashList(["ap1","ap2","apWarn"], nil, func(val) { + if (val.apWarn == 2 and (val.ap1 or val.ap2)) { + apWarnNode.setValue(0); + ecam.lights[0].setBoolValue(0); + } + }), + props.UpdateManager.FromHashList(["athr","athrWarn"], nil, func(val) { + if (val.athrWarn == 2 and val.athr) { + athrWarnNode.setValue(0); + } + }), + props.UpdateManager.FromHashList(["engine1State","engine2State","gear0Wow"], nil, func(val) { + if (val.engine1State != 3 or val.engine2State != 3) { engStrtTimeSw.setBoolValue(0); engStrtTime.setValue(0); - } - } else if (stateL == 3 and stateR == 3 and wow == 1) { - if (!engStrtTimeSw.getBoolValue()) { - engStrtTime.setValue(elapsedTime); + } else if (val.engine1State == 3 and val.engine2State == 3 and val.gear0Wow) { + engStrtTime.setValue(val.elapsedTime); engStrtTimeSw.setBoolValue(1); - } - } else if (wow == 1) { - if (engStrtTimeSw.getBoolValue()) { + } else if (val.gear0Wow) { engStrtTimeSw.setBoolValue(0); } + }), + ], + lights: { + "apu": props.globals.initNode("/ECAM/Lower/light/apu", 0, "BOOL"), + "bleed": props.globals.initNode("/ECAM/Lower/light/bleed", 0, "BOOL"), + "cond": props.globals.initNode("/ECAM/Lower/light/cond", 0, "BOOL"), + "door": props.globals.initNode("/ECAM/Lower/light/door", 0, "BOOL"), + "elec": props.globals.initNode("/ECAM/Lower/light/elec", 0, "BOOL"), + "eng": props.globals.initNode("/ECAM/Lower/light/eng", 0, "BOOL"), + "fctl": props.globals.initNode("/ECAM/Lower/light/fctl", 0, "BOOL"), + "fuel": props.globals.initNode("/ECAM/Lower/light/fuel", 0, "BOOL"), + "hyd": props.globals.initNode("/ECAM/Lower/light/hyd", 0, "BOOL"), + "press": props.globals.initNode("/ECAM/Lower/light/press", 0, "BOOL"), + "sts": props.globals.initNode("/ECAM/Lower/light/sts", 0, "BOOL"), + "wheel": props.globals.initNode("/ECAM/Lower/light/wheel", 0, "BOOL"), + "clr": props.globals.initNode("/ECAM/Lower/light/clr", 0, "BOOL"), + }, + reset: func() { + for (var i = 0; i <= 8; i = i + 1) { + setprop("ECAM/msg/line" ~ i, ""); + setprop("ECAM/rightmsg/line" ~ i, ""); + setprop("ECAM/msg/linec" ~ i, "w"); + setprop("ECAM/rightmsg/linec" ~ i, "w"); } + page.setValue("door"); + me.lights.apu.setValue(0); + me.lights.bleed.setValue(0); + me.lights.cond.setValue(0); + me.lights.door.setValue(0); + me.lights.elec.setValue(0); + me.lights.eng.setValue(0); + me.lights.fctl.setValue(0); + me.lights.fuel.setValue(0); + me.lights.hyd.setValue(0); + me.lights.press.setValue(0); + me.lights.sts.setValue(0); + me.lights.wheel.setValue(0); + me.lights.clr.setValue(0); + }, + loop: func(notification) { # AP / ATHR warnings - if (ap_active == 1 and apWarnNode.getValue() == 0) { + if (ap_active == 1 and !notification.apWarn) { ap_active = 0; - } elsif (ap_active == 1 and apWarnNode.getValue() == 1 and elapsedTime > (apOffTime.getValue() + 9)) { + } elsif (ap_active == 1 and notification.apWarn == 1 and notification.elapsedTime > (notification.apOffTime + 9)) { ap_active = 0; apWarnNode.setValue(0); - } elsif (ap_active == 0 and apWarnNode.getValue() != 0) { + } elsif (ap_active == 0 and notification.apWarn != 0) { ap_active = 1; } - if (ap_active == 1 and apWarnNode.getValue() == 1 and elapsedTime > (apOffTime.getValue() + 3) and ecam.lights[0].getBoolValue()) { + if (ap_active == 1 and notification.apWarn == 1 and notification.elapsedTime > (notification.apOffTime + 3) and notification.masterWarn) { ecam.lights[0].setBoolValue(0); } - if (apWarnNode.getValue() == 2 and (fmgc.Output.ap1.getValue() == 1 or fmgc.Output.ap2.getValue() == 1)) { - apWarnNode.setValue(0); - } - - if (athr_active == 1 and athrWarnNode.getValue() == 0) { + if (athr_active == 1 and !notification.athrWarn) { athr_active = 0; - } elsif (athr_active == 1 and athrWarnNode.getValue() == 1 and elapsedTime > (athrOffTime.getValue() + 9)) { + } elsif (athr_active == 1 and notification.athrWarn == 1 and notification.elapsedTime > (notification.athrOffTime + 9)) { athr_active = 0; athrWarnNode.setValue(0); - } elsif (athr_active == 0 and athrWarnNode.getValue() != 0) { + } elsif (athr_active == 0 and notification.athrWarn != 0) { athr_active = 1; } - - if (athr_active == 1 and athrWarnNode.getValue() == 1 and elapsedTime > (athrOffTime.getValue() + 3) and ecam.lights[1].getBoolValue()) { + if (athr_active == 1 and notification.athrWarn == 1 and notification.elapsedTime > (notification.athrOffTime + 3) and notification.masterCaution) { ecam.lights[1].setValue(0); } - if (athrWarnNode.getValue() == 2 and fmgc.Output.athr.getValue() == 1) { - athrWarnNode.setValue(0); + foreach (var update_item; me.update_items) { + update_item.update(notification); } - SystemDisplay.update(); + SystemDisplay.update(notification); if (me._cachePage != SystemDisplay.page) { - me._cachePage = SystemDisplay.page; - page.setValue(SystemDisplay.page); + me.updateSDPage(SystemDisplay.page); } }, + updateSDPage: func(newPage) { + me._cachePage = newPage; + page.setValue(newPage); + }, clrLight: func() { - clrLight.setValue(1); - } + me.lights.clr.setValue(1); + }, }; var SystemDisplay = { @@ -217,12 +175,10 @@ var SystemDisplay = { me.page = page; } }, - update: func() { - phase = pts.ECAM.fwcWarningPhase.getValue(); + update: func(notification) { APUMaster = systems.APUNodes.Controls.master.getValue(); APURPM = pts.APU.rpm.getValue(); engModeSel = pts.Controls.Engines.startSw.getValue(); - elapsedSec = pts.Sim.Time.elapsedSec.getValue(); if (APUMaster == 1 and me.APU10sec != 1) { me.autoCall("apu"); @@ -230,10 +186,10 @@ var SystemDisplay = { if (me.APU10sec == 9 and APURPM >= 95.0) { me.APU10sec = 0; - me._apuTime = elapsedSec; + me._apuTime = notification.elapsedTime; } - if (me.APU10sec != 9 and elapsedSec > me._apuTime + 10) { + if (me.APU10sec != 9 and notification.elapsedTime > me._apuTime + 10) { me.APU10sec = 1; } } elsif (engModeSel == 0 or engModeSel == 2 or (engModeSel == 1 and me.eng10sec == 0)) { @@ -242,10 +198,10 @@ var SystemDisplay = { if (me.eng10sec == 9 and engModeSel == 1) { me.eng10sec = 0; - me._engTime = elapsedSec; + me._engTime = notification.elapsedTime; } - if (me.eng10sec != 9 and elapsedSec > me._engTime + 10) { + if (me.eng10sec != 9 and notification.elapsedTime > me._engTime + 10) { me.eng10sec = 1; } } else { @@ -256,49 +212,43 @@ var SystemDisplay = { me.eng10sec = 9; # Phase logic - if (phase == 1) { + if (notification.FWCPhase == 1) { me.autoCall("door"); me.fctl20sec = 9; - } elsif (phase == 2) { - aileron = pts.Fdm.JSBsim.Fbw.aileron.getValue(); - elevator = pts.Fdm.JSBsim.Fbw.elevator.getValue(); - - if (abs(aileron) >= 0.15 or abs(elevator) >= 0.15 and me.fctl20sec == 9) { + } elsif (notification.FWCPhase == 2) { + if (notification.aileronFBW >= 0.15 or notification.elevatorFBW >= 0.15 and me.fctl20sec == 9) { me.autoCall("fctl"); if (me.fctl20sec == 9) { me.fctl20sec = 0; - me._fctlTime = elapsedSec; + me._fctlTime = notification.elapsedTime; } - if (me.fctl20sec != 9 and elapsedSec > me._fctlTime + 20) { + if (me.fctl20sec != 9 and notification.elapsedTime > me._fctlTime + 20) { me.fctl20sec = 1; } } elsif (me.fctl20sec == 0) { - if (me.fctl20sec != 9 and elapsedSec > me._fctlTime + 20) { + if (me.fctl20sec != 9 and notification.elapsedTime > me._fctlTime + 20) { me.fctl20sec = 1; } } else { me.autoCall("wheel"); me.fctl20sec = 9; } - } elsif (phase >= 3 and phase <= 5) { + } elsif (notification.FWCPhase >= 3 and notification.FWCPhase <= 5) { me.autoCall("eng"); me.fctl20sec = 9; - } elsif (phase == 6) { - gearLever = pts.Controls.Gear.gearDown.getValue(); - agl = pts.Position.gearAglFt.getValue(); - - if (gearLever and agl <= 16000) { + } elsif (notification.FWCPhase == 6) { + if (notification.gearLever and notification.agl <= 16000) { me.autoCall("wheel"); } else { me.autoCall("crz"); } me.fctl20sec = 9; - } elsif (phase >= 7 and phase <= 9) { + } elsif (notification.FWCPhase >= 7 and notification.FWCPhase <= 9) { me.autoCall("wheel"); me.fctl20sec = 9; - } elsif (phase == 10) { + } elsif (notification.FWCPhase == 10) { me.autoCall("door"); me.fctl20sec = 9; } @@ -358,34 +308,10 @@ var ECAMControlPanel = { # todo }, lightOff: func(page) { - if (page == "clr") { clrLight.setBoolValue(0); } - elsif (page == "apu") { apuLight.setBoolValue(0); } - elsif (page == "bleed") { bleedLight.setBoolValue(0); } - elsif (page == "cond") { condLight.setBoolValue(0); } - elsif (page == "door") { doorLight.setBoolValue(0); } - elsif (page == "elec") { elecLight.setBoolValue(0); } - elsif (page == "eng") { engLight.setBoolValue(0); } - elsif (page == "fctl") { fctlLight.setBoolValue(0); } - elsif (page == "fuel") { fuelLight.setBoolValue(0); } - elsif (page == "hyd") { hydLight.setBoolValue(0); } - elsif (page == "press") { pressLight.setBoolValue(0); } - elsif (page == "sts") { stsLight.setBoolValue(0); } - elsif (page == "wheel") { wheelLight.setBoolValue(0); } + ECAM.lights[page].setBoolValue(0); }, lightOn: func(page) { - if (page == "clr") { clrLight.setBoolValue(1); } - elsif (page == "apu") { apuLight.setBoolValue(1); } - elsif (page == "bleed") { bleedLight.setBoolValue(1); } - elsif (page == "cond") { condLight.setBoolValue(1); } - elsif (page == "door") { doorLight.setBoolValue(1); } - elsif (page == "elec") { elecLight.setBoolValue(1); } - elsif (page == "eng") { engLight.setBoolValue(1); } - elsif (page == "fctl") { fctlLight.setBoolValue(1); } - elsif (page == "fuel") { fuelLight.setBoolValue(1); } - elsif (page == "hyd") { hydLight.setBoolValue(1); } - elsif (page == "press") { pressLight.setBoolValue(1); } - elsif (page == "sts") { stsLight.setBoolValue(1); } - elsif (page == "wheel") { wheelLight.setBoolValue(1); } + ECAM.lights[page].setBoolValue(1); }, }; @@ -414,4 +340,48 @@ var doApWarn = func(type) { apWarnNode.setValue(2); # master warning handled by warning system in this case } +} + +# Emesary +var ECAMRecipient = +{ + new: func(_ident) + { + var NewECAMRecipient = emesary.Recipient.new(_ident); + NewECAMRecipient.Receive = func(notification) + { + if (notification.NotificationType == "FrameNotification") + { + if (!math.mod(notifications.frameNotification.FrameCount,2)){ + ECAM.loop(notification); + } + return emesary.Transmitter.ReceiptStatus_OK; + } + return emesary.Transmitter.ReceiptStatus_NotProcessed; + }; + return NewECAMRecipient; + }, +}; + +var A320ECAM = ECAMRecipient.new("A320 ECAM"); +emesary.GlobalTransmitter.Register(A320ECAM); + +var input = { + "aileronFBW": "/fdm/jsbsim/fbw/aileron-sidestick", + "agl": "/position/gear-agl-ft", + "athr": "/it-autoflight/output/athr", + "athrWarn": "/it-autoflight/output/athr-warning", + "athrOffTime": "/ECAM/warnings/athr-off-time", + "ap1": "/it-autoflight/output/ap1", + "ap2": "/it-autoflight/output/ap2", + "apWarn": "/it-autoflight/output/ap-warning", + "apOffTime": "/ECAM/warnings/ap-off-time", + "elevatorFBW": "/fdm/jsbsim/fbw/elevator-sidestick", + "gearLever": "/controls/gear/gear-down", + "masterCaution": "/ECAM/warnings/master-caution-light", + "masterWarn": "/ECAM/warnings/master-warning-light", +}; + +foreach (var name; keys(input)) { + emesary.GlobalTransmitter.NotifyAll(notifications.FrameNotificationAddProperty.new("A320 ECAM", name, input[name])); } \ No newline at end of file diff --git a/Nasal/Libraries/libraries.nas b/Nasal/Libraries/libraries.nas index 426258dc..2e93c670 100644 --- a/Nasal/Libraries/libraries.nas +++ b/Nasal/Libraries/libraries.nas @@ -259,7 +259,6 @@ var systemsLoop = maketimer(0.1, func { systems.ADIRS.loop(); systems.APUController.loop(); systems.BrakeSys.update(); - ecam.ECAM.loop(); fadec.FADEC.loop(); rmp.rmpUpdate(); fcu.FCUController.loop(); diff --git a/Nasal/emesary/exec.nas b/Nasal/emesary/exec.nas index 885e2447..40661a0b 100644 --- a/Nasal/emesary/exec.nas +++ b/Nasal/emesary/exec.nas @@ -23,13 +23,11 @@ var frame_inc = 0; var cur_frame_inc = 0.05; var execLoop = func { - # notifications.frameNotification.fetchvars(); - if (notifications.frameNotification.FrameCount >= 4) { + if (notifications.frameNotification.FrameCount >= 1) { notifications.frameNotification.FrameCount = 0; } emesary.GlobalTransmitter.NotifyAll(notifications.frameNotification); - # notifications.frameNotification.FrameCount = notifications.frameNotification.FrameCount + 1; @@ -57,8 +55,12 @@ var execLoop = func # setup the properties to monitor for this system input = { -frame_rate : "/sim/frame-rate", -elapsed_seconds : "/sim/time/elapsed-sec", + frame_rate: "/sim/frame-rate", + elapsedTime: "/sim/time/elapsed-sec", + FWCPhase: "/ECAM/warning-phase", + gear0Wow: "/gear/gear[0]/wow", + engine1State: "/engines/engine[0]/state", + engine2State: "/engines/engine[1]/state", }; foreach (var name; keys(input)) {