From 5ec363fcd80f1a789cf5d8e749c6b83c11cfe60b Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Tue, 29 Mar 2022 22:39:35 -0400 Subject: [PATCH 01/93] Fix missing SimVarDataRequestAttribute on states CowlFlaps1/2/3/4 and AutoPilotFlightDirectorCurrentBank. Fixes https://github.com/mpaperno/MSFSTouchPortalPlugin/issues/29 --- MSFSTouchPortalPlugin/Constants/SimVars.cs | 7 ++++++- MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs | 3 ++- .../Objects/FlightSystems/FlightSystems.cs | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/MSFSTouchPortalPlugin/Constants/SimVars.cs b/MSFSTouchPortalPlugin/Constants/SimVars.cs index f472405..f902fe9 100644 --- a/MSFSTouchPortalPlugin/Constants/SimVars.cs +++ b/MSFSTouchPortalPlugin/Constants/SimVars.cs @@ -50,7 +50,7 @@ public enum Definition { AutoPilotWingLeveler, AutoPilotYawDampener, AutoThrottleArm, - AutoThrottleGA, + AutoThrottleGoAround, AvionicsMasterSwitch, Com1ActiveFrequency, Com1StandbyFrequency, @@ -123,6 +123,11 @@ public enum Definition { RPMPropeller4, RudderTrimPct, SimulationRate, + SpoilersArmed, + SpoilersAvailable, + SpoilersHandlePosition, + SpoilersLeftPosition, + SpoilersRightPosition, StallWarning, ThrottleEngine1, ThrottleEngine2, diff --git a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs index b13d443..af6a629 100644 --- a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs +++ b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs @@ -261,7 +261,7 @@ internal static class AutoPilotMapping { [SimVarDataRequest] [TouchPortalState("AutoThrottleGoAround", "text", "Auto Throttle GoAround", "")] public static readonly SimVarItem AUTO_THROTTLE_GA = - new SimVarItem { Def = Definition.AutoThrottleGA, SimVarName = "AUTOPILOT TAKEOFF POWER ACTIVE", Unit = Units.Bool, CanSet = false }; + new SimVarItem { Def = Definition.AutoThrottleGoAround, SimVarName = "AUTOPILOT TAKEOFF POWER ACTIVE", Unit = Units.Bool, CanSet = false }; #endregion @@ -320,6 +320,7 @@ internal static class AutoPilotMapping { public static readonly SimVarItem SYNC_FLIGHT_DIRECTOR_PITCH = new SimVarItem { Def = Definition.AutoPilotFlightDirectorCurrentPitch, SimVarName = "AUTOPILOT FLIGHT DIRECTOR PITCH", Unit = Units.radians, CanSet = false }; + [SimVarDataRequest] [TouchPortalState("AutoPilotFlightDirectorCurrentBank", "text", "Flight Director Current Bank", "")] public static readonly SimVarItem SYNC_FLIGHT_DIRECTOR_Bank = new SimVarItem { Def = Definition.AutoPilotFlightDirectorCurrentBank, SimVarName = "AUTOPILOT FLIGHT DIRECTOR BANK", Unit = Units.radians, CanSet = false }; diff --git a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs index b143511..5d41fe6 100644 --- a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs +++ b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs @@ -85,6 +85,7 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("DEC_COWL_FLAPS", "Decrease")] public static object CowlFlapsAll { get; } + [SimVarDataRequest] [TouchPortalAction("CowlFlaps1", "Cowl Flaps 1", "MSFS", "Cowl Flaps 1", "Cowl Flaps 1 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS1", "Increase")] @@ -92,6 +93,7 @@ internal static class FlightSystemsMapping { [TouchPortalState("CowlFlaps1Percent", "text", "Cowl Flaps 1 Opened Percentage", "")] public static readonly SimVarItem CowlFlaps1 = new SimVarItem { Def = Definition.CowlFlaps1Percent, SimVarName = "RECIP ENG COWL FLAP POSITION:1", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; + [SimVarDataRequest] [TouchPortalAction("CowlFlaps2", "Cowl Flaps 2", "MSFS", "Cowl Flaps 2", "Cowl Flaps 2 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS2", "Increase")] @@ -99,6 +101,7 @@ internal static class FlightSystemsMapping { [TouchPortalState("CowlFlaps2Percent", "text", "Cowl Flaps 2 Opened Percentage", "")] public static readonly SimVarItem CowlFlaps2 = new SimVarItem { Def = Definition.CowlFlaps2Percent, SimVarName = "RECIP ENG COWL FLAP POSITION:2", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; + [SimVarDataRequest] [TouchPortalAction("CowlFlaps3", "Cowl Flaps 3", "MSFS", "Cowl Flaps 3", "Cowl Flaps 3 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS3", "Increase")] @@ -106,6 +109,7 @@ internal static class FlightSystemsMapping { [TouchPortalState("CowlFlaps3Percent", "text", "Cowl Flaps 3 Opened Percentage", "")] public static readonly SimVarItem CowlFlaps3 = new SimVarItem { Def = Definition.CowlFlaps3Percent, SimVarName = "RECIP ENG COWL FLAP POSITION:3", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; + [SimVarDataRequest] [TouchPortalAction("CowlFlaps4", "Cowl Flaps 4", "MSFS", "Cowl Flaps 4", "Cowl Flaps 4 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS4", "Increase")] From bea9a5797540682bf22bdaeb3538022bb932736c Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Tue, 29 Mar 2022 22:47:18 -0400 Subject: [PATCH 02/93] Minor unit type conversion optimization. --- MSFSTouchPortalPlugin/Constants/Units.cs | 29 ++++++++++--------- .../Services/PluginService.cs | 11 +++---- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/MSFSTouchPortalPlugin/Constants/Units.cs b/MSFSTouchPortalPlugin/Constants/Units.cs index ee825a8..e057de4 100644 --- a/MSFSTouchPortalPlugin/Constants/Units.cs +++ b/MSFSTouchPortalPlugin/Constants/Units.cs @@ -4,25 +4,26 @@ [assembly: InternalsVisibleTo("MSFSTouchPortalPlugin-Tests")] namespace MSFSTouchPortalPlugin.Constants { internal static class Units { + + private static readonly string[] _floatUnits = new string[] { + degrees, + radians, + knots, + feet, + MHz, + percent, + percentover100, + rpm, + mach, + feetminute, + }; + /// /// All the unit types that should convert to Float /// /// The unit type /// True if it should be a float - internal static bool ShouldConvertToFloat(string unit) { - var floatUnits = new string[] { - Units.degrees, - Units.knots, - Units.feet, - Units.MHz, - Units.percent, - Units.rpm, - Units.mach, - Units.feetminute - }; - - return floatUnits.Contains(unit); - } + internal static bool ShouldConvertToFloat(string unit) => _floatUnits.Contains(unit); internal const string amp = "amp"; internal const string ampere = "ampere"; diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index b1ba2e4..15f3024 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -188,11 +188,11 @@ private void SimConnectEvent_OnConnect() { private void SimConnectEvent_OnDataUpdateEvent(Definition def, Definition req, object data) { // Lookup State Mapping - if (statesDictionary.TryGetValue(def, out var value)) { + if (statesDictionary.TryGetValue(def, out SimVarItem value)) { var stringVal = data.ToString(); // Only update state on changes - // TODO: Move these to after parsing due to fractional unnoticable changes. + // TODO: Move these to after parsing due to fractional unnoticeable changes. if (value.Value != stringVal) { value.Value = stringVal; object valObj = stringVal; @@ -200,12 +200,13 @@ private void SimConnectEvent_OnDataUpdateEvent(Definition def, Definition req, o // Handle conversions if (Units.ShouldConvertToFloat(value.Unit)) { valObj = float.Parse(stringVal); - } else if (value.Unit == Units.radians) { + } + if (value.Unit == Units.radians) { // Convert to Degrees - valObj = float.Parse(stringVal) * (180 / Math.PI); + valObj = (float)valObj * (180 / Math.PI); } else if (value.Unit == Units.percentover100) { // Convert to actual percentage (percentover100 range is 0 to 1) - valObj = float.Parse(stringVal) * 100.0f; + valObj = (float)valObj * 100.0f; } // Update if known id. From ddf3f5fde721322f3ae12226caa6907d8eb286f9 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Tue, 29 Mar 2022 23:00:07 -0400 Subject: [PATCH 03/93] Get rid of InternalEventAttribute to simplify event construction in ReflectionService; Rename Groups.System enum to Groups.Plugin, add Groups.None, and re-sort the other Groups in alpha order. --- .../Attributes/InternalEventAttribute.cs | 6 ----- MSFSTouchPortalPlugin/Enums/Groups.cs | 25 ++++++++++--------- .../Objects/Plugin/Plugin.cs | 9 ++++--- .../Services/ReflectionService.cs | 22 ++++++++-------- 4 files changed, 29 insertions(+), 33 deletions(-) delete mode 100644 MSFSTouchPortalPlugin/Attributes/InternalEventAttribute.cs diff --git a/MSFSTouchPortalPlugin/Attributes/InternalEventAttribute.cs b/MSFSTouchPortalPlugin/Attributes/InternalEventAttribute.cs deleted file mode 100644 index 487a08e..0000000 --- a/MSFSTouchPortalPlugin/Attributes/InternalEventAttribute.cs +++ /dev/null @@ -1,6 +0,0 @@ -using System; - -namespace MSFSTouchPortalPlugin.Attributes { - internal class InternalEventAttribute : Attribute { - } -} diff --git a/MSFSTouchPortalPlugin/Enums/Groups.cs b/MSFSTouchPortalPlugin/Enums/Groups.cs index b6e93ac..5056ec2 100644 --- a/MSFSTouchPortalPlugin/Enums/Groups.cs +++ b/MSFSTouchPortalPlugin/Enums/Groups.cs @@ -1,15 +1,16 @@ namespace MSFSTouchPortalPlugin.Enums { - internal enum Groups { - System = 0, - AutoPilot = 1, - Fuel = 2, - SimSystem = 3, - Engine = 4, - Environment = 5, - Electrical = 6, - FlightSystems = 7, - FlightInstruments = 8, - Failures = 9, - Communication = 10 + public enum Groups { + None = 0, + Plugin, + AutoPilot, + Communication, + Electrical, + Engine, + Environment, + Failures, + FlightInstruments, + FlightSystems, + Fuel, + SimSystem, } } diff --git a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs index 8a53c61..cd1e926 100644 --- a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs +++ b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs @@ -1,10 +1,11 @@ using MSFSTouchPortalPlugin.Attributes; +using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Types; using TouchPortalExtension.Attributes; namespace MSFSTouchPortalPlugin.Objects.Plugin { - [InternalEvent] + [SimNotificationGroup(Groups.Plugin)] [TouchPortalCategory("Plugin", "MSFS - Plugin")] internal static class PluginMapping { [TouchPortalAction("Connection", "Connection", "MSFS", "Toggle/On/Off SimConnect Connection", "SimConnect Connection - {0}")] @@ -16,7 +17,7 @@ internal static class PluginMapping { public static readonly object Connection; } - [InternalEvent] + [SimNotificationGroup(Groups.Plugin)] [TouchPortalCategory("Plugin", "MSFS - Plugin")] [TouchPortalSettingsContainer] public static class Settings { @@ -56,10 +57,10 @@ internal enum Plugin : short { ActionRepeatIntervalSet, } - // Dynamicaly generated SimConnect client event IDs are "parented" to this enum type, + // Dynamically generated SimConnect client event IDs are "parented" to this enum type, // meaning they become of this Type when they need to be cast to en Enum type (eg. for SimConnect C# API). // This is done by the ReflectionService when generating the list of events for SimConnect. - // They really could be cast any any Enum type at all, so this is mostly for semantics. + // They really could be cast any Enum type at all, so this is mostly for semantics. internal enum SimEventClientId { // Starting point diff --git a/MSFSTouchPortalPlugin/Services/ReflectionService.cs b/MSFSTouchPortalPlugin/Services/ReflectionService.cs index f631d0b..206fc49 100644 --- a/MSFSTouchPortalPlugin/Services/ReflectionService.cs +++ b/MSFSTouchPortalPlugin/Services/ReflectionService.cs @@ -38,15 +38,15 @@ public Dictionary GetActionEvents() { int nextId = (int)SimEventClientId.Init + 1; // Get all types which have actions mapped to events, sim or internal. - var eventContainers = Assembly.GetExecutingAssembly().GetTypes().Where( - t => t.IsClass && (t.GetCustomAttribute() != null || t.GetCustomAttribute() != null) - ).ToList(); - eventContainers.ForEach(notifyType => { + var eventContainers = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsClass && (t.GetCustomAttribute() != null)); + foreach (var notifyType in eventContainers) { // Get the TP Category ID for this type - var catName = notifyType.GetCustomAttribute().Id; + var catId = notifyType.GetCustomAttribute().Id; // And the SimConnect Group ID Groups simGroup = notifyType.GetCustomAttribute()?.Group ?? default; - var internalEvent = simGroup == default; + if (simGroup == default) + continue; + var internalEvent = simGroup == Groups.Plugin; // Get all members which have an action mapping, which is some unique combination of value(s) mapped to a SimConnect event name or internal event enum List actionMembers = notifyType.GetMembers().Where(m => m.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalActionMappingAttribute))).ToList(); actionMembers.ForEach(e => { @@ -57,7 +57,7 @@ public Dictionary GetActionEvents() { ActionId = e.GetCustomAttribute().Id, //ActionObject = e }; - // Loop over all the data attributes to find the "choice" types for mapping and also the index of any freeform data value field + // Loop over all the data attributes to find the "choice" types for mapping and also the index of any free-form data value field var dataAttribs = e.GetCustomAttributes().ToList() ?? new(); for (var i = 0; i < dataAttribs.Count; ++i) { var dataAttrib = dataAttribs[i]; @@ -68,7 +68,7 @@ public Dictionary GetActionEvents() { act.KeyFormatStr += $"{{{i}}}"; } else { - // we only support one freeform value per mapping, which is all SimConnect supports, and internal events can handle the data as necessary already. + // we only support one free-form value per mapping, which is all SimConnect supports, and internal events can handle the data as necessary already. act.ValueIndex = i; act.MinValue = dataAttrib.MinValue; act.MaxValue = dataAttrib.MaxValue; @@ -87,10 +87,10 @@ public Dictionary GetActionEvents() { clientEventIdToNameMap[mapTarget] = new { EventName = ma.ActionId, GroupId = simGroup }; }); // Put into returned collection - if (!returnDict.TryAdd($"{rootName}.{catName}.Action.{act.ActionId}", act)) - _logger.LogWarning($"Duplicate action ID found for action '{act.ActionId}' in category '{catName}', skipping.'"); + if (!returnDict.TryAdd($"{rootName}.{catId}.Action.{act.ActionId}", act)) + _logger.LogWarning($"Duplicate action ID found for action '{act.ActionId}' in category '{catId}', skipping.'"); }); - }); + } return returnDict; } From 49625c0f98ae0ae4ea2a1077b9db64de9eca9eae Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Wed, 30 Mar 2022 00:49:04 -0400 Subject: [PATCH 04/93] Revert "Fix missing SimVarDataRequestAttribute on states CowlFlaps1/2/3/4 and AutoPilotFlightDirectorCurrentBank. Fixes https://github.com/mpaperno/MSFSTouchPortalPlugin/issues/29" This reverts commit 5ec363fcd80f1a789cf5d8e749c6b83c11cfe60b. --- MSFSTouchPortalPlugin/Constants/SimVars.cs | 7 +------ MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs | 3 +-- .../Objects/FlightSystems/FlightSystems.cs | 4 ---- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/MSFSTouchPortalPlugin/Constants/SimVars.cs b/MSFSTouchPortalPlugin/Constants/SimVars.cs index f902fe9..f472405 100644 --- a/MSFSTouchPortalPlugin/Constants/SimVars.cs +++ b/MSFSTouchPortalPlugin/Constants/SimVars.cs @@ -50,7 +50,7 @@ public enum Definition { AutoPilotWingLeveler, AutoPilotYawDampener, AutoThrottleArm, - AutoThrottleGoAround, + AutoThrottleGA, AvionicsMasterSwitch, Com1ActiveFrequency, Com1StandbyFrequency, @@ -123,11 +123,6 @@ public enum Definition { RPMPropeller4, RudderTrimPct, SimulationRate, - SpoilersArmed, - SpoilersAvailable, - SpoilersHandlePosition, - SpoilersLeftPosition, - SpoilersRightPosition, StallWarning, ThrottleEngine1, ThrottleEngine2, diff --git a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs index af6a629..b13d443 100644 --- a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs +++ b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs @@ -261,7 +261,7 @@ internal static class AutoPilotMapping { [SimVarDataRequest] [TouchPortalState("AutoThrottleGoAround", "text", "Auto Throttle GoAround", "")] public static readonly SimVarItem AUTO_THROTTLE_GA = - new SimVarItem { Def = Definition.AutoThrottleGoAround, SimVarName = "AUTOPILOT TAKEOFF POWER ACTIVE", Unit = Units.Bool, CanSet = false }; + new SimVarItem { Def = Definition.AutoThrottleGA, SimVarName = "AUTOPILOT TAKEOFF POWER ACTIVE", Unit = Units.Bool, CanSet = false }; #endregion @@ -320,7 +320,6 @@ internal static class AutoPilotMapping { public static readonly SimVarItem SYNC_FLIGHT_DIRECTOR_PITCH = new SimVarItem { Def = Definition.AutoPilotFlightDirectorCurrentPitch, SimVarName = "AUTOPILOT FLIGHT DIRECTOR PITCH", Unit = Units.radians, CanSet = false }; - [SimVarDataRequest] [TouchPortalState("AutoPilotFlightDirectorCurrentBank", "text", "Flight Director Current Bank", "")] public static readonly SimVarItem SYNC_FLIGHT_DIRECTOR_Bank = new SimVarItem { Def = Definition.AutoPilotFlightDirectorCurrentBank, SimVarName = "AUTOPILOT FLIGHT DIRECTOR BANK", Unit = Units.radians, CanSet = false }; diff --git a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs index 5d41fe6..b143511 100644 --- a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs +++ b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs @@ -85,7 +85,6 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("DEC_COWL_FLAPS", "Decrease")] public static object CowlFlapsAll { get; } - [SimVarDataRequest] [TouchPortalAction("CowlFlaps1", "Cowl Flaps 1", "MSFS", "Cowl Flaps 1", "Cowl Flaps 1 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS1", "Increase")] @@ -93,7 +92,6 @@ internal static class FlightSystemsMapping { [TouchPortalState("CowlFlaps1Percent", "text", "Cowl Flaps 1 Opened Percentage", "")] public static readonly SimVarItem CowlFlaps1 = new SimVarItem { Def = Definition.CowlFlaps1Percent, SimVarName = "RECIP ENG COWL FLAP POSITION:1", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; - [SimVarDataRequest] [TouchPortalAction("CowlFlaps2", "Cowl Flaps 2", "MSFS", "Cowl Flaps 2", "Cowl Flaps 2 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS2", "Increase")] @@ -101,7 +99,6 @@ internal static class FlightSystemsMapping { [TouchPortalState("CowlFlaps2Percent", "text", "Cowl Flaps 2 Opened Percentage", "")] public static readonly SimVarItem CowlFlaps2 = new SimVarItem { Def = Definition.CowlFlaps2Percent, SimVarName = "RECIP ENG COWL FLAP POSITION:2", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; - [SimVarDataRequest] [TouchPortalAction("CowlFlaps3", "Cowl Flaps 3", "MSFS", "Cowl Flaps 3", "Cowl Flaps 3 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS3", "Increase")] @@ -109,7 +106,6 @@ internal static class FlightSystemsMapping { [TouchPortalState("CowlFlaps3Percent", "text", "Cowl Flaps 3 Opened Percentage", "")] public static readonly SimVarItem CowlFlaps3 = new SimVarItem { Def = Definition.CowlFlaps3Percent, SimVarName = "RECIP ENG COWL FLAP POSITION:3", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; - [SimVarDataRequest] [TouchPortalAction("CowlFlaps4", "Cowl Flaps 4", "MSFS", "Cowl Flaps 4", "Cowl Flaps 4 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS4", "Increase")] From c1d048ca62657e1f09393f085c26a54a3a0ddf9f Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Wed, 30 Mar 2022 00:52:57 -0400 Subject: [PATCH 05/93] Fix missing SimVarDataRequestAttribute on states CowlFlaps1/2/3/4 and AutoPilotFlightDirectorCurrentBank. Fixes https://github.com/mpaperno/MSFSTouchPortalPlugin/issues/29 --- MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs | 1 + MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs index b13d443..3531512 100644 --- a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs +++ b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs @@ -320,6 +320,7 @@ internal static class AutoPilotMapping { public static readonly SimVarItem SYNC_FLIGHT_DIRECTOR_PITCH = new SimVarItem { Def = Definition.AutoPilotFlightDirectorCurrentPitch, SimVarName = "AUTOPILOT FLIGHT DIRECTOR PITCH", Unit = Units.radians, CanSet = false }; + [SimVarDataRequest] [TouchPortalState("AutoPilotFlightDirectorCurrentBank", "text", "Flight Director Current Bank", "")] public static readonly SimVarItem SYNC_FLIGHT_DIRECTOR_Bank = new SimVarItem { Def = Definition.AutoPilotFlightDirectorCurrentBank, SimVarName = "AUTOPILOT FLIGHT DIRECTOR BANK", Unit = Units.radians, CanSet = false }; diff --git a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs index b143511..5d41fe6 100644 --- a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs +++ b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs @@ -85,6 +85,7 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("DEC_COWL_FLAPS", "Decrease")] public static object CowlFlapsAll { get; } + [SimVarDataRequest] [TouchPortalAction("CowlFlaps1", "Cowl Flaps 1", "MSFS", "Cowl Flaps 1", "Cowl Flaps 1 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS1", "Increase")] @@ -92,6 +93,7 @@ internal static class FlightSystemsMapping { [TouchPortalState("CowlFlaps1Percent", "text", "Cowl Flaps 1 Opened Percentage", "")] public static readonly SimVarItem CowlFlaps1 = new SimVarItem { Def = Definition.CowlFlaps1Percent, SimVarName = "RECIP ENG COWL FLAP POSITION:1", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; + [SimVarDataRequest] [TouchPortalAction("CowlFlaps2", "Cowl Flaps 2", "MSFS", "Cowl Flaps 2", "Cowl Flaps 2 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS2", "Increase")] @@ -99,6 +101,7 @@ internal static class FlightSystemsMapping { [TouchPortalState("CowlFlaps2Percent", "text", "Cowl Flaps 2 Opened Percentage", "")] public static readonly SimVarItem CowlFlaps2 = new SimVarItem { Def = Definition.CowlFlaps2Percent, SimVarName = "RECIP ENG COWL FLAP POSITION:2", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; + [SimVarDataRequest] [TouchPortalAction("CowlFlaps3", "Cowl Flaps 3", "MSFS", "Cowl Flaps 3", "Cowl Flaps 3 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS3", "Increase")] @@ -106,6 +109,7 @@ internal static class FlightSystemsMapping { [TouchPortalState("CowlFlaps3Percent", "text", "Cowl Flaps 3 Opened Percentage", "")] public static readonly SimVarItem CowlFlaps3 = new SimVarItem { Def = Definition.CowlFlaps3Percent, SimVarName = "RECIP ENG COWL FLAP POSITION:3", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; + [SimVarDataRequest] [TouchPortalAction("CowlFlaps4", "Cowl Flaps 4", "MSFS", "Cowl Flaps 4", "Cowl Flaps 4 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS4", "Increase")] From eb90e053a6aa67afe4426413628ce469cfa31e5a Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Wed, 30 Mar 2022 03:43:21 -0400 Subject: [PATCH 06/93] Add FlightSystems spoiler states: SpoilersAvailable, SpoilersArmed, SpoilersHandlePosition, SpoilersLeftPosition, SpoilersRightPosition. Closes https://github.com/mpaperno/MSFSTouchPortalPlugin/issues/7 --- MSFSTouchPortalPlugin/Constants/SimVars.cs | 5 ++++ .../Objects/FlightSystems/FlightSystems.cs | 26 ++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/MSFSTouchPortalPlugin/Constants/SimVars.cs b/MSFSTouchPortalPlugin/Constants/SimVars.cs index f472405..8d84d11 100644 --- a/MSFSTouchPortalPlugin/Constants/SimVars.cs +++ b/MSFSTouchPortalPlugin/Constants/SimVars.cs @@ -123,6 +123,11 @@ public enum Definition { RPMPropeller4, RudderTrimPct, SimulationRate, + SpoilersArmed, + SpoilersAvailable, + SpoilersHandlePosition, + SpoilersLeftPosition, + SpoilersRightPosition, StallWarning, ThrottleEngine1, ThrottleEngine2, diff --git a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs index 5d41fe6..c00e7b1 100644 --- a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs +++ b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs @@ -151,25 +151,43 @@ internal static class FlightSystemsMapping { #endregion #region Spoilers + // https://docs.flightsimulator.com/html/Programming_Tools/SimVars/Aircraft_SimVars/Aircraft_Control_Variables.htm#spoilers + [SimVarDataRequest] [TouchPortalAction("Spoilers", "Spoilers", "MSFS", "Spoilers", "Spoilers - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("SPOILERS_TOGGLE", "Toggle")] [TouchPortalActionMapping("SPOILERS_ON", "On")] [TouchPortalActionMapping("SPOILERS_OFF", "Off")] - public static object Spoilers { get; } + [TouchPortalState("SpoilersAvailable", "text", "Spoilers Available (0/1)", "0")] + public static readonly SimVarItem SpoilersAvailable = new SimVarItem { Def = Definition.SpoilersAvailable, SimVarName = "SPOILERS AVAILABLE", Unit = Units.Bool, CanSet = false }; - [TouchPortalAction("SpoilersSet", "Spoilers Set", "MSFS", " Set Spoilers", "Spoilers set to {0} (0- +16383)")] + [SimVarDataRequest] + [TouchPortalAction("SpoilersSet", "Spoilers Set", "MSFS", " Set Spoilers", "Set Spoilers handle position to {0} (0 - 16383)")] [TouchPortalActionText("0", 0, 16383)] [TouchPortalActionMapping("SPOILERS_SET")] - public static object SpoilersSet { get; } + [TouchPortalState("SpoilersHandlePosition", "text", "Spoilers Handle Position (0 - 16383)", "0")] + public static readonly SimVarItem SpoilersHandlePosition = + new SimVarItem { Def = Definition.SpoilersHandlePosition, SimVarName = "SPOILERS HANDLE POSITION", Unit = Units.position, CanSet = true }; + + [SimVarDataRequest] + [TouchPortalState("SpoilersLeftPosition", "text", "Spoilers Left Position Percent", "0")] + public static readonly SimVarItem SpoilersLeftPosition = + new SimVarItem { Def = Definition.SpoilersLeftPosition, SimVarName = "SPOILERS LEFT POSITION", Unit = Units.percentover100, CanSet = false, StringFormat = "{0:F1}" }; + [SimVarDataRequest] + [TouchPortalState("SpoilersRightPosition", "text", "Spoilers Right Position Percent", "0")] + public static readonly SimVarItem SpoilersRightPosition = + new SimVarItem { Def = Definition.SpoilersRightPosition, SimVarName = "SPOILERS RIGHT POSITION", Unit = Units.percentover100, CanSet = false, StringFormat = "{0:F1}" }; + + [SimVarDataRequest] [TouchPortalAction("SpoilersArm", "Spoilers Arm", "MSFS", "Spoilers Arm", "Spoilers Arm - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("SPOILERS_ARM_TOGGLE", "Toggle")] [TouchPortalActionMapping("SPOILERS_ARM_ON", "On")] [TouchPortalActionMapping("SPOILERS_ARM_OFF", "Off")] - public static object SpoilersArm { get; } + [TouchPortalState("SpoilersArmed", "text", "Spoilers Armed (0/1)", "0")] + public static readonly SimVarItem SpoilersArmed = new SimVarItem { Def = Definition.SpoilersArmed, SimVarName = "SPOILERS ARMED", Unit = Units.Bool, CanSet = false }; #endregion From fed245f87480ff659b589a0f07b6099f9d32f435 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Wed, 30 Mar 2022 03:46:31 -0400 Subject: [PATCH 07/93] Fix formatting of AutoPilotAirSpeedVar state. Also rename a Definition.AutoThrottleGA to AutoThrottleGoAround to conform with state name. Fixes https://github.com/mpaperno/MSFSTouchPortalPlugin/issues/26 --- MSFSTouchPortalPlugin/Constants/SimVars.cs | 2 +- MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MSFSTouchPortalPlugin/Constants/SimVars.cs b/MSFSTouchPortalPlugin/Constants/SimVars.cs index 8d84d11..f902fe9 100644 --- a/MSFSTouchPortalPlugin/Constants/SimVars.cs +++ b/MSFSTouchPortalPlugin/Constants/SimVars.cs @@ -50,7 +50,7 @@ public enum Definition { AutoPilotWingLeveler, AutoPilotYawDampener, AutoThrottleArm, - AutoThrottleGA, + AutoThrottleGoAround, AvionicsMasterSwitch, Com1ActiveFrequency, Com1StandbyFrequency, diff --git a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs index 3531512..c28c5cd 100644 --- a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs +++ b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs @@ -238,7 +238,7 @@ internal static class AutoPilotMapping { [TouchPortalActionMapping("AP_SPD_VAR_DEC", "Decrease")] [TouchPortalState("AutoPilotAirSpeedVar", "text", "AutoPilot Air Speed Value", "")] public static readonly SimVarItem AP_AIRSPEED_VAR = - new SimVarItem { Def = Definition.AutoPilotAirSpeedVar, SimVarName = "AUTOPILOT AIRSPEED HOLD VAR", Unit = Units.knots, CanSet = false }; + new SimVarItem { Def = Definition.AutoPilotAirSpeedVar, SimVarName = "AUTOPILOT AIRSPEED HOLD VAR", Unit = Units.knots, CanSet = false, StringFormat = "{0:0.0#}" }; [TouchPortalAction("AutoPilotAirSpeedSet", "Airspeed Value Set", "MSFS", "Sets the airspeed value", "Set Airspeed Hold Value to {0}")] [TouchPortalActionText("0", 0, 5000)] @@ -261,7 +261,7 @@ internal static class AutoPilotMapping { [SimVarDataRequest] [TouchPortalState("AutoThrottleGoAround", "text", "Auto Throttle GoAround", "")] public static readonly SimVarItem AUTO_THROTTLE_GA = - new SimVarItem { Def = Definition.AutoThrottleGA, SimVarName = "AUTOPILOT TAKEOFF POWER ACTIVE", Unit = Units.Bool, CanSet = false }; + new SimVarItem { Def = Definition.AutoThrottleGoAround, SimVarName = "AUTOPILOT TAKEOFF POWER ACTIVE", Unit = Units.Bool, CanSet = false }; #endregion From e1b480e81f34a26f4526d4e59078f9f34a15c7fc Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Wed, 30 Mar 2022 03:57:03 -0400 Subject: [PATCH 08/93] Minor optimization on SimVarItem request timeout check. Also remove redundant check TouchPortalStateId != null in PluginService. --- MSFSTouchPortalPlugin/Constants/SimVars.cs | 16 ++++++++++++---- MSFSTouchPortalPlugin/Services/PluginService.cs | 9 ++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/MSFSTouchPortalPlugin/Constants/SimVars.cs b/MSFSTouchPortalPlugin/Constants/SimVars.cs index f902fe9..9453e3b 100644 --- a/MSFSTouchPortalPlugin/Constants/SimVars.cs +++ b/MSFSTouchPortalPlugin/Constants/SimVars.cs @@ -1,4 +1,5 @@ using System; +using Stopwatch = System.Diagnostics.Stopwatch; namespace MSFSTouchPortalPlugin.Constants { public enum Definition { @@ -150,7 +151,6 @@ public enum Definition { /// The SimVarItem which defines all data variables for SimConnect /// public class SimVarItem { - public bool PendingRequest { get; private set; } public Definition Def { get; set; } public string SimVarName { get; set; } public string Unit { get; set; } @@ -159,6 +159,11 @@ public class SimVarItem { public string Value { get; set; } = string.Empty; public string StringFormat { get; set; } = "{0}"; public string TouchPortalStateId { get; set; } = ""; + /// Indicates if a SimConnect request for this variable is already pending. + public bool PendingRequest { get; private set; } + + private long _timeoutTicks; + /// /// Updates the object to either set pending update or no longer pending @@ -168,17 +173,20 @@ public void SetPending(bool val) { PendingRequest = val; if (val) { - LastPending = DateTime.Now; + _timeoutTicks = Stopwatch.GetTimestamp() + 30 * Stopwatch.Frequency; } } /// /// If pending for more than 30 seconds, timeout /// - public void PendingTimeout() { - if (PendingRequest && DateTime.Now > LastPending.AddSeconds(30)) { + /// true if a timeout occurred, false otherwise. + public bool PendingTimeout() { + if (PendingRequest && Stopwatch.GetTimestamp() > _timeoutTicks) { SetPending(false); + return true; } + return false; } } } diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 15f3024..1f36566 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -209,10 +209,8 @@ private void SimConnectEvent_OnDataUpdateEvent(Definition def, Definition req, o valObj = (float)valObj * 100.0f; } - // Update if known id. - if (!string.IsNullOrWhiteSpace(value.TouchPortalStateId)) { - _client.StateUpdate(value.TouchPortalStateId, string.Format(value.StringFormat, valObj)); - } + // Update TP state. + _client.StateUpdate(value.TouchPortalStateId, string.Format(value.StringFormat, valObj)); } value.SetPending(false); @@ -346,7 +344,8 @@ private void ProcessSimEvent(ActionEventType action, Enum eventId, in string[] d private void CheckPendingRequests() { foreach (var s in statesDictionary.Values) { // Expire pending if more than 30 seconds - s.PendingTimeout(); + if (s.PendingTimeout()) + _logger.LogDebug($"Request for SimVar '{s.SimVarName}' timed out!"); // Check if Pending data request in paly if (!s.PendingRequest) { From 1d0c9209381c3eb8da1f1972fd1937fcf9c8f7c9 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Wed, 30 Mar 2022 23:15:39 -0400 Subject: [PATCH 09/93] Fix SpoilersAvailable sim var name and SpoilersHandlePosition unit type. --- MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs index c00e7b1..b944763 100644 --- a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs +++ b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs @@ -160,7 +160,7 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("SPOILERS_ON", "On")] [TouchPortalActionMapping("SPOILERS_OFF", "Off")] [TouchPortalState("SpoilersAvailable", "text", "Spoilers Available (0/1)", "0")] - public static readonly SimVarItem SpoilersAvailable = new SimVarItem { Def = Definition.SpoilersAvailable, SimVarName = "SPOILERS AVAILABLE", Unit = Units.Bool, CanSet = false }; + public static readonly SimVarItem SpoilersAvailable = new SimVarItem { Def = Definition.SpoilersAvailable, SimVarName = "SPOILER AVAILABLE", Unit = Units.Bool, CanSet = false }; [SimVarDataRequest] [TouchPortalAction("SpoilersSet", "Spoilers Set", "MSFS", " Set Spoilers", "Set Spoilers handle position to {0} (0 - 16383)")] @@ -168,7 +168,7 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("SPOILERS_SET")] [TouchPortalState("SpoilersHandlePosition", "text", "Spoilers Handle Position (0 - 16383)", "0")] public static readonly SimVarItem SpoilersHandlePosition = - new SimVarItem { Def = Definition.SpoilersHandlePosition, SimVarName = "SPOILERS HANDLE POSITION", Unit = Units.position, CanSet = true }; + new SimVarItem { Def = Definition.SpoilersHandlePosition, SimVarName = "SPOILERS HANDLE POSITION", Unit = Units.position16k, CanSet = true, StringFormat = "{0:0}" }; [SimVarDataRequest] [TouchPortalState("SpoilersLeftPosition", "text", "Spoilers Left Position Percent", "0")] From 9d9b17e8dbe43fae944b3998c650c6ee5c04069d Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 00:22:13 -0400 Subject: [PATCH 10/93] Fix that the +/-16383 value limit is actually +/- 16384 (0x4000 which is actually more logical and an even number). --- .../Objects/AutoPilot/AutoPilot.cs | 4 +-- .../Objects/FlightSystems/FlightSystems.cs | 26 +++++++++---------- .../Objects/InstrumentsSystems/Engine.cs | 8 +++--- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs index c28c5cd..4cd0324 100644 --- a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs +++ b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs @@ -55,8 +55,8 @@ internal static class AutoPilotMapping { public static readonly SimVarItem AP_ATTITUDE_PITCH = new SimVarItem { Def = Definition.AutoPilotAttitudeVar, SimVarName = "AUTOPILOT PITCH HOLD REF", Unit = Units.radians, CanSet = false }; - [TouchPortalAction("AutoPilotAttitudeSet", "Attitude Hold Pitch Value Set", "MSFS", "Sets the airspeed value", "Set Attitude Hold Pitch Value to {0} (-16383 - +16383)")] - [TouchPortalActionText("0", -16383, 16383)] + [TouchPortalAction("AutoPilotAttitudeSet", "Attitude Hold Pitch Value Set", "MSFS", "Sets the airspeed value", "Set Attitude Hold Pitch Value to {0} (-16384 to +16384)")] + [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("AP_PITCH_REF_SET")] public static object AP_ATTITUDE_PITCH_SET { get; } diff --git a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs index b944763..4741222 100644 --- a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs +++ b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs @@ -18,8 +18,8 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("AILERONS_RIGHT", "Right")] public static object Ailerons { get; } - [TouchPortalAction("AileronsSet", "Ailerons Set", "MSFS", " Set Ailerons", "Ailerons set to {0} (-16383 - +16383)")] - [TouchPortalActionText("0", -16383, 16383)] + [TouchPortalAction("AileronsSet", "Ailerons Set", "MSFS", " Set Ailerons", "Ailerons set to {0} (-16384 to +16384)")] + [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("AILERON_SET")] public static object AileronsSet { get; } @@ -33,8 +33,8 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("ELEV_DOWN", "Down")] public static object Elevator { get; } - [TouchPortalAction("ElevatorSet", "Elevator Set", "MSFS", " Set Elevator", "Elevator set to {0} (-16383 - +16383)")] - [TouchPortalActionText("0", -16383, 16383)] + [TouchPortalAction("ElevatorSet", "Elevator Set", "MSFS", " Set Elevator", "Elevator set to {0} (-16384 to +16384)")] + [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("ELEVATOR_SET")] public static object ElevatorSet { get; } @@ -73,8 +73,8 @@ internal static class FlightSystemsMapping { [TouchPortalState("FlapsHandlePercent", "text", "Flaps Handle Percentage", "")] public static readonly SimVarItem FlapsHandlePercent = new SimVarItem { Def = Definition.FlapsHandlePercent, SimVarName = "FLAPS HANDLE PERCENT", Unit = Units.percent, CanSet = false, StringFormat = "{0:0.0#}" }; - [TouchPortalAction("FlapsSet", "Flaps Set", "MSFS", " Set Flaps", "Flaps set to {0} (0 - +16383)")] - [TouchPortalActionText("0", 0, 16383)] + [TouchPortalAction("FlapsSet", "Flaps Set", "MSFS", " Set Flaps", "Flaps set to {0} (0 to 16384)")] + [TouchPortalActionText("0", 0, 16384)] [TouchPortalActionMapping("FLAPS_SET")] public static object FlapsSet { get; } @@ -143,8 +143,8 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("RUDDER_RIGHT", "Right")] public static object Rudder { get; } - [TouchPortalAction("RudderSet", "Rudder Set", "MSFS", " Set Rudder", "Rudder set to {0} (-16383 - +16383)")] - [TouchPortalActionText("0", -16383, 16383)] + [TouchPortalAction("RudderSet", "Rudder Set", "MSFS", " Set Rudder", "Rudder set to {0} (-16384 to +16384)")] + [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("RUDDER_SET")] public static object RudderSet { get; } @@ -163,10 +163,10 @@ internal static class FlightSystemsMapping { public static readonly SimVarItem SpoilersAvailable = new SimVarItem { Def = Definition.SpoilersAvailable, SimVarName = "SPOILER AVAILABLE", Unit = Units.Bool, CanSet = false }; [SimVarDataRequest] - [TouchPortalAction("SpoilersSet", "Spoilers Set", "MSFS", " Set Spoilers", "Set Spoilers handle position to {0} (0 - 16383)")] - [TouchPortalActionText("0", 0, 16383)] + [TouchPortalAction("SpoilersSet", "Spoilers Set", "MSFS", " Set Spoilers", "Set Spoilers handle position to {0} (0 to 16384)")] + [TouchPortalActionText("0", 0, 16384)] [TouchPortalActionMapping("SPOILERS_SET")] - [TouchPortalState("SpoilersHandlePosition", "text", "Spoilers Handle Position (0 - 16383)", "0")] + [TouchPortalState("SpoilersHandlePosition", "text", "Spoilers Handle Position (0 - 16384)", "0")] public static readonly SimVarItem SpoilersHandlePosition = new SimVarItem { Def = Definition.SpoilersHandlePosition, SimVarName = "SPOILERS HANDLE POSITION", Unit = Units.position16k, CanSet = true, StringFormat = "{0:0}" }; @@ -217,8 +217,8 @@ internal static class FlightSystemsMapping { public static readonly SimVarItem ElevatorTrim = new SimVarItem { Def = Definition.ElevatorTrim, SimVarName = "ELEVATOR TRIM POSITION", Unit = Units.degrees, CanSet = true, StringFormat = "{0:0.0#}" }; - [TouchPortalAction("ElevatorTrimSet", "Elevator Trim Set", "MSFS", " Set Elevator Trim", "Elevator Trim set to {0} (-16383 - +16383)")] - [TouchPortalActionText("0", -16383, 16383)] + [TouchPortalAction("ElevatorTrimSet", "Elevator Trim Set", "MSFS", " Set Elevator Trim", "Elevator Trim set to {0} (-16384 to +16384)")] + [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("ELEVATOR_TRIM_SET")] public static object ElevatorTrimSet { get; } diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs index 17dd905..0fd9e5a 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs @@ -152,9 +152,9 @@ internal static class EngineMapping { [TouchPortalActionMapping("THROTTLE4_CUT", new[] { "4", "Cut" })] public static object THROTTLE_SPECIFIC { get; } - [TouchPortalAction("ThrottleSet", "Throttle Set", "MSFS", "Sets all or specific Throttle(s) to specific value", "Set Throttle {0} to {1} (-16383 - +16383)")] + [TouchPortalAction("ThrottleSet", "Throttle Set", "MSFS", "Sets all or specific Throttle(s) to specific value", "Set Throttle {0} to {1} (-16384 to +16384)")] [TouchPortalActionChoice(new[] { "All", "1", "2", "3", "4" }, "All")] - [TouchPortalActionText("0", -16383, 16383)] + [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("THROTTLE_SET", new[] { "All" })] [TouchPortalActionMapping("THROTTLE1_SET", new[] { "1" })] [TouchPortalActionMapping("THROTTLE2_SET", new[] { "2" })] @@ -290,9 +290,9 @@ internal static class EngineMapping { [TouchPortalActionMapping("TOGGLE_FEATHER_SWITCH_4", new[] { "4", "Toggle Feather Switch" })] public static object PROPELLER_PITCH { get; } - [TouchPortalAction("PropellerPitchSet", "Propeller Pitch Set", "MSFS", "Sets propeller pitch lever to value", "Set Propeller {0} Pitch to {1} (0 to 16383)")] + [TouchPortalAction("PropellerPitchSet", "Propeller Pitch Set", "MSFS", "Sets propeller pitch lever to value", "Set Propeller {0} Pitch to {1} (0 to 16384)")] [TouchPortalActionChoice(new[] { "All", "1", "2", "3", "4" })] - [TouchPortalActionText("0", 0, 16383)] + [TouchPortalActionText("0", 0, 16384)] [TouchPortalActionMapping("PROP_PITCH_SET", "All")] [TouchPortalActionMapping("PROP_PITCH1_SET", "1")] [TouchPortalActionMapping("PROP_PITCH2_SET", "2")] From 5614eec4ce358a98bc8084b5ce8ce9b1410ca352 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 00:23:48 -0400 Subject: [PATCH 11/93] Fix ThrottleSpecific Decrement and Decrement Small actions broken due to spelling. --- .../Objects/InstrumentsSystems/Engine.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs index 0fd9e5a..957f29c 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs @@ -129,26 +129,26 @@ internal static class EngineMapping { [TouchPortalActionMapping("THROTTLE1_FULL", new[] { "1", "Full" })] [TouchPortalActionMapping("THROTTLE1_INCR", new[] { "1", "Increase" })] [TouchPortalActionMapping("THROTTLE1_INCR_SMALL", new[] { "1", "Increase Small" })] - [TouchPortalActionMapping("THROTTLE1_DECR", new[] { "1", "Descrease" })] - [TouchPortalActionMapping("THROTTLE1_DECR_SMALL", new[] { "1", "Descrease Small" })] + [TouchPortalActionMapping("THROTTLE1_DECR", new[] { "1", "Decrease" })] + [TouchPortalActionMapping("THROTTLE1_DECR_SMALL", new[] { "1", "Decrease Small" })] [TouchPortalActionMapping("THROTTLE1_CUT", new[] { "1", "Cut" })] [TouchPortalActionMapping("THROTTLE2_FULL", new[] { "2", "Full" })] [TouchPortalActionMapping("THROTTLE2_INCR", new[] { "2", "Increase" })] [TouchPortalActionMapping("THROTTLE2_INCR_SMALL", new[] { "2", "Increase Small" })] - [TouchPortalActionMapping("THROTTLE2_DECR", new[] { "2", "Descrease" })] - [TouchPortalActionMapping("THROTTLE2_DECR_SMALL", new[] { "2", "Descrease Small" })] + [TouchPortalActionMapping("THROTTLE2_DECR", new[] { "2", "Decrease" })] + [TouchPortalActionMapping("THROTTLE2_DECR_SMALL", new[] { "2", "Decrease Small" })] [TouchPortalActionMapping("THROTTLE2_CUT", new[] { "2", "Cut" })] [TouchPortalActionMapping("THROTTLE3_FULL", new[] { "3", "Full" })] [TouchPortalActionMapping("THROTTLE3_INCR", new[] { "3", "Increase" })] [TouchPortalActionMapping("THROTTLE3_INCR_SMALL", new[] { "3", "Increase Small" })] - [TouchPortalActionMapping("THROTTLE3_DECR", new[] { "3", "Descrease" })] - [TouchPortalActionMapping("THROTTLE3_DECR_SMALL", new[] { "3", "Descrease Small" })] + [TouchPortalActionMapping("THROTTLE3_DECR", new[] { "3", "Decrease" })] + [TouchPortalActionMapping("THROTTLE3_DECR_SMALL", new[] { "3", "Decrease Small" })] [TouchPortalActionMapping("THROTTLE3_CUT", new[] { "3", "Cut" })] [TouchPortalActionMapping("THROTTLE4_FULL", new[] { "4", "Full" })] [TouchPortalActionMapping("THROTTLE4_INCR", new[] { "4", "Increase" })] [TouchPortalActionMapping("THROTTLE4_INCR_SMALL", new[] { "4", "Increase Small" })] - [TouchPortalActionMapping("THROTTLE4_DECR", new[] { "4", "Descrease" })] - [TouchPortalActionMapping("THROTTLE4_DECR_SMALL", new[] { "4", "Descrease Small" })] + [TouchPortalActionMapping("THROTTLE4_DECR", new[] { "4", "Decrease" })] + [TouchPortalActionMapping("THROTTLE4_DECR_SMALL", new[] { "4", "Decrease Small" })] [TouchPortalActionMapping("THROTTLE4_CUT", new[] { "4", "Cut" })] public static object THROTTLE_SPECIFIC { get; } From 70f822691e428529a6d579a1383a581acb035dba Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 00:55:24 -0400 Subject: [PATCH 12/93] Refactor SimVarItem to handle data typing and formatting internally, track extra meta data, as well as other features for future use. --- .../Constants/UnitsTests.cs | 4 +- MSFSTouchPortalPlugin/Constants/SimVars.cs | 229 +++++++++++++++++- MSFSTouchPortalPlugin/Constants/Units.cs | 37 +-- MSFSTouchPortalPlugin/Enums/UpdateFreq.cs | 14 ++ .../Services/PluginService.cs | 42 +--- .../Services/SimConnectService.cs | 38 ++- 6 files changed, 290 insertions(+), 74 deletions(-) create mode 100644 MSFSTouchPortalPlugin/Enums/UpdateFreq.cs diff --git a/MSFSTouchPortalPlugin-Tests/Constants/UnitsTests.cs b/MSFSTouchPortalPlugin-Tests/Constants/UnitsTests.cs index 1c36c99..77143b0 100644 --- a/MSFSTouchPortalPlugin-Tests/Constants/UnitsTests.cs +++ b/MSFSTouchPortalPlugin-Tests/Constants/UnitsTests.cs @@ -7,7 +7,7 @@ public class UnitsTests { public void ShouldConvertToFloat_ShouldReturnTrue() { // arrange // act - var result = Units.ShouldConvertToFloat(Units.degrees); + var result = Units.IsRealType(Units.degrees); // assert Assert.True(result); } @@ -16,7 +16,7 @@ public void ShouldConvertToFloat_ShouldReturnTrue() { public void ShouldConvertToFloat_ShouldReturnFalse() { // arrange // act - var result = Units.ShouldConvertToFloat(Units.Boolean); + var result = Units.IsRealType(Units.Boolean); // assert Assert.False(result); } diff --git a/MSFSTouchPortalPlugin/Constants/SimVars.cs b/MSFSTouchPortalPlugin/Constants/SimVars.cs index 9453e3b..e9c0696 100644 --- a/MSFSTouchPortalPlugin/Constants/SimVars.cs +++ b/MSFSTouchPortalPlugin/Constants/SimVars.cs @@ -1,9 +1,13 @@ -using System; +using MSFSTouchPortalPlugin.Enums; using Stopwatch = System.Diagnostics.Stopwatch; +using SIMCONNECT_DATATYPE = Microsoft.FlightSimulator.SimConnect.SIMCONNECT_DATATYPE; -namespace MSFSTouchPortalPlugin.Constants { - public enum Definition { - Init = 0, +namespace MSFSTouchPortalPlugin.Constants +{ + // TODO: Remove all values except Init, move to Enums folder/namespace + public enum Definition + { + None = 0, AileronTrimPct, AircraftTitle, AirSpeedIndicated, @@ -145,25 +149,219 @@ public enum Definition { ElevatorTrim, RudderTrim, #endregion + + Init = 1000, } /// /// The SimVarItem which defines all data variables for SimConnect /// - public class SimVarItem { - public Definition Def { get; set; } + public class SimVarItem // TODO: Move to Types folder/namespace + { + /// Unique ID string, used to generate TouchPortal state ID (and possibly other uses). + public string Id { get; set; } + /// Category for sorting/organizing, also used in TouchPortal state ID. + public Groups CategoryId { get; set; } = default; + /// Descriptive name for this data (for TouchPortal or other UI). + public string Name { get; set; } + /// Corresponding SimConnect SimVar name (blank if used for internal purposes). public string SimVarName { get; set; } - public string Unit { get; set; } - public bool CanSet { get; set; } - public DateTime LastPending { get; set; } = DateTime.Now; - public string Value { get; set; } = string.Empty; - public string StringFormat { get; set; } = "{0}"; - public string TouchPortalStateId { get; set; } = ""; - /// Indicates if a SimConnect request for this variable is already pending. + /// Default value string when no data from SimConnect. + public string DefaultValue { get; set; } + /// SimConnect settable value (future use) + public bool CanSet { get; set; } = false; + /// How often updates are sent by SimConnect if value changes (SIMCONNECT_PERIOD). Default is equivalent to SIMCONNECT_PERIOD_SIM_FRAME. + public UpdateFreq UpdateFreqency { get; set; } = UpdateFreq.Default; + /// Update frequency in ms when UpdateFrequency is set to UpdateFreq.Milliseconds + public uint UpdateInterval { get; set; } + /// Only report change if it is greater than the value of this parameter (not greater than or equal to). Default is any change. + public float DeltaEpsilon { get; set; } = 0.0f; + /// Could also be "choice" but we don't use that (yet?) + public string TouchPortalValueType { get; set; } = "text"; + /// This could/should be populated by whatever is creating the SimVarItem instance + public string TouchPortalStateId { get; set; } + + /// + /// SimConnect unit name. Changing this property will clear any current value! + /// Setting this property also sets the SimConnectDataType, StorageDataType, and all the Is*Type properties. + /// + public string Unit + { + get => _unit; + set { + if (_unit == value) + return; + _unit = value; + // set up type information based on the new Unit. + IsStringType = Units.IsStringType(_unit); + IsBooleanType = !IsStringType && Units.IsBooleantype(_unit); + IsIntegralType = !IsStringType && !IsBooleanType && Units.IsIntegraltype(_unit); + IsRealType = !IsStringType && !IsBooleanType && !IsIntegralType; + SimConnectDataType = IsStringType ? SIMCONNECT_DATATYPE.STRING64 : IsIntegralType ? SIMCONNECT_DATATYPE.INT64 : IsBooleanType ? SIMCONNECT_DATATYPE.INT32 : SIMCONNECT_DATATYPE.FLOAT64; + StorageDataType = IsStringType ? typeof(string) : IsIntegralType ? typeof(long) : IsBooleanType ? typeof(uint) : typeof(double); + } + } + + /// + /// This returns a full formatting string, as in "{0}" or "{0:FormattingString}" as needed. + /// It can be set with either a full string (with "{}" brackets and/or "0:" part(s)) or just as the actual formatting part (what goes after the "0:" part). + /// To get the "raw" formatting string, w/out any "{}" or "0:" parts, use the FormattingString property (which may be blank/null). + public string StringFormat + { + get => string.IsNullOrWhiteSpace(_formatString) ? "{0}" : "{0:" + _formatString + "}"; + set { + if (value.StartsWith('{')) + value = value.Trim('{', '}'); + if (value.StartsWith("0:")) + value = value.Remove(0, 2); + _formatString = value; + } + } + + /// The current value as an object. May be null; + public object Value + { + get => _value; + set { + _value = value; + _valInit = true; + } + } + + /// + /// Returns the current value as a formatted string according to the value type and StringFormat property. + /// If no value has been explicitly set, returns the DefaultValue. + /// + public string FormattedValue + { + get { + if (!_valInit) + return DefaultValue; + return Value switch { + double v => string.Format(StringFormat, v), + uint v => string.Format(StringFormat, v), + long v => string.Format(StringFormat, v), + string v => string.Format(StringFormat, v), + _ => string.Empty, + }; + } + } + + /// + /// The actual system Type used for Value property. This is determined automatically when setting the Unit type. + /// The return type could be null if Value type is null. Changing this property will clear any current value! + /// + public System.Type StorageDataType + { + get => Value?.GetType(); + private set { + if (Value == null || Value.GetType() != value) { + _value = value == typeof(string) ? string.Empty : System.Activator.CreateInstance(value); + _valInit = false; + } + } + } + + /// Returns true if this value is of a real (double) type, false otherwise + public bool IsRealType { get; private set; } + /// Returns true if this value is of a string type, false if numeric. + public bool IsStringType { get; private set; } + /// Returns true if this value is of a integer type, false if string or real. + public bool IsIntegralType { get; private set; } + /// Returns true if this value is of a boolean type, false otherwise + public bool IsBooleanType { get; private set; } + + /// Unique Definition ID for SimConnect + public Definition Def { get; set; } // TODO: make read-only + /// The SimConnect data type for registering this var. + public SIMCONNECT_DATATYPE SimConnectDataType { get; private set; } + /// Indicates if a SimConnect request for this variable is already pending. public bool PendingRequest { get; private set; } + /// For serializing the raw value + public string FormattingString => _formatString; + private object _value; + private bool _valInit; + private string _unit; + private string _formatString; private long _timeoutTicks; + private static Definition _nextDefinionId = Definition.Init; + private static Definition NextId() => ++_nextDefinionId; + + public SimVarItem() { + Def = NextId(); + } + + public bool ValueEquals(string value) => _valInit && IsStringType && value == (string)Value; + public bool ValueEquals(double value) => _valInit && IsRealType && System.Math.Abs((double)Value - ConvertValueIfNeeded(value)) <= DeltaEpsilon; + public bool ValueEquals(long value) => _valInit && IsIntegralType && System.Math.Abs((long)Value - value) <= (long)DeltaEpsilon; + public bool ValueEquals(uint value) => _valInit && IsBooleanType && System.Math.Abs((uint)Value - value) <= (uint)DeltaEpsilon; + + public bool ValueEquals(object value) { + if (!_valInit) + return false; + try { + return value switch { + double v => ValueEquals(v), + uint v => ValueEquals(v), + long v => ValueEquals(v), + _ => ValueEquals(value.ToString()), + }; + } + catch { + return false; + } + } + + public bool SetValue(string value) { + if (IsStringType) + Value = value; + return IsStringType; + } + + public bool SetValue(double value) { + if (!IsStringType) + Value = ConvertValueIfNeeded(value); + return !IsStringType; + } + + public bool SetValue(long value) { + if (IsIntegralType) + Value = value; + return IsIntegralType; + } + + public bool SetValue(uint value) { + if (IsBooleanType) + Value = value; + return IsBooleanType; + } + + public bool SetValue(object value) { + try { + return value switch { + double v => SetValue(v), + uint v => SetValue(v), + long v => SetValue(v), + _ => SetValue(value.ToString()), + }; + } + catch { + return false; + } + } + + public double ConvertValueIfNeeded(double value) { + // Convert to Degrees + if (Unit == Units.radians) + return value * (180.0 / System.Math.PI); + // Convert to actual percentage (percentover100 range is 0 to 1) + if (Unit == Units.percentover100) + return value * 100.0; + // no conversion + return value; + } /// /// Updates the object to either set pending update or no longer pending @@ -188,5 +386,10 @@ public bool PendingTimeout() { } return false; } + + public string ToDebugString() { + return $"{GetType()}: {{Def: {Def}; SimVarName: {SimVarName}; Unit: {Unit}; Cat: {CategoryId}; Name: {Name}}}"; + } + } } diff --git a/MSFSTouchPortalPlugin/Constants/Units.cs b/MSFSTouchPortalPlugin/Constants/Units.cs index e057de4..3ba63e0 100644 --- a/MSFSTouchPortalPlugin/Constants/Units.cs +++ b/MSFSTouchPortalPlugin/Constants/Units.cs @@ -5,25 +5,28 @@ namespace MSFSTouchPortalPlugin.Constants { internal static class Units { - private static readonly string[] _floatUnits = new string[] { - degrees, - radians, - knots, - feet, - MHz, - percent, - percentover100, - rpm, - mach, - feetminute, - }; + private static readonly string[] _integralUnits = new string[] { + Enum, mask, flags, position16k, position32k, position128, Bco16, second, seconds, minute, minutes, hour, hours, day, days, hourover10, hoursover10, year, years + }; + private static readonly string[] _booleanUnits = new string[] { Bool, Boolean }; + + /// + /// Returns true if the unit string corresponds to a string type. + /// + internal static bool IsStringType(string unit) => unit == String; + /// + /// Returns true if the unit string corresponds to an integer type. + /// + internal static bool IsIntegraltype(string unit) => _integralUnits.Contains(unit); + /// + /// Returns true if the unit string corresponds to a boolean type. + /// + internal static bool IsBooleantype(string unit) => _booleanUnits.Contains(unit); /// - /// All the unit types that should convert to Float + /// Returns true if the unit string corresponds to a real (float/double) type. /// - /// The unit type - /// True if it should be a float - internal static bool ShouldConvertToFloat(string unit) => _floatUnits.Contains(unit); + internal static bool IsRealType(string unit) => !IsStringType(unit) && !IsIntegraltype(unit); internal const string amp = "amp"; internal const string ampere = "ampere"; @@ -357,7 +360,7 @@ internal static class Units { internal const string squareyard = "square yard"; internal const string squareyards = "square yards"; internal const string sqyd = "sq yd"; - internal const string String = "String"; + internal const string String = "string"; internal const string third = "third"; internal const string thirds = "thirds"; internal const string times = "times"; diff --git a/MSFSTouchPortalPlugin/Enums/UpdateFreq.cs b/MSFSTouchPortalPlugin/Enums/UpdateFreq.cs new file mode 100644 index 0000000..53904ef --- /dev/null +++ b/MSFSTouchPortalPlugin/Enums/UpdateFreq.cs @@ -0,0 +1,14 @@ +namespace MSFSTouchPortalPlugin.Enums +{ + public enum UpdateFreq // SIMCONNECT_PERIOD + { + Never, + Once, + VisualFrame, + SimFrame, + Second, + Milliseconds, // custom frequency + + Default = SimFrame // default value (do not add anything below here) + } +} diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 1f36566..a34c674 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -17,7 +17,8 @@ using TouchPortalSDK.Messages.Events; using Timer = MSFSTouchPortalPlugin.Helpers.UnthreadedTimer; -namespace MSFSTouchPortalPlugin.Services { +namespace MSFSTouchPortalPlugin.Services +{ /// internal class PluginService : IPluginService, IDisposable, ITouchPortalEventHandler { private CancellationToken _cancellationToken; @@ -187,34 +188,17 @@ private void SimConnectEvent_OnConnect() { } private void SimConnectEvent_OnDataUpdateEvent(Definition def, Definition req, object data) { - // Lookup State Mapping - if (statesDictionary.TryGetValue(def, out SimVarItem value)) { - var stringVal = data.ToString(); - - // Only update state on changes - // TODO: Move these to after parsing due to fractional unnoticeable changes. - if (value.Value != stringVal) { - value.Value = stringVal; - object valObj = stringVal; - - // Handle conversions - if (Units.ShouldConvertToFloat(value.Unit)) { - valObj = float.Parse(stringVal); - } - if (value.Unit == Units.radians) { - // Convert to Degrees - valObj = (float)valObj * (180 / Math.PI); - } else if (value.Unit == Units.percentover100) { - // Convert to actual percentage (percentover100 range is 0 to 1) - valObj = (float)valObj * 100.0f; - } - - // Update TP state. - _client.StateUpdate(value.TouchPortalStateId, string.Format(value.StringFormat, valObj)); - } + // Lookup State Mapping. + if (!statesDictionary.TryGetValue(def, out SimVarItem simVar)) + return; - value.SetPending(false); - } + // Update SimVarItem value and TP state on changes. + // TODO: sim vars on a regular request interval will only be sent when changed, so skip the equality check. + // SimVarItem.SetValue() takes care of setting the correct value type and also any unit conversions as needed. Returns false if conversion failed. + if (!simVar.ValueEquals(data) && simVar.SetValue(data)) + _client.StateUpdate(simVar.TouchPortalStateId, simVar.FormattedValue); + // clear pending flag last + simVar.SetPending(false); } private void SimConnectEvent_OnDisconnect() { @@ -347,7 +331,7 @@ private void CheckPendingRequests() { if (s.PendingTimeout()) _logger.LogDebug($"Request for SimVar '{s.SimVarName}' timed out!"); - // Check if Pending data request in paly + // Check if Pending data request in play if (!s.PendingRequest) { s.SetPending(true); _simConnectService.RequestDataOnSimObjectType(s); diff --git a/MSFSTouchPortalPlugin/Services/SimConnectService.cs b/MSFSTouchPortalPlugin/Services/SimConnectService.cs index f886d62..e22b75a 100644 --- a/MSFSTouchPortalPlugin/Services/SimConnectService.cs +++ b/MSFSTouchPortalPlugin/Services/SimConnectService.cs @@ -12,7 +12,8 @@ using System.Threading; using System.Threading.Tasks; -namespace MSFSTouchPortalPlugin.Services { +namespace MSFSTouchPortalPlugin.Services +{ /// /// Wrapper for SimConnect /// @@ -152,17 +153,28 @@ public void SetNotificationGroupPriorities() { public bool RegisterToSimConnect(SimVarItem simVar) { if (_connected) { - bool isStrType = simVar.Unit == Units.String; - string unitName = isStrType ? null : simVar.Unit; - SIMCONNECT_DATATYPE scType = isStrType ? SIMCONNECT_DATATYPE.STRING64 : SIMCONNECT_DATATYPE.FLOAT64; - _simConnect.AddToDataDefinition(simVar.Def, simVar.SimVarName, unitName, scType, 0.0f, SimConnect.SIMCONNECT_UNUSED); - DbgAddSendRecord($"AddToDataDefinition(simVar.Def: {simVar.Def}; simVar.SimVarName: {simVar.SimVarName}; simVar.Unit: {simVar.Unit};"); - - if (isStrType) - _simConnect.RegisterDataDefineStruct(simVar.Def); - else - _simConnect.RegisterDataDefineStruct(simVar.Def); - DbgAddSendRecord($"RegisterDataDefineStruct<{(isStrType ? "String64" : "double")}>(simVar.Def: {simVar.Def}"); + string unitName = simVar.IsStringType ? null : simVar.Unit; + _simConnect.AddToDataDefinition(simVar.Def, simVar.SimVarName, unitName, simVar.SimConnectDataType, simVar.DeltaEpsilon, SimConnect.SIMCONNECT_UNUSED); + DbgAddSendRecord($"AddToDataDefinition({simVar.ToDebugString()}, {unitName}, {simVar.SimConnectDataType})"); + + switch (simVar.Value) { + case double: + _simConnect.RegisterDataDefineStruct(simVar.Def); + break; + case uint: + _simConnect.RegisterDataDefineStruct(simVar.Def); + break; + case long: + _simConnect.RegisterDataDefineStruct(simVar.Def); + break; + case string: + _simConnect.RegisterDataDefineStruct(simVar.Def); + break; + default: + _logger.LogError($"Unable to register storage type for '{simVar.StorageDataType}'"); + return false; + } + DbgAddSendRecord($"RegisterDataDefineStruct<{simVar.StorageDataType}>({simVar.ToDebugString()})"); return true; } @@ -174,7 +186,7 @@ public bool RequestDataOnSimObjectType(SimVarItem simVar) { if (_connected) { try { _simConnect.RequestDataOnSimObjectType(simVar.Def, simVar.Def, 0, SIMCONNECT_SIMOBJECT_TYPE.USER); - DbgAddSendRecord($"RequestDataOnSimObjectType(simVar.Def: {simVar.Def})"); + DbgAddSendRecord($"RequestDataOnSimObjectType({simVar.ToDebugString()})"); return true; } catch (Exception ex) { From 456571196a1afa25b4ae93e0edc1bb8e8670e398 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Wed, 30 Mar 2022 04:28:49 -0400 Subject: [PATCH 13/93] Add SharpConfig libs; Move SimConnect libs; Remove unnecessary SimConnect.lib; Update plugin project file. --- .../MSFSTouchPortalPlugin.csproj | 31 +++++++++++++++--- MSFSTouchPortalPlugin/SimConnect.lib | Bin 21892 -> 0 bytes .../Microsoft.FlightSimulator.SimConnect.dll | Bin .../lib/SharpConfig/CHANGELOG.txt | 4 +++ MSFSTouchPortalPlugin/lib/SharpConfig/LICENSE | 22 +++++++++++++ .../lib/SharpConfig/SharpConfig.dll | Bin 0 -> 34816 bytes .../lib/SharpConfig/SharpConfig.pdb | Bin 0 -> 21168 bytes .../{ => lib}/SimConnect.dll | Bin 8 files changed, 53 insertions(+), 4 deletions(-) delete mode 100644 MSFSTouchPortalPlugin/SimConnect.lib rename MSFSTouchPortalPlugin/{ => lib}/Microsoft.FlightSimulator.SimConnect.dll (100%) create mode 100644 MSFSTouchPortalPlugin/lib/SharpConfig/CHANGELOG.txt create mode 100644 MSFSTouchPortalPlugin/lib/SharpConfig/LICENSE create mode 100644 MSFSTouchPortalPlugin/lib/SharpConfig/SharpConfig.dll create mode 100644 MSFSTouchPortalPlugin/lib/SharpConfig/SharpConfig.pdb rename MSFSTouchPortalPlugin/{ => lib}/SimConnect.dll (100%) diff --git a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj index 980f42d..5c88416 100644 --- a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj +++ b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj @@ -18,8 +18,14 @@ x64 1701;1702;S125;CS8032 + + + .dll + + none + false - + @@ -54,19 +60,36 @@ + + + .\lib\SharpConfig\SharpConfig.dll + true + false + + + + - .\Microsoft.FlightSimulator.SimConnect.dll + .\lib\Microsoft.FlightSimulator.SimConnect.dll false true - + + PreserveNewest + SimConnect.dll + + + + + + PreserveNewest - + PreserveNewest diff --git a/MSFSTouchPortalPlugin/SimConnect.lib b/MSFSTouchPortalPlugin/SimConnect.lib deleted file mode 100644 index 561f59ee99ec4e741b78ad8f5f6c66b8fa7934ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21892 zcmd5@O>A4m7Cuc&DFsR>e+3E+P-qMNNz>X+`jgtRlhkSca9rLK3sTpV%Wb@) ztb~H^S5QhHN?FD{LKc6JvV?abW8X+Q{S|DXbF~Ie;%m@s2W&?IPq#Ny-B1 z5mLA-WOcQ;u)I_%uNJ0P@}=T(d8xFzQeV%vnoY0Th1J!?tMl_F2L2U+B&2-#>f)8v zLav+xlC)6S$T#X+2hYY1N7(+RE)pv$J0B5|ySR4n%aUc-@s- zovtVCSm{=}o~eTa5goZ&jhIkwEw;M#8}(|XTW>XI+pWzFQ)7G_ot@KxB^r^>*XWNJXZK`aM2{Q_k#T0@8wiryZ`6pu%&Y@QUZn)~ zsU*@aF+@YqdL^&pwQqX0D;_gG84S^|vU$BzZP%}RIJrjinpq4Bj%d2t?1Z{z+O2h` zHV2ky{F_(lu6gaC0L5n4Yj3m~IOEoqu8RWu6Q*fuvTRl!qC3|WHO@{5DhZn>8N(vZ zia43|L!@Oj`SNY7K`&0ViZZpx?jNT`MVVT#vy)n~wweZG^(?e%-iqk(ZI>E?vT7-> zdF#xi7&I>0B9uswRmW_jb-mJvvm`)Swd6ND-PU@XmH@?SDcovS*6Y=)8#Qe666=>l z}6P&?R7_OsVGy6>}7FUR20#YD@uapO8Y&pmMiCT z^>(#gxgk7NZJD)08GX}DblbXKB83c=(YV-ZCeo(i7)|(UGnh1ahaxPg4pelNJapeW z9y<2jc8lrlxuS;9atn{t=Fy0}BwfjgV=!!^nMVEHweE7G!ue-K9!PXezwcGCjTY*i z4$kPgDnGBYD553bTHk1RT~Ch07-cRn)!Arun8Aeu4_^~p$?LZ3-c6j)+x6zVW|!5{ zNzPfXJ72*?1~*a5mFj!wITg(89Ej*x+VGf)_ump!zS3wE>Ya^Bx4LFhD5)eDQZIV% zckLN<0LdHqHyf>H$1B&@y^hJuqKK9vW^Lmt%-5`vN^<2Z>t4HpT@O1yItn+vbYW@6 zBCCJ`NdBM|q;F_h zQy2`n+WBhU);MY9B zI|a;h8Q^2g^E2M>BKA8>D#|(c|XDWIts_&2)qt&z;QSM4?+$efwM3UQ}8G}3437_PC*tct|IVhJ)FTRA*S>seGD<={=DvUd z;VZ?VRv?MJU?mq$i;R~`A+aH26bdXrT_+*=>7VF9+T4j8YgIt7)qp%lk zaX+USMJ5F^+~%ZSxF0Nus*SS7?Fw8g6t|x6>r;#a?(l(a{%Nc;vjxyUu#gHxOcWgr zJC^(mLs7(qHD8DBqLyO!PpvMbzTuEtN7-zKQ8F>Ip0=sejR_J(7p1{?Vm^=11Lyp@ zYN~Pe=s*dL@|qejmF$1abm}!Lsmh*`q*z@wOmNf-rbZ)oD=e?-jR0~Fpr#siVbMY& z%$yoafn&Wd0w4^u=jeW4%qK(QrRjljQgDJ&oIU~x!gq3eA&M*_)3)?5MrNkVL1E7? z(_Q)f&u=6y$Z_}>Tg={?frN;27!puICbyWx|BJ6ge)$q2r;kMrrOBuf>?A%?+}2#mwwI#ZYy( z%uvTVX9kM6XqL1sN6nJXhZ;Di%}}7bZU#Vc;EdEQFHN*ug3y)j%y9}Mrzq^u(t9$o z#Ids_hVC7kPMkbPx>W@qcQ)NVL$~4lF^FI#6Gx0h-I`sPck`0soM$c)#$*zVGsZ!* zl~r{UZ7AwHF&M>VG}3YQjV2?rro=L=xz(JC$Vc3A|J*R?19BLn2Avv-(`Q%dtRUh+ zMs3VgM0i{x=IDhb_?YA{8*wUi*VCvdf>j()1IW6eZdB+@+EA)X>PE%u(%na9bT<;=IP7lAT;gcG5z+vkpR1T?tQro~$(*r$%B=H;<-+ zbI#ZH)^6{Fzwp|>1LMsYPZih6nss&u)Lnc z+ka5-n5%#7#J^b>y%XRt8Z572ybU2=h!bzKFmh)I?;_qRu6>c4FA8-8o)~@*Ukml( z+K0i}&+7XZ>eY6u)4I_eIa)e4GGAO2A#~$<1m6f-gkZGW|4-QPfd8LLgqBp%*Fuv& ztMAzH*IzUj!a>+L1Yyau81xoz*1|V_86{VMIV&&-i7=_?jd=fx_!C@07sl9wCvh8s zzvJJ2w=~Jl3@7U~W2rNw*9`f+=HI@B!cM-|)MV1uU(##v`+>YfulXW;2=1cS+^^n{ zq_o_W6e!PuMjFFm?G5?#9E;R3kJ0y6LvGr0?`qMcni!hgl2^L7~j`3;ZJBZ8^j{9leYmWl=y~9Nm?)2 zJe9WehBwfHUAhHY;6$VDy$@{tu1M{P$B_F0lbeKO24!~D`)-|?5B6v#sE_e2KW=Gx zv6?-oSr8jVQptPn1Fn&<$FL>b&5J!2i3AO3LppS_w2^QU>*FEDhV5lfS!San$i7!) zR|0BUp?e@W-odnYiP#@!ap{wN|(fF+~K$W!T-sfuhM;RNox8>88 zRp51Ofqe{n6gAKasy)Wny=w)0jA1r{3>staYjDa^_7N=mux^2RR8%Z38h?C=GiUiV zUu**&*Z3R}+(G-BW5wIAS)qr9HR|5Sdxoz8Ji>&hY80=B4LT3XpR(}3iu_Ni{3M(` zC~MlW&OgPmZUxE*Wp0GA(CTCSD09td2Ml)QRJ2Of z^Z0hHqR*<#%I64;w)gsWtfJ4Uy!?j_%>eZ=zQ!|_{w+4`&s+HQFDNQ!?{m3pS9j2` z!S-!tP%BcFmUsfs7Yu&|s$YdrTss?7Ztfj}X zrbledmSU^6#MgJq(%%mu_e(0bc9^Qq`P5O?msLLVtDRc7p64{z{`eJ+I~rOVS0BC0 zQSQS!xBj7V&~jULL?S-(D$6bWa{ee=f35DC(Oyepfp%1`KP%Ff8BOfaj<8llU*!Cg z@B_AkYi&8o@^Ak>A)VsA))QC<$5DOKJ)7~cJ`!VpZt!}zrpg;Z?{_W z_wL(R=2{UWJFc@UpUnqmwnV6oBJ&BAS^Xfd5vz~!tBb3*oiup2d)9G{_frOY6b;z= z;;$B0TLT6w{iuUqo+Qx7d+u!+OJB#InBZ?bl>I5TxiQn&DxqgPTrr_B!w%y>g;ovf zV}dqtjJY!w_U);ESMNKkF-M0xq7_H$ZrWplk~>!Yl$D>3!a^w3zswFvVKK9(B{qP#@#l$u)Q6ZB~#obSmHW&Rqo;zwrZ; sK37o3+&%5$H-5mUK_99kWB)Zm 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

(6pJshBzK{b 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 literal 0 HcmV?d00001 diff --git a/MSFSTouchPortalPlugin/SimConnect.dll b/MSFSTouchPortalPlugin/lib/SimConnect.dll similarity index 100% rename from MSFSTouchPortalPlugin/SimConnect.dll rename to MSFSTouchPortalPlugin/lib/SimConnect.dll From 328f624d8989d22715636bcb2a8e45e250df6822 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 01:15:20 -0400 Subject: [PATCH 14/93] Add initial version of PluginConfig class. --- .../Configuration/PluginConfig.cs | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 MSFSTouchPortalPlugin/Configuration/PluginConfig.cs diff --git a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs new file mode 100644 index 0000000..45b07e6 --- /dev/null +++ b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using MSFSTouchPortalPlugin.Enums; +using System.Linq; +using System.IO; +using System.Text; +using MSFSTouchPortalPlugin.Constants; +//using SharpConfig; + +namespace MSFSTouchPortalPlugin.Configuration +{ + + internal class PluginConfig + { + + public static PluginConfig Instance { + get { + if (_instance == null) + _instance = new PluginConfig(); + return _instance; + } + } + + ///

+ /// RootName is used as the basis for the user folder name and TP State ID generation. + /// + public static string RootName { get; set; } = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; + + public static string StatesConfigFile { get; set; } = "States.ini"; + public static string PluginStatesConfigFile { get; set; } = "PluginStates.ini"; + + public static string AppConfigFolder { get; set; } = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "Configuration"); + public static string UserConfigFolder { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), RootName); + + public IReadOnlyCollection ErrorsList => _errorsList; + public bool HaveErrors => _errorsList.Any(); + + private static PluginConfig _instance; + private readonly List _errorsList = new(); + + private PluginConfig() + { + // set SC writing options + SharpConfig.Configuration.SpaceBetweenEquals = true; + SharpConfig.Configuration.AlwaysQuoteStringValues = true; // custom SharpConfig v3.2.9.2-mp feature + } + + public IReadOnlyCollection LoadSimVarItems(bool isUserConfig = true, string filename = default) { + List ret = new(); + if (filename == default) + filename = StatesConfigFile; + _errorsList.Clear(); + string filepath = Path.Combine(isUserConfig ? UserConfigFolder : AppConfigFolder, filename); + if (!File.Exists(filepath)) { + _errorsList.Add(new FileNotFoundException("Configuration file not found.", filepath)); + return ret; + } + SharpConfig.Configuration cfg; + try { + cfg = SharpConfig.Configuration.LoadFromFile(filepath, Encoding.UTF8); + } + catch (Exception e) { + _errorsList.Add(e); + return ret; + } + foreach (SharpConfig.Section item in cfg) { + if (item.Name == SharpConfig.Section.DefaultSectionName) + continue; + SimVarItem simVar; + try { + simVar = item.ToObject(); + } + catch (Exception e) { + _errorsList.Add(e); + continue; + } + simVar.Id = item.Name; + // check unique + if (ret.FirstOrDefault(s => s.Id == simVar.Id) != null) { + _errorsList.Add(new Exception($"Duplicate SimVar ID found for '{simVar.Id}'")); + continue; + } + simVar.TouchPortalStateId = $"{RootName}.{simVar.CategoryId}.State.{simVar.Id}"; + ret.Add(simVar); + } + + return ret; + } + + public IReadOnlyCollection LoadPluginStates() + => LoadSimVarItems(false, PluginStatesConfigFile); + + public bool SaveSimVarItems(IReadOnlyCollection items, bool isUserConfig = true, string filename = default) { + var cfg = new SharpConfig.Configuration(); + Groups lastCatId = default; + _errorsList.Clear(); + + foreach (SimVarItem item in items) { + try { + var sect = cfg.Add(item.Id); + if (item.CategoryId != lastCatId) { + sect.PreComment = " Category: " + item.CategoryId.ToString() + " " + new string('#', 30) + '\n'; + lastCatId = item.CategoryId; + } + + sect.Add("CategoryId", item.CategoryId); + sect.Add("Name", item.Name); + sect.Add("SimVarName", item.SimVarName); + sect.Add("Unit", item.Unit); + if (!string.IsNullOrWhiteSpace(item.DefaultValue)) + sect.Add("DefaultValue", item.DefaultValue); + if (!string.IsNullOrWhiteSpace(item.FormattingString)) + sect.Add("StringFormat", item.FormattingString); + if (item.CanSet) + sect.Add("CanSet", item.CanSet); + if (item.UpdateFreqency != UpdateFreq.Default) { + sect.Add("UpdateFreqency", item.UpdateFreqency); + if (item.UpdateFreqency == UpdateFreq.Milliseconds) + sect.Add("UpdateInterval", item.UpdateInterval); + } + if (item.DeltaEpsilon != 0.0f) + sect.Add("DeltaEpsilon", item.DeltaEpsilon); + } + catch (Exception e) { + _errorsList.Add(e); + } + } + + if (filename == default) + filename = StatesConfigFile; + SaveToFile(cfg, isUserConfig ? UserConfigFolder : AppConfigFolder, filename); + + return HaveErrors; + } + + private void SaveToFile(SharpConfig.Configuration cfg, string folder, string filename) { + try { + Directory.CreateDirectory(folder); + cfg.SaveToFile(Path.Combine(folder, filename), Encoding.UTF8); + } + catch (Exception e) { + _errorsList.Add(e); + } + } + + } + +} From 038a113b67d632f0f5d4081b8963e3a1c09f7900 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 01:16:34 -0400 Subject: [PATCH 15/93] Add default state config files with all SimVar states as of current plugin version. --- .../Configuration/PluginStates.ini | 14 + .../Configuration/States.ini | 970 ++++++++++++++++++ 2 files changed, 984 insertions(+) create mode 100644 MSFSTouchPortalPlugin/Configuration/PluginStates.ini create mode 100644 MSFSTouchPortalPlugin/Configuration/States.ini diff --git a/MSFSTouchPortalPlugin/Configuration/PluginStates.ini b/MSFSTouchPortalPlugin/Configuration/PluginStates.ini new file mode 100644 index 0000000..2232838 --- /dev/null +++ b/MSFSTouchPortalPlugin/Configuration/PluginStates.ini @@ -0,0 +1,14 @@ +# This file describes Touch Portal states which are not tied to any SimConnect variable but are only used for internal plugin data. +# Primarily used for generating entry.tp and docs. Only modify when actually adding new data states in plugin code. + +# Category: Plugin ############################## +# +[ActionRepeatInterval] +CategoryId = Plugin +Name = "The current Held Action Repeat Rate (ms)" +DefaultValue = "450" + +[Connected] +CategoryId = Plugin +Name = "The status of SimConnect (true/false/connecting)" +DefaultValue = "false" diff --git a/MSFSTouchPortalPlugin/Configuration/States.ini b/MSFSTouchPortalPlugin/Configuration/States.ini new file mode 100644 index 0000000..eaadd58 --- /dev/null +++ b/MSFSTouchPortalPlugin/Configuration/States.ini @@ -0,0 +1,970 @@ +######################################### +# Configuration file for specifying SimConnect variables which will be available as Touch Portal states. +# Standard .ini file format: +# # and ; chars start a comment +# Trailing (in-line) comments are allowed +# Blank lines are ignored +# Spaces around key=value = signs are optional +# Strings with spaces or comments chars _must_ be quoted +# Escape literal quotes with backslash (\") +# +# An example entry describing each possible parameter (some are required, others have reasonable default values): +# +# [RPMN1Engine1] ; Unique ID string for this state (this will be used as the last part of the Touch Portal State ID). Required. +# CategoryId = Engine ; The category ID this state will be sorted into. Must be a known category ID. Required. +# ; Available categories: AutoPilot, Communication, Electrical, Engine, Environment, Failures, FlightInstruments, FlightSystems, Fuel, SimSystem +# Name = "RPM - Engine 1" ; Descriptive name for this state, shown in Touch Portal and maybe other UI. Required. +# SimVarName = "ENG N1 RPM:1" ; Name of corresponding SimConnect variable (as detailed in https://docs.flightsimulator.com/html/Programming_Tools/SimVars/Simulation_Variables.htm). Required. +# Unit = "percent" ; Expected SimConnect unit type name for this SimVar. Required. +# ; This is generally the text shown in "Units" column of SimConnect variables reference, but when in doubt check the Units.cs file of this project. +# ; Another useful reference is https://docs.flightsimulator.com/html/Programming_Tools/SimVars/Simulation_Variable_Units.htm +# ; Some common ones: "Bool", "knots", "feet", "degrees", "radians", "percent, "percent over 100", "MHz", "string" +# CanSet = True ; Indicates if this SimConnect variable is settable (as per SimConnect specs, for future use). Optional, default is False; +# DefaultValue = "0" ; Default value when no data has been received from SimConnect. Optional, default is blank. +# StringFormat = "0.0#" ; How to format the value for display. Optional, default is no special formatting, just show the value as-is. +# ; Formatting string references: +# ; https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings +# ; https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings +# UpdateFreqency = Milliseconds ; Determines how often the SimConnect value is checked/updated. Optional, default is SimFrame. +# ; Must be one of the following: Never, Once, SimFrame, VisualFrame, Second, Milliseconds, Default (same as SimFrame) +# UpdateInterval = 100 ; The update frequency (in ms) to use when UpdateFreqency is set to Milliseconds. Required when UpdateFreqency = Milliseconds, ignored otherwise. +# DeltaEpsilon = 1.0 ; Only report change if it is greater than the value of this parameter. Optional, default is any change (0). +# +########################################## + +# Category: AutoPilot ############################## +# +[AutoPilotMaster] +CategoryId = AutoPilot +Name = "AutoPilot Master Status" +SimVarName = "AUTOPILOT MASTER" +Unit = "Bool" + +[AutoPilotAvailable] +CategoryId = AutoPilot +Name = "AutoPilot Availability" +SimVarName = "AUTOPILOT AVAILABLE" +Unit = "Bool" + +[AutoPilotPitchHold] +CategoryId = AutoPilot +Name = "The status of Auto Pilot Pitch Hold button" +SimVarName = "AUTOPILOT PITCH HOLD" +Unit = "Bool" + +[AutoPilotAttitudeHold] +CategoryId = AutoPilot +Name = "AutoPilot Attitude Status" +SimVarName = "AUTOPILOT ATTITUDE HOLD" +Unit = "Bool" + +[AutoPilotAttitudeVar] +CategoryId = AutoPilot +Name = "AutoPilot Pitch Reference Value" +SimVarName = "AUTOPILOT PITCH HOLD REF" +Unit = "radians" + +[AutoPilotApproachHold] +CategoryId = AutoPilot +Name = "AutoPilot Approach Status" +SimVarName = "AUTOPILOT APPROACH HOLD" +Unit = "Bool" + +[AutoPilotBanking] +CategoryId = AutoPilot +Name = "AutoPilot Max Bank Angle" +SimVarName = "AUTOPILOT MAX BANK" +Unit = "radians" + +[AutoPilotHeadingHold] +CategoryId = AutoPilot +Name = "AutoPilot Heading Status" +SimVarName = "AUTOPILOT HEADING LOCK" +Unit = "Bool" + +[AutoPilotHeadingVar] +CategoryId = AutoPilot +Name = "AutoPilot Heading Direction" +SimVarName = "AUTOPILOT HEADING LOCK DIR" +Unit = "degrees" +StringFormat = "F0" + +[AutoPilotAltitudeHold] +CategoryId = AutoPilot +Name = "AutoPilot Altitude Status" +SimVarName = "AUTOPILOT ALTITUDE LOCK" +Unit = "Bool" + +[AutoPilotAltitudeVar] +CategoryId = AutoPilot +Name = "AutoPilot Altitude Value" +SimVarName = "AUTOPILOT ALTITUDE LOCK VAR" +Unit = "feet" + +[AutoPilotBackCourseHold] +CategoryId = AutoPilot +Name = "AutoPilot Back Course Status" +SimVarName = "AUTOPILOT BACKCOURSE HOLD" +Unit = "Bool" + +[AutoPilotNav1Hold] +CategoryId = AutoPilot +Name = "AutoPilot Nav1 Status" +SimVarName = "AUTOPILOT NAV1 LOCK" +Unit = "Bool" + +[AutoPilotNavSelected] +CategoryId = AutoPilot +Name = "AutoPilot Nav Selected Index" +SimVarName = "AUTOPILOT NAV SELECTED" +Unit = "number" + +[AutoPilotVerticalSpeedHold] +CategoryId = AutoPilot +Name = "AutoPilot Vertical Speed Status" +SimVarName = "AUTOPILOT VERTICAL HOLD" +Unit = "Bool" + +[AutoPilotVerticalSpeedVar] +CategoryId = AutoPilot +Name = "AutoPilot Vertical Speed Value" +SimVarName = "AUTOPILOT VERTICAL HOLD VAR" +Unit = "feet/minute" + +[AutoPilotAirSpeedHold] +CategoryId = AutoPilot +Name = "AutoPilot Air Speed Status" +SimVarName = "AUTOPILOT AIRSPEED HOLD" +Unit = "Bool" + +[AutoPilotAirSpeedVar] +CategoryId = AutoPilot +Name = "AutoPilot Air Speed Value" +SimVarName = "AUTOPILOT AIRSPEED HOLD VAR" +Unit = "knots" +StringFormat = "0.0#" + +[AutoThrottleArm] +CategoryId = AutoPilot +Name = "Auto Throttle Armed" +SimVarName = "AUTOPILOT THROTTLE ARM" +Unit = "Bool" + +[AutoThrottleGoAround] +CategoryId = AutoPilot +Name = "Auto Throttle GoAround" +SimVarName = "AUTOPILOT TAKEOFF POWER ACTIVE" +Unit = "Bool" + +[AutoPilotMach] +CategoryId = AutoPilot +Name = "AutoPilot Mach Hold" +SimVarName = "AUTOPILOT MACH HOLD" +Unit = "Bool" + +[AutoPilotMachVar] +CategoryId = AutoPilot +Name = "AutoPilot Mach Value" +SimVarName = "AUTOPILOT MACH HOLD VAR" +Unit = "number" + +[AutoPilotFlightDirector] +CategoryId = AutoPilot +Name = "AutoPilot Flight Director Status" +SimVarName = "AUTOPILOT FLIGHT DIRECTOR ACTIVE" +Unit = "Bool" + +[AutoPilotFlightDirectorCurrentBank] +CategoryId = AutoPilot +Name = "Flight Director Current Bank" +SimVarName = "AUTOPILOT FLIGHT DIRECTOR BANK" +Unit = "radians" + +[AutoPilotFlightDirectorCurrentPitch] +CategoryId = AutoPilot +Name = "Flight Director Current Pitch" +SimVarName = "AUTOPILOT FLIGHT DIRECTOR PITCH" +Unit = "radians" + +[AutoPilotWingLeveler] +CategoryId = AutoPilot +Name = "AutoPilot Wing Leveler" +SimVarName = "AUTOPILOT WING LEVELER" +Unit = "Bool" + +[AutoPilotYawDampener] +CategoryId = AutoPilot +Name = "Yaw Dampener Status" +SimVarName = "AUTOPILOT YAW DAMPER" +Unit = "Bool" + + +# Category: Communication ############################## +# +[Com1ActiveFrequency] +CategoryId = Communication +Name = "The frequency of the active COM1 radio" +SimVarName = "COM ACTIVE FREQUENCY:1" +Unit = "MHz" +StringFormat = "0.000#" + +[Com1StandbyFrequency] +CategoryId = Communication +Name = "The frequency of the standby COM1 radio" +SimVarName = "COM STANDBY FREQUENCY:1" +Unit = "MHz" +StringFormat = "0.000#" + +[Com2ActiveFrequency] +CategoryId = Communication +Name = "The frequency of the active COM2 radio" +SimVarName = "COM ACTIVE FREQUENCY:2" +Unit = "MHz" +StringFormat = "0.000#" + +[Com2StandbyFrequency] +CategoryId = Communication +Name = "The frequency of the standby COM2 radio" +SimVarName = "COM STANDBY FREQUENCY:2" +Unit = "MHz" +StringFormat = "0.000#" + +[Nav1ActiveFrequency] +CategoryId = Communication +Name = "The frequency of the active NAV1 radio" +SimVarName = "NAV ACTIVE FREQUENCY:1" +Unit = "MHz" +StringFormat = "0.000#" + +[Nav1StandbyFrequency] +CategoryId = Communication +Name = "The frequency of the standby NAV1 radio" +SimVarName = "NAV STANDBY FREQUENCY:1" +Unit = "MHz" +StringFormat = "0.000#" + +[Nav2ActiveFrequency] +CategoryId = Communication +Name = "The frequency of the active NAV2 radio" +SimVarName = "NAV ACTIVE FREQUENCY:2" +Unit = "MHz" +StringFormat = "0.000#" + +[Nav2StandbyFrequency] +CategoryId = Communication +Name = "The frequency of the standby NAV2 radio" +SimVarName = "NAV STANDBY FREQUENCY:2" +Unit = "MHz" +StringFormat = "0.000#" + + +# Category: Electrical ############################## +# +[AvionicsMasterSwitch] +CategoryId = Electrical +Name = "Avionics Master Switch" +SimVarName = "AVIONICS MASTER SWITCH" +Unit = "Bool" + +[MasterAlternator] +CategoryId = Electrical +Name = "Master Alternator Status" +SimVarName = "GENERAL ENG MASTER ALTERNATOR:1" +Unit = "Bool" + +[MasterBattery] +CategoryId = Electrical +Name = "Master Battery Status" +SimVarName = "ELECTRICAL MASTER BATTERY" +Unit = "Bool" + +[LightBeaconOn] +CategoryId = Electrical +Name = "Light Beacon Switch Status" +SimVarName = "LIGHT BEACON" +Unit = "Bool" + +[LightBrakeOn] +CategoryId = Electrical +Name = "Light Brake Switch or Light Status" +SimVarName = "LIGHT BRAKE ON" +Unit = "Bool" + +[LightCabinOn] +CategoryId = Electrical +Name = "Light Cabin Switch Status" +SimVarName = "LIGHT CABIN" +Unit = "Bool" + +[LightHeadOn] +CategoryId = Electrical +Name = "Light Head Switch or Light Status" +SimVarName = "LIGHT HEAD ON" +Unit = "Bool" + +[LightLandingOn] +CategoryId = Electrical +Name = "Light Landing Switch Status" +SimVarName = "LIGHT LANDING" +Unit = "Bool" + +[LightLogoOn] +CategoryId = Electrical +Name = "Light Logo Switch Status" +SimVarName = "LIGHT LOGO" +Unit = "Bool" + +[LightNavOn] +CategoryId = Electrical +Name = "Light Nav Switch Status" +SimVarName = "LIGHT NAV" +Unit = "Bool" + +[LightPanelOn] +CategoryId = Electrical +Name = "Light Panel Switch Status" +SimVarName = "LIGHT PANEL" +Unit = "Bool" + +[LightRecognitionOn] +CategoryId = Electrical +Name = "Light Recognition Switch Status" +SimVarName = "LIGHT RECOGNITION" +Unit = "Bool" + +[LightStrobeOn] +CategoryId = Electrical +Name = "Light Strobe Switch Status" +SimVarName = "LIGHT STROBE" +Unit = "Bool" + +[LightTaxiOn] +CategoryId = Electrical +Name = "Light Taxi Switch Status" +SimVarName = "LIGHT TAXI" +Unit = "Bool" + +[LightWingOn] +CategoryId = Electrical +Name = "Light Wing Switch Status" +SimVarName = "LIGHT WING" +Unit = "Bool" + + +# Category: Fuel ############################## +# +# No states + + +# Category: Engine ############################## +# +[MasterIgnitionSwitch] +CategoryId = Engine +Name = "Master Ignition Switch Status" +SimVarName = "MASTER IGNITION SWITCH" +Unit = "Bool" + +[ThrottleEngine1] +CategoryId = Engine +Name = "Throttle - Engine 1 - Percentage" +SimVarName = "GENERAL ENG THROTTLE LEVER POSITION:1" +Unit = "percent" +StringFormat = "0.#" +CanSet = True + +[ThrottleEngine2] +CategoryId = Engine +Name = "Throttle - Engine 2 - Percentage" +SimVarName = "GENERAL ENG THROTTLE LEVER POSITION:2" +Unit = "percent" +StringFormat = "0.#" +CanSet = True + +[ThrottleEngine3] +CategoryId = Engine +Name = "Throttle - Engine 3 - Percentage" +SimVarName = "GENERAL ENG THROTTLE LEVER POSITION:3" +Unit = "percent" +StringFormat = "0.#" +CanSet = True + +[ThrottleEngine4] +CategoryId = Engine +Name = "Throttle - Engine 4 - Percentage" +SimVarName = "GENERAL ENG THROTTLE LEVER POSITION:4" +Unit = "percent" +StringFormat = "0.#" +CanSet = True + +[MixtureEngine1] +CategoryId = Engine +Name = "Mixture - Engine 1 - Percentage" +SimVarName = "GENERAL ENG MIXTURE LEVER POSITION:1" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[MixtureEngine2] +CategoryId = Engine +Name = "Mixture - Engine 2 - Percentage" +SimVarName = "GENERAL ENG MIXTURE LEVER POSITION:2" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[MixtureEngine3] +CategoryId = Engine +Name = "Mixture - Engine 3 - Percentage" +SimVarName = "GENERAL ENG MIXTURE LEVER POSITION:3" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[MixtureEngine4] +CategoryId = Engine +Name = "Mixture - Engine 4 - Percentage" +SimVarName = "GENERAL ENG MIXTURE LEVER POSITION:4" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[PropellerEngine1] +CategoryId = Engine +Name = "Propeller - Engine 1 - Percentage" +SimVarName = "GENERAL ENG PROPELLER LEVER POSITION:1" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[PropellerEngine2] +CategoryId = Engine +Name = "Propeller - Engine 2 - Percentage" +SimVarName = "GENERAL ENG PROPELLER LEVER POSITION:2" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[PropellerEngine3] +CategoryId = Engine +Name = "Propeller - Engine 3 - Percentage" +SimVarName = "GENERAL ENG PROPELLER LEVER POSITION:3" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[PropellerEngine4] +CategoryId = Engine +Name = "Propeller - Engine 4 - Percentage" +SimVarName = "GENERAL ENG PROPELLER LEVER POSITION:4" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[Propeller1FeatherSw] +CategoryId = Engine +Name = "Propeller - Engine 1 - Feather Switch State (bool)" +SimVarName = "PROP FEATHER SWITCH:1" +Unit = "Bool" + +[Propeller2FeatherSw] +CategoryId = Engine +Name = "Propeller - Engine 2 - Feather Switch State (bool)" +SimVarName = "PROP FEATHER SWITCH:2" +Unit = "Bool" + +[Propeller3FeatherSw] +CategoryId = Engine +Name = "Propeller - Engine 3 - Feather Switch State (bool)" +SimVarName = "PROP FEATHER SWITCH:3" +Unit = "Bool" + +[Propeller4FeatherSw] +CategoryId = Engine +Name = "Propeller - Engine 4 - Feather Switch State (bool)" +SimVarName = "PROP FEATHER SWITCH:4" +Unit = "Bool" + +[Propeller1Feathered] +CategoryId = Engine +Name = "Propeller - Engine 1 - Feathered (bool)" +SimVarName = "PROP FEATHERED:1" +Unit = "Bool" + +[Propeller2Feathered] +CategoryId = Engine +Name = "Propeller - Engine 2 - Feathered (bool)" +SimVarName = "PROP FEATHERED:2" +Unit = "Bool" + +[Propeller3Feathered] +CategoryId = Engine +Name = "Propeller - Engine 3 - Feathered (bool)" +SimVarName = "PROP FEATHERED:3" +Unit = "Bool" + +[Propeller4Feathered] +CategoryId = Engine +Name = "Propeller - Engine 4 - Feathered (bool)" +SimVarName = "PROP FEATHERED:4" +Unit = "Bool" + +[RPMN1Engine1] +CategoryId = Engine +Name = "RPM - Engine 1" +SimVarName = "ENG N1 RPM:1" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[RPMN1Engine2] +CategoryId = Engine +Name = "RPM - Engine 2" +SimVarName = "ENG N1 RPM:2" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[RPMN1Engine3] +CategoryId = Engine +Name = "RPM - Engine 3" +SimVarName = "ENG N1 RPM:3" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[RPMN1Engine4] +CategoryId = Engine +Name = "RPM - Engine 4" +SimVarName = "ENG N1 RPM:4" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[RPMPropeller1] +CategoryId = Engine +Name = "RPM - Propeller 1" +SimVarName = "PROP RPM:1" +Unit = "rpm" +StringFormat = "0.0#" +CanSet = True + +[RPMPropeller2] +CategoryId = Engine +Name = "RPM - Propeller 2" +SimVarName = "PROP RPM:2" +Unit = "rpm" +StringFormat = "0.0#" +CanSet = True + +[RPMPropeller3] +CategoryId = Engine +Name = "RPM - Propeller 3" +SimVarName = "PROP RPM:3" +Unit = "rpm" +StringFormat = "0.0#" +CanSet = True + +[RPMPropeller4] +CategoryId = Engine +Name = "RPM - Propeller 4" +SimVarName = "PROP RPM:4" +Unit = "rpm" +StringFormat = "0.0#" +CanSet = True + + +# Category: Environment ############################## +# +[AntiIceEng1] +CategoryId = Environment +Name = "Anti-Ice Engine 1" +SimVarName = "GENERAL ENG ANTI ICE POSITION:1" +Unit = "Bool" + +[AntiIceEng2] +CategoryId = Environment +Name = "Anti-Ice Engine 2" +SimVarName = "GENERAL ENG ANTI ICE POSITION:2" +Unit = "Bool" + +[AntiIceEng3] +CategoryId = Environment +Name = "Anti-Ice Engine 3" +SimVarName = "GENERAL ENG ANTI ICE POSITION:3" +Unit = "Bool" + +[AntiIceEng4] +CategoryId = Environment +Name = "Anti-Ice Engine 4" +SimVarName = "GENERAL ENG ANTI ICE POSITION:4" +Unit = "Bool" + +[AntiIcePanelSwitch] +CategoryId = Environment +Name = "Panel Anti-Ice Switch" +SimVarName = "PANEL ANTI ICE SWITCH" +Unit = "Bool" + +[AntiIceStructuralSwitch] +CategoryId = Environment +Name = "Structural Deice Switch" +SimVarName = "STRUCTURAL DEICE SWITCH" +Unit = "Bool" + +[AntiIceWindshieldSwitch] +CategoryId = Environment +Name = "Windshield Deice Switch" +SimVarName = "WINDSHIELD DEICE SWITCH" +Unit = "Bool" + +[AntiIcePropeller1Switch] +CategoryId = Environment +Name = "Propeller 1 Deice Switch" +SimVarName = "PROP DEICE SWITCH:1" +Unit = "Bool" + +[AntiIcePropeller2Switch] +CategoryId = Environment +Name = "Propeller 2 Deice Switch" +SimVarName = "PROP DEICE SWITCH:2" +Unit = "Bool" + +[AntiIcePropeller3Switch] +CategoryId = Environment +Name = "Propeller 3 Deice Switch" +SimVarName = "PROP DEICE SWITCH:3" +Unit = "Bool" + +[AntiIcePropeller4Switch] +CategoryId = Environment +Name = "Propeller 4 Deice Switch" +SimVarName = "PROP DEICE SWITCH:4" +Unit = "Bool" + +[PitotHeat] +CategoryId = Environment +Name = "Pitot Heat Status" +SimVarName = "PITOT HEAT" +Unit = "Bool" + +[PitotHeatSwitch1] +CategoryId = Environment +Name = "Pitot Heat Switch 1 State (0=Off; 1=On; 2=Auto)" +SimVarName = "PITOT HEAT SWITCH:1" +Unit = "Enum" + +[PitotHeatSwitch2] +CategoryId = Environment +Name = "Pitot Heat Switch 2 State (0=Off; 1=On; 2=Auto)" +SimVarName = "PITOT HEAT SWITCH:2" +Unit = "Enum" + +[PitotHeatSwitch3] +CategoryId = Environment +Name = "Pitot Heat Switch 3 State (0=Off; 1=On; 2=Auto)" +SimVarName = "PITOT HEAT SWITCH:3" +Unit = "Enum" + +[PitotHeatSwitch4] +CategoryId = Environment +Name = "Pitot Heat Switch 4 State (0=Off; 1=On; 2=Auto)" +SimVarName = "PITOT HEAT SWITCH:4" +Unit = "Enum" + + +# Category: FlightInstruments ############################## +# +[GroundVelocity] +CategoryId = FlightInstruments +Name = "Ground Speed in Knots" +SimVarName = "GROUND VELOCITY" +Unit = "knots" +StringFormat = "0.0#" + +[AirSpeedTrue] +CategoryId = FlightInstruments +Name = "Air speed true in Knots" +SimVarName = "AIRSPEED TRUE" +Unit = "knots" +StringFormat = "0.0#" +CanSet = True + +[AirSpeedIndicated] +CategoryId = FlightInstruments +Name = "Air speed indicated in Knots" +SimVarName = "AIRSPEED INDICATED" +Unit = "knots" +StringFormat = "0.0#" +CanSet = True + +[AirSpeedMach] +CategoryId = FlightInstruments +Name = "Air speed indicated in Mach" +SimVarName = "AIRSPEED MACH" +Unit = "mach" +StringFormat = "0.0#" +CanSet = True + +[PlaneAltitude] +CategoryId = FlightInstruments +Name = "Plane Altitude in Feet" +SimVarName = "PLANE ALTITUDE" +Unit = "feet" +StringFormat = "0.#" + +[PlaneAltitudeAGL] +CategoryId = FlightInstruments +Name = "Plane Altitude AGL in Feet" +SimVarName = "PLANE ALT ABOVE GROUND" +Unit = "feet" +StringFormat = "0.#" + +[GroundAltitude] +CategoryId = FlightInstruments +Name = "Ground level in Feet" +SimVarName = "GROUND ALTITUDE" +Unit = "feet" +StringFormat = "0.#" + +[PlaneHeadingTrue] +CategoryId = FlightInstruments +Name = "Plane Heading (True North) in Degrees" +SimVarName = "PLANE HEADING DEGREES TRUE" +Unit = "radians" +StringFormat = "0" + +[PlaneHeadingMagnetic] +CategoryId = FlightInstruments +Name = "Plane Heading (Magnetic North) in Degrees" +SimVarName = "PLANE HEADING DEGREES MAGNETIC" +Unit = "radians" +StringFormat = "0" + +[PlaneBankAngle] +CategoryId = FlightInstruments +Name = "Plane Bank Angle in Degrees" +SimVarName = "PLANE BANK DEGREES" +Unit = "radians" +StringFormat = "0" + +[PlanePitchAngle] +CategoryId = FlightInstruments +Name = "Plane Pitch Angle in Degrees" +SimVarName = "PLANE PITCH DEGREES" +Unit = "radians" +StringFormat = "0" + +[VerticalSpeed] +CategoryId = FlightInstruments +Name = "Vertical Speed in feet per minute" +SimVarName = "VERTICAL SPEED" +Unit = "feet/minute" +StringFormat = "0.0#" +CanSet = True + +[StallWarning] +CategoryId = FlightInstruments +Name = "Stall Warning true/false" +SimVarName = "STALL WARNING" +Unit = "Bool" + +[OverspeedWarning] +CategoryId = FlightInstruments +Name = "Overspeed Warning true/false" +SimVarName = "OVERSPEED WARNING" +Unit = "Bool" + +[FlapSpeedExceeeded] +CategoryId = FlightInstruments +Name = "Flap Speed Exceeded Warning true/false" +SimVarName = "FLAP SPEED EXCEEDED" +Unit = "Bool" + + +# Category: FlightSystems ############################## +# +[ParkingBrakeIndicator] +CategoryId = FlightSystems +Name = "Parking Brake Indicator true/false" +SimVarName = "BRAKE PARKING POSITION" +Unit = "Bool" + +[FlapsHandlePercent] +CategoryId = FlightSystems +Name = "Flaps Handle Percentage" +SimVarName = "FLAPS HANDLE PERCENT" +Unit = "percent" +StringFormat = "0.0#" + +[CowlFlaps1Percent] +CategoryId = FlightSystems +Name = "Cowl Flaps 1 Opened Percentage" +SimVarName = "RECIP ENG COWL FLAP POSITION:1" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[CowlFlaps2Percent] +CategoryId = FlightSystems +Name = "Cowl Flaps 2 Opened Percentage" +SimVarName = "RECIP ENG COWL FLAP POSITION:2" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[CowlFlaps3Percent] +CategoryId = FlightSystems +Name = "Cowl Flaps 3 Opened Percentage" +SimVarName = "RECIP ENG COWL FLAP POSITION:3" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[CowlFlaps4Percent] +CategoryId = FlightSystems +Name = "Cowl Flaps 4 Opened Percentage" +SimVarName = "RECIP ENG COWL FLAP POSITION:4" +Unit = "percent" +StringFormat = "0.0#" +CanSet = True + +[GearTotalExtended] +CategoryId = FlightSystems +Name = "Total percentage of gear extended" +SimVarName = "GEAR TOTAL PCT EXTENDED" +Unit = "percentage" + +[SpoilersAvailable] +CategoryId = FlightSystems +Name = "Spoilers Available (0/1)" +SimVarName = "SPOILER AVAILABLE" +Unit = "Bool" +DefaultValue = "0" + +[SpoilersHandlePosition] +CategoryId = FlightSystems +Name = "Spoilers Handle Position (0 - 16384)" +SimVarName = "SPOILERS HANDLE POSITION" +Unit = "position 16k" +DefaultValue = "0" +CanSet = True + +[SpoilersLeftPosition] +CategoryId = FlightSystems +Name = "Spoilers Left Position Percent" +SimVarName = "SPOILERS LEFT POSITION" +Unit = "percent over 100" +DefaultValue = "0" +StringFormat = "F1" + +[SpoilersRightPosition] +CategoryId = FlightSystems +Name = "Spoilers Right Position Percent" +SimVarName = "SPOILERS RIGHT POSITION" +Unit = "percent over 100" +DefaultValue = "0" +StringFormat = "F1" + +[SpoilersArmed] +CategoryId = FlightSystems +Name = "Spoilers Armed (0/1)" +SimVarName = "SPOILERS ARMED" +Unit = "Bool" +DefaultValue = "0" + +[AileronTrim] +CategoryId = FlightSystems +Name = "Aileron Trim Angle" +SimVarName = "AILERON TRIM" +Unit = "degrees" +StringFormat = "0.0#" + +[ElevatorTrim] +CategoryId = FlightSystems +Name = "Elevator Trim Angle" +SimVarName = "ELEVATOR TRIM POSITION" +Unit = "degrees" +StringFormat = "0.0#" +CanSet = True + +[RudderTrim] +CategoryId = FlightSystems +Name = "Rudder Trim Angle" +SimVarName = "RUDDER TRIM" +Unit = "degrees" +StringFormat = "0.0#" + +[AileronTrimPct] +CategoryId = FlightSystems +Name = "Aileron Trim Percent" +SimVarName = "AILERON TRIM PCT" +Unit = "percent over 100" +DefaultValue = "0" +StringFormat = "F1" +CanSet = True + +[ElevatorTrimPct] +CategoryId = FlightSystems +Name = "Elevator Trim Percent" +SimVarName = "ELEVATOR TRIM PCT" +Unit = "percent over 100" +DefaultValue = "0" +StringFormat = "F1" + +[RudderTrimPct] +CategoryId = FlightSystems +Name = "Rudder Trim Percent" +SimVarName = "RUDDER TRIM PCT" +Unit = "percent over 100" +DefaultValue = "0" +StringFormat = "F1" +CanSet = True + + +# Category: SimSystem ############################## +# +[SimulationRate] +CategoryId = SimSystem +Name = "The current simulation rate" +SimVarName = "SIMULATION RATE" +Unit = "number" + +[AtcType] +CategoryId = SimSystem +Name = "Type of aircraft used by ATC" +SimVarName = "ATC TYPE" +Unit = "string" + +[AtcModel] +CategoryId = SimSystem +Name = "Model of aircraft used by ATC" +SimVarName = "ATC MODEL" +Unit = "string" + +[AtcId] +CategoryId = SimSystem +Name = "Aircraft Id used by ATC" +SimVarName = "ATC ID" +Unit = "string" +CanSet = True + +[AtcAirline] +CategoryId = SimSystem +Name = "Airline used by ATC" +SimVarName = "ATC AIRLINE" +Unit = "string" +CanSet = True + +[AtcFlightNumber] +CategoryId = SimSystem +Name = "Flight Number used by ATC" +SimVarName = "ATC FLIGHT NUMBER" +Unit = "string" +CanSet = True + +[AircraftTitle] +CategoryId = SimSystem +Name = "Aircraft Title" +SimVarName = "TITLE" +Unit = "string" + From e548721b02c1ea7ce11a2e93a7cd97ccb2209d88 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 01:35:29 -0400 Subject: [PATCH 16/93] Switch to loading all states from config files. Temp version of ReflectionService which can optionally generate the config files from current attribute data in Object files. --- .../GenerateDoc.cs | 76 +++++++++++-------- .../GenerateEntry.cs | 72 +++++++++++------- .../Services/ReflectionService.cs | 40 ++++++++-- 3 files changed, 125 insertions(+), 63 deletions(-) diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index 051a0d1..37c62eb 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -1,5 +1,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using MSFSTouchPortalPlugin.Configuration; +using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin_Generator.Configuration; using MSFSTouchPortalPlugin_Generator.Interfaces; using MSFSTouchPortalPlugin_Generator.Model; @@ -12,7 +14,8 @@ using TouchPortalExtension.Attributes; using MSFSTouchPortalPlugin.Constants; -namespace MSFSTouchPortalPlugin_Generator { +namespace MSFSTouchPortalPlugin_Generator +{ internal class GenerateDoc : IGenerateDoc { private readonly ILogger _logger; private readonly IOptions _options; @@ -49,19 +52,32 @@ private DocBase CreateModel() { var assembly = Assembly.Load(a); var assemblyList = assembly.GetTypes().ToList(); + // read default states config + // TODO: Allow configuration of which state config file(s) to read. + var pc = PluginConfig.Instance; + var configStates = pc.LoadSimVarItems(false); + if (pc.HaveErrors) { + foreach (var e in pc.ErrorsList) + _logger.LogError(e, "Configuration reader error:"); + } + // Get all classes with the TouchPortalCategory - var classList = assemblyList.Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalCategoryAttribute))).OrderBy(o => o.Name).ToList(); + var classList = assemblyList.Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalCategoryAttribute))).OrderBy(o => o.Name); // Loop through categories - classList.ForEach(cat => { + foreach (var cat in classList) { var catAttr = (TouchPortalCategoryAttribute)Attribute.GetCustomAttribute(cat, typeof(TouchPortalCategoryAttribute)); - bool newCatCreated = false; + // FIXME: the Split() is necessary due to legacy mis-named category InstrumentsSystems.Fuel + if (!Enum.TryParse(catAttr.Id.Split('.').Last(), false, out Groups catId)) { + _logger.LogWarning($"Could not parse category ID: '{catAttr.Id}', skipping.'"); + continue; + } var newCat = model.Categories.FirstOrDefault(c => c.Name == catAttr.Name); if (newCat == null) { newCat = new DocCategory { Name = catAttr.Name }; - newCatCreated = true; + model.Categories.Add(newCat); } // Loop through Actions @@ -109,32 +125,30 @@ private DocBase CreateModel() { newCat.Actions = newCat.Actions.OrderBy(c => c.Name).ToList(); // Loop through States - var states = cat.GetFields().Where(m => m.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalStateAttribute))).ToList(); - states.ForEach(state => { - var stateAttribute = state.GetCustomAttribute(); - - if (stateAttribute != null) { - var newState = new DocState { - Id = $"{_options.Value.PluginName}.{catAttr.Id}.State.{stateAttribute.Id}", - Type = stateAttribute.Type, - Description = stateAttribute.Description, - DefaultValue = stateAttribute.Default, - SimVarName = state.FieldType == typeof(SimVarItem) ? ((SimVarItem)state.GetValue(null)).SimVarName : "" - }; - - newCat.States.Add(newState); - } - }); + if (newCat.States.Any()) + continue; // skip if already added for this category + + System.Collections.Generic.IEnumerable categoryStates; + // Plugin (non SimConnect) states are stored in a separate config file. + if (catId == Groups.Plugin) + categoryStates = pc.LoadPluginStates(); + else + categoryStates = configStates.Where(s => s.CategoryId == catId); + + foreach (SimVarItem state in categoryStates) { + var newState = new DocState { + Id = state.TouchPortalStateId, + Type = state.TouchPortalValueType, + Description = state.Name, + DefaultValue = state.DefaultValue ?? string.Empty, + SimVarName = state.SimVarName + }; + newCat.States.Add(newState); + } + // Sort the states newCat.States = newCat.States.OrderBy(c => c.Description).ToList(); - - // Loop through Events - - if (newCatCreated) - model.Categories.Add(newCat); - }); - - model.Categories = model.Categories.OrderBy(c => c.Name).ToList(); + } // categories loop // Settings var setContainers = assemblyList.Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalSettingsContainerAttribute))).OrderBy(o => o.Name).ToList(); @@ -206,12 +220,12 @@ private static string CreateMarkdown(DocBase model) { cat.Actions.ForEach(act => { s.Append($"{act.Name}{act.Description}{act.Format}"); // Loop action data - // I first tried by making a nested table to list the datas, but it looked like ass on GitHub due to their CSS which forces a table width and (as of Feb '22). -MP + // I first tried by making a nested table to list the data, but it looked like ass on GitHub due to their CSS which forces a table width and (as of Feb '22). -MP s.Append("
    \n"); act.Data.ForEach(ad => { s.Append($"
  1. [{ad.Type}]   "); if (ad.Type == "choice") - s.Append(new Regex(Regex.Escape(ad.DefaultValue)).Replace(ad.Values, $"{ad.DefaultValue}", 1)); // only replace 1st occurence of default string + s.Append(new Regex(Regex.Escape(ad.DefaultValue)).Replace(ad.Values, $"{ad.DefaultValue}", 1)); // only replace 1st occurrence of default string else if (!string.IsNullOrWhiteSpace(ad.DefaultValue)) s.Append($"{ad.DefaultValue}"); else diff --git a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs index a7e496f..d70bbb8 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs @@ -1,5 +1,8 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using MSFSTouchPortalPlugin.Configuration; +using MSFSTouchPortalPlugin.Constants; +using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin_Generator.Configuration; using MSFSTouchPortalPlugin_Generator.Interfaces; using MSFSTouchPortalPlugin_Generator.Model; @@ -13,7 +16,8 @@ using System.Reflection; using TouchPortalExtension.Attributes; -namespace MSFSTouchPortalPlugin_Generator { +namespace MSFSTouchPortalPlugin_Generator +{ internal class GenerateEntry : IGenerateEntry { private readonly ILogger _logger; private readonly IOptions _options; @@ -49,29 +53,44 @@ public void Generate() { }; // Add Configuration - // Add Plug Start Comand + // Add Plugin Start Command model.Plugin_start_cmd = Path.Combine("%TP_PLUGIN_FOLDER%", "MSFS-TouchPortal-Plugin\\dist", "MSFSTouchPortalPlugin.exe"); - // Load asembly + // Load assembly _ = MSFSTouchPortalPlugin.Objects.Plugin.Plugin.Init; var q = assembly.GetTypes().ToList(); + // read default states config + // TODO: Allow configuration of which state config file(s) to read. + var pc = PluginConfig.Instance; + var configStates = pc.LoadSimVarItems(false); + if (pc.HaveErrors) { + foreach (var e in pc.ErrorsList) + _logger.LogError(e, "Configuration reader error:"); + } + // Get all classes with the TouchPortalCategory - var s = q.Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalCategoryAttribute))).OrderBy(o => o.Name).ToList(); + var categoryClasses = q.Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalCategoryAttribute))).OrderBy(o => o.Name); // For each category, add to model - s.ForEach(cat => { + foreach (var cat in categoryClasses) { var att = (TouchPortalCategoryAttribute)Attribute.GetCustomAttribute(cat, typeof(TouchPortalCategoryAttribute)); - bool newCatCreated = false; + // FIXME: the Split() is necessary due to legacy mis-named category InstrumentsSystems.Fuel + if (!Enum.TryParse(att.Id.Split('.').Last(), false, out Groups catId)) { + _logger.LogWarning($"Could not parse category ID: '{att.Id}', skipping.'"); + continue; + } + var category = model.Categories.FirstOrDefault(c => c.Name == att.Name); if (category == null) { category = new TouchPortalCategory { + // FIXME: For now use attribute Id (att.Id) instead of actual parsed Groups enum (catId) for backwards compat with mis-named actions in category InstrumentsSystems.Fuel Id = $"{_options.Value.PluginName}.{att.Id}", Name = att.Name, // Imagepath = att.ImagePath Imagepath = Path.Combine("%TP_PLUGIN_FOLDER%", "MSFS-TouchPortal-Plugin", "airplane_takeoff24.png") }; - newCatCreated = true; + model.Categories.Add(category); } // Add actions @@ -117,38 +136,37 @@ public void Generate() { _logger.LogWarning($"Duplicate action ID found: '{action.Id}', skipping.'"); }); - // Ordering + // Sort the actions category.Actions = category.Actions.OrderBy(c => c.Name).ToList(); // States - var states = cat.GetMembers().Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalStateAttribute))).ToList(); - states.ForEach(state => { - var stateAttribute = state.GetCustomAttribute(); + if (category.States.Any()) + continue; // skip if already added for this category + + System.Collections.Generic.IEnumerable categoryStates; + // Plugin (non SimConnect) states are stored in a separate config file. + if (catId == Groups.Plugin) + categoryStates = pc.LoadPluginStates(); + else + categoryStates = configStates.Where(s => s.CategoryId == catId); + + foreach (SimVarItem state in categoryStates) { var newState = new TouchPortalState { - Id = $"{category.Id}.State.{stateAttribute.Id}", - Type = stateAttribute.Type, - Description = $"{category.Name} - {stateAttribute.Description}", - DefaultValue = stateAttribute.Default + Id = state.TouchPortalStateId, + Type = state.TouchPortalValueType, + Description = $"{category.Name} - {state.Name}", + DefaultValue = state.DefaultValue ?? string.Empty, }; - // validate unique ID if (category.States.FirstOrDefault(s => s.Id == newState.Id) == null) category.States.Add(newState); else _logger.LogWarning($"Duplicate state ID found: '{newState.Id}', skipping.'"); - }); + } - // Ordering + // Sort the states category.States = category.States.OrderBy(c => c.Description).ToList(); - - // Add events - - if (newCatCreated) - model.Categories.Add(category); - }); - - // Ordering - model.Categories = model.Categories.OrderBy(c => c.Name).ToList(); + } // categories loop // Settings var setContainers = q.Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalSettingsContainerAttribute))).OrderBy(o => o.Name).ToList(); diff --git a/MSFSTouchPortalPlugin/Services/ReflectionService.cs b/MSFSTouchPortalPlugin/Services/ReflectionService.cs index 206fc49..1b7860b 100644 --- a/MSFSTouchPortalPlugin/Services/ReflectionService.cs +++ b/MSFSTouchPortalPlugin/Services/ReflectionService.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Logging; using MSFSTouchPortalPlugin.Attributes; +using MSFSTouchPortalPlugin.Configuration; using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Interfaces; @@ -12,7 +13,9 @@ using TouchPortalExtension.Attributes; using TouchPortalExtension.Enums; -namespace MSFSTouchPortalPlugin.Services { +namespace MSFSTouchPortalPlugin.Services +{ + internal class ReflectionService : IReflectionService { private readonly string rootName = Assembly.GetExecutingAssembly().GetName().Name; private readonly ILogger _logger; @@ -95,24 +98,50 @@ public Dictionary GetActionEvents() { return returnDict; } + // TODO: not needed here anymore, move to main plugin code public Dictionary GetStates() { + var pc = PluginConfig.Instance; +#if False var returnDict = new Dictionary(); - var stateFieldList = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsClass && t.GetCustomAttribute() != null).ToList(); stateFieldList.ForEach(stateFieldClass => { + + // Find the category info + var cat = stateFieldClass.GetCustomAttribute(); + if (!Enum.TryParse(cat.Id.Split('.').Last(), true, out Groups groupId)) + _logger.LogError($"Can't parse category '{cat.Id}' to Groups enum."); + // Get all States and register to SimConnect var states = stateFieldClass.GetFields().Where(m => m.CustomAttributes.Any(att => att.AttributeType == typeof(SimVarDataRequestAttribute))).ToList(); states.ForEach(s => { // Evaluate and setup the Touch Portal State ID - string catId = stateFieldClass.GetCustomAttribute().Id; var item = (SimVarItem)s.GetValue(null); - item.TouchPortalStateId = $"{rootName}.{catId}.State.{item.Def}"; + + var stateAttribute = s.GetCustomAttribute(); + if (stateAttribute != null) { + item.Name = stateAttribute.Description; + item.DefaultValue = stateAttribute.Default; + } + + item.CategoryId = groupId; + item.TouchPortalStateId = $"{rootName}.{item.CategoryId}.State.{item.Id}"; returnDict.TryAdd(item.Def, item); }); }); - return returnDict; + if (!pc.SaveSimVarItems(returnDict.Values)) { + foreach (var e in pc.ErrorsList) + _logger.LogError(e, "Configuration writer error:"); + } +#endif + // read default states config + var configStates = pc.LoadSimVarItems(false); + if (pc.HaveErrors) { + foreach (var e in pc.ErrorsList) + _logger.LogError(e, "Configuration reader error:"); + } + return configStates.ToDictionary(s => s.Def, s => s); } public Dictionary GetSettings() { @@ -139,4 +168,5 @@ public Dictionary GetSettings() { return returnDict; } } + } From eeba3feb7190db18d2c975f184223197c8b522a0 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 01:36:27 -0400 Subject: [PATCH 17/93] [PluginConfig] Add custom exception type for duplicate state IDs. --- .../Configuration/PluginConfig.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs index 45b07e6..8f1a0b1 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs +++ b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs @@ -1,10 +1,11 @@ -using System; -using System.Collections.Generic; +using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; +using System; +using System.Collections.Generic; using System.Linq; using System.IO; using System.Text; -using MSFSTouchPortalPlugin.Constants; +using System.Runtime.Serialization; //using SharpConfig; namespace MSFSTouchPortalPlugin.Configuration @@ -77,7 +78,7 @@ public IReadOnlyCollection LoadSimVarItems(bool isUserConfig = true, simVar.Id = item.Name; // check unique if (ret.FirstOrDefault(s => s.Id == simVar.Id) != null) { - _errorsList.Add(new Exception($"Duplicate SimVar ID found for '{simVar.Id}'")); + _errorsList.Add(new DuplicateIdException($"Duplicate SimVar ID found for '{simVar.Id}'")); continue; } simVar.TouchPortalStateId = $"{RootName}.{simVar.CategoryId}.State.{simVar.Id}"; @@ -145,4 +146,14 @@ private void SaveToFile(SharpConfig.Configuration cfg, string folder, string fil } + [Serializable] + public class DuplicateIdException : Exception + { + public DuplicateIdException() : base() { } + public DuplicateIdException(string message) : base(message) { } + protected DuplicateIdException(SerializationInfo info, StreamingContext context) + : base(info, context) { + } + } + } From ebe25309c595a1329ffc2d9abb234c9381a00730 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 03:30:38 -0400 Subject: [PATCH 18/93] Move StringVal64 to own file, rename to StringVal, increase size to 256, use as SimVarItem type instead of generic string. --- MSFSTouchPortalPlugin/Constants/SimVars.cs | 30 +++++++++--------- .../Services/SimConnectService.cs | 31 ++++++++----------- MSFSTouchPortalPlugin/Types/StringVal.cs | 18 +++++++++++ 3 files changed, 47 insertions(+), 32 deletions(-) create mode 100644 MSFSTouchPortalPlugin/Types/StringVal.cs diff --git a/MSFSTouchPortalPlugin/Constants/SimVars.cs b/MSFSTouchPortalPlugin/Constants/SimVars.cs index e9c0696..a08106b 100644 --- a/MSFSTouchPortalPlugin/Constants/SimVars.cs +++ b/MSFSTouchPortalPlugin/Constants/SimVars.cs @@ -1,4 +1,5 @@ using MSFSTouchPortalPlugin.Enums; +using MSFSTouchPortalPlugin.Types; using Stopwatch = System.Diagnostics.Stopwatch; using SIMCONNECT_DATATYPE = Microsoft.FlightSimulator.SimConnect.SIMCONNECT_DATATYPE; @@ -197,8 +198,8 @@ public string Unit IsBooleanType = !IsStringType && Units.IsBooleantype(_unit); IsIntegralType = !IsStringType && !IsBooleanType && Units.IsIntegraltype(_unit); IsRealType = !IsStringType && !IsBooleanType && !IsIntegralType; - SimConnectDataType = IsStringType ? SIMCONNECT_DATATYPE.STRING64 : IsIntegralType ? SIMCONNECT_DATATYPE.INT64 : IsBooleanType ? SIMCONNECT_DATATYPE.INT32 : SIMCONNECT_DATATYPE.FLOAT64; - StorageDataType = IsStringType ? typeof(string) : IsIntegralType ? typeof(long) : IsBooleanType ? typeof(uint) : typeof(double); + SimConnectDataType = IsStringType ? SIMCONNECT_DATATYPE.STRING256 : IsIntegralType ? SIMCONNECT_DATATYPE.INT64 : IsBooleanType ? SIMCONNECT_DATATYPE.INT32 : SIMCONNECT_DATATYPE.FLOAT64; + StorageDataType = IsStringType ? typeof(StringVal) : IsIntegralType ? typeof(long) : IsBooleanType ? typeof(uint) : typeof(double); } } @@ -241,7 +242,7 @@ public string FormattedValue double v => string.Format(StringFormat, v), uint v => string.Format(StringFormat, v), long v => string.Format(StringFormat, v), - string v => string.Format(StringFormat, v), + StringVal v => string.Format(StringFormat, v.ToString()), _ => string.Empty, }; } @@ -256,7 +257,7 @@ public System.Type StorageDataType get => Value?.GetType(); private set { if (Value == null || Value.GetType() != value) { - _value = value == typeof(string) ? string.Empty : System.Activator.CreateInstance(value); + _value = value == typeof(StringVal) ? new StringVal() : System.Activator.CreateInstance(value); _valInit = false; } } @@ -293,10 +294,10 @@ public SimVarItem() { Def = NextId(); } - public bool ValueEquals(string value) => _valInit && IsStringType && value == (string)Value; + public bool ValueEquals(string value) => _valInit && IsStringType && value == Value.ToString(); public bool ValueEquals(double value) => _valInit && IsRealType && System.Math.Abs((double)Value - ConvertValueIfNeeded(value)) <= DeltaEpsilon; - public bool ValueEquals(long value) => _valInit && IsIntegralType && System.Math.Abs((long)Value - value) <= (long)DeltaEpsilon; - public bool ValueEquals(uint value) => _valInit && IsBooleanType && System.Math.Abs((uint)Value - value) <= (uint)DeltaEpsilon; + public bool ValueEquals(long value) => _valInit && IsIntegralType && System.Math.Abs((long)Value - value) <= (long)DeltaEpsilon; + public bool ValueEquals(uint value) => _valInit && IsBooleanType && System.Math.Abs((uint)Value - value) <= (uint)DeltaEpsilon; public bool ValueEquals(object value) { if (!_valInit) @@ -314,37 +315,38 @@ public bool ValueEquals(object value) { } } - public bool SetValue(string value) { + internal bool SetValue(StringVal value) { if (IsStringType) Value = value; return IsStringType; } - public bool SetValue(double value) { + internal bool SetValue(double value) { if (!IsStringType) Value = ConvertValueIfNeeded(value); return !IsStringType; } - public bool SetValue(long value) { + internal bool SetValue(long value) { if (IsIntegralType) Value = value; return IsIntegralType; } - public bool SetValue(uint value) { + internal bool SetValue(uint value) { if (IsBooleanType) Value = value; return IsBooleanType; } - public bool SetValue(object value) { + internal bool SetValue(object value) { try { return value switch { double v => SetValue(v), uint v => SetValue(v), long v => SetValue(v), - _ => SetValue(value.ToString()), + StringVal v => SetValue(v), + _ => false }; } catch { @@ -388,7 +390,7 @@ public bool PendingTimeout() { } public string ToDebugString() { - return $"{GetType()}: {{Def: {Def}; SimVarName: {SimVarName}; Unit: {Unit}; Cat: {CategoryId}; Name: {Name}}}"; + return $"{GetType().Name}: {{Def: {Def}; SimVarName: {SimVarName}; Unit: {Unit}; Cat: {CategoryId}; Name: {Name}}}"; } } diff --git a/MSFSTouchPortalPlugin/Services/SimConnectService.cs b/MSFSTouchPortalPlugin/Services/SimConnectService.cs index e22b75a..b1c5db0 100644 --- a/MSFSTouchPortalPlugin/Services/SimConnectService.cs +++ b/MSFSTouchPortalPlugin/Services/SimConnectService.cs @@ -7,6 +7,7 @@ using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Interfaces; +using MSFSTouchPortalPlugin.Types; using System; using System.Runtime.InteropServices; using System.Threading; @@ -151,6 +152,11 @@ public void SetNotificationGroupPriorities() { } } + private void ClearDataDefinition(Definition def) { + _simConnect.ClearDataDefinition(def); + DbgAddSendRecord($"ClearDataDefinition({def})"); + } + public bool RegisterToSimConnect(SimVarItem simVar) { if (_connected) { string unitName = simVar.IsStringType ? null : simVar.Unit; @@ -167,11 +173,12 @@ public bool RegisterToSimConnect(SimVarItem simVar) { case long: _simConnect.RegisterDataDefineStruct(simVar.Def); break; - case string: - _simConnect.RegisterDataDefineStruct(simVar.Def); + case StringVal: + _simConnect.RegisterDataDefineStruct(simVar.Def); break; default: _logger.LogError($"Unable to register storage type for '{simVar.StorageDataType}'"); + ClearDataDefinition(simVar.Def); return false; } DbgAddSendRecord($"RegisterDataDefineStruct<{simVar.StorageDataType}>({simVar.ToDebugString()})"); @@ -274,7 +281,9 @@ public void Dispose() { private void DbgSetupRequestTracking() { // Get direct access to the SimConnect handle, to use functions otherwise not supported. +#pragma warning disable S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields System.Reflection.FieldInfo fiSimConnect = typeof(SimConnect).GetField("hSimConnect", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); +#pragma warning restore S3011 hSimConnect = (IntPtr)fiSimConnect.GetValue(_simConnect); } @@ -298,25 +307,11 @@ private string DbgGetSendRecord(uint sendId) { } #else -#pragma warning disable S1172 // Unused method parameters should be removed [System.Diagnostics.Conditional("DEBUG_REQUESTS")] // prevents any parameters being passed to this method from being evaluated - private static void DbgAddSendRecord(string _) { /* no-op when request tracking disabled */ } - private static string DbgGetSendRecord(uint _) => "Request tracking disabled."; -#pragma warning restore S1172 + private static void DbgAddSendRecord(string record) { _ = record; /* no-op when request tracking disabled */ } + private static string DbgGetSendRecord(uint sendId) { _ = sendId; return "Request tracking disabled."; } #endif // DEBUG_REQUESTS #endregion } - - internal readonly struct StringVal64 : IEquatable { - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] - public readonly string Value; - - public bool Equals(StringVal64 other) => other.Value == Value; - public override bool Equals(object obj) => (obj is StringVal64 && Equals((StringVal64)obj)); - public override string ToString() => Value; - public override int GetHashCode() => Value.GetHashCode(); - public static bool operator ==(StringVal64 obj1, StringVal64 obj2) => obj1.Equals(obj2); - public static bool operator !=(StringVal64 obj1, StringVal64 obj2) => !obj1.Equals(obj2); - } } diff --git a/MSFSTouchPortalPlugin/Types/StringVal.cs b/MSFSTouchPortalPlugin/Types/StringVal.cs new file mode 100644 index 0000000..afbc235 --- /dev/null +++ b/MSFSTouchPortalPlugin/Types/StringVal.cs @@ -0,0 +1,18 @@ + +using System; +using System.Runtime.InteropServices; + +namespace MSFSTouchPortalPlugin.Types +{ + internal readonly struct StringVal : IEquatable { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public readonly string Value; + + public bool Equals(StringVal other) => other.Value == Value; + public override bool Equals(object obj) => (obj is StringVal && Equals((StringVal)obj)); + public override string ToString() => Value; + public override int GetHashCode() => Value.GetHashCode(); + public static bool operator ==(StringVal obj1, StringVal obj2) => obj1.Equals(obj2); + public static bool operator !=(StringVal obj1, StringVal obj2) => !obj1.Equals(obj2); + } +} From 5c1bad206a64d47f7341d65fd87ad537d0496bfd Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 03:34:57 -0400 Subject: [PATCH 19/93] Remove ReflectionService.GetStates() and move that functionality to PluginService (using PluginConfig). Add SimConnectService.ClearAllDataDefinitions() for clearing any existing definitions if reloading states. --- .../Interfaces/IReflectionService.cs | 1 - .../Interfaces/ISimConnectService.cs | 1 + .../Services/PluginService.cs | 21 +++++++-- .../Services/ReflectionService.cs | 46 ------------------- .../Services/SimConnectService.cs | 12 ++++- 5 files changed, 28 insertions(+), 53 deletions(-) diff --git a/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs b/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs index b0c3fc8..1764bbd 100644 --- a/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs +++ b/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs @@ -7,7 +7,6 @@ namespace MSFSTouchPortalPlugin.Interfaces { internal interface IReflectionService { Dictionary GetActionEvents(); - Dictionary GetStates(); Dictionary GetSettings(); ref readonly Dictionary GetClientEventIdToNameMap(); string GetSimEventNameById(Enum id); diff --git a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs index a5f2192..5b113c3 100644 --- a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs +++ b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs @@ -22,6 +22,7 @@ internal interface ISimConnectService { void Disconnect(); bool MapClientEventToSimEvent(Enum eventId, string eventName); void SetNotificationGroupPriorities(); + void ClearAllDataDefinitions(); bool RegisterToSimConnect(SimVarItem simVar); bool RequestDataOnSimObjectType(SimVarItem simVar); bool TransmitClientEvent(Groups group, Enum eventId, uint data); diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index a34c674..809cfbb 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using MSFSTouchPortalPlugin.Configuration; using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Interfaces; using MSFSTouchPortalPlugin.Objects.Plugin; @@ -179,10 +180,7 @@ private void SimConnectEvent_OnConnect() { // must be called after adding notifications _simConnectService.SetNotificationGroupPriorities(); - // Register SimVars - foreach (var s in statesDictionary) { - _simConnectService.RegisterToSimConnect(s.Value); - } + SetupSimVars(); Task.WhenAll(RunPluginServices(_simConnectCancellationTokenSource.Token)); } @@ -211,10 +209,23 @@ private void SimConnectEvent_OnDisconnect() { private void SetupEventLists() { actionsDictionary = _reflectionService.GetActionEvents(); - statesDictionary = _reflectionService.GetStates(); pluginSettingsDictionary = _reflectionService.GetSettings(); } + private void SetupSimVars() { + var pc = PluginConfig.Instance; + var configStates = pc.LoadSimVarItems(false); + if (pc.HaveErrors) { + foreach (var e in pc.ErrorsList) + _logger.LogWarning(e, "Configuration reader error:"); + } + statesDictionary = configStates.ToDictionary(s => s.Def, s => s); + // Register SimVars + _simConnectService.ClearAllDataDefinitions(); + foreach (var s in statesDictionary) + _simConnectService.RegisterToSimConnect(s.Value); + } + private Task TryConnect() { short i = 0; while (!_cancellationToken.IsCancellationRequested) { diff --git a/MSFSTouchPortalPlugin/Services/ReflectionService.cs b/MSFSTouchPortalPlugin/Services/ReflectionService.cs index 1b7860b..6dbdf89 100644 --- a/MSFSTouchPortalPlugin/Services/ReflectionService.cs +++ b/MSFSTouchPortalPlugin/Services/ReflectionService.cs @@ -98,52 +98,6 @@ public Dictionary GetActionEvents() { return returnDict; } - // TODO: not needed here anymore, move to main plugin code - public Dictionary GetStates() { - var pc = PluginConfig.Instance; -#if False - var returnDict = new Dictionary(); - var stateFieldList = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsClass && t.GetCustomAttribute() != null).ToList(); - stateFieldList.ForEach(stateFieldClass => { - - // Find the category info - var cat = stateFieldClass.GetCustomAttribute(); - if (!Enum.TryParse(cat.Id.Split('.').Last(), true, out Groups groupId)) - _logger.LogError($"Can't parse category '{cat.Id}' to Groups enum."); - - // Get all States and register to SimConnect - var states = stateFieldClass.GetFields().Where(m => m.CustomAttributes.Any(att => att.AttributeType == typeof(SimVarDataRequestAttribute))).ToList(); - states.ForEach(s => { - // Evaluate and setup the Touch Portal State ID - var item = (SimVarItem)s.GetValue(null); - - var stateAttribute = s.GetCustomAttribute(); - if (stateAttribute != null) { - item.Name = stateAttribute.Description; - item.DefaultValue = stateAttribute.Default; - } - - item.CategoryId = groupId; - item.TouchPortalStateId = $"{rootName}.{item.CategoryId}.State.{item.Id}"; - - returnDict.TryAdd(item.Def, item); - }); - }); - - if (!pc.SaveSimVarItems(returnDict.Values)) { - foreach (var e in pc.ErrorsList) - _logger.LogError(e, "Configuration writer error:"); - } -#endif - // read default states config - var configStates = pc.LoadSimVarItems(false); - if (pc.HaveErrors) { - foreach (var e in pc.ErrorsList) - _logger.LogError(e, "Configuration reader error:"); - } - return configStates.ToDictionary(s => s.Def, s => s); - } - public Dictionary GetSettings() { Dictionary returnDict = new(); diff --git a/MSFSTouchPortalPlugin/Services/SimConnectService.cs b/MSFSTouchPortalPlugin/Services/SimConnectService.cs index b1c5db0..4c520e4 100644 --- a/MSFSTouchPortalPlugin/Services/SimConnectService.cs +++ b/MSFSTouchPortalPlugin/Services/SimConnectService.cs @@ -31,8 +31,9 @@ internal class SimConnectService : ISimConnectService, IDisposable { private static readonly bool DEBUG_NOTIFICATIONS = false; SimConnect _simConnect; - readonly EventWaitHandle _scReady = new EventWaitHandle(false, EventResetMode.AutoReset); private bool _connected; + private readonly EventWaitHandle _scReady = new EventWaitHandle(false, EventResetMode.AutoReset); + private readonly System.Collections.Generic.List _addedDefinitions = new(); public event DataUpdateEventHandler OnDataUpdateEvent; public event ConnectEventHandler OnConnect; @@ -183,12 +184,21 @@ public bool RegisterToSimConnect(SimVarItem simVar) { } DbgAddSendRecord($"RegisterDataDefineStruct<{simVar.StorageDataType}>({simVar.ToDebugString()})"); + _addedDefinitions.Add(simVar.Def); return true; } return false; } + public void ClearAllDataDefinitions() { + if (_connected) { + foreach (var def in _addedDefinitions) + ClearDataDefinition(def); + } + _addedDefinitions.Clear(); + } + public bool RequestDataOnSimObjectType(SimVarItem simVar) { if (_connected) { try { From 9a311abf3e192728fe2d11cabe64153fd762e783 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 05:47:20 -0400 Subject: [PATCH 20/93] [Generator] Add unit type column to state description and re-sort states by Id instead of name (only in docs, for now?). --- MSFSTouchPortalPlugin-Generator/GenerateDoc.cs | 11 ++++++----- MSFSTouchPortalPlugin-Generator/Model/DocBase.cs | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index 37c62eb..42f69f3 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -141,13 +141,14 @@ private DocBase CreateModel() { Type = state.TouchPortalValueType, Description = state.Name, DefaultValue = state.DefaultValue ?? string.Empty, - SimVarName = state.SimVarName + SimVarName = state.SimVarName, + Unit = state.Unit, }; newCat.States.Add(newState); } // Sort the states - newCat.States = newCat.States.OrderBy(c => c.Description).ToList(); + newCat.States = newCat.States.OrderBy(c => c.Id).ToList(); } // categories loop // Settings @@ -257,10 +258,10 @@ private static string CreateMarkdown(DocBase model) { if (cat.States.Count > 0) { // Loop States s.Append("### States\n\n"); - s.Append("| Id | SimVar Name | Description | DefaultValue |\n"); - s.Append("| --- | --- | --- | --- |\n"); + s.Append("| Id | SimVar Name | Description | Unit | DefaultValue |\n"); + s.Append("| --- | --- | --- | --- | --- |\n"); cat.States.ForEach(state => { - s.Append($"| {state.Id} | {state.SimVarName} | {state.Description} | {state.DefaultValue} |\n"); + s.Append($"| {state.Id} | {state.SimVarName} | {state.Description} | {state.Unit} | {state.DefaultValue} |\n"); }); s.Append("\n\n"); } diff --git a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs index 4d5fcce..531c29b 100644 --- a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs +++ b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs @@ -58,5 +58,6 @@ public class DocState { public string Description { get; set; } public string DefaultValue { get; set; } public string SimVarName { get; set; } + public string Unit { get; set; } } } From 2458b329916702a25ff4d246d9e09098d86d5ff8 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 07:06:06 -0400 Subject: [PATCH 21/93] Add StateId property to TouchPortalSettingAttribute instead of relying on TouchPortalStateAttribute. --- MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs | 3 ++- MSFSTouchPortalPlugin/Services/ReflectionService.cs | 4 ++-- .../Attributes/TouchPortalSettingAttribute.cs | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs index cd1e926..8c1b481 100644 --- a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs +++ b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs @@ -31,7 +31,8 @@ public static class Settings { [TouchPortalSetting( Name = "Held Action Repeat Rate (ms)", Description = "Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Action Repeat Interval' action.", - Type = "number", Default = "450", MinValue = 50, MaxValue = int.MaxValue, ReadOnly = true + Type = "number", Default = "450", MinValue = 50, MaxValue = int.MaxValue, ReadOnly = true, + StateId = "ActionRepeatInterval" )] [TouchPortalAction("ActionRepeatInterval", "Action Repeat Interval", "MSFS", "Held Action Repeat Rate (ms)", "Repeat Interval: {0} to/by: {1} ms", true)] [TouchPortalActionChoice(new[] { "Set", "Increment", "Decrement" })] diff --git a/MSFSTouchPortalPlugin/Services/ReflectionService.cs b/MSFSTouchPortalPlugin/Services/ReflectionService.cs index 6dbdf89..1211822 100644 --- a/MSFSTouchPortalPlugin/Services/ReflectionService.cs +++ b/MSFSTouchPortalPlugin/Services/ReflectionService.cs @@ -112,8 +112,8 @@ public Dictionary GetSettings() { settingsObj.Name = settingAttr.Name; if (settingsObj.Default == null) settingsObj.Default = settingAttr.Default; - if (settingsObj.TouchPortalStateId == null && settingField.GetCustomAttribute()?.Id is var stateId && stateId != null) - settingsObj.TouchPortalStateId = $"{rootName}.Plugin.State.{stateId}"; + if (settingsObj.TouchPortalStateId == null && !string.IsNullOrWhiteSpace(settingAttr.StateId)) + settingsObj.TouchPortalStateId = $"{rootName}.Plugin.State.{settingAttr.StateId}"; returnDict.TryAdd(settingAttr.Name, settingsObj); } }); diff --git a/TouchPortalExtension/Attributes/TouchPortalSettingAttribute.cs b/TouchPortalExtension/Attributes/TouchPortalSettingAttribute.cs index 107376e..f83b3e9 100644 --- a/TouchPortalExtension/Attributes/TouchPortalSettingAttribute.cs +++ b/TouchPortalExtension/Attributes/TouchPortalSettingAttribute.cs @@ -14,6 +14,7 @@ public class TouchPortalSettingAttribute : Attribute public double MaxValue { get; set; } = double.NaN; public bool ReadOnly { get; set; } = false; public bool IsPassword { get; set; } = false; + public string StateId { get; set; } = default; public TouchPortalSettingAttribute() { } public TouchPortalSettingAttribute(string name, string description = default, string type = "text", string dflt = default, bool readOnly = false) { From 3e0a815dedf74bc81201c99940a37a2b76aacc11 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 07:25:46 -0400 Subject: [PATCH 22/93] Remove all state attributes/data from object mapping files. --- .../Objects/AutoPilot/AutoPilot.cs | 152 ++++-------------- .../Objects/Failures/Failures.cs | 5 +- .../Objects/FlightSystems/FlightSystems.cs | 107 +++--------- .../InstrumentsSystems/Communication.cs | 44 +---- .../Objects/InstrumentsSystems/Electrical.cs | 64 ++------ .../Objects/InstrumentsSystems/Engine.cs | 140 ++-------------- .../Objects/InstrumentsSystems/Environment.cs | 72 +-------- .../InstrumentsSystems/FlightInstruments.cs | 82 +--------- .../Objects/InstrumentsSystems/Fuel.cs | 19 ++- .../Objects/Plugin/Plugin.cs | 2 - .../Objects/SimSystem/SimSystem.cs | 33 +--- 11 files changed, 100 insertions(+), 620 deletions(-) diff --git a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs index 4cd0324..bf00b24 100644 --- a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs +++ b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs @@ -5,264 +5,197 @@ namespace MSFSTouchPortalPlugin.Objects.AutoPilot { - [SimVarDataRequestGroup] [SimNotificationGroup(Groups.AutoPilot)] [TouchPortalCategory("AutoPilot", "MSFS - AutoPilot")] internal static class AutoPilotMapping { #region AutoPilot Master - [SimVarDataRequest] [TouchPortalAction("AutoPilotMaster", "AutoPilot", "MSFS", "Toggle/On/Off Auto Pilot", "Auto Pilot Master - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("AP_MASTER", "Toggle")] [TouchPortalActionMapping("AUTOPILOT_ON", "On")] [TouchPortalActionMapping("AUTOPILOT_OFF", "Off")] - [TouchPortalState("AutoPilotMaster", "text", "AutoPilot Master Status", "")] - public static readonly SimVarItem AP_MASTER = new SimVarItem { Def = Definition.AutoPilotMaster, SimVarName = "AUTOPILOT MASTER", Unit = Units.Bool, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("AutoPilotAvailable", "text", "AutoPilot Availability", "")] - public static readonly SimVarItem AutoPilotAvailable = - new SimVarItem { Def = Definition.AutoPilotAvailable, SimVarName = "AUTOPILOT AVAILABLE", Unit = Units.Bool, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("AutoPilotPitchHold", "text", "The status of Auto Pilot Pitch Hold button", "")] - public static readonly SimVarItem AutoPilotPitchHold = - new SimVarItem { Def = Definition.AutoPilotPitchHold, SimVarName = "AUTOPILOT PITCH HOLD", Unit = Units.Bool, CanSet = false }; + public static readonly object AP_MASTER; #endregion #region Attitude - [SimVarDataRequest] [TouchPortalAction("AutoPilotAttitude", "Attitude Hold", "MSFS", "Toggle/On/Off the attitude hold for auto pilot", "Attitude Hold - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("AP_ATT_HOLD", "Toggle")] [TouchPortalActionMapping("AP_ATT_HOLD_ON", "On")] [TouchPortalActionMapping("AP_ATT_HOLD_OFF", "Off")] - [TouchPortalState("AutoPilotAttitudeHold", "text", "AutoPilot Attitude Status", "")] - public static readonly SimVarItem AP_ATTITUDE = - new SimVarItem { Def = Definition.AutoPilotAttitudeHold, SimVarName = "AUTOPILOT ATTITUDE HOLD", Unit = Units.Bool, CanSet = false }; + public static readonly object AP_ATTITUDE; - [SimVarDataRequest] [TouchPortalAction("AutoPilotAttitudeVar", "Attitude Hold Pitch Value", "MSFS", "Sets the attitude hold pitch value", "Attitude Hold Pitch Value - {0}", true)] [TouchPortalActionChoice(new [] { "Select", "Increase", "Decrease" })] [TouchPortalActionMapping("AP_PITCH_REF_INC_UP", "Increase")] [TouchPortalActionMapping("AP_PITCH_REF_INC_DN", "Decrease")] [TouchPortalActionMapping("AP_PITCH_REF_SELECT", "Select")] - [TouchPortalState("AutoPilotAttitudeVar", "text", "AutoPilot Pitch Reference Value", "")] - public static readonly SimVarItem AP_ATTITUDE_PITCH = - new SimVarItem { Def = Definition.AutoPilotAttitudeVar, SimVarName = "AUTOPILOT PITCH HOLD REF", Unit = Units.radians, CanSet = false }; + public static readonly object AP_ATTITUDE_PITCH; [TouchPortalAction("AutoPilotAttitudeSet", "Attitude Hold Pitch Value Set", "MSFS", "Sets the airspeed value", "Set Attitude Hold Pitch Value to {0} (-16384 to +16384)")] [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("AP_PITCH_REF_SET")] - public static object AP_ATTITUDE_PITCH_SET { get; } + public static readonly object AP_ATTITUDE_PITCH_SET; #endregion #region Approach - [SimVarDataRequest] [TouchPortalAction("AutoPilotApproach", "Approach Mode", "MSFS", "Toggle/On/Off the approach mode for auto pilot", "Approach Mode - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("AP_APR_HOLD", "Toggle")] [TouchPortalActionMapping("AP_APR_HOLD_ON", "On")] [TouchPortalActionMapping("AP_APR_HOLD_OFF", "Off")] - [TouchPortalState("AutoPilotApproachHold", "text", "AutoPilot Approach Status", "")] - public static readonly SimVarItem AP_APPROACH = - new SimVarItem { Def = Definition.AutoPilotApproachHold, SimVarName = "AUTOPILOT APPROACH HOLD", Unit = Units.Bool, CanSet = false }; + public static readonly object AP_APPROACH; #endregion #region Bank - [SimVarDataRequest] [TouchPortalAction("AutoPilotBanking", "AP Max Bank Angle", "MSFS", "Increase/Decrease the max bank angle", "Max Bank Angle - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("AP_MAX_BANK_INC", "Increase")] [TouchPortalActionMapping("AP_MAX_BANK_DEC", "Decrease")] - [TouchPortalState("AutoPilotBanking", "text", "AutoPilot Max Bank Angle", "")] - public static readonly SimVarItem AP_MAX_BANK = - new SimVarItem { Def = Definition.AutoPilotBanking, SimVarName = "AUTOPILOT MAX BANK", Unit = Units.radians, CanSet = false }; + public static readonly object AP_MAX_BANK; #endregion #region Heading - [SimVarDataRequest] [TouchPortalAction("AutoPilotHeading", "Heading Hold", "MSFS", "Toggle/On/Off the heading hold for auto pilot", "Heading Hold - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("AP_HDG_HOLD", "Toggle")] [TouchPortalActionMapping("AP_HDG_HOLD_ON", "On")] [TouchPortalActionMapping("AP_HDG_HOLD_OFF", "Off")] - [TouchPortalState("AutoPilotHeadingHold", "text", "AutoPilot Heading Status", "")] - public static readonly SimVarItem AP_HEADING = - new SimVarItem { Def = Definition.AutoPilotHeadingHold, SimVarName = "AUTOPILOT HEADING LOCK", Unit = Units.Bool, CanSet = false }; + public static readonly object AP_HEADING; - [SimVarDataRequest] [TouchPortalAction("AutoPilotHeadingVar", "Heading Hold Value Adj/Sel", "MSFS", "Adjusts the heading hold value", "Heading Hold Value - {0}", true)] [TouchPortalActionChoice(new [] { "Select", "Increase", "Decrease" })] [TouchPortalActionMapping("HEADING_BUG_INC", "Increase")] [TouchPortalActionMapping("HEADING_BUG_DEC", "Decrease")] [TouchPortalActionMapping("HEADING_BUG_SELECT", "Select")] - [TouchPortalState("AutoPilotHeadingVar", "text", "AutoPilot Heading Direction", "")] - public static readonly SimVarItem AP_HEADING_VAR = - new SimVarItem { Def = Definition.AutoPilotHeadingVar, SimVarName = "AUTOPILOT HEADING LOCK DIR", Unit = Units.degrees, CanSet = false, StringFormat = "{0:F0}" }; + public static readonly object AP_HEADING_VAR; [TouchPortalAction("AutoPilotHeadingSet", "Heading Hold Value Set", "MSFS", "Sets the heading hold value", "Heading Hold Value - {0}")] [TouchPortalActionText("1", 0, 359)] [TouchPortalActionMapping("HEADING_BUG_SET")] - public static object AP_HEADING_SET { get; } + public static readonly object AP_HEADING_SET; #endregion #region Altitude - [SimVarDataRequest] [TouchPortalAction("AutoPilotAltitude", "Altitude Hold", "MSFS", "Toggle/On/Off the altitude hold for auto pilot", "Altitude Hold - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("AP_ALT_HOLD", "Toggle")] [TouchPortalActionMapping("AP_ALT_HOLD_ON", "On")] [TouchPortalActionMapping("AP_ALT_HOLD_OFF", "Off")] - [TouchPortalState("AutoPilotAltitudeHold", "text", "AutoPilot Altitude Status", "")] - public static readonly SimVarItem AP_ALTITUDE = - new SimVarItem { Def = Definition.AutoPilotAltitudeHold, SimVarName = "AUTOPILOT ALTITUDE LOCK", Unit = Units.Bool, CanSet = false }; + public static readonly object AP_ALTITUDE; - [SimVarDataRequest] [TouchPortalAction("AutoPilotAltitudeVar", "Altitude Hold Value Adj/Sel", "MSFS", "Adjusts or selects the altitude hold value", "Altitude Hold Value - {0}", true)] [TouchPortalActionChoice(new [] { "Select", "Increase", "Decrease" })] [TouchPortalActionMapping("AP_ALT_VAR_INC", "Increase")] [TouchPortalActionMapping("AP_ALT_VAR_DEC", "Decrease")] [TouchPortalActionMapping("ALTITUDE_BUG_SELECT", "Select")] - [TouchPortalState("AutoPilotAltitudeVar", "text", "AutoPilot Altitude Value", "")] - public static readonly SimVarItem AP_ALTITUDE_VAR = - new SimVarItem { Def = Definition.AutoPilotAltitudeVar, SimVarName = "AUTOPILOT ALTITUDE LOCK VAR", Unit = Units.feet, CanSet = false }; + public static readonly object AP_ALTITUDE_VAR; [TouchPortalAction("AutoPilotAltitudeSet", "Altitude Hold Value Set", "MSFS", "Sets the altitude hold value", "Altitude Hold Value - {0} to {1}")] [TouchPortalActionChoice(new[] { "Set English", "Set Metric" })] [TouchPortalActionText("0")] [TouchPortalActionMapping("AP_ALT_VAR_SET_ENGLISH", "Set English")] [TouchPortalActionMapping("AP_ALT_VAR_SET_METRIC", "Set Metric")] - public static object AP_ALTITUDE_SET { get; } + public static readonly object AP_ALTITUDE_SET; #endregion #region Back Course - [SimVarDataRequest] [TouchPortalAction("AutoPilotBackCourse", "Back Course Mode", "MSFS", "Toggle/On/Off the back course mode for auto pilot", "Back Course Mode - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("AP_BC_HOLD", "Toggle")] [TouchPortalActionMapping("AP_BC_HOLD_ON", "On")] [TouchPortalActionMapping("AP_BC_HOLD_OFF", "Off")] - [TouchPortalState("AutoPilotBackCourseHold", "text", "AutoPilot Back Course Status", "")] - public static readonly SimVarItem AP_BACKCOURSE = - new SimVarItem { Def = Definition.AutoPilotBackCourseHold, SimVarName = "AUTOPILOT BACKCOURSE HOLD", Unit = Units.Bool, CanSet = false }; + public static readonly object AP_BACKCOURSE; #endregion #region Nav1 - [SimVarDataRequest] [TouchPortalAction("AutoPilotNav1", "Nav1 Mode", "MSFS", "Toggle/On/Off the Nav1 mode for auto pilot", "Nav1 Mode - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("AP_NAV1_HOLD", "Toggle")] [TouchPortalActionMapping("AP_NAV1_HOLD_ON", "On")] [TouchPortalActionMapping("AP_NAV1_HOLD_OFF", "Off")] - [TouchPortalState("AutoPilotNav1Hold", "text", "AutoPilot Nav1 Status", "")] - public static readonly SimVarItem AP_NAV1 = - new SimVarItem { Def = Definition.AutoPilotNav1Hold, SimVarName = "AUTOPILOT NAV1 LOCK", Unit = Units.Bool, CanSet = false }; + public static readonly object AP_NAV1; - [SimVarDataRequest] [TouchPortalAction("AutoPilotNavSelect", "Nav Mode - Set", "MSFS", "Sets the nav to 1 or 2 for Nav mode", "Nav Mode - {0} ")] [TouchPortalActionNumeric(1, 1, 2)] [TouchPortalActionMapping("AP_NAV_SELECT_SET")] - [TouchPortalState("AutoPilotNavSelected", "text", "AutoPilot Nav Selected Index", "")] - public static readonly SimVarItem AP_NAV_SELECT_SET = - new SimVarItem { Def = Definition.AutoPilotNavSelected, SimVarName = "AUTOPILOT NAV SELECTED", Unit = Units.number, CanSet = false }; + public static readonly object AP_NAV_SELECT_SET; #endregion #region Vertical Speed - [SimVarDataRequest] [TouchPortalAction("AutoPilotVerticalSpeed", "Vertical Speed", "MSFS", "Toggle the Vertical Speed for auto pilot", "Vertical Speed - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("AP_VS_HOLD", "Toggle")] [TouchPortalActionMapping("AP_VS_ON", "On")] [TouchPortalActionMapping("AP_VS_OFF", "Off")] - [TouchPortalState("AutoPilotVerticalSpeedHold", "text", "AutoPilot Vertical Speed Status", "")] - public static readonly SimVarItem AutoPilotVerticalSpeedHold = - new SimVarItem { Def = Definition.AutoPilotVerticalSpeedHold, SimVarName = "AUTOPILOT VERTICAL HOLD", Unit = Units.Bool, CanSet = false }; + public static readonly object AutoPilotVerticalSpeedHold; - [SimVarDataRequest] [TouchPortalAction("AutoPilotVerticalSpeedVar", "Vertical Speed Value", "MSFS", "Adjusts or selects the vertical speed value", "Vertical Speed Value - {0}", true)] [TouchPortalActionChoice(new [] { "Select", "Increase", "Decrease", "Set Current" })] [TouchPortalActionMapping("VSI_BUG_SELECT", "Select")] [TouchPortalActionMapping("AP_VS_VAR_INC", "Increase")] [TouchPortalActionMapping("AP_VS_VAR_DEC", "Decrease")] [TouchPortalActionMapping("AP_VS_VAR_SET_CURRENT", "Set Current")] - [TouchPortalState("AutoPilotVerticalSpeedVar", "text", "AutoPilot Vertical Speed Value", "")] - public static readonly SimVarItem AP_VERTICALSPEED_VAR = - new SimVarItem { Def = Definition.AutoPilotVerticalSpeedVar, SimVarName = "AUTOPILOT VERTICAL HOLD VAR", Unit = Units.feetminute, CanSet = false }; + public static readonly object AP_VERTICALSPEED_VAR; [TouchPortalAction("AutoPilotVerticalSpeedSet", "Vertical Speed Value Set", "MSFS", "Sets the vertical speed value", "Vertical Speed Hold Value - {0} to {1}")] [TouchPortalActionChoice(new[] { "Set English", "Set Metric" })] [TouchPortalActionText("1", -5000, 5000)] [TouchPortalActionMapping("AP_VS_VAR_SET_ENGLISH", "Set English")] [TouchPortalActionMapping("AP_VS_VAR_SET_METRIC", "Set Metric")] - public static object AP_VERTICALSPEED_SET { get; } + public static readonly object AP_VERTICALSPEED_SET; #endregion #region Airspeed - [SimVarDataRequest] [TouchPortalAction("AutoPilotAirSpeed", "Airspeed Hold", "MSFS", "Toggle/On/Off the airspeed hold for auto pilot", "Airspeed Hold - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("AP_AIRSPEED_HOLD", "Toggle")] [TouchPortalActionMapping("AP_AIRSPEED_ON", "On")] [TouchPortalActionMapping("AP_AIRSPEED_OFF", "Off")] - [TouchPortalState("AutoPilotAirSpeedHold", "text", "AutoPilot Air Speed Status", "")] - public static readonly SimVarItem AP_AIRSPEED = - new SimVarItem { Def = Definition.AutoPilotAirSpeedHold, SimVarName = "AUTOPILOT AIRSPEED HOLD", Unit = Units.Bool, CanSet = false }; + public static readonly object AP_AIRSPEED; - [SimVarDataRequest] [TouchPortalAction("AutoPilotAirSpeedVar", "Airspeed Hold Value", "MSFS", "Adjusts the airspeed hold value", "Airspeed Hold Value - {0}", true)] [TouchPortalActionChoice(new [] { "Select", "Increase", "Decrease" })] [TouchPortalActionMapping("AIRSPEED_BUG_SELECT", "Select")] [TouchPortalActionMapping("AP_SPD_VAR_INC", "Increase")] [TouchPortalActionMapping("AP_SPD_VAR_DEC", "Decrease")] - [TouchPortalState("AutoPilotAirSpeedVar", "text", "AutoPilot Air Speed Value", "")] - public static readonly SimVarItem AP_AIRSPEED_VAR = - new SimVarItem { Def = Definition.AutoPilotAirSpeedVar, SimVarName = "AUTOPILOT AIRSPEED HOLD VAR", Unit = Units.knots, CanSet = false, StringFormat = "{0:0.0#}" }; + public static readonly object AP_AIRSPEED_VAR; [TouchPortalAction("AutoPilotAirSpeedSet", "Airspeed Value Set", "MSFS", "Sets the airspeed value", "Set Airspeed Hold Value to {0}")] [TouchPortalActionText("0", 0, 5000)] [TouchPortalActionMapping("AP_SPD_VAR_SET")] - public static object AP_AIRSPEED_SET { get; } + public static readonly object AP_AIRSPEED_SET; #endregion #region AutoThrottle - [SimVarDataRequest] [TouchPortalAction("AutoThrottle", "Auto Throttle Mode", "MSFS", "Toggles the Arm/GoAround modes for auto throttle", "Toggle Auto Throttle - {0}")] [TouchPortalActionChoice(new [] { "Arm", "GoAround" })] [TouchPortalActionMapping("AUTO_THROTTLE_ARM", "Arm")] [TouchPortalActionMapping("AUTO_THROTTLE_TO_GA", "GoAround")] - [TouchPortalState("AutoThrottleArm", "text", "Auto Throttle Armed", "")] - public static readonly SimVarItem AUTO_THROTTLE = - new SimVarItem { Def = Definition.AutoThrottleArm, SimVarName = "AUTOPILOT THROTTLE ARM", Unit = Units.Bool, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("AutoThrottleGoAround", "text", "Auto Throttle GoAround", "")] - public static readonly SimVarItem AUTO_THROTTLE_GA = - new SimVarItem { Def = Definition.AutoThrottleGoAround, SimVarName = "AUTOPILOT TAKEOFF POWER ACTIVE", Unit = Units.Bool, CanSet = false }; - + public static readonly object AUTO_THROTTLE; #endregion @@ -272,72 +205,52 @@ internal static class AutoPilotMapping { [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INCREASE_AUTOBRAKE_CONTROL", "Increase")] [TouchPortalActionMapping("DECREASE_AUTOBRAKE_CONTROL", "Decrease")] - public static object AUTO_BRAKE { get; } + public static readonly object AUTO_BRAKE; #endregion #region Mach - [SimVarDataRequest] [TouchPortalAction("AutoPilotMach", "Mach Hold", "MSFS", "Toggle/On/Off the mach hold for auto pilot", "Mach Hold - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off"})] [TouchPortalActionMapping("AP_MACH_HOLD", "Toggle")] [TouchPortalActionMapping("AP_MACH_ON", "On")] [TouchPortalActionMapping("AP_MACH_OFF", "Off")] - [TouchPortalState("AutoPilotMach", "text", "AutoPilot Mach Hold", "")] - public static readonly SimVarItem AP_MACH = - new SimVarItem { Def = Definition.AutoPilotMach, SimVarName = "AUTOPILOT MACH HOLD", Unit = Units.Bool, CanSet = false }; + public static readonly object AP_MACH; - [SimVarDataRequest] [TouchPortalAction("AutoPilotMachVar", "Mach Hold Value", "MSFS", "Sets the mach hold value", "Mach Hold Value - {0}")] [TouchPortalActionChoice(new [] { "Select", "Increase", "Decrease" }, "Increase")] [TouchPortalActionMapping("AP_MACH_VAR_INC", "Increase")] [TouchPortalActionMapping("AP_MACH_VAR_DEC", "Decrease")] - [TouchPortalState("AutoPilotMachVar", "text", "AutoPilot Mach Value", "")] - public static readonly SimVarItem AP_MACH_VAR = - new SimVarItem { Def = Definition.AutoPilotMachVar, SimVarName = "AUTOPILOT MACH HOLD VAR", Unit = Units.number, CanSet = false }; + public static readonly object AP_MACH_VAR; [TouchPortalAction("AutoPilotMachSet", "Mach Hold Value Set", "MSFS", "Sets the mach hold value", "Set Mach Hold Value to {0}")] [TouchPortalActionText("0", 0, 20)] [TouchPortalActionMapping("AP_MACH_VAR_SET")] - public static object AP_MACH_SET { get; } + public static readonly object AP_MACH_SET; #endregion #region Flight Director - [SimVarDataRequest] [TouchPortalAction("AutoPilotFlightDirector", "Flight Director", "MSFS", "Toggle the Flight Director for auto pilot", "Toggle Flight Director On/Off")] [TouchPortalActionMapping("TOGGLE_FLIGHT_DIRECTOR")] - [TouchPortalState("AutoPilotFlightDirector", "text", "AutoPilot Flight Director Status", "")] - public static readonly SimVarItem AP_FLIGHT_DIRECTOR = - new SimVarItem { Def = Definition.AutoPilotFlightDirector, SimVarName = "AUTOPILOT FLIGHT DIRECTOR ACTIVE", Unit = Units.Bool, CanSet = false }; + public static readonly object AP_FLIGHT_DIRECTOR; - [SimVarDataRequest] [TouchPortalAction("AutoPilotFlightDirectorCurrentPitch", "Flight Director Pitch Sync", "MSFS", "Syncs the Flight Director with the current pitch", "Flight Director Pitch Sync")] [TouchPortalActionMapping("SYNC_FLIGHT_DIRECTOR_PITCH")] - [TouchPortalState("AutoPilotFlightDirectorCurrentPitch", "text", "Flight Director Current Pitch", "")] - public static readonly SimVarItem SYNC_FLIGHT_DIRECTOR_PITCH = - new SimVarItem { Def = Definition.AutoPilotFlightDirectorCurrentPitch, SimVarName = "AUTOPILOT FLIGHT DIRECTOR PITCH", Unit = Units.radians, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("AutoPilotFlightDirectorCurrentBank", "text", "Flight Director Current Bank", "")] - public static readonly SimVarItem SYNC_FLIGHT_DIRECTOR_Bank = - new SimVarItem { Def = Definition.AutoPilotFlightDirectorCurrentBank, SimVarName = "AUTOPILOT FLIGHT DIRECTOR BANK", Unit = Units.radians, CanSet = false }; + public static readonly object SYNC_FLIGHT_DIRECTOR_PITCH; #endregion #region Wing Leveler - [SimVarDataRequest] [TouchPortalAction("AutoPilotWingLeveler", "Wing Leveler", "MSFS", "Toggle/On/Off the Wing Leveler for auto pilot", "Wing Leveler - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("AP_WING_LEVELER", "Toggle")] [TouchPortalActionMapping("AP_WING_LEVELER_ON", "On")] [TouchPortalActionMapping("AP_WING_LEVELER_OFF", "Off")] - [TouchPortalState("AutoPilotWingLeveler", "text", "AutoPilot Wing Leveler", "")] - public static readonly SimVarItem AP_WING_LEVELER = - new SimVarItem { Def = Definition.AutoPilotWingLeveler, SimVarName = "AUTOPILOT WING LEVELER", Unit = Units.Bool, CanSet = false }; + public static readonly object AP_WING_LEVELER; #endregion @@ -349,21 +262,18 @@ internal static class AutoPilotMapping { [TouchPortalActionMapping("AP_LOC_HOLD", "Toggle")] [TouchPortalActionMapping("AP_LOC_HOLD_ON", "On")] [TouchPortalActionMapping("AP_LOC_HOLD_OFF", "Off")] - public static object AP_LOCALIZER { get; } + public static readonly object AP_LOCALIZER; #endregion #region Yaw Dampener - [SimVarDataRequest] [TouchPortalAction("AutoPilotYawDampener", "Yaw Dampener", "MSFS", "Toggle/On/Off the Yaw Dampener", "Yaw Dampener - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("YAW_DAMPER_TOGGLE", "Toggle")] [TouchPortalActionMapping("YAW_DAMPER_ON", "On")] [TouchPortalActionMapping("YAW_DAMPER_OFF", "Off")] - [TouchPortalState("AutoPilotYawDampener", "text", "Yaw Dampener Status", "")] - public static readonly SimVarItem AP_YAWDAMPENER = - new SimVarItem { Def = Definition.AutoPilotYawDampener, SimVarName = "AUTOPILOT YAW DAMPER", Unit = Units.Bool, CanSet = false }; + public static readonly object AP_YAWDAMPENER; #endregion diff --git a/MSFSTouchPortalPlugin/Objects/Failures/Failures.cs b/MSFSTouchPortalPlugin/Objects/Failures/Failures.cs index d3db03b..783fa46 100644 --- a/MSFSTouchPortalPlugin/Objects/Failures/Failures.cs +++ b/MSFSTouchPortalPlugin/Objects/Failures/Failures.cs @@ -2,9 +2,8 @@ using MSFSTouchPortalPlugin.Enums; using TouchPortalExtension.Attributes; -namespace MSFSTouchPortalPlugin.Objects.Failures +namespace MSFSTouchPortalPlugin.Objects.Failures { - [SimVarDataRequestGroup] [SimNotificationGroup(Groups.Failures)] [TouchPortalCategory("Failures", "MSFS - Failures")] internal static class FailuresMapping { @@ -23,6 +22,6 @@ internal static class FailuresMapping { [TouchPortalActionMapping("TOGGLE_ENGINE2_FAILURE", "Engine 2")] [TouchPortalActionMapping("TOGGLE_ENGINE3_FAILURE", "Engine 3")] [TouchPortalActionMapping("TOGGLE_ENGINE4_FAILURE", "Engine 4")] - public static object FAILURES { get; } + public static readonly object FAILURES; } } diff --git a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs index 4741222..43ebb2f 100644 --- a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs +++ b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs @@ -5,7 +5,6 @@ namespace MSFSTouchPortalPlugin.Objects.FlightSystems { - [SimVarDataRequestGroup] [SimNotificationGroup(Groups.FlightSystems)] [TouchPortalCategory("FlightSystems", "MSFS - Flight Systems")] internal static class FlightSystemsMapping { @@ -16,12 +15,12 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("CENTER_AILER_RUDDER", "Center")] [TouchPortalActionMapping("AILERONS_LEFT", "Left")] [TouchPortalActionMapping("AILERONS_RIGHT", "Right")] - public static object Ailerons { get; } + public static readonly object Ailerons; [TouchPortalAction("AileronsSet", "Ailerons Set", "MSFS", " Set Ailerons", "Ailerons set to {0} (-16384 to +16384)")] [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("AILERON_SET")] - public static object AileronsSet { get; } + public static readonly object AileronsSet; #endregion @@ -31,12 +30,12 @@ internal static class FlightSystemsMapping { [TouchPortalActionChoice(new[] { "Up", "Down" })] [TouchPortalActionMapping("ELEV_UP", "Up")] [TouchPortalActionMapping("ELEV_DOWN", "Down")] - public static object Elevator { get; } + public static readonly object Elevator; [TouchPortalAction("ElevatorSet", "Elevator Set", "MSFS", " Set Elevator", "Elevator set to {0} (-16384 to +16384)")] [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("ELEVATOR_SET")] - public static object ElevatorSet { get; } + public static readonly object ElevatorSet; #endregion @@ -47,19 +46,16 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("BRAKES", "All")] [TouchPortalActionMapping("BRAKES_LEFT", "Left")] [TouchPortalActionMapping("BRAKES_RIGHT", "Right")] - public static object Brakes { get; } + public static readonly object Brakes; - [SimVarDataRequest] [TouchPortalAction("ParkingBreak", "Toggle Parking Brake", "MSFS", "Toggle Parking Brake", "Toggle Parking Brake")] [TouchPortalActionMapping("PARKING_BRAKES")] - [TouchPortalState("ParkingBrakeIndicator", "text", "Parking Brake Indicator true/false", "")] - public static readonly SimVarItem ParkingBrake = new SimVarItem { Def = Definition.ParkingBrakeIndicator, SimVarName = "BRAKE PARKING POSITION", Unit = Units.Bool, CanSet = false }; + public static readonly object ParkingBrake; #endregion #region Flaps - [SimVarDataRequest] [TouchPortalAction("Flaps", "Flaps", "MSFS", "Flaps", "Flaps - {0}")] [TouchPortalActionChoice(new [] { "Up", "Down", "Increase", "Decrease", "1", "2", "3", "4" })] [TouchPortalActionMapping("FLAPS_UP", "Up")] @@ -70,67 +66,55 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("FLAPS_2", "2")] [TouchPortalActionMapping("FLAPS_3", "3")] [TouchPortalActionMapping("FLAPS_3", "4")] - [TouchPortalState("FlapsHandlePercent", "text", "Flaps Handle Percentage", "")] - public static readonly SimVarItem FlapsHandlePercent = new SimVarItem { Def = Definition.FlapsHandlePercent, SimVarName = "FLAPS HANDLE PERCENT", Unit = Units.percent, CanSet = false, StringFormat = "{0:0.0#}" }; + public static readonly object FlapsHandlePercent; [TouchPortalAction("FlapsSet", "Flaps Set", "MSFS", " Set Flaps", "Flaps set to {0} (0 to 16384)")] [TouchPortalActionText("0", 0, 16384)] [TouchPortalActionMapping("FLAPS_SET")] - public static object FlapsSet { get; } + public static readonly object FlapsSet; [TouchPortalAction("CowlFlapsAll", "Cowl Flaps All", "MSFS", "Cowl Flaps All", "Cowl Flaps All - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS", "Increase")] [TouchPortalActionMapping("DEC_COWL_FLAPS", "Decrease")] - public static object CowlFlapsAll { get; } + public static readonly object CowlFlapsAll; - [SimVarDataRequest] [TouchPortalAction("CowlFlaps1", "Cowl Flaps 1", "MSFS", "Cowl Flaps 1", "Cowl Flaps 1 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS1", "Increase")] [TouchPortalActionMapping("DEC_COWL_FLAPS1", "Decrease")] - [TouchPortalState("CowlFlaps1Percent", "text", "Cowl Flaps 1 Opened Percentage", "")] - public static readonly SimVarItem CowlFlaps1 = new SimVarItem { Def = Definition.CowlFlaps1Percent, SimVarName = "RECIP ENG COWL FLAP POSITION:1", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; + public static readonly object CowlFlaps1; - [SimVarDataRequest] [TouchPortalAction("CowlFlaps2", "Cowl Flaps 2", "MSFS", "Cowl Flaps 2", "Cowl Flaps 2 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS2", "Increase")] [TouchPortalActionMapping("DEC_COWL_FLAPS2", "Decrease")] - [TouchPortalState("CowlFlaps2Percent", "text", "Cowl Flaps 2 Opened Percentage", "")] - public static readonly SimVarItem CowlFlaps2 = new SimVarItem { Def = Definition.CowlFlaps2Percent, SimVarName = "RECIP ENG COWL FLAP POSITION:2", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; + public static readonly object CowlFlaps2; - [SimVarDataRequest] [TouchPortalAction("CowlFlaps3", "Cowl Flaps 3", "MSFS", "Cowl Flaps 3", "Cowl Flaps 3 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS3", "Increase")] [TouchPortalActionMapping("DEC_COWL_FLAPS3", "Decrease")] - [TouchPortalState("CowlFlaps3Percent", "text", "Cowl Flaps 3 Opened Percentage", "")] - public static readonly SimVarItem CowlFlaps3 = new SimVarItem { Def = Definition.CowlFlaps3Percent, SimVarName = "RECIP ENG COWL FLAP POSITION:3", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; + public static readonly object CowlFlaps3; - [SimVarDataRequest] [TouchPortalAction("CowlFlaps4", "Cowl Flaps 4", "MSFS", "Cowl Flaps 4", "Cowl Flaps 4 - {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" })] [TouchPortalActionMapping("INC_COWL_FLAPS4", "Increase")] [TouchPortalActionMapping("DEC_COWL_FLAPS4", "Decrease")] - [TouchPortalState("CowlFlaps4Percent", "text", "Cowl Flaps 4 Opened Percentage", "")] - public static readonly SimVarItem CowlFlaps4 = new SimVarItem { Def = Definition.CowlFlaps4Percent, SimVarName = "RECIP ENG COWL FLAP POSITION:4", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; + public static readonly object CowlFlaps4; #endregion #region Gear - [SimVarDataRequest] [TouchPortalAction("Gear", "Gear Manipulation", "MSFS", "Gear Manipulation", "Gear - {0}", true)] [TouchPortalActionChoice(new [] { "Toggle", "Up", "Down", "Pump" })] [TouchPortalActionMapping("GEAR_TOGGLE", "Toggle")] [TouchPortalActionMapping("GEAR_UP", "Up")] [TouchPortalActionMapping("GEAR_DOWN", "Down")] [TouchPortalActionMapping("GEAR_PUMP", "Pump")] - [TouchPortalState("GearTotalExtended", "text", "Total percentage of gear extended", "")] - public static readonly SimVarItem Gear = - new SimVarItem { Def = Definition.GearTotalExtended, SimVarName = "GEAR TOTAL PCT EXTENDED", Unit = Units.percentage, CanSet = false }; + public static readonly object Gear; #endregion @@ -141,117 +125,76 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("RUDDER_CENTER", "Center")] [TouchPortalActionMapping("RUDDER_LEFT", "Left")] [TouchPortalActionMapping("RUDDER_RIGHT", "Right")] - public static object Rudder { get; } + public static readonly object Rudder; [TouchPortalAction("RudderSet", "Rudder Set", "MSFS", " Set Rudder", "Rudder set to {0} (-16384 to +16384)")] [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("RUDDER_SET")] - public static object RudderSet { get; } + public static readonly object RudderSet; #endregion #region Spoilers // https://docs.flightsimulator.com/html/Programming_Tools/SimVars/Aircraft_SimVars/Aircraft_Control_Variables.htm#spoilers - [SimVarDataRequest] [TouchPortalAction("Spoilers", "Spoilers", "MSFS", "Spoilers", "Spoilers - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("SPOILERS_TOGGLE", "Toggle")] [TouchPortalActionMapping("SPOILERS_ON", "On")] [TouchPortalActionMapping("SPOILERS_OFF", "Off")] - [TouchPortalState("SpoilersAvailable", "text", "Spoilers Available (0/1)", "0")] - public static readonly SimVarItem SpoilersAvailable = new SimVarItem { Def = Definition.SpoilersAvailable, SimVarName = "SPOILER AVAILABLE", Unit = Units.Bool, CanSet = false }; + public static readonly object SpoilersAvailable; - [SimVarDataRequest] [TouchPortalAction("SpoilersSet", "Spoilers Set", "MSFS", " Set Spoilers", "Set Spoilers handle position to {0} (0 to 16384)")] [TouchPortalActionText("0", 0, 16384)] [TouchPortalActionMapping("SPOILERS_SET")] - [TouchPortalState("SpoilersHandlePosition", "text", "Spoilers Handle Position (0 - 16384)", "0")] - public static readonly SimVarItem SpoilersHandlePosition = - new SimVarItem { Def = Definition.SpoilersHandlePosition, SimVarName = "SPOILERS HANDLE POSITION", Unit = Units.position16k, CanSet = true, StringFormat = "{0:0}" }; + public static readonly object SpoilersHandlePosition; - [SimVarDataRequest] - [TouchPortalState("SpoilersLeftPosition", "text", "Spoilers Left Position Percent", "0")] - public static readonly SimVarItem SpoilersLeftPosition = - new SimVarItem { Def = Definition.SpoilersLeftPosition, SimVarName = "SPOILERS LEFT POSITION", Unit = Units.percentover100, CanSet = false, StringFormat = "{0:F1}" }; - - [SimVarDataRequest] - [TouchPortalState("SpoilersRightPosition", "text", "Spoilers Right Position Percent", "0")] - public static readonly SimVarItem SpoilersRightPosition = - new SimVarItem { Def = Definition.SpoilersRightPosition, SimVarName = "SPOILERS RIGHT POSITION", Unit = Units.percentover100, CanSet = false, StringFormat = "{0:F1}" }; - - [SimVarDataRequest] [TouchPortalAction("SpoilersArm", "Spoilers Arm", "MSFS", "Spoilers Arm", "Spoilers Arm - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("SPOILERS_ARM_TOGGLE", "Toggle")] [TouchPortalActionMapping("SPOILERS_ARM_ON", "On")] [TouchPortalActionMapping("SPOILERS_ARM_OFF", "Off")] - [TouchPortalState("SpoilersArmed", "text", "Spoilers Armed (0/1)", "0")] - public static readonly SimVarItem SpoilersArmed = new SimVarItem { Def = Definition.SpoilersArmed, SimVarName = "SPOILERS ARMED", Unit = Units.Bool, CanSet = false }; + public static readonly object SpoilersArmed; #endregion #region Trimming - [SimVarDataRequest] [TouchPortalAction("AileronTrim", "Aileron Trim", "MSFS", "Aileron Trim", "Aileron Trim - {0}", true)] [TouchPortalActionChoice(new [] { "Left", "Right" }, "Left")] [TouchPortalActionMapping("AILERON_TRIM_LEFT", "Left")] [TouchPortalActionMapping("AILERON_TRIM_RIGHT", "Right")] - [TouchPortalState("AileronTrim", "text", "Aileron Trim Angle", "")] - public static readonly SimVarItem AileronTrim = - new SimVarItem { Def = Definition.AileronTrim, SimVarName = "AILERON TRIM", Unit = Units.degrees, CanSet = false, StringFormat = "{0:0.0#}" }; + public static readonly object AileronTrim; [TouchPortalAction("AileronTrimSet", "Aileron Trim Set", "MSFS", " Set Aileron Trim", "Aileron Trim set to {0}% (-100 - +100)")] [TouchPortalActionText("0", -100, 100)] [TouchPortalActionMapping("AILERON_TRIM_SET")] - public static object AileronTrimSet { get; } + public static readonly object AileronTrimSet; - [SimVarDataRequest] [TouchPortalAction("ElevatorTrim", "Elevator Trim", "MSFS", "Elevator Trim", "Elevator Trim - {0}", true)] [TouchPortalActionChoice(new [] { "Up", "Down" })] [TouchPortalActionMapping("ELEV_TRIM_DN", "Down")] [TouchPortalActionMapping("ELEV_TRIM_UP", "Up")] - [TouchPortalState("ElevatorTrim", "text", "Elevator Trim Angle", "")] - public static readonly SimVarItem ElevatorTrim = - new SimVarItem { Def = Definition.ElevatorTrim, SimVarName = "ELEVATOR TRIM POSITION", Unit = Units.degrees, CanSet = true, StringFormat = "{0:0.0#}" }; + public static readonly object ElevatorTrim; [TouchPortalAction("ElevatorTrimSet", "Elevator Trim Set", "MSFS", " Set Elevator Trim", "Elevator Trim set to {0} (-16384 to +16384)")] [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("ELEVATOR_TRIM_SET")] - public static object ElevatorTrimSet { get; } + public static readonly object ElevatorTrimSet; - [SimVarDataRequest] [TouchPortalAction("RudderTrim", "Rudder Trim", "MSFS", "Rudder Trim", "Rudder Trim - {0}", true)] [TouchPortalActionChoice(new [] { "Left", "Right" })] [TouchPortalActionMapping("RUDDER_TRIM_LEFT", "Left")] [TouchPortalActionMapping("RUDDER_TRIM_RIGHT", "Right")] - [TouchPortalState("RudderTrim", "text", "Rudder Trim Angle", "")] - public static readonly SimVarItem RudderTrim = - new SimVarItem { Def = Definition.RudderTrim, SimVarName = "RUDDER TRIM", Unit = Units.degrees, CanSet = false, StringFormat = "{0:0.0#}" }; + public static readonly object RudderTrim; [TouchPortalAction("RudderTrimSet", "Rudder Trim Set", "MSFS", " Set Rudder Trim", "Rudder Trim set to {0}% (-100 - +100)")] [TouchPortalActionText("0", -100, 100)] [TouchPortalActionMapping("RUDDER_TRIM_SET")] - public static object RudderTrimSet { get; } - - - [SimVarDataRequest] // XYZ - [TouchPortalState("AileronTrimPct", "text", "Aileron Trim Percent", "0")] - public static readonly SimVarItem AileronTrimPct = - new SimVarItem { Def = Definition.AileronTrimPct, SimVarName = "AILERON TRIM PCT", Unit = Units.percentover100, CanSet = true, StringFormat = "{0:F1}" }; - - [SimVarDataRequest] - [TouchPortalState("ElevatorTrimPct", "text", "Elevator Trim Percent", "0")] - public static readonly SimVarItem ElevatorTrimPct = - new SimVarItem { Def = Definition.ElevatorTrimPct, SimVarName = "ELEVATOR TRIM PCT", Unit = Units.percentover100, CanSet = false, StringFormat = "{0:F1}" }; + public static readonly object RudderTrimSet; - [SimVarDataRequest] - [TouchPortalState("RudderTrimPct", "text", "Rudder Trim Percent", "0")] - public static readonly SimVarItem RudderTrimPct = - new SimVarItem { Def = Definition.RudderTrimPct, SimVarName = "RUDDER TRIM PCT", Unit = Units.percentover100, CanSet = true, StringFormat = "{0:F1}" }; #endregion } } diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Communication.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Communication.cs index ae1730a..006c5c8 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Communication.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Communication.cs @@ -3,9 +3,8 @@ using MSFSTouchPortalPlugin.Enums; using TouchPortalExtension.Attributes; -namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems +namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { - [SimVarDataRequestGroup] [SimNotificationGroup(Groups.Communication)] [TouchPortalCategory("Communication", "MSFS - Communication")] internal static class CommunicationMapping { @@ -41,47 +40,8 @@ internal static class CommunicationMapping { [TouchPortalActionMapping("NAV2_RADIO_FRACT_DEC_CARRY", new[] { "NAV2", "Decrease 25 KHz w/ Carry Digits" })] [TouchPortalActionMapping("NAV2_RADIO_FRACT_INC", new[] { "NAV2", "Increase 25 KHz" })] [TouchPortalActionMapping("NAV2_RADIO_FRACT_INC_CARRY", new[] { "NAV2", "Increase 25 KHz w/ Carry Digits" })] - public static object Radios { get; } + public static readonly object Radios; - [SimVarDataRequest] - [TouchPortalState("Com1ActiveFrequency", "text", "The frequency of the active COM1 radio", "")] - public static readonly SimVarItem Com1ActiveFrequency = - new SimVarItem { Def = Definition.Com1ActiveFrequency, SimVarName = "COM ACTIVE FREQUENCY:1", Unit = Units.MHz, CanSet = false, StringFormat = "{0:0.000#}" }; - - [SimVarDataRequest] - [TouchPortalState("Com1StandbyFrequency", "text", "The frequency of the standby COM1 radio", "")] - public static readonly SimVarItem Com1StandbyFrequency = - new SimVarItem { Def = Definition.Com1StandbyFrequency, SimVarName = "COM STANDBY FREQUENCY:1", Unit = Units.MHz, CanSet = false, StringFormat = "{0:0.000#}" }; - - [SimVarDataRequest] - [TouchPortalState("Com2ActiveFrequency", "text", "The frequency of the active COM2 radio", "")] - public static readonly SimVarItem Com2ActiveFrequency = - new SimVarItem { Def = Definition.Com2ActiveFrequency, SimVarName = "COM ACTIVE FREQUENCY:2", Unit = Units.MHz, CanSet = false, StringFormat = "{0:0.000#}" }; - - [SimVarDataRequest] - [TouchPortalState("Com2StandbyFrequency", "text", "The frequency of the standby COM2 radio", "")] - public static readonly SimVarItem Com2StandbyFrequency = - new SimVarItem { Def = Definition.Com2StandbyFrequency, SimVarName = "COM STANDBY FREQUENCY:2", Unit = Units.MHz, CanSet = false, StringFormat = "{0:0.000#}" }; - - [SimVarDataRequest] - [TouchPortalState("Nav1ActiveFrequency", "text", "The frequency of the active NAV1 radio", "")] - public static readonly SimVarItem Nav1ActiveFrequency = - new SimVarItem { Def = Definition.Nav1ActiveFrequency, SimVarName = "NAV ACTIVE FREQUENCY:1", Unit = Units.MHz, CanSet = false, StringFormat = "{0:0.000#}" }; - - [SimVarDataRequest] - [TouchPortalState("Nav1StandbyFrequency", "text", "The frequency of the standby NAV1 radio", "")] - public static readonly SimVarItem Nav1StandbyFrequency = - new SimVarItem { Def = Definition.Nav1StandbyFrequency, SimVarName = "NAV STANDBY FREQUENCY:1", Unit = Units.MHz, CanSet = false, StringFormat = "{0:0.000#}" }; - - [SimVarDataRequest] - [TouchPortalState("Nav2ActiveFrequency", "text", "The frequency of the active NAV2 radio", "")] - public static readonly SimVarItem Nav2ActiveFrequency = - new SimVarItem { Def = Definition.Nav2ActiveFrequency, SimVarName = "NAV ACTIVE FREQUENCY:2", Unit = Units.MHz, CanSet = false, StringFormat = "{0:0.000#}" }; - - [SimVarDataRequest] - [TouchPortalState("Nav2StandbyFrequency", "text", "The frequency of the standby NAV2 radio", "")] - public static readonly SimVarItem Nav2StandbyFrequency = - new SimVarItem { Def = Definition.Nav2StandbyFrequency, SimVarName = "NAV STANDBY FREQUENCY:2", Unit = Units.MHz, CanSet = false, StringFormat = "{0:0.000#}" }; } /* diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs index e188adc..9dab9b0 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs @@ -5,39 +5,30 @@ namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { - [SimVarDataRequestGroup] [SimNotificationGroup(Groups.Electrical)] [TouchPortalCategory("Electrical", "MSFS - Electrical")] internal static class ElectricalMapping { #region Avionics - [SimVarDataRequest] [TouchPortalAction("AvionicsMasterSwitch", "Avionics Master", "MSFS", "Toggle Avionics Master", "Toggle Avionics Master")] [TouchPortalActionMapping("TOGGLE_AVIONICS_MASTER")] - [TouchPortalState("AvionicsMasterSwitch", "text", "Avionics Master Switch", "")] - public static readonly SimVarItem TOGGLE_AVIONICS_MASTER = new SimVarItem { Def = Definition.AvionicsMasterSwitch, SimVarName = "AVIONICS MASTER SWITCH", Unit = Units.Bool, CanSet = false }; + public static readonly object TOGGLE_AVIONICS_MASTER; #endregion #region Alternator & Battery - [SimVarDataRequest] [TouchPortalAction("MasterAlternator", "Master Alternator", "MSFS", "Toggle Master Alternator", "Toggle Master Alternator")] [TouchPortalActionMapping("TOGGLE_MASTER_ALTERNATOR")] - [TouchPortalState("MasterAlternator", "text", "Master Alternator Status", "")] - public static readonly SimVarItem MASTER_ALTERNATOR = - new SimVarItem { Def = Definition.MasterAlternator, SimVarName = "GENERAL ENG MASTER ALTERNATOR:1", Unit = Units.Bool, CanSet = false }; + public static readonly object MASTER_ALTERNATOR; - [SimVarDataRequest] [TouchPortalAction("MasterBattery", "Master Battery", "MSFS", "Toggle Master Battery", "Toggle Master Battery")] [TouchPortalActionMapping("TOGGLE_MASTER_BATTERY")] - [TouchPortalState("MasterBattery", "text", "Master Battery Status", "")] - public static readonly SimVarItem MASTER_BATTERY = - new SimVarItem { Def = Definition.MasterBattery, SimVarName = "ELECTRICAL MASTER BATTERY", Unit = Units.Bool, CanSet = false }; + public static readonly object MASTER_BATTERY; [TouchPortalAction("MasterBatteryAlternator", "Master Battery & Alternator", "MSFS", "Toggle Master Battery & Alternator", "Toggle Master Battery & Alternator")] [TouchPortalActionMapping("TOGGLE_MASTER_BATTERY_ALTERNATOR")] - public static object MASTER_BATTERY_ALTERNATOR { get; } + public static readonly object MASTER_BATTERY_ALTERNATOR; [TouchPortalAction("AlternatorIndex", "Alternator - Specific", "MSFS", "Toggle Specific Alternator", "Toggle Altenator - {0}")] [TouchPortalActionChoice(new[] { "1", "2", "3", "4" })] @@ -45,7 +36,7 @@ internal static class ElectricalMapping { [TouchPortalActionMapping("TOGGLE_ALTERNATOR2", "2")] [TouchPortalActionMapping("TOGGLE_ALTERNATOR3", "3")] [TouchPortalActionMapping("TOGGLE_ALTERNATOR4", "4")] - public static object ALTERNATOR_INDEX { get; } + public static readonly object ALTERNATOR_INDEX; #endregion @@ -56,21 +47,21 @@ internal static class ElectricalMapping { [TouchPortalActionMapping("STROBES_TOGGLE", "Toggle")] [TouchPortalActionMapping("STROBES_ON", "On")] [TouchPortalActionMapping("STROBES_OFF", "Off")] - public static object STROBE_LIGHTS { get; } + public static readonly object STROBE_LIGHTS; [TouchPortalAction("PanelLights", "Toggle/On/Off Panel Lights", "MSFS", "Toggle/On/Off Panel Lights", "Panel Lights - {0}")] [TouchPortalActionChoice(new[] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("PANEL_LIGHTS_TOGGLE", "Toggle")] [TouchPortalActionMapping("PANEL_LIGHTS_ON", "On")] [TouchPortalActionMapping("PANEL_LIGHTS_OFF", "Off")] - public static object PANEL_LIGHTS { get; } + public static readonly object PANEL_LIGHTS; [TouchPortalAction("LandingLights", "Toggle/On/Off Landing Lights", "MSFS", "Toggle/On/Off Landing Lights", "Landing Lights - {0}")] [TouchPortalActionChoice(new[] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("LANDING_LIGHTS_TOGGLE", "Toggle")] [TouchPortalActionMapping("LANDING_LIGHTS_ON", "On")] [TouchPortalActionMapping("LANDING_LIGHTS_OFF", "Off")] - public static object LANDING_LIGHTS { get; } + public static readonly object LANDING_LIGHTS; [TouchPortalAction("ToggleLights", "Toggle All/Specific Lights", "MSFS", "Toggle All/Specific Lights", "Toggle Lights - {0}")] [TouchPortalActionChoice(new[] { "All", "Beacon", "Taxi", "Logo", "Recognition", "Wing", "Nav", "Cabin" })] @@ -82,44 +73,7 @@ internal static class ElectricalMapping { [TouchPortalActionMapping("TOGGLE_WING_LIGHTS", "Wing")] [TouchPortalActionMapping("TOGGLE_NAV_LIGHTS", "Nav")] [TouchPortalActionMapping("TOGGLE_CABIN_LIGHTS", "Cabin")] - public static object ALL_LIGHTS { get; } - - [SimVarDataRequest] - [TouchPortalState("LightBeaconOn", "text", "Light Beacon Switch Status", "")] - public static readonly SimVarItem LightBeaconOn = new SimVarItem { Def = Definition.LightBeaconOn, SimVarName = "LIGHT BEACON", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("LightBrakeOn", "text", "Light Brake Switch or Light Status", "")] - public static readonly SimVarItem LightBrakeOn = new SimVarItem { Def = Definition.LightBrakeOn, SimVarName = "LIGHT BRAKE ON", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("LightCabinOn", "text", "Light Cabin Switch Status", "")] - public static readonly SimVarItem LightCabinOn = new SimVarItem { Def = Definition.LightCabinOn, SimVarName = "LIGHT CABIN", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("LightHeadOn", "text", "Light Head Switch or Light Status", "")] - public static readonly SimVarItem LightHeadOn = new SimVarItem { Def = Definition.LightHeadOn, SimVarName = "LIGHT HEAD ON", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("LightLandingOn", "text", "Light Landing Switch Status", "")] - public static readonly SimVarItem LightLandingOn = new SimVarItem { Def = Definition.LightLandingOn, SimVarName = "LIGHT LANDING", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("LightLogoOn", "text", "Light Logo Switch Status", "")] - public static readonly SimVarItem LightLogoOn = new SimVarItem { Def = Definition.LightLogoOn, SimVarName = "LIGHT LOGO", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("LightNavOn", "text", "Light Nav Switch Status", "")] - public static readonly SimVarItem LightNavOn = new SimVarItem { Def = Definition.LightNavOn, SimVarName = "LIGHT NAV", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("LightPanelOn", "text", "Light Panel Switch Status", "")] - public static readonly SimVarItem LightPanelOn = new SimVarItem { Def = Definition.LightPanelOn, SimVarName = "LIGHT PANEL", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("LightRecognitionOn", "text", "Light Recognition Switch Status", "")] - public static readonly SimVarItem LightRecognitionOn = new SimVarItem { Def = Definition.LightRecognitionOn, SimVarName = "LIGHT RECOGNITION", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("LightStrobeOn", "text", "Light Strobe Switch Status", "")] - public static readonly SimVarItem LightStrobeOn = new SimVarItem { Def = Definition.LightStrobeOn, SimVarName = "LIGHT STROBE", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("LightTaxiOn", "text", "Light Taxi Switch Status", "")] - public static readonly SimVarItem LightTaxiOn = new SimVarItem { Def = Definition.LightTaxiOn, SimVarName = "LIGHT TAXI", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("LightWingOn", "text", "Light Wing Switch Status", "")] - public static readonly SimVarItem LightWingOn = new SimVarItem { Def = Definition.LightWingOn, SimVarName = "LIGHT WING", Unit = Units.Bool, CanSet = false }; + public static readonly object ALL_LIGHTS; #endregion diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs index 957f29c..e33de1e 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs @@ -5,24 +5,21 @@ namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { - [SimVarDataRequestGroup] [SimNotificationGroup(Groups.Engine)] [TouchPortalCategory("Engine", "MSFS - Engine")] internal static class EngineMapping { #region Ignition - [SimVarDataRequest] [TouchPortalAction("MasterIgnition", "Master Ignition Switch", "MSFS", "Toggle Master Ignition Switch", "Toggle Master Ignition Switch")] [TouchPortalActionMapping("TOGGLE_MASTER_IGNITION_SWITCH")] - [TouchPortalState("MasterIgnitionSwitch", "text", "Master Ignition Switch Status", "")] - public static readonly SimVarItem MASTER_IGNITION = new SimVarItem { Def = Definition.MasterIgnitionSwitch, SimVarName = "MASTER IGNITION SWITCH", Unit = Units.Bool, CanSet = false }; + public static readonly object MASTER_IGNITION; [TouchPortalAction("EngineAuto", "Engine Auto Start/Shutdown", "MSFS", "Start/Shutdown Engine", "Engine - {0}")] [TouchPortalActionChoice(new [] { "Start", "Shutdown" }, "Start")] [TouchPortalActionMapping("ENGINE_AUTO_START", "Start")] [TouchPortalActionMapping("ENGINE_AUTO_SHUTDOWN", "Shutdown")] - public static object ENGINE_AUTO { get; } + public static readonly object ENGINE_AUTO; #endregion @@ -38,7 +35,7 @@ internal static class EngineMapping { [TouchPortalActionMapping("MAGNETO_DECR", "Decrease")] [TouchPortalActionMapping("MAGNETO_INCR", "Increase")] [TouchPortalActionMapping("MAGNETO", "Select (for +/-)")] - public static object ALL_MAGNETOS { get; } + public static readonly object ALL_MAGNETOS; [TouchPortalAction("MagnetoSpecific", "Magnetos Specific", "MSFS", "Toggle Magneto Specific", "Toggle Magneto {0} - {1}")] [TouchPortalActionChoice(new [] { "1", "2", "3", "4" }, "1")] @@ -74,7 +71,7 @@ internal static class EngineMapping { [TouchPortalActionMapping("MAGNETO4_START", new[] { "4", "Start" })] [TouchPortalActionMapping("MAGNETO4_DECR", new[] { "4", "Decrease" })] [TouchPortalActionMapping("MAGNETO4_INCR", new[] { "4", "Increase" })] - public static object MAGNETO_SPECIFIC { get; } + public static readonly object MAGNETO_SPECIFIC; [TouchPortalAction("MagnetoSet", "Magnetos Set", "MSFS", "Set Magneto Switch Position", "Magneto Switch {0} to position {1} (0-4)")] [TouchPortalActionChoice(new[] { "1", "2", "3", "4" })] @@ -85,7 +82,7 @@ internal static class EngineMapping { [TouchPortalActionMapping("MAGNETO2_SET", "2")] [TouchPortalActionMapping("MAGNETO3_SET", "3")] [TouchPortalActionMapping("MAGNETO4_SET", "4")] - public static object MAGNETO_SET { get; } + public static readonly object MAGNETO_SET; #endregion @@ -98,7 +95,7 @@ internal static class EngineMapping { [TouchPortalActionMapping("TOGGLE_STARTER2", "2")] [TouchPortalActionMapping("TOGGLE_STARTER3", "3")] [TouchPortalActionMapping("TOGGLE_STARTER4", "4")] - public static object STARTERS { get; } + public static readonly object STARTERS; #endregion @@ -121,7 +118,7 @@ internal static class EngineMapping { [TouchPortalActionMapping("THROTTLE_70", "70%")] [TouchPortalActionMapping("THROTTLE_80", "80%")] [TouchPortalActionMapping("THROTTLE_90", "90%")] - public static object THROTTLE { get; } + public static readonly object THROTTLE; [TouchPortalAction("ThrottleSpecific", "Throttle Specific", "MSFS", "Sets Throttle on specific engine", "Throttle {0} - {1}", true)] [TouchPortalActionChoice(new [] { "1", "2", "3", "4" }, "1")] @@ -150,7 +147,7 @@ internal static class EngineMapping { [TouchPortalActionMapping("THROTTLE4_DECR", new[] { "4", "Decrease" })] [TouchPortalActionMapping("THROTTLE4_DECR_SMALL", new[] { "4", "Decrease Small" })] [TouchPortalActionMapping("THROTTLE4_CUT", new[] { "4", "Cut" })] - public static object THROTTLE_SPECIFIC { get; } + public static readonly object THROTTLE_SPECIFIC; [TouchPortalAction("ThrottleSet", "Throttle Set", "MSFS", "Sets all or specific Throttle(s) to specific value", "Set Throttle {0} to {1} (-16384 to +16384)")] [TouchPortalActionChoice(new[] { "All", "1", "2", "3", "4" }, "All")] @@ -160,24 +157,7 @@ internal static class EngineMapping { [TouchPortalActionMapping("THROTTLE2_SET", new[] { "2" })] [TouchPortalActionMapping("THROTTLE3_SET", new[] { "3" })] [TouchPortalActionMapping("THROTTLE4_SET", new[] { "4" })] - public static object THROTTLE_SET { get; } - - - [SimVarDataRequest] - [TouchPortalState("ThrottleEngine1", "text", "Throttle - Engine 1 - Percentage", "")] - public static readonly SimVarItem ThrottleEngine1 = new SimVarItem { Def = Definition.ThrottleEngine1, SimVarName = "GENERAL ENG THROTTLE LEVER POSITION:1", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.#}" }; - - [SimVarDataRequest] - [TouchPortalState("ThrottleEngine2", "text", "Throttle - Engine 2 - Percentage", "")] - public static readonly SimVarItem ThrottleEngine2 = new SimVarItem { Def = Definition.ThrottleEngine2, SimVarName = "GENERAL ENG THROTTLE LEVER POSITION:2", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.#}" }; - - [SimVarDataRequest] - [TouchPortalState("ThrottleEngine3", "text", "Throttle - Engine 3 - Percentage", "")] - public static readonly SimVarItem ThrottleEngine3 = new SimVarItem { Def = Definition.ThrottleEngine3, SimVarName = "GENERAL ENG THROTTLE LEVER POSITION:3", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.#}" }; - - [SimVarDataRequest] - [TouchPortalState("ThrottleEngine4", "text", "Throttle - Engine 4 - Percentage", "")] - public static readonly SimVarItem ThrottleEngine4 = new SimVarItem { Def = Definition.ThrottleEngine4, SimVarName = "GENERAL ENG THROTTLE LEVER POSITION:4", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.#}" }; + public static readonly object THROTTLE_SET; #endregion @@ -192,7 +172,7 @@ internal static class EngineMapping { [TouchPortalActionMapping("MIXTURE_DECR_SMALL", "Decrease Small")] [TouchPortalActionMapping("MIXTURE_LEAN", "Lean")] [TouchPortalActionMapping("MIXTURE_SET_BEST", "Best")] - public static object MIXTURE { get; } + public static readonly object MIXTURE; [TouchPortalAction("MixtureSpecific", "Mixture Specific", "MSFS", "Sets mixture on specific engine", "Mixture {0} - {1}", true)] [TouchPortalActionChoice(new [] { "1", "2", "3", "4" }, "1")] @@ -224,23 +204,7 @@ internal static class EngineMapping { [TouchPortalActionMapping("MIXTURE4_DECR", new[] { "4", "Decrease" })] [TouchPortalActionMapping("MIXTURE4_DECR_SMALL", new[] { "4", "Decrease Small" })] [TouchPortalActionMapping("MIXTURE4_LEAN", new[] { "4", "Lean" })] - public static object MIXTURE_SPECIFIC { get; } - - [SimVarDataRequest] - [TouchPortalState("MixtureEngine1", "text", "Mixture - Engine 1 - Percentage", "")] - public static readonly SimVarItem MixtureEngine1 = new SimVarItem { Def = Definition.MixtureEngine1, SimVarName = "GENERAL ENG MIXTURE LEVER POSITION:1", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("MixtureEngine2", "text", "Mixture - Engine 2 - Percentage", "")] - public static readonly SimVarItem MixtureEngine2 = new SimVarItem { Def = Definition.MixtureEngine2, SimVarName = "GENERAL ENG MIXTURE LEVER POSITION:2", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("MixtureEngine3", "text", "Mixture - Engine 3 - Percentage", "")] - public static readonly SimVarItem MixtureEngine3 = new SimVarItem { Def = Definition.MixtureEngine3, SimVarName = "GENERAL ENG MIXTURE LEVER POSITION:3", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("MixtureEngine4", "text", "Mixture - Engine 4 - Percentage", "")] - public static readonly SimVarItem MixtureEngine4 = new SimVarItem { Def = Definition.MixtureEngine4, SimVarName = "GENERAL ENG MIXTURE LEVER POSITION:4", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; + public static readonly object MIXTURE_SPECIFIC; #endregion @@ -288,7 +252,7 @@ internal static class EngineMapping { [TouchPortalActionMapping("PROP_PITCH4_HI", new[] { "4", "Min (hi pitch)" })] [TouchPortalActionMapping("PROP_PITCH4_LO", new[] { "4", "Max (lo pitch)" })] [TouchPortalActionMapping("TOGGLE_FEATHER_SWITCH_4", new[] { "4", "Toggle Feather Switch" })] - public static object PROPELLER_PITCH { get; } + public static readonly object PROPELLER_PITCH; [TouchPortalAction("PropellerPitchSet", "Propeller Pitch Set", "MSFS", "Sets propeller pitch lever to value", "Set Propeller {0} Pitch to {1} (0 to 16384)")] [TouchPortalActionChoice(new[] { "All", "1", "2", "3", "4" })] @@ -298,88 +262,10 @@ internal static class EngineMapping { [TouchPortalActionMapping("PROP_PITCH2_SET", "2")] [TouchPortalActionMapping("PROP_PITCH3_SET", "3")] [TouchPortalActionMapping("PROP_PITCH4_SET", "4")] - public static object PROPELLER_PITCH_SET { get; } - - [SimVarDataRequest] - [TouchPortalState("PropellerEngine1", "text", "Propeller - Engine 1 - Percentage", "")] - public static readonly SimVarItem PropellerEngine1 = new SimVarItem { Def = Definition.PropellerEngine1, SimVarName = "GENERAL ENG PROPELLER LEVER POSITION:1", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("PropellerEngine2", "text", "Propeller - Engine 2 - Percentage", "")] - public static readonly SimVarItem PropellerEngine2 = new SimVarItem { Def = Definition.PropellerEngine2, SimVarName = "GENERAL ENG PROPELLER LEVER POSITION:2", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("PropellerEngine3", "text", "Propeller - Engine 3 - Percentage", "")] - public static readonly SimVarItem PropellerEngine3 = new SimVarItem { Def = Definition.PropellerEngine3, SimVarName = "GENERAL ENG PROPELLER LEVER POSITION:3", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("PropellerEngine4", "text", "Propeller - Engine 4 - Percentage", "")] - public static readonly SimVarItem PropellerEngine4 = new SimVarItem { Def = Definition.PropellerEngine4, SimVarName = "GENERAL ENG PROPELLER LEVER POSITION:4", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("Propeller1FeatherSw", "text", "Propeller - Engine 1 - Feather Switch State (bool)", "")] - public static readonly SimVarItem Propeller1FeatherSw = new SimVarItem { Def = Definition.Propeller1FeatherSw, SimVarName = "PROP FEATHER SWITCH:1", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("Propeller2FeatherSw", "text", "Propeller - Engine 2 - Feather Switch State (bool)", "")] - public static readonly SimVarItem Propeller2FeatherSw = new SimVarItem { Def = Definition.Propeller2FeatherSw, SimVarName = "PROP FEATHER SWITCH:2", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("Propeller3FeatherSw", "text", "Propeller - Engine 3 - Feather Switch State (bool)", "")] - public static readonly SimVarItem Propeller3FeatherSw = new SimVarItem { Def = Definition.Propeller3FeatherSw, SimVarName = "PROP FEATHER SWITCH:3", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("Propeller4FeatherSw", "text", "Propeller - Engine 4 - Feather Switch State (bool)", "")] - public static readonly SimVarItem Propeller4FeatherSw = new SimVarItem { Def = Definition.Propeller4FeatherSw, SimVarName = "PROP FEATHER SWITCH:4", Unit = Units.Bool, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("Propeller1Feathered", "text", "Propeller - Engine 1 - Feathered (bool)", "")] - public static readonly SimVarItem Propeller1Feathered = new SimVarItem { Def = Definition.Propeller1Feathered, SimVarName = "PROP FEATHERED:1", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("Propeller2Feathered", "text", "Propeller - Engine 2 - Feathered (bool)", "")] - public static readonly SimVarItem Propeller2Feathered = new SimVarItem { Def = Definition.Propeller2Feathered, SimVarName = "PROP FEATHERED:2", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("Propeller3Feathered", "text", "Propeller - Engine 3 - Feathered (bool)", "")] - public static readonly SimVarItem Propeller3Feathered = new SimVarItem { Def = Definition.Propeller3Feathered, SimVarName = "PROP FEATHERED:3", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("Propeller4Feathered", "text", "Propeller - Engine 4 - Feathered (bool)", "")] - public static readonly SimVarItem Propeller4Feathered = new SimVarItem { Def = Definition.Propeller4Feathered, SimVarName = "PROP FEATHERED:4", Unit = Units.Bool, CanSet = false }; - + public static readonly object PROPELLER_PITCH_SET; #endregion - #region RPM - - [SimVarDataRequest] - [TouchPortalState("RPMN1Engine1", "text", "RPM - Engine 1", "")] - public static readonly SimVarItem RPMN1Engine1 = new SimVarItem { Def = Definition.RPMN1Engine1, SimVarName = "ENG N1 RPM:1", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("RPMN1Engine2", "text", "RPM - Engine 2", "")] - public static readonly SimVarItem RPMN1Engine2 = new SimVarItem { Def = Definition.RPMN1Engine2, SimVarName = "ENG N1 RPM:2", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("RPMN1Engine3", "text", "RPM - Engine 3", "")] - public static readonly SimVarItem RPMN1Engine3 = new SimVarItem { Def = Definition.RPMN1Engine3, SimVarName = "ENG N1 RPM:3", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("RPMN1Engine4", "text", "RPM - Engine 4", "")] - public static readonly SimVarItem RPMN1Engine4 = new SimVarItem { Def = Definition.RPMN1Engine4, SimVarName = "ENG N1 RPM:4", Unit = Units.percent, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("RPMPropeller1", "text", "RPM - Propeller 1", "")] - public static readonly SimVarItem RPMPropeller1 = new SimVarItem { Def = Definition.RPMPropeller1, SimVarName = "PROP RPM:1", Unit = Units.rpm, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("RPMPropeller2", "text", "RPM - Propeller 2", "")] - public static readonly SimVarItem RPMPropeller2 = new SimVarItem { Def = Definition.RPMPropeller2, SimVarName = "PROP RPM:2", Unit = Units.rpm, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("RPMPropeller3", "text", "RPM - Propeller 3", "")] - public static readonly SimVarItem RPMPropeller3 = new SimVarItem { Def = Definition.RPMPropeller3, SimVarName = "PROP RPM:3", Unit = Units.rpm, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("RPMPropeller4", "text", "RPM - Propeller 4", "")] - public static readonly SimVarItem RPMPropeller4 = new SimVarItem { Def = Definition.RPMPropeller4, SimVarName = "PROP RPM:4", Unit = Units.rpm, CanSet = true, StringFormat = "{0:0.0#}" }; - - #endregion } } diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Environment.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Environment.cs index d1f6022..9ae570e 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Environment.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Environment.cs @@ -5,7 +5,6 @@ namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { - [SimVarDataRequestGroup] [SimNotificationGroup(Groups.Environment)] [TouchPortalCategory("Environment", "MSFS - Environment")] internal static class EnvironmentMapping { @@ -16,7 +15,7 @@ internal static class EnvironmentMapping { [TouchPortalActionMapping("ANTI_ICE_TOGGLE", "Toggle")] [TouchPortalActionMapping("ANTI_ICE_ON", "On")] [TouchPortalActionMapping("ANTI_ICE_OFF", "Off")] - public static object AntiIce { get; } + public static readonly object AntiIce; [TouchPortalAction("AntiIceEng", "Anti-Ice Engine", "MSFS", "Toggle Anti Ice Engine", "Anti Ice Engine {0} Toggle")] [TouchPortalActionChoice(new[] { "1", "2", "3", "4" })] @@ -24,7 +23,7 @@ internal static class EnvironmentMapping { [TouchPortalActionMapping("ANTI_ICE_TOGGLE_ENG2", "2")] [TouchPortalActionMapping("ANTI_ICE_TOGGLE_ENG3", "3")] [TouchPortalActionMapping("ANTI_ICE_TOGGLE_ENG4", "4")] - public static object AntiIceEng { get; } + public static readonly object AntiIceEng; [TouchPortalAction("AntiIceEngSet", "Anti-Ice Engine Set", "MSFS", "Set On/Off Anti Ice Engine", "Anti Ice Engine {0} - {1}")] [TouchPortalActionChoice(new[] { "1", "2", "3", "4" })] @@ -33,85 +32,26 @@ internal static class EnvironmentMapping { [TouchPortalActionMapping("ANTI_ICE_SET_ENG2", "2")] [TouchPortalActionMapping("ANTI_ICE_SET_ENG3", "3")] [TouchPortalActionMapping("ANTI_ICE_SET_ENG4", "4")] - public static object ANTI_ICE_ENGINE { get; } - - [SimVarDataRequest] - [TouchPortalState("AntiIceEng1", "text", "Anti-Ice Engine 1", "")] - public static readonly SimVarItem AntiIceEng1 = new SimVarItem { Def = Definition.AntiIceEng1, SimVarName = "GENERAL ENG ANTI ICE POSITION:1", Unit = Units.Bool, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("AntiIceEng2", "text", "Anti-Ice Engine 2", "")] - public static readonly SimVarItem AntiIceEng2 = new SimVarItem { Def = Definition.AntiIceEng2, SimVarName = "GENERAL ENG ANTI ICE POSITION:2", Unit = Units.Bool, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("AntiIceEng3", "text", "Anti-Ice Engine 3", "")] - public static readonly SimVarItem AntiIceEng3 = new SimVarItem { Def = Definition.AntiIceEng3, SimVarName = "GENERAL ENG ANTI ICE POSITION:3", Unit = Units.Bool, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("AntiIceEng4", "text", "Anti-Ice Engine 4", "")] - public static readonly SimVarItem AntiIceEng4 = new SimVarItem { Def = Definition.AntiIceEng4, SimVarName = "GENERAL ENG ANTI ICE POSITION:4", Unit = Units.Bool, CanSet = false }; + public static readonly object ANTI_ICE_ENGINE; [TouchPortalAction("StructuralDeIce", "Structural De-ice", "MSFS", "Toggle Structural DeIce", "Toggle Structural DeIce")] [TouchPortalActionMapping("TOGGLE_STRUCTURAL_DEICE")] - public static object STRUCTURAL_DEICE { get; } + public static readonly object STRUCTURAL_DEICE; [TouchPortalAction("PropellerDeIce", "Propeller De-ice", "MSFS", "Toggle Propeller DeIce", "Toggle Propeller DeIce")] [TouchPortalActionMapping("TOGGLE_PROPELLER_DEICE")] - public static object PROPELLER_DEICE { get; } - - [SimVarDataRequest] - [TouchPortalState("AntiIcePanelSwitch", "text", "Panel Anti-Ice Switch", "")] - public static readonly SimVarItem AntiIcePanelSwitch = new SimVarItem { Def = Definition.AntiIcePanelSwitch, SimVarName = "PANEL ANTI ICE SWITCH", Unit = Units.Bool, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("AntiIceStructuralSwitch", "text", "Structural Deice Switch", "")] - public static readonly SimVarItem AntiIceStructuralSwitch = new SimVarItem { Def = Definition.AntiIceStructuralSwitch, SimVarName = "STRUCTURAL DEICE SWITCH", Unit = Units.Bool, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("AntiIceWindshieldSwitch", "text", "Windshield Deice Switch", "")] - public static readonly SimVarItem AntiIceWindshieldSwitch = new SimVarItem { Def = Definition.AntiIceWindshieldSwitch, SimVarName = "WINDSHIELD DEICE SWITCH", Unit = Units.Bool, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("AntiIcePropeller1Switch", "text", "Propeller 1 Deice Switch", "")] - public static readonly SimVarItem AntiIcePropeller1Switch = new SimVarItem { Def = Definition.AntiIcePropeller1Switch, SimVarName = "PROP DEICE SWITCH:1", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("AntiIcePropeller2Switch", "text", "Propeller 2 Deice Switch", "")] - public static readonly SimVarItem AntiIcePropeller2Switch = new SimVarItem { Def = Definition.AntiIcePropeller2Switch, SimVarName = "PROP DEICE SWITCH:2", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("AntiIcePropeller3Switch", "text", "Propeller 3 Deice Switch", "")] - public static readonly SimVarItem AntiIcePropeller3Switch = new SimVarItem { Def = Definition.AntiIcePropeller3Switch, SimVarName = "PROP DEICE SWITCH:3", Unit = Units.Bool, CanSet = false }; - [SimVarDataRequest] - [TouchPortalState("AntiIcePropeller4Switch", "text", "Propeller 4 Deice Switch", "")] - public static readonly SimVarItem AntiIcePropeller4Switch = new SimVarItem { Def = Definition.AntiIcePropeller4Switch, SimVarName = "PROP DEICE SWITCH:4", Unit = Units.Bool, CanSet = false }; + public static readonly object PROPELLER_DEICE; #endregion #region Pitot Heat - [SimVarDataRequest] [TouchPortalAction("PitotHeat", "Pitot Heat", "MSFS", "Toggle/On/Off Pitot Heat", "Pitot Heat - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] [TouchPortalActionMapping("PITOT_HEAT_TOGGLE", "Toggle")] [TouchPortalActionMapping("PITOT_HEAT_ON", "On")] [TouchPortalActionMapping("PITOT_HEAT_OFF", "Off")] - [TouchPortalState("PitotHeat", "text", "Pitot Heat Status", "")] - public static readonly SimVarItem PITOT_HEAT = new SimVarItem { Def = Definition.PitotHeat, SimVarName = "PITOT HEAT", Unit = Units.Bool, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("PitotHeatSwitch1", "text", "Pitot Heat Switch 1 State (0=Off; 1=On; 2=Auto)", "")] - public static readonly SimVarItem PitotHeatSwitch1 = new SimVarItem { Def = Definition.PitotHeatSwitch1, SimVarName = "PITOT HEAT SWITCH:1", Unit = Units.Enum, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("PitotHeatSwitch2", "text", "Pitot Heat Switch 2 State (0=Off; 1=On; 2=Auto)", "")] - public static readonly SimVarItem PitotHeatSwitch2 = new SimVarItem { Def = Definition.PitotHeatSwitch2, SimVarName = "PITOT HEAT SWITCH:2", Unit = Units.Enum, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("PitotHeatSwitch3", "text", "Pitot Heat Switch 3 State (0=Off; 1=On; 2=Auto)", "")] - public static readonly SimVarItem PitotHeatSwitch3 = new SimVarItem { Def = Definition.PitotHeatSwitch3, SimVarName = "PITOT HEAT SWITCH:3", Unit = Units.Enum, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("PitotHeatSwitch4", "text", "Pitot Heat Switch 4 State (0=Off; 1=On; 2=Auto)", "")] - public static readonly SimVarItem PitotHeatSwitch4 = new SimVarItem { Def = Definition.PitotHeatSwitch4, SimVarName = "PITOT HEAT SWITCH:4", Unit = Units.Enum, CanSet = false }; + public static readonly object PITOT_HEAT; #endregion } diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/FlightInstruments.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/FlightInstruments.cs index 114cd0c..6b72158 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/FlightInstruments.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/FlightInstruments.cs @@ -2,91 +2,11 @@ using MSFSTouchPortalPlugin.Constants; using TouchPortalExtension.Attributes; -namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems +namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { - [SimVarDataRequestGroup] //[SimNotificationGroup(Groups.FlightInstruments)] [TouchPortalCategory("FlightInstruments", "MSFS - Flight Instruments")] internal static class FlightInstrumentsMapping { - #region Velocity - - [SimVarDataRequest] - [TouchPortalState("GroundVelocity", "text", "Ground Speed in Knots", "")] - public static readonly SimVarItem GroundVelocity = new SimVarItem { Def = Definition.GroundVelocity, SimVarName = "GROUND VELOCITY", Unit = Units.knots, CanSet = false, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("AirSpeedTrue", "text", "Air speed true in Knots", "")] - public static readonly SimVarItem AirSpeedTrue = new SimVarItem { Def = Definition.AirSpeedTrue, SimVarName = "AIRSPEED TRUE", Unit = Units.knots, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("AirSpeedIndicated", "text", "Air speed indicated in Knots", "")] - public static readonly SimVarItem AirSpeedIndicated = new SimVarItem { Def = Definition.AirSpeedIndicated, SimVarName = "AIRSPEED INDICATED", Unit = Units.knots, CanSet = true, StringFormat = "{0:0.0#}" }; - - [SimVarDataRequest] - [TouchPortalState("AirSpeedMach", "text", "Air speed indicated in mach", "")] - public static readonly SimVarItem AirSpeedMach = new SimVarItem { Def = Definition.AirSpeedMach, SimVarName = "AIRSPEED MACH", Unit = Units.mach, CanSet = true, StringFormat = "{0:0.0#}" }; - #endregion - - #region Altitude - - [SimVarDataRequest] - [TouchPortalState("PlaneAltitude", "text", "Plane Altitude in Feet", "")] - public static readonly SimVarItem PlaneAltitude = new SimVarItem { Def = Definition.PlaneAltitude, SimVarName = "PLANE ALTITUDE", Unit = Units.feet, CanSet = false, StringFormat = "{0:0.#}" }; - - [SimVarDataRequest] - [TouchPortalState("PlaneAltitudeAGL", "text", "Plane Altitude AGL in Feet", "")] - public static readonly SimVarItem PlaneAltitudeAGL = new SimVarItem { Def = Definition.PlaneAltitudeAGL, SimVarName = "PLANE ALT ABOVE GROUND", Unit = Units.feet, CanSet = false, StringFormat = "{0:0.#}" }; - - [SimVarDataRequest] - [TouchPortalState("GroundAltitude", "text", "Ground level in Feet", "")] - public static readonly SimVarItem GroundAltitude = new SimVarItem { Def = Definition.GroundAltitude, SimVarName = "GROUND ALTITUDE", Unit = Units.feet, CanSet = false, StringFormat = "{0:0.#}" }; - - #endregion - - #region Heading - - [SimVarDataRequest] - [TouchPortalState("PlaneHeadingTrue", "text", "Plane Heading (True North) in Degrees", "")] - public static readonly SimVarItem PlaneHeadingTrue = new SimVarItem { Def = Definition.PlaneHeadingTrue, SimVarName = "PLANE HEADING DEGREES TRUE", Unit = Units.radians, CanSet = false, StringFormat = "{0:0}" }; - - [SimVarDataRequest] - [TouchPortalState("PlaneHeadingMagnetic", "text", "Plane Heading (Magnetic North) in Degrees", "")] - public static readonly SimVarItem PlaneHeadingMagnetic = new SimVarItem { Def = Definition.PlaneHeadingMagnetic, SimVarName = "PLANE HEADING DEGREES MAGNETIC", Unit = Units.radians, CanSet = false, StringFormat = "{0:0}" }; - - #endregion - - #region Bank and Pitch - - [SimVarDataRequest] - [TouchPortalState("PlaneBankAngle", "text", "Plane Bank Angle in Degrees", "")] - public static readonly SimVarItem PlaneBankAngle = new SimVarItem { Def = Definition.PlaneBankAngle, SimVarName = "PLANE BANK DEGREES", Unit = Units.radians, CanSet = false, StringFormat = "{0:0}" }; - - [SimVarDataRequest] - [TouchPortalState("PlanePitchAngle", "text", "Plane Pitch Angle in Degrees", "")] - public static readonly SimVarItem PlanePitchAngle = new SimVarItem { Def = Definition.PlanePitchAngle, SimVarName = "PLANE PITCH DEGREES", Unit = Units.radians, CanSet = false, StringFormat = "{0:0}" }; - - [SimVarDataRequest] - [TouchPortalState("VerticalSpeed", "text", "Vertical Speed in feet per minute", "")] - public static readonly SimVarItem VerticalSpeed = new SimVarItem { Def = Definition.VerticalSpeed, SimVarName = "VERTICAL SPEED", Unit = Units.feetminute, CanSet = true, StringFormat = "{0:0.0#}" }; - - #endregion - - #region Warnings - - [SimVarDataRequest] - [TouchPortalState("StallWarning", "text", "Stall Warning true/false", "")] - public static readonly SimVarItem StallWarning = new SimVarItem { Def = Definition.StallWarning, SimVarName = "STALL WARNING", Unit = Units.Bool, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("OverspeedWarning", "text", "Overspeed Warning true/false", "")] - public static readonly SimVarItem OverspeedWarning = new SimVarItem { Def = Definition.OverspeedWarning, SimVarName = "OVERSPEED WARNING", Unit = Units.Bool, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("FlapSpeedExceeeded", "text", "Flap Speed Exceeded Warning true/false", "")] - public static readonly SimVarItem FlapSpeedExceeeded = new SimVarItem { Def = Definition.FlapSpeedExceeeded, SimVarName = "FLAP SPEED EXCEEDED", Unit = Units.Bool, CanSet = false }; - - #endregion - } } diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs index a5fb5d2..a59882d 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs @@ -2,16 +2,15 @@ using MSFSTouchPortalPlugin.Enums; using TouchPortalExtension.Attributes; -namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems +namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { - [SimVarDataRequestGroup] [SimNotificationGroup(Groups.Fuel)] [TouchPortalCategory("InstrumentsSystems.Fuel", "MSFS - Fuel")] internal static class FuelMapping { [TouchPortalAction("AddFuel", "Add Fuel", "MSFS", "Adds 25% amount of Fuel", "Add 25% amount of fuel")] [TouchPortalActionMapping("ADD_FUEL_QUANTITY")] - public static object ADD_FUEL { get; } + public static readonly object ADD_FUEL; [TouchPortalAction("FuelSelectors", "Fuel Selectors", "MSFS", "Fuel Selectors", "Fuel Selector {0} - {1}")] [TouchPortalActionChoice(new [] { "1", "2", "3", "4" })] @@ -55,7 +54,7 @@ internal static class FuelMapping { [TouchPortalActionMapping("FUEL_SELECTOR_4_LEFT_AUX", new[] { "4", "Left - Aux" })] [TouchPortalActionMapping("FUEL_SELECTOR_4_RIGHT_AUX", new[] { "4", "Right - Aux" })] [TouchPortalActionMapping("FUEL_SELECTOR_4_CENTER", new[] { "4", "Center" })] - public static object FUEL_SELECTORS { get; } + public static readonly object FUEL_SELECTORS; [TouchPortalAction("Primers", "Toggle All/Specific Primers", "MSFS", "Toggle All/Specific Primers", "Toggle Primers - {0}")] [TouchPortalActionChoice(new [] { "All", "1", "2", "3", "4" })] @@ -64,18 +63,18 @@ internal static class FuelMapping { [TouchPortalActionMapping("TOGGLE_PRIMER2", "2")] [TouchPortalActionMapping("TOGGLE_PRIMER3", "3")] [TouchPortalActionMapping("TOGGLE_PRIMER4", "4")] - public static object PRIMERS { get; } + public static readonly object PRIMERS; [TouchPortalAction("FuelDump", "Fuel Dump - Toggle", "MSFS", "Toggles the Fuel Dump", "Toggle Fuel Dump")] [TouchPortalActionMapping("FUEL_DUMP_TOGGLE")] - public static object FUEL_DUMP { get; } + public static readonly object FUEL_DUMP; [TouchPortalAction("CrossFeed", "Toggle/Open/Off Cross Feed", "MSFS", "Toggle/Open/Off Cross Feed", "Cross Feed - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "Open", "Off" }, "Open")] [TouchPortalActionMapping("CROSS_FEED_TOGGLE", "Toggle")] [TouchPortalActionMapping("CROSS_FEED_OPEN", "Open")] [TouchPortalActionMapping("CROSS_FEED_OFF", "Off")] - public static object CROSS_FEED { get; } + public static readonly object CROSS_FEED; [TouchPortalAction("FuelValve", "Toggle All/Specific Fuel Valve", "MSFS", "Toggle All/Specific Fuel Valve", "Toggle Fuel Valve - {0}")] [TouchPortalActionChoice(new [] { "All", "1", "2", "3", "4" })] @@ -84,13 +83,13 @@ internal static class FuelMapping { [TouchPortalActionMapping("TOGGLE_FUEL_VALVE_ENG2", "2")] [TouchPortalActionMapping("TOGGLE_FUEL_VALVE_ENG3", "3")] [TouchPortalActionMapping("TOGGLE_FUEL_VALVE_ENG4", "4")] - public static object FUEL_VALVE { get; } + public static readonly object FUEL_VALVE; #region Fuel Pump [TouchPortalAction("FuelPump", "Fuel Pump - Toggle", "MSFS", "Toggles the Fuel Pump", "Toggle Fuel Pump")] [TouchPortalActionMapping("FUEL_PUMP")] - public static object FUEL_PUMP { get; } + public static readonly object FUEL_PUMP; [TouchPortalAction("ElectricFuelPump", "Electric Fuel Pump - Toggle", "MSFS", "Toggles the Electric Fuel Pump", "Toggle Electric Fuel Pump - {0}")] [TouchPortalActionChoice(new [] { "All", "1", "2", "3", "4" })] @@ -99,7 +98,7 @@ internal static class FuelMapping { [TouchPortalActionMapping("TOGGLE_ELECT_FUEL_PUMP2", "2")] [TouchPortalActionMapping("TOGGLE_ELECT_FUEL_PUMP3", "3")] [TouchPortalActionMapping("TOGGLE_ELECT_FUEL_PUMP4", "4")] - public static object ELECTRIC_FUEL_PUMP { get; } + public static readonly object ELECTRIC_FUEL_PUMP; #endregion } diff --git a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs index 8c1b481..26126b6 100644 --- a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs +++ b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs @@ -13,7 +13,6 @@ internal static class PluginMapping { [TouchPortalActionMapping("ToggleConnection", "Toggle")] [TouchPortalActionMapping("Connect", "On")] [TouchPortalActionMapping("Disconnect", "Off")] - [TouchPortalState("Connected", "text", "The status of SimConnect (true/false/connecting)", "false")] public static readonly object Connection; } @@ -40,7 +39,6 @@ public static class Settings { [TouchPortalActionMapping("ActionRepeatIntervalSet", "Set")] [TouchPortalActionMapping("ActionRepeatIntervalInc", "Increment")] [TouchPortalActionMapping("ActionRepeatIntervalDec", "Decrement")] - [TouchPortalState("ActionRepeatInterval", "text", "The current Held Action Repeat Rate (ms)", "450")] public static readonly PluginSetting ActionRepeatInterval = new PluginSetting("ActionRepeatInterval", 50, int.MaxValue); } diff --git a/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs b/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs index eab4325..5428e94 100644 --- a/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs +++ b/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs @@ -1,52 +1,23 @@ using MSFSTouchPortalPlugin.Attributes; -using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; using TouchPortalExtension.Attributes; namespace MSFSTouchPortalPlugin.Objects.SimSystem { - [SimVarDataRequestGroup] [SimNotificationGroup(Groups.SimSystem)] [TouchPortalCategory("SimSystem", "MSFS - System")] internal static class SimSystemMapping { - [SimVarDataRequest] [TouchPortalAction("SimulationRate", "Simulation Rate", "MSFS", "Simulation Rate", "Rate {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" }, "Decrease")] [TouchPortalActionMapping("SIM_RATE_INCR", "Increase")] [TouchPortalActionMapping("SIM_RATE_DECR", "Decrease")] - [TouchPortalState("SimulationRate", "text", "The current simulation rate", "")] - public static readonly SimVarItem SimulationRate = - new SimVarItem { Def = Definition.SimulationRate, SimVarName = "SIMULATION RATE", Unit = Units.number, CanSet = false }; + public static readonly object SimulationRate; - [SimVarDataRequest] [TouchPortalAction("SelectedParameter", "Change Selected Value (+/-)", "MSFS", "Selected Value", "Value {0}", true)] [TouchPortalActionChoice(new[] { "Increase", "Decrease" }, "Decrease")] [TouchPortalActionMapping("PLUS", "Increase")] [TouchPortalActionMapping("MINUS", "Decrease")] - public static object SELECTED_PARAMETER_CHANGE { get; } + public static readonly object SELECTED_PARAMETER_CHANGE; - [SimVarDataRequest] - [TouchPortalState("AtcType", "text", "Type of aircraft used by ATC", "")] - public static readonly SimVarItem AtcType = new SimVarItem { Def = Definition.AtcType, SimVarName = "ATC TYPE", Unit = Units.String, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("AtcModel", "text", "Model of aircraft used by ATC", "")] - public static readonly SimVarItem AtcModel = new SimVarItem { Def = Definition.AtcModel, SimVarName = "ATC MODEL", Unit = Units.String, CanSet = false }; - - [SimVarDataRequest] - [TouchPortalState("AtcId", "text", "Aircraft Id used by ATC", "")] - public static readonly SimVarItem AtcId = new SimVarItem { Def = Definition.AtcId, SimVarName = "ATC ID", Unit = Units.String, CanSet = true }; - - [SimVarDataRequest] - [TouchPortalState("AtcAirline", "text", "Airline used by ATC", "")] - public static readonly SimVarItem AtcAirline = new SimVarItem { Def = Definition.AtcAirline, SimVarName = "ATC AIRLINE", Unit = Units.String, CanSet = true }; - - [SimVarDataRequest] - [TouchPortalState("AtcFlightNumber", "text", "Flight Number used by ATC", "")] - public static readonly SimVarItem AtcFlightNumber = new SimVarItem { Def = Definition.AtcFlightNumber, SimVarName = "ATC FLIGHT NUMBER", Unit = Units.String, CanSet = true }; - - [SimVarDataRequest] - [TouchPortalState("AircraftTitle", "text", "Aircraft Title", "")] - public static readonly SimVarItem AircraftTitle = new SimVarItem { Def = Definition.AircraftTitle, SimVarName = "TITLE", Unit = Units.String, CanSet = false }; } } From 2ea73d4c90493a2955d6d510815eff3fa0fc39f0 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 07:26:25 -0400 Subject: [PATCH 23/93] Remove obsolete attribute types. --- .../Attributes/SimVarDataRequestAttribute.cs | 6 ------ .../Attributes/SimVarDataRequestGroupAttribute.cs | 6 ------ .../Attributes/TouchPortalCategoryMappingAttribute.cs | 10 ---------- .../Attributes/TouchPortalStateAttribute.cs | 5 ++++- 4 files changed, 4 insertions(+), 23 deletions(-) delete mode 100644 MSFSTouchPortalPlugin/Attributes/SimVarDataRequestAttribute.cs delete mode 100644 MSFSTouchPortalPlugin/Attributes/SimVarDataRequestGroupAttribute.cs delete mode 100644 MSFSTouchPortalPlugin/Attributes/TouchPortalCategoryMappingAttribute.cs diff --git a/MSFSTouchPortalPlugin/Attributes/SimVarDataRequestAttribute.cs b/MSFSTouchPortalPlugin/Attributes/SimVarDataRequestAttribute.cs deleted file mode 100644 index 1c5d693..0000000 --- a/MSFSTouchPortalPlugin/Attributes/SimVarDataRequestAttribute.cs +++ /dev/null @@ -1,6 +0,0 @@ -using System; - -namespace MSFSTouchPortalPlugin.Attributes { - internal class SimVarDataRequestAttribute : Attribute { - } -} diff --git a/MSFSTouchPortalPlugin/Attributes/SimVarDataRequestGroupAttribute.cs b/MSFSTouchPortalPlugin/Attributes/SimVarDataRequestGroupAttribute.cs deleted file mode 100644 index 0f82b3d..0000000 --- a/MSFSTouchPortalPlugin/Attributes/SimVarDataRequestGroupAttribute.cs +++ /dev/null @@ -1,6 +0,0 @@ -using System; - -namespace MSFSTouchPortalPlugin.Attributes { - internal class SimVarDataRequestGroupAttribute : Attribute { - } -} diff --git a/MSFSTouchPortalPlugin/Attributes/TouchPortalCategoryMappingAttribute.cs b/MSFSTouchPortalPlugin/Attributes/TouchPortalCategoryMappingAttribute.cs deleted file mode 100644 index f81b238..0000000 --- a/MSFSTouchPortalPlugin/Attributes/TouchPortalCategoryMappingAttribute.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace MSFSTouchPortalPlugin.Attributes { - internal class TouchPortalCategoryMappingAttribute : Attribute { - public string CategoryId; - public TouchPortalCategoryMappingAttribute(string categoryId) { - CategoryId = categoryId; - } - } -} diff --git a/TouchPortalExtension/Attributes/TouchPortalStateAttribute.cs b/TouchPortalExtension/Attributes/TouchPortalStateAttribute.cs index 006e80a..5e5a8ce 100644 --- a/TouchPortalExtension/Attributes/TouchPortalStateAttribute.cs +++ b/TouchPortalExtension/Attributes/TouchPortalStateAttribute.cs @@ -1,6 +1,9 @@ using System; -namespace TouchPortalExtension.Attributes { +namespace TouchPortalExtension.Attributes +{ + // Currently unused, preserved for posterity (or possible future use). + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class TouchPortalStateAttribute : Attribute { public string Id { get; set; } From f4164649da6cc2f9cb0582d51e320dded27a651d Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 07:28:18 -0400 Subject: [PATCH 24/93] Move SimVarItem and Definition to Types namespace. Remove unused Definition enums. --- .../GenerateDoc.cs | 2 +- .../GenerateEntry.cs | 2 +- .../Configuration/PluginConfig.cs | 4 +- MSFSTouchPortalPlugin/Constants/SimVars.cs | 160 +----------------- .../Interfaces/ISimConnectService.cs | 7 +- .../Services/PluginService.cs | 1 - .../Services/SimConnectService.cs | 1 - 7 files changed, 17 insertions(+), 160 deletions(-) diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index 42f69f3..51916b7 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -12,7 +12,7 @@ using System.Reflection; using System.Text; using TouchPortalExtension.Attributes; -using MSFSTouchPortalPlugin.Constants; +using MSFSTouchPortalPlugin.Types; namespace MSFSTouchPortalPlugin_Generator { diff --git a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs index d70bbb8..242eb62 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs @@ -1,8 +1,8 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using MSFSTouchPortalPlugin.Configuration; -using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; +using MSFSTouchPortalPlugin.Types; using MSFSTouchPortalPlugin_Generator.Configuration; using MSFSTouchPortalPlugin_Generator.Interfaces; using MSFSTouchPortalPlugin_Generator.Model; diff --git a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs index 8f1a0b1..d14018a 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs +++ b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs @@ -1,5 +1,5 @@ -using MSFSTouchPortalPlugin.Constants; -using MSFSTouchPortalPlugin.Enums; +using MSFSTouchPortalPlugin.Enums; +using MSFSTouchPortalPlugin.Types; using System; using System.Collections.Generic; using System.Linq; diff --git a/MSFSTouchPortalPlugin/Constants/SimVars.cs b/MSFSTouchPortalPlugin/Constants/SimVars.cs index a08106b..0ef0a60 100644 --- a/MSFSTouchPortalPlugin/Constants/SimVars.cs +++ b/MSFSTouchPortalPlugin/Constants/SimVars.cs @@ -1,157 +1,15 @@ -using MSFSTouchPortalPlugin.Enums; -using MSFSTouchPortalPlugin.Types; +using MSFSTouchPortalPlugin.Constants; +using MSFSTouchPortalPlugin.Enums; using Stopwatch = System.Diagnostics.Stopwatch; using SIMCONNECT_DATATYPE = Microsoft.FlightSimulator.SimConnect.SIMCONNECT_DATATYPE; -namespace MSFSTouchPortalPlugin.Constants +namespace MSFSTouchPortalPlugin.Types { - // TODO: Remove all values except Init, move to Enums folder/namespace + /// Dynamically generated SimConnect definition IDs are "parented" to this enum type, + /// meaning they become of this Type when they need to be cast to en Enum type (eg. for SimConnect C# API). public enum Definition { - None = 0, - AileronTrimPct, - AircraftTitle, - AirSpeedIndicated, - AirSpeedMach, - AirSpeedTrue, - AntiIceEng1, - AntiIceEng2, - AntiIceEng3, - AntiIceEng4, - AntiIcePanelSwitch, - AntiIcePropeller1Switch, - AntiIcePropeller2Switch, - AntiIcePropeller3Switch, - AntiIcePropeller4Switch, - AntiIceStructuralSwitch, - AntiIceWindshieldSwitch, - AtcAirline, - AtcFlightNumber, - AtcId, - AtcModel, - AtcType, - AutoPilotAirSpeedHold, - AutoPilotAirSpeedVar, - AutoPilotAltitudeHold, - AutoPilotAltitudeVar, - AutoPilotApproachHold, - AutoPilotAttitudeHold, - AutoPilotAttitudeVar, - AutoPilotAvailable, - AutoPilotBackCourseHold, - AutoPilotBanking, - AutoPilotFlightDirector, - AutoPilotFlightDirectorCurrentBank, - AutoPilotFlightDirectorCurrentPitch, - AutoPilotHeadingHold, - AutoPilotHeadingVar, - AutoPilotMach, - AutoPilotMachVar, - AutoPilotMaster, - AutoPilotNav1Hold, - AutoPilotNavSelected, - AutoPilotPitchHold, - AutoPilotVerticalSpeedHold, - AutoPilotVerticalSpeedVar, - AutoPilotWingLeveler, - AutoPilotYawDampener, - AutoThrottleArm, - AutoThrottleGoAround, - AvionicsMasterSwitch, - Com1ActiveFrequency, - Com1StandbyFrequency, - Com2ActiveFrequency, - Com2StandbyFrequency, - CowlFlaps1Percent, - CowlFlaps2Percent, - CowlFlaps3Percent, - CowlFlaps4Percent, - ElevatorTrimPct, - FlapsHandlePercent, - FlapSpeedExceeeded, - GroundAltitude, - GroundVelocity, - LightBeaconOn, - LightBrakeOn, - LightCabinOn, - LightHeadOn, - LightLandingOn, - LightLogoOn, - LightNavOn, - LightPanelOn, - LightRecognitionOn, - LightStrobeOn, - LightTaxiOn, - LightWingOn, - MasterAlternator, - MasterBattery, - MasterIgnitionSwitch, - MixtureEngine1, - MixtureEngine2, - MixtureEngine3, - MixtureEngine4, - Nav1ActiveFrequency, - Nav1StandbyFrequency, - Nav2ActiveFrequency, - Nav2StandbyFrequency, - OverspeedWarning, - ParkingBrakeIndicator, - PitotHeat, - PitotHeatSwitch1, - PitotHeatSwitch2, - PitotHeatSwitch3, - PitotHeatSwitch4, - PlaneAltitude, - PlaneAltitudeAGL, - PlaneBankAngle, - PlaneHeadingTrue, - PlaneHeadingMagnetic, - PlanePitchAngle, - Propeller1Feathered, - Propeller2Feathered, - Propeller3Feathered, - Propeller4Feathered, - Propeller1FeatherSw, - Propeller2FeatherSw, - Propeller3FeatherSw, - Propeller4FeatherSw, - PropellerEngine1, - PropellerEngine2, - PropellerEngine3, - PropellerEngine4, - RPMN1Engine1, - RPMN1Engine2, - RPMN1Engine3, - RPMN1Engine4, - RPMPropeller1, - RPMPropeller2, - RPMPropeller3, - RPMPropeller4, - RudderTrimPct, - SimulationRate, - SpoilersArmed, - SpoilersAvailable, - SpoilersHandlePosition, - SpoilersLeftPosition, - SpoilersRightPosition, - StallWarning, - ThrottleEngine1, - ThrottleEngine2, - ThrottleEngine3, - ThrottleEngine4, - VerticalSpeed, - - #region Landing Gear - GearTotalExtended, - #endregion - - #region Trimming - AileronTrim, - ElevatorTrim, - RudderTrim, - #endregion - - Init = 1000, + None = 0 } /// @@ -273,7 +131,7 @@ private set { public bool IsBooleanType { get; private set; } /// Unique Definition ID for SimConnect - public Definition Def { get; set; } // TODO: make read-only + public Definition Def { get; private set; } /// The SimConnect data type for registering this var. public SIMCONNECT_DATATYPE SimConnectDataType { get; private set; } /// Indicates if a SimConnect request for this variable is already pending. @@ -287,7 +145,7 @@ private set { private string _formatString; private long _timeoutTicks; - private static Definition _nextDefinionId = Definition.Init; + private static Definition _nextDefinionId = Definition.None; private static Definition NextId() => ++_nextDefinionId; public SimVarItem() { @@ -354,7 +212,7 @@ internal bool SetValue(object value) { } } - public double ConvertValueIfNeeded(double value) { + private double ConvertValueIfNeeded(double value) { // Convert to Degrees if (Unit == Units.radians) return value * (180.0 / System.Math.PI); diff --git a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs index 5b113c3..fcfabf7 100644 --- a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs +++ b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs @@ -1,12 +1,13 @@ -using MSFSTouchPortalPlugin.Constants; -using MSFSTouchPortalPlugin.Enums; +using MSFSTouchPortalPlugin.Enums; +using MSFSTouchPortalPlugin.Types; using System; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] -namespace MSFSTouchPortalPlugin.Interfaces { +namespace MSFSTouchPortalPlugin.Interfaces +{ internal delegate void DataUpdateEventHandler(Definition def, Definition req, object data); internal delegate void ConnectEventHandler(); internal delegate void DisconnectEventHandler(); diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 809cfbb..3fdae75 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -1,7 +1,6 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using MSFSTouchPortalPlugin.Configuration; -using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Interfaces; using MSFSTouchPortalPlugin.Objects.Plugin; using MSFSTouchPortalPlugin.Types; diff --git a/MSFSTouchPortalPlugin/Services/SimConnectService.cs b/MSFSTouchPortalPlugin/Services/SimConnectService.cs index 4c520e4..f6ab400 100644 --- a/MSFSTouchPortalPlugin/Services/SimConnectService.cs +++ b/MSFSTouchPortalPlugin/Services/SimConnectService.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.Logging; using Microsoft.FlightSimulator.SimConnect; -using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Interfaces; using MSFSTouchPortalPlugin.Types; From 5e710a37aab612eb475e959635c070b37414a5d2 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 07:31:54 -0400 Subject: [PATCH 25/93] Move/rename SimVars.cs -> SimVarItem.cs (no code changes). --- .../{Constants/SimVars.cs => Types/SimVarItem.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename MSFSTouchPortalPlugin/{Constants/SimVars.cs => Types/SimVarItem.cs} (99%) diff --git a/MSFSTouchPortalPlugin/Constants/SimVars.cs b/MSFSTouchPortalPlugin/Types/SimVarItem.cs similarity index 99% rename from MSFSTouchPortalPlugin/Constants/SimVars.cs rename to MSFSTouchPortalPlugin/Types/SimVarItem.cs index 0ef0a60..5df1feb 100644 --- a/MSFSTouchPortalPlugin/Constants/SimVars.cs +++ b/MSFSTouchPortalPlugin/Types/SimVarItem.cs @@ -15,7 +15,7 @@ public enum Definition /// /// The SimVarItem which defines all data variables for SimConnect /// - public class SimVarItem // TODO: Move to Types folder/namespace + public class SimVarItem { /// Unique ID string, used to generate TouchPortal state ID (and possibly other uses). public string Id { get; set; } From cf736dd41bcf1d3f31cd37e401b41e68d074ee68 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 07:59:52 -0400 Subject: [PATCH 26/93] [build] Define DEBUG_REQUESTS for debug builds; Add .editorconfig. --- .editorconfig | 13 +++++++++++++ MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj | 8 +++++--- 2 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..67c3249 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# overrides for top-level config +[*.cs] +indent_style=space +indent_size = 2 +insert_final_newline=true +csharp_new_line_before_open_brace=properties,types + +# CA1416: Validate platform compatibility +dotnet_diagnostic.CA1416.severity = none +# S125: Sections of code should not be commented out +dotnet_diagnostic.S125.severity=none +# S3358: Ternary operators should not be nested +dotnet_diagnostic.S3358.severity = none diff --git a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj index 5c88416..6936cfa 100644 --- a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj +++ b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj @@ -13,19 +13,21 @@ x64 + DEBUG;TRACE;DEBUG_REQUESTS x64 - 1701;1702;S125;CS8032 + 1701;1702 .dll none - false + false + - + From 4e469c83b6b36dc3ff80b46d6fd973b050e943c5 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 08:00:52 -0400 Subject: [PATCH 27/93] [workflows] Run verify-build.yml on mp/next branch changes. --- .github/workflows/verify-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/verify-build.yml b/.github/workflows/verify-build.yml index fe93758..6675928 100644 --- a/.github/workflows/verify-build.yml +++ b/.github/workflows/verify-build.yml @@ -2,9 +2,9 @@ name: verify-build on: push: - branches: [ mp/main, nightly-build ] + branches: [ mp/main, mp/next ] pull_request: - branches: [ mp/main, nightly-build ] + branches: [ mp/main, mp/next ] jobs: build: From be8f3257e296d7b93619c2fba65269a9fa2be7c5 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 08:01:23 -0400 Subject: [PATCH 28/93] [git] Ignore double underscore folders. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5cfe8e1..6149084 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ packages-dist/ MSFSTouchPortalPlugin-Tests/TestResults/ .sonarqube/ *.user +__*/ From df934e44778789730df6642e45c3589e543bfc84 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 10:13:31 -0400 Subject: [PATCH 29/93] Add custom TouchPortalSDK lib. --- .../lib/TouchPortalSDK/LICENSE.md | 7 +++++++ .../lib/TouchPortalSDK/TouchPortalSDK.dll | Bin 0 -> 38912 bytes 2 files changed, 7 insertions(+) create mode 100644 MSFSTouchPortalPlugin/lib/TouchPortalSDK/LICENSE.md create mode 100644 MSFSTouchPortalPlugin/lib/TouchPortalSDK/TouchPortalSDK.dll diff --git a/MSFSTouchPortalPlugin/lib/TouchPortalSDK/LICENSE.md b/MSFSTouchPortalPlugin/lib/TouchPortalSDK/LICENSE.md new file mode 100644 index 0000000..2c838ea --- /dev/null +++ b/MSFSTouchPortalPlugin/lib/TouchPortalSDK/LICENSE.md @@ -0,0 +1,7 @@ +# The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/MSFSTouchPortalPlugin/lib/TouchPortalSDK/TouchPortalSDK.dll b/MSFSTouchPortalPlugin/lib/TouchPortalSDK/TouchPortalSDK.dll new file mode 100644 index 0000000000000000000000000000000000000000..db99087950e4824f8f65cf19eb2de447efa52eec GIT binary patch literal 38912 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 z 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 Xt((zPd Date: Thu, 31 Mar 2022 10:26:34 -0400 Subject: [PATCH 30/93] Switch to using updated TouchPortalClient. Add CTRL-C exit handler. Prevent recursion during shutdown. --- .../MSFSTouchPortalPlugin.csproj | 10 +++- .../Services/PluginService.cs | 54 ++++++++++++++----- MSFSTouchPortalPlugin/appsettings.json | 2 +- 3 files changed, 50 insertions(+), 16 deletions(-) diff --git a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj index 6936cfa..8b4f6e1 100644 --- a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj +++ b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj @@ -55,13 +55,21 @@ - + + + .\lib\TouchPortalSDK\TouchPortalSDK.dll + true + false + + + + .\lib\SharpConfig\SharpConfig.dll diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 3fdae75..f94a98f 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Collections.Concurrent; using System.Linq; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; using TouchPortalExtension.Enums; @@ -31,6 +30,7 @@ internal class PluginService : IPluginService, IDisposable, ITouchPortalEventHan private readonly IReflectionService _reflectionService; private static readonly System.Data.DataTable _expressionEvaluator = new(); // used to evaluate basic math in action data private bool autoReconnectSimConnect = false; + private bool _quitting; public string PluginId => "MSFSTouchPortalPlugin"; @@ -108,10 +108,22 @@ public Task StartAsync(CancellationToken cancellationToken) { _hostApplicationLifetime.ApplicationStopping.Register(() => { // Disconnect from SimConnect + _quitting = true; autoReconnectSimConnect = false; _simConnectService.Disconnect(); + if (_client?.IsConnected ?? false) { + try { _client.Close(); } // exits the event loop keeping us alive + catch (Exception) { /* ignore */ } + } }); + // register ctrl-c exit handler + Console.CancelKeyPress += (_, _) => { + _logger.LogInformation("Quitting due to keyboard interrupt."); + _hostApplicationLifetime.StopApplication(); + //Environment.Exit(0); + }; + return Task.CompletedTask; } @@ -391,14 +403,6 @@ public void OnInfoEvent(InfoEvent message) { autoReconnectSimConnect = (Settings.ConnectSimOnStartup.ValueAsInt() != 0); } - public void OnListChangedEvent(ListChangeEvent message) { - // not implemented yet - } - - public void OnBroadcastEvent(BroadcastEvent message) { - // not implemented yet - } - public void OnSettingsEvent(SettingsEvent message) { ProcessPluginSettings(message.Values); } @@ -434,16 +438,38 @@ public void OnActionEvent(ActionEvent message) { } public void OnClosedEvent(string message) { - _logger?.LogInformation("TouchPortal Disconnected."); + _logger?.LogInformation($"TouchPortal Disconnected with message: {message}"); - //Optional force exits this plugin. - Environment.Exit(0); + if (!_quitting) { + _hostApplicationLifetime.StopApplication(); + //Environment.Exit(0); + } + } + + public void OnListChangedEvent(ListChangeEvent message) { + // not implemented yet + } + + public void OnConnecterChangeEvent(ConnectorChangeEvent message) { + // not implemented yet + } + + public void OnShortConnectorIdNotificationEvent(ShortConnectorIdNotificationEvent message) { + // not implemented yet + } + + public void OnNotificationOptionClickedEvent(NotificationOptionClickedEvent message) { + // not implemented yet + } + + public void OnBroadcastEvent(BroadcastEvent message) { + // not implemented yet } public void OnUnhandledEvent(string jsonMessage) { - var jsonDocument = JsonSerializer.Deserialize(jsonMessage); - _logger?.LogWarning($"Unhandled message: {jsonDocument}"); + _logger?.LogDebug($"Unhandled message: {jsonMessage}"); } + #endregion } } diff --git a/MSFSTouchPortalPlugin/appsettings.json b/MSFSTouchPortalPlugin/appsettings.json index 009327b..0659fa5 100644 --- a/MSFSTouchPortalPlugin/appsettings.json +++ b/MSFSTouchPortalPlugin/appsettings.json @@ -21,7 +21,7 @@ "Default": "Warning", "Override": { "Microsoft": "Warning", - "TouchPortalSDK": "Warning", + "TouchPortalSDK": "Information", "MSFSTouchPortalPlugin": "Information" } } From a6b36bc2ae9cc6023cfa6028964c07b7c365bbb0 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 10:32:22 -0400 Subject: [PATCH 31/93] Prevent redundant disconnect attempts in SimConnectService, and catch SimConnect.Dispose() exceptions (if any?). --- .../Services/SimConnectService.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/MSFSTouchPortalPlugin/Services/SimConnectService.cs b/MSFSTouchPortalPlugin/Services/SimConnectService.cs index f6ab400..d48b5a3 100644 --- a/MSFSTouchPortalPlugin/Services/SimConnectService.cs +++ b/MSFSTouchPortalPlugin/Services/SimConnectService.cs @@ -83,16 +83,21 @@ public bool Connect() { } public void Disconnect() { - _logger.LogInformation("Disconnect SimConnect"); - - if (_simConnect != null) { - /// Dispose serves the same purpose as SimConnect_Close() - _simConnect.Dispose(); - _simConnect = null; - } + if (!_connected) + return; _connected = false; + // Dispose serves the same purpose as SimConnect_Close() + try { + _simConnect?.Dispose(); + _logger.LogInformation("SimConnect Disconnected"); + } + catch (Exception e) { + _logger.LogWarning(e, "Exception while trying to dispose SimConnect client."); + } + _simConnect = null; + // Invoke Handler OnDisconnect?.Invoke(); } From 6afd33fc9931a68d1baf4af3e80f50a865c7ba83 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 14:22:56 -0400 Subject: [PATCH 32/93] [build] Consolidate build output to top-level build folder; Add missing dependency on TouchPortalSDK to sub-projects; Switch TouchPortalExtension to NET5.0 framework. --- .gitignore | 1 + .../MSFSTouchPortalPlugin-Generator.csproj | 7 ++++++- .../Properties/launchSettings.json | 2 +- .../MSFSTouchPortalPlugin-Tests.csproj | 8 +++++--- MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj | 7 ++++--- TouchPortalExtension/TouchPortalExtension.csproj | 3 ++- 6 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 6149084..885528c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ bin/ obj/ dist/ +build/ packages-dist/ MSFSTouchPortalPlugin-Tests/TestResults/ .sonarqube/ diff --git a/MSFSTouchPortalPlugin-Generator/MSFSTouchPortalPlugin-Generator.csproj b/MSFSTouchPortalPlugin-Generator/MSFSTouchPortalPlugin-Generator.csproj index 7aa0a07..0a709ce 100644 --- a/MSFSTouchPortalPlugin-Generator/MSFSTouchPortalPlugin-Generator.csproj +++ b/MSFSTouchPortalPlugin-Generator/MSFSTouchPortalPlugin-Generator.csproj @@ -7,10 +7,11 @@ ..\.sonarlint\msfstouchportalplugincsharp.ruleset x64 1.1.0 + ..\build\$(Platform)-$(Configuration)\ - 1701;1702;S125;CS8032 + 1701;1702 @@ -24,6 +25,10 @@ + + ..\MSFSTouchPortalPlugin\lib\TouchPortalSDK\TouchPortalSDK.dll + true + diff --git a/MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json b/MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json index fa48df2..a2802ad 100644 --- a/MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json +++ b/MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "MSFSTouchPortalPlugin-Generator": { "commandName": "Project", - "commandLineArgs": "..\\..\\..\\..\\..\\" + "commandLineArgs": "..\\..\\..\\" } } } \ No newline at end of file diff --git a/MSFSTouchPortalPlugin-Tests/MSFSTouchPortalPlugin-Tests.csproj b/MSFSTouchPortalPlugin-Tests/MSFSTouchPortalPlugin-Tests.csproj index 8129a7b..54e268f 100644 --- a/MSFSTouchPortalPlugin-Tests/MSFSTouchPortalPlugin-Tests.csproj +++ b/MSFSTouchPortalPlugin-Tests/MSFSTouchPortalPlugin-Tests.csproj @@ -1,11 +1,9 @@ - + net5.0 MSFSTouchPortalPlugin_Tests - false - x64 @@ -31,6 +29,10 @@ + + ..\MSFSTouchPortalPlugin\lib\TouchPortalSDK\TouchPortalSDK.dll + true + diff --git a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj index 8b4f6e1..b724f96 100644 --- a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj +++ b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj @@ -9,6 +9,7 @@ 0.6.0.0 ..\.sonarlint\msfstouchportalplugincsharp.ruleset x64 + ..\build\$(Platform)-$(Configuration)\ @@ -25,7 +26,7 @@ none false - + @@ -62,7 +63,7 @@ - + .\lib\TouchPortalSDK\TouchPortalSDK.dll true false @@ -71,7 +72,7 @@ - + .\lib\SharpConfig\SharpConfig.dll true false diff --git a/TouchPortalExtension/TouchPortalExtension.csproj b/TouchPortalExtension/TouchPortalExtension.csproj index e7b2f93..b4dea9f 100644 --- a/TouchPortalExtension/TouchPortalExtension.csproj +++ b/TouchPortalExtension/TouchPortalExtension.csproj @@ -1,10 +1,11 @@ - netstandard2.0 + net5.0 ..\.sonarlint\msfstouchportalplugincsharp.ruleset x64 1.1.0 + ..\build\$(Platform)-$(Configuration)\ From 4bc689a0fbcbd9fa5f1e02f3b25f40ab1a67c663 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 15:22:14 -0400 Subject: [PATCH 33/93] Add VersionInfo helper and use it in connection info log, and in JSON generator; entry.tp version number incremented x100 to account for build number; Add version number to generated docs; Add new Generator config option for plugin folder name. --- .../Configuration/GeneratorOptions.cs | 1 + .../GenerateDoc.cs | 17 ++++--- .../GenerateEntry.cs | 37 +++++++------- MSFSTouchPortalPlugin-Generator/Model/Base.cs | 18 ++++++- .../Model/DocBase.cs | 1 + MSFSTouchPortalPlugin-Generator/Program.cs | 8 +-- .../Properties/launchSettings.json | 8 --- MSFSTouchPortalPlugin/Helpers/VersionInfo.cs | 50 +++++++++++++++++++ .../Services/PluginService.cs | 7 ++- 9 files changed, 106 insertions(+), 41 deletions(-) delete mode 100644 MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json create mode 100644 MSFSTouchPortalPlugin/Helpers/VersionInfo.cs diff --git a/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs b/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs index 38a86bb..6f08996 100644 --- a/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs +++ b/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs @@ -1,6 +1,7 @@ namespace MSFSTouchPortalPlugin_Generator.Configuration { internal class GeneratorOptions { public string PluginName { get; set; } + public string PluginFolder { get; set; } public string TargetPath { get; set; } } } diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index 51916b7..cbd57bc 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -2,6 +2,8 @@ using Microsoft.Extensions.Options; using MSFSTouchPortalPlugin.Configuration; using MSFSTouchPortalPlugin.Enums; +using MSFSTouchPortalPlugin.Helpers; +using MSFSTouchPortalPlugin.Types; using MSFSTouchPortalPlugin_Generator.Configuration; using MSFSTouchPortalPlugin_Generator.Interfaces; using MSFSTouchPortalPlugin_Generator.Model; @@ -12,7 +14,6 @@ using System.Reflection; using System.Text; using TouchPortalExtension.Attributes; -using MSFSTouchPortalPlugin.Types; namespace MSFSTouchPortalPlugin_Generator { @@ -44,14 +45,17 @@ private DocBase CreateModel() { throw new FileNotFoundException("Unable to load assembly for reflection."); } - var model = new DocBase { - Title = "MSFS 2020 TouchPortal Plugin", - Overview = "This plugin will provide a two way interface between Touch Portal and Microsoft Flight Simulator 2020 through SimConnect." - }; - var assembly = Assembly.Load(a); var assemblyList = assembly.GetTypes().ToList(); + VersionInfo.Assembly = assembly; + + var model = new DocBase { + Title = "MSFS 2020 Touch Portal Plugin", + Overview = "This plugin will provide a two-way interface between Touch Portal and Microsoft Flight Simulator 2020 through SimConnect.", + Version = VersionInfo.GetProductVersionString() + }; + // read default states config // TODO: Allow configuration of which state config file(s) to read. var pc = PluginConfig.Instance; @@ -182,6 +186,7 @@ private static string CreateMarkdown(DocBase model) { s.Append($"# {model.Title}\n\n"); s.Append($"{model.Overview}\n\n"); + s.Append($"Docuemntation generated for plugin version {model.Version}\n\n"); s.Append("---\n\n"); // Table of Contents diff --git a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs index 242eb62..1647b3e 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Options; using MSFSTouchPortalPlugin.Configuration; using MSFSTouchPortalPlugin.Enums; +using MSFSTouchPortalPlugin.Helpers; using MSFSTouchPortalPlugin.Types; using MSFSTouchPortalPlugin_Generator.Configuration; using MSFSTouchPortalPlugin_Generator.Interfaces; @@ -40,25 +41,7 @@ public void Generate() { } var assembly = Assembly.Load(a); - - var fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(assembly.Location); - var version = fvi.FileVersion; - - // Setup Base Model - var model = new Base { - Sdk = 3, - Version = int.Parse(version.Replace(".", "")), - Name = _options.Value.PluginName, - Id = _options.Value.PluginName - }; - - // Add Configuration - // Add Plugin Start Command - model.Plugin_start_cmd = Path.Combine("%TP_PLUGIN_FOLDER%", "MSFS-TouchPortal-Plugin\\dist", "MSFSTouchPortalPlugin.exe"); - // Load assembly - _ = MSFSTouchPortalPlugin.Objects.Plugin.Plugin.Init; - - var q = assembly.GetTypes().ToList(); + string basePath = $"%TP_PLUGIN_FOLDER%{_options.Value.PluginFolder}"; // read default states config // TODO: Allow configuration of which state config file(s) to read. @@ -69,6 +52,20 @@ public void Generate() { _logger.LogError(e, "Configuration reader error:"); } + // Load assembly + _ = MSFSTouchPortalPlugin.Objects.Plugin.Plugin.Init; + var q = assembly.GetTypes().ToList(); + + // Setup Base Model + VersionInfo.Assembly = assembly; + var model = new Base { + Sdk = 3, + Version = VersionInfo.GetProductVersionNumber(), + Name = _options.Value.PluginName, + Id = _options.Value.PluginName, + Plugin_start_cmd = $"{basePath}/dist/{_options.Value.PluginName}.exe" + }; + // Get all classes with the TouchPortalCategory var categoryClasses = q.Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalCategoryAttribute))).OrderBy(o => o.Name); @@ -88,7 +85,7 @@ public void Generate() { Id = $"{_options.Value.PluginName}.{att.Id}", Name = att.Name, // Imagepath = att.ImagePath - Imagepath = Path.Combine("%TP_PLUGIN_FOLDER%", "MSFS-TouchPortal-Plugin", "airplane_takeoff24.png") + Imagepath = basePath + "/airplane_takeoff24.png" }; model.Categories.Add(category); } diff --git a/MSFSTouchPortalPlugin-Generator/Model/Base.cs b/MSFSTouchPortalPlugin-Generator/Model/Base.cs index 47afe41..5b8e8e0 100644 --- a/MSFSTouchPortalPlugin-Generator/Model/Base.cs +++ b/MSFSTouchPortalPlugin-Generator/Model/Base.cs @@ -7,8 +7,9 @@ namespace MSFSTouchPortalPlugin_Generator.Model { class Base { [Required, Range(1, int.MaxValue)] public int Sdk { get; set; } = 3; + [JsonConverter(typeof(VersionNumberJsonConverter))] [Required, Range(1, int.MaxValue)] - public int Version { get; set; } + public uint Version { get; set; } [Required, MinLength(5)] public string Name { get; set; } = string.Empty; [Required, MinLength(5)] @@ -145,4 +146,19 @@ public void AddResult(ValidationResult validationResult) { _results.Add(validationResult); } } + + public sealed class VersionNumberJsonConverter : JsonConverter + { + public override bool CanConvert(System.Type objectType) { + return typeof(uint).Equals(objectType); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { + writer.WriteValue(uint.Parse($"{value:X}")); + } + + public override object ReadJson(JsonReader reader, System.Type objectType, object existingValue, JsonSerializer serializer) { + throw new System.NotImplementedException(); + } + } } diff --git a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs index 531c29b..266b5a2 100644 --- a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs +++ b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs @@ -4,6 +4,7 @@ namespace MSFSTouchPortalPlugin_Generator.Model { public class DocBase { public string Title { get; set; } public string Overview { get; set; } + public string Version { get; set; } public List Settings { get; set; } = new(); public List Categories { get; set; } = new(); } diff --git a/MSFSTouchPortalPlugin-Generator/Program.cs b/MSFSTouchPortalPlugin-Generator/Program.cs index 3cd4828..0a1cae7 100644 --- a/MSFSTouchPortalPlugin-Generator/Program.cs +++ b/MSFSTouchPortalPlugin-Generator/Program.cs @@ -14,11 +14,11 @@ await Host.CreateDefaultBuilder(args).ConfigureServices((context, services) => { .AddLogging() .Configure((opt) => { opt.PluginName = "MSFSTouchPortalPlugin"; - opt.TargetPath = "..\\..\\..\\..\\"; - - if (args.Length >= 1) { + opt.PluginFolder = "MSFS-TouchPortal-Plugin"; + if (args.Length >= 1) opt.TargetPath = args[0]; - } + else + opt.TargetPath = "..\\..\\..\\"; // assumes it is being run from the build output folder }) .AddHostedService() .AddSingleton() // Force load of assembly for generation diff --git a/MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json b/MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json deleted file mode 100644 index a2802ad..0000000 --- a/MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "MSFSTouchPortalPlugin-Generator": { - "commandName": "Project", - "commandLineArgs": "..\\..\\..\\" - } - } -} \ No newline at end of file diff --git a/MSFSTouchPortalPlugin/Helpers/VersionInfo.cs b/MSFSTouchPortalPlugin/Helpers/VersionInfo.cs new file mode 100644 index 0000000..e1bd9fa --- /dev/null +++ b/MSFSTouchPortalPlugin/Helpers/VersionInfo.cs @@ -0,0 +1,50 @@ + +namespace MSFSTouchPortalPlugin.Helpers +{ + /// + /// Helper class to get version information about C#/.Net assemblies (DLLs). + /// + internal static class VersionInfo + { + + /// + /// The .dll for version information lookup. Defaults to currently executing assembly. + /// + internal static System.Reflection.Assembly Assembly { get; set; } = System.Reflection.Assembly.GetExecutingAssembly(); + + /// + /// The .dll file path to use for version information lookup. Defaults to VersionInfo.Assembly.Location + /// + internal static string AssemblyLocation { get; set; } = Assembly.Location; + + /// + /// Short name of the DLL specified in Assembly property. + /// + internal static string AssemblyName { get; set; } = Assembly.GetName().Name; + + /// + /// Returns an integer which represents version number in hex notation, eg. 1.22.3.0 => 0x1220300 + /// + internal static uint GetProductVersionNumber() { + var vi = GetVersionInfo(); + return (uint)((byte)(vi.ProductMajorPart & 0xFF) << 24 | (byte)(vi.ProductMinorPart & 0xFF) << 16 | (byte)(vi.ProductBuildPart & 0xFF) << 8 | (byte)(vi.ProductPrivatePart & 0xFF)); + } + + /// + /// Returns a string representing the full version in dotted notation, eg. 1.22.3.0 + /// + internal static string GetProductVersionString() { + var vi = GetVersionInfo(); + return $"{vi.ProductMajorPart}.{vi.ProductMinorPart}.{vi.ProductBuildPart}.{vi.ProductPrivatePart}"; + } + + /// + /// Returns a System.Diagnostics.FileVersionInfo for a file at given location, or AssemblyLocation if location is default/null. + /// + /// + /// + internal static System.Diagnostics.FileVersionInfo GetVersionInfo(string location = default) + => System.Diagnostics.FileVersionInfo.GetVersionInfo(location ?? AssemblyLocation); + + } +} diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index f94a98f..8e11787 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -15,6 +15,7 @@ using TouchPortalSDK.Interfaces; using TouchPortalSDK.Messages.Events; using Timer = MSFSTouchPortalPlugin.Helpers.UnthreadedTimer; +using MSFSTouchPortalPlugin.Helpers; namespace MSFSTouchPortalPlugin.Services { @@ -396,9 +397,11 @@ private void UpdateSimConnectState() { #region TouchPortalSDK Events public void OnInfoEvent(InfoEvent message) { - _logger.LogInformation( - $"[Info] VersionCode: '{message.TpVersionCode}', VersionString: '{message.TpVersionString}', SDK: '{message.SdkVersion}', PluginVersion: '{message.PluginVersion}', Status: '{message.Status}'" + _logger?.LogInformation( + $"Touch Portal Connected with: TP v{message.TpVersionString}, SDK v{message.SdkVersion}, {PluginId} entry.tp v{message.PluginVersion}, " + + $"{VersionInfo.AssemblyName} running v{VersionInfo.GetProductVersionString()} ({VersionInfo.GetProductVersionNumber():X})" ); + ProcessPluginSettings(message.Settings); autoReconnectSimConnect = (Settings.ConnectSimOnStartup.ValueAsInt() != 0); } From 74fd6f28a260c346ef2629f91183c1333101ece3 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 15:23:30 -0400 Subject: [PATCH 34/93] Faster action data selector with new TP API. --- MSFSTouchPortalPlugin/Services/PluginService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 8e11787..5a20d80 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -262,7 +262,7 @@ private void ProcessEvent(ActionEvent actionEvent) { if (!actionsDictionary.TryGetValue(actionEvent.ActionId, out ActionEventType action)) return; - var dataArry = actionEvent.Data.Select(x => x.Value).ToArray(); + var dataArry = actionEvent.Data.Values.ToArray(); if (!action.TryGetEventMapping(in dataArry, out Enum eventId)) return; From 8c2af548e961f217d34c066dd2c89aaa1e70eabb Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 15:24:38 -0400 Subject: [PATCH 35/93] Fix typo in "Toggle Alternator" action description. --- MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs index 9dab9b0..fb413f0 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs @@ -30,7 +30,7 @@ internal static class ElectricalMapping { [TouchPortalActionMapping("TOGGLE_MASTER_BATTERY_ALTERNATOR")] public static readonly object MASTER_BATTERY_ALTERNATOR; - [TouchPortalAction("AlternatorIndex", "Alternator - Specific", "MSFS", "Toggle Specific Alternator", "Toggle Altenator - {0}")] + [TouchPortalAction("AlternatorIndex", "Alternator - Specific", "MSFS", "Toggle Specific Alternator", "Toggle Alternator - {0}")] [TouchPortalActionChoice(new[] { "1", "2", "3", "4" })] [TouchPortalActionMapping("TOGGLE_ALTERNATOR1", "1")] [TouchPortalActionMapping("TOGGLE_ALTERNATOR2", "2")] From 276aa4f07eb0b94b7518a70333a4eedbb5321ad1 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 18:17:59 -0400 Subject: [PATCH 36/93] Fix possible null exception in SimConnectService and prevent concurrent connection attempts. Fix reconnection delay in PluginService. --- .../Services/PluginService.cs | 3 +++ .../Services/SimConnectService.cs | 27 +++++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 5a20d80..a553152 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -250,6 +250,9 @@ private Task TryConnect() { --i; } } + else if (i != 0) { + i = 0; + } // SimConnect is typically available even before loading into a flight. This should connect and be ready by the time a flight is started. Thread.Sleep(1000); diff --git a/MSFSTouchPortalPlugin/Services/SimConnectService.cs b/MSFSTouchPortalPlugin/Services/SimConnectService.cs index d48b5a3..e9c79f0 100644 --- a/MSFSTouchPortalPlugin/Services/SimConnectService.cs +++ b/MSFSTouchPortalPlugin/Services/SimConnectService.cs @@ -31,6 +31,7 @@ internal class SimConnectService : ISimConnectService, IDisposable { SimConnect _simConnect; private bool _connected; + private bool _connecting; private readonly EventWaitHandle _scReady = new EventWaitHandle(false, EventResetMode.AutoReset); private readonly System.Collections.Generic.List _addedDefinitions = new(); @@ -46,7 +47,11 @@ public SimConnectService(ILogger logger, IReflectionService r public bool IsConnected() => (_connected && _simConnect != null); public bool Connect() { - _logger.LogInformation("Connect SimConnect"); + if (_connecting || _simConnect != null) + return _connected; + + _connecting = true; + _logger.LogInformation("Connecting to SimConnect..."); try { _simConnect = new SimConnect("Touch Portal Plugin", GetConsoleWindow(), WM_USER_SIMCONNECT, _scReady, 0); @@ -69,17 +74,18 @@ public bool Connect() { #if DEBUG_REQUESTS DbgSetupRequestTracking(); #endif - _simConnect.Text(SIMCONNECT_TEXT_TYPE.PRINT_BLACK, 5, Events.StartupMessage, "TouchPortal Connected"); + //_simConnect.Text(SIMCONNECT_TEXT_TYPE.PRINT_BLACK, 5, Events.StartupMessage, "TouchPortal Connected"); // not currently supported in MSFS SDK // Invoke Handler OnConnect?.Invoke(); - return true; } catch (COMException ex) { + _connected = false; _logger.LogInformation("Connection to Sim failed: {exception}", ex.Message); } - return false; + _connecting = false; + return _connected; } public void Disconnect() { @@ -91,21 +97,26 @@ public void Disconnect() { // Dispose serves the same purpose as SimConnect_Close() try { _simConnect?.Dispose(); + _simConnect = null; _logger.LogInformation("SimConnect Disconnected"); } catch (Exception e) { _logger.LogWarning(e, "Exception while trying to dispose SimConnect client."); } - _simConnect = null; - + _addedDefinitions.Clear(); // Invoke Handler OnDisconnect?.Invoke(); } public Task WaitForMessage(CancellationToken cancellationToken) { while (_connected && !cancellationToken.IsCancellationRequested) { - if (_scReady.WaitOne(5000)) { - _simConnect?.ReceiveMessage(); + try { + if (_scReady.WaitOne(5000)) + _simConnect?.ReceiveMessage(); + } + catch (Exception e) { + _logger.LogError(e, $"WaitForMessage() failed, disconnecting."); + Disconnect(); } } From ec705d55389dad21a68d8c177ce35ec33663d641 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 18:31:17 -0400 Subject: [PATCH 37/93] Reduce SimConnectService message wait time to 1s. --- MSFSTouchPortalPlugin/Services/SimConnectService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MSFSTouchPortalPlugin/Services/SimConnectService.cs b/MSFSTouchPortalPlugin/Services/SimConnectService.cs index e9c79f0..438a7d8 100644 --- a/MSFSTouchPortalPlugin/Services/SimConnectService.cs +++ b/MSFSTouchPortalPlugin/Services/SimConnectService.cs @@ -111,7 +111,7 @@ public void Disconnect() { public Task WaitForMessage(CancellationToken cancellationToken) { while (_connected && !cancellationToken.IsCancellationRequested) { try { - if (_scReady.WaitOne(5000)) + if (_scReady.WaitOne(1000)) _simConnect?.ReceiveMessage(); } catch (Exception e) { From 647c9374cd98b9fde9c850064234c6d537616a23 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 18:33:50 -0400 Subject: [PATCH 38/93] Switch to Async file logging; Reduce logging level for event tracking and adjust some SimConnectService log messages. --- .../MSFSTouchPortalPlugin.csproj | 1 + MSFSTouchPortalPlugin/Services/PluginService.cs | 4 ++-- .../Services/SimConnectService.cs | 8 ++++---- MSFSTouchPortalPlugin/appsettings.json | 17 ++++++++++++----- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj index b724f96..d3a3462 100644 --- a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj +++ b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj @@ -47,6 +47,7 @@ + diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index a553152..df84243 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -277,7 +277,7 @@ private void ProcessEvent(ActionEvent actionEvent) { private void ProcessInternalEvent(ActionEventType action, Enum eventId, in string[] dataArry) { Plugin pluginEventId = (Plugin)eventId; - _logger.LogInformation($"Firing Internal Event - action: {action.ActionId}; enum: {pluginEventId}; data: {string.Join(", ", dataArry)}"); + _logger.LogDebug($"Firing Internal Event - action: {action.ActionId}; enum: {pluginEventId}; data: {string.Join(", ", dataArry)}"); switch (pluginEventId) { case Plugin.ToggleConnection: @@ -347,7 +347,7 @@ private void ProcessSimEvent(ActionEventType action, Enum eventId, in string[] d _logger.LogWarning(e, $"Action {action.ActionId} for sim event {eventName} with data string '{valStr}' - Failed to convert data to numeric value."); } } - _logger.LogInformation($"Firing Sim Event - action: {action.ActionId}; group: {action.SimConnectGroup}; name: {eventName}; data {dataUint}"); + _logger.LogDebug($"Firing Sim Event - action: {action.ActionId}; group: {action.SimConnectGroup}; name: {eventName}; data {dataUint}"); _simConnectService.TransmitClientEvent(action.SimConnectGroup, eventId, dataUint); } diff --git a/MSFSTouchPortalPlugin/Services/SimConnectService.cs b/MSFSTouchPortalPlugin/Services/SimConnectService.cs index 438a7d8..6185094 100644 --- a/MSFSTouchPortalPlugin/Services/SimConnectService.cs +++ b/MSFSTouchPortalPlugin/Services/SimConnectService.cs @@ -231,7 +231,7 @@ public bool RequestDataOnSimObjectType(SimVarItem simVar) { } private void Simconnect_OnRecvQuit(SimConnect sender, SIMCONNECT_RECV data) { - _logger.LogInformation("Quit"); + _logger.LogInformation("Received shutdown command from SimConnect, disconnecting."); Disconnect(); } @@ -243,13 +243,13 @@ private void Simconnect_OnRecvSimobjectDataBytype(SimConnect sender, SIMCONNECT_ } private void Simconnect_OnRecvOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data) { - _logger.LogInformation("Opened"); + _logger.LogInformation("SimConnect Connected"); } private void Simconnect_OnRecvException(SimConnect sender, SIMCONNECT_RECV_EXCEPTION data) { SIMCONNECT_EXCEPTION eException = (SIMCONNECT_EXCEPTION)data.dwException; string request = DbgGetSendRecord(data.dwSendID); - _logger.LogInformation($"SimConnect_OnRecvException: {eException}; SendID: {data.dwSendID}; Index: {data.dwIndex}; Request: {request}"); + _logger.LogWarning($"SimConnect_OnRecvException: {eException}; SendID: {data.dwSendID}; Index: {data.dwIndex}; Request: {request}"); } /// @@ -264,7 +264,7 @@ private void Simconnect_OnRecvEvent(SimConnect sender, SIMCONNECT_RECV_EVENT dat grpName = ((Groups)data.uGroupID).ToString(); eventId = _reflectionService.GetSimEventNameById(data.uEventID); } - _logger.LogInformation($"Simconnect_OnRecvEvent Recieved: Group: {grpName}; Event: {eventId}"); + _logger.LogDebug($"Simconnect_OnRecvEvent Recieved: Group: {grpName}; Event: {eventId}"); } //private void Simconnect_OnRecvSimObjectData(SimConnect sender, SIMCONNECT_RECV_SIMOBJECT_DATA data) { diff --git a/MSFSTouchPortalPlugin/appsettings.json b/MSFSTouchPortalPlugin/appsettings.json index 0659fa5..8c390bc 100644 --- a/MSFSTouchPortalPlugin/appsettings.json +++ b/MSFSTouchPortalPlugin/appsettings.json @@ -3,11 +3,18 @@ "Using": [ "Serilog.Sinks.File", "Serilog.Sinks.Console" ], "WriteTo": [ { - "Name": "File", - "Args": { - "path": "logs/MSFSTouchPortalPlugin.log", - "rollingInterval": "Day", - "retainedFileCountLimit": 7 + "Name": "Async", + "Args": { + "configure": [ + { + "Name": "File", + "Args": { + "path": "logs/MSFSTouchPortalPlugin.log", + "rollingInterval": "Day", + "retainedFileCountLimit": 7 + } + } + ] } }, { From 61c7da79809b9610ea1529689d31ae144310f7fc Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 31 Mar 2022 19:01:42 -0400 Subject: [PATCH 39/93] Add plugin version number information states, for both running and entry.tp version numbers. Closes https://github.com/mpaperno/MSFSTouchPortalPlugin/issues/11 --- MSFSTouchPortalPlugin/Configuration/PluginStates.ini | 10 ++++++++++ MSFSTouchPortalPlugin/Services/PluginService.cs | 8 ++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/MSFSTouchPortalPlugin/Configuration/PluginStates.ini b/MSFSTouchPortalPlugin/Configuration/PluginStates.ini index 2232838..3ded99b 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginStates.ini +++ b/MSFSTouchPortalPlugin/Configuration/PluginStates.ini @@ -12,3 +12,13 @@ DefaultValue = "450" CategoryId = Plugin Name = "The status of SimConnect (true/false/connecting)" DefaultValue = "false" + +[RunningVersion] +CategoryId = Plugin +Name = "The running plugin version number." +DefaultValue = "0" + +[EntryVersion] +CategoryId = Plugin +Name = "The loaded entry.tp plugin version number." +DefaultValue = "0" diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index df84243..305bcfb 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -394,19 +394,23 @@ private void UpdateSimConnectState() { string stat = "true"; if (!_simConnectService.IsConnected()) stat = autoReconnectSimConnect ? "connecting" : "false"; - _client.StateUpdate("MSFSTouchPortalPlugin.Plugin.State.Connected", stat); + _client.StateUpdate(PluginId + ".Plugin.State.Connected", stat); } #region TouchPortalSDK Events public void OnInfoEvent(InfoEvent message) { + var runtimeVer = string.Format("{0:X}", VersionInfo.GetProductVersionNumber()); _logger?.LogInformation( $"Touch Portal Connected with: TP v{message.TpVersionString}, SDK v{message.SdkVersion}, {PluginId} entry.tp v{message.PluginVersion}, " + - $"{VersionInfo.AssemblyName} running v{VersionInfo.GetProductVersionString()} ({VersionInfo.GetProductVersionNumber():X})" + $"{VersionInfo.AssemblyName} running v{VersionInfo.GetProductVersionString()} ({runtimeVer})" ); ProcessPluginSettings(message.Settings); autoReconnectSimConnect = (Settings.ConnectSimOnStartup.ValueAsInt() != 0); + + _client.StateUpdate(PluginId + ".Plugin.State.RunningVersion", runtimeVer); + _client.StateUpdate(PluginId + ".Plugin.State.EntryVersion", $"{message.PluginVersion}"); } public void OnSettingsEvent(SettingsEvent message) { From 4d460c64d85ac531b13ad4a75e8579627e04ae6c Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Fri, 1 Apr 2022 01:26:02 -0400 Subject: [PATCH 40/93] Switch to using SimConnect RequestDataOnSimObject instead of ByType (push instead of pull/poll). This lets SimConnect handle the value change tracking and update intervals for all states except any SimVarItems configured with custom millisecond timer. Significant performance increase across the board and much faster value updates. Custom millisecond-accuracy request periods are also supported using old polling method, with SimVarItem tracking when updates are needed and request timeouts internally. --- .../Configuration/PluginConfig.cs | 9 +- .../Configuration/States.ini | 7 +- .../Enums/{UpdateFreq.cs => UpdatePeriod.cs} | 4 +- .../Services/PluginService.cs | 55 ++++----- .../Services/SimConnectService.cs | 18 +-- MSFSTouchPortalPlugin/Types/SimVarItem.cs | 105 +++++++++++------- 6 files changed, 108 insertions(+), 90 deletions(-) rename MSFSTouchPortalPlugin/Enums/{UpdateFreq.cs => UpdatePeriod.cs} (69%) diff --git a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs index d14018a..e8e8bd4 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs +++ b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs @@ -114,11 +114,10 @@ public bool SaveSimVarItems(IReadOnlyCollection items, bool isUserCo sect.Add("StringFormat", item.FormattingString); if (item.CanSet) sect.Add("CanSet", item.CanSet); - if (item.UpdateFreqency != UpdateFreq.Default) { - sect.Add("UpdateFreqency", item.UpdateFreqency); - if (item.UpdateFreqency == UpdateFreq.Milliseconds) - sect.Add("UpdateInterval", item.UpdateInterval); - } + if (item.UpdatePeriod != UpdatePeriod.Default) + sect.Add("UpdateFreqency", item.UpdatePeriod); + if (item.UpdateInterval != 0) + sect.Add("UpdateInterval", item.UpdateInterval); if (item.DeltaEpsilon != 0.0f) sect.Add("DeltaEpsilon", item.DeltaEpsilon); } diff --git a/MSFSTouchPortalPlugin/Configuration/States.ini b/MSFSTouchPortalPlugin/Configuration/States.ini index eaadd58..e7ad75e 100644 --- a/MSFSTouchPortalPlugin/Configuration/States.ini +++ b/MSFSTouchPortalPlugin/Configuration/States.ini @@ -25,9 +25,10 @@ # ; Formatting string references: # ; https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings # ; https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings -# UpdateFreqency = Milliseconds ; Determines how often the SimConnect value is checked/updated. Optional, default is SimFrame. -# ; Must be one of the following: Never, Once, SimFrame, VisualFrame, Second, Milliseconds, Default (same as SimFrame) -# UpdateInterval = 100 ; The update frequency (in ms) to use when UpdateFreqency is set to Milliseconds. Required when UpdateFreqency = Milliseconds, ignored otherwise. +# UpdatePeriod = Millisecond ; Determines how often the SimConnect value is checked/updated. Optional, default is SimFrame. +# ; Must be one of the following: Never, Once, SimFrame, VisualFrame, Second, Millisecond, Default (same as SimFrame) +# UpdateInterval = 100 ; The number of UpdatePeriod events that should elapse between data updates. Optional, default is 0, which means the data is transmitted every UpdatePeriod. +# ; Note that when UpdatePeriod = Millisecond, there is an effective minimum of ~25ms. # DeltaEpsilon = 1.0 ; Only report change if it is greater than the value of this parameter. Optional, default is any change (0). # ########################################## diff --git a/MSFSTouchPortalPlugin/Enums/UpdateFreq.cs b/MSFSTouchPortalPlugin/Enums/UpdatePeriod.cs similarity index 69% rename from MSFSTouchPortalPlugin/Enums/UpdateFreq.cs rename to MSFSTouchPortalPlugin/Enums/UpdatePeriod.cs index 53904ef..1468f61 100644 --- a/MSFSTouchPortalPlugin/Enums/UpdateFreq.cs +++ b/MSFSTouchPortalPlugin/Enums/UpdatePeriod.cs @@ -1,13 +1,13 @@ namespace MSFSTouchPortalPlugin.Enums { - public enum UpdateFreq // SIMCONNECT_PERIOD + public enum UpdatePeriod // SIMCONNECT_PERIOD { Never, Once, VisualFrame, SimFrame, Second, - Milliseconds, // custom frequency + Millisecond, // custom frequency Default = SimFrame // default value (do not add anything below here) } diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 305bcfb..abdf748 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -1,6 +1,8 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using MSFSTouchPortalPlugin.Configuration; +using MSFSTouchPortalPlugin.Enums; +using MSFSTouchPortalPlugin.Helpers; using MSFSTouchPortalPlugin.Interfaces; using MSFSTouchPortalPlugin.Objects.Plugin; using MSFSTouchPortalPlugin.Types; @@ -15,7 +17,6 @@ using TouchPortalSDK.Interfaces; using TouchPortalSDK.Messages.Events; using Timer = MSFSTouchPortalPlugin.Helpers.UnthreadedTimer; -using MSFSTouchPortalPlugin.Helpers; namespace MSFSTouchPortalPlugin.Services { @@ -38,6 +39,7 @@ internal class PluginService : IPluginService, IDisposable, ITouchPortalEventHan private Dictionary actionsDictionary = new(); private Dictionary statesDictionary = new(); private Dictionary pluginSettingsDictionary = new(); + private readonly ConcurrentBag customIntervalStates = new(); private readonly ConcurrentDictionary repeatingActionTimers = new(); @@ -76,19 +78,12 @@ await Task.WhenAll(new Task[] { /// /// The Cancellation Token private async Task RunTimedEventsTask(CancellationToken cancellationToken) { - // Run Data Polling - var dataPollTimer = new Timer(250); - dataPollTimer.Elapsed += delegate { CheckPendingRequests(); }; - dataPollTimer.Start(); - while (_simConnectService.IsConnected() && !cancellationToken.IsCancellationRequested) { - dataPollTimer.Tick(); foreach (Timer tim in repeatingActionTimers.Values) tim.Tick(); + CheckPendingRequests(); await Task.Delay(25, cancellationToken); } - - dataPollTimer.Dispose(); } /// @@ -113,7 +108,7 @@ public Task StartAsync(CancellationToken cancellationToken) { autoReconnectSimConnect = false; _simConnectService.Disconnect(); if (_client?.IsConnected ?? false) { - try { _client.Close(); } // exits the event loop keeping us alive + try { _client.Close(); } catch (Exception) { /* ignore */ } } }); @@ -203,12 +198,10 @@ private void SimConnectEvent_OnDataUpdateEvent(Definition def, Definition req, o return; // Update SimVarItem value and TP state on changes. - // TODO: sim vars on a regular request interval will only be sent when changed, so skip the equality check. - // SimVarItem.SetValue() takes care of setting the correct value type and also any unit conversions as needed. Returns false if conversion failed. - if (!simVar.ValueEquals(data) && simVar.SetValue(data)) + // Sim vars sent on a standard SimConnect request period will only be sent when changed by > simVar.DeltaEpsilon anyway, so skip the equality check. + // SimVarItem.SetValue() takes care of setting the correct value type, any unit conversions needed, and resets any expiry timers. Returns false if value is of the wrong type. + if ((!simVar.NeedsScheduledRequest || !simVar.ValueEquals(data)) && simVar.SetValue(data)) _client.StateUpdate(simVar.TouchPortalStateId, simVar.FormattedValue); - // clear pending flag last - simVar.SetPending(false); } private void SimConnectEvent_OnDisconnect() { @@ -232,10 +225,14 @@ private void SetupSimVars() { _logger.LogWarning(e, "Configuration reader error:"); } statesDictionary = configStates.ToDictionary(s => s.Def, s => s); + customIntervalStates.Clear(); // Register SimVars _simConnectService.ClearAllDataDefinitions(); - foreach (var s in statesDictionary) - _simConnectService.RegisterToSimConnect(s.Value); + foreach (var simVar in statesDictionary.Values) { + if (simVar.NeedsScheduledRequest) + customIntervalStates.Add(simVar.Def); + _simConnectService.RegisterToSimConnect(simVar); + } } private Task TryConnect() { @@ -261,6 +258,16 @@ private Task TryConnect() { return Task.CompletedTask; } + private void CheckPendingRequests() { + foreach (var def in customIntervalStates) { + // Check if a value update is required based on the SimVar's internal tracking mechanism. + if (statesDictionary.TryGetValue(def, out var s) && s.UpdateRequired) { + s.SetPending(true); + _simConnectService.RequestDataOnSimObjectType(s); + } + } + } + private void ProcessEvent(ActionEvent actionEvent) { if (!actionsDictionary.TryGetValue(actionEvent.ActionId, out ActionEventType action)) return; @@ -351,20 +358,6 @@ private void ProcessSimEvent(ActionEventType action, Enum eventId, in string[] d _simConnectService.TransmitClientEvent(action.SimConnectGroup, eventId, dataUint); } - private void CheckPendingRequests() { - foreach (var s in statesDictionary.Values) { - // Expire pending if more than 30 seconds - if (s.PendingTimeout()) - _logger.LogDebug($"Request for SimVar '{s.SimVarName}' timed out!"); - - // Check if Pending data request in play - if (!s.PendingRequest) { - s.SetPending(true); - _simConnectService.RequestDataOnSimObjectType(s); - } - } - } - private void ClearRepeatingActions() { foreach (var act in repeatingActionTimers) { if (repeatingActionTimers.TryRemove(act.Key, out var tim)) { diff --git a/MSFSTouchPortalPlugin/Services/SimConnectService.cs b/MSFSTouchPortalPlugin/Services/SimConnectService.cs index 6185094..39e5192 100644 --- a/MSFSTouchPortalPlugin/Services/SimConnectService.cs +++ b/MSFSTouchPortalPlugin/Services/SimConnectService.cs @@ -69,7 +69,7 @@ public bool Connect() { // Sim Data _simConnect.OnRecvSimobjectDataBytype += new SimConnect.RecvSimobjectDataBytypeEventHandler(Simconnect_OnRecvSimobjectDataBytype); - //_simConnect.OnRecvSimobjectData += new SimConnect.RecvSimobjectDataEventHandler(Simconnect_OnRecvSimObjectData); // unused for now + _simConnect.OnRecvSimobjectData += new SimConnect.RecvSimobjectDataEventHandler(Simconnect_OnRecvSimObjectData); #if DEBUG_REQUESTS DbgSetupRequestTracking(); @@ -199,6 +199,11 @@ public bool RegisterToSimConnect(SimVarItem simVar) { } DbgAddSendRecord($"RegisterDataDefineStruct<{simVar.StorageDataType}>({simVar.ToDebugString()})"); + if (!simVar.NeedsScheduledRequest) { + _simConnect.RequestDataOnSimObject(simVar.Def, simVar.Def, (uint)SIMCONNECT_SIMOBJECT_TYPE.USER, (SIMCONNECT_PERIOD)simVar.UpdatePeriod, SIMCONNECT_DATA_REQUEST_FLAG.CHANGED, 0, simVar.UpdateInterval, 0); + DbgAddSendRecord($"RequestDataOnSimObject({simVar.ToDebugString()})"); + } + _addedDefinitions.Add(simVar.Def); return true; } @@ -236,10 +241,13 @@ private void Simconnect_OnRecvQuit(SimConnect sender, SIMCONNECT_RECV data) { } private void Simconnect_OnRecvSimobjectDataBytype(SimConnect sender, SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE data) { - if (data.dwData.Length > 0) { + if (data.dwData.Length > 0) OnDataUpdateEvent?.Invoke((Definition)data.dwDefineID, (Definition)data.dwRequestID, data.dwData[0]); - } + } + private void Simconnect_OnRecvSimObjectData(SimConnect sender, SIMCONNECT_RECV_SIMOBJECT_DATA data) { + if (data.dwData.Length > 0) + OnDataUpdateEvent?.Invoke((Definition)data.dwDefineID, (Definition)data.dwRequestID, data.dwData[0]); } private void Simconnect_OnRecvOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data) { @@ -267,10 +275,6 @@ private void Simconnect_OnRecvEvent(SimConnect sender, SIMCONNECT_RECV_EVENT dat _logger.LogDebug($"Simconnect_OnRecvEvent Recieved: Group: {grpName}; Event: {eventId}"); } - //private void Simconnect_OnRecvSimObjectData(SimConnect sender, SIMCONNECT_RECV_SIMOBJECT_DATA data) { - // // Empty method for now, not implemented - //} - #region IDisposable Support private bool disposedValue; // To detect redundant calls diff --git a/MSFSTouchPortalPlugin/Types/SimVarItem.cs b/MSFSTouchPortalPlugin/Types/SimVarItem.cs index 5df1feb..e7356c2 100644 --- a/MSFSTouchPortalPlugin/Types/SimVarItem.cs +++ b/MSFSTouchPortalPlugin/Types/SimVarItem.cs @@ -30,9 +30,10 @@ public class SimVarItem /// SimConnect settable value (future use) public bool CanSet { get; set; } = false; /// How often updates are sent by SimConnect if value changes (SIMCONNECT_PERIOD). Default is equivalent to SIMCONNECT_PERIOD_SIM_FRAME. - public UpdateFreq UpdateFreqency { get; set; } = UpdateFreq.Default; - /// Update frequency in ms when UpdateFrequency is set to UpdateFreq.Milliseconds - public uint UpdateInterval { get; set; } + public UpdatePeriod UpdatePeriod { get; set; } = UpdatePeriod.Default; + /// The number of UpdatePeriod events that should elapse between data updates. Default is 0, which means the data is transmitted every Period. + /// Note that when UpdatePeriod = Millisecond, there is an effective minimum of ~25ms. + public uint UpdateInterval { get; set; } = 0; /// Only report change if it is greater than the value of this parameter (not greater than or equal to). Default is any change. public float DeltaEpsilon { get; set; } = 0.0f; /// Could also be "choice" but we don't use that (yet?) @@ -83,7 +84,13 @@ public object Value get => _value; set { _value = value; - _valInit = true; + _lastUpdate = Stopwatch.GetTimestamp(); + SetPending(false); + _valueExpires = UpdatePeriod switch { + UpdatePeriod.Millisecond => _lastUpdate + UpdateInterval * (Stopwatch.Frequency / 1000L), + //UpdatePeriod.Second => _lastUpdate + UpdateInterval * Stopwatch.Frequency, + _ => 0, // never? or always? + }; } } @@ -94,7 +101,7 @@ public object Value public string FormattedValue { get { - if (!_valInit) + if (!ValInit) return DefaultValue; return Value switch { double v => string.Format(StringFormat, v), @@ -116,16 +123,16 @@ public System.Type StorageDataType private set { if (Value == null || Value.GetType() != value) { _value = value == typeof(StringVal) ? new StringVal() : System.Activator.CreateInstance(value); - _valInit = false; + _lastUpdate = 0; } } } /// Returns true if this value is of a real (double) type, false otherwise public bool IsRealType { get; private set; } - /// Returns true if this value is of a string type, false if numeric. + /// Returns true if this value is of a string type, false if numeric or bool. public bool IsStringType { get; private set; } - /// Returns true if this value is of a integer type, false if string or real. + /// Returns true if this value is of a integer type, false if string, real or bool. public bool IsIntegralType { get; private set; } /// Returns true if this value is of a boolean type, false otherwise public bool IsBooleanType { get; private set; } @@ -134,31 +141,46 @@ private set { public Definition Def { get; private set; } /// The SimConnect data type for registering this var. public SIMCONNECT_DATATYPE SimConnectDataType { get; private set; } - /// Indicates if a SimConnect request for this variable is already pending. - public bool PendingRequest { get; private set; } - /// For serializing the raw value + /// Indicates that this state needs a scheduled update request (UpdatePeriod == Millisecond). + public bool NeedsScheduledRequest => UpdatePeriod == UpdatePeriod.Millisecond; + /// For serializing the "raw" formatting string w/out "{0}" parts public string FormattingString => _formatString; - private object _value; - private bool _valInit; - private string _unit; - private string _formatString; - private long _timeoutTicks; + /// + /// Indicates that the value has "expired" based on the UpdatePeriod and UpdateInterval since the last time the value was set. + /// This always returns false if UpdatePeriod != UpdatePeriod.Millisecond. Also returns false if a request for this value is pending and hasn't yet timed out. + /// + public bool UpdateRequired => _valueExpires > 0 && !CheckPending() && Stopwatch.GetTimestamp() > _valueExpires; + + private object _value; // the actual Value storage + private string _unit; // unit type storage + private string _formatString; // the "raw" formatting string w/out "{0}" part + private long _lastUpdate = 0; // value update timestamp in Stopwatch ticks + private long _valueExpires; // value expiry timestamp in Stopwatch ticks, if a timed UpdatePeriod type, zero otherwise + private long _requestTimeout; // for tracking last data request time to avoid race conditions, next pending timeout ticks count or zero if not pending + private const short REQ_TIMEOUT_SEC = 30; // pending value timeout period in seconds + + private bool ValInit => _lastUpdate > 0; // has value been set at least once + // this is how we generate unique Def IDs for every instance of SimVarItem. Assigned in c'tor. private static Definition _nextDefinionId = Definition.None; - private static Definition NextId() => ++_nextDefinionId; + private static Definition NextId() => ++_nextDefinionId; // got a warning when trying to increment this directly from c'tor, but not via static member... ? public SimVarItem() { Def = NextId(); } - public bool ValueEquals(string value) => _valInit && IsStringType && value == Value.ToString(); - public bool ValueEquals(double value) => _valInit && IsRealType && System.Math.Abs((double)Value - ConvertValueIfNeeded(value)) <= DeltaEpsilon; - public bool ValueEquals(long value) => _valInit && IsIntegralType && System.Math.Abs((long)Value - value) <= (long)DeltaEpsilon; - public bool ValueEquals(uint value) => _valInit && IsBooleanType && System.Math.Abs((uint)Value - value) <= (uint)DeltaEpsilon; + public bool ValueEquals(string value) => ValInit && IsStringType && value == Value.ToString(); + public bool ValueEquals(double value) => ValInit && IsRealType && System.Math.Abs((double)Value - ConvertValueIfNeeded(value)) <= DeltaEpsilon; + public bool ValueEquals(long value) => ValInit && IsIntegralType && System.Math.Abs((long)Value - value) <= (long)DeltaEpsilon; + public bool ValueEquals(uint value) => ValInit && IsBooleanType && System.Math.Abs((uint)Value - value) <= (uint)DeltaEpsilon; + /// + /// Compare this instance's value to the given object's value. + /// Uses strict type matching for double, long, uint, and falls back to string compare for all other types. + /// public bool ValueEquals(object value) { - if (!_valInit) + if (!ValInit) return false; try { return value switch { @@ -197,6 +219,11 @@ internal bool SetValue(uint value) { return IsBooleanType; } + /// + /// Prefer using this method, or one of the type-specific SetValue() overloads to + /// to set the Value property, vs. direct access. Returns false if the given object's + /// value type doesn't match this type. + /// internal bool SetValue(object value) { try { return value switch { @@ -212,6 +239,14 @@ internal bool SetValue(object value) { } } + /// + /// Updates the object to either set pending update or no longer pending + /// + /// True/False + public void SetPending(bool val) { + _requestTimeout = val ? Stopwatch.GetTimestamp() + REQ_TIMEOUT_SEC * Stopwatch.Frequency : 0; + } + private double ConvertValueIfNeeded(double value) { // Convert to Degrees if (Unit == Units.radians) @@ -223,28 +258,14 @@ private double ConvertValueIfNeeded(double value) { return value; } - /// - /// Updates the object to either set pending update or no longer pending - /// - /// True/False - public void SetPending(bool val) { - PendingRequest = val; - - if (val) { - _timeoutTicks = Stopwatch.GetTimestamp() + 30 * Stopwatch.Frequency; - } - } - - /// - /// If pending for more than 30 seconds, timeout - /// - /// true if a timeout occurred, false otherwise. - public bool PendingTimeout() { - if (PendingRequest && Stopwatch.GetTimestamp() > _timeoutTicks) { + private bool CheckPending() { + if (_requestTimeout == 0) + return false; + if (Stopwatch.GetTimestamp() > _requestTimeout) { SetPending(false); - return true; + return false; } - return false; + return true; } public string ToDebugString() { From e27baddf274986adf963fb78e829b5ef47831490 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Fri, 1 Apr 2022 01:28:42 -0400 Subject: [PATCH 41/93] Make UnthreadedTimer more precise (though a little slower) by using Stopwatch. --- .../Helpers/UnthreadedTimer.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/MSFSTouchPortalPlugin/Helpers/UnthreadedTimer.cs b/MSFSTouchPortalPlugin/Helpers/UnthreadedTimer.cs index 53c1683..909897c 100644 --- a/MSFSTouchPortalPlugin/Helpers/UnthreadedTimer.cs +++ b/MSFSTouchPortalPlugin/Helpers/UnthreadedTimer.cs @@ -1,4 +1,5 @@ using System; +using Stopwatch = System.Diagnostics.Stopwatch; namespace MSFSTouchPortalPlugin.Helpers { @@ -16,17 +17,26 @@ public class UnthreadedTimer : IDisposable public int Interval { get { return interval; } - set { interval = Math.Max(value, 1); UpdateNextTick(); } + set { + interval = Math.Max(value, 1); + tickInterval = interval * (int)(Stopwatch.Frequency / 1000L); + UpdateNextTick(); + } } public bool Enabled { get { return enabled; } - set { enabled = value; UpdateNextTick(); } + set { + enabled = value; + if (enabled) + UpdateNextTick(); + } } private bool enabled = false; private int interval = 1000; - private int nextTick = 0; + private long tickInterval; + private long nextTick = 0; public UnthreadedTimer() { } public UnthreadedTimer(int interval) { @@ -34,7 +44,7 @@ public UnthreadedTimer(int interval) { } public void Tick() { - if (enabled && Environment.TickCount >= nextTick) { + if (enabled && Stopwatch.GetTimestamp() >= nextTick) { enabled = false; if (Elapsed != null) Elapsed.Invoke(this, EventArgs.Empty); @@ -52,7 +62,7 @@ public void Stop() { } private void UpdateNextTick() { - nextTick = Environment.TickCount + interval; + nextTick = Stopwatch.GetTimestamp() + tickInterval; } public void Dispose() { From 1fcf06f55c897609359c0b0f5fff6c416caa6e40 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Fri, 1 Apr 2022 02:38:44 -0400 Subject: [PATCH 42/93] Limit SimVar value change reporting to 2 decimal places for most real units (except MHz to 3 places) using SimVarItem.DeltaEpsilon setting. --- MSFSTouchPortalPlugin/Configuration/States.ini | 16 +++++++++++++++- MSFSTouchPortalPlugin/Types/SimVarItem.cs | 7 ++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/MSFSTouchPortalPlugin/Configuration/States.ini b/MSFSTouchPortalPlugin/Configuration/States.ini index e7ad75e..430f2a3 100644 --- a/MSFSTouchPortalPlugin/Configuration/States.ini +++ b/MSFSTouchPortalPlugin/Configuration/States.ini @@ -29,7 +29,8 @@ # ; Must be one of the following: Never, Once, SimFrame, VisualFrame, Second, Millisecond, Default (same as SimFrame) # UpdateInterval = 100 ; The number of UpdatePeriod events that should elapse between data updates. Optional, default is 0, which means the data is transmitted every UpdatePeriod. # ; Note that when UpdatePeriod = Millisecond, there is an effective minimum of ~25ms. -# DeltaEpsilon = 1.0 ; Only report change if it is greater than the value of this parameter. Optional, default is any change (0). +# DeltaEpsilon = 0.0009 ; Only report change if it is greater than the value of this parameter. Optional, default is 0.009 which will only report changes greater than within 2 decimal places. +# ; For example for unit "percent over 100", to detect fractional changes the epsilon value needs to be 0.0009 or smaller. # ########################################## @@ -208,6 +209,7 @@ Name = "The frequency of the active COM1 radio" SimVarName = "COM ACTIVE FREQUENCY:1" Unit = "MHz" StringFormat = "0.000#" +DeltaEpsilon = 0.0009999 [Com1StandbyFrequency] CategoryId = Communication @@ -215,6 +217,7 @@ Name = "The frequency of the standby COM1 radio" SimVarName = "COM STANDBY FREQUENCY:1" Unit = "MHz" StringFormat = "0.000#" +DeltaEpsilon = 0.0009999 [Com2ActiveFrequency] CategoryId = Communication @@ -222,6 +225,7 @@ Name = "The frequency of the active COM2 radio" SimVarName = "COM ACTIVE FREQUENCY:2" Unit = "MHz" StringFormat = "0.000#" +DeltaEpsilon = 0.0009999 [Com2StandbyFrequency] CategoryId = Communication @@ -229,6 +233,7 @@ Name = "The frequency of the standby COM2 radio" SimVarName = "COM STANDBY FREQUENCY:2" Unit = "MHz" StringFormat = "0.000#" +DeltaEpsilon = 0.0009999 [Nav1ActiveFrequency] CategoryId = Communication @@ -236,6 +241,7 @@ Name = "The frequency of the active NAV1 radio" SimVarName = "NAV ACTIVE FREQUENCY:1" Unit = "MHz" StringFormat = "0.000#" +DeltaEpsilon = 0.0009999 [Nav1StandbyFrequency] CategoryId = Communication @@ -243,6 +249,7 @@ Name = "The frequency of the standby NAV1 radio" SimVarName = "NAV STANDBY FREQUENCY:1" Unit = "MHz" StringFormat = "0.000#" +DeltaEpsilon = 0.0009999 [Nav2ActiveFrequency] CategoryId = Communication @@ -250,6 +257,7 @@ Name = "The frequency of the active NAV2 radio" SimVarName = "NAV ACTIVE FREQUENCY:2" Unit = "MHz" StringFormat = "0.000#" +DeltaEpsilon = 0.0009999 [Nav2StandbyFrequency] CategoryId = Communication @@ -257,6 +265,7 @@ Name = "The frequency of the standby NAV2 radio" SimVarName = "NAV STANDBY FREQUENCY:2" Unit = "MHz" StringFormat = "0.000#" +DeltaEpsilon = 0.0009999 # Category: Electrical ############################## @@ -857,6 +866,7 @@ SimVarName = "SPOILERS LEFT POSITION" Unit = "percent over 100" DefaultValue = "0" StringFormat = "F1" +DeltaEpsilon = 0.0000999 [SpoilersRightPosition] CategoryId = FlightSystems @@ -865,6 +875,7 @@ SimVarName = "SPOILERS RIGHT POSITION" Unit = "percent over 100" DefaultValue = "0" StringFormat = "F1" +DeltaEpsilon = 0.0000999 [SpoilersArmed] CategoryId = FlightSystems @@ -902,6 +913,7 @@ SimVarName = "AILERON TRIM PCT" Unit = "percent over 100" DefaultValue = "0" StringFormat = "F1" +DeltaEpsilon = 0.0000999 CanSet = True [ElevatorTrimPct] @@ -911,6 +923,7 @@ SimVarName = "ELEVATOR TRIM PCT" Unit = "percent over 100" DefaultValue = "0" StringFormat = "F1" +DeltaEpsilon = 0.0000999 [RudderTrimPct] CategoryId = FlightSystems @@ -919,6 +932,7 @@ SimVarName = "RUDDER TRIM PCT" Unit = "percent over 100" DefaultValue = "0" StringFormat = "F1" +DeltaEpsilon = 0.0000999 CanSet = True diff --git a/MSFSTouchPortalPlugin/Types/SimVarItem.cs b/MSFSTouchPortalPlugin/Types/SimVarItem.cs index e7356c2..49a41bc 100644 --- a/MSFSTouchPortalPlugin/Types/SimVarItem.cs +++ b/MSFSTouchPortalPlugin/Types/SimVarItem.cs @@ -34,8 +34,9 @@ public class SimVarItem /// The number of UpdatePeriod events that should elapse between data updates. Default is 0, which means the data is transmitted every Period. /// Note that when UpdatePeriod = Millisecond, there is an effective minimum of ~25ms. public uint UpdateInterval { get; set; } = 0; - /// Only report change if it is greater than the value of this parameter (not greater than or equal to). Default is any change. - public float DeltaEpsilon { get; set; } = 0.0f; + /// Only report change if it is greater than the value of this parameter (not greater than or equal to). + /// Default is 0.0099999f limits changes to 2 decimal places which is suitable for most unit types (except perhaps MHz and "percent over 100"). + public float DeltaEpsilon { get; set; } = 0.0099999f; /// Could also be "choice" but we don't use that (yet?) public string TouchPortalValueType { get; set; } = "text"; /// This could/should be populated by whatever is creating the SimVarItem instance @@ -176,7 +177,7 @@ public SimVarItem() { public bool ValueEquals(uint value) => ValInit && IsBooleanType && System.Math.Abs((uint)Value - value) <= (uint)DeltaEpsilon; /// - /// Compare this instance's value to the given object's value. + /// Compare this instance's value to the given object's value. For numeric types, it takes the DeltaEpsilon property into account. /// Uses strict type matching for double, long, uint, and falls back to string compare for all other types. /// public bool ValueEquals(object value) { From 5ec8f4d29a8013d058a98a2e6030e744cbed7489 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Fri, 1 Apr 2022 02:40:46 -0400 Subject: [PATCH 43/93] Fix formatting on "Total percentage of gear extended" state (which BTW was also being shown as string value before latest changes). Also adjust trim angle degrees formatting to 2 fixed decimal places. --- MSFSTouchPortalPlugin/Configuration/States.ini | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/MSFSTouchPortalPlugin/Configuration/States.ini b/MSFSTouchPortalPlugin/Configuration/States.ini index 430f2a3..b07f91e 100644 --- a/MSFSTouchPortalPlugin/Configuration/States.ini +++ b/MSFSTouchPortalPlugin/Configuration/States.ini @@ -842,7 +842,8 @@ CanSet = True CategoryId = FlightSystems Name = "Total percentage of gear extended" SimVarName = "GEAR TOTAL PCT EXTENDED" -Unit = "percentage" +Unit = "percent" +StringFormat = "F0" [SpoilersAvailable] CategoryId = FlightSystems @@ -889,14 +890,14 @@ CategoryId = FlightSystems Name = "Aileron Trim Angle" SimVarName = "AILERON TRIM" Unit = "degrees" -StringFormat = "0.0#" +StringFormat = "F2" [ElevatorTrim] CategoryId = FlightSystems Name = "Elevator Trim Angle" SimVarName = "ELEVATOR TRIM POSITION" Unit = "degrees" -StringFormat = "0.0#" +StringFormat = "F2" CanSet = True [RudderTrim] @@ -904,7 +905,7 @@ CategoryId = FlightSystems Name = "Rudder Trim Angle" SimVarName = "RUDDER TRIM" Unit = "degrees" -StringFormat = "0.0#" +StringFormat = "F2" [AileronTrimPct] CategoryId = FlightSystems From c9bba537dc68d40b9cbc8135a400125506e43c77 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Fri, 1 Apr 2022 03:39:01 -0400 Subject: [PATCH 44/93] Just request the desired unit type for sim vars instead of doing conversions (radians -> degrees and "percent over 100" -> percent). Also fix formatting for AP bank and pitch degrees. --- .../Configuration/States.ini | 35 +++++++++---------- MSFSTouchPortalPlugin/Types/SimVarItem.cs | 15 ++------ 2 files changed, 19 insertions(+), 31 deletions(-) diff --git a/MSFSTouchPortalPlugin/Configuration/States.ini b/MSFSTouchPortalPlugin/Configuration/States.ini index b07f91e..524fc02 100644 --- a/MSFSTouchPortalPlugin/Configuration/States.ini +++ b/MSFSTouchPortalPlugin/Configuration/States.ini @@ -64,7 +64,8 @@ Unit = "Bool" CategoryId = AutoPilot Name = "AutoPilot Pitch Reference Value" SimVarName = "AUTOPILOT PITCH HOLD REF" -Unit = "radians" +Unit = "degrees" +StringFormat = "F2" [AutoPilotApproachHold] CategoryId = AutoPilot @@ -76,7 +77,8 @@ Unit = "Bool" CategoryId = AutoPilot Name = "AutoPilot Max Bank Angle" SimVarName = "AUTOPILOT MAX BANK" -Unit = "radians" +Unit = "degrees" +StringFormat = "F2" [AutoPilotHeadingHold] CategoryId = AutoPilot @@ -180,13 +182,15 @@ Unit = "Bool" CategoryId = AutoPilot Name = "Flight Director Current Bank" SimVarName = "AUTOPILOT FLIGHT DIRECTOR BANK" -Unit = "radians" +Unit = "degrees" +StringFormat = "F2" [AutoPilotFlightDirectorCurrentPitch] CategoryId = AutoPilot Name = "Flight Director Current Pitch" SimVarName = "AUTOPILOT FLIGHT DIRECTOR PITCH" -Unit = "radians" +Unit = "degrees" +StringFormat = "F2" [AutoPilotWingLeveler] CategoryId = AutoPilot @@ -740,28 +744,28 @@ StringFormat = "0.#" CategoryId = FlightInstruments Name = "Plane Heading (True North) in Degrees" SimVarName = "PLANE HEADING DEGREES TRUE" -Unit = "radians" +Unit = "degrees" StringFormat = "0" [PlaneHeadingMagnetic] CategoryId = FlightInstruments Name = "Plane Heading (Magnetic North) in Degrees" SimVarName = "PLANE HEADING DEGREES MAGNETIC" -Unit = "radians" +Unit = "degrees" StringFormat = "0" [PlaneBankAngle] CategoryId = FlightInstruments Name = "Plane Bank Angle in Degrees" SimVarName = "PLANE BANK DEGREES" -Unit = "radians" +Unit = "degrees" StringFormat = "0" [PlanePitchAngle] CategoryId = FlightInstruments Name = "Plane Pitch Angle in Degrees" SimVarName = "PLANE PITCH DEGREES" -Unit = "radians" +Unit = "degrees" StringFormat = "0" [VerticalSpeed] @@ -864,19 +868,17 @@ CanSet = True CategoryId = FlightSystems Name = "Spoilers Left Position Percent" SimVarName = "SPOILERS LEFT POSITION" -Unit = "percent over 100" +Unit = "percent" DefaultValue = "0" StringFormat = "F1" -DeltaEpsilon = 0.0000999 [SpoilersRightPosition] CategoryId = FlightSystems Name = "Spoilers Right Position Percent" SimVarName = "SPOILERS RIGHT POSITION" -Unit = "percent over 100" +Unit = "percent" DefaultValue = "0" StringFormat = "F1" -DeltaEpsilon = 0.0000999 [SpoilersArmed] CategoryId = FlightSystems @@ -911,29 +913,26 @@ StringFormat = "F2" CategoryId = FlightSystems Name = "Aileron Trim Percent" SimVarName = "AILERON TRIM PCT" -Unit = "percent over 100" +Unit = "percent" DefaultValue = "0" StringFormat = "F1" -DeltaEpsilon = 0.0000999 CanSet = True [ElevatorTrimPct] CategoryId = FlightSystems Name = "Elevator Trim Percent" SimVarName = "ELEVATOR TRIM PCT" -Unit = "percent over 100" +Unit = "percent" DefaultValue = "0" StringFormat = "F1" -DeltaEpsilon = 0.0000999 [RudderTrimPct] CategoryId = FlightSystems Name = "Rudder Trim Percent" SimVarName = "RUDDER TRIM PCT" -Unit = "percent over 100" +Unit = "percent" DefaultValue = "0" StringFormat = "F1" -DeltaEpsilon = 0.0000999 CanSet = True diff --git a/MSFSTouchPortalPlugin/Types/SimVarItem.cs b/MSFSTouchPortalPlugin/Types/SimVarItem.cs index 49a41bc..1679cdb 100644 --- a/MSFSTouchPortalPlugin/Types/SimVarItem.cs +++ b/MSFSTouchPortalPlugin/Types/SimVarItem.cs @@ -172,7 +172,7 @@ public SimVarItem() { } public bool ValueEquals(string value) => ValInit && IsStringType && value == Value.ToString(); - public bool ValueEquals(double value) => ValInit && IsRealType && System.Math.Abs((double)Value - ConvertValueIfNeeded(value)) <= DeltaEpsilon; + public bool ValueEquals(double value) => ValInit && IsRealType && System.Math.Abs((double)Value - value) <= DeltaEpsilon; public bool ValueEquals(long value) => ValInit && IsIntegralType && System.Math.Abs((long)Value - value) <= (long)DeltaEpsilon; public bool ValueEquals(uint value) => ValInit && IsBooleanType && System.Math.Abs((uint)Value - value) <= (uint)DeltaEpsilon; @@ -204,7 +204,7 @@ internal bool SetValue(StringVal value) { internal bool SetValue(double value) { if (!IsStringType) - Value = ConvertValueIfNeeded(value); + Value = value; return !IsStringType; } @@ -248,17 +248,6 @@ public void SetPending(bool val) { _requestTimeout = val ? Stopwatch.GetTimestamp() + REQ_TIMEOUT_SEC * Stopwatch.Frequency : 0; } - private double ConvertValueIfNeeded(double value) { - // Convert to Degrees - if (Unit == Units.radians) - return value * (180.0 / System.Math.PI); - // Convert to actual percentage (percentover100 range is 0 to 1) - if (Unit == Units.percentover100) - return value * 100.0; - // no conversion - return value; - } - private bool CheckPending() { if (_requestTimeout == 0) return false; From fbab350dde1f5efdc1ee35cef631d3c775f49bc2 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Fri, 1 Apr 2022 03:40:35 -0400 Subject: [PATCH 45/93] Add state's Format string to generated docs. --- MSFSTouchPortalPlugin-Generator/GenerateDoc.cs | 7 ++++--- MSFSTouchPortalPlugin-Generator/Model/DocBase.cs | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index cbd57bc..ab40abf 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -147,6 +147,7 @@ private DocBase CreateModel() { DefaultValue = state.DefaultValue ?? string.Empty, SimVarName = state.SimVarName, Unit = state.Unit, + FormattingString = state.FormattingString }; newCat.States.Add(newState); } @@ -263,10 +264,10 @@ private static string CreateMarkdown(DocBase model) { if (cat.States.Count > 0) { // Loop States s.Append("### States\n\n"); - s.Append("| Id | SimVar Name | Description | Unit | DefaultValue |\n"); - s.Append("| --- | --- | --- | --- | --- |\n"); + s.Append("| Id | SimVar Name | Description | Unit | Format | DefaultValue |\n"); + s.Append("| --- | --- | --- | --- | --- | --- |\n"); cat.States.ForEach(state => { - s.Append($"| {state.Id} | {state.SimVarName} | {state.Description} | {state.Unit} | {state.DefaultValue} |\n"); + s.Append($"| {state.Id} | {state.SimVarName} | {state.Description} | {state.Unit} | {state.FormattingString} | {state.DefaultValue} |\n"); }); s.Append("\n\n"); } diff --git a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs index 266b5a2..0c1ff1b 100644 --- a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs +++ b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs @@ -60,5 +60,6 @@ public class DocState { public string DefaultValue { get; set; } public string SimVarName { get; set; } public string Unit { get; set; } + public string FormattingString { get; set; } } } From 8b034753c71b44a9f10c0f8d52e646005b9cbd57 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Fri, 1 Apr 2022 12:28:10 -0400 Subject: [PATCH 46/93] Consolidate TP Category information (name, image) to new Categories static helper. --- .../GenerateDoc.cs | 14 ++-- .../GenerateEntry.cs | 26 ++++--- .../Model/DocBase.cs | 1 + MSFSTouchPortalPlugin/Constants/Categories.cs | 74 +++++++++++++++++++ 4 files changed, 99 insertions(+), 16 deletions(-) create mode 100644 MSFSTouchPortalPlugin/Constants/Categories.cs diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index ab40abf..05da42a 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -14,6 +14,7 @@ using System.Reflection; using System.Text; using TouchPortalExtension.Attributes; +using MSFSTouchPortalPlugin.Constants; namespace MSFSTouchPortalPlugin_Generator { @@ -33,8 +34,10 @@ public void Generate() { // Create Markdown var result = CreateMarkdown(model); - File.WriteAllText(Path.Combine(_options.Value.TargetPath, "DOCUMENTATION.md"), result); - _logger.LogInformation("DOCUMENTATION.md generated."); + // Save + var dest = Path.Combine(_options.Value.TargetPath, "DOCUMENTATION.md"); + File.WriteAllText(dest, result); + _logger.LogInformation($"Generated '{dest}'."); } private DocBase CreateModel() { @@ -76,10 +79,11 @@ private DocBase CreateModel() { _logger.LogWarning($"Could not parse category ID: '{catAttr.Id}', skipping.'"); continue; } - var newCat = model.Categories.FirstOrDefault(c => c.Name == catAttr.Name); + var newCat = model.Categories.FirstOrDefault(c => c.Id == catId); if (newCat == null) { newCat = new DocCategory { - Name = catAttr.Name + Id = catId, + Name = Categories.FullCategoryName(catId), }; model.Categories.Add(newCat); } @@ -121,7 +125,7 @@ private DocBase CreateModel() { } // Warn about missing mappings if (!mapAttribs.Any()) - _logger.LogWarning($"No event mappings found for action ID '{actionAttribute.Id}' in category '{catAttr.Name}'"); + _logger.LogWarning($"No event mappings found for action ID '{actionAttribute.Id}' in category '{cat.Name}'"); newCat.Actions.Add(newAct); }); diff --git a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs index 1647b3e..bc1d1bf 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using MSFSTouchPortalPlugin.Configuration; +using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Helpers; using MSFSTouchPortalPlugin.Types; @@ -41,7 +42,7 @@ public void Generate() { } var assembly = Assembly.Load(a); - string basePath = $"%TP_PLUGIN_FOLDER%{_options.Value.PluginFolder}"; + string basePath = $"%TP_PLUGIN_FOLDER%{_options.Value.PluginFolder}/"; // read default states config // TODO: Allow configuration of which state config file(s) to read. @@ -63,7 +64,7 @@ public void Generate() { Version = VersionInfo.GetProductVersionNumber(), Name = _options.Value.PluginName, Id = _options.Value.PluginName, - Plugin_start_cmd = $"{basePath}/dist/{_options.Value.PluginName}.exe" + Plugin_start_cmd = $"{basePath}dist/{_options.Value.PluginName}.exe" }; // Get all classes with the TouchPortalCategory @@ -78,24 +79,26 @@ public void Generate() { continue; } - var category = model.Categories.FirstOrDefault(c => c.Name == att.Name); + string catIdStr = $"{_options.Value.PluginName}.{catId}"; + var category = model.Categories.FirstOrDefault(c => c.Id == catIdStr); if (category == null) { category = new TouchPortalCategory { - // FIXME: For now use attribute Id (att.Id) instead of actual parsed Groups enum (catId) for backwards compat with mis-named actions in category InstrumentsSystems.Fuel - Id = $"{_options.Value.PluginName}.{att.Id}", - Name = att.Name, - // Imagepath = att.ImagePath - Imagepath = basePath + "/airplane_takeoff24.png" + Id = catIdStr, + Name = Categories.FullCategoryName(catId), + Imagepath = basePath + Categories.CategoryImage(catId) }; model.Categories.Add(category); } + // workaround for backwards compat with mis-named actions in category InstrumentsSystems.Fuel + string actionCatId = _options.Value.PluginName + "." + Categories.ActionCategoryId(catId); + // Add actions var actions = cat.GetMembers().Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalActionAttribute))).ToList(); actions.ForEach(act => { var actionAttribute = (TouchPortalActionAttribute)Attribute.GetCustomAttribute(act, typeof(TouchPortalActionAttribute)); var action = new TouchPortalAction { - Id = $"{category.Id}.Action.{actionAttribute.Id}", + Id = $"{actionCatId}.Action.{actionAttribute.Id}", Name = actionAttribute.Name, Prefix = actionAttribute.Prefix, Type = actionAttribute.Type, @@ -203,8 +206,9 @@ public void Generate() { } var result = JsonConvert.SerializeObject(model, Formatting.Indented); - File.WriteAllText(Path.Combine(_options.Value.TargetPath, "entry.tp"), result); - _logger.LogInformation("entry.tp generated."); + var dest = Path.Combine(_options.Value.TargetPath, "entry.tp"); + File.WriteAllText(dest, result); + _logger.LogInformation($"Generated '{dest}'."); } } } diff --git a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs index 0c1ff1b..57b945e 100644 --- a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs +++ b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs @@ -22,6 +22,7 @@ public class DocSetting { } public class DocCategory { + public MSFSTouchPortalPlugin.Enums.Groups Id { get; set; } public string Name { get; set; } public List Actions { get; set; } = new(); public List States { get; set; } = new(); diff --git a/MSFSTouchPortalPlugin/Constants/Categories.cs b/MSFSTouchPortalPlugin/Constants/Categories.cs new file mode 100644 index 0000000..12157df --- /dev/null +++ b/MSFSTouchPortalPlugin/Constants/Categories.cs @@ -0,0 +1,74 @@ +using MSFSTouchPortalPlugin.Enums; + +namespace MSFSTouchPortalPlugin.Constants +{ + internal static class Categories + { + + internal static string CategoryPrefix { get; set; } = "MSFS"; + internal static string NameSeparator { get; set; } = " - "; + internal static string CategoryDefaultImage { get; set; } = "airplane_takeoff24.png"; + + + private static readonly string[] categoryNames = new string[] + { + /* None, */ "None", + /* Plugin, */ "Plugin", + /* AutoPilot, */ "AutoPilot", + /* Communication, */ "Communication", + /* Electrical, */ "Electrical", + /* Engine, */ "Engine", + /* Environment, */ "Environment", + /* Failures, */ "Failures", + /* FlightInstruments, */ "Flight Instruments", + /* FlightSystems, */ "Flight Systems", + /* Fuel, */ "Fuel", + /* SimSystem, */ "System", + }; + + /// + /// Returns the category name for given enum value, or a blank string if the id is invalid.. + /// + internal static string CategoryName(Groups catId) { + if (System.Enum.IsDefined(catId)) + return categoryNames[(int)catId]; + return string.Empty; + } + + /// + /// Returns category name with the CategoryPrefix added. Or just the CategoryPrefix if the category id is invalid. + /// + internal static string FullCategoryName(Groups catId) { + if (CategoryName(catId) is var name && !string.IsNullOrEmpty(name)) + return CategoryPrefix + NameSeparator + name; + return CategoryPrefix; + } + + /// + /// Returns value with the full category name prepended to it using the common separator string. + /// + /// + /// + /// + internal static string PrependFullCategoryName(Groups catId, string value) { + return FullCategoryName(catId) + NameSeparator + value; + } + + /// + /// Workaround for legacy issue with mis-named actions in category InstrumentsSystems.Fuel instead of just Fuel. TODO + /// + internal static string ActionCategoryId(Groups catId) { + string ret = catId.ToString(); + if (catId == Groups.Fuel) + return "InstrumentsSystems." + ret; + return ret; + } + + internal static string CategoryImage(Groups catId) { + _ = catId; + // just the one, for now? + return CategoryDefaultImage; + } + + } +} From d78db790dfb7b169cbea9aedd48188944a778d16 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Fri, 1 Apr 2022 12:52:14 -0400 Subject: [PATCH 47/93] Get rid of TouchPortalExtension as separate project and move all attributes and enums to main plugin project. For simplicity and easier type sharing, and most attribute usage is being phased out anyway. --- .../GenerateDoc.cs | 4 ++-- .../GenerateEntry.cs | 2 +- MSFSTouchPortalPlugin.sln | 8 +------- .../Attributes/TouchPortalActionAttribute.cs | 6 +++--- .../TouchPortalCategoryAttribute.cs | 6 ++++-- .../Attributes/TouchPortalSettingAttribute.cs | 2 +- .../Attributes/TouchPortalStateAttribute.cs | 5 +++-- .../Enums/DataType.cs | 2 +- .../MSFSTouchPortalPlugin.csproj | 4 ---- .../Objects/AutoPilot/AutoPilot.cs | 2 -- .../Objects/Failures/Failures.cs | 1 - .../Objects/FlightSystems/FlightSystems.cs | 2 -- .../InstrumentsSystems/Communication.cs | 2 -- .../Objects/InstrumentsSystems/Electrical.cs | 2 -- .../Objects/InstrumentsSystems/Engine.cs | 2 -- .../Objects/InstrumentsSystems/Environment.cs | 2 -- .../InstrumentsSystems/FlightInstruments.cs | 3 +-- .../Objects/InstrumentsSystems/Fuel.cs | 1 - .../Objects/Plugin/Plugin.cs | 1 - .../Objects/SimSystem/SimSystem.cs | 1 - .../Services/ReflectionService.cs | 4 ---- .../Types/ActionEventType.cs | 1 - MSFSTouchPortalPlugin/Types/PluginSetting.cs | 4 ++-- .../TouchPortalExtension.csproj | 19 ------------------- 24 files changed, 19 insertions(+), 67 deletions(-) rename {TouchPortalExtension => MSFSTouchPortalPlugin}/Attributes/TouchPortalActionAttribute.cs (97%) rename {TouchPortalExtension => MSFSTouchPortalPlugin}/Attributes/TouchPortalCategoryAttribute.cs (81%) rename {TouchPortalExtension => MSFSTouchPortalPlugin}/Attributes/TouchPortalSettingAttribute.cs (96%) rename {TouchPortalExtension => MSFSTouchPortalPlugin}/Attributes/TouchPortalStateAttribute.cs (87%) rename {TouchPortalExtension => MSFSTouchPortalPlugin}/Enums/DataType.cs (68%) delete mode 100644 TouchPortalExtension/TouchPortalExtension.csproj diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index 05da42a..8d422fa 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -1,6 +1,8 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using MSFSTouchPortalPlugin.Attributes; using MSFSTouchPortalPlugin.Configuration; +using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Helpers; using MSFSTouchPortalPlugin.Types; @@ -13,8 +15,6 @@ using System.Text.RegularExpressions; using System.Reflection; using System.Text; -using TouchPortalExtension.Attributes; -using MSFSTouchPortalPlugin.Constants; namespace MSFSTouchPortalPlugin_Generator { diff --git a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs index bc1d1bf..15a0aef 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using MSFSTouchPortalPlugin.Attributes; using MSFSTouchPortalPlugin.Configuration; using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; @@ -16,7 +17,6 @@ using System.IO; using System.Linq; using System.Reflection; -using TouchPortalExtension.Attributes; namespace MSFSTouchPortalPlugin_Generator { diff --git a/MSFSTouchPortalPlugin.sln b/MSFSTouchPortalPlugin.sln index cfe5943..31953ba 100644 --- a/MSFSTouchPortalPlugin.sln +++ b/MSFSTouchPortalPlugin.sln @@ -7,8 +7,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSFSTouchPortalPlugin", "MS EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSFSTouchPortalPlugin-Generator", "MSFSTouchPortalPlugin-Generator\MSFSTouchPortalPlugin-Generator.csproj", "{53A38611-6486-4FF5-8637-1C6BC38A6A18}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TouchPortalExtension", "TouchPortalExtension\TouchPortalExtension.csproj", "{C217E7C3-4A14-457E-ABC8-180546369646}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6DF0C85E-3EB4-4898-A304-EFA04CB547A6}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig @@ -43,7 +41,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PULL_REQUEST_TEMPLATE", "PU .github\PULL_REQUEST_TEMPLATE\pull-request-template.md = .github\PULL_REQUEST_TEMPLATE\pull-request-template.md EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSFSTouchPortalPlugin-Tests", "MSFSTouchPortalPlugin-Tests\MSFSTouchPortalPlugin-Tests.csproj", "{562A58FF-DD9A-4839-B503-4520D3BE96DA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MSFSTouchPortalPlugin-Tests", "MSFSTouchPortalPlugin-Tests\MSFSTouchPortalPlugin-Tests.csproj", "{562A58FF-DD9A-4839-B503-4520D3BE96DA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -59,10 +57,6 @@ Global {53A38611-6486-4FF5-8637-1C6BC38A6A18}.Debug|x64.Build.0 = Debug|x64 {53A38611-6486-4FF5-8637-1C6BC38A6A18}.Release|x64.ActiveCfg = Release|x64 {53A38611-6486-4FF5-8637-1C6BC38A6A18}.Release|x64.Build.0 = Release|x64 - {C217E7C3-4A14-457E-ABC8-180546369646}.Debug|x64.ActiveCfg = Debug|x64 - {C217E7C3-4A14-457E-ABC8-180546369646}.Debug|x64.Build.0 = Debug|x64 - {C217E7C3-4A14-457E-ABC8-180546369646}.Release|x64.ActiveCfg = Release|x64 - {C217E7C3-4A14-457E-ABC8-180546369646}.Release|x64.Build.0 = Release|x64 {562A58FF-DD9A-4839-B503-4520D3BE96DA}.Debug|x64.ActiveCfg = Debug|x64 {562A58FF-DD9A-4839-B503-4520D3BE96DA}.Debug|x64.Build.0 = Debug|x64 {562A58FF-DD9A-4839-B503-4520D3BE96DA}.Release|x64.ActiveCfg = Release|x64 diff --git a/TouchPortalExtension/Attributes/TouchPortalActionAttribute.cs b/MSFSTouchPortalPlugin/Attributes/TouchPortalActionAttribute.cs similarity index 97% rename from TouchPortalExtension/Attributes/TouchPortalActionAttribute.cs rename to MSFSTouchPortalPlugin/Attributes/TouchPortalActionAttribute.cs index 7b469a1..4dd9eba 100644 --- a/TouchPortalExtension/Attributes/TouchPortalActionAttribute.cs +++ b/MSFSTouchPortalPlugin/Attributes/TouchPortalActionAttribute.cs @@ -1,7 +1,7 @@ -using System; -using TouchPortalExtension.Enums; +using MSFSTouchPortalPlugin.Enums; +using System; -namespace TouchPortalExtension.Attributes +namespace MSFSTouchPortalPlugin.Attributes { [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class TouchPortalActionAttribute : Attribute diff --git a/TouchPortalExtension/Attributes/TouchPortalCategoryAttribute.cs b/MSFSTouchPortalPlugin/Attributes/TouchPortalCategoryAttribute.cs similarity index 81% rename from TouchPortalExtension/Attributes/TouchPortalCategoryAttribute.cs rename to MSFSTouchPortalPlugin/Attributes/TouchPortalCategoryAttribute.cs index 6a46194..3f00ba7 100644 --- a/TouchPortalExtension/Attributes/TouchPortalCategoryAttribute.cs +++ b/MSFSTouchPortalPlugin/Attributes/TouchPortalCategoryAttribute.cs @@ -1,8 +1,10 @@ using System; -namespace TouchPortalExtension.Attributes { +namespace MSFSTouchPortalPlugin.Attributes +{ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum)] - public class TouchPortalCategoryAttribute : Attribute { + public class TouchPortalCategoryAttribute : Attribute + { public string Id { get; set; } public string Name { get; set; } public string ImagePath { get; set; } diff --git a/TouchPortalExtension/Attributes/TouchPortalSettingAttribute.cs b/MSFSTouchPortalPlugin/Attributes/TouchPortalSettingAttribute.cs similarity index 96% rename from TouchPortalExtension/Attributes/TouchPortalSettingAttribute.cs rename to MSFSTouchPortalPlugin/Attributes/TouchPortalSettingAttribute.cs index f83b3e9..738dba6 100644 --- a/TouchPortalExtension/Attributes/TouchPortalSettingAttribute.cs +++ b/MSFSTouchPortalPlugin/Attributes/TouchPortalSettingAttribute.cs @@ -1,6 +1,6 @@ using System; -namespace TouchPortalExtension.Attributes +namespace MSFSTouchPortalPlugin.Attributes { [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class TouchPortalSettingAttribute : Attribute diff --git a/TouchPortalExtension/Attributes/TouchPortalStateAttribute.cs b/MSFSTouchPortalPlugin/Attributes/TouchPortalStateAttribute.cs similarity index 87% rename from TouchPortalExtension/Attributes/TouchPortalStateAttribute.cs rename to MSFSTouchPortalPlugin/Attributes/TouchPortalStateAttribute.cs index 5e5a8ce..5ca22c2 100644 --- a/TouchPortalExtension/Attributes/TouchPortalStateAttribute.cs +++ b/MSFSTouchPortalPlugin/Attributes/TouchPortalStateAttribute.cs @@ -1,11 +1,12 @@ using System; -namespace TouchPortalExtension.Attributes +namespace MSFSTouchPortalPlugin.Attributes { // Currently unused, preserved for posterity (or possible future use). [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public class TouchPortalStateAttribute : Attribute { + public class TouchPortalStateAttribute : Attribute + { public string Id { get; set; } public string Type { get; set; } public string Description { get; set; } diff --git a/TouchPortalExtension/Enums/DataType.cs b/MSFSTouchPortalPlugin/Enums/DataType.cs similarity index 68% rename from TouchPortalExtension/Enums/DataType.cs rename to MSFSTouchPortalPlugin/Enums/DataType.cs index ddcec09..a12f277 100644 --- a/TouchPortalExtension/Enums/DataType.cs +++ b/MSFSTouchPortalPlugin/Enums/DataType.cs @@ -1,4 +1,4 @@ -namespace TouchPortalExtension.Enums +namespace MSFSTouchPortalPlugin.Enums { public enum DataType { diff --git a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj index d3a3462..afe651d 100644 --- a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj +++ b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj @@ -59,10 +59,6 @@ - - - - .\lib\TouchPortalSDK\TouchPortalSDK.dll diff --git a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs index bf00b24..49e4375 100644 --- a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs +++ b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs @@ -1,7 +1,5 @@ using MSFSTouchPortalPlugin.Attributes; -using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; -using TouchPortalExtension.Attributes; namespace MSFSTouchPortalPlugin.Objects.AutoPilot { diff --git a/MSFSTouchPortalPlugin/Objects/Failures/Failures.cs b/MSFSTouchPortalPlugin/Objects/Failures/Failures.cs index 783fa46..02a670a 100644 --- a/MSFSTouchPortalPlugin/Objects/Failures/Failures.cs +++ b/MSFSTouchPortalPlugin/Objects/Failures/Failures.cs @@ -1,6 +1,5 @@ using MSFSTouchPortalPlugin.Attributes; using MSFSTouchPortalPlugin.Enums; -using TouchPortalExtension.Attributes; namespace MSFSTouchPortalPlugin.Objects.Failures { diff --git a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs index 43ebb2f..06e16e8 100644 --- a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs +++ b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs @@ -1,7 +1,5 @@ using MSFSTouchPortalPlugin.Attributes; -using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; -using TouchPortalExtension.Attributes; namespace MSFSTouchPortalPlugin.Objects.FlightSystems { diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Communication.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Communication.cs index 006c5c8..8f3b834 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Communication.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Communication.cs @@ -1,7 +1,5 @@ using MSFSTouchPortalPlugin.Attributes; -using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; -using TouchPortalExtension.Attributes; namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs index fb413f0..22a8439 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs @@ -1,7 +1,5 @@ using MSFSTouchPortalPlugin.Attributes; -using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; -using TouchPortalExtension.Attributes; namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs index e33de1e..a3e3f10 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs @@ -1,7 +1,5 @@ using MSFSTouchPortalPlugin.Attributes; -using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; -using TouchPortalExtension.Attributes; namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Environment.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Environment.cs index 9ae570e..e0d4b2a 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Environment.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Environment.cs @@ -1,7 +1,5 @@ using MSFSTouchPortalPlugin.Attributes; -using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; -using TouchPortalExtension.Attributes; namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/FlightInstruments.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/FlightInstruments.cs index 6b72158..54e30fc 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/FlightInstruments.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/FlightInstruments.cs @@ -1,6 +1,5 @@ using MSFSTouchPortalPlugin.Attributes; -using MSFSTouchPortalPlugin.Constants; -using TouchPortalExtension.Attributes; +//using MSFSTouchPortalPlugin.Enums; namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs index a59882d..50a0408 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs @@ -1,6 +1,5 @@ using MSFSTouchPortalPlugin.Attributes; using MSFSTouchPortalPlugin.Enums; -using TouchPortalExtension.Attributes; namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { diff --git a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs index 26126b6..c1b57ef 100644 --- a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs +++ b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs @@ -1,7 +1,6 @@ using MSFSTouchPortalPlugin.Attributes; using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Types; -using TouchPortalExtension.Attributes; namespace MSFSTouchPortalPlugin.Objects.Plugin { diff --git a/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs b/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs index 5428e94..6eaffac 100644 --- a/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs +++ b/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs @@ -1,6 +1,5 @@ using MSFSTouchPortalPlugin.Attributes; using MSFSTouchPortalPlugin.Enums; -using TouchPortalExtension.Attributes; namespace MSFSTouchPortalPlugin.Objects.SimSystem { diff --git a/MSFSTouchPortalPlugin/Services/ReflectionService.cs b/MSFSTouchPortalPlugin/Services/ReflectionService.cs index 1211822..4b0d2ae 100644 --- a/MSFSTouchPortalPlugin/Services/ReflectionService.cs +++ b/MSFSTouchPortalPlugin/Services/ReflectionService.cs @@ -1,7 +1,5 @@ using Microsoft.Extensions.Logging; using MSFSTouchPortalPlugin.Attributes; -using MSFSTouchPortalPlugin.Configuration; -using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Interfaces; using MSFSTouchPortalPlugin.Objects.Plugin; @@ -10,8 +8,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using TouchPortalExtension.Attributes; -using TouchPortalExtension.Enums; namespace MSFSTouchPortalPlugin.Services { diff --git a/MSFSTouchPortalPlugin/Types/ActionEventType.cs b/MSFSTouchPortalPlugin/Types/ActionEventType.cs index 19d8253..b982a0b 100644 --- a/MSFSTouchPortalPlugin/Types/ActionEventType.cs +++ b/MSFSTouchPortalPlugin/Types/ActionEventType.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; -using TouchPortalExtension.Enums; namespace MSFSTouchPortalPlugin.Types { diff --git a/MSFSTouchPortalPlugin/Types/PluginSetting.cs b/MSFSTouchPortalPlugin/Types/PluginSetting.cs index 38c1bc9..903f28c 100644 --- a/MSFSTouchPortalPlugin/Types/PluginSetting.cs +++ b/MSFSTouchPortalPlugin/Types/PluginSetting.cs @@ -1,5 +1,5 @@ -using System; -using TouchPortalExtension.Enums; +using MSFSTouchPortalPlugin.Enums; +using System; namespace MSFSTouchPortalPlugin.Types { diff --git a/TouchPortalExtension/TouchPortalExtension.csproj b/TouchPortalExtension/TouchPortalExtension.csproj deleted file mode 100644 index b4dea9f..0000000 --- a/TouchPortalExtension/TouchPortalExtension.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - net5.0 - ..\.sonarlint\msfstouchportalplugincsharp.ruleset - x64 - 1.1.0 - ..\build\$(Platform)-$(Configuration)\ - - - - - - - - - - - From a0852de9ff24be14126399696cd750a213a90ca8 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Fri, 1 Apr 2022 13:29:59 -0400 Subject: [PATCH 48/93] Refactor TouchPortalCategoryAttribute to only contain the actual category ID enum (Groups), which is was redundant with SimNotificationGroupAttribute (which is now removed). --- MSFSTouchPortalPlugin-Generator/GenerateDoc.cs | 6 +----- .../GenerateEntry.cs | 7 +------ .../Attributes/SimNotificationGroupAttribute.cs | 13 ------------- .../Attributes/TouchPortalCategoryAttribute.cs | 17 +++++------------ .../Objects/AutoPilot/AutoPilot.cs | 3 +-- .../Objects/Failures/Failures.cs | 3 +-- .../Objects/FlightSystems/FlightSystems.cs | 3 +-- .../Objects/InstrumentsSystems/Communication.cs | 3 +-- .../Objects/InstrumentsSystems/Electrical.cs | 3 +-- .../Objects/InstrumentsSystems/Engine.cs | 3 +-- .../Objects/InstrumentsSystems/Environment.cs | 3 +-- .../InstrumentsSystems/FlightInstruments.cs | 5 ++--- .../Objects/InstrumentsSystems/Fuel.cs | 3 +-- MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs | 6 ++---- .../Objects/SimSystem/SimSystem.cs | 3 +-- MSFSTouchPortalPlugin/Services/PluginService.cs | 5 ++--- .../Services/ReflectionService.cs | 14 ++++++-------- MSFSTouchPortalPlugin/Types/ActionEventType.cs | 2 +- 18 files changed, 29 insertions(+), 73 deletions(-) delete mode 100644 MSFSTouchPortalPlugin/Attributes/SimNotificationGroupAttribute.cs diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index 8d422fa..fa2806a 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -74,11 +74,7 @@ private DocBase CreateModel() { // Loop through categories foreach (var cat in classList) { var catAttr = (TouchPortalCategoryAttribute)Attribute.GetCustomAttribute(cat, typeof(TouchPortalCategoryAttribute)); - // FIXME: the Split() is necessary due to legacy mis-named category InstrumentsSystems.Fuel - if (!Enum.TryParse(catAttr.Id.Split('.').Last(), false, out Groups catId)) { - _logger.LogWarning($"Could not parse category ID: '{catAttr.Id}', skipping.'"); - continue; - } + Groups catId = catAttr.Id; var newCat = model.Categories.FirstOrDefault(c => c.Id == catId); if (newCat == null) { newCat = new DocCategory { diff --git a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs index 15a0aef..905292d 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs @@ -73,12 +73,7 @@ public void Generate() { // For each category, add to model foreach (var cat in categoryClasses) { var att = (TouchPortalCategoryAttribute)Attribute.GetCustomAttribute(cat, typeof(TouchPortalCategoryAttribute)); - // FIXME: the Split() is necessary due to legacy mis-named category InstrumentsSystems.Fuel - if (!Enum.TryParse(att.Id.Split('.').Last(), false, out Groups catId)) { - _logger.LogWarning($"Could not parse category ID: '{att.Id}', skipping.'"); - continue; - } - + Groups catId = att.Id; string catIdStr = $"{_options.Value.PluginName}.{catId}"; var category = model.Categories.FirstOrDefault(c => c.Id == catIdStr); if (category == null) { diff --git a/MSFSTouchPortalPlugin/Attributes/SimNotificationGroupAttribute.cs b/MSFSTouchPortalPlugin/Attributes/SimNotificationGroupAttribute.cs deleted file mode 100644 index 5e835c9..0000000 --- a/MSFSTouchPortalPlugin/Attributes/SimNotificationGroupAttribute.cs +++ /dev/null @@ -1,13 +0,0 @@ -using MSFSTouchPortalPlugin.Enums; -using System; - -namespace MSFSTouchPortalPlugin.Attributes { - [AttributeUsage(AttributeTargets.Enum | AttributeTargets.Class)] - internal class SimNotificationGroupAttribute : Attribute { - public Groups Group; - - public SimNotificationGroupAttribute(Groups group) { - Group = group; - } - } -} diff --git a/MSFSTouchPortalPlugin/Attributes/TouchPortalCategoryAttribute.cs b/MSFSTouchPortalPlugin/Attributes/TouchPortalCategoryAttribute.cs index 3f00ba7..b1378d8 100644 --- a/MSFSTouchPortalPlugin/Attributes/TouchPortalCategoryAttribute.cs +++ b/MSFSTouchPortalPlugin/Attributes/TouchPortalCategoryAttribute.cs @@ -1,22 +1,15 @@ -using System; +using MSFSTouchPortalPlugin.Enums; +using System; namespace MSFSTouchPortalPlugin.Attributes { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum)] + [AttributeUsage(AttributeTargets.All)] public class TouchPortalCategoryAttribute : Attribute { - public string Id { get; set; } - public string Name { get; set; } - public string ImagePath { get; set; } + public Groups Id; - public TouchPortalCategoryAttribute(string id, string name) { - SetupProperties(id, name, string.Empty); - } - - private void SetupProperties(string id, string name, string imagePath) { + public TouchPortalCategoryAttribute(Groups id) { Id = id; - Name = name; - ImagePath = imagePath; } } } diff --git a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs index 49e4375..2e0208f 100644 --- a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs +++ b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs @@ -3,8 +3,7 @@ namespace MSFSTouchPortalPlugin.Objects.AutoPilot { - [SimNotificationGroup(Groups.AutoPilot)] - [TouchPortalCategory("AutoPilot", "MSFS - AutoPilot")] + [TouchPortalCategory(Groups.AutoPilot)] internal static class AutoPilotMapping { #region AutoPilot Master diff --git a/MSFSTouchPortalPlugin/Objects/Failures/Failures.cs b/MSFSTouchPortalPlugin/Objects/Failures/Failures.cs index 02a670a..963cd2d 100644 --- a/MSFSTouchPortalPlugin/Objects/Failures/Failures.cs +++ b/MSFSTouchPortalPlugin/Objects/Failures/Failures.cs @@ -3,8 +3,7 @@ namespace MSFSTouchPortalPlugin.Objects.Failures { - [SimNotificationGroup(Groups.Failures)] - [TouchPortalCategory("Failures", "MSFS - Failures")] + [TouchPortalCategory(Groups.Failures)] internal static class FailuresMapping { [TouchPortalAction("Failures", "Failures", "MSFS", "Toggle Failures", "Toggle Failures - {0}")] diff --git a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs index 06e16e8..6cf0a81 100644 --- a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs +++ b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs @@ -3,8 +3,7 @@ namespace MSFSTouchPortalPlugin.Objects.FlightSystems { - [SimNotificationGroup(Groups.FlightSystems)] - [TouchPortalCategory("FlightSystems", "MSFS - Flight Systems")] + [TouchPortalCategory(Groups.FlightSystems)] internal static class FlightSystemsMapping { #region Ailerons diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Communication.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Communication.cs index 8f3b834..55484e7 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Communication.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Communication.cs @@ -3,8 +3,7 @@ namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { - [SimNotificationGroup(Groups.Communication)] - [TouchPortalCategory("Communication", "MSFS - Communication")] + [TouchPortalCategory(Groups.Communication)] internal static class CommunicationMapping { [TouchPortalAction("Radios", "Radio Interaction", "MSFS", "Radio Interaction", "Radio {0} - {1}", true)] diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs index 22a8439..491507b 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Electrical.cs @@ -3,8 +3,7 @@ namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { - [SimNotificationGroup(Groups.Electrical)] - [TouchPortalCategory("Electrical", "MSFS - Electrical")] + [TouchPortalCategory(Groups.Electrical)] internal static class ElectricalMapping { #region Avionics diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs index a3e3f10..45221e2 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs @@ -3,8 +3,7 @@ namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { - [SimNotificationGroup(Groups.Engine)] - [TouchPortalCategory("Engine", "MSFS - Engine")] + [TouchPortalCategory(Groups.Engine)] internal static class EngineMapping { #region Ignition diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Environment.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Environment.cs index e0d4b2a..ab841a9 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Environment.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Environment.cs @@ -3,8 +3,7 @@ namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { - [SimNotificationGroup(Groups.Environment)] - [TouchPortalCategory("Environment", "MSFS - Environment")] + [TouchPortalCategory(Groups.Environment)] internal static class EnvironmentMapping { #region Anti-Ice diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/FlightInstruments.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/FlightInstruments.cs index 54e30fc..a82b971 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/FlightInstruments.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/FlightInstruments.cs @@ -1,10 +1,9 @@ using MSFSTouchPortalPlugin.Attributes; -//using MSFSTouchPortalPlugin.Enums; +using MSFSTouchPortalPlugin.Enums; namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { - //[SimNotificationGroup(Groups.FlightInstruments)] - [TouchPortalCategory("FlightInstruments", "MSFS - Flight Instruments")] + [TouchPortalCategory(Groups.FlightInstruments)] internal static class FlightInstrumentsMapping { } diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs index 50a0408..2b8fe0c 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs @@ -3,8 +3,7 @@ namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems { - [SimNotificationGroup(Groups.Fuel)] - [TouchPortalCategory("InstrumentsSystems.Fuel", "MSFS - Fuel")] + [TouchPortalCategory(Groups.Fuel)] internal static class FuelMapping { [TouchPortalAction("AddFuel", "Add Fuel", "MSFS", "Adds 25% amount of Fuel", "Add 25% amount of fuel")] diff --git a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs index c1b57ef..2203411 100644 --- a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs +++ b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs @@ -4,8 +4,7 @@ namespace MSFSTouchPortalPlugin.Objects.Plugin { - [SimNotificationGroup(Groups.Plugin)] - [TouchPortalCategory("Plugin", "MSFS - Plugin")] + [TouchPortalCategory(Groups.Plugin)] internal static class PluginMapping { [TouchPortalAction("Connection", "Connection", "MSFS", "Toggle/On/Off SimConnect Connection", "SimConnect Connection - {0}")] [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] @@ -15,8 +14,7 @@ internal static class PluginMapping { public static readonly object Connection; } - [SimNotificationGroup(Groups.Plugin)] - [TouchPortalCategory("Plugin", "MSFS - Plugin")] + [TouchPortalCategory(Groups.Plugin)] [TouchPortalSettingsContainer] public static class Settings { [TouchPortalSetting( diff --git a/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs b/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs index 6eaffac..c288f87 100644 --- a/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs +++ b/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs @@ -3,8 +3,7 @@ namespace MSFSTouchPortalPlugin.Objects.SimSystem { - [SimNotificationGroup(Groups.SimSystem)] - [TouchPortalCategory("SimSystem", "MSFS - System")] + [TouchPortalCategory(Groups.SimSystem)] internal static class SimSystemMapping { [TouchPortalAction("SimulationRate", "Simulation Rate", "MSFS", "Simulation Rate", "Rate {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" }, "Decrease")] diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index abdf748..d508353 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -12,7 +12,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using TouchPortalExtension.Enums; using TouchPortalSDK; using TouchPortalSDK.Interfaces; using TouchPortalSDK.Messages.Events; @@ -354,8 +353,8 @@ private void ProcessSimEvent(ActionEventType action, Enum eventId, in string[] d _logger.LogWarning(e, $"Action {action.ActionId} for sim event {eventName} with data string '{valStr}' - Failed to convert data to numeric value."); } } - _logger.LogDebug($"Firing Sim Event - action: {action.ActionId}; group: {action.SimConnectGroup}; name: {eventName}; data {dataUint}"); - _simConnectService.TransmitClientEvent(action.SimConnectGroup, eventId, dataUint); + _logger.LogDebug($"Firing Sim Event - action: {action.ActionId}; category: {action.CategoryId}; name: {eventName}; data {dataUint}"); + _simConnectService.TransmitClientEvent(action.CategoryId, eventId, dataUint); } private void ClearRepeatingActions() { diff --git a/MSFSTouchPortalPlugin/Services/ReflectionService.cs b/MSFSTouchPortalPlugin/Services/ReflectionService.cs index 4b0d2ae..912a9e5 100644 --- a/MSFSTouchPortalPlugin/Services/ReflectionService.cs +++ b/MSFSTouchPortalPlugin/Services/ReflectionService.cs @@ -37,22 +37,20 @@ public Dictionary GetActionEvents() { int nextId = (int)SimEventClientId.Init + 1; // Get all types which have actions mapped to events, sim or internal. - var eventContainers = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsClass && (t.GetCustomAttribute() != null)); + var eventContainers = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsClass && (t.GetCustomAttribute() != null)); foreach (var notifyType in eventContainers) { // Get the TP Category ID for this type - var catId = notifyType.GetCustomAttribute().Id; - // And the SimConnect Group ID - Groups simGroup = notifyType.GetCustomAttribute()?.Group ?? default; - if (simGroup == default) + Groups catId = notifyType.GetCustomAttribute()?.Id ?? default; + if (catId == default) continue; - var internalEvent = simGroup == Groups.Plugin; + var internalEvent = catId == Groups.Plugin; // Get all members which have an action mapping, which is some unique combination of value(s) mapped to a SimConnect event name or internal event enum List actionMembers = notifyType.GetMembers().Where(m => m.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalActionMappingAttribute))).ToList(); actionMembers.ForEach(e => { // Create the action data object to store in the return dict, using the meta data we've collected so far. ActionEventType act = new ActionEventType { InternalEvent = internalEvent, - SimConnectGroup = simGroup, + CategoryId = catId, ActionId = e.GetCustomAttribute().Id, //ActionObject = e }; @@ -83,7 +81,7 @@ public Dictionary GetActionEvents() { _logger.LogWarning($"Duplicate action-to-event mapping found for action {act.ActionId} with choices '{string.Join(",", ma.Values)} for event '{ma.ActionId}'!'"); // keep track of generated event IDs for Sim actions (for registering to SimConnect, and debug) if (!internalEvent) - clientEventIdToNameMap[mapTarget] = new { EventName = ma.ActionId, GroupId = simGroup }; + clientEventIdToNameMap[mapTarget] = new { EventName = ma.ActionId, GroupId = catId }; }); // Put into returned collection if (!returnDict.TryAdd($"{rootName}.{catId}.Action.{act.ActionId}", act)) diff --git a/MSFSTouchPortalPlugin/Types/ActionEventType.cs b/MSFSTouchPortalPlugin/Types/ActionEventType.cs index b982a0b..055a97f 100644 --- a/MSFSTouchPortalPlugin/Types/ActionEventType.cs +++ b/MSFSTouchPortalPlugin/Types/ActionEventType.cs @@ -8,7 +8,7 @@ namespace MSFSTouchPortalPlugin.Types internal class ActionEventType { public bool InternalEvent = false; - public Groups SimConnectGroup; + public Groups CategoryId; public string ActionId; public int ValueIndex = -1; public string KeyFormatStr = string.Empty; From 2226c0728f28eaa987b14522dc512abb8fc7b496 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Fri, 1 Apr 2022 16:32:42 -0400 Subject: [PATCH 49/93] [Generator] Use ReflectionService to generate Settings data; Removes requirement to load PluginService and SDK dependency (yay!); Change default data destination to CWD and re-add launchSettings.json; Silence Microsoft log entries and remove debug info from release builds. --- .../GenerateDoc.cs | 40 +++++++------ .../GenerateEntry.cs | 56 +++++++++---------- .../MSFSTouchPortalPlugin-Generator.csproj | 7 +-- MSFSTouchPortalPlugin-Generator/Program.cs | 5 +- .../Properties/launchSettings.json | 8 +++ 5 files changed, 60 insertions(+), 56 deletions(-) create mode 100644 MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index fa2806a..435bb19 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -5,6 +5,7 @@ using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Helpers; +using MSFSTouchPortalPlugin.Interfaces; using MSFSTouchPortalPlugin.Types; using MSFSTouchPortalPlugin_Generator.Configuration; using MSFSTouchPortalPlugin_Generator.Interfaces; @@ -21,10 +22,12 @@ namespace MSFSTouchPortalPlugin_Generator internal class GenerateDoc : IGenerateDoc { private readonly ILogger _logger; private readonly IOptions _options; + private readonly IReflectionService _reflectionSvc; - public GenerateDoc(ILogger logger, IOptions options) { + public GenerateDoc(ILogger logger, IOptions options, IReflectionService reflectionSvc) { _logger = logger; _options = options; + _reflectionSvc = reflectionSvc ?? throw new ArgumentNullException(nameof(reflectionSvc)); } public void Generate() { @@ -157,26 +160,21 @@ private DocBase CreateModel() { } // categories loop // Settings - var setContainers = assemblyList.Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalSettingsContainerAttribute))).OrderBy(o => o.Name).ToList(); - setContainers.ForEach(setCtr => { - var settingsList = setCtr.GetMembers().Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalSettingAttribute))).ToList(); - settingsList.ForEach(setType => { - var att = (TouchPortalSettingAttribute)Attribute.GetCustomAttribute(setType, typeof(TouchPortalSettingAttribute)); - var setting = new DocSetting { - Name = att.Name, - Description = att.Description, - Type = att.Type, - DefaultValue = att.Default, - MaxLength = att.MaxLength, - MinValue = att.MinValue, - MaxValue = att.MaxValue, - IsPassword = att.IsPassword, - ReadOnly = att.ReadOnly - }; - - model.Settings.Add(setting); - }); - }); + var settings = _reflectionSvc.GetSettings().Values; + foreach (var s in settings) { + var setting = new DocSetting { + Name = s.Name, + Description = s.Description, + Type = s.TouchPortalType, + DefaultValue = s.Default, + MaxLength = s.MaxLength, + MinValue = s.MinValue, + MaxValue = s.MaxValue, + IsPassword = s.IsPassword, + ReadOnly = s.ReadOnly + }; + model.Settings.Add(setting); + } model.Settings = model.Settings.OrderBy(c => c.Name).ToList(); return model; diff --git a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs index 905292d..b3d2f6d 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs @@ -5,6 +5,7 @@ using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Helpers; +using MSFSTouchPortalPlugin.Interfaces; using MSFSTouchPortalPlugin.Types; using MSFSTouchPortalPlugin_Generator.Configuration; using MSFSTouchPortalPlugin_Generator.Interfaces; @@ -23,10 +24,12 @@ namespace MSFSTouchPortalPlugin_Generator internal class GenerateEntry : IGenerateEntry { private readonly ILogger _logger; private readonly IOptions _options; + private readonly IReflectionService _reflectionSvc; - public GenerateEntry(ILogger logger, IOptions options) { + public GenerateEntry(ILogger logger, IOptions options, IReflectionService reflectionSvc) { _logger = logger; _options = options; + _reflectionSvc = reflectionSvc ?? throw new ArgumentNullException(nameof(reflectionSvc)); JsonConvert.DefaultSettings = () => new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() @@ -41,6 +44,7 @@ public void Generate() { throw new FileNotFoundException("Unable to load assembly for reflection."); } + // Load assembly var assembly = Assembly.Load(a); string basePath = $"%TP_PLUGIN_FOLDER%{_options.Value.PluginFolder}/"; @@ -53,8 +57,6 @@ public void Generate() { _logger.LogError(e, "Configuration reader error:"); } - // Load assembly - _ = MSFSTouchPortalPlugin.Objects.Plugin.Plugin.Init; var q = assembly.GetTypes().ToList(); // Setup Base Model @@ -164,32 +166,28 @@ public void Generate() { } // categories loop // Settings - var setContainers = q.Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalSettingsContainerAttribute))).OrderBy(o => o.Name).ToList(); - setContainers.ForEach(setCtr => { - var settingsList = setCtr.GetMembers().Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalSettingAttribute))).ToList(); - settingsList.ForEach(setType => { - var att = (TouchPortalSettingAttribute)Attribute.GetCustomAttribute(setType, typeof(TouchPortalSettingAttribute)); - var setting = new TouchPortalSetting { - Name = att.Name, - Type = att.Type, - DefaultValue = att.Default, - IsPassword = att.IsPassword, - ReadOnly = att.ReadOnly - }; - if (att.MaxLength > 0) - setting.MaxLength = att.MaxLength; - if (!double.IsNaN(att.MinValue)) - setting.MinValue = att.MinValue; - if (!double.IsNaN(att.MaxValue)) - setting.MaxValue = att.MaxValue; - - // validate unique Name - if (model.Settings.FirstOrDefault(s => s.Name == setting.Name) == null) - model.Settings.Add(setting); - else - _logger.LogWarning($"Duplicate Setting Name found: '{setting.Name}', skipping.'"); - }); - }); + var settings = _reflectionSvc.GetSettings().Values; + foreach (var s in settings) { + var setting = new TouchPortalSetting { + Name = s.Name, + Type = s.TouchPortalType, + DefaultValue = s.Default, + IsPassword = s.IsPassword, + ReadOnly = s.ReadOnly + }; + if (s.MaxLength > 0) + setting.MaxLength = s.MaxLength; + if (!double.IsNaN(s.MinValue)) + setting.MinValue = s.MinValue; + if (!double.IsNaN(s.MaxValue)) + setting.MaxValue = s.MaxValue; + + // validate unique Name + if (model.Settings.FirstOrDefault(s => s.Name == setting.Name) == null) + model.Settings.Add(setting); + else + _logger.LogWarning($"Duplicate Setting Name found: '{setting.Name}', skipping.'"); + } var context = new ValidationContext(model, null, null); var errors = new Collection(); diff --git a/MSFSTouchPortalPlugin-Generator/MSFSTouchPortalPlugin-Generator.csproj b/MSFSTouchPortalPlugin-Generator/MSFSTouchPortalPlugin-Generator.csproj index 0a709ce..27aa131 100644 --- a/MSFSTouchPortalPlugin-Generator/MSFSTouchPortalPlugin-Generator.csproj +++ b/MSFSTouchPortalPlugin-Generator/MSFSTouchPortalPlugin-Generator.csproj @@ -12,6 +12,9 @@ 1701;1702 + + none + false @@ -25,10 +28,6 @@ - - ..\MSFSTouchPortalPlugin\lib\TouchPortalSDK\TouchPortalSDK.dll - true - diff --git a/MSFSTouchPortalPlugin-Generator/Program.cs b/MSFSTouchPortalPlugin-Generator/Program.cs index 0a1cae7..cc643e6 100644 --- a/MSFSTouchPortalPlugin-Generator/Program.cs +++ b/MSFSTouchPortalPlugin-Generator/Program.cs @@ -9,6 +9,7 @@ namespace MSFSTouchPortalPlugin_Generator { static class Program { static async Task Main(string[] args) { + System.Environment.SetEnvironmentVariable("Logging:LogLevel:Microsoft", "Warning"); await Host.CreateDefaultBuilder(args).ConfigureServices((context, services) => { services .AddLogging() @@ -18,10 +19,10 @@ await Host.CreateDefaultBuilder(args).ConfigureServices((context, services) => { if (args.Length >= 1) opt.TargetPath = args[0]; else - opt.TargetPath = "..\\..\\..\\"; // assumes it is being run from the build output folder + opt.TargetPath = ".\\"; // cwd }) .AddHostedService() - .AddSingleton() // Force load of assembly for generation + .AddSingleton() .AddSingleton() .AddSingleton(); }).RunConsoleAsync(); diff --git a/MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json b/MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json new file mode 100644 index 0000000..1ebc451 --- /dev/null +++ b/MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "MSFSTouchPortalPlugin-Generator": { + "commandName": "Project", + "commandLineArgs": "$(SolutionDir)" + } + } +} \ No newline at end of file From 1fcf319cd635620f81072634d79eb65ca4f4b06b Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Fri, 1 Apr 2022 16:35:02 -0400 Subject: [PATCH 50/93] [PluginSetting] Refactor to remove need for TouchPortalSettingAttribute. Also remove unused TouchPortalStateAttribute. --- .../Attributes/TouchPortalSettingAttribute.cs | 35 ------------ .../TouchPortalSettingsContainerAttribute.cs | 7 +++ .../Attributes/TouchPortalStateAttribute.cs | 26 --------- .../Objects/Plugin/Plugin.cs | 25 ++++----- .../Services/ReflectionService.cs | 26 ++++----- MSFSTouchPortalPlugin/Types/PluginSetting.cs | 54 ++++++++++++++----- 6 files changed, 72 insertions(+), 101 deletions(-) delete mode 100644 MSFSTouchPortalPlugin/Attributes/TouchPortalSettingAttribute.cs create mode 100644 MSFSTouchPortalPlugin/Attributes/TouchPortalSettingsContainerAttribute.cs delete mode 100644 MSFSTouchPortalPlugin/Attributes/TouchPortalStateAttribute.cs diff --git a/MSFSTouchPortalPlugin/Attributes/TouchPortalSettingAttribute.cs b/MSFSTouchPortalPlugin/Attributes/TouchPortalSettingAttribute.cs deleted file mode 100644 index 738dba6..0000000 --- a/MSFSTouchPortalPlugin/Attributes/TouchPortalSettingAttribute.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; - -namespace MSFSTouchPortalPlugin.Attributes -{ - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public class TouchPortalSettingAttribute : Attribute - { - public string Name { get; set; } = default; - public string Type { get; set; } = "text"; - public string Default { get; set; } = default; - public string Description { get; set; } // for generated docs - public int MaxLength { get; set; } = int.MinValue; - public double MinValue { get; set; } = double.NaN; - public double MaxValue { get; set; } = double.NaN; - public bool ReadOnly { get; set; } = false; - public bool IsPassword { get; set; } = false; - public string StateId { get; set; } = default; - - public TouchPortalSettingAttribute() { } - public TouchPortalSettingAttribute(string name, string description = default, string type = "text", string dflt = default, bool readOnly = false) { - SetupProperties(name, description, type, dflt, readOnly); - } - - private void SetupProperties(string name, string descript, string type, string dflt, bool readOnly) { - Name = name; - Type = type; - Description = descript; - Default = dflt; - ReadOnly = readOnly; - } - } - - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] - public class TouchPortalSettingsContainerAttribute : Attribute { } -} diff --git a/MSFSTouchPortalPlugin/Attributes/TouchPortalSettingsContainerAttribute.cs b/MSFSTouchPortalPlugin/Attributes/TouchPortalSettingsContainerAttribute.cs new file mode 100644 index 0000000..b7ce530 --- /dev/null +++ b/MSFSTouchPortalPlugin/Attributes/TouchPortalSettingsContainerAttribute.cs @@ -0,0 +1,7 @@ +using System; + +namespace MSFSTouchPortalPlugin.Attributes +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] + public class TouchPortalSettingsContainerAttribute : Attribute { } +} diff --git a/MSFSTouchPortalPlugin/Attributes/TouchPortalStateAttribute.cs b/MSFSTouchPortalPlugin/Attributes/TouchPortalStateAttribute.cs deleted file mode 100644 index 5ca22c2..0000000 --- a/MSFSTouchPortalPlugin/Attributes/TouchPortalStateAttribute.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; - -namespace MSFSTouchPortalPlugin.Attributes -{ - // Currently unused, preserved for posterity (or possible future use). - - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] - public class TouchPortalStateAttribute : Attribute - { - public string Id { get; set; } - public string Type { get; set; } - public string Description { get; set; } - public string Default { get; set; } - - public TouchPortalStateAttribute(string id, string type, string description, string defaultValue) { - SetupProperties(id, type, description, defaultValue); - } - - private void SetupProperties(string id, string type, string description, string defaultValue) { - Id = id; - Type = type; - Description = description; - Default = defaultValue; - } - } -} diff --git a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs index 2203411..5637b8d 100644 --- a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs +++ b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs @@ -16,27 +16,28 @@ internal static class PluginMapping { [TouchPortalCategory(Groups.Plugin)] [TouchPortalSettingsContainer] - public static class Settings { - [TouchPortalSetting( + public static class Settings + { + public static readonly PluginSetting ConnectSimOnStartup = new PluginSetting("ConnectSimOnStartup", "1", DataType.Switch) { Name = "Connect To Flight Sim on Startup (0/1)", Description = "Set to 1 to automatically attempt connection to flight simulator upon Touch Portal startup. Set to 0 to only connect manually via the provided Action.", - Type = "number", Default = "1", MinValue = 0, MaxValue = 1 - )] - public static readonly PluginSetting ConnectSimOnStartup = new PluginSetting("ConnectSimOnStartup", 0, 1); + }; - [TouchPortalSetting( - Name = "Held Action Repeat Rate (ms)", - Description = "Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Action Repeat Interval' action.", - Type = "number", Default = "450", MinValue = 50, MaxValue = int.MaxValue, ReadOnly = true, - StateId = "ActionRepeatInterval" - )] [TouchPortalAction("ActionRepeatInterval", "Action Repeat Interval", "MSFS", "Held Action Repeat Rate (ms)", "Repeat Interval: {0} to/by: {1} ms", true)] [TouchPortalActionChoice(new[] { "Set", "Increment", "Decrement" })] [TouchPortalActionText("450", 50, int.MaxValue)] [TouchPortalActionMapping("ActionRepeatIntervalSet", "Set")] [TouchPortalActionMapping("ActionRepeatIntervalInc", "Increment")] [TouchPortalActionMapping("ActionRepeatIntervalDec", "Decrement")] - public static readonly PluginSetting ActionRepeatInterval = new PluginSetting("ActionRepeatInterval", 50, int.MaxValue); + public static readonly PluginSetting ActionRepeatInterval = new PluginSetting("ActionRepeatInterval", DataType.Number) { + Name = "Held Action Repeat Rate (ms)", + Description = "Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Action Repeat Interval' action.", + Default = "450", + MinValue = 50, + MaxValue = int.MaxValue, + ReadOnly = true, + TouchPortalStateId = "ActionRepeatInterval" + }; } // IDs for handling internal events diff --git a/MSFSTouchPortalPlugin/Services/ReflectionService.cs b/MSFSTouchPortalPlugin/Services/ReflectionService.cs index 912a9e5..d4723c7 100644 --- a/MSFSTouchPortalPlugin/Services/ReflectionService.cs +++ b/MSFSTouchPortalPlugin/Services/ReflectionService.cs @@ -95,23 +95,17 @@ public Dictionary GetActionEvents() { public Dictionary GetSettings() { Dictionary returnDict = new(); - var setContainers = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsClass && t.GetCustomAttribute() != null).ToList(); - setContainers.ForEach(setCtr => { - var fieldList = setCtr.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public).ToList(); - fieldList.ForEach(settingField => { - var settingAttr = settingField.GetCustomAttribute(); - var settingsObj = (PluginSetting)settingField.GetValue(null); - if (settingAttr != null && settingsObj != null) { - if (settingsObj.Name == null) - settingsObj.Name = settingAttr.Name; - if (settingsObj.Default == null) - settingsObj.Default = settingAttr.Default; - if (settingsObj.TouchPortalStateId == null && !string.IsNullOrWhiteSpace(settingAttr.StateId)) - settingsObj.TouchPortalStateId = $"{rootName}.Plugin.State.{settingAttr.StateId}"; - returnDict.TryAdd(settingAttr.Name, settingsObj); + var setContainers = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsClass && t.GetCustomAttribute() != null); + foreach (Type setCtr in setContainers) { + var settingFields = setCtr.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); + foreach (FieldInfo field in settingFields) { + if (field.FieldType == typeof(PluginSetting) && ((PluginSetting)field.GetValue(null) is var setting && setting != null)) { + if (!string.IsNullOrWhiteSpace(setting.TouchPortalStateId)) + setting.TouchPortalStateId = $"{rootName}.Plugin.State.{setting.TouchPortalStateId}"; + returnDict.TryAdd(setting.Name, setting); } - }); - }); + } + } return returnDict; } diff --git a/MSFSTouchPortalPlugin/Types/PluginSetting.cs b/MSFSTouchPortalPlugin/Types/PluginSetting.cs index 903f28c..41e4cca 100644 --- a/MSFSTouchPortalPlugin/Types/PluginSetting.cs +++ b/MSFSTouchPortalPlugin/Types/PluginSetting.cs @@ -7,14 +7,20 @@ public class PluginSetting { public string SettingID { get; set; } public string Name { get; set; } = null; + public string Description { get; set; } // for generated docs public string Default { get; set; } = null; - public DataType ValueType { get; set; } = DataType.Text; public int MaxLength { get; set; } = int.MinValue; public double MinValue { get; set; } = double.NaN; public double MaxValue { get; set; } = double.NaN; - public string TouchPortalStateId { get; set; } = null; + + public bool ReadOnly { get; set; } = false; // for TP definition (maybe also future use) + public bool IsPassword { get; set; } = false; // for TP definition + public string TouchPortalStateId { get; set; } = null; // Id of corresponding TP State, if any + public string TouchPortalType { get; private set; } = "text"; // TP definition type equivalent, "text" or "number". private dynamic _value = null; + private DataType _type = DataType.Text; + public dynamic Value { get { @@ -27,13 +33,33 @@ public dynamic Value } } - public void SetValueFromString(string value) { - if (ValueType == DataType.Number) { - if (double.TryParse(value, out var numVal)) - Value = numVal; + public DataType ValueType { + get => _type; + set { + _type = value; + TouchPortalType = _type == DataType.Text ? "text" : "number"; // treat Switch as numeric and Choice is not supported + if (_type == DataType.Switch) { + MinValue = 0; + MaxValue = 1; + } } - else { - Value = value; + } + + public void SetValueFromString(string value) { + switch (ValueType) { + case DataType.Number: { + if (double.TryParse(value, out var numVal)) + Value = numVal; + break; + } + + case DataType.Switch: + Value = (bool)new BooleanString(value); + break; + + default: + Value = value; + break; } } @@ -47,12 +73,15 @@ public void SetValueDynamic(dynamic value) { realVal = Math.Min(realVal, MaxValue); _value = realVal; } - // string + // string or "switch" bool else { string strVal = Convert.ToString(value); if (MaxLength > 0 && !string.IsNullOrEmpty(strVal)) strVal = strVal[..Math.Min(strVal.Length, MaxLength)]; - _value = strVal; + if (ValueType == DataType.Switch) + _value = (bool)new BooleanString(strVal); + else + _value = strVal; } } catch (Exception e) { @@ -60,7 +89,8 @@ public void SetValueDynamic(dynamic value) { } } - public int ValueAsInt() => Value == null || ValueType != DataType.Number ? 0 : (int)Value; + public int ValueAsInt() => Value == null || ValueType == DataType.Text ? 0 : (int)Value; + public bool ValueAsBool() => Value == null ? false : ValueType == DataType.Text ? new BooleanString(ValueAsStr()) : ValueType == DataType.Number ? ValueAsInt() != 0 : (bool)Value; public double ValueAsDbl() => Value == null ? double.NaN : (double)Value; public string ValueAsStr() => Value == null ? string.Empty : Value.ToString(); @@ -75,10 +105,10 @@ private void SetProperties(string id, string name, string defaultValue, DataType ValueType = type; Name = name; Default = defaultValue; - ValueType = type; MinValue = min; MaxValue = max; MaxLength = maxLen; + ValueType = type; // after min/max if (Value == null && defaultValue != null) SetValueFromString(defaultValue); } From 82dee9d57971c2cde76ce174ca0528ed2e236bc6 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sat, 2 Apr 2022 01:46:45 -0400 Subject: [PATCH 51/93] Refactor and clean up task handling spaghetti in PluginService and SimConnectService: * Adjust how the PluginService starts up and shuts down in StartAsync() and StopAsync() (no application start/stop hooks); * PluginService sim connection monitor task optimized to do nothing while connected or not trying to connect; * SimConnectService now handles its own message receive waiting task; * Fixes possible exception on SimConnectService disconnect when messages are pending (due to new "push" message scheme); * Do not process sim actions if not connected. * Remove PluginService IDisposable implementation (not needed), and code cosmetics. --- .../Interfaces/IPluginService.cs | 1 - .../Interfaces/ISimConnectService.cs | 3 - .../Services/PluginService.cs | 224 +++++++++--------- .../Services/SimConnectService.cs | 48 ++-- 4 files changed, 145 insertions(+), 131 deletions(-) diff --git a/MSFSTouchPortalPlugin/Interfaces/IPluginService.cs b/MSFSTouchPortalPlugin/Interfaces/IPluginService.cs index 0fed075..496edee 100644 --- a/MSFSTouchPortalPlugin/Interfaces/IPluginService.cs +++ b/MSFSTouchPortalPlugin/Interfaces/IPluginService.cs @@ -7,7 +7,6 @@ namespace MSFSTouchPortalPlugin.Interfaces { /// Handles communication with the Touch Portal /// internal interface IPluginService : IHostedService { - Task RunPluginServices(CancellationToken simConnectCancelToken); new Task StartAsync(CancellationToken cancellationToken); new Task StopAsync(CancellationToken cancellationToken); } diff --git a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs index fcfabf7..b3368e4 100644 --- a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs +++ b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs @@ -2,8 +2,6 @@ using MSFSTouchPortalPlugin.Types; using System; using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] namespace MSFSTouchPortalPlugin.Interfaces @@ -27,6 +25,5 @@ internal interface ISimConnectService { bool RegisterToSimConnect(SimVarItem simVar); bool RequestDataOnSimObjectType(SimVarItem simVar); bool TransmitClientEvent(Groups group, Enum eventId, uint data); - Task WaitForMessage(CancellationToken cancellationToken); } } diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index d508353..631d08b 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -20,28 +20,32 @@ namespace MSFSTouchPortalPlugin.Services { /// - internal class PluginService : IPluginService, IDisposable, ITouchPortalEventHandler { - private CancellationToken _cancellationToken; - private CancellationTokenSource _simConnectCancellationTokenSource; + internal class PluginService : IPluginService, ITouchPortalEventHandler + { + public string PluginId => "MSFSTouchPortalPlugin"; // for ITouchPortalEventHandler private readonly IHostApplicationLifetime _hostApplicationLifetime; private readonly ILogger _logger; private readonly ITouchPortalClient _client; private readonly ISimConnectService _simConnectService; private readonly IReflectionService _reflectionService; - private static readonly System.Data.DataTable _expressionEvaluator = new(); // used to evaluate basic math in action data + + private CancellationToken _cancellationToken; + private CancellationTokenSource _simTasksCTS; + private CancellationToken _simTasksCancelToken; + private Task _timerEventsTask; + private readonly ManualResetEventSlim _simConnectionRequest = new(false); private bool autoReconnectSimConnect = false; private bool _quitting; - public string PluginId => "MSFSTouchPortalPlugin"; - private Dictionary actionsDictionary = new(); private Dictionary statesDictionary = new(); private Dictionary pluginSettingsDictionary = new(); private readonly ConcurrentBag customIntervalStates = new(); - private readonly ConcurrentDictionary repeatingActionTimers = new(); + private static readonly System.Data.DataTable _expressionEvaluator = new(); // used to evaluate basic math in action data + /// /// Constructor /// @@ -56,61 +60,21 @@ public PluginService(IHostApplicationLifetime hostApplicationLifetime, ILogger

    - /// Runs the plugin services - ///

    - /// The Cancellation Token - public async Task RunPluginServices(CancellationToken simConnectCancelToken) { - // Run Data Polling and repeating actions timers - var runTimersTask = RunTimedEventsTask(simConnectCancelToken); - - await Task.WhenAll(new Task[] { - runTimersTask, - // Run Listen and pairing - _simConnectService.WaitForMessage(simConnectCancelToken) - }).ConfigureAwait(false); - } - - /// - /// Async task for running various timed SimConnect events on one thread. - /// This is primarily used for data polling and repeating (held) actions. - /// - /// The Cancellation Token - private async Task RunTimedEventsTask(CancellationToken cancellationToken) { - while (_simConnectService.IsConnected() && !cancellationToken.IsCancellationRequested) { - foreach (Timer tim in repeatingActionTimers.Values) - tim.Tick(); - CheckPendingRequests(); - await Task.Delay(25, cancellationToken); - } - } + #region Startup, Shutdown and Processing Tasks ////////////////// /// /// Starts the plugin service /// /// The cancellation token public Task StartAsync(CancellationToken cancellationToken) { - _cancellationToken = cancellationToken; + _logger.LogDebug("Starting up..."); - _hostApplicationLifetime.ApplicationStarted.Register(() => { - if (!Initialize()) { - _hostApplicationLifetime.StopApplication(); - return; - } + _cancellationToken = cancellationToken; - Task.WhenAll(TryConnect()); - }); - - _hostApplicationLifetime.ApplicationStopping.Register(() => { - // Disconnect from SimConnect - _quitting = true; - autoReconnectSimConnect = false; - _simConnectService.Disconnect(); - if (_client?.IsConnected ?? false) { - try { _client.Close(); } - catch (Exception) { /* ignore */ } - } - }); + if (!Initialize()) { + _hostApplicationLifetime.StopApplication(); + return Task.CompletedTask; + } // register ctrl-c exit handler Console.CancelKeyPress += (_, _) => { @@ -119,7 +83,9 @@ public Task StartAsync(CancellationToken cancellationToken) { //Environment.Exit(0); }; - return Task.CompletedTask; + if (autoReconnectSimConnect) + _simConnectionRequest.Set(); // enable connection attempts + return Task.WhenAll(SimConnectionMonitor()); } /// @@ -127,31 +93,20 @@ public Task StartAsync(CancellationToken cancellationToken) { /// /// The cancellation token public Task StopAsync(CancellationToken cancellationToken) { - return Task.CompletedTask; - } - - #region IDisposable Support - private bool disposedValue; // To detect redundant calls - - protected virtual void Dispose(bool disposing) { - if (!disposedValue) { - if (disposing) { - // Dispose managed state (managed objects). - _simConnectCancellationTokenSource?.Dispose(); - } - - disposedValue = true; + if (_quitting) + return Task.CompletedTask; + // Shut down + _quitting = true; + _logger.LogDebug("Shutting down..."); + _simConnectionRequest.Reset(); // just in case + _simConnectService?.Disconnect(); + if (_client?.IsConnected ?? false) { + try { _client.Close(); } + catch (Exception) { /* ignore */ } } + return Task.CompletedTask; } - // This code added to correctly implement the disposable pattern. - public void Dispose() { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); - GC.SuppressFinalize(this); - } - #endregion - /// /// Initialized the Touch Portal Message Processor /// @@ -170,10 +125,53 @@ private bool Initialize() { return true; } - #region SimConnect Events + private async Task SimConnectionMonitor() { + _logger.LogDebug("SimConnectionMonitor task started."); + try { + while (!_cancellationToken.IsCancellationRequested) { + _simConnectionRequest.Wait(_cancellationToken); + if (!_simConnectService.IsConnected() && !_simConnectService.Connect()) + await Task.Delay(10000, _cancellationToken); // delay 10s on connection error + } + } + catch (OperationCanceledException) { /* ignore but exit */ } + catch (ObjectDisposedException) { /* ignore but exit */ } + catch (Exception e) { + _logger.LogError("Exception in SimConnectionMonitor task, cannot continue.", e); + } + _logger.LogDebug("SimConnectionMonitor task stopped."); + } + + /// + /// Task for running various timed SimConnect events on one thread. + /// This is primarily used for data polling and repeating (held) actions. + /// + private async Task PluginEventsTask() { + _logger.LogDebug("PluginEventsTask task started."); + try { + while (_simConnectService.IsConnected() && !_simTasksCancelToken.IsCancellationRequested) { + foreach (Timer tim in repeatingActionTimers.Values) + tim.Tick(); + CheckPendingRequests(); + await Task.Delay(25, _simTasksCancelToken); + } + } + catch (OperationCanceledException) { /* ignore but exit */ } + catch (ObjectDisposedException) { /* ignore but exit */ } + catch (Exception e) { + _logger.LogError("Exception in PluginEventsTask task, cannot continue.", e); + } + _logger.LogDebug("PluginEventsTask task stopped."); + } + + #endregion Startup, Shutdown and Processing Tasks + + #region SimConnect Events ///////////////////////////////////// private void SimConnectEvent_OnConnect() { - _simConnectCancellationTokenSource = new CancellationTokenSource(); + _simConnectionRequest.Reset(); + _simTasksCTS = new CancellationTokenSource(); + _simTasksCancelToken = _simTasksCTS.Token; UpdateSimConnectState(); @@ -188,7 +186,9 @@ private void SimConnectEvent_OnConnect() { SetupSimVars(); - Task.WhenAll(RunPluginServices(_simConnectCancellationTokenSource.Token)); + // start checking timer events + _timerEventsTask = Task.Run(PluginEventsTask); + _timerEventsTask.ConfigureAwait(false); // needed? } private void SimConnectEvent_OnDataUpdateEvent(Definition def, Definition req, object data) { @@ -204,12 +204,26 @@ private void SimConnectEvent_OnDataUpdateEvent(Definition def, Definition req, o } private void SimConnectEvent_OnDisconnect() { - _simConnectCancellationTokenSource?.Cancel(); + _simTasksCTS?.Cancel(); + if (_timerEventsTask.Status == TaskStatus.Running && !_timerEventsTask.Wait(5000)) + _logger.LogWarning("Timed events task timed out while stopping."); + try { _timerEventsTask.Dispose(); } + catch { /* ignore in case it hung */ } + ClearRepeatingActions(); UpdateSimConnectState(); + + _timerEventsTask = null; + _simTasksCTS?.Dispose(); + _simTasksCTS = null; + + if (autoReconnectSimConnect && !_quitting) + _simConnectionRequest.Set(); // re-enable connection attempts } - #endregion + #endregion SimConnect Events + + #region Plugin Events and Handlers ///////////////////////////////////// private void SetupEventLists() { actionsDictionary = _reflectionService.GetActionEvents(); @@ -225,7 +239,7 @@ private void SetupSimVars() { } statesDictionary = configStates.ToDictionary(s => s.Def, s => s); customIntervalStates.Clear(); - // Register SimVars + // Register SimVars, but first clear out any old ones. _simConnectService.ClearAllDataDefinitions(); foreach (var simVar in statesDictionary.Values) { if (simVar.NeedsScheduledRequest) @@ -234,29 +248,6 @@ private void SetupSimVars() { } } - private Task TryConnect() { - short i = 0; - while (!_cancellationToken.IsCancellationRequested) { - if (autoReconnectSimConnect && !_simConnectService.IsConnected()) { - if (i == 0) { - if (!_simConnectService.Connect()) - i = 10; // delay reconnect attempt on error - } - else { - --i; - } - } - else if (i != 0) { - i = 0; - } - - // SimConnect is typically available even before loading into a flight. This should connect and be ready by the time a flight is started. - Thread.Sleep(1000); - } - - return Task.CompletedTask; - } - private void CheckPendingRequests() { foreach (var def in customIntervalStates) { // Check if a value update is required based on the SimVar's internal tracking mechanism. @@ -277,7 +268,7 @@ private void ProcessEvent(ActionEvent actionEvent) { if (action.InternalEvent) ProcessInternalEvent(action, eventId, in dataArry); - else + else if (_simConnectService.IsConnected()) ProcessSimEvent(action, eventId, in dataArry); } @@ -288,17 +279,24 @@ private void ProcessInternalEvent(ActionEventType action, Enum eventId, in strin switch (pluginEventId) { case Plugin.ToggleConnection: autoReconnectSimConnect = !autoReconnectSimConnect; - if (_simConnectService.IsConnected()) + if (_simConnectService.IsConnected()) { + _simConnectionRequest.Reset(); _simConnectService.Disconnect(); - else + } + else { + _simConnectionRequest.Set(); UpdateSimConnectState(); + } break; case Plugin.Connect: autoReconnectSimConnect = true; + if (!_simConnectService.IsConnected()) + _simConnectionRequest.Set(); UpdateSimConnectState(); break; case Plugin.Disconnect: autoReconnectSimConnect = false; + _simConnectionRequest.Reset(); if (_simConnectService.IsConnected()) _simConnectService.Disconnect(); else @@ -385,11 +383,13 @@ private void ProcessPluginSettings(IReadOnlyCollection _addedDefinitions = new(); @@ -76,16 +77,18 @@ public bool Connect() { #endif //_simConnect.Text(SIMCONNECT_TEXT_TYPE.PRINT_BLACK, 5, Events.StartupMessage, "TouchPortal Connected"); // not currently supported in MSFS SDK - // Invoke Handler - OnConnect?.Invoke(); + _messageWaitTask = Task.Run(ReceiveMessages); } catch (COMException ex) { _connected = false; _logger.LogInformation("Connection to Sim failed: {exception}", ex.Message); + return false; } _connecting = false; - return _connected; + // Invoke Handler + OnConnect?.Invoke(); + return true; } public void Disconnect() { @@ -93,34 +96,46 @@ public void Disconnect() { return; _connected = false; + _scReady.Set(); // trigger message wait task to exit + var sw = System.Diagnostics.Stopwatch.StartNew(); + while (_messageWaitTask.Status == TaskStatus.Running && sw.ElapsedMilliseconds <= 5000) { + Thread.Sleep(2); + _scReady.Set(); + } + if (sw.ElapsedMilliseconds > 5000) + _logger.LogWarning("Message wait task timed out while stopping."); + try { _messageWaitTask.Dispose(); } + catch { /* ignore in case it hung */ } // Dispose serves the same purpose as SimConnect_Close() try { _simConnect?.Dispose(); - _simConnect = null; _logger.LogInformation("SimConnect Disconnected"); } catch (Exception e) { _logger.LogWarning(e, "Exception while trying to dispose SimConnect client."); } + _simConnect = null; + _messageWaitTask = null; _addedDefinitions.Clear(); // Invoke Handler OnDisconnect?.Invoke(); } - public Task WaitForMessage(CancellationToken cancellationToken) { - while (_connected && !cancellationToken.IsCancellationRequested) { - try { - if (_scReady.WaitOne(1000)) + public void ReceiveMessages() { + _logger.LogDebug("ReceiveMessages task started."); + try { + while (_connected) { + if (_scReady.WaitOne(5000) && _connected) _simConnect?.ReceiveMessage(); } - catch (Exception e) { - _logger.LogError(e, $"WaitForMessage() failed, disconnecting."); - Disconnect(); - } } - - return Task.CompletedTask; + catch (ObjectDisposedException) { /* ignore but exit */ } + catch (Exception e) { + _logger.LogError(e, "ReceiveMessages task exception, disconnecting."); + Task.Run(Disconnect); // async to avoid deadlock + } + _logger.LogDebug("ReceiveMessages task stopped."); } public bool MapClientEventToSimEvent(Enum eventId, string eventName) { @@ -282,7 +297,10 @@ protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { // Dispose managed state (managed objects). - _scReady.Dispose(); + if (_connected) + Disconnect(); + _scReady?.Dispose(); + _messageWaitTask?.Dispose(); } disposedValue = true; From b696c1f4b323ba63cb55d2dc51323de2f871d85a Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sat, 2 Apr 2022 04:16:20 -0400 Subject: [PATCH 52/93] Refactor PluginConfig to a hosted singleton instance with own logging. --- .../GenerateDoc.cs | 19 ++----- .../GenerateEntry.cs | 19 ++----- MSFSTouchPortalPlugin-Generator/Program.cs | 2 + .../Configuration/PluginConfig.cs | 49 +++++++++---------- MSFSTouchPortalPlugin/Program.cs | 3 +- .../Services/PluginService.cs | 13 +++-- 6 files changed, 42 insertions(+), 63 deletions(-) diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index 435bb19..f3cfa0f 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -23,11 +23,13 @@ internal class GenerateDoc : IGenerateDoc { private readonly ILogger _logger; private readonly IOptions _options; private readonly IReflectionService _reflectionSvc; + private readonly PluginConfig _pluginConfig; - public GenerateDoc(ILogger logger, IOptions options, IReflectionService reflectionSvc) { + public GenerateDoc(ILogger logger, IOptions options, IReflectionService reflectionSvc, PluginConfig pluginConfig) { _logger = logger; _options = options; _reflectionSvc = reflectionSvc ?? throw new ArgumentNullException(nameof(reflectionSvc)); + _pluginConfig = pluginConfig ?? throw new ArgumentNullException(nameof(pluginConfig)); } public void Generate() { @@ -64,12 +66,7 @@ private DocBase CreateModel() { // read default states config // TODO: Allow configuration of which state config file(s) to read. - var pc = PluginConfig.Instance; - var configStates = pc.LoadSimVarItems(false); - if (pc.HaveErrors) { - foreach (var e in pc.ErrorsList) - _logger.LogError(e, "Configuration reader error:"); - } + SimVarItem[] simVars = _pluginConfig.LoadSimVarItems(false).Concat(_pluginConfig.LoadPluginStates()).ToArray(); // Get all classes with the TouchPortalCategory var classList = assemblyList.Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalCategoryAttribute))).OrderBy(o => o.Name); @@ -135,13 +132,7 @@ private DocBase CreateModel() { if (newCat.States.Any()) continue; // skip if already added for this category - System.Collections.Generic.IEnumerable categoryStates; - // Plugin (non SimConnect) states are stored in a separate config file. - if (catId == Groups.Plugin) - categoryStates = pc.LoadPluginStates(); - else - categoryStates = configStates.Where(s => s.CategoryId == catId); - + var categoryStates = simVars.Where(s => s.CategoryId == catId); foreach (SimVarItem state in categoryStates) { var newState = new DocState { Id = state.TouchPortalStateId, diff --git a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs index b3d2f6d..41482d4 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs @@ -25,11 +25,13 @@ internal class GenerateEntry : IGenerateEntry { private readonly ILogger _logger; private readonly IOptions _options; private readonly IReflectionService _reflectionSvc; + private readonly PluginConfig _pluginConfig; - public GenerateEntry(ILogger logger, IOptions options, IReflectionService reflectionSvc) { + public GenerateEntry(ILogger logger, IOptions options, IReflectionService reflectionSvc, PluginConfig pluginConfig) { _logger = logger; _options = options; _reflectionSvc = reflectionSvc ?? throw new ArgumentNullException(nameof(reflectionSvc)); + _pluginConfig = pluginConfig ?? throw new ArgumentNullException(nameof(pluginConfig)); JsonConvert.DefaultSettings = () => new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() @@ -50,12 +52,7 @@ public void Generate() { // read default states config // TODO: Allow configuration of which state config file(s) to read. - var pc = PluginConfig.Instance; - var configStates = pc.LoadSimVarItems(false); - if (pc.HaveErrors) { - foreach (var e in pc.ErrorsList) - _logger.LogError(e, "Configuration reader error:"); - } + SimVarItem[] simVars = _pluginConfig.LoadSimVarItems(false).Concat(_pluginConfig.LoadPluginStates()).ToArray(); var q = assembly.GetTypes().ToList(); @@ -140,13 +137,7 @@ public void Generate() { if (category.States.Any()) continue; // skip if already added for this category - System.Collections.Generic.IEnumerable categoryStates; - // Plugin (non SimConnect) states are stored in a separate config file. - if (catId == Groups.Plugin) - categoryStates = pc.LoadPluginStates(); - else - categoryStates = configStates.Where(s => s.CategoryId == catId); - + var categoryStates = simVars.Where(s => s.CategoryId == catId); foreach (SimVarItem state in categoryStates) { var newState = new TouchPortalState { Id = state.TouchPortalStateId, diff --git a/MSFSTouchPortalPlugin-Generator/Program.cs b/MSFSTouchPortalPlugin-Generator/Program.cs index cc643e6..1dfed5a 100644 --- a/MSFSTouchPortalPlugin-Generator/Program.cs +++ b/MSFSTouchPortalPlugin-Generator/Program.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using MSFSTouchPortalPlugin.Configuration; using MSFSTouchPortalPlugin.Interfaces; using MSFSTouchPortalPlugin.Services; using MSFSTouchPortalPlugin_Generator.Configuration; @@ -23,6 +24,7 @@ await Host.CreateDefaultBuilder(args).ConfigureServices((context, services) => { }) .AddHostedService() .AddSingleton() + .AddSingleton(typeof(PluginConfig)) .AddSingleton() .AddSingleton(); }).RunConsoleAsync(); diff --git a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs index e8e8bd4..0559646 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs +++ b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs @@ -6,6 +6,7 @@ using System.IO; using System.Text; using System.Runtime.Serialization; +using Microsoft.Extensions.Logging; //using SharpConfig; namespace MSFSTouchPortalPlugin.Configuration @@ -14,14 +15,6 @@ namespace MSFSTouchPortalPlugin.Configuration internal class PluginConfig { - public static PluginConfig Instance { - get { - if (_instance == null) - _instance = new PluginConfig(); - return _instance; - } - } - /// /// RootName is used as the basis for the user folder name and TP State ID generation. /// @@ -30,17 +23,15 @@ public static PluginConfig Instance { public static string StatesConfigFile { get; set; } = "States.ini"; public static string PluginStatesConfigFile { get; set; } = "PluginStates.ini"; - public static string AppConfigFolder { get; set; } = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "Configuration"); + public static string AppRootFolder { get; set; } = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + public static string AppConfigFolder { get; set; } = Path.Combine(AppRootFolder, "Configuration"); public static string UserConfigFolder { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), RootName); - public IReadOnlyCollection ErrorsList => _errorsList; - public bool HaveErrors => _errorsList.Any(); + private readonly ILogger _logger; - private static PluginConfig _instance; - private readonly List _errorsList = new(); + public PluginConfig(ILogger logger) { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - private PluginConfig() - { // set SC writing options SharpConfig.Configuration.SpaceBetweenEquals = true; SharpConfig.Configuration.AlwaysQuoteStringValues = true; // custom SharpConfig v3.2.9.2-mp feature @@ -50,10 +41,9 @@ public IReadOnlyCollection LoadSimVarItems(bool isUserConfig = true, List ret = new(); if (filename == default) filename = StatesConfigFile; - _errorsList.Clear(); string filepath = Path.Combine(isUserConfig ? UserConfigFolder : AppConfigFolder, filename); if (!File.Exists(filepath)) { - _errorsList.Add(new FileNotFoundException("Configuration file not found.", filepath)); + _logger.LogWarning($"Cannot load SimVar states, file '{filename}' not found at '{filepath}'"); return ret; } SharpConfig.Configuration cfg; @@ -61,7 +51,7 @@ public IReadOnlyCollection LoadSimVarItems(bool isUserConfig = true, cfg = SharpConfig.Configuration.LoadFromFile(filepath, Encoding.UTF8); } catch (Exception e) { - _errorsList.Add(e); + _logger.LogWarning(e, $"Configuration LoadFromFile error in '{filepath}':"); return ret; } foreach (SharpConfig.Section item in cfg) { @@ -72,13 +62,17 @@ public IReadOnlyCollection LoadSimVarItems(bool isUserConfig = true, simVar = item.ToObject(); } catch (Exception e) { - _errorsList.Add(e); + _logger.LogWarning(e, $"Deserialize exception for section '{item}':"); + continue; + } + if (simVar == null) { + _logger.LogWarning($"Produced SimVar is null from section '{item}':"); continue; } simVar.Id = item.Name; // check unique if (ret.FirstOrDefault(s => s.Id == simVar.Id) != null) { - _errorsList.Add(new DuplicateIdException($"Duplicate SimVar ID found for '{simVar.Id}'")); + _logger.LogWarning($"Duplicate SimVar ID found for '{simVar.Id}', skipping."); continue; } simVar.TouchPortalStateId = $"{RootName}.{simVar.CategoryId}.State.{simVar.Id}"; @@ -94,9 +88,10 @@ public IReadOnlyCollection LoadPluginStates() public bool SaveSimVarItems(IReadOnlyCollection items, bool isUserConfig = true, string filename = default) { var cfg = new SharpConfig.Configuration(); Groups lastCatId = default; - _errorsList.Clear(); foreach (SimVarItem item in items) { + if (item == null) + continue; try { var sect = cfg.Add(item.Id); if (item.CategoryId != lastCatId) { @@ -122,25 +117,25 @@ public bool SaveSimVarItems(IReadOnlyCollection items, bool isUserCo sect.Add("DeltaEpsilon", item.DeltaEpsilon); } catch (Exception e) { - _errorsList.Add(e); + _logger.LogWarning(e, $"Serialize exception for {item.ToDebugString()}:"); } } if (filename == default) filename = StatesConfigFile; - SaveToFile(cfg, isUserConfig ? UserConfigFolder : AppConfigFolder, filename); - - return HaveErrors; + return SaveToFile(cfg, isUserConfig ? UserConfigFolder : AppConfigFolder, filename); } - private void SaveToFile(SharpConfig.Configuration cfg, string folder, string filename) { + private bool SaveToFile(SharpConfig.Configuration cfg, string folder, string filename) { try { Directory.CreateDirectory(folder); cfg.SaveToFile(Path.Combine(folder, filename), Encoding.UTF8); + return true; } catch (Exception e) { - _errorsList.Add(e); + _logger.LogWarning(e, $"Error trying to write config file '{filename}' to folder '{folder}'"); } + return false; } } diff --git a/MSFSTouchPortalPlugin/Program.cs b/MSFSTouchPortalPlugin/Program.cs index b60d7ca..abe1039 100644 --- a/MSFSTouchPortalPlugin/Program.cs +++ b/MSFSTouchPortalPlugin/Program.cs @@ -40,7 +40,7 @@ private static async Task Main(string[] args) { try { await Host.CreateDefaultBuilder(args) - .ConfigureLogging((hostContext, loggingBuilder) => { + .ConfigureLogging((hostContext, loggingBuilder) => { loggingBuilder .ClearProviders() .AddSerilog(logger: new LoggerConfiguration().ReadFrom.Configuration(configurationRoot).CreateLogger(), dispose: true); @@ -51,6 +51,7 @@ await Host.CreateDefaultBuilder(args) .AddHostedService() .AddSingleton() .AddSingleton() + .AddSingleton(typeof(PluginConfig)) .AddTouchPortalSdk(configurationRoot); }) .RunConsoleAsync(); diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 631d08b..dcfd709 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -29,6 +29,7 @@ internal class PluginService : IPluginService, ITouchPortalEventHandler private readonly ITouchPortalClient _client; private readonly ISimConnectService _simConnectService; private readonly IReflectionService _reflectionService; + private readonly PluginConfig _pluginConfig; private CancellationToken _cancellationToken; private CancellationTokenSource _simTasksCTS; @@ -51,13 +52,16 @@ internal class PluginService : IPluginService, ITouchPortalEventHandler ///
/// Message Processor Object public PluginService(IHostApplicationLifetime hostApplicationLifetime, ILogger logger, - ITouchPortalClientFactory clientFactory, ISimConnectService simConnectService, IReflectionService reflectionService) { + ITouchPortalClientFactory clientFactory, ISimConnectService simConnectService, IReflectionService reflectionService, + PluginConfig pluginConfig) + { _hostApplicationLifetime = hostApplicationLifetime ?? throw new ArgumentNullException(nameof(hostApplicationLifetime)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _simConnectService = simConnectService ?? throw new ArgumentNullException(nameof(simConnectService)); _reflectionService = reflectionService ?? throw new ArgumentNullException(nameof(reflectionService)); _client = clientFactory?.Create(this) ?? throw new ArgumentNullException(nameof(clientFactory)); + _pluginConfig = pluginConfig ?? throw new ArgumentNullException(nameof(pluginConfig)); } #region Startup, Shutdown and Processing Tasks ////////////////// @@ -231,12 +235,7 @@ private void SetupEventLists() { } private void SetupSimVars() { - var pc = PluginConfig.Instance; - var configStates = pc.LoadSimVarItems(false); - if (pc.HaveErrors) { - foreach (var e in pc.ErrorsList) - _logger.LogWarning(e, "Configuration reader error:"); - } + var configStates = _pluginConfig.LoadSimVarItems(false); statesDictionary = configStates.ToDictionary(s => s.Def, s => s); customIntervalStates.Clear(); // Register SimVars, but first clear out any old ones. From 658545da4b156414e457b3069f8f8bb362e9373f Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sat, 2 Apr 2022 05:03:11 -0400 Subject: [PATCH 53/93] [logging] Adjust logging format to include ms, implement template for log file, allow setting log levels from env. vars, and enable debug logging level for debug builds. --- MSFSTouchPortalPlugin/Program.cs | 7 ++++++- MSFSTouchPortalPlugin/appsettings.json | 23 ++++++++++++----------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/MSFSTouchPortalPlugin/Program.cs b/MSFSTouchPortalPlugin/Program.cs index abe1039..537d6a5 100644 --- a/MSFSTouchPortalPlugin/Program.cs +++ b/MSFSTouchPortalPlugin/Program.cs @@ -20,13 +20,18 @@ namespace MSFSTouchPortalPlugin { public static class Program { private static async Task Main(string[] args) { // Logger +#if DEBUG + // always debug logging in debug build + Environment.SetEnvironmentVariable("Serilog__MinimumLevel__Override__MSFSTouchPortalPlugin", "Debug"); +#endif var logFactory = new LoggerFactory(); var logger = logFactory.CreateLogger("Program"); //Build configuration: var configurationRoot = new ConfigurationBuilder() .SetBasePath(Directory.GetParent(AppContext.BaseDirectory).FullName) - .AddJsonFile("appsettings.json", false, true) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables() // inject env. vars .Build(); // Ensure only one running instance diff --git a/MSFSTouchPortalPlugin/appsettings.json b/MSFSTouchPortalPlugin/appsettings.json index 8c390bc..c42a91d 100644 --- a/MSFSTouchPortalPlugin/appsettings.json +++ b/MSFSTouchPortalPlugin/appsettings.json @@ -1,5 +1,13 @@ { "Serilog": { + "MinimumLevel": { + "Default": "Warning", + "Override": { + "Microsoft": "Warning", + "TouchPortalSDK": "Information", + "MSFSTouchPortalPlugin": "Information" + } + }, "Using": [ "Serilog.Sinks.File", "Serilog.Sinks.Console" ], "WriteTo": [ { @@ -11,7 +19,8 @@ "Args": { "path": "logs/MSFSTouchPortalPlugin.log", "rollingInterval": "Day", - "retainedFileCountLimit": 7 + "retainedFileCountLimit": 7, + "outputTemplate": "[{Timestamp:MM.dd HH:mm:ss.fff}] [{Level:u3}] {SourceContext} {Message:lj} {NewLine}{Exception}" } } ] @@ -20,18 +29,10 @@ { "Name": "Console", "Args": { - "outputTemplate": "[{Timestamp:MM.dd HH:mm:ss}] [{Level:u3}] {SourceContext} {NewLine} {Message:lj} {NewLine}{Exception}" + "outputTemplate": "[{Timestamp:HH:mm:ss.fff}] [{Level:u3}] {SourceContext} {NewLine} {Message:lj} {NewLine}{Exception}" } } - ], - "MinimumLevel": { - "Default": "Warning", - "Override": { - "Microsoft": "Warning", - "TouchPortalSDK": "Information", - "MSFSTouchPortalPlugin": "Information" - } - } + ] }, "TouchPortalOptions": { "IpAddress": "127.0.0.1", From 95da1e3ec942e2848ebc27746c97d9f12ad0d80f Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sat, 2 Apr 2022 08:03:58 -0400 Subject: [PATCH 54/93] Add SimConnect.cfg with default connection type for MSFS; Add a TP Setting to allow user to choose connection profile index; Add ability for custom SimConnect.cfg to be loaded from user's AppData folder. Shorten typed value getters in PluginSetting. --- .../Configuration/PluginConfig.cs | 16 ++++++++ .../Configuration/SimConnect.cfg | Bin 0 -> 2074 bytes .../Interfaces/ISimConnectService.cs | 2 +- .../MSFSTouchPortalPlugin.csproj | 37 ++++++++++-------- .../Objects/Plugin/Plugin.cs | 16 ++++++++ .../Services/PluginService.cs | 18 +++++---- .../Services/SimConnectService.cs | 4 +- MSFSTouchPortalPlugin/Types/PluginSetting.cs | 9 +++-- 8 files changed, 72 insertions(+), 30 deletions(-) create mode 100644 MSFSTouchPortalPlugin/Configuration/SimConnect.cfg diff --git a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs index 0559646..57910ba 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs +++ b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs @@ -37,6 +37,22 @@ public PluginConfig(ILogger logger) { SharpConfig.Configuration.AlwaysQuoteStringValues = true; // custom SharpConfig v3.2.9.2-mp feature } + public bool CopySimConnectConfig() { + string filename = "SimConnect.cfg"; + string srcFile = Path.Combine(UserConfigFolder, filename); + if (File.Exists(srcFile)) { + try { + File.Copy(srcFile, Path.Combine(AppRootFolder, filename), true); + return true; + } + catch (Exception e) { + _logger.LogWarning(e, "Error trying to copy SimConnect.cfg file from user's AppData folder:"); + return false; + } + } + return false; + } + public IReadOnlyCollection LoadSimVarItems(bool isUserConfig = true, string filename = default) { List ret = new(); if (filename == default) diff --git a/MSFSTouchPortalPlugin/Configuration/SimConnect.cfg b/MSFSTouchPortalPlugin/Configuration/SimConnect.cfg new file mode 100644 index 0000000000000000000000000000000000000000..3a83ee3b5be02bda1c041e8990e5daf3add4d45d GIT binary patch literal 2074 zcmd5+O-sW-5S_E&f5=fmVym?tG(zzMdQgg91o6}sB} z-?SyNFMDb+Ht#R-zC% zvZapt<|xy1i94yh!A}*QH)L;vTb%-iS5-!|<*IOZ4-PANRGdBFFYv{>e&X#Y1~=Fx zIHg%W;A^V*YA<6{SICBXbsYQ1eIebfhNenqP+DueeI@F!PaCj13VS=8Tro0ishiB3 zeanhZpvfGXu={o8w&`G7I<*{&UA)fGF5$Blk?OD+(9PVi>)7#}Qg#&I?78YmwBVT( zPI3YQ*x1RIiV~~7h!t|3;=jx0Qq5bAW+sEP<-un`+u~G`E3F~yf#S#P$oNXJ%WKsf z*{D^Fc7UjyN>{0t0Lerwo|HYi>FKl=Yl{{W41TlD|{ literal 0 HcmV?d00001 diff --git a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs index b3368e4..06aefe9 100644 --- a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs +++ b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs @@ -17,7 +17,7 @@ internal interface ISimConnectService { bool IsConnected(); bool AddNotification(Groups group, Enum eventId); - bool Connect(); + bool Connect(uint configIndex = 0); void Disconnect(); bool MapClientEventToSimEvent(Enum eventId, string eventName); void SetNotificationGroupPriorities(); diff --git a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj index afe651d..caa2899 100644 --- a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj +++ b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj @@ -59,6 +59,20 @@ + + + + .\lib\Microsoft.FlightSimulator.SimConnect.dll + false + true + + + PreserveNewest + SimConnect.dll + + + + .\lib\TouchPortalSDK\TouchPortalSDK.dll @@ -68,6 +82,7 @@ + .\lib\SharpConfig\SharpConfig.dll @@ -77,22 +92,7 @@ - - - .\lib\Microsoft.FlightSimulator.SimConnect.dll - false - true - - - - - - PreserveNewest - SimConnect.dll - - - - + PreserveNewest @@ -100,6 +100,11 @@ PreserveNewest + + + PreserveNewest + SimConnect.cfg + diff --git a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs index 5637b8d..7fd8558 100644 --- a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs +++ b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs @@ -23,6 +23,21 @@ public static class Settings Description = "Set to 1 to automatically attempt connection to flight simulator upon Touch Portal startup. Set to 0 to only connect manually via the provided Action.", }; + public static readonly PluginSetting SimConnectConfigIndex = new PluginSetting("SimConnectConfigIndex", DataType.Number) { + Name = "SimConnect.cfg Index (0 for MSFS, 1 for FSX, or custom)", + Description = @"A default SimConnect.cfg is included with this plugin (in the installation folder). " + + "You may also use a custom configuration file stored in your 'C:\\Users\\\\AppData\\Roaming\\MSFSTouchPortalPlugin' folder. \n\n" + + "The SimConnect.cfg file can contain a number of configurations, identified in sections with the [SimConnect.N] title. " + + "The index number can be specified in this setting. This is useful for \n" + + " 1. compatibility with FSX, and/or \n" + + " 2. custom configurations over network connections (running Touch Portal on a different computer than the sim). \n" + + "The default configuration index is zero, which (in the included default SimConnect.cfg) is suitable for MSFS (2020). Use the index 1 for compatibility with FSX (or perhaps other sims). \n\n" + + "See here for more info: https://docs.flightsimulator.com/html/Programming_Tools/SimConnect/SimConnect_CFG_Definition.htm", + Default = "0", + MinValue = 0, + MaxValue = 20 + }; + [TouchPortalAction("ActionRepeatInterval", "Action Repeat Interval", "MSFS", "Held Action Repeat Rate (ms)", "Repeat Interval: {0} to/by: {1} ms", true)] [TouchPortalActionChoice(new[] { "Set", "Increment", "Decrement" })] [TouchPortalActionText("450", 50, int.MaxValue)] @@ -38,6 +53,7 @@ public static class Settings ReadOnly = true, TouchPortalStateId = "ActionRepeatInterval" }; + } // IDs for handling internal events diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index dcfd709..a2743e8 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -126,6 +126,10 @@ private bool Initialize() { _simConnectService.OnConnect += SimConnectEvent_OnConnect; _simConnectService.OnDisconnect += SimConnectEvent_OnDisconnect; + // Check for custom SimConnect.cfg and try copy it to application dir (may require elevated privileges) + if (_pluginConfig.CopySimConnectConfig()) + _logger.LogInformation("Using custom SimConnect.cfg file from user's AppData folder."); + return true; } @@ -134,7 +138,7 @@ private async Task SimConnectionMonitor() { try { while (!_cancellationToken.IsCancellationRequested) { _simConnectionRequest.Wait(_cancellationToken); - if (!_simConnectService.IsConnected() && !_simConnectService.Connect()) + if (!_simConnectService.IsConnected() && !_simConnectService.Connect(Settings.SimConnectConfigIndex.UIntValue)) await Task.Delay(10000, _cancellationToken); // delay 10s on connection error } } @@ -307,11 +311,11 @@ private void ProcessInternalEvent(ActionEventType action, Enum eventId, in strin case Plugin.ActionRepeatIntervalSet: if (action.ValueIndex < dataArry.Length && double.TryParse(dataArry[action.ValueIndex], out var interval)) { if (pluginEventId == Plugin.ActionRepeatIntervalInc) - interval = Settings.ActionRepeatInterval.ValueAsDbl() + interval; + interval = Settings.ActionRepeatInterval.RealValue + interval; else if (pluginEventId == Plugin.ActionRepeatIntervalDec) - interval = Settings.ActionRepeatInterval.ValueAsDbl() - interval; + interval = Settings.ActionRepeatInterval.RealValue - interval; interval = Math.Clamp(interval, Settings.ActionRepeatInterval.MinValue, Settings.ActionRepeatInterval.MaxValue); - if (interval != Settings.ActionRepeatInterval.ValueAsDbl()) + if (interval != Settings.ActionRepeatInterval.RealValue) _client.SettingUpdate(Settings.ActionRepeatInterval.Name, $"{interval:F0}"); // this will trigger the actual value update } break; @@ -374,7 +378,7 @@ private void ProcessPluginSettings(IReadOnlyCollection logger, IReflectionService r public bool IsConnected() => (_connected && _simConnect != null); - public bool Connect() { + public bool Connect(uint configIndex = 0) { if (_connecting || _simConnect != null) return _connected; @@ -55,7 +55,7 @@ public bool Connect() { _logger.LogInformation("Connecting to SimConnect..."); try { - _simConnect = new SimConnect("Touch Portal Plugin", GetConsoleWindow(), WM_USER_SIMCONNECT, _scReady, 0); + _simConnect = new SimConnect("Touch Portal Plugin", GetConsoleWindow(), WM_USER_SIMCONNECT, _scReady, configIndex); _connected = true; diff --git a/MSFSTouchPortalPlugin/Types/PluginSetting.cs b/MSFSTouchPortalPlugin/Types/PluginSetting.cs index 41e4cca..b0f72c9 100644 --- a/MSFSTouchPortalPlugin/Types/PluginSetting.cs +++ b/MSFSTouchPortalPlugin/Types/PluginSetting.cs @@ -89,10 +89,11 @@ public void SetValueDynamic(dynamic value) { } } - public int ValueAsInt() => Value == null || ValueType == DataType.Text ? 0 : (int)Value; - public bool ValueAsBool() => Value == null ? false : ValueType == DataType.Text ? new BooleanString(ValueAsStr()) : ValueType == DataType.Number ? ValueAsInt() != 0 : (bool)Value; - public double ValueAsDbl() => Value == null ? double.NaN : (double)Value; - public string ValueAsStr() => Value == null ? string.Empty : Value.ToString(); + public int IntValue => Value == null || ValueType == DataType.Text ? 0 : (int)Value; + public uint UIntValue => (uint)IntValue; + public bool BoolValue => Value == null ? false : ValueType == DataType.Text ? new BooleanString(StringValue) : ValueType == DataType.Number ? IntValue != 0 : (bool)Value; + public double RealValue => Value == null ? double.NaN : (double)Value; + public string StringValue => Value == null ? string.Empty : Value.ToString(); public PluginSetting(string id, DataType type = DataType.Text) { SetProperties(id, null, null, type); } public PluginSetting(string id, double minValue, double maxValue, string defaultValue = null) { SetProperties(id, null, defaultValue, DataType.Number, minValue, maxValue); } From 4daef5eccc6a38e137c48be0eb8fad17d20314a4 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Mon, 4 Apr 2022 09:01:10 -0400 Subject: [PATCH 55/93] [Plugin] Add ability to load custom state config file(s) via new plugin setting; * Also add Settings option to adjust the folder where all user config files are loaded from; * Includes feature to use entry.tp version number to track static vs. dynamic states to generate, and also version number of loaded configuration (with a new corresponding state); * Adds a new "Reload States" option to the current "Connect" action. --- .../Configuration/PluginConfig.cs | 62 ++++++++--- .../Configuration/PluginStates.ini | 5 + .../Objects/Plugin/Plugin.cs | 45 ++++++-- .../Services/PluginService.cs | 100 +++++++++++++++--- 4 files changed, 174 insertions(+), 38 deletions(-) diff --git a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs index 57910ba..63abf0b 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs +++ b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs @@ -7,7 +7,6 @@ using System.Text; using System.Runtime.Serialization; using Microsoft.Extensions.Logging; -//using SharpConfig; namespace MSFSTouchPortalPlugin.Configuration { @@ -25,7 +24,23 @@ internal class PluginConfig public static string AppRootFolder { get; set; } = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); public static string AppConfigFolder { get; set; } = Path.Combine(AppRootFolder, "Configuration"); - public static string UserConfigFolder { get; set; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), RootName); + + public static string UserConfigFolder { + get => _currentUserCfgFolder; + set { + if (string.IsNullOrWhiteSpace(value)) + _currentUserCfgFolder = _defaultUserCfgFolder; + else + _currentUserCfgFolder = value; + } + } + + private static readonly string _defaultUserCfgFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), RootName); + private static string _currentUserCfgFolder = _defaultUserCfgFolder; + + public const uint ENTRY_FILE_VER_MASK_NOSTATES = 0x80000000; // leading bit indicates an entry file with no static SimVar states at all (create all states dynamically) + public const uint ENTRY_FILE_VER_MASK_CUSTOM = 0x7F000000; // any of the other 7 bits indicates an entry file generated from custom config files (do not create any dynamic states) + public const ushort ENTRY_FILE_CONF_VER_SHIFT = 24; // bit position of custom config version number, used as the last 7 bits of file version private readonly ILogger _logger; @@ -55,19 +70,27 @@ public bool CopySimConnectConfig() { public IReadOnlyCollection LoadSimVarItems(bool isUserConfig = true, string filename = default) { List ret = new(); + + // qualify the file name and path if (filename == default) filename = StatesConfigFile; - string filepath = Path.Combine(isUserConfig ? UserConfigFolder : AppConfigFolder, filename); - if (!File.Exists(filepath)) { - _logger.LogWarning($"Cannot load SimVar states, file '{filename}' not found at '{filepath}'"); + else if (string.IsNullOrWhiteSpace(Path.GetExtension(filename))) + filename += ".ini"; + if (filename.IndexOfAny(new char[] { '\\', '/' }) < 0) + filename = Path.Combine(isUserConfig ? UserConfigFolder : AppConfigFolder, filename); + + if (!File.Exists(filename)) { + _logger.LogError($"Cannot load SimVar states, file not found at '{filename}'"); return ret; } + + _logger.LogDebug($"Loading SimVars from file '{filename}'..."); SharpConfig.Configuration cfg; try { - cfg = SharpConfig.Configuration.LoadFromFile(filepath, Encoding.UTF8); + cfg = SharpConfig.Configuration.LoadFromFile(filename, Encoding.UTF8); } catch (Exception e) { - _logger.LogWarning(e, $"Configuration LoadFromFile error in '{filepath}':"); + _logger.LogWarning(e, $"Configuration LoadFromFile error in '{filename}':"); return ret; } foreach (SharpConfig.Section item in cfg) { @@ -86,15 +109,30 @@ public IReadOnlyCollection LoadSimVarItems(bool isUserConfig = true, continue; } simVar.Id = item.Name; + simVar.TouchPortalStateId = $"{RootName}.{simVar.CategoryId}.State.{simVar.Id}"; // check unique - if (ret.FirstOrDefault(s => s.Id == simVar.Id) != null) { - _logger.LogWarning($"Duplicate SimVar ID found for '{simVar.Id}', skipping."); - continue; + if (ret.FindIndex(s => s.Id == simVar.Id) is int idx && idx > -1) { + _logger.LogWarning($"Duplicate SimVar ID found for '{simVar.Id}', overwriting."); + ret[idx] = simVar; + } + else { + ret.Add(simVar); } - simVar.TouchPortalStateId = $"{RootName}.{simVar.CategoryId}.State.{simVar.Id}"; - ret.Add(simVar); } + _logger.LogDebug($"Loaded {ret.Count} SimVars from '{filename}'"); + return ret; + } + + public IReadOnlyCollection LoadCustomSimVars(IEnumerable fileNames) { + SimVarItem[] ret = Array.Empty(); + foreach (var file in fileNames) { + // special case for anything named "default" which indicates the States.ini file in the plugin's install folder + if (file.ToLower()[0..7] == "default") + ret = ret.Concat(LoadSimVarItems(false)).ToArray(); + else + ret = ret.Concat(LoadSimVarItems(true, file)).ToArray(); + } return ret; } diff --git a/MSFSTouchPortalPlugin/Configuration/PluginStates.ini b/MSFSTouchPortalPlugin/Configuration/PluginStates.ini index 3ded99b..3066d62 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginStates.ini +++ b/MSFSTouchPortalPlugin/Configuration/PluginStates.ini @@ -22,3 +22,8 @@ DefaultValue = "0" CategoryId = Plugin Name = "The loaded entry.tp plugin version number." DefaultValue = "0" + +[ConfigVersion] +CategoryId = Plugin +Name = "The loaded entry.tp custom configuration version." +DefaultValue = "0" diff --git a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs index 7fd8558..44b5207 100644 --- a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs +++ b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs @@ -7,10 +7,11 @@ namespace MSFSTouchPortalPlugin.Objects.Plugin [TouchPortalCategory(Groups.Plugin)] internal static class PluginMapping { [TouchPortalAction("Connection", "Connection", "MSFS", "Toggle/On/Off SimConnect Connection", "SimConnect Connection - {0}")] - [TouchPortalActionChoice(new [] { "Toggle", "On", "Off" })] + [TouchPortalActionChoice(new [] { "Toggle", "On", "Off", "Reload States" })] [TouchPortalActionMapping("ToggleConnection", "Toggle")] [TouchPortalActionMapping("Connect", "On")] [TouchPortalActionMapping("Disconnect", "Off")] + [TouchPortalActionMapping("ReloadStates", "Reload States")] public static readonly object Connection; } @@ -23,21 +24,44 @@ public static class Settings Description = "Set to 1 to automatically attempt connection to flight simulator upon Touch Portal startup. Set to 0 to only connect manually via the provided Action.", }; + public static readonly PluginSetting UserStateFiles = new PluginSetting("UserStateFiles", DataType.Text) { + Name = "Sim Variable State Config File(s) (blank = Default)", + Description = "Here you can specify one or more custom configuration files which define SimConnect variables to request as Touch Portal States. " + + "This plugin comes with an extensive default set of states, however since the possibilities between which variables are requested, which units they are displayed in," + + "and how they are formatted are almost endless. This option provides a way to customize the output as desired.\n\n" + + "Enter a file name here, with or w/out the suffix (`.ini` is assumed). Separate multiple files with commas (and optional space). " + + "To include the default set of variables/states, use the name `Default` as one of the file names (in any position of the list).\n\n" + + "Files are loaded in the order in which they appear in the list, and in case of conflicting state IDs, the last one found will be used.\n\n" + + "The custom file(s) are expected to be in the folder specified in the \"User Config Files Path\" setting (see below).", + Default = "Default", + MaxLength = 255 + }; + public static readonly PluginSetting SimConnectConfigIndex = new PluginSetting("SimConnectConfigIndex", DataType.Number) { Name = "SimConnect.cfg Index (0 for MSFS, 1 for FSX, or custom)", - Description = @"A default SimConnect.cfg is included with this plugin (in the installation folder). " + - "You may also use a custom configuration file stored in your 'C:\\Users\\\\AppData\\Roaming\\MSFSTouchPortalPlugin' folder. \n\n" + - "The SimConnect.cfg file can contain a number of configurations, identified in sections with the [SimConnect.N] title. " + - "The index number can be specified in this setting. This is useful for \n" + - " 1. compatibility with FSX, and/or \n" + - " 2. custom configurations over network connections (running Touch Portal on a different computer than the sim). \n" + - "The default configuration index is zero, which (in the included default SimConnect.cfg) is suitable for MSFS (2020). Use the index 1 for compatibility with FSX (or perhaps other sims). \n\n" + - "See here for more info: https://docs.flightsimulator.com/html/Programming_Tools/SimConnect/SimConnect_CFG_Definition.htm", + Description = + "A __SimConnect.cfg__ file can contain a number of connection configurations, identified in sections with the `[SimConnect.N]` title. " + + "A default __SimConnect.cfg__ is included with this plugin (in the installation folder). " + + "You may also use a custom configuration file stored in the \"User Config Files Path\" folder (see below). \n\n" + + "The index number can be specified in this setting. This is useful for \n" + + " 1. compatibility with FSX, and/or \n" + + " 2. custom configurations over network connections (running Touch Portal on a different computer than the sim). \n\n" + + "The default configuration index is zero, which (in the included default SimConnect.cfg) is suitable for MSFS (2020). Use the index 1 for compatibility with FSX (or perhaps other sims). \n\n" + + "See here for more info: https://docs.flightsimulator.com/html/Programming_Tools/SimConnect/SimConnect_CFG_Definition.htm", Default = "0", MinValue = 0, MaxValue = 20 }; + public static readonly PluginSetting UserConfigFilesPath = new PluginSetting("UserConfigFilesPath", DataType.Text) { + Name = "User Config Files Path (blank for default)", + Description = "The system path where custom user configuration files for sate definitions, SimConnect.cfg, etc, are stored. " + + "Keep it blank for default, which is `C:\\Users\\\\AppData\\Roaming\\MSFSTouchPortalPlugin`.\n\n" + + "Note that using this plugin's installation folder for custom data storage is not recommended, since anything in there will likely get overwritten during a plugin update/re-install.", + Default = "", + MaxLength = 255 + }; + [TouchPortalAction("ActionRepeatInterval", "Action Repeat Interval", "MSFS", "Held Action Repeat Rate (ms)", "Repeat Interval: {0} to/by: {1} ms", true)] [TouchPortalActionChoice(new[] { "Set", "Increment", "Decrement" })] [TouchPortalActionText("450", 50, int.MaxValue)] @@ -46,7 +70,7 @@ public static class Settings [TouchPortalActionMapping("ActionRepeatIntervalDec", "Decrement")] public static readonly PluginSetting ActionRepeatInterval = new PluginSetting("ActionRepeatInterval", DataType.Number) { Name = "Held Action Repeat Rate (ms)", - Description = "Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Action Repeat Interval' action.", + Description = "Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Action Repeat Interval' action. This setting cannot be changed from the TP Plugin Settings page (even though it appears to be editable on there).", Default = "450", MinValue = 50, MaxValue = int.MaxValue, @@ -64,6 +88,7 @@ internal enum Plugin : short { ToggleConnection, Connect, Disconnect, + ReloadStates, ActionRepeatIntervalInc, ActionRepeatIntervalDec, diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index a2743e8..273e4e6 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using MSFSTouchPortalPlugin.Configuration; +using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Helpers; using MSFSTouchPortalPlugin.Interfaces; @@ -37,16 +38,20 @@ internal class PluginService : IPluginService, ITouchPortalEventHandler private Task _timerEventsTask; private readonly ManualResetEventSlim _simConnectionRequest = new(false); private bool autoReconnectSimConnect = false; + private EntryFileType _entryFileType = EntryFileType.Default; private bool _quitting; private Dictionary actionsDictionary = new(); private Dictionary statesDictionary = new(); private Dictionary pluginSettingsDictionary = new(); - private readonly ConcurrentBag customIntervalStates = new(); - private readonly ConcurrentDictionary repeatingActionTimers = new(); + private readonly ConcurrentBag _customIntervalStates = new(); // IDs of SimVars which need periodic value polling; concurrent because the polling happens in separate task from setter. + private readonly List _dynamicStateIds = new(); // keep track of dynamically created states for clearing them if/when reloading sim var state files + private readonly ConcurrentDictionary _repeatingActionTimers = new(); private static readonly System.Data.DataTable _expressionEvaluator = new(); // used to evaluate basic math in action data + private enum EntryFileType { Default, NoStates, Custom }; + /// /// Constructor /// @@ -158,7 +163,7 @@ private async Task PluginEventsTask() { _logger.LogDebug("PluginEventsTask task started."); try { while (_simConnectService.IsConnected() && !_simTasksCancelToken.IsCancellationRequested) { - foreach (Timer tim in repeatingActionTimers.Values) + foreach (Timer tim in _repeatingActionTimers.Values) tim.Tick(); CheckPendingRequests(); await Task.Delay(25, _simTasksCancelToken); @@ -239,20 +244,61 @@ private void SetupEventLists() { } private void SetupSimVars() { - var configStates = _pluginConfig.LoadSimVarItems(false); - statesDictionary = configStates.ToDictionary(s => s.Def, s => s); - customIntervalStates.Clear(); - // Register SimVars, but first clear out any old ones. - _simConnectService.ClearAllDataDefinitions(); + // We may need to generate all, some, or no states dynamically. + bool allStatesDynamic = (_entryFileType == EntryFileType.NoStates); + bool someStateDynamic = false; + IEnumerable defaultStates = Array.Empty(); // in case we need to create _some_ custom states dynamically + + // First we figure out which file(s) to load. + bool useCustomCfg = !string.IsNullOrWhiteSpace(Settings.UserStateFiles.StringValue) && Settings.UserStateFiles.StringValue.ToLower() != "default"; + if (useCustomCfg) { + // Create the file(s) list + string[] cfgFiles = Settings.UserStateFiles.StringValue.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + _logger.LogInformation($"Loading custom state file(s) '{string.Join(", ", cfgFiles)}' from folder '{PluginConfig.UserConfigFolder}'."); + // load the file(s); the special file name "default" (if present) will actually use the one from plugin's install folder. + statesDictionary = _pluginConfig.LoadCustomSimVars(cfgFiles).ToDictionary(s => s.Def, s => s); + + // if the user is NOT using dynamic states for everything (entry_no-states.tp) nor a custom entry.tp file, + // then lets figure out what the default states are so we can create any missing ones dynamically if needed. + if (_entryFileType == EntryFileType.Default) { + // get the defaults + defaultStates = _pluginConfig.LoadSimVarItems(false).Select(s => s.Id); + someStateDynamic = defaultStates.Any(); + } + } + else { // Default config + _logger.LogInformation($"Loading default SimVar state file '{PluginConfig.AppConfigFolder}/{PluginConfig.StatesConfigFile}'."); + statesDictionary = _pluginConfig.LoadSimVarItems(false).ToDictionary(s => s.Def, s => s); + } + + // clear out any old data + _customIntervalStates.Clear(); + RemoveDynamicStates(); // if any + _simConnectService.ClearAllDataDefinitions(); // if any (SimConnectService keeps track of this internally) + + // Now register SimVars and possibly create TP states foreach (var simVar in statesDictionary.Values) { + // Does this var need value polling? if (simVar.NeedsScheduledRequest) - customIntervalStates.Add(simVar.Def); + _customIntervalStates.Add(simVar.Def); + // Need a dynamic state? + if (allStatesDynamic || (someStateDynamic && !defaultStates.Contains(simVar.Id))) { + _dynamicStateIds.Add(simVar.TouchPortalStateId); // keep track for removing them later if needed + _client.CreateState(simVar.TouchPortalStateId, Categories.PrependFullCategoryName(simVar.CategoryId, simVar.Name), simVar.DefaultValue); + _logger.LogDebug($"Created dynamic state {simVar.TouchPortalStateId}'."); + } + // Register it. If the SimVar gets regular updates (not custom polling) then this also starts the data requests for this value. _simConnectService.RegisterToSimConnect(simVar); } } + private void RemoveDynamicStates() { + foreach (var id in _dynamicStateIds) + _client.RemoveState(id); + } + private void CheckPendingRequests() { - foreach (var def in customIntervalStates) { + foreach (var def in _customIntervalStates) { // Check if a value update is required based on the SimVar's internal tracking mechanism. if (statesDictionary.TryGetValue(def, out var s) && s.UpdateRequired) { s.SetPending(true); @@ -305,6 +351,9 @@ private void ProcessInternalEvent(ActionEventType action, Enum eventId, in strin else UpdateSimConnectState(); break; + case Plugin.ReloadStates: + SetupSimVars(); + break; case Plugin.ActionRepeatIntervalInc: case Plugin.ActionRepeatIntervalDec: @@ -359,8 +408,8 @@ private void ProcessSimEvent(ActionEventType action, Enum eventId, in string[] d } private void ClearRepeatingActions() { - foreach (var act in repeatingActionTimers) { - if (repeatingActionTimers.TryRemove(act.Key, out var tim)) { + foreach (var act in _repeatingActionTimers) { + if (_repeatingActionTimers.TryRemove(act.Key, out var tim)) { tim.Dispose(); } } @@ -374,6 +423,10 @@ private void ClearRepeatingActions() { private void ProcessPluginSettings(IReadOnlyCollection settings) { if (settings == null) return; + // change tracking + string oldCfgPath = Settings.UserConfigFilesPath.StringValue; + string oldCfgFiles = Settings.UserStateFiles.StringValue; + // loop over incoming new settings foreach (var s in settings) { if (pluginSettingsDictionary.TryGetValue(s.Name, out PluginSetting setting)) { setting.SetValueFromString(s.Value); @@ -381,6 +434,10 @@ private void ProcessPluginSettings(IReadOnlyCollection> 8); // strip the patch version _logger?.LogInformation( $"Touch Portal Connected with: TP v{message.TpVersionString}, SDK v{message.SdkVersion}, {PluginId} entry.tp v{message.PluginVersion}, " + $"{VersionInfo.AssemblyName} running v{VersionInfo.GetProductVersionString()} ({runtimeVer})" ); + // convert the entry.tp version back to the actual decimal value + uint tpVer; + try { tpVer = uint.Parse($"{message.PluginVersion}", System.Globalization.NumberStyles.HexNumber); } + catch { tpVer = VersionInfo.GetProductVersionNumber() >> 8; } + _entryFileType = (tpVer & PluginConfig.ENTRY_FILE_VER_MASK_NOSTATES) > 0 ? EntryFileType.NoStates + : (tpVer & PluginConfig.ENTRY_FILE_VER_MASK_CUSTOM) > 0 ? EntryFileType.Custom + : EntryFileType.Default; + + _logger.LogInformation($"Detected {_entryFileType} type entry.tp definition file."); + ProcessPluginSettings(message.Settings); autoReconnectSimConnect = Settings.ConnectSimOnStartup.BoolValue; // we only care about this at startup _client.StateUpdate(PluginId + ".Plugin.State.RunningVersion", runtimeVer); - _client.StateUpdate(PluginId + ".Plugin.State.EntryVersion", $"{message.PluginVersion}"); + _client.StateUpdate(PluginId + ".Plugin.State.EntryVersion", $"{tpVer & 0xFFFFFF:X}"); + _client.StateUpdate(PluginId + ".Plugin.State.ConfigVersion", $"{tpVer >> 24:X}"); } public void OnSettingsEvent(SettingsEvent message) { @@ -422,7 +490,7 @@ public void OnActionEvent(ActionEvent message) { // "On Hold" activated ("down" event). Try to add this action to the repeating/scheduled actions queue, unless it already exists. var timer = new Timer(Settings.ActionRepeatInterval.IntValue); timer.Elapsed += delegate { ProcessEvent(message); }; - if (repeatingActionTimers.TryAdd(message.ActionId, timer)) + if (_repeatingActionTimers.TryAdd(message.ActionId, timer)) timer.Start(); else timer.Dispose(); @@ -430,7 +498,7 @@ public void OnActionEvent(ActionEvent message) { case TouchPortalSDK.Messages.Models.Enums.Press.Up: // "On Hold" released ("up" event). Mark action for removal from repeating queue. - if (repeatingActionTimers.TryRemove(message.ActionId, out var tim)) + if (_repeatingActionTimers.TryRemove(message.ActionId, out var tim)) tim.Dispose(); // No further processing for this action. break; From 1b0520ed0ec2c1433307a742905abbb8486fbba2 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Mon, 4 Apr 2022 09:18:01 -0400 Subject: [PATCH 56/93] [Generator] Add ability to generate entry JSON and docs from custom state config files; * Create a new entry_no-states.tp file for users with custom configs who just want all states to be dynamic; * Use the 8 high bits in version number of generated entry files to flag custom/no-state/default types; * Refactor command line parsing and add help text; * Revert the entry.tp version number back to shorter original w/out the "build" part. !! Changes the default entry.tp plugin name to version with spaces ("MSFS Touch Portal Plugin"); This resets the "auto-connect on startup" and "repeat rate" settings back to defaults. !! The default value of "Connect To Flight Sim on Startup" option is now **0 (false)** --- .../Configuration/GeneratorOptions.cs | 43 ++++++++++- .../GenerateDoc.cs | 70 +++++++++++------ .../GenerateEntry.cs | 76 ++++++++++++++----- .../MSFSTouchPortalPlugin-Generator.csproj | 14 +++- MSFSTouchPortalPlugin-Generator/Model/Base.cs | 2 +- .../Model/DocBase.cs | 3 +- MSFSTouchPortalPlugin-Generator/Program.cs | 71 ++++++++++++++--- .../Properties/launchSettings.json | 2 +- .../Objects/Plugin/Plugin.cs | 3 +- 9 files changed, 224 insertions(+), 60 deletions(-) diff --git a/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs b/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs index 6f08996..3e00f0f 100644 --- a/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs +++ b/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs @@ -1,7 +1,44 @@ -namespace MSFSTouchPortalPlugin_Generator.Configuration { - internal class GeneratorOptions { +using CommandLine; +using CommandLine.Text; +using System; + +namespace MSFSTouchPortalPlugin_Generator.Configuration +{ + internal class GeneratorOptions + { + [Option('o', "OutputPath", Default = ".\\", MetaValue = "", Required = false, + HelpText = "Output directory path for generated files (default is current working directory).")] + public string OutputPath { get; set; } + + [Option('s', "StateFiles", Default = null, MetaValue = "", Separator = ',', Required = false, + HelpText = "\nOne or more custom configuration files which define SimConnect variables to request as Touch Portal States. \n" + + "Separate multiple files with commas (eg. \"Engines,Fuel\"). The file extension is optional, \".ini\" is assumed. \n" + + "The file names may optionally include folder paths, but the default location is according to the StateFilesPath option (see below). \n" + + "To include the default set of variables/states, use the name \"Default\" as one of the names (in any position of the list).")] + public System.Collections.Generic.IEnumerable StateFiles { get; set; } + + [Option('c', "ConfigVersion", Default = 1U, MetaValue = "<1-79>", Required = false, + HelpText = "\nWhen used with 'StateFiles' option, specifies a configuration version number (ignored otherwise). " + + "This number will be used in the first two digits of the generated plugin version in entry.tp file.\n" + + "Must be in the range of 1-79.")] + public uint ConfigVersion { get; set; } + + [Option('p', "StateFilesPath", Default = "", MetaValue = "", Required = false, + HelpText = "\nWhere to find the custom state configuration file(s) specified in 'StateFiles' parameter.\n" + + "Default is the current user's 'AppData/Roaming/MSFSTouchPortalPlugin' directory.\n" + + "Note that the Default states file (if used) is always read from the 'Configuration' subfolder of this utility.")] + public string StateFilesPath { get; set; } + + [Option('i', "PluginId", Default = "MSFSTouchPortalPlugin", MetaValue = "", Required = false, + HelpText = "\nThe Plugin ID string which will be used for all the entry definitions.")] + public string PluginId { get; set; } + + [Option('n', "PluginName", Default = "MSFS Touch Portal Plugin", MetaValue = "", Required = false, + HelpText = "\nThe Plugin Name for TP entry.tp base name attribute.")] public string PluginName { get; set; } + + [Option('f', "PluginFolder", Default = "MSFS-TouchPortal-Plugin", MetaValue = "", Required = false, + HelpText = "\nName of the plugin's folder once installed to TP.")] public string PluginFolder { get; set; } - public string TargetPath { get; set; } } } diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index f3cfa0f..60ede01 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using MSFSTouchPortalPlugin.Attributes; using MSFSTouchPortalPlugin.Configuration; using MSFSTouchPortalPlugin.Constants; @@ -16,16 +15,17 @@ using System.Text.RegularExpressions; using System.Reflection; using System.Text; +using System.Collections.Generic; namespace MSFSTouchPortalPlugin_Generator { internal class GenerateDoc : IGenerateDoc { private readonly ILogger _logger; - private readonly IOptions _options; + private readonly GeneratorOptions _options; private readonly IReflectionService _reflectionSvc; private readonly PluginConfig _pluginConfig; - public GenerateDoc(ILogger logger, IOptions options, IReflectionService reflectionSvc, PluginConfig pluginConfig) { + public GenerateDoc(ILogger logger, GeneratorOptions options, IReflectionService reflectionSvc, PluginConfig pluginConfig) { _logger = logger; _options = options; _reflectionSvc = reflectionSvc ?? throw new ArgumentNullException(nameof(reflectionSvc)); @@ -35,22 +35,25 @@ public GenerateDoc(ILogger logger, IOptions optio public void Generate() { // Create reflection model var model = CreateModel(); + if (model == null) + return; // Create Markdown var result = CreateMarkdown(model); // Save - var dest = Path.Combine(_options.Value.TargetPath, "DOCUMENTATION.md"); + var dest = Path.Combine(_options.OutputPath, "DOCUMENTATION.md"); File.WriteAllText(dest, result); _logger.LogInformation($"Generated '{dest}'."); } private DocBase CreateModel() { // Find assembly - var a = Assembly.GetExecutingAssembly().GetReferencedAssemblies().FirstOrDefault(a => a.Name == _options.Value.PluginName); + var a = Assembly.GetExecutingAssembly().GetReferencedAssemblies().FirstOrDefault(a => a.Name == _options.PluginId); if (a == null) { - throw new FileNotFoundException("Unable to load assembly for reflection."); + _logger.LogError($"Unable to load assembly '{_options.PluginId}' for reflection.'"); + return null; } var assembly = Assembly.Load(a); @@ -59,26 +62,39 @@ private DocBase CreateModel() { VersionInfo.Assembly = assembly; var model = new DocBase { - Title = "MSFS 2020 Touch Portal Plugin", - Overview = "This plugin will provide a two-way interface between Touch Portal and Microsoft Flight Simulator 2020 through SimConnect.", + Title = _options.PluginName + " Documentation", + Overview = "This plugin will provide a two-way interface between Touch Portal and Flight Simulators which use SimConnect, such as Microsoft Flight Simulator 2020 and FS-X.", Version = VersionInfo.GetProductVersionString() }; - // read default states config - // TODO: Allow configuration of which state config file(s) to read. - SimVarItem[] simVars = _pluginConfig.LoadSimVarItems(false).Concat(_pluginConfig.LoadPluginStates()).ToArray(); + // read states config + IEnumerable simVars; + if (_options.StateFiles.Any()) { + if (!string.IsNullOrWhiteSpace(_options.StateFilesPath)) + PluginConfig.UserConfigFolder = _options.StateFilesPath; + simVars = _pluginConfig.LoadCustomSimVars(_options.StateFiles); + model.Version += "
" + + $"Custom configuration version {_options.ConfigVersion}
" + + $"Custom State Definitions Source(s): {string.Join(", ", _options.StateFiles)}"; + } + else { + simVars = _pluginConfig.LoadSimVarItems(false); + } + // always include the internal plugin states + simVars = simVars.Concat(_pluginConfig.LoadPluginStates()); - // Get all classes with the TouchPortalCategory + // Get all classes with the TouchPortalCategory Attribute var classList = assemblyList.Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalCategoryAttribute))).OrderBy(o => o.Name); // Loop through categories foreach (var cat in classList) { var catAttr = (TouchPortalCategoryAttribute)Attribute.GetCustomAttribute(cat, typeof(TouchPortalCategoryAttribute)); Groups catId = catAttr.Id; - var newCat = model.Categories.FirstOrDefault(c => c.Id == catId); + var newCat = model.Categories.FirstOrDefault(c => c.CategoryId == catId); if (newCat == null) { newCat = new DocCategory { - Id = catId, + CategoryId = catId, + Id = $"{_options.PluginId}.{catId}", Name = Categories.FullCategoryName(catId), }; model.Categories.Add(newCat); @@ -135,7 +151,7 @@ private DocBase CreateModel() { var categoryStates = simVars.Where(s => s.CategoryId == catId); foreach (SimVarItem state in categoryStates) { var newState = new DocState { - Id = state.TouchPortalStateId, + Id = state.TouchPortalStateId.Split('.').Last(), Type = state.TouchPortalValueType, Description = state.Name, DefaultValue = state.DefaultValue ?? string.Empty, @@ -166,7 +182,6 @@ private DocBase CreateModel() { }; model.Settings.Add(setting); } - model.Settings = model.Settings.OrderBy(c => c.Name).ToList(); return model; } @@ -190,14 +205,22 @@ private static string CreateMarkdown(DocBase model) { // Show settings first s.Append("## Plugin Settings\n
Click to expand\n\n"); model.Settings.ForEach(setting => { + bool[] f = new[] { setting.MaxLength > 0, !double.IsNaN(setting.MinValue), !double.IsNaN(setting.MaxValue) }; s.Append($"### {setting.Name}\n\n"); - s.Append("| Read-only | Type | Default Value | Max. Length | Min. Value | Max. Value |\n"); - s.Append("| --- | --- | --- | --- | --- | --- |\n"); - s.Append($"| {setting.ReadOnly} | {setting.Type} | {setting.DefaultValue} "); - s.Append($"| {(setting.MaxLength > 0 ? setting.MaxLength : "N/A")} "); - s.Append($"| {(double.IsNaN(setting.MinValue) ? "N/A" : setting.MinValue)} "); - s.Append($"| {(double.IsNaN(setting.MaxValue) ? "N/A" : setting.MaxValue)} "); - s.Append("|\n\n"); + s.Append("| Read-only | Type | Default Value"); + if (f[0]) s.Append(" | Max. Length"); + if (f[1]) s.Append(" | Min. Value"); + if (f[2]) s.Append(" | Max. Value"); + s.Append(" |\n"); + s.Append("| --- | --- | ---"); + for (var i = 0; i < 3; ++i) + if (f[i]) s.Append(" | ---"); + s.Append(" |\n"); + s.Append($"| {setting.ReadOnly} | {setting.Type} | {setting.DefaultValue}"); + if (f[0]) s.Append(" | ").Append(setting.MaxLength); + if (f[1]) s.Append(" | ").Append(setting.MinValue); + if (f[2]) s.Append(" | ").Append(setting.MaxValue); + s.Append(" |\n\n"); s.Append(setting.Description + "\n\n"); }); s.Append("
\n\n---\n\n"); @@ -253,6 +276,7 @@ private static string CreateMarkdown(DocBase model) { if (cat.States.Count > 0) { // Loop States s.Append("### States\n\n"); + s.Append($" **Base Id:** {cat.Id}.State.\n\n"); s.Append("| Id | SimVar Name | Description | Unit | Format | DefaultValue |\n"); s.Append("| --- | --- | --- | --- | --- | --- |\n"); cat.States.ForEach(state => { diff --git a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs index 41482d4..5bb52a9 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using MSFSTouchPortalPlugin.Attributes; using MSFSTouchPortalPlugin.Configuration; using MSFSTouchPortalPlugin.Constants; @@ -13,6 +12,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel.DataAnnotations; using System.IO; @@ -23,13 +23,13 @@ namespace MSFSTouchPortalPlugin_Generator { internal class GenerateEntry : IGenerateEntry { private readonly ILogger _logger; - private readonly IOptions _options; + private readonly GeneratorOptions _options; private readonly IReflectionService _reflectionSvc; private readonly PluginConfig _pluginConfig; - public GenerateEntry(ILogger logger, IOptions options, IReflectionService reflectionSvc, PluginConfig pluginConfig) { - _logger = logger; - _options = options; + public GenerateEntry(ILogger logger, GeneratorOptions options, IReflectionService reflectionSvc, PluginConfig pluginConfig) { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _options = options ?? throw new ArgumentNullException(nameof(options)); _reflectionSvc = reflectionSvc ?? throw new ArgumentNullException(nameof(reflectionSvc)); _pluginConfig = pluginConfig ?? throw new ArgumentNullException(nameof(pluginConfig)); @@ -40,30 +40,55 @@ public GenerateEntry(ILogger logger, IOptions o public void Generate() { // Find assembly - var a = Assembly.GetExecutingAssembly().GetReferencedAssemblies().FirstOrDefault(a => a.Name == _options.Value.PluginName); + var a = Assembly.GetExecutingAssembly().GetReferencedAssemblies().FirstOrDefault(a => a.Name == _options.PluginId); if (a == null) { - throw new FileNotFoundException("Unable to load assembly for reflection."); + _logger.LogError($"Unable to load assembly '{_options.PluginId}' for reflection.'"); + return; } // Load assembly var assembly = Assembly.Load(a); - string basePath = $"%TP_PLUGIN_FOLDER%{_options.Value.PluginFolder}/"; - // read default states config - // TODO: Allow configuration of which state config file(s) to read. - SimVarItem[] simVars = _pluginConfig.LoadSimVarItems(false).Concat(_pluginConfig.LoadPluginStates()).ToArray(); + string basePath = $"%TP_PLUGIN_FOLDER%{_options.PluginFolder}/"; + bool useCustomConfigs = _options.StateFiles.Any(); + + // Get version and see if we need a custom version number + VersionInfo.Assembly = assembly; + uint vNum = VersionInfo.GetProductVersionNumber() >> 8; // strip the patch level + // add custom config version if using custom files + if (useCustomConfigs) { + uint cVer = _options.ConfigVersion << PluginConfig.ENTRY_FILE_CONF_VER_SHIFT; + if (cVer > PluginConfig.ENTRY_FILE_VER_MASK_CUSTOM) + cVer = PluginConfig.ENTRY_FILE_VER_MASK_CUSTOM; + vNum |= cVer; + } + + // read states config + IEnumerable simVars = Array.Empty(); + if (useCustomConfigs) { + if (!string.IsNullOrWhiteSpace(_options.StateFilesPath)) + PluginConfig.UserConfigFolder = _options.StateFilesPath; + simVars = _pluginConfig.LoadCustomSimVars(_options.StateFiles); + _logger.LogInformation($"Generating entry.tp v{vNum:X} to '{_options.OutputPath}' with Custom states from file(s): {string.Join(", ", _options.StateFiles)}"); + } + else { + simVars = _pluginConfig.LoadSimVarItems(false); + _logger.LogInformation($"Generating entry.tp v{vNum:X} to '{_options.OutputPath}' with Default states."); + } + // always include the internal plugin states + simVars = simVars.Concat(_pluginConfig.LoadPluginStates()); + var q = assembly.GetTypes().ToList(); // Setup Base Model - VersionInfo.Assembly = assembly; var model = new Base { Sdk = 3, - Version = VersionInfo.GetProductVersionNumber(), - Name = _options.Value.PluginName, - Id = _options.Value.PluginName, - Plugin_start_cmd = $"{basePath}dist/{_options.Value.PluginName}.exe" + Version = vNum, + Name = _options.PluginName, + Id = _options.PluginId, + Plugin_start_cmd = $"{basePath}dist/{_options.PluginId}.exe" }; // Get all classes with the TouchPortalCategory @@ -73,7 +98,7 @@ public void Generate() { foreach (var cat in categoryClasses) { var att = (TouchPortalCategoryAttribute)Attribute.GetCustomAttribute(cat, typeof(TouchPortalCategoryAttribute)); Groups catId = att.Id; - string catIdStr = $"{_options.Value.PluginName}.{catId}"; + string catIdStr = $"{_options.PluginId}.{catId}"; var category = model.Categories.FirstOrDefault(c => c.Id == catIdStr); if (category == null) { category = new TouchPortalCategory { @@ -85,7 +110,7 @@ public void Generate() { } // workaround for backwards compat with mis-named actions in category InstrumentsSystems.Fuel - string actionCatId = _options.Value.PluginName + "." + Categories.ActionCategoryId(catId); + string actionCatId = _options.PluginId + "." + Categories.ActionCategoryId(catId); // Add actions var actions = cat.GetMembers().Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalActionAttribute))).ToList(); @@ -190,7 +215,20 @@ public void Generate() { } var result = JsonConvert.SerializeObject(model, Formatting.Indented); - var dest = Path.Combine(_options.Value.TargetPath, "entry.tp"); + var dest = Path.Combine(_options.OutputPath, "entry.tp"); + File.WriteAllText(dest, result); + _logger.LogInformation($"Generated '{dest}'."); + + if (useCustomConfigs) + return; + + // Generate the entry.tp version with no states; use version as runtime indicator for plugin + model.Version |= PluginConfig.ENTRY_FILE_VER_MASK_NOSTATES; + foreach (var cat in model.Categories) + if (!cat.Id.EndsWith(".Plugin")) + cat.States.Clear(); + result = JsonConvert.SerializeObject(model, Formatting.Indented); + dest = Path.Combine(_options.OutputPath, "entry_no-states.tp"); File.WriteAllText(dest, result); _logger.LogInformation($"Generated '{dest}'."); } diff --git a/MSFSTouchPortalPlugin-Generator/MSFSTouchPortalPlugin-Generator.csproj b/MSFSTouchPortalPlugin-Generator/MSFSTouchPortalPlugin-Generator.csproj index 27aa131..a51bc27 100644 --- a/MSFSTouchPortalPlugin-Generator/MSFSTouchPortalPlugin-Generator.csproj +++ b/MSFSTouchPortalPlugin-Generator/MSFSTouchPortalPlugin-Generator.csproj @@ -6,15 +6,26 @@ MSFSTouchPortalPlugin_Generator ..\.sonarlint\msfstouchportalplugincsharp.ruleset x64 - 1.1.0 + 1.2.0 ..\build\$(Platform)-$(Configuration)\ + + x64 + 1701;1702 + DEBUG;TRACE + + + x64 1701;1702 none false + + + .dll + @@ -22,6 +33,7 @@ + diff --git a/MSFSTouchPortalPlugin-Generator/Model/Base.cs b/MSFSTouchPortalPlugin-Generator/Model/Base.cs index 5b8e8e0..096a6e5 100644 --- a/MSFSTouchPortalPlugin-Generator/Model/Base.cs +++ b/MSFSTouchPortalPlugin-Generator/Model/Base.cs @@ -8,7 +8,7 @@ class Base { [Required, Range(1, int.MaxValue)] public int Sdk { get; set; } = 3; [JsonConverter(typeof(VersionNumberJsonConverter))] - [Required, Range(1, int.MaxValue)] + [Required, Range(1, uint.MaxValue)] public uint Version { get; set; } [Required, MinLength(5)] public string Name { get; set; } = string.Empty; diff --git a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs index 57b945e..f0612d6 100644 --- a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs +++ b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs @@ -22,7 +22,8 @@ public class DocSetting { } public class DocCategory { - public MSFSTouchPortalPlugin.Enums.Groups Id { get; set; } + public MSFSTouchPortalPlugin.Enums.Groups CategoryId { get; set; } + public string Id { get; set; } public string Name { get; set; } public List Actions { get; set; } = new(); public List States { get; set; } = new(); diff --git a/MSFSTouchPortalPlugin-Generator/Program.cs b/MSFSTouchPortalPlugin-Generator/Program.cs index 1dfed5a..8d82531 100644 --- a/MSFSTouchPortalPlugin-Generator/Program.cs +++ b/MSFSTouchPortalPlugin-Generator/Program.cs @@ -1,33 +1,84 @@ -using Microsoft.Extensions.DependencyInjection; +using CommandLine; +using CommandLine.Text; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using MSFSTouchPortalPlugin.Configuration; using MSFSTouchPortalPlugin.Interfaces; using MSFSTouchPortalPlugin.Services; using MSFSTouchPortalPlugin_Generator.Configuration; using MSFSTouchPortalPlugin_Generator.Interfaces; +using System; +using System.IO; using System.Threading.Tasks; namespace MSFSTouchPortalPlugin_Generator { static class Program { + static async Task Main(string[] args) { - System.Environment.SetEnvironmentVariable("Logging:LogLevel:Microsoft", "Warning"); + System.Environment.SetEnvironmentVariable("Logging__LogLevel__Microsoft", "Warning"); // silence internal .net info messages + + // parse CLI options + GeneratorOptions opts = new(); + try { + var parser = new Parser(with => { with.HelpWriter = null; with.CaseSensitive = false; }); // Parser.Default + var parseResult = parser.ParseArguments(() => opts, args); + parseResult.WithNotParsed(errs => DisplayHelp(parseResult, errs)); + parser.Dispose(); + } + catch (Exception e) { + OnError($"Command line parsing failed with error: {e.Message}"); + } + + // validate options + try { opts.OutputPath = Path.GetFullPath(opts.OutputPath); } + catch (Exception e) { OnError($"TargetPath option error, not found or not accessible: {e.Message}"); } + if (!string.IsNullOrWhiteSpace(opts.StateFilesPath)) { + try { opts.StateFilesPath = Path.GetFullPath(opts.StateFilesPath); } + catch (Exception e) { OnError($"StateFilesPath option error, not found or not accessible: {e.Message}"); } + } + opts.ConfigVersion = Math.Clamp(opts.ConfigVersion, 1, 79); + await Host.CreateDefaultBuilder(args).ConfigureServices((context, services) => { services .AddLogging() - .Configure((opt) => { - opt.PluginName = "MSFSTouchPortalPlugin"; - opt.PluginFolder = "MSFS-TouchPortal-Plugin"; - if (args.Length >= 1) - opt.TargetPath = args[0]; - else - opt.TargetPath = ".\\"; // cwd - }) .AddHostedService() + .AddSingleton(opts) .AddSingleton() .AddSingleton(typeof(PluginConfig)) .AddSingleton() .AddSingleton(); }).RunConsoleAsync(); } + + static void OnError(string message, int exitCode = -1) { + Console.WriteLine(message); + Environment.Exit(exitCode); + } + + static void DisplayHelp(ParserResult result, System.Collections.Generic.IEnumerable errs) { + HelpText helpText = null; + if (errs.IsVersion()) { + OnError(HelpText.AutoBuild(result), 0); + } + + helpText = HelpText.AutoBuild(result, h => + { + h.MaximumDisplayWidth = 160; + //h.AdditionalNewLineAfterOption = false; + var tgt = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "TouchPortal", "plugins", "MSFS-TouchPortal-Plugin") + '\n'; + if (!errs.IsVersion()) { + h.AddPreOptionsText( + "\nUsage:\n" + + $" {AppDomain.CurrentDomain.FriendlyName} [-s ] [-c ] [] [-o ]\n\n" + + "Example: Generate with Default states plus a custom states file and put output in your plugin's installation folder.\n" + + $" {AppDomain.CurrentDomain.FriendlyName} -s Default,CustomStates -o {tgt}\n\n "); + h.AddPreOptionsLine("\nOptions:"); + } + + return HelpText.DefaultParsingErrorsHandler(result, h); + }, e => e); + OnError(helpText); + } + } } diff --git a/MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json b/MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json index 1ebc451..3f8aa97 100644 --- a/MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json +++ b/MSFSTouchPortalPlugin-Generator/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "MSFSTouchPortalPlugin-Generator": { "commandName": "Project", - "commandLineArgs": "$(SolutionDir)" + "commandLineArgs": "-o $(SolutionDir)" } } } \ No newline at end of file diff --git a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs index 44b5207..691b57a 100644 --- a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs +++ b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs @@ -19,9 +19,10 @@ internal static class PluginMapping { [TouchPortalSettingsContainer] public static class Settings { - public static readonly PluginSetting ConnectSimOnStartup = new PluginSetting("ConnectSimOnStartup", "1", DataType.Switch) { + public static readonly PluginSetting ConnectSimOnStartup = new PluginSetting("ConnectSimOnStartup", DataType.Switch) { Name = "Connect To Flight Sim on Startup (0/1)", Description = "Set to 1 to automatically attempt connection to flight simulator upon Touch Portal startup. Set to 0 to only connect manually via the provided Action.", + Default = "0", }; public static readonly PluginSetting UserStateFiles = new PluginSetting("UserStateFiles", DataType.Text) { From c43f6a5f19657bffb9666bd5708a8bd051a2e8df Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Mon, 4 Apr 2022 21:58:26 -0400 Subject: [PATCH 57/93] Add "Set Simulator Variable Value" action for settable SimVars, with option to release AI control first; * Includes new supporting methods in SimConnectService; * Add StringVal string constructor and MAX_SIZE constant; * Add SimVarItem.SetValue(string) and SetValue(double) will now convert to other numeric types if needed; * Fix bug in Units.IsRealType returning true for bool values. --- MSFSTouchPortalPlugin/Constants/Categories.cs | 10 +- MSFSTouchPortalPlugin/Constants/Units.cs | 8 +- .../Interfaces/ISimConnectService.cs | 7 +- .../Objects/Plugin/Plugin.cs | 11 ++ .../Services/PluginService.cs | 115 ++++++++++++------ .../Services/SimConnectService.cs | 57 ++++++++- MSFSTouchPortalPlugin/Types/SimVarItem.cs | 29 ++++- MSFSTouchPortalPlugin/Types/StringVal.cs | 5 +- 8 files changed, 186 insertions(+), 56 deletions(-) diff --git a/MSFSTouchPortalPlugin/Constants/Categories.cs b/MSFSTouchPortalPlugin/Constants/Categories.cs index 12157df..992634b 100644 --- a/MSFSTouchPortalPlugin/Constants/Categories.cs +++ b/MSFSTouchPortalPlugin/Constants/Categories.cs @@ -35,6 +35,13 @@ internal static string CategoryName(Groups catId) { return string.Empty; } + /// + /// Returns value with the category name prepended to it using the common separator string. + /// + internal static string PrependCategoryName(Groups catId, string value) { + return CategoryName(catId) + NameSeparator + value; + } + /// /// Returns category name with the CategoryPrefix added. Or just the CategoryPrefix if the category id is invalid. /// @@ -47,9 +54,6 @@ internal static string FullCategoryName(Groups catId) { /// /// Returns value with the full category name prepended to it using the common separator string. /// - /// - /// - /// internal static string PrependFullCategoryName(Groups catId, string value) { return FullCategoryName(catId) + NameSeparator + value; } diff --git a/MSFSTouchPortalPlugin/Constants/Units.cs b/MSFSTouchPortalPlugin/Constants/Units.cs index 3ba63e0..95d459d 100644 --- a/MSFSTouchPortalPlugin/Constants/Units.cs +++ b/MSFSTouchPortalPlugin/Constants/Units.cs @@ -14,19 +14,19 @@ internal static class Units { /// /// Returns true if the unit string corresponds to a string type. /// - internal static bool IsStringType(string unit) => unit == String; + internal static bool IsStringType(string unit) => unit.ToLower() == String; /// /// Returns true if the unit string corresponds to an integer type. /// - internal static bool IsIntegraltype(string unit) => _integralUnits.Contains(unit); + internal static bool IsIntegralType(string unit) => _integralUnits.Contains(unit); /// /// Returns true if the unit string corresponds to a boolean type. /// - internal static bool IsBooleantype(string unit) => _booleanUnits.Contains(unit); + internal static bool IsBooleanType(string unit) => _booleanUnits.Contains(unit); /// /// Returns true if the unit string corresponds to a real (float/double) type. /// - internal static bool IsRealType(string unit) => !IsStringType(unit) && !IsIntegraltype(unit); + internal static bool IsRealType(string unit) => !IsStringType(unit) && !IsBooleanType(unit) && !IsIntegralType(unit); internal const string amp = "amp"; internal const string ampere = "ampere"; diff --git a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs index 06aefe9..3eded8f 100644 --- a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs +++ b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs @@ -1,4 +1,5 @@ -using MSFSTouchPortalPlugin.Enums; +using Microsoft.FlightSimulator.SimConnect; +using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Types; using System; using System.Runtime.CompilerServices; @@ -23,7 +24,9 @@ internal interface ISimConnectService { void SetNotificationGroupPriorities(); void ClearAllDataDefinitions(); bool RegisterToSimConnect(SimVarItem simVar); - bool RequestDataOnSimObjectType(SimVarItem simVar); + bool RequestDataOnSimObjectType(SimVarItem simVar, SIMCONNECT_SIMOBJECT_TYPE objectType = SIMCONNECT_SIMOBJECT_TYPE.USER); bool TransmitClientEvent(Groups group, Enum eventId, uint data); + bool SetSimVar(SimVarItem simVar, uint objectId = (uint)SIMCONNECT_SIMOBJECT_TYPE.USER); + bool ReleaseAIControl(Definition def, uint objectId = (uint)SIMCONNECT_SIMOBJECT_TYPE.USER); } } diff --git a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs index 691b57a..9a4b5bd 100644 --- a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs +++ b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs @@ -13,6 +13,15 @@ internal static class PluginMapping { [TouchPortalActionMapping("Disconnect", "Off")] [TouchPortalActionMapping("ReloadStates", "Reload States")] public static readonly object Connection; + + + [TouchPortalAction("SetSimVar", "Set Simulator Variable Value", "MSFS", "Sets a value on any loaded State which is marked as settable.", "Set Variable {0} to {1} (release AI: {2})")] + [TouchPortalActionChoice(new[] { "" }, "")] + [TouchPortalActionText("0")] + //[TouchPortalActionChoice(new[] { "No", "Yes" })] + [TouchPortalActionSwitch(false)] + [TouchPortalActionMapping("SetSimVar")] + public static readonly object SetSimVar; } [TouchPortalCategory(Groups.Plugin)] @@ -94,6 +103,8 @@ internal enum Plugin : short { ActionRepeatIntervalInc, ActionRepeatIntervalDec, ActionRepeatIntervalSet, + + SetSimVar, } // Dynamically generated SimConnect client event IDs are "parented" to this enum type, diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 273e4e6..3da454e 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -45,6 +45,7 @@ internal class PluginService : IPluginService, ITouchPortalEventHandler private Dictionary statesDictionary = new(); private Dictionary pluginSettingsDictionary = new(); private readonly ConcurrentBag _customIntervalStates = new(); // IDs of SimVars which need periodic value polling; concurrent because the polling happens in separate task from setter. + private readonly Dictionary _settableSimVarIds = new(); // mapping of settable SimVar IDs for lookup in statesDictionary private readonly List _dynamicStateIds = new(); // keep track of dynamically created states for clearing them if/when reloading sim var state files private readonly ConcurrentDictionary _repeatingActionTimers = new(); @@ -177,6 +178,17 @@ private async Task PluginEventsTask() { _logger.LogDebug("PluginEventsTask task stopped."); } + // runs in PluginEventsTask + private void CheckPendingRequests() { + foreach (var def in _customIntervalStates) { + // Check if a value update is required based on the SimVar's internal tracking mechanism. + if (statesDictionary.TryGetValue(def, out var s) && s.UpdateRequired) { + s.SetPending(true); + _simConnectService.RequestDataOnSimObjectType(s); + } + } + } + #endregion Startup, Shutdown and Processing Tasks #region SimConnect Events ///////////////////////////////////// @@ -273,6 +285,7 @@ private void SetupSimVars() { // clear out any old data _customIntervalStates.Clear(); + _settableSimVarIds.Clear(); RemoveDynamicStates(); // if any _simConnectService.ClearAllDataDefinitions(); // if any (SimConnectService keeps track of this internally) @@ -281,6 +294,8 @@ private void SetupSimVars() { // Does this var need value polling? if (simVar.NeedsScheduledRequest) _customIntervalStates.Add(simVar.Def); + if (simVar.CanSet) + _settableSimVarIds.Add(simVar.Id, simVar.Def); // Need a dynamic state? if (allStatesDynamic || (someStateDynamic && !defaultStates.Contains(simVar.Id))) { _dynamicStateIds.Add(simVar.TouchPortalStateId); // keep track for removing them later if needed @@ -290,6 +305,8 @@ private void SetupSimVars() { // Register it. If the SimVar gets regular updates (not custom polling) then this also starts the data requests for this value. _simConnectService.RegisterToSimConnect(simVar); } + + UpdateSettableStatesList(); } private void RemoveDynamicStates() { @@ -297,12 +314,15 @@ private void RemoveDynamicStates() { _client.RemoveState(id); } - private void CheckPendingRequests() { - foreach (var def in _customIntervalStates) { - // Check if a value update is required based on the SimVar's internal tracking mechanism. - if (statesDictionary.TryGetValue(def, out var s) && s.UpdateRequired) { - s.SetPending(true); - _simConnectService.RequestDataOnSimObjectType(s); + private void UpdateSettableStatesList() { + var list = (from s in statesDictionary.Values where s.CanSet select Categories.PrependCategoryName(s.CategoryId, s.Name) + $" [{s.Id}]").OrderBy(n => n).ToArray(); + _client.ChoiceUpdate(PluginId + ".Plugin.Action.SetSimVar.Data.0", list); + } + + private void ClearRepeatingActions() { + foreach (var act in _repeatingActionTimers) { + if (_repeatingActionTimers.TryRemove(act.Key, out var tim)) { + tim.Dispose(); } } } @@ -369,6 +389,29 @@ private void ProcessInternalEvent(ActionEventType action, Enum eventId, in strin } break; + case Plugin.SetSimVar: + if (dataArry.Length > 2 && dataArry[0].EndsWith(']') && (dataArry[0].IndexOf('[') is var brIdx && brIdx++ > -1)) { + var varId = dataArry[0][brIdx..^1]; + if (!_settableSimVarIds.TryGetValue(varId, out Definition def) || !statesDictionary.TryGetValue(def, out SimVarItem simVar)) { + _logger.LogWarning($"Could not find definition for settable SimVar Id: '{varId}' Name: '{dataArry[0]}'"); + break; + } + if (simVar.IsStringType) { + if (!simVar.SetValue(new StringVal(dataArry[1]))) { + _logger.LogWarning($"Could not set string value '{dataArry[1]}' for SimVar Id: '{varId}' Name: '{dataArry[0]}'"); + break; + } + } + else if (!TryEvaluateValue(dataArry[1], out double dVal) || !simVar.SetValue(dVal)) { + _logger.LogWarning($"Could not set numeric value '{dataArry[1]}' for SimVar Id: '{varId}' Name: '{dataArry[0]}'"); + break; + } + if (new BooleanString(dataArry[2]) && !_simConnectService.ReleaseAIControl(simVar.Def)) + break; + _simConnectService.SetSimVar(simVar); + } + break; + default: // No other types of events supported right now. break; @@ -377,44 +420,34 @@ private void ProcessInternalEvent(ActionEventType action, Enum eventId, in strin private void ProcessSimEvent(ActionEventType action, Enum eventId, in string[] dataArry) { uint dataUint = 0; - string eventName = _reflectionService.GetSimEventNameById(eventId); // just for logging if (action.ValueIndex > -1 && action.ValueIndex < dataArry.Length) { double dataReal = double.NaN; var valStr = dataArry[action.ValueIndex]; - try { - switch (action.ValueType) { - case DataType.Number: - case DataType.Text: - dataReal = Convert.ToDouble(_expressionEvaluator.Compute(valStr, null)); - break; - case DataType.Switch: - dataReal = new BooleanString(valStr); - break; - } - if (!double.IsNaN(dataReal)) { - if (!double.IsNaN(action.MinValue)) - dataReal = Math.Max(dataReal, action.MinValue); - if (!double.IsNaN(action.MaxValue)) - dataReal = Math.Min(dataReal, action.MaxValue); - dataUint = (uint)Math.Round(dataReal); - } + switch (action.ValueType) { + case DataType.Number: + case DataType.Text: + if (!TryEvaluateValue(valStr, out dataReal)) { + _logger.LogWarning($"Data conversion failed for action '{action.ActionId}' on sim event '{_reflectionService.GetSimEventNameById(eventId)}'."); + return; + } + dataReal = Convert.ToDouble(_expressionEvaluator.Compute(valStr, null)); + break; + case DataType.Switch: + dataReal = new BooleanString(valStr); + break; } - catch (Exception e) { - _logger.LogWarning(e, $"Action {action.ActionId} for sim event {eventName} with data string '{valStr}' - Failed to convert data to numeric value."); + if (!double.IsNaN(dataReal)) { + if (!double.IsNaN(action.MinValue)) + dataReal = Math.Max(dataReal, action.MinValue); + if (!double.IsNaN(action.MaxValue)) + dataReal = Math.Min(dataReal, action.MaxValue); + dataUint = (uint)Math.Round(dataReal); } } - _logger.LogDebug($"Firing Sim Event - action: {action.ActionId}; category: {action.CategoryId}; name: {eventName}; data {dataUint}"); + _logger.LogDebug($"Firing Sim Event - action: {action.ActionId}; category: {action.CategoryId}; name: {_reflectionService.GetSimEventNameById(eventId)}; data {dataUint}"); _simConnectService.TransmitClientEvent(action.CategoryId, eventId, dataUint); } - private void ClearRepeatingActions() { - foreach (var act in _repeatingActionTimers) { - if (_repeatingActionTimers.TryRemove(act.Key, out var tim)) { - tim.Dispose(); - } - } - } - /// /// Handles an array of `Setting` types sent from TP. This could come from either the /// initial `OnInfoEvent` message, or the dedicated `OnSettingsEvent` message. @@ -447,6 +480,18 @@ private void UpdateSimConnectState() { _client.StateUpdate(PluginId + ".Plugin.State.Connected", stat); } + private bool TryEvaluateValue(string strValue, out double value) { + value = double.NaN; + try { + value = Convert.ToDouble(_expressionEvaluator.Compute(strValue, null)); + } + catch (Exception e) { + _logger.LogWarning(e, $"Failed to convert data value '{strValue}' to numeric."); + return false; + } + return true; + } + #endregion Plugin Events and Handlers #region TouchPortalSDK Events /////////////////////////////// diff --git a/MSFSTouchPortalPlugin/Services/SimConnectService.cs b/MSFSTouchPortalPlugin/Services/SimConnectService.cs index 52e1267..6c04129 100644 --- a/MSFSTouchPortalPlugin/Services/SimConnectService.cs +++ b/MSFSTouchPortalPlugin/Services/SimConnectService.cs @@ -122,7 +122,7 @@ public void Disconnect() { OnDisconnect?.Invoke(); } - public void ReceiveMessages() { + private void ReceiveMessages() { _logger.LogDebug("ReceiveMessages task started."); try { while (_connected) { @@ -184,8 +184,13 @@ public void SetNotificationGroupPriorities() { } private void ClearDataDefinition(Definition def) { - _simConnect.ClearDataDefinition(def); - DbgAddSendRecord($"ClearDataDefinition({def})"); + try { + _simConnect.ClearDataDefinition(def); + DbgAddSendRecord($"ClearDataDefinition({def})"); + } + catch (Exception e) { + _logger.LogError(e, $"ClearDataDefinition({def}) failed."); + } } public bool RegisterToSimConnect(SimVarItem simVar) { @@ -234,10 +239,10 @@ public void ClearAllDataDefinitions() { _addedDefinitions.Clear(); } - public bool RequestDataOnSimObjectType(SimVarItem simVar) { + public bool RequestDataOnSimObjectType(SimVarItem simVar, SIMCONNECT_SIMOBJECT_TYPE objectType = SIMCONNECT_SIMOBJECT_TYPE.USER) { if (_connected) { try { - _simConnect.RequestDataOnSimObjectType(simVar.Def, simVar.Def, 0, SIMCONNECT_SIMOBJECT_TYPE.USER); + _simConnect.RequestDataOnSimObjectType(simVar.Def, simVar.Def, 0, objectType); DbgAddSendRecord($"RequestDataOnSimObjectType({simVar.ToDebugString()})"); return true; } @@ -250,6 +255,44 @@ public bool RequestDataOnSimObjectType(SimVarItem simVar) { return false; } + /// + /// Set the value associated with a SimVar + /// + public bool SetSimVar(SimVarItem simVar, uint objectId = (uint)SIMCONNECT_SIMOBJECT_TYPE.USER) { + if (!_connected) + return false; + try { + _simConnect.SetDataOnSimObject(simVar.Def, objectId, SIMCONNECT_DATA_SET_FLAG.DEFAULT, simVar.Value); + DbgAddSendRecord($"SetDataOnSimObject({simVar.ToDebugString()})"); + return true; + } + catch (Exception e) { + _logger.LogError(e, $"SetSimVar({simVar.ToDebugString()}) failed."); + } + return false; + } + + /// + /// Clear the AI control of a simulated object, typically an aircraft, in order for it to be controlled by a SimConnect client. + /// + /// The previously-registered data definition ID of the variable to release. + /// True on request success, false otherwise (this is the status of transmitting the command, not whether control was actually released). + public bool ReleaseAIControl(Definition def, uint objectId = (uint)SIMCONNECT_SIMOBJECT_TYPE.USER) { + if (!_connected) + return false; + try { + _simConnect.AIReleaseControl(objectId, def); + DbgAddSendRecord($"AIReleaseControl({def})"); + return true; + } + catch (Exception e) { + _logger.LogError(e, $"ReleaseAIControl({def}) failed."); + } + return false; + } + + #region SimConnect Event Handlers + private void Simconnect_OnRecvQuit(SimConnect sender, SIMCONNECT_RECV data) { _logger.LogInformation("Received shutdown command from SimConnect, disconnecting."); Disconnect(); @@ -290,6 +333,8 @@ private void Simconnect_OnRecvEvent(SimConnect sender, SIMCONNECT_RECV_EVENT dat _logger.LogDebug($"Simconnect_OnRecvEvent Recieved: Group: {grpName}; Event: {eventId}"); } + #endregion SimConnect Event Handlers + #region IDisposable Support private bool disposedValue; // To detect redundant calls @@ -313,7 +358,7 @@ public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } - #endregion + #endregion IDisposable Support #region Request Debugging diff --git a/MSFSTouchPortalPlugin/Types/SimVarItem.cs b/MSFSTouchPortalPlugin/Types/SimVarItem.cs index 1679cdb..ef4824b 100644 --- a/MSFSTouchPortalPlugin/Types/SimVarItem.cs +++ b/MSFSTouchPortalPlugin/Types/SimVarItem.cs @@ -55,8 +55,8 @@ public string Unit _unit = value; // set up type information based on the new Unit. IsStringType = Units.IsStringType(_unit); - IsBooleanType = !IsStringType && Units.IsBooleantype(_unit); - IsIntegralType = !IsStringType && !IsBooleanType && Units.IsIntegraltype(_unit); + IsBooleanType = !IsStringType && Units.IsBooleanType(_unit); + IsIntegralType = !IsStringType && !IsBooleanType && Units.IsIntegralType(_unit); IsRealType = !IsStringType && !IsBooleanType && !IsIntegralType; SimConnectDataType = IsStringType ? SIMCONNECT_DATATYPE.STRING256 : IsIntegralType ? SIMCONNECT_DATATYPE.INT64 : IsBooleanType ? SIMCONNECT_DATATYPE.INT32 : SIMCONNECT_DATATYPE.FLOAT64; StorageDataType = IsStringType ? typeof(StringVal) : IsIntegralType ? typeof(long) : IsBooleanType ? typeof(uint) : typeof(double); @@ -203,9 +203,15 @@ internal bool SetValue(StringVal value) { } internal bool SetValue(double value) { - if (!IsStringType) + if (IsStringType) + return false; + if (IsRealType) Value = value; - return !IsStringType; + else if (IsBooleanType) + Value = ((long)value != 0); + else + Value = (long)value; + return true; } internal bool SetValue(long value) { @@ -220,6 +226,19 @@ internal bool SetValue(uint value) { return IsBooleanType; } + internal bool SetValue(string value) { + switch (Value) { + case string: + return SetValue(new StringVal(value)); + case uint: + return SetValue((uint)new BooleanString(value)); + case double: + case long: + return double.TryParse(value, out var dVal) && SetValue(dVal); + } + return false; + } + /// /// Prefer using this method, or one of the type-specific SetValue() overloads to /// to set the Value property, vs. direct access. Returns false if the given object's @@ -232,7 +251,7 @@ internal bool SetValue(object value) { uint v => SetValue(v), long v => SetValue(v), StringVal v => SetValue(v), - _ => false + _ => SetValue(value.ToString()) }; } catch { diff --git a/MSFSTouchPortalPlugin/Types/StringVal.cs b/MSFSTouchPortalPlugin/Types/StringVal.cs index afbc235..661dff8 100644 --- a/MSFSTouchPortalPlugin/Types/StringVal.cs +++ b/MSFSTouchPortalPlugin/Types/StringVal.cs @@ -5,9 +5,12 @@ namespace MSFSTouchPortalPlugin.Types { internal readonly struct StringVal : IEquatable { - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_SIZE)] public readonly string Value; + const int MAX_SIZE = 256; + + public StringVal(string value = default) { Value = (value.Length > MAX_SIZE ? value.Substring(0, MAX_SIZE) : value); } public bool Equals(StringVal other) => other.Value == Value; public override bool Equals(object obj) => (obj is StringVal && Equals((StringVal)obj)); public override string ToString() => Value; From bdb13a0994ab84adb016491b0d600d5d4ab357c3 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Mon, 4 Apr 2022 22:07:08 -0400 Subject: [PATCH 58/93] [Tests] Fix PluginServiceTests for new PluginService c'tor signature; Add some more Units tests. --- .../Constants/UnitsTests.cs | 28 +++++++++++++++++-- .../Services/PluginServiceTests.cs | 24 +++++++++------- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/MSFSTouchPortalPlugin-Tests/Constants/UnitsTests.cs b/MSFSTouchPortalPlugin-Tests/Constants/UnitsTests.cs index 77143b0..fe0f28a 100644 --- a/MSFSTouchPortalPlugin-Tests/Constants/UnitsTests.cs +++ b/MSFSTouchPortalPlugin-Tests/Constants/UnitsTests.cs @@ -4,7 +4,7 @@ namespace MSFSTouchPortalPlugin_Tests.Constants { public class UnitsTests { [Fact] - public void ShouldConvertToFloat_ShouldReturnTrue() { + public void IsRealType_ShouldReturnTrue() { // arrange // act var result = Units.IsRealType(Units.degrees); @@ -13,7 +13,7 @@ public void ShouldConvertToFloat_ShouldReturnTrue() { } [Fact] - public void ShouldConvertToFloat_ShouldReturnFalse() { + public void IsRealType_ShouldReturnFalse() { // arrange // act var result = Units.IsRealType(Units.Boolean); @@ -21,5 +21,29 @@ public void ShouldConvertToFloat_ShouldReturnFalse() { Assert.False(result); } + [Fact] + public void IsStringType_ShouldReturnTrue() { + var result = Units.IsStringType(Units.String); + Assert.True(result); + } + + [Fact] + public void IsIntegralType_ShouldReturnTrue() { + var result = Units.IsIntegralType(Units.position16k); + Assert.True(result); + } + + [Fact] + public void IsIntegralType_ShouldReturnFalse() { + var result = Units.IsIntegralType(Units.Bool); + Assert.False(result); + } + + [Fact] + public void IsBooleanType_ShouldReturnTrue() { + var result = Units.IsBooleanType(Units.Boolean); + Assert.True(result); + } + } } diff --git a/MSFSTouchPortalPlugin-Tests/Services/PluginServiceTests.cs b/MSFSTouchPortalPlugin-Tests/Services/PluginServiceTests.cs index 52c70f0..32ed62f 100644 --- a/MSFSTouchPortalPlugin-Tests/Services/PluginServiceTests.cs +++ b/MSFSTouchPortalPlugin-Tests/Services/PluginServiceTests.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging.Abstractions; using Moq; +using MSFSTouchPortalPlugin.Configuration; using MSFSTouchPortalPlugin.Interfaces; using MSFSTouchPortalPlugin.Services; using System; @@ -22,14 +23,13 @@ public void Constructor_ShouldCreateAndDispose() { mockITouchPortalClientFactory.Setup(c => c.Create(It.IsAny())).Returns(mockITouchPortalClient.Object); var mockISimConnectService = new Mock(); var mockIReflectionService = new Mock(); + var mockPluginConfig = new Mock(new object[] { new NullLogger() }); // act - var service = new PluginService(mockHostApplicationLifetime.Object, mockILoggerPluginService, mockITouchPortalClientFactory.Object, mockISimConnectService.Object, mockIReflectionService.Object); + var service = new PluginService(mockHostApplicationLifetime.Object, mockILoggerPluginService, mockITouchPortalClientFactory.Object, mockISimConnectService.Object, mockIReflectionService.Object, mockPluginConfig.Object); // assert Assert.NotNull(service); - - service.Dispose(); } [Fact] @@ -40,13 +40,15 @@ public void Constructor_ShouldThrowArgumentNull() { var mockITouchPortalClientFactory = new Mock(); var mockISimConnectService = new Mock(); var mockIReflectionService = new Mock(); + var mockPluginConfig = new Mock(new object[] { new NullLogger() }); // act/assert - Assert.Throws(() => new PluginService(null, mockILoggerPluginService, mockITouchPortalClientFactory.Object, mockISimConnectService.Object, mockIReflectionService.Object)); - Assert.Throws(() => new PluginService(mockHostApplicationLifetime.Object, null, mockITouchPortalClientFactory.Object, mockISimConnectService.Object, mockIReflectionService.Object)); - Assert.Throws(() => new PluginService(mockHostApplicationLifetime.Object, mockILoggerPluginService, null, mockISimConnectService.Object, mockIReflectionService.Object)); - Assert.Throws(() => new PluginService(mockHostApplicationLifetime.Object, mockILoggerPluginService, mockITouchPortalClientFactory.Object, null, mockIReflectionService.Object)); - Assert.Throws(() => new PluginService(mockHostApplicationLifetime.Object, mockILoggerPluginService, mockITouchPortalClientFactory.Object, mockISimConnectService.Object, null)); + Assert.Throws(() => new PluginService(null, mockILoggerPluginService, mockITouchPortalClientFactory.Object, mockISimConnectService.Object, mockIReflectionService.Object, mockPluginConfig.Object)); + Assert.Throws(() => new PluginService(mockHostApplicationLifetime.Object, null, mockITouchPortalClientFactory.Object, mockISimConnectService.Object, mockIReflectionService.Object, mockPluginConfig.Object)); + Assert.Throws(() => new PluginService(mockHostApplicationLifetime.Object, mockILoggerPluginService, null, mockISimConnectService.Object, mockIReflectionService.Object, mockPluginConfig.Object)); + Assert.Throws(() => new PluginService(mockHostApplicationLifetime.Object, mockILoggerPluginService, mockITouchPortalClientFactory.Object, null, mockIReflectionService.Object, mockPluginConfig.Object)); + Assert.Throws(() => new PluginService(mockHostApplicationLifetime.Object, mockILoggerPluginService, mockITouchPortalClientFactory.Object, mockISimConnectService.Object, null, mockPluginConfig.Object)); + Assert.Throws(() => new PluginService(mockHostApplicationLifetime.Object, mockILoggerPluginService, mockITouchPortalClientFactory.Object, mockISimConnectService.Object, mockIReflectionService.Object, null)); } [Fact] @@ -59,9 +61,10 @@ public void PluginId_ShouldBeExpected() { mockITouchPortalClientFactory.Setup(c => c.Create(It.IsAny())).Returns(mockITouchPortalClient.Object); var mockISimConnectService = new Mock(); var mockIReflectionService = new Mock(); + var mockPluginConfig = new Mock(new object[] { new NullLogger() }); // act - var service = new PluginService(mockHostApplicationLifetime.Object, mockILoggerPluginService, mockITouchPortalClientFactory.Object, mockISimConnectService.Object, mockIReflectionService.Object); + var service = new PluginService(mockHostApplicationLifetime.Object, mockILoggerPluginService, mockITouchPortalClientFactory.Object, mockISimConnectService.Object, mockIReflectionService.Object, mockPluginConfig.Object); // assert Assert.Equal("MSFSTouchPortalPlugin", service.PluginId); @@ -77,9 +80,10 @@ public async Task StopAsync_ShouldComplete() { mockITouchPortalClientFactory.Setup(c => c.Create(It.IsAny())).Returns(mockITouchPortalClient.Object); var mockISimConnectService = new Mock(); var mockIReflectionService = new Mock(); + var mockPluginConfig = new Mock(new object[] { new NullLogger() }); // act - var service = new PluginService(mockHostApplicationLifetime.Object, mockILoggerPluginService, mockITouchPortalClientFactory.Object, mockISimConnectService.Object, mockIReflectionService.Object); + var service = new PluginService(mockHostApplicationLifetime.Object, mockILoggerPluginService, mockITouchPortalClientFactory.Object, mockISimConnectService.Object, mockIReflectionService.Object, mockPluginConfig.Object); Assert.NotNull(service); await service.StopAsync(new CancellationToken()); From 6c7c7478a2e86ac871eaf1b6a5d2d1438afe00cb Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Mon, 4 Apr 2022 22:10:13 -0400 Subject: [PATCH 59/93] [Generator] Add "Can Set" column/flag to States lists; Add hidden "debug" option to generate entry.tp w/out startup command line entry. --- .../Configuration/GeneratorOptions.cs | 4 ++++ MSFSTouchPortalPlugin-Generator/GenerateDoc.cs | 10 ++++++---- MSFSTouchPortalPlugin-Generator/GenerateEntry.cs | 6 ++++-- MSFSTouchPortalPlugin-Generator/Model/Base.cs | 4 +++- MSFSTouchPortalPlugin-Generator/Model/DocBase.cs | 1 + 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs b/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs index 3e00f0f..98f74e0 100644 --- a/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs +++ b/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs @@ -40,5 +40,9 @@ internal class GeneratorOptions [Option('f', "PluginFolder", Default = "MSFS-TouchPortal-Plugin", MetaValue = "", Required = false, HelpText = "\nName of the plugin's folder once installed to TP.")] public string PluginFolder { get; set; } + + [Option('d', "debug", Hidden = true, Default = false, Required = false, + HelpText = "Enables generating a 'debug' version of entry.tp. Currently that means w/out a startup command.")] + public bool Debug { get; set; } } } diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index 60ede01..bcd847f 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -157,7 +157,8 @@ private DocBase CreateModel() { DefaultValue = state.DefaultValue ?? string.Empty, SimVarName = state.SimVarName, Unit = state.Unit, - FormattingString = state.FormattingString + FormattingString = state.FormattingString, + CanSet = state.CanSet, }; newCat.States.Add(newState); } @@ -277,10 +278,11 @@ private static string CreateMarkdown(DocBase model) { // Loop States s.Append("### States\n\n"); s.Append($" **Base Id:** {cat.Id}.State.\n\n"); - s.Append("| Id | SimVar Name | Description | Unit | Format | DefaultValue |\n"); - s.Append("| --- | --- | --- | --- | --- | --- |\n"); + s.Append("| Id | SimVar Name | Description | Unit | Format | DefaultValue | Can Set |\n"); + s.Append("| --- | --- | --- | --- | --- | --- | --- |\n"); cat.States.ForEach(state => { - s.Append($"| {state.Id} | {state.SimVarName} | {state.Description} | {state.Unit} | {state.FormattingString} | {state.DefaultValue} |\n"); + s.Append($"| {state.Id} | {state.SimVarName} | {state.Description} | {state.Unit} | {state.FormattingString} | {state.DefaultValue}"); + s.Append($"| {(state.CanSet ? "☑" : "")} |\n"); // U+2611 Ballot Box with Check Emoji }); s.Append("\n\n"); } diff --git a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs index 5bb52a9..4875923 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs @@ -87,9 +87,11 @@ public void Generate() { Sdk = 3, Version = vNum, Name = _options.PluginName, - Id = _options.PluginId, - Plugin_start_cmd = $"{basePath}dist/{_options.PluginId}.exe" + Id = _options.PluginId }; + if (!_options.Debug) + model.Plugin_start_cmd = $"{basePath}dist/{_options.PluginId}.exe"; + // Get all classes with the TouchPortalCategory var categoryClasses = q.Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalCategoryAttribute))).OrderBy(o => o.Name); diff --git a/MSFSTouchPortalPlugin-Generator/Model/Base.cs b/MSFSTouchPortalPlugin-Generator/Model/Base.cs index 096a6e5..eff1561 100644 --- a/MSFSTouchPortalPlugin-Generator/Model/Base.cs +++ b/MSFSTouchPortalPlugin-Generator/Model/Base.cs @@ -16,7 +16,9 @@ class Base { public string Id { get; set; } = string.Empty; [Required, ValidateObject] public Configuration Configuration { get; set; } = new Configuration(); - public string Plugin_start_cmd { get; set; } = string.Empty; + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [DefaultValue(null)] + public string Plugin_start_cmd { get; set; } = null; [ValidateObject] public List Categories { get; set; } = new(); public List Settings { get; set; } = new(); diff --git a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs index f0612d6..63aab7b 100644 --- a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs +++ b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs @@ -63,5 +63,6 @@ public class DocState { public string SimVarName { get; set; } public string Unit { get; set; } public string FormattingString { get; set; } + public bool CanSet { get; set; } } } From 2f9f6296af5b4a2f831dc31241e7384856ed278e Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Tue, 5 Apr 2022 02:58:34 -0400 Subject: [PATCH 60/93] Consolidate attribute meta data collection to ReflectionService with new methods GetCategoryAttributes() and GetActionAttributes(), which are now used internally and by Generator. Adds some more properties to attributes to facilitate data exchange. --- .../GenerateDoc.cs | 169 +++++++++--------- .../GenerateEntry.cs | 84 +++------ .../Attributes/TouchPortalActionAttribute.cs | 47 ++--- .../TouchPortalActionMappingAttribute.cs | 7 +- .../TouchPortalCategoryAttribute.cs | 11 +- .../Interfaces/IReflectionService.cs | 6 +- .../Services/ReflectionService.cs | 86 ++++++--- 7 files changed, 204 insertions(+), 206 deletions(-) diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index bcd847f..5f3793f 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -48,19 +48,11 @@ public void Generate() { } private DocBase CreateModel() { - // Find assembly - var a = Assembly.GetExecutingAssembly().GetReferencedAssemblies().FirstOrDefault(a => a.Name == _options.PluginId); - if (a == null) { - _logger.LogError($"Unable to load assembly '{_options.PluginId}' for reflection.'"); - return null; - } - - var assembly = Assembly.Load(a); - var assemblyList = assembly.GetTypes().ToList(); - - VersionInfo.Assembly = assembly; + // set plugin file location for version info + VersionInfo.AssemblyLocation = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), _options.PluginId + ".dll"); + // create the base model var model = new DocBase { Title = _options.PluginName + " Documentation", Overview = "This plugin will provide a two-way interface between Touch Portal and Flight Simulators which use SimConnect, such as Microsoft Flight Simulator 2020 and FS-X.", @@ -83,72 +75,59 @@ private DocBase CreateModel() { // always include the internal plugin states simVars = simVars.Concat(_pluginConfig.LoadPluginStates()); - // Get all classes with the TouchPortalCategory Attribute - var classList = assemblyList.Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalCategoryAttribute))).OrderBy(o => o.Name); - - // Loop through categories - foreach (var cat in classList) { - var catAttr = (TouchPortalCategoryAttribute)Attribute.GetCustomAttribute(cat, typeof(TouchPortalCategoryAttribute)); - Groups catId = catAttr.Id; - var newCat = model.Categories.FirstOrDefault(c => c.CategoryId == catId); - if (newCat == null) { - newCat = new DocCategory { - CategoryId = catId, - Id = $"{_options.PluginId}.{catId}", - Name = Categories.FullCategoryName(catId), - }; - model.Categories.Add(newCat); - } - - // Loop through Actions - var actions = cat.GetMembers().Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalActionAttribute))).ToList(); - actions.ForEach(act => { - var actionAttribute = act.GetCustomAttribute(); - var newAct = new DocAction { - Name = actionAttribute.Name, - Description = actionAttribute.Description, - Type = actionAttribute.Type, - Format = actionAttribute.Format, - HasHoldFunctionality = actionAttribute.HasHoldFunctionality + var categegoryAttribs = _reflectionSvc.GetCategoryAttributes(); + foreach (var catAttrib in categegoryAttribs) { + var category = new DocCategory { + CategoryId = catAttrib.Id, + Id = $"{_options.PluginId}.{catAttrib.Id}", + Name = catAttrib.Name + }; + model.Categories.Add(category); + + // workaround for backwards compat with mis-named actions in category InstrumentsSystems.Fuel + string actionCatId = _options.PluginId + "." + Categories.ActionCategoryId(catAttrib.Id); + + foreach (var actionAttrib in catAttrib.Actions) { + var action = new DocAction { + Name = actionAttrib.Name, + Description = actionAttrib.Description, + Type = actionAttrib.Type, + Format = actionAttrib.Format, + HasHoldFunctionality = actionAttrib.HasHoldFunctionality, }; - - // Loop through Action Data - var dataAttributes = act.GetCustomAttributes(); - foreach (var attrib in dataAttributes) { - var data = new DocActionData { - Type = attrib.Type, - DefaultValue = attrib.GetDefaultValue()?.ToString(), - Values = attrib.ChoiceValues != null ? string.Join(", ", attrib.ChoiceValues) : "", - MinValue = attrib.MinValue, - MaxValue = attrib.MaxValue, - AllowDecimals = attrib.AllowDecimals - }; - newAct.Data.Add(data); - } - - // Loop through Action mappings - var mapAttribs = act.GetCustomAttributes(); - foreach (var attrib in mapAttribs) { + category.Actions.Add(action); + + if (actionAttrib.Data.Any()) { + foreach (var attrib in actionAttrib.Data) { + var data = new DocActionData { + Type = attrib.Type, + DefaultValue = attrib.GetDefaultValue()?.ToString(), + Values = attrib.ChoiceValues != null ? string.Join(", ", attrib.ChoiceValues) : "", + MinValue = attrib.MinValue, + MaxValue = attrib.MaxValue, + AllowDecimals = attrib.AllowDecimals, + }; + + action.Data.Add(data); + } + } // action data + + foreach (var attrib in actionAttrib.Mappings) { var map = new DocActionMapping { ActionId = attrib.ActionId, Values = attrib.Values, }; - newAct.Mappings.Add(map); + action.Mappings.Add(map); } // Warn about missing mappings - if (!mapAttribs.Any()) - _logger.LogWarning($"No event mappings found for action ID '{actionAttribute.Id}' in category '{cat.Name}'"); + if (!actionAttrib.Mappings.Any()) + _logger.LogWarning($"No event mappings found for action ID '{actionAttrib.Id}' in category '{category.Name}'"); + } // actions - newCat.Actions.Add(newAct); - }); - - newCat.Actions = newCat.Actions.OrderBy(c => c.Name).ToList(); + category.Actions = category.Actions.OrderBy(c => c.Name).ToList(); // Loop through States - if (newCat.States.Any()) - continue; // skip if already added for this category - - var categoryStates = simVars.Where(s => s.CategoryId == catId); + var categoryStates = simVars.Where(s => s.CategoryId == catAttrib.Id); foreach (SimVarItem state in categoryStates) { var newState = new DocState { Id = state.TouchPortalStateId.Split('.').Last(), @@ -160,11 +139,11 @@ private DocBase CreateModel() { FormattingString = state.FormattingString, CanSet = state.CanSet, }; - newCat.States.Add(newState); + category.States.Add(newState); } // Sort the states - newCat.States = newCat.States.OrderBy(c => c.Id).ToList(); + category.States = category.States.OrderBy(c => c.Id).ToList(); } // categories loop // Settings @@ -198,8 +177,9 @@ private static string CreateMarkdown(DocBase model) { // Table of Contents s.Append("## Table of Contents\n\n"); s.Append("[Plugin Settings](#plugin-settings)\n\n"); + s.Append("[Actions and States by Category](#actions-and-states-by-category)\n\n"); model.Categories.ForEach(cat => { - s.Append($"[{cat.Name}](#{cat.Name.Replace(" ", "-").ToLower()})\n\n"); + s.Append($"* [{cat.Name}](#{cat.Name.Replace(" ", "-").ToLower()})\n\n"); }); s.Append("---\n\n"); @@ -226,17 +206,24 @@ private static string CreateMarkdown(DocBase model) { }); s.Append("\n\n---\n\n"); + s.Append("## Actions and States by Category\n\n"); + // Loop Categories model.Categories.ForEach(cat => { - s.Append($"## {cat.Name}\n
Click to expand\n\n"); + s.Append($"### {cat.Name}\n
Click to expand\n\n"); // Loop Actions if (cat.Actions.Count > 0) { - s.Append("### Actions\n\n"); + s.Append("#### Actions\n\n"); s.Append("\n"); // use HTML table for row valign attribute - s.Append("" + + s.Append("" + + "" + + "" + + "" + "" + - "\n"); + (cat.CategoryId != Groups.Plugin ? "" : "") + + "" + + "\n"); cat.Actions.ForEach(act => { s.Append($""); // Loop action data @@ -257,26 +244,32 @@ private static string CreateMarkdown(DocBase model) { s.Append($" <max: {ad.MaxValue.ToString($"F{prec}")}>"); // seriously... printf("%.*f", prec, val) anyone? s.Append("\n"); }); - s.Append($"\n\n\n"); // U+2611 Ballot Box with Check Emoji + s.Append("\n"); + // mappings (only for SimConnect events) + if (cat.CategoryId != Groups.Plugin) { + s.Append("\n"); + } + // has hold + s.Append($"\n"); // U+2611 Ballot Box with Check Emoji }); s.Append("
NameDescriptionFormat
NameDescriptionFormatData
index.   [type]     choices/default (in bold)
Sim Event(s)On
Hold
Sim Event(s)On
Hold
{act.Name}{act.Description}{act.Format}"); - if (act.Mappings.Count > 2) // collapsible only if more than 2 items - s.Append($"
details"); - s.Append("
"); - act.Mappings.ForEach(am => { - if (am.Values?.Length > 0) - s.Append($"
{string.Join("+", am.Values)}
"); - s.Append($"
{am.ActionId}
"); - }); - s.Append($"
"); - if (act.Mappings.Count > 2) - s.Append($"
"); - s.Append($"
{(act.HasHoldFunctionality ? "☑" : "")}
"); + if (act.Mappings.Count > 2) // collapsible only if more than 2 items + s.Append("
details"); + s.Append("
"); + act.Mappings.ForEach(am => { + if (am.Values?.Length > 0) + s.Append($"
{string.Join("+", am.Values)}
"); + s.Append($"
{am.ActionId}
"); + }); + s.Append($"
"); + if (act.Mappings.Count > 2) + s.Append($"
"); + s.Append("
{(act.HasHoldFunctionality ? "☑" : "")}
\n\n\n"); } if (cat.States.Count > 0) { // Loop States - s.Append("### States\n\n"); + s.Append("#### States\n\n"); s.Append($" **Base Id:** {cat.Id}.State.\n\n"); s.Append("| Id | SimVar Name | Description | Unit | Format | DefaultValue | Can Set |\n"); s.Append("| --- | --- | --- | --- | --- | --- | --- |\n"); diff --git a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs index 4875923..1666dd9 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs @@ -39,22 +39,12 @@ public GenerateEntry(ILogger logger, GeneratorOptions options, IR } public void Generate() { - // Find assembly - var a = Assembly.GetExecutingAssembly().GetReferencedAssemblies().FirstOrDefault(a => a.Name == _options.PluginId); - - if (a == null) { - _logger.LogError($"Unable to load assembly '{_options.PluginId}' for reflection.'"); - return; - } - - // Load assembly - var assembly = Assembly.Load(a); string basePath = $"%TP_PLUGIN_FOLDER%{_options.PluginFolder}/"; bool useCustomConfigs = _options.StateFiles.Any(); // Get version and see if we need a custom version number - VersionInfo.Assembly = assembly; + VersionInfo.AssemblyLocation = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), _options.PluginId + ".dll"); uint vNum = VersionInfo.GetProductVersionNumber() >> 8; // strip the patch level // add custom config version if using custom files if (useCustomConfigs) { @@ -79,9 +69,6 @@ public void Generate() { // always include the internal plugin states simVars = simVars.Concat(_pluginConfig.LoadPluginStates()); - - var q = assembly.GetTypes().ToList(); - // Setup Base Model var model = new Base { Sdk = 3, @@ -92,50 +79,35 @@ public void Generate() { if (!_options.Debug) model.Plugin_start_cmd = $"{basePath}dist/{_options.PluginId}.exe"; - - // Get all classes with the TouchPortalCategory - var categoryClasses = q.Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalCategoryAttribute))).OrderBy(o => o.Name); - - // For each category, add to model - foreach (var cat in categoryClasses) { - var att = (TouchPortalCategoryAttribute)Attribute.GetCustomAttribute(cat, typeof(TouchPortalCategoryAttribute)); - Groups catId = att.Id; - string catIdStr = $"{_options.PluginId}.{catId}"; - var category = model.Categories.FirstOrDefault(c => c.Id == catIdStr); - if (category == null) { - category = new TouchPortalCategory { - Id = catIdStr, - Name = Categories.FullCategoryName(catId), - Imagepath = basePath + Categories.CategoryImage(catId) - }; - model.Categories.Add(category); - } + var categegoryAttribs = _reflectionSvc.GetCategoryAttributes(); + foreach (var catAttrib in categegoryAttribs) { + var category = new TouchPortalCategory { + Id = $"{_options.PluginId}.{catAttrib.Id}", + Name = Categories.FullCategoryName(catAttrib.Id), + Imagepath = basePath + catAttrib.Imagepath + }; + model.Categories.Add(category); // workaround for backwards compat with mis-named actions in category InstrumentsSystems.Fuel - string actionCatId = _options.PluginId + "." + Categories.ActionCategoryId(catId); + string actionCatId = _options.PluginId + "." + Categories.ActionCategoryId(catAttrib.Id); - // Add actions - var actions = cat.GetMembers().Where(t => t.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalActionAttribute))).ToList(); - actions.ForEach(act => { - var actionAttribute = (TouchPortalActionAttribute)Attribute.GetCustomAttribute(act, typeof(TouchPortalActionAttribute)); + foreach (var actionAttrib in catAttrib.Actions) { var action = new TouchPortalAction { - Id = $"{actionCatId}.Action.{actionAttribute.Id}", - Name = actionAttribute.Name, - Prefix = actionAttribute.Prefix, - Type = actionAttribute.Type, - Description = actionAttribute.Description, + Id = $"{actionCatId}.Action.{actionAttrib.Id}", + Name = actionAttrib.Name, + Prefix = actionAttrib.Prefix, + Type = actionAttrib.Type, + Description = actionAttrib.Description, TryInline = true, - Format = actionAttribute.Format, - HasHoldFunctionality = actionAttribute.HasHoldFunctionality, + Format = actionAttrib.Format, + HasHoldFunctionality = actionAttrib.HasHoldFunctionality, }; - // Action Data - var dataAttributes = act.GetCustomAttributes(); - if (dataAttributes.Any()) { - for (int i = 0, e = dataAttributes.Count(); i < e; ++i) { - var attrib = dataAttributes.ElementAt(i); + if (actionAttrib.Data.Any()) { + int i = 0; + foreach (var attrib in actionAttrib.Data) { var data = new TouchPortalActionData { - Id = $"{action.Id}.Data.{i}", + Id = $"{action.Id}.Data.{i++}", Type = attrib.Type, Label = attrib.Label ?? "Action", DefaultValue = attrib.GetDefaultValue(), @@ -148,23 +120,20 @@ public void Generate() { action.Data.Add(data); } action.Format = string.Format(action.Format, action.Data.Select(d => $"{{${d.Id}$}}").ToArray()); - } + } // action data // validate unique ID if (category.Actions.FirstOrDefault(a => a.Id == action.Id) == null) category.Actions.Add(action); else _logger.LogWarning($"Duplicate action ID found: '{action.Id}', skipping.'"); - }); + + } // actions // Sort the actions category.Actions = category.Actions.OrderBy(c => c.Name).ToList(); - // States - if (category.States.Any()) - continue; // skip if already added for this category - - var categoryStates = simVars.Where(s => s.CategoryId == catId); + var categoryStates = simVars.Where(s => s.CategoryId == catAttrib.Id); foreach (SimVarItem state in categoryStates) { var newState = new TouchPortalState { Id = state.TouchPortalStateId, @@ -181,6 +150,7 @@ public void Generate() { // Sort the states category.States = category.States.OrderBy(c => c.Description).ToList(); + } // categories loop // Settings diff --git a/MSFSTouchPortalPlugin/Attributes/TouchPortalActionAttribute.cs b/MSFSTouchPortalPlugin/Attributes/TouchPortalActionAttribute.cs index 4dd9eba..4d60a63 100644 --- a/MSFSTouchPortalPlugin/Attributes/TouchPortalActionAttribute.cs +++ b/MSFSTouchPortalPlugin/Attributes/TouchPortalActionAttribute.cs @@ -13,6 +13,8 @@ public class TouchPortalActionAttribute : Attribute public string Format { get; set; } public string Type { get; set; } public bool HasHoldFunctionality { get; set; } + public TouchPortalActionDataAttribute[] Data { get; set; } = Array.Empty(); + public TouchPortalActionMappingAttribute[] Mappings { get; set; } = Array.Empty(); public TouchPortalActionAttribute(string id, string name, string prefix, string description, string format, bool holdable = false) { SetupProperties(id, name, prefix, description, format, holdable, "communicate"); @@ -35,24 +37,19 @@ public class TouchPortalActionDataAttribute : Attribute //public string Id { get; set; } public DataType ValueType { get; set; } public virtual string Label { get; set; } = "Action"; - public virtual bool AllowDecimals { get; set; } = true; // this default will prevent includsion in entry.tp by json generator + public virtual bool AllowDecimals { get; set; } = true; // this default will prevent inclusion in entry.tp by json generator public virtual double MinValue { get; set; } = double.NaN; public virtual double MaxValue { get; set; } = double.NaN; public virtual string[] ChoiceValues { get; set; } public string Type { get { - switch (ValueType) { - case DataType.Number: - return "number"; - case DataType.Switch: - return "switch"; - case DataType.Choice: - return "choice"; - case DataType.Text: - default: - return "text"; - } + return ValueType switch { + DataType.Number => "number", + DataType.Switch => "switch", + DataType.Choice => "choice", + _ => "text", + }; } } @@ -60,24 +57,18 @@ public string Type public dynamic GetDefaultValue() { try { - switch (ValueType) { - case DataType.Number: - return Convert.ToDouble(_defaultValue); - case DataType.Switch: - return Convert.ToBoolean(_defaultValue); - default: - return Convert.ToString(_defaultValue); - } + return ValueType switch { + DataType.Number => Convert.ToDouble(_defaultValue), + DataType.Switch => Convert.ToBoolean(_defaultValue), + _ => Convert.ToString(_defaultValue), + }; } catch { - switch (ValueType) { - case DataType.Number: - return 0.0; - case DataType.Switch: - return false; - default: - return string.Empty; - } + return ValueType switch { + DataType.Number => 0.0, + DataType.Switch => false, + _ => string.Empty, + }; } } diff --git a/MSFSTouchPortalPlugin/Attributes/TouchPortalActionMappingAttribute.cs b/MSFSTouchPortalPlugin/Attributes/TouchPortalActionMappingAttribute.cs index 70d48bc..3da2453 100644 --- a/MSFSTouchPortalPlugin/Attributes/TouchPortalActionMappingAttribute.cs +++ b/MSFSTouchPortalPlugin/Attributes/TouchPortalActionMappingAttribute.cs @@ -3,9 +3,10 @@ namespace MSFSTouchPortalPlugin.Attributes { [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal class TouchPortalActionMappingAttribute : Attribute { - public string ActionId; - public string[] Values; + public class TouchPortalActionMappingAttribute : Attribute + { + public string ActionId { get; set; } + public string[] Values { get; set; } = Array.Empty(); public TouchPortalActionMappingAttribute(string actionId, string value) : this(actionId, new [] { value }) { } diff --git a/MSFSTouchPortalPlugin/Attributes/TouchPortalCategoryAttribute.cs b/MSFSTouchPortalPlugin/Attributes/TouchPortalCategoryAttribute.cs index b1378d8..4dd90e0 100644 --- a/MSFSTouchPortalPlugin/Attributes/TouchPortalCategoryAttribute.cs +++ b/MSFSTouchPortalPlugin/Attributes/TouchPortalCategoryAttribute.cs @@ -6,10 +6,19 @@ namespace MSFSTouchPortalPlugin.Attributes [AttributeUsage(AttributeTargets.All)] public class TouchPortalCategoryAttribute : Attribute { - public Groups Id; + public Groups Id { get; set; } + public string Name { get; set; } + public string Imagepath { get; set; } + + public TouchPortalActionAttribute[] Actions { get; set; } = Array.Empty(); + public object[] States { get; set; } = Array.Empty(); + public object[] Events { get; set; } = Array.Empty(); + public object[] Connectors { get; set; } = Array.Empty(); public TouchPortalCategoryAttribute(Groups id) { Id = id; + Name = Constants.Categories.CategoryName(id); + Imagepath = Constants.Categories.CategoryImage(id); } } } diff --git a/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs b/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs index 1764bbd..59ebefd 100644 --- a/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs +++ b/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs @@ -1,4 +1,6 @@ -using MSFSTouchPortalPlugin.Constants; +using MSFSTouchPortalPlugin.Attributes; +using MSFSTouchPortalPlugin.Constants; +using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Types; using System; using System.Collections.Generic; @@ -12,5 +14,7 @@ internal interface IReflectionService { string GetSimEventNameById(Enum id); string GetSimEventNameById(uint id); string GetSimEventNameById(int id); + IEnumerable GetActionAttributes(Groups catId); + IEnumerable GetCategoryAttributes(); } } diff --git a/MSFSTouchPortalPlugin/Services/ReflectionService.cs b/MSFSTouchPortalPlugin/Services/ReflectionService.cs index d4723c7..8a6165e 100644 --- a/MSFSTouchPortalPlugin/Services/ReflectionService.cs +++ b/MSFSTouchPortalPlugin/Services/ReflectionService.cs @@ -12,8 +12,14 @@ namespace MSFSTouchPortalPlugin.Services { - internal class ReflectionService : IReflectionService { - private readonly string rootName = Assembly.GetExecutingAssembly().GetName().Name; + internal class ReflectionService : IReflectionService + { + + public static Assembly ExecutingAssembly { get; set; } = Assembly.GetExecutingAssembly(); + public static string TouchPortalBaseId { get; set; } = ExecutingAssembly.GetName().Name; + + private readonly Type[] _assemblyTypes = ExecutingAssembly.GetTypes(); + private readonly ILogger _logger; public ReflectionService(ILogger logger) { @@ -32,32 +38,55 @@ public string GetSimEventNameById(Enum id) { public string GetSimEventNameById(uint id) => GetSimEventNameById((SimEventClientId)id); public string GetSimEventNameById(int id) => GetSimEventNameById((SimEventClientId)id); + public IEnumerable GetCategoryAttributes() { + List ret = new(); + foreach (Groups catId in Enum.GetValues()) { + if (catId != Groups.None) + ret.Add(new TouchPortalCategoryAttribute(catId) { + Actions = GetActionAttributes(catId).ToArray() + }); + } + return ret; + } + + public IEnumerable GetActionAttributes(Groups catId) { + List ret = new(); + var container = _assemblyTypes.Where(t => t.IsClass && t.GetCustomAttribute()?.Id == catId); + foreach (var c in container) { + foreach (var m in c.GetMembers()) { + if (m.GetCustomAttribute() is var actionAttrib && actionAttrib != null) { + actionAttrib.Data = m.GetCustomAttributes(true).ToArray(); + actionAttrib.Mappings = m.GetCustomAttributes(true).ToArray(); + ret.Add(actionAttrib); + } + } + } + return ret; + } + public Dictionary GetActionEvents() { var returnDict = new Dictionary(); int nextId = (int)SimEventClientId.Init + 1; - // Get all types which have actions mapped to events, sim or internal. - var eventContainers = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsClass && (t.GetCustomAttribute() != null)); - foreach (var notifyType in eventContainers) { - // Get the TP Category ID for this type - Groups catId = notifyType.GetCustomAttribute()?.Id ?? default; - if (catId == default) - continue; - var internalEvent = catId == Groups.Plugin; - // Get all members which have an action mapping, which is some unique combination of value(s) mapped to a SimConnect event name or internal event enum - List actionMembers = notifyType.GetMembers().Where(m => m.CustomAttributes.Any(att => att.AttributeType == typeof(TouchPortalActionMappingAttribute))).ToList(); - actionMembers.ForEach(e => { + var catAttribs = GetCategoryAttributes(); + foreach (var catAttr in catAttribs) { + bool internalEvent = catAttr.Id == Groups.Plugin; + // Loop over all actions which have an action mapping, which is some unique combination of value(s) mapped to a SimConnect event name or internal event enum + foreach (var actAttr in catAttr.Actions) { + // check that there are any mappings at all + if (!actAttr.Mappings.Any()) { + _logger.LogWarning($"No event mappings found for action ID '{actAttr.Id}' in category '{catAttr.Name}'."); + continue; + } // Create the action data object to store in the return dict, using the meta data we've collected so far. ActionEventType act = new ActionEventType { InternalEvent = internalEvent, - CategoryId = catId, - ActionId = e.GetCustomAttribute().Id, - //ActionObject = e + CategoryId = catAttr.Id, + ActionId = actAttr.Id }; // Loop over all the data attributes to find the "choice" types for mapping and also the index of any free-form data value field - var dataAttribs = e.GetCustomAttributes().ToList() ?? new(); - for (var i = 0; i < dataAttribs.Count; ++i) { - var dataAttrib = dataAttribs[i]; + int i = 0; + foreach (var dataAttrib in actAttr.Data) { if (dataAttrib.ValueType == DataType.Choice) { // for Choice types, we combine them to create a unique lookup key which maps to a particular event. if (act.KeyFormatStr.Length > 1) @@ -71,23 +100,24 @@ public Dictionary GetActionEvents() { act.MaxValue = dataAttrib.MaxValue; act.ValueType = dataAttrib.ValueType; } + ++i; } // Now get all the action mappings to produce the final list of all possible action events - var mappingAttribs = e.GetCustomAttributes()?.ToList(); - mappingAttribs?.ForEach(ma => { + foreach (var ma in actAttr.Mappings) { Enum mapTarget = internalEvent ? Enum.Parse(ma.ActionId) : (SimEventClientId)nextId++; // Put into collections if (!act.TpActionToEventMap.TryAdd($"{string.Join(",", ma.Values)}", mapTarget)) _logger.LogWarning($"Duplicate action-to-event mapping found for action {act.ActionId} with choices '{string.Join(",", ma.Values)} for event '{ma.ActionId}'!'"); // keep track of generated event IDs for Sim actions (for registering to SimConnect, and debug) if (!internalEvent) - clientEventIdToNameMap[mapTarget] = new { EventName = ma.ActionId, GroupId = catId }; - }); + clientEventIdToNameMap[mapTarget] = new { EventName = ma.ActionId, GroupId = catAttr.Id }; + } + // Put into returned collection - if (!returnDict.TryAdd($"{rootName}.{catId}.Action.{act.ActionId}", act)) - _logger.LogWarning($"Duplicate action ID found for action '{act.ActionId}' in category '{catId}', skipping.'"); - }); - } + if (!returnDict.TryAdd($"{TouchPortalBaseId}.{catAttr.Id}.Action.{act.ActionId}", act)) + _logger.LogWarning($"Duplicate action ID found for action '{act.ActionId}' in category '{catAttr.Id}', skipping.'"); + } // actions loop + } // categories loop return returnDict; } @@ -101,7 +131,7 @@ public Dictionary GetSettings() { foreach (FieldInfo field in settingFields) { if (field.FieldType == typeof(PluginSetting) && ((PluginSetting)field.GetValue(null) is var setting && setting != null)) { if (!string.IsNullOrWhiteSpace(setting.TouchPortalStateId)) - setting.TouchPortalStateId = $"{rootName}.Plugin.State.{setting.TouchPortalStateId}"; + setting.TouchPortalStateId = $"{TouchPortalBaseId}.Plugin.State.{setting.TouchPortalStateId}"; returnDict.TryAdd(setting.Name, setting); } } From ead5aa624ae1c89a1e1fbc87973fd2e2b76054eb Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Tue, 5 Apr 2022 03:38:15 -0400 Subject: [PATCH 61/93] Fix stray double assignment after changes in commit "Add Set Simulator Value Action ..." (c43f6a5f). --- MSFSTouchPortalPlugin/Services/PluginService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 3da454e..02f20e3 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -430,7 +430,6 @@ private void ProcessSimEvent(ActionEventType action, Enum eventId, in string[] d _logger.LogWarning($"Data conversion failed for action '{action.ActionId}' on sim event '{_reflectionService.GetSimEventNameById(eventId)}'."); return; } - dataReal = Convert.ToDouble(_expressionEvaluator.Compute(valStr, null)); break; case DataType.Switch: dataReal = new BooleanString(valStr); From 2ee11d444ed49aa2fbec4d5c82f217501a3377ea Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Tue, 5 Apr 2022 03:42:02 -0400 Subject: [PATCH 62/93] Misc. small cosmetics and adjustments; Remove unused ObservableObject.cs; Update SharpConfig libs to .net5 for smaller distro; Adjust logging format; Add data type info to PluginStates.ini. --- .../Configuration/PluginStates.ini | 5 +++ .../Configuration/States.ini | 1 - .../Helpers/ObservableObject.cs | 31 ------------------ MSFSTouchPortalPlugin/Types/SimVarItem.cs | 16 ++++----- MSFSTouchPortalPlugin/appsettings.json | 4 +-- .../lib/SharpConfig/SharpConfig.dll | Bin 34816 -> 34816 bytes .../lib/SharpConfig/SharpConfig.pdb | Bin 21168 -> 136704 bytes 7 files changed, 13 insertions(+), 44 deletions(-) delete mode 100644 MSFSTouchPortalPlugin/Helpers/ObservableObject.cs diff --git a/MSFSTouchPortalPlugin/Configuration/PluginStates.ini b/MSFSTouchPortalPlugin/Configuration/PluginStates.ini index 3066d62..e13b0da 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginStates.ini +++ b/MSFSTouchPortalPlugin/Configuration/PluginStates.ini @@ -7,23 +7,28 @@ CategoryId = Plugin Name = "The current Held Action Repeat Rate (ms)" DefaultValue = "450" +Unit = "millisecond" ; not an existing SimConnect Unit?! [Connected] CategoryId = Plugin Name = "The status of SimConnect (true/false/connecting)" DefaultValue = "false" +Unit = "string" [RunningVersion] CategoryId = Plugin Name = "The running plugin version number." DefaultValue = "0" +Unit = "number" [EntryVersion] CategoryId = Plugin Name = "The loaded entry.tp plugin version number." DefaultValue = "0" +Unit = "number" [ConfigVersion] CategoryId = Plugin Name = "The loaded entry.tp custom configuration version." DefaultValue = "0" +Unit = "number" diff --git a/MSFSTouchPortalPlugin/Configuration/States.ini b/MSFSTouchPortalPlugin/Configuration/States.ini index 524fc02..511e615 100644 --- a/MSFSTouchPortalPlugin/Configuration/States.ini +++ b/MSFSTouchPortalPlugin/Configuration/States.ini @@ -6,7 +6,6 @@ # Blank lines are ignored # Spaces around key=value = signs are optional # Strings with spaces or comments chars _must_ be quoted -# Escape literal quotes with backslash (\") # # An example entry describing each possible parameter (some are required, others have reasonable default values): # diff --git a/MSFSTouchPortalPlugin/Helpers/ObservableObject.cs b/MSFSTouchPortalPlugin/Helpers/ObservableObject.cs deleted file mode 100644 index e1e0c5d..0000000 --- a/MSFSTouchPortalPlugin/Helpers/ObservableObject.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.ComponentModel; -using System.Runtime.CompilerServices; - -namespace MSFSTouchPortalPlugin.Helpers { - public class ObservableObject : INotifyPropertyChanged { - public event PropertyChangedEventHandler PropertyChanged; - - public void OnPropertyChanged([CallerMemberName] string _sPropertyName = null) { - PropertyChangedEventHandler hEventHandler = this.PropertyChanged; - if (hEventHandler != null && !string.IsNullOrEmpty(_sPropertyName)) { - hEventHandler(this, new PropertyChangedEventArgs(_sPropertyName)); - } - } - - protected bool SetProperty(ref T _tField, T _tValue, [CallerMemberName] string _sPropertyName = null) { - return this.SetProperty(ref _tField, _tValue, out T tPreviousValue, _sPropertyName); - } - - protected bool SetProperty(ref T _tField, T _tValue, out T _tPreviousValue, [CallerMemberName] string _sPropertyName = null) { - if (!object.Equals(_tField, _tValue)) { - _tPreviousValue = _tField; - _tField = _tValue; - this.OnPropertyChanged(_sPropertyName); - return true; - } - - _tPreviousValue = default(T); - return false; - } - } -} diff --git a/MSFSTouchPortalPlugin/Types/SimVarItem.cs b/MSFSTouchPortalPlugin/Types/SimVarItem.cs index ef4824b..ebc21c2 100644 --- a/MSFSTouchPortalPlugin/Types/SimVarItem.cs +++ b/MSFSTouchPortalPlugin/Types/SimVarItem.cs @@ -227,16 +227,12 @@ internal bool SetValue(uint value) { } internal bool SetValue(string value) { - switch (Value) { - case string: - return SetValue(new StringVal(value)); - case uint: - return SetValue((uint)new BooleanString(value)); - case double: - case long: - return double.TryParse(value, out var dVal) && SetValue(dVal); - } - return false; + return Value switch { + string => SetValue(new StringVal(value)), + uint => SetValue((uint)new BooleanString(value)), + double or long => double.TryParse(value, out var dVal) && SetValue(dVal), + _ => false, + }; } /// diff --git a/MSFSTouchPortalPlugin/appsettings.json b/MSFSTouchPortalPlugin/appsettings.json index c42a91d..e199c04 100644 --- a/MSFSTouchPortalPlugin/appsettings.json +++ b/MSFSTouchPortalPlugin/appsettings.json @@ -20,7 +20,7 @@ "path": "logs/MSFSTouchPortalPlugin.log", "rollingInterval": "Day", "retainedFileCountLimit": 7, - "outputTemplate": "[{Timestamp:MM.dd HH:mm:ss.fff}] [{Level:u3}] {SourceContext} {Message:lj} {NewLine}{Exception}" + "outputTemplate": "[{Timestamp:MM.dd HH:mm:ss.fff}] [{Level:u3}] {SourceContext}: {Message:lj} {NewLine}{Exception}" } } ] @@ -29,7 +29,7 @@ { "Name": "Console", "Args": { - "outputTemplate": "[{Timestamp:HH:mm:ss.fff}] [{Level:u3}] {SourceContext} {NewLine} {Message:lj} {NewLine}{Exception}" + "outputTemplate": "[{Timestamp:HH:mm:ss.fff}] [{Level:u3}] {SourceContext}:{NewLine} {Message:lj} {NewLine}{Exception}" } } ] diff --git a/MSFSTouchPortalPlugin/lib/SharpConfig/SharpConfig.dll b/MSFSTouchPortalPlugin/lib/SharpConfig/SharpConfig.dll index 957b1240131c4b9511afbcc89e0beececa22837d..f76183a6daa5605115654a594dd40512a998b0da 100644 GIT binary patch delta 11656 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 KU+1g(yZXO=dSash delta 11705 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 WKlZ)q#ZQdlPohtDt2HaHu>S&@WRola diff --git a/MSFSTouchPortalPlugin/lib/SharpConfig/SharpConfig.pdb b/MSFSTouchPortalPlugin/lib/SharpConfig/SharpConfig.pdb index 593c12382fbfe8b01e344b52218d94670a2bc823..4bb00b328a0ca4f456a8d3235a0b55d40678a29c 100644 GIT binary patch literal 136704 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 VxE63N;99`7fNKHQ0zVB4{1+;OIlBM= literal 21168 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 From 9d5911aa35f51f86dddce34b2c67e7f861229afe Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Tue, 5 Apr 2022 03:48:03 -0400 Subject: [PATCH 63/93] [build] Adjust publish.ps1 scrips for latest changes, add named CLI option parsing, add --clean option, do not delete previous version archives. Also now build/include Generator with plugin distro. --- publish.ps1 | 75 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/publish.ps1 b/publish.ps1 index ec4f5d9..48422db 100644 --- a/publish.ps1 +++ b/publish.ps1 @@ -1,54 +1,63 @@ + +[CmdletBinding(PositionalBinding=$false)] Param( - [Parameter(Position = 0)] - [Boolean]$IsBuildAgent = $false, - [Parameter(Position = 1)] - [String]$Configuration = "Release", - [Parameter(Position = 2)] - [String]$VersionSuffix = "" + [string]$ProjectName = "MSFSTouchPortalPlugin", + [string]$DistroName = "MSFS-TouchPortal-Plugin", + [string]$Configuration = "Release", + [string]$Platform = "x64", + [String]$VersionSuffix = "", + [switch]$Clean = $false, + [switch]$BuildAgent = $false ) -if((-Not ($IsBuildAgent)) -And ([string]::IsNullOrEmpty($VersionSuffix))) { - $VersionSuffix = "1" -} +$CurrentDir = [System.IO.Path]::GetDirectoryName($myInvocation.MyCommand.Path) +$DistFolderPath = "$CurrentDir\packages-dist" +$PluginFilesPath = "$DistFolderPath\$DistroName" +$BinFilesPath = "$PluginFilesPath\dist" $VersionSuffixCommand = "" if(-Not ([string]::IsNullOrEmpty($VersionSuffix))) { $VersionSuffixCommand = "--version-suffix" } -Write-Information "Restoring 'MSFSTouchPortalPlugin' component....`n" -InformationAction Continue -dotnet restore "MSFSTouchPortalPlugin" -dotnet restore "MSFSTouchPortalPlugin.Tests" - -Write-Information "Building 'MSFSTouchPortalPlugin' component...`n" -InformationAction Continue -dotnet build "MSFSTouchPortalPlugin" --configuration $Configuration -p:Platform=x64 - -Write-Information "Cleaning 'MSFSTouchPortalPlugin' packages-dist folder..." -InformationAction Continue -$CurrentDir = [System.IO.Path]::GetDirectoryName($myInvocation.MyCommand.Path) -$DistFolderPath = "$CurrentDir\packages-dist" -if (Test-Path $DistFolderPath) { - Remove-Item $DistFolderPath -Force -Recurse +if (Test-Path $PluginFilesPath) { + Write-Information "Cleaning '$ProjectName' packages-dist folder '$PluginFilesPath'..." -InformationAction Continue + Remove-Item $PluginFilesPath -Force -Recurse } -Write-Information "Publishing 'MSFSTouchPortalPlugin' component...`n" -InformationAction Continue -dotnet publish "MSFSTouchPortalPlugin" --output "$DistFolderPath\MSFS-TouchPortal-Plugin\dist" --configuration $Configuration -p:Platform=x64 $VersionSuffixCommand $VersionSuffix -r "win-x64" --self-contained true +Write-Information "`nPublishing '$ProjectName' component to '$BinFilesPath' ...`n" -InformationAction Continue +dotnet publish "$ProjectName" --output "$BinFilesPath" --configuration $Configuration -p:Platform=$Platform $VersionSuffixCommand $VersionSuffix -r "win-$Platform" + +Write-Information "`nPublishing '$ProjectName-Generator' component...`n" -InformationAction Continue +dotnet publish "$ProjectName-Generator" --output "$BinFilesPath" --configuration $Configuration -p:Platform=$Platform -r "win-$Platform" /p:ValidateExecutableReferencesMatchSelfContained=false # Run Documentation -dotnet run -p "MSFSTouchPortalPlugin-Generator" "$DistFolderPath\MSFS-TouchPortal-Plugin" +Write-Information "`nGenerating entry.tp JSON and Documentation..." -InformationAction Continue +#dotnet run -p "$ProjectName-Generator" -o "$PluginFilesPath" +& "$BinFilesPath\$ProjectName-Generator.exe" -o "$PluginFilesPath" -# Copy Entry.tp, Readme, Documentation, CHANGELOG to publish -copy "README.md" "$DistFolderPath\MSFS-TouchPortal-Plugin" -copy "CHANGELOG.md" "$DistFolderPath\MSFS-TouchPortal-Plugin" -copy "airplane_takeoff24.png" "$DistFolderPath\MSFS-TouchPortal-Plugin" +# Copy Readme, CHANGELOG, image(s) to publish folder +copy "README.md" "$PluginFilesPath" +copy "CHANGELOG.md" "$PluginFilesPath" +copy "airplane_takeoff24.png" "$PluginFilesPath" # Get version -$FileVersion = (Get-Command $DistFolderPath\MSFS-TouchPortal-Plugin\dist\MSFSTouchPortalPlugin.dll).FileVersionInfo.FileVersion +$FileVersion = (Get-Command $BinFilesPath\$ProjectName.dll).FileVersionInfo.FileVersion # Create TPP File -#Compress-Archive -Path "$DistFolderPath\MSFS-TouchPortal-Plugin" -DestinationPath "$DistFolderPath\MSFS-TouchPortal-Plugin.zip" -#Rename-Item -Path "$DistFolderPath\MSFS-TouchPortal-Plugin.zip" -NewName "MSFS-TouchPortal-Plugin.tpp" -& "C:\Program Files\7-Zip\7z.exe" a $DistFolderPath\MSFS-TouchPortal-Plugin-$FileVersion.tpp "$DistFolderPath\*" -r -tzip +$TppFile = "$DistFolderPath\$DistroName-$FileVersion.tpp" +if (Test-Path $TppFile) { + Remove-Item $TppFile -Force +} +& "C:\Program Files\7-Zip\7z.exe" a "$TppFile" "$DistFolderPath\*" -tzip `-xr!*.tpp + +if ($Clean) { + Write-Information "`nCleaning '$ProjectName-Generator' component....`n" -InformationAction Continue + dotnet clean "$ProjectName-Generator" --configuration $Configuration -p:Platform=$Platform -r "win-$Platform" /p:ValidateExecutableReferencesMatchSelfContained=false + Write-Information "`nCleaning '$ProjectName' component....`n" -InformationAction Continue + dotnet clean "$ProjectName" --configuration $Configuration -p:Platform=$Platform -r "win-$Platform" +} -if ($IsBuildAgent) { +if ($BuildAgent) { exit 0 } From cf45693f64ad48d037ee83fa2c328caf7e556de3 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Tue, 5 Apr 2022 15:40:38 -0400 Subject: [PATCH 64/93] [SimConnectService] Refactor SimConnect method invocations to a central handler using delegates, for simpler exception handling and debug logging; Also simplifies setting the data storage type for RegisterDataDefineStruct(). --- .../Interfaces/ISimConnectService.cs | 6 +- .../Services/PluginService.cs | 2 +- .../Services/SimConnectService.cs | 209 ++++++++---------- 3 files changed, 98 insertions(+), 119 deletions(-) diff --git a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs index 3eded8f..ce2a428 100644 --- a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs +++ b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs @@ -21,12 +21,14 @@ internal interface ISimConnectService { bool Connect(uint configIndex = 0); void Disconnect(); bool MapClientEventToSimEvent(Enum eventId, string eventName); + bool TransmitClientEvent(Groups group, Enum eventId, uint data); void SetNotificationGroupPriorities(); void ClearAllDataDefinitions(); bool RegisterToSimConnect(SimVarItem simVar); + bool RequestDataOnSimObject(SimVarItem simVar, uint objectId = (uint)SIMCONNECT_SIMOBJECT_TYPE.USER); bool RequestDataOnSimObjectType(SimVarItem simVar, SIMCONNECT_SIMOBJECT_TYPE objectType = SIMCONNECT_SIMOBJECT_TYPE.USER); - bool TransmitClientEvent(Groups group, Enum eventId, uint data); - bool SetSimVar(SimVarItem simVar, uint objectId = (uint)SIMCONNECT_SIMOBJECT_TYPE.USER); + bool SetDataOnSimObject(SimVarItem simVar, uint objectId = (uint)SIMCONNECT_SIMOBJECT_TYPE.USER); bool ReleaseAIControl(Definition def, uint objectId = (uint)SIMCONNECT_SIMOBJECT_TYPE.USER); + bool ClearDataDefinition(Definition def); } } diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 02f20e3..43a18f8 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -408,7 +408,7 @@ private void ProcessInternalEvent(ActionEventType action, Enum eventId, in strin } if (new BooleanString(dataArry[2]) && !_simConnectService.ReleaseAIControl(simVar.Def)) break; - _simConnectService.SetSimVar(simVar); + _simConnectService.SetDataOnSimObject(simVar); } break; diff --git a/MSFSTouchPortalPlugin/Services/SimConnectService.cs b/MSFSTouchPortalPlugin/Services/SimConnectService.cs index 6c04129..626d3f6 100644 --- a/MSFSTouchPortalPlugin/Services/SimConnectService.cs +++ b/MSFSTouchPortalPlugin/Services/SimConnectService.cs @@ -8,6 +8,7 @@ using MSFSTouchPortalPlugin.Interfaces; using MSFSTouchPortalPlugin.Types; using System; +using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -29,17 +30,30 @@ internal class SimConnectService : ISimConnectService, IDisposable { /// enable AddNotification(), SetNotificationGroupPriorities(), and Simconnect_OnRecvEvent(); currently they serve no purpose except possible debug info. private static readonly bool DEBUG_NOTIFICATIONS = false; - SimConnect _simConnect; + private SimConnect _simConnect; private bool _connected; private bool _connecting; private Task _messageWaitTask; private readonly EventWaitHandle _scReady = new EventWaitHandle(false, EventResetMode.AutoReset); - private readonly System.Collections.Generic.List _addedDefinitions = new(); + private readonly List _addedDefinitions = new(); public event DataUpdateEventHandler OnDataUpdateEvent; public event ConnectEventHandler OnConnect; public event DisconnectEventHandler OnDisconnect; + // SimConnect method delegates, for centralized interaction in InvokeSimMethod() + private Action ClearDataDefinitionDelegate; + private Action SetNotificationGroupPriorityDelegate; + private Action AIReleaseControlDelegate; + private Action MapClientEventToSimEventDelegate; + private Action AddClientEventToNotificationGroupDelegate; + private Action RequestDataOnSimObjectTypeDelegate; + private Action TransmitClientEventDelegate; + private Action SetDataOnSimObjectDelegate; + private Action AddToDataDefinitionDelegate; + private Action RequestDataOnSimObjectDelegate; + private readonly Dictionary > _registerDataDelegates = new(); + public SimConnectService(ILogger logger, IReflectionService reflectionService) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _reflectionService = reflectionService ?? throw new ArgumentNullException(nameof(reflectionService)); @@ -72,6 +86,24 @@ public bool Connect(uint configIndex = 0) { _simConnect.OnRecvSimobjectDataBytype += new SimConnect.RecvSimobjectDataBytypeEventHandler(Simconnect_OnRecvSimobjectDataBytype); _simConnect.OnRecvSimobjectData += new SimConnect.RecvSimobjectDataEventHandler(Simconnect_OnRecvSimObjectData); + // Method delegates + MapClientEventToSimEventDelegate = _simConnect.MapClientEventToSimEvent; + TransmitClientEventDelegate = _simConnect.TransmitClientEvent; + AddClientEventToNotificationGroupDelegate = _simConnect.AddClientEventToNotificationGroup; + SetNotificationGroupPriorityDelegate = _simConnect.SetNotificationGroupPriority; + ClearDataDefinitionDelegate = _simConnect.ClearDataDefinition; + AddToDataDefinitionDelegate = _simConnect.AddToDataDefinition; + RequestDataOnSimObjectDelegate = _simConnect.RequestDataOnSimObject; + RequestDataOnSimObjectTypeDelegate = _simConnect.RequestDataOnSimObjectType; + SetDataOnSimObjectDelegate = _simConnect.SetDataOnSimObject; + AIReleaseControlDelegate = _simConnect.AIReleaseControl; + + _registerDataDelegates.Clear(); + _registerDataDelegates.Add(typeof(double), _simConnect.RegisterDataDefineStruct); + _registerDataDelegates.Add(typeof(uint), _simConnect.RegisterDataDefineStruct); + _registerDataDelegates.Add(typeof(long), _simConnect.RegisterDataDefineStruct); + _registerDataDelegates.Add(typeof(StringVal), _simConnect.RegisterDataDefineStruct); + #if DEBUG_REQUESTS DbgSetupRequestTracking(); #endif @@ -122,6 +154,7 @@ public void Disconnect() { OnDisconnect?.Invoke(); } + // runs in separate task/thread private void ReceiveMessages() { _logger.LogDebug("ReceiveMessages task started."); try { @@ -138,138 +171,97 @@ private void ReceiveMessages() { _logger.LogDebug("ReceiveMessages task stopped."); } - public bool MapClientEventToSimEvent(Enum eventId, string eventName) { - if (_connected) { - _simConnect.MapClientEventToSimEvent(eventId, eventName); - DbgAddSendRecord($"MapClientEventToSimEvent(eventId: {eventId}; eventName: {eventName})"); + // Centralized SimConnect method handler + private bool InvokeSimMethod(Delegate method, params object[] args) { + if (method == null || !_connected) + return false; + try { + _logger.LogTrace($"Invoking: {method.Method.Name}({(args != null ? string.Join(", ", args) : "null")})"); + method.DynamicInvoke(args); + DbgAddSendRecord($"{method.Method.Name}({(args != null ? string.Join(", ", args) : "null")})"); return true; } - + catch (COMException e) { + _logger.LogWarning($"SimConnect returned an error: [{e.HResult}] {e.Message} ()) { + if (g != Groups.None) + InvokeSimMethod(SetNotificationGroupPriorityDelegate, g, NOTIFICATION_PRIORITY); } } } - private void ClearDataDefinition(Definition def) { - try { - _simConnect.ClearDataDefinition(def); - DbgAddSendRecord($"ClearDataDefinition({def})"); - } - catch (Exception e) { - _logger.LogError(e, $"ClearDataDefinition({def}) failed."); + public void ClearAllDataDefinitions() { + if (_connected) { + foreach (var def in _addedDefinitions) + ClearDataDefinition(def); } + _addedDefinitions.Clear(); } - public bool RegisterToSimConnect(SimVarItem simVar) { - if (_connected) { - string unitName = simVar.IsStringType ? null : simVar.Unit; - _simConnect.AddToDataDefinition(simVar.Def, simVar.SimVarName, unitName, simVar.SimConnectDataType, simVar.DeltaEpsilon, SimConnect.SIMCONNECT_UNUSED); - DbgAddSendRecord($"AddToDataDefinition({simVar.ToDebugString()}, {unitName}, {simVar.SimConnectDataType})"); - - switch (simVar.Value) { - case double: - _simConnect.RegisterDataDefineStruct(simVar.Def); - break; - case uint: - _simConnect.RegisterDataDefineStruct(simVar.Def); - break; - case long: - _simConnect.RegisterDataDefineStruct(simVar.Def); - break; - case StringVal: - _simConnect.RegisterDataDefineStruct(simVar.Def); - break; - default: - _logger.LogError($"Unable to register storage type for '{simVar.StorageDataType}'"); - ClearDataDefinition(simVar.Def); - return false; - } - DbgAddSendRecord($"RegisterDataDefineStruct<{simVar.StorageDataType}>({simVar.ToDebugString()})"); - - if (!simVar.NeedsScheduledRequest) { - _simConnect.RequestDataOnSimObject(simVar.Def, simVar.Def, (uint)SIMCONNECT_SIMOBJECT_TYPE.USER, (SIMCONNECT_PERIOD)simVar.UpdatePeriod, SIMCONNECT_DATA_REQUEST_FLAG.CHANGED, 0, simVar.UpdateInterval, 0); - DbgAddSendRecord($"RequestDataOnSimObject({simVar.ToDebugString()})"); - } + public bool ClearDataDefinition(Definition def) { + return InvokeSimMethod(ClearDataDefinitionDelegate, def); + } - _addedDefinitions.Add(simVar.Def); + public bool RegisterToSimConnect(SimVarItem simVar) { + if (_addedDefinitions.Contains(simVar.Def)) { + _logger.LogDebug($"SimVar already registered. {simVar.ToDebugString()}"); return true; } + if (!_registerDataDelegates.TryGetValue(simVar.StorageDataType, out var registerDataDelegate)) { + _logger.LogError($"Unable to register storage type for '{simVar.StorageDataType}'"); + return false; + } - return false; - } + string unitName = simVar.IsStringType ? null : simVar.Unit; + if (!InvokeSimMethod(AddToDataDefinitionDelegate, simVar.Def, simVar.SimVarName, unitName, simVar.SimConnectDataType, simVar.DeltaEpsilon, SimConnect.SIMCONNECT_UNUSED)) + return false; - public void ClearAllDataDefinitions() { - if (_connected) { - foreach (var def in _addedDefinitions) - ClearDataDefinition(def); + if (!InvokeSimMethod(registerDataDelegate, simVar.Def)) { + ClearDataDefinition(simVar.Def); + return false; } - _addedDefinitions.Clear(); + + _addedDefinitions.Add(simVar.Def); + return simVar.NeedsScheduledRequest || RequestDataOnSimObject(simVar); } public bool RequestDataOnSimObjectType(SimVarItem simVar, SIMCONNECT_SIMOBJECT_TYPE objectType = SIMCONNECT_SIMOBJECT_TYPE.USER) { - if (_connected) { - try { - _simConnect.RequestDataOnSimObjectType(simVar.Def, simVar.Def, 0, objectType); - DbgAddSendRecord($"RequestDataOnSimObjectType({simVar.ToDebugString()})"); - return true; - } - catch (Exception ex) { - _logger.LogError(ex, $"RequestDataOnSimObjectType({simVar.Def}) failed, disconnecting."); - Disconnect(); - } - } + return InvokeSimMethod(RequestDataOnSimObjectTypeDelegate, simVar.Def, simVar.Def, 0U, objectType); + } - return false; + public bool RequestDataOnSimObject(SimVarItem simVar, uint objectId = (uint)SIMCONNECT_SIMOBJECT_TYPE.USER) { + return InvokeSimMethod( + RequestDataOnSimObjectDelegate, + simVar.Def, simVar.Def, objectId, (SIMCONNECT_PERIOD)simVar.UpdatePeriod, SIMCONNECT_DATA_REQUEST_FLAG.CHANGED, 0U, simVar.UpdateInterval, 0U + ); } /// /// Set the value associated with a SimVar /// - public bool SetSimVar(SimVarItem simVar, uint objectId = (uint)SIMCONNECT_SIMOBJECT_TYPE.USER) { - if (!_connected) - return false; - try { - _simConnect.SetDataOnSimObject(simVar.Def, objectId, SIMCONNECT_DATA_SET_FLAG.DEFAULT, simVar.Value); - DbgAddSendRecord($"SetDataOnSimObject({simVar.ToDebugString()})"); - return true; - } - catch (Exception e) { - _logger.LogError(e, $"SetSimVar({simVar.ToDebugString()}) failed."); - } - return false; + public bool SetDataOnSimObject(SimVarItem simVar, uint objectId = (uint)SIMCONNECT_SIMOBJECT_TYPE.USER) { + return InvokeSimMethod(SetDataOnSimObjectDelegate, simVar.Def, objectId, SIMCONNECT_DATA_SET_FLAG.DEFAULT, simVar.Value); } /// @@ -278,17 +270,7 @@ public bool SetSimVar(SimVarItem simVar, uint objectId = (uint)SIMCONNECT_SIMOBJ /// The previously-registered data definition ID of the variable to release. /// True on request success, false otherwise (this is the status of transmitting the command, not whether control was actually released). public bool ReleaseAIControl(Definition def, uint objectId = (uint)SIMCONNECT_SIMOBJECT_TYPE.USER) { - if (!_connected) - return false; - try { - _simConnect.AIReleaseControl(objectId, def); - DbgAddSendRecord($"AIReleaseControl({def})"); - return true; - } - catch (Exception e) { - _logger.LogError(e, $"ReleaseAIControl({def}) failed."); - } - return false; + return InvokeSimMethod(AIReleaseControlDelegate, objectId, def); } #region SimConnect Event Handlers @@ -318,11 +300,6 @@ private void Simconnect_OnRecvException(SimConnect sender, SIMCONNECT_RECV_EXCEP _logger.LogWarning($"SimConnect_OnRecvException: {eException}; SendID: {data.dwSendID}; Index: {data.dwIndex}; Request: {request}"); } - /// - /// Events triggered by sending events to the Sim - /// - /// - /// private void Simconnect_OnRecvEvent(SimConnect sender, SIMCONNECT_RECV_EVENT data) { string grpName = data.uGroupID.ToString(); string eventId = data.uEventID.ToString(); @@ -330,7 +307,7 @@ private void Simconnect_OnRecvEvent(SimConnect sender, SIMCONNECT_RECV_EVENT dat grpName = ((Groups)data.uGroupID).ToString(); eventId = _reflectionService.GetSimEventNameById(data.uEventID); } - _logger.LogDebug($"Simconnect_OnRecvEvent Recieved: Group: {grpName}; Event: {eventId}"); + _logger.LogDebug($"Simconnect_OnRecvEvent Received: Group: {grpName}; Event: {eventId}"); } #endregion SimConnect Event Handlers From ea246459fd3a907614179da43a030df09a0777ae Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Thu, 7 Apr 2022 02:43:51 -0400 Subject: [PATCH 65/93] Refactor action mappings so that internal plugin actions can have proper data member names instead of auto-gen indexes. Add file, folder, and color TP data attribute types. Make a formal SimEventRecord for tracking sim event names instead of dynamic struct. --- .../GenerateDoc.cs | 40 ++--- .../GenerateEntry.cs | 10 +- .../Attributes/TouchPortalActionAttribute.cs | 32 ++-- MSFSTouchPortalPlugin/Enums/DataType.cs | 5 +- .../Interfaces/IReflectionService.cs | 3 +- .../Objects/Plugin/Plugin.cs | 30 ++-- .../Services/PluginService.cs | 138 +++++++++++------- .../Services/ReflectionService.cs | 73 ++++++--- .../Types/ActionEventType.cs | 7 +- MSFSTouchPortalPlugin/Types/SimEventRecord.cs | 14 ++ .../lib/TouchPortalSDK/TouchPortalSDK.dll | Bin 38912 -> 39424 bytes 11 files changed, 231 insertions(+), 121 deletions(-) create mode 100644 MSFSTouchPortalPlugin/Types/SimEventRecord.cs diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index 5f3793f..25ddfc5 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.Logging; -using MSFSTouchPortalPlugin.Attributes; using MSFSTouchPortalPlugin.Configuration; using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; @@ -87,6 +86,7 @@ private DocBase CreateModel() { // workaround for backwards compat with mis-named actions in category InstrumentsSystems.Fuel string actionCatId = _options.PluginId + "." + Categories.ActionCategoryId(catAttrib.Id); + // Add Actions foreach (var actionAttrib in catAttrib.Actions) { var action = new DocAction { Name = actionAttrib.Name, @@ -97,21 +97,28 @@ private DocBase CreateModel() { }; category.Actions.Add(action); - if (actionAttrib.Data.Any()) { - foreach (var attrib in actionAttrib.Data) { - var data = new DocActionData { - Type = attrib.Type, - DefaultValue = attrib.GetDefaultValue()?.ToString(), - Values = attrib.ChoiceValues != null ? string.Join(", ", attrib.ChoiceValues) : "", - MinValue = attrib.MinValue, - MaxValue = attrib.MaxValue, - AllowDecimals = attrib.AllowDecimals, - }; + // Action data + foreach (var attrib in actionAttrib.Data) { + var data = new DocActionData { + Type = attrib.Type, + DefaultValue = attrib.GetDefaultValue()?.ToString(), + Values = attrib.ChoiceValues != null ? string.Join(", ", attrib.ChoiceValues) : "", + MinValue = attrib.MinValue, + MaxValue = attrib.MaxValue, + AllowDecimals = attrib.AllowDecimals, + }; + action.Data.Add(data); + } - action.Data.Add(data); - } - } // action data + // Action data mappings, but Plugin category doesn't show them anyway + if (catAttrib.Id == Groups.Plugin) + continue; + // Warn about missing mappings + if (!actionAttrib.Mappings.Any()) { + _logger.LogWarning($"No event mappings found for action ID '{actionAttrib.Id}' in category '{category.Name}'"); + continue; + } foreach (var attrib in actionAttrib.Mappings) { var map = new DocActionMapping { ActionId = attrib.ActionId, @@ -119,14 +126,11 @@ private DocBase CreateModel() { }; action.Mappings.Add(map); } - // Warn about missing mappings - if (!actionAttrib.Mappings.Any()) - _logger.LogWarning($"No event mappings found for action ID '{actionAttrib.Id}' in category '{category.Name}'"); } // actions category.Actions = category.Actions.OrderBy(c => c.Name).ToList(); - // Loop through States + // Add States var categoryStates = simVars.Where(s => s.CategoryId == catAttrib.Id); foreach (SimVarItem state in categoryStates) { var newState = new DocState { diff --git a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs index 1666dd9..285fb9a 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs @@ -1,8 +1,6 @@ using Microsoft.Extensions.Logging; -using MSFSTouchPortalPlugin.Attributes; using MSFSTouchPortalPlugin.Configuration; using MSFSTouchPortalPlugin.Constants; -using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Helpers; using MSFSTouchPortalPlugin.Interfaces; using MSFSTouchPortalPlugin.Types; @@ -91,6 +89,7 @@ public void Generate() { // workaround for backwards compat with mis-named actions in category InstrumentsSystems.Fuel string actionCatId = _options.PluginId + "." + Categories.ActionCategoryId(catAttrib.Id); + // Actions foreach (var actionAttrib in catAttrib.Actions) { var action = new TouchPortalAction { Id = $"{actionCatId}.Action.{actionAttrib.Id}", @@ -103,11 +102,13 @@ public void Generate() { HasHoldFunctionality = actionAttrib.HasHoldFunctionality, }; + // Action Data if (actionAttrib.Data.Any()) { int i = 0; foreach (var attrib in actionAttrib.Data) { + string dataId = (string.IsNullOrWhiteSpace(attrib.Id) ? i.ToString() : attrib.Id); var data = new TouchPortalActionData { - Id = $"{action.Id}.Data.{i++}", + Id = $"{action.Id}.Data.{dataId}", Type = attrib.Type, Label = attrib.Label ?? "Action", DefaultValue = attrib.GetDefaultValue(), @@ -116,7 +117,7 @@ public void Generate() { MaxValue = attrib.MaxValue, AllowDecimals = attrib.AllowDecimals, }; - + ++i; action.Data.Add(data); } action.Format = string.Format(action.Format, action.Data.Select(d => $"{{${d.Id}$}}").ToArray()); @@ -133,6 +134,7 @@ public void Generate() { // Sort the actions category.Actions = category.Actions.OrderBy(c => c.Name).ToList(); + // States var categoryStates = simVars.Where(s => s.CategoryId == catAttrib.Id); foreach (SimVarItem state in categoryStates) { var newState = new TouchPortalState { diff --git a/MSFSTouchPortalPlugin/Attributes/TouchPortalActionAttribute.cs b/MSFSTouchPortalPlugin/Attributes/TouchPortalActionAttribute.cs index 4d60a63..667f274 100644 --- a/MSFSTouchPortalPlugin/Attributes/TouchPortalActionAttribute.cs +++ b/MSFSTouchPortalPlugin/Attributes/TouchPortalActionAttribute.cs @@ -16,6 +16,10 @@ public class TouchPortalActionAttribute : Attribute public TouchPortalActionDataAttribute[] Data { get; set; } = Array.Empty(); public TouchPortalActionMappingAttribute[] Mappings { get; set; } = Array.Empty(); + public TouchPortalActionAttribute(string id, string name, string description, string format, bool holdable = false) { + SetupProperties(id, name, "MSFS", description, format, holdable, "communicate"); + } + public TouchPortalActionAttribute(string id, string name, string prefix, string description, string format, bool holdable = false) { SetupProperties(id, name, prefix, description, format, holdable, "communicate"); } @@ -36,22 +40,13 @@ public class TouchPortalActionDataAttribute : Attribute { //public string Id { get; set; } public DataType ValueType { get; set; } + public string Type => ValueType.ToString().ToLower(); + public virtual string Id { get; set; } public virtual string Label { get; set; } = "Action"; public virtual bool AllowDecimals { get; set; } = true; // this default will prevent inclusion in entry.tp by json generator public virtual double MinValue { get; set; } = double.NaN; public virtual double MaxValue { get; set; } = double.NaN; public virtual string[] ChoiceValues { get; set; } - public string Type - { - get { - return ValueType switch { - DataType.Number => "number", - DataType.Switch => "switch", - DataType.Choice => "choice", - _ => "text", - }; - } - } protected dynamic _defaultValue; @@ -101,6 +96,21 @@ protected TouchPortalActionTextAttribute(DataType type, string defaultValue = "" } } + public class TouchPortalActionFileAttribute : TouchPortalActionTextAttribute + { + public TouchPortalActionFileAttribute(string defaultValue = default) : base(DataType.File, defaultValue) { } + } + + public class TouchPortalActionFolderAttribute : TouchPortalActionTextAttribute + { + public TouchPortalActionFolderAttribute(string defaultValue = default) : base(DataType.Folder, defaultValue) { } + } + + public class TouchPortalActionColorAttribute : TouchPortalActionTextAttribute + { + public TouchPortalActionColorAttribute(string defaultValue = default) : base(DataType.Color, defaultValue) { } + } + public class TouchPortalActionChoiceAttribute : TouchPortalActionTextAttribute { diff --git a/MSFSTouchPortalPlugin/Enums/DataType.cs b/MSFSTouchPortalPlugin/Enums/DataType.cs index a12f277..b98e336 100644 --- a/MSFSTouchPortalPlugin/Enums/DataType.cs +++ b/MSFSTouchPortalPlugin/Enums/DataType.cs @@ -6,6 +6,9 @@ public enum DataType Text, Number, Switch, - Choice + Choice, + File, + Folder, + Color } } diff --git a/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs b/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs index 59ebefd..dd6e923 100644 --- a/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs +++ b/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs @@ -1,5 +1,4 @@ using MSFSTouchPortalPlugin.Attributes; -using MSFSTouchPortalPlugin.Constants; using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Types; using System; @@ -10,7 +9,7 @@ namespace MSFSTouchPortalPlugin.Interfaces internal interface IReflectionService { Dictionary GetActionEvents(); Dictionary GetSettings(); - ref readonly Dictionary GetClientEventIdToNameMap(); + ref readonly Dictionary GetClientEventIdToNameMap(); string GetSimEventNameById(Enum id); string GetSimEventNameById(uint id); string GetSimEventNameById(int id); diff --git a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs index 9a4b5bd..e1e1302 100644 --- a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs +++ b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs @@ -6,8 +6,8 @@ namespace MSFSTouchPortalPlugin.Objects.Plugin { [TouchPortalCategory(Groups.Plugin)] internal static class PluginMapping { - [TouchPortalAction("Connection", "Connection", "MSFS", "Toggle/On/Off SimConnect Connection", "SimConnect Connection - {0}")] - [TouchPortalActionChoice(new [] { "Toggle", "On", "Off", "Reload States" })] + [TouchPortalAction("Connection", "Connection", "Toggle/On/Off SimConnect Connection", "SimConnect Connection - {0}")] + [TouchPortalActionChoice(new [] { "Toggle", "On", "Off", "Reload States" }, Id = "Action")] [TouchPortalActionMapping("ToggleConnection", "Toggle")] [TouchPortalActionMapping("Connect", "On")] [TouchPortalActionMapping("Disconnect", "Off")] @@ -15,12 +15,10 @@ internal static class PluginMapping { public static readonly object Connection; - [TouchPortalAction("SetSimVar", "Set Simulator Variable Value", "MSFS", "Sets a value on any loaded State which is marked as settable.", "Set Variable {0} to {1} (release AI: {2})")] - [TouchPortalActionChoice(new[] { "" }, "")] - [TouchPortalActionText("0")] - //[TouchPortalActionChoice(new[] { "No", "Yes" })] - [TouchPortalActionSwitch(false)] - [TouchPortalActionMapping("SetSimVar")] + [TouchPortalAction("SetSimVar", "Set Simulator Variable Value", "Sets a value on any loaded State which is marked as settable.", "Set Variable {0} to {1} (release AI: {2})")] + [TouchPortalActionChoice(new[] { "" }, "", Id = "VarName", Label = "Simulator Variable")] + [TouchPortalActionText("0", Id = "Value", Label = "Value")] + [TouchPortalActionSwitch(false, Id = "RelAI", Label = "Release AI")] public static readonly object SetSimVar; } @@ -72,9 +70,9 @@ public static class Settings MaxLength = 255 }; - [TouchPortalAction("ActionRepeatInterval", "Action Repeat Interval", "MSFS", "Held Action Repeat Rate (ms)", "Repeat Interval: {0} to/by: {1} ms", true)] - [TouchPortalActionChoice(new[] { "Set", "Increment", "Decrement" })] - [TouchPortalActionText("450", 50, int.MaxValue)] + [TouchPortalAction("ActionRepeatInterval", "Action Repeat Interval", "Held Action Repeat Rate (ms)", "Repeat Interval: {0} to/by: {1} ms", true)] + [TouchPortalActionChoice(new[] { "Set", "Increment", "Decrement" }, Id = "Action")] + [TouchPortalActionText("450", 50, int.MaxValue, Id = "Interval")] [TouchPortalActionMapping("ActionRepeatIntervalSet", "Set")] [TouchPortalActionMapping("ActionRepeatIntervalInc", "Increment")] [TouchPortalActionMapping("ActionRepeatIntervalDec", "Decrement")] @@ -92,9 +90,14 @@ public static class Settings // IDs for handling internal events internal enum Plugin : short { - // Starting point - Init = 255, + None = 0, + + // Action IDs + Connection, + ActionRepeatInterval, + SetSimVar, + // Action choice mapping IDs ToggleConnection, Connect, Disconnect, @@ -104,7 +107,6 @@ internal enum Plugin : short { ActionRepeatIntervalDec, ActionRepeatIntervalSet, - SetSimVar, } // Dynamically generated SimConnect client event IDs are "parented" to this enum type, diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 43a18f8..bfc62fa 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -68,6 +68,8 @@ public PluginService(IHostApplicationLifetime hostApplicationLifetime, ILogger

n).ToArray(); - _client.ChoiceUpdate(PluginId + ".Plugin.Action.SetSimVar.Data.0", list); + _client.ChoiceUpdate(PluginId + ".Plugin.Action.SetSimVar.Data.VarName", list); } private void ClearRepeatingActions() { @@ -327,25 +329,8 @@ private void ClearRepeatingActions() { } } - private void ProcessEvent(ActionEvent actionEvent) { - if (!actionsDictionary.TryGetValue(actionEvent.ActionId, out ActionEventType action)) - return; - - var dataArry = actionEvent.Data.Values.ToArray(); - if (!action.TryGetEventMapping(in dataArry, out Enum eventId)) - return; - - if (action.InternalEvent) - ProcessInternalEvent(action, eventId, in dataArry); - else if (_simConnectService.IsConnected()) - ProcessSimEvent(action, eventId, in dataArry); - } - - private void ProcessInternalEvent(ActionEventType action, Enum eventId, in string[] dataArry) { - Plugin pluginEventId = (Plugin)eventId; - _logger.LogDebug($"Firing Internal Event - action: {action.ActionId}; enum: {pluginEventId}; data: {string.Join(", ", dataArry)}"); - - switch (pluginEventId) { + private void ProcessPluginCommandAction(Plugin actionId) { + switch (actionId) { case Plugin.ToggleConnection: autoReconnectSimConnect = !autoReconnectSimConnect; if (_simConnectService.IsConnected()) { @@ -357,12 +342,14 @@ private void ProcessInternalEvent(ActionEventType action, Enum eventId, in strin UpdateSimConnectState(); } break; + case Plugin.Connect: autoReconnectSimConnect = true; if (!_simConnectService.IsConnected()) _simConnectionRequest.Set(); UpdateSimConnectState(); break; + case Plugin.Disconnect: autoReconnectSimConnect = false; _simConnectionRequest.Reset(); @@ -371,46 +358,97 @@ private void ProcessInternalEvent(ActionEventType action, Enum eventId, in strin else UpdateSimConnectState(); break; + case Plugin.ReloadStates: SetupSimVars(); break; + } + } + private void ProcessPluginCommandAction(Plugin actionId, double value) { + switch (actionId) { case Plugin.ActionRepeatIntervalInc: case Plugin.ActionRepeatIntervalDec: case Plugin.ActionRepeatIntervalSet: - if (action.ValueIndex < dataArry.Length && double.TryParse(dataArry[action.ValueIndex], out var interval)) { - if (pluginEventId == Plugin.ActionRepeatIntervalInc) - interval = Settings.ActionRepeatInterval.RealValue + interval; - else if (pluginEventId == Plugin.ActionRepeatIntervalDec) - interval = Settings.ActionRepeatInterval.RealValue - interval; - interval = Math.Clamp(interval, Settings.ActionRepeatInterval.MinValue, Settings.ActionRepeatInterval.MaxValue); - if (interval != Settings.ActionRepeatInterval.RealValue) - _client.SettingUpdate(Settings.ActionRepeatInterval.Name, $"{interval:F0}"); // this will trigger the actual value update - } + if (actionId == Plugin.ActionRepeatIntervalInc) + value = Settings.ActionRepeatInterval.RealValue + value; + else if (actionId == Plugin.ActionRepeatIntervalDec) + value = Settings.ActionRepeatInterval.RealValue - value; + value = Math.Clamp(value, Settings.ActionRepeatInterval.MinValue, Settings.ActionRepeatInterval.MaxValue); + if (value != Settings.ActionRepeatInterval.RealValue) + _client.SettingUpdate(Settings.ActionRepeatInterval.Name, $"{value:F0}"); // this will trigger the actual value update + break; + } + } + + private void SetSimVarValueFromActionData(string varName, string value, bool releaseAi) { + if (!varName.EndsWith(']') || (varName.IndexOf('[') is var brIdx && brIdx++ < 0)) { + _logger.LogWarning($"Could not find ID in SimVar Name: '{varName}'"); + return; + } + + var varId = varName[brIdx..^1]; + if (!_settableSimVarIds.TryGetValue(varId, out Definition def) || !statesDictionary.TryGetValue(def, out SimVarItem simVar)) { + _logger.LogWarning($"Could not find definition for settable SimVar Id: '{varId}' Name: '{varName}'"); + return; + } + if (simVar.IsStringType) { + if (!simVar.SetValue(new StringVal(value))) { + _logger.LogWarning($"Could not set string value '{value}' for SimVar Id: '{varId}' Name: '{varName}'"); + return; + } + } + else if (!TryEvaluateValue(value, out double dVal) || !simVar.SetValue(dVal)) { + _logger.LogWarning($"Could not set numeric value '{value}' for SimVar Id: '{varId}' Name: '{varName}'"); + return; + } + if (releaseAi && !_simConnectService.ReleaseAIControl(simVar.Def)) + return; + + _simConnectService.SetDataOnSimObject(simVar); + } + + private void ProcessEvent(ActionEvent actionEvent) { + if (!actionsDictionary.TryGetValue(actionEvent.ActionId, out ActionEventType action)) + return; + + if (action.CategoryId == Groups.Plugin) { + ProcessInternalEvent(action, actionEvent.Data); + return; + } + + if (!_simConnectService.IsConnected()) + return; + + var dataArry = actionEvent.Data.Values.ToArray(); + if (action.TryGetEventMapping(in dataArry, out Enum eventId)) + ProcessSimEvent(action, eventId, in dataArry); + } + + private void ProcessInternalEvent(ActionEventType action, TouchPortalSDK.Messages.Models.ActionData data) { + Plugin pluginEventId = (Plugin)action.Id; + _logger.LogDebug($"Firing Internal Event - action: {action.ActionId}; enum: {pluginEventId}; data: {string.Join(", ", data.Select(d => $"{d.Key}={d.Value}"))}"); + switch (pluginEventId) { + case Plugin.Connection: { + // preserve backwards compatibility with old actions which used indexed data IDs + if (action.TryGetEventMapping(new string[] { data.GetValueOrDefault("Action", data.GetValueOrDefault("0")) }, out Enum eventId)) + ProcessPluginCommandAction((Plugin)eventId); break; + } - case Plugin.SetSimVar: - if (dataArry.Length > 2 && dataArry[0].EndsWith(']') && (dataArry[0].IndexOf('[') is var brIdx && brIdx++ > -1)) { - var varId = dataArry[0][brIdx..^1]; - if (!_settableSimVarIds.TryGetValue(varId, out Definition def) || !statesDictionary.TryGetValue(def, out SimVarItem simVar)) { - _logger.LogWarning($"Could not find definition for settable SimVar Id: '{varId}' Name: '{dataArry[0]}'"); - break; - } - if (simVar.IsStringType) { - if (!simVar.SetValue(new StringVal(dataArry[1]))) { - _logger.LogWarning($"Could not set string value '{dataArry[1]}' for SimVar Id: '{varId}' Name: '{dataArry[0]}'"); - break; - } - } - else if (!TryEvaluateValue(dataArry[1], out double dVal) || !simVar.SetValue(dVal)) { - _logger.LogWarning($"Could not set numeric value '{dataArry[1]}' for SimVar Id: '{varId}' Name: '{dataArry[0]}'"); - break; - } - if (new BooleanString(dataArry[2]) && !_simConnectService.ReleaseAIControl(simVar.Def)) - break; - _simConnectService.SetDataOnSimObject(simVar); - } + case Plugin.ActionRepeatInterval: { + // preserve backwards compatibility with old actions which used indexed data IDs + if (action.TryGetEventMapping(new string[] { data.GetValueOrDefault("Action", data.GetValueOrDefault("0")) }, out Enum eventId) && + double.TryParse(data.GetValueOrDefault("Interval", data.GetValueOrDefault("1")), out var interval)) + ProcessPluginCommandAction((Plugin)eventId, interval); break; + } + + case Plugin.SetSimVar: { + if (data.TryGetValue("VarName", out var varName) && data.TryGetValue("Value", out var value) && data.TryGetValue("RelAi", out var relAi)) + SetSimVarValueFromActionData(varName, value, new BooleanString(relAi)); + break; + } default: // No other types of events supported right now. diff --git a/MSFSTouchPortalPlugin/Services/ReflectionService.cs b/MSFSTouchPortalPlugin/Services/ReflectionService.cs index 8a6165e..bb36fd5 100644 --- a/MSFSTouchPortalPlugin/Services/ReflectionService.cs +++ b/MSFSTouchPortalPlugin/Services/ReflectionService.cs @@ -29,9 +29,9 @@ public ReflectionService(ILogger logger) { // Mapping of the generated SimConnect client event ID Enum to the SimConnect event name and group id. // Primarily used to register events with SimConnect, but can also be useful for debug/logging. // Structure is: -> new { string EventName, Enums.Groups GroupId } - private static readonly Dictionary clientEventIdToNameMap = new(); + private static readonly Dictionary clientEventIdToNameMap = new(); - public ref readonly Dictionary GetClientEventIdToNameMap() => ref clientEventIdToNameMap; + public ref readonly Dictionary GetClientEventIdToNameMap() => ref clientEventIdToNameMap; public string GetSimEventNameById(Enum id) { return clientEventIdToNameMap.TryGetValue(id, out var entry) ? entry.EventName : "[unknown event]"; } @@ -64,13 +64,46 @@ public IEnumerable GetActionAttributes(Groups catId) return ret; } - public Dictionary GetActionEvents() { + private Dictionary GetInternalActionEvents() { var returnDict = new Dictionary(); - int nextId = (int)SimEventClientId.Init + 1; + var catAttribs = GetActionAttributes(Groups.Plugin); + // Loop over all actions which have an action mapping, which is some unique combination of value(s) mapped to a SimConnect event name or internal event enum + foreach (var actAttr in catAttribs) { + // Create the action data object to store in the return dict, using the meta data we've collected so far. + ActionEventType act = new ActionEventType { + Id = Enum.Parse(actAttr.Id), + ActionId = actAttr.Id, + CategoryId = Groups.Plugin, + DataAttributes = actAttr.Data.ToDictionary(d => d.Id, d => d) + }; + // Put into returned collection + if (!returnDict.TryAdd($"{TouchPortalBaseId}.{Groups.Plugin}.Action.{act.ActionId}", act)) { + _logger.LogWarning($"Duplicate action ID found for Plugin action '{act.ActionId}', skipping.'"); + continue; + } + // for Choice types, we combine them to create a unique lookup key which maps to a particular event. + if (actAttr.Mappings.Any()) { + List fmtStrList = new(); + for (int i=0, e = actAttr.Data.Count(d => d.ValueType == DataType.Choice); i < e; ++i) + fmtStrList.Add($"{{{i}}}"); + act.KeyFormatStr = string.Join(",", fmtStrList); + foreach (var ma in actAttr.Mappings) { + if (!act.TpActionToEventMap.TryAdd($"{string.Join(",", ma.Values)}", Enum.Parse(ma.ActionId))) + _logger.LogWarning($"Duplicate action-to-event mapping found for Plugin action {act.ActionId} with choices '{string.Join(",", ma.Values)} for event '{ma.ActionId}'."); + } + } + } + return returnDict; + } + public Dictionary GetActionEvents() { + int nextId = (int)SimEventClientId.Init + 1; + var returnDict = GetInternalActionEvents(); var catAttribs = GetCategoryAttributes(); + foreach (var catAttr in catAttribs) { - bool internalEvent = catAttr.Id == Groups.Plugin; + if (catAttr.Id == Groups.Plugin) + continue; // Loop over all actions which have an action mapping, which is some unique combination of value(s) mapped to a SimConnect event name or internal event enum foreach (var actAttr in catAttr.Actions) { // check that there are any mappings at all @@ -80,42 +113,46 @@ public Dictionary GetActionEvents() { } // Create the action data object to store in the return dict, using the meta data we've collected so far. ActionEventType act = new ActionEventType { - InternalEvent = internalEvent, CategoryId = catAttr.Id, ActionId = actAttr.Id }; + // Put into returned collection + if (!returnDict.TryAdd($"{TouchPortalBaseId}.{catAttr.Id}.Action.{act.ActionId}", act)) { + _logger.LogWarning($"Duplicate action ID found for action '{act.ActionId}' in category '{catAttr.Id}', skipping.'"); + continue; + } + // Check for mappings + if (!actAttr.Mappings.Any()) { + _logger.LogWarning($"No ActionMapping attributes found for action '{act.ActionId}' in category '{catAttr.Id}', skipping.'"); + continue; + } // Loop over all the data attributes to find the "choice" types for mapping and also the index of any free-form data value field int i = 0; + List fmtStrList = new(); foreach (var dataAttrib in actAttr.Data) { if (dataAttrib.ValueType == DataType.Choice) { // for Choice types, we combine them to create a unique lookup key which maps to a particular event. - if (act.KeyFormatStr.Length > 1) - act.KeyFormatStr += ","; - act.KeyFormatStr += $"{{{i}}}"; + fmtStrList.Add($"{{{i++}}}"); } else { // we only support one free-form value per mapping, which is all SimConnect supports, and internal events can handle the data as necessary already. - act.ValueIndex = i; + act.ValueIndex = i++; act.MinValue = dataAttrib.MinValue; act.MaxValue = dataAttrib.MaxValue; act.ValueType = dataAttrib.ValueType; } - ++i; } + act.KeyFormatStr = string.Join(",", fmtStrList); // Now get all the action mappings to produce the final list of all possible action events foreach (var ma in actAttr.Mappings) { - Enum mapTarget = internalEvent ? Enum.Parse(ma.ActionId) : (SimEventClientId)nextId++; + Enum mapTarget = (SimEventClientId)nextId++; // Put into collections if (!act.TpActionToEventMap.TryAdd($"{string.Join(",", ma.Values)}", mapTarget)) - _logger.LogWarning($"Duplicate action-to-event mapping found for action {act.ActionId} with choices '{string.Join(",", ma.Values)} for event '{ma.ActionId}'!'"); + _logger.LogWarning($"Duplicate action-to-event mapping found for action {act.ActionId} with choices '{string.Join(",", ma.Values)} for event '{ma.ActionId}'."); // keep track of generated event IDs for Sim actions (for registering to SimConnect, and debug) - if (!internalEvent) - clientEventIdToNameMap[mapTarget] = new { EventName = ma.ActionId, GroupId = catAttr.Id }; + clientEventIdToNameMap[mapTarget] = new SimEventRecord(catAttr.Id, ma.ActionId); } - // Put into returned collection - if (!returnDict.TryAdd($"{TouchPortalBaseId}.{catAttr.Id}.Action.{act.ActionId}", act)) - _logger.LogWarning($"Duplicate action ID found for action '{act.ActionId}' in category '{catAttr.Id}', skipping.'"); } // actions loop } // categories loop diff --git a/MSFSTouchPortalPlugin/Types/ActionEventType.cs b/MSFSTouchPortalPlugin/Types/ActionEventType.cs index 055a97f..34b5153 100644 --- a/MSFSTouchPortalPlugin/Types/ActionEventType.cs +++ b/MSFSTouchPortalPlugin/Types/ActionEventType.cs @@ -1,4 +1,5 @@ -using MSFSTouchPortalPlugin.Enums; +using MSFSTouchPortalPlugin.Attributes; +using MSFSTouchPortalPlugin.Enums; using System; using System.Collections.Generic; using System.Linq; @@ -7,7 +8,7 @@ namespace MSFSTouchPortalPlugin.Types { internal class ActionEventType { - public bool InternalEvent = false; + public Enum Id; public Groups CategoryId; public string ActionId; public int ValueIndex = -1; @@ -23,7 +24,7 @@ internal class ActionEventType // possible future use //public object ActionObject = null; // the member to which all action attributes are assigned to - //public IReadOnlyCollection DataAttributes; // list of all data type attributes + public IReadOnlyDictionary DataAttributes; // list of all data type attributes // Get a unique event ID for this action, possibly based on data values // in the \c data array. Certain combination of values, eg. from choices, diff --git a/MSFSTouchPortalPlugin/Types/SimEventRecord.cs b/MSFSTouchPortalPlugin/Types/SimEventRecord.cs new file mode 100644 index 0000000..1584018 --- /dev/null +++ b/MSFSTouchPortalPlugin/Types/SimEventRecord.cs @@ -0,0 +1,14 @@ +using MSFSTouchPortalPlugin.Enums; + +namespace MSFSTouchPortalPlugin.Types +{ + public struct SimEventRecord + { + public Groups GroupId; + public string EventName; + public SimEventRecord(Groups id, string name) { + GroupId = id; + EventName = name; + } + } +} diff --git a/MSFSTouchPortalPlugin/lib/TouchPortalSDK/TouchPortalSDK.dll b/MSFSTouchPortalPlugin/lib/TouchPortalSDK/TouchPortalSDK.dll index db99087950e4824f8f65cf19eb2de447efa52eec..9513a980e177fa578b3bbe4e2cb2c8a1387361bc 100644 GIT binary patch literal 39424 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 z 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 Xt((zPd Date: Sat, 9 Apr 2022 18:00:01 -0400 Subject: [PATCH 66/93] Refactor SimVar state loading to consolidate all settings to PluginConfig class; * Make the ActionAttributes accept actual enum type IDs for use in internal events instead of strings; * Rename `Plugin` enum to `PluginActions` and move to Enums namespace; * [Generator] Don't sort Plugin category states or actions; * Cosmetics. --- .../GenerateDoc.cs | 27 ++-- .../GenerateEntry.cs | 25 ++- .../Attributes/TouchPortalActionAttribute.cs | 9 +- .../TouchPortalActionMappingAttribute.cs | 11 +- .../Configuration/PluginConfig.cs | 141 ++++++++++------- .../Objects/Plugin/Plugin.cs | 39 ++--- .../Services/PluginService.cs | 144 ++++++++---------- .../Services/ReflectionService.cs | 5 +- .../Types/ActionEventType.cs | 3 + 9 files changed, 215 insertions(+), 189 deletions(-) diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index 25ddfc5..7ef01d0 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -59,23 +59,20 @@ private DocBase CreateModel() { }; // read states config - IEnumerable simVars; + // always include the internal plugin states + IEnumerable simVars = _pluginConfig.LoadPluginStates(); if (_options.StateFiles.Any()) { - if (!string.IsNullOrWhiteSpace(_options.StateFilesPath)) - PluginConfig.UserConfigFolder = _options.StateFilesPath; - simVars = _pluginConfig.LoadCustomSimVars(_options.StateFiles); + PluginConfig.UserConfigFolder = _options.StateFilesPath; + PluginConfig.UserStateFiles = string.Join(',', _options.StateFiles); model.Version += "
" + $"Custom configuration version {_options.ConfigVersion}
" + $"Custom State Definitions Source(s): {string.Join(", ", _options.StateFiles)}"; } - else { - simVars = _pluginConfig.LoadSimVarItems(false); - } - // always include the internal plugin states - simVars = simVars.Concat(_pluginConfig.LoadPluginStates()); + simVars = simVars.Concat(_pluginConfig.LoadSimVarStateConfigs()); var categegoryAttribs = _reflectionSvc.GetCategoryAttributes(); foreach (var catAttrib in categegoryAttribs) { + var category = new DocCategory { CategoryId = catAttrib.Id, Id = $"{_options.PluginId}.{catAttrib.Id}", @@ -128,8 +125,6 @@ private DocBase CreateModel() { } } // actions - category.Actions = category.Actions.OrderBy(c => c.Name).ToList(); - // Add States var categoryStates = simVars.Where(s => s.CategoryId == catAttrib.Id); foreach (SimVarItem state in categoryStates) { @@ -146,8 +141,12 @@ private DocBase CreateModel() { category.States.Add(newState); } - // Sort the states - category.States = category.States.OrderBy(c => c.Id).ToList(); + // Sort the actions and states for SimConnect groups + if (catAttrib.Id != MSFSTouchPortalPlugin.Enums.Groups.Plugin) { + category.Actions = category.Actions.OrderBy(c => c.Name).ToList(); + category.States = category.States.OrderBy(c => c.Description).ToList(); + } + } // categories loop // Settings @@ -275,7 +274,7 @@ private static string CreateMarkdown(DocBase model) { // Loop States s.Append("#### States\n\n"); s.Append($" **Base Id:** {cat.Id}.State.\n\n"); - s.Append("| Id | SimVar Name | Description | Unit | Format | DefaultValue | Can Set |\n"); + s.Append("| Id | SimVar Name | Description | Unit | Format | DefaultValue | Settable |\n"); s.Append("| --- | --- | --- | --- | --- | --- | --- |\n"); cat.States.ForEach(state => { s.Append($"| {state.Id} | {state.SimVarName} | {state.Description} | {state.Unit} | {state.FormattingString} | {state.DefaultValue}"); diff --git a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs index 285fb9a..642d04b 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs @@ -53,19 +53,17 @@ public void Generate() { } // read states config - IEnumerable simVars = Array.Empty(); + // always include the internal plugin states + IEnumerable simVars = _pluginConfig.LoadPluginStates(); if (useCustomConfigs) { - if (!string.IsNullOrWhiteSpace(_options.StateFilesPath)) - PluginConfig.UserConfigFolder = _options.StateFilesPath; - simVars = _pluginConfig.LoadCustomSimVars(_options.StateFiles); - _logger.LogInformation($"Generating entry.tp v{vNum:X} to '{_options.OutputPath}' with Custom states from file(s): {string.Join(", ", _options.StateFiles)}"); + PluginConfig.UserConfigFolder = _options.StateFilesPath; + PluginConfig.UserStateFiles = string.Join(',', _options.StateFiles); + _logger.LogInformation($"Generating entry.tp v{vNum:X} to '{_options.OutputPath}' with Custom states from file(s): {PluginConfig.UserStateFiles}"); } else { - simVars = _pluginConfig.LoadSimVarItems(false); _logger.LogInformation($"Generating entry.tp v{vNum:X} to '{_options.OutputPath}' with Default states."); } - // always include the internal plugin states - simVars = simVars.Concat(_pluginConfig.LoadPluginStates()); + simVars = simVars.Concat(_pluginConfig.LoadSimVarStateConfigs()); // Setup Base Model var model = new Base { @@ -131,9 +129,6 @@ public void Generate() { } // actions - // Sort the actions - category.Actions = category.Actions.OrderBy(c => c.Name).ToList(); - // States var categoryStates = simVars.Where(s => s.CategoryId == catAttrib.Id); foreach (SimVarItem state in categoryStates) { @@ -150,9 +145,11 @@ public void Generate() { _logger.LogWarning($"Duplicate state ID found: '{newState.Id}', skipping.'"); } - // Sort the states - category.States = category.States.OrderBy(c => c.Description).ToList(); - + // Sort the actions and states for SimConnect groups + if (catAttrib.Id != MSFSTouchPortalPlugin.Enums.Groups.Plugin) { + category.Actions = category.Actions.OrderBy(c => c.Name).ToList(); + category.States = category.States.OrderBy(c => c.Description).ToList(); + } } // categories loop // Settings diff --git a/MSFSTouchPortalPlugin/Attributes/TouchPortalActionAttribute.cs b/MSFSTouchPortalPlugin/Attributes/TouchPortalActionAttribute.cs index 667f274..0ecc17a 100644 --- a/MSFSTouchPortalPlugin/Attributes/TouchPortalActionAttribute.cs +++ b/MSFSTouchPortalPlugin/Attributes/TouchPortalActionAttribute.cs @@ -7,6 +7,7 @@ namespace MSFSTouchPortalPlugin.Attributes public class TouchPortalActionAttribute : Attribute { public string Id { get; set; } + public Enum EnumId { get; set; } = default; public string Name { get; set; } public string Prefix { get; set; } public string Description { get; set; } @@ -20,6 +21,11 @@ public TouchPortalActionAttribute(string id, string name, string description, st SetupProperties(id, name, "MSFS", description, format, holdable, "communicate"); } + public TouchPortalActionAttribute(PluginActions id, string name, string description, string format, bool holdable = false) { + SetupProperties(id.ToString(), name, "MSFS", description, format, holdable, "communicate"); + EnumId = id; + } + public TouchPortalActionAttribute(string id, string name, string prefix, string description, string format, bool holdable = false) { SetupProperties(id, name, prefix, description, format, holdable, "communicate"); } @@ -113,12 +119,13 @@ public TouchPortalActionColorAttribute(string defaultValue = default) : base(Dat public class TouchPortalActionChoiceAttribute : TouchPortalActionTextAttribute { - public TouchPortalActionChoiceAttribute(string[] choiceValues, string defaultValue = default) : base(DataType.Choice, defaultValue) { ChoiceValues = choiceValues; if (defaultValue == default && choiceValues?.Length > 0) DefaultValue = choiceValues[0]; } + + public TouchPortalActionChoiceAttribute(string choiceValue, string defaultValue = default) : this(new [] { choiceValue }, defaultValue) { } } public class TouchPortalActionSwitchAttribute : TouchPortalActionDataAttribute diff --git a/MSFSTouchPortalPlugin/Attributes/TouchPortalActionMappingAttribute.cs b/MSFSTouchPortalPlugin/Attributes/TouchPortalActionMappingAttribute.cs index 3da2453..974ef7e 100644 --- a/MSFSTouchPortalPlugin/Attributes/TouchPortalActionMappingAttribute.cs +++ b/MSFSTouchPortalPlugin/Attributes/TouchPortalActionMappingAttribute.cs @@ -1,10 +1,12 @@ -using System; +using MSFSTouchPortalPlugin.Enums; +using System; namespace MSFSTouchPortalPlugin.Attributes { [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] public class TouchPortalActionMappingAttribute : Attribute { + public Enum EnumId { get; set; } = default; public string ActionId { get; set; } public string[] Values { get; set; } = Array.Empty(); @@ -14,9 +16,16 @@ public TouchPortalActionMappingAttribute(string actionId, string value1, string this(actionId, new[] { value1, value2 }) { } public TouchPortalActionMappingAttribute(string actionId, string value1, string value2, string value3) : this(actionId, new[] { value1, value2, value3 }) { } + public TouchPortalActionMappingAttribute(string actionId, string[] values = null) { ActionId = actionId; Values = values ?? Array.Empty(); } + + public TouchPortalActionMappingAttribute(PluginActions actionId, string value) : this(actionId, new[] { value }) { } + public TouchPortalActionMappingAttribute(PluginActions actionId, string[] values = null) : this(actionId.ToString(), values) { + EnumId = actionId; + } + } } diff --git a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs index 63abf0b..48b0065 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs +++ b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs @@ -1,11 +1,11 @@ -using MSFSTouchPortalPlugin.Enums; +using MSFSTouchPortalPlugin.Constants; +using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Types; using System; using System.Collections.Generic; using System.Linq; using System.IO; using System.Text; -using System.Runtime.Serialization; using Microsoft.Extensions.Logging; namespace MSFSTouchPortalPlugin.Configuration @@ -13,6 +13,10 @@ namespace MSFSTouchPortalPlugin.Configuration internal class PluginConfig { + // these constants are for entry.tp version number en/decoding + public const uint ENTRY_FILE_VER_MASK_NOSTATES = 0x80000000; // leading bit indicates an entry file with no static SimVar states at all (create all states dynamically) + public const uint ENTRY_FILE_VER_MASK_CUSTOM = 0x7F000000; // any of the other 7 bits indicates an entry file generated from custom config files (do not create any dynamic states) + public const ushort ENTRY_FILE_CONF_VER_SHIFT = 24; // bit position of custom config version number, used as the last 7 bits of file version ///

/// RootName is used as the basis for the user folder name and TP State ID generation. @@ -28,22 +32,40 @@ internal class PluginConfig public static string UserConfigFolder { get => _currentUserCfgFolder; set { - if (string.IsNullOrWhiteSpace(value)) + if (string.IsNullOrWhiteSpace(value) || (value.Trim() is var val && val.ToLower() == STR_DEFAULT)) _currentUserCfgFolder = _defaultUserCfgFolder; else - _currentUserCfgFolder = value; + _currentUserCfgFolder = val; } } - private static readonly string _defaultUserCfgFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), RootName); - private static string _currentUserCfgFolder = _defaultUserCfgFolder; + public static string UserStateFiles + { + get => string.Join(',', _userStateFiles); + set { + if (string.IsNullOrWhiteSpace(value) || value.Trim().ToLower().Replace(".ini", "") == STR_DEFAULT) { + _userStateFiles = Array.Empty(); + return; + } + _userStateFiles = value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + for (int i=0, e=_userStateFiles.Length; i < e; ++i) { + if (_userStateFiles[i].ToLower().Replace(".ini", "") == STR_DEFAULT) + _userStateFiles[i] = STR_DEFAULT; // "normalize" the default string for simpler comparison later. + } + } + } + + public static bool HaveUserStateFiles => _userStateFiles.Any(); + public static IReadOnlyCollection UserStateFilesArray => _userStateFiles; - public const uint ENTRY_FILE_VER_MASK_NOSTATES = 0x80000000; // leading bit indicates an entry file with no static SimVar states at all (create all states dynamically) - public const uint ENTRY_FILE_VER_MASK_CUSTOM = 0x7F000000; // any of the other 7 bits indicates an entry file generated from custom config files (do not create any dynamic states) - public const ushort ENTRY_FILE_CONF_VER_SHIFT = 24; // bit position of custom config version number, used as the last 7 bits of file version + const string STR_DEFAULT = "default"; + private static readonly string _defaultUserCfgFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), RootName); + private static string _currentUserCfgFolder = _defaultUserCfgFolder; + private static string[] _userStateFiles = Array.Empty(); private readonly ILogger _logger; + public PluginConfig(ILogger logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -61,7 +83,7 @@ public bool CopySimConnectConfig() { return true; } catch (Exception e) { - _logger.LogWarning(e, "Error trying to copy SimConnect.cfg file from user's AppData folder:"); + _logger.LogError(e, $"Error trying to copy SimConnect.cfg file from '{srcFile}': {e.Message}"); return false; } } @@ -70,29 +92,12 @@ public bool CopySimConnectConfig() { public IReadOnlyCollection LoadSimVarItems(bool isUserConfig = true, string filename = default) { List ret = new(); - - // qualify the file name and path - if (filename == default) - filename = StatesConfigFile; - else if (string.IsNullOrWhiteSpace(Path.GetExtension(filename))) - filename += ".ini"; - if (filename.IndexOfAny(new char[] { '\\', '/' }) < 0) - filename = Path.Combine(isUserConfig ? UserConfigFolder : AppConfigFolder, filename); - - if (!File.Exists(filename)) { - _logger.LogError($"Cannot load SimVar states, file not found at '{filename}'"); - return ret; - } + filename = GetFullFilePath(filename, isUserConfig); _logger.LogDebug($"Loading SimVars from file '{filename}'..."); - SharpConfig.Configuration cfg; - try { - cfg = SharpConfig.Configuration.LoadFromFile(filename, Encoding.UTF8); - } - catch (Exception e) { - _logger.LogWarning(e, $"Configuration LoadFromFile error in '{filename}':"); + if (!LoadFromFile(filename, out var cfg)) return ret; - } + foreach (SharpConfig.Section item in cfg) { if (item.Name == SharpConfig.Section.DefaultSectionName) continue; @@ -101,11 +106,11 @@ public IReadOnlyCollection LoadSimVarItems(bool isUserConfig = true, simVar = item.ToObject(); } catch (Exception e) { - _logger.LogWarning(e, $"Deserialize exception for section '{item}':"); + _logger.LogError(e, $"Deserialize exception for section '{item}': {e.Message}:"); continue; } if (simVar == null) { - _logger.LogWarning($"Produced SimVar is null from section '{item}':"); + _logger.LogError($"Produced SimVar is null from section '{item}':"); continue; } simVar.Id = item.Name; @@ -124,14 +129,14 @@ public IReadOnlyCollection LoadSimVarItems(bool isUserConfig = true, return ret; } - public IReadOnlyCollection LoadCustomSimVars(IEnumerable fileNames) { + public IReadOnlyCollection LoadSimVarStateConfigs() { + if (!HaveUserStateFiles) + return LoadSimVarItems(false); + SimVarItem[] ret = Array.Empty(); - foreach (var file in fileNames) { - // special case for anything named "default" which indicates the States.ini file in the plugin's install folder - if (file.ToLower()[0..7] == "default") - ret = ret.Concat(LoadSimVarItems(false)).ToArray(); - else - ret = ret.Concat(LoadSimVarItems(true, file)).ToArray(); + foreach (var file in UserStateFilesArray) { + bool isUserCfg = file != STR_DEFAULT; + ret = ret.Concat(LoadSimVarItems(isUserCfg, isUserCfg ? file : default)).ToArray(); } return ret; } @@ -139,10 +144,10 @@ public IReadOnlyCollection LoadCustomSimVars(IEnumerable fil public IReadOnlyCollection LoadPluginStates() => LoadSimVarItems(false, PluginStatesConfigFile); - public bool SaveSimVarItems(IReadOnlyCollection items, bool isUserConfig = true, string filename = default) { + public bool SaveSimVarItems(IEnumerable items, bool isUserConfig = true, string filename = default) { var cfg = new SharpConfig.Configuration(); Groups lastCatId = default; - + int count = 0; foreach (SimVarItem item in items) { if (item == null) continue; @@ -169,39 +174,61 @@ public bool SaveSimVarItems(IReadOnlyCollection items, bool isUserCo sect.Add("UpdateInterval", item.UpdateInterval); if (item.DeltaEpsilon != 0.0f) sect.Add("DeltaEpsilon", item.DeltaEpsilon); + + ++count; } catch (Exception e) { - _logger.LogWarning(e, $"Serialize exception for {item.ToDebugString()}:"); + _logger.LogError(e, $"Serialize exception for {item.ToDebugString()}: {e.Message}:"); } } + filename = GetFullFilePath(filename, isUserConfig); + if (SaveToFile(cfg, filename) is bool ok) + _logger.LogDebug($"Saved {count} SimVars to '{filename}'"); + return ok; + } + + + // Private common utility methods + + static string GetFullFilePath(string filename, bool isUserConfig) { if (filename == default) filename = StatesConfigFile; - return SaveToFile(cfg, isUserConfig ? UserConfigFolder : AppConfigFolder, filename); + else if (string.IsNullOrWhiteSpace(Path.GetExtension(filename))) + filename += ".ini"; + if (filename.IndexOfAny(new char[] { '\\', '/' }) < 0) + filename = Path.Combine(isUserConfig ? UserConfigFolder : AppConfigFolder, filename); + return filename; + } + + bool LoadFromFile(string filename, out SharpConfig.Configuration cfg) { + cfg = null; + if (!File.Exists(filename)) { + _logger.LogError($"Cannot import SimVars, file not found at '{filename}'"); + return false; + } + try { + cfg = SharpConfig.Configuration.LoadFromFile(filename, Encoding.UTF8); + } + catch (Exception e) { + _logger.LogError(e, $"Configuration error in '{filename}': {e.Message}"); + return false; + } + return true; } - private bool SaveToFile(SharpConfig.Configuration cfg, string folder, string filename) { + bool SaveToFile(SharpConfig.Configuration cfg, string filename) { try { - Directory.CreateDirectory(folder); - cfg.SaveToFile(Path.Combine(folder, filename), Encoding.UTF8); + Directory.CreateDirectory(Path.GetDirectoryName(filename)); + cfg.SaveToFile(filename, Encoding.UTF8); return true; } catch (Exception e) { - _logger.LogWarning(e, $"Error trying to write config file '{filename}' to folder '{folder}'"); + _logger.LogError(e, $"Error trying to write config file '{filename}': {e.Message}"); } return false; } } - [Serializable] - public class DuplicateIdException : Exception - { - public DuplicateIdException() : base() { } - public DuplicateIdException(string message) : base(message) { } - protected DuplicateIdException(SerializationInfo info, StreamingContext context) - : base(info, context) { - } - } - } diff --git a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs index e1e1302..67442e0 100644 --- a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs +++ b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs @@ -5,18 +5,26 @@ namespace MSFSTouchPortalPlugin.Objects.Plugin { [TouchPortalCategory(Groups.Plugin)] - internal static class PluginMapping { - [TouchPortalAction("Connection", "Connection", "Toggle/On/Off SimConnect Connection", "SimConnect Connection - {0}")] - [TouchPortalActionChoice(new [] { "Toggle", "On", "Off", "Reload States" }, Id = "Action")] - [TouchPortalActionMapping("ToggleConnection", "Toggle")] - [TouchPortalActionMapping("Connect", "On")] - [TouchPortalActionMapping("Disconnect", "Off")] - [TouchPortalActionMapping("ReloadStates", "Reload States")] + internal static class PluginMapping + { + [TouchPortalAction(PluginActions.Connection, "Connection", "Toggle/On/Off SimConnect Connection", "SimConnect Connection - {0}")] + [TouchPortalActionChoice(new[] { "Toggle", "On", "Off", "Reload States" }, Id = "Action")] + [TouchPortalActionMapping(PluginActions.ToggleConnection, "Toggle")] + [TouchPortalActionMapping(PluginActions.Connect, "On")] + [TouchPortalActionMapping(PluginActions.Disconnect, "Off")] + [TouchPortalActionMapping(PluginActions.ReloadStates, "Reload States")] public static readonly object Connection; + [TouchPortalAction(PluginActions.ActionRepeatInterval, "Action Repeat Interval", "Held Action Repeat Rate (ms)", "Repeat Interval: {0} to/by: {1} ms", true)] + [TouchPortalActionChoice(new[] { "Set", "Increment", "Decrement" }, Id = "Action")] + [TouchPortalActionText("450", 50, int.MaxValue, Id = "Value")] + [TouchPortalActionMapping(PluginActions.ActionRepeatIntervalSet, "Set")] + [TouchPortalActionMapping(PluginActions.ActionRepeatIntervalInc, "Increment")] + [TouchPortalActionMapping(PluginActions.ActionRepeatIntervalDec, "Decrement")] + public static readonly object ActionRepeatInterval; - [TouchPortalAction("SetSimVar", "Set Simulator Variable Value", "Sets a value on any loaded State which is marked as settable.", "Set Variable {0} to {1} (release AI: {2})")] - [TouchPortalActionChoice(new[] { "" }, "", Id = "VarName", Label = "Simulator Variable")] + [TouchPortalAction(PluginActions.SetSimVar, "Set Simulator Variable Value", "Sets a value on any loaded State which is marked as settable.", "Set Variable {0} to {1} (release AI: {2})")] + [TouchPortalActionChoice("", "", Id = "VarName", Label = "Simulator Variable")] [TouchPortalActionText("0", Id = "Value", Label = "Value")] [TouchPortalActionSwitch(false, Id = "RelAI", Label = "Release AI")] public static readonly object SetSimVar; @@ -70,12 +78,6 @@ public static class Settings MaxLength = 255 }; - [TouchPortalAction("ActionRepeatInterval", "Action Repeat Interval", "Held Action Repeat Rate (ms)", "Repeat Interval: {0} to/by: {1} ms", true)] - [TouchPortalActionChoice(new[] { "Set", "Increment", "Decrement" }, Id = "Action")] - [TouchPortalActionText("450", 50, int.MaxValue, Id = "Interval")] - [TouchPortalActionMapping("ActionRepeatIntervalSet", "Set")] - [TouchPortalActionMapping("ActionRepeatIntervalInc", "Increment")] - [TouchPortalActionMapping("ActionRepeatIntervalDec", "Decrement")] public static readonly PluginSetting ActionRepeatInterval = new PluginSetting("ActionRepeatInterval", DataType.Number) { Name = "Held Action Repeat Rate (ms)", Description = "Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Action Repeat Interval' action. This setting cannot be changed from the TP Plugin Settings page (even though it appears to be editable on there).", @@ -85,11 +87,14 @@ public static class Settings ReadOnly = true, TouchPortalStateId = "ActionRepeatInterval" }; - } +} +namespace MSFSTouchPortalPlugin.Enums +{ // IDs for handling internal events - internal enum Plugin : short { + public enum PluginActions : short + { None = 0, // Action IDs diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index bfc62fa..63417c5 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -16,6 +16,7 @@ using TouchPortalSDK; using TouchPortalSDK.Interfaces; using TouchPortalSDK.Messages.Events; +using TouchPortalSDK.Messages.Models; using Timer = MSFSTouchPortalPlugin.Helpers.UnthreadedTimer; namespace MSFSTouchPortalPlugin.Services @@ -35,7 +36,7 @@ internal class PluginService : IPluginService, ITouchPortalEventHandler private CancellationToken _cancellationToken; private CancellationTokenSource _simTasksCTS; private CancellationToken _simTasksCancelToken; - private Task _timerEventsTask; + private Task _pluginEventsTask; private readonly ManualResetEventSlim _simConnectionRequest = new(false); private bool autoReconnectSimConnect = false; private EntryFileType _entryFileType = EntryFileType.Default; @@ -126,6 +127,7 @@ private bool Initialize() { SetupEventLists(); // set these up first to have access to defined plugin settings if (!_client.Connect()) { + _logger.LogCritical("Failed to connect to Touch Portal! Quitting."); return false; } @@ -146,14 +148,16 @@ private async Task SimConnectionMonitor() { try { while (!_cancellationToken.IsCancellationRequested) { _simConnectionRequest.Wait(_cancellationToken); - if (!_simConnectService.IsConnected() && !_simConnectService.Connect(Settings.SimConnectConfigIndex.UIntValue)) + if (!_simConnectService.IsConnected() && !_simConnectService.Connect(Settings.SimConnectConfigIndex.UIntValue)) { + _logger.LogWarning("Connection to Simulator failed, retrying in 10 seconds..."); await Task.Delay(10000, _cancellationToken); // delay 10s on connection error + } } } catch (OperationCanceledException) { /* ignore but exit */ } catch (ObjectDisposedException) { /* ignore but exit */ } catch (Exception e) { - _logger.LogError("Exception in SimConnectionMonitor task, cannot continue.", e); + _logger.LogError(e, "Exception in SimConnectionMonitor task, cannot continue."); } _logger.LogDebug("SimConnectionMonitor task stopped."); } @@ -175,7 +179,7 @@ private async Task PluginEventsTask() { catch (OperationCanceledException) { /* ignore but exit */ } catch (ObjectDisposedException) { /* ignore but exit */ } catch (Exception e) { - _logger.LogError("Exception in PluginEventsTask task, cannot continue.", e); + _logger.LogError(e, "Exception in PluginEventsTask task, cannot continue."); } _logger.LogDebug("PluginEventsTask task stopped."); } @@ -214,8 +218,8 @@ private void SimConnectEvent_OnConnect() { SetupSimVars(); // start checking timer events - _timerEventsTask = Task.Run(PluginEventsTask); - _timerEventsTask.ConfigureAwait(false); // needed? + _pluginEventsTask = Task.Run(PluginEventsTask); + _pluginEventsTask.ConfigureAwait(false); // needed? } private void SimConnectEvent_OnDataUpdateEvent(Definition def, Definition req, object data) { @@ -232,15 +236,15 @@ private void SimConnectEvent_OnDataUpdateEvent(Definition def, Definition req, o private void SimConnectEvent_OnDisconnect() { _simTasksCTS?.Cancel(); - if (_timerEventsTask.Status == TaskStatus.Running && !_timerEventsTask.Wait(5000)) - _logger.LogWarning("Timed events task timed out while stopping."); - try { _timerEventsTask.Dispose(); } + if (_pluginEventsTask.Status == TaskStatus.Running && !_pluginEventsTask.Wait(5000)) + _logger.LogWarning("PluginEventsTask timed out while stopping."); + try { _pluginEventsTask.Dispose(); } catch { /* ignore in case it hung */ } ClearRepeatingActions(); UpdateSimConnectState(); - _timerEventsTask = null; + _pluginEventsTask = null; _simTasksCTS?.Dispose(); _simTasksCTS = null; @@ -252,26 +256,14 @@ private void SimConnectEvent_OnDisconnect() { #region Plugin Events and Handlers ///////////////////////////////////// - private void SetupEventLists() { - actionsDictionary = _reflectionService.GetActionEvents(); - pluginSettingsDictionary = _reflectionService.GetSettings(); - } - private void SetupSimVars() { // We may need to generate all, some, or no states dynamically. bool allStatesDynamic = (_entryFileType == EntryFileType.NoStates); bool someStateDynamic = false; IEnumerable defaultStates = Array.Empty(); // in case we need to create _some_ custom states dynamically - // First we figure out which file(s) to load. - bool useCustomCfg = !string.IsNullOrWhiteSpace(Settings.UserStateFiles.StringValue) && Settings.UserStateFiles.StringValue.ToLower() != "default"; - if (useCustomCfg) { - // Create the file(s) list - string[] cfgFiles = Settings.UserStateFiles.StringValue.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - _logger.LogInformation($"Loading custom state file(s) '{string.Join(", ", cfgFiles)}' from folder '{PluginConfig.UserConfigFolder}'."); - // load the file(s); the special file name "default" (if present) will actually use the one from plugin's install folder. - statesDictionary = _pluginConfig.LoadCustomSimVars(cfgFiles).ToDictionary(s => s.Def, s => s); - + // First check if we're using a custom config. + if (PluginConfig.HaveUserStateFiles) { // if the user is NOT using dynamic states for everything (entry_no-states.tp) nor a custom entry.tp file, // then lets figure out what the default states are so we can create any missing ones dynamically if needed. if (_entryFileType == EntryFileType.Default) { @@ -279,11 +271,14 @@ private void SetupSimVars() { defaultStates = _pluginConfig.LoadSimVarItems(false).Select(s => s.Id); someStateDynamic = defaultStates.Any(); } + _logger.LogInformation($"Loading custom state file(s) '{PluginConfig.UserStateFiles}' from '{PluginConfig.UserConfigFolder}'."); } else { // Default config - _logger.LogInformation($"Loading default SimVar state file '{PluginConfig.AppConfigFolder}/{PluginConfig.StatesConfigFile}'."); - statesDictionary = _pluginConfig.LoadSimVarItems(false).ToDictionary(s => s.Def, s => s); + _logger.LogInformation($"Loading default SimVar State file '{PluginConfig.AppConfigFolder}/{PluginConfig.StatesConfigFile}'."); } + // Load the vars. PluginConfig tracks which files should be loaded, defaults or custom. + statesDictionary = _pluginConfig.LoadSimVarStateConfigs().ToDictionary(s => s.Def, s => s); + _logger.LogInformation($"Loaded {simVars.Count} SimVars from file(s)."); // clear out any old data _customIntervalStates.Clear(); @@ -329,50 +324,42 @@ private void ClearRepeatingActions() { } } - private void ProcessPluginCommandAction(Plugin actionId) { + private void ProcessPluginCommandAction(PluginActions actionId, double value = double.NaN) { switch (actionId) { - case Plugin.ToggleConnection: - autoReconnectSimConnect = !autoReconnectSimConnect; - if (_simConnectService.IsConnected()) { - _simConnectionRequest.Reset(); - _simConnectService.Disconnect(); - } - else { - _simConnectionRequest.Set(); - UpdateSimConnectState(); - } + case PluginActions.ToggleConnection: + ProcessPluginCommandAction(autoReconnectSimConnect ? PluginActions.Disconnect : PluginActions.Connect); break; - case Plugin.Connect: + case PluginActions.Connect: autoReconnectSimConnect = true; if (!_simConnectService.IsConnected()) _simConnectionRequest.Set(); UpdateSimConnectState(); break; - case Plugin.Disconnect: + case PluginActions.Disconnect: autoReconnectSimConnect = false; + bool wasSet = _simConnectionRequest.IsSet; _simConnectionRequest.Reset(); if (_simConnectService.IsConnected()) _simConnectService.Disconnect(); - else - UpdateSimConnectState(); + else if (wasSet) + _logger.LogInformation("Connection attempts to Simulator were canceled."); + UpdateSimConnectState(); break; - case Plugin.ReloadStates: + case PluginActions.ReloadStates: SetupSimVars(); break; - } - } - private void ProcessPluginCommandAction(Plugin actionId, double value) { - switch (actionId) { - case Plugin.ActionRepeatIntervalInc: - case Plugin.ActionRepeatIntervalDec: - case Plugin.ActionRepeatIntervalSet: - if (actionId == Plugin.ActionRepeatIntervalInc) + case PluginActions.ActionRepeatIntervalInc: + case PluginActions.ActionRepeatIntervalDec: + case PluginActions.ActionRepeatIntervalSet: + if (double.IsNaN(value)) + break; + if (actionId == PluginActions.ActionRepeatIntervalInc) value = Settings.ActionRepeatInterval.RealValue + value; - else if (actionId == Plugin.ActionRepeatIntervalDec) + else if (actionId == PluginActions.ActionRepeatIntervalDec) value = Settings.ActionRepeatInterval.RealValue - value; value = Math.Clamp(value, Settings.ActionRepeatInterval.MinValue, Settings.ActionRepeatInterval.MaxValue); if (value != Settings.ActionRepeatInterval.RealValue) @@ -425,33 +412,28 @@ private void ProcessEvent(ActionEvent actionEvent) { ProcessSimEvent(action, eventId, in dataArry); } - private void ProcessInternalEvent(ActionEventType action, TouchPortalSDK.Messages.Models.ActionData data) { - Plugin pluginEventId = (Plugin)action.Id; + private void ProcessInternalEvent(ActionEventType action, ActionData data) { + PluginActions pluginEventId = (PluginActions)action.Id; _logger.LogDebug($"Firing Internal Event - action: {action.ActionId}; enum: {pluginEventId}; data: {string.Join(", ", data.Select(d => $"{d.Key}={d.Value}"))}"); switch (pluginEventId) { - case Plugin.Connection: { - // preserve backwards compatibility with old actions which used indexed data IDs - if (action.TryGetEventMapping(new string[] { data.GetValueOrDefault("Action", data.GetValueOrDefault("0")) }, out Enum eventId)) - ProcessPluginCommandAction((Plugin)eventId); - break; - } - - case Plugin.ActionRepeatInterval: { + case PluginActions.Connection: + case PluginActions.ActionRepeatInterval: { // preserve backwards compatibility with old actions which used indexed data IDs - if (action.TryGetEventMapping(new string[] { data.GetValueOrDefault("Action", data.GetValueOrDefault("0")) }, out Enum eventId) && - double.TryParse(data.GetValueOrDefault("Interval", data.GetValueOrDefault("1")), out var interval)) - ProcessPluginCommandAction((Plugin)eventId, interval); + if ((data.TryGetValue("Action", out var actId) || data.TryGetValue("0", out actId)) && action.TryGetEventMapping(actId, out Enum eventId)) { + if (!(data.TryGetValue("Value", out var sVal) || data.TryGetValue("1", out sVal)) || !double.TryParse(sVal, out var value)) + value = double.NaN; + ProcessPluginCommandAction((PluginActions)eventId, value); + } break; } - case Plugin.SetSimVar: { - if (data.TryGetValue("VarName", out var varName) && data.TryGetValue("Value", out var value) && data.TryGetValue("RelAi", out var relAi)) - SetSimVarValueFromActionData(varName, value, new BooleanString(relAi)); + case PluginActions.SetSimVar: { + if (data.TryGetValue("VarName", out var varName) && data.TryGetValue("Value", out var value)) + SetSimVarValueFromActionData(varName, value, data.TryGetValue("RelAi", out var relAi) && new BooleanString(relAi)); break; } default: - // No other types of events supported right now. break; } } @@ -485,17 +467,11 @@ private void ProcessSimEvent(ActionEventType action, Enum eventId, in string[] d _simConnectService.TransmitClientEvent(action.CategoryId, eventId, dataUint); } - /// - /// Handles an array of `Setting` types sent from TP. This could come from either the - /// initial `OnInfoEvent` message, or the dedicated `OnSettingsEvent` message. - /// - /// - private void ProcessPluginSettings(IReadOnlyCollection settings) { + // Handles an array of `Setting` types sent from TP/API. This could come from either the + // initial `OnInfoEvent` message, or the dedicated `OnSettingsEvent` message. + private void ProcessPluginSettings(IReadOnlyCollection settings) { if (settings == null) return; - // change tracking - string oldCfgPath = Settings.UserConfigFilesPath.StringValue; - string oldCfgFiles = Settings.UserStateFiles.StringValue; // loop over incoming new settings foreach (var s in settings) { if (pluginSettingsDictionary.TryGetValue(s.Name, out PluginSetting setting)) { @@ -504,9 +480,13 @@ private void ProcessPluginSettings(IReadOnlyCollection> 8); // strip the patch version - _logger?.LogInformation( + _logger?.LogInformation(new EventId(1, "Connected"), $"Touch Portal Connected with: TP v{message.TpVersionString}, SDK v{message.SdkVersion}, {PluginId} entry.tp v{message.PluginVersion}, " + $"{VersionInfo.AssemblyName} running v{VersionInfo.GetProductVersionString()} ({runtimeVer})" ); @@ -551,7 +531,7 @@ public void OnInfoEvent(InfoEvent message) { _logger.LogInformation($"Detected {_entryFileType} type entry.tp definition file."); ProcessPluginSettings(message.Settings); - autoReconnectSimConnect = Settings.ConnectSimOnStartup.BoolValue; // we only care about this at startup + autoReconnectSimConnect = Settings.ConnectSimOnStartup.BoolValue; // we only care about the Settings value at startup _client.StateUpdate(PluginId + ".Plugin.State.RunningVersion", runtimeVer); _client.StateUpdate(PluginId + ".Plugin.State.EntryVersion", $"{tpVer & 0xFFFFFF:X}"); diff --git a/MSFSTouchPortalPlugin/Services/ReflectionService.cs b/MSFSTouchPortalPlugin/Services/ReflectionService.cs index bb36fd5..6701dd7 100644 --- a/MSFSTouchPortalPlugin/Services/ReflectionService.cs +++ b/MSFSTouchPortalPlugin/Services/ReflectionService.cs @@ -2,7 +2,6 @@ using MSFSTouchPortalPlugin.Attributes; using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Interfaces; -using MSFSTouchPortalPlugin.Objects.Plugin; using MSFSTouchPortalPlugin.Types; using System; using System.Collections.Generic; @@ -71,7 +70,7 @@ private Dictionary GetInternalActionEvents() { foreach (var actAttr in catAttribs) { // Create the action data object to store in the return dict, using the meta data we've collected so far. ActionEventType act = new ActionEventType { - Id = Enum.Parse(actAttr.Id), + Id = actAttr.EnumId, ActionId = actAttr.Id, CategoryId = Groups.Plugin, DataAttributes = actAttr.Data.ToDictionary(d => d.Id, d => d) @@ -88,7 +87,7 @@ private Dictionary GetInternalActionEvents() { fmtStrList.Add($"{{{i}}}"); act.KeyFormatStr = string.Join(",", fmtStrList); foreach (var ma in actAttr.Mappings) { - if (!act.TpActionToEventMap.TryAdd($"{string.Join(",", ma.Values)}", Enum.Parse(ma.ActionId))) + if (!act.TpActionToEventMap.TryAdd($"{string.Join(",", ma.Values)}", ma.EnumId)) _logger.LogWarning($"Duplicate action-to-event mapping found for Plugin action {act.ActionId} with choices '{string.Join(",", ma.Values)} for event '{ma.ActionId}'."); } } diff --git a/MSFSTouchPortalPlugin/Types/ActionEventType.cs b/MSFSTouchPortalPlugin/Types/ActionEventType.cs index 34b5153..b758747 100644 --- a/MSFSTouchPortalPlugin/Types/ActionEventType.cs +++ b/MSFSTouchPortalPlugin/Types/ActionEventType.cs @@ -37,6 +37,9 @@ public bool TryGetEventMapping(in string[] values, out Enum eventEnum) { return TpActionToEventMap.TryGetValue(FormatLookupKey(values), out eventEnum); } + public bool TryGetEventMapping(string value, out Enum eventEnum) => + TryGetEventMapping(new string[] { value }, out eventEnum); + // Helper to format an array of action data values into a unique key // used for indexing the TpActionToEventMap dictionary. public string FormatLookupKey(in string[] values) { From 0e2b61d3ad98702218d707710ea0e0ccdfd2431d Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sun, 10 Apr 2022 02:15:54 -0400 Subject: [PATCH 67/93] Add custom SimVars/States editing actions: AddCustomSimVar, AddKnownSimVar, RemoveSimVar, LoadSimVars, SaveSimVars; Add generated definitions of most known simulator variables for use in the editing "UI". --- .../Configuration/PluginConfig.cs | 52 + .../Configuration/SimVars.ini | 7521 +++++++++++++++++ MSFSTouchPortalPlugin/Constants/Categories.cs | 22 + MSFSTouchPortalPlugin/Constants/Units.cs | 566 ++ .../MSFSTouchPortalPlugin.csproj | 7 +- .../Objects/Plugin/Plugin.cs | 60 +- .../Services/PluginService.cs | 419 +- MSFSTouchPortalPlugin/Types/SimVariable.cs | 26 + 8 files changed, 8595 insertions(+), 78 deletions(-) create mode 100644 MSFSTouchPortalPlugin/Configuration/SimVars.ini create mode 100644 MSFSTouchPortalPlugin/Types/SimVariable.cs diff --git a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs index 48b0065..2be8552 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs +++ b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs @@ -25,6 +25,8 @@ internal class PluginConfig public static string StatesConfigFile { get; set; } = "States.ini"; public static string PluginStatesConfigFile { get; set; } = "PluginStates.ini"; + public static string SimVarsImportsFile { get; set; } = "SimVars.ini"; + public static string SimEventsImportsFile { get; set; } = "SimEvents.ini"; public static string AppRootFolder { get; set; } = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); public static string AppConfigFolder { get; set; } = Path.Combine(AppRootFolder, "Configuration"); @@ -188,6 +190,56 @@ public bool SaveSimVarItems(IEnumerable items, bool isUserConfig = t return ok; } + public IReadOnlyCollection ImportSimVars() { + List ret = new(); + var filename = Path.Combine(AppConfigFolder, SimVarsImportsFile); + _logger.LogDebug($"Importing SimVars from file '{filename}'..."); + + if (!LoadFromFile(filename, out var cfg)) + return ret; + + foreach (SharpConfig.Section section in cfg) { + if (section.Name == SharpConfig.Section.DefaultSectionName || section.Name.StartsWith("category_")) + continue; // do something with category later? + + SimVariable simVar; + try { + simVar = section.ToObject(); + } + catch (Exception e) { + _logger.LogError(e, $"Deserialize exception for section '{section}': {e.Message}:"); + continue; + } + if (simVar == null) { + _logger.LogError($"Produced SimVar is null from section '{section}':"); + continue; + } + string normUnit = Units.NormalizedUnit(simVar.Unit); + if (normUnit != null) + simVar.Unit = normUnit; + else + _logger.LogWarning($"Could not find Unit '{simVar.Unit}' for '{simVar.Id}'"); + + // set a default name. Some of the Descriptions would be suitable names but it's tricky to filter that. + if (string.IsNullOrWhiteSpace(simVar.Name)) + simVar.Name = simVar.SimVarName; + // set up a name to use in the TP UI selection list + simVar.TouchPortalSelectorName = $"{simVar.CategoryId} - {simVar.SimVarName}{(simVar.Indexed ? ":N" : "")}"; + + // check unique + if (ret.FindIndex(s => s.Id == simVar.Id) is int idx && idx > -1) { + _logger.LogWarning($"Duplicate SimVar ID found for '{simVar.Id}', overwriting."); + ret[idx] = simVar; + } + else { + ret.Add(simVar); + } + } + ret = ret.OrderBy(s => s.TouchPortalSelectorName).ToList(); + _logger.LogDebug($"Imported {ret.Count} SimVars from '{filename}'"); + return ret; + } + // Private common utility methods diff --git a/MSFSTouchPortalPlugin/Configuration/SimVars.ini b/MSFSTouchPortalPlugin/Configuration/SimVars.ini new file mode 100644 index 0000000..1fee0f2 --- /dev/null +++ b/MSFSTouchPortalPlugin/Configuration/SimVars.ini @@ -0,0 +1,7521 @@ +## Generated on 2022-04-09 22:46:28.881320 UTC with data from https://github.com/odwdinc/Python-SimConnect v0.4.25 + + +# Engine ####################### +# +[category_Engine] + +[NUMBER_OF_ENGINES] +Id = NumberOfEngines +CategoryId = Engine +SimVarName = "NUMBER OF ENGINES" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Number of engines (minimum 0, maximum 4)" + +[ENGINE_CONTROL_SELECT] +Id = EngineControlSelect +CategoryId = Engine +SimVarName = "ENGINE CONTROL SELECT" +Unit = "Mask" +CanSet = True +Indexed = False +Description = "Selected engines (combination of bit flags); 1 = Engine 1; 2 = Engine 2; 4 = Engine 3; 8 = Engine 4" + +[THROTTLE_LOWER_LIMIT] +Id = ThrottleLowerLimit +CategoryId = Engine +SimVarName = "THROTTLE LOWER LIMIT" +Unit = "Percent" +CanSet = False +Indexed = False +Description = "Percent throttle defining lower limit (negative for reverse thrust equipped airplanes)" + +[ENGINE_TYPE] +Id = EngineType +CategoryId = Engine +SimVarName = "ENGINE TYPE" +Unit = "Enum" +CanSet = False +Indexed = False +Description = "Engine type:; 0 = Piston; 1 = Jet; 2 = None; 3 = Helo(Bell) turbine; 4 = Unsupported; 5 = Turboprop" + +[MASTER_IGNITION_SWITCH] +Id = MasterIgnitionSwitch +CategoryId = Engine +SimVarName = "MASTER IGNITION SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Aircraft master ignition switch (grounds all engines magnetos)" + +[GENERAL_ENG_COMBUSTION] +Id = GeneralEngCombustion +CategoryId = Engine +SimVarName = "GENERAL ENG COMBUSTION" +Unit = "Bool" +CanSet = True +Indexed = True +Description = "Combustion flag" + +[GENERAL_ENG_MASTER_ALTERNATOR] +Id = GeneralEngMasterAlternator +CategoryId = Engine +SimVarName = "GENERAL ENG MASTER ALTERNATOR" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Alternator (generator) switch" + +[GENERAL_ENG_FUEL_PUMP_SWITCH] +Id = GeneralEngFuelPumpSwitch +CategoryId = Engine +SimVarName = "GENERAL ENG FUEL PUMP SWITCH" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Fuel pump switch" + +[GENERAL_ENG_FUEL_PUMP_ON] +Id = GeneralEngFuelPumpOn +CategoryId = Engine +SimVarName = "GENERAL ENG FUEL PUMP ON" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Fuel pump on/off" + +[GENERAL_ENG_RPM] +Id = GeneralEngRpm +CategoryId = Engine +SimVarName = "GENERAL ENG RPM" +Unit = "Rpm" +CanSet = False +Indexed = True +Description = "Engine rpm" + +[GENERAL_ENG_PCT_MAX_RPM] +Id = GeneralEngPctMaxRpm +CategoryId = Engine +SimVarName = "GENERAL ENG PCT MAX RPM" +Unit = "Percent" +CanSet = False +Indexed = True +Description = "Percent of max rated rpm" + +[GENERAL_ENG_MAX_REACHED_RPM] +Id = GeneralEngMaxReachedRpm +CategoryId = Engine +SimVarName = "GENERAL ENG MAX REACHED RPM" +Unit = "Rpm" +CanSet = False +Indexed = True +Description = "Maximum attained rpm" + +[GENERAL_ENG_THROTTLE_LEVER_POSITION] +Id = GeneralEngThrottleLeverPosition +CategoryId = Engine +SimVarName = "GENERAL ENG THROTTLE LEVER POSITION" +Unit = "Percent" +CanSet = True +Indexed = True +Description = "Percent of max throttle position" + +[GENERAL_ENG_MIXTURE_LEVER_POSITION] +Id = GeneralEngMixtureLeverPosition +CategoryId = Engine +SimVarName = "GENERAL ENG MIXTURE LEVER POSITION" +Unit = "Percent" +CanSet = True +Indexed = True +Description = "Percent of max mixture lever position" + +[GENERAL_ENG_PROPELLER_LEVER_POSITION] +Id = GeneralEngPropellerLeverPosition +CategoryId = Engine +SimVarName = "GENERAL ENG PROPELLER LEVER POSITION" +Unit = "Percent" +CanSet = True +Indexed = True +Description = "Percent of max prop lever position" + +[GENERAL_ENG_STARTER] +Id = GeneralEngStarter +CategoryId = Engine +SimVarName = "GENERAL ENG STARTER" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Engine starter on/off" + +[GENERAL_ENG_EXHAUST_GAS_TEMPERATURE] +Id = GeneralEngExhaustGasTemperature +CategoryId = Engine +SimVarName = "GENERAL ENG EXHAUST GAS TEMPERATURE" +Unit = "Rankine" +CanSet = True +Indexed = True +Description = "Engine exhaust gas temperature." + +[GENERAL_ENG_OIL_PRESSURE] +Id = GeneralEngOilPressure +CategoryId = Engine +SimVarName = "GENERAL ENG OIL PRESSURE" +Unit = "Psf" +CanSet = True +Indexed = True +Description = "Engine oil pressure" + +[GENERAL_ENG_OIL_LEAKED_PERCENT] +Id = GeneralEngOilLeakedPercent +CategoryId = Engine +SimVarName = "GENERAL ENG OIL LEAKED PERCENT" +Unit = "Percent" +CanSet = False +Indexed = True +Description = "Percent of max oil capacity leaked" + +[GENERAL_ENG_COMBUSTION_SOUND_PERCENT] +Id = GeneralEngCombustionSoundPercent +CategoryId = Engine +SimVarName = "GENERAL ENG COMBUSTION SOUND PERCENT" +Unit = "Percent" +CanSet = False +Indexed = True +Description = "Percent of maximum engine sound" + +[GENERAL_ENG_DAMAGE_PERCENT] +Id = GeneralEngDamagePercent +CategoryId = Engine +SimVarName = "GENERAL ENG DAMAGE PERCENT" +Unit = "Percent" +CanSet = False +Indexed = True +Description = "Percent of total engine damage" + +[GENERAL_ENG_OIL_TEMPERATURE] +Id = GeneralEngOilTemperature +CategoryId = Engine +SimVarName = "GENERAL ENG OIL TEMPERATURE" +Unit = "Rankine" +CanSet = True +Indexed = True +Description = "Engine oil temperature" + +[GENERAL_ENG_FAILED] +Id = GeneralEngFailed +CategoryId = Engine +SimVarName = "GENERAL ENG FAILED" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Fail flag" + +[GENERAL_ENG_GENERATOR_SWITCH] +Id = GeneralEngGeneratorSwitch +CategoryId = Engine +SimVarName = "GENERAL ENG GENERATOR SWITCH" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Alternator (generator) switch" + +[GENERAL_ENG_GENERATOR_ACTIVE] +Id = GeneralEngGeneratorActive +CategoryId = Engine +SimVarName = "GENERAL ENG GENERATOR ACTIVE" +Unit = "Bool" +CanSet = True +Indexed = True +Description = "Alternator (generator) on/off" + +[GENERAL_ENG_ANTI_ICE_POSITION] +Id = GeneralEngAntiIcePosition +CategoryId = Engine +SimVarName = "GENERAL ENG ANTI ICE POSITION" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Engine anti-ice switch" + +[GENERAL_ENG_FUEL_VALVE] +Id = GeneralEngFuelValve +CategoryId = Engine +SimVarName = "GENERAL ENG FUEL VALVE" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Fuel valve state" + +[GENERAL_ENG_FUEL_PRESSURE] +Id = GeneralEngFuelPressure +CategoryId = Engine +SimVarName = "GENERAL ENG FUEL PRESSURE" +Unit = "Psi" +CanSet = True +Indexed = True +Description = "Engine fuel pressure" + +[GENERAL_ENG_ELAPSED_TIME] +Id = GeneralEngElapsedTime +CategoryId = Engine +SimVarName = "GENERAL ENG ELAPSED TIME" +Unit = "Hours" +CanSet = False +Indexed = True +Description = "Total engine elapsed time" + +[RECIP_ENG_COWL_FLAP_POSITION] +Id = RecipEngCowlFlapPosition +CategoryId = Engine +SimVarName = "RECIP ENG COWL FLAP POSITION" +Unit = "Percent" +CanSet = True +Indexed = True +Description = "Percent cowl flap opened" + +[RECIP_ENG_PRIMER] +Id = RecipEngPrimer +CategoryId = Engine +SimVarName = "RECIP ENG PRIMER" +Unit = "Bool" +CanSet = True +Indexed = True +Description = "Engine primer position" + +[RECIP_ENG_MANIFOLD_PRESSURE] +Id = RecipEngManifoldPressure +CategoryId = Engine +SimVarName = "RECIP ENG MANIFOLD PRESSURE" +Unit = "Psi" +CanSet = True +Indexed = True +Description = "Engine manifold pressure" + +[RECIP_ENG_ALTERNATE_AIR_POSITION] +Id = RecipEngAlternateAirPosition +CategoryId = Engine +SimVarName = "RECIP ENG ALTERNATE AIR POSITION" +Unit = "Position" +CanSet = True +Indexed = True +Description = "Alternate air control" + +[RECIP_ENG_COOLANT_RESERVOIR_PERCENT] +Id = RecipEngCoolantReservoirPercent +CategoryId = Engine +SimVarName = "RECIP ENG COOLANT RESERVOIR PERCENT" +Unit = "Percent" +CanSet = True +Indexed = True +Description = "Percent coolant available" + +[RECIP_ENG_LEFT_MAGNETO] +Id = RecipEngLeftMagneto +CategoryId = Engine +SimVarName = "RECIP ENG LEFT MAGNETO" +Unit = "Bool" +CanSet = True +Indexed = True +Description = "Left magneto state" + +[RECIP_ENG_RIGHT_MAGNETO] +Id = RecipEngRightMagneto +CategoryId = Engine +SimVarName = "RECIP ENG RIGHT MAGNETO" +Unit = "Bool" +CanSet = True +Indexed = True +Description = "Right magneto state" + +[RECIP_ENG_BRAKE_POWER] +Id = RecipEngBrakePower +CategoryId = Engine +SimVarName = "RECIP ENG BRAKE POWER" +Unit = "ft lb per second" +CanSet = True +Indexed = True +Description = "Brake power produced by engine" + +[RECIP_ENG_STARTER_TORQUE] +Id = RecipEngStarterTorque +CategoryId = Engine +SimVarName = "RECIP ENG STARTER TORQUE" +Unit = "Foot pound" +CanSet = True +Indexed = True +Description = "Torque produced by engine" + +[RECIP_ENG_TURBOCHARGER_FAILED] +Id = RecipEngTurbochargerFailed +CategoryId = Engine +SimVarName = "RECIP ENG TURBOCHARGER FAILED" +Unit = "Bool" +CanSet = True +Indexed = True +Description = "Turbo failed state" + +[RECIP_ENG_EMERGENCY_BOOST_ACTIVE] +Id = RecipEngEmergencyBoostActive +CategoryId = Engine +SimVarName = "RECIP ENG EMERGENCY BOOST ACTIVE" +Unit = "Bool" +CanSet = True +Indexed = True +Description = "War emergency power active" + +[RECIP_ENG_EMERGENCY_BOOST_ELAPSED_TIME] +Id = RecipEngEmergencyBoostElapsedTime +CategoryId = Engine +SimVarName = "RECIP ENG EMERGENCY BOOST ELAPSED TIME" +Unit = "Hours" +CanSet = True +Indexed = True +Description = "Elapsed time war emergency power active" + +[RECIP_ENG_WASTEGATE_POSITION] +Id = RecipEngWastegatePosition +CategoryId = Engine +SimVarName = "RECIP ENG WASTEGATE POSITION" +Unit = "Percent" +CanSet = True +Indexed = True +Description = "Percent turbo wastegate closed" + +[RECIP_ENG_TURBINE_INLET_TEMPERATURE] +Id = RecipEngTurbineInletTemperature +CategoryId = Engine +SimVarName = "RECIP ENG TURBINE INLET TEMPERATURE" +Unit = "Celsius" +CanSet = True +Indexed = True +Description = "Engine turbine inlet temperature" + +[RECIP_ENG_CYLINDER_HEAD_TEMPERATURE] +Id = RecipEngCylinderHeadTemperature +CategoryId = Engine +SimVarName = "RECIP ENG CYLINDER HEAD TEMPERATURE" +Unit = "Celsius" +CanSet = True +Indexed = True +Description = "Engine cylinder head temperature" + +[RECIP_ENG_RADIATOR_TEMPERATURE] +Id = RecipEngRadiatorTemperature +CategoryId = Engine +SimVarName = "RECIP ENG RADIATOR TEMPERATURE" +Unit = "Celsius" +CanSet = True +Indexed = True +Description = "Engine radiator temperature" + +[RECIP_ENG_FUEL_AVAILABLE] +Id = RecipEngFuelAvailable +CategoryId = Engine +SimVarName = "RECIP ENG FUEL AVAILABLE" +Unit = "Bool" +CanSet = True +Indexed = True +Description = "True if fuel is available" + +[RECIP_ENG_FUEL_FLOW] +Id = RecipEngFuelFlow +CategoryId = Engine +SimVarName = "RECIP ENG FUEL FLOW" +Unit = "Pounds per hour" +CanSet = True +Indexed = True +Description = "Engine fuel flow" + +[RECIP_ENG_FUEL_TANK_SELECTOR] +Id = RecipEngFuelTankSelector +CategoryId = Engine +SimVarName = "RECIP ENG FUEL TANK SELECTOR" +Unit = "Enum" +CanSet = False +Indexed = True +Description = "Fuel tank selected for engine. See fuel tank list." + +[RECIP_ENG_FUEL_NUMBER_TANKS_USED] +Id = RecipEngFuelNumberTanksUsed +CategoryId = Engine +SimVarName = "RECIP ENG FUEL NUMBER TANKS USED" +Unit = "Number" +CanSet = False +Indexed = True +Description = "Number of tanks currently being used" + +[RECIP_CARBURETOR_TEMPERATURE] +Id = RecipCarburetorTemperature +CategoryId = Engine +SimVarName = "RECIP CARBURETOR TEMPERATURE" +Unit = "Celsius" +CanSet = True +Indexed = True +Description = "Carburetor temperature" + +[RECIP_MIXTURE_RATIO] +Id = RecipMixtureRatio +CategoryId = Engine +SimVarName = "RECIP MIXTURE RATIO" +Unit = "Ratio" +CanSet = True +Indexed = True +Description = "Fuel / Air mixture ratio" + +[TURB_ENG_N1] +Id = TurbEngN1 +CategoryId = Engine +SimVarName = "TURB ENG N1" +Unit = "Percent" +CanSet = True +Indexed = True +Description = "Turbine engine N1" + +[TURB_ENG_N2] +Id = TurbEngN2 +CategoryId = Engine +SimVarName = "TURB ENG N2" +Unit = "Percent" +CanSet = True +Indexed = True +Description = "Turbine engine N2" + +[TURB_ENG_CORRECTED_N1] +Id = TurbEngCorrectedN1 +CategoryId = Engine +SimVarName = "TURB ENG CORRECTED N1" +Unit = "Percent" +CanSet = True +Indexed = True +Description = "Turbine engine corrected N1" + +[TURB_ENG_CORRECTED_N2] +Id = TurbEngCorrectedN2 +CategoryId = Engine +SimVarName = "TURB ENG CORRECTED N2" +Unit = "Percent" +CanSet = True +Indexed = True +Description = "Turbine engine corrected N2" + +[TURB_ENG_CORRECTED_FF] +Id = TurbEngCorrectedFf +CategoryId = Engine +SimVarName = "TURB ENG CORRECTED FF" +Unit = "Pounds per hour" +CanSet = True +Indexed = True +Description = "Corrected fuel flow" + +[TURB_ENG_MAX_TORQUE_PERCENT] +Id = TurbEngMaxTorquePercent +CategoryId = Engine +SimVarName = "TURB ENG MAX TORQUE PERCENT" +Unit = "Percent" +CanSet = True +Indexed = True +Description = "Percent of max rated torque" + +[TURB_ENG_PRESSURE_RATIO] +Id = TurbEngPressureRatio +CategoryId = Engine +SimVarName = "TURB ENG PRESSURE RATIO" +Unit = "Ratio" +CanSet = True +Indexed = True +Description = "Engine pressure ratio" + +[TURB_ENG_ITT] +Id = TurbEngItt +CategoryId = Engine +SimVarName = "TURB ENG ITT" +Unit = "Rankine" +CanSet = True +Indexed = True +Description = "Engine ITT" + +[TURB_ENG_AFTERBURNER] +Id = TurbEngAfterburner +CategoryId = Engine +SimVarName = "TURB ENG AFTERBURNER" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Afterburner state" + +[TURB_ENG_JET_THRUST] +Id = TurbEngJetThrust +CategoryId = Engine +SimVarName = "TURB ENG JET THRUST" +Unit = "Pounds" +CanSet = False +Indexed = True +Description = "Engine jet thrust" + +[TURB_ENG_BLEED_AIR] +Id = TurbEngBleedAir +CategoryId = Engine +SimVarName = "TURB ENG BLEED AIR" +Unit = "Psi" +CanSet = False +Indexed = True +Description = "Bleed air pressure" + +[TURB_ENG_TANK_SELECTOR] +Id = TurbEngTankSelector +CategoryId = Engine +SimVarName = "TURB ENG TANK SELECTOR" +Unit = "Enum" +CanSet = False +Indexed = True +Description = "Fuel tank selected for engine. See fuel tank list." + +[TURB_ENG_NUM_TANKS_USED] +Id = TurbEngNumTanksUsed +CategoryId = Engine +SimVarName = "TURB ENG NUM TANKS USED" +Unit = "Number" +CanSet = False +Indexed = True +Description = "Number of tanks currently being used" + +[TURB_ENG_FUEL_FLOW_PPH] +Id = TurbEngFuelFlowPph +CategoryId = Engine +SimVarName = "TURB ENG FUEL FLOW PPH" +Unit = "Pounds per hour" +CanSet = False +Indexed = True +Description = "Engine fuel flow" + +[TURB_ENG_FUEL_AVAILABLE] +Id = TurbEngFuelAvailable +CategoryId = Engine +SimVarName = "TURB ENG FUEL AVAILABLE" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "True if fuel is available" + +[TURB_ENG_REVERSE_NOZZLE_PERCENT] +Id = TurbEngReverseNozzlePercent +CategoryId = Engine +SimVarName = "TURB ENG REVERSE NOZZLE PERCENT" +Unit = "Percent" +CanSet = False +Indexed = True +Description = "Percent thrust reverser nozzles deployed" + +[TURB_ENG_VIBRATION] +Id = TurbEngVibration +CategoryId = Engine +SimVarName = "TURB ENG VIBRATION" +Unit = "Number" +CanSet = False +Indexed = True +Description = "Engine vibration value" + +[ENG_FAILED] +Id = EngFailed +CategoryId = Engine +SimVarName = "ENG FAILED" +Unit = "Number" +CanSet = False +Indexed = True +Description = "Failure flag" + +[ENG_RPM_ANIMATION_PERCENT] +Id = EngRpmAnimationPercent +CategoryId = Engine +SimVarName = "ENG RPM ANIMATION PERCENT" +Unit = "Percent" +CanSet = False +Indexed = True +Description = "Percent max rated rpm used for visual animation" + +[ENG_ON_FIRE] +Id = EngOnFire +CategoryId = Engine +SimVarName = "ENG ON FIRE" +Unit = "Bool" +CanSet = True +Indexed = True +Description = "On fire state" + +[ENG_FUEL_FLOW_BUG_POSITION] +Id = EngFuelFlowBugPosition +CategoryId = Engine +SimVarName = "ENG FUEL FLOW BUG POSITION" +Unit = "Pounds per hour" +CanSet = False +Indexed = True +Description = "Fuel flow reference" + +[PROP_RPM] +Id = PropRpm +CategoryId = Engine +SimVarName = "PROP RPM" +Unit = "Rpm" +CanSet = True +Indexed = True +Description = "Propeller rpm" + +[PROP_MAX_RPM_PERCENT] +Id = PropMaxRpmPercent +CategoryId = Engine +SimVarName = "PROP MAX RPM PERCENT" +Unit = "Percent" +CanSet = False +Indexed = True +Description = "Percent of max rated rpm" + +[PROP_THRUST] +Id = PropThrust +CategoryId = Engine +SimVarName = "PROP THRUST" +Unit = "Pounds" +CanSet = False +Indexed = True +Description = "Propeller thrust" + +[PROP_BETA] +Id = PropBeta +CategoryId = Engine +SimVarName = "PROP BETA" +Unit = "Radians" +CanSet = False +Indexed = True +Description = "Prop blade pitch angle" + +[PROP_FEATHERING_INHIBIT] +Id = PropFeatheringInhibit +CategoryId = Engine +SimVarName = "PROP FEATHERING INHIBIT" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Feathering inhibit flag" + +[PROP_FEATHERED] +Id = PropFeathered +CategoryId = Engine +SimVarName = "PROP FEATHERED" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Feathered state" + +[PROP_SYNC_DELTA_LEVER] +Id = PropSyncDeltaLever +CategoryId = Engine +SimVarName = "PROP SYNC DELTA LEVER" +Unit = "Position" +CanSet = False +Indexed = True +Description = "Corrected prop correction input on slaved engine" + +[PROP_AUTO_FEATHER_ARMED] +Id = PropAutoFeatherArmed +CategoryId = Engine +SimVarName = "PROP AUTO FEATHER ARMED" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Auto-feather armed state" + +[PROP_FEATHER_SWITCH] +Id = PropFeatherSwitch +CategoryId = Engine +SimVarName = "PROP FEATHER SWITCH" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Prop feather switch" + +[PANEL_AUTO_FEATHER_SWITCH] +Id = PanelAutoFeatherSwitch +CategoryId = Engine +SimVarName = "PANEL AUTO FEATHER SWITCH" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Auto-feather arming switch" + +[PROP_SYNC_ACTIVE] +Id = PropSyncActive +CategoryId = Engine +SimVarName = "PROP SYNC ACTIVE" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "True if prop sync is active" + +[PROP_DEICE_SWITCH] +Id = PropDeiceSwitch +CategoryId = Engine +SimVarName = "PROP DEICE SWITCH" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "True if prop deice switch on" + +[ENG_COMBUSTION] +Id = EngCombustion +CategoryId = Engine +SimVarName = "ENG COMBUSTION" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the engine is running" + +[ENG_N1_RPM] +Id = EngN1Rpm +CategoryId = Engine +SimVarName = "ENG N1 RPM" +Unit = "Percent" +CanSet = False +Indexed = True +Description = "Engine N1 rpm" + +[ENG_N2_RPM] +Id = EngN2Rpm +CategoryId = Engine +SimVarName = "ENG N2 RPM" +Unit = "Percent" +CanSet = False +Indexed = True +Description = "Engine N2 rpm" + +[ENG_FUEL_FLOW_GPH] +Id = EngFuelFlowGph +CategoryId = Engine +SimVarName = "ENG FUEL FLOW GPH" +Unit = "Gallons per hour" +CanSet = False +Indexed = True +Description = "Engine fuel flow" + +[ENG_FUEL_FLOW_PPH] +Id = EngFuelFlowPph +CategoryId = Engine +SimVarName = "ENG FUEL FLOW PPH" +Unit = "Pounds per hour" +CanSet = False +Indexed = True +Description = "Engine fuel flow" + +[ENG_TORQUE] +Id = EngTorque +CategoryId = Engine +SimVarName = "ENG TORQUE" +Unit = "Foot pounds" +CanSet = False +Indexed = True +Description = "Torque" + +[ENG_ANTI_ICE] +Id = EngAntiIce +CategoryId = Engine +SimVarName = "ENG ANTI ICE" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Anti-ice switch" + +[ENG_PRESSURE_RATIO] +Id = EngPressureRatio +CategoryId = Engine +SimVarName = "ENG PRESSURE RATIO" +Unit = "Ratio" +CanSet = False +Indexed = True +Description = "Engine pressure ratio" + +[ENG_EXHAUST_GAS_TEMPERATURE] +Id = EngExhaustGasTemperature +CategoryId = Engine +SimVarName = "ENG EXHAUST GAS TEMPERATURE" +Unit = "Rankine" +CanSet = False +Indexed = True +Description = "Exhaust gas temperature" + +[ENG_EXHAUST_GAS_TEMPERATURE_GES] +Id = EngExhaustGasTemperatureGes +CategoryId = Engine +SimVarName = "ENG EXHAUST GAS TEMPERATURE GES" +Unit = "Percent over 100" +CanSet = False +Indexed = True +Description = "Governed engine setting" + +[ENG_CYLINDER_HEAD_TEMPERATURE] +Id = EngCylinderHeadTemperature +CategoryId = Engine +SimVarName = "ENG CYLINDER HEAD TEMPERATURE" +Unit = "Rankine" +CanSet = False +Indexed = True +Description = "Engine cylinder head temperature" + +[ENG_OIL_TEMPERATURE] +Id = EngOilTemperature +CategoryId = Engine +SimVarName = "ENG OIL TEMPERATURE" +Unit = "Rankine" +CanSet = False +Indexed = True +Description = "Engine oil temperature" + +[ENG_OIL_PRESSURE] +Id = EngOilPressure +CategoryId = Engine +SimVarName = "ENG OIL PRESSURE" +Unit = "foot pounds" +CanSet = False +Indexed = True +Description = "Engine oil pressure" + +[ENG_OIL_QUANTITY] +Id = EngOilQuantity +CategoryId = Engine +SimVarName = "ENG OIL QUANTITY" +Unit = "Percent over 100" +CanSet = False +Indexed = True +Description = "Engine oil quantitiy as a percentage of full capacity" + +[ENG_HYDRAULIC_PRESSURE] +Id = EngHydraulicPressure +CategoryId = Engine +SimVarName = "ENG HYDRAULIC PRESSURE" +Unit = "foot pounds" +CanSet = False +Indexed = True +Description = "Engine hydraulic pressure" + +[ENG_HYDRAULIC_QUANTITY] +Id = EngHydraulicQuantity +CategoryId = Engine +SimVarName = "ENG HYDRAULIC QUANTITY" +Unit = "Percent over 100" +CanSet = False +Indexed = True +Description = "Engine hydraulic fluid quantity, as a percentage of total capacity" + +[ENG_MANIFOLD_PRESSURE] +Id = EngManifoldPressure +CategoryId = Engine +SimVarName = "ENG MANIFOLD PRESSURE" +Unit = "inHG" +CanSet = False +Indexed = True +Description = "Engine manifold pressure." + +[ENG_VIBRATION] +Id = EngVibration +CategoryId = Engine +SimVarName = "ENG VIBRATION" +Unit = "Number" +CanSet = False +Indexed = True +Description = "Engine vibration" + +[ENG_MAX_RPM] +Id = EngMaxRpm +CategoryId = Engine +SimVarName = "ENG MAX RPM" +Unit = "Rpm" +CanSet = False +Indexed = False +Description = "Maximum rpm" + +[GENERAL_ENG_STARTER_ACTIVE] +Id = GeneralEngStarterActive +CategoryId = Engine +SimVarName = "GENERAL ENG STARTER ACTIVE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if engine starter is active" + +[GENERAL_ENG_FUEL_USED_SINCE_START] +Id = GeneralEngFuelUsedSinceStart +CategoryId = Engine +SimVarName = "GENERAL ENG FUEL USED SINCE START" +Unit = "Pounds" +CanSet = False +Indexed = False +Description = "Fuel used since the engines were last started" + +[TURB_ENG_PRIMARY_NOZZLE_PERCENT] +Id = TurbEngPrimaryNozzlePercent +CategoryId = Engine +SimVarName = "TURB ENG PRIMARY NOZZLE PERCENT" +Unit = "Percent over 100" +CanSet = False +Indexed = True +Description = "Percent thrust of primary nozzle" + +[TURB_ENG_IGNITION_SWITCH] +Id = TurbEngIgnitionSwitch +CategoryId = Engine +SimVarName = "TURB ENG IGNITION SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the turbine engine ignition switch is on" + +[TURB_ENG_MASTER_STARTER_SWITCH] +Id = TurbEngMasterStarterSwitch +CategoryId = Engine +SimVarName = "TURB ENG MASTER STARTER SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the turbine engine master starter switch is on" + +[TURB_ENG_AFTERBURNER_STAGE_ACTIVE] +Id = TurbEngAfterburnerStageActive +CategoryId = Engine +SimVarName = "TURB ENG AFTERBURNER STAGE ACTIVE" +Unit = "Number" +CanSet = False +Indexed = False +Description = "The stage of the afterburner, or 0 if the afterburner is not active." + + + +# FuelTankSelection ####################### +# +[category_FuelTankSelection] + + + +# Fuel ####################### +# +[category_Fuel] + +[FUEL_TANK_CENTER_LEVEL] +Id = FuelTankCenterLevel +CategoryId = Fuel +SimVarName = "FUEL TANK CENTER LEVEL" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent of maximum capacity" + +[FUEL_TANK_CENTER2_LEVEL] +Id = FuelTankCenter2Level +CategoryId = Fuel +SimVarName = "FUEL TANK CENTER2 LEVEL" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent of maximum capacity" + +[FUEL_TANK_CENTER3_LEVEL] +Id = FuelTankCenter3Level +CategoryId = Fuel +SimVarName = "FUEL TANK CENTER3 LEVEL" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent of maximum capacity" + +[FUEL_TANK_LEFT_MAIN_LEVEL] +Id = FuelTankLeftMainLevel +CategoryId = Fuel +SimVarName = "FUEL TANK LEFT MAIN LEVEL" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent of maximum capacity" + +[FUEL_TANK_LEFT_AUX_LEVEL] +Id = FuelTankLeftAuxLevel +CategoryId = Fuel +SimVarName = "FUEL TANK LEFT AUX LEVEL" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent of maximum capacity" + +[FUEL_TANK_LEFT_TIP_LEVEL] +Id = FuelTankLeftTipLevel +CategoryId = Fuel +SimVarName = "FUEL TANK LEFT TIP LEVEL" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent of maximum capacity" + +[FUEL_TANK_RIGHT_MAIN_LEVEL] +Id = FuelTankRightMainLevel +CategoryId = Fuel +SimVarName = "FUEL TANK RIGHT MAIN LEVEL" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent of maximum capacity" + +[FUEL_TANK_RIGHT_AUX_LEVEL] +Id = FuelTankRightAuxLevel +CategoryId = Fuel +SimVarName = "FUEL TANK RIGHT AUX LEVEL" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent of maximum capacity" + +[FUEL_TANK_RIGHT_TIP_LEVEL] +Id = FuelTankRightTipLevel +CategoryId = Fuel +SimVarName = "FUEL TANK RIGHT TIP LEVEL" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent of maximum capacity" + +[FUEL_TANK_EXTERNAL1_LEVEL] +Id = FuelTankExternal1Level +CategoryId = Fuel +SimVarName = "FUEL TANK EXTERNAL1 LEVEL" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent of maximum capacity" + +[FUEL_TANK_EXTERNAL2_LEVEL] +Id = FuelTankExternal2Level +CategoryId = Fuel +SimVarName = "FUEL TANK EXTERNAL2 LEVEL" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent of maximum capacity" + +[FUEL_TANK_CENTER_CAPACITY] +Id = FuelTankCenterCapacity +CategoryId = Fuel +SimVarName = "FUEL TANK CENTER CAPACITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Maximum capacity in volume" + +[FUEL_TANK_CENTER2_CAPACITY] +Id = FuelTankCenter2Capacity +CategoryId = Fuel +SimVarName = "FUEL TANK CENTER2 CAPACITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Maximum capacity in volume" + +[FUEL_TANK_CENTER3_CAPACITY] +Id = FuelTankCenter3Capacity +CategoryId = Fuel +SimVarName = "FUEL TANK CENTER3 CAPACITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Maximum capacity in volume" + +[FUEL_TANK_LEFT_MAIN_CAPACITY] +Id = FuelTankLeftMainCapacity +CategoryId = Fuel +SimVarName = "FUEL TANK LEFT MAIN CAPACITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Maximum capacity in volume" + +[FUEL_TANK_LEFT_AUX_CAPACITY] +Id = FuelTankLeftAuxCapacity +CategoryId = Fuel +SimVarName = "FUEL TANK LEFT AUX CAPACITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Maximum capacity in volume" + +[FUEL_TANK_LEFT_TIP_CAPACITY] +Id = FuelTankLeftTipCapacity +CategoryId = Fuel +SimVarName = "FUEL TANK LEFT TIP CAPACITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Maximum capacity in volume" + +[FUEL_TANK_RIGHT_MAIN_CAPACITY] +Id = FuelTankRightMainCapacity +CategoryId = Fuel +SimVarName = "FUEL TANK RIGHT MAIN CAPACITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Maximum capacity in volume" + +[FUEL_TANK_RIGHT_AUX_CAPACITY] +Id = FuelTankRightAuxCapacity +CategoryId = Fuel +SimVarName = "FUEL TANK RIGHT AUX CAPACITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Maximum capacity in volume" + +[FUEL_TANK_RIGHT_TIP_CAPACITY] +Id = FuelTankRightTipCapacity +CategoryId = Fuel +SimVarName = "FUEL TANK RIGHT TIP CAPACITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Maximum capacity in volume" + +[FUEL_TANK_EXTERNAL1_CAPACITY] +Id = FuelTankExternal1Capacity +CategoryId = Fuel +SimVarName = "FUEL TANK EXTERNAL1 CAPACITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Maximum capacity in volume" + +[FUEL_TANK_EXTERNAL2_CAPACITY] +Id = FuelTankExternal2Capacity +CategoryId = Fuel +SimVarName = "FUEL TANK EXTERNAL2 CAPACITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Maximum capacity in volume" + +[FUEL_LEFT_CAPACITY] +Id = FuelLeftCapacity +CategoryId = Fuel +SimVarName = "FUEL LEFT CAPACITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Maximum capacity in volume" + +[FUEL_RIGHT_CAPACITY] +Id = FuelRightCapacity +CategoryId = Fuel +SimVarName = "FUEL RIGHT CAPACITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Maximum capacity in volume" + +[FUEL_TANK_CENTER_QUANTITY] +Id = FuelTankCenterQuantity +CategoryId = Fuel +SimVarName = "FUEL TANK CENTER QUANTITY" +Unit = "Gallons" +CanSet = True +Indexed = False +Description = "Current quantity in volume" + +[FUEL_TANK_CENTER2_QUANTITY] +Id = FuelTankCenter2Quantity +CategoryId = Fuel +SimVarName = "FUEL TANK CENTER2 QUANTITY" +Unit = "Gallons" +CanSet = True +Indexed = False +Description = "Current quantity in volume" + +[FUEL_TANK_CENTER3_QUANTITY] +Id = FuelTankCenter3Quantity +CategoryId = Fuel +SimVarName = "FUEL TANK CENTER3 QUANTITY" +Unit = "Gallons" +CanSet = True +Indexed = False +Description = "Current quantity in volume" + +[FUEL_TANK_LEFT_MAIN_QUANTITY] +Id = FuelTankLeftMainQuantity +CategoryId = Fuel +SimVarName = "FUEL TANK LEFT MAIN QUANTITY" +Unit = "Gallons" +CanSet = True +Indexed = False +Description = "Current quantity in volume" + +[FUEL_TANK_LEFT_AUX_QUANTITY] +Id = FuelTankLeftAuxQuantity +CategoryId = Fuel +SimVarName = "FUEL TANK LEFT AUX QUANTITY" +Unit = "Gallons" +CanSet = True +Indexed = False +Description = "Current quantity in volume" + +[FUEL_TANK_LEFT_TIP_QUANTITY] +Id = FuelTankLeftTipQuantity +CategoryId = Fuel +SimVarName = "FUEL TANK LEFT TIP QUANTITY" +Unit = "Gallons" +CanSet = True +Indexed = False +Description = "Current quantity in volume" + +[FUEL_TANK_RIGHT_MAIN_QUANTITY] +Id = FuelTankRightMainQuantity +CategoryId = Fuel +SimVarName = "FUEL TANK RIGHT MAIN QUANTITY" +Unit = "Gallons" +CanSet = True +Indexed = False +Description = "Current quantity in volume" + +[FUEL_TANK_RIGHT_AUX_QUANTITY] +Id = FuelTankRightAuxQuantity +CategoryId = Fuel +SimVarName = "FUEL TANK RIGHT AUX QUANTITY" +Unit = "Gallons" +CanSet = True +Indexed = False +Description = "Current quantity in volume" + +[FUEL_TANK_RIGHT_TIP_QUANTITY] +Id = FuelTankRightTipQuantity +CategoryId = Fuel +SimVarName = "FUEL TANK RIGHT TIP QUANTITY" +Unit = "Gallons" +CanSet = True +Indexed = False +Description = "Current quantity in volume" + +[FUEL_TANK_EXTERNAL1_QUANTITY] +Id = FuelTankExternal1Quantity +CategoryId = Fuel +SimVarName = "FUEL TANK EXTERNAL1 QUANTITY" +Unit = "Gallons" +CanSet = True +Indexed = False +Description = "Current quantity in volume" + +[FUEL_TANK_EXTERNAL2_QUANTITY] +Id = FuelTankExternal2Quantity +CategoryId = Fuel +SimVarName = "FUEL TANK EXTERNAL2 QUANTITY" +Unit = "Gallons" +CanSet = True +Indexed = False +Description = "Current quantity in volume" + +[FUEL_LEFT_QUANTITY] +Id = FuelLeftQuantity +CategoryId = Fuel +SimVarName = "FUEL LEFT QUANTITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Current quantity in volume" + +[FUEL_RIGHT_QUANTITY] +Id = FuelRightQuantity +CategoryId = Fuel +SimVarName = "FUEL RIGHT QUANTITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Current quantity in volume" + +[FUEL_TOTAL_QUANTITY] +Id = FuelTotalQuantity +CategoryId = Fuel +SimVarName = "FUEL TOTAL QUANTITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Current quantity in volume" + +[FUEL_WEIGHT_PER_GALLON] +Id = FuelWeightPerGallon +CategoryId = Fuel +SimVarName = "FUEL WEIGHT PER GALLON" +Unit = "Pounds" +CanSet = False +Indexed = False +Description = "Fuel weight per gallon" + +[FUEL_TANK_SELECTOR] +Id = FuelTankSelector +CategoryId = Fuel +SimVarName = "FUEL TANK SELECTOR" +Unit = "Enum" +CanSet = False +Indexed = True +Description = "Which tank is selected. See fuel tank list." + +[FUEL_CROSS_FEED] +Id = FuelCrossFeed +CategoryId = Fuel +SimVarName = "FUEL CROSS FEED" +Unit = "Enum" +CanSet = False +Indexed = False +Description = "Cross feed valve:; 0 = Closed; 1 = Open" + +[FUEL_TOTAL_CAPACITY] +Id = FuelTotalCapacity +CategoryId = Fuel +SimVarName = "FUEL TOTAL CAPACITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Total capacity of the aircraft" + +[FUEL_SELECTED_QUANTITY_PERCENT] +Id = FuelSelectedQuantityPercent +CategoryId = Fuel +SimVarName = "FUEL SELECTED QUANTITY PERCENT" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Percent or capacity for selected tank" + +[FUEL_SELECTED_QUANTITY] +Id = FuelSelectedQuantity +CategoryId = Fuel +SimVarName = "FUEL SELECTED QUANTITY" +Unit = "Gallons" +CanSet = False +Indexed = False +Description = "Quantity of selected tank" + +[FUEL_TOTAL_QUANTITY_WEIGHT] +Id = FuelTotalQuantityWeight +CategoryId = Fuel +SimVarName = "FUEL TOTAL QUANTITY WEIGHT" +Unit = "Pounds" +CanSet = False +Indexed = False +Description = "Current total fuel weight of the aircraft" + +[NUM_FUEL_SELECTORS] +Id = NumFuelSelectors +CategoryId = Fuel +SimVarName = "NUM FUEL SELECTORS" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Number of selectors on the aircraft" + +[UNLIMITED_FUEL] +Id = UnlimitedFuel +CategoryId = Fuel +SimVarName = "UNLIMITED FUEL" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Unlimited fuel flag" + +[ESTIMATED_FUEL_FLOW] +Id = EstimatedFuelFlow +CategoryId = Fuel +SimVarName = "ESTIMATED FUEL FLOW" +Unit = "Pounds per hour" +CanSet = False +Indexed = False +Description = "Estimated fuel flow at cruise" + + + +# Lights ####################### +# +[category_Lights] + +[LIGHT_STROBE] +Id = LightStrobe +CategoryId = Lights +SimVarName = "LIGHT STROBE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Light switch state" + +[LIGHT_PANEL] +Id = LightPanel +CategoryId = Lights +SimVarName = "LIGHT PANEL" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Light switch state" + +[LIGHT_LANDING] +Id = LightLanding +CategoryId = Lights +SimVarName = "LIGHT LANDING" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Light switch state" + +[LIGHT_TAXI] +Id = LightTaxi +CategoryId = Lights +SimVarName = "LIGHT TAXI" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Light switch state" + +[LIGHT_BEACON] +Id = LightBeacon +CategoryId = Lights +SimVarName = "LIGHT BEACON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Light switch state" + +[LIGHT_NAV] +Id = LightNav +CategoryId = Lights +SimVarName = "LIGHT NAV" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Light switch state" + +[LIGHT_LOGO] +Id = LightLogo +CategoryId = Lights +SimVarName = "LIGHT LOGO" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Light switch state" + +[LIGHT_WING] +Id = LightWing +CategoryId = Lights +SimVarName = "LIGHT WING" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Light switch state" + +[LIGHT_RECOGNITION] +Id = LightRecognition +CategoryId = Lights +SimVarName = "LIGHT RECOGNITION" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Light switch state" + +[LIGHT_CABIN] +Id = LightCabin +CategoryId = Lights +SimVarName = "LIGHT CABIN" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Light switch state" + +[LIGHT_ON_STATES] +Id = LightOnStates +CategoryId = Lights +SimVarName = "LIGHT ON STATES" +Unit = "Mask" +CanSet = False +Indexed = False +Description = "Bit mask:; 0x0001: Nav; 0x0002: Beacon; 0x0004: Landing; 0x0008: Taxi; 0x0010: Strobe; 0x0020: Panel; 0x0040: Recognition; 0x0080: Wing; 0x0100: Logo; 0x0200: Cabin" + +[LIGHT_STATES] +Id = LightStates +CategoryId = Lights +SimVarName = "LIGHT STATES" +Unit = "Mask" +CanSet = False +Indexed = False +Description = "Same as LIGHT ON STATES" + +[LIGHT_TAXI_ON] +Id = LightTaxiOn +CategoryId = Lights +SimVarName = "LIGHT TAXI ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Return true if the light is on." + +[LIGHT_STROBE_ON] +Id = LightStrobeOn +CategoryId = Lights +SimVarName = "LIGHT STROBE ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Return true if the light is on." + +[LIGHT_PANEL_ON] +Id = LightPanelOn +CategoryId = Lights +SimVarName = "LIGHT PANEL ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Return true if the light is on." + +[LIGHT_RECOGNITION_ON] +Id = LightRecognitionOn +CategoryId = Lights +SimVarName = "LIGHT RECOGNITION ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Return true if the light is on." + +[LIGHT_WING_ON] +Id = LightWingOn +CategoryId = Lights +SimVarName = "LIGHT WING ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Return true if the light is on." + +[LIGHT_LOGO_ON] +Id = LightLogoOn +CategoryId = Lights +SimVarName = "LIGHT LOGO ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Return true if the light is on." + +[LIGHT_CABIN_ON] +Id = LightCabinOn +CategoryId = Lights +SimVarName = "LIGHT CABIN ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Return true if the light is on." + +[LIGHT_HEAD_ON] +Id = LightHeadOn +CategoryId = Lights +SimVarName = "LIGHT HEAD ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Return true if the light is on." + +[LIGHT_BRAKE_ON] +Id = LightBrakeOn +CategoryId = Lights +SimVarName = "LIGHT BRAKE ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Return true if the light is on." + +[LIGHT_NAV_ON] +Id = LightNavOn +CategoryId = Lights +SimVarName = "LIGHT NAV ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Return true if the light is on." + +[LIGHT_BEACON_ON] +Id = LightBeaconOn +CategoryId = Lights +SimVarName = "LIGHT BEACON ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Return true if the light is on." + +[LIGHT_LANDING_ON] +Id = LightLandingOn +CategoryId = Lights +SimVarName = "LIGHT LANDING ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Return true if the light is on." + + + +# PositionandSpeed ####################### +# +[category_PositionandSpeed] + +[GROUND_VELOCITY] +Id = GroundVelocity +CategoryId = PositionandSpeed +SimVarName = "GROUND VELOCITY" +Unit = "Knots" +CanSet = False +Indexed = False +Description = "Speed relative to the earths surface" + +[TOTAL_WORLD_VELOCITY] +Id = TotalWorldVelocity +CategoryId = PositionandSpeed +SimVarName = "TOTAL WORLD VELOCITY" +Unit = "Feet per second" +CanSet = False +Indexed = False +Description = "Speed relative to the earths center" + +[VELOCITY_BODY_Z] +Id = VelocityBodyZ +CategoryId = PositionandSpeed +SimVarName = "VELOCITY BODY Z" +Unit = "Feet per second" +CanSet = True +Indexed = False +Description = "True longitudinal speed, relative to aircraft axis" + +[VELOCITY_BODY_X] +Id = VelocityBodyX +CategoryId = PositionandSpeed +SimVarName = "VELOCITY BODY X" +Unit = "Feet per second" +CanSet = True +Indexed = False +Description = "True lateral speed, relative to aircraft axis" + +[VELOCITY_BODY_Y] +Id = VelocityBodyY +CategoryId = PositionandSpeed +SimVarName = "VELOCITY BODY Y" +Unit = "Feet per second" +CanSet = True +Indexed = False +Description = "True vertical speed, relative to aircraft axis" + +[VELOCITY_WORLD_Z] +Id = VelocityWorldZ +CategoryId = PositionandSpeed +SimVarName = "VELOCITY WORLD Z" +Unit = "Feet per second" +CanSet = True +Indexed = False +Description = "Speed relative to earth, in North/South direction" + +[VELOCITY_WORLD_X] +Id = VelocityWorldX +CategoryId = PositionandSpeed +SimVarName = "VELOCITY WORLD X" +Unit = "Feet per second" +CanSet = True +Indexed = False +Description = "Speed relative to earth, in East/West direction" + +[VELOCITY_WORLD_Y] +Id = VelocityWorldY +CategoryId = PositionandSpeed +SimVarName = "VELOCITY WORLD Y" +Unit = "Feet per second" +CanSet = True +Indexed = False +Description = "Speed relative to earth, in vertical direction" + +[ACCELERATION_WORLD_X] +Id = AccelerationWorldX +CategoryId = PositionandSpeed +SimVarName = "ACCELERATION WORLD X" +Unit = "Feet per second squared" +CanSet = True +Indexed = False +Description = "Acceleration relative to earth, in east/west direction" + +[ACCELERATION_WORLD_Y] +Id = AccelerationWorldY +CategoryId = PositionandSpeed +SimVarName = "ACCELERATION WORLD Y" +Unit = "Feet per second squared" +CanSet = True +Indexed = False +Description = "Acceleration relative to earch, in vertical direction" + +[ACCELERATION_WORLD_Z] +Id = AccelerationWorldZ +CategoryId = PositionandSpeed +SimVarName = "ACCELERATION WORLD Z" +Unit = "Feet per second squared" +CanSet = True +Indexed = False +Description = "Acceleration relative to earth, in north/south direction" + +[ACCELERATION_BODY_X] +Id = AccelerationBodyX +CategoryId = PositionandSpeed +SimVarName = "ACCELERATION BODY X" +Unit = "Feet per second squared" +CanSet = True +Indexed = False +Description = "Acceleration relative to aircraft axix, in east/west direction" + +[ACCELERATION_BODY_Y] +Id = AccelerationBodyY +CategoryId = PositionandSpeed +SimVarName = "ACCELERATION BODY Y" +Unit = "Feet per second squared" +CanSet = True +Indexed = False +Description = "Acceleration relative to aircraft axis, in vertical direction" + +[ACCELERATION_BODY_Z] +Id = AccelerationBodyZ +CategoryId = PositionandSpeed +SimVarName = "ACCELERATION BODY Z" +Unit = "Feet per second squared" +CanSet = True +Indexed = False +Description = "Acceleration relative to aircraft axis, in north/south direction" + +[ROTATION_VELOCITY_BODY_X] +Id = RotationVelocityBodyX +CategoryId = PositionandSpeed +SimVarName = "ROTATION VELOCITY BODY X" +Unit = "Feet per second" +CanSet = True +Indexed = False +Description = "Rotation relative to aircraft axis" + +[ROTATION_VELOCITY_BODY_Y] +Id = RotationVelocityBodyY +CategoryId = PositionandSpeed +SimVarName = "ROTATION VELOCITY BODY Y" +Unit = "Feet per second" +CanSet = True +Indexed = False +Description = "Rotation relative to aircraft axis" + +[ROTATION_VELOCITY_BODY_Z] +Id = RotationVelocityBodyZ +CategoryId = PositionandSpeed +SimVarName = "ROTATION VELOCITY BODY Z" +Unit = "Feet per second" +CanSet = True +Indexed = False +Description = "Rotation relative to aircraft axis" + +[RELATIVE_WIND_VELOCITY_BODY_X] +Id = RelativeWindVelocityBodyX +CategoryId = PositionandSpeed +SimVarName = "RELATIVE WIND VELOCITY BODY X" +Unit = "Feet per second" +CanSet = False +Indexed = False +Description = "Lateral speed relative to wind" + +[RELATIVE_WIND_VELOCITY_BODY_Y] +Id = RelativeWindVelocityBodyY +CategoryId = PositionandSpeed +SimVarName = "RELATIVE WIND VELOCITY BODY Y" +Unit = "Feet per second" +CanSet = False +Indexed = False +Description = "Vertical speed relative to wind" + +[RELATIVE_WIND_VELOCITY_BODY_Z] +Id = RelativeWindVelocityBodyZ +CategoryId = PositionandSpeed +SimVarName = "RELATIVE WIND VELOCITY BODY Z" +Unit = "Feet per second" +CanSet = False +Indexed = False +Description = "Longitudinal speed relative to wind" + +[PLANE_ALT_ABOVE_GROUND] +Id = PlaneAltAboveGround +CategoryId = PositionandSpeed +SimVarName = "PLANE ALT ABOVE GROUND" +Unit = "Feet" +CanSet = True +Indexed = False +Description = "Altitude above the surface" + +[PLANE_LATITUDE] +Id = PlaneLatitude +CategoryId = PositionandSpeed +SimVarName = "PLANE LATITUDE" +Unit = "Degrees" +CanSet = True +Indexed = False +Description = "Latitude of aircraft, North is positive, South negative" + +[PLANE_LONGITUDE] +Id = PlaneLongitude +CategoryId = PositionandSpeed +SimVarName = "PLANE LONGITUDE" +Unit = "Degrees" +CanSet = True +Indexed = False +Description = "Longitude of aircraft, East is positive, West negative" + +[PLANE_ALTITUDE] +Id = PlaneAltitude +CategoryId = PositionandSpeed +SimVarName = "PLANE ALTITUDE" +Unit = "Feet" +CanSet = True +Indexed = False +Description = "Altitude of aircraft" + +[PLANE_PITCH_DEGREES] +Id = PlanePitchDegrees +CategoryId = PositionandSpeed +SimVarName = "PLANE PITCH DEGREES" +Unit = "Radians" +CanSet = True +Indexed = False +Description = "Pitch angle, although the name mentions degrees the units used are radians" + +[PLANE_BANK_DEGREES] +Id = PlaneBankDegrees +CategoryId = PositionandSpeed +SimVarName = "PLANE BANK DEGREES" +Unit = "Radians" +CanSet = True +Indexed = False +Description = "Bank angle, although the name mentions degrees the units used are radians" + +[PLANE_HEADING_DEGREES_TRUE] +Id = PlaneHeadingDegreesTrue +CategoryId = PositionandSpeed +SimVarName = "PLANE HEADING DEGREES TRUE" +Unit = "Radians" +CanSet = True +Indexed = False +Description = "Heading relative to true north, although the name mentions degrees the units used are radians" + +[PLANE_HEADING_DEGREES_MAGNETIC] +Id = PlaneHeadingDegreesMagnetic +CategoryId = PositionandSpeed +SimVarName = "PLANE HEADING DEGREES MAGNETIC" +Unit = "Radians" +CanSet = True +Indexed = False +Description = "Heading relative to magnetic north, although the name mentions degrees the units used are radians" + +[MAGVAR] +Id = Magvar +CategoryId = PositionandSpeed +SimVarName = "MAGVAR" +Unit = "Degrees" +CanSet = False +Indexed = False +Description = "Magnetic variation" + +[GROUND_ALTITUDE] +Id = GroundAltitude +CategoryId = PositionandSpeed +SimVarName = "GROUND ALTITUDE" +Unit = "Meters" +CanSet = False +Indexed = False +Description = "Altitude of surface" + +[SIM_ON_GROUND] +Id = SimOnGround +CategoryId = PositionandSpeed +SimVarName = "SIM ON GROUND" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "On ground flag" + +[INCIDENCE_ALPHA] +Id = IncidenceAlpha +CategoryId = PositionandSpeed +SimVarName = "INCIDENCE ALPHA" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Angle of attack" + +[INCIDENCE_BETA] +Id = IncidenceBeta +CategoryId = PositionandSpeed +SimVarName = "INCIDENCE BETA" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Sideslip angle" + +[WING_FLEX_PCT] +Id = WingFlexPct +CategoryId = PositionandSpeed +SimVarName = "WING FLEX PCT" +Unit = "Percent over 100" +CanSet = True +Indexed = True +Description = "The current wing flex. Different values can be set for each wing (for example, during banking). Set an index of 1 for the left wing, and 2 for the right wing." + + + +# FlightInstrumentation ####################### +# +[category_FlightInstrumentation] + +[AIRSPEED_TRUE] +Id = AirspeedTrue +CategoryId = FlightInstrumentation +SimVarName = "AIRSPEED TRUE" +Unit = "Knots" +CanSet = True +Indexed = False +Description = "True airspeed" + +[AIRSPEED_INDICATED] +Id = AirspeedIndicated +CategoryId = FlightInstrumentation +SimVarName = "AIRSPEED INDICATED" +Unit = "Knots" +CanSet = True +Indexed = False +Description = "Indicated airspeed" + +[AIRSPEED_TRUE_CALIBRATE] +Id = AirspeedTrueCalibrate +CategoryId = FlightInstrumentation +SimVarName = "AIRSPEED TRUE CALIBRATE" +Unit = "Degrees" +CanSet = True +Indexed = False +Description = "Angle of True calibration scale on airspeed indicator" + +[AIRSPEED_BARBER_POLE] +Id = AirspeedBarberPole +CategoryId = FlightInstrumentation +SimVarName = "AIRSPEED BARBER POLE" +Unit = "Knots" +CanSet = False +Indexed = False +Description = "Redline airspeed (dynamic on some aircraft)" + +[AIRSPEED_MACH] +Id = AirspeedMach +CategoryId = FlightInstrumentation +SimVarName = "AIRSPEED MACH" +Unit = "Mach" +CanSet = False +Indexed = False +Description = "Current mach" + +[VERTICAL_SPEED] +Id = VerticalSpeed +CategoryId = FlightInstrumentation +SimVarName = "VERTICAL SPEED" +Unit = "feet/minute" +CanSet = True +Indexed = False +Description = "Vertical speed indication" + +[MACH_MAX_OPERATE] +Id = MachMaxOperate +CategoryId = FlightInstrumentation +SimVarName = "MACH MAX OPERATE" +Unit = "Mach" +CanSet = False +Indexed = False +Description = "Maximum design mach" + +[STALL_WARNING] +Id = StallWarning +CategoryId = FlightInstrumentation +SimVarName = "STALL WARNING" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Stall warning state" + +[OVERSPEED_WARNING] +Id = OverspeedWarning +CategoryId = FlightInstrumentation +SimVarName = "OVERSPEED WARNING" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Overspeed warning state" + +[BARBER_POLE_MACH] +Id = BarberPoleMach +CategoryId = FlightInstrumentation +SimVarName = "BARBER POLE MACH" +Unit = "Mach" +CanSet = False +Indexed = False +Description = "Mach associated with maximum airspeed" + +[INDICATED_ALTITUDE] +Id = IndicatedAltitude +CategoryId = FlightInstrumentation +SimVarName = "INDICATED ALTITUDE" +Unit = "Feet" +CanSet = True +Indexed = False +Description = "Altimeter indication" + +[KOHLSMAN_SETTING_MB] +Id = KohlsmanSettingMb +CategoryId = FlightInstrumentation +SimVarName = "KOHLSMAN SETTING MB" +Unit = "Millibars" +CanSet = True +Indexed = False +Description = "Altimeter setting" + +[KOHLSMAN_SETTING_HG] +Id = KohlsmanSettingHg +CategoryId = FlightInstrumentation +SimVarName = "KOHLSMAN SETTING HG" +Unit = "inHg" +CanSet = False +Indexed = False +Description = "Altimeter setting" + +[ATTITUDE_INDICATOR_PITCH_DEGREES] +Id = AttitudeIndicatorPitchDegrees +CategoryId = FlightInstrumentation +SimVarName = "ATTITUDE INDICATOR PITCH DEGREES" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "AI pitch indication" + +[ATTITUDE_INDICATOR_BANK_DEGREES] +Id = AttitudeIndicatorBankDegrees +CategoryId = FlightInstrumentation +SimVarName = "ATTITUDE INDICATOR BANK DEGREES" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "AI bank indication" + +[ATTITUDE_BARS_POSITION] +Id = AttitudeBarsPosition +CategoryId = FlightInstrumentation +SimVarName = "ATTITUDE BARS POSITION" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "AI reference pitch reference bars" + +[ATTITUDE_CAGE] +Id = AttitudeCage +CategoryId = FlightInstrumentation +SimVarName = "ATTITUDE CAGE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "AI caged state" + +[WISKEY_COMPASS_INDICATION_DEGREES] +Id = WiskeyCompassIndicationDegrees +CategoryId = FlightInstrumentation +SimVarName = "WISKEY COMPASS INDICATION DEGREES" +Unit = "Degrees" +CanSet = True +Indexed = False +Description = "Magnetic compass indication" + +[PLANE_HEADING_DEGREES_GYRO] +Id = PlaneHeadingDegreesGyro +CategoryId = FlightInstrumentation +SimVarName = "PLANE HEADING DEGREES GYRO" +Unit = "Radians" +CanSet = True +Indexed = False +Description = "Heading indicator (directional gyro) indication" + +[HEADING_INDICATOR] +Id = HeadingIndicator +CategoryId = FlightInstrumentation +SimVarName = "HEADING INDICATOR" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Heading indicator (directional gyro) indication" + +[GYRO_DRIFT_ERROR] +Id = GyroDriftError +CategoryId = FlightInstrumentation +SimVarName = "GYRO DRIFT ERROR" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Angular error of heading indicator" + +[DELTA_HEADING_RATE] +Id = DeltaHeadingRate +CategoryId = FlightInstrumentation +SimVarName = "DELTA HEADING RATE" +Unit = "Radians per second" +CanSet = True +Indexed = False +Description = "Rate of turn of heading indicator" + +[TURN_COORDINATOR_BALL] +Id = TurnCoordinatorBall +CategoryId = FlightInstrumentation +SimVarName = "TURN COORDINATOR BALL" +Unit = "Position" +CanSet = False +Indexed = False +Description = "Turn coordinator ball position" + +[ANGLE_OF_ATTACK_INDICATOR] +Id = AngleOfAttackIndicator +CategoryId = FlightInstrumentation +SimVarName = "ANGLE OF ATTACK INDICATOR" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "AoA indication" + +[RADIO_HEIGHT] +Id = RadioHeight +CategoryId = FlightInstrumentation +SimVarName = "RADIO HEIGHT" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "Radar altitude" + +[PARTIAL_PANEL_ADF] +Id = PartialPanelAdf +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL ADF" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_AIRSPEED] +Id = PartialPanelAirspeed +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL AIRSPEED" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_ALTIMETER] +Id = PartialPanelAltimeter +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL ALTIMETER" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_ATTITUDE] +Id = PartialPanelAttitude +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL ATTITUDE" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_COMM] +Id = PartialPanelComm +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL COMM" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_COMPASS] +Id = PartialPanelCompass +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL COMPASS" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_ELECTRICAL] +Id = PartialPanelElectrical +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL ELECTRICAL" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_AVIONICS] +Id = PartialPanelAvionics +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL AVIONICS" +Unit = "Enum" +CanSet = False +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_ENGINE] +Id = PartialPanelEngine +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL ENGINE" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_FUEL_INDICATOR] +Id = PartialPanelFuelIndicator +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL FUEL INDICATOR" +Unit = "Enum" +CanSet = False +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_HEADING] +Id = PartialPanelHeading +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL HEADING" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_VERTICAL_VELOCITY] +Id = PartialPanelVerticalVelocity +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL VERTICAL VELOCITY" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_TRANSPONDER] +Id = PartialPanelTransponder +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL TRANSPONDER" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_NAV] +Id = PartialPanelNav +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL NAV" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_PITOT] +Id = PartialPanelPitot +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL PITOT" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_TURN_COORDINATOR] +Id = PartialPanelTurnCoordinator +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL TURN COORDINATOR" +Unit = "Enum" +CanSet = False +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[PARTIAL_PANEL_VACUUM] +Id = PartialPanelVacuum +CategoryId = FlightInstrumentation +SimVarName = "PARTIAL PANEL VACUUM" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Gauge fail flag (0 = ok, 1 = fail, 2 = blank)" + +[MAX_G_FORCE] +Id = MaxGForce +CategoryId = FlightInstrumentation +SimVarName = "MAX G FORCE" +Unit = "Gforce" +CanSet = False +Indexed = False +Description = "Maximum G force attained" + +[MIN_G_FORCE] +Id = MinGForce +CategoryId = FlightInstrumentation +SimVarName = "MIN G FORCE" +Unit = "Gforce" +CanSet = False +Indexed = False +Description = "Minimum G force attained" + +[SUCTION_PRESSURE] +Id = SuctionPressure +CategoryId = FlightInstrumentation +SimVarName = "SUCTION PRESSURE" +Unit = "inHg" +CanSet = True +Indexed = False +Description = "Vacuum system suction pressure" + + + +# Avionics ####################### +# +[category_Avionics] + +[AVIONICS_MASTER_SWITCH] +Id = AvionicsMasterSwitch +CategoryId = Avionics +SimVarName = "AVIONICS MASTER SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Avionics switch state" + +[NAV_SOUND] +Id = NavSound +CategoryId = Avionics +SimVarName = "NAV SOUND" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Nav audio flag. Index of 1 or 2." + +[DME_SOUND] +Id = DmeSound +CategoryId = Avionics +SimVarName = "DME SOUND" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "DME audio flag" + +[ADF_SOUND] +Id = AdfSound +CategoryId = Avionics +SimVarName = "ADF SOUND" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "ADF audio flag. Index of 0 or 1." + +[MARKER_SOUND] +Id = MarkerSound +CategoryId = Avionics +SimVarName = "MARKER SOUND" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Marker audio flag" + +[COM_TRANSMIT] +Id = ComTransmit +CategoryId = Avionics +SimVarName = "COM TRANSMIT" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Audio panel com transmit state. Index of 1 or 2." + +[COM_RECIEVE_ALL] +Id = ComRecieveAll +CategoryId = Avionics +SimVarName = "COM RECIEVE ALL" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Flag if all Coms receiving" + +[COM_ACTIVE_FREQUENCY] +Id = ComActiveFrequency +CategoryId = Avionics +SimVarName = "COM ACTIVE FREQUENCY" +Unit = "MHz" +CanSet = False +Indexed = True +Description = "Com frequency. Index is 1 or 2." + +[COM_STANDBY_FREQUENCY] +Id = ComStandbyFrequency +CategoryId = Avionics +SimVarName = "COM STANDBY FREQUENCY" +Unit = "MHz" +CanSet = False +Indexed = True +Description = "Com standby frequency. Index is 1 or 2." + +[NAV_AVAILABLE] +Id = NavAvailable +CategoryId = Avionics +SimVarName = "NAV AVAILABLE" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Flag if Nav equipped on aircraft" + +[NAV_ACTIVE_FREQUENCY] +Id = NavActiveFrequency +CategoryId = Avionics +SimVarName = "NAV ACTIVE FREQUENCY" +Unit = "MHz" +CanSet = False +Indexed = True +Description = "Nav active frequency. Index is 1 or 2." + +[NAV_STANDBY_FREQUENCY] +Id = NavStandbyFrequency +CategoryId = Avionics +SimVarName = "NAV STANDBY FREQUENCY" +Unit = "MHz" +CanSet = False +Indexed = True +Description = "Nav standby frequency. Index is 1 or 2." + +[NAV_SIGNAL] +Id = NavSignal +CategoryId = Avionics +SimVarName = "NAV SIGNAL" +Unit = "Number" +CanSet = False +Indexed = True +Description = "Nav signal strength" + +[NAV_HAS_NAV] +Id = NavHasNav +CategoryId = Avionics +SimVarName = "NAV HAS NAV" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Flag if Nav has signal" + +[NAV_HAS_LOCALIZER] +Id = NavHasLocalizer +CategoryId = Avionics +SimVarName = "NAV HAS LOCALIZER" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Flag if tuned station is a localizer" + +[NAV_HAS_DME] +Id = NavHasDme +CategoryId = Avionics +SimVarName = "NAV HAS DME" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Flag if tuned station has a DME" + +[NAV_HAS_GLIDE_SLOPE] +Id = NavHasGlideSlope +CategoryId = Avionics +SimVarName = "NAV HAS GLIDE SLOPE" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Flag if tuned station has a glideslope" + +[NAV_BACK_COURSE_FLAGS] +Id = NavBackCourseFlags +CategoryId = Avionics +SimVarName = "NAV BACK COURSE FLAGS" +Unit = "Flags" +CanSet = False +Indexed = True +Description = "Returns the following bit flags:; BIT0: 1=back course available; BIT1: 1=localizer tuned in; BIT2: 1=on course; BIT7: 1=station active" + +[NAV_MAGVAR] +Id = NavMagvar +CategoryId = Avionics +SimVarName = "NAV MAGVAR" +Unit = "Degrees" +CanSet = False +Indexed = True +Description = "Magnetic variation of tuned nav station" + +[NAV_RADIAL] +Id = NavRadial +CategoryId = Avionics +SimVarName = "NAV RADIAL" +Unit = "Degrees" +CanSet = False +Indexed = True +Description = "Radial that aircraft is on" + +[NAV_RADIAL_ERROR] +Id = NavRadialError +CategoryId = Avionics +SimVarName = "NAV RADIAL ERROR" +Unit = "Degrees" +CanSet = False +Indexed = True +Description = "Difference between current radial and OBS tuned radial" + +[NAV_LOCALIZER] +Id = NavLocalizer +CategoryId = Avionics +SimVarName = "NAV LOCALIZER" +Unit = "Degrees" +CanSet = False +Indexed = True +Description = "Localizer course heading" + +[NAV_GLIDE_SLOPE_ERROR] +Id = NavGlideSlopeError +CategoryId = Avionics +SimVarName = "NAV GLIDE SLOPE ERROR" +Unit = "Degrees" +CanSet = False +Indexed = True +Description = "Difference between current position and glideslope angle. Note that this provides 32 bit floating point precision, rather than the 8 bit integer precision of NAV GSI." + +[NAV_CDI] +Id = NavCdi +CategoryId = Avionics +SimVarName = "NAV CDI" +Unit = "Number" +CanSet = False +Indexed = True +Description = "CDI needle deflection (+/- 127)" + +[NAV_GSI] +Id = NavGsi +CategoryId = Avionics +SimVarName = "NAV GSI" +Unit = "Number" +CanSet = False +Indexed = True +Description = "Glideslope needle deflection (+/- 119). Note that this provides only 8 bit precision, whereas NAV GLIDE SLOPE ERROR provides 32 bit floating point precision." + +[NAV_GS_FLAG] +Id = NavGsFlag +CategoryId = Avionics +SimVarName = "NAV GS FLAG" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Glideslope flag" + +[NAV_OBS] +Id = NavObs +CategoryId = Avionics +SimVarName = "NAV OBS" +Unit = "Degrees" +CanSet = False +Indexed = True +Description = "OBS setting. Index of 1 or 2." + +[NAV_DME] +Id = NavDme +CategoryId = Avionics +SimVarName = "NAV DME" +Unit = "Nautical miles" +CanSet = False +Indexed = True +Description = "DME distance" + +[NAV_DMESPEED] +Id = NavDmespeed +CategoryId = Avionics +SimVarName = "NAV DMESPEED" +Unit = "Knots" +CanSet = False +Indexed = True +Description = "DME speed" + +[ADF_ACTIVE_FREQUENCY] +Id = AdfActiveFrequency +CategoryId = Avionics +SimVarName = "ADF ACTIVE FREQUENCY" +Unit = "Frequency ADF BCD32" +CanSet = False +Indexed = True +Description = "ADF frequency. Index of 1 or 2." + +[ADF_STANDBY_FREQUENCY] +Id = AdfStandbyFrequency +CategoryId = Avionics +SimVarName = "ADF STANDBY FREQUENCY" +Unit = "Hz" +CanSet = False +Indexed = True +Description = "ADF standby frequency" + +[ADF_RADIAL] +Id = AdfRadial +CategoryId = Avionics +SimVarName = "ADF RADIAL" +Unit = "Degrees" +CanSet = False +Indexed = True +Description = "Current direction from NDB station" + +[ADF_SIGNAL] +Id = AdfSignal +CategoryId = Avionics +SimVarName = "ADF SIGNAL" +Unit = "Number" +CanSet = False +Indexed = True +Description = "Signal strength" + +[TRANSPONDER_CODE] +Id = TransponderCode +CategoryId = Avionics +SimVarName = "TRANSPONDER CODE" +Unit = "BCO16" +CanSet = False +Indexed = True +Description = "4-digit code" + +[MARKER_BEACON_STATE] +Id = MarkerBeaconState +CategoryId = Avionics +SimVarName = "MARKER BEACON STATE" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Marker beacon state:; 0 = None; 1 = Outer; 2 = Middle; 3 = Inner" + +[INNER_MARKER] +Id = InnerMarker +CategoryId = Avionics +SimVarName = "INNER MARKER" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "Inner marker state" + +[MIDDLE_MARKER] +Id = MiddleMarker +CategoryId = Avionics +SimVarName = "MIDDLE MARKER" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "Middle marker state" + +[OUTER_MARKER] +Id = OuterMarker +CategoryId = Avionics +SimVarName = "OUTER MARKER" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "Outer marker state" + +[NAV_RAW_GLIDE_SLOPE] +Id = NavRawGlideSlope +CategoryId = Avionics +SimVarName = "NAV RAW GLIDE SLOPE" +Unit = "Degrees" +CanSet = False +Indexed = True +Description = "Glide slope angle" + +[ADF_CARD] +Id = AdfCard +CategoryId = Avionics +SimVarName = "ADF CARD" +Unit = "Degrees" +CanSet = False +Indexed = False +Description = "ADF compass rose setting" + +[HSI_CDI_NEEDLE] +Id = HsiCdiNeedle +CategoryId = Avionics +SimVarName = "HSI CDI NEEDLE" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Needle deflection (+/- 127)" + +[HSI_GSI_NEEDLE] +Id = HsiGsiNeedle +CategoryId = Avionics +SimVarName = "HSI GSI NEEDLE" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Needle deflection (+/- 119)" + +[HSI_CDI_NEEDLE_VALID] +Id = HsiCdiNeedleValid +CategoryId = Avionics +SimVarName = "HSI CDI NEEDLE VALID" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Signal valid" + +[HSI_GSI_NEEDLE_VALID] +Id = HsiGsiNeedleValid +CategoryId = Avionics +SimVarName = "HSI GSI NEEDLE VALID" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Signal valid" + +[HSI_TF_FLAGS] +Id = HsiTfFlags +CategoryId = Avionics +SimVarName = "HSI TF FLAGS" +Unit = "Enum" +CanSet = False +Indexed = False +Description = "Nav TO/FROM flag:; 0 = Off; 1 = TO; 2 = FROM" + +[HSI_BEARING_VALID] +Id = HsiBearingValid +CategoryId = Avionics +SimVarName = "HSI BEARING VALID" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "This will return true if the HSI BEARING variable contains valid data." + +[HSI_BEARING] +Id = HsiBearing +CategoryId = Avionics +SimVarName = "HSI BEARING" +Unit = "Degrees" +CanSet = False +Indexed = False +Description = "If the GPS DRIVES NAV1 variable is true and the HSI BEARING VALID variable is true, this variable contains the HSI needle bearing. If the GPS DRIVES NAV1 variable is false and the HSI BEARING VALID variable is true, this variable contains the ADF1 frequency." + +[HSI_HAS_LOCALIZER] +Id = HsiHasLocalizer +CategoryId = Avionics +SimVarName = "HSI HAS LOCALIZER" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Station is a localizer" + +[HSI_SPEED] +Id = HsiSpeed +CategoryId = Avionics +SimVarName = "HSI SPEED" +Unit = "Knots" +CanSet = False +Indexed = False +Description = "DME/GPS speed" + +[HSI_DISTANCE] +Id = HsiDistance +CategoryId = Avionics +SimVarName = "HSI DISTANCE" +Unit = "Nautical miles" +CanSet = False +Indexed = False +Description = "DME/GPS distance" + +[GPS_POSITION_LAT] +Id = GpsPositionLat +CategoryId = Avionics +SimVarName = "GPS POSITION LAT" +Unit = "Degrees" +CanSet = False +Indexed = False +Description = "Current GPS latitude" + +[GPS_POSITION_LON] +Id = GpsPositionLon +CategoryId = Avionics +SimVarName = "GPS POSITION LON" +Unit = "Degrees" +CanSet = False +Indexed = False +Description = "Current GPS longitude" + +[GPS_POSITION_ALT] +Id = GpsPositionAlt +CategoryId = Avionics +SimVarName = "GPS POSITION ALT" +Unit = "Meters" +CanSet = False +Indexed = False +Description = "Current GPS altitude" + +[GPS_MAGVAR] +Id = GpsMagvar +CategoryId = Avionics +SimVarName = "GPS MAGVAR" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Current GPS magnetic variation" + +[GPS_IS_ACTIVE_FLIGHT_PLAN] +Id = GpsIsActiveFlightPlan +CategoryId = Avionics +SimVarName = "GPS IS ACTIVE FLIGHT PLAN" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Flight plan mode active" + +[GPS_IS_ACTIVE_WAY_POINT] +Id = GpsIsActiveWayPoint +CategoryId = Avionics +SimVarName = "GPS IS ACTIVE WAY POINT" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Waypoint mode active" + +[GPS_IS_ARRIVED] +Id = GpsIsArrived +CategoryId = Avionics +SimVarName = "GPS IS ARRIVED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is flight plan destination reached" + +[GPS_IS_DIRECTTO_FLIGHTPLAN] +Id = GpsIsDirecttoFlightplan +CategoryId = Avionics +SimVarName = "GPS IS DIRECTTO FLIGHTPLAN" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is Direct To Waypoint mode active" + +[GPS_GROUND_SPEED] +Id = GpsGroundSpeed +CategoryId = Avionics +SimVarName = "GPS GROUND SPEED" +Unit = "Meters per second" +CanSet = False +Indexed = False +Description = "Current ground speed" + +[GPS_GROUND_TRUE_HEADING] +Id = GpsGroundTrueHeading +CategoryId = Avionics +SimVarName = "GPS GROUND TRUE HEADING" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Current true heading" + +[GPS_GROUND_MAGNETIC_TRACK] +Id = GpsGroundMagneticTrack +CategoryId = Avionics +SimVarName = "GPS GROUND MAGNETIC TRACK" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Current magnetic ground track" + +[GPS_GROUND_TRUE_TRACK] +Id = GpsGroundTrueTrack +CategoryId = Avionics +SimVarName = "GPS GROUND TRUE TRACK" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Current true ground track" + +[GPS_WP_DISTANCE] +Id = GpsWpDistance +CategoryId = Avionics +SimVarName = "GPS WP DISTANCE" +Unit = "Meters" +CanSet = False +Indexed = False +Description = "Distance to waypoint" + +[GPS_WP_BEARING] +Id = GpsWpBearing +CategoryId = Avionics +SimVarName = "GPS WP BEARING" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Magnetic bearing to waypoint" + +[GPS_WP_TRUE_BEARING] +Id = GpsWpTrueBearing +CategoryId = Avionics +SimVarName = "GPS WP TRUE BEARING" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "True bearing to waypoint" + +[GPS_WP_CROSS_TRK] +Id = GpsWpCrossTrk +CategoryId = Avionics +SimVarName = "GPS WP CROSS TRK" +Unit = "Meters" +CanSet = False +Indexed = False +Description = "Cross track distance" + +[GPS_WP_DESIRED_TRACK] +Id = GpsWpDesiredTrack +CategoryId = Avionics +SimVarName = "GPS WP DESIRED TRACK" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Desired track to waypoint" + +[GPS_WP_TRUE_REQ_HDG] +Id = GpsWpTrueReqHdg +CategoryId = Avionics +SimVarName = "GPS WP TRUE REQ HDG" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Required true heading to waypoint" + +[GPS_WP_VERTICAL_SPEED] +Id = GpsWpVerticalSpeed +CategoryId = Avionics +SimVarName = "GPS WP VERTICAL SPEED" +Unit = "Meters per second" +CanSet = False +Indexed = False +Description = "Vertical speed to waypoint" + +[GPS_WP_TRACK_ANGLE_ERROR] +Id = GpsWpTrackAngleError +CategoryId = Avionics +SimVarName = "GPS WP TRACK ANGLE ERROR" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Tracking angle error to waypoint" + +[GPS_ETE] +Id = GpsEte +CategoryId = Avionics +SimVarName = "GPS ETE" +Unit = "Seconds" +CanSet = False +Indexed = False +Description = "Estimated time enroute to destination" + +[GPS_ETA] +Id = GpsEta +CategoryId = Avionics +SimVarName = "GPS ETA" +Unit = "Seconds" +CanSet = False +Indexed = False +Description = "Estimated time of arrival at destination" + +[GPS_WP_NEXT_LAT] +Id = GpsWpNextLat +CategoryId = Avionics +SimVarName = "GPS WP NEXT LAT" +Unit = "Degrees" +CanSet = False +Indexed = False +Description = "Latitude of next waypoint" + +[GPS_WP_NEXT_LON] +Id = GpsWpNextLon +CategoryId = Avionics +SimVarName = "GPS WP NEXT LON" +Unit = "Degrees" +CanSet = False +Indexed = False +Description = "Longitude of next waypoint" + +[GPS_WP_NEXT_ALT] +Id = GpsWpNextAlt +CategoryId = Avionics +SimVarName = "GPS WP NEXT ALT" +Unit = "Meters" +CanSet = False +Indexed = False +Description = "Altitude of next waypoint" + +[GPS_WP_PREV_VALID] +Id = GpsWpPrevValid +CategoryId = Avionics +SimVarName = "GPS WP PREV VALID" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is previous waypoint valid (i.e. current waypoint is not the first waypoint)" + +[GPS_WP_PREV_LAT] +Id = GpsWpPrevLat +CategoryId = Avionics +SimVarName = "GPS WP PREV LAT" +Unit = "Degrees" +CanSet = False +Indexed = False +Description = "Latitude of previous waypoint" + +[GPS_WP_PREV_LON] +Id = GpsWpPrevLon +CategoryId = Avionics +SimVarName = "GPS WP PREV LON" +Unit = "Degrees" +CanSet = False +Indexed = False +Description = "Longitude of previous waypoint" + +[GPS_WP_PREV_ALT] +Id = GpsWpPrevAlt +CategoryId = Avionics +SimVarName = "GPS WP PREV ALT" +Unit = "Meters" +CanSet = False +Indexed = False +Description = "Altitude of previous waypoint" + +[GPS_WP_ETE] +Id = GpsWpEte +CategoryId = Avionics +SimVarName = "GPS WP ETE" +Unit = "Seconds" +CanSet = False +Indexed = False +Description = "Estimated time enroute to waypoint" + +[GPS_WP_ETA] +Id = GpsWpEta +CategoryId = Avionics +SimVarName = "GPS WP ETA" +Unit = "Seconds" +CanSet = False +Indexed = False +Description = "Estimated time of arrival at waypoint" + +[GPS_COURSE_TO_STEER] +Id = GpsCourseToSteer +CategoryId = Avionics +SimVarName = "GPS COURSE TO STEER" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Suggested heading to steer (for autopilot)" + +[GPS_FLIGHT_PLAN_WP_INDEX] +Id = GpsFlightPlanWpIndex +CategoryId = Avionics +SimVarName = "GPS FLIGHT PLAN WP INDEX" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Index of waypoint" + +[GPS_FLIGHT_PLAN_WP_COUNT] +Id = GpsFlightPlanWpCount +CategoryId = Avionics +SimVarName = "GPS FLIGHT PLAN WP COUNT" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Number of waypoints" + +[GPS_IS_ACTIVE_WP_LOCKED] +Id = GpsIsActiveWpLocked +CategoryId = Avionics +SimVarName = "GPS IS ACTIVE WP LOCKED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is switching to next waypoint locked" + +[GPS_IS_APPROACH_LOADED] +Id = GpsIsApproachLoaded +CategoryId = Avionics +SimVarName = "GPS IS APPROACH LOADED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is approach loaded" + +[GPS_IS_APPROACH_ACTIVE] +Id = GpsIsApproachActive +CategoryId = Avionics +SimVarName = "GPS IS APPROACH ACTIVE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is approach mode active" + +[GPS_APPROACH_IS_WP_RUNWAY] +Id = GpsApproachIsWpRunway +CategoryId = Avionics +SimVarName = "GPS APPROACH IS WP RUNWAY" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Waypoint is the runway" + +[GPS_APPROACH_APPROACH_INDEX] +Id = GpsApproachApproachIndex +CategoryId = Avionics +SimVarName = "GPS APPROACH APPROACH INDEX" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Index of approach for given airport" + +[GPS_APPROACH_TRANSITION_INDEX] +Id = GpsApproachTransitionIndex +CategoryId = Avionics +SimVarName = "GPS APPROACH TRANSITION INDEX" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Index of approach transition" + +[GPS_APPROACH_IS_FINAL] +Id = GpsApproachIsFinal +CategoryId = Avionics +SimVarName = "GPS APPROACH IS FINAL" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is approach transition final approach segment" + +[GPS_APPROACH_IS_MISSED] +Id = GpsApproachIsMissed +CategoryId = Avionics +SimVarName = "GPS APPROACH IS MISSED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is approach segment missed approach segment" + +[GPS_APPROACH_TIMEZONE_DEVIATION] +Id = GpsApproachTimezoneDeviation +CategoryId = Avionics +SimVarName = "GPS APPROACH TIMEZONE DEVIATION" +Unit = "Seconds" +CanSet = False +Indexed = False +Description = "Deviation of local time from GMT" + +[GPS_APPROACH_WP_INDEX] +Id = GpsApproachWpIndex +CategoryId = Avionics +SimVarName = "GPS APPROACH WP INDEX" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Index of current waypoint" + +[GPS_APPROACH_WP_COUNT] +Id = GpsApproachWpCount +CategoryId = Avionics +SimVarName = "GPS APPROACH WP COUNT" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Number of waypoints" + +[GPS_DRIVES_NAV1] +Id = GpsDrivesNav1 +CategoryId = Avionics +SimVarName = "GPS DRIVES NAV1" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "GPS is driving Nav 1 indicator" + +[COM_RECEIVE_ALL] +Id = ComReceiveAll +CategoryId = Avionics +SimVarName = "COM RECEIVE ALL" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Toggles all COM radios to receive on" + +[COM_AVAILABLE] +Id = ComAvailable +CategoryId = Avionics +SimVarName = "COM AVAILABLE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if either COM1 or COM2 is available" + +[COM_TEST] +Id = ComTest +CategoryId = Avionics +SimVarName = "COM TEST" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Enter an index of 1 or 2. True if the COM system is working." + +[TRANSPONDER_AVAILABLE] +Id = TransponderAvailable +CategoryId = Avionics +SimVarName = "TRANSPONDER AVAILABLE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if a transponder is available" + +[ADF_AVAILABLE] +Id = AdfAvailable +CategoryId = Avionics +SimVarName = "ADF AVAILABLE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if ADF is available" + +[ADF_FREQUENCY] +Id = AdfFrequency +CategoryId = Avionics +SimVarName = "ADF FREQUENCY" +Unit = "Frequency BCD16" +CanSet = False +Indexed = True +Description = "Legacy, use ADF ACTIVE FREQUENCY" + +[ADF_EXT_FREQUENCY] +Id = AdfExtFrequency +CategoryId = Avionics +SimVarName = "ADF EXT FREQUENCY" +Unit = "Frequency BCD16" +CanSet = False +Indexed = True +Description = "Legacy, use ADF ACTIVE FREQUENCY" + +[ADF_IDENT] +Id = AdfIdent +CategoryId = Avionics +SimVarName = "ADF IDENT" +Unit = "String" +CanSet = False +Indexed = False +Description = "ICAO code" + +[ADF_NAME] +Id = AdfName +CategoryId = Avionics +SimVarName = "ADF NAME" +Unit = "String" +CanSet = False +Indexed = False +Description = "Descriptive name" + +[NAV_IDENT] +Id = NavIdent +CategoryId = Avionics +SimVarName = "NAV IDENT" +Unit = "String" +CanSet = False +Indexed = False +Description = "ICAO code" + +[NAV_NAME] +Id = NavName +CategoryId = Avionics +SimVarName = "NAV NAME" +Unit = "String" +CanSet = False +Indexed = False +Description = "Descriptive name" + +[NAV_CODES] +Id = NavCodes +CategoryId = Avionics +SimVarName = "NAV CODES" +Unit = "Flags" +CanSet = False +Indexed = True +Description = "Returns bit flags with the following meaning:; BIT7: 0= VOR 1= Localizer; BIT6: 1= glideslope available; BIT5: 1= no localizer backcourse; BIT4: 1= DME transmitter at glide slope transmitter; BIT3: 1= no nav signal available; BIT2: 1= voice available; BIT1: 1 = TACAN available; BIT0: 1= DME available" + +[NAV_GLIDE_SLOPE] +Id = NavGlideSlope +CategoryId = Avionics +SimVarName = "NAV GLIDE SLOPE" +Unit = "Number" +CanSet = False +Indexed = False +Description = "The glide slope gradient." + +[NAV_RELATIVE_BEARING_TO_STATION] +Id = NavRelativeBearingToStation +CategoryId = Avionics +SimVarName = "NAV RELATIVE BEARING TO STATION" +Unit = "Degrees" +CanSet = False +Indexed = True +Description = "Relative bearing to station" + +[SELECTED_DME] +Id = SelectedDme +CategoryId = Avionics +SimVarName = "SELECTED DME" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Selected DME" + +[GPS_WP_NEXT_ID] +Id = GpsWpNextId +CategoryId = Avionics +SimVarName = "GPS WP NEXT ID" +Unit = "String" +CanSet = False +Indexed = False +Description = "ID of next GPS waypoint" + +[GPS_WP_PREV_ID] +Id = GpsWpPrevId +CategoryId = Avionics +SimVarName = "GPS WP PREV ID" +Unit = "String" +CanSet = False +Indexed = False +Description = "ID of previous GPS waypoint" + +[GPS_TARGET_DISTANCE] +Id = GpsTargetDistance +CategoryId = Avionics +SimVarName = "GPS TARGET DISTANCE" +Unit = "Meters" +CanSet = False +Indexed = False +Description = "Distance to target" + +[GPS_TARGET_ALTITUDE] +Id = GpsTargetAltitude +CategoryId = Avionics +SimVarName = "GPS TARGET ALTITUDE" +Unit = "Meters" +CanSet = False +Indexed = False +Description = "Altitude of GPS target" + + + +# Controls ####################### +# +[category_Controls] + +[YOKE_Y_POSITION] +Id = YokeYPosition +CategoryId = Controls +SimVarName = "YOKE Y POSITION" +Unit = "Position" +CanSet = True +Indexed = False +Description = "Percent control deflection fore/aft (for animation)" + +[YOKE_X_POSITION] +Id = YokeXPosition +CategoryId = Controls +SimVarName = "YOKE X POSITION" +Unit = "Position" +CanSet = True +Indexed = False +Description = "Percent control deflection left/right (for animation)" + +[RUDDER_PEDAL_POSITION] +Id = RudderPedalPosition +CategoryId = Controls +SimVarName = "RUDDER PEDAL POSITION" +Unit = "Position" +CanSet = True +Indexed = False +Description = "Percent rudder pedal deflection (for animation)" + +[RUDDER_POSITION] +Id = RudderPosition +CategoryId = Controls +SimVarName = "RUDDER POSITION" +Unit = "Position" +CanSet = True +Indexed = False +Description = "Percent rudder input deflection" + +[ELEVATOR_POSITION] +Id = ElevatorPosition +CategoryId = Controls +SimVarName = "ELEVATOR POSITION" +Unit = "Position" +CanSet = True +Indexed = False +Description = "Percent elevator input deflection" + +[AILERON_POSITION] +Id = AileronPosition +CategoryId = Controls +SimVarName = "AILERON POSITION" +Unit = "Position" +CanSet = True +Indexed = False +Description = "Percent aileron input left/right" + +[ELEVATOR_TRIM_POSITION] +Id = ElevatorTrimPosition +CategoryId = Controls +SimVarName = "ELEVATOR TRIM POSITION" +Unit = "Radians" +CanSet = True +Indexed = False +Description = "Elevator trim deflection" + +[ELEVATOR_TRIM_INDICATOR] +Id = ElevatorTrimIndicator +CategoryId = Controls +SimVarName = "ELEVATOR TRIM INDICATOR" +Unit = "Position" +CanSet = False +Indexed = False +Description = "Percent elevator trim (for indication)" + +[ELEVATOR_TRIM_PCT] +Id = ElevatorTrimPct +CategoryId = Controls +SimVarName = "ELEVATOR TRIM PCT" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Percent elevator trim" + +[BRAKE_LEFT_POSITION] +Id = BrakeLeftPosition +CategoryId = Controls +SimVarName = "BRAKE LEFT POSITION" +Unit = "Position" +CanSet = True +Indexed = False +Description = "Percent left brake" + +[BRAKE_RIGHT_POSITION] +Id = BrakeRightPosition +CategoryId = Controls +SimVarName = "BRAKE RIGHT POSITION" +Unit = "Position" +CanSet = True +Indexed = False +Description = "Percent right brake" + +[BRAKE_INDICATOR] +Id = BrakeIndicator +CategoryId = Controls +SimVarName = "BRAKE INDICATOR" +Unit = "Position" +CanSet = False +Indexed = False +Description = "Brake on indication" + +[BRAKE_PARKING_POSITION] +Id = BrakeParkingPosition +CategoryId = Controls +SimVarName = "BRAKE PARKING POSITION" +Unit = "Position" +CanSet = True +Indexed = False +Description = "Parking brake on" + +[BRAKE_PARKING_INDICATOR] +Id = BrakeParkingIndicator +CategoryId = Controls +SimVarName = "BRAKE PARKING INDICATOR" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Parking brake indicator" + +[SPOILERS_ARMED] +Id = SpoilersArmed +CategoryId = Controls +SimVarName = "SPOILERS ARMED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Auto-spoilers armed" + +[SPOILERS_HANDLE_POSITION] +Id = SpoilersHandlePosition +CategoryId = Controls +SimVarName = "SPOILERS HANDLE POSITION" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Spoiler handle position" + +[SPOILERS_LEFT_POSITION] +Id = SpoilersLeftPosition +CategoryId = Controls +SimVarName = "SPOILERS LEFT POSITION" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Percent left spoiler deflected" + +[SPOILERS_RIGHT_POSITION] +Id = SpoilersRightPosition +CategoryId = Controls +SimVarName = "SPOILERS RIGHT POSITION" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Percent right spoiler deflected" + +[FLAPS_HANDLE_PERCENT] +Id = FlapsHandlePercent +CategoryId = Controls +SimVarName = "FLAPS HANDLE PERCENT" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Percent flap handle extended" + +[FLAPS_HANDLE_INDEX] +Id = FlapsHandleIndex +CategoryId = Controls +SimVarName = "FLAPS HANDLE INDEX" +Unit = "Number" +CanSet = True +Indexed = False +Description = "Index of current flap position" + +[FLAPS_NUM_HANDLE_POSITIONS] +Id = FlapsNumHandlePositions +CategoryId = Controls +SimVarName = "FLAPS NUM HANDLE POSITIONS" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Number of flap positions" + +[TRAILING_EDGE_FLAPS_LEFT_PERCENT] +Id = TrailingEdgeFlapsLeftPercent +CategoryId = Controls +SimVarName = "TRAILING EDGE FLAPS LEFT PERCENT" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent left trailing edge flap extended" + +[TRAILING_EDGE_FLAPS_RIGHT_PERCENT] +Id = TrailingEdgeFlapsRightPercent +CategoryId = Controls +SimVarName = "TRAILING EDGE FLAPS RIGHT PERCENT" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent right trailing edge flap extended" + +[TRAILING_EDGE_FLAPS_LEFT_ANGLE] +Id = TrailingEdgeFlapsLeftAngle +CategoryId = Controls +SimVarName = "TRAILING EDGE FLAPS LEFT ANGLE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Angle left trailing edge flap extended. Use TRAILING EDGE FLAPS LEFT PERCENT to set a value." + +[TRAILING_EDGE_FLAPS_RIGHT_ANGLE] +Id = TrailingEdgeFlapsRightAngle +CategoryId = Controls +SimVarName = "TRAILING EDGE FLAPS RIGHT ANGLE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Angle right trailing edge flap extended. Use TRAILING EDGE FLAPS RIGHT PERCENT to set a value." + +[LEADING_EDGE_FLAPS_LEFT_PERCENT] +Id = LeadingEdgeFlapsLeftPercent +CategoryId = Controls +SimVarName = "LEADING EDGE FLAPS LEFT PERCENT" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent left leading edge flap extended" + +[LEADING_EDGE_FLAPS_RIGHT_PERCENT] +Id = LeadingEdgeFlapsRightPercent +CategoryId = Controls +SimVarName = "LEADING EDGE FLAPS RIGHT PERCENT" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent right leading edge flap extended" + +[LEADING_EDGE_FLAPS_LEFT_ANGLE] +Id = LeadingEdgeFlapsLeftAngle +CategoryId = Controls +SimVarName = "LEADING EDGE FLAPS LEFT ANGLE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Angle left leading edge flap extended. Use LEADING EDGE FLAPS LEFT PERCENT to set a value." + +[LEADING_EDGE_FLAPS_RIGHT_ANGLE] +Id = LeadingEdgeFlapsRightAngle +CategoryId = Controls +SimVarName = "LEADING EDGE FLAPS RIGHT ANGLE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Angle right leading edge flap extended. Use LEADING EDGE FLAPS RIGHT PERCENT to set a value." + +[AILERON_LEFT_DEFLECTION] +Id = AileronLeftDeflection +CategoryId = Controls +SimVarName = "AILERON LEFT DEFLECTION" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Angle deflection" + +[AILERON_LEFT_DEFLECTION_PCT] +Id = AileronLeftDeflectionPct +CategoryId = Controls +SimVarName = "AILERON LEFT DEFLECTION PCT" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Percent deflection" + +[AILERON_RIGHT_DEFLECTION] +Id = AileronRightDeflection +CategoryId = Controls +SimVarName = "AILERON RIGHT DEFLECTION" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Angle deflection" + +[AILERON_RIGHT_DEFLECTION_PCT] +Id = AileronRightDeflectionPct +CategoryId = Controls +SimVarName = "AILERON RIGHT DEFLECTION PCT" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Percent deflection" + +[AILERON_AVERAGE_DEFLECTION] +Id = AileronAverageDeflection +CategoryId = Controls +SimVarName = "AILERON AVERAGE DEFLECTION" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Angle deflection" + +[AILERON_TRIM] +Id = AileronTrim +CategoryId = Controls +SimVarName = "AILERON TRIM" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Angle deflection" + +[AILERON_TRIM_PCT] +Id = AileronTrimPct +CategoryId = Controls +SimVarName = "AILERON TRIM PCT" +Unit = "Percent over 100" +CanSet = True +Indexed = False +Description = "The trim position of the ailerons. Zero is fully retracted." + +[RUDDER_DEFLECTION] +Id = RudderDeflection +CategoryId = Controls +SimVarName = "RUDDER DEFLECTION" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Angle deflection" + +[RUDDER_DEFLECTION_PCT] +Id = RudderDeflectionPct +CategoryId = Controls +SimVarName = "RUDDER DEFLECTION PCT" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Percent deflection" + +[RUDDER_TRIM] +Id = RudderTrim +CategoryId = Controls +SimVarName = "RUDDER TRIM" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Angle deflection" + +[RUDDER_TRIM_PCT] +Id = RudderTrimPct +CategoryId = Controls +SimVarName = "RUDDER TRIM PCT" +Unit = "Percent over 100" +CanSet = True +Indexed = False +Description = "The trim position of the rudder. Zero is no trim." + +[FLAPS_AVAILABLE] +Id = FlapsAvailable +CategoryId = Controls +SimVarName = "FLAPS AVAILABLE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if flaps available" + +[FLAP_DAMAGE_BY_SPEED] +Id = FlapDamageBySpeed +CategoryId = Controls +SimVarName = "FLAP DAMAGE BY SPEED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if flagps are damaged by excessive speed" + +[FLAP_SPEED_EXCEEDED] +Id = FlapSpeedExceeded +CategoryId = Controls +SimVarName = "FLAP SPEED EXCEEDED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if safe speed limit for flaps exceeded" + +[ELEVATOR_DEFLECTION] +Id = ElevatorDeflection +CategoryId = Controls +SimVarName = "ELEVATOR DEFLECTION" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Angle deflection" + +[ELEVATOR_DEFLECTION_PCT] +Id = ElevatorDeflectionPct +CategoryId = Controls +SimVarName = "ELEVATOR DEFLECTION PCT" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Percent deflection" + +[ALTERNATE_STATIC_SOURCE_OPEN] +Id = AlternateStaticSourceOpen +CategoryId = Controls +SimVarName = "ALTERNATE STATIC SOURCE OPEN" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Alternate static air source" + +[FOLDING_WING_HANDLE_POSITION] +Id = FoldingWingHandlePosition +CategoryId = Controls +SimVarName = "FOLDING WING HANDLE POSITION" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the folding wing handle is engaged." + +[FUEL_DUMP_SWITCH] +Id = FuelDumpSwitch +CategoryId = Controls +SimVarName = "FUEL DUMP SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "If true the aircraft is dumping fuel at the rate set in the configuration file." + + + +# Autopilot ####################### +# +[category_Autopilot] + +[AUTOPILOT_AVAILABLE] +Id = AutopilotAvailable +CategoryId = Autopilot +SimVarName = "AUTOPILOT AVAILABLE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Available flag" + +[AUTOPILOT_MASTER] +Id = AutopilotMaster +CategoryId = Autopilot +SimVarName = "AUTOPILOT MASTER" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "On/off flag" + +[AUTOPILOT_NAV_SELECTED] +Id = AutopilotNavSelected +CategoryId = Autopilot +SimVarName = "AUTOPILOT NAV SELECTED" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Index of Nav radio selected" + +[AUTOPILOT_WING_LEVELER] +Id = AutopilotWingLeveler +CategoryId = Autopilot +SimVarName = "AUTOPILOT WING LEVELER" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Wing leveler active" + +[AUTOPILOT_NAV1_LOCK] +Id = AutopilotNav1Lock +CategoryId = Autopilot +SimVarName = "AUTOPILOT NAV1 LOCK" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if autopilot nav1 lock applied" + +[AUTOPILOT_HEADING_LOCK] +Id = AutopilotHeadingLock +CategoryId = Autopilot +SimVarName = "AUTOPILOT HEADING LOCK" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Heading mode active" + +[AUTOPILOT_HEADING_LOCK_DIR] +Id = AutopilotHeadingLockDir +CategoryId = Autopilot +SimVarName = "AUTOPILOT HEADING LOCK DIR" +Unit = "Degrees" +CanSet = False +Indexed = False +Description = "Selected heading" + +[AUTOPILOT_ALTITUDE_LOCK] +Id = AutopilotAltitudeLock +CategoryId = Autopilot +SimVarName = "AUTOPILOT ALTITUDE LOCK" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Altitude hole active" + +[AUTOPILOT_ALTITUDE_LOCK_VAR] +Id = AutopilotAltitudeLockVar +CategoryId = Autopilot +SimVarName = "AUTOPILOT ALTITUDE LOCK VAR" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "Selected altitude" + +[AUTOPILOT_ATTITUDE_HOLD] +Id = AutopilotAttitudeHold +CategoryId = Autopilot +SimVarName = "AUTOPILOT ATTITUDE HOLD" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Attitude hold active" + +[AUTOPILOT_GLIDESLOPE_HOLD] +Id = AutopilotGlideslopeHold +CategoryId = Autopilot +SimVarName = "AUTOPILOT GLIDESLOPE HOLD" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "GS hold active" + +[AUTOPILOT_PITCH_HOLD_REF] +Id = AutopilotPitchHoldRef +CategoryId = Autopilot +SimVarName = "AUTOPILOT PITCH HOLD REF" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Current reference pitch" + +[AUTOPILOT_APPROACH_HOLD] +Id = AutopilotApproachHold +CategoryId = Autopilot +SimVarName = "AUTOPILOT APPROACH HOLD" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Approach mode active" + +[AUTOPILOT_BACKCOURSE_HOLD] +Id = AutopilotBackcourseHold +CategoryId = Autopilot +SimVarName = "AUTOPILOT BACKCOURSE HOLD" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Back course mode active" + +[AUTOPILOT_VERTICAL_HOLD_VAR] +Id = AutopilotVerticalHoldVar +CategoryId = Autopilot +SimVarName = "AUTOPILOT VERTICAL HOLD VAR" +Unit = "Feet/minute" +CanSet = False +Indexed = False +Description = "Selected vertical speed" + +[AUTOPILOT_PITCH_HOLD] +Id = AutopilotPitchHold +CategoryId = Autopilot +SimVarName = "AUTOPILOT PITCH HOLD" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Set to True if the autopilot pitch hold has is engaged." + +[AUTOPILOT_FLIGHT_DIRECTOR_ACTIVE] +Id = AutopilotFlightDirectorActive +CategoryId = Autopilot +SimVarName = "AUTOPILOT FLIGHT DIRECTOR ACTIVE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Flight director active" + +[AUTOPILOT_FLIGHT_DIRECTOR_PITCH] +Id = AutopilotFlightDirectorPitch +CategoryId = Autopilot +SimVarName = "AUTOPILOT FLIGHT DIRECTOR PITCH" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Reference pitch angle" + +[AUTOPILOT_FLIGHT_DIRECTOR_BANK] +Id = AutopilotFlightDirectorBank +CategoryId = Autopilot +SimVarName = "AUTOPILOT FLIGHT DIRECTOR BANK" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Reference bank angle" + +[AUTOPILOT_AIRSPEED_HOLD] +Id = AutopilotAirspeedHold +CategoryId = Autopilot +SimVarName = "AUTOPILOT AIRSPEED HOLD" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Airspeed hold active" + +[AUTOPILOT_AIRSPEED_HOLD_VAR] +Id = AutopilotAirspeedHoldVar +CategoryId = Autopilot +SimVarName = "AUTOPILOT AIRSPEED HOLD VAR" +Unit = "Knots" +CanSet = False +Indexed = False +Description = "Selected airspeed" + +[AUTOPILOT_MACH_HOLD] +Id = AutopilotMachHold +CategoryId = Autopilot +SimVarName = "AUTOPILOT MACH HOLD" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Mach hold active" + +[AUTOPILOT_MACH_HOLD_VAR] +Id = AutopilotMachHoldVar +CategoryId = Autopilot +SimVarName = "AUTOPILOT MACH HOLD VAR" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Selected mach" + +[AUTOPILOT_YAW_DAMPER] +Id = AutopilotYawDamper +CategoryId = Autopilot +SimVarName = "AUTOPILOT YAW DAMPER" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Yaw damper active" + +[AUTOPILOT_RPM_HOLD_VAR] +Id = AutopilotRpmHoldVar +CategoryId = Autopilot +SimVarName = "AUTOPILOT RPM HOLD VAR" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Selected rpm" + +[AUTOPILOT_THROTTLE_ARM] +Id = AutopilotThrottleArm +CategoryId = Autopilot +SimVarName = "AUTOPILOT THROTTLE ARM" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Autothrottle armed" + +[AUTOPILOT_TAKEOFF_POWER_ACTIVE] +Id = AutopilotTakeoffPowerActive +CategoryId = Autopilot +SimVarName = "AUTOPILOT TAKEOFF POWER ACTIVE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Takeoff / Go Around power mode active" + +[AUTOTHROTTLE_ACTIVE] +Id = AutothrottleActive +CategoryId = Autopilot +SimVarName = "AUTOTHROTTLE ACTIVE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Auto-throttle active" + +[AUTOPILOT_VERTICAL_HOLD] +Id = AutopilotVerticalHold +CategoryId = Autopilot +SimVarName = "AUTOPILOT VERTICAL HOLD" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if autopilot vertical hold applied" + +[AUTOPILOT_RPM_HOLD] +Id = AutopilotRpmHold +CategoryId = Autopilot +SimVarName = "AUTOPILOT RPM HOLD" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if autopilot rpm hold applied" + +[AUTOPILOT_MAX_BANK] +Id = AutopilotMaxBank +CategoryId = Autopilot +SimVarName = "AUTOPILOT MAX BANK" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "True if autopilot max bank applied" + +[FLY_BY_WIRE_ELAC_SWITCH] +Id = FlyByWireElacSwitch +CategoryId = Autopilot +SimVarName = "FLY BY WIRE ELAC SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the fly by wire Elevators and Ailerons computer is on." + +[FLY_BY_WIRE_FAC_SWITCH] +Id = FlyByWireFacSwitch +CategoryId = Autopilot +SimVarName = "FLY BY WIRE FAC SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the fly by wire Flight Augmentation computer is on." + +[FLY_BY_WIRE_SEC_SWITCH] +Id = FlyByWireSecSwitch +CategoryId = Autopilot +SimVarName = "FLY BY WIRE SEC SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the fly by wire Spoilers and Elevators computer is on." + +[FLY_BY_WIRE_ELAC_FAILED] +Id = FlyByWireElacFailed +CategoryId = Autopilot +SimVarName = "FLY BY WIRE ELAC FAILED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the Elevators and Ailerons computer has failed." + +[FLY_BY_WIRE_FAC_FAILED] +Id = FlyByWireFacFailed +CategoryId = Autopilot +SimVarName = "FLY BY WIRE FAC FAILED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the Flight Augmentation computer has failed." + +[FLY_BY_WIRE_SEC_FAILED] +Id = FlyByWireSecFailed +CategoryId = Autopilot +SimVarName = "FLY BY WIRE SEC FAILED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the Spoilers and Elevators computer has failed." + +[AUTOPILOT_FLIGHT_LEVEL_CHANGE] +Id = AutopilotFlightLevelChange +CategoryId = Autopilot +SimVarName = "AUTOPILOT FLIGHT LEVEL CHANGE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if autopilot FLC mode applied" + + + +# LandingGear ####################### +# +[category_LandingGear] + +[IS_GEAR_RETRACTABLE] +Id = IsGearRetractable +CategoryId = LandingGear +SimVarName = "IS GEAR RETRACTABLE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if gear can be retracted" + +[IS_GEAR_SKIS] +Id = IsGearSkis +CategoryId = LandingGear +SimVarName = "IS GEAR SKIS" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if landing gear is skis" + +[IS_GEAR_FLOATS] +Id = IsGearFloats +CategoryId = LandingGear +SimVarName = "IS GEAR FLOATS" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if landing gear is floats" + +[IS_GEAR_SKIDS] +Id = IsGearSkids +CategoryId = LandingGear +SimVarName = "IS GEAR SKIDS" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if landing gear is skids" + +[IS_GEAR_WHEELS] +Id = IsGearWheels +CategoryId = LandingGear +SimVarName = "IS GEAR WHEELS" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if landing gear is wheels" + +[GEAR_HANDLE_POSITION] +Id = GearHandlePosition +CategoryId = LandingGear +SimVarName = "GEAR HANDLE POSITION" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "True if gear handle is applied" + +[GEAR_HYDRAULIC_PRESSURE] +Id = GearHydraulicPressure +CategoryId = LandingGear +SimVarName = "GEAR HYDRAULIC PRESSURE" +Unit = "psf" +CanSet = False +Indexed = False +Description = "Gear hydraulic pressure" + +[TAILWHEEL_LOCK_ON] +Id = TailwheelLockOn +CategoryId = LandingGear +SimVarName = "TAILWHEEL LOCK ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if tailwheel lock applied" + +[GEAR_CENTER_POSITION] +Id = GearCenterPosition +CategoryId = LandingGear +SimVarName = "GEAR CENTER POSITION" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent center gear extended" + +[GEAR_LEFT_POSITION] +Id = GearLeftPosition +CategoryId = LandingGear +SimVarName = "GEAR LEFT POSITION" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent left gear extended" + +[GEAR_RIGHT_POSITION] +Id = GearRightPosition +CategoryId = LandingGear +SimVarName = "GEAR RIGHT POSITION" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent right gear extended" + +[GEAR_TAIL_POSITION] +Id = GearTailPosition +CategoryId = LandingGear +SimVarName = "GEAR TAIL POSITION" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Percent tail gear extended" + +[GEAR_AUX_POSITION] +Id = GearAuxPosition +CategoryId = LandingGear +SimVarName = "GEAR AUX POSITION" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Percent auxiliary gear extended" + +[GEAR_POSITION] +Id = GearPosition +CategoryId = LandingGear +SimVarName = "GEAR POSITION" +Unit = "Enum" +CanSet = True +Indexed = True +Description = "Position of landing gear:; 0 = unknown; 1 = up; 2 = down" + +[GEAR_ANIMATION_POSITION] +Id = GearAnimationPosition +CategoryId = LandingGear +SimVarName = "GEAR ANIMATION POSITION" +Unit = "Number" +CanSet = False +Indexed = True +Description = "Percent gear animation extended" + +[GEAR_TOTAL_PCT_EXTENDED] +Id = GearTotalPctExtended +CategoryId = LandingGear +SimVarName = "GEAR TOTAL PCT EXTENDED" +Unit = "Percentage" +CanSet = False +Indexed = False +Description = "Percent total gear extended" + +[AUTO_BRAKE_SWITCH_CB] +Id = AutoBrakeSwitchCb +CategoryId = LandingGear +SimVarName = "AUTO BRAKE SWITCH CB" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Auto brake switch position" + +[WATER_RUDDER_HANDLE_POSITION] +Id = WaterRudderHandlePosition +CategoryId = LandingGear +SimVarName = "WATER RUDDER HANDLE POSITION" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Position of the water rudder handle (0 handle retracted, 100 rudder handle applied)" + +[WATER_LEFT_RUDDER_EXTENDED] +Id = WaterLeftRudderExtended +CategoryId = LandingGear +SimVarName = "WATER LEFT RUDDER EXTENDED" +Unit = "Percentage" +CanSet = False +Indexed = False +Description = "Percent extended" + +[WATER_RIGHT_RUDDER_EXTENDED] +Id = WaterRightRudderExtended +CategoryId = LandingGear +SimVarName = "WATER RIGHT RUDDER EXTENDED" +Unit = "Percentage" +CanSet = False +Indexed = False +Description = "Percent extended" + +[GEAR_CENTER_STEER_ANGLE] +Id = GearCenterSteerAngle +CategoryId = LandingGear +SimVarName = "GEAR CENTER STEER ANGLE" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Center wheel angle, negative to the left, positive to the right." + +[GEAR_LEFT_STEER_ANGLE] +Id = GearLeftSteerAngle +CategoryId = LandingGear +SimVarName = "GEAR LEFT STEER ANGLE" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Left wheel angle, negative to the left, positive to the right." + +[GEAR_RIGHT_STEER_ANGLE] +Id = GearRightSteerAngle +CategoryId = LandingGear +SimVarName = "GEAR RIGHT STEER ANGLE" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Right wheel angle, negative to the left, positive to the right." + +[GEAR_AUX_STEER_ANGLE] +Id = GearAuxSteerAngle +CategoryId = LandingGear +SimVarName = "GEAR AUX STEER ANGLE" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Aux wheel angle, negative to the left, positive to the right. The aux wheel is the fourth set of gear, sometimes used on helicopters." + +[GEAR_STEER_ANGLE] +Id = GearSteerAngle +CategoryId = LandingGear +SimVarName = "GEAR STEER ANGLE" +Unit = "Percent Over 100" +CanSet = False +Indexed = True +Description = "Alternative method of getting the steer angle. Index is; 0 = center; 1 = left; 2 = right; 3 = aux" + +[WATER_LEFT_RUDDER_STEER_ANGLE] +Id = WaterLeftRudderSteerAngle +CategoryId = LandingGear +SimVarName = "WATER LEFT RUDDER STEER ANGLE" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Water left rudder angle, negative to the left, positive to the right." + +[WATER_RIGHT_RUDDER_STEER_ANGLE] +Id = WaterRightRudderSteerAngle +CategoryId = LandingGear +SimVarName = "WATER RIGHT RUDDER STEER ANGLE" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Water right rudder angle, negative to the left, positive to the right." + +[GEAR_CENTER_STEER_ANGLE_PCT] +Id = GearCenterSteerAnglePct +CategoryId = LandingGear +SimVarName = "GEAR CENTER STEER ANGLE PCT" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Center steer angle as a percentage" + +[GEAR_LEFT_STEER_ANGLE_PCT] +Id = GearLeftSteerAnglePct +CategoryId = LandingGear +SimVarName = "GEAR LEFT STEER ANGLE PCT" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Left steer angle as a percentage" + +[GEAR_RIGHT_STEER_ANGLE_PCT] +Id = GearRightSteerAnglePct +CategoryId = LandingGear +SimVarName = "GEAR RIGHT STEER ANGLE PCT" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Right steer angle as a percentage" + +[GEAR_AUX_STEER_ANGLE_PCT] +Id = GearAuxSteerAnglePct +CategoryId = LandingGear +SimVarName = "GEAR AUX STEER ANGLE PCT" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Aux steer angle as a percentage" + +[GEAR_STEER_ANGLE_PCT] +Id = GearSteerAnglePct +CategoryId = LandingGear +SimVarName = "GEAR STEER ANGLE PCT" +Unit = "Percent Over 100" +CanSet = False +Indexed = True +Description = "Alternative method of getting steer angle as a percentage. Index is; 0 = center; 1 = left; 2 = right; 3 = aux" + +[WATER_LEFT_RUDDER_STEER_ANGLE_PCT] +Id = WaterLeftRudderSteerAnglePct +CategoryId = LandingGear +SimVarName = "WATER LEFT RUDDER STEER ANGLE PCT" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Water left rudder angle as a percentage" + +[WATER_RIGHT_RUDDER_STEER_ANGLE_PCT] +Id = WaterRightRudderSteerAnglePct +CategoryId = LandingGear +SimVarName = "WATER RIGHT RUDDER STEER ANGLE PCT" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Water right rudder as a percentage" + +[WHEEL_RPM] +Id = WheelRpm +CategoryId = LandingGear +SimVarName = "WHEEL RPM" +Unit = "Rpm" +CanSet = False +Indexed = True +Description = "Wheel rpm. Index is; 0 = center; 1 = left; 2 = right; 3 = aux" + +[CENTER_WHEEL_RPM] +Id = CenterWheelRpm +CategoryId = LandingGear +SimVarName = "CENTER WHEEL RPM" +Unit = "Rpm" +CanSet = False +Indexed = False +Description = "Center landing gear rpm" + +[LEFT_WHEEL_RPM] +Id = LeftWheelRpm +CategoryId = LandingGear +SimVarName = "LEFT WHEEL RPM" +Unit = "Rpm" +CanSet = False +Indexed = False +Description = "Left landing gear rpm" + +[RIGHT_WHEEL_RPM] +Id = RightWheelRpm +CategoryId = LandingGear +SimVarName = "RIGHT WHEEL RPM" +Unit = "Rpm" +CanSet = False +Indexed = False +Description = "Right landing gear rpm" + +[AUX_WHEEL_RPM] +Id = AuxWheelRpm +CategoryId = LandingGear +SimVarName = "AUX WHEEL RPM" +Unit = "Rpm" +CanSet = False +Indexed = False +Description = "Rpm of fourth set of gear wheels." + +[WHEEL_ROTATION_ANGLE] +Id = WheelRotationAngle +CategoryId = LandingGear +SimVarName = "WHEEL ROTATION ANGLE" +Unit = "Radians" +CanSet = False +Indexed = True +Description = "Wheel rotation angle. Index is; 0 = center; 1 = left; 2 = right; 3 = aux" + +[CENTER_WHEEL_ROTATION_ANGLE] +Id = CenterWheelRotationAngle +CategoryId = LandingGear +SimVarName = "CENTER WHEEL ROTATION ANGLE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Center wheel rotation angle" + +[LEFT_WHEEL_ROTATION_ANGLE] +Id = LeftWheelRotationAngle +CategoryId = LandingGear +SimVarName = "LEFT WHEEL ROTATION ANGLE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Left wheel rotation angle" + +[RIGHT_WHEEL_ROTATION_ANGLE] +Id = RightWheelRotationAngle +CategoryId = LandingGear +SimVarName = "RIGHT WHEEL ROTATION ANGLE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Right wheel rotation angle" + +[AUX_WHEEL_ROTATION_ANGLE] +Id = AuxWheelRotationAngle +CategoryId = LandingGear +SimVarName = "AUX WHEEL ROTATION ANGLE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Aux wheel rotation angle" + +[GEAR_EMERGENCY_HANDLE_POSITION] +Id = GearEmergencyHandlePosition +CategoryId = LandingGear +SimVarName = "GEAR EMERGENCY HANDLE POSITION" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if gear emergency handle applied" + +[GEAR_WARNING] +Id = GearWarning +CategoryId = LandingGear +SimVarName = "GEAR WARNING" +Unit = "Enum" +CanSet = False +Indexed = False +Description = "One of:; 0: unknown; 1: normal; 2: amphib" + +[ANTISKID_BRAKES_ACTIVE] +Id = AntiskidBrakesActive +CategoryId = LandingGear +SimVarName = "ANTISKID BRAKES ACTIVE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if antiskid brakes active" + +[RETRACT_FLOAT_SWITCH] +Id = RetractFloatSwitch +CategoryId = LandingGear +SimVarName = "RETRACT FLOAT SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if retract float switch on" + +[RETRACT_LEFT_FLOAT_EXTENDED] +Id = RetractLeftFloatExtended +CategoryId = LandingGear +SimVarName = "RETRACT LEFT FLOAT EXTENDED" +Unit = "Percent" +CanSet = False +Indexed = False +Description = "If aircraft has retractable floats." + +[RETRACT_RIGHT_FLOAT_EXTENDED] +Id = RetractRightFloatExtended +CategoryId = LandingGear +SimVarName = "RETRACT RIGHT FLOAT EXTENDED" +Unit = "Percent" +CanSet = False +Indexed = False +Description = "If aircraft has retractable floats." + +[STEER_INPUT_CONTROL] +Id = SteerInputControl +CategoryId = LandingGear +SimVarName = "STEER INPUT CONTROL" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Position of steering tiller" + +[GEAR_DAMAGE_BY_SPEED] +Id = GearDamageBySpeed +CategoryId = LandingGear +SimVarName = "GEAR DAMAGE BY SPEED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if gear has been damaged by excessive speed" + +[GEAR_SPEED_EXCEEDED] +Id = GearSpeedExceeded +CategoryId = LandingGear +SimVarName = "GEAR SPEED EXCEEDED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if safe speed limit for gear exceeded" + +[NOSEWHEEL_LOCK_ON] +Id = NosewheelLockOn +CategoryId = LandingGear +SimVarName = "NOSEWHEEL LOCK ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the nosewheel lock is engaged." + + + +# Environment ####################### +# +[category_Environment] + +[AMBIENT_DENSITY] +Id = AmbientDensity +CategoryId = Environment +SimVarName = "AMBIENT DENSITY" +Unit = "Slugs per cubic feet" +CanSet = False +Indexed = False +Description = "Ambient density" + +[AMBIENT_TEMPERATURE] +Id = AmbientTemperature +CategoryId = Environment +SimVarName = "AMBIENT TEMPERATURE" +Unit = "Celsius" +CanSet = False +Indexed = False +Description = "Ambient temperature" + +[AMBIENT_PRESSURE] +Id = AmbientPressure +CategoryId = Environment +SimVarName = "AMBIENT PRESSURE" +Unit = "inHg" +CanSet = False +Indexed = False +Description = "Ambient pressure" + +[AMBIENT_WIND_VELOCITY] +Id = AmbientWindVelocity +CategoryId = Environment +SimVarName = "AMBIENT WIND VELOCITY" +Unit = "Knots" +CanSet = False +Indexed = False +Description = "Wind velocity" + +[AMBIENT_WIND_DIRECTION] +Id = AmbientWindDirection +CategoryId = Environment +SimVarName = "AMBIENT WIND DIRECTION" +Unit = "Degrees" +CanSet = False +Indexed = False +Description = "Wind direction" + +[AMBIENT_WIND_X] +Id = AmbientWindX +CategoryId = Environment +SimVarName = "AMBIENT WIND X" +Unit = "Meters per second" +CanSet = False +Indexed = False +Description = "Wind component in East/West direction." + +[AMBIENT_WIND_Y] +Id = AmbientWindY +CategoryId = Environment +SimVarName = "AMBIENT WIND Y" +Unit = "Meters per second" +CanSet = False +Indexed = False +Description = "Wind component in vertical direction." + +[AMBIENT_WIND_Z] +Id = AmbientWindZ +CategoryId = Environment +SimVarName = "AMBIENT WIND Z" +Unit = "Meters per second" +CanSet = False +Indexed = False +Description = "Wind component in North/South direction." + +[STRUCT_AMBIENT_WIND] +Id = StructAmbientWind +CategoryId = Environment +SimVarName = "STRUCT AMBIENT WIND" +Unit = "Feet per second" +CanSet = False +Indexed = False +Description = "X (latitude), Y (vertical) and Z (longitude) components of the wind." + +[AIRCRAFT_WIND_X] +Id = AircraftWindX +CategoryId = Environment +SimVarName = "AIRCRAFT WIND X" +Unit = "Knots" +CanSet = False +Indexed = False +Description = "Wind component in aircraft lateral axis" + +[AIRCRAFT_WIND_Y] +Id = AircraftWindY +CategoryId = Environment +SimVarName = "AIRCRAFT WIND Y" +Unit = "Knots" +CanSet = False +Indexed = False +Description = "Wind component in aircraft vertical axis" + +[AIRCRAFT_WIND_Z] +Id = AircraftWindZ +CategoryId = Environment +SimVarName = "AIRCRAFT WIND Z" +Unit = "Knots" +CanSet = False +Indexed = False +Description = "Wind component in aircraft longitudinal axis" + +[BAROMETER_PRESSURE] +Id = BarometerPressure +CategoryId = Environment +SimVarName = "BAROMETER PRESSURE" +Unit = "Millibars" +CanSet = False +Indexed = False +Description = "Barometric pressure" + +[SEA_LEVEL_PRESSURE] +Id = SeaLevelPressure +CategoryId = Environment +SimVarName = "SEA LEVEL PRESSURE" +Unit = "Millibars" +CanSet = False +Indexed = False +Description = "Barometric pressure at sea level" + +[TOTAL_AIR_TEMPERATURE] +Id = TotalAirTemperature +CategoryId = Environment +SimVarName = "TOTAL AIR TEMPERATURE" +Unit = "Celsius" +CanSet = False +Indexed = False +Description = "Total air temperature is the air temperature at the front of the aircraft where the ram pressure from the speed of the aircraft is taken into account." + +[WINDSHIELD_RAIN_EFFECT_AVAILABLE] +Id = WindshieldRainEffectAvailable +CategoryId = Environment +SimVarName = "WINDSHIELD RAIN EFFECT AVAILABLE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is visual effect available on this aircraft" + +[AMBIENT_IN_CLOUD] +Id = AmbientInCloud +CategoryId = Environment +SimVarName = "AMBIENT IN CLOUD" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the aircraft is in a cloud." + +[AMBIENT_VISIBILITY] +Id = AmbientVisibility +CategoryId = Environment +SimVarName = "AMBIENT VISIBILITY" +Unit = "Meters" +CanSet = False +Indexed = False +Description = "Ambient visibility" + +[STANDARD_ATM_TEMPERATURE] +Id = StandardAtmTemperature +CategoryId = Environment +SimVarName = "STANDARD ATM TEMPERATURE" +Unit = "Rankine" +CanSet = False +Indexed = False +Description = "Outside temperature on the standard ATM scale" + + + +# HelicopterSpecific ####################### +# +[category_HelicopterSpecific] + +[ROTOR_BRAKE_HANDLE_POS] +Id = RotorBrakeHandlePos +CategoryId = HelicopterSpecific +SimVarName = "ROTOR BRAKE HANDLE POS" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Percent actuated" + +[ROTOR_BRAKE_ACTIVE] +Id = RotorBrakeActive +CategoryId = HelicopterSpecific +SimVarName = "ROTOR BRAKE ACTIVE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Active" + +[ROTOR_CLUTCH_SWITCH_POS] +Id = RotorClutchSwitchPos +CategoryId = HelicopterSpecific +SimVarName = "ROTOR CLUTCH SWITCH POS" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Switch position" + +[ROTOR_CLUTCH_ACTIVE] +Id = RotorClutchActive +CategoryId = HelicopterSpecific +SimVarName = "ROTOR CLUTCH ACTIVE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Active" + +[ROTOR_TEMPERATURE] +Id = RotorTemperature +CategoryId = HelicopterSpecific +SimVarName = "ROTOR TEMPERATURE" +Unit = "Rankine" +CanSet = False +Indexed = False +Description = "Main rotor transmission temperature" + +[ROTOR_CHIP_DETECTED] +Id = RotorChipDetected +CategoryId = HelicopterSpecific +SimVarName = "ROTOR CHIP DETECTED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Chip detection" + +[ROTOR_GOV_SWITCH_POS] +Id = RotorGovSwitchPos +CategoryId = HelicopterSpecific +SimVarName = "ROTOR GOV SWITCH POS" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Switch position" + +[ROTOR_GOV_ACTIVE] +Id = RotorGovActive +CategoryId = HelicopterSpecific +SimVarName = "ROTOR GOV ACTIVE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Active" + +[ROTOR_LATERAL_TRIM_PCT] +Id = RotorLateralTrimPct +CategoryId = HelicopterSpecific +SimVarName = "ROTOR LATERAL TRIM PCT" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Trim percent" + +[ROTOR_RPM_PCT] +Id = RotorRpmPct +CategoryId = HelicopterSpecific +SimVarName = "ROTOR RPM PCT" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Percent max rated rpm" + +[ENG_TORQUE_PERCENT] +Id = EngTorquePercent +CategoryId = HelicopterSpecific +SimVarName = "ENG TORQUE PERCENT" +Unit = "Percent scaler 16K" +CanSet = False +Indexed = True +Description = "Torque. Returns main rotor torque for Bell helicopter, or the indexed rotor torque of other helicopters." + +[ENG_FUEL_PRESSURE] +Id = EngFuelPressure +CategoryId = HelicopterSpecific +SimVarName = "ENG FUEL PRESSURE" +Unit = "PSI" +CanSet = False +Indexed = False +Description = "Fuel pressure. Applies only to Bell helicopter." + +[ENG_ROTOR_RPM] +Id = EngRotorRpm +CategoryId = HelicopterSpecific +SimVarName = "ENG ROTOR RPM" +Unit = "percent scaler 16k" +CanSet = False +Indexed = True +Description = "Rotor rpm. Returns main rotor rpm for Bell helicopter, or the indexed rotor rpm of other helicopters." + +[COLLECTIVE_POSITION] +Id = CollectivePosition +CategoryId = HelicopterSpecific +SimVarName = "COLLECTIVE POSITION" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "The position of the helicopter's collective. 0 is fully up, 100 fully depressed." + + + +# MiscellaneousSystems ####################### +# +[category_MiscellaneousSystems] + +[SMOKE_ENABLE] +Id = SmokeEnable +CategoryId = MiscellaneousSystems +SimVarName = "SMOKE ENABLE" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "Set to True to activate the smoke system, if one is available (for example, on the Extra)." + +[SMOKESYSTEM_AVAILABLE] +Id = SmokesystemAvailable +CategoryId = MiscellaneousSystems +SimVarName = "SMOKESYSTEM AVAILABLE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Smoke system available" + +[PITOT_HEAT] +Id = PitotHeat +CategoryId = MiscellaneousSystems +SimVarName = "PITOT HEAT" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Pitot heat active" + +[FOLDING_WING_LEFT_PERCENT] +Id = FoldingWingLeftPercent +CategoryId = MiscellaneousSystems +SimVarName = "FOLDING WING LEFT PERCENT" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Left folding wing position, 100 is fully folded" + +[FOLDING_WING_RIGHT_PERCENT] +Id = FoldingWingRightPercent +CategoryId = MiscellaneousSystems +SimVarName = "FOLDING WING RIGHT PERCENT" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Right folding wing position, 100 is fully folded" + +[CANOPY_OPEN] +Id = CanopyOpen +CategoryId = MiscellaneousSystems +SimVarName = "CANOPY OPEN" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent primary door/exit open" + +[TAILHOOK_POSITION] +Id = TailhookPosition +CategoryId = MiscellaneousSystems +SimVarName = "TAILHOOK POSITION" +Unit = "Percent Over 100" +CanSet = True +Indexed = False +Description = "Percent tail hook extended" + +[EXIT_OPEN] +Id = ExitOpen +CategoryId = MiscellaneousSystems +SimVarName = "EXIT OPEN" +Unit = "Percent Over 100" +CanSet = True +Indexed = True +Description = "Percent door/exit open" + +[STALL_HORN_AVAILABLE] +Id = StallHornAvailable +CategoryId = MiscellaneousSystems +SimVarName = "STALL HORN AVAILABLE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if stall alarm available" + +[CARB_HEAT_AVAILABLE] +Id = CarbHeatAvailable +CategoryId = MiscellaneousSystems +SimVarName = "CARB HEAT AVAILABLE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if carb heat available" + +[SPOILER_AVAILABLE] +Id = SpoilerAvailable +CategoryId = MiscellaneousSystems +SimVarName = "SPOILER AVAILABLE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if spoiler system available" + +[IS_TAIL_DRAGGER] +Id = IsTailDragger +CategoryId = MiscellaneousSystems +SimVarName = "IS TAIL DRAGGER" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the aircraft is a taildragger" + +[STROBES_AVAILABLE] +Id = StrobesAvailable +CategoryId = MiscellaneousSystems +SimVarName = "STROBES AVAILABLE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if strobe lights are available" + +[TOE_BRAKES_AVAILABLE] +Id = ToeBrakesAvailable +CategoryId = MiscellaneousSystems +SimVarName = "TOE BRAKES AVAILABLE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if toe brakes are available" + +[PUSHBACK_STATE] +Id = PushbackState +CategoryId = MiscellaneousSystems +SimVarName = "PUSHBACK STATE" +Unit = "Enum" +CanSet = True +Indexed = False +Description = "Type of pushback :; 0 = Straight; 1 = Left; 2 = Right" + +[ELECTRICAL_MASTER_BATTERY] +Id = ElectricalMasterBattery +CategoryId = MiscellaneousSystems +SimVarName = "ELECTRICAL MASTER BATTERY" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "Battery switch position" + +[ELECTRICAL_TOTAL_LOAD_AMPS] +Id = ElectricalTotalLoadAmps +CategoryId = MiscellaneousSystems +SimVarName = "ELECTRICAL TOTAL LOAD AMPS" +Unit = "Amperes" +CanSet = True +Indexed = False +Description = "Total load amps" + +[ELECTRICAL_BATTERY_LOAD] +Id = ElectricalBatteryLoad +CategoryId = MiscellaneousSystems +SimVarName = "ELECTRICAL BATTERY LOAD" +Unit = "Amperes" +CanSet = True +Indexed = False +Description = "Battery load" + +[ELECTRICAL_BATTERY_VOLTAGE] +Id = ElectricalBatteryVoltage +CategoryId = MiscellaneousSystems +SimVarName = "ELECTRICAL BATTERY VOLTAGE" +Unit = "Volts" +CanSet = True +Indexed = False +Description = "Battery voltage" + +[ELECTRICAL_MAIN_BUS_VOLTAGE] +Id = ElectricalMainBusVoltage +CategoryId = MiscellaneousSystems +SimVarName = "ELECTRICAL MAIN BUS VOLTAGE" +Unit = "Volts" +CanSet = True +Indexed = False +Description = "Main bus voltage" + +[ELECTRICAL_MAIN_BUS_AMPS] +Id = ElectricalMainBusAmps +CategoryId = MiscellaneousSystems +SimVarName = "ELECTRICAL MAIN BUS AMPS" +Unit = "Amperes" +CanSet = True +Indexed = False +Description = "Main bus current" + +[ELECTRICAL_AVIONICS_BUS_VOLTAGE] +Id = ElectricalAvionicsBusVoltage +CategoryId = MiscellaneousSystems +SimVarName = "ELECTRICAL AVIONICS BUS VOLTAGE" +Unit = "Volts" +CanSet = True +Indexed = False +Description = "Avionics bus voltage" + +[ELECTRICAL_AVIONICS_BUS_AMPS] +Id = ElectricalAvionicsBusAmps +CategoryId = MiscellaneousSystems +SimVarName = "ELECTRICAL AVIONICS BUS AMPS" +Unit = "Amperes" +CanSet = True +Indexed = False +Description = "Avionics bus current" + +[ELECTRICAL_HOT_BATTERY_BUS_VOLTAGE] +Id = ElectricalHotBatteryBusVoltage +CategoryId = MiscellaneousSystems +SimVarName = "ELECTRICAL HOT BATTERY BUS VOLTAGE" +Unit = "Volts" +CanSet = True +Indexed = False +Description = "Voltage available when battery switch is turned off" + +[ELECTRICAL_HOT_BATTERY_BUS_AMPS] +Id = ElectricalHotBatteryBusAmps +CategoryId = MiscellaneousSystems +SimVarName = "ELECTRICAL HOT BATTERY BUS AMPS" +Unit = "Amperes" +CanSet = True +Indexed = False +Description = "Current available when battery switch is turned off" + +[ELECTRICAL_BATTERY_BUS_VOLTAGE] +Id = ElectricalBatteryBusVoltage +CategoryId = MiscellaneousSystems +SimVarName = "ELECTRICAL BATTERY BUS VOLTAGE" +Unit = "Volts" +CanSet = True +Indexed = False +Description = "Battery bus voltage" + +[ELECTRICAL_BATTERY_BUS_AMPS] +Id = ElectricalBatteryBusAmps +CategoryId = MiscellaneousSystems +SimVarName = "ELECTRICAL BATTERY BUS AMPS" +Unit = "Amperes" +CanSet = True +Indexed = False +Description = "Battery bus current" + +[ELECTRICAL_GENALT_BUS_VOLTAGE] +Id = ElectricalGenaltBusVoltage +CategoryId = MiscellaneousSystems +SimVarName = "ELECTRICAL GENALT BUS VOLTAGE" +Unit = "Volts" +CanSet = True +Indexed = True +Description = "Genalt bus voltage (takes engine index)" + +[ELECTRICAL_GENALT_BUS_AMPS] +Id = ElectricalGenaltBusAmps +CategoryId = MiscellaneousSystems +SimVarName = "ELECTRICAL GENALT BUS AMPS" +Unit = "Amperes" +CanSet = True +Indexed = True +Description = "Genalt bus current (takes engine index)" + +[CIRCUIT_GENERAL_PANEL_ON] +Id = CircuitGeneralPanelOn +CategoryId = MiscellaneousSystems +SimVarName = "CIRCUIT GENERAL PANEL ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is electrical power available to this circuit" + +[CIRCUIT_FLAP_MOTOR_ON] +Id = CircuitFlapMotorOn +CategoryId = MiscellaneousSystems +SimVarName = "CIRCUIT FLAP MOTOR ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is electrical power available to this circuit" + +[CIRCUIT_GEAR_MOTOR_ON] +Id = CircuitGearMotorOn +CategoryId = MiscellaneousSystems +SimVarName = "CIRCUIT GEAR MOTOR ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is electrical power available to this circuit" + +[CIRCUIT_AUTOPILOT_ON] +Id = CircuitAutopilotOn +CategoryId = MiscellaneousSystems +SimVarName = "CIRCUIT AUTOPILOT ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is electrical power available to this circuit" + +[CIRCUIT_AVIONICS_ON] +Id = CircuitAvionicsOn +CategoryId = MiscellaneousSystems +SimVarName = "CIRCUIT AVIONICS ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is electrical power available to this circuit" + +[CIRCUIT_PITOT_HEAT_ON] +Id = CircuitPitotHeatOn +CategoryId = MiscellaneousSystems +SimVarName = "CIRCUIT PITOT HEAT ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is electrical power available to this circuit" + +[CIRCUIT_PROP_SYNC_ON] +Id = CircuitPropSyncOn +CategoryId = MiscellaneousSystems +SimVarName = "CIRCUIT PROP SYNC ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is electrical power available to this circuit" + +[CIRCUIT_AUTO_FEATHER_ON] +Id = CircuitAutoFeatherOn +CategoryId = MiscellaneousSystems +SimVarName = "CIRCUIT AUTO FEATHER ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is electrical power available to this circuit" + +[CIRCUIT_AUTO_BRAKES_ON] +Id = CircuitAutoBrakesOn +CategoryId = MiscellaneousSystems +SimVarName = "CIRCUIT AUTO BRAKES ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is electrical power available to this circuit" + +[CIRCUIT_STANDY_VACUUM_ON] +Id = CircuitStandyVacuumOn +CategoryId = MiscellaneousSystems +SimVarName = "CIRCUIT STANDY VACUUM ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is electrical power available to this circuit" + +[CIRCUIT_MARKER_BEACON_ON] +Id = CircuitMarkerBeaconOn +CategoryId = MiscellaneousSystems +SimVarName = "CIRCUIT MARKER BEACON ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is electrical power available to this circuit" + +[CIRCUIT_GEAR_WARNING_ON] +Id = CircuitGearWarningOn +CategoryId = MiscellaneousSystems +SimVarName = "CIRCUIT GEAR WARNING ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is electrical power available to this circuit" + +[CIRCUIT_HYDRAULIC_PUMP_ON] +Id = CircuitHydraulicPumpOn +CategoryId = MiscellaneousSystems +SimVarName = "CIRCUIT HYDRAULIC PUMP ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is electrical power available to this circuit" + +[HYDRAULIC_PRESSURE] +Id = HydraulicPressure +CategoryId = MiscellaneousSystems +SimVarName = "HYDRAULIC PRESSURE" +Unit = "psf" +CanSet = False +Indexed = True +Description = "Hydraulic system pressure. Indexes start at 1." + +[HYDRAULIC_RESERVOIR_PERCENT] +Id = HydraulicReservoirPercent +CategoryId = MiscellaneousSystems +SimVarName = "HYDRAULIC RESERVOIR PERCENT" +Unit = "Percent Over 100" +CanSet = True +Indexed = True +Description = "Hydraulic pressure changes will follow changes to this variable. Indexes start at 1." + +[HYDRAULIC_SYSTEM_INTEGRITY] +Id = HydraulicSystemIntegrity +CategoryId = MiscellaneousSystems +SimVarName = "HYDRAULIC SYSTEM INTEGRITY" +Unit = "Percent Over 100" +CanSet = False +Indexed = False +Description = "Percent system functional" + +[STRUCTURAL_DEICE_SWITCH] +Id = StructuralDeiceSwitch +CategoryId = MiscellaneousSystems +SimVarName = "STRUCTURAL DEICE SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the aircraft structure deice switch is on" + +[APPLY_HEAT_TO_SYSTEMS] +Id = ApplyHeatToSystems +CategoryId = MiscellaneousSystems +SimVarName = "APPLY HEAT TO SYSTEMS" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "Used when too close to a fire." + +[DROPPABLE_OBJECTS_TYPE] +Id = DroppableObjectsType +CategoryId = MiscellaneousSystems +SimVarName = "DROPPABLE OBJECTS TYPE" +Unit = "String" +CanSet = True +Indexed = True +Description = "The type of droppable object at the station number identified by the index." + +[DROPPABLE_OBJECTS_COUNT] +Id = DroppableObjectsCount +CategoryId = MiscellaneousSystems +SimVarName = "DROPPABLE OBJECTS COUNT" +Unit = "Number" +CanSet = False +Indexed = True +Description = "The number of droppable objects at the station number identified by the index." + + + +# Miscellaneous ####################### +# +[category_Miscellaneous] + +[TOTAL_WEIGHT] +Id = TotalWeight +CategoryId = Miscellaneous +SimVarName = "TOTAL WEIGHT" +Unit = "Pounds" +CanSet = False +Indexed = False +Description = "Total weight of the aircraft" + +[MAX_GROSS_WEIGHT] +Id = MaxGrossWeight +CategoryId = Miscellaneous +SimVarName = "MAX GROSS WEIGHT" +Unit = "Pounds" +CanSet = False +Indexed = False +Description = "Maximum gross weight of the aircaft" + +[EMPTY_WEIGHT] +Id = EmptyWeight +CategoryId = Miscellaneous +SimVarName = "EMPTY WEIGHT" +Unit = "Pounds" +CanSet = False +Indexed = False +Description = "Empty weight of the aircraft" + +[IS_USER_SIM] +Id = IsUserSim +CategoryId = Miscellaneous +SimVarName = "IS USER SIM" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Is this the user loaded aircraft" + +[SIM_DISABLED] +Id = SimDisabled +CategoryId = Miscellaneous +SimVarName = "SIM DISABLED" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "Is sim disabled" + +[G_FORCE] +Id = GForce +CategoryId = Miscellaneous +SimVarName = "G FORCE" +Unit = "GForce" +CanSet = True +Indexed = False +Description = "Current g force" + +[ATC_HEAVY] +Id = AtcHeavy +CategoryId = Miscellaneous +SimVarName = "ATC HEAVY" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "Is this aircraft recognized by ATC as heavy" + +[AUTO_COORDINATION] +Id = AutoCoordination +CategoryId = Miscellaneous +SimVarName = "AUTO COORDINATION" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "Is auto-coordination active" + +[REALISM] +Id = Realism +CategoryId = Miscellaneous +SimVarName = "REALISM" +Unit = "Number" +CanSet = True +Indexed = False +Description = "General realism percent" + +[TRUE_AIRSPEED_SELECTED] +Id = TrueAirspeedSelected +CategoryId = Miscellaneous +SimVarName = "TRUE AIRSPEED SELECTED" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "True if True Airspeed has been selected" + +[DESIGN_SPEED_VS0] +Id = DesignSpeedVs0 +CategoryId = Miscellaneous +SimVarName = "DESIGN SPEED VS0" +Unit = "Feet per second" +CanSet = False +Indexed = False +Description = "Design speed at VS0" + +[DESIGN_SPEED_VS1] +Id = DesignSpeedVs1 +CategoryId = Miscellaneous +SimVarName = "DESIGN SPEED VS1" +Unit = "Feet per second" +CanSet = False +Indexed = False +Description = "Design speed at VS1" + +[DESIGN_SPEED_VC] +Id = DesignSpeedVc +CategoryId = Miscellaneous +SimVarName = "DESIGN SPEED VC" +Unit = "Feet per second" +CanSet = False +Indexed = False +Description = "Design speed at VC" + +[MIN_DRAG_VELOCITY] +Id = MinDragVelocity +CategoryId = Miscellaneous +SimVarName = "MIN DRAG VELOCITY" +Unit = "Feet per second" +CanSet = False +Indexed = False +Description = "Minimum drag velocity" + +[ESTIMATED_CRUISE_SPEED] +Id = EstimatedCruiseSpeed +CategoryId = Miscellaneous +SimVarName = "ESTIMATED CRUISE SPEED" +Unit = "Feet per second" +CanSet = False +Indexed = False +Description = "Estimated cruise speed" + +[CG_PERCENT] +Id = CgPercent +CategoryId = Miscellaneous +SimVarName = "CG PERCENT" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Longitudinal CG position as a percent of reference chord" + +[CG_PERCENT_LATERAL] +Id = CgPercentLateral +CategoryId = Miscellaneous +SimVarName = "CG PERCENT LATERAL" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Lateral CG position as a percent of reference chord" + +[IS_SLEW_ACTIVE] +Id = IsSlewActive +CategoryId = Miscellaneous +SimVarName = "IS SLEW ACTIVE" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "True if slew is active" + +[IS_SLEW_ALLOWED] +Id = IsSlewAllowed +CategoryId = Miscellaneous +SimVarName = "IS SLEW ALLOWED" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "True if slew is enabled" + +[ATC_SUGGESTED_MIN_RWY_TAKEOFF] +Id = AtcSuggestedMinRwyTakeoff +CategoryId = Miscellaneous +SimVarName = "ATC SUGGESTED MIN RWY TAKEOFF" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "Suggested minimum runway length for takeoff. Used by ATC " + +[ATC_SUGGESTED_MIN_RWY_LANDING] +Id = AtcSuggestedMinRwyLanding +CategoryId = Miscellaneous +SimVarName = "ATC SUGGESTED MIN RWY LANDING" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "Suggested minimum runway length for landing. Used by ATC " + +[PAYLOAD_STATION_WEIGHT] +Id = PayloadStationWeight +CategoryId = Miscellaneous +SimVarName = "PAYLOAD STATION WEIGHT" +Unit = "Pounds" +CanSet = True +Indexed = True +Description = "Individual payload station weight" + +[PAYLOAD_STATION_COUNT] +Id = PayloadStationCount +CategoryId = Miscellaneous +SimVarName = "PAYLOAD STATION COUNT" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Number of payload stations" + +[USER_INPUT_ENABLED] +Id = UserInputEnabled +CategoryId = Miscellaneous +SimVarName = "USER INPUT ENABLED" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "Is input allowed from the user" + +[TYPICAL_DESCENT_RATE] +Id = TypicalDescentRate +CategoryId = Miscellaneous +SimVarName = "TYPICAL DESCENT RATE" +Unit = "Feet per minute" +CanSet = False +Indexed = False +Description = "Normal descent rate" + +[VISUAL_MODEL_RADIUS] +Id = VisualModelRadius +CategoryId = Miscellaneous +SimVarName = "VISUAL MODEL RADIUS" +Unit = "Meters" +CanSet = False +Indexed = False +Description = "Model radius" + +[SIGMA_SQRT] +Id = SigmaSqrt +CategoryId = Miscellaneous +SimVarName = "SIGMA SQRT" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Sigma sqrt" + +[DYNAMIC_PRESSURE] +Id = DynamicPressure +CategoryId = Miscellaneous +SimVarName = "DYNAMIC PRESSURE" +Unit = "foot pounds" +CanSet = False +Indexed = False +Description = "Dynamic pressure" + +[TOTAL_VELOCITY] +Id = TotalVelocity +CategoryId = Miscellaneous +SimVarName = "TOTAL VELOCITY" +Unit = "Feet per second" +CanSet = False +Indexed = False +Description = "Velocity regardless of direction. For example, if a helicopter is ascending vertically at 100 fps, getting this variable will return 100." + +[AIRSPEED_SELECT_INDICATED_OR_TRUE] +Id = AirspeedSelectIndicatedOrTrue +CategoryId = Miscellaneous +SimVarName = "AIRSPEED SELECT INDICATED OR TRUE" +Unit = "Knots" +CanSet = False +Indexed = False +Description = "The airspeed, whether true or indicated airspeed has been selected." + +[VARIOMETER_RATE] +Id = VariometerRate +CategoryId = Miscellaneous +SimVarName = "VARIOMETER RATE" +Unit = "Feet per second" +CanSet = False +Indexed = False +Description = "Variometer rate" + +[VARIOMETER_SWITCH] +Id = VariometerSwitch +CategoryId = Miscellaneous +SimVarName = "VARIOMETER SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the variometer switch is on" + +[PRESSURE_ALTITUDE] +Id = PressureAltitude +CategoryId = Miscellaneous +SimVarName = "PRESSURE ALTITUDE" +Unit = "Meters" +CanSet = False +Indexed = False +Description = "Altitude reading" + +[MAGNETIC_COMPASS] +Id = MagneticCompass +CategoryId = Miscellaneous +SimVarName = "MAGNETIC COMPASS" +Unit = "Degrees" +CanSet = False +Indexed = False +Description = "Compass reading" + +[TURN_INDICATOR_RATE] +Id = TurnIndicatorRate +CategoryId = Miscellaneous +SimVarName = "TURN INDICATOR RATE" +Unit = "Radians per second" +CanSet = False +Indexed = False +Description = "Turn indicator reading" + +[TURN_INDICATOR_SWITCH] +Id = TurnIndicatorSwitch +CategoryId = Miscellaneous +SimVarName = "TURN INDICATOR SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if turn indicator switch is on" + +[YOKE_Y_INDICATOR] +Id = YokeYIndicator +CategoryId = Miscellaneous +SimVarName = "YOKE Y INDICATOR" +Unit = "Position" +CanSet = False +Indexed = False +Description = "Yoke position in vertical direction" + +[YOKE_X_INDICATOR] +Id = YokeXIndicator +CategoryId = Miscellaneous +SimVarName = "YOKE X INDICATOR" +Unit = "Position" +CanSet = False +Indexed = False +Description = "Yoke position in horizontal direction" + +[RUDDER_PEDAL_INDICATOR] +Id = RudderPedalIndicator +CategoryId = Miscellaneous +SimVarName = "RUDDER PEDAL INDICATOR" +Unit = "Position" +CanSet = False +Indexed = False +Description = "Rudder pedal position" + +[BRAKE_DEPENDENT_HYDRAULIC_PRESSURE] +Id = BrakeDependentHydraulicPressure +CategoryId = Miscellaneous +SimVarName = "BRAKE DEPENDENT HYDRAULIC PRESSURE" +Unit = "foot pounds" +CanSet = False +Indexed = False +Description = "Brake dependent hydraulic pressure reading" + +[PANEL_ANTI_ICE_SWITCH] +Id = PanelAntiIceSwitch +CategoryId = Miscellaneous +SimVarName = "PANEL ANTI ICE SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if panel anti-ice switch is on" + +[WING_AREA] +Id = WingArea +CategoryId = Miscellaneous +SimVarName = "WING AREA" +Unit = "Square feet" +CanSet = False +Indexed = False +Description = "Total wing area" + +[WING_SPAN] +Id = WingSpan +CategoryId = Miscellaneous +SimVarName = "WING SPAN" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "Total wing span" + +[BETA_DOT] +Id = BetaDot +CategoryId = Miscellaneous +SimVarName = "BETA DOT" +Unit = "Radians per second" +CanSet = False +Indexed = False +Description = "Beta dot" + +[LINEAR_CL_ALPHA] +Id = LinearClAlpha +CategoryId = Miscellaneous +SimVarName = "LINEAR CL ALPHA" +Unit = "Per radian" +CanSet = False +Indexed = False +Description = "Linear CL alpha" + +[STALL_ALPHA] +Id = StallAlpha +CategoryId = Miscellaneous +SimVarName = "STALL ALPHA" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Stall alpha" + +[ZERO_LIFT_ALPHA] +Id = ZeroLiftAlpha +CategoryId = Miscellaneous +SimVarName = "ZERO LIFT ALPHA" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Zero lift alpha" + +[CG_AFT_LIMIT] +Id = CgAftLimit +CategoryId = Miscellaneous +SimVarName = "CG AFT LIMIT" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Aft limit of CG" + +[CG_FWD_LIMIT] +Id = CgFwdLimit +CategoryId = Miscellaneous +SimVarName = "CG FWD LIMIT" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Forward limit of CG" + +[CG_MAX_MACH] +Id = CgMaxMach +CategoryId = Miscellaneous +SimVarName = "CG MAX MACH" +Unit = "Machs" +CanSet = False +Indexed = False +Description = "Max mach CG" + +[CG_MIN_MACH] +Id = CgMinMach +CategoryId = Miscellaneous +SimVarName = "CG MIN MACH" +Unit = "Machs" +CanSet = False +Indexed = False +Description = "Min mach CG" + +[PAYLOAD_STATION_NAME] +Id = PayloadStationName +CategoryId = Miscellaneous +SimVarName = "PAYLOAD STATION NAME" +Unit = "String" +CanSet = False +Indexed = False +Description = "Descriptive name for payload station" + +[ELEVON_DEFLECTION] +Id = ElevonDeflection +CategoryId = Miscellaneous +SimVarName = "ELEVON DEFLECTION" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Elevon deflection" + +[EXIT_TYPE] +Id = ExitType +CategoryId = Miscellaneous +SimVarName = "EXIT TYPE" +Unit = "Enum" +CanSet = False +Indexed = False +Description = "One of:; 0: Main; 1: Cargo; 2: Emergency; 3: Unknown" + +[EXIT_POSX] +Id = ExitPosx +CategoryId = Miscellaneous +SimVarName = "EXIT POSX" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "Position of exit relative to datum reference point" + +[EXIT_POSY] +Id = ExitPosy +CategoryId = Miscellaneous +SimVarName = "EXIT POSY" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "Position of exit relative to datum reference point" + +[EXIT_POSZ] +Id = ExitPosz +CategoryId = Miscellaneous +SimVarName = "EXIT POSZ" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "Position of exit relative to datum reference point" + +[DECISION_HEIGHT] +Id = DecisionHeight +CategoryId = Miscellaneous +SimVarName = "DECISION HEIGHT" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "Design decision height" + +[DECISION_ALTITUDE_MSL] +Id = DecisionAltitudeMsl +CategoryId = Miscellaneous +SimVarName = "DECISION ALTITUDE MSL" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "Design decision altitude above mean sea level" + +[EMPTY_WEIGHT_PITCH_MOI] +Id = EmptyWeightPitchMoi +CategoryId = Miscellaneous +SimVarName = "EMPTY WEIGHT PITCH MOI" +Unit = "slug feet squared" +CanSet = False +Indexed = False +Description = "Empty weight pitch moment of inertia" + +[EMPTY_WEIGHT_ROLL_MOI] +Id = EmptyWeightRollMoi +CategoryId = Miscellaneous +SimVarName = "EMPTY WEIGHT ROLL MOI" +Unit = "slug feet squared" +CanSet = False +Indexed = False +Description = "Empty weight roll moment of inertia" + +[EMPTY_WEIGHT_YAW_MOI] +Id = EmptyWeightYawMoi +CategoryId = Miscellaneous +SimVarName = "EMPTY WEIGHT YAW MOI" +Unit = "slug feet squared" +CanSet = False +Indexed = False +Description = "Empty weight yaw moment of inertia" + +[EMPTY_WEIGHT_CROSS_COUPLED_MOI] +Id = EmptyWeightCrossCoupledMoi +CategoryId = Miscellaneous +SimVarName = "EMPTY WEIGHT CROSS COUPLED MOI" +Unit = "slug feet squared" +CanSet = False +Indexed = False +Description = "Empty weigth cross coupled moment of inertia" + +[TOTAL_WEIGHT_PITCH_MOI] +Id = TotalWeightPitchMoi +CategoryId = Miscellaneous +SimVarName = "TOTAL WEIGHT PITCH MOI" +Unit = "slug feet squared" +CanSet = False +Indexed = False +Description = "Total weight pitch moment of inertia" + +[TOTAL_WEIGHT_ROLL_MOI] +Id = TotalWeightRollMoi +CategoryId = Miscellaneous +SimVarName = "TOTAL WEIGHT ROLL MOI" +Unit = "slug feet squared" +CanSet = False +Indexed = False +Description = "Total weight roll moment of inertia" + +[TOTAL_WEIGHT_YAW_MOI] +Id = TotalWeightYawMoi +CategoryId = Miscellaneous +SimVarName = "TOTAL WEIGHT YAW MOI" +Unit = "slug feet squared" +CanSet = False +Indexed = False +Description = "Total weight yaw moment of inertia" + +[TOTAL_WEIGHT_CROSS_COUPLED_MOI] +Id = TotalWeightCrossCoupledMoi +CategoryId = Miscellaneous +SimVarName = "TOTAL WEIGHT CROSS COUPLED MOI" +Unit = "slug feet squared" +CanSet = False +Indexed = False +Description = "Total weight cross coupled moment of inertia" + +[WATER_BALLAST_VALVE] +Id = WaterBallastValve +CategoryId = Miscellaneous +SimVarName = "WATER BALLAST VALVE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if water ballast valve is available" + +[MAX_RATED_ENGINE_RPM] +Id = MaxRatedEngineRpm +CategoryId = Miscellaneous +SimVarName = "MAX RATED ENGINE RPM" +Unit = "Rpm" +CanSet = False +Indexed = False +Description = "Maximum rated rpm" + +[FULL_THROTTLE_THRUST_TO_WEIGHT_RATIO] +Id = FullThrottleThrustToWeightRatio +CategoryId = Miscellaneous +SimVarName = "FULL THROTTLE THRUST TO WEIGHT RATIO" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Full throttle thrust to weight ratio" + +[PROP_AUTO_CRUISE_ACTIVE] +Id = PropAutoCruiseActive +CategoryId = Miscellaneous +SimVarName = "PROP AUTO CRUISE ACTIVE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if prop auto cruise active" + +[PROP_ROTATION_ANGLE] +Id = PropRotationAngle +CategoryId = Miscellaneous +SimVarName = "PROP ROTATION ANGLE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Prop rotation angle" + +[PROP_BETA_MAX] +Id = PropBetaMax +CategoryId = Miscellaneous +SimVarName = "PROP BETA MAX" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Prop beta max" + +[PROP_BETA_MIN] +Id = PropBetaMin +CategoryId = Miscellaneous +SimVarName = "PROP BETA MIN" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Prop beta min" + +[PROP_BETA_MIN_REVERSE] +Id = PropBetaMinReverse +CategoryId = Miscellaneous +SimVarName = "PROP BETA MIN REVERSE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Prop beta min reverse" + +[FUEL_SELECTED_TRANSFER_MODE] +Id = FuelSelectedTransferMode +CategoryId = Miscellaneous +SimVarName = "FUEL SELECTED TRANSFER MODE" +Unit = "Enum" +CanSet = False +Indexed = False +Description = "One of:; -1: off; 0: auto; 1: forward; 2: aft; 3: manual" + +[DROPPABLE_OBJECTS_UI_NAME] +Id = DroppableObjectsUiName +CategoryId = Miscellaneous +SimVarName = "DROPPABLE OBJECTS UI NAME" +Unit = "String" +CanSet = False +Indexed = False +Description = "Descriptive name, used in User Interface dialogs, of a droppable object" + +[MANUAL_FUEL_PUMP_HANDLE] +Id = ManualFuelPumpHandle +CategoryId = Miscellaneous +SimVarName = "MANUAL FUEL PUMP HANDLE" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Position of manual fuel pump handle. 100 is fully deployed." + +[BLEED_AIR_SOURCE_CONTROL] +Id = BleedAirSourceControl +CategoryId = Miscellaneous +SimVarName = "BLEED AIR SOURCE CONTROL" +Unit = "Enum" +CanSet = False +Indexed = False +Description = "One of:; 0: min; 1: auto; 2: off; 3: apu; 4: engines" + +[ELECTRICAL_OLD_CHARGING_AMPS] +Id = ElectricalOldChargingAmps +CategoryId = Miscellaneous +SimVarName = "ELECTRICAL OLD CHARGING AMPS" +Unit = "Amps" +CanSet = False +Indexed = False +Description = "Legacy, use ELECTRICAL BATTERY LOAD" + +[HYDRAULIC_SWITCH] +Id = HydraulicSwitch +CategoryId = Miscellaneous +SimVarName = "HYDRAULIC SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if hydraulic switch is on" + +[CONCORDE_VISOR_POSITION_PERCENT] +Id = ConcordeVisorPositionPercent +CategoryId = Miscellaneous +SimVarName = "CONCORDE VISOR POSITION PERCENT" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "0 = up, 1.0 = extended/down" + +[CONCORDE_NOSE_ANGLE] +Id = ConcordeNoseAngle +CategoryId = Miscellaneous +SimVarName = "CONCORDE NOSE ANGLE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "0 = up" + +[REALISM_CRASH_WITH_OTHERS] +Id = RealismCrashWithOthers +CategoryId = Miscellaneous +SimVarName = "REALISM CRASH WITH OTHERS" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True indicates crashing with other aircraft is possible." + +[REALISM_CRASH_DETECTION] +Id = RealismCrashDetection +CategoryId = Miscellaneous +SimVarName = "REALISM CRASH DETECTION" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True indicates crash detection is turned on." + +[MANUAL_INSTRUMENT_LIGHTS] +Id = ManualInstrumentLights +CategoryId = Miscellaneous +SimVarName = "MANUAL INSTRUMENT LIGHTS" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if instrument lights are set manually" + +[PITOT_ICE_PCT] +Id = PitotIcePct +CategoryId = Miscellaneous +SimVarName = "PITOT ICE PCT" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Amount of pitot ice. 100 is fully iced." + +[SEMIBODY_LOADFACTOR_Y] +Id = SemibodyLoadfactorY +CategoryId = Miscellaneous +SimVarName = "SEMIBODY LOADFACTOR Y" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Semibody loadfactor x and z are not supported." + +[SEMIBODY_LOADFACTOR_YDOT] +Id = SemibodyLoadfactorYdot +CategoryId = Miscellaneous +SimVarName = "SEMIBODY LOADFACTOR YDOT" +Unit = "Per second" +CanSet = False +Indexed = False +Description = "Semibody loadfactory ydot" + +[RAD_INS_SWITCH] +Id = RadInsSwitch +CategoryId = Miscellaneous +SimVarName = "RAD INS SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if Rad INS switch on" + +[SIMULATED_RADIUS] +Id = SimulatedRadius +CategoryId = Miscellaneous +SimVarName = "SIMULATED RADIUS" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "Simulated radius" + +[STRUCTURAL_ICE_PCT] +Id = StructuralIcePct +CategoryId = Miscellaneous +SimVarName = "STRUCTURAL ICE PCT" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Amount of ice on aircraft structure. 100 is fully iced." + +[ARTIFICIAL_GROUND_ELEVATION] +Id = ArtificialGroundElevation +CategoryId = Miscellaneous +SimVarName = "ARTIFICIAL GROUND ELEVATION" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "In case scenery is not loaded for AI planes, this variable can be used to set a default surface elevation." + +[SURFACE_INFO_VALID] +Id = SurfaceInfoValid +CategoryId = Miscellaneous +SimVarName = "SURFACE INFO VALID" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True indicates SURFACE CONDITION is meaningful." + +[SURFACE_CONDITION] +Id = SurfaceCondition +CategoryId = Miscellaneous +SimVarName = "SURFACE CONDITION" +Unit = "Enum" +CanSet = False +Indexed = False +Description = "One of:; 0: Normal; 1: Wet; 2: Icy; 3: Snow" + +[PUSHBACK_ANGLE] +Id = PushbackAngle +CategoryId = Miscellaneous +SimVarName = "PUSHBACK ANGLE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Pushback angle (the heading of the tug)" + +[PUSHBACK_CONTACTX] +Id = PushbackContactx +CategoryId = Miscellaneous +SimVarName = "PUSHBACK CONTACTX" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "The towpoint position, relative to the aircrafts datum reference point." + +[PUSHBACK_CONTACTY] +Id = PushbackContacty +CategoryId = Miscellaneous +SimVarName = "PUSHBACK CONTACTY" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "Pushback contact position in vertical direction" + +[PUSHBACK_CONTACTZ] +Id = PushbackContactz +CategoryId = Miscellaneous +SimVarName = "PUSHBACK CONTACTZ" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "Pushback contact position in fore/aft direction" + +[PUSHBACK_WAIT] +Id = PushbackWait +CategoryId = Miscellaneous +SimVarName = "PUSHBACK WAIT" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if waiting for pushback." + +[YAW_STRING_ANGLE] +Id = YawStringAngle +CategoryId = Miscellaneous +SimVarName = "YAW STRING ANGLE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "The yaw string angle. Yaw strings are attached to gliders as visible indicators of the yaw angle. An animation of this is not implemented in ESP." + +[YAW_STRING_PCT_EXTENDED] +Id = YawStringPctExtended +CategoryId = Miscellaneous +SimVarName = "YAW STRING PCT EXTENDED" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Yaw string angle as a percentage" + +[INDUCTOR_COMPASS_PERCENT_DEVIATION] +Id = InductorCompassPercentDeviation +CategoryId = Miscellaneous +SimVarName = "INDUCTOR COMPASS PERCENT DEVIATION" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Inductor compass deviation reading" + +[INDUCTOR_COMPASS_HEADING_REF] +Id = InductorCompassHeadingRef +CategoryId = Miscellaneous +SimVarName = "INDUCTOR COMPASS HEADING REF" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Inductor compass heading" + +[ANEMOMETER_PCT_RPM] +Id = AnemometerPctRpm +CategoryId = Miscellaneous +SimVarName = "ANEMOMETER PCT RPM" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Anemometer rpm as a percentage" + +[ROTOR_ROTATION_ANGLE] +Id = RotorRotationAngle +CategoryId = Miscellaneous +SimVarName = "ROTOR ROTATION ANGLE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Main rotor rotation angle (helicopters only)" + +[DISK_PITCH_ANGLE] +Id = DiskPitchAngle +CategoryId = Miscellaneous +SimVarName = "DISK PITCH ANGLE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Main rotor pitch angle (helicopters only)" + +[DISK_BANK_ANGLE] +Id = DiskBankAngle +CategoryId = Miscellaneous +SimVarName = "DISK BANK ANGLE" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Main rotor bank angle (helicopters only)" + +[DISK_PITCH_PCT] +Id = DiskPitchPct +CategoryId = Miscellaneous +SimVarName = "DISK PITCH PCT" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Main rotor pitch percent (helicopters only)" + +[DISK_BANK_PCT] +Id = DiskBankPct +CategoryId = Miscellaneous +SimVarName = "DISK BANK PCT" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Main rotor bank percent (helicopters only)" + +[DISK_CONING_PCT] +Id = DiskConingPct +CategoryId = Miscellaneous +SimVarName = "DISK CONING PCT" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Main rotor coning percent (helicopters only)" + +[STATIC_CG_TO_GROUND] +Id = StaticCgToGround +CategoryId = Miscellaneous +SimVarName = "STATIC CG TO GROUND" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "Static CG to ground" + +[STATIC_PITCH] +Id = StaticPitch +CategoryId = Miscellaneous +SimVarName = "STATIC PITCH" +Unit = "Radians" +CanSet = False +Indexed = False +Description = "Static pitch" + +[CRASH_SEQUENCE] +Id = CrashSequence +CategoryId = Miscellaneous +SimVarName = "CRASH SEQUENCE" +Unit = "Enum" +CanSet = False +Indexed = False +Description = "One of:; 0: off; 1: complete; 3: reset; 4: pause; 11: start" + +[CRASH_FLAG] +Id = CrashFlag +CategoryId = Miscellaneous +SimVarName = "CRASH FLAG" +Unit = "Enum" +CanSet = False +Indexed = False +Description = "One of:; 0: None; 2: Mountain; 4: General; 6: Building; 8: Splash; 10: Gear up; 12: Overstress; 14: Building; 16: Aircraft; 18: Fuel Truck" + +[TOW_RELEASE_HANDLE] +Id = TowReleaseHandle +CategoryId = Miscellaneous +SimVarName = "TOW RELEASE HANDLE" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Position of tow release handle. 100 is fully deployed." + +[TOW_CONNECTION] +Id = TowConnection +CategoryId = Miscellaneous +SimVarName = "TOW CONNECTION" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if a towline is connected to both tow plane and glider." + +[APU_PCT_RPM] +Id = ApuPctRpm +CategoryId = Miscellaneous +SimVarName = "APU PCT RPM" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Auxiliary power unit rpm, as a percentage" + +[APU_PCT_STARTER] +Id = ApuPctStarter +CategoryId = Miscellaneous +SimVarName = "APU PCT STARTER" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Auxiliary power unit starter, as a percentage" + +[APU_VOLTS] +Id = ApuVolts +CategoryId = Miscellaneous +SimVarName = "APU VOLTS" +Unit = "Volts" +CanSet = False +Indexed = False +Description = "Auxiliary power unit voltage" + +[APU_GENERATOR_SWITCH] +Id = ApuGeneratorSwitch +CategoryId = Miscellaneous +SimVarName = "APU GENERATOR SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if APU generator switch on" + +[APU_GENERATOR_ACTIVE] +Id = ApuGeneratorActive +CategoryId = Miscellaneous +SimVarName = "APU GENERATOR ACTIVE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if APU generator active" + +[APU_ON_FIRE_DETECTED] +Id = ApuOnFireDetected +CategoryId = Miscellaneous +SimVarName = "APU ON FIRE DETECTED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if APU on fire" + +[PRESSURIZATION_CABIN_ALTITUDE] +Id = PressurizationCabinAltitude +CategoryId = Miscellaneous +SimVarName = "PRESSURIZATION CABIN ALTITUDE" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "The current altitude of the cabin pressurization.." + +[PRESSURIZATION_CABIN_ALTITUDE_GOAL] +Id = PressurizationCabinAltitudeGoal +CategoryId = Miscellaneous +SimVarName = "PRESSURIZATION CABIN ALTITUDE GOAL" +Unit = "Feet" +CanSet = False +Indexed = False +Description = "The set altitude of the cabin pressurization." + +[PRESSURIZATION_CABIN_ALTITUDE_RATE] +Id = PressurizationCabinAltitudeRate +CategoryId = Miscellaneous +SimVarName = "PRESSURIZATION CABIN ALTITUDE RATE" +Unit = "Feet per second" +CanSet = False +Indexed = False +Description = "The rate at which cabin pressurization changes." + +[PRESSURIZATION_PRESSURE_DIFFERENTIAL] +Id = PressurizationPressureDifferential +CategoryId = Miscellaneous +SimVarName = "PRESSURIZATION PRESSURE DIFFERENTIAL" +Unit = "foot pounds" +CanSet = False +Indexed = False +Description = "The difference in pressure between the set altitude pressurization and the current pressurization." + +[PRESSURIZATION_DUMP_SWITCH] +Id = PressurizationDumpSwitch +CategoryId = Miscellaneous +SimVarName = "PRESSURIZATION DUMP SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the cabin pressurization dump switch is on." + +[FIRE_BOTTLE_SWITCH] +Id = FireBottleSwitch +CategoryId = Miscellaneous +SimVarName = "FIRE BOTTLE SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the fire bottle switch is on." + +[FIRE_BOTTLE_DISCHARGED] +Id = FireBottleDischarged +CategoryId = Miscellaneous +SimVarName = "FIRE BOTTLE DISCHARGED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the fire bottle is discharged." + +[CABIN_NO_SMOKING_ALERT_SWITCH] +Id = CabinNoSmokingAlertSwitch +CategoryId = Miscellaneous +SimVarName = "CABIN NO SMOKING ALERT SWITCH" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "True if the No Smoking switch is on." + +[CABIN_SEATBELTS_ALERT_SWITCH] +Id = CabinSeatbeltsAlertSwitch +CategoryId = Miscellaneous +SimVarName = "CABIN SEATBELTS ALERT SWITCH" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "True if the Seatbelts switch is on." + +[GPWS_WARNING] +Id = GpwsWarning +CategoryId = Miscellaneous +SimVarName = "GPWS WARNING" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if Ground Proximity Warning System installed." + +[GPWS_SYSTEM_ACTIVE] +Id = GpwsSystemActive +CategoryId = Miscellaneous +SimVarName = "GPWS SYSTEM ACTIVE" +Unit = "Bool" +CanSet = True +Indexed = False +Description = "True if the Ground Proximity Warning System is active" + +[IS_ALTITUDE_FREEZE_ON] +Id = IsAltitudeFreezeOn +CategoryId = Miscellaneous +SimVarName = "IS ALTITUDE FREEZE ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the altitude of the aircraft is frozen." + +[IS_ATTITUDE_FREEZE_ON] +Id = IsAttitudeFreezeOn +CategoryId = Miscellaneous +SimVarName = "IS ATTITUDE FREEZE ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the attitude (pitch, bank and heading) of the aircraft is frozen." + +[SLING_HOOK_IN_PICKUP_MODE] +Id = SlingHookInPickupMode +CategoryId = Miscellaneous +SimVarName = "SLING HOOK IN PICKUP MODE" +Unit = "Bool" +CanSet = False +Indexed = True +Description = " " + +[AI_TRAFFIC_STATE] +Id = AiTrafficState +CategoryId = Miscellaneous +SimVarName = "AI TRAFFIC STATE" +Unit = "String" +CanSet = False +Indexed = False +Description = " " + +[AI_TRAFFIC_ASSIGNED_PARKING] +Id = AiTrafficAssignedParking +CategoryId = Miscellaneous +SimVarName = "AI TRAFFIC ASSIGNED PARKING" +Unit = "String" +CanSet = False +Indexed = False +Description = " " + +[RECIP_ENG_FUEL_TANKS_USED] +Id = RecipEngFuelTanksUsed +CategoryId = Miscellaneous +SimVarName = "RECIP ENG FUEL TANKS USED" +Unit = "Mask" +CanSet = True +Indexed = True +Description = " " + +[TURB_ENG_TANKS_USED] +Id = TurbEngTanksUsed +CategoryId = Miscellaneous +SimVarName = "TURB ENG TANKS USED" +Unit = "Mask" +CanSet = False +Indexed = True +Description = " " + +[ENG_TURBINE_TEMPERATURE] +Id = EngTurbineTemperature +CategoryId = Miscellaneous +SimVarName = "ENG TURBINE TEMPERATURE" +Unit = "Celsius" +CanSet = False +Indexed = True +Description = " " + +[ENG_ELECTRICAL_LOAD] +Id = EngElectricalLoad +CategoryId = Miscellaneous +SimVarName = "ENG ELECTRICAL LOAD" +Unit = "Percent" +CanSet = False +Indexed = True +Description = " " + +[ENG_TRANSMISSION_PRESSURE] +Id = EngTransmissionPressure +CategoryId = Miscellaneous +SimVarName = "ENG TRANSMISSION PRESSURE" +Unit = "PSI" +CanSet = False +Indexed = True +Description = " " + +[ENG_TRANSMISSION_TEMPERATURE] +Id = EngTransmissionTemperature +CategoryId = Miscellaneous +SimVarName = "ENG TRANSMISSION TEMPERATURE" +Unit = "Celsius" +CanSet = False +Indexed = True +Description = " " + +[SURFACE_TYPE] +Id = SurfaceType +CategoryId = Miscellaneous +SimVarName = "SURFACE TYPE" +Unit = "Enum" +CanSet = False +Indexed = False +Description = " " + +[COM_STATUS] +Id = ComStatus +CategoryId = Miscellaneous +SimVarName = "COM STATUS" +Unit = "Enum" +CanSet = False +Indexed = True +Description = " " + +[NAV_TOFROM] +Id = NavTofrom +CategoryId = Miscellaneous +SimVarName = "NAV TOFROM" +Unit = "Enum" +CanSet = False +Indexed = True +Description = " " + +[GPS_APPROACH_MODE] +Id = GpsApproachMode +CategoryId = Miscellaneous +SimVarName = "GPS APPROACH MODE" +Unit = "Enum" +CanSet = False +Indexed = False +Description = " " + +[GPS_APPROACH_WP_TYPE] +Id = GpsApproachWpType +CategoryId = Miscellaneous +SimVarName = "GPS APPROACH WP TYPE" +Unit = "Enum" +CanSet = False +Indexed = False +Description = " " + +[GPS_APPROACH_SEGMENT_TYPE] +Id = GpsApproachSegmentType +CategoryId = Miscellaneous +SimVarName = "GPS APPROACH SEGMENT TYPE" +Unit = "Enum" +CanSet = False +Indexed = False +Description = " " + +[GPS_APPROACH_APPROACH_TYPE] +Id = GpsApproachApproachType +CategoryId = Miscellaneous +SimVarName = "GPS APPROACH APPROACH TYPE" +Unit = "Enum" +CanSet = False +Indexed = False +Description = " " + +[AMBIENT_PRECIP_STATE] +Id = AmbientPrecipState +CategoryId = Miscellaneous +SimVarName = "AMBIENT PRECIP STATE" +Unit = "Mask" +CanSet = False +Indexed = False +Description = " " + +[CATEGORY] +Id = Category +CategoryId = Miscellaneous +SimVarName = "CATEGORY" +Unit = "String" +CanSet = False +Indexed = False +Description = " " + +[CONCORDE_VISOR_NOSE_HANDLE] +Id = ConcordeVisorNoseHandle +CategoryId = Miscellaneous +SimVarName = "CONCORDE VISOR NOSE HANDLE" +Unit = "Enum" +CanSet = False +Indexed = False +Description = " " + +[IS_LATITUDE_LONGITUDE_FREEZE_ON] +Id = IsLatitudeLongitudeFreezeOn +CategoryId = Miscellaneous +SimVarName = "IS LATITUDE LONGITUDE FREEZE ON" +Unit = "Bool" +CanSet = False +Indexed = False +Description = " " + +[TIME_OF_DAY] +Id = TimeOfDay +CategoryId = Miscellaneous +SimVarName = "TIME OF DAY" +Unit = "Enum" +CanSet = False +Indexed = False +Description = " " + +[SIMULATION_RATE] +Id = SimulationRate +CategoryId = Miscellaneous +SimVarName = "SIMULATION RATE" +Unit = "Number" +CanSet = False +Indexed = False +Description = " " + +[UNITS_OF_MEASURE] +Id = UnitsOfMeasure +CategoryId = Miscellaneous +SimVarName = "UNITS OF MEASURE" +Unit = "Enum" +CanSet = False +Indexed = False +Description = " " + + + +# String ####################### +# +[category_String] + +[ATC_TYPE] +Id = AtcType +CategoryId = String +SimVarName = "ATC TYPE" +Unit = "String" +CanSet = False +Indexed = False +Description = "Type used by ATC" + +[ATC_MODEL] +Id = AtcModel +CategoryId = String +SimVarName = "ATC MODEL" +Unit = "String" +CanSet = False +Indexed = False +Description = "Model used by ATC" + +[ATC_ID] +Id = AtcId +CategoryId = String +SimVarName = "ATC ID" +Unit = "String" +CanSet = True +Indexed = False +Description = "ID used by ATC" + +[ATC_AIRLINE] +Id = AtcAirline +CategoryId = String +SimVarName = "ATC AIRLINE" +Unit = "String" +CanSet = True +Indexed = False +Description = "Airline used by ATC" + +[ATC_FLIGHT_NUMBER] +Id = AtcFlightNumber +CategoryId = String +SimVarName = "ATC FLIGHT NUMBER" +Unit = "String" +CanSet = True +Indexed = False +Description = "Flight Number used by ATC" + +[TITLE] +Id = Title +CategoryId = String +SimVarName = "TITLE" +Unit = "String" +CanSet = False +Indexed = False +Description = "Title from aircraft.cfg" + +[HSI_STATION_IDENT] +Id = HsiStationIdent +CategoryId = String +SimVarName = "HSI STATION IDENT" +Unit = "String" +CanSet = False +Indexed = False +Description = "Tuned station identifier" + +[GPS_APPROACH_AIRPORT_ID] +Id = GpsApproachAirportId +CategoryId = String +SimVarName = "GPS APPROACH AIRPORT ID" +Unit = "String" +CanSet = False +Indexed = False +Description = "ID of airport" + +[GPS_APPROACH_APPROACH_ID] +Id = GpsApproachApproachId +CategoryId = String +SimVarName = "GPS APPROACH APPROACH ID" +Unit = "String" +CanSet = False +Indexed = False +Description = "ID of approach" + +[GPS_APPROACH_TRANSITION_ID] +Id = GpsApproachTransitionId +CategoryId = String +SimVarName = "GPS APPROACH TRANSITION ID" +Unit = "String" +CanSet = False +Indexed = False +Description = "ID of approach transition" + + + +# AIControlledAircraft ####################### +# +[category_AIControlledAircraft] + +[AI_DESIRED_SPEED] +Id = AiDesiredSpeed +CategoryId = AIControlledAircraft +SimVarName = "AI DESIRED SPEED" +Unit = "Knots" +CanSet = True +Indexed = False +Description = "Desired speed of the AI object." + +[AI_CURRENT_WAYPOINT] +Id = AiCurrentWaypoint +CategoryId = AIControlledAircraft +SimVarName = "AI CURRENT WAYPOINT" +Unit = "Number" +CanSet = True +Indexed = False +Description = "Current waypoint in the list" + +[AI_DESIRED_HEADING] +Id = AiDesiredHeading +CategoryId = AIControlledAircraft +SimVarName = "AI DESIRED HEADING" +Unit = "Degrees" +CanSet = True +Indexed = False +Description = "Desired heading of the AI object." + +[AI_GROUNDTURNTIME] +Id = AiGroundturntime +CategoryId = AIControlledAircraft +SimVarName = "AI GROUNDTURNTIME" +Unit = "Seconds" +CanSet = True +Indexed = False +Description = "Time to make a 90 degree turn." + +[AI_GROUNDCRUISESPEED] +Id = AiGroundcruisespeed +CategoryId = AIControlledAircraft +SimVarName = "AI GROUNDCRUISESPEED" +Unit = "Knots" +CanSet = True +Indexed = False +Description = "Cruising speed." + +[AI_GROUNDTURNSPEED] +Id = AiGroundturnspeed +CategoryId = AIControlledAircraft +SimVarName = "AI GROUNDTURNSPEED" +Unit = "Knots" +CanSet = True +Indexed = False +Description = "Turning speed." + +[AI_TRAFFIC_ISIFR] +Id = AiTrafficIsifr +CategoryId = AIControlledAircraft +SimVarName = "AI TRAFFIC ISIFR" +Unit = "Boolean" +CanSet = False +Indexed = False +Description = "Request whether this aircraft is IFR or VFR See Note 1." + +[AI_TRAFFIC_CURRENT_AIRPORT] +Id = AiTrafficCurrentAirport +CategoryId = AIControlledAircraft +SimVarName = "AI TRAFFIC CURRENT AIRPORT" +Unit = "String" +CanSet = False +Indexed = False +Description = "ICAO code of current airport. See Note 1." + +[AI_TRAFFIC_ASSIGNED_RUNWAY] +Id = AiTrafficAssignedRunway +CategoryId = AIControlledAircraft +SimVarName = "AI TRAFFIC ASSIGNED RUNWAY" +Unit = "String" +CanSet = False +Indexed = False +Description = "Assigned runway name (for example: "32R"). See Note 1." + +[AI_TRAFFIC_FROMAIRPORT] +Id = AiTrafficFromairport +CategoryId = AIControlledAircraft +SimVarName = "AI TRAFFIC FROMAIRPORT" +Unit = "String" +CanSet = False +Indexed = False +Description = "ICAO code of the departure airport in the current schedule. See Note 2." + +[AI_TRAFFIC_TOAIRPORT] +Id = AiTrafficToairport +CategoryId = AIControlledAircraft +SimVarName = "AI TRAFFIC TOAIRPORT" +Unit = "String" +CanSet = False +Indexed = False +Description = "ICAO code of the destination airport in the current schedule. See Note 2." + +[AI_TRAFFIC_ETD] +Id = AiTrafficEtd +CategoryId = AIControlledAircraft +SimVarName = "AI TRAFFIC ETD" +Unit = "Seconds" +CanSet = False +Indexed = False +Description = "Estimated time of departure for the current schedule entry, given as the number of seconds difference from the current simulation time. This can be negative if ETD is earlier than the current simulation time. See Note 2." + +[AI_TRAFFIC_ETA] +Id = AiTrafficEta +CategoryId = AIControlledAircraft +SimVarName = "AI TRAFFIC ETA" +Unit = "Seconds" +CanSet = False +Indexed = False +Description = "Estimated time of arrival for the current schedule entry, given as the number of seconds difference from the current simulated time. This can be negative if ETA is earlier than the current simulated time. See Note 2." + + + +# CarrierOperations ####################### +# +[category_CarrierOperations] + +[LAUNCHBAR_POSITION] +Id = LaunchbarPosition +CategoryId = CarrierOperations +SimVarName = "LAUNCHBAR POSITION" +Unit = "Percent over 100" +CanSet = False +Indexed = False +Description = "Installed on aircraft before takeoff from a carrier catapult. Note that gear cannot retract with this extended. 100 = fully extended. Refer to the document Notes on Aircraft Systems." + +[LAUNCHBAR_SWITCH] +Id = LaunchbarSwitch +CategoryId = CarrierOperations +SimVarName = "LAUNCHBAR SWITCH" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "If this is set to True the launch bar switch has been engaged." + +[LAUNCHBAR_HELD_EXTENDED] +Id = LaunchbarHeldExtended +CategoryId = CarrierOperations +SimVarName = "LAUNCHBAR HELD EXTENDED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "This will be True if the launchbar is fully extended, and can be used, for example, to change the color of an instrument light." + +[NUMBER_OF_CATAPULTS] +Id = NumberOfCatapults +CategoryId = CarrierOperations +SimVarName = "NUMBER OF CATAPULTS" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Maximum of 4. A model can contain more than 4 catapults, but only the first four will be read and recognized by the simulation." + +[CATAPULT_STROKE_POSITION] +Id = CatapultStrokePosition +CategoryId = CarrierOperations +SimVarName = "CATAPULT STROKE POSITION" +Unit = "Number" +CanSet = False +Indexed = True +Description = "Catapults are indexed from 1. This value will be 0 before the catapult fires, and then up to 100 as the aircraft is propelled down the catapult. The aircraft may takeoff before the value reaches 100 (depending on the aircraft weight, power applied, and other factors), in which case this value will not be further updated. This value could be used to drive a bogie animation." + +[HOLDBACK_BAR_INSTALLED] +Id = HoldbackBarInstalled +CategoryId = CarrierOperations +SimVarName = "HOLDBACK BAR INSTALLED" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Holdback bars allow build up of thrust before takeoff from a catapult, and are installed by the deck crew of an aircraft carrier." + +[BLAST_SHIELD_POSITION] +Id = BlastShieldPosition +CategoryId = CarrierOperations +SimVarName = "BLAST SHIELD POSITION" +Unit = "Percent over 100" +CanSet = False +Indexed = True +Description = "Indexed from 1, 100 is fully deployed, 0 flat on deck" + +[CABLE_CAUGHT_BY_TAILHOOK] +Id = CableCaughtByTailhook +CategoryId = CarrierOperations +SimVarName = "CABLE CAUGHT BY TAILHOOK" +Unit = "Number" +CanSet = False +Indexed = False +Description = "A number 1 through 4 for the cable number caught by the tailhook. Cable 1 is the one closest to the stern of the carrier. A value of 0 indicates no cable was caught." + +[TAILHOOK_HANDLE] +Id = TailhookHandle +CategoryId = CarrierOperations +SimVarName = "TAILHOOK HANDLE" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "True if the tailhook handle is engaged." + +[SURFACE_RELATIVE_GROUND_SPEED] +Id = SurfaceRelativeGroundSpeed +CategoryId = CarrierOperations +SimVarName = "SURFACE RELATIVE GROUND SPEED" +Unit = "Feet per second" +CanSet = False +Indexed = False +Description = "The speed of the aircraft relative to the speed of the first surface directly underneath it. Use this to retrieve, for example, an aircraft's taxiing speed while it is moving on a moving carrier. It also applies to airborne aircraft, for example when a helicopter is successfully hovering above a moving ship, this value should be zero. The returned value will be the same as GROUND VELOCITY if the first surface beneath it is not moving." + + + +# Racing ####################### +# +[category_Racing] + +[RECIP_ENG_DETONATING] +Id = RecipEngDetonating +CategoryId = Racing +SimVarName = "RECIP ENG DETONATING" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "Indexed from 1. Set to True if the engine is detonating." + +[RECIP_ENG_CYLINDER_HEALTH] +Id = RecipEngCylinderHealth +CategoryId = Racing +SimVarName = "RECIP ENG CYLINDER HEALTH" +Unit = "Percent over 100" +CanSet = False +Indexed = True +Description = "Index high 16 bits is engine number, low 16 cylinder number, both indexed from 1." + +[RECIP_ENG_NUM_CYLINDERS] +Id = RecipEngNumCylinders +CategoryId = Racing +SimVarName = "RECIP ENG NUM CYLINDERS" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Indexed from 1. The number of engine cylinders." + +[RECIP_ENG_NUM_CYLINDERS_FAILED] +Id = RecipEngNumCylindersFailed +CategoryId = Racing +SimVarName = "RECIP ENG NUM CYLINDERS FAILED" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Indexed from 1. The number of cylinders that have failed." + +[RECIP_ENG_ANTIDETONATION_TANK_VALVE] +Id = RecipEngAntidetonationTankValve +CategoryId = Racing +SimVarName = "RECIP ENG ANTIDETONATION TANK VALVE" +Unit = "Bool" +CanSet = True +Indexed = True +Description = "Indexed from 1, each engine can have one antidetonation tank. Installed on racing aircraft. Refer to the document Notes on Aircraft Systems." + +[RECIP_ENG_ANTIDETONATION_TANK_QUANTITY] +Id = RecipEngAntidetonationTankQuantity +CategoryId = Racing +SimVarName = "RECIP ENG ANTIDETONATION TANK QUANTITY" +Unit = "Gallons" +CanSet = True +Indexed = True +Description = "Indexed from 1. Refer to the Mission Creation documentationfor the procedure for refilling tanks." + +[RECIP_ENG_ANTIDETONATION_TANK_MAX_QUANTITY] +Id = RecipEngAntidetonationTankMaxQuantity +CategoryId = Racing +SimVarName = "RECIP ENG ANTIDETONATION TANK MAX QUANTITY" +Unit = "Gallons" +CanSet = False +Indexed = True +Description = "Indexed from 1. This value set in the Aircraft Configuration File." + +[RECIP_ENG_NITROUS_TANK_VALVE] +Id = RecipEngNitrousTankValve +CategoryId = Racing +SimVarName = "RECIP ENG NITROUS TANK VALVE" +Unit = "Bool" +CanSet = True +Indexed = True +Description = "Indexed from 1. Each engine can have one Nitrous fuel tank installed." + +[RECIP_ENG_NITROUS_TANK_QUANTITY] +Id = RecipEngNitrousTankQuantity +CategoryId = Racing +SimVarName = "RECIP ENG NITROUS TANK QUANTITY" +Unit = "Gallons" +CanSet = True +Indexed = True +Description = "Indexed from 1. Refer to the Mission Creation documentationfor the procedure for refilling tanks." + +[RECIP_ENG_NITROUS_TANK_MAX_QUANTITY] +Id = RecipEngNitrousTankMaxQuantity +CategoryId = Racing +SimVarName = "RECIP ENG NITROUS TANK MAX QUANTITY" +Unit = "Gallons" +CanSet = False +Indexed = True +Description = "Indexed from 1. This value set in the Aircraft Configuration File." + + + +# Environment ####################### +# +[category_Environment] + +[ABSOLUTE_TIME] +Id = AbsoluteTime +CategoryId = Environment +SimVarName = "ABSOLUTE TIME" +Unit = "Seconds" +CanSet = False +Indexed = False +Description = "Time, as referenced from 12:00 AM January 1, 0000" + +[ZULU_TIME] +Id = ZuluTime +CategoryId = Environment +SimVarName = "ZULU TIME" +Unit = "Seconds" +CanSet = False +Indexed = False +Description = "Greenwich Mean Time (GMT)" + +[ZULU_DAY_OF_WEEK] +Id = ZuluDayOfWeek +CategoryId = Environment +SimVarName = "ZULU DAY OF WEEK" +Unit = "Number" +CanSet = False +Indexed = False +Description = "GMT day of week" + +[ZULU_DAY_OF_MONTH] +Id = ZuluDayOfMonth +CategoryId = Environment +SimVarName = "ZULU DAY OF MONTH" +Unit = "Number" +CanSet = False +Indexed = False +Description = "GMT day of month" + +[ZULU_MONTH_OF_YEAR] +Id = ZuluMonthOfYear +CategoryId = Environment +SimVarName = "ZULU MONTH OF YEAR" +Unit = "Number" +CanSet = False +Indexed = False +Description = "GMT month of year" + +[ZULU_DAY_OF_YEAR] +Id = ZuluDayOfYear +CategoryId = Environment +SimVarName = "ZULU DAY OF YEAR" +Unit = "Number" +CanSet = False +Indexed = False +Description = "GMT day of year" + +[ZULU_YEAR] +Id = ZuluYear +CategoryId = Environment +SimVarName = "ZULU YEAR" +Unit = "Number" +CanSet = False +Indexed = False +Description = "GMT year" + +[LOCAL_TIME] +Id = LocalTime +CategoryId = Environment +SimVarName = "LOCAL TIME" +Unit = "Seconds" +CanSet = False +Indexed = False +Description = "Local time" + +[LOCAL_DAY_OF_WEEK] +Id = LocalDayOfWeek +CategoryId = Environment +SimVarName = "LOCAL DAY OF WEEK" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Local day of week" + +[LOCAL_DAY_OF_MONTH] +Id = LocalDayOfMonth +CategoryId = Environment +SimVarName = "LOCAL DAY OF MONTH" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Local day of month" + +[LOCAL_MONTH_OF_YEAR] +Id = LocalMonthOfYear +CategoryId = Environment +SimVarName = "LOCAL MONTH OF YEAR" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Local month of year" + +[LOCAL_DAY_OF_YEAR] +Id = LocalDayOfYear +CategoryId = Environment +SimVarName = "LOCAL DAY OF YEAR" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Local day of year" + +[LOCAL_YEAR] +Id = LocalYear +CategoryId = Environment +SimVarName = "LOCAL YEAR" +Unit = "Number" +CanSet = False +Indexed = False +Description = "Local year" + +[TIME_ZONE_OFFSET] +Id = TimeZoneOffset +CategoryId = Environment +SimVarName = "TIME ZONE OFFSET" +Unit = "Seconds" +CanSet = False +Indexed = False +Description = "Local time difference from GMT" + + + +# SlingsandHoists ####################### +# +[category_SlingsandHoists] + +[NUM_SLING_CABLES] +Id = NumSlingCables +CategoryId = SlingsandHoists +SimVarName = "NUM SLING CABLES" +Unit = "Number" +CanSet = False +Indexed = False +Description = "The number of sling cables (not hoists) that are configured for the aircraft. Refer to the document Notes on Aircraft Systems." + +[PAYLOAD_STATION_OBJECT] +Id = PayloadStationObject +CategoryId = SlingsandHoists +SimVarName = "PAYLOAD STATION OBJECT" +Unit = "String" +CanSet = False +Indexed = True +Description = "Places the named object at the payload station identified by the index (starting from 1). The string is the Container name (refer to the title property of Simulation Object Configuration Files)." + +[PAYLOAD_STATION_NUM_SIMOBJECTS] +Id = PayloadStationNumSimobjects +CategoryId = SlingsandHoists +SimVarName = "PAYLOAD STATION NUM SIMOBJECTS" +Unit = "Number" +CanSet = False +Indexed = True +Description = "The number of objects at the payload station (indexed from 1)." + +[SLING_OBJECT_ATTACHED] +Id = SlingObjectAttached +CategoryId = SlingsandHoists +SimVarName = "SLING OBJECT ATTACHED" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "If units are set as boolean, returns True if a sling object is attached. If units are set as a string, returns the container title of the object. There can be multiple sling positions, indexed from 1. The sling positions are set in the Aircraft Configuration File." + +[SLING_CABLE_BROKEN] +Id = SlingCableBroken +CategoryId = SlingsandHoists +SimVarName = "SLING CABLE BROKEN" +Unit = "Bool" +CanSet = False +Indexed = True +Description = "True if the cable is broken." + +[SLING_CABLE_EXTENDED_LENGTH] +Id = SlingCableExtendedLength +CategoryId = SlingsandHoists +SimVarName = "SLING CABLE EXTENDED LENGTH" +Unit = "Feet" +CanSet = True +Indexed = True +Description = "The length of the cable extending from the aircraft." + +[SLING_ACTIVE_PAYLOAD_STATION] +Id = SlingActivePayloadStation +CategoryId = SlingsandHoists +SimVarName = "SLING ACTIVE PAYLOAD STATION" +Unit = "Number" +CanSet = True +Indexed = True +Description = "The payload station (identified by the parameter) where objects will be placed from the sling (identified by the index)." + +[SLING_HOIST_PERCENT_DEPLOYED] +Id = SlingHoistPercentDeployed +CategoryId = SlingsandHoists +SimVarName = "SLING HOIST PERCENT DEPLOYED" +Unit = "Percent over 100" +CanSet = False +Indexed = True +Description = "The percentage of the full length of the sling cable deployed." + +[IS_ATTACHED_TO_SLING] +Id = IsAttachedToSling +CategoryId = SlingsandHoists +SimVarName = "IS ATTACHED TO SLING" +Unit = "Bool" +CanSet = False +Indexed = False +Description = "Set to true if this object is attached to a sling." + + + diff --git a/MSFSTouchPortalPlugin/Constants/Categories.cs b/MSFSTouchPortalPlugin/Constants/Categories.cs index 992634b..d06cba4 100644 --- a/MSFSTouchPortalPlugin/Constants/Categories.cs +++ b/MSFSTouchPortalPlugin/Constants/Categories.cs @@ -1,4 +1,5 @@ using MSFSTouchPortalPlugin.Enums; +using System.Linq; namespace MSFSTouchPortalPlugin.Constants { @@ -26,6 +27,9 @@ internal static class Categories /* SimSystem, */ "System", }; + public static string[] ListAll => categoryNames; + public static string[] ListUsable => categoryNames[2..^0]; // w/out None and Plugin + /// /// Returns the category name for given enum value, or a blank string if the id is invalid.. /// @@ -35,6 +39,24 @@ internal static string CategoryName(Groups catId) { return string.Empty; } + /// + /// Returns the category ID for given name, or Groups.None if the string is invalid.. + /// + internal static Groups CategoryId(string name) => + categoryNames.ToList().IndexOf(name) is var idx && idx > -1 ? (Groups)idx : Groups.None; + + /// + /// Places the category ID for given name in the out parameter, and returns true if string was valid. + /// + internal static bool TryGetCategoryId(string name, out Groups id) { + if (categoryNames.ToList().IndexOf(name) is var idx && idx > -1) { + id = (Groups)idx; + return true; + } + id = Groups.None; + return false; + } + /// /// Returns value with the category name prepended to it using the common separator string. /// diff --git a/MSFSTouchPortalPlugin/Constants/Units.cs b/MSFSTouchPortalPlugin/Constants/Units.cs index 95d459d..275418f 100644 --- a/MSFSTouchPortalPlugin/Constants/Units.cs +++ b/MSFSTouchPortalPlugin/Constants/Units.cs @@ -28,6 +28,14 @@ internal static class Units { /// internal static bool IsRealType(string unit) => !IsStringType(unit) && !IsBooleanType(unit) && !IsIntegralType(unit); + internal static string NormalizedUnit(string unit) { + if (ListAll.FirstOrDefault(u => u.ToLower() == unit.ToLower()) is var result && result != null) + return result; + return null; + } + + #region Unit Names + internal const string amp = "amp"; internal const string ampere = "ampere"; internal const string amperes = "amperes"; @@ -374,5 +382,563 @@ internal static class Units { internal const string yd3 = "yd3"; internal const string year = "year"; internal const string years = "years"; + + #endregion + + #region Unit List Arrays + + // eliminates duplicates and obscure stuff + static public readonly string[] ListUsable = new string[] + { + amp, + amps, + atm, + atmospheres, + bar, + Bco16, + bels, + Bool, + celsius, + cm, + cm2, + cm3, + cmHg, + cucm, + cuft, + cuin, + cukm, + cum, + cumm, + cuyd, + days, + decibels, + decimiles, + decinmiles, + degrees, + degreeslatitude, + degreeslongitude, + degreespersecond, + Enum, + fahrenheit, + farenheit, + feet, + feetminute, + feetperminute, + feetpersecond, + feetpersecondsquared, + feetsecond, + flags, + foot, + footpersecondsquared, + FrequencyADFBCD32, + FrequencyBCD16, + FrequencyBCD32, + ft, + ft2, + ft3, + ftlbpersecond, + ftlbs, + ftmin, + gallons, + geepounds, + GForce, + gph, + grads, + halfs, + hectopascals, + hours, + hoursover10, + Hz, + inch, + in2, + in3, + inches, + inHg, + kelvin, + keyframes, + kg, + kgfmeter, + kgfmeters, + KgFSqCm, + KHz, + kilogramforcepersquarecentimeter, + kilogrammeters, + kilograms, + kilogramsmetersquared, + kilogramspercubicmeter, + kilogramspersecond, + kilometershour, + kilometersperhour, + kilopascal, + km, + km2, + km3, + knot, + knots, + knotsscaler128, + kPa, + kph, + lbffeet, + lbs, + liters, + litersperhour, + m, + m2, + m3, + mach, + mach3d2over64k, + mask, + mbars, + meters, + meterscubed, + meterscubedpersecond, + metersecond, + meterslatitude, + metersperminute, + meterspersecond, + meterspersecondscaler256, + meterspersecondsquared, + metersscaler256, + meterssecond, + MHz, + miles, + milesperhour, + millibars, + millibarscaler16, + millibarsscaler16, + millimeters, + minutes, + minutesperround, + mm2, + mm3, + mmHg, + morethanahalf, + mph, + ms, + nauticalmiles, + newtonspersquaremeter, + niceminutesperround, + Nm, + nmiles, + number, + Pa, + part, + pascals, + percent, + percentage, + percentover100, + percentscaler16k, + percentscaler2pow23, + percentscaler32k, + perdegree, + perhour, + perminute, + perradian, + persecond, + position, + position128, + position16k, + position32k, + poundalfeet, + poundforcepersquarefoot, + poundforcepersquareinch, + pounds, + poundscaler256, + poundsperhour, + poundsscaler256, + pph, + psf, + psfscaler16k, + psi, + psi4over16k, + psiscaler16k, + quarts, + radians, + radianspersecond, + rankine, + ratio, + rounds, + rpm, + rpm1over16k, + scaler, + seconds, + Slugft3, + slugs, + slugsfeetsquared, + Slugspercubicfeet, + Slugspercubicfoot, + sqcm, + sqft, + sqin, + sqkm, + sqm, + sqmm, + squaremile, + squaremiles, + squaremillimeter, + squaremillimeters, + sqyd, + String, + thirds, + times, + volts, + Watts, + yards, + yd2, + yd3, + years, + }; + + static public readonly string[] ListAll = new string[] + { + amp, + ampere, + amperes, + amps, + angl16, + angl32, + atm, + atmosphere, + atmospheres, + bar, + bars, + Bco16, + bel, + bels, + Bool, + Boolean, + boostcmHg, + boostinHg, + boostpsi, + celsius, + celsiusfs7egt, + celsiusfs7oiltemp, + celsiusscaler1256, + celsiusscaler16k, + celsiusscaler256, + centimeter, + centimeterofmercury, + centimeters, + centimetersofmercury, + cm, + cm2, + cm3, + cmHg, + cubiccentimeter, + cubiccentimeters, + cubicfeet, + cubicfoot, + cubicinch, + cubicinches, + cubickilometer, + cubickilometers, + cubicmeter, + cubicmeters, + cubicmile, + cubicmiles, + cubicmillimeter, + cubicmillimeters, + cubicyard, + cubicyards, + cucm, + cuft, + cuin, + cukm, + cum, + cumm, + cuyd, + day, + days, + decibel, + decibels, + decimile, + decimiles, + decinmile, + decinmiles, + degree, + degreeangl16, + degreeangl32, + degreelatitude, + degreelongitude, + degreepersecond, + degreepersecondang16, + degrees, + degreesangl16, + degreesangl32, + degreeslatitude, + degreeslongitude, + degreespersecond, + degreespersecondang16, + Enum, + fahrenheit, + farenheit, + feet, + feetminute, + feetperminute, + feetpersecond, + feetpersecondsquared, + feetsecond, + flags, + foot, + footpersecondsquared, + footpound, + footpounds, + FrequencyADFBCD32, + FrequencyBCD16, + FrequencyBCD32, + fs7chargingamps, + fs7oilquantity, + ft, + ft2, + ft3, + ftlbpersecond, + ftlbs, + ftmin, + gallon, + gallonperhour, + gallons, + gallonsperhour, + geepound, + geepounds, + GForce, + GForce624scaled, + GLOBALPdeltaheadingrate, + GLOBALPeng1manifoldpressure, + GLOBALPeng1oilprs, + GLOBALPeng1oiltmp, + GLOBALPverticalspeed, + gph, + grad, + grads, + half, + halfs, + hectopascal, + hectopascals, + Hertz, + hour, + hourover10, + hours, + hoursover10, + Hz, + inch, + in2, + in3, + inches, + inchesofmercury, + inchofmercury, + inHg, + inHg64over64k, + kelvin, + keyframe, + keyframes, + kg, + kgfmeter, + kgfmeters, + KgFSqCm, + KHz, + kilogram, + kilogramforcepersquarecentimeter, + kilogrammeter, + kilogrammeters, + kilogrammetersquared, + kilogrampercubicmeter, + kilogrampersecond, + kilograms, + kilogramsmetersquared, + kilogramspercubicmeter, + kilogramspersecond, + Kilohertz, + kilometer, + kilometerhour, + kilometerperhour, + kilometers, + kilometershour, + kilometersperhour, + kilopascal, + km, + km2, + km3, + knot, + knots, + knotscaler128, + knotsscaler128, + kPa, + kph, + lbffeet, + lbs, + liter, + literperhour, + liters, + litersperhour, + m, + m2, + m3, + mach, + mach3d2over64k, + machs, + mask, + mbar, + mbars, + Megahertz, + meter, + metercubed, + metercubedpersecond, + meterlatitude, + meterperminute, + meterpersecond, + meterpersecondscaler256, + meterpersecondsquared, + meters, + meterscaler256, + meterscubed, + meterscubedpersecond, + metersecond, + meterslatitude, + metersperminute, + meterspersecond, + meterspersecondscaler256, + meterspersecondsquared, + metersscaler256, + meterssecond, + MHz, + mile, + mileperhour, + miles, + milesperhour, + millibar, + millibars, + millibarscaler16, + millibarsscaler16, + millimeter, + millimeterofmercury, + millimeterofwater, + millimeters, + millimetersofmercury, + millimetersofwater, + minute, + minuteperround, + minutes, + minutesperround, + mm2, + mm3, + mmHg, + morethanahalf, + mph, + ms, + nauticalmile, + nauticalmiles, + newtonmeter, + newtonmeters, + newtonpersquaremeter, + newtonspersquaremeter, + niceminuteperround, + niceminutesperround, + Nm, + nmile, + nmiles, + number, + numbers, + Pa, + part, + pascal, + pascals, + percent, + percentage, + percentover100, + percentscaler16k, + percentscaler2pow23, + percentscaler32k, + perdegree, + perhour, + perminute, + perradian, + persecond, + position, + position128, + position16k, + position32k, + pound, + poundalfeet, + poundforcepersquarefoot, + poundforcepersquareinch, + poundperhour, + pounds, + poundscaler256, + poundsperhour, + poundsscaler256, + pph, + psf, + psfscaler16k, + psi, + psi4over16k, + psifs7oilpressure, + psiscaler16k, + quart, + quarts, + radian, + radianpersecond, + radians, + radianspersecond, + rankine, + ratio, + revolutionperminute, + revolutionsperminute, + round, + rounds, + rpm, + rpm1over16k, + rpms, + scaler, + second, + seconds, + slug, + slugfeetsquared, + Slugft3, + Slugpercubicfeet, + Slugpercubicfoot, + slugs, + slugsfeetsquared, + Slugspercubicfeet, + Slugspercubicfoot, + sqcm, + sqft, + sqin, + sqkm, + sqm, + sqmm, + squarecentimeter, + squarecentimeters, + squarefeet, + squarefoot, + squareinch, + squareinches, + squarekilometer, + squarekilometers, + squaremeter, + squaremeters, + squaremile, + squaremiles, + squaremillimeter, + squaremillimeters, + squareyard, + squareyards, + sqyd, + String, + third, + thirds, + times, + volt, + volts, + Watt, + Watts, + yard, + yards, + yd2, + yd3, + year, + years, + }; + + #endregion } } diff --git a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj index caa2899..29a0ecb 100644 --- a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj +++ b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj @@ -29,10 +29,6 @@ - - - - @@ -100,6 +96,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs index 67442e0..1085b4e 100644 --- a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs +++ b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs @@ -28,9 +28,60 @@ internal static class PluginMapping [TouchPortalActionText("0", Id = "Value", Label = "Value")] [TouchPortalActionSwitch(false, Id = "RelAI", Label = "Release AI")] public static readonly object SetSimVar; + + [TouchPortalAction(PluginActions.AddCustomSimVar, "Add Custom Simulator Variable", + "Request Simulator Variable Name: Index (if any): Category: " + + "Units: Format: Default Value: " + + "Settable: Update Period: Update Interval: Delta Epsilon: ", + "{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}")] + [TouchPortalActionText("SIMULATOR VARIABLE FULL NAME", Id = "VarName", Label = "Simulator Variable Name")] + [TouchPortalActionNumeric(0, 0, 99, false, Id = "VarIndex", Label = "Variable Index")] + [TouchPortalActionChoice("", "", Id = "CatId", Label = "Category")] + [TouchPortalActionChoice("", "", Id = "Unit", Label = "Unit Name")] + [TouchPortalActionText("F2", Id = "Format", Label = "Formatting String")] + [TouchPortalActionText("0", Id = "DfltVal", Label = "Default Value")] + [TouchPortalActionSwitch(false, Id = "CanSet", Label = "Settable")] + [TouchPortalActionChoice(new[] { /*"Never",*/ "Once", "VisualFrame", "SimFrame", "Second", "Millisecond", }, "SimFrame", Id = "UpdPer", Label = "Update Period")] + [TouchPortalActionNumeric(0, 0, int.MaxValue, false, Id = "UpdInt", Label = "Interval")] + [TouchPortalActionNumeric(0.0099999, 0.0, float.MaxValue, true, Id = "Epsilon", Label = "Delta Epsilon")] + public static readonly object AddCustomSimVar; + + [TouchPortalAction(PluginActions.AddKnownSimVar, "Add Custom Simulator Variable From List", + "Request Simulator Variable Name Index (if any): Category: " + + "Units: Format: Default Value: " + + "Update Period: Update Interval: Delta Epsilon: ", + "{0}{1}{2}{3}{4}{5}{6}{7}{8}")] + [TouchPortalActionChoice("", "", Id = "VarName", Label = "Simulator Variable Name")] + [TouchPortalActionNumeric(0, 0, 99, false, Id = "VarIndex", Label = "Variable Index")] + [TouchPortalActionChoice("", "", Id = "CatId", Label = "Category")] + [TouchPortalActionChoice("", "", Id = "Unit", Label = "Unit Name")] + [TouchPortalActionText("F2", Id = "Format", Label = "Formatting String")] + [TouchPortalActionText("0", Id = "DfltVal", Label = "Default Value")] + //[TouchPortalActionSwitch(false, Id = "CanSet", Label = "Settable")] // we should know if it's settable from the imported data + [TouchPortalActionChoice(new[] { /*"Never",*/ "Once", "VisualFrame", "SimFrame", "Second", "Millisecond", }, "SimFrame", Id = "UpdPer", Label = "Update Period")] + [TouchPortalActionNumeric(0, 0, int.MaxValue, false, Id = "UpdInt", Label = "Interval")] + [TouchPortalActionNumeric(0.0099999, 0.0, float.MaxValue, true, Id = "Epsilon", Label = "Delta Epsilon")] + public static readonly object AddKnownSimVar; + + [TouchPortalAction(PluginActions.RemoveSimVar, "Remove a Simulator Variable", + "Remove an existing Simulator Variable. Note that static TP States cannot be removed.", + "Remove Simulator Variable {0}")] + [TouchPortalActionChoice("", "", Id = "VarName", Label = "Simulator Variable")] + public static readonly object RemoveSimVar; + + [TouchPortalAction(PluginActions.LoadSimVars, "Load SimVar Definitions From File", "Load a set of simulator variable state definitions from a configuration file.", "Load from file {0} (name only for user config. folder, or full path with file name)")] + [TouchPortalActionFile("CustomStates.ini", Id = "VarsFile", Label = "Load From File")] + public static readonly object LoadSimVars; + + [TouchPortalAction(PluginActions.SaveSimVars, "Save SimVar Definitions To File", "Save the current simulator variable state definitions to a configuration file.", "Save {0} states to file {1} (name only for user config. folder, or full path with file name)")] + [TouchPortalActionChoice(new[] { "Custom", "All" }, Id = "VarsSet", Label = "Variables Set")] + [TouchPortalActionText("CustomStates.ini", Id = "VarsFile", Label = "Save to File")] + //[TouchPortalActionFile("CustomStates.ini", Id = "VarsFile", Label = "Save to File")] // doesn't allow freeform entry or selecting a non-existing file as of TP 3.0.10 + [TouchPortalActionMapping(PluginActions.SaveCustomSimVars, "Custom")] + [TouchPortalActionMapping(PluginActions.SaveAllSimVars, "All")] + public static readonly object SaveSimVars; } - [TouchPortalCategory(Groups.Plugin)] [TouchPortalSettingsContainer] public static class Settings { @@ -101,6 +152,11 @@ public enum PluginActions : short Connection, ActionRepeatInterval, SetSimVar, + AddCustomSimVar, + AddKnownSimVar, + RemoveSimVar, + SaveSimVars, + LoadSimVars, // Action choice mapping IDs ToggleConnection, @@ -112,6 +168,8 @@ public enum PluginActions : short ActionRepeatIntervalDec, ActionRepeatIntervalSet, + SaveCustomSimVars, + SaveAllSimVars, } // Dynamically generated SimConnect client event IDs are "parented" to this enum type, diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 63417c5..ffa3f8d 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.Collections.Concurrent; using System.Linq; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using TouchPortalSDK; @@ -43,10 +44,12 @@ internal class PluginService : IPluginService, ITouchPortalEventHandler private bool _quitting; private Dictionary actionsDictionary = new(); - private Dictionary statesDictionary = new(); private Dictionary pluginSettingsDictionary = new(); - private readonly ConcurrentBag _customIntervalStates = new(); // IDs of SimVars which need periodic value polling; concurrent because the polling happens in separate task from setter. + private Dictionary _importedSimVars = new(); + private readonly ConcurrentDictionary _statesDictionary = new(); + private readonly ConcurrentDictionary _customIntervalStates = new(); // IDs of SimVars which need periodic value polling; concurrent because the polling happens in separate task from setter. private readonly Dictionary _settableSimVarIds = new(); // mapping of settable SimVar IDs for lookup in statesDictionary + private readonly Dictionary _addedSimVars = new(); // keep track of definitions added by user private readonly List _dynamicStateIds = new(); // keep track of dynamically created states for clearing them if/when reloading sim var state files private readonly ConcurrentDictionary _repeatingActionTimers = new(); @@ -80,10 +83,10 @@ public PluginService(IHostApplicationLifetime hostApplicationLifetime, ILogger

/// The cancellation token public Task StartAsync(CancellationToken cancellationToken) { - _logger.LogDebug("Starting up..."); - _cancellationToken = cancellationToken; + _logger.LogInformation($"======= {PluginId} Starting ======="); + if (!Initialize()) { _hostApplicationLifetime.StopApplication(); return Task.CompletedTask; @@ -117,6 +120,7 @@ public Task StopAsync(CancellationToken cancellationToken) { try { _client.Close(); } catch (Exception) { /* ignore */ } } + _logger.LogInformation($"======= {PluginId} Stopped ======="); return Task.CompletedTask; } @@ -125,6 +129,8 @@ public Task StopAsync(CancellationToken cancellationToken) { ///

private bool Initialize() { SetupEventLists(); // set these up first to have access to defined plugin settings + // sent to TP upon initial connection + _importedSimVars = _pluginConfig.ImportSimVars().ToDictionary(s => s.SimVarName, s => s); if (!_client.Connect()) { _logger.LogCritical("Failed to connect to Touch Portal! Quitting."); @@ -170,7 +176,8 @@ private async Task PluginEventsTask() { _logger.LogDebug("PluginEventsTask task started."); try { while (_simConnectService.IsConnected() && !_simTasksCancelToken.IsCancellationRequested) { - foreach (Timer tim in _repeatingActionTimers.Values) + IReadOnlyCollection timers = _repeatingActionTimers.Values.ToArray(); + foreach (Timer tim in timers) tim.Tick(); CheckPendingRequests(); await Task.Delay(25, _simTasksCancelToken); @@ -186,15 +193,22 @@ private async Task PluginEventsTask() { // runs in PluginEventsTask private void CheckPendingRequests() { - foreach (var def in _customIntervalStates) { + IReadOnlyCollection vars = _customIntervalStates.Values.ToArray(); + foreach (var simVar in vars) { // Check if a value update is required based on the SimVar's internal tracking mechanism. - if (statesDictionary.TryGetValue(def, out var s) && s.UpdateRequired) { - s.SetPending(true); - _simConnectService.RequestDataOnSimObjectType(s); + if (simVar.UpdateRequired) { + simVar.SetPending(true); + _simConnectService.RequestDataOnSimObjectType(simVar); } } } + // this is done once at startup before any connections are established + private void SetupEventLists() { + actionsDictionary = _reflectionService.GetActionEvents(); + pluginSettingsDictionary = _reflectionService.GetSettings(); + } + #endregion Startup, Shutdown and Processing Tasks #region SimConnect Events ///////////////////////////////////// @@ -206,7 +220,7 @@ private void SimConnectEvent_OnConnect() { UpdateSimConnectState(); - // Register Actions + // Register Action events var eventMap = _reflectionService.GetClientEventIdToNameMap(); foreach (var m in eventMap) { _simConnectService.MapClientEventToSimEvent(m.Key, m.Value.EventName); @@ -215,7 +229,8 @@ private void SimConnectEvent_OnConnect() { // must be called after adding notifications _simConnectService.SetNotificationGroupPriorities(); - SetupSimVars(); + // Register SimVars for States + RegisterAllSimVars(); // start checking timer events _pluginEventsTask = Task.Run(PluginEventsTask); @@ -224,7 +239,7 @@ private void SimConnectEvent_OnConnect() { private void SimConnectEvent_OnDataUpdateEvent(Definition def, Definition req, object data) { // Lookup State Mapping. - if (!statesDictionary.TryGetValue(def, out SimVarItem simVar)) + if (!_statesDictionary.TryGetValue(def, out SimVarItem simVar)) return; // Update SimVarItem value and TP state on changes. @@ -254,12 +269,15 @@ private void SimConnectEvent_OnDisconnect() { #endregion SimConnect Events - #region Plugin Events and Handlers ///////////////////////////////////// + #region SimVar Setup Handlers ///////////////////////////////////// + // This is done once after TP connects or upon the "Reload States" action. + // It is safe to be called multiple times if config files need to be reloaded. SimConnect does not have to be connected. private void SetupSimVars() { // We may need to generate all, some, or no states dynamically. bool allStatesDynamic = (_entryFileType == EntryFileType.NoStates); bool someStateDynamic = false; + IReadOnlyCollection simVars; IEnumerable defaultStates = Array.Empty(); // in case we need to create _some_ custom states dynamically // First check if we're using a custom config. @@ -277,43 +295,170 @@ private void SetupSimVars() { _logger.LogInformation($"Loading default SimVar State file '{PluginConfig.AppConfigFolder}/{PluginConfig.StatesConfigFile}'."); } // Load the vars. PluginConfig tracks which files should be loaded, defaults or custom. - statesDictionary = _pluginConfig.LoadSimVarStateConfigs().ToDictionary(s => s.Def, s => s); + simVars = _pluginConfig.LoadSimVarStateConfigs(); _logger.LogInformation($"Loaded {simVars.Count} SimVars from file(s)."); - // clear out any old data - _customIntervalStates.Clear(); - _settableSimVarIds.Clear(); - RemoveDynamicStates(); // if any - _simConnectService.ClearAllDataDefinitions(); // if any (SimConnectService keeps track of this internally) - - // Now register SimVars and possibly create TP states - foreach (var simVar in statesDictionary.Values) { - // Does this var need value polling? - if (simVar.NeedsScheduledRequest) - _customIntervalStates.Add(simVar.Def); - if (simVar.CanSet) - _settableSimVarIds.Add(simVar.Id, simVar.Def); - // Need a dynamic state? - if (allStatesDynamic || (someStateDynamic && !defaultStates.Contains(simVar.Id))) { - _dynamicStateIds.Add(simVar.TouchPortalStateId); // keep track for removing them later if needed - _client.CreateState(simVar.TouchPortalStateId, Categories.PrependFullCategoryName(simVar.CategoryId, simVar.Name), simVar.DefaultValue); - _logger.LogDebug($"Created dynamic state {simVar.TouchPortalStateId}'."); - } - // Register it. If the SimVar gets regular updates (not custom polling) then this also starts the data requests for this value. - _simConnectService.RegisterToSimConnect(simVar); + // Now create the SimVars and track them. We're probably not connected to SimConnect at this point so the registration may happen later. + foreach (var simVar in simVars) + AddSimVar(simVar, allStatesDynamic || (someStateDynamic && !defaultStates.Contains(simVar.Id)), true); + + UpdateAllSimVarsList(); + UpdateSettableSimVarsList(); + UpdateKnownSimVars(); + } + + private void RegisterAllSimVars() { + if (_simConnectService.IsConnected()) + foreach (var simVar in _statesDictionary.Values) + _simConnectService.RegisterToSimConnect(simVar); + } + + private bool AddSimVar(SimVarItem simVar, bool dynamicState = true, bool postponeUpdate = false) { + if (simVar == null) + return false; + + if (_statesDictionary.ContainsKey(simVar.Def)) + RemoveSimVar(simVar, true); + + // Register it. If the SimVar gets regular updates (not custom polling) then this also starts the data requests for this value. + // If we're not connected now then the var will be registered in RegisterAllSimVars() when we do connect. + if (_simConnectService.IsConnected() && !_simConnectService.RegisterToSimConnect(simVar)) + return false; + + _statesDictionary.TryAdd(simVar.Def, simVar); + + // Does this var need value polling? + if (simVar.NeedsScheduledRequest) + _customIntervalStates.TryAdd(simVar.Def, simVar); + if (simVar.CanSet) + _settableSimVarIds.TryAdd(simVar.Id, simVar.Def); + // Need a dynamic state? + if (dynamicState && !_dynamicStateIds.Contains(simVar.TouchPortalStateId)) { + _dynamicStateIds.Add(simVar.TouchPortalStateId); // keep track for removing them later if needed + _client.CreateState(simVar.TouchPortalStateId, Categories.PrependFullCategoryName(simVar.CategoryId, simVar.Name), simVar.DefaultValue); + _logger.LogTrace($"Created dynamic state {simVar.TouchPortalStateId}'."); } - UpdateSettableStatesList(); + if (!postponeUpdate) { + UpdateAllSimVarsList(); + UpdateSettableSimVarsList(); + UpdateKnownSimVars(); // reload to mark any sim vars already used (experimental) + } + _logger.LogTrace($"Added SimVar: {simVar.ToDebugString()}"); + return true; } - private void RemoveDynamicStates() { - foreach (var id in _dynamicStateIds) - _client.RemoveState(id); + // note that the stored list of custom added SimVars is not affected here so that we can reload those during a SimConnect restart + private bool RemoveSimVar(SimVarItem simVar, bool postponeUpdate = false) { + if (simVar == null || !_statesDictionary.TryRemove(simVar.Def, out _)) + return false; + _customIntervalStates.TryRemove(simVar.Def, out _); + _settableSimVarIds.Remove(simVar.Id); + _simConnectService.ClearDataDefinition(simVar.Def); + if (_dynamicStateIds.Remove(simVar.TouchPortalStateId)) + _client.RemoveState(simVar.TouchPortalStateId); + if (!postponeUpdate) { + UpdateAllSimVarsList(); + UpdateSettableSimVarsList(); + UpdateKnownSimVars(); // reload to un-mark the SimVar just removed (experimental) + } + return true; + } + + private bool AddCustomSimVar(SimVarItem simVar) { + if (!AddSimVar(simVar, true)) + return false; + if (_addedSimVars.FirstOrDefault(s => s.Value == simVar.Id) is var old && !string.IsNullOrEmpty(old.Value)) + _addedSimVars.Remove(old.Key); + _addedSimVars.TryAdd(simVar.Def, simVar.Id); + return true; + } + + private bool RemoveCustomSimVar(SimVarItem simVar) { + if (RemoveSimVar(simVar)) + return _addedSimVars.Remove(simVar.Def); + return false; + } + + private void LoadCustomSimVarsFromFile(string filepath) { + var simVars = _pluginConfig.LoadSimVarItems(true, filepath); + foreach (var simVar in simVars) { + if (_addedSimVars.ContainsKey(simVar.Def)) + RemoveCustomSimVar(simVar); + AddCustomSimVar(simVar); + } + _logger.LogInformation($"Loaded {simVars.Count} SimVar States from file '{filepath}'"); + return; } - private void UpdateSettableStatesList() { - var list = (from s in statesDictionary.Values where s.CanSet select Categories.PrependCategoryName(s.CategoryId, s.Name) + $" [{s.Id}]").OrderBy(n => n).ToArray(); - _client.ChoiceUpdate(PluginId + ".Plugin.Action.SetSimVar.Data.VarName", list); + private void SaveSimVarsToFile(string filepath, bool customOnly = true) { + bool ok = false; + if (customOnly) + ok = _pluginConfig.SaveSimVarItems((from s in _statesDictionary.Values where _addedSimVars.ContainsKey(s.Def) select s), true, filepath); + else + ok = _pluginConfig.SaveSimVarItems(_statesDictionary.Values, true, filepath); + if (ok) + _logger.LogInformation($"Saved {(customOnly ? "Custom" : "All")} SimVar States to file '{filepath}'"); + } + + #endregion SimVar Setup Handlers + + #region Data Updaters ///////////////////////////////////// + + // Action data list updaters + + // common handler for other action list updaters + private void UpdateActionDataList(PluginActions actionId, string dataId, IReadOnlyCollection data, string instanceId = null) { + _client.ChoiceUpdate(PluginId + $".Plugin.Action.{actionId}.Data.{dataId}", data.ToArray(), instanceId); + } + + // List of settable SimVars + private void UpdateSettableSimVarsList() { + var list = (from s in _statesDictionary.Values where s.CanSet select Categories.PrependCategoryName(s.CategoryId, s.Name) + $" [{s.Id}]").OrderBy(n => n); + UpdateActionDataList(PluginActions.SetSimVar, "VarName", list.ToArray()); + } + + // List of all current SimVars + private void UpdateAllSimVarsList() { + var list = (from s in _statesDictionary.Values select Categories.PrependCategoryName(s.CategoryId, s.Name) + $" [{s.Id}]").OrderBy(n => n); + UpdateActionDataList(PluginActions.RemoveSimVar, "VarName", list.ToArray()); + } + + // Unit lists + private void UpdateUnitsLists() { + UpdateActionDataList(PluginActions.AddCustomSimVar, "Unit", Units.ListUsable); + UpdateActionDataList(PluginActions.AddKnownSimVar, "Unit", Units.ListUsable); + } + + // This will re-populate the list of Units for an action instance, but put the default Unit for the selected SimVar at the top. + private void UpdateUnitsListForKnownSimVar(string varName, string instanceId) { + varName = GetImportedSimVarKeyFromName(varName); + if (varName != null && _importedSimVars.TryGetValue(varName, out SimVariable var)) { + var list = new []{ var.Unit }.Concat(Units.ListUsable); + UpdateActionDataList(PluginActions.AddKnownSimVar, "Unit", list.ToArray(), instanceId); + } + } + + // Available state/action categories + private void UpdateUCategoryLists() { + UpdateActionDataList(PluginActions.AddCustomSimVar, "CatId", Categories.ListUsable); + UpdateActionDataList(PluginActions.AddKnownSimVar, "CatId", Categories.ListUsable); + } + + // List of imported SimVariables + private void UpdateKnownSimVars() { + // select variable names with category info and mark if already used + var list = _importedSimVars.Values.Select(v => (_statesDictionary.Values.FirstOrDefault(s => s.SimVarName == v.SimVarName) == null ? "" : "* ") + v.TouchPortalSelectorName); + UpdateActionDataList(PluginActions.AddKnownSimVar, "VarName", list.ToArray()); + } + + // Misc. data update/clear + + private void UpdateSimConnectState() { + string stat = "true"; + if (!_simConnectService.IsConnected()) + stat = _simConnectionRequest.IsSet ? "connecting" : "false"; + _client.StateUpdate(PluginId + ".Plugin.State.Connected", stat); } private void ClearRepeatingActions() { @@ -324,6 +469,10 @@ private void ClearRepeatingActions() { } } + #endregion Data Updaters + + #region Plugin Action/Event Handlers ///////////////////////////////////// + private void ProcessPluginCommandAction(PluginActions actionId, double value = double.NaN) { switch (actionId) { case PluginActions.ToggleConnection: @@ -369,24 +518,22 @@ private void ProcessPluginCommandAction(PluginActions actionId, double value = d } private void SetSimVarValueFromActionData(string varName, string value, bool releaseAi) { - if (!varName.EndsWith(']') || (varName.IndexOf('[') is var brIdx && brIdx++ < 0)) { - _logger.LogWarning($"Could not find ID in SimVar Name: '{varName}'"); + if (!TryGetSimVarIdFromActionData(varName, out string varId)) { + _logger.LogError($"Could not find ID in SimVar Name: '{varName}'"); return; } - - var varId = varName[brIdx..^1]; - if (!_settableSimVarIds.TryGetValue(varId, out Definition def) || !statesDictionary.TryGetValue(def, out SimVarItem simVar)) { - _logger.LogWarning($"Could not find definition for settable SimVar Id: '{varId}' Name: '{varName}'"); + if (!_settableSimVarIds.TryGetValue(varId, out Definition def) || !_statesDictionary.TryGetValue(def, out SimVarItem simVar)) { + _logger.LogError($"Could not find definition for settable SimVar Id: '{varId}' Name: '{varName}'"); return; } if (simVar.IsStringType) { if (!simVar.SetValue(new StringVal(value))) { - _logger.LogWarning($"Could not set string value '{value}' for SimVar Id: '{varId}' Name: '{varName}'"); + _logger.LogError($"Could not set string value '{value}' for SimVar Id: '{varId}' Name: '{varName}'"); return; } } else if (!TryEvaluateValue(value, out double dVal) || !simVar.SetValue(dVal)) { - _logger.LogWarning($"Could not set numeric value '{value}' for SimVar Id: '{varId}' Name: '{varName}'"); + _logger.LogError($"Could not set numeric value '{value}' for SimVar Id: '{varId}' Name: '{varName}'"); return; } if (releaseAi && !_simConnectService.ReleaseAIControl(simVar.Def)) @@ -395,6 +542,90 @@ private void SetSimVarValueFromActionData(string varName, string value, bool rel _simConnectService.SetDataOnSimObject(simVar); } + private void AddSimVarFromActionData(PluginActions actId, ActionData data) { + if (!data.TryGetValue("VarName", out var varName) || + !data.TryGetValue("VarIndex", out var sIndex) || + !data.TryGetValue("CatId", out var sCatId) || + !data.TryGetValue("Unit", out var sUnit) || + !Categories.TryGetCategoryId(sCatId, out Groups catId) + ) { + _logger.LogWarning($"Could not parse required action parameters for {actId} from data: {string.Join(", ", data.Select(d => $"{d.Key}={d.Value}"))}"); + return; + } + bool canSet = false; + bool indexed = false; + string varId; + string name; + varName = varName.Trim(); + // imported vars are formatted in the data list with leading category name and possible ":N" suffix + if (actId == PluginActions.AddKnownSimVar) { + varName = GetImportedSimVarKeyFromName(varName); + } + // user-supplied name may contain a ":N" which would indicate an indexed type + else if (varName.IndexOf(':') is int colIdx && colIdx > -1) { + indexed = true; + varName = varName.Remove(colIdx); + } + // check if we've imported this var and have meta data + if (_importedSimVars.TryGetValue(varName, out SimVariable impSimVar)) { + varId = impSimVar.Id; + canSet = impSimVar.CanSet; + indexed = impSimVar.Indexed; + name = impSimVar.Name; + } + else { + canSet = data.TryGetValue("CanSet", out var sCanSet) && new BooleanString(sCanSet); + // if there was no :N in the var name but the user provided a non-zero index, we assume they meant it + indexed = indexed || sIndex != "0"; + // Create a reasonable string for a TP state ID + varId = Regex.Replace(varName.ToLower(), @"(?:\b|\W|_)(\w)", m => (m.Groups[1].ToString().ToUpper())); + name = varName; // for lack of anything better + } + if (indexed) { + if (sIndex == "0") + sIndex = "1"; + varName = string.Concat(varName, ":", sIndex); + } + // create the SimVarItem from collected data + var simVar = new SimVarItem() { + Id = varId, + Name = name, + SimVarName = varName, + CategoryId = catId, + Unit = sUnit, + CanSet = canSet, + StringFormat = data.GetValueOrDefault("Format", string.Empty).Trim(), + DefaultValue = data.GetValueOrDefault("DfltVal", string.Empty).Trim(), + TouchPortalStateId = $"{PluginId}.{catId}.State.{varId}" + }; + if (data.TryGetValue("UpdPer", out var sPeriod) && Enum.TryParse(sPeriod, out UpdatePeriod period)) + simVar.UpdatePeriod = period; + if (data.TryGetValue("UpdInt", out var sInterval) && uint.TryParse(sInterval, out uint interval)) + simVar.UpdateInterval = interval; + if (data.TryGetValue("Epsilon", out var sEpsilon) && float.TryParse(sEpsilon, out float epsilon)) + simVar.DeltaEpsilon = epsilon; + + if (AddCustomSimVar(simVar)) + _logger.LogInformation($"Added new SimVar state from action data: {simVar.ToDebugString()}"); + else + _logger.LogError($"Failed to add SimVar from action data, check previous log messages. Action data: {string.Join(", ", data.Select(d => $"{d.Key}={d.Value}"))}"); + } + + + private void RemoveSimVarByActionDataName(string varName) { + if (!TryGetSimVarIdFromActionData(varName, out string varId)) { + _logger.LogWarning($"Could not find ID in SimVar Name: '{varName}'"); + return; + } + SimVarItem simVar = _statesDictionary.Values.FirstOrDefault(s => s.Id == varId); + if (simVar != null && RemoveSimVar(simVar)) + _logger.LogInformation($"Removed SimVar '{simVar.SimVarName}'"); + else + _logger.LogWarning($"Could not find definition for settable SimVar Id: '{varId}' from Name: '{varName}'"); + } + + // Main TP Action event handlers + private void ProcessEvent(ActionEvent actionEvent) { if (!actionsDictionary.TryGetValue(actionEvent.ActionId, out ActionEventType action)) return; @@ -433,6 +664,31 @@ private void ProcessInternalEvent(ActionEventType action, ActionData data) { break; } + case PluginActions.LoadSimVars: { + if (data.TryGetValue("VarsFile", out var filepath) && !string.IsNullOrWhiteSpace(filepath)) + LoadCustomSimVarsFromFile(filepath.Trim()); + break; + } + + case PluginActions.SaveSimVars: { + if (data.TryGetValue("VarsFile", out var filepath) && !string.IsNullOrWhiteSpace(filepath)) { + bool customOnly = !action.TryGetEventMapping(data.GetValueOrDefault("VarsSet", "Custom"), out Enum eventId) || (PluginActions)eventId == PluginActions.SaveCustomSimVars; + SaveSimVarsToFile(filepath.Trim(), customOnly); + } + break; + } + + case PluginActions.AddCustomSimVar: + case PluginActions.AddKnownSimVar: + AddSimVarFromActionData(pluginEventId, data); + break; + + case PluginActions.RemoveSimVar: { + if (data.TryGetValue("VarName", out var varName)) + RemoveSimVarByActionDataName(varName); + break; + } + default: break; } @@ -486,30 +742,11 @@ private void ProcessPluginSettings(IReadOnlyCollection settings) { PluginConfig.UserStateFiles = Settings.UserStateFiles.StringValue; // will (re-)set to default if needed. // compare with actual current config values (not Settings) because they may not have changed even if settings string did // states dict will be empty on initial startup - if (p[0] != PluginConfig.UserConfigFolder || p[1] != PluginConfig.UserStateFiles) + if (!_statesDictionary.Any() || p[0] != PluginConfig.UserConfigFolder || p[1] != PluginConfig.UserStateFiles) SetupSimVars(); } - private void UpdateSimConnectState() { - string stat = "true"; - if (!_simConnectService.IsConnected()) - stat = _simConnectionRequest.IsSet ? "connecting" : "false"; - _client.StateUpdate(PluginId + ".Plugin.State.Connected", stat); - } - - private bool TryEvaluateValue(string strValue, out double value) { - value = double.NaN; - try { - value = Convert.ToDouble(_expressionEvaluator.Compute(strValue, null)); - } - catch (Exception e) { - _logger.LogWarning(e, $"Failed to convert data value '{strValue}' to numeric."); - return false; - } - return true; - } - - #endregion Plugin Events and Handlers + #endregion Plugin Action/Event Handlers #region TouchPortalSDK Events /////////////////////////////// @@ -536,6 +773,10 @@ public void OnInfoEvent(InfoEvent message) { _client.StateUpdate(PluginId + ".Plugin.State.RunningVersion", runtimeVer); _client.StateUpdate(PluginId + ".Plugin.State.EntryVersion", $"{tpVer & 0xFFFFFF:X}"); _client.StateUpdate(PluginId + ".Plugin.State.ConfigVersion", $"{tpVer >> 24:X}"); + + UpdateUCategoryLists(); + UpdateUnitsLists(); + UpdateKnownSimVars(); } public void OnSettingsEvent(SettingsEvent message) { @@ -582,7 +823,9 @@ public void OnClosedEvent(string message) { } public void OnListChangedEvent(ListChangeEvent message) { - // not implemented yet + // for now we only need to update the Units list of a SimVar selected in the AddKnownSimVar action + if (!string.IsNullOrWhiteSpace(message.InstanceId) && !string.IsNullOrWhiteSpace(message.Value) && message.ListId.EndsWith(PluginActions.AddKnownSimVar.ToString() + ".Data.VarName")) + UpdateUnitsListForKnownSimVar(message.Value, message.InstanceId); } public void OnConnecterChangeEvent(ConnectorChangeEvent message) { @@ -605,6 +848,36 @@ public void OnUnhandledEvent(string jsonMessage) { _logger?.LogDebug($"Unhandled message: {jsonMessage}"); } - #endregion + #endregion TouchPortalSDK Events + + #region Utilities /////////////////////////////// + + private bool TryEvaluateValue(string strValue, out double value) { + value = double.NaN; + try { + value = Convert.ToDouble(_expressionEvaluator.Compute(strValue, null)); + } + catch (Exception e) { + _logger.LogWarning(e, $"Failed to convert data value '{strValue}' to numeric."); + return false; + } + return true; + } + + private static bool TryGetSimVarIdFromActionData(string varName, out string varId) { + if (varName.EndsWith(']') && (varName.IndexOf('[') is var brIdx && brIdx++ > -1)) { + varId = varName[brIdx..^1]; + return true; + } + varId = string.Empty; + return false; + } + + private static string GetImportedSimVarKeyFromName(string name) => + name?.Split(" - ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Last().Replace(":N", "") ?? string.Empty; + + #endregion Utilities + } + } diff --git a/MSFSTouchPortalPlugin/Types/SimVariable.cs b/MSFSTouchPortalPlugin/Types/SimVariable.cs new file mode 100644 index 0000000..29120f5 --- /dev/null +++ b/MSFSTouchPortalPlugin/Types/SimVariable.cs @@ -0,0 +1,26 @@ + +namespace MSFSTouchPortalPlugin.Types +{ + // This container class is currently used for importing "known" SimVars/states from INI files to present in a UI to the user for selection. + internal class SimVariable + { + /// Unique ID string, used to generate TouchPortal state ID (and possibly other uses). + public string Id { get; set; } + /// Category for sorting/organizing. + public string CategoryId { get; set; } + /// Corresponding SimConnect SimVar name. + public string SimVarName { get; set; } + /// SimConnect unit name. + public string Unit { get; set; } + /// SimConnect settable value + public bool CanSet { get; set; } = false; + /// SimConnect expects an index value appended to the name + public bool Indexed { get; set; } + /// "Friendly" name for UI, typically defaults to SimConnect SimVar name. + public string Name { get; set; } + /// Long Description. + public string Description { get; set; } + /// A preformatted string to use in the TP UI when selecting variables. + public string TouchPortalSelectorName { get; set; } + } +} From a57a2fe6dbc082b3e5af119e34a71dc01807ffa3 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Mon, 11 Apr 2022 02:06:35 -0400 Subject: [PATCH 68/93] [SharpConfig] Updated to include Section.TryGetSetting(). --- .../lib/SharpConfig/CHANGELOG.txt | 2 ++ .../lib/SharpConfig/SharpConfig.dll | Bin 34816 -> 34816 bytes .../lib/SharpConfig/SharpConfig.pdb | Bin 136704 -> 136704 bytes 3 files changed, 2 insertions(+) diff --git a/MSFSTouchPortalPlugin/lib/SharpConfig/CHANGELOG.txt b/MSFSTouchPortalPlugin/lib/SharpConfig/CHANGELOG.txt index 7133a59..bda56f5 100644 --- a/MSFSTouchPortalPlugin/lib/SharpConfig/CHANGELOG.txt +++ b/MSFSTouchPortalPlugin/lib/SharpConfig/CHANGELOG.txt @@ -2,3 +2,5 @@ v3.2.9.2-mp (from v3.2.9.1) * Fixed that ConfigurationReader wasn't setting the Setting.RawValue properly, instead relying on a redundant string to string conversion. * Added Configuration.AlwaysQuoteStringValues option. * EnumStringConverter is case insensitive, a little faster, and returns null for a null value string (instead of default(double)). +* Added Section.TryGetSetting() method. +* Section settings accessors don't throw but return null/false instead. diff --git a/MSFSTouchPortalPlugin/lib/SharpConfig/SharpConfig.dll b/MSFSTouchPortalPlugin/lib/SharpConfig/SharpConfig.dll index f76183a6daa5605115654a594dd40512a998b0da..219d0625363bc43821641122bf218ba023323fc7 100644 GIT binary patch delta 9849 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 delta 10087 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 Bj$;4- diff --git a/MSFSTouchPortalPlugin/lib/SharpConfig/SharpConfig.pdb b/MSFSTouchPortalPlugin/lib/SharpConfig/SharpConfig.pdb index 4bb00b328a0ca4f456a8d3235a0b55d40678a29c..4ba3228d3ea9eecb864419f3dd58e6c838be88d4 100644 GIT binary patch delta 16990 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 P$#c8k)N-rY(8BOP`S^HI From 2a2e71cb5f8d292c9c2a41329bd42418334308c0 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Mon, 11 Apr 2022 02:34:21 -0400 Subject: [PATCH 69/93] [SimConnectService] Some cleanup of request debug tracking; Just call AddNotification() from MapClientEventToSimEvent() if needed internally; Minor fixes/tweaks. --- .../Interfaces/ISimConnectService.cs | 2 +- .../Services/PluginService.cs | 6 +- .../Services/SimConnectService.cs | 69 ++++++++++--------- 3 files changed, 40 insertions(+), 37 deletions(-) diff --git a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs index ce2a428..b87d344 100644 --- a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs +++ b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs @@ -20,7 +20,7 @@ internal interface ISimConnectService { bool AddNotification(Groups group, Enum eventId); bool Connect(uint configIndex = 0); void Disconnect(); - bool MapClientEventToSimEvent(Enum eventId, string eventName); + bool MapClientEventToSimEvent(Enum eventId, string eventName, Groups group); bool TransmitClientEvent(Groups group, Enum eventId, uint data); void SetNotificationGroupPriorities(); void ClearAllDataDefinitions(); diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index ffa3f8d..0d97aa0 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -222,10 +222,8 @@ private void SimConnectEvent_OnConnect() { // Register Action events var eventMap = _reflectionService.GetClientEventIdToNameMap(); - foreach (var m in eventMap) { - _simConnectService.MapClientEventToSimEvent(m.Key, m.Value.EventName); - _simConnectService.AddNotification(m.Value.GroupId, m.Key); - } + foreach (var m in eventMap) + _simConnectService.MapClientEventToSimEvent(m.Key, m.Value.EventName, m.Value.GroupId); // must be called after adding notifications _simConnectService.SetNotificationGroupPriorities(); diff --git a/MSFSTouchPortalPlugin/Services/SimConnectService.cs b/MSFSTouchPortalPlugin/Services/SimConnectService.cs index 626d3f6..4a84f53 100644 --- a/MSFSTouchPortalPlugin/Services/SimConnectService.cs +++ b/MSFSTouchPortalPlugin/Services/SimConnectService.cs @@ -111,16 +111,16 @@ public bool Connect(uint configIndex = 0) { _messageWaitTask = Task.Run(ReceiveMessages); - } catch (COMException ex) { + } catch (COMException e) { _connected = false; - _logger.LogInformation("Connection to Sim failed: {exception}", ex.Message); - return false; + _logger.LogDebug("Connection to Simulator failed: {0}", e.Message); } _connecting = false; // Invoke Handler - OnConnect?.Invoke(); - return true; + if (_connected) + OnConnect?.Invoke(); + return _connected; } public void Disconnect() { @@ -178,7 +178,9 @@ private bool InvokeSimMethod(Delegate method, params object[] args) { try { _logger.LogTrace($"Invoking: {method.Method.Name}({(args != null ? string.Join(", ", args) : "null")})"); method.DynamicInvoke(args); +#if DEBUG_REQUESTS DbgAddSendRecord($"{method.Method.Name}({(args != null ? string.Join(", ", args) : "null")})"); +#endif return true; } catch (COMException e) { @@ -190,8 +192,10 @@ private bool InvokeSimMethod(Delegate method, params object[] args) { return false; } - public bool MapClientEventToSimEvent(Enum eventId, string eventName) { - return InvokeSimMethod(MapClientEventToSimEventDelegate, eventId, eventName); + public bool MapClientEventToSimEvent(Enum eventId, string eventName, Groups group) { + if (InvokeSimMethod(MapClientEventToSimEventDelegate, eventId, eventName)) + return AddNotification(group, eventId); + return false; } public bool TransmitClientEvent(Groups group, Enum eventId, uint data) { @@ -216,11 +220,10 @@ public void ClearAllDataDefinitions() { foreach (var def in _addedDefinitions) ClearDataDefinition(def); } - _addedDefinitions.Clear(); } public bool ClearDataDefinition(Definition def) { - return InvokeSimMethod(ClearDataDefinitionDelegate, def); + return _addedDefinitions.Remove(def) && InvokeSimMethod(ClearDataDefinitionDelegate, def); } public bool RegisterToSimConnect(SimVarItem simVar) { @@ -237,12 +240,13 @@ public bool RegisterToSimConnect(SimVarItem simVar) { if (!InvokeSimMethod(AddToDataDefinitionDelegate, simVar.Def, simVar.SimVarName, unitName, simVar.SimConnectDataType, simVar.DeltaEpsilon, SimConnect.SIMCONNECT_UNUSED)) return false; + _addedDefinitions.Add(simVar.Def); + if (!InvokeSimMethod(registerDataDelegate, simVar.Def)) { ClearDataDefinition(simVar.Def); return false; } - _addedDefinitions.Add(simVar.Def); return simVar.NeedsScheduledRequest || RequestDataOnSimObject(simVar); } @@ -296,8 +300,11 @@ private void Simconnect_OnRecvOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data) private void Simconnect_OnRecvException(SimConnect sender, SIMCONNECT_RECV_EXCEPTION data) { SIMCONNECT_EXCEPTION eException = (SIMCONNECT_EXCEPTION)data.dwException; - string request = DbgGetSendRecord(data.dwSendID); - _logger.LogWarning($"SimConnect_OnRecvException: {eException}; SendID: {data.dwSendID}; Index: {data.dwIndex}; Request: {request}"); +#if DEBUG_REQUESTS + _logger.LogWarning($"SimConnect Error: {eException}; SendID: {data.dwSendID}; Index: {data.dwIndex}; Request: {DbgGetSendRecord(data.dwSendID)}"); +#else + _logger.LogWarning($"SimConnect Error: {eException}; SendID: {data.dwSendID}; Index: {data.dwIndex};"); +#endif } private void Simconnect_OnRecvEvent(SimConnect sender, SIMCONNECT_RECV_EVENT data) { @@ -319,8 +326,7 @@ protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { // Dispose managed state (managed objects). - if (_connected) - Disconnect(); + Disconnect(); _scReady?.Dispose(); _messageWaitTask?.Dispose(); } @@ -337,36 +343,41 @@ public void Dispose() { } #endregion IDisposable Support - #region Request Debugging - #if DEBUG_REQUESTS // Extra SimConnect functions via native pointer - IntPtr hSimConnect; + IntPtr hSimConnect = IntPtr.Zero; [DllImport("SimConnect.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] private static extern int /* HRESULT */ SimConnect_GetLastSentPacketID(IntPtr hSimConnect, out uint /* DWORD */ dwSendID); // for tracking requests by their SendID - private readonly System.Collections.Generic.Dictionary dbgSendRecordsDict = new(); + private readonly SortedDictionary dbgSendRecordsDict = new(); +#pragma warning disable S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields private void DbgSetupRequestTracking() { // Get direct access to the SimConnect handle, to use functions otherwise not supported. -#pragma warning disable S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields - System.Reflection.FieldInfo fiSimConnect = typeof(SimConnect).GetField("hSimConnect", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); -#pragma warning restore S3011 - hSimConnect = (IntPtr)fiSimConnect.GetValue(_simConnect); + try { + System.Reflection.FieldInfo fiSimConnect = typeof(SimConnect).GetField("hSimConnect", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + hSimConnect = (IntPtr)fiSimConnect.GetValue(_simConnect); + } + catch (Exception e) { + hSimConnect = IntPtr.Zero; + _logger.LogError(e, $"Exception trying to get handle to SimConnect: {e.Message}"); + } } +#pragma warning restore S3011 private void DbgAddSendRecord(string record) { - if (_simConnect == null || !_connected) + if (_simConnect == null || hSimConnect == IntPtr.Zero) return; + if (SimConnect_GetLastSentPacketID(hSimConnect, out uint dwSendID) == 0) + dbgSendRecordsDict[dwSendID] = record; + // clean out old records if (dbgSendRecordsDict.Count > 5000) { - // we'd like to remove the oldest, first, record but the order isn't really guaranteed. We could get a list of keys and sort it... but this should suffice for debugs. + // Remove the oldest, first, record. var enmr = dbgSendRecordsDict.Keys.GetEnumerator(); enmr.MoveNext(); dbgSendRecordsDict.Remove(enmr.Current); } - if (SimConnect_GetLastSentPacketID(hSimConnect, out uint dwSendID) == 0) - _ = dbgSendRecordsDict.TryAdd(dwSendID, record); } private string DbgGetSendRecord(uint sendId) { @@ -375,12 +386,6 @@ private string DbgGetSendRecord(uint sendId) { return $"Record not found for SendID {sendId}"; } -#else - [System.Diagnostics.Conditional("DEBUG_REQUESTS")] // prevents any parameters being passed to this method from being evaluated - private static void DbgAddSendRecord(string record) { _ = record; /* no-op when request tracking disabled */ } - private static string DbgGetSendRecord(uint sendId) { _ = sendId; return "Request tracking disabled."; } #endif // DEBUG_REQUESTS - - #endregion } } From 9a25434ea07f730712415425de5044e409e5f00b Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Mon, 11 Apr 2022 02:51:50 -0400 Subject: [PATCH 70/93] Refactor ActionEventType to generate its own dynamic SimEventClientId values as needed, and allow access to mapping dictionary only via public methods; Adds a new c'tor for creating dynamic SimConnect events at runtime. --- .../Interfaces/IReflectionService.cs | 1 + .../Services/ReflectionService.cs | 16 +++---- .../Types/ActionEventType.cs | 46 +++++++++++++++---- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs b/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs index dd6e923..841f61e 100644 --- a/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs +++ b/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs @@ -13,6 +13,7 @@ internal interface IReflectionService { string GetSimEventNameById(Enum id); string GetSimEventNameById(uint id); string GetSimEventNameById(int id); + void AddSimEventNameMapping(Enum id, SimEventRecord record); IEnumerable GetActionAttributes(Groups catId); IEnumerable GetCategoryAttributes(); } diff --git a/MSFSTouchPortalPlugin/Services/ReflectionService.cs b/MSFSTouchPortalPlugin/Services/ReflectionService.cs index 6701dd7..36b3599 100644 --- a/MSFSTouchPortalPlugin/Services/ReflectionService.cs +++ b/MSFSTouchPortalPlugin/Services/ReflectionService.cs @@ -31,11 +31,10 @@ public ReflectionService(ILogger logger) { private static readonly Dictionary clientEventIdToNameMap = new(); public ref readonly Dictionary GetClientEventIdToNameMap() => ref clientEventIdToNameMap; - public string GetSimEventNameById(Enum id) { - return clientEventIdToNameMap.TryGetValue(id, out var entry) ? entry.EventName : "[unknown event]"; - } + public string GetSimEventNameById(Enum id) => clientEventIdToNameMap.TryGetValue(id, out var entry) ? entry.EventName : "[unknown event]"; public string GetSimEventNameById(uint id) => GetSimEventNameById((SimEventClientId)id); public string GetSimEventNameById(int id) => GetSimEventNameById((SimEventClientId)id); + public void AddSimEventNameMapping(Enum id, SimEventRecord record) => clientEventIdToNameMap[id] = record; public IEnumerable GetCategoryAttributes() { List ret = new(); @@ -87,7 +86,7 @@ private Dictionary GetInternalActionEvents() { fmtStrList.Add($"{{{i}}}"); act.KeyFormatStr = string.Join(",", fmtStrList); foreach (var ma in actAttr.Mappings) { - if (!act.TpActionToEventMap.TryAdd($"{string.Join(",", ma.Values)}", ma.EnumId)) + if (!act.TryAddPluginEventMapping($"{string.Join(",", ma.Values)}", (PluginActions)ma.EnumId)) _logger.LogWarning($"Duplicate action-to-event mapping found for Plugin action {act.ActionId} with choices '{string.Join(",", ma.Values)} for event '{ma.ActionId}'."); } } @@ -96,7 +95,6 @@ private Dictionary GetInternalActionEvents() { } public Dictionary GetActionEvents() { - int nextId = (int)SimEventClientId.Init + 1; var returnDict = GetInternalActionEvents(); var catAttribs = GetCategoryAttributes(); @@ -144,12 +142,12 @@ public Dictionary GetActionEvents() { act.KeyFormatStr = string.Join(",", fmtStrList); // Now get all the action mappings to produce the final list of all possible action events foreach (var ma in actAttr.Mappings) { - Enum mapTarget = (SimEventClientId)nextId++; // Put into collections - if (!act.TpActionToEventMap.TryAdd($"{string.Join(",", ma.Values)}", mapTarget)) + if (act.TryAddSimEventMapping($"{string.Join(",", ma.Values)}", out Enum mapTarget)) + // keep track of generated event IDs for Sim actions (for registering to SimConnect, and debug) + clientEventIdToNameMap[mapTarget] = new SimEventRecord(catAttr.Id, ma.ActionId); + else _logger.LogWarning($"Duplicate action-to-event mapping found for action {act.ActionId} with choices '{string.Join(",", ma.Values)} for event '{ma.ActionId}'."); - // keep track of generated event IDs for Sim actions (for registering to SimConnect, and debug) - clientEventIdToNameMap[mapTarget] = new SimEventRecord(catAttr.Id, ma.ActionId); } } // actions loop diff --git a/MSFSTouchPortalPlugin/Types/ActionEventType.cs b/MSFSTouchPortalPlugin/Types/ActionEventType.cs index b758747..ca68740 100644 --- a/MSFSTouchPortalPlugin/Types/ActionEventType.cs +++ b/MSFSTouchPortalPlugin/Types/ActionEventType.cs @@ -17,28 +17,54 @@ internal class ActionEventType public double MaxValue = double.NaN; public DataType ValueType = DataType.None; // assuming single value here also + public IReadOnlyDictionary DataAttributes; // list of all data type attributes + // Mapping of TP actions to SimConnect or "Native" events. For SimConnect, the Enum is a generated number // of type SimEventClientId (but doesn't actually exist), and for internal plugin events its an actual - // member of the MSFSTouchPortalPlugin.Objects.Plugin.Plugin enum. - public readonly Dictionary TpActionToEventMap = new(); + // member of the MSFSTouchPortalPlugin.Objects.Plugin.PluginActions enum. + readonly Dictionary TpActionToEventMap = new(); - // possible future use - //public object ActionObject = null; // the member to which all action attributes are assigned to - public IReadOnlyDictionary DataAttributes; // list of all data type attributes + // this is how we generate unique SimConnect client Event IDs. + private static SimEventClientId _nextEventId = SimEventClientId.Init; + private static SimEventClientId NextId() => ++_nextEventId; // got a warning when trying to increment this directly from c'tor, but not via static member... ? + + public ActionEventType() { } + + /// c'tor for dynamically added actions with just one sim event, which returns the generated event ID of actual type SimEventClientId + public ActionEventType(string actionId, Groups categoryId, bool hasValue, out Enum eventId) { + Id = eventId = NextId(); + ActionId = actionId; + CategoryId = categoryId; + ValueIndex = hasValue ? 0 : -1; + ValueType = DataType.Number; + } + + public bool TryAddSimEventMapping(string actionId, out Enum eventId) { + eventId = NextId(); + return TpActionToEventMap.TryAdd(actionId, eventId); + } + + public bool TryAddPluginEventMapping(string actionName, PluginActions eventId) { + return TpActionToEventMap.TryAdd(actionName, eventId); + } // Get a unique event ID for this action, possibly based on data values // in the \c data array. Certain combination of values, eg. from choices, // may have their own unique events. Returns `false` if the lookup fails. - public bool TryGetEventMapping(in string[] values, out Enum eventEnum) { + public bool TryGetEventMapping(in string[] values, out Enum eventId) { + if (!TpActionToEventMap.Any()) { + eventId = Id; + return true; + } if (TpActionToEventMap.Count == 1) { - eventEnum = TpActionToEventMap.First().Value; + eventId = TpActionToEventMap.First().Value; return true; } - return TpActionToEventMap.TryGetValue(FormatLookupKey(values), out eventEnum); + return TpActionToEventMap.TryGetValue(FormatLookupKey(values), out eventId); } - public bool TryGetEventMapping(string value, out Enum eventEnum) => - TryGetEventMapping(new string[] { value }, out eventEnum); + public bool TryGetEventMapping(string value, out Enum eventId) => + TryGetEventMapping(new string[] { value }, out eventId); // Helper to format an array of action data values into a unique key // used for indexing the TpActionToEventMap dictionary. From a741b20843bfb40bf7cd357e6bcc035cda409bbc Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Mon, 11 Apr 2022 18:12:27 -0400 Subject: [PATCH 71/93] Add actions for triggering arbitrary sim Events, from a selection list of imported events or free-form entry; The AddKnownSimVar action now has a category selector to narrow down the sim var choices; Fixes that added custom SimVars were not properly removed when re-adding them; Move all imported data handling to PluginConfig. --- .../Configuration/PluginConfig.cs | 184 +- .../Configuration/SimEvents.ini | 6908 +++++++++++++++++ .../Configuration/SimVars.ini | 46 +- .../MSFSTouchPortalPlugin.csproj | 3 + .../Objects/Plugin/Plugin.cs | 70 +- .../Services/PluginService.cs | 323 +- MSFSTouchPortalPlugin/Types/SimEvent.cs | 22 + 7 files changed, 7377 insertions(+), 179 deletions(-) create mode 100644 MSFSTouchPortalPlugin/Configuration/SimEvents.ini create mode 100644 MSFSTouchPortalPlugin/Types/SimEvent.cs diff --git a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs index 2be8552..5e4354f 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs +++ b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs @@ -7,6 +7,7 @@ using System.IO; using System.Text; using Microsoft.Extensions.Logging; +using System.Text.RegularExpressions; namespace MSFSTouchPortalPlugin.Configuration { @@ -60,11 +61,15 @@ public static string UserStateFiles public static bool HaveUserStateFiles => _userStateFiles.Any(); public static IReadOnlyCollection UserStateFilesArray => _userStateFiles; + public IEnumerable ImportedSimVarCategoryNames => _importedSimVars.Keys; + public IEnumerable ImportedSimEvenCategoryNames => _importedSimEvents.Keys; const string STR_DEFAULT = "default"; private static readonly string _defaultUserCfgFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), RootName); private static string _currentUserCfgFolder = _defaultUserCfgFolder; private static string[] _userStateFiles = Array.Empty(); + private IReadOnlyDictionary> _importedSimVars; + private IReadOnlyDictionary> _importedSimEvents; private readonly ILogger _logger; @@ -76,22 +81,92 @@ public PluginConfig(ILogger logger) { SharpConfig.Configuration.AlwaysQuoteStringValues = true; // custom SharpConfig v3.2.9.2-mp feature } + // Loads all imports + public void Init() { + _importedSimVars = ImportSimVars(); + _importedSimEvents = ImportSimEvents(); + // Check for custom SimConnect.cfg and try copy it to application dir (may require elevated privileges) + CopySimConnectConfig(); + } + + // Imported SimVariables methods + + public bool TryGetImportedSimVarNamesForCateogy(string simCategoryName, out IEnumerable list) { + if (_importedSimVars.TryGetValue(simCategoryName, out var dict)) { + list = dict.Keys; + return true; + } + list = Array.Empty(); + return false; + } + + public bool TryGetImportedSimVarsForCateogy(string simCategoryName, out IEnumerable list) { + if (_importedSimVars.TryGetValue(simCategoryName, out var dict)) { + list = dict.Values; + return true; + } + list = Array.Empty(); + return false; + } + + public bool TryGetImportedSimVarBySelector(string selector, out SimVariable simVar) { + simVar = null; + selector = selector?.Trim().Replace(":N", "").Replace("* ", "") ?? string.Empty; + return (_importedSimVars.Values.FirstOrDefault(c => c.ContainsKey(selector)) is var cat && cat != null) && cat.TryGetValue(selector, out simVar); + } + + public SimVariable GetOrCreateImportedSimVariable(string varName) { + if (string.IsNullOrWhiteSpace(varName)) + return null; + var simVarName = varName.Trim().Replace(":N", "").Replace("* ", ""); + if ((_importedSimVars.Values.FirstOrDefault(c => c.ContainsKey(simVarName)) is var cat && cat != null) && cat.TryGetValue(simVarName, out SimVariable simVar)) + return simVar; + simVar = new() { + // Create a reasonable string for a TP state ID + Id = Regex.Replace(simVarName.ToLower(), @"(?:\b|\W|_)(\w)", m => (m.Groups[1].ToString().ToUpper())), + SimVarName = simVarName, + Name = simVarName, // for lack of anything better + Indexed = varName.Trim().EndsWith(":N") + }; + return simVar; + } + + // Imported SimEvents methods + + public bool TryGetImportedSimEventNamesForCateogy(string simCategoryName, out IEnumerable list) { + if (_importedSimEvents.TryGetValue(simCategoryName, out var dict)) { + list = dict.Keys; + return true; + } + list = Array.Empty(); + return false; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static")] + public bool TryGetImportedSimEventIdFromSelector(string selector, out string eventId) { + eventId = selector?.Split(" - ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).First() ?? string.Empty; + return !string.IsNullOrEmpty(eventId); + } + + // Check if user config folder contains a SimConnect.cfg file and tries to copy it into the current running folder. public bool CopySimConnectConfig() { string filename = "SimConnect.cfg"; string srcFile = Path.Combine(UserConfigFolder, filename); if (File.Exists(srcFile)) { try { File.Copy(srcFile, Path.Combine(AppRootFolder, filename), true); + _logger.LogInformation($"Using custom SimConnect.cfg file from '{UserConfigFolder}'."); return true; } catch (Exception e) { - _logger.LogError(e, $"Error trying to copy SimConnect.cfg file from '{srcFile}': {e.Message}"); + _logger.LogError($"Error trying to copy SimConnect.cfg file from '{srcFile}': {e.Message}"); return false; } } return false; } + // Loads an individual sim var states config file, either from user's config folder, the default app config location, or a full file path public IReadOnlyCollection LoadSimVarItems(bool isUserConfig = true, string filename = default) { List ret = new(); filename = GetFullFilePath(filename, isUserConfig); @@ -131,6 +206,7 @@ public IReadOnlyCollection LoadSimVarItems(bool isUserConfig = true, return ret; } + // Loads all sim var config as per current configuration of user/default state files public IReadOnlyCollection LoadSimVarStateConfigs() { if (!HaveUserStateFiles) return LoadSimVarItems(false); @@ -143,9 +219,11 @@ public IReadOnlyCollection LoadSimVarStateConfigs() { return ret; } + // Loads the definitions of the internal plugin states public IReadOnlyCollection LoadPluginStates() => LoadSimVarItems(false, PluginStatesConfigFile); + // Save collection to file public bool SaveSimVarItems(IEnumerable items, bool isUserConfig = true, string filename = default) { var cfg = new SharpConfig.Configuration(); Groups lastCatId = default; @@ -190,17 +268,38 @@ public bool SaveSimVarItems(IEnumerable items, bool isUserConfig = t return ok; } - public IReadOnlyCollection ImportSimVars() { - List ret = new(); + + // private importers, loaded data is stored internally + + IReadOnlyDictionary> ImportSimVars() { + Dictionary> ret = new(); var filename = Path.Combine(AppConfigFolder, SimVarsImportsFile); _logger.LogDebug($"Importing SimVars from file '{filename}'..."); if (!LoadFromFile(filename, out var cfg)) return ret; + int count = 0; + string currCatName = string.Empty; + Dictionary catDict = null; foreach (SharpConfig.Section section in cfg) { - if (section.Name == SharpConfig.Section.DefaultSectionName || section.Name.StartsWith("category_")) - continue; // do something with category later? + if (section.Name == SharpConfig.Section.DefaultSectionName) + continue; + + // new category + if (section.Name.StartsWith("category_")) { + if (catDict != null) { + ret[currCatName] = catDict.OrderBy(kv => kv.Key).ToDictionary(s => s.Key, s => s.Value); + } + if (section.TryGetSetting("Name", out var setting)) { + currCatName = setting.StringValue; + // the categories should be sequential, but JIC we check if we already have it + catDict = ret.GetValueOrDefault(currCatName, new Dictionary()).ToDictionary(s => s.Key, s => s.Value); + } + continue; + } + if (catDict == null) + continue; SimVariable simVar; try { @@ -224,19 +323,78 @@ public IReadOnlyCollection ImportSimVars() { if (string.IsNullOrWhiteSpace(simVar.Name)) simVar.Name = simVar.SimVarName; // set up a name to use in the TP UI selection list - simVar.TouchPortalSelectorName = $"{simVar.CategoryId} - {simVar.SimVarName}{(simVar.Indexed ? ":N" : "")}"; + simVar.TouchPortalSelectorName = $"{simVar.SimVarName}{(simVar.Indexed ? ":N" : "")}"; + // pad with 2 spaces per char because TP uses _very_ proportional fonts and we need the names to be of same width + simVar.TouchPortalSelectorName += string.Concat(Enumerable.Repeat(" ", Math.Max(0, 45 - simVar.TouchPortalSelectorName.Length))); // check unique - if (ret.FindIndex(s => s.Id == simVar.Id) is int idx && idx > -1) { - _logger.LogWarning($"Duplicate SimVar ID found for '{simVar.Id}', overwriting."); - ret[idx] = simVar; + if (!catDict.TryAdd(simVar.SimVarName, simVar)) { + catDict[simVar.SimVarName] = simVar; + _logger.LogWarning($"Duplicate SimVar ID found for '{simVar.Id}' ('{simVar.SimVarName}'), overwriting."); } - else { - ret.Add(simVar); + ++count; + } + ret = ret.OrderBy(s => s.Key).ToDictionary(s => s.Key, s => s.Value); + _logger.LogDebug($"Imported {count} SimVars in {ret.Count} categories from '{filename}'"); + return ret; + } + + IReadOnlyDictionary> ImportSimEvents() { + Dictionary> ret = new(); + var filename = Path.Combine(AppConfigFolder, SimEventsImportsFile); + _logger.LogDebug($"Importing SimEvents from file '{filename}'..."); + + if (!LoadFromFile(filename, out var cfg)) + return ret; + + int count = 0; + string currCatName = string.Empty; + Dictionary catDict = null; + foreach (SharpConfig.Section section in cfg) { + if (section.Name == SharpConfig.Section.DefaultSectionName) + continue; + + // new category + if (section.Name.StartsWith("category_")) { + if (catDict != null) { + ret[currCatName] = catDict.OrderBy(kv => kv.Key).ToDictionary(s => s.Key, s => s.Value); + } + if (section.TryGetSetting("Name", out var setting)) { + currCatName = setting.StringValue; + // the categories should be sequential, but JIC we check if we already have it + catDict = ret.GetValueOrDefault(currCatName, new Dictionary()).ToDictionary(s => s.Key, s => s.Value); + } + continue; + } + if (catDict == null) + continue; + + SimEvent simEvt; + try { + simEvt = section.ToObject(); + } + catch (Exception e) { + _logger.LogError(e, $"Deserialize exception for section '{section}': {e.Message}:"); + continue; + } + if (simEvt == null) { + _logger.LogError($"Produced SimEvent is null from section '{section}':"); + continue; + } + // INI section name is the ID + simEvt.Id = section.Name; + // set up a name to use in the TP UI selection list + simEvt.TouchPortalSelectorName = $"{simEvt.Id} - {simEvt.Name}"; + + // check unique + if (!catDict.TryAdd(simEvt.TouchPortalSelectorName, simEvt)) { + catDict[simEvt.TouchPortalSelectorName] = simEvt; + _logger.LogWarning($"Duplicate SimEvent ID found for '{simEvt.Id}', overwriting."); } + ++count; } - ret = ret.OrderBy(s => s.TouchPortalSelectorName).ToList(); - _logger.LogDebug($"Imported {ret.Count} SimVars from '{filename}'"); + ret = ret.OrderBy(s => s.Key).ToDictionary(s => s.Key, s => s.Value); + _logger.LogDebug($"Imported {count} SimEvents in {ret.Count} categories from '{filename}'"); return ret; } diff --git a/MSFSTouchPortalPlugin/Configuration/SimEvents.ini b/MSFSTouchPortalPlugin/Configuration/SimEvents.ini new file mode 100644 index 0000000..9125751 --- /dev/null +++ b/MSFSTouchPortalPlugin/Configuration/SimEvents.ini @@ -0,0 +1,6908 @@ +## Generated on 2022-04-10 04:56:53.711397 UTC with data from https://github.com/odwdinc/Python-SimConnect v0.4.25 +# !! Manual cleanup of auto-assigned Name attributes done by mpaperno 2022-04-10. If re-generating, be sure to run a diff/patch. !! +# + +# Engine ####################### +# +[category_Engine] +Name = "Engine" + + +[THROTTLE_FULL] +CategoryId = Engine +SimEventName = "THROTTLE_FULL" +Name = "Set throttles max" +Description = "Set throttles max" +System = "Shared Cockpit" + +[THROTTLE_INCR] +CategoryId = Engine +SimEventName = "THROTTLE_INCR" +Name = "Increment throttles" +Description = "Increment throttles" +System = "Shared Cockpit" + +[THROTTLE_INCR_SMALL] +CategoryId = Engine +SimEventName = "THROTTLE_INCR_SMALL" +Name = "Increment throttles small" +Description = "Increment throttles small" +System = "Shared Cockpit" + +[THROTTLE_DECR] +CategoryId = Engine +SimEventName = "THROTTLE_DECR" +Name = "Decrement throttles" +Description = "Decrement throttles" +System = "Shared Cockpit" + +[THROTTLE_DECR_SMALL] +CategoryId = Engine +SimEventName = "THROTTLE_DECR_SMALL" +Name = "Decrease throttles small" +Description = "Decrease throttles small" +System = "Shared Cockpit" + +[THROTTLE_CUT] +CategoryId = Engine +SimEventName = "THROTTLE_CUT" +Name = "Set throttles to idle" +Description = "Set throttles to idle" +System = "Shared Cockpit" + +[INCREASE_THROTTLE] +CategoryId = Engine +SimEventName = "INCREASE_THROTTLE" +Name = "Increment throttles" +Description = "Increment throttles" +System = "Shared Cockpit" + +[DECREASE_THROTTLE] +CategoryId = Engine +SimEventName = "DECREASE_THROTTLE" +Name = "Decrement throttles" +Description = "Decrement throttles" +System = "Shared Cockpit" + +[THROTTLE_SET] +CategoryId = Engine +SimEventName = "THROTTLE_SET" +Name = "Set throttles exactly (0- 16384)" +Description = "Set throttles exactly (0- 16384)" +System = "Shared Cockpit" + +[AXIS_THROTTLE_SET] +CategoryId = Engine +SimEventName = "AXIS_THROTTLE_SET" +Name = "Set throttles (0- 16384)" +Description = "Set throttles (0- 16384)" +System = "Shared Cockpit (Pilot only, transmitted to Co-pilot if in a helicopter, not-transmitted otherwise)." + +[THROTTLE1_SET] +CategoryId = Engine +SimEventName = "THROTTLE1_SET" +Name = "Set throttle 1 exactly (0 to 16384)" +Description = "Set throttle 1 exactly (0 to 16384)" +System = "Shared Cockpit" + +[THROTTLE2_SET] +CategoryId = Engine +SimEventName = "THROTTLE2_SET" +Name = "Set throttle 2 exactly (0 to 16384)" +Description = "Set throttle 2 exactly (0 to 16384)" +System = "Shared Cockpit" + +[THROTTLE3_SET] +CategoryId = Engine +SimEventName = "THROTTLE3_SET" +Name = "Set throttle 3 exactly (0 to 16384)" +Description = "Set throttle 3 exactly (0 to 16384)" +System = "Shared Cockpit" + +[THROTTLE4_SET] +CategoryId = Engine +SimEventName = "THROTTLE4_SET" +Name = "Set throttle 4 exactly (0 to 16384)" +Description = "Set throttle 4 exactly (0 to 16384)" +System = "Shared Cockpit" + +[THROTTLE1_FULL] +CategoryId = Engine +SimEventName = "THROTTLE1_FULL" +Name = "Set throttle 1 max" +Description = "Set throttle 1 max" +System = "Shared Cockpit" + +[THROTTLE1_INCR] +CategoryId = Engine +SimEventName = "THROTTLE1_INCR" +Name = "Increment throttle 1" +Description = "Increment throttle 1" +System = "Shared Cockpit" + +[THROTTLE1_INCR_SMALL] +CategoryId = Engine +SimEventName = "THROTTLE1_INCR_SMALL" +Name = "Increment throttle 1 small" +Description = "Increment throttle 1 small" +System = "Shared Cockpit" + +[THROTTLE1_DECR] +CategoryId = Engine +SimEventName = "THROTTLE1_DECR" +Name = "Decrement throttle 1" +Description = "Decrement throttle 1" +System = "Shared Cockpit" + +[THROTTLE1_CUT] +CategoryId = Engine +SimEventName = "THROTTLE1_CUT" +Name = "Set throttle 1 to idle" +Description = "Set throttle 1 to idle" +System = "Shared Cockpit" + +[THROTTLE2_FULL] +CategoryId = Engine +SimEventName = "THROTTLE2_FULL" +Name = "Set throttle 2 max" +Description = "Set throttle 2 max" +System = "Shared Cockpit" + +[THROTTLE2_INCR] +CategoryId = Engine +SimEventName = "THROTTLE2_INCR" +Name = "Increment throttle 2" +Description = "Increment throttle 2" +System = "Shared Cockpit" + +[THROTTLE2_INCR_SMALL] +CategoryId = Engine +SimEventName = "THROTTLE2_INCR_SMALL" +Name = "Increment throttle 2 small" +Description = "Increment throttle 2 small" +System = "Shared Cockpit" + +[THROTTLE2_DECR] +CategoryId = Engine +SimEventName = "THROTTLE2_DECR" +Name = "Decrement throttle 2" +Description = "Decrement throttle 2" +System = "Shared Cockpit" + +[THROTTLE2_CUT] +CategoryId = Engine +SimEventName = "THROTTLE2_CUT" +Name = "Set throttle 2 to idle" +Description = "Set throttle 2 to idle" +System = "Shared Cockpit" + +[THROTTLE3_FULL] +CategoryId = Engine +SimEventName = "THROTTLE3_FULL" +Name = "Set throttle 3 max" +Description = "Set throttle 3 max" +System = "Shared Cockpit" + +[THROTTLE3_INCR] +CategoryId = Engine +SimEventName = "THROTTLE3_INCR" +Name = "Increment throttle 3" +Description = "Increment throttle 3" +System = "Shared Cockpit" + +[THROTTLE3_INCR_SMALL] +CategoryId = Engine +SimEventName = "THROTTLE3_INCR_SMALL" +Name = "Increment throttle 3 small" +Description = "Increment throttle 3 small" +System = "Shared Cockpit" + +[THROTTLE3_DECR] +CategoryId = Engine +SimEventName = "THROTTLE3_DECR" +Name = "Decrement throttle 3" +Description = "Decrement throttle 3" +System = "Shared Cockpit" + +[THROTTLE3_CUT] +CategoryId = Engine +SimEventName = "THROTTLE3_CUT" +Name = "Set throttle 3 to idle" +Description = "Set throttle 3 to idle" +System = "Shared Cockpit" + +[THROTTLE4_FULL] +CategoryId = Engine +SimEventName = "THROTTLE4_FULL" +Name = "Set throttle 1 max" +Description = "Set throttle 1 max" +System = "Shared Cockpit" + +[THROTTLE4_INCR] +CategoryId = Engine +SimEventName = "THROTTLE4_INCR" +Name = "Increment throttle 4" +Description = "Increment throttle 4" +System = "Shared Cockpit" + +[THROTTLE4_INCR_SMALL] +CategoryId = Engine +SimEventName = "THROTTLE4_INCR_SMALL" +Name = "Increment throttle 4 small" +Description = "Increment throttle 4 small" +System = "Shared Cockpit" + +[THROTTLE4_DECR] +CategoryId = Engine +SimEventName = "THROTTLE4_DECR" +Name = "Decrement throttle 4" +Description = "Decrement throttle 4" +System = "Shared Cockpit" + +[THROTTLE4_CUT] +CategoryId = Engine +SimEventName = "THROTTLE4_CUT" +Name = "Set throttle 4 to idle" +Description = "Set throttle 4 to idle" +System = "Shared Cockpit" + +[THROTTLE_10] +CategoryId = Engine +SimEventName = "THROTTLE_10" +Name = "Set throttles to 10%" +Description = "Set throttles to 10%" +System = "Shared Cockpit" + +[THROTTLE_20] +CategoryId = Engine +SimEventName = "THROTTLE_20" +Name = "Set throttles to 20%" +Description = "Set throttles to 20%" +System = "Shared Cockpit" + +[THROTTLE_30] +CategoryId = Engine +SimEventName = "THROTTLE_30" +Name = "Set throttles to 30%" +Description = "Set throttles to 30%" +System = "Shared Cockpit" + +[THROTTLE_40] +CategoryId = Engine +SimEventName = "THROTTLE_40" +Name = "Set throttles to 40%" +Description = "Set throttles to 40%" +System = "Shared Cockpit" + +[THROTTLE_50] +CategoryId = Engine +SimEventName = "THROTTLE_50" +Name = "Set throttles to 50%" +Description = "Set throttles to 50%" +System = "Shared Cockpit" + +[THROTTLE_60] +CategoryId = Engine +SimEventName = "THROTTLE_60" +Name = "Set throttles to 60%" +Description = "Set throttles to 60%" +System = "Shared Cockpit" + +[THROTTLE_70] +CategoryId = Engine +SimEventName = "THROTTLE_70" +Name = "Set throttles to 70%" +Description = "Set throttles to 70%" +System = "Shared Cockpit" + +[THROTTLE_80] +CategoryId = Engine +SimEventName = "THROTTLE_80" +Name = "Set throttles to 80%" +Description = "Set throttles to 80%" +System = "Shared Cockpit" + +[THROTTLE_90] +CategoryId = Engine +SimEventName = "THROTTLE_90" +Name = "Set throttles to 90%" +Description = "Set throttles to 90%" +System = "Shared Cockpit" + +[AXIS_THROTTLE1_SET] +CategoryId = Engine +SimEventName = "AXIS_THROTTLE1_SET" +Name = "Set throttle 1 exactly (-16384 - +16384)" +Description = "Set throttle 1 exactly (-16384 - +16384)" +System = "Shared Cockpit" + +[AXIS_THROTTLE2_SET] +CategoryId = Engine +SimEventName = "AXIS_THROTTLE2_SET" +Name = "Set throttle 2 exactly (-16384 - +16384)" +Description = "Set throttle 2 exactly (-16384 - +16384)" +System = "Shared Cockpit" + +[AXIS_THROTTLE3_SET] +CategoryId = Engine +SimEventName = "AXIS_THROTTLE3_SET" +Name = "Set throttle 3 exactly (-16384 - +16384)" +Description = "Set throttle 3 exactly (-16384 - +16384)" +System = "Shared Cockpit" + +[AXIS_THROTTLE4_SET] +CategoryId = Engine +SimEventName = "AXIS_THROTTLE4_SET" +Name = "Set throttle 4 exactly (-16384 - +16384)" +Description = "Set throttle 4 exactly (-16384 - +16384)" +System = "Shared Cockpit" + +[THROTTLE1_DECR_SMALL] +CategoryId = Engine +SimEventName = "THROTTLE1_DECR_SMALL" +Name = "Decrease throttle 1 small" +Description = "Decrease throttle 1 small" +System = "Shared Cockpit" + +[THROTTLE2_DECR_SMALL] +CategoryId = Engine +SimEventName = "THROTTLE2_DECR_SMALL" +Name = "Decrease throttle 2 small" +Description = "Decrease throttle 2 small" +System = "Shared Cockpit" + +[THROTTLE3_DECR_SMALL] +CategoryId = Engine +SimEventName = "THROTTLE3_DECR_SMALL" +Name = "Decrease throttle 3 small" +Description = "Decrease throttle 3 small" +System = "Shared Cockpit" + +[THROTTLE4_DECR_SMALL] +CategoryId = Engine +SimEventName = "THROTTLE4_DECR_SMALL" +Name = "Decrease throttle 4 small" +Description = "Decrease throttle 4 small" +System = "Shared Cockpit" + +[PROP_PITCH_DECR_SMALL] +CategoryId = Engine +SimEventName = "PROP_PITCH_DECR_SMALL" +Name = "Decrease prop levers small" +Description = "Decrease prop levers small" +System = "Shared Cockpit" + +[PROP_PITCH1_DECR_SMALL] +CategoryId = Engine +SimEventName = "PROP_PITCH1_DECR_SMALL" +Name = "Decrease prop lever 1 small" +Description = "Decrease prop lever 1 small" +System = "Shared Cockpit" + +[PROP_PITCH2_DECR_SMALL] +CategoryId = Engine +SimEventName = "PROP_PITCH2_DECR_SMALL" +Name = "Decrease prop lever 2 small" +Description = "Decrease prop lever 2 small" +System = "Shared Cockpit" + +[PROP_PITCH3_DECR_SMALL] +CategoryId = Engine +SimEventName = "PROP_PITCH3_DECR_SMALL" +Name = "Decrease prop lever 3 small" +Description = "Decrease prop lever 3 small" +System = "Shared Cockpit" + +[PROP_PITCH4_DECR_SMALL] +CategoryId = Engine +SimEventName = "PROP_PITCH4_DECR_SMALL" +Name = "Decrease prop lever 4 small" +Description = "Decrease prop lever 4 small" +System = "Shared Cockpit" + +[MIXTURE1_RICH] +CategoryId = Engine +SimEventName = "MIXTURE1_RICH" +Name = "Set mixture lever 1 to max rich" +Description = "Set mixture lever 1 to max rich" +System = "Shared Cockpit" + +[MIXTURE1_INCR] +CategoryId = Engine +SimEventName = "MIXTURE1_INCR" +Name = "Increment mixture lever 1" +Description = "Increment mixture lever 1" +System = "Shared Cockpit" + +[MIXTURE1_INCR_SMALL] +CategoryId = Engine +SimEventName = "MIXTURE1_INCR_SMALL" +Name = "Increment mixture lever 1 small" +Description = "Increment mixture lever 1 small" +System = "Shared Cockpit" + +[MIXTURE1_DECR] +CategoryId = Engine +SimEventName = "MIXTURE1_DECR" +Name = "Decrement mixture lever 1" +Description = "Decrement mixture lever 1" +System = "Shared Cockpit" + +[MIXTURE1_LEAN] +CategoryId = Engine +SimEventName = "MIXTURE1_LEAN" +Name = "Set mixture lever 1 to max lean" +Description = "Set mixture lever 1 to max lean" +System = "Shared Cockpit" + +[MIXTURE2_RICH] +CategoryId = Engine +SimEventName = "MIXTURE2_RICH" +Name = "Set mixture lever 2 to max rich" +Description = "Set mixture lever 2 to max rich" +System = "Shared Cockpit" + +[MIXTURE2_INCR] +CategoryId = Engine +SimEventName = "MIXTURE2_INCR" +Name = "Increment mixture lever 2" +Description = "Increment mixture lever 2" +System = "Shared Cockpit" + +[MIXTURE2_INCR_SMALL] +CategoryId = Engine +SimEventName = "MIXTURE2_INCR_SMALL" +Name = "Increment mixture lever 2 small" +Description = "Increment mixture lever 2 small" +System = "Shared Cockpit" + +[MIXTURE2_DECR] +CategoryId = Engine +SimEventName = "MIXTURE2_DECR" +Name = "Decrement mixture lever 2" +Description = "Decrement mixture lever 2" +System = "Shared Cockpit" + +[MIXTURE2_LEAN] +CategoryId = Engine +SimEventName = "MIXTURE2_LEAN" +Name = "Set mixture lever 2 to max lean" +Description = "Set mixture lever 2 to max lean" +System = "Shared Cockpit" + +[MIXTURE3_RICH] +CategoryId = Engine +SimEventName = "MIXTURE3_RICH" +Name = "Set mixture lever 3 to max rich" +Description = "Set mixture lever 3 to max rich" +System = "Shared Cockpit" + +[MIXTURE3_INCR] +CategoryId = Engine +SimEventName = "MIXTURE3_INCR" +Name = "Increment mixture lever 3" +Description = "Increment mixture lever 3" +System = "Shared Cockpit" + +[MIXTURE3_INCR_SMALL] +CategoryId = Engine +SimEventName = "MIXTURE3_INCR_SMALL" +Name = "Increment mixture lever 3 small" +Description = "Increment mixture lever 3 small" +System = "Shared Cockpit" + +[MIXTURE3_DECR] +CategoryId = Engine +SimEventName = "MIXTURE3_DECR" +Name = "Decrement mixture lever 3" +Description = "Decrement mixture lever 3" +System = "Shared Cockpit" + +[MIXTURE3_LEAN] +CategoryId = Engine +SimEventName = "MIXTURE3_LEAN" +Name = "Set mixture lever 3 to max lean" +Description = "Set mixture lever 3 to max lean" +System = "Shared Cockpit" + +[MIXTURE4_RICH] +CategoryId = Engine +SimEventName = "MIXTURE4_RICH" +Name = "Set mixture lever 4 to max rich" +Description = "Set mixture lever 4 to max rich" +System = "Shared Cockpit" + +[MIXTURE4_INCR] +CategoryId = Engine +SimEventName = "MIXTURE4_INCR" +Name = "Increment mixture lever 4" +Description = "Increment mixture lever 4" +System = "Shared Cockpit" + +[MIXTURE4_INCR_SMALL] +CategoryId = Engine +SimEventName = "MIXTURE4_INCR_SMALL" +Name = "Increment mixture lever 4 small" +Description = "Increment mixture lever 4 small" +System = "Shared Cockpit" + +[MIXTURE4_DECR] +CategoryId = Engine +SimEventName = "MIXTURE4_DECR" +Name = "Decrement mixture lever 4" +Description = "Decrement mixture lever 4" +System = "Shared Cockpit" + +[MIXTURE4_LEAN] +CategoryId = Engine +SimEventName = "MIXTURE4_LEAN" +Name = "Set mixture lever 4 to max lean" +Description = "Set mixture lever 4 to max lean" +System = "Shared Cockpit" + +[MIXTURE_SET] +CategoryId = Engine +SimEventName = "MIXTURE_SET" +Name = "Set mixture levers to exact value (0 to 16384)" +Description = "Set mixture levers to exact value (0 to 16384)" +System = "Shared Cockpit" + +[MIXTURE_RICH] +CategoryId = Engine +SimEventName = "MIXTURE_RICH" +Name = "Set mixture levers to max rich" +Description = "Set mixture levers to max rich" +System = "Shared Cockpit" + +[MIXTURE_INCR] +CategoryId = Engine +SimEventName = "MIXTURE_INCR" +Name = "Increment mixture levers" +Description = "Increment mixture levers" +System = "Shared Cockpit" + +[MIXTURE_INCR_SMALL] +CategoryId = Engine +SimEventName = "MIXTURE_INCR_SMALL" +Name = "Increment mixture levers small" +Description = "Increment mixture levers small" +System = "Shared Cockpit" + +[MIXTURE_DECR] +CategoryId = Engine +SimEventName = "MIXTURE_DECR" +Name = "Decrement mixture levers" +Description = "Decrement mixture levers" +System = "Shared Cockpit" + +[MIXTURE_LEAN] +CategoryId = Engine +SimEventName = "MIXTURE_LEAN" +Name = "Set mixture levers to max lean" +Description = "Set mixture levers to max lean" +System = "Shared Cockpit" + +[MIXTURE1_SET] +CategoryId = Engine +SimEventName = "MIXTURE1_SET" +Name = "Set mixture lever 1 exact value (0 to 16384)" +Description = "Set mixture lever 1 exact value (0 to 16384)" +System = "Shared Cockpit" + +[MIXTURE2_SET] +CategoryId = Engine +SimEventName = "MIXTURE2_SET" +Name = "Set mixture lever 2 exact value (0 to 16384)" +Description = "Set mixture lever 2 exact value (0 to 16384)" +System = "Shared Cockpit" + +[MIXTURE3_SET] +CategoryId = Engine +SimEventName = "MIXTURE3_SET" +Name = "Set mixture lever 3 exact value (0 to 16384)" +Description = "Set mixture lever 3 exact value (0 to 16384)" +System = "Shared Cockpit" + +[MIXTURE4_SET] +CategoryId = Engine +SimEventName = "MIXTURE4_SET" +Name = "Set mixture lever 4 exact value (0 to 16384)" +Description = "Set mixture lever 4 exact value (0 to 16384)" +System = "Shared Cockpit" + +[AXIS_MIXTURE_SET] +CategoryId = Engine +SimEventName = "AXIS_MIXTURE_SET" +Name = "Set mixture lever 1 exact value (-16384 to +16384)" +Description = "Set mixture lever 1 exact value (-16384 to +16384)" +System = "Shared Cockpit" + +[AXIS_MIXTURE1_SET] +CategoryId = Engine +SimEventName = "AXIS_MIXTURE1_SET" +Name = "Set mixture lever 1 exact value (-16384 to +16384)" +Description = "Set mixture lever 1 exact value (-16384 to +16384)" +System = "Shared Cockpit" + +[AXIS_MIXTURE2_SET] +CategoryId = Engine +SimEventName = "AXIS_MIXTURE2_SET" +Name = "Set mixture lever 2 exact value (-16384 to +16384)" +Description = "Set mixture lever 2 exact value (-16384 to +16384)" +System = "Shared Cockpit" + +[AXIS_MIXTURE3_SET] +CategoryId = Engine +SimEventName = "AXIS_MIXTURE3_SET" +Name = "Set mixture lever 3 exact value (-16384 to +16384)" +Description = "Set mixture lever 3 exact value (-16384 to +16384)" +System = "Shared Cockpit" + +[AXIS_MIXTURE4_SET] +CategoryId = Engine +SimEventName = "AXIS_MIXTURE4_SET" +Name = "Set mixture lever 4 exact value (-16384 to +16384)" +Description = "Set mixture lever 4 exact value (-16384 to +16384)" +System = "Shared Cockpit" + +[MIXTURE_SET_BEST] +CategoryId = Engine +SimEventName = "MIXTURE_SET_BEST" +Name = "Set mixture levers to current best power setting" +Description = "Set mixture levers to current best power setting" +System = "Shared Cockpit" + +[MIXTURE_DECR_SMALL] +CategoryId = Engine +SimEventName = "MIXTURE_DECR_SMALL" +Name = "Decrement mixture levers small" +Description = "Decrement mixture levers small" +System = "Shared Cockpit" + +[MIXTURE1_DECR_SMALL] +CategoryId = Engine +SimEventName = "MIXTURE1_DECR_SMALL" +Name = "Decrement mixture lever 1 small" +Description = "Decrement mixture lever 1 small" +System = "Shared Cockpit" + +[MIXTURE2_DECR_SMALL] +CategoryId = Engine +SimEventName = "MIXTURE2_DECR_SMALL" +Name = "Decrement mixture lever 4 small" +Description = "Decrement mixture lever 4 small" +System = "Shared Cockpit" + +[MIXTURE3_DECR_SMALL] +CategoryId = Engine +SimEventName = "MIXTURE3_DECR_SMALL" +Name = "Decrement mixture lever 4 small" +Description = "Decrement mixture lever 4 small" +System = "Shared Cockpit" + +[MIXTURE4_DECR_SMALL] +CategoryId = Engine +SimEventName = "MIXTURE4_DECR_SMALL" +Name = "Decrement mixture lever 4 small" +Description = "Decrement mixture lever 4 small" +System = "Shared Cockpit" + +[PROP_PITCH_SET] +CategoryId = Engine +SimEventName = "PROP_PITCH_SET" +Name = "Set prop pitch levers (0 to 16384)" +Description = "Set prop pitch levers (0 to 16384)" +System = "Shared Cockpit" + +[PROP_PITCH_LO] +CategoryId = Engine +SimEventName = "PROP_PITCH_LO" +Name = "Set prop pitch levers max (lo pitch)" +Description = "Set prop pitch levers max (lo pitch)" +System = "Shared Cockpit" + +[PROP_PITCH_INCR] +CategoryId = Engine +SimEventName = "PROP_PITCH_INCR" +Name = "Increment prop pitch levers" +Description = "Increment prop pitch levers" +System = "Shared Cockpit" + +[PROP_PITCH_INCR_SMALL] +CategoryId = Engine +SimEventName = "PROP_PITCH_INCR_SMALL" +Name = "Increment prop pitch levers small" +Description = "Increment prop pitch levers small" +System = "Shared Cockpit" + +[PROP_PITCH_DECR] +CategoryId = Engine +SimEventName = "PROP_PITCH_DECR" +Name = "Decrement prop pitch levers" +Description = "Decrement prop pitch levers" +System = "Shared Cockpit" + +[PROP_PITCH_HI] +CategoryId = Engine +SimEventName = "PROP_PITCH_HI" +Name = "Set prop pitch levers min (hi pitch)" +Description = "Set prop pitch levers min (hi pitch)" +System = "Shared Cockpit" + +[PROP_PITCH1_SET] +CategoryId = Engine +SimEventName = "PROP_PITCH1_SET" +Name = "Set prop pitch lever 1 exact value (0 to 16384)" +Description = "Set prop pitch lever 1 exact value (0 to 16384)" +System = "Shared Cockpit" + +[PROP_PITCH2_SET] +CategoryId = Engine +SimEventName = "PROP_PITCH2_SET" +Name = "Set prop pitch lever 2 exact value (0 to 16384)" +Description = "Set prop pitch lever 2 exact value (0 to 16384)" +System = "Shared Cockpit" + +[PROP_PITCH3_SET] +CategoryId = Engine +SimEventName = "PROP_PITCH3_SET" +Name = "Set prop pitch lever 3 exact value (0 to 16384)" +Description = "Set prop pitch lever 3 exact value (0 to 16384)" +System = "Shared Cockpit" + +[PROP_PITCH4_SET] +CategoryId = Engine +SimEventName = "PROP_PITCH4_SET" +Name = "Set prop pitch lever 4 exact value (0 to 16384)" +Description = "Set prop pitch lever 4 exact value (0 to 16384)" +System = "Shared Cockpit" + +[PROP_PITCH1_LO] +CategoryId = Engine +SimEventName = "PROP_PITCH1_LO" +Name = "Set prop pitch lever 1 max (lo pitch)" +Description = "Set prop pitch lever 1 max (lo pitch)" +System = "Shared Cockpit" + +[PROP_PITCH1_INCR] +CategoryId = Engine +SimEventName = "PROP_PITCH1_INCR" +Name = "Increment prop pitch lever 1" +Description = "Increment prop pitch lever 1" +System = "Shared Cockpit" + +[PROP_PITCH1_INCR_SMALL] +CategoryId = Engine +SimEventName = "PROP_PITCH1_INCR_SMALL" +Name = "Increment prop pitch lever 1 small" +Description = "Increment prop pitch lever 1 small" +System = "Shared Cockpit" + +[PROP_PITCH1_DECR] +CategoryId = Engine +SimEventName = "PROP_PITCH1_DECR" +Name = "Decrement prop pitch lever 1" +Description = "Decrement prop pitch lever 1" +System = "Shared Cockpit" + +[PROP_PITCH1_HI] +CategoryId = Engine +SimEventName = "PROP_PITCH1_HI" +Name = "Set prop pitch lever 1 min (hi pitch)" +Description = "Set prop pitch lever 1 min (hi pitch)" +System = "Shared Cockpit" + +[PROP_PITCH2_LO] +CategoryId = Engine +SimEventName = "PROP_PITCH2_LO" +Name = "Set prop pitch lever 2 max (lo pitch)" +Description = "Set prop pitch lever 2 max (lo pitch)" +System = "Shared Cockpit" + +[PROP_PITCH2_INCR] +CategoryId = Engine +SimEventName = "PROP_PITCH2_INCR" +Name = "Increment prop pitch lever 2" +Description = "Increment prop pitch lever 2" +System = "Shared Cockpit" + +[PROP_PITCH2_INCR_SMALL] +CategoryId = Engine +SimEventName = "PROP_PITCH2_INCR_SMALL" +Name = "Increment prop pitch lever 2 small" +Description = "Increment prop pitch lever 2 small" +System = "Shared Cockpit" + +[PROP_PITCH2_DECR] +CategoryId = Engine +SimEventName = "PROP_PITCH2_DECR" +Name = "Decrement prop pitch lever 2" +Description = "Decrement prop pitch lever 2" +System = "Shared Cockpit" + +[PROP_PITCH2_HI] +CategoryId = Engine +SimEventName = "PROP_PITCH2_HI" +Name = "Set prop pitch lever 2 min (hi pitch)" +Description = "Set prop pitch lever 2 min (hi pitch)" +System = "Shared Cockpit" + +[PROP_PITCH3_LO] +CategoryId = Engine +SimEventName = "PROP_PITCH3_LO" +Name = "Set prop pitch lever 3 max (lo pitch)" +Description = "Set prop pitch lever 3 max (lo pitch)" +System = "Shared Cockpit" + +[PROP_PITCH3_INCR] +CategoryId = Engine +SimEventName = "PROP_PITCH3_INCR" +Name = "Increment prop pitch lever 3" +Description = "Increment prop pitch lever 3" +System = "Shared Cockpit" + +[PROP_PITCH3_INCR_SMALL] +CategoryId = Engine +SimEventName = "PROP_PITCH3_INCR_SMALL" +Name = "Increment prop pitch lever 3 small" +Description = "Increment prop pitch lever 3 small" +System = "Shared Cockpit" + +[PROP_PITCH3_DECR] +CategoryId = Engine +SimEventName = "PROP_PITCH3_DECR" +Name = "Decrement prop pitch lever 3" +Description = "Decrement prop pitch lever 3" +System = "Shared Cockpit" + +[PROP_PITCH3_HI] +CategoryId = Engine +SimEventName = "PROP_PITCH3_HI" +Name = "Set prop pitch lever 3 min (hi pitch)" +Description = "Set prop pitch lever 3 min (hi pitch)" +System = "Shared Cockpit" + +[PROP_PITCH4_LO] +CategoryId = Engine +SimEventName = "PROP_PITCH4_LO" +Name = "Set prop pitch lever 4 max (lo pitch)" +Description = "Set prop pitch lever 4 max (lo pitch)" +System = "Shared Cockpit" + +[PROP_PITCH4_INCR] +CategoryId = Engine +SimEventName = "PROP_PITCH4_INCR" +Name = "Increment prop pitch lever 4" +Description = "Increment prop pitch lever 4" +System = "Shared Cockpit" + +[PROP_PITCH4_INCR_SMALL] +CategoryId = Engine +SimEventName = "PROP_PITCH4_INCR_SMALL" +Name = "Increment prop pitch lever 4 small" +Description = "Increment prop pitch lever 4 small" +System = "Shared Cockpit" + +[PROP_PITCH4_DECR] +CategoryId = Engine +SimEventName = "PROP_PITCH4_DECR" +Name = "Decrement prop pitch lever 4" +Description = "Decrement prop pitch lever 4" +System = "Shared Cockpit" + +[PROP_PITCH4_HI] +CategoryId = Engine +SimEventName = "PROP_PITCH4_HI" +Name = "Set prop pitch lever 4 min (hi pitch)" +Description = "Set prop pitch lever 4 min (hi pitch)" +System = "Shared Cockpit" + +[AXIS_PROPELLER_SET] +CategoryId = Engine +SimEventName = "AXIS_PROPELLER_SET" +Name = "Set propeller levers exact value (-16384 to +16384)" +Description = "Set propeller levers exact value (-16384 to +16384)" +System = "Shared Cockpit" + +[AXIS_PROPELLER1_SET] +CategoryId = Engine +SimEventName = "AXIS_PROPELLER1_SET" +Name = "Set propeller lever 1 exact value (-16384 to +16384)" +Description = "Set propeller lever 1 exact value (-16384 to +16384)" +System = "Shared Cockpit" + +[AXIS_PROPELLER2_SET] +CategoryId = Engine +SimEventName = "AXIS_PROPELLER2_SET" +Name = "Set propeller lever 2 exact value (-16384 to +16384)" +Description = "Set propeller lever 2 exact value (-16384 to +16384)" +System = "Shared Cockpit" + +[AXIS_PROPELLER3_SET] +CategoryId = Engine +SimEventName = "AXIS_PROPELLER3_SET" +Name = "Set propeller lever 3 exact value (-16384 to +16384)" +Description = "Set propeller lever 3 exact value (-16384 to +16384)" +System = "Shared Cockpit" + +[AXIS_PROPELLER4_SET] +CategoryId = Engine +SimEventName = "AXIS_PROPELLER4_SET" +Name = "Set propeller lever 4 exact value (-16384 to +16384)" +Description = "Set propeller lever 4 exact value (-16384 to +16384)" +System = "Shared Cockpit" + +[JET_STARTER] +CategoryId = Engine +SimEventName = "JET_STARTER" +Name = "Selects jet engine starter (for +/- sequence)" +Description = "Selects jet engine starter (for +/- sequence)" +System = "Shared Cockpit" + +[MAGNETO_SET] +CategoryId = Engine +SimEventName = "MAGNETO_SET" +Name = "Sets magnetos (0,1)" +Description = "Sets magnetos (0,1)" +System = "Shared Cockpit" + +[TOGGLE_STARTER1] +CategoryId = Engine +SimEventName = "TOGGLE_STARTER1" +Name = "Toggle starter 1" +Description = "Toggle starter 1" +System = "Shared Cockpit" + +[TOGGLE_STARTER2] +CategoryId = Engine +SimEventName = "TOGGLE_STARTER2" +Name = "Toggle starter 2" +Description = "Toggle starter 2" +System = "Shared Cockpit" + +[TOGGLE_STARTER3] +CategoryId = Engine +SimEventName = "TOGGLE_STARTER3" +Name = "Toggle starter 3" +Description = "Toggle starter 3" +System = "Shared Cockpit" + +[TOGGLE_STARTER4] +CategoryId = Engine +SimEventName = "TOGGLE_STARTER4" +Name = "Toggle starter 4" +Description = "Toggle starter 4" +System = "Shared Cockpit" + +[TOGGLE_ALL_STARTERS] +CategoryId = Engine +SimEventName = "TOGGLE_ALL_STARTERS" +Name = "Toggle starters" +Description = "Toggle starters" +System = "Shared Cockpit" + +[ENGINE_AUTO_START] +CategoryId = Engine +SimEventName = "ENGINE_AUTO_START" +Name = "Triggers auto-start" +Description = "Triggers auto-start" +System = "Shared Cockpit" + +[ENGINE_AUTO_SHUTDOWN] +CategoryId = Engine +SimEventName = "ENGINE_AUTO_SHUTDOWN" +Name = "Triggers auto-shutdown" +Description = "Triggers auto-shutdown" +System = "Shared Cockpit" + +[MAGNETO] +CategoryId = Engine +SimEventName = "MAGNETO" +Name = "Selects magnetos (for +/- sequence)" +Description = "Selects magnetos (for +/- sequence)" +System = "Shared Cockpit" + +[MAGNETO_DECR] +CategoryId = Engine +SimEventName = "MAGNETO_DECR" +Name = "Decrease magneto switches positions" +Description = "Decrease magneto switches positions" +System = "Shared Cockpit" + +[MAGNETO_INCR] +CategoryId = Engine +SimEventName = "MAGNETO_INCR" +Name = "Increase magneto switches positions" +Description = "Increase magneto switches positions" +System = "Shared Cockpit" + +[MAGNETO1_OFF] +CategoryId = Engine +SimEventName = "MAGNETO1_OFF" +Name = "Set engine 1 magnetos off" +Description = "Set engine 1 magnetos off" +System = "Shared Cockpit" + +[MAGNETO1_RIGHT] +CategoryId = Engine +SimEventName = "MAGNETO1_RIGHT" +Name = "Toggle engine 1 right magneto" +Description = "Toggle engine 1 right magneto" +System = "All aircraft" + +[MAGNETO1_LEFT] +CategoryId = Engine +SimEventName = "MAGNETO1_LEFT" +Name = "Toggle engine 1 left magneto" +Description = "Toggle engine 1 left magneto" +System = "All aircraft" + +[MAGNETO1_BOTH] +CategoryId = Engine +SimEventName = "MAGNETO1_BOTH" +Name = "Set engine 1 magnetos on" +Description = "Set engine 1 magnetos on" +System = "Shared Cockpit" + +[MAGNETO1_START] +CategoryId = Engine +SimEventName = "MAGNETO1_START" +Name = "Set engine 1 magnetos on and toggle starter" +Description = "Set engine 1 magnetos on and toggle starter" +System = "Shared Cockpit" + +[MAGNETO2_OFF] +CategoryId = Engine +SimEventName = "MAGNETO2_OFF" +Name = "Set engine 2 magnetos off" +Description = "Set engine 2 magnetos off" +System = "Shared Cockpit" + +[MAGNETO2_RIGHT] +CategoryId = Engine +SimEventName = "MAGNETO2_RIGHT" +Name = "Toggle engine 2 right magneto" +Description = "Toggle engine 2 right magneto" +System = "All aircraft" + +[MAGNETO2_LEFT] +CategoryId = Engine +SimEventName = "MAGNETO2_LEFT" +Name = "Toggle engine 2 left magneto" +Description = "Toggle engine 2 left magneto" +System = "All aircraft" + +[MAGNETO2_BOTH] +CategoryId = Engine +SimEventName = "MAGNETO2_BOTH" +Name = "Set engine 2 magnetos on" +Description = "Set engine 2 magnetos on" +System = "Shared Cockpit" + +[MAGNETO2_START] +CategoryId = Engine +SimEventName = "MAGNETO2_START" +Name = "Set engine 2 magnetos on and toggle starter" +Description = "Set engine 2 magnetos on and toggle starter" +System = "Shared Cockpit" + +[MAGNETO3_OFF] +CategoryId = Engine +SimEventName = "MAGNETO3_OFF" +Name = "Set engine 3 magnetos off" +Description = "Set engine 3 magnetos off" +System = "Shared Cockpit" + +[MAGNETO3_RIGHT] +CategoryId = Engine +SimEventName = "MAGNETO3_RIGHT" +Name = "Toggle engine 3 right magneto" +Description = "Toggle engine 3 right magneto" +System = "All aircraft" + +[MAGNETO3_LEFT] +CategoryId = Engine +SimEventName = "MAGNETO3_LEFT" +Name = "Toggle engine 3 left magneto" +Description = "Toggle engine 3 left magneto" +System = "All aircraft" + +[MAGNETO3_BOTH] +CategoryId = Engine +SimEventName = "MAGNETO3_BOTH" +Name = "Set engine 3 magnetos on" +Description = "Set engine 3 magnetos on" +System = "Shared Cockpit" + +[MAGNETO3_START] +CategoryId = Engine +SimEventName = "MAGNETO3_START" +Name = "Set engine 3 magnetos on and toggle starter" +Description = "Set engine 3 magnetos on and toggle starter" +System = "Shared Cockpit" + +[MAGNETO4_OFF] +CategoryId = Engine +SimEventName = "MAGNETO4_OFF" +Name = "Set engine 4 magnetos off" +Description = "Set engine 4 magnetos off" +System = "Shared Cockpit" + +[MAGNETO4_RIGHT] +CategoryId = Engine +SimEventName = "MAGNETO4_RIGHT" +Name = "Toggle engine 4 right magneto" +Description = "Toggle engine 4 right magneto" +System = "All aircraft" + +[MAGNETO4_LEFT] +CategoryId = Engine +SimEventName = "MAGNETO4_LEFT" +Name = "Toggle engine 4 left magneto" +Description = "Toggle engine 4 left magneto" +System = "All aircraft" + +[MAGNETO4_BOTH] +CategoryId = Engine +SimEventName = "MAGNETO4_BOTH" +Name = "Set engine 4 magnetos on" +Description = "Set engine 4 magnetos on" +System = "Shared Cockpit" + +[MAGNETO4_START] +CategoryId = Engine +SimEventName = "MAGNETO4_START" +Name = "Set engine 4 magnetos on and toggle starter" +Description = "Set engine 4 magnetos on and toggle starter" +System = "Shared Cockpit" + +[MAGNETO_OFF] +CategoryId = Engine +SimEventName = "MAGNETO_OFF" +Name = "Set engine magnetos off" +Description = "Set engine magnetos off" +System = "Shared Cockpit" + +[MAGNETO_RIGHT] +CategoryId = Engine +SimEventName = "MAGNETO_RIGHT" +Name = "Set engine right magnetos on" +Description = "Set engine right magnetos on" +System = "Shared Cockpit" + +[MAGNETO_LEFT] +CategoryId = Engine +SimEventName = "MAGNETO_LEFT" +Name = "Set engine left magnetos on" +Description = "Set engine left magnetos on" +System = "Shared Cockpit" + +[MAGNETO_BOTH] +CategoryId = Engine +SimEventName = "MAGNETO_BOTH" +Name = "Set engine magnetos on" +Description = "Set engine magnetos on" +System = "Shared Cockpit" + +[MAGNETO_START] +CategoryId = Engine +SimEventName = "MAGNETO_START" +Name = "Set engine magnetos on and toggle starters" +Description = "Set engine magnetos on and toggle starters" +System = "Shared Cockpit" + +[MAGNETO1_DECR] +CategoryId = Engine +SimEventName = "MAGNETO1_DECR" +Name = "Decrease engine 1 magneto switch position" +Description = "Decrease engine 1 magneto switch position" +System = "Shared Cockpit" + +[MAGNETO1_INCR] +CategoryId = Engine +SimEventName = "MAGNETO1_INCR" +Name = "Increase engine 1 magneto switch position" +Description = "Increase engine 1 magneto switch position" +System = "Shared Cockpit" + +[MAGNETO2_DECR] +CategoryId = Engine +SimEventName = "MAGNETO2_DECR" +Name = "Decrease engine 2 magneto switch position" +Description = "Decrease engine 2 magneto switch position" +System = "Shared Cockpit" + +[MAGNETO2_INCR] +CategoryId = Engine +SimEventName = "MAGNETO2_INCR" +Name = "Increase engine 2 magneto switch position" +Description = "Increase engine 2 magneto switch position" +System = "Shared Cockpit" + +[MAGNETO3_DECR] +CategoryId = Engine +SimEventName = "MAGNETO3_DECR" +Name = "Decrease engine 3 magneto switch position" +Description = "Decrease engine 3 magneto switch position" +System = "Shared Cockpit" + +[MAGNETO3_INCR] +CategoryId = Engine +SimEventName = "MAGNETO3_INCR" +Name = "Increase engine 3 magneto switch position" +Description = "Increase engine 3 magneto switch position" +System = "Shared Cockpit" + +[MAGNETO4_DECR] +CategoryId = Engine +SimEventName = "MAGNETO4_DECR" +Name = "Decrease engine 4 magneto switch position" +Description = "Decrease engine 4 magneto switch position" +System = "Shared Cockpit" + +[MAGNETO4_INCR] +CategoryId = Engine +SimEventName = "MAGNETO4_INCR" +Name = "Increase engine 4 magneto switch position" +Description = "Increase engine 4 magneto switch position" +System = "Shared Cockpit" + +[Not supported] +CategoryId = Engine +SimEventName = "Not supported" +Name = "Set engine magneto switches" +Description = "Set engine magneto switches" +System = "Shared Cockpit" + +[MAGNETO1_SET] +CategoryId = Engine +SimEventName = "MAGNETO1_SET" +Name = "Set engine 1 magneto switch" +Description = "Set engine 1 magneto switch" +System = "Shared Cockpit" + +[MAGNETO2_SET] +CategoryId = Engine +SimEventName = "MAGNETO2_SET" +Name = "Set engine 2 magneto switch" +Description = "Set engine 2 magneto switch" +System = "Shared Cockpit" + +[MAGNETO3_SET] +CategoryId = Engine +SimEventName = "MAGNETO3_SET" +Name = "Set engine 3 magneto switch" +Description = "Set engine 3 magneto switch" +System = "Shared Cockpit" + +[MAGNETO4_SET] +CategoryId = Engine +SimEventName = "MAGNETO4_SET" +Name = "Set engine 4 magneto switch" +Description = "Set engine 4 magneto switch" +System = "Shared Cockpit" + +[ANTI_ICE_ON] +CategoryId = Engine +SimEventName = "ANTI_ICE_ON" +Name = "Sets anti-ice switches on" +Description = "Sets anti-ice switches on" +System = "Shared Cockpit" + +[ANTI_ICE_OFF] +CategoryId = Engine +SimEventName = "ANTI_ICE_OFF" +Name = "Sets anti-ice switches off" +Description = "Sets anti-ice switches off" +System = "Shared Cockpit" + +[ANTI_ICE_SET] +CategoryId = Engine +SimEventName = "ANTI_ICE_SET" +Name = "Sets anti-ice switches from argument (0,1)" +Description = "Sets anti-ice switches from argument (0,1)" +System = "Shared Cockpit" + +[ANTI_ICE_TOGGLE] +CategoryId = Engine +SimEventName = "ANTI_ICE_TOGGLE" +Name = "Toggle anti-ice switches" +Description = "Toggle anti-ice switches" +System = "Shared Cockpit" + +[ANTI_ICE_TOGGLE_ENG1] +CategoryId = Engine +SimEventName = "ANTI_ICE_TOGGLE_ENG1" +Name = "Toggle engine 1 anti-ice switch" +Description = "Toggle engine 1 anti-ice switch" +System = "Shared Cockpit" + +[ANTI_ICE_TOGGLE_ENG2] +CategoryId = Engine +SimEventName = "ANTI_ICE_TOGGLE_ENG2" +Name = "Toggle engine 2 anti-ice switch" +Description = "Toggle engine 2 anti-ice switch" +System = "Shared Cockpit" + +[ANTI_ICE_TOGGLE_ENG3] +CategoryId = Engine +SimEventName = "ANTI_ICE_TOGGLE_ENG3" +Name = "Toggle engine 3 anti-ice switch" +Description = "Toggle engine 3 anti-ice switch" +System = "Shared Cockpit" + +[ANTI_ICE_TOGGLE_ENG4] +CategoryId = Engine +SimEventName = "ANTI_ICE_TOGGLE_ENG4" +Name = "Toggle engine 4 anti-ice switch" +Description = "Toggle engine 4 anti-ice switch" +System = "Shared Cockpit" + +[ANTI_ICE_SET_ENG1] +CategoryId = Engine +SimEventName = "ANTI_ICE_SET_ENG1" +Name = "Sets engine 1 anti-ice switch (0,1)" +Description = "Sets engine 1 anti-ice switch (0,1)" +System = "Shared Cockpit" + +[ANTI_ICE_SET_ENG2] +CategoryId = Engine +SimEventName = "ANTI_ICE_SET_ENG2" +Name = "Sets engine 2 anti-ice switch (0,1)" +Description = "Sets engine 2 anti-ice switch (0,1)" +System = "Shared Cockpit" + +[ANTI_ICE_SET_ENG3] +CategoryId = Engine +SimEventName = "ANTI_ICE_SET_ENG3" +Name = "Sets engine 3 anti-ice switch (0,1)" +Description = "Sets engine 3 anti-ice switch (0,1)" +System = "Shared Cockpit" + +[ANTI_ICE_SET_ENG4] +CategoryId = Engine +SimEventName = "ANTI_ICE_SET_ENG4" +Name = "Sets engine 4 anti-ice switch (0,1)" +Description = "Sets engine 4 anti-ice switch (0,1)" +System = "Shared Cockpit" + +[TOGGLE_FUEL_VALVE_ALL] +CategoryId = Engine +SimEventName = "TOGGLE_FUEL_VALVE_ALL" +Name = "Toggle engine fuel valves" +Description = "Toggle engine fuel valves" +System = "Shared Cockpit" + +[TOGGLE_FUEL_VALVE_ENG1] +CategoryId = Engine +SimEventName = "TOGGLE_FUEL_VALVE_ENG1" +Name = "Toggle engine 1 fuel valve" +Description = "Toggle engine 1 fuel valve" +System = "All aircraft" + +[TOGGLE_FUEL_VALVE_ENG2] +CategoryId = Engine +SimEventName = "TOGGLE_FUEL_VALVE_ENG2" +Name = "Toggle engine 2 fuel valve" +Description = "Toggle engine 2 fuel valve" +System = "All aircraft" + +[TOGGLE_FUEL_VALVE_ENG3] +CategoryId = Engine +SimEventName = "TOGGLE_FUEL_VALVE_ENG3" +Name = "Toggle engine 3 fuel valve" +Description = "Toggle engine 3 fuel valve" +System = "All aircraft" + +[TOGGLE_FUEL_VALVE_ENG4] +CategoryId = Engine +SimEventName = "TOGGLE_FUEL_VALVE_ENG4" +Name = "Toggle engine 4 fuel valve" +Description = "Toggle engine 4 fuel valve" +System = "All aircraft" + +[COWLFLAP1_SET] +CategoryId = Engine +SimEventName = "COWLFLAP1_SET" +Name = "Sets engine 1 cowl flap lever position (0 to 16384)" +Description = "Sets engine 1 cowl flap lever position (0 to 16384)" +System = "Shared Cockpit" + +[COWLFLAP2_SET] +CategoryId = Engine +SimEventName = "COWLFLAP2_SET" +Name = "Sets engine 2 cowl flap lever position (0 to 16384)" +Description = "Sets engine 2 cowl flap lever position (0 to 16384)" +System = "Shared Cockpit" + +[COWLFLAP3_SET] +CategoryId = Engine +SimEventName = "COWLFLAP3_SET" +Name = "Sets engine 3 cowl flap lever position (0 to 16384)" +Description = "Sets engine 3 cowl flap lever position (0 to 16384)" +System = "Shared Cockpit" + +[COWLFLAP4_SET] +CategoryId = Engine +SimEventName = "COWLFLAP4_SET" +Name = "Sets engine 4 cowl flap lever position (0 to 16384)" +Description = "Sets engine 4 cowl flap lever position (0 to 16384)" +System = "Shared Cockpit" + +[INC_COWL_FLAPS] +CategoryId = Engine +SimEventName = "INC_COWL_FLAPS" +Name = "Increment cowl flap levers" +Description = "Increment cowl flap levers" +System = "Shared Cockpit" + +[DEC_COWL_FLAPS] +CategoryId = Engine +SimEventName = "DEC_COWL_FLAPS" +Name = "Decrement cowl flap levers" +Description = "Decrement cowl flap levers" +System = "Shared Cockpit" + +[INC_COWL_FLAPS1] +CategoryId = Engine +SimEventName = "INC_COWL_FLAPS1" +Name = "Increment engine 1 cowl flap lever" +Description = "Increment engine 1 cowl flap lever" +System = "Shared Cockpit" + +[DEC_COWL_FLAPS1] +CategoryId = Engine +SimEventName = "DEC_COWL_FLAPS1" +Name = "Decrement engine 1 cowl flap lever" +Description = "Decrement engine 1 cowl flap lever" +System = "Shared Cockpit" + +[INC_COWL_FLAPS2] +CategoryId = Engine +SimEventName = "INC_COWL_FLAPS2" +Name = "Increment engine 2 cowl flap lever" +Description = "Increment engine 2 cowl flap lever" +System = "Shared Cockpit" + +[DEC_COWL_FLAPS2] +CategoryId = Engine +SimEventName = "DEC_COWL_FLAPS2" +Name = "Decrement engine 2 cowl flap lever" +Description = "Decrement engine 2 cowl flap lever" +System = "Shared Cockpit" + +[INC_COWL_FLAPS3] +CategoryId = Engine +SimEventName = "INC_COWL_FLAPS3" +Name = "Increment engine 3 cowl flap lever" +Description = "Increment engine 3 cowl flap lever" +System = "Shared Cockpit" + +[DEC_COWL_FLAPS3] +CategoryId = Engine +SimEventName = "DEC_COWL_FLAPS3" +Name = "Decrement engine 3 cowl flap lever" +Description = "Decrement engine 3 cowl flap lever" +System = "Shared Cockpit" + +[INC_COWL_FLAPS4] +CategoryId = Engine +SimEventName = "INC_COWL_FLAPS4" +Name = "Increment engine 4 cowl flap lever" +Description = "Increment engine 4 cowl flap lever" +System = "Shared Cockpit" + +[DEC_COWL_FLAPS4] +CategoryId = Engine +SimEventName = "DEC_COWL_FLAPS4" +Name = "Decrement engine 4 cowl flap lever" +Description = "Decrement engine 4 cowl flap lever" +System = "Shared Cockpit" + +[FUEL_PUMP] +CategoryId = Engine +SimEventName = "FUEL_PUMP" +Name = "Toggle electric fuel pumps" +Description = "Toggle electric fuel pumps" +System = "Shared Cockpit" + +[TOGGLE_ELECT_FUEL_PUMP] +CategoryId = Engine +SimEventName = "TOGGLE_ELECT_FUEL_PUMP" +Name = "Toggle electric fuel pumps" +Description = "Toggle electric fuel pumps" +System = "Shared Cockpit" + +[TOGGLE_ELECT_FUEL_PUMP1] +CategoryId = Engine +SimEventName = "TOGGLE_ELECT_FUEL_PUMP1" +Name = "Toggle engine 1 electric fuel pump" +Description = "Toggle engine 1 electric fuel pump" +System = "All aircraft" + +[TOGGLE_ELECT_FUEL_PUMP2] +CategoryId = Engine +SimEventName = "TOGGLE_ELECT_FUEL_PUMP2" +Name = "Toggle engine 2 electric fuel pump" +Description = "Toggle engine 2 electric fuel pump" +System = "All aircraft" + +[TOGGLE_ELECT_FUEL_PUMP3] +CategoryId = Engine +SimEventName = "TOGGLE_ELECT_FUEL_PUMP3" +Name = "Toggle engine 3 electric fuel pump" +Description = "Toggle engine 3 electric fuel pump" +System = "All aircraft" + +[TOGGLE_ELECT_FUEL_PUMP4] +CategoryId = Engine +SimEventName = "TOGGLE_ELECT_FUEL_PUMP4" +Name = "Toggle engine 4 electric fuel pump" +Description = "Toggle engine 4 electric fuel pump" +System = "All aircraft" + +[ENGINE_PRIMER] +CategoryId = Engine +SimEventName = "ENGINE_PRIMER" +Name = "Trigger engine primers" +Description = "Trigger engine primers" +System = "Shared Cockpit" + +[TOGGLE_PRIMER] +CategoryId = Engine +SimEventName = "TOGGLE_PRIMER" +Name = "Trigger engine primers" +Description = "Trigger engine primers" +System = "Shared Cockpit" + +[TOGGLE_PRIMER1] +CategoryId = Engine +SimEventName = "TOGGLE_PRIMER1" +Name = "Trigger engine 1 primer" +Description = "Trigger engine 1 primer" +System = "Shared Cockpit" + +[TOGGLE_PRIMER2] +CategoryId = Engine +SimEventName = "TOGGLE_PRIMER2" +Name = "Trigger engine 2 primer" +Description = "Trigger engine 2 primer" +System = "Shared Cockpit" + +[TOGGLE_PRIMER3] +CategoryId = Engine +SimEventName = "TOGGLE_PRIMER3" +Name = "Trigger engine 3 primer" +Description = "Trigger engine 3 primer" +System = "Shared Cockpit" + +[TOGGLE_PRIMER4] +CategoryId = Engine +SimEventName = "TOGGLE_PRIMER4" +Name = "Trigger engine 4 primer" +Description = "Trigger engine 4 primer" +System = "Shared Cockpit" + +[TOGGLE_FEATHER_SWITCHES] +CategoryId = Engine +SimEventName = "TOGGLE_FEATHER_SWITCHES" +Name = "Trigger propeller switches" +Description = "Trigger propeller switches" +System = "Shared Cockpit" + +[TOGGLE_FEATHER_SWITCH_1] +CategoryId = Engine +SimEventName = "TOGGLE_FEATHER_SWITCH_1" +Name = "Trigger propeller 1 switch" +Description = "Trigger propeller 1 switch" +System = "Shared Cockpit" + +[TOGGLE_FEATHER_SWITCH_2] +CategoryId = Engine +SimEventName = "TOGGLE_FEATHER_SWITCH_2" +Name = "Trigger propeller 2 switch" +Description = "Trigger propeller 2 switch" +System = "Shared Cockpit" + +[TOGGLE_FEATHER_SWITCH_3] +CategoryId = Engine +SimEventName = "TOGGLE_FEATHER_SWITCH_3" +Name = "Trigger propeller 3 switch" +Description = "Trigger propeller 3 switch" +System = "Shared Cockpit" + +[TOGGLE_FEATHER_SWITCH_4] +CategoryId = Engine +SimEventName = "TOGGLE_FEATHER_SWITCH_4" +Name = "Trigger propeller 4 switch" +Description = "Trigger propeller 4 switch" +System = "Shared Cockpit" + +[TOGGLE_PROPELLER_SYNC] +CategoryId = Engine +SimEventName = "TOGGLE_PROPELLER_SYNC" +Name = "Turns propeller synchronization switch on" +Description = "Turns propeller synchronization switch on" +System = "Shared Cockpit" + +[TOGGLE_AUTOFEATHER_ARM] +CategoryId = Engine +SimEventName = "TOGGLE_AUTOFEATHER_ARM" +Name = "Turns auto-feather arming switch on" +Description = "Turns auto-feather arming switch on." +System = "Shared Cockpit" + +[TOGGLE_AFTERBURNER] +CategoryId = Engine +SimEventName = "TOGGLE_AFTERBURNER" +Name = "Toggles afterburners" +Description = "Toggles afterburners" +System = "Shared Cockpit" + +[TOGGLE_AFTERBURNER1] +CategoryId = Engine +SimEventName = "TOGGLE_AFTERBURNER1" +Name = "Toggles engine 1 afterburner" +Description = "Toggles engine 1 afterburner" +System = "Shared Cockpit" + +[TOGGLE_AFTERBURNER2] +CategoryId = Engine +SimEventName = "TOGGLE_AFTERBURNER2" +Name = "Toggles engine 2 afterburner" +Description = "Toggles engine 2 afterburner" +System = "Shared Cockpit" + +[TOGGLE_AFTERBURNER3] +CategoryId = Engine +SimEventName = "TOGGLE_AFTERBURNER3" +Name = "Toggles engine 3 afterburner" +Description = "Toggles engine 3 afterburner" +System = "Shared Cockpit" + +[TOGGLE_AFTERBURNER4] +CategoryId = Engine +SimEventName = "TOGGLE_AFTERBURNER4" +Name = "Toggles engine 4 afterburner" +Description = "Toggles engine 4 afterburner" +System = "Shared Cockpit" + +[ENGINE] +CategoryId = Engine +SimEventName = "ENGINE" +Name = "Sets engines for 1,2,3,4 selection (to be followed by SELECT_n)" +Description = "Sets engines for 1,2,3,4 selection (to be followed by SELECT_n)" +System = "Shared Cockpit" + + + +# Flight_Controls ####################### +# +[category_Flight_Controls] +Name = "Flight Controls" + + +[SPOILERS_TOGGLE] +CategoryId = Flight_Controls +SimEventName = "SPOILERS_TOGGLE" +Name = "Toggles spoiler handle" +Description = "Toggles spoiler handle " +System = "All aircraft" + +[FLAPS_UP] +CategoryId = Flight_Controls +SimEventName = "FLAPS_UP" +Name = "Sets flap handle to full retract position" +Description = "Sets flap handle to full retract position" +System = "All aircraft" + +[FLAPS_1] +CategoryId = Flight_Controls +SimEventName = "FLAPS_1" +Name = "Sets flap handle to first extension position" +Description = "Sets flap handle to first extension position" +System = "All aircraft" + +[FLAPS_2] +CategoryId = Flight_Controls +SimEventName = "FLAPS_2" +Name = "Sets flap handle to second extension position" +Description = "Sets flap handle to second extension position" +System = "All aircraft" + +[FLAPS_3] +CategoryId = Flight_Controls +SimEventName = "FLAPS_3" +Name = "Sets flap handle to third extension position" +Description = "Sets flap handle to third extension position" +System = "All aircraft" + +[FLAPS_DOWN] +CategoryId = Flight_Controls +SimEventName = "FLAPS_DOWN" +Name = "Sets flap handle to full extension position" +Description = "Sets flap handle to full extension position" +System = "All aircraft" + +[ELEV_TRIM_DN] +CategoryId = Flight_Controls +SimEventName = "ELEV_TRIM_DN" +Name = "Increments elevator trim down" +Description = "Increments elevator trim down" +System = "Shared Cockpit" + +[ELEV_DOWN] +CategoryId = Flight_Controls +SimEventName = "ELEV_DOWN" +Name = "Increments elevator down" +Description = "Increments elevator down" +System = "Shared Cockpit (Pilot only),." + +[AILERONS_LEFT] +CategoryId = Flight_Controls +SimEventName = "AILERONS_LEFT" +Name = "Increments ailerons left" +Description = "Increments ailerons left" +System = "Shared Cockpit (Pilot only),." + +[CENTER_AILER_RUDDER] +CategoryId = Flight_Controls +SimEventName = "CENTER_AILER_RUDDER" +Name = "Centers aileron and rudder positions" +Description = "Centers aileron and rudder positions" +System = "Shared Cockpit" + +[AILERONS_RIGHT] +CategoryId = Flight_Controls +SimEventName = "AILERONS_RIGHT" +Name = "Increments ailerons right" +Description = "Increments ailerons right" +System = "Shared Cockpit (Pilot only),." + +[ELEV_TRIM_UP] +CategoryId = Flight_Controls +SimEventName = "ELEV_TRIM_UP" +Name = "Increment elevator trim up" +Description = "Increment elevator trim up" +System = "Shared Cockpit" + +[ELEV_UP] +CategoryId = Flight_Controls +SimEventName = "ELEV_UP" +Name = "Increments elevator up" +Description = "Increments elevator up" +System = "Shared Cockpit (Pilot only),." + +[RUDDER_LEFT] +CategoryId = Flight_Controls +SimEventName = "RUDDER_LEFT" +Name = "Increments rudder left" +Description = "Increments rudder left" +System = "Shared Cockpit" + +[RUDDER_CENTER] +CategoryId = Flight_Controls +SimEventName = "RUDDER_CENTER" +Name = "Centers rudder position" +Description = "Centers rudder position" +System = "Shared Cockpit" + +[RUDDER_RIGHT] +CategoryId = Flight_Controls +SimEventName = "RUDDER_RIGHT" +Name = "Increments rudder right" +Description = "Increments rudder right" +System = "Shared Cockpit" + +[ELEVATOR_SET] +CategoryId = Flight_Controls +SimEventName = "ELEVATOR_SET" +Name = "Sets elevator position (-16384 - +16384)" +Description = "Sets elevator position (-16384 - +16384)" +System = "Shared Cockpit" + +[AILERON_SET] +CategoryId = Flight_Controls +SimEventName = "AILERON_SET" +Name = "Sets aileron position (-16384 - +16384)" +Description = "Sets aileron position (-16384 - +16384)" +System = "Shared Cockpit" + +[RUDDER_SET] +CategoryId = Flight_Controls +SimEventName = "RUDDER_SET" +Name = "Sets rudder position (-16384 - +16384)" +Description = "Sets rudder position (-16384 - +16384)" +System = "Shared Cockpit" + +[FLAPS_INCR] +CategoryId = Flight_Controls +SimEventName = "FLAPS_INCR" +Name = "Increments flap handle position" +Description = "Increments flap handle position" +System = "All aircraft" + +[FLAPS_DECR] +CategoryId = Flight_Controls +SimEventName = "FLAPS_DECR" +Name = "Decrements flap handle position" +Description = "Decrements flap handle position" +System = "All aircraft" + +[AXIS_ELEVATOR_SET] +CategoryId = Flight_Controls +SimEventName = "AXIS_ELEVATOR_SET" +Name = "Sets elevator position (-16384 - +16384)" +Description = "Sets elevator position (-16384 - +16384)" +System = "Shared Cockpit (Pilot only, and not transmitted to Co-pilot)" + +[AXIS_AILERONS_SET] +CategoryId = Flight_Controls +SimEventName = "AXIS_AILERONS_SET" +Name = "Sets aileron position (-16384 - +16384)" +Description = "Sets aileron position (-16384 - +16384)" +System = "Shared Cockpit (Pilot only, and not transmitted to Co-pilot)" + +[AXIS_RUDDER_SET] +CategoryId = Flight_Controls +SimEventName = "AXIS_RUDDER_SET" +Name = "Sets rudder position (-16384 - +16384)" +Description = "Sets rudder position (-16384 - +16384)" +System = "Shared Cockpit (Pilot only, and not transmitted to Co-pilot)" + +[AXIS_ELEV_TRIM_SET] +CategoryId = Flight_Controls +SimEventName = "AXIS_ELEV_TRIM_SET" +Name = "Sets elevator trim position (-16384 - +16384)" +Description = "Sets elevator trim position (-16384 - +16384)" +System = "Shared Cockpit" + +[SPOILERS_SET] +CategoryId = Flight_Controls +SimEventName = "SPOILERS_SET" +Name = "Sets spoiler handle position (0 to 16384)" +Description = "Sets spoiler handle position (0 to 16384)" +System = "All aircraft" + +[SPOILERS_ARM_TOGGLE] +CategoryId = Flight_Controls +SimEventName = "SPOILERS_ARM_TOGGLE" +Name = "Toggles arming of auto-spoilers" +Description = "Toggles arming of auto-spoilers" +System = "All aircraft" + +[SPOILERS_ON] +CategoryId = Flight_Controls +SimEventName = "SPOILERS_ON" +Name = "Sets spoiler handle to full extend position" +Description = "Sets spoiler handle to full extend position" +System = "All aircraft" + +[SPOILERS_OFF] +CategoryId = Flight_Controls +SimEventName = "SPOILERS_OFF" +Name = "Sets spoiler handle to full retract position" +Description = "Sets spoiler handle to full retract position" +System = "All aircraft" + +[SPOILERS_ARM_ON] +CategoryId = Flight_Controls +SimEventName = "SPOILERS_ARM_ON" +Name = "Sets auto-spoiler arming on" +Description = "Sets auto-spoiler arming on" +System = "All aircraft" + +[SPOILERS_ARM_OFF] +CategoryId = Flight_Controls +SimEventName = "SPOILERS_ARM_OFF" +Name = "Sets auto-spoiler arming off" +Description = "Sets auto-spoiler arming off" +System = "All aircraft" + +[SPOILERS_ARM_SET] +CategoryId = Flight_Controls +SimEventName = "SPOILERS_ARM_SET" +Name = "Sets auto-spoiler arming (0,1)" +Description = "Sets auto-spoiler arming (0,1)" +System = "All aircraft" + +[AILERON_TRIM_LEFT] +CategoryId = Flight_Controls +SimEventName = "AILERON_TRIM_LEFT" +Name = "Increments aileron trim left" +Description = "Increments aileron trim left" +System = "Shared Cockpit" + +[AILERON_TRIM_RIGHT] +CategoryId = Flight_Controls +SimEventName = "AILERON_TRIM_RIGHT" +Name = "Increments aileron trim right" +Description = "Increments aileron trim right" +System = "Shared Cockpit" + +[RUDDER_TRIM_LEFT] +CategoryId = Flight_Controls +SimEventName = "RUDDER_TRIM_LEFT" +Name = "Increments rudder trim left" +Description = "Increments rudder trim left" +System = "Shared Cockpit" + +[RUDDER_TRIM_RIGHT] +CategoryId = Flight_Controls +SimEventName = "RUDDER_TRIM_RIGHT" +Name = "Increments aileron trim right" +Description = "Increments aileron trim right" +System = "Shared Cockpit" + +[AXIS_SPOILER_SET] +CategoryId = Flight_Controls +SimEventName = "AXIS_SPOILER_SET" +Name = "Sets spoiler handle position (-16384 - +16384)" +Description = "Sets spoiler handle position (-16384 - +16384)" +System = "All aircraft" + +[FLAPS_SET] +CategoryId = Flight_Controls +SimEventName = "FLAPS_SET" +Name = "Sets flap handle to closest increment (0 to 16384)" +Description = "Sets flap handle to closest increment (0 to 16384)" +System = "All aircraft" + +[ELEVATOR_TRIM_SET] +CategoryId = Flight_Controls +SimEventName = "ELEVATOR_TRIM_SET" +Name = "Sets elevator trim position (0 to 16384)" +Description = "Sets elevator trim position (0 to 16384)" +System = "Shared Cockpit" + +[AXIS_FLAPS_SET] +CategoryId = Flight_Controls +SimEventName = "AXIS_FLAPS_SET" +Name = "Sets flap handle to closest increment (-16384 - +16384)" +Description = "Sets flap handle to closest increment (-16384 - +16384)" +System = "Shared Cockpit" + + + +# Autopilot ####################### +# +[category_Autopilot] +Name = "Autopilot" + + +[AP_MASTER] +CategoryId = Autopilot +SimEventName = "AP_MASTER" +Name = "Toggles AP on/off" +Description = "Toggles AP on/off" +System = "Shared Cockpit" + +[AUTOPILOT_OFF] +CategoryId = Autopilot +SimEventName = "AUTOPILOT_OFF" +Name = "Turns AP off" +Description = "Turns AP off" +System = "Shared Cockpit" + +[AUTOPILOT_ON] +CategoryId = Autopilot +SimEventName = "AUTOPILOT_ON" +Name = "Turns AP on" +Description = "Turns AP on" +System = "Shared Cockpit" + +[YAW_DAMPER_TOGGLE] +CategoryId = Autopilot +SimEventName = "YAW_DAMPER_TOGGLE" +Name = "Toggles yaw damper on/off" +Description = "Toggles yaw damper on/off" +System = "Shared Cockpit" + +[AP_PANEL_HEADING_HOLD] +CategoryId = Autopilot +SimEventName = "AP_PANEL_HEADING_HOLD" +Name = "Toggles heading hold mode on/off" +Description = "Toggles heading hold mode on/off" +System = "Shared Cockpit" + +[AP_PANEL_ALTITUDE_HOLD] +CategoryId = Autopilot +SimEventName = "AP_PANEL_ALTITUDE_HOLD" +Name = "Toggles altitude hold mode on/off" +Description = "Toggles altitude hold mode on/off" +System = "Shared Cockpit" + +[AP_ATT_HOLD_ON] +CategoryId = Autopilot +SimEventName = "AP_ATT_HOLD_ON" +Name = "Turns on AP wing leveler and pitch hold mode" +Description = "Turns on AP wing leveler and pitch hold mode" +System = "Shared Cockpit" + +[AP_LOC_HOLD_ON] +CategoryId = Autopilot +SimEventName = "AP_LOC_HOLD_ON" +Name = "Turns AP localizer hold on/armed and glide-slope hold mode off" +Description = "Turns AP localizer hold on/armed and glide-slope hold mode off" +System = "Shared Cockpit" + +[AP_APR_HOLD_ON] +CategoryId = Autopilot +SimEventName = "AP_APR_HOLD_ON" +Name = "Turns both AP localizer and glide-slope modes on/armed" +Description = "Turns both AP localizer and glide-slope modes on/armed" +System = "Shared Cockpit" + +[AP_HDG_HOLD_ON] +CategoryId = Autopilot +SimEventName = "AP_HDG_HOLD_ON" +Name = "Turns heading hold mode on" +Description = "Turns heading hold mode on" +System = "Shared Cockpit" + +[AP_ALT_HOLD_ON] +CategoryId = Autopilot +SimEventName = "AP_ALT_HOLD_ON" +Name = "Turns altitude hold mode on" +Description = "Turns altitude hold mode on" +System = "Shared Cockpit" + +[AP_WING_LEVELER_ON] +CategoryId = Autopilot +SimEventName = "AP_WING_LEVELER_ON" +Name = "Turns wing leveler mode on" +Description = "Turns wing leveler mode on" +System = "Shared Cockpit" + +[AP_BC_HOLD_ON] +CategoryId = Autopilot +SimEventName = "AP_BC_HOLD_ON" +Name = "Turns localizer back course hold mode on/armed" +Description = "Turns localizer back course hold mode on/armed" +System = "Shared Cockpit" + +[AP_NAV1_HOLD_ON] +CategoryId = Autopilot +SimEventName = "AP_NAV1_HOLD_ON" +Name = "Turns lateral hold mode on" +Description = "Turns lateral hold mode on" +System = "Shared Cockpit" + +[AP_ATT_HOLD_OFF] +CategoryId = Autopilot +SimEventName = "AP_ATT_HOLD_OFF" +Name = "Turns off attitude hold mode" +Description = "Turns off attitude hold mode" +System = "Shared Cockpit" + +[AP_LOC_HOLD_OFF] +CategoryId = Autopilot +SimEventName = "AP_LOC_HOLD_OFF" +Name = "Turns off localizer hold mode" +Description = "Turns off localizer hold mode" +System = "Shared Cockpit" + +[AP_APR_HOLD_OFF] +CategoryId = Autopilot +SimEventName = "AP_APR_HOLD_OFF" +Name = "Turns off approach hold mode" +Description = "Turns off approach hold mode" +System = "Shared Cockpit" + +[AP_HDG_HOLD_OFF] +CategoryId = Autopilot +SimEventName = "AP_HDG_HOLD_OFF" +Name = "Turns off heading hold mode" +Description = "Turns off heading hold mode" +System = "Shared Cockpit" + +[AP_ALT_HOLD_OFF] +CategoryId = Autopilot +SimEventName = "AP_ALT_HOLD_OFF" +Name = "Turns off altitude hold mode" +Description = "Turns off altitude hold mode" +System = "Shared Cockpit" + +[AP_WING_LEVELER_OFF] +CategoryId = Autopilot +SimEventName = "AP_WING_LEVELER_OFF" +Name = "Turns off wing leveler mode" +Description = "Turns off wing leveler mode" +System = "Shared Cockpit" + +[AP_BC_HOLD_OFF] +CategoryId = Autopilot +SimEventName = "AP_BC_HOLD_OFF" +Name = "Turns off backcourse mode for localizer hold" +Description = "Turns off backcourse mode for localizer hold" +System = "Shared Cockpit" + +[AP_NAV1_HOLD_OFF] +CategoryId = Autopilot +SimEventName = "AP_NAV1_HOLD_OFF" +Name = "Turns off nav hold mode" +Description = "Turns off nav hold mode" +System = "Shared Cockpit" + +[AP_AIRSPEED_HOLD] +CategoryId = Autopilot +SimEventName = "AP_AIRSPEED_HOLD" +Name = "Toggles airspeed hold mode" +Description = "Toggles airspeed hold mode" +System = "Shared Cockpit" + +[AUTO_THROTTLE_ARM] +CategoryId = Autopilot +SimEventName = "AUTO_THROTTLE_ARM" +Name = "Toggles autothrottle arming mode" +Description = "Toggles autothrottle arming mode" +System = "Shared Cockpit" + +[AUTO_THROTTLE_TO_GA] +CategoryId = Autopilot +SimEventName = "AUTO_THROTTLE_TO_GA" +Name = "Toggles Takeoff/Go Around mode" +Description = "Toggles Takeoff/Go Around mode" +System = "Shared Cockpit" + +[HEADING_BUG_INC] +CategoryId = Autopilot +SimEventName = "HEADING_BUG_INC" +Name = "Increments heading hold reference bug" +Description = "Increments heading hold reference bug" +System = "Shared Cockpit" + +[HEADING_BUG_DEC] +CategoryId = Autopilot +SimEventName = "HEADING_BUG_DEC" +Name = "Decrements heading hold reference bug" +Description = "Decrements heading hold reference bug" +System = "Shared Cockpit" + +[HEADING_BUG_SET] +CategoryId = Autopilot +SimEventName = "HEADING_BUG_SET" +Name = "Set heading hold reference bug (degrees)" +Description = "Set heading hold reference bug (degrees)" +System = "Shared Cockpit" + +[AP_PANEL_SPEED_HOLD] +CategoryId = Autopilot +SimEventName = "AP_PANEL_SPEED_HOLD" +Name = "Toggles airspeed hold mode" +Description = "Toggles airspeed hold mode" +System = "Shared Cockpit" + +[AP_ALT_VAR_INC] +CategoryId = Autopilot +SimEventName = "AP_ALT_VAR_INC" +Name = "Increments reference altitude" +Description = "Increments reference altitude" +System = "Shared Cockpit" + +[AP_ALT_VAR_DEC] +CategoryId = Autopilot +SimEventName = "AP_ALT_VAR_DEC" +Name = "Decrements reference altitude" +Description = "Decrements reference altitude" +System = "Shared Cockpit" + +[AP_VS_VAR_INC] +CategoryId = Autopilot +SimEventName = "AP_VS_VAR_INC" +Name = "Increments vertical speed reference" +Description = "Increments vertical speed reference" +System = "Shared Cockpit" + +[AP_VS_VAR_DEC] +CategoryId = Autopilot +SimEventName = "AP_VS_VAR_DEC" +Name = "Decrements vertical speed reference" +Description = "Decrements vertical speed reference" +System = "Shared Cockpit" + +[AP_SPD_VAR_INC] +CategoryId = Autopilot +SimEventName = "AP_SPD_VAR_INC" +Name = "Increments airspeed hold reference" +Description = "Increments airspeed hold reference" +System = "Shared Cockpit" + +[AP_SPD_VAR_DEC] +CategoryId = Autopilot +SimEventName = "AP_SPD_VAR_DEC" +Name = "Decrements airspeed hold reference" +Description = "Decrements airspeed hold reference" +System = "Shared Cockpit" + +[AP_PANEL_MACH_HOLD] +CategoryId = Autopilot +SimEventName = "AP_PANEL_MACH_HOLD" +Name = "Toggles mach hold" +Description = "Toggles mach hold" +System = "Shared Cockpit" + +[AP_MACH_VAR_INC] +CategoryId = Autopilot +SimEventName = "AP_MACH_VAR_INC" +Name = "Increments reference mach" +Description = "Increments reference mach" +System = "Shared Cockpit" + +[AP_MACH_VAR_DEC] +CategoryId = Autopilot +SimEventName = "AP_MACH_VAR_DEC" +Name = "Decrements reference mach" +Description = "Decrements reference mach" +System = "Shared Cockpit" + +[AP_MACH_HOLD] +CategoryId = Autopilot +SimEventName = "AP_MACH_HOLD" +Name = "Toggles mach hold" +Description = "Toggles mach hold" +System = "Shared Cockpit" + +[AP_ALT_VAR_SET_METRIC] +CategoryId = Autopilot +SimEventName = "AP_ALT_VAR_SET_METRIC" +Name = "Sets reference altitude in meters" +Description = "Sets reference altitude in meters" +System = "Shared Cockpit" + +[AP_VS_VAR_SET_ENGLISH] +CategoryId = Autopilot +SimEventName = "AP_VS_VAR_SET_ENGLISH" +Name = "Sets reference vertical speed in feet per minute" +Description = "Sets reference vertical speed in feet per minute" +System = "Shared Cockpit" + +[AP_SPD_VAR_SET] +CategoryId = Autopilot +SimEventName = "AP_SPD_VAR_SET" +Name = "Sets airspeed reference in knots" +Description = "Sets airspeed reference in knots" +System = "Shared Cockpit" + +[AP_MACH_VAR_SET] +CategoryId = Autopilot +SimEventName = "AP_MACH_VAR_SET" +Name = "Sets mach reference" +Description = "Sets mach reference" +System = "Shared Cockpit" + +[YAW_DAMPER_ON] +CategoryId = Autopilot +SimEventName = "YAW_DAMPER_ON" +Name = "Turns yaw damper on" +Description = "Turns yaw damper on" +System = "Shared Cockpit" + +[YAW_DAMPER_OFF] +CategoryId = Autopilot +SimEventName = "YAW_DAMPER_OFF" +Name = "Turns yaw damper off" +Description = "Turns yaw damper off" +System = "Shared Cockpit" + +[YAW_DAMPER_SET] +CategoryId = Autopilot +SimEventName = "YAW_DAMPER_SET" +Name = "Sets yaw damper on/off (1,0)" +Description = "Sets yaw damper on/off (1,0)" +System = "Shared Cockpit" + +[AP_AIRSPEED_ON] +CategoryId = Autopilot +SimEventName = "AP_AIRSPEED_ON" +Name = "Turns airspeed hold on" +Description = "Turns airspeed hold on" +System = "Shared Cockpit" + +[AP_AIRSPEED_OFF] +CategoryId = Autopilot +SimEventName = "AP_AIRSPEED_OFF" +Name = "Turns airspeed hold off" +Description = "Turns airspeed hold off" +System = "Shared Cockpit" + +[AP_AIRSPEED_SET] +CategoryId = Autopilot +SimEventName = "AP_AIRSPEED_SET" +Name = "Sets airspeed hold on/off (1,0)" +Description = "Sets airspeed hold on/off (1,0)" +System = "Shared Cockpit" + +[AP_MACH_ON] +CategoryId = Autopilot +SimEventName = "AP_MACH_ON" +Name = "Turns mach hold on" +Description = "Turns mach hold on" +System = "Shared Cockpit" + +[AP_MACH_OFF] +CategoryId = Autopilot +SimEventName = "AP_MACH_OFF" +Name = "Turns mach hold off" +Description = "Turns mach hold off" +System = "Shared Cockpit" + +[AP_MACH_SET] +CategoryId = Autopilot +SimEventName = "AP_MACH_SET" +Name = "Sets mach hold on/off (1,0)" +Description = "Sets mach hold on/off (1,0)" +System = "Shared Cockpit" + +[AP_PANEL_ALTITUDE_ON] +CategoryId = Autopilot +SimEventName = "AP_PANEL_ALTITUDE_ON" +Name = "Turns altitude hold mode on (without capturing current altitude)" +Description = "Turns altitude hold mode on (without capturing current altitude)" +System = "Shared Cockpit" + +[AP_PANEL_ALTITUDE_OFF] +CategoryId = Autopilot +SimEventName = "AP_PANEL_ALTITUDE_OFF" +Name = "Turns altitude hold mode off" +Description = "Turns altitude hold mode off" +System = "Shared Cockpit" + +[AP_PANEL_ALTITUDE_SET] +CategoryId = Autopilot +SimEventName = "AP_PANEL_ALTITUDE_SET" +Name = "Sets altitude hold mode on/off (1,0)" +Description = "Sets altitude hold mode on/off (1,0)" +System = "Shared Cockpit" + +[AP_PANEL_HEADING_ON] +CategoryId = Autopilot +SimEventName = "AP_PANEL_HEADING_ON" +Name = "Turns heading mode on (without capturing current heading)" +Description = "Turns heading mode on (without capturing current heading)" +System = "Shared Cockpit" + +[AP_PANEL_HEADING_OFF] +CategoryId = Autopilot +SimEventName = "AP_PANEL_HEADING_OFF" +Name = "Turns heading mode off" +Description = "Turns heading mode off" +System = "Shared Cockpit" + +[AP_PANEL_HEADING_SET] +CategoryId = Autopilot +SimEventName = "AP_PANEL_HEADING_SET" +Name = "Set heading mode on/off (1,0)" +Description = "Set heading mode on/off (1,0)" +System = "Shared Cockpit" + +[AP_PANEL_MACH_ON] +CategoryId = Autopilot +SimEventName = "AP_PANEL_MACH_ON" +Name = "Turns on mach hold" +Description = "Turns on mach hold" +System = "Shared Cockpit" + +[AP_PANEL_MACH_OFF] +CategoryId = Autopilot +SimEventName = "AP_PANEL_MACH_OFF" +Name = "Turns off mach hold" +Description = "Turns off mach hold" +System = "Shared Cockpit" + +[AP_PANEL_MACH_SET] +CategoryId = Autopilot +SimEventName = "AP_PANEL_MACH_SET" +Name = "Sets mach hold on/off (1,0)" +Description = "Sets mach hold on/off (1,0)" +System = "Shared Cockpit" + +[AP_PANEL_SPEED_ON] +CategoryId = Autopilot +SimEventName = "AP_PANEL_SPEED_ON" +Name = "Turns on speed hold mode" +Description = "Turns on speed hold mode" +System = "Shared Cockpit" + +[AP_PANEL_SPEED_OFF] +CategoryId = Autopilot +SimEventName = "AP_PANEL_SPEED_OFF" +Name = "Turns off speed hold mode" +Description = "Turns off speed hold mode" +System = "Shared Cockpit" + +[AP_PANEL_SPEED_SET] +CategoryId = Autopilot +SimEventName = "AP_PANEL_SPEED_SET" +Name = "Set speed hold mode on/off (1,0)" +Description = "Set speed hold mode on/off (1,0)" +System = "Shared Cockpit" + +[AP_ALT_VAR_SET_ENGLISH] +CategoryId = Autopilot +SimEventName = "AP_ALT_VAR_SET_ENGLISH" +Name = "Sets altitude reference in feet" +Description = "Sets altitude reference in feet" +System = "Shared Cockpit" + +[AP_VS_VAR_SET_METRIC] +CategoryId = Autopilot +SimEventName = "AP_VS_VAR_SET_METRIC" +Name = "Sets vertical speed reference in meters per minute" +Description = "Sets vertical speed reference in meters per minute" +System = "Shared Cockpit" + +[TOGGLE_FLIGHT_DIRECTOR] +CategoryId = Autopilot +SimEventName = "TOGGLE_FLIGHT_DIRECTOR" +Name = "Toggles flight director on/off" +Description = "Toggles flight director on/off" +System = "Shared Cockpit" + +[SYNC_FLIGHT_DIRECTOR_PITCH] +CategoryId = Autopilot +SimEventName = "SYNC_FLIGHT_DIRECTOR_PITCH" +Name = "Synchronizes flight director pitch with current aircraft pitch" +Description = "Synchronizes flight director pitch with current aircraft pitch" +System = "Shared Cockpit" + +[INCREASE_AUTOBRAKE_CONTROL] +CategoryId = Autopilot +SimEventName = "INCREASE_AUTOBRAKE_CONTROL" +Name = "Increments autobrake level" +Description = "Increments autobrake level" +System = "Shared Cockpit" + +[DECREASE_AUTOBRAKE_CONTROL] +CategoryId = Autopilot +SimEventName = "DECREASE_AUTOBRAKE_CONTROL" +Name = "Decrements autobrake level" +Description = "Decrements autobrake level" +System = "Shared Cockpit" + +[AP_PANEL_SPEED_HOLD_TOGGLE] +CategoryId = Autopilot +SimEventName = "AP_PANEL_SPEED_HOLD_TOGGLE" +Name = "Turns airspeed hold mode on with current airspeed" +Description = "Turns airspeed hold mode on with current airspeed" +System = "Shared Cockpit" + +[AP_PANEL_MACH_HOLD_TOGGLE] +CategoryId = Autopilot +SimEventName = "AP_PANEL_MACH_HOLD_TOGGLE" +Name = "Sets mach hold reference to current mach" +Description = "Sets mach hold reference to current mach" +System = "Shared Cockpit" + +[AP_NAV_SELECT_SET] +CategoryId = Autopilot +SimEventName = "AP_NAV_SELECT_SET" +Name = "Sets the nav (1 or 2), which is used by the Nav hold modes" +Description = "Sets the nav (1 or 2), which is used by the Nav hold modes" +System = "Shared Cockpit" + +[HEADING_BUG_SELECT] +CategoryId = Autopilot +SimEventName = "HEADING_BUG_SELECT" +Name = "Selects the heading bug for use with +/-" +Description = "Selects the heading bug for use with +/-" +System = "Shared Cockpit" + +[ALTITUDE_BUG_SELECT] +CategoryId = Autopilot +SimEventName = "ALTITUDE_BUG_SELECT" +Name = "Selects the altitude reference for use with +/-" +Description = "Selects the altitude reference for use with +/-" +System = "Shared Cockpit" + +[VSI_BUG_SELECT] +CategoryId = Autopilot +SimEventName = "VSI_BUG_SELECT" +Name = "Selects the vertical speed reference for use with +/-" +Description = "Selects the vertical speed reference for use with +/-" +System = "Shared Cockpit" + +[AIRSPEED_BUG_SELECT] +CategoryId = Autopilot +SimEventName = "AIRSPEED_BUG_SELECT" +Name = "Selects the airspeed reference for use with +/-" +Description = "Selects the airspeed reference for use with +/-" +System = "Shared Cockpit" + +[AP_PITCH_REF_INC_UP] +CategoryId = Autopilot +SimEventName = "AP_PITCH_REF_INC_UP" +Name = "Increments the pitch reference for pitch hold mode" +Description = "Increments the pitch reference for pitch hold mode" +System = "Shared Cockpit" + +[AP_PITCH_REF_INC_DN] +CategoryId = Autopilot +SimEventName = "AP_PITCH_REF_INC_DN" +Name = "Decrements the pitch reference for pitch hold mode" +Description = "Decrements the pitch reference for pitch hold mode" +System = "Shared Cockpit" + +[AP_PITCH_REF_SELECT] +CategoryId = Autopilot +SimEventName = "AP_PITCH_REF_SELECT" +Name = "Selects pitch reference for use with +/-" +Description = "Selects pitch reference for use with +/-" +System = "Shared Cockpit" + +[AP_ATT_HOLD] +CategoryId = Autopilot +SimEventName = "AP_ATT_HOLD" +Name = "Toggle attitude hold mode" +Description = "Toggle attitude hold mode" +System = "Shared Cockpit" + +[AP_LOC_HOLD] +CategoryId = Autopilot +SimEventName = "AP_LOC_HOLD" +Name = "Toggles localizer (only), hold mode" +Description = "Toggles localizer (only), hold mode" +System = "Shared Cockpit" + +[AP_APR_HOLD] +CategoryId = Autopilot +SimEventName = "AP_APR_HOLD" +Name = "Toggles approach hold (localizer and glide-slope)" +Description = "Toggles approach hold (localizer and glide-slope)" +System = "Shared Cockpit" + +[AP_HDG_HOLD] +CategoryId = Autopilot +SimEventName = "AP_HDG_HOLD" +Name = "Toggles heading hold mode" +Description = "Toggles heading hold mode" +System = "Shared Cockpit" + +[AP_ALT_HOLD] +CategoryId = Autopilot +SimEventName = "AP_ALT_HOLD" +Name = "Toggles altitude hold mode" +Description = "Toggles altitude hold mode" +System = "Shared Cockpit" + +[AP_WING_LEVELER] +CategoryId = Autopilot +SimEventName = "AP_WING_LEVELER" +Name = "Toggles wing leveler mode" +Description = "Toggles wing leveler mode" +System = "Shared Cockpit" + +[AP_BC_HOLD] +CategoryId = Autopilot +SimEventName = "AP_BC_HOLD" +Name = "Toggles the backcourse mode for the localizer hold" +Description = "Toggles the backcourse mode for the localizer hold" +System = "Shared Cockpit" + +[AP_NAV1_HOLD] +CategoryId = Autopilot +SimEventName = "AP_NAV1_HOLD" +Name = "Toggles the nav hold mode" +Description = "Toggles the nav hold mode" +System = "Shared Cockpit" + +[AP_MAX_BANK_INC] +CategoryId = Autopilot +SimEventName = "AP_MAX_BANK_INC" +Name = "Autopilot max bank angle increment" +Description = "Autopilot max bank angle increment." +System = "Shared Cockpit" + +[AP_MAX_BANK_DEC] +CategoryId = Autopilot +SimEventName = "AP_MAX_BANK_DEC" +Name = "Autopilot max bank angle decrement" +Description = "Autopilot max bank angle decrement." +System = "Shared Cockpit" + +[AP_N1_HOLD] +CategoryId = Autopilot +SimEventName = "AP_N1_HOLD" +Name = "Autopilot, hold the N1 percentage at its current level" +Description = "Autopilot, hold the N1 percentage at its current level." +System = "Shared Cockpit" + +[AP_N1_REF_INC] +CategoryId = Autopilot +SimEventName = "AP_N1_REF_INC" +Name = "Increment the autopilot N1 reference" +Description = "Increment the autopilot N1 reference." +System = "Shared Cockpit" + +[AP_N1_REF_DEC] +CategoryId = Autopilot +SimEventName = "AP_N1_REF_DEC" +Name = "Decrement the autopilot N1 reference" +Description = "Decrement the autopilot N1 reference." +System = "Shared Cockpit" + +[AP_N1_REF_SET] +CategoryId = Autopilot +SimEventName = "AP_N1_REF_SET" +Name = "Sets the autopilot N1 reference" +Description = "Sets the autopilot N1 reference." +System = "Shared Cockpit" + +[FLY_BY_WIRE_ELAC_TOGGLE] +CategoryId = Autopilot +SimEventName = "FLY_BY_WIRE_ELAC_TOGGLE" +Name = "Turn on or off the fly by wire Elevators and Ailerons computer" +Description = "Turn on or off the fly by wire Elevators and Ailerons computer." +System = "Shared Cockpit" + +[FLY_BY_WIRE_FAC_TOGGLE] +CategoryId = Autopilot +SimEventName = "FLY_BY_WIRE_FAC_TOGGLE" +Name = "Turn on or off the fly by wire Flight Augmentation computer" +Description = "Turn on or off the fly by wire Flight Augmentation computer." +System = "Shared Cockpit" + +[FLY_BY_WIRE_SEC_TOGGLE] +CategoryId = Autopilot +SimEventName = "FLY_BY_WIRE_SEC_TOGGLE" +Name = "Turn on or off the fly by wire Spoilers and Elevators computer" +Description = "Turn on or off the fly by wire Spoilers and Elevators computer." +System = "Shared Cockpit" + +[AP_VS_HOLD] +CategoryId = Autopilot +SimEventName = "AP_VS_HOLD" +Name = "Toggle VS hold mode" +Description = "Toggle VS hold mode" +System = "Shared Cockpit" + +[FLIGHT_LEVEL_CHANGE] +CategoryId = Autopilot +SimEventName = "FLIGHT_LEVEL_CHANGE" +Name = "Toggle FLC mode" +Description = "Toggle FLC mode" +System = "Shared Cockpit" + + + +# Fuel_System ####################### +# +[category_Fuel_System] +Name = "Fuel System" + + +[FUEL_SELECTOR_OFF] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_OFF" +Name = "Turns selector 1 to OFF position" +Description = "Turns selector 1 to OFF position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_ALL] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_ALL" +Name = "Turns selector 1 to ALL position" +Description = "Turns selector 1 to ALL position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_LEFT] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_LEFT" +Name = "Turns selector 1 to LEFT position" +Description = "Turns selector 1 to LEFT position (burns from tip then aux then main)" +System = "Shared Cockpit" + +[FUEL_SELECTOR_RIGHT] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_RIGHT" +Name = "Turns selector 1 to RIGHT position" +Description = "Turns selector 1 to RIGHT position (burns from tip then aux then main)" +System = "Shared Cockpit" + +[FUEL_SELECTOR_LEFT_AUX] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_LEFT_AUX" +Name = "Turns selector 1 to LEFT AUX position" +Description = "Turns selector 1 to LEFT AUX position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_RIGHT_AUX] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_RIGHT_AUX" +Name = "Turns selector 1 to RIGHT AUX position" +Description = "Turns selector 1 to RIGHT AUX position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_CENTER] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_CENTER" +Name = "Turns selector 1 to CENTER position" +Description = "Turns selector 1 to CENTER position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_SET] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_SET" +Name = "Sets selector 1 position (see code list)" +Description = "Sets selector 1 position (see code list below),\n FUEL_TANK_SELECTOR_OFF = 0\n FUEL_TANK_SELECTOR_ALL = 1\n FUEL_TANK_SELECTOR_LEFT = 2\n FUEL_TANK_SELECTOR_RIGHT = 3\n FUEL_TANK_SELECTOR_LEFT_AUX = 4\n FUEL_TANK_SELECTOR_RIGHT_AUX = 5\n FUEL_TANK_SELECTOR_CENTER = 6\n FUEL_TANK_SELECTOR_CENTER2 = 7\n FUEL_TANK_SELECTOR_CENTER3 = 8\n FUEL_TANK_SELECTOR_EXTERNAL1 = 9\n FUEL_TANK_SELECTOR_EXTERNAL2 = 10\n FUEL_TANK_SELECTOR_RIGHT_TIP = 11\n FUEL_TANK_SELECTOR_LEFT_TIP = 12\n FUEL_TANK_SELECTOR_CROSSFEED = 13\n FUEL_TANK_SELECTOR_CROSSFEED_L2R = 14\n FUEL_TANK_SELECTOR_CROSSFEED_R2L = 15\n FUEL_TANK_SELECTOR_BOTH = 16\n FUEL_TANK_SELECTOR_EXTERNAL_ALL = 17\n FUEL_TANK_SELECTOR_ISOLATE = 18" +System = "Shared Cockpit" + +[FUEL_SELECTOR_2_OFF] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_2_OFF" +Name = "Turns selector 2 to OFF position" +Description = "Turns selector 2 to OFF position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_2_ALL] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_2_ALL" +Name = "Turns selector 2 to ALL position" +Description = "Turns selector 2 to ALL position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_2_LEFT] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_2_LEFT" +Name = "Turns selector 2 to LEFT position" +Description = "Turns selector 2 to LEFT position (burns from tip then aux then main)" +System = "Shared Cockpit" + +[FUEL_SELECTOR_2_RIGHT] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_2_RIGHT" +Name = "Turns selector 2 to RIGHT position" +Description = "Turns selector 2 to RIGHT position (burns from tip then aux then main)" +System = "Shared Cockpit" + +[FUEL_SELECTOR_2_LEFT_AUX] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_2_LEFT_AUX" +Name = "Turns selector 2 to LEFT AUX position" +Description = "Turns selector 2 to LEFT AUX position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_2_RIGHT_AUX] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_2_RIGHT_AUX" +Name = "Turns selector 2 to RIGHT AUX position" +Description = "Turns selector 2 to RIGHT AUX position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_2_CENTER] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_2_CENTER" +Name = "Turns selector 2 to CENTER position" +Description = "Turns selector 2 to CENTER position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_2_SET] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_2_SET" +Name = "Sets selector 2 position (see code list below)" +Description = "Sets selector 2 position (see code list)" +System = "Shared Cockpit" + +[FUEL_SELECTOR_3_OFF] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_3_OFF" +Name = "Turns selector 3 to OFF position" +Description = "Turns selector 3 to OFF position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_3_ALL] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_3_ALL" +Name = "Turns selector 3 to ALL position" +Description = "Turns selector 3 to ALL position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_3_LEFT] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_3_LEFT" +Name = "Turns selector 3 to LEFT position" +Description = "Turns selector 3 to LEFT position (burns from tip then aux then main)" +System = "Shared Cockpit" + +[FUEL_SELECTOR_3_RIGHT] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_3_RIGHT" +Name = "Turns selector 3 to RIGHT position" +Description = "Turns selector 3 to RIGHT position (burns from tip then aux then main)" +System = "Shared Cockpit" + +[FUEL_SELECTOR_3_LEFT_AUX] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_3_LEFT_AUX" +Name = "Turns selector 3 to LEFT AUX position" +Description = "Turns selector 3 to LEFT AUX position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_3_RIGHT_AUX] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_3_RIGHT_AUX" +Name = "Turns selector 3 to RIGHT AUX position" +Description = "Turns selector 3 to RIGHT AUX position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_3_CENTER] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_3_CENTER" +Name = "Turns selector 3 to CENTER position" +Description = "Turns selector 3 to CENTER position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_3_SET] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_3_SET" +Name = "Sets selector 3 position (see code list below)" +Description = "Sets selector 3 position (see code list below)" +System = "Shared Cockpit" + +[FUEL_SELECTOR_4_OFF] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_4_OFF" +Name = "Turns selector 4 to OFF position" +Description = "Turns selector 4 to OFF position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_4_ALL] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_4_ALL" +Name = "Turns selector 4 to ALL position" +Description = "Turns selector 4 to ALL position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_4_LEFT] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_4_LEFT" +Name = "Turns selector 4 to LEFT position" +Description = "Turns selector 4 to LEFT position (burns from tip then aux then main)" +System = "Shared Cockpit" + +[FUEL_SELECTOR_4_RIGHT] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_4_RIGHT" +Name = "Turns selector 4 to RIGHT position" +Description = "Turns selector 4 to RIGHT position (burns from tip then aux then main)" +System = "Shared Cockpit" + +[FUEL_SELECTOR_4_LEFT_AUX] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_4_LEFT_AUX" +Name = "Turns selector 4 to LEFT AUX position" +Description = "Turns selector 4 to LEFT AUX position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_4_RIGHT_AUX] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_4_RIGHT_AUX" +Name = "Turns selector 4 to RIGHT AUX position" +Description = "Turns selector 4 to RIGHT AUX position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_4_CENTER] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_4_CENTER" +Name = "Turns selector 4 to CENTER position" +Description = "Turns selector 4 to CENTER position" +System = "Shared Cockpit" + +[FUEL_SELECTOR_4_SET] +CategoryId = Fuel_System +SimEventName = "FUEL_SELECTOR_4_SET" +Name = "Sets selector 4 position (see code list below)" +Description = "Sets selector 4 position (see code list below)" +System = "Shared Cockpit" + +[CROSS_FEED_OPEN] +CategoryId = Fuel_System +SimEventName = "CROSS_FEED_OPEN" +Name = "Opens cross feed valve" +Description = "Opens cross feed valve (when used in conjunction with "isolate" tank)" +System = "Shared Cockpit" + +[CROSS_FEED_TOGGLE] +CategoryId = Fuel_System +SimEventName = "CROSS_FEED_TOGGLE" +Name = "Toggles crossfeed valve" +Description = "Toggles crossfeed valve (when used in conjunction with "isolate" tank)" +System = "Shared Cockpit" + +[CROSS_FEED_OFF] +CategoryId = Fuel_System +SimEventName = "CROSS_FEED_OFF" +Name = "Closes crossfeed valve" +Description = "Closes crossfeed valve (when used in conjunction with "isolate" tank)" +System = "Shared Cockpit" + +[FUEL_DUMP_SWITCH_SET] +CategoryId = Fuel_System +SimEventName = "FUEL_DUMP_SWITCH_SET" +Name = "Fuel Dump Switch Set to 0/1" +Description = "Set to True or False. The switch can only be set to True if fuel_dump_rate is specified in the aircraft configuration file, which indicates that a fuel dump system exists." +System = "Shared Cockpit" + +[ANTIDETONATION_TANK_VALVE_TOGGLE] +CategoryId = Fuel_System +SimEventName = "ANTIDETONATION_TANK_VALVE_TOGGLE" +Name = "Toggle the antidetonation valve" +Description = "Toggle the antidetonation valve. Pass a value to determine which tank, if there are multiple tanks, to use. Tanks are indexed from 1. Refer to the document Notes on Aircraft Systems." +System = "Shared Cockpit" + +[NITROUS_TANK_VALVE_TOGGLE] +CategoryId = Fuel_System +SimEventName = "NITROUS_TANK_VALVE_TOGGLE" +Name = "Toggle the nitrous valve" +Description = "Toggle the nitrous valve. Pass a value to determine which tank, if there are multiple tanks, to use. Tanks are indexed from 1." +System = "Shared Cockpit" + +[REPAIR_AND_REFUEL] +CategoryId = Fuel_System +SimEventName = "REPAIR_AND_REFUEL" +Name = "Fully repair and refuel the user aircraft" +Description = "Fully repair and refuel the user aircraft. Ignored if flight realism is enforced." +System = "Shared Cockpit" + +[FUEL_DUMP_TOGGLE] +CategoryId = Fuel_System +SimEventName = "FUEL_DUMP_TOGGLE" +Name = "Turns on or off the fuel dump switch" +Description = "Turns on or off the fuel dump switch." +System = "Shared Cockpit" + +[REQUEST_FUEL_KEY] +CategoryId = Fuel_System +SimEventName = "REQUEST_FUEL_KEY" +Name = "Request a fuel truck" +Description = "Request a fuel truck. The aircraft must be in a parking spot for this to be successful." +System = "Shared Cockpit" + + + +# Fuel_Selection_Keys ####################### +# +[category_Fuel_Selection_Keys] +Name = "Fuel Selection Keys" + + +[FUEL_SELECTOR_LEFT_MAIN] +CategoryId = Fuel_Selection_Keys +SimEventName = "FUEL_SELECTOR_LEFT_MAIN" +Name = "Sets the fuel selector" +Description = "Sets the fuel selector. Fuel will be taken in the order left tip, left aux, then main fuel tanks." +System = "Shared Cockpit" + +[FUEL_SELECTOR_2_LEFT_MAIN] +CategoryId = Fuel_Selection_Keys +SimEventName = "FUEL_SELECTOR_2_LEFT_MAIN" +Name = "Sets the fuel selector for engine 2" +Description = "Sets the fuel selector for engine 2." +System = "Shared Cockpit" + +[FUEL_SELECTOR_3_LEFT_MAIN] +CategoryId = Fuel_Selection_Keys +SimEventName = "FUEL_SELECTOR_3_LEFT_MAIN" +Name = "Sets the fuel selector for engine 3" +Description = "Sets the fuel selector for engine 3." +System = "Shared Cockpit" + +[FUEL_SELECTOR_4_LEFT_MAIN] +CategoryId = Fuel_Selection_Keys +SimEventName = "FUEL_SELECTOR_4_LEFT_MAIN" +Name = "Sets the fuel selector for engine 4" +Description = "Sets the fuel selector for engine 4." +System = "Shared Cockpit" + +[FUEL_SELECTOR_RIGHT_MAIN] +CategoryId = Fuel_Selection_Keys +SimEventName = "FUEL_SELECTOR_RIGHT_MAIN" +Name = "Sets the fuel selector" +Description = "Sets the fuel selector. Fuel will be taken in the order right tip, right aux, then main fuel tanks." +System = "Shared Cockpit" + +[FUEL_SELECTOR_2_RIGHT_MAIN] +CategoryId = Fuel_Selection_Keys +SimEventName = "FUEL_SELECTOR_2_RIGHT_MAIN" +Name = "Sets the fuel selector for engine 2" +Description = "Sets the fuel selector for engine 2." +System = "Shared Cockpit" + +[FUEL_SELECTOR_3_RIGHT_MAIN] +CategoryId = Fuel_Selection_Keys +SimEventName = "FUEL_SELECTOR_3_RIGHT_MAIN" +Name = "Sets the fuel selector for engine 3" +Description = "Sets the fuel selector for engine 3." +System = "Shared Cockpit" + +[FUEL_SELECTOR_4_RIGHT_MAIN] +CategoryId = Fuel_Selection_Keys +SimEventName = "FUEL_SELECTOR_4_RIGHT_MAIN" +Name = "Sets the fuel selector for engine 4" +Description = "Sets the fuel selector for engine 4." +System = "Shared Cockpit" + + + +# Avionics ####################### +# +[category_Avionics] +Name = "Avionics" + + +[XPNDR] +CategoryId = Avionics +SimEventName = "XPNDR" +Name = "Sequentially selects the transponder digits for use with +/-" +Description = "Sequentially selects the transponder digits for use with +/-." +System = "Shared Cockpit" + +[ADF] +CategoryId = Avionics +SimEventName = "ADF" +Name = "Sequentially selects the ADF tuner digits for use with +/-" +Description = "Sequentially selects the ADF tuner digits for use with +/-. Follow by KEY_SELECT_2 for ADF 2." +System = "Shared Cockpit" + +[DME] +CategoryId = Avionics +SimEventName = "DME" +Name = "Selects the DME for use with +/-" +Description = "Selects the DME for use with +/-" +System = "Shared Cockpit" + +[COM_RADIO] +CategoryId = Avionics +SimEventName = "COM_RADIO" +Name = "Sequentially selects the COM tuner digits for use with +/-" +Description = "Sequentially selects the COM tuner digits for use with +/-. Follow by KEY_SELECT_2 for COM 2." +System = "All aircraft" + +[VOR_OBS] +CategoryId = Avionics +SimEventName = "VOR_OBS" +Name = "Sequentially selects the VOR OBS for use with +/-" +Description = "Sequentially selects the VOR OBS for use with +/-. Follow by KEY_SELECT_2 for VOR 2." +System = "Shared Cockpit" + +[NAV_RADIO] +CategoryId = Avionics +SimEventName = "NAV_RADIO" +Name = "Sequentially selects the NAV tuner digits for use with +/-" +Description = "Sequentially selects the NAV tuner digits for use with +/-. Follow by KEY_SELECT_2 for NAV 2." +System = "Shared Cockpit" + +[COM_RADIO_WHOLE_DEC] +CategoryId = Avionics +SimEventName = "COM_RADIO_WHOLE_DEC" +Name = "Decrements COM by one MHz" +Description = "Decrements COM by one MHz" +System = "All aircraft" + +[COM_RADIO_WHOLE_INC] +CategoryId = Avionics +SimEventName = "COM_RADIO_WHOLE_INC" +Name = "Increments COM by one MHz" +Description = "Increments COM by one MHz" +System = "All aircraft" + +[COM_RADIO_FRACT_DEC] +CategoryId = Avionics +SimEventName = "COM_RADIO_FRACT_DEC" +Name = "Decrements COM by 25 KHz" +Description = "Decrements COM by 25 KHz" +System = "All aircraft" + +[COM_RADIO_FRACT_INC] +CategoryId = Avionics +SimEventName = "COM_RADIO_FRACT_INC" +Name = "Increments COM by 25 KHz" +Description = "Increments COM by 25 KHz" +System = "All aircraft" + +[NAV1_RADIO_WHOLE_DEC] +CategoryId = Avionics +SimEventName = "NAV1_RADIO_WHOLE_DEC" +Name = "Decrements Nav 1 by one MHz" +Description = "Decrements Nav 1 by one MHz" +System = "Shared Cockpit" + +[NAV1_RADIO_WHOLE_INC] +CategoryId = Avionics +SimEventName = "NAV1_RADIO_WHOLE_INC" +Name = "Increments Nav 1 by one MHz" +Description = "Increments Nav 1 by one MHz" +System = "Shared Cockpit" + +[NAV1_RADIO_FRACT_DEC] +CategoryId = Avionics +SimEventName = "NAV1_RADIO_FRACT_DEC" +Name = "Decrements Nav 1 by 25 KHz" +Description = "Decrements Nav 1 by 25 KHz" +System = "Shared Cockpit" + +[NAV1_RADIO_FRACT_INC] +CategoryId = Avionics +SimEventName = "NAV1_RADIO_FRACT_INC" +Name = "Increments Nav 1 by 25 KHz" +Description = "Increments Nav 1 by 25 KHz" +System = "Shared Cockpit" + +[NAV2_RADIO_WHOLE_DEC] +CategoryId = Avionics +SimEventName = "NAV2_RADIO_WHOLE_DEC" +Name = "Decrements Nav 2 by one MHz" +Description = "Decrements Nav 2 by one MHz" +System = "Shared Cockpit" + +[NAV2_RADIO_WHOLE_INC] +CategoryId = Avionics +SimEventName = "NAV2_RADIO_WHOLE_INC" +Name = "Increments Nav 2 by one MHz" +Description = "Increments Nav 2 by one MHz" +System = "Shared Cockpit" + +[NAV2_RADIO_FRACT_DEC] +CategoryId = Avionics +SimEventName = "NAV2_RADIO_FRACT_DEC" +Name = "Decrements Nav 2 by 25 KHz" +Description = "Decrements Nav 2 by 25 KHz" +System = "Shared Cockpit" + +[NAV2_RADIO_FRACT_INC] +CategoryId = Avionics +SimEventName = "NAV2_RADIO_FRACT_INC" +Name = "Increments Nav 2 by 25 KHz" +Description = "Increments Nav 2 by 25 KHz" +System = "Shared Cockpit" + +[ADF_100_INC] +CategoryId = Avionics +SimEventName = "ADF_100_INC" +Name = "Increments ADF by 100 KHz" +Description = "Increments ADF by 100 KHz" +System = "Shared Cockpit" + +[ADF_10_INC] +CategoryId = Avionics +SimEventName = "ADF_10_INC" +Name = "Increments ADF by 10 KHz" +Description = "Increments ADF by 10 KHz" +System = "Shared Cockpit" + +[ADF_1_INC] +CategoryId = Avionics +SimEventName = "ADF_1_INC" +Name = "Increments ADF by 1 KHz" +Description = "Increments ADF by 1 KHz" +System = "Shared Cockpit" + +[XPNDR_1000_INC] +CategoryId = Avionics +SimEventName = "XPNDR_1000_INC" +Name = "Increments first digit of transponder" +Description = "Increments first digit of transponder" +System = "Shared Cockpit" + +[XPNDR_100_INC] +CategoryId = Avionics +SimEventName = "XPNDR_100_INC" +Name = "Increments second digit of transponder" +Description = "Increments second digit of transponder" +System = "Shared Cockpit" + +[XPNDR_10_INC] +CategoryId = Avionics +SimEventName = "XPNDR_10_INC" +Name = "Increments third digit of transponder" +Description = "Increments third digit of transponder" +System = "Shared Cockpit" + +[XPNDR_1_INC] +CategoryId = Avionics +SimEventName = "XPNDR_1_INC" +Name = "Increments fourth digit of transponder" +Description = "Increments fourth digit of transponder" +System = "Shared Cockpit" + +[VOR1_OBI_DEC] +CategoryId = Avionics +SimEventName = "VOR1_OBI_DEC" +Name = "Decrements the VOR 1 OBS setting" +Description = "Decrements the VOR 1 OBS setting" +System = "Shared Cockpit" + +[VOR1_OBI_INC] +CategoryId = Avionics +SimEventName = "VOR1_OBI_INC" +Name = "Increments the VOR 1 OBS setting" +Description = "Increments the VOR 1 OBS setting" +System = "Shared Cockpit" + +[VOR2_OBI_DEC] +CategoryId = Avionics +SimEventName = "VOR2_OBI_DEC" +Name = "Decrements the VOR 2 OBS setting" +Description = "Decrements the VOR 2 OBS setting" +System = "Shared Cockpit" + +[VOR2_OBI_INC] +CategoryId = Avionics +SimEventName = "VOR2_OBI_INC" +Name = "Increments the VOR 2 OBS setting" +Description = "Increments the VOR 2 OBS setting" +System = "Shared Cockpit" + +[ADF_100_DEC] +CategoryId = Avionics +SimEventName = "ADF_100_DEC" +Name = "Decrements ADF by 100 KHz" +Description = "Decrements ADF by 100 KHz" +System = "Shared Cockpit" + +[ADF_10_DEC] +CategoryId = Avionics +SimEventName = "ADF_10_DEC" +Name = "Decrements ADF by 10 KHz" +Description = "Decrements ADF by 10 KHz" +System = "Shared Cockpit" + +[ADF_1_DEC] +CategoryId = Avionics +SimEventName = "ADF_1_DEC" +Name = "Decrements ADF by 1 KHz" +Description = "Decrements ADF by 1 KHz" +System = "Shared Cockpit" + +[COM_RADIO_SET] +CategoryId = Avionics +SimEventName = "COM_RADIO_SET" +Name = "Sets COM frequency (BCD Hz)" +Description = "Sets COM frequency (BCD Hz)" +System = "All aircraft" + +[NAV1_RADIO_SET] +CategoryId = Avionics +SimEventName = "NAV1_RADIO_SET" +Name = "Sets NAV 1 frequency (BCD Hz)" +Description = "Sets NAV 1 frequency (BCD Hz)" +System = "Shared Cockpit" + +[NAV2_RADIO_SET] +CategoryId = Avionics +SimEventName = "NAV2_RADIO_SET" +Name = "Sets NAV 2 frequency (BCD Hz)" +Description = "Sets NAV 2 frequency (BCD Hz)" +System = "Shared Cockpit" + +[ADF_SET] +CategoryId = Avionics +SimEventName = "ADF_SET" +Name = "Sets ADF frequency (BCD Hz)" +Description = "Sets ADF frequency (BCD Hz)" +System = "Shared Cockpit" + +[XPNDR_SET] +CategoryId = Avionics +SimEventName = "XPNDR_SET" +Name = "Sets transponder code (BCD)" +Description = "Sets transponder code (BCD)" +System = "All aircraft" + +[VOR1_SET] +CategoryId = Avionics +SimEventName = "VOR1_SET" +Name = "Sets OBS 1 (0 to 360)" +Description = "Sets OBS 1 (0 to 360)" +System = "Shared Cockpit" + +[VOR2_SET] +CategoryId = Avionics +SimEventName = "VOR2_SET" +Name = "Sets OBS 2 (0 to 360)" +Description = "Sets OBS 2 (0 to 360)" +System = "Shared Cockpit" + +[DME1_TOGGLE] +CategoryId = Avionics +SimEventName = "DME1_TOGGLE" +Name = "Sets DME display to Nav 1" +Description = "Sets DME display to Nav 1" +System = "Shared Cockpit" + +[DME2_TOGGLE] +CategoryId = Avionics +SimEventName = "DME2_TOGGLE" +Name = "Sets DME display to Nav 2" +Description = "Sets DME display to Nav 2" +System = "Shared Cockpit" + +[RADIO_VOR1_IDENT_DISABLE] +CategoryId = Avionics +SimEventName = "RADIO_VOR1_IDENT_DISABLE" +Name = "Turns NAV 1 ID off" +Description = "Turns NAV 1 ID off" +System = "Shared Cockpit" + +[RADIO_VOR2_IDENT_DISABLE] +CategoryId = Avionics +SimEventName = "RADIO_VOR2_IDENT_DISABLE" +Name = "Turns NAV 2 ID off" +Description = "Turns NAV 2 ID off" +System = "Shared Cockpit" + +[RADIO_DME1_IDENT_DISABLE] +CategoryId = Avionics +SimEventName = "RADIO_DME1_IDENT_DISABLE" +Name = "Turns DME 1 ID off" +Description = "Turns DME 1 ID off" +System = "Shared Cockpit" + +[RADIO_DME2_IDENT_DISABLE] +CategoryId = Avionics +SimEventName = "RADIO_DME2_IDENT_DISABLE" +Name = "Turns DME 2 ID off" +Description = "Turns DME 2 ID off" +System = "Shared Cockpit" + +[RADIO_ADF_IDENT_DISABLE] +CategoryId = Avionics +SimEventName = "RADIO_ADF_IDENT_DISABLE" +Name = "Turns ADF 1 ID off" +Description = "Turns ADF 1 ID off" +System = "Shared Cockpit" + +[RADIO_VOR1_IDENT_ENABLE] +CategoryId = Avionics +SimEventName = "RADIO_VOR1_IDENT_ENABLE" +Name = "Turns NAV 1 ID on" +Description = "Turns NAV 1 ID on" +System = "Shared Cockpit" + +[RADIO_VOR2_IDENT_ENABLE] +CategoryId = Avionics +SimEventName = "RADIO_VOR2_IDENT_ENABLE" +Name = "Turns NAV 2 ID on" +Description = "Turns NAV 2 ID on" +System = "Shared Cockpit" + +[RADIO_DME1_IDENT_ENABLE] +CategoryId = Avionics +SimEventName = "RADIO_DME1_IDENT_ENABLE" +Name = "Turns DME 1 ID on" +Description = "Turns DME 1 ID on" +System = "Shared Cockpit" + +[RADIO_DME2_IDENT_ENABLE] +CategoryId = Avionics +SimEventName = "RADIO_DME2_IDENT_ENABLE" +Name = "Turns DME 2 ID on" +Description = "Turns DME 2 ID on" +System = "Shared Cockpit" + +[RADIO_ADF_IDENT_ENABLE] +CategoryId = Avionics +SimEventName = "RADIO_ADF_IDENT_ENABLE" +Name = "Turns ADF 1 ID on" +Description = "Turns ADF 1 ID on" +System = "Shared Cockpit" + +[RADIO_VOR1_IDENT_TOGGLE] +CategoryId = Avionics +SimEventName = "RADIO_VOR1_IDENT_TOGGLE" +Name = "Toggles NAV 1 ID" +Description = "Toggles NAV 1 ID" +System = "Shared Cockpit" + +[RADIO_VOR2_IDENT_TOGGLE] +CategoryId = Avionics +SimEventName = "RADIO_VOR2_IDENT_TOGGLE" +Name = "Toggles NAV 2 ID" +Description = "Toggles NAV 2 ID" +System = "Shared Cockpit" + +[RADIO_DME1_IDENT_TOGGLE] +CategoryId = Avionics +SimEventName = "RADIO_DME1_IDENT_TOGGLE" +Name = "Toggles DME 1 ID" +Description = "Toggles DME 1 ID" +System = "Shared Cockpit" + +[RADIO_DME2_IDENT_TOGGLE] +CategoryId = Avionics +SimEventName = "RADIO_DME2_IDENT_TOGGLE" +Name = "Toggles DME 2 ID" +Description = "Toggles DME 2 ID" +System = "Shared Cockpit" + +[RADIO_ADF_IDENT_TOGGLE] +CategoryId = Avionics +SimEventName = "RADIO_ADF_IDENT_TOGGLE" +Name = "Toggles ADF 1 ID" +Description = "Toggles ADF 1 ID" +System = "Shared Cockpit" + +[RADIO_VOR1_IDENT_SET] +CategoryId = Avionics +SimEventName = "RADIO_VOR1_IDENT_SET" +Name = "Sets NAV 1 ID (on/off)" +Description = "Sets NAV 1 ID (on/off)" +System = "Shared Cockpit" + +[RADIO_VOR2_IDENT_SET] +CategoryId = Avionics +SimEventName = "RADIO_VOR2_IDENT_SET" +Name = "Sets NAV 2 ID (on/off)" +Description = "Sets NAV 2 ID (on/off)" +System = "Shared Cockpit" + +[RADIO_DME1_IDENT_SET] +CategoryId = Avionics +SimEventName = "RADIO_DME1_IDENT_SET" +Name = "Sets DME 1 ID (on/off)" +Description = "Sets DME 1 ID (on/off)" +System = "Shared Cockpit" + +[RADIO_DME2_IDENT_SET] +CategoryId = Avionics +SimEventName = "RADIO_DME2_IDENT_SET" +Name = "Sets DME 2 ID (on/off)" +Description = "Sets DME 2 ID (on/off)" +System = "Shared Cockpit" + +[RADIO_ADF_IDENT_SET] +CategoryId = Avionics +SimEventName = "RADIO_ADF_IDENT_SET" +Name = "Sets ADF 1 ID (on/off)" +Description = "Sets ADF 1 ID (on/off)" +System = "Shared Cockpit" + +[ADF_CARD_INC] +CategoryId = Avionics +SimEventName = "ADF_CARD_INC" +Name = "Increments ADF card" +Description = "Increments ADF card" +System = "Shared Cockpit" + +[ADF_CARD_DEC] +CategoryId = Avionics +SimEventName = "ADF_CARD_DEC" +Name = "Decrements ADF card" +Description = "Decrements ADF card" +System = "Shared Cockpit" + +[ADF_CARD_SET] +CategoryId = Avionics +SimEventName = "ADF_CARD_SET" +Name = "Sets ADF card (0-360)" +Description = "Sets ADF card (0-360)" +System = "Shared Cockpit" + +[TOGGLE_DME] +CategoryId = Avionics +SimEventName = "TOGGLE_DME" +Name = "Toggles between NAV 1 and NAV 2" +Description = "Toggles between NAV 1 and NAV 2" +System = "Shared Cockpit" + +[AVIONICS_MASTER_SET] +CategoryId = Avionics +SimEventName = "AVIONICS_MASTER_SET" +Name = "Sets the avionics master switch" +Description = "Sets the avionics master switch" +System = "All aircraft" + +[TOGGLE_AVIONICS_MASTER] +CategoryId = Avionics +SimEventName = "TOGGLE_AVIONICS_MASTER" +Name = "Toggles the avionics master switch" +Description = "Toggles the avionics master switch" +System = "All aircraft" + +[COM_STBY_RADIO_SET] +CategoryId = Avionics +SimEventName = "COM_STBY_RADIO_SET" +Name = "Sets COM 1 standby frequency (BCD Hz)" +Description = "Sets COM 1 standby frequency (BCD Hz)" +System = "All aircraft" + +[COM_STBY_RADIO_SWAP] +CategoryId = Avionics +SimEventName = "COM_STBY_RADIO_SWAP" +Name = "Swaps COM 1 frequency with standby" +Description = "Swaps COM 1 frequency with standby" +System = "All aircraft" + +[COM_RADIO_FRACT_DEC_CARRY] +CategoryId = Avionics +SimEventName = "COM_RADIO_FRACT_DEC_CARRY" +Name = "Decrement COM 1 frequency by 25 KHz, and carry when digit wraps" +Description = "Decrement COM 1 frequency by 25 KHz, and carry when digit wraps" +System = "All aircraft" + +[COM_RADIO_FRACT_INC_CARRY] +CategoryId = Avionics +SimEventName = "COM_RADIO_FRACT_INC_CARRY" +Name = "Increment COM 1 frequency by 25 KHz, and carry when digit wraps" +Description = "Increment COM 1 frequency by 25 KHz, and carry when digit wraps" +System = "All aircraft" + +[COM2_RADIO_WHOLE_DEC] +CategoryId = Avionics +SimEventName = "COM2_RADIO_WHOLE_DEC" +Name = "Decrement COM 2 frequency by 1 MHz, with no carry when digit wraps" +Description = "Decrement COM 2 frequency by 1 MHz, with no carry when digit wraps" +System = "All aircraft" + +[COM2_RADIO_WHOLE_INC] +CategoryId = Avionics +SimEventName = "COM2_RADIO_WHOLE_INC" +Name = "Increment COM 2 frequency by 1 MHz, with no carry when digit wraps" +Description = "Increment COM 2 frequency by 1 MHz, with no carry when digit wraps" +System = "All aircraft" + +[COM2_RADIO_FRACT_DEC] +CategoryId = Avionics +SimEventName = "COM2_RADIO_FRACT_DEC" +Name = "Decrement COM 2 frequency by 25 KHz, with no carry when digit wraps" +Description = "Decrement COM 2 frequency by 25 KHz, with no carry when digit wraps" +System = "All aircraft" + +[COM2_RADIO_FRACT_DEC_CARRY] +CategoryId = Avionics +SimEventName = "COM2_RADIO_FRACT_DEC_CARRY" +Name = "Decrement COM 2 frequency by 25 KHz, and carry when digit wraps" +Description = "Decrement COM 2 frequency by 25 KHz, and carry when digit wraps" +System = "All aircraft" + +[COM2_RADIO_FRACT_INC] +CategoryId = Avionics +SimEventName = "COM2_RADIO_FRACT_INC" +Name = "Increment COM 2 frequency by 25 KHz, with no carry when digit wraps" +Description = "Increment COM 2 frequency by 25 KHz, with no carry when digit wraps" +System = "All aircraft" + +[COM2_RADIO_FRACT_INC_CARRY] +CategoryId = Avionics +SimEventName = "COM2_RADIO_FRACT_INC_CARRY" +Name = "Increment COM 2 frequency by 25 KHz, and carry when digit wraps" +Description = "Increment COM 2 frequency by 25 KHz, and carry when digit wraps" +System = "All aircraft" + +[COM2_RADIO_SET] +CategoryId = Avionics +SimEventName = "COM2_RADIO_SET" +Name = "Sets COM 2 frequency (BCD Hz)" +Description = "Sets COM 2 frequency (BCD Hz)" +System = "All aircraft" + +[COM2_STBY_RADIO_SET] +CategoryId = Avionics +SimEventName = "COM2_STBY_RADIO_SET" +Name = "Sets COM 2 standby frequency (BCD Hz)" +Description = "Sets COM 2 standby frequency (BCD Hz)" +System = "All aircraft" + +[COM2_RADIO_SWAP] +CategoryId = Avionics +SimEventName = "COM2_RADIO_SWAP" +Name = "Swaps COM 2 frequency with standby" +Description = "Swaps COM 2 frequency with standby" +System = "All aircraft" + +[NAV1_RADIO_FRACT_DEC_CARRY] +CategoryId = Avionics +SimEventName = "NAV1_RADIO_FRACT_DEC_CARRY" +Name = "Decrement NAV 1 frequency by 50 KHz, and carry when digit wraps" +Description = "Decrement NAV 1 frequency by 50 KHz, and carry when digit wraps" +System = "Shared Cockpit" + +[NAV1_RADIO_FRACT_INC_CARRY] +CategoryId = Avionics +SimEventName = "NAV1_RADIO_FRACT_INC_CARRY" +Name = "Increment NAV 1 frequency by 50 KHz, and carry when digit wraps" +Description = "Increment NAV 1 frequency by 50 KHz, and carry when digit wraps" +System = "Shared Cockpit" + +[NAV1_STBY_SET] +CategoryId = Avionics +SimEventName = "NAV1_STBY_SET" +Name = "Sets NAV 1 standby frequency (BCD Hz)" +Description = "Sets NAV 1 standby frequency (BCD Hz)" +System = "Shared Cockpit" + +[NAV1_RADIO_SWAP] +CategoryId = Avionics +SimEventName = "NAV1_RADIO_SWAP" +Name = "Swaps NAV 1 frequency with standby" +Description = "Swaps NAV 1 frequency with standby" +System = "Shared Cockpit" + +[NAV2_RADIO_FRACT_DEC_CARRY] +CategoryId = Avionics +SimEventName = "NAV2_RADIO_FRACT_DEC_CARRY" +Name = "Decrement NAV 2 frequency by 50 KHz, and carry when digit wraps" +Description = "Decrement NAV 2 frequency by 50 KHz, and carry when digit wraps" +System = "Shared Cockpit" + +[NAV2_RADIO_FRACT_INC_CARRY] +CategoryId = Avionics +SimEventName = "NAV2_RADIO_FRACT_INC_CARRY" +Name = "Increment NAV 2 frequency by 50 KHz, and carry when digit wraps" +Description = "Increment NAV 2 frequency by 50 KHz, and carry when digit wraps" +System = "Shared Cockpit" + +[NAV2_STBY_SET] +CategoryId = Avionics +SimEventName = "NAV2_STBY_SET" +Name = "Sets NAV 2 standby frequency (BCD Hz)" +Description = "Sets NAV 2 standby frequency (BCD Hz)" +System = "Shared Cockpit" + +[NAV2_RADIO_SWAP] +CategoryId = Avionics +SimEventName = "NAV2_RADIO_SWAP" +Name = "Swaps NAV 2 frequency with standby" +Description = "Swaps NAV 2 frequency with standby" +System = "Shared Cockpit" + +[ADF1_RADIO_TENTHS_DEC] +CategoryId = Avionics +SimEventName = "ADF1_RADIO_TENTHS_DEC" +Name = "Decrements ADF 1 by 0" +Description = "Decrements ADF 1 by 0.1 KHz." +System = "Shared Cockpit" + +[ADF1_RADIO_TENTHS_INC] +CategoryId = Avionics +SimEventName = "ADF1_RADIO_TENTHS_INC" +Name = "Increments ADF 1 by 0" +Description = "Increments ADF 1 by 0.1 KHz." +System = "Shared Cockpit" + +[XPNDR_1000_DEC] +CategoryId = Avionics +SimEventName = "XPNDR_1000_DEC" +Name = "Decrements first digit of transponder" +Description = "Decrements first digit of transponder" +System = "Shared Cockpit" + +[XPNDR_100_DEC] +CategoryId = Avionics +SimEventName = "XPNDR_100_DEC" +Name = "Decrements second digit of transponder" +Description = "Decrements second digit of transponder" +System = "Shared Cockpit" + +[XPNDR_10_DEC] +CategoryId = Avionics +SimEventName = "XPNDR_10_DEC" +Name = "Decrements third digit of transponder" +Description = "Decrements third digit of transponder" +System = "Shared Cockpit" + +[XPNDR_1_DEC] +CategoryId = Avionics +SimEventName = "XPNDR_1_DEC" +Name = "Decrements fourth digit of transponder" +Description = "Decrements fourth digit of transponder" +System = "Shared Cockpit" + +[XPNDR_DEC_CARRY] +CategoryId = Avionics +SimEventName = "XPNDR_DEC_CARRY" +Name = "Decrements fourth digit of transponder, and with carry" +Description = "Decrements fourth digit of transponder, and with carry." +System = "Shared Cockpit" + +[XPNDR_INC_CARRY] +CategoryId = Avionics +SimEventName = "XPNDR_INC_CARRY" +Name = "Increments fourth digit of transponder, and with carry" +Description = "Increments fourth digit of transponder, and with carry." +System = "Shared Cockpit" + +[ADF_FRACT_DEC_CARRY] +CategoryId = Avionics +SimEventName = "ADF_FRACT_DEC_CARRY" +Name = "Decrements ADF 1 frequency by 0" +Description = "Decrements ADF 1 frequency by 0.1 KHz, with carry" +System = "Shared Cockpit" + +[ADF_FRACT_INC_CARRY] +CategoryId = Avionics +SimEventName = "ADF_FRACT_INC_CARRY" +Name = "Increments ADF 1 frequency by 0" +Description = "Increments ADF 1 frequency by 0.1 KHz, with carry" +System = "Shared Cockpit" + +[COM1_TRANSMIT_SELECT] +CategoryId = Avionics +SimEventName = "COM1_TRANSMIT_SELECT" +Name = "Selects COM 1 to transmit" +Description = "Selects COM 1 to transmit" +System = "All aircraft" + +[COM2_TRANSMIT_SELECT] +CategoryId = Avionics +SimEventName = "COM2_TRANSMIT_SELECT" +Name = "Selects COM 2 to transmit" +Description = "Selects COM 2 to transmit" +System = "All aircraft" + +[COM_RECEIVE_ALL_TOGGLE] +CategoryId = Avionics +SimEventName = "COM_RECEIVE_ALL_TOGGLE" +Name = "Toggles all COM radios to receive on" +Description = "Toggles all COM radios to receive on" +System = "All aircraft" + +[COM_RECEIVE_ALL_SET] +CategoryId = Avionics +SimEventName = "COM_RECEIVE_ALL_SET" +Name = "Sets whether to receive on all COM radios (1,0)" +Description = "Sets whether to receive on all COM radios (1,0)" +System = "All aircraft" + +[MARKER_SOUND_TOGGLE] +CategoryId = Avionics +SimEventName = "MARKER_SOUND_TOGGLE" +Name = "Toggles marker beacon sound on/off" +Description = "Toggles marker beacon sound on/off" +System = "Shared Cockpit" + +[ADF_COMPLETE_SET] +CategoryId = Avionics +SimEventName = "ADF_COMPLETE_SET" +Name = "Sets ADF 1 frequency (BCD Hz)" +Description = "Sets ADF 1 frequency (BCD Hz)" +System = "Shared Cockpit" + +[ADF1_WHOLE_INC] +CategoryId = Avionics +SimEventName = "ADF1_WHOLE_INC" +Name = "Increments ADF 1 by 1 KHz, with carry as digits wrap" +Description = "Increments ADF 1 by 1 KHz, with carry as digits wrap." +System = "Shared Cockpit" + +[ADF1_WHOLE_DEC] +CategoryId = Avionics +SimEventName = "ADF1_WHOLE_DEC" +Name = "Decrements ADF 1 by 1 KHz, with carry as digits wrap" +Description = "Decrements ADF 1 by 1 KHz, with carry as digits wrap." +System = "Shared Cockpit" + +[ADF2_100_INC] +CategoryId = Avionics +SimEventName = "ADF2_100_INC" +Name = "Increments the ADF 2 frequency 100 digit, with wrapping" +Description = "Increments the ADF 2 frequency 100 digit, with wrapping" +System = "Shared Cockpit" + +[ADF2_10_INC] +CategoryId = Avionics +SimEventName = "ADF2_10_INC" +Name = "Increments the ADF 2 frequency 10 digit, with wrapping" +Description = "Increments the ADF 2 frequency 10 digit, with wrapping" +System = "Shared Cockpit" + +[ADF2_1_INC] +CategoryId = Avionics +SimEventName = "ADF2_1_INC" +Name = "Increments the ADF 2 frequency 1 digit, with wrapping" +Description = "Increments the ADF 2 frequency 1 digit, with wrapping" +System = "Shared Cockpit" + +[ADF2_RADIO_TENTHS_INC] +CategoryId = Avionics +SimEventName = "ADF2_RADIO_TENTHS_INC" +Name = "Increments ADF 2 frequency 1/10 digit, with wrapping" +Description = "Increments ADF 2 frequency 1/10 digit, with wrapping" +System = "Shared Cockpit" + +[ADF2_100_DEC] +CategoryId = Avionics +SimEventName = "ADF2_100_DEC" +Name = "Decrements the ADF 2 frequency 100 digit, with wrapping" +Description = "Decrements the ADF 2 frequency 100 digit, with wrapping" +System = "Shared Cockpit" + +[ADF2_10_DEC] +CategoryId = Avionics +SimEventName = "ADF2_10_DEC" +Name = "Decrements the ADF 2 frequency 10 digit, with wrapping" +Description = "Decrements the ADF 2 frequency 10 digit, with wrapping" +System = "Shared Cockpit" + +[ADF2_1_DEC] +CategoryId = Avionics +SimEventName = "ADF2_1_DEC" +Name = "Decrements the ADF 2 frequency 1 digit, with wrapping" +Description = "Decrements the ADF 2 frequency 1 digit, with wrapping" +System = "Shared Cockpit" + +[ADF2_RADIO_TENTHS_DEC] +CategoryId = Avionics +SimEventName = "ADF2_RADIO_TENTHS_DEC" +Name = "Decrements ADF 2 frequency 1/10 digit, with wrapping" +Description = "Decrements ADF 2 frequency 1/10 digit, with wrapping" +System = "Shared Cockpit" + +[ADF2_WHOLE_INC] +CategoryId = Avionics +SimEventName = "ADF2_WHOLE_INC" +Name = "Increments ADF 2 by 1 KHz, with carry as digits wrap" +Description = "Increments ADF 2 by 1 KHz, with carry as digits wrap." +System = "Shared Cockpit" + +[ADF2_WHOLE_DEC] +CategoryId = Avionics +SimEventName = "ADF2_WHOLE_DEC" +Name = "Decrements ADF 2 by 1 KHz, with carry as digits wrap" +Description = "Decrements ADF 2 by 1 KHz, with carry as digits wrap." +System = "Shared Cockpit" + +[ADF2_FRACT_DEC_CARRY] +CategoryId = Avionics +SimEventName = "ADF2_FRACT_DEC_CARRY" +Name = "Decrements ADF 2 frequency by 0" +Description = "Decrements ADF 2 frequency by 0.1 KHz, with carry" +System = "Shared Cockpit" + +[ADF2_FRACT_INC_CARRY] +CategoryId = Avionics +SimEventName = "ADF2_FRACT_INC_CARRY" +Name = "Increments ADF 2 frequency by 0" +Description = "Increments ADF 2 frequency by 0.1 KHz, with carry" +System = "Shared Cockpit" + +[ADF2_COMPLETE_SET] +CategoryId = Avionics +SimEventName = "ADF2_COMPLETE_SET" +Name = "Sets ADF 1 frequency (BCD Hz)" +Description = "Sets ADF 1 frequency (BCD Hz)" +System = "Shared Cockpit" + +[RADIO_ADF2_IDENT_DISABLE] +CategoryId = Avionics +SimEventName = "RADIO_ADF2_IDENT_DISABLE" +Name = "Turns ADF 2 ID off" +Description = "Turns ADF 2 ID off" +System = "Shared Cockpit" + +[RADIO_ADF2_IDENT_ENABLE] +CategoryId = Avionics +SimEventName = "RADIO_ADF2_IDENT_ENABLE" +Name = "Turns ADF 2 ID on" +Description = "Turns ADF 2 ID on" +System = "Shared Cockpit" + +[RADIO_ADF2_IDENT_TOGGLE] +CategoryId = Avionics +SimEventName = "RADIO_ADF2_IDENT_TOGGLE" +Name = "Toggles ADF 2 ID" +Description = "Toggles ADF 2 ID" +System = "Shared Cockpit" + +[RADIO_ADF2_IDENT_SET] +CategoryId = Avionics +SimEventName = "RADIO_ADF2_IDENT_SET" +Name = "Sets ADF 2 ID on/off (1,0)" +Description = "Sets ADF 2 ID on/off (1,0)" +System = "Shared Cockpit" + +[FREQUENCY_SWAP] +CategoryId = Avionics +SimEventName = "FREQUENCY_SWAP" +Name = "Swaps frequency with standby on whichever NAV or COM radio is selected" +Description = "Swaps frequency with standby on whichever NAV or COM radio is selected." +System = "Shared Cockpit" + +[TOGGLE_GPS_DRIVES_NAV1] +CategoryId = Avionics +SimEventName = "TOGGLE_GPS_DRIVES_NAV1" +Name = "Toggles between GPS and NAV 1 driving NAV 1 OBS display (and AP)" +Description = "Toggles between GPS and NAV 1 driving NAV 1 OBS display (and AP)" +System = "Shared Cockpit" + +[GPS_POWER_BUTTON] +CategoryId = Avionics +SimEventName = "GPS_POWER_BUTTON" +Name = "Toggles power button" +Description = "Toggles power button" +System = "Shared Cockpit" + +[GPS_NEAREST_BUTTON] +CategoryId = Avionics +SimEventName = "GPS_NEAREST_BUTTON" +Name = "Selects Nearest Airport Page" +Description = "Selects Nearest Airport Page" +System = "Shared Cockpit" + +[GPS_OBS_BUTTON] +CategoryId = Avionics +SimEventName = "GPS_OBS_BUTTON" +Name = "Toggles automatic sequencing of waypoints" +Description = "Toggles automatic sequencing of waypoints" +System = "Shared Cockpit" + +[GPS_MSG_BUTTON] +CategoryId = Avionics +SimEventName = "GPS_MSG_BUTTON" +Name = "Toggles the Message Page" +Description = "Toggles the Message Page" +System = "Shared Cockpit" + +[GPS_MSG_BUTTON_DOWN] +CategoryId = Avionics +SimEventName = "GPS_MSG_BUTTON_DOWN" +Name = "Triggers the pressing of the message button" +Description = "Triggers the pressing of the message button." +System = "Shared Cockpit" + +[GPS_MSG_BUTTON_UP] +CategoryId = Avionics +SimEventName = "GPS_MSG_BUTTON_UP" +Name = "Triggers the release of the message button" +Description = "Triggers the release of the message button" +System = "Shared Cockpit" + +[GPS_FLIGHTPLAN_BUTTON] +CategoryId = Avionics +SimEventName = "GPS_FLIGHTPLAN_BUTTON" +Name = "Displays the programmed flightplan" +Description = "Displays the programmed flightplan." +System = "Shared Cockpit" + +[GPS_TERRAIN_BUTTON] +CategoryId = Avionics +SimEventName = "GPS_TERRAIN_BUTTON" +Name = "Displays terrain information on default display" +Description = "Displays terrain information on default display" +System = "Shared Cockpit" + +[GPS_PROCEDURE_BUTTON] +CategoryId = Avionics +SimEventName = "GPS_PROCEDURE_BUTTON" +Name = "Displays the approach procedure page" +Description = "Displays the approach procedure page." +System = "Shared Cockpit" + +[GPS_ZOOMIN_BUTTON] +CategoryId = Avionics +SimEventName = "GPS_ZOOMIN_BUTTON" +Name = "Zooms in default display" +Description = "Zooms in default display" +System = "Shared Cockpit" + +[GPS_ZOOMOUT_BUTTON] +CategoryId = Avionics +SimEventName = "GPS_ZOOMOUT_BUTTON" +Name = "Zooms out default display" +Description = "Zooms out default display" +System = "Shared Cockpit" + +[GPS_DIRECTTO_BUTTON] +CategoryId = Avionics +SimEventName = "GPS_DIRECTTO_BUTTON" +Name = "Brings up the "Direct To" page" +Description = "Brings up the "Direct To" page" +System = "Shared Cockpit" + +[GPS_MENU_BUTTON] +CategoryId = Avionics +SimEventName = "GPS_MENU_BUTTON" +Name = "Brings up page to select active legs in a flightplan" +Description = "Brings up page to select active legs in a flightplan." +System = "Shared Cockpit" + +[GPS_CLEAR_BUTTON] +CategoryId = Avionics +SimEventName = "GPS_CLEAR_BUTTON" +Name = "Clears entered data on a page" +Description = "Clears entered data on a page" +System = "Shared Cockpit" + +[GPS_CLEAR_ALL_BUTTON] +CategoryId = Avionics +SimEventName = "GPS_CLEAR_ALL_BUTTON" +Name = "Clears all data immediately" +Description = "Clears all data immediately" +System = "Shared Cockpit" + +[GPS_CLEAR_BUTTON_DOWN] +CategoryId = Avionics +SimEventName = "GPS_CLEAR_BUTTON_DOWN" +Name = "Triggers the pressing of the Clear button" +Description = "Triggers the pressing of the Clear button" +System = "Shared Cockpit" + +[GPS_CLEAR_BUTTON_UP] +CategoryId = Avionics +SimEventName = "GPS_CLEAR_BUTTON_UP" +Name = "Triggers the release of the Clear button" +Description = "Triggers the release of the Clear button." +System = "Shared Cockpit" + +[GPS_ENTER_BUTTON] +CategoryId = Avionics +SimEventName = "GPS_ENTER_BUTTON" +Name = "Approves entered data" +Description = "Approves entered data." +System = "Shared Cockpit" + +[GPS_CURSOR_BUTTON] +CategoryId = Avionics +SimEventName = "GPS_CURSOR_BUTTON" +Name = "Selects GPS cursor" +Description = "Selects GPS cursor" +System = "Shared Cockpit" + +[GPS_GROUP_KNOB_INC] +CategoryId = Avionics +SimEventName = "GPS_GROUP_KNOB_INC" +Name = "Increments cursor" +Description = "Increments cursor" +System = "Shared Cockpit" + +[GPS_GROUP_KNOB_DEC] +CategoryId = Avionics +SimEventName = "GPS_GROUP_KNOB_DEC" +Name = "Decrements cursor" +Description = "Decrements cursor" +System = "Shared Cockpit" + +[GPS_PAGE_KNOB_INC] +CategoryId = Avionics +SimEventName = "GPS_PAGE_KNOB_INC" +Name = "Increments through pages" +Description = "Increments through pages" +System = "Shared Cockpit" + +[GPS_PAGE_KNOB_DEC] +CategoryId = Avionics +SimEventName = "GPS_PAGE_KNOB_DEC" +Name = "Decrements through pages" +Description = "Decrements through pages" +System = "Shared Cockpit" + +[DME_SELECT] +CategoryId = Avionics +SimEventName = "DME_SELECT" +Name = "Selects one of the two DME systems (1,2)," +Description = "Selects one of the two DME systems (1,2),." +System = "Shared Cockpit" + +[RADIO_SELECTED_DME_IDENT_ENABLE] +CategoryId = Avionics +SimEventName = "RADIO_SELECTED_DME_IDENT_ENABLE" +Name = "Turns on the identification sound for the selected DME" +Description = "Turns on the identification sound for the selected DME." +System = "Shared Cockpit" + +[RADIO_SELECTED_DME_IDENT_DISABLE] +CategoryId = Avionics +SimEventName = "RADIO_SELECTED_DME_IDENT_DISABLE" +Name = "Turns off the identification sound for the selected DME" +Description = "Turns off the identification sound for the selected DME." +System = "Shared Cockpit" + +[RADIO_SELECTED_DME_IDENT_SET] +CategoryId = Avionics +SimEventName = "RADIO_SELECTED_DME_IDENT_SET" +Name = "Sets the DME identification sound to the given filename" +Description = "Sets the DME identification sound to the given filename." +System = "Shared Cockpit" + +[RADIO_SELECTED_DME_IDENT_TOGGLE] +CategoryId = Avionics +SimEventName = "RADIO_SELECTED_DME_IDENT_TOGGLE" +Name = "Turns on or off the identification sound for the selected DME" +Description = "Turns on or off the identification sound for the selected DME." +System = "Shared Cockpit" + + + +# Instruments ####################### +# +[category_Instruments] +Name = "Instruments" + + +[EGT] +CategoryId = Instruments +SimEventName = "EGT" +Name = "Selects EGT bug for +/-" +Description = "Selects EGT bug for +/-" +System = "Shared Cockpit" + +[EGT_INC] +CategoryId = Instruments +SimEventName = "EGT_INC" +Name = "Increments EGT bugs" +Description = "Increments EGT bugs" +System = "Shared Cockpit" + +[EGT_DEC] +CategoryId = Instruments +SimEventName = "EGT_DEC" +Name = "Decrements EGT bugs" +Description = "Decrements EGT bugs" +System = "Shared Cockpit" + +[EGT_SET] +CategoryId = Instruments +SimEventName = "EGT_SET" +Name = "Sets EGT bugs (0 to 32767)" +Description = "Sets EGT bugs (0 to 32767)" +System = "Shared Cockpit" + +[BAROMETRIC] +CategoryId = Instruments +SimEventName = "BAROMETRIC" +Name = "Syncs altimeter setting to sea level pressure, or 29.92 if > 18000 feet" +Description = "Syncs altimeter setting to sea level pressure, or 29.92 if above 18000 feet" +System = "Shared Cockpit" + +[GYRO_DRIFT_INC] +CategoryId = Instruments +SimEventName = "GYRO_DRIFT_INC" +Name = "Increments heading indicator" +Description = "Increments heading indicator" +System = "Shared Cockpit" + +[GYRO_DRIFT_DEC] +CategoryId = Instruments +SimEventName = "GYRO_DRIFT_DEC" +Name = "Decrements heading indicator" +Description = "Decrements heading indicator" +System = "Shared Cockpit" + +[KOHLSMAN_INC] +CategoryId = Instruments +SimEventName = "KOHLSMAN_INC" +Name = "Increments altimeter setting" +Description = "Increments altimeter setting" +System = "Shared Cockpit" + +[KOHLSMAN_DEC] +CategoryId = Instruments +SimEventName = "KOHLSMAN_DEC" +Name = "Decrements altimeter setting" +Description = "Decrements altimeter setting" +System = "Shared Cockpit" + +[KOHLSMAN_SET] +CategoryId = Instruments +SimEventName = "KOHLSMAN_SET" +Name = "Sets altimeter setting (Millibars * 16)" +Description = "Sets altimeter setting (Millibars * 16)" +System = "Shared Cockpit" + +[TRUE_AIRSPEED_CAL_INC] +CategoryId = Instruments +SimEventName = "TRUE_AIRSPEED_CAL_INC" +Name = "Increments airspeed indicators true airspeed reference card" +Description = "Increments airspeed indicators true airspeed reference card" +System = "Shared Cockpit" + +[TRUE_AIRSPEED_CAL_DEC] +CategoryId = Instruments +SimEventName = "TRUE_AIRSPEED_CAL_DEC" +Name = "Decrements airspeed indicators true airspeed reference card" +Description = "Decrements airspeed indicators true airspeed reference card" +System = "Shared Cockpit" + +[TRUE_AIRSPEED_CAL_SET] +CategoryId = Instruments +SimEventName = "TRUE_AIRSPEED_CAL_SET" +Name = "Sets airspeed indicators true airspeed reference card" +Description = "Sets airspeed indicators true airspeed reference card (degrees, where 0 is standard sea level conditions)" +System = "Shared Cockpit" + +[EGT1_INC] +CategoryId = Instruments +SimEventName = "EGT1_INC" +Name = "Increments EGT bug 1" +Description = "Increments EGT bug 1" +System = "Shared Cockpit" + +[EGT1_DEC] +CategoryId = Instruments +SimEventName = "EGT1_DEC" +Name = "Decrements EGT bug 1" +Description = "Decrements EGT bug 1" +System = "Shared Cockpit" + +[EGT1_SET] +CategoryId = Instruments +SimEventName = "EGT1_SET" +Name = "Sets EGT bug 1 (0 to 32767)" +Description = "Sets EGT bug 1 (0 to 32767)" +System = "Shared Cockpit" + +[EGT2_INC] +CategoryId = Instruments +SimEventName = "EGT2_INC" +Name = "Increments EGT bug 2" +Description = "Increments EGT bug 2" +System = "Shared Cockpit" + +[EGT2_DEC] +CategoryId = Instruments +SimEventName = "EGT2_DEC" +Name = "Decrements EGT bug 2" +Description = "Decrements EGT bug 2" +System = "Shared Cockpit" + +[EGT2_SET] +CategoryId = Instruments +SimEventName = "EGT2_SET" +Name = "Sets EGT bug 2 (0 to 32767)" +Description = "Sets EGT bug 2 (0 to 32767)" +System = "Shared Cockpit" + +[EGT3_INC] +CategoryId = Instruments +SimEventName = "EGT3_INC" +Name = "Increments EGT bug 3" +Description = "Increments EGT bug 3" +System = "Shared Cockpit" + +[EGT3_DEC] +CategoryId = Instruments +SimEventName = "EGT3_DEC" +Name = "Decrements EGT bug 3" +Description = "Decrements EGT bug 3" +System = "Shared Cockpit" + +[EGT3_SET] +CategoryId = Instruments +SimEventName = "EGT3_SET" +Name = "Sets EGT bug 3 (0 to 32767)" +Description = "Sets EGT bug 3 (0 to 32767)" +System = "Shared Cockpit" + +[EGT4_INC] +CategoryId = Instruments +SimEventName = "EGT4_INC" +Name = "Increments EGT bug 4" +Description = "Increments EGT bug 4" +System = "Shared Cockpit" + +[EGT4_DEC] +CategoryId = Instruments +SimEventName = "EGT4_DEC" +Name = "Decrements EGT bug 4" +Description = "Decrements EGT bug 4" +System = "Shared Cockpit" + +[EGT4_SET] +CategoryId = Instruments +SimEventName = "EGT4_SET" +Name = "Sets EGT bug 4 (0 to 32767)" +Description = "Sets EGT bug 4 (0 to 32767)" +System = "Shared Cockpit" + +[ATTITUDE_BARS_POSITION_UP] +CategoryId = Instruments +SimEventName = "ATTITUDE_BARS_POSITION_UP" +Name = "Increments attitude indicator pitch reference bars" +Description = "Increments attitude indicator pitch reference bars" +System = "Shared Cockpit" + +[ATTITUDE_BARS_POSITION_DOWN] +CategoryId = Instruments +SimEventName = "ATTITUDE_BARS_POSITION_DOWN" +Name = "Decrements attitude indicator pitch reference bars" +Description = "Decrements attitude indicator pitch reference bars" +System = "Shared Cockpit" + +[ATTITUDE_CAGE_BUTTON] +CategoryId = Instruments +SimEventName = "ATTITUDE_CAGE_BUTTON" +Name = "Cages attitude indicator at 0 pitch and bank" +Description = "Cages attitude indicator at 0 pitch and bank" +System = "Shared Cockpit" + +[RESET_G_FORCE_INDICATOR] +CategoryId = Instruments +SimEventName = "RESET_G_FORCE_INDICATOR" +Name = "Resets max/min indicated G force to 1" +Description = "Resets max/min indicated G force to 1.0." +System = "Shared Cockpit" + +[RESET_MAX_RPM_INDICATOR] +CategoryId = Instruments +SimEventName = "RESET_MAX_RPM_INDICATOR" +Name = "Reset max indicated engine rpm to 0" +Description = "Reset max indicated engine rpm to 0." +System = "Shared Cockpit" + +[HEADING_GYRO_SET] +CategoryId = Instruments +SimEventName = "HEADING_GYRO_SET" +Name = "Sets heading indicator to 0 drift error" +Description = "Sets heading indicator to 0 drift error." +System = "Shared Cockpit" + +[GYRO_DRIFT_SET] +CategoryId = Instruments +SimEventName = "GYRO_DRIFT_SET" +Name = "Sets heading indicator drift angle (degrees)," +Description = "Sets heading indicator drift angle (degrees),." +System = "Shared Cockpit" + + + +# Lights ####################### +# +[category_Lights] +Name = "Lights" + + +[STROBES_TOGGLE] +CategoryId = Lights +SimEventName = "STROBES_TOGGLE" +Name = "Toggle strobe lights" +Description = "Toggle strobe lights " +System = "All aircraft" + +[ALL_LIGHTS_TOGGLE] +CategoryId = Lights +SimEventName = "ALL_LIGHTS_TOGGLE" +Name = "Toggle all lights" +Description = "Toggle all lights" +System = "Shared Cockpit" + +[PANEL_LIGHTS_TOGGLE] +CategoryId = Lights +SimEventName = "PANEL_LIGHTS_TOGGLE" +Name = "Toggle panel lights" +Description = "Toggle panel lights" +System = "All aircraft" + +[LANDING_LIGHTS_TOGGLE] +CategoryId = Lights +SimEventName = "LANDING_LIGHTS_TOGGLE" +Name = "Toggle landing lights" +Description = "Toggle landing lights" +System = "All aircraft" + +[LANDING_LIGHT_UP] +CategoryId = Lights +SimEventName = "LANDING_LIGHT_UP" +Name = "Rotate landing light up" +Description = "Rotate landing light up" +System = "Shared Cockpit" + +[LANDING_LIGHT_DOWN] +CategoryId = Lights +SimEventName = "LANDING_LIGHT_DOWN" +Name = "Rotate landing light down" +Description = "Rotate landing light down" +System = "Shared Cockpit" + +[LANDING_LIGHT_LEFT] +CategoryId = Lights +SimEventName = "LANDING_LIGHT_LEFT" +Name = "Rotate landing light left" +Description = "Rotate landing light left" +System = "Shared Cockpit" + +[LANDING_LIGHT_RIGHT] +CategoryId = Lights +SimEventName = "LANDING_LIGHT_RIGHT" +Name = "Rotate landing light right" +Description = "Rotate landing light right" +System = "Shared Cockpit" + +[LANDING_LIGHT_HOME] +CategoryId = Lights +SimEventName = "LANDING_LIGHT_HOME" +Name = "Return landing light to default position" +Description = "Return landing light to default position" +System = "Shared Cockpit" + +[STROBES_ON] +CategoryId = Lights +SimEventName = "STROBES_ON" +Name = "Turn strobe lights on" +Description = "Turn strobe lights on" +System = "All aircraft" + +[STROBES_OFF] +CategoryId = Lights +SimEventName = "STROBES_OFF" +Name = "Turn strobe light off" +Description = "Turn strobe light off" +System = "All aircraft" + +[STROBES_SET] +CategoryId = Lights +SimEventName = "STROBES_SET" +Name = "Set strobe lights on/off (1,0)" +Description = "Set strobe lights on/off (1,0)" +System = "All aircraft" + +[PANEL_LIGHTS_ON] +CategoryId = Lights +SimEventName = "PANEL_LIGHTS_ON" +Name = "Turn panel lights on" +Description = "Turn panel lights on" +System = "All aircraft" + +[PANEL_LIGHTS_OFF] +CategoryId = Lights +SimEventName = "PANEL_LIGHTS_OFF" +Name = "Turn panel lights off" +Description = "Turn panel lights off" +System = "All aircraft" + +[PANEL_LIGHTS_SET] +CategoryId = Lights +SimEventName = "PANEL_LIGHTS_SET" +Name = "Set panel lights on/off (1,0)" +Description = "Set panel lights on/off (1,0)" +System = "All aircraft" + +[LANDING_LIGHTS_ON] +CategoryId = Lights +SimEventName = "LANDING_LIGHTS_ON" +Name = "Turn landing lights on" +Description = "Turn landing lights on" +System = "All aircraft" + +[LANDING_LIGHTS_OFF] +CategoryId = Lights +SimEventName = "LANDING_LIGHTS_OFF" +Name = "Turn landing lights off" +Description = "Turn landing lights off" +System = "All aircraft" + +[LANDING_LIGHTS_SET] +CategoryId = Lights +SimEventName = "LANDING_LIGHTS_SET" +Name = "Set landing lights on/off (1,0)" +Description = "Set landing lights on/off (1,0)" +System = "All aircraft" + +[TOGGLE_BEACON_LIGHTS] +CategoryId = Lights +SimEventName = "TOGGLE_BEACON_LIGHTS" +Name = "Toggle beacon lights" +Description = "Toggle beacon lights" +System = "All aircraft" + +[TOGGLE_TAXI_LIGHTS] +CategoryId = Lights +SimEventName = "TOGGLE_TAXI_LIGHTS" +Name = "Toggle taxi lights" +Description = "Toggle taxi lights" +System = "All aircraft" + +[TOGGLE_LOGO_LIGHTS] +CategoryId = Lights +SimEventName = "TOGGLE_LOGO_LIGHTS" +Name = "Toggle logo lights" +Description = "Toggle logo lights" +System = "All aircraft" + +[TOGGLE_RECOGNITION_LIGHTS] +CategoryId = Lights +SimEventName = "TOGGLE_RECOGNITION_LIGHTS" +Name = "Toggle recognition lights" +Description = "Toggle recognition lights" +System = "All aircraft" + +[TOGGLE_WING_LIGHTS] +CategoryId = Lights +SimEventName = "TOGGLE_WING_LIGHTS" +Name = "Toggle wing lights" +Description = "Toggle wing lights" +System = "All aircraft" + +[TOGGLE_NAV_LIGHTS] +CategoryId = Lights +SimEventName = "TOGGLE_NAV_LIGHTS" +Name = "Toggle navigation lights" +Description = "Toggle navigation lights" +System = "All aircraft" + +[TOGGLE_CABIN_LIGHTS] +CategoryId = Lights +SimEventName = "TOGGLE_CABIN_LIGHTS" +Name = "Toggle cockpit/cabin lights" +Description = "Toggle cockpit/cabin lights" +System = "All aircraft" + + + +# Failures ####################### +# +[category_Failures] +Name = "Failures" + + +[TOGGLE_VACUUM_FAILURE] +CategoryId = Failures +SimEventName = "TOGGLE_VACUUM_FAILURE" +Name = "Toggle vacuum system failure" +Description = "Toggle vacuum system failure" +System = "Shared Cockpit" + +[TOGGLE_ELECTRICAL_FAILURE] +CategoryId = Failures +SimEventName = "TOGGLE_ELECTRICAL_FAILURE" +Name = "Toggle electrical system failure" +Description = "Toggle electrical system failure" +System = "Shared Cockpit" + +[TOGGLE_PITOT_BLOCKAGE] +CategoryId = Failures +SimEventName = "TOGGLE_PITOT_BLOCKAGE" +Name = "Toggles blocked pitot tube" +Description = "Toggles blocked pitot tube" +System = "Shared Cockpit" + +[TOGGLE_STATIC_PORT_BLOCKAGE] +CategoryId = Failures +SimEventName = "TOGGLE_STATIC_PORT_BLOCKAGE" +Name = "Toggles blocked static port" +Description = " Toggles blocked static port" +System = "Shared Cockpit" + +[TOGGLE_HYDRAULIC_FAILURE] +CategoryId = Failures +SimEventName = "TOGGLE_HYDRAULIC_FAILURE" +Name = "Toggles hydraulic system failure" +Description = "Toggles hydraulic system failure" +System = "Shared Cockpit" + +[TOGGLE_TOTAL_BRAKE_FAILURE] +CategoryId = Failures +SimEventName = "TOGGLE_TOTAL_BRAKE_FAILURE" +Name = "Toggles brake failure (both)" +Description = "Toggles brake failure (both)" +System = "Shared Cockpit" + +[TOGGLE_LEFT_BRAKE_FAILURE] +CategoryId = Failures +SimEventName = "TOGGLE_LEFT_BRAKE_FAILURE" +Name = "Toggles left brake failure" +Description = "Toggles left brake failure" +System = "Shared Cockpit" + +[TOGGLE_RIGHT_BRAKE_FAILURE] +CategoryId = Failures +SimEventName = "TOGGLE_RIGHT_BRAKE_FAILURE" +Name = "Toggles right brake failure" +Description = "Toggles right brake failure" +System = "Shared Cockpit" + +[TOGGLE_ENGINE1_FAILURE] +CategoryId = Failures +SimEventName = "TOGGLE_ENGINE1_FAILURE" +Name = "Toggle engine 1 failure" +Description = "Toggle engine 1 failure" +System = "Shared Cockpit" + +[TOGGLE_ENGINE2_FAILURE] +CategoryId = Failures +SimEventName = "TOGGLE_ENGINE2_FAILURE" +Name = "Toggle engine 2 failure" +Description = "Toggle engine 2 failure" +System = "Shared Cockpit" + +[TOGGLE_ENGINE3_FAILURE] +CategoryId = Failures +SimEventName = "TOGGLE_ENGINE3_FAILURE" +Name = "Toggle engine 3 failure" +Description = "Toggle engine 3 failure" +System = "Shared Cockpit" + +[TOGGLE_ENGINE4_FAILURE] +CategoryId = Failures +SimEventName = "TOGGLE_ENGINE4_FAILURE" +Name = "Toggle engine 4 failure" +Description = "Toggle engine 4 failure" +System = "Shared Cockpit" + + + +# Miscellaneous_Systems ####################### +# +[category_Miscellaneous_Systems] +Name = "Miscellaneous Systems" + + +[SMOKE_TOGGLE] +CategoryId = Miscellaneous_Systems +SimEventName = "SMOKE_TOGGLE" +Name = "Toggle smoke system switch" +Description = "Toggle smoke system switch" +System = "All aircraft" + +[GEAR_TOGGLE] +CategoryId = Miscellaneous_Systems +SimEventName = "GEAR_TOGGLE" +Name = "Toggle gear handle" +Description = "Toggle gear handle" +System = "All aircraft" + +[BRAKES] +CategoryId = Miscellaneous_Systems +SimEventName = "BRAKES" +Name = "Increment brake pressure" +Description = "Increment brake pressure " +System = "Shared Cockpit" + +[GEAR_SET] +CategoryId = Miscellaneous_Systems +SimEventName = "GEAR_SET" +Name = "Sets gear handle position up/down (0,1)" +Description = "Sets gear handle position up/down (0,1)" +System = "All aircraft" + +[BRAKES_LEFT] +CategoryId = Miscellaneous_Systems +SimEventName = "BRAKES_LEFT" +Name = "Increments left brake pressure" +Description = "Increments left brake pressure" +System = "Shared Cockpit" + +[BRAKES_RIGHT] +CategoryId = Miscellaneous_Systems +SimEventName = "BRAKES_RIGHT" +Name = "Increments right brake pressure" +Description = "Increments right brake pressure" +System = "Shared Cockpit" + +[PARKING_BRAKES] +CategoryId = Miscellaneous_Systems +SimEventName = "PARKING_BRAKES" +Name = "Toggles parking brake on/off" +Description = "Toggles parking brake on/off" +System = "Shared Cockpit" + +[GEAR_PUMP] +CategoryId = Miscellaneous_Systems +SimEventName = "GEAR_PUMP" +Name = "Increments emergency gear extension" +Description = "Increments emergency gear extension" +System = "Shared Cockpit" + +[PITOT_HEAT_TOGGLE] +CategoryId = Miscellaneous_Systems +SimEventName = "PITOT_HEAT_TOGGLE" +Name = "Toggles pitot heat switch" +Description = "Toggles pitot heat switch" +System = "All aircraft" + +[SMOKE_ON] +CategoryId = Miscellaneous_Systems +SimEventName = "SMOKE_ON" +Name = "Turns smoke system on" +Description = "Turns smoke system on" +System = "All aircraft" + +[SMOKE_OFF] +CategoryId = Miscellaneous_Systems +SimEventName = "SMOKE_OFF" +Name = "Turns smoke system off" +Description = "Turns smoke system off" +System = "All aircraft" + +[SMOKE_SET] +CategoryId = Miscellaneous_Systems +SimEventName = "SMOKE_SET" +Name = "Sets smoke system on/off (1,0)" +Description = "Sets smoke system on/off (1,0)" +System = "All aircraft" + +[PITOT_HEAT_ON] +CategoryId = Miscellaneous_Systems +SimEventName = "PITOT_HEAT_ON" +Name = "Turns pitot heat switch on" +Description = "Turns pitot heat switch on" +System = "Shared Cockpit" + +[PITOT_HEAT_OFF] +CategoryId = Miscellaneous_Systems +SimEventName = "PITOT_HEAT_OFF" +Name = "Turns pitot heat switch off" +Description = "Turns pitot heat switch off" +System = "Shared Cockpit" + +[PITOT_HEAT_SET] +CategoryId = Miscellaneous_Systems +SimEventName = "PITOT_HEAT_SET" +Name = "Sets pitot heat switch on/off (1,0)" +Description = "Sets pitot heat switch on/off (1,0)" +System = "Shared Cockpit" + +[GEAR_UP] +CategoryId = Miscellaneous_Systems +SimEventName = "GEAR_UP" +Name = "Sets gear handle in UP position" +Description = "Sets gear handle in UP position" +System = "All aircraft" + +[GEAR_DOWN] +CategoryId = Miscellaneous_Systems +SimEventName = "GEAR_DOWN" +Name = "Sets gear handle in DOWN position" +Description = "Sets gear handle in DOWN position" +System = "All aircraft" + +[TOGGLE_MASTER_BATTERY] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_MASTER_BATTERY" +Name = "Toggles main battery switch" +Description = "Toggles main battery switch" +System = "All aircraft" + +[TOGGLE_MASTER_ALTERNATOR] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_MASTER_ALTERNATOR" +Name = "Toggles main alternator/generator switch" +Description = "Toggles main alternator/generator switch" +System = "All aircraft" + +[TOGGLE_ELECTRIC_VACUUM_PUMP] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_ELECTRIC_VACUUM_PUMP" +Name = "Toggles backup electric vacuum pump" +Description = "Toggles backup electric vacuum pump" +System = "Shared Cockpit" + +[TOGGLE_ALTERNATE_STATIC] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_ALTERNATE_STATIC" +Name = "Toggles alternate static pressure port" +Description = "Toggles alternate static pressure port" +System = "All aircraft" + +[DECREASE_DECISION_HEIGHT] +CategoryId = Miscellaneous_Systems +SimEventName = "DECREASE_DECISION_HEIGHT" +Name = "Decrements decision height reference" +Description = "Decrements decision height reference" +System = "Shared Cockpit" + +[INCREASE_DECISION_HEIGHT] +CategoryId = Miscellaneous_Systems +SimEventName = "INCREASE_DECISION_HEIGHT" +Name = "Increments decision height reference" +Description = "Increments decision height reference" +System = "Shared Cockpit" + +[TOGGLE_STRUCTURAL_DEICE] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_STRUCTURAL_DEICE" +Name = "Toggles structural deice switch" +Description = "Toggles structural deice switch" +System = "Shared Cockpit" + +[TOGGLE_PROPELLER_DEICE] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_PROPELLER_DEICE" +Name = "Toggles propeller deice switch" +Description = "Toggles propeller deice switch" +System = "Shared Cockpit" + +[TOGGLE_ALTERNATOR1] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_ALTERNATOR1" +Name = "Toggles alternator/generator 1 switch" +Description = "Toggles alternator/generator 1 switch" +System = "All aircraft" + +[TOGGLE_ALTERNATOR2] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_ALTERNATOR2" +Name = "Toggles alternator/generator 2 switch" +Description = "Toggles alternator/generator 2 switch" +System = "All aircraft" + +[TOGGLE_ALTERNATOR3] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_ALTERNATOR3" +Name = "Toggles alternator/generator 3 switch" +Description = "Toggles alternator/generator 3 switch" +System = "All aircraft" + +[TOGGLE_ALTERNATOR4] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_ALTERNATOR4" +Name = "Toggles alternator/generator 4 switch" +Description = "Toggles alternator/generator 4 switch" +System = "All aircraft" + +[TOGGLE_MASTER_BATTERY_ALTERNATOR] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_MASTER_BATTERY_ALTERNATOR" +Name = "Toggles master battery and alternator switch" +Description = "Toggles master battery and alternator switch" +System = "Shared Cockpit" + +[AXIS_LEFT_BRAKE_SET] +CategoryId = Miscellaneous_Systems +SimEventName = "AXIS_LEFT_BRAKE_SET" +Name = "Sets left brake position from axis controller (-16384 to +16384)" +Description = "Sets left brake position from axis controller (e.g. joystick),. -16384 (0 brakes) to +16384 (max brakes)" +System = "Shared Cockpit" + +[AXIS_RIGHT_BRAKE_SET] +CategoryId = Miscellaneous_Systems +SimEventName = "AXIS_RIGHT_BRAKE_SET" +Name = "Sets right brake position from axis controller (-16384 to +16384)" +Description = "Sets right brake position from axis controller (e.g. joystick),. -16384 (0 brakes) to +16384 (max brakes)" +System = "Shared Cockpit" + +[TOGGLE_AIRCRAFT_EXIT] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_AIRCRAFT_EXIT" +Name = "Toggles primary door open/close" +Description = "Toggles primary door open/close. Follow by KEY_SELECT_2, etc for subsequent doors." +System = "Shared Cockpit" + +[TOGGLE_WING_FOLD] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_WING_FOLD" +Name = "Toggles wing folding" +Description = "Toggles wing folding" +System = "Shared Cockpit" + +[SET_WING_FOLD] +CategoryId = Miscellaneous_Systems +SimEventName = "SET_WING_FOLD" +Name = "Sets the wings into the folded (1) or unfolded (0) position " +Description = "Sets the wings into the folded position suitable for storage, typically on a carrier. Takes a value:\n 1 - fold wings,\n 0 - unfold wings" +System = "Shared Cockpit" + +[TOGGLE_TAIL_HOOK_HANDLE] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_TAIL_HOOK_HANDLE" +Name = "Toggles tail hook" +Description = "Toggles tail hook" +System = "Shared Cockpit" + +[SET_TAIL_HOOK_HANDLE] +CategoryId = Miscellaneous_Systems +SimEventName = "SET_TAIL_HOOK_HANDLE" +Name = "Sets the tail hook handle (0/1)" +Description = "Sets the tail hook handle. Takes a value:\n 1 - set tail hook,\n 0 - retract tail hook" +System = "Shared Cockpit" + +[TOGGLE_WATER_RUDDER] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_WATER_RUDDER" +Name = "Toggles water rudders" +Description = "Toggles water rudders" +System = "Shared Cockpit" + +[TOGGLE_PUSHBACK] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_PUSHBACK" +Name = "Toggles pushback" +Description = "Toggles pushback." +System = "Shared Cockpit" + +[KEY_TUG_HEADING] +CategoryId = Miscellaneous_Systems +SimEventName = "KEY_TUG_HEADING" +Name = "Triggers tug and sets the desired heading" +Description = "Triggers tug and sets the desired heading. The units are a 32 bit integer (0 to 4294967295), which represent 0 to 360 degrees. To set a 45 degree angle, for example, set the value to 4294967295 / 8." +System = "Shared Cockpit" + +[KEY_TUG_SPEED] +CategoryId = Miscellaneous_Systems +SimEventName = "KEY_TUG_SPEED" +Name = "Triggers tug, and sets desired speed, in feet per second" +Description = "Triggers tug, and sets desired speed, in feet per second. The speed can be both positive (forward movement), and negative (backward movement)." +System = "Shared Cockpit" + +[TUG_DISABLE] +CategoryId = Miscellaneous_Systems +SimEventName = "TUG_DISABLE" +Name = "Disables tug" +Description = "Disables tug" +System = "Shared Cockpit" + +[TOGGLE_MASTER_IGNITION_SWITCH] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_MASTER_IGNITION_SWITCH" +Name = "Toggles master ignition switch" +Description = "Toggles master ignition switch" +System = "Shared Cockpit" + +[TOGGLE_TAILWHEEL_LOCK] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_TAILWHEEL_LOCK" +Name = "Toggles tail wheel lock" +Description = "Toggles tail wheel lock" +System = "Shared Cockpit" + +[ADD_FUEL_QUANTITY] +CategoryId = Miscellaneous_Systems +SimEventName = "ADD_FUEL_QUANTITY" +Name = "Adds fuel to the aircraft (0 to 65535 or 25% of capacity by default)" +Description = "Adds fuel to the aircraft, 25% of capacity by default. 0 to 65535 (max fuel), can be passed." +System = "Shared Cockpit" + +[TOW_PLANE_RELEASE] +CategoryId = Miscellaneous_Systems +SimEventName = "TOW_PLANE_RELEASE" +Name = "Release a towed aircraft, usually a glider" +Description = "Release a towed aircraft, usually a glider." +System = "Shared Cockpit" + +[TOW_PLANE_REQUEST] +CategoryId = Miscellaneous_Systems +SimEventName = "TOW_PLANE_REQUEST" +Name = "Request a tow plane" +Description = "Request a tow plane. The user aircraft must be tow-able, stationary, on the ground and not already attached for this to succeed." +System = "Shared Cockpit" + +[RELEASE_DROPPABLE_OBJECTS] +CategoryId = Miscellaneous_Systems +SimEventName = "RELEASE_DROPPABLE_OBJECTS" +Name = "Release one droppable object" +Description = "Release one droppable object. Multiple key events will release multiple objects." +System = "Shared Cockpit" + +[RETRACT_FLOAT_SWITCH_DEC] +CategoryId = Miscellaneous_Systems +SimEventName = "RETRACT_FLOAT_SWITCH_DEC" +Name = "Retract floats position from Extend to Neutral, or Neutral to Retract" +Description = "If the plane has retractable floats, moves the retract position from Extend to Neutral, or Neutral to Retract." +System = "Shared Cockpit" + +[RETRACT_FLOAT_SWITCH_INC] +CategoryId = Miscellaneous_Systems +SimEventName = "RETRACT_FLOAT_SWITCH_INC" +Name = "Extend floats position from Retract to Neutral, or Neutral to Extend" +Description = "If the plane has retractable floats, moves the retract position from Retract to Neutral, or Neutral to Extend." +System = "Shared Cockpit" + +[TOGGLE_WATER_BALLAST_VALVE] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_WATER_BALLAST_VALVE" +Name = "Turn the water ballast valve on or off" +Description = "Turn the water ballast valve on or off." +System = "Shared Cockpit" + +[TOGGLE_VARIOMETER_SWITCH] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_VARIOMETER_SWITCH" +Name = "Turn the variometer on or off" +Description = "Turn the variometer on or off." +System = "Shared Cockpit" + +[TOGGLE_TURN_INDICATOR_SWITCH] +CategoryId = Miscellaneous_Systems +SimEventName = "TOGGLE_TURN_INDICATOR_SWITCH" +Name = "Turn the turn indicator on or off" +Description = "Turn the turn indicator on or off." +System = "Shared Cockpit" + +[APU_STARTER] +CategoryId = Miscellaneous_Systems +SimEventName = "APU_STARTER" +Name = "Start up the auxiliary power unit (APU)," +Description = "Start up the auxiliary power unit (APU),." +System = "Shared Cockpit" + +[APU_OFF_SWITCH] +CategoryId = Miscellaneous_Systems +SimEventName = "APU_OFF_SWITCH" +Name = "Turn the APU off" +Description = "Turn the APU off." +System = "Shared Cockpit" + +[APU_GENERATOR_SWITCH_TOGGLE] +CategoryId = Miscellaneous_Systems +SimEventName = "APU_GENERATOR_SWITCH_TOGGLE" +Name = "Turn the auxiliary generator on or off" +Description = "Turn the auxiliary generator on or off." +System = "Shared Cockpit" + +[APU_GENERATOR_SWITCH_SET] +CategoryId = Miscellaneous_Systems +SimEventName = "APU_GENERATOR_SWITCH_SET" +Name = "Set the auxiliary generator switch (0,1)," +Description = "Set the auxiliary generator switch (0,1),." +System = "Shared Cockpit" + +[EXTINGUISH_ENGINE_FIRE] +CategoryId = Miscellaneous_Systems +SimEventName = "EXTINGUISH_ENGINE_FIRE" +Name = "Extinguish engine fire (two digit argument, ext. index + engine index)" +Description = "Takes a two digit argument. The first digit represents the fire extinguisher index, and the second represents the engine index. For example, 11 would represent using bottle 1 on engine 1. 21 would represent using bottle 2 on engine 1. Typical entries for a twin engine aircraft would be 11 and 22." +System = "Shared Cockpit" + +[HYDRAULIC_SWITCH_TOGGLE] +CategoryId = Miscellaneous_Systems +SimEventName = "HYDRAULIC_SWITCH_TOGGLE" +Name = "Turn the hydraulic switch on or off" +Description = "Turn the hydraulic switch on or off." +System = "Shared Cockpit" + +[BLEED_AIR_SOURCE_CONTROL_INC] +CategoryId = Miscellaneous_Systems +SimEventName = "BLEED_AIR_SOURCE_CONTROL_INC" +Name = "Increases the bleed air source control" +Description = "Increases the bleed air source control." +System = "Shared Cockpit" + +[BLEED_AIR_SOURCE_CONTROL_DEC] +CategoryId = Miscellaneous_Systems +SimEventName = "BLEED_AIR_SOURCE_CONTROL_DEC" +Name = "Decreases the bleed air source control" +Description = "Decreases the bleed air source control." +System = "Shared Cockpit" + +[BLEED_AIR_SOURCE_CONTROL_SET] +CategoryId = Miscellaneous_Systems +SimEventName = "BLEED_AIR_SOURCE_CONTROL_SET" +Name = "Set bleed air source to one of: 0=auto, 1=off, 2=apu, 3=engines" +Description = "Set to one of:\n 0: auto\n 1: off\n 2: apu\n 3: engines" +System = "Shared Cockpit" + +[TURBINE_IGNITION_SWITCH_TOGGLE] +CategoryId = Miscellaneous_Systems +SimEventName = "TURBINE_IGNITION_SWITCH_TOGGLE" +Name = "Turn the turbine ignition switch on or off" +Description = "Turn the turbine ignition switch on or off." +System = "Shared Cockpit" + +[CABIN_NO_SMOKING_ALERT_SWITCH_TOGGLE] +CategoryId = Miscellaneous_Systems +SimEventName = "CABIN_NO_SMOKING_ALERT_SWITCH_TOGGLE" +Name = "Turn the "No smoking" alert on or off" +Description = "Turn the "No smoking" alert on or off." +System = "Shared Cockpit" + +[CABIN_SEATBELTS_ALERT_SWITCH_TOGGLE] +CategoryId = Miscellaneous_Systems +SimEventName = "CABIN_SEATBELTS_ALERT_SWITCH_TOGGLE" +Name = "Turn the "Fasten seatbelts" alert on or off" +Description = "Turn the "Fasten seatbelts" alert on or off." +System = "Shared Cockpit" + +[ANTISKID_BRAKES_TOGGLE] +CategoryId = Miscellaneous_Systems +SimEventName = "ANTISKID_BRAKES_TOGGLE" +Name = "Turn the anti-skid braking system on or off" +Description = "Turn the anti-skid braking system on or off." +System = "Shared Cockpit" + +[GPWS_SWITCH_TOGGLE] +CategoryId = Miscellaneous_Systems +SimEventName = "GPWS_SWITCH_TOGGLE" +Name = "Turn the g round proximity warning system (GPWS), on or off" +Description = "Turn the g round proximity warning system (GPWS), on or off." +System = "Shared Cockpit" + +[MANUAL_FUEL_PRESSURE_PUMP] +CategoryId = Miscellaneous_Systems +SimEventName = "MANUAL_FUEL_PRESSURE_PUMP" +Name = "Activate the manual fuel pressure pump" +Description = "Activate the manual fuel pressure pump." +System = "Shared Cockpit" + + + +# Nose_wheel_steering ####################### +# +[category_Nose_wheel_steering] +Name = "Nose wheel steering" + + +[STEERING_INC] +CategoryId = Nose_wheel_steering +SimEventName = "STEERING_INC" +Name = "Increments the nose wheel steering position by 5 percent" +Description = "Increments the nose wheel steering position by 5 percent." +System = "Shared Cockpit" + +[STEERING_DEC] +CategoryId = Nose_wheel_steering +SimEventName = "STEERING_DEC" +Name = "Decrements the nose wheel steering position by 5 percent" +Description = "Decrements the nose wheel steering position by 5 percent." +System = "Shared Cockpit" + +[STEERING_SET] +CategoryId = Nose_wheel_steering +SimEventName = "STEERING_SET" +Name = "Sets the value of the nose wheel steering position" +Description = "Sets the value of the nose wheel steering position. Zero is straight ahead (-16384, far left +16384, far right),." +System = "Shared Cockpit" + + + +# Cabin_pressurization ####################### +# +[category_Cabin_pressurization] +Name = "Cabin pressurization" + + +[KEY_PRESSURIZATION_PRESSURE_ALT_INC] +CategoryId = Cabin_pressurization +SimEventName = "KEY_PRESSURIZATION_PRESSURE_ALT_INC" +Name = "Increases the altitude that the cabin is pressurized to" +Description = "Increases the altitude that the cabin is pressurized to." +System = "Shared Cockpit" + +[KEY_PRESSURIZATION_PRESSURE_ALT_DEC] +CategoryId = Cabin_pressurization +SimEventName = "KEY_PRESSURIZATION_PRESSURE_ALT_DEC" +Name = "Decreases the altitude that the cabin is pressurized to" +Description = "Decreases the altitude that the cabin is pressurized to." +System = "Shared Cockpit" + +[PRESSURIZATION_CLIMB_RATE_INC] +CategoryId = Cabin_pressurization +SimEventName = "PRESSURIZATION_CLIMB_RATE_INC" +Name = "Sets the rate at which cabin pressurization is increased" +Description = "Sets the rate at which cabin pressurization is increased." +System = "Shared Cockpit" + +[PRESSURIZATION_CLIMB_RATE_DEC] +CategoryId = Cabin_pressurization +SimEventName = "PRESSURIZATION_CLIMB_RATE_DEC" +Name = "Sets the rate at which cabin pressurization is decreased" +Description = "Sets the rate at which cabin pressurization is decreased." +System = "Shared Cockpit" + +[PRESSURIZATION_PRESSURE_DUMP_SWTICH] +CategoryId = Cabin_pressurization +SimEventName = "PRESSURIZATION_PRESSURE_DUMP_SWTICH" +Name = "Sets the cabin pressure to the outside air pressure" +Description = "Sets the cabin pressure to the outside air pressure." +System = "Shared Cockpit" + + + +# Catapult_Launches ####################### +# +[category_Catapult_Launches] +Name = "Catapult Launches" + + +[TAKEOFF_ASSIST_ARM_TOGGLE] +CategoryId = Catapult_Launches +SimEventName = "TAKEOFF_ASSIST_ARM_TOGGLE" +Name = "Deploy or remove the assist arm" +Description = "Deploy or remove the assist arm. Refer to the document Notes on Aircraft Systems." +System = "Shared Cockpit" + +[TAKEOFF_ASSIST_ARM_SET] +CategoryId = Catapult_Launches +SimEventName = "TAKEOFF_ASSIST_ARM_SET" +Name = "Value:" +Description = "Value:\n TRUE request set\n FALSE request unset" +System = "Shared Cockpit" + +[TAKEOFF_ASSIST_FIRE] +CategoryId = Catapult_Launches +SimEventName = "TAKEOFF_ASSIST_FIRE" +Name = "If everything is set up correctly" +Description = "If everything is set up correctly. Launch from the catapult." +System = "Shared Cockpit" + +[TOGGLE_LAUNCH_BAR_SWITCH] +CategoryId = Catapult_Launches +SimEventName = "TOGGLE_LAUNCH_BAR_SWITCH" +Name = "Toggle the request for the launch bar to be installed or removed" +Description = "Toggle the request for the launch bar to be installed or removed." +System = "Shared Cockpit" + +[SET_LAUNCH_BAR_SWITCH] +CategoryId = Catapult_Launches +SimEventName = "SET_LAUNCH_BAR_SWITCH" +Name = "Set Launch Bar switch (0/1)" +Description = "Value:\n TRUE request set\n FALSE request unset" +System = "Shared Cockpit" + + + +# Helicopter_Specific_Systems ####################### +# +[category_Helicopter_Specific_Systems] +Name = "Helicopter Systems" + + +[ROTOR_BRAKE] +CategoryId = Helicopter_Specific_Systems +SimEventName = "ROTOR_BRAKE" +Name = "Triggers rotor braking input" +Description = " Triggers rotor braking input" +System = "Shared Cockpit" + +[ROTOR_CLUTCH_SWITCH_TOGGLE] +CategoryId = Helicopter_Specific_Systems +SimEventName = "ROTOR_CLUTCH_SWITCH_TOGGLE" +Name = "Toggles on electric rotor clutch switch" +Description = "Toggles on electric rotor clutch switch" +System = "Shared Cockpit" + +[ROTOR_CLUTCH_SWITCH_SET] +CategoryId = Helicopter_Specific_Systems +SimEventName = "ROTOR_CLUTCH_SWITCH_SET" +Name = "Sets electric rotor clutch switch on/off (1,0)" +Description = "Sets electric rotor clutch switch on/off (1,0)" +System = "Shared Cockpit" + +[ROTOR_GOV_SWITCH_TOGGLE] +CategoryId = Helicopter_Specific_Systems +SimEventName = "ROTOR_GOV_SWITCH_TOGGLE" +Name = "Toggles the electric rotor governor switch" +Description = "Toggles the electric rotor governor switch" +System = "Shared Cockpit" + +[ROTOR_GOV_SWITCH_SET] +CategoryId = Helicopter_Specific_Systems +SimEventName = "ROTOR_GOV_SWITCH_SET" +Name = "Sets the electric rotor governor switch on/off (1,0)" +Description = "Sets the electric rotor governor switch on/off (1,0)" +System = "Shared Cockpit" + +[ROTOR_LATERAL_TRIM_INC] +CategoryId = Helicopter_Specific_Systems +SimEventName = "ROTOR_LATERAL_TRIM_INC" +Name = "Increments the lateral (right), rotor trim" +Description = "Increments the lateral (right), rotor trim" +System = "Shared Cockpit" + +[ROTOR_LATERAL_TRIM_DEC] +CategoryId = Helicopter_Specific_Systems +SimEventName = "ROTOR_LATERAL_TRIM_DEC" +Name = "Decrements the lateral (right), rotor trim" +Description = "Decrements the lateral (right), rotor trim" +System = "Shared Cockpit" + +[ROTOR_LATERAL_TRIM_SET] +CategoryId = Helicopter_Specific_Systems +SimEventName = "ROTOR_LATERAL_TRIM_SET" +Name = "Sets the lateral (right), rotor trim (0 to 16384)" +Description = "Sets the lateral (right), rotor trim (0 to 16384)" +System = "Shared Cockpit" + + + +# Slings_and_Hoists ####################### +# +[category_Slings_and_Hoists] +Name = "Slings and Hoists" + + +[SLING_PICKUP_RELEASE] +CategoryId = Slings_and_Hoists +SimEventName = "SLING_PICKUP_RELEASE" +Name = "Toggle between pickup and release mode" +Description = "Toggle between pickup and release mode. Hold mode is automatic and cannot be selected. Refer to the document Notes on Aircraft Systems." +System = "Shared Cockpit" + +[HOIST_SWITCH_EXTEND] +CategoryId = Slings_and_Hoists +SimEventName = "HOIST_SWITCH_EXTEND" +Name = "Hoist swtich extend" +Description = "The rate at which a hoist cable extends is set in the Aircraft Configuration File." +System = "Shared Cockpit" + +[HOIST_SWITCH_RETRACT] +CategoryId = Slings_and_Hoists +SimEventName = "HOIST_SWITCH_RETRACT" +Name = "Hoist swtich retract" +Description = "The rate at which a hoist cable retracts is set in the Aircraft Configuration File." +System = "Shared Cockpit" + +[HOIST_SWITCH_SET] +CategoryId = Slings_and_Hoists +SimEventName = "HOIST_SWITCH_SET" +Name = "Hoist swtich set: (<0 = up, 0 = off, >0 = down)" +Description = "The data value should be set to one of:\n <0 up\n =0 off\n >0 down" +System = "Shared Cockpit" + +[HOIST_DEPLOY_TOGGLE] +CategoryId = Slings_and_Hoists +SimEventName = "HOIST_DEPLOY_TOGGLE" +Name = "Toggles the hoist arm switch, extend or retract" +Description = "Toggles the hoist arm switch, extend or retract." +System = "Shared Cockpit" + +[HOIST_DEPLOY_SET] +CategoryId = Slings_and_Hoists +SimEventName = "HOIST_DEPLOY_SET" +Name = "Hoist arm deploy set (0=retract, 1=extend)" +Description = "The data value should be set to:\n 0 - set hoist switch to retract the arm\n 1 - set hoist switch to extend the arm" +System = "Shared Cockpit" + + + +# Slew_System ####################### +# +[category_Slew_System] +Name = "Slew System" + + +[SLEW_TOGGLE] +CategoryId = Slew_System +SimEventName = "SLEW_TOGGLE" +Name = "Toggles slew on/off" +Description = "Toggles slew on/off" +System = "Shared Cockpit (Pilot only)," + +[SLEW_OFF] +CategoryId = Slew_System +SimEventName = "SLEW_OFF" +Name = "Turns slew off" +Description = "Turns slew off" +System = "Shared Cockpit (Pilot only)," + +[SLEW_ON] +CategoryId = Slew_System +SimEventName = "SLEW_ON" +Name = "Turns slew on" +Description = "Turns slew on" +System = "Shared Cockpit (Pilot only)," + +[SLEW_SET] +CategoryId = Slew_System +SimEventName = "SLEW_SET" +Name = "Sets slew on/off (1,0)" +Description = "Sets slew on/off (1,0)" +System = "Shared Cockpit (Pilot only)" + +[SLEW_RESET] +CategoryId = Slew_System +SimEventName = "SLEW_RESET" +Name = "Stop slew and reset pitch, bank, and heading all to zero" +Description = "Stop slew and reset pitch, bank, and heading all to zero." +System = "Shared Cockpit (Pilot only)," + +[SLEW_ALTIT_UP_FAST] +CategoryId = Slew_System +SimEventName = "SLEW_ALTIT_UP_FAST" +Name = "Slew upward fast" +Description = "Slew upward fast" +System = "Shared Cockpit (Pilot only)," + +[SLEW_ALTIT_UP_SLOW] +CategoryId = Slew_System +SimEventName = "SLEW_ALTIT_UP_SLOW" +Name = "Slew upward slow" +Description = "Slew upward slow" +System = "Shared Cockpit (Pilot only)," + +[SLEW_ALTIT_FREEZE] +CategoryId = Slew_System +SimEventName = "SLEW_ALTIT_FREEZE" +Name = "Stop vertical slew" +Description = "Stop vertical slew" +System = "Shared Cockpit (Pilot only)," + +[SLEW_ALTIT_DN_SLOW] +CategoryId = Slew_System +SimEventName = "SLEW_ALTIT_DN_SLOW" +Name = "Slew downward slow" +Description = "Slew downward slow" +System = "Shared Cockpit (Pilot only)," + +[SLEW_ALTIT_DN_FAST] +CategoryId = Slew_System +SimEventName = "SLEW_ALTIT_DN_FAST" +Name = "Slew downward fast" +Description = "Slew downward fast" +System = "Shared Cockpit (Pilot only)," + +[SLEW_ALTIT_PLUS] +CategoryId = Slew_System +SimEventName = "SLEW_ALTIT_PLUS" +Name = "Increase upward slew" +Description = "Increase upward slew" +System = "Shared Cockpit (Pilot only)," + +[SLEW_ALTIT_MINUS] +CategoryId = Slew_System +SimEventName = "SLEW_ALTIT_MINUS" +Name = "Decrease upward slew" +Description = "Decrease upward slew " +System = "Shared Cockpit (Pilot only)," + +[SLEW_PITCH_DN_FAST] +CategoryId = Slew_System +SimEventName = "SLEW_PITCH_DN_FAST" +Name = "Slew pitch downward fast" +Description = "Slew pitch downward fast" +System = "Shared Cockpit (Pilot only)," + +[SLEW_PITCH_DN_SLOW] +CategoryId = Slew_System +SimEventName = "SLEW_PITCH_DN_SLOW" +Name = "Slew pitch downward slow" +Description = "Slew pitch downward slow" +System = "Shared Cockpit (Pilot only)," + +[SLEW_PITCH_FREEZE] +CategoryId = Slew_System +SimEventName = "SLEW_PITCH_FREEZE" +Name = "Stop pitch slew" +Description = "Stop pitch slew" +System = "Shared Cockpit (Pilot only)," + +[SLEW_PITCH_UP_SLOW] +CategoryId = Slew_System +SimEventName = "SLEW_PITCH_UP_SLOW" +Name = "Slew pitch up slow" +Description = "Slew pitch up slow" +System = "Shared Cockpit (Pilot only)," + +[SLEW_PITCH_UP_FAST] +CategoryId = Slew_System +SimEventName = "SLEW_PITCH_UP_FAST" +Name = "Slew pitch upward fast" +Description = "Slew pitch upward fast" +System = "Shared Cockpit (Pilot only)," + +[SLEW_PITCH_PLUS] +CategoryId = Slew_System +SimEventName = "SLEW_PITCH_PLUS" +Name = "Increase pitch up slew" +Description = "Increase pitch up slew" +System = "Shared Cockpit (Pilot only)," + +[SLEW_PITCH_MINUS] +CategoryId = Slew_System +SimEventName = "SLEW_PITCH_MINUS" +Name = "Decrease pitch up slew" +Description = "Decrease pitch up slew" +System = "Shared Cockpit (Pilot only)," + +[SLEW_BANK_MINUS] +CategoryId = Slew_System +SimEventName = "SLEW_BANK_MINUS" +Name = "Increase left bank slew" +Description = "Increase left bank slew" +System = "Shared Cockpit (Pilot only)," + +[SLEW_AHEAD_PLUS] +CategoryId = Slew_System +SimEventName = "SLEW_AHEAD_PLUS" +Name = "Increase forward slew" +Description = "Increase forward slew" +System = "Shared Cockpit (Pilot only)," + +[SLEW_BANK_PLUS] +CategoryId = Slew_System +SimEventName = "SLEW_BANK_PLUS" +Name = "Increase right bank slew" +Description = "Increase right bank slew" +System = "Shared Cockpit (Pilot only)," + +[SLEW_LEFT] +CategoryId = Slew_System +SimEventName = "SLEW_LEFT" +Name = "Slew to the left" +Description = "Slew to the left" +System = "Shared Cockpit (Pilot only)," + +[SLEW_FREEZE] +CategoryId = Slew_System +SimEventName = "SLEW_FREEZE" +Name = "Stop all slew" +Description = "Stop all slew" +System = "Shared Cockpit (Pilot only)," + +[SLEW_RIGHT] +CategoryId = Slew_System +SimEventName = "SLEW_RIGHT" +Name = "Slew to the right" +Description = "Slew to the right" +System = "Shared Cockpit (Pilot only)," + +[SLEW_HEADING_MINUS] +CategoryId = Slew_System +SimEventName = "SLEW_HEADING_MINUS" +Name = "Increase slew heading to the left" +Description = "Increase slew heading to the left" +System = "Shared Cockpit (Pilot only)," + +[SLEW_AHEAD_MINUS] +CategoryId = Slew_System +SimEventName = "SLEW_AHEAD_MINUS" +Name = "Decrease forward slew" +Description = "Decrease forward slew" +System = "Shared Cockpit (Pilot only)," + +[SLEW_HEADING_PLUS] +CategoryId = Slew_System +SimEventName = "SLEW_HEADING_PLUS" +Name = "Increase slew heading to the right" +Description = "Increase slew heading to the right" +System = "Shared Cockpit (Pilot only)," + +[AXIS_SLEW_AHEAD_SET] +CategoryId = Slew_System +SimEventName = "AXIS_SLEW_AHEAD_SET" +Name = "Sets forward slew (+/- 16384)" +Description = "Sets forward slew (+/- 16384)" +System = "Shared Cockpit (Pilot only)" + +[AXIS_SLEW_SIDEWAYS_SET] +CategoryId = Slew_System +SimEventName = "AXIS_SLEW_SIDEWAYS_SET" +Name = "Sets sideways slew (+/- 16384)" +Description = "Sets sideways slew (+/- 16384)" +System = "Shared Cockpit (Pilot only)" + +[AXIS_SLEW_HEADING_SET] +CategoryId = Slew_System +SimEventName = "AXIS_SLEW_HEADING_SET" +Name = "Sets heading slew (+/- 16384)" +Description = "Sets heading slew (+/- 16384)" +System = "Shared Cockpit (Pilot only)" + +[AXIS_SLEW_ALT_SET] +CategoryId = Slew_System +SimEventName = "AXIS_SLEW_ALT_SET" +Name = "Sets vertical slew (+/- 16384)" +Description = "Sets vertical slew (+/- 16384)" +System = "Shared Cockpit (Pilot only)" + +[AXIS_SLEW_BANK_SET] +CategoryId = Slew_System +SimEventName = "AXIS_SLEW_BANK_SET" +Name = "Sets roll slew (+/- 16384)" +Description = "Sets roll slew (+/- 16384)" +System = "Shared Cockpit (Pilot only)" + +[AXIS_SLEW_PITCH_SET] +CategoryId = Slew_System +SimEventName = "AXIS_SLEW_PITCH_SET" +Name = "Sets pitch slew (+/- 16384)" +Description = "Sets pitch slew (+/- 16384)" +System = "Shared Cockpit (Pilot only)" + + + +# View_System ####################### +# +[category_View_System] +Name = "View System" + + +[VIEW_MODE] +CategoryId = View_System +SimEventName = "VIEW_MODE" +Name = "Selects next view" +Description = "Selects next view" +System = "Shared Cockpit" + +[VIEW_WINDOW_TO_FRONT] +CategoryId = View_System +SimEventName = "VIEW_WINDOW_TO_FRONT" +Name = "Sets active window to front" +Description = "Sets active window to front" +System = "Shared Cockpit" + +[VIEW_RESET] +CategoryId = View_System +SimEventName = "VIEW_RESET" +Name = "Resets the view to the default" +Description = "Resets the view to the default" +System = "Shared Cockpit" + +[VIEW_ALWAYS_PAN_UP] +CategoryId = View_System +SimEventName = "VIEW_ALWAYS_PAN_UP" +Name = "" +Description = " " +System = "Shared Cockpit" + +[VIEW_ALWAYS_PAN_DOWN] +CategoryId = View_System +SimEventName = "VIEW_ALWAYS_PAN_DOWN" +Name = "" +Description = " " +System = "Shared Cockpit" + +[NEXT_SUB_VIEW] +CategoryId = View_System +SimEventName = "NEXT_SUB_VIEW" +Name = "" +Description = " " +System = "Shared Cockpit" + +[PREV_SUB_VIEW] +CategoryId = View_System +SimEventName = "PREV_SUB_VIEW" +Name = "" +Description = " " +System = "Shared Cockpit" + +[VIEW_TRACK_PAN_TOGGLE] +CategoryId = View_System +SimEventName = "VIEW_TRACK_PAN_TOGGLE" +Name = "" +Description = " " +System = "Shared Cockpit" + +[VIEW_PREVIOUS_TOGGLE] +CategoryId = View_System +SimEventName = "VIEW_PREVIOUS_TOGGLE" +Name = "" +Description = " " +System = "Shared Cockpit" + +[VIEW_CAMERA_SELECT_START] +CategoryId = View_System +SimEventName = "VIEW_CAMERA_SELECT_START" +Name = "" +Description = " " +System = "Shared Cockpit" + +[PANEL_HUD_NEXT] +CategoryId = View_System +SimEventName = "PANEL_HUD_NEXT" +Name = "" +Description = " " +System = "Shared Cockpit" + +[PANEL_HUD_PREVIOUS] +CategoryId = View_System +SimEventName = "PANEL_HUD_PREVIOUS" +Name = "" +Description = " " +System = "Shared Cockpit" + +[ZOOM_IN] +CategoryId = View_System +SimEventName = "ZOOM_IN" +Name = "Zooms view in" +Description = "Zooms view in" +System = "Shared Cockpit" + +[ZOOM_OUT] +CategoryId = View_System +SimEventName = "ZOOM_OUT" +Name = "Zooms view out" +Description = "Zooms view out" +System = "Shared Cockpit" + +[MAP_ZOOM_FINE_IN] +CategoryId = View_System +SimEventName = "MAP_ZOOM_FINE_IN" +Name = "Fine zoom in map view" +Description = "Fine zoom in map view" +System = "Shared Cockpit" + +[PAN_LEFT] +CategoryId = View_System +SimEventName = "PAN_LEFT" +Name = "Pans view left" +Description = "Pans view left" +System = "Shared Cockpit" + +[PAN_RIGHT] +CategoryId = View_System +SimEventName = "PAN_RIGHT" +Name = "Pans view right" +Description = "Pans view right" +System = "Shared Cockpit" + +[MAP_ZOOM_FINE_OUT] +CategoryId = View_System +SimEventName = "MAP_ZOOM_FINE_OUT" +Name = "Fine zoom out in map view" +Description = "Fine zoom out in map view" +System = "Shared Cockpit" + +[VIEW_FORWARD] +CategoryId = View_System +SimEventName = "VIEW_FORWARD" +Name = "Sets view direction forward" +Description = "Sets view direction forward" +System = "Shared Cockpit" + +[VIEW_FORWARD_RIGHT] +CategoryId = View_System +SimEventName = "VIEW_FORWARD_RIGHT" +Name = "Sets view direction forward and right" +Description = "Sets view direction forward and right" +System = "Shared Cockpit" + +[VIEW_RIGHT] +CategoryId = View_System +SimEventName = "VIEW_RIGHT" +Name = "Sets view direction to the right" +Description = "Sets view direction to the right" +System = "Shared Cockpit" + +[VIEW_REAR_RIGHT] +CategoryId = View_System +SimEventName = "VIEW_REAR_RIGHT" +Name = "Sets view direction to the rear and right" +Description = "Sets view direction to the rear and right" +System = "Shared Cockpit" + +[VIEW_REAR] +CategoryId = View_System +SimEventName = "VIEW_REAR" +Name = "Sets view direction to the rear" +Description = "Sets view direction to the rear" +System = "Shared Cockpit" + +[VIEW_REAR_LEFT] +CategoryId = View_System +SimEventName = "VIEW_REAR_LEFT" +Name = "Sets view direction to the rear and left" +Description = "Sets view direction to the rear and left" +System = "Shared Cockpit" + +[VIEW_LEFT] +CategoryId = View_System +SimEventName = "VIEW_LEFT" +Name = "Sets view direction to the left" +Description = "Sets view direction to the left" +System = "Shared Cockpit" + +[VIEW_FORWARD_LEFT] +CategoryId = View_System +SimEventName = "VIEW_FORWARD_LEFT" +Name = "Sets view direction forward and left" +Description = "Sets view direction forward and left" +System = "Shared Cockpit" + +[VIEW_DOWN] +CategoryId = View_System +SimEventName = "VIEW_DOWN" +Name = "Sets view direction down" +Description = "Sets view direction down" +System = "Shared Cockpit" + +[ZOOM_MINUS] +CategoryId = View_System +SimEventName = "ZOOM_MINUS" +Name = "Decreases zoom" +Description = "Decreases zoom" +System = "Shared Cockpit" + +[ZOOM_PLUS] +CategoryId = View_System +SimEventName = "ZOOM_PLUS" +Name = "Increase zoom" +Description = "Increase zoom" +System = "Shared Cockpit" + +[PAN_UP] +CategoryId = View_System +SimEventName = "PAN_UP" +Name = "Pan view up" +Description = "Pan view up" +System = "Shared Cockpit" + +[PAN_DOWN] +CategoryId = View_System +SimEventName = "PAN_DOWN" +Name = "Pan view down" +Description = "Pan view down" +System = "Shared Cockpit" + +[VIEW_MODE_REV] +CategoryId = View_System +SimEventName = "VIEW_MODE_REV" +Name = "Reverse view cycle" +Description = "Reverse view cycle" +System = "Shared Cockpit" + +[ZOOM_IN_FINE] +CategoryId = View_System +SimEventName = "ZOOM_IN_FINE" +Name = "Zoom in fine" +Description = "Zoom in fine" +System = "Shared Cockpit" + +[ZOOM_OUT_FINE] +CategoryId = View_System +SimEventName = "ZOOM_OUT_FINE" +Name = "Zoom out fine" +Description = "Zoom out fine" +System = "Shared Cockpit" + +[CLOSE_VIEW] +CategoryId = View_System +SimEventName = "CLOSE_VIEW" +Name = "Close current view" +Description = "Close current view" +System = "Shared Cockpit" + +[NEW_VIEW] +CategoryId = View_System +SimEventName = "NEW_VIEW" +Name = "Open new view" +Description = "Open new view" +System = "Shared Cockpit" + +[NEXT_VIEW] +CategoryId = View_System +SimEventName = "NEXT_VIEW" +Name = "Select next view" +Description = "Select next view" +System = "Shared Cockpit" + +[PREV_VIEW] +CategoryId = View_System +SimEventName = "PREV_VIEW" +Name = "Select previous view" +Description = "Select previous view" +System = "Shared Cockpit" + +[PAN_LEFT_UP] +CategoryId = View_System +SimEventName = "PAN_LEFT_UP" +Name = "Pan view left" +Description = "Pan view left" +System = "Shared Cockpit" + +[PAN_LEFT_DOWN] +CategoryId = View_System +SimEventName = "PAN_LEFT_DOWN" +Name = "Pan view left and down" +Description = "Pan view left and down" +System = "Shared Cockpit" + +[PAN_RIGHT_UP] +CategoryId = View_System +SimEventName = "PAN_RIGHT_UP" +Name = "Pan view right and up" +Description = "Pan view right and up" +System = "Shared Cockpit" + +[PAN_RIGHT_DOWN] +CategoryId = View_System +SimEventName = "PAN_RIGHT_DOWN" +Name = "Pan view right and down" +Description = "Pan view right and down" +System = "Shared Cockpit" + +[PAN_TILT_LEFT] +CategoryId = View_System +SimEventName = "PAN_TILT_LEFT" +Name = "Tilt view left" +Description = "Tilt view left" +System = "Shared Cockpit" + +[PAN_TILT_RIGHT] +CategoryId = View_System +SimEventName = "PAN_TILT_RIGHT" +Name = "Tilt view right" +Description = "Tilt view right" +System = "Shared Cockpit" + +[PAN_RESET] +CategoryId = View_System +SimEventName = "PAN_RESET" +Name = "Reset view to forward" +Description = "Reset view to forward" +System = "Shared Cockpit" + +[VIEW_FORWARD_UP] +CategoryId = View_System +SimEventName = "VIEW_FORWARD_UP" +Name = "Sets view forward and up" +Description = "Sets view forward and up" +System = "Shared Cockpit" + +[VIEW_FORWARD_RIGHT_UP] +CategoryId = View_System +SimEventName = "VIEW_FORWARD_RIGHT_UP" +Name = "Sets view forward, right, and up" +Description = "Sets view forward, right, and up" +System = "Shared Cockpit" + +[VIEW_RIGHT_UP] +CategoryId = View_System +SimEventName = "VIEW_RIGHT_UP" +Name = "Sets view right and up" +Description = "Sets view right and up" +System = "Shared Cockpit" + +[VIEW_REAR_RIGHT_UP] +CategoryId = View_System +SimEventName = "VIEW_REAR_RIGHT_UP" +Name = "Sets view rear, right, and up" +Description = "Sets view rear, right, and up" +System = "Shared Cockpit" + +[VIEW_REAR_UP] +CategoryId = View_System +SimEventName = "VIEW_REAR_UP" +Name = "Sets view rear and up" +Description = "Sets view rear and up" +System = "Shared Cockpit" + +[VIEW_REAR_LEFT_UP] +CategoryId = View_System +SimEventName = "VIEW_REAR_LEFT_UP" +Name = "Sets view rear left and up" +Description = "Sets view rear left and up" +System = "Shared Cockpit" + +[VIEW_LEFT_UP] +CategoryId = View_System +SimEventName = "VIEW_LEFT_UP" +Name = "Sets view left and up" +Description = "Sets view left and up" +System = "Shared Cockpit" + +[VIEW_FORWARD_LEFT_UP] +CategoryId = View_System +SimEventName = "VIEW_FORWARD_LEFT_UP" +Name = "Sets view forward left and up" +Description = "Sets view forward left and up" +System = "Shared Cockpit" + +[VIEW_UP] +CategoryId = View_System +SimEventName = "VIEW_UP" +Name = "Sets view up" +Description = "Sets view up" +System = "Shared Cockpit" + +; duplicate of above +; [VIEW_RESET] +; CategoryId = View_System +; SimEventName = "VIEW_RESET" +; Name = "Reset view forward" +; Description = "Reset view forward" +; System = "Shared Cockpit" + +[PAN_RESET_COCKPIT] +CategoryId = View_System +SimEventName = "PAN_RESET_COCKPIT" +Name = "Reset panning to forward, if in cockpit view" +Description = "Reset panning to forward, if in cockpit view" +System = "Shared Cockpit" + +[KEY_CHASE_VIEW_NEXT] +CategoryId = View_System +SimEventName = "KEY_CHASE_VIEW_NEXT" +Name = "Cycle view to next target" +Description = "Cycle view to next target" +System = "Shared Cockpit" + +[KEY_CHASE_VIEW_PREV] +CategoryId = View_System +SimEventName = "KEY_CHASE_VIEW_PREV" +Name = "Cycle view to previous target" +Description = "Cycle view to previous target" +System = "Shared Cockpit" + +[CHASE_VIEW_TOGGLE] +CategoryId = View_System +SimEventName = "CHASE_VIEW_TOGGLE" +Name = "Toggles chase view on/off" +Description = "Toggles chase view on/off" +System = "Shared Cockpit" + +[EYEPOINT_UP] +CategoryId = View_System +SimEventName = "EYEPOINT_UP" +Name = "Move eyepoint up" +Description = "Move eyepoint up" +System = "Shared Cockpit" + +[EYEPOINT_DOWN] +CategoryId = View_System +SimEventName = "EYEPOINT_DOWN" +Name = "Move eyepoint down" +Description = "Move eyepoint down" +System = "Shared Cockpit" + +[EYEPOINT_RIGHT] +CategoryId = View_System +SimEventName = "EYEPOINT_RIGHT" +Name = "Move eyepoint right" +Description = "Move eyepoint right" +System = "Shared Cockpit" + +[EYEPOINT_LEFT] +CategoryId = View_System +SimEventName = "EYEPOINT_LEFT" +Name = "Move eyepoint left" +Description = "Move eyepoint left" +System = "Shared Cockpit" + +[EYEPOINT_FORWARD] +CategoryId = View_System +SimEventName = "EYEPOINT_FORWARD" +Name = "Move eyepoint forward" +Description = "Move eyepoint forward" +System = "Shared Cockpit" + +[EYEPOINT_BACK] +CategoryId = View_System +SimEventName = "EYEPOINT_BACK" +Name = "Move eyepoint backward" +Description = "Move eyepoint backward" +System = "Shared Cockpit" + +[EYEPOINT_RESET] +CategoryId = View_System +SimEventName = "EYEPOINT_RESET" +Name = "Move eyepoint to default position" +Description = "Move eyepoint to default position" +System = "Shared Cockpit" + +[NEW_MAP] +CategoryId = View_System +SimEventName = "NEW_MAP" +Name = "Opens new map view" +Description = "Opens new map view" +System = "Shared Cockpit" + +[VIEW_COCKPIT_FORWARD] +CategoryId = View_System +SimEventName = "VIEW_COCKPIT_FORWARD" +Name = "Switch immediately to the forward view, in 2D mode" +Description = "Switch immediately to the forward view, in 2D mode." +System = "Shared Cockpit" + +[VIEW_VIRTUAL_COCKPIT_FORWARD] +CategoryId = View_System +SimEventName = "VIEW_VIRTUAL_COCKPIT_FORWARD" +Name = "Switch immediately to the forward view, in virtual cockpit mode" +Description = "Switch immediately to the forward view, in virtual cockpit mode." +System = "Shared Cockpit" + +[VIEW_PANEL_ALPHA_SET] +CategoryId = View_System +SimEventName = "VIEW_PANEL_ALPHA_SET" +Name = "Sets the alpha-blending value for the panel" +Description = "Sets the alpha-blending value for the panel. Takes a parameter in the range 0 to 255. The alpha-blending can be changed from the keyboard using Ctrl-Shift-T, and the plus and minus keys." +System = "Shared Cockpit" + +[VIEW_PANEL_ALPHA_SELECT] +CategoryId = View_System +SimEventName = "VIEW_PANEL_ALPHA_SELECT" +Name = "Select the mode to change the alpha-blending for +/-" +Description = "Sets the mode to change the alpha-blending, so the keys KEY_PLUS and KEY_MINUS increment and decrement the value." +System = "Shared Cockpit" + +[VIEW_PANEL_ALPHA_INC] +CategoryId = View_System +SimEventName = "VIEW_PANEL_ALPHA_INC" +Name = "Increment alpha-blending for the panel" +Description = "Increment alpha-blending for the panel." +System = "Shared Cockpit" + +[VIEW_PANEL_ALPHA_DEC] +CategoryId = View_System +SimEventName = "VIEW_PANEL_ALPHA_DEC" +Name = "Decrement alpha-blending for the panel" +Description = "Decrement alpha-blending for the panel." +System = "Shared Cockpit" + +[VIEW_LINKING_SET] +CategoryId = View_System +SimEventName = "VIEW_LINKING_SET" +Name = "Links all the views from one camera together" +Description = "Links all the views from one camera together, so that panning the view will change the view of all the linked cameras." +System = "Shared Cockpit" + +[VIEW_LINKING_TOGGLE] +CategoryId = View_System +SimEventName = "VIEW_LINKING_TOGGLE" +Name = "Turns view linking on or off" +Description = "Turns view linking on or off." +System = "Shared Cockpit" + +[VIEW_CHASE_DISTANCE_ADD] +CategoryId = View_System +SimEventName = "VIEW_CHASE_DISTANCE_ADD" +Name = "Increments the distance of the view camera from the chase object " +Description = "Increments the distance of the view camera from the chase object (such as in Spot Plane view, or viewing an AI controlled aircraft)." +System = "Shared Cockpit" + +[VIEW_CHASE_DISTANCE_SUB] +CategoryId = View_System +SimEventName = "VIEW_CHASE_DISTANCE_SUB" +Name = "Decrements the distance of the view camera from the chase object" +Description = "Decrements the distance of the view camera from the chase object." +System = "Shared Cockpit" + + + +# Miscellaneous_Events ####################### +# +[category_Miscellaneous_Events] +Name = "Miscellaneous Events" + + +[PAUSE_TOGGLE] +CategoryId = Miscellaneous_Events +SimEventName = "PAUSE_TOGGLE" +Name = "Toggles pause on/off" +Description = "Toggles pause on/off" +System = "Disabled" + +[PAUSE_ON] +CategoryId = Miscellaneous_Events +SimEventName = "PAUSE_ON" +Name = "Turns pause on" +Description = "Turns pause on" +System = "Disabled" + +[PAUSE_OFF] +CategoryId = Miscellaneous_Events +SimEventName = "PAUSE_OFF" +Name = "Turns pause off" +Description = "Turns pause off" +System = "Disabled" + +[PAUSE_SET] +CategoryId = Miscellaneous_Events +SimEventName = "PAUSE_SET" +Name = "Sets pause on/off (1,0)" +Description = "Sets pause on/off (1,0)" +System = "Disabled" + +[DEMO_STOP] +CategoryId = Miscellaneous_Events +SimEventName = "DEMO_STOP" +Name = "Stops demo system playback" +Description = "Stops demo system playback" +System = "Shared Cockpit" + +[SELECT_1] +CategoryId = Miscellaneous_Events +SimEventName = "SELECT_1" +Name = "Sets "selected" index (for other events), to 1" +Description = "Sets "selected" index (for other events), to 1" +System = "Shared Cockpit" + +[SELECT_2] +CategoryId = Miscellaneous_Events +SimEventName = "SELECT_2" +Name = "Sets "selected" index (for other events), to 2" +Description = "Sets "selected" index (for other events), to 2" +System = "Shared Cockpit" + +[SELECT_3] +CategoryId = Miscellaneous_Events +SimEventName = "SELECT_3" +Name = "Sets "selected" index (for other events), to 3" +Description = "Sets "selected" index (for other events), to 3" +System = "Shared Cockpit" + +[SELECT_4] +CategoryId = Miscellaneous_Events +SimEventName = "SELECT_4" +Name = "Sets "selected" index (for other events), to 4" +Description = "Sets "selected" index (for other events), to 4" +System = "Shared Cockpit" + +[MINUS] +CategoryId = Miscellaneous_Events +SimEventName = "MINUS" +Name = "Used in conjunction with "selected" parameters to decrease their value" +Description = "Used in conjunction with "selected" parameters to decrease their value (e.g., radio frequency)" +System = "Shared Cockpit" + +[PLUS] +CategoryId = Miscellaneous_Events +SimEventName = "PLUS" +Name = "Used in conjunction with "selected" parameters to increase their value" +Description = "Used in conjunction with "selected" parameters to increase their value (e.g., radio frequency)" +System = "Shared Cockpit" + +[ZOOM_1X] +CategoryId = Miscellaneous_Events +SimEventName = "ZOOM_1X" +Name = "Sets zoom level to 1" +Description = "Sets zoom level to 1" +System = "Shared Cockpit" + +[SOUND_TOGGLE] +CategoryId = Miscellaneous_Events +SimEventName = "SOUND_TOGGLE" +Name = "Toggles sound on/off" +Description = "Toggles sound on/off" +System = "Shared Cockpit" + +[SIM_RATE] +CategoryId = Miscellaneous_Events +SimEventName = "SIM_RATE" +Name = "Selects simulation rate for +/-" +Description = "Selects simulation rate (use KEY_MINUS, KEY_PLUS to change)" +System = "Shared Cockpit" + +[JOYSTICK_CALIBRATE] +CategoryId = Miscellaneous_Events +SimEventName = "JOYSTICK_CALIBRATE" +Name = "Toggles joystick on/off" +Description = "Toggles joystick on/off" +System = "Shared Cockpit" + +[SITUATION_SAVE] +CategoryId = Miscellaneous_Events +SimEventName = "SITUATION_SAVE" +Name = "Saves flight situation" +Description = "Saves flight situation" +System = "Shared Cockpit" + +[SITUATION_RESET] +CategoryId = Miscellaneous_Events +SimEventName = "SITUATION_RESET" +Name = "Resets flight situation" +Description = "Resets flight situation" +System = "Shared Cockpit" + +[SOUND_SET] +CategoryId = Miscellaneous_Events +SimEventName = "SOUND_SET" +Name = "Sets sound on/off (1,0)" +Description = "Sets sound on/off (1,0)" +System = "Shared Cockpit" + +[EXIT] +CategoryId = Miscellaneous_Events +SimEventName = "EXIT" +Name = "Quit ESP with a message" +Description = "Quit ESP with a message" +System = "Shared Cockpit" + +[ABORT] +CategoryId = Miscellaneous_Events +SimEventName = "ABORT" +Name = "Quit ESP without a message" +Description = "Quit ESP without a message" +System = "Shared Cockpit" + +[READOUTS_SLEW] +CategoryId = Miscellaneous_Events +SimEventName = "READOUTS_SLEW" +Name = "Cycle through information readouts while in slew" +Description = "Cycle through information readouts while in slew" +System = "Shared Cockpit" + +[READOUTS_FLIGHT] +CategoryId = Miscellaneous_Events +SimEventName = "READOUTS_FLIGHT" +Name = "Cycle through information readouts" +Description = "Cycle through information readouts" +System = "Shared Cockpit" + +[MINUS_SHIFT] +CategoryId = Miscellaneous_Events +SimEventName = "MINUS_SHIFT" +Name = "Used with other events" +Description = "Used with other events" +System = "Shared Cockpit" + +[PLUS_SHIFT] +CategoryId = Miscellaneous_Events +SimEventName = "PLUS_SHIFT" +Name = "Used with other events" +Description = "Used with other events" +System = "Shared Cockpit" + +[SIM_RATE_INCR] +CategoryId = Miscellaneous_Events +SimEventName = "SIM_RATE_INCR" +Name = "Increase sim rate" +Description = "Increase sim rate" +System = "Shared Cockpit" + +[SIM_RATE_DECR] +CategoryId = Miscellaneous_Events +SimEventName = "SIM_RATE_DECR" +Name = "Decrease sim rate" +Description = "Decrease sim rate" +System = "Shared Cockpit" + +[KNEEBOARD_VIEW] +CategoryId = Miscellaneous_Events +SimEventName = "KNEEBOARD_VIEW" +Name = "Toggles kneeboard" +Description = "Toggles kneeboard" +System = "Shared Cockpit" + +[PANEL_1] +CategoryId = Miscellaneous_Events +SimEventName = "PANEL_1" +Name = "Toggles panel 1" +Description = "Toggles panel 1" +System = "Shared Cockpit" + +[PANEL_2] +CategoryId = Miscellaneous_Events +SimEventName = "PANEL_2" +Name = "Toggles panel 2" +Description = "Toggles panel 2" +System = "Shared Cockpit" + +[PANEL_3] +CategoryId = Miscellaneous_Events +SimEventName = "PANEL_3" +Name = "Toggles panel 3" +Description = "Toggles panel 3" +System = "Shared Cockpit" + +[PANEL_4] +CategoryId = Miscellaneous_Events +SimEventName = "PANEL_4" +Name = "Toggles panel 4" +Description = "Toggles panel 4" +System = "Shared Cockpit" + +[PANEL_5] +CategoryId = Miscellaneous_Events +SimEventName = "PANEL_5" +Name = "Toggles panel 5" +Description = "Toggles panel 5" +System = "Shared Cockpit" + +[PANEL_6] +CategoryId = Miscellaneous_Events +SimEventName = "PANEL_6" +Name = "Toggles panel 6" +Description = "Toggles panel 6" +System = "Shared Cockpit" + +[PANEL_7] +CategoryId = Miscellaneous_Events +SimEventName = "PANEL_7" +Name = "Toggles panel 7" +Description = "Toggles panel 7" +System = "Shared Cockpit" + +[PANEL_8] +CategoryId = Miscellaneous_Events +SimEventName = "PANEL_8" +Name = "Toggles panel 8" +Description = "Toggles panel 8" +System = "Shared Cockpit" + +[PANEL_9] +CategoryId = Miscellaneous_Events +SimEventName = "PANEL_9" +Name = "Toggles panel 9" +Description = "Toggles panel 9" +System = "Shared Cockpit" + +[SOUND_ON] +CategoryId = Miscellaneous_Events +SimEventName = "SOUND_ON" +Name = "Turns sound on" +Description = "Turns sound on" +System = "Shared Cockpit" + +[SOUND_OFF] +CategoryId = Miscellaneous_Events +SimEventName = "SOUND_OFF" +Name = "Turns sound off" +Description = "Turns sound off" +System = "Shared Cockpit" + +[INVOKE_HELP] +CategoryId = Miscellaneous_Events +SimEventName = "INVOKE_HELP" +Name = "Brings up Help system" +Description = "Brings up Help system" +System = "Shared Cockpit" + +[TOGGLE_AIRCRAFT_LABELS] +CategoryId = Miscellaneous_Events +SimEventName = "TOGGLE_AIRCRAFT_LABELS" +Name = "Toggles aircraft labels" +Description = "Toggles aircraft labels" +System = "Shared Cockpit" + +[FLIGHT_MAP] +CategoryId = Miscellaneous_Events +SimEventName = "FLIGHT_MAP" +Name = "Brings up flight map" +Description = "Brings up flight map" +System = "Shared Cockpit" + +[RELOAD_PANELS] +CategoryId = Miscellaneous_Events +SimEventName = "RELOAD_PANELS" +Name = "Reload panel data" +Description = "Reload panel data" +System = "Shared Cockpit" + +[PANEL_ID_TOGGLE] +CategoryId = Miscellaneous_Events +SimEventName = "PANEL_ID_TOGGLE" +Name = "Toggles indexed panel (1 to 9)" +Description = "Toggles indexed panel (1 to 9)" +System = "Shared Cockpit" + +[PANEL_ID_OPEN] +CategoryId = Miscellaneous_Events +SimEventName = "PANEL_ID_OPEN" +Name = "Opens indexed panel (1 to 9)" +Description = "Opens indexed panel (1 to 9)" +System = "Shared Cockpit" + +[PANEL_ID_CLOSE] +CategoryId = Miscellaneous_Events +SimEventName = "PANEL_ID_CLOSE" +Name = "Closes indexed panel (1 to 9)" +Description = "Closes indexed panel (1 to 9)" +System = "Shared Cockpit" + +[RELOAD_USER_AIRCRAFT] +CategoryId = Miscellaneous_Events +SimEventName = "RELOAD_USER_AIRCRAFT" +Name = "Reloads the user aircraft data" +Description = "Reloads the user aircraft data (from cache if same type loaded as an AI, otherwise from disk)" +System = "Shared Cockpit" + +[SIM_RESET] +CategoryId = Miscellaneous_Events +SimEventName = "SIM_RESET" +Name = "Resets aircraft state" +Description = "Resets aircraft state" +System = "Shared Cockpit" + +[VIRTUAL_COPILOT_TOGGLE] +CategoryId = Miscellaneous_Events +SimEventName = "VIRTUAL_COPILOT_TOGGLE" +Name = "Turns Flying Tips on/off" +Description = "Turns Flying Tips on/off" +System = "Shared Cockpit" + +[VIRTUAL_COPILOT_SET] +CategoryId = Miscellaneous_Events +SimEventName = "VIRTUAL_COPILOT_SET" +Name = "Sets Flying Tips on/off (1,0)" +Description = "Sets Flying Tips on/off (1,0)" +System = "Shared Cockpit" + +[VIRTUAL_COPILOT_ACTION] +CategoryId = Miscellaneous_Events +SimEventName = "VIRTUAL_COPILOT_ACTION" +Name = "Triggers action noted in Flying Tips" +Description = "Triggers action noted in Flying Tips" +System = "Shared Cockpit" + +[REFRESH_SCENERY] +CategoryId = Miscellaneous_Events +SimEventName = "REFRESH_SCENERY" +Name = "Reloads scenery" +Description = "Reloads scenery" +System = "Shared Cockpit" + +[CLOCK_HOURS_DEC] +CategoryId = Miscellaneous_Events +SimEventName = "CLOCK_HOURS_DEC" +Name = "Decrements time by hours" +Description = "Decrements time by hours" +System = "Shared Cockpit" + +[CLOCK_HOURS_INC] +CategoryId = Miscellaneous_Events +SimEventName = "CLOCK_HOURS_INC" +Name = "Increments time by hours" +Description = "Increments time by hours" +System = "Shared Cockpit" + +[CLOCK_MINUTES_DEC] +CategoryId = Miscellaneous_Events +SimEventName = "CLOCK_MINUTES_DEC" +Name = "Decrements time by minutes" +Description = "Decrements time by minutes" +System = "Shared Cockpit" + +[CLOCK_MINUTES_INC] +CategoryId = Miscellaneous_Events +SimEventName = "CLOCK_MINUTES_INC" +Name = "Increments time by minutes" +Description = "Increments time by minutes" +System = "Shared Cockpit" + +[CLOCK_SECONDS_ZERO] +CategoryId = Miscellaneous_Events +SimEventName = "CLOCK_SECONDS_ZERO" +Name = "Zeros seconds" +Description = "Zeros seconds" +System = "Shared Cockpit" + +[CLOCK_HOURS_SET] +CategoryId = Miscellaneous_Events +SimEventName = "CLOCK_HOURS_SET" +Name = "Sets hour of day" +Description = "Sets hour of day" +System = "Shared Cockpit" + +[CLOCK_MINUTES_SET] +CategoryId = Miscellaneous_Events +SimEventName = "CLOCK_MINUTES_SET" +Name = "Sets minutes of the hour" +Description = "Sets minutes of the hour" +System = "Shared Cockpit" + +[ZULU_HOURS_SET] +CategoryId = Miscellaneous_Events +SimEventName = "ZULU_HOURS_SET" +Name = "Sets hours, zulu time" +Description = "Sets hours, zulu time" +System = "Shared Cockpit" + +[ZULU_MINUTES_SET] +CategoryId = Miscellaneous_Events +SimEventName = "ZULU_MINUTES_SET" +Name = "Sets minutes, in zulu time" +Description = "Sets minutes, in zulu time" +System = "Shared Cockpit" + +[ZULU_DAY_SET] +CategoryId = Miscellaneous_Events +SimEventName = "ZULU_DAY_SET" +Name = "Sets day, in zulu time" +Description = "Sets day, in zulu time" +System = "Shared Cockpit" + +[ZULU_YEAR_SET] +CategoryId = Miscellaneous_Events +SimEventName = "ZULU_YEAR_SET" +Name = "Sets year, in zulu time" +Description = "Sets year, in zulu time" +System = "Shared Cockpit" + +[GAUGE_KEYSTROKE] +CategoryId = Miscellaneous_Events +SimEventName = "GAUGE_KEYSTROKE" +Name = "Enables a keystroke to be sent to a gauge that is in focus (0-9, A-Z, or +/-/,/.)" +Description = "Enables a keystroke to be sent to a gauge that is in focus. The keystrokes can only be in the range 0 to 9, A to Z, and the four keys: plus, minus, comma and period. This is typically used to allow some keyboard entry to a complex device such as a GPS to enter such things as ICAO codes using the keyboard, rather than turning dials." +System = "Shared Cockpit" + +[SIMUI_WINDOW_HIDESHOW] +CategoryId = Miscellaneous_Events +SimEventName = "SIMUI_WINDOW_HIDESHOW" +Name = "Display the ATC window" +Description = "Display the ATC window." +System = "Shared Cockpit" + +[VIEW_WINDOW_TITLES_TOGGLE] +CategoryId = Miscellaneous_Events +SimEventName = "VIEW_WINDOW_TITLES_TOGGLE" +Name = "Turn window titles on or off" +Description = "Turn window titles on or off." +System = "Shared Cockpit" + +[AXIS_PAN_PITCH] +CategoryId = Miscellaneous_Events +SimEventName = "AXIS_PAN_PITCH" +Name = "Sets the pitch of the axis" +Description = "Sets the pitch of the axis. Requires an angle." +System = "Shared Cockpit" + +[AXIS_PAN_HEADING] +CategoryId = Miscellaneous_Events +SimEventName = "AXIS_PAN_HEADING" +Name = "Sets the heading of the axis" +Description = "Sets the heading of the axis. Requires an angle." +System = "Shared Cockpit" + +[AXIS_PAN_TILT] +CategoryId = Miscellaneous_Events +SimEventName = "AXIS_PAN_TILT" +Name = "Sets the tilt of the axis" +Description = "Sets the tilt of the axis. Requires an angle." +System = "Shared Cockpit" + +[VIEW_AXIS_INDICATOR_CYCLE] +CategoryId = Miscellaneous_Events +SimEventName = "VIEW_AXIS_INDICATOR_CYCLE" +Name = "Step through the view axes" +Description = "Step through the view axes." +System = "Shared Cockpit" + +[VIEW_MAP_ORIENTATION_CYCLE] +CategoryId = Miscellaneous_Events +SimEventName = "VIEW_MAP_ORIENTATION_CYCLE" +Name = "Step through the map orientations" +Description = "Step through the map orientations." +System = "Shared Cockpit" + +[TOGGLE_JETWAY] +CategoryId = Miscellaneous_Events +SimEventName = "TOGGLE_JETWAY" +Name = "Requests a jetway" +Description = "Requests a jetway, which will only be answered if the aircraft is at a parking spot." +System = "Shared Cockpit" + +[VIDEO_RECORD_TOGGLE] +CategoryId = Miscellaneous_Events +SimEventName = "VIDEO_RECORD_TOGGLE" +Name = "Turn on or off the video recording feature" +Description = "Turn on or off the video recording feature. This records uncompressed AVI format files to:\n My Documents\My Videos\" +System = "Shared Cockpit" + +[TOGGLE_AIRPORT_NAME_DISPLAY] +CategoryId = Miscellaneous_Events +SimEventName = "TOGGLE_AIRPORT_NAME_DISPLAY" +Name = "Turn on or off the airport name" +Description = "Turn on or off the airport name." +System = "Shared Cockpit" + +[CAPTURE_SCREENSHOT] +CategoryId = Miscellaneous_Events +SimEventName = "CAPTURE_SCREENSHOT" +Name = "Capture the current view as a screenshot" +Description = "Capture the current view as a screenshot. Which will be saved to a bmp file in:\n My Documents\My Pictures\" +System = "Shared Cockpit" + +[MOUSE_LOOK_TOGGLE] +CategoryId = Miscellaneous_Events +SimEventName = "MOUSE_LOOK_TOGGLE" +Name = "Switch Mouse Look mode on or off" +Description = "Switch Mouse Look mode on or off. Mouse Look mode enables a user to control their view using the mouse, and holding down the space bar." +System = "Shared Cockpit" + +[YAXIS_INVERT_TOGGLE] +CategoryId = Miscellaneous_Events +SimEventName = "YAXIS_INVERT_TOGGLE" +Name = "Switch inversion of Y axis controls on or off" +Description = "Switch inversion of Y axis controls on or off." +System = "Shared Cockpit" + +[AUTORUDDER_TOGGLE] +CategoryId = Miscellaneous_Events +SimEventName = "AUTORUDDER_TOGGLE" +Name = "Turn the automatic rudder control feature on or off" +Description = "Turn the automatic rudder control feature on or off." +System = "Shared Cockpit" + + + +# Freezing_position ####################### +# +[category_Freezing_position] +Name = "Freezing position" + + +[FREEZE_LATITUDE_LONGITUDE_TOGGLE] +CategoryId = Freezing_position +SimEventName = "FREEZE_LATITUDE_LONGITUDE_TOGGLE" +Name = "Toggles the freezing of the lat/lon position of the aircraft" +Description = "Turns the freezing of the lat/lon position of the aircraft (either user or AI controlled), on or off. If this key event is set, it means that the latitude and longitude of the aircraft are not being controlled by ESP, so enabling, for example, a SimConnect client to control the position of the aircraft. This can also apply to altitude and attitude. Refer to the simulation variables:\n IS LATITUDE LONGITUDE FREEZE ON,\n IS ALTITUDE FREEZE ON, and\n IS ATTITUDE FREEZE ON\n Refer also to the SimConnect_AIReleaseControl function. " +System = "Shared Cockpit" + +[FREEZE_LATITUDE_LONGITUDE_SET] +CategoryId = Freezing_position +SimEventName = "FREEZE_LATITUDE_LONGITUDE_SET" +Name = "Freezes the lat/lon position of the aircraft" +Description = "Freezes the lat/lon position of the aircraft." +System = "Shared Cockpit" + +[FREEZE_ALTITUDE_TOGGLE] +CategoryId = Freezing_position +SimEventName = "FREEZE_ALTITUDE_TOGGLE" +Name = "Turns the freezing of the altitude of the aircraft on or off" +Description = "Turns the freezing of the altitude of the aircraft on or off." +System = "Shared Cockpit" + +[FREEZE_ALTITUDE_SET] +CategoryId = Freezing_position +SimEventName = "FREEZE_ALTITUDE_SET" +Name = "Freezes the altitude of the aircraft" +Description = "Freezes the altitude of the aircraft.." +System = "Shared Cockpit" + +[FREEZE_ATTITUDE_TOGGLE] +CategoryId = Freezing_position +SimEventName = "FREEZE_ATTITUDE_TOGGLE" +Name = "Turns the freezing of the attitude of the aircraft on or off" +Description = "Turns the freezing of the attitude (pitch, bank and heading), of the aircraft on or off." +System = "Shared Cockpit" + +[FREEZE_ATTITUDE_SET] +CategoryId = Freezing_position +SimEventName = "FREEZE_ATTITUDE_SET" +Name = "Freezes the attitude of the aircraft" +Description = "Freezes the attitude (pitch, bank and heading), of the aircraft." +System = "Shared Cockpit" + + + +# Mission_Keys ####################### +# +[category_Mission_Keys] +Name = "Mission Keys" + + +[POINT_OF_INTEREST_TOGGLE_POINTER] +CategoryId = Mission_Keys +SimEventName = "POINT_OF_INTEREST_TOGGLE_POINTER" +Name = "Turn the point-of-interest indicator on or off" +Description = "Turn the point-of-interest indicator (often a light beam), on or off. Refer to the Missions system documentation." +System = "Shared Cockpit" + +[POINT_OF_INTEREST_CYCLE_PREVIOUS] +CategoryId = Mission_Keys +SimEventName = "POINT_OF_INTEREST_CYCLE_PREVIOUS" +Name = "Change the current point-of-interest to the previous point-of-interest" +Description = "Change the current point-of-interest to the previous point-of-interest." +System = "Shared Cockpit" + +[POINT_OF_INTEREST_CYCLE_NEXT] +CategoryId = Mission_Keys +SimEventName = "POINT_OF_INTEREST_CYCLE_NEXT" +Name = "Change the current point-of-interest to the next point-of-interest" +Description = "Change the current point-of-interest to the next point-of-interest." +System = "Shared Cockpit" + + + +# ATC ####################### +# +[category_ATC] +Name = "ATC" + + +[ATC] +CategoryId = ATC +SimEventName = "ATC" +Name = "Activates ATC window" +Description = "Activates ATC window" +System = "Shared Cockpit" + +[ATC_MENU_1] +CategoryId = ATC +SimEventName = "ATC_MENU_1" +Name = "Selects ATC option 1" +Description = "Selects ATC option 1" +System = "Shared Cockpit" + +[ATC_MENU_2] +CategoryId = ATC +SimEventName = "ATC_MENU_2" +Name = "Selects ATC option 2" +Description = "Selects ATC option 2" +System = "Shared Cockpit" + +[ATC_MENU_3] +CategoryId = ATC +SimEventName = "ATC_MENU_3" +Name = "Selects ATC option 3" +Description = "Selects ATC option 3" +System = "Shared Cockpit" + +[ATC_MENU_4] +CategoryId = ATC +SimEventName = "ATC_MENU_4" +Name = "Selects ATC option 4" +Description = "Selects ATC option 4" +System = "Shared Cockpit" + +[ATC_MENU_5] +CategoryId = ATC +SimEventName = "ATC_MENU_5" +Name = "Selects ATC option 5" +Description = "Selects ATC option 5" +System = "Shared Cockpit" + +[ATC_MENU_6] +CategoryId = ATC +SimEventName = "ATC_MENU_6" +Name = "Selects ATC option 6" +Description = "Selects ATC option 6" +System = "Shared Cockpit" + +[ATC_MENU_7] +CategoryId = ATC +SimEventName = "ATC_MENU_7" +Name = "Selects ATC option 7" +Description = "Selects ATC option 7" +System = "Shared Cockpit" + +[ATC_MENU_8] +CategoryId = ATC +SimEventName = "ATC_MENU_8" +Name = "Selects ATC option 8" +Description = "Selects ATC option 8" +System = "Shared Cockpit" + +[ATC_MENU_9] +CategoryId = ATC +SimEventName = "ATC_MENU_9" +Name = "Selects ATC option 9" +Description = "Selects ATC option 9" +System = "Shared Cockpit" + +[ATC_MENU_0] +CategoryId = ATC +SimEventName = "ATC_MENU_0" +Name = "Selects ATC option 10" +Description = "Selects ATC option 10" +System = "Shared Cockpit" + + + +# Multiplayer ####################### +# +[category_Multiplayer] +Name = "Multiplayer" + + +[MP_TRANSFER_CONTROL] +CategoryId = Multiplayer +SimEventName = "MP_TRANSFER_CONTROL" +Name = "Toggle to the next player to track" +Description = "Toggle to the next player to track" +System = "-" + +[MP_PLAYER_CYCLE] +CategoryId = Multiplayer +SimEventName = "MP_PLAYER_CYCLE" +Name = "Cycle through the current user aircraft" +Description = "Cycle through the current user aircraft." +System = "Shared Cockpit" + +[MP_PLAYER_FOLLOW] +CategoryId = Multiplayer +SimEventName = "MP_PLAYER_FOLLOW" +Name = "Set the view to follow the selected user aircraft" +Description = "Set the view to follow the selected user aircraft." +System = "Shared Cockpit" + +[MP_CHAT] +CategoryId = Multiplayer +SimEventName = "MP_CHAT" +Name = "Toggles chat window visible/invisible" +Description = "Toggles chat window visible/invisible" +System = "Shared Cockpit" + +[MP_ACTIVATE_CHAT] +CategoryId = Multiplayer +SimEventName = "MP_ACTIVATE_CHAT" +Name = "Activates chat window" +Description = "Activates chat window" +System = "Shared Cockpit" + +[MP_VOICE_CAPTURE_START] +CategoryId = Multiplayer +SimEventName = "MP_VOICE_CAPTURE_START" +Name = "Start capturing radio audio" +Description = "Start capturing audio from the users computer and transmitting it to all other players in the multiplayer session who are turned to the same radio frequency." +System = "Shared Cockpit" + +[MP_VOICE_CAPTURE_STOP] +CategoryId = Multiplayer +SimEventName = "MP_VOICE_CAPTURE_STOP" +Name = "Stop capturing radio audio" +Description = "Stop capturing radio audio." +System = "Shared Cockpit" + +[MP_BROADCAST_VOICE_CAPTURE_START] +CategoryId = Multiplayer +SimEventName = "MP_BROADCAST_VOICE_CAPTURE_START" +Name = "Start capturing broadcast audio" +Description = "Start capturing audio from the users computer and transmitting it to all other players in the multiplayer session." +System = "Shared Cockpit" + +[MP_BROADCAST_VOICE_CAPTURE_STOP] +CategoryId = Multiplayer +SimEventName = "MP_BROADCAST_VOICE_CAPTURE_STOP" +Name = "Stop capturing broadcast audio" +Description = "Stop capturing broadcast audio." +System = "Shared Cockpit" + +[TOGGLE_RACERESULTS_WINDOW] +CategoryId = Multiplayer +SimEventName = "TOGGLE_RACERESULTS_WINDOW" +Name = "Show or hide multi-player race results" +Description = "Show or hide multi-player race results." +System = "Disabled" + + + diff --git a/MSFSTouchPortalPlugin/Configuration/SimVars.ini b/MSFSTouchPortalPlugin/Configuration/SimVars.ini index 1fee0f2..9b387dd 100644 --- a/MSFSTouchPortalPlugin/Configuration/SimVars.ini +++ b/MSFSTouchPortalPlugin/Configuration/SimVars.ini @@ -1,9 +1,11 @@ -## Generated on 2022-04-09 22:46:28.881320 UTC with data from https://github.com/odwdinc/Python-SimConnect v0.4.25 +## Generated on 2022-04-10 14:12:23.460122 UTC with data from https://github.com/odwdinc/Python-SimConnect v0.4.25 # Engine ####################### # [category_Engine] +Name = "Engine" + [NUMBER_OF_ENGINES] Id = NumberOfEngines @@ -988,15 +990,11 @@ Description = "The stage of the afterburner, or 0 if the afterburner is not acti -# FuelTankSelection ####################### -# -[category_FuelTankSelection] - - - # Fuel ####################### # [category_Fuel] +Name = "Fuel" + [FUEL_TANK_CENTER_LEVEL] Id = FuelTankCenterLevel @@ -1435,6 +1433,8 @@ Description = "Estimated fuel flow at cruise" # Lights ####################### # [category_Lights] +Name = "Lights" + [LIGHT_STROBE] Id = LightStrobe @@ -1657,6 +1657,8 @@ Description = "Return true if the light is on." # PositionandSpeed ####################### # [category_PositionandSpeed] +Name = "Position & Speed" + [GROUND_VELOCITY] Id = GroundVelocity @@ -1969,6 +1971,8 @@ Description = "The current wing flex. Different values can be set for each wing # FlightInstrumentation ####################### # [category_FlightInstrumentation] +Name = "Flight Instruments" + [AIRSPEED_TRUE] Id = AirspeedTrue @@ -2380,6 +2384,8 @@ Description = "Vacuum system suction pressure" # Avionics ####################### # [category_Avionics] +Name = "Avionics" + [AVIONICS_MASTER_SWITCH] Id = AvionicsMasterSwitch @@ -3421,6 +3427,8 @@ Description = "Altitude of GPS target" # Controls ####################### # [category_Controls] +Name = "Controls" + [YOKE_Y_POSITION] Id = YokeYPosition @@ -3859,6 +3867,8 @@ Description = "If true the aircraft is dumping fuel at the rate set in the confi # Autopilot ####################### # [category_Autopilot] +Name = "Autopilot" + [AUTOPILOT_AVAILABLE] Id = AutopilotAvailable @@ -4207,6 +4217,8 @@ Description = "True if autopilot FLC mode applied" # LandingGear ####################### # [category_LandingGear] +Name = "Landing Gear" + [IS_GEAR_RETRACTABLE] Id = IsGearRetractable @@ -4699,6 +4711,8 @@ Description = "True if the nosewheel lock is engaged." # Environment ####################### # [category_Environment] +Name = "Environment" + [AMBIENT_DENSITY] Id = AmbientDensity @@ -4876,6 +4890,8 @@ Description = "Outside temperature on the standard ATM scale" # HelicopterSpecific ####################### # [category_HelicopterSpecific] +Name = "Helicopter" + [ROTOR_BRAKE_HANDLE_POS] Id = RotorBrakeHandlePos @@ -5008,6 +5024,8 @@ Description = "The position of the helicopter's collective. 0 is fully up, 100 f # MiscellaneousSystems ####################### # [category_MiscellaneousSystems] +Name = "Misc. Systems" + [SMOKE_ENABLE] Id = SmokeEnable @@ -5455,6 +5473,8 @@ Description = "The number of droppable objects at the station number identified # Miscellaneous ####################### # [category_Miscellaneous] +Name = "Miscellaneous" + [TOTAL_WEIGHT] Id = TotalWeight @@ -6892,6 +6912,8 @@ Description = " " # String ####################### # [category_String] +Name = "Misc. Strings" + [ATC_TYPE] Id = AtcType @@ -6988,6 +7010,8 @@ Description = "ID of approach transition" # AIControlledAircraft ####################### # [category_AIControlledAircraft] +Name = "AI Aircraft" + [AI_DESIRED_SPEED] Id = AiDesiredSpeed @@ -7111,6 +7135,8 @@ Description = "Estimated time of arrival for the current schedule entry, given a # CarrierOperations ####################### # [category_CarrierOperations] +Name = "Carrier Ops." + [LAUNCHBAR_POSITION] Id = LaunchbarPosition @@ -7207,6 +7233,8 @@ Description = "The speed of the aircraft relative to the speed of the first surf # Racing ####################### # [category_Racing] +Name = "Racing" + [RECIP_ENG_DETONATING] Id = RecipEngDetonating @@ -7303,6 +7331,8 @@ Description = "Indexed from 1. This value set in the Aircraft Configuration File # Environment ####################### # [category_Environment] +Name = "Environment" + [ABSOLUTE_TIME] Id = AbsoluteTime @@ -7435,6 +7465,8 @@ Description = "Local time difference from GMT" # SlingsandHoists ####################### # [category_SlingsandHoists] +Name = "Slings & Hoists" + [NUM_SLING_CABLES] Id = NumSlingCables diff --git a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj index 29a0ecb..1e075fb 100644 --- a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj +++ b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj @@ -99,6 +99,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs index 1085b4e..683deb4 100644 --- a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs +++ b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs @@ -7,7 +7,9 @@ namespace MSFSTouchPortalPlugin.Objects.Plugin [TouchPortalCategory(Groups.Plugin)] internal static class PluginMapping { - [TouchPortalAction(PluginActions.Connection, "Connection", "Toggle/On/Off SimConnect Connection", "SimConnect Connection - {0}")] + [TouchPortalAction(PluginActions.Connection, "Connection", + "Toggle/On/Off SimConnect Connection", + "SimConnect Connection - {0}")] [TouchPortalActionChoice(new[] { "Toggle", "On", "Off", "Reload States" }, Id = "Action")] [TouchPortalActionMapping(PluginActions.ToggleConnection, "Toggle")] [TouchPortalActionMapping(PluginActions.Connect, "On")] @@ -15,7 +17,10 @@ internal static class PluginMapping [TouchPortalActionMapping(PluginActions.ReloadStates, "Reload States")] public static readonly object Connection; - [TouchPortalAction(PluginActions.ActionRepeatInterval, "Action Repeat Interval", "Held Action Repeat Rate (ms)", "Repeat Interval: {0} to/by: {1} ms", true)] + [TouchPortalAction(PluginActions.ActionRepeatInterval, "Action Repeat Interval", + "Held Action Repeat Rate (ms)", + "Repeat Interval: {0} to/by: {1} ms", + holdable: true)] [TouchPortalActionChoice(new[] { "Set", "Increment", "Decrement" }, Id = "Action")] [TouchPortalActionText("450", 50, int.MaxValue, Id = "Value")] [TouchPortalActionMapping(PluginActions.ActionRepeatIntervalSet, "Set")] @@ -23,21 +28,43 @@ internal static class PluginMapping [TouchPortalActionMapping(PluginActions.ActionRepeatIntervalDec, "Decrement")] public static readonly object ActionRepeatInterval; - [TouchPortalAction(PluginActions.SetSimVar, "Set Simulator Variable Value", "Sets a value on any loaded State which is marked as settable.", "Set Variable {0} to {1} (release AI: {2})")] + [TouchPortalAction(PluginActions.SetCustomSimEvent, "Activate a Custom Simulator Event", + "Trigger a Simulator Event by name. The value, if any, should evaluate to numeric. Using basic math operators and dynamic state values is possible.", + "Activate Event {0} with value {1} (if any)", + holdable: true)] + [TouchPortalActionText("SIMULATOR_EVENT_NAME", Id = "EvtId", Label = "Event ID")] + [TouchPortalActionText("", Id = "Value", Label = "Value")] + public static readonly object SetCustomSimEvent; + + [TouchPortalAction(PluginActions.SetKnownSimEvent, "Activate a Simulator Event From List", + "Trigger a Simulator Event. The value, if any, should evaluate to numeric. Using basic math operators and dynamic state values is possible.", + "From Category {0} Activate Event {1} with value {2} (if any)", + holdable: true)] + [TouchPortalActionChoice("", "", Id = "SimCatName", Label = "Category")] + [TouchPortalActionChoice(" ", "", Id = "VarName", Label = "Simulator Variable Name")] [TouchPortalActionNumeric(0, 0, 99, false, Id = "VarIndex", Label = "Variable Index")] - [TouchPortalActionChoice("", "", Id = "CatId", Label = "Category")] - [TouchPortalActionChoice("", "", Id = "Unit", Label = "Unit Name")] + [TouchPortalActionChoice("", "", Id = "CatId", Label = "Category")] + [TouchPortalActionChoice(" ", "", Id = "Unit", Label = "Unit Name")] [TouchPortalActionText("F2", Id = "Format", Label = "Formatting String")] [TouchPortalActionText("0", Id = "DfltVal", Label = "Default Value")] //[TouchPortalActionSwitch(false, Id = "CanSet", Label = "Settable")] // we should know if it's settable from the imported data @@ -69,11 +99,15 @@ internal static class PluginMapping [TouchPortalActionChoice("", "", Id = "VarName", Label = "Simulator Variable")] public static readonly object RemoveSimVar; - [TouchPortalAction(PluginActions.LoadSimVars, "Load SimVar Definitions From File", "Load a set of simulator variable state definitions from a configuration file.", "Load from file {0} (name only for user config. folder, or full path with file name)")] + [TouchPortalAction(PluginActions.LoadSimVars, "Load SimVar Definitions From File", + "Load a set of simulator variable state definitions from a configuration file.", + "Load from file {0} (name only for user config. folder, or full path with file name)")] [TouchPortalActionFile("CustomStates.ini", Id = "VarsFile", Label = "Load From File")] public static readonly object LoadSimVars; - [TouchPortalAction(PluginActions.SaveSimVars, "Save SimVar Definitions To File", "Save the current simulator variable state definitions to a configuration file.", "Save {0} states to file {1} (name only for user config. folder, or full path with file name)")] + [TouchPortalAction(PluginActions.SaveSimVars, "Save SimVar Definitions To File", + "Save the current simulator variable state definitions to a configuration file.", + "Save {0} states to file {1} (name only for user config. folder, or full path with file name)")] [TouchPortalActionChoice(new[] { "Custom", "All" }, Id = "VarsSet", Label = "Variables Set")] [TouchPortalActionText("CustomStates.ini", Id = "VarsFile", Label = "Save to File")] //[TouchPortalActionFile("CustomStates.ini", Id = "VarsFile", Label = "Save to File")] // doesn't allow freeform entry or selecting a non-existing file as of TP 3.0.10 @@ -151,6 +185,8 @@ public enum PluginActions : short // Action IDs Connection, ActionRepeatInterval, + SetCustomSimEvent, + SetKnownSimEvent, SetSimVar, AddCustomSimVar, AddKnownSimVar, diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 0d97aa0..6e70760 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -11,7 +11,6 @@ using System.Collections.Generic; using System.Collections.Concurrent; using System.Linq; -using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using TouchPortalSDK; @@ -45,7 +44,6 @@ internal class PluginService : IPluginService, ITouchPortalEventHandler private Dictionary actionsDictionary = new(); private Dictionary pluginSettingsDictionary = new(); - private Dictionary _importedSimVars = new(); private readonly ConcurrentDictionary _statesDictionary = new(); private readonly ConcurrentDictionary _customIntervalStates = new(); // IDs of SimVars which need periodic value polling; concurrent because the polling happens in separate task from setter. private readonly Dictionary _settableSimVarIds = new(); // mapping of settable SimVar IDs for lookup in statesDictionary @@ -128,9 +126,10 @@ public Task StopAsync(CancellationToken cancellationToken) { /// Initialized the Touch Portal Message Processor ///
private bool Initialize() { - SetupEventLists(); // set these up first to have access to defined plugin settings - // sent to TP upon initial connection - _importedSimVars = _pluginConfig.ImportSimVars().ToDictionary(s => s.SimVarName, s => s); + // set up data which may be sent to TP upon initial connection + _pluginConfig.Init(); + actionsDictionary = _reflectionService.GetActionEvents(); + pluginSettingsDictionary = _reflectionService.GetSettings(); if (!_client.Connect()) { _logger.LogCritical("Failed to connect to Touch Portal! Quitting."); @@ -142,10 +141,6 @@ private bool Initialize() { _simConnectService.OnConnect += SimConnectEvent_OnConnect; _simConnectService.OnDisconnect += SimConnectEvent_OnDisconnect; - // Check for custom SimConnect.cfg and try copy it to application dir (may require elevated privileges) - if (_pluginConfig.CopySimConnectConfig()) - _logger.LogInformation("Using custom SimConnect.cfg file from user's AppData folder."); - return true; } @@ -203,12 +198,6 @@ private void CheckPendingRequests() { } } - // this is done once at startup before any connections are established - private void SetupEventLists() { - actionsDictionary = _reflectionService.GetActionEvents(); - pluginSettingsDictionary = _reflectionService.GetSettings(); - } - #endregion Startup, Shutdown and Processing Tasks #region SimConnect Events ///////////////////////////////////// @@ -248,6 +237,8 @@ private void SimConnectEvent_OnDataUpdateEvent(Definition def, Definition req, o } private void SimConnectEvent_OnDisconnect() { + _logger.LogInformation("SimConnect Disconnected"); + _simTasksCTS?.Cancel(); if (_pluginEventsTask.Status == TaskStatus.Running && !_pluginEventsTask.Wait(5000)) _logger.LogWarning("PluginEventsTask timed out while stopping."); @@ -302,7 +293,6 @@ private void SetupSimVars() { UpdateAllSimVarsList(); UpdateSettableSimVarsList(); - UpdateKnownSimVars(); } private void RegisterAllSimVars() { @@ -316,7 +306,7 @@ private bool AddSimVar(SimVarItem simVar, bool dynamicState = true, bool postpon return false; if (_statesDictionary.ContainsKey(simVar.Def)) - RemoveSimVar(simVar, true); + RemoveSimVar(simVar.Def, true); // Register it. If the SimVar gets regular updates (not custom polling) then this also starts the data requests for this value. // If we're not connected now then the var will be registered in RegisterAllSimVars() when we do connect. @@ -340,15 +330,14 @@ private bool AddSimVar(SimVarItem simVar, bool dynamicState = true, bool postpon if (!postponeUpdate) { UpdateAllSimVarsList(); UpdateSettableSimVarsList(); - UpdateKnownSimVars(); // reload to mark any sim vars already used (experimental) } _logger.LogTrace($"Added SimVar: {simVar.ToDebugString()}"); return true; } // note that the stored list of custom added SimVars is not affected here so that we can reload those during a SimConnect restart - private bool RemoveSimVar(SimVarItem simVar, bool postponeUpdate = false) { - if (simVar == null || !_statesDictionary.TryRemove(simVar.Def, out _)) + private bool RemoveSimVar(Definition def, bool postponeUpdate = false) { + if (!_statesDictionary.TryRemove(def, out var simVar)) return false; _customIntervalStates.TryRemove(simVar.Def, out _); _settableSimVarIds.Remove(simVar.Id); @@ -358,22 +347,20 @@ private bool RemoveSimVar(SimVarItem simVar, bool postponeUpdate = false) { if (!postponeUpdate) { UpdateAllSimVarsList(); UpdateSettableSimVarsList(); - UpdateKnownSimVars(); // reload to un-mark the SimVar just removed (experimental) } return true; } private bool AddCustomSimVar(SimVarItem simVar) { - if (!AddSimVar(simVar, true)) - return false; - if (_addedSimVars.FirstOrDefault(s => s.Value == simVar.Id) is var old && !string.IsNullOrEmpty(old.Value)) + if (_addedSimVars.FirstOrDefault(s => s.Value == simVar.Id) is var old && !string.IsNullOrEmpty(old.Value)) { + RemoveSimVar(old.Key, true); _addedSimVars.Remove(old.Key); - _addedSimVars.TryAdd(simVar.Def, simVar.Id); - return true; + } + return AddSimVar(simVar) && _addedSimVars.TryAdd(simVar.Def, simVar.Id); } private bool RemoveCustomSimVar(SimVarItem simVar) { - if (RemoveSimVar(simVar)) + if (RemoveSimVar(simVar.Def)) return _addedSimVars.Remove(simVar.Def); return false; } @@ -386,7 +373,6 @@ private void LoadCustomSimVarsFromFile(string filepath) { AddCustomSimVar(simVar); } _logger.LogInformation($"Loaded {simVars.Count} SimVar States from file '{filepath}'"); - return; } private void SaveSimVarsToFile(string filepath, bool customOnly = true) { @@ -406,20 +392,18 @@ private void SaveSimVarsToFile(string filepath, bool customOnly = true) { // Action data list updaters // common handler for other action list updaters - private void UpdateActionDataList(PluginActions actionId, string dataId, IReadOnlyCollection data, string instanceId = null) { + private void UpdateActionDataList(PluginActions actionId, string dataId, IEnumerable data, string instanceId = null) { _client.ChoiceUpdate(PluginId + $".Plugin.Action.{actionId}.Data.{dataId}", data.ToArray(), instanceId); } // List of settable SimVars private void UpdateSettableSimVarsList() { - var list = (from s in _statesDictionary.Values where s.CanSet select Categories.PrependCategoryName(s.CategoryId, s.Name) + $" [{s.Id}]").OrderBy(n => n); - UpdateActionDataList(PluginActions.SetSimVar, "VarName", list.ToArray()); + UpdateActionDataList(PluginActions.SetSimVar, "VarName", GetSimVarSelectorList(settable: true)); } // List of all current SimVars private void UpdateAllSimVarsList() { - var list = (from s in _statesDictionary.Values select Categories.PrependCategoryName(s.CategoryId, s.Name) + $" [{s.Id}]").OrderBy(n => n); - UpdateActionDataList(PluginActions.RemoveSimVar, "VarName", list.ToArray()); + UpdateActionDataList(PluginActions.RemoveSimVar, "VarName", GetSimVarSelectorList(settable: false)); } // Unit lists @@ -428,26 +412,44 @@ private void UpdateUnitsLists() { UpdateActionDataList(PluginActions.AddKnownSimVar, "Unit", Units.ListUsable); } + // Available plugin's state/action categories + private void UpdateUCategoryLists() { + UpdateActionDataList(PluginActions.AddCustomSimVar, "CatId", Categories.ListUsable); + UpdateActionDataList(PluginActions.AddKnownSimVar, "CatId", Categories.ListUsable); + } + + // List of imported SimVariable categories + void UpdateSimVarCategories() { + UpdateActionDataList(PluginActions.AddKnownSimVar, "SimCatName", _pluginConfig.ImportedSimVarCategoryNames); + } + + // List of imported SimVariables per category + private void UpdateKnownSimVars(string categoryName, string instanceId) { + // select variable names in category and mark if already used + if (_pluginConfig.TryGetImportedSimVarsForCateogy(categoryName, out var vars)) { + var list = vars.Select(v => (_statesDictionary.Values.FirstOrDefault(s => s.SimVarName == v.SimVarName) == null ? "" : "* ") + v.TouchPortalSelectorName); + UpdateActionDataList(PluginActions.AddKnownSimVar, "VarName", list, instanceId); + } + } + // This will re-populate the list of Units for an action instance, but put the default Unit for the selected SimVar at the top. + // Possible future improvement would be to only present units of a compatible conversion type. private void UpdateUnitsListForKnownSimVar(string varName, string instanceId) { - varName = GetImportedSimVarKeyFromName(varName); - if (varName != null && _importedSimVars.TryGetValue(varName, out SimVariable var)) { - var list = new []{ var.Unit }.Concat(Units.ListUsable); - UpdateActionDataList(PluginActions.AddKnownSimVar, "Unit", list.ToArray(), instanceId); + if (_pluginConfig.TryGetImportedSimVarBySelector(varName, out SimVariable var)) { + var list = new[] { var.Unit }.Concat(Units.ListUsable); + UpdateActionDataList(PluginActions.AddKnownSimVar, "Unit", list, instanceId); } } - // Available state/action categories - private void UpdateUCategoryLists() { - UpdateActionDataList(PluginActions.AddCustomSimVar, "CatId", Categories.ListUsable); - UpdateActionDataList(PluginActions.AddKnownSimVar, "CatId", Categories.ListUsable); + // List of imported Sim Event categories + void UpdateSimEventCategories() { + UpdateActionDataList(PluginActions.SetKnownSimEvent, "SimCatName", _pluginConfig.ImportedSimEvenCategoryNames); } - // List of imported SimVariables - private void UpdateKnownSimVars() { - // select variable names with category info and mark if already used - var list = _importedSimVars.Values.Select(v => (_statesDictionary.Values.FirstOrDefault(s => s.SimVarName == v.SimVarName) == null ? "" : "* ") + v.TouchPortalSelectorName); - UpdateActionDataList(PluginActions.AddKnownSimVar, "VarName", list.ToArray()); + // List of imported SimEvents per category + private void UpdateKnownSimEventsForCategory(string categoryName, string instanceId) { + if (_pluginConfig.TryGetImportedSimEventNamesForCateogy(categoryName, out var list)) + UpdateActionDataList(PluginActions.SetKnownSimEvent, "EvtId", list, instanceId); } // Misc. data update/clear @@ -471,7 +473,8 @@ private void ClearRepeatingActions() { #region Plugin Action/Event Handlers ///////////////////////////////////// - private void ProcessPluginCommandAction(PluginActions actionId, double value = double.NaN) { + // Handles some basic actions like sim connection and repeat rate, with optional data value(s). + private void ProcessPluginCommandAction(PluginActions actionId, ActionData data = null) { switch (actionId) { case PluginActions.ToggleConnection: ProcessPluginCommandAction(autoReconnectSimConnect ? PluginActions.Disconnect : PluginActions.Connect); @@ -501,9 +504,12 @@ private void ProcessPluginCommandAction(PluginActions actionId, double value = d case PluginActions.ActionRepeatIntervalInc: case PluginActions.ActionRepeatIntervalDec: - case PluginActions.ActionRepeatIntervalSet: - if (double.IsNaN(value)) + case PluginActions.ActionRepeatIntervalSet: { + // preserve backwards compatibility with old actions which used indexed data IDs + if (data == null || !(data.TryGetValue("Value", out var sVal) || data.TryGetValue("1", out sVal)) || !TryEvaluateValue(sVal, out var value)) { + _logger.LogWarning($"Could not find or parse numeric value for repeat rate from data: {ActionDataToKVPairString(data)}"); break; + } if (actionId == PluginActions.ActionRepeatIntervalInc) value = Settings.ActionRepeatInterval.RealValue + value; else if (actionId == PluginActions.ActionRepeatIntervalDec) @@ -511,19 +517,24 @@ private void ProcessPluginCommandAction(PluginActions actionId, double value = d value = Math.Clamp(value, Settings.ActionRepeatInterval.MinValue, Settings.ActionRepeatInterval.MaxValue); if (value != Settings.ActionRepeatInterval.RealValue) _client.SettingUpdate(Settings.ActionRepeatInterval.Name, $"{value:F0}"); // this will trigger the actual value update - break; + break; + } } } - private void SetSimVarValueFromActionData(string varName, string value, bool releaseAi) { - if (!TryGetSimVarIdFromActionData(varName, out string varId)) { - _logger.LogError($"Could not find ID in SimVar Name: '{varName}'"); + // Parse and process PluginActions.SetSimVar action + private void SetSimVarValueFromActionData(ActionData data) { + if (!data.TryGetValue("VarName", out var varName) || + !data.TryGetValue("Value", out var value) || + !TryGetSimVarIdFromActionData(varName, out string varId)) { + _logger.LogWarning($"Could not parse required action parameters for {PluginActions.SetSimVar} from data: {ActionDataToKVPairString(data)}"); return; } if (!_settableSimVarIds.TryGetValue(varId, out Definition def) || !_statesDictionary.TryGetValue(def, out SimVarItem simVar)) { _logger.LogError($"Could not find definition for settable SimVar Id: '{varId}' Name: '{varName}'"); return; } + if (simVar.IsStringType) { if (!simVar.SetValue(new StringVal(value))) { _logger.LogError($"Could not set string value '{value}' for SimVar Id: '{varId}' Name: '{varName}'"); @@ -534,67 +545,44 @@ private void SetSimVarValueFromActionData(string varName, string value, bool rel _logger.LogError($"Could not set numeric value '{value}' for SimVar Id: '{varId}' Name: '{varName}'"); return; } - if (releaseAi && !_simConnectService.ReleaseAIControl(simVar.Def)) + if (data.TryGetValue("RelAi", out var relAi) && new BooleanString(relAi) && !_simConnectService.ReleaseAIControl(simVar.Def)) return; _simConnectService.SetDataOnSimObject(simVar); } private void AddSimVarFromActionData(PluginActions actId, ActionData data) { - if (!data.TryGetValue("VarName", out var varName) || - !data.TryGetValue("VarIndex", out var sIndex) || - !data.TryGetValue("CatId", out var sCatId) || - !data.TryGetValue("Unit", out var sUnit) || - !Categories.TryGetCategoryId(sCatId, out Groups catId) + if (!data.TryGetValue("VarName", out var varName) || string.IsNullOrWhiteSpace(varName) || + !data.TryGetValue("CatId", out var sCatId) || !Categories.TryGetCategoryId(sCatId, out Groups catId) || + !data.TryGetValue("Unit", out var sUnit) ) { - _logger.LogWarning($"Could not parse required action parameters for {actId} from data: {string.Join(", ", data.Select(d => $"{d.Key}={d.Value}"))}"); + _logger.LogWarning($"Could not parse required action parameters for {actId} from data: {ActionDataToKVPairString(data)}"); return; } - bool canSet = false; - bool indexed = false; - string varId; - string name; - varName = varName.Trim(); - // imported vars are formatted in the data list with leading category name and possible ":N" suffix - if (actId == PluginActions.AddKnownSimVar) { - varName = GetImportedSimVarKeyFromName(varName); - } - // user-supplied name may contain a ":N" which would indicate an indexed type - else if (varName.IndexOf(':') is int colIdx && colIdx > -1) { - indexed = true; - varName = varName.Remove(colIdx); - } + // check if we've imported this var and have meta data - if (_importedSimVars.TryGetValue(varName, out SimVariable impSimVar)) { - varId = impSimVar.Id; - canSet = impSimVar.CanSet; - indexed = impSimVar.Indexed; - name = impSimVar.Name; - } - else { - canSet = data.TryGetValue("CanSet", out var sCanSet) && new BooleanString(sCanSet); - // if there was no :N in the var name but the user provided a non-zero index, we assume they meant it - indexed = indexed || sIndex != "0"; - // Create a reasonable string for a TP state ID - varId = Regex.Replace(varName.ToLower(), @"(?:\b|\W|_)(\w)", m => (m.Groups[1].ToString().ToUpper())); - name = varName; // for lack of anything better - } - if (indexed) { - if (sIndex == "0") - sIndex = "1"; - varName = string.Concat(varName, ":", sIndex); - } + SimVariable impSimVar = _pluginConfig.GetOrCreateImportedSimVariable(varName); + if (impSimVar == null) // highly unlikely + return; + + uint index = 0; + bool haveIndexValue = (data.TryGetValue("VarIndex", out var sIndex) && uint.TryParse(sIndex, out index) && index > 0); + impSimVar.Indexed = impSimVar.Indexed || haveIndexValue; + if (impSimVar.Indexed) + impSimVar.SimVarName = string.Concat(impSimVar.SimVarName, ":", Math.Clamp(index, 1, 99).ToString()); + impSimVar.CanSet = impSimVar.CanSet || (data.TryGetValue("CanSet", out var sCanSet) && new BooleanString(sCanSet)); + // create the SimVarItem from collected data var simVar = new SimVarItem() { - Id = varId, - Name = name, - SimVarName = varName, + Id = impSimVar.Id, + Name = impSimVar.Name, + SimVarName = impSimVar.SimVarName, CategoryId = catId, - Unit = sUnit, - CanSet = canSet, + Unit = sUnit ?? "number", + CanSet = impSimVar.CanSet, StringFormat = data.GetValueOrDefault("Format", string.Empty).Trim(), DefaultValue = data.GetValueOrDefault("DfltVal", string.Empty).Trim(), - TouchPortalStateId = $"{PluginId}.{catId}.State.{varId}" + TouchPortalStateId = $"{PluginId}.{catId}.State.{impSimVar.Id}" }; if (data.TryGetValue("UpdPer", out var sPeriod) && Enum.TryParse(sPeriod, out UpdatePeriod period)) simVar.UpdatePeriod = period; @@ -606,22 +594,47 @@ private void AddSimVarFromActionData(PluginActions actId, ActionData data) { if (AddCustomSimVar(simVar)) _logger.LogInformation($"Added new SimVar state from action data: {simVar.ToDebugString()}"); else - _logger.LogError($"Failed to add SimVar from action data, check previous log messages. Action data: {string.Join(", ", data.Select(d => $"{d.Key}={d.Value}"))}"); + _logger.LogError($"Failed to add SimVar from action data, check previous log messages. Action data: {ActionDataToKVPairString(data)}"); } - - private void RemoveSimVarByActionDataName(string varName) { - if (!TryGetSimVarIdFromActionData(varName, out string varId)) { - _logger.LogWarning($"Could not find ID in SimVar Name: '{varName}'"); + private void RemoveSimVarByActionDataName(ActionData data) { + if (!data.TryGetValue("VarName", out var varName) || !TryGetSimVarIdFromActionData(varName, out string varId)) { + _logger.LogWarning($"Could not find valid SimVar ID in action data: {ActionDataToKVPairString(data)}'"); return; } SimVarItem simVar = _statesDictionary.Values.FirstOrDefault(s => s.Id == varId); - if (simVar != null && RemoveSimVar(simVar)) + if (simVar != null && RemoveSimVar(simVar.Def)) _logger.LogInformation($"Removed SimVar '{simVar.SimVarName}'"); else _logger.LogWarning($"Could not find definition for settable SimVar Id: '{varId}' from Name: '{varName}'"); } + // Dynamic sim Events (actions) + private void ProcessSimEventFromActionData(PluginActions actId, ActionData data) { + if (!data.TryGetValue("EvtId", out var evtId) || !data.TryGetValue("Value", out var sValue)) { + _logger.LogWarning($"Could not find required action parameters for {actId} from data: {ActionDataToKVPairString(data)}"); + return; + } + // Check for known/imported type, which may have special formatting applied to the name + if (actId != PluginActions.SetKnownSimEvent || !_pluginConfig.TryGetImportedSimEventIdFromSelector(evtId, out evtId)) + evtId = evtId.Trim(); + + Enum eventId; + // dynamically added event actions have no mappings, the ActionEventType.Id is the SimEventClientId + if (actionsDictionary.TryGetValue(evtId, out ActionEventType ev)) { + eventId = ev.Id; + } + else { + ev = new ActionEventType(evtId, Groups.SimSystem, !string.IsNullOrWhiteSpace(sValue), out eventId); + actionsDictionary[evtId] = ev; + _reflectionService.AddSimEventNameMapping(eventId, new SimEventRecord(ev.CategoryId, evtId)); + _simConnectService.MapClientEventToSimEvent(eventId, evtId, ev.CategoryId); // no-op if not connected, will get mapped in the OnConnected handler + } + + if (_simConnectService.IsConnected()) + ProcessSimEvent(ev, eventId, sValue); + } + // Main TP Action event handlers private void ProcessEvent(ActionEvent actionEvent) { @@ -637,30 +650,45 @@ private void ProcessEvent(ActionEvent actionEvent) { return; var dataArry = actionEvent.Data.Values.ToArray(); - if (action.TryGetEventMapping(in dataArry, out Enum eventId)) - ProcessSimEvent(action, eventId, in dataArry); + if (action.TryGetEventMapping(in dataArry, out Enum eventId)) { + string valStr = null; + if (action.ValueIndex > -1 && action.ValueIndex < dataArry.Length) + valStr = dataArry[action.ValueIndex]; + ProcessSimEvent(action, eventId, valStr); + } } private void ProcessInternalEvent(ActionEventType action, ActionData data) { PluginActions pluginEventId = (PluginActions)action.Id; - _logger.LogDebug($"Firing Internal Event - action: {action.ActionId}; enum: {pluginEventId}; data: {string.Join(", ", data.Select(d => $"{d.Key}={d.Value}"))}"); + _logger.LogDebug($"Firing Internal Event - action: {action.ActionId}; enum: {pluginEventId}; data: {ActionDataToKVPairString(data)}"); switch (pluginEventId) { case PluginActions.Connection: case PluginActions.ActionRepeatInterval: { // preserve backwards compatibility with old actions which used indexed data IDs - if ((data.TryGetValue("Action", out var actId) || data.TryGetValue("0", out actId)) && action.TryGetEventMapping(actId, out Enum eventId)) { - if (!(data.TryGetValue("Value", out var sVal) || data.TryGetValue("1", out sVal)) || !double.TryParse(sVal, out var value)) - value = double.NaN; - ProcessPluginCommandAction((PluginActions)eventId, value); - } + if ((data.TryGetValue("Action", out var actId) || data.TryGetValue("0", out actId)) && action.TryGetEventMapping(actId, out Enum eventId)) + ProcessPluginCommandAction((PluginActions)eventId, data); + else + _logger.LogWarning($"Could not parse required action parameters for {action.ActionId} from data: {ActionDataToKVPairString(data)}"); break; } - case PluginActions.SetSimVar: { - if (data.TryGetValue("VarName", out var varName) && data.TryGetValue("Value", out var value)) - SetSimVarValueFromActionData(varName, value, data.TryGetValue("RelAi", out var relAi) && new BooleanString(relAi)); + case PluginActions.SetSimVar: + SetSimVarValueFromActionData(data); + break; + + case PluginActions.SetCustomSimEvent: + case PluginActions.SetKnownSimEvent: + ProcessSimEventFromActionData(pluginEventId, data); + break; + + case PluginActions.AddCustomSimVar: + case PluginActions.AddKnownSimVar: + AddSimVarFromActionData(pluginEventId, data); + break; + + case PluginActions.RemoveSimVar: + RemoveSimVarByActionDataName(data); break; - } case PluginActions.LoadSimVars: { if (data.TryGetValue("VarsFile", out var filepath) && !string.IsNullOrWhiteSpace(filepath)) @@ -669,21 +697,10 @@ private void ProcessInternalEvent(ActionEventType action, ActionData data) { } case PluginActions.SaveSimVars: { - if (data.TryGetValue("VarsFile", out var filepath) && !string.IsNullOrWhiteSpace(filepath)) { - bool customOnly = !action.TryGetEventMapping(data.GetValueOrDefault("VarsSet", "Custom"), out Enum eventId) || (PluginActions)eventId == PluginActions.SaveCustomSimVars; - SaveSimVarsToFile(filepath.Trim(), customOnly); - } - break; + if (data.TryGetValue("VarsFile", out var filepath) && !string.IsNullOrWhiteSpace(filepath)) { + bool customOnly = !action.TryGetEventMapping(data.GetValueOrDefault("VarsSet", "Custom"), out Enum eventId) || (PluginActions)eventId == PluginActions.SaveCustomSimVars; + SaveSimVarsToFile(filepath.Trim(), customOnly); } - - case PluginActions.AddCustomSimVar: - case PluginActions.AddKnownSimVar: - AddSimVarFromActionData(pluginEventId, data); - break; - - case PluginActions.RemoveSimVar: { - if (data.TryGetValue("VarName", out var varName)) - RemoveSimVarByActionDataName(varName); break; } @@ -692,21 +709,20 @@ private void ProcessInternalEvent(ActionEventType action, ActionData data) { } } - private void ProcessSimEvent(ActionEventType action, Enum eventId, in string[] dataArry) { + private void ProcessSimEvent(ActionEventType action, Enum eventId, string value = null) { uint dataUint = 0; - if (action.ValueIndex > -1 && action.ValueIndex < dataArry.Length) { + if (!string.IsNullOrWhiteSpace(value)) { double dataReal = double.NaN; - var valStr = dataArry[action.ValueIndex]; switch (action.ValueType) { case DataType.Number: case DataType.Text: - if (!TryEvaluateValue(valStr, out dataReal)) { + if (!TryEvaluateValue(value, out dataReal)) { _logger.LogWarning($"Data conversion failed for action '{action.ActionId}' on sim event '{_reflectionService.GetSimEventNameById(eventId)}'."); return; } break; case DataType.Switch: - dataReal = new BooleanString(valStr); + dataReal = new BooleanString(value); break; } if (!double.IsNaN(dataReal)) { @@ -774,7 +790,8 @@ public void OnInfoEvent(InfoEvent message) { UpdateUCategoryLists(); UpdateUnitsLists(); - UpdateKnownSimVars(); + UpdateSimVarCategories(); + UpdateSimEventCategories(); } public void OnSettingsEvent(SettingsEvent message) { @@ -821,9 +838,28 @@ public void OnClosedEvent(string message) { } public void OnListChangedEvent(ListChangeEvent message) { - // for now we only need to update the Units list of a SimVar selected in the AddKnownSimVar action - if (!string.IsNullOrWhiteSpace(message.InstanceId) && !string.IsNullOrWhiteSpace(message.Value) && message.ListId.EndsWith(PluginActions.AddKnownSimVar.ToString() + ".Data.VarName")) - UpdateUnitsListForKnownSimVar(message.Value, message.InstanceId); + // Handle dynamic list updates based on value selected in another list, indicated by the message.ListId which is an action data ID. + // There's also a ListChangeEvent.ActionId property, but since our data IDs already contain the action ID, this is moot. + if (string.IsNullOrWhiteSpace(message.InstanceId) || string.IsNullOrWhiteSpace(message.Value) || string.IsNullOrWhiteSpace(message.ListId)) + return; + // get the last 3 parts of the data ID, which is in the form of: .Data. + var listParts = message.ListId.Split('.')[^3..]; + if (listParts.Length != 3 || !Enum.TryParse(listParts[0], true, out PluginActions actId)) + return; + switch (actId) { + case PluginActions.AddKnownSimVar: + if (listParts[2] == "SimCatName") + UpdateKnownSimVars(message.Value, message.InstanceId); + else if (listParts[2] == "VarName") + UpdateUnitsListForKnownSimVar(message.Value, message.InstanceId); + break; + case PluginActions.SetKnownSimEvent: + if (listParts[2] == "SimCatName") + UpdateKnownSimEventsForCategory(message.Value, message.InstanceId); + break; + default: + break; + } } public void OnConnecterChangeEvent(ConnectorChangeEvent message) { @@ -862,6 +898,9 @@ private bool TryEvaluateValue(string strValue, out double value) { return true; } + private IOrderedEnumerable GetSimVarSelectorList(bool settable = false) => + (from s in _statesDictionary.Values where !settable || s.CanSet select Categories.PrependCategoryName(s.CategoryId, s.Name) + $" [{s.Id}]").OrderBy(n => n); + private static bool TryGetSimVarIdFromActionData(string varName, out string varId) { if (varName.EndsWith(']') && (varName.IndexOf('[') is var brIdx && brIdx++ > -1)) { varId = varName[brIdx..^1]; @@ -871,8 +910,8 @@ private static bool TryGetSimVarIdFromActionData(string varName, out string varI return false; } - private static string GetImportedSimVarKeyFromName(string name) => - name?.Split(" - ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Last().Replace(":N", "") ?? string.Empty; + static string ActionDataToKVPairString(ActionData data) => + '{' + string.Join(", ", data.Select(d => $"{d.Key}={d.Value}")) + '}'; #endregion Utilities diff --git a/MSFSTouchPortalPlugin/Types/SimEvent.cs b/MSFSTouchPortalPlugin/Types/SimEvent.cs new file mode 100644 index 0000000..f0077cd --- /dev/null +++ b/MSFSTouchPortalPlugin/Types/SimEvent.cs @@ -0,0 +1,22 @@ + +namespace MSFSTouchPortalPlugin.Types +{ + // This container class is currently used for importing "known" SimConnect events/actions from INI files to present in a UI to the user for selection. + internal class SimEvent + { + /// Unique ID string. + public string Id { get; set; } + /// Category for sorting/organizing. + public string CategoryId { get; set; } + /// Corresponding SimConnect Event name. + public string SimEventName { get; set; } + /// "Friendly" name for UI. + public string Name { get; set; } + /// Long Description. + public string Description { get; set; } + /// "Shared Cockpit" and so on... maybe useful later? + public string System { get; set; } + /// A preformatted string to use in the TP UI when selecting variables. This is set during import. + public string TouchPortalSelectorName { get; set; } + } +} From 05d07600c02df8b000093e85150f3adbd8918367 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Mon, 11 Apr 2022 18:16:03 -0400 Subject: [PATCH 72/93] Update some settable flags in the States.ini config. --- MSFSTouchPortalPlugin/Configuration/States.ini | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/MSFSTouchPortalPlugin/Configuration/States.ini b/MSFSTouchPortalPlugin/Configuration/States.ini index 511e615..70816d8 100644 --- a/MSFSTouchPortalPlugin/Configuration/States.ini +++ b/MSFSTouchPortalPlugin/Configuration/States.ini @@ -91,6 +91,7 @@ Name = "AutoPilot Heading Direction" SimVarName = "AUTOPILOT HEADING LOCK DIR" Unit = "degrees" StringFormat = "F0" +CanSet = True [AutoPilotAltitudeHold] CategoryId = AutoPilot @@ -103,6 +104,7 @@ CategoryId = AutoPilot Name = "AutoPilot Altitude Value" SimVarName = "AUTOPILOT ALTITUDE LOCK VAR" Unit = "feet" +CanSet = True [AutoPilotBackCourseHold] CategoryId = AutoPilot @@ -133,6 +135,7 @@ CategoryId = AutoPilot Name = "AutoPilot Vertical Speed Value" SimVarName = "AUTOPILOT VERTICAL HOLD VAR" Unit = "feet/minute" +CanSet = True [AutoPilotAirSpeedHold] CategoryId = AutoPilot @@ -290,6 +293,7 @@ CategoryId = Electrical Name = "Master Battery Status" SimVarName = "ELECTRICAL MASTER BATTERY" Unit = "Bool" +CanSet = True [LightBeaconOn] CategoryId = Electrical @@ -527,7 +531,6 @@ Name = "RPM - Engine 1" SimVarName = "ENG N1 RPM:1" Unit = "percent" StringFormat = "0.0#" -CanSet = True [RPMN1Engine2] CategoryId = Engine @@ -535,7 +538,6 @@ Name = "RPM - Engine 2" SimVarName = "ENG N1 RPM:2" Unit = "percent" StringFormat = "0.0#" -CanSet = True [RPMN1Engine3] CategoryId = Engine @@ -543,7 +545,6 @@ Name = "RPM - Engine 3" SimVarName = "ENG N1 RPM:3" Unit = "percent" StringFormat = "0.0#" -CanSet = True [RPMN1Engine4] CategoryId = Engine @@ -551,7 +552,6 @@ Name = "RPM - Engine 4" SimVarName = "ENG N1 RPM:4" Unit = "percent" StringFormat = "0.0#" -CanSet = True [RPMPropeller1] CategoryId = Engine @@ -716,7 +716,6 @@ Name = "Air speed indicated in Mach" SimVarName = "AIRSPEED MACH" Unit = "mach" StringFormat = "0.0#" -CanSet = True [PlaneAltitude] CategoryId = FlightInstruments @@ -724,6 +723,7 @@ Name = "Plane Altitude in Feet" SimVarName = "PLANE ALTITUDE" Unit = "feet" StringFormat = "0.#" +CanSet = True [PlaneAltitudeAGL] CategoryId = FlightInstruments @@ -731,6 +731,7 @@ Name = "Plane Altitude AGL in Feet" SimVarName = "PLANE ALT ABOVE GROUND" Unit = "feet" StringFormat = "0.#" +CanSet = True [GroundAltitude] CategoryId = FlightInstruments @@ -745,6 +746,7 @@ Name = "Plane Heading (True North) in Degrees" SimVarName = "PLANE HEADING DEGREES TRUE" Unit = "degrees" StringFormat = "0" +CanSet = True [PlaneHeadingMagnetic] CategoryId = FlightInstruments @@ -752,6 +754,7 @@ Name = "Plane Heading (Magnetic North) in Degrees" SimVarName = "PLANE HEADING DEGREES MAGNETIC" Unit = "degrees" StringFormat = "0" +CanSet = True [PlaneBankAngle] CategoryId = FlightInstruments @@ -759,6 +762,7 @@ Name = "Plane Bank Angle in Degrees" SimVarName = "PLANE BANK DEGREES" Unit = "degrees" StringFormat = "0" +CanSet = True [PlanePitchAngle] CategoryId = FlightInstruments @@ -766,6 +770,7 @@ Name = "Plane Pitch Angle in Degrees" SimVarName = "PLANE PITCH DEGREES" Unit = "degrees" StringFormat = "0" +CanSet = True [VerticalSpeed] CategoryId = FlightInstruments From d38673556834b54a758c97e6592e2419f697a092 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Mon, 11 Apr 2022 18:20:45 -0400 Subject: [PATCH 73/93] [Generator] Make default UI colors a darker blue and a lighter black to help readability; Add command line options to generator to set the colors. --- .../Configuration/GeneratorOptions.cs | 8 ++++++++ MSFSTouchPortalPlugin-Generator/GenerateEntry.cs | 2 ++ MSFSTouchPortalPlugin-Generator/Model/Base.cs | 4 ++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs b/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs index 98f74e0..c2637b8 100644 --- a/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs +++ b/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs @@ -29,6 +29,14 @@ internal class GeneratorOptions "Note that the Default states file (if used) is always read from the 'Configuration' subfolder of this utility.")] public string StateFilesPath { get; set; } + [Option("ColorLight", Default = "0090ff", MetaValue = "", Required = false, + HelpText = "\nColor for the background of action labels and descriptions. Hex color string without the leading '#' sign.")] + public string ColorLight { get; set; } + + [Option("ColorDark", Default = "212121", MetaValue = "", Required = false, + HelpText = "\nColor for the background of editable fields and selection lists. Hex color string without the leading '#' sign.")] + public string ColorDark { get; set; } + [Option('i', "PluginId", Default = "MSFSTouchPortalPlugin", MetaValue = "", Required = false, HelpText = "\nThe Plugin ID string which will be used for all the entry definitions.")] public string PluginId { get; set; } diff --git a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs index 642d04b..2c85791 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs @@ -74,6 +74,8 @@ public void Generate() { }; if (!_options.Debug) model.Plugin_start_cmd = $"{basePath}dist/{_options.PluginId}.exe"; + model.Configuration.ColorDark = "#" + _options.ColorDark.Trim('#'); + model.Configuration.ColorLight = "#" + _options.ColorLight.Trim('#'); var categegoryAttribs = _reflectionSvc.GetCategoryAttributes(); foreach (var catAttrib in categegoryAttribs) { diff --git a/MSFSTouchPortalPlugin-Generator/Model/Base.cs b/MSFSTouchPortalPlugin-Generator/Model/Base.cs index eff1561..82bbe2f 100644 --- a/MSFSTouchPortalPlugin-Generator/Model/Base.cs +++ b/MSFSTouchPortalPlugin-Generator/Model/Base.cs @@ -26,9 +26,9 @@ class Base { class Configuration { [Required, RegularExpression(@"^#[A-Fa-f0-9]{6}$")] - public string ColorDark { get; set; } = "#000000"; + public string ColorDark { get; set; } [Required, RegularExpression(@"^#[A-Fa-f0-9]{6}$")] - public string ColorLight { get; set; } = "#00B4FF"; + public string ColorLight { get; set; } } class TouchPortalCategory { From fc86c7d7556062505e2cc23df6f36ae12eecad34 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Tue, 12 Apr 2022 11:19:58 -0400 Subject: [PATCH 74/93] [SimConnectService] Enable request tracking/diagnostics on a permanent basis (remove DEBUG_REQUESTS macro usage); New helper class RequestTrackingData for passing request and error diagnostics; New OnException event and ExceptionEventHandler delegate type; OnConnect event now passes simulator version information in new SimulatorInfo struct; Connect() now returns hResult type so plugin can determine appropriate action; Handle error case where specified SimConnect.cfg index doesn't exist and cancel further connection attempts; Also handle unknown Connect() exceptions by stopping re-connection attempts. * Increase simulator re-connection delay on failure to 30s. --- .../Interfaces/ISimConnectService.cs | 6 +- .../Services/PluginService.cs | 33 ++- .../Services/SimConnectService.cs | 221 ++++++++++-------- .../Types/RequestTrackingData.cs | 58 +++++ MSFSTouchPortalPlugin/Types/SimulatorInfo.cs | 42 ++++ 5 files changed, 252 insertions(+), 108 deletions(-) create mode 100644 MSFSTouchPortalPlugin/Types/RequestTrackingData.cs create mode 100644 MSFSTouchPortalPlugin/Types/SimulatorInfo.cs diff --git a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs index b87d344..c12d3f9 100644 --- a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs +++ b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs @@ -8,17 +8,19 @@ namespace MSFSTouchPortalPlugin.Interfaces { internal delegate void DataUpdateEventHandler(Definition def, Definition req, object data); - internal delegate void ConnectEventHandler(); + internal delegate void ConnectEventHandler(SimulatorInfo info); internal delegate void DisconnectEventHandler(); + internal delegate void ExceptionEventHandler(RequestTrackingData data); internal interface ISimConnectService { event DataUpdateEventHandler OnDataUpdateEvent; event ConnectEventHandler OnConnect; event DisconnectEventHandler OnDisconnect; + event ExceptionEventHandler OnException; bool IsConnected(); bool AddNotification(Groups group, Enum eventId); - bool Connect(uint configIndex = 0); + uint Connect(uint configIndex = 0); void Disconnect(); bool MapClientEventToSimEvent(Enum eventId, string eventName, Groups group); bool TransmitClientEvent(Groups group, Enum eventId, uint data); diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 6e70760..a4d3b4b 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -26,6 +26,8 @@ internal class PluginService : IPluginService, ITouchPortalEventHandler { public string PluginId => "MSFSTouchPortalPlugin"; // for ITouchPortalEventHandler + const int SIM_RECONNECT_DELAY_SEC = 30; // SimConnect connection attempts delay on failure + private readonly IHostApplicationLifetime _hostApplicationLifetime; private readonly ILogger _logger; private readonly ITouchPortalClient _client; @@ -140,18 +142,31 @@ private bool Initialize() { _simConnectService.OnDataUpdateEvent += SimConnectEvent_OnDataUpdateEvent; _simConnectService.OnConnect += SimConnectEvent_OnConnect; _simConnectService.OnDisconnect += SimConnectEvent_OnDisconnect; + _simConnectService.OnException += SimConnectEvent_OnException; return true; } private async Task SimConnectionMonitor() { _logger.LogDebug("SimConnectionMonitor task started."); + uint hResult; try { while (!_cancellationToken.IsCancellationRequested) { _simConnectionRequest.Wait(_cancellationToken); - if (!_simConnectService.IsConnected() && !_simConnectService.Connect(Settings.SimConnectConfigIndex.UIntValue)) { - _logger.LogWarning("Connection to Simulator failed, retrying in 10 seconds..."); - await Task.Delay(10000, _cancellationToken); // delay 10s on connection error + if (!_simConnectService.IsConnected() && (hResult = _simConnectService.Connect(Settings.SimConnectConfigIndex.UIntValue)) != SimConnectService.S_OK) { + if (hResult != SimConnectService.E_FAIL) { + if (hResult == SimConnectService.E_INVALIDARG) + _logger.LogError("SimConnect returned IVALID ARGUMENT for SimConnect.cfg index value " + Settings.SimConnectConfigIndex.UIntValue.ToString() + + ". Connection attempts aborted. Please fix setting or config. file and retry."); + else + _logger.LogError("Unknown exception occurred trying to connect to SimConnect. Connection attempts aborted, please check plugin logs. Error code/message: " + $"{hResult:X}"); + autoReconnectSimConnect = false; + _simConnectionRequest.Reset(); + UpdateSimConnectState(); + continue; + } + _logger.LogWarning("Connection to Simulator failed, retrying in " + SIM_RECONNECT_DELAY_SEC.ToString() + " seconds..."); + await Task.Delay(SIM_RECONNECT_DELAY_SEC * 1000, _cancellationToken); // delay on connection error } } } @@ -202,13 +217,13 @@ private void CheckPendingRequests() { #region SimConnect Events ///////////////////////////////////// - private void SimConnectEvent_OnConnect() { + private void SimConnectEvent_OnConnect(SimulatorInfo info) { + _logger.LogInformation("Connected to " + info.ToString()); + _simConnectionRequest.Reset(); _simTasksCTS = new CancellationTokenSource(); _simTasksCancelToken = _simTasksCTS.Token; - UpdateSimConnectState(); - // Register Action events var eventMap = _reflectionService.GetClientEventIdToNameMap(); foreach (var m in eventMap) @@ -222,6 +237,8 @@ private void SimConnectEvent_OnConnect() { // start checking timer events _pluginEventsTask = Task.Run(PluginEventsTask); _pluginEventsTask.ConfigureAwait(false); // needed? + + UpdateSimConnectState(); } private void SimConnectEvent_OnDataUpdateEvent(Definition def, Definition req, object data) { @@ -256,6 +273,10 @@ private void SimConnectEvent_OnDisconnect() { _simConnectionRequest.Set(); // re-enable connection attempts } + private void SimConnectEvent_OnException(RequestTrackingData data) { + _logger.LogWarning($"SimConnect Request Exception: " + data.ToString()); + } + #endregion SimConnect Events #region SimVar Setup Handlers ///////////////////////////////////// diff --git a/MSFSTouchPortalPlugin/Services/SimConnectService.cs b/MSFSTouchPortalPlugin/Services/SimConnectService.cs index 4a84f53..3d3e9f3 100644 --- a/MSFSTouchPortalPlugin/Services/SimConnectService.cs +++ b/MSFSTouchPortalPlugin/Services/SimConnectService.cs @@ -1,14 +1,11 @@ - -// define to enable extra request tracking code (for UNKNOWN_ID and similar errors) -//#define DEBUG_REQUESTS - -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using Microsoft.FlightSimulator.SimConnect; using MSFSTouchPortalPlugin.Enums; using MSFSTouchPortalPlugin.Interfaces; using MSFSTouchPortalPlugin.Types; using System; using System.Collections.Generic; +using System.Reflection; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -19,40 +16,66 @@ namespace MSFSTouchPortalPlugin.Services /// Wrapper for SimConnect /// internal class SimConnectService : ISimConnectService, IDisposable { + #region DLL Imports + // get pointer to console window for SimConnect binding [DllImport("kernel32.dll")] static extern IntPtr GetConsoleWindow(); - private readonly ILogger _logger; - private readonly IReflectionService _reflectionService; - - const uint NOTIFICATION_PRIORITY = 10000000; - const int WM_USER_SIMCONNECT = 0x0402; - /// enable AddNotification(), SetNotificationGroupPriorities(), and Simconnect_OnRecvEvent(); currently they serve no purpose except possible debug info. - private static readonly bool DEBUG_NOTIFICATIONS = false; - - private SimConnect _simConnect; - private bool _connected; - private bool _connecting; - private Task _messageWaitTask; - private readonly EventWaitHandle _scReady = new EventWaitHandle(false, EventResetMode.AutoReset); - private readonly List _addedDefinitions = new(); + // Set up tracking requests by their SendID for diagnostic purposes with Simconnect_OnRecvException() + // https://docs.flightsimulator.com/html/Programming_Tools/SimConnect/API_Reference/Debug/SimConnect_GetLastSentPacketID.htm + IntPtr _hSimConnect = IntPtr.Zero; // native SimConnect handle pointer + // Import methods from the actual SimConnect client which aren't available in the C# wrapper. + [DllImport("SimConnect.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] + static extern int /* HRESULT */ SimConnect_GetLastSentPacketID(IntPtr hSimConnect, out uint /* DWORD */ dwSendID); +#pragma warning disable S3011 + // Get a FieldInfo object on the SimConnect.hSimConnect private field variable so that we can query its value to get the handle to the actual SimConnect client. + static readonly FieldInfo _fiSimConnect = typeof(SimConnect).GetField("hSimConnect", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); +#pragma warning restore S3011 + #endregion DLL Imports public event DataUpdateEventHandler OnDataUpdateEvent; public event ConnectEventHandler OnConnect; public event DisconnectEventHandler OnDisconnect; + public event ExceptionEventHandler OnException; + + // hResult + public const uint S_OK = 0; + public const uint E_FAIL = 0x80004005; + public const uint E_INVALIDARG = 0x80070057; + + // maximum stored requests for error tracking (adding is fast but search is 0(n)), should be large enough to handle flood of requests at initial connection + const int MAX_STORED_REQUEST_RECORDS = 500; + + const uint NOTIFICATION_PRIORITY = 10000000; // notification group priority for SimConnect.SetNotificationGroupPriority() + const int WM_USER_SIMCONNECT = 0x0402; // user event ID for SimConnect + + // enable AddNotification(), SetNotificationGroupPriorities(), and Simconnect_OnRecvEvent() for events we initiate; currently this serves no purpose except possible debug info. + static readonly bool DEBUG_NOTIFICATIONS = false; + + readonly ILogger _logger; + readonly IReflectionService _reflectionService; + + bool _connected; + bool _connecting; + int _reqTrackIndex = 0; // current write slot index in _requestTracking array + Task _messageWaitTask; + SimConnect _simConnect; + readonly EventWaitHandle _scReady = new EventWaitHandle(false, EventResetMode.AutoReset); + readonly List _addedDefinitions = new(); // keep track of added SimVar definitions to avoid redundant registrations + readonly RequestTrackingData[] _requestTracking = new RequestTrackingData[MAX_STORED_REQUEST_RECORDS]; // rolling buffer array for request tracking // SimConnect method delegates, for centralized interaction in InvokeSimMethod() - private Action ClearDataDefinitionDelegate; - private Action SetNotificationGroupPriorityDelegate; - private Action AIReleaseControlDelegate; - private Action MapClientEventToSimEventDelegate; - private Action AddClientEventToNotificationGroupDelegate; - private Action RequestDataOnSimObjectTypeDelegate; - private Action TransmitClientEventDelegate; - private Action SetDataOnSimObjectDelegate; - private Action AddToDataDefinitionDelegate; - private Action RequestDataOnSimObjectDelegate; - private readonly Dictionary > _registerDataDelegates = new(); + Action ClearDataDefinitionDelegate; + Action SetNotificationGroupPriorityDelegate; + Action AIReleaseControlDelegate; + Action MapClientEventToSimEventDelegate; + Action AddClientEventToNotificationGroupDelegate; + Action RequestDataOnSimObjectTypeDelegate; + Action TransmitClientEventDelegate; + Action SetDataOnSimObjectDelegate; + Action AddToDataDefinitionDelegate; + Action RequestDataOnSimObjectDelegate; + readonly Dictionary > _registerDataDelegates = new(); public SimConnectService(ILogger logger, IReflectionService reflectionService) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -61,10 +84,11 @@ public SimConnectService(ILogger logger, IReflectionService r public bool IsConnected() => (_connected && _simConnect != null); - public bool Connect(uint configIndex = 0) { + public uint Connect(uint configIndex = 0) { if (_connecting || _simConnect != null) - return _connected; + return _connected ? S_OK : E_FAIL; + uint ret = E_FAIL; _connecting = true; _logger.LogInformation("Connecting to SimConnect..."); @@ -104,23 +128,21 @@ public bool Connect(uint configIndex = 0) { _registerDataDelegates.Add(typeof(long), _simConnect.RegisterDataDefineStruct); _registerDataDelegates.Add(typeof(StringVal), _simConnect.RegisterDataDefineStruct); -#if DEBUG_REQUESTS - DbgSetupRequestTracking(); -#endif + SetupRequestTracking(); //_simConnect.Text(SIMCONNECT_TEXT_TYPE.PRINT_BLACK, 5, Events.StartupMessage, "TouchPortal Connected"); // not currently supported in MSFS SDK _messageWaitTask = Task.Run(ReceiveMessages); - } catch (COMException e) { + ret = S_OK; + } + catch (Exception e) { _connected = false; - _logger.LogDebug("Connection to Simulator failed: {0}", e.Message); + _logger.LogDebug("Connection to SimConnect failed: [{0:X}] {0}", e.HResult, e.Message); + unchecked { ret = (uint)e.HResult; } } _connecting = false; - // Invoke Handler - if (_connected) - OnConnect?.Invoke(); - return _connected; + return ret; } public void Disconnect() { @@ -142,7 +164,7 @@ public void Disconnect() { // Dispose serves the same purpose as SimConnect_Close() try { _simConnect?.Dispose(); - _logger.LogInformation("SimConnect Disconnected"); + _logger.LogDebug("SimConnect disposed"); } catch (Exception e) { _logger.LogWarning(e, "Exception while trying to dispose SimConnect client."); @@ -176,25 +198,23 @@ private bool InvokeSimMethod(Delegate method, params object[] args) { if (method == null || !_connected) return false; try { - _logger.LogTrace($"Invoking: {method.Method.Name}({(args != null ? string.Join(", ", args) : "null")})"); + _logger.LogTrace($"Invoking: {method.Method.Name}({string.Join(", ", args)})"); method.DynamicInvoke(args); -#if DEBUG_REQUESTS - DbgAddSendRecord($"{method.Method.Name}({(args != null ? string.Join(", ", args) : "null")})"); -#endif + AddRequestRecord(method.Method, args); return true; } catch (COMException e) { - _logger.LogWarning($"SimConnect returned an error: [{e.HResult}] {e.Message} r.dwSendId == sendId) ?? new RequestTrackingData(sendId); + } + + #endregion SimConnect Request Tracking + #region IDisposable Support private bool disposedValue; // To detect redundant calls @@ -343,49 +407,6 @@ public void Dispose() { } #endregion IDisposable Support -#if DEBUG_REQUESTS - - // Extra SimConnect functions via native pointer - IntPtr hSimConnect = IntPtr.Zero; - [DllImport("SimConnect.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] - private static extern int /* HRESULT */ SimConnect_GetLastSentPacketID(IntPtr hSimConnect, out uint /* DWORD */ dwSendID); - // for tracking requests by their SendID - private readonly SortedDictionary dbgSendRecordsDict = new(); - -#pragma warning disable S3011 // Reflection should not be used to increase accessibility of classes, methods, or fields - private void DbgSetupRequestTracking() { - // Get direct access to the SimConnect handle, to use functions otherwise not supported. - try { - System.Reflection.FieldInfo fiSimConnect = typeof(SimConnect).GetField("hSimConnect", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - hSimConnect = (IntPtr)fiSimConnect.GetValue(_simConnect); - } - catch (Exception e) { - hSimConnect = IntPtr.Zero; - _logger.LogError(e, $"Exception trying to get handle to SimConnect: {e.Message}"); - } - } -#pragma warning restore S3011 - - private void DbgAddSendRecord(string record) { - if (_simConnect == null || hSimConnect == IntPtr.Zero) - return; - if (SimConnect_GetLastSentPacketID(hSimConnect, out uint dwSendID) == 0) - dbgSendRecordsDict[dwSendID] = record; - // clean out old records - if (dbgSendRecordsDict.Count > 5000) { - // Remove the oldest, first, record. - var enmr = dbgSendRecordsDict.Keys.GetEnumerator(); - enmr.MoveNext(); - dbgSendRecordsDict.Remove(enmr.Current); - } - } - - private string DbgGetSendRecord(uint sendId) { - if (dbgSendRecordsDict.TryGetValue(sendId, out string record)) - return record; - return $"Record not found for SendID {sendId}"; - } - -#endif // DEBUG_REQUESTS } + } diff --git a/MSFSTouchPortalPlugin/Types/RequestTrackingData.cs b/MSFSTouchPortalPlugin/Types/RequestTrackingData.cs new file mode 100644 index 0000000..68f312d --- /dev/null +++ b/MSFSTouchPortalPlugin/Types/RequestTrackingData.cs @@ -0,0 +1,58 @@ +using Microsoft.FlightSimulator.SimConnect; +using System.Reflection; + +namespace MSFSTouchPortalPlugin.Types +{ + /// + /// SimConnect request (method invocation) tracking record for storing which request caused a simulator error (such as unknown variable/event name, etc). + /// + internal class RequestTrackingData + { + public uint dwSendId; // the "dwSendId" from SimConnect_GetLastSentPacketID() and SIMCONNECT_RECV_EXCEPTION struct + public string sMethod; // method name of the invoker + public ParameterInfo[] aParamInfo; // meta data about the invocation method params + public object[] aArguments; // parameter values passed in the invoker method + public SIMCONNECT_EXCEPTION eException; // associated exception, if any, from SIMCONNECT_RECV_EXCEPTION (default is SIMCONNECT_EXCEPTION.NONE) + public uint dwExceptionIndex; // The index number (starting at 1) of the first parameter that caused an error, if any, from SIMCONNECT_RECV_EXCEPTION (0 if unknown) + + public RequestTrackingData(uint sendId, string method, ParameterInfo[] paramInfo, params object[] args) { + dwSendId = sendId; + sMethod = method; + aParamInfo = paramInfo; + aArguments = args; + eException = SIMCONNECT_EXCEPTION.NONE; + dwExceptionIndex = 0; + } + + // constructs a "null" instance with a valid request ID but no tracking data + public RequestTrackingData(uint sendId) : this(sendId, null, null) { } + + /// Returns formated information about the method invocation which triggered the request, and the SimConnect error, if any. + public override string ToString() { + if (sMethod == null) { + var ret = string.Empty; + if (eException != SIMCONNECT_EXCEPTION.NONE) + ret += eException.ToString() + " but: "; + return ret + "Request record not found for SendId " + dwSendId.ToString(); + } + var sb = new System.Text.StringBuilder(150); + if (eException != SIMCONNECT_EXCEPTION.NONE) + sb.Append(eException.ToString()).Append(" for request: "); + sb.Append(sMethod).Append('('); + for (int i = 0, e = aParamInfo.Length; i < e; ++i) { + if (i > 0) + sb.Append(", "); + if (i == dwExceptionIndex - 1) + sb.Append("[@] "); + sb.Append(aParamInfo[i].ParameterType.ToString().Split('.')[^1]).Append(' '). + Append(aParamInfo[i].Name).Append(" = "). + Append(aArguments[i]); + } + sb.Append(')'); + if (dwExceptionIndex > 0) + sb.Append(" ([@] = error source)"); + return sb.ToString(); + } + } + +} diff --git a/MSFSTouchPortalPlugin/Types/SimulatorInfo.cs b/MSFSTouchPortalPlugin/Types/SimulatorInfo.cs new file mode 100644 index 0000000..df28ef3 --- /dev/null +++ b/MSFSTouchPortalPlugin/Types/SimulatorInfo.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MSFSTouchPortalPlugin.Types +{ + struct SimulatorInfo + { + public string ApplicationName; + public uint AppVersionMaj; + public uint AppVersionMin; + public uint AppBuildMaj; + public uint AppBuildMin; + public uint SimConnectVersionMaj; + public uint SimConnectVersionMin; + public uint SimConnectBuildMaj; + public uint SimConnectBuildMin; + public uint SimConnectServerVersion; + + public SimulatorInfo(Microsoft.FlightSimulator.SimConnect.SIMCONNECT_RECV_OPEN data) { + ApplicationName = data.szApplicationName; + AppVersionMaj = data.dwApplicationVersionMajor; + AppVersionMin = data.dwApplicationVersionMinor; + AppBuildMaj = data.dwApplicationBuildMajor; + AppBuildMin = data.dwApplicationBuildMinor; + SimConnectVersionMaj = data.dwSimConnectVersionMajor; + SimConnectVersionMin = data.dwSimConnectVersionMinor; + SimConnectBuildMaj = data.dwSimConnectBuildMajor; + SimConnectBuildMin = data.dwSimConnectBuildMinor; + SimConnectServerVersion = data.dwVersion; + } + + public override string ToString() { + return "Simulator " + + $"\"{ApplicationName}\" v{AppVersionMaj}.{AppVersionMin}.{AppBuildMaj}.{AppBuildMin}" + + ", with SimConnect v" + $"{SimConnectVersionMaj}.{SimConnectVersionMin}.{SimConnectBuildMaj}.{SimConnectBuildMin}" + + $" (Server v{SimConnectServerVersion})"; + } + } +} From 4163edc66c1c05b7eb5a928612b47c4abb2bacdf Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Tue, 12 Apr 2022 19:27:15 -0400 Subject: [PATCH 75/93] [PluginConfig] Improve and optimize sim var name parsing in GetOrCreateImportedSimVariable(). --- .editorconfig | 2 ++ .../Configuration/PluginConfig.cs | 25 ++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/.editorconfig b/.editorconfig index 67c3249..32638f7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,3 +11,5 @@ dotnet_diagnostic.CA1416.severity = none dotnet_diagnostic.S125.severity=none # S3358: Ternary operators should not be nested dotnet_diagnostic.S3358.severity = none +# S1121: Assignments should not be made from within sub-expressions +dotnet_diagnostic.S1121.severity = none diff --git a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs index 5e4354f..b4e3fbd 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs +++ b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs @@ -115,18 +115,31 @@ public bool TryGetImportedSimVarBySelector(string selector, out SimVariable simV return (_importedSimVars.Values.FirstOrDefault(c => c.ContainsKey(selector)) is var cat && cat != null) && cat.TryGetValue(selector, out simVar); } + private static readonly Regex _reSimVarIdFromName = new Regex(@"(?:\b|\W|_)(\w)"); // for creating a PascalCase ID from a SimVar name + public SimVariable GetOrCreateImportedSimVariable(string varName) { if (string.IsNullOrWhiteSpace(varName)) return null; - var simVarName = varName.Trim().Replace(":N", "").Replace("* ", ""); - if ((_importedSimVars.Values.FirstOrDefault(c => c.ContainsKey(simVarName)) is var cat && cat != null) && cat.TryGetValue(simVarName, out SimVariable simVar)) + bool indexed = false; + // "normalize" the name passed from touch portal + var name = varName.Trim(); + // strip leading "exists" indicator "* " + if (name[0] == '*') + name = name[2..]; + // strip trailing single-char index indicator, ":N" or ":i" and such + if ((indexed = name[^2] == ':')) + name = name[..^2]; + // otherwise check and strip ":index" suffix + else if ((indexed = name[^6] == ':' && name[^5..].ToLowerInvariant() == "index")) + name = name[..^6]; + if ((_importedSimVars.Values.FirstOrDefault(c => c.ContainsKey(name)) is var cat && cat != null) && cat.TryGetValue(name, out SimVariable simVar)) return simVar; simVar = new() { // Create a reasonable string for a TP state ID - Id = Regex.Replace(simVarName.ToLower(), @"(?:\b|\W|_)(\w)", m => (m.Groups[1].ToString().ToUpper())), - SimVarName = simVarName, - Name = simVarName, // for lack of anything better - Indexed = varName.Trim().EndsWith(":N") + Id = _reSimVarIdFromName.Replace(name.ToLower(), m => (m.Groups[1].ToString().ToUpper())), + SimVarName = name, + Name = name, // for lack of anything better + Indexed = indexed }; return simVar; } From 567b06345314f3bbfb175b57f30337d03e5bdf82 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Fri, 15 Apr 2022 05:19:12 -0400 Subject: [PATCH 76/93] Create SimVarCollection storage type to manage active SimVarItems in a consolidated manner and offload some functionality from PluginService; Fixes a few issues with dynamic states by tracking all SimVarItems by unique Id instead of dynamic Definition; Store some more meta data in SimVarItem for simpler source tracking and UI presentation; Only load the default state IDs once at startup vs. on every reload and have PluginConfig store them; Fix that PluginConfig.GetOrCreateImportedSimVariable() didn't append index number to Id or SimVarName; Rename SimVars.ini to SimVariables.ini. --- .../Configuration/PluginConfig.cs | 89 +++++--- .../{SimVars.ini => SimVariables.ini} | 0 MSFSTouchPortalPlugin/Enums/DataSourceType.cs | 12 + .../MSFSTouchPortalPlugin.csproj | 2 +- .../Services/PluginService.cs | 141 +++++------- .../Types/SimVarCollection.cs | 206 ++++++++++++++++++ MSFSTouchPortalPlugin/Types/SimVarItem.cs | 31 ++- 7 files changed, 361 insertions(+), 120 deletions(-) rename MSFSTouchPortalPlugin/Configuration/{SimVars.ini => SimVariables.ini} (100%) create mode 100644 MSFSTouchPortalPlugin/Enums/DataSourceType.cs create mode 100644 MSFSTouchPortalPlugin/Types/SimVarCollection.cs diff --git a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs index b4e3fbd..8691c1a 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs +++ b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs @@ -24,10 +24,10 @@ internal class PluginConfig /// public static string RootName { get; set; } = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; - public static string StatesConfigFile { get; set; } = "States.ini"; + public static string StatesConfigFile { get; set; } = "States.ini"; public static string PluginStatesConfigFile { get; set; } = "PluginStates.ini"; - public static string SimVarsImportsFile { get; set; } = "SimVars.ini"; - public static string SimEventsImportsFile { get; set; } = "SimEvents.ini"; + public static string SimVarsImportsFile { get; set; } = "SimVariables.ini"; + public static string SimEventsImportsFile { get; set; } = "SimEvents.ini"; public static string AppRootFolder { get; set; } = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); public static string AppConfigFolder { get; set; } = Path.Combine(AppRootFolder, "Configuration"); @@ -61,6 +61,7 @@ public static string UserStateFiles public static bool HaveUserStateFiles => _userStateFiles.Any(); public static IReadOnlyCollection UserStateFilesArray => _userStateFiles; + public IEnumerable DefaultStateIds { get; private set; } public IEnumerable ImportedSimVarCategoryNames => _importedSimVars.Keys; public IEnumerable ImportedSimEvenCategoryNames => _importedSimEvents.Keys; @@ -72,7 +73,6 @@ public static string UserStateFiles private IReadOnlyDictionary> _importedSimEvents; private readonly ILogger _logger; - public PluginConfig(ILogger logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -83,6 +83,7 @@ public PluginConfig(ILogger logger) { // Loads all imports public void Init() { + DefaultStateIds = LoadSimVarItems(false).Select(s => s.Id); _importedSimVars = ImportSimVars(); _importedSimEvents = ImportSimEvents(); // Check for custom SimConnect.cfg and try copy it to application dir (may require elevated privileges) @@ -110,38 +111,56 @@ public bool TryGetImportedSimVarsForCateogy(string simCategoryName, out IEnumera } public bool TryGetImportedSimVarBySelector(string selector, out SimVariable simVar) { - simVar = null; - selector = selector?.Trim().Replace(":N", "").Replace("* ", "") ?? string.Empty; - return (_importedSimVars.Values.FirstOrDefault(c => c.ContainsKey(selector)) is var cat && cat != null) && cat.TryGetValue(selector, out simVar); + return TryGetImportedSimVarBySelector(selector, out simVar, out _, out _); } private static readonly Regex _reSimVarIdFromName = new Regex(@"(?:\b|\W|_)(\w)"); // for creating a PascalCase ID from a SimVar name - public SimVariable GetOrCreateImportedSimVariable(string varName) { - if (string.IsNullOrWhiteSpace(varName)) - return null; - bool indexed = false; - // "normalize" the name passed from touch portal - var name = varName.Trim(); + public SimVariable GetOrCreateImportedSimVariable(string varName, uint index = 0) { + if (!TryGetImportedSimVarBySelector(varName, out SimVariable simVar, out var name, out var indexed)) { + simVar = new() { + // Create a reasonable string for a TP state ID + Id = _reSimVarIdFromName.Replace(name.ToLower(), m => (m.Groups[1].ToString().ToUpper())), + SimVarName = name, + Name = name, // for lack of anything better + Indexed = indexed + }; + } + simVar.Indexed = simVar.Indexed || index > 0; + if (simVar.Indexed) { + simVar.Id += index.ToString(); + simVar.SimVarName += ":" + Math.Clamp(index, 1, 99).ToString(); + } + return simVar; + } + + private bool TryGetImportedSimVarBySelector(string varName, out SimVariable simVar, out string cleanName, out bool indexed) { + simVar = null; + if (!TryNormalizeVarName(varName, out cleanName, out indexed)) + return false; + foreach (var cat in _importedSimVars.Values) { + if (cat.TryGetValue(cleanName, out simVar)) + return true; + } + return false; + } + + // "normalize" a SimVar name passed from touch portal + private static bool TryNormalizeVarName(string name, out string varName, out bool indexed) { + indexed = false; + varName = name.Trim(); + if (string.IsNullOrEmpty(varName)) + return false; // strip leading "exists" indicator "* " - if (name[0] == '*') - name = name[2..]; + if (varName[0] == '*') + varName = varName[2..]; // strip trailing single-char index indicator, ":N" or ":i" and such - if ((indexed = name[^2] == ':')) - name = name[..^2]; + if ((indexed = varName[^2] == ':')) + varName = varName[..^2]; // otherwise check and strip ":index" suffix - else if ((indexed = name[^6] == ':' && name[^5..].ToLowerInvariant() == "index")) - name = name[..^6]; - if ((_importedSimVars.Values.FirstOrDefault(c => c.ContainsKey(name)) is var cat && cat != null) && cat.TryGetValue(name, out SimVariable simVar)) - return simVar; - simVar = new() { - // Create a reasonable string for a TP state ID - Id = _reSimVarIdFromName.Replace(name.ToLower(), m => (m.Groups[1].ToString().ToUpper())), - SimVarName = name, - Name = name, // for lack of anything better - Indexed = indexed - }; - return simVar; + else if ((indexed = varName[^6] == ':' && varName[^5..].ToLowerInvariant() == "index")) + varName = varName[..^6]; + return true; } // Imported SimEvents methods @@ -204,7 +223,9 @@ public IReadOnlyCollection LoadSimVarItems(bool isUserConfig = true, continue; } simVar.Id = item.Name; + simVar.DataSource = isUserConfig ? DataSourceType.UserFile : DataSourceType.DefaultFile; simVar.TouchPortalStateId = $"{RootName}.{simVar.CategoryId}.State.{simVar.Id}"; + simVar.TouchPortalSelector = Categories.PrependCategoryName(simVar.CategoryId, simVar.Name) + $" [{simVar.Id}]"; // check unique if (ret.FindIndex(s => s.Id == simVar.Id) is int idx && idx > -1) { _logger.LogWarning($"Duplicate SimVar ID found for '{simVar.Id}', overwriting."); @@ -237,7 +258,9 @@ public IReadOnlyCollection LoadPluginStates() => LoadSimVarItems(false, PluginStatesConfigFile); // Save collection to file - public bool SaveSimVarItems(IEnumerable items, bool isUserConfig = true, string filename = default) { + public int SaveSimVarItems(IEnumerable items, bool isUserConfig = true, string filename = default) { + if (!items.Any()) + return 0; var cfg = new SharpConfig.Configuration(); Groups lastCatId = default; int count = 0; @@ -276,9 +299,11 @@ public bool SaveSimVarItems(IEnumerable items, bool isUserConfig = t } filename = GetFullFilePath(filename, isUserConfig); - if (SaveToFile(cfg, filename) is bool ok) + if (SaveToFile(cfg, filename)) _logger.LogDebug($"Saved {count} SimVars to '{filename}'"); - return ok; + else + count = 0; + return count; } diff --git a/MSFSTouchPortalPlugin/Configuration/SimVars.ini b/MSFSTouchPortalPlugin/Configuration/SimVariables.ini similarity index 100% rename from MSFSTouchPortalPlugin/Configuration/SimVars.ini rename to MSFSTouchPortalPlugin/Configuration/SimVariables.ini diff --git a/MSFSTouchPortalPlugin/Enums/DataSourceType.cs b/MSFSTouchPortalPlugin/Enums/DataSourceType.cs new file mode 100644 index 0000000..349c2c2 --- /dev/null +++ b/MSFSTouchPortalPlugin/Enums/DataSourceType.cs @@ -0,0 +1,12 @@ + +namespace MSFSTouchPortalPlugin.Enums +{ + public enum DataSourceType : short + { + None = 0, + DefaultFile, // loaded from default file(s) from plugin distro + UserFile, // loaded from user-provided file, either at startup or dynamically from Action + Dynamic, // added (or edited) by user via TP Action + Imported, // for meta data + } +} diff --git a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj index 1e075fb..e484a64 100644 --- a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj +++ b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj @@ -96,7 +96,7 @@ PreserveNewest - + PreserveNewest diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index a4d3b4b..7583a16 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -46,10 +46,7 @@ internal class PluginService : IPluginService, ITouchPortalEventHandler private Dictionary actionsDictionary = new(); private Dictionary pluginSettingsDictionary = new(); - private readonly ConcurrentDictionary _statesDictionary = new(); - private readonly ConcurrentDictionary _customIntervalStates = new(); // IDs of SimVars which need periodic value polling; concurrent because the polling happens in separate task from setter. - private readonly Dictionary _settableSimVarIds = new(); // mapping of settable SimVar IDs for lookup in statesDictionary - private readonly Dictionary _addedSimVars = new(); // keep track of definitions added by user + private readonly SimVarCollection _statesDictionary = new(); private readonly List _dynamicStateIds = new(); // keep track of dynamically created states for clearing them if/when reloading sim var state files private readonly ConcurrentDictionary _repeatingActionTimers = new(); @@ -203,7 +200,7 @@ private async Task PluginEventsTask() { // runs in PluginEventsTask private void CheckPendingRequests() { - IReadOnlyCollection vars = _customIntervalStates.Values.ToArray(); + var vars = _statesDictionary.PolledUpdateVars; foreach (var simVar in vars) { // Check if a value update is required based on the SimVar's internal tracking mechanism. if (simVar.UpdateRequired) { @@ -243,7 +240,7 @@ private void SimConnectEvent_OnConnect(SimulatorInfo info) { private void SimConnectEvent_OnDataUpdateEvent(Definition def, Definition req, object data) { // Lookup State Mapping. - if (!_statesDictionary.TryGetValue(def, out SimVarItem simVar)) + if (!_statesDictionary.TryGet(def, out SimVarItem simVar)) return; // Update SimVarItem value and TP state on changes. @@ -288,17 +285,13 @@ private void SetupSimVars() { bool allStatesDynamic = (_entryFileType == EntryFileType.NoStates); bool someStateDynamic = false; IReadOnlyCollection simVars; - IEnumerable defaultStates = Array.Empty(); // in case we need to create _some_ custom states dynamically // First check if we're using a custom config. if (PluginConfig.HaveUserStateFiles) { // if the user is NOT using dynamic states for everything (entry_no-states.tp) nor a custom entry.tp file, // then lets figure out what the default states are so we can create any missing ones dynamically if needed. - if (_entryFileType == EntryFileType.Default) { - // get the defaults - defaultStates = _pluginConfig.LoadSimVarItems(false).Select(s => s.Id); - someStateDynamic = defaultStates.Any(); - } + if (_entryFileType == EntryFileType.Default) + someStateDynamic = _pluginConfig.DefaultStateIds.Any(); _logger.LogInformation($"Loading custom state file(s) '{PluginConfig.UserStateFiles}' from '{PluginConfig.UserConfigFolder}'."); } else { // Default config @@ -310,15 +303,14 @@ private void SetupSimVars() { // Now create the SimVars and track them. We're probably not connected to SimConnect at this point so the registration may happen later. foreach (var simVar in simVars) - AddSimVar(simVar, allStatesDynamic || (someStateDynamic && !defaultStates.Contains(simVar.Id)), true); + AddSimVar(simVar, allStatesDynamic || (someStateDynamic && !_pluginConfig.DefaultStateIds.Contains(simVar.Id)), true); - UpdateAllSimVarsList(); - UpdateSettableSimVarsList(); + UpdateSimVarLists(); } private void RegisterAllSimVars() { if (_simConnectService.IsConnected()) - foreach (var simVar in _statesDictionary.Values) + foreach (SimVarItem simVar in _statesDictionary) _simConnectService.RegisterToSimConnect(simVar); } @@ -326,21 +318,16 @@ private bool AddSimVar(SimVarItem simVar, bool dynamicState = true, bool postpon if (simVar == null) return false; - if (_statesDictionary.ContainsKey(simVar.Def)) - RemoveSimVar(simVar.Def, true); + if (_statesDictionary.TryGet(simVar.Id, out var old)) + RemoveSimVar(old, true); // Register it. If the SimVar gets regular updates (not custom polling) then this also starts the data requests for this value. // If we're not connected now then the var will be registered in RegisterAllSimVars() when we do connect. if (_simConnectService.IsConnected() && !_simConnectService.RegisterToSimConnect(simVar)) return false; - _statesDictionary.TryAdd(simVar.Def, simVar); + _statesDictionary.Add(simVar.Def, simVar); - // Does this var need value polling? - if (simVar.NeedsScheduledRequest) - _customIntervalStates.TryAdd(simVar.Def, simVar); - if (simVar.CanSet) - _settableSimVarIds.TryAdd(simVar.Id, simVar.Def); // Need a dynamic state? if (dynamicState && !_dynamicStateIds.Contains(simVar.TouchPortalStateId)) { _dynamicStateIds.Add(simVar.TouchPortalStateId); // keep track for removing them later if needed @@ -348,62 +335,47 @@ private bool AddSimVar(SimVarItem simVar, bool dynamicState = true, bool postpon _logger.LogTrace($"Created dynamic state {simVar.TouchPortalStateId}'."); } - if (!postponeUpdate) { - UpdateAllSimVarsList(); - UpdateSettableSimVarsList(); - } + if (!postponeUpdate) + UpdateSimVarLists(); _logger.LogTrace($"Added SimVar: {simVar.ToDebugString()}"); return true; } // note that the stored list of custom added SimVars is not affected here so that we can reload those during a SimConnect restart - private bool RemoveSimVar(Definition def, bool postponeUpdate = false) { - if (!_statesDictionary.TryRemove(def, out var simVar)) + private bool RemoveSimVar(SimVarItem simVar, bool postponeUpdate = false) { + if (simVar == null || !_statesDictionary.Remove(simVar.Id)) return false; - _customIntervalStates.TryRemove(simVar.Def, out _); - _settableSimVarIds.Remove(simVar.Id); _simConnectService.ClearDataDefinition(simVar.Def); if (_dynamicStateIds.Remove(simVar.TouchPortalStateId)) _client.RemoveState(simVar.TouchPortalStateId); - if (!postponeUpdate) { - UpdateAllSimVarsList(); - UpdateSettableSimVarsList(); - } + if (!postponeUpdate) + UpdateSimVarLists(); return true; } - private bool AddCustomSimVar(SimVarItem simVar) { - if (_addedSimVars.FirstOrDefault(s => s.Value == simVar.Id) is var old && !string.IsNullOrEmpty(old.Value)) { - RemoveSimVar(old.Key, true); - _addedSimVars.Remove(old.Key); - } - return AddSimVar(simVar) && _addedSimVars.TryAdd(simVar.Def, simVar.Id); - } - - private bool RemoveCustomSimVar(SimVarItem simVar) { - if (RemoveSimVar(simVar.Def)) - return _addedSimVars.Remove(simVar.Def); - return false; - } - private void LoadCustomSimVarsFromFile(string filepath) { var simVars = _pluginConfig.LoadSimVarItems(true, filepath); - foreach (var simVar in simVars) { - if (_addedSimVars.ContainsKey(simVar.Def)) - RemoveCustomSimVar(simVar); - AddCustomSimVar(simVar); + if (simVars.Any()) { + foreach (var simVar in simVars) + AddSimVar(simVar, true); + UpdateSimVarLists(); + _logger.LogInformation($"Loaded {simVars.Count} SimVar States from file '{filepath}'"); + } + else { + _logger.LogWarning($"Did not load any SimVar States from file '{filepath}'"); } - _logger.LogInformation($"Loaded {simVars.Count} SimVar States from file '{filepath}'"); } private void SaveSimVarsToFile(string filepath, bool customOnly = true) { - bool ok = false; + int count = 0; if (customOnly) - ok = _pluginConfig.SaveSimVarItems((from s in _statesDictionary.Values where _addedSimVars.ContainsKey(s.Def) select s), true, filepath); + count = _pluginConfig.SaveSimVarItems((from SimVarItem s in _statesDictionary where s.DataSource != DataSourceType.DefaultFile select s), true, filepath); else - ok = _pluginConfig.SaveSimVarItems(_statesDictionary.Values, true, filepath); - if (ok) - _logger.LogInformation($"Saved {(customOnly ? "Custom" : "All")} SimVar States to file '{filepath}'"); + count = _pluginConfig.SaveSimVarItems(_statesDictionary, true, filepath); + if (count > 0) + _logger.LogInformation($"Saved {(customOnly ? $"{count} Custom" : $"All {count}")} SimVar States to file '{filepath}'"); + else + _logger.LogError($"Error saving SimVar States to file '{filepath}'; Please check log messages."); } #endregion SimVar Setup Handlers @@ -417,14 +389,19 @@ private void UpdateActionDataList(PluginActions actionId, string dataId, IEnumer _client.ChoiceUpdate(PluginId + $".Plugin.Action.{actionId}.Data.{dataId}", data.ToArray(), instanceId); } + private void UpdateSimVarLists() { + UpdateAllSimVarsList(); + UpdateSettableSimVarsList(); + } + // List of settable SimVars private void UpdateSettableSimVarsList() { - UpdateActionDataList(PluginActions.SetSimVar, "VarName", GetSimVarSelectorList(settable: true)); + UpdateActionDataList(PluginActions.SetSimVar, "VarName", _statesDictionary.GetSimVarSelectorList(settable: true)); } // List of all current SimVars private void UpdateAllSimVarsList() { - UpdateActionDataList(PluginActions.RemoveSimVar, "VarName", GetSimVarSelectorList(settable: false)); + UpdateActionDataList(PluginActions.RemoveSimVar, "VarName", _statesDictionary.GetSimVarSelectorList(settable: false)); } // Unit lists @@ -448,7 +425,12 @@ void UpdateSimVarCategories() { private void UpdateKnownSimVars(string categoryName, string instanceId) { // select variable names in category and mark if already used if (_pluginConfig.TryGetImportedSimVarsForCateogy(categoryName, out var vars)) { - var list = vars.Select(v => (_statesDictionary.Values.FirstOrDefault(s => s.SimVarName == v.SimVarName) == null ? "" : "* ") + v.TouchPortalSelectorName); + //var list = vars.Select(v => (_statesDictionary.GetBySimName(v.SimVarName) == null ? "" : "* ") + v.TouchPortalSelectorName); + List list = new(vars.Count()); + foreach (var v in vars) { + string pfx = _statesDictionary.GetBySimName(v.SimVarName) == null ? string.Empty : "* "; + list.Add(pfx + v.TouchPortalSelectorName); + } UpdateActionDataList(PluginActions.AddKnownSimVar, "VarName", list, instanceId); } } @@ -551,7 +533,7 @@ private void SetSimVarValueFromActionData(ActionData data) { _logger.LogWarning($"Could not parse required action parameters for {PluginActions.SetSimVar} from data: {ActionDataToKVPairString(data)}"); return; } - if (!_settableSimVarIds.TryGetValue(varId, out Definition def) || !_statesDictionary.TryGetValue(def, out SimVarItem simVar)) { + if (!_statesDictionary.TryGet(varId, out SimVarItem simVar)) { _logger.LogError($"Could not find definition for settable SimVar Id: '{varId}' Name: '{varName}'"); return; } @@ -581,18 +563,14 @@ private void AddSimVarFromActionData(PluginActions actId, ActionData data) { return; } + uint index = 0; + bool haveIndexValue = (data.TryGetValue("VarIndex", out var sIndex) && uint.TryParse(sIndex, out index) && index > 0); + // check if we've imported this var and have meta data - SimVariable impSimVar = _pluginConfig.GetOrCreateImportedSimVariable(varName); + SimVariable impSimVar = _pluginConfig.GetOrCreateImportedSimVariable(varName, index); if (impSimVar == null) // highly unlikely return; - uint index = 0; - bool haveIndexValue = (data.TryGetValue("VarIndex", out var sIndex) && uint.TryParse(sIndex, out index) && index > 0); - impSimVar.Indexed = impSimVar.Indexed || haveIndexValue; - if (impSimVar.Indexed) - impSimVar.SimVarName = string.Concat(impSimVar.SimVarName, ":", Math.Clamp(index, 1, 99).ToString()); - impSimVar.CanSet = impSimVar.CanSet || (data.TryGetValue("CanSet", out var sCanSet) && new BooleanString(sCanSet)); - // create the SimVarItem from collected data var simVar = new SimVarItem() { Id = impSimVar.Id, @@ -600,11 +578,13 @@ private void AddSimVarFromActionData(PluginActions actId, ActionData data) { SimVarName = impSimVar.SimVarName, CategoryId = catId, Unit = sUnit ?? "number", - CanSet = impSimVar.CanSet, + DataSource = DataSourceType.Dynamic, + CanSet = impSimVar.CanSet || (data.TryGetValue("CanSet", out var sCanSet) && new BooleanString(sCanSet)), StringFormat = data.GetValueOrDefault("Format", string.Empty).Trim(), DefaultValue = data.GetValueOrDefault("DfltVal", string.Empty).Trim(), - TouchPortalStateId = $"{PluginId}.{catId}.State.{impSimVar.Id}" }; + simVar.TouchPortalStateId = $"{PluginId}.{catId}.State.{simVar.Id}"; + simVar.TouchPortalSelector = Categories.PrependCategoryName(catId, simVar.Name) + $" [{simVar.Id}]"; if (data.TryGetValue("UpdPer", out var sPeriod) && Enum.TryParse(sPeriod, out UpdatePeriod period)) simVar.UpdatePeriod = period; if (data.TryGetValue("UpdInt", out var sInterval) && uint.TryParse(sInterval, out uint interval)) @@ -612,7 +592,7 @@ private void AddSimVarFromActionData(PluginActions actId, ActionData data) { if (data.TryGetValue("Epsilon", out var sEpsilon) && float.TryParse(sEpsilon, out float epsilon)) simVar.DeltaEpsilon = epsilon; - if (AddCustomSimVar(simVar)) + if (AddSimVar(simVar)) _logger.LogInformation($"Added new SimVar state from action data: {simVar.ToDebugString()}"); else _logger.LogError($"Failed to add SimVar from action data, check previous log messages. Action data: {ActionDataToKVPairString(data)}"); @@ -623,8 +603,8 @@ private void RemoveSimVarByActionDataName(ActionData data) { _logger.LogWarning($"Could not find valid SimVar ID in action data: {ActionDataToKVPairString(data)}'"); return; } - SimVarItem simVar = _statesDictionary.Values.FirstOrDefault(s => s.Id == varId); - if (simVar != null && RemoveSimVar(simVar.Def)) + SimVarItem simVar = _statesDictionary[varId]; + if (simVar != null && RemoveSimVar(simVar)) _logger.LogInformation($"Removed SimVar '{simVar.SimVarName}'"); else _logger.LogWarning($"Could not find definition for settable SimVar Id: '{varId}' from Name: '{varName}'"); @@ -777,7 +757,7 @@ private void ProcessPluginSettings(IReadOnlyCollection settings) { PluginConfig.UserStateFiles = Settings.UserStateFiles.StringValue; // will (re-)set to default if needed. // compare with actual current config values (not Settings) because they may not have changed even if settings string did // states dict will be empty on initial startup - if (!_statesDictionary.Any() || p[0] != PluginConfig.UserConfigFolder || p[1] != PluginConfig.UserStateFiles) + if (_statesDictionary.IsEmpty || p[0] != PluginConfig.UserConfigFolder || p[1] != PluginConfig.UserStateFiles) SetupSimVars(); } @@ -919,11 +899,8 @@ private bool TryEvaluateValue(string strValue, out double value) { return true; } - private IOrderedEnumerable GetSimVarSelectorList(bool settable = false) => - (from s in _statesDictionary.Values where !settable || s.CanSet select Categories.PrependCategoryName(s.CategoryId, s.Name) + $" [{s.Id}]").OrderBy(n => n); - private static bool TryGetSimVarIdFromActionData(string varName, out string varId) { - if (varName.EndsWith(']') && (varName.IndexOf('[') is var brIdx && brIdx++ > -1)) { + if (varName[^1] == ']' && (varName.IndexOf('[') is var brIdx && ++brIdx > 0)) { varId = varName[brIdx..^1]; return true; } diff --git a/MSFSTouchPortalPlugin/Types/SimVarCollection.cs b/MSFSTouchPortalPlugin/Types/SimVarCollection.cs new file mode 100644 index 0000000..525e474 --- /dev/null +++ b/MSFSTouchPortalPlugin/Types/SimVarCollection.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace MSFSTouchPortalPlugin.Types +{ + /// + /// Manages the main collection of SimVarItems for TP States and provides indexed lookup by several properties (Definition, Id, SimVarName) + /// as well as maintaining some reference lists of settable and polled-update SimVars. + /// It provides an enumerable collection of SimVarItems as well as Dictionary-style behavior. + /// + [DebuggerDisplay("Count = {Count}")] + internal sealed class SimVarCollection : ICollection, ICollection, IDictionary, IReadOnlyDictionary + { + public bool IsEmpty => _idxByDef.IsEmpty; + public int Count => _idxByDef.Count; + public IEnumerable Keys => ((IReadOnlyDictionary )_idxByDef).Keys; + public IEnumerable Values => ((IReadOnlyDictionary )_idxByDef).Values; + ICollection IDictionary.Keys => ((IDictionary)_idxByDef).Keys; + ICollection IDictionary.Values => ((IDictionary)_idxByDef).Values; + public bool IsReadOnly => false; + object ICollection.SyncRoot => _idxByDef; + bool ICollection.IsSynchronized => false; + + public IReadOnlyList SettableVars => _settableVars; + + public IReadOnlyList PolledUpdateVars { + get { + int cnt = _polledUpdateVars.Count; + if (cnt == 0) + return Array.Empty(); + SimVarItem[] list = new SimVarItem[cnt]; + lock (_polledUpdateVars) + _polledUpdateVars.CopyTo(list); + return list; + } + } + + const int MAX_CONCURRENCY = 6; + const int INITIAL_CAPACITY = 500; + + // Primary storage by enum value; concurrent since may be used from both the main thread and when data arrives asynchronously from SimConnect; indexed by Definition for quickest lookup when data arrives. + readonly ConcurrentDictionary _idxByDef = new(MAX_CONCURRENCY, INITIAL_CAPACITY); + readonly Dictionary _idxById = new(INITIAL_CAPACITY); // index by string id + readonly Dictionary _idxBySimName = new(INITIAL_CAPACITY); // index by SimVarName + readonly List _settableVars = new(INITIAL_CAPACITY / 4); // list of all settables + readonly List _polledUpdateVars = new(); // list of all vars needing request polling, protected by lock + + SimVarItem IDictionary.this[Definition def] + { + get { + try { return _idxByDef[def]; } + catch { return null; } + } + set { + Remove(def); + Add(value); + } + } + + SimVarItem IReadOnlyDictionary.this[Definition def] { + get { + try { return ((IReadOnlyDictionary)_idxByDef)[def]; } + catch { return null; } + } + } + + public SimVarItem this[string id] + { + get { + try { return _idxById[id]; } + catch { return null; } + } + set { + Remove(id); + Add(value); + } + } + + public SimVarItem Get(Definition def) { + return ((IDictionary)this)[def]; + } + + public SimVarItem Get(string id) { + return this[id]; + } + + public bool TryGet(Definition def, [MaybeNullWhen(false)] out SimVarItem obj) { + return _idxByDef.TryGetValue(def, out obj); + } + + public bool TryGet(string id, [MaybeNullWhen(false)] out SimVarItem obj) { + return _idxById.TryGetValue(id, out obj); + } + + // IDictionary compat + public bool TryGetValue(Definition key, [MaybeNullWhen(false)] out SimVarItem item) { + return TryGet(key, out item); + } + + public SimVarItem GetBySimName(string simVarName) { + try { return _idxBySimName[simVarName]; } + catch { return null; } + } + + public bool TryGetBySimName(string simVarName, [MaybeNullWhen(false)] out SimVarItem item) { + item = GetBySimName(simVarName); + return item != null; + } + + public bool ContainsKey(Definition key) { + return ((IReadOnlyDictionary)_idxByDef).ContainsKey(key); + } + + public bool Contains(SimVarItem item) => item != null && ContainsKey(item.Def); + public bool Contains(Definition key) => ContainsKey(key); + public bool Contains(string id) => _idxById.ContainsKey(id); + public bool Contains(KeyValuePair pair) => ContainsKey(pair.Key); + + public void Add(SimVarItem item) { + if (item != null) { + _idxByDef[item.Def] = item; + _idxById[item.Id] = item; + _idxBySimName[item.SimVarName] = item; + if (item.CanSet) + _settableVars.Add(item); + if (item.NeedsScheduledRequest) { + lock (_polledUpdateVars) + _polledUpdateVars.Add(item); + } + } + } + + // IDictionary + public void Add(Definition key, SimVarItem value) => Add(value); + // IDictionary + public void Add(KeyValuePair pair) => Add(pair.Value); + + public bool TryAdd(SimVarItem item) { + if (Contains(item)) + return false; + Add(item); + return true; + } + + public bool Remove(SimVarItem item) { + bool ret = false; + if (item != null) { + ret = _idxByDef.Remove(item.Def, out _); + _idxById.Remove(item.Id, out _); + _idxBySimName.Remove(item.SimVarName, out _); + if (item.CanSet) + _settableVars.Remove(item); + if (item.NeedsScheduledRequest) { + lock (_polledUpdateVars) + _polledUpdateVars.Remove(item); + } + } + return ret; + } + + public bool Remove(Definition def) => Remove(Get(def)); + public bool Remove(string id) => Remove(Get(id)); + bool ICollection>.Remove(KeyValuePair pair) => Remove(pair.Value); + + public void Clear() { + _idxByDef.Clear(); + _idxById.Clear(); + _idxBySimName.Clear(); + _settableVars.Clear(); + lock (_polledUpdateVars) + _polledUpdateVars.Clear(); + } + + // Utility methods + + public IOrderedEnumerable GetSimVarSelectorList(bool settable = false) { + IEnumerable src = settable ? SettableVars : this; + int cnt = settable ? SettableVars.Count : this.Count; + List list = new(cnt); + foreach (SimVarItem v in src) + list.Add(v.TouchPortalSelector); + return list.OrderBy(n => n); + } + + // Interface implementations + + // IEnumerable + public IEnumerator GetEnumerator() => _idxByDef.Values.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _idxByDef.Values.GetEnumerator(); + // IDictionary + IEnumerator> IEnumerable>.GetEnumerator() => _idxByDef.GetEnumerator(); + + public override bool Equals(object obj) => _idxByDef.Equals(obj); + public override int GetHashCode() => _idxByDef.GetHashCode(); + public override string ToString() => _idxByDef.ToString(); + public void CopyTo(KeyValuePair[] array, int arrayIndex) => ((ICollection)_idxByDef).CopyTo(array, arrayIndex); + public void CopyTo(SimVarItem[] array, int arrayIndex) => _idxByDef.Values.CopyTo(array, arrayIndex); + public void CopyTo(Array array, int index) => ((ICollection)_idxByDef.Values).CopyTo(array, index); + } + +} diff --git a/MSFSTouchPortalPlugin/Types/SimVarItem.cs b/MSFSTouchPortalPlugin/Types/SimVarItem.cs index ebc21c2..15cea77 100644 --- a/MSFSTouchPortalPlugin/Types/SimVarItem.cs +++ b/MSFSTouchPortalPlugin/Types/SimVarItem.cs @@ -15,7 +15,7 @@ public enum Definition /// /// The SimVarItem which defines all data variables for SimConnect /// - public class SimVarItem + public class SimVarItem : System.IComparable, System.IComparable { /// Unique ID string, used to generate TouchPortal state ID (and possibly other uses). public string Id { get; set; } @@ -41,6 +41,10 @@ public class SimVarItem public string TouchPortalValueType { get; set; } = "text"; /// This could/should be populated by whatever is creating the SimVarItem instance public string TouchPortalStateId { get; set; } + /// A string used to identify this var in TP selection lists. This could/should be populated by whatever is creating the SimVarItem instance + public string TouchPortalSelector { get; set; } + /// Tracks the origin of this item for later reference. + public DataSourceType DataSource { get; set; } /// /// SimConnect unit name. Changing this property will clear any current value! @@ -71,19 +75,18 @@ public string StringFormat { get => string.IsNullOrWhiteSpace(_formatString) ? "{0}" : "{0:" + _formatString + "}"; set { - if (value.StartsWith('{')) - value = value.Trim('{', '}'); + value = value.Trim('{', '}'); if (value.StartsWith("0:")) value = value.Remove(0, 2); _formatString = value; } } - /// The current value as an object. May be null; + /// The current value as an object. Read-only, and may be null. Use SetValue() methods to set value. public object Value { get => _value; - set { + private set { _value = value; _lastUpdate = Stopwatch.GetTimestamp(); SetPending(false); @@ -273,9 +276,27 @@ private bool CheckPending() { return true; } + // Object + + public override int GetHashCode() => Id.GetHashCode(); + public override string ToString() => SimVarName; + public string ToDebugString() { return $"{GetType().Name}: {{Def: {Def}; SimVarName: {SimVarName}; Unit: {Unit}; Cat: {CategoryId}; Name: {Name}}}"; } + // IComparable + + public override bool Equals(object obj) => ReferenceEquals(this, obj) || (obj is SimVarItem item && item.Id == Id); + public static bool operator ==(SimVarItem left, SimVarItem right) => left is null ? right is null : left.Equals(right); + public static bool operator !=(SimVarItem left, SimVarItem right) => !(left == right); + public static bool operator <(SimVarItem left, SimVarItem right) => left is null ? right is not null : left.CompareTo(right) < 0; + public static bool operator <=(SimVarItem left, SimVarItem right) => left is null || left.CompareTo(right) <= 0; + public static bool operator >(SimVarItem left, SimVarItem right) => left is not null && left.CompareTo(right) > 0; + public static bool operator >=(SimVarItem left, SimVarItem right) => left is null ? right is null : left.CompareTo(right) >= 0; + + public int CompareTo(object obj) => obj is SimVarItem item ? CompareTo(item) : -1; + public int CompareTo(SimVarItem other) => other is null ? -1 : Id.CompareTo(other.Id); + } } From 83c298be3f901c423c9c92ee58fa2b65a3d97aab Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Fri, 15 Apr 2022 05:37:10 -0400 Subject: [PATCH 77/93] Add initial framework for supporting TP Events. (This commit does not actually implement any yet.) --- .../GenerateDoc.cs | 48 +++++++++++- .../GenerateEntry.cs | 20 +++++ MSFSTouchPortalPlugin-Generator/Model/Base.cs | 20 ++++- .../Model/DocBase.cs | 13 +++- MSFSTouchPortalPlugin/Enums/EventIds.cs | 73 +++++++++++++++++++ MSFSTouchPortalPlugin/Enums/Events.cs | 6 -- .../Interfaces/IReflectionService.cs | 1 + .../Services/ReflectionService.cs | 25 ++++++- .../Types/TouchPortalEvent.cs | 56 ++++++++++++++ 9 files changed, 249 insertions(+), 13 deletions(-) create mode 100644 MSFSTouchPortalPlugin/Enums/EventIds.cs delete mode 100644 MSFSTouchPortalPlugin/Enums/Events.cs create mode 100644 MSFSTouchPortalPlugin/Types/TouchPortalEvent.cs diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index 7ef01d0..f165c6d 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -129,7 +129,7 @@ private DocBase CreateModel() { var categoryStates = simVars.Where(s => s.CategoryId == catAttrib.Id); foreach (SimVarItem state in categoryStates) { var newState = new DocState { - Id = state.TouchPortalStateId.Split('.').Last(), + Id = state.TouchPortalStateId.Split('.')[^1], Type = state.TouchPortalValueType, Description = state.Name, DefaultValue = state.DefaultValue ?? string.Empty, @@ -141,10 +141,25 @@ private DocBase CreateModel() { category.States.Add(newState); } + // Events + var catEvents = _reflectionSvc.GetEvents(catAttrib.Id, fullStateId: false); + foreach (var ev in catEvents) { + var tpEv = new DocEvent { + Id = ev.Id, + Name = ev.Name, + Format = ev.Format.Replace("$val", "$" + ev.ValueType), + ValueType = ev.ValueType, + ValueChoices = ev.ValueChoices, + ValueStateId = ev.ValueStateId.Remove(0, ev.ValueStateId.IndexOf('.') + 1), + }; + category.Events.Add(tpEv); + } + // Sort the actions and states for SimConnect groups - if (catAttrib.Id != MSFSTouchPortalPlugin.Enums.Groups.Plugin) { + if (catAttrib.Id != Groups.Plugin) { category.Actions = category.Actions.OrderBy(c => c.Name).ToList(); category.States = category.States.OrderBy(c => c.Description).ToList(); + category.States = category.States.OrderBy(c => c.Description).ToList(); } } // categories loop @@ -216,7 +231,7 @@ private static string CreateMarkdown(DocBase model) { s.Append($"### {cat.Name}\n
Click to expand\n\n"); // Loop Actions - if (cat.Actions.Count > 0) { + if (cat.Actions.Any()) { s.Append("#### Actions\n\n"); s.Append("\n"); // use HTML table for row valign attribute s.Append("" + @@ -270,8 +285,33 @@ private static string CreateMarkdown(DocBase model) { s.Append("
\n\n\n"); } - if (cat.States.Count > 0) { + if (cat.Events.Any()) { // Loop States + s.Append("#### Events\n\n"); + s.Append($" **Base Id:** {cat.Id}.Event.     **Base State Id** {cat.Id.Split('.')[0]}.\n\n"); + s.Append("\n"); // use HTML table for row valign attribute + s.Append("" + + "" + + "" + + "" + + "" + + "" + + "" + + "\n"); + cat.Events.ForEach(ev => { + s.Append($"" + + $"<" + + $"td>{ev.Name}" + + $"" + + $"" + + $""); + s.Append(""); + s.Append("\n"); + }); + s.Append("
IdNameEvaluated State IdFormatTypeChoice(s)
{ev.Id}{ev.ValueStateId}{ev.Format}{ev.ValueType}").AppendJoin(", ", ev.ValueChoices).Append("
\n\n\n"); + } + + if (cat.States.Any()) { s.Append("#### States\n\n"); s.Append($" **Base Id:** {cat.Id}.State.\n\n"); s.Append("| Id | SimVar Name | Description | Unit | Format | DefaultValue | Settable |\n"); diff --git a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs index 2c85791..c500701 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateEntry.cs @@ -147,9 +147,29 @@ public void Generate() { _logger.LogWarning($"Duplicate state ID found: '{newState.Id}', skipping.'"); } + // Events + var catEvents = _reflectionSvc.GetEvents(catAttrib.Id, fullStateId: true); + foreach (var ev in catEvents) { + var tpEv = new Model.TouchPortalEvent { + Id = category.Id + ".Event." + ev.Id, // these come unqualified + Name = ev.Name, + Format = ev.Format, + Type = ev.Type, + ValueType = ev.ValueType, + ValueChoices = ev.ValueChoices, + ValueStateId =ev.ValueStateId, + }; + // validate unique ID + if (category.Events.FirstOrDefault(s => s.Id == tpEv.Id) == null) + category.Events.Add(tpEv); + else + _logger.LogWarning($"Duplicate Event ID found: '{ev.Id}', skipping.'"); + } + // Sort the actions and states for SimConnect groups if (catAttrib.Id != MSFSTouchPortalPlugin.Enums.Groups.Plugin) { category.Actions = category.Actions.OrderBy(c => c.Name).ToList(); + category.Events = category.Events.OrderBy(c => c.Name).ToList(); category.States = category.States.OrderBy(c => c.Description).ToList(); } } // categories loop diff --git a/MSFSTouchPortalPlugin-Generator/Model/Base.cs b/MSFSTouchPortalPlugin-Generator/Model/Base.cs index 82bbe2f..5adf4b9 100644 --- a/MSFSTouchPortalPlugin-Generator/Model/Base.cs +++ b/MSFSTouchPortalPlugin-Generator/Model/Base.cs @@ -38,7 +38,7 @@ class TouchPortalCategory { public string Name { get; set; } public string Imagepath { get; set; } = string.Empty; public List Actions { get; set; } = new List(); - public List Events { get; set; } = new List(); + public List Events { get; set; } = new List(); public List States { get; set; } = new List(); } @@ -93,6 +93,24 @@ class TouchPortalState { public string DefaultValue { get; set; } } + public class TouchPortalEvent + { + [Required, MinLength(5)] + public string Id { get; set; } + [Required, MinLength(5)] + public string Name { get; set; } + [Required, MinLength(5)] + public string Format { get; set; } + [Required] + public string Type { get; set; } + [Required] + public string ValueType { get; set; } + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public virtual string[] ValueChoices { get; set; } + [Required, MinLength(5)] + public string ValueStateId { get; set; } + } + class TouchPortalSetting { [Required, MinLength(5)] diff --git a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs index 63aab7b..1b1eea9 100644 --- a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs +++ b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs @@ -27,7 +27,7 @@ public class DocCategory { public string Name { get; set; } public List Actions { get; set; } = new(); public List States { get; set; } = new(); - public List Events { get; set; } = new(); + public List Events { get; set; } = new(); } public class DocAction { @@ -65,4 +65,15 @@ public class DocState { public string FormattingString { get; set; } public bool CanSet { get; set; } } + + public class DocEvent + { + public string Id { get; set; } + public string Name { get; set; } + public string Format { get; set; } + public string ValueType { get; set; } + public virtual string[] ValueChoices { get; set; } + public string ValueStateId { get; set; } + } + } diff --git a/MSFSTouchPortalPlugin/Enums/EventIds.cs b/MSFSTouchPortalPlugin/Enums/EventIds.cs new file mode 100644 index 0000000..689c043 --- /dev/null +++ b/MSFSTouchPortalPlugin/Enums/EventIds.cs @@ -0,0 +1,73 @@ + +namespace MSFSTouchPortalPlugin.Enums +{ + public enum EventIds : short + { + None = 0, + + // Plugin events + SimConnecting, // attempting to connect + SimConnected, + SimDisconnected, + SimTimedOut, // connection error, sim not running + SimError, // SimConnect error + PluginError, // internal plugin error + + // SimConnect events (must match names accepted by SimConnect_SubscribeToSystemEvent, see comment block at EOF) + SimEventNone, // marker + //1sec, + //4sec, + //6Hz, + AircraftLoaded, + Crashed, + CrashReset, + FlightLoaded, + FlightSaved, + FlightPlanActivated, + FlightPlanDeactivated, + //Frame, // no notification + Pause, + Paused, + //PauseFrame, // no notification + PositionChanged, + Sim, + SimStart, + SimStop, + Sound, + Unpaused, + View, // this one is actually 2 events, ViewCockpit or ViewExternal (below) + SimEventLast, // marker + + // "virtual" events + ViewCockpit, // View event if dwData == 2 + ViewExternal, // View event if dwData == 0 + + // start marker for dynamically generated events (TP actions) + DynamicEventInit = 1000, + } +} + +/* https://docs.flightsimulator.com/html/Programming_Tools/SimConnect/API_Reference/Events_And_Data/SimConnect_SubscribeToSystemEvent.htm + +1sec Request a notification every second. +4sec Request a notification every four seconds. +6Hz Request notifications six times per second. This is the same rate that joystick movement events are transmitted. +AircraftLoaded Request a notification when the aircraft flight dynamics file is changed. These files have a .AIR extension. The filename is returned in a SIMCONNECT_RECV_EVENT_FILENAME structure. +Crashed Request a notification if the user aircraft crashes. +CrashReset Request a notification when the crash cut-scene has completed. +FlightLoaded Request a notification when a flight is loaded. Note that when a flight is ended, a default flight is typically loaded, so these events will occur when flights and missions are started and finished. The filename of the flight loaded is returned in a SIMCONNECT_RECV_EVENT_FILENAME structure. +FlightSaved Request a notification when a flight is saved correctly. The filename of the flight saved is returned in a SIMCONNECT_RECV_EVENT_FILENAME structure. +FlightPlanActivated Request a notification when a new flight plan is activated. The filename of the activated flight plan is returned in a SIMCONNECT_RECV_EVENT_FILENAME structure. +FlightPlanDeactivated Request a notification when the active flight plan is de-activated. +Frame Request notifications every visual frame. Information is returned in a SIMCONNECT_RECV_EVENT structure. +Pause Request notifications when the flight is paused or unpaused, and also immediately returns the current pause state (1 = paused or 0 = unpaused). The state is returned in the dwData parameter. +Paused Request a notification when the flight is paused. +PauseFrame Request notifications for every visual frame that the simulation is paused. Information is returned in a SIMCONNECT_RECV_EVENT structure. +PositionChanged Request a notification when the user changes the position of their aircraft through a dialog. +Sim Request notifications when the flight is running or not, and also immediately returns the current state (1 = running or 0 = not running). The state is returned in the dwData parameter. +SimStart The simulator is running. Typically the user is actively controlling the aircraft on the ground or in the air. However, in some cases additional pairs of SimStart/SimStop events are sent. For example, when a flight is reset the events that are sent are SimStop, SimStart, SimStop, SimStart. Also when a flight is started with the SHOW_OPENING_SCREEN value set to zero, then an additional SimStart/SimStop pair are sent before a second SimStart event is sent when the scenery is fully loaded. The opening screen provides the options to change aircraft, departure airport, and so on. +SimStop The simulator is not running. Typically the user is loading a flight, navigating the shell or in a dialog. +Sound Requests a notification when the master sound switch is changed. This request will also return the current state of the master sound switch immediately. A flag is returned in the dwData parameter, 0 if the switch is off, SIMCONNECT_SOUND_SYSTEM_EVENT_DATA_MASTER (0x1) if the switch is on. +Unpaused Request a notification when the flight is un-paused. +View Requests a notification when the user aircraft view is changed. This request will also return the current view immediately. A flag is returned in the dwData parameter, one of: SIMCONNECT_VIEW_SYSTEM_EVENT_DATA_COCKPIT_2D SIMCONNECT_VIEW_SYSTEM_EVENT_DATA_COCKPIT_VIRTUAL SIMCONNECT_VIEW_SYSTEM_EVENT_DATA_ORTHOGONAL (the map view). + */ diff --git a/MSFSTouchPortalPlugin/Enums/Events.cs b/MSFSTouchPortalPlugin/Enums/Events.cs deleted file mode 100644 index a2edc02..0000000 --- a/MSFSTouchPortalPlugin/Enums/Events.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MSFSTouchPortalPlugin.Enums { - internal enum Events { - StartupMessage = 0, - SimVars = 1 - } -} diff --git a/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs b/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs index 841f61e..b76bb56 100644 --- a/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs +++ b/MSFSTouchPortalPlugin/Interfaces/IReflectionService.cs @@ -16,5 +16,6 @@ internal interface IReflectionService { void AddSimEventNameMapping(Enum id, SimEventRecord record); IEnumerable GetActionAttributes(Groups catId); IEnumerable GetCategoryAttributes(); + IEnumerable GetEvents(Groups catId, bool fullStateId = false); } } diff --git a/MSFSTouchPortalPlugin/Services/ReflectionService.cs b/MSFSTouchPortalPlugin/Services/ReflectionService.cs index 36b3599..3047c65 100644 --- a/MSFSTouchPortalPlugin/Services/ReflectionService.cs +++ b/MSFSTouchPortalPlugin/Services/ReflectionService.cs @@ -62,6 +62,24 @@ public IEnumerable GetActionAttributes(Groups catId) return ret; } + public IEnumerable GetEvents(Groups catId, bool fullStateId = false) { + List ret = new(); + var container = _assemblyTypes.Where(t => t.IsClass && t.GetCustomAttribute()?.Id == catId); + foreach (Type c in container) { + var fields = c.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); + foreach (FieldInfo field in fields) { + if (field.FieldType == typeof(TouchPortalEvent) && ((TouchPortalEvent)field.GetValue(null) is var ev && ev != null)) { + if (ev.ValueStateId?.IndexOf('.') < 0) + ev.ValueStateId = catId.ToString() + ".State." + ev.ValueStateId; // qualify with category name, but not plugin name (which is assumed) + if (fullStateId) + ev.ValueStateId = TouchPortalBaseId + '.' + ev.ValueStateId; // ok actually add the plugin name also, this is for the generators + ret.Add(ev); + } + } + } + return ret; + } + private Dictionary GetInternalActionEvents() { var returnDict = new Dictionary(); var catAttribs = GetActionAttributes(Groups.Plugin); @@ -91,12 +109,14 @@ private Dictionary GetInternalActionEvents() { } } } + _logger.LogDebug($"Loaded {returnDict.Count} Internal Actions"); return returnDict; } public Dictionary GetActionEvents() { var returnDict = GetInternalActionEvents(); var catAttribs = GetCategoryAttributes(); + var intActsCnt = returnDict.Count; foreach (var catAttr in catAttribs) { if (catAttr.Id == Groups.Plugin) @@ -153,13 +173,14 @@ public Dictionary GetActionEvents() { } // actions loop } // categories loop + _logger.LogDebug($"Loaded {returnDict.Count - intActsCnt} SimConnect Actions"); return returnDict; } public Dictionary GetSettings() { Dictionary returnDict = new(); - var setContainers = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsClass && t.GetCustomAttribute() != null); + var setContainers = _assemblyTypes.Where(t => t.IsClass && t.GetCustomAttribute() != null); foreach (Type setCtr in setContainers) { var settingFields = setCtr.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); foreach (FieldInfo field in settingFields) { @@ -173,6 +194,8 @@ public Dictionary GetSettings() { return returnDict; } + + } } diff --git a/MSFSTouchPortalPlugin/Types/TouchPortalEvent.cs b/MSFSTouchPortalPlugin/Types/TouchPortalEvent.cs new file mode 100644 index 0000000..75daade --- /dev/null +++ b/MSFSTouchPortalPlugin/Types/TouchPortalEvent.cs @@ -0,0 +1,56 @@ +using MSFSTouchPortalPlugin.Enums; +using System.Collections.Generic; +using System.Linq; + +namespace MSFSTouchPortalPlugin.Types +{ + public class TouchPortalEvent + { + // Touch Portal properties + public string Id { get; set; } + public string Name { get; set; } + public string Format { get; set; } + public string Type { get; set; } = "communicate"; + public string ValueType => DataType.ToString().ToLower(); + public string ValueStateId { get; set; } + public string[] ValueChoices { + get => _choices; + set { + _choices = value; + _mappings = null; + } + } + + // Plugin-specific properties, etc. + + public DataType DataType { get; set; } = DataType.Choice; + + public Dictionary ChoiceMappings + { + get => _mappings; + set { + _mappings = value; + _choices = value?.Values.ToArray(); + } + } + + string[] _choices = null; + Dictionary _mappings = null; + + public TouchPortalEvent(string id, string name, string format, string stateId = null) { + Id = id; + Name = name; + Format = format; + ValueStateId = stateId ?? id; + } + + public TouchPortalEvent(string id, string name, string format, string[] choices, string stateId = null) : this(id, name, format, stateId) { + ValueChoices = choices; + } + + public TouchPortalEvent(string id, string name, string format, Dictionary mappings, string stateId = null) : this(id, name, format, stateId) { + ChoiceMappings = mappings; + } + } + +} From e7ba3f4113efe4ca350923f5336857d93808ebb9 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Fri, 15 Apr 2022 05:41:00 -0400 Subject: [PATCH 78/93] [SimConnectService] Add support for subscribing to SimConnect system events and deliver them to client with new OnEventReceived handler. (https://docs.flightsimulator.com/html/Programming_Tools/SimConnect/API_Reference/Events_And_Data/SimConnect_SubscribeToSystemEvent.htm) --- .../Interfaces/ISimConnectService.cs | 5 ++- .../Services/SimConnectService.cs | 38 ++++++++++++++----- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs index c12d3f9..dd2d744 100644 --- a/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs +++ b/MSFSTouchPortalPlugin/Interfaces/ISimConnectService.cs @@ -8,12 +8,14 @@ namespace MSFSTouchPortalPlugin.Interfaces { internal delegate void DataUpdateEventHandler(Definition def, Definition req, object data); + internal delegate void RecvEventEventHandler(EventIds evenId, Groups categoryId, object data); internal delegate void ConnectEventHandler(SimulatorInfo info); internal delegate void DisconnectEventHandler(); internal delegate void ExceptionEventHandler(RequestTrackingData data); - internal interface ISimConnectService { + internal interface ISimConnectService : IDisposable { event DataUpdateEventHandler OnDataUpdateEvent; + event RecvEventEventHandler OnEventReceived; event ConnectEventHandler OnConnect; event DisconnectEventHandler OnDisconnect; event ExceptionEventHandler OnException; @@ -32,5 +34,6 @@ internal interface ISimConnectService { bool SetDataOnSimObject(SimVarItem simVar, uint objectId = (uint)SIMCONNECT_SIMOBJECT_TYPE.USER); bool ReleaseAIControl(Definition def, uint objectId = (uint)SIMCONNECT_SIMOBJECT_TYPE.USER); bool ClearDataDefinition(Definition def); + bool SubscribeToSystemEvent(Enum eventId, string eventName); } } diff --git a/MSFSTouchPortalPlugin/Services/SimConnectService.cs b/MSFSTouchPortalPlugin/Services/SimConnectService.cs index 3d3e9f3..8cf3004 100644 --- a/MSFSTouchPortalPlugin/Services/SimConnectService.cs +++ b/MSFSTouchPortalPlugin/Services/SimConnectService.cs @@ -15,7 +15,7 @@ namespace MSFSTouchPortalPlugin.Services /// /// Wrapper for SimConnect /// - internal class SimConnectService : ISimConnectService, IDisposable { + internal class SimConnectService : ISimConnectService { #region DLL Imports // get pointer to console window for SimConnect binding [DllImport("kernel32.dll")] @@ -34,6 +34,7 @@ internal class SimConnectService : ISimConnectService, IDisposable { #endregion DLL Imports public event DataUpdateEventHandler OnDataUpdateEvent; + public event RecvEventEventHandler OnEventReceived; public event ConnectEventHandler OnConnect; public event DisconnectEventHandler OnDisconnect; public event ExceptionEventHandler OnException; @@ -42,6 +43,10 @@ internal class SimConnectService : ISimConnectService, IDisposable { public const uint S_OK = 0; public const uint E_FAIL = 0x80004005; public const uint E_INVALIDARG = 0x80070057; + // SIMCONNECT_RECV_EVENT.dwData value for "View" event, from SimConnect.h + public const uint VIEW_EVENT_DATA_COCKPIT_2D = 0x00000001; // 2D Panels in cockpit view + public const uint VIEW_EVENT_DATA_COCKPIT_3D = 0x00000002; // Virtual (3D) panels in cockpit view + public const uint VIEW_EVENT_DATA_ORTHOGONAL = 0x00000004; // Orthogonal (Map) view // maximum stored requests for error tracking (adding is fast but search is 0(n)), should be large enough to handle flood of requests at initial connection const int MAX_STORED_REQUEST_RECORDS = 500; @@ -70,6 +75,7 @@ internal class SimConnectService : ISimConnectService, IDisposable { Action AIReleaseControlDelegate; Action MapClientEventToSimEventDelegate; Action AddClientEventToNotificationGroupDelegate; + Action SubscribeToSystemEventDelegate; Action RequestDataOnSimObjectTypeDelegate; Action TransmitClientEventDelegate; Action SetDataOnSimObjectDelegate; @@ -103,8 +109,8 @@ public uint Connect(uint configIndex = 0) { _simConnect.OnRecvException += new SimConnect.RecvExceptionEventHandler(Simconnect_OnRecvException); // Sim mapped events - if (DEBUG_NOTIFICATIONS) - _simConnect.OnRecvEvent += new SimConnect.RecvEventEventHandler(Simconnect_OnRecvEvent); + _simConnect.OnRecvEvent += Simconnect_OnRecvEvent; + _simConnect.OnRecvEventFilename += Simconnect_OnRecvFilename; // Sim Data _simConnect.OnRecvSimobjectDataBytype += new SimConnect.RecvSimobjectDataBytypeEventHandler(Simconnect_OnRecvSimobjectDataBytype); @@ -121,6 +127,7 @@ public uint Connect(uint configIndex = 0) { RequestDataOnSimObjectTypeDelegate = _simConnect.RequestDataOnSimObjectType; SetDataOnSimObjectDelegate = _simConnect.SetDataOnSimObject; AIReleaseControlDelegate = _simConnect.AIReleaseControl; + SubscribeToSystemEventDelegate = _simConnect.SubscribeToSystemEvent; _registerDataDelegates.Clear(); _registerDataDelegates.Add(typeof(double), _simConnect.RegisterDataDefineStruct); @@ -297,6 +304,10 @@ public bool ReleaseAIControl(Definition def, uint objectId = (uint)SIMCONNECT_SI return InvokeSimMethod(AIReleaseControlDelegate, objectId, def); } + public bool SubscribeToSystemEvent(Enum eventId, string eventName) { + return InvokeSimMethod(SubscribeToSystemEventDelegate, eventId, eventName); + } + #region SimConnect Event Handlers private void Simconnect_OnRecvQuit(SimConnect sender, SIMCONNECT_RECV data) { @@ -322,17 +333,26 @@ private void Simconnect_OnRecvException(SimConnect sender, SIMCONNECT_RECV_EXCEP RequestTrackingData record = GetRequestRecord(data.dwSendID); record.eException = (SIMCONNECT_EXCEPTION)data.dwException; record.dwExceptionIndex = data.dwIndex; + _logger.LogDebug($"SimConnect Error: {record.eException}; SendID: {data.dwSendID}; Index: {data.dwIndex};"); OnException?.Invoke(record); } private void Simconnect_OnRecvEvent(SimConnect sender, SIMCONNECT_RECV_EVENT data) { - string grpName = data.uGroupID.ToString(); - string eventId = data.uEventID.ToString(); - if (Enum.IsDefined(typeof(Groups), (int)data.uGroupID)) { - grpName = ((Groups)data.uGroupID).ToString(); - eventId = _reflectionService.GetSimEventNameById(data.uEventID); + EventIds evId = (EventIds)data.uEventID; + Groups gId = (Groups)data.uGroupID; + string evName = evId.ToString(); + if (DEBUG_NOTIFICATIONS && !Enum.IsDefined(evId) && Enum.IsDefined(gId)) { + evName = _reflectionService.GetSimEventNameById(data.uEventID); } - _logger.LogDebug($"Simconnect_OnRecvEvent Received: Group: {grpName}; Event: {eventId}"); + _logger.LogDebug($"Simconnect_OnRecvEvent Received: Group: {gId}; Event: {evName}; Data: {data.dwData}"); + OnEventReceived?.Invoke(evId, gId, data.dwData); + } + + private void Simconnect_OnRecvFilename(SimConnect sender, SIMCONNECT_RECV_EVENT_FILENAME data) { + EventIds evId = (EventIds)data.uEventID; + Groups gId = (Groups)data.uGroupID; + _logger.LogDebug($"Simconnect_OnRecvFilename Received: Group: {gId}; Event: {evId}; Data: {data.szFileName}"); + OnEventReceived?.Invoke(evId, gId, data.szFileName); } #endregion SimConnect Event Handlers From 7195a92630c7b98b2fd4c2bbf16b82a0371ac240 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sat, 16 Apr 2022 04:56:40 -0400 Subject: [PATCH 79/93] [PluginService] Fix for properly exiting SimConnect re-connection attempt wait delay, using multiple wait handles instead of async wait; Cosmetics. --- .../Services/PluginService.cs | 96 +++++++++++-------- 1 file changed, 55 insertions(+), 41 deletions(-) diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 7583a16..c687e8c 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -28,6 +28,8 @@ internal class PluginService : IPluginService, ITouchPortalEventHandler const int SIM_RECONNECT_DELAY_SEC = 30; // SimConnect connection attempts delay on failure + private enum EntryFileType { Default, NoStates, Custom }; + private readonly IHostApplicationLifetime _hostApplicationLifetime; private readonly ILogger _logger; private readonly ITouchPortalClient _client; @@ -35,29 +37,25 @@ internal class PluginService : IPluginService, ITouchPortalEventHandler private readonly IReflectionService _reflectionService; private readonly PluginConfig _pluginConfig; - private CancellationToken _cancellationToken; - private CancellationTokenSource _simTasksCTS; - private CancellationToken _simTasksCancelToken; - private Task _pluginEventsTask; - private readonly ManualResetEventSlim _simConnectionRequest = new(false); - private bool autoReconnectSimConnect = false; - private EntryFileType _entryFileType = EntryFileType.Default; - private bool _quitting; + private CancellationToken _cancellationToken; // main run enable token passed from Program startup in StartAsync() + private CancellationTokenSource _simTasksCTS; // for _simTasksCancelToken + private CancellationToken _simTasksCancelToken; // used to shut down local task(s) only needed while simulator is connected + private Task _pluginEventsTask; // runs timed events needed while simulator is connected + private readonly ManualResetEventSlim _simConnectionRequest = new(false); // is Set when connection should be attempted, SimConnectionMonitor task will wait until this, or cancellation token, is set. + private readonly ManualResetEventSlim _simAutoConnectDisable = new(true); // basically the opposite, used to break out of the reconnection wait timeout and as a flag to indicate if _simConnectionRequest should be Set (eg. at startup). private Dictionary actionsDictionary = new(); private Dictionary pluginSettingsDictionary = new(); private readonly SimVarCollection _statesDictionary = new(); private readonly List _dynamicStateIds = new(); // keep track of dynamically created states for clearing them if/when reloading sim var state files - private readonly ConcurrentDictionary _repeatingActionTimers = new(); + private readonly ConcurrentDictionary _repeatingActionTimers = new(); // storage for temporary repeating (held) action timers, index by action ID + + private bool _quitting; // prevent recursion at shutdown + private EntryFileType _entryFileType = EntryFileType.Default; // detected entry.tp States configuration type private static readonly System.Data.DataTable _expressionEvaluator = new(); // used to evaluate basic math in action data - private enum EntryFileType { Default, NoStates, Custom }; - /// - /// Constructor - /// - /// Message Processor Object public PluginService(IHostApplicationLifetime hostApplicationLifetime, ILogger logger, ITouchPortalClientFactory clientFactory, ISimConnectService simConnectService, IReflectionService reflectionService, PluginConfig pluginConfig) @@ -96,7 +94,7 @@ public Task StartAsync(CancellationToken cancellationToken) { //Environment.Exit(0); }; - if (autoReconnectSimConnect) + if (!_simAutoConnectDisable.IsSet) _simConnectionRequest.Set(); // enable connection attempts return Task.WhenAll(SimConnectionMonitor()); } @@ -111,12 +109,12 @@ public Task StopAsync(CancellationToken cancellationToken) { // Shut down _quitting = true; _logger.LogDebug("Shutting down..."); - _simConnectionRequest.Reset(); // just in case - _simConnectService?.Disconnect(); + DisconnectSimConnect(); if (_client?.IsConnected ?? false) { try { _client.Close(); } catch (Exception) { /* ignore */ } } + _simConnectService?.Dispose(); _logger.LogInformation($"======= {PluginId} Stopped ======="); return Task.CompletedTask; } @@ -144,35 +142,39 @@ private bool Initialize() { return true; } - private async Task SimConnectionMonitor() { + private Task SimConnectionMonitor() { _logger.LogDebug("SimConnectionMonitor task started."); uint hResult; + // reconnection delay WaitHandle will exit on any of these handles being set (or SIM_RECONNECT_DELAY_SEC timeout) + var waitHandles = new WaitHandle[] { _cancellationToken.WaitHandle, _simAutoConnectDisable.WaitHandle }; try { while (!_cancellationToken.IsCancellationRequested) { _simConnectionRequest.Wait(_cancellationToken); if (!_simConnectService.IsConnected() && (hResult = _simConnectService.Connect(Settings.SimConnectConfigIndex.UIntValue)) != SimConnectService.S_OK) { if (hResult != SimConnectService.E_FAIL) { + DisconnectSimConnect(); if (hResult == SimConnectService.E_INVALIDARG) - _logger.LogError("SimConnect returned IVALID ARGUMENT for SimConnect.cfg index value " + Settings.SimConnectConfigIndex.UIntValue.ToString() + + _logger.LogError((int)EventIds.SimError, + "SimConnect returned IVALID ARGUMENT for SimConnect.cfg index value " + Settings.SimConnectConfigIndex.UIntValue.ToString() + ". Connection attempts aborted. Please fix setting or config. file and retry."); else - _logger.LogError("Unknown exception occurred trying to connect to SimConnect. Connection attempts aborted, please check plugin logs. Error code/message: " + $"{hResult:X}"); - autoReconnectSimConnect = false; - _simConnectionRequest.Reset(); - UpdateSimConnectState(); + _logger.LogError((int)EventIds.SimError, + "Unknown exception occurred trying to connect to SimConnect. Connection attempts aborted, please check plugin logs. " + + "Error code/message: " + $"{hResult:X}"); continue; } - _logger.LogWarning("Connection to Simulator failed, retrying in " + SIM_RECONNECT_DELAY_SEC.ToString() + " seconds..."); - await Task.Delay(SIM_RECONNECT_DELAY_SEC * 1000, _cancellationToken); // delay on connection error + _logger.LogWarning((int)EventIds.SimTimedOut, "Connection to Simulator failed, retrying in " + SIM_RECONNECT_DELAY_SEC.ToString() + " seconds..."); + WaitHandle.WaitAny(waitHandles, SIM_RECONNECT_DELAY_SEC * 1000); // delay on connection error } } } catch (OperationCanceledException) { /* ignore but exit */ } catch (ObjectDisposedException) { /* ignore but exit */ } catch (Exception e) { - _logger.LogError(e, "Exception in SimConnectionMonitor task, cannot continue."); + _logger.LogError(e, "Exception in SimConnectionMonitor task, cannot continue: " + e.Message); } _logger.LogDebug("SimConnectionMonitor task stopped."); + return Task.CompletedTask; } /// @@ -266,7 +268,7 @@ private void SimConnectEvent_OnDisconnect() { _simTasksCTS?.Dispose(); _simTasksCTS = null; - if (autoReconnectSimConnect && !_quitting) + if (!_simAutoConnectDisable.IsSet && !_quitting) _simConnectionRequest.Set(); // re-enable connection attempts } @@ -476,29 +478,40 @@ private void ClearRepeatingActions() { #region Plugin Action/Event Handlers ///////////////////////////////////// + void ConnectSimConnect() { + _simAutoConnectDisable.Reset(); + if (!_simConnectService.IsConnected()) + _simConnectionRequest.Set(); + UpdateSimConnectState(); + } + + void DisconnectSimConnect() { + _simAutoConnectDisable.Set(); + bool wasSet = _simConnectionRequest.IsSet; + _simConnectionRequest.Reset(); + if (_simConnectService.IsConnected()) + _simConnectService.Disconnect(); + else if (wasSet) + _logger.LogInformation("Connection attempts to Simulator were canceled."); + UpdateSimConnectState(); + } + // Handles some basic actions like sim connection and repeat rate, with optional data value(s). private void ProcessPluginCommandAction(PluginActions actionId, ActionData data = null) { switch (actionId) { case PluginActions.ToggleConnection: - ProcessPluginCommandAction(autoReconnectSimConnect ? PluginActions.Disconnect : PluginActions.Connect); + if (_simAutoConnectDisable.IsSet) + ConnectSimConnect(); + else + DisconnectSimConnect(); break; case PluginActions.Connect: - autoReconnectSimConnect = true; - if (!_simConnectService.IsConnected()) - _simConnectionRequest.Set(); - UpdateSimConnectState(); + ConnectSimConnect(); break; case PluginActions.Disconnect: - autoReconnectSimConnect = false; - bool wasSet = _simConnectionRequest.IsSet; - _simConnectionRequest.Reset(); - if (_simConnectService.IsConnected()) - _simConnectService.Disconnect(); - else if (wasSet) - _logger.LogInformation("Connection attempts to Simulator were canceled."); - UpdateSimConnectState(); + DisconnectSimConnect(); break; case PluginActions.ReloadStates: @@ -783,7 +796,8 @@ public void OnInfoEvent(InfoEvent message) { _logger.LogInformation($"Detected {_entryFileType} type entry.tp definition file."); ProcessPluginSettings(message.Settings); - autoReconnectSimConnect = Settings.ConnectSimOnStartup.BoolValue; // we only care about the Settings value at startup + if (Settings.ConnectSimOnStartup.BoolValue) // we only care about the Settings value at startup + _simAutoConnectDisable.Set(); _client.StateUpdate(PluginId + ".Plugin.State.RunningVersion", runtimeVer); _client.StateUpdate(PluginId + ".Plugin.State.EntryVersion", $"{tpVer & 0xFFFFFF:X}"); From a799369bd341f5e7e91638ae6b17fb63330725c4 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sat, 16 Apr 2022 09:24:46 -0400 Subject: [PATCH 80/93] [SimConnectService] Fix deadlock condition in ReceiveMessages task when Simulator quits and our OnRecvQuit handler called Disconnect() synchronously; Also use a dedicated event handle to exit ReceiveMessages instead of (ab)using the SC wait handle. --- .../Services/SimConnectService.cs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/MSFSTouchPortalPlugin/Services/SimConnectService.cs b/MSFSTouchPortalPlugin/Services/SimConnectService.cs index 8cf3004..9a87b65 100644 --- a/MSFSTouchPortalPlugin/Services/SimConnectService.cs +++ b/MSFSTouchPortalPlugin/Services/SimConnectService.cs @@ -48,6 +48,7 @@ internal class SimConnectService : ISimConnectService { public const uint VIEW_EVENT_DATA_COCKPIT_3D = 0x00000002; // Virtual (3D) panels in cockpit view public const uint VIEW_EVENT_DATA_ORTHOGONAL = 0x00000004; // Orthogonal (Map) view + const int MESSAGE_RCV_WAIT_TIME = 5000; // SimConnect.ReceiveMessage() wait time // maximum stored requests for error tracking (adding is fast but search is 0(n)), should be large enough to handle flood of requests at initial connection const int MAX_STORED_REQUEST_RECORDS = 500; @@ -66,6 +67,7 @@ internal class SimConnectService : ISimConnectService { Task _messageWaitTask; SimConnect _simConnect; readonly EventWaitHandle _scReady = new EventWaitHandle(false, EventResetMode.AutoReset); + readonly AutoResetEvent _scQuit = new(false); readonly List _addedDefinitions = new(); // keep track of added SimVar definitions to avoid redundant registrations readonly RequestTrackingData[] _requestTracking = new RequestTrackingData[MAX_STORED_REQUEST_RECORDS]; // rolling buffer array for request tracking @@ -157,13 +159,11 @@ public void Disconnect() { return; _connected = false; - _scReady.Set(); // trigger message wait task to exit + _scQuit.Set(); // trigger message wait task to exit var sw = System.Diagnostics.Stopwatch.StartNew(); - while (_messageWaitTask.Status == TaskStatus.Running && sw.ElapsedMilliseconds <= 5000) { + while (_messageWaitTask.Status == TaskStatus.Running && sw.ElapsedMilliseconds <= MESSAGE_RCV_WAIT_TIME) Thread.Sleep(2); - _scReady.Set(); - } - if (sw.ElapsedMilliseconds > 5000) + if (_messageWaitTask.Status == TaskStatus.Running) _logger.LogWarning("Message wait task timed out while stopping."); try { _messageWaitTask.Dispose(); } catch { /* ignore in case it hung */ } @@ -186,10 +186,15 @@ public void Disconnect() { // runs in separate task/thread private void ReceiveMessages() { _logger.LogDebug("ReceiveMessages task started."); + int sig; + var waitHandles = new WaitHandle[] { _scReady, _scQuit }; try { while (_connected) { - if (_scReady.WaitOne(5000) && _connected) - _simConnect?.ReceiveMessage(); + sig = WaitHandle.WaitAny(waitHandles, MESSAGE_RCV_WAIT_TIME); + if (sig == 0 && _simConnect != null) + _simConnect.ReceiveMessage(); // note that this calls our event handlers synchronously on this same thread. + else if (sig != WaitHandle.WaitTimeout) + break; } } catch (ObjectDisposedException) { /* ignore but exit */ } @@ -312,7 +317,7 @@ public bool SubscribeToSystemEvent(Enum eventId, string eventName) { private void Simconnect_OnRecvQuit(SimConnect sender, SIMCONNECT_RECV data) { _logger.LogInformation("Received shutdown command from SimConnect, disconnecting."); - Disconnect(); + Task.Run(Disconnect); // async to avoid deadlock } private void Simconnect_OnRecvSimobjectDataBytype(SimConnect sender, SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE data) { From 8282918ee7afdcc8d9a8f816d738384a1fa11bfc Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sat, 16 Apr 2022 09:48:16 -0400 Subject: [PATCH 81/93] Add all Simulator System events as the first TP Event with multiple choices; Sim connection state changes are also sent as events, including states for connection timeout and "hard" errors. --- .../Configuration/PluginStates.ini | 15 ++++++ MSFSTouchPortalPlugin/Enums/EventIds.cs | 5 +- .../Objects/SimSystem/SimSystem.cs | 35 ++++++++++++- .../Services/PluginService.cs | 52 +++++++++++++++---- 4 files changed, 94 insertions(+), 13 deletions(-) diff --git a/MSFSTouchPortalPlugin/Configuration/PluginStates.ini b/MSFSTouchPortalPlugin/Configuration/PluginStates.ini index e13b0da..5a0bbf5 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginStates.ini +++ b/MSFSTouchPortalPlugin/Configuration/PluginStates.ini @@ -32,3 +32,18 @@ CategoryId = Plugin Name = "The loaded entry.tp custom configuration version." DefaultValue = "0" Unit = "number" + + +# Category: SimSystem ############################## +# +[SimSystemEvent] +CategoryId = SimSystem +Name = "Most recent Simulator System Event name." +DefaultValue = "" +Unit = "string" + +[SimSystemEventData] +CategoryId = SimSystem +Name = "Data from the most recent Simulator System Event, if any." +DefaultValue = "" +Unit = "string" diff --git a/MSFSTouchPortalPlugin/Enums/EventIds.cs b/MSFSTouchPortalPlugin/Enums/EventIds.cs index 689c043..0542023 100644 --- a/MSFSTouchPortalPlugin/Enums/EventIds.cs +++ b/MSFSTouchPortalPlugin/Enums/EventIds.cs @@ -4,7 +4,7 @@ namespace MSFSTouchPortalPlugin.Enums public enum EventIds : short { None = 0, - + Ignore, // a way to flag > info log entries which shouldn't trigger a PluginError/SimError events // Plugin events SimConnecting, // attempting to connect SimConnected, @@ -12,6 +12,7 @@ public enum EventIds : short SimTimedOut, // connection error, sim not running SimError, // SimConnect error PluginError, // internal plugin error + PluginInfo, // internal plugin "info"/success event w/message (log entry) // SimConnect events (must match names accepted by SimConnect_SubscribeToSystemEvent, see comment block at EOF) SimEventNone, // marker @@ -28,7 +29,7 @@ public enum EventIds : short //Frame, // no notification Pause, Paused, - //PauseFrame, // no notification + //PauseFrame, // no notification (I've read this could be used to detect active pause, but...?) PositionChanged, Sim, SimStart, diff --git a/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs b/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs index c288f87..c9030cb 100644 --- a/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs +++ b/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs @@ -1,10 +1,12 @@ using MSFSTouchPortalPlugin.Attributes; using MSFSTouchPortalPlugin.Enums; +using MSFSTouchPortalPlugin.Types; namespace MSFSTouchPortalPlugin.Objects.SimSystem { [TouchPortalCategory(Groups.SimSystem)] - internal static class SimSystemMapping { + internal static class SimSystemMapping + { [TouchPortalAction("SimulationRate", "Simulation Rate", "MSFS", "Simulation Rate", "Rate {0}", true)] [TouchPortalActionChoice(new [] { "Increase", "Decrease" }, "Decrease")] [TouchPortalActionMapping("SIM_RATE_INCR", "Increase")] @@ -17,5 +19,36 @@ internal static class SimSystemMapping { [TouchPortalActionMapping("MINUS", "Decrease")] public static readonly object SELECTED_PARAMETER_CHANGE; + + // Event + + public static readonly TouchPortalEvent SimSystemEvent = new("SimSystemEvent", "Simulator System Event", "On Simulator Event $val", + new System.Collections.Generic.Dictionary() { + { EventIds.SimConnecting, "Connecting" }, + { EventIds.SimConnected, "Connected" }, + { EventIds.SimDisconnected, "Disconnected" }, + { EventIds.SimTimedOut, "Connection Timed Out" }, + { EventIds.SimError, "SimConnect Error" }, + { EventIds.PluginError, "Plugin Error" }, + { EventIds.PluginInfo, "Plugin Information" }, + { EventIds.Paused, "Paused" }, + { EventIds.Unpaused, "Unpaused" }, + { EventIds.Pause, "Pause Toggled" }, + //{ EventIds.PauseFrame, "Pause Frame" }, // doesn't seem to work + { EventIds.SimStart, "Flight Started" }, + { EventIds.SimStop, "Flight Stopped" }, + { EventIds.Sim, "Flight Toggled" }, + { EventIds.AircraftLoaded, "Aircraft Loaded" }, + { EventIds.Crashed, "Crashed" }, + { EventIds.CrashReset, "Crash Reset" }, + { EventIds.FlightLoaded, "Flight Loaded" }, + { EventIds.FlightSaved, "Flight Saved" }, + { EventIds.FlightPlanActivated, "Flight Plan Activated" }, + { EventIds.FlightPlanDeactivated, "Flight Plan Deactivated" }, + { EventIds.PositionChanged, "Position Changed" }, + { EventIds.Sound, "Sound Toggled" }, + { EventIds.ViewCockpit, "View 3D Cockpit" }, + { EventIds.ViewExternal, "View External" }, + }); } } diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index c687e8c..28df979 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -6,6 +6,7 @@ using MSFSTouchPortalPlugin.Helpers; using MSFSTouchPortalPlugin.Interfaces; using MSFSTouchPortalPlugin.Objects.Plugin; +using MSFSTouchPortalPlugin.Objects.SimSystem; using MSFSTouchPortalPlugin.Types; using System; using System.Collections.Generic; @@ -24,7 +25,8 @@ namespace MSFSTouchPortalPlugin.Services /// internal class PluginService : IPluginService, ITouchPortalEventHandler { - public string PluginId => "MSFSTouchPortalPlugin"; // for ITouchPortalEventHandler + const string PLUGIN_ID = "MSFSTouchPortalPlugin"; + public string PluginId => PLUGIN_ID; // for ITouchPortalEventHandler const int SIM_RECONNECT_DELAY_SEC = 30; // SimConnect connection attempts delay on failure @@ -138,6 +140,7 @@ private bool Initialize() { _simConnectService.OnConnect += SimConnectEvent_OnConnect; _simConnectService.OnDisconnect += SimConnectEvent_OnDisconnect; _simConnectService.OnException += SimConnectEvent_OnException; + _simConnectService.OnEventReceived += SimConnectEvent_OnEventReceived; return true; } @@ -233,6 +236,10 @@ private void SimConnectEvent_OnConnect(SimulatorInfo info) { // Register SimVars for States RegisterAllSimVars(); + // Request system events + for (EventIds eventId = EventIds.SimEventNone + 1; eventId < EventIds.SimEventLast; ++eventId) + _simConnectService.SubscribeToSystemEvent(eventId, eventId.ToString()); + // start checking timer events _pluginEventsTask = Task.Run(PluginEventsTask); _pluginEventsTask.ConfigureAwait(false); // needed? @@ -262,7 +269,6 @@ private void SimConnectEvent_OnDisconnect() { catch { /* ignore in case it hung */ } ClearRepeatingActions(); - UpdateSimConnectState(); _pluginEventsTask = null; _simTasksCTS?.Dispose(); @@ -270,6 +276,16 @@ private void SimConnectEvent_OnDisconnect() { if (!_simAutoConnectDisable.IsSet && !_quitting) _simConnectionRequest.Set(); // re-enable connection attempts + + UpdateSimConnectState(); + } + + private void SimConnectEvent_OnEventReceived(EventIds eventId, Groups categoryId, object data) { + if (eventId > EventIds.SimEventNone && eventId < EventIds.SimEventLast) { + if (eventId == EventIds.View) + eventId = (uint)data == SimConnectService.VIEW_EVENT_DATA_COCKPIT_3D ? EventIds.ViewCockpit : EventIds.ViewExternal; + UpdateSimSystemEventState(eventId, data); + } } private void SimConnectEvent_OnException(RequestTrackingData data) { @@ -388,7 +404,7 @@ private void SaveSimVarsToFile(string filepath, bool customOnly = true) { // common handler for other action list updaters private void UpdateActionDataList(PluginActions actionId, string dataId, IEnumerable data, string instanceId = null) { - _client.ChoiceUpdate(PluginId + $".Plugin.Action.{actionId}.Data.{dataId}", data.ToArray(), instanceId); + _client.ChoiceUpdate(PLUGIN_ID + $".Plugin.Action.{actionId}.Data.{dataId}", data.ToArray(), instanceId); } private void UpdateSimVarLists() { @@ -459,11 +475,27 @@ private void UpdateKnownSimEventsForCategory(string categoryName, string instanc // Misc. data update/clear + // common handler for state updates + void UpdateTpStateValue(string stateId, string value, Groups catId = Groups.Plugin) { + _client.StateUpdate(PLUGIN_ID + "." + catId.ToString() + ".State." + stateId, value); + } + + // update SimSystemEvent and (maybe) SimSystemEventData states in SimSystem group + private void UpdateSimSystemEventState(EventIds eventId, object data = null) { + if (SimSystemMapping.SimSystemEvent.ChoiceMappings.TryGetValue(eventId, out var eventName)) { + UpdateTpStateValue("SimSystemEvent", eventName, Groups.SimSystem); + if (data is string) + UpdateTpStateValue("SimSystemEventData", data.ToString(), Groups.SimSystem); + } + } + + // update Connected state and trigger corresponding UpdateSimSystemEventState update private void UpdateSimConnectState() { - string stat = "true"; + EventIds evtId = EventIds.SimConnected; if (!_simConnectService.IsConnected()) - stat = _simConnectionRequest.IsSet ? "connecting" : "false"; - _client.StateUpdate(PluginId + ".Plugin.State.Connected", stat); + evtId = _simConnectionRequest.IsSet ? EventIds.SimConnecting : EventIds.SimDisconnected; + UpdateTpStateValue("Connected", evtId switch { EventIds.SimConnected => "true", EventIds.SimDisconnected => "false", _ => "connecting" }); + UpdateSimSystemEventState(evtId); } private void ClearRepeatingActions() { @@ -596,7 +628,7 @@ private void AddSimVarFromActionData(PluginActions actId, ActionData data) { StringFormat = data.GetValueOrDefault("Format", string.Empty).Trim(), DefaultValue = data.GetValueOrDefault("DfltVal", string.Empty).Trim(), }; - simVar.TouchPortalStateId = $"{PluginId}.{catId}.State.{simVar.Id}"; + simVar.TouchPortalStateId = PLUGIN_ID + $".{catId}.State.{simVar.Id}"; simVar.TouchPortalSelector = Categories.PrependCategoryName(catId, simVar.Name) + $" [{simVar.Id}]"; if (data.TryGetValue("UpdPer", out var sPeriod) && Enum.TryParse(sPeriod, out UpdatePeriod period)) simVar.UpdatePeriod = period; @@ -799,9 +831,9 @@ public void OnInfoEvent(InfoEvent message) { if (Settings.ConnectSimOnStartup.BoolValue) // we only care about the Settings value at startup _simAutoConnectDisable.Set(); - _client.StateUpdate(PluginId + ".Plugin.State.RunningVersion", runtimeVer); - _client.StateUpdate(PluginId + ".Plugin.State.EntryVersion", $"{tpVer & 0xFFFFFF:X}"); - _client.StateUpdate(PluginId + ".Plugin.State.ConfigVersion", $"{tpVer >> 24:X}"); + UpdateTpStateValue("RunningVersion", runtimeVer); + UpdateTpStateValue("EntryVersion", $"{tpVer & 0xFFFFFF:X}"); + UpdateTpStateValue("ConfigVersion", $"{tpVer >> 24:X}"); UpdateUCategoryLists(); UpdateUnitsLists(); From fa4ae3d4d0e2f21d38acbaec58b55e54a49d91cc Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sat, 16 Apr 2022 09:54:33 -0400 Subject: [PATCH 82/93] Add PluginLogger class. --- .../Services/PluginLogger.cs | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 MSFSTouchPortalPlugin/Services/PluginLogger.cs diff --git a/MSFSTouchPortalPlugin/Services/PluginLogger.cs b/MSFSTouchPortalPlugin/Services/PluginLogger.cs new file mode 100644 index 0000000..6edcbc8 --- /dev/null +++ b/MSFSTouchPortalPlugin/Services/PluginLogger.cs @@ -0,0 +1,75 @@ +using System; +using Microsoft.Extensions.Logging; + +namespace MSFSTouchPortalPlugin.Services +{ + /// + /// Delivers tersely formatted log messages to any handler attached to the OnMessageReady event. + /// This is used to pass through events and messages to the PluginService for centralized handling of events. + /// + class PluginLogger : ILogger + { + public static event MessageReadyHandler OnMessageReady; + public delegate void MessageReadyHandler(string message, LogLevel logLevel, EventId eventId); + + public static string LogFormat { get; set; } = "{0:mm:ss} [{1}] {2}"; + + public PluginLogger(string categoryName) + { + _ = categoryName; + //_categoryName = categoryName.Split('.')[^1]; + } + //readonly string _categoryName; + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (OnMessageReady == null || formatter == null) + return; + + string message; + try { + message = string.Format(LogFormat, DateTime.Now, GetLogLevelStr(logLevel), formatter(state, exception)); + } + catch (Exception e) { + message = $""; + } + + OnMessageReady.Invoke(message, logLevel, eventId); + } + + static readonly string[] _logLevelStrings = new [] { "TRC", "DBG", "INF", "WRN", "ERR", "CRT", "UNK" }; + static string GetLogLevelStr(LogLevel logLevel) + { + try { return _logLevelStrings[(int)logLevel]; } + catch { return "???"; } + } + + public bool IsEnabled(LogLevel logLevel) => logLevel > LogLevel.Debug && logLevel < LogLevel.None; + public IDisposable BeginScope(TState state) => NullDisposable.Instance; + } + + [ProviderAlias("PluginLogger")] + internal class PluginLoggerProvider : ILoggerProvider + { + PluginLoggerProvider() { } + + public static ILoggerProvider Instance { get; } = new PluginLoggerProvider(); + public ILogger CreateLogger(string categoryName) => new PluginLogger(categoryName); + + protected virtual void Dispose(bool d) { /* nothing to dispose */ } + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + } + + internal class NullDisposable : IDisposable + { + public static IDisposable Instance { get; } = new NullDisposable(); + protected virtual void Dispose(bool d) { /* nothing to dispose */ } + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} From 849f62721108210e12e93017a1b40ce14cece004 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sat, 16 Apr 2022 10:08:23 -0400 Subject: [PATCH 83/93] Set up PluginService to use PluginLogger for reporting important events and messages to the plugin user; Add new LogMessages state with the contents of the last 12 log messages; Clean up some logging to be more consistent and useful to a user, and select which messages will be passed on as TP Events. --- .../Configuration/PluginStates.ini | 6 ++ MSFSTouchPortalPlugin/Program.cs | 7 +- .../Services/PluginService.cs | 81 ++++++++++++------- 3 files changed, 66 insertions(+), 28 deletions(-) diff --git a/MSFSTouchPortalPlugin/Configuration/PluginStates.ini b/MSFSTouchPortalPlugin/Configuration/PluginStates.ini index 5a0bbf5..b5cbfb3 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginStates.ini +++ b/MSFSTouchPortalPlugin/Configuration/PluginStates.ini @@ -33,6 +33,12 @@ Name = "The loaded entry.tp custom configuration version." DefaultValue = "0" Unit = "number" +[LogMessages] +CategoryId = Plugin +Name = "Most recent plugin log messages." +DefaultValue = "" +Unit = "string" + # Category: SimSystem ############################## # diff --git a/MSFSTouchPortalPlugin/Program.cs b/MSFSTouchPortalPlugin/Program.cs index 537d6a5..89d9106 100644 --- a/MSFSTouchPortalPlugin/Program.cs +++ b/MSFSTouchPortalPlugin/Program.cs @@ -48,7 +48,12 @@ await Host.CreateDefaultBuilder(args) .ConfigureLogging((hostContext, loggingBuilder) => { loggingBuilder .ClearProviders() - .AddSerilog(logger: new LoggerConfiguration().ReadFrom.Configuration(configurationRoot).CreateLogger(), dispose: true); + .AddSerilog(logger: new LoggerConfiguration().ReadFrom.Configuration(configurationRoot).CreateLogger(), dispose: true) + // PluginLogger is a feedback mechanism to get log entries delivered to the plugin's handler, which may then pass them on to TP as a State. + .AddProvider(PluginLoggerProvider.Instance) + // Do not change these filters w/out a good reason! + .AddFilter("", LogLevel.None) + .AddFilter("MSFSTouchPortalPlugin.Services.PluginService", LogLevel.Information); }) .ConfigureServices((context, services) => { services diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index 28df979..c9f809e 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -29,6 +29,7 @@ internal class PluginService : IPluginService, ITouchPortalEventHandler public string PluginId => PLUGIN_ID; // for ITouchPortalEventHandler const int SIM_RECONNECT_DELAY_SEC = 30; // SimConnect connection attempts delay on failure + const int MAX_LOG_MSGS_FOR_STATE = 12; // maximum number of log lines to send in the LogMessages state private enum EntryFileType { Default, NoStates, Custom }; @@ -51,6 +52,7 @@ private enum EntryFileType { Default, NoStates, Custom }; private readonly SimVarCollection _statesDictionary = new(); private readonly List _dynamicStateIds = new(); // keep track of dynamically created states for clearing them if/when reloading sim var state files private readonly ConcurrentDictionary _repeatingActionTimers = new(); // storage for temporary repeating (held) action timers, index by action ID + private readonly ConcurrentQueue _logMessages = new(); // stores the last MAX_LOG_MSGS_FOR_STATE log messages for the LogMessages state value, used in PluginLogger callback private bool _quitting; // prevent recursion at shutdown private EntryFileType _entryFileType = EntryFileType.Default; // detected entry.tp States configuration type @@ -70,6 +72,7 @@ public PluginService(IHostApplicationLifetime hostApplicationLifetime, ILogger

= MAX_LOG_MSGS_FOR_STATE) + _logMessages.TryDequeue(out _); + _logMessages.Enqueue(message); + if (_client.IsConnected) { + var evId = (EventIds)eventId.Id; + if (evId == EventIds.None && logLevel > LogLevel.Information) + evId = EventIds.PluginError; + if (evId != EventIds.None) + UpdateSimSystemEventState(evId, message); + UpdateTpStateValue("LogMessages", string.Join('\n', _logMessages.ToArray())); + } + } + #endregion Startup, Shutdown and Processing Tasks #region SimConnect Events ///////////////////////////////////// private void SimConnectEvent_OnConnect(SimulatorInfo info) { - _logger.LogInformation("Connected to " + info.ToString()); + _logger.LogInformation((int)EventIds.PluginInfo, "Connected to " + info.ToString()); _simConnectionRequest.Reset(); _simTasksCTS = new CancellationTokenSource(); @@ -264,7 +283,7 @@ private void SimConnectEvent_OnDisconnect() { _simTasksCTS?.Cancel(); if (_pluginEventsTask.Status == TaskStatus.Running && !_pluginEventsTask.Wait(5000)) - _logger.LogWarning("PluginEventsTask timed out while stopping."); + _logger.LogWarning((int)EventIds.Ignore, "PluginEventsTask timed out while stopping."); try { _pluginEventsTask.Dispose(); } catch { /* ignore in case it hung */ } @@ -289,7 +308,7 @@ private void SimConnectEvent_OnEventReceived(EventIds eventId, Groups categoryId } private void SimConnectEvent_OnException(RequestTrackingData data) { - _logger.LogWarning($"SimConnect Request Exception: " + data.ToString()); + _logger.LogWarning((int)EventIds.SimError, "SimConnect Request Error: " + data.ToString()); } #endregion SimConnect Events @@ -302,6 +321,7 @@ private void SetupSimVars() { // We may need to generate all, some, or no states dynamically. bool allStatesDynamic = (_entryFileType == EntryFileType.NoStates); bool someStateDynamic = false; + string logMsg; IReadOnlyCollection simVars; // First check if we're using a custom config. @@ -310,14 +330,15 @@ private void SetupSimVars() { // then lets figure out what the default states are so we can create any missing ones dynamically if needed. if (_entryFileType == EntryFileType.Default) someStateDynamic = _pluginConfig.DefaultStateIds.Any(); - _logger.LogInformation($"Loading custom state file(s) '{PluginConfig.UserStateFiles}' from '{PluginConfig.UserConfigFolder}'."); + logMsg = $"custom state file(s) '{PluginConfig.UserStateFiles}' in '{PluginConfig.UserConfigFolder}'"; } else { // Default config - _logger.LogInformation($"Loading default SimVar State file '{PluginConfig.AppConfigFolder}/{PluginConfig.StatesConfigFile}'."); + logMsg = $"default file '{PluginConfig.AppConfigFolder}/{PluginConfig.StatesConfigFile}'"; } // Load the vars. PluginConfig tracks which files should be loaded, defaults or custom. simVars = _pluginConfig.LoadSimVarStateConfigs(); - _logger.LogInformation($"Loaded {simVars.Count} SimVars from file(s)."); + + _logger.LogInformation((int)EventIds.PluginInfo, $"Loaded {simVars.Count} SimVar States from {logMsg}."); // Now create the SimVars and track them. We're probably not connected to SimConnect at this point so the registration may happen later. foreach (var simVar in simVars) @@ -377,7 +398,7 @@ private void LoadCustomSimVarsFromFile(string filepath) { foreach (var simVar in simVars) AddSimVar(simVar, true); UpdateSimVarLists(); - _logger.LogInformation($"Loaded {simVars.Count} SimVar States from file '{filepath}'"); + _logger.LogInformation((int)EventIds.PluginInfo, $"Loaded {simVars.Count} SimVar States from file '{filepath}'"); } else { _logger.LogWarning($"Did not load any SimVar States from file '{filepath}'"); @@ -391,7 +412,7 @@ private void SaveSimVarsToFile(string filepath, bool customOnly = true) { else count = _pluginConfig.SaveSimVarItems(_statesDictionary, true, filepath); if (count > 0) - _logger.LogInformation($"Saved {(customOnly ? $"{count} Custom" : $"All {count}")} SimVar States to file '{filepath}'"); + _logger.LogInformation((int)EventIds.PluginInfo, $"Saved {(customOnly ? $"{count} Custom" : $"All {count}")} SimVar States to file '{filepath}'"); else _logger.LogError($"Error saving SimVar States to file '{filepath}'; Please check log messages."); } @@ -524,7 +545,7 @@ void DisconnectSimConnect() { if (_simConnectService.IsConnected()) _simConnectService.Disconnect(); else if (wasSet) - _logger.LogInformation("Connection attempts to Simulator were canceled."); + _logger.LogInformation((int)EventIds.PluginInfo, "Connection attempts to Simulator were canceled."); UpdateSimConnectState(); } @@ -553,9 +574,12 @@ private void ProcessPluginCommandAction(PluginActions actionId, ActionData data case PluginActions.ActionRepeatIntervalInc: case PluginActions.ActionRepeatIntervalDec: case PluginActions.ActionRepeatIntervalSet: { + string errMsg = null; // preserve backwards compatibility with old actions which used indexed data IDs - if (data == null || !(data.TryGetValue("Value", out var sVal) || data.TryGetValue("1", out sVal)) || !TryEvaluateValue(sVal, out var value)) { - _logger.LogWarning($"Could not find or parse numeric value for repeat rate from data: {ActionDataToKVPairString(data)}"); + if (data == null || !(data.TryGetValue("Value", out var sVal) || data.TryGetValue("1", out sVal)) || !TryEvaluateValue(sVal, out var value, out errMsg)) { + if (errMsg == null) + errMsg = "Required parameter 'Value' missing or invalid."; + _logger.LogError($"Error getting value for repeat rate: '{errMsg}'; From data: {ActionDataToKVPairString(data)}"); break; } if (actionId == PluginActions.ActionRepeatIntervalInc) @@ -575,7 +599,7 @@ private void SetSimVarValueFromActionData(ActionData data) { if (!data.TryGetValue("VarName", out var varName) || !data.TryGetValue("Value", out var value) || !TryGetSimVarIdFromActionData(varName, out string varId)) { - _logger.LogWarning($"Could not parse required action parameters for {PluginActions.SetSimVar} from data: {ActionDataToKVPairString(data)}"); + _logger.LogError($"Could not parse required action parameters for {PluginActions.SetSimVar} from data: {ActionDataToKVPairString(data)}"); return; } if (!_statesDictionary.TryGet(varId, out SimVarItem simVar)) { @@ -589,8 +613,9 @@ private void SetSimVarValueFromActionData(ActionData data) { return; } } - else if (!TryEvaluateValue(value, out double dVal) || !simVar.SetValue(dVal)) { - _logger.LogError($"Could not set numeric value '{value}' for SimVar Id: '{varId}' Name: '{varName}'"); + else if (!TryEvaluateValue(value, out double dVal, out var errMsg) || !simVar.SetValue(dVal)) { + if (errMsg == null) errMsg = "Value is of wrong type."; + _logger.LogError($"Could not set numeric value '{value}' for SimVar Id: '{varId}' Name: '{varName}'; Error: {errMsg}"); return; } if (data.TryGetValue("RelAi", out var relAi) && new BooleanString(relAi) && !_simConnectService.ReleaseAIControl(simVar.Def)) @@ -604,7 +629,7 @@ private void AddSimVarFromActionData(PluginActions actId, ActionData data) { !data.TryGetValue("CatId", out var sCatId) || !Categories.TryGetCategoryId(sCatId, out Groups catId) || !data.TryGetValue("Unit", out var sUnit) ) { - _logger.LogWarning($"Could not parse required action parameters for {actId} from data: {ActionDataToKVPairString(data)}"); + _logger.LogError($"Could not parse required action parameters for {actId} from data: {ActionDataToKVPairString(data)}"); return; } @@ -638,27 +663,27 @@ private void AddSimVarFromActionData(PluginActions actId, ActionData data) { simVar.DeltaEpsilon = epsilon; if (AddSimVar(simVar)) - _logger.LogInformation($"Added new SimVar state from action data: {simVar.ToDebugString()}"); + _logger.LogInformation((int)EventIds.PluginInfo, $"Added new SimVar state from action data: {simVar.ToDebugString()}"); else _logger.LogError($"Failed to add SimVar from action data, check previous log messages. Action data: {ActionDataToKVPairString(data)}"); } private void RemoveSimVarByActionDataName(ActionData data) { if (!data.TryGetValue("VarName", out var varName) || !TryGetSimVarIdFromActionData(varName, out string varId)) { - _logger.LogWarning($"Could not find valid SimVar ID in action data: {ActionDataToKVPairString(data)}'"); + _logger.LogError($"Could not find valid SimVar ID in action data: {ActionDataToKVPairString(data)}'"); return; } SimVarItem simVar = _statesDictionary[varId]; if (simVar != null && RemoveSimVar(simVar)) - _logger.LogInformation($"Removed SimVar '{simVar.SimVarName}'"); + _logger.LogInformation((int)EventIds.PluginInfo, $"Removed SimVar '{simVar.SimVarName}'"); else - _logger.LogWarning($"Could not find definition for settable SimVar Id: '{varId}' from Name: '{varName}'"); + _logger.LogError($"Could not find definition for settable SimVar Id: '{varId}' from Name: '{varName}'"); } // Dynamic sim Events (actions) private void ProcessSimEventFromActionData(PluginActions actId, ActionData data) { if (!data.TryGetValue("EvtId", out var evtId) || !data.TryGetValue("Value", out var sValue)) { - _logger.LogWarning($"Could not find required action parameters for {actId} from data: {ActionDataToKVPairString(data)}"); + _logger.LogError($"Could not find required action parameters for {actId} from data: {ActionDataToKVPairString(data)}"); return; } // Check for known/imported type, which may have special formatting applied to the name @@ -714,7 +739,7 @@ private void ProcessInternalEvent(ActionEventType action, ActionData data) { if ((data.TryGetValue("Action", out var actId) || data.TryGetValue("0", out actId)) && action.TryGetEventMapping(actId, out Enum eventId)) ProcessPluginCommandAction((PluginActions)eventId, data); else - _logger.LogWarning($"Could not parse required action parameters for {action.ActionId} from data: {ActionDataToKVPairString(data)}"); + _logger.LogError($"Could not parse required action parameters for {action.ActionId} from data: {ActionDataToKVPairString(data)}"); break; } @@ -762,8 +787,8 @@ private void ProcessSimEvent(ActionEventType action, Enum eventId, string value switch (action.ValueType) { case DataType.Number: case DataType.Text: - if (!TryEvaluateValue(value, out dataReal)) { - _logger.LogWarning($"Data conversion failed for action '{action.ActionId}' on sim event '{_reflectionService.GetSimEventNameById(eventId)}'."); + if (!TryEvaluateValue(value, out dataReal, out var errMsg)) { + _logger.LogError($"Data conversion for string '{value}' failed for action '{action.ActionId}' on sim event '{_reflectionService.GetSimEventNameById(eventId)}' with Error: {errMsg}."); return; } break; @@ -933,13 +958,15 @@ public void OnUnhandledEvent(string jsonMessage) { #region Utilities /////////////////////////////// - private bool TryEvaluateValue(string strValue, out double value) { + private bool TryEvaluateValue(string strValue, out double value, out string errMsg) { value = double.NaN; + errMsg = null; try { value = Convert.ToDouble(_expressionEvaluator.Compute(strValue, null)); } catch (Exception e) { - _logger.LogWarning(e, $"Failed to convert data value '{strValue}' to numeric."); + errMsg = e.Message; + _logger.LogDebug(e, $"Eval exception with '{strValue}'"); return false; } return true; From 1410a71122a803eba68b7d13a41c01386191f529 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sat, 16 Apr 2022 10:11:37 -0400 Subject: [PATCH 84/93] Minor fix in RequestTrackingData and mostly-cosmetic change in ActionEventType. --- MSFSTouchPortalPlugin/Types/ActionEventType.cs | 2 +- MSFSTouchPortalPlugin/Types/RequestTrackingData.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MSFSTouchPortalPlugin/Types/ActionEventType.cs b/MSFSTouchPortalPlugin/Types/ActionEventType.cs index ca68740..a1adca4 100644 --- a/MSFSTouchPortalPlugin/Types/ActionEventType.cs +++ b/MSFSTouchPortalPlugin/Types/ActionEventType.cs @@ -57,7 +57,7 @@ public bool TryGetEventMapping(in string[] values, out Enum eventId) { return true; } if (TpActionToEventMap.Count == 1) { - eventId = TpActionToEventMap.First().Value; + eventId = TpActionToEventMap.Values.First(); return true; } return TpActionToEventMap.TryGetValue(FormatLookupKey(values), out eventId); diff --git a/MSFSTouchPortalPlugin/Types/RequestTrackingData.cs b/MSFSTouchPortalPlugin/Types/RequestTrackingData.cs index 68f312d..e6c37ba 100644 --- a/MSFSTouchPortalPlugin/Types/RequestTrackingData.cs +++ b/MSFSTouchPortalPlugin/Types/RequestTrackingData.cs @@ -49,8 +49,8 @@ public override string ToString() { Append(aArguments[i]); } sb.Append(')'); - if (dwExceptionIndex > 0) - sb.Append(" ([@] = error source)"); + if (dwExceptionIndex > 0 && dwExceptionIndex < uint.MaxValue) + sb.Append(" ([@] = error source arg. ").Append($"{dwExceptionIndex})"); return sb.ToString(); } } From ee8fcc91a3410de110b23c805f308495612a54fd Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sat, 16 Apr 2022 11:28:19 -0400 Subject: [PATCH 85/93] [Generator] Add docs URL option and display. --- .../Configuration/GeneratorOptions.cs | 4 ++++ .../GenerateDoc.cs | 22 ++++++++++++++----- .../Model/DocBase.cs | 1 + 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs b/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs index c2637b8..41c48ed 100644 --- a/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs +++ b/MSFSTouchPortalPlugin-Generator/Configuration/GeneratorOptions.cs @@ -49,6 +49,10 @@ internal class GeneratorOptions HelpText = "\nName of the plugin's folder once installed to TP.")] public string PluginFolder { get; set; } + [Option('u', "DocsUrl", Default = "", MetaValue = "", Required = false, + HelpText = "\nURL to full project documentation, if any.")] + public string DocumentationUrl { get; set; } + [Option('d', "debug", Hidden = true, Default = false, Required = false, HelpText = "Enables generating a 'debug' version of entry.tp. Currently that means w/out a startup command.")] public bool Debug { get; set; } diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index f165c6d..98d586d 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -49,13 +49,23 @@ public void Generate() { private DocBase CreateModel() { // set plugin file location for version info - VersionInfo.AssemblyLocation = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), _options.PluginId + ".dll"); + var assembly = Assembly.GetExecutingAssembly(); + VersionInfo.AssemblyLocation = Path.Combine(Path.GetDirectoryName(assembly.Location), _options.PluginId + ".dll"); + + string docsUrl = _options.DocumentationUrl; + if (string.IsNullOrWhiteSpace(docsUrl)) { + var metaAttr = assembly.GetCustomAttributes(); + foreach (var att in metaAttr) + if (att.Key == "DocumentationUrl") + docsUrl = att.Value; + } // create the base model var model = new DocBase { Title = _options.PluginName + " Documentation", - Overview = "This plugin will provide a two-way interface between Touch Portal and Flight Simulators which use SimConnect, such as Microsoft Flight Simulator 2020 and FS-X.", - Version = VersionInfo.GetProductVersionString() + Overview = "This plugin provides a two-way interface between Touch Portal and Flight Simulators which use SimConnect, such as Microsoft Flight Simulator 2020 and FS-X.", + Version = VersionInfo.GetProductVersionString(), + DocsUrl = docsUrl }; // read states config @@ -185,11 +195,13 @@ private DocBase CreateModel() { } private static string CreateMarkdown(DocBase model) { - var s = new StringBuilder(); + var s = new StringBuilder(75 * 1024); s.Append($"# {model.Title}\n\n"); s.Append($"{model.Overview}\n\n"); - s.Append($"Docuemntation generated for plugin version {model.Version}\n\n"); + if (!string.IsNullOrWhiteSpace(model.DocsUrl)) + s.Append("For further documentation, please see ").Append(model.DocsUrl).Append("\n\n"); + s.Append($"This documentation generated for plugin v{model.Version}\n\n"); s.Append("---\n\n"); // Table of Contents diff --git a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs index 1b1eea9..752472d 100644 --- a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs +++ b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs @@ -5,6 +5,7 @@ public class DocBase { public string Title { get; set; } public string Overview { get; set; } public string Version { get; set; } + public string DocsUrl { get; set; } public List Settings { get; set; } = new(); public List Categories { get; set; } = new(); } From 013a22d427972be1bb5e0aedfc613f872d2b2410 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sat, 16 Apr 2022 19:01:28 -0400 Subject: [PATCH 86/93] Use ProductVersion for version string instead of FileVersion in VersionInfo helper and publish.ps1 script. --- MSFSTouchPortalPlugin/Helpers/VersionInfo.cs | 7 ++----- publish.ps1 | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/MSFSTouchPortalPlugin/Helpers/VersionInfo.cs b/MSFSTouchPortalPlugin/Helpers/VersionInfo.cs index e1bd9fa..37d5807 100644 --- a/MSFSTouchPortalPlugin/Helpers/VersionInfo.cs +++ b/MSFSTouchPortalPlugin/Helpers/VersionInfo.cs @@ -31,12 +31,9 @@ internal static uint GetProductVersionNumber() { } ///

- /// Returns a string representing the full version in dotted notation, eg. 1.22.3.0 + /// Returns a string representing the full version in dotted notation with possible qualifier, eg. 1.22.3.0-beta /// - internal static string GetProductVersionString() { - var vi = GetVersionInfo(); - return $"{vi.ProductMajorPart}.{vi.ProductMinorPart}.{vi.ProductBuildPart}.{vi.ProductPrivatePart}"; - } + internal static string GetProductVersionString() => GetVersionInfo().ProductVersion; /// /// Returns a System.Diagnostics.FileVersionInfo for a file at given location, or AssemblyLocation if location is default/null. diff --git a/publish.ps1 b/publish.ps1 index 48422db..d45115b 100644 --- a/publish.ps1 +++ b/publish.ps1 @@ -42,7 +42,7 @@ copy "CHANGELOG.md" "$PluginFilesPath" copy "airplane_takeoff24.png" "$PluginFilesPath" # Get version -$FileVersion = (Get-Command $BinFilesPath\$ProjectName.dll).FileVersionInfo.FileVersion +$FileVersion = (Get-Command $BinFilesPath\$ProjectName.dll).FileVersionInfo.ProductVersion # Create TPP File $TppFile = "$DistFolderPath\$DistroName-$FileVersion.tpp" From 8a9c38464bb64534707d628b74140bbb65c97512 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sat, 16 Apr 2022 19:03:28 -0400 Subject: [PATCH 87/93] Move all of the dynamic SimVarItem creation to PluginConfig, don't create an intermediate SimVariable unnecessarily. --- .../Configuration/PluginConfig.cs | 91 ++++++++++++------- .../Services/PluginService.cs | 30 ++---- 2 files changed, 68 insertions(+), 53 deletions(-) diff --git a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs index 8691c1a..d5c9b0c 100644 --- a/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs +++ b/MSFSTouchPortalPlugin/Configuration/PluginConfig.cs @@ -66,12 +66,13 @@ public static string UserStateFiles public IEnumerable ImportedSimEvenCategoryNames => _importedSimEvents.Keys; const string STR_DEFAULT = "default"; - private static readonly string _defaultUserCfgFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), RootName); - private static string _currentUserCfgFolder = _defaultUserCfgFolder; - private static string[] _userStateFiles = Array.Empty(); - private IReadOnlyDictionary> _importedSimVars; - private IReadOnlyDictionary> _importedSimEvents; - private readonly ILogger _logger; + static readonly string _defaultUserCfgFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), RootName); + static readonly Regex _reSimVarIdFromName = new Regex(@"(?:\b|\W|_)(\w)"); // for creating a PascalCase ID from a SimVar name + static string _currentUserCfgFolder = _defaultUserCfgFolder; + static string[] _userStateFiles = Array.Empty(); + IReadOnlyDictionary> _importedSimVars; + IReadOnlyDictionary> _importedSimEvents; + readonly ILogger _logger; public PluginConfig(ILogger logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -90,7 +91,7 @@ public void Init() { CopySimConnectConfig(); } - // Imported SimVariables methods + // SimVar/SimVariable helper methods public bool TryGetImportedSimVarNamesForCateogy(string simCategoryName, out IEnumerable list) { if (_importedSimVars.TryGetValue(simCategoryName, out var dict)) { @@ -114,27 +115,7 @@ public bool TryGetImportedSimVarBySelector(string selector, out SimVariable simV return TryGetImportedSimVarBySelector(selector, out simVar, out _, out _); } - private static readonly Regex _reSimVarIdFromName = new Regex(@"(?:\b|\W|_)(\w)"); // for creating a PascalCase ID from a SimVar name - - public SimVariable GetOrCreateImportedSimVariable(string varName, uint index = 0) { - if (!TryGetImportedSimVarBySelector(varName, out SimVariable simVar, out var name, out var indexed)) { - simVar = new() { - // Create a reasonable string for a TP state ID - Id = _reSimVarIdFromName.Replace(name.ToLower(), m => (m.Groups[1].ToString().ToUpper())), - SimVarName = name, - Name = name, // for lack of anything better - Indexed = indexed - }; - } - simVar.Indexed = simVar.Indexed || index > 0; - if (simVar.Indexed) { - simVar.Id += index.ToString(); - simVar.SimVarName += ":" + Math.Clamp(index, 1, 99).ToString(); - } - return simVar; - } - - private bool TryGetImportedSimVarBySelector(string varName, out SimVariable simVar, out string cleanName, out bool indexed) { + bool TryGetImportedSimVarBySelector(string varName, out SimVariable simVar, out string cleanName, out bool indexed) { simVar = null; if (!TryNormalizeVarName(varName, out cleanName, out indexed)) return false; @@ -146,7 +127,7 @@ private bool TryGetImportedSimVarBySelector(string varName, out SimVariable simV } // "normalize" a SimVar name passed from touch portal - private static bool TryNormalizeVarName(string name, out string varName, out bool indexed) { + static bool TryNormalizeVarName(string name, out string varName, out bool indexed) { indexed = false; varName = name.Trim(); if (string.IsNullOrEmpty(varName)) @@ -163,6 +144,44 @@ private static bool TryNormalizeVarName(string name, out string varName, out boo return true; } + // This is a helper for creating SimVars dynamically at runtime. It is here to centralize how some of the + // information is populated/formatted to keep things consistent with SimVars read from config files. + public SimVarItem CreateDynamicSimVarItem(string varName, Groups catId, string unit, uint index = 0) { + SimVarItem simVar; + if (TryGetImportedSimVarBySelector(varName, out SimVariable impSimVar, out var name, out var indexed)) { + simVar = new SimVarItem() { + Id = impSimVar.Id, + Name = impSimVar.Name, + SimVarName = impSimVar.SimVarName, + CanSet = impSimVar.CanSet + }; + } + else { + simVar = new SimVarItem() { + // Create a reasonable string for a TP state ID + Id = _reSimVarIdFromName.Replace(name.ToLower(), m => (m.Groups[1].ToString().ToUpper())), + Name = name, // for lack of anything better + SimVarName = name, + }; + } + if (indexed || index > 0) { + simVar.Id += index.ToString(); + simVar.SimVarName += ":" + Math.Clamp(index, 1, 99).ToString(); + } + simVar.CategoryId = catId; + simVar.Unit = unit ?? "number"; + simVar.DataSource = DataSourceType.Dynamic; + SetSimVarItemTpMetaData(simVar); + + return simVar; + } + + static void SetSimVarItemTpMetaData(SimVarItem simVar) { + simVar.TouchPortalStateId = $"{RootName}.{simVar.CategoryId}.State.{simVar.Id}"; + simVar.TouchPortalSelector = Categories.PrependCategoryName(simVar.CategoryId, simVar.Name) + $" [{simVar.Id}]"; + } + + // Imported SimEvents methods public bool TryGetImportedSimEventNamesForCateogy(string simCategoryName, out IEnumerable list) { @@ -180,6 +199,9 @@ public bool TryGetImportedSimEventIdFromSelector(string selector, out string eve return !string.IsNullOrEmpty(eventId); } + + // Config File loaders + // Check if user config folder contains a SimConnect.cfg file and tries to copy it into the current running folder. public bool CopySimConnectConfig() { string filename = "SimConnect.cfg"; @@ -224,8 +246,7 @@ public IReadOnlyCollection LoadSimVarItems(bool isUserConfig = true, } simVar.Id = item.Name; simVar.DataSource = isUserConfig ? DataSourceType.UserFile : DataSourceType.DefaultFile; - simVar.TouchPortalStateId = $"{RootName}.{simVar.CategoryId}.State.{simVar.Id}"; - simVar.TouchPortalSelector = Categories.PrependCategoryName(simVar.CategoryId, simVar.Name) + $" [{simVar.Id}]"; + SetSimVarItemTpMetaData(simVar); // check unique if (ret.FindIndex(s => s.Id == simVar.Id) is int idx && idx > -1) { _logger.LogWarning($"Duplicate SimVar ID found for '{simVar.Id}', overwriting."); @@ -370,7 +391,9 @@ IReadOnlyDictionary> ImportSimV catDict[simVar.SimVarName] = simVar; _logger.LogWarning($"Duplicate SimVar ID found for '{simVar.Id}' ('{simVar.SimVarName}'), overwriting."); } - ++count; + else { + ++count; + } } ret = ret.OrderBy(s => s.Key).ToDictionary(s => s.Key, s => s.Value); _logger.LogDebug($"Imported {count} SimVars in {ret.Count} categories from '{filename}'"); @@ -429,7 +452,9 @@ IReadOnlyDictionary> ImportSimEven catDict[simEvt.TouchPortalSelectorName] = simEvt; _logger.LogWarning($"Duplicate SimEvent ID found for '{simEvt.Id}', overwriting."); } - ++count; + else { + ++count; + } } ret = ret.OrderBy(s => s.Key).ToDictionary(s => s.Key, s => s.Value); _logger.LogDebug($"Imported {count} SimEvents in {ret.Count} categories from '{filename}'"); diff --git a/MSFSTouchPortalPlugin/Services/PluginService.cs b/MSFSTouchPortalPlugin/Services/PluginService.cs index c9f809e..0df60db 100644 --- a/MSFSTouchPortalPlugin/Services/PluginService.cs +++ b/MSFSTouchPortalPlugin/Services/PluginService.cs @@ -632,29 +632,19 @@ private void AddSimVarFromActionData(PluginActions actId, ActionData data) { _logger.LogError($"Could not parse required action parameters for {actId} from data: {ActionDataToKVPairString(data)}"); return; } - uint index = 0; - bool haveIndexValue = (data.TryGetValue("VarIndex", out var sIndex) && uint.TryParse(sIndex, out index) && index > 0); - - // check if we've imported this var and have meta data - SimVariable impSimVar = _pluginConfig.GetOrCreateImportedSimVariable(varName, index); - if (impSimVar == null) // highly unlikely - return; + if (data.TryGetValue("VarIndex", out var sIndex) && !uint.TryParse(sIndex, out index)) + index = 0; // create the SimVarItem from collected data - var simVar = new SimVarItem() { - Id = impSimVar.Id, - Name = impSimVar.Name, - SimVarName = impSimVar.SimVarName, - CategoryId = catId, - Unit = sUnit ?? "number", - DataSource = DataSourceType.Dynamic, - CanSet = impSimVar.CanSet || (data.TryGetValue("CanSet", out var sCanSet) && new BooleanString(sCanSet)), - StringFormat = data.GetValueOrDefault("Format", string.Empty).Trim(), - DefaultValue = data.GetValueOrDefault("DfltVal", string.Empty).Trim(), - }; - simVar.TouchPortalStateId = PLUGIN_ID + $".{catId}.State.{simVar.Id}"; - simVar.TouchPortalSelector = Categories.PrependCategoryName(catId, simVar.Name) + $" [{simVar.Id}]"; + SimVarItem simVar = _pluginConfig.CreateDynamicSimVarItem(varName, catId, sUnit, index); + // check for optional properties + if (data.TryGetValue("Format", out var sFormat)) + simVar.StringFormat = sFormat.Trim(); + if (data.TryGetValue("DfltVal", out var sDefault)) + simVar.DefaultValue = sDefault.Trim(); + if (data.TryGetValue("CanSet", out var sCanSet) && new BooleanString(sCanSet)) + simVar.CanSet = true; if (data.TryGetValue("UpdPer", out var sPeriod) && Enum.TryParse(sPeriod, out UpdatePeriod period)) simVar.UpdatePeriod = period; if (data.TryGetValue("UpdInt", out var sInterval) && uint.TryParse(sInterval, out uint interval)) From 70bb13446c0c09220aded99f39189c1a169b7494 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sun, 17 Apr 2022 03:25:53 -0400 Subject: [PATCH 88/93] Use EventIds enum for all SimConnect events and get rid of SimEventClientId; Move PluginActions enum to own file. --- MSFSTouchPortalPlugin/Enums/PluginActions.cs | 33 ++++++++++++++ .../Objects/Plugin/Plugin.cs | 44 ------------------- .../Services/ReflectionService.cs | 4 +- .../Types/ActionEventType.cs | 4 +- 4 files changed, 37 insertions(+), 48 deletions(-) create mode 100644 MSFSTouchPortalPlugin/Enums/PluginActions.cs diff --git a/MSFSTouchPortalPlugin/Enums/PluginActions.cs b/MSFSTouchPortalPlugin/Enums/PluginActions.cs new file mode 100644 index 0000000..8e9aca0 --- /dev/null +++ b/MSFSTouchPortalPlugin/Enums/PluginActions.cs @@ -0,0 +1,33 @@ +namespace MSFSTouchPortalPlugin.Enums +{ + // IDs for handling internal events + public enum PluginActions : short + { + None = 0, + + // Action IDs + Connection, + ActionRepeatInterval, + SetCustomSimEvent, + SetKnownSimEvent, + SetSimVar, + AddCustomSimVar, + AddKnownSimVar, + RemoveSimVar, + SaveSimVars, + LoadSimVars, + + // Action choice mapping IDs + ToggleConnection, + Connect, + Disconnect, + ReloadStates, + + ActionRepeatIntervalInc, + ActionRepeatIntervalDec, + ActionRepeatIntervalSet, + + SaveCustomSimVars, + SaveAllSimVars, + } +} diff --git a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs index 683deb4..d88850d 100644 --- a/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs +++ b/MSFSTouchPortalPlugin/Objects/Plugin/Plugin.cs @@ -174,47 +174,3 @@ public static class Settings }; } } - -namespace MSFSTouchPortalPlugin.Enums -{ - // IDs for handling internal events - public enum PluginActions : short - { - None = 0, - - // Action IDs - Connection, - ActionRepeatInterval, - SetCustomSimEvent, - SetKnownSimEvent, - SetSimVar, - AddCustomSimVar, - AddKnownSimVar, - RemoveSimVar, - SaveSimVars, - LoadSimVars, - - // Action choice mapping IDs - ToggleConnection, - Connect, - Disconnect, - ReloadStates, - - ActionRepeatIntervalInc, - ActionRepeatIntervalDec, - ActionRepeatIntervalSet, - - SaveCustomSimVars, - SaveAllSimVars, - } - - // Dynamically generated SimConnect client event IDs are "parented" to this enum type, - // meaning they become of this Type when they need to be cast to en Enum type (eg. for SimConnect C# API). - // This is done by the ReflectionService when generating the list of events for SimConnect. - // They really could be cast any Enum type at all, so this is mostly for semantics. - internal enum SimEventClientId - { - // Starting point - Init = 1000, - } -} diff --git a/MSFSTouchPortalPlugin/Services/ReflectionService.cs b/MSFSTouchPortalPlugin/Services/ReflectionService.cs index 3047c65..cecc5a8 100644 --- a/MSFSTouchPortalPlugin/Services/ReflectionService.cs +++ b/MSFSTouchPortalPlugin/Services/ReflectionService.cs @@ -32,8 +32,8 @@ public ReflectionService(ILogger logger) { public ref readonly Dictionary GetClientEventIdToNameMap() => ref clientEventIdToNameMap; public string GetSimEventNameById(Enum id) => clientEventIdToNameMap.TryGetValue(id, out var entry) ? entry.EventName : "[unknown event]"; - public string GetSimEventNameById(uint id) => GetSimEventNameById((SimEventClientId)id); - public string GetSimEventNameById(int id) => GetSimEventNameById((SimEventClientId)id); + public string GetSimEventNameById(uint id) => GetSimEventNameById((EventIds)id); + public string GetSimEventNameById(int id) => GetSimEventNameById((EventIds)id); public void AddSimEventNameMapping(Enum id, SimEventRecord record) => clientEventIdToNameMap[id] = record; public IEnumerable GetCategoryAttributes() { diff --git a/MSFSTouchPortalPlugin/Types/ActionEventType.cs b/MSFSTouchPortalPlugin/Types/ActionEventType.cs index a1adca4..bef38e4 100644 --- a/MSFSTouchPortalPlugin/Types/ActionEventType.cs +++ b/MSFSTouchPortalPlugin/Types/ActionEventType.cs @@ -25,8 +25,8 @@ internal class ActionEventType readonly Dictionary TpActionToEventMap = new(); // this is how we generate unique SimConnect client Event IDs. - private static SimEventClientId _nextEventId = SimEventClientId.Init; - private static SimEventClientId NextId() => ++_nextEventId; // got a warning when trying to increment this directly from c'tor, but not via static member... ? + private static EventIds _nextEventId = EventIds.DynamicEventInit; + private static EventIds NextId() => ++_nextEventId; // got a warning when trying to increment this directly from c'tor, but not via static member... ? public ActionEventType() { } From 2acd8a14cfa5c99af1b485874b2087bae410ebb5 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sun, 17 Apr 2022 04:13:22 -0400 Subject: [PATCH 89/93] Use DisplayAttributes on the EventIds enum values to generate both TP UI name mappings and documentation; TouchPortalEvent handles the attributes and gets new c'tor which auto populates the choice mappings. --- .../GenerateDoc.cs | 14 ++- .../Model/DocBase.cs | 1 + MSFSTouchPortalPlugin/Enums/EventIds.cs | 115 +++++++++++------- .../Objects/SimSystem/SimSystem.cs | 51 ++++---- .../Types/TouchPortalEvent.cs | 80 +++++++++++- 5 files changed, 186 insertions(+), 75 deletions(-) diff --git a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs index 98d586d..7ccb969 100644 --- a/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs +++ b/MSFSTouchPortalPlugin-Generator/GenerateDoc.cs @@ -161,8 +161,9 @@ private DocBase CreateModel() { ValueType = ev.ValueType, ValueChoices = ev.ValueChoices, ValueStateId = ev.ValueStateId.Remove(0, ev.ValueStateId.IndexOf('.') + 1), + ChoiceMappings = (Dictionary)ev.GetEventDescriptions(), }; - category.Events.Add(tpEv); + category.Events.Add(tpEv); } // Sort the actions and states for SimConnect groups @@ -317,7 +318,16 @@ private static string CreateMarkdown(DocBase model) { $"{ev.ValueStateId}" + $"{ev.Format}" + $"{ev.ValueType}"); - s.Append("").AppendJoin(", ", ev.ValueChoices).Append(""); + s.Append(""); + if (ev.ChoiceMappings == null || !ev.ChoiceMappings.Any()) { + s.AppendJoin(", ", ev.ValueChoices); + } + else { + s.Append("
details\n
    "); + s.AppendJoin('\n', ev.ChoiceMappings.Select(m => $"
  • {m.Key} - {m.Value}
  • ")); + s.Append($"
"); + } + s.Append(""); s.Append("\n"); }); s.Append("\n\n\n"); diff --git a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs index 752472d..81f6fd7 100644 --- a/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs +++ b/MSFSTouchPortalPlugin-Generator/Model/DocBase.cs @@ -75,6 +75,7 @@ public class DocEvent public string ValueType { get; set; } public virtual string[] ValueChoices { get; set; } public string ValueStateId { get; set; } + public Dictionary ChoiceMappings { get; set; } } } diff --git a/MSFSTouchPortalPlugin/Enums/EventIds.cs b/MSFSTouchPortalPlugin/Enums/EventIds.cs index 0542023..3a766f4 100644 --- a/MSFSTouchPortalPlugin/Enums/EventIds.cs +++ b/MSFSTouchPortalPlugin/Enums/EventIds.cs @@ -1,74 +1,107 @@ - +using System.ComponentModel.DataAnnotations; + namespace MSFSTouchPortalPlugin.Enums { + // Note that the Display.Name properties are used in TP in the event type selector and cannot be changed w/out breaking any events a plugin user may have already set up. + // The Display.Description properties are used for documentation generation. public enum EventIds : short { None = 0, - Ignore, // a way to flag > info log entries which shouldn't trigger a PluginError/SimError events + Ignore, // a way to flag > Info log entries which shouldn't trigger a PluginError/SimError events + // Plugin events - SimConnecting, // attempting to connect + [Display(Name = "Connecting", Description = "Upon every connection attempt to the Simulator.")] + SimConnecting, + + [Display(Name = "Connected", Description = "Upon successful connection to the Simulator.")] SimConnected, + + [Display(Name = "Disconnected", Description = "Upon disconnection from the Simulator.")] SimDisconnected, - SimTimedOut, // connection error, sim not running - SimError, // SimConnect error - PluginError, // internal plugin error - PluginInfo, // internal plugin "info"/success event w/message (log entry) - // SimConnect events (must match names accepted by SimConnect_SubscribeToSystemEvent, see comment block at EOF) + [Display(Name = "Connection Timed Out", Description = "When a connection attempt to the Simulator times out, eg. when sim is not running.")] + SimTimedOut, + + [Display(Name = "SimConnect Error", Description = "When a Simulator (SimConnect) error or warning is detected. Error details (log entry) are sent in the SimSystemEventData state value.")] + SimError, + + [Display(Name = "Plugin Error", Description = "When a Plugin-specific error or warning is detected (eg. could not parse action data, load a file, etc). Error details (log entry) are sent in the SimSystemEventData state value.")] + PluginError, + + [Display(Name = "Plugin Information", Description = "When a notable plugin informational (\"success\") action is detected. Information details (log entry) are sent in the SimSystemEventData state value.")] + PluginInfo, + + // SimConnect events (must match names accepted by SimConnect_SubscribeToSystemEvent) + // https://docs.flightsimulator.com/html/Programming_Tools/SimConnect/API_Reference/Events_And_Data/SimConnect_SubscribeToSystemEvent.htm SimEventNone, // marker - //1sec, - //4sec, - //6Hz, + + [Display(Name = "Aircraft Loaded", Description = "When the aircraft flight dynamics file is changed. These files have a .AIR extension. The filename is sent in the SimSystemEventData state value.")] AircraftLoaded, + + [Display(Name = "Crashed", Description = "When the user aircraft crashes.")] Crashed, + + [Display(Name = "Crash Reset", Description = "When the crash cut-scene has completed.")] CrashReset, + + [Display(Name = "Flight Loaded", Description = "When a flight is loaded. Note that when a flight is ended, a default flight is typically loaded, so these events will occur when flights and missions are started and finished. The filename of the flight loaded is sent in the SimSystemEventData state value.")] FlightLoaded, + + [Display(Name = "Flight Saved", Description = "When a flight is saved correctly. The filename of the flight saved is sent in the SimSystemEventData state value.")] FlightSaved, + + [Display(Name = "Flight Plan Activated", Description = "When a new flight plan is activated. The filename of the activated flight plan is sent in the SimSystemEventData state value.")] FlightPlanActivated, + + [Display(Name = "Flight Plan Deactivated", Description = "When the active flight plan is de-activated.")] FlightPlanDeactivated, - //Frame, // no notification + + //[Display(Description = "Upon every visual frame")] + //Frame, // not working, no notification + + [Display(Name = "Pause Toggled", Description = "When the flight is paused or unpaused, and also immediately returns the current pause state (1 = paused or 0 = unpaused).")] Pause, + + [Display(Name = "Paused", Description = "When the flight is paused.")] Paused, - //PauseFrame, // no notification (I've read this could be used to detect active pause, but...?) + + //[Display(Description = "Upon every visual frame that the simulation is paused.")] + //PauseFrame, // no notification (I've read this could be used to detect active pause, but...?) + + [Display(Name = "Position Changed", Description = "When the user changes the position of their aircraft through a dialog or loading a flight.")] PositionChanged, + + [Display(Name = "Flight Toggled", Description = "When the flight changes between running and not.")] Sim, + + [Display(Name = "Flight Started", Description = "The simulator is running. Typically the user is actively controlling the aircraft on the ground or in the air. However, in some cases additional pairs of SimStart/SimStop events are sent. For example, when a flight is reset the events that are sent are SimStop, SimStart, SimStop, SimStart.")] SimStart, + + [Display(Name = "Flight Stopped", Description = "The simulator is not running. Typically the user is loading a flight, navigating the shell or in a dialog.")] SimStop, + + [Display(Name = "Sound Toggled", Description = "When the master sound switch is changed.")] Sound, + + [Display(Name = "Unpaused", Description = "When the flight is un-paused.")] Unpaused, - View, // this one is actually 2 events, ViewCockpit or ViewExternal (below) + + [Display(Name = "View Changed", Description = "When the user aircraft view is changed. This request will also return the current view immediately. A Enum type is returned in the dwData parameter (0 = External, 2 = Virtual cockpit, .. possibly others for FSX?).")] + View, // this one is actually 2 events, ViewCockpit or ViewExternal (below) and is currently not presented to the user as a choice. + SimEventLast, // marker - // "virtual" events + // "virtual" SimConnect events + [Display(Name = "View 3D Cockpit", Description = "When the view changes to the 3D virtual cockpit view.")] ViewCockpit, // View event if dwData == 2 + + [Display(Name = "View External", Description = "When the view changes to an external view.")] ViewExternal, // View event if dwData == 0 - // start marker for dynamically generated events (TP actions) + // Start marker for dynamically generated events (TP actions) + // Dynamically generated SimConnect client event IDs are "parented" to this enum type, + // meaning they become of this Type when they need to be cast to en Enum type (eg. for SimConnect C# API). + // This is done by ActionEventType as needed to generate unique event IDs for SimConnect. DynamicEventInit = 1000, } } - -/* https://docs.flightsimulator.com/html/Programming_Tools/SimConnect/API_Reference/Events_And_Data/SimConnect_SubscribeToSystemEvent.htm - -1sec Request a notification every second. -4sec Request a notification every four seconds. -6Hz Request notifications six times per second. This is the same rate that joystick movement events are transmitted. -AircraftLoaded Request a notification when the aircraft flight dynamics file is changed. These files have a .AIR extension. The filename is returned in a SIMCONNECT_RECV_EVENT_FILENAME structure. -Crashed Request a notification if the user aircraft crashes. -CrashReset Request a notification when the crash cut-scene has completed. -FlightLoaded Request a notification when a flight is loaded. Note that when a flight is ended, a default flight is typically loaded, so these events will occur when flights and missions are started and finished. The filename of the flight loaded is returned in a SIMCONNECT_RECV_EVENT_FILENAME structure. -FlightSaved Request a notification when a flight is saved correctly. The filename of the flight saved is returned in a SIMCONNECT_RECV_EVENT_FILENAME structure. -FlightPlanActivated Request a notification when a new flight plan is activated. The filename of the activated flight plan is returned in a SIMCONNECT_RECV_EVENT_FILENAME structure. -FlightPlanDeactivated Request a notification when the active flight plan is de-activated. -Frame Request notifications every visual frame. Information is returned in a SIMCONNECT_RECV_EVENT structure. -Pause Request notifications when the flight is paused or unpaused, and also immediately returns the current pause state (1 = paused or 0 = unpaused). The state is returned in the dwData parameter. -Paused Request a notification when the flight is paused. -PauseFrame Request notifications for every visual frame that the simulation is paused. Information is returned in a SIMCONNECT_RECV_EVENT structure. -PositionChanged Request a notification when the user changes the position of their aircraft through a dialog. -Sim Request notifications when the flight is running or not, and also immediately returns the current state (1 = running or 0 = not running). The state is returned in the dwData parameter. -SimStart The simulator is running. Typically the user is actively controlling the aircraft on the ground or in the air. However, in some cases additional pairs of SimStart/SimStop events are sent. For example, when a flight is reset the events that are sent are SimStop, SimStart, SimStop, SimStart. Also when a flight is started with the SHOW_OPENING_SCREEN value set to zero, then an additional SimStart/SimStop pair are sent before a second SimStart event is sent when the scenery is fully loaded. The opening screen provides the options to change aircraft, departure airport, and so on. -SimStop The simulator is not running. Typically the user is loading a flight, navigating the shell or in a dialog. -Sound Requests a notification when the master sound switch is changed. This request will also return the current state of the master sound switch immediately. A flag is returned in the dwData parameter, 0 if the switch is off, SIMCONNECT_SOUND_SYSTEM_EVENT_DATA_MASTER (0x1) if the switch is on. -Unpaused Request a notification when the flight is un-paused. -View Requests a notification when the user aircraft view is changed. This request will also return the current view immediately. A flag is returned in the dwData parameter, one of: SIMCONNECT_VIEW_SYSTEM_EVENT_DATA_COCKPIT_2D SIMCONNECT_VIEW_SYSTEM_EVENT_DATA_COCKPIT_VIRTUAL SIMCONNECT_VIEW_SYSTEM_EVENT_DATA_ORTHOGONAL (the map view). - */ diff --git a/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs b/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs index c9030cb..c6beca4 100644 --- a/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs +++ b/MSFSTouchPortalPlugin/Objects/SimSystem/SimSystem.cs @@ -23,32 +23,31 @@ internal static class SimSystemMapping // Event public static readonly TouchPortalEvent SimSystemEvent = new("SimSystemEvent", "Simulator System Event", "On Simulator Event $val", - new System.Collections.Generic.Dictionary() { - { EventIds.SimConnecting, "Connecting" }, - { EventIds.SimConnected, "Connected" }, - { EventIds.SimDisconnected, "Disconnected" }, - { EventIds.SimTimedOut, "Connection Timed Out" }, - { EventIds.SimError, "SimConnect Error" }, - { EventIds.PluginError, "Plugin Error" }, - { EventIds.PluginInfo, "Plugin Information" }, - { EventIds.Paused, "Paused" }, - { EventIds.Unpaused, "Unpaused" }, - { EventIds.Pause, "Pause Toggled" }, - //{ EventIds.PauseFrame, "Pause Frame" }, // doesn't seem to work - { EventIds.SimStart, "Flight Started" }, - { EventIds.SimStop, "Flight Stopped" }, - { EventIds.Sim, "Flight Toggled" }, - { EventIds.AircraftLoaded, "Aircraft Loaded" }, - { EventIds.Crashed, "Crashed" }, - { EventIds.CrashReset, "Crash Reset" }, - { EventIds.FlightLoaded, "Flight Loaded" }, - { EventIds.FlightSaved, "Flight Saved" }, - { EventIds.FlightPlanActivated, "Flight Plan Activated" }, - { EventIds.FlightPlanDeactivated, "Flight Plan Deactivated" }, - { EventIds.PositionChanged, "Position Changed" }, - { EventIds.Sound, "Sound Toggled" }, - { EventIds.ViewCockpit, "View 3D Cockpit" }, - { EventIds.ViewExternal, "View External" }, + new System.Enum[] { + EventIds.SimConnecting, + EventIds.SimConnected, + EventIds.SimDisconnected, + EventIds.SimTimedOut, + EventIds.SimError, + EventIds.PluginError, + EventIds.PluginInfo, + EventIds.Paused, + EventIds.Unpaused, + EventIds.Pause, + EventIds.SimStart, + EventIds.SimStop, + EventIds.Sim, + EventIds.AircraftLoaded, + EventIds.Crashed, + EventIds.CrashReset, + EventIds.FlightLoaded, + EventIds.FlightSaved, + EventIds.FlightPlanActivated, + EventIds.FlightPlanDeactivated, + EventIds.PositionChanged, + EventIds.Sound, + EventIds.ViewCockpit, + EventIds.ViewExternal, }); } } diff --git a/MSFSTouchPortalPlugin/Types/TouchPortalEvent.cs b/MSFSTouchPortalPlugin/Types/TouchPortalEvent.cs index 75daade..76ef587 100644 --- a/MSFSTouchPortalPlugin/Types/TouchPortalEvent.cs +++ b/MSFSTouchPortalPlugin/Types/TouchPortalEvent.cs @@ -1,6 +1,9 @@ using MSFSTouchPortalPlugin.Enums; +using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; +using DisplayAttribute = System.ComponentModel.DataAnnotations.DisplayAttribute; namespace MSFSTouchPortalPlugin.Types { @@ -25,7 +28,7 @@ public string[] ValueChoices { public DataType DataType { get; set; } = DataType.Choice; - public Dictionary ChoiceMappings + public Dictionary ChoiceMappings { get => _mappings; set { @@ -35,22 +38,87 @@ public Dictionary ChoiceMappings } string[] _choices = null; - Dictionary _mappings = null; + Dictionary _mappings = null; - public TouchPortalEvent(string id, string name, string format, string stateId = null) { + /// + /// Create an event with no choices and optionally a specific DataType. + /// If stateId is null then the id parameter is used. + /// + public TouchPortalEvent(string id, string name, string format, string stateId = null, DataType type = DataType.Choice) { Id = id; Name = name; Format = format; ValueStateId = stateId ?? id; + DataType = type; } - public TouchPortalEvent(string id, string name, string format, string[] choices, string stateId = null) : this(id, name, format, stateId) { + /// + /// Create an event of Choice type with given array of choice text values. + /// If stateId is null then the id parameter is used. + /// + public TouchPortalEvent(string id, string name, string format, string[] choices, string stateId = null) + : this(id, name, format, stateId) + { ValueChoices = choices; } - public TouchPortalEvent(string id, string name, string format, Dictionary mappings, string stateId = null) : this(id, name, format, stateId) { + /// + /// Create an event of Choice type with given dict of Enum ids to TP choice name mappings. + /// If stateId is null then the id parameter is used. + /// + public TouchPortalEvent(string id, string name, string format, Dictionary mappings, string stateId = null) + : this(id, name, format, stateId) + { ChoiceMappings = mappings; } - } + /// + /// Create an event of Choice type with event mappings automatically generated based on DisplayAttribute.Name properties applied to eventIds enum values. + /// If stateId is null then the id parameter is used. + /// + public TouchPortalEvent(string id, string name, string format, IEnumerable eventIds, string stateId = null) + : this(id, name, format, stateId) + { + SetMappingsFromEventIds(eventIds); + } + + /// + /// Returns a dictionary mapping event names (as they would appear in TP UI) to longer descriptions based on DisplayAttribute.Description properties applied to enum values, + /// for doc/UI purposes. Returns an empty dict if no mappings, or no applied DisplayAttributes, were found. + /// + public IReadOnlyDictionary GetEventDescriptions() { + Dictionary ret = new(); + if (_mappings == null || !_mappings.Any()) + return ret; + var enumType = _mappings.Keys.First().GetType(); + foreach (var map in _mappings) { + if (TryGetEnumMetaData(enumType, map.Key, out _, out var descript)) + ret.Add(map.Value, descript); + } + return ret; + } + + void SetMappingsFromEventIds(IEnumerable eventIds) { + ChoiceMappings = new(); + if (!eventIds.Any()) + return; + var enumType = eventIds.First().GetType(); + foreach (var id in eventIds) { + if (TryGetEnumMetaData(enumType, id, out var name, out _)) + _mappings.Add(id, name); + } + _choices = _mappings.Values.ToArray(); + } + + static bool TryGetEnumMetaData(Type enumType, Enum member, out string name, out string descript) { + if (enumType.GetMember(member.ToString()).First()?.GetCustomAttribute() is var attr && attr != null) { + name = attr.Name; + descript = attr.Description; + return true; + } + name = descript = null; + return false; + } + + } } From 0303308604bd326e21d5bb29299d90245c17893c Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sun, 17 Apr 2022 06:03:42 -0400 Subject: [PATCH 90/93] Make most "Set" actions repeatable (on hold); Add optional value for "Add Fuel" action. --- .../Objects/AutoPilot/AutoPilot.cs | 12 ++++++------ .../Objects/FlightSystems/FlightSystems.cs | 16 ++++++++-------- .../Objects/InstrumentsSystems/Engine.cs | 4 ++-- .../Objects/InstrumentsSystems/Fuel.cs | 3 ++- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs index 2e0208f..a43d64f 100644 --- a/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs +++ b/MSFSTouchPortalPlugin/Objects/AutoPilot/AutoPilot.cs @@ -33,7 +33,7 @@ internal static class AutoPilotMapping { [TouchPortalActionMapping("AP_PITCH_REF_SELECT", "Select")] public static readonly object AP_ATTITUDE_PITCH; - [TouchPortalAction("AutoPilotAttitudeSet", "Attitude Hold Pitch Value Set", "MSFS", "Sets the airspeed value", "Set Attitude Hold Pitch Value to {0} (-16384 to +16384)")] + [TouchPortalAction("AutoPilotAttitudeSet", "Attitude Hold Pitch Value Set", "MSFS", "Sets the airspeed value", "Set Attitude Hold Pitch Value to {0} (-16384 to +16384)", true)] [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("AP_PITCH_REF_SET")] public static readonly object AP_ATTITUDE_PITCH_SET; @@ -77,7 +77,7 @@ internal static class AutoPilotMapping { [TouchPortalActionMapping("HEADING_BUG_SELECT", "Select")] public static readonly object AP_HEADING_VAR; - [TouchPortalAction("AutoPilotHeadingSet", "Heading Hold Value Set", "MSFS", "Sets the heading hold value", "Heading Hold Value - {0}")] + [TouchPortalAction("AutoPilotHeadingSet", "Heading Hold Value Set", "MSFS", "Sets the heading hold value", "Heading Hold Value - {0}", true)] [TouchPortalActionText("1", 0, 359)] [TouchPortalActionMapping("HEADING_BUG_SET")] public static readonly object AP_HEADING_SET; @@ -101,7 +101,7 @@ internal static class AutoPilotMapping { [TouchPortalActionMapping("ALTITUDE_BUG_SELECT", "Select")] public static readonly object AP_ALTITUDE_VAR; - [TouchPortalAction("AutoPilotAltitudeSet", "Altitude Hold Value Set", "MSFS", "Sets the altitude hold value", "Altitude Hold Value - {0} to {1}")] + [TouchPortalAction("AutoPilotAltitudeSet", "Altitude Hold Value Set", "MSFS", "Sets the altitude hold value", "Altitude Hold Value - {0} to {1}", true)] [TouchPortalActionChoice(new[] { "Set English", "Set Metric" })] [TouchPortalActionText("0")] [TouchPortalActionMapping("AP_ALT_VAR_SET_ENGLISH", "Set English")] @@ -154,7 +154,7 @@ internal static class AutoPilotMapping { [TouchPortalActionMapping("AP_VS_VAR_SET_CURRENT", "Set Current")] public static readonly object AP_VERTICALSPEED_VAR; - [TouchPortalAction("AutoPilotVerticalSpeedSet", "Vertical Speed Value Set", "MSFS", "Sets the vertical speed value", "Vertical Speed Hold Value - {0} to {1}")] + [TouchPortalAction("AutoPilotVerticalSpeedSet", "Vertical Speed Value Set", "MSFS", "Sets the vertical speed value", "Vertical Speed Hold Value - {0} to {1}", true)] [TouchPortalActionChoice(new[] { "Set English", "Set Metric" })] [TouchPortalActionText("1", -5000, 5000)] [TouchPortalActionMapping("AP_VS_VAR_SET_ENGLISH", "Set English")] @@ -179,7 +179,7 @@ internal static class AutoPilotMapping { [TouchPortalActionMapping("AP_SPD_VAR_DEC", "Decrease")] public static readonly object AP_AIRSPEED_VAR; - [TouchPortalAction("AutoPilotAirSpeedSet", "Airspeed Value Set", "MSFS", "Sets the airspeed value", "Set Airspeed Hold Value to {0}")] + [TouchPortalAction("AutoPilotAirSpeedSet", "Airspeed Value Set", "MSFS", "Sets the airspeed value", "Set Airspeed Hold Value to {0}", true)] [TouchPortalActionText("0", 0, 5000)] [TouchPortalActionMapping("AP_SPD_VAR_SET")] public static readonly object AP_AIRSPEED_SET; @@ -221,7 +221,7 @@ internal static class AutoPilotMapping { [TouchPortalActionMapping("AP_MACH_VAR_DEC", "Decrease")] public static readonly object AP_MACH_VAR; - [TouchPortalAction("AutoPilotMachSet", "Mach Hold Value Set", "MSFS", "Sets the mach hold value", "Set Mach Hold Value to {0}")] + [TouchPortalAction("AutoPilotMachSet", "Mach Hold Value Set", "MSFS", "Sets the mach hold value", "Set Mach Hold Value to {0}", true)] [TouchPortalActionText("0", 0, 20)] [TouchPortalActionMapping("AP_MACH_VAR_SET")] public static readonly object AP_MACH_SET; diff --git a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs index 6cf0a81..398247a 100644 --- a/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs +++ b/MSFSTouchPortalPlugin/Objects/FlightSystems/FlightSystems.cs @@ -14,7 +14,7 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("AILERONS_RIGHT", "Right")] public static readonly object Ailerons; - [TouchPortalAction("AileronsSet", "Ailerons Set", "MSFS", " Set Ailerons", "Ailerons set to {0} (-16384 to +16384)")] + [TouchPortalAction("AileronsSet", "Ailerons Set", "MSFS", " Set Ailerons", "Ailerons set to {0} (-16384 to +16384)", true)] [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("AILERON_SET")] public static readonly object AileronsSet; @@ -29,7 +29,7 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("ELEV_DOWN", "Down")] public static readonly object Elevator; - [TouchPortalAction("ElevatorSet", "Elevator Set", "MSFS", " Set Elevator", "Elevator set to {0} (-16384 to +16384)")] + [TouchPortalAction("ElevatorSet", "Elevator Set", "MSFS", " Set Elevator", "Elevator set to {0} (-16384 to +16384)", true)] [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("ELEVATOR_SET")] public static readonly object ElevatorSet; @@ -65,7 +65,7 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("FLAPS_3", "4")] public static readonly object FlapsHandlePercent; - [TouchPortalAction("FlapsSet", "Flaps Set", "MSFS", " Set Flaps", "Flaps set to {0} (0 to 16384)")] + [TouchPortalAction("FlapsSet", "Flaps Set", "MSFS", " Set Flaps", "Flaps set to {0} (0 to 16384)", true)] [TouchPortalActionText("0", 0, 16384)] [TouchPortalActionMapping("FLAPS_SET")] public static readonly object FlapsSet; @@ -124,7 +124,7 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("RUDDER_RIGHT", "Right")] public static readonly object Rudder; - [TouchPortalAction("RudderSet", "Rudder Set", "MSFS", " Set Rudder", "Rudder set to {0} (-16384 to +16384)")] + [TouchPortalAction("RudderSet", "Rudder Set", "MSFS", " Set Rudder", "Rudder set to {0} (-16384 to +16384)", true)] [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("RUDDER_SET")] public static readonly object RudderSet; @@ -141,7 +141,7 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("SPOILERS_OFF", "Off")] public static readonly object SpoilersAvailable; - [TouchPortalAction("SpoilersSet", "Spoilers Set", "MSFS", " Set Spoilers", "Set Spoilers handle position to {0} (0 to 16384)")] + [TouchPortalAction("SpoilersSet", "Spoilers Set", "MSFS", " Set Spoilers", "Set Spoilers handle position to {0} (0 to 16384)", true)] [TouchPortalActionText("0", 0, 16384)] [TouchPortalActionMapping("SPOILERS_SET")] public static readonly object SpoilersHandlePosition; @@ -163,7 +163,7 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("AILERON_TRIM_RIGHT", "Right")] public static readonly object AileronTrim; - [TouchPortalAction("AileronTrimSet", "Aileron Trim Set", "MSFS", " Set Aileron Trim", "Aileron Trim set to {0}% (-100 - +100)")] + [TouchPortalAction("AileronTrimSet", "Aileron Trim Set", "MSFS", " Set Aileron Trim", "Aileron Trim set to {0}% (-100 - +100)", true)] [TouchPortalActionText("0", -100, 100)] [TouchPortalActionMapping("AILERON_TRIM_SET")] public static readonly object AileronTrimSet; @@ -175,7 +175,7 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("ELEV_TRIM_UP", "Up")] public static readonly object ElevatorTrim; - [TouchPortalAction("ElevatorTrimSet", "Elevator Trim Set", "MSFS", " Set Elevator Trim", "Elevator Trim set to {0} (-16384 to +16384)")] + [TouchPortalAction("ElevatorTrimSet", "Elevator Trim Set", "MSFS", " Set Elevator Trim", "Elevator Trim set to {0} (-16384 to +16384)", true)] [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("ELEVATOR_TRIM_SET")] public static readonly object ElevatorTrimSet; @@ -187,7 +187,7 @@ internal static class FlightSystemsMapping { [TouchPortalActionMapping("RUDDER_TRIM_RIGHT", "Right")] public static readonly object RudderTrim; - [TouchPortalAction("RudderTrimSet", "Rudder Trim Set", "MSFS", " Set Rudder Trim", "Rudder Trim set to {0}% (-100 - +100)")] + [TouchPortalAction("RudderTrimSet", "Rudder Trim Set", "MSFS", " Set Rudder Trim", "Rudder Trim set to {0}% (-100 - +100)", true)] [TouchPortalActionText("0", -100, 100)] [TouchPortalActionMapping("RUDDER_TRIM_SET")] public static readonly object RudderTrimSet; diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs index 45221e2..e4e8a67 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Engine.cs @@ -146,7 +146,7 @@ internal static class EngineMapping { [TouchPortalActionMapping("THROTTLE4_CUT", new[] { "4", "Cut" })] public static readonly object THROTTLE_SPECIFIC; - [TouchPortalAction("ThrottleSet", "Throttle Set", "MSFS", "Sets all or specific Throttle(s) to specific value", "Set Throttle {0} to {1} (-16384 to +16384)")] + [TouchPortalAction("ThrottleSet", "Throttle Set", "MSFS", "Sets all or specific Throttle(s) to specific value", "Set Throttle {0} to {1} (-16384 to +16384)", true)] [TouchPortalActionChoice(new[] { "All", "1", "2", "3", "4" }, "All")] [TouchPortalActionText("0", -16384, 16384)] [TouchPortalActionMapping("THROTTLE_SET", new[] { "All" })] @@ -251,7 +251,7 @@ internal static class EngineMapping { [TouchPortalActionMapping("TOGGLE_FEATHER_SWITCH_4", new[] { "4", "Toggle Feather Switch" })] public static readonly object PROPELLER_PITCH; - [TouchPortalAction("PropellerPitchSet", "Propeller Pitch Set", "MSFS", "Sets propeller pitch lever to value", "Set Propeller {0} Pitch to {1} (0 to 16384)")] + [TouchPortalAction("PropellerPitchSet", "Propeller Pitch Set", "MSFS", "Sets propeller pitch lever to value", "Set Propeller {0} Pitch to {1} (0 to 16384)", true)] [TouchPortalActionChoice(new[] { "All", "1", "2", "3", "4" })] [TouchPortalActionText("0", 0, 16384)] [TouchPortalActionMapping("PROP_PITCH_SET", "All")] diff --git a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs index 2b8fe0c..fe30b3f 100644 --- a/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs +++ b/MSFSTouchPortalPlugin/Objects/InstrumentsSystems/Fuel.cs @@ -6,7 +6,8 @@ namespace MSFSTouchPortalPlugin.Objects.InstrumentsSystems [TouchPortalCategory(Groups.Fuel)] internal static class FuelMapping { - [TouchPortalAction("AddFuel", "Add Fuel", "MSFS", "Adds 25% amount of Fuel", "Add 25% amount of fuel")] + [TouchPortalAction("AddFuel", "Add Fuel", "MSFS", "Add Fuel (1 to 65535 or zero for 25% of capacity)", "Add {0} amount of Fuel", true)] + [TouchPortalActionText("0", 0, 65535)] [TouchPortalActionMapping("ADD_FUEL_QUANTITY")] public static readonly object ADD_FUEL; From 16e0791b993dd646c8666ff282b50ac3a88a8d29 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sun, 17 Apr 2022 07:56:04 -0400 Subject: [PATCH 91/93] Bump version, CHANGELOG.md, README.md. --- CHANGELOG.md | 137 +++++++++++++++++- .../MSFSTouchPortalPlugin.csproj | 4 +- README.md | 64 ++++---- 3 files changed, 169 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e038bff..c8d7ef6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,137 @@ # MSFS Touch Portal Plugin - MP fork +## 0.7.0.0-mp (April-17-2022) + +### Major Features + +#### Simulator Variables (SimVars) +* All SimVar/Touch Portal State definitions loaded from easily editable .ini configuration files. +* Can load custom state config file(s) at startup or via new TP Action. +* Request any arbitrary [Simulation Variable](https://docs.flightsimulator.com/html/Programming_Tools/SimVars/Simulation_Variables.htm) by name to be sent as a TP State via 2 new TP Actions, + with ability to save added variables to a config. file. +* New TP Action to **Set** a value on any settable SimVar. The Action presents a dynamic list of loaded SimVars which are configured to be settable. + +#### Events +* Run any arbitrary [Simulator Event](https://docs.flightsimulator.com/html/Programming_Tools/Event_IDs/Event_IDs.htm) (action), with optional value, + either selected from a list of known (imported) events or any custom event name (allows full MobiFlight compatibility, for example). +* Adds ability to monitor [Simulator System Events](https://docs.flightsimulator.com/html/Programming_Tools/SimConnect/API_Reference/Events_And_Data/SimConnect_SubscribeToSystemEvent.htm) + via a new _Touch Portal_ Event (and corresponding State). +* New _Touch Portal_ Events to indicate simulator connection status, connection timeout, simulator errors/warnings, and plugin errors and informative events. +* A new TP State to transmit corresponding event data, if any, such as name of flight being loaded (for sim System Events) or details about error/information events (log messages). + +#### Performance +The changes below lead to considerable performance improvements and much quicker state updates (when the values change), while also being more efficient. +* SimVars/states are now updated via "push" from SimConnect at a settable period and interval (defaults to every "Sim Frame" eg. ~60Hz) vs. being polled individually every 250ms. + Custom update polling period can also be set per SimVar in milliseconds. +* SimVars are only updated if changed by a configurable epsilon value, eg. for a decimal value SimConnect will not send update unless it changes by more than 0.001. +* Implemented custom version of the Touch Portal C# client/API layer which is several times more performant and, among other things, + allows our plugin to exit cleanly if stopped (send any last state updates, shut down SimConnect connection properly, etc). + +#### Others +* Add ability to use a custom [SimConnect.cfg](https://docs.flightsimulator.com/html/Programming_Tools/SimConnect/SimConnect_CFG_Definition.htm) file for simulator connection properties -- + this allows (among other things) to easily use the plugin on a Touch Portal instance running on a different networked computer than the simulator, + including multiple plugin instances which can be used on multiple TP client devices. +* Add new LogMessags TP State which contains the last 12 plugin log entries (same entries as can be found in the log file, but fewer of them and with shorter formatting). + +### Fixes and Minor Improvements +* Fix missing values for states CowlFlaps1/2/3/4 and AutoPilotFlightDirectorCurrentBank. [29](https://github.com/mpaperno/MSFSTouchPortalPlugin/issues/29) +* Fix that the +/-16383 value limits are actually +/- 16384 regardless of what the SimConnect docs claim (0x4000, which is actually more logical and an even number). +* Fix formatting of _AutoPilotAirSpeedVar_ state. [26](https://github.com/mpaperno/MSFSTouchPortalPlugin/issues/26) +* Fix formatting on "Total percentage of gear extended," "AutoPilot Pitch Reference Value," "AutoPilot Max Bank Angle," "Flight Director Current Bank," and "Flight Director Current Pitch" states. +* Fix _Throttle Specific Decrement_ and _Decrement Small_ actions broken due to spelling. +* Add FlightSystems spoiler states: _SpoilersAvailable_, _SpoilersArmed_, _SpoilersHandlePosition_, _SpoilersLeftPosition_, _SpoilersRightPosition_. [7](https://github.com/mpaperno/MSFSTouchPortalPlugin/issues/7) +* Add plugin version number information States, for both running and entry.tp version numbers. [11](https://github.com/mpaperno/MSFSTouchPortalPlugin/issues/11) +* Add optional value for "Add Fuel" action. +* Make most "Set" actions repeatable (on hold). +* Unit conversions (eg. radians to degrees) are no longer necessary since we can request simulator variable values in specific units right from SimConnect. +* More reliable SimConnect re-connection attempts and connection/disconnection/timeout/error detection and reporting. +* Changed (again) the plugin's TP UI colors to a darker blue and lighter black to help with readability. +* Adjust logging format to include ms, implement better template for log file, allow setting log levels from env. vars, file logging is now asynchronous. +* A lot of the logging in general has been improved/added/made more consistent throughout all the parts of the plugin. Eliminates noise and focuses on important details. +* Numerous internal code optimizations, smaller improvements, organization, and documentation. + +### Documentation +* Add SimVar state's Unit, Format, and Settable status to generated docs. +* Add documentation for new TP Events with full descriptions of each choice (currently there's only the one event in "System" category). +* Add plugin version number to generated docs. +* MSFSTouchPortalPlugin-Generator.exe utility is now included with the plugin for optionally generating entry.tp from custom state configs. Run with `--help` for options. + +### Upgrade Notes +* **The plugin's Settings will be reset to default.** Specifically the "connect on startup" and "action repeat interval" options. +* The new default for the _Connect To Flight Sim on Startup_ setting is **false**. +* **For use with FS-X** (and compatible sims), the new setting "_SimConnect.cfg Index (0 for MSFS, 1 for FSX, or custom)_" should be set to `1`. +* The delay time between reconnection attempts to the simulator (eg. when it's not yet running) has been increased from 10 to 30 seconds. + +(See DOCUMENTATION.md for details on all the new items listed below.) + +#### New Settings +* Sim Variable State Config File(s) (blank = Default) +* SimConnect.cfg Index (0 for MSFS, 1 for FSX, or custom) +* User Config Files Path (blank for default) + +#### New Actions +##### Plugin +* Activate a Custom Simulator Event +* Activate a Simulator Event From List +* Set Simulator Variable Value +* Request a Custom Simulator Variable +* Request a Simulator Variable From List +* Remove a Simulator Variable +* Load SimVar Definitions From File +* Save SimVar Definitions To File +* Reload SimVar Definition Files (as configured in Settings) + +#### New States +##### Plugin +* RunningVersion -- The running plugin version number. +* EntryVersion - The loaded entry.tp plugin version number. +* ConfigVersion - The loaded entry.tp custom configuration version. +* LogMessages - Most recent plugin log messages. +##### Flight Systems +* Spoilers Armed (0/1) +* Spoilers Available (0/1) +* Spoilers Handle Position (0 - 16384) +* Spoilers Left & Right Position Percent +##### System +* SimSystemEvent - Most recent Simulator System Event name (triggers the new events below). +* SimSystemEventData - Data from the most recent Simulator System Event, if any. + +#### New Events +##### System +* Simulator Connecting +* Simulator Connected +* Simulator Disconnected +* Simulator Connection Timed Out +* SimConnect Error +* Plugin Error +* Plugin Information +* Paused +* Unpaused +* Pause Toggled +* Flight Started +* Flight Stopped +* Flight Toggled +* Aircraft Loaded +* Crashed +* Crash Reset +* Flight Loaded +* Flight Saved +* Flight Plan Activated +* Flight Plan Deactivated +* Position Changed +* Sound Toggled +* View 3D Cockpit +* View External + +-------------------------------------------------- + ## 0.6.0-mp (Feb-15-2022) * Added support for sending numeric data values to the simulator with the various "Set" actions. * Most/all "Set" actions have been broken out into their own separate action types. Since they now have a data field, it doesn't make sense to pair them with actions which don't use them, such as "Increment." * Buttons which did *not* use the old "Set" choices, such as "Increment," etc, will continue to work (although they will still show the old choices in existing TP buttons until replaced with the new versions). However any buttons which relied on the old behavior of the "Set" action value always being `0` (zero) will need to be updated. - * Simple arithmetic operations (`+`, `-`, `*`, `/`, `%`, precedence with `( )`, scientific notation) are supported in most of the data fields which can be sent to the sim. -That means you can do things like `25 * 30`, but more interestingly one could use states/variables, like: - `"AP Set Alt. Hold to:" ${value:MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotAltitudeVar} + 1000` + * Simple arithmetic operations (`+`, `-`, `*`, `/`, `%`, precedence with `( )`, scientific notation) are supported in most of the data fields which can be sent to the sim. +That means you can do things like `25 * 30`, but more interestingly one could use states/variables, like: + `"AP Set Alt. Hold to:" ${value:MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotAltitudeVar} + 1000` This evaluation could be expanded upon later if there is a need (to include higher math, rounding, etc). * Generated documentation updated to include all Action Data attributes and mappings to the actual SimConnect events. Also added SimVar names to the list of States. * New TP UI color for MSFS Plugin actions, "MSFS Blue." @@ -44,7 +169,7 @@ This evaluation could be expanded upon later if there is a need (to include high For some hints on using the new Set commands, check out the wiki page [Tips And Tricks For Setting Simulator Values](https://github.com/tlewis17/MSFSTouchPortalPlugin/wiki/Tips-And-Tricks-For-Setting-Simulator-Values). ## 0.5.4-mp (Feb-08-2022) -* Added support for "On Hold" type Touch Portal actions with a configurable repeat time. +* Added support for "On Hold" type Touch Portal actions with a configurable repeat time. All current actions which may make sense to repeat (such as for control surfaces or AP adjustments) should now be available in the "On Hold" TP button configuration page. The generated documentation now shows which actions can be held. - Note that "On Hold" actions do _not_ trigger upon first button press, you need to configure an "On Pressed" action as well, which is a bit more setup but is more flexible @@ -54,7 +179,7 @@ For some hints on using the new Set commands, check out the wiki page [Tips And * Fixed issue with re-connecting to SimConnect automatically despite user's Disconnect/Toggle Off action. * Fixed text values like ATC ID and Aircraft Title not updating properly after the first time. [#42](https://github.com/tlewis17/MSFSTouchPortalPlugin/issues/42) * Fixed/changed light switch states to only reflect switch status, not the light OR switch being on. [#5](https://github.com/tlewis17/MSFSTouchPortalPlugin/issues/5) -* Fixed that Elevevator Trim Position (degrees) was actually reporting the percentage-open value (percents also added, see below). +* Fixed that Elevator Trim Position (degrees) was actually reporting the percentage-open value (percents also added, see below). * More robust recovery (reconnection) in case of (some) unexpected SimConnect errors. * Fixed generated documentation to include all TP States per category. It now also includes settings information. * Less verbose logging by default, with more control via `appsettings.json` file (changes in config reflected w/out restarting the plugin). @@ -186,4 +311,4 @@ Expanded with lots of controls for Flight Control Systems and Electrical. ## 0.1.0 (9-7-2020) This release is primarily as a test run. It Supports a handful of Autopilot functions and a Fuel Selector. -AddFuel doesn't work at the moment. \ No newline at end of file +AddFuel doesn't work at the moment. diff --git a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj index e484a64..fabe903 100644 --- a/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj +++ b/MSFSTouchPortalPlugin/MSFSTouchPortalPlugin.csproj @@ -4,9 +4,7 @@ Exe net5.0 MSFSTouchPortalPlugin - 0.6.0 - 0.6.0.0 - 0.6.0.0 + 0.7.0.0-mp ..\.sonarlint\msfstouchportalplugincsharp.ruleset x64 ..\build\$(Platform)-$(Configuration)\ diff --git a/README.md b/README.md index 7a4120f..e0d403a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# MSFS 2020 TouchPortal Plugin - MP fork +# SimConnect Touch Portal Plugin - MP fork [![verify-build](https://github.com/mpaperno/MSFSTouchPortalPlugin/actions/workflows/verify-build.yml/badge.svg)](https://github.com/mpaperno/MSFSTouchPortalPlugin/actions/workflows/verify-build.yml) [![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/mpaperno/MSFSTouchPortalPlugin?include_prereleases)](https://github.com/mpaperno/MSFSTouchPortalPlugin/releases) @@ -11,35 +11,45 @@ Please note you're on the main branch for this fork (version). This README is sp ## Overview -This plugin will provide a two way interface between Touch Portal and Microsoft Flight Simulator 2020 through SimConnect. This may work for other simulators that use SimConnect. -A good starting point for pages would be to look at FordMustang0288's repo: [MSFSTouchPortalPages](https://github.com/FordMustang0288/MSFSTouchPortalPages). -Another good page for use is created by TaxTips at: [FltSim-msfs2020-Control](https://github.com/HiDTH/FltSim-msfs2020-Control). +This plugin provided a two way interface between [Touch Portal](https://www.touch-portal.com/) software and Flight Simulators which use SimConnect, +such as Microsoft Flight Simulator 2020 (MSFS) and FS-X. + +A good starting point for pages would be to look at those created by Denham at [FltSim-msfs2020-Control](https://github.com/HiDTH/FltSim-msfs2020-Control). +Another source to check out is FordMustang0288's repo: [MSFSTouchPortalPages](https://github.com/FordMustang0288/MSFSTouchPortalPages). A (slightly dated) installation and usage guide is available at _Simvol_ [How to use Touch Portal [with MSFS]](https://www.simvol.org/en/articles/tutorials/use-touch-portal). ## Features -* Connects automatically through SimConnect -* Allows getting data variables from MSFS such as flight instruments or button/switch states. +* Connects automatically through SimConnect. +* Allows getting data variables from simulator such as flight instruments or button/switch states. * Allows triggering various aircraft components from a Touch Portal panel. +* Completely configurable to request any variable or trigger any even supported by the connected simulator, including with custom extensions like MobiFlight. +* Supports simulator system events as Touch Portal events. ## Documentation Auto-generated documentation on all actions, states, and settings can be found in [DOCUMENTATION.MD](DOCUMENTATION.MD). -A [nascent wiki](https://github.com/tlewis17/MSFSTouchPortalPlugin/wiki/) is available with some tips. +A [nascent wiki](https://github.com/mpaperno/MSFSTouchPortalPlugin/wiki/) is available with some tips. ## Installation & Usage 1. Get the latest release of this plugin from the [Releases](https://github.com/mpaperno/MSFSTouchPortalPlugin/releases) page. 2. The plugin is distributed and installed as a standard Touch Portal `.tpp` plugin file. If you know how to import a plugin, -just do that and skip to step 4. There is also a [short guide](https://www.touch-portal.com/blog/post/tutorials/import-plugin-guide.php) on the Touch Portal site. +just do that and skip to step 5. There is also a [short guide](https://www.touch-portal.com/blog/post/tutorials/import-plugin-guide.php) on the Touch Portal site. 3. Import the plugin: - 1. Start _Touch Portal_ (if not already running). + 1. Start/open _Touch Portal_. 2. Click the "gear" icon at the top and select "Import plugin..." from the menu. 3. Browse to where you downloaded this plugin's `.tpp` file and select it. 4. Restart _Touch Portal_ * When prompted by _Touch Portal_ to trust the plugin startup script, select "Yes" (the source code is public!). +5. **By default the plugin will not attempt to connect to a flight simulator on startup.** You have two options: + 1. Create a new Touch Portal button which triggers the "MSFS - Plugin - Toggle/On/Off SimConnect Connection" action. (Also a good place to show the current connection status.) + 2. Go to the plugin's settings, via the Touch Portal "gear" icon then navigate to _Settings -> Plugins -> "MSFS Touch Portal Plugin"_ and set the + "Connect To Flight Sim on Startup" setting to a value of `1` (one). Then save the settings. To restart the plugin, go back to the same plugin settings page and + press the "Stop" button, then the "Start" button. The plugin will keep attempting to connect to the simulator every 30 seconds. +6. **For use with FS-X** (and compatible sims): Change the "SimConnect.cfg Index" plugin setting to `1` (one). After that you will have a list of new actions you can choose from. Also "Dynamic Text" variables are now available. You can see them from the Dynamic Text Updater, @@ -54,12 +64,12 @@ This is very much a work in progress! A comprehensive installation and usage guide was published on the _Simvol_ Web site: [How to use Touch Portal [with MSFS]](https://www.simvol.org/en/articles/tutorials/use-touch-portal). Sample Touch Portal page files can be found at FordMustang0288's [MSFSTouchPortalPages](https://github.com/FordMustang0288/MSFSTouchPortalPages) -and HiDTH's [FltSim-msfs2020-Control](https://github.com/HiDTH/FltSim-msfs2020-Control) +and Denham's [FltSim-msfs2020-Control](https://github.com/HiDTH/FltSim-msfs2020-Control) ## Update Notifications The latest version of this software is always published on the GitHub [Releases](https://github.com/mpaperno/MSFSTouchPortalPlugin/releases) page. -GitHub publishes an "Atom" (like RSS) feed with the latest version of any repository by adding `.atom` to the end o the URL. This reposotiry's +GitHub publishes an "Atom" (like RSS) feed with the latest version of any repository by adding `.atom` to the end o the URL. This repository's release feed URL is:
https://github.com/mpaperno/MSFSTouchPortalPlugin/releases.atom @@ -85,28 +95,28 @@ There is also a Touch Portal Discord server discussion room at [#msfs2020](https ## Related Plugin(s) -My [TJoy Touch Portal Plugin](https://github.com/mpaperno/TJoy) is an interface between Touch Portal and several virtual joystick/gamepad emulation drivers like _vJoy_, _vXBox_, and _ViGEm Bus_. +My [TJoy Touch Portal Plugin](https://github.com/mpaperno/TJoy) is an interface between Touch Portal and several virtual joystick/game pad emulation drivers like _vJoy_, _vXBox_, and _ViGEm Bus_. ## TODO -* Flight Instruments - * Pitch/Bank need to be times by -1. * Sliders ("connectors") -* Events? +* Custom SimVar/states setup GUI + +## Credits +Originally created by Tim Lewis at https://github.com/tlewis17/MSFSTouchPortalPlugin and published under the MIT license. + +Heavily modified by Maxim Paperno, version found at https://github.com/mpaperno/MSFSTouchPortalPlugin -**Documentation revamp** +Uses a modified version of [TouchPortalSDK for C#](https://github.com/oddbear/TouchPortalSDK) library +binaries, used under the MIT license. The modified source is [published here](https://github.com/mpaperno/TouchPortalSDK). -* Create interactive documentation site. -* Also generate base doc data into JSON file from code. -* Version selector -* Note tested/not tested -* Note Sim compatibility, may be useful on tested/not tested -* Filter by category -* Search -* Filter by Sim +Uses a modified version of [SharpConfig](https://github.com/cemdervis/SharpConfig) library, used under the MIT license. +Change log is included in this repo alongside the library files. ## References -* [MSFS EventIds](https://docs.microsoft.com/en-us/previous-versions/microsoft-esp/cc526980\(v=msdn.10\)) -* [MSFS Variables](https://docs.microsoft.com/en-us/previous-versions/microsoft-esp/cc526981\(v=msdn.10\)) -* [FlightSimulator.com SDK Reference](https://docs.flightsimulator.com/html/index.htm#t=Programming_Tools%2FSimVars%2FSimulation_Variables.htm) +* [MSFS EventIDs (old but has all events on one page)](https://docs.microsoft.com/en-us/previous-versions/microsoft-esp/cc526980\(v=msdn.10\)) +* [MSFS Variables (also old but also all on one page)](https://docs.microsoft.com/en-us/previous-versions/microsoft-esp/cc526981\(v=msdn.10\)) +* [FlightSimulator.com SDK Reference (current)](https://docs.flightsimulator.com/html/Programming_Tools/SimConnect/SimConnect_SDK.htm) +* [SDK Event IDs (current)](https://docs.flightsimulator.com/html/Programming_Tools/Event_IDs/Event_IDs.htm) +* [SDK Simulator Variables (current)](https://docs.flightsimulator.com/html/Programming_Tools/SimVars/Simulation_Variables.htm) From 5bdc62daea4e03228f60e985a8190cff953ee602 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sun, 17 Apr 2022 07:58:50 -0400 Subject: [PATCH 92/93] Remove old "actions" and "states" files. --- actions | 266 ---------------------- states | 695 -------------------------------------------------------- 2 files changed, 961 deletions(-) delete mode 100644 actions delete mode 100644 states diff --git a/actions b/actions deleted file mode 100644 index d72c85c..0000000 --- a/actions +++ /dev/null @@ -1,266 +0,0 @@ -// Aircraft Engine -(b'INCREASE_THROTTLE', "Increment throttles", "Shared Cockpit"), -(b'DECREASE_THROTTLE', "Decrement throttles", "Shared Cockpit"), -(b'AXIS_THROTTLE_SET', "Set throttles (0- 16383),", "Shared Cockpit (Pilot only, transmitted to Co-pilot if in a helicopter, not-transmitted otherwise)."), -(b'AXIS_THROTTLE1_SET', "Set throttle 1 exactly (-16383 - +16383),", "Shared Cockpit"), -(b'AXIS_THROTTLE2_SET', "Set throttle 2 exactly (-16383 - +16383),", "Shared Cockpit"), -(b'AXIS_THROTTLE3_SET', "Set throttle 3 exactly (-16383 - +16383),", "Shared Cockpit"), -(b'AXIS_THROTTLE4_SET', "Set throttle 4 exactly (-16383 - +16383),", "Shared Cockpit"), -(b'AXIS_MIXTURE_SET', "Set mixture lever 1 exact value (-16383 to +16383),", "Shared Cockpit"), -(b'AXIS_MIXTURE1_SET', "Set mixture lever 1 exact value (-16383 to +16383),", "Shared Cockpit"), -(b'AXIS_MIXTURE2_SET', "Set mixture lever 2 exact value (-16383 to +16383),", "Shared Cockpit"), -(b'AXIS_MIXTURE3_SET', "Set mixture lever 3 exact value (-16383 to +16383),", "Shared Cockpit"), -(b'AXIS_MIXTURE4_SET', "Set mixture lever 4 exact value (-16383 to +16383),", "Shared Cockpit"), -(b'AXIS_PROPELLER_SET', "Set propeller levers exact value (-16383 to +16383),", "Shared Cockpit"), -(b'AXIS_PROPELLER1_SET', "Set propeller lever 1 exact value (-16383 to +16383),", "Shared Cockpit"), -(b'AXIS_PROPELLER2_SET', "Set propeller lever 2 exact value (-16383 to +16383),", "Shared Cockpit"), -(b'AXIS_PROPELLER3_SET', "Set propeller lever 3 exact value (-16383 to +16383),", "Shared Cockpit"), -(b'AXIS_PROPELLER4_SET', "Set propeller lever 4 exact value (-16383 to +16383),", "Shared Cockpit"), -(b'JET_STARTER', "Selects jet engine starter (for +/- sequence),", "Shared Cockpit"), -(b'COWLFLAP1_SET', "Sets engine 1 cowl flap lever position (0 to 16383),", "Shared Cockpit"), -(b'COWLFLAP2_SET', "Sets engine 2 cowl flap lever position (0 to 16383),", "Shared Cockpit"), -(b'COWLFLAP3_SET', "Sets engine 3 cowl flap lever position (0 to 16383),", "Shared Cockpit"), -(b'COWLFLAP4_SET', "Sets engine 4 cowl flap lever position (0 to 16383),", "Shared Cockpit"), -(b'ENGINE_PRIMER', "Trigger engine primers", "Shared Cockpit"), -(b'TOGGLE_PRIMER', "Trigger engine primers", "Shared Cockpit"), -(b'TOGGLE_PRIMER1', "Trigger engine 1 primer", "Shared Cockpit"), -(b'TOGGLE_PRIMER2', "Trigger engine 2 primer", "Shared Cockpit"), -(b'TOGGLE_PRIMER3', "Trigger engine 3 primer", "Shared Cockpit"), -(b'TOGGLE_PRIMER4', "Trigger engine 4 primer", "Shared Cockpit"), -(b'TOGGLE_PROPELLER_SYNC', "Turns propeller synchronization switch on", "Shared Cockpit"), -(b'TOGGLE_AUTOFEATHER_ARM', "Turns auto-feather arming switch on.", "Shared Cockpit"), -(b'TOGGLE_AFTERBURNER', "Toggles afterburners", "Shared Cockpit"), -(b'TOGGLE_AFTERBURNER1', "Toggles engine 1 afterburner", "Shared Cockpit"), -(b'TOGGLE_AFTERBURNER2', "Toggles engine 2 afterburner", "Shared Cockpit"), -(b'TOGGLE_AFTERBURNER3', "Toggles engine 3 afterburner", "Shared Cockpit"), -(b'TOGGLE_AFTERBURNER4', "Toggles engine 4 afterburner", "Shared Cockpit"), -(b'ENGINE', "Sets engines for 1,2,3,4 selection (to be followed by SELECT_n),", "Shared Cockpit"), - -// Aircraft Controls -(b'AXIS_ELEVATOR_SET', "Sets elevator position (-16383 - +16383),", "Shared Cockpit (Pilot only, and not transmitted to Co-pilot)"), -(b'AXIS_AILERONS_SET', "Sets aileron position (-16383 - +16383),", "Shared Cockpit (Pilot only, and not transmitted to Co-pilot)"), -(b'AXIS_RUDDER_SET', "Sets rudder position (-16383 - +16383),", "Shared Cockpit (Pilot only, and not transmitted to Co-pilot)"), -(b'AXIS_ELEV_TRIM_SET', "Sets elevator trim position (-16383 - +16383),", "Shared Cockpit"), -(b'AXIS_SPOILER_SET', "Sets spoiler handle position (-16383 - +16383),", "All aircraft"), -(b'AXIS_FLAPS_SET', "Sets flap handle to closest increment (-16383 - +16383),", "Shared Cockpit"), - -// Fuel System -(b'FUEL_DUMP_SWITCH_SET', "Set to True or False. The switch can only be set to True if fuel_dump_rate is specified in the aircraft configuration file, which indicates that a fuel dump system exists.", "Shared Cockpit"), -(b'ANTIDETONATION_TANK_VALVE_TOGGLE', "Toggle the antidetonation valve. Pass a value to determine which tank, if there are multiple tanks, to use. Tanks are indexed from 1. Refer to the document Notes on Aircraft Systems.", "Shared Cockpit"), -(b'NITROUS_TANK_VALVE_TOGGLE', "Toggle the nitrous valve. Pass a value to determine which tank, if there are multiple tanks, to use. Tanks are indexed from 1.", "Shared Cockpit"), -(b'REPAIR_AND_REFUEL', "Fully repair and refuel the user aircraft. Ignored if flight realism is enforced.", "Shared Cockpit"), -(b'REQUEST_FUEL_KEY', "Request a fuel truck. The aircraft must be in a parking spot for this to be successful.", "Shared Cockpit"), - -// Avionics -(b'XPNDR', "Sequentially selects the transponder digits for use with +/-.", "Shared Cockpit"), -(b'ADF', "Sequentially selects the ADF tuner digits for use with +/-. Follow by KEY_SELECT_2 for ADF 2.", "Shared Cockpit"), -(b'DME', "Selects the DME for use with +/-", "Shared Cockpit"), -(b'COM_RADIO', "Sequentially selects the COM tuner digits for use with +/-. Follow by KEY_SELECT_2 for COM 2.", "All aircraft"), -(b'VOR_OBS', "Sequentially selects the VOR OBS for use with +/-. Follow by KEY_SELECT_2 for VOR 2.", "Shared Cockpit"), -(b'NAV_RADIO', "Sequentially selects the NAV tuner digits for use with +/-. Follow by KEY_SELECT_2 for NAV 2.", "Shared Cockpit"), -(b'ADF_100_INC', "Increments ADF by 100 KHz", "Shared Cockpit"), -(b'ADF_10_INC', "Increments ADF by 10 KHz", "Shared Cockpit"), -(b'ADF_1_INC', "Increments ADF by 1 KHz", "Shared Cockpit"), -(b'XPNDR_1000_INC', "Increments first digit of transponder", "Shared Cockpit"), -(b'XPNDR_100_INC', "Increments second digit of transponder", "Shared Cockpit"), -(b'XPNDR_10_INC', "Increments third digit of transponder", "Shared Cockpit"), -(b'XPNDR_1_INC', "Increments fourth digit of transponder", "Shared Cockpit"), -(b'VOR1_OBI_DEC', "Decrements the VOR 1 OBS setting", "Shared Cockpit"), -(b'VOR1_OBI_INC', "Increments the VOR 1 OBS setting", "Shared Cockpit"), -(b'VOR2_OBI_DEC', "Decrements the VOR 2 OBS setting", "Shared Cockpit"), -(b'VOR2_OBI_INC', "Increments the VOR 2 OBS setting", "Shared Cockpit"), -(b'ADF_100_DEC', "Decrements ADF by 100 KHz", "Shared Cockpit"), -(b'ADF_10_DEC', "Decrements ADF by 10 KHz", "Shared Cockpit"), -(b'ADF_1_DEC', "Decrements ADF by 1 KHz", "Shared Cockpit"), -(b'ADF_SET', "Sets ADF frequency (BCD Hz),", "Shared Cockpit"), -(b'XPNDR_SET', "Sets transponder code (BCD),", "All aircraft"), -(b'VOR1_SET', "Sets OBS 1 (0 to 360),", "Shared Cockpit"), -(b'VOR2_SET', "Sets OBS 2 (0 to 360),", "Shared Cockpit"), -(b'DME1_TOGGLE', "Sets DME display to Nav 1", "Shared Cockpit"), -(b'DME2_TOGGLE', "Sets DME display to Nav 2", "Shared Cockpit"), -(b'RADIO_VOR1_IDENT_DISABLE', "Turns NAV 1 ID off", "Shared Cockpit"), -(b'RADIO_VOR2_IDENT_DISABLE', "Turns NAV 2 ID off", "Shared Cockpit"), -(b'RADIO_DME1_IDENT_DISABLE', "Turns DME 1 ID off", "Shared Cockpit"), -(b'RADIO_DME2_IDENT_DISABLE', "Turns DME 2 ID off", "Shared Cockpit"), -(b'RADIO_ADF_IDENT_DISABLE', "Turns ADF 1 ID off", "Shared Cockpit"), -(b'RADIO_VOR1_IDENT_ENABLE', "Turns NAV 1 ID on", "Shared Cockpit"), -(b'RADIO_VOR2_IDENT_ENABLE', "Turns NAV 2 ID on", "Shared Cockpit"), -(b'RADIO_DME1_IDENT_ENABLE', "Turns DME 1 ID on", "Shared Cockpit"), -(b'RADIO_DME2_IDENT_ENABLE', "Turns DME 2 ID on", "Shared Cockpit"), -(b'RADIO_ADF_IDENT_ENABLE', "Turns ADF 1 ID on", "Shared Cockpit"), -(b'RADIO_VOR1_IDENT_TOGGLE', "Toggles NAV 1 ID", "Shared Cockpit"), -(b'RADIO_VOR2_IDENT_TOGGLE', "Toggles NAV 2 ID", "Shared Cockpit"), -(b'RADIO_DME1_IDENT_TOGGLE', "Toggles DME 1 ID", "Shared Cockpit"), -(b'RADIO_DME2_IDENT_TOGGLE', "Toggles DME 2 ID", "Shared Cockpit"), -(b'RADIO_ADF_IDENT_TOGGLE', "Toggles ADF 1 ID", "Shared Cockpit"), -(b'RADIO_VOR1_IDENT_SET', "Sets NAV 1 ID (on/off),", "Shared Cockpit"), -(b'RADIO_VOR2_IDENT_SET', "Sets NAV 2 ID (on/off),", "Shared Cockpit"), -(b'RADIO_DME1_IDENT_SET', "Sets DME 1 ID (on/off),", "Shared Cockpit"), -(b'RADIO_DME2_IDENT_SET', "Sets DME 2 ID (on/off),", "Shared Cockpit"), -(b'RADIO_ADF_IDENT_SET', "Sets ADF 1 ID (on/off),", "Shared Cockpit"), -(b'ADF_CARD_INC', "Increments ADF card", "Shared Cockpit"), -(b'ADF_CARD_DEC', "Decrements ADF card", "Shared Cockpit"), -(b'ADF_CARD_SET', "Sets ADF card (0-360),", "Shared Cockpit"), -(b'TOGGLE_DME', "Toggles between NAV 1 and NAV 2", "Shared Cockpit"), -(b'AVIONICS_MASTER_SET', "Sets the avionics master switch", "All aircraft"), -(b'ADF1_RADIO_TENTHS_DEC', "Decrements ADF 1 by 0.1 KHz.", "Shared Cockpit"), -(b'ADF1_RADIO_TENTHS_INC', "Increments ADF 1 by 0.1 KHz.", "Shared Cockpit"), -(b'XPNDR_1000_DEC', "Decrements first digit of transponder", "Shared Cockpit"), -(b'XPNDR_100_DEC', "Decrements second digit of transponder", "Shared Cockpit"), -(b'XPNDR_10_DEC', "Decrements third digit of transponder", "Shared Cockpit"), -(b'XPNDR_1_DEC', "Decrements fourth digit of transponder", "Shared Cockpit"), -(b'XPNDR_DEC_CARRY', "Decrements fourth digit of transponder, and with carry.", "Shared Cockpit"), -(b'XPNDR_INC_CARRY', "Increments fourth digit of transponder, and with carry.", "Shared Cockpit"), -(b'ADF_FRACT_DEC_CARRY', "Decrements ADF 1 frequency by 0.1 KHz, with carry", "Shared Cockpit"), -(b'ADF_FRACT_INC_CARRY', "Increments ADF 1 frequency by 0.1 KHz, with carry", "Shared Cockpit"), -(b'COM1_TRANSMIT_SELECT', "Selects COM 1 to transmit", "All aircraft"), -(b'COM2_TRANSMIT_SELECT', "Selects COM 2 to transmit", "All aircraft"), -(b'COM_RECEIVE_ALL_TOGGLE', "Toggles all COM radios to receive on", "All aircraft"), -(b'COM_RECEIVE_ALL_SET', "Sets whether to receive on all COM radios (1,0),", "All aircraft"), -(b'MARKER_SOUND_TOGGLE', "Toggles marker beacon sound on/off", "Shared Cockpit"), -(b'Unsupported', "Sets marker beacon sound (1, 0),", "Shared Cockpit"), -(b'ADF_COMPLETE_SET', "Sets ADF 1 frequency (BCD Hz),", "Shared Cockpit"), -(b'ADF1_WHOLE_INC', "Increments ADF 1 by 1 KHz, with carry as digits wrap.", "Shared Cockpit"), -(b'ADF1_WHOLE_DEC', "Decrements ADF 1 by 1 KHz, with carry as digits wrap.", "Shared Cockpit"), -(b'ADF2_100_INC', "Increments the ADF 2 frequency 100 digit, with wrapping", "Shared Cockpit"), -(b'ADF2_10_INC', "Increments the ADF 2 frequency 10 digit, with wrapping", "Shared Cockpit"), -(b'ADF2_1_INC', "Increments the ADF 2 frequency 1 digit, with wrapping", "Shared Cockpit"), -(b'ADF2_RADIO_TENTHS_INC', "Increments ADF 2 frequency 1/10 digit, with wrapping", "Shared Cockpit"), -(b'ADF2_100_DEC', "Decrements the ADF 2 frequency 100 digit, with wrapping", "Shared Cockpit"), -(b'ADF2_10_DEC', "Decrements the ADF 2 frequency 10 digit, with wrapping", "Shared Cockpit"), -(b'ADF2_1_DEC', "Decrements the ADF 2 frequency 1 digit, with wrapping", "Shared Cockpit"), -(b'ADF2_RADIO_TENTHS_DEC', "Decrements ADF 2 frequency 1/10 digit, with wrapping", "Shared Cockpit"), -(b'ADF2_WHOLE_INC', "Increments ADF 2 by 1 KHz, with carry as digits wrap.", "Shared Cockpit"), -(b'ADF2_WHOLE_DEC', "Decrements ADF 2 by 1 KHz, with carry as digits wrap.", "Shared Cockpit"), -(b'ADF2_FRACT_DEC_CARRY', "Decrements ADF 2 frequency by 0.1 KHz, with carry", "Shared Cockpit"), -(b'ADF2_FRACT_INC_CARRY', "Increments ADF 2 frequency by 0.1 KHz, with carry", "Shared Cockpit"), -(b'ADF2_COMPLETE_SET', "Sets ADF 1 frequency (BCD Hz),", "Shared Cockpit"), -(b'RADIO_ADF2_IDENT_DISABLE', "Turns ADF 2 ID off", "Shared Cockpit"), -(b'RADIO_ADF2_IDENT_ENABLE', "Turns ADF 2 ID on", "Shared Cockpit"), -(b'RADIO_ADF2_IDENT_TOGGLE', "Toggles ADF 2 ID", "Shared Cockpit"), -(b'RADIO_ADF2_IDENT_SET', "Sets ADF 2 ID on/off (1,0),", "Shared Cockpit"), -(b'FREQUENCY_SWAP', "Swaps frequency with standby on whichever NAV or COM radio is selected.", "Shared Cockpit"), -(b'TOGGLE_GPS_DRIVES_NAV1', "Toggles between GPS and NAV 1 driving NAV 1 OBS display (and AP),", "Shared Cockpit"), -(b'GPS_POWER_BUTTON', "Toggles power button", "Shared Cockpit"), -(b'GPS_NEAREST_BUTTON', "Selects Nearest Airport Page", "Shared Cockpit"), -(b'GPS_OBS_BUTTON', "Toggles automatic sequencing of waypoints", "Shared Cockpit"), -(b'GPS_MSG_BUTTON', "Toggles the Message Page", "Shared Cockpit"), -(b'GPS_MSG_BUTTON_DOWN', "Triggers the pressing of the message button.", "Shared Cockpit"), -(b'GPS_MSG_BUTTON_UP', "Triggers the release of the message button", "Shared Cockpit"), -(b'GPS_FLIGHTPLAN_BUTTON', "Displays the programmed flightplan.", "Shared Cockpit"), -(b'GPS_TERRAIN_BUTTON', "Displays terrain information on default display", "Shared Cockpit"), -(b'GPS_PROCEDURE_BUTTON', "Displays the approach procedure page.", "Shared Cockpit"), -(b'GPS_ZOOMIN_BUTTON', "Zooms in default display", "Shared Cockpit"), -(b'GPS_ZOOMOUT_BUTTON', "Zooms out default display", "Shared Cockpit"), -(b'GPS_DIRECTTO_BUTTON', "Brings up the \"Direct To\" page", "Shared Cockpit"), -(b'GPS_MENU_BUTTON', "Brings up page to select active legs in a flightplan.", "Shared Cockpit"), -(b'GPS_CLEAR_BUTTON', "Clears entered data on a page", "Shared Cockpit"), -(b'GPS_CLEAR_ALL_BUTTON', "Clears all data immediately", "Shared Cockpit"), -(b'GPS_CLEAR_BUTTON_DOWN', "Triggers the pressing of the Clear button", "Shared Cockpit"), -(b'GPS_CLEAR_BUTTON_UP', "Triggers the release of the Clear button.", "Shared Cockpit"), -(b'GPS_ENTER_BUTTON', "Approves entered data.", "Shared Cockpit"), -(b'GPS_CURSOR_BUTTON', "Selects GPS cursor", "Shared Cockpit"), -(b'GPS_GROUP_KNOB_INC', "Increments cursor", "Shared Cockpit"), -(b'GPS_GROUP_KNOB_DEC', "Decrements cursor", "Shared Cockpit"), -(b'GPS_PAGE_KNOB_INC', "Increments through pages", "Shared Cockpit"), -(b'GPS_PAGE_KNOB_DEC', "Decrements through pages", "Shared Cockpit"), -(b'DME_SELECT', "Selects one of the two DME systems (1,2),.", "Shared Cockpit"), -(b'RADIO_SELECTED_DME_IDENT_ENABLE', "Turns on the identification sound for the selected DME.", "Shared Cockpit"), -(b'RADIO_SELECTED_DME_IDENT_DISABLE', "Turns off the identification sound for the selected DME.", "Shared Cockpit"), -(b'RADIO_SELECTED_DME_IDENT_SET', "Sets the DME identification sound to the given filename.", "Shared Cockpit"), -(b'RADIO_SELECTED_DME_IDENT_TOGGLE', "Turns on or off the identification sound for the selected DME.", "Shared Cockpit"), - -// Instruments -(b'EGT', "Selects EGT bug for +/-", "Shared Cockpit"), -(b'EGT_INC', "Increments EGT bugs", "Shared Cockpit"), -(b'EGT_DEC', "Decrements EGT bugs", "Shared Cockpit"), -(b'EGT_SET', "Sets EGT bugs (0 to 32767),", "Shared Cockpit"), -(b'BAROMETRIC', "Syncs altimeter setting to sea level pressure, or 29.92 if above 18000 feet", "Shared Cockpit"), -(b'GYRO_DRIFT_INC', "Increments heading indicator", "Shared Cockpit"), -(b'GYRO_DRIFT_DEC', "Decrements heading indicator", "Shared Cockpit"), -(b'KOHLSMAN_INC', "Increments altimeter setting", "Shared Cockpit"), -(b'KOHLSMAN_DEC', "Decrements altimeter setting", "Shared Cockpit"), -(b'KOHLSMAN_SET', "Sets altimeter setting (Millibars * 16),", "Shared Cockpit"), -(b'TRUE_AIRSPEED_CAL_INC', "Increments airspeed indicators true airspeed reference card", "Shared Cockpit"), -(b'TRUE_AIRSPEED_CAL_DEC', "Decrements airspeed indicators true airspeed reference card", "Shared Cockpit"), -(b'TRUE_AIRSPEED_CAL_SET', "Sets airspeed indicators true airspeed reference card (degrees, where 0 is standard sea level conditions),", "Shared Cockpit"), -(b'EGT1_INC', "Increments EGT bug 1", "Shared Cockpit"), -(b'EGT1_DEC', "Decrements EGT bug 1", "Shared Cockpit"), -(b'EGT1_SET', "Sets EGT bug 1 (0 to 32767),", "Shared Cockpit"), -(b'EGT2_INC', "Increments EGT bug 2", "Shared Cockpit"), -(b'EGT2_DEC', "Decrements EGT bug 2", "Shared Cockpit"), -(b'EGT2_SET', "Sets EGT bug 2 (0 to 32767),", "Shared Cockpit"), -(b'EGT3_INC', "Increments EGT bug 3", "Shared Cockpit"), -(b'EGT3_DEC', "Decrements EGT bug 3", "Shared Cockpit"), -(b'EGT3_SET', "Sets EGT bug 3 (0 to 32767),", "Shared Cockpit"), -(b'EGT4_INC', "Increments EGT bug 4", "Shared Cockpit"), -(b'EGT4_DEC', "Decrements EGT bug 4", "Shared Cockpit"), -(b'EGT4_SET', "Sets EGT bug 4 (0 to 32767),", "Shared Cockpit"), -(b'ATTITUDE_BARS_POSITION_UP', "Increments attitude indicator pitch reference bars", "Shared Cockpit"), -(b'ATTITUDE_BARS_POSITION_DOWN', "Decrements attitude indicator pitch reference bars", "Shared Cockpit"), -(b'ATTITUDE_CAGE_BUTTON', "Cages attitude indicator at 0 pitch and bank", "Shared Cockpit"), -(b'RESET_G_FORCE_INDICATOR', "Resets max/min indicated G force to 1.0.", "Shared Cockpit"), -(b'RESET_MAX_RPM_INDICATOR', "Reset max indicated engine rpm to 0.", "Shared Cockpit"), -(b'HEADING_GYRO_SET', "Sets heading indicator to 0 drift error.", "Shared Cockpit"), -(b'GYRO_DRIFT_SET', "Sets heading indicator drift angle (degrees),.", "Shared Cockpit"), - -// Lights -(b'LANDING_LIGHT_UP', "Rotate landing light up", "Shared Cockpit"), -(b'LANDING_LIGHT_DOWN', "Rotate landing light down", "Shared Cockpit"), -(b'LANDING_LIGHT_LEFT', "Rotate landing light left", "Shared Cockpit"), -(b'LANDING_LIGHT_RIGHT', "Rotate landing light right", "Shared Cockpit"), -(b'LANDING_LIGHT_HOME', "Return landing light to default position", "Shared Cockpit"), - -// Misc -(b'SMOKE_TOGGLE', "Toggle smoke system switch", "All aircraft"), -(b'SMOKE_ON', "Turns smoke system on", "All aircraft"), -(b'SMOKE_OFF', "Turns smoke system off", "All aircraft"), -(b'SMOKE_SET', "Sets smoke system on/off (1,0),", "All aircraft"), -(b'TOGGLE_ELECTRIC_VACUUM_PUMP', "Toggles backup electric vacuum pump", "Shared Cockpit"), -(b'TOGGLE_ALTERNATE_STATIC', "Toggles alternate static pressure port", "All aircraft"), -(b'DECREASE_DECISION_HEIGHT', "Decrements decision height reference", "Shared Cockpit"), -(b'INCREASE_DECISION_HEIGHT', "Increments decision height reference", "Shared Cockpit"), -(b'AXIS_LEFT_BRAKE_SET', "Sets left brake position from axis controller (e.g. joystick),. -16383 (0 brakes) to +16383 (max brakes)", "Shared Cockpit"), -(b'AXIS_RIGHT_BRAKE_SET', "Sets right brake position from axis controller (e.g. joystick),. -16383 (0 brakes) to +16383 (max brakes)", "Shared Cockpit"), -(b'TOGGLE_AIRCRAFT_EXIT', "Toggles primary door open/close. Follow by KEY_SELECT_2, etc for subsequent doors.", "Shared Cockpit"), -(b'TOGGLE_WING_FOLD', "Toggles wing folding", "Shared Cockpit"), -(b'SET_WING_FOLD', '''Sets the wings into the folded position suitable for storage, typically on a carrier. Takes a value: - 1 - fold wings, - 0 - unfold wings''', "Shared Cockpit"), -(b'TOGGLE_TAIL_HOOK_HANDLE', "Toggles tail hook", "Shared Cockpit"), -(b'SET_TAIL_HOOK_HANDLE', '''Sets the tail hook handle. Takes a value: - 1 - set tail hook, - 0 - retract tail hook''', "Shared Cockpit"), -(b'TOGGLE_WATER_RUDDER', "Toggles water rudders", "Shared Cockpit"), -(b'TOGGLE_PUSHBACK', "Toggles pushback.", "Shared Cockpit"), -(b'KEY_TUG_HEADING', "Triggers tug and sets the desired heading. The units are a 32 bit integer (0 to 4294967295), which represent 0 to 360 degrees. To set a 45 degree angle, for example, set the value to 4294967295 / 8.", "Shared Cockpit"), -(b'KEY_TUG_SPEED', "Triggers tug, and sets desired speed, in feet per second. The speed can be both positive (forward movement), and negative (backward movement).", "Shared Cockpit"), -(b'TUG_DISABLE', "Disables tug", "Shared Cockpit"), -(b'TOGGLE_TAILWHEEL_LOCK', "Toggles tail wheel lock", "Shared Cockpit"), -(b'TOW_PLANE_RELEASE', "Release a towed aircraft, usually a glider.", "Shared Cockpit"), -(b'TOW_PLANE_REQUEST', "Request a tow plane. The user aircraft must be tow-able, stationary, on the ground and not already attached for this to succeed.", "Shared Cockpit"), -(b'RELEASE_DROPPABLE_OBJECTS', "Release one droppable object. Multiple key events will release multiple objects.", "Shared Cockpit"), -(b'RETRACT_FLOAT_SWITCH_DEC', "If the plane has retractable floats, moves the retract position from Extend to Neutral, or Neutral to Retract.", "Shared Cockpit"), -(b'RETRACT_FLOAT_SWITCH_INC', "If the plane has retractable floats, moves the retract position from Retract to Neutral, or Neutral to Extend.", "Shared Cockpit"), -(b'TOGGLE_WATER_BALLAST_VALVE', "Turn the water ballast valve on or off.", "Shared Cockpit"), -(b'TOGGLE_VARIOMETER_SWITCH', "Turn the variometer on or off.", "Shared Cockpit"), -(b'TOGGLE_TURN_INDICATOR_SWITCH', "Turn the turn indicator on or off.", "Shared Cockpit"), -(b'APU_STARTER', "Start up the auxiliary power unit (APU),.", "Shared Cockpit"), -(b'APU_OFF_SWITCH', "Turn the APU off.", "Shared Cockpit"), -(b'APU_GENERATOR_SWITCH_TOGGLE', "Turn the auxiliary generator on or off.", "Shared Cockpit"), -(b'APU_GENERATOR_SWITCH_SET', "Set the auxiliary generator switch (0,1),.", "Shared Cockpit"), -(b'EXTINGUISH_ENGINE_FIRE', "Takes a two digit argument. The first digit represents the fire extinguisher index, and the second represents the engine index. For example, 11 would represent using bottle 1 on engine 1. 21 would represent using bottle 2 on engine 1. Typical entries for a twin engine aircraft would be 11 and 22.", "Shared Cockpit"), -(b'HYDRAULIC_SWITCH_TOGGLE', "Turn the hydraulic switch on or off.", "Shared Cockpit"), -(b'BLEED_AIR_SOURCE_CONTROL_INC', "Increases the bleed air source control.", "Shared Cockpit"), -(b'BLEED_AIR_SOURCE_CONTROL_DEC', "Decreases the bleed air source control.", "Shared Cockpit"), -(b'BLEED_AIR_SOURCE_CONTROL_SET', '''Set to one of: - 0: auto - 1: off - 2: apu - 3: engines''', "Shared Cockpit"), -(b'TURBINE_IGNITION_SWITCH_TOGGLE', "Turn the turbine ignition switch on or off.", "Shared Cockpit"), -(b'CABIN_NO_SMOKING_ALERT_SWITCH_TOGGLE', "Turn the \"No smoking\" alert on or off.", "Shared Cockpit"), -(b'CABIN_SEATBELTS_ALERT_SWITCH_TOGGLE', "Turn the \"Fasten seatbelts\" alert on or off.", "Shared Cockpit"), -(b'ANTISKID_BRAKES_TOGGLE', "Turn the anti-skid braking system on or off.", "Shared Cockpit"), -(b'GPWS_SWITCH_TOGGLE', "Turn the g round proximity warning system (GPWS), on or off.", "Shared Cockpit"), -(b'MANUAL_FUEL_PRESSURE_PUMP', "Activate the manual fuel pressure pump.", "Shared Cockpit"), \ No newline at end of file diff --git a/states b/states deleted file mode 100644 index 602d66d..0000000 --- a/states +++ /dev/null @@ -1,695 +0,0 @@ -// Engines -"NUMBER_OF_ENGINES": ["Number of engines (minimum 0, maximum 4)", b'NUMBER OF ENGINES', b'Number', 'N'], -"ENGINE_CONTROL_SELECT": ["Selected engines (combination of bit flags); 1 = Engine 1; 2 = Engine 2; 4 = Engine 3; 8 = Engine 4", b'ENGINE CONTROL SELECT', b'Mask', 'Y'], -"THROTTLE_LOWER_LIMIT": ["Percent throttle defining lower limit (negative for reverse thrust equipped airplanes)", b'THROTTLE LOWER LIMIT', b'Percent', 'N'], -"ENGINE_TYPE": ["Engine type:; 0 = Piston; 1 = Jet; 2 = None; 3 = Helo(Bell) turbine; 4 = Unsupported; 5 = Turboprop", b'ENGINE TYPE', b'Enum', 'N'], -"GENERAL_ENG_COMBUSTION:index": ["Combustion flag", b'GENERAL ENG COMBUSTION:index', b'Bool', 'Y'], -"GENERAL_ENG_MASTER_ALTERNATOR:index": ["Alternator (generator) switch", b'GENERAL ENG MASTER ALTERNATOR:index', b'Bool', 'N'], -"GENERAL_ENG_FUEL_PUMP_SWITCH:index": ["Fuel pump switch", b'GENERAL ENG FUEL PUMP SWITCH:index', b'Bool', 'N'], -"GENERAL_ENG_FUEL_PUMP_ON:index": ["Fuel pump on/off", b'GENERAL ENG FUEL PUMP ON:index', b'Bool', 'N'], -"GENERAL_ENG_PCT_MAX_RPM:index": ["Percent of max rated rpm", b'GENERAL ENG PCT MAX RPM:index', b'Percent', 'N'], -"GENERAL_ENG_MAX_REACHED_RPM:index": ["Maximum attained rpm", b'GENERAL ENG MAX REACHED RPM:index', b'Rpm', 'N'], -"GENERAL_ENG_STARTER:index": ["Engine starter on/off", b'GENERAL ENG STARTER:index', b'Bool', 'N'], -"GENERAL_ENG_EXHAUST_GAS_TEMPERATURE:index": ["Engine exhaust gas temperature.", b'GENERAL ENG EXHAUST GAS TEMPERATURE:index', b'Rankine', 'Y'], -"GENERAL_ENG_OIL_PRESSURE:index": ["Engine oil pressure", b'GENERAL ENG OIL PRESSURE:index', b'Psf', 'Y'], -"GENERAL_ENG_OIL_LEAKED_PERCENT:index": ["Percent of max oil capacity leaked", b'GENERAL ENG OIL LEAKED PERCENT:index', b'Percent', 'N'], -"GENERAL_ENG_COMBUSTION_SOUND_PERCENT:index": ["Percent of maximum engine sound", b'GENERAL ENG COMBUSTION SOUND PERCENT:index', b'Percent', 'N'], -"GENERAL_ENG_DAMAGE_PERCENT:index": ["Percent of total engine damage", b'GENERAL ENG DAMAGE PERCENT:index', b'Percent', 'N'], -"GENERAL_ENG_OIL_TEMPERATURE:index": ["Engine oil temperature", b'GENERAL ENG OIL TEMPERATURE:index', b'Rankine', 'Y'], -"GENERAL_ENG_FAILED:index": ["Fail flag", b'GENERAL ENG FAILED:index', b'Bool', 'N'], -"GENERAL_ENG_GENERATOR_SWITCH:index": ["Alternator (generator) switch", b'GENERAL ENG GENERATOR SWITCH:index', b'Bool', 'N'], -"GENERAL_ENG_GENERATOR_ACTIVE:index": ["Alternator (generator) on/off", b'GENERAL ENG GENERATOR ACTIVE:index', b'Bool', 'Y'], -"GENERAL_ENG_FUEL_VALVE:index": ["Fuel valve state", b'GENERAL ENG FUEL VALVE:index', b'Bool', 'N'], -"GENERAL_ENG_FUEL_PRESSURE:index": ["Engine fuel pressure", b'GENERAL ENG FUEL PRESSURE:index', b'Psi', 'Y'], -"GENERAL_ENG_ELAPSED_TIME:index": ["Total engine elapsed time", b'GENERAL ENG ELAPSED TIME:index', b'Hours', 'N'], -"RECIP_ENG_PRIMER:index": ["Engine primer position", b'RECIP ENG PRIMER:index', b'Bool', 'Y'], -"RECIP_ENG_MANIFOLD_PRESSURE:index": ["Engine manifold pressure", b'RECIP ENG MANIFOLD PRESSURE:index', b'Psi', 'Y'], -"RECIP_ENG_ALTERNATE_AIR_POSITION:index": ["Alternate air control", b'RECIP ENG ALTERNATE AIR POSITION:index', b'Position', 'Y'], -"RECIP_ENG_COOLANT_RESERVOIR_PERCENT:index": ["Percent coolant available", b'RECIP ENG COOLANT RESERVOIR PERCENT:index', b'Percent', 'Y'], -"RECIP_ENG_LEFT_MAGNETO:index": ["Left magneto state", b'RECIP ENG LEFT MAGNETO:index', b'Bool', 'Y'], -"RECIP_ENG_RIGHT_MAGNETO:index": ["Right magneto state", b'RECIP ENG RIGHT MAGNETO:index', b'Bool', 'Y'], -"RECIP_ENG_BRAKE_POWER:index": ["Brake power produced by engine", b'RECIP ENG BRAKE POWER:index', b'Foot pounds per second', 'Y'], -"RECIP_ENG_STARTER_TORQUE:index": ["Torque produced by engine", b'RECIP ENG STARTER TORQUE:index', b'Foot pound', 'Y'], -"RECIP_ENG_TURBOCHARGER_FAILED:index": ["Turbo failed state", b'RECIP ENG TURBOCHARGER FAILED:index', b'Bool', 'Y'], -"RECIP_ENG_EMERGENCY_BOOST_ACTIVE:index": ["War emergency power active", b'RECIP ENG EMERGENCY BOOST ACTIVE:index', b'Bool', 'Y'], -"RECIP_ENG_EMERGENCY_BOOST_ELAPSED_TIME:index": ["Elapsed time war emergency power active", b'RECIP ENG EMERGENCY BOOST ELAPSED TIME:index', b'Hours', 'Y'], -"RECIP_ENG_WASTEGATE_POSITION:index": ["Percent turbo wastegate closed", b'RECIP ENG WASTEGATE POSITION:index', b'Percent', 'Y'], -"RECIP_ENG_TURBINE_INLET_TEMPERATURE:index": ["Engine turbine inlet temperature", b'RECIP ENG TURBINE INLET TEMPERATURE:index', b'Celsius', 'Y'], -"RECIP_ENG_CYLINDER_HEAD_TEMPERATURE:index": ["Engine cylinder head temperature", b'RECIP ENG CYLINDER HEAD TEMPERATURE:index', b'Celsius', 'Y'], -"RECIP_ENG_RADIATOR_TEMPERATURE:index": ["Engine radiator temperature", b'RECIP ENG RADIATOR TEMPERATURE:index', b'Celsius', 'Y'], -"RECIP_ENG_FUEL_AVAILABLE:index": ["True if fuel is available", b'RECIP ENG FUEL AVAILABLE:index', b'Bool', 'Y'], -"RECIP_ENG_FUEL_FLOW:index": ["Engine fuel flow", b'RECIP ENG FUEL FLOW:index', b'Pounds per hour', 'Y'], -"RECIP_ENG_FUEL_TANK_SELECTOR:index": ["Fuel tank selected for engine. See fuel tank list.", b'RECIP ENG FUEL TANK SELECTOR:index', b'Enum', 'N'], -"ENGINE_TYPE": ["Engine type:; 0 = Piston; 1 = Jet; 2 = None; 3 = Helo(Bell) turbine; 4 = Unsupported; 5 = Turboprop", b'ENGINE TYPE', b'Enum', 'N'], -"RECIP_ENG_FUEL_NUMBER_TANKS_USED:index": ["Number of tanks currently being used", b'RECIP ENG FUEL NUMBER TANKS USED:index', b'Number', 'N'], -"RECIP_CARBURETOR_TEMPERATURE:index": ["Carburetor temperature", b'RECIP CARBURETOR TEMPERATURE:index', b'Celsius', 'Y'], -"RECIP_MIXTURE_RATIO:index": ["Fuel / Air mixture ratio", b'RECIP MIXTURE RATIO:index', b'Ratio', 'Y'], -"TURB_ENG_N1:index": ["Turbine engine N1", b'TURB ENG N1:index', b'Percent', 'Y'], -"TURB_ENG_N2:index": ["Turbine engine N2", b'TURB ENG N2:index', b'Percent', 'Y'], -"TURB_ENG_CORRECTED_N1:index": ["Turbine engine corrected N1", b'TURB ENG CORRECTED N1:index', b'Percent', 'Y'], -"TURB_ENG_CORRECTED_N2:index": ["Turbine engine corrected N2", b'TURB ENG CORRECTED N2:index', b'Percent', 'Y'], -"TURB_ENG_CORRECTED_FF:index": ["Corrected fuel flow", b'TURB ENG CORRECTED FF:index', b'Pounds per hour', 'Y'], -"TURB_ENG_MAX_TORQUE_PERCENT:index": ["Percent of max rated torque", b'TURB ENG MAX TORQUE PERCENT:index', b'Percent', 'Y'], -"TURB_ENG_PRESSURE_RATIO:index": ["Engine pressure ratio", b'TURB ENG PRESSURE RATIO:index', b'Ratio', 'Y'], -"TURB_ENG_ITT:index": ["Engine ITT", b'TURB ENG ITT:index', b'Rankine', 'Y'], -"TURB_ENG_AFTERBURNER:index": ["Afterburner state", b'TURB ENG AFTERBURNER:index', b'Bool', 'N'], -"TURB_ENG_JET_THRUST:index": ["Engine jet thrust", b'TURB ENG JET THRUST:index', b'Pounds', 'N'], -"TURB_ENG_BLEED_AIR:index": ["Bleed air pressure", b'TURB ENG BLEED AIR:index', b'Psi', 'N'], -"TURB_ENG_TANK_SELECTOR:index": ["Fuel tank selected for engine. See fuel tank list.", b'TURB ENG TANK SELECTOR:index', b'Enum', 'N'], -"ENGINE_TYPE": ["Engine type:; 0 = Piston; 1 = Jet; 2 = None; 3 = Helo(Bell) turbine; 4 = Unsupported; 5 = Turboprop", b'ENGINE TYPE', b'Enum', 'N'], -"TURB_ENG_NUM_TANKS_USED:index": ["Number of tanks currently being used", b'TURB ENG NUM TANKS USED:index', b'Number', 'N'], -"TURB_ENG_FUEL_FLOW_PPH:index": ["Engine fuel flow", b'TURB ENG FUEL FLOW PPH:index', b'Pounds per hour', 'N'], -"TURB_ENG_FUEL_AVAILABLE:index": ["True if fuel is available", b'TURB ENG FUEL AVAILABLE:index', b'Bool', 'N'], -"TURB_ENG_REVERSE_NOZZLE_PERCENT:index": ["Percent thrust reverser nozzles deployed", b'TURB ENG REVERSE NOZZLE PERCENT:index', b'Percent', 'N'], -"TURB_ENG_VIBRATION:index": ["Engine vibration value", b'TURB ENG VIBRATION:index', b'Number', 'N'], -"ENG_FAILED:index": ["Failure flag", b'ENG FAILED:index', b'Number', 'N'], -"ENG_RPM_ANIMATION_PERCENT:index": ["Percent max rated rpm used for visual animation", b'ENG RPM ANIMATION PERCENT:index', b'Percent', 'N'], -"ENG_ON_FIRE:index": ["On fire state", b'ENG ON FIRE:index', b'Bool', 'Y'], -"ENG_FUEL_FLOW_BUG_POSITION:index": ["Fuel flow reference", b'ENG FUEL FLOW BUG POSITION:index', b'Pounds per hour', 'N'], -"PROP_MAX_RPM_PERCENT:index": ["Percent of max rated rpm", b'PROP MAX RPM PERCENT:index', b'Percent', 'N'], -"PROP_THRUST:index": ["Propeller thrust", b'PROP THRUST:index', b'Pounds', 'N'], -"PROP_BETA:index": ["Prop blade pitch angle", b'PROP BETA:index', b'Radians', 'N'], -"PROP_FEATHERING_INHIBIT:index": ["Feathering inhibit flag", b'PROP FEATHERING INHIBIT:index', b'Bool', 'N'], -"PROP_SYNC_DELTA_LEVER:index": ["Corrected prop correction input on slaved engine", b'PROP SYNC DELTA LEVER:index', b'Position', 'N'], -"PROP_AUTO_FEATHER_ARMED:index": ["Auto-feather armed state", b'PROP AUTO FEATHER ARMED:index', b'Bool', 'N'], -"PANEL_AUTO_FEATHER_SWITCH:index": ["Auto-feather arming switch", b'PANEL AUTO FEATHER SWITCH:index', b'Bool', 'N'], -"PROP_SYNC_ACTIVE:index": ["True if prop sync is active", b'PROP SYNC ACTIVE:index', b'Bool', 'N'], -"ENG_COMBUSTION": ["True if the engine is running", b'ENG COMBUSTION', b'Bool', 'N'], -"ENG_N1_RPM:index": ["Engine N1 rpm", b'ENG N1 RPM:index', b'Rpm (0 to 16384 = 0 to 100%)', 'N'], -"ENG_N2_RPM:index": ["Engine N2 rpm", b'ENG N2 RPM:index', b'Rpm(0 to 16384 = 0 to 100%)', 'N'], -"ENG_FUEL_FLOW_GPH:index": ["Engine fuel flow", b'ENG FUEL FLOW GPH:index', b'Gallons per hour', 'N'], -"ENG_FUEL_FLOW_PPH:index": ["Engine fuel flow", b'ENG FUEL FLOW PPH:index', b'Pounds per hour', 'N'], -"ENG_TORQUE:index": ["Torque", b'ENG TORQUE:index', b'Foot pounds', 'N'], -"ENG_ANTI_ICE:index": ["Anti-ice switch", b'ENG ANTI ICE:index', b'Bool', 'N'], -"ENG_PRESSURE_RATIO:index": ["Engine pressure ratio", b'ENG PRESSURE RATIO:index', b'Ratio (0-16384)', 'N'], -"ENG_EXHAUST_GAS_TEMPERATURE:index": ["Exhaust gas temperature", b'ENG EXHAUST GAS TEMPERATURE:index', b'Rankine', 'N'], -"ENG_EXHAUST_GAS_TEMPERATURE_GES:index": ["Governed engine setting", b'ENG EXHAUST GAS TEMPERATURE GES:index', b'Percent over 100', 'N'], -"ENG_CYLINDER_HEAD_TEMPERATURE:index": ["Engine cylinder head temperature", b'ENG CYLINDER HEAD TEMPERATURE:index', b'Rankine', 'N'], -"ENG_OIL_TEMPERATURE:index": ["Engine oil temperature", b'ENG OIL TEMPERATURE:index', b'Rankine', 'N'], -"ENG_OIL_PRESSURE:index": ["Engine oil pressure", b'ENG OIL PRESSURE:index', b'Pounds per square foot', 'N'], -"ENG_OIL_QUANTITY:index": ["Engine oil quantitiy as a percentage of full capacity", b'ENG OIL QUANTITY:index', b'Percent over 100', 'N'], -"ENG_HYDRAULIC_PRESSURE:index": ["Engine hydraulic pressure", b'ENG HYDRAULIC PRESSURE:index', b'Pounds per square foot', 'N'], -"ENG_HYDRAULIC_QUANTITY:index": ["Engine hydraulic fluid quantity, as a percentage of total capacity", b'ENG HYDRAULIC QUANTITY:index', b'Percent over 100', 'N'], -"ENG_MANIFOLD_PRESSURE:index": ["Engine manifold pressure.", b'ENG MANIFOLD PRESSURE:index', b'inHG.', 'N'], -"ENG_VIBRATION:index": ["Engine vibration", b'ENG VIBRATION:index', b'Number', 'N'], -"ENG_RPM_SCALER:index": ["Obsolete", b'ENG RPM SCALER:index', b'Scalar', 'N'], -"ENG_MAX_RPM": ["Maximum rpm", b'ENG MAX RPM', b'Rpm', 'N'], -"GENERAL_ENG_STARTER_ACTIVE": ["True if engine starter is active", b'GENERAL ENG STARTER ACTIVE', b'Bool', 'N'], -"GENERAL_ENG_FUEL_USED_SINCE_START": ["Fuel used since the engines were last started", b'GENERAL ENG FUEL USED SINCE START', b'Pounds', 'N'], -"TURB_ENG_PRIMARY_NOZZLE_PERCENT:index": ["Percent thrust of primary nozzle", b'TURB ENG PRIMARY NOZZLE PERCENT:index', b'Percent over 100', 'N'], -"TURB_ENG_IGNITION_SWITCH": ["True if the turbine engine ignition switch is on", b'TURB ENG IGNITION SWITCH', b'Bool', 'N'], -"TURB_ENG_MASTER_STARTER_SWITCH": ["True if the turbine engine master starter switch is on", b'TURB ENG MASTER STARTER SWITCH', b'Bool', 'N'], -"TURB_ENG_AFTERBURNER_STAGE_ACTIVE": ["The stage of the afterburner, or 0 if the afterburner is not active.", b'TURB ENG AFTERBURNER STAGE ACTIVE', b'Number', 'N'], -"TURB_ENG_AFTERBURNER_PCT_ACTIVE": ["The percentage that the afterburner is running at.", b'TURB ENG AFTERBURNER PCT ACTIVE', b'Percent_over_100', 'N'], - -// Fuel -"FUEL_TANK_CENTER_LEVEL": ["Percent of maximum capacity", b'FUEL TANK CENTER LEVEL', b'Percent Over 100', 'Y'], -"FUEL_TANK_CENTER2_LEVEL": ["Percent of maximum capacity", b'FUEL TANK CENTER2 LEVEL', b'Percent Over 100', 'Y'], -"FUEL_TANK_CENTER3_LEVEL": ["Percent of maximum capacity", b'FUEL TANK CENTER3 LEVEL', b'Percent Over 100', 'Y'], -"FUEL_TANK_LEFT_MAIN_LEVEL": ["Percent of maximum capacity", b'FUEL TANK LEFT MAIN LEVEL', b'Percent Over 100', 'Y'], -"FUEL_TANK_LEFT_AUX_LEVEL": ["Percent of maximum capacity", b'FUEL TANK LEFT AUX LEVEL', b'Percent Over 100', 'Y'], -"FUEL_TANK_LEFT_TIP_LEVEL": ["Percent of maximum capacity", b'FUEL TANK LEFT TIP LEVEL', b'Percent Over 100', 'Y'], -"FUEL_TANK_RIGHT_MAIN_LEVEL": ["Percent of maximum capacity", b'FUEL TANK RIGHT MAIN LEVEL', b'Percent Over 100', 'Y'], -"FUEL_TANK_RIGHT_AUX_LEVEL": ["Percent of maximum capacity", b'FUEL TANK RIGHT AUX LEVEL', b'Percent Over 100', 'Y'], -"FUEL_TANK_RIGHT_TIP_LEVEL": ["Percent of maximum capacity", b'FUEL TANK RIGHT TIP LEVEL', b'Percent Over 100', 'Y'], -"FUEL_TANK_EXTERNAL1_LEVEL": ["Percent of maximum capacity", b'FUEL TANK EXTERNAL1 LEVEL', b'Percent Over 100', 'Y'], -"FUEL_TANK_EXTERNAL2_LEVEL": ["Percent of maximum capacity", b'FUEL TANK EXTERNAL2 LEVEL', b'Percent Over 100', 'Y'], -"FUEL_TANK_CENTER_CAPACITY": ["Maximum capacity in volume", b'FUEL TANK CENTER CAPACITY', b'Gallons', 'N'], -"FUEL_TANK_CENTER2_CAPACITY": ["Maximum capacity in volume", b'FUEL TANK CENTER2 CAPACITY', b'Gallons', 'N'], -"FUEL_TANK_CENTER3_CAPACITY": ["Maximum capacity in volume", b'FUEL TANK CENTER3 CAPACITY', b'Gallons', 'N'], -"FUEL_TANK_LEFT_MAIN_CAPACITY": ["Maximum capacity in volume", b'FUEL TANK LEFT MAIN CAPACITY', b'Gallons', 'N'], -"FUEL_TANK_LEFT_AUX_CAPACITY": ["Maximum capacity in volume", b'FUEL TANK LEFT AUX CAPACITY', b'Gallons', 'N'], -"FUEL_TANK_LEFT_TIP_CAPACITY": ["Maximum capacity in volume", b'FUEL TANK LEFT TIP CAPACITY', b'Gallons', 'N'], -"FUEL_TANK_RIGHT_MAIN_CAPACITY": ["Maximum capacity in volume", b'FUEL TANK RIGHT MAIN CAPACITY', b'Gallons', 'N'], -"FUEL_TANK_RIGHT_AUX_CAPACITY": ["Maximum capacity in volume", b'FUEL TANK RIGHT AUX CAPACITY', b'Gallons', 'N'], -"FUEL_TANK_RIGHT_TIP_CAPACITY": ["Maximum capacity in volume", b'FUEL TANK RIGHT TIP CAPACITY', b'Gallons', 'N'], -"FUEL_TANK_EXTERNAL1_CAPACITY": ["Maximum capacity in volume", b'FUEL TANK EXTERNAL1 CAPACITY', b'Gallons', 'N'], -"FUEL_TANK_EXTERNAL2_CAPACITY": ["Maximum capacity in volume", b'FUEL TANK EXTERNAL2 CAPACITY', b'Gallons', 'N'], -"FUEL_LEFT_CAPACITY": ["Maximum capacity in volume", b'FUEL LEFT CAPACITY', b'Gallons', 'N'], -"FUEL_RIGHT_CAPACITY": ["Maximum capacity in volume", b'FUEL RIGHT CAPACITY', b'Gallons', 'N'], -"FUEL_TANK_CENTER_QUANTITY": ["Current quantity in volume", b'FUEL TANK CENTER QUANTITY', b'Gallons', 'Y'], -"FUEL_TANK_CENTER2_QUANTITY": ["Current quantity in volume", b'FUEL TANK CENTER2 QUANTITY', b'Gallons', 'Y'], -"FUEL_TANK_CENTER3_QUANTITY": ["Current quantity in volume", b'FUEL TANK CENTER3 QUANTITY', b'Gallons', 'Y'], -"FUEL_TANK_LEFT_MAIN_QUANTITY": ["Current quantity in volume", b'FUEL TANK LEFT MAIN QUANTITY', b'Gallons', 'Y'], -"FUEL_TANK_LEFT_AUX_QUANTITY": ["Current quantity in volume", b'FUEL TANK LEFT AUX QUANTITY', b'Gallons', 'Y'], -"FUEL_TANK_LEFT_TIP_QUANTITY": ["Current quantity in volume", b'FUEL TANK LEFT TIP QUANTITY', b'Gallons', 'Y'], -"FUEL_TANK_RIGHT_MAIN_QUANTITY": ["Current quantity in volume", b'FUEL TANK RIGHT MAIN QUANTITY', b'Gallons', 'Y'], -"FUEL_TANK_RIGHT_AUX_QUANTITY": ["Current quantity in volume", b'FUEL TANK RIGHT AUX QUANTITY', b'Gallons', 'Y'], -"FUEL_TANK_RIGHT_TIP_QUANTITY": ["Current quantity in volume", b'FUEL TANK RIGHT TIP QUANTITY', b'Gallons', 'Y'], -"FUEL_TANK_EXTERNAL1_QUANTITY": ["Current quantity in volume", b'FUEL TANK EXTERNAL1 QUANTITY', b'Gallons', 'Y'], -"FUEL_TANK_EXTERNAL2_QUANTITY": ["Current quantity in volume", b'FUEL TANK EXTERNAL2 QUANTITY', b'Gallons', 'Y'], -"FUEL_LEFT_QUANTITY": ["Current quantity in volume", b'FUEL LEFT QUANTITY', b'Gallons', 'N'], -"FUEL_RIGHT_QUANTITY": ["Current quantity in volume", b'FUEL RIGHT QUANTITY', b'Gallons', 'N'], -"FUEL_TOTAL_QUANTITY": ["Current quantity in volume", b'FUEL TOTAL QUANTITY', b'Gallons', 'N'], -"FUEL_WEIGHT_PER_GALLON": ["Fuel weight per gallon", b'FUEL WEIGHT PER GALLON', b'Pounds', 'N'], -"FUEL_TANK_SELECTOR:index": ["Which tank is selected. See fuel tank list.", b'FUEL TANK SELECTOR:index', b'Enum', 'N'], -"FUEL_CROSS_FEED": ["Cross feed valve:; 0 = Closed; 1 = Open", b'FUEL CROSS FEED', b'Enum', 'N'], -"FUEL_TOTAL_CAPACITY": ["Total capacity of the aircraft", b'FUEL TOTAL CAPACITY', b'Gallons', 'N'], -"FUEL_SELECTED_QUANTITY_PERCENT": ["Percent or capacity for selected tank", b'FUEL SELECTED QUANTITY PERCENT', b'Percent Over 100', 'N'], -"FUEL_SELECTED_QUANTITY": ["Quantity of selected tank", b'FUEL SELECTED QUANTITY', b'Gallons', 'N'], -"FUEL_TOTAL_QUANTITY_WEIGHT": ["Current total fuel weight of the aircraft", b'FUEL TOTAL QUANTITY WEIGHT', b'Pounds', 'N'], -"NUM_FUEL_SELECTORS": ["Number of selectors on the aircraft", b'NUM FUEL SELECTORS', b'Number', 'N'], -"UNLIMITED_FUEL": ["Unlimited fuel flag", b'UNLIMITED FUEL', b'Bool', 'N'], -"ESTIMATED_FUEL_FLOW": ["Estimated fuel flow at cruise", b'ESTIMATED FUEL FLOW', b'Pounds per hour', 'N'], - -// Lights -"LIGHT_PANEL_ON": ["Return true if the light OR switch is on", b'LIGHT PANEL ON', b'Bool', 'N'], -"LIGHT_LANDING_ON": ["Return true if the light OR switch is on", b'LIGHT LANDING ON', b'Bool', 'N'], -"LIGHT_TAXI_ON": ["Return true if the light OR switch is on", b'LIGHT TAXI ON', b'Bool', 'N'], -"LIGHT_BEACON_ON": ["Return true if the light OR switch is on", b'LIGHT BEACON ON', b'Bool', 'N'], -"LIGHT_NAV_ON": ["Return true if the light OR switch is on", b'LIGHT NAV ON', b'Bool', 'N'], -"LIGHT_LOGO_ON": ["Return true if the light OR switch is on", b'LIGHT LOGO ON', b'Bool', 'N'], -"LIGHT_WING_ON": ["Return true if the light OR switch is on", b'LIGHT WING ON', b'Bool', 'N'], -"LIGHT_RECOGNITION_ON": ["Return true if the light OR switch is on", b'LIGHT RECOGNITION ON', b'Bool', 'N'], -"LIGHT_CABIN_ON": ["Return true if the light OR switch is on", b'LIGHT CABIN ON', b'Bool', 'N'], -"LIGHT_ON_STATES": ["Bit mask:; 0x0001: Nav; 0x0002: Beacon; 0x0004: Landing; 0x0008: Taxi; 0x0010: Strobe; 0x0020: Panel; 0x0040: Recognition; 0x0080: Wing; 0x0100: Logo; 0x0200: Cabin", b'LIGHT ON STATES', b'Mask', 'N'], -"LIGHT_STATES": ["Same as LIGHT ON STATES", b'LIGHT STATES', b'Mask', 'N'], -"LANDING_LIGHT_PBH": ["Landing light pitch bank and heading", b'LANDING LIGHT PBH', b'SIMCONNECT_DATA_XYZ structure', 'N'], - -// Position and Speed - System -"TOTAL_WORLD_VELOCITY": ["Speed relative to the earths center", b'TOTAL WORLD VELOCITY', b'Feet per second', 'N'], -"VELOCITY_BODY_Z": ["True longitudinal speed, relative to aircraft axis", b'VELOCITY BODY Z', b'Feet per second', 'Y'], -"VELOCITY_BODY_X": ["True lateral speed, relative to aircraft axis", b'VELOCITY BODY X', b'Feet per second', 'Y'], -"VELOCITY_BODY_Y": ["True vertical speed, relative to aircraft axis", b'VELOCITY BODY Y', b'Feet per second', 'Y'], -"VELOCITY_WORLD_Z": ["Speed relative to earth, in North/South direction", b'VELOCITY WORLD Z', b'Feet per second', 'Y'], -"VELOCITY_WORLD_X": ["Speed relative to earth, in East/West direction", b'VELOCITY WORLD X', b'Feet per second', 'Y'], -"VELOCITY_WORLD_Y": ["Speed relative to earth, in vertical direction", b'VELOCITY WORLD Y', b'Feet per second', 'Y'], -"ACCELERATION_WORLD_X": ["Acceleration relative to earth, in east/west direction", b'ACCELERATION WORLD X', b'Feet per second squared', 'Y'], -"ACCELERATION_WORLD_Y": ["Acceleration relative to earch, in vertical direction", b'ACCELERATION WORLD Y', b'Feet per second squared', 'Y'], -"ACCELERATION_WORLD_Z": ["Acceleration relative to earth, in north/south direction", b'ACCELERATION WORLD Z', b'Feet per second squared', 'Y'], -"ACCELERATION_BODY_X": ["Acceleration relative to aircraft axix, in east/west direction", b'ACCELERATION BODY X', b'Feet per second squared', 'Y'], -"ACCELERATION_BODY_Y": ["Acceleration relative to aircraft axis, in vertical direction", b'ACCELERATION BODY Y', b'Feet per second squared', 'Y'], -"ACCELERATION_BODY_Z": ["Acceleration relative to aircraft axis, in north/south direction", b'ACCELERATION BODY Z', b'Feet per second squared', 'Y'], -"ROTATION_VELOCITY_BODY_X": ["Rotation relative to aircraft axis", b'ROTATION VELOCITY BODY X', b'Feet per second', 'Y'], -"ROTATION_VELOCITY_BODY_Y": ["Rotation relative to aircraft axis", b'ROTATION VELOCITY BODY Y', b'Feet per second', 'Y'], -"ROTATION_VELOCITY_BODY_Z": ["Rotation relative to aircraft axis", b'ROTATION VELOCITY BODY Z', b'Feet per second', 'Y'], -"RELATIVE_WIND_VELOCITY_BODY_X": ["Lateral speed relative to wind", b'RELATIVE WIND VELOCITY BODY X', b'Feet per second', 'N'], -"RELATIVE_WIND_VELOCITY_BODY_Y": ["Vertical speed relative to wind", b'RELATIVE WIND VELOCITY BODY Y', b'Feet per second', 'N'], -"RELATIVE_WIND_VELOCITY_BODY_Z": ["Longitudinal speed relative to wind", b'RELATIVE WIND VELOCITY BODY Z', b'Feet per second', 'N'], -"PLANE_LATITUDE": ["Latitude of aircraft, North is positive, South negative", b'PLANE LATITUDE', b'Degrees', 'Y'], -"PLANE_LONGITUDE": ["Longitude of aircraft, East is positive, West negative", b'PLANE LONGITUDE', b'Degrees', 'Y'], -"MAGVAR": ["Magnetic variation", b'MAGVAR', b'Degrees', 'N'], -"SIM_ON_GROUND": ["On ground flag", b'SIM ON GROUND', b'Bool', 'N'], -"INCIDENCE_ALPHA": ["Angle of attack", b'INCIDENCE ALPHA', b'Radians', 'N'], -"INCIDENCE_BETA": ["Sideslip angle", b'INCIDENCE BETA', b'Radians', 'N'], -"WING_FLEX_PCT:index": ["The current wing flex. Different values can be set for each wing (for example, during banking). Set an index of 1 for the left wing, and 2 for the right wing.", b'WING FLEX PCT:index', b'Percent over 100', 'Y'], -"STRUCT_LATLONALT": ["Returns the latitude, longitude and altitude of the user aircraft.", b'STRUCT LATLONALT', b'SIMCONNECT_DATA_LATLONALTstructure', 'N'], -"STRUCT_LATLONALTPBH": ["Returns the pitch, bank and heading of the user aircraft.", b'STRUCT LATLONALTPBH', b'SIMCONNECT_DATA_LATLONALTstructure', 'N'], -"STRUCT_SURFACE_RELATIVE_VELOCITY": ["The relative surface velocity.", b'STRUCT SURFACE RELATIVE VELOCITY', b'SIMCONNECT_DATA_XYZ structure, feet per second', 'N'], -"STRUCT_WORLDVELOCITY": ["The world velocity.", b'STRUCT WORLDVELOCITY', b'SIMCONNECT_DATA_XYZ structure, feet per second', 'N'], -"STRUCT_WORLD_ROTATION_VELOCITY": ["The world rotation velocity.", b'STRUCT WORLD ROTATION VELOCITY', b'SIMCONNECT_DATA_XYZ structure, radians per second', 'N'], -"STRUCT_BODY_VELOCITY": ["The object body velocity.", b'STRUCT BODY VELOCITY', b'SIMCONNECT_DATA_XYZ structure, feet per second', 'N'], -"STRUCT_BODY_ROTATION_VELOCITY": ["The body rotation velocity. Individual body rotation values are in the Aircraft Position and Speed section.", b'STRUCT BODY ROTATION VELOCITY', b'SIMCONNECT_DATA_XYZ structure, radians per second', 'N'], -"STRUCT_WORLD_ACCELERATION": ["The world acceleration for each axis. Individual world acceleration values are in the Aircraft Position and Speed section.", b'STRUCT WORLD ACCELERATION', b'SIMCONNECT_DATA_XYZ structure, feet per second squared', 'N'], -"STRUCT_ENGINE_POSITION:index": ["The engine position relative to the reference datum position for the aircraft.", b'STRUCT ENGINE POSITION:index', b'SIMCONNECT_DATA_XYZ structure, feet.', 'N'], -"STRUCT_EYEPOINT_DYNAMIC_ANGLE": ["The angle of the eyepoint view. Zero, zero, zero is straight ahead.", b'STRUCT EYEPOINT DYNAMIC ANGLE', b'SIMCONNECT_DATA_XYZ structure, radians', 'N'], -"STRUCT_EYEPOINT_DYNAMIC_OFFSET": ["A variable offset away from the EYEPOINT POSITION", b'STRUCT EYEPOINT DYNAMIC OFFSET', b'SIMCONNECT_DATA_XYZ structure, feet', 'N'], -"EYEPOINT_POSITION": ["The eyepoint position relative to the reference datum position for the aircraft.", b'EYEPOINT POSITION', b'SIMCONNECT_DATA_XYZ structure, feet', 'N'], - -// Instrumentation -"AIRSPEED_TRUE_CALIBRATE": ["Angle of True calibration scale on airspeed indicator", b'AIRSPEED TRUE CALIBRATE', b'Degrees', 'Y'], -"AIRSPEED_BARBER_POLE": ["Redline airspeed (dynamic on some aircraft)", b'AIRSPEED BARBER POLE', b'Knots', 'N'], -"MACH_MAX_OPERATE": ["Maximum design mach", b'MACH MAX OPERATE', b'Mach', 'N'], -"BARBER_POLE_MACH": ["Mach associated with maximum airspeed", b'BARBER POLE MACH', b'Mach', 'N'], -"INDICATED_ALTITUDE": ["Altimeter indication", b'INDICATED ALTITUDE', b'Feet', 'Y'], -"KOHLSMAN_SETTING_MB": ["Altimeter setting", b'KOHLSMAN SETTING MB', b'Millibars', 'Y'], -"KOHLSMAN_SETTING_HG": ["Altimeter setting", b'KOHLSMAN SETTING HG', b'inHg', 'N'], -"ATTITUDE_INDICATOR_PITCH_DEGREES": ["AI pitch indication", b'ATTITUDE INDICATOR PITCH DEGREES', b'Radians', 'N'], -"ATTITUDE_INDICATOR_BANK_DEGREES": ["AI bank indication", b'ATTITUDE INDICATOR BANK DEGREES', b'Radians', 'N'], -"ATTITUDE_BARS_POSITION": ["AI reference pitch reference bars", b'ATTITUDE BARS POSITION', b'Percent Over 100', 'N'], -"ATTITUDE_CAGE": ["AI caged state", b'ATTITUDE CAGE', b'Bool', 'N'], -"WISKEY_COMPASS_INDICATION_DEGREES": ["Magnetic compass indication", b'WISKEY COMPASS INDICATION DEGREES', b'Degrees', 'Y'], -"PLANE_HEADING_DEGREES_GYRO": ["Heading indicator (directional gyro) indication", b'PLANE HEADING DEGREES GYRO', b'Radians', 'Y'], -"HEADING_INDICATOR": ["Heading indicator (directional gyro) indication", b'HEADING INDICATOR', b'Radians', 'N'], -"GYRO_DRIFT_ERROR": ["Angular error of heading indicator", b'GYRO DRIFT ERROR', b'Radians', 'N'], -"DELTA_HEADING_RATE": ["Rate of turn of heading indicator", b'DELTA HEADING RATE', b'Radians per second', 'Y'], -"TURN_COORDINATOR_BALL": ["Turn coordinator ball position", b'TURN COORDINATOR BALL', b'Position 128 (-127 to 127)', 'N'], -"ANGLE_OF_ATTACK_INDICATOR": ["AoA indication", b'ANGLE OF ATTACK INDICATOR', b'Radians', 'N'], -"RADIO_HEIGHT": ["Radar altitude", b'RADIO HEIGHT', b'Feet', 'N'], -"PARTIAL_PANEL_ADF": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL ADF', b'Enum', 'Y'], -"PARTIAL_PANEL_AIRSPEED": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL AIRSPEED', b'Enum', 'Y'], -"PARTIAL_PANEL_ALTIMETER": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL ALTIMETER', b'Enum', 'Y'], -"PARTIAL_PANEL_ATTITUDE": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL ATTITUDE', b'Enum', 'Y'], -"PARTIAL_PANEL_COMM": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL COMM', b'Enum', 'Y'], -"PARTIAL_PANEL_COMPASS": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL COMPASS', b'Enum', 'Y'], -"PARTIAL_PANEL_ELECTRICAL": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL ELECTRICAL', b'Enum', 'Y'], -"PARTIAL_PANEL_AVIONICS": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL AVIONICS', b'Enum', 'N'], -"PARTIAL_PANEL_ENGINE": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL ENGINE', b'Enum', 'Y'], -"PARTIAL_PANEL_FUEL_INDICATOR": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL FUEL INDICATOR', b'Enum', 'N'], -"PARTIAL_PANEL_HEADING": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL HEADING', b'Enum', 'Y'], -"PARTIAL_PANEL_VERTICAL_VELOCITY": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL VERTICAL VELOCITY', b'Enum', 'Y'], -"PARTIAL_PANEL_TRANSPONDER": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL TRANSPONDER', b'Enum', 'Y'], -"PARTIAL_PANEL_NAV": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL NAV', b'Enum', 'Y'], -"PARTIAL_PANEL_PITOT": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL PITOT', b'Enum', 'Y'], -"PARTIAL_PANEL_TURN_COORDINATOR": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL TURN COORDINATOR', b'Enum', 'N'], -"PARTIAL_PANEL_VACUUM": ["Gauge fail flag (0 = ok, 1 = fail, 2 = blank)", b'PARTIAL PANEL VACUUM', b'Enum', 'Y'], -"MAX_G_FORCE": ["Maximum G force attained", b'MAX G FORCE', b'Gforce', 'N'], -"MIN_G_FORCE": ["Minimum G force attained", b'MIN G FORCE', b'Gforce', 'N'], -"SUCTION_PRESSURE": ["Vacuum system suction pressure", b'SUCTION PRESSURE', b'Inches of Mercury, inHg', 'Y'], - -// Avionics -"NAV_SOUND:index": ["Nav audio flag. Index of 1 or 2.", b'NAV SOUND:index', b'Bool', 'N'], -"DME_SOUND": ["DME audio flag", b'DME SOUND', b'Bool', 'N'], -"ADF_SOUND:index": ["ADF audio flag. Index of 0 or 1.", b'ADF SOUND:index', b'Bool', 'N'], -"MARKER_SOUND": ["Marker audio flag", b'MARKER SOUND', b'Bool', 'N'], -"COM_TRANSMIT:index": ["Audio panel com transmit state. Index of 1 or 2.", b'COM TRANSMIT:index', b'Bool', 'N'], -"COM_RECIEVE_ALL": ["Flag if all Coms receiving", b'COM RECIEVE ALL', b'Bool', 'N'], -"NAV_AVAILABLE:index": ["Flag if Nav equipped on aircraft", b'NAV AVAILABLE:index', b'Bool', 'N'], -"NAV_SIGNAL:index": ["Nav signal strength", b'NAV SIGNAL:index', b'Number', 'N'], -"NAV_HAS_NAV:index": ["Flag if Nav has signal", b'NAV HAS NAV:index', b'Bool', 'N'], -"NAV_HAS_LOCALIZER:index": ["Flag if tuned station is a localizer", b'NAV HAS LOCALIZER:index', b'Bool', 'N'], -"NAV_HAS_DME:index": ["Flag if tuned station has a DME", b'NAV HAS DME:index', b'Bool', 'N'], -"NAV_HAS_GLIDE_SLOPE:index": ["Flag if tuned station has a glideslope", b'NAV HAS GLIDE SLOPE:index', b'Bool', 'N'], -"NAV_BACK_COURSE_FLAGS:index": ["Returns the following bit flags:; BIT0: 1=back course available; BIT1: 1=localizer tuned in; BIT2: 1=on course; BIT7: 1=station active", b'NAV BACK COURSE FLAGS:index', b'Flags', 'N'], -"NAV_MAGVAR:index": ["Magnetic variation of tuned nav station", b'NAV MAGVAR:index', b'Degrees', 'N'], -"NAV_RADIAL:index": ["Radial that aircraft is on", b'NAV RADIAL:index', b'Degrees', 'N'], -"NAV_RADIAL_ERROR:index": ["Difference between current radial and OBS tuned radial", b'NAV RADIAL ERROR:index', b'Degrees', 'N'], -"NAV_LOCALIZER:index": ["Localizer course heading", b'NAV LOCALIZER:index', b'Degrees', 'N'], -"NAV_GLIDE_SLOPE_ERROR:index": ["Difference between current position and glideslope angle. Note that this provides 32 bit floating point precision, rather than the 8 bit integer precision of NAV GSI.", b'NAV GLIDE SLOPE ERROR:index', b'Degrees', 'N'], -"NAV_CDI:index": ["CDI needle deflection (+/- 127)", b'NAV CDI:index', b'Number', 'N'], -"NAV_GSI:index": ["Glideslope needle deflection (+/- 119). Note that this provides only 8 bit precision, whereas NAV GLIDE SLOPE ERROR provides 32 bit floating point precision.", b'NAV GSI:index', b'Number', 'N'], -"NAV_GS_FLAG:index": ["Glideslope flag", b'NAV GS FLAG:index', b'Bool', 'N'], -"NAV_OBS:index": ["OBS setting. Index of 1 or 2.", b'NAV OBS:index', b'Degrees', 'N'], -"NAV_DME:index": ["DME distance", b'NAV DME:index', b'Nautical miles', 'N'], -"NAV_DMESPEED:index": ["DME speed", b'NAV DMESPEED:index', b'Knots', 'N'], -"ADF_ACTIVE_FREQUENCY:index": ["ADF frequency. Index of 1 or 2.", b'ADF ACTIVE FREQUENCY:index', b'Frequency ADF BCD32', 'N'], -"ADF_STANDBY_FREQUENCY:index": ["ADF standby frequency", b'ADF STANDBY FREQUENCY:index', b'Hz', 'N'], -"ADF_RADIAL:index": ["Current direction from NDB station", b'ADF RADIAL:index', b'Degrees', 'N'], -"ADF_SIGNAL:index": ["Signal strength", b'ADF SIGNAL:index', b'Number', 'N'], -"TRANSPONDER_CODE:index": ["4-digit code", b'TRANSPONDER CODE:index', b'BCO16', 'N'], -"MARKER_BEACON_STATE": ["Marker beacon state:; 0 = None; 1 = Outer; 2 = Middle; 3 = Inner", b'MARKER BEACON STATE', b'Enum', 'Y'], -"INNER_MARKER": ["Inner marker state", b'INNER MARKER', b'Bool', 'Y'], -"MIDDLE_MARKER": ["Middle marker state", b'MIDDLE MARKER', b'Bool', 'Y'], -"OUTER_MARKER": ["Outer marker state", b'OUTER MARKER', b'Bool', 'Y'], -"NAV_RAW_GLIDE_SLOPE:index": ["Glide slope angle", b'NAV RAW GLIDE SLOPE:index', b'Degrees', 'N'], -"ADF_CARD": ["ADF compass rose setting", b'ADF CARD', b'Degrees', 'N'], -"HSI_CDI_NEEDLE": ["Needle deflection (+/- 127)", b'HSI CDI NEEDLE', b'Number', 'N'], -"HSI_GSI_NEEDLE": ["Needle deflection (+/- 119)", b'HSI GSI NEEDLE', b'Number', 'N'], -"HSI_CDI_NEEDLE_VALID": ["Signal valid", b'HSI CDI NEEDLE VALID', b'Bool', 'N'], -"HSI_GSI_NEEDLE_VALID": ["Signal valid", b'HSI GSI NEEDLE VALID', b'Bool', 'N'], -"HSI_TF_FLAGS": ["Nav TO/FROM flag:; 0 = Off; 1 = TO; 2 = FROM", b'HSI TF FLAGS', b'Enum', 'N'], -"HSI_BEARING_VALID": ["This will return true if the HSI BEARING variable contains valid data.", b'HSI BEARING VALID', b'Bool', 'N'], -"HSI_BEARING": ["If the GPS DRIVES NAV1 variable is true and the HSI BEARING VALID variable is true, this variable contains the HSI needle bearing. If the GPS DRIVES NAV1 variable is false and the HSI BEARING VALID variable is true, this variable contains the ADF1 frequency.", b'HSI BEARING', b'Degrees', 'N'], -"HSI_HAS_LOCALIZER": ["Station is a localizer", b'HSI HAS LOCALIZER', b'Bool', 'N'], -"HSI_SPEED": ["DME/GPS speed", b'HSI SPEED', b'Knots', 'N'], -"HSI_DISTANCE": ["DME/GPS distance", b'HSI DISTANCE', b'Nautical miles', 'N'], -"GPS_POSITION_LAT": ["Current GPS latitude", b'GPS POSITION LAT', b'Degrees', 'N'], -"GPS_POSITION_LON": ["Current GPS longitude", b'GPS POSITION LON', b'Degrees', 'N'], -"GPS_POSITION_ALT": ["Current GPS altitude", b'GPS POSITION ALT', b'Meters', 'N'], -"GPS_MAGVAR": ["Current GPS magnetic variation", b'GPS MAGVAR', b'Radians', 'N'], -"GPS_IS_ACTIVE_FLIGHT_PLAN": ["Flight plan mode active", b'GPS IS ACTIVE FLIGHT PLAN', b'Bool', 'N'], -"GPS_IS_ACTIVE_WAY_POINT": ["Waypoint mode active", b'GPS IS ACTIVE WAY POINT', b'Bool', 'N'], -"GPS_IS_ARRIVED": ["Is flight plan destination reached", b'GPS IS ARRIVED', b'Bool', 'N'], -"GPS_IS_DIRECTTO_FLIGHTPLAN": ["Is Direct To Waypoint mode active", b'GPS IS DIRECTTO FLIGHTPLAN', b'Bool', 'N'], -"GPS_GROUND_SPEED": ["Current ground speed", b'GPS GROUND SPEED', b'Meters per second', 'N'], -"GPS_GROUND_TRUE_HEADING": ["Current true heading", b'GPS GROUND TRUE HEADING', b'Radians', 'N'], -"GPS_GROUND_MAGNETIC_TRACK": ["Current magnetic ground track", b'GPS GROUND MAGNETIC TRACK', b'Radians', 'N'], -"GPS_GROUND_TRUE_TRACK": ["Current true ground track", b'GPS GROUND TRUE TRACK', b'Radians', 'N'], -"GPS_WP_DISTANCE": ["Distance to waypoint", b'GPS WP DISTANCE', b'Meters', 'N'], -"GPS_WP_BEARING": ["Magnetic bearing to waypoint", b'GPS WP BEARING', b'Radians', 'N'], -"GPS_WP_TRUE_BEARING": ["True bearing to waypoint", b'GPS WP TRUE BEARING', b'Radians', 'N'], -"GPS_WP_CROSS_TRK": ["Cross track distance", b'GPS WP CROSS TRK', b'Meters', 'N'], -"GPS_WP_DESIRED_TRACK": ["Desired track to waypoint", b'GPS WP DESIRED TRACK', b'Radians', 'N'], -"GPS_WP_TRUE_REQ_HDG": ["Required true heading to waypoint", b'GPS WP TRUE REQ HDG', b'Radians', 'N'], -"GPS_WP_VERTICAL_SPEED": ["Vertical speed to waypoint", b'GPS WP VERTICAL SPEED', b'Meters per second', 'N'], -"GPS_WP_TRACK_ANGLE_ERROR": ["Tracking angle error to waypoint", b'GPS WP TRACK ANGLE ERROR', b'Radians', 'N'], -"GPS_ETE": ["Estimated time enroute to destination", b'GPS ETE', b'Seconds', 'N'], -"GPS_ETA": ["Estimated time of arrival at destination", b'GPS ETA', b'Seconds', 'N'], -"GPS_WP_NEXT_LAT": ["Latitude of next waypoint", b'GPS WP NEXT LAT', b'Degrees', 'N'], -"GPS_WP_NEXT_LON": ["Longitude of next waypoint", b'GPS WP NEXT LON', b'Degrees', 'N'], -"GPS_WP_NEXT_ALT": ["Altitude of next waypoint", b'GPS WP NEXT ALT', b'Meters', 'N'], -"GPS_WP_PREV_VALID": ["Is previous waypoint valid (i.e. current waypoint is not the first waypoint)", b'GPS WP PREV VALID', b'Bool', 'N'], -"GPS_WP_PREV_LAT": ["Latitude of previous waypoint", b'GPS WP PREV LAT', b'Degrees', 'N'], -"GPS_WP_PREV_LON": ["Longitude of previous waypoint", b'GPS WP PREV LON', b'Degrees', 'N'], -"GPS_WP_PREV_ALT": ["Altitude of previous waypoint", b'GPS WP PREV ALT', b'Meters', 'N'], -"GPS_WP_ETE": ["Estimated time enroute to waypoint", b'GPS WP ETE', b'Seconds', 'N'], -"GPS_WP_ETA": ["Estimated time of arrival at waypoint", b'GPS WP ETA', b'Seconds', 'N'], -"GPS_COURSE_TO_STEER": ["Suggested heading to steer (for autopilot)", b'GPS COURSE TO STEER', b'Radians', 'N'], -"GPS_FLIGHT_PLAN_WP_INDEX": ["Index of waypoint", b'GPS FLIGHT PLAN WP INDEX', b'Number', 'N'], -"GPS_FLIGHT_PLAN_WP_COUNT": ["Number of waypoints", b'GPS FLIGHT PLAN WP COUNT', b'Number', 'N'], -"GPS_IS_ACTIVE_WP_LOCKED": ["Is switching to next waypoint locked", b'GPS IS ACTIVE WP LOCKED', b'Bool', 'N'], -"GPS_IS_APPROACH_LOADED": ["Is approach loaded", b'GPS IS APPROACH LOADED', b'Bool', 'N'], -"GPS_IS_APPROACH_ACTIVE": ["Is approach mode active", b'GPS IS APPROACH ACTIVE', b'Bool', 'N'], -"GPS_APPROACH_IS_WP_RUNWAY": ["Waypoint is the runway", b'GPS APPROACH IS WP RUNWAY', b'Bool', 'N'], -"GPS_APPROACH_APPROACH_INDEX": ["Index of approach for given airport", b'GPS APPROACH APPROACH INDEX', b'Number', 'N'], -"GPS_APPROACH_TRANSITION_INDEX": ["Index of approach transition", b'GPS APPROACH TRANSITION INDEX', b'Number', 'N'], -"GPS_APPROACH_IS_FINAL": ["Is approach transition final approach segment", b'GPS APPROACH IS FINAL', b'Bool', 'N'], -"GPS_APPROACH_IS_MISSED": ["Is approach segment missed approach segment", b'GPS APPROACH IS MISSED', b'Bool', 'N'], -"GPS_APPROACH_TIMEZONE_DEVIATION": ["Deviation of local time from GMT", b'GPS APPROACH TIMEZONE DEVIATION', b'Seconds', 'N'], -"GPS_APPROACH_WP_INDEX": ["Index of current waypoint", b'GPS APPROACH WP INDEX', b'Number', 'N'], -"GPS_APPROACH_WP_COUNT": ["Number of waypoints", b'GPS APPROACH WP COUNT', b'Number', 'N'], -"GPS_DRIVES_NAV1": ["GPS is driving Nav 1 indicator", b'GPS DRIVES NAV1', b'Bool', 'N'], -"COM_RECEIVE_ALL": ["Toggles all COM radios to receive on", b'COM RECEIVE ALL', b'Bool', 'N'], -"COM_AVAILABLE": ["True if either COM1 or COM2 is available", b'COM AVAILABLE', b'Bool', 'N'], -"COM_TEST:index": ["Enter an index of 1 or 2. True if the COM system is working.", b'COM TEST:index', b'Bool', 'N'], -"TRANSPONDER_AVAILABLE": ["True if a transponder is available", b'TRANSPONDER AVAILABLE', b'Bool', 'N'], -"ADF_AVAILABLE": ["True if ADF is available", b'ADF AVAILABLE', b'Bool', 'N'], -"ADF_FREQUENCY:index": ["Legacy, use ADF ACTIVE FREQUENCY", b'ADF FREQUENCY:index', b'Frequency BCD16', 'N'], -"ADF_EXT_FREQUENCY:index": ["Legacy, use ADF ACTIVE FREQUENCY", b'ADF EXT FREQUENCY:index', b'Frequency BCD16', 'N'], -"ADF_IDENT": ["ICAO code", b'ADF IDENT', b'String', 'N'], -"ADF_NAME": ["Descriptive name", b'ADF NAME', b'String', 'N'], -"NAV_IDENT": ["ICAO code", b'NAV IDENT', b'String', 'N'], -"NAV_NAME": ["Descriptive name", b'NAV NAME', b'String', 'N'], -"NAV_CODES:index": ["Returns bit flags with the following meaning:; BIT7: 0= VOR 1= Localizer; BIT6: 1= glideslope available; BIT5: 1= no localizer backcourse; BIT4: 1= DME transmitter at glide slope transmitter; BIT3: 1= no nav signal available; BIT2: 1= voice available; BIT1: 1 = TACAN available; BIT0: 1= DME available", b'NAV CODES:index', b'Flags', 'N'], -"NAV_GLIDE_SLOPE": ["The glide slope gradient.", b'NAV GLIDE SLOPE', b'Number', 'N'], -"NAV_RELATIVE_BEARING_TO_STATION:index": ["Relative bearing to station", b'NAV RELATIVE BEARING TO STATION:index', b'Degrees', 'N'], -"SELECTED_DME": ["Selected DME", b'SELECTED DME', b'Number', 'N'], -"GPS_WP_NEXT_ID": ["ID of next GPS waypoint", b'GPS WP NEXT ID', b'String', 'N'], -"GPS_WP_PREV_ID": ["ID of previous GPS waypoint", b'GPS WP PREV ID', b'String', 'N'], -"GPS_TARGET_DISTANCE": ["Distance to target", b'GPS TARGET DISTANCE', b'Meters', 'N'], -"GPS_TARGET_ALTITUDE": ["Altitude of GPS target", b'GPS TARGET ALTITUDE', b'Meters', 'N'], -"ADF_LATLONALT:index": ["Returns the latitude, longitude and altitude of the station the radio equipment is currently tuned to, or zeros if the radio is not tuned to any ADF station. Index of 1 or 2 for ADF 1 and ADF 2.", b'ADF LATLONALT:index', b'SIMCONNECT_DATA_LATLONALTstructure', 'N'], -"NAV_VOR_LATLONALT:index": ["Returns the VOR station latitude, longitude and altitude.", b'NAV VOR LATLONALT:index', b'SIMCONNECT_DATA_LATLONALTstructure', 'N'], -"NAV_GS_LATLONALT:index": ["Returns the glide slope.", b'NAV GS LATLONALT:index', b'SIMCONNECT_DATA_LATLONALTstructure', 'N'], -"NAV_DME_LATLONALT:index": ["Returns the DME station.", b'NAV DME LATLONALT:index', b'SIMCONNECT_DATA_LATLONALTstructure', 'N'], -"INNER_MARKER_LATLONALT": ["Returns the latitude, longitude and altitude of the inner marker of an approach to a runway, if the aircraft is within the required proximity, otherwise it will return zeros.", b'INNER MARKER LATLONALT', b'SIMCONNECT_DATA_LATLONALTstructure', 'N'], -"MIDDLE_MARKER_LATLONALT": ["Returns the latitude, longitude and altitude of the middle marker.", b'MIDDLE MARKER LATLONALT', b'SIMCONNECT_DATA_LATLONALTstructure', 'N'], -"OUTER_MARKER_LATLONALT": ["Returns the latitude, longitude and altitude of the outer marker.", b'OUTER MARKER LATLONALT', b'SIMCONNECT_DATA_LATLONALTstructure', 'N'], - -// Aircraft Controls -"YOKE_Y_POSITION": ["Percent control deflection fore/aft (for animation)", b'YOKE Y POSITION', b'Position (-16K to 0) -16K = Yoke fully pushed in', 'Y'], -"YOKE_X_POSITION": ["Percent control deflection left/right (for animation)", b'YOKE X POSITION', b'Position (-16K to 0) -16K =', 'Y'], -"RUDDER_PEDAL_POSITION": ["Percent rudder pedal deflection (for animation)", b'RUDDER PEDAL POSITION', b'Position (-16K to 0) -16K = left pedal pushed full in', 'Y'], -"RUDDER_POSITION": ["Percent rudder input deflection", b'RUDDER POSITION', b'Position (-16K to 0) -16K = full left', 'Y'], -"ELEVATOR_POSITION": ["Percent elevator input deflection", b'ELEVATOR POSITION', b'Position (-16K to 0) -16K = full down', 'Y'], -"AILERON_POSITION": ["Percent aileron input left/right", b'AILERON POSITION', b'Position (-16K to 0) -16K = full left', 'Y'], -"ELEVATOR_TRIM_INDICATOR": ["Percent elevator trim (for indication)", b'ELEVATOR TRIM INDICATOR', b'Position (-16K to 0) -16K = full down', 'N'], -"BRAKE_LEFT_POSITION": ["Percent left brake", b'BRAKE LEFT POSITION', b'Position (0 to 32K) 0 = off, 32K full', 'Y'], -"BRAKE_RIGHT_POSITION": ["Percent right brake", b'BRAKE RIGHT POSITION', b'Position (0 to 32K) 0 = off, 32K full', 'Y'], -"BRAKE_INDICATOR": ["Brake on indication", b'BRAKE INDICATOR', b'Position (0 to 16K) 0 = off, 16K full', 'N'], -"BRAKE_PARKING_POSITION": ["Parking brake on", b'BRAKE PARKING POSITION', b'Position (0 to 32K) 0 = off, 32K full', 'Y'], -"SPOILERS_ARMED": ["Auto-spoilers armed", b'SPOILERS ARMED', b'Bool', 'N'], -"SPOILERS_HANDLE_POSITION": ["Spoiler handle position", b'SPOILERS HANDLE POSITION', b'Percent Over 100 or Position (16K = down, 0 = up)', 'Y'], -"SPOILERS_LEFT_POSITION": ["Percent left spoiler deflected", b'SPOILERS LEFT POSITION', b'Percent Over 100 or Position (0 = retracted, 16K fully extended)', 'N'], -"SPOILERS_RIGHT_POSITION": ["Percent right spoiler deflected", b'SPOILERS RIGHT POSITION', b'Percent Over 100 or Position (0 = retracted, 16K fully extended)', 'N'], -"FLAPS_HANDLE_INDEX": ["Index of current flap position", b'FLAPS HANDLE INDEX', b'Number', 'Y'], -"FLAPS_NUM_HANDLE_POSITIONS": ["Number of flap positions", b'FLAPS NUM HANDLE POSITIONS', b'Number', 'N'], -"TRAILING_EDGE_FLAPS_LEFT_PERCENT": ["Percent left trailing edge flap extended", b'TRAILING EDGE FLAPS LEFT PERCENT', b'Percent Over 100', 'Y'], -"TRAILING_EDGE_FLAPS_RIGHT_PERCENT": ["Percent right trailing edge flap extended", b'TRAILING EDGE FLAPS RIGHT PERCENT', b'Percent Over 100', 'Y'], -"TRAILING_EDGE_FLAPS_LEFT_ANGLE": ["Angle left trailing edge flap extended. Use TRAILING EDGE FLAPS LEFT PERCENT to set a value.", b'TRAILING EDGE FLAPS LEFT ANGLE', b'Radians', 'N'], -"TRAILING_EDGE_FLAPS_RIGHT_ANGLE": ["Angle right trailing edge flap extended. Use TRAILING EDGE FLAPS RIGHT PERCENT to set a value.", b'TRAILING EDGE FLAPS RIGHT ANGLE', b'Radians', 'N'], -"LEADING_EDGE_FLAPS_LEFT_PERCENT": ["Percent left leading edge flap extended", b'LEADING EDGE FLAPS LEFT PERCENT', b'Percent Over 100', 'Y'], -"LEADING_EDGE_FLAPS_RIGHT_PERCENT": ["Percent right leading edge flap extended", b'LEADING EDGE FLAPS RIGHT PERCENT', b'Percent Over 100', 'Y'], -"LEADING_EDGE_FLAPS_LEFT_ANGLE": ["Angle left leading edge flap extended. Use LEADING EDGE FLAPS LEFT PERCENT to set a value.", b'LEADING EDGE FLAPS LEFT ANGLE', b'Radians', 'N'], -"LEADING_EDGE_FLAPS_RIGHT_ANGLE": ["Angle right leading edge flap extended. Use LEADING EDGE FLAPS RIGHT PERCENT to set a value.", b'LEADING EDGE FLAPS RIGHT ANGLE', b'Radians', 'N'], -"AILERON_LEFT_DEFLECTION": ["Angle deflection", b'AILERON LEFT DEFLECTION', b'Radians', 'N'], -"AILERON_LEFT_DEFLECTION_PCT": ["Percent deflection", b'AILERON LEFT DEFLECTION PCT', b'Percent Over 100', 'N'], -"AILERON_RIGHT_DEFLECTION": ["Angle deflection", b'AILERON RIGHT DEFLECTION', b'Radians', 'N'], -"AILERON_RIGHT_DEFLECTION_PCT": ["Percent deflection", b'AILERON RIGHT DEFLECTION PCT', b'Percent Over 100', 'N'], -"AILERON_AVERAGE_DEFLECTION": ["Angle deflection", b'AILERON AVERAGE DEFLECTION', b'Radians', 'N'], -"RUDDER_DEFLECTION": ["Angle deflection", b'RUDDER DEFLECTION', b'Radians', 'N'], -"RUDDER_DEFLECTION_PCT": ["Percent deflection", b'RUDDER DEFLECTION PCT', b'Percent Over 100', 'N'], -"FLAPS_AVAILABLE": ["True if flaps available", b'FLAPS AVAILABLE', b'Bool', 'N'], -"FLAP_DAMAGE_BY_SPEED": ["True if flagps are damaged by excessive speed", b'FLAP DAMAGE BY SPEED', b'Bool', 'N'], -"ELEVATOR_DEFLECTION": ["Angle deflection", b'ELEVATOR DEFLECTION', b'Radians', 'N'], -"ELEVATOR_DEFLECTION_PCT": ["Percent deflection", b'ELEVATOR DEFLECTION PCT', b'Percent Over 100', 'N'], -"ALTERNATE_STATIC_SOURCE_OPEN": ["Alternate static air source", b'ALTERNATE STATIC SOURCE OPEN', b'Bool', 'N'], -"FOLDING_WING_HANDLE_POSITION": ["True if the folding wing handle is engaged.", b'FOLDING WING HANDLE POSITION', b'Bool', 'N'], -"FUEL_DUMP_SWITCH": ["If true the aircraft is dumping fuel at the rate set in the configuration file.", b'FUEL DUMP SWITCH', b'Bool', 'N'], - -// Autopilot Data -"AUTOPILOT_GLIDESLOPE_HOLD": ["GS hold active", b'AUTOPILOT GLIDESLOPE HOLD', b'Bool', 'N'], -"AUTOPILOT_RPM_HOLD_VAR": ["Selected rpm", b'AUTOPILOT RPM HOLD VAR', b'Number', 'N'], -"AUTOTHROTTLE_ACTIVE": ["Auto-throttle active", b'AUTOTHROTTLE ACTIVE', b'Bool', 'N'], -"AUTOPILOT_RPM_HOLD": ["True if autopilot rpm hold applied", b'AUTOPILOT RPM HOLD', b'Bool', 'N'], -"FLY_BY_WIRE_ELAC_SWITCH": ["True if the fly by wire Elevators and Ailerons computer is on.", b'FLY BY WIRE ELAC SWITCH', b'Bool', 'N'], -"FLY_BY_WIRE_FAC_SWITCH": ["True if the fly by wire Flight Augmentation computer is on.", b'FLY BY WIRE FAC SWITCH', b'Bool', 'N'], -"FLY_BY_WIRE_SEC_SWITCH": ["True if the fly by wire Spoilers and Elevators computer is on.", b'FLY BY WIRE SEC SWITCH', b'Bool', 'N'], -"FLY_BY_WIRE_ELAC_FAILED": ["True if the Elevators and Ailerons computer has failed.", b'FLY BY WIRE ELAC FAILED', b'Bool', 'N'], -"FLY_BY_WIRE_FAC_FAILED": ["True if the Flight Augmentation computer has failed.", b'FLY BY WIRE FAC FAILED', b'Bool', 'N'], -"FLY_BY_WIRE_SEC_FAILED": ["True if the Spoilers and Elevators computer has failed.", b'FLY BY WIRE SEC FAILED', b'Bool', 'N'], - -// Landing Gear -"IS_GEAR_RETRACTABLE": ["True if gear can be retracted", b'IS GEAR RETRACTABLE', b'Bool', 'N'], -"IS_GEAR_SKIS": ["True if landing gear is skis", b'IS GEAR SKIS', b'Bool', 'N'], -"IS_GEAR_FLOATS": ["True if landing gear is floats", b'IS GEAR FLOATS', b'Bool', 'N'], -"IS_GEAR_SKIDS": ["True if landing gear is skids", b'IS GEAR SKIDS', b'Bool', 'N'], -"IS_GEAR_WHEELS": ["True if landing gear is wheels", b'IS GEAR WHEELS', b'Bool', 'N'], -"GEAR_HANDLE_POSITION": ["True if gear handle is applied", b'GEAR HANDLE POSITION', b'Bool', 'Y'], -"GEAR_HYDRAULIC_PRESSURE": ["Gear hydraulic pressure", b'GEAR HYDRAULIC PRESSURE', b'Pound force per square foot (psf)', 'N'], -"TAILWHEEL_LOCK_ON": ["True if tailwheel lock applied", b'TAILWHEEL LOCK ON', b'Bool', 'N'], -"GEAR_CENTER_POSITION": ["Percent center gear extended", b'GEAR CENTER POSITION', b'Percent Over 100', 'Y'], -"GEAR_LEFT_POSITION": ["Percent left gear extended", b'GEAR LEFT POSITION', b'Percent Over 100', 'Y'], -"GEAR_RIGHT_POSITION": ["Percent right gear extended", b'GEAR RIGHT POSITION', b'Percent Over 100', 'Y'], -"GEAR_TAIL_POSITION": ["Percent tail gear extended", b'GEAR TAIL POSITION', b'Percent Over 100', 'N'], -"GEAR_AUX_POSITION": ["Percent auxiliary gear extended", b'GEAR AUX POSITION', b'Percent Over 100', 'N'], -"GEAR_POSITION:index": ["Position of landing gear:; 0 = unknown; 1 = up; 2 = down", b'GEAR POSITION:index', b'Enum', 'Y'], -"GEAR_ANIMATION_POSITION:index": ["Percent gear animation extended", b'GEAR ANIMATION POSITION:index', b'Number', 'N'], -"AUTO_BRAKE_SWITCH_CB": ["Auto brake switch position", b'AUTO BRAKE SWITCH CB', b'Number', 'N'], -"WATER_RUDDER_HANDLE_POSITION": ["Position of the water rudder handle (0 handle retracted, 100 rudder handle applied)", b'WATER RUDDER HANDLE POSITION', b'Percent Over 100', 'Y'], -"WATER_LEFT_RUDDER_EXTENDED": ["Percent extended", b'WATER LEFT RUDDER EXTENDED', b'Percentage', 'N'], -"WATER_RIGHT_RUDDER_EXTENDED": ["Percent extended", b'WATER RIGHT RUDDER EXTENDED', b'Percentage', 'N'], -"GEAR_CENTER_STEER_ANGLE": ["Center wheel angle, negative to the left, positive to the right.", b'GEAR CENTER STEER ANGLE', b'Percent Over 100', 'N'], -"GEAR_LEFT_STEER_ANGLE": ["Left wheel angle, negative to the left, positive to the right.", b'GEAR LEFT STEER ANGLE', b'Percent Over 100', 'N'], -"GEAR_RIGHT_STEER_ANGLE": ["Right wheel angle, negative to the left, positive to the right.", b'GEAR RIGHT STEER ANGLE', b'Percent Over 100', 'N'], -"GEAR_AUX_STEER_ANGLE": ["Aux wheel angle, negative to the left, positive to the right. The aux wheel is the fourth set of gear, sometimes used on helicopters.", b'GEAR AUX STEER ANGLE', b'Percent Over 100', 'N'], -"GEAR_STEER_ANGLE:index": ["Alternative method of getting the steer angle. Index is; 0 = center; 1 = left; 2 = right; 3 = aux", b'GEAR STEER ANGLE:index', b'Percent Over 100', 'N'], -"WATER_LEFT_RUDDER_STEER_ANGLE": ["Water left rudder angle, negative to the left, positive to the right.", b'WATER LEFT RUDDER STEER ANGLE', b'Percent Over 100', 'N'], -"WATER_RIGHT_RUDDER_STEER_ANGLE": ["Water right rudder angle, negative to the left, positive to the right.", b'WATER RIGHT RUDDER STEER ANGLE', b'Percent Over 100', 'N'], -"GEAR_CENTER_STEER_ANGLE_PCT": ["Center steer angle as a percentage", b'GEAR CENTER STEER ANGLE PCT', b'Percent Over 100', 'N'], -"GEAR_LEFT_STEER_ANGLE_PCT": ["Left steer angle as a percentage", b'GEAR LEFT STEER ANGLE PCT', b'Percent Over 100', 'N'], -"GEAR_RIGHT_STEER_ANGLE_PCT": ["Right steer angle as a percentage", b'GEAR RIGHT STEER ANGLE PCT', b'Percent Over 100', 'N'], -"GEAR_AUX_STEER_ANGLE_PCT": ["Aux steer angle as a percentage", b'GEAR AUX STEER ANGLE PCT', b'Percent Over 100', 'N'], -"GEAR_STEER_ANGLE_PCT:index": ["Alternative method of getting steer angle as a percentage. Index is; 0 = center; 1 = left; 2 = right; 3 = aux", b'GEAR STEER ANGLE PCT:index', b'Percent Over 100', 'N'], -"WATER_LEFT_RUDDER_STEER_ANGLE_PCT": ["Water left rudder angle as a percentage", b'WATER LEFT RUDDER STEER ANGLE PCT', b'Percent Over 100', 'N'], -"WATER_RIGHT_RUDDER_STEER_ANGLE_PCT": ["Water right rudder as a percentage", b'WATER RIGHT RUDDER STEER ANGLE PCT', b'Percent Over 100', 'N'], -"WHEEL_RPM:index": ["Wheel rpm. Index is; 0 = center; 1 = left; 2 = right; 3 = aux", b'WHEEL RPM:index', b'Rpm', 'N'], -"CENTER_WHEEL_RPM": ["Center landing gear rpm", b'CENTER WHEEL RPM', b'Rpm', 'N'], -"LEFT_WHEEL_RPM": ["Left landing gear rpm", b'LEFT WHEEL RPM', b'Rpm', 'N'], -"RIGHT_WHEEL_RPM": ["Right landing gear rpm", b'RIGHT WHEEL RPM', b'Rpm', 'N'], -"AUX_WHEEL_RPM": ["Rpm of fourth set of gear wheels.", b'AUX WHEEL RPM', b'Rpm', 'N'], -"WHEEL_ROTATION_ANGLE:index": ["Wheel rotation angle. Index is; 0 = center; 1 = left; 2 = right; 3 = aux", b'WHEEL ROTATION ANGLE:index', b'Radians', 'N'], -"CENTER_WHEEL_ROTATION_ANGLE": ["Center wheel rotation angle", b'CENTER WHEEL ROTATION ANGLE', b'Radians', 'N'], -"LEFT_WHEEL_ROTATION_ANGLE": ["Left wheel rotation angle", b'LEFT WHEEL ROTATION ANGLE', b'Radians', 'N'], -"RIGHT_WHEEL_ROTATION_ANGLE": ["Right wheel rotation angle", b'RIGHT WHEEL ROTATION ANGLE', b'Radians', 'N'], -"AUX_WHEEL_ROTATION_ANGLE": ["Aux wheel rotation angle", b'AUX WHEEL ROTATION ANGLE', b'Radians', 'N'], -"GEAR_EMERGENCY_HANDLE_POSITION": ["True if gear emergency handle applied", b'GEAR EMERGENCY HANDLE POSITION', b'Bool', 'N'], -"GEAR_WARNING": ["One of:; 0: unknown; 1: normal; 2: amphib", b'GEAR WARNING', b'Enum', 'N'], -"ANTISKID_BRAKES_ACTIVE": ["True if antiskid brakes active", b'ANTISKID BRAKES ACTIVE', b'Bool', 'N'], -"RETRACT_FLOAT_SWITCH": ["True if retract float switch on", b'RETRACT FLOAT SWITCH', b'Bool', 'N'], -"RETRACT_LEFT_FLOAT_EXTENDED": ["If aircraft has retractable floats.", b'RETRACT LEFT FLOAT EXTENDED', b'Percent (0 is fully retracted, 100 is fully extended)', 'N'], -"RETRACT_RIGHT_FLOAT_EXTENDED": ["If aircraft has retractable floats.", b'RETRACT RIGHT FLOAT EXTENDED', b'Percent (0 is fully retracted, 100 is fully extended)', 'N'], -"STEER_INPUT_CONTROL": ["Position of steering tiller", b'STEER INPUT CONTROL', b'Percent over 100', 'N'], -"GEAR_DAMAGE_BY_SPEED": ["True if gear has been damaged by excessive speed", b'GEAR DAMAGE BY SPEED', b'Bool', 'N'], -"GEAR_SPEED_EXCEEDED": ["True if safe speed limit for gear exceeded", b'GEAR SPEED EXCEEDED', b'Bool', 'N'], -"NOSEWHEEL_LOCK_ON": ["True if the nosewheel lock is engaged.", b'NOSEWHEEL LOCK ON', b'Bool', 'N'], - -// Aircraft Environment Data -"AMBIENT_DENSITY": ["Ambient density", b'AMBIENT DENSITY', b'Slugs per cubic feet', 'N'], -"AMBIENT_TEMPERATURE": ["Ambient temperature", b'AMBIENT TEMPERATURE', b'Celsius', 'N'], -"AMBIENT_PRESSURE": ["Ambient pressure", b'AMBIENT PRESSURE', b'Inches of mercury, inHg', 'N'], -"AMBIENT_WIND_VELOCITY": ["Wind velocity", b'AMBIENT WIND VELOCITY', b'Knots', 'N'], -"AMBIENT_WIND_DIRECTION": ["Wind direction", b'AMBIENT WIND DIRECTION', b'Degrees', 'N'], -"AMBIENT_WIND_X": ["Wind component in East/West direction.", b'AMBIENT WIND X', b'Meters per second', 'N'], -"AMBIENT_WIND_Y": ["Wind component in vertical direction.", b'AMBIENT WIND Y', b'Meters per second', 'N'], -"AMBIENT_WIND_Z": ["Wind component in North/South direction.", b'AMBIENT WIND Z', b'Meters per second', 'N'], -"STRUCT_AMBIENT_WIND": ["X (latitude), Y (vertical) and Z (longitude) components of the wind.", b'STRUCT AMBIENT WIND', b'Feet_per_second', 'N'], -"AIRCRAFT_WIND_X": ["Wind component in aircraft lateral axis", b'AIRCRAFT WIND X', b'Knots', 'N'], -"AIRCRAFT_WIND_Y": ["Wind component in aircraft vertical axis", b'AIRCRAFT WIND Y', b'Knots', 'N'], -"AIRCRAFT_WIND_Z": ["Wind component in aircraft longitudinal axis", b'AIRCRAFT WIND Z', b'Knots', 'N'], -"BAROMETER_PRESSURE": ["Barometric pressure", b'BAROMETER PRESSURE', b'Millibars', 'N'], -"SEA_LEVEL_PRESSURE": ["Barometric pressure at sea level", b'SEA LEVEL PRESSURE', b'Millibars', 'N'], -"TOTAL_AIR_TEMPERATURE": ["Total air temperature is the air temperature at the front of the aircraft where the ram pressure from the speed of the aircraft is taken into account.", b'TOTAL AIR TEMPERATURE', b'Celsius', 'N'], -"WINDSHIELD_RAIN_EFFECT_AVAILABLE": ["Is visual effect available on this aircraft", b'WINDSHIELD RAIN EFFECT AVAILABLE', b'Bool', 'N'], -"AMBIENT_IN_CLOUD": ["True if the aircraft is in a cloud.", b'AMBIENT IN CLOUD', b'Bool', 'N'], -"AMBIENT_VISIBILITY": ["Ambient visibility", b'AMBIENT VISIBILITY', b'Meters', 'N'], -"STANDARD_ATM_TEMPERATURE": ["Outside temperature on the standard ATM scale", b'STANDARD ATM TEMPERATURE', b'Rankine', 'N'], - -// Misc Systems Data -"SMOKE_ENABLE": ["Set to True to activate the smoke system, if one is available (for example, on the Extra).", b'SMOKE ENABLE', b'Bool', 'Y'], -"SMOKESYSTEM_AVAILABLE": ["Smoke system available", b'SMOKESYSTEM AVAILABLE', b'Bool', 'N'], -"FOLDING_WING_LEFT_PERCENT": ["Left folding wing position, 100 is fully folded", b'FOLDING WING LEFT PERCENT', b'Percent Over 100', 'Y'], -"FOLDING_WING_RIGHT_PERCENT": ["Right folding wing position, 100 is fully folded", b'FOLDING WING RIGHT PERCENT', b'Percent Over 100', 'Y'], -"CANOPY_OPEN": ["Percent primary door/exit open", b'CANOPY OPEN', b'Percent Over 100', 'Y'], -"TAILHOOK_POSITION": ["Percent tail hook extended", b'TAILHOOK POSITION', b'Percent Over 100', 'Y'], -"EXIT_OPEN:index": ["Percent door/exit open", b'EXIT OPEN:index', b'Percent Over 100', 'Y'], -"STALL_HORN_AVAILABLE": ["True if stall alarm available", b'STALL HORN AVAILABLE', b'Bool', 'N'], -"ENGINE_MIXURE_AVAILABLE": ["True if engine mixture is available for prop engines. Obsolete value as mixture is always available. Spelling error in variable name.", b'ENGINE MIXURE AVAILABLE', b'Bool', 'N'], -"CARB_HEAT_AVAILABLE": ["True if carb heat available", b'CARB HEAT AVAILABLE', b'Bool', 'N'], -"SPOILER_AVAILABLE": ["True if spoiler system available", b'SPOILER AVAILABLE', b'Bool', 'N'], -"IS_TAIL_DRAGGER": ["True if the aircraft is a taildragger", b'IS TAIL DRAGGER', b'Bool', 'N'], -"STROBES_AVAILABLE": ["True if strobe lights are available", b'STROBES AVAILABLE', b'Bool', 'N'], -"TOE_BRAKES_AVAILABLE": ["True if toe brakes are available", b'TOE BRAKES AVAILABLE', b'Bool', 'N'], -"PUSHBACK_STATE": ["Type of pushback :; 0 = Straight; 1 = Left; 2 = Right", b'PUSHBACK STATE', b'Enum', 'Y'], -"ELECTRICAL_MASTER_BATTERY": ["Battery switch position", b'ELECTRICAL MASTER BATTERY', b'Bool', 'Y'], -"ELECTRICAL_TOTAL_LOAD_AMPS": ["Total load amps", b'ELECTRICAL TOTAL LOAD AMPS', b'Amperes', 'Y'], -"ELECTRICAL_BATTERY_LOAD": ["Battery load", b'ELECTRICAL BATTERY LOAD', b'Amperes', 'Y'], -"ELECTRICAL_BATTERY_VOLTAGE": ["Battery voltage", b'ELECTRICAL BATTERY VOLTAGE', b'Volts', 'Y'], -"ELECTRICAL_MAIN_BUS_VOLTAGE": ["Main bus voltage", b'ELECTRICAL MAIN BUS VOLTAGE', b'Volts', 'Y'], -"ELECTRICAL_MAIN_BUS_AMPS": ["Main bus current", b'ELECTRICAL MAIN BUS AMPS', b'Amperes', 'Y'], -"ELECTRICAL_AVIONICS_BUS_VOLTAGE": ["Avionics bus voltage", b'ELECTRICAL AVIONICS BUS VOLTAGE', b'Volts', 'Y'], -"ELECTRICAL_AVIONICS_BUS_AMPS": ["Avionics bus current", b'ELECTRICAL AVIONICS BUS AMPS', b'Amperes', 'Y'], -"ELECTRICAL_HOT_BATTERY_BUS_VOLTAGE": ["Voltage available when battery switch is turned off", b'ELECTRICAL HOT BATTERY BUS VOLTAGE', b'Volts', 'Y'], -"ELECTRICAL_HOT_BATTERY_BUS_AMPS": ["Current available when battery switch is turned off", b'ELECTRICAL HOT BATTERY BUS AMPS', b'Amperes', 'Y'], -"ELECTRICAL_BATTERY_BUS_VOLTAGE": ["Battery bus voltage", b'ELECTRICAL BATTERY BUS VOLTAGE', b'Volts', 'Y'], -"ELECTRICAL_BATTERY_BUS_AMPS": ["Battery bus current", b'ELECTRICAL BATTERY BUS AMPS', b'Amperes', 'Y'], -"ELECTRICAL_GENALT_BUS_VOLTAGE:index": ["Genalt bus voltage (takes engine index)", b'ELECTRICAL GENALT BUS VOLTAGE:index', b'Volts', 'Y'], -"ELECTRICAL_GENALT_BUS_AMPS:index": ["Genalt bus current (takes engine index)", b'ELECTRICAL GENALT BUS AMPS:index', b'Amperes', 'Y'], -"CIRCUIT_GENERAL_PANEL_ON": ["Is electrical power available to this circuit", b'CIRCUIT GENERAL PANEL ON', b'Bool', 'N'], -"CIRCUIT_FLAP_MOTOR_ON": ["Is electrical power available to this circuit", b'CIRCUIT FLAP MOTOR ON', b'Bool', 'N'], -"CIRCUIT_GEAR_MOTOR_ON": ["Is electrical power available to this circuit", b'CIRCUIT GEAR MOTOR ON', b'Bool', 'N'], -"CIRCUIT_AUTOPILOT_ON": ["Is electrical power available to this circuit", b'CIRCUIT AUTOPILOT ON', b'Bool', 'N'], -"CIRCUIT_AVIONICS_ON": ["Is electrical power available to this circuit", b'CIRCUIT AVIONICS ON', b'Bool', 'N'], -"CIRCUIT_PITOT_HEAT_ON": ["Is electrical power available to this circuit", b'CIRCUIT PITOT HEAT ON', b'Bool', 'N'], -"CIRCUIT_PROP_SYNC_ON": ["Is electrical power available to this circuit", b'CIRCUIT PROP SYNC ON', b'Bool', 'N'], -"CIRCUIT_AUTO_FEATHER_ON": ["Is electrical power available to this circuit", b'CIRCUIT AUTO FEATHER ON', b'Bool', 'N'], -"CIRCUIT_AUTO_BRAKES_ON": ["Is electrical power available to this circuit", b'CIRCUIT AUTO BRAKES ON', b'Bool', 'N'], -"CIRCUIT_STANDY_VACUUM_ON": ["Is electrical power available to this circuit", b'CIRCUIT STANDY VACUUM ON', b'Bool', 'N'], -"CIRCUIT_MARKER_BEACON_ON": ["Is electrical power available to this circuit", b'CIRCUIT MARKER BEACON ON', b'Bool', 'N'], -"CIRCUIT_GEAR_WARNING_ON": ["Is electrical power available to this circuit", b'CIRCUIT GEAR WARNING ON', b'Bool', 'N'], -"CIRCUIT_HYDRAULIC_PUMP_ON": ["Is electrical power available to this circuit", b'CIRCUIT HYDRAULIC PUMP ON', b'Bool', 'N'], -"HYDRAULIC_PRESSURE:index": ["Hydraulic system pressure. Indexes start at 1.", b'HYDRAULIC PRESSURE:index', b'Pound force per square foot', 'N'], -"HYDRAULIC_RESERVOIR_PERCENT:index": ["Hydraulic pressure changes will follow changes to this variable. Indexes start at 1.", b'HYDRAULIC RESERVOIR PERCENT:index', b'Percent Over 100', 'Y'], -"HYDRAULIC_SYSTEM_INTEGRITY": ["Percent system functional", b'HYDRAULIC SYSTEM INTEGRITY', b'Percent Over 100', 'N'], -"APPLY_HEAT_TO_SYSTEMS": ["Used when too close to a fire.", b'APPLY HEAT TO SYSTEMS', b'Bool', 'Y'], -"DROPPABLE_OBJECTS_TYPE:index": ["The type of droppable object at the station number identified by the index.", b'DROPPABLE OBJECTS TYPE:index', b'String', 'Y'], -"DROPPABLE_OBJECTS_COUNT:index": ["The number of droppable objects at the station number identified by the index.", b'DROPPABLE OBJECTS COUNT:index', b'Number', 'N'], - -// Misc Data -"TOTAL_WEIGHT": ["Total weight of the aircraft", b'TOTAL WEIGHT', b'Pounds', 'N'], -"MAX_GROSS_WEIGHT": ["Maximum gross weight of the aircaft", b'MAX GROSS WEIGHT', b'Pounds', 'N'], -"EMPTY_WEIGHT": ["Empty weight of the aircraft", b'EMPTY WEIGHT', b'Pounds', 'N'], -"IS_USER_SIM": ["Is this the user loaded aircraft", b'IS USER SIM', b'Bool', 'N'], -"SIM_DISABLED": ["Is sim disabled", b'SIM DISABLED', b'Bool', 'Y'], -"G_FORCE": ["Current g force", b'G FORCE', b'GForce', 'Y'], -"ATC_HEAVY": ["Is this aircraft recognized by ATC as heavy", b'ATC HEAVY', b'Bool', 'Y'], -"AUTO_COORDINATION": ["Is auto-coordination active", b'AUTO COORDINATION', b'Bool', 'Y'], -"REALISM": ["General realism percent", b'REALISM', b'Number', 'Y'], -"TRUE_AIRSPEED_SELECTED": ["True if True Airspeed has been selected", b'TRUE AIRSPEED SELECTED', b'Bool', 'Y'], -"DESIGN_SPEED_VS0": ["Design speed at VS0", b'DESIGN SPEED VS0', b'Feet per second', 'N'], -"DESIGN_SPEED_VS1": ["Design speed at VS1", b'DESIGN SPEED VS1', b'Feet per second', 'N'], -"DESIGN_SPEED_VC": ["Design speed at VC", b'DESIGN SPEED VC', b'Feet per second', 'N'], -"MIN_DRAG_VELOCITY": ["Minimum drag velocity", b'MIN DRAG VELOCITY', b'Feet per second', 'N'], -"ESTIMATED_CRUISE_SPEED": ["Estimated cruise speed", b'ESTIMATED CRUISE SPEED', b'Feet per second', 'N'], -"CG_PERCENT": ["Longitudinal CG position as a percent of reference chord", b'CG PERCENT', b'Percent over 100', 'N'], -"CG_PERCENT_LATERAL": ["Lateral CG position as a percent of reference chord", b'CG PERCENT LATERAL', b'Percent over 100', 'N'], -"IS_SLEW_ACTIVE": ["True if slew is active", b'IS SLEW ACTIVE', b'Bool', 'Y'], -"IS_SLEW_ALLOWED": ["True if slew is enabled", b'IS SLEW ALLOWED', b'Bool', 'Y'], -"ATC_SUGGESTED_MIN_RWY_TAKEOFF": ["Suggested minimum runway length for takeoff. Used by ATC ", b'ATC SUGGESTED MIN RWY TAKEOFF', b'Feet', 'N'], -"ATC_SUGGESTED_MIN_RWY_LANDING": ["Suggested minimum runway length for landing. Used by ATC ", b'ATC SUGGESTED MIN RWY LANDING', b'Feet', 'N'], -"PAYLOAD_STATION_WEIGHT:index": ["Individual payload station weight", b'PAYLOAD STATION WEIGHT:index', b'Pounds', 'Y'], -"PAYLOAD_STATION_COUNT": ["Number of payload stations", b'PAYLOAD STATION COUNT', b'Number', 'N'], -"USER_INPUT_ENABLED": ["Is input allowed from the user", b'USER INPUT ENABLED', b'Bool', 'Y'], -"TYPICAL_DESCENT_RATE": ["Normal descent rate", b'TYPICAL DESCENT RATE', b'Feet per minute', 'N'], -"VISUAL_MODEL_RADIUS": ["Model radius", b'VISUAL MODEL RADIUS', b'Meters', 'N'], -"SimpleObject": ["; Viewer", b'SimpleObject', b'String', 'N'], -"SIGMA_SQRT": ["Sigma sqrt", b'SIGMA SQRT', b'Number', 'N'], -"DYNAMIC_PRESSURE": ["Dynamic pressure", b'DYNAMIC PRESSURE', b'Pounds per square foot', 'N'], -"TOTAL_VELOCITY": ["Velocity regardless of direction. For example, if a helicopter is ascending vertically at 100 fps, getting this variable will return 100.", b'TOTAL VELOCITY', b'Feet per second', 'N'], -"AIRSPEED_SELECT_INDICATED_OR_TRUE": ["The airspeed, whether true or indicated airspeed has been selected.", b'AIRSPEED SELECT INDICATED OR TRUE', b'Knots', 'N'], -"VARIOMETER_RATE": ["Variometer rate", b'VARIOMETER RATE', b'Feet per second', 'N'], -"VARIOMETER_SWITCH": ["True if the variometer switch is on", b'VARIOMETER SWITCH', b'Bool', 'N'], -"PRESSURE_ALTITUDE": ["Altitude reading", b'PRESSURE ALTITUDE', b'Meters', 'N'], -"MAGNETIC_COMPASS": ["Compass reading", b'MAGNETIC COMPASS', b'Degrees', 'N'], -"TURN_INDICATOR_RATE": ["Turn indicator reading", b'TURN INDICATOR RATE', b'Radians per second', 'N'], -"TURN_INDICATOR_SWITCH": ["True if turn indicator switch is on", b'TURN INDICATOR SWITCH', b'Bool', 'N'], -"YOKE_Y_INDICATOR": ["Yoke position in vertical direction", b'YOKE Y INDICATOR', b'Position', 'N'], -"YOKE_X_INDICATOR": ["Yoke position in horizontal direction", b'YOKE X INDICATOR', b'Position', 'N'], -"RUDDER_PEDAL_INDICATOR": ["Rudder pedal position", b'RUDDER PEDAL INDICATOR', b'Position', 'N'], -"BRAKE_DEPENDENT_HYDRAULIC_PRESSURE": ["Brake dependent hydraulic pressure reading", b'BRAKE DEPENDENT HYDRAULIC PRESSURE', b'Pounds per square foot', 'N'], -"WING_AREA": ["Total wing area", b'WING AREA', b'Square feet', 'N'], -"WING_SPAN": ["Total wing span", b'WING SPAN', b'Feet', 'N'], -"BETA_DOT": ["Beta dot", b'BETA DOT', b'Radians per second', 'N'], -"LINEAR_CL_ALPHA": ["Linear CL alpha", b'LINEAR CL ALPHA', b'Per radian', 'N'], -"STALL_ALPHA": ["Stall alpha", b'STALL ALPHA', b'Radians', 'N'], -"ZERO_LIFT_ALPHA": ["Zero lift alpha", b'ZERO LIFT ALPHA', b'Radians', 'N'], -"CG_AFT_LIMIT": ["Aft limit of CG", b'CG AFT LIMIT', b'Percent over 100', 'N'], -"CG_FWD_LIMIT": ["Forward limit of CG", b'CG FWD LIMIT', b'Percent over 100', 'N'], -"CG_MAX_MACH": ["Max mach CG", b'CG MAX MACH', b'Machs', 'N'], -"CG_MIN_MACH": ["Min mach CG", b'CG MIN MACH', b'Machs', 'N'], -"PAYLOAD_STATION_NAME": ["Descriptive name for payload station", b'PAYLOAD STATION NAME', b'String', 'N'], -"ELEVON_DEFLECTION": ["Elevon deflection", b'ELEVON DEFLECTION', b'Radians', 'N'], -"EXIT_TYPE": ["One of:; 0: Main; 1: Cargo; 2: Emergency; 3: Unknown", b'EXIT TYPE', b'Enum', 'N'], -"EXIT_POSX": ["Position of exit relative to datum reference point", b'EXIT POSX', b'Feet', 'N'], -"EXIT_POSY": ["Position of exit relative to datum reference point", b'EXIT POSY', b'Feet', 'N'], -"EXIT_POSZ": ["Position of exit relative to datum reference point", b'EXIT POSZ', b'Feet', 'N'], -"DECISION_HEIGHT": ["Design decision height", b'DECISION HEIGHT', b'Feet', 'N'], -"DECISION_ALTITUDE_MSL": ["Design decision altitude above mean sea level", b'DECISION ALTITUDE MSL', b'Feet', 'N'], -"EMPTY_WEIGHT_PITCH_MOI": ["Empty weight pitch moment of inertia", b'EMPTY WEIGHT PITCH MOI', b'Slugs per feet squared', 'N'], -"EMPTY_WEIGHT_ROLL_MOI": ["Empty weight roll moment of inertia", b'EMPTY WEIGHT ROLL MOI', b'Slugs per feet squared', 'N'], -"EMPTY_WEIGHT_YAW_MOI": ["Empty weight yaw moment of inertia", b'EMPTY WEIGHT YAW MOI', b'Slugs per feet squared', 'N'], -"EMPTY_WEIGHT_CROSS_COUPLED_MOI": ["Empty weigth cross coupled moment of inertia", b'EMPTY WEIGHT CROSS COUPLED MOI', b'Slugs per feet squared', 'N'], -"TOTAL_WEIGHT_PITCH_MOI": ["Total weight pitch moment of inertia", b'TOTAL WEIGHT PITCH MOI', b'Slugs per feet squared', 'N'], -"TOTAL_WEIGHT_ROLL_MOI": ["Total weight roll moment of inertia", b'TOTAL WEIGHT ROLL MOI', b'Slugs per feet squared', 'N'], -"TOTAL_WEIGHT_YAW_MOI": ["Total weight yaw moment of inertia", b'TOTAL WEIGHT YAW MOI', b'Slugs per feet squared', 'N'], -"TOTAL_WEIGHT_CROSS_COUPLED_MOI": ["Total weight cross coupled moment of inertia", b'TOTAL WEIGHT CROSS COUPLED MOI', b'Slugs per feet squared', 'N'], -"WATER_BALLAST_VALVE": ["True if water ballast valve is available", b'WATER BALLAST VALVE', b'Bool', 'N'], -"MAX_RATED_ENGINE_RPM": ["Maximum rated rpm", b'MAX RATED ENGINE RPM', b'Rpm', 'N'], -"FULL_THROTTLE_THRUST_TO_WEIGHT_RATIO": ["Full throttle thrust to weight ratio", b'FULL THROTTLE THRUST TO WEIGHT RATIO', b'Number', 'N'], -"PROP_AUTO_CRUISE_ACTIVE": ["True if prop auto cruise active", b'PROP AUTO CRUISE ACTIVE', b'Bool', 'N'], -"PROP_ROTATION_ANGLE": ["Prop rotation angle", b'PROP ROTATION ANGLE', b'Radians', 'N'], -"PROP_BETA_MAX": ["Prop beta max", b'PROP BETA MAX', b'Radians', 'N'], -"PROP_BETA_MIN": ["Prop beta min", b'PROP BETA MIN', b'Radians', 'N'], -"PROP_BETA_MIN_REVERSE": ["Prop beta min reverse", b'PROP BETA MIN REVERSE', b'Radians', 'N'], -"FUEL_SELECTED_TRANSFER_MODE": ["One of:; -1: off; 0: auto; 1: forward; 2: aft; 3: manual", b'FUEL SELECTED TRANSFER MODE', b'Enum', 'N'], -"DROPPABLE_OBJECTS_UI_NAME": ["Descriptive name, used in User Interface dialogs, of a droppable object", b'DROPPABLE OBJECTS UI NAME', b'String', 'N'], -"MANUAL_FUEL_PUMP_HANDLE": ["Position of manual fuel pump handle. 100 is fully deployed.", b'MANUAL FUEL PUMP HANDLE', b'Percent over 100', 'N'], -"BLEED_AIR_SOURCE_CONTROL": ["One of:; 0: min; 1: auto; 2: off; 3: apu; 4: engines", b'BLEED AIR SOURCE CONTROL', b'Enum', 'N'], -"ELECTRICAL_OLD_CHARGING_AMPS": ["Legacy, use ELECTRICAL BATTERY LOAD", b'ELECTRICAL OLD CHARGING AMPS', b'Amps', 'N'], -"HYDRAULIC_SWITCH": ["True if hydraulic switch is on", b'HYDRAULIC SWITCH', b'Bool', 'N'], -"CONCORDE_VISOR_POSITION_PERCENT": ["0 = up, 1.0 = extended/down", b'CONCORDE VISOR POSITION PERCENT', b'Percent over 100', 'N'], -"CONCORDE_NOSE_ANGLE": ["0 = up", b'CONCORDE NOSE ANGLE', b'Radians', 'N'], -"REALISM_CRASH_WITH_OTHERS": ["True indicates crashing with other aircraft is possible.", b'REALISM CRASH WITH OTHERS', b'Bool', 'N'], -"REALISM_CRASH_DETECTION": ["True indicates crash detection is turned on.", b'REALISM CRASH DETECTION', b'Bool', 'N'], -"MANUAL_INSTRUMENT_LIGHTS": ["True if instrument lights are set manually", b'MANUAL INSTRUMENT LIGHTS', b'Bool', 'N'], -"PITOT_ICE_PCT": ["Amount of pitot ice. 100 is fully iced.", b'PITOT ICE PCT', b'Percent over 100', 'N'], -"SEMIBODY_LOADFACTOR_Y": ["Semibody loadfactor x and z are not supported.", b'SEMIBODY LOADFACTOR Y', b'Number', 'N'], -"SEMIBODY_LOADFACTOR_YDOT": ["Semibody loadfactory ydot", b'SEMIBODY LOADFACTOR YDOT', b'Per second', 'N'], -"RAD_INS_SWITCH": ["True if Rad INS switch on", b'RAD INS SWITCH', b'Bool', 'N'], -"SIMULATED_RADIUS": ["Simulated radius", b'SIMULATED RADIUS', b'Feet', 'N'], -"STRUCTURAL_ICE_PCT": ["Amount of ice on aircraft structure. 100 is fully iced.", b'STRUCTURAL ICE PCT', b'Percent over 100', 'N'], -"ARTIFICIAL_GROUND_ELEVATION": ["In case scenery is not loaded for AI planes, this variable can be used to set a default surface elevation.", b'ARTIFICIAL GROUND ELEVATION', b'Feet', 'N'], -"SURFACE_INFO_VALID": ["True indicates SURFACE CONDITION is meaningful.", b'SURFACE INFO VALID', b'Bool', 'N'], -"SURFACE_CONDITION": ["One of:; 0: Normal; 1: Wet; 2: Icy; 3: Snow", b'SURFACE CONDITION', b'Enum', 'N'], -"PUSHBACK_ANGLE": ["Pushback angle (the heading of the tug)", b'PUSHBACK ANGLE', b'Radians', 'N'], -"PUSHBACK_CONTACTX": ["The towpoint position, relative to the aircrafts datum reference point.", b'PUSHBACK CONTACTX', b'Feet', 'N'], -"PUSHBACK_CONTACTY": ["Pushback contact position in vertical direction", b'PUSHBACK CONTACTY', b'Feet', 'N'], -"PUSHBACK_CONTACTZ": ["Pushback contact position in fore/aft direction", b'PUSHBACK CONTACTZ', b'Feet', 'N'], -"PUSHBACK_WAIT": ["True if waiting for pushback.", b'PUSHBACK WAIT', b'Bool', 'N'], -"YAW_STRING_ANGLE": ["The yaw string angle. Yaw strings are attached to gliders as visible indicators of the yaw angle. An animation of this is not implemented in ESP.", b'YAW STRING ANGLE', b'Radians', 'N'], -"YAW_STRING_PCT_EXTENDED": ["Yaw string angle as a percentage", b'YAW STRING PCT EXTENDED', b'Percent over 100', 'N'], -"INDUCTOR_COMPASS_PERCENT_DEVIATION": ["Inductor compass deviation reading", b'INDUCTOR COMPASS PERCENT DEVIATION', b'Percent over 100', 'N'], -"INDUCTOR_COMPASS_HEADING_REF": ["Inductor compass heading", b'INDUCTOR COMPASS HEADING REF', b'Radians', 'N'], -"ANEMOMETER_PCT_RPM": ["Anemometer rpm as a percentage", b'ANEMOMETER PCT RPM', b'Percent over 100', 'N'], -"ROTOR_ROTATION_ANGLE": ["Main rotor rotation angle (helicopters only)", b'ROTOR ROTATION ANGLE', b'Radians', 'N'], -"DISK_PITCH_ANGLE": ["Main rotor pitch angle (helicopters only)", b'DISK PITCH ANGLE', b'Radians', 'N'], -"DISK_BANK_ANGLE": ["Main rotor bank angle (helicopters only)", b'DISK BANK ANGLE', b'Radians', 'N'], -"DISK_PITCH_PCT": ["Main rotor pitch percent (helicopters only)", b'DISK PITCH PCT', b'Percent over 100', 'N'], -"DISK_BANK_PCT": ["Main rotor bank percent (helicopters only)", b'DISK BANK PCT', b'Percent over 100', 'N'], -"DISK_CONING_PCT": ["Main rotor coning percent (helicopters only)", b'DISK CONING PCT', b'Percent over 100', 'N'], -"NAV_VOR_LLAF64": ["Nav VOR latitude, longitude, altitude", b'NAV VOR LLAF64', b'LLA structure', 'N'], -"NAV_GS_LLAF64": ["Nav GS latitude, longitude, altitude", b'NAV GS LLAF64', b'LLA structure', 'N'], -"STATIC_CG_TO_GROUND": ["Static CG to ground", b'STATIC CG TO GROUND', b'Feet', 'N'], -"STATIC_PITCH": ["Static pitch", b'STATIC PITCH', b'Radians', 'N'], -"CRASH_SEQUENCE": ["One of:; 0: off; 1: complete; 3: reset; 4: pause; 11: start", b'CRASH SEQUENCE', b'Enum', 'N'], -"CRASH_FLAG": ["One of:; 0: None; 2: Mountain; 4: General; 6: Building; 8: Splash; 10: Gear up; 12: Overstress; 14: Building; 16: Aircraft; 18: Fuel Truck", b'CRASH FLAG', b'Enum', 'N'], -"TOW_RELEASE_HANDLE": ["Position of tow release handle. 100 is fully deployed.", b'TOW RELEASE HANDLE', b'Percent over 100', 'N'], -"TOW_CONNECTION": ["True if a towline is connected to both tow plane and glider.", b'TOW CONNECTION', b'Bool', 'N'], -"APU_PCT_RPM": ["Auxiliary power unit rpm, as a percentage", b'APU PCT RPM', b'Percent over 100', 'N'], -"APU_PCT_STARTER": ["Auxiliary power unit starter, as a percentage", b'APU PCT STARTER', b'Percent over 100', 'N'], -"APU_VOLTS": ["Auxiliary power unit voltage", b'APU VOLTS', b'Volts', 'N'], -"APU_GENERATOR_SWITCH": ["True if APU generator switch on", b'APU GENERATOR SWITCH', b'Bool', 'N'], -"APU_GENERATOR_ACTIVE": ["True if APU generator active", b'APU GENERATOR ACTIVE', b'Bool', 'N'], -"APU_ON_FIRE_DETECTED": ["True if APU on fire", b'APU ON FIRE DETECTED', b'Bool', 'N'], -"PRESSURIZATION_CABIN_ALTITUDE": ["The current altitude of the cabin pressurization..", b'PRESSURIZATION CABIN ALTITUDE', b'Feet', 'N'], -"PRESSURIZATION_CABIN_ALTITUDE_GOAL": ["The set altitude of the cabin pressurization.", b'PRESSURIZATION CABIN ALTITUDE GOAL', b'Feet', 'N'], -"PRESSURIZATION_CABIN_ALTITUDE_RATE": ["The rate at which cabin pressurization changes.", b'PRESSURIZATION CABIN ALTITUDE RATE', b'Feet per second', 'N'], -"PRESSURIZATION_PRESSURE_DIFFERENTIAL": ["The difference in pressure between the set altitude pressurization and the current pressurization.", b'PRESSURIZATION PRESSURE DIFFERENTIAL', b'Pounds per square foot', 'N'], -"PRESSURIZATION_DUMP_SWITCH": ["True if the cabin pressurization dump switch is on.", b'PRESSURIZATION DUMP SWITCH', b'Bool', 'N'], -"FIRE_BOTTLE_SWITCH": ["True if the fire bottle switch is on.", b'FIRE BOTTLE SWITCH', b'Bool', 'N'], -"FIRE_BOTTLE_DISCHARGED": ["True if the fire bottle is discharged.", b'FIRE BOTTLE DISCHARGED', b'Bool', 'N'], -"CABIN_NO_SMOKING_ALERT_SWITCH": ["True if the No Smoking switch is on.", b'CABIN NO SMOKING ALERT SWITCH', b'Bool', 'Y'], -"CABIN_SEATBELTS_ALERT_SWITCH": ["True if the Seatbelts switch is on.", b'CABIN SEATBELTS ALERT SWITCH', b'Bool', 'Y'], -"GPWS_WARNING": ["True if Ground Proximity Warning System installed.", b'GPWS WARNING', b'Bool', 'N'], -"GPWS_SYSTEM_ACTIVE": ["True if the Ground Proximity Warning System is active", b'GPWS SYSTEM ACTIVE', b'Bool', 'Y'], -"CRASH_FLAG": ["One of:; 0: None; 2: Mountain; 4: General; 6: Building; 8: Splash; 10: Gear up; 12: Overstress; 14: Building; 16: Aircraft; 18: Fuel Truck", b'CRASH FLAG', b'Enum', 'N'], -"IS_ALTITUDE_FREEZE_ON": ["True if the altitude of the aircraft is frozen.", b'IS ALTITUDE FREEZE ON', b'Bool', 'N'], -"IS_ATTITUDE_FREEZE_ON": ["True if the attitude (pitch, bank and heading) of the aircraft is frozen.", b'IS ATTITUDE FREEZE ON', b'Bool', 'N'], - -// Aircraft String Data -"HSI_STATION_IDENT": ["Tuned station identifier", b'HSI STATION IDENT', b'String', 'N'], -"GPS_WP_PREV_ID": ["ID of previous GPS waypoint", b'GPS WP PREV ID', b'String', 'N'], -"GPS_WP_NEXT_ID": ["ID of next GPS waypoint", b'GPS WP NEXT ID', b'String', 'N'], -"GPS_APPROACH_AIRPORT_ID": ["ID of airport", b'GPS APPROACH AIRPORT ID', b'String', 'N'], -"GPS_APPROACH_APPROACH_ID": ["ID of approach", b'GPS APPROACH APPROACH ID', b'String', 'N'], -"GPS_APPROACH_TRANSITION_ID": ["ID of approach transition", b'GPS APPROACH TRANSITION ID', b'String', 'N'], \ No newline at end of file From c2e625f1dfc65f1e59dc2c5e35f0d191f02d1795 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sun, 17 Apr 2022 07:59:27 -0400 Subject: [PATCH 93/93] Update generated docs and JSON. --- DOCUMENTATION.MD | 702 ++++++++++++++++++++++++++---------------- entry.tp | 771 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 1064 insertions(+), 409 deletions(-) diff --git a/DOCUMENTATION.MD b/DOCUMENTATION.MD index fc68c6c..a7cb96d 100644 --- a/DOCUMENTATION.MD +++ b/DOCUMENTATION.MD @@ -1,6 +1,10 @@ -# MSFS 2020 TouchPortal Plugin +# MSFS Touch Portal Plugin Documentation -This plugin will provide a two way interface between Touch Portal and Microsoft Flight Simulator 2020 through SimConnect. +This plugin provides a two-way interface between Touch Portal and Flight Simulators which use SimConnect, such as Microsoft Flight Simulator 2020 and FS-X. + +For further documentation, please see https://github.com/mpaperno/MSFSTouchPortalPlugin/wiki + +This documentation generated for plugin v0.7.0.0-mp --- @@ -8,27 +12,29 @@ This plugin will provide a two way interface between Touch Portal and Microsoft [Plugin Settings](#plugin-settings) -[MSFS - AutoPilot](#msfs---autopilot) +[Actions and States by Category](#actions-and-states-by-category) + +* [Plugin](#plugin) -[MSFS - Communication](#msfs---communication) +* [AutoPilot](#autopilot) -[MSFS - Electrical](#msfs---electrical) +* [Communication](#communication) -[MSFS - Engine](#msfs---engine) +* [Electrical](#electrical) -[MSFS - Environment](#msfs---environment) +* [Engine](#engine) -[MSFS - Failures](#msfs---failures) +* [Environment](#environment) -[MSFS - Flight Instruments](#msfs---flight-instruments) +* [Failures](#failures) -[MSFS - Flight Systems](#msfs---flight-systems) +* [Flight Instruments](#flight-instruments) -[MSFS - Fuel](#msfs---fuel) +* [Flight Systems](#flight-systems) -[MSFS - Plugin](#msfs---plugin) +* [Fuel](#fuel) -[MSFS - System](#msfs---system) +* [System](#system) --- @@ -37,28 +43,163 @@ This plugin will provide a two way interface between Touch Portal and Microsoft ### Connect To Flight Sim on Startup (0/1) -| Read-only | Type | Default Value | Max. Length | Min. Value | Max. Value | -| --- | --- | --- | --- | --- | --- | -| False | number | 1 | N/A | 0 | 1 | +| Read-only | Type | Default Value | Min. Value | Max. Value | +| --- | --- | --- | --- | --- | +| False | number | 0 | 0 | 1 | Set to 1 to automatically attempt connection to flight simulator upon Touch Portal startup. Set to 0 to only connect manually via the provided Action. +### Sim Variable State Config File(s) (blank = Default) + +| Read-only | Type | Default Value | Max. Length | +| --- | --- | --- | --- | +| False | text | Default | 255 | + +Here you can specify one or more custom configuration files which define SimConnect variables to request as Touch Portal States. This plugin comes with an extensive default set of states, however since the possibilities between which variables are requested, which units they are displayed in,and how they are formatted are almost endless. This option provides a way to customize the output as desired. + +Enter a file name here, with or w/out the suffix (`.ini` is assumed). Separate multiple files with commas (and optional space). To include the default set of variables/states, use the name `Default` as one of the file names (in any position of the list). + +Files are loaded in the order in which they appear in the list, and in case of conflicting state IDs, the last one found will be used. + +The custom file(s) are expected to be in the folder specified in the "User Config Files Path" setting (see below). + +### SimConnect.cfg Index (0 for MSFS, 1 for FSX, or custom) + +| Read-only | Type | Default Value | Min. Value | Max. Value | +| --- | --- | --- | --- | --- | +| False | number | 0 | 0 | 20 | + +A __SimConnect.cfg__ file can contain a number of connection configurations, identified in sections with the `[SimConnect.N]` title. A default __SimConnect.cfg__ is included with this plugin (in the installation folder). You may also use a custom configuration file stored in the "User Config Files Path" folder (see below). + +The index number can be specified in this setting. This is useful for + 1. compatibility with FSX, and/or + 2. custom configurations over network connections (running Touch Portal on a different computer than the sim). + +The default configuration index is zero, which (in the included default SimConnect.cfg) is suitable for MSFS (2020). Use the index 1 for compatibility with FSX (or perhaps other sims). + +See here for more info: https://docs.flightsimulator.com/html/Programming_Tools/SimConnect/SimConnect_CFG_Definition.htm + +### User Config Files Path (blank for default) + +| Read-only | Type | Default Value | Max. Length | +| --- | --- | --- | --- | +| False | text | | 255 | + +The system path where custom user configuration files for sate definitions, SimConnect.cfg, etc, are stored. Keep it blank for default, which is `C:\Users\\AppData\Roaming\MSFSTouchPortalPlugin`. + +Note that using this plugin's installation folder for custom data storage is not recommended, since anything in there will likely get overwritten during a plugin update/re-install. + ### Held Action Repeat Rate (ms) -| Read-only | Type | Default Value | Max. Length | Min. Value | Max. Value | -| --- | --- | --- | --- | --- | --- | -| True | number | 450 | N/A | 50 | 2147483647 | +| Read-only | Type | Default Value | Min. Value | Max. Value | +| --- | --- | --- | --- | --- | +| True | number | 450 | 50 | 2147483647 | -Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Action Repeat Interval' action. +Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Action Repeat Interval' action. This setting cannot be changed from the TP Plugin Settings page (even though it appears to be editable on there). --- -## MSFS - AutoPilot +## Actions and States by Category + +### Plugin
Click to expand -### Actions +#### Actions + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionFormatData
index.   [type]     choices/default (in bold)
On
Hold
ConnectionToggle/On/Off SimConnect ConnectionSimConnect Connection - {0}
    +
  1. [choice]   Toggle, On, Off, Reload States
  2. +
Action Repeat IntervalHeld Action Repeat Rate (ms)Repeat Interval: {0} to/by: {1} ms
    +
  1. [choice]   Set, Increment, Decrement
  2. +
  3. [text]   450   <min: 50> <max: 2147483647>
  4. +
Activate a Custom Simulator EventTrigger a Simulator Event by name. The value, if any, should evaluate to numeric. Using basic math operators and dynamic state values is possible.Activate Event {0} with value {1} (if any)
    +
  1. [text]   SIMULATOR_EVENT_NAME
  2. +
  3. [text]   <empty>
  4. +
Activate a Simulator Event From ListTrigger a Simulator Event. The value, if any, should evaluate to numeric. Using basic math operators and dynamic state values is possible.From Category {0} Activate Event {1} with value {2} (if any)
    +
  1. [choice]  
  2. +
  3. [choice]  
Set Simulator Variable ValueSets a value on any loaded State which is marked as settable. If a numeric value is required, using basic math operators and dynamic state values is possible.Set Variable {0} to {1} (release AI: {2})
    +
  1. [choice]  
  2. +
  3. [text]   0
  4. +
  5. [switch]   False
  6. +
Request a Custom Simulator VariableRequest Simulator Variable Name: Index (if req'd): Category: Units: Format: Default Value: Settable: Update Period: Update Interval: Delta Epsilon: {0}{1}{2}{3}{4}{5}{6}{7}{8}{9}
    +
  1. [text]   SIMULATOR VARIABLE FULL NAME
  2. +
  3. [number]   0   <min: 0> <max: 99>
  4. +
  5. [choice]  
  6. +
  7. [choice]  
  8. +
  9. [text]   F2
  10. +
  11. [text]   0
  12. +
  13. [switch]   False
  14. +
  15. [choice]   Once, VisualFrame, SimFrame, Second, Millisecond
  16. +
  17. [number]   0   <min: 0> <max: 2147483647>
  18. +
  19. [number]   0.0099999   <min: 0.00> <max: 340282346638528859811704183484516925440.00>
  20. +
Request a Simulator Variable From ListFrom Simulator Category Request Simulator Variable Index (if req'd): Plugin Category: Units: Format: Default Value: Update Period: Update Interval: Delta Epsilon: {0}{1}{2}{3}{4}{5}{6}{7}{8}{9}
    +
  1. [choice]  
  2. +
  3. [choice]  
Remove a Simulator VariableRemove an existing Simulator Variable. Note that static TP States cannot be removed.Remove Simulator Variable {0}
    +
  1. [choice]  
  2. +
Load SimVar Definitions From FileLoad a set of simulator variable state definitions from a configuration file.Load from file {0} (name only for user config. folder, or full path with file name)
    +
  1. [file]   CustomStates.ini
  2. +
Save SimVar Definitions To FileSave the current simulator variable state definitions to a configuration file.Save {0} states to file {1} (name only for user config. folder, or full path with file name)
    +
  1. [choice]   Custom, All
  2. +
  3. [text]   CustomStates.ini
  4. +
+ + +#### States + + **Base Id:** MSFSTouchPortalPlugin.Plugin.State. + +| Id | SimVar Name | Description | Unit | Format | DefaultValue | Settable | +| --- | --- | --- | --- | --- | --- | --- | +| ActionRepeatInterval | | The current Held Action Repeat Rate (ms) | millisecond | | 450| | +| Connected | | The status of SimConnect (true/false/connecting) | string | | false| | +| RunningVersion | | The running plugin version number. | number | | 0| | +| EntryVersion | | The loaded entry.tp plugin version number. | number | | 0| | +| ConfigVersion | | The loaded entry.tp custom configuration version. | number | | 0| | +| LogMessages | | Most recent plugin log messages. | string | | | | + + +
+ +--- + +### AutoPilot +
Click to expand + +#### Actions @@ -76,7 +217,7 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac
  • [text]   0   <min: 0> <max: 5000>
  • - + @@ -92,7 +233,7 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac
  • [text]   0
  • - + @@ -113,11 +254,11 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac - - + @@ -160,7 +301,7 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac
  • [text]   1   <min: 0> <max: 359>
  • - + @@ -180,7 +321,7 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac
  • [text]   0   <min: 0> <max: 20>
  • - + @@ -206,7 +347,7 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac
  • [text]   1   <min: -5000> <max: 5000>
  • - + @@ -220,47 +361,49 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac
    NameDescriptionFormatData
    index.   [type]     choices/default (in bold)
    Sim Event(s)On
    Hold
    AP_SPD_VAR_SET
    Altitude HoldToggle/On/Off the altitude hold for auto pilotAltitude Hold - {0}
    1. [choice]   Toggle, On, Off
    Set English
    AP_ALT_VAR_SET_ENGLISH
    Set Metric
    AP_ALT_VAR_SET_METRIC
    AP Max Bank AngleIncrease/Decrease the max bank angleMax Bank Angle - {0}
    1. [choice]   Increase, Decrease
    details
    Increase
    AP_PITCH_REF_INC_UP
    Decrease
    AP_PITCH_REF_INC_DN
    Select
    AP_PITCH_REF_SELECT
    Attitude Hold Pitch Value SetSets the airspeed valueSet Attitude Hold Pitch Value to {0} (-16383 - +16383)
      -
    1. [text]   0   <min: -16383> <max: 16383>
    2. +
    Attitude Hold Pitch Value SetSets the airspeed valueSet Attitude Hold Pitch Value to {0} (-16384 to +16384)
      +
    1. [text]   0   <min: -16384> <max: 16384>
    AP_PITCH_REF_SET
    Auto BrakeIncrease/Decrease the auto brakeAuto Brake - {0}
    1. [choice]   Increase, Decrease
    HEADING_BUG_SET
    LocalizerToggle/On/Off the localizer for auto pilotLocalizer - {0}
    1. [choice]   Toggle, On, Off
    AP_MACH_VAR_SET
    Nav Mode - SetSets the nav to 1 or 2 for Nav modeNav Mode - {0}
    1. [number]   1   <min: 1> <max: 2>
    Set English
    AP_VS_VAR_SET_ENGLISH
    Set Metric
    AP_VS_VAR_SET_METRIC
    Wing LevelerToggle/On/Off the Wing Leveler for auto pilotWing Leveler - {0}
    1. [choice]   Toggle, On, Off
    -### States - -| Id | SimVar Name | Description | DefaultValue | -| --- | --- | --- | --- | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoThrottleArm | AUTOPILOT THROTTLE ARM | Auto Throttle Armed | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoThrottleGoAround | AUTOPILOT TAKEOFF POWER ACTIVE | Auto Throttle GoAround | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotAirSpeedHold | AUTOPILOT AIRSPEED HOLD | AutoPilot Air Speed Status | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotAirSpeedVar | AUTOPILOT AIRSPEED HOLD VAR | AutoPilot Air Speed Value | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotAltitudeHold | AUTOPILOT ALTITUDE LOCK | AutoPilot Altitude Status | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotAltitudeVar | AUTOPILOT ALTITUDE LOCK VAR | AutoPilot Altitude Value | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotApproachHold | AUTOPILOT APPROACH HOLD | AutoPilot Approach Status | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotAttitudeHold | AUTOPILOT ATTITUDE HOLD | AutoPilot Attitude Status | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotAvailable | AUTOPILOT AVAILABLE | AutoPilot Availability | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotBackCourseHold | AUTOPILOT BACKCOURSE HOLD | AutoPilot Back Course Status | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotFlightDirector | AUTOPILOT FLIGHT DIRECTOR ACTIVE | AutoPilot Flight Director Status | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotHeadingVar | AUTOPILOT HEADING LOCK DIR | AutoPilot Heading Direction | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotHeadingHold | AUTOPILOT HEADING LOCK | AutoPilot Heading Status | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotMach | AUTOPILOT MACH HOLD | AutoPilot Mach Hold | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotMachVar | AUTOPILOT MACH HOLD VAR | AutoPilot Mach Value | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotMaster | AUTOPILOT MASTER | AutoPilot Master Status | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotBanking | AUTOPILOT MAX BANK | AutoPilot Max Bank Angle | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotNavSelected | AUTOPILOT NAV SELECTED | AutoPilot Nav Selected Index | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotNav1Hold | AUTOPILOT NAV1 LOCK | AutoPilot Nav1 Status | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotAttitudeVar | AUTOPILOT PITCH HOLD REF | AutoPilot Pitch Reference Value | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotVerticalSpeedHold | AUTOPILOT VERTICAL HOLD | AutoPilot Vertical Speed Status | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotVerticalSpeedVar | AUTOPILOT VERTICAL HOLD VAR | AutoPilot Vertical Speed Value | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotWingLeveler | AUTOPILOT WING LEVELER | AutoPilot Wing Leveler | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotFlightDirectorCurrentBank | AUTOPILOT FLIGHT DIRECTOR BANK | Flight Director Current Bank | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotFlightDirectorCurrentPitch | AUTOPILOT FLIGHT DIRECTOR PITCH | Flight Director Current Pitch | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotPitchHold | AUTOPILOT PITCH HOLD | The status of Auto Pilot Pitch Hold button | | -| MSFSTouchPortalPlugin.AutoPilot.State.AutoPilotYawDampener | AUTOPILOT YAW DAMPER | Yaw Dampener Status | | +#### States + + **Base Id:** MSFSTouchPortalPlugin.AutoPilot.State. + +| Id | SimVar Name | Description | Unit | Format | DefaultValue | Settable | +| --- | --- | --- | --- | --- | --- | --- | +| AutoThrottleArm | AUTOPILOT THROTTLE ARM | Auto Throttle Armed | Bool | | | | +| AutoThrottleGoAround | AUTOPILOT TAKEOFF POWER ACTIVE | Auto Throttle GoAround | Bool | | | | +| AutoPilotAirSpeedHold | AUTOPILOT AIRSPEED HOLD | AutoPilot Air Speed Status | Bool | | | | +| AutoPilotAirSpeedVar | AUTOPILOT AIRSPEED HOLD VAR | AutoPilot Air Speed Value | knots | 0.0# | | | +| AutoPilotAltitudeHold | AUTOPILOT ALTITUDE LOCK | AutoPilot Altitude Status | Bool | | | | +| AutoPilotAltitudeVar | AUTOPILOT ALTITUDE LOCK VAR | AutoPilot Altitude Value | feet | | | ☑ | +| AutoPilotApproachHold | AUTOPILOT APPROACH HOLD | AutoPilot Approach Status | Bool | | | | +| AutoPilotAttitudeHold | AUTOPILOT ATTITUDE HOLD | AutoPilot Attitude Status | Bool | | | | +| AutoPilotAvailable | AUTOPILOT AVAILABLE | AutoPilot Availability | Bool | | | | +| AutoPilotBackCourseHold | AUTOPILOT BACKCOURSE HOLD | AutoPilot Back Course Status | Bool | | | | +| AutoPilotFlightDirector | AUTOPILOT FLIGHT DIRECTOR ACTIVE | AutoPilot Flight Director Status | Bool | | | | +| AutoPilotHeadingVar | AUTOPILOT HEADING LOCK DIR | AutoPilot Heading Direction | degrees | F0 | | ☑ | +| AutoPilotHeadingHold | AUTOPILOT HEADING LOCK | AutoPilot Heading Status | Bool | | | | +| AutoPilotMach | AUTOPILOT MACH HOLD | AutoPilot Mach Hold | Bool | | | | +| AutoPilotMachVar | AUTOPILOT MACH HOLD VAR | AutoPilot Mach Value | number | | | | +| AutoPilotMaster | AUTOPILOT MASTER | AutoPilot Master Status | Bool | | | | +| AutoPilotBanking | AUTOPILOT MAX BANK | AutoPilot Max Bank Angle | degrees | F2 | | | +| AutoPilotNavSelected | AUTOPILOT NAV SELECTED | AutoPilot Nav Selected Index | number | | | | +| AutoPilotNav1Hold | AUTOPILOT NAV1 LOCK | AutoPilot Nav1 Status | Bool | | | | +| AutoPilotAttitudeVar | AUTOPILOT PITCH HOLD REF | AutoPilot Pitch Reference Value | degrees | F2 | | | +| AutoPilotVerticalSpeedHold | AUTOPILOT VERTICAL HOLD | AutoPilot Vertical Speed Status | Bool | | | | +| AutoPilotVerticalSpeedVar | AUTOPILOT VERTICAL HOLD VAR | AutoPilot Vertical Speed Value | feet/minute | | | ☑ | +| AutoPilotWingLeveler | AUTOPILOT WING LEVELER | AutoPilot Wing Leveler | Bool | | | | +| AutoPilotFlightDirectorCurrentBank | AUTOPILOT FLIGHT DIRECTOR BANK | Flight Director Current Bank | degrees | F2 | | | +| AutoPilotFlightDirectorCurrentPitch | AUTOPILOT FLIGHT DIRECTOR PITCH | Flight Director Current Pitch | degrees | F2 | | | +| AutoPilotPitchHold | AUTOPILOT PITCH HOLD | The status of Auto Pilot Pitch Hold button | Bool | | | | +| AutoPilotYawDampener | AUTOPILOT YAW DAMPER | Yaw Dampener Status | Bool | | | |
    --- -## MSFS - Communication +### Communication
    Click to expand -### Actions +#### Actions @@ -273,32 +416,34 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac
    NameDescriptionFormatData
    index.   [type]     choices/default (in bold)
    Sim Event(s)On
    Hold
    -### States +#### States -| Id | SimVar Name | Description | DefaultValue | -| --- | --- | --- | --- | -| MSFSTouchPortalPlugin.Communication.State.Com1ActiveFrequency | COM ACTIVE FREQUENCY:1 | The frequency of the active COM1 radio | | -| MSFSTouchPortalPlugin.Communication.State.Com2ActiveFrequency | COM ACTIVE FREQUENCY:2 | The frequency of the active COM2 radio | | -| MSFSTouchPortalPlugin.Communication.State.Nav1ActiveFrequency | NAV ACTIVE FREQUENCY:1 | The frequency of the active NAV1 radio | | -| MSFSTouchPortalPlugin.Communication.State.Nav2ActiveFrequency | NAV ACTIVE FREQUENCY:2 | The frequency of the active NAV2 radio | | -| MSFSTouchPortalPlugin.Communication.State.Com1StandbyFrequency | COM STANDBY FREQUENCY:1 | The frequency of the standby COM1 radio | | -| MSFSTouchPortalPlugin.Communication.State.Com2StandbyFrequency | COM STANDBY FREQUENCY:2 | The frequency of the standby COM2 radio | | -| MSFSTouchPortalPlugin.Communication.State.Nav1StandbyFrequency | NAV STANDBY FREQUENCY:1 | The frequency of the standby NAV1 radio | | -| MSFSTouchPortalPlugin.Communication.State.Nav2StandbyFrequency | NAV STANDBY FREQUENCY:2 | The frequency of the standby NAV2 radio | | + **Base Id:** MSFSTouchPortalPlugin.Communication.State. + +| Id | SimVar Name | Description | Unit | Format | DefaultValue | Settable | +| --- | --- | --- | --- | --- | --- | --- | +| Com1ActiveFrequency | COM ACTIVE FREQUENCY:1 | The frequency of the active COM1 radio | MHz | 0.000# | | | +| Com2ActiveFrequency | COM ACTIVE FREQUENCY:2 | The frequency of the active COM2 radio | MHz | 0.000# | | | +| Nav1ActiveFrequency | NAV ACTIVE FREQUENCY:1 | The frequency of the active NAV1 radio | MHz | 0.000# | | | +| Nav2ActiveFrequency | NAV ACTIVE FREQUENCY:2 | The frequency of the active NAV2 radio | MHz | 0.000# | | | +| Com1StandbyFrequency | COM STANDBY FREQUENCY:1 | The frequency of the standby COM1 radio | MHz | 0.000# | | | +| Com2StandbyFrequency | COM STANDBY FREQUENCY:2 | The frequency of the standby COM2 radio | MHz | 0.000# | | | +| Nav1StandbyFrequency | NAV STANDBY FREQUENCY:1 | The frequency of the standby NAV1 radio | MHz | 0.000# | | | +| Nav2StandbyFrequency | NAV STANDBY FREQUENCY:2 | The frequency of the standby NAV2 radio | MHz | 0.000# | | |
    --- -## MSFS - Electrical +### Electrical
    Click to expand -### Actions +#### Actions - @@ -342,35 +487,37 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac
    NameDescriptionFormatData
    index.   [type]     choices/default (in bold)
    Sim Event(s)On
    Hold
    Alternator - SpecificToggle Specific AlternatorToggle Altenator - {0}
      +
    Alternator - SpecificToggle Specific AlternatorToggle Alternator - {0}
    1. [choice]   1, 2, 3, 4
    details
    1
    TOGGLE_ALTERNATOR1
    2
    TOGGLE_ALTERNATOR2
    3
    TOGGLE_ALTERNATOR3
    4
    TOGGLE_ALTERNATOR4
    -### States +#### States -| Id | SimVar Name | Description | DefaultValue | -| --- | --- | --- | --- | -| MSFSTouchPortalPlugin.Electrical.State.AvionicsMasterSwitch | AVIONICS MASTER SWITCH | Avionics Master Switch | | -| MSFSTouchPortalPlugin.Electrical.State.LightBeaconOn | LIGHT BEACON | Light Beacon Switch Status | | -| MSFSTouchPortalPlugin.Electrical.State.LightBrakeOn | LIGHT BRAKE ON | Light Brake Switch or Light Status | | -| MSFSTouchPortalPlugin.Electrical.State.LightCabinOn | LIGHT CABIN | Light Cabin Switch Status | | -| MSFSTouchPortalPlugin.Electrical.State.LightHeadOn | LIGHT HEAD ON | Light Head Switch or Light Status | | -| MSFSTouchPortalPlugin.Electrical.State.LightLandingOn | LIGHT LANDING | Light Landing Switch Status | | -| MSFSTouchPortalPlugin.Electrical.State.LightLogoOn | LIGHT LOGO | Light Logo Switch Status | | -| MSFSTouchPortalPlugin.Electrical.State.LightNavOn | LIGHT NAV | Light Nav Switch Status | | -| MSFSTouchPortalPlugin.Electrical.State.LightPanelOn | LIGHT PANEL | Light Panel Switch Status | | -| MSFSTouchPortalPlugin.Electrical.State.LightRecognitionOn | LIGHT RECOGNITION | Light Recognition Switch Status | | -| MSFSTouchPortalPlugin.Electrical.State.LightStrobeOn | LIGHT STROBE | Light Strobe Switch Status | | -| MSFSTouchPortalPlugin.Electrical.State.LightTaxiOn | LIGHT TAXI | Light Taxi Switch Status | | -| MSFSTouchPortalPlugin.Electrical.State.LightWingOn | LIGHT WING | Light Wing Switch Status | | -| MSFSTouchPortalPlugin.Electrical.State.MasterAlternator | GENERAL ENG MASTER ALTERNATOR:1 | Master Alternator Status | | -| MSFSTouchPortalPlugin.Electrical.State.MasterBattery | ELECTRICAL MASTER BATTERY | Master Battery Status | | + **Base Id:** MSFSTouchPortalPlugin.Electrical.State. + +| Id | SimVar Name | Description | Unit | Format | DefaultValue | Settable | +| --- | --- | --- | --- | --- | --- | --- | +| AvionicsMasterSwitch | AVIONICS MASTER SWITCH | Avionics Master Switch | Bool | | | | +| LightBeaconOn | LIGHT BEACON | Light Beacon Switch Status | Bool | | | | +| LightBrakeOn | LIGHT BRAKE ON | Light Brake Switch or Light Status | Bool | | | | +| LightCabinOn | LIGHT CABIN | Light Cabin Switch Status | Bool | | | | +| LightHeadOn | LIGHT HEAD ON | Light Head Switch or Light Status | Bool | | | | +| LightLandingOn | LIGHT LANDING | Light Landing Switch Status | Bool | | | | +| LightLogoOn | LIGHT LOGO | Light Logo Switch Status | Bool | | | | +| LightNavOn | LIGHT NAV | Light Nav Switch Status | Bool | | | | +| LightPanelOn | LIGHT PANEL | Light Panel Switch Status | Bool | | | | +| LightRecognitionOn | LIGHT RECOGNITION | Light Recognition Switch Status | Bool | | | | +| LightStrobeOn | LIGHT STROBE | Light Strobe Switch Status | Bool | | | | +| LightTaxiOn | LIGHT TAXI | Light Taxi Switch Status | Bool | | | | +| LightWingOn | LIGHT WING | Light Wing Switch Status | Bool | | | | +| MasterAlternator | GENERAL ENG MASTER ALTERNATOR:1 | Master Alternator Status | Bool | | | | +| MasterBattery | ELECTRICAL MASTER BATTERY | Master Battery Status | Bool | | | ☑ |
    --- -## MSFS - Engine +### Engine
    Click to expand -### Actions +#### Actions @@ -412,28 +559,28 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac - - + - - + - +
    NameDescriptionFormatData
    index.   [type]     choices/default (in bold)
    Sim Event(s)On
    Hold
    details
    All+Increment
    PROP_PITCH_INCR
    All+Increment Small
    PROP_PITCH_INCR_SMALL
    All+Decrement
    PROP_PITCH_DECR
    All+Decrement Small
    PROP_PITCH_DECR_SMALL
    All+Min (hi pitch)
    PROP_PITCH_HI
    All+Max (lo pitch)
    PROP_PITCH_LO
    All+Toggle Feather Switch
    TOGGLE_FEATHER_SWITCHES
    1+Increment
    PROP_PITCH1_INCR
    1+Increment Small
    PROP_PITCH1_INCR_SMALL
    1+Decrement
    PROP_PITCH1_DECR
    1+Decrement Small
    PROP_PITCH1_DECR_SMALL
    1+Min (hi pitch)
    PROP_PITCH1_HI
    1+Max (lo pitch)
    PROP_PITCH1_LO
    1+Toggle Feather Switch
    TOGGLE_FEATHER_SWITCH_1
    2+Increment
    PROP_PITCH2_INCR
    2+Increment Small
    PROP_PITCH2_INCR_SMALL
    2+Decrement
    PROP_PITCH2_DECR
    2+Decrement Small
    PROP_PITCH2_DECR_SMALL
    2+Min (hi pitch)
    PROP_PITCH2_HI
    2+Max (lo pitch)
    PROP_PITCH2_LO
    2+Toggle Feather Switch
    TOGGLE_FEATHER_SWITCH_2
    3+Increment
    PROP_PITCH3_INCR
    3+Increment Small
    PROP_PITCH3_INCR_SMALL
    3+Decrement
    PROP_PITCH3_DECR
    3+Decrement Small
    PROP_PITCH3_DECR_SMALL
    3+Min (hi pitch)
    PROP_PITCH3_HI
    3+Max (lo pitch)
    PROP_PITCH3_LO
    3+Toggle Feather Switch
    TOGGLE_FEATHER_SWITCH_3
    4+Increment
    PROP_PITCH4_INCR
    4+Increment Small
    PROP_PITCH4_INCR_SMALL
    4+Decrement
    PROP_PITCH4_DECR
    4+Decrement Small
    PROP_PITCH4_DECR_SMALL
    4+Min (hi pitch)
    PROP_PITCH4_HI
    4+Max (lo pitch)
    PROP_PITCH4_LO
    4+Toggle Feather Switch
    TOGGLE_FEATHER_SWITCH_4
    Propeller Pitch SetSets propeller pitch lever to valueSet Propeller {0} Pitch to {1} (0 to 16383)
      +
    Propeller Pitch SetSets propeller pitch lever to valueSet Propeller {0} Pitch to {1} (0 to 16384)
    1. [choice]   All, 1, 2, 3, 4
    2. -
    3. [text]   0   <min: 0> <max: 16383>
    4. +
    5. [text]   0   <min: 0> <max: 16384>
    details
    All
    PROP_PITCH_SET
    1
    PROP_PITCH1_SET
    2
    PROP_PITCH2_SET
    3
    PROP_PITCH3_SET
    4
    PROP_PITCH4_SET
    ThrottleSets all throttlesAll Throttles - {0}
    1. [choice]   Full, Increase, Increase Small, Decrease, Decrease Small, Cut, 10%, 20%, 30%, 40%, 50%, 60%, 70%, 80%, 90%
    details
    Full
    THROTTLE_FULL
    Increase
    THROTTLE_INCR
    Increase Small
    THROTTLE_INCR_SMALL
    Decrease
    THROTTLE_DECR
    Decrease Small
    THROTTLE_DECR_SMALL
    Cut
    THROTTLE_CUT
    10%
    THROTTLE_10
    20%
    THROTTLE_20
    30%
    THROTTLE_30
    40%
    THROTTLE_40
    50%
    THROTTLE_50
    60%
    THROTTLE_60
    70%
    THROTTLE_70
    80%
    THROTTLE_80
    90%
    THROTTLE_90
    Throttle SetSets all or specific Throttle(s) to specific valueSet Throttle {0} to {1} (-16383 - +16383)
      +
    Throttle SetSets all or specific Throttle(s) to specific valueSet Throttle {0} to {1} (-16384 to +16384)
    1. [choice]   All, 1, 2, 3, 4
    2. -
    3. [text]   0   <min: -16383> <max: 16383>
    4. +
    5. [text]   0   <min: -16384> <max: 16384>
    details
    All
    THROTTLE_SET
    1
    THROTTLE1_SET
    2
    THROTTLE2_SET
    3
    THROTTLE3_SET
    4
    THROTTLE4_SET
    Throttle SpecificSets Throttle on specific engineThrottle {0} - {1}
    1. [choice]   1, 2, 3, 4
    2. [choice]   Full, Increase, Increase Small, Decrease, Decrease Small, Cut
    details
    1+Full
    THROTTLE1_FULL
    1+Increase
    THROTTLE1_INCR
    1+Increase Small
    THROTTLE1_INCR_SMALL
    1+Descrease
    THROTTLE1_DECR
    1+Descrease Small
    THROTTLE1_DECR_SMALL
    1+Cut
    THROTTLE1_CUT
    2+Full
    THROTTLE2_FULL
    2+Increase
    THROTTLE2_INCR
    2+Increase Small
    THROTTLE2_INCR_SMALL
    2+Descrease
    THROTTLE2_DECR
    2+Descrease Small
    THROTTLE2_DECR_SMALL
    2+Cut
    THROTTLE2_CUT
    3+Full
    THROTTLE3_FULL
    3+Increase
    THROTTLE3_INCR
    3+Increase Small
    THROTTLE3_INCR_SMALL
    3+Descrease
    THROTTLE3_DECR
    3+Descrease Small
    THROTTLE3_DECR_SMALL
    3+Cut
    THROTTLE3_CUT
    4+Full
    THROTTLE4_FULL
    4+Increase
    THROTTLE4_INCR
    4+Increase Small
    THROTTLE4_INCR_SMALL
    4+Descrease
    THROTTLE4_DECR
    4+Descrease Small
    THROTTLE4_DECR_SMALL
    4+Cut
    THROTTLE4_CUT
    details
    1+Full
    THROTTLE1_FULL
    1+Increase
    THROTTLE1_INCR
    1+Increase Small
    THROTTLE1_INCR_SMALL
    1+Decrease
    THROTTLE1_DECR
    1+Decrease Small
    THROTTLE1_DECR_SMALL
    1+Cut
    THROTTLE1_CUT
    2+Full
    THROTTLE2_FULL
    2+Increase
    THROTTLE2_INCR
    2+Increase Small
    THROTTLE2_INCR_SMALL
    2+Decrease
    THROTTLE2_DECR
    2+Decrease Small
    THROTTLE2_DECR_SMALL
    2+Cut
    THROTTLE2_CUT
    3+Full
    THROTTLE3_FULL
    3+Increase
    THROTTLE3_INCR
    3+Increase Small
    THROTTLE3_INCR_SMALL
    3+Decrease
    THROTTLE3_DECR
    3+Decrease Small
    THROTTLE3_DECR_SMALL
    3+Cut
    THROTTLE3_CUT
    4+Full
    THROTTLE4_FULL
    4+Increase
    THROTTLE4_INCR
    4+Increase Small
    THROTTLE4_INCR_SMALL
    4+Decrease
    THROTTLE4_DECR
    4+Decrease Small
    THROTTLE4_DECR_SMALL
    4+Cut
    THROTTLE4_CUT
    Toggle All MagnetosToggle All MagnetosToggle All Magnetos - {0}
    1. [choice]   Start, Off, Right, Left, Both, Decrease, Increase, Select (for +/-)
    2. @@ -448,49 +595,51 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac
    -### States - -| Id | SimVar Name | Description | DefaultValue | -| --- | --- | --- | --- | -| MSFSTouchPortalPlugin.Engine.State.MasterIgnitionSwitch | MASTER IGNITION SWITCH | Master Ignition Switch Status | | -| MSFSTouchPortalPlugin.Engine.State.MixtureEngine1 | GENERAL ENG MIXTURE LEVER POSITION:1 | Mixture - Engine 1 - Percentage | | -| MSFSTouchPortalPlugin.Engine.State.MixtureEngine2 | GENERAL ENG MIXTURE LEVER POSITION:2 | Mixture - Engine 2 - Percentage | | -| MSFSTouchPortalPlugin.Engine.State.MixtureEngine3 | GENERAL ENG MIXTURE LEVER POSITION:3 | Mixture - Engine 3 - Percentage | | -| MSFSTouchPortalPlugin.Engine.State.MixtureEngine4 | GENERAL ENG MIXTURE LEVER POSITION:4 | Mixture - Engine 4 - Percentage | | -| MSFSTouchPortalPlugin.Engine.State.Propeller1FeatherSw | PROP FEATHER SWITCH:1 | Propeller - Engine 1 - Feather Switch State (bool) | | -| MSFSTouchPortalPlugin.Engine.State.Propeller1Feathered | PROP FEATHERED:1 | Propeller - Engine 1 - Feathered (bool) | | -| MSFSTouchPortalPlugin.Engine.State.PropellerEngine1 | GENERAL ENG PROPELLER LEVER POSITION:1 | Propeller - Engine 1 - Percentage | | -| MSFSTouchPortalPlugin.Engine.State.Propeller2FeatherSw | PROP FEATHER SWITCH:2 | Propeller - Engine 2 - Feather Switch State (bool) | | -| MSFSTouchPortalPlugin.Engine.State.Propeller2Feathered | PROP FEATHERED:2 | Propeller - Engine 2 - Feathered (bool) | | -| MSFSTouchPortalPlugin.Engine.State.PropellerEngine2 | GENERAL ENG PROPELLER LEVER POSITION:2 | Propeller - Engine 2 - Percentage | | -| MSFSTouchPortalPlugin.Engine.State.Propeller3FeatherSw | PROP FEATHER SWITCH:3 | Propeller - Engine 3 - Feather Switch State (bool) | | -| MSFSTouchPortalPlugin.Engine.State.Propeller3Feathered | PROP FEATHERED:3 | Propeller - Engine 3 - Feathered (bool) | | -| MSFSTouchPortalPlugin.Engine.State.PropellerEngine3 | GENERAL ENG PROPELLER LEVER POSITION:3 | Propeller - Engine 3 - Percentage | | -| MSFSTouchPortalPlugin.Engine.State.Propeller4FeatherSw | PROP FEATHER SWITCH:4 | Propeller - Engine 4 - Feather Switch State (bool) | | -| MSFSTouchPortalPlugin.Engine.State.Propeller4Feathered | PROP FEATHERED:4 | Propeller - Engine 4 - Feathered (bool) | | -| MSFSTouchPortalPlugin.Engine.State.PropellerEngine4 | GENERAL ENG PROPELLER LEVER POSITION:4 | Propeller - Engine 4 - Percentage | | -| MSFSTouchPortalPlugin.Engine.State.RPMN1Engine1 | ENG N1 RPM:1 | RPM - Engine 1 | | -| MSFSTouchPortalPlugin.Engine.State.RPMN1Engine2 | ENG N1 RPM:2 | RPM - Engine 2 | | -| MSFSTouchPortalPlugin.Engine.State.RPMN1Engine3 | ENG N1 RPM:3 | RPM - Engine 3 | | -| MSFSTouchPortalPlugin.Engine.State.RPMN1Engine4 | ENG N1 RPM:4 | RPM - Engine 4 | | -| MSFSTouchPortalPlugin.Engine.State.RPMPropeller1 | PROP RPM:1 | RPM - Propeller 1 | | -| MSFSTouchPortalPlugin.Engine.State.RPMPropeller2 | PROP RPM:2 | RPM - Propeller 2 | | -| MSFSTouchPortalPlugin.Engine.State.RPMPropeller3 | PROP RPM:3 | RPM - Propeller 3 | | -| MSFSTouchPortalPlugin.Engine.State.RPMPropeller4 | PROP RPM:4 | RPM - Propeller 4 | | -| MSFSTouchPortalPlugin.Engine.State.ThrottleEngine1 | GENERAL ENG THROTTLE LEVER POSITION:1 | Throttle - Engine 1 - Percentage | | -| MSFSTouchPortalPlugin.Engine.State.ThrottleEngine2 | GENERAL ENG THROTTLE LEVER POSITION:2 | Throttle - Engine 2 - Percentage | | -| MSFSTouchPortalPlugin.Engine.State.ThrottleEngine3 | GENERAL ENG THROTTLE LEVER POSITION:3 | Throttle - Engine 3 - Percentage | | -| MSFSTouchPortalPlugin.Engine.State.ThrottleEngine4 | GENERAL ENG THROTTLE LEVER POSITION:4 | Throttle - Engine 4 - Percentage | | +#### States + + **Base Id:** MSFSTouchPortalPlugin.Engine.State. + +| Id | SimVar Name | Description | Unit | Format | DefaultValue | Settable | +| --- | --- | --- | --- | --- | --- | --- | +| MasterIgnitionSwitch | MASTER IGNITION SWITCH | Master Ignition Switch Status | Bool | | | | +| MixtureEngine1 | GENERAL ENG MIXTURE LEVER POSITION:1 | Mixture - Engine 1 - Percentage | percent | 0.0# | | ☑ | +| MixtureEngine2 | GENERAL ENG MIXTURE LEVER POSITION:2 | Mixture - Engine 2 - Percentage | percent | 0.0# | | ☑ | +| MixtureEngine3 | GENERAL ENG MIXTURE LEVER POSITION:3 | Mixture - Engine 3 - Percentage | percent | 0.0# | | ☑ | +| MixtureEngine4 | GENERAL ENG MIXTURE LEVER POSITION:4 | Mixture - Engine 4 - Percentage | percent | 0.0# | | ☑ | +| Propeller1FeatherSw | PROP FEATHER SWITCH:1 | Propeller - Engine 1 - Feather Switch State (bool) | Bool | | | | +| Propeller1Feathered | PROP FEATHERED:1 | Propeller - Engine 1 - Feathered (bool) | Bool | | | | +| PropellerEngine1 | GENERAL ENG PROPELLER LEVER POSITION:1 | Propeller - Engine 1 - Percentage | percent | 0.0# | | ☑ | +| Propeller2FeatherSw | PROP FEATHER SWITCH:2 | Propeller - Engine 2 - Feather Switch State (bool) | Bool | | | | +| Propeller2Feathered | PROP FEATHERED:2 | Propeller - Engine 2 - Feathered (bool) | Bool | | | | +| PropellerEngine2 | GENERAL ENG PROPELLER LEVER POSITION:2 | Propeller - Engine 2 - Percentage | percent | 0.0# | | ☑ | +| Propeller3FeatherSw | PROP FEATHER SWITCH:3 | Propeller - Engine 3 - Feather Switch State (bool) | Bool | | | | +| Propeller3Feathered | PROP FEATHERED:3 | Propeller - Engine 3 - Feathered (bool) | Bool | | | | +| PropellerEngine3 | GENERAL ENG PROPELLER LEVER POSITION:3 | Propeller - Engine 3 - Percentage | percent | 0.0# | | ☑ | +| Propeller4FeatherSw | PROP FEATHER SWITCH:4 | Propeller - Engine 4 - Feather Switch State (bool) | Bool | | | | +| Propeller4Feathered | PROP FEATHERED:4 | Propeller - Engine 4 - Feathered (bool) | Bool | | | | +| PropellerEngine4 | GENERAL ENG PROPELLER LEVER POSITION:4 | Propeller - Engine 4 - Percentage | percent | 0.0# | | ☑ | +| RPMN1Engine1 | ENG N1 RPM:1 | RPM - Engine 1 | percent | 0.0# | | | +| RPMN1Engine2 | ENG N1 RPM:2 | RPM - Engine 2 | percent | 0.0# | | | +| RPMN1Engine3 | ENG N1 RPM:3 | RPM - Engine 3 | percent | 0.0# | | | +| RPMN1Engine4 | ENG N1 RPM:4 | RPM - Engine 4 | percent | 0.0# | | | +| RPMPropeller1 | PROP RPM:1 | RPM - Propeller 1 | rpm | 0.0# | | ☑ | +| RPMPropeller2 | PROP RPM:2 | RPM - Propeller 2 | rpm | 0.0# | | ☑ | +| RPMPropeller3 | PROP RPM:3 | RPM - Propeller 3 | rpm | 0.0# | | ☑ | +| RPMPropeller4 | PROP RPM:4 | RPM - Propeller 4 | rpm | 0.0# | | ☑ | +| ThrottleEngine1 | GENERAL ENG THROTTLE LEVER POSITION:1 | Throttle - Engine 1 - Percentage | percent | 0.# | | ☑ | +| ThrottleEngine2 | GENERAL ENG THROTTLE LEVER POSITION:2 | Throttle - Engine 2 - Percentage | percent | 0.# | | ☑ | +| ThrottleEngine3 | GENERAL ENG THROTTLE LEVER POSITION:3 | Throttle - Engine 3 - Percentage | percent | 0.# | | ☑ | +| ThrottleEngine4 | GENERAL ENG THROTTLE LEVER POSITION:4 | Throttle - Engine 4 - Percentage | percent | 0.# | | ☑ |
    --- -## MSFS - Environment +### Environment
    Click to expand -### Actions +#### Actions @@ -526,36 +675,38 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac
    NameDescriptionFormatData
    index.   [type]     choices/default (in bold)
    Sim Event(s)On
    Hold
    -### States +#### States -| Id | SimVar Name | Description | DefaultValue | -| --- | --- | --- | --- | -| MSFSTouchPortalPlugin.Environment.State.AntiIceEng1 | GENERAL ENG ANTI ICE POSITION:1 | Anti-Ice Engine 1 | | -| MSFSTouchPortalPlugin.Environment.State.AntiIceEng2 | GENERAL ENG ANTI ICE POSITION:2 | Anti-Ice Engine 2 | | -| MSFSTouchPortalPlugin.Environment.State.AntiIceEng3 | GENERAL ENG ANTI ICE POSITION:3 | Anti-Ice Engine 3 | | -| MSFSTouchPortalPlugin.Environment.State.AntiIceEng4 | GENERAL ENG ANTI ICE POSITION:4 | Anti-Ice Engine 4 | | -| MSFSTouchPortalPlugin.Environment.State.AntiIcePanelSwitch | PANEL ANTI ICE SWITCH | Panel Anti-Ice Switch | | -| MSFSTouchPortalPlugin.Environment.State.PitotHeat | PITOT HEAT | Pitot Heat Status | | -| MSFSTouchPortalPlugin.Environment.State.PitotHeatSwitch1 | PITOT HEAT SWITCH:1 | Pitot Heat Switch 1 State (0=Off; 1=On; 2=Auto) | | -| MSFSTouchPortalPlugin.Environment.State.PitotHeatSwitch2 | PITOT HEAT SWITCH:2 | Pitot Heat Switch 2 State (0=Off; 1=On; 2=Auto) | | -| MSFSTouchPortalPlugin.Environment.State.PitotHeatSwitch3 | PITOT HEAT SWITCH:3 | Pitot Heat Switch 3 State (0=Off; 1=On; 2=Auto) | | -| MSFSTouchPortalPlugin.Environment.State.PitotHeatSwitch4 | PITOT HEAT SWITCH:4 | Pitot Heat Switch 4 State (0=Off; 1=On; 2=Auto) | | -| MSFSTouchPortalPlugin.Environment.State.AntiIcePropeller1Switch | PROP DEICE SWITCH:1 | Propeller 1 Deice Switch | | -| MSFSTouchPortalPlugin.Environment.State.AntiIcePropeller2Switch | PROP DEICE SWITCH:2 | Propeller 2 Deice Switch | | -| MSFSTouchPortalPlugin.Environment.State.AntiIcePropeller3Switch | PROP DEICE SWITCH:3 | Propeller 3 Deice Switch | | -| MSFSTouchPortalPlugin.Environment.State.AntiIcePropeller4Switch | PROP DEICE SWITCH:4 | Propeller 4 Deice Switch | | -| MSFSTouchPortalPlugin.Environment.State.AntiIceStructuralSwitch | STRUCTURAL DEICE SWITCH | Structural Deice Switch | | -| MSFSTouchPortalPlugin.Environment.State.AntiIceWindshieldSwitch | WINDSHIELD DEICE SWITCH | Windshield Deice Switch | | + **Base Id:** MSFSTouchPortalPlugin.Environment.State. + +| Id | SimVar Name | Description | Unit | Format | DefaultValue | Settable | +| --- | --- | --- | --- | --- | --- | --- | +| AntiIceEng1 | GENERAL ENG ANTI ICE POSITION:1 | Anti-Ice Engine 1 | Bool | | | | +| AntiIceEng2 | GENERAL ENG ANTI ICE POSITION:2 | Anti-Ice Engine 2 | Bool | | | | +| AntiIceEng3 | GENERAL ENG ANTI ICE POSITION:3 | Anti-Ice Engine 3 | Bool | | | | +| AntiIceEng4 | GENERAL ENG ANTI ICE POSITION:4 | Anti-Ice Engine 4 | Bool | | | | +| AntiIcePanelSwitch | PANEL ANTI ICE SWITCH | Panel Anti-Ice Switch | Bool | | | | +| PitotHeat | PITOT HEAT | Pitot Heat Status | Bool | | | | +| PitotHeatSwitch1 | PITOT HEAT SWITCH:1 | Pitot Heat Switch 1 State (0=Off; 1=On; 2=Auto) | Enum | | | | +| PitotHeatSwitch2 | PITOT HEAT SWITCH:2 | Pitot Heat Switch 2 State (0=Off; 1=On; 2=Auto) | Enum | | | | +| PitotHeatSwitch3 | PITOT HEAT SWITCH:3 | Pitot Heat Switch 3 State (0=Off; 1=On; 2=Auto) | Enum | | | | +| PitotHeatSwitch4 | PITOT HEAT SWITCH:4 | Pitot Heat Switch 4 State (0=Off; 1=On; 2=Auto) | Enum | | | | +| AntiIcePropeller1Switch | PROP DEICE SWITCH:1 | Propeller 1 Deice Switch | Bool | | | | +| AntiIcePropeller2Switch | PROP DEICE SWITCH:2 | Propeller 2 Deice Switch | Bool | | | | +| AntiIcePropeller3Switch | PROP DEICE SWITCH:3 | Propeller 3 Deice Switch | Bool | | | | +| AntiIcePropeller4Switch | PROP DEICE SWITCH:4 | Propeller 4 Deice Switch | Bool | | | | +| AntiIceStructuralSwitch | STRUCTURAL DEICE SWITCH | Structural Deice Switch | Bool | | | | +| AntiIceWindshieldSwitch | WINDSHIELD DEICE SWITCH | Windshield Deice Switch | Bool | | | |
    --- -## MSFS - Failures +### Failures
    Click to expand -### Actions +#### Actions @@ -571,38 +722,40 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac --- -## MSFS - Flight Instruments +### Flight Instruments
    Click to expand -### States - -| Id | SimVar Name | Description | DefaultValue | -| --- | --- | --- | --- | -| MSFSTouchPortalPlugin.FlightInstruments.State.AirSpeedIndicated | AIRSPEED INDICATED | Air speed indicated in Knots | | -| MSFSTouchPortalPlugin.FlightInstruments.State.AirSpeedMach | AIRSPEED MACH | Air speed indicated in mach | | -| MSFSTouchPortalPlugin.FlightInstruments.State.AirSpeedTrue | AIRSPEED TRUE | Air speed true in Knots | | -| MSFSTouchPortalPlugin.FlightInstruments.State.FlapSpeedExceeeded | FLAP SPEED EXCEEDED | Flap Speed Exceeded Warning true/false | | -| MSFSTouchPortalPlugin.FlightInstruments.State.GroundAltitude | GROUND ALTITUDE | Ground level in Feet | | -| MSFSTouchPortalPlugin.FlightInstruments.State.GroundVelocity | GROUND VELOCITY | Ground Speed in Knots | | -| MSFSTouchPortalPlugin.FlightInstruments.State.OverspeedWarning | OVERSPEED WARNING | Overspeed Warning true/false | | -| MSFSTouchPortalPlugin.FlightInstruments.State.PlaneAltitudeAGL | PLANE ALT ABOVE GROUND | Plane Altitude AGL in Feet | | -| MSFSTouchPortalPlugin.FlightInstruments.State.PlaneAltitude | PLANE ALTITUDE | Plane Altitude in Feet | | -| MSFSTouchPortalPlugin.FlightInstruments.State.PlaneBankAngle | PLANE BANK DEGREES | Plane Bank Angle in Degrees | | -| MSFSTouchPortalPlugin.FlightInstruments.State.PlaneHeadingMagnetic | PLANE HEADING DEGREES MAGNETIC | Plane Heading (Magnetic North) in Degrees | | -| MSFSTouchPortalPlugin.FlightInstruments.State.PlaneHeadingTrue | PLANE HEADING DEGREES TRUE | Plane Heading (True North) in Degrees | | -| MSFSTouchPortalPlugin.FlightInstruments.State.PlanePitchAngle | PLANE PITCH DEGREES | Plane Pitch Angle in Degrees | | -| MSFSTouchPortalPlugin.FlightInstruments.State.StallWarning | STALL WARNING | Stall Warning true/false | | -| MSFSTouchPortalPlugin.FlightInstruments.State.VerticalSpeed | VERTICAL SPEED | Vertical Speed in feet per minute | | +#### States + + **Base Id:** MSFSTouchPortalPlugin.FlightInstruments.State. + +| Id | SimVar Name | Description | Unit | Format | DefaultValue | Settable | +| --- | --- | --- | --- | --- | --- | --- | +| AirSpeedIndicated | AIRSPEED INDICATED | Air speed indicated in Knots | knots | 0.0# | | ☑ | +| AirSpeedMach | AIRSPEED MACH | Air speed indicated in Mach | mach | 0.0# | | | +| AirSpeedTrue | AIRSPEED TRUE | Air speed true in Knots | knots | 0.0# | | ☑ | +| FlapSpeedExceeeded | FLAP SPEED EXCEEDED | Flap Speed Exceeded Warning true/false | Bool | | | | +| GroundAltitude | GROUND ALTITUDE | Ground level in Feet | feet | 0.# | | | +| GroundVelocity | GROUND VELOCITY | Ground Speed in Knots | knots | 0.0# | | | +| OverspeedWarning | OVERSPEED WARNING | Overspeed Warning true/false | Bool | | | | +| PlaneAltitudeAGL | PLANE ALT ABOVE GROUND | Plane Altitude AGL in Feet | feet | 0.# | | ☑ | +| PlaneAltitude | PLANE ALTITUDE | Plane Altitude in Feet | feet | 0.# | | ☑ | +| PlaneBankAngle | PLANE BANK DEGREES | Plane Bank Angle in Degrees | degrees | 0 | | ☑ | +| PlaneHeadingMagnetic | PLANE HEADING DEGREES MAGNETIC | Plane Heading (Magnetic North) in Degrees | degrees | 0 | | ☑ | +| PlaneHeadingTrue | PLANE HEADING DEGREES TRUE | Plane Heading (True North) in Degrees | degrees | 0 | | ☑ | +| PlanePitchAngle | PLANE PITCH DEGREES | Plane Pitch Angle in Degrees | degrees | 0 | | ☑ | +| StallWarning | STALL WARNING | Stall Warning true/false | Bool | | | | +| VerticalSpeed | VERTICAL SPEED | Vertical Speed in feet per minute | feet/minute | 0.0# | | ☑ |
    --- -## MSFS - Flight Systems +### Flight Systems
    Click to expand -### Actions +#### Actions
    NameDescriptionFormatData
    index.   [type]     choices/default (in bold)
    Sim Event(s)On
    Hold
    @@ -615,17 +768,17 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac
  • [text]   0   <min: -100> <max: 100>
  • - + - - + @@ -661,31 +814,31 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac - - + - - + - - + @@ -696,11 +849,11 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac - - + @@ -710,7 +863,7 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac
  • [text]   0   <min: -100> <max: 100>
  • - + @@ -721,11 +874,11 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac - - + @@ -733,40 +886,48 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac
    NameDescriptionFormatData
    index.   [type]     choices/default (in bold)
    Sim Event(s)On
    Hold
    AILERON_TRIM_SET
    AileronsAileronsAilerons - {0}
    1. [choice]   Center, Left, Right
    details
    Center
    CENTER_AILER_RUDDER
    Left
    AILERONS_LEFT
    Right
    AILERONS_RIGHT
    Ailerons Set Set AileronsAilerons set to {0} (-16383 - +16383)
      -
    1. [text]   0   <min: -16383> <max: 16383>
    2. +
    Ailerons Set Set AileronsAilerons set to {0} (-16384 to +16384)
      +
    1. [text]   0   <min: -16384> <max: 16384>
    AILERON_SET
    BrakesBrakesBrakes - {0}
    1. [choice]   All, Left, Right
    Up
    ELEV_UP
    Down
    ELEV_DOWN
    Elevator Set Set ElevatorElevator set to {0} (-16383 - +16383)
      -
    1. [text]   0   <min: -16383> <max: 16383>
    2. +
    Elevator Set Set ElevatorElevator set to {0} (-16384 to +16384)
      +
    1. [text]   0   <min: -16384> <max: 16384>
    ELEVATOR_SET
    Elevator TrimElevator TrimElevator Trim - {0}
    1. [choice]   Up, Down
    Down
    ELEV_TRIM_DN
    Up
    ELEV_TRIM_UP
    Elevator Trim Set Set Elevator TrimElevator Trim set to {0} (-16383 - +16383)
      -
    1. [text]   0   <min: -16383> <max: 16383>
    2. +
    Elevator Trim Set Set Elevator TrimElevator Trim set to {0} (-16384 to +16384)
      +
    1. [text]   0   <min: -16384> <max: 16384>
    ELEVATOR_TRIM_SET
    FlapsFlapsFlaps - {0}
    1. [choice]   Up, Down, Increase, Decrease, 1, 2, 3, 4
    details
    Up
    FLAPS_UP
    Down
    FLAPS_DOWN
    Increase
    FLAPS_INCR
    Decrease
    FLAPS_DECR
    1
    FLAPS_1
    2
    FLAPS_2
    3
    FLAPS_3
    4
    FLAPS_3
    Flaps Set Set FlapsFlaps set to {0} (0 - +16383)
      -
    1. [text]   0   <min: 0> <max: 16383>
    2. +
    Flaps Set Set FlapsFlaps set to {0} (0 to 16384)
      +
    1. [text]   0   <min: 0> <max: 16384>
    FLAPS_SET
    Gear ManipulationGear ManipulationGear - {0}
    1. [choice]   Toggle, Up, Down, Pump
    details
    Center
    RUDDER_CENTER
    Left
    RUDDER_LEFT
    Right
    RUDDER_RIGHT
    Rudder Set Set RudderRudder set to {0} (-16383 - +16383)
      -
    1. [text]   0   <min: -16383> <max: 16383>
    2. +
    Rudder Set Set RudderRudder set to {0} (-16384 to +16384)
      +
    1. [text]   0   <min: -16384> <max: 16384>
    RUDDER_SET
    Rudder TrimRudder TrimRudder Trim - {0}
    1. [choice]   Left, Right
    RUDDER_TRIM_SET
    SpoilersSpoilersSpoilers - {0}
    1. [choice]   Toggle, On, Off
    details
    Toggle
    SPOILERS_ARM_TOGGLE
    On
    SPOILERS_ARM_ON
    Off
    SPOILERS_ARM_OFF
    Spoilers Set Set SpoilersSpoilers set to {0} (0- +16383)
      -
    1. [text]   0   <min: 0> <max: 16383>
    2. +
    Spoilers Set Set SpoilersSet Spoilers handle position to {0} (0 to 16384)
      +
    1. [text]   0   <min: 0> <max: 16384>
    SPOILERS_SET
    Toggle Parking BrakeToggle Parking BrakeToggle Parking Brake
    PARKING_BRAKES
    -### States - -| Id | SimVar Name | Description | DefaultValue | -| --- | --- | --- | --- | -| MSFSTouchPortalPlugin.FlightSystems.State.AileronTrim | AILERON TRIM | Aileron Trim Angle | | -| MSFSTouchPortalPlugin.FlightSystems.State.AileronTrimPct | AILERON TRIM PCT | Aileron Trim Percent | 0 | -| MSFSTouchPortalPlugin.FlightSystems.State.CowlFlaps1Percent | RECIP ENG COWL FLAP POSITION:1 | Cowl Flaps 1 Opened Percentage | | -| MSFSTouchPortalPlugin.FlightSystems.State.CowlFlaps2Percent | RECIP ENG COWL FLAP POSITION:2 | Cowl Flaps 2 Opened Percentage | | -| MSFSTouchPortalPlugin.FlightSystems.State.CowlFlaps3Percent | RECIP ENG COWL FLAP POSITION:3 | Cowl Flaps 3 Opened Percentage | | -| MSFSTouchPortalPlugin.FlightSystems.State.CowlFlaps4Percent | RECIP ENG COWL FLAP POSITION:4 | Cowl Flaps 4 Opened Percentage | | -| MSFSTouchPortalPlugin.FlightSystems.State.ElevatorTrim | ELEVATOR TRIM POSITION | Elevator Trim Angle | | -| MSFSTouchPortalPlugin.FlightSystems.State.ElevatorTrimPct | ELEVATOR TRIM PCT | Elevator Trim Percent | 0 | -| MSFSTouchPortalPlugin.FlightSystems.State.FlapsHandlePercent | FLAPS HANDLE PERCENT | Flaps Handle Percentage | | -| MSFSTouchPortalPlugin.FlightSystems.State.ParkingBrakeIndicator | BRAKE PARKING POSITION | Parking Brake Indicator true/false | | -| MSFSTouchPortalPlugin.FlightSystems.State.RudderTrim | RUDDER TRIM | Rudder Trim Angle | | -| MSFSTouchPortalPlugin.FlightSystems.State.RudderTrimPct | RUDDER TRIM PCT | Rudder Trim Percent | 0 | -| MSFSTouchPortalPlugin.FlightSystems.State.GearTotalExtended | GEAR TOTAL PCT EXTENDED | Total percentage of gear extended | | +#### States + + **Base Id:** MSFSTouchPortalPlugin.FlightSystems.State. + +| Id | SimVar Name | Description | Unit | Format | DefaultValue | Settable | +| --- | --- | --- | --- | --- | --- | --- | +| AileronTrim | AILERON TRIM | Aileron Trim Angle | degrees | F2 | | | +| AileronTrimPct | AILERON TRIM PCT | Aileron Trim Percent | percent | F1 | 0| ☑ | +| CowlFlaps1Percent | RECIP ENG COWL FLAP POSITION:1 | Cowl Flaps 1 Opened Percentage | percent | 0.0# | | ☑ | +| CowlFlaps2Percent | RECIP ENG COWL FLAP POSITION:2 | Cowl Flaps 2 Opened Percentage | percent | 0.0# | | ☑ | +| CowlFlaps3Percent | RECIP ENG COWL FLAP POSITION:3 | Cowl Flaps 3 Opened Percentage | percent | 0.0# | | ☑ | +| CowlFlaps4Percent | RECIP ENG COWL FLAP POSITION:4 | Cowl Flaps 4 Opened Percentage | percent | 0.0# | | ☑ | +| ElevatorTrim | ELEVATOR TRIM POSITION | Elevator Trim Angle | degrees | F2 | | ☑ | +| ElevatorTrimPct | ELEVATOR TRIM PCT | Elevator Trim Percent | percent | F1 | 0| | +| FlapsHandlePercent | FLAPS HANDLE PERCENT | Flaps Handle Percentage | percent | 0.0# | | | +| ParkingBrakeIndicator | BRAKE PARKING POSITION | Parking Brake Indicator true/false | Bool | | | | +| RudderTrim | RUDDER TRIM | Rudder Trim Angle | degrees | F2 | | | +| RudderTrimPct | RUDDER TRIM PCT | Rudder Trim Percent | percent | F1 | 0| ☑ | +| SpoilersArmed | SPOILERS ARMED | Spoilers Armed (0/1) | Bool | | 0| | +| SpoilersAvailable | SPOILER AVAILABLE | Spoilers Available (0/1) | Bool | | 0| | +| SpoilersHandlePosition | SPOILERS HANDLE POSITION | Spoilers Handle Position (0 - 16384) | position 16k | | 0| ☑ | +| SpoilersLeftPosition | SPOILERS LEFT POSITION | Spoilers Left Position Percent | percent | F1 | 0| | +| SpoilersRightPosition | SPOILERS RIGHT POSITION | Spoilers Right Position Percent | percent | F1 | 0| | +| GearTotalExtended | GEAR TOTAL PCT EXTENDED | Total percentage of gear extended | percent | F0 | | |
    --- -## MSFS - Fuel +### Fuel
    Click to expand -### Actions +#### Actions - - + @@ -808,43 +969,10 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac --- -## MSFS - Plugin -
    Click to expand - -### Actions - -
    NameDescriptionFormatData
    index.   [type]     choices/default (in bold)
    Sim Event(s)On
    Hold
    Add FuelAdds 25% amount of FuelAdd 25% amount of fuel
      +
    Add FuelAdd Fuel (1 to 65535 or zero for 25% of capacity)Add {0} amount of Fuel
      +
    1. [text]   0   <min: 0> <max: 65535>
    ADD_FUEL_QUANTITY
    Electric Fuel Pump - ToggleToggles the Electric Fuel PumpToggle Electric Fuel Pump - {0}
    1. [choice]   All, 1, 2, 3, 4
    - - - - - - - -
    NameDescriptionFormatData
    index.   [type]     choices/default (in bold)
    Sim Event(s)On
    Hold
    Action Repeat IntervalHeld Action Repeat Rate (ms)Repeat Interval: {0} to/by: {1} ms
      -
    1. [choice]   Set, Increment, Decrement
    2. -
    3. [text]   450   <min: 50> <max: 2147483647>
    4. -
    details
    Set
    ActionRepeatIntervalSet
    Increment
    ActionRepeatIntervalInc
    Decrement
    ActionRepeatIntervalDec
    ConnectionToggle/On/Off SimConnect ConnectionSimConnect Connection - {0}
      -
    1. [choice]   Toggle, On, Off
    2. -
    details
    Toggle
    ToggleConnection
    On
    Connect
    Off
    Disconnect
    - - -### States - -| Id | SimVar Name | Description | DefaultValue | -| --- | --- | --- | --- | -| MSFSTouchPortalPlugin.Plugin.State.ActionRepeatInterval | | The current Held Action Repeat Rate (ms) | 450 | -| MSFSTouchPortalPlugin.Plugin.State.Connected | | The status of SimConnect (true/false/connecting) | false | - - -
    - ---- - -## MSFS - System +### System
    Click to expand -### Actions +#### Actions @@ -861,17 +989,55 @@ Stores the held action repeat rate, which can be set via the 'MSFS - Plugin - Ac
    NameDescriptionFormatData
    index.   [type]     choices/default (in bold)
    Sim Event(s)On
    Hold
    -### States +#### Events -| Id | SimVar Name | Description | DefaultValue | -| --- | --- | --- | --- | -| MSFSTouchPortalPlugin.SimSystem.State.AtcId | ATC ID | Aircraft Id used by ATC | | -| MSFSTouchPortalPlugin.SimSystem.State.AircraftTitle | TITLE | Aircraft Title | | -| MSFSTouchPortalPlugin.SimSystem.State.AtcAirline | ATC AIRLINE | Airline used by ATC | | -| MSFSTouchPortalPlugin.SimSystem.State.AtcFlightNumber | ATC FLIGHT NUMBER | Flight Number used by ATC | | -| MSFSTouchPortalPlugin.SimSystem.State.AtcModel | ATC MODEL | Model of aircraft used by ATC | | -| MSFSTouchPortalPlugin.SimSystem.State.SimulationRate | SIMULATION RATE | The current simulation rate | | -| MSFSTouchPortalPlugin.SimSystem.State.AtcType | ATC TYPE | Type of aircraft used by ATC | | + **Base Id:** MSFSTouchPortalPlugin.SimSystem.Event.     **Base State Id** MSFSTouchPortalPlugin. + + + + +
    IdNameEvaluated State IdFormatTypeChoice(s)
    SimSystemEventSimulator System EventSimSystem.State.SimSystemEventOn Simulator Event $choicechoice
    details +
    • Connecting - Upon every connection attempt to the Simulator.
    • +
    • Connected - Upon successful connection to the Simulator.
    • +
    • Disconnected - Upon disconnection from the Simulator.
    • +
    • Connection Timed Out - When a connection attempt to the Simulator times out, eg. when sim is not running.
    • +
    • SimConnect Error - When a Simulator (SimConnect) error or warning is detected. Error details (log entry) are sent in the SimSystemEventData state value.
    • +
    • Plugin Error - When a Plugin-specific error or warning is detected (eg. could not parse action data, load a file, etc). Error details (log entry) are sent in the SimSystemEventData state value.
    • +
    • Plugin Information - When a notable plugin informational ("success") action is detected. Information details (log entry) are sent in the SimSystemEventData state value.
    • +
    • Paused - When the flight is paused.
    • +
    • Unpaused - When the flight is un-paused.
    • +
    • Pause Toggled - When the flight is paused or unpaused, and also immediately returns the current pause state (1 = paused or 0 = unpaused).
    • +
    • Flight Started - The simulator is running. Typically the user is actively controlling the aircraft on the ground or in the air. However, in some cases additional pairs of SimStart/SimStop events are sent. For example, when a flight is reset the events that are sent are SimStop, SimStart, SimStop, SimStart.
    • +
    • Flight Stopped - The simulator is not running. Typically the user is loading a flight, navigating the shell or in a dialog.
    • +
    • Flight Toggled - When the flight changes between running and not.
    • +
    • Aircraft Loaded - When the aircraft flight dynamics file is changed. These files have a .AIR extension. The filename is sent in the SimSystemEventData state value.
    • +
    • Crashed - When the user aircraft crashes.
    • +
    • Crash Reset - When the crash cut-scene has completed.
    • +
    • Flight Loaded - When a flight is loaded. Note that when a flight is ended, a default flight is typically loaded, so these events will occur when flights and missions are started and finished. The filename of the flight loaded is sent in the SimSystemEventData state value.
    • +
    • Flight Saved - When a flight is saved correctly. The filename of the flight saved is sent in the SimSystemEventData state value.
    • +
    • Flight Plan Activated - When a new flight plan is activated. The filename of the activated flight plan is sent in the SimSystemEventData state value.
    • +
    • Flight Plan Deactivated - When the active flight plan is de-activated.
    • +
    • Position Changed - When the user changes the position of their aircraft through a dialog or loading a flight.
    • +
    • Sound Toggled - When the master sound switch is changed.
    • +
    • View 3D Cockpit - When the view changes to the 3D virtual cockpit view.
    • +
    • View External - When the view changes to an external view.
    + + +#### States + + **Base Id:** MSFSTouchPortalPlugin.SimSystem.State. + +| Id | SimVar Name | Description | Unit | Format | DefaultValue | Settable | +| --- | --- | --- | --- | --- | --- | --- | +| AtcId | ATC ID | Aircraft Id used by ATC | string | | | ☑ | +| AircraftTitle | TITLE | Aircraft Title | string | | | | +| AtcAirline | ATC AIRLINE | Airline used by ATC | string | | | ☑ | +| SimSystemEventData | | Data from the most recent Simulator System Event, if any. | string | | | | +| AtcFlightNumber | ATC FLIGHT NUMBER | Flight Number used by ATC | string | | | ☑ | +| AtcModel | ATC MODEL | Model of aircraft used by ATC | string | | | | +| SimSystemEvent | | Most recent Simulator System Event name. | string | | | | +| SimulationRate | SIMULATION RATE | The current simulation rate | number | | | | +| AtcType | ATC TYPE | Type of aircraft used by ATC | string | | | |
    diff --git a/entry.tp b/entry.tp index 1fae7cd..e515563 100644 --- a/entry.tp +++ b/entry.tp @@ -1,18 +1,472 @@ { "sdk": 3, - "version": 600, - "name": "MSFSTouchPortalPlugin", + "version": 700, + "name": "MSFS Touch Portal Plugin", "id": "MSFSTouchPortalPlugin", "configuration": { - "colorDark": "#000000", - "colorLight": "#00B4FF" + "colorDark": "#212121", + "colorLight": "#0090ff" }, - "plugin_start_cmd": "%TP_PLUGIN_FOLDER%\\MSFS-TouchPortal-Plugin\\dist\\MSFSTouchPortalPlugin.exe", + "plugin_start_cmd": "%TP_PLUGIN_FOLDER%MSFS-TouchPortal-Plugin/dist/MSFSTouchPortalPlugin.exe", "categories": [ + { + "id": "MSFSTouchPortalPlugin.Plugin", + "name": "MSFS - Plugin", + "imagepath": "%TP_PLUGIN_FOLDER%MSFS-TouchPortal-Plugin/airplane_takeoff24.png", + "actions": [ + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.Connection", + "name": "Connection", + "prefix": "MSFS", + "type": "communicate", + "description": "Toggle/On/Off SimConnect Connection", + "tryInline": true, + "hasHoldFunctionality": false, + "format": "SimConnect Connection - {$MSFSTouchPortalPlugin.Plugin.Action.Connection.Data.Action$}", + "data": [ + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.Connection.Data.Action", + "type": "choice", + "label": "Action", + "default": "Toggle", + "valueChoices": [ + "Toggle", + "On", + "Off", + "Reload States" + ] + } + ] + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.ActionRepeatInterval", + "name": "Action Repeat Interval", + "prefix": "MSFS", + "type": "communicate", + "description": "Held Action Repeat Rate (ms)", + "tryInline": true, + "hasHoldFunctionality": true, + "format": "Repeat Interval: {$MSFSTouchPortalPlugin.Plugin.Action.ActionRepeatInterval.Data.Action$} to/by: {$MSFSTouchPortalPlugin.Plugin.Action.ActionRepeatInterval.Data.Value$} ms", + "data": [ + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.ActionRepeatInterval.Data.Action", + "type": "choice", + "label": "Action", + "default": "Set", + "valueChoices": [ + "Set", + "Increment", + "Decrement" + ] + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.ActionRepeatInterval.Data.Value", + "type": "text", + "label": "Action", + "default": "450", + "minValue": 50.0, + "maxValue": 2147483647.0, + "allowDecimals": false + } + ] + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.SetCustomSimEvent", + "name": "Activate a Custom Simulator Event", + "prefix": "MSFS", + "type": "communicate", + "description": "Trigger a Simulator Event by name. The value, if any, should evaluate to numeric. Using basic math operators and dynamic state values is possible.", + "tryInline": true, + "hasHoldFunctionality": true, + "format": "Activate Event {$MSFSTouchPortalPlugin.Plugin.Action.SetCustomSimEvent.Data.EvtId$} with value {$MSFSTouchPortalPlugin.Plugin.Action.SetCustomSimEvent.Data.Value$} (if any)", + "data": [ + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.SetCustomSimEvent.Data.EvtId", + "type": "text", + "label": "Event ID", + "default": "SIMULATOR_EVENT_NAME" + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.SetCustomSimEvent.Data.Value", + "type": "text", + "label": "Value", + "default": "" + } + ] + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.SetKnownSimEvent", + "name": "Activate a Simulator Event From List", + "prefix": "MSFS", + "type": "communicate", + "description": "Trigger a Simulator Event. The value, if any, should evaluate to numeric. Using basic math operators and dynamic state values is possible.", + "tryInline": true, + "hasHoldFunctionality": true, + "format": "From Category {$MSFSTouchPortalPlugin.Plugin.Action.SetKnownSimEvent.Data.SimCatName$} Activate Event {$MSFSTouchPortalPlugin.Plugin.Action.SetKnownSimEvent.Data.EvtId$} with value {$MSFSTouchPortalPlugin.Plugin.Action.SetKnownSimEvent.Data.Value$} (if any)", + "data": [ + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.SetKnownSimEvent.Data.SimCatName", + "type": "choice", + "label": "Category", + "default": "", + "valueChoices": [ + "" + ] + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.SetKnownSimEvent.Data.EvtId", + "type": "choice", + "label": "Event ID", + "default": "", + "valueChoices": [ + " " + ] + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.AddKnownSimVar.Data.VarIndex", + "type": "number", + "label": "Variable Index", + "default": 0.0, + "minValue": 0.0, + "maxValue": 99.0, + "allowDecimals": false + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.AddKnownSimVar.Data.CatId", + "type": "choice", + "label": "Category", + "default": "", + "valueChoices": [ + "" + ] + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.AddKnownSimVar.Data.Unit", + "type": "choice", + "label": "Unit Name", + "default": "", + "valueChoices": [ + " " + ] + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.AddKnownSimVar.Data.Format", + "type": "text", + "label": "Formatting String", + "default": "F2" + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.AddKnownSimVar.Data.DfltVal", + "type": "text", + "label": "Default Value", + "default": "0" + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.AddKnownSimVar.Data.UpdPer", + "type": "choice", + "label": "Update Period", + "default": "SimFrame", + "valueChoices": [ + "Once", + "VisualFrame", + "SimFrame", + "Second", + "Millisecond" + ] + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.AddKnownSimVar.Data.UpdInt", + "type": "number", + "label": "Interval", + "default": 0.0, + "minValue": 0.0, + "maxValue": 2147483647.0, + "allowDecimals": false + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.AddKnownSimVar.Data.Epsilon", + "type": "number", + "label": "Delta Epsilon", + "default": 0.0099999, + "minValue": 0.0, + "maxValue": 3.4028234663852886E+38 + } + ] + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.RemoveSimVar", + "name": "Remove a Simulator Variable", + "prefix": "MSFS", + "type": "communicate", + "description": "Remove an existing Simulator Variable. Note that static TP States cannot be removed.", + "tryInline": true, + "hasHoldFunctionality": false, + "format": "Remove Simulator Variable {$MSFSTouchPortalPlugin.Plugin.Action.RemoveSimVar.Data.VarName$}", + "data": [ + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.RemoveSimVar.Data.VarName", + "type": "choice", + "label": "Simulator Variable", + "default": "", + "valueChoices": [ + "" + ] + } + ] + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.LoadSimVars", + "name": "Load SimVar Definitions From File", + "prefix": "MSFS", + "type": "communicate", + "description": "Load a set of simulator variable state definitions from a configuration file.", + "tryInline": true, + "hasHoldFunctionality": false, + "format": "Load from file {$MSFSTouchPortalPlugin.Plugin.Action.LoadSimVars.Data.VarsFile$} (name only for user config. folder, or full path with file name)", + "data": [ + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.LoadSimVars.Data.VarsFile", + "type": "file", + "label": "Load From File", + "default": "CustomStates.ini" + } + ] + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.SaveSimVars", + "name": "Save SimVar Definitions To File", + "prefix": "MSFS", + "type": "communicate", + "description": "Save the current simulator variable state definitions to a configuration file.", + "tryInline": true, + "hasHoldFunctionality": false, + "format": "Save {$MSFSTouchPortalPlugin.Plugin.Action.SaveSimVars.Data.VarsSet$} states to file {$MSFSTouchPortalPlugin.Plugin.Action.SaveSimVars.Data.VarsFile$} (name only for user config. folder, or full path with file name)", + "data": [ + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.SaveSimVars.Data.VarsSet", + "type": "choice", + "label": "Variables Set", + "default": "Custom", + "valueChoices": [ + "Custom", + "All" + ] + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.Action.SaveSimVars.Data.VarsFile", + "type": "text", + "label": "Save to File", + "default": "CustomStates.ini" + } + ] + } + ], + "events": [], + "states": [ + { + "id": "MSFSTouchPortalPlugin.Plugin.State.ActionRepeatInterval", + "type": "text", + "desc": "MSFS - Plugin - The current Held Action Repeat Rate (ms)", + "default": "450" + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.State.Connected", + "type": "text", + "desc": "MSFS - Plugin - The status of SimConnect (true/false/connecting)", + "default": "false" + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.State.RunningVersion", + "type": "text", + "desc": "MSFS - Plugin - The running plugin version number.", + "default": "0" + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.State.EntryVersion", + "type": "text", + "desc": "MSFS - Plugin - The loaded entry.tp plugin version number.", + "default": "0" + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.State.ConfigVersion", + "type": "text", + "desc": "MSFS - Plugin - The loaded entry.tp custom configuration version.", + "default": "0" + }, + { + "id": "MSFSTouchPortalPlugin.Plugin.State.LogMessages", + "type": "text", + "desc": "MSFS - Plugin - Most recent plugin log messages.", + "default": "" + } + ] + }, { "id": "MSFSTouchPortalPlugin.AutoPilot", "name": "MSFS - AutoPilot", - "imagepath": "%TP_PLUGIN_FOLDER%\\MSFS-TouchPortal-Plugin\\airplane_takeoff24.png", + "imagepath": "%TP_PLUGIN_FOLDER%MSFS-TouchPortal-Plugin/airplane_takeoff24.png", "actions": [ { "id": "MSFSTouchPortalPlugin.AutoPilot.Action.AutoPilotAirSpeed", @@ -67,7 +521,7 @@ "type": "communicate", "description": "Sets the airspeed value", "tryInline": true, - "hasHoldFunctionality": false, + "hasHoldFunctionality": true, "format": "Set Airspeed Hold Value to {$MSFSTouchPortalPlugin.AutoPilot.Action.AutoPilotAirSpeedSet.Data.0$}", "data": [ { @@ -134,7 +588,7 @@ "type": "communicate", "description": "Sets the altitude hold value", "tryInline": true, - "hasHoldFunctionality": false, + "hasHoldFunctionality": true, "format": "Altitude Hold Value - {$MSFSTouchPortalPlugin.AutoPilot.Action.AutoPilotAltitudeSet.Data.0$} to {$MSFSTouchPortalPlugin.AutoPilot.Action.AutoPilotAltitudeSet.Data.1$}", "data": [ { @@ -253,16 +707,16 @@ "type": "communicate", "description": "Sets the airspeed value", "tryInline": true, - "hasHoldFunctionality": false, - "format": "Set Attitude Hold Pitch Value to {$MSFSTouchPortalPlugin.AutoPilot.Action.AutoPilotAttitudeSet.Data.0$} (-16383 - +16383)", + "hasHoldFunctionality": true, + "format": "Set Attitude Hold Pitch Value to {$MSFSTouchPortalPlugin.AutoPilot.Action.AutoPilotAttitudeSet.Data.0$} (-16384 to +16384)", "data": [ { "id": "MSFSTouchPortalPlugin.AutoPilot.Action.AutoPilotAttitudeSet.Data.0", "type": "text", "label": "Action", "default": "0", - "minValue": -16383.0, - "maxValue": 16383.0, + "minValue": -16384.0, + "maxValue": 16384.0, "allowDecimals": false } ] @@ -432,7 +886,7 @@ "type": "communicate", "description": "Sets the heading hold value", "tryInline": true, - "hasHoldFunctionality": false, + "hasHoldFunctionality": true, "format": "Heading Hold Value - {$MSFSTouchPortalPlugin.AutoPilot.Action.AutoPilotHeadingSet.Data.0$}", "data": [ { @@ -522,7 +976,7 @@ "type": "communicate", "description": "Sets the mach hold value", "tryInline": true, - "hasHoldFunctionality": false, + "hasHoldFunctionality": true, "format": "Set Mach Hold Value to {$MSFSTouchPortalPlugin.AutoPilot.Action.AutoPilotMachSet.Data.0$}", "data": [ { @@ -634,7 +1088,7 @@ "type": "communicate", "description": "Sets the vertical speed value", "tryInline": true, - "hasHoldFunctionality": false, + "hasHoldFunctionality": true, "format": "Vertical Speed Hold Value - {$MSFSTouchPortalPlugin.AutoPilot.Action.AutoPilotVerticalSpeedSet.Data.0$} to {$MSFSTouchPortalPlugin.AutoPilot.Action.AutoPilotVerticalSpeedSet.Data.1$}", "data": [ { @@ -874,7 +1328,7 @@ { "id": "MSFSTouchPortalPlugin.Communication", "name": "MSFS - Communication", - "imagepath": "%TP_PLUGIN_FOLDER%\\MSFS-TouchPortal-Plugin\\airplane_takeoff24.png", + "imagepath": "%TP_PLUGIN_FOLDER%MSFS-TouchPortal-Plugin/airplane_takeoff24.png", "actions": [ { "id": "MSFSTouchPortalPlugin.Communication.Action.Radios", @@ -971,7 +1425,7 @@ { "id": "MSFSTouchPortalPlugin.Electrical", "name": "MSFS - Electrical", - "imagepath": "%TP_PLUGIN_FOLDER%\\MSFS-TouchPortal-Plugin\\airplane_takeoff24.png", + "imagepath": "%TP_PLUGIN_FOLDER%MSFS-TouchPortal-Plugin/airplane_takeoff24.png", "actions": [ { "id": "MSFSTouchPortalPlugin.Electrical.Action.AlternatorIndex", @@ -981,7 +1435,7 @@ "description": "Toggle Specific Alternator", "tryInline": true, "hasHoldFunctionality": false, - "format": "Toggle Altenator - {$MSFSTouchPortalPlugin.Electrical.Action.AlternatorIndex.Data.0$}", + "format": "Toggle Alternator - {$MSFSTouchPortalPlugin.Electrical.Action.AlternatorIndex.Data.0$}", "data": [ { "id": "MSFSTouchPortalPlugin.Electrical.Action.AlternatorIndex.Data.0", @@ -1236,7 +1690,7 @@ { "id": "MSFSTouchPortalPlugin.Engine", "name": "MSFS - Engine", - "imagepath": "%TP_PLUGIN_FOLDER%\\MSFS-TouchPortal-Plugin\\airplane_takeoff24.png", + "imagepath": "%TP_PLUGIN_FOLDER%MSFS-TouchPortal-Plugin/airplane_takeoff24.png", "actions": [ { "id": "MSFSTouchPortalPlugin.Engine.Action.EngineAuto", @@ -1455,8 +1909,8 @@ "type": "communicate", "description": "Sets propeller pitch lever to value", "tryInline": true, - "hasHoldFunctionality": false, - "format": "Set Propeller {$MSFSTouchPortalPlugin.Engine.Action.PropellerPitchSet.Data.0$} Pitch to {$MSFSTouchPortalPlugin.Engine.Action.PropellerPitchSet.Data.1$} (0 to 16383)", + "hasHoldFunctionality": true, + "format": "Set Propeller {$MSFSTouchPortalPlugin.Engine.Action.PropellerPitchSet.Data.0$} Pitch to {$MSFSTouchPortalPlugin.Engine.Action.PropellerPitchSet.Data.1$} (0 to 16384)", "data": [ { "id": "MSFSTouchPortalPlugin.Engine.Action.PropellerPitchSet.Data.0", @@ -1477,7 +1931,7 @@ "label": "Action", "default": "0", "minValue": 0.0, - "maxValue": 16383.0, + "maxValue": 16384.0, "allowDecimals": false } ] @@ -1524,8 +1978,8 @@ "type": "communicate", "description": "Sets all or specific Throttle(s) to specific value", "tryInline": true, - "hasHoldFunctionality": false, - "format": "Set Throttle {$MSFSTouchPortalPlugin.Engine.Action.ThrottleSet.Data.0$} to {$MSFSTouchPortalPlugin.Engine.Action.ThrottleSet.Data.1$} (-16383 - +16383)", + "hasHoldFunctionality": true, + "format": "Set Throttle {$MSFSTouchPortalPlugin.Engine.Action.ThrottleSet.Data.0$} to {$MSFSTouchPortalPlugin.Engine.Action.ThrottleSet.Data.1$} (-16384 to +16384)", "data": [ { "id": "MSFSTouchPortalPlugin.Engine.Action.ThrottleSet.Data.0", @@ -1545,8 +1999,8 @@ "type": "text", "label": "Action", "default": "0", - "minValue": -16383.0, - "maxValue": 16383.0, + "minValue": -16384.0, + "maxValue": 16384.0, "allowDecimals": false } ] @@ -1824,7 +2278,7 @@ { "id": "MSFSTouchPortalPlugin.Environment", "name": "MSFS - Environment", - "imagepath": "%TP_PLUGIN_FOLDER%\\MSFS-TouchPortal-Plugin\\airplane_takeoff24.png", + "imagepath": "%TP_PLUGIN_FOLDER%MSFS-TouchPortal-Plugin/airplane_takeoff24.png", "actions": [ { "id": "MSFSTouchPortalPlugin.Environment.Action.AntiIce", @@ -2052,7 +2506,7 @@ { "id": "MSFSTouchPortalPlugin.Failures", "name": "MSFS - Failures", - "imagepath": "%TP_PLUGIN_FOLDER%\\MSFS-TouchPortal-Plugin\\airplane_takeoff24.png", + "imagepath": "%TP_PLUGIN_FOLDER%MSFS-TouchPortal-Plugin/airplane_takeoff24.png", "actions": [ { "id": "MSFSTouchPortalPlugin.Failures.Action.Failures", @@ -2093,7 +2547,7 @@ { "id": "MSFSTouchPortalPlugin.FlightInstruments", "name": "MSFS - Flight Instruments", - "imagepath": "%TP_PLUGIN_FOLDER%\\MSFS-TouchPortal-Plugin\\airplane_takeoff24.png", + "imagepath": "%TP_PLUGIN_FOLDER%MSFS-TouchPortal-Plugin/airplane_takeoff24.png", "actions": [], "events": [], "states": [ @@ -2106,7 +2560,7 @@ { "id": "MSFSTouchPortalPlugin.FlightInstruments.State.AirSpeedMach", "type": "text", - "desc": "MSFS - Flight Instruments - Air speed indicated in mach", + "desc": "MSFS - Flight Instruments - Air speed indicated in Mach", "default": "" }, { @@ -2192,7 +2646,7 @@ { "id": "MSFSTouchPortalPlugin.FlightSystems", "name": "MSFS - Flight Systems", - "imagepath": "%TP_PLUGIN_FOLDER%\\MSFS-TouchPortal-Plugin\\airplane_takeoff24.png", + "imagepath": "%TP_PLUGIN_FOLDER%MSFS-TouchPortal-Plugin/airplane_takeoff24.png", "actions": [ { "id": "MSFSTouchPortalPlugin.FlightSystems.Action.AileronTrim", @@ -2223,7 +2677,7 @@ "type": "communicate", "description": " Set Aileron Trim", "tryInline": true, - "hasHoldFunctionality": false, + "hasHoldFunctionality": true, "format": "Aileron Trim set to {$MSFSTouchPortalPlugin.FlightSystems.Action.AileronTrimSet.Data.0$}% (-100 - +100)", "data": [ { @@ -2267,16 +2721,16 @@ "type": "communicate", "description": " Set Ailerons", "tryInline": true, - "hasHoldFunctionality": false, - "format": "Ailerons set to {$MSFSTouchPortalPlugin.FlightSystems.Action.AileronsSet.Data.0$} (-16383 - +16383)", + "hasHoldFunctionality": true, + "format": "Ailerons set to {$MSFSTouchPortalPlugin.FlightSystems.Action.AileronsSet.Data.0$} (-16384 to +16384)", "data": [ { "id": "MSFSTouchPortalPlugin.FlightSystems.Action.AileronsSet.Data.0", "type": "text", "label": "Action", "default": "0", - "minValue": -16383.0, - "maxValue": 16383.0, + "minValue": -16384.0, + "maxValue": 16384.0, "allowDecimals": false } ] @@ -2443,16 +2897,16 @@ "type": "communicate", "description": " Set Elevator", "tryInline": true, - "hasHoldFunctionality": false, - "format": "Elevator set to {$MSFSTouchPortalPlugin.FlightSystems.Action.ElevatorSet.Data.0$} (-16383 - +16383)", + "hasHoldFunctionality": true, + "format": "Elevator set to {$MSFSTouchPortalPlugin.FlightSystems.Action.ElevatorSet.Data.0$} (-16384 to +16384)", "data": [ { "id": "MSFSTouchPortalPlugin.FlightSystems.Action.ElevatorSet.Data.0", "type": "text", "label": "Action", "default": "0", - "minValue": -16383.0, - "maxValue": 16383.0, + "minValue": -16384.0, + "maxValue": 16384.0, "allowDecimals": false } ] @@ -2486,16 +2940,16 @@ "type": "communicate", "description": " Set Elevator Trim", "tryInline": true, - "hasHoldFunctionality": false, - "format": "Elevator Trim set to {$MSFSTouchPortalPlugin.FlightSystems.Action.ElevatorTrimSet.Data.0$} (-16383 - +16383)", + "hasHoldFunctionality": true, + "format": "Elevator Trim set to {$MSFSTouchPortalPlugin.FlightSystems.Action.ElevatorTrimSet.Data.0$} (-16384 to +16384)", "data": [ { "id": "MSFSTouchPortalPlugin.FlightSystems.Action.ElevatorTrimSet.Data.0", "type": "text", "label": "Action", "default": "0", - "minValue": -16383.0, - "maxValue": 16383.0, + "minValue": -16384.0, + "maxValue": 16384.0, "allowDecimals": false } ] @@ -2535,8 +2989,8 @@ "type": "communicate", "description": " Set Flaps", "tryInline": true, - "hasHoldFunctionality": false, - "format": "Flaps set to {$MSFSTouchPortalPlugin.FlightSystems.Action.FlapsSet.Data.0$} (0 - +16383)", + "hasHoldFunctionality": true, + "format": "Flaps set to {$MSFSTouchPortalPlugin.FlightSystems.Action.FlapsSet.Data.0$} (0 to 16384)", "data": [ { "id": "MSFSTouchPortalPlugin.FlightSystems.Action.FlapsSet.Data.0", @@ -2544,7 +2998,7 @@ "label": "Action", "default": "0", "minValue": 0.0, - "maxValue": 16383.0, + "maxValue": 16384.0, "allowDecimals": false } ] @@ -2603,16 +3057,16 @@ "type": "communicate", "description": " Set Rudder", "tryInline": true, - "hasHoldFunctionality": false, - "format": "Rudder set to {$MSFSTouchPortalPlugin.FlightSystems.Action.RudderSet.Data.0$} (-16383 - +16383)", + "hasHoldFunctionality": true, + "format": "Rudder set to {$MSFSTouchPortalPlugin.FlightSystems.Action.RudderSet.Data.0$} (-16384 to +16384)", "data": [ { "id": "MSFSTouchPortalPlugin.FlightSystems.Action.RudderSet.Data.0", "type": "text", "label": "Action", "default": "0", - "minValue": -16383.0, - "maxValue": 16383.0, + "minValue": -16384.0, + "maxValue": 16384.0, "allowDecimals": false } ] @@ -2646,7 +3100,7 @@ "type": "communicate", "description": " Set Rudder Trim", "tryInline": true, - "hasHoldFunctionality": false, + "hasHoldFunctionality": true, "format": "Rudder Trim set to {$MSFSTouchPortalPlugin.FlightSystems.Action.RudderTrimSet.Data.0$}% (-100 - +100)", "data": [ { @@ -2713,8 +3167,8 @@ "type": "communicate", "description": " Set Spoilers", "tryInline": true, - "hasHoldFunctionality": false, - "format": "Spoilers set to {$MSFSTouchPortalPlugin.FlightSystems.Action.SpoilersSet.Data.0$} (0- +16383)", + "hasHoldFunctionality": true, + "format": "Set Spoilers handle position to {$MSFSTouchPortalPlugin.FlightSystems.Action.SpoilersSet.Data.0$} (0 to 16384)", "data": [ { "id": "MSFSTouchPortalPlugin.FlightSystems.Action.SpoilersSet.Data.0", @@ -2722,7 +3176,7 @@ "label": "Action", "default": "0", "minValue": 0.0, - "maxValue": 16383.0, + "maxValue": 16384.0, "allowDecimals": false } ] @@ -2813,6 +3267,36 @@ "desc": "MSFS - Flight Systems - Rudder Trim Percent", "default": "0" }, + { + "id": "MSFSTouchPortalPlugin.FlightSystems.State.SpoilersArmed", + "type": "text", + "desc": "MSFS - Flight Systems - Spoilers Armed (0/1)", + "default": "0" + }, + { + "id": "MSFSTouchPortalPlugin.FlightSystems.State.SpoilersAvailable", + "type": "text", + "desc": "MSFS - Flight Systems - Spoilers Available (0/1)", + "default": "0" + }, + { + "id": "MSFSTouchPortalPlugin.FlightSystems.State.SpoilersHandlePosition", + "type": "text", + "desc": "MSFS - Flight Systems - Spoilers Handle Position (0 - 16384)", + "default": "0" + }, + { + "id": "MSFSTouchPortalPlugin.FlightSystems.State.SpoilersLeftPosition", + "type": "text", + "desc": "MSFS - Flight Systems - Spoilers Left Position Percent", + "default": "0" + }, + { + "id": "MSFSTouchPortalPlugin.FlightSystems.State.SpoilersRightPosition", + "type": "text", + "desc": "MSFS - Flight Systems - Spoilers Right Position Percent", + "default": "0" + }, { "id": "MSFSTouchPortalPlugin.FlightSystems.State.GearTotalExtended", "type": "text", @@ -2822,20 +3306,30 @@ ] }, { - "id": "MSFSTouchPortalPlugin.InstrumentsSystems.Fuel", + "id": "MSFSTouchPortalPlugin.Fuel", "name": "MSFS - Fuel", - "imagepath": "%TP_PLUGIN_FOLDER%\\MSFS-TouchPortal-Plugin\\airplane_takeoff24.png", + "imagepath": "%TP_PLUGIN_FOLDER%MSFS-TouchPortal-Plugin/airplane_takeoff24.png", "actions": [ { "id": "MSFSTouchPortalPlugin.InstrumentsSystems.Fuel.Action.AddFuel", "name": "Add Fuel", "prefix": "MSFS", "type": "communicate", - "description": "Adds 25% amount of Fuel", + "description": "Add Fuel (1 to 65535 or zero for 25% of capacity)", "tryInline": true, - "hasHoldFunctionality": false, - "format": "Add 25% amount of fuel", - "data": [] + "hasHoldFunctionality": true, + "format": "Add {$MSFSTouchPortalPlugin.InstrumentsSystems.Fuel.Action.AddFuel.Data.0$} amount of Fuel", + "data": [ + { + "id": "MSFSTouchPortalPlugin.InstrumentsSystems.Fuel.Action.AddFuel.Data.0", + "type": "text", + "label": "Action", + "default": "0", + "minValue": 0.0, + "maxValue": 65535.0, + "allowDecimals": false + } + ] }, { "id": "MSFSTouchPortalPlugin.InstrumentsSystems.Fuel.Action.ElectricFuelPump", @@ -3002,87 +3496,10 @@ "events": [], "states": [] }, - { - "id": "MSFSTouchPortalPlugin.Plugin", - "name": "MSFS - Plugin", - "imagepath": "%TP_PLUGIN_FOLDER%\\MSFS-TouchPortal-Plugin\\airplane_takeoff24.png", - "actions": [ - { - "id": "MSFSTouchPortalPlugin.Plugin.Action.ActionRepeatInterval", - "name": "Action Repeat Interval", - "prefix": "MSFS", - "type": "communicate", - "description": "Held Action Repeat Rate (ms)", - "tryInline": true, - "hasHoldFunctionality": true, - "format": "Repeat Interval: {$MSFSTouchPortalPlugin.Plugin.Action.ActionRepeatInterval.Data.0$} to/by: {$MSFSTouchPortalPlugin.Plugin.Action.ActionRepeatInterval.Data.1$} ms", - "data": [ - { - "id": "MSFSTouchPortalPlugin.Plugin.Action.ActionRepeatInterval.Data.0", - "type": "choice", - "label": "Action", - "default": "Set", - "valueChoices": [ - "Set", - "Increment", - "Decrement" - ] - }, - { - "id": "MSFSTouchPortalPlugin.Plugin.Action.ActionRepeatInterval.Data.1", - "type": "text", - "label": "Action", - "default": "450", - "minValue": 50.0, - "maxValue": 2147483647.0, - "allowDecimals": false - } - ] - }, - { - "id": "MSFSTouchPortalPlugin.Plugin.Action.Connection", - "name": "Connection", - "prefix": "MSFS", - "type": "communicate", - "description": "Toggle/On/Off SimConnect Connection", - "tryInline": true, - "hasHoldFunctionality": false, - "format": "SimConnect Connection - {$MSFSTouchPortalPlugin.Plugin.Action.Connection.Data.0$}", - "data": [ - { - "id": "MSFSTouchPortalPlugin.Plugin.Action.Connection.Data.0", - "type": "choice", - "label": "Action", - "default": "Toggle", - "valueChoices": [ - "Toggle", - "On", - "Off" - ] - } - ] - } - ], - "events": [], - "states": [ - { - "id": "MSFSTouchPortalPlugin.Plugin.State.ActionRepeatInterval", - "type": "text", - "desc": "MSFS - Plugin - The current Held Action Repeat Rate (ms)", - "default": "450" - }, - { - "id": "MSFSTouchPortalPlugin.Plugin.State.Connected", - "type": "text", - "desc": "MSFS - Plugin - The status of SimConnect (true/false/connecting)", - "default": "false" - } - ] - }, { "id": "MSFSTouchPortalPlugin.SimSystem", "name": "MSFS - System", - "imagepath": "%TP_PLUGIN_FOLDER%\\MSFS-TouchPortal-Plugin\\airplane_takeoff24.png", + "imagepath": "%TP_PLUGIN_FOLDER%MSFS-TouchPortal-Plugin/airplane_takeoff24.png", "actions": [ { "id": "MSFSTouchPortalPlugin.SimSystem.Action.SelectedParameter", @@ -3129,7 +3546,42 @@ ] } ], - "events": [], + "events": [ + { + "id": "MSFSTouchPortalPlugin.SimSystem.Event.SimSystemEvent", + "name": "Simulator System Event", + "format": "On Simulator Event $val", + "type": "communicate", + "valueType": "choice", + "valueChoices": [ + "Connecting", + "Connected", + "Disconnected", + "Connection Timed Out", + "SimConnect Error", + "Plugin Error", + "Plugin Information", + "Paused", + "Unpaused", + "Pause Toggled", + "Flight Started", + "Flight Stopped", + "Flight Toggled", + "Aircraft Loaded", + "Crashed", + "Crash Reset", + "Flight Loaded", + "Flight Saved", + "Flight Plan Activated", + "Flight Plan Deactivated", + "Position Changed", + "Sound Toggled", + "View 3D Cockpit", + "View External" + ], + "valueStateId": "MSFSTouchPortalPlugin.SimSystem.State.SimSystemEvent" + } + ], "states": [ { "id": "MSFSTouchPortalPlugin.SimSystem.State.AtcId", @@ -3149,6 +3601,12 @@ "desc": "MSFS - System - Airline used by ATC", "default": "" }, + { + "id": "MSFSTouchPortalPlugin.SimSystem.State.SimSystemEventData", + "type": "text", + "desc": "MSFS - System - Data from the most recent Simulator System Event, if any.", + "default": "" + }, { "id": "MSFSTouchPortalPlugin.SimSystem.State.AtcFlightNumber", "type": "text", @@ -3161,6 +3619,12 @@ "desc": "MSFS - System - Model of aircraft used by ATC", "default": "" }, + { + "id": "MSFSTouchPortalPlugin.SimSystem.State.SimSystemEvent", + "type": "text", + "desc": "MSFS - System - Most recent Simulator System Event name.", + "default": "" + }, { "id": "MSFSTouchPortalPlugin.SimSystem.State.SimulationRate", "type": "text", @@ -3180,12 +3644,37 @@ { "name": "Connect To Flight Sim on Startup (0/1)", "type": "number", - "default": "1", + "default": "0", "minValue": 0.0, "maxValue": 1.0, "isPassword": false, "readOnly": false }, + { + "name": "Sim Variable State Config File(s) (blank = Default)", + "type": "text", + "default": "Default", + "maxLength": 255, + "isPassword": false, + "readOnly": false + }, + { + "name": "SimConnect.cfg Index (0 for MSFS, 1 for FSX, or custom)", + "type": "number", + "default": "0", + "minValue": 0.0, + "maxValue": 20.0, + "isPassword": false, + "readOnly": false + }, + { + "name": "User Config Files Path (blank for default)", + "type": "text", + "default": "", + "maxLength": 255, + "isPassword": false, + "readOnly": false + }, { "name": "Held Action Repeat Rate (ms)", "type": "number",