├── .gitattributes ├── .gitignore ├── CopyLatestToRelease.ps1 ├── Fenix2GSX-Installer-latest.exe ├── Fenix2GSX.sln ├── Fenix2GSX ├── Aircraft │ ├── AircraftInterface.cs │ └── Flightplan.cs ├── AppConfig │ ├── AircraftProfile.cs │ ├── AudioMapping.cs │ ├── Config.cs │ ├── Definition.cs │ └── ServiceConfig.cs ├── AppService.cs ├── AssemblyInfo.cs ├── Audio │ ├── AudioController.cs │ ├── AudioSession.cs │ ├── DeviceManager.cs │ └── SessionManager.cs ├── BuildApp.ps1 ├── Fenix2GSX.cs ├── Fenix2GSX.csproj ├── FenixInterface.dll ├── GSX │ ├── GsxAutomationController.cs │ ├── GsxConstants.cs │ ├── GsxController.cs │ ├── GsxMessages.cs │ ├── Menu │ │ ├── GsxMenu.cs │ │ ├── GsxMenuCommand.cs │ │ ├── GsxMenuSequence.cs │ │ └── GsxOperator.cs │ └── Services │ │ ├── GsxService.cs │ │ ├── GsxServiceBoarding.cs │ │ ├── GsxServiceCatering.cs │ │ ├── GsxServiceDeboarding.cs │ │ ├── GsxServiceDeice.cs │ │ ├── GsxServiceGpu.cs │ │ ├── GsxServiceJetway.cs │ │ ├── GsxServiceLavatory.cs │ │ ├── GsxServicePushback.cs │ │ ├── GsxServiceRefuel.cs │ │ ├── GsxServiceReposition.cs │ │ ├── GsxServiceStairs.cs │ │ └── GsxServiceWater.cs ├── GlobalSuppressions.cs ├── Microsoft.FlightSimulator.SimConnect.dll ├── Properties │ └── launchSettings.json ├── SimConnect.dll ├── Tools.cs └── UI │ ├── AppWindow.xaml │ ├── AppWindow.xaml.cs │ ├── Icons │ ├── AppIcon.ico │ ├── AppIconUpdate.ico │ ├── add.png │ ├── automation.png │ ├── chevron-down.png │ ├── chevron-up.png │ ├── cursor.png │ ├── edit.png │ ├── monitor.png │ ├── profiles.png │ ├── remove.png │ ├── settings.png │ ├── upload.png │ └── volume.png │ ├── NotifyIcon │ └── NotifyIconModelExt.cs │ ├── StyleResources.xaml │ ├── TimeSpanConverter.cs │ └── Views │ ├── Audio │ ├── ModelAppMappings.cs │ ├── ModelAudio.cs │ ├── ModelDeviceBlacklist.cs │ ├── ViewAudio.xaml │ └── ViewAudio.xaml.cs │ ├── Automation │ ├── ControlAircraftOptions.xaml │ ├── ControlAircraftOptions.xaml.cs │ ├── ControlCompanyHubs.xaml │ ├── ControlCompanyHubs.xaml.cs │ ├── ControlGateDoors.xaml │ ├── ControlGateDoors.xaml.cs │ ├── ControlGroundEquip.xaml │ ├── ControlGroundEquip.xaml.cs │ ├── ControlGsxServices.xaml │ ├── ControlGsxServices.xaml.cs │ ├── ControlOperatorSelection.xaml │ ├── ControlOperatorSelection.xaml.cs │ ├── ControlSkipQuestions.xaml │ ├── ControlSkipQuestions.xaml.cs │ ├── ModelAutomation.cs │ ├── ModelCompanyHubs.cs │ ├── ModelDepartureServices.cs │ ├── ModelOperatorPreferences.cs │ ├── ViewAutomation.xaml │ └── ViewAutomation.xaml.cs │ ├── ModelBase.cs │ ├── Monitor │ ├── ModelMonitor.cs │ ├── ViewMonitor.xaml │ └── ViewMonitor.xaml.cs │ ├── Profiles │ ├── ModelProfileCollection.cs │ ├── ModelProfiles.cs │ ├── ViewProfiles.xaml │ └── ViewProfiles.xaml.cs │ └── Settings │ ├── ModelSettings.cs │ ├── ViewSettings.xaml │ └── ViewSettings.xaml.cs ├── Installer ├── App.config ├── AppIcon.ico ├── AppMain.cs ├── Config.cs ├── ConfigPage.cs ├── CreateDefaultConfig.ps1 ├── Definition.cs ├── ExportInstaller.ps1 ├── FodyWeavers.xml ├── Installer.csproj ├── PackageApp.ps1 ├── Payload │ ├── AppConfig.json │ ├── AppPackage.zip │ ├── icon.png │ └── version.json ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── WorkerInstallUpdate.cs ├── WorkerManager.cs ├── WorkerRemoveMobi.cs └── packages.config ├── LICENSE.txt ├── NuPreBuild.ps1 ├── README.md ├── img ├── Fs2Crew.png ├── appmonitor.png ├── flowpro.png ├── flowproGSX.png ├── icon.png ├── ui.png ├── ui3.png ├── ui4-commented.png ├── ui4.png ├── ui5.png └── ui6.png └── nuget.exe /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /CopyLatestToRelease.ps1: -------------------------------------------------------------------------------- 1 | ### Run in SolutionDir 2 | $appName = "Fenix2GSX" 3 | $suffix = "latest" 4 | $versionPath = "Installer\Payload\version.json" 5 | $releaseDir = "Releases\" 6 | $includeStamp = $false 7 | 8 | Write-Host "Getting App Version ..." 9 | $versionJson = (Get-Content -Raw $versionPath | ConvertFrom-Json) 10 | $version = $versionJson.Version 11 | $version = $version.Substring(0,$version.LastIndexOf('.')) 12 | $timestamp = $versionJson.Timestamp 13 | 14 | if (-not (Test-Path $releaseDir)) { 15 | mkdir $releaseDir | Out-Null 16 | } 17 | $releaseFile = "$appName-Installer-v$version.exe" 18 | if ($includeStamp) { 19 | $releaseFile = "$appName-Installer-v$version-$timestamp.exe" 20 | } 21 | 22 | $releasePath = Join-Path $releaseDir "$releaseFile" 23 | 24 | Write-Host "Exporting $releaseFile ..." 25 | if (-not (Test-Path $releasePath)) { 26 | Copy-Item "$appName-Installer-$suffix.exe" $releasePath | Out-Null 27 | } else { 28 | Write-Host "WARNING: The File '$releaseFile' already exists!" 29 | } -------------------------------------------------------------------------------- /Fenix2GSX-Installer-latest.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX-Installer-latest.exe -------------------------------------------------------------------------------- /Fenix2GSX.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.13.35828.75 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fenix2GSX", "Fenix2GSX\Fenix2GSX.csproj", "{F540953A-20B6-463E-B219-C3FC94827415}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Installer", "Installer\Installer.csproj", "{3B7EEEA0-4233-47AD-8F2A-95B612834BF1}" 9 | ProjectSection(ProjectDependencies) = postProject 10 | {F540953A-20B6-463E-B219-C3FC94827415} = {F540953A-20B6-463E-B219-C3FC94827415} 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {F540953A-20B6-463E-B219-C3FC94827415}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {F540953A-20B6-463E-B219-C3FC94827415}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {F540953A-20B6-463E-B219-C3FC94827415}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {F540953A-20B6-463E-B219-C3FC94827415}.Release|Any CPU.Build.0 = Release|Any CPU 23 | {3B7EEEA0-4233-47AD-8F2A-95B612834BF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {3B7EEEA0-4233-47AD-8F2A-95B612834BF1}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {3B7EEEA0-4233-47AD-8F2A-95B612834BF1}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {3B7EEEA0-4233-47AD-8F2A-95B612834BF1}.Release|Any CPU.Build.0 = Release|Any CPU 27 | EndGlobalSection 28 | GlobalSection(SolutionProperties) = preSolution 29 | HideSolutionNode = FALSE 30 | EndGlobalSection 31 | GlobalSection(ExtensibilityGlobals) = postSolution 32 | SolutionGuid = {B4A9771E-1A68-486B-BB8C-78EE1C1DF42F} 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /Fenix2GSX/Aircraft/Flightplan.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppLogger; 2 | using Fenix2GSX.AppConfig; 3 | using System; 4 | using System.Net.Http; 5 | using System.Net.Http.Headers; 6 | using System.Text.Json.Nodes; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Fenix2GSX.Aircraft 11 | { 12 | public class Flightplan 13 | { 14 | public virtual Config Config => AppService.Instance.Config; 15 | public virtual string SimbriefUser => AppService.Instance.GsxService.AircraftInterface.SimbriefUser; 16 | public virtual CancellationToken Token => AppService.Instance.Token; 17 | protected virtual HttpClient HttpClient { get; } 18 | 19 | public Flightplan() 20 | { 21 | HttpClient = new() 22 | { 23 | BaseAddress = new(Config.SimbriefUrlBase) 24 | }; 25 | HttpClient.DefaultRequestHeaders.Accept.Clear(); 26 | HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 27 | } 28 | 29 | protected virtual async Task GetJsonNode() 30 | { 31 | if (long.TryParse(SimbriefUser, out _)) 32 | { 33 | Logger.Debug($"Requesting SimBrief (via Userid) ..."); 34 | return JsonNode.Parse(await HttpClient.GetStringAsync(string.Format(Config.SimbriefUrlPathId, SimbriefUser), Token)); 35 | } 36 | else 37 | { 38 | Logger.Debug($"Requesting SimBrief (via Username) ..."); 39 | return JsonNode.Parse(await HttpClient.GetStringAsync(string.Format(Config.SimbriefUrlPathName, SimbriefUser), Token)); 40 | } 41 | } 42 | 43 | protected virtual bool GetJsonString(JsonNode node, out string value) 44 | { 45 | value = ""; 46 | if (node!.GetValueKind() == System.Text.Json.JsonValueKind.String) 47 | { 48 | value = node!.GetValue(); 49 | return true; 50 | } 51 | else 52 | return false; 53 | } 54 | 55 | public virtual async Task GetDestinationIcao() 56 | { 57 | try 58 | { 59 | var json = await GetJsonNode(); 60 | Logger.Debug($"Received Result"); 61 | if (GetJsonString(json["origin"]!["icao_code"], out string icao)) 62 | { 63 | Logger.Debug($"Departure ICAO received: {icao}"); 64 | return icao; 65 | } 66 | } 67 | catch (Exception ex) 68 | { 69 | Logger.Warning($"Error while fetching SimBrief Departure ICAO (Exception: {ex.GetType().Name})"); 70 | } 71 | 72 | return ""; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Fenix2GSX/AppConfig/AircraftProfile.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppLogger; 2 | using Fenix2GSX.GSX.Services; 3 | using FenixInterface; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace Fenix2GSX.AppConfig 8 | { 9 | public enum ProfileMatchType 10 | { 11 | Default = 0, 12 | Airline = 1, 13 | Title = 2, 14 | Registration = 3, 15 | } 16 | 17 | public class AircraftProfile : IAircraftProfile 18 | { 19 | public virtual string Name { get; set; } = "default"; 20 | public virtual ProfileMatchType MatchType { get; set; } = ProfileMatchType.Default; 21 | public virtual string MatchString { get; set; } = ""; 22 | 23 | public virtual void Copy(AircraftProfile profile) 24 | { 25 | Name = profile.Name; 26 | MatchType = profile.MatchType; 27 | MatchString = profile.MatchString; 28 | } 29 | 30 | public override string ToString() 31 | { 32 | if (MatchType != ProfileMatchType.Default) 33 | return $"{Name}: {MatchType} ~ '{MatchString}'"; 34 | else 35 | return $"{Name}: {MatchType}"; 36 | } 37 | 38 | public override bool Equals(object? obj) 39 | { 40 | if (obj is AircraftProfile profile) 41 | return this.Name.Equals(profile.Name); 42 | else 43 | return false; 44 | } 45 | 46 | public override int GetHashCode() 47 | { 48 | return Name?.GetHashCode() ?? 0 ^ MatchType.GetHashCode() ^ MatchString?.GetHashCode() ?? 0; 49 | } 50 | 51 | //Settings 52 | public virtual int ConnectPca { get; set; } = 2; // 0 => false | 1 => true | 2 => only on jetway stand 53 | public virtual bool DoorStairHandling { get; set; } = true; 54 | public virtual bool DoorStairIncludeL2 { get; set; } = false; 55 | public virtual bool DoorCargoHandling { get; set; } = true; 56 | public virtual int FinalDelayMin { get; set; } = 90; 57 | public virtual int FinalDelayMax { get; set; } = 150; 58 | public virtual int ChockDelayMin { get; set; } = 10; 59 | public virtual int ChockDelayMax { get; set; } = 20; 60 | public virtual bool FuelSaveLoadFob { get; set; } = true; 61 | public virtual bool RandomizePax { get; set; } = true; 62 | public virtual double ChancePerSeat { get; set; } = 0.025; 63 | public virtual double RefuelRateKgSec { get; set; } = 28; 64 | public virtual bool UseRefuelTimeTarget { get; set; } = false; 65 | public virtual int RefuelTimeTargetSeconds { get; set; } = 300; 66 | public virtual bool DoorsCargoKeepOpenOnLoaded { get; set; } = false; 67 | public virtual bool DoorsCargoKeepOpenOnUnloaded { get; set; } = false; 68 | public virtual bool OperatorAutoSelect { get; set; } = true; 69 | public virtual List OperatorPreferences { get; set; } = []; 70 | public virtual List CompanyHubs { get; set; } = []; 71 | public virtual bool SkipFuelOnTankering { get; set; } = true; 72 | public virtual bool CallReposition { get; set; } = true; 73 | public virtual bool CallJetwayStairsOnPrep { get; set; } = true; 74 | public virtual bool CallJetwayStairsDuringDeparture { get; set; } = false; 75 | public virtual bool CallJetwayStairsOnArrival { get; set; } = true; 76 | public virtual int RemoveStairsAfterDepature { get; set; } = 2; // 0 => false | 1 => true | 2 => only on jetway stand 77 | public virtual int AttachTugDuringBoarding { get; set; } = 1; // 0 => not answer | 1 => no | 2 => yes 78 | public virtual int CallPushbackWhenTugAttached { get; set; } = 2; // 0 => false | 1 => after Departure Services | 2 => after Final LS 79 | public virtual bool SkipCrewQuestion { get; set; } = true; 80 | public virtual bool SkipFollowMe { get; set; } = true; 81 | public virtual bool KeepDirectionMenuOpen { get; set; } = true; 82 | public virtual bool CloseDoorsOnFinal { get; set; } = true; 83 | public virtual bool RemoveJetwayStairsOnFinal { get; set; } = true; 84 | public virtual bool CallPushbackOnBeacon { get; set; } = false; 85 | public virtual bool ClearGroundEquipOnBeacon { get; set; } = true; 86 | public virtual bool CallDeboardOnArrival { get; set; } = true; 87 | public virtual bool AnswerCabinCallGround { get; set; } = true; 88 | public virtual int DelayCabinCallGround { get; set; } = 4000; 89 | public virtual bool AnswerCabinCallAir { get; set; } = true; 90 | public virtual int DelayCabinCallAir { get; set; } = 2500; 91 | public virtual SortedDictionary DepartureServices { get; set; } = new() 92 | { 93 | { 0, new ServiceConfig(GsxServiceType.Refuel, GsxServiceActivation.AfterCalled) }, 94 | { 1, new ServiceConfig(GsxServiceType.Catering, GsxServiceActivation.AfterCalled) }, 95 | { 2, new ServiceConfig(GsxServiceType.Lavatory, GsxServiceActivation.Skip) }, 96 | { 3, new ServiceConfig(GsxServiceType.Water, GsxServiceActivation.AfterRequested) }, 97 | { 4, new ServiceConfig(GsxServiceType.Boarding, GsxServiceActivation.AfterAllCompleted) }, 98 | }; 99 | 100 | public virtual bool IsCompanyHub(string icao) 101 | { 102 | try 103 | { 104 | if (string.IsNullOrEmpty(icao)) 105 | return false; 106 | 107 | foreach (var hub in CompanyHubs) 108 | { 109 | if (hub.StartsWith(icao, StringComparison.InvariantCultureIgnoreCase)) 110 | return true; 111 | } 112 | } 113 | catch (Exception ex) 114 | { 115 | Logger.LogException(ex); 116 | } 117 | return false; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Fenix2GSX/AppConfig/AudioMapping.cs: -------------------------------------------------------------------------------- 1 | using Fenix2GSX.Audio; 2 | using System; 3 | using System.Text.Json.Serialization; 4 | 5 | namespace Fenix2GSX.AppConfig 6 | { 7 | public class AudioMapping : IComparable 8 | { 9 | public virtual AudioChannel Channel { get; set; } 10 | public virtual string Device { get; set; } 11 | public virtual string Binary { get; set; } 12 | public virtual bool UseLatch { get; set; } 13 | 14 | public AudioMapping() { } 15 | 16 | public AudioMapping(AudioChannel channel, string device, string binary, bool useLatch = true) 17 | { 18 | Channel = channel; 19 | Device = device; 20 | Binary = binary; 21 | UseLatch = useLatch; 22 | } 23 | 24 | [JsonIgnore] 25 | public virtual string DeviceName { get => string.IsNullOrWhiteSpace(Device) ? "All" : Device; set { Device = string.IsNullOrWhiteSpace(value) || value == "All" ? "" : value; } } 26 | 27 | public int CompareTo(AudioMapping? other) 28 | { 29 | return Channel.CompareTo(other.Channel); 30 | } 31 | 32 | public override bool Equals(object? obj) 33 | { 34 | if (obj is not AudioMapping mapping) 35 | return false; 36 | else 37 | return Channel == mapping.Channel && Device.Equals(mapping.Device) && Binary.Equals(mapping.Binary) && UseLatch == mapping.UseLatch; 38 | } 39 | 40 | public override int GetHashCode() 41 | { 42 | return Channel.GetHashCode() ^ Device.GetHashCode() ^ Binary.GetHashCode() ^ UseLatch.GetHashCode(); 43 | } 44 | 45 | public override string ToString() 46 | { 47 | return $"Channel: {Channel} - Binary '{Binary}' @ Device '{DeviceName}' (UseLatch: {UseLatch})"; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Fenix2GSX/AppConfig/Definition.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework.AppConfig; 2 | using System.IO; 3 | 4 | namespace Fenix2GSX.AppConfig 5 | { 6 | public class Definition : ProductDefinitionBase 7 | { 8 | public override int BuildConfigVersion { get; } = 6; 9 | public override string ProductName => "Fenix2GSX"; 10 | public override string ProductExePath => Path.Join(Path.Join(ProductPath, "bin"), ProductExe); 11 | public override bool ProductVersionCheckDev => true; 12 | public override bool RequireSimRunning => true; 13 | public override bool WaitForSim => true; 14 | public override bool SingleInstance => true; 15 | public override bool MainWindowShowOnStartup => false; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Fenix2GSX/AppConfig/ServiceConfig.cs: -------------------------------------------------------------------------------- 1 | using Fenix2GSX.GSX.Services; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text.Json.Serialization; 5 | 6 | namespace Fenix2GSX.AppConfig 7 | { 8 | public class ServiceConfig 9 | { 10 | [JsonIgnore] 11 | public static Dictionary TextServiceActivations { get; } = new() 12 | { 13 | { GsxServiceActivation.Skip, "Skip / Ignore" }, 14 | { GsxServiceActivation.Manual, "Manual by User" }, 15 | { GsxServiceActivation.AfterCalled, "Previous Service called" }, 16 | { GsxServiceActivation.AfterRequested, "Previous Service requested" }, 17 | { GsxServiceActivation.AfterActive, "Previous Service active" }, 18 | { GsxServiceActivation.AfterPrevCompleted, "Previous Service completed" }, 19 | { GsxServiceActivation.AfterAllCompleted, "All Services completed" }, 20 | }; 21 | 22 | [JsonIgnore] 23 | public static Dictionary TextServiceConstraints { get; } = new() 24 | { 25 | { GsxServiceConstraint.NoneAlways, "None" }, 26 | { GsxServiceConstraint.FirstLeg, "Only Departure" }, 27 | { GsxServiceConstraint.TurnAround, "Only Turn" }, 28 | { GsxServiceConstraint.CompanyHub, "Only on Hub" }, 29 | }; 30 | 31 | public virtual GsxServiceType ServiceType { get; set; } = GsxServiceType.Unknown; 32 | public virtual GsxServiceActivation ServiceActivation { get; set; } = GsxServiceActivation.Manual; 33 | [JsonIgnore] 34 | public virtual string ServiceActivationName => TextServiceActivations[ServiceActivation]; 35 | public virtual GsxServiceConstraint ServiceConstraint { get; set; } = GsxServiceConstraint.NoneAlways; 36 | [JsonIgnore] 37 | public virtual string ServiceConstraintName => TextServiceConstraints[ServiceConstraint]; 38 | public virtual TimeSpan MinimumFlightDuration { get; set; } = TimeSpan.Zero; 39 | [JsonIgnore] 40 | public virtual bool HasDurationConstraint => MinimumFlightDuration > TimeSpan.Zero; 41 | [JsonIgnore] 42 | public virtual int ActivationCount { get; set; } = 0; 43 | 44 | public ServiceConfig(){ } 45 | 46 | public ServiceConfig(GsxServiceType type, GsxServiceActivation activation) : this(type, activation, TimeSpan.Zero, GsxServiceConstraint.NoneAlways) { } 47 | 48 | public ServiceConfig(GsxServiceType type, GsxServiceActivation activation, TimeSpan duration, GsxServiceConstraint constraint) 49 | { 50 | ServiceType = type; 51 | ServiceActivation = activation; 52 | MinimumFlightDuration = duration; 53 | ServiceConstraint = constraint; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Fenix2GSX/AppService.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework.Messages; 2 | using CFIT.AppFramework.Services; 3 | using CFIT.AppLogger; 4 | using CFIT.AppTools; 5 | using Fenix2GSX.AppConfig; 6 | using Fenix2GSX.Audio; 7 | using Fenix2GSX.GSX; 8 | using System; 9 | using System.IO; 10 | using System.Threading.Tasks; 11 | 12 | namespace Fenix2GSX 13 | { 14 | public enum AppResetRequest 15 | { 16 | None = 0, 17 | App = 1, 18 | AppGsx = 2, 19 | } 20 | 21 | public class AppService(Config config) : AppService(config) 22 | { 23 | public virtual GsxController GsxService { get; protected set; } 24 | public virtual AudioController AudioService { get; protected set; } 25 | public virtual AppResetRequest ResetRequested { get; set; } = AppResetRequest.None; 26 | public virtual bool IsFenixAircraft => SimConnect.AircraftString.Contains(Config.FenixAircraftString, StringComparison.InvariantCultureIgnoreCase); 27 | 28 | protected override void CreateServiceControllers() 29 | { 30 | GsxService = new GsxController(Config); 31 | AudioService = new AudioController(Config); 32 | } 33 | 34 | protected override void InitReceivers() 35 | { 36 | base.InitReceivers(); 37 | ReceiverStore.Add().OnMessage += OnSessionReady; 38 | ReceiverStore.Add().OnMessage += OnSessionEnded; 39 | } 40 | 41 | protected virtual void OnSessionEnded(MsgSessionEnded obj) 42 | { 43 | if (GsxService.IsActive) 44 | { 45 | Logger.Debug($"Stop GsxService"); 46 | GsxService.Stop(); 47 | } 48 | 49 | if (AudioService.IsActive) 50 | { 51 | Logger.Debug($"Stop AudioService"); 52 | AudioService.Stop(); 53 | } 54 | } 55 | 56 | protected virtual void OnSessionReady(MsgSessionReady obj) 57 | { 58 | if (!IsFenixAircraft) 59 | return; 60 | 61 | if (App.Config.RunGsxService) 62 | { 63 | Logger.Debug($"Start GsxService"); 64 | GsxService.Start(); 65 | } 66 | 67 | if (App.Config.RunAudioService) 68 | { 69 | Logger.Debug($"Start AudioService"); 70 | AudioService.Start(); 71 | } 72 | } 73 | 74 | public virtual async Task RestartGsx() 75 | { 76 | Logger.Debug($"Kill Couatl Process"); 77 | Sys.KillProcess(App.Config.BinaryGsx2020); 78 | Sys.KillProcess(App.Config.BinaryGsx2024); 79 | 80 | Logger.Debug($"Awaiting Couatl Restart ({Config.DelayGsxBinaryStart}ms) ..."); 81 | await Task.Delay(Config.DelayGsxBinaryStart, App.Token); 82 | 83 | if (SimService.IsMsfs2020 && !Sys.GetProcessRunning(App.Config.BinaryGsx2020)) 84 | { 85 | Logger.Debug($"Starting Process {App.Config.BinaryGsx2020}"); 86 | string dir = Path.Join(GsxService.PathInstallation, "couatl64"); 87 | Sys.StartProcess(Path.Join(dir, $"{App.Config.BinaryGsx2020}.exe"), dir); 88 | } 89 | 90 | if (SimService.IsMsfs2024 && !Sys.GetProcessRunning(App.Config.BinaryGsx2024)) 91 | { 92 | Logger.Debug($"Starting Process {App.Config.BinaryGsx2024}"); 93 | string dir = Path.Join(GsxService.PathInstallation, "couatl64"); 94 | Sys.StartProcess(Path.Join(dir, $"{App.Config.BinaryGsx2024}.exe"), dir); 95 | } 96 | } 97 | 98 | protected override async Task MainLoop() 99 | { 100 | await Task.Delay(App.Config.TimerGsxCheck, App.Token); 101 | 102 | if (ResetRequested > AppResetRequest.None) 103 | { 104 | Logger.Debug($"Reset was requested: {ResetRequested}"); 105 | OnSessionEnded(null); 106 | if (ResetRequested == AppResetRequest.App) 107 | await Task.Delay(1500, App.Token); 108 | else 109 | await RestartGsx(); 110 | OnSessionReady(null); 111 | ResetRequested = AppResetRequest.None; 112 | } 113 | } 114 | 115 | protected override void FreeResources() 116 | { 117 | base.FreeResources(); 118 | ReceiverStore.Remove().OnMessage -= OnSessionReady; 119 | ReceiverStore.Remove().OnMessage -= OnSessionEnded; 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Fenix2GSX/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly:ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /Fenix2GSX/Audio/SessionManager.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppLogger; 2 | using CFIT.AppTools; 3 | using Fenix2GSX.AppConfig; 4 | using System.Collections.Concurrent; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace Fenix2GSX.Audio 9 | { 10 | public class SessionManager(AudioController controller) 11 | { 12 | protected virtual AudioController Controller { get; } = controller; 13 | protected virtual DeviceManager DeviceManager => Controller.DeviceManager; 14 | protected virtual Config Config => Controller.Config; 15 | protected virtual ConcurrentDictionary> MappedAudioSessions { get; } = []; 16 | public virtual bool HasEmptySearches => MappedAudioSessions.Any(c => c.Value.Any(s => s.SearchCounter > Config.AudioProcessMaxSearchCount)); 17 | public virtual bool HasInactiveSessions => MappedAudioSessions.Any(c => c.Value.Any(s => s.SessionControls.Any(sc => sc.State != CoreAudio.AudioSessionState.AudioSessionStateActive))); 18 | 19 | public virtual void RegisterMappings() 20 | { 21 | foreach (var mapping in Config.AudioMappings) 22 | RegisterMapping(mapping); 23 | } 24 | 25 | protected virtual void RegisterMapping(AudioMapping mapping) 26 | { 27 | if (!MappedAudioSessions.ContainsKey(mapping.Channel)) 28 | MappedAudioSessions.Add(mapping.Channel, []); 29 | 30 | var session = new AudioSession(Controller, mapping); 31 | MappedAudioSessions[mapping.Channel].Add(session); 32 | Logger.Debug($"Registered AudioSession {session}"); 33 | } 34 | 35 | public virtual void UnregisterMappings() 36 | { 37 | foreach (var channel in MappedAudioSessions) 38 | foreach (var session in channel.Value.ToList()) 39 | UnregisterMapping(session.Mapping); 40 | } 41 | 42 | protected virtual void UnregisterMapping(AudioMapping mapping) 43 | { 44 | if (!MappedAudioSessions.TryGetValue(mapping.Channel, out List? sessionList)) 45 | return; 46 | 47 | var list = sessionList.Where(s => s.Binary == mapping.Binary && s.Device == mapping.Device).ToList(); 48 | foreach (var item in list) 49 | { 50 | try { item.RestoreVolumes(); } catch { } 51 | try { item.ClearSimSubscriptions(); } catch { } 52 | sessionList.Remove(item); 53 | Logger.Debug($"Removed AudioSession {item}"); 54 | } 55 | } 56 | 57 | public virtual void Clear() 58 | { 59 | MappedAudioSessions.Clear(); 60 | } 61 | 62 | public virtual bool CheckProcesses(bool force = false) 63 | { 64 | bool result = false; 65 | 66 | foreach (var channel in MappedAudioSessions) 67 | foreach (var session in channel.Value) 68 | if (session.CheckProcess(force) != 0) 69 | result = true; 70 | 71 | return result; 72 | } 73 | 74 | public virtual void RestoreVolumes() 75 | { 76 | foreach (var channel in MappedAudioSessions) 77 | foreach (var session in channel.Value) 78 | session.RestoreVolumes(); 79 | } 80 | 81 | public virtual void CheckSessions(bool force = false) 82 | { 83 | foreach (var channel in MappedAudioSessions) 84 | { 85 | foreach (var session in channel.Value) 86 | { 87 | if (session.IsActive && (session.SessionControls.Count == 0 || force)) 88 | { 89 | if (force) 90 | Logger.Debug($"Query SessionControls for AudioSession {session}"); 91 | else 92 | Logger.Verbose($"Query SessionControls for AudioSession {session}"); 93 | var sessions = DeviceManager.GetAudioSessions(session); 94 | if (force || sessions.Count != session.SessionControls.Count) 95 | session.SessionControls.Clear(); 96 | 97 | if (session.SessionControls.Count == 0 && sessions.Count != 0) 98 | { 99 | session.SetSessionList(sessions); 100 | session.SynchControls(); 101 | Logger.Debug($"Added {sessions.Count} SessionControls to AudioSession {session}"); 102 | } 103 | else if (session.SessionControls.Count == 0) 104 | session.SearchCounter++; 105 | } 106 | } 107 | } 108 | } 109 | 110 | public virtual void SynchControls() 111 | { 112 | foreach (var channel in MappedAudioSessions) 113 | foreach (var session in channel.Value) 114 | session.SynchControls(); 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /Fenix2GSX/BuildApp.ps1: -------------------------------------------------------------------------------- 1 | # POST 2 | # pwsh -ExecutionPolicy Unrestricted -file "$(ProjectDir)BuildApp.ps1" $(Configuration) $(SolutionDir) $(ProjectDir) "APPNAME" "AppConfig.json" 3 | 4 | ######### CONFIG 5 | $cfgDeploy = $false 6 | $cfgCleanDeploy = $false # !!! 7 | $cfgCleanLog = $false 8 | $cfgResetConfig = $false # !!! 9 | $cfgBuildInstaller = $true 10 | 11 | if ($args[0] -eq "*Undefined*") { 12 | exit 0 13 | } 14 | 15 | if ($args[1] -eq "*Undefined*") { 16 | exit 0 17 | } 18 | 19 | try { 20 | $buildConfiguration = $args[0] 21 | if (-not $buildConfiguration -or $buildConfiguration -eq "Debug") { 22 | exit 0 23 | } 24 | 25 | $pathBase = $args[1] 26 | $pathProject = $args[2] 27 | $appName = $args[3] 28 | $appCfg = $args[4] 29 | $msBuildDir = "C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\amd64" 30 | 31 | $pathPublish = Join-Path $pathProject "bin\publish" 32 | $pathDeploy = Join-Path (Join-Path ($env:APPDATA) $appName) "bin" 33 | $pathInstallerPayload = Join-Path $pathBase "Installer\Payload" 34 | 35 | $projectFile = Join-Path $pathProject "$appName.csproj" 36 | [xml]$projectXml = Get-Content $projectFile 37 | $appVersion = "$($projectXml.Project.ChildNodes.Version)".Trim() 38 | 39 | ######### BUILD 40 | ## Create Lock 41 | Write-Host $pathBase 42 | cd $pathBase 43 | if (-not (Test-Path -Path "build.lck")) { 44 | "lock" | Out-File -File "build.lck" 45 | } 46 | else { 47 | Write-Host "Lock active - sure?" 48 | exit 0 49 | } 50 | 51 | if (-not $buildConfiguration) { 52 | $buildConfiguration = "Release" 53 | } 54 | Write-Host ("Build Configuration: '$buildConfiguration'") 55 | 56 | ## Build App 57 | Write-Host "dotnet publish for $appName (v$appVersion) ..." 58 | Remove-Item -Recurse -Force -Path ($pathPublish + "\*") -ErrorAction SilentlyContinue | Out-Null 59 | cd $pathProject 60 | dotnet publish -p:PublishProfile=FolderProfile$buildConfiguration -c $buildConfiguration --verbosity quiet 61 | if ($buildConfiguration -eq "Release") { 62 | Remove-Item -Recurse -Force -Path ($pathPublish + "\*.pdb") -ErrorAction SilentlyContinue | Out-Null 63 | } 64 | 65 | 66 | ######### DEPLOY 67 | ## Stop App 68 | if ($cfgDeploy) { 69 | Write-Host "Stopping $appName ..." 70 | Get-Process -Name $appName -ErrorAction SilentlyContinue | Stop-Process –force -ErrorAction SilentlyContinue 71 | Sleep(2) 72 | } 73 | 74 | ## Clean Deploy 75 | if ($cfgDeploy -and $cfgCleanDeploy) { 76 | Write-Host "Removing old App Files ..." 77 | 78 | New-Item -Type Directory -Path $pathDeploy -ErrorAction SilentlyContinue | Out-Null 79 | Remove-Item -Recurse -Force -Path ($pathDeploy + "\*") | Out-Null 80 | Copy-Item -Path ($pathPublish + "\*") -Destination $pathDeploy -Recurse -Force | Out-Null 81 | } 82 | ## Update Deploy 83 | elseif ($cfgDeploy) { 84 | Write-Host "Copy new Binaries ..." 85 | Copy-Item -Path ($pathPublish + "\*") -Destination $pathDeploy -Force | Out-Null 86 | 87 | if ($cfgResetConfig) { 88 | $configPath = Join-Path $pathDeploy $appCfg 89 | Write-Host "Resetting Configuration ..." 90 | if ((Test-Path $configPath)) { 91 | Remove-Item -Path $configPath -Force -ErrorAction SilentlyContinue | Out-Null 92 | } 93 | } 94 | 95 | if ($cfgDeploy -and $cfgCleanLog) { 96 | Write-Host "Removing Logs ..." 97 | New-Item -Type Directory -Path $pathDeploy -ErrorAction SilentlyContinue | Out-Null 98 | Remove-Item -Recurse -Force -Path ($pathDeploy + "\log\*") -ErrorAction SilentlyContinue | Out-Null 99 | Remove-Item -Recurse -Force -Path ($pathDeploy + "\logs\*") -ErrorAction SilentlyContinue | Out-Null 100 | } 101 | } 102 | 103 | 104 | ######### INSTALLER 105 | if ($cfgBuildInstaller -and $buildConfiguration -eq "Release") { 106 | Write-Host "msbuild for Installer ..." 107 | cd $msBuildDir 108 | .\msbuild.exe (Join-Path $pathBase "$appName.sln") /t:Installer:rebuild /p:Configuration="Release" /p:BuildProjectReferences=false -verbosity:minimal 109 | if ($cfgDeploy) { 110 | Write-Host "Copy version.json ..." 111 | Copy-Item -Path (Join-Path $pathInstallerPayload "version.json") -Destination $pathDeploy -Force -ErrorAction SilentlyContinue | Out-Null 112 | } 113 | } 114 | 115 | 116 | ## Remove lock 117 | cd $pathBase 118 | if ((Test-Path -Path "build.lck")) { 119 | Remove-Item -Path "build.lck" 120 | } 121 | 122 | Write-Host "SUCCESS: Build complete!" 123 | exit 0 124 | } 125 | catch { 126 | Write-Host "FAILED: Exception in BuildApp.ps1!" 127 | cd $pathBase 128 | Remove-Item -Path "build.lck" 129 | exit -1 130 | } -------------------------------------------------------------------------------- /Fenix2GSX/Fenix2GSX.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework; 2 | using CFIT.AppLogger; 3 | using Fenix2GSX.AppConfig; 4 | using Fenix2GSX.UI; 5 | using Fenix2GSX.UI.NotifyIcon; 6 | using System; 7 | 8 | namespace Fenix2GSX 9 | { 10 | public class Fenix2GSX(Type windowType) : SimApp(windowType, typeof(NotifyIconModelExt)) 11 | { 12 | [STAThread] 13 | public static int Main(string[] args) 14 | { 15 | try 16 | { 17 | var app = new Fenix2GSX(typeof(AppWindow)); 18 | return app.Start(args); 19 | } 20 | catch (Exception ex) 21 | { 22 | Logger.LogException(ex); 23 | return -1; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Fenix2GSX/Fenix2GSX.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net8.0-windows10.0.17763.0 6 | annotations 7 | disable 8 | true 9 | all 10 | x64 11 | Fenix2GSX.Fenix2GSX 12 | 13 | 14 | 15 | build$([System.DateTime]::UtcNow.ToString("yyyy.MM.dd.HHmm")) 16 | 0.5.0 17 | $(AssemblyName) 18 | Fragtality 19 | Fragtality 20 | UI\Icons\AppIcon.ico 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | FenixInterface.dll 57 | 58 | 59 | Microsoft.FlightSimulator.SimConnect.dll 60 | true 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | Always 82 | true 83 | 84 | 85 | 86 | 87 | 88 | Never 89 | 90 | 91 | Always 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /Fenix2GSX/FenixInterface.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/FenixInterface.dll -------------------------------------------------------------------------------- /Fenix2GSX/GSX/GsxConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Fenix2GSX.GSX 2 | { 3 | public static class GsxConstants 4 | { 5 | //Paths 6 | public static string RegPath { get { return @"HKEY_CURRENT_USER\SOFTWARE\FSDreamTeam"; } } 7 | public static string RegValue { get { return "root"; } } 8 | public static string PathDefault { get { return @"C:\Program Files (x86)\Addon Manager"; } } 9 | public static string RelativePathMenu { get { return @"\MSFS\fsdreamteam-gsx-pro\html_ui\InGamePanels\FSDT_GSX_Panel\menu"; } } 10 | 11 | //Events 12 | public static string EventMenu { get; } = "EXTERNAL_SYSTEM_TOGGLE"; 13 | 14 | //Variables 15 | public static string VarCouatlStarted { get; } = "L:FSDT_GSX_COUATL_STARTED"; 16 | public static string VarCouatlStartProgress { get; } = "L:FSDT_GSX_COUATL_STARTED_PROGRESS"; 17 | public static string VarCouatlStartStatus { get; } = "L:FSDT_GSX_COUATL_STARTED_STATUS"; 18 | public static string VarMenuOpen { get; } = "L:FSDT_GSX_MENU_OPEN"; 19 | public static string VarMenuChoice { get; } = "L:FSDT_GSX_MENU_CHOICE"; 20 | public static string VarReadProgFuel { get; } = "L:FSDT_GSX_SETTINGS_PROGRESS_REFUEL"; 21 | public static string VarSetProgFuel { get; } = "L:FSDT_GSX_SET_PROGRESS_REFUEL"; 22 | public static string VarReadCustFuel { get; } = "L:FSDT_GSX_SETTINGS_DETECT_CUST_REFUEL"; 23 | public static string VarSetCustFuel { get; } = "L:FSDT_GSX_SET_DETECT_CUST_REFUEL"; 24 | public static string VarReadAutoMode { get; } = "L:FSDT_GSX_SETTINGS_AUTOMODE"; 25 | public static string VarSetAutoMode { get; } = "L:FSDT_GSX_SET_AUTOMODE"; 26 | public static string VarServiceJetway { get; } = "L:FSDT_GSX_JETWAY"; 27 | public static string VarServiceJetwayOperation { get; } = "L:FSDT_GSX_OPERATEJETWAYS_STATE"; 28 | public static string VarServiceStairs { get; } = "L:FSDT_GSX_STAIRS"; 29 | public static string VarServiceStairsOperation { get; } = "L:FSDT_GSX_OPERATESTAIRS_STATE"; 30 | public static string VarServiceRefuel { get; } = "L:FSDT_GSX_REFUELING_STATE"; 31 | public static string VarServiceRefuelHose { get; } = "L:FSDT_GSX_FUELHOSE_CONNECTED"; 32 | public static string VarServiceCatering { get; } = "L:FSDT_GSX_CATERING_STATE"; 33 | public static string VarServiceBoarding { get; } = "L:FSDT_GSX_BOARDING_STATE"; 34 | public static string VarServiceDeboarding { get; } = "L:FSDT_GSX_DEBOARDING_STATE"; 35 | public static string VarServiceDeparture { get; } = "L:FSDT_GSX_DEPARTURE_STATE"; 36 | public static string VarServiceGpu { get; } = "L:FSDT_GSX_GPU_STATE"; 37 | public static string VarPusbackStatus { get; } = "L:FSDT_GSX_PUSHBACK_STATUS"; 38 | public static string VarBypassPin { get; } = "L:FSDT_GSX_BYPASS_PIN"; 39 | public static string VarServiceDeice { get; } = "L:FSDT_GSX_DEICING_STATE"; 40 | public static string VarServiceLavatory { get; } = "L:FSDT_GSX_LAVATORY_STATE"; 41 | public static string VarServiceWater { get; } = "L:FSDT_GSX_WATER_STATE"; 42 | public static string VarPaxTarget { get; } = "L:FSDT_GSX_NUMPASSENGERS"; 43 | public static string VarPaxTotalBoard { get; } = "L:FSDT_GSX_NUMPASSENGERS_BOARDING_TOTAL"; 44 | public static string VarPaxTotalDeboard { get; } = "L:FSDT_GSX_NUMPASSENGERS_DEBOARDING_TOTAL"; 45 | public static string VarCargoPercentBoard { get; } = "L:FSDT_GSX_BOARDING_CARGO_PERCENT"; 46 | public static string VarCargoPercentDeboard { get; } = "L:FSDT_GSX_DEBOARDING_CARGO_PERCENT"; 47 | public static string VarNoCrewBoard { get; } = "L:FSDT_GSX_CREW_NOT_BOARDING"; 48 | public static string VarNoPilotsBoard { get; } = "L:FSDT_GSX_PILOTS_NOT_BOARDING"; 49 | public static string VarNoCrewDeboard { get; } = "L:FSDT_GSX_CREW_NOT_DEBOARDING"; 50 | public static string VarNoPilotsDeboard { get; } = "L:FSDT_GSX_PILOTS_NOT_DEBOARDING"; 51 | public static string VarDoorToggleCargo1 { get; } = "L:FSDT_GSX_AIRCRAFT_CARGO_1_TOGGLE"; 52 | public static string VarDoorToggleCargo2 { get; } = "L:FSDT_GSX_AIRCRAFT_CARGO_2_TOGGLE"; 53 | 54 | //Menu 55 | public static string GsxChoice { get; } = "[GSX choice]"; 56 | public static string MenuGate { get; } = "Activate Services at"; 57 | public static string MenuParkingSelect { get; } = "Select Parking at"; 58 | public static string MenuParkingChange { get; } = "Change parking or service"; 59 | public static string MenuAdditionalServices { get; } = "Activate Ground Services"; 60 | public static string MenuOperatorHandling { get; } = "Select handling operator"; 61 | public static string MenuOperatorCater { get; } = "Select catering operator"; 62 | public static string MenuTugAttach { get; } = "Attach Pushback Tug"; 63 | public static string MenuPushbackInterrupt { get; } = "Interrupt pushback"; 64 | public static string MenuPushbackDirection { get; } = "Select pushback direction"; 65 | public static string MenuPushbackChange { get; } = "Change Direction"; 66 | public static string MenuDeiceOnPush { get; } = "Ice warning: do you request the de-icing treatment"; 67 | public static string MenuPushbackConfirm { get; } = "Interrupt pushback"; 68 | public static string MenuPushbackRequest { get; } = "Do you want to request"; 69 | public static string MenuFollowMe { get; } = "Request FollowMe"; 70 | public static string MenuDeboardCrew { get; } = "Do you want to deboard crew"; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/GsxMessages.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework.MessageService; 2 | 3 | namespace Fenix2GSX.GSX 4 | { 5 | public class MessageDataGsx(GsxController controller, object value = null) : AppMessageData(controller, value) 6 | { 7 | public virtual GsxController Controller { get { return Sender as GsxController; } } 8 | } 9 | 10 | public class MessageGsx(MessageDataGsx value) : AppMessage(value) 11 | { 12 | public virtual MessageDataGsx Data { get { return Value as MessageDataGsx; } } 13 | 14 | public static TMessage Create(GsxController controller, object value = null) where TMessage : MessageGsx 15 | { 16 | return Create(controller, value); 17 | } 18 | } 19 | 20 | public class MsgGsxMenuReady(MessageDataGsx value) : MessageGsx(value) { } 21 | 22 | public class MsgGsxMenuReceived(MessageDataGsx value) : MessageGsx(value) { } 23 | 24 | public class MsgGsxCouatlStarted(MessageDataGsx value) : MessageGsx(value) { } 25 | 26 | public class MsgGsxCouatlStopped(MessageDataGsx value) : MessageGsx(value) { } 27 | } 28 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/Menu/GsxMenuCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Fenix2GSX.GSX.Menu 2 | { 3 | public enum GsxMenuCommandType 4 | { 5 | Number = 0, 6 | DummyWait = 1, 7 | Operator = 2, 8 | Reset = 3, 9 | } 10 | 11 | public class GsxMenuCommand(int number, string title = "", bool open = false, GsxMenuCommandType type = GsxMenuCommandType.Number) 12 | { 13 | public int Number { get; } = number; 14 | public string Title { get; } = title; 15 | public bool HasTitle => !string.IsNullOrWhiteSpace(Title); 16 | public bool OpenMenu { get; set; } = open; 17 | public bool NoHide { get; set; } = false; 18 | public bool WaitReady { get; set; } = open; 19 | public GsxMenuCommandType Type { get; } = type; 20 | 21 | public static GsxMenuCommand CreateOperator() 22 | { 23 | return new GsxMenuCommand(0, "", false, GsxMenuCommandType.Operator); 24 | } 25 | 26 | public static GsxMenuCommand CreateDummy(bool open = false) 27 | { 28 | return new GsxMenuCommand(0, "", open, GsxMenuCommandType.DummyWait); 29 | } 30 | 31 | public static GsxMenuCommand CreateReset() 32 | { 33 | return new GsxMenuCommand(0, "", false, GsxMenuCommandType.Reset); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/Menu/GsxMenuSequence.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Fenix2GSX.GSX.Menu 5 | { 6 | public class GsxMenuSequence 7 | { 8 | public virtual List Commands { get; } = []; 9 | public virtual bool IsExecuting { get; set; } = false; 10 | public virtual bool IsSuccess { get; set; } = false; 11 | public virtual bool IgnoreGsxState { get; set; } = false; 12 | public virtual Action CallbackCompleted { get; set; } = null; 13 | 14 | public virtual void Reset() 15 | { 16 | IsSuccess = false; 17 | IsExecuting = false; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/Menu/GsxOperator.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppLogger; 2 | using Fenix2GSX.AppConfig; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace Fenix2GSX.GSX.Menu 8 | { 9 | public class GsxOperator(string title, int number, bool gsxChoice = false) 10 | { 11 | public virtual string Title { get; set; } = title; 12 | public virtual int Number { get; set; } = number; 13 | public virtual bool GsxChoice { get; set; } = gsxChoice; 14 | 15 | public static GsxOperator OperatorSelection(AircraftProfile profile, List menuLines) 16 | { 17 | GsxOperator gsxOperator = null; 18 | try 19 | { 20 | var operators = ParseOperators(menuLines); 21 | 22 | gsxOperator = operators?.Where(o => o.GsxChoice)?.FirstOrDefault(); 23 | foreach (var preference in profile.OperatorPreferences) 24 | { 25 | var query = operators?.Where(o => o.Title.Contains(preference, StringComparison.InvariantCultureIgnoreCase)); 26 | if (query?.Any() == true) 27 | { 28 | gsxOperator = query?.FirstOrDefault(); 29 | Logger.Debug($"Found preferred Operator: '{gsxOperator?.Title ?? "null"}'"); 30 | break; 31 | } 32 | } 33 | } 34 | catch (Exception ex) 35 | { 36 | Logger.LogException(ex); 37 | } 38 | 39 | return gsxOperator; 40 | } 41 | 42 | public static List ParseOperators(List menuLines) 43 | { 44 | int lineCounter = 1; 45 | List operators = []; 46 | foreach (var line in menuLines) 47 | { 48 | bool gsxChoice = line.Contains(GsxConstants.GsxChoice, StringComparison.InvariantCultureIgnoreCase); 49 | operators.Add(new(line.Replace(GsxConstants.GsxChoice, "").TrimEnd(), lineCounter, gsxChoice)); 50 | lineCounter++; 51 | } 52 | 53 | return operators; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/Services/GsxServiceBoarding.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppLogger; 2 | using CFIT.SimConnectLib.SimResources; 3 | using Fenix2GSX.GSX.Menu; 4 | using System; 5 | using System.Threading.Tasks; 6 | 7 | namespace Fenix2GSX.GSX.Services 8 | { 9 | public class GsxServiceBoarding(GsxController controller) : GsxService(controller) 10 | { 11 | public override GsxServiceType Type => GsxServiceType.Boarding; 12 | public virtual ISimResourceSubscription SubBoardService { get; protected set; } 13 | public virtual int PaxTarget => (int)SubPaxTarget.GetNumber(); 14 | public virtual ISimResourceSubscription SubPaxTarget { get; protected set; } 15 | public virtual int PaxTotal => (int)SubPaxTotal.GetNumber(); 16 | public virtual ISimResourceSubscription SubPaxTotal { get; protected set; } 17 | public virtual int CargoPercent => (int)SubCargoPercent.GetNumber(); 18 | public virtual ISimResourceSubscription SubCargoPercent { get; protected set; } 19 | 20 | public event Action OnPaxChange; 21 | public event Action OnCargoChange; 22 | 23 | protected override GsxMenuSequence InitCallSequence() 24 | { 25 | var sequence = new GsxMenuSequence(); 26 | sequence.Commands.Add(new(4, GsxConstants.MenuGate, true)); 27 | sequence.Commands.Add(GsxMenuCommand.CreateOperator()); 28 | sequence.Commands.Add(GsxMenuCommand.CreateDummy()); 29 | 30 | return sequence; 31 | } 32 | 33 | protected override void InitSubscriptions() 34 | { 35 | SubBoardService = SimStore.AddVariable(GsxConstants.VarServiceBoarding); 36 | SubBoardService.OnReceived += OnStateChange; 37 | 38 | SubPaxTarget = SimStore.AddVariable(GsxConstants.VarPaxTarget); 39 | SubPaxTotal = SimStore.AddVariable(GsxConstants.VarPaxTotalBoard); 40 | SubPaxTotal.OnReceived += NotifyPaxChange; 41 | SubCargoPercent = SimStore.AddVariable(GsxConstants.VarCargoPercentBoard); 42 | SubCargoPercent.OnReceived += NotifyCargoChange; 43 | 44 | SimStore.AddVariable(GsxConstants.VarNoCrewBoard); 45 | SimStore.AddVariable(GsxConstants.VarNoPilotsBoard); 46 | } 47 | 48 | protected override void DoReset() 49 | { 50 | 51 | } 52 | 53 | public override async Task Call() 54 | { 55 | await base.Call(); 56 | } 57 | 58 | protected override void NotifyStateChange() 59 | { 60 | base.NotifyStateChange(); 61 | 62 | if (IsFenixAircraft && State == GsxServiceState.Active) 63 | Controller.SubScriptSupress.WriteValue(1); 64 | } 65 | 66 | public override void FreeResources() 67 | { 68 | SubBoardService.OnReceived -= OnStateChange; 69 | SubPaxTotal.OnReceived -= NotifyPaxChange; 70 | SubCargoPercent.OnReceived -= NotifyCargoChange; 71 | 72 | SimStore.Remove(GsxConstants.VarServiceBoarding); 73 | SimStore.Remove(GsxConstants.VarPaxTarget); 74 | SimStore.Remove(GsxConstants.VarPaxTotalBoard); 75 | SimStore.Remove(GsxConstants.VarCargoPercentBoard); 76 | SimStore.Remove(GsxConstants.VarNoCrewBoard); 77 | SimStore.Remove(GsxConstants.VarNoPilotsBoard); 78 | } 79 | 80 | protected override GsxServiceState GetState() 81 | { 82 | return ReadState(SubBoardService); 83 | } 84 | 85 | public virtual bool SetPaxTarget(int num) 86 | { 87 | if (Profile.SkipCrewQuestion) 88 | { 89 | SimStore[GsxConstants.VarNoCrewBoard].WriteValue(1); 90 | SimStore[GsxConstants.VarNoPilotsBoard].WriteValue(1); 91 | } 92 | 93 | return SubPaxTarget.WriteValue(num); 94 | } 95 | 96 | protected virtual void NotifyPaxChange(ISimResourceSubscription sub, object data) 97 | { 98 | if (!IsFenixAircraft) 99 | return; 100 | 101 | if (State != GsxServiceState.Active) 102 | { 103 | Logger.Debug($"Ignoring Pax Change - Service not active"); 104 | return; 105 | } 106 | 107 | var pax = sub.GetNumber(); 108 | if (pax < 0 || pax > PaxTarget) 109 | { 110 | Logger.Warning($"Ignoring Pax Change - Value received: {pax}"); 111 | return; 112 | } 113 | 114 | OnPaxChange?.Invoke(this); 115 | } 116 | 117 | protected virtual void NotifyCargoChange(ISimResourceSubscription sub, object data) 118 | { 119 | if (!IsFenixAircraft) 120 | return; 121 | 122 | if (State != GsxServiceState.Active) 123 | { 124 | Logger.Debug($"Ignoring Cargo Change - Service not active"); 125 | return; 126 | } 127 | 128 | var cargo = sub.GetNumber(); 129 | if (cargo < 0 || cargo > 100) 130 | { 131 | Logger.Warning($"Ignoring Cargo Change - Value received: {cargo}"); 132 | return; 133 | } 134 | 135 | OnCargoChange?.Invoke(this); 136 | } 137 | 138 | protected override void NotifyCompleted() 139 | { 140 | if (!IsFenixAircraft) 141 | return; 142 | 143 | base.NotifyCompleted(); 144 | Controller.SubScriptSupress.WriteValue(0); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/Services/GsxServiceCatering.cs: -------------------------------------------------------------------------------- 1 | using CFIT.SimConnectLib.SimResources; 2 | using Fenix2GSX.GSX.Menu; 3 | 4 | namespace Fenix2GSX.GSX.Services 5 | { 6 | public class GsxServiceCatering(GsxController controller) : GsxService(controller) 7 | { 8 | public override GsxServiceType Type => GsxServiceType.Catering; 9 | public virtual ISimResourceSubscription SubCaterService { get; protected set; } 10 | 11 | protected override GsxMenuSequence InitCallSequence() 12 | { 13 | var sequence = new GsxMenuSequence(); 14 | sequence.Commands.Add(new(2, GsxConstants.MenuGate, true)); 15 | sequence.Commands.Add(GsxMenuCommand.CreateOperator()); 16 | sequence.Commands.Add(GsxMenuCommand.CreateDummy()); 17 | 18 | return sequence; 19 | } 20 | 21 | protected override void InitSubscriptions() 22 | { 23 | SubCaterService = SimStore.AddVariable(GsxConstants.VarServiceCatering); 24 | SubCaterService.OnReceived += OnStateChange; 25 | } 26 | 27 | protected override void DoReset() 28 | { 29 | 30 | } 31 | 32 | public override void FreeResources() 33 | { 34 | SubCaterService.OnReceived -= OnStateChange; 35 | 36 | SimStore.Remove(GsxConstants.VarServiceCatering); 37 | } 38 | 39 | protected override GsxServiceState GetState() 40 | { 41 | return ReadState(SubCaterService); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/Services/GsxServiceDeboarding.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppLogger; 2 | using CFIT.SimConnectLib.SimResources; 3 | using Fenix2GSX.GSX.Menu; 4 | using System; 5 | 6 | namespace Fenix2GSX.GSX.Services 7 | { 8 | public class GsxServiceDeboarding(GsxController controller) : GsxService(controller) 9 | { 10 | public override GsxServiceType Type => GsxServiceType.Deboarding; 11 | public virtual ISimResourceSubscription SubDeboardService { get; protected set; } 12 | public virtual int PaxTarget => (int)SubPaxTarget.GetNumber(); 13 | public virtual ISimResourceSubscription SubPaxTarget { get; protected set; } 14 | public virtual int PaxTotal => (int)SubPaxTotal.GetNumber(); 15 | public virtual ISimResourceSubscription SubPaxTotal { get; protected set; } 16 | public virtual int CargoPercent => (int)SubCargoPercent.GetNumber(); 17 | public virtual ISimResourceSubscription SubCargoPercent { get; protected set; } 18 | 19 | public event Action OnPaxChange; 20 | public event Action OnCargoChange; 21 | 22 | protected override GsxMenuSequence InitCallSequence() 23 | { 24 | var sequence = new GsxMenuSequence(); 25 | sequence.Commands.Add(new(1, GsxConstants.MenuGate, true)); 26 | sequence.Commands.Add(GsxMenuCommand.CreateOperator()); 27 | sequence.Commands.Add(GsxMenuCommand.CreateDummy()); 28 | 29 | return sequence; 30 | } 31 | 32 | protected override void InitSubscriptions() 33 | { 34 | SubDeboardService = SimStore.AddVariable(GsxConstants.VarServiceDeboarding); 35 | SubDeboardService.OnReceived += OnStateChange; 36 | 37 | SubPaxTarget = SimStore.AddVariable(GsxConstants.VarPaxTarget); 38 | SubPaxTotal = SimStore.AddVariable(GsxConstants.VarPaxTotalDeboard); 39 | SubPaxTotal.OnReceived += NotifyPaxChange; 40 | SubCargoPercent = SimStore.AddVariable(GsxConstants.VarCargoPercentDeboard); 41 | SubCargoPercent.OnReceived += NotifyCargoChange; 42 | 43 | SimStore.AddVariable(GsxConstants.VarNoCrewDeboard); 44 | SimStore.AddVariable(GsxConstants.VarNoPilotsDeboard); 45 | } 46 | 47 | protected override void DoReset() 48 | { 49 | 50 | } 51 | 52 | public override void FreeResources() 53 | { 54 | SubDeboardService.OnReceived -= OnStateChange; 55 | SubPaxTotal.OnReceived -= NotifyPaxChange; 56 | SubCargoPercent.OnReceived -= NotifyCargoChange; 57 | 58 | SimStore.Remove(GsxConstants.VarServiceDeboarding); 59 | SimStore.Remove(GsxConstants.VarPaxTarget); 60 | SimStore.Remove(GsxConstants.VarPaxTotalDeboard); 61 | SimStore.Remove(GsxConstants.VarCargoPercentDeboard); 62 | SimStore.Remove(GsxConstants.VarNoCrewDeboard); 63 | SimStore.Remove(GsxConstants.VarNoPilotsDeboard); 64 | } 65 | 66 | protected override GsxServiceState GetState() 67 | { 68 | return ReadState(SubDeboardService); 69 | } 70 | 71 | public virtual bool SetPaxTarget(int num) 72 | { 73 | if (Profile.SkipCrewQuestion) 74 | { 75 | SimStore[GsxConstants.VarNoCrewDeboard].WriteValue(1); 76 | SimStore[GsxConstants.VarNoPilotsDeboard].WriteValue(1); 77 | } 78 | 79 | return SubPaxTarget.WriteValue(num); 80 | } 81 | 82 | protected virtual void NotifyPaxChange(ISimResourceSubscription sub, object data) 83 | { 84 | if (!IsFenixAircraft) 85 | return; 86 | 87 | if (State != GsxServiceState.Active) 88 | { 89 | Logger.Debug($"Ignoring Pax Change - Service not active"); 90 | return; 91 | } 92 | 93 | var pax = sub.GetNumber(); 94 | if (pax < 0 || pax > PaxTarget) 95 | { 96 | Logger.Warning($"Ignoring Pax Change - Value received: {pax}"); 97 | return; 98 | } 99 | 100 | OnPaxChange?.Invoke(this); 101 | } 102 | 103 | protected virtual void NotifyCargoChange(ISimResourceSubscription sub, object data) 104 | { 105 | if (!IsFenixAircraft) 106 | return; 107 | 108 | if (State != GsxServiceState.Active) 109 | { 110 | Logger.Debug($"Ignoring Cargo Change - Service not active"); 111 | return; 112 | } 113 | 114 | var cargo = sub.GetNumber(); 115 | if (cargo < 0 || cargo > 100) 116 | { 117 | Logger.Warning($"Ignoring Cargo Change - Value received: {cargo}"); 118 | return; 119 | } 120 | 121 | OnCargoChange?.Invoke(this); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/Services/GsxServiceDeice.cs: -------------------------------------------------------------------------------- 1 | using CFIT.SimConnectLib.SimResources; 2 | using Fenix2GSX.GSX.Menu; 3 | 4 | namespace Fenix2GSX.GSX.Services 5 | { 6 | public class GsxServiceDeice(GsxController controller) : GsxService(controller) 7 | { 8 | public override GsxServiceType Type => GsxServiceType.Deice; 9 | public virtual ISimResourceSubscription SubDeiceService { get; protected set; } 10 | 11 | protected override GsxMenuSequence InitCallSequence() 12 | { 13 | var sequence = new GsxMenuSequence(); 14 | sequence.Commands.Add(new(8, GsxConstants.MenuGate, true)); 15 | sequence.Commands.Add(new(2, GsxConstants.MenuAdditionalServices) { WaitReady = true }); 16 | sequence.Commands.Add(GsxMenuCommand.CreateOperator()); 17 | sequence.Commands.Add(GsxMenuCommand.CreateDummy()); 18 | 19 | return sequence; 20 | } 21 | 22 | protected override void InitSubscriptions() 23 | { 24 | SubDeiceService = SimStore.AddVariable(GsxConstants.VarServiceDeice); 25 | SubDeiceService.OnReceived += OnStateChange; 26 | } 27 | 28 | protected override void DoReset() 29 | { 30 | 31 | } 32 | 33 | public override void FreeResources() 34 | { 35 | SubDeiceService.OnReceived -= OnStateChange; 36 | 37 | SimStore.Remove(GsxConstants.VarServiceDeice); 38 | } 39 | 40 | protected override GsxServiceState GetState() 41 | { 42 | return ReadState(SubDeiceService); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/Services/GsxServiceGpu.cs: -------------------------------------------------------------------------------- 1 | using CFIT.SimConnectLib.SimResources; 2 | using Fenix2GSX.GSX.Menu; 3 | 4 | namespace Fenix2GSX.GSX.Services 5 | { 6 | public class GsxServiceGpu(GsxController controller) : GsxService(controller) 7 | { 8 | public override GsxServiceType Type => GsxServiceType.GPU; 9 | public virtual ISimResourceSubscription SubGpuService { get; protected set; } 10 | 11 | protected override GsxMenuSequence InitCallSequence() 12 | { 13 | var sequence = new GsxMenuSequence(); 14 | sequence.Commands.Add(new(8, GsxConstants.MenuGate, true)); 15 | sequence.Commands.Add(new(1, GsxConstants.MenuAdditionalServices) { WaitReady = true }); 16 | sequence.Commands.Add(GsxMenuCommand.CreateOperator()); 17 | sequence.Commands.Add(GsxMenuCommand.CreateReset()); 18 | 19 | return sequence; 20 | } 21 | 22 | protected override void InitSubscriptions() 23 | { 24 | SubGpuService = SimStore.AddVariable(GsxConstants.VarServiceGpu); 25 | SubGpuService.OnReceived += OnStateChange; 26 | } 27 | 28 | protected override void DoReset() 29 | { 30 | 31 | } 32 | 33 | public override void FreeResources() 34 | { 35 | SubGpuService.OnReceived -= OnStateChange; 36 | 37 | SimStore.Remove(GsxConstants.VarServiceGpu); 38 | } 39 | 40 | protected override GsxServiceState GetState() 41 | { 42 | return ReadState(SubGpuService); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/Services/GsxServiceJetway.cs: -------------------------------------------------------------------------------- 1 | using CFIT.SimConnectLib.SimResources; 2 | using Fenix2GSX.GSX.Menu; 3 | using System.Threading.Tasks; 4 | 5 | namespace Fenix2GSX.GSX.Services 6 | { 7 | public class GsxServiceJetway(GsxController controller) : GsxService(controller) 8 | { 9 | 10 | public override GsxServiceType Type => GsxServiceType.Jetway; 11 | public virtual ISimResourceSubscription SubService { get; protected set; } 12 | public virtual ISimResourceSubscription SubOperating { get; protected set; } 13 | 14 | public virtual bool IsAvailable => State != GsxServiceState.NotAvailable; 15 | public virtual bool IsConnected => SubService.GetNumber() == (int)GsxServiceState.Active && SubOperating.GetNumber() < 3; 16 | public virtual bool IsOperating => SubService.GetNumber() == (int)GsxServiceState.Requested || SubOperating.GetNumber() > 3; 17 | 18 | protected override GsxMenuSequence InitCallSequence() 19 | { 20 | var sequence = new GsxMenuSequence(); 21 | sequence.Commands.Add(new(6, GsxConstants.MenuGate, true)); 22 | sequence.Commands.Add(GsxMenuCommand.CreateOperator()); 23 | sequence.Commands.Add(GsxMenuCommand.CreateDummy()); 24 | 25 | return sequence; 26 | } 27 | 28 | protected override void InitSubscriptions() 29 | { 30 | SubService = SimStore.AddVariable(GsxConstants.VarServiceJetway); 31 | SubOperating = SimStore.AddVariable(GsxConstants.VarServiceJetwayOperation); 32 | SubService.OnReceived += OnStateChange; 33 | } 34 | 35 | protected override void DoReset() 36 | { 37 | 38 | } 39 | 40 | public override void FreeResources() 41 | { 42 | SubService.OnReceived -= OnStateChange; 43 | SimStore.Remove(GsxConstants.VarServiceJetway); 44 | SimStore.Remove(GsxConstants.VarServiceJetwayOperation); 45 | } 46 | 47 | protected override GsxServiceState GetState() 48 | { 49 | return ReadState(SubService); 50 | } 51 | 52 | protected override bool CheckCalled() 53 | { 54 | return IsOperating || IsConnected; 55 | } 56 | 57 | protected override async Task DoCall() 58 | { 59 | if (IsAvailable) 60 | return await base.DoCall(); 61 | else 62 | return true; 63 | } 64 | 65 | public virtual async Task Remove() 66 | { 67 | if (!IsConnected || !IsAvailable || IsOperating) 68 | return; 69 | 70 | await DoCall(); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/Services/GsxServiceLavatory.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppLogger; 2 | using CFIT.SimConnectLib.SimResources; 3 | using Fenix2GSX.GSX.Menu; 4 | 5 | namespace Fenix2GSX.GSX.Services 6 | { 7 | public class GsxServiceLavatory(GsxController controller) : GsxService(controller) 8 | { 9 | public override GsxServiceType Type => GsxServiceType.Lavatory; 10 | public virtual ISimResourceSubscription SubLavatoryService { get; protected set; } 11 | 12 | protected override GsxMenuSequence InitCallSequence() 13 | { 14 | var sequence = new GsxMenuSequence(); 15 | sequence.Commands.Add(new(8, GsxConstants.MenuGate, true)); 16 | sequence.Commands.Add(new(3, GsxConstants.MenuAdditionalServices) { WaitReady = true }); 17 | sequence.Commands.Add(GsxMenuCommand.CreateOperator()); 18 | sequence.Commands.Add(GsxMenuCommand.CreateReset()); 19 | 20 | return sequence; 21 | } 22 | 23 | protected override void InitSubscriptions() 24 | { 25 | SubLavatoryService = SimStore.AddVariable(GsxConstants.VarServiceLavatory); 26 | SubLavatoryService.OnReceived += OnStateChange; 27 | } 28 | 29 | protected override void DoReset() 30 | { 31 | 32 | } 33 | 34 | public override void FreeResources() 35 | { 36 | SubLavatoryService.OnReceived -= OnStateChange; 37 | 38 | SimStore.Remove(GsxConstants.VarServiceLavatory); 39 | } 40 | 41 | protected override bool CheckCalled() 42 | { 43 | return SequenceResult; 44 | } 45 | 46 | protected override void OnStateChange(ISimResourceSubscription sub, object data) 47 | { 48 | if (!IsFenixAircraft) 49 | return; 50 | 51 | if (sub.GetNumber() == 4) 52 | { 53 | Logger.Information($"{Type} Service requested"); 54 | } 55 | else if (sub.GetNumber() == 5) 56 | { 57 | Logger.Information($"{Type} Service active"); 58 | WasActive = true; 59 | NotifyActive(); 60 | } 61 | else if (sub.GetNumber() == 1 && WasActive) 62 | { 63 | Logger.Information($"{Type} Service completed"); 64 | NotifyCompleted(); 65 | } 66 | NotifyStateChange(); 67 | } 68 | 69 | protected override GsxServiceState GetState() 70 | { 71 | var state = ReadState(SubLavatoryService); 72 | if (state == GsxServiceState.Callable && WasActive) 73 | return GsxServiceState.Completed; 74 | else 75 | return state; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/Services/GsxServicePushback.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppLogger; 2 | using CFIT.AppTools; 3 | using CFIT.SimConnectLib.SimResources; 4 | using Fenix2GSX.GSX.Menu; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | namespace Fenix2GSX.GSX.Services 9 | { 10 | public class GsxServicePushback(GsxController controller) : GsxService(controller) 11 | { 12 | public override GsxServiceType Type => GsxServiceType.Pushback; 13 | public virtual ISimResourceSubscription SubDepartService { get; protected set; } 14 | public virtual ISimResourceSubscription SubPushStatus { get; protected set; } 15 | public virtual bool IsPinInserted => SubBypassPin.GetNumber() == 1; 16 | public virtual int PushStatus => (int)SubPushStatus.GetNumber(); 17 | public virtual bool IsTugConnected => SubPushStatus.GetNumber() == 3 || SubPushStatus.GetNumber() == 4; 18 | public virtual bool TugAttachedOnBoarding { get; protected set; } = false; 19 | public virtual ISimResourceSubscription SubBypassPin { get; protected set; } 20 | 21 | public event Action OnBypassPin; 22 | 23 | protected override GsxMenuSequence InitCallSequence() 24 | { 25 | var sequence = new GsxMenuSequence(); 26 | sequence.Commands.Add(new(5, GsxConstants.MenuGate, true)); 27 | sequence.Commands.Add(GsxMenuCommand.CreateOperator()); 28 | sequence.Commands.Add(GsxMenuCommand.CreateDummy()); 29 | 30 | return sequence; 31 | } 32 | 33 | protected override void InitSubscriptions() 34 | { 35 | SubDepartService = SimStore.AddVariable(GsxConstants.VarServiceDeparture); 36 | SubDepartService.OnReceived += OnStateChange; 37 | SubPushStatus = SimStore.AddVariable(GsxConstants.VarPusbackStatus); 38 | SubPushStatus.OnReceived += OnPushChange; 39 | 40 | SubBypassPin = SimStore.AddVariable(GsxConstants.VarBypassPin); 41 | SubBypassPin.OnReceived += NotifyBypassPin; 42 | } 43 | 44 | protected virtual void OnPushChange(ISimResourceSubscription sub, object data) 45 | { 46 | if (!IsFenixAircraft) 47 | return; 48 | 49 | var state = sub.GetNumber(); 50 | Logger.Debug($"Push Status Change: {state}"); 51 | if (!TugAttachedOnBoarding && state > 0 && Controller.GsxServices[GsxServiceType.Boarding].State == GsxServiceState.Active) 52 | { 53 | Logger.Information($"Tug attaching during Boarding"); 54 | TugAttachedOnBoarding = true; 55 | } 56 | } 57 | 58 | protected virtual void NotifyBypassPin(ISimResourceSubscription sub, object data) 59 | { 60 | if (!IsFenixAircraft) 61 | return; 62 | 63 | TaskTools.RunLogged(() => OnBypassPin?.Invoke(this), Controller.Token); 64 | } 65 | 66 | protected override void DoReset() 67 | { 68 | TugAttachedOnBoarding = false; 69 | } 70 | 71 | public override void FreeResources() 72 | { 73 | SubDepartService.OnReceived -= OnStateChange; 74 | SubBypassPin.OnReceived -= NotifyBypassPin; 75 | SubPushStatus.OnReceived -= OnPushChange; 76 | 77 | SimStore.Remove(GsxConstants.VarServiceDeparture); 78 | SimStore.Remove(GsxConstants.VarBypassPin); 79 | SimStore.Remove(GsxConstants.VarPusbackStatus); 80 | } 81 | 82 | public override async Task Call() 83 | { 84 | if (PushStatus == 0 || !IsCalled) 85 | await base.Call(); 86 | else if (PushStatus > 0 && PushStatus < 5) 87 | { 88 | var sequence = new GsxMenuSequence(); 89 | sequence.Commands.Add(new(5, GsxConstants.MenuGate, true) { NoHide = true }); 90 | await Controller.Menu.RunSequence(sequence); 91 | } 92 | } 93 | 94 | protected override GsxServiceState GetState() 95 | { 96 | return ReadState(SubDepartService); 97 | } 98 | 99 | public virtual async Task EndPushback(int selection = 1) 100 | { 101 | Logger.Debug($"End Pushback ({PushStatus})"); 102 | if (PushStatus < 5) 103 | return; 104 | 105 | var sequence = new GsxMenuSequence(); 106 | sequence.Commands.Add(new(selection, GsxConstants.MenuPushbackInterrupt, true)); 107 | sequence.Commands.Add(GsxMenuCommand.CreateDummy()); 108 | await Controller.Menu.RunSequence(sequence); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/Services/GsxServiceRefuel.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppLogger; 2 | using CFIT.SimConnectLib.SimResources; 3 | using Fenix2GSX.GSX.Menu; 4 | 5 | namespace Fenix2GSX.GSX.Services 6 | { 7 | public class GsxServiceRefuel(GsxController controller) : GsxService(controller) 8 | { 9 | public override GsxServiceType Type => GsxServiceType.Refuel; 10 | public virtual ISimResourceSubscription SubRefuelService { get; protected set; } 11 | public virtual ISimResourceSubscription SubRefuelHose { get; protected set; } 12 | public virtual bool IsRefueling => IsActive && SubRefuelHose.GetNumber() == 1; 13 | protected virtual bool CompleteNotified { get; set; } = false; 14 | 15 | protected override GsxMenuSequence InitCallSequence() 16 | { 17 | var sequence = new GsxMenuSequence(); 18 | sequence.Commands.Add(new(3, GsxConstants.MenuGate, true)); 19 | sequence.Commands.Add(GsxMenuCommand.CreateOperator()); 20 | sequence.Commands.Add(GsxMenuCommand.CreateDummy()); 21 | 22 | return sequence; 23 | } 24 | 25 | protected override void InitSubscriptions() 26 | { 27 | SubRefuelService = SimStore.AddVariable(GsxConstants.VarServiceRefuel); 28 | SubRefuelHose = SimStore.AddVariable(GsxConstants.VarServiceRefuelHose); 29 | 30 | SubRefuelService.OnReceived += OnStateChange; 31 | SubRefuelHose.OnReceived += OnHoseChange; 32 | } 33 | 34 | protected override void OnStateChange(ISimResourceSubscription sub, object data) 35 | { 36 | if (!IsFenixAircraft) 37 | return; 38 | 39 | if (sub.GetNumber() == 4) 40 | { 41 | Logger.Information($"{Type} Service requested"); 42 | } 43 | else if (sub.GetNumber() == 5) 44 | { 45 | Logger.Information($"{Type} Service active"); 46 | WasActive = true; 47 | NotifyActive(); 48 | } 49 | else if (sub.GetNumber() == 1 && WasActive && !CompleteNotified) 50 | { 51 | Logger.Information($"{Type} Service completed"); 52 | CompleteNotified = true; 53 | Controller.AircraftInterface.RefuelComplete(); 54 | NotifyCompleted(); 55 | } 56 | NotifyStateChange(); 57 | } 58 | 59 | protected virtual void OnHoseChange(ISimResourceSubscription sub, object data) 60 | { 61 | if (!IsFenixAircraft) 62 | return; 63 | 64 | if (sub.GetNumber() == 1 && State != GsxServiceState.Unknown && State != GsxServiceState.Completed) 65 | { 66 | Logger.Information($"Fuel Hose connected"); 67 | if (State == GsxServiceState.Active) 68 | Controller.AircraftInterface.RefuelStart(); 69 | } 70 | else if (sub.GetNumber() == 0 && State != GsxServiceState.Unknown) 71 | Logger.Information($"Fuel Hose disconnected"); 72 | } 73 | 74 | protected override void DoReset() 75 | { 76 | CompleteNotified = false; 77 | } 78 | 79 | public override void FreeResources() 80 | { 81 | SubRefuelService.OnReceived -= OnStateChange; 82 | SubRefuelHose.OnReceived -= OnHoseChange; 83 | 84 | SimStore.Remove(GsxConstants.VarServiceRefuel); 85 | SimStore.Remove(GsxConstants.VarServiceRefuelHose); 86 | } 87 | 88 | protected override GsxServiceState GetState() 89 | { 90 | var state = ReadState(SubRefuelService); 91 | if (state == GsxServiceState.Callable && WasActive) 92 | return GsxServiceState.Completed; 93 | else 94 | return state; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/Services/GsxServiceReposition.cs: -------------------------------------------------------------------------------- 1 | using Fenix2GSX.GSX.Menu; 2 | 3 | namespace Fenix2GSX.GSX.Services 4 | { 5 | public class GsxServiceReposition(GsxController controller) : GsxService(controller) 6 | { 7 | public override GsxServiceType Type => GsxServiceType.Reposition; 8 | protected override GsxMenuSequence InitCallSequence() 9 | { 10 | var sequence = new GsxMenuSequence(); 11 | sequence.Commands.Add(new(10, GsxConstants.MenuGate, true)); 12 | sequence.Commands.Add(new(1, GsxConstants.MenuParkingSelect) { WaitReady = true }); 13 | sequence.Commands.Add(GsxMenuCommand.CreateDummy()); 14 | sequence.Commands.Add(GsxMenuCommand.CreateDummy()); 15 | sequence.Commands.Add(GsxMenuCommand.CreateReset()); 16 | sequence.IgnoreGsxState = true; 17 | 18 | return sequence; 19 | } 20 | 21 | protected override void InitSubscriptions() 22 | { 23 | 24 | } 25 | 26 | protected override void DoReset() 27 | { 28 | 29 | } 30 | 31 | public override void FreeResources() 32 | { 33 | 34 | } 35 | 36 | protected override GsxServiceState GetState() 37 | { 38 | if (SequenceResult) 39 | return GsxServiceState.Completed; 40 | else 41 | return GsxServiceState.Callable; 42 | } 43 | 44 | protected override bool CheckCalled() 45 | { 46 | return SequenceResult; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/Services/GsxServiceStairs.cs: -------------------------------------------------------------------------------- 1 | using CFIT.SimConnectLib.SimResources; 2 | using Fenix2GSX.GSX.Menu; 3 | using System.Threading.Tasks; 4 | 5 | namespace Fenix2GSX.GSX.Services 6 | { 7 | public class GsxServiceStairs(GsxController controller) : GsxService(controller) 8 | { 9 | 10 | public override GsxServiceType Type => GsxServiceType.Stairs; 11 | public virtual ISimResourceSubscription SubService { get; protected set; } 12 | public virtual ISimResourceSubscription SubOperating { get; protected set; } 13 | 14 | public virtual bool IsAvailable => State != GsxServiceState.NotAvailable; 15 | public virtual bool IsConnected => SubService.GetNumber() == (int)GsxServiceState.Active && SubOperating.GetNumber() < 3; 16 | public virtual bool IsOperating => SubService.GetNumber() == (int)GsxServiceState.Requested || SubOperating.GetNumber() > 3; 17 | 18 | protected override GsxMenuSequence InitCallSequence() 19 | { 20 | var sequence = new GsxMenuSequence(); 21 | sequence.Commands.Add(new(7, GsxConstants.MenuGate, true)); 22 | sequence.Commands.Add(GsxMenuCommand.CreateOperator()); 23 | sequence.Commands.Add(GsxMenuCommand.CreateDummy()); 24 | 25 | return sequence; 26 | } 27 | 28 | protected override void InitSubscriptions() 29 | { 30 | SubService = SimStore.AddVariable(GsxConstants.VarServiceStairs); 31 | SubOperating = SimStore.AddVariable(GsxConstants.VarServiceStairsOperation); 32 | SubService.OnReceived += OnStateChange; 33 | } 34 | 35 | protected override void DoReset() 36 | { 37 | 38 | } 39 | 40 | public override void FreeResources() 41 | { 42 | SubService.OnReceived -= OnStateChange; 43 | SimStore.Remove(GsxConstants.VarServiceStairs); 44 | SimStore.Remove(GsxConstants.VarServiceStairsOperation); 45 | } 46 | 47 | protected override GsxServiceState GetState() 48 | { 49 | return ReadState(SubService); 50 | } 51 | 52 | protected override bool CheckCalled() 53 | { 54 | return IsOperating || IsConnected; 55 | } 56 | 57 | protected override async Task DoCall() 58 | { 59 | if (IsAvailable) 60 | return await base.DoCall(); 61 | else 62 | return true; 63 | } 64 | 65 | public virtual async Task Remove() 66 | { 67 | if (!IsConnected || !IsAvailable || IsOperating) 68 | return; 69 | 70 | await DoCall(); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Fenix2GSX/GSX/Services/GsxServiceWater.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppLogger; 2 | using CFIT.SimConnectLib.SimResources; 3 | using Fenix2GSX.GSX.Menu; 4 | 5 | namespace Fenix2GSX.GSX.Services 6 | { 7 | public class GsxServiceWater(GsxController controller) : GsxService(controller) 8 | { 9 | public override GsxServiceType Type => GsxServiceType.Water; 10 | public virtual ISimResourceSubscription SubWaterService { get; protected set; } 11 | 12 | protected override GsxMenuSequence InitCallSequence() 13 | { 14 | var sequence = new GsxMenuSequence(); 15 | sequence.Commands.Add(new(8, GsxConstants.MenuGate, true)); 16 | sequence.Commands.Add(new(4, GsxConstants.MenuAdditionalServices) { WaitReady = true }); 17 | sequence.Commands.Add(GsxMenuCommand.CreateOperator()); 18 | sequence.Commands.Add(GsxMenuCommand.CreateReset()); 19 | 20 | return sequence; 21 | } 22 | 23 | protected override void InitSubscriptions() 24 | { 25 | SubWaterService = SimStore.AddVariable(GsxConstants.VarServiceWater); 26 | SubWaterService.OnReceived += OnStateChange; 27 | } 28 | 29 | protected override void DoReset() 30 | { 31 | 32 | } 33 | 34 | public override void FreeResources() 35 | { 36 | SubWaterService.OnReceived -= OnStateChange; 37 | 38 | SimStore.Remove(GsxConstants.VarServiceWater); 39 | } 40 | 41 | protected override bool CheckCalled() 42 | { 43 | return SequenceResult; 44 | } 45 | 46 | protected override void OnStateChange(ISimResourceSubscription sub, object data) 47 | { 48 | if (sub.GetNumber() == 4) 49 | { 50 | Logger.Information($"{Type} Service requested"); 51 | } 52 | else if (sub.GetNumber() == 5) 53 | { 54 | Logger.Information($"{Type} Service active"); 55 | WasActive = true; 56 | NotifyActive(); 57 | } 58 | else if (sub.GetNumber() == 1 && WasActive) 59 | { 60 | Logger.Information($"{Type} Service completed"); 61 | NotifyCompleted(); 62 | } 63 | NotifyStateChange(); 64 | } 65 | 66 | protected override GsxServiceState GetState() 67 | { 68 | var state = ReadState(SubWaterService); 69 | if (state == GsxServiceState.Callable && WasActive) 70 | return GsxServiceState.Completed; 71 | else 72 | return state; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Fenix2GSX/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("Style", "IDE0059:Unnötige Zuweisung eines Werts.", Justification = "", Scope = "member", Target = "~M:Fenix2GSX.GSX.Menu.GsxMenu.RunSequence(Fenix2GSX.GSX.Menu.GsxMenuSequence)~System.Threading.Tasks.Task{System.Boolean}")] 9 | [assembly: SuppressMessage("Performance", "CA1864:Methode \"Dictionary.TryAdd(TKey, TValue)\" bevorzugen", Justification = "", Scope = "member", Target = "~M:Fenix2GSX.AppConfig.Config.SetFuelFob(System.String,System.Double)")] 10 | [assembly: SuppressMessage("Performance", "CA1854:Methode „IDictionary.TryGetValue(TKey, out TValue)“ bevorzugen", Justification = "", Scope = "member", Target = "~M:Fenix2GSX.Audio.AudioSession.OnVolumeChange(CFIT.SimConnectLib.SimResources.ISimResourceSubscription,System.Object)")] 11 | [assembly: SuppressMessage("Performance", "CA1854:Methode „IDictionary.TryGetValue(TKey, out TValue)“ bevorzugen", Justification = "", Scope = "member", Target = "~M:Fenix2GSX.Audio.AudioSession.OnMuteChange(CFIT.SimConnectLib.SimResources.ISimResourceSubscription,System.Object)")] 12 | [assembly: SuppressMessage("Style", "IDE0305:Initialisierung der Sammlung vereinfachen", Justification = "", Scope = "member", Target = "~M:Fenix2GSX.UI.Models.ModelMonitor.Stop")] 13 | [assembly: SuppressMessage("Style", "IDE0180:Tupel zum Tauschen von Werten verwenden", Justification = "", Scope = "member", Target = "~M:Fenix2GSX.UI.Views.Automation.ModelOperatorPreferences.MoveItem(System.Int32,System.Int32)")] 14 | [assembly: SuppressMessage("Style", "IDE0180:Tupel zum Tauschen von Werten verwenden", Justification = "", Scope = "member", Target = "~M:Fenix2GSX.UI.Views.Automation.ModelDepartureServices.MoveItem(System.Int32,System.Int32)")] 15 | [assembly: SuppressMessage("Style", "IDE0041:\"Ist NULL\"-Prüfung verwenden", Justification = "", Scope = "member", Target = "~M:Fenix2GSX.UI.Views.Monitor.ModelMonitor.UpdateBoolState(System.String,System.String,System.Boolean,System.Boolean)")] 16 | -------------------------------------------------------------------------------- /Fenix2GSX/Microsoft.FlightSimulator.SimConnect.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/Microsoft.FlightSimulator.SimConnect.dll -------------------------------------------------------------------------------- /Fenix2GSX/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Fenix2GSX": { 4 | "commandName": "Project" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /Fenix2GSX/SimConnect.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/SimConnect.dll -------------------------------------------------------------------------------- /Fenix2GSX/Tools.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppLogger; 2 | using System; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Threading; 7 | 8 | namespace Fenix2GSX 9 | { 10 | public static class Tools 11 | { 12 | public static string GetMsfsWindowTitle() 13 | { 14 | string result = ""; 15 | try 16 | { 17 | Process proc = Process.GetProcessesByName(Fenix2GSX.Instance.Config.BinaryMsfs2024)?.FirstOrDefault(); 18 | if (!string.IsNullOrWhiteSpace(proc?.MainWindowTitle)) 19 | result = proc.MainWindowTitle; 20 | else 21 | result = $"{Fenix2GSX.Instance.Config.Msfs2024WindowTitle}{proc?.MainModule?.FileVersionInfo?.FileVersion?.Replace(',', '.')}"; 22 | } 23 | catch (Exception ex) 24 | { 25 | Logger.LogException(ex); 26 | } 27 | 28 | return result; 29 | } 30 | 31 | public static void SendWalkaroundKeystroke() 32 | { 33 | _ = SendInput(1, CreateInputStruct(VK_LSHIFT, KeyEventF.KeyDown), Marshal.SizeOf(typeof(Input))); 34 | _ = SendInput(1, CreateInputStruct(VK_KEY_C, KeyEventF.KeyDown), Marshal.SizeOf(typeof(Input))); 35 | Thread.Sleep(100); 36 | _ = SendInput(1, CreateInputStruct(VK_LSHIFT, KeyEventF.KeyUp), Marshal.SizeOf(typeof(Input))); 37 | _ = SendInput(1, CreateInputStruct(VK_KEY_C, KeyEventF.KeyUp), Marshal.SizeOf(typeof(Input))); 38 | } 39 | #pragma warning disable 40 | [DllImport("user32.dll")] 41 | private static extern uint SendInput(uint nInputs, Input[] pInputs, int cbSize); 42 | 43 | [DllImport("user32.dll")] 44 | private static extern IntPtr GetMessageExtraInfo(); 45 | 46 | [DllImport("user32.dll")] 47 | static extern uint MapVirtualKey(uint uCode, MapVirtualKeyMapTypes uMapType); 48 | #pragma warning restore 49 | public enum MapVirtualKeyMapTypes : uint 50 | { 51 | MAPVK_VK_TO_VSC = 0x00, 52 | MAPVK_VSC_TO_VK = 0x01, 53 | MAPVK_VK_TO_CHAR = 0x02, 54 | MAPVK_VSC_TO_VK_EX = 0x03, 55 | MAPVK_VK_TO_VSC_EX = 0x04 56 | } 57 | 58 | 59 | [StructLayout(LayoutKind.Sequential)] 60 | public struct KeyboardInput 61 | { 62 | public ushort wVk; 63 | public ushort wScan; 64 | public uint dwFlags; 65 | public uint time; 66 | public IntPtr dwExtraInfo; 67 | } 68 | 69 | [StructLayout(LayoutKind.Sequential)] 70 | public struct MouseInput 71 | { 72 | public int dx; 73 | public int dy; 74 | public uint mouseData; 75 | public uint dwFlags; 76 | public uint time; 77 | public IntPtr dwExtraInfo; 78 | } 79 | 80 | [StructLayout(LayoutKind.Sequential)] 81 | public struct HardwareInput 82 | { 83 | public uint uMsg; 84 | public ushort wParamL; 85 | public ushort wParamH; 86 | } 87 | 88 | [StructLayout(LayoutKind.Explicit)] 89 | public struct InputUnion 90 | { 91 | [FieldOffset(0)] public MouseInput mi; 92 | [FieldOffset(0)] public KeyboardInput ki; 93 | [FieldOffset(0)] public HardwareInput hi; 94 | } 95 | 96 | public struct Input 97 | { 98 | public int type; 99 | public InputUnion u; 100 | } 101 | 102 | [Flags] 103 | public enum InputType 104 | { 105 | Mouse = 0, 106 | Keyboard = 1, 107 | Hardware = 2 108 | } 109 | 110 | [Flags] 111 | public enum KeyEventF 112 | { 113 | KeyDown = 0x0000, 114 | ExtendedKey = 0x0001, 115 | KeyUp = 0x0002, 116 | Unicode = 0x0004, 117 | Scancode = 0x0008 118 | } 119 | 120 | public static readonly byte VK_LSHIFT = 0xA0; 121 | public static readonly byte VK_KEY_C = 0x43; 122 | 123 | public static Input[] CreateInputStruct(byte vk, KeyEventF flags) 124 | { 125 | return 126 | [ 127 | new Input 128 | { 129 | type = (int)InputType.Keyboard, 130 | u = new InputUnion 131 | { 132 | ki = new KeyboardInput 133 | { 134 | wVk = 0, 135 | wScan = (ushort)MapVirtualKey(vk, MapVirtualKeyMapTypes.MAPVK_VK_TO_VSC), 136 | dwFlags = (uint)(flags | KeyEventF.Scancode), 137 | dwExtraInfo = GetMessageExtraInfo() 138 | } 139 | } 140 | } 141 | ]; 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/AppWindow.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 30 | 36 | 42 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/AppWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppTools; 2 | using Fenix2GSX.UI.Views.Audio; 3 | using Fenix2GSX.UI.Views.Automation; 4 | using Fenix2GSX.UI.Views.Monitor; 5 | using Fenix2GSX.UI.Views.Profiles; 6 | using Fenix2GSX.UI.Views.Settings; 7 | using System; 8 | using System.Reflection; 9 | using System.Windows; 10 | using System.Windows.Controls; 11 | using System.Windows.Documents; 12 | using System.Windows.Media; 13 | using System.Windows.Navigation; 14 | 15 | namespace Fenix2GSX.UI 16 | { 17 | public interface IView 18 | { 19 | public void Start(); 20 | public void Stop(); 21 | } 22 | 23 | public partial class AppWindow : Window 24 | { 25 | public static UiIconLoader IconLoader { get; } = new(Assembly.GetExecutingAssembly(), IconLoadSource.Embedded, "Fenix2GSX.UI.Icons."); 26 | protected virtual Button CurrentButton { get; set; } = null; 27 | protected virtual IView CurrentView { get; set; } = null; 28 | protected static SolidColorBrush BrushDefault { get; } = SystemColors.WindowFrameBrush; 29 | protected static SolidColorBrush BrushHighlight { get; } = SystemColors.HighlightBrush; 30 | protected static Thickness ThicknessDefault { get; } = new(1); 31 | protected static Thickness ThicknessHighlight { get; } = new(1.5); 32 | 33 | protected virtual IView ViewMonitor { get; } = new ViewMonitor(); 34 | protected virtual IView ViewAutomation { get; } = new ViewAutomation(); 35 | protected virtual IView ViewProfiles { get; } = new ViewProfiles(); 36 | protected virtual IView ViewAudio { get; } = new ViewAudio(); 37 | protected virtual IView ViewSettings { get; } = new ViewSettings(); 38 | 39 | public AppWindow() 40 | { 41 | InitializeComponent(); 42 | this.Loaded += OnWindowLoaded; 43 | this.IsVisibleChanged += OnVisibleChanged; 44 | 45 | ButtonMonitor.Click += (_, _) => SetView(ButtonMonitor, ViewMonitor); 46 | ButtonAutomation.Click += (_, _) => SetView(ButtonAutomation, ViewAutomation); 47 | ButtonProfiles.Click += (_, _) => SetView(ButtonProfiles, ViewProfiles); 48 | ButtonAudio.Click += (_, _) => SetView(ButtonAudio, ViewAudio); 49 | ButtonSettings.Click += (_, _) => SetView(ButtonSettings, ViewSettings); 50 | 51 | if (Fenix2GSX.Instance.UpdateDetected) 52 | { 53 | if (Fenix2GSX.Instance.UpdateIsDev) 54 | LabelVersionCheck.Inlines.Add("New Develop Version "); 55 | else 56 | LabelVersionCheck.Inlines.Add("New Stable Version "); 57 | var run = new Run($"{Fenix2GSX.Instance.UpdateVersion}"); 58 | 59 | Hyperlink hyperlink; 60 | if (Fenix2GSX.Instance.UpdateIsDev) 61 | hyperlink = new Hyperlink(run) 62 | { 63 | NavigateUri = new Uri("https://github.com/Fragtality/Fenix2GSX/blob/master/Fenix2GSX-Installer-latest.exe") 64 | }; 65 | else 66 | hyperlink = new Hyperlink(run) 67 | { 68 | NavigateUri = new Uri("https://github.com/Fragtality/Fenix2GSX/releases/latest") 69 | }; 70 | LabelVersionCheck.Inlines.Add(hyperlink); 71 | LabelVersionCheck.Inlines.Add(" available!"); 72 | this.AddHandler(Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler(Nav.RequestNavigateHandler)); 73 | PanelVersion.Visibility = Visibility.Visible; 74 | } 75 | } 76 | 77 | protected virtual void OnWindowLoaded(object sender, RoutedEventArgs e) 78 | { 79 | if (CurrentButton == null) 80 | SetView(ButtonAutomation, ViewAutomation); 81 | } 82 | 83 | protected virtual void OnVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) 84 | { 85 | if (this.Visibility != Visibility.Visible) 86 | CurrentView?.Stop(); 87 | else 88 | CurrentView?.Start(); 89 | } 90 | 91 | protected virtual void SetView(Button menuButton, IView viewControl) 92 | { 93 | CurrentView?.Stop(); 94 | 95 | if (CurrentButton != null) 96 | { 97 | CurrentButton.IsHitTestVisible = true; 98 | CurrentButton.BorderBrush = BrushDefault; 99 | CurrentButton.BorderThickness = ThicknessDefault; 100 | } 101 | 102 | if (CurrentView != null) 103 | ViewControl.SizeChanged -= OnViewSizeChanged; 104 | 105 | CurrentButton = menuButton; 106 | CurrentButton.IsHitTestVisible = false; 107 | CurrentButton.BorderBrush = BrushHighlight; 108 | CurrentButton.BorderThickness = ThicknessHighlight; 109 | 110 | ViewControl.Content = viewControl; 111 | CurrentView = viewControl; 112 | ViewControl.SizeChanged += OnViewSizeChanged; 113 | viewControl.Start(); 114 | InvalidateArrange(); 115 | InvalidateMeasure(); 116 | InvalidateVisual(); 117 | } 118 | 119 | protected virtual void OnViewSizeChanged(object sender, SizeChangedEventArgs e) 120 | { 121 | try 122 | { 123 | double height = Math.Max(ViewControl.ActualHeight + 96, 0); 124 | this.MinHeight = height; 125 | this.Height = height; 126 | } 127 | catch { } 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Icons/AppIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/UI/Icons/AppIcon.ico -------------------------------------------------------------------------------- /Fenix2GSX/UI/Icons/AppIconUpdate.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/UI/Icons/AppIconUpdate.ico -------------------------------------------------------------------------------- /Fenix2GSX/UI/Icons/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/UI/Icons/add.png -------------------------------------------------------------------------------- /Fenix2GSX/UI/Icons/automation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/UI/Icons/automation.png -------------------------------------------------------------------------------- /Fenix2GSX/UI/Icons/chevron-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/UI/Icons/chevron-down.png -------------------------------------------------------------------------------- /Fenix2GSX/UI/Icons/chevron-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/UI/Icons/chevron-up.png -------------------------------------------------------------------------------- /Fenix2GSX/UI/Icons/cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/UI/Icons/cursor.png -------------------------------------------------------------------------------- /Fenix2GSX/UI/Icons/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/UI/Icons/edit.png -------------------------------------------------------------------------------- /Fenix2GSX/UI/Icons/monitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/UI/Icons/monitor.png -------------------------------------------------------------------------------- /Fenix2GSX/UI/Icons/profiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/UI/Icons/profiles.png -------------------------------------------------------------------------------- /Fenix2GSX/UI/Icons/remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/UI/Icons/remove.png -------------------------------------------------------------------------------- /Fenix2GSX/UI/Icons/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/UI/Icons/settings.png -------------------------------------------------------------------------------- /Fenix2GSX/UI/Icons/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/UI/Icons/upload.png -------------------------------------------------------------------------------- /Fenix2GSX/UI/Icons/volume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Fenix2GSX/UI/Icons/volume.png -------------------------------------------------------------------------------- /Fenix2GSX/UI/NotifyIcon/NotifyIconModelExt.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework; 2 | using CFIT.AppFramework.UI.NotifyIcon; 3 | using CommunityToolkit.Mvvm.Input; 4 | 5 | namespace Fenix2GSX.UI.NotifyIcon 6 | { 7 | public partial class NotifyIconModelExt(ISimApp simApp) : NotifyIconViewModel(simApp) 8 | { 9 | protected override void CreateItems() 10 | { 11 | base.CreateItems(); 12 | Items.Add(new(null, null)); 13 | Items.Add(new($"Restart {SimApp.DefinitionBase.ProductName}", RestartAppCommand)); 14 | Items.Add(new($"Restart {SimApp.DefinitionBase.ProductName} and GSX", RestartAppGsxCommand)); 15 | } 16 | 17 | [RelayCommand] 18 | public virtual void RestartApp() 19 | { 20 | try { (SimApp as Fenix2GSX).AppService.ResetRequested = AppResetRequest.App; } catch { } 21 | } 22 | 23 | [RelayCommand] 24 | public virtual void RestartAppGsx() 25 | { 26 | try { (SimApp as Fenix2GSX).AppService.ResetRequested = AppResetRequest.AppGsx; } catch { } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/TimeSpanConverter.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppTools; 2 | using System; 3 | using System.Globalization; 4 | using System.Windows.Data; 5 | 6 | namespace Fenix2GSX.UI 7 | { 8 | [ValueConversion(typeof(TimeSpan), typeof(string))] 9 | public class TimeSpanConverter : IValueConverter 10 | { 11 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 12 | { 13 | if (value is TimeSpan span) 14 | return $"{(int)span.TotalMinutes}"; 15 | else 16 | return $"0"; 17 | } 18 | 19 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 20 | { 21 | if (value is string str && !string.IsNullOrWhiteSpace(str) && double.TryParse(str, new RealInvariantFormat(str), out double span)) 22 | return TimeSpan.FromMinutes((int)span); 23 | else 24 | return TimeSpan.Zero; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Audio/ModelAppMappings.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework.UI.ValueConverter; 2 | using CFIT.AppFramework.UI.ViewModels; 3 | using Fenix2GSX.AppConfig; 4 | using Fenix2GSX.Audio; 5 | using System.Collections.Generic; 6 | using System.Collections.Specialized; 7 | 8 | namespace Fenix2GSX.UI.Views.Audio 9 | { 10 | public partial class ModelAppMappings(ModelAudio modelAudio) : ViewModelCollection(modelAudio.Source.AudioMappings, (s) => s, (s) => s != null) 11 | { 12 | protected virtual ModelAudio ModelAudio { get; } = modelAudio; 13 | public override ICollection Source => ModelAudio.Source.AudioMappings; 14 | 15 | protected override void InitializeMemberBindings() 16 | { 17 | base.InitializeMemberBindings(); 18 | 19 | CreateMemberBinding(nameof(AudioMapping.Channel), new NoneConverter()); 20 | CreateMemberBinding(nameof(AudioMapping.DeviceName), new NoneConverter()); 21 | CreateMemberBinding(nameof(AudioMapping.UseLatch), new NoneConverter()); 22 | } 23 | 24 | public override bool UpdateSource(AudioMapping oldItem, AudioMapping newItem) 25 | { 26 | try 27 | { 28 | oldItem.Channel = newItem.Channel; 29 | oldItem.Device = newItem.Device; 30 | oldItem.Binary = newItem.Binary; 31 | oldItem.UseLatch = newItem.UseLatch; 32 | return true; 33 | } 34 | catch { } 35 | 36 | return false; 37 | } 38 | 39 | public override void NotifyCollectionChanged(NotifyCollectionChangedEventArgs e = null) 40 | { 41 | ModelAudio.Source.AudioMappings.Sort(); 42 | base.NotifyCollectionChanged(e); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Audio/ModelAudio.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using CommunityToolkit.Mvvm.Input; 3 | using CoreAudio; 4 | using Fenix2GSX.AppConfig; 5 | using Fenix2GSX.Audio; 6 | using System.Collections.Generic; 7 | using System.ComponentModel; 8 | using System.Windows.Input; 9 | 10 | namespace Fenix2GSX.UI.Views.Audio 11 | { 12 | public partial class ModelAudio : ModelBase 13 | { 14 | public ICommand CommandDebugInfo { get; } = new RelayCommand(() => AppService.Instance.AudioService.DeviceManager.WriteDebugInformation()); 15 | 16 | public ModelAudio(AppService appService) : base(appService.Config, appService) 17 | { 18 | AppMappingCollection = new(this); 19 | AppMappingCollection.CollectionChanged += (_, _) => { SaveConfig(); AudioController.ResetMappings = true; }; 20 | 21 | BlacklistCollection = new(this); 22 | BlacklistCollection.CollectionChanged += (_, _) => SaveConfig(); 23 | } 24 | 25 | protected override void InitializeModel() 26 | { 27 | this.PropertyChanged += OnPropertyChanged; 28 | AudioController.DeviceManager.DevicesChanged += () => NotifyPropertyChanged(nameof(AudioDevices)); 29 | } 30 | 31 | protected virtual void OnPropertyChanged(object? sender, PropertyChangedEventArgs e) 32 | { 33 | if (e?.PropertyName == nameof(CurrentChannel)) 34 | { 35 | NotifyPropertyChanged(nameof(SetStartupVolume)); 36 | NotifyPropertyChanged(nameof(StartupVolume)); 37 | NotifyPropertyChanged(nameof(StartupUnmute)); 38 | } 39 | } 40 | 41 | public virtual Dictionary AcpSideOptions { get; } = new() 42 | { 43 | { AcpSide.CPT, "Captain" }, 44 | { AcpSide.FO, "First Officer" }, 45 | }; 46 | 47 | public virtual AcpSide AudioAcpSide { get => Source.AudioAcpSide; set { SetModelValue(value); AudioController.ResetVolumes = true; } } 48 | 49 | [ObservableProperty] 50 | protected AudioChannel _CurrentChannel = AudioChannel.VHF1; 51 | 52 | public virtual bool SetStartupVolume 53 | { 54 | get => Source.AudioStartupVolumes[CurrentChannel] >= 0.0; 55 | set 56 | { 57 | double setValue = value ? 1.0 : -1.0; 58 | Source.AudioStartupVolumes[CurrentChannel] = setValue; 59 | Source.SaveConfiguration(); 60 | OnPropertyChanged(nameof(SetStartupVolume)); 61 | OnPropertyChanged(nameof(StartupVolume)); 62 | } 63 | } 64 | 65 | public virtual double StartupVolume 66 | { 67 | get => (Source.AudioStartupVolumes[CurrentChannel] >= 0.0 ? Source.AudioStartupVolumes[CurrentChannel] * 100.0 : 0); 68 | set 69 | { 70 | Source.AudioStartupVolumes[CurrentChannel] = value / 100.0; 71 | Source.SaveConfiguration(); 72 | OnPropertyChanged(nameof(StartupVolume)); 73 | } 74 | } 75 | 76 | public virtual bool StartupUnmute 77 | { 78 | get => Source.AudioStartupUnmute[CurrentChannel]; 79 | set 80 | { 81 | Source.AudioStartupUnmute[CurrentChannel] = value; 82 | Source.SaveConfiguration(); 83 | OnPropertyChanged(nameof(StartupUnmute)); 84 | } 85 | } 86 | 87 | public virtual ModelAppMappings AppMappingCollection { get; } 88 | 89 | public virtual List AudioDevices 90 | { 91 | get 92 | { 93 | var list = new List { "All" }; 94 | list.AddRange([.. AudioController.DeviceManager.GetDeviceNames()]); 95 | 96 | return list; 97 | } 98 | } 99 | 100 | public virtual ModelDeviceBlacklist BlacklistCollection { get; } 101 | 102 | public virtual Dictionary DeviceDataFlows { get; } = new() 103 | { 104 | { DataFlow.Render, DataFlow.Render.ToString() }, 105 | { DataFlow.Capture, DataFlow.Capture.ToString() }, 106 | { DataFlow.All, DataFlow.All.ToString() }, 107 | }; 108 | public virtual DataFlow AudioDeviceFlow { get => Source.AudioDeviceFlow; set { SetModelValue(value); AudioController.ResetMappings = true; } } 109 | 110 | public virtual Dictionary DeviceStates { get; } = new() 111 | { 112 | { DeviceState.Active, DeviceState.Active.ToString() }, 113 | { DeviceState.Disabled, DeviceState.Disabled.ToString() }, 114 | { DeviceState.NotPresent, DeviceState.NotPresent.ToString() }, 115 | { DeviceState.Unplugged, DeviceState.Unplugged.ToString() }, 116 | { DeviceState.MaskAll, DeviceState.MaskAll.ToString() }, 117 | }; 118 | public virtual DeviceState AudioDeviceState { get => Source.AudioDeviceState; set { SetModelValue(value); AudioController.ResetMappings = true; } } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Audio/ModelDeviceBlacklist.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework.UI.ViewModels; 2 | using System.Collections.Generic; 3 | 4 | namespace Fenix2GSX.UI.Views.Audio 5 | { 6 | public partial class ModelDeviceBlacklist(ModelAudio modelAudio) : ViewModelCollection(modelAudio.Source.AudioDeviceBlacklist, (s) => s, (s) => !string.IsNullOrWhiteSpace(s)) 7 | { 8 | protected virtual ModelAudio ModelAudio { get; } = modelAudio; 9 | public override ICollection Source => ModelAudio.Source.AudioDeviceBlacklist; 10 | public virtual List DeviceBlacklist => Source as List; 11 | 12 | public override bool UpdateSource(string oldItem, string newItem) 13 | { 14 | try 15 | { 16 | int index = DeviceBlacklist.IndexOf(oldItem); 17 | if (IsUpdateAllowed(oldItem, newItem) && index >= 0) 18 | { 19 | DeviceBlacklist[index] = newItem; 20 | return true; 21 | } 22 | else 23 | return false; 24 | } 25 | catch 26 | { 27 | return false; 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ControlAircraftOptions.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Aircraft & OFP Options 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Final LS Delay Minimum 52 | 53 | 54 | s 55 | 56 | 57 | Final LS Delay Maximum 58 | 59 | 60 | s 61 | 62 | 63 | 65 | Save FOB on Arrival and Load on Session Start (per Aircraft) 66 | 67 | 68 | 70 | 71 | Randomize Passenger on OFP Import: 72 | 73 | 74 | % Chance per Seat 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ControlAircraftOptions.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Fenix2GSX.UI.Views.Automation 4 | { 5 | public partial class ControlAircraftOptions : UserControl 6 | { 7 | protected virtual ModelAutomation ViewModel { get; } 8 | 9 | public ControlAircraftOptions(ModelAutomation viewModel) 10 | { 11 | InitializeComponent(); 12 | ViewModel = viewModel; 13 | this.DataContext = ViewModel; 14 | 15 | ViewModel.BindStringInteger(nameof(ViewModel.FinalDelayMin), InputFinalMinimum, "90"); 16 | ViewModel.BindStringInteger(nameof(ViewModel.FinalDelayMax), InputFinalMaximum, "150"); 17 | ViewModel.BindStringNumber(nameof(ViewModel.ChancePerSeat), InputChanceSeat, "2"); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ControlCompanyHubs.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Company Hubs 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 51 | 54 | 55 | 56 | 57 | 58 | 59 | 62 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ControlCompanyHubs.xaml.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework.UI.ViewModels; 2 | using CFIT.AppFramework.UI.ViewModels.Commands; 3 | using System.Windows.Controls; 4 | 5 | namespace Fenix2GSX.UI.Views.Automation 6 | { 7 | public partial class ControlCompanyHubs : UserControl 8 | { 9 | protected virtual ModelAutomation ViewModel { get; } 10 | protected virtual ViewModelSelector ViewModelSelector { get; } 11 | 12 | public ControlCompanyHubs(ModelAutomation viewModel) 13 | { 14 | InitializeComponent(); 15 | ViewModel = viewModel; 16 | this.DataContext = ViewModel; 17 | 18 | ViewModelSelector = new(ListHubs, ViewModel.CompanyHubs, AppWindow.IconLoader); 19 | ViewModelSelector.BindTextElement(InputHub); 20 | 21 | ButtonAdd.Command = ViewModelSelector.BindAddUpdateButton(ButtonAdd, ImageAdd, () => InputHub?.Text ?? "", () => !string.IsNullOrWhiteSpace(InputHub?.Text ?? "")); 22 | ViewModelSelector.AddUpdateCommand.Subscribe(InputHub); 23 | ViewModelSelector.AddUpdateCommand.Bind(InputHub); 24 | ButtonRemove.Command = ViewModelSelector.BindRemoveButton(ButtonRemove, () => ListHubs?.SelectedValue is string str && !string.IsNullOrWhiteSpace(str)); 25 | 26 | ButtonUp.Command = new CommandWrapper(() => ViewModel.CompanyHubs.MoveItem(ListHubs.SelectedIndex, -1), () => ListHubs?.SelectedIndex != -1).Subscribe(ListHubs); 27 | ButtonDown.Command = new CommandWrapper(() => ViewModel.CompanyHubs.MoveItem(ListHubs.SelectedIndex, 1), () => ListHubs?.SelectedIndex != -1).Subscribe(ListHubs); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ControlGateDoors.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Fenix2GSX.UI.Views.Automation 4 | { 5 | public partial class ControlGateDoors : UserControl 6 | { 7 | protected virtual ModelAutomation ViewModel { get; } 8 | 9 | public ControlGateDoors(ModelAutomation viewModel) 10 | { 11 | InitializeComponent(); 12 | ViewModel = viewModel; 13 | this.DataContext = ViewModel; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ControlGroundEquip.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Fenix2GSX.UI.Views.Automation 4 | { 5 | public partial class ControlGroundEquip : UserControl 6 | { 7 | protected virtual ModelAutomation ViewModel { get; } 8 | 9 | public ControlGroundEquip(ModelAutomation viewModel) 10 | { 11 | InitializeComponent(); 12 | ViewModel = viewModel; 13 | this.DataContext = ViewModel; 14 | 15 | ViewModel.BindStringInteger(nameof(ViewModel.ChockDelayMin), InputChockMinimum, "10"); 16 | ViewModel.BindStringInteger(nameof(ViewModel.ChockDelayMax), InputChockMaximum, "20"); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ControlGsxServices.xaml.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework.UI.ViewModels; 2 | using CFIT.AppFramework.UI.ViewModels.Commands; 3 | using Fenix2GSX.AppConfig; 4 | using Fenix2GSX.GSX.Services; 5 | using System; 6 | using System.ComponentModel; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | 10 | namespace Fenix2GSX.UI.Views.Automation 11 | { 12 | public partial class ControlGsxServices : UserControl, INotifyPropertyChanged 13 | { 14 | protected virtual ModelAutomation ViewModel { get; } 15 | protected virtual ViewModelSelector ViewModelSelector { get; } 16 | protected virtual TimeSpanConverter TimeSpanConverter { get; } = new TimeSpanConverter(); 17 | public virtual bool HasSelection => GridDepartureServices?.SelectedIndex != -1; 18 | 19 | public event PropertyChangedEventHandler? PropertyChanged; 20 | 21 | public ControlGsxServices(ModelAutomation viewModel) 22 | { 23 | InitializeComponent(); 24 | ViewModel = viewModel; 25 | this.DataContext = ViewModel; 26 | ImageEdit.Source = AppWindow.IconLoader.LoadIcon("edit"); 27 | 28 | ViewModel.BindStringNumber(nameof(ViewModel.RefuelRateKgSec), InputRefuelRate, "28"); 29 | ViewModel.BindStringInteger(nameof(ViewModel.RefuelTimeTargetSeconds), InputTimeTarget, "150"); 30 | 31 | ViewModelSelector = new(GridDepartureServices, ViewModel.DepartureServices, AppWindow.IconLoader); 32 | ViewModelSelector.BindTextElement(LabelServiceName, nameof(ServiceConfig.ServiceType)); 33 | ViewModelSelector.BindMember(SelectorActivation, nameof(ServiceConfig.ServiceActivation)); 34 | ViewModelSelector.BindTextElement(InputDuration, nameof(ServiceConfig.MinimumFlightDuration), "0", TimeSpanConverter); 35 | ViewModelSelector.BindMember(SelectorConstraint, nameof(ServiceConfig.ServiceConstraint)); 36 | 37 | ViewModelSelector.BindAddUpdateButton(ButtonEdit, null, GetItem, () => HasSelection); 38 | ViewModelSelector.AddUpdateCommand.Subscribe(SelectorActivation); 39 | ViewModelSelector.AddUpdateCommand.Subscribe(InputDuration); 40 | ViewModelSelector.AddUpdateCommand.Subscribe(SelectorConstraint); 41 | 42 | GridDepartureServices.SelectionChanged += (_, _) => NotifyPropertyChanged(nameof(HasSelection)); 43 | 44 | ButtonUp.Command = new CommandWrapper(() => ViewModel.DepartureServices.MoveItem(GridDepartureServices.SelectedIndex, -1), () => GridDepartureServices?.SelectedIndex != -1).Subscribe(GridDepartureServices); 45 | ButtonDown.Command = new CommandWrapper(() => ViewModel.DepartureServices.MoveItem(GridDepartureServices.SelectedIndex, 1), () => GridDepartureServices?.SelectedIndex != -1).Subscribe(GridDepartureServices); 46 | 47 | GridDepartureServices.SizeChanged += OnGridSizeChanged; 48 | } 49 | 50 | protected virtual void OnGridSizeChanged(object sender, SizeChangedEventArgs e) 51 | { 52 | try 53 | { 54 | LabelServiceName.Width = GridDepartureServices.Columns[0].ActualWidth; 55 | SelectorActivation.Width = GridDepartureServices.Columns[1].ActualWidth; 56 | SelectorConstraint.Width = GridDepartureServices.Columns[3].ActualWidth - 10; 57 | } 58 | catch { } 59 | } 60 | 61 | protected virtual ServiceConfig GetItem() 62 | { 63 | try 64 | { 65 | if (GridDepartureServices?.SelectedValue is ServiceConfig serviceConfig 66 | && SelectorActivation?.SelectedValue is GsxServiceActivation activation 67 | && !string.IsNullOrWhiteSpace(InputDuration?.Text) 68 | && SelectorConstraint?.SelectedValue is GsxServiceConstraint constraint) 69 | return new ServiceConfig(serviceConfig.ServiceType, activation, (TimeSpan)TimeSpanConverter.ConvertBack(InputDuration.Text, typeof(TimeSpan), null, null), constraint); 70 | } 71 | catch { } 72 | 73 | return null; 74 | } 75 | 76 | public virtual void NotifyPropertyChanged(string propertyName) 77 | { 78 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 79 | } 80 | 81 | public virtual void NotifyUpdate() 82 | { 83 | NotifyPropertyChanged(string.Empty); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ControlOperatorSelection.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Operator Selection 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | Automatically select Handling & Catering Operator 41 | 42 | 43 | Preferred Operators 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 53 | 54 | 55 | 58 | 61 | 62 | 63 | 64 | 65 | 66 | 69 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ControlOperatorSelection.xaml.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework.UI.ViewModels; 2 | using CFIT.AppFramework.UI.ViewModels.Commands; 3 | using System.Windows.Controls; 4 | 5 | namespace Fenix2GSX.UI.Views.Automation 6 | { 7 | 8 | public partial class ControlOperatorSelection : UserControl 9 | { 10 | protected virtual ModelAutomation ViewModel { get; } 11 | protected virtual ViewModelSelector ViewModelSelector { get; } 12 | 13 | public ControlOperatorSelection(ModelAutomation viewModel) 14 | { 15 | InitializeComponent(); 16 | ViewModel = viewModel; 17 | this.DataContext = ViewModel; 18 | 19 | ViewModelSelector = new(ListOperators, ViewModel.OperatorPreferences, AppWindow.IconLoader); 20 | ViewModelSelector.BindTextElement(InputOperator); 21 | 22 | ButtonAdd.Command = ViewModelSelector.BindAddUpdateButton(ButtonAdd, ImageAdd, () => InputOperator?.Text ?? "", () => !string.IsNullOrWhiteSpace(InputOperator?.Text ?? "")); 23 | ViewModelSelector.AddUpdateCommand.Subscribe(InputOperator); 24 | ViewModelSelector.AddUpdateCommand.Bind(InputOperator); 25 | ButtonRemove.Command = ViewModelSelector.BindRemoveButton(ButtonRemove, () => ListOperators?.SelectedValue is string str && !string.IsNullOrWhiteSpace(str)); 26 | 27 | ButtonUp.Command = new CommandWrapper(() => ViewModel.OperatorPreferences.MoveItem(ListOperators.SelectedIndex, -1), () => ListOperators?.SelectedIndex != -1).Subscribe(ListOperators); 28 | ButtonDown.Command = new CommandWrapper(() => ViewModel.OperatorPreferences.MoveItem(ListOperators.SelectedIndex, 1), () => ListOperators?.SelectedIndex != -1).Subscribe(ListOperators); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ControlSkipQuestions.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Skip Questions / Pop-ups 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | Suppress GSX Crew Question on Boarding & Deboarding 43 | 44 | 45 | 46 | Answer Tug Question during Boarding with: 47 | 49 | 50 | 51 | 52 | 54 | Skip Follow-Me Question on Gate Selection 55 | 56 | 57 | 59 | Automatically reopen Pushback Direction Menu 60 | 61 | 62 | 64 | Answer Cabin Call during Taxi-Out 65 | 66 | 67 | 69 | Answer Cabin Call during Approach 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ControlSkipQuestions.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Fenix2GSX.UI.Views.Automation 4 | { 5 | public partial class ControlSkipQuestions : UserControl 6 | { 7 | protected virtual ModelAutomation ViewModel { get; } 8 | 9 | public ControlSkipQuestions(ModelAutomation viewModel) 10 | { 11 | InitializeComponent(); 12 | ViewModel = viewModel; 13 | this.DataContext = ViewModel; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ModelCompanyHubs.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework.UI.ViewModels; 2 | using System.Collections.Generic; 3 | 4 | namespace Fenix2GSX.UI.Views.Automation 5 | { 6 | public partial class ModelCompanyHubs(ModelAutomation modelAutomation) : ViewModelCollection(modelAutomation.Source.CompanyHubs, (s) => s, (s) => !string.IsNullOrWhiteSpace(s)) 7 | { 8 | protected virtual ModelAutomation ModelAutomation { get; } = modelAutomation; 9 | public override ICollection Source => ModelAutomation.Source.CompanyHubs; 10 | public virtual List HubList => Source as List; 11 | 12 | public override bool UpdateSource(string oldItem, string newItem) 13 | { 14 | try 15 | { 16 | int index = HubList.IndexOf(oldItem); 17 | if (IsUpdateAllowed(oldItem, newItem) && index >= 0) 18 | { 19 | HubList[index] = newItem; 20 | return true; 21 | } 22 | else 23 | return false; 24 | } 25 | catch 26 | { 27 | return false; 28 | } 29 | } 30 | 31 | public virtual void MoveItem(int fromIndex, int step) 32 | { 33 | int toIndex = fromIndex + step; 34 | if (fromIndex < 0 || fromIndex >= HubList.Count || toIndex < 0 || toIndex >= HubList.Count) 35 | return; 36 | 37 | string temp = HubList[toIndex]; 38 | HubList[toIndex] = HubList[fromIndex]; 39 | HubList[fromIndex] = temp; 40 | NotifyCollectionChanged(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ModelDepartureServices.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework.UI.ValueConverter; 2 | using CFIT.AppFramework.UI.ViewModels; 3 | using Fenix2GSX.AppConfig; 4 | using Fenix2GSX.GSX.Services; 5 | using System.Collections.Generic; 6 | 7 | namespace Fenix2GSX.UI.Views.Automation 8 | { 9 | public partial class ModelDepartureServices(ModelAutomation modelAutomation) : ViewModelCollection(modelAutomation.Source.DepartureServices.Values, (s) => s, (s) => s != null) 10 | { 11 | protected virtual ModelAutomation ModelAutomation { get; } = modelAutomation; 12 | public override ICollection Source => ModelAutomation.Source.DepartureServices.Values; 13 | public virtual SortedDictionary DepartureServices => ModelAutomation.Source.DepartureServices; 14 | 15 | protected override void InitializeMemberBindings() 16 | { 17 | base.InitializeMemberBindings(); 18 | 19 | CreateMemberBinding(nameof(ServiceConfig.ServiceActivation), new NoneConverter()); 20 | CreateMemberBinding(nameof(ServiceConfig.ServiceConstraint), new NoneConverter()); 21 | } 22 | 23 | public override bool UpdateSource(ServiceConfig oldItem, ServiceConfig newItem) 24 | { 25 | try 26 | { 27 | if (oldItem.ServiceType == newItem.ServiceType) 28 | { 29 | oldItem.ServiceActivation = newItem.ServiceActivation; 30 | oldItem.MinimumFlightDuration = newItem.MinimumFlightDuration; 31 | oldItem.ServiceConstraint = newItem.ServiceConstraint; 32 | return true; 33 | } 34 | } 35 | catch { } 36 | 37 | return false; 38 | } 39 | 40 | public virtual void MoveItem(int fromIndex, int step) 41 | { 42 | int toIndex = fromIndex + step; 43 | if (fromIndex < 0 || fromIndex >= DepartureServices.Count || toIndex < 0 || toIndex >= DepartureServices.Count) 44 | return; 45 | 46 | var temp = DepartureServices[toIndex]; 47 | DepartureServices[toIndex] = DepartureServices[fromIndex]; 48 | DepartureServices[fromIndex] = temp; 49 | NotifyCollectionChanged(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ModelOperatorPreferences.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework.UI.ViewModels; 2 | using System.Collections.Generic; 3 | 4 | namespace Fenix2GSX.UI.Views.Automation 5 | { 6 | public partial class ModelOperatorPreferences(ModelAutomation modelAutomation) : ViewModelCollection(modelAutomation.Source.OperatorPreferences, (s) => s, (s) => !string.IsNullOrWhiteSpace(s)) 7 | { 8 | protected virtual ModelAutomation ModelAutomation { get; } = modelAutomation; 9 | public override ICollection Source => ModelAutomation.Source.OperatorPreferences; 10 | public virtual List OperatorList => Source as List; 11 | 12 | public override bool UpdateSource(string oldItem, string newItem) 13 | { 14 | try 15 | { 16 | int index = OperatorList.IndexOf(oldItem); 17 | if (IsUpdateAllowed(oldItem, newItem) && index >= 0) 18 | { 19 | OperatorList[index] = newItem; 20 | return true; 21 | } 22 | else 23 | return false; 24 | } 25 | catch 26 | { 27 | return false; 28 | } 29 | } 30 | 31 | public virtual void MoveItem(int fromIndex, int step) 32 | { 33 | int toIndex = fromIndex + step; 34 | if (fromIndex < 0 || fromIndex >= OperatorList.Count || toIndex < 0 || toIndex >= OperatorList.Count) 35 | return; 36 | 37 | string temp = OperatorList[toIndex]; 38 | OperatorList[toIndex] = OperatorList[fromIndex]; 39 | OperatorList[fromIndex] = temp; 40 | NotifyCollectionChanged(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ViewAutomation.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Profile: 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Automation/ViewAutomation.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Windows.Controls; 3 | 4 | namespace Fenix2GSX.UI.Views.Automation 5 | { 6 | public enum SettingControl 7 | { 8 | GateDoors = 0, 9 | GroundEquip = 1, 10 | GsxServices = 2, 11 | OperatorSelection = 3, 12 | CompanyHubs = 4, 13 | SkipQuestions = 5, 14 | AircraftOptions = 6, 15 | } 16 | 17 | public partial class ViewAutomation : UserControl, IView 18 | { 19 | protected virtual ModelAutomation ViewModel { get; } 20 | protected virtual Dictionary SettingControls { get; } = []; 21 | protected static Dictionary SettingGroups { get; } = new() 22 | { 23 | { SettingControl.GateDoors, "Gate & Doors" }, 24 | { SettingControl.GroundEquip, "Ground Equipment" }, 25 | { SettingControl.GsxServices, "GSX Services" }, 26 | { SettingControl.OperatorSelection, "Operator Selection" }, 27 | { SettingControl.CompanyHubs, "Company Hubs" }, 28 | { SettingControl.SkipQuestions, "Skip Questions" }, 29 | { SettingControl.AircraftOptions, "Aircraft Options" }, 30 | }; 31 | 32 | public ViewAutomation() 33 | { 34 | InitializeComponent(); 35 | ViewModel = new(AppService.Instance); 36 | this.DataContext = ViewModel; 37 | 38 | SettingControls.Add(SettingControl.GateDoors, new ControlGateDoors(ViewModel)); 39 | SettingControls.Add(SettingControl.GroundEquip, new ControlGroundEquip(ViewModel)); 40 | SettingControls.Add(SettingControl.GsxServices, new ControlGsxServices(ViewModel)); 41 | SettingControls.Add(SettingControl.OperatorSelection, new ControlOperatorSelection(ViewModel)); 42 | SettingControls.Add(SettingControl.CompanyHubs, new ControlCompanyHubs(ViewModel)); 43 | SettingControls.Add(SettingControl.SkipQuestions, new ControlSkipQuestions(ViewModel)); 44 | SettingControls.Add(SettingControl.AircraftOptions, new ControlAircraftOptions(ViewModel)); 45 | 46 | SelectorSettingGroup.ItemsSource = SettingGroups; 47 | SelectorSettingGroup.SelectionChanged += OnSelectionChanged; 48 | } 49 | 50 | protected virtual void OnSelectionChanged(object sender, SelectionChangedEventArgs e) 51 | { 52 | if (SelectorSettingGroup?.SelectedValue is SettingControl controlKey && SettingControls.TryGetValue(controlKey, out var control)) 53 | ViewSettingGroup.Content = control; 54 | } 55 | 56 | public virtual void Start() 57 | { 58 | if (SelectorSettingGroup?.SelectedValue is not SettingControl) 59 | SelectorSettingGroup.SelectedIndex = 0; 60 | } 61 | 62 | public virtual void Stop() 63 | { 64 | 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/ModelBase.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework.UI.ViewModels; 2 | using CFIT.SimConnectLib; 3 | using Fenix2GSX.Aircraft; 4 | using Fenix2GSX.AppConfig; 5 | using Fenix2GSX.Audio; 6 | using Fenix2GSX.GSX; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.ComponentModel.DataAnnotations; 10 | using System.Runtime.CompilerServices; 11 | 12 | namespace Fenix2GSX.UI.Views 13 | { 14 | public abstract partial class ModelBase(TObject source, AppService appService) : ViewModelBase(source) where TObject : class 15 | { 16 | protected virtual AppService AppService { get; } = appService; 17 | protected virtual Config Config => AppService.Config; 18 | protected virtual SimConnectController SimConnectController => AppService.SimService.Controller; 19 | protected virtual SimConnectManager SimConnect => AppService.SimConnect; 20 | protected virtual GsxController GsxController => AppService.GsxService; 21 | protected virtual AudioController AudioController => AppService.AudioService; 22 | protected virtual AircraftInterface AircraftInterface => GsxController?.AircraftInterface; 23 | protected virtual AircraftProfile AircraftProfile => GsxController?.AircraftProfile; 24 | 25 | public virtual DisplayUnit DisplayUnit { get => Config.DisplayUnit; set { Config.WeightDisplayUnit = value; NotifyDisplayUnit(); Config.NotifyDisplayUnit(); } } 26 | public virtual string DisplayUnitString => Config.WeightDisplayUnitString.ToString().ToLowerInvariant(); 27 | public virtual Dictionary TextDisplayUnit { get; } = new() 28 | { 29 | { DisplayUnit.KG, "kg" }, 30 | { DisplayUnit.LB, "lb" }, 31 | }; 32 | 33 | public virtual double ConvertKgToDisplayUnit(double kg) 34 | { 35 | return Config.ConvertKgToDisplayUnit(kg); 36 | } 37 | 38 | public virtual double ConvertLbToDisplayUnit(double lb) 39 | { 40 | return Config.ConvertLbToDisplayUnit(lb); 41 | } 42 | 43 | public virtual double ConvertFromDisplayUnitKg(double value) 44 | { 45 | return Config.ConvertFromDisplayUnitKg(value); 46 | } 47 | 48 | public virtual void SaveConfig() 49 | { 50 | Config.SaveConfiguration(); 51 | } 52 | 53 | public virtual void SetModelValue(T value, Func validator = null, Action callback = null, [CallerMemberName] string propertyName = null!) 54 | { 55 | SetSourceValue(value, validator, callback, propertyName); 56 | SaveConfig(); 57 | } 58 | 59 | public virtual void NotifyDisplayUnit() 60 | { 61 | NotifyPropertyChanged(nameof(DisplayUnit)); 62 | NotifyPropertyChanged(nameof(DisplayUnitString)); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Monitor/ViewMonitor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Fenix2GSX.UI.Views.Monitor 4 | { 5 | public partial class ViewMonitor : UserControl, IView 6 | { 7 | protected virtual ModelMonitor ViewModel { get; } 8 | 9 | public ViewMonitor() 10 | { 11 | InitializeComponent(); 12 | ViewModel = new(AppService.Instance); 13 | this.DataContext = ViewModel; 14 | } 15 | 16 | public virtual void Start() 17 | { 18 | ViewModel.Start(); 19 | } 20 | 21 | public virtual void Stop() 22 | { 23 | ViewModel?.Stop(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Profiles/ModelProfileCollection.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework.UI.ViewModels; 2 | using Fenix2GSX.AppConfig; 3 | using System.Collections.Generic; 4 | 5 | namespace Fenix2GSX.UI.Views.Profiles 6 | { 7 | public partial class ModelProfileCollection(ICollection source) : ViewModelCollection(source, (i) => i, (p) => !string.IsNullOrWhiteSpace(p?.Name)) 8 | { 9 | public override bool UpdateSource(AircraftProfile oldItem, AircraftProfile newItem) 10 | { 11 | try 12 | { 13 | if (Contains(oldItem)) 14 | { 15 | oldItem.Copy(newItem); 16 | return true; 17 | } 18 | else 19 | return false; 20 | } 21 | catch 22 | { 23 | return false; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Profiles/ModelProfiles.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework.UI.ViewModels; 2 | using CFIT.AppFramework.UI.ViewModels.Commands; 3 | using CFIT.AppTools; 4 | using CommunityToolkit.Mvvm.ComponentModel; 5 | using Fenix2GSX.Aircraft; 6 | using Fenix2GSX.AppConfig; 7 | using Fenix2GSX.GSX; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Collections.Specialized; 11 | using System.Linq; 12 | using System.Windows.Controls.Primitives; 13 | using System.Windows.Threading; 14 | 15 | namespace Fenix2GSX.UI.Views.Profiles 16 | { 17 | public partial class ModelProfiles : ViewModelBase 18 | { 19 | protected virtual Selector Selector { get; } 20 | protected virtual ModelProfileCollection ProfileCollection { get; } 21 | public virtual ViewModelSelector ViewModelSelector { get; } 22 | protected virtual DispatcherTimer UpdateTimer { get; set; } 23 | protected virtual Config Config => this.Source.Config; 24 | protected virtual GsxController GsxController => this.Source.GsxService; 25 | protected virtual AircraftInterface AircraftInterface => GsxController?.AircraftInterface; 26 | protected virtual bool ForceRefresh { get; set; } = false; 27 | public virtual ICommandWrapper SetActiveCommand { get; } 28 | 29 | public ModelProfiles(AppService source, Selector selector) : base(source) 30 | { 31 | Selector = selector; 32 | ProfileCollection = new ModelProfileCollection(Config.AircraftProfiles); 33 | ViewModelSelector = new(Selector, ProfileCollection, AppWindow.IconLoader); 34 | 35 | Selector.SelectionChanged += (_, _) => NotifyPropertyChanged(nameof(IsEditAllowed)); 36 | ProfileCollection.CreateMemberBinding(nameof(AircraftProfile.MatchType), null); 37 | ProfileCollection.CollectionChanged += OnCollectionChanged; 38 | 39 | SetActiveCommand = new CommandWrapper(() => GsxController.SetAircraftProfile((Selector?.SelectedValue as AircraftProfile)?.Name), () => Selector?.SelectedValue is AircraftProfile); 40 | SetActiveCommand.Subscribe(Selector); 41 | } 42 | 43 | protected virtual void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) 44 | { 45 | Config.SaveConfiguration(); 46 | } 47 | 48 | public virtual void CheckActiveProfile() 49 | { 50 | if (!Config.AircraftProfiles.Any(p => p.Name == GsxController.AircraftProfile?.Name)) 51 | GsxController.LoadAircraftProfile(); 52 | } 53 | 54 | protected override void InitializeModel() 55 | { 56 | UpdateTimer = new DispatcherTimer() 57 | { 58 | Interval = TimeSpan.FromMilliseconds(AppService.Instance.Config.UiRefreshInterval), 59 | }; 60 | UpdateTimer.Tick += OnUpdate; 61 | } 62 | 63 | public virtual void Start() 64 | { 65 | ForceRefresh = true; 66 | UpdateTimer.Start(); 67 | } 68 | 69 | public virtual void Stop() 70 | { 71 | UpdateTimer?.Stop(); 72 | } 73 | 74 | protected virtual void UpdateState(string propertyValue, T value) 75 | { 76 | try 77 | { 78 | if (string.IsNullOrEmpty(propertyValue) || (object)value == null) 79 | return; 80 | 81 | if (!this.GetPropertyValue(propertyValue)?.Equals(value) == true || ForceRefresh) 82 | this.SetPropertyValue(propertyValue, value); 83 | } 84 | catch { } 85 | } 86 | 87 | protected virtual void OnUpdate(object? sender, EventArgs e) 88 | { 89 | try { UpdateState(nameof(CurrentAirline), AircraftInterface?.Airline); } catch { } 90 | try { UpdateState(nameof(CurrentRegistration), AircraftInterface?.Registration); } catch { } 91 | try { UpdateState(nameof(CurrentTitle), AircraftInterface?.Title); } catch { } 92 | try { UpdateState(nameof(CurrentProfile), GsxController?.AircraftProfile?.ToString() ?? ""); } catch { } 93 | ForceRefresh = false; 94 | } 95 | 96 | [ObservableProperty] 97 | protected string _CurrentAirline = ""; 98 | 99 | [ObservableProperty] 100 | protected string _CurrentRegistration = ""; 101 | 102 | [ObservableProperty] 103 | protected string _CurrentTitle = ""; 104 | 105 | [ObservableProperty] 106 | protected string _CurrentProfile = ""; 107 | 108 | public static Dictionary MatchTypes { get; } = new() 109 | { 110 | {ProfileMatchType.Default, "Default" }, 111 | {ProfileMatchType.Airline, "Airline" }, 112 | {ProfileMatchType.Title, "Title" }, 113 | {ProfileMatchType.Registration, "Registration" }, 114 | }; 115 | 116 | public virtual bool IsSelectionNonDefault() 117 | { 118 | return !IsSelectionDefault(); 119 | } 120 | 121 | public virtual bool IsEditAllowed => !IsSelectionDefault() || Selector?.SelectedValue == null; 122 | 123 | public virtual bool IsSelectionDefault() 124 | { 125 | return (Selector?.SelectedValue is AircraftProfile profile && profile.MatchType == ProfileMatchType.Default); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Profiles/ViewProfiles.xaml.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppFramework.UI.ViewModels; 2 | using Fenix2GSX.AppConfig; 3 | using System.Windows.Controls; 4 | 5 | namespace Fenix2GSX.UI.Views.Profiles 6 | { 7 | public partial class ViewProfiles : UserControl, IView 8 | { 9 | protected virtual ModelProfiles ViewModel { get; } 10 | protected virtual ViewModelSelector ViewModelSelector => ViewModel.ViewModelSelector; 11 | 12 | public ViewProfiles() 13 | { 14 | InitializeComponent(); 15 | 16 | ViewModel = new(AppService.Instance, SelectorProfiles); 17 | this.DataContext = ViewModel; 18 | 19 | InputType.ItemsSource = ModelProfiles.MatchTypes; 20 | 21 | ViewModelSelector.BindTextElement(InputName, nameof(AircraftProfile.Name)); 22 | ViewModelSelector.BindMember(InputType, nameof(AircraftProfile.MatchType)); 23 | ViewModelSelector.BindTextElement(InputMatchString, nameof(AircraftProfile.MatchString)); 24 | 25 | ButtonAdd.Command = ViewModelSelector.BindAddUpdateButton(ButtonAdd, ImageAdd, GetItem, IsItemValid); 26 | ViewModelSelector.AddUpdateCommand.Subscribe(InputName); 27 | ViewModelSelector.AddUpdateCommand.Subscribe(InputType); 28 | ViewModelSelector.AddUpdateCommand.Subscribe(InputMatchString); 29 | 30 | ButtonRemove.Command = ViewModelSelector.BindRemoveButton(ButtonRemove, ViewModel.IsSelectionNonDefault); 31 | ViewModelSelector.RemoveCommand.Executed += () => ViewModel.CheckActiveProfile(); 32 | ViewModelSelector.RemoveCommand.Subscribe(InputName); 33 | ViewModelSelector.RemoveCommand.Subscribe(InputType); 34 | ViewModelSelector.RemoveCommand.Subscribe(InputMatchString); 35 | 36 | ButtonSetActive.Command = ViewModel.SetActiveCommand; 37 | } 38 | 39 | protected virtual AircraftProfile GetItem() 40 | { 41 | try 42 | { 43 | return new AircraftProfile() { Name = InputName.Text, MatchType = (ProfileMatchType)InputType.SelectedValue, MatchString = InputMatchString.Text }; 44 | } 45 | catch 46 | { 47 | return default; 48 | } 49 | } 50 | 51 | public virtual bool IsItemValid() 52 | { 53 | return !string.IsNullOrWhiteSpace(InputName?.Text) && InputType?.SelectedValue is ProfileMatchType type && type != ProfileMatchType.Default && !string.IsNullOrWhiteSpace(InputMatchString?.Text); 54 | } 55 | 56 | public virtual void Start() 57 | { 58 | ViewModel.Start(); 59 | } 60 | 61 | public virtual void Stop() 62 | { 63 | ViewModel?.Stop(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Settings/ModelSettings.cs: -------------------------------------------------------------------------------- 1 | using Fenix2GSX.AppConfig; 2 | using System.ComponentModel; 3 | 4 | namespace Fenix2GSX.UI.Views.Settings 5 | { 6 | public partial class ModelSettings(AppService appService) : ModelBase(appService.Config, appService) 7 | { 8 | protected override void InitializeModel() 9 | { 10 | Config.PropertyChanged += OnPropertyChanged; 11 | } 12 | 13 | protected virtual void OnPropertyChanged(object? sender, PropertyChangedEventArgs e) 14 | { 15 | if (e?.PropertyName == nameof(DisplayUnit)) 16 | { 17 | NotifyPropertyChanged(nameof(FenixWeightBag)); 18 | NotifyPropertyChanged(nameof(FuelResetDefaultKg)); 19 | NotifyPropertyChanged(nameof(FuelCompareVariance)); 20 | } 21 | } 22 | 23 | public virtual double FenixWeightBag { get => ConvertKgToDisplayUnit(Source.FenixWeightBag); set => SetModelValue(ConvertFromDisplayUnitKg(value)); } 24 | public virtual double FuelResetDefaultKg { get => ConvertKgToDisplayUnit(Source.FuelResetDefaultKg); set => SetModelValue(ConvertFromDisplayUnitKg(value)); } 25 | public virtual double FuelCompareVariance { get => ConvertKgToDisplayUnit(Source.FuelCompareVariance); set => SetModelValue(ConvertFromDisplayUnitKg(value)); } 26 | public virtual bool FuelRoundUp100 { get => Source.FuelRoundUp100; set => SetModelValue(value); } 27 | public virtual bool DingOnStartup { get => Source.DingOnStartup; set => SetModelValue(value); } 28 | public virtual bool DingOnFinal { get => Source.DingOnFinal; set => SetModelValue(value); } 29 | public virtual bool DingOnTurnaround { get => Source.DingOnTurnaround; set => SetModelValue(value); } 30 | public virtual int CargoPercentChangePerSec { get => Source.CargoPercentChangePerSec; set => SetModelValue(value); } 31 | public virtual int DoorCargoDelay { get => Source.DoorCargoDelay; set => SetModelValue(value); } 32 | public virtual bool SkipWalkAround { get => Source.SkipWalkAround; set => SetModelValue(value); } 33 | public virtual bool RestartGsxOnTaxiIn { get => Source.RestartGsxOnTaxiIn; set => SetModelValue(value); } 34 | public virtual bool EfbResetOnStartup { get => Source.EfbResetOnStartup; set => SetModelValue(value); } 35 | public virtual bool RunGsxService { get => Source.RunGsxService; set => SetModelValue(value); } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Fenix2GSX/UI/Views/Settings/ViewSettings.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace Fenix2GSX.UI.Views.Settings 4 | { 5 | public partial class ViewSettings : UserControl, IView 6 | { 7 | protected virtual ModelSettings ViewModel { get; } 8 | 9 | public ViewSettings() 10 | { 11 | InitializeComponent(); 12 | ViewModel = new(AppService.Instance); 13 | this.DataContext = ViewModel; 14 | 15 | ViewModel.BindStringNumber(nameof(ViewModel.FenixWeightBag), InputBagWeight); 16 | ViewModel.BindStringNumber(nameof(ViewModel.FuelResetDefaultKg), InputFuelDefault); 17 | ViewModel.BindStringNumber(nameof(ViewModel.FuelCompareVariance), InputFuelVariance); 18 | ViewModel.BindStringInteger(nameof(ViewModel.CargoPercentChangePerSec), InputCargoRate); 19 | ViewModel.BindStringInteger(nameof(ViewModel.DoorCargoDelay), InputDoorCargoDelay); 20 | } 21 | 22 | public virtual void Start() 23 | { 24 | 25 | } 26 | 27 | public virtual void Stop() 28 | { 29 | 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Installer/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Installer/AppIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Installer/AppIcon.ico -------------------------------------------------------------------------------- /Installer/AppMain.cs: -------------------------------------------------------------------------------- 1 | using CFIT.Installer; 2 | using CFIT.Installer.UI.Behavior; 3 | using System; 4 | 5 | namespace Installer 6 | { 7 | public class AppMain 8 | { 9 | public static InstallerApp Instance { get; private set; } 10 | 11 | [STAThread] 12 | public static int Main(string[] args) 13 | { 14 | Instance = new InstallerApp(new Definition(args)); 15 | return Instance.Start(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Installer/Config.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppLogger; 2 | using CFIT.AppTools; 3 | using CFIT.Installer.Product; 4 | using System; 5 | using System.IO; 6 | 7 | namespace Installer 8 | { 9 | public class Config : ConfigBase 10 | { 11 | public override string ProductName { get { return "Fenix2GSX"; } } 12 | public override string ProductExePath { get { return Path.Combine(ProductPath, "bin", ProductExe); } } 13 | public virtual string InstallerExtractDir { get { return Path.Combine(ProductPath, "bin"); } } 14 | 15 | public static readonly string OptionResetConfiguration = "ResetConfiguration"; 16 | public static readonly string OptionRemoveMobiflight = "RemoveMobiflight"; 17 | public static readonly string StateRemoveMobiAllowed = "RemoveMobiAllowed"; 18 | 19 | //Worker: .NET 20 | public virtual bool NetRuntimeDesktop { get; set; } = true; 21 | public virtual string NetVersion { get; set; } = "8.0.16"; 22 | public virtual bool CheckMajorEqual { get; set; } = true; 23 | public virtual string NetUrl { get; set; } = "https://builds.dotnet.microsoft.com/dotnet/WindowsDesktop/8.0.16/windowsdesktop-runtime-8.0.16-win-x64.exe"; 24 | public virtual string NetInstaller { get; set; } = "windowsdesktop-runtime-8.0.16-win-x64.exe"; 25 | 26 | public override void CheckInstallerOptions() 27 | { 28 | base.CheckInstallerOptions(); 29 | 30 | //ResetConfig 31 | SetOption(OptionResetConfiguration, false); 32 | 33 | //Removal of Mobi Flight 34 | SetOption(OptionRemoveMobiflight, false); 35 | if (MobiInstalled() || PilotsdeckInstalled()) 36 | SetOption(StateRemoveMobiAllowed, false); 37 | else 38 | SetOption(StateRemoveMobiAllowed, true); 39 | } 40 | 41 | public static bool MobiInstalled() 42 | { 43 | bool result; 44 | try 45 | { 46 | result = !string.IsNullOrWhiteSpace(Sys.GetRegistryValue(@"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\MobiFlight Connector", "UninstallString")); 47 | } 48 | catch (Exception ex) 49 | { 50 | result = false; 51 | Logger.LogException(ex); 52 | } 53 | return result; 54 | } 55 | 56 | public static bool PilotsdeckInstalled() 57 | { 58 | bool result; 59 | try 60 | { 61 | result = File.Exists(Path.Combine(Sys.FolderAppDataRoaming(), @"Elgato\StreamDeck\Plugins\com.extension.pilotsdeck.sdPlugin\PilotsDeck.exe")); 62 | } 63 | catch (Exception ex) 64 | { 65 | result = false; 66 | Logger.LogException(ex); 67 | } 68 | return result; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Installer/ConfigPage.cs: -------------------------------------------------------------------------------- 1 | using CFIT.Installer.Product; 2 | using CFIT.Installer.UI.Behavior; 3 | using CFIT.Installer.UI.Config; 4 | 5 | namespace Installer 6 | { 7 | public class ConfigPage : PageConfig 8 | { 9 | public Config Config { get { return BaseConfig as Config; } } 10 | 11 | public override void CreateConfigItems() 12 | { 13 | ConfigItemHelper.CreateCheckboxDesktopLink(Config, ConfigBase.OptionDesktopLink, Items); 14 | ConfigItemHelper.CreateRadioAutoStart(Config, Items); 15 | if (Config.Mode == SetupMode.UPDATE) 16 | Items.Add(new ConfigItemCheckbox("Reset Configuration", "Reset App Configuration to Default (only for Troubleshooting)", Config.OptionResetConfiguration, Config)); 17 | if (Config.GetOption(Config.StateRemoveMobiAllowed)) 18 | Items.Add(new ConfigItemCheckbox("Remove Mobiflight Module", "Remove the Mobiflight Module from MSFS' Community Folder (not required anymore for Fenix2GSX).\n\nATTENTION: Make sure no other Application is using it before removing it!\n(You will only see this Option if MobiFlight Connector and PilotsDeck are not detected as installed)", Config.OptionRemoveMobiflight, Config)); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Installer/CreateDefaultConfig.ps1: -------------------------------------------------------------------------------- 1 | ### args[0] => Solution/Base Path 2 | ### args[1] => Payload Path 3 | ### args[2] => App Binary (published) Path 4 | 5 | $cmd = $args[2] 6 | $dest = $args[1] 7 | write-host "$cmd" 8 | write-host "$dest" 9 | Invoke-Expression "$cmd --writeConfig $dest" | Out-Null #For AppFramework -------------------------------------------------------------------------------- /Installer/Definition.cs: -------------------------------------------------------------------------------- 1 | using CFIT.Installer.Product; 2 | 3 | namespace Installer 4 | { 5 | public class Definition : ProductDefinition 6 | { 7 | public Config Config { get { return BaseConfig as Config; } } 8 | public WorkerManager WorkerManager { get { return BaseWorker as WorkerManager; } } 9 | 10 | public Definition(string[] args) : base(args) 11 | { 12 | 13 | } 14 | 15 | protected override void CreateConfig() 16 | { 17 | BaseConfig = new Config(); 18 | } 19 | 20 | protected override void CreateWorker() 21 | { 22 | BaseWorker = new WorkerManager(Config); 23 | } 24 | 25 | protected override void CreateWindowBehavior() 26 | { 27 | base.CreateWindowBehavior(); 28 | BaseBehavior.WelcomeLogoWidth = 192; 29 | BaseBehavior.WelcomeLogoResource = "Payload/icon"; 30 | } 31 | 32 | protected override void CreatePageConfig() 33 | { 34 | PageBehaviors.Add(InstallerPages.CONFIG, new ConfigPage()); 35 | } 36 | 37 | //protected override void ParseArguments(string[] args) 38 | //{ 39 | // base.ParseArguments(args); 40 | // WorkerPackagePaths.ParseSimArguments(args, BaseConfig); 41 | //} 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Installer/ExportInstaller.ps1: -------------------------------------------------------------------------------- 1 | ### POST (Installer) 2 | ### pwsh -ExecutionPolicy Unrestricted -file "$(ProjectDir)ExportInstaller.ps1" $(SolutionDir) $(TargetDir) $(TargetFileName) "" 3 | 4 | #Exit inner Invocation when invoked with dotnet cli 5 | if ($args[0] -eq "*Undefined*") { 6 | exit 0 7 | } 8 | 9 | #Script Parameters 10 | $pathBase = $args[0] #0 Solution Directory 11 | $binOutDir = $args[1] #1 Absolute Path to Output Directory 12 | $binOutFile = $args[2] #2 Installer Binary Filename 13 | $binOutPath = Join-Path $args[1] $args[2] 14 | $appName = $args[3] #3 Application Name to use in Filename 15 | $suffix = "latest" #4 (Optional) Suffix to add to the File (default 'latest') 16 | if ([bool]$args[4]) { 17 | $suffix = $args[4] 18 | } 19 | 20 | Write-Host "Exporting $appName Installer ..." 21 | Copy-Item $binOutPath (Join-Path $pathBase "$appName-Installer-$suffix.exe") -Force | Out-Null 22 | 23 | -------------------------------------------------------------------------------- /Installer/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /Installer/PackageApp.ps1: -------------------------------------------------------------------------------- 1 | ### PRE (Installer) 2 | ### pwsh -ExecutionPolicy Unrestricted -file "$(ProjectDir)PackageApp.ps1" $(SolutionDir) "Installer" "Payload" "APP-PROJECT" "APP-NAME" "bin\publish" 3 | ### add version.json as Embedded Resource 4 | 5 | #Exit inner Invocation when invoked with dotnet cli 6 | if ($args[0] -eq "*Undefined*") { 7 | exit 0 8 | } 9 | 10 | Function UpdateAssemblyInfo{ 11 | param ($projectDir, $assemblyField, $newValue) 12 | $inFile = Join-Path $projectDir "Properties\AssemblyInfo.cs" 13 | $outFile = Join-Path $projectDir "Properties\AssemblyInfo.out" 14 | Get-Content -Path $inFile | % { $_ -Replace ('\[assembly: (' + "$assemblyField" + ')\("([^"]*)"\)]'), ('[assembly: $1("' + "$newValue" + '")]') } | Out-File $outFile -Encoding utf8NoBOM 15 | Move-Item $outFile $inFile -Force | Out-Null 16 | } 17 | 18 | #Script Parameters 19 | $basePath = $args[0] #0 Solution Directory (all other Paths relative to that) 20 | $pathProjectInstaller = Join-Path $basePath $args[1] #1 Installer Project Directory 21 | $pathPayload = Join-Path $basePath (Join-Path $args[1] $args[2]) #2 Payload Directory for Installer 22 | $projectName = $args[3] #3 App Project (Directory) Name 23 | $appName = $args[4] #4 App Binary Name 24 | $pathPublish = Join-Path (Join-Path $basePath $appName) $args[5] #5 Publish Directory of App (to pack for Installer) - relative to base 25 | $binFile = "$appName.exe" 26 | 27 | $zipPath = Join-Path $pathPayload "AppPackage.zip" 28 | $binPath = Join-Path $pathPublish $binFile 29 | $confPath = Join-Path $basePath $confFile 30 | $pathProjectApp = Join-Path $basePath $projectName 31 | 32 | #Get App Version 33 | Write-Host "Read $appName Binary ..." 34 | $version = (Get-Item $binPath).VersionInfo.FileVersion 35 | $timestamp = (Get-Item $binPath).LastWriteTimeUtc | Get-Date -Format "yyyy.MM.dd.HHmm" 36 | $company = (Get-Item $binPath).VersionInfo.CompanyName 37 | $year = Get-Date -Format "yyyy" 38 | 39 | #Version JSON 40 | Write-Host "Create version.json ..." 41 | @" 42 | { 43 | "Version": "$version", 44 | "Timestamp": "$timestamp" 45 | } 46 | "@ | Out-File (Join-Path $pathPayload "version.json") -Encoding utf8NoBOM 47 | 48 | #AssemblyInfo File 49 | Write-Host "Update Installer AssemblyInfo ..." 50 | UpdateAssemblyInfo $pathProjectInstaller "AssemblyTitle" "$appName Installer v$version ($timestamp)" 51 | UpdateAssemblyInfo $pathProjectInstaller "AssemblyDescription" "Installer Application for $appName" 52 | UpdateAssemblyInfo $pathProjectInstaller "AssemblyCompany" "$company" 53 | UpdateAssemblyInfo $pathProjectInstaller "AssemblyProduct" "$appName Installer" 54 | UpdateAssemblyInfo $pathProjectInstaller "AssemblyCopyright" "Copyright © $year" 55 | UpdateAssemblyInfo $pathProjectInstaller "AssemblyVersion" "$version" 56 | UpdateAssemblyInfo $pathProjectInstaller "AssemblyFileVersion" "$version" 57 | 58 | 59 | #AppPackage ZIP 60 | Write-Host "Zip AppPackage ..." 61 | Remove-Item $zipPath -ErrorAction SilentlyContinue | Out-Null 62 | & "C:\Program Files\7-Zip\7z.exe" a -tzip $zipPath ($pathPublish + "\*") | Out-Null 63 | 64 | #Config File 65 | Write-Host "Create default Config ..." 66 | cd $pathProjectInstaller 67 | .\CreateDefaultConfig.ps1 $basePath $pathPayload $binPath | Out-Null 68 | 69 | exit 0 70 | 71 | 72 | -------------------------------------------------------------------------------- /Installer/Payload/AppPackage.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Installer/Payload/AppPackage.zip -------------------------------------------------------------------------------- /Installer/Payload/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/Installer/Payload/icon.png -------------------------------------------------------------------------------- /Installer/Payload/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "0.5.0.0", 3 | "Timestamp": "2025.06.10.1913" 4 | } 5 | -------------------------------------------------------------------------------- /Installer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using System.Windows; 4 | 5 | // Allgemeine Informationen über eine Assembly werden über die folgenden 6 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, 7 | // die einer Assembly zugeordnet sind. 8 | [assembly: AssemblyTitle("Fenix2GSX Installer v0.5.0.0 (2025.06.10.1913)")] 9 | [assembly: AssemblyDescription("Installer Application for Fenix2GSX")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Fragtality")] 12 | [assembly: AssemblyProduct("Fenix2GSX Installer")] 13 | [assembly: AssemblyCopyright("Copyright © 2025")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly 18 | // für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von 19 | // COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. 20 | [assembly: ComVisible(false)] 21 | 22 | //Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie 23 | //ImCodeVerwendeteKultur in der .csproj-Datei 24 | //in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch 25 | //(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung 26 | //des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile, 27 | //sodass es mit der UICulture-Einstellung in der Projektdatei übereinstimmt. 28 | 29 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 30 | 31 | 32 | [assembly: ThemeInfo( 33 | ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher 34 | //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird, 35 | // oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.) 36 | ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs 37 | //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird, 38 | // designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.) 39 | )] 40 | 41 | 42 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: 43 | // 44 | // Hauptversion 45 | // Nebenversion 46 | // Buildnummer 47 | // Revision 48 | // 49 | [assembly: AssemblyVersion("0.5.0.0")] 50 | [assembly: AssemblyFileVersion("0.5.0.0")] 51 | -------------------------------------------------------------------------------- /Installer/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Dieser Code wurde von einem Tool generiert. 4 | // Laufzeitversion: 4.0.30319.42000 5 | // 6 | // Änderungen an dieser Datei können fehlerhaftes Verhalten verursachen und gehen verloren, wenn 7 | // der Code erneut generiert wird. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Installer.Properties 12 | { 13 | 14 | 15 | /// 16 | /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. 17 | /// 18 | // Diese Klasse wurde von der StronglyTypedResourceBuilder-Klasse 19 | // über ein Tool wie ResGen oder Visual Studio automatisch generiert. 20 | // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen 21 | // mit der Option /str erneut aus, oder erstellen Sie Ihr VS-Projekt neu. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Installer.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle 56 | /// Ressourcenlookups, die diese stark typisierte Ressourcenklasse verwenden. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Installer/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /Installer/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Installer.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Installer/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Installer/WorkerInstallUpdate.cs: -------------------------------------------------------------------------------- 1 | using CFIT.AppLogger; 2 | using CFIT.Installer.LibFunc; 3 | using CFIT.Installer.LibWorker; 4 | using System.IO; 5 | using System.Threading; 6 | 7 | namespace Installer 8 | { 9 | public class WorkerInstallUpdate : WorkerAppInstall 10 | { 11 | public bool ResetConfiguration { get; set; } = false; 12 | 13 | public WorkerInstallUpdate(Config config) : base(config) 14 | { 15 | SetPropertyFromOption(Config.OptionResetConfiguration); 16 | } 17 | 18 | protected override void CreateFileExclusions() 19 | { 20 | 21 | } 22 | 23 | protected override bool DeleteOldFiles() 24 | { 25 | FuncIO.DeleteDirectory(Path.Combine(Config.ProductPath, "log"), true, true); 26 | FuncIO.DeleteDirectory(InstallerExtractDir, true, true); 27 | 28 | if (File.Exists(Config.ProductConfigPath) && ResetConfiguration) 29 | { 30 | Logger.Debug($"Deleting Config File '{Config.ProductConfigPath}'"); 31 | FuncIO.DeleteFile(Config.ProductConfigPath); 32 | } 33 | 34 | return Directory.Exists(InstallerExtractDir); 35 | } 36 | 37 | protected override bool CreateDefaultConfig() 38 | { 39 | using (var stream = GetAppConfig()) 40 | { 41 | var confStream = File.Create(Config.ProductConfigPath); 42 | stream.Seek(0, SeekOrigin.Begin); 43 | stream.CopyTo(confStream); 44 | confStream.Flush(true); 45 | confStream.Close(); 46 | } 47 | Thread.Sleep(250); 48 | return Config.HasConfigFile; 49 | } 50 | 51 | protected override bool FinalizeSetup() 52 | { 53 | string logDir = Path.Combine(Config.ProductPath, "log"); 54 | if (!Directory.Exists(logDir)) 55 | Directory.CreateDirectory(logDir); 56 | 57 | return Directory.Exists(logDir); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Installer/WorkerManager.cs: -------------------------------------------------------------------------------- 1 | using CFIT.Installer.LibFunc; 2 | using CFIT.Installer.LibWorker; 3 | using CFIT.Installer.Product; 4 | 5 | namespace Installer 6 | { 7 | public class WorkerManager : WorkerManagerBase 8 | { 9 | public Config Config { get { return BaseConfig as Config; } } 10 | 11 | public WorkerManager(Config config) : base(config) 12 | { 13 | 14 | } 15 | 16 | protected void CreateInstallUpdateTasks(SetupMode key) 17 | { 18 | WorkerQueues[key].Enqueue(new WorkerDotNet(Config)); 19 | if (Config?.GetOption(Config.StateRemoveMobiAllowed) == true) 20 | { 21 | var worker = new WorkerPackagePaths(Config); 22 | worker.SearchSimulators.Add(Simulator.MSFS2020); 23 | worker.SearchSimulators.Add(Simulator.MSFS2024); 24 | WorkerQueues[key].Enqueue(worker); 25 | } 26 | WorkerQueues[key].Enqueue(new WorkerInstallUpdate(Config)); 27 | WorkerQueues[key].Enqueue(new WorkerAutoStart(Config)); 28 | if (Config?.GetOption(ConfigBase.OptionDesktopLink) == true) 29 | WorkerQueues[key].Enqueue(new WorkerDesktopLinkCreate(Config)); 30 | if (Config?.GetOption(Config.OptionRemoveMobiflight) == true) 31 | WorkerQueues[key].Enqueue(new WorkerRemoveMobi(Config)); 32 | } 33 | 34 | protected override void CreateInstallTasks() 35 | { 36 | CreateInstallUpdateTasks(SetupMode.INSTALL); 37 | } 38 | 39 | protected override void CreateRemovalTasks() 40 | { 41 | WorkerQueues[SetupMode.REMOVE].Enqueue(new WorkerAppRemove(Config)); 42 | if (Config.Mode == SetupMode.REMOVE) 43 | Config.SetOption(ConfigBase.OptionAutoStartTargets, SimAutoStart.NOAUTO); 44 | WorkerQueues[SetupMode.REMOVE].Enqueue(new WorkerAutoStart(Config)); 45 | WorkerQueues[SetupMode.REMOVE].Enqueue(new WorkerDesktopLinkRemove(Config)); 46 | } 47 | 48 | protected override void CreateUpdateTasks() 49 | { 50 | CreateInstallUpdateTasks(SetupMode.UPDATE); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Installer/WorkerRemoveMobi.cs: -------------------------------------------------------------------------------- 1 | using CFIT.Installer.LibFunc; 2 | using CFIT.Installer.Product; 3 | using CFIT.Installer.Tasks; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Threading.Tasks; 7 | 8 | namespace Installer 9 | { 10 | public class WorkerRemoveMobi : TaskWorker 11 | { 12 | public virtual Dictionary MsfsPackagePaths { get; set; } 13 | public static readonly string ModuleName = "mobiflight-event-module"; 14 | 15 | public WorkerRemoveMobi(Config config) : base(config, "Remove MobiFlight Module", "Checking Package Paths ...") 16 | { 17 | Model.DisplayInSummary = false; 18 | Model.DisplayCompleted = true; 19 | } 20 | 21 | protected override async Task DoRun() 22 | { 23 | int result = 0; 24 | 25 | if (MsfsPackagePaths == null || MsfsPackagePaths.Count <= 0) 26 | { 27 | if (!Config.HasOption(ConfigBase.OptionPackagePaths, out Dictionary paths) || paths?.Count == 0) 28 | { 29 | Model.SetError($"No Package Paths for MSFS set in Options - abort!"); 30 | return false; 31 | } 32 | 33 | MsfsPackagePaths = paths; 34 | } 35 | 36 | if (MsfsPackagePaths.ContainsKey(Simulator.MSFS2020)) 37 | { 38 | foreach (var packagePath in MsfsPackagePaths[Simulator.MSFS2020]) 39 | { 40 | string path = $@"{packagePath}\{ModuleName}"; 41 | if (Directory.Exists(path)) 42 | { 43 | Model.SetState($"Removing MobiFlight Module for MSFS2020"); 44 | Directory.Delete(path, true); 45 | if (Directory.Exists(path)) 46 | result--; 47 | } 48 | } 49 | } 50 | 51 | await Task.Delay(150); 52 | 53 | if (MsfsPackagePaths.ContainsKey(Simulator.MSFS2024)) 54 | { 55 | foreach (var packagePath in MsfsPackagePaths[Simulator.MSFS2024]) 56 | { 57 | string path = $@"{packagePath}\{ModuleName}"; 58 | if (Directory.Exists(path)) 59 | { 60 | Model.SetState($"Removing MobiFlight Module for MSFS2024"); 61 | Directory.Delete(path, true); 62 | if (Directory.Exists(path)) 63 | result--; 64 | } 65 | } 66 | } 67 | 68 | if (result >= 0) 69 | Model.SetSuccess("MobiFlight Module removed from Community Folder."); 70 | 71 | return result >= 0; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Installer/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Fragtality 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NuPreBuild.ps1: -------------------------------------------------------------------------------- 1 | ### PRE 2 | ### pwsh -ExecutionPolicy Unrestricted -file "$(ProjectDir)..\NuPreBuild.ps1" $(SolutionDir) $(ProjectDir) "PROJECT" PACKAGES... 3 | 4 | $basePath = $args[0] 5 | $projectDir = $args[1] 6 | if ($args[0] -eq "*Undefined*" -or -not $args[0]) { 7 | cd $projectDir 8 | cd .. 9 | $basePath = (pwd).Path 10 | } 11 | 12 | $packageName = $args[2] 13 | $pathRepo = "..\CFIT\PackageRepo" 14 | $nugetCli = Join-Path $basePath "nuget.exe" 15 | 16 | $packCfg = "packages.config" 17 | Function GetInstalledVersion{ 18 | param ($package) 19 | if ((Test-Path -Path $packCfg)) { 20 | return (([xml](Get-Content $packCfg)).packages.ChildNodes | Where-Object id -like $package).version 21 | } else { 22 | $regex = (dotnet list package | Select-String -Pattern ($package + '\s+\S+\s+(\S+)')) 23 | if ($regex.Matches -and $regex.Matches.length -gt 0 -and $regex.Matches[0].Groups.length -gt 1) { 24 | return $regex.Matches[0].Groups[1].Value 25 | } else { 26 | return "" 27 | } 28 | } 29 | } 30 | 31 | Function UpdatePackage{ 32 | param ($package) 33 | if ((Test-Path -Path $packCfg)) { 34 | Invoke-Expression "$nugetCli update $packCfg -Id $package -Source $pathRepo -NonInteractive -Verbosity quiet" 35 | } else { 36 | Invoke-Expression "dotnet add package $package" | Out-Null 37 | } 38 | } 39 | 40 | cd $basePath 41 | cd $pathRepo 42 | $pathRepo = (pwd).Path 43 | 44 | Write-Host "Checking NuGet Dependencies for $packageName ..." 45 | cd $projectDir 46 | $count = 0 47 | for ($index = 3; $index -lt $args.length; $index++) { 48 | $package = $args[$index] 49 | $packageVersion = GetInstalledVersion($package) 50 | $latestFile = (ls $pathRepo | Where-Object Name -like "$package*" | Sort-Object LastWriteTime)[-1].Name 51 | $latestVersion = (echo $latestFile | Select-String -Pattern '[^0-9]*(\d+\.\d+\.\d+\.\d+)\.nupkg').Matches[0].Groups[1].Value 52 | if ($latestVersion -and $packageVersion -and $latestVersion -ne $packageVersion) { 53 | Write-Host " => Updating '$package': $packageVersion => $latestVersion" 54 | UpdatePackage($package) 55 | $count++ 56 | } 57 | } 58 | 59 | if ($count -gt 0) { 60 | if (-not (Test-Path -Path $packCfg)) { 61 | dotnet restore --verbosity quiet 62 | } 63 | } 64 | exit 0 -------------------------------------------------------------------------------- /img/Fs2Crew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/img/Fs2Crew.png -------------------------------------------------------------------------------- /img/appmonitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/img/appmonitor.png -------------------------------------------------------------------------------- /img/flowpro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/img/flowpro.png -------------------------------------------------------------------------------- /img/flowproGSX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/img/flowproGSX.png -------------------------------------------------------------------------------- /img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/img/icon.png -------------------------------------------------------------------------------- /img/ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/img/ui.png -------------------------------------------------------------------------------- /img/ui3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/img/ui3.png -------------------------------------------------------------------------------- /img/ui4-commented.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/img/ui4-commented.png -------------------------------------------------------------------------------- /img/ui4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/img/ui4.png -------------------------------------------------------------------------------- /img/ui5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/img/ui5.png -------------------------------------------------------------------------------- /img/ui6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/img/ui6.png -------------------------------------------------------------------------------- /nuget.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fragtality/Fenix2GSX/348dac054db90562082d96727a397cae5b64e862/nuget.exe --------------------------------------------------------------------------------