From 607cb3d52eaf93d5d0b8110a5d53e7d427ed0307 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Mon, 28 Jan 2013 22:40:01 +0000 Subject: [PATCH 01/16] Branched for long(er)term changes. Planned: - UI overhaul - Customizing the various LED states for each function - Multiple profiles From 688b859f663acc5f919648d56a75e054889c8727 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Wed, 6 Feb 2013 07:19:35 +0000 Subject: [PATCH 02/16] Added: basic interfaces for refactoring --- G940LEDControl/Forms/ButtonSelectFrm.dfm | 2 - G940LEDControl/Forms/ButtonSelectFrm.pas | 2 +- G940LEDControl/Forms/MainFrm.dfm | 4 - G940LEDControl/G940LEDControl.dpr | 10 +- G940LEDControl/G940LEDControl.dproj | 8 + G940LEDControl/Units/ConfigConversion.pas | 57 +++++ G940LEDControl/Units/LEDFunction.pas | 181 ++++++++++++++++ G940LEDControl/Units/LEDFunctionIntf.pas | 43 ++++ G940LEDControl/Units/LEDFunctionRegistry.pas | 216 +++++++++++++++++++ G940LEDControl/Units/LEDState.pas | 52 +++++ G940LEDControl/Units/LEDStateIntf.pas | 17 ++ G940LEDControl/Units/ObserverIntf.pas | 20 ++ G940LEDControl/Units/StaticLEDFunction.pas | 107 +++++++++ 13 files changed, 711 insertions(+), 8 deletions(-) create mode 100644 G940LEDControl/Units/ConfigConversion.pas create mode 100644 G940LEDControl/Units/LEDFunction.pas create mode 100644 G940LEDControl/Units/LEDFunctionIntf.pas create mode 100644 G940LEDControl/Units/LEDFunctionRegistry.pas create mode 100644 G940LEDControl/Units/LEDState.pas create mode 100644 G940LEDControl/Units/LEDStateIntf.pas create mode 100644 G940LEDControl/Units/ObserverIntf.pas create mode 100644 G940LEDControl/Units/StaticLEDFunction.pas diff --git a/G940LEDControl/Forms/ButtonSelectFrm.dfm b/G940LEDControl/Forms/ButtonSelectFrm.dfm index 461ceda..9ea0bce 100644 --- a/G940LEDControl/Forms/ButtonSelectFrm.dfm +++ b/G940LEDControl/Forms/ButtonSelectFrm.dfm @@ -67,7 +67,6 @@ object ButtonSelectForm: TButtonSelectForm Default = True ModalResult = 1 TabOrder = 1 - ExplicitTop = 94 end object btnCancel: TButton Left = 401 @@ -79,7 +78,6 @@ object ButtonSelectForm: TButtonSelectForm Caption = 'Cancel' ModalResult = 2 TabOrder = 2 - ExplicitTop = 94 end object edtButton: TEdit Left = 80 diff --git a/G940LEDControl/Forms/ButtonSelectFrm.pas b/G940LEDControl/Forms/ButtonSelectFrm.pas index 575a45c..deeeb88 100644 --- a/G940LEDControl/Forms/ButtonSelectFrm.pas +++ b/G940LEDControl/Forms/ButtonSelectFrm.pas @@ -116,7 +116,7 @@ begin info.InstanceGUID := lpddi.guidInstance; info.ProductGUID := lpddi.guidProduct; - items.AddObject(String(lpddi.tszProductName), info); + items.AddObject(string(lpddi.tszProductName), info); Result := True; end; diff --git a/G940LEDControl/Forms/MainFrm.dfm b/G940LEDControl/Forms/MainFrm.dfm index f63ad32..85423db 100644 --- a/G940LEDControl/Forms/MainFrm.dfm +++ b/G940LEDControl/Forms/MainFrm.dfm @@ -275,10 +275,6 @@ object MainForm: TMainForm Caption = 'Extra' ImageIndex = 1 TabVisible = False - ExplicitLeft = 0 - ExplicitTop = 0 - ExplicitWidth = 0 - ExplicitHeight = 0 object GroupBox1: TGroupBox AlignWithMargins = True Left = 6 diff --git a/G940LEDControl/G940LEDControl.dpr b/G940LEDControl/G940LEDControl.dpr index 0510214..6cc4657 100644 --- a/G940LEDControl/G940LEDControl.dpr +++ b/G940LEDControl/G940LEDControl.dpr @@ -10,7 +10,15 @@ uses G940LEDStateConsumer in 'Units\G940LEDStateConsumer.pas', LEDFunctionMap in 'Units\LEDFunctionMap.pas', LEDStateConsumer in 'Units\LEDStateConsumer.pas', - LEDStateProvider in 'Units\LEDStateProvider.pas'; + LEDStateProvider in 'Units\LEDStateProvider.pas', + LEDStateIntf in 'Units\LEDStateIntf.pas', + LEDState in 'Units\LEDState.pas', + LEDFunctionIntf in 'Units\LEDFunctionIntf.pas', + ObserverIntf in 'Units\ObserverIntf.pas', + LEDFunction in 'Units\LEDFunction.pas', + StaticLEDFunction in 'Units\StaticLEDFunction.pas', + ConfigConversion in 'Units\ConfigConversion.pas', + LEDFunctionRegistry in 'Units\LEDFunctionRegistry.pas'; {$R *.res} diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj index 1e505f8..c4c26ff 100644 --- a/G940LEDControl/G940LEDControl.dproj +++ b/G940LEDControl/G940LEDControl.dproj @@ -173,6 +173,14 @@ + + + + + + + + Cfg_2 Base diff --git a/G940LEDControl/Units/ConfigConversion.pas b/G940LEDControl/Units/ConfigConversion.pas new file mode 100644 index 0000000..dd97bef --- /dev/null +++ b/G940LEDControl/Units/ConfigConversion.pas @@ -0,0 +1,57 @@ +unit ConfigConversion; + +interface + + { Version 0.x: registry -> 1.x: XML } + procedure Convert0To1; + + +implementation + +procedure Convert0To1; +begin +// FUNCTION_NONE = 0; +// FUNCTION_OFF = 1; +// FUNCTION_RED = 2; +// FUNCTION_AMBER = 3; +// FUNCTION_GREEN = 4; + +{ +FUNCTION_PROVIDER_OFFSET = 4 + + FUNCTION_FSX_GEAR = FUNCTION_PROVIDER_OFFSET + 1; + FUNCTION_FSX_LANDINGLIGHTS = FUNCTION_PROVIDER_OFFSET + 2; + FUNCTION_FSX_INSTRUMENTLIGHTS = FUNCTION_PROVIDER_OFFSET + 3; + FUNCTION_FSX_PARKINGBRAKE = FUNCTION_PROVIDER_OFFSET + 4; + FUNCTION_FSX_ENGINE = FUNCTION_PROVIDER_OFFSET + 5; + + FUNCTION_FSX_EXITDOOR = FUNCTION_PROVIDER_OFFSET + 6; + FUNCTION_FSX_STROBELIGHTS = FUNCTION_PROVIDER_OFFSET + 7; + FUNCTION_FSX_NAVLIGHTS = FUNCTION_PROVIDER_OFFSET + 8; + FUNCTION_FSX_BEACONLIGHTS = FUNCTION_PROVIDER_OFFSET + 9; + FUNCTION_FSX_FLAPS = FUNCTION_PROVIDER_OFFSET + 10; + FUNCTION_FSX_BATTERYMASTER = FUNCTION_PROVIDER_OFFSET + 11; + FUNCTION_FSX_AVIONICSMASTER = FUNCTION_PROVIDER_OFFSET + 12; + + FUNCTION_FSX_SPOILERS = FUNCTION_PROVIDER_OFFSET + 13; + + FUNCTION_FSX_PRESSURIZATIONDUMPSWITCH = FUNCTION_PROVIDER_OFFSET + 14; + FUNCTION_FSX_CARBHEAT = FUNCTION_PROVIDER_OFFSET + 15; + FUNCTION_FSX_AUTOPILOT = FUNCTION_PROVIDER_OFFSET + 16; + FUNCTION_FSX_FUELPUMP = FUNCTION_PROVIDER_OFFSET + 17; + + FUNCTION_FSX_TAILHOOK = FUNCTION_PROVIDER_OFFSET + 18; + + FUNCTION_FSX_AUTOPILOT_AMBER = FUNCTION_PROVIDER_OFFSET + 19; + FUNCTION_FSX_AUTOPILOT_HEADING = FUNCTION_PROVIDER_OFFSET + 20; + FUNCTION_FSX_AUTOPILOT_APPROACH = FUNCTION_PROVIDER_OFFSET + 21; + FUNCTION_FSX_AUTOPILOT_BACKCOURSE = FUNCTION_PROVIDER_OFFSET + 22; + FUNCTION_FSX_AUTOPILOT_ALTITUDE = FUNCTION_PROVIDER_OFFSET + 23; + FUNCTION_FSX_AUTOPILOT_NAV = FUNCTION_PROVIDER_OFFSET + 24; + + FUNCTION_FSX_TAXILIGHTS = FUNCTION_PROVIDER_OFFSET + 25; + FUNCTION_FSX_RECOGNITIONLIGHTS = FUNCTION_PROVIDER_OFFSET + 26; + } +end; + +end. diff --git a/G940LEDControl/Units/LEDFunction.pas b/G940LEDControl/Units/LEDFunction.pas new file mode 100644 index 0000000..47f8770 --- /dev/null +++ b/G940LEDControl/Units/LEDFunction.pas @@ -0,0 +1,181 @@ +unit LEDFunction; + +interface +uses + Classes, + + LEDFunctionIntf, + LEDStateIntf, + ObserverIntf; + + +type + TCustomLEDFunctionProvider = class(TInterfacedObject, ILEDFunctionProvider) + private + FFunctions: TInterfaceList; + protected + procedure RegisterFunction(AFunction: ILEDFunction); + protected + { ILEDFunctionProvider } + function GetUniqueName: string; virtual; abstract; + + function GetEnumerator: ILEDFunctionEnumerator; virtual; + public + constructor Create; + destructor Destroy; override; + end; + + + TCustomLEDFunction = class(TInterfacedObject, IObservable, ILEDFunction) + private + FCurrentState: ILEDState; + FObservers: TInterfaceList; + protected + procedure SetCurrentState(AState: ILEDState); virtual; + + procedure NotifyObservers; virtual; + + property Observers: TInterfaceList read FObservers; + protected + { IObservable } + procedure Attach(AObserver: IObserver); + procedure Detach(AObserver: IObserver); + + { ILEDFunction } + function GetCategoryName: string; virtual; abstract; + function GetDisplayName: string; virtual; abstract; + function GetUniqueName: string; virtual; abstract; + + function GetCurrentState: ILEDState; virtual; + public + constructor Create; + destructor Destroy; override; + end; + + + TLEDFunctionEnumerator = class(TInterfacedObject, ILEDFunctionEnumerator) + private + FList: TInterfaceList; + FIndex: Integer; + protected + { ILEDFunctionEnumerator } + function GetCurrent: ILEDFunction; virtual; + function MoveNext: Boolean; virtual; + public + constructor Create(AList: TInterfaceList); + end; + + +implementation +uses + SysUtils; + + +{ TCustomLEDFunction } +constructor TCustomLEDFunction.Create; +begin + inherited Create; + + FObservers := TInterfaceList.Create; +end; + + +destructor TCustomLEDFunction.Destroy; +begin + FreeAndNil(FObservers); + + inherited Destroy; +end; + + +procedure TCustomLEDFunction.Attach(AObserver: IObserver); +begin + +end; + + +procedure TCustomLEDFunction.Detach(AObserver: IObserver); +begin + +end; + + +function TCustomLEDFunction.GetCurrentState: ILEDState; +begin + +end; + + +procedure TCustomLEDFunction.SetCurrentState(AState: ILEDState); +begin + FCurrentState := AState; + NotifyObservers; +end; + + +procedure TCustomLEDFunction.NotifyObservers; +var + observer: IInterface; + +begin + for observer in Observers do + (observer as IObserver).Update(Self); +end; + + +{ TCustomLEDFunctionProvider } +constructor TCustomLEDFunctionProvider.Create; +begin + inherited Create; + + FFunctions := TInterfaceList.Create; +end; + + +destructor TCustomLEDFunctionProvider.Destroy; +begin + FreeAndNil(FFunctions); + + inherited; +end; + + +procedure TCustomLEDFunctionProvider.RegisterFunction(AFunction: ILEDFunction); +begin + { Make sure to explicitly request the ILEDFunction interface; I've experienced + incomparable pointers otherwise if we ever need to write an UnregisterFunction. + My best, but unverified, guess is that it works kinda like a VMT. } + FFunctions.Add(AFunction as ILEDFunction); +end; + + +function TCustomLEDFunctionProvider.GetEnumerator: ILEDFunctionEnumerator; +begin + Result := TLEDFunctionEnumerator.Create(FFunctions); +end; + + +{ TLEDFunctionEnumerator } +constructor TLEDFunctionEnumerator.Create(AList: TInterfaceList); +begin + inherited Create; + + FList := AList; + FIndex := -1; +end; + + +function TLEDFunctionEnumerator.GetCurrent: ILEDFunction; +begin + Result := (FList[FIndex] as ILEDFunction); +end; + + +function TLEDFunctionEnumerator.MoveNext: Boolean; +begin + Result := (FIndex < Pred(FList.Count)); + if Result then + Inc(FIndex); +end; + +end. diff --git a/G940LEDControl/Units/LEDFunctionIntf.pas b/G940LEDControl/Units/LEDFunctionIntf.pas new file mode 100644 index 0000000..1c56fec --- /dev/null +++ b/G940LEDControl/Units/LEDFunctionIntf.pas @@ -0,0 +1,43 @@ +unit LEDFunctionIntf; + +interface +uses + LEDStateIntf, + ObserverIntf; + + +type + ILEDFunction = interface; + ILEDFunctionEnumerator = interface; + + + ILEDFunctionProvider = interface + ['{B38F6F90-DC96-42CE-B8F0-21F0DD8AA537}'] + function GetUniqueName: string; + + function GetEnumerator: ILEDFunctionEnumerator; + end; + + + ILEDFunction = interface(IObservable) + ['{7087067A-1016-4A7D-ACB1-BA1F388DAD6C}'] + function GetCategoryName: string; + function GetDisplayName: string; + function GetUniqueName: string; + + function GetCurrentState: ILEDState; + end; + + + ILEDFunctionEnumerator = interface + ['{A03E4E54-19CB-4C08-AD5F-20265817086D}'] + function GetCurrent: ILEDFunction; + function MoveNext: Boolean; + + property Current: ILEDFunction read GetCurrent; + end; + + +implementation + +end. diff --git a/G940LEDControl/Units/LEDFunctionRegistry.pas b/G940LEDControl/Units/LEDFunctionRegistry.pas new file mode 100644 index 0000000..1cd56bf --- /dev/null +++ b/G940LEDControl/Units/LEDFunctionRegistry.pas @@ -0,0 +1,216 @@ +unit LEDFunctionRegistry; + +interface +uses + Classes, + + LEDFunctionIntf; + + +type + TLEDFunctionProviderList = class; + + TLEDFunctionRegistry = class(TObject) + private + FProviders: TLEDFunctionProviderList; + protected + class function Instance: TLEDFunctionRegistry; + + procedure DoRegister(AProvider: ILEDFunctionProvider); + procedure DoUnregister(AProvider: ILEDFunctionProvider); + function DoFind(const AUniqueName: string): ILEDFunctionProvider; + + function GetProviders: TLEDFunctionProviderList; + public + constructor Create; + destructor Destroy; override; + + class procedure Register(AProvider: ILEDFunctionProvider); + class procedure Unregister(AProvider: ILEDFunctionProvider); + + class function Find(const AUniqueName: string): ILEDFunctionProvider; + + class function Providers: TLEDFunctionProviderList; + end; + + + TLEDFunctionProviderListEnumerator = class; + + TLEDFunctionProviderList = class(TObject) + private + FList: TInterfaceList; + protected + procedure Add(AProvider: ILEDFunctionProvider); + procedure Remove(AProvider: ILEDFunctionProvider); + public + constructor Create; + destructor Destroy; override; + + function Find(const AUniqueName: string): ILEDFunctionProvider; + + function GetEnumerator: TLEDFunctionProviderListEnumerator; + end; + + + TLEDFunctionProviderListEnumerator = class(TInterfaceListEnumerator) + public + function GetCurrent: ILEDFunctionProvider; inline; + property Current: ILEDFunctionProvider read GetCurrent; + end; + + +implementation +uses + SysUtils; + + +var + RegistryInstance: TLEDFunctionRegistry; + + +{ TLEDFunctionRegistry } +class procedure TLEDFunctionRegistry.Register(AProvider: ILEDFunctionProvider); +begin + Instance.DoRegister(AProvider); +end; + + +class procedure TLEDFunctionRegistry.Unregister(AProvider: ILEDFunctionProvider); +begin + Instance.DoUnregister(AProvider); +end; + + +class function TLEDFunctionRegistry.Find(const AUniqueName: string): ILEDFunctionProvider; +begin + Result := Instance.DoFind(AUniqueName); +end; + + +class function TLEDFunctionRegistry.Providers: TLEDFunctionProviderList; +begin + Result := Instance.GetProviders; +end; + + +class function TLEDFunctionRegistry.Instance: TLEDFunctionRegistry; +begin + if not Assigned(RegistryInstance) then + RegistryInstance := TLEDFunctionRegistry.Create; + + Result := RegistryInstance; +end; + + +constructor TLEDFunctionRegistry.Create; +begin + inherited Create; + + FProviders := TLEDFunctionProviderList.Create; +end; + + +destructor TLEDFunctionRegistry.Destroy; +begin + FreeAndNil(FProviders); + + inherited; +end; + + +procedure TLEDFunctionRegistry.DoRegister(AProvider: ILEDFunctionProvider); +begin + FProviders.Add(AProvider); +end; + + +procedure TLEDFunctionRegistry.DoUnregister(AProvider: ILEDFunctionProvider); +begin + FProviders.Remove(AProvider); +end; + + +function TLEDFunctionRegistry.DoFind(const AUniqueName: string): ILEDFunctionProvider; +begin + Result := FProviders.Find(AUniqueName); +end; + + +function TLEDFunctionRegistry.GetProviders: TLEDFunctionProviderList; +begin + Result := FProviders; +end; + + +{ TLEDFunctionProviderList } +constructor TLEDFunctionProviderList.Create; +begin + inherited Create; + + FList := TInterfaceList.Create; +end; + + +destructor TLEDFunctionProviderList.Destroy; +begin + FreeAndNil(FList); + + inherited; +end; + + +function TLEDFunctionProviderList.Find(const AUniqueName: string): ILEDFunctionProvider; +var + provider: ILEDFunctionProvider; + +begin + Result := nil; + + for provider in Self do + if provider.GetUniqueName = AUniqueName then + begin + Result := provider; + break; + end; +end; + +procedure TLEDFunctionProviderList.Add(AProvider: ILEDFunctionProvider); +var + stableReference: ILEDFunctionProvider; + +begin + stableReference := (AProvider as ILEDFunctionProvider); + if FList.IndexOf(stableReference) = -1 then + FList.Add(stableReference); +end; + + +procedure TLEDFunctionProviderList.Remove(AProvider: ILEDFunctionProvider); +var + index: Integer; + +begin + index := FList.IndexOf(AProvider as ILEDFunctionProvider); + if index > -1 then + FList.Delete(index); +end; + + +function TLEDFunctionProviderList.GetEnumerator: TLEDFunctionProviderListEnumerator; +begin + Result := TLEDFunctionProviderListEnumerator.Create(FList); +end; + + +{ TLEDFunctionProviderListEnumerator } +function TLEDFunctionProviderListEnumerator.GetCurrent: ILEDFunctionProvider; +begin + Result := ((inherited GetCurrent) as ILEDFunctionProvider); +end; + + +initialization +finalization + FreeAndNil(RegistryInstance); + +end. diff --git a/G940LEDControl/Units/LEDState.pas b/G940LEDControl/Units/LEDState.pas new file mode 100644 index 0000000..2241b4f --- /dev/null +++ b/G940LEDControl/Units/LEDState.pas @@ -0,0 +1,52 @@ +unit LEDState; + +interface +uses + SysUtils, + + LEDStateIntf; + + +type + TCustomLEDState = class(TInterfacedObject, ILEDState) + protected + { ILEDState } + procedure Tick; virtual; + function GetColor: TLEDColor; virtual; abstract; + end; + + + TStaticLEDState = class(TCustomLEDState) + private + FColor: TLEDColor; + protected + function GetColor: TLEDColor; override; + public + constructor Create(AColor: TLEDColor); + end; + + +implementation + + +{ TCustomLEDState } +procedure TCustomLEDState.Tick; +begin +end; + + +{ TStaticLEDState } +constructor TStaticLEDState.Create(AColor: TLEDColor); +begin + inherited Create; + + FColor := AColor; +end; + + +function TStaticLEDState.GetColor: TLEDColor; +begin + Result := FColor; +end; + +end. diff --git a/G940LEDControl/Units/LEDStateIntf.pas b/G940LEDControl/Units/LEDStateIntf.pas new file mode 100644 index 0000000..8e56f5c --- /dev/null +++ b/G940LEDControl/Units/LEDStateIntf.pas @@ -0,0 +1,17 @@ +unit LEDStateIntf; + +interface +type + TLEDColor = (lcOff, lcGreen, lcAmber, lcRed); + + ILEDState = interface + ['{B40DF462-B660-4002-A6B9-DD30AC69E8DB}'] + procedure Tick; + + function GetColor: TLEDColor; + end; + + +implementation + +end. diff --git a/G940LEDControl/Units/ObserverIntf.pas b/G940LEDControl/Units/ObserverIntf.pas new file mode 100644 index 0000000..c5b0123 --- /dev/null +++ b/G940LEDControl/Units/ObserverIntf.pas @@ -0,0 +1,20 @@ +unit ObserverIntf; + +interface +type + IObserver = interface + ['{B78415C9-9F64-4AF1-8983-BACE2B7225EF}'] + procedure Update(Sender: IInterface); + end; + + + IObservable = interface + ['{BC004BDA-14E4-4923-BE6D-98A0746852F1}'] + procedure Attach(AObserver: IObserver); + procedure Detach(AObserver: IObserver); + end; + + +implementation + +end. diff --git a/G940LEDControl/Units/StaticLEDFunction.pas b/G940LEDControl/Units/StaticLEDFunction.pas new file mode 100644 index 0000000..5bd2c39 --- /dev/null +++ b/G940LEDControl/Units/StaticLEDFunction.pas @@ -0,0 +1,107 @@ +unit StaticLEDFunction; + +interface +uses + LEDFunction, + LEDStateIntf; + + +type + TStaticLEDFunctionProvider = class(TCustomLEDFunctionProvider) + protected + function GetUniqueName: string; override; + public + constructor Create; + end; + + + TStaticLEDFunction = class(TCustomLEDFunction) + private + FColor: TLEDColor; + protected + function GetCategoryName: string; override; + function GetDisplayName: string; override; + function GetUniqueName: string; override; + public + constructor Create(AColor: TLEDColor); + end; + + +implementation +uses + LEDFunctionRegistry; + + +const + CategoryStatic = 'Static'; + + ProviderUniqueName = 'static'; + + FunctionUniqueName: array[TLEDColor] of string = + ( + 'off', + 'green', + 'amber', + 'red' + ); + + FunctionDisplayName: array[TLEDColor] of string = + ( + 'Off', + 'Green', + 'Amber', + 'Red' + ); + + + +{ TStaticLEDFunctionProvider } +constructor TStaticLEDFunctionProvider.Create; +var + color: TLEDColor; + +begin + inherited Create; + + for color := Low(TLEDColor) to High(TLEDColor) do + RegisterFunction(TStaticLEDFunction.Create(color)); +end; + + +function TStaticLEDFunctionProvider.GetUniqueName: string; +begin + Result := ProviderUniqueName; +end; + + +{ TStaticLEDFunction } +constructor TStaticLEDFunction.Create(AColor: TLEDColor); +begin + inherited Create; + + FColor := AColor; +end; + + +function TStaticLEDFunction.GetCategoryName: string; +begin + Result := CategoryStatic; +end; + + +function TStaticLEDFunction.GetDisplayName: string; +begin + Result := FunctionDisplayName[FColor]; +end; + + +function TStaticLEDFunction.GetUniqueName: string; +begin + Result := FunctionUniqueName[FColor]; +end; + + +initialization + TLEDFunctionRegistry.Register(TStaticLEDFunctionProvider.Create); + +end. From 9da668afc16d65b165bb21b27ae45982eb4cf7bb Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Sun, 10 Feb 2013 16:39:00 +0000 Subject: [PATCH 03/16] Added: State abstraction layer; provides the ability for the user to select a color for each state a function supports Added: loading/saving profiles --- G940LEDControl/Forms/MainFrm.dfm | 755 ++++++++---------- G940LEDControl/Forms/MainFrm.pas | 720 ++++++++--------- G940LEDControl/G940LEDControl.dpr | 11 +- G940LEDControl/G940LEDControl.dproj | 11 +- G940LEDControl/G940LEDControl.res | Bin 28524 -> 27760 bytes G940LEDControl/Units/ConfigConversion.pas | 8 +- G940LEDControl/Units/DynamicLEDColor.pas | 76 ++ G940LEDControl/Units/LEDColor.pas | 27 + .../{LEDStateIntf.pas => LEDColorIntf.pas} | 13 +- G940LEDControl/Units/LEDFunction.pas | 72 +- G940LEDControl/Units/LEDFunctionIntf.pas | 17 +- G940LEDControl/Units/LEDFunctionRegistry.pas | 18 +- G940LEDControl/Units/LEDState.pas | 52 -- G940LEDControl/Units/LEDStateConsumer.pas | 1 - G940LEDControl/Units/Profile.pas | 232 ++++++ G940LEDControl/Units/StaticLEDColor.pas | 38 + G940LEDControl/Units/StaticLEDFunction.pas | 36 +- 17 files changed, 1200 insertions(+), 887 deletions(-) create mode 100644 G940LEDControl/Units/DynamicLEDColor.pas create mode 100644 G940LEDControl/Units/LEDColor.pas rename G940LEDControl/Units/{LEDStateIntf.pas => LEDColorIntf.pas} (54%) delete mode 100644 G940LEDControl/Units/LEDState.pas create mode 100644 G940LEDControl/Units/Profile.pas create mode 100644 G940LEDControl/Units/StaticLEDColor.pas diff --git a/G940LEDControl/Forms/MainFrm.dfm b/G940LEDControl/Forms/MainFrm.dfm index 85423db..24f95f5 100644 --- a/G940LEDControl/Forms/MainFrm.dfm +++ b/G940LEDControl/Forms/MainFrm.dfm @@ -4,7 +4,7 @@ object MainForm: TMainForm BorderIcons = [biSystemMenu, biMinimize] BorderStyle = bsSingle Caption = 'G940 LED Control' - ClientHeight = 513 + ClientHeight = 562 ClientWidth = 465 Color = clBtnFace Font.Charset = DEFAULT_CHARSET @@ -16,14 +16,15 @@ object MainForm: TMainForm Position = poScreenCenter OnCloseQuery = FormCloseQuery OnCreate = FormCreate + OnDestroy = FormDestroy PixelsPerInch = 96 TextHeight = 13 - object pcConnections: TPageControl + object PageControl: TPageControl AlignWithMargins = True Left = 8 Top = 80 Width = 449 - Height = 425 + Height = 474 Margins.Left = 8 Margins.Top = 8 Margins.Right = 8 @@ -31,417 +32,353 @@ object MainForm: TMainForm ActivePage = tsFSX Align = alClient TabOrder = 1 + ExplicitWidth = 390 + ExplicitHeight = 510 object tsFSX: TTabSheet - Caption = 'Flight Simulator X' - object gbFSXConnection: TGroupBox - AlignWithMargins = True - Left = 6 - Top = 6 - Width = 429 - Height = 63 - Margins.Left = 6 - Margins.Top = 6 - Margins.Right = 6 - Margins.Bottom = 0 - Align = alTop - Caption = ' Connection ' - TabOrder = 0 - object lblFSXLocal: TLabel - Left = 12 - Top = 29 - Width = 24 - Height = 13 - Caption = 'Local' - end - object btnFSXConnect: TButton - Left = 69 - Top = 24 - Width = 75 - Height = 25 - Caption = '&Connect' - TabOrder = 0 - OnClick = btnFSXConnectClick - end - object btnFSXDisconnect: TButton - Left = 150 - Top = 24 - Width = 75 - Height = 25 - Caption = '&Disconnect' - Enabled = False - TabOrder = 1 - OnClick = btnFSXDisconnectClick - end + Caption = 'Configuration' + ExplicitWidth = 382 + ExplicitHeight = 482 + DesignSize = ( + 441 + 446) + object lblP1Function: TLabel + Left = 64 + Top = 89 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: function]' + EllipsisPosition = epEndEllipsis + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + ExplicitWidth = 421 end - object pcFSXOptions: TPageControl - AlignWithMargins = True - Left = 6 - Top = 75 - Width = 429 - Height = 316 - Margins.Left = 6 - Margins.Top = 6 - Margins.Right = 6 - Margins.Bottom = 6 - ActivePage = tsFSXLEDButtons - Align = alClient + object lblP1Category: TLabel + Left = 64 + Top = 73 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: category]' + EllipsisPosition = epEndEllipsis + ExplicitWidth = 421 + end + object lblP2Function: TLabel + Left = 64 + Top = 136 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: function]' + EllipsisPosition = epEndEllipsis + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + ExplicitWidth = 421 + end + object lblP2Category: TLabel + Left = 64 + Top = 120 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: category]' + EllipsisPosition = epEndEllipsis + ExplicitWidth = 421 + end + object lblP3Function: TLabel + Left = 64 + Top = 183 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: function]' + EllipsisPosition = epEndEllipsis + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + ExplicitWidth = 421 + end + object lblP3Category: TLabel + Left = 64 + Top = 167 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: category]' + EllipsisPosition = epEndEllipsis + ExplicitWidth = 421 + end + object lblP4Function: TLabel + Left = 64 + Top = 230 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: function]' + EllipsisPosition = epEndEllipsis + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + ExplicitWidth = 421 + end + object lblP4Category: TLabel + Left = 64 + Top = 214 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: category]' + EllipsisPosition = epEndEllipsis + ExplicitWidth = 421 + end + object lblP5Function: TLabel + Left = 64 + Top = 277 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: function]' + EllipsisPosition = epEndEllipsis + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + ExplicitWidth = 421 + end + object lblP5Category: TLabel + Left = 64 + Top = 261 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: category]' + EllipsisPosition = epEndEllipsis + ExplicitWidth = 421 + end + object lblP6Function: TLabel + Left = 64 + Top = 324 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: function]' + EllipsisPosition = epEndEllipsis + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + ExplicitWidth = 421 + end + object lblP6Category: TLabel + Left = 64 + Top = 308 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: category]' + EllipsisPosition = epEndEllipsis + ExplicitWidth = 421 + end + object lblP7Function: TLabel + Left = 64 + Top = 371 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: function]' + EllipsisPosition = epEndEllipsis + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + ExplicitWidth = 421 + end + object lblP7Category: TLabel + Left = 64 + Top = 355 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: category]' + EllipsisPosition = epEndEllipsis + ExplicitWidth = 421 + end + object lblP8Function: TLabel + Left = 64 + Top = 418 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: function]' + EllipsisPosition = epEndEllipsis + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + ExplicitWidth = 421 + end + object lblP8Category: TLabel + Left = 64 + Top = 402 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: category]' + EllipsisPosition = epEndEllipsis + ExplicitWidth = 421 + end + object lblProfile: TLabel + Left = 11 + Top = 19 + Width = 30 + Height = 13 + Caption = 'Profile' + end + object bvlProfiles: TBevel + Left = 11 + Top = 52 + Width = 474 + Height = 13 + Shape = bsTopLine + end + object btnP1: TButton + Left = 11 + Top = 67 + Width = 41 + Height = 41 + Caption = 'P1' + TabOrder = 0 + end + object btnP2: TButton + Left = 11 + Top = 114 + Width = 41 + Height = 41 + Caption = 'P2' TabOrder = 1 - object tsFSXLEDButtons: TTabSheet - Caption = 'LED Buttons' - object gbFSXButtons: TGroupBox - AlignWithMargins = True - Left = 6 - Top = 6 - Width = 409 - Height = 251 - Margins.Left = 6 - Margins.Top = 6 - Margins.Right = 6 - Margins.Bottom = 6 - Align = alTop - Caption = ' Button configuration ' - TabOrder = 0 - DesignSize = ( - 409 - 251) - object lblFSXP1: TLabel - Left = 12 - Top = 27 - Width = 12 - Height = 13 - Caption = 'P1' - end - object lblFSXP2: TLabel - Left = 12 - Top = 54 - Width = 12 - Height = 13 - Caption = 'P2' - end - object lblFSXP3: TLabel - Left = 12 - Top = 81 - Width = 12 - Height = 13 - Caption = 'P3' - end - object lblFSXP4: TLabel - Left = 12 - Top = 108 - Width = 12 - Height = 13 - Caption = 'P4' - end - object lblFSXP5: TLabel - Left = 12 - Top = 135 - Width = 12 - Height = 13 - Caption = 'P5' - end - object lblFSXP6: TLabel - Left = 12 - Top = 162 - Width = 12 - Height = 13 - Caption = 'P6' - end - object lblFSXP7: TLabel - Left = 12 - Top = 189 - Width = 12 - Height = 13 - Caption = 'P7' - end - object lblFSXP8: TLabel - Left = 12 - Top = 216 - Width = 12 - Height = 13 - Caption = 'P8' - end - object cmbFSXP1: TComboBoxEx - Left = 69 - Top = 24 - Width = 328 - Height = 22 - ItemsEx = <> - Style = csExDropDownList - Anchors = [akLeft, akTop, akRight] - Font.Charset = DEFAULT_CHARSET - Font.Color = clWindowText - Font.Height = -11 - Font.Name = 'Tahoma' - Font.Style = [] - ParentFont = False - TabOrder = 0 - OnChange = FunctionComboBoxChange - DropDownCount = 20 - end - object cmbFSXP2: TComboBoxEx - Tag = 1 - Left = 69 - Top = 50 - Width = 328 - Height = 22 - ItemsEx = <> - Style = csExDropDownList - Anchors = [akLeft, akTop, akRight] - TabOrder = 1 - OnChange = FunctionComboBoxChange - DropDownCount = 20 - end - object cmbFSXP3: TComboBoxEx - Tag = 2 - Left = 69 - Top = 78 - Width = 328 - Height = 22 - ItemsEx = <> - Style = csExDropDownList - Anchors = [akLeft, akTop, akRight] - TabOrder = 2 - OnChange = FunctionComboBoxChange - DropDownCount = 20 - end - object cmbFSXP4: TComboBoxEx - Tag = 3 - Left = 69 - Top = 105 - Width = 328 - Height = 22 - ItemsEx = <> - Style = csExDropDownList - Anchors = [akLeft, akTop, akRight] - TabOrder = 3 - OnChange = FunctionComboBoxChange - DropDownCount = 20 - end - object cmbFSXP5: TComboBoxEx - Tag = 4 - Left = 69 - Top = 131 - Width = 328 - Height = 22 - ItemsEx = <> - Style = csExDropDownList - Anchors = [akLeft, akTop, akRight] - TabOrder = 4 - OnChange = FunctionComboBoxChange - DropDownCount = 20 - end - object cmbFSXP6: TComboBoxEx - Tag = 5 - Left = 69 - Top = 159 - Width = 328 - Height = 22 - ItemsEx = <> - Style = csExDropDownList - Anchors = [akLeft, akTop, akRight] - TabOrder = 5 - OnChange = FunctionComboBoxChange - DropDownCount = 20 - end - object cmbFSXP7: TComboBoxEx - Tag = 6 - Left = 69 - Top = 186 - Width = 328 - Height = 22 - ItemsEx = <> - Style = csExDropDownList - Anchors = [akLeft, akTop, akRight] - TabOrder = 6 - OnChange = FunctionComboBoxChange - DropDownCount = 20 - end - object cmbFSXP8: TComboBoxEx - Tag = 7 - Left = 69 - Top = 213 - Width = 328 - Height = 22 - ItemsEx = <> - Style = csExDropDownList - Anchors = [akLeft, akTop, akRight] - TabOrder = 7 - OnChange = FunctionComboBoxChange - DropDownCount = 20 - end - end - end - object tsFSXExtra: TTabSheet - Caption = 'Extra' - ImageIndex = 1 - TabVisible = False - object GroupBox1: TGroupBox - AlignWithMargins = True - Left = 6 - Top = 6 - Width = 409 - Height = 171 - Margins.Left = 6 - Margins.Top = 6 - Margins.Right = 6 - Margins.Bottom = 0 - Align = alTop - Caption = ' Zoom ' - TabOrder = 0 - object lblFSXToggleZoomButton: TLabel - Left = 57 - Top = 56 - Width = 77 - Height = 13 - Caption = 'Joystick button:' - end - object lblFSXZoomDepressed: TLabel - Left = 59 - Top = 111 - Width = 151 - Height = 13 - Caption = 'Zoom level (button depressed):' - end - object lblFSXZoomPressed: TLabel - Left = 59 - Top = 142 - Width = 139 - Height = 13 - Caption = 'Zoom level (button pressed):' - end - object lblFSXToggleZoomButtonName: TLabel - Left = 57 - Top = 75 - Width = 305 - Height = 13 - AutoSize = False - Caption = '[runtime]' - EllipsisPosition = epEndEllipsis - Font.Charset = DEFAULT_CHARSET - Font.Color = clWindowText - Font.Height = -11 - Font.Name = 'Tahoma' - Font.Style = [fsBold] - ParentFont = False - end - object cbFSXToggleZoom: TCheckBox - Left = 16 - Top = 24 - Width = 161 - Height = 17 - Caption = ' Toggle zoom level' - Checked = True - State = cbChecked - TabOrder = 0 - end - object btnFSXToggleZoom: TButton - Left = 368 - Top = 70 - Width = 34 - Height = 25 - Caption = '...' - TabOrder = 1 - OnClick = btnFSXToggleZoomClick - end - object cmbFSXZoomDepressed: TComboBox - Left = 288 - Top = 106 - Width = 114 - Height = 21 - Style = csDropDownList - DropDownCount = 20 - ItemIndex = 5 - TabOrder = 2 - Text = '80%' - Items.Strings = ( - '30%' - '40%' - '50%' - '60%' - '70%' - '80%' - '90%' - '100%' - '110%' - '120%' - '130%' - '140%' - '150%' - '175%' - '200%' - '250%' - '300%' - '400%') - end - object cmbFSXZoomPressed: TComboBox - Left = 288 - Top = 133 - Width = 114 - Height = 21 - Style = csDropDownList - DropDownCount = 20 - ItemIndex = 16 - TabOrder = 3 - Text = '300%' - Items.Strings = ( - '30%' - '40%' - '50%' - '60%' - '70%' - '80%' - '90%' - '100%' - '110%' - '120%' - '130%' - '140%' - '150%' - '175%' - '200%' - '250%' - '300%' - '400%') - end - end - object GroupBox2: TGroupBox - AlignWithMargins = True - Left = 6 - Top = 183 - Width = 409 - Height = 98 - Margins.Left = 6 - Margins.Top = 6 - Margins.Right = 6 - Margins.Bottom = 0 - Align = alTop - Caption = ' Engine thrust match ' - TabOrder = 1 - object TLabel - Left = 104 - Top = 40 - Width = 201 - Height = 13 - Caption = 'Sorry, configuration not implemented yet!' - end - object TLabel - Left = 120 - Top = 59 - Width = 160 - Height = 13 - Caption = 'Engine 1 links to 4, engine 2 to 3.' - end - end - end + end + object btnP3: TButton + Left = 11 + Top = 161 + Width = 41 + Height = 41 + Caption = 'P3' + TabOrder = 2 + end + object btnP4: TButton + Left = 11 + Top = 208 + Width = 41 + Height = 41 + Caption = 'P4' + TabOrder = 3 + end + object btnP5: TButton + Left = 11 + Top = 255 + Width = 41 + Height = 41 + Caption = 'P5' + TabOrder = 4 + end + object btnP6: TButton + Left = 11 + Top = 302 + Width = 41 + Height = 41 + Caption = 'P6' + TabOrder = 5 + end + object btnP7: TButton + Left = 11 + Top = 349 + Width = 41 + Height = 41 + Caption = 'P7' + TabOrder = 6 + end + object btnP8: TButton + Left = 11 + Top = 396 + Width = 41 + Height = 41 + Caption = 'P8' + TabOrder = 7 + end + object cmbProfiles: TComboBox + Left = 64 + Top = 16 + Width = 213 + Height = 21 + Style = csDropDownList + Anchors = [akLeft, akTop, akRight] + TabOrder = 8 + ExplicitWidth = 270 + end + object btnSaveProfile: TButton + Left = 283 + Top = 16 + Width = 75 + Height = 21 + Anchors = [akTop, akRight] + Caption = 'Save as...' + TabOrder = 9 + ExplicitLeft = 340 + end + object btnDeleteProfile: TButton + Left = 364 + Top = 16 + Width = 64 + Height = 21 + Anchors = [akTop, akRight] + Caption = 'Delete' + TabOrder = 10 + ExplicitLeft = 421 end end object tsAbout: TTabSheet Caption = 'About' ImageIndex = 1 - ExplicitLeft = 0 - ExplicitTop = 0 - ExplicitWidth = 0 - ExplicitHeight = 0 + ExplicitWidth = 382 + ExplicitHeight = 482 object lblVersionCaption: TLabel Left = 16 Top = 67 @@ -456,7 +393,7 @@ object MainForm: TMainForm Height = 13 Caption = 'lblVersion' end - object Label1: TLabel + object lblProductName: TLabel Left = 16 Top = 16 Width = 96 @@ -469,7 +406,7 @@ object MainForm: TMainForm Font.Style = [fsBold] ParentFont = False end - object Label2: TLabel + object lblCopyright: TLabel Left = 16 Top = 35 Width = 95 @@ -557,6 +494,7 @@ object MainForm: TMainForm Align = alTop BevelOuter = bvNone TabOrder = 0 + ExplicitWidth = 390 DesignSize = ( 449 64) @@ -938,6 +876,7 @@ object MainForm: TMainForm TabOrder = 0 Visible = False OnClick = btnRetryClick + ExplicitLeft = 315 end end end diff --git a/G940LEDControl/Forms/MainFrm.pas b/G940LEDControl/Forms/MainFrm.pas index e07ffc1..b8f3cdb 100644 --- a/G940LEDControl/Forms/MainFrm.pas +++ b/G940LEDControl/Forms/MainFrm.pas @@ -21,7 +21,8 @@ uses LEDFunctionMap, LEDStateConsumer, - LEDStateProvider; + LEDStateProvider, + Profile; const @@ -30,8 +31,15 @@ const MSG_UPDATE = 1; MSG_NOUPDATE = 2; + LED_COUNT = 8; + type - TComboBoxArray = array[0..7] of TComboBoxEx; + TLEDControls = record + ConfigureButton: TButton; + CategoryLabel: TLabel; + FunctionLabel: TLabel; + end; + TMainForm = class(TForm) imgStateNotFound: TImage; @@ -39,48 +47,13 @@ type imgStateFound: TImage; lblG940ThrottleState: TLabel; btnRetry: TButton; - pcConnections: TPageControl; + PageControl: TPageControl; pnlG940: TPanel; - tsFSX: TTabSheet; - gbFSXButtons: TGroupBox; - lblFSXP1: TLabel; - cmbFSXP1: TComboBoxEx; - cmbFSXP2: TComboBoxEx; - lblFSXP2: TLabel; - cmbFSXP3: TComboBoxEx; - lblFSXP3: TLabel; - cmbFSXP4: TComboBoxEx; - lblFSXP4: TLabel; - cmbFSXP5: TComboBoxEx; - lblFSXP5: TLabel; - cmbFSXP6: TComboBoxEx; - lblFSXP6: TLabel; - cmbFSXP7: TComboBoxEx; - lblFSXP7: TLabel; - cmbFSXP8: TComboBoxEx; - lblFSXP8: TLabel; - gbFSXConnection: TGroupBox; - btnFSXConnect: TButton; - btnFSXDisconnect: TButton; - lblFSXLocal: TLabel; - pcFSXOptions: TPageControl; - tsFSXLEDButtons: TTabSheet; - tsFSXExtra: TTabSheet; - GroupBox1: TGroupBox; - cbFSXToggleZoom: TCheckBox; - lblFSXToggleZoomButton: TLabel; - lblFSXZoomDepressed: TLabel; - lblFSXZoomPressed: TLabel; - lblFSXToggleZoomButtonName: TLabel; - btnFSXToggleZoom: TButton; - cmbFSXZoomDepressed: TComboBox; - cmbFSXZoomPressed: TComboBox; - GroupBox2: TGroupBox; tsAbout: TTabSheet; lblVersionCaption: TLabel; lblVersion: TLabel; - Label1: TLabel; - Label2: TLabel; + lblProductName: TLabel; + lblCopyright: TLabel; lblWebsiteLink: TLinkLabel; lblEmailLink: TLinkLabel; lblWebsite: TLabel; @@ -88,43 +61,76 @@ type cbCheckUpdates: TCheckBox; btnCheckUpdates: TButton; lblProxy: TLabel; + tsFSX: TTabSheet; + btnP1: TButton; + lblP1Function: TLabel; + lblP1Category: TLabel; + btnP2: TButton; + lblP2Function: TLabel; + lblP2Category: TLabel; + btnP3: TButton; + lblP3Function: TLabel; + lblP3Category: TLabel; + btnP4: TButton; + lblP4Function: TLabel; + lblP4Category: TLabel; + btnP5: TButton; + lblP5Function: TLabel; + lblP5Category: TLabel; + btnP6: TButton; + lblP6Function: TLabel; + lblP6Category: TLabel; + btnP7: TButton; + lblP7Function: TLabel; + lblP7Category: TLabel; + btnP8: TButton; + lblP8Function: TLabel; + lblP8Category: TLabel; + lblProfile: TLabel; + cmbProfiles: TComboBox; + btnSaveProfile: TButton; + btnDeleteProfile: TButton; + bvlProfiles: TBevel; procedure FormCreate(Sender: TObject); procedure btnRetryClick(Sender: TObject); - procedure btnFSXConnectClick(Sender: TObject); - procedure btnFSXDisconnectClick(Sender: TObject); - procedure btnFSXToggleZoomClick(Sender: TObject); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure FunctionComboBoxChange(Sender: TObject); procedure lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType); procedure btnCheckUpdatesClick(Sender: TObject); + procedure LEDButtonClick(Sender: TObject); + procedure FormDestroy(Sender: TObject); private + FLEDControls: array[0..LED_COUNT - 1] of TLEDControls; FEventMonitor: TOmniEventMonitor; - FStateConsumerTask: IOmniTaskControl; - FFSXComboBoxes: TComboBoxArray; - FFSXToggleZoomDeviceGUID: TGUID; - FFSXToggleZoomButtonIndex: Integer; + + FProfilesFilename: string; + FProfiles: TProfileList; +// FStateConsumerTask: IOmniTaskControl; protected - procedure LoadFunctions(AProviderClass: TLEDStateProviderClass; AComboBoxes: TComboBoxArray); - procedure SetFunctions(AComboBoxes: TComboBoxArray); +// procedure ReadFunctions(AReader: IX2PersistReader; AComboBoxes: TComboBoxArray); +// procedure ReadFSXExtra(AReader: IX2PersistReader); +// procedure ReadAutoUpdate(AReader: IX2PersistReader); +// procedure WriteFunctions(AWriter: IX2PersistWriter; AComboBoxes: TComboBoxArray); +// procedure WriteFSXExtra(AWriter: IX2PersistWriter); +// procedure WriteAutoUpdate(AWriter: IX2PersistWriter); - procedure ReadFunctions(AReader: IX2PersistReader; AComboBoxes: TComboBoxArray); - procedure ReadFSXExtra(AReader: IX2PersistReader); - procedure ReadAutoUpdate(AReader: IX2PersistReader); - procedure WriteFunctions(AWriter: IX2PersistWriter; AComboBoxes: TComboBoxArray); - procedure WriteFSXExtra(AWriter: IX2PersistWriter); - procedure WriteAutoUpdate(AWriter: IX2PersistWriter); + procedure FindLEDControls; + procedure LoadProfiles; + procedure SaveProfiles; - procedure LoadDefaultProfile; - procedure SaveDefaultProfile; + function CreateDefaultProfile: TProfile; + +// procedure LoadDefaultProfile; +// procedure SaveDefaultProfile; procedure SetDeviceState(const AMessage: string; AFound: Boolean); - procedure SetFSXToggleZoomButton(const ADeviceGUID: TGUID; AButtonIndex: Integer; const ADisplayText: string); +// procedure SetFSXToggleZoomButton(const ADeviceGUID: TGUID; AButtonIndex: Integer; const ADisplayText: string); - procedure InitializeStateProvider(AProviderClass: TLEDStateProviderClass); - procedure FinalizeStateProvider; +// procedure InitializeStateProvider(AProviderClass: TLEDStateProviderClass); +// procedure FinalizeStateProvider; - procedure UpdateMapping; +// procedure UpdateMapping; procedure CheckForUpdatesThread(const ATask: IOmniTask); procedure CheckForUpdates(AReportNoUpdates: Boolean); @@ -140,7 +146,8 @@ type procedure CMAskAutoUpdate(var Msg: TMessage); message CM_ASKAUTOUPDATE; property EventMonitor: TOmniEventMonitor read FEventMonitor; - property StateConsumerTask: IOmniTaskControl read FStateConsumerTask; + property Profiles: TProfileList read FProfiles; +// property StateConsumerTask: IOmniTaskControl read FStateConsumerTask; end; @@ -155,17 +162,24 @@ uses IdHTTP, OtlCommon, X2UtApp, - X2UtPersistRegistry, + X2UtPersistXML, ButtonSelectFrm, + ConfigConversion, FSXLEDStateProvider, - G940LEDStateConsumer; + G940LEDStateConsumer, + LEDColorIntf, + StaticLEDFunction; {$R *.dfm} const + NameDefaultProfile = 'Default'; + + FILENAME_PROFILES = 'G940LEDControl\Profiles.xml'; + SPECIAL_CATEGORY = -1; TEXT_STATE_SEARCHING = 'Searching...'; @@ -197,46 +211,144 @@ type { TMainForm } procedure TMainForm.FormCreate(Sender: TObject); -var - consumer: IOmniWorker; - +//var +// consumer: IOmniWorker; +// begin lblVersion.Caption := App.Version.FormatVersion(False); - pcConnections.ActivePageIndex := 0; - pcFSXOptions.ActivePageIndex := 0; - lblFSXToggleZoomButtonName.Caption := ''; + PageControl.ActivePageIndex := 0; FEventMonitor := TOmniEventMonitor.Create(Self); - consumer := TG940LEDStateConsumer.Create; - FStateConsumerTask := FEventMonitor.Monitor(CreateTask(consumer)).MsgWait; +// consumer := TG940LEDStateConsumer.Create; +// FStateConsumerTask := FEventMonitor.Monitor(CreateTask(consumer)).MsgWait; EventMonitor.OnTaskMessage := EventMonitorMessage; EventMonitor.OnTaskTerminated := EventMonitorTerminated; - StateConsumerTask.Run; +// StateConsumerTask.Run; - FFSXComboBoxes[0] := cmbFSXP1; - FFSXComboBoxes[1] := cmbFSXP2; - FFSXComboBoxes[2] := cmbFSXP3; - FFSXComboBoxes[3] := cmbFSXP4; - FFSXComboBoxes[4] := cmbFSXP5; - FFSXComboBoxes[5] := cmbFSXP6; - FFSXComboBoxes[6] := cmbFSXP7; - FFSXComboBoxes[7] := cmbFSXP8; - LoadFunctions(TFSXLEDStateProvider, FFSXComboBoxes); - LoadDefaultProfile; + FindLEDControls; + + FProfilesFilename := App.UserPath + FILENAME_PROFILES; + FProfiles := TProfileList.Create(True); + LoadProfiles; + +// LoadFunctions(TFSXLEDStateProvider, FFSXComboBoxes); +// LoadDefaultProfile; end; procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin - if Assigned(StateConsumerTask) then - begin - SaveDefaultProfile; + SaveProfiles; +// if Assigned(StateConsumerTask) then +// begin +// SaveDefaultProfile; +// +// LEDStateConsumer.Finalize(StateConsumerTask); +// CanClose := False; +// end; +end; - LEDStateConsumer.Finalize(StateConsumerTask); - CanClose := False; + +procedure TMainForm.FormDestroy(Sender: TObject); +begin + FreeAndNil(FProfiles); +end; + + +procedure TMainForm.FindLEDControls; + + function ComponentByName(const AName: string; ATag: NativeInt): TComponent; + begin + Result := FindComponent(AName); + if not Assigned(Result) then + raise EArgumentException.CreateFmt('"%s" is not a valid component', [AName]); + + Result.Tag := ATag; + end; + +var + ledIndex: Integer; + ledNumber: string; + +begin + for ledIndex := 0 to Pred(LED_COUNT) do + begin + ledNumber := IntToStr(Succ(ledIndex)); + + FLEDControls[ledIndex].ConfigureButton := (ComponentByName('btnP' + ledNumber, ledIndex) as TButton); + FLEDControls[ledIndex].CategoryLabel := (ComponentByName('lblP' + ledNumber + 'Category', ledIndex) as TLabel); + FLEDControls[ledIndex].FunctionLabel := (ComponentByName('lblP' + ledNumber + 'Function', ledIndex) as TLabel); + + FLEDControls[ledIndex].ConfigureButton.OnClick := LEDButtonClick; + FLEDControls[ledIndex].CategoryLabel.Caption := ''; + FLEDControls[ledIndex].FunctionLabel.Caption := ''; + end; +end; + + +procedure TMainForm.LoadProfiles; +var + defaultProfile: TProfile; + persistXML: TX2UtPersistXML; + +begin + if not FileExists(FProfilesFilename) then + begin + { Check if version 0.x settings are in the registry } + defaultProfile := ConfigConversion.Convert0To1; + + if not Assigned(defaultProfile) then + defaultProfile := CreateDefaultProfile; + + if Assigned(defaultProfile) then + begin + defaultProfile.Name := NameDefaultProfile; + Profiles.Add(defaultProfile); + end; + end else + begin + persistXML := TX2UtPersistXML.Create; + try + persistXML.FileName := FProfilesFilename; + Profiles.Load(persistXML.CreateReader); + finally + FreeAndNil(persistXML); + end; + end; +end; + + +procedure TMainForm.SaveProfiles; +var + persistXML: TX2UtPersistXML; + +begin + persistXML := TX2UtPersistXML.Create; + try + persistXML.FileName := FProfilesFilename; + Profiles.Save(persistXML.CreateWriter); + finally + FreeAndNil(persistXML); + end; +end; + + +function TMainForm.CreateDefaultProfile: TProfile; +var + ledIndex: Integer; + button: TProfileButton; + +begin + Result := TProfile.Create; + + for ledIndex := 0 to Pred(LED_COUNT) do + begin + button := Result.Buttons[ledIndex]; + button.ProviderUID := StaticProviderUID; + button.FunctionUID := StaticFunctionUID[lcGreen]; end; end; @@ -262,249 +374,155 @@ begin end; -procedure TMainForm.SetFSXToggleZoomButton(const ADeviceGUID: TGUID; AButtonIndex: Integer; const ADisplayText: string); -begin - FFSXToggleZoomDeviceGUID := ADeviceGUID; - FFSXToggleZoomButtonIndex := AButtonIndex; - lblFSXToggleZoomButtonName.Caption := ADisplayText; -end; +//procedure TMainForm.ReadFunctions(AReader: IX2PersistReader; AComboBoxes: TComboBoxArray); +//var +// comboBox: TComboBoxEx; +// value: Integer; +// itemIndex: Integer; +// +//begin +// if AReader.BeginSection(SECTION_FSX) then +// try +// for comboBox in AComboBoxes do +// begin +// if AReader.ReadInteger('Function' + IntToStr(comboBox.Tag), value) then +// begin +// for itemIndex := 0 to Pred(comboBox.ItemsEx.Count) do +// if Integer(comboBox.ItemsEx[itemIndex].Data) = value then +// begin +// comboBox.ItemIndex := itemIndex; +// break; +// end; +// end; +// end; +// finally +// AReader.EndSection; +// end; +//end; -procedure TMainForm.LoadFunctions(AProviderClass: TLEDStateProviderClass; AComboBoxes: TComboBoxArray); +//procedure TMainForm.ReadAutoUpdate(AReader: IX2PersistReader); +//var +// checkUpdates: Boolean; +// askAutoUpdate: Boolean; +// +//begin +// askAutoUpdate := True; +// +// if AReader.BeginSection(SECTION_SETTINGS) then +// try +// if AReader.ReadBoolean('CheckUpdates', checkUpdates) then +// begin +// cbCheckUpdates.Checked := checkUpdates; +// askAutoUpdate := False; +// end; +// finally +// AReader.EndSection; +// end; +// +// if askAutoUpdate then +// PostMessage(Self.Handle, CM_ASKAUTOUPDATE, 0, 0) +// else if cbCheckUpdates.Checked then +// CheckForUpdates(False); +//end; + + +//procedure TMainForm.WriteAutoUpdate(AWriter: IX2PersistWriter); +//begin +// if AWriter.BeginSection(SECTION_SETTINGS) then +// try +// AWriter.WriteBoolean('CheckUpdates', cbCheckUpdates.Checked); +// finally +// AWriter.EndSection; +// end; +//end; + + +//procedure TMainForm.LoadDefaultProfile; +//var +// registryReader: TX2UtPersistRegistry; +// reader: IX2PersistReader; +// +//begin +// registryReader := TX2UtPersistRegistry.Create; +// try +// registryReader.RootKey := HKEY_CURRENT_USER; +// registryReader.Key := KEY_SETTINGS; +// +// reader := registryReader.CreateReader; +// +// if reader.BeginSection(SECTION_DEFAULTPROFILE) then +// try +// ReadFunctions(reader, FFSXComboBoxes); +// ReadFSXExtra(reader); +// finally +// reader.EndSection; +// end; +// +// ReadAutoUpdate(reader); +// finally +// FreeAndNil(registryReader); +// end; +//end; +// +// +//procedure TMainForm.SaveDefaultProfile; +//var +// registryWriter: TX2UtPersistRegistry; +// writer: IX2PersistWriter; +// +//begin +// registryWriter := TX2UtPersistRegistry.Create; +// try +// registryWriter.RootKey := HKEY_CURRENT_USER; +// registryWriter.Key := KEY_SETTINGS; +// +// writer := registryWriter.CreateWriter; +// if writer.BeginSection(SECTION_DEFAULTPROFILE) then +// try +// WriteFunctions(writer, FFSXComboBoxes); +// WriteFSXExtra(writer); +// finally +// writer.EndSection; +// end; +// +// WriteAutoUpdate(writer); +// finally +// FreeAndNil(registryWriter); +// end; +//end; + + +//procedure TMainForm.InitializeStateProvider(AProviderClass: TLEDStateProviderClass); +//begin +// UpdateMapping; +// LEDStateConsumer.InitializeStateProvider(StateConsumerTask, AProviderClass); +//end; +// +// +//procedure TMainForm.FinalizeStateProvider; +//begin +// LEDStateConsumer.FinalizeStateProvider(StateConsumerTask); +//end; + + +//procedure TMainForm.UpdateMapping; +//begin +// if not Assigned(StateConsumerTask) then +// Exit; +// +// LEDStateConsumer.ClearFunctions(StateConsumerTask); +// SetFunctions(FFSXComboBoxes); +//end; + + +procedure TMainForm.LEDButtonClick(Sender: TObject); var - comboBox: TComboBoxEx; + ledIndex: NativeInt; begin - for comboBox in AComboBoxes do - begin - comboBox.Items.BeginUpdate; - try - comboBox.Items.Clear; - AProviderClass.EnumFunctions(TComboBoxFunctionConsumer.Create(comboBox)); - - comboBox.ItemIndex := 0; - if Assigned(comboBox.OnChange) then - comboBox.OnChange(comboBox); - finally - comboBox.Items.EndUpdate; - end; - end; -end; - - -procedure TMainForm.SetFunctions(AComboBoxes: TComboBoxArray); -var - comboBox: TComboBoxEx; - -begin - for comboBox in AComboBoxes do - begin - if comboBox.ItemIndex > -1 then - LEDStateConsumer.SetFunction(StateConsumerTask, comboBox.Tag, Integer(comboBox.ItemsEx[comboBox.ItemIndex].Data)); - end; -end; - - -procedure TMainForm.ReadFunctions(AReader: IX2PersistReader; AComboBoxes: TComboBoxArray); -var - comboBox: TComboBoxEx; - value: Integer; - itemIndex: Integer; - -begin - if AReader.BeginSection(SECTION_FSX) then - try - for comboBox in AComboBoxes do - begin - if AReader.ReadInteger('Function' + IntToStr(comboBox.Tag), value) then - begin - for itemIndex := 0 to Pred(comboBox.ItemsEx.Count) do - if Integer(comboBox.ItemsEx[itemIndex].Data) = value then - begin - comboBox.ItemIndex := itemIndex; - break; - end; - end; - end; - finally - AReader.EndSection; - end; -end; - - -procedure TMainForm.ReadFSXExtra(AReader: IX2PersistReader); -var - deviceGUID: string; - buttonIndex: Integer; - displayText: string; - -begin - if AReader.BeginSection(SECTION_FSX) then - try - if AReader.ReadString('ToggleZoomDeviceGUID', deviceGUID) and - AReader.ReadInteger('ToggleZoomButtonIndex', buttonIndex) and - AReader.ReadString('ToggleZoomDisplayText', displayText) then - begin - try - SetFSXToggleZoomButton(StringToGUID(deviceGUID), buttonIndex, displayText); - except - on E:EConvertError do; - end; - end; - finally - AReader.EndSection; - end; -end; - - -procedure TMainForm.ReadAutoUpdate(AReader: IX2PersistReader); -var - checkUpdates: Boolean; - askAutoUpdate: Boolean; - -begin - askAutoUpdate := True; - - if AReader.BeginSection(SECTION_SETTINGS) then - try - if AReader.ReadBoolean('CheckUpdates', checkUpdates) then - begin - cbCheckUpdates.Checked := checkUpdates; - askAutoUpdate := False; - end; - finally - AReader.EndSection; - end; - - if askAutoUpdate then - PostMessage(Self.Handle, CM_ASKAUTOUPDATE, 0, 0) - else if cbCheckUpdates.Checked then - CheckForUpdates(False); -end; - - -procedure TMainForm.WriteFunctions(AWriter: IX2PersistWriter; AComboBoxes: TComboBoxArray); -var - comboBox: TComboBoxEx; - value: Integer; - -begin - if AWriter.BeginSection(SECTION_FSX) then - try - for comboBox in AComboBoxes do - begin - value := -1; - if comboBox.ItemIndex > -1 then - value := Integer(comboBox.ItemsEx[comboBox.ItemIndex].Data); - - AWriter.WriteInteger('Function' + IntToStr(comboBox.Tag), value); - end; - finally - AWriter.EndSection; - end; -end; - - -procedure TMainForm.WriteFSXExtra(AWriter: IX2PersistWriter); -begin - if AWriter.BeginSection(SECTION_FSX) then - try - AWriter.WriteString('ToggleZoomDeviceGUID', GUIDToString(FFSXToggleZoomDeviceGUID)); - AWriter.WriteInteger('ToggleZoomButtonIndex', FFSXToggleZoomButtonIndex); - AWriter.WriteString('ToggleZoomDisplayText', lblFSXToggleZoomButtonName.Caption); - // ToDo pressed / depressed levels - finally - AWriter.EndSection; - end; -end; - - -procedure TMainForm.WriteAutoUpdate(AWriter: IX2PersistWriter); -begin - if AWriter.BeginSection(SECTION_SETTINGS) then - try - AWriter.WriteBoolean('CheckUpdates', cbCheckUpdates.Checked); - finally - AWriter.EndSection; - end; -end; - - -procedure TMainForm.LoadDefaultProfile; -var - registryReader: TX2UtPersistRegistry; - reader: IX2PersistReader; - -begin - registryReader := TX2UtPersistRegistry.Create; - try - registryReader.RootKey := HKEY_CURRENT_USER; - registryReader.Key := KEY_SETTINGS; - - reader := registryReader.CreateReader; - - if reader.BeginSection(SECTION_DEFAULTPROFILE) then - try - ReadFunctions(reader, FFSXComboBoxes); - ReadFSXExtra(reader); - finally - reader.EndSection; - end; - - ReadAutoUpdate(reader); - finally - FreeAndNil(registryReader); - end; -end; - - -procedure TMainForm.SaveDefaultProfile; -var - registryWriter: TX2UtPersistRegistry; - writer: IX2PersistWriter; - -begin - registryWriter := TX2UtPersistRegistry.Create; - try - registryWriter.RootKey := HKEY_CURRENT_USER; - registryWriter.Key := KEY_SETTINGS; - - writer := registryWriter.CreateWriter; - if writer.BeginSection(SECTION_DEFAULTPROFILE) then - try - WriteFunctions(writer, FFSXComboBoxes); - WriteFSXExtra(writer); - finally - writer.EndSection; - end; - - WriteAutoUpdate(writer); - finally - FreeAndNil(registryWriter); - end; -end; - - -procedure TMainForm.InitializeStateProvider(AProviderClass: TLEDStateProviderClass); -begin - UpdateMapping; - LEDStateConsumer.InitializeStateProvider(StateConsumerTask, AProviderClass); -end; - - -procedure TMainForm.FinalizeStateProvider; -begin - LEDStateConsumer.FinalizeStateProvider(StateConsumerTask); -end; - - -procedure TMainForm.UpdateMapping; -begin - if not Assigned(StateConsumerTask) then - Exit; - - LEDStateConsumer.ClearFunctions(StateConsumerTask); - SetFunctions(FFSXComboBoxes); + ledIndex := (Sender as TComponent).Tag; + // TODO configure led end; @@ -631,12 +649,12 @@ end; procedure TMainForm.EventMonitorTerminated(const task: IOmniTaskControl); begin - if task = StateConsumerTask then - begin - FStateConsumerTask := nil; - Close; - end else if task.Name = 'CheckForUpdatesThread' then - btnCheckUpdates.Enabled := True; +// if task = StateConsumerTask then +// begin +// FStateConsumerTask := nil; +// Close; +// end else if task.Name = 'CheckForUpdatesThread' then +// btnCheckUpdates.Enabled := True; end; @@ -680,8 +698,8 @@ var msg: string; begin - btnFSXDisconnect.Enabled := False; - btnFSXConnect.Enabled := True; +// btnFSXDisconnect.Enabled := False; +// btnFSXConnect.Enabled := True; msg := AMessage.MsgData; if Length(msg) > 0 then @@ -694,43 +712,11 @@ begin CheckForUpdates(True); end; -procedure TMainForm.btnFSXConnectClick(Sender: TObject); -begin - SaveDefaultProfile; - InitializeStateProvider(TFSXLEDStateProvider); - - btnFSXDisconnect.Enabled := True; - btnFSXConnect.Enabled := False; -end; - - -procedure TMainForm.btnFSXDisconnectClick(Sender: TObject); -begin - FinalizeStateProvider; - btnFSXDisconnect.Enabled := False; - btnFSXConnect.Enabled := True; -end; - - -procedure TMainForm.btnFSXToggleZoomClick(Sender: TObject); -var - deviceGUID: TGUID; - button: Integer; - displayText: string; - -begin - FillChar(deviceGUID, SizeOf(deviceGUID), 0); - button := -1; - - if TButtonSelectForm.Execute(deviceGUID, button, displayText) then - SetFSXToggleZoomButton(deviceGUID, button, displayText); -end; - procedure TMainForm.btnRetryClick(Sender: TObject); begin btnRetry.Visible := False; - StateConsumerTask.Comm.Send(MSG_FINDTHROTTLEDEVICE); +// StateConsumerTask.Comm.Send(MSG_FINDTHROTTLEDEVICE); end; diff --git a/G940LEDControl/G940LEDControl.dpr b/G940LEDControl/G940LEDControl.dpr index 6cc4657..32ffd00 100644 --- a/G940LEDControl/G940LEDControl.dpr +++ b/G940LEDControl/G940LEDControl.dpr @@ -11,14 +11,19 @@ uses LEDFunctionMap in 'Units\LEDFunctionMap.pas', LEDStateConsumer in 'Units\LEDStateConsumer.pas', LEDStateProvider in 'Units\LEDStateProvider.pas', - LEDStateIntf in 'Units\LEDStateIntf.pas', - LEDState in 'Units\LEDState.pas', + LEDColorIntf in 'Units\LEDColorIntf.pas', + LEDColor in 'Units\LEDColor.pas', LEDFunctionIntf in 'Units\LEDFunctionIntf.pas', ObserverIntf in 'Units\ObserverIntf.pas', LEDFunction in 'Units\LEDFunction.pas', StaticLEDFunction in 'Units\StaticLEDFunction.pas', ConfigConversion in 'Units\ConfigConversion.pas', - LEDFunctionRegistry in 'Units\LEDFunctionRegistry.pas'; + LEDFunctionRegistry in 'Units\LEDFunctionRegistry.pas', + StaticLEDColor in 'Units\StaticLEDColor.pas', + DynamicLEDColor in 'Units\DynamicLEDColor.pas', + LEDStateIntf in 'Units\LEDStateIntf.pas', + LEDState in 'Units\LEDState.pas', + Profile in 'Units\Profile.pas'; {$R *.res} diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj index c4c26ff..5c37c22 100644 --- a/G940LEDControl/G940LEDControl.dproj +++ b/G940LEDControl/G940LEDControl.dproj @@ -8,7 +8,7 @@ VCL 13.4 True - Release + Debug Win32 1 Application @@ -173,14 +173,19 @@ - - + + + + + + + Cfg_2 Base diff --git a/G940LEDControl/G940LEDControl.res b/G940LEDControl/G940LEDControl.res index 673824825398b275e260cf11c6695f7f545ef1b1..d98ff0a8e18f4a9808bfeee4072191112fccef14 100644 GIT binary patch delta 147 zcmaEJkMY9|#t90WZ!l$KGPX~4%!!yhJ!dH+=VX^$Em6+0)S}|d{5)GFGd%+kRO030 zoIEjCS%)(xF)zI|F+J5*Ned)cP?VpXT3no8W`%1HUEJroiMu>?u!mjR9j;qE>+#n9{tK{h+F1Cr->7U29MdZYx1SaY zI=jfB5F$Z9T!M7&%i3q(7(=@hZw+P{1eLYXi1Y{A8$xb zNy+huepUImagO)uJjDjx8GIvPz|fFa5(Bu!bULZP4(s0Fu9GE3Y)X-Ov@kmX>yf2M zJ7KiYpwI^6^aO6>JmYs57f@1+^@7L&mFxNg^a%%EG1XOuU*RY4?>csIfuOzWp6;E- z)m3S0+=se|aS77}G$^BZ%teozrY&~}Oz{9ib|jx_XASyaH!j1bsAnu%#3{>jNEK5~e`Ta1Lem+ztq`Vk4%dKeH zS}vbPUALwP5zCQ`UB}Bh$taDant%y04BNiuxz2$XXVPh2*J{eI(e|l@d6dn#{TmF+ Bq|5*S diff --git a/G940LEDControl/Units/ConfigConversion.pas b/G940LEDControl/Units/ConfigConversion.pas index dd97bef..a5e0b93 100644 --- a/G940LEDControl/Units/ConfigConversion.pas +++ b/G940LEDControl/Units/ConfigConversion.pas @@ -1,15 +1,19 @@ unit ConfigConversion; interface +uses + Profile; { Version 0.x: registry -> 1.x: XML } - procedure Convert0To1; + function Convert0To1: TProfile; implementation -procedure Convert0To1; +function Convert0To1: TProfile; begin + Result := nil; + // FUNCTION_NONE = 0; // FUNCTION_OFF = 1; // FUNCTION_RED = 2; diff --git a/G940LEDControl/Units/DynamicLEDColor.pas b/G940LEDControl/Units/DynamicLEDColor.pas new file mode 100644 index 0000000..99754b5 --- /dev/null +++ b/G940LEDControl/Units/DynamicLEDColor.pas @@ -0,0 +1,76 @@ +unit DynamicLEDColor; + +interface +uses + LEDColor, + LEDColorIntf; + + +const + TICKINTERVAL_NORMAL = 2; + TICKINTERVAL_FAST = 1; + + +type + TLEDColorDynArray = array of TLEDColor; + + + TDynamicLEDColor = class(TCustomDynamicLEDColor) + private + FCycleColors: TLEDColorDynArray; + FCycleIndex: Integer; + FTickInterval: Integer; + FTickCount: Integer; + protected + { ILEDState } + function GetColor: TLEDColor; override; + + { ITickLEDState } + procedure Tick; override; + public + constructor Create(ACycleColors: TLEDColorDynArray; ATickInterval: Integer = TICKINTERVAL_NORMAL); + end; + + + +implementation +uses + SysUtils; + + +{ TDynamicLEDState } +constructor TDynamicLEDColor.Create(ACycleColors: TLEDColorDynArray; ATickInterval: Integer); +begin + inherited Create; + + if Length(ACycleColors) = 0 then + raise Exception.Create(Self.ClassName + ' must have at least one color in a cycle'); + + FCycleColors := ACycleColors; + FCycleIndex := Low(FCycleColors); + FTickInterval := ATickInterval; + FTickCount := 0; +end; + + +function TDynamicLEDColor.GetColor: TLEDColor; +begin + Result := FCycleColors[FCycleIndex]; +end; + + +procedure TDynamicLEDColor.Tick; +begin + Inc(FTickCount); + + if FTickCount >= FTickInterval then + begin + Inc(FCycleIndex); + if FCycleIndex > High(FCycleColors) then + FCycleIndex := 0; + + FTickCount := 0; + end; +end; + +end. diff --git a/G940LEDControl/Units/LEDColor.pas b/G940LEDControl/Units/LEDColor.pas new file mode 100644 index 0000000..bcb3c03 --- /dev/null +++ b/G940LEDControl/Units/LEDColor.pas @@ -0,0 +1,27 @@ +unit LEDColor; + +interface +uses + SysUtils, + + LEDColorIntf; + + +type + TCustomLEDColor = class(TInterfacedObject, ILEDColor) + protected + { ILEDState } + function GetColor: TLEDColor; virtual; abstract; + end; + + + TCustomDynamicLEDColor = class(TCustomLEDColor, IDynamicLEDColor) + protected + { ITickLEDState } + procedure Tick; virtual; abstract; + end; + + +implementation + +end. diff --git a/G940LEDControl/Units/LEDStateIntf.pas b/G940LEDControl/Units/LEDColorIntf.pas similarity index 54% rename from G940LEDControl/Units/LEDStateIntf.pas rename to G940LEDControl/Units/LEDColorIntf.pas index 8e56f5c..e283152 100644 --- a/G940LEDControl/Units/LEDStateIntf.pas +++ b/G940LEDControl/Units/LEDColorIntf.pas @@ -1,17 +1,22 @@ -unit LEDStateIntf; +unit LEDColorIntf; interface type TLEDColor = (lcOff, lcGreen, lcAmber, lcRed); - ILEDState = interface - ['{B40DF462-B660-4002-A6B9-DD30AC69E8DB}'] - procedure Tick; + ILEDColor = interface + ['{B40DF462-B660-4002-A6B9-DD30AC69E8DB}'] function GetColor: TLEDColor; end; + IDynamicLEDColor = interface(ILEDColor) + ['{9770E851-580D-4803-9979-0C608CB108A0}'] + procedure Tick; + end; + + implementation end. diff --git a/G940LEDControl/Units/LEDFunction.pas b/G940LEDControl/Units/LEDFunction.pas index 47f8770..74bb71d 100644 --- a/G940LEDControl/Units/LEDFunction.pas +++ b/G940LEDControl/Units/LEDFunction.pas @@ -17,7 +17,7 @@ type procedure RegisterFunction(AFunction: ILEDFunction); protected { ILEDFunctionProvider } - function GetUniqueName: string; virtual; abstract; + function GetUID: string; virtual; abstract; function GetEnumerator: ILEDFunctionEnumerator; virtual; public @@ -28,10 +28,10 @@ type TCustomLEDFunction = class(TInterfacedObject, IObservable, ILEDFunction) private - FCurrentState: ILEDState; FObservers: TInterfaceList; + FStates: TInterfaceList; protected - procedure SetCurrentState(AState: ILEDState); virtual; +// procedure SetCurrentState(AState: ILEDState); virtual; procedure NotifyObservers; virtual; @@ -44,9 +44,10 @@ type { ILEDFunction } function GetCategoryName: string; virtual; abstract; function GetDisplayName: string; virtual; abstract; - function GetUniqueName: string; virtual; abstract; + function GetUID: string; virtual; abstract; - function GetCurrentState: ILEDState; virtual; + function GetEnumerator: ILEDStateEnumerator; virtual; + function GetCurrentState: ILEDState; virtual; abstract; public constructor Create; destructor Destroy; override; @@ -66,6 +67,20 @@ type end; + TLEDStateEnumerator = class(TInterfacedObject, ILEDStateEnumerator) + private + FList: TInterfaceList; + FIndex: Integer; + protected + { ILEDStateEnumerator } + function GetCurrent: ILEDState; virtual; + function MoveNext: Boolean; virtual; + public + constructor Create(AList: TInterfaceList); + end; + + + implementation uses SysUtils; @@ -77,11 +92,13 @@ begin inherited Create; FObservers := TInterfaceList.Create; + FStates := TInterfaceList.Create; end; destructor TCustomLEDFunction.Destroy; begin + FreeAndNil(FStates); FreeAndNil(FObservers); inherited Destroy; @@ -90,27 +107,27 @@ end; procedure TCustomLEDFunction.Attach(AObserver: IObserver); begin - + FObservers.Add(AObserver as IObserver); end; procedure TCustomLEDFunction.Detach(AObserver: IObserver); begin - + FObservers.Remove(AObserver as IObserver); end; -function TCustomLEDFunction.GetCurrentState: ILEDState; +function TCustomLEDFunction.GetEnumerator: ILEDStateEnumerator; begin - + Result := TLEDStateEnumerator.Create(FStates); end; -procedure TCustomLEDFunction.SetCurrentState(AState: ILEDState); -begin - FCurrentState := AState; - NotifyObservers; -end; +//procedure TCustomLEDFunction.SetCurrentState(AState: ILEDState); +//begin +// FCurrentState := AState; +// NotifyObservers; +//end; procedure TCustomLEDFunction.NotifyObservers; @@ -142,9 +159,6 @@ end; procedure TCustomLEDFunctionProvider.RegisterFunction(AFunction: ILEDFunction); begin - { Make sure to explicitly request the ILEDFunction interface; I've experienced - incomparable pointers otherwise if we ever need to write an UnregisterFunction. - My best, but unverified, guess is that it works kinda like a VMT. } FFunctions.Add(AFunction as ILEDFunction); end; @@ -178,4 +192,28 @@ begin Inc(FIndex); end; + +{ TLEDStateEnumerator } +constructor TLEDStateEnumerator.Create(AList: TInterfaceList); +begin + inherited Create; + + FList := AList; + FIndex := -1; +end; + + +function TLEDStateEnumerator.GetCurrent: ILEDState; +begin + Result := (FList[FIndex] as ILEDState); +end; + + +function TLEDStateEnumerator.MoveNext: Boolean; +begin + Result := (FIndex < Pred(FList.Count)); + if Result then + Inc(FIndex); +end; + end. diff --git a/G940LEDControl/Units/LEDFunctionIntf.pas b/G940LEDControl/Units/LEDFunctionIntf.pas index 1c56fec..1759ac5 100644 --- a/G940LEDControl/Units/LEDFunctionIntf.pas +++ b/G940LEDControl/Units/LEDFunctionIntf.pas @@ -2,18 +2,19 @@ unit LEDFunctionIntf; interface uses + LEDColorIntf, LEDStateIntf, ObserverIntf; type - ILEDFunction = interface; ILEDFunctionEnumerator = interface; + ILEDStateEnumerator = interface; ILEDFunctionProvider = interface ['{B38F6F90-DC96-42CE-B8F0-21F0DD8AA537}'] - function GetUniqueName: string; + function GetUID: string; function GetEnumerator: ILEDFunctionEnumerator; end; @@ -23,8 +24,9 @@ type ['{7087067A-1016-4A7D-ACB1-BA1F388DAD6C}'] function GetCategoryName: string; function GetDisplayName: string; - function GetUniqueName: string; + function GetUID: string; + function GetEnumerator: ILEDStateEnumerator; function GetCurrentState: ILEDState; end; @@ -38,6 +40,15 @@ type end; + ILEDStateEnumerator = interface + ['{045E8466-831A-4704-ABBB-31E85789F314}'] + function GetCurrent: ILEDState; + function MoveNext: Boolean; + + property Current: ILEDState read GetCurrent; + end; + + implementation end. diff --git a/G940LEDControl/Units/LEDFunctionRegistry.pas b/G940LEDControl/Units/LEDFunctionRegistry.pas index 1cd56bf..c328115 100644 --- a/G940LEDControl/Units/LEDFunctionRegistry.pas +++ b/G940LEDControl/Units/LEDFunctionRegistry.pas @@ -18,7 +18,7 @@ type procedure DoRegister(AProvider: ILEDFunctionProvider); procedure DoUnregister(AProvider: ILEDFunctionProvider); - function DoFind(const AUniqueName: string): ILEDFunctionProvider; + function DoFind(const AUID: string): ILEDFunctionProvider; function GetProviders: TLEDFunctionProviderList; public @@ -28,7 +28,7 @@ type class procedure Register(AProvider: ILEDFunctionProvider); class procedure Unregister(AProvider: ILEDFunctionProvider); - class function Find(const AUniqueName: string): ILEDFunctionProvider; + class function Find(const AUID: string): ILEDFunctionProvider; class function Providers: TLEDFunctionProviderList; end; @@ -46,7 +46,7 @@ type constructor Create; destructor Destroy; override; - function Find(const AUniqueName: string): ILEDFunctionProvider; + function Find(const AUID: string): ILEDFunctionProvider; function GetEnumerator: TLEDFunctionProviderListEnumerator; end; @@ -81,9 +81,9 @@ begin end; -class function TLEDFunctionRegistry.Find(const AUniqueName: string): ILEDFunctionProvider; +class function TLEDFunctionRegistry.Find(const AUID: string): ILEDFunctionProvider; begin - Result := Instance.DoFind(AUniqueName); + Result := Instance.DoFind(AUID); end; @@ -130,9 +130,9 @@ begin end; -function TLEDFunctionRegistry.DoFind(const AUniqueName: string): ILEDFunctionProvider; +function TLEDFunctionRegistry.DoFind(const AUID: string): ILEDFunctionProvider; begin - Result := FProviders.Find(AUniqueName); + Result := FProviders.Find(AUID); end; @@ -159,7 +159,7 @@ begin end; -function TLEDFunctionProviderList.Find(const AUniqueName: string): ILEDFunctionProvider; +function TLEDFunctionProviderList.Find(const AUID: string): ILEDFunctionProvider; var provider: ILEDFunctionProvider; @@ -167,7 +167,7 @@ begin Result := nil; for provider in Self do - if provider.GetUniqueName = AUniqueName then + if provider.GetUID = AUID then begin Result := provider; break; diff --git a/G940LEDControl/Units/LEDState.pas b/G940LEDControl/Units/LEDState.pas deleted file mode 100644 index 2241b4f..0000000 --- a/G940LEDControl/Units/LEDState.pas +++ /dev/null @@ -1,52 +0,0 @@ -unit LEDState; - -interface -uses - SysUtils, - - LEDStateIntf; - - -type - TCustomLEDState = class(TInterfacedObject, ILEDState) - protected - { ILEDState } - procedure Tick; virtual; - function GetColor: TLEDColor; virtual; abstract; - end; - - - TStaticLEDState = class(TCustomLEDState) - private - FColor: TLEDColor; - protected - function GetColor: TLEDColor; override; - public - constructor Create(AColor: TLEDColor); - end; - - -implementation - - -{ TCustomLEDState } -procedure TCustomLEDState.Tick; -begin -end; - - -{ TStaticLEDState } -constructor TStaticLEDState.Create(AColor: TLEDColor); -begin - inherited Create; - - FColor := AColor; -end; - - -function TStaticLEDState.GetColor: TLEDColor; -begin - Result := FColor; -end; - -end. diff --git a/G940LEDControl/Units/LEDStateConsumer.pas b/G940LEDControl/Units/LEDStateConsumer.pas index e0aac12..c809a12 100644 --- a/G940LEDControl/Units/LEDStateConsumer.pas +++ b/G940LEDControl/Units/LEDStateConsumer.pas @@ -28,7 +28,6 @@ const type - { This interface name made me giggle. Because it's true. } IRunInMainThread = interface(IOmniWaitableValue) ['{68B8F2F7-ED40-4078-9D99-503D7AFA068B}'] procedure Execute; diff --git a/G940LEDControl/Units/Profile.pas b/G940LEDControl/Units/Profile.pas new file mode 100644 index 0000000..4aaa0bc --- /dev/null +++ b/G940LEDControl/Units/Profile.pas @@ -0,0 +1,232 @@ +unit Profile; + +interface +uses + Generics.Collections, + + X2UtPersistIntf; + + +type + TProfileButton = class(TObject) + private + FProviderUID: string; + FFunctionUID: string; + protected + function Load(AReader: IX2PersistReader): Boolean; + procedure Save(AWriter: IX2PersistWriter); + public + property ProviderUID: string read FProviderUID write FProviderUID; + property FunctionUID: string read FFunctionUID write FFunctionUID; + end; + + + TProfileButtonList = class(TObjectList); + + + TProfile = class(TObject) + private + FName: string; + FButtons: TProfileButtonList; + + function GetButton(Index: Integer): TProfileButton; + function GetButtonCount: Integer; + protected + function Load(AReader: IX2PersistReader): Boolean; + procedure Save(AWriter: IX2PersistWriter); + public + constructor Create; + destructor Destroy; override; + + property Name: string read FName write FName; + + property ButtonCount: Integer read GetButtonCount; + property Buttons[Index: Integer]: TProfileButton read GetButton; + end; + + + TProfileList = class(TObjectList) + public + procedure Load(AReader: IX2PersistReader); + procedure Save(AWriter: IX2PersistWriter); + end; + + +implementation +uses + Classes, + SysUtils; + + +const + SectionProfiles = 'Profiles'; + SectionButton = 'Button'; + + KeyProviderUID = 'ProviderUID'; + KeyFunctionUID = 'FunctionUID'; + + +{ TProfileButton } +function TProfileButton.Load(AReader: IX2PersistReader): Boolean; +begin + Result := AReader.ReadString(KeyProviderUID, FProviderUID) and + AReader.ReadString(KeyFunctionUID, FFunctionUID); +end; + + +procedure TProfileButton.Save(AWriter: IX2PersistWriter); +begin + AWriter.WriteString(KeyProviderUID, FProviderUID); + AWriter.WriteString(KeyFunctionUID, FFunctionUID); +end; + + +{ TProfile } +constructor TProfile.Create; +begin + inherited Create; + + FButtons := TProfileButtonList.Create(True); +end; + + +destructor TProfile.Destroy; +begin + FreeAndNil(FButtons); + + inherited; +end; + + +function TProfile.Load(AReader: IX2PersistReader): Boolean; +var + buttonIndex: Integer; + button: TProfileButton; + +begin + Result := False; + buttonIndex := 0; + + while AReader.BeginSection(SectionButton + IntToStr(buttonIndex)) do + try + button := TProfileButton.Create; + if button.Load(AReader) then + begin + FButtons.Add(button); + Result := True; + end else + FreeAndNil(button); + finally + AReader.EndSection; + Inc(buttonIndex); + end; +end; + + +procedure TProfile.Save(AWriter: IX2PersistWriter); +var + buttonIndex: Integer; + +begin + for buttonIndex := 0 to Pred(FButtons.Count) do + begin + if AWriter.BeginSection(SectionButton + IntToStr(buttonIndex)) then + try + FButtons[buttonIndex].Save(AWriter); + finally + AWriter.EndSection; + end; + end; +end; + + +function TProfile.GetButtonCount: Integer; +begin + Result := FButtons.Count; +end; + + +function TProfile.GetButton(Index: Integer): TProfileButton; +var + oldCount: Integer; + buttonIndex: Integer; + +begin + oldCount := FButtons.Count; + if Index >= oldCount then + begin + FButtons.Count := Succ(Index); + + for buttonIndex := oldCount to Pred(FButtons.Count) do + FButtons[buttonIndex] := nil; + end; + + Result := FButtons[Index]; + if not Assigned(Result) then + begin + Result := TProfileButton.Create; + FButtons[Index] := Result; + end; +end; + + +{ TProfileList } +procedure TProfileList.Load(AReader: IX2PersistReader); +var + profiles: TStringList; + profileName: string; + profile: TProfile; + +begin + if AReader.BeginSection(SectionProfiles) then + try + profiles := TStringList.Create; + try + AReader.GetSections(profiles); + + for profileName in profiles do + begin + if AReader.BeginSection(profileName) then + try + profile := TProfile.Create; + profile.Name := profileName; + + if profile.Load(AReader) then + Add(profile) + else + FreeAndNil(profile); + finally + AReader.EndSection; + end; + end; + finally + FreeAndNil(profiles); + end; + finally + AReader.EndSection; + end; +end; + + +procedure TProfileList.Save(AWriter: IX2PersistWriter); +var + profile: TProfile; + +begin + if AWriter.BeginSection(SectionProfiles) then + try + for profile in Self do + begin + if AWriter.BeginSection(profile.Name) then + try + profile.Save(AWriter); + finally + AWriter.EndSection; + end; + end; + finally + AWriter.EndSection; + end; +end; + +end. diff --git a/G940LEDControl/Units/StaticLEDColor.pas b/G940LEDControl/Units/StaticLEDColor.pas new file mode 100644 index 0000000..ecabd62 --- /dev/null +++ b/G940LEDControl/Units/StaticLEDColor.pas @@ -0,0 +1,38 @@ +unit StaticLEDColor; + +interface +uses + LEDColor, + LEDColorIntf; + + +type + TStaticLEDColor = class(TCustomLEDColor) + private + FColor: TLEDColor; + protected + function GetColor: TLEDColor; override; + public + constructor Create(AColor: TLEDColor); + end; + + +implementation + + +{ TStaticLEDState } +constructor TStaticLEDColor.Create(AColor: TLEDColor); +begin + inherited Create; + + FColor := AColor; +end; + + +function TStaticLEDColor.GetColor: TLEDColor; +begin + Result := FColor; +end; + + +end. diff --git a/G940LEDControl/Units/StaticLEDFunction.pas b/G940LEDControl/Units/StaticLEDFunction.pas index 5bd2c39..b92195f 100644 --- a/G940LEDControl/Units/StaticLEDFunction.pas +++ b/G940LEDControl/Units/StaticLEDFunction.pas @@ -3,13 +3,13 @@ unit StaticLEDFunction; interface uses LEDFunction, - LEDStateIntf; + LEDColorIntf; type TStaticLEDFunctionProvider = class(TCustomLEDFunctionProvider) protected - function GetUniqueName: string; override; + function GetUID: string; override; public constructor Create; end; @@ -21,12 +21,23 @@ type protected function GetCategoryName: string; override; function GetDisplayName: string; override; - function GetUniqueName: string; override; + function GetUID: string; override; public constructor Create(AColor: TLEDColor); end; +const + StaticProviderUID = 'static'; + StaticFunctionUID: array[TLEDColor] of string = + ( + 'off', + 'green', + 'amber', + 'red' + ); + + implementation uses LEDFunctionRegistry; @@ -34,17 +45,6 @@ uses const CategoryStatic = 'Static'; - - ProviderUniqueName = 'static'; - - FunctionUniqueName: array[TLEDColor] of string = - ( - 'off', - 'green', - 'amber', - 'red' - ); - FunctionDisplayName: array[TLEDColor] of string = ( 'Off', @@ -68,9 +68,9 @@ begin end; -function TStaticLEDFunctionProvider.GetUniqueName: string; +function TStaticLEDFunctionProvider.GetUID: string; begin - Result := ProviderUniqueName; + Result := StaticProviderUID; end; @@ -95,9 +95,9 @@ begin end; -function TStaticLEDFunction.GetUniqueName: string; +function TStaticLEDFunction.GetUID: string; begin - Result := FunctionUniqueName[FColor]; + Result := StaticFunctionUID[FColor]; end; From 9929578ef094eb3e955f13a7114a7ca79525273c Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Sun, 10 Feb 2013 20:39:34 +0000 Subject: [PATCH 04/16] Added: start of button configuration form --- G940LEDControl/Forms/ButtonFunctionFrm.dfm | 196 +++++++++++++++++++++ G940LEDControl/Forms/ButtonFunctionFrm.pas | 176 ++++++++++++++++++ G940LEDControl/Forms/MainFrm.dfm | 169 ++++++++---------- G940LEDControl/Forms/MainFrm.pas | 117 ++++++++++-- G940LEDControl/G940LEDControl.dpr | 6 +- G940LEDControl/G940LEDControl.dproj | 5 + G940LEDControl/Units/ConfigConversion.pas | 2 + G940LEDControl/Units/LEDColorPool.pas | 101 +++++++++++ G940LEDControl/Units/LEDFunction.pas | 19 +- G940LEDControl/Units/LEDFunctionIntf.pas | 11 +- G940LEDControl/Units/LEDState.pas | 57 ++++++ G940LEDControl/Units/LEDStateIntf.pas | 20 +++ G940LEDControl/Units/Profile.pas | 9 + G940LEDControl/Units/StaticLEDFunction.pas | 12 +- 14 files changed, 786 insertions(+), 114 deletions(-) create mode 100644 G940LEDControl/Forms/ButtonFunctionFrm.dfm create mode 100644 G940LEDControl/Forms/ButtonFunctionFrm.pas create mode 100644 G940LEDControl/Units/LEDColorPool.pas create mode 100644 G940LEDControl/Units/LEDState.pas create mode 100644 G940LEDControl/Units/LEDStateIntf.pas diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.dfm b/G940LEDControl/Forms/ButtonFunctionFrm.dfm new file mode 100644 index 0000000..84d8bf2 --- /dev/null +++ b/G940LEDControl/Forms/ButtonFunctionFrm.dfm @@ -0,0 +1,196 @@ +object ButtonFunctionForm: TButtonFunctionForm + Left = 0 + Top = 0 + BorderIcons = [biSystemMenu] + BorderStyle = bsDialog + Caption = 'Configure button' + ClientHeight = 401 + ClientWidth = 692 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + Position = poMainFormCenter + OnCreate = FormCreate + OnDestroy = FormDestroy + PixelsPerInch = 96 + TextHeight = 13 + object pnlButtons: TPanel + Left = 0 + Top = 360 + Width = 692 + Height = 41 + Align = alBottom + BevelOuter = bvNone + TabOrder = 0 + ExplicitLeft = 168 + ExplicitTop = 232 + ExplicitWidth = 185 + DesignSize = ( + 692 + 41) + object btnOK: TButton + Left = 528 + Top = 8 + Width = 75 + Height = 25 + Anchors = [akTop, akRight] + Caption = 'OK' + Default = True + ModalResult = 1 + TabOrder = 0 + ExplicitLeft = 467 + end + object btnCancel: TButton + Left = 609 + Top = 8 + Width = 75 + Height = 25 + Anchors = [akTop, akRight] + Cancel = True + Caption = 'Cancel' + ModalResult = 2 + TabOrder = 1 + ExplicitLeft = 548 + end + end + object vstFunctions: TVirtualStringTree + AlignWithMargins = True + Left = 8 + Top = 8 + Width = 257 + Height = 352 + Margins.Left = 8 + Margins.Top = 8 + Margins.Right = 0 + Margins.Bottom = 0 + Align = alLeft + Header.AutoSizeIndex = 0 + Header.Font.Charset = DEFAULT_CHARSET + Header.Font.Color = clWindowText + Header.Font.Height = -11 + Header.Font.Name = 'Tahoma' + Header.Font.Style = [] + Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoShowSortGlyphs, hoVisible] + TabOrder = 1 + TreeOptions.AutoOptions = [toAutoDropExpand, toAutoScrollOnExpand, toAutoSort, toAutoTristateTracking, toAutoDeleteMovedNodes] + TreeOptions.PaintOptions = [toShowButtons, toShowDropmark, toShowTreeLines, toThemeAware, toUseBlendedImages] + TreeOptions.SelectionOptions = [toFullRowSelect] + OnGetText = vstFunctionsGetText + Columns = < + item + Position = 0 + Width = 253 + WideText = 'Available functions' + end> + end + object pnlFunction: TPanel + AlignWithMargins = True + Left = 273 + Top = 8 + Width = 411 + Height = 352 + Margins.Left = 8 + Margins.Top = 8 + Margins.Right = 8 + Margins.Bottom = 0 + Align = alClient + BevelOuter = bvNone + TabOrder = 2 + ExplicitLeft = 224 + ExplicitTop = 192 + ExplicitWidth = 185 + ExplicitHeight = 41 + object vstStates: TVirtualStringTree + Left = 0 + Top = 113 + Width = 411 + Height = 239 + Align = alClient + Header.AutoSizeIndex = 0 + Header.Font.Charset = DEFAULT_CHARSET + Header.Font.Color = clWindowText + Header.Font.Height = -11 + Header.Font.Name = 'Tahoma' + Header.Font.Style = [] + Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoShowSortGlyphs, hoVisible] + TabOrder = 0 + ExplicitTop = 93 + ExplicitWidth = 407 + ExplicitHeight = 259 + Columns = < + item + Position = 0 + Width = 207 + WideText = 'State' + end + item + Position = 1 + Width = 200 + WideText = 'Color' + end> + end + object pnlName: TPanel + Left = 0 + Top = 0 + Width = 411 + Height = 113 + Align = alTop + BevelOuter = bvNone + TabOrder = 1 + ExplicitWidth = 407 + DesignSize = ( + 411 + 113) + object lblFunctionName: TLabel + Left = 0 + Top = 19 + Width = 405 + Height = 19 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = 'runtime: function' + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -16 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + ExplicitWidth = 401 + end + object lblCategoryName: TLabel + Left = 0 + Top = 0 + Width = 405 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = 'runtime: category' + ExplicitWidth = 401 + end + object lblHasStates: TLabel + Left = 0 + Top = 74 + Width = 401 + Height = 31 + AutoSize = False + Caption = + 'This function provides the following states. Each state can be c' + + 'ustomized by clicking on the state and changing the setting in t' + + 'he Color column.' + WordWrap = True + end + object lblNoStates: TLabel + Left = 0 + Top = 55 + Width = 195 + Height = 13 + Caption = 'This function has no configurable states.' + Visible = False + end + end + end +end diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.pas b/G940LEDControl/Forms/ButtonFunctionFrm.pas new file mode 100644 index 0000000..5ed3f9c --- /dev/null +++ b/G940LEDControl/Forms/ButtonFunctionFrm.pas @@ -0,0 +1,176 @@ +unit ButtonFunctionFrm; + +interface +uses + System.Classes, + Vcl.Controls, + Vcl.ExtCtrls, + Vcl.Forms, + Vcl.StdCtrls, + + VirtualTrees, + + Profile; + + +type + TButtonFunctionForm = class(TForm) + pnlButtons: TPanel; + btnOK: TButton; + btnCancel: TButton; + vstFunctions: TVirtualStringTree; + vstStates: TVirtualStringTree; + pnlFunction: TPanel; + pnlName: TPanel; + lblFunctionName: TLabel; + lblCategoryName: TLabel; + lblHasStates: TLabel; + lblNoStates: TLabel; + + procedure FormCreate(Sender: TObject); + procedure FormDestroy(Sender: TObject); + procedure vstFunctionsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); + private + FButtonIndex: Integer; + FProfile: TProfile; + protected + procedure LoadFunctions; + + property ButtonIndex: Integer read FButtonIndex write FButtonIndex; + property Profile: TProfile read FProfile write FProfile; + public + class function Execute(AProfile: TProfile; AButtonIndex: Integer): Boolean; + end; + + +implementation +uses + System.SysUtils, + Generics.Collections, + + LEDFunctionIntf, + LEDFunctionRegistry; + + +type + TNodeType = (ntCategory, ntFunction); + TNodeData = record + NodeType: TNodeType; + Provider: ILEDFunctionProvider; + LEDFunction: ILEDFunction; + end; + + PNodeData = ^TNodeData; + + +{$R *.dfm} + + +{ TButtonFunctionForm } +class function TButtonFunctionForm.Execute(AProfile: TProfile; AButtonIndex: Integer): Boolean; +begin + with Self.Create(nil) do + try + Profile := AProfile; + ButtonIndex := AButtonIndex; + + Result := (ShowModal = mrOk); + finally + Free; + end; +end; + +procedure TButtonFunctionForm.FormCreate(Sender: TObject); +begin + vstFunctions.NodeDataSize := SizeOf(TNodeData); + + lblNoStates.Top := lblHasStates.Top; + + lblCategoryName.Caption := ''; + lblFunctionName.Caption := ''; + + LoadFunctions; +end; + + +procedure TButtonFunctionForm.FormDestroy(Sender: TObject); +begin + // +end; + + +procedure TButtonFunctionForm.LoadFunctions; +var + categoryNodes: TDictionary; + + function GetCategoryNode(AProvider: ILEDFunctionProvider; AFunction: ILEDFunction): PVirtualNode; + var + category: string; + nodeData: PNodeData; + + begin + category := AFunction.GetCategoryName; + + if not categoryNodes.ContainsKey(category) then + begin + Result := vstFunctions.AddChild(nil); + Include(Result^.States, vsExpanded); + + nodeData := vstFunctions.GetNodeData(Result); + nodeData^.NodeType := ntCategory; + nodeData^.Provider := AProvider; + nodeData^.LEDFunction := AFunction; + + categoryNodes.Add(category, Result); + end else + Result := categoryNodes.Items[category]; + end; + +var + node: PVirtualNode; + nodeData: PNodeData; + provider: ILEDFunctionProvider; + ledFunction: ILEDFunction; + +begin + vstFunctions.BeginUpdate; + try + vstFunctions.Clear; + + categoryNodes := TDictionary.Create; + try + for provider in TLEDFunctionRegistry.Providers do + begin + for ledFunction in provider do + begin + node := vstFunctions.AddChild(GetCategoryNode(provider, ledFunction)); + nodeData := vstFunctions.GetNodeData(node); + + nodeData^.NodeType := ntFunction; + nodeData^.Provider := provider; + nodeData^.LEDFunction := ledFunction; + end; + end; + finally + FreeAndNil(categoryNodes); + end; + finally + vstFunctions.EndUpdate; + end; +end; + + +procedure TButtonFunctionForm.vstFunctionsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; + TextType: TVSTTextType; var CellText: string); +var + nodeData: PNodeData; + +begin + nodeData := Sender.GetNodeData(Node); + case nodeData^.NodeType of + ntCategory: CellText := nodeData^.LEDFunction.GetCategoryName; + ntFunction: CellText := nodeData^.LEDFunction.GetDisplayName; + end; +end; + +end. diff --git a/G940LEDControl/Forms/MainFrm.dfm b/G940LEDControl/Forms/MainFrm.dfm index 24f95f5..65e216e 100644 --- a/G940LEDControl/Forms/MainFrm.dfm +++ b/G940LEDControl/Forms/MainFrm.dfm @@ -43,7 +43,7 @@ object MainForm: TMainForm 446) object lblP1Function: TLabel Left = 64 - Top = 89 + Top = 73 Width = 364 Height = 13 Anchors = [akLeft, akTop, akRight] @@ -56,54 +56,24 @@ object MainForm: TMainForm Font.Name = 'Tahoma' Font.Style = [fsBold] ParentFont = False - ExplicitWidth = 421 end object lblP1Category: TLabel Left = 64 - Top = 73 + Top = 89 Width = 364 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False Caption = '[runtime: category]' EllipsisPosition = epEndEllipsis - ExplicitWidth = 421 end object lblP2Function: TLabel - Left = 64 - Top = 136 - Width = 364 - Height = 13 - Anchors = [akLeft, akTop, akRight] - AutoSize = False - Caption = '[runtime: function]' - EllipsisPosition = epEndEllipsis - Font.Charset = DEFAULT_CHARSET - Font.Color = clWindowText - Font.Height = -11 - Font.Name = 'Tahoma' - Font.Style = [fsBold] - ParentFont = False - ExplicitWidth = 421 - end - object lblP2Category: TLabel Left = 64 Top = 120 Width = 364 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False - Caption = '[runtime: category]' - EllipsisPosition = epEndEllipsis - ExplicitWidth = 421 - end - object lblP3Function: TLabel - Left = 64 - Top = 183 - Width = 364 - Height = 13 - Anchors = [akLeft, akTop, akRight] - AutoSize = False Caption = '[runtime: function]' EllipsisPosition = epEndEllipsis Font.Charset = DEFAULT_CHARSET @@ -112,26 +82,24 @@ object MainForm: TMainForm Font.Name = 'Tahoma' Font.Style = [fsBold] ParentFont = False - ExplicitWidth = 421 end - object lblP3Category: TLabel + object lblP2Category: TLabel + Left = 64 + Top = 136 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: category]' + EllipsisPosition = epEndEllipsis + end + object lblP3Function: TLabel Left = 64 Top = 167 Width = 364 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False - Caption = '[runtime: category]' - EllipsisPosition = epEndEllipsis - ExplicitWidth = 421 - end - object lblP4Function: TLabel - Left = 64 - Top = 230 - Width = 364 - Height = 13 - Anchors = [akLeft, akTop, akRight] - AutoSize = False Caption = '[runtime: function]' EllipsisPosition = epEndEllipsis Font.Charset = DEFAULT_CHARSET @@ -140,26 +108,24 @@ object MainForm: TMainForm Font.Name = 'Tahoma' Font.Style = [fsBold] ParentFont = False - ExplicitWidth = 421 end - object lblP4Category: TLabel + object lblP3Category: TLabel + Left = 64 + Top = 183 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: category]' + EllipsisPosition = epEndEllipsis + end + object lblP4Function: TLabel Left = 64 Top = 214 Width = 364 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False - Caption = '[runtime: category]' - EllipsisPosition = epEndEllipsis - ExplicitWidth = 421 - end - object lblP5Function: TLabel - Left = 64 - Top = 277 - Width = 364 - Height = 13 - Anchors = [akLeft, akTop, akRight] - AutoSize = False Caption = '[runtime: function]' EllipsisPosition = epEndEllipsis Font.Charset = DEFAULT_CHARSET @@ -168,26 +134,24 @@ object MainForm: TMainForm Font.Name = 'Tahoma' Font.Style = [fsBold] ParentFont = False - ExplicitWidth = 421 end - object lblP5Category: TLabel + object lblP4Category: TLabel + Left = 64 + Top = 230 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: category]' + EllipsisPosition = epEndEllipsis + end + object lblP5Function: TLabel Left = 64 Top = 261 Width = 364 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False - Caption = '[runtime: category]' - EllipsisPosition = epEndEllipsis - ExplicitWidth = 421 - end - object lblP6Function: TLabel - Left = 64 - Top = 324 - Width = 364 - Height = 13 - Anchors = [akLeft, akTop, akRight] - AutoSize = False Caption = '[runtime: function]' EllipsisPosition = epEndEllipsis Font.Charset = DEFAULT_CHARSET @@ -196,26 +160,24 @@ object MainForm: TMainForm Font.Name = 'Tahoma' Font.Style = [fsBold] ParentFont = False - ExplicitWidth = 421 end - object lblP6Category: TLabel + object lblP5Category: TLabel + Left = 64 + Top = 277 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: category]' + EllipsisPosition = epEndEllipsis + end + object lblP6Function: TLabel Left = 64 Top = 308 Width = 364 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False - Caption = '[runtime: category]' - EllipsisPosition = epEndEllipsis - ExplicitWidth = 421 - end - object lblP7Function: TLabel - Left = 64 - Top = 371 - Width = 364 - Height = 13 - Anchors = [akLeft, akTop, akRight] - AutoSize = False Caption = '[runtime: function]' EllipsisPosition = epEndEllipsis Font.Charset = DEFAULT_CHARSET @@ -224,22 +186,46 @@ object MainForm: TMainForm Font.Name = 'Tahoma' Font.Style = [fsBold] ParentFont = False - ExplicitWidth = 421 end - object lblP7Category: TLabel + object lblP6Category: TLabel + Left = 64 + Top = 324 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False + Caption = '[runtime: category]' + EllipsisPosition = epEndEllipsis + end + object lblP7Function: TLabel Left = 64 Top = 355 Width = 364 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False + Caption = '[runtime: function]' + EllipsisPosition = epEndEllipsis + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + end + object lblP7Category: TLabel + Left = 64 + Top = 371 + Width = 364 + Height = 13 + Anchors = [akLeft, akTop, akRight] + AutoSize = False Caption = '[runtime: category]' EllipsisPosition = epEndEllipsis - ExplicitWidth = 421 end object lblP8Function: TLabel Left = 64 - Top = 418 + Top = 402 Width = 364 Height = 13 Anchors = [akLeft, akTop, akRight] @@ -252,18 +238,16 @@ object MainForm: TMainForm Font.Name = 'Tahoma' Font.Style = [fsBold] ParentFont = False - ExplicitWidth = 421 end object lblP8Category: TLabel Left = 64 - Top = 402 + Top = 418 Width = 364 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False Caption = '[runtime: category]' EllipsisPosition = epEndEllipsis - ExplicitWidth = 421 end object lblProfile: TLabel Left = 11 @@ -350,8 +334,9 @@ object MainForm: TMainForm Height = 21 Style = csDropDownList Anchors = [akLeft, akTop, akRight] + Sorted = True TabOrder = 8 - ExplicitWidth = 270 + OnClick = cmbProfilesClick end object btnSaveProfile: TButton Left = 283 diff --git a/G940LEDControl/Forms/MainFrm.pas b/G940LEDControl/Forms/MainFrm.pas index b8f3cdb..f47f618 100644 --- a/G940LEDControl/Forms/MainFrm.pas +++ b/G940LEDControl/Forms/MainFrm.pas @@ -100,12 +100,15 @@ type procedure btnCheckUpdatesClick(Sender: TObject); procedure LEDButtonClick(Sender: TObject); procedure FormDestroy(Sender: TObject); + procedure cmbProfilesClick(Sender: TObject); private FLEDControls: array[0..LED_COUNT - 1] of TLEDControls; FEventMonitor: TOmniEventMonitor; FProfilesFilename: string; FProfiles: TProfileList; + FActiveProfile: TProfile; + FLoadingProfiles: Boolean; // FStateConsumerTask: IOmniTaskControl; protected // procedure ReadFunctions(AReader: IX2PersistReader; AComboBoxes: TComboBoxArray); @@ -120,9 +123,8 @@ type procedure SaveProfiles; function CreateDefaultProfile: TProfile; - -// procedure LoadDefaultProfile; -// procedure SaveDefaultProfile; + procedure LoadActiveProfile; + procedure UpdateButton(AProfile: TProfile; AButtonIndex: Integer); procedure SetDeviceState(const AMessage: string; AFound: Boolean); // procedure SetFSXToggleZoomButton(const ADeviceGUID: TGUID; AButtonIndex: Integer; const ADisplayText: string); @@ -145,6 +147,7 @@ type procedure CMAskAutoUpdate(var Msg: TMessage); message CM_ASKAUTOUPDATE; + property ActiveProfile: TProfile read FActiveProfile; property EventMonitor: TOmniEventMonitor read FEventMonitor; property Profiles: TProfileList read FProfiles; // property StateConsumerTask: IOmniTaskControl read FStateConsumerTask; @@ -155,6 +158,7 @@ implementation uses ComObj, Dialogs, + Graphics, ShellAPI, SysUtils, @@ -164,11 +168,13 @@ uses X2UtApp, X2UtPersistXML, - ButtonSelectFrm, + ButtonFunctionFrm, ConfigConversion, FSXLEDStateProvider, G940LEDStateConsumer, LEDColorIntf, + LEDFunctionIntf, + LEDFunctionRegistry, StaticLEDFunction; @@ -176,7 +182,7 @@ uses const - NameDefaultProfile = 'Default'; + DefaultProfileName = 'Default'; FILENAME_PROFILES = 'G940LEDControl\Profiles.xml'; @@ -284,6 +290,7 @@ begin FLEDControls[ledIndex].ConfigureButton.OnClick := LEDButtonClick; FLEDControls[ledIndex].CategoryLabel.Caption := ''; + FLEDControls[ledIndex].CategoryLabel.Font.Color := clGrayText; FLEDControls[ledIndex].FunctionLabel.Caption := ''; end; end; @@ -293,6 +300,7 @@ procedure TMainForm.LoadProfiles; var defaultProfile: TProfile; persistXML: TX2UtPersistXML; + profile: TProfile; begin if not FileExists(FProfilesFilename) then @@ -305,7 +313,7 @@ begin if Assigned(defaultProfile) then begin - defaultProfile.Name := NameDefaultProfile; + defaultProfile.Name := DefaultProfileName; Profiles.Add(defaultProfile); end; end else @@ -318,6 +326,29 @@ begin FreeAndNil(persistXML); end; end; + + FLoadingProfiles := True; + try + cmbProfiles.Items.BeginUpdate; + try + cmbProfiles.Items.Clear; + + for profile in Profiles do + cmbProfiles.Items.AddObject(profile.Name, profile); + finally + cmbProfiles.Items.EndUpdate; + + if cmbProfiles.Items.Count > 0 then + begin + cmbProfiles.ItemIndex := 0; + + FActiveProfile := TProfile(cmbProfiles.Items.Objects[0]); + LoadActiveProfile; + end; + end; + finally + FLoadingProfiles := False; + end; end; @@ -337,18 +368,68 @@ end; function TMainForm.CreateDefaultProfile: TProfile; +begin + { Default button functions are assigned during UpdateButton } + Result := TProfile.Create; +end; + + +procedure TMainForm.LoadActiveProfile; var - ledIndex: Integer; - button: TProfileButton; + buttonIndex: Integer; begin - Result := TProfile.Create; + if not Assigned(ActiveProfile) then + exit; - for ledIndex := 0 to Pred(LED_COUNT) do + for buttonIndex := 0 to Pred(LED_COUNT) do + UpdateButton(ActiveProfile, buttonIndex); +end; + + +procedure TMainForm.UpdateButton(AProfile: TProfile; AButtonIndex: Integer); +var + button: TProfileButton; + providerUID: string; + functionUID: string; + provider: ILEDFunctionProvider; + buttonFunction: ILEDFunction; + +begin + if AProfile.HasButton(AButtonIndex) then begin - button := Result.Buttons[ledIndex]; - button.ProviderUID := StaticProviderUID; - button.FunctionUID := StaticFunctionUID[lcGreen]; + button := AProfile.Buttons[AButtonIndex]; + providerUID := button.ProviderUID; + functionUID := button.FunctionUID; + end else + begin + providerUID := StaticProviderUID; + functionUID := StaticFunctionUID[lcGreen]; + end; + + buttonFunction := nil; + provider := TLEDFunctionRegistry.Find(providerUID); + if Assigned(provider) then + buttonFunction := provider.Find(functionUID); + + if Assigned(buttonFunction) then + begin + FLEDControls[AButtonIndex].CategoryLabel.Caption := buttonFunction.GetCategoryName; + FLEDControls[AButtonIndex].FunctionLabel.Caption := buttonFunction.GetDisplayName; + end; +end; + + +procedure TMainForm.cmbProfilesClick(Sender: TObject); +begin + if not FLoadingProfiles then + begin + if cmbProfiles.ItemIndex > -1 then + FActiveProfile := TProfile(cmbProfiles.Items.Objects[cmbProfiles.ItemIndex]) + else + FActiveProfile := nil; + + LoadActiveProfile; end; end; @@ -518,11 +599,15 @@ end; procedure TMainForm.LEDButtonClick(Sender: TObject); var - ledIndex: NativeInt; + buttonIndex: NativeInt; begin - ledIndex := (Sender as TComponent).Tag; - // TODO configure led + if not Assigned(ActiveProfile) then + exit; + + buttonIndex := (Sender as TComponent).Tag; + if TButtonFunctionForm.Execute(ActiveProfile, buttonIndex) then + UpdateButton(ActiveProfile, buttonIndex); end; diff --git a/G940LEDControl/G940LEDControl.dpr b/G940LEDControl/G940LEDControl.dpr index 32ffd00..8ccefcb 100644 --- a/G940LEDControl/G940LEDControl.dpr +++ b/G940LEDControl/G940LEDControl.dpr @@ -23,14 +23,16 @@ uses DynamicLEDColor in 'Units\DynamicLEDColor.pas', LEDStateIntf in 'Units\LEDStateIntf.pas', LEDState in 'Units\LEDState.pas', - Profile in 'Units\Profile.pas'; + Profile in 'Units\Profile.pas', + LEDColorPool in 'Units\LEDColorPool.pas', + ButtonFunctionFrm in 'Forms\ButtonFunctionFrm.pas' {ButtonFunctionForm}; {$R *.res} var MainForm: TMainForm; - + begin Application.Initialize; Application.MainFormOnTaskbar := True; diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj index 5c37c22..6257cfb 100644 --- a/G940LEDControl/G940LEDControl.dproj +++ b/G940LEDControl/G940LEDControl.dproj @@ -186,6 +186,11 @@ + + +
ButtonFunctionForm
+ dfm +
Cfg_2 Base diff --git a/G940LEDControl/Units/ConfigConversion.pas b/G940LEDControl/Units/ConfigConversion.pas index a5e0b93..f8adcd0 100644 --- a/G940LEDControl/Units/ConfigConversion.pas +++ b/G940LEDControl/Units/ConfigConversion.pas @@ -14,6 +14,8 @@ function Convert0To1: TProfile; begin Result := nil; + + // FUNCTION_NONE = 0; // FUNCTION_OFF = 1; // FUNCTION_RED = 2; diff --git a/G940LEDControl/Units/LEDColorPool.pas b/G940LEDControl/Units/LEDColorPool.pas new file mode 100644 index 0000000..720c3be --- /dev/null +++ b/G940LEDControl/Units/LEDColorPool.pas @@ -0,0 +1,101 @@ +unit LEDColorPool; + +interface +uses + LEDColorIntf; + + +type + TLEDColorPoolEntry = (cpeStaticOff, + cpeStaticGreen, + cpeStaticAmber, + cpeStaticRed, + + cpeFlashingGreenFast, + cpeFlashingGreenNormal, + cpeFlashingAmberFast, + cpeFlashingAmberNormal, + cpeFlashingRedFast, + cpeFlashingRedNormal); + + TLEDColorPool = class(TObject) + private + FStates: array[TLEDColorPoolEntry] of ILEDColor; + protected + class function Instance: TLEDColorPool; + + function DoGetColor(AEntry: TLEDColorPoolEntry): ILEDColor; + public + class function GetColor(AEntry: TLEDColorPoolEntry): ILEDColor; + end; + + +implementation +uses + SysUtils, + + DynamicLEDColor, + StaticLEDColor; + + +var + LEDColorPoolInstance: TLEDColorPool; + + +{ TLEDStatePool } +class function TLEDColorPool.GetColor(AEntry: TLEDColorPoolEntry): ILEDColor; +begin + Result := Instance.DoGetColor(AEntry); +end; + + +class function TLEDColorPool.Instance: TLEDColorPool; +begin + if not Assigned(LEDColorPoolInstance) then + LEDColorPoolInstance := TLEDColorPool.Create; + + Result := LEDColorPoolInstance; +end; + + +function TLEDColorPool.DoGetColor(AEntry: TLEDColorPoolEntry): ILEDColor; + + function GetFlashingCycle(AColor: TLEDColor): TLEDColorDynArray; + begin + SetLength(Result, 2); + Result[0] := AColor; + Result[1] := lcOff; + end; + +var + state: ILEDColor; + +begin + if not Assigned(FStates[AEntry]) then + begin + case AEntry of + cpeStaticOff: state := TStaticLEDColor.Create(lcOff); + cpeStaticGreen: state := TStaticLEDColor.Create(lcGreen); + cpeStaticAmber: state := TStaticLEDColor.Create(lcAmber); + cpeStaticRed: state := TStaticLEDColor.Create(lcRed); + + cpeFlashingGreenFast: state := TDynamicLEDColor.Create(GetFlashingCycle(lcGreen), TICKINTERVAL_FAST); + cpeFlashingGreenNormal: state := TDynamicLEDColor.Create(GetFlashingCycle(lcGreen), TICKINTERVAL_NORMAL); + cpeFlashingAmberFast: state := TDynamicLEDColor.Create(GetFlashingCycle(lcAmber), TICKINTERVAL_FAST); + cpeFlashingAmberNormal: state := TDynamicLEDColor.Create(GetFlashingCycle(lcAmber), TICKINTERVAL_NORMAL); + cpeFlashingRedFast: state := TDynamicLEDColor.Create(GetFlashingCycle(lcRed), TICKINTERVAL_FAST); + cpeFlashingRedNormal: state := TDynamicLEDColor.Create(GetFlashingCycle(lcRed), TICKINTERVAL_NORMAL); + end; + + FStates[AEntry] := state; + Result := state; + end else + Result := FStates[AEntry]; +end; + + +initialization +finalization + FreeAndNil(LEDColorPoolInstance); + +end. diff --git a/G940LEDControl/Units/LEDFunction.pas b/G940LEDControl/Units/LEDFunction.pas index 74bb71d..9dab8b5 100644 --- a/G940LEDControl/Units/LEDFunction.pas +++ b/G940LEDControl/Units/LEDFunction.pas @@ -18,8 +18,9 @@ type protected { ILEDFunctionProvider } function GetUID: string; virtual; abstract; - function GetEnumerator: ILEDFunctionEnumerator; virtual; + + function Find(const AFunctionUID: string): ILEDFunction; virtual; public constructor Create; destructor Destroy; override; @@ -157,6 +158,22 @@ begin end; +function TCustomLEDFunctionProvider.Find(const AFunctionUID: string): ILEDFunction; +var + ledFunction: ILEDFunction; + +begin + Result := nil; + + for ledFunction in (Self as ILEDFunctionProvider) do + if ledFunction.GetUID = AFunctionUID then + begin + Result := ledFunction; + break; + end; +end; + + procedure TCustomLEDFunctionProvider.RegisterFunction(AFunction: ILEDFunction); begin FFunctions.Add(AFunction as ILEDFunction); diff --git a/G940LEDControl/Units/LEDFunctionIntf.pas b/G940LEDControl/Units/LEDFunctionIntf.pas index 1759ac5..4af1fbb 100644 --- a/G940LEDControl/Units/LEDFunctionIntf.pas +++ b/G940LEDControl/Units/LEDFunctionIntf.pas @@ -8,6 +8,7 @@ uses type + ILEDFunction = interface; ILEDFunctionEnumerator = interface; ILEDStateEnumerator = interface; @@ -15,8 +16,9 @@ type ILEDFunctionProvider = interface ['{B38F6F90-DC96-42CE-B8F0-21F0DD8AA537}'] function GetUID: string; - function GetEnumerator: ILEDFunctionEnumerator; + + function Find(const AFunctionUID: string): ILEDFunction; end; @@ -26,11 +28,16 @@ type function GetDisplayName: string; function GetUID: string; - function GetEnumerator: ILEDStateEnumerator; function GetCurrentState: ILEDState; end; + ILEDMultiStateFunction = interface(ILEDFunction) + ['{F16ADF7E-1C1C-4676-8D4F-135B68A80B52}'] + function GetEnumerator: ILEDStateEnumerator; + end; + + ILEDFunctionEnumerator = interface ['{A03E4E54-19CB-4C08-AD5F-20265817086D}'] function GetCurrent: ILEDFunction; diff --git a/G940LEDControl/Units/LEDState.pas b/G940LEDControl/Units/LEDState.pas new file mode 100644 index 0000000..446d43b --- /dev/null +++ b/G940LEDControl/Units/LEDState.pas @@ -0,0 +1,57 @@ +unit LEDState; + +interface +uses + LEDColorIntf, + LEDStateIntf; + + +type + TLEDState = class(TInterfacedObject, ILEDState) + private + FDisplayName: string; + FUID: string; + FColor: ILEDColor; + protected + { ILEDState } + function GetDisplayName: string; + function GetUID: string; + + function GetColor: ILEDColor; + public + constructor Create(const AUID, ADisplayName: string; AColor: ILEDColor); + end; + + +implementation + +{ TLEDState } +constructor TLEDState.Create(const AUID, ADisplayName: string; AColor: ILEDColor); +begin + inherited Create; + + FUID := AUID; + FDisplayName := ADisplayName; + + FColor := AColor; +end; + + +function TLEDState.GetDisplayName: string; +begin + Result := FDisplayName; +end; + + +function TLEDState.GetUID: string; +begin + Result := FUID; +end; + + +function TLEDState.GetColor: ILEDColor; +begin + Result := FColor; +end; + +end. diff --git a/G940LEDControl/Units/LEDStateIntf.pas b/G940LEDControl/Units/LEDStateIntf.pas new file mode 100644 index 0000000..606b599 --- /dev/null +++ b/G940LEDControl/Units/LEDStateIntf.pas @@ -0,0 +1,20 @@ +unit LEDStateIntf; + +interface +uses + LEDColorIntf; + + +type + ILEDState = interface + ['{0361CBD5-E64E-4972-A8A4-D5FE0B0DFB1C}'] + function GetDisplayName: string; + function GetUID: string; + + function GetColor: ILEDColor; + end; + + +implementation + +end. diff --git a/G940LEDControl/Units/Profile.pas b/G940LEDControl/Units/Profile.pas index 4aaa0bc..1dfc8df 100644 --- a/G940LEDControl/Units/Profile.pas +++ b/G940LEDControl/Units/Profile.pas @@ -38,6 +38,8 @@ type constructor Create; destructor Destroy; override; + function HasButton(AIndex: Integer): Boolean; + property Name: string read FName write FName; property ButtonCount: Integer read GetButtonCount; @@ -140,6 +142,13 @@ begin end; +function TProfile.HasButton(AIndex: Integer): Boolean; +begin + Result := (FButtons.Count > AIndex) and + Assigned(FButtons[AIndex]); +end; + + function TProfile.GetButtonCount: Integer; begin Result := FButtons.Count; diff --git a/G940LEDControl/Units/StaticLEDFunction.pas b/G940LEDControl/Units/StaticLEDFunction.pas index b92195f..bf4eeeb 100644 --- a/G940LEDControl/Units/StaticLEDFunction.pas +++ b/G940LEDControl/Units/StaticLEDFunction.pas @@ -3,7 +3,8 @@ unit StaticLEDFunction; interface uses LEDFunction, - LEDColorIntf; + LEDColorIntf, + LEDStateIntf; type @@ -22,6 +23,8 @@ type function GetCategoryName: string; override; function GetDisplayName: string; override; function GetUID: string; override; + + function GetCurrentState: ILEDState; override; public constructor Create(AColor: TLEDColor); end; @@ -101,6 +104,13 @@ begin end; +function TStaticLEDFunction.GetCurrentState: ILEDState; +begin + +end; + + + initialization TLEDFunctionRegistry.Register(TStaticLEDFunctionProvider.Create); From b1035c6db2117c6d436bd104f56b68e2366c572d Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Thu, 14 Feb 2013 07:32:03 +0000 Subject: [PATCH 05/16] Added: categories shown in bold --- G940LEDControl/Forms/ButtonFunctionFrm.dfm | 14 +------------- G940LEDControl/Forms/ButtonFunctionFrm.pas | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.dfm b/G940LEDControl/Forms/ButtonFunctionFrm.dfm index 84d8bf2..a4a0057 100644 --- a/G940LEDControl/Forms/ButtonFunctionFrm.dfm +++ b/G940LEDControl/Forms/ButtonFunctionFrm.dfm @@ -26,9 +26,6 @@ object ButtonFunctionForm: TButtonFunctionForm Align = alBottom BevelOuter = bvNone TabOrder = 0 - ExplicitLeft = 168 - ExplicitTop = 232 - ExplicitWidth = 185 DesignSize = ( 692 41) @@ -42,7 +39,6 @@ object ButtonFunctionForm: TButtonFunctionForm Default = True ModalResult = 1 TabOrder = 0 - ExplicitLeft = 467 end object btnCancel: TButton Left = 609 @@ -54,7 +50,6 @@ object ButtonFunctionForm: TButtonFunctionForm Caption = 'Cancel' ModalResult = 2 TabOrder = 1 - ExplicitLeft = 548 end end object vstFunctions: TVirtualStringTree @@ -80,6 +75,7 @@ object ButtonFunctionForm: TButtonFunctionForm TreeOptions.PaintOptions = [toShowButtons, toShowDropmark, toShowTreeLines, toThemeAware, toUseBlendedImages] TreeOptions.SelectionOptions = [toFullRowSelect] OnGetText = vstFunctionsGetText + OnPaintText = vstFunctionsPaintText Columns = < item Position = 0 @@ -100,10 +96,6 @@ object ButtonFunctionForm: TButtonFunctionForm Align = alClient BevelOuter = bvNone TabOrder = 2 - ExplicitLeft = 224 - ExplicitTop = 192 - ExplicitWidth = 185 - ExplicitHeight = 41 object vstStates: TVirtualStringTree Left = 0 Top = 113 @@ -118,9 +110,6 @@ object ButtonFunctionForm: TButtonFunctionForm Header.Font.Style = [] Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoShowSortGlyphs, hoVisible] TabOrder = 0 - ExplicitTop = 93 - ExplicitWidth = 407 - ExplicitHeight = 259 Columns = < item Position = 0 @@ -141,7 +130,6 @@ object ButtonFunctionForm: TButtonFunctionForm Align = alTop BevelOuter = bvNone TabOrder = 1 - ExplicitWidth = 407 DesignSize = ( 411 113) diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.pas b/G940LEDControl/Forms/ButtonFunctionFrm.pas index 5ed3f9c..6c1c5b4 100644 --- a/G940LEDControl/Forms/ButtonFunctionFrm.pas +++ b/G940LEDControl/Forms/ButtonFunctionFrm.pas @@ -6,6 +6,7 @@ uses Vcl.Controls, Vcl.ExtCtrls, Vcl.Forms, + Vcl.Graphics, Vcl.StdCtrls, VirtualTrees, @@ -30,6 +31,7 @@ type procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure vstFunctionsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); + procedure vstFunctionsPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); private FButtonIndex: Integer; FProfile: TProfile; @@ -167,10 +169,26 @@ var begin nodeData := Sender.GetNodeData(Node); + case nodeData^.NodeType of ntCategory: CellText := nodeData^.LEDFunction.GetCategoryName; ntFunction: CellText := nodeData^.LEDFunction.GetDisplayName; end; end; + +procedure TButtonFunctionForm.vstFunctionsPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; + Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); +var + nodeData: PNodeData; + +begin + nodeData := Sender.GetNodeData(Node); + + if nodeData^.NodeType = ntCategory then + TargetCanvas.Font.Style := [fsBold] + else + TargetCanvas.Font.Style := []; +end; + end. From 32f411c1b8cfbd78c256af0b22837d1df84068fe Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Thu, 14 Feb 2013 21:45:25 +0000 Subject: [PATCH 06/16] Added: start of configuration conversion Added: registration of states Added: prototype FSX function implemention - subject to change; separation of definition and running state will be added later --- G940LEDControl/Forms/MainFrm.dfm | 10 +- G940LEDControl/Forms/MainFrm.pas | 64 +---- G940LEDControl/G940LEDControl.dpr | 6 +- G940LEDControl/G940LEDControl.dproj | 4 + G940LEDControl/Units/ConfigConversion.pas | 192 +++++++++++---- G940LEDControl/Units/FSXLEDFunction.pas | 231 +++++++++++++++++++ G940LEDControl/Units/FSXResources.pas | 28 +++ G940LEDControl/Units/FSXSimConnectClient.pas | 14 ++ G940LEDControl/Units/LEDColorPool.pas | 16 +- G940LEDControl/Units/LEDFunction.pas | 64 +++-- G940LEDControl/Units/StaticLEDFunction.pas | 44 ++-- G940LEDControl/Units/StaticResources.pas | 31 +++ 12 files changed, 546 insertions(+), 158 deletions(-) create mode 100644 G940LEDControl/Units/FSXLEDFunction.pas create mode 100644 G940LEDControl/Units/FSXResources.pas create mode 100644 G940LEDControl/Units/FSXSimConnectClient.pas create mode 100644 G940LEDControl/Units/StaticResources.pas diff --git a/G940LEDControl/Forms/MainFrm.dfm b/G940LEDControl/Forms/MainFrm.dfm index 65e216e..85a5514 100644 --- a/G940LEDControl/Forms/MainFrm.dfm +++ b/G940LEDControl/Forms/MainFrm.dfm @@ -32,12 +32,8 @@ object MainForm: TMainForm ActivePage = tsFSX Align = alClient TabOrder = 1 - ExplicitWidth = 390 - ExplicitHeight = 510 object tsFSX: TTabSheet Caption = 'Configuration' - ExplicitWidth = 382 - ExplicitHeight = 482 DesignSize = ( 441 446) @@ -346,7 +342,6 @@ object MainForm: TMainForm Anchors = [akTop, akRight] Caption = 'Save as...' TabOrder = 9 - ExplicitLeft = 340 end object btnDeleteProfile: TButton Left = 364 @@ -356,12 +351,13 @@ object MainForm: TMainForm Anchors = [akTop, akRight] Caption = 'Delete' TabOrder = 10 - ExplicitLeft = 421 end end object tsAbout: TTabSheet Caption = 'About' ImageIndex = 1 + ExplicitLeft = 0 + ExplicitTop = 0 ExplicitWidth = 382 ExplicitHeight = 482 object lblVersionCaption: TLabel @@ -479,7 +475,6 @@ object MainForm: TMainForm Align = alTop BevelOuter = bvNone TabOrder = 0 - ExplicitWidth = 390 DesignSize = ( 449 64) @@ -861,7 +856,6 @@ object MainForm: TMainForm TabOrder = 0 Visible = False OnClick = btnRetryClick - ExplicitLeft = 315 end end end diff --git a/G940LEDControl/Forms/MainFrm.pas b/G940LEDControl/Forms/MainFrm.pas index f47f618..51bd1c9 100644 --- a/G940LEDControl/Forms/MainFrm.pas +++ b/G940LEDControl/Forms/MainFrm.pas @@ -175,7 +175,7 @@ uses LEDColorIntf, LEDFunctionIntf, LEDFunctionRegistry, - StaticLEDFunction; + StaticResources; {$R *.dfm} @@ -192,11 +192,6 @@ const TEXT_STATE_NOTFOUND = 'Not found'; TEXT_STATE_FOUND = 'Connected'; - KEY_SETTINGS = '\Software\X2Software\G940LEDControl\'; - SECTION_DEFAULTPROFILE = 'DefaultProfile'; - SECTION_FSX = 'FSX'; - SECTION_SETTINGS = 'Settings'; - type TComboBoxFunctionConsumer = class(TInterfacedObject, IFunctionConsumer) @@ -306,7 +301,7 @@ begin if not FileExists(FProfilesFilename) then begin { Check if version 0.x settings are in the registry } - defaultProfile := ConfigConversion.Convert0To1; + defaultProfile := ConfigConversion.ConvertProfile0To1; if not Assigned(defaultProfile) then defaultProfile := CreateDefaultProfile; @@ -519,61 +514,6 @@ end; //end; -//procedure TMainForm.LoadDefaultProfile; -//var -// registryReader: TX2UtPersistRegistry; -// reader: IX2PersistReader; -// -//begin -// registryReader := TX2UtPersistRegistry.Create; -// try -// registryReader.RootKey := HKEY_CURRENT_USER; -// registryReader.Key := KEY_SETTINGS; -// -// reader := registryReader.CreateReader; -// -// if reader.BeginSection(SECTION_DEFAULTPROFILE) then -// try -// ReadFunctions(reader, FFSXComboBoxes); -// ReadFSXExtra(reader); -// finally -// reader.EndSection; -// end; -// -// ReadAutoUpdate(reader); -// finally -// FreeAndNil(registryReader); -// end; -//end; -// -// -//procedure TMainForm.SaveDefaultProfile; -//var -// registryWriter: TX2UtPersistRegistry; -// writer: IX2PersistWriter; -// -//begin -// registryWriter := TX2UtPersistRegistry.Create; -// try -// registryWriter.RootKey := HKEY_CURRENT_USER; -// registryWriter.Key := KEY_SETTINGS; -// -// writer := registryWriter.CreateWriter; -// if writer.BeginSection(SECTION_DEFAULTPROFILE) then -// try -// WriteFunctions(writer, FFSXComboBoxes); -// WriteFSXExtra(writer); -// finally -// writer.EndSection; -// end; -// -// WriteAutoUpdate(writer); -// finally -// FreeAndNil(registryWriter); -// end; -//end; - - //procedure TMainForm.InitializeStateProvider(AProviderClass: TLEDStateProviderClass); //begin // UpdateMapping; diff --git a/G940LEDControl/G940LEDControl.dpr b/G940LEDControl/G940LEDControl.dpr index 8ccefcb..da5f91d 100644 --- a/G940LEDControl/G940LEDControl.dpr +++ b/G940LEDControl/G940LEDControl.dpr @@ -25,7 +25,11 @@ uses LEDState in 'Units\LEDState.pas', Profile in 'Units\Profile.pas', LEDColorPool in 'Units\LEDColorPool.pas', - ButtonFunctionFrm in 'Forms\ButtonFunctionFrm.pas' {ButtonFunctionForm}; + ButtonFunctionFrm in 'Forms\ButtonFunctionFrm.pas' {ButtonFunctionForm}, + FSXLEDFunction in 'Units\FSXLEDFunction.pas', + StaticResources in 'Units\StaticResources.pas', + FSXResources in 'Units\FSXResources.pas', + FSXSimConnectClient in 'Units\FSXSimConnectClient.pas'; {$R *.res} diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj index 6257cfb..5638f7d 100644 --- a/G940LEDControl/G940LEDControl.dproj +++ b/G940LEDControl/G940LEDControl.dproj @@ -191,6 +191,10 @@
ButtonFunctionForm
dfm
+ + + + Cfg_2 Base diff --git a/G940LEDControl/Units/ConfigConversion.pas b/G940LEDControl/Units/ConfigConversion.pas index f8adcd0..2b4b7e5 100644 --- a/G940LEDControl/Units/ConfigConversion.pas +++ b/G940LEDControl/Units/ConfigConversion.pas @@ -5,59 +5,167 @@ uses Profile; { Version 0.x: registry -> 1.x: XML } - function Convert0To1: TProfile; + function ConvertProfile0To1: TProfile; implementation +uses + System.SysUtils, + Winapi.Windows, + + X2UtPersistIntf, + X2UtPersistRegistry, + + LEDColorIntf, + StaticResources; + + +const + V0_FUNCTION_NONE = 0; + V0_FUNCTION_OFF = 1; + V0_FUNCTION_RED = 2; + V0_FUNCTION_AMBER = 3; + V0_FUNCTION_GREEN = 4; + V0_FUNCTIONPROVIDER_OFFSET = V0_FUNCTION_GREEN; + + + V0_FUNCTIONFSX_GEAR = V0_FUNCTIONPROVIDER_OFFSET + 1; + V0_FUNCTIONFSX_LANDINGLIGHTS = V0_FUNCTIONPROVIDER_OFFSET + 2; + V0_FUNCTIONFSX_INSTRUMENTLIGHTS = V0_FUNCTIONPROVIDER_OFFSET + 3; + V0_FUNCTIONFSX_PARKINGBRAKE = V0_FUNCTIONPROVIDER_OFFSET + 4; + V0_FUNCTIONFSX_ENGINE = V0_FUNCTIONPROVIDER_OFFSET + 5; + + V0_FUNCTIONFSX_EXITDOOR = V0_FUNCTIONPROVIDER_OFFSET + 6; + V0_FUNCTIONFSX_STROBELIGHTS = V0_FUNCTIONPROVIDER_OFFSET + 7; + V0_FUNCTIONFSX_NAVLIGHTS = V0_FUNCTIONPROVIDER_OFFSET + 8; + V0_FUNCTIONFSX_BEACONLIGHTS = V0_FUNCTIONPROVIDER_OFFSET + 9; + V0_FUNCTIONFSX_FLAPS = V0_FUNCTIONPROVIDER_OFFSET + 10; + V0_FUNCTIONFSX_BATTERYMASTER = V0_FUNCTIONPROVIDER_OFFSET + 11; + V0_FUNCTIONFSX_AVIONICSMASTER = V0_FUNCTIONPROVIDER_OFFSET + 12; + + V0_FUNCTIONFSX_SPOILERS = V0_FUNCTIONPROVIDER_OFFSET + 13; + + V0_FUNCTIONFSX_PRESSURIZATIONDUMPSWITCH = V0_FUNCTIONPROVIDER_OFFSET + 14; + V0_FUNCTIONFSX_CARBHEAT = V0_FUNCTIONPROVIDER_OFFSET + 15; + V0_FUNCTIONFSX_AUTOPILOT = V0_FUNCTIONPROVIDER_OFFSET + 16; + V0_FUNCTIONFSX_FUELPUMP = V0_FUNCTIONPROVIDER_OFFSET + 17; + + V0_FUNCTIONFSX_TAILHOOK = V0_FUNCTIONPROVIDER_OFFSET + 18; + + V0_FUNCTIONFSX_AUTOPILOT_AMBER = V0_FUNCTIONPROVIDER_OFFSET + 19; + V0_FUNCTIONFSX_AUTOPILOT_HEADING = V0_FUNCTIONPROVIDER_OFFSET + 20; + V0_FUNCTIONFSX_AUTOPILOT_APPROACH = V0_FUNCTIONPROVIDER_OFFSET + 21; + V0_FUNCTIONFSX_AUTOPILOT_BACKCOURSE = V0_FUNCTIONPROVIDER_OFFSET + 22; + V0_FUNCTIONFSX_AUTOPILOT_ALTITUDE = V0_FUNCTIONPROVIDER_OFFSET + 23; + V0_FUNCTIONFSX_AUTOPILOT_NAV = V0_FUNCTIONPROVIDER_OFFSET + 24; + + V0_FUNCTIONFSX_TAXILIGHTS = V0_FUNCTIONPROVIDER_OFFSET + 25; + V0_FUNCTIONFSX_RECOGNITIONLIGHTS = V0_FUNCTIONPROVIDER_OFFSET + 26; + + + +procedure ConvertProfileFunction0To1(AOldFunction: Integer; AButton: TProfileButton); + + procedure SetButton(const AProviderUID, AFunctionUID: string); + begin + AButton.ProviderUID := AProviderUID; + AButton.FunctionUID := AFunctionUID; + end; + + +begin + case AOldFunction of + { Static } + V0_FUNCTION_OFF: SetButton(StaticProviderUID, StaticFunctionUID[lcOff]); + V0_FUNCTION_RED: SetButton(StaticProviderUID, StaticFunctionUID[lcRed]); + V0_FUNCTION_AMBER: SetButton(StaticProviderUID, StaticFunctionUID[lcAmber]); + V0_FUNCTION_GREEN: SetButton(StaticProviderUID, StaticFunctionUID[lcGreen]); + + { FSX } + { + V0_FUNCTIONFSX_GEAR: + V0_FUNCTIONFSX_LANDINGLIGHTS: + V0_FUNCTIONFSX_INSTRUMENTLIGHTS: + V0_FUNCTIONFSX_PARKINGBRAKE: + V0_FUNCTIONFSX_ENGINE: + + V0_FUNCTIONFSX_EXITDOOR: + V0_FUNCTIONFSX_STROBELIGHTS: + V0_FUNCTIONFSX_NAVLIGHTS: + V0_FUNCTIONFSX_BEACONLIGHTS: + V0_FUNCTIONFSX_FLAPS: + V0_FUNCTIONFSX_BATTERYMASTER: + V0_FUNCTIONFSX_AVIONICSMASTER: + V0_FUNCTIONFSX_SPOILERS: + V0_FUNCTIONFSX_PRESSURIZATIONDUMPSWITCH: + V0_FUNCTIONFSX_CARBHEAT: + V0_FUNCTIONFSX_AUTOPILOT: + V0_FUNCTIONFSX_FUELPUMP: + V0_FUNCTIONFSX_TAILHOOK: + V0_FUNCTIONFSX_AUTOPILOT_AMBER: + V0_FUNCTIONFSX_AUTOPILOT_HEADING: + V0_FUNCTIONFSX_AUTOPILOT_APPROACH: + V0_FUNCTIONFSX_AUTOPILOT_BACKCOURSE: + V0_FUNCTIONFSX_AUTOPILOT_ALTITUDE: + V0_FUNCTIONFSX_AUTOPILOT_NAV: + V0_FUNCTIONFSX_TAXILIGHTS: + V0_FUNCTIONFSX_RECOGNITIONLIGHTS: + } + else + SetButton(StaticProviderUID, StaticFunctionUID[lcGreen]); + end; +end; + + +function ConvertProfile0To1: TProfile; +const + KEY_SETTINGS = '\Software\X2Software\G940LEDControl\'; + SECTION_DEFAULTPROFILE = 'DefaultProfile'; + SECTION_FSX = 'FSX'; + SECTION_SETTINGS = 'Settings'; + +var + registryReader: TX2UtPersistRegistry; + reader: IX2PersistReader; + buttonIndex: Integer; + value: Integer; -function Convert0To1: TProfile; begin Result := nil; + registryReader := TX2UtPersistRegistry.Create; + try + registryReader.RootKey := HKEY_CURRENT_USER; + registryReader.Key := KEY_SETTINGS; + reader := registryReader.CreateReader; -// FUNCTION_NONE = 0; -// FUNCTION_OFF = 1; -// FUNCTION_RED = 2; -// FUNCTION_AMBER = 3; -// FUNCTION_GREEN = 4; + if reader.BeginSection(SECTION_DEFAULTPROFILE) then + try + if reader.BeginSection(SECTION_FSX) then + try + for buttonIndex := 0 to 7 do + begin + if reader.ReadInteger('Function' + IntToStr(buttonIndex), value) then + begin + if not Assigned(Result) then + Result := TProfile.Create; -{ -FUNCTION_PROVIDER_OFFSET = 4 + ConvertProfileFunction0To1(value, Result.Buttons[buttonIndex]); + end; + end; + finally + reader.EndSection; + end; + finally + reader.EndSection; + end; - FUNCTION_FSX_GEAR = FUNCTION_PROVIDER_OFFSET + 1; - FUNCTION_FSX_LANDINGLIGHTS = FUNCTION_PROVIDER_OFFSET + 2; - FUNCTION_FSX_INSTRUMENTLIGHTS = FUNCTION_PROVIDER_OFFSET + 3; - FUNCTION_FSX_PARKINGBRAKE = FUNCTION_PROVIDER_OFFSET + 4; - FUNCTION_FSX_ENGINE = FUNCTION_PROVIDER_OFFSET + 5; - - FUNCTION_FSX_EXITDOOR = FUNCTION_PROVIDER_OFFSET + 6; - FUNCTION_FSX_STROBELIGHTS = FUNCTION_PROVIDER_OFFSET + 7; - FUNCTION_FSX_NAVLIGHTS = FUNCTION_PROVIDER_OFFSET + 8; - FUNCTION_FSX_BEACONLIGHTS = FUNCTION_PROVIDER_OFFSET + 9; - FUNCTION_FSX_FLAPS = FUNCTION_PROVIDER_OFFSET + 10; - FUNCTION_FSX_BATTERYMASTER = FUNCTION_PROVIDER_OFFSET + 11; - FUNCTION_FSX_AVIONICSMASTER = FUNCTION_PROVIDER_OFFSET + 12; - - FUNCTION_FSX_SPOILERS = FUNCTION_PROVIDER_OFFSET + 13; - - FUNCTION_FSX_PRESSURIZATIONDUMPSWITCH = FUNCTION_PROVIDER_OFFSET + 14; - FUNCTION_FSX_CARBHEAT = FUNCTION_PROVIDER_OFFSET + 15; - FUNCTION_FSX_AUTOPILOT = FUNCTION_PROVIDER_OFFSET + 16; - FUNCTION_FSX_FUELPUMP = FUNCTION_PROVIDER_OFFSET + 17; - - FUNCTION_FSX_TAILHOOK = FUNCTION_PROVIDER_OFFSET + 18; - - FUNCTION_FSX_AUTOPILOT_AMBER = FUNCTION_PROVIDER_OFFSET + 19; - FUNCTION_FSX_AUTOPILOT_HEADING = FUNCTION_PROVIDER_OFFSET + 20; - FUNCTION_FSX_AUTOPILOT_APPROACH = FUNCTION_PROVIDER_OFFSET + 21; - FUNCTION_FSX_AUTOPILOT_BACKCOURSE = FUNCTION_PROVIDER_OFFSET + 22; - FUNCTION_FSX_AUTOPILOT_ALTITUDE = FUNCTION_PROVIDER_OFFSET + 23; - FUNCTION_FSX_AUTOPILOT_NAV = FUNCTION_PROVIDER_OFFSET + 24; - - FUNCTION_FSX_TAXILIGHTS = FUNCTION_PROVIDER_OFFSET + 25; - FUNCTION_FSX_RECOGNITIONLIGHTS = FUNCTION_PROVIDER_OFFSET + 26; - } + // TODO auto update settings + //ReadAutoUpdate(reader); + finally + FreeAndNil(registryReader); + end; end; end. diff --git a/G940LEDControl/Units/FSXLEDFunction.pas b/G940LEDControl/Units/FSXLEDFunction.pas new file mode 100644 index 0000000..a9f0684 --- /dev/null +++ b/G940LEDControl/Units/FSXLEDFunction.pas @@ -0,0 +1,231 @@ +unit FSXLEDFunction; + +interface +uses + Generics.Collections, + + LEDFunction, + LEDStateIntf, + ObserverIntf; + + +type + TCustomFSXFunction = class; + TCustomFSXFunctionList = TObjectList; + + + TFSXLEDFunctionProvider = class(TCustomLEDFunctionProvider) + private + FConnectedFunctions: TCustomFSXFunctionList; + FSimConnectHandle: THandle; + protected + procedure SimConnect; + procedure SimDisconnect; + + procedure Connect(AFunction: TCustomFSXFunction); virtual; + procedure Disconnect(AFunction: TCustomFSXFunction); virtual; + + property ConnectedFunctions: TCustomFSXFunctionList read FConnectedFunctions; + property SimConnectHandle: THandle read FSimConnectHandle; + protected + procedure RegisterFunctions; override; + + function GetUID: string; override; + end; + + + TCustomFSXFunction = class(TCustomMultiStateLEDFunction) + private + FProvider: TFSXLEDFunctionProvider; + protected + procedure SimConnected; virtual; + procedure SimDisconnected; virtual; + + property Provider: TFSXLEDFunctionProvider read FProvider; + protected + procedure Attach(AObserver: IObserver); override; + procedure Detach(AObserver: IObserver); override; + + function GetCategoryName: string; override; + public + constructor Create(AProvider: TFSXLEDFunctionProvider); + end; + + + TFSXGearFunction = class(TCustomFSXFunction) + private + FRetractedState: ILEDState; + FBetweenState: ILEDState; + FExtendedState: ILEDState; + FSpeedExceededState: ILEDState; + FDamageBySpeedState: ILEDState; + protected + procedure RegisterStates; override; + + function GetDisplayName: string; override; + function GetUID: string; override; + + function GetCurrentState: ILEDState; override; + end; + + +implementation +uses + FSXResources, + LEDColorPool, + LEDFunctionRegistry, + LEDState; + + +{ TFSXLEDFunctionProvider } +procedure TFSXLEDFunctionProvider.RegisterFunctions; +begin + RegisterFunction(TFSXGearFunction.Create(Self)); +end; + + +function TFSXLEDFunctionProvider.GetUID: string; +begin + Result := FSXProviderUID; +end; + + +procedure TFSXLEDFunctionProvider.SimConnect; +var + fsxFunction: TCustomFSXFunction; + +begin + if SimConnectHandle <> 0 then + exit; + +// FSimConnectHandle := + + if SimConnectHandle <> 0 then + begin + for fsxFunction in ConnectedFunctions do + fsxFunction.SimConnected; + end; +end; + + +procedure TFSXLEDFunctionProvider.SimDisconnect; +begin + if SimConnectHandle = 0 then + exit; +end; + + +procedure TFSXLEDFunctionProvider.Connect(AFunction: TCustomFSXFunction); +begin + if ConnectedFunctions.IndexOf(AFunction) = -1 then + begin + ConnectedFunctions.Add(AFunction); + + if ConnectedFunctions.Count > 0 then + SimConnect; + end; +end; + +procedure TFSXLEDFunctionProvider.Disconnect(AFunction: TCustomFSXFunction); +begin + ConnectedFunctions.Remove(AFunction); + + if ConnectedFunctions.Count = 0 then + SimDisconnect; +end; + + +{ TCustomFSXFunction } +constructor TCustomFSXFunction.Create(AProvider: TFSXLEDFunctionProvider); +begin + inherited Create; + + FProvider := AProvider; +end; + + +procedure TCustomFSXFunction.Attach(AObserver: IObserver); +begin + if Observers.Count = 0 then + Provider.Connect(Self); + + inherited Attach(AObserver); +end; + + +procedure TCustomFSXFunction.Detach(AObserver: IObserver); +begin + if Assigned(Provider) and (Observers.Count > 0) then + begin + inherited Detach(AObserver); + + if Observers.Count = 0 then + Provider.Disconnect(Self); + end else + inherited Detach(AObserver); +end; + + +function TCustomFSXFunction.GetCategoryName: string; +begin + Result := FSXCategory; +end; + + +procedure TCustomFSXFunction.SimConnected; +begin +end; + + +procedure TCustomFSXFunction.SimDisconnected; +begin +end; + + +{ TFSXGearFunction } +procedure TFSXGearFunction.RegisterStates; +begin + FRetractedState := RegisterState(TLEDState.Create(FSXStateUIDGearRetracted, + FSXStateDisplayNameGearRetracted, + TLEDColorPool.GetColor(cpeStaticRed))); + + FBetweenState := RegisterState(TLEDState.Create(FSXStateUIDGearBetween, + FSXStateDisplayNameGearBetween, + TLEDColorPool.GetColor(cpeStaticAmber))); + + FExtendedState := RegisterState(TLEDState.Create(FSXStateUIDGearExtended, + FSXStateDisplayNameGearExtended, + TLEDColorPool.GetColor(cpeStaticGreen))); + + FSpeedExceededState := RegisterState(TLEDState.Create(FSXStateUIDGearSpeedExceeded, + FSXStateDisplayNameGearSpeedExceeded, + TLEDColorPool.GetColor(cpeFlashingAmberNormal))); + + FDamageBySpeedState := RegisterState(TLEDState.Create(FSXStateUIDGearDamageBySpeed, + FSXStateDisplayNameGearDamageBySpeed, + TLEDColorPool.GetColor(cpeFlashingRedFast))); +end; + + +function TFSXGearFunction.GetDisplayName: string; +begin + Result := FSXFunctionDisplayNameGear; +end; + + +function TFSXGearFunction.GetUID: string; +begin + Result := FSXFunctionUIDGear; +end; + + +function TFSXGearFunction.GetCurrentState: ILEDState; +begin + // TODO TFSXGearFunction.GetCurrentState +end; + + +initialization + TLEDFunctionRegistry.Register(TFSXLEDFunctionProvider.Create); + +end. diff --git a/G940LEDControl/Units/FSXResources.pas b/G940LEDControl/Units/FSXResources.pas new file mode 100644 index 0000000..c256474 --- /dev/null +++ b/G940LEDControl/Units/FSXResources.pas @@ -0,0 +1,28 @@ +unit FSXResources; + +interface +const + FSXProviderUID = 'fsx'; + + FSXCategory = 'Flight Simulator X'; + + FSXFunctionUIDGear = 'gear'; + FSXFunctionDisplayNameGear = 'Gear'; + + FSXStateUIDGearRetracted = 'retracted'; + FSXStateUIDGearBetween = 'between'; + FSXStateUIDGearExtended = 'extended'; + FSXStateUIDGearSpeedExceeded = 'speedExceeded'; + FSXStateUIDGearDamageBySpeed = 'damageBySpeed'; + + FSXStateDisplayNameGearRetracted = 'Retracted'; + FSXStateDisplayNameGearBetween = 'Extending / retracting'; + FSXStateDisplayNameGearExtended = 'Extended'; + FSXStateDisplayNameGearSpeedExceeded = 'Speed exceeded'; + FSXStateDisplayNameGearDamageBySpeed = 'Damage by speed'; + + + +implementation + +end. diff --git a/G940LEDControl/Units/FSXSimConnectClient.pas b/G940LEDControl/Units/FSXSimConnectClient.pas new file mode 100644 index 0000000..563492b --- /dev/null +++ b/G940LEDControl/Units/FSXSimConnectClient.pas @@ -0,0 +1,14 @@ +unit FSXSimConnectClient; + +interface +uses + OtlTaskControl; + +type + TFSXSimConnectClient = class(TOmniWorker) + + end; + +implementation + +end. diff --git a/G940LEDControl/Units/LEDColorPool.pas b/G940LEDControl/Units/LEDColorPool.pas index 720c3be..8b91516 100644 --- a/G940LEDControl/Units/LEDColorPool.pas +++ b/G940LEDControl/Units/LEDColorPool.pas @@ -26,7 +26,8 @@ type function DoGetColor(AEntry: TLEDColorPoolEntry): ILEDColor; public - class function GetColor(AEntry: TLEDColorPoolEntry): ILEDColor; + class function GetColor(AEntry: TLEDColorPoolEntry): ILEDColor; overload; + class function GetColor(AColor: TLEDColor): ILEDColor; overload; end; @@ -49,6 +50,19 @@ begin end; +class function TLEDColorPool.GetColor(AColor: TLEDColor): ILEDColor; +begin + Result := nil; + + case AColor of + lcOff: Result := GetColor(cpeStaticOff); + lcGreen: Result := GetColor(cpeStaticGreen); + lcAmber: Result := GetColor(cpeStaticAmber); + lcRed: Result := GetColor(cpeStaticRed); + end; +end; + + class function TLEDColorPool.Instance: TLEDColorPool; begin if not Assigned(LEDColorPoolInstance) then diff --git a/G940LEDControl/Units/LEDFunction.pas b/G940LEDControl/Units/LEDFunction.pas index 9dab8b5..d810e22 100644 --- a/G940LEDControl/Units/LEDFunction.pas +++ b/G940LEDControl/Units/LEDFunction.pas @@ -14,7 +14,8 @@ type private FFunctions: TInterfaceList; protected - procedure RegisterFunction(AFunction: ILEDFunction); + procedure RegisterFunctions; virtual; abstract; + function RegisterFunction(AFunction: ILEDFunction): ILEDFunction; virtual; protected { ILEDFunctionProvider } function GetUID: string; virtual; abstract; @@ -30,24 +31,20 @@ type TCustomLEDFunction = class(TInterfacedObject, IObservable, ILEDFunction) private FObservers: TInterfaceList; - FStates: TInterfaceList; protected -// procedure SetCurrentState(AState: ILEDState); virtual; - procedure NotifyObservers; virtual; property Observers: TInterfaceList read FObservers; protected { IObservable } - procedure Attach(AObserver: IObserver); - procedure Detach(AObserver: IObserver); + procedure Attach(AObserver: IObserver); virtual; + procedure Detach(AObserver: IObserver); virtual; { ILEDFunction } function GetCategoryName: string; virtual; abstract; function GetDisplayName: string; virtual; abstract; function GetUID: string; virtual; abstract; - function GetEnumerator: ILEDStateEnumerator; virtual; function GetCurrentState: ILEDState; virtual; abstract; public constructor Create; @@ -55,6 +52,22 @@ type end; + TCustomMultiStateLEDFunction = class(TCustomLEDFunction, ILEDMultiStateFunction) + private + FStates: TInterfaceList; + protected +// procedure SetCurrentState(AState: ILEDState); virtual; + procedure RegisterStates; virtual; abstract; + function RegisterState(AState: ILEDState): ILEDState; virtual; + protected + { ILEDMultiStateFunction } + function GetEnumerator: ILEDStateEnumerator; virtual; + public + constructor Create; + destructor Destroy; override; + end; + + TLEDFunctionEnumerator = class(TInterfacedObject, ILEDFunctionEnumerator) private FList: TInterfaceList; @@ -93,13 +106,11 @@ begin inherited Create; FObservers := TInterfaceList.Create; - FStates := TInterfaceList.Create; end; destructor TCustomLEDFunction.Destroy; begin - FreeAndNil(FStates); FreeAndNil(FObservers); inherited Destroy; @@ -118,13 +129,38 @@ begin end; -function TCustomLEDFunction.GetEnumerator: ILEDStateEnumerator; +{ TCustomMultiStateLEDFunction } +constructor TCustomMultiStateLEDFunction.Create; +begin + inherited Create; + + FStates := TInterfaceList.Create; + RegisterStates; +end; + + +destructor TCustomMultiStateLEDFunction.Destroy; +begin + FreeAndNil(FStates); + + inherited; +end; + + +function TCustomMultiStateLEDFunction.RegisterState(AState: ILEDState): ILEDState; +begin + Result := AState as ILEDState; + FStates.Add(Result); +end; + + +function TCustomMultiStateLEDFunction.GetEnumerator: ILEDStateEnumerator; begin Result := TLEDStateEnumerator.Create(FStates); end; -//procedure TCustomLEDFunction.SetCurrentState(AState: ILEDState); +//procedure TCustomMultiStateLEDFunction.SetCurrentState(AState: ILEDState); //begin // FCurrentState := AState; // NotifyObservers; @@ -147,6 +183,7 @@ begin inherited Create; FFunctions := TInterfaceList.Create; + RegisterFunctions; end; @@ -174,9 +211,10 @@ begin end; -procedure TCustomLEDFunctionProvider.RegisterFunction(AFunction: ILEDFunction); +function TCustomLEDFunctionProvider.RegisterFunction(AFunction: ILEDFunction): ILEDFunction; begin - FFunctions.Add(AFunction as ILEDFunction); + Result := AFunction as ILEDFunction; + FFunctions.Add(Result); end; diff --git a/G940LEDControl/Units/StaticLEDFunction.pas b/G940LEDControl/Units/StaticLEDFunction.pas index bf4eeeb..02864ec 100644 --- a/G940LEDControl/Units/StaticLEDFunction.pas +++ b/G940LEDControl/Units/StaticLEDFunction.pas @@ -10,15 +10,16 @@ uses type TStaticLEDFunctionProvider = class(TCustomLEDFunctionProvider) protected + procedure RegisterFunctions; override; + function GetUID: string; override; - public - constructor Create; end; TStaticLEDFunction = class(TCustomLEDFunction) private FColor: TLEDColor; + FState: ILEDState; protected function GetCategoryName: string; override; function GetDisplayName: string; override; @@ -30,42 +31,20 @@ type end; -const - StaticProviderUID = 'static'; - StaticFunctionUID: array[TLEDColor] of string = - ( - 'off', - 'green', - 'amber', - 'red' - ); - - implementation uses - LEDFunctionRegistry; - - -const - CategoryStatic = 'Static'; - FunctionDisplayName: array[TLEDColor] of string = - ( - 'Off', - 'Green', - 'Amber', - 'Red' - ); - + LEDColorPool, + LEDFunctionRegistry, + LEDState, + StaticResources; { TStaticLEDFunctionProvider } -constructor TStaticLEDFunctionProvider.Create; +procedure TStaticLEDFunctionProvider.RegisterFunctions; var color: TLEDColor; begin - inherited Create; - for color := Low(TLEDColor) to High(TLEDColor) do RegisterFunction(TStaticLEDFunction.Create(color)); end; @@ -88,13 +67,13 @@ end; function TStaticLEDFunction.GetCategoryName: string; begin - Result := CategoryStatic; + Result := StaticCategory; end; function TStaticLEDFunction.GetDisplayName: string; begin - Result := FunctionDisplayName[FColor]; + Result := StaticFunctionDisplayName[FColor]; end; @@ -106,7 +85,10 @@ end; function TStaticLEDFunction.GetCurrentState: ILEDState; begin + if not Assigned(FState) then + FState := TLEDState.Create('', '', TLEDColorPool.GetColor(FColor)); + Result := FState; end; diff --git a/G940LEDControl/Units/StaticResources.pas b/G940LEDControl/Units/StaticResources.pas new file mode 100644 index 0000000..b3d2588 --- /dev/null +++ b/G940LEDControl/Units/StaticResources.pas @@ -0,0 +1,31 @@ +unit StaticResources; + +interface +uses + LEDColorIntf; + + +const + StaticProviderUID = 'static'; + StaticFunctionUID: array[TLEDColor] of string = + ( + 'off', + 'green', + 'amber', + 'red' + ); + + + StaticCategory = 'Static'; + StaticFunctionDisplayName: array[TLEDColor] of string = + ( + 'Off', + 'Green', + 'Amber', + 'Red' + ); + + +implementation + +end. From 59fc11081486e4ec7c32a58918e5ba5a363aced3 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Sun, 17 Feb 2013 10:39:25 +0000 Subject: [PATCH 07/16] Added: start of colour editor in button function form --- G940LEDControl/Forms/ButtonFunctionFrm.dfm | 31 +++- G940LEDControl/Forms/ButtonFunctionFrm.pas | 181 +++++++++++++++++++-- G940LEDControl/G940LEDControl.dpr | 3 +- G940LEDControl/G940LEDControl.dproj | 1 + G940LEDControl/Units/ColourEditor.pas | 141 ++++++++++++++++ 5 files changed, 335 insertions(+), 22 deletions(-) create mode 100644 G940LEDControl/Units/ColourEditor.pas diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.dfm b/G940LEDControl/Forms/ButtonFunctionFrm.dfm index a4a0057..6e8825f 100644 --- a/G940LEDControl/Forms/ButtonFunctionFrm.dfm +++ b/G940LEDControl/Forms/ButtonFunctionFrm.dfm @@ -72,10 +72,13 @@ object ButtonFunctionForm: TButtonFunctionForm Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoShowSortGlyphs, hoVisible] TabOrder = 1 TreeOptions.AutoOptions = [toAutoDropExpand, toAutoScrollOnExpand, toAutoSort, toAutoTristateTracking, toAutoDeleteMovedNodes] + TreeOptions.MiscOptions = [toAcceptOLEDrop, toFullRepaintOnResize, toInitOnSave, toWheelPanning, toEditOnClick] TreeOptions.PaintOptions = [toShowButtons, toShowDropmark, toShowTreeLines, toThemeAware, toUseBlendedImages] TreeOptions.SelectionOptions = [toFullRowSelect] + OnFocusChanged = vstFunctionsFocusChanged OnGetText = vstFunctionsGetText OnPaintText = vstFunctionsPaintText + ExplicitTop = 5 Columns = < item Position = 0 @@ -98,9 +101,9 @@ object ButtonFunctionForm: TButtonFunctionForm TabOrder = 2 object vstStates: TVirtualStringTree Left = 0 - Top = 113 + Top = 81 Width = 411 - Height = 239 + Height = 271 Align = alClient Header.AutoSizeIndex = 0 Header.Font.Charset = DEFAULT_CHARSET @@ -108,8 +111,16 @@ object ButtonFunctionForm: TButtonFunctionForm Header.Font.Height = -11 Header.Font.Name = 'Tahoma' Header.Font.Style = [] + Header.MainColumn = 1 Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoShowSortGlyphs, hoVisible] TabOrder = 0 + TreeOptions.MiscOptions = [toAcceptOLEDrop, toEditable, toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning] + TreeOptions.PaintOptions = [toShowButtons, toShowDropmark, toThemeAware, toUseBlendedImages] + TreeOptions.SelectionOptions = [toExtendedFocus, toFullRowSelect] + OnChange = vstStatesChange + OnCreateEditor = vstStatesCreateEditor + OnEditing = vstStatesEditing + OnGetText = vstStatesGetText Columns = < item Position = 0 @@ -119,20 +130,20 @@ object ButtonFunctionForm: TButtonFunctionForm item Position = 1 Width = 200 - WideText = 'Color' + WideText = 'Colour' end> end object pnlName: TPanel Left = 0 Top = 0 Width = 411 - Height = 113 + Height = 81 Align = alTop BevelOuter = bvNone TabOrder = 1 DesignSize = ( 411 - 113) + 81) object lblFunctionName: TLabel Left = 0 Top = 19 @@ -157,11 +168,17 @@ object ButtonFunctionForm: TButtonFunctionForm Anchors = [akLeft, akTop, akRight] AutoSize = False Caption = 'runtime: category' + Font.Charset = DEFAULT_CHARSET + Font.Color = clGrayText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False ExplicitWidth = 401 end object lblHasStates: TLabel Left = 0 - Top = 74 + Top = 47 Width = 401 Height = 31 AutoSize = False @@ -173,7 +190,7 @@ object ButtonFunctionForm: TButtonFunctionForm end object lblNoStates: TLabel Left = 0 - Top = 55 + Top = 47 Width = 195 Height = 13 Caption = 'This function has no configurable states.' diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.pas b/G940LEDControl/Forms/ButtonFunctionFrm.pas index 6c1c5b4..cb1b237 100644 --- a/G940LEDControl/Forms/ButtonFunctionFrm.pas +++ b/G940LEDControl/Forms/ButtonFunctionFrm.pas @@ -8,12 +8,18 @@ uses Vcl.Forms, Vcl.Graphics, Vcl.StdCtrls, + Winapi.Messages, VirtualTrees, + LEDFunctionIntf, Profile; +const + WM_STARTEDITING = WM_USER + 1; + + type TButtonFunctionForm = class(TForm) pnlButtons: TPanel; @@ -32,11 +38,22 @@ type procedure FormDestroy(Sender: TObject); procedure vstFunctionsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); procedure vstFunctionsPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); + procedure vstFunctionsFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); + procedure vstStatesGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); + procedure vstStatesChange(Sender: TBaseVirtualTree; Node: PVirtualNode); + procedure vstStatesCreateEditor(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink); + procedure vstStatesEditing(Sender: TBaseVirtualTree; + Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean); private FButtonIndex: Integer; FProfile: TProfile; + protected + procedure WMStartEditing(var Msg: TMessage); message WM_STARTEDITING; protected procedure LoadFunctions; + procedure SetFunction(AProvider: ILEDFunctionProvider; AFunction: ILEDFunction); + + procedure LoadStates(AFunction: ILEDMultiStateFunction); property ButtonIndex: Integer read FButtonIndex write FButtonIndex; property Profile: TProfile read FProfile write FProfile; @@ -47,22 +64,36 @@ type implementation uses - System.SysUtils, Generics.Collections, + System.SysUtils, + Winapi.Windows, - LEDFunctionIntf, - LEDFunctionRegistry; + ColourEditor, + LEDFunctionRegistry, + LEDStateIntf; type - TNodeType = (ntCategory, ntFunction); - TNodeData = record - NodeType: TNodeType; + TFunctionNodeType = (ntCategory, ntFunction); + TFunctionNodeData = record + NodeType: TFunctionNodeType; Provider: ILEDFunctionProvider; LEDFunction: ILEDFunction; end; - PNodeData = ^TNodeData; + PFunctionNodeData = ^TFunctionNodeData; + + + TStateNodeData = record + State: ILEDState; + end; + + PStateNodeData = ^TStateNodeData; + + +const + ColumnState = 0; + ColumnColour = 1; {$R *.dfm} @@ -84,9 +115,8 @@ end; procedure TButtonFunctionForm.FormCreate(Sender: TObject); begin - vstFunctions.NodeDataSize := SizeOf(TNodeData); - - lblNoStates.Top := lblHasStates.Top; + vstFunctions.NodeDataSize := SizeOf(TFunctionNodeData); + vstStates.NodeDataSize := SizeOf(TStateNodeData); lblCategoryName.Caption := ''; lblFunctionName.Caption := ''; @@ -108,7 +138,7 @@ var function GetCategoryNode(AProvider: ILEDFunctionProvider; AFunction: ILEDFunction): PVirtualNode; var category: string; - nodeData: PNodeData; + nodeData: PFunctionNodeData; begin category := AFunction.GetCategoryName; @@ -130,7 +160,7 @@ var var node: PVirtualNode; - nodeData: PNodeData; + nodeData: PFunctionNodeData; provider: ILEDFunctionProvider; ledFunction: ILEDFunction; @@ -162,10 +192,88 @@ begin end; +procedure TButtonFunctionForm.SetFunction(AProvider: ILEDFunctionProvider; AFunction: ILEDFunction); +var + multiStateFunction: ILEDMultiStateFunction; + +begin + lblCategoryName.Caption := AFunction.GetCategoryName; + lblFunctionName.Caption := AFunction.GetDisplayName; + + if Supports(AFunction, ILEDMultiStateFunction, multiStateFunction) then + begin + lblNoStates.Visible := False; + lblHasStates.Visible := True; + + LoadStates(multiStateFunction); + vstStates.Visible := True; + end else + begin + lblNoStates.Visible := True; + lblHasStates.Visible := False; + + vstStates.Visible := False; + vstStates.Clear; + end; +end; + + +procedure TButtonFunctionForm.LoadStates(AFunction: ILEDMultiStateFunction); +var + node: PVirtualNode; + nodeData: PStateNodeData; + state: ILEDState; + +begin + vstStates.BeginUpdate; + try + vstStates.Clear; + + for state in AFunction do + begin + node := vstStates.AddChild(nil); + nodeData := vstStates.GetNodeData(node); + nodeData^.State := state; + end; + finally + vstStates.EndUpdate; + end; +end; + + +procedure TButtonFunctionForm.vstFunctionsFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); +var + nodeData: PFunctionNodeData; + functionNode: PVirtualNode; + +begin + if Assigned(Node) then + begin + nodeData := Sender.GetNodeData(Node); + + case nodeData^.NodeType of + ntCategory: + begin + { Select first child (function) node instead } + functionNode := Sender.GetFirstChild(Node); + if not Assigned(functionNode) then + exit; + + Sender.FocusedNode := functionNode; + Sender.Selected[functionNode] := True; + end; + + ntFunction: + SetFunction(nodeData^.Provider, nodeData^.LEDFunction); + end; + end; +end; + + procedure TButtonFunctionForm.vstFunctionsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); var - nodeData: PNodeData; + nodeData: PFunctionNodeData; begin nodeData := Sender.GetNodeData(Node); @@ -180,7 +288,7 @@ end; procedure TButtonFunctionForm.vstFunctionsPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); var - nodeData: PNodeData; + nodeData: PFunctionNodeData; begin nodeData := Sender.GetNodeData(Node); @@ -191,4 +299,49 @@ begin TargetCanvas.Font.Style := []; end; + +procedure TButtonFunctionForm.vstStatesGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; + TextType: TVSTTextType; var CellText: string); +var + nodeData: PStateNodeData; + +begin + nodeData := Sender.GetNodeData(Node); + + case Column of + ColumnState: CellText := nodeData^.State.GetDisplayName; + ColumnColour: CellText := 'Red'; + end; +end; + + +procedure TButtonFunctionForm.vstStatesChange(Sender: TBaseVirtualTree; Node: PVirtualNode); +begin + if Assigned(Node) and not (tsIncrementalSearching in Sender.TreeStates) then + PostMessage(Self.Handle, WM_STARTEDITING, WPARAM(Node), 0); +end; + + +procedure TButtonFunctionForm.vstStatesCreateEditor(Sender: TBaseVirtualTree; Node: PVirtualNode; + Column: TColumnIndex; out EditLink: IVTEditLink); +begin + EditLink := TVTColourEditor.Create; +end; + + +procedure TButtonFunctionForm.vstStatesEditing(Sender: TBaseVirtualTree; + Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean); +begin + Allowed := True; +end; + +procedure TButtonFunctionForm.WMStartEditing(var Msg: TMessage); +var + node: PVirtualNode; + +begin + node := Pointer(Msg.WParam); + vstStates.EditNode(Node, 1); +end; + end. diff --git a/G940LEDControl/G940LEDControl.dpr b/G940LEDControl/G940LEDControl.dpr index da5f91d..0bba2da 100644 --- a/G940LEDControl/G940LEDControl.dpr +++ b/G940LEDControl/G940LEDControl.dpr @@ -29,7 +29,8 @@ uses FSXLEDFunction in 'Units\FSXLEDFunction.pas', StaticResources in 'Units\StaticResources.pas', FSXResources in 'Units\FSXResources.pas', - FSXSimConnectClient in 'Units\FSXSimConnectClient.pas'; + FSXSimConnectClient in 'Units\FSXSimConnectClient.pas', + ColourEditor in 'Units\ColourEditor.pas'; {$R *.res} diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj index 5638f7d..4c8b157 100644 --- a/G940LEDControl/G940LEDControl.dproj +++ b/G940LEDControl/G940LEDControl.dproj @@ -195,6 +195,7 @@ + Cfg_2 Base diff --git a/G940LEDControl/Units/ColourEditor.pas b/G940LEDControl/Units/ColourEditor.pas new file mode 100644 index 0000000..2daf26d --- /dev/null +++ b/G940LEDControl/Units/ColourEditor.pas @@ -0,0 +1,141 @@ +unit ColourEditor; + +interface +uses + System.Types, + System.Classes, + Vcl.StdCtrls, + Winapi.Messages, + + VirtualTrees; + + +type + TVTColourEditor = class(TInterfacedObject, IVTEditLink) + private + FEdit: TComboBox; + FTree: TBaseVirtualTree; + FColumn: TColumnIndex; + protected + procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); + protected + { IVTEditLink } + function BeginEdit: Boolean; stdcall; + function CancelEdit: Boolean; stdcall; + function EndEdit: Boolean; stdcall; + + function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall; + procedure ProcessMessage(var Message: TMessage); stdcall; + + function GetBounds: TRect; stdcall; + procedure SetBounds(R: TRect); stdcall; + public + destructor Destroy; override; + end; + +implementation +uses + System.SysUtils, + Winapi.Windows; + + +{ TVTColourEditor } +destructor TVTColourEditor.Destroy; +begin + FreeAndNil(FEdit); + + inherited; +end; + + +function TVTColourEditor.BeginEdit: Boolean; +begin + Result := True; + FEdit.Show; + FEdit.SetFocus; +end; + + +function TVTColourEditor.CancelEdit: Boolean; +begin + Result := True; + FEdit.Hide; +end; + + +function TVTColourEditor.EndEdit: Boolean; +begin + Result := True; + // TODO update node data +end; + + +function TVTColourEditor.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; +begin + Result := True; + + FTree := Tree; +// FNode := Node; + FColumn := Column; + + FreeAndNil(FEdit); + + FEdit := TComboBox.Create(nil); + FEdit.Visible := False; + FEdit.Parent := Tree; + +// FEdit.Text := Data.Value; +// FEdit.Items.Add(); + + FEdit.OnKeyDown := EditKeyDown; +end; + + +procedure TVTColourEditor.ProcessMessage(var Message: TMessage); +begin + FEdit.WindowProc(Message); +end; + + +function TVTColourEditor.GetBounds: TRect; +begin + Result := FEdit.BoundsRect; +end; + + +procedure TVTColourEditor.SetBounds(R: TRect); +var + dummy: Integer; + +begin + (FTree as TVirtualStringTree).Header.Columns.GetColumnBounds(FColumn, dummy, R.Right); + FEdit.BoundsRect := R; +end; + + +procedure TVTColourEditor.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); +begin + case Key of + VK_ESCAPE: + begin + FTree.CancelEditNode; + Key := 0; + end; + + VK_RETURN: + begin + FTree.EndEditNode; + Key := 0; + end; + + VK_UP, + VK_DOWN: + if (Shift = []) and (not FEdit.DroppedDown) then + begin + PostMessage(FTree.Handle, WM_KEYDOWN, Key, 0); + Key := 0; + end; + end; +end; + +end. From d665f2d979d48878a782ee2128792dc169466d6e Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Sun, 17 Feb 2013 19:06:27 +0000 Subject: [PATCH 08/16] Refactored: separated definition and functionality by introducing the Function Worker Changed: button configuration form uses runtime components for the states intead of a VirtualTreeView --- G940LEDControl/Forms/ButtonFunctionFrm.dfm | 175 +++++++--- G940LEDControl/Forms/ButtonFunctionFrm.pas | 322 ++++++++++++------ G940LEDControl/Forms/MainFrm.dfm | 12 +- G940LEDControl/Forms/MainFrm.pas | 5 +- G940LEDControl/G940LEDControl.dpr | 7 +- G940LEDControl/G940LEDControl.dproj | 11 +- G940LEDControl/Units/ColourEditor.pas | 141 -------- G940LEDControl/Units/ConfigConversion.pas | 2 + G940LEDControl/Units/FSXLEDFunction.pas | 231 ------------- .../Units/FSXLEDFunctionProvider.pas | 251 ++++++++++++++ G940LEDControl/Units/FSXResources.pas | 2 +- G940LEDControl/Units/FSXSimConnectClient.pas | 97 +++++- G940LEDControl/Units/FSXSimConnectIntf.pas | 37 ++ G940LEDControl/Units/LEDColorIntf.pas | 8 +- G940LEDControl/Units/LEDColorPool.pas | 62 +--- G940LEDControl/Units/LEDFunction.pas | 154 ++++++--- G940LEDControl/Units/LEDFunctionIntf.pas | 18 +- G940LEDControl/Units/LEDResources.pas | 61 ++++ G940LEDControl/Units/LEDState.pas | 66 +++- G940LEDControl/Units/LEDStateIntf.pas | 16 +- G940LEDControl/Units/Profile.pas | 96 +++++- G940LEDControl/Units/StaticLEDFunction.pas | 38 ++- G940LEDControl/Units/StaticResources.pas | 4 +- 23 files changed, 1145 insertions(+), 671 deletions(-) delete mode 100644 G940LEDControl/Units/ColourEditor.pas delete mode 100644 G940LEDControl/Units/FSXLEDFunction.pas create mode 100644 G940LEDControl/Units/FSXLEDFunctionProvider.pas create mode 100644 G940LEDControl/Units/FSXSimConnectIntf.pas create mode 100644 G940LEDControl/Units/LEDResources.pas diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.dfm b/G940LEDControl/Forms/ButtonFunctionFrm.dfm index 6e8825f..5b4ac44 100644 --- a/G940LEDControl/Forms/ButtonFunctionFrm.dfm +++ b/G940LEDControl/Forms/ButtonFunctionFrm.dfm @@ -1,10 +1,11 @@ object ButtonFunctionForm: TButtonFunctionForm Left = 0 Top = 0 + ActiveControl = vstFunctions BorderIcons = [biSystemMenu] BorderStyle = bsDialog Caption = 'Configure button' - ClientHeight = 401 + ClientHeight = 484 ClientWidth = 692 Color = clBtnFace Font.Charset = DEFAULT_CHARSET @@ -18,31 +19,53 @@ object ButtonFunctionForm: TButtonFunctionForm OnDestroy = FormDestroy PixelsPerInch = 96 TextHeight = 13 - object pnlButtons: TPanel + object bvlHeader: TBevel Left = 0 - Top = 360 + Top = 50 Width = 692 - Height = 41 + Height = 2 + Align = alTop + Shape = bsTopLine + ExplicitTop = 41 + end + object pnlButtons: TPanel + AlignWithMargins = True + Left = 0 + Top = 441 + Width = 692 + Height = 43 + Margins.Left = 0 + Margins.Top = 8 + Margins.Right = 0 + Margins.Bottom = 0 Align = alBottom BevelOuter = bvNone - TabOrder = 0 + TabOrder = 2 DesignSize = ( 692 - 41) + 43) + object bvlFooter: TBevel + Left = 0 + Top = 0 + Width = 692 + Height = 8 + Align = alTop + Shape = bsTopLine + end object btnOK: TButton Left = 528 - Top = 8 + Top = 10 Width = 75 Height = 25 Anchors = [akTop, akRight] Caption = 'OK' Default = True - ModalResult = 1 TabOrder = 0 + OnClick = btnOKClick end object btnCancel: TButton Left = 609 - Top = 8 + Top = 10 Width = 75 Height = 25 Anchors = [akTop, akRight] @@ -55,9 +78,9 @@ object ButtonFunctionForm: TButtonFunctionForm object vstFunctions: TVirtualStringTree AlignWithMargins = True Left = 8 - Top = 8 + Top = 60 Width = 257 - Height = 352 + Height = 373 Margins.Left = 8 Margins.Top = 8 Margins.Right = 0 @@ -70,7 +93,7 @@ object ButtonFunctionForm: TButtonFunctionForm Header.Font.Name = 'Tahoma' Header.Font.Style = [] Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoShowSortGlyphs, hoVisible] - TabOrder = 1 + TabOrder = 0 TreeOptions.AutoOptions = [toAutoDropExpand, toAutoScrollOnExpand, toAutoSort, toAutoTristateTracking, toAutoDeleteMovedNodes] TreeOptions.MiscOptions = [toAcceptOLEDrop, toFullRepaintOnResize, toInitOnSave, toWheelPanning, toEditOnClick] TreeOptions.PaintOptions = [toShowButtons, toShowDropmark, toShowTreeLines, toThemeAware, toUseBlendedImages] @@ -78,7 +101,7 @@ object ButtonFunctionForm: TButtonFunctionForm OnFocusChanged = vstFunctionsFocusChanged OnGetText = vstFunctionsGetText OnPaintText = vstFunctionsPaintText - ExplicitTop = 5 + ExplicitHeight = 383 Columns = < item Position = 0 @@ -89,61 +112,28 @@ object ButtonFunctionForm: TButtonFunctionForm object pnlFunction: TPanel AlignWithMargins = True Left = 273 - Top = 8 + Top = 60 Width = 411 - Height = 352 + Height = 373 Margins.Left = 8 Margins.Top = 8 Margins.Right = 8 Margins.Bottom = 0 Align = alClient BevelOuter = bvNone - TabOrder = 2 - object vstStates: TVirtualStringTree - Left = 0 - Top = 81 - Width = 411 - Height = 271 - Align = alClient - Header.AutoSizeIndex = 0 - Header.Font.Charset = DEFAULT_CHARSET - Header.Font.Color = clWindowText - Header.Font.Height = -11 - Header.Font.Name = 'Tahoma' - Header.Font.Style = [] - Header.MainColumn = 1 - Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoShowSortGlyphs, hoVisible] - TabOrder = 0 - TreeOptions.MiscOptions = [toAcceptOLEDrop, toEditable, toFullRepaintOnResize, toInitOnSave, toToggleOnDblClick, toWheelPanning] - TreeOptions.PaintOptions = [toShowButtons, toShowDropmark, toThemeAware, toUseBlendedImages] - TreeOptions.SelectionOptions = [toExtendedFocus, toFullRowSelect] - OnChange = vstStatesChange - OnCreateEditor = vstStatesCreateEditor - OnEditing = vstStatesEditing - OnGetText = vstStatesGetText - Columns = < - item - Position = 0 - Width = 207 - WideText = 'State' - end - item - Position = 1 - Width = 200 - WideText = 'Colour' - end> - end + TabOrder = 1 + ExplicitHeight = 383 object pnlName: TPanel Left = 0 Top = 0 Width = 411 - Height = 81 + Height = 97 Align = alTop BevelOuter = bvNone - TabOrder = 1 + TabOrder = 0 DesignSize = ( 411 - 81) + 97) object lblFunctionName: TLabel Left = 0 Top = 19 @@ -184,8 +174,7 @@ object ButtonFunctionForm: TButtonFunctionForm AutoSize = False Caption = 'This function provides the following states. Each state can be c' + - 'ustomized by clicking on the state and changing the setting in t' + - 'he Color column.' + 'ustomized by changing the color below.' WordWrap = True end object lblNoStates: TLabel @@ -197,5 +186,81 @@ object ButtonFunctionForm: TButtonFunctionForm Visible = False end end + object sbStates: TScrollBox + Left = 0 + Top = 97 + Width = 411 + Height = 276 + Align = alClient + BorderStyle = bsNone + TabOrder = 1 + ExplicitHeight = 286 + end + end + object pnlHeader: TPanel + Left = 0 + Top = 0 + Width = 692 + Height = 50 + Align = alTop + BevelOuter = bvNone + Color = clWindow + ParentBackground = False + TabOrder = 3 + DesignSize = ( + 692 + 50) + object lblButton: TLabel + Left = 8 + Top = 13 + Width = 24 + Height = 23 + Caption = 'P1' + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -19 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + end + object lblCurrentAssignment: TLabel + Left = 586 + Top = 8 + Width = 98 + Height = 13 + Alignment = taRightJustify + Anchors = [akTop, akRight] + Caption = 'Current assignment:' + end + object lblCurrentFunction: TLabel + Left = 587 + Top = 27 + Width = 97 + Height = 13 + Alignment = taRightJustify + Anchors = [akTop, akRight] + Caption = 'runtime: function' + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + end + object lblCurrentCategory: TLabel + Left = 478 + Top = 27 + Width = 86 + Height = 13 + Alignment = taRightJustify + Anchors = [akTop, akRight] + Caption = 'runtime: category' + Font.Charset = DEFAULT_CHARSET + Font.Color = clGrayText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + ParentFont = False + end end end diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.pas b/G940LEDControl/Forms/ButtonFunctionFrm.pas index cb1b237..60072dc 100644 --- a/G940LEDControl/Forms/ButtonFunctionFrm.pas +++ b/G940LEDControl/Forms/ButtonFunctionFrm.pas @@ -2,6 +2,7 @@ unit ButtonFunctionFrm; interface uses + Generics.Collections, System.Classes, Vcl.Controls, Vcl.ExtCtrls, @@ -12,65 +13,95 @@ uses VirtualTrees, + LEDColorIntf, LEDFunctionIntf, + LEDStateIntf, Profile; -const - WM_STARTEDITING = WM_USER + 1; - - type + TStateControlInfo = class; + TStateControlInfoList = TObjectList; + + TButtonFunctionForm = class(TForm) pnlButtons: TPanel; btnOK: TButton; btnCancel: TButton; vstFunctions: TVirtualStringTree; - vstStates: TVirtualStringTree; pnlFunction: TPanel; pnlName: TPanel; lblFunctionName: TLabel; lblCategoryName: TLabel; lblHasStates: TLabel; lblNoStates: TLabel; + sbStates: TScrollBox; + pnlHeader: TPanel; + bvlHeader: TBevel; + lblButton: TLabel; + lblCurrentAssignment: TLabel; + lblCurrentFunction: TLabel; + lblCurrentCategory: TLabel; + bvlFooter: TBevel; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure vstFunctionsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); procedure vstFunctionsPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); procedure vstFunctionsFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); - procedure vstStatesGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); - procedure vstStatesChange(Sender: TBaseVirtualTree; Node: PVirtualNode); - procedure vstStatesCreateEditor(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; out EditLink: IVTEditLink); - procedure vstStatesEditing(Sender: TBaseVirtualTree; - Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean); + procedure btnOKClick(Sender: TObject); private - FButtonIndex: Integer; FProfile: TProfile; + FButtonIndex: Integer; + FButton: TProfileButton; + FCurrentProvider: ILEDFunctionProvider; + FCurrentFunction: ILEDFunction; + FSelectedProvider: ILEDFunctionProvider; + FSelectedFunction: ILEDFunction; + FStateControls: TStateControlInfoList; protected - procedure WMStartEditing(var Msg: TMessage); message WM_STARTEDITING; - protected + procedure Initialize(AProfile: TProfile; AButtonIndex: Integer); + procedure LoadFunctions; procedure SetFunction(AProvider: ILEDFunctionProvider; AFunction: ILEDFunction); - procedure LoadStates(AFunction: ILEDMultiStateFunction); + procedure LoadStates(AProvider: ILEDFunctionProvider; AFunction: ILEDMultiStateFunction); - property ButtonIndex: Integer read FButtonIndex write FButtonIndex; - property Profile: TProfile read FProfile write FProfile; + property Button: TProfileButton read FButton; + property CurrentProvider: ILEDFunctionProvider read FCurrentProvider; + property CurrentFunction: ILEDFunction read FCurrentFunction; + property SelectedProvider: ILEDFunctionProvider read FSelectedProvider; + property SelectedFunction: ILEDFunction read FSelectedFunction; + + property Profile: TProfile read FProfile; + property ButtonIndex: Integer read FButtonIndex; public class function Execute(AProfile: TProfile; AButtonIndex: Integer): Boolean; end; + TStateControlInfo = class(TObject) + private + FState: ILEDState; + FStateLabel: TLabel; + FComboBox: TComboBox; + public + constructor Create(AState: ILEDState; AStateLabel: TLabel; AComboBox: TComboBox); + destructor Destroy; override; + + property State: ILEDState read FState; + property StateLabel: TLabel read FStateLabel; + property ComboBox: TComboBox read FComboBox; + end; + + implementation uses - Generics.Collections, System.SysUtils, Winapi.Windows, - ColourEditor, LEDFunctionRegistry, - LEDStateIntf; + LEDResources; type @@ -86,6 +117,7 @@ type TStateNodeData = record State: ILEDState; + Color: TLEDColor; end; PStateNodeData = ^TStateNodeData; @@ -93,7 +125,7 @@ type const ColumnState = 0; - ColumnColour = 1; + ColumnColor = 1; {$R *.dfm} @@ -104,9 +136,7 @@ class function TButtonFunctionForm.Execute(AProfile: TProfile; AButtonIndex: Int begin with Self.Create(nil) do try - Profile := AProfile; - ButtonIndex := AButtonIndex; - + Initialize(AProfile, AButtonIndex); Result := (ShowModal = mrOk); finally Free; @@ -115,19 +145,21 @@ end; procedure TButtonFunctionForm.FormCreate(Sender: TObject); begin - vstFunctions.NodeDataSize := SizeOf(TFunctionNodeData); - vstStates.NodeDataSize := SizeOf(TStateNodeData); + FStateControls := TStateControlInfoList.Create(True); + vstFunctions.NodeDataSize := SizeOf(TFunctionNodeData); + + lblButton.Caption := ''; + lblCurrentCategory.Caption := ''; + lblCurrentFunction.Caption := ''; lblCategoryName.Caption := ''; lblFunctionName.Caption := ''; - - LoadFunctions; end; procedure TButtonFunctionForm.FormDestroy(Sender: TObject); begin - // + FreeAndNil(FStateControls); end; @@ -163,6 +195,7 @@ var nodeData: PFunctionNodeData; provider: ILEDFunctionProvider; ledFunction: ILEDFunction; + isCurrentProvider: Boolean; begin vstFunctions.BeginUpdate; @@ -173,6 +206,8 @@ begin try for provider in TLEDFunctionRegistry.Providers do begin + isCurrentProvider := Assigned(CurrentProvider) and (provider.GetUID = CurrentProvider.GetUID); + for ledFunction in provider do begin node := vstFunctions.AddChild(GetCategoryNode(provider, ledFunction)); @@ -181,6 +216,9 @@ begin nodeData^.NodeType := ntFunction; nodeData^.Provider := provider; nodeData^.LEDFunction := ledFunction; + + if isCurrentProvider and Assigned(CurrentFunction) and (ledFunction.GetUID = CurrentFunction.GetUID) then + vstFunctions.Selected[node] := True; end; end; finally @@ -197,50 +235,169 @@ var multiStateFunction: ILEDMultiStateFunction; begin - lblCategoryName.Caption := AFunction.GetCategoryName; - lblFunctionName.Caption := AFunction.GetDisplayName; + FSelectedProvider := AProvider; + FSelectedFunction := AFunction; - if Supports(AFunction, ILEDMultiStateFunction, multiStateFunction) then + lblCategoryName.Caption := SelectedFunction.GetCategoryName; + lblFunctionName.Caption := SelectedFunction.GetDisplayName; + + if Supports(SelectedFunction, ILEDMultiStateFunction, multiStateFunction) then begin lblNoStates.Visible := False; lblHasStates.Visible := True; - LoadStates(multiStateFunction); - vstStates.Visible := True; + LoadStates(AProvider, multiStateFunction); + sbStates.Visible := True; end else begin lblNoStates.Visible := True; lblHasStates.Visible := False; - vstStates.Visible := False; - vstStates.Clear; + sbStates.Visible := False; + FStateControls.Clear; end; end; -procedure TButtonFunctionForm.LoadStates(AFunction: ILEDMultiStateFunction); +procedure TButtonFunctionForm.Initialize(AProfile: TProfile; AButtonIndex: Integer); +begin + FProfile := AProfile; + FButtonIndex := AButtonIndex; + FButton := nil; + FCurrentProvider := nil; + FCurrentFunction := nil; + + lblButton.Caption := 'P' + IntToStr(Succ(ButtonIndex)); + + if Profile.HasButton(ButtonIndex) then + begin + FButton := Profile.Buttons[ButtonIndex]; + FCurrentProvider := TLEDFunctionRegistry.Find(Button.ProviderUID); + + if Assigned(CurrentProvider) then + FCurrentFunction := CurrentProvider.Find(Button.FunctionUID); + end; + + LoadFunctions; + + if Assigned(CurrentFunction) then + begin + lblCurrentCategory.Caption := CurrentFunction.GetCategoryName + ': '; + lblCurrentFunction.Caption := CurrentFunction.GetDisplayName; + + lblCurrentCategory.Left := lblCurrentFunction.Left - lblCurrentCategory.Width; + + SetFunction(CurrentProvider, CurrentFunction); + end else + begin + lblCurrentCategory.Caption := ''; + lblCurrentFunction.Caption := 'Unassigned'; + end; +end; + + +procedure TButtonFunctionForm.LoadStates(AProvider: ILEDFunctionProvider; AFunction: ILEDMultiStateFunction); + + procedure FillColorComboBox(AComboBox: TComboBox; ASelectedColor: TLEDColor); + var + color: TLEDColor; + itemIndex: Integer; + + begin + AComboBox.Items.BeginUpdate; + try + AComboBox.Items.Clear; + + for color := Low(TLEDColor) to High(TLEDColor) do + begin + itemIndex := AComboBox.Items.AddObject(LEDColorDisplayName[color], TObject(color)); + + if color = ASelectedColor then + AComboBox.ItemIndex := itemIndex; + end; + finally + AComboBox.Items.EndUpdate; + end; + end; + + var - node: PVirtualNode; - nodeData: PStateNodeData; state: ILEDState; + stateLabel: TLabel; + colorCombobox: TComboBox; + comboBoxWidth: Integer; + currentY: Integer; + selectedColor: TLEDColor; + isCurrent: Boolean; begin - vstStates.BeginUpdate; - try - vstStates.Clear; + FStateControls.Clear; - for state in AFunction do - begin - node := vstStates.AddChild(nil); - nodeData := vstStates.GetNodeData(node); - nodeData^.State := state; - end; - finally - vstStates.EndUpdate; + currentY := 0; + comboBoxWidth := sbStates.ClientWidth div 2; + + isCurrent := Assigned(CurrentProvider) and (AProvider.GetUID = CurrentProvider.GetUID) and + Assigned(CurrentFunction) and (AFunction.GetUID = CurrentFunction.GetUID); + + for state in AFunction do + begin + stateLabel := TLabel.Create(nil); + stateLabel.AutoSize := False; + stateLabel.Caption := state.GetDisplayName; + stateLabel.EllipsisPosition := epEndEllipsis; + stateLabel.Left := 0; + stateLabel.Top := currentY + 4; + stateLabel.Width := comboBoxWidth - 8; + stateLabel.Parent := sbStates; + + colorCombobox := TComboBox.Create(nil); + colorCombobox.DropDownCount := Length(LEDColorDisplayName); + colorCombobox.Style := csDropDownList; + colorCombobox.Left := sbStates.ClientWidth - comboBoxWidth; + colorCombobox.Top := currentY; + colorCombobox.Width := comboBoxWidth; + colorCombobox.Parent := sbStates; + + if (not isCurrent) or (not Button.GetStateColor(state.GetUID, selectedColor)) then + selectedColor := state.GetDefaultColor; + + FillColorComboBox(colorComboBox, selectedColor); + + FStateControls.Add(TStateControlInfo.Create(state, stateLabel, colorCombobox)); + Inc(currentY, colorCombobox.Height + 8); end; end; +procedure TButtonFunctionForm.btnOKClick(Sender: TObject); +var + multiStateFunction: ILEDMultiStateFunction; + stateControlInfo: TStateControlInfo; + comboBox: TComboBox; + color: TLEDColor; + +begin + Button.ProviderUID := SelectedProvider.GetUID; + Button.FunctionUID := SelectedFunction.GetUID; + + Button.ClearStateColors; + if Supports(SelectedFunction, ILEDMultiStateFunction, multiStateFunction) then + begin + for stateControlInfo in FStateControls do + begin + comboBox := stateControlInfo.ComboBox; + if comboBox.ItemIndex > -1 then + begin + color := TLEDColor(comboBox.Items.Objects[comboBox.ItemIndex]); + Button.SetStateColor(stateControlInfo.State.GetUID, color); + end; + end; + end; + + ModalResult := mrOk; +end; + + procedure TButtonFunctionForm.vstFunctionsFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); var nodeData: PFunctionNodeData; @@ -251,21 +408,17 @@ begin begin nodeData := Sender.GetNodeData(Node); - case nodeData^.NodeType of - ntCategory: - begin - { Select first child (function) node instead } - functionNode := Sender.GetFirstChild(Node); - if not Assigned(functionNode) then - exit; + if nodeData^.NodeType = ntCategory then + begin + { Get first child (function) node instead } + functionNode := Sender.GetFirstChild(Node); + if not Assigned(functionNode) then + exit; - Sender.FocusedNode := functionNode; - Sender.Selected[functionNode] := True; - end; - - ntFunction: - SetFunction(nodeData^.Provider, nodeData^.LEDFunction); + nodeData := Sender.GetNodeData(functionNode); end; + + SetFunction(nodeData^.Provider, nodeData^.LEDFunction); end; end; @@ -300,48 +453,23 @@ begin end; -procedure TButtonFunctionForm.vstStatesGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; - TextType: TVSTTextType; var CellText: string); -var - nodeData: PStateNodeData; - +{ TStateControlInfo } +constructor TStateControlInfo.Create(AState: ILEDState; AStateLabel: TLabel; AComboBox: TComboBox); begin - nodeData := Sender.GetNodeData(Node); + inherited Create; - case Column of - ColumnState: CellText := nodeData^.State.GetDisplayName; - ColumnColour: CellText := 'Red'; - end; + FState := AState; + FStateLabel := AStateLabel; + FComboBox := AComboBox; end; -procedure TButtonFunctionForm.vstStatesChange(Sender: TBaseVirtualTree; Node: PVirtualNode); +destructor TStateControlInfo.Destroy; begin - if Assigned(Node) and not (tsIncrementalSearching in Sender.TreeStates) then - PostMessage(Self.Handle, WM_STARTEDITING, WPARAM(Node), 0); -end; + FreeAndNil(FComboBox); + FreeAndNil(FStateLabel); - -procedure TButtonFunctionForm.vstStatesCreateEditor(Sender: TBaseVirtualTree; Node: PVirtualNode; - Column: TColumnIndex; out EditLink: IVTEditLink); -begin - EditLink := TVTColourEditor.Create; -end; - - -procedure TButtonFunctionForm.vstStatesEditing(Sender: TBaseVirtualTree; - Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean); -begin - Allowed := True; -end; - -procedure TButtonFunctionForm.WMStartEditing(var Msg: TMessage); -var - node: PVirtualNode; - -begin - node := Pointer(Msg.WParam); - vstStates.EditNode(Node, 1); + inherited; end; end. diff --git a/G940LEDControl/Forms/MainFrm.dfm b/G940LEDControl/Forms/MainFrm.dfm index 85a5514..f1ed398 100644 --- a/G940LEDControl/Forms/MainFrm.dfm +++ b/G940LEDControl/Forms/MainFrm.dfm @@ -326,7 +326,7 @@ object MainForm: TMainForm object cmbProfiles: TComboBox Left = 64 Top = 16 - Width = 213 + Width = 234 Height = 21 Style = csDropDownList Anchors = [akLeft, akTop, akRight] @@ -335,12 +335,12 @@ object MainForm: TMainForm OnClick = cmbProfilesClick end object btnSaveProfile: TButton - Left = 283 + Left = 304 Top = 16 - Width = 75 + Width = 54 Height = 21 Anchors = [akTop, akRight] - Caption = 'Save as...' + Caption = 'New' TabOrder = 9 end object btnDeleteProfile: TButton @@ -356,10 +356,6 @@ object MainForm: TMainForm object tsAbout: TTabSheet Caption = 'About' ImageIndex = 1 - ExplicitLeft = 0 - ExplicitTop = 0 - ExplicitWidth = 382 - ExplicitHeight = 482 object lblVersionCaption: TLabel Left = 16 Top = 67 diff --git a/G940LEDControl/Forms/MainFrm.pas b/G940LEDControl/Forms/MainFrm.pas index 51bd1c9..696ecf6 100644 --- a/G940LEDControl/Forms/MainFrm.pas +++ b/G940LEDControl/Forms/MainFrm.pas @@ -242,7 +242,7 @@ end; procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin - SaveProfiles; +// SaveProfiles; // if Assigned(StateConsumerTask) then // begin // SaveDefaultProfile; @@ -547,7 +547,10 @@ begin buttonIndex := (Sender as TComponent).Tag; if TButtonFunctionForm.Execute(ActiveProfile, buttonIndex) then + begin UpdateButton(ActiveProfile, buttonIndex); + SaveProfiles; + end; end; diff --git a/G940LEDControl/G940LEDControl.dpr b/G940LEDControl/G940LEDControl.dpr index 0bba2da..73926ad 100644 --- a/G940LEDControl/G940LEDControl.dpr +++ b/G940LEDControl/G940LEDControl.dpr @@ -5,7 +5,6 @@ uses MainFrm in 'Forms\MainFrm.pas' {MainForm}, LogiJoystickDLL in '..\Shared\LogiJoystickDLL.pas', SimConnect in '..\Shared\SimConnect.pas', - ButtonSelectFrm in 'Forms\ButtonSelectFrm.pas' {ButtonSelectForm}, FSXLEDStateProvider in 'Units\FSXLEDStateProvider.pas', G940LEDStateConsumer in 'Units\G940LEDStateConsumer.pas', LEDFunctionMap in 'Units\LEDFunctionMap.pas', @@ -26,11 +25,13 @@ uses Profile in 'Units\Profile.pas', LEDColorPool in 'Units\LEDColorPool.pas', ButtonFunctionFrm in 'Forms\ButtonFunctionFrm.pas' {ButtonFunctionForm}, - FSXLEDFunction in 'Units\FSXLEDFunction.pas', + FSXLEDFunctionProvider in 'Units\FSXLEDFunctionProvider.pas', StaticResources in 'Units\StaticResources.pas', FSXResources in 'Units\FSXResources.pas', FSXSimConnectClient in 'Units\FSXSimConnectClient.pas', - ColourEditor in 'Units\ColourEditor.pas'; + FSXSimConnectIntf in 'Units\FSXSimConnectIntf.pas', + FSXLEDFunction in 'Units\FSXLEDFunction.pas', + LEDResources in 'Units\LEDResources.pas'; {$R *.res} diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj index 4c8b157..6e8807e 100644 --- a/G940LEDControl/G940LEDControl.dproj +++ b/G940LEDControl/G940LEDControl.dproj @@ -98,6 +98,7 @@ False + true false CompanyName=;FileDescription=;FileVersion=0.2.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=0.2;Comments= F:\Components\X2Utils\Resources\VistaManAsInvoker.manifest @@ -164,10 +165,6 @@ - -
ButtonSelectForm
- dfm -
@@ -191,11 +188,13 @@
ButtonFunctionForm
dfm
- + - + + + Cfg_2 Base diff --git a/G940LEDControl/Units/ColourEditor.pas b/G940LEDControl/Units/ColourEditor.pas deleted file mode 100644 index 2daf26d..0000000 --- a/G940LEDControl/Units/ColourEditor.pas +++ /dev/null @@ -1,141 +0,0 @@ -unit ColourEditor; - -interface -uses - System.Types, - System.Classes, - Vcl.StdCtrls, - Winapi.Messages, - - VirtualTrees; - - -type - TVTColourEditor = class(TInterfacedObject, IVTEditLink) - private - FEdit: TComboBox; - FTree: TBaseVirtualTree; - FColumn: TColumnIndex; - protected - procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); - protected - { IVTEditLink } - function BeginEdit: Boolean; stdcall; - function CancelEdit: Boolean; stdcall; - function EndEdit: Boolean; stdcall; - - function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall; - procedure ProcessMessage(var Message: TMessage); stdcall; - - function GetBounds: TRect; stdcall; - procedure SetBounds(R: TRect); stdcall; - public - destructor Destroy; override; - end; - -implementation -uses - System.SysUtils, - Winapi.Windows; - - -{ TVTColourEditor } -destructor TVTColourEditor.Destroy; -begin - FreeAndNil(FEdit); - - inherited; -end; - - -function TVTColourEditor.BeginEdit: Boolean; -begin - Result := True; - FEdit.Show; - FEdit.SetFocus; -end; - - -function TVTColourEditor.CancelEdit: Boolean; -begin - Result := True; - FEdit.Hide; -end; - - -function TVTColourEditor.EndEdit: Boolean; -begin - Result := True; - // TODO update node data -end; - - -function TVTColourEditor.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; -begin - Result := True; - - FTree := Tree; -// FNode := Node; - FColumn := Column; - - FreeAndNil(FEdit); - - FEdit := TComboBox.Create(nil); - FEdit.Visible := False; - FEdit.Parent := Tree; - -// FEdit.Text := Data.Value; -// FEdit.Items.Add(); - - FEdit.OnKeyDown := EditKeyDown; -end; - - -procedure TVTColourEditor.ProcessMessage(var Message: TMessage); -begin - FEdit.WindowProc(Message); -end; - - -function TVTColourEditor.GetBounds: TRect; -begin - Result := FEdit.BoundsRect; -end; - - -procedure TVTColourEditor.SetBounds(R: TRect); -var - dummy: Integer; - -begin - (FTree as TVirtualStringTree).Header.Columns.GetColumnBounds(FColumn, dummy, R.Right); - FEdit.BoundsRect := R; -end; - - -procedure TVTColourEditor.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); -begin - case Key of - VK_ESCAPE: - begin - FTree.CancelEditNode; - Key := 0; - end; - - VK_RETURN: - begin - FTree.EndEditNode; - Key := 0; - end; - - VK_UP, - VK_DOWN: - if (Shift = []) and (not FEdit.DroppedDown) then - begin - PostMessage(FTree.Handle, WM_KEYDOWN, Key, 0); - Key := 0; - end; - end; -end; - -end. diff --git a/G940LEDControl/Units/ConfigConversion.pas b/G940LEDControl/Units/ConfigConversion.pas index 2b4b7e5..90b04ae 100644 --- a/G940LEDControl/Units/ConfigConversion.pas +++ b/G940LEDControl/Units/ConfigConversion.pas @@ -62,6 +62,8 @@ const V0_FUNCTIONFSX_TAXILIGHTS = V0_FUNCTIONPROVIDER_OFFSET + 25; V0_FUNCTIONFSX_RECOGNITIONLIGHTS = V0_FUNCTIONPROVIDER_OFFSET + 26; + // TODO 27 (de-ice) + procedure ConvertProfileFunction0To1(AOldFunction: Integer; AButton: TProfileButton); diff --git a/G940LEDControl/Units/FSXLEDFunction.pas b/G940LEDControl/Units/FSXLEDFunction.pas deleted file mode 100644 index a9f0684..0000000 --- a/G940LEDControl/Units/FSXLEDFunction.pas +++ /dev/null @@ -1,231 +0,0 @@ -unit FSXLEDFunction; - -interface -uses - Generics.Collections, - - LEDFunction, - LEDStateIntf, - ObserverIntf; - - -type - TCustomFSXFunction = class; - TCustomFSXFunctionList = TObjectList; - - - TFSXLEDFunctionProvider = class(TCustomLEDFunctionProvider) - private - FConnectedFunctions: TCustomFSXFunctionList; - FSimConnectHandle: THandle; - protected - procedure SimConnect; - procedure SimDisconnect; - - procedure Connect(AFunction: TCustomFSXFunction); virtual; - procedure Disconnect(AFunction: TCustomFSXFunction); virtual; - - property ConnectedFunctions: TCustomFSXFunctionList read FConnectedFunctions; - property SimConnectHandle: THandle read FSimConnectHandle; - protected - procedure RegisterFunctions; override; - - function GetUID: string; override; - end; - - - TCustomFSXFunction = class(TCustomMultiStateLEDFunction) - private - FProvider: TFSXLEDFunctionProvider; - protected - procedure SimConnected; virtual; - procedure SimDisconnected; virtual; - - property Provider: TFSXLEDFunctionProvider read FProvider; - protected - procedure Attach(AObserver: IObserver); override; - procedure Detach(AObserver: IObserver); override; - - function GetCategoryName: string; override; - public - constructor Create(AProvider: TFSXLEDFunctionProvider); - end; - - - TFSXGearFunction = class(TCustomFSXFunction) - private - FRetractedState: ILEDState; - FBetweenState: ILEDState; - FExtendedState: ILEDState; - FSpeedExceededState: ILEDState; - FDamageBySpeedState: ILEDState; - protected - procedure RegisterStates; override; - - function GetDisplayName: string; override; - function GetUID: string; override; - - function GetCurrentState: ILEDState; override; - end; - - -implementation -uses - FSXResources, - LEDColorPool, - LEDFunctionRegistry, - LEDState; - - -{ TFSXLEDFunctionProvider } -procedure TFSXLEDFunctionProvider.RegisterFunctions; -begin - RegisterFunction(TFSXGearFunction.Create(Self)); -end; - - -function TFSXLEDFunctionProvider.GetUID: string; -begin - Result := FSXProviderUID; -end; - - -procedure TFSXLEDFunctionProvider.SimConnect; -var - fsxFunction: TCustomFSXFunction; - -begin - if SimConnectHandle <> 0 then - exit; - -// FSimConnectHandle := - - if SimConnectHandle <> 0 then - begin - for fsxFunction in ConnectedFunctions do - fsxFunction.SimConnected; - end; -end; - - -procedure TFSXLEDFunctionProvider.SimDisconnect; -begin - if SimConnectHandle = 0 then - exit; -end; - - -procedure TFSXLEDFunctionProvider.Connect(AFunction: TCustomFSXFunction); -begin - if ConnectedFunctions.IndexOf(AFunction) = -1 then - begin - ConnectedFunctions.Add(AFunction); - - if ConnectedFunctions.Count > 0 then - SimConnect; - end; -end; - -procedure TFSXLEDFunctionProvider.Disconnect(AFunction: TCustomFSXFunction); -begin - ConnectedFunctions.Remove(AFunction); - - if ConnectedFunctions.Count = 0 then - SimDisconnect; -end; - - -{ TCustomFSXFunction } -constructor TCustomFSXFunction.Create(AProvider: TFSXLEDFunctionProvider); -begin - inherited Create; - - FProvider := AProvider; -end; - - -procedure TCustomFSXFunction.Attach(AObserver: IObserver); -begin - if Observers.Count = 0 then - Provider.Connect(Self); - - inherited Attach(AObserver); -end; - - -procedure TCustomFSXFunction.Detach(AObserver: IObserver); -begin - if Assigned(Provider) and (Observers.Count > 0) then - begin - inherited Detach(AObserver); - - if Observers.Count = 0 then - Provider.Disconnect(Self); - end else - inherited Detach(AObserver); -end; - - -function TCustomFSXFunction.GetCategoryName: string; -begin - Result := FSXCategory; -end; - - -procedure TCustomFSXFunction.SimConnected; -begin -end; - - -procedure TCustomFSXFunction.SimDisconnected; -begin -end; - - -{ TFSXGearFunction } -procedure TFSXGearFunction.RegisterStates; -begin - FRetractedState := RegisterState(TLEDState.Create(FSXStateUIDGearRetracted, - FSXStateDisplayNameGearRetracted, - TLEDColorPool.GetColor(cpeStaticRed))); - - FBetweenState := RegisterState(TLEDState.Create(FSXStateUIDGearBetween, - FSXStateDisplayNameGearBetween, - TLEDColorPool.GetColor(cpeStaticAmber))); - - FExtendedState := RegisterState(TLEDState.Create(FSXStateUIDGearExtended, - FSXStateDisplayNameGearExtended, - TLEDColorPool.GetColor(cpeStaticGreen))); - - FSpeedExceededState := RegisterState(TLEDState.Create(FSXStateUIDGearSpeedExceeded, - FSXStateDisplayNameGearSpeedExceeded, - TLEDColorPool.GetColor(cpeFlashingAmberNormal))); - - FDamageBySpeedState := RegisterState(TLEDState.Create(FSXStateUIDGearDamageBySpeed, - FSXStateDisplayNameGearDamageBySpeed, - TLEDColorPool.GetColor(cpeFlashingRedFast))); -end; - - -function TFSXGearFunction.GetDisplayName: string; -begin - Result := FSXFunctionDisplayNameGear; -end; - - -function TFSXGearFunction.GetUID: string; -begin - Result := FSXFunctionUIDGear; -end; - - -function TFSXGearFunction.GetCurrentState: ILEDState; -begin - // TODO TFSXGearFunction.GetCurrentState -end; - - -initialization - TLEDFunctionRegistry.Register(TFSXLEDFunctionProvider.Create); - -end. diff --git a/G940LEDControl/Units/FSXLEDFunctionProvider.pas b/G940LEDControl/Units/FSXLEDFunctionProvider.pas new file mode 100644 index 0000000..c477692 --- /dev/null +++ b/G940LEDControl/Units/FSXLEDFunctionProvider.pas @@ -0,0 +1,251 @@ +unit FSXLEDFunctionProvider; + +interface +uses + Generics.Collections, + System.SyncObjs, + + FSXSimConnectIntf, + LEDFunction, + LEDFunctionIntf, + LEDStateIntf, + ObserverIntf; + + +type + TCustomFSXFunction = class; + TCustomFSXFunctionList = TObjectList; + + + TFSXLEDFunctionProvider = class(TCustomLEDFunctionProvider) + private + FConnectedFunctions: TCustomFSXFunctionList; + FSimConnectHandle: THandle; + protected + procedure SimConnect; + procedure SimDisconnect; + + procedure Connect(AFunction: TCustomFSXFunction); virtual; + procedure Disconnect(AFunction: TCustomFSXFunction); virtual; + + property ConnectedFunctions: TCustomFSXFunctionList read FConnectedFunctions; + property SimConnectHandle: THandle read FSimConnectHandle; + protected + procedure RegisterFunctions; override; + + function GetUID: string; override; + public + function GetSimConnect: IFSXSimConnect; + end; + + + TCustomFSXFunction = class(TCustomMultiStateLEDFunction) + private + FProvider: TFSXLEDFunctionProvider; + FDisplayName: string; + FUID: string; + protected + procedure SimConnected; virtual; + procedure SimDisconnected; virtual; + + property Provider: TFSXLEDFunctionProvider read FProvider; + protected + function GetCategoryName: string; override; + function GetDisplayName: string; override; + function GetUID: string; override; + public + constructor Create(AProvider: TFSXLEDFunctionProvider; const ADisplayName, AUID: string); + end; + + + TCustomFSXFunctionWorker = class(TCustomLEDFunctionWorker) + private + FSimConnect: IFSXSimConnect; + FDefinition: IFSXSimConnectDefinition; + FCurrentStateLock: TCriticalSection; + FCurrentState: ILEDStateWorker; + protected + procedure RegisterVariables; virtual; abstract; + + procedure SetCurrentState(const AUID: string); + + property Definition: IFSXSimConnectDefinition read FDefinition; + property SimConnect: IFSXSimConnect read FSimConnect; + protected + function GetCurrentState: ILEDStateWorker; override; + public + constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings; ASimConnect: IFSXSimConnect); + destructor Destroy; override; + end; + + +implementation +uses + System.SysUtils, + + FSXLEDFunction, + FSXResources, + LEDFunctionRegistry, + SimConnect; + + + +{ TFSXLEDFunctionProvider } +procedure TFSXLEDFunctionProvider.RegisterFunctions; +begin + RegisterFunction(TFSXGearFunction.Create(Self, FSXFunctionDisplayNameGear, FSXFunctionUIDGear)); +end; + + +function TFSXLEDFunctionProvider.GetUID: string; +begin + Result := FSXProviderUID; +end; + + +function TFSXLEDFunctionProvider.GetSimConnect: IFSXSimConnect; +begin + // TODO +end; + + +procedure TFSXLEDFunctionProvider.SimConnect; +var + fsxFunction: TCustomFSXFunction; + +begin + if SimConnectHandle <> 0 then + exit; + +// FSimConnectHandle := + + if SimConnectHandle <> 0 then + begin + for fsxFunction in ConnectedFunctions do + fsxFunction.SimConnected; + end; +end; + + +procedure TFSXLEDFunctionProvider.SimDisconnect; +begin + if SimConnectHandle = 0 then + exit; +end; + + +procedure TFSXLEDFunctionProvider.Connect(AFunction: TCustomFSXFunction); +begin + if ConnectedFunctions.IndexOf(AFunction) = -1 then + begin + ConnectedFunctions.Add(AFunction); + + if ConnectedFunctions.Count > 0 then + SimConnect; + end; +end; + +procedure TFSXLEDFunctionProvider.Disconnect(AFunction: TCustomFSXFunction); +begin + ConnectedFunctions.Remove(AFunction); + + if ConnectedFunctions.Count = 0 then + SimDisconnect; +end; + + +{ TCustomFSXFunction } +constructor TCustomFSXFunction.Create(AProvider: TFSXLEDFunctionProvider; const ADisplayName, AUID: string); +begin + inherited Create; + + FProvider := AProvider; + FDisplayName := ADisplayName; + FUID := AUID; +end; + + +function TCustomFSXFunction.GetCategoryName: string; +begin + Result := FSXCategory; +end; + + +function TCustomFSXFunction.GetDisplayName: string; +begin + Result := FDisplayName; +end; + + +function TCustomFSXFunction.GetUID: string; +begin + Result := FUID; +end; + + +procedure TCustomFSXFunction.SimConnected; +begin +end; + + +procedure TCustomFSXFunction.SimDisconnected; +begin +end; + + +{ TCustomFSXFunctionWorker } +constructor TCustomFSXFunctionWorker.Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings; ASimConnect: IFSXSimConnect); +begin + inherited Create(AStates, ASettings); + + FCurrentStateLock := TCriticalSection.Create; + FSimConnect := ASimConnect; + + FDefinition := ASimConnect.CreateDefinition; + RegisterVariables; + ASimConnect.AddDefinition(FDefinition); +end; + + +destructor TCustomFSXFunctionWorker.Destroy; +begin + FreeAndNil(FCurrentStateLock); + + inherited; +end; + + +function TCustomFSXFunctionWorker.GetCurrentState: ILEDStateWorker; +begin + FCurrentStateLock.Acquire; + try + Result := FCurrentState; + finally + FCurrentStateLock.Release; + end; +end; + + +procedure TCustomFSXFunctionWorker.SetCurrentState(const AUID: string); +var + newState: ILEDStateWorker; + +begin + FCurrentStateLock.Acquire; + try + newState := FindState(AUID); + if newState <> FCurrentState then + begin + FCurrentState := newState; + NotifyObservers; + end; + finally + FCurrentStateLock.Release; + end; +end; + + +initialization + TLEDFunctionRegistry.Register(TFSXLEDFunctionProvider.Create); + +end. diff --git a/G940LEDControl/Units/FSXResources.pas b/G940LEDControl/Units/FSXResources.pas index c256474..969913e 100644 --- a/G940LEDControl/Units/FSXResources.pas +++ b/G940LEDControl/Units/FSXResources.pas @@ -7,7 +7,7 @@ const FSXCategory = 'Flight Simulator X'; FSXFunctionUIDGear = 'gear'; - FSXFunctionDisplayNameGear = 'Gear'; + FSXFunctionDisplayNameGear = 'Landing gear'; FSXStateUIDGearRetracted = 'retracted'; FSXStateUIDGearBetween = 'between'; diff --git a/G940LEDControl/Units/FSXSimConnectClient.pas b/G940LEDControl/Units/FSXSimConnectClient.pas index 563492b..4cb8fc8 100644 --- a/G940LEDControl/Units/FSXSimConnectClient.pas +++ b/G940LEDControl/Units/FSXSimConnectClient.pas @@ -2,13 +2,104 @@ unit FSXSimConnectClient; interface uses - OtlTaskControl; + OtlTaskControl, + + FSXSimConnectIntf; + +type + TFSXSimConnectInterface = class(TInterfacedObject, IFSXSimConnect) + private + FClient: IOmniTaskControl; + protected + property Client: IOmniTaskControl read FClient; + protected + { IFSXSimConnect } + function CreateDefinition: IFSXSimConnectDefinition; + procedure AddDefinition(ADefinition: IFSXSimConnectDefinition); + public + constructor Create; + destructor Destroy; override; + end; + + +implementation +uses + System.SysUtils, + + SimConnect; + type TFSXSimConnectClient = class(TOmniWorker) - end; -implementation + + TFSXSimConnectDefinition = class(TInterfacedObject, IFSXSimConnectDefinition) + private + FSimConnect: IFSXSimConnect; + protected + property SimConnect: IFSXSimConnect read FSimConnect; + protected + { IFSXSimConnectDefinition } + procedure AddVariable(AVariableName, AUnitsName: string; ADatumType: SIMCONNECT_DATAType; AEpsilon: Single = 0); + procedure Apply(ASimConnectHandle: THandle; ADefinitionID: Integer); + public + constructor Create(ASimConnect: IFSXSimConnect); + end; + + + + +{ TFSXSimConnectInterface } +constructor TFSXSimConnectInterface.Create; +var + worker: IOmniWorker; + +begin + worker := TFSXSimConnectClient.Create; + FClient := CreateTask(worker); +end; + + +destructor TFSXSimConnectInterface.Destroy; +begin + FClient.Terminate; + FClient := nil; + + inherited; +end; + + +function TFSXSimConnectInterface.CreateDefinition: IFSXSimConnectDefinition; +begin + Result := TFSXSimConnectDefinition.Create(Self); +end; + + +procedure TFSXSimConnectInterface.AddDefinition(ADefinition: IFSXSimConnectDefinition); +begin + // TODO +end; + + + +{ TFSXSimConnectDefinition } +constructor TFSXSimConnectDefinition.Create(ASimConnect: IFSXSimConnect); +begin + +end; + + +procedure TFSXSimConnectDefinition.AddVariable(AVariableName, AUnitsName: string; ADatumType: SIMCONNECT_DATAType; AEpsilon: Single); +begin + +end; + + +procedure TFSXSimConnectDefinition.Apply(ASimConnectHandle: THandle; ADefinitionID: Integer); +begin +// SimConnect_AddToDataDefinition(ASimConnectHandle, ADefinitionID, +// AnsiString(AVariableName), AnsiString(AUnitsName), ADatumType, AEpsilon, 0); +end; end. diff --git a/G940LEDControl/Units/FSXSimConnectIntf.pas b/G940LEDControl/Units/FSXSimConnectIntf.pas new file mode 100644 index 0000000..46704de --- /dev/null +++ b/G940LEDControl/Units/FSXSimConnectIntf.pas @@ -0,0 +1,37 @@ +unit FSXSimConnectIntf; + +interface +uses + SimConnect; + + +type + IFSXSimConnectDefinition = interface; + + + IFSXSimConnect = interface + ['{B6BE3E7C-0804-43D6-84DE-8010C5728A07}'] + function CreateDefinition: IFSXSimConnectDefinition; + procedure AddDefinition(ADefinition: IFSXSimConnectDefinition); + end; + + + IFSXSimConnectDefinition = interface + ['{F1EAB3B1-0A3D-4B06-A75F-823E15C313B8}'] + procedure AddVariable(AVariableName, AUnitsName: string; ADatumType: SIMCONNECT_DATAType; AEpsilon: Single = 0); + procedure Apply(ASimConnectHandle: THandle; ADefinitionID: Integer); + end; + + + +const + FSX_UNIT_PERCENT = 'percent'; + FSX_UNIT_MASK = 'mask'; + FSX_UNIT_BOOL = 'bool'; + FSX_UNIT_NUMBER = 'number'; + + + +implementation + +end. diff --git a/G940LEDControl/Units/LEDColorIntf.pas b/G940LEDControl/Units/LEDColorIntf.pas index e283152..352a0fe 100644 --- a/G940LEDControl/Units/LEDColorIntf.pas +++ b/G940LEDControl/Units/LEDColorIntf.pas @@ -2,7 +2,13 @@ unit LEDColorIntf; interface type - TLEDColor = (lcOff, lcGreen, lcAmber, lcRed); + TLEDColor = (lcOff, lcGreen, lcAmber, lcRed, + lcFlashingGreenFast, lcFlashingGreenNormal, + lcFlashingAmberFast, lcFlashingAmberNormal, + lcFlashingRedFast, lcFlashingRedNormal); + + TStaticLEDColor = lcOff..lcRed; + ILEDColor = interface diff --git a/G940LEDControl/Units/LEDColorPool.pas b/G940LEDControl/Units/LEDColorPool.pas index 8b91516..b24ce5a 100644 --- a/G940LEDControl/Units/LEDColorPool.pas +++ b/G940LEDControl/Units/LEDColorPool.pas @@ -6,27 +6,14 @@ uses type - TLEDColorPoolEntry = (cpeStaticOff, - cpeStaticGreen, - cpeStaticAmber, - cpeStaticRed, - - cpeFlashingGreenFast, - cpeFlashingGreenNormal, - cpeFlashingAmberFast, - cpeFlashingAmberNormal, - cpeFlashingRedFast, - cpeFlashingRedNormal); - TLEDColorPool = class(TObject) private - FStates: array[TLEDColorPoolEntry] of ILEDColor; + FStates: array[TLEDColor] of ILEDColor; protected class function Instance: TLEDColorPool; - function DoGetColor(AEntry: TLEDColorPoolEntry): ILEDColor; + function DoGetColor(AColor: TLEDColor): ILEDColor; public - class function GetColor(AEntry: TLEDColorPoolEntry): ILEDColor; overload; class function GetColor(AColor: TLEDColor): ILEDColor; overload; end; @@ -44,22 +31,9 @@ var { TLEDStatePool } -class function TLEDColorPool.GetColor(AEntry: TLEDColorPoolEntry): ILEDColor; -begin - Result := Instance.DoGetColor(AEntry); -end; - - class function TLEDColorPool.GetColor(AColor: TLEDColor): ILEDColor; begin - Result := nil; - - case AColor of - lcOff: Result := GetColor(cpeStaticOff); - lcGreen: Result := GetColor(cpeStaticGreen); - lcAmber: Result := GetColor(cpeStaticAmber); - lcRed: Result := GetColor(cpeStaticRed); - end; + Result := Instance.DoGetColor(AColor); end; @@ -72,7 +46,7 @@ begin end; -function TLEDColorPool.DoGetColor(AEntry: TLEDColorPoolEntry): ILEDColor; +function TLEDColorPool.DoGetColor(AColor: TLEDColor): ILEDColor; function GetFlashingCycle(AColor: TLEDColor): TLEDColorDynArray; begin @@ -85,26 +59,26 @@ var state: ILEDColor; begin - if not Assigned(FStates[AEntry]) then + if not Assigned(FStates[AColor]) then begin - case AEntry of - cpeStaticOff: state := TStaticLEDColor.Create(lcOff); - cpeStaticGreen: state := TStaticLEDColor.Create(lcGreen); - cpeStaticAmber: state := TStaticLEDColor.Create(lcAmber); - cpeStaticRed: state := TStaticLEDColor.Create(lcRed); + case AColor of + lcOff: state := TStaticLEDColor.Create(lcOff); + lcGreen: state := TStaticLEDColor.Create(lcGreen); + lcAmber: state := TStaticLEDColor.Create(lcAmber); + lcRed: state := TStaticLEDColor.Create(lcRed); - cpeFlashingGreenFast: state := TDynamicLEDColor.Create(GetFlashingCycle(lcGreen), TICKINTERVAL_FAST); - cpeFlashingGreenNormal: state := TDynamicLEDColor.Create(GetFlashingCycle(lcGreen), TICKINTERVAL_NORMAL); - cpeFlashingAmberFast: state := TDynamicLEDColor.Create(GetFlashingCycle(lcAmber), TICKINTERVAL_FAST); - cpeFlashingAmberNormal: state := TDynamicLEDColor.Create(GetFlashingCycle(lcAmber), TICKINTERVAL_NORMAL); - cpeFlashingRedFast: state := TDynamicLEDColor.Create(GetFlashingCycle(lcRed), TICKINTERVAL_FAST); - cpeFlashingRedNormal: state := TDynamicLEDColor.Create(GetFlashingCycle(lcRed), TICKINTERVAL_NORMAL); + lcFlashingGreenFast: state := TDynamicLEDColor.Create(GetFlashingCycle(lcGreen), TICKINTERVAL_FAST); + lcFlashingGreenNormal: state := TDynamicLEDColor.Create(GetFlashingCycle(lcGreen), TICKINTERVAL_NORMAL); + lcFlashingAmberFast: state := TDynamicLEDColor.Create(GetFlashingCycle(lcAmber), TICKINTERVAL_FAST); + lcFlashingAmberNormal: state := TDynamicLEDColor.Create(GetFlashingCycle(lcAmber), TICKINTERVAL_NORMAL); + lcFlashingRedFast: state := TDynamicLEDColor.Create(GetFlashingCycle(lcRed), TICKINTERVAL_FAST); + lcFlashingRedNormal: state := TDynamicLEDColor.Create(GetFlashingCycle(lcRed), TICKINTERVAL_NORMAL); end; - FStates[AEntry] := state; + FStates[AColor] := state; Result := state; end else - Result := FStates[AEntry]; + Result := FStates[AColor]; end; diff --git a/G940LEDControl/Units/LEDFunction.pas b/G940LEDControl/Units/LEDFunction.pas index d810e22..7d9bdf2 100644 --- a/G940LEDControl/Units/LEDFunction.pas +++ b/G940LEDControl/Units/LEDFunction.pas @@ -28,27 +28,14 @@ type end; - TCustomLEDFunction = class(TInterfacedObject, IObservable, ILEDFunction) - private - FObservers: TInterfaceList; + TCustomLEDFunction = class(TInterfacedObject, ILEDFunction) protected - procedure NotifyObservers; virtual; - - property Observers: TInterfaceList read FObservers; - protected - { IObservable } - procedure Attach(AObserver: IObserver); virtual; - procedure Detach(AObserver: IObserver); virtual; - { ILEDFunction } function GetCategoryName: string; virtual; abstract; function GetDisplayName: string; virtual; abstract; function GetUID: string; virtual; abstract; - function GetCurrentState: ILEDState; virtual; abstract; - public - constructor Create; - destructor Destroy; override; + function CreateWorker(ASettings: ILEDFunctionWorkerSettings): ILEDFunctionWorker; virtual; abstract; end; @@ -56,7 +43,6 @@ type private FStates: TInterfaceList; protected -// procedure SetCurrentState(AState: ILEDState); virtual; procedure RegisterStates; virtual; abstract; function RegisterState(AState: ILEDState): ILEDState; virtual; protected @@ -68,6 +54,32 @@ type end; + TCustomLEDFunctionWorker = class(TInterfacedObject, ILEDFunctionWorker) + private + FObservers: TInterfaceList; + FStates: TInterfaceList; + protected + procedure RegisterStates(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); virtual; + function FindState(const AUID: string): ILEDStateWorker; virtual; + + procedure NotifyObservers; virtual; + + property Observers: TInterfaceList read FObservers; + property States: TInterfaceList read FStates; + protected + { IObservable } + procedure Attach(AObserver: IObserver); virtual; + procedure Detach(AObserver: IObserver); virtual; + + function GetCurrentState: ILEDStateWorker; virtual; abstract; + public + constructor Create; overload; + constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); overload; + + destructor Destroy; override; + end; + + TLEDFunctionEnumerator = class(TInterfacedObject, ILEDFunctionEnumerator) private FList: TInterfaceList; @@ -97,36 +109,11 @@ type implementation uses - SysUtils; + System.SysUtils, - -{ TCustomLEDFunction } -constructor TCustomLEDFunction.Create; -begin - inherited Create; - - FObservers := TInterfaceList.Create; -end; - - -destructor TCustomLEDFunction.Destroy; -begin - FreeAndNil(FObservers); - - inherited Destroy; -end; - - -procedure TCustomLEDFunction.Attach(AObserver: IObserver); -begin - FObservers.Add(AObserver as IObserver); -end; - - -procedure TCustomLEDFunction.Detach(AObserver: IObserver); -begin - FObservers.Remove(AObserver as IObserver); -end; + LEDColorIntf, + LEDColorPool, + LEDState; { TCustomMultiStateLEDFunction } @@ -160,14 +147,79 @@ begin end; -//procedure TCustomMultiStateLEDFunction.SetCurrentState(AState: ILEDState); -//begin -// FCurrentState := AState; -// NotifyObservers; -//end; +{ TCustomLEDFunctionWorker } +constructor TCustomLEDFunctionWorker.Create; +begin + inherited Create; + + FObservers := TInterfaceList.Create; +end; -procedure TCustomLEDFunction.NotifyObservers; +constructor TCustomLEDFunctionWorker.Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); +begin + Create; + + FStates := TInterfaceList.Create; + RegisterStates(AStates, ASettings); +end; + + +destructor TCustomLEDFunctionWorker.Destroy; +begin + FreeAndNil(FStates); + FreeAndNil(FObservers); + + inherited Destroy; +end; + + +procedure TCustomLEDFunctionWorker.Attach(AObserver: IObserver); +begin + { TInterfaceList is thread-safe } + Observers.Add(AObserver as IObserver); +end; + + +procedure TCustomLEDFunctionWorker.Detach(AObserver: IObserver); +begin + Observers.Remove(AObserver as IObserver); +end; + + +procedure TCustomLEDFunctionWorker.RegisterStates(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); +var + state: ILEDState; + color: TLEDColor; + +begin + for state in AStates do + begin + if (not Assigned(ASettings)) or (not ASettings.GetStateColor(state.GetUID, color)) then + color := state.GetDefaultColor; + + States.Add(TLEDStateWorker.Create(state.GetUID, TLEDColorPool.GetColor(color))); + end; +end; + + +function TCustomLEDFunctionWorker.FindState(const AUID: string): ILEDStateWorker; +var + state: IInterface; + +begin + Result := nil; + + for state in States do + if (state as ICustomLEDState).GetUID = AUID then + begin + Result := (state as ILEDStateWorker); + break; + end; +end; + + +procedure TCustomLEDFunctionWorker.NotifyObservers; var observer: IInterface; diff --git a/G940LEDControl/Units/LEDFunctionIntf.pas b/G940LEDControl/Units/LEDFunctionIntf.pas index 4af1fbb..11394e9 100644 --- a/G940LEDControl/Units/LEDFunctionIntf.pas +++ b/G940LEDControl/Units/LEDFunctionIntf.pas @@ -9,6 +9,8 @@ uses type ILEDFunction = interface; + ILEDFunctionWorker = interface; + ILEDFunctionWorkerSettings = interface; ILEDFunctionEnumerator = interface; ILEDStateEnumerator = interface; @@ -22,13 +24,13 @@ type end; - ILEDFunction = interface(IObservable) + ILEDFunction = interface ['{7087067A-1016-4A7D-ACB1-BA1F388DAD6C}'] function GetCategoryName: string; function GetDisplayName: string; function GetUID: string; - function GetCurrentState: ILEDState; + function CreateWorker(ASettings: ILEDFunctionWorkerSettings): ILEDFunctionWorker; end; @@ -38,6 +40,18 @@ type end; + ILEDFunctionWorker = interface(IObservable) + ['{5EF3230D-B52F-4BD6-8AD3-F3A035F155B1}'] + function GetCurrentState: ILEDStateWorker; + end; + + + ILEDFunctionWorkerSettings = interface + ['{8FA287F6-9FE6-4A49-9C87-05C7F3F2B256}'] + function GetStateColor(const AUID: string; out AColor: TLEDColor): Boolean; + end; + + ILEDFunctionEnumerator = interface ['{A03E4E54-19CB-4C08-AD5F-20265817086D}'] function GetCurrent: ILEDFunction; diff --git a/G940LEDControl/Units/LEDResources.pas b/G940LEDControl/Units/LEDResources.pas new file mode 100644 index 0000000..c7e98d7 --- /dev/null +++ b/G940LEDControl/Units/LEDResources.pas @@ -0,0 +1,61 @@ +unit LEDResources; + +interface +uses + LEDColorIntf; + + +const + LEDColorUID: array[TLEDColor] of string = + ( + 'off', + 'green', + 'amber', + 'red', + 'green.flashing.fast', + 'green.flashing', + 'amber.flashing.fast', + 'amber.flashing', + 'red.flashing.fast', + 'red.flashing' + ); + + LEDColorDisplayName: array[TLEDColor] of string = + ( + 'Off', + 'Green', + 'Amber', + 'Red', + 'Flashing green (fast)', + 'Flashing green (normal)', + 'Flashing amber (fast)', + 'Flashing amber (normal)', + 'Flashing red (fast)', + 'Flashing red (normal)' + ); + + + function StringToLEDColor(const AValue: string; out AColor: TLEDColor): Boolean; + +implementation + + +function StringToLEDColor(const AValue: string; out AColor: TLEDColor): Boolean; +var + color: TLEDColor; + +begin + Result := False; + + for color := Low(TLEDColor) to High(TLEDColor) do + begin + if LEDColorUID[color] = AValue then + begin + Result := True; + AColor := color; + break; + end; + end; +end; + +end. diff --git a/G940LEDControl/Units/LEDState.pas b/G940LEDControl/Units/LEDState.pas index 446d43b..73a671f 100644 --- a/G940LEDControl/Units/LEDState.pas +++ b/G940LEDControl/Units/LEDState.pas @@ -7,33 +7,66 @@ uses type - TLEDState = class(TInterfacedObject, ILEDState) + TCustomLEDState = class(TInterfacedObject, ICustomLEDState) + private + FUID: string; + protected + { ICustomLEDState } + function GetUID: string; + public + constructor Create(const AUID: string); + end; + + + TLEDState = class(TCustomLEDState, ILEDState) private FDisplayName: string; - FUID: string; - FColor: ILEDColor; + FDefaultColor: TLEDColor; protected { ILEDState } function GetDisplayName: string; - function GetUID: string; + function GetDefaultColor: TLEDColor; + public + constructor Create(const AUID, ADisplayName: string; ADefaultColor: TLEDColor); + end; + + TLEDStateWorker = class(TCustomLEDState, ILEDStateWorker) + private + FColor: ILEDColor; + protected + { ILEDStateWorker } function GetColor: ILEDColor; public - constructor Create(const AUID, ADisplayName: string; AColor: ILEDColor); + constructor Create(const AUID: string; AColor: ILEDColor); end; implementation -{ TLEDState } -constructor TLEDState.Create(const AUID, ADisplayName: string; AColor: ILEDColor); + +{ TCustomLEDState } +constructor TCustomLEDState.Create(const AUID: string); begin inherited Create; FUID := AUID; - FDisplayName := ADisplayName; +end; - FColor := AColor; + +function TCustomLEDState.GetUID: string; +begin + Result := FUID; +end; + + +{ TLEDState } +constructor TLEDState.Create(const AUID, ADisplayName: string; ADefaultColor: TLEDColor); +begin + inherited Create(AUID); + + FDisplayName := ADisplayName; + FDefaultColor := ADefaultColor; end; @@ -43,13 +76,22 @@ begin end; -function TLEDState.GetUID: string; +function TLEDState.GetDefaultColor: TLEDColor; begin - Result := FUID; + Result := FDefaultColor; end; -function TLEDState.GetColor: ILEDColor; +{ TLEDStateWorker } +constructor TLEDStateWorker.Create(const AUID: string; AColor: ILEDColor); +begin + inherited Create(AUID); + + FColor := AColor; +end; + + +function TLEDStateWorker.GetColor: ILEDColor; begin Result := FColor; end; diff --git a/G940LEDControl/Units/LEDStateIntf.pas b/G940LEDControl/Units/LEDStateIntf.pas index 606b599..357a6e5 100644 --- a/G940LEDControl/Units/LEDStateIntf.pas +++ b/G940LEDControl/Units/LEDStateIntf.pas @@ -6,11 +6,21 @@ uses type - ILEDState = interface - ['{0361CBD5-E64E-4972-A8A4-D5FE0B0DFB1C}'] - function GetDisplayName: string; + ICustomLEDState = interface + ['{B5567129-74E1-4888-83F5-8A6174706671}'] function GetUID: string; + end; + + ILEDState = interface(ICustomLEDState) + ['{2C91D49C-2B67-42A3-B5EF-475976DD33F8}'] + function GetDisplayName: string; + function GetDefaultColor: TLEDColor; + end; + + + ILEDStateWorker = interface(ICustomLEDState) + ['{0361CBD5-E64E-4972-A8A4-D5FE0B0DFB1C}'] function GetColor: ILEDColor; end; diff --git a/G940LEDControl/Units/Profile.pas b/G940LEDControl/Units/Profile.pas index 1dfc8df..96c8ea3 100644 --- a/G940LEDControl/Units/Profile.pas +++ b/G940LEDControl/Units/Profile.pas @@ -4,18 +4,33 @@ interface uses Generics.Collections, - X2UtPersistIntf; + X2UtPersistIntf, + + LEDColorIntf; type + TProfileButtonStateColors = TDictionary; + TProfileButton = class(TObject) private FProviderUID: string; FFunctionUID: string; + + FStateColors: TProfileButtonStateColors; protected function Load(AReader: IX2PersistReader): Boolean; procedure Save(AWriter: IX2PersistWriter); + + property StateColors: TProfileButtonStateColors read FStateColors; public + constructor Create; + destructor Destroy; override; + + procedure ClearStateColors; + function GetStateColor(const AStateUID: string; out AValue: TLEDColor): Boolean; + procedure SetStateColor(const AStateUID: string; const AValue: TLEDColor); + property ProviderUID: string read FProviderUID write FProviderUID; property FunctionUID: string read FFunctionUID write FFunctionUID; end; @@ -57,29 +72,106 @@ type implementation uses Classes, - SysUtils; + SysUtils, + + LEDResources; const SectionProfiles = 'Profiles'; SectionButton = 'Button'; + SectionStates = 'States'; KeyProviderUID = 'ProviderUID'; KeyFunctionUID = 'FunctionUID'; { TProfileButton } +constructor TProfileButton.Create; +begin + inherited Create; + + FStateColors := TProfileButtonStateColors.Create; +end; + + +destructor TProfileButton.Destroy; +begin + FreeAndNil(FStateColors); + + inherited Destroy; +end; + + +procedure TProfileButton.ClearStateColors; +begin + FStateColors.Clear; +end; + + +function TProfileButton.GetStateColor(const AStateUID: string; out AValue: TLEDColor): Boolean; +begin + Result := StateColors.TryGetValue(AStateUID, AValue); +end; + + +procedure TProfileButton.SetStateColor(const AStateUID: string; const AValue: TLEDColor); +begin + StateColors.AddOrSetValue(AStateUID, AValue); +end; + + function TProfileButton.Load(AReader: IX2PersistReader): Boolean; +var + stateUIDs: TStringList; + stateUID: string; + colorUID: string; + color: TLEDColor; + begin Result := AReader.ReadString(KeyProviderUID, FProviderUID) and AReader.ReadString(KeyFunctionUID, FFunctionUID); + + if Result and AReader.BeginSection(SectionStates) then + try + StateColors.Clear; + + stateUIDs := TStringList.Create; + try + AReader.GetKeys(stateUIDs); + + for stateUID in stateUIDs do + begin + if AReader.ReadString(stateUID, colorUID) and + StringToLEDColor(colorUID, color) then + begin + StateColors.Add(stateUID, color); + end; + end; + finally + FreeAndNil(stateUIDs); + end; + finally + AReader.EndSection; + end; end; procedure TProfileButton.Save(AWriter: IX2PersistWriter); +var + stateUID: string; + begin AWriter.WriteString(KeyProviderUID, FProviderUID); AWriter.WriteString(KeyFunctionUID, FFunctionUID); + + if AWriter.BeginSection(SectionStates) then + try + for stateUID in StateColors.Keys do + AWriter.WriteString(stateUID, LEDColorUID[StateColors[stateUID]]); + finally + AWriter.EndSection; + end; end; diff --git a/G940LEDControl/Units/StaticLEDFunction.pas b/G940LEDControl/Units/StaticLEDFunction.pas index 02864ec..1bdced1 100644 --- a/G940LEDControl/Units/StaticLEDFunction.pas +++ b/G940LEDControl/Units/StaticLEDFunction.pas @@ -3,6 +3,7 @@ unit StaticLEDFunction; interface uses LEDFunction, + LEDFunctionIntf, LEDColorIntf, LEDStateIntf; @@ -19,13 +20,12 @@ type TStaticLEDFunction = class(TCustomLEDFunction) private FColor: TLEDColor; - FState: ILEDState; protected function GetCategoryName: string; override; function GetDisplayName: string; override; function GetUID: string; override; - function GetCurrentState: ILEDState; override; + function CreateWorker(ASettings: ILEDFunctionWorkerSettings): ILEDFunctionWorker; override; public constructor Create(AColor: TLEDColor); end; @@ -39,13 +39,24 @@ uses StaticResources; +type + TStaticLEDFunctionWorker = class(TCustomLEDFunctionWorker) + private + FState: ILEDStateWorker; + protected + function GetCurrentState: ILEDStateWorker; override; + public + constructor Create(AColor: TLEDColor); + end; + + { TStaticLEDFunctionProvider } procedure TStaticLEDFunctionProvider.RegisterFunctions; var color: TLEDColor; begin - for color := Low(TLEDColor) to High(TLEDColor) do + for color := Low(TStaticLEDColor) to High(TStaticLEDColor) do RegisterFunction(TStaticLEDFunction.Create(color)); end; @@ -83,15 +94,26 @@ begin end; -function TStaticLEDFunction.GetCurrentState: ILEDState; +function TStaticLEDFunction.CreateWorker(ASettings: ILEDFunctionWorkerSettings): ILEDFunctionWorker; begin - if not Assigned(FState) then - FState := TLEDState.Create('', '', TLEDColorPool.GetColor(FColor)); - - Result := FState; + Result := TStaticLEDFunctionWorker.Create(FColor); end; +{ TStaticLEDFunctionWorker } +constructor TStaticLEDFunctionWorker.Create(AColor: TLEDColor); +begin + inherited Create; + + FState := TLEDStateWorker.Create('', TLEDColorPool.GetColor(AColor)); +end; + + +function TStaticLEDFunctionWorker.GetCurrentState: ILEDStateWorker; +begin + Result := FState; +end; + initialization TLEDFunctionRegistry.Register(TStaticLEDFunctionProvider.Create); diff --git a/G940LEDControl/Units/StaticResources.pas b/G940LEDControl/Units/StaticResources.pas index b3d2588..b6bf052 100644 --- a/G940LEDControl/Units/StaticResources.pas +++ b/G940LEDControl/Units/StaticResources.pas @@ -7,7 +7,7 @@ uses const StaticProviderUID = 'static'; - StaticFunctionUID: array[TLEDColor] of string = + StaticFunctionUID: array[TStaticLEDColor] of string = ( 'off', 'green', @@ -17,7 +17,7 @@ const StaticCategory = 'Static'; - StaticFunctionDisplayName: array[TLEDColor] of string = + StaticFunctionDisplayName: array[TStaticLEDColor] of string = ( 'Off', 'Green', From b66659cd956ddc6c03b2ce11007a7d68d0563e54 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Mon, 18 Feb 2013 21:46:32 +0000 Subject: [PATCH 09/16] Added: engine, landing gear and light state functions as an implementation proof of concept Added: IFSXSimConnect interface and data handling prototype --- G940LEDControl/G940LEDControl.dpr | 1 - G940LEDControl/G940LEDControl.dproj | 1 - G940LEDControl/Units/FSXLEDFunction.pas | 366 ++++++++++++++++++ .../Units/FSXLEDFunctionProvider.pas | 164 ++++---- G940LEDControl/Units/FSXResources.pas | 50 ++- G940LEDControl/Units/FSXSimConnectClient.pas | 43 +- G940LEDControl/Units/FSXSimConnectIntf.pas | 29 +- G940LEDControl/Units/LEDFunction.pas | 40 +- G940LEDControl/Units/LEDFunctionIntf.pas | 14 +- 9 files changed, 614 insertions(+), 94 deletions(-) create mode 100644 G940LEDControl/Units/FSXLEDFunction.pas diff --git a/G940LEDControl/G940LEDControl.dpr b/G940LEDControl/G940LEDControl.dpr index 73926ad..313a0e4 100644 --- a/G940LEDControl/G940LEDControl.dpr +++ b/G940LEDControl/G940LEDControl.dpr @@ -13,7 +13,6 @@ uses LEDColorIntf in 'Units\LEDColorIntf.pas', LEDColor in 'Units\LEDColor.pas', LEDFunctionIntf in 'Units\LEDFunctionIntf.pas', - ObserverIntf in 'Units\ObserverIntf.pas', LEDFunction in 'Units\LEDFunction.pas', StaticLEDFunction in 'Units\StaticLEDFunction.pas', ConfigConversion in 'Units\ConfigConversion.pas', diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj index 6e8807e..88044e5 100644 --- a/G940LEDControl/G940LEDControl.dproj +++ b/G940LEDControl/G940LEDControl.dproj @@ -173,7 +173,6 @@ - diff --git a/G940LEDControl/Units/FSXLEDFunction.pas b/G940LEDControl/Units/FSXLEDFunction.pas new file mode 100644 index 0000000..0f8c13c --- /dev/null +++ b/G940LEDControl/Units/FSXLEDFunction.pas @@ -0,0 +1,366 @@ +unit FSXLEDFunction; + +interface +uses + FSXLEDFunctionProvider, + LEDFunction, + LEDFunctionIntf; + + +type + { Base classes } + TFSXOnOffFunction = class(TCustomFSXFunction) + protected + procedure RegisterStates; override; + end; + + + { Function implementations } + TFSXEngineFunction = class(TCustomFSXFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + TFSXGearFunction = class(TCustomFSXFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + + TFSXLightFunction = class(TFSXOnOffFunction) + protected + function GetCategoryName: string; override; + + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + function DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; override; + protected + function GetLightMask: Integer; virtual; abstract; + end; + + TFSXLandingLightsFunction = class(TFSXLightFunction) + protected + function GetLightMask: Integer; override; + end; + + TFSXInstrumentLightsFunction = class(TFSXLightFunction) + protected + function GetLightMask: Integer; override; + end; + + TFSXBeaconLightsFunction = class(TFSXLightFunction) + protected + function GetLightMask: Integer; override; + end; + + TFSXNavLightsFunction = class(TFSXLightFunction) + protected + function GetLightMask: Integer; override; + end; + + TFSXStrobeLightsFunction = class(TFSXLightFunction) + protected + function GetLightMask: Integer; override; + end; + + TFSXTaxiLightsFunction = class(TFSXLightFunction) + protected + function GetLightMask: Integer; override; + end; + + TFSXRecognitionLightsFunction = class(TFSXLightFunction) + protected + function GetLightMask: Integer; override; + end; + + +implementation +uses + System.Math, + System.SysUtils, + + FSXSimConnectIntf, + FSXResources, + LEDColorIntf, + LEDState, + LEDStateIntf, + SimConnect; + + +type + { Worker implementations } + TFSXEngineFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables; override; + procedure HandleData(AData: Pointer); override; + end; + + TFSXGearFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables; override; + procedure HandleData(AData: Pointer); override; + end; + + TFSXLightStatesFunctionWorker = class(TCustomFSXFunctionWorker) + private + FStateMask: Integer; + protected + procedure RegisterVariables; override; + procedure HandleData(AData: Pointer); override; + public + property StateMask: Integer read FStateMask write FStateMask; + end; + + +{ TFSXOnOffFunction } +procedure TFSXOnOffFunction.RegisterStates; +begin + RegisterState(TLEDState.Create(FSXStateUIDOn, FSXStateDisplayNameOn, lcGreen)); + RegisterState(TLEDState.Create(FSXStateUIDOff, FSXStateDisplayNameOff, lcRed)); +end; + + +{ TFSXEngineFunction } +procedure TFSXEngineFunction.RegisterStates; +begin + RegisterState(TLEDState.Create(FSXStateUIDEngineNoEngines, FSXStateDisplayNameEngineNoEngines, lcOff)); + RegisterState(TLEDState.Create(FSXStateUIDEngineAllRunning, FSXStateDisplayNameEngineAllRunning, lcGreen)); + RegisterState(TLEDState.Create(FSXStateUIDEnginePartiallyRunning, FSXStateDisplayNameEnginePartiallyRunning, lcAmber)); + RegisterState(TLEDState.Create(FSXStateUIDEngineAllOff, FSXStateDisplayNameEngineAllOff, lcRed)); + RegisterState(TLEDState.Create(FSXStateUIDEngineFailed, FSXStateDisplayNameEngineFailed, lcFlashingRedNormal)); + RegisterState(TLEDState.Create(FSXStateUIDEngineOnFire, FSXStateDisplayNameEngineOnFire, lcFlashingRedFast)); +end; + + +function TFSXEngineFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXEngineFunctionWorker; +end; + + +{ TFSXEngineFunctionWorker } +procedure TFSXEngineFunctionWorker.RegisterVariables; +var + engineIndex: Integer; + +begin + Definition.AddVariable('NUMBER OF ENGINES', FSX_UNIT_NUMBER, SIMCONNECT_DATAType_INT32); + + for engineIndex := 1 to FSX_MAX_ENGINES do + Definition.AddVariable(Format('GENERAL ENG COMBUSTION:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + + for engineIndex := 1 to FSX_MAX_ENGINES do + Definition.AddVariable(Format('ENG FAILED:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + + for engineIndex := 1 to FSX_MAX_ENGINES do + Definition.AddVariable(Format('ENG ON FIRE:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); +end; + + +procedure TFSXEngineFunctionWorker.HandleData(AData: Pointer); +type + PEngineData = ^TEngineData; + TEngineData = packed record + NumberOfEngines: Integer; + Combustion: array[1..FSX_MAX_ENGINES] of Integer; + Failed: array[1..FSX_MAX_ENGINES] of Integer; + OnFire: array[1..FSX_MAX_ENGINES] of Integer; + end; + +var + engineData: PEngineData; + engineCount: Integer; + engineIndex: Integer; + hasFire: Boolean; + hasFailure: Boolean; + runningCount: Integer; + +begin + engineData := AData; + + if engineData^.NumberOfEngines > 0 then + begin + engineCount := Min(engineData^.NumberOfEngines, FSX_MAX_ENGINES); + hasFire := False; + hasFailure := False; + runningCount := 0; + + for engineIndex := 1 to engineCount do + begin + if engineData^.OnFire[engineIndex] <> 0 then + hasFire := True; + + if engineData^.Failed[engineIndex] <> 0 then + hasFailure := True; + + if engineData^.Combustion[engineIndex] <> 0 then + Inc(runningCount); + end; + + if hasFire then + SetCurrentState(FSXStateUIDEngineOnFire) + + else if hasFailure then + SetCurrentState(FSXStateUIDEngineFailed) + + else if runningCount = 0 then + SetCurrentState(FSXStateUIDEngineAllOff) + + else if runningCount = engineCount then + SetCurrentState(FSXStateUIDEngineAllRunning) + + else + SetCurrentState(FSXStateUIDEnginePartiallyRunning); + end else + SetCurrentState(FSXStateUIDEngineNoEngines); +end; + + +{ TFSXGearFunction } +procedure TFSXGearFunction.RegisterStates; +begin + RegisterState(TLEDState.Create(FSXStateUIDGearNotRetractable, FSXStateDisplayNameGearNotRetractable, lcOff)); + RegisterState(TLEDState.Create(FSXStateUIDGearRetracted, FSXStateDisplayNameGearRetracted, lcRed)); + RegisterState(TLEDState.Create(FSXStateUIDGearBetween, FSXStateDisplayNameGearBetween, lcAmber)); + RegisterState(TLEDState.Create(FSXStateUIDGearExtended, FSXStateDisplayNameGearExtended, lcGreen)); + RegisterState(TLEDState.Create(FSXStateUIDGearSpeedExceeded, FSXStateDisplayNameGearSpeedExceeded, lcFlashingAmberNormal)); + RegisterState(TLEDState.Create(FSXStateUIDGearDamageBySpeed, FSXStateDisplayNameGearDamageBySpeed, lcFlashingRedFast)); +end; + + +function TFSXGearFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXGearFunctionWorker; +end; + + +{ TFSXGearFunctionWorker } +procedure TFSXGearFunctionWorker.RegisterVariables; +begin + Definition.AddVariable('IS GEAR RETRACTABLE', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + Definition.AddVariable('GEAR TOTAL PCT EXTENDED', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); + Definition.AddVariable('GEAR DAMAGE BY SPEED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + Definition.AddVariable('GEAR SPEED EXCEEDED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); +end; + + +procedure TFSXGearFunctionWorker.HandleData(AData: Pointer); +type + PGearData = ^TGearData; + TGearData = packed record + IsGearRetractable: Cardinal; + TotalPctExtended: Double; + DamageBySpeed: Integer; + SpeedExceeded: Integer; + end; + +var + gearData: PGearData; + +begin + gearData := AData; + + if gearData^.DamageBySpeed <> 0 then + SetCurrentState(FSXStateUIDGearDamageBySpeed) + + else if gearData^.SpeedExceeded <> 0 then + SetCurrentState(FSXStateUIDGearSpeedExceeded) + + else if gearData^.IsGearRetractable <> 0 then + begin + case Trunc(gearData ^.TotalPctExtended * 100) of + 0: SetCurrentState(FSXStateUIDGearRetracted); + 95..100: SetCurrentState(FSXStateUIDGearExtended); + else SetCurrentState(FSXStateUIDGearBetween); + end; + end else + SetCurrentState(FSXStateUIDGearNotRetractable); +end; + + +{ TFSXLightFunction } +function TFSXLightFunction.GetCategoryName: string; +begin + Result := FSXCategoryLights; +end; + + +function TFSXLightFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXLightStatesFunctionWorker; +end; + + +function TFSXLightFunction.DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; +begin + Result := inherited DoCreateWorker(ASettings); + (Result as TFSXLightStatesFunctionWorker).StateMask := GetLightMask; +end; + + +{ TFSXLandingLightsFunction } +function TFSXLandingLightsFunction.GetLightMask: Integer; +begin + Result := FSX_LIGHTON_LANDING; +end; + + +{ TFSXInstrumentLightsFunction } +function TFSXInstrumentLightsFunction.GetLightMask: Integer; +begin + Result := FSX_LIGHTON_PANEL; +end; + + +{ TFSXBeaconLightsFunction } +function TFSXBeaconLightsFunction.GetLightMask: Integer; +begin + Result := FSX_LIGHTON_BEACON; +end; + + +{ TFSXNavLightsFunction } +function TFSXNavLightsFunction.GetLightMask: Integer; +begin + Result := FSX_LIGHTON_NAV; +end; + + +{ TFSXStrobeLightsFunction } +function TFSXStrobeLightsFunction.GetLightMask: Integer; +begin + Result := FSX_LIGHTON_STROBE; +end; + + +{ TFSXTaxiLightsFunction } +function TFSXTaxiLightsFunction.GetLightMask: Integer; +begin + Result := FSX_LIGHTON_TAXI; +end; + + +{ TFSXRecognitionLightsFunction } +function TFSXRecognitionLightsFunction.GetLightMask: Integer; +begin + Result := FSX_LIGHTON_RECOGNITION; +end; + + +{ TFSXLightStatesFunctionWorker } +procedure TFSXLightStatesFunctionWorker.RegisterVariables; +begin + Definition.AddVariable('LIGHT ON STATES', FSX_UNIT_MASK, SIMCONNECT_DATATYPE_INT32); +end; + + +procedure TFSXLightStatesFunctionWorker.HandleData(AData: Pointer); +begin + if (PCardinal(AData)^ and StateMask) <> 0 then + SetCurrentState(FSXStateUIDOn) + else + SetCurrentState(FSXStateUIDOff); +end; + +end. diff --git a/G940LEDControl/Units/FSXLEDFunctionProvider.pas b/G940LEDControl/Units/FSXLEDFunctionProvider.pas index c477692..19659e1 100644 --- a/G940LEDControl/Units/FSXLEDFunctionProvider.pas +++ b/G940LEDControl/Units/FSXLEDFunctionProvider.pas @@ -8,8 +8,7 @@ uses FSXSimConnectIntf, LEDFunction, LEDFunctionIntf, - LEDStateIntf, - ObserverIntf; + LEDStateIntf; type @@ -17,24 +16,21 @@ type TCustomFSXFunctionList = TObjectList; - TFSXLEDFunctionProvider = class(TCustomLEDFunctionProvider) + TFSXLEDFunctionProvider = class(TCustomLEDFunctionProvider, IFSXSimConnectObserver) private - FConnectedFunctions: TCustomFSXFunctionList; - FSimConnectHandle: THandle; - protected - procedure SimConnect; - procedure SimDisconnect; - - procedure Connect(AFunction: TCustomFSXFunction); virtual; - procedure Disconnect(AFunction: TCustomFSXFunction); virtual; - - property ConnectedFunctions: TCustomFSXFunctionList read FConnectedFunctions; - property SimConnectHandle: THandle read FSimConnectHandle; + FSimConnect: IFSXSimConnect; + FSimConnectLock: TCriticalSection; protected procedure RegisterFunctions; override; function GetUID: string; override; + protected + { IFSXSimConnectObserver } + procedure ObserveDestroy(Sender: IFSXSimConnect); public + constructor Create; + destructor Destroy; override; + function GetSimConnect: IFSXSimConnect; end; @@ -45,9 +41,6 @@ type FDisplayName: string; FUID: string; protected - procedure SimConnected; virtual; - procedure SimDisconnected; virtual; - property Provider: TFSXLEDFunctionProvider read FProvider; protected function GetCategoryName: string; override; @@ -58,7 +51,10 @@ type end; - TCustomFSXFunctionWorker = class(TCustomLEDFunctionWorker) + TCustomFSXFunctionClass = class of TCustomFSXFunction; + + + TCustomFSXFunctionWorker = class(TCustomLEDFunctionWorker, IFSXSimConnectDataHandler) private FSimConnect: IFSXSimConnect; FDefinition: IFSXSimConnectDefinition; @@ -73,6 +69,9 @@ type property SimConnect: IFSXSimConnect read FSimConnect; protected function GetCurrentState: ILEDStateWorker; override; + + { IFSXSimConnectDataHandler } + procedure HandleData(AData: Pointer); virtual; abstract; public constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings; ASimConnect: IFSXSimConnect); destructor Destroy; override; @@ -85,15 +84,63 @@ uses FSXLEDFunction, FSXResources, + FSXSimConnectClient, LEDFunctionRegistry, SimConnect; { TFSXLEDFunctionProvider } +constructor TFSXLEDFunctionProvider.Create; +begin + inherited Create; + + FSimConnectLock := TCriticalSection.Create; +end; + + +destructor TFSXLEDFunctionProvider.Destroy; +begin + FreeAndNil(FSimConnectLock); + + inherited Destroy; +end; + + procedure TFSXLEDFunctionProvider.RegisterFunctions; begin - RegisterFunction(TFSXGearFunction.Create(Self, FSXFunctionDisplayNameGear, FSXFunctionUIDGear)); + { + AConsumer.AddFunction(FUNCTION_FSX_CARBHEAT, 'Anti-ice'); + AConsumer.AddFunction(FUNCTION_FSX_AUTOPILOT, 'Auto pilot (main)'); + AConsumer.AddFunction(FUNCTION_FSX_AUTOPILOT_AMBER, 'Auto pilot (main - off / amber)'); + AConsumer.AddFunction(FUNCTION_FSX_AUTOPILOT_ALTITUDE, 'Auto pilot - Altitude'); + AConsumer.AddFunction(FUNCTION_FSX_AUTOPILOT_APPROACH, 'Auto pilot - Approach'); + AConsumer.AddFunction(FUNCTION_FSX_AUTOPILOT_BACKCOURSE, 'Auto pilot - Backcourse'); + AConsumer.AddFunction(FUNCTION_FSX_AUTOPILOT_HEADING, 'Auto pilot - Heading'); + AConsumer.AddFunction(FUNCTION_FSX_AUTOPILOT_NAV, 'Auto pilot - Nav'); + AConsumer.AddFunction(FUNCTION_FSX_AVIONICSMASTER, 'Avionics master switch'); + AConsumer.AddFunction(FUNCTION_FSX_BATTERYMASTER, 'Battery master switch'); + AConsumer.AddFunction(FUNCTION_FSX_ENGINE, 'Engine'); + AConsumer.AddFunction(FUNCTION_FSX_EXITDOOR, 'Exit door'); + AConsumer.AddFunction(FUNCTION_FSX_FLAPS, 'Flaps'); + AConsumer.AddFunction(FUNCTION_FSX_PARKINGBRAKE, 'Parking brake'); + AConsumer.AddFunction(FUNCTION_FSX_PRESSURIZATIONDUMPSWITCH, 'Pressurization dump switch'); + AConsumer.AddFunction(FUNCTION_FSX_SPOILERS, 'Spoilers (air brake)'); + AConsumer.AddFunction(FUNCTION_FSX_TAILHOOK, 'Tail hook'); + } + + { Misc } + RegisterFunction(TFSXEngineFunction.Create( Self, FSXFunctionDisplayNameEngine, FSXFunctionUIDEngine)); + RegisterFunction(TFSXGearFunction.Create( Self, FSXFunctionDisplayNameGear, FSXFunctionUIDGear)); + + { Lights } + RegisterFunction(TFSXBeaconLightsFunction.Create( Self, FSXFunctionDisplayNameBeaconLights, FSXFunctionUIDBeaconLights)); + RegisterFunction(TFSXInstrumentLightsFunction.Create( Self, FSXFunctionDisplayNameInstrumentLights, FSXFunctionUIDInstrumentLights)); + RegisterFunction(TFSXLandingLightsFunction.Create( Self, FSXFunctionDisplayNameLandingLights, FSXFunctionUIDLandingLights)); + RegisterFunction(TFSXNavLightsFunction.Create( Self, FSXFunctionDisplayNameNavLights, FSXFunctionUIDNavLights)); + RegisterFunction(TFSXRecognitionLightsFunction.Create(Self, FSXFunctionDisplayNameRecognitionLights, FSXFunctionUIDRecognitionLights)); + RegisterFunction(TFSXStrobeLightsFunction.Create( Self, FSXFunctionDisplayNameStrobeLights, FSXFunctionUIDStrobeLights)); + RegisterFunction(TFSXTaxiLightsFunction.Create( Self, FSXFunctionDisplayNameTaxiLights, FSXFunctionUIDTaxiLights)); end; @@ -103,57 +150,34 @@ begin end; +procedure TFSXLEDFunctionProvider.ObserveDestroy(Sender: IFSXSimConnect); +begin + FSimConnectLock.Acquire; + try + FSimConnect := nil; + finally + FSimConnectLock.Release; + end; +end; + + function TFSXLEDFunctionProvider.GetSimConnect: IFSXSimConnect; begin - // TODO -end; + FSimConnectLock.Acquire; + try + if not Assigned(FSimConnect) then + begin + FSimConnect := TFSXSimConnectInterface.Create; + FSimConnect.Attach(Self); + end; - -procedure TFSXLEDFunctionProvider.SimConnect; -var - fsxFunction: TCustomFSXFunction; - -begin - if SimConnectHandle <> 0 then - exit; - -// FSimConnectHandle := - - if SimConnectHandle <> 0 then - begin - for fsxFunction in ConnectedFunctions do - fsxFunction.SimConnected; + Result := FSimConnect; + finally + FSimConnectLock.Release; end; end; -procedure TFSXLEDFunctionProvider.SimDisconnect; -begin - if SimConnectHandle = 0 then - exit; -end; - - -procedure TFSXLEDFunctionProvider.Connect(AFunction: TCustomFSXFunction); -begin - if ConnectedFunctions.IndexOf(AFunction) = -1 then - begin - ConnectedFunctions.Add(AFunction); - - if ConnectedFunctions.Count > 0 then - SimConnect; - end; -end; - -procedure TFSXLEDFunctionProvider.Disconnect(AFunction: TCustomFSXFunction); -begin - ConnectedFunctions.Remove(AFunction); - - if ConnectedFunctions.Count = 0 then - SimDisconnect; -end; - - { TCustomFSXFunction } constructor TCustomFSXFunction.Create(AProvider: TFSXLEDFunctionProvider; const ADisplayName, AUID: string); begin @@ -183,16 +207,6 @@ begin end; -procedure TCustomFSXFunction.SimConnected; -begin -end; - - -procedure TCustomFSXFunction.SimDisconnected; -begin -end; - - { TCustomFSXFunctionWorker } constructor TCustomFSXFunctionWorker.Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings; ASimConnect: IFSXSimConnect); begin @@ -203,7 +217,9 @@ begin FDefinition := ASimConnect.CreateDefinition; RegisterVariables; - ASimConnect.AddDefinition(FDefinition); + + // TODO pass self as callback for this definition + ASimConnect.AddDefinition(FDefinition, Self); end; diff --git a/G940LEDControl/Units/FSXResources.pas b/G940LEDControl/Units/FSXResources.pas index 969913e..d1dad2a 100644 --- a/G940LEDControl/Units/FSXResources.pas +++ b/G940LEDControl/Units/FSXResources.pas @@ -3,18 +3,45 @@ unit FSXResources; interface const FSXProviderUID = 'fsx'; - FSXCategory = 'Flight Simulator X'; + FSXCategoryLights = FSXCategory + ' - Lights'; + + FSXStateUIDOn = 'on'; + FSXStateUIDOff = 'off'; + + FSXStateDisplayNameOn = 'On'; + FSXStateDisplayNameOff = 'Off'; + + + FSXFunctionUIDEngine = 'engine'; + FSXFunctionDisplayNameEngine = 'Engine'; + + FSXStateUIDEngineNoEngines = 'noEngines'; + FSXStateUIDEngineAllRunning = 'allRunning'; + FSXStateUIDEnginePartiallyRunning = 'partiallyRunning'; + FSXStateUIDEngineAllOff = 'allOff'; + FSXStateUIDEngineFailed = 'failed'; + FSXStateUIDEngineOnFire = 'onFire'; + + FSXStateDisplayNameEngineNoEngines = 'No engines'; + FSXStateDisplayNameEngineAllRunning = 'All running'; + FSXStateDisplayNameEnginePartiallyRunning = 'Partially running'; + FSXStateDisplayNameEngineAllOff = 'All off'; + FSXStateDisplayNameEngineFailed = 'Engine failure'; + FSXStateDisplayNameEngineOnFire = 'On fire'; + FSXFunctionUIDGear = 'gear'; FSXFunctionDisplayNameGear = 'Landing gear'; + FSXStateUIDGearNotRetractable = 'notRetractable'; FSXStateUIDGearRetracted = 'retracted'; FSXStateUIDGearBetween = 'between'; FSXStateUIDGearExtended = 'extended'; FSXStateUIDGearSpeedExceeded = 'speedExceeded'; FSXStateUIDGearDamageBySpeed = 'damageBySpeed'; + FSXStateDisplayNameGearNotRetractable = 'Not retractable'; FSXStateDisplayNameGearRetracted = 'Retracted'; FSXStateDisplayNameGearBetween = 'Extending / retracting'; FSXStateDisplayNameGearExtended = 'Extended'; @@ -22,6 +49,27 @@ const FSXStateDisplayNameGearDamageBySpeed = 'Damage by speed'; + FSXFunctionUIDLandingLights = 'landingLights'; + FSXFunctionDisplayNameLandingLights = 'Landing lights'; + + FSXFunctionUIDInstrumentLights = 'instrumentLights'; + FSXFunctionDisplayNameInstrumentLights = 'Instrument lights'; + + FSXFunctionUIDBeaconLights = 'beaconLights'; + FSXFunctionDisplayNameBeaconLights = 'Beacon lights'; + + FSXFunctionUIDNavLights = 'navLights'; + FSXFunctionDisplayNameNavLights = 'Nav lights'; + + FSXFunctionUIDStrobeLights = 'strobeLights'; + FSXFunctionDisplayNameStrobeLights = 'Strobe lights'; + + FSXFunctionUIDTaxiLights = 'taxiLights'; + FSXFunctionDisplayNameTaxiLights = 'Taxi lights'; + + FSXFunctionUIDRecognitionLights = 'recognitionLights'; + FSXFunctionDisplayNameRecognitionLights = 'Recognition lights'; + implementation diff --git a/G940LEDControl/Units/FSXSimConnectClient.pas b/G940LEDControl/Units/FSXSimConnectClient.pas index 4cb8fc8..e55d7e5 100644 --- a/G940LEDControl/Units/FSXSimConnectClient.pas +++ b/G940LEDControl/Units/FSXSimConnectClient.pas @@ -2,20 +2,29 @@ unit FSXSimConnectClient; interface uses + Classes, + OtlTaskControl, FSXSimConnectIntf; + type TFSXSimConnectInterface = class(TInterfacedObject, IFSXSimConnect) private FClient: IOmniTaskControl; + FObservers: TInterfaceList; protected property Client: IOmniTaskControl read FClient; + property Observers: TInterfaceList read FObservers; protected { IFSXSimConnect } + procedure Attach(AObserver: IFSXSimConnectObserver); + procedure Detach(AObserver: IFSXSimConnectObserver); + function CreateDefinition: IFSXSimConnectDefinition; - procedure AddDefinition(ADefinition: IFSXSimConnectDefinition); + procedure AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler); + procedure RemoveDefinition(ADataHandler: IFSXSimConnectDataHandler); public constructor Create; destructor Destroy; override; @@ -29,6 +38,11 @@ uses SimConnect; +const + TM_ADDDEFINITION = 3001; + TM_REMOVEDEFINITION = 3002; + + type TFSXSimConnectClient = class(TOmniWorker) end; @@ -58,11 +72,15 @@ var begin worker := TFSXSimConnectClient.Create; FClient := CreateTask(worker); + + FObservers := TInterfaceList.Create; end; destructor TFSXSimConnectInterface.Destroy; begin + FreeAndNil(FObservers); + FClient.Terminate; FClient := nil; @@ -70,15 +88,34 @@ begin end; +procedure TFSXSimConnectInterface.Attach(AObserver: IFSXSimConnectObserver); +begin + Observers.Add(AObserver as IFSXSimConnectObserver); +end; + + +procedure TFSXSimConnectInterface.Detach(AObserver: IFSXSimConnectObserver); +begin + Observers.Remove(AObserver as IFSXSimConnectObserver); +end; + + function TFSXSimConnectInterface.CreateDefinition: IFSXSimConnectDefinition; begin Result := TFSXSimConnectDefinition.Create(Self); end; -procedure TFSXSimConnectInterface.AddDefinition(ADefinition: IFSXSimConnectDefinition); +procedure TFSXSimConnectInterface.AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler); begin - // TODO + Client.Comm.Send(TM_ADDDEFINITION, [ADefinition, ADataHandler]); + // TODO pass to thread; if definition already exists (same variables), link to existing definition to avoid too many SimConnect definition +end; + + +procedure TFSXSimConnectInterface.RemoveDefinition(ADataHandler: IFSXSimConnectDataHandler); +begin + Client.Comm.Send(TM_REMOVEDEFINITION, ADataHandler); end; diff --git a/G940LEDControl/Units/FSXSimConnectIntf.pas b/G940LEDControl/Units/FSXSimConnectIntf.pas index 46704de..928a9d9 100644 --- a/G940LEDControl/Units/FSXSimConnectIntf.pas +++ b/G940LEDControl/Units/FSXSimConnectIntf.pas @@ -6,13 +6,30 @@ uses type + IFSXSimConnect = interface; IFSXSimConnectDefinition = interface; + IFSXSimConnectObserver = interface + ['{ACE8979A-D656-4F97-A332-A54BB615C4D1}'] + procedure ObserveDestroy(Sender: IFSXSimConnect); + end; + + + IFSXSimConnectDataHandler = interface + ['{29F00FB8-00AB-419F-83A3-A6AB3582599F}'] + procedure HandleData(AData: Pointer); + end; + + IFSXSimConnect = interface ['{B6BE3E7C-0804-43D6-84DE-8010C5728A07}'] + procedure Attach(AObserver: IFSXSimConnectObserver); + procedure Detach(AObserver: IFSXSimConnectObserver); + function CreateDefinition: IFSXSimConnectDefinition; - procedure AddDefinition(ADefinition: IFSXSimConnectDefinition); + procedure AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler); + procedure RemoveDefinition(ADataHandler: IFSXSimConnectDataHandler); end; @@ -30,6 +47,16 @@ const FSX_UNIT_BOOL = 'bool'; FSX_UNIT_NUMBER = 'number'; + FSX_LIGHTON_NAV = $0001; + FSX_LIGHTON_BEACON = $0002; + FSX_LIGHTON_LANDING = $0004; + FSX_LIGHTON_TAXI = $0008; + FSX_LIGHTON_STROBE = $0010; + FSX_LIGHTON_PANEL = $0020; + FSX_LIGHTON_RECOGNITION = $0040; + FSX_LIGHTON_CABIN = $0200; + + FSX_MAX_ENGINES = 4; implementation diff --git a/G940LEDControl/Units/LEDFunction.pas b/G940LEDControl/Units/LEDFunction.pas index 7d9bdf2..247ced4 100644 --- a/G940LEDControl/Units/LEDFunction.pas +++ b/G940LEDControl/Units/LEDFunction.pas @@ -5,11 +5,14 @@ uses Classes, LEDFunctionIntf, - LEDStateIntf, - ObserverIntf; + LEDStateIntf; type + TCustomLEDFunctionWorker = class; + TCustomLEDFunctionWorkerClass = class of TCustomLEDFunctionWorker; + + TCustomLEDFunctionProvider = class(TInterfacedObject, ILEDFunctionProvider) private FFunctions: TInterfaceList; @@ -45,7 +48,12 @@ type protected procedure RegisterStates; virtual; abstract; function RegisterState(AState: ILEDState): ILEDState; virtual; + + function GetWorkerClass: TCustomLEDFunctionWorkerClass; virtual; abstract; + function DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; virtual; protected + function CreateWorker(ASettings: ILEDFunctionWorkerSettings): ILEDFunctionWorker; override; + { ILEDMultiStateFunction } function GetEnumerator: ILEDStateEnumerator; virtual; public @@ -67,9 +75,9 @@ type property Observers: TInterfaceList read FObservers; property States: TInterfaceList read FStates; protected - { IObservable } - procedure Attach(AObserver: IObserver); virtual; - procedure Detach(AObserver: IObserver); virtual; + { ILEDFunctionWorker } + procedure Attach(AObserver: ILEDFunctionObserver); virtual; + procedure Detach(AObserver: ILEDFunctionObserver); virtual; function GetCurrentState: ILEDStateWorker; virtual; abstract; public @@ -147,6 +155,18 @@ begin end; +function TCustomMultiStateLEDFunction.CreateWorker(ASettings: ILEDFunctionWorkerSettings): ILEDFunctionWorker; +begin + Result := DoCreateWorker(ASettings); +end; + + +function TCustomMultiStateLEDFunction.DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; +begin + Result := GetWorkerClass.Create(Self, ASettings); +end; + + { TCustomLEDFunctionWorker } constructor TCustomLEDFunctionWorker.Create; begin @@ -174,16 +194,16 @@ begin end; -procedure TCustomLEDFunctionWorker.Attach(AObserver: IObserver); +procedure TCustomLEDFunctionWorker.Attach(AObserver: ILEDFunctionObserver); begin { TInterfaceList is thread-safe } - Observers.Add(AObserver as IObserver); + Observers.Add(AObserver as ILEDFunctionObserver); end; -procedure TCustomLEDFunctionWorker.Detach(AObserver: IObserver); +procedure TCustomLEDFunctionWorker.Detach(AObserver: ILEDFunctionObserver); begin - Observers.Remove(AObserver as IObserver); + Observers.Remove(AObserver as ILEDFunctionObserver); end; @@ -225,7 +245,7 @@ var begin for observer in Observers do - (observer as IObserver).Update(Self); + (observer as ILEDFunctionObserver).ObserveUpdate(Self); end; diff --git a/G940LEDControl/Units/LEDFunctionIntf.pas b/G940LEDControl/Units/LEDFunctionIntf.pas index 11394e9..81bba41 100644 --- a/G940LEDControl/Units/LEDFunctionIntf.pas +++ b/G940LEDControl/Units/LEDFunctionIntf.pas @@ -3,8 +3,7 @@ unit LEDFunctionIntf; interface uses LEDColorIntf, - LEDStateIntf, - ObserverIntf; + LEDStateIntf; type @@ -40,8 +39,17 @@ type end; - ILEDFunctionWorker = interface(IObservable) + ILEDFunctionObserver = interface + ['{B78415C9-9F64-4AF1-8983-BACE2B7225EF}'] + procedure ObserveUpdate(Sender: ILEDFunctionWorker); + end; + + + ILEDFunctionWorker = interface ['{5EF3230D-B52F-4BD6-8AD3-F3A035F155B1}'] + procedure Attach(AObserver: ILEDFunctionObserver); + procedure Detach(AObserver: ILEDFunctionObserver); + function GetCurrentState: ILEDStateWorker; end; From f1b9a63fdafdb36417dc8aea8c0dfe984a205607 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Tue, 19 Feb 2013 22:22:15 +0000 Subject: [PATCH 10/16] Refactored: ILEDColor to ILEDStateColor (to prevent conflicts with the TLEDColor enum) Reanimated: LEDStateConsumer and G940LEDStateConsumer; all LED state colors (including flashing) work and update properly after changing the profile - FSX functions crash horribly --- G940LEDControl/Forms/MainFrm.pas | 109 +---- G940LEDControl/G940LEDControl.dpr | 3 - G940LEDControl/G940LEDControl.dproj | 3 - G940LEDControl/Units/DynamicLEDColor.pas | 23 +- G940LEDControl/Units/G940LEDStateConsumer.pas | 166 +++---- G940LEDControl/Units/LEDColor.pas | 7 +- G940LEDControl/Units/LEDColorIntf.pas | 7 +- G940LEDControl/Units/LEDColorPool.pas | 22 +- G940LEDControl/Units/LEDFunctionMap.pas | 228 --------- G940LEDControl/Units/LEDState.pas | 10 +- G940LEDControl/Units/LEDStateConsumer.pas | 440 +++++++----------- G940LEDControl/Units/LEDStateIntf.pas | 2 +- G940LEDControl/Units/LEDStateProvider.pas | 127 ----- G940LEDControl/Units/Profile.pas | 8 +- G940LEDControl/Units/StaticLEDColor.pas | 12 +- 15 files changed, 305 insertions(+), 862 deletions(-) delete mode 100644 G940LEDControl/Units/LEDFunctionMap.pas delete mode 100644 G940LEDControl/Units/LEDStateProvider.pas diff --git a/G940LEDControl/Forms/MainFrm.pas b/G940LEDControl/Forms/MainFrm.pas index 696ecf6..bd5a9de 100644 --- a/G940LEDControl/Forms/MainFrm.pas +++ b/G940LEDControl/Forms/MainFrm.pas @@ -19,9 +19,7 @@ uses pngimage, X2UtPersistIntf, - LEDFunctionMap, LEDStateConsumer, - LEDStateProvider, Profile; @@ -95,7 +93,6 @@ type procedure FormCreate(Sender: TObject); procedure btnRetryClick(Sender: TObject); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); - procedure FunctionComboBoxChange(Sender: TObject); procedure lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType); procedure btnCheckUpdatesClick(Sender: TObject); procedure LEDButtonClick(Sender: TObject); @@ -109,15 +106,8 @@ type FProfiles: TProfileList; FActiveProfile: TProfile; FLoadingProfiles: Boolean; -// FStateConsumerTask: IOmniTaskControl; + FStateConsumerTask: IOmniTaskControl; protected -// procedure ReadFunctions(AReader: IX2PersistReader; AComboBoxes: TComboBoxArray); -// procedure ReadFSXExtra(AReader: IX2PersistReader); -// procedure ReadAutoUpdate(AReader: IX2PersistReader); -// procedure WriteFunctions(AWriter: IX2PersistWriter; AComboBoxes: TComboBoxArray); -// procedure WriteFSXExtra(AWriter: IX2PersistWriter); -// procedure WriteAutoUpdate(AWriter: IX2PersistWriter); - procedure FindLEDControls; procedure LoadProfiles; procedure SaveProfiles; @@ -129,11 +119,6 @@ type procedure SetDeviceState(const AMessage: string; AFound: Boolean); // procedure SetFSXToggleZoomButton(const ADeviceGUID: TGUID; AButtonIndex: Integer; const ADisplayText: string); -// procedure InitializeStateProvider(AProviderClass: TLEDStateProviderClass); -// procedure FinalizeStateProvider; - -// procedure UpdateMapping; - procedure CheckForUpdatesThread(const ATask: IOmniTask); procedure CheckForUpdates(AReportNoUpdates: Boolean); @@ -150,7 +135,7 @@ type property ActiveProfile: TProfile read FActiveProfile; property EventMonitor: TOmniEventMonitor read FEventMonitor; property Profiles: TProfileList read FProfiles; -// property StateConsumerTask: IOmniTaskControl read FStateConsumerTask; + property StateConsumerTask: IOmniTaskControl read FStateConsumerTask; end; @@ -170,7 +155,6 @@ uses ButtonFunctionFrm, ConfigConversion, - FSXLEDStateProvider, G940LEDStateConsumer, LEDColorIntf, LEDFunctionIntf, @@ -193,28 +177,13 @@ const TEXT_STATE_FOUND = 'Connected'; -type - TComboBoxFunctionConsumer = class(TInterfacedObject, IFunctionConsumer) - private - FComboBox: TComboBoxEx; - protected - { IFunctionConsumer } - procedure SetCategory(const ACategory: string); - procedure AddFunction(AFunction: Integer; const ADescription: string); - - property ComboBox: TComboBoxEx read FComboBox; - public - constructor Create(AComboBox: TComboBoxEx); - end; - - { TMainForm } procedure TMainForm.FormCreate(Sender: TObject); -//var -// consumer: IOmniWorker; -// +var + consumer: IOmniWorker; + begin lblVersion.Caption := App.Version.FormatVersion(False); @@ -222,12 +191,12 @@ begin FEventMonitor := TOmniEventMonitor.Create(Self); -// consumer := TG940LEDStateConsumer.Create; -// FStateConsumerTask := FEventMonitor.Monitor(CreateTask(consumer)).MsgWait; + consumer := TG940LEDStateConsumer.Create; + FStateConsumerTask := FEventMonitor.Monitor(CreateTask(consumer)).MsgWait; EventMonitor.OnTaskMessage := EventMonitorMessage; EventMonitor.OnTaskTerminated := EventMonitorTerminated; -// StateConsumerTask.Run; + StateConsumerTask.Run; FindLEDControls; @@ -235,6 +204,7 @@ begin FProfiles := TProfileList.Create(True); LoadProfiles; + FStateConsumerTask.Comm.Send(TM_LOADPROFILE, ActiveProfile); // LoadFunctions(TFSXLEDStateProvider, FFSXComboBoxes); // LoadDefaultProfile; end; @@ -549,6 +519,7 @@ begin if TButtonFunctionForm.Execute(ActiveProfile, buttonIndex) then begin UpdateButton(ActiveProfile, buttonIndex); + FStateConsumerTask.Comm.Send(TM_LOADPROFILE, ActiveProfile); SaveProfiles; end; end; @@ -657,20 +628,20 @@ end; procedure TMainForm.EventMonitorMessage(const task: IOmniTaskControl; const msg: TOmniMessage); begin case msg.MsgID of - MSG_NOTIFY_DEVICESTATE: HandleDeviceStateMessage(task, msg); - MSG_RUN_IN_MAINTHREAD: HandleRunInMainThreadMessage(task, msg); - MSG_PROVIDER_KILLED: HandleProviderKilled(task, msg); + TM_NOTIFY_DEVICESTATE: HandleDeviceStateMessage(task, msg); +// MSG_RUN_IN_MAINTHREAD: HandleRunInMainThreadMessage(task, msg); +// MSG_PROVIDER_KILLED: HandleProviderKilled(task, msg); MSG_UPDATE: if MessageBox(Self.Handle, 'An update is available on the G940 LED Control website.'#13#10'Do you want to go there now?', 'Update available', MB_YESNO or MB_ICONINFORMATION) = ID_YES then ShellExecute(Self.Handle, 'open', PChar('http://g940.x2software.net/#download'), nil, nil, SW_SHOWNORMAL); - MSG_NOUPDATE: - if msg.MsgData.AsBoolean then - MessageBox(Self.Handle, 'You are using the latest version.', 'No update available', MB_OK or MB_ICONINFORMATION) - else - MessageBox(Self.Handle, 'Failed to check for updates. Maybe try again later?', 'Uh-oh', MB_OK or MB_ICONWARNING); +// MSG_NOUPDATE: +// if msg.MsgData.AsBoolean then +// MessageBox(Self.Handle, 'You are using the latest version.', 'No update available', MB_OK or MB_ICONINFORMATION) +// else +// MessageBox(Self.Handle, 'Failed to check for updates. Maybe try again later?', 'Uh-oh', MB_OK or MB_ICONWARNING); end; end; @@ -748,53 +719,9 @@ begin end; -procedure TMainForm.FunctionComboBoxChange(Sender: TObject); -var - comboBox: TComboBoxEx; - -begin - comboBox := TComboBoxEx(Sender); - if comboBox.ItemIndex > -1 then - begin - if not Assigned(comboBox.ItemsEx[comboBox.ItemIndex].Data) then - comboBox.ItemIndex := Succ(comboBox.ItemIndex); - end; -end; - - procedure TMainForm.lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType); begin ShellExecute(Self.Handle, 'open', PChar(Link), nil, nil, SW_SHOWNORMAL); end; - -{ TComboBoxFunctionConsumer } -constructor TComboBoxFunctionConsumer.Create(AComboBox: TComboBoxEx); -begin - inherited Create; - - FComboBox := AComboBox; -end; - - -procedure TComboBoxFunctionConsumer.SetCategory(const ACategory: string); -begin - with ComboBox.ItemsEx.Add do - begin - Caption := ACategory; - Data := nil; - end; -end; - - -procedure TComboBoxFunctionConsumer.AddFunction(AFunction: Integer; const ADescription: string); -begin - with ComboBox.ItemsEx.Add do - begin - Caption := ADescription; - Indent := 1; - Data := Pointer(AFunction); - end; -end; - end. diff --git a/G940LEDControl/G940LEDControl.dpr b/G940LEDControl/G940LEDControl.dpr index 313a0e4..99c2a5c 100644 --- a/G940LEDControl/G940LEDControl.dpr +++ b/G940LEDControl/G940LEDControl.dpr @@ -5,11 +5,8 @@ uses MainFrm in 'Forms\MainFrm.pas' {MainForm}, LogiJoystickDLL in '..\Shared\LogiJoystickDLL.pas', SimConnect in '..\Shared\SimConnect.pas', - FSXLEDStateProvider in 'Units\FSXLEDStateProvider.pas', G940LEDStateConsumer in 'Units\G940LEDStateConsumer.pas', - LEDFunctionMap in 'Units\LEDFunctionMap.pas', LEDStateConsumer in 'Units\LEDStateConsumer.pas', - LEDStateProvider in 'Units\LEDStateProvider.pas', LEDColorIntf in 'Units\LEDColorIntf.pas', LEDColor in 'Units\LEDColor.pas', LEDFunctionIntf in 'Units\LEDFunctionIntf.pas', diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj index 88044e5..a3cb490 100644 --- a/G940LEDControl/G940LEDControl.dproj +++ b/G940LEDControl/G940LEDControl.dproj @@ -165,11 +165,8 @@ - - - diff --git a/G940LEDControl/Units/DynamicLEDColor.pas b/G940LEDControl/Units/DynamicLEDColor.pas index 99754b5..178e1c2 100644 --- a/G940LEDControl/Units/DynamicLEDColor.pas +++ b/G940LEDControl/Units/DynamicLEDColor.pas @@ -12,23 +12,24 @@ const type - TLEDColorDynArray = array of TLEDColor; + TStaticLEDColorDynArray = array of TStaticLEDColor; - TDynamicLEDColor = class(TCustomDynamicLEDColor) + TDynamicLEDColor = class(TCustomLEDStateDynamicColor) private - FCycleColors: TLEDColorDynArray; + FCycleColors: TStaticLEDColorDynArray; FCycleIndex: Integer; FTickInterval: Integer; FTickCount: Integer; protected { ILEDState } - function GetColor: TLEDColor; override; + function GetCurrentColor: TStaticLEDColor; override; { ITickLEDState } + procedure Reset; override; procedure Tick; override; public - constructor Create(ACycleColors: TLEDColorDynArray; ATickInterval: Integer = TICKINTERVAL_NORMAL); + constructor Create(ACycleColors: TStaticLEDColorDynArray; ATickInterval: Integer = TICKINTERVAL_NORMAL); end; @@ -39,7 +40,7 @@ uses { TDynamicLEDState } -constructor TDynamicLEDColor.Create(ACycleColors: TLEDColorDynArray; ATickInterval: Integer); +constructor TDynamicLEDColor.Create(ACycleColors: TStaticLEDColorDynArray; ATickInterval: Integer); begin inherited Create; @@ -49,16 +50,22 @@ begin FCycleColors := ACycleColors; FCycleIndex := Low(FCycleColors); FTickInterval := ATickInterval; - FTickCount := 0; + Reset; end; -function TDynamicLEDColor.GetColor: TLEDColor; +function TDynamicLEDColor.GetCurrentColor: TStaticLEDColor; begin Result := FCycleColors[FCycleIndex]; end; +procedure TDynamicLEDColor.Reset; +begin + FCycleIndex := 0; +end; + + procedure TDynamicLEDColor.Tick; begin Inc(FTickCount); diff --git a/G940LEDControl/Units/G940LEDStateConsumer.pas b/G940LEDControl/Units/G940LEDStateConsumer.pas index 8739416..caf058c 100644 --- a/G940LEDControl/Units/G940LEDStateConsumer.pas +++ b/G940LEDControl/Units/G940LEDStateConsumer.pas @@ -8,46 +8,31 @@ uses OtlComm, OtlTaskControl, - LEDFunctionMap, - LEDStateConsumer, - LEDStateProvider; + LEDStateConsumer; const - MSG_FINDTHROTTLEDEVICE = MSG_CONSUMER_OFFSET + 1; - MSG_NOTIFY_DEVICESTATE = MSG_CONSUMER_OFFSET + 2; - MSG_TIMER_BLINK = MSG_CONSUMER_OFFSET + 3; + TM_FINDTHROTTLEDEVICE = 2001; + TM_NOTIFY_DEVICESTATE = 2002; - TIMER_BLINK = TIMER_CONSUMER_OFFSET + 1; type TG940LEDStateConsumer = class(TLEDStateConsumer) private FDirectInput: IDirectInput8; FThrottleDevice: IDirectInputDevice8; - - FRed: Byte; - FGreen: Byte; - - FBlinkTimerStarted: Boolean; - FBlinkCounter: Integer; protected - procedure MsgFindThrottleDevice(var msg: TOmniMessage); message MSG_FINDTHROTTLEDEVICE; - procedure MsgTimerBlink(var msg: TOmniMessage); message MSG_TIMER_BLINK; + procedure MsgFindThrottleDevice(var msg: TOmniMessage); message TM_FINDTHROTTLEDEVICE; protected function Initialize: Boolean; override; - procedure ResetLEDState; override; - procedure LEDStateChanged(ALEDIndex: Integer; AState: TLEDState); override; - procedure Changed; override; - - procedure StartBlinkTimer; - procedure StopBlinkTimer; procedure FindThrottleDevice; procedure FoundThrottleDevice(ADeviceGUID: TGUID); procedure SetDeviceState(AState: Integer); + procedure Update; override; + property DirectInput: IDirectInput8 read FDirectInput; property ThrottleDevice: IDirectInputDevice8 read FThrottleDevice; end; @@ -58,8 +43,8 @@ const DEVICESTATE_FOUND = 1; DEVICESTATE_NOTFOUND = 2; - EXIT_ERROR_LOGIJOYSTICKDLL = EXIT_CONSUMER_OFFSET + 1; - EXIT_ERROR_DIRECTINPUT = EXIT_CONSUMER_OFFSET + 2; + EXIT_ERROR_LOGIJOYSTICKDLL = 9001; + EXIT_ERROR_DIRECTINPUT = 9002; implementation @@ -68,15 +53,18 @@ uses Windows, OtlCommon, + OtlTask, + LEDColorIntf, LogiJoystickDLL; - const - BLINK_INTERVAL = 500; + G940_BUTTONCOUNT = 8; + +(* type TRunInMainThreadSetLEDs = class(TOmniWaitableValue, IRunInMainThread) private @@ -89,6 +77,7 @@ type public constructor Create(ADevice: IDirectInputDevice8; ARed, AGreen: Byte); end; +*) function EnumDevicesProc(var lpddi: TDIDeviceInstanceW; pvRef: Pointer): BOOL; stdcall; @@ -134,10 +123,11 @@ begin end; Result := True; - Task.Comm.OtherEndpoint.Send(MSG_FINDTHROTTLEDEVICE); + Task.Comm.OtherEndpoint.Send(TM_FINDTHROTTLEDEVICE); end; +{ procedure TG940LEDStateConsumer.ResetLEDState; begin FRed := 0; @@ -200,36 +190,18 @@ begin inherited; end; +} +{ procedure TG940LEDStateConsumer.Changed; begin inherited; if Assigned(ThrottleDevice) then - { Logitech SDK will not change the color outside of the main thread } + { Logitech SDK will not change the color outside of the main thread RunInMainThread(TRunInMainThreadSetLEDs.Create(ThrottleDevice, FRed, FGreen), Destroying); end; - - -procedure TG940LEDStateConsumer.StartBlinkTimer; -begin - if FBlinkTimerStarted then - exit; - - FBlinkCounter := 0; - Task.SetTimer(TIMER_BLINK, BLINK_INTERVAL, MSG_TIMER_BLINK); - FBlinkTimerStarted := True; -end; - - -procedure TG940LEDStateConsumer.StopBlinkTimer; -begin - if not FBlinkTimerStarted then - exit; - - Task.ClearTimer(TIMER_BLINK); - FBlinkTimerStarted := False; -end; +} procedure TG940LEDStateConsumer.FindThrottleDevice; @@ -243,7 +215,7 @@ begin if not Assigned(ThrottleDevice) then SetDeviceState(DEVICESTATE_NOTFOUND) else - Changed; + Update; end; @@ -256,7 +228,54 @@ end; procedure TG940LEDStateConsumer.SetDeviceState(AState: Integer); begin - Task.Comm.Send(MSG_NOTIFY_DEVICESTATE, AState); + Task.Comm.Send(TM_NOTIFY_DEVICESTATE, AState); +end; + + +procedure TG940LEDStateConsumer.Update; + + procedure SetBit(var AMask: Byte; ABit: Integer); inline; + begin + AMask := AMask or (1 shl ABit) + end; + + +var + red: Byte; + green: Byte; + buttonIndex: Integer; + buttonColor: TStaticLEDColor; + +begin + if not Assigned(ThrottleDevice) then + exit; + + red := 0; + green := 0; + + for buttonIndex := 0 to Pred(G940_BUTTONCOUNT) do + begin + if buttonIndex >= ButtonColors.Count then + buttonColor := lcOff + else + buttonColor := (ButtonColors[buttonIndex] as ILEDStateColor).GetCurrentColor; + + case buttonColor of + lcGreen: + SetBit(green, buttonIndex); + + lcAmber: + begin + SetBit(green, buttonIndex); + SetBit(red, buttonIndex); + end; + + lcRed: + SetBit(red, buttonIndex); + end; + end; + + SetLEDs(ThrottleDevice, red, green); end; @@ -266,52 +285,8 @@ begin end; -procedure TG940LEDStateConsumer.MsgTimerBlink(var msg: TOmniMessage); -var - warningState: TLEDState; - errorState: TLEDState; - ledIndex: Integer; - state: TLEDState; - -begin - Inc(FBlinkCounter); - if FBlinkCounter > 3 then - FBlinkCounter := 0; - - warningState := lsOff; - errorState := lsOff; - - { Error lights blink twice as fast } - if (FBlinkCounter in [0, 1]) then - warningState := lsAmber; - - if (FBlinkCounter in [0, 2]) then - errorState := lsRed; - - if StateMap.FindFirst([lsWarning, lsError], ledIndex, state) then - begin - BeginUpdate; - try - repeat - case state of - lsWarning: - if StateMap.GetState(ledIndex) <> warningState then - LEDStateChanged(ledIndex, warningState); - - lsError: - if StateMap.GetState(ledIndex) <> errorState then - LEDStateChanged(ledIndex, errorState); - end; - until not StateMap.FindNext([lsWarning, lsError], ledIndex, state); - finally - EndUpdate; - end; - end else - StopBlinkTimer; -end; - - { TRunInMainThreadSetLEDs } +(* constructor TRunInMainThreadSetLEDs.Create(ADevice: IDirectInputDevice8; ARed, AGreen: Byte); begin inherited Create; @@ -326,5 +301,6 @@ procedure TRunInMainThreadSetLEDs.Execute; begin SetLEDs(FDevice, FRed, FGreen); end; +*) end. diff --git a/G940LEDControl/Units/LEDColor.pas b/G940LEDControl/Units/LEDColor.pas index bcb3c03..03f4b7a 100644 --- a/G940LEDControl/Units/LEDColor.pas +++ b/G940LEDControl/Units/LEDColor.pas @@ -8,16 +8,17 @@ uses type - TCustomLEDColor = class(TInterfacedObject, ILEDColor) + TCustomLEDStateColor = class(TInterfacedObject, ILEDStateColor) protected { ILEDState } - function GetColor: TLEDColor; virtual; abstract; + function GetCurrentColor: TStaticLEDColor; virtual; abstract; end; - TCustomDynamicLEDColor = class(TCustomLEDColor, IDynamicLEDColor) + TCustomLEDStateDynamicColor = class(TCustomLEDStateColor, ILEDStateDynamicColor) protected { ITickLEDState } + procedure Reset; virtual; abstract; procedure Tick; virtual; abstract; end; diff --git a/G940LEDControl/Units/LEDColorIntf.pas b/G940LEDControl/Units/LEDColorIntf.pas index 352a0fe..17b415f 100644 --- a/G940LEDControl/Units/LEDColorIntf.pas +++ b/G940LEDControl/Units/LEDColorIntf.pas @@ -11,14 +11,15 @@ type - ILEDColor = interface + ILEDStateColor = interface ['{B40DF462-B660-4002-A6B9-DD30AC69E8DB}'] - function GetColor: TLEDColor; + function GetCurrentColor: TStaticLEDColor; end; - IDynamicLEDColor = interface(ILEDColor) + ILEDStateDynamicColor = interface(ILEDStateColor) ['{9770E851-580D-4803-9979-0C608CB108A0}'] + procedure Reset; procedure Tick; end; diff --git a/G940LEDControl/Units/LEDColorPool.pas b/G940LEDControl/Units/LEDColorPool.pas index b24ce5a..03a337d 100644 --- a/G940LEDControl/Units/LEDColorPool.pas +++ b/G940LEDControl/Units/LEDColorPool.pas @@ -8,13 +8,13 @@ uses type TLEDColorPool = class(TObject) private - FStates: array[TLEDColor] of ILEDColor; + FStates: array[TLEDColor] of ILEDStateColor; protected class function Instance: TLEDColorPool; - function DoGetColor(AColor: TLEDColor): ILEDColor; + function DoGetColor(AColor: TLEDColor): ILEDStateColor; public - class function GetColor(AColor: TLEDColor): ILEDColor; overload; + class function GetColor(AColor: TLEDColor): ILEDStateColor; overload; end; @@ -31,7 +31,7 @@ var { TLEDStatePool } -class function TLEDColorPool.GetColor(AColor: TLEDColor): ILEDColor; +class function TLEDColorPool.GetColor(AColor: TLEDColor): ILEDStateColor; begin Result := Instance.DoGetColor(AColor); end; @@ -46,9 +46,9 @@ begin end; -function TLEDColorPool.DoGetColor(AColor: TLEDColor): ILEDColor; +function TLEDColorPool.DoGetColor(AColor: TLEDColor): ILEDStateColor; - function GetFlashingCycle(AColor: TLEDColor): TLEDColorDynArray; + function GetFlashingCycle(AColor: TLEDColor): TStaticLEDColorDynArray; begin SetLength(Result, 2); Result[0] := AColor; @@ -56,16 +56,16 @@ function TLEDColorPool.DoGetColor(AColor: TLEDColor): ILEDColor; end; var - state: ILEDColor; + state: ILEDStateColor; begin if not Assigned(FStates[AColor]) then begin case AColor of - lcOff: state := TStaticLEDColor.Create(lcOff); - lcGreen: state := TStaticLEDColor.Create(lcGreen); - lcAmber: state := TStaticLEDColor.Create(lcAmber); - lcRed: state := TStaticLEDColor.Create(lcRed); + lcOff: state := TLEDStateStaticColor.Create(lcOff); + lcGreen: state := TLEDStateStaticColor.Create(lcGreen); + lcAmber: state := TLEDStateStaticColor.Create(lcAmber); + lcRed: state := TLEDStateStaticColor.Create(lcRed); lcFlashingGreenFast: state := TDynamicLEDColor.Create(GetFlashingCycle(lcGreen), TICKINTERVAL_FAST); lcFlashingGreenNormal: state := TDynamicLEDColor.Create(GetFlashingCycle(lcGreen), TICKINTERVAL_NORMAL); diff --git a/G940LEDControl/Units/LEDFunctionMap.pas b/G940LEDControl/Units/LEDFunctionMap.pas deleted file mode 100644 index 2b68e7a..0000000 --- a/G940LEDControl/Units/LEDFunctionMap.pas +++ /dev/null @@ -1,228 +0,0 @@ -unit LEDFunctionMap; - -interface -uses - Classes, - SyncObjs, - - X2UtHashes; - - -type - TLEDState = (lsOff, lsGreen, lsAmber, lsRed, lsWarning, lsError); - TLEDStateSet = set of TLEDState; - - - TLEDFunctionMap = class(TObject) - private - FFunctions: TX2IIHash; - public - constructor Create; - destructor Destroy; override; - - procedure Clear; - - procedure SetFunction(ALEDIndex, AFunction: Integer); - function GetFunction(ALEDIndex: Integer): Integer; - - function HasFunction(AFunction: Integer): Boolean; overload; - function HasFunction(AFunctions: array of Integer): Boolean; overload; - - function FindFirst(AFunction: Integer; out ALEDIndex: Integer): Boolean; - function FindNext(AFunction: Integer; out ALEDIndex: Integer): Boolean; - end; - - - TLEDStateMap = class(TObject) - private - FStates: TX2IIHash; - public - constructor Create; - destructor Destroy; override; - - procedure Clear; - - function GetState(ALEDIndex: Integer; ADefault: TLEDState = lsGreen): TLEDState; - function SetState(ALEDIndex: Integer; AState: TLEDState): Boolean; - - function HasStates(AStates: TLEDStateSet): Boolean; - function FindFirst(AStates: TLEDStateSet; out ALEDIndex: Integer; out AState: TLEDState): Boolean; - function FindNext(AStates: TLEDStateSet; out ALEDIndex: Integer; out AState: TLEDState): Boolean; - end; - - -const - FUNCTION_NONE = 0; - FUNCTION_OFF = 1; - FUNCTION_RED = 2; - FUNCTION_AMBER = 3; - FUNCTION_GREEN = 4; - - { Note: if this offset ever changes, make sure to write a conversion for - existing configurations. And probably reserve a bit more. } - FUNCTION_PROVIDER_OFFSET = FUNCTION_GREEN; - - -implementation -uses - SysUtils; - - -{ TLEDFunctionMap } -constructor TLEDFunctionMap.Create; -begin - inherited; - - FFunctions := TX2IIHash.Create; -end; - - -destructor TLEDFunctionMap.Destroy; -begin - FreeAndNil(FFunctions); - - inherited; -end; - - -procedure TLEDFunctionMap.Clear; -begin - FFunctions.Clear; -end; - - -procedure TLEDFunctionMap.SetFunction(ALEDIndex, AFunction: Integer); -begin - FFunctions[ALEDIndex] := AFunction; -end; - - -function TLEDFunctionMap.GetFunction(ALEDIndex: Integer): Integer; -begin - Result := FFunctions[ALEDIndex]; -end; - - -function TLEDFunctionMap.HasFunction(AFunctions: array of Integer): Boolean; -var - functionNo: Integer; - -begin - Result := False; - - for functionNo in AFunctions do - begin - Result := HasFunction(functionNo); - if Result then - break; - end; -end; - -function TLEDFunctionMap.HasFunction(AFunction: Integer): Boolean; -var - ledIndex: Integer; - -begin - Result := FindFirst(AFunction, ledIndex); -end; - - -function TLEDFunctionMap.FindFirst(AFunction: Integer; out ALEDIndex: Integer): Boolean; -begin - FFunctions.First; - Result := FindNext(AFunction, ALEDIndex); -end; - - -function TLEDFunctionMap.FindNext(AFunction: Integer; out ALEDIndex: Integer): Boolean; -begin - Result := False; - - while FFunctions.Next do - begin - if FFunctions.CurrentValue = AFunction then - begin - ALEDIndex := FFunctions.CurrentKey; - Result := True; - break; - end; - end; -end; - - -{ TLEDStateMap } -constructor TLEDStateMap.Create; -begin - inherited; - - FStates := TX2IIHash.Create; -end; - - -destructor TLEDStateMap.Destroy; -begin - FreeAndNil(FStates); - - inherited; -end; - - -procedure TLEDStateMap.Clear; -begin - FStates.Clear; -end; - - -function TLEDStateMap.GetState(ALEDIndex: Integer; ADefault: TLEDState): TLEDState; -begin - Result := ADefault; - if FStates.Exists(ALEDIndex) then - Result := TLEDState(FStates[ALEDIndex]); -end; - - -function TLEDStateMap.SetState(ALEDIndex: Integer; AState: TLEDState): Boolean; -begin - if FStates.Exists(ALEDIndex) then - Result := (FStates[ALEDIndex] <> Ord(AState)) - else - Result := True; - - if Result then - FStates[ALEDIndex] := Ord(AState); -end; - - -function TLEDStateMap.HasStates(AStates: TLEDStateSet): Boolean; -var - ledIndex: Integer; - state: TLEDState; - -begin - Result := FindFirst(AStates, ledIndex, state); -end; - - - -function TLEDStateMap.FindFirst(AStates: TLEDStateSet; out ALEDIndex: Integer; out AState: TLEDState): Boolean; -begin - FStates.First; - Result := FindNext(AStates, ALEDIndex, AState); -end; - - -function TLEDStateMap.FindNext(AStates: TLEDStateSet; out ALEDIndex: Integer; out AState: TLEDState): Boolean; -begin - Result := False; - - while FStates.Next do - if TLEDState(FStates.CurrentValue) in AStates then - begin - ALEDIndex := FStates.CurrentKey; - AState := TLEDState(FStates.CurrentValue); - Result := True; - break; - end; -end; - -end. diff --git a/G940LEDControl/Units/LEDState.pas b/G940LEDControl/Units/LEDState.pas index 73a671f..cd3aace 100644 --- a/G940LEDControl/Units/LEDState.pas +++ b/G940LEDControl/Units/LEDState.pas @@ -33,12 +33,12 @@ type TLEDStateWorker = class(TCustomLEDState, ILEDStateWorker) private - FColor: ILEDColor; + FColor: ILEDStateColor; protected { ILEDStateWorker } - function GetColor: ILEDColor; + function GetColor: ILEDStateColor; public - constructor Create(const AUID: string; AColor: ILEDColor); + constructor Create(const AUID: string; AColor: ILEDStateColor); end; @@ -83,7 +83,7 @@ end; { TLEDStateWorker } -constructor TLEDStateWorker.Create(const AUID: string; AColor: ILEDColor); +constructor TLEDStateWorker.Create(const AUID: string; AColor: ILEDStateColor); begin inherited Create(AUID); @@ -91,7 +91,7 @@ begin end; -function TLEDStateWorker.GetColor: ILEDColor; +function TLEDStateWorker.GetColor: ILEDStateColor; begin Result := FColor; end; diff --git a/G940LEDControl/Units/LEDStateConsumer.pas b/G940LEDControl/Units/LEDStateConsumer.pas index c809a12..a791bc5 100644 --- a/G940LEDControl/Units/LEDStateConsumer.pas +++ b/G940LEDControl/Units/LEDStateConsumer.pas @@ -2,29 +2,22 @@ unit LEDStateConsumer; interface uses + System.Classes, + OtlComm, OtlCommon, OtlTaskControl, - LEDFunctionMap, - LEDStateProvider; + LEDColorIntf, + LEDFunctionIntf, + Profile; const - MSG_CLEAR_FUNCTIONS = 1001; - MSG_SET_FUNCTION = 1002; - MSG_INITIALIZE_PROVIDER = 1003; - MSG_FINALIZE_PROVIDER = 1004; - MSG_PROCESS_MESSAGES = 1005; - MSG_FINALIZE = 1006; + TM_LOADPROFILE = 1001; + TM_TICK = 1002; - MSG_PROVIDER_KILLED = 1007; - MSG_RUN_IN_MAINTHREAD = 1008; - - MSG_CONSUMER_OFFSET = MSG_RUN_IN_MAINTHREAD; - - TIMER_PROCESSMESSAGES = 1001; - TIMER_CONSUMER_OFFSET = TIMER_PROCESSMESSAGES; + TIMER_TICK = 101; type @@ -32,320 +25,219 @@ type ['{68B8F2F7-ED40-4078-9D99-503D7AFA068B}'] procedure Execute; end; - - TLEDStateConsumer = class(TOmniWorker, ILEDStateConsumer) + + TLEDStateConsumer = class(TOmniWorker) private - FFunctionMap: TLEDFunctionMap; - FStateMap: TLEDStateMap; - FProvider: TLEDStateProvider; - FTimerSet: Boolean; - FChanged: Boolean; - FUpdateCount: Integer; - FDestroying: Boolean; + FButtonWorkers: TInterfaceList; + FButtonColors: TInterfaceList; + FHasTickTimer: Boolean; protected - procedure MsgClearFunctions(var msg: TOmniMessage); message MSG_CLEAR_FUNCTIONS; - procedure MsgSetFunction(var msg: TOmniMessage); message MSG_SET_FUNCTION; - procedure MsgInitializeProvider(var msg: TOmniMessage); message MSG_INITIALIZE_PROVIDER; - procedure MsgFinalizeProvider(var msg: TOmniMessage); message MSG_FINALIZE_PROVIDER; - procedure MsgProcessMessages(var msg: TOmniMessage); message MSG_PROCESS_MESSAGES; - procedure MsgFinalize(var msg: TOmniMessage); message MSG_FINALIZE; - + function Initialize: Boolean; override; procedure Cleanup; override; - procedure InitializeProvider(AProviderClass: TLEDStateProviderClass); - procedure FinalizeProvider; + function CreateWorker(AProfileButton: TProfileButton): ILEDFunctionWorker; - procedure RunInMainThread(AExecutor: IRunInMainThread; AWait: Boolean = False); - procedure InitializeLEDState; virtual; - procedure ResetLEDState; virtual; - procedure LEDStateChanged(ALEDIndex: Integer; AState: TLEDState); virtual; + property ButtonWorkers: TInterfaceList read FButtonWorkers; + property ButtonColors: TInterfaceList read FButtonColors; + property HasTickTimer: Boolean read FHasTickTimer; + protected procedure Changed; virtual; - - { ILEDStateConsumer } - function GetFunctionMap: TLEDFunctionMap; - procedure SetStateByFunction(AFunction: Integer; AState: TLEDState); - - property Destroying: Boolean read FDestroying; - property FunctionMap: TLEDFunctionMap read GetFunctionMap; - property StateMap: TLEDStateMap read FStateMap; - property Provider: TLEDStateProvider read FProvider; - property UpdateCount: Integer read FUpdateCount write FUpdateCount; - public - constructor Create; - - procedure BeginUpdate; - procedure EndUpdate; + procedure Update; virtual; abstract; + protected + procedure TMLoadProfile(var Msg: TOmniMessage); message TM_LOADPROFILE; + procedure TMTick(var Msg: TOmniMessage); message TM_TICK; end; - procedure ClearFunctions(AConsumer: IOmniTaskControl); - procedure SetFunction(AConsumer: IOmniTaskControl; ALEDIndex, AFunction: Integer); - procedure InitializeStateProvider(AConsumer: IOmniTaskControl; AProviderClass: TLEDStateProviderClass); - procedure FinalizeStateProvider(AConsumer: IOmniTaskControl); - procedure Finalize(AConsumer: IOmniTaskControl); - - implementation uses - SysUtils, - Windows; + System.SysUtils, + Winapi.Windows, + + LEDFunctionRegistry, + LEDStateIntf; const - G940_LED_COUNT = 8; + INTERVAL_TICK = 500; + + +type + TProfileButtonWorkerSettings = class(TInterfacedObject, ILEDFunctionWorkerSettings) + private + FProfileButton: TProfileButton; + protected + { ILEDFunctionWorkerSettings } + function GetStateColor(const AUID: string; out AColor: TLEDColor): Boolean; + + property ProfileButton: TProfileButton read FProfileButton; + public + constructor Create(AProfileButton: TProfileButton); + end; { TLEDStateConsumer } -constructor TLEDStateConsumer.Create; +function TLEDStateConsumer.Initialize: Boolean; begin - inherited; + Result := inherited Initialize; + if not Result then + exit; - FFunctionMap := TLEDFunctionMap.Create; - FStateMap := TLEDStateMap.Create; - - InitializeLEDState; + FButtonWorkers := TInterfaceList.Create; + FButtonColors := TInterfaceList.Create; end; procedure TLEDStateConsumer.Cleanup; begin - inherited; + FreeAndNil(FButtonColors); + FreeAndNil(FButtonWorkers); - FreeAndNil(FStateMap); - FreeAndNil(FFunctionMap); + inherited Cleanup; end; -procedure TLEDStateConsumer.BeginUpdate; -begin - if FUpdateCount = 0 then - FChanged := False; - - Inc(FUpdateCount); -end; - - -procedure TLEDStateConsumer.EndUpdate; -begin - if FUpdateCount > 0 then - Dec(FUpdateCount); - - if (FUpdateCount = 0) and FChanged then - Changed; -end; - - -function TLEDStateConsumer.GetFunctionMap: TLEDFunctionMap; -begin - Result := FFunctionMap; -end; - - -procedure TLEDStateConsumer.SetStateByFunction(AFunction: Integer; AState: TLEDState); +function TLEDStateConsumer.CreateWorker(AProfileButton: TProfileButton): ILEDFunctionWorker; var - ledIndex: Integer; + provider: ILEDFunctionProvider; + ledFunction: ILEDFunction; begin - if FunctionMap.FindFirst(AFunction, ledIndex) then - repeat - if StateMap.SetState(ledIndex, AState) then - LEDStateChanged(ledIndex, AState); - until not FunctionMap.FindNext(AFunction, ledIndex); -end; + Result := nil; - - -procedure TLEDStateConsumer.MsgClearFunctions(var msg: TOmniMessage); -begin - FunctionMap.Clear; -end; - - -procedure TLEDStateConsumer.MsgSetFunction(var msg: TOmniMessage); -var - values: TOmniValueContainer; - -begin - values := msg.MsgData.AsArray; - FunctionMap.SetFunction(values[0], values[1]); -end; - - -procedure TLEDStateConsumer.MsgInitializeProvider(var msg: TOmniMessage); -begin - InitializeProvider(TLEDStateProviderClass(msg.MsgData.AsPointer)); -end; - - -procedure TLEDStateConsumer.MsgFinalizeProvider(var msg: TOmniMessage); -begin - FinalizeProvider; -end; - - -procedure TLEDStateConsumer.MsgProcessMessages(var msg: TOmniMessage); -begin - BeginUpdate; - try - Provider.ProcessMessages; - - if Provider.Terminated then - begin - FinalizeProvider; - Task.Comm.Send(MSG_PROVIDER_KILLED, ''); - end; - finally - EndUpdate; - end; -end; - - -procedure TLEDStateConsumer.MsgFinalize(var msg: TOmniMessage); -begin - FDestroying := True; - FinalizeProvider; - Task.Terminate; -end; - - -procedure TLEDStateConsumer.InitializeProvider(AProviderClass: TLEDStateProviderClass); -begin - FinalizeProvider; - - FProvider := AProviderClass.Create(Self); - try - Provider.Initialize; - - if Provider.ProcessMessagesInterval > -1 then - begin - Task.SetTimer(TIMER_PROCESSMESSAGES, Provider.ProcessMessagesInterval, MSG_PROCESS_MESSAGES); - FTimerSet := True; - end; - - InitializeLEDState; - except - on E:Exception do - begin - FProvider := nil; - Task.Comm.Send(MSG_PROVIDER_KILLED, E.Message); - end; - end; -end; - - -procedure TLEDStateConsumer.FinalizeProvider; -begin - if Assigned(Provider) then + provider := TLEDFunctionRegistry.Find(AProfileButton.ProviderUID); + if Assigned(provider) then begin - if FTimerSet then - begin - Task.ClearTimer(TIMER_PROCESSMESSAGES); - FTimerSet := False; - end; - - Provider.Terminate; - Provider.Finalize; - FreeAndNil(FProvider); - - StateMap.Clear; - ResetLEDState; + ledFunction := provider.Find(AProfileButton.FunctionUID); + if Assigned(ledFunction) then + Result := ledFunction.CreateWorker(TProfileButtonWorkerSettings.Create(AProfileButton)); end; end; -procedure TLEDStateConsumer.RunInMainThread(AExecutor: IRunInMainThread; AWait: Boolean); -begin - Task.Comm.Send(MSG_RUN_IN_MAINTHREAD, AExecutor); - if AWait then - AExecutor.WaitFor(INFINITE); -end; - - -procedure TLEDStateConsumer.InitializeLEDState; -var - ledIndex: Integer; - state: TLEDState; - newState: TLEDState; - -begin - BeginUpdate; - try - ResetLEDState; - - for ledIndex := 0 to Pred(G940_LED_COUNT) do - begin - state := StateMap.GetState(ledIndex, lsGreen); - newState := state; - - case FunctionMap.GetFunction(ledIndex) of - FUNCTION_OFF: newState := lsOff; - FUNCTION_RED: newState := lsRed; - FUNCTION_AMBER: newState := lsAmber; - FUNCTION_GREEN: newState := lsGreen; - end; - - if state <> newState then - LEDStateChanged(ledIndex, newState); - end; - finally - EndUpdate; - end; -end; - - -procedure TLEDStateConsumer.ResetLEDState; -begin - if UpdateCount = 0 then - Changed - else - FChanged := True; -end; - - -procedure TLEDStateConsumer.LEDStateChanged(ALEDIndex: Integer; AState: TLEDState); -begin - if UpdateCount = 0 then - Changed - else - FChanged := True; -end; - - procedure TLEDStateConsumer.Changed; +var + hasDynamicColors: Boolean; + buttonIndex: Integer; + state: ILEDStateWorker; + color: ILEDStateColor; + dynamicColor: ILEDStateDynamicColor; + begin - FChanged := False; + hasDynamicColors := False; + ButtonColors.Clear; + + for buttonIndex := 0 to Pred(ButtonWorkers.Count) do + begin + color := nil; + + if Assigned(ButtonWorkers[buttonIndex]) then + begin + state := (ButtonWorkers[buttonIndex] as ILEDFunctionWorker).GetCurrentState; + if Assigned(state) then + begin + color := state.GetColor; + if Assigned(color) then + begin + if (hasDynamicColors = False) and Supports(color, ILEDStateDynamicColor, dynamicColor) then + begin + { If the tick timer isn't currently running, there were no + dynamic colors before. Reset each dynamic colors now. } + if not HasTickTimer then + dynamicColor.Reset; + + hasDynamicColors := True; + end; + + ButtonColors.Add(color as ILEDStateColor); + end; + end; + end; + + if not Assigned(color) then + ButtonColors.Add(nil); + end; + + if hasDynamicColors <> HasTickTimer then + begin + if hasDynamicColors then + Task.SetTimer(TIMER_TICK, INTERVAL_TICK, TM_TICK) + else + Task.ClearTimer(TIMER_TICK); + end; + + Update; end; -{ Helpers } -procedure ClearFunctions(AConsumer: IOmniTaskControl); +procedure TLEDStateConsumer.TMLoadProfile(var Msg: TOmniMessage); +var + profile: TProfile; + buttonIndex: Integer; + begin - AConsumer.Comm.Send(MSG_CLEAR_FUNCTIONS); + profile := Msg.MsgData; + ButtonWorkers.Clear; + + for buttonIndex := 0 to Pred(profile.ButtonCount) do + begin + if profile.HasButton(buttonIndex) then + ButtonWorkers.Add(CreateWorker(profile.Buttons[buttonIndex]) as ILEDFunctionWorker) + else + ButtonWorkers.Add(nil); + end; + + Changed; end; -procedure SetFunction(AConsumer: IOmniTaskControl; ALEDIndex, AFunction: Integer); +procedure TLEDStateConsumer.TMTick(var Msg: TOmniMessage); +var + buttonIndex: Integer; + checkButtonIndex: Integer; + alreadyTicked: Boolean; + color: ILEDStateColor; + dynamicColor: ILEDStateDynamicColor; + begin - AConsumer.Comm.Send(MSG_SET_FUNCTION, [ALEDIndex, AFunction]); + // (MvR) 19-2-2013: I could pass a tick count to Tick() so that they can all use modulus to blink synchronously... think about it. + + for buttonIndex := 0 to Pred(ButtonColors.Count) do + begin + alreadyTicked := False; + color := (ButtonColors[buttonIndex] as ILEDStateColor); + + if Supports(color, ILEDStateDynamicColor, dynamicColor) then + begin + { Check if this color has already been ticked } + for checkButtonIndex := Pred(buttonIndex) downto 0 do + if (ButtonColors[checkButtonIndex] as ILEDStateColor) = color then + begin + alreadyTicked := True; + break; + end; + + if not alreadyTicked then + dynamicColor.Tick; + end; + end; + + Update; end; -procedure InitializeStateProvider(AConsumer: IOmniTaskControl; AProviderClass: TLEDStateProviderClass); +{ TProfileButtonWorkerSettings } +constructor TProfileButtonWorkerSettings.Create(AProfileButton: TProfileButton); begin - AConsumer.Comm.Send(MSG_INITIALIZE_PROVIDER, Pointer(AProviderClass)); + inherited Create; + + FProfileButton := AProfileButton; end; - -procedure FinalizeStateProvider(AConsumer: IOmniTaskControl); +function TProfileButtonWorkerSettings.GetStateColor(const AUID: string; out AColor: TLEDColor): Boolean; begin - AConsumer.Comm.Send(MSG_FINALIZE_PROVIDER); -end; - - -procedure Finalize(AConsumer: IOmniTaskControl); -begin - AConsumer.Comm.Send(MSG_FINALIZE); + Result := ProfileButton.GetStateColor(AUID, AColor); end; end. diff --git a/G940LEDControl/Units/LEDStateIntf.pas b/G940LEDControl/Units/LEDStateIntf.pas index 357a6e5..ad40fa1 100644 --- a/G940LEDControl/Units/LEDStateIntf.pas +++ b/G940LEDControl/Units/LEDStateIntf.pas @@ -21,7 +21,7 @@ type ILEDStateWorker = interface(ICustomLEDState) ['{0361CBD5-E64E-4972-A8A4-D5FE0B0DFB1C}'] - function GetColor: ILEDColor; + function GetColor: ILEDStateColor; end; diff --git a/G940LEDControl/Units/LEDStateProvider.pas b/G940LEDControl/Units/LEDStateProvider.pas deleted file mode 100644 index ba81459..0000000 --- a/G940LEDControl/Units/LEDStateProvider.pas +++ /dev/null @@ -1,127 +0,0 @@ -unit LEDStateProvider; - -interface -uses - Classes, - SyncObjs, - SysUtils, - - LEDFunctionMap; - -type - EInitializeError = class(Exception); - - ILEDStateConsumer = interface - ['{6E630C92-7C5C-4D16-8BED-AE27559FA584}'] - function GetFunctionMap: TLEDFunctionMap; - procedure SetStateByFunction(AFunction: Integer; AState: TLEDState); - - property FunctionMap: TLEDFunctionMap read GetFunctionMap; - end; - - - IFunctionConsumer = interface - ['{97B47A29-BA7F-4C48-934D-EB66D2741647}'] - procedure SetCategory(const ACategory: string); - procedure AddFunction(AFunction: Integer; const ADescription: string); - end; - - - TLEDStateProvider = class(TObject) - private - FConsumer: ILEDStateConsumer; - FTerminated: Boolean; - protected - procedure Execute; virtual; abstract; - - function GetProcessMessagesInterval: Integer; virtual; - - property Consumer: ILEDStateConsumer read FConsumer; - public - class procedure EnumFunctions(AConsumer: IFunctionConsumer); virtual; - - constructor Create(AConsumer: ILEDStateConsumer); virtual; - destructor Destroy; override; - - procedure Initialize; virtual; - procedure Finalize; virtual; - procedure ProcessMessages; virtual; - - procedure Terminate; virtual; - - property ProcessMessagesInterval: Integer read GetProcessMessagesInterval; - property Terminated: Boolean read FTerminated; - end; - - TLEDStateProviderClass = class of TLEDStateProvider; - - -const - EXIT_SUCCESS = 0; - EXIT_ERROR = 1; - - EXIT_CONSUMER_OFFSET = 100; - EXIT_PROVIDER_OFFSET = 200; - -implementation -const - CATEGORY_STATIC = 'Static'; - - FUNCTION_DESC_OFF = 'Light off'; - FUNCTION_DESC_GREEN = 'Green'; - FUNCTION_DESC_AMBER = 'Amber'; - FUNCTION_DESC_RED = 'Red'; - - -{ TCustomLEDStateProvider } -class procedure TLEDStateProvider.EnumFunctions(AConsumer: IFunctionConsumer); -begin - AConsumer.SetCategory(CATEGORY_STATIC); - AConsumer.AddFunction(FUNCTION_OFF, FUNCTION_DESC_OFF); - AConsumer.AddFunction(FUNCTION_GREEN, FUNCTION_DESC_GREEN); - AConsumer.AddFunction(FUNCTION_AMBER, FUNCTION_DESC_AMBER); - AConsumer.AddFunction(FUNCTION_RED, FUNCTION_DESC_RED); -end; - - -constructor TLEDStateProvider.Create(AConsumer: ILEDStateConsumer); -begin - inherited Create; - - FConsumer := AConsumer; -end; - - -destructor TLEDStateProvider.Destroy; -begin - inherited; -end; - - -procedure TLEDStateProvider.Initialize; -begin -end; - - -procedure TLEDStateProvider.Finalize; -begin -end; - - -procedure TLEDStateProvider.ProcessMessages; -begin -end; - - -procedure TLEDStateProvider.Terminate; -begin - FTerminated := True; -end; - - -function TLEDStateProvider.GetProcessMessagesInterval: Integer; -begin - Result := -1; -end; - -end. diff --git a/G940LEDControl/Units/Profile.pas b/G940LEDControl/Units/Profile.pas index 96c8ea3..66eb916 100644 --- a/G940LEDControl/Units/Profile.pas +++ b/G940LEDControl/Units/Profile.pas @@ -3,6 +3,7 @@ unit Profile; interface uses Generics.Collections, + System.Classes, X2UtPersistIntf, @@ -12,7 +13,7 @@ uses type TProfileButtonStateColors = TDictionary; - TProfileButton = class(TObject) + TProfileButton = class(TPersistent) private FProviderUID: string; FFunctionUID: string; @@ -39,7 +40,7 @@ type TProfileButtonList = class(TObjectList); - TProfile = class(TObject) + TProfile = class(TPersistent) private FName: string; FButtons: TProfileButtonList; @@ -71,8 +72,7 @@ type implementation uses - Classes, - SysUtils, + System.SysUtils, LEDResources; diff --git a/G940LEDControl/Units/StaticLEDColor.pas b/G940LEDControl/Units/StaticLEDColor.pas index ecabd62..59f8464 100644 --- a/G940LEDControl/Units/StaticLEDColor.pas +++ b/G940LEDControl/Units/StaticLEDColor.pas @@ -7,13 +7,13 @@ uses type - TStaticLEDColor = class(TCustomLEDColor) + TLEDStateStaticColor = class(TCustomLEDStateColor) private - FColor: TLEDColor; + FColor: TStaticLEDColor; protected - function GetColor: TLEDColor; override; + function GetCurrentColor: TStaticLEDColor; override; public - constructor Create(AColor: TLEDColor); + constructor Create(AColor: TStaticLEDColor); end; @@ -21,7 +21,7 @@ implementation { TStaticLEDState } -constructor TStaticLEDColor.Create(AColor: TLEDColor); +constructor TLEDStateStaticColor.Create(AColor: TStaticLEDColor); begin inherited Create; @@ -29,7 +29,7 @@ begin end; -function TStaticLEDColor.GetColor: TLEDColor; +function TLEDStateStaticColor.GetCurrentColor: TStaticLEDColor; begin Result := FColor; end; From 4d0e2a5af6bef8499909347d044c5ab5ba0dab9c Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Thu, 21 Feb 2013 21:59:29 +0000 Subject: [PATCH 11/16] Added: automatic detection of Throttle plug in / out Added: actual SimConnect connection and auto-reconnect --- G940LEDControl/Forms/ButtonFunctionFrm.dfm | 3 - G940LEDControl/Forms/ButtonFunctionFrm.pas | 2 +- G940LEDControl/Forms/MainFrm.dfm | 15 +- G940LEDControl/Forms/MainFrm.pas | 219 ++++---- G940LEDControl/G940LEDControl.dproj | 1 - G940LEDControl/Units/FSXLEDFunction.pas | 30 +- .../Units/FSXLEDFunctionProvider.pas | 131 ++++- G940LEDControl/Units/FSXResources.pas | 2 + G940LEDControl/Units/FSXSimConnectClient.pas | 511 +++++++++++++++++- G940LEDControl/Units/FSXSimConnectIntf.pas | 22 +- G940LEDControl/Units/G940LEDStateConsumer.pas | 130 +---- G940LEDControl/Units/LEDFunction.pas | 10 +- G940LEDControl/Units/LEDFunctionRegistry.pas | 4 +- G940LEDControl/Units/LEDStateConsumer.pas | 53 +- G940LEDControl/Units/Profile.pas | 2 +- 15 files changed, 801 insertions(+), 334 deletions(-) diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.dfm b/G940LEDControl/Forms/ButtonFunctionFrm.dfm index 5b4ac44..a58a285 100644 --- a/G940LEDControl/Forms/ButtonFunctionFrm.dfm +++ b/G940LEDControl/Forms/ButtonFunctionFrm.dfm @@ -101,7 +101,6 @@ object ButtonFunctionForm: TButtonFunctionForm OnFocusChanged = vstFunctionsFocusChanged OnGetText = vstFunctionsGetText OnPaintText = vstFunctionsPaintText - ExplicitHeight = 383 Columns = < item Position = 0 @@ -122,7 +121,6 @@ object ButtonFunctionForm: TButtonFunctionForm Align = alClient BevelOuter = bvNone TabOrder = 1 - ExplicitHeight = 383 object pnlName: TPanel Left = 0 Top = 0 @@ -194,7 +192,6 @@ object ButtonFunctionForm: TButtonFunctionForm Align = alClient BorderStyle = bsNone TabOrder = 1 - ExplicitHeight = 286 end end object pnlHeader: TPanel diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.pas b/G940LEDControl/Forms/ButtonFunctionFrm.pas index 60072dc..f442d4c 100644 --- a/G940LEDControl/Forms/ButtonFunctionFrm.pas +++ b/G940LEDControl/Forms/ButtonFunctionFrm.pas @@ -469,7 +469,7 @@ begin FreeAndNil(FComboBox); FreeAndNil(FStateLabel); - inherited; + inherited Destroy; end; end. diff --git a/G940LEDControl/Forms/MainFrm.dfm b/G940LEDControl/Forms/MainFrm.dfm index f1ed398..857ac1a 100644 --- a/G940LEDControl/Forms/MainFrm.dfm +++ b/G940LEDControl/Forms/MainFrm.dfm @@ -356,6 +356,10 @@ object MainForm: TMainForm object tsAbout: TTabSheet Caption = 'About' ImageIndex = 1 + ExplicitLeft = 0 + ExplicitTop = 0 + ExplicitWidth = 0 + ExplicitHeight = 0 object lblVersionCaption: TLabel Left = 16 Top = 67 @@ -842,16 +846,5 @@ object MainForm: TMainForm ParentFont = False ExplicitWidth = 401 end - object btnRetry: TButton - Left = 374 - Top = 20 - Width = 75 - Height = 25 - Anchors = [akTop, akRight] - Caption = '&Retry' - TabOrder = 0 - Visible = False - OnClick = btnRetryClick - end end end diff --git a/G940LEDControl/Forms/MainFrm.pas b/G940LEDControl/Forms/MainFrm.pas index bd5a9de..7cb4299 100644 --- a/G940LEDControl/Forms/MainFrm.pas +++ b/G940LEDControl/Forms/MainFrm.pas @@ -2,15 +2,15 @@ unit MainFrm; interface uses - Classes, - Contnrs, - Controls, - ComCtrls, - ExtCtrls, - Forms, - Messages, - StdCtrls, - Windows, + System.Classes, + System.Contnrs, + Vcl.ComCtrls, + Vcl.Controls, + Vcl.ExtCtrls, + Vcl.Forms, + Vcl.StdCtrls, + Winapi.Messages, + Winapi.Windows, OtlComm, OtlEventMonitor, @@ -20,7 +20,7 @@ uses X2UtPersistIntf, LEDStateConsumer, - Profile; + Profile, Vcl.AppEvnts; const @@ -31,6 +31,12 @@ const LED_COUNT = 8; + DBT_DEVICEARRIVAL = $8000; + DBT_DEVICEREMOVECOMPLETE = $8004; + DBT_DEVTYP_DEVICEINTERFACE = $0005; + DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = $0004; + + type TLEDControls = record ConfigureButton: TButton; @@ -44,7 +50,6 @@ type lblG940Throttle: TLabel; imgStateFound: TImage; lblG940ThrottleState: TLabel; - btnRetry: TButton; PageControl: TPageControl; pnlG940: TPanel; tsAbout: TTabSheet; @@ -91,7 +96,6 @@ type bvlProfiles: TBevel; procedure FormCreate(Sender: TObject); - procedure btnRetryClick(Sender: TObject); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType); procedure btnCheckUpdatesClick(Sender: TObject); @@ -107,6 +111,14 @@ type FActiveProfile: TProfile; FLoadingProfiles: Boolean; FStateConsumerTask: IOmniTaskControl; + + FDeviceNotification: Pointer; + FG940Found: Boolean; + protected + procedure RegisterDeviceArrival; + procedure UnregisterDeviceArrival; + + procedure WMDeviceChange(var Msg: TMessage); message WM_DEVICECHANGE; protected procedure FindLEDControls; procedure LoadProfiles; @@ -126,9 +138,6 @@ type procedure EventMonitorTerminated(const task: IOmniTaskControl); procedure HandleDeviceStateMessage(ATask: IOmniTaskControl; AMessage: TOmniMessage); - procedure HandleRunInMainThreadMessage(ATask: IOmniTaskControl; AMessage: TOmniMessage); - procedure HandleProviderKilled(ATask: IOmniTaskControl; AMessage: TOmniMessage); - procedure HandleProviderKilledFSX(ATask: IOmniTaskControl; AMessage: TOmniMessage); procedure CMAskAutoUpdate(var Msg: TMessage); message CM_ASKAUTOUPDATE; @@ -204,9 +213,10 @@ begin FProfiles := TProfileList.Create(True); LoadProfiles; + // TODO implement profile changing properly FStateConsumerTask.Comm.Send(TM_LOADPROFILE, ActiveProfile); -// LoadFunctions(TFSXLEDStateProvider, FFSXComboBoxes); -// LoadDefaultProfile; + + RegisterDeviceArrival; end; @@ -225,10 +235,63 @@ end; procedure TMainForm.FormDestroy(Sender: TObject); begin + UnregisterDeviceArrival; + FreeAndNil(FProfiles); end; +procedure TMainForm.RegisterDeviceArrival; +type + TDevBroadcastDeviceInterface = packed record + dbcc_size: DWORD; + dbcc_devicetype: DWORD; + dbcc_reserved: DWORD; + dbcc_classguid: TGUID; + dbcc_name: PChar; + end; + +var + request: TDevBroadcastDeviceInterface; + +begin + ZeroMemory(@request, SizeOf(request)); + request.dbcc_size := SizeOf(request); + request.dbcc_devicetype := DBT_DEVTYP_DEVICEINTERFACE; + + FDeviceNotification := RegisterDeviceNotification(Self.Handle, @request, + DEVICE_NOTIFY_WINDOW_HANDLE or + DEVICE_NOTIFY_ALL_INTERFACE_CLASSES); +end; + + +procedure TMainForm.UnregisterDeviceArrival; +begin + if Assigned(FDeviceNotification) then + begin + UnregisterDeviceNotification(FDeviceNotification); + FDeviceNotification := nil; + end; +end; + + +procedure TMainForm.WMDeviceChange(var Msg: TMessage); +begin + if not Assigned(StateConsumerTask) then + exit; + + case Msg.WParam of + DBT_DEVICEARRIVAL: + if (not FG940Found) then + StateConsumerTask.Comm.Send(TM_FINDTHROTTLEDEVICE); + + DBT_DEVICEREMOVECOMPLETE: + if FG940Found then + StateConsumerTask.Comm.Send(TM_TESTTHROTTLEDEVICE); + end; +end; + + procedure TMainForm.FindLEDControls; function ComponentByName(const AName: string; ATag: NativeInt): TComponent; @@ -417,36 +480,11 @@ begin imgStateFound.Visible := AFound; imgStateNotFound.Visible := not AFound; + + FG940Found := AFound; end; -//procedure TMainForm.ReadFunctions(AReader: IX2PersistReader; AComboBoxes: TComboBoxArray); -//var -// comboBox: TComboBoxEx; -// value: Integer; -// itemIndex: Integer; -// -//begin -// if AReader.BeginSection(SECTION_FSX) then -// try -// for comboBox in AComboBoxes do -// begin -// if AReader.ReadInteger('Function' + IntToStr(comboBox.Tag), value) then -// begin -// for itemIndex := 0 to Pred(comboBox.ItemsEx.Count) do -// if Integer(comboBox.ItemsEx[itemIndex].Data) = value then -// begin -// comboBox.ItemIndex := itemIndex; -// break; -// end; -// end; -// end; -// finally -// AReader.EndSection; -// end; -//end; - - //procedure TMainForm.ReadAutoUpdate(AReader: IX2PersistReader); //var // checkUpdates: Boolean; @@ -484,29 +522,6 @@ end; //end; -//procedure TMainForm.InitializeStateProvider(AProviderClass: TLEDStateProviderClass); -//begin -// UpdateMapping; -// LEDStateConsumer.InitializeStateProvider(StateConsumerTask, AProviderClass); -//end; -// -// -//procedure TMainForm.FinalizeStateProvider; -//begin -// LEDStateConsumer.FinalizeStateProvider(StateConsumerTask); -//end; - - -//procedure TMainForm.UpdateMapping; -//begin -// if not Assigned(StateConsumerTask) then -// Exit; -// -// LEDStateConsumer.ClearFunctions(StateConsumerTask); -// SetFunctions(FFSXComboBoxes); -//end; - - procedure TMainForm.LEDButtonClick(Sender: TObject); var buttonIndex: NativeInt; @@ -628,32 +643,31 @@ end; procedure TMainForm.EventMonitorMessage(const task: IOmniTaskControl; const msg: TOmniMessage); begin case msg.MsgID of - TM_NOTIFY_DEVICESTATE: HandleDeviceStateMessage(task, msg); -// MSG_RUN_IN_MAINTHREAD: HandleRunInMainThreadMessage(task, msg); -// MSG_PROVIDER_KILLED: HandleProviderKilled(task, msg); + TM_NOTIFY_DEVICESTATE: + HandleDeviceStateMessage(task, msg); MSG_UPDATE: if MessageBox(Self.Handle, 'An update is available on the G940 LED Control website.'#13#10'Do you want to go there now?', 'Update available', MB_YESNO or MB_ICONINFORMATION) = ID_YES then ShellExecute(Self.Handle, 'open', PChar('http://g940.x2software.net/#download'), nil, nil, SW_SHOWNORMAL); -// MSG_NOUPDATE: -// if msg.MsgData.AsBoolean then -// MessageBox(Self.Handle, 'You are using the latest version.', 'No update available', MB_OK or MB_ICONINFORMATION) -// else -// MessageBox(Self.Handle, 'Failed to check for updates. Maybe try again later?', 'Uh-oh', MB_OK or MB_ICONWARNING); + MSG_NOUPDATE: + if msg.MsgData.AsBoolean then + MessageBox(Self.Handle, 'You are using the latest version.', 'No update available', MB_OK or MB_ICONINFORMATION) + else + MessageBox(Self.Handle, 'Failed to check for updates. Maybe try again later?', 'Uh-oh', MB_OK or MB_ICONWARNING); end; end; procedure TMainForm.EventMonitorTerminated(const task: IOmniTaskControl); begin -// if task = StateConsumerTask then -// begin -// FStateConsumerTask := nil; -// Close; -// end else if task.Name = 'CheckForUpdatesThread' then -// btnCheckUpdates.Enabled := True; + if task = StateConsumerTask then + begin + FStateConsumerTask := nil; + Close; + end else if task.Name = 'CheckForUpdatesThread' then + btnCheckUpdates.Enabled := True; end; @@ -667,58 +681,17 @@ begin SetDeviceState(TEXT_STATE_FOUND, True); DEVICESTATE_NOTFOUND: - begin - SetDeviceState(TEXT_STATE_NOTFOUND, False); - btnRetry.Visible := True; - end; + SetDeviceState(TEXT_STATE_NOTFOUND, False); end; end; -procedure TMainForm.HandleRunInMainThreadMessage(ATask: IOmniTaskControl; AMessage: TOmniMessage); -var - executor: IRunInMainThread; - -begin - executor := (AMessage.MsgData.AsInterface as IRunInMainThread); - executor.Execute; - executor.Signal; -end; - - -procedure TMainForm.HandleProviderKilled(ATask: IOmniTaskControl; AMessage: TOmniMessage); -begin - HandleProviderKilledFSX(ATask, AMessage); -end; - - -procedure TMainForm.HandleProviderKilledFSX(ATask: IOmniTaskControl; AMessage: TOmniMessage); -var - msg: string; - -begin -// btnFSXDisconnect.Enabled := False; -// btnFSXConnect.Enabled := True; - - msg := AMessage.MsgData; - if Length(msg) > 0 then - ShowMessage(msg); -end; - - procedure TMainForm.btnCheckUpdatesClick(Sender: TObject); begin CheckForUpdates(True); end; -procedure TMainForm.btnRetryClick(Sender: TObject); -begin - btnRetry.Visible := False; -// StateConsumerTask.Comm.Send(MSG_FINDTHROTTLEDEVICE); -end; - - procedure TMainForm.lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType); begin ShellExecute(Self.Handle, 'open', PChar(Link), nil, nil, SW_SHOWNORMAL); diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj index a3cb490..d357a23 100644 --- a/G940LEDControl/G940LEDControl.dproj +++ b/G940LEDControl/G940LEDControl.dproj @@ -98,7 +98,6 @@ False - true false CompanyName=;FileDescription=;FileVersion=0.2.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=0.2;Comments= F:\Components\X2Utils\Resources\VistaManAsInvoker.manifest diff --git a/G940LEDControl/Units/FSXLEDFunction.pas b/G940LEDControl/Units/FSXLEDFunction.pas index 0f8c13c..f829279 100644 --- a/G940LEDControl/Units/FSXLEDFunction.pas +++ b/G940LEDControl/Units/FSXLEDFunction.pas @@ -92,13 +92,13 @@ type { Worker implementations } TFSXEngineFunctionWorker = class(TCustomFSXFunctionWorker) protected - procedure RegisterVariables; override; + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; procedure HandleData(AData: Pointer); override; end; TFSXGearFunctionWorker = class(TCustomFSXFunctionWorker) protected - procedure RegisterVariables; override; + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; procedure HandleData(AData: Pointer); override; end; @@ -106,7 +106,7 @@ type private FStateMask: Integer; protected - procedure RegisterVariables; override; + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; procedure HandleData(AData: Pointer); override; public property StateMask: Integer read FStateMask write FStateMask; @@ -140,21 +140,21 @@ end; { TFSXEngineFunctionWorker } -procedure TFSXEngineFunctionWorker.RegisterVariables; +procedure TFSXEngineFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); var engineIndex: Integer; begin - Definition.AddVariable('NUMBER OF ENGINES', FSX_UNIT_NUMBER, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('NUMBER OF ENGINES', FSX_UNIT_NUMBER, SIMCONNECT_DATAType_INT32); for engineIndex := 1 to FSX_MAX_ENGINES do - Definition.AddVariable(Format('GENERAL ENG COMBUSTION:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable(Format('GENERAL ENG COMBUSTION:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); for engineIndex := 1 to FSX_MAX_ENGINES do - Definition.AddVariable(Format('ENG FAILED:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable(Format('ENG FAILED:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); for engineIndex := 1 to FSX_MAX_ENGINES do - Definition.AddVariable(Format('ENG ON FIRE:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable(Format('ENG ON FIRE:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); end; @@ -236,12 +236,12 @@ end; { TFSXGearFunctionWorker } -procedure TFSXGearFunctionWorker.RegisterVariables; +procedure TFSXGearFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); begin - Definition.AddVariable('IS GEAR RETRACTABLE', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); - Definition.AddVariable('GEAR TOTAL PCT EXTENDED', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); - Definition.AddVariable('GEAR DAMAGE BY SPEED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); - Definition.AddVariable('GEAR SPEED EXCEEDED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('IS GEAR RETRACTABLE', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('GEAR TOTAL PCT EXTENDED', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); + ADefinition.AddVariable('GEAR DAMAGE BY SPEED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('GEAR SPEED EXCEEDED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); end; @@ -349,9 +349,9 @@ end; { TFSXLightStatesFunctionWorker } -procedure TFSXLightStatesFunctionWorker.RegisterVariables; +procedure TFSXLightStatesFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); begin - Definition.AddVariable('LIGHT ON STATES', FSX_UNIT_MASK, SIMCONNECT_DATATYPE_INT32); + ADefinition.AddVariable('LIGHT ON STATES', FSX_UNIT_MASK, SIMCONNECT_DATATYPE_INT32); end; diff --git a/G940LEDControl/Units/FSXLEDFunctionProvider.pas b/G940LEDControl/Units/FSXLEDFunctionProvider.pas index 19659e1..2a7d61b 100644 --- a/G940LEDControl/Units/FSXLEDFunctionProvider.pas +++ b/G940LEDControl/Units/FSXLEDFunctionProvider.pas @@ -18,7 +18,7 @@ type TFSXLEDFunctionProvider = class(TCustomLEDFunctionProvider, IFSXSimConnectObserver) private - FSimConnect: IFSXSimConnect; + FSimConnect: TInterfacedObject; FSimConnectLock: TCriticalSection; protected procedure RegisterFunctions; override; @@ -41,6 +41,8 @@ type FDisplayName: string; FUID: string; protected + function DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; override; + property Provider: TFSXLEDFunctionProvider read FProvider; protected function GetCategoryName: string; override; @@ -54,26 +56,30 @@ type TCustomFSXFunctionClass = class of TCustomFSXFunction; - TCustomFSXFunctionWorker = class(TCustomLEDFunctionWorker, IFSXSimConnectDataHandler) + TCustomFSXFunctionWorker = class(TCustomLEDFunctionWorker) private + FDataHandler: IFSXSimConnectDataHandler; + FDefinitionID: Cardinal; FSimConnect: IFSXSimConnect; - FDefinition: IFSXSimConnectDefinition; FCurrentStateLock: TCriticalSection; FCurrentState: ILEDStateWorker; protected - procedure RegisterVariables; virtual; abstract; + procedure RegisterStates(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); override; + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); virtual; abstract; - procedure SetCurrentState(const AUID: string); + procedure SetCurrentState(const AUID: string; ANotifyObservers: Boolean = True); overload; virtual; + procedure SetCurrentState(AState: ILEDStateWorker; ANotifyObservers: Boolean = True); overload; virtual; + procedure SetSimConnect(const Value: IFSXSimConnect); virtual; - property Definition: IFSXSimConnectDefinition read FDefinition; - property SimConnect: IFSXSimConnect read FSimConnect; + property DataHandler: IFSXSimConnectDataHandler read FDataHandler; + property DefinitionID: Cardinal read FDefinitionID; + property SimConnect: IFSXSimConnect read FSimConnect write SetSimConnect; protected function GetCurrentState: ILEDStateWorker; override; - { IFSXSimConnectDataHandler } procedure HandleData(AData: Pointer); virtual; abstract; public - constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings; ASimConnect: IFSXSimConnect); + constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); override; destructor Destroy; override; end; @@ -89,6 +95,20 @@ uses SimConnect; +type + TCustomFSXFunctionWorkerDataHandler = class(TInterfacedObject, IFSXSimConnectDataHandler) + private + FWorker: TCustomFSXFunctionWorker; + protected + { IFSXSimConnectDataHandler } + procedure HandleData(AData: Pointer); + + property Worker: TCustomFSXFunctionWorker read FWorker; + public + constructor Create(AWorker: TCustomFSXFunctionWorker); + end; + + { TFSXLEDFunctionProvider } constructor TFSXLEDFunctionProvider.Create; @@ -167,11 +187,13 @@ begin try if not Assigned(FSimConnect) then begin + { Keep an object reference so we don't increment the reference count. + We'll know when it's gone through the ObserveDestroy. } FSimConnect := TFSXSimConnectInterface.Create; - FSimConnect.Attach(Self); + (FSimConnect as IFSXSimConnect).Attach(Self); end; - Result := FSimConnect; + Result := (FSimConnect as IFSXSimConnect); finally FSimConnectLock.Release; end; @@ -189,6 +211,14 @@ begin end; +function TCustomFSXFunction.DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; +begin + Result := inherited DoCreateWorker(ASettings); + + (Result as TCustomFSXFunctionWorker).SimConnect := Provider.GetSimConnect; +end; + + function TCustomFSXFunction.GetCategoryName: string; begin Result := FSXCategory; @@ -208,18 +238,16 @@ end; { TCustomFSXFunctionWorker } -constructor TCustomFSXFunctionWorker.Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings; ASimConnect: IFSXSimConnect); +constructor TCustomFSXFunctionWorker.Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); begin - inherited Create(AStates, ASettings); - FCurrentStateLock := TCriticalSection.Create; - FSimConnect := ASimConnect; - FDefinition := ASimConnect.CreateDefinition; - RegisterVariables; + { We can't pass ourselves as the Data Handler, as it would keep a reference to + this worker from the SimConnect interface. That'd mean the worker never + gets destroyed, and SimConnect never shuts down. Hence this proxy class. } + FDataHandler := TCustomFSXFunctionWorkerDataHandler.Create(Self); - // TODO pass self as callback for this definition - ASimConnect.AddDefinition(FDefinition, Self); + inherited Create(AStates, ASettings); end; @@ -227,7 +255,20 @@ destructor TCustomFSXFunctionWorker.Destroy; begin FreeAndNil(FCurrentStateLock); - inherited; + if DefinitionID <> 0 then + SimConnect.RemoveDefinition(DefinitionID, DataHandler); + + inherited Destroy; +end; + + +procedure TCustomFSXFunctionWorker.RegisterStates(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); +begin + inherited RegisterStates(AStates, ASettings); + + { Make sure we have a default state } + if States.Count > 0 then + SetCurrentState((States[0] as ILEDStateWorker), False); end; @@ -242,18 +283,22 @@ begin end; -procedure TCustomFSXFunctionWorker.SetCurrentState(const AUID: string); -var - newState: ILEDStateWorker; +procedure TCustomFSXFunctionWorker.SetCurrentState(const AUID: string; ANotifyObservers: Boolean); +begin + SetCurrentState(FindState(AUID), ANotifyObservers); +end; + +procedure TCustomFSXFunctionWorker.SetCurrentState(AState: ILEDStateWorker; ANotifyObservers: Boolean); begin FCurrentStateLock.Acquire; try - newState := FindState(AUID); - if newState <> FCurrentState then + if AState <> FCurrentState then begin - FCurrentState := newState; - NotifyObservers; + FCurrentState := AState; + + if ANotifyObservers then + NotifyObservers; end; finally FCurrentStateLock.Release; @@ -261,6 +306,38 @@ begin end; +procedure TCustomFSXFunctionWorker.SetSimConnect(const Value: IFSXSimConnect); +var + definition: IFSXSimConnectDefinition; + +begin + FSimConnect := Value; + + if Assigned(SimConnect) then + begin + definition := SimConnect.CreateDefinition; + RegisterVariables(definition); + + FDefinitionID := SimConnect.AddDefinition(definition, DataHandler); + end; +end; + + +{ TCustomFSXFunctionWorkerDataHandler } +constructor TCustomFSXFunctionWorkerDataHandler.Create(AWorker: TCustomFSXFunctionWorker); +begin + inherited Create; + + FWorker := AWorker; +end; + + +procedure TCustomFSXFunctionWorkerDataHandler.HandleData(AData: Pointer); +begin + Worker.HandleData(AData); +end; + + initialization TLEDFunctionRegistry.Register(TFSXLEDFunctionProvider.Create); diff --git a/G940LEDControl/Units/FSXResources.pas b/G940LEDControl/Units/FSXResources.pas index d1dad2a..b6f6300 100644 --- a/G940LEDControl/Units/FSXResources.pas +++ b/G940LEDControl/Units/FSXResources.pas @@ -2,6 +2,8 @@ unit FSXResources; interface const + FSXSimConnectAppName = 'G940 LED Control'; + FSXProviderUID = 'fsx'; FSXCategory = 'Flight Simulator X'; FSXCategoryLights = FSXCategory + ' - Lights'; diff --git a/G940LEDControl/Units/FSXSimConnectClient.pas b/G940LEDControl/Units/FSXSimConnectClient.pas index e55d7e5..3155883 100644 --- a/G940LEDControl/Units/FSXSimConnectClient.pas +++ b/G940LEDControl/Units/FSXSimConnectClient.pas @@ -23,8 +23,8 @@ type procedure Detach(AObserver: IFSXSimConnectObserver); function CreateDefinition: IFSXSimConnectDefinition; - procedure AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler); - procedure RemoveDefinition(ADataHandler: IFSXSimConnectDataHandler); + function AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler): Integer; + procedure RemoveDefinition(ADefinitionID: Cardinal; ADataHandler: IFSXSimConnectDataHandler); public constructor Create; destructor Destroy; override; @@ -33,36 +33,149 @@ type implementation uses + Generics.Collections, + System.Math, + System.SyncObjs, System.SysUtils, + Winapi.Windows, - SimConnect; + OtlComm, + OtlCommon, + SimConnect, + + FSXResources; const TM_ADDDEFINITION = 3001; TM_REMOVEDEFINITION = 3002; + TM_TRYSIMCONNECT = 3003; + + TIMER_TRYSIMCONNECT = 201; + + INTERVAL_TRYSIMCONNECT = 5000; type - TFSXSimConnectClient = class(TOmniWorker) + TFSXSimConnectDefinitionRef = class(TObject) + private + FDefinition: IFSXSimConnectDefinitionAccess; + FDataHandlers: TInterfaceList; + protected + property DataHandlers: TInterfaceList read FDataHandlers; + public + constructor Create(ADefinition: IFSXSimConnectDefinitionAccess); + destructor Destroy; override; + + procedure Attach(ADataHandler: IFSXSimConnectDataHandler); + procedure Detach(ADataHandler: IFSXSimConnectDataHandler); + + procedure HandleData(AData: Pointer); + + property Definition: IFSXSimConnectDefinitionAccess read FDefinition; end; - TFSXSimConnectDefinition = class(TInterfacedObject, IFSXSimConnectDefinition) + TFSXSimConnectDefinitionMap = TDictionary; + + TFSXSimConnectClient = class(TOmniWorker) + private + FDefinitions: TFSXSimConnectDefinitionMap; + FLastDefinitionID: Cardinal; + FSimConnectHandle: THandle; + FSimConnectDataEvent: TEvent; + protected + procedure TMAddDefinition(var Msg: TOmniMessage); message TM_ADDDEFINITION; + procedure TMRemoveDefinition(var Msg: TOmniMessage); message TM_REMOVEDEFINITION; + procedure TMTrySimConnect(var Msg: TOmniMessage); message TM_TRYSIMCONNECT; + + procedure HandleSimConnectDataEvent; + protected + function Initialize: Boolean; override; + procedure Cleanup; override; + + procedure TrySimConnect; + + procedure RegisterDefinitions; + procedure RegisterDefinition(ADefinitionID: Cardinal; ADefinition: IFSXSimConnectDefinitionAccess); + + function SameDefinition(ADefinition1, ADefinition2: IFSXSimConnectDefinitionAccess): Boolean; + + property Definitions: TFSXSimConnectDefinitionMap read FDefinitions; + property LastDefinitionID: Cardinal read FLastDefinitionID; + property SimConnectHandle: THandle read FSimConnectHandle; + property SimConnectDataEvent: TEvent read FSimConnectDataEvent; + end; + + + TFSXSimConnectVariable = class(TInterfacedPersistent, IFSXSimConnectVariable) + private + FVariableName: string; + FUnitsName: string; + FDataType: SIMCONNECT_DATAType; + FEpsilon: Single; + protected + { IFSXSimConnectVariable } + function GetVariableName: string; + function GetUnitsName: string; + function GetDataType: SIMCONNECT_DATAType; + function GetEpsilon: Single; + public + constructor Create(AVariableName, AUnitsName: string; ADataType: SIMCONNECT_DATAType; AEpsilon: Single); + end; + + + TFSXSimConnectVariableList = TObjectList; + + TFSXSimConnectDefinition = class(TInterfacedObject, IFSXSimConnectDefinition, IFSXSimConnectDefinitionAccess) private FSimConnect: IFSXSimConnect; + FVariables: TFSXSimConnectVariableList; protected property SimConnect: IFSXSimConnect read FSimConnect; + property Variables: TFSXSimConnectVariableList read FVariables; protected { IFSXSimConnectDefinition } - procedure AddVariable(AVariableName, AUnitsName: string; ADatumType: SIMCONNECT_DATAType; AEpsilon: Single = 0); - procedure Apply(ASimConnectHandle: THandle; ADefinitionID: Integer); + procedure AddVariable(AVariableName, AUnitsName: string; ADataType: SIMCONNECT_DATAType; AEpsilon: Single = 0); + + { IFSXSimConnectDefinitionAccess } + function GetVariableCount: Integer; + function GetVariable(AIndex: Integer): IFSXSimConnectVariable; public - constructor Create(ASimConnect: IFSXSimConnect); + constructor Create; + destructor Destroy; override; end; + TAddDefinitionValue = class(TOmniWaitableValue) + private + FDataHandler: IFSXSimConnectDataHandler; + FDefinition: IFSXSimConnectDefinition; + FDefinitionID: Cardinal; + + procedure SetDefinitionID(const Value: Cardinal); + public + constructor Create(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler); + + property DataHandler: IFSXSimConnectDataHandler read FDataHandler; + property Definition: IFSXSimConnectDefinition read FDefinition; + + property DefinitionID: Cardinal read FDefinitionID write SetDefinitionID; + end; + + + TRemoveDefinitionValue = class(TOmniWaitableValue) + private + FDataHandler: IFSXSimConnectDataHandler; + FDefinitionID: Cardinal; + public + constructor Create(ADefinitionID: Cardinal; ADataHandler: IFSXSimConnectDataHandler); + + property DataHandler: IFSXSimConnectDataHandler read FDataHandler; + property DefinitionID: Cardinal read FDefinitionID; + end; + { TFSXSimConnectInterface } constructor TFSXSimConnectInterface.Create; @@ -70,21 +183,30 @@ var worker: IOmniWorker; begin - worker := TFSXSimConnectClient.Create; - FClient := CreateTask(worker); + inherited Create; FObservers := TInterfaceList.Create; + + worker := TFSXSimConnectClient.Create; + FClient := CreateTask(worker).Run; end; destructor TFSXSimConnectInterface.Destroy; +var + observer: IInterface; + begin + for observer in Observers do + (observer as IFSXSimConnectObserver).ObserveDestroy(Self); + FreeAndNil(FObservers); + // TODO this doesn't get triggered yet. The connection is killed fine, but not because of us. Needs work. FClient.Terminate; FClient := nil; - inherited; + inherited Destroy; end; @@ -102,41 +224,386 @@ end; function TFSXSimConnectInterface.CreateDefinition: IFSXSimConnectDefinition; begin - Result := TFSXSimConnectDefinition.Create(Self); + Result := TFSXSimConnectDefinition.Create; end; -procedure TFSXSimConnectInterface.AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler); +function TFSXSimConnectInterface.AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler): Integer; +var + addDefinition: TAddDefinitionValue; + begin - Client.Comm.Send(TM_ADDDEFINITION, [ADefinition, ADataHandler]); - // TODO pass to thread; if definition already exists (same variables), link to existing definition to avoid too many SimConnect definition + addDefinition := TAddDefinitionValue.Create(ADefinition, ADataHandler); + Client.Comm.Send(TM_ADDDEFINITION, addDefinition); + + addDefinition.WaitFor(INFINITE); + Result := addDefinition.DefinitionID; end; -procedure TFSXSimConnectInterface.RemoveDefinition(ADataHandler: IFSXSimConnectDataHandler); +procedure TFSXSimConnectInterface.RemoveDefinition(ADefinitionID: Cardinal; ADataHandler: IFSXSimConnectDataHandler); +var + removeDefinition: TRemoveDefinitionValue; + begin - Client.Comm.Send(TM_REMOVEDEFINITION, ADataHandler); + removeDefinition := TRemoveDefinitionValue.Create(ADefinitionID, ADataHandler); + Client.Comm.Send(TM_REMOVEDEFINITION, removeDefinition); + + removeDefinition.WaitFor(INFINITE); end; { TFSXSimConnectDefinition } -constructor TFSXSimConnectDefinition.Create(ASimConnect: IFSXSimConnect); +constructor TFSXSimConnectDefinition.Create; begin + inherited Create; + FVariables := TFSXSimConnectVariableList.Create(True); end; -procedure TFSXSimConnectDefinition.AddVariable(AVariableName, AUnitsName: string; ADatumType: SIMCONNECT_DATAType; AEpsilon: Single); +destructor TFSXSimConnectDefinition.Destroy; begin + FreeAndNil(FVariables); + inherited Destroy; end; -procedure TFSXSimConnectDefinition.Apply(ASimConnectHandle: THandle; ADefinitionID: Integer); +procedure TFSXSimConnectDefinition.AddVariable(AVariableName, AUnitsName: string; ADataType: SIMCONNECT_DATAType; AEpsilon: Single); begin -// SimConnect_AddToDataDefinition(ASimConnectHandle, ADefinitionID, -// AnsiString(AVariableName), AnsiString(AUnitsName), ADatumType, AEpsilon, 0); + Variables.Add(TFSXSimConnectVariable.Create(AVariableName, AUnitsName, ADataType, AEpsilon)); +end; + + +function TFSXSimConnectDefinition.GetVariable(AIndex: Integer): IFSXSimConnectVariable; +begin + Result := Variables[AIndex]; +end; + + +function TFSXSimConnectDefinition.GetVariableCount: Integer; +begin + Result := Variables.Count; +end; + + +{ TFSXSimConnectClient } +function TFSXSimConnectClient.Initialize: Boolean; +begin + Result := inherited Initialize; + if not Result then + exit; + + FDefinitions := TFSXSimConnectDefinitionMap.Create; + FSimConnectDataEvent := TEvent.Create(nil, False, False, ''); + + Task.RegisterWaitObject(SimConnectDataEvent.Handle, HandleSimConnectDataEvent); + + TrySimConnect; +end; + + +procedure TFSXSimConnectClient.Cleanup; +begin + // TODO unregister definitions ? + if SimConnectHandle <> 0 then + SimConnect_Close(SimConnectHandle); + + FreeAndNil(FSimConnectDataEvent); + FreeAndNil(FDefinitions); + + inherited Cleanup; +end; + + +procedure TFSXSimConnectClient.TrySimConnect; +begin + if SimConnectHandle <> 0 then + exit; + + if InitSimConnect then + begin + if SimConnect_Open(FSimConnectHandle, FSXSimConnectAppName, 0, 0, SimConnectDataEvent.Handle, 0) = S_OK then + begin + Task.ClearTimer(TIMER_TRYSIMCONNECT); + RegisterDefinitions; + end; + end; + + if SimConnectHandle = 0 then + Task.SetTimer(TIMER_TRYSIMCONNECT, INTERVAL_TRYSIMCONNECT, TM_TRYSIMCONNECT); +end; + + +procedure TFSXSimConnectClient.HandleSimConnectDataEvent; +var + data: PSimConnectRecv; + dataSize: Cardinal; + simObjectData: PSimConnectRecvSimObjectData; + definitionRef: TFSXSimConnectDefinitionRef; + +begin + while (SimConnectHandle <> 0) and + (SimConnect_GetNextDispatch(SimConnectHandle, data, dataSize) = S_OK) do + begin + case SIMCONNECT_RECV_ID(data^.dwID) of + SIMCONNECT_RECV_ID_SIMOBJECT_DATA: + begin + simObjectData := PSimConnectRecvSimObjectData(data); + + if Definitions.ContainsKey(simObjectData^.dwDefineID) then + begin + definitionRef := Definitions[simObjectData^.dwDefineID]; + definitionRef.HandleData(@simObjectData^.dwData); + end; + end; + + SIMCONNECT_RECV_ID_QUIT: + begin + FSimConnectHandle := 0; + Task.SetTimer(TIMER_TRYSIMCONNECT, INTERVAL_TRYSIMCONNECT, TM_TRYSIMCONNECT); + end; + end; + end; +end; + + +procedure TFSXSimConnectClient.RegisterDefinitions; +var + definitionID: Cardinal; + +begin + if SimConnectHandle = 0 then + exit; + + for definitionID in Definitions.Keys do + RegisterDefinition(definitionID, Definitions[definitionID].Definition); +end; + + +procedure TFSXSimConnectClient.RegisterDefinition(ADefinitionID: Cardinal; ADefinition: IFSXSimConnectDefinitionAccess); +var + variableIndex: Integer; + variable: IFSXSimConnectVariable; + +begin + if SimConnectHandle = 0 then + exit; + + for variableIndex := 0 to Pred(ADefinition.GetVariableCount) do + begin + variable := ADefinition.GetVariable(variableIndex); + SimConnect_AddToDataDefinition(SimConnectHandle, ADefinitionID, + AnsiString(variable.GetVariableName), + AnsiString(variable.GetUnitsName), + variable.GetDataType, + variable.GetEpsilon); + end; + + SimConnect_RequestDataOnSimObject(SimConnectHandle, ADefinitionID, ADefinitionID, + SIMCONNECT_OBJECT_ID_USER, + SIMCONNECT_PERIOD_SIM_FRAME, + SIMCONNECT_DATA_REQUEST_FLAG_CHANGED); +end; + + +function TFSXSimConnectClient.SameDefinition(ADefinition1, ADefinition2: IFSXSimConnectDefinitionAccess): Boolean; +var + variableIndex: Integer; + variable1: IFSXSimConnectVariable; + variable2: IFSXSimConnectVariable; + +begin + if ADefinition1.GetVariableCount = ADefinition2.GetVariableCount then + begin + Result := True; + + { Order is very important in the definitions, as the Data Handler depends + on it to interpret the data. } + for variableIndex := 0 to Pred(ADefinition1.GetVariableCount) do + begin + variable1 := ADefinition1.GetVariable(variableIndex); + variable2 := ADefinition2.GetVariable(variableIndex); + + if (variable1.GetVariableName <> variable2.GetVariableName) or + (variable1.GetUnitsName <> variable2.GetUnitsName) or + (variable1.GetDataType <> variable2.GetDataType) or + (not SameValue(variable1.GetEpsilon, variable2.GetEpsilon, 0.00001)) then + begin + Result := False; + break; + end; + end; + end else + Result := False; +end; + + +procedure TFSXSimConnectClient.TMAddDefinition(var Msg: TOmniMessage); +var + addDefinition: TAddDefinitionValue; + definitionID: Cardinal; + definitionRef: TFSXSimConnectDefinitionRef; + definitionAccess: IFSXSimConnectDefinitionAccess; + hasDefinition: Boolean; + +begin + addDefinition := Msg.MsgData; + definitionAccess := (addDefinition.Definition as IFSXSimConnectDefinitionAccess); + hasDefinition := False; + + { Attempt to re-use existing definition to save on SimConnect traffic } + for definitionID in Definitions.Keys do + begin + definitionRef := Definitions[definitionID]; + + if SameDefinition(definitionRef.Definition, definitionAccess) then + begin + definitionRef.Attach(addDefinition.DataHandler); + addDefinition.DefinitionID := definitionID; + hasDefinition := True; + break; + end; + end; + + if not hasDefinition then + begin + { Add as new definition } + Inc(FLastDefinitionID); + + definitionRef := TFSXSimConnectDefinitionRef.Create(definitionAccess); + definitionRef.Attach(addDefinition.DataHandler); + + Definitions.Add(LastDefinitionID, definitionRef); + addDefinition.DefinitionID := LastDefinitionID; + + { Register with SimConnect } + RegisterDefinition(LastDefinitionID, definitionAccess); + end; +end; + + +procedure TFSXSimConnectClient.TMRemoveDefinition(var Msg: TOmniMessage); +var + removeDefinition: TRemoveDefinitionValue; + +begin + removeDefinition := Msg.MsgData; + + // TODO actually remove the definition + + removeDefinition.Signal; +end; + + +procedure TFSXSimConnectClient.TMTrySimConnect(var Msg: TOmniMessage); +begin + TrySimConnect; +end; + + +{ TFSXSimConnectDefinitionRef } +constructor TFSXSimConnectDefinitionRef.Create(ADefinition: IFSXSimConnectDefinitionAccess); +begin + inherited Create; + + FDataHandlers := TInterfaceList.Create; + FDefinition := ADefinition; +end; + + +destructor TFSXSimConnectDefinitionRef.Destroy; +begin + FreeAndNil(FDataHandlers); + + inherited Destroy; +end; + + +procedure TFSXSimConnectDefinitionRef.HandleData(AData: Pointer); +var + dataHandler: IInterface; + +begin + for dataHandler in DataHandlers do + (dataHandler as IFSXSimConnectDataHandler).HandleData(AData); +end; + + +procedure TFSXSimConnectDefinitionRef.Attach(ADataHandler: IFSXSimConnectDataHandler); +begin + DataHandlers.Add(ADataHandler as IFSXSimConnectDataHandler); +end; + + +procedure TFSXSimConnectDefinitionRef.Detach(ADataHandler: IFSXSimConnectDataHandler); +begin + DataHandlers.Remove(ADataHandler as IFSXSimConnectDataHandler); +end; + + +{ TFSXSimConnectVariable } +constructor TFSXSimConnectVariable.Create(AVariableName, AUnitsName: string; ADataType: SIMCONNECT_DATAType; AEpsilon: Single); +begin + inherited Create; + + FVariableName := AVariableName; + FUnitsName := AUnitsName; + FDataType := ADataType; + FEpsilon := AEpsilon; +end; + + +function TFSXSimConnectVariable.GetVariableName: string; +begin + Result := FVariableName; +end; + + +function TFSXSimConnectVariable.GetUnitsName: string; +begin + Result := FUnitsName; +end; + + +function TFSXSimConnectVariable.GetDataType: SIMCONNECT_DATAType; +begin + Result := FDataType; +end; + + +function TFSXSimConnectVariable.GetEpsilon: Single; +begin + Result := FEpsilon; +end; + + +{ TAddDefinitionValue } +constructor TAddDefinitionValue.Create(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler); +begin + inherited Create; + + FDefinition := ADefinition; + FDataHandler := ADataHandler; +end; + + +procedure TAddDefinitionValue.SetDefinitionID(const Value: Cardinal); +begin + FDefinitionID := Value; + Signal; +end; + + +{ TRemoveDefinitionValue } +constructor TRemoveDefinitionValue.Create(ADefinitionID: Cardinal; ADataHandler: IFSXSimConnectDataHandler); +begin + inherited Create; + + FDefinitionID := ADefinitionID; + FDataHandler := ADataHandler; end; end. diff --git a/G940LEDControl/Units/FSXSimConnectIntf.pas b/G940LEDControl/Units/FSXSimConnectIntf.pas index 928a9d9..8b9bae5 100644 --- a/G940LEDControl/Units/FSXSimConnectIntf.pas +++ b/G940LEDControl/Units/FSXSimConnectIntf.pas @@ -28,18 +28,32 @@ type procedure Detach(AObserver: IFSXSimConnectObserver); function CreateDefinition: IFSXSimConnectDefinition; - procedure AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler); - procedure RemoveDefinition(ADataHandler: IFSXSimConnectDataHandler); + function AddDefinition(ADefinition: IFSXSimConnectDefinition; ADataHandler: IFSXSimConnectDataHandler): Integer; + procedure RemoveDefinition(ADefinitionID: Cardinal; ADataHandler: IFSXSimConnectDataHandler); end; IFSXSimConnectDefinition = interface ['{F1EAB3B1-0A3D-4B06-A75F-823E15C313B8}'] - procedure AddVariable(AVariableName, AUnitsName: string; ADatumType: SIMCONNECT_DATAType; AEpsilon: Single = 0); - procedure Apply(ASimConnectHandle: THandle; ADefinitionID: Integer); + procedure AddVariable(AVariableName, AUnitsName: string; ADataType: SIMCONNECT_DATAType; AEpsilon: Single = 0); end; + IFSXSimConnectVariable = interface + ['{A41AD003-77C0-4E34-91E3-B0BAADD08FCE}'] + function GetVariableName: string; + function GetUnitsName: string; + function GetDataType: SIMCONNECT_DATAType; + function GetEpsilon: Single; + end; + + + IFSXSimConnectDefinitionAccess = interface + ['{2592534C-0344-4442-8A5F-1AB34B96E1B5}'] + function GetVariableCount: Integer; + function GetVariable(AIndex: Integer): IFSXSimConnectVariable; + end; + const FSX_UNIT_PERCENT = 'percent'; diff --git a/G940LEDControl/Units/G940LEDStateConsumer.pas b/G940LEDControl/Units/G940LEDStateConsumer.pas index caf058c..9df311e 100644 --- a/G940LEDControl/Units/G940LEDStateConsumer.pas +++ b/G940LEDControl/Units/G940LEDStateConsumer.pas @@ -13,7 +13,9 @@ uses const TM_FINDTHROTTLEDEVICE = 2001; - TM_NOTIFY_DEVICESTATE = 2002; + TM_TESTTHROTTLEDEVICE = 2002; + + TM_NOTIFY_DEVICESTATE = 2003; type @@ -21,10 +23,13 @@ type private FDirectInput: IDirectInput8; FThrottleDevice: IDirectInputDevice8; + FTHrottleDeviceGUID: TGUID; protected - procedure MsgFindThrottleDevice(var msg: TOmniMessage); message TM_FINDTHROTTLEDEVICE; + procedure TMFindThrottleDevice(var Msg: TOmniMessage); message TM_FINDTHROTTLEDEVICE; + procedure TMTestThrottleDevice(var Msg: TOmniMessage); message TM_TESTTHROTTLEDEVICE; protected function Initialize: Boolean; override; + procedure Cleanup; override; procedure FindThrottleDevice; procedure FoundThrottleDevice(ADeviceGUID: TGUID); @@ -35,6 +40,7 @@ type property DirectInput: IDirectInput8 read FDirectInput; property ThrottleDevice: IDirectInputDevice8 read FThrottleDevice; + property ThrottleDeviceGUID: TGUID read FTHrottleDeviceGUID; end; @@ -63,23 +69,6 @@ const G940_BUTTONCOUNT = 8; - -(* -type - TRunInMainThreadSetLEDs = class(TOmniWaitableValue, IRunInMainThread) - private - FDevice: IDirectInputDevice8; - FRed: Byte; - FGreen: Byte; - protected - { IRunInMainThread } - procedure Execute; - public - constructor Create(ADevice: IDirectInputDevice8; ARed, AGreen: Byte); - end; -*) - - function EnumDevicesProc(var lpddi: TDIDeviceInstanceW; pvRef: Pointer): BOOL; stdcall; var vendorID: Word; @@ -127,81 +116,13 @@ begin end; -{ -procedure TG940LEDStateConsumer.ResetLEDState; +procedure TG940LEDStateConsumer.Cleanup; begin - FRed := 0; - FGreen := $FF; - - inherited; -end; - - -procedure TG940LEDStateConsumer.LEDStateChanged(ALEDIndex: Integer; AState: TLEDState); - - procedure SetBit(var AMask: Byte; ABit: Integer; ASet: Boolean); inline; - begin - if ASet then - AMask := AMask or (1 shl ABit) - else - AMask := AMask and not (1 shl ABit); - end; - -var - red: Boolean; - green: Boolean; - -begin - red := False; - green := False; - - case AState of - lsGreen: - green := True; - - lsAmber: - begin - red := True; - green := True; - end; - - lsRed: - red := True; - - lsWarning: - begin - red := True; - green := True; - - StartBlinkTimer; - end; - - lsError: - begin - red := True; - - StartBlinkTimer; - end; - end; - - SetBit(FRed, ALEDIndex, red); - SetBit(FGreen, ALEDIndex, green); - - inherited; -end; - -} - -{ -procedure TG940LEDStateConsumer.Changed; -begin - inherited; + inherited Cleanup; if Assigned(ThrottleDevice) then - { Logitech SDK will not change the color outside of the main thread - RunInMainThread(TRunInMainThreadSetLEDs.Create(ThrottleDevice, FRed, FGreen), Destroying); + SetLEDs(ThrottleDevice, 0, $FF); end; -} procedure TG940LEDStateConsumer.FindThrottleDevice; @@ -222,7 +143,10 @@ end; procedure TG940LEDStateConsumer.FoundThrottleDevice(ADeviceGUID: TGUID); begin if DirectInput.CreateDevice(ADeviceGUID, FThrottleDevice, nil) = S_OK then + begin + FTHrottleDeviceGUID := ADeviceGUID; SetDeviceState(DEVICESTATE_FOUND); + end; end; @@ -279,28 +203,22 @@ begin end; -procedure TG940LEDStateConsumer.MsgFindThrottleDevice(var msg: TOmniMessage); +procedure TG940LEDStateConsumer.TMFindThrottleDevice(var Msg: TOmniMessage); begin FindThrottleDevice; end; -{ TRunInMainThreadSetLEDs } -(* -constructor TRunInMainThreadSetLEDs.Create(ADevice: IDirectInputDevice8; ARed, AGreen: Byte); +procedure TG940LEDStateConsumer.TMTestThrottleDevice(var Msg: TOmniMessage); begin - inherited Create; - - FDevice := ADevice; - FRed := ARed; - FGreen := AGreen; + if Assigned(ThrottleDevice) then + begin + if DirectInput.GetDeviceStatus(ThrottleDeviceGUID) = DI_NOTATTACHED then + begin + FThrottleDevice := nil; + SetDeviceState(DEVICESTATE_NOTFOUND); + end; + end; end; - -procedure TRunInMainThreadSetLEDs.Execute; -begin - SetLEDs(FDevice, FRed, FGreen); -end; -*) - end. diff --git a/G940LEDControl/Units/LEDFunction.pas b/G940LEDControl/Units/LEDFunction.pas index 247ced4..98448ad 100644 --- a/G940LEDControl/Units/LEDFunction.pas +++ b/G940LEDControl/Units/LEDFunction.pas @@ -81,8 +81,8 @@ type function GetCurrentState: ILEDStateWorker; virtual; abstract; public - constructor Create; overload; - constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); overload; + constructor Create; overload; virtual; + constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); overload; virtual; destructor Destroy; override; end; @@ -138,7 +138,7 @@ destructor TCustomMultiStateLEDFunction.Destroy; begin FreeAndNil(FStates); - inherited; + inherited Destroy; end; @@ -229,6 +229,8 @@ var begin Result := nil; + if not Assigned(States) then + exit; for state in States do if (state as ICustomLEDState).GetUID = AUID then @@ -263,7 +265,7 @@ destructor TCustomLEDFunctionProvider.Destroy; begin FreeAndNil(FFunctions); - inherited; + inherited Destroy; end; diff --git a/G940LEDControl/Units/LEDFunctionRegistry.pas b/G940LEDControl/Units/LEDFunctionRegistry.pas index c328115..a4605f7 100644 --- a/G940LEDControl/Units/LEDFunctionRegistry.pas +++ b/G940LEDControl/Units/LEDFunctionRegistry.pas @@ -114,7 +114,7 @@ destructor TLEDFunctionRegistry.Destroy; begin FreeAndNil(FProviders); - inherited; + inherited Destroy; end; @@ -155,7 +155,7 @@ destructor TLEDFunctionProviderList.Destroy; begin FreeAndNil(FList); - inherited; + inherited Destroy; end; diff --git a/G940LEDControl/Units/LEDStateConsumer.pas b/G940LEDControl/Units/LEDStateConsumer.pas index a791bc5..09b998a 100644 --- a/G940LEDControl/Units/LEDStateConsumer.pas +++ b/G940LEDControl/Units/LEDStateConsumer.pas @@ -21,13 +21,7 @@ const type - IRunInMainThread = interface(IOmniWaitableValue) - ['{68B8F2F7-ED40-4078-9D99-503D7AFA068B}'] - procedure Execute; - end; - - - TLEDStateConsumer = class(TOmniWorker) + TLEDStateConsumer = class(TOmniWorker, ILEDFunctionObserver) private FButtonWorkers: TInterfaceList; FButtonColors: TInterfaceList; @@ -42,6 +36,9 @@ type property ButtonColors: TInterfaceList read FButtonColors; property HasTickTimer: Boolean read FHasTickTimer; protected + { ILEDFunctionObserver } + procedure ObserveUpdate(Sender: ILEDFunctionWorker); + procedure Changed; virtual; procedure Update; virtual; abstract; protected @@ -171,21 +168,49 @@ begin end; +procedure TLEDStateConsumer.ObserveUpdate(Sender: ILEDFunctionWorker); +begin + Changed; +end; + + procedure TLEDStateConsumer.TMLoadProfile(var Msg: TOmniMessage); var + oldWorkers: TInterfaceList; + oldWorker: IInterface; profile: TProfile; buttonIndex: Integer; + worker: ILEDFunctionWorker; begin profile := Msg.MsgData; - ButtonWorkers.Clear; - for buttonIndex := 0 to Pred(profile.ButtonCount) do - begin - if profile.HasButton(buttonIndex) then - ButtonWorkers.Add(CreateWorker(profile.Buttons[buttonIndex]) as ILEDFunctionWorker) - else - ButtonWorkers.Add(nil); + { Keep a copy of the old workers until all the new ones are initialized, + so we don't get unneccessary SimConnect reconnects. } + oldWorkers := TInterfaceList.Create; + try + for oldWorker in ButtonWorkers do + begin + if Assigned(oldWorker) then + oldWorkers.Add(oldWorker); + end; + + ButtonWorkers.Clear; + + for buttonIndex := 0 to Pred(profile.ButtonCount) do + begin + if profile.HasButton(buttonIndex) then + begin + worker := CreateWorker(profile.Buttons[buttonIndex]) as ILEDFunctionWorker; + ButtonWorkers.Add(worker); + + if Assigned(worker) then + worker.Attach(Self); + end else + ButtonWorkers.Add(nil); + end; + finally + FreeAndNil(oldWorkers); end; Changed; diff --git a/G940LEDControl/Units/Profile.pas b/G940LEDControl/Units/Profile.pas index 66eb916..a4a1c6a 100644 --- a/G940LEDControl/Units/Profile.pas +++ b/G940LEDControl/Units/Profile.pas @@ -188,7 +188,7 @@ destructor TProfile.Destroy; begin FreeAndNil(FButtons); - inherited; + inherited Destroy; end; From 25a26c188832b7d6c9d7390bc32608abbf26cec7 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Thu, 21 Feb 2013 22:31:44 +0000 Subject: [PATCH 12/16] Removed todo --- G940LEDControl/Units/FSXSimConnectClient.pas | 1 - 1 file changed, 1 deletion(-) diff --git a/G940LEDControl/Units/FSXSimConnectClient.pas b/G940LEDControl/Units/FSXSimConnectClient.pas index 3155883..70a4c37 100644 --- a/G940LEDControl/Units/FSXSimConnectClient.pas +++ b/G940LEDControl/Units/FSXSimConnectClient.pas @@ -202,7 +202,6 @@ begin FreeAndNil(FObservers); - // TODO this doesn't get triggered yet. The connection is killed fine, but not because of us. Needs work. FClient.Terminate; FClient := nil; From 2cef07e33733b7277c518a9fbf11f10b844a46c2 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Fri, 22 Feb 2013 21:52:12 +0000 Subject: [PATCH 13/16] Added: converting old registry settings to new XMLs Added: framework for all converted FSX functions --- G940LEDControl/Forms/MainFrm.dfm | 2 +- G940LEDControl/Forms/MainFrm.pas | 137 +++-- G940LEDControl/G940LEDControl.dpr | 4 +- G940LEDControl/G940LEDControl.dproj | 2 + G940LEDControl/Units/ConfigConversion.pas | 118 ++-- G940LEDControl/Units/FSXLEDFunction.pas | 499 +++++++++++------ .../Units/FSXLEDFunctionProvider.pas | 57 +- G940LEDControl/Units/FSXLEDFunctionWorker.pas | 526 ++++++++++++++++++ G940LEDControl/Units/FSXResources.pas | 71 +++ G940LEDControl/Units/FSXSimConnectClient.pas | 5 +- G940LEDControl/Units/LEDStateConsumer.pas | 3 + G940LEDControl/Units/Settings.pas | 63 +++ 12 files changed, 1192 insertions(+), 295 deletions(-) create mode 100644 G940LEDControl/Units/FSXLEDFunctionWorker.pas create mode 100644 G940LEDControl/Units/Settings.pas diff --git a/G940LEDControl/Forms/MainFrm.dfm b/G940LEDControl/Forms/MainFrm.dfm index 857ac1a..f43fd5a 100644 --- a/G940LEDControl/Forms/MainFrm.dfm +++ b/G940LEDControl/Forms/MainFrm.dfm @@ -14,7 +14,6 @@ object MainForm: TMainForm Font.Style = [] OldCreateOrder = False Position = poScreenCenter - OnCloseQuery = FormCloseQuery OnCreate = FormCreate OnDestroy = FormDestroy PixelsPerInch = 96 @@ -450,6 +449,7 @@ object MainForm: TMainForm Height = 17 Caption = ' Automatically check for &updates' TabOrder = 2 + OnClick = cbCheckUpdatesClick end object btnCheckUpdates: TButton Left = 344 diff --git a/G940LEDControl/Forms/MainFrm.pas b/G940LEDControl/Forms/MainFrm.pas index 7cb4299..abed7b8 100644 --- a/G940LEDControl/Forms/MainFrm.pas +++ b/G940LEDControl/Forms/MainFrm.pas @@ -20,7 +20,8 @@ uses X2UtPersistIntf, LEDStateConsumer, - Profile, Vcl.AppEvnts; + Profile, + Settings; const @@ -96,12 +97,12 @@ type bvlProfiles: TBevel; procedure FormCreate(Sender: TObject); - procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType); procedure btnCheckUpdatesClick(Sender: TObject); procedure LEDButtonClick(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure cmbProfilesClick(Sender: TObject); + procedure cbCheckUpdatesClick(Sender: TObject); private FLEDControls: array[0..LED_COUNT - 1] of TLEDControls; FEventMonitor: TOmniEventMonitor; @@ -114,6 +115,9 @@ type FDeviceNotification: Pointer; FG940Found: Boolean; + + FSettingsFileName: string; + FSettings: TSettings; protected procedure RegisterDeviceArrival; procedure UnregisterDeviceArrival; @@ -124,6 +128,9 @@ type procedure LoadProfiles; procedure SaveProfiles; + procedure LoadSettings; + procedure SaveSettings; + function CreateDefaultProfile: TProfile; procedure LoadActiveProfile; procedure UpdateButton(AProfile: TProfile; AButtonIndex: Integer); @@ -144,6 +151,7 @@ type property ActiveProfile: TProfile read FActiveProfile; property EventMonitor: TOmniEventMonitor read FEventMonitor; property Profiles: TProfileList read FProfiles; + property Settings: TSettings read FSettings; property StateConsumerTask: IOmniTaskControl read FStateConsumerTask; end; @@ -178,6 +186,7 @@ const DefaultProfileName = 'Default'; FILENAME_PROFILES = 'G940LEDControl\Profiles.xml'; + FILENAME_SETTINGS = 'G940LEDControl\Settings.xml'; SPECIAL_CATEGORY = -1; @@ -213,26 +222,16 @@ begin FProfiles := TProfileList.Create(True); LoadProfiles; - // TODO implement profile changing properly + FSettingsFileName := App.UserPath + FILENAME_SETTINGS; + LoadSettings; + + // #ToDo1 -oMvR: 22-2-2013: implement profile changing properly FStateConsumerTask.Comm.Send(TM_LOADPROFILE, ActiveProfile); RegisterDeviceArrival; end; -procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); -begin -// SaveProfiles; -// if Assigned(StateConsumerTask) then -// begin -// SaveDefaultProfile; -// -// LEDStateConsumer.Finalize(StateConsumerTask); -// CanClose := False; -// end; -end; - - procedure TMainForm.FormDestroy(Sender: TObject); begin UnregisterDeviceArrival; @@ -395,6 +394,55 @@ begin end; +procedure TMainForm.LoadSettings; +var + persistXML: TX2UtPersistXML; + +begin + if not FileExists(FSettingsFileName) then + begin + { Check if version 0.x settings are in the registry } + FSettings := ConfigConversion.ConvertSettings0To1; + + if not Assigned(FSettings) then + FSettings := TSettings.Create; + end else + begin + FSettings := TSettings.Create; + + persistXML := TX2UtPersistXML.Create; + try + persistXML.FileName := FSettingsFileName; + Settings.Load(persistXML.CreateReader); + finally + FreeAndNil(persistXML); + end; + end; + + cbCheckUpdates.Checked := Settings.CheckUpdates; + + if not Settings.HasCheckUpdates then + PostMessage(Self.Handle, CM_ASKAUTOUPDATE, 0, 0) + else if Settings.CheckUpdates then + CheckForUpdates(False); +end; + + +procedure TMainForm.SaveSettings; +var + persistXML: TX2UtPersistXML; + +begin + persistXML := TX2UtPersistXML.Create; + try + persistXML.FileName := FSettingsFileName; + Settings.Save(persistXML.CreateWriter); + finally + FreeAndNil(persistXML); + end; +end; + + function TMainForm.CreateDefaultProfile: TProfile; begin { Default button functions are assigned during UpdateButton } @@ -468,8 +516,12 @@ begin 'Do you want to automatically check for updates?', 'Check for updates', MB_YESNO or MB_ICONQUESTION) = ID_YES then begin cbCheckUpdates.Checked := True; + Settings.CheckUpdates := True; + CheckForUpdates(False); end; + + SaveSettings; end; @@ -485,43 +537,6 @@ begin end; -//procedure TMainForm.ReadAutoUpdate(AReader: IX2PersistReader); -//var -// checkUpdates: Boolean; -// askAutoUpdate: Boolean; -// -//begin -// askAutoUpdate := True; -// -// if AReader.BeginSection(SECTION_SETTINGS) then -// try -// if AReader.ReadBoolean('CheckUpdates', checkUpdates) then -// begin -// cbCheckUpdates.Checked := checkUpdates; -// askAutoUpdate := False; -// end; -// finally -// AReader.EndSection; -// end; -// -// if askAutoUpdate then -// PostMessage(Self.Handle, CM_ASKAUTOUPDATE, 0, 0) -// else if cbCheckUpdates.Checked then -// CheckForUpdates(False); -//end; - - -//procedure TMainForm.WriteAutoUpdate(AWriter: IX2PersistWriter); -//begin -// if AWriter.BeginSection(SECTION_SETTINGS) then -// try -// AWriter.WriteBoolean('CheckUpdates', cbCheckUpdates.Checked); -// finally -// AWriter.EndSection; -// end; -//end; - - procedure TMainForm.LEDButtonClick(Sender: TObject); var buttonIndex: NativeInt; @@ -607,7 +622,7 @@ begin try latestVersion := httpClient.Get('http://g940.x2software.net/version'); if VersionIsNewer(Format('%d.%d.%d', [App.Version.Major, App.Version.Minor, App.Version.Release]), latestVersion) then - ATask.Comm.Send(MSG_UPDATE) + ATask.Comm.Send(MSG_UPDATE, latestVersion) else begin if ATask.Param.ByName('ReportNoUpdates').AsBoolean then @@ -629,6 +644,13 @@ begin end; +procedure TMainForm.cbCheckUpdatesClick(Sender: TObject); +begin + Settings.CheckUpdates := cbCheckUpdates.Checked; + SaveSettings; +end; + + procedure TMainForm.CheckForUpdates(AReportNoUpdates: Boolean); begin btnCheckUpdates.Enabled := False; @@ -647,9 +669,10 @@ begin HandleDeviceStateMessage(task, msg); MSG_UPDATE: - if MessageBox(Self.Handle, 'An update is available on the G940 LED Control website.'#13#10'Do you want to go there now?', - 'Update available', MB_YESNO or MB_ICONINFORMATION) = ID_YES then - ShellExecute(Self.Handle, 'open', PChar('http://g940.x2software.net/#download'), nil, nil, SW_SHOWNORMAL); + if MessageBox(Self.Handle, PChar('Version ' + msg.MsgData + ' is available on the G940 LED Control website.'#13#10 + + 'Do you want to open the website now?'), 'Update available', + MB_YESNO or MB_ICONINFORMATION) = ID_YES then + ShellExecute(Self.Handle, 'open', PChar('http://g940.x2software.net/category/releases/'), nil, nil, SW_SHOWNORMAL); MSG_NOUPDATE: if msg.MsgData.AsBoolean then diff --git a/G940LEDControl/G940LEDControl.dpr b/G940LEDControl/G940LEDControl.dpr index 99c2a5c..382ee4e 100644 --- a/G940LEDControl/G940LEDControl.dpr +++ b/G940LEDControl/G940LEDControl.dpr @@ -27,7 +27,9 @@ uses FSXSimConnectClient in 'Units\FSXSimConnectClient.pas', FSXSimConnectIntf in 'Units\FSXSimConnectIntf.pas', FSXLEDFunction in 'Units\FSXLEDFunction.pas', - LEDResources in 'Units\LEDResources.pas'; + LEDResources in 'Units\LEDResources.pas', + Settings in 'Units\Settings.pas', + FSXLEDFunctionWorker in 'Units\FSXLEDFunctionWorker.pas'; {$R *.res} diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj index d357a23..25ff0fd 100644 --- a/G940LEDControl/G940LEDControl.dproj +++ b/G940LEDControl/G940LEDControl.dproj @@ -190,6 +190,8 @@ + + Cfg_2 Base diff --git a/G940LEDControl/Units/ConfigConversion.pas b/G940LEDControl/Units/ConfigConversion.pas index 90b04ae..a7e6790 100644 --- a/G940LEDControl/Units/ConfigConversion.pas +++ b/G940LEDControl/Units/ConfigConversion.pas @@ -2,10 +2,12 @@ unit ConfigConversion; interface uses - Profile; + Profile, + Settings; { Version 0.x: registry -> 1.x: XML } function ConvertProfile0To1: TProfile; + function ConvertSettings0To1: TSettings; implementation @@ -16,6 +18,7 @@ uses X2UtPersistIntf, X2UtPersistRegistry, + FSXResources, LEDColorIntf, StaticResources; @@ -46,7 +49,7 @@ const V0_FUNCTIONFSX_SPOILERS = V0_FUNCTIONPROVIDER_OFFSET + 13; V0_FUNCTIONFSX_PRESSURIZATIONDUMPSWITCH = V0_FUNCTIONPROVIDER_OFFSET + 14; - V0_FUNCTIONFSX_CARBHEAT = V0_FUNCTIONPROVIDER_OFFSET + 15; + V0_FUNCTIONFSX_ENGINEANTIICE = V0_FUNCTIONPROVIDER_OFFSET + 15; V0_FUNCTIONFSX_AUTOPILOT = V0_FUNCTIONPROVIDER_OFFSET + 16; V0_FUNCTIONFSX_FUELPUMP = V0_FUNCTIONPROVIDER_OFFSET + 17; @@ -62,7 +65,7 @@ const V0_FUNCTIONFSX_TAXILIGHTS = V0_FUNCTIONPROVIDER_OFFSET + 25; V0_FUNCTIONFSX_RECOGNITIONLIGHTS = V0_FUNCTIONPROVIDER_OFFSET + 26; - // TODO 27 (de-ice) + V0_FUNCTIONFSX_DEICE = V0_FUNCTIONPROVIDER_OFFSET + 27; @@ -76,45 +79,50 @@ procedure ConvertProfileFunction0To1(AOldFunction: Integer; AButton: TProfileBut begin + { Default states are handled by the specific functions } case AOldFunction of { Static } - V0_FUNCTION_OFF: SetButton(StaticProviderUID, StaticFunctionUID[lcOff]); - V0_FUNCTION_RED: SetButton(StaticProviderUID, StaticFunctionUID[lcRed]); - V0_FUNCTION_AMBER: SetButton(StaticProviderUID, StaticFunctionUID[lcAmber]); - V0_FUNCTION_GREEN: SetButton(StaticProviderUID, StaticFunctionUID[lcGreen]); + V0_FUNCTION_OFF: SetButton(StaticProviderUID, StaticFunctionUID[lcOff]); + V0_FUNCTION_RED: SetButton(StaticProviderUID, StaticFunctionUID[lcRed]); + V0_FUNCTION_AMBER: SetButton(StaticProviderUID, StaticFunctionUID[lcAmber]); + V0_FUNCTION_GREEN: SetButton(StaticProviderUID, StaticFunctionUID[lcGreen]); { FSX } - { - V0_FUNCTIONFSX_GEAR: - V0_FUNCTIONFSX_LANDINGLIGHTS: - V0_FUNCTIONFSX_INSTRUMENTLIGHTS: - V0_FUNCTIONFSX_PARKINGBRAKE: - V0_FUNCTIONFSX_ENGINE: + V0_FUNCTIONFSX_GEAR: SetButton(FSXProviderUID, FSXFunctionUIDGear); + V0_FUNCTIONFSX_LANDINGLIGHTS: SetButton(FSXProviderUID, FSXFunctionUIDLandingLights); + V0_FUNCTIONFSX_INSTRUMENTLIGHTS: SetButton(FSXProviderUID, FSXFunctionUIDInstrumentLights); + V0_FUNCTIONFSX_PARKINGBRAKE: SetButton(FSXProviderUID, FSXFunctionUIDParkingBrake); + V0_FUNCTIONFSX_ENGINE: SetButton(FSXProviderUID, FSXFunctionUIDEngine); - V0_FUNCTIONFSX_EXITDOOR: - V0_FUNCTIONFSX_STROBELIGHTS: - V0_FUNCTIONFSX_NAVLIGHTS: - V0_FUNCTIONFSX_BEACONLIGHTS: - V0_FUNCTIONFSX_FLAPS: - V0_FUNCTIONFSX_BATTERYMASTER: - V0_FUNCTIONFSX_AVIONICSMASTER: - V0_FUNCTIONFSX_SPOILERS: - V0_FUNCTIONFSX_PRESSURIZATIONDUMPSWITCH: - V0_FUNCTIONFSX_CARBHEAT: + V0_FUNCTIONFSX_EXITDOOR: SetButton(FSXProviderUID, FSXFunctionUIDExitDoor); + V0_FUNCTIONFSX_STROBELIGHTS: SetButton(FSXProviderUID, FSXFunctionUIDStrobeLights); + V0_FUNCTIONFSX_NAVLIGHTS: SetButton(FSXProviderUID, FSXFunctionUIDNavLights); + V0_FUNCTIONFSX_BEACONLIGHTS: SetButton(FSXProviderUID, FSXFunctionUIDBeaconLights); + V0_FUNCTIONFSX_FLAPS: SetButton(FSXProviderUID, FSXFunctionUIDFlaps); + V0_FUNCTIONFSX_BATTERYMASTER: SetButton(FSXProviderUID, FSXFunctionUIDBatteryMaster); + V0_FUNCTIONFSX_AVIONICSMASTER: SetButton(FSXProviderUID, FSXFunctionUIDAvionicsMaster); + V0_FUNCTIONFSX_SPOILERS: SetButton(FSXProviderUID, FSXFunctionUIDSpoilers); + V0_FUNCTIONFSX_PRESSURIZATIONDUMPSWITCH: SetButton(FSXProviderUID, FSXFunctionUIDPressDumpSwitch); + V0_FUNCTIONFSX_ENGINEANTIICE: SetButton(FSXProviderUID, FSXFunctionUIDEngineAntiIce); V0_FUNCTIONFSX_AUTOPILOT: - V0_FUNCTIONFSX_FUELPUMP: - V0_FUNCTIONFSX_TAILHOOK: - V0_FUNCTIONFSX_AUTOPILOT_AMBER: - V0_FUNCTIONFSX_AUTOPILOT_HEADING: - V0_FUNCTIONFSX_AUTOPILOT_APPROACH: - V0_FUNCTIONFSX_AUTOPILOT_BACKCOURSE: - V0_FUNCTIONFSX_AUTOPILOT_ALTITUDE: - V0_FUNCTIONFSX_AUTOPILOT_NAV: - V0_FUNCTIONFSX_TAXILIGHTS: - V0_FUNCTIONFSX_RECOGNITIONLIGHTS: - } - else - SetButton(StaticProviderUID, StaticFunctionUID[lcGreen]); + begin + { The only exception regarding states; the new default is Amber / Off } + SetButton(FSXProviderUID, FSXFunctionUIDAutoPilot); + AButton.SetStateColor(FSXStateUIDOn, lcGreen); + AButton.SetStateColor(FSXStateUIDOff, lcRed); + end; + + V0_FUNCTIONFSX_FUELPUMP: SetButton(FSXProviderUID, FSXFunctionUIDFuelPump); + V0_FUNCTIONFSX_TAILHOOK: SetButton(FSXProviderUID, FSXFunctionUIDTailHook); + V0_FUNCTIONFSX_AUTOPILOT_AMBER: SetButton(FSXProviderUID, FSXFunctionUIDAutoPilot); + V0_FUNCTIONFSX_AUTOPILOT_HEADING: SetButton(FSXProviderUID, FSXFunctionUIDAutoPilotHeading); + V0_FUNCTIONFSX_AUTOPILOT_APPROACH: SetButton(FSXProviderUID, FSXFunctionUIDAutoPilotApproach); + V0_FUNCTIONFSX_AUTOPILOT_BACKCOURSE: SetButton(FSXProviderUID, FSXFunctionUIDAutoPilotBackcourse); + V0_FUNCTIONFSX_AUTOPILOT_ALTITUDE: SetButton(FSXProviderUID, FSXFunctionUIDAutoPilotAltitude); + V0_FUNCTIONFSX_AUTOPILOT_NAV: SetButton(FSXProviderUID, FSXFunctionUIDAutoPilotNav); + V0_FUNCTIONFSX_TAXILIGHTS: SetButton(FSXProviderUID, FSXFunctionUIDTaxiLights); + V0_FUNCTIONFSX_RECOGNITIONLIGHTS: SetButton(FSXProviderUID, FSXFunctionUIDRecognitionLights); + V0_FUNCTIONFSX_DEICE: SetButton(FSXProviderUID, FSXFunctionUIDDeIce); end; end; @@ -124,7 +132,6 @@ const KEY_SETTINGS = '\Software\X2Software\G940LEDControl\'; SECTION_DEFAULTPROFILE = 'DefaultProfile'; SECTION_FSX = 'FSX'; - SECTION_SETTINGS = 'Settings'; var registryReader: TX2UtPersistRegistry; @@ -162,9 +169,42 @@ begin finally reader.EndSection; end; + finally + FreeAndNil(registryReader); + end; +end; - // TODO auto update settings - //ReadAutoUpdate(reader); + +function ConvertSettings0To1: TSettings; +const + KEY_SETTINGS = '\Software\X2Software\G940LEDControl\'; + SECTION_SETTINGS = 'Settings'; + +var + registryReader: TX2UtPersistRegistry; + reader: IX2PersistReader; + value: Boolean; + +begin + Result := nil; + + registryReader := TX2UtPersistRegistry.Create; + try + registryReader.RootKey := HKEY_CURRENT_USER; + registryReader.Key := KEY_SETTINGS; + + reader := registryReader.CreateReader; + + if reader.BeginSection(SECTION_SETTINGS) then + try + if reader.ReadBoolean('CheckUpdates', value) then + begin + Result := TSettings.Create; + Result.CheckUpdates := value; + end; + finally + reader.EndSection; + end; finally FreeAndNil(registryReader); end; diff --git a/G940LEDControl/Units/FSXLEDFunction.pas b/G940LEDControl/Units/FSXLEDFunction.pas index f829279..acf7aa6 100644 --- a/G940LEDControl/Units/FSXLEDFunction.pas +++ b/G940LEDControl/Units/FSXLEDFunction.pas @@ -8,14 +8,13 @@ uses type - { Base classes } - TFSXOnOffFunction = class(TCustomFSXFunction) + TCustomFSXOnOffFunction = class(TCustomFSXFunction) protected procedure RegisterStates; override; end; - { Function implementations } + { Misc } TFSXEngineFunction = class(TCustomFSXFunction) protected procedure RegisterStates; override; @@ -28,8 +27,74 @@ type function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; end; + TFSXParkingBrakeFunction = class(TCustomFSXOnOffFunction) + protected + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; - TFSXLightFunction = class(TFSXOnOffFunction) + TFSXExitDoorFunction = class(TCustomFSXFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + TFSXTailHookFunction = class(TCustomFSXFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + TFSXFlapsFunction = class(TCustomFSXFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + TFSXSpoilersFunction = class(TCustomFSXFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + TFSXBatteryMasterFunction = class(TCustomFSXFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + TFSXAvionicsMasterFunction = class(TCustomFSXFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + TFSXPressDumpSwitchFunction = class(TCustomFSXFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + TFSXEngineAntiIceFunction = class(TCustomFSXFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + TFSXFuelPumpFunction = class(TCustomFSXFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + TFSXDeIceFunction = class(TCustomFSXFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + + { Lights } + TCustomFSXLightFunction = class(TCustomFSXOnOffFunction) protected function GetCategoryName: string; override; @@ -39,82 +104,96 @@ type function GetLightMask: Integer; virtual; abstract; end; - TFSXLandingLightsFunction = class(TFSXLightFunction) + TFSXLandingLightsFunction = class(TCustomFSXLightFunction) protected function GetLightMask: Integer; override; end; - TFSXInstrumentLightsFunction = class(TFSXLightFunction) + TFSXInstrumentLightsFunction = class(TCustomFSXLightFunction) protected function GetLightMask: Integer; override; end; - TFSXBeaconLightsFunction = class(TFSXLightFunction) + TFSXBeaconLightsFunction = class(TCustomFSXLightFunction) protected function GetLightMask: Integer; override; end; - TFSXNavLightsFunction = class(TFSXLightFunction) + TFSXNavLightsFunction = class(TCustomFSXLightFunction) protected function GetLightMask: Integer; override; end; - TFSXStrobeLightsFunction = class(TFSXLightFunction) + TFSXStrobeLightsFunction = class(TCustomFSXLightFunction) protected function GetLightMask: Integer; override; end; - TFSXTaxiLightsFunction = class(TFSXLightFunction) + TFSXTaxiLightsFunction = class(TCustomFSXLightFunction) protected function GetLightMask: Integer; override; end; - TFSXRecognitionLightsFunction = class(TFSXLightFunction) + TFSXRecognitionLightsFunction = class(TCustomFSXLightFunction) protected function GetLightMask: Integer; override; end; + { Autopilot } + TCustomFSXAutoPilotFunction = class(TCustomFSXFunction) + protected + function GetCategoryName: string; override; + end; + + TFSXAutoPilotFunction = class(TCustomFSXAutoPilotFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + TFSXAutoPilotHeadingFunction = class(TCustomFSXAutoPilotFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + TFSXAutoPilotApproachFunction = class(TCustomFSXAutoPilotFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + TFSXAutoPilotBackcourseFunction = class(TCustomFSXAutoPilotFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + TFSXAutoPilotAltitudeFunction = class(TCustomFSXAutoPilotFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + TFSXAutoPilotNavFunction = class(TCustomFSXAutoPilotFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + end; + + implementation uses - System.Math, - System.SysUtils, - - FSXSimConnectIntf, + FSXLEDFunctionWorker, FSXResources, + FSXSimConnectIntf, LEDColorIntf, - LEDState, - LEDStateIntf, - SimConnect; - - -type - { Worker implementations } - TFSXEngineFunctionWorker = class(TCustomFSXFunctionWorker) - protected - procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; - procedure HandleData(AData: Pointer); override; - end; - - TFSXGearFunctionWorker = class(TCustomFSXFunctionWorker) - protected - procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; - procedure HandleData(AData: Pointer); override; - end; - - TFSXLightStatesFunctionWorker = class(TCustomFSXFunctionWorker) - private - FStateMask: Integer; - protected - procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; - procedure HandleData(AData: Pointer); override; - public - property StateMask: Integer read FStateMask write FStateMask; - end; + LEDState; { TFSXOnOffFunction } -procedure TFSXOnOffFunction.RegisterStates; +procedure TCustomFSXOnOffFunction.RegisterStates; begin RegisterState(TLEDState.Create(FSXStateUIDOn, FSXStateDisplayNameOn, lcGreen)); RegisterState(TLEDState.Create(FSXStateUIDOff, FSXStateDisplayNameOff, lcRed)); @@ -139,84 +218,6 @@ begin end; -{ TFSXEngineFunctionWorker } -procedure TFSXEngineFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); -var - engineIndex: Integer; - -begin - ADefinition.AddVariable('NUMBER OF ENGINES', FSX_UNIT_NUMBER, SIMCONNECT_DATAType_INT32); - - for engineIndex := 1 to FSX_MAX_ENGINES do - ADefinition.AddVariable(Format('GENERAL ENG COMBUSTION:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); - - for engineIndex := 1 to FSX_MAX_ENGINES do - ADefinition.AddVariable(Format('ENG FAILED:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); - - for engineIndex := 1 to FSX_MAX_ENGINES do - ADefinition.AddVariable(Format('ENG ON FIRE:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); -end; - - -procedure TFSXEngineFunctionWorker.HandleData(AData: Pointer); -type - PEngineData = ^TEngineData; - TEngineData = packed record - NumberOfEngines: Integer; - Combustion: array[1..FSX_MAX_ENGINES] of Integer; - Failed: array[1..FSX_MAX_ENGINES] of Integer; - OnFire: array[1..FSX_MAX_ENGINES] of Integer; - end; - -var - engineData: PEngineData; - engineCount: Integer; - engineIndex: Integer; - hasFire: Boolean; - hasFailure: Boolean; - runningCount: Integer; - -begin - engineData := AData; - - if engineData^.NumberOfEngines > 0 then - begin - engineCount := Min(engineData^.NumberOfEngines, FSX_MAX_ENGINES); - hasFire := False; - hasFailure := False; - runningCount := 0; - - for engineIndex := 1 to engineCount do - begin - if engineData^.OnFire[engineIndex] <> 0 then - hasFire := True; - - if engineData^.Failed[engineIndex] <> 0 then - hasFailure := True; - - if engineData^.Combustion[engineIndex] <> 0 then - Inc(runningCount); - end; - - if hasFire then - SetCurrentState(FSXStateUIDEngineOnFire) - - else if hasFailure then - SetCurrentState(FSXStateUIDEngineFailed) - - else if runningCount = 0 then - SetCurrentState(FSXStateUIDEngineAllOff) - - else if runningCount = engineCount then - SetCurrentState(FSXStateUIDEngineAllRunning) - - else - SetCurrentState(FSXStateUIDEnginePartiallyRunning); - end else - SetCurrentState(FSXStateUIDEngineNoEngines); -end; - - { TFSXGearFunction } procedure TFSXGearFunction.RegisterStates; begin @@ -235,64 +236,161 @@ begin end; -{ TFSXGearFunctionWorker } -procedure TFSXGearFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +{ TFSXParkingBrakeFunction } +function TFSXParkingBrakeFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; begin - ADefinition.AddVariable('IS GEAR RETRACTABLE', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); - ADefinition.AddVariable('GEAR TOTAL PCT EXTENDED', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); - ADefinition.AddVariable('GEAR DAMAGE BY SPEED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); - ADefinition.AddVariable('GEAR SPEED EXCEEDED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + Result := TFSXParkingBrakeFunctionWorker; end; -procedure TFSXGearFunctionWorker.HandleData(AData: Pointer); -type - PGearData = ^TGearData; - TGearData = packed record - IsGearRetractable: Cardinal; - TotalPctExtended: Double; - DamageBySpeed: Integer; - SpeedExceeded: Integer; - end; - -var - gearData: PGearData; - +{ TFSXExitDoorFunction } +procedure TFSXExitDoorFunction.RegisterStates; begin - gearData := AData; + RegisterState(TLEDState.Create(FSXStateUIDExitDoorClosed, FSXStateDisplayNameExitDoorClosed, lcGreen)); + RegisterState(TLEDState.Create(FSXStateUIDExitDoorBetween, FSXStateDisplayNameExitDoorBetween, lcAmber)); + RegisterState(TLEDState.Create(FSXStateUIDExitDoorOpen, FSXStateDisplayNameExitDoorOpen, lcRed)); +end; - if gearData^.DamageBySpeed <> 0 then - SetCurrentState(FSXStateUIDGearDamageBySpeed) - else if gearData^.SpeedExceeded <> 0 then - SetCurrentState(FSXStateUIDGearSpeedExceeded) +function TFSXExitDoorFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXExitDoorFunctionWorker; +end; - else if gearData^.IsGearRetractable <> 0 then - begin - case Trunc(gearData ^.TotalPctExtended * 100) of - 0: SetCurrentState(FSXStateUIDGearRetracted); - 95..100: SetCurrentState(FSXStateUIDGearExtended); - else SetCurrentState(FSXStateUIDGearBetween); - end; - end else - SetCurrentState(FSXStateUIDGearNotRetractable); + +{ TFSXTailHookFunction } +procedure TFSXTailHookFunction.RegisterStates; +begin + RegisterState(TLEDState.Create(FSXStateUIDTailHookRetracted, FSXStateDisplayNameTailHookRetracted, lcGreen)); + RegisterState(TLEDState.Create(FSXStateUIDTailHookBetween, FSXStateDisplayNameTailHookBetween, lcAmber)); + RegisterState(TLEDState.Create(FSXStateUIDTailHookExtended, FSXStateDisplayNameTailHookExtended, lcRed)); +end; + + +function TFSXTailHookFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXTailHookFunctionWorker; +end; + + +{ TFSXFlapsFunction } +procedure TFSXFlapsFunction.RegisterStates; +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXFlapsFunction.RegisterStates +end; + + +function TFSXFlapsFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXFlapsFunctionWorker; +end; + + +{ TFSXSpoilersFunction } +procedure TFSXSpoilersFunction.RegisterStates; +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXSpoilersFunction.RegisterStates +end; + + +function TFSXSpoilersFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXSpoilersFunctionWorker; +end; + + +{ TFSXBatteryMasterFunction } +procedure TFSXBatteryMasterFunction.RegisterStates; +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXBatteryMasterFunction.RegisterStates +end; + + +function TFSXBatteryMasterFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXBatteryMasterFunctionWorker; +end; + + +{ TFSXAvionicsMasterFunction } +procedure TFSXAvionicsMasterFunction.RegisterStates; +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAvionicsMasterFunction.RegisterStates +end; + + +function TFSXAvionicsMasterFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXAvionicsMasterFunctionWorker; +end; + + +{ TFSXPressDumpSwitchFunction } +procedure TFSXPressDumpSwitchFunction.RegisterStates; +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXPressDumpSwitchFunction.RegisterStates +end; + + +function TFSXPressDumpSwitchFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXPressDumpSwitchFunctionWorker; +end; + + +{ TFSXEngineAntiIceFunction } +procedure TFSXEngineAntiIceFunction.RegisterStates; +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXEngineAntiIceFunction.RegisterStates +end; + + +function TFSXEngineAntiIceFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXEngineAntiIceFunctionWorker; +end; + + +{ TFSXFuelPumpFunction } +procedure TFSXFuelPumpFunction.RegisterStates; +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXFuelPumpFunction.RegisterStates +end; + + +function TFSXFuelPumpFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXFuelPumpFunctionWorker; +end; + + +{ TFSXDeIceFunction } +procedure TFSXDeIceFunction.RegisterStates; +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXDeIceFunction.RegisterStates +end; + + +function TFSXDeIceFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXDeIceFunctionWorker; end; { TFSXLightFunction } -function TFSXLightFunction.GetCategoryName: string; +function TCustomFSXLightFunction.GetCategoryName: string; begin Result := FSXCategoryLights; end; -function TFSXLightFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +function TCustomFSXLightFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; begin Result := TFSXLightStatesFunctionWorker; end; -function TFSXLightFunction.DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; +function TCustomFSXLightFunction.DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; begin Result := inherited DoCreateWorker(ASettings); (Result as TFSXLightStatesFunctionWorker).StateMask := GetLightMask; @@ -348,19 +446,88 @@ begin end; -{ TFSXLightStatesFunctionWorker } -procedure TFSXLightStatesFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +{ TCustomFSXAutoPilotFunction } +function TCustomFSXAutoPilotFunction.GetCategoryName: string; begin - ADefinition.AddVariable('LIGHT ON STATES', FSX_UNIT_MASK, SIMCONNECT_DATATYPE_INT32); + Result := FSXCategoryAutoPilot; end; -procedure TFSXLightStatesFunctionWorker.HandleData(AData: Pointer); +{ TFSXAutoPilotFunction } +procedure TFSXAutoPilotFunction.RegisterStates; begin - if (PCardinal(AData)^ and StateMask) <> 0 then - SetCurrentState(FSXStateUIDOn) - else - SetCurrentState(FSXStateUIDOff); + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotFunction.RegisterStates +end; + + +function TFSXAutoPilotFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXAutoPilotFunctionWorker; +end; + + +{ TFSXAutoPilotHeadingFunction } +procedure TFSXAutoPilotHeadingFunction.RegisterStates; +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotHeadingFunction.RegisterState +end; + + +function TFSXAutoPilotHeadingFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXAutoPilotHeadingFunctionWorker; +end; + + +{ TFSXAutoPilotApproachFunction } +procedure TFSXAutoPilotApproachFunction.RegisterStates; +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotApproachFunction.RegisterStates +end; + + +function TFSXAutoPilotApproachFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXAutoPilotApproachFunctionWorker; +end; + + +{ TFSXAutoPilotBackcourseFunction } +procedure TFSXAutoPilotBackcourseFunction.RegisterStates; +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotBackcourseFunction.RegisterStates +end; + + +function TFSXAutoPilotBackcourseFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXAutoPilotBackcourseFunctionWorker; +end; + + +{ TFSXAutoPilotAltitudeFunction } +procedure TFSXAutoPilotAltitudeFunction.RegisterStates; +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotAltitudeFunction.RegisterStates +end; + + +function TFSXAutoPilotAltitudeFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXAutoPilotAltitudeFunctionWorker; +end; + + +{ TFSXAutoPilotNavFunction } +procedure TFSXAutoPilotNavFunction.RegisterStates; +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotNavFunction.RegisterStates +end; + + +function TFSXAutoPilotNavFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +begin + Result := TFSXAutoPilotNavFunctionWorker; end; end. diff --git a/G940LEDControl/Units/FSXLEDFunctionProvider.pas b/G940LEDControl/Units/FSXLEDFunctionProvider.pas index 2a7d61b..e02864f 100644 --- a/G940LEDControl/Units/FSXLEDFunctionProvider.pas +++ b/G940LEDControl/Units/FSXLEDFunctionProvider.pas @@ -129,38 +129,37 @@ end; procedure TFSXLEDFunctionProvider.RegisterFunctions; begin - { - AConsumer.AddFunction(FUNCTION_FSX_CARBHEAT, 'Anti-ice'); - AConsumer.AddFunction(FUNCTION_FSX_AUTOPILOT, 'Auto pilot (main)'); - AConsumer.AddFunction(FUNCTION_FSX_AUTOPILOT_AMBER, 'Auto pilot (main - off / amber)'); - AConsumer.AddFunction(FUNCTION_FSX_AUTOPILOT_ALTITUDE, 'Auto pilot - Altitude'); - AConsumer.AddFunction(FUNCTION_FSX_AUTOPILOT_APPROACH, 'Auto pilot - Approach'); - AConsumer.AddFunction(FUNCTION_FSX_AUTOPILOT_BACKCOURSE, 'Auto pilot - Backcourse'); - AConsumer.AddFunction(FUNCTION_FSX_AUTOPILOT_HEADING, 'Auto pilot - Heading'); - AConsumer.AddFunction(FUNCTION_FSX_AUTOPILOT_NAV, 'Auto pilot - Nav'); - AConsumer.AddFunction(FUNCTION_FSX_AVIONICSMASTER, 'Avionics master switch'); - AConsumer.AddFunction(FUNCTION_FSX_BATTERYMASTER, 'Battery master switch'); - AConsumer.AddFunction(FUNCTION_FSX_ENGINE, 'Engine'); - AConsumer.AddFunction(FUNCTION_FSX_EXITDOOR, 'Exit door'); - AConsumer.AddFunction(FUNCTION_FSX_FLAPS, 'Flaps'); - AConsumer.AddFunction(FUNCTION_FSX_PARKINGBRAKE, 'Parking brake'); - AConsumer.AddFunction(FUNCTION_FSX_PRESSURIZATIONDUMPSWITCH, 'Pressurization dump switch'); - AConsumer.AddFunction(FUNCTION_FSX_SPOILERS, 'Spoilers (air brake)'); - AConsumer.AddFunction(FUNCTION_FSX_TAILHOOK, 'Tail hook'); - } - { Misc } - RegisterFunction(TFSXEngineFunction.Create( Self, FSXFunctionDisplayNameEngine, FSXFunctionUIDEngine)); - RegisterFunction(TFSXGearFunction.Create( Self, FSXFunctionDisplayNameGear, FSXFunctionUIDGear)); + RegisterFunction(TFSXAvionicsMasterFunction.Create( Self, FSXFunctionDisplayNameAvionicsMaster, FSXFunctionUIDAvionicsMaster)); + RegisterFunction(TFSXBatteryMasterFunction.Create( Self, FSXFunctionDisplayNameBatteryMaster, FSXFunctionUIDBatteryMaster)); + RegisterFunction(TFSXDeIceFunction.Create( Self, FSXFunctionDisplayNameDeIce, FSXFunctionUIDDeIce)); + RegisterFunction(TFSXEngineAntiIceFunction.Create( Self, FSXFunctionDisplayNameEngineAntiIce, FSXFunctionUIDEngineAntiIce)); + RegisterFunction(TFSXEngineFunction.Create( Self, FSXFunctionDisplayNameEngine, FSXFunctionUIDEngine)); + RegisterFunction(TFSXExitDoorFunction.Create( Self, FSXFunctionDisplayNameExitDoor, FSXFunctionUIDExitDoor)); + RegisterFunction(TFSXFlapsFunction.Create( Self, FSXFunctionDisplayNameFlaps, FSXFunctionUIDFlaps)); + RegisterFunction(TFSXFuelPumpFunction.Create( Self, FSXFunctionDisplayNameFuelPump, FSXFunctionUIDFuelPump)); + RegisterFunction(TFSXGearFunction.Create( Self, FSXFunctionDisplayNameGear, FSXFunctionUIDGear)); + RegisterFunction(TFSXParkingBrakeFunction.Create( Self, FSXFunctionDisplayNameParkingBrake, FSXFunctionUIDParkingBrake)); + RegisterFunction(TFSXPressDumpSwitchFunction.Create( Self, FSXFunctionDisplayNamePressDumpSwitch, FSXFunctionUIDPressDumpSwitch)); + RegisterFunction(TFSXSpoilersFunction.Create( Self, FSXFunctionDisplayNameSpoilers, FSXFunctionUIDSpoilers)); + RegisterFunction(TFSXTailHookFunction.Create( Self, FSXFunctionDisplayNameTailHook, FSXFunctionUIDTailHook)); { Lights } - RegisterFunction(TFSXBeaconLightsFunction.Create( Self, FSXFunctionDisplayNameBeaconLights, FSXFunctionUIDBeaconLights)); - RegisterFunction(TFSXInstrumentLightsFunction.Create( Self, FSXFunctionDisplayNameInstrumentLights, FSXFunctionUIDInstrumentLights)); - RegisterFunction(TFSXLandingLightsFunction.Create( Self, FSXFunctionDisplayNameLandingLights, FSXFunctionUIDLandingLights)); - RegisterFunction(TFSXNavLightsFunction.Create( Self, FSXFunctionDisplayNameNavLights, FSXFunctionUIDNavLights)); - RegisterFunction(TFSXRecognitionLightsFunction.Create(Self, FSXFunctionDisplayNameRecognitionLights, FSXFunctionUIDRecognitionLights)); - RegisterFunction(TFSXStrobeLightsFunction.Create( Self, FSXFunctionDisplayNameStrobeLights, FSXFunctionUIDStrobeLights)); - RegisterFunction(TFSXTaxiLightsFunction.Create( Self, FSXFunctionDisplayNameTaxiLights, FSXFunctionUIDTaxiLights)); + RegisterFunction(TFSXBeaconLightsFunction.Create( Self, FSXFunctionDisplayNameBeaconLights, FSXFunctionUIDBeaconLights)); + RegisterFunction(TFSXInstrumentLightsFunction.Create( Self, FSXFunctionDisplayNameInstrumentLights, FSXFunctionUIDInstrumentLights)); + RegisterFunction(TFSXLandingLightsFunction.Create( Self, FSXFunctionDisplayNameLandingLights, FSXFunctionUIDLandingLights)); + RegisterFunction(TFSXNavLightsFunction.Create( Self, FSXFunctionDisplayNameNavLights, FSXFunctionUIDNavLights)); + RegisterFunction(TFSXRecognitionLightsFunction.Create( Self, FSXFunctionDisplayNameRecognitionLights, FSXFunctionUIDRecognitionLights)); + RegisterFunction(TFSXStrobeLightsFunction.Create( Self, FSXFunctionDisplayNameStrobeLights, FSXFunctionUIDStrobeLights)); + RegisterFunction(TFSXTaxiLightsFunction.Create( Self, FSXFunctionDisplayNameTaxiLights, FSXFunctionUIDTaxiLights)); + + { Autopilot } + RegisterFunction(TFSXAutoPilotFunction.Create( Self, FSXFunctionDisplayNameAutoPilot, FSXFunctionUIDAutoPilot)); + RegisterFunction(TFSXAutoPilotAltitudeFunction.Create( Self, FSXFunctionDisplayNameAutoPilotAltitude, FSXFunctionUIDAutoPilotAltitude)); + RegisterFunction(TFSXAutoPilotApproachFunction.Create( Self, FSXFunctionDisplayNameAutoPilotApproach, FSXFunctionUIDAutoPilotApproach)); + RegisterFunction(TFSXAutoPilotBackcourseFunction.Create(Self, FSXFunctionDisplayNameAutoPilotBackcourse, FSXFunctionUIDAutoPilotBackcourse)); + RegisterFunction(TFSXAutoPilotHeadingFunction.Create( Self, FSXFunctionDisplayNameAutoPilotHeading, FSXFunctionUIDAutoPilotHeading)); + RegisterFunction(TFSXAutoPilotNavFunction.Create( Self, FSXFunctionDisplayNameAutoPilotNav, FSXFunctionUIDAutoPilotNav)); end; diff --git a/G940LEDControl/Units/FSXLEDFunctionWorker.pas b/G940LEDControl/Units/FSXLEDFunctionWorker.pas new file mode 100644 index 0000000..7d97d89 --- /dev/null +++ b/G940LEDControl/Units/FSXLEDFunctionWorker.pas @@ -0,0 +1,526 @@ +unit FSXLEDFunctionWorker; + +interface +uses + FSXLEDFunctionProvider, + FSXSimConnectIntf; + + +type + { Misc } + TFSXEngineFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + TFSXGearFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + TFSXParkingBrakeFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + TFSXExitDoorFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + TFSXTailHookFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + + TFSXFlapsFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + + TFSXSpoilersFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + + TFSXBatteryMasterFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + + TFSXAvionicsMasterFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + + TFSXPressDumpSwitchFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + + TFSXEngineAntiIceFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + + TFSXFuelPumpFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + + TFSXDeIceFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + + TFSXLightStatesFunctionWorker = class(TCustomFSXFunctionWorker) + private + FStateMask: Integer; + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + public + property StateMask: Integer read FStateMask write FStateMask; + end; + + + TFSXAutoPilotFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + + TFSXAutoPilotHeadingFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + + TFSXAutoPilotApproachFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + + TFSXAutoPilotBackcourseFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + + TFSXAutoPilotAltitudeFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + + TFSXAutoPilotNavFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + +implementation +uses + System.Math, + System.SysUtils, + + FSXResources, + LEDStateIntf, + SimConnect; + + +{ TFSXEngineFunctionWorker } +procedure TFSXEngineFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +var + engineIndex: Integer; + +begin + ADefinition.AddVariable('NUMBER OF ENGINES', FSX_UNIT_NUMBER, SIMCONNECT_DATAType_INT32); + + for engineIndex := 1 to FSX_MAX_ENGINES do + ADefinition.AddVariable(Format('GENERAL ENG COMBUSTION:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + + for engineIndex := 1 to FSX_MAX_ENGINES do + ADefinition.AddVariable(Format('ENG FAILED:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + + for engineIndex := 1 to FSX_MAX_ENGINES do + ADefinition.AddVariable(Format('ENG ON FIRE:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); +end; + + +procedure TFSXEngineFunctionWorker.HandleData(AData: Pointer); +type + PEngineData = ^TEngineData; + TEngineData = packed record + NumberOfEngines: Integer; + Combustion: array[1..FSX_MAX_ENGINES] of Integer; + Failed: array[1..FSX_MAX_ENGINES] of Integer; + OnFire: array[1..FSX_MAX_ENGINES] of Integer; + end; + +var + engineData: PEngineData; + engineCount: Integer; + engineIndex: Integer; + hasFire: Boolean; + hasFailure: Boolean; + runningCount: Integer; + +begin + engineData := AData; + + if engineData^.NumberOfEngines > 0 then + begin + engineCount := Min(engineData^.NumberOfEngines, FSX_MAX_ENGINES); + hasFire := False; + hasFailure := False; + runningCount := 0; + + for engineIndex := 1 to engineCount do + begin + if engineData^.OnFire[engineIndex] <> 0 then + hasFire := True; + + if engineData^.Failed[engineIndex] <> 0 then + hasFailure := True; + + if engineData^.Combustion[engineIndex] <> 0 then + Inc(runningCount); + end; + + if hasFire then + SetCurrentState(FSXStateUIDEngineOnFire) + + else if hasFailure then + SetCurrentState(FSXStateUIDEngineFailed) + + else if runningCount = 0 then + SetCurrentState(FSXStateUIDEngineAllOff) + + else if runningCount = engineCount then + SetCurrentState(FSXStateUIDEngineAllRunning) + + else + SetCurrentState(FSXStateUIDEnginePartiallyRunning); + end else + SetCurrentState(FSXStateUIDEngineNoEngines); +end; + + +{ TFSXGearFunctionWorker } +procedure TFSXGearFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + ADefinition.AddVariable('IS GEAR RETRACTABLE', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('GEAR TOTAL PCT EXTENDED', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); + ADefinition.AddVariable('GEAR DAMAGE BY SPEED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('GEAR SPEED EXCEEDED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); +end; + + +procedure TFSXGearFunctionWorker.HandleData(AData: Pointer); +type + PGearData = ^TGearData; + TGearData = packed record + IsGearRetractable: Cardinal; + TotalPctExtended: Double; + DamageBySpeed: Integer; + SpeedExceeded: Integer; + end; + +var + gearData: PGearData; + +begin + gearData := AData; + + if gearData^.DamageBySpeed <> 0 then + SetCurrentState(FSXStateUIDGearDamageBySpeed) + + else if gearData^.SpeedExceeded <> 0 then + SetCurrentState(FSXStateUIDGearSpeedExceeded) + + else if gearData^.IsGearRetractable <> 0 then + begin + case Trunc(gearData ^.TotalPctExtended * 100) of + 0: SetCurrentState(FSXStateUIDGearRetracted); + 95..100: SetCurrentState(FSXStateUIDGearExtended); + else SetCurrentState(FSXStateUIDGearBetween); + end; + end else + SetCurrentState(FSXStateUIDGearNotRetractable); +end; + + +{ TFSXParkingBrakeFunctionWorker } +procedure TFSXParkingBrakeFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + ADefinition.AddVariable('BRAKE PARKING INDICATOR', FSX_UNIT_BOOL, SIMCONNECT_DATATYPE_INT32); +end; + + +procedure TFSXParkingBrakeFunctionWorker.HandleData(AData: Pointer); +begin + if PCardinal(AData)^ <> 0 then + SetCurrentState(FSXStateUIDOn) + else + SetCurrentState(FSXStateUIDOff); +end; + + +{ TFSXExitDoorFunctionWorker } +procedure TFSXExitDoorFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + ADefinition.AddVariable('CANOPY OPEN', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); +end; + + +procedure TFSXExitDoorFunctionWorker.HandleData(AData: Pointer); +begin + case Trunc(PDouble(AData)^) of + 0..5: SetCurrentState(FSXStateUIDExitDoorClosed); + 95..100: SetCurrentState(FSXStateUIDExitDoorOpen); + else SetCurrentState(FSXStateUIDExitDoorBetween); + end; +end; + + +{ TFSXTailHookFunctionWorker } +procedure TFSXTailHookFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXTailHookFunctionWorker.RegisterVariables +end; + + +procedure TFSXTailHookFunctionWorker.HandleData(AData: Pointer); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXTailHookFunctionWorker.HandleData +end; + + +{ TFSXFlapsFunctionWorker } +procedure TFSXFlapsFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXFlapsFunctionWorker.RegisterVariables +end; + + +procedure TFSXFlapsFunctionWorker.HandleData(AData: Pointer); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXFlapsFunctionWorker.HandleData +end; + + +{ TFSXSpoilersFunctionWorker } +procedure TFSXSpoilersFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXSpoilersFunctionWorker.RegisterVariables +end; + + +procedure TFSXSpoilersFunctionWorker.HandleData(AData: Pointer); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXSpoilersFunctionWorker.HandleData +end; + + +{ TFSXBatteryMasterFunctionWorker } +procedure TFSXBatteryMasterFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXBatteryMasterFunctionWorker.RegisterVariables +end; + + +procedure TFSXBatteryMasterFunctionWorker.HandleData(AData: Pointer); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXBatteryMasterFunctionWorker.HandleData +end; + + +{ TFSXAvionicsMasterFunctionWorker } +procedure TFSXAvionicsMasterFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAvionicsMasterFunctionWorker.RegisterVariables +end; + + +procedure TFSXAvionicsMasterFunctionWorker.HandleData(AData: Pointer); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAvionicsMasterFunctionWorker.HandleData +end; + + +{ TFSXPressDumpSwitchFunctionWorker } +procedure TFSXPressDumpSwitchFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXPressDumpSwitchFunctionWorker.RegisterVariables +end; + + +procedure TFSXPressDumpSwitchFunctionWorker.HandleData(AData: Pointer); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXPressDumpSwitchFunctionWorker.HandleData +end; + + +{ TFSXEngineAntiIceFunctionWorker } +procedure TFSXEngineAntiIceFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXEngineAntiIceFunctionWorker.RegisterVariables +end; + + +procedure TFSXEngineAntiIceFunctionWorker.HandleData(AData: Pointer); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXEngineAntiIceFunctionWorker.HandleData +end; + + +{ TFSXFuelPumpFunctionWorker } +procedure TFSXFuelPumpFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXFuelPumpFunctionWorker.RegisterVariables +end; + + +procedure TFSXFuelPumpFunctionWorker.HandleData(AData: Pointer); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXFuelPumpFunctionWorker.HandleData +end; + + +{ TFSXDeIceFunctionWorker } +procedure TFSXDeIceFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXDeIceFunctionWorker.RegisterVariables +end; + + +procedure TFSXDeIceFunctionWorker.HandleData(AData: Pointer); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXDeIceFunctionWorker.HandleData +end; + + +{ TFSXLightStatesFunctionWorker } +procedure TFSXLightStatesFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + ADefinition.AddVariable('LIGHT ON STATES', FSX_UNIT_MASK, SIMCONNECT_DATATYPE_INT32); +end; + + +procedure TFSXLightStatesFunctionWorker.HandleData(AData: Pointer); +begin + if (PCardinal(AData)^ and StateMask) <> 0 then + SetCurrentState(FSXStateUIDOn) + else + SetCurrentState(FSXStateUIDOff); +end; + + +{ TFSXAutoPilotFunctionWorker } +procedure TFSXAutoPilotFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotFunctionWorker.RegisterVariables +end; + + +procedure TFSXAutoPilotFunctionWorker.HandleData(AData: Pointer); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotFunctionWorker.HandleData +end; + + +{ TFSXAutoPilotHeadingFunctionWorker } +procedure TFSXAutoPilotHeadingFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotHeadingFunctionWorker.RegisterVariables +end; + + +procedure TFSXAutoPilotHeadingFunctionWorker.HandleData(AData: Pointer); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotHeadingFunctionWorker.HandleData +end; + + +{ TFSXAutoPilotApproachFunctionWorker } +procedure TFSXAutoPilotApproachFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotApproachFunctionWorker.RegisterVariables +end; + + +procedure TFSXAutoPilotApproachFunctionWorker.HandleData(AData: Pointer); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotApproachFunctionWorker.HandleData +end; + + +{ TFSXAutoPilotBackcourseFunctionWorker } +procedure TFSXAutoPilotBackcourseFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotBackcourseFunctionWorker.RegisterVariables +end; + + +procedure TFSXAutoPilotBackcourseFunctionWorker.HandleData(AData: Pointer); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotBackcourseFunctionWorker.HandleData +end; + + +{ TFSXAutoPilotAltitudeFunctionWorker } +procedure TFSXAutoPilotAltitudeFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotAltitudeFunctionWorker.RegisterVariables +end; + + +procedure TFSXAutoPilotAltitudeFunctionWorker.HandleData(AData: Pointer); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotAltitudeFunctionWorker.HandleData +end; + + +{ TFSXAutoPilotNavFunctionWorker } +procedure TFSXAutoPilotNavFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotNavFunctionWorker.RegisterVariables +end; + + +procedure TFSXAutoPilotNavFunctionWorker.HandleData(AData: Pointer); +begin + // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotNavFunctionWorker.HandleData +end; + +end. diff --git a/G940LEDControl/Units/FSXResources.pas b/G940LEDControl/Units/FSXResources.pas index b6f6300..f22919e 100644 --- a/G940LEDControl/Units/FSXResources.pas +++ b/G940LEDControl/Units/FSXResources.pas @@ -7,6 +7,7 @@ const FSXProviderUID = 'fsx'; FSXCategory = 'Flight Simulator X'; FSXCategoryLights = FSXCategory + ' - Lights'; + FSXCategoryAutoPilot = FSXCategory + ' - Autopilot'; FSXStateUIDOn = 'on'; FSXStateUIDOff = 'off'; @@ -72,6 +73,76 @@ const FSXFunctionUIDRecognitionLights = 'recognitionLights'; FSXFunctionDisplayNameRecognitionLights = 'Recognition lights'; + FSXFunctionUIDParkingBrake = 'parkingBrake'; + FSXFunctionDisplayNameParkingBrake = 'Parking brake'; + + FSXFunctionUIDExitDoor = 'exitDoor'; + FSXFunctionDisplayNameExitDoor = 'Exit door'; + + FSXStateUIDExitDoorClosed = 'closed'; + FSXStateUIDExitDoorBetween = 'between'; + FSXStateUIDExitDoorOpen = 'open'; + + FSXStateDisplayNameExitDoorClosed = 'Closed'; + FSXStateDisplayNameExitDoorBetween = 'Opening / closing'; + FSXStateDisplayNameExitDoorOpen = 'Open'; + + FSXFunctionUIDTailHook = 'tailHook'; + FSXFunctionDisplayNameTailHook = 'Tail hook'; + + FSXStateUIDTailHookRetracted = 'retracted'; + FSXStateUIDTailHookBetween = 'between'; + FSXStateUIDTailHookExtended = 'extended'; + + FSXStateDisplayNameTailHookRetracted = 'Retracted'; + FSXStateDisplayNameTailHookBetween = 'Extending / retracting'; + FSXStateDisplayNameTailHookExtended = 'Extended'; + + + FSXFunctionUIDFlaps = 'flaps'; + FSXFunctionDisplayNameFlaps = 'Flaps'; + + FSXFunctionUIDSpoilers = 'spoilers'; + FSXFunctionDisplayNameSpoilers = 'Spoilers'; + + FSXFunctionUIDBatteryMaster = 'batteryMaster'; + FSXFunctionDisplayNameBatteryMaster = 'Battery master'; + + FSXFunctionUIDAvionicsMaster = 'avionicsMaster'; + FSXFunctionDisplayNameAvionicsMaster = 'Avionics master'; + + FSXFunctionUIDPressDumpSwitch = 'pressurizationDumpSwitch'; + FSXFunctionDisplayNamePressDumpSwitch = 'Pressurization dump switch'; + + FSXFunctionUIDEngineAntiIce = 'engineAntiIce'; + FSXFunctionDisplayNameEngineAntiIce = 'Engine anti-ice'; + + FSXFunctionUIDFuelPump = 'fuelPump'; + FSXFunctionDisplayNameFuelPump = 'Fuel pump'; + + FSXFunctionUIDDeIce = 'structuralDeIce'; + FSXFunctionDisplayNameDeIce = 'De-ice'; + + FSXFunctionUIDAutoPilot = 'autoPilotMaster'; + FSXFunctionDisplayNameAutoPilot = 'Autopilot master'; + + FSXFunctionUIDAutoPilotHeading = 'autoPilotHeading'; + FSXFunctionDisplayNameAutoPilotHeading = 'Autopilot heading'; + + FSXFunctionUIDAutoPilotApproach = 'autoPilotApproach'; + FSXFunctionDisplayNameAutoPilotApproach = 'Autopilot approach'; + + FSXFunctionUIDAutoPilotBackcourse = 'autoPilotBackcourse'; + FSXFunctionDisplayNameAutoPilotBackcourse = 'Autopilot backcourse'; + + FSXFunctionUIDAutoPilotAltitude = 'autoPilotAltitude'; + FSXFunctionDisplayNameAutoPilotAltitude = 'Autopilot altitude'; + + FSXFunctionUIDAutoPilotNav = 'autoPilotNav'; + FSXFunctionDisplayNameAutoPilotNav = 'Autopilot nav'; + + + implementation diff --git a/G940LEDControl/Units/FSXSimConnectClient.pas b/G940LEDControl/Units/FSXSimConnectClient.pas index 70a4c37..bfd34e7 100644 --- a/G940LEDControl/Units/FSXSimConnectClient.pas +++ b/G940LEDControl/Units/FSXSimConnectClient.pas @@ -306,7 +306,8 @@ end; procedure TFSXSimConnectClient.Cleanup; begin - // TODO unregister definitions ? + // #ToDo1 -oMvR: 22-2-2013: unregister definitions + if SimConnectHandle <> 0 then SimConnect_Close(SimConnectHandle); @@ -491,7 +492,7 @@ var begin removeDefinition := Msg.MsgData; - // TODO actually remove the definition + // #ToDo1 -oMvR: 22-2-2013: actually remove the definition removeDefinition.Signal; end; diff --git a/G940LEDControl/Units/LEDStateConsumer.pas b/G940LEDControl/Units/LEDStateConsumer.pas index 09b998a..f7c1912 100644 --- a/G940LEDControl/Units/LEDStateConsumer.pas +++ b/G940LEDControl/Units/LEDStateConsumer.pas @@ -192,7 +192,10 @@ begin for oldWorker in ButtonWorkers do begin if Assigned(oldWorker) then + begin + (oldWorker as ILEDFunctionWorker).Detach(Self); oldWorkers.Add(oldWorker); + end; end; ButtonWorkers.Clear; diff --git a/G940LEDControl/Units/Settings.pas b/G940LEDControl/Units/Settings.pas new file mode 100644 index 0000000..14c9d4a --- /dev/null +++ b/G940LEDControl/Units/Settings.pas @@ -0,0 +1,63 @@ +unit Settings; + +interface +uses + X2UtPersistIntf; + +type + TSettings = class(TObject) + private + FCheckUpdates: Boolean; + FHasCheckUpdates: Boolean; + + procedure SetCheckUpdates(const Value: Boolean); + public + procedure Load(AReader: IX2PersistReader); + procedure Save(AWriter: IX2PersistWriter); + + property CheckUpdates: Boolean read FCheckUpdates write SetCheckUpdates; + property HasCheckUpdates: Boolean read FHasCheckUpdates; + end; + + +implementation +const + SectionSettings = 'Settings'; + + KeyCheckUpdates = 'CheckUpdates'; + + +{ TSettings } +procedure TSettings.Load(AReader: IX2PersistReader); +var + value: Boolean; + +begin + if AReader.BeginSection(SectionSettings) then + try + if AReader.ReadBoolean(KeyCheckUpdates, value) then + CheckUpdates := value; + finally + AReader.EndSection; + end; +end; + + +procedure TSettings.Save(AWriter: IX2PersistWriter); +begin + if AWriter.BeginSection(SectionSettings) then + try + AWriter.WriteBoolean(KeyCheckUpdates, CheckUpdates); + finally + AWriter.EndSection; + end; +end; + + +procedure TSettings.SetCheckUpdates(const Value: Boolean); +begin + FCheckUpdates := Value; + FHasCheckUpdates := True; +end; + +end. From 62fb8e1357e1174edf43c4175c5fb3fba567c689 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Sat, 23 Feb 2013 08:32:20 +0000 Subject: [PATCH 14/16] Added: tail hook worker, flaps and spoilers --- G940LEDControl/Units/FSXLEDFunction.pas | 10 +++- G940LEDControl/Units/FSXLEDFunctionWorker.pas | 58 +++++++++++++++++-- G940LEDControl/Units/FSXResources.pas | 22 +++++++ 3 files changed, 82 insertions(+), 8 deletions(-) diff --git a/G940LEDControl/Units/FSXLEDFunction.pas b/G940LEDControl/Units/FSXLEDFunction.pas index acf7aa6..1283ede 100644 --- a/G940LEDControl/Units/FSXLEDFunction.pas +++ b/G940LEDControl/Units/FSXLEDFunction.pas @@ -276,7 +276,10 @@ end; { TFSXFlapsFunction } procedure TFSXFlapsFunction.RegisterStates; begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXFlapsFunction.RegisterStates + RegisterState(TLEDState.Create(FSXStateUIDFlapsNotAvailable, FSXStateDisplayNameFlapsNotAvailable, lcOff)); + RegisterState(TLEDState.Create(FSXStateUIDFlapsRetracted, FSXStateDisplayNameFlapsRetracted, lcGreen)); + RegisterState(TLEDState.Create(FSXStateUIDFlapsBetween, FSXStateDisplayNameFlapsBetween, lcAmber)); + RegisterState(TLEDState.Create(FSXStateUIDFlapsExtended, FSXStateDisplayNameFlapsExtended, lcRed)); end; @@ -289,7 +292,10 @@ end; { TFSXSpoilersFunction } procedure TFSXSpoilersFunction.RegisterStates; begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXSpoilersFunction.RegisterStates + RegisterState(TLEDState.Create(FSXStateUIDSpoilersNotAvailable, FSXStateDisplayNameSpoilersNotAvailable, lcOff)); + RegisterState(TLEDState.Create(FSXStateUIDSpoilersRetracted, FSXStateDisplayNameSpoilersRetracted, lcGreen)); + RegisterState(TLEDState.Create(FSXStateUIDSpoilersBetween, FSXStateDisplayNameSpoilersBetween, lcAmber)); + RegisterState(TLEDState.Create(FSXStateUIDSpoilersExtended, FSXStateDisplayNameSpoilersExtended, lcRed)); end; diff --git a/G940LEDControl/Units/FSXLEDFunctionWorker.pas b/G940LEDControl/Units/FSXLEDFunctionWorker.pas index 7d97d89..711a298 100644 --- a/G940LEDControl/Units/FSXLEDFunctionWorker.pas +++ b/G940LEDControl/Units/FSXLEDFunctionWorker.pas @@ -316,39 +316,85 @@ end; { TFSXTailHookFunctionWorker } procedure TFSXTailHookFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXTailHookFunctionWorker.RegisterVariables + ADefinition.AddVariable('TAILHOOK POSITION', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); end; procedure TFSXTailHookFunctionWorker.HandleData(AData: Pointer); begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXTailHookFunctionWorker.HandleData + case Trunc(PDouble(AData)^) of + 0..5: SetCurrentState(FSXStateUIDTailHookRetracted); + 95..100: SetCurrentState(FSXStateUIDTailHookBetween); + else SetCurrentState(FSXStateUIDTailHookExtended); + end; end; { TFSXFlapsFunctionWorker } procedure TFSXFlapsFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXFlapsFunctionWorker.RegisterVariables + ADefinition.AddVariable('FLAPS AVAILABLE', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('FLAPS HANDLE PERCENT', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); end; procedure TFSXFlapsFunctionWorker.HandleData(AData: Pointer); +type + PFlapsData = ^TFlapsData; + TFlapsData = packed record + FlapsAvailable: Cardinal; + FlapsHandlePercent: Double; + end; + +var + flapsData: PFlapsData; + begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXFlapsFunctionWorker.HandleData + flapsData := AData; + + if flapsData^.FlapsAvailable <> 0 then + begin + case Trunc(flapsData^.FlapsHandlePercent) of + 0..5: SetCurrentState(FSXStateUIDFlapsRetracted); + 95..100: SetCurrentState(FSXStateUIDFlapsExtended); + else SetCurrentState(FSXStateUIDFlapsBetween); + end; + end else + SetCurrentState(FSXStateUIDFlapsNotAvailable); end; { TFSXSpoilersFunctionWorker } procedure TFSXSpoilersFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXSpoilersFunctionWorker.RegisterVariables + ADefinition.AddVariable('SPOILER AVAILABLE', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('SPOILERS HANDLE POSITION', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); end; procedure TFSXSpoilersFunctionWorker.HandleData(AData: Pointer); +type + PSpoilersData = ^TSpoilersData; + TSpoilersData = packed record + SpoilersAvailable: Cardinal; + SpoilersHandlePercent: Double; + end; + +var + spoilersData: PSpoilersData; + begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXSpoilersFunctionWorker.HandleData + SpoilersData := AData; + + if SpoilersData^.SpoilersAvailable <> 0 then + begin + case Trunc(SpoilersData^.SpoilersHandlePercent) of + 0..5: SetCurrentState(FSXStateUIDSpoilersRetracted); + 95..100: SetCurrentState(FSXStateUIDSpoilersExtended); + else SetCurrentState(FSXStateUIDSpoilersBetween); + end; + end else + SetCurrentState(FSXStateUIDSpoilersNotAvailable); end; diff --git a/G940LEDControl/Units/FSXResources.pas b/G940LEDControl/Units/FSXResources.pas index f22919e..1e9a7c7 100644 --- a/G940LEDControl/Units/FSXResources.pas +++ b/G940LEDControl/Units/FSXResources.pas @@ -102,9 +102,31 @@ const FSXFunctionUIDFlaps = 'flaps'; FSXFunctionDisplayNameFlaps = 'Flaps'; + FSXStateUIDFlapsNotAvailable = 'notAvailable'; + FSXStateUIDFlapsRetracted = 'retracted'; + FSXStateUIDFlapsBetween = 'between'; + FSXStateUIDFlapsExtended = 'extended'; + + FSXStateDisplayNameFlapsNotAvailable = 'No flaps'; + FSXStateDisplayNameFlapsRetracted = 'Retracted'; + FSXStateDisplayNameFlapsBetween = 'Extending / retracting'; + FSXStateDisplayNameFlapsExtended = 'Extended'; + + FSXFunctionUIDSpoilers = 'spoilers'; FSXFunctionDisplayNameSpoilers = 'Spoilers'; + FSXStateUIDSpoilersNotAvailable = 'notAvailable'; + FSXStateUIDSpoilersRetracted = 'retracted'; + FSXStateUIDSpoilersBetween = 'between'; + FSXStateUIDSpoilersExtended = 'extended'; + + FSXStateDisplayNameSpoilersNotAvailable = 'No spoilers'; + FSXStateDisplayNameSpoilersRetracted = 'Retracted'; + FSXStateDisplayNameSpoilersBetween = 'Extending / retracting'; + FSXStateDisplayNameSpoilersExtended = 'Extended'; + + FSXFunctionUIDBatteryMaster = 'batteryMaster'; FSXFunctionDisplayNameBatteryMaster = 'Battery master'; From 50580144ac057316c7add1a485f4ad9ceff3b9eb Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Sun, 24 Feb 2013 20:09:47 +0000 Subject: [PATCH 15/16] Ready for beta 1.0! Added: FSX state Added: incremental search for functions Added: converted all 0.6 functions Fixed: state being forgotten when changing the profile --- G940LEDControl/Forms/ButtonFunctionFrm.dfm | 10 +- G940LEDControl/Forms/ButtonFunctionFrm.pas | 23 + G940LEDControl/Forms/MainFrm.dfm | 1021 ++++++++++------- G940LEDControl/Forms/MainFrm.pas | 428 ++++++- G940LEDControl/G940LEDControl.dpr | 3 +- G940LEDControl/G940LEDControl.dproj | 113 +- G940LEDControl/G940LEDControl.res | Bin 27760 -> 28524 bytes .../Resources/Images/FSXConnected.png | Bin 0 -> 1852 bytes .../Resources/Images/FSXDisconnected.png | Bin 0 -> 1296 bytes G940LEDControl/Resources/Images/Found.png | Bin 5641 -> 5322 bytes G940LEDControl/Resources/Images/NotFound.png | Bin 4218 -> 5215 bytes G940LEDControl/Units/ConfigConversion.pas | 57 +- G940LEDControl/Units/FSXLEDFunction.pas | 466 ++++---- .../Units/FSXLEDFunctionProvider.pas | 90 +- G940LEDControl/Units/FSXLEDFunctionWorker.pas | 615 +++++----- G940LEDControl/Units/FSXResources.pas | 29 +- G940LEDControl/Units/FSXSimConnectClient.pas | 72 +- G940LEDControl/Units/FSXSimConnectIntf.pas | 8 + .../Units/FSXSimConnectStateMonitor.pas | 114 ++ G940LEDControl/Units/G940LEDStateConsumer.pas | 4 +- G940LEDControl/Units/LEDFunction.pas | 165 ++- G940LEDControl/Units/LEDFunctionIntf.pas | 5 +- G940LEDControl/Units/LEDStateConsumer.pas | 56 +- G940LEDControl/Units/Profile.pas | 84 +- G940LEDControl/Units/Settings.pas | 8 + G940LEDControl/Units/StaticLEDFunction.pas | 12 +- 26 files changed, 2232 insertions(+), 1151 deletions(-) create mode 100644 G940LEDControl/Resources/Images/FSXConnected.png create mode 100644 G940LEDControl/Resources/Images/FSXDisconnected.png create mode 100644 G940LEDControl/Units/FSXSimConnectStateMonitor.pas diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.dfm b/G940LEDControl/Forms/ButtonFunctionFrm.dfm index a58a285..a9f3300 100644 --- a/G940LEDControl/Forms/ButtonFunctionFrm.dfm +++ b/G940LEDControl/Forms/ButtonFunctionFrm.dfm @@ -40,7 +40,7 @@ object ButtonFunctionForm: TButtonFunctionForm Margins.Bottom = 0 Align = alBottom BevelOuter = bvNone - TabOrder = 2 + TabOrder = 3 DesignSize = ( 692 43) @@ -93,7 +93,8 @@ object ButtonFunctionForm: TButtonFunctionForm Header.Font.Name = 'Tahoma' Header.Font.Style = [] Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoShowSortGlyphs, hoVisible] - TabOrder = 0 + IncrementalSearch = isAll + TabOrder = 1 TreeOptions.AutoOptions = [toAutoDropExpand, toAutoScrollOnExpand, toAutoSort, toAutoTristateTracking, toAutoDeleteMovedNodes] TreeOptions.MiscOptions = [toAcceptOLEDrop, toFullRepaintOnResize, toInitOnSave, toWheelPanning, toEditOnClick] TreeOptions.PaintOptions = [toShowButtons, toShowDropmark, toShowTreeLines, toThemeAware, toUseBlendedImages] @@ -101,6 +102,7 @@ object ButtonFunctionForm: TButtonFunctionForm OnFocusChanged = vstFunctionsFocusChanged OnGetText = vstFunctionsGetText OnPaintText = vstFunctionsPaintText + OnIncrementalSearch = vstFunctionsIncrementalSearch Columns = < item Position = 0 @@ -120,7 +122,7 @@ object ButtonFunctionForm: TButtonFunctionForm Margins.Bottom = 0 Align = alClient BevelOuter = bvNone - TabOrder = 1 + TabOrder = 2 object pnlName: TPanel Left = 0 Top = 0 @@ -203,7 +205,7 @@ object ButtonFunctionForm: TButtonFunctionForm BevelOuter = bvNone Color = clWindow ParentBackground = False - TabOrder = 3 + TabOrder = 0 DesignSize = ( 692 50) diff --git a/G940LEDControl/Forms/ButtonFunctionFrm.pas b/G940LEDControl/Forms/ButtonFunctionFrm.pas index f442d4c..a1e81bf 100644 --- a/G940LEDControl/Forms/ButtonFunctionFrm.pas +++ b/G940LEDControl/Forms/ButtonFunctionFrm.pas @@ -49,6 +49,7 @@ type procedure vstFunctionsGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); procedure vstFunctionsPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); procedure vstFunctionsFocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); + procedure vstFunctionsIncrementalSearch(Sender: TBaseVirtualTree; Node: PVirtualNode; const SearchText: string; var Result: Integer); procedure btnOKClick(Sender: TObject); private FProfile: TProfile; @@ -97,6 +98,7 @@ type implementation uses + System.Math, System.SysUtils, Winapi.Windows, @@ -377,6 +379,9 @@ var color: TLEDColor; begin + if not Assigned(Button) then + FButton := Profile.Buttons[ButtonIndex]; + Button.ProviderUID := SelectedProvider.GetUID; Button.FunctionUID := SelectedFunction.GetUID; @@ -438,6 +443,24 @@ begin end; +procedure TButtonFunctionForm.vstFunctionsIncrementalSearch(Sender: TBaseVirtualTree; Node: PVirtualNode; + const SearchText: string; var Result: Integer); +var + nodeData: PFunctionNodeData; + displayName: string; + +begin + nodeData := Sender.GetNodeData(Node); + + if nodeData^.NodeType = ntFunction then + begin + displayName := nodeData^.LEDFunction.GetDisplayName; + Result := StrLIComp(PChar(displayName), PChar(SearchText), Min(Length(displayName), Length(searchText))); + end else + Result := -1; +end; + + procedure TButtonFunctionForm.vstFunctionsPaintText(Sender: TBaseVirtualTree; const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType); var diff --git a/G940LEDControl/Forms/MainFrm.dfm b/G940LEDControl/Forms/MainFrm.dfm index f43fd5a..8fdca19 100644 --- a/G940LEDControl/Forms/MainFrm.dfm +++ b/G940LEDControl/Forms/MainFrm.dfm @@ -1,11 +1,12 @@ object MainForm: TMainForm Left = 0 Top = 0 + ActiveControl = cmbProfiles BorderIcons = [biSystemMenu, biMinimize] BorderStyle = bsSingle Caption = 'G940 LED Control' - ClientHeight = 562 - ClientWidth = 465 + ClientHeight = 548 + ClientWidth = 466 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText @@ -21,25 +22,25 @@ object MainForm: TMainForm object PageControl: TPageControl AlignWithMargins = True Left = 8 - Top = 80 - Width = 449 - Height = 474 + Top = 60 + Width = 450 + Height = 480 Margins.Left = 8 Margins.Top = 8 Margins.Right = 8 Margins.Bottom = 8 ActivePage = tsFSX Align = alClient - TabOrder = 1 + TabOrder = 0 object tsFSX: TTabSheet Caption = 'Configuration' DesignSize = ( - 441 - 446) + 442 + 452) object lblP1Function: TLabel Left = 64 Top = 73 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False @@ -51,21 +52,23 @@ object MainForm: TMainForm Font.Name = 'Tahoma' Font.Style = [fsBold] ParentFont = False + ExplicitWidth = 364 end object lblP1Category: TLabel Left = 64 Top = 89 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False Caption = '[runtime: category]' EllipsisPosition = epEndEllipsis + ExplicitWidth = 364 end object lblP2Function: TLabel Left = 64 Top = 120 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False @@ -77,21 +80,23 @@ object MainForm: TMainForm Font.Name = 'Tahoma' Font.Style = [fsBold] ParentFont = False + ExplicitWidth = 364 end object lblP2Category: TLabel Left = 64 Top = 136 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False Caption = '[runtime: category]' EllipsisPosition = epEndEllipsis + ExplicitWidth = 364 end object lblP3Function: TLabel Left = 64 Top = 167 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False @@ -103,21 +108,23 @@ object MainForm: TMainForm Font.Name = 'Tahoma' Font.Style = [fsBold] ParentFont = False + ExplicitWidth = 364 end object lblP3Category: TLabel Left = 64 Top = 183 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False Caption = '[runtime: category]' EllipsisPosition = epEndEllipsis + ExplicitWidth = 364 end object lblP4Function: TLabel Left = 64 Top = 214 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False @@ -129,21 +136,23 @@ object MainForm: TMainForm Font.Name = 'Tahoma' Font.Style = [fsBold] ParentFont = False + ExplicitWidth = 364 end object lblP4Category: TLabel Left = 64 Top = 230 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False Caption = '[runtime: category]' EllipsisPosition = epEndEllipsis + ExplicitWidth = 364 end object lblP5Function: TLabel Left = 64 Top = 261 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False @@ -155,21 +164,23 @@ object MainForm: TMainForm Font.Name = 'Tahoma' Font.Style = [fsBold] ParentFont = False + ExplicitWidth = 364 end object lblP5Category: TLabel Left = 64 Top = 277 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False Caption = '[runtime: category]' EllipsisPosition = epEndEllipsis + ExplicitWidth = 364 end object lblP6Function: TLabel Left = 64 Top = 308 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False @@ -181,21 +192,23 @@ object MainForm: TMainForm Font.Name = 'Tahoma' Font.Style = [fsBold] ParentFont = False + ExplicitWidth = 364 end object lblP6Category: TLabel Left = 64 Top = 324 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False Caption = '[runtime: category]' EllipsisPosition = epEndEllipsis + ExplicitWidth = 364 end object lblP7Function: TLabel Left = 64 Top = 355 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False @@ -207,21 +220,23 @@ object MainForm: TMainForm Font.Name = 'Tahoma' Font.Style = [fsBold] ParentFont = False + ExplicitWidth = 364 end object lblP7Category: TLabel Left = 64 Top = 371 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False Caption = '[runtime: category]' EllipsisPosition = epEndEllipsis + ExplicitWidth = 364 end object lblP8Function: TLabel Left = 64 Top = 402 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False @@ -233,16 +248,18 @@ object MainForm: TMainForm Font.Name = 'Tahoma' Font.Style = [fsBold] ParentFont = False + ExplicitWidth = 364 end object lblP8Category: TLabel Left = 64 Top = 418 - Width = 364 + Width = 365 Height = 13 Anchors = [akLeft, akTop, akRight] AutoSize = False Caption = '[runtime: category]' EllipsisPosition = epEndEllipsis + ExplicitWidth = 364 end object lblProfile: TLabel Left = 11 @@ -264,7 +281,7 @@ object MainForm: TMainForm Width = 41 Height = 41 Caption = 'P1' - TabOrder = 0 + TabOrder = 3 end object btnP2: TButton Left = 11 @@ -272,7 +289,7 @@ object MainForm: TMainForm Width = 41 Height = 41 Caption = 'P2' - TabOrder = 1 + TabOrder = 4 end object btnP3: TButton Left = 11 @@ -280,7 +297,7 @@ object MainForm: TMainForm Width = 41 Height = 41 Caption = 'P3' - TabOrder = 2 + TabOrder = 5 end object btnP4: TButton Left = 11 @@ -288,7 +305,7 @@ object MainForm: TMainForm Width = 41 Height = 41 Caption = 'P4' - TabOrder = 3 + TabOrder = 6 end object btnP5: TButton Left = 11 @@ -296,7 +313,7 @@ object MainForm: TMainForm Width = 41 Height = 41 Caption = 'P5' - TabOrder = 4 + TabOrder = 7 end object btnP6: TButton Left = 11 @@ -304,7 +321,7 @@ object MainForm: TMainForm Width = 41 Height = 41 Caption = 'P6' - TabOrder = 5 + TabOrder = 8 end object btnP7: TButton Left = 11 @@ -312,7 +329,7 @@ object MainForm: TMainForm Width = 41 Height = 41 Caption = 'P7' - TabOrder = 6 + TabOrder = 9 end object btnP8: TButton Left = 11 @@ -320,45 +337,43 @@ object MainForm: TMainForm Width = 41 Height = 41 Caption = 'P8' - TabOrder = 7 + TabOrder = 10 end object cmbProfiles: TComboBox Left = 64 Top = 16 - Width = 234 + Width = 208 Height = 21 Style = csDropDownList Anchors = [akLeft, akTop, akRight] Sorted = True - TabOrder = 8 + TabOrder = 0 OnClick = cmbProfilesClick end object btnSaveProfile: TButton - Left = 304 - Top = 16 - Width = 54 - Height = 21 + Left = 279 + Top = 15 + Width = 72 + Height = 23 Anchors = [akTop, akRight] - Caption = 'New' - TabOrder = 9 + Caption = 'Sa&ve As...' + TabOrder = 1 + OnClick = btnSaveProfileClick end object btnDeleteProfile: TButton - Left = 364 - Top = 16 - Width = 64 - Height = 21 + Left = 357 + Top = 15 + Width = 72 + Height = 23 Anchors = [akTop, akRight] - Caption = 'Delete' - TabOrder = 10 + Caption = '&Delete' + TabOrder = 2 + OnClick = btnDeleteProfileClick end end object tsAbout: TTabSheet Caption = 'About' ImageIndex = 1 - ExplicitLeft = 0 - ExplicitTop = 0 - ExplicitWidth = 0 - ExplicitHeight = 0 object lblVersionCaption: TLabel Left = 16 Top = 67 @@ -462,389 +477,537 @@ object MainForm: TMainForm end end end - object pnlG940: TPanel + object pnlState: TPanel AlignWithMargins = True - Left = 8 - Top = 8 - Width = 449 - Height = 64 - Margins.Left = 8 - Margins.Top = 8 - Margins.Right = 8 - Margins.Bottom = 0 + Left = 3 + Top = 3 + Width = 460 + Height = 46 Align = alTop BevelOuter = bvNone - TabOrder = 0 - DesignSize = ( - 449 - 64) - object imgStateFound: TImage - Left = 0 - Top = 0 - Width = 64 - Height = 64 - AutoSize = True - Picture.Data = { - 0954506E67496D61676589504E470D0A1A0A0000000D49484452000000400000 - 00400806000000AA6971DE0000001974455874536F6674776172650041646F62 - 6520496D616765526561647971C9653C000016424944415478DAED5B09745465 - 96BEB5A4925452492A5B5565237B422A1B842D203620A233CD32EA8C23DD6D6B - AB38CCA0D29E565BB6695A5C41A0C1BD955D96398D0B22626B1F1515B121812C - 908DAC55D993CA5AD9AA2A5573BFBFF26241E3B4A860E6D87FCE3BAFB6B7DCEF - FFEEBDDFBDFF8BCCE572251251276F72FA710D276F5A1903A01D01E0C7380400 - 61FCA2ED87BE931F6884FD03801F000095D56AD5DBEDF6009BCDE6373C3CECCD - AFB179F1A6181C1854A9FDD4D69494943F0F0E0E6A9B9B9B93F9774AFE4EEE74 - 3A8937194E2297CB9D0A85C2A1542A6D5E5E5E761F1F9FDEB0B0B07A7EEF18B3 - 00B4B6B61ADFF8D3C16D1D1D1DE90EC7B0920D53381C0EF9D0D0909C8DA5FEFE - 7EEAEAEAC2DE71DFFDF7AD292D29FDF7828282F49E9E1E396FD4D7DF47B6211B - F131C4C091D3E574D96D76676868A833332B7378CE7573FE7CFBEDB7DFC6C0D8 - C61C00ED6DED098FFDFEB1BF984CA6581F6F1F72F11F06661506F17D505F5F1F - 353535512783306DEA54F1596F6F2F3163686060801828F15B6683D8982D1417 - 1F4773E7CEA5F0F070EAE7DFDC7DCF5D130D06C399310500A8FEE8238F7E78FC - F8F15C3F3F3F61984C26137B00803DCF1A7577775375753585EB741415192900 - 81D1D860B09875B71BE09C949E9E4E1919198239CC2A8A8D8BA5DB7EB6F8E149 - 3939CF8E2900DE39F4CEFD2B57ACDC0AE361288CF71CECCFE2B39A9A1A31E3E3 - C68D1B3512332EB1447A8DCF636262C4CC9796968A0DAF333233282030E0FC2D - B7DC325BA3D1348C0900F88695BF7E60F9F1FCFCD353008064ACB461001450BF - ACBC8CB45A2D2527258B59C78C63F3A43DDE0380D4D454CA9934897C7C7D68FB - 6BDBE8D65B6FA5B884381CE75ABC787136BB44D19800A0B7B747F75F4B979570 - 700BF6F5F51500C0600C4F269CCA3B4595559594919E41498949170020190F20 - 603C3ECBC9C911B1019F7FF4F14774F34D37D3D4695369F69CD90F2425253DCF - A7748D09001A1A1A8CCBEF5F9ECF37EFCD83384D11A7AD5157C01E061D3B768C - 6A4C35343967F2280012ED3D37F83B8E9F316306718AA4BADA3AAA33D751CEC4 - 1C321AD368C97FDC3B272222E2E36F787B571E80A2A2A2EB1F79E8910F70D300 - 007B80800D6C50A954C2D88F3FF998EA1BEA39FA4FA3F8B878425AF47401C90D - F0398E4B4B4B63838D545656466016B24A5FAF951EFCCD83F35252533F1C3300 - 7CFED967B7AE5CB1EA7FD46AB530569A7D0908EC01C0B14F8F51635323CD983E - 8362A262687068F082E007E3A50088E358F40800CE9E3DCB2039E991471FA1A6 - C6C6F6DCE9D37374BA70D39801E0C30F3EB86BD5CAD5DBFCFDFD85B19E0C905E - C3A8CF8F7F4EED1DED3463DA74D285EB69C83674010092F801587ABD9E58FC50 - 5D5D1D353636D21D77FC92162C5A4401019A0FF9BB791767991F1480B7DE7CF3 - A1B5BFFBFD06002031C0D305F01EB37DF2E449B19F3471120506068E467C4FE3 - A52D2424443080330BC7042B6D7D6EAB0051A7D79FCBCDCDCDE1CB0E8D190076 - EDDCB5EEA9279F5ACD79799401921B481B025BF9F9722183A74E9E2A7C5A8AFA - 608104060413400300ACFD292F2F8FDADA5A69DE0D375050509073F9F2E54BB3 - B2B35EBD8CDBBBF200BCF4E24BEBD73FB3FE61CCAAA7EF63F304A0BAA69A7AAD - BD8201F8CE53F8783200BFC7ECB3C1545E5E4E26CE0009F189347FE1828655AB - 56A630407D630A802D7FD8B265C3FA0D0F80019E867B02009D5FC10C4030CBCE - CA16E9F162C325198CEF38CD0937816A940223CF7CE1A6CD9B263300F63105C0 - E64D9BB63EFDD433F70300180B0A7BC6007C86D456535BC3CA88282D354D50DD - 53F74B00482E00A98C8DD1A0B0F030FAE2F8173471C28477376CDCB0E0326FEF - CA03F0C273CF3FFD5B1EC1C121C26849FC486048002005028044A6B35424499B - E77B9C030C08E1F379FB780B006A590CCDBDEEBA5797DDBFECDE3107C0892F4E - DCB86CD9B29D0DF50D3A501D0623C849AA102080C22DAD2DE2754C748C384E18 - EE74A1E6170048ACC0315CEE8A18A0F6558B7489E397FEE7D2DF2E5CB470FD98 - 030083357B585161E1A453274F5DC39A7F527151714E7D7D43080CF6E5683ECC - C6593A2DACDE5D141A1C3ACA1430028A1EC64BA0E03BE800082B1457A80A2B2B - 2B69D57FAFBAF99A19D7BC352601B8787475751A8A8A8AB3563EBA6273FEE9FC - 541F6604040E0CD40669C58CE2B55C8178E1454EC7B0384E124F3A9D8E828383 - C566369B893546CBC1370F66325B1CBD3DBDDECCB06EFEAE7FCC0280515559F5 - 935FDDF9AB3D2525E7A255EC0E3DBD3D62D693129284F19DDD9D28A4C4ECC7C6 - 8C6323355C32378A78A1D5060F474545F5474545D6C4C5C79F0C0D0DB35A7B7B - 354DCD4DC96C7C09EB864206AB2C3323B3F417BFFC4533DC6D4C01F0C1FB1F3C - 5C5C5CFCC4C1837FF2AAA8382F440D00C08CC7C5C489C2A6974BDDE6966652F1 - 8CCF9E3D9B6EBCE146AAAEAEA2F3E72BE102676EBAE9A617144A45F7F1CF8FFF - 339AA9D7CEBAF6DD59B3661D429394D9A432D599346FBFF5B67F557555F7DAB5 - 6BBBF506FDA5CAE3AB0B80B5D71AF0EEE1773773D172170C7EE38D3778961B85 - 44C67BCC5474543433C045DD3D5D34C0B31DCC2E71EDAC9F882AB1ABB3536806 - 8E2936DE0F984C260D8B28D9BC79F3F6C425C415707D50C7E9B19CB3441DBB8A - 15D73C72E4880F03E1DCB869A33D2020E06210AE1E001CF4528F1C3EBCDD6EB7 - E56A3401A299B165EB16B2B47790973703D0D3C381CD9722F411C20598D38889 - E2F5D2A54B29392599BABABBA8AFAF9FAC3DBDD4C9604008E1B8969616B14796 - 60306D5AADB6695A6EEEB165F72D5B131E1E66DAB963A7A2BAAA4AF6D8E3EB2E - 6E995F1D00CE9C39F34F1FFDE5A3577D7D7D220F1D3A44164B07F50FF4898686 - 52E1C5D457A073441AF673C85C180D790C46801D6969E3897D9E1C6CA028A038 - 304225221E801152E75800D2DD4336BB8302028368E1A2F9C50F3FFC9B397C8E - F67B97DCAB5C72EF12E79429539C571580A3EF1D7D282F2FFFF1E8C848EFBDFB - F6B21FD7607D80121212841126B349A442748071D3C15AED280032B94C748A7B - 7ABA19086F217E50554203C4C6C68A521800201B203B747676513F33242C3C9C - 8CE9E9AC39E434F7FAEB56DC70C3BCA7BF3CF1A59C63856CE6B53387AF0A006C - 80EF81FD07B6D6D6D4DE83EECDE1C3EF504949A9C8DFA02CD45C9BA55D1867B7 - D90500A121A104C90C60F01EB466FD2798A00DD48A2A108C409A8C8E8E1620E1 - 77A816C10CBB7D98821840B4CBA572392252FFD9E6CD1B67738681E123CAE20A - 03D0D4D814BD7DDBF6DD7CA3B392921239729FA78307DF10378616166E4E5A1F - C00643C18AB0D030A1FC60100C03A5E53239F9F9FB89DC8F5906782884525252 - 04EDE146D87B31438238601AD38D02C892921271DD88088365E7AEED690C7CEB - 256EF5FB07A0A8B068D26BAFBEF63AA7A394969656BEF170D6EAB5C24850B5A8 - A848F8B3541461762D160B718416F4961641F07BA9318AEFB081F2301840800D - 7ECCA66176171F1F35A919502C92A05384365955559588117E6A5FE7CBAFBC38 - 333129E98B2B0EC091778FDC79F8D0E18D757575C1C8E9A02BA42AFC147255EA - 00C3F8D14ACF394C1D1C140D1106CEF9AAD122C94D6937103016511F1523C002 - 2BB014C63A803541041B1D46E9C6740A0D0B15006375498087E33986ACF9DDEA - 658B7FB6F8C52B0600FBB072DBF66DBFFBFCD867ABD38C4621642A2A2AC4CD77 - 777531BDDBC8DA67E51B97896006E1834A0E6B84608190BD6CB054F682099191 - 91D4D6D636BA6CC699843A3A3BF8782FD2EB0C5C04D9983DBE149F104F937272 - 845B15161609A0058B46E434D619391BECDEB8F1D93BAE0800EDEDED219B366F - FDE3F9CACA9B53929349E3A7E674C61BFB29C6F98A4A3A95970711C411DA4A03 - FD0334281A9EEEFA5E21773747008A00865D0200A0629400C0AA11661E8085B3 - 4BC1405FB53FC5C7C7D3848913D8CDC2A8A0A0902ACACF8B0029DA69E822F3B1 - 880F29A9C927DE7CF3E0357C1DE745B7FFDD00282F2BCFDCF487AD2FFBD9ACB9 - B18670B2ABD82755BEA440DB8B8D191AB4513DEBF9C484586A696C26B3A9DEBD - E2CB22C7CAE96AA0DF2A80E9636086871D9CE7DDA52F673F31EB5084727E03F1 - 0377822BD8386304040452744C344D9C982300013BCEB3A40630A3EB8960D348 - B7880596E5C87B87D338905E1C0843BF35002C6C166EDFB9FB95DCE8507D4643 - 15B59C2D25E39C9994A78BA3B25E0745A9B9D61FE8A5E68E6EB2D858DAF6BBFB - FCC8E17E7E6AE1FB2E50B4A34300D5D9D5C972D84A8303FD0290418E1B76D113 - 441F5029D2A39CD982A2289A8BA38939D93CF37A2A3853C099A59C8D1F1C5D37 - 947A8938BF6364696DEDDA350FDEF2AFB7BCC40CF3EC18877C2B0076EED87DDF - E1770E6D08090FF7991D1D4EC98D55A460BAB61B22E81355281902FD28ADCB44 - 75A74F5138DF6C6F5C2A1DADED6661E3A43E2B1A1F2ABE59A63DFB306656C12E - D0DAD62ED82013DD1F6941D4C686D99815288D95423019583F4C98908DF446A7 - 617C6919477BB74B499DE4610030D245827B216DD6D7D701FC73D3A7E77EF2E8 - 8A15EB5887B45C36009C977DB76E79EEC9BF9EFAF4D7FE61323143319111E44F - 6114E8E5478D5DECE3EC65330C8194DAD7448A965AEAF7F1A72F65A154452A76 - 11339536549292293E292D894C6715E492F9927CA88F382F507BDFA060035C40 - EA09628F3259C96EA5D7EB84F18608CC7C11950AE3072F584116C78C000070BD - B9CEE86756595874B5715AC66F76EFDEBDE4E7B7FFFCB5CB7281FAFAFA88679E - 5AFFC7D696869FC6E57851514B2959070768667A1AF5D7710A5386D2B0AC857A - 07BA48ABD65094269C111BA6762E5CCE5B06B8E0B19336A1971A069901321769 - 0682C96689A0047687487339595ADB2922369A4AD421F469451D29C82D9220DB - A0FDE1EBD913B2C860D071B42FA6D212B7F152E7D8F3010AEC11447D38A09A1B - 4C42318E144A22083FBB71C3EABBEFBEFB896F0C00FB59CE9AD56B5E6A696E9D - 3C79CA44F20DE9259B5737073BCE02B52E9EFD78D2EA07C9642FA573D566CACD - 8EA7005B1C59DBC2C8EEECE2A8DF4676A6B28F8F8AB5A882DFDBA8ADD941722F - 352D8E09A294CA52EA31379226399E0EC8FCA96C5046410C0CA2BF7C641D6002 - 1B1F1169A0C282223A77AE74D478CF45130900B5DA4FA44B93B97674995D4868 - FE6D7656E6D9EDDBB72FE2345BFD8D006061B3E0F9E75ED8C1371282A52BBDDE - 400A414939A97DBD69D825E79CEE4B6A6D2F39FD2D6493338D3B5C34D81CCA2A - CC8F9C41662A305793BF9F17658F4BA28E8A403EDE9F025D7DA41876B7F07D38 - 038429896A7AFAA8D2A1A2F15C3B984D26D11182E0C9CECEA4C8E8482A2E3CCB - 2AEFDCE88C7ACE3A06F61A4EA11048B575B51C108784B4F6E5F48999CFCCCE2A - DEB56BC702B6A1EEEF6701E61E2375F78EED3BD673DA090E0A0A142206BE2817 - 6B8F6E55C7DE3A5A5DA854729E6525CF869C733E5F58C36245D74E168785A33E - 0BA67A5FF2B2479351ADA098F66A1AE8B050045785653EA1F476593D69183023 - D7FD90BC260600B2163E1F1D13C50AAF98CE9D2D11C67B2E984A6D736C087610 - 5426531DC727DB081BD4420D721D726EF79EDDF30D0643AD87955FAF030EBF73 - F8AE279F78F2153EA912911485147232F233E804FDCDF5BD0832022F178D0423 - 690183C4C28582B587028FEEB1DF5BDA988E0E17FD4B948612384BF80F769245 - 1940EF0FFA51A9C39B92B97002EDEBCD6691F3B398F6E3C645D3D9E2B3545C7C - 4EE801CF4553C970188A3A0303C0391C765C9AFC35FED4C62A34DD682CDEBD77 - CF02835E5F7791999706802FE2BFE49E25F92C2E9259E9D100073B342B908B47 - D7F9C5D29652F4F1509C88A00335A7F61592177F0EBB8306850FBA7BFC2CE1C5 - EFB55E0A0A241B05CB1CD431E4A06A363E44AFA7D6A666045B0A64AD8080372E - 3686835DA9A03E84130CF37C68C20DBC4B300520C078B176C0E8070469A891C1 - 4C4B1B5FBC67CF1EA6FDDF187F6900CC6673F2334F3DF3DAE9D3A76762D1014F - 6041B448E908C602844056631035FE7EFEC27091CF47567CC018374BDCD2169F - E146A5A035C4C0D8F97C03C2979D94CA121A52B7818DD770D5979595211E782A - 2B2917FA1EA5318CB7E3385682F071F783922EAE0B7442F79B98356E692DA790 - E0208E017594323EB578FFFE7D3FE53862FE9A10270008E517ED7877F4BDA3F3 - 5F79F9951718841818696EA8A7162E3F316B9E6B73D22C6028990578522B4013 - 20000130124BA4AA0EBF517A292F783E00E7828E8066B7D91CD4CD85136A808C - CC7496CEF154CED2B690F57D4FCFC80228723C5F339A75472333C5C6C76281C4 - C60CC3DA0018E670DAF9FA01D4C2339F929A5ABC77DFDE057AC32567FE020042 - D8988ECD9B36AF7C7DCFEB2B38C8F881CE30A2ACA24C94B252F92AEA770E842E - 69B98A68341A4B01092CC1CC8319084AA2CE1F618978308AE308CA598929D8E0 - DBDE1CA9D38C6994989C40D55535ACEF0B447F4F620DF6B11C0FE2E262E9AF27 - F3C5B92197516B60383893201EB5B5B6A01D56B86FEFDE053ABDCE4CFFF71000 - 0472D455DF38EFC61A36DA5B4A2BB8281E5B931E6985C1A8D7BD1442A288FF36 - F07CD6CF73FD6E1414BE29C402188994A809D05050A09B256083B43638CC8133 - 8BA37D567616D5D6D4B2F185D4D5D9258E7757770E5150C1354EB2F11C4D38CB - F45103670B5C1FEEA1F2F6A2D666186F2CDEC7B4E7C2E7EF19FF15004D8D4D7E - 93A74C36A9942A055A5500008B95164E539E03F4C54CCAE55FFD73892700E8B8 - 31E9DD8D3799FB33113F1C5F0102AA4A9D20B88D8175055812C1F5FFF8B4F14C - FB62F1D8AB98F991670273A74F65E002282FAF805D49258AA566369E46DC11F7 - D5DCDC40E9191985FB98F6DFD0F8AF62006F96FDFBF62F3B74E8D06206C388BE - BAB5CFDACDC28703BACCC5741DC69E33421447DA28AE09E4122030447AFA5300 - F215349E6F4687E7834F985D1C9B9591254050B3AB208D4A353DCE9995696425 - 184A79F9057C3D959879F405C14B9C03B46F68AC479EBF5CE34701F0CC020A46 - DFC0AED0EDA5F21A90140E8CC7CD707CF0E7AC909E979797CBDB848282829CCA - CACAF8EEEE6E2F111047A2BF686B21E77DCDC35A12583012C24617AE130F47DA - D978C402E969B07096C053A7E6B0CF9F1631096D310BA7652980C27873BD995D - 23F3CCEB7BF72CE2F35C8EF19704E0B206CF946F4545451A8331F5C48913D30A - 0B0BB31890644B47870F8D640CE93980AF7B744D2C80706035A61947AA3E1547 - 7BA74871D0135A2DC78C202D07B756D13BC09C2023C078AC29188D6945070EEC - 9F7F9933FFFD0070F1604AAA6A6B6B13199029A74E9D9ACA0CC92E292931B6B5 - B569A4B4E9F960C4C83182092949C9A28891C914A3A9169D5EF4FF9B39ADE109 - 32B796B08B42A796E56E464646D108EDBFE983915716808B079F5BC1054D4C7E - 7EFE140663228332A9A8A86802FBB0167E8E219E08E3E0181511457A76058E91 - 0C8052C8D8C4C44432D5D6B146E816B1C1E1B0899947A648CB3016EDDFBF7FA1 - 5EAFABFB0EB778F597C7990D11881DC5C5C559701B569C13ABABAB0D620D3035 - 4D640C9D3E42B4C9F937ACE8AA455CD185E9C4E269554D3565A4A71771AA83BC - FDB633FFC30170F1E8E9E90979FFFDF7E73FBE6EDDCAD696B664A4454E6742CA - 82F6434303E277288EA04B8CC674F8FCC2AFD1F6FFFF0090467B5B9B61DD638F - 6F3A70E0C06D580683DEE8EFEF631DE172477B96BB995999856EDA7FE7991F7B - 00607040F479F2F12736EEDF7FE04E9E7D753B17488E61BBE8065F7FFDF52777 - EDDAF96FDF21E08D7D00A4C1622B85E3C414CE28B17C7F4E0E861573E7CE3DCA - 19C4FA3D5F6A6C02701547D83FFE79FAC7FEEFF3FF0B43E930A009E1CAF90000 - 000049454E44AE426082} - Visible = False + TabOrder = 1 + object pnlFSX: TPanel + AlignWithMargins = True + Left = 233 + Top = 3 + Width = 224 + Height = 40 + Align = alRight + BevelOuter = bvNone + TabOrder = 0 + object imgFSXStateNotConnected: TImage + Left = 4 + Top = 4 + Width = 32 + Height = 32 + AutoSize = True + Picture.Data = { + 0954506E67496D61676589504E470D0A1A0A0000000D49484452000000200000 + 00200806000000737A7AF40000001974455874536F6674776172650041646F62 + 6520496D616765526561647971C9653C000005774944415478DAC597C74B6C59 + 10C64F9B737A9815DA8839A388115C28032266915988CE6660FE9199EDB8706F + C4D1850CA2A8288A2898338AA298033EF3336B4F7DA5E77A5B6DB5DF660E1CEE + EDDBB74FFD4ED55755A7353A9D4EFC9F4303008D46A33C282E2E76373535B536 + 333313262626FC9DFC5EFDDE47436E0A57CCC7C747F1F0F08079DEDCDCFC5DFD + 9E02505A5AEA78727252ECECECEC636969C9CF2480BC1A33A461F5F5EAEA4A77 + 7676B6E2E2E2D2DAD0D070AD07909D9DFD5B4040808F56AB153E3E3EC2C2C242 + D9BDDA0B3FE301CCDBDB5BB1B5B525565757C5FAFAFA7C4747C73F0A40414181 + 3BEDFAF798981891989868D0A8B121C0FBD8C8DDDD9DB8BFBFE7E7232323988F + E6E6E67F3535355D33406161A1D6DEDEBE22232343F8FAFAEAC55E6DD41800D2 + 91080B0B131452717979294647471962737353747777E39DEAC6C6C6430620E1 + 31406666A6F0F0F07803606C0820E0F0F070E1E4E4A400F5F7F7731876777745 + 5757171E57D7D7D73F0194949430404A4A8AF0F2F262B7595959899B9B1B1690 + 3100F86D545494A0F594671473B1B4B4C46B6D6F6F8BDEDE5EAC575D5757F704 + 4019C000494949C2DFDF5F242424080707074E9DF3F37341CAE5EBE9E929EFC2 + 50ED00747474B4B0B5B5559EEDEDED89E9E969FE0DD6DBD9D9110303031C0205 + A0ACAC4C4B062BE2E3E35984212121BCD8EB5D630710D4F1F131C715D78B8B0B + 7E6663632362636385B5B5B5F23EA5B5127B590B00303C3C8CCFD5B5B5B52F00 + 8E8E8E15701D42400A156E6E6E9C8E521386C4F69CDF1C77D40F39E0B5B1B131 + 0E230C030020D000A0E8D95B808888083688C5A062188627BCBDBD393BE0DAAF + 68E1F0F0504C4D4D291A820D18070800262626DE07080D0D6500189710EA4A48 + 158C61E0194320070707627C7C9C8DBD2AC30C2135F1068052A622282848B8BB + BB2BC6E57C5D8AE10D84EB3D0800A0D8C0B09CD238E6FEFEBE989B9BC3BDBE08 + 01E0E7E7A700A821A406A4417C4E4F4F17E4B53700D003F25C6D5CC61F6205C0 + E2E2E2FB00308E459182883DC408636A0FE01E9902B11A126667672767897AF7 + 00432A2373500DDF0580F26118C5048AC60484DA1B308E8605202CBEBCBC2C82 + 8383F53265707050ACADADB161D40D881113F7D7D7D75F07901E9061409141AE + C338DC89F28A0E87D0A5A5A529109393932C44E901BCFB2900B2002130040081 + C2088C63319453C4530E9471BC8381546B6F6FFF10809EBDF482CF0002030305 + 1A15EE115B7433C4523D90BE3939397C0F4394624AFABD06D8D8D8600FE801A0 + 1463113A98700B45DC2500F21E5E4075EBE9E9E1EB7B83CE152C60ACD9DADA2A + 8E8E8EBE0680662401D090D013E00DB5B0D088A4BA0D0DFC0EF501030DE739DD + 18408A510D40C7B297760C0018453341083C3D3D051DD178413B3B3B313F3FCF + 4DE4A381142E2A2A629D2C2C2C30C47B1E407B46257C73208107A406301182E4 + E4646ECFB3B3B39F0260E4E5E5B1F720508441ED017508004047B243BD23995A + 84302EBB627979B9989999114343439F024446468AD4D454365C5353C3460120 + 21641600808EE84F00F9F9F90CE0EAEAAAE701D9942A2B2BF9348B02F3D9C0EF + ABAAAAB8E1F4F5F5293D00531624D40E00B4B4B43C01E4E6E6B206D0EDD4BB97 + 15302E2E8EC5B7B2B2F22980AC0908973C0B604A4FE08A8645DFFDDDD6D6F69D + 01B2B2B2BE11C01FD4EF35EA562CA7B187520CD98AD5FD40C250463D5028FEA4 + 7A72CB00546235B4E35F290401A09667007523FA993F261240FDEF08058E0EA6 + 13B4C97FE960A263003A649850A7FA4622FC853CA125A38A3575173466C0A01A + E6F9D9239D1397E850D249E23EA386F50440954F432EB1A47B7BD2803DD1E164 + 699CCFBFE0188AFF0F0AC5396DE69CDAF3ADDE7F432A4026746F8ACD1A1B6F23 + 43F340000F246A1D3EFF0712759FFDF03F71A80000000049454E44AE426082} + end + object imgFSXStateConnected: TImage + Left = 4 + Top = 4 + Width = 32 + Height = 32 + AutoSize = True + Picture.Data = { + 0954506E67496D61676589504E470D0A1A0A0000000D49484452000000200000 + 00200806000000737A7AF4000000097048597300000EC400000EC401952B0E1B + 000008124944415478DAC5977B7054D51DC7BFF7B177DF9BCDE649221188840D + 2492A243487C2C3E68476D29D599A2A53E3AAD5275A60FFFE91FDAD67F9CB68E + 33AD558A05E91F75C4222DA2B49476B4DD100832986002C612A76D90B0E4BDBB + D9DDECEE7DF677CEEE86242E26FCD53373F7DEB97BEEF97DCEF7F738E708F83F + 3761FE8B8AF61DCD922405645986284AD4837511F37761EE975691D1F83BFAB1 + ACFCDD84699A300C9D2EF3D2E8F1C7078A0254DDBC73693A36F2A6ECAFDB20D8 + 7D648FFDC500A4FC5D2CC67B8596330C187437F8DD2220331DB5AC44E4AFAE40 + CDB648C7A3B13900BEA6674F3856843634D59761ED32179C6E0982457F8B22D9 + 16721D8BCDBA88A656BE8F65E4402CC1443A65A277701A670646A00F75BD19ED + FDE9D61980D2F52F360B8AB7AFF9861B70FFCD15106DB9893315662BCFC558CC + FCF5DC37C40D9743443A6342CD92021AF087E363E8EDEA3414C5563E7A6C7B2C + 07D0FA7248F45487BFFAC5B568A1D98B9200E67E360087B06685C16200A8BF4C + 1F6F5AEF476DB9827852C7BEF7C691552DF49E4FE1E0A11390856C70F4E863E7 + F890810D3B09A02AFC350268AC7390EA050001DCF382B068EFB3E650047CA9B5 + 04D565F63C90855D6F8F209535F1F1A7691CF8731714510B0E87BF9D0768FB6D + 48745785EFDAD88CE6650E2E9BCF2B2291A6E8B5989A020B8585FD4FCD6517F1 + E5363FCA4B6C33EF7A0692F86777028669E1CC600687FFCE007402F856016037 + 77C11DB7AC41FB6A27B6DE164065A90D9A6E612CA66124AA632CAE2132AE619A + 6434CDE2243E9784CDED2528F55E367E8E667CA82B46C6019DBEEBFB4F16EFBE + F73E6C821A1CE978A400F02A01D4846F696BC44377FA70FB3A1FBC349830CFE9 + CC7046353134AA6272CAC0D0988AF1B8C1DFF93D12EE0DF9E173CB33FD2F4DA8 + 78E31F516435529226A3190C4045E7D15390580C743C9403286DDB13927CB5E1 + D61B1BD07CAD02874D44439D82B5F52EACBAC601492A1E013CB7498CA9940E85 + BE713B2EA7C96854C5BE700C094A3F9DFAE99A059520CE7EAAE2FD63DD145B99 + E058C7837980F6DF85244F6D78DD1756A2E95A1B6CB2489700990C7B9D228138 + D052EF44C0277F469562EDBF97B278AB33CE83CE20E306D5225537B94BCF0EAA + E839D9531CA0B979255613804286D9251388C4B281152232BCAC4AC6F52B1C04 + E4BC22C8274359ECEB8843D7993ACCB8C59F5523A740FF7915677A7A28B5B3C1 + F18E6FCE06B8261C0CD623B89400949C028A4DE0F92C3100F1722AAE5B69C7E6 + 365F51884F2E66F1DABB711E2F2CEA59E06979F9558A958F2F6838D7779AFC97 + 094ECC075876DD0A342E5560B793719A3D73814D2A005CAEDB32A9F2E4E600AA + 03B6CF004CA50CFCEC8D711EF50C4033732AB040CCE60106FB3FA4882680A3F3 + 00BC55CB515B21A3DA2FA1C42DC24E81C514281426BE3CD1F38377F8D0B4DC71 + C5C07C6EEF3826E239FFEBCC38F93F963431126399A32139D4474B442638391F + C053B91C5E5A845831712B123C2E114E52C34EAEB0B18B201EBED38BD6602E06 + D8E01D7D296CBCDE3D27535EF94B0C5D1F65C8B085695A079254D05234FB74C6 + E00AA52E2E02C045000EBACBF96C60F7FBDA5DF8FAAD1E6E9C49FAAB8371F4FC + 5BC54D8D763CF99512AE0E6BFB3B93D81B4E72E935EAC7EAC422006AC39E8A15 + F07A8A03DCBED6C18D30E3A9B481170EC4D07F419F99F5E3777B4909177FFEE8 + 7C16CFFC3EFA3900BDB4541340E74C1A5221721340657D518050B3033FBCD7CF + 17A768C2C0CFF74731386ACCF13DAB1F3F7E20C09FD394FFDB9E1FE52E2A0A30 + F42129909D05C02A21956277E57578FA1B65A8ABB471BFB320B451656DA142C4 + 54189ED4F18B3FC61099348A06E0AFB797A1AA54E681F8BD9DE3383FA253F199 + 0540CA4D4D13C085D3D48701144AF186DD548A6BC2AE8A9578645309EEBFCD47 + 25D8CEA3BFD08627353CFB7A0C1309F38A1570DB4637B6B47BF8F3CBEFC4F0B7 + EE3434329CD17200D304902080E9A16E52400D4E1E7BB800B02B2479ABC38EF2 + 551484325F8ED7D4D9716B93135B6EF2A08C4AF0E15329EC3E92F8DC125C1B90 + F0D213E53C4E8E7C90C24B6FC7732EA04294CA92617241825C908E7C4031A006 + A3C7F3CBB1BFF595904C1B12A5BC91528F0148947E221C548C1EBDDB87074891 + 432793D87D38B1E0BEF485EF04D040EA9DBB90C5F77F33CE6B000348AB24FD34 + 2991D6908D9C2205B460B42BBF21F1AFDF41315019B60556C3E950E0A6FCB753 + 39E6AB62AD823D4F55E160570ABB0EC7B150DBD2EEC6F67BFCDCF7773D13E101 + 99290422D5836C3A03F51201587A307A22BF25F3DFF822015485E5400364C53D + A3002B3EAC24EF7F7A098EF7A7B1F3D0C2003E82FFD34F96E0C0B1247EF9568C + AF822A03C8B2A24467032D0575F8347741ECE41339005FCBF321D9571D964B96 + 43905D906C0E329E5F9629FAB76EF4F2F40B9FC92C08C0DA77EFF161CF91295E + 01355A14748D4168B42BCED08E39096D7C809E93ABE2DD4F0D70006FE38F1A24 + F7927F89AE0A41A03DB9202AFC62FB7341620B4EFE847435DBE2FCC1C4A21311 + 4C95CD985E6BFC6E242EAAA63A5596E87F2EC94774D6DC2742741DB1F91B3699 + 6A32B7CC8A32372C08855391781507A3C2B18C00D8E9888E6630753AA0581015 + 0FD491D3AF0A8AE3B1F4D05E8B0F69F3B448E6F4708354B26687E45C12A2A54F + E40315CE857C672C2ED27A9E814E43B95DB48999EDB4A9EB4672E81D3D71F607 + B26FD54535DE65720049AE132C7DCA473D6A04D1552B88F6001B220731F3931F + 7901CBF3552A9CD30416F89951CBCA4428D022A635999CD35D14A88A5826399E + 0E2D10AFE61CB27855F81656D0C8A5AA694DF092FA3F8CC8125D22B18C700000 + 000049454E44AE426082} + Visible = False + end + object lblFSX: TLabel + Left = 42 + Top = 4 + Width = 146 + Height = 19 + Caption = 'Flight Simulator X' + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -16 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + end + object lblFSXState: TLabel + Left = 42 + Top = 23 + Width = 70 + Height = 13 + Caption = 'Not connected' + end end - object imgStateNotFound: TImage - Left = 0 - Top = 0 - Width = 64 - Height = 64 - AutoSize = True - Picture.Data = { - 0954506E67496D61676589504E470D0A1A0A0000000D49484452000000400000 - 00400806000000AA6971DE0000001974455874536F6674776172650041646F62 - 6520496D616765526561647971C9653C000010B74944415478DAED9B096C5755 - 16C65F77282D5BA150CA2A4B45A050948285418442214E6140C74463C718B463 - C448C6688C6B8C44258A88041D9851B4A22D33A962D906991123B22450108A0A - B21611690B48D94A59DAFF7CBF1B4EE759CB48070AFF89DEE4E5F5FFD6FB7DE7 - 9CEF9C7BEF6B48201068E9795EA5F7CB6C8D424440A35F3A01D1FAA3E26AF7E4 - 2AB5E85F09B80A04849D387122E6ECD9B35167CE9C89ACAAAA0AD3DFE1DA42D9 - 4E9D3A15D6A44993334949493B2B2B2B1B979494C4E93ACE855457577B6CB4D0 - D0D040585858757878785544444475A3468D4EB76EDDFA987E57072D01656565 - F1F9F9F9637FF8E187F873E7CE85024CFB90D3A74F8708AC575151E1959797B3 - AF7EF0C107576CDDBAB5F7A64D9BE28F1D3B16A2CD3B79F2A4A77B3CDDE38938 - C808889840AB56AD02C9C9C981112346ECCCCACACA1731554147C0C183075B3E - F7DC737FF8F6DB6F9B474545D51CC7A200523F1CC003070E3812060E1CE88E1D - 3F7EDC93C778F20C4F44B96B05DA6D1CBBE69A6BBCF4F4742F3E3EDEFD9E3871 - E29C84848403414500AEFED8638F65AD5EBDBA83DCDB010B0909717B08602FAB - 79478F1EF576EFDEEDB569D3C64B4C4C7484009A0DC0E7ADEE363CA177EFDE5E - 9F3E7D9CE7C8ABBCCE9D3B7B77DC71C7F21B6EB8614D501150505030F089279E - 1803788002DEDF14CFEED89E3D7B9CC53B75EA5403128B9B97D8DF1CEFD8B1A3 - B3BCC2C46DFC0D19CD9A353B7CEBADB7E6C4C6C61E0B0A02D4E1D0C993274FDC - B0614322041858DB689082EB6FDBB6CD6BD1A285D7A3470F67752CCEE6777B7E - 43C0B5D75EEBC9D29EC4CF7BEBADB7BCDB6FBFDD8583EE0BC80B662B244A8382 - 008957CC030F3C304971DD58CD1100609ADF13D6AF5FEFEDDAB5CBB975F7EEDD - 7F4480818708C073ECFAEBAF77DAC0F1152B567813264C70BA317CF8F07FE8FE - 757A64202808D8BF7F7FFC430F3D94ADCE87237E4A539ED2564D28B007D0679F - 7DE61517173BAB1A01E6F6FE8D78E7FEC183077B4A91EE1E09AB23A457AF5E5E - 7676764EBB76EDF65C64F71A9E80A2A2A2AE8F3EFA68169D8600F690C0863744 - 46463AB09F7EFA2964392BE2CAA4457F085818709CFBAEBBEE3A0798B0C1B310 - 523CE2E1871F9EA7F0D81534047CFEF9E7BD2480BF8F8E8E7660CDFA46047B08 - C003D081B4B434AF43870EEE985FFC006F02C87D2A7A1C015F7EF9A5234959C6 - FBFEFBEF2B74FF1C6591A34143C0F2E5CB539E7CF2C9713131310EACDF03EC6F - 40AD5AB5CA3B7CF8B023809CCE313F0156FC404CDBB66D3D153FDEDEBD7B01ED - DD7DF7DDDED8B163BDA64D9BEED2B979B5B3CC5525E0C30F3F4C7BF6D9674741 - 8079803F04F88D5BAF5BB7CE81239695CA6A14DF0FDEB6B8B838E701CA2C4E13 - 66CE9CE90813F8B21B6FBCF12F7AEDB9A021E09D77DE19FEE28B2F0E555EAEF1 - 000B03DB00B17DFB765701A6A6A6BA9836D5C70B8C0CE21CD22080F457585848 - 85E98D1A35CA6BDEBC7940E97671BF7EFD36D4A37B0D4FC01B6FBC31F2A5975E - 1A8C55FDB1CFE627800A1011C30338E72F7CFC1EC0F5585F80BD6FBEF9C66580 - AE5DBB7A999999C7146AB344D099A02260C68C19635E7EF9E58178801FB89F00 - 6A783C0080B2A04B8FB5815B19CC39A5391726548D268C7DFBF62D79F5D557FF - 2A022E7620746508983E7DFA98A953A73A02008B0BFB35806368006530E07AF6 - ECE95CDD5FF71B01160294CA6C5C8F37AC59B3C64B4949D93E6DDAB4DC7A76AF - E10998356B56BA52D490962D5B3AD056FC181946006ACE39DCD90649B6F97FF3 - 0C3C80E7A103104036D050788386D08B828E0059A7DBA449937EA72227065707 - 302267552124E0C2A5A5A5EE6F0639343F78DBF002EED170D76900CFE15EB6FB - EFBFFF9FE3C68D5B1D7404D0246E4D366FDEDC4EF57E476DEDB66CD992F0DD77 - DF4503182B028E1A8086C29BA758E3BC91C239EA000A2B065710B673E74EEFA9 - A79EFADB902143B6062501B5DB9123476245429BC71F7F7CB472792B2C69951F - 96B522C8C412CBD3AC7862BE801060DBB76F9FA71AE3C4071F7CF067DD532D61 - 0CD7F32A75EE6CD0124093D53ADF73CF3DE3BFFEFAEB661448283A56EFD6AD9B - 034F4DC0D800EB2378145294CAE88586CCD5EDDBB73FABADBC4B972EFBA50367 - 747FA4CEC709FC41D50DA522EB507272F2C1ACACAC13FE19A8A020E0E38F3F1E - 2C0F189E9F9F1F46FA230C98F3C3E280A5511330DAC3E237DF7CB3377AF46857 - 2BECD8B1831038307EFCF8F5BABE72F5EAD5DD994CBDE9A69BB60F1B366C1B93 - A4F2A6300963D4471F7D14A92176A52AD1D3D28DBA86C757960059296AF1E2C5 - A3A5F829585C6EEBAC8C0740009662208407303D76DEDA9EC07983060D22745C - CD2072AAB43FAB22288A224A9560914690251A1F948BC0C3CA12E522CE15444B - 962C095FB0604140E9B85A6385DA245C3902247AAD047E9CACD5819A000BBFF6 - DA6B4EFC8C00840D858700CE5BFA93C2BB592248619E10F220833DF79141D8A3 - 157A5695483BAE31C15E659F151A581D7DFBEDB743F19E2953A6D49E32BF3204 - 7CF1C517DD3FF9E4934CC567D3828202071ACBE1E25611020062C8EB80E63C1E - 01398CFD15EFAE32B401147FE3217884CD1CB38724CE51296A8458FAC8238FE4 - E81915F7DD775F6876767640630DBF17343C014B972E4D93D20F4F4C4C0CCFCD - CD75715C5656E60A1E3A4A2D4F16C0B2741A973702104500410E449022114332 - 0533C0144F1040368014BC82FB184E33B50659E9E9E9FFCAC8C858B576EDDA10 - 1657860E1DEAF7828623401D89C8CBCB1B535C5CDC1F0B2E5AB4C893E23B37C7 - 65A9E60E1D3AE4C091F62080313E5E0031FCE61C618027001A022082EBD10AC0 - 721DA345C0721F0402DE86CB7ACF5EC57F8EC80438C545C37B802CD36CEEDCB9 - E3D5D1CECCEFA1DC527CD731A6B0E89CAD0FB00114AF80007E03C8621D2FC0EA - E47EAC0C79784A5252524DA6600F3180679688E74036EF95A6546848FEBA883F - 5947572F3F01547C6FBEF9E604C5762B3A4BC799B80424AE5A5454E4E2D90645 - 58174D90423BA0B608C2F53631CA39365CDE5223DE8037112E841084B22E0078 - A6C998614623742E307BF6ECB932C4BE0627402ADF6FE1C28519CAC18D1136AC - 42A94A9C52AEDA0C30E0FD233D5675507FAEB741129B110158E29B112364718E - 38E75AEEB3F941F6108CCE189168C8D34F3FBDE4CE3BEF5CDF6004B0C829971F - B672E5CAA1740437A6C0A1F37400F7C64D6D1698C207CBB3C70B20827336ECC5 - 13581A63B6C796CD94491C51DCCF58001DE07E6690994431F0100D782BA7A928 - 33333337BFF2CA2B0B1A84000959F48C193332156F3D894B5C1100B82C8D3864 - EA8A7826AE112E5BDC30709000182386FB716B23801218CB738E900220EF017C - 4A4A8AF306859E239D67DB428A4D95AB5FFB5474CDD57B2E6F2124416BA362E6 - B77A46072CE69FF0A0B374944A8F8E12BB0C5C6CC5D714DC88F14F7AD89C8179 - 06E72D0D021011240B6079C0E31D106D96F72D9FBBDF22B34215E1EB22EFE465 - 2340854D524E4E4EA62AB41844C9567588775CDE4675580E006C36DA33F162C3 - ADE938318EB58C189E63738290417A840CF6E84AFFFEFD9D376CDAB4C965165B - 4AF32FA89AC6E00DCF3CF3CCB2DB6EBBAD50863977C90428ADA44AEC4629EEC2 - 496D58CC263B008FDA633152119E41A751661A40F1101A2ECF75FCC6DDFDE5AF - AD035A2C5B98503F306FC81ECB031EB5B7D9639B4CB5E7701F1E43C125F2CBD2 - D2D28A350C5FA9FB4FD49B00B96BC4CC993347AC59B3661031C88B0048CC0292 - C206C6C9FD8400220839581931C4BA780AC7D00BAEB785118E59EAE36F7361F6 - FCB67900621EE537CBDB129A7FF5C808805C368CC3BB300ED7BCFBEEBB0BEFBA - EBAE8DF522408399D8A953A7662AB7F7807D80F072549F0E40080089692B5CE8 - 0416A71E0004D7A0CA3400E1D25C07017490FA002F40CD69361304495C87E5C9 - 009CE79B00DE6FF1EEFF8082BD4DBBA13BF4EBFC40C991316DDAB41513274E5C - 79D104C8D5DA2997DE22208903060CA8C9D1EC01640B1500658292995D3C0200 - 741252B02C1DB23940EEA3433C8FEB2000902839BA611320B60E606E8FDA135A - 06DEBF686204D83A247D316DE037D7F6EDDBB74C293B4F9E7BE4A20850AC27CD - 9A356B9C3A12CD4B713FAC89556C5516F0FE3C0EE374048BD3013A62391B0B43 - 04C0CCC2DC4BA771514285B103314B0641E5D569176A5BB66CF1BEFAEAAB1A8B - FAAD4E630F713CDBC0738C7763793DA754FA95270CE53F2B82744E4CF5D7587A - A4727A63D4DBD6F6ECEB0E63DE1AA4700DE778B9A54513373A0129589A8E11F3 - CC000188B5413ACF971F94BC1040598BE549797EF0FE0553FFCC3162C7FB006F - 7A00B1902AB12E9B376F5EAE0FFC7F2740964F79E1851732F5D050536D9BC707 - 24D6B7C2C508AB9DCBFD9FC1D06CD2028B5A594C27E9305A8178E2F6C42D6105 - 78521EE0C92280F72F9A1A709E41E6A1411CD7D8208AEC229D2A15F8BC5AE02F - 4C801E1079EFBDF7FE51C5451CB189E58869368B2FFFD2165E6184B037E074C4 - DCD096B5B89E67D80713E47C2C8457502C496C5DAD60E0113B08E03AFF52197F - 1BF1780ACF07BC8D35A8442153E1E4C04B3C6B83AF9B00B11F27B51FBB71E3C6 - 4E749E0ED44E4700E0057414962D9F1B21160AE62116EFFE8F9DF81B62792E29 - 11E1033CCF4D4E4EF6BA74E9E2D21C8A6F637EAB0D6CAA9C675A16B12A13F00C - 8BF12A8553695E5E5E2ED3621790B81F13B074E9D21E73E6CCB9450F6B06483A - 8455CC5DED0566059AADF4E01D10421C9A97D8A8CEBCC4FF7D406D42080FC864 - 488B5852DA92EB6D01D45C9F4C409FB897948891006F4632CB03FEFDF7DFAFCB - ED7F4A806E3C357DFAF4DFBCF7DE7B43146791000204CBCF58C986AF66598B3D - 9AA9B109125E82E5113BC8B0713EC7ECC32823C51649B130E7517FD60518F652 - E5418A91C49E90C033F8A28C67D33732058DF3781BA9588257027811F4739FCA - 38021A497523323232260B74B809132F6568E9CF0A663DFBED1739FFFA9D9F14 - 4B991636749CCD74C4A6B2A8F010470A2C2C8F285A6DCFB3F00ACE932D7826A4 - D9822AE7791EE0113CDCFE22BF13FA0F01A9A9A97F12C0502C07013C8C12D6DF - 20C072BE353F01F6DB7FAC36211CB3D12261435D0131E4790A28629EF7FABF09 - 644D80F0628E8F3E18780B478EF15BE153929B9B9B57EF8FA4B49DD28D030A0A - 0AFA287E5A4B4494BA4F54F215F779E5AE66AF8CD0544ADB5471196284587557 - D78749751DF37FF80440EE45F42001F2FD56E77E34C12638B132E0318EB93DC7 - 0803DCBE9EE07F2A826AA1623F46EE7A5AE06A1617CDAAE883B2427C6161617B - 6D0972D50485490BA531F7E9A76984C5FA85BED6B2E380B4696C6A0000E161F6 - 3518C0F96688982784080BD2B20928EF42A845E001E9D7FC7A82AF93807A3559 - 2A42B57B6B9191B876EDDAF6AAD3DB8A9038A534271458D7BE03B81019100010 - 06559666ADBAE337AE4F5AA34CB690B49847FDC9F3F3E7CFBFD898BFBC04D46E - B24A9844AC2584C86AEDE5216D357069AD6A2CCAD2A6FFC388F3F738402C7D61 - 654B9D36C2A40CB6FF21B029731BE8283C4AE5F6FF2BF8CB4F40EDA667872A3E - 9BF1A538E1C2C71112B9B68AE1C6C439CDBE0843046D086D3340A4449B5237A2 - 209034A99847ED2F54E105070175357943ACC8E02B9136848D2ACE84DDBB77C7 - 6255B20040C90C143C94C080E51C3A818730E57D1E7CEE45E4F9E023A0769375 - A3972D5BD663CA9429BF519CC751DDA1FCB8386ECF109CC6E088C50EF2BC62FE - 522D1F3C0458C333444286C0F52614C80636AF6082C7B780E7DDFE522D1F7C04 - D0F89F82E79F7F3E4318FBC9FA110C906C497CE4C891FB737272FE7E098217FC - 045853B1D54A3A9128016CAEFE05248687D3D3D3774800EBF319ECFF2F0157B0 - 45FFFACFD3BFF47F9FFF371E3422DF2CEB57F80000000049454E44AE426082} - end - object lblG940Throttle: TLabel - Left = 79 - Top = 8 - Width = 281 - Height = 25 - Anchors = [akLeft, akTop, akRight] - AutoSize = False - Caption = 'G940 Throttle:' - Font.Charset = DEFAULT_CHARSET - Font.Color = clWindowText - Font.Height = -21 - Font.Name = 'Tahoma' - Font.Style = [] - ParentFont = False - ExplicitWidth = 401 - end - object lblG940ThrottleState: TLabel - Left = 79 - Top = 35 - Width = 281 - Height = 16 - Anchors = [akLeft, akTop, akRight] - AutoSize = False - Caption = 'Searching...' - Font.Charset = DEFAULT_CHARSET - Font.Color = clWindowText - Font.Height = -13 - Font.Name = 'Tahoma' - Font.Style = [] - ParentFont = False - ExplicitWidth = 401 + object pnlG940: TPanel + AlignWithMargins = True + Left = 3 + Top = 3 + Width = 224 + Height = 40 + Align = alClient + BevelOuter = bvNone + TabOrder = 1 + object imgStateFound: TImage + Left = 4 + Top = 4 + Width = 32 + Height = 32 + AutoSize = True + Picture.Data = { + 0954506E67496D61676589504E470D0A1A0A0000000D49484452000000200000 + 00200806000000737A7AF4000000097048597300000B1300000B1301009A9C18 + 00000A4F6943435050686F746F73686F70204943432070726F66696C65000078 + DA9D53675453E9163DF7DEF4424B8880944B6F5215082052428B801491262A21 + 09104A8821A1D91551C1114545041BC8A088038E8E808C15512C0C8A0AD807E4 + 21A28E83A3888ACAFBE17BA36BD6BCF7E6CDFEB5D73EE7ACF39DB3CF07C0080C + 9648335135800CA9421E11E083C7C4C6E1E42E40810A2470001008B3642173FD + 230100F87E3C3C2B22C007BE000178D30B0800C04D9BC0301C87FF0FEA42995C + 01808401C07491384B08801400407A8E42A600404601809D98265300A0040060 + CB6362E300502D0060277FE6D300809DF8997B01005B94211501A09100201365 + 884400683B00ACCF568A450058300014664BC43900D82D00304957664800B0B7 + 00C0CE100BB200080C00305188852900047B0060C8232378008499001446F257 + 3CF12BAE10E72A00007899B23CB9243945815B082D710757572E1E28CE49172B + 14366102619A402EC27999193281340FE0F3CC0000A0911511E083F3FD78CE0E + AECECE368EB60E5F2DEABF06FF226262E3FEE5CFAB70400000E1747ED1FE2C2F + B31A803B06806DFEA225EE04685E0BA075F78B66B20F40B500A0E9DA57F370F8 + 7E3C3C45A190B9D9D9E5E4E4D84AC4425B61CA577DFE67C25FC057FD6CF97E3C + FCF7F5E0BEE22481325D814704F8E0C2CCF44CA51CCF92098462DCE68F47FCB7 + 0BFFFC1DD322C44962B9582A14E35112718E449A8CF332A52289429229C525D2 + FF64E2DF2CFB033EDF3500B06A3E017B912DA85D6303F64B27105874C0E2F700 + 00F2BB6FC1D4280803806883E1CF77FFEF3FFD47A02500806649927100005E44 + 242E54CAB33FC708000044A0812AB0411BF4C1182CC0061CC105DCC10BFC6036 + 844224C4C24210420A64801C726029AC82422886CDB01D2A602FD4401D34C051 + 688693700E2EC255B80E3D700FFA61089EC128BC81090441C808136121DA8801 + 628A58238E08179985F821C14804128B2420C9881451224B91354831528A5420 + 55481DF23D720239875C46BA913BC8003282FC86BC47319481B2513DD40CB543 + B9A8371A8446A20BD06474319A8F16A09BD072B41A3D8C36A1E7D0AB680FDA8F + 3E43C730C0E8180733C46C302EC6C342B1382C099363CBB122AC0CABC61AB056 + AC03BB89F563CFB17704128145C0093604774220611E4148584C584ED848A820 + 1C243411DA093709038451C2272293A84BB426BA11F9C4186232318758482C23 + D6128F132F107B8843C437241289433227B9900249B1A454D212D246D26E5223 + E92CA99B34481A2393C9DA646BB20739942C202BC885E49DE4C3E433E41BE421 + F25B0A9D624071A4F853E22852CA6A4A19E510E534E5066598324155A39A52DD + A8A15411358F5A42ADA1B652AF5187A81334759A39CD8316494BA5ADA295D31A + 681768F769AFE874BA11DD951E4E97D057D2CBE947E897E803F4770C0D861583 + C7886728199B18071867197718AF984CA619D38B19C754303731EB98E7990F99 + 6F55582AB62A7C1591CA0A954A9526951B2A2F54A9AAA6AADEAA0B55F355CB54 + 8FA95E537DAE46553353E3A909D496AB55AA9D50EB531B5367A93BA887AA67A8 + 6F543FA47E59FD890659C34CC34F43A451A0B15FE3BCC6200B6319B3782C216B + 0DAB86758135C426B1CDD97C762ABB98FD1DBB8B3DAAA9A13943334A3357B352 + F394663F07E39871F89C744E09E728A797F37E8ADE14EF29E2291BA6344CB931 + 655C6BAA96979658AB48AB51AB47EBBD36AEEDA79DA6BD45BB59FB810E41C74A + 275C2747678FCE059DE753D953DDA70AA7164D3D3AF5AE2EAA6BA51BA1BB4477 + BF6EA7EE989EBE5E809E4C6FA7DE79BDE7FA1C7D2FFD54FD6DFAA7F5470C5806 + B30C2406DB0CCE183CC535716F3C1D2FC7DBF151435DC34043A561956197E184 + 91B9D13CA3D5468D460F8C69C65CE324E36DC66DC6A326062621264B4DEA4DEE + 9A524DB9A629A63B4C3B4CC7CDCCCDA2CDD699359B3D31D732E79BE79BD79BDF + B7605A785A2CB6A8B6B86549B2E45AA659EEB6BC6E855A3959A558555A5DB346 + AD9DAD25D6BBADBBA711A7B94E934EAB9ED667C3B0F1B6C9B6A9B719B0E5D806 + DBAEB66DB67D6167621767B7C5AEC3EE93BD937DBA7D8DFD3D070D87D90EAB1D + 5A1D7E73B472143A563ADE9ACE9CEE3F7DC5F496E92F6758CF10CFD833E3B613 + CB29C4699D539BD347671767B97383F3888B894B82CB2E973E2E9B1BC6DDC8BD + E44A74F5715DE17AD2F59D9BB39BC2EDA8DBAFEE36EE69EE87DC9FCC349F299E + 593373D0C3C843E051E5D13F0B9F95306BDFAC7E4F434F8167B5E7232F632F91 + 57ADD7B0B7A577AAF761EF173EF63E729FE33EE33C37DE32DE595FCC37C0B7C8 + B7CB4FC36F9E5F85DF437F23FF64FF7AFFD100A78025016703898141815B02FB + F87A7C21BF8E3F3ADB65F6B2D9ED418CA0B94115418F82AD82E5C1AD2168C8EC + 90AD21F7E798CE91CE690E85507EE8D6D00761E6618BC37E0C2785878557863F + 8E7088581AD131973577D1DC4373DF44FA449644DE9B67314F39AF2D4A352A3E + AA2E6A3CDA37BA34BA3FC62E6659CCD5589D58496C4B1C392E2AAE366E6CBEDF + FCEDF387E29DE20BE37B17982FC85D7079A1CEC2F485A716A92E122C3A96404C + 884E3894F041102AA8168C25F21377258E0A79C21DC267222FD136D188D8435C + 2A1E4EF2482A4D7A92EC91BC357924C533A52CE5B98427A990BC4C0D4CDD9B3A + 9E169A76206D323D3ABD31839291907142AA214D93B667EA67E66676CBAC6585 + B2FEC56E8BB72F1E9507C96BB390AC05592D0AB642A6E8545A28D72A07B26765 + 5766BFCD89CA3996AB9E2BCDEDCCB3CADB90379CEF9FFFED12C212E192B6A586 + 4B572D1D58E6BDAC6A39B23C7179DB0AE315052B865606AC3CB88AB62A6DD54F + ABED5797AE7EBD267A4D6B815EC1CA82C1B5016BEB0B550AE5857DEBDCD7ED5D + 4F582F59DFB561FA869D1B3E15898AAE14DB1797157FD828DC78E51B876FCABF + 99DC94B4A9ABC4B964CF66D266E9E6DE2D9E5B0E96AA97E6970E6E0DD9DAB40D + DF56B4EDF5F645DB2F97CD28DBBB83B643B9A3BF3CB8BC65A7C9CECD3B3F54A4 + 54F454FA5436EED2DDB561D7F86ED1EE1B7BBCF634ECD5DB5BBCF7FD3EC9BEDB + 5501554DD566D565FB49FBB3F73FAE89AAE9F896FB6D5DAD4E6D71EDC703D203 + FD07230EB6D7B9D4D51DD23D54528FD62BEB470EC71FBEFE9DEF772D0D360D55 + 8D9CC6E223704479E4E9F709DFF71E0D3ADA768C7BACE107D31F761D671D2F6A + 429AF29A469B539AFB5B625BBA4FCC3ED1D6EADE7AFC47DB1F0F9C343C59794A + F354C969DAE982D39367F2CF8C9D959D7D7E2EF9DC60DBA2B67BE763CEDF6A0F + 6FEFBA1074E1D245FF8BE73BBC3BCE5CF2B874F2B2DBE51357B8579AAF3A5F6D + EA74EA3CFE93D34FC7BB9CBB9AAEB95C6BB9EE7ABDB57B66F7E91B9E37CEDDF4 + BD79F116FFD6D59E393DDDBDF37A6FF7C5F7F5DF16DD7E7227FDCECBBBD97727 + EEADBC4FBC5FF440ED41D943DD87D53F5BFEDCD8EFDC7F6AC077A0F3D1DC47F7 + 068583CFFE91F58F0F43058F998FCB860D86EB9E383E3939E23F72FDE9FCA743 + CF64CF269E17FEA2FECBAE17162F7EF8D5EBD7CED198D1A197F29793BF6D7CA5 + FDEAC0EB19AFDBC6C2C61EBEC97833315EF456FBEDC177DC771DEFA3DF0F4FE4 + 7C207F28FF68F9B1F553D0A7FB93199393FF040398F3FC63332DDB000009C549 + 44415478DA9D570B5094D7153EFB7EF0581658965704410444855D23D030680D + AD428805634631C6689B264ECD74DA344D26D3C9A4292A1AED34613A7D68673A + 93345640DB8ACA43790A62001D231A890875C37B9FB00FD85DF6D9732E6068C5 + 84E69FF9877F97FFDEF3DD73BEEF3B67397EBF5F06002EF816977FF6C207FCC0 + 01E072B91C9FCF37FBDDDCC5E170E8A66BB12D841C7C598C0FCE2507F5F961C6 + 3D03629118EC763B381C0E70D81DE072B920441E42A0606A6A8AFDE5F3F92010 + 08402E978350285C6C3B310190E0836329C1BD5E2FDBD86C3607F5F6F6FEC462 + B6C4210091D566155A262DD3098909D78D46E37AAD56CB9B99992150BE808000 + 6FEE86DCBE8D1B37FE01B3E3C52C018FC79BDF52B2240014D4E3F13C4CDBFB47 + DF3FD570B9E179A140083EBF0FA6A7A76164640456AE5C097A9D1E2626275876 + 444211A8D7A961F5EAD570E0A7AF6585CAE5DDB4974C2683B9927C33005A305F + 5342DFD6D6B6FDF59FBD7E363030909D846ECD971AC60379881CEC0E3B389D4E + 569A0D1B363070898989B07BCFEE8308E45DB7DB0D4141414B0380A966279FE7 + 8F542A858AD395C72B4F57FE325C11CEEA4BE0EA2FD7436A4A2A848785B3FAD3 + E969DD9A356BE0CEE7774095A1F2BDF3EE3B7961A1A1AD7494057C581C00D60A + 060606C03C39C94EFDF06D0470BEFAFCC74D8D4D7B140A050344EFD6D6D55210 + 080D0D6527260074D2F4F47470CE38619D7A9D6573FEE60C2CD997A496A8A8A8 + AFCF002D6E6C68983BFD57F2914824505B5BF7AFABED578BC3C3C31900AFCF0B + 1DD73A20636D06FB3CAF0C7AA6F531B131F0E65B6FDE5F959696C1998BB360CF + C50190A42A4E57B05A2ECC80582C063C7D437777F7F7C2C2C2182002DBFB452F + A4A4A4009FC767C14901243D2AC7C4C4041C3B7EEC4F45C545AFC1AC632CBC16 + 07401B9C3C7102377B0400A7B1A1B1FDC68D1B39048000D12989842B56AC60DB + D35ABA294309090960301A61FFFE575F2E78A6E0AFC3C3C30C2071E36B0178F0 + 5475F5F5E0240094AEB99449F1C435B5351FFDE3EC3F5FC4DA734522113324AD + 410BB1D1B12CB51EB7075C6E17E343F2CA6410880490979757A052A9EAABABAB + 19A81776BFF0CD3E407AD66834CCCD18A8391F085728A4D7AE5E5523BBD7F4DF + EFDFD9D2DABAD1ED71832C58F690C06E2CA15C1E0ACB972FA74C18DFFED5DB45 + 28D7818ABF574CA8D42A4FF1B6E279337A3C00EDB81686068700DD0D7A6EF580 + 3252C96A2A100A20521909C343C311353535151F967FB889C7E7416C4C2C8C8C + 8E62C03058BB7A2D984C266BF677B21B91271359D959EDE81B3DB77B6E8F2237 + 2C3B4B76BA29068278BC0AEA6AEB41815A4F4A4E82D6E656C8CECE86F1F17198 + 9A9E02A53272737767E789F3E7CFC7373537B3D344474503DA30E4E4E4C08E9D + 3B08A00BDFB7194D46C7B265CBDA51B6F7925352EA313BD7456211646565F983 + 838317073089FA3FF5C9697C29139627C443DB9536C8CCCC648CBEDFDFFFF33B + 3D3D472E5CB820B6DA6C603018180065849249502215435C5C3CE3008FCB636B + F47A3D58AD36282ADE3651B26B47AEE6C183DEA7729E82E8E8E84701500DC7C6 + C6A0E6621DA8D52A06A0E36A07F9B9E4E6CD9BBFC5BA1FB872A515B45A3DD639 + 84F9BE6BC60564CD1488144046430A898989C1F7B4C82321AC7BF249DC771CB6 + 3F5FFCD6F6E7B61D2762A3C21E05A0D3E9989B353634013217525293A1A9A929 + AEE7B39E13B76FDFDE4206333434C42C988212E3490944565AC7E57001BB2393 + 266545817766661666C00AED6DEDF04C61FEA9575EF9F18BC4AD4748481A45A2 + 40547414D4D75D6275777A3D595567CE7E14201024BB313BF7BEB8079A7FF723 + D3DD730D8A03429463705030604D190FFA07FA41887D4219198D010BC186C1BB + BBAF83C56281F8F8B85B9F9CFA58856D9A7AC97F03C01483C968825569AB3003 + CDB8B1748FAEB9A63CD2E7941B57A9818BF380116B3A643083CBE98269EC7CE6 + 491398903333F8ECC2F493F78B44124C7F2C6CC9DF02668B15BA3A3B1F3A247A + 87FD771F1CDF849ED19D9B9BFBD540423AAF3E57CD1C2C353515FE7CF28FBFFE + FCC167EFE561870B98E2825EA104B5A91F46BC4268E109C0E21A04B9301A7C76 + 1198274C3089249B0DE284106CCB79DFCFC393DBE0DAB54EF61D05273BE360C6 + F47AAD75DFDEBD1F1C2A3B7CF021007AA1E6620D2E0E096969BC523E6CBCF392 + 275407324E1280800F2EEF08240B5240333E090EB1199CBC29F05815F0DD2917 + 4458F4501310099A491B08D1BA373DBD114821D73A3EC57E3283B276314BF7E3 + F0A2D58D3362BEF1C62F4E1E3B76EC000380A396834CA6B9A939AEB6A6EE6F23 + 23A3B951514A6432F56D1EF0653670FB1DE0B7CBC01EA4C1FAFB20DABA02126D + 3AE0E13B7D76005B5018F3FDF5EBD781C3E940C275B066469E32DBFFFD30AE1D + 431E98A1A4A4E46CE9C1D21F29C2154E9A62258383830E742C41E97BA52DCDCD + CD39D4EB674925C3B62A41B2883009B809D78B785C30E57081C2CF851CAF1EFA + 3181B778A13085C155EA74B46C37FA463B7A8283D937C9917A84C1A0071BAA03 + BBE299D2D2DFEC93C982ED229158C2C1BAF1D15223AE775DFFFDC58B179F1B78 + 30C0BC801A4D803480313B147D9D8C854629A9241019CE071FC70F765402FE01 + 271AD0EA356934BF416B6B1B3B39A9C0472A4159EA745AB0A3833EBBB5F0CCC1 + C387F622204708CE85CC8AD1F514BB76EE6AE9EBEB4B9BB64F831E9152BDA412 + 2926CDCF864E1AC5E93BD23B699F864AEAF7418134DB71616D463A8825626869 + 6A65FD22292991AD19191D63B29C98304251D10FAA0E971DDE477C1361568270 + 1FE60338F729F7BFBABF4AA7D5255BAC16331A8B0F3921460B8DC5FF09580D45 + 421020F389C6349A538628AD64C14FC43EC18CC66030B11932252589B88E9D74 + 900537998C74F2AA2347CAF6E1791C344191D2E6E60C2C81DD211A1C1AF44644 + 44C8912CD3B8A91F83F2CF9D3B977CF7EE5D3576B59CEEAE6E954EAF4BC26C89 + 695AA2F290F3111802B12A358DD9AD103BE5F2F87874CA6130A15F188C06282C + 2CA83A7A148303C741F3852C440664420FE7012221DE0EB2548958C2BE259BAD + ACAC64A9A61B1B8E082D38A9ABAB8B0065B7B7B7AB474747D330DD812CE5892B + 90230ADC5C0E5E2421353322DD96822D678E1E3D82C1FD76370E2A544202B0E8 + 4C480E26E00B586AE864980136F3CD8F5D3482D3C2B8B838282F2FE7171414C4 + 5FBA74291D3DFE6994EF4B9111518162EC131ACD005B9F9F9F5F5576A4EC87B8 + 9F9DCC27501AC84AB9A499707E26989F6029232855068ED2D7D383038A520997 + 2F5F86AD5BB7C2D8E8584ED9E1B2BFE0049532E39AF1173E5B5879E8D0A19711 + BC833C804AF6984BF27FFF385DD8B6890F6C644380F7FAFA649D9F76AAB83CAE + A7A46457874824F42FF80DF8B88BF5826FFDF3FC7F2EAC94C73DA70E3A327709 + 6B84FF0180E96622F4B22C130000000049454E44AE426082} + Visible = False + end + object imgStateNotFound: TImage + Left = 4 + Top = 4 + Width = 32 + Height = 32 + AutoSize = True + Picture.Data = { + 0954506E67496D61676589504E470D0A1A0A0000000D49484452000000200000 + 00200806000000737A7AF4000000097048597300000B1300000B1301009A9C18 + 00000A4F6943435050686F746F73686F70204943432070726F66696C65000078 + DA9D53675453E9163DF7DEF4424B8880944B6F5215082052428B801491262A21 + 09104A8821A1D91551C1114545041BC8A088038E8E808C15512C0C8A0AD807E4 + 21A28E83A3888ACAFBE17BA36BD6BCF7E6CDFEB5D73EE7ACF39DB3CF07C0080C + 9648335135800CA9421E11E083C7C4C6E1E42E40810A2470001008B3642173FD + 230100F87E3C3C2B22C007BE000178D30B0800C04D9BC0301C87FF0FEA42995C + 01808401C07491384B08801400407A8E42A600404601809D98265300A0040060 + CB6362E300502D0060277FE6D300809DF8997B01005B94211501A09100201365 + 884400683B00ACCF568A450058300014664BC43900D82D00304957664800B0B7 + 00C0CE100BB200080C00305188852900047B0060C8232378008499001446F257 + 3CF12BAE10E72A00007899B23CB9243945815B082D710757572E1E28CE49172B + 14366102619A402EC27999193281340FE0F3CC0000A0911511E083F3FD78CE0E + AECECE368EB60E5F2DEABF06FF226262E3FEE5CFAB70400000E1747ED1FE2C2F + B31A803B06806DFEA225EE04685E0BA075F78B66B20F40B500A0E9DA57F370F8 + 7E3C3C45A190B9D9D9E5E4E4D84AC4425B61CA577DFE67C25FC057FD6CF97E3C + FCF7F5E0BEE22481325D814704F8E0C2CCF44CA51CCF92098462DCE68F47FCB7 + 0BFFFC1DD322C44962B9582A14E35112718E449A8CF332A52289429229C525D2 + FF64E2DF2CFB033EDF3500B06A3E017B912DA85D6303F64B27105874C0E2F700 + 00F2BB6FC1D4280803806883E1CF77FFEF3FFD47A02500806649927100005E44 + 242E54CAB33FC708000044A0812AB0411BF4C1182CC0061CC105DCC10BFC6036 + 844224C4C24210420A64801C726029AC82422886CDB01D2A602FD4401D34C051 + 688693700E2EC255B80E3D700FFA61089EC128BC81090441C808136121DA8801 + 628A58238E08179985F821C14804128B2420C9881451224B91354831528A5420 + 55481DF23D720239875C46BA913BC8003282FC86BC47319481B2513DD40CB543 + B9A8371A8446A20BD06474319A8F16A09BD072B41A3D8C36A1E7D0AB680FDA8F + 3E43C730C0E8180733C46C302EC6C342B1382C099363CBB122AC0CABC61AB056 + AC03BB89F563CFB17704128145C0093604774220611E4148584C584ED848A820 + 1C243411DA093709038451C2272293A84BB426BA11F9C4186232318758482C23 + D6128F132F107B8843C437241289433227B9900249B1A454D212D246D26E5223 + E92CA99B34481A2393C9DA646BB20739942C202BC885E49DE4C3E433E41BE421 + F25B0A9D624071A4F853E22852CA6A4A19E510E534E5066598324155A39A52DD + A8A15411358F5A42ADA1B652AF5187A81334759A39CD8316494BA5ADA295D31A + 681768F769AFE874BA11DD951E4E97D057D2CBE947E897E803F4770C0D861583 + C7886728199B18071867197718AF984CA619D38B19C754303731EB98E7990F99 + 6F55582AB62A7C1591CA0A954A9526951B2A2F54A9AAA6AADEAA0B55F355CB54 + 8FA95E537DAE46553353E3A909D496AB55AA9D50EB531B5367A93BA887AA67A8 + 6F543FA47E59FD890659C34CC34F43A451A0B15FE3BCC6200B6319B3782C216B + 0DAB86758135C426B1CDD97C762ABB98FD1DBB8B3DAAA9A13943334A3357B352 + F394663F07E39871F89C744E09E728A797F37E8ADE14EF29E2291BA6344CB931 + 655C6BAA96979658AB48AB51AB47EBBD36AEEDA79DA6BD45BB59FB810E41C74A + 275C2747678FCE059DE753D953DDA70AA7164D3D3AF5AE2EAA6BA51BA1BB4477 + BF6EA7EE989EBE5E809E4C6FA7DE79BDE7FA1C7D2FFD54FD6DFAA7F5470C5806 + B30C2406DB0CCE183CC535716F3C1D2FC7DBF151435DC34043A561956197E184 + 91B9D13CA3D5468D460F8C69C65CE324E36DC66DC6A326062621264B4DEA4DEE + 9A524DB9A629A63B4C3B4CC7CDCCCDA2CDD699359B3D31D732E79BE79BD79BDF + B7605A785A2CB6A8B6B86549B2E45AA659EEB6BC6E855A3959A558555A5DB346 + AD9DAD25D6BBADBBA711A7B94E934EAB9ED667C3B0F1B6C9B6A9B719B0E5D806 + DBAEB66DB67D6167621767B7C5AEC3EE93BD937DBA7D8DFD3D070D87D90EAB1D + 5A1D7E73B472143A563ADE9ACE9CEE3F7DC5F496E92F6758CF10CFD833E3B613 + CB29C4699D539BD347671767B97383F3888B894B82CB2E973E2E9B1BC6DDC8BD + E44A74F5715DE17AD2F59D9BB39BC2EDA8DBAFEE36EE69EE87DC9FCC349F299E + 593373D0C3C843E051E5D13F0B9F95306BDFAC7E4F434F8167B5E7232F632F91 + 57ADD7B0B7A577AAF761EF173EF63E729FE33EE33C37DE32DE595FCC37C0B7C8 + B7CB4FC36F9E5F85DF437F23FF64FF7AFFD100A78025016703898141815B02FB + F87A7C21BF8E3F3ADB65F6B2D9ED418CA0B94115418F82AD82E5C1AD2168C8EC + 90AD21F7E798CE91CE690E85507EE8D6D00761E6618BC37E0C2785878557863F + 8E7088581AD131973577D1DC4373DF44FA449644DE9B67314F39AF2D4A352A3E + AA2E6A3CDA37BA34BA3FC62E6659CCD5589D58496C4B1C392E2AAE366E6CBEDF + FCEDF387E29DE20BE37B17982FC85D7079A1CEC2F485A716A92E122C3A96404C + 884E3894F041102AA8168C25F21377258E0A79C21DC267222FD136D188D8435C + 2A1E4EF2482A4D7A92EC91BC357924C533A52CE5B98427A990BC4C0D4CDD9B3A + 9E169A76206D323D3ABD31839291907142AA214D93B667EA67E66676CBAC6585 + B2FEC56E8BB72F1E9507C96BB390AC05592D0AB642A6E8545A28D72A07B26765 + 5766BFCD89CA3996AB9E2BCDEDCCB3CADB90379CEF9FFFED12C212E192B6A586 + 4B572D1D58E6BDAC6A39B23C7179DB0AE315052B865606AC3CB88AB62A6DD54F + ABED5797AE7EBD267A4D6B815EC1CA82C1B5016BEB0B550AE5857DEBDCD7ED5D + 4F582F59DFB561FA869D1B3E15898AAE14DB1797157FD828DC78E51B876FCABF + 99DC94B4A9ABC4B964CF66D266E9E6DE2D9E5B0E96AA97E6970E6E0DD9DAB40D + DF56B4EDF5F645DB2F97CD28DBBB83B643B9A3BF3CB8BC65A7C9CECD3B3F54A4 + 54F454FA5436EED2DDB561D7F86ED1EE1B7BBCF634ECD5DB5BBCF7FD3EC9BEDB + 5501554DD566D565FB49FBB3F73FAE89AAE9F896FB6D5DAD4E6D71EDC703D203 + FD07230EB6D7B9D4D51DD23D54528FD62BEB470EC71FBEFE9DEF772D0D360D55 + 8D9CC6E223704479E4E9F709DFF71E0D3ADA768C7BACE107D31F761D671D2F6A + 429AF29A469B539AFB5B625BBA4FCC3ED1D6EADE7AFC47DB1F0F9C343C59794A + F354C969DAE982D39367F2CF8C9D959D7D7E2EF9DC60DBA2B67BE763CEDF6A0F + 6FEFBA1074E1D245FF8BE73BBC3BCE5CF2B874F2B2DBE51357B8579AAF3A5F6D + EA74EA3CFE93D34FC7BB9CBB9AAEB95C6BB9EE7ABDB57B66F7E91B9E37CEDDF4 + BD79F116FFD6D59E393DDDBDF37A6FF7C5F7F5DF16DD7E7227FDCECBBBD97727 + EEADBC4FBC5FF440ED41D943DD87D53F5BFEDCD8EFDC7F6AC077A0F3D1DC47F7 + 068583CFFE91F58F0F43058F998FCB860D86EB9E383E3939E23F72FDE9FCA743 + CF64CF269E17FEA2FECBAE17162F7EF8D5EBD7CED198D1A197F29793BF6D7CA5 + FDEAC0EB19AFDBC6C2C61EBEC97833315EF456FBEDC177DC771DEFA3DF0F4FE4 + 7C207F28FF68F9B1F553D0A7FB93199393FF040398F3FC63332DDB000007EF49 + 44415478DAA557494C5459147D9FFA50C52020833289322B4A83A248DA38A431 + 22A24C894A42ECD00BDDB831313171E1C279484C74D5B8E9EE841834B62DA0E0 + 80A222600403E21845914150041151849AFB9C177E350A38F54F7EA07EFDF7EE + B9E79E73EF2BC56EB71B841016F10317D63A6E455184939393B0D96CF2B376F1 + B9768F73A90A5E56BF070003984C26613018C4C78F1FC5D0D090FCCB6793274F + 96C13F7CF820FFAAAA2A9C9D9DE573171797FF0FC06AB5CA8DFBFBFBF50F1F3E + 5CF0EEDD3B2F00500706067478660A0F0FEFECEDED0DEEEEEE5686878709CAEE + EEEE6E5FB26449EFD2A54BEB00DE4E96743ADDF70160508BC5F18AEEE0C18339 + 972E5D5A80EC6CF8CE3E3838285EBC78618F8E8EB622B8FAF6ED5BC90AB34E4C + 4C749A33678E69F3E6CD053E3E3E9DDCCBCBCB4B2BC9D7016835E645F4555555 + B15BB66CC9F3F0F0B020133BB379FEFCB9FC1E542B086C67F62C0D3217040766 + F41B366CA800904AB3D92C264D9AF46D0040EBE8CC859B9B9B3871E2C40ADCBF + F8FBFB0FB3BE0477F1E2453173E64CE1E7E727EBCFECB92E2E2E4EDCBB774F99 + 3B77AE7DC78E1D7F818156EE334A0FE303A0D09E3E7D2A4825B31E0DA0A4A424 + FBCA952B490060E467BE5B5E5E2E10442080CC980098697C7CBC001B3A946130 + 3535B50080FB99796060E09719E0E28A8A0A99C568FBB8BABA32586E7575F54F + C8D6440014666D6DAD0CC6CF9A33F83FD7070707ABDBB66DEB9E3D7BF6EF5A9C + 517B8E0F80962A2A2A22FA4F18A0F590FDAF75757531BEBEBE660222583842CC + 9A354BAA9BC18D46A3B41ECBD1D7D7E772E8D0A19AACACAC724AEA9B6CC80D8E + 1D3B2637FB0C8072F9F2E5DF6EDFBE1D0900260262961461545494D403D7F2A6 + 1E203E015B3A6FDAB4E9EF55AB5635767474C83DA98D2F026056172E5C180380 + B4969595659F3E7D3A11B577D2EBF53680B0C07AA45A52CBB5BCA907D85281E0 + 949494944268A419FA91A0F2F2F2BEDE07B829336337E3A5B901E273AEA9A909 + 82BAFD9F3C791277EDDAB5307C67A3B7C900454900B4645858980E4C0C6CDFBE + BD08E5E943598700C4969D9DAD35A38901BC7AF54AB4B5B549C477EEDC110101 + 01B2A6B4D0D4A95305E874071B6B8F1C391201905632D0D9D929A907C5CA9B37 + 6F869393935BA09321FC6D43DFE86E6A6A1A0033C3EBD7AFB7C98EA6D34D5C82 + F3E7CFCBCD626262446565A5C026E2E5CB97D26600107EEBD6ADACD2D2523F88 + D2C46C828282586FB168D122B16EDD3A02B4E27D239E59A64F9FDE0EE67AB157 + 3304DE492D2D5CB8D0EEE9E9393E00FAFFF8F1E3022F09D028AE5FBF2E929292 + A868D1DCDC9C7CF7EEDD1567CF9ED5BF7FFFDEFCFAF56B492759A105E90C0494 + 1AC073056B14BC23E705A8EFCBCDCDFDE3D9B3673D040AD06301B0865D5D5D14 + 9B98376F9E0400DF0BB451B5A1A12115007E06201B4A64A5D5088AB605C58260 + E800361A3A84656129D931E7CF9FAF605F7D4E4E4E29EEDA91F13D1600C5479A + 6137D9DDD86241B317749081CC63E104537B7B3B67900CAA8D668A95EBE81A64 + 2B054C56A64C992299E433CC117D5A5A5AFDC68D1BFF898888182B42DA0E4291 + F5A40D597704083975EA540E8204A2EB191F3D7A245A5A5A64A6DA41037614A8 + A7BCA903B024C54AE1A6A7A7CBE0F5F5F502E3DB79C68C191D8585850518D364 + E65300A0586E80B62919C0C6F100948EE0EEB099995943DD92256DE2B104D40C + FF271B7C4E4021212102FD9F410504AB7548F60EE3E1C387FF04F0AEC58B17FF + 07803E2F2E2EA6CF655B2D28285876FFFEFD94D8D85805F44A80DC7864F6CBA0 + DEDEDEB26C04C1405A1BE6F3E5CB97CBCC6FDEBCE9E88EBC502227E8C2949F9F + 5FBD77EFDE2A0700BE70EEDC393610036C97D6DADABA00945BF0D9C6BA327068 + 68A8B422B3A4557951686486CFC90CB5B06CD9324770ED5DEEA1BD0700EE5BB7 + 6EADC28C28930018884D0662F3C6B4CB41438946FD8CC8D8AED559DB449BF5B4 + 19EB4C85B32CFC8EE583DA2513748E169CEF69C1799C83151B77EFDE5D02B62D + 0A6CA7A2E359E05FDDCE9D3BF3917D140F1B9AA8D8FFB981D692695306A08830 + 9024F5CC9625A16B18F0C68D1B0EA0740813A045D137F49999998DBB76ED3A83 + BDCDF84E55B09902CF7B60C4A6A304716812161E1E596F06270866CB9B47290D + 9036F934F750B8BCD8B4983999D1663F4B0386F5AB57AF6EDCB367CF193C3353 + 27B21503B91B28C97FFCF87118501B891474DAC1886D24633BB366B723287A9F + 83874D888078252424C80E78F5EA55392F2223232540CE06960525D267646434 + ECDBB7AF989D9EFB8CAC5515D4C403F37A2D50FA81CE61506887265428DB93F5 + C26727646CE50998070A7C27013133829A366D9A6CD30CC4332467072F88D811 + 1CBDA061FFFEFD67008AECCA193332E6559E6275EC6CE858060432F3A48BA03A + 58D2EFC1830701D82014E50984727D10C000AFEB780EE0042418BE0FAB4ACA79 + B375633F69CD9E9E1E47703A8DA522F5D48FE33C4011E2B6B08990465EA4EFE4 + C993926ADED84887DAFBA2A1040250084416047AFD41B72B6E15949BA1111B75 + 42116AC157AE5CD978E0C0817F189CCF493D018C7B26D484436A78D064532220 + EDD8C5EFB89093EEE8D1A34EE8E9DE388E07C00161B06F02FABE1B32B340C45C + EFC2E0CC1CFB99B98EDA19E7E7D9C40712ADD1703119E1E184E0481FE705070D + 7E1D89356BD6506CA1105826EAEE0F6728A0BD096A2F0178338332F309AEEFFF + 713A7A6CB36CEC0F04081719D0F902A1091B5CD5CE2636EA37E01701FCF0CFF3 + CF31F16C38E20E4656BE618DFA2FD3308F8CBFBD23FB0000000049454E44AE42 + 6082} + end + object lblG940Throttle: TLabel + Left = 42 + Top = 4 + Width = 114 + Height = 19 + Caption = 'G940 Throttle' + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -16 + Font.Name = 'Tahoma' + Font.Style = [fsBold] + ParentFont = False + end + object lblG940ThrottleState: TLabel + Left = 42 + Top = 23 + Width = 59 + Height = 13 + Caption = 'Searching...' + end end end end diff --git a/G940LEDControl/Forms/MainFrm.pas b/G940LEDControl/Forms/MainFrm.pas index abed7b8..3652440 100644 --- a/G940LEDControl/Forms/MainFrm.pas +++ b/G940LEDControl/Forms/MainFrm.pas @@ -19,6 +19,7 @@ uses pngimage, X2UtPersistIntf, + FSXSimConnectIntf, LEDStateConsumer, Profile, Settings; @@ -27,8 +28,9 @@ uses const CM_ASKAUTOUPDATE = WM_APP + 1; - MSG_UPDATE = 1; - MSG_NOUPDATE = 2; + TM_UPDATE = 1; + TM_NOUPDATE = 2; + TM_FSXSTATE = 3; LED_COUNT = 8; @@ -95,6 +97,12 @@ type btnSaveProfile: TButton; btnDeleteProfile: TButton; bvlProfiles: TBevel; + pnlFSX: TPanel; + imgFSXStateNotConnected: TImage; + imgFSXStateConnected: TImage; + lblFSX: TLabel; + lblFSXState: TLabel; + pnlState: TPanel; procedure FormCreate(Sender: TObject); procedure lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType); @@ -103,6 +111,8 @@ type procedure FormDestroy(Sender: TObject); procedure cmbProfilesClick(Sender: TObject); procedure cbCheckUpdatesClick(Sender: TObject); + procedure btnSaveProfileClick(Sender: TObject); + procedure btnDeleteProfileClick(Sender: TObject); private FLEDControls: array[0..LED_COUNT - 1] of TLEDControls; FEventMonitor: TOmniEventMonitor; @@ -110,7 +120,7 @@ type FProfilesFilename: string; FProfiles: TProfileList; FActiveProfile: TProfile; - FLoadingProfiles: Boolean; + FLockChangeProfile: Boolean; FStateConsumerTask: IOmniTaskControl; FDeviceNotification: Pointer; @@ -118,6 +128,8 @@ type FSettingsFileName: string; FSettings: TSettings; + + procedure SetActiveProfile(const Value: TProfile); protected procedure RegisterDeviceArrival; procedure UnregisterDeviceArrival; @@ -135,7 +147,12 @@ type procedure LoadActiveProfile; procedure UpdateButton(AProfile: TProfile; AButtonIndex: Integer); + procedure AddProfile(AProfile: TProfile); + procedure UpdateProfile(AProfile: TProfile); + procedure DeleteProfile(AProfile: TProfile; ASetActiveProfile: Boolean); + procedure SetDeviceState(const AMessage: string; AFound: Boolean); + procedure SetFSXState(const AMessage: string; AConnected: Boolean); // procedure SetFSXToggleZoomButton(const ADeviceGUID: TGUID; AButtonIndex: Integer; const ADisplayText: string); procedure CheckForUpdatesThread(const ATask: IOmniTask); @@ -144,11 +161,12 @@ type procedure EventMonitorMessage(const task: IOmniTaskControl; const msg: TOmniMessage); procedure EventMonitorTerminated(const task: IOmniTaskControl); - procedure HandleDeviceStateMessage(ATask: IOmniTaskControl; AMessage: TOmniMessage); + procedure HandleDeviceStateMessage(AMessage: TOmniMessage); + procedure HandleFSXStateMessage(AMessage: TOmniMessage); procedure CMAskAutoUpdate(var Msg: TMessage); message CM_ASKAUTOUPDATE; - property ActiveProfile: TProfile read FActiveProfile; + property ActiveProfile: TProfile read FActiveProfile write SetActiveProfile; property EventMonitor: TOmniEventMonitor read FEventMonitor; property Profiles: TProfileList read FProfiles; property Settings: TSettings read FSettings; @@ -158,11 +176,11 @@ type implementation uses - ComObj, - Dialogs, - Graphics, - ShellAPI, - SysUtils, + System.SysUtils, + System.Win.ComObj, + Vcl.Dialogs, + Vcl.Graphics, + Winapi.ShellAPI, IdException, IdHTTP, @@ -172,6 +190,7 @@ uses ButtonFunctionFrm, ConfigConversion, + FSXSimConnectStateMonitor, G940LEDStateConsumer, LEDColorIntf, LEDFunctionIntf, @@ -184,15 +203,31 @@ uses const DefaultProfileName = 'Default'; + ProfilePostfixModified = ' (modified)'; - FILENAME_PROFILES = 'G940LEDControl\Profiles.xml'; - FILENAME_SETTINGS = 'G940LEDControl\Settings.xml'; + FilenameProfiles = 'G940LEDControl\Profiles.xml'; + FilenameSettings = 'G940LEDControl\Settings.xml'; - SPECIAL_CATEGORY = -1; + TextStateSearching = 'Searching...'; + TextStateNotFound = 'Not found'; + TextStateFound = 'Connected'; - TEXT_STATE_SEARCHING = 'Searching...'; - TEXT_STATE_NOTFOUND = 'Not found'; - TEXT_STATE_FOUND = 'Connected'; + TextFSXConnected = 'Connected'; + TextFSXDisconnected = 'Not connected'; + TextFSXFailed = 'Failed to connect'; + + + + +type + TFSXStateMonitorWorker = class(TOmniWorker, IFSXSimConnectStateObserver) + protected + function Initialize: Boolean; override; + procedure Cleanup; override; + + { IFSXSimConnectStateObserver } + procedure ObserverStateUpdate(ANewState: TFSXSimConnectState); + end; @@ -200,7 +235,7 @@ const { TMainForm } procedure TMainForm.FormCreate(Sender: TObject); var - consumer: IOmniWorker; + worker: IOmniWorker; begin lblVersion.Caption := App.Version.FormatVersion(False); @@ -209,25 +244,25 @@ begin FEventMonitor := TOmniEventMonitor.Create(Self); - consumer := TG940LEDStateConsumer.Create; - FStateConsumerTask := FEventMonitor.Monitor(CreateTask(consumer)).MsgWait; + worker := TG940LEDStateConsumer.Create; + FStateConsumerTask := EventMonitor.Monitor(CreateTask(worker)).MsgWait; EventMonitor.OnTaskMessage := EventMonitorMessage; EventMonitor.OnTaskTerminated := EventMonitorTerminated; StateConsumerTask.Run; + worker := TFSXStateMonitorWorker.Create; + EventMonitor.Monitor(CreateTask(worker)).Run; + FindLEDControls; - FProfilesFilename := App.UserPath + FILENAME_PROFILES; + FProfilesFilename := App.UserPath + FilenameProfiles; FProfiles := TProfileList.Create(True); LoadProfiles; - FSettingsFileName := App.UserPath + FILENAME_SETTINGS; + FSettingsFileName := App.UserPath + FilenameSettings; LoadSettings; - // #ToDo1 -oMvR: 22-2-2013: implement profile changing properly - FStateConsumerTask.Comm.Send(TM_LOADPROFILE, ActiveProfile); - RegisterDeviceArrival; end; @@ -336,13 +371,15 @@ begin defaultProfile := ConfigConversion.ConvertProfile0To1; if not Assigned(defaultProfile) then - defaultProfile := CreateDefaultProfile; - - if Assigned(defaultProfile) then + defaultProfile := CreateDefaultProfile + else begin defaultProfile.Name := DefaultProfileName; - Profiles.Add(defaultProfile); + defaultProfile.IsTemporary := True; end; + + if Assigned(defaultProfile) then + Profiles.Add(defaultProfile); end else begin persistXML := TX2UtPersistXML.Create; @@ -354,7 +391,11 @@ begin end; end; - FLoadingProfiles := True; + { Make sure we always have a profile } + if Profiles.Count = 0 then + Profiles.Add(CreateDefaultProfile); + + FLockChangeProfile := True; try cmbProfiles.Items.BeginUpdate; try @@ -364,17 +405,9 @@ begin cmbProfiles.Items.AddObject(profile.Name, profile); finally cmbProfiles.Items.EndUpdate; - - if cmbProfiles.Items.Count > 0 then - begin - cmbProfiles.ItemIndex := 0; - - FActiveProfile := TProfile(cmbProfiles.Items.Objects[0]); - LoadActiveProfile; - end; end; finally - FLoadingProfiles := False; + FLockChangeProfile := False; end; end; @@ -397,6 +430,7 @@ end; procedure TMainForm.LoadSettings; var persistXML: TX2UtPersistXML; + profile: TProfile; begin if not FileExists(FSettingsFileName) then @@ -419,6 +453,18 @@ begin end; end; + { Default profile } + profile := nil; + if Length(Settings.ActiveProfile) > 0 then + profile := Profiles.Find(Settings.ActiveProfile); + + { LoadProfiles ensures there's always at least 1 profile } + if (not Assigned(profile)) and (Profiles.Count > 0) then + profile := Profiles[0]; + + SetActiveProfile(profile); + + { Auto-update } cbCheckUpdates.Checked := Settings.CheckUpdates; if not Settings.HasCheckUpdates then @@ -447,6 +493,8 @@ function TMainForm.CreateDefaultProfile: TProfile; begin { Default button functions are assigned during UpdateButton } Result := TProfile.Create; + Result.Name := DefaultProfileName; + Result.IsTemporary := True; end; @@ -460,6 +508,9 @@ begin for buttonIndex := 0 to Pred(LED_COUNT) do UpdateButton(ActiveProfile, buttonIndex); + + if Assigned(StateConsumerTask) then + StateConsumerTask.Comm.Send(TM_LOADPROFILE, ActiveProfile); end; @@ -496,16 +547,75 @@ begin end; +procedure TMainForm.AddProfile(AProfile: TProfile); +begin + Profiles.Add(AProfile); + cmbProfiles.Items.AddObject(AProfile.Name, AProfile); + SetActiveProfile(AProfile); +end; + + +procedure TMainForm.UpdateProfile(AProfile: TProfile); +var + itemIndex: Integer; + oldItemIndex: Integer; + +begin + itemIndex := cmbProfiles.Items.IndexOfObject(AProfile); + if itemIndex > -1 then + begin + oldItemIndex := cmbProfiles.ItemIndex; + FLockChangeProfile := True; + try + cmbProfiles.Items[itemIndex] := AProfile.Name; + cmbProfiles.ItemIndex := oldItemIndex; + finally + FLockChangeProfile := False; + end; + end; +end; + + +procedure TMainForm.DeleteProfile(AProfile: TProfile; ASetActiveProfile: Boolean); +var + itemIndex: Integer; + +begin + if AProfile = ActiveProfile then + FActiveProfile := nil; + + itemIndex := cmbProfiles.Items.IndexOfObject(AProfile); + if itemIndex > -1 then + begin + Profiles.Remove(AProfile); + cmbProfiles.Items.Delete(itemIndex); + + if Profiles.Count = 0 then + AddProfile(CreateDefaultProfile); + + if ASetActiveProfile then + begin + if itemIndex >= Profiles.Count then + itemIndex := Pred(Profiles.Count); + + FLockChangeProfile := True; + try + cmbProfiles.ItemIndex := itemIndex; + SetActiveProfile(TProfile(cmbProfiles.Items.Objects[itemIndex])); + finally + FLockChangeProfile := False; + end; + end; + end; +end; + + procedure TMainForm.cmbProfilesClick(Sender: TObject); begin - if not FLoadingProfiles then + if not FLockChangeProfile then begin if cmbProfiles.ItemIndex > -1 then - FActiveProfile := TProfile(cmbProfiles.Items.Objects[cmbProfiles.ItemIndex]) - else - FActiveProfile := nil; - - LoadActiveProfile; + SetActiveProfile(TProfile(cmbProfiles.Items.Objects[cmbProfiles.ItemIndex])); end; end; @@ -525,6 +635,33 @@ begin end; +procedure TMainForm.SetActiveProfile(const Value: TProfile); +begin + if Value <> FActiveProfile then + begin + FActiveProfile := Value; + + if Assigned(ActiveProfile) then + begin + if Settings.ActiveProfile <> ActiveProfile.Name then + begin + Settings.ActiveProfile := ActiveProfile.Name; + SaveSettings; + end; + + FLockChangeProfile := True; + try + cmbProfiles.ItemIndex := cmbProfiles.Items.IndexOfObject(ActiveProfile); + finally + FLockChangeProfile := False; + end; + + LoadActiveProfile; + end; + end; +end; + + procedure TMainForm.SetDeviceState(const AMessage: string; AFound: Boolean); begin lblG940ThrottleState.Caption := AMessage; @@ -537,20 +674,74 @@ begin end; +procedure TMainForm.SetFSXState(const AMessage: string; AConnected: Boolean); +begin + lblFSXState.Caption := AMessage; + lblFSXState.Update; + + imgFSXStateConnected.Visible := AConnected; + imgFSXStateNotConnected.Visible := not AConnected; +end; + + procedure TMainForm.LEDButtonClick(Sender: TObject); + + function GetUniqueProfileName(const AName: string): string; + var + counter: Integer; + + begin + Result := AName; + counter := 0; + + while Assigned(Profiles.Find(Result)) do + begin + Inc(counter); + Result := Format('%s (%d)', [AName, counter]); + end; + end; + + var buttonIndex: NativeInt; + profile: TProfile; + newProfile: Boolean; begin if not Assigned(ActiveProfile) then exit; - buttonIndex := (Sender as TComponent).Tag; - if TButtonFunctionForm.Execute(ActiveProfile, buttonIndex) then + { Behaviour similar to the Windows System Sounds control panel; + when a change occurs, create a temporary profile "(modified)" + so the original profile can still be selected } + if not ActiveProfile.IsTemporary then begin - UpdateButton(ActiveProfile, buttonIndex); - FStateConsumerTask.Comm.Send(TM_LOADPROFILE, ActiveProfile); + profile := TProfile.Create; + profile.Assign(ActiveProfile); + profile.Name := GetUniqueProfileName(profile.Name + ProfilePostfixModified); + profile.IsTemporary := True; + newProfile := True; + end else + begin + profile := ActiveProfile; + newProfile := False; + end; + + buttonIndex := (Sender as TComponent).Tag; + if TButtonFunctionForm.Execute(profile, buttonIndex) then + begin + if newProfile then + AddProfile(profile); + SaveProfiles; + UpdateButton(profile, buttonIndex); + + if Assigned(StateConsumerTask) then + StateConsumerTask.Comm.Send(TM_LOADPROFILE, profile); + end else + begin + if newProfile then + FreeAndNil(profile); end; end; @@ -622,11 +813,11 @@ begin try latestVersion := httpClient.Get('http://g940.x2software.net/version'); if VersionIsNewer(Format('%d.%d.%d', [App.Version.Major, App.Version.Minor, App.Version.Release]), latestVersion) then - ATask.Comm.Send(MSG_UPDATE, latestVersion) + ATask.Comm.Send(TM_UPDATE, latestVersion) else begin if ATask.Param.ByName('ReportNoUpdates').AsBoolean then - ATask.Comm.Send(MSG_NOUPDATE, True); + ATask.Comm.Send(TM_NOUPDATE, True); end; msgSent := True; @@ -638,7 +829,82 @@ begin on E:Exception do begin if not msgSent then - ATask.Comm.Send(MSG_NOUPDATE, False); + ATask.Comm.Send(TM_NOUPDATE, False); + end; + end; +end; + + +procedure TMainForm.btnSaveProfileClick(Sender: TObject); +var + name: string; + profile: TProfile; + existingProfile: TProfile; + newProfile: TProfile; + +begin + name := ''; + profile := ActiveProfile; + existingProfile := nil; + + repeat + if InputQuery('Save profile as', 'Save this profile as:', name) then + begin + existingProfile := Profiles.Find(name); + if Assigned(existingProfile) then + begin + case MessageBox(Self.Handle, PChar(Format('A profile named "%s" exists, do you want to overwrite it?', [name])), + 'Save profile as', MB_ICONQUESTION or MB_YESNOCANCEL) of + ID_YES: + break; + + ID_CANCEL: + exit; + end; + end else + break; + end else + exit; + until False; + + if Assigned(existingProfile) then + begin + existingProfile.Assign(profile); + existingProfile.Name := name; + UpdateProfile(existingProfile); + SetActiveProfile(existingProfile); + + if profile.IsTemporary then + DeleteProfile(profile, False); + end else + begin + if profile.IsTemporary then + begin + profile.Name := name; + profile.IsTemporary := False; + UpdateProfile(profile); + end else + begin + newProfile := TProfile.Create; + newProfile.Assign(profile); + newProfile.Name := name; + AddProfile(newProfile); + end; + end; + + SaveProfiles; +end; + + +procedure TMainForm.btnDeleteProfileClick(Sender: TObject); +begin + if Assigned(ActiveProfile) then + begin + if MessageBox(Self.Handle, PChar(Format('Do you want to remove the profile named "%s"?', [ActiveProfile.Name])), + 'Remove profile', MB_ICONQUESTION or MB_YESNO) = ID_YES then + begin + DeleteProfile(ActiveProfile, True); + SaveProfiles; end; end; end; @@ -666,15 +932,18 @@ procedure TMainForm.EventMonitorMessage(const task: IOmniTaskControl; const msg: begin case msg.MsgID of TM_NOTIFY_DEVICESTATE: - HandleDeviceStateMessage(task, msg); + HandleDeviceStateMessage(msg); - MSG_UPDATE: + TM_FSXSTATE: + HandleFSXStateMessage(msg); + + TM_UPDATE: if MessageBox(Self.Handle, PChar('Version ' + msg.MsgData + ' is available on the G940 LED Control website.'#13#10 + 'Do you want to open the website now?'), 'Update available', MB_YESNO or MB_ICONINFORMATION) = ID_YES then ShellExecute(Self.Handle, 'open', PChar('http://g940.x2software.net/category/releases/'), nil, nil, SW_SHOWNORMAL); - MSG_NOUPDATE: + TM_NOUPDATE: if msg.MsgData.AsBoolean then MessageBox(Self.Handle, 'You are using the latest version.', 'No update available', MB_OK or MB_ICONINFORMATION) else @@ -694,17 +963,37 @@ begin end; -procedure TMainForm.HandleDeviceStateMessage(ATask: IOmniTaskControl; AMessage: TOmniMessage); +procedure TMainForm.HandleDeviceStateMessage(AMessage: TOmniMessage); begin case AMessage.MsgData.AsInteger of DEVICESTATE_SEARCHING: - SetDeviceState(TEXT_STATE_SEARCHING, False); + SetDeviceState(TextStateSearching, False); DEVICESTATE_FOUND: - SetDeviceState(TEXT_STATE_FOUND, True); + SetDeviceState(TextStateFound, True); DEVICESTATE_NOTFOUND: - SetDeviceState(TEXT_STATE_NOTFOUND, False); + SetDeviceState(TextStateNotFound, False); + end; +end; + + +procedure TMainForm.HandleFSXStateMessage(AMessage: TOmniMessage); +var + state: TFSXSimConnectState; + +begin + state := TFSXSimConnectState(AMessage.MsgData.AsInteger); + + case state of + scsDisconnected: + SetFSXState(TextFSXDisconnected, False); + + scsConnected: + SetFSXState(TextFSXConnected, True); + + scsFailed: + SetFSXState(TextFSXFailed, False); end; end; @@ -720,4 +1009,27 @@ begin ShellExecute(Self.Handle, 'open', PChar(Link), nil, nil, SW_SHOWNORMAL); end; + +{ TFSXStateMonitorWorker } +function TFSXStateMonitorWorker.Initialize: Boolean; +begin + Result := inherited Initialize; + + if Result then + TFSXSimConnectStateMonitor.Instance.Attach(Self); +end; + + +procedure TFSXStateMonitorWorker.Cleanup; +begin + TFSXSimConnectStateMonitor.Instance.Detach(Self); + + inherited Cleanup; +end; + +procedure TFSXStateMonitorWorker.ObserverStateUpdate(ANewState: TFSXSimConnectState); +begin + Task.Comm.Send(TM_FSXSTATE, Integer(ANewState)); +end; + end. diff --git a/G940LEDControl/G940LEDControl.dpr b/G940LEDControl/G940LEDControl.dpr index 382ee4e..87b0bc9 100644 --- a/G940LEDControl/G940LEDControl.dpr +++ b/G940LEDControl/G940LEDControl.dpr @@ -29,7 +29,8 @@ uses FSXLEDFunction in 'Units\FSXLEDFunction.pas', LEDResources in 'Units\LEDResources.pas', Settings in 'Units\Settings.pas', - FSXLEDFunctionWorker in 'Units\FSXLEDFunctionWorker.pas'; + FSXLEDFunctionWorker in 'Units\FSXLEDFunctionWorker.pas', + FSXSimConnectStateMonitor in 'Units\FSXSimConnectStateMonitor.pas'; {$R *.res} diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj index 25ff0fd..37832db 100644 --- a/G940LEDControl/G940LEDControl.dproj +++ b/G940LEDControl/G940LEDControl.dproj @@ -8,7 +8,7 @@ VCL 13.4 True - Debug + Release Win32 1 Application @@ -49,6 +49,7 @@ true + rtl;dbrtl;$(DCC_UsePackage) Lib 0 Bin @@ -81,8 +82,9 @@ RELEASE;$(DCC_Define) - 6 - CompanyName=X²Software;FileDescription=G940 LED Control;FileVersion=0.6.0.0;InternalName=;LegalCopyright=© 2011 X²Software;LegalTrademarks=;OriginalFilename=G940LEDControl.exe;ProductName=G940 LED Control;ProductVersion=0.6;Comments= + 1 + 0 + CompanyName=X²Software;FileDescription=G940 LED Control;FileVersion=1.0.0.0;InternalName=;LegalCopyright=© 2011 X²Software;LegalTrademarks=;OriginalFilename=G940LEDControl.exe;ProductName=G940 LED Control;ProductVersion=1.0;Comments= 1033 $(BDS)\bin\default_app.manifest @@ -144,6 +146,110 @@ G940LEDControl.dpr + ExpressCoreLibrary by Developer Express Inc. + Express Cross Platform Library by Developer Express Inc. + ExpressPageControl by Developer Express Inc. + ExpressEditors Library by Developer Express Inc. + ExpressBars by Developer Express Inc. + ExpressBars Ribbon controls by Developer Express Inc. + ExpressScheduler by Developer Express Inc. + ExpressSkins Library by Developer Express Inc. + ExpressPrinting System by Developer Express Inc. + ExpressPivotGrid by Developer Express Inc. + ExpressOrgChart by Developer Express Inc. + ExpressSkins Library Uses Clause Auto Fill Helper by Developer Express Inc. + ExpressSkins Library Painter for PageControl by Developer Express Inc. + ExpressSkins Library Painter for Scheduler by Developer Express Inc. + ExpressSkins Library Painter for Bars by Developer Express Inc. + ExpressSkins Library Painter for NavBar by Developer Express Inc. + ExpressSkins Library Painter for Ribbon by Developer Express Inc. + ExpressSkins Library Painter for Docking Library by Developer Express Inc. + ExpressPrinting System ReportLink for ExpressLayoutControl by Developer Express Inc. + ExpressEditors FieldLink by Developer Express Inc. + ExpressBars DBNavigator by Developer Express Inc. + ExpressBars extended DB items by Developer Express Inc. + ExpressBars extended items by Developer Express Inc. + ExpressBars Tabbed MDI by Developer Express Inc. + ExpressLayout Control by Developer Express Inc. + ExpressQuantumTreeList 5 by Developer Express Inc. + ExpressQuantumGrid by Developer Express Inc. + ExpressVerticalGrid by Developer Express Inc. + ExpressMemData by Developer Express Inc. + ExpressSpellChecker 2 by Developer Express Inc. + ExpressSpreadSheet by Developer Express Inc. + ExpressDocking Library by Developer Express Inc. + ExpressNavBar by Developer Express Inc. + ExpressSkins - Black Skin by Developer Express Inc. + ExpressSkins - Blue Skin by Developer Express Inc. + ExpressSkins - Blueprint Skin by Developer Express Inc. + ExpressSkins - Caramel Skin by Developer Express Inc. + ExpressSkins - Coffee Skin by Developer Express Inc. + ExpressSkins - Darkroom Skin by Developer Express Inc. + ExpressSkins - DarkSide Skin by Developer Express Inc. + ExpressSkins - DevExpressDarkStyle Skin by Developer Express Inc. + ExpressSkins - DevExpressStyle Skin by Developer Express Inc. + ExpressSkins - Foggy Skin by Developer Express Inc. + ExpressSkins - GlassOceans Skin by Developer Express Inc. + ExpressSkins - HighContrast Skin by Developer Express Inc. + ExpressSkins - iMaginary Skin by Developer Express Inc. + ExpressSkins - Lilian Skin by Developer Express Inc. + ExpressSkins - LiquidSky Skin by Developer Express Inc. + ExpressSkins - LondonLiquidSky Skin by Developer Express Inc. + ExpressSkins - McSkin Skin by Developer Express Inc. + ExpressSkins - MoneyTwins Skin by Developer Express Inc. + ExpressSkins - Office2007Black Skin by Developer Express Inc. + ExpressSkins - Office2007Blue Skin by Developer Express Inc. + ExpressSkins - Office2007Green Skin by Developer Express Inc. + ExpressSkins - Office2007Pink Skin by Developer Express Inc. + ExpressSkins - Office2007Silver Skin by Developer Express Inc. + ExpressSkins - Office2010Black Skin by Developer Express Inc. + ExpressSkins - Office2010Blue Skin by Developer Express Inc. + ExpressSkins - Office2010Silver Skin by Developer Express Inc. + ExpressSkins - Pumpkin Skin by Developer Express Inc. + ExpressSkins - SevenClassic Skin by Developer Express Inc. + ExpressSkins - Seven Skin by Developer Express Inc. + ExpressSkins - Sharp Skin by Developer Express Inc. + ExpressSkins - SharpPlus Skin by Developer Express Inc. + ExpressSkins - Silver Skin by Developer Express Inc. + ExpressSkins - Springtime Skin by Developer Express Inc. + ExpressSkins - Stardust Skin by Developer Express Inc. + ExpressSkins - Summer2008 Skin by Developer Express Inc. + ExpressSkins - TheAsphaltWorld Skin by Developer Express Inc. + ExpressSkins - Valentine Skin by Developer Express Inc. + ExpressSkins - VS2010 Skin by Developer Express Inc. + ExpressSkins - Whiteprint Skin by Developer Express Inc. + ExpressSkins - Xmas2008Blue Skin by Developer Express Inc. + ExpressPrinting System ReportLinks (Standard) by Developer Express Inc. + ExpressPrinting System ContainerProducer for ExpressPageControl by Developer Express Inc. + ExpressDBTree by Developer Express Inc. + ExpressTreePrintedDataSet by Developer Express Inc. + ExpressDBOrgChart by Developer Express Inc. + ExpressFlowChart by Developer Express Inc. + ExpressPageControl dxBar Popup Menu by Developer Express Inc. + ExpressBars cxEditor item by Developer Express Inc. + ExpressScheduler connection to ExpressQuantumGrid by Developer Express Inc. + ExpressQuantumTreeList 5 dxBar Built-In Menu by Developer Express Inc. + ExpressSkins Library Uses Clause Auto Fill Helper for ExpressEditors by Developer Express Inc. + ExpressSkins Library Uses Clause Auto Fill Helper for PageControl Painter by Developer Express Inc. + ExpressSkins Library Uses Clause Auto Fill Helper for Scheduler Painter by Developer Express Inc. + ExpressSkins Library Uses Clause Auto Fill Helper for Bars Painters by Developer Express Inc. + ExpressSkins Library Uses Clause Auto Fill Helper for NavBar Painter by Developer Express Inc. + ExpressSkins Library Uses Clause Auto Fill Helper for Ribbon Painters by Developer Express Inc. + ExpressPrinting System Cross Platform Library by Developer Express Inc. + ExpressPrinting System Extended Cross Platform Library by Developer Express Inc. + ExpressPrinting System ReportLink for ExpressPivotGrid by Developer Express Inc. + ExpressPrinting System ReportLink for ExpressScheduler by Developer Express Inc. + ExpressPrinting System ReportLink for ExpressSpreadSheet by Developer Express Inc. + ExpressPrinting System ReportLink for ExpressQuantumTreeList by Developer Express Inc. + ExpressPrinting System ReportLink for ExpressVerticalGrid by Developer Express Inc. + ExpressPrinting System ReportLinks for ExpressDBOrgChart by Developer Express Inc. + ExpressPrinting System ReportLink for ExpressDBTree by Developer Express Inc. + ExpressPrinting System ReportLinks for ExpressFlowChart by Developer Express Inc. + ExpressPrinting System ReportLink for ExpressQuantumGrid by Developer Express Inc. + ExpressPrinting System ReportLinks for ExpressOrgChart by Developer Express Inc. + ExpressPrinting System Advanced Preview Window by Developer Express Inc. + ExpressPrinting System Ribbon Preview Window by Developer Express Inc. + ExpressPivotGrid 2 connection to ExpressQuantumGrid Chart View by Developer Express Inc. ExpressPivotGrid 2 OLAP by Developer Express Inc. @@ -192,6 +298,7 @@ + Cfg_2 Base diff --git a/G940LEDControl/G940LEDControl.res b/G940LEDControl/G940LEDControl.res index d98ff0a8e18f4a9808bfeee4072191112fccef14..b2c506169f10a1d5d3d0d58837ab67330cc5b5c5 100644 GIT binary patch delta 921 zcmaKqJ#5oJ6vzKA8QP*se2D$zGF2!^X;s8vQ0NDws#-)))D2nY>RfH?$ad1g;t{bS zdFjr?2s|vMZHsu!gdc3Rs_zh_P5yP&x%`KpT_GYZYF9#)+ z9Tdo*kRT*ZA%-M6!2wcQex#SfkXAWwnst+VU=8P~U#HCz^x@O~DZ}x3l7_#Jwe z6gZ+j8|$}mk$(1_a)as|feD~bQ$?4=0PgZR{iwhW^WNa9A4^QIDTCCbiP;HQm#mDm zGsZINw6PPohfB2Ip(%3}&W?wGRNCosih3|Nt3s+<<|ziM1#mmN$q{;tWWB49mrTf3g= zjE(!O>%dj>%RFR2y0c@RhoG2 zTD_@1cVXUMRqqcMZ`ecZC*i;krROe9nDnzWka?bFtt{xNLI%YkGbYuQs8MZ2E6z&w yEb4fcB19~QGWJ|QZzsbvlGy}Ih=FPan&-I(ew<6UaZ5*2eT%lwEzF~8#H~Nu_N2G~ delta 147 zcmaEJkMY9|#t90WZ!l$KGPX~4%!!yhJ!dH+=VX^$Em6+0)S}|d{5)GFGd%+kRO030 zoIEjCS%)(xF)zI|F+J5*Ned)cP?VpXT3no8uqhBobX)7?$7 zvpX|8Gw*rd=kem4o!w0~&EAL)%$%9`yyras=l^`Z@P8J$d|~_H$8SxNWFk#doJ&9i zaOG9jKWQ7h%_zRi%B!NP#2AQC&(9tE_UR1)>^SnvPjs8JPo?9NcS*H@hyV#lQeNFg zc&{RbVu)Z7h?p7LT`+6S9Ia36`r6D3-)sV;E01)WvrpCb-FMe5Q(HN-x6aUTf|!7F zh?7F~MpkdYsg-*y7C=mtZbv!s-m<)LdiHbO>6d>6JUU=t%N@VCRVt&e-+KG)Ja%L| zE-MTdk#)>VvPt_x@r)Cw*BsrR;`@f?@VjI4oOt=gSgB;&=3e?%GcDgdA=L&`yXxd> zxF9$KCsu&4G7RD-0T`rCc<_#Kc5kb&*bY4T*9&+xrgjbSMs+kn`a6IEfJlOfBN0b1 za9W^uA~;GW;^rf}RuMjP=NLP;Rsk3rNdY04;mUw^uAsAIzyXe)hY+A%6B;9qR#ypD zc)+<$_#dcO9iP8@oNZ&-VC}Ut9ok(ZD)jt{Pu&^-IFJOdLbc}j@&glGwnI0!_k5pcUtFZqSE3m)f{!Z$59}3G*uvA!7<)t^f9DA$B z-W@3i_tiKwHM9=MS~)Y_=gAiq387#oMhL<1v1A~u6f3zLfRN`#zi(JYP@v+B6d|!9 zQO=$ZoIT&>++5DXcQ-Z$A%!AUhzda=FO_?5_)u)JjTq+GD!uF$_@JSESIl#5CcfU$6Gzx=GUiz@(JY#Hy)M|O@Uj161K6+{OvDRadM zkKWhVP&vS8-7!4mxVWfA>P6@WquDlQo63bHU8{dR05e$Z7bjc1(xutz@_%RQytRXSej_H@vwHVnec8Y+1N z^WY<-0lsv&&Yd?F0oc!#pZ{Tz<7Yi@y&w4QLt`Ya0I{`^((0E1c#RUCuJ7Sj-+1rM zRlaiX2qHqK8~NGqo1FY00C4Q>J_m2;a{s|P05?r$o0u1DEr8X9jpUx2s(k06F+_xg zR^%s7FYw;ndedHft2Y3!Z)f)Lqstmy>1sq627K?~aVD?JsAfVnb7U#pHdVuk@X@8f zPoHTrbLmQ6-#l0Jj2&B2Y87Gk)`WAj;Yz;_0IX%e6htwM>G_-^w+wM$PZj4@?fB?Y z&JTavHJ*N{&2z6VbMs`Cdv6)y zk$Xni+DJKm)-&7u_=tM`wJu+{dl(Vnle;pG{(BiM`-Q3%ztu1lGhNe+OhF3(F&4G^ z%uLP)bCG(@@znfZkl+*GG01coK z(>^|=q+W1hfO2ZuGjlPJN2TYl4d74z>hO)vHhA_}n@&&hLGiiby^-e;Ghets!zus` zeVlqM?-0!BXbqodWT_)dlqZfhS!hKT+SeWo7uu00jyC!ApON!wkwBw`h5+-ik@xp^`uO;u=H_Pc#^vSZzwGfco|4oYh`{YlO-&Lj8~Z|CuK+lY zsJFM5IHdIa{7kE>tD+k=*j9+=R5}1K2*DyCRP=tyw_=dMeSLjYQ&S`V%gf89hi-pj zhpjQV36r3ptgMV)Uti^aA)bKC2L=Wx8jZTKlamw40qBAa8_o;>a8Yq_F%1t7Q&m-! z{7)nj#F;Do{r$Zt3i1R%4-O7eMMZ@hJ3l|Cot+(71AXcR)!_hO5HGnzWEbj6#1Nt` zL_*!(-b!52KR!On7~DKGG(?d|giK}8*4CD+GhEs@K$9?J7wRPIEg)=+*y>r}UG832&k*j+N2bey1z z^%=-frq$Kefu)ElvL1jggc4Qr^z;;*15gg*ScHs*fESSA2nA$xbd*|JTAZu*_V%c! zr^i(a!U8WKR4@$QKr%w`ob^5s)U@uEJU}uyEnx)XOG`^Q;ppPxg2u6=CWMN@Jc5Ryl zP+wo4^X#yj4f_WtCMJY>5qHENZbjXrN9JSa*(w0Ug>jrAScfDchdl7}h>G<V?$OvIP*2 z|DnX^LW(Ir;Q%mi->{nx8XFtCfm*Lx%kJs$hy%GzbpS^`gqeoSEI^VlV&{2b-7i!wUgEDQf4`GaFP{!bi= z1aBt6VR*~LI*2m>0000KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000T~NklrCyR-t6zT79v0TqmJWL#~FX9y@0LaC{qCqOhI!2bp!$gLY8DhvYXxP**$x9&;5M; z5fY(*rR_U&{+#*dd!KpU=X>AhC5*8!00;j=u$cfD00M9vM}i=j`FAAVf`&XN!em!Zb~)mvpJ970ay)TaV{*xqH&-G;iBB9LEVlp^(4!{7A^YmRAvf!Essj2 z0xD5-tm=SKGO(bvwJ#cp3^2wJ4u>&Y67E)#GseL4JVa4MZ*T9Gci(yUaRBfG9~{Ra zH=Tnl%P8q3Ffb^J0##M9Zv8q4f&kZbk;`Tuk|YTncWuhAWuQPJ;oTpYf5 zZ>&Cs`dB?gQ3PWQrv^`9>5`?Wud9b)7^qY#@H`K-wY5l%r;zXx!Mu6%dIX*a&-1ui zkT(Pn1ObMJhf&b9YY;1{igY^N==mOe-v^}>K@fli3{;{JMG=xDT>(u%DMfpGJ65b% zQL-%SQZ}0d2oR6Q=RgQI6Whh%;kk?E04NG_xmu92jFKY};n3R0^hPV)^prUk`xiIJ7KS0L!$%5rSD2 zs*1wS&1<^Y-{1eubUMvZN+DAjp6|gZ86bqfb3Hh&1Jg9&xgHuCV$gMc;{4Egy!YOF zXl-r%&j18bMCa;GB$G)9!pxD`KJfEj{PNk()t#@MI(=&Sh4UAlJbdIxm+$*16bo|# zb{z*i&m)`5qQ1U9xALx)I^jrzWmH!YW3K|wKSq#%Oq3b#nMg8`q+ovfy&=EeXF z!@$^B3h{UxV3-pkr4*xMV?R66bNGe#-+y1$OQma`E~ONMXHMhw*9}CYQHkgHx_my5 z!PA4=b-jeG-+kn#+qOTkHaRkK#xM-r5I_*j^mb7czY-VWST*-P()Kx6N<$mLZNEh-?a{@)Od7cWMuPWk3Mn+ zH)}-MY!;5=%(Y)iDHvGdtvBC#_2kKu8&p+=W!VUaYoMws1W|xYWe9?RNF)M|aL`LS zl9!S&O%t((Myy`l30>E5_RJZ4{@MJMt2#Q+y0GY-|iunGDjIG<02`VWCpSNOAS z@Nw$Y>1ENnC|uXQo`JJx&qCLAG|y`Wr7{N3oc~^?Xa9@e2&_o%&JJ+C54mjtqO}n? zmV>fcK|!0ww5DNlauTDLF2S-bgw!fDH8o?y#tkSGb(}cy8Eo4IAq44kX3@!$Csz|f z2G*`!%jOvHJP*l{BqT|KD2NzMUHtXC2j6%@36qE?hTs$mxbxEIXw@dbsEdj+hUje) zS`sZ-IDbCk@g_7h#?jaqM@!2BZ2Znf7>0p?{sB0S1JCmy@H~RRSD)Jc)S(0a`0zIX zT;XaJ6GGtoe(g(tc=^Rt?$py#Lk=fN1c(th{s{!JB}h(cV8S#c3KVr0513^%I9cqk zyA4T=M|E`#)~)Y?UMiutw-2^$!*yKq%)I9r_(i#=f>ws;QKx-%K{-J@s~gU z_093|%WLEDM${A=z`#QYmmsie4C@9eb>ncI0JrORU|~4}UXk!AyA48h9cn#>d+u3< za;1zKS#VwViXUW{OlMFo8+hiKAN}pQUp@C6V~i6RV@Rb^P!vV{=+H-hI(F>n znpiA`sSH6)O&F@GfN9kbg*t%qAywL7m<|LmG^@Y`#v@x3g^|mlW7XZT9UI4veljx) zJP)!ggAjsjb_$N|VB5CsZ$JP1Z+C=iYAS>fg_x!(?BBn?;duY?mp=I5gGYx)h7kk- zD5VIgA=K2=AR37x8jYg5x*Dod1xXYT5Qd85f-u0URM5VB8Ne8h9Qh|K%Yr0|2mo+| zLnf0(rEK7#Et}u|-L9SACxn=_;V^ifSBR!*vF%T8KRh%vw5(hyV=6lZj^m&zDi|07 zHsg3Wj)P2PR8>_W91bHAiJ-cw8ia6Iao0-7ij2b_AAw;QXlq*t7BGxoouh&99l-}mKwKHpp@6hzl`AyEk& z;o!IqvbiiWQyCCK!1FxjHP1t|E{be+8kS|Dy?rSFf@E?Onx>&p(D3lqhxc~B(7gi; zrXTnSsp?mYi&-)0=-8;=(9jT(B&p2vJab)F*tc)r;=#eej_K*?H3R(vcW0(DZJMUZ zj^lt*3PBJsJ67*px(tFKL6Sr)XlcRNSPIkmJhHhgHgDdvclYk@9RM*c(?Ym5j8G^v z=kiDp1WFJDrsFzLWM$3~?b)*j;cyt?a2VNama3}S*5BXXF+Dwf@6n@2J1$?oyv#6+ zD#I|)w(t%_qcMbQBk(;Jnx-L}ox+Aq8{Xc%`-L3 z#^?LK4?z$R1OZIbge=Q8&-16(tXXq9kx0A|1c9(=)25aK2M(;%b$xx$$30IIpvrM< zB$LDNeIFY)Zrt15z3T@6Dum+@jYJ{Q>(?{F7*hb4H}V9t4wf;7(a}+G9EVUSgfGAR z5{-?GIC$_NwrtsgiOUmfx_5QImP{s>*pAIMKeTzz&Ye4dD9f@bNfIcfH}gFTVT{TD z&2oD6*mfLeW^gfvb3;SnzTUpOIiB;jZQK3{r4qYp2fSIxNCGx>SG(e*pN@4e4^@AKaG8$~P@ zD==c)NOWi%Qk9tzwT)DpgpmRtGg2O-QfT z`?IsN2l;b&$tqxUbhPrrfB)d!iHV7-a5$o7!BpXJSUEpGuVi6qW@d(_rl#;3e&p{S zdAhTs^DkRlTdRCNpOOa!W&#wP4-#KPtT>9sqOnjg6g8Pl(VE(t$PGWd;kS3)b=P09 zVuI&7Ck1DLYx7$sVtb}!I z*NX4_em^l{Xm)m%SW)6!MuQlP7FxD^8ExFSk#cf!Xok_6bgk95DLI6TaCP4FAWY3Qci9zO-@ba>lU0qF#DC+6yq2gu5G~{yq@H`Z7X>$L;0|)Q>^wUop@E%ZSg$SYWDyV16 zOw#Mcd$I;-A$i;_8DblWKdyYA)T<>AL48BmP`y_YpFl~ z?6W)5)6)so=inu*F9!}-AJCujP6j2aGdNu$v!+oD^{!!N=Q_I z6dsCz@c1r2Kc7xB?D0JmH$Fa2D@s<-Ew|kA7{>$ql0ksa=QpuM3=kRuJ zf&7k%%E&LHxfE9VHhzPkK!eN5x5=-!|!hQFe`Y5Dk>|e zv!k7I&1NfS2OU3le#!6v4ua`2=SxzP$O8lg4$P27t679OzL)1fQ27~(km(a#!0-1F z*9x?4+q?A1CymkMq~u?mkMNfS0qp-e2*Sn6H~blHfPjX9-$fQ+!SK7JyciNG5&<|L zpK(3XrGMH-LxV%Ks=nS`T~obh$yI}FIaGQ=DCzrBfJ6l_Um&h+7W4g?g3CA{f5Yco zrp!o(K!*oJfFHNy6cCL@6q3j!3NKUQA_7==@Bo~bm?;tiq0AB($o-jFS(KfVO@|L1 zCXHI-2!-}PX;%s0K@?ZhXcw;43uZ4L)-)Bc*Z@X)9aWt=_xHO zjf#tl-Al3|Qonlj7o}xo_p+bnqPT&;V0uy^k_bUKya$|RnGBcUntw5S$RH_7Va^;l zzpuAXTvJe3(6b~J&~RI4C$$nCi7tWu0Io*`h*CJylzHmA%ugIo48+ zsxBS}qLg9G5Ed1PP=WPnC>oAi9*O~PK=2bWfQQrRq?DADkq`dkgBnIl*zfb}7-3%i zZDz?dM9w^n#te6!HLp~uR2dA(1}uesN(D3z^Eq$0f1_ zqa&i#YG;hb^uG1itv_lqWlz8I(km~xhux(td!`+rrQg_#Er%v^t8DhKRjyv9D z>g+xAKZkDNdL#4t4cG76uwldBqDjhV(mDnlsUQ94M=6gz`k3*VXP)tzE#}x|cY$gD zblSegeXn!Ta-WY`+=m~2$X0f-pjyIbSgIhuK%_*kZ;EC)J7ls;T)*K@Xx-X%G{waa ze4klhkY8sV4o4~zEyboyn|3W*v8qLWsub%0%E8qeInf4`m;5qr3>QdCOXZBfOi&}H ztYw&ML4pu8_wCzvH^1JE;K<4hGDCE)YiRhx!;d`t^p-7K9Al$ns#jimS@rDm&xJ3G z3miXw{N~?({`+@0XXiCG?jw(9f@Wp|qKl(eX@v9&u}q_0OnE4Z^ql8`4!hmPrSAlV z7swNn9&uj;M3mX64neii6!Cd|+5w-lXmZMbw??a7_4woeaKkk>I+23$`H!Hy4`i*hm}W0Mnp42bb(nJpwU2qh7n?7p)0E@$&_WHj*bpS2pYrD3UrCt zRYuR&E4<(eu(jE)-uBM6_gH|kk&#hho&NrQ(Z&|Ao}ZVUA|WIqKfZ@cz@oATfbXFs zSQ==-Z-6}F1{P4TXkuXhk1$%&(u|bM=VFlr@!-UX6XKab(S*m7;Tjq$DOtI4^rG{C z{rmRc+xT(gs{;cAnTY7H5GH|1p$|n0!(DH*{H9tFJ4vh{aiyXWm8jg9V_7QMe7K$539Fs z-TK_#-FtQv78Pc$zka=#3_K1I{P5w!)Z5ofr%(4RS`iDKusm0bVqFh~k6avuVxRyh z0>a~fMWF;_2Jn9cvVlfV>HK?bZ4H?%xzyg?E`%2`Vxhnau>qP}TGn1PCdk5Mz4rPW zZ=dSv`SY^UQc^LjQ&W;@nvgP~~^-jgt1eNigKhhY6 zq^(%*!@xOW)rb|7tWTz#+#KO1216<>U%s4H)vx0CkS$^iiXs^xz^bYO;+F)b7WevkehR)s~=LlIvt10z)OhX9|87ywiRIb#@&nJ9m$zJ9e(2=ff; zId%FQD@Y=fEVvZAL8-^%F>xS0GY-VQvjUjd+`j$SJKt(3Fqx~F&W;>7LX|gMN6op* zsLL0i{O~;KX8n{Io}p9rcJj{535x*I);uduE+Tw%lC2Cy!MA;^jJeqvp^!NalJRA+ zh(JYZYO3h}r>7fP_#&!bUB{L;Q|s|ofwIJmTDs`PE)z}z~Tdeej)@u31n3l&j;i*>gVq;+{?gl{BSTv% z))~n*(n-^Evs7v^kZ&NF=AubN$$kpYPLXYTiY8KX7+}>j?(@^BaV9D{A7!ivlbH(~ zr6R_V50Ea&N-GS>l;?DjXLOvb#Rb%noJ(JJ50GkMZ4l@H_lK|7t*T`{kxOlDb|zFO z|7Zz)v5&~YqwS_=*KrOxM0UYBnWiiegy({Dx#txG+0Rx#{ZCK-YGioynrqh7Q#jzK zl8iz^9pkWt+4>qvF^`ZV)Jg4QPHL!IPVOL=zcbksQpQM^?xzq#dD^8VBNg!kf;2cB zCM8#_H>RnnEIdcPfR9o`vviEh;53dCMpg`>Kk5U}+EpyPHP1qAt!(*@xaFnQWye?! zG*7fz4c8g{i^K~G3G%sX)?RaB^XAQa&$%DK#OAht`{kQEcp_OSU(IF}@BQ<+U$bG;!OEtRYf4TVRc`f1sZ; z#=M}`Njkl-d`(@g{WrhaaU07puppl4+ZDj9dHeS5_wU%gGS46q& zW9rOxNc+xRJ8xx5-hWOFk;cZx``&r`ovnORH5#;DuTSJ;Ey!5M70e2!lHaSVLw3~?Q!7~7fofi7_TUfa6oZl35^LB6f!nC2AXW&`QEPEEN1h- zIkm+2At?_&@LvI`9>K+!m#6WKm@QR8H+21<_^g*1$;m|jN zo)jKPfwiUZYSw<5F_l~#Ln6I6a9q{2UWDk<10}b%E~ly3>cvDtIF-W zckkZ9am4xmtqYhy=@+^;x4yOYtvC5*g%Hv?G)N=E!@_EbYje^HB8@N#^jMxsO%n=% zVkAoBIv&nZYvM|Q0_A22@QAQj{tlCut3J@(>Y6GlS+Sf>cAw&i-v$;J-$a2N&(ADi z-r-`T1o>JBA7q3M)CSF3D;GwwRBh|Q>_zby*gk@lPAOk)qYWW3}h>cMkM^Mg6 zq+no^1EM!JIxJtYjCxM@ilFCbW+as9jZa94Xnz^g<0t_dWY;GWj&Q)MquRP!>hJ64;--zJCa1*p@!cR{Mn$o3jiX0f z_~{A`wgK!470*X((CRcaIy{1okbTeIJzKc&ah?OkiN^qlF)=lv#;yx=VW5DK(Gd~+ z&e-(GNn0-_Ernjpn(-A5!P>MuU~!=kjvo*Z^}pBe6>ycEAh-=yf`F2^|ECLItK9Y?A^2X;g3K5 z_^;hAcP0Aj(}8I(6CV!fiWtK+f>+K6`5bBUxh7C9x&#X%x3JMSx{ivov>?CU94$LS zxVE;sRwyBvT?LIzxd#aMtgWeJCTODOmR5eAR#+Z=!Pr7q30!~3HHbWa-ogU`XQn~b z#KeS!UBRo-YGx(Ff$*3N$A^^8&d#dl=H><-t6E!ISNHVvEcbf78o78WsylWQ`1#os zkhnt>LLz(Q=H$}Kl9e13zm7rSmZFG7I3JPd=p(PgQ z5+PCO^fTS9uCBH%z~1p4r<{FJ7f1QtDlTXWTUuJyurlgdDOcNUwpHxI8FD9x*aOS~ zFW;I^W_ATm=d1uVrKF}%Nl6Jg`UjXJctvaoGrx!Bktr&wD{XuC?!Db?&K>w3Q_Q~P z8zdMFR>Tgw-Chf~Id<$=eQ$5C1+k-|yh3D*T(gxdRtxbt%;Aqg7 zuBx(essp`se9!4*UzQyr-`q&)%Q|@Q;H}R;_uNyXBV(mVi=55`l?CkQ=i{ju4N>rt z%E~JE{O#Yu?ax~QfN6GOGvRU#4sw=9A9-|djwvTrU0EHwX7x3(%Bw13{X)53n9@$97VY+gUFp8n9 jg+sj4XIf;anr8$t}8p8o#F;6>`DV(bY3 zkYpNf}Of!OD3_*uiqPRLF zm&Md9eDBxuzc`(3l9Y6o@E9Q0H!G%v;+l-0Q()= zNJYS23aA=;`c?@r5&&rI)O#d=*++nYs-c|<@Ua2t7=@D80uTy7KqoSc7a$4%EC=r0 z^93-O0L|k=L+RfSYoOaO5UI>sX&je?Qiv(!kvoZ@As-vhs4D$K8d0koR$20VU4EI2 zLLmsM)uUkmC`hCQt3A5(9i^-p9TknkQd&Iv(N28LX>GlJwK-PlCJz8>UO^LAaQ-@` zU@78Ym#cU8_lfMEkr#NMM%&d=$=3q~JM)G|ZvSDURG5I9o7>#jm{sdRnpzDS2411N zEIJG?o&N?&|2sWdZTrR*B4i$-Omw!|HGHg5%rcflim_hYidX*EKz{v?70=nDYK=33 z(X8uGyT>VJ#T*G0b4DSPczf?pUO)e~0>2=XT@e93I?56RBt9xLw;}wa{s3y^)$#2V z01lg7JHPTk2!riI*T#IV59BVD3+@BKcB)D40Pswai%);BPOcvU0Ez`6u#XDN$DIuD z4q}$hn~R;~|13o@NUokPBos;ZJebA9lDFy&k|%uRBP-03|EoMJqC>|%JlTzsugjp3 zQpSzy+zOJ{$S*63awgg!IujH6S9h&q~!{YxZ`4ySseh>D=@ zTYYu|71l!;j~fyZW{;Z^r3L_@H~+$0lm$~lYIS73Qq-exWnxU2$M49;pL+>r#nTmJ zk9&M(l8F&5+#avAr<#wKMfD4Gd}kmL?r|32>+42-5{Hu}+YEi;tfHvwWBT~Cim*BA z#AfW14-s{UQU|Fhkq?sHicLYqNW(~LR;7%cg$r?eo>YvK5Tn!efHS#F{WEvd?Y}(? z)L`@Z16;xl)lcvBC#feNPrlH-w*xQFMyN2|Oa3zd z+^LpCA_dO=a3E|WccXhlc!PO^<+>^OJXFHPF20Q`duoX$)4W+)ZxQ-ucq5escc&5!Hh~ z$|5SzWv=>i?}Lmlqlk^WHM+G??{x}3S**A8v6I^{*7?FYoNiTydxwbhaDN(^d-W35 zoN>w894%8(UMPVQRueX?(>+SDlOtKra6ilO(jwjFDNR2Ldb><>!26Atxdfqs6SOsR zsz^&uGpban)Tvb7Z56HAE6puwRcbcA2%%KyS9@UgKp7oBdNX%hZ%%Kil&q9aQ%3Tn zhOfe@n7Oc7(Wm`I>3&9D+q2+24cF3Tzt-(@mO%bX{Y#hg(i3G0BjP#=F5(4>Q3mN= z5qDQ=$f#1LMcdS>lBntP3{K z3T;h~Zs@J)-cN2#4r~gHy_6xj6){Iy$!fwHc2y)(qyTibl(q;LwnfsOoS~t|ZPTbAc{3lOxv+J_E z_R1DM?2GO@>_;r-46{l7Q^s;|sJ@VIO*QLdlg_eyihQ&G>1{#L6yIu1v!d6hP?_EE?_P?G0N`vnc zpb4Ww6hcgIT%7s8;%JYPlU%U50BJD zV6qi_j?(jDO^7DBK0Z6+;VGkBBUNX0770FBjpz&Kwv~T-M-kt0zmcy8&}Z-un7jEE zj&>b~mC}{-QNtzEmLUZy@h)zV#qRe1yT8Any zP5-2yi7g_2T)o1@@J$@%L3u)p#x0GdY{@*jte5OALNb!Af}u%?3GT=W4$WjN`UiT; z?&VOGZCLG9?~CXLezsJSFHUs{RX2?vkztV~-}Oy+W?&?>Xj?3uNoGHy?&SC9aZge2ay-Oleo{ z8K&0+wQ~DwI)yyz9DLJRPvEpmZa}sf7Pz}OvuCbt#BaP(Z*?A?%PdP9^|pp)fj*t~ z^(@b#?tK84V#??8${yVFF}q2-ovr0Zd8U+U73o{)lrL`0&40eN^7QlG$seT! z{`3)^h2~H!3Rl!F-PmGV`ek%PN>2a1{;~eKzUcnsQoYs8*^7yN=75$zh@VpnrY)zJ zvR~R33 zAMPqs_TO#0%02LTbW8M>L2ORnw|j1iej7Wdq${UvwKyXWJR;`J0i0V zvmR&bWnGwBn>J|=%T7%HSoZ&?yl|H)2O(|jr*@?O#bJKu4L%utI2Mzenj09z_jl29 z;T<+$+h%mdk;?U;3F@^rsdOE^E!845YIfM@by&NwdB?EPFzbV)h071GAJSLJcA2PJH{JF0RGx&)QtOew>uK-l$qL<4kjI0|(V^x! zp9lNZE!r&>$MSm$bEu_=DLK=t#o2?!*Nz=N1xFK$%BRF(p<`ExC-GFMoAH_PL*ci= zck{$$B_zJdd0g)ND@BbxALGuWzj}8yR6|e4A9OagJ2N_wF;SR)n4aA3@~ZgqN5?MX zhlRebKDM?mzQTgfj-Ouab^0v&upShxP-$tZZU6VviQahWMKWV@@--1*#?e0; z;YSPqJw9zzbTt9M|2_Z&hXTOgYw+Fy0B->R_+beEk{JL%@0w)M`xtzhzExFJ(D$9s zNndikW60E(FN~H(OKUmz&n8&DaU#Q%U}7iZt3(~68ja)~>P%l6gjJeXpVRAKQoTif z2$*_UQ=!(nhAP&Lo}a9!rmltYJc(%Bz<2gUwfEbMok|3RE{)$9+E3iFLkMq+jrlEa z=cGe8^Z~Z3cn-{WGYnKEiD*;;1Ih>D7;z*1{3E-nmtiVIR?tyTW2J~lNmLDriDVvO(z8nUp2gNv@y zK!Lsv36gD=7(t>N1mMrcjzA1;D3s&bQsY>qra!|gpTg-BAPN>179_}^q`9=TG|9V; z@stdf3uE8DpTu8OAuP>`ye{j|~OWQym z5@P)cGE~c^BX3}!52NiBh@lMLZ32LP_tgto4LrtW!7`?B&e{6q%cmybC*glwc0J4P zOD*5}EW#nH{U-aAqe0Egu40<)EcVve(W*Elh&KlM(S;f7f-*2*k@SKT>#9OH*k-Yh z!0IitxWmK4^C;O>sV8@i4*-Pbo#^Q3DQ2-3zmSCm0)34Y6?q32_6Q~rLT~fN8ge{h zt%*;Si@L35_pxYpZ|Xxsx7Ci2Yf31D=+}?R57yq^w1?eg_J-Zf2Wy|yOgoGjYXuPy@#6JaX4QDo^P$-a($v9IyVq$42cD+A7`MHooY)s4|BLjoc zt6!@MRVLLhPft$DiD#+mpXjNP~`u632|+Qv~-RK&?b zDaY0!K(XN=aeQ*30Gap=ssU%VA3yWK;`#`kAJ_+yURUSEXfyC!sL^fhhV>_g;*@Y$ zD%qFkLhj}>JEpF!B9bB^-d28oIC}?US1QM>Eu*yioQV*GW+;3Jj8O|ODlH{R;!>FLuBxf& zLTLV6Ze<1w9QQVd!{L$ouqi1ap*P$4udhzI5t>K0w}tx>M(j>UhKK*5AcErJ;`Z4k zyiBpggPTGgI2J4x`-F$4VAo$cEHyP%3mzXETR7#oHeGGHnscPVT^BG@W1b5HD26oq z93W@Bk55lS1;fL_!sLd>#VAIgzz`mS z!%2Yt2{tY(N5;B-0QAPf3N1qpF>cJ8Q!pHCTD zT<%5O4H9=|2(NE#ee~l*lBFLU2^AGc9U4rx6;Y^yN)kqeYf?}TXVy0`0C8UEik$>l`hbt=VAmC?Ft!If*OEQefaNaq?nlV^k7VdNhi4 z7f$?_)VQcYPr>PyW1Q-lJk1StX?oMSqMBv5T)pXV!g)}U!)HMvyP*7 za&iK7SvKY9?BQ{^_4jC-pOiy-R03Hz{vi2^-PVD z1~a~c{Os)AhcslLOsY#H<4}w?W@)x7r-2H*w4tG)5TYC4QYpm`Y%;1mf0f=glMFofIv(P0|2gF?b9pjjK}9(3yzmF) zeC?vmQ#>A@4)|gcwB}?zou>Id{E6}j3}j8>;-De33JPKi`17-~-f8C7R<4%r_4>uw>!A%lw%3jCX#l@)pN(q@V4U7w)u^RN8+^e5s!CdlHAu@h F{{xiB>tFx? literal 4218 zcmV-=5QXoFP)3iR`*1OiXzD**XPM1efbS%0uKZUva z=g1XIKq{$!lF^m>NnXj|!zcGLJTvFep+hAnPo69|apFXNTU%R>{?65wWIKBFXwFqv zU3H?mx_Zm8W5)@?R4QWo&2-6hPEJlLH#fH}FE1}uSXg+xTeofp^gWfe z1hllYR4!Ps;Ld&f_Enxbbt(xk?Gwk3A5REubmYj9sI|3~ed?iy9{Tv(Z@(R~cJ12A z0|yQyv_*#xAC7cQq-!D(2N`J*lkVEJYkJtQVd+VeCT*ELd-ehm)0TAv?B2b*ysoZp z*7oh&%L)n#epew`6hO*G0pPA(yC5MsZ{EDg{C4o*!AKqx$ulAm70wmtu<5tNfB^%d z$&)8Xm6esz&p-b>7?OMR=&>uS2*A~!dg`f}t5&TllY0lSi9{moA%MNC7VY1^9}rRZ z?%ktbfBiKO0%$e>XRO{%+Q^Y3qoG5GM)Ig=%a$!szkdCqojZ5-KNkWni3q;;-g_fI z_~3&+{Ee&Q#(=UrLjd69CBG%-!ou)3;eZEl4d=6G!y~Gys-h7iMns!7ZHhkp@WZG} zmo8D?zI~$&8#avAHJ!4?17xk_^5x5ib?)3b!uk@gV0~{v6=nU5>jxkbvj7-U*y3)_ zcZidfqoSgsX#Du`k+dQzFE5V>EqPJ7LZhg(wDdq$5Fl$6%bm*^AOnT~0P`^ce7^tw z`w-{XTysqzAx?n7Lj>UYt#|L6Ry4e_S=_94_dOu1LTgy0+t780JB%x zisIiL+kA&469r&4&QW@650~QN;@~A**WBD3tWT)mMe;Jhr?ZBDy?ghT$_sJ<&42;M z9|oXBw@d*Pu7dzJ#FAr=KKdvG98vh}$*Mv~?w^lxGiU?iZv$gg7};C zhetq&y}~+RBDmhiiFt(c`s=TcHg4QVYAjWD0Mn_gsRI;~h0ggdYT|W(OW^$c{B}3z zcb@}Lf45GsQ41h^nkz)Fyz)x4c=6)2#BA_2MroDLUCF`$qHa9JriQ9 z+XHZCdyc_-vq(CmIRB@gehSz0>C794pp+^va zk;^lH{zL#_L59FP6fVK~t{n&qKJ1xhGdtv5yh@&vqkWn7WxBTNkw+exd*6Nc9eLn^ z2a;cW@kL_n*skOUFy;Z|l{oS8jcMeTZXOB($$+T*!@srQPg^=Y28*=Q7iS z2s{TdBva4()_BSFt=6r<)5{syDu~C+z{)-r!u1d-FRw0B8t+2+s^5gL`3l3n=#oJmDeY zqh-sM4U|T#R7j*QSr(9GI=}krt1~sweyRdP*LLmNwP9f2efM3k9_bHI00bzO#+CU_ zN_3S7K!B3_u`WG8nju&nGSCscfWHBK!wolt``|Tri0;>oF0n%8a7Vqu3r+wPpXTTP z5`eA@7VF3hLVmYfXF5dzBx1<#gakm{1K@W^!qRvN#{h)akRhlCh=07rEq4}40KZ|w zhVV>0LV8oAOjAB^;K0Kd-2C0usj~HZr!>tKk)oQq$N~-SBZcf7mWZGIpy*bSFBj^PX
Oa2RU@(mT0ofa3>HRNAA~@KUYHccSC3s!==X9~q4k^#s{s6jQDnI3$W9sO$)s{CN5s_g$M{SF>z9y5lAXbJb z+?y~lG2F+?Vt56WAm20X@31I>H;`TMviMS3yqlCslf}!IFCTjM-FL6ny-uC?T;TQB zUmq*6o2t0W#qvCa0mC-nouWuZ#4>|NnZ2Upd~XflJMj(y?*3RLJvDZiKpv~Y?6Ev) zl~fq-Cwd9Z*CCP=Tq3V!{P^)(H4R^(>kpmtTtH0bJ^%dklUA-=IgYf$pbnK0B*Zvedh$SxT z1_)tAinH6J_GpS%lj7@&-nf=~GZsG=LO55Gp_ba(+Vy9x0gCnN7hZVbE)ArZhvI&= z3^(P0#HMR;HW<#nHC=~sg=w7MT~Mgka|iknfdz4qD^S*8f}b%(}rY3AGA zNu+R&EzUpyXK`Z?kb*liF!X@AK7ce;4THb#y6Zy83jtJ+Se}#@2k&^d+uQPZ4$lL; z3n7SwREc%Vym|BLW8nX~A)?va$d_M!`3~JQ2_SCbK0Y9^e!OsTSJyK;Yl7p^K zQ0_xM=NvQw_a__xPuv@XZ3>a2ohCYk(e-zi{Eg z2?Cs?v}DkzJR6Kc0J++AJ6>TzJh%6{zunNT$oT-~8pz{5fI(P^dEOF(7|L5-g8_Kl z^6>=|gGVFy4tVz58$!9C!ArAR&#Dv_`xR3AJC)ul7(CYCrrgZw2oiVzVS(EMi~*Vu zhk`xC@ov#_cgMvJDUtZc`mXH>4RSp7L)`71GX1iJ=LL+@4G{<=3=~eyZ@&3v{a?K^ z@COZ8x^(HFmtJ~lx^C7PkXQp%_Io_>H#p+mTdax*6<3{4REuR73q1*pS19V|x&YU^7yz#~x6U25tfOr6L zA77a<2#ZeU+V<65Ea+YyVh0(X%iI0^-j6+n4GVJ)>fgVA|Ijug*CU=*d&YwvaGVs2 z+v7oG3VQ?N#%OZ;CculJ|NFKG9#E)(=hf8I{9BgGbz0$}OfceE!PfKl_`81b<*oRj zoiZIZkizB?*l&YT1d;A2u&_K~Vo~K@cm>DF7ThyVILEmk+M?-rb3;Qzy)w<&yejj1 z7SIEeFplM{%%``lJ-J9`-m;IUK>zLKE3f&*YXjUIcXcO-u%WJi7{EXbmZx+zc=o&u zNqA+J!P~(*9C;Ss6+TV%_4N&B3I2Bt&~{W8j#;#5(NM)tH;IV2ICPA+xHD7J6nj-~ zouss#6qgBy1VG%o`_s)k_$^+y1CVPGJxGB3xYfBHO>l$L*$3k|t@0aTfZLxd_>Kyp zq~fbY?s{C{PR4<-p${rUD_5^x-CJ7^xoQuENf&zY@j}S4-Vp(DVgQL}Q0-9z_(dt} z3cyvmMvaY)A%qAEN@xox&m!hU;4bOc{Bs2V*OlN|w!-q1F1v}0UY~#dd2iL}RU(Au zL#|8Q_=^!|XrBilw9=erT|n_2*C(x!CJ{dq$2EkLgtK}6{Q32N1N`|DaQa79$&rYa zLx`-_TOLxiapT5rs>TYOHsdcw*o^^1`y4Afb#gu-K^g3pm|Nza=qNmz<@SFU{Dmi= zgZV^K@!47N+lz4F QNB{r;07*qoM6N<$f;m|jT>t<8 diff --git a/G940LEDControl/Units/ConfigConversion.pas b/G940LEDControl/Units/ConfigConversion.pas index a7e6790..b86bbc8 100644 --- a/G940LEDControl/Units/ConfigConversion.pas +++ b/G940LEDControl/Units/ConfigConversion.pas @@ -106,20 +106,61 @@ begin V0_FUNCTIONFSX_ENGINEANTIICE: SetButton(FSXProviderUID, FSXFunctionUIDEngineAntiIce); V0_FUNCTIONFSX_AUTOPILOT: begin - { The only exception regarding states; the new default is Amber / Off } + { The new default is Green / Off } SetButton(FSXProviderUID, FSXFunctionUIDAutoPilot); AButton.SetStateColor(FSXStateUIDOn, lcGreen); AButton.SetStateColor(FSXStateUIDOff, lcRed); end; - V0_FUNCTIONFSX_FUELPUMP: SetButton(FSXProviderUID, FSXFunctionUIDFuelPump); V0_FUNCTIONFSX_TAILHOOK: SetButton(FSXProviderUID, FSXFunctionUIDTailHook); - V0_FUNCTIONFSX_AUTOPILOT_AMBER: SetButton(FSXProviderUID, FSXFunctionUIDAutoPilot); - V0_FUNCTIONFSX_AUTOPILOT_HEADING: SetButton(FSXProviderUID, FSXFunctionUIDAutoPilotHeading); - V0_FUNCTIONFSX_AUTOPILOT_APPROACH: SetButton(FSXProviderUID, FSXFunctionUIDAutoPilotApproach); - V0_FUNCTIONFSX_AUTOPILOT_BACKCOURSE: SetButton(FSXProviderUID, FSXFunctionUIDAutoPilotBackcourse); - V0_FUNCTIONFSX_AUTOPILOT_ALTITUDE: SetButton(FSXProviderUID, FSXFunctionUIDAutoPilotAltitude); - V0_FUNCTIONFSX_AUTOPILOT_NAV: SetButton(FSXProviderUID, FSXFunctionUIDAutoPilotNav); + V0_FUNCTIONFSX_AUTOPILOT_AMBER: + begin + { The new default is Green / Off } + SetButton(FSXProviderUID, FSXFunctionUIDAutoPilot); + AButton.SetStateColor(FSXStateUIDOn, lcAmber); + AButton.SetStateColor(FSXStateUIDOff, lcOff); + end; + + V0_FUNCTIONFSX_AUTOPILOT_HEADING: + begin + { The new default is Green / Off } + SetButton(FSXProviderUID, FSXFunctionUIDAutoPilotHeading); + AButton.SetStateColor(FSXStateUIDOn, lcAmber); + AButton.SetStateColor(FSXStateUIDOff, lcOff); + end; + + V0_FUNCTIONFSX_AUTOPILOT_APPROACH: + begin + { The new default is Green / Off } + SetButton(FSXProviderUID, FSXFunctionUIDAutoPilotApproach); + AButton.SetStateColor(FSXStateUIDOn, lcAmber); + AButton.SetStateColor(FSXStateUIDOff, lcOff); + end; + + V0_FUNCTIONFSX_AUTOPILOT_BACKCOURSE: + begin + { The new default is Green / Off } + SetButton(FSXProviderUID, FSXFunctionUIDAutoPilotBackcourse); + AButton.SetStateColor(FSXStateUIDOn, lcAmber); + AButton.SetStateColor(FSXStateUIDOff, lcOff); + end; + + V0_FUNCTIONFSX_AUTOPILOT_ALTITUDE: + begin + { The new default is Green / Off } + SetButton(FSXProviderUID, FSXFunctionUIDAutoPilotAltitude); + AButton.SetStateColor(FSXStateUIDOn, lcAmber); + AButton.SetStateColor(FSXStateUIDOff, lcOff); + end; + + V0_FUNCTIONFSX_AUTOPILOT_NAV: + begin + { The new default is Green / Off } + SetButton(FSXProviderUID, FSXFunctionUIDAutoPilotNav); + AButton.SetStateColor(FSXStateUIDOn, lcAmber); + AButton.SetStateColor(FSXStateUIDOff, lcOff); + end; + V0_FUNCTIONFSX_TAXILIGHTS: SetButton(FSXProviderUID, FSXFunctionUIDTaxiLights); V0_FUNCTIONFSX_RECOGNITIONLIGHTS: SetButton(FSXProviderUID, FSXFunctionUIDRecognitionLights); V0_FUNCTIONFSX_DEICE: SetButton(FSXProviderUID, FSXFunctionUIDDeIce); diff --git a/G940LEDControl/Units/FSXLEDFunction.pas b/G940LEDControl/Units/FSXLEDFunction.pas index 1283ede..0206860 100644 --- a/G940LEDControl/Units/FSXLEDFunction.pas +++ b/G940LEDControl/Units/FSXLEDFunction.pas @@ -13,83 +13,89 @@ type procedure RegisterStates; override; end; + TCustomFSXInvertedOnOffFunction = class(TCustomFSXFunction) + protected + procedure RegisterStates; override; + end; + + { Systems } + TCustomFSXSystemsFunction = class(TCustomFSXFunction) + protected + function GetCategoryName: string; override; + end; + + TFSXBatteryMasterFunction = class(TCustomFSXOnOffFunction) + protected + function GetCategoryName: string; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; + end; + + TFSXDeIceFunction = class(TCustomFSXInvertedOnOffFunction) + protected + function GetCategoryName: string; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; + end; + + TFSXExitDoorFunction = class(TCustomFSXSystemsFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; + end; + + TFSXGearFunction = class(TCustomFSXSystemsFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; + end; + + TFSXParkingBrakeFunction = class(TCustomFSXInvertedOnOffFunction) + protected + function GetCategoryName: string; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; + end; + + TFSXPressDumpSwitchFunction = class(TCustomFSXInvertedOnOffFunction) + protected + function GetCategoryName: string; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; + end; + + TFSXTailHookFunction = class(TCustomFSXSystemsFunction) + protected + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; + end; + + + { Engines } + TFSXEngineAntiIceFunction = class(TCustomFSXFunction) + protected + function GetCategoryName: string; override; + procedure RegisterStates; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; + end; - { Misc } TFSXEngineFunction = class(TCustomFSXFunction) protected + function GetCategoryName: string; override; procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; end; - TFSXGearFunction = class(TCustomFSXFunction) - protected - procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; - end; - - TFSXParkingBrakeFunction = class(TCustomFSXOnOffFunction) - protected - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; - end; - - TFSXExitDoorFunction = class(TCustomFSXFunction) - protected - procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; - end; - - TFSXTailHookFunction = class(TCustomFSXFunction) - protected - procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; - end; + { Control surfaces } TFSXFlapsFunction = class(TCustomFSXFunction) protected + function GetCategoryName: string; override; procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; end; TFSXSpoilersFunction = class(TCustomFSXFunction) protected + function GetCategoryName: string; override; procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; - end; - - TFSXBatteryMasterFunction = class(TCustomFSXFunction) - protected - procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; - end; - - TFSXAvionicsMasterFunction = class(TCustomFSXFunction) - protected - procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; - end; - - TFSXPressDumpSwitchFunction = class(TCustomFSXFunction) - protected - procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; - end; - - TFSXEngineAntiIceFunction = class(TCustomFSXFunction) - protected - procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; - end; - - TFSXFuelPumpFunction = class(TCustomFSXFunction) - protected - procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; - end; - - TFSXDeIceFunction = class(TCustomFSXFunction) - protected - procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; end; @@ -98,8 +104,8 @@ type protected function GetCategoryName: string; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; - function DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; + function DoCreateWorker(ASettings: ILEDFunctionWorkerSettings; const APreviousState: string = ''): TCustomLEDFunctionWorker; override; protected function GetLightMask: Integer; virtual; abstract; end; @@ -143,43 +149,46 @@ type { Autopilot } TCustomFSXAutoPilotFunction = class(TCustomFSXFunction) protected + procedure RegisterStates; override; function GetCategoryName: string; override; end; TFSXAutoPilotFunction = class(TCustomFSXAutoPilotFunction) protected - procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; end; TFSXAutoPilotHeadingFunction = class(TCustomFSXAutoPilotFunction) protected - procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; end; TFSXAutoPilotApproachFunction = class(TCustomFSXAutoPilotFunction) protected - procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; end; TFSXAutoPilotBackcourseFunction = class(TCustomFSXAutoPilotFunction) protected - procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; end; TFSXAutoPilotAltitudeFunction = class(TCustomFSXAutoPilotFunction) protected - procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; end; TFSXAutoPilotNavFunction = class(TCustomFSXAutoPilotFunction) protected - procedure RegisterStates; override; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; + end; + + + { Radios } + TFSXAvionicsMasterFunction = class(TCustomFSXOnOffFunction) + protected + function GetCategoryName: string; override; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; override; end; @@ -200,21 +209,59 @@ begin end; -{ TFSXEngineFunction } -procedure TFSXEngineFunction.RegisterStates; +{ TCustomFSXInvertedOnOffFunction } +procedure TCustomFSXInvertedOnOffFunction.RegisterStates; begin - RegisterState(TLEDState.Create(FSXStateUIDEngineNoEngines, FSXStateDisplayNameEngineNoEngines, lcOff)); - RegisterState(TLEDState.Create(FSXStateUIDEngineAllRunning, FSXStateDisplayNameEngineAllRunning, lcGreen)); - RegisterState(TLEDState.Create(FSXStateUIDEnginePartiallyRunning, FSXStateDisplayNameEnginePartiallyRunning, lcAmber)); - RegisterState(TLEDState.Create(FSXStateUIDEngineAllOff, FSXStateDisplayNameEngineAllOff, lcRed)); - RegisterState(TLEDState.Create(FSXStateUIDEngineFailed, FSXStateDisplayNameEngineFailed, lcFlashingRedNormal)); - RegisterState(TLEDState.Create(FSXStateUIDEngineOnFire, FSXStateDisplayNameEngineOnFire, lcFlashingRedFast)); + RegisterState(TLEDState.Create(FSXStateUIDOn, FSXStateDisplayNameOn, lcRed)); + RegisterState(TLEDState.Create(FSXStateUIDOff, FSXStateDisplayNameOff, lcGreen)); end; -function TFSXEngineFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +{ TCustomFSXSystemsFunction } +function TCustomFSXSystemsFunction.GetCategoryName: string; begin - Result := TFSXEngineFunctionWorker; + Result := FSXCategorySystems; +end; + + +{ TFSXBatteryMasterFunction } +function TFSXBatteryMasterFunction.GetCategoryName: string; +begin + Result := FSXCategorySystems; +end; + + +function TFSXBatteryMasterFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; +begin + Result := TFSXBatteryMasterFunctionWorker; +end; + + +{ TFSXDeIceFunction } +function TFSXDeIceFunction.GetCategoryName: string; +begin + Result := FSXCategorySystems; +end; + + +function TFSXDeIceFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; +begin + Result := TFSXDeIceFunctionWorker; +end; + + +{ TFSXExitDoorFunction } +procedure TFSXExitDoorFunction.RegisterStates; +begin + RegisterState(TLEDState.Create(FSXStateUIDExitDoorClosed, FSXStateDisplayNameExitDoorClosed, lcGreen)); + RegisterState(TLEDState.Create(FSXStateUIDExitDoorBetween, FSXStateDisplayNameExitDoorBetween, lcAmber)); + RegisterState(TLEDState.Create(FSXStateUIDExitDoorOpen, FSXStateDisplayNameExitDoorOpen, lcRed)); +end; + + +function TFSXExitDoorFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; +begin + Result := TFSXExitDoorFunctionWorker; end; @@ -230,31 +277,33 @@ begin end; -function TFSXGearFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +function TFSXGearFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; begin Result := TFSXGearFunctionWorker; end; { TFSXParkingBrakeFunction } -function TFSXParkingBrakeFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +function TFSXParkingBrakeFunction.GetCategoryName: string; +begin + Result := FSXCategorySystems; +end; + +function TFSXParkingBrakeFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; begin Result := TFSXParkingBrakeFunctionWorker; end; -{ TFSXExitDoorFunction } -procedure TFSXExitDoorFunction.RegisterStates; +{ TFSXPressDumpSwitchFunction } +function TFSXPressDumpSwitchFunction.GetCategoryName: string; begin - RegisterState(TLEDState.Create(FSXStateUIDExitDoorClosed, FSXStateDisplayNameExitDoorClosed, lcGreen)); - RegisterState(TLEDState.Create(FSXStateUIDExitDoorBetween, FSXStateDisplayNameExitDoorBetween, lcAmber)); - RegisterState(TLEDState.Create(FSXStateUIDExitDoorOpen, FSXStateDisplayNameExitDoorOpen, lcRed)); + Result := FSXCategorySystems; end; - -function TFSXExitDoorFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +function TFSXPressDumpSwitchFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; begin - Result := TFSXExitDoorFunctionWorker; + Result := TFSXPressDumpSwitchFunctionWorker; end; @@ -267,29 +316,89 @@ begin end; -function TFSXTailHookFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +function TFSXTailHookFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; begin Result := TFSXTailHookFunctionWorker; end; -{ TFSXFlapsFunction } -procedure TFSXFlapsFunction.RegisterStates; +{ TFSXEngineAntiIceFunction } +function TFSXEngineAntiIceFunction.GetCategoryName: string; begin - RegisterState(TLEDState.Create(FSXStateUIDFlapsNotAvailable, FSXStateDisplayNameFlapsNotAvailable, lcOff)); - RegisterState(TLEDState.Create(FSXStateUIDFlapsRetracted, FSXStateDisplayNameFlapsRetracted, lcGreen)); - RegisterState(TLEDState.Create(FSXStateUIDFlapsBetween, FSXStateDisplayNameFlapsBetween, lcAmber)); - RegisterState(TLEDState.Create(FSXStateUIDFlapsExtended, FSXStateDisplayNameFlapsExtended, lcRed)); + Result := FSXCategoryEngines; end; -function TFSXFlapsFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +procedure TFSXEngineAntiIceFunction.RegisterStates; +begin + RegisterState(TLEDState.Create(FSXStateUIDEngineAntiIceNoEngines, FSXStateDisplayNameEngineAntiIceNoEngines, lcOff)); + RegisterState(TLEDState.Create(FSXStateUIDEngineAntiIceAll, FSXStateDisplayNameEngineAntiIceAll, lcRed)); + RegisterState(TLEDState.Create(FSXStateUIDEngineAntiIcePartial, FSXStateDisplayNameEngineAntiIcePartial, lcAmber)); + RegisterState(TLEDState.Create(FSXStateUIDEngineAntiIceNone, FSXStateDisplayNameEngineAntiIceNone, lcGreen)); +end; + + +function TFSXEngineAntiIceFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; +begin + Result := TFSXEngineAntiIceFunctionWorker; +end; + + +{ TFSXEngineFunction } +function TFSXEngineFunction.GetCategoryName: string; +begin + Result := FSXCategoryEngines; +end; + + +procedure TFSXEngineFunction.RegisterStates; +begin + RegisterState(TLEDState.Create(FSXStateUIDEngineNoEngines, FSXStateDisplayNameEngineNoEngines, lcOff)); + RegisterState(TLEDState.Create(FSXStateUIDEngineAllRunning, FSXStateDisplayNameEngineAllRunning, lcGreen)); + RegisterState(TLEDState.Create(FSXStateUIDEnginePartiallyRunning, FSXStateDisplayNameEnginePartiallyRunning, lcAmber)); + RegisterState(TLEDState.Create(FSXStateUIDEngineAllOff, FSXStateDisplayNameEngineAllOff, lcRed)); + RegisterState(TLEDState.Create(FSXStateUIDEngineFailed, FSXStateDisplayNameEngineFailed, lcFlashingRedNormal)); + RegisterState(TLEDState.Create(FSXStateUIDEngineOnFire, FSXStateDisplayNameEngineOnFire, lcFlashingRedFast)); +end; + + +function TFSXEngineFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; +begin + Result := TFSXEngineFunctionWorker; +end; + + +{ TFSXFlapsFunction } +function TFSXFlapsFunction.GetCategoryName: string; +begin + Result := FSXCategoryControlSurfaces; +end; + + +procedure TFSXFlapsFunction.RegisterStates; +begin + RegisterState(TLEDState.Create(FSXStateUIDFlapsNotAvailable, FSXStateDisplayNameFlapsNotAvailable, lcOff)); + RegisterState(TLEDState.Create(FSXStateUIDFlapsRetracted, FSXStateDisplayNameFlapsRetracted, lcGreen)); + RegisterState(TLEDState.Create(FSXStateUIDFlapsBetween, FSXStateDisplayNameFlapsBetween, lcAmber)); + RegisterState(TLEDState.Create(FSXStateUIDFlapsExtended, FSXStateDisplayNameFlapsExtended, lcRed)); + RegisterState(TLEDState.Create(FSXStateUIDFlapsSpeedExceeded, FSXStateDisplayNameFlapsSpeedExceeded, lcFlashingAmberNormal)); + RegisterState(TLEDState.Create(FSXStateUIDFlapsDamageBySpeed, FSXStateDisplayNameFlapsDamageBySpeed, lcFlashingRedFast)); +end; + + +function TFSXFlapsFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; begin Result := TFSXFlapsFunctionWorker; end; { TFSXSpoilersFunction } +function TFSXSpoilersFunction.GetCategoryName: string; +begin + Result := FSXCategoryControlSurfaces; +end; + + procedure TFSXSpoilersFunction.RegisterStates; begin RegisterState(TLEDState.Create(FSXStateUIDSpoilersNotAvailable, FSXStateDisplayNameSpoilersNotAvailable, lcOff)); @@ -299,90 +408,12 @@ begin end; -function TFSXSpoilersFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +function TFSXSpoilersFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; begin Result := TFSXSpoilersFunctionWorker; end; -{ TFSXBatteryMasterFunction } -procedure TFSXBatteryMasterFunction.RegisterStates; -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXBatteryMasterFunction.RegisterStates -end; - - -function TFSXBatteryMasterFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; -begin - Result := TFSXBatteryMasterFunctionWorker; -end; - - -{ TFSXAvionicsMasterFunction } -procedure TFSXAvionicsMasterFunction.RegisterStates; -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAvionicsMasterFunction.RegisterStates -end; - - -function TFSXAvionicsMasterFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; -begin - Result := TFSXAvionicsMasterFunctionWorker; -end; - - -{ TFSXPressDumpSwitchFunction } -procedure TFSXPressDumpSwitchFunction.RegisterStates; -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXPressDumpSwitchFunction.RegisterStates -end; - - -function TFSXPressDumpSwitchFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; -begin - Result := TFSXPressDumpSwitchFunctionWorker; -end; - - -{ TFSXEngineAntiIceFunction } -procedure TFSXEngineAntiIceFunction.RegisterStates; -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXEngineAntiIceFunction.RegisterStates -end; - - -function TFSXEngineAntiIceFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; -begin - Result := TFSXEngineAntiIceFunctionWorker; -end; - - -{ TFSXFuelPumpFunction } -procedure TFSXFuelPumpFunction.RegisterStates; -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXFuelPumpFunction.RegisterStates -end; - - -function TFSXFuelPumpFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; -begin - Result := TFSXFuelPumpFunctionWorker; -end; - - -{ TFSXDeIceFunction } -procedure TFSXDeIceFunction.RegisterStates; -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXDeIceFunction.RegisterStates -end; - - -function TFSXDeIceFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; -begin - Result := TFSXDeIceFunctionWorker; -end; - - { TFSXLightFunction } function TCustomFSXLightFunction.GetCategoryName: string; begin @@ -390,15 +421,15 @@ begin end; -function TCustomFSXLightFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +function TCustomFSXLightFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; begin Result := TFSXLightStatesFunctionWorker; end; -function TCustomFSXLightFunction.DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; +function TCustomFSXLightFunction.DoCreateWorker(ASettings: ILEDFunctionWorkerSettings; const APreviousState: string): TCustomLEDFunctionWorker; begin - Result := inherited DoCreateWorker(ASettings); + Result := inherited DoCreateWorker(ASettings, APreviousState); (Result as TFSXLightStatesFunctionWorker).StateMask := GetLightMask; end; @@ -459,81 +490,66 @@ begin end; -{ TFSXAutoPilotFunction } -procedure TFSXAutoPilotFunction.RegisterStates; +procedure TCustomFSXAutoPilotFunction.RegisterStates; begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotFunction.RegisterStates + RegisterState(TLEDState.Create(FSXStateUIDAutoPilotNotAvailable, FSXStateDisplayNameAutoPilotNotAvailable, lcOff)); + RegisterState(TLEDState.Create(FSXStateUIDOn, FSXStateDisplayNameOn, lcGreen)); + RegisterState(TLEDState.Create(FSXStateUIDOff, FSXStateDisplayNameOff, lcOff)); end; -function TFSXAutoPilotFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +{ TFSXAutoPilotFunction } +function TFSXAutoPilotFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; begin Result := TFSXAutoPilotFunctionWorker; end; { TFSXAutoPilotHeadingFunction } -procedure TFSXAutoPilotHeadingFunction.RegisterStates; -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotHeadingFunction.RegisterState -end; - - -function TFSXAutoPilotHeadingFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +function TFSXAutoPilotHeadingFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; begin Result := TFSXAutoPilotHeadingFunctionWorker; end; { TFSXAutoPilotApproachFunction } -procedure TFSXAutoPilotApproachFunction.RegisterStates; -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotApproachFunction.RegisterStates -end; - - -function TFSXAutoPilotApproachFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +function TFSXAutoPilotApproachFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; begin Result := TFSXAutoPilotApproachFunctionWorker; end; { TFSXAutoPilotBackcourseFunction } -procedure TFSXAutoPilotBackcourseFunction.RegisterStates; -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotBackcourseFunction.RegisterStates -end; - - -function TFSXAutoPilotBackcourseFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +function TFSXAutoPilotBackcourseFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; begin Result := TFSXAutoPilotBackcourseFunctionWorker; end; { TFSXAutoPilotAltitudeFunction } -procedure TFSXAutoPilotAltitudeFunction.RegisterStates; -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotAltitudeFunction.RegisterStates -end; - - -function TFSXAutoPilotAltitudeFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +function TFSXAutoPilotAltitudeFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; begin Result := TFSXAutoPilotAltitudeFunctionWorker; end; { TFSXAutoPilotNavFunction } -procedure TFSXAutoPilotNavFunction.RegisterStates; -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotNavFunction.RegisterStates -end; - - -function TFSXAutoPilotNavFunction.GetWorkerClass: TCustomLEDFunctionWorkerClass; +function TFSXAutoPilotNavFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; begin Result := TFSXAutoPilotNavFunctionWorker; end; + +{ TFSXAvionicsMasterFunction } +function TFSXAvionicsMasterFunction.GetCategoryName: string; +begin + Result := FSXCategoryRadios; +end; + + +function TFSXAvionicsMasterFunction.GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; +begin + Result := TFSXAvionicsMasterFunctionWorker; +end; + end. diff --git a/G940LEDControl/Units/FSXLEDFunctionProvider.pas b/G940LEDControl/Units/FSXLEDFunctionProvider.pas index e02864f..84ea46a 100644 --- a/G940LEDControl/Units/FSXLEDFunctionProvider.pas +++ b/G940LEDControl/Units/FSXLEDFunctionProvider.pas @@ -41,7 +41,7 @@ type FDisplayName: string; FUID: string; protected - function DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; override; + function DoCreateWorker(ASettings: ILEDFunctionWorkerSettings; const APreviousState: string = ''): TCustomLEDFunctionWorker; override; property Provider: TFSXLEDFunctionProvider read FProvider; protected @@ -56,30 +56,23 @@ type TCustomFSXFunctionClass = class of TCustomFSXFunction; - TCustomFSXFunctionWorker = class(TCustomLEDFunctionWorker) + TCustomFSXFunctionWorker = class(TCustomLEDMultiStateFunctionWorker) private FDataHandler: IFSXSimConnectDataHandler; FDefinitionID: Cardinal; FSimConnect: IFSXSimConnect; - FCurrentStateLock: TCriticalSection; - FCurrentState: ILEDStateWorker; protected - procedure RegisterStates(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); override; procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); virtual; abstract; - procedure SetCurrentState(const AUID: string; ANotifyObservers: Boolean = True); overload; virtual; - procedure SetCurrentState(AState: ILEDStateWorker; ANotifyObservers: Boolean = True); overload; virtual; procedure SetSimConnect(const Value: IFSXSimConnect); virtual; property DataHandler: IFSXSimConnectDataHandler read FDataHandler; property DefinitionID: Cardinal read FDefinitionID; property SimConnect: IFSXSimConnect read FSimConnect write SetSimConnect; protected - function GetCurrentState: ILEDStateWorker; override; - procedure HandleData(AData: Pointer); virtual; abstract; public - constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); override; + constructor Create(const AProviderUID, AFunctionUID: string; AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings; const APreviousState: string = ''); override; destructor Destroy; override; end; @@ -129,21 +122,23 @@ end; procedure TFSXLEDFunctionProvider.RegisterFunctions; begin - { Misc } - RegisterFunction(TFSXAvionicsMasterFunction.Create( Self, FSXFunctionDisplayNameAvionicsMaster, FSXFunctionUIDAvionicsMaster)); + { Systems } RegisterFunction(TFSXBatteryMasterFunction.Create( Self, FSXFunctionDisplayNameBatteryMaster, FSXFunctionUIDBatteryMaster)); RegisterFunction(TFSXDeIceFunction.Create( Self, FSXFunctionDisplayNameDeIce, FSXFunctionUIDDeIce)); - RegisterFunction(TFSXEngineAntiIceFunction.Create( Self, FSXFunctionDisplayNameEngineAntiIce, FSXFunctionUIDEngineAntiIce)); - RegisterFunction(TFSXEngineFunction.Create( Self, FSXFunctionDisplayNameEngine, FSXFunctionUIDEngine)); RegisterFunction(TFSXExitDoorFunction.Create( Self, FSXFunctionDisplayNameExitDoor, FSXFunctionUIDExitDoor)); - RegisterFunction(TFSXFlapsFunction.Create( Self, FSXFunctionDisplayNameFlaps, FSXFunctionUIDFlaps)); - RegisterFunction(TFSXFuelPumpFunction.Create( Self, FSXFunctionDisplayNameFuelPump, FSXFunctionUIDFuelPump)); RegisterFunction(TFSXGearFunction.Create( Self, FSXFunctionDisplayNameGear, FSXFunctionUIDGear)); RegisterFunction(TFSXParkingBrakeFunction.Create( Self, FSXFunctionDisplayNameParkingBrake, FSXFunctionUIDParkingBrake)); RegisterFunction(TFSXPressDumpSwitchFunction.Create( Self, FSXFunctionDisplayNamePressDumpSwitch, FSXFunctionUIDPressDumpSwitch)); - RegisterFunction(TFSXSpoilersFunction.Create( Self, FSXFunctionDisplayNameSpoilers, FSXFunctionUIDSpoilers)); RegisterFunction(TFSXTailHookFunction.Create( Self, FSXFunctionDisplayNameTailHook, FSXFunctionUIDTailHook)); + { Engines } + RegisterFunction(TFSXEngineAntiIceFunction.Create( Self, FSXFunctionDisplayNameEngineAntiIce, FSXFunctionUIDEngineAntiIce)); + RegisterFunction(TFSXEngineFunction.Create( Self, FSXFunctionDisplayNameEngine, FSXFunctionUIDEngine)); + + { Control surfaces } + RegisterFunction(TFSXFlapsFunction.Create( Self, FSXFunctionDisplayNameFlaps, FSXFunctionUIDFlaps)); + RegisterFunction(TFSXSpoilersFunction.Create( Self, FSXFunctionDisplayNameSpoilers, FSXFunctionUIDSpoilers)); + { Lights } RegisterFunction(TFSXBeaconLightsFunction.Create( Self, FSXFunctionDisplayNameBeaconLights, FSXFunctionUIDBeaconLights)); RegisterFunction(TFSXInstrumentLightsFunction.Create( Self, FSXFunctionDisplayNameInstrumentLights, FSXFunctionUIDInstrumentLights)); @@ -160,6 +155,9 @@ begin RegisterFunction(TFSXAutoPilotBackcourseFunction.Create(Self, FSXFunctionDisplayNameAutoPilotBackcourse, FSXFunctionUIDAutoPilotBackcourse)); RegisterFunction(TFSXAutoPilotHeadingFunction.Create( Self, FSXFunctionDisplayNameAutoPilotHeading, FSXFunctionUIDAutoPilotHeading)); RegisterFunction(TFSXAutoPilotNavFunction.Create( Self, FSXFunctionDisplayNameAutoPilotNav, FSXFunctionUIDAutoPilotNav)); + + { Radios } + RegisterFunction(TFSXAvionicsMasterFunction.Create( Self, FSXFunctionDisplayNameAvionicsMaster, FSXFunctionUIDAvionicsMaster)); end; @@ -202,7 +200,7 @@ end; { TCustomFSXFunction } constructor TCustomFSXFunction.Create(AProvider: TFSXLEDFunctionProvider; const ADisplayName, AUID: string); begin - inherited Create; + inherited Create(AProvider.GetUID); FProvider := AProvider; FDisplayName := ADisplayName; @@ -210,9 +208,9 @@ begin end; -function TCustomFSXFunction.DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; +function TCustomFSXFunction.DoCreateWorker(ASettings: ILEDFunctionWorkerSettings; const APreviousState: string): TCustomLEDFunctionWorker; begin - Result := inherited DoCreateWorker(ASettings); + Result := inherited DoCreateWorker(ASettings, APreviousState); (Result as TCustomFSXFunctionWorker).SimConnect := Provider.GetSimConnect; end; @@ -237,23 +235,19 @@ end; { TCustomFSXFunctionWorker } -constructor TCustomFSXFunctionWorker.Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); +constructor TCustomFSXFunctionWorker.Create(const AProviderUID, AFunctionUID: string; AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings; const APreviousState: string); begin - FCurrentStateLock := TCriticalSection.Create; - { We can't pass ourselves as the Data Handler, as it would keep a reference to this worker from the SimConnect interface. That'd mean the worker never gets destroyed, and SimConnect never shuts down. Hence this proxy class. } FDataHandler := TCustomFSXFunctionWorkerDataHandler.Create(Self); - inherited Create(AStates, ASettings); + inherited Create(AProviderUID, AFunctionUID, AStates, ASettings, APreviousState); end; destructor TCustomFSXFunctionWorker.Destroy; begin - FreeAndNil(FCurrentStateLock); - if DefinitionID <> 0 then SimConnect.RemoveDefinition(DefinitionID, DataHandler); @@ -261,50 +255,6 @@ begin end; -procedure TCustomFSXFunctionWorker.RegisterStates(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); -begin - inherited RegisterStates(AStates, ASettings); - - { Make sure we have a default state } - if States.Count > 0 then - SetCurrentState((States[0] as ILEDStateWorker), False); -end; - - -function TCustomFSXFunctionWorker.GetCurrentState: ILEDStateWorker; -begin - FCurrentStateLock.Acquire; - try - Result := FCurrentState; - finally - FCurrentStateLock.Release; - end; -end; - - -procedure TCustomFSXFunctionWorker.SetCurrentState(const AUID: string; ANotifyObservers: Boolean); -begin - SetCurrentState(FindState(AUID), ANotifyObservers); -end; - - -procedure TCustomFSXFunctionWorker.SetCurrentState(AState: ILEDStateWorker; ANotifyObservers: Boolean); -begin - FCurrentStateLock.Acquire; - try - if AState <> FCurrentState then - begin - FCurrentState := AState; - - if ANotifyObservers then - NotifyObservers; - end; - finally - FCurrentStateLock.Release; - end; -end; - - procedure TCustomFSXFunctionWorker.SetSimConnect(const Value: IFSXSimConnect); var definition: IFSXSimConnectDefinition; diff --git a/G940LEDControl/Units/FSXLEDFunctionWorker.pas b/G940LEDControl/Units/FSXLEDFunctionWorker.pas index 711a298..634145a 100644 --- a/G940LEDControl/Units/FSXLEDFunctionWorker.pas +++ b/G940LEDControl/Units/FSXLEDFunctionWorker.pas @@ -7,8 +7,20 @@ uses type - { Misc } - TFSXEngineFunctionWorker = class(TCustomFSXFunctionWorker) + { Systems } + TFSXBatteryMasterFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + TFSXDeIceFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + TFSXExitDoorFunctionWorker = class(TCustomFSXFunctionWorker) protected procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; procedure HandleData(AData: Pointer); override; @@ -26,7 +38,7 @@ type procedure HandleData(AData: Pointer); override; end; - TFSXExitDoorFunctionWorker = class(TCustomFSXFunctionWorker) + TFSXPressDumpSwitchFunctionWorker = class(TCustomFSXFunctionWorker) protected procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; procedure HandleData(AData: Pointer); override; @@ -39,12 +51,26 @@ type end; - TFSXFlapsFunctionWorker = class(TCustomFSXFunctionWorker) + { Engines } + TFSXEngineAntiIceFunctionWorker = class(TCustomFSXFunctionWorker) protected procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; procedure HandleData(AData: Pointer); override; end; + TFSXEngineFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; + + + { Control surfaces } + TFSXFlapsFunctionWorker = class(TCustomFSXFunctionWorker) + protected + procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; + procedure HandleData(AData: Pointer); override; + end; TFSXSpoilersFunctionWorker = class(TCustomFSXFunctionWorker) protected @@ -53,48 +79,7 @@ type end; - TFSXBatteryMasterFunctionWorker = class(TCustomFSXFunctionWorker) - protected - procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; - procedure HandleData(AData: Pointer); override; - end; - - - TFSXAvionicsMasterFunctionWorker = class(TCustomFSXFunctionWorker) - protected - procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; - procedure HandleData(AData: Pointer); override; - end; - - - TFSXPressDumpSwitchFunctionWorker = class(TCustomFSXFunctionWorker) - protected - procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; - procedure HandleData(AData: Pointer); override; - end; - - - TFSXEngineAntiIceFunctionWorker = class(TCustomFSXFunctionWorker) - protected - procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; - procedure HandleData(AData: Pointer); override; - end; - - - TFSXFuelPumpFunctionWorker = class(TCustomFSXFunctionWorker) - protected - procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; - procedure HandleData(AData: Pointer); override; - end; - - - TFSXDeIceFunctionWorker = class(TCustomFSXFunctionWorker) - protected - procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; - procedure HandleData(AData: Pointer); override; - end; - - + { Lights } TFSXLightStatesFunctionWorker = class(TCustomFSXFunctionWorker) private FStateMask: Integer; @@ -106,42 +91,67 @@ type end; - TFSXAutoPilotFunctionWorker = class(TCustomFSXFunctionWorker) - protected - procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; - procedure HandleData(AData: Pointer); override; + { Autopilot } + PAutoPilotData = ^TAutoPilotData; + TAutoPilotData = packed record + AutoPilotAvailable: Cardinal; + AutoPilotMaster: Cardinal; + AutoPilotHeading: Cardinal; + AutoPilotApproach: Cardinal; + AutoPilotBackcourse: Cardinal; + AutoPilotAltitude: Cardinal; + AutoPilotNav: Cardinal; end; - TFSXAutoPilotHeadingFunctionWorker = class(TCustomFSXFunctionWorker) + TCustomFSXAutoPilotFunctionWorker = class(TCustomFSXFunctionWorker) protected procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; procedure HandleData(AData: Pointer); override; + + procedure SetOnOffState(AState: Cardinal); virtual; + procedure HandleAutoPilotData(AData: PAutoPilotData); virtual; abstract; end; - TFSXAutoPilotApproachFunctionWorker = class(TCustomFSXFunctionWorker) + TFSXAutoPilotFunctionWorker = class(TCustomFSXAutoPilotFunctionWorker) protected - procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; - procedure HandleData(AData: Pointer); override; + procedure HandleAutoPilotData(AData: PAutoPilotData); override; end; - TFSXAutoPilotBackcourseFunctionWorker = class(TCustomFSXFunctionWorker) + TFSXAutoPilotHeadingFunctionWorker = class(TCustomFSXAutoPilotFunctionWorker) protected - procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; - procedure HandleData(AData: Pointer); override; + procedure HandleAutoPilotData(AData: PAutoPilotData); override; end; - TFSXAutoPilotAltitudeFunctionWorker = class(TCustomFSXFunctionWorker) + TFSXAutoPilotApproachFunctionWorker = class(TCustomFSXAutoPilotFunctionWorker) protected - procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; - procedure HandleData(AData: Pointer); override; + procedure HandleAutoPilotData(AData: PAutoPilotData); override; end; - TFSXAutoPilotNavFunctionWorker = class(TCustomFSXFunctionWorker) + TFSXAutoPilotBackcourseFunctionWorker = class(TCustomFSXAutoPilotFunctionWorker) + protected + procedure HandleAutoPilotData(AData: PAutoPilotData); override; + end; + + + TFSXAutoPilotAltitudeFunctionWorker = class(TCustomFSXAutoPilotFunctionWorker) + protected + procedure HandleAutoPilotData(AData: PAutoPilotData); override; + end; + + + TFSXAutoPilotNavFunctionWorker = class(TCustomFSXAutoPilotFunctionWorker) + protected + procedure HandleAutoPilotData(AData: PAutoPilotData); override; + end; + + + { Radios } + TFSXAvionicsMasterFunctionWorker = class(TCustomFSXFunctionWorker) protected procedure RegisterVariables(ADefinition: IFSXSimConnectDefinition); override; procedure HandleData(AData: Pointer); override; @@ -158,6 +168,200 @@ uses SimConnect; +{ TFSXBatteryMasterFunctionWorker } +procedure TFSXBatteryMasterFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + ADefinition.AddVariable('ELECTRICAL MASTER BATTERY', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); +end; + + +procedure TFSXBatteryMasterFunctionWorker.HandleData(AData: Pointer); +begin + if PCardinal(AData)^ <> 0 then + SetCurrentState(FSXStateUIDOn) + else + SetCurrentState(FSXStateUIDOff); +end; + + +{ TFSXDeIceFunctionWorker } +procedure TFSXDeIceFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + ADefinition.AddVariable('STRUCTURAL DEICE SWITCH', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); +end; + + +procedure TFSXDeIceFunctionWorker.HandleData(AData: Pointer); +begin + if PCardinal(AData)^ <> 0 then + SetCurrentState(FSXStateUIDOn) + else + SetCurrentState(FSXStateUIDOff); +end; + + +{ TFSXExitDoorFunctionWorker } +procedure TFSXExitDoorFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + ADefinition.AddVariable('CANOPY OPEN', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); +end; + + +procedure TFSXExitDoorFunctionWorker.HandleData(AData: Pointer); +begin + case Trunc(PDouble(AData)^) of + 0..5: SetCurrentState(FSXStateUIDExitDoorClosed); + 95..100: SetCurrentState(FSXStateUIDExitDoorOpen); + else SetCurrentState(FSXStateUIDExitDoorBetween); + end; +end; + + +{ TFSXGearFunctionWorker } +procedure TFSXGearFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + ADefinition.AddVariable('IS GEAR RETRACTABLE', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('GEAR TOTAL PCT EXTENDED', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); + ADefinition.AddVariable('GEAR DAMAGE BY SPEED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('GEAR SPEED EXCEEDED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); +end; + + +procedure TFSXGearFunctionWorker.HandleData(AData: Pointer); +type + PGearData = ^TGearData; + TGearData = packed record + IsGearRetractable: Cardinal; + TotalPctExtended: Double; + DamageBySpeed: Integer; + SpeedExceeded: Integer; + end; + +var + gearData: PGearData; + +begin + gearData := AData; + + if gearData^.DamageBySpeed <> 0 then + SetCurrentState(FSXStateUIDGearDamageBySpeed) + + else if gearData^.SpeedExceeded <> 0 then + SetCurrentState(FSXStateUIDGearSpeedExceeded) + + else if gearData^.IsGearRetractable <> 0 then + begin + case Trunc(gearData ^.TotalPctExtended * 100) of + 0: SetCurrentState(FSXStateUIDGearRetracted); + 95..100: SetCurrentState(FSXStateUIDGearExtended); + else SetCurrentState(FSXStateUIDGearBetween); + end; + end else + SetCurrentState(FSXStateUIDGearNotRetractable); +end; + + +{ TFSXParkingBrakeFunctionWorker } +procedure TFSXParkingBrakeFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + ADefinition.AddVariable('BRAKE PARKING INDICATOR', FSX_UNIT_BOOL, SIMCONNECT_DATATYPE_INT32); +end; + + +procedure TFSXParkingBrakeFunctionWorker.HandleData(AData: Pointer); +begin + if PCardinal(AData)^ <> 0 then + SetCurrentState(FSXStateUIDOn) + else + SetCurrentState(FSXStateUIDOff); +end; + + +{ TFSXPressDumpSwitchFunctionWorker } +procedure TFSXPressDumpSwitchFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + ADefinition.AddVariable('PRESSURIZATION DUMP SWITCH', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); +end; + + +procedure TFSXPressDumpSwitchFunctionWorker.HandleData(AData: Pointer); +begin + if PCardinal(AData)^ <> 0 then + SetCurrentState(FSXStateUIDOn) + else + SetCurrentState(FSXStateUIDOff); +end; + + +{ TFSXTailHookFunctionWorker } +procedure TFSXTailHookFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +begin + ADefinition.AddVariable('TAILHOOK POSITION', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); +end; + + +procedure TFSXTailHookFunctionWorker.HandleData(AData: Pointer); +begin + case Trunc(PDouble(AData)^) of + 0..5: SetCurrentState(FSXStateUIDTailHookRetracted); + 95..100: SetCurrentState(FSXStateUIDTailHookBetween); + else SetCurrentState(FSXStateUIDTailHookExtended); + end; +end; + + +{ TFSXEngineAntiIceFunctionWorker } +procedure TFSXEngineAntiIceFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +var + engineIndex: Integer; + +begin + ADefinition.AddVariable('NUMBER OF ENGINES', FSX_UNIT_NUMBER, SIMCONNECT_DATAType_INT32); + + for engineIndex := 1 to FSX_MAX_ENGINES do + ADefinition.AddVariable(Format('ENG ANTI ICE:%d', [engineIndex]), FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); +end; + + +procedure TFSXEngineAntiIceFunctionWorker.HandleData(AData: Pointer); +type + PAntiIceData = ^TAntiIceData; + TAntiIceData = packed record + NumberOfEngines: Integer; + EngineAntiIce: array[1..FSX_MAX_ENGINES] of Integer; + end; + +var + antiIceData: PAntiIceData; + engineCount: Integer; + antiIceCount: Integer; + engineIndex: Integer; + +begin + antiIceData := AData; + engineCount := Min(antiIceData^.NumberOfEngines, FSX_MAX_ENGINES); + antiIceCount := 0; + + for engineIndex := 1 to engineCount do + begin + if antiIceData^.EngineAntiIce[engineIndex] <> 0 then + Inc(antiIceCount); + end; + + if engineCount > 0 then + begin + if antiIceCount = 0 then + SetCurrentState(FSXStateUIDEngineAntiIceNone) + else if antiIceCount = engineCount then + SetCurrentState(FSXStateUIDEngineAntiIceAll) + else + SetCurrentState(FSXStateUIDEngineAntiIcePartial); + end else + SetCurrentState(FSXStateUIDEngineAntiIceNoEngines); +end; + + + { TFSXEngineFunctionWorker } procedure TFSXEngineFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); var @@ -236,105 +440,13 @@ begin end; -{ TFSXGearFunctionWorker } -procedure TFSXGearFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); -begin - ADefinition.AddVariable('IS GEAR RETRACTABLE', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); - ADefinition.AddVariable('GEAR TOTAL PCT EXTENDED', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); - ADefinition.AddVariable('GEAR DAMAGE BY SPEED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); - ADefinition.AddVariable('GEAR SPEED EXCEEDED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); -end; - - -procedure TFSXGearFunctionWorker.HandleData(AData: Pointer); -type - PGearData = ^TGearData; - TGearData = packed record - IsGearRetractable: Cardinal; - TotalPctExtended: Double; - DamageBySpeed: Integer; - SpeedExceeded: Integer; - end; - -var - gearData: PGearData; - -begin - gearData := AData; - - if gearData^.DamageBySpeed <> 0 then - SetCurrentState(FSXStateUIDGearDamageBySpeed) - - else if gearData^.SpeedExceeded <> 0 then - SetCurrentState(FSXStateUIDGearSpeedExceeded) - - else if gearData^.IsGearRetractable <> 0 then - begin - case Trunc(gearData ^.TotalPctExtended * 100) of - 0: SetCurrentState(FSXStateUIDGearRetracted); - 95..100: SetCurrentState(FSXStateUIDGearExtended); - else SetCurrentState(FSXStateUIDGearBetween); - end; - end else - SetCurrentState(FSXStateUIDGearNotRetractable); -end; - - -{ TFSXParkingBrakeFunctionWorker } -procedure TFSXParkingBrakeFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); -begin - ADefinition.AddVariable('BRAKE PARKING INDICATOR', FSX_UNIT_BOOL, SIMCONNECT_DATATYPE_INT32); -end; - - -procedure TFSXParkingBrakeFunctionWorker.HandleData(AData: Pointer); -begin - if PCardinal(AData)^ <> 0 then - SetCurrentState(FSXStateUIDOn) - else - SetCurrentState(FSXStateUIDOff); -end; - - -{ TFSXExitDoorFunctionWorker } -procedure TFSXExitDoorFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); -begin - ADefinition.AddVariable('CANOPY OPEN', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); -end; - - -procedure TFSXExitDoorFunctionWorker.HandleData(AData: Pointer); -begin - case Trunc(PDouble(AData)^) of - 0..5: SetCurrentState(FSXStateUIDExitDoorClosed); - 95..100: SetCurrentState(FSXStateUIDExitDoorOpen); - else SetCurrentState(FSXStateUIDExitDoorBetween); - end; -end; - - -{ TFSXTailHookFunctionWorker } -procedure TFSXTailHookFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); -begin - ADefinition.AddVariable('TAILHOOK POSITION', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); -end; - - -procedure TFSXTailHookFunctionWorker.HandleData(AData: Pointer); -begin - case Trunc(PDouble(AData)^) of - 0..5: SetCurrentState(FSXStateUIDTailHookRetracted); - 95..100: SetCurrentState(FSXStateUIDTailHookBetween); - else SetCurrentState(FSXStateUIDTailHookExtended); - end; -end; - - { TFSXFlapsFunctionWorker } procedure TFSXFlapsFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); begin ADefinition.AddVariable('FLAPS AVAILABLE', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); ADefinition.AddVariable('FLAPS HANDLE PERCENT', FSX_UNIT_PERCENT, SIMCONNECT_DATAType_FLOAT64); + ADefinition.AddVariable('FLAP DAMAGE BY SPEED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('FLAP SPEED EXCEEDED', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); end; @@ -344,6 +456,8 @@ type TFlapsData = packed record FlapsAvailable: Cardinal; FlapsHandlePercent: Double; + DamageBySpeed: Integer; + SpeedExceeded: Integer; end; var @@ -354,11 +468,16 @@ begin if flapsData^.FlapsAvailable <> 0 then begin - case Trunc(flapsData^.FlapsHandlePercent) of - 0..5: SetCurrentState(FSXStateUIDFlapsRetracted); - 95..100: SetCurrentState(FSXStateUIDFlapsExtended); - else SetCurrentState(FSXStateUIDFlapsBetween); - end; + if flapsData^.DamageBySpeed <> 0 then + SetCurrentState(FSXStateUIDFlapsDamageBySpeed) + else if flapsData^.SpeedExceeded <> 0 then + SetCurrentState(FSXStateUIDFlapsSpeedExceeded) + else + case Trunc(flapsData^.FlapsHandlePercent) of + 0..5: SetCurrentState(FSXStateUIDFlapsRetracted); + 95..100: SetCurrentState(FSXStateUIDFlapsExtended); + else SetCurrentState(FSXStateUIDFlapsBetween); + end; end else SetCurrentState(FSXStateUIDFlapsNotAvailable); end; @@ -398,84 +517,6 @@ begin end; -{ TFSXBatteryMasterFunctionWorker } -procedure TFSXBatteryMasterFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXBatteryMasterFunctionWorker.RegisterVariables -end; - - -procedure TFSXBatteryMasterFunctionWorker.HandleData(AData: Pointer); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXBatteryMasterFunctionWorker.HandleData -end; - - -{ TFSXAvionicsMasterFunctionWorker } -procedure TFSXAvionicsMasterFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAvionicsMasterFunctionWorker.RegisterVariables -end; - - -procedure TFSXAvionicsMasterFunctionWorker.HandleData(AData: Pointer); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAvionicsMasterFunctionWorker.HandleData -end; - - -{ TFSXPressDumpSwitchFunctionWorker } -procedure TFSXPressDumpSwitchFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXPressDumpSwitchFunctionWorker.RegisterVariables -end; - - -procedure TFSXPressDumpSwitchFunctionWorker.HandleData(AData: Pointer); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXPressDumpSwitchFunctionWorker.HandleData -end; - - -{ TFSXEngineAntiIceFunctionWorker } -procedure TFSXEngineAntiIceFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXEngineAntiIceFunctionWorker.RegisterVariables -end; - - -procedure TFSXEngineAntiIceFunctionWorker.HandleData(AData: Pointer); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXEngineAntiIceFunctionWorker.HandleData -end; - - -{ TFSXFuelPumpFunctionWorker } -procedure TFSXFuelPumpFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXFuelPumpFunctionWorker.RegisterVariables -end; - - -procedure TFSXFuelPumpFunctionWorker.HandleData(AData: Pointer); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXFuelPumpFunctionWorker.HandleData -end; - - -{ TFSXDeIceFunctionWorker } -procedure TFSXDeIceFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXDeIceFunctionWorker.RegisterVariables -end; - - -procedure TFSXDeIceFunctionWorker.HandleData(AData: Pointer); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXDeIceFunctionWorker.HandleData -end; - - { TFSXLightStatesFunctionWorker } procedure TFSXLightStatesFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); begin @@ -492,81 +533,97 @@ begin end; -{ TFSXAutoPilotFunctionWorker } -procedure TFSXAutoPilotFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +{ TCustomFSXAutoPilotFunctionWorker } +procedure TCustomFSXAutoPilotFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotFunctionWorker.RegisterVariables + ADefinition.AddVariable('AUTOPILOT AVAILABLE', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('AUTOPILOT MASTER', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('AUTOPILOT HEADING LOCK', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('AUTOPILOT APPROACH HOLD', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('AUTOPILOT BACKCOURSE HOLD', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('AUTOPILOT ALTITUDE LOCK', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); + ADefinition.AddVariable('AUTOPILOT NAV1 LOCK', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); end; -procedure TFSXAutoPilotFunctionWorker.HandleData(AData: Pointer); +procedure TCustomFSXAutoPilotFunctionWorker.HandleData(AData: Pointer); +var + autoPilotData: PAutoPilotData; + begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotFunctionWorker.HandleData + autoPilotData := AData; + + if autoPilotData^.AutoPilotAvailable <> 0 then + HandleAutoPilotData(autoPilotData) + else + SetCurrentState(FSXStateUIDOff); +end; + + +procedure TCustomFSXAutoPilotFunctionWorker.SetOnOffState(AState: Cardinal); +begin + if AState <> 0 then + SetCurrentState(FSXStateUIDOn) + else + SetCurrentState(FSXStateUIDOff); +end; + + +{ TFSXAutoPilotFunctionWorker } +procedure TFSXAutoPilotFunctionWorker.HandleAutoPilotData(AData: PAutoPilotData); +begin + SetOnOffState(AData^.AutoPilotMaster); end; { TFSXAutoPilotHeadingFunctionWorker } -procedure TFSXAutoPilotHeadingFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +procedure TFSXAutoPilotHeadingFunctionWorker.HandleAutoPilotData(AData: PAutoPilotData); begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotHeadingFunctionWorker.RegisterVariables -end; - - -procedure TFSXAutoPilotHeadingFunctionWorker.HandleData(AData: Pointer); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotHeadingFunctionWorker.HandleData + SetOnOffState(AData^.AutoPilotHeading); end; { TFSXAutoPilotApproachFunctionWorker } -procedure TFSXAutoPilotApproachFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +procedure TFSXAutoPilotApproachFunctionWorker.HandleAutoPilotData(AData: PAutoPilotData); begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotApproachFunctionWorker.RegisterVariables -end; - - -procedure TFSXAutoPilotApproachFunctionWorker.HandleData(AData: Pointer); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotApproachFunctionWorker.HandleData + SetOnOffState(AData^.AutoPilotApproach); end; { TFSXAutoPilotBackcourseFunctionWorker } -procedure TFSXAutoPilotBackcourseFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +procedure TFSXAutoPilotBackcourseFunctionWorker.HandleAutoPilotData(AData: PAutoPilotData); begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotBackcourseFunctionWorker.RegisterVariables -end; - - -procedure TFSXAutoPilotBackcourseFunctionWorker.HandleData(AData: Pointer); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotBackcourseFunctionWorker.HandleData + SetOnOffState(AData^.AutoPilotBackcourse); end; { TFSXAutoPilotAltitudeFunctionWorker } -procedure TFSXAutoPilotAltitudeFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +procedure TFSXAutoPilotAltitudeFunctionWorker.HandleAutoPilotData(AData: PAutoPilotData); begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotAltitudeFunctionWorker.RegisterVariables -end; - - -procedure TFSXAutoPilotAltitudeFunctionWorker.HandleData(AData: Pointer); -begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotAltitudeFunctionWorker.HandleData + SetOnOffState(AData^.AutoPilotAltitude); end; { TFSXAutoPilotNavFunctionWorker } -procedure TFSXAutoPilotNavFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); +procedure TFSXAutoPilotNavFunctionWorker.HandleAutoPilotData(AData: PAutoPilotData); begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotNavFunctionWorker.RegisterVariables + SetOnOffState(AData^.AutoPilotNav); end; -procedure TFSXAutoPilotNavFunctionWorker.HandleData(AData: Pointer); +{ TFSXAvionicsMasterFunctionWorker } +procedure TFSXAvionicsMasterFunctionWorker.RegisterVariables(ADefinition: IFSXSimConnectDefinition); begin - // #ToDo1 -cEmpty -oMvR: 22-2-2013: TFSXAutoPilotNavFunctionWorker.HandleData + ADefinition.AddVariable('AVIONICS MASTER SWITCH', FSX_UNIT_BOOL, SIMCONNECT_DATAType_INT32); +end; + + +procedure TFSXAvionicsMasterFunctionWorker.HandleData(AData: Pointer); +begin + if PCardinal(AData)^ <> 0 then + SetCurrentState(FSXStateUIDOn) + else + SetCurrentState(FSXStateUIDOff); end; end. diff --git a/G940LEDControl/Units/FSXResources.pas b/G940LEDControl/Units/FSXResources.pas index 1e9a7c7..04eed5d 100644 --- a/G940LEDControl/Units/FSXResources.pas +++ b/G940LEDControl/Units/FSXResources.pas @@ -6,8 +6,12 @@ const FSXProviderUID = 'fsx'; FSXCategory = 'Flight Simulator X'; + FSXCategorySystems = FSXCategory + ' - Systems'; + FSXCategoryEngines = FSXCategory + ' - Engines'; + FSXCategoryControlSurfaces = FSXCategory + ' - Control surfaces'; FSXCategoryLights = FSXCategory + ' - Lights'; FSXCategoryAutoPilot = FSXCategory + ' - Autopilot'; + FSXCategoryRadios = FSXCategory + ' - Radios'; FSXStateUIDOn = 'on'; FSXStateUIDOff = 'off'; @@ -73,9 +77,11 @@ const FSXFunctionUIDRecognitionLights = 'recognitionLights'; FSXFunctionDisplayNameRecognitionLights = 'Recognition lights'; + FSXFunctionUIDParkingBrake = 'parkingBrake'; FSXFunctionDisplayNameParkingBrake = 'Parking brake'; + FSXFunctionUIDExitDoor = 'exitDoor'; FSXFunctionDisplayNameExitDoor = 'Exit door'; @@ -87,6 +93,7 @@ const FSXStateDisplayNameExitDoorBetween = 'Opening / closing'; FSXStateDisplayNameExitDoorOpen = 'Open'; + FSXFunctionUIDTailHook = 'tailHook'; FSXFunctionDisplayNameTailHook = 'Tail hook'; @@ -106,11 +113,15 @@ const FSXStateUIDFlapsRetracted = 'retracted'; FSXStateUIDFlapsBetween = 'between'; FSXStateUIDFlapsExtended = 'extended'; + FSXStateUIDFlapsSpeedExceeded = 'speedExceeded'; + FSXStateUIDFlapsDamageBySpeed = 'damageBySpeed'; FSXStateDisplayNameFlapsNotAvailable = 'No flaps'; FSXStateDisplayNameFlapsRetracted = 'Retracted'; FSXStateDisplayNameFlapsBetween = 'Extending / retracting'; FSXStateDisplayNameFlapsExtended = 'Extended'; + FSXStateDisplayNameFlapsSpeedExceeded = 'Speed exceeded'; + FSXStateDisplayNameFlapsDamageBySpeed = 'Damage by speed'; FSXFunctionUIDSpoilers = 'spoilers'; @@ -130,21 +141,35 @@ const FSXFunctionUIDBatteryMaster = 'batteryMaster'; FSXFunctionDisplayNameBatteryMaster = 'Battery master'; + FSXFunctionUIDAvionicsMaster = 'avionicsMaster'; FSXFunctionDisplayNameAvionicsMaster = 'Avionics master'; + FSXFunctionUIDPressDumpSwitch = 'pressurizationDumpSwitch'; FSXFunctionDisplayNamePressDumpSwitch = 'Pressurization dump switch'; FSXFunctionUIDEngineAntiIce = 'engineAntiIce'; FSXFunctionDisplayNameEngineAntiIce = 'Engine anti-ice'; - FSXFunctionUIDFuelPump = 'fuelPump'; - FSXFunctionDisplayNameFuelPump = 'Fuel pump'; + FSXStateUIDEngineAntiIceNoEngines = 'noEngines'; + FSXStateUIDEngineAntiIceAll = 'all'; + FSXStateUIDEngineAntiIcePartial = 'partial'; + FSXStateUIDEngineAntiIceNone = 'none'; + + FSXStateDisplayNameEngineAntiIceNoEngines = 'No engines'; + FSXStateDisplayNameEngineAntiIceAll = 'All'; + FSXStateDisplayNameEngineAntiIcePartial = 'Partial'; + FSXStateDisplayNameEngineAntiIceNone = 'None'; + FSXFunctionUIDDeIce = 'structuralDeIce'; FSXFunctionDisplayNameDeIce = 'De-ice'; + + FSXStateUIDAutoPilotNotAvailable = 'notAvailable'; + FSXStateDisplayNameAutoPilotNotAvailable = 'Not available'; + FSXFunctionUIDAutoPilot = 'autoPilotMaster'; FSXFunctionDisplayNameAutoPilot = 'Autopilot master'; diff --git a/G940LEDControl/Units/FSXSimConnectClient.pas b/G940LEDControl/Units/FSXSimConnectClient.pas index bfd34e7..25043a0 100644 --- a/G940LEDControl/Units/FSXSimConnectClient.pas +++ b/G940LEDControl/Units/FSXSimConnectClient.pas @@ -43,7 +43,8 @@ uses OtlCommon, SimConnect, - FSXResources; + FSXResources, + FSXSimConnectStateMonitor; const @@ -68,7 +69,7 @@ type destructor Destroy; override; procedure Attach(ADataHandler: IFSXSimConnectDataHandler); - procedure Detach(ADataHandler: IFSXSimConnectDataHandler); + function Detach(ADataHandler: IFSXSimConnectDataHandler): Integer; procedure HandleData(AData: Pointer); @@ -76,7 +77,10 @@ type end; - TFSXSimConnectDefinitionMap = TDictionary; + TFSXSimConnectDefinitionMap = class(TObjectDictionary) + public + constructor Create(ACapacity: Integer = 0); reintroduce; + end; TFSXSimConnectClient = class(TOmniWorker) private @@ -98,6 +102,8 @@ type procedure RegisterDefinitions; procedure RegisterDefinition(ADefinitionID: Cardinal; ADefinition: IFSXSimConnectDefinitionAccess); + procedure UpdateDefinition(ADefinitionID: Cardinal); + procedure UnregisterDefinition(ADefinitionID: Cardinal); function SameDefinition(ADefinition1, ADefinition2: IFSXSimConnectDefinitionAccess): Boolean; @@ -306,13 +312,13 @@ end; procedure TFSXSimConnectClient.Cleanup; begin - // #ToDo1 -oMvR: 22-2-2013: unregister definitions + FreeAndNil(FSimConnectDataEvent); + FreeAndNil(FDefinitions); if SimConnectHandle <> 0 then SimConnect_Close(SimConnectHandle); - FreeAndNil(FSimConnectDataEvent); - FreeAndNil(FDefinitions); + TFSXSimConnectStateMonitor.SetCurrentState(scsDisconnected); inherited Cleanup; end; @@ -327,13 +333,19 @@ begin begin if SimConnect_Open(FSimConnectHandle, FSXSimConnectAppName, 0, 0, SimConnectDataEvent.Handle, 0) = S_OK then begin + TFSXSimConnectStateMonitor.SetCurrentState(scsConnected); + Task.ClearTimer(TIMER_TRYSIMCONNECT); RegisterDefinitions; end; end; if SimConnectHandle = 0 then + begin + TFSXSimConnectStateMonitor.SetCurrentState(scsFailed); + Task.SetTimer(TIMER_TRYSIMCONNECT, INTERVAL_TRYSIMCONNECT, TM_TRYSIMCONNECT); + end; end; @@ -364,6 +376,8 @@ begin begin FSimConnectHandle := 0; Task.SetTimer(TIMER_TRYSIMCONNECT, INTERVAL_TRYSIMCONNECT, TM_TRYSIMCONNECT); + + TFSXSimConnectStateMonitor.SetCurrentState(scsDisconnected); end; end; end; @@ -409,6 +423,25 @@ begin end; +procedure TFSXSimConnectClient.UpdateDefinition(ADefinitionID: Cardinal); +begin + if SimConnectHandle <> 0 then + { One-time data update; the RequestID is counted backwards to avoid conflicts with + the FLAG_CHANGED request which is still active } + SimConnect_RequestDataOnSimObject(SimConnectHandle, High(Cardinal) - ADefinitionID, ADefinitionID, + SIMCONNECT_OBJECT_ID_USER, + SIMCONNECT_PERIOD_SIM_FRAME, + 0, 0, 0, 1); +end; + + +procedure TFSXSimConnectClient.UnregisterDefinition(ADefinitionID: Cardinal); +begin + if SimConnectHandle <> 0 then + SimConnect_ClearDataDefinition(SimConnectHandle, ADefinitionID); +end; + + function TFSXSimConnectClient.SameDefinition(ADefinition1, ADefinition2: IFSXSimConnectDefinitionAccess): Boolean; var variableIndex: Integer; @@ -463,6 +496,10 @@ begin begin definitionRef.Attach(addDefinition.DataHandler); addDefinition.DefinitionID := definitionID; + + { Request an update on the definition to update the new worker } + UpdateDefinition(definitionID); + hasDefinition := True; break; end; @@ -488,11 +525,22 @@ end; procedure TFSXSimConnectClient.TMRemoveDefinition(var Msg: TOmniMessage); var removeDefinition: TRemoveDefinitionValue; + definitionRef: TFSXSimConnectDefinitionRef; begin removeDefinition := Msg.MsgData; - // #ToDo1 -oMvR: 22-2-2013: actually remove the definition + if Definitions.ContainsKey(removeDefinition.DefinitionID) then + begin + definitionRef := Definitions[removeDefinition.DefinitionID]; + if definitionRef.Detach(removeDefinition.DataHandler) = 0 then + begin + { Unregister with SimConnect } + UnregisterDefinition(removeDefinition.DefinitionID); + + Definitions.Remove(removeDefinition.DefinitionID); + end; + end; removeDefinition.Signal; end; @@ -538,9 +586,17 @@ begin end; -procedure TFSXSimConnectDefinitionRef.Detach(ADataHandler: IFSXSimConnectDataHandler); +function TFSXSimConnectDefinitionRef.Detach(ADataHandler: IFSXSimConnectDataHandler): Integer; begin DataHandlers.Remove(ADataHandler as IFSXSimConnectDataHandler); + Result := DataHandlers.Count; +end; + + +{ TFSXSimConnectDefinitionMap } +constructor TFSXSimConnectDefinitionMap.Create(ACapacity: Integer); +begin + inherited Create([doOwnsValues], ACapacity); end; diff --git a/G940LEDControl/Units/FSXSimConnectIntf.pas b/G940LEDControl/Units/FSXSimConnectIntf.pas index 8b9bae5..5f235a9 100644 --- a/G940LEDControl/Units/FSXSimConnectIntf.pas +++ b/G940LEDControl/Units/FSXSimConnectIntf.pas @@ -55,6 +55,14 @@ type end; + TFSXSimConnectState = (scsDisconnected, scsConnected, scsFailed); + + IFSXSimConnectStateObserver = interface + ['{0508904F-8189-479D-AF70-E98B00C9D9B2}'] + procedure ObserverStateUpdate(ANewState: TFSXSimConnectState); + end; + + const FSX_UNIT_PERCENT = 'percent'; FSX_UNIT_MASK = 'mask'; diff --git a/G940LEDControl/Units/FSXSimConnectStateMonitor.pas b/G940LEDControl/Units/FSXSimConnectStateMonitor.pas new file mode 100644 index 0000000..60e9aa4 --- /dev/null +++ b/G940LEDControl/Units/FSXSimConnectStateMonitor.pas @@ -0,0 +1,114 @@ +unit FSXSimConnectStateMonitor; + +interface +uses + System.Classes, + System.SyncObjs, + + FSXSimConnectIntf; + + +type + TFSXSimConnectStateMonitor = class(TObject) + private + FObservers: TInterfaceList; + FCurrentStateLock: TCriticalSection; + FCurrentState: TFSXSimConnectState; + + procedure DoSetCurrentState(const Value: TFSXSimConnectState); + protected + property CurrentStateLock: TCriticalSection read FCurrentStateLock; + property Observers: TInterfaceList read FObservers; + public + constructor Create; + destructor Destroy; override; + + class function Instance: TFSXSimConnectStateMonitor; + class procedure SetCurrentState(AState: TFSXSimConnectState); + + procedure Attach(AObserver: IFSXSimConnectStateObserver); + procedure Detach(AObserver: IFSXSimConnectStateObserver); + + property CurrentState: TFSXSimConnectState read FCurrentState write DoSetCurrentState; + end; + + +implementation +uses + System.SysUtils; + + +var + FSXSimConnectStateInstance: TFSXSimConnectStateMonitor; + + +{ TFSXSimConnectState } +class function TFSXSimConnectStateMonitor.Instance: TFSXSimConnectStateMonitor; +begin + Result := FSXSimConnectStateInstance; +end; + + +class procedure TFSXSimConnectStateMonitor.SetCurrentState(AState: TFSXSimConnectState); +begin + Instance.DoSetCurrentState(AState); +end; + + +constructor TFSXSimConnectStateMonitor.Create; +begin + inherited Create; + + FCurrentStateLock := TCriticalSection.Create; + FObservers := TInterfaceList.Create; +end; + + +destructor TFSXSimConnectStateMonitor.Destroy; +begin + FreeAndNil(FObservers); + FreeAndNil(FCurrentStateLock); + + inherited Destroy; +end; + + +procedure TFSXSimConnectStateMonitor.Attach(AObserver: IFSXSimConnectStateObserver); +begin + Observers.Add(AObserver as IFSXSimConnectStateObserver); +end; + + +procedure TFSXSimConnectStateMonitor.Detach(AObserver: IFSXSimConnectStateObserver); +begin + Observers.Remove(AObserver as IFSXSimConnectStateObserver); +end; + + +procedure TFSXSimConnectStateMonitor.DoSetCurrentState(const Value: TFSXSimConnectState); +var + observer: IInterface; + +begin + CurrentStateLock.Acquire; + try + if Value <> FCurrentState then + begin + FCurrentState := Value; + + for observer in Observers do + (observer as IFSXSimConnectStateObserver).ObserverStateUpdate(CurrentState); + end; + finally + CurrentStateLock.Release; + end; +end; + + +initialization + FSXSimConnectStateInstance := TFSXSimConnectStateMonitor.Create; + +finalization + FreeAndNil(FSXSimConnectStateInstance); + +end. diff --git a/G940LEDControl/Units/G940LEDStateConsumer.pas b/G940LEDControl/Units/G940LEDStateConsumer.pas index 9df311e..f668616 100644 --- a/G940LEDControl/Units/G940LEDStateConsumer.pas +++ b/G940LEDControl/Units/G940LEDStateConsumer.pas @@ -179,8 +179,8 @@ begin for buttonIndex := 0 to Pred(G940_BUTTONCOUNT) do begin - if buttonIndex >= ButtonColors.Count then - buttonColor := lcOff + if (buttonIndex >= ButtonColors.Count) or (not Assigned(ButtonColors[buttonIndex])) then + buttonColor := lcGreen else buttonColor := (ButtonColors[buttonIndex] as ILEDStateColor).GetCurrentColor; diff --git a/G940LEDControl/Units/LEDFunction.pas b/G940LEDControl/Units/LEDFunction.pas index 98448ad..07ed8d3 100644 --- a/G940LEDControl/Units/LEDFunction.pas +++ b/G940LEDControl/Units/LEDFunction.pas @@ -2,7 +2,8 @@ unit LEDFunction; interface uses - Classes, + System.Classes, + System.SyncObjs, LEDFunctionIntf, LEDStateIntf; @@ -10,7 +11,7 @@ uses type TCustomLEDFunctionWorker = class; - TCustomLEDFunctionWorkerClass = class of TCustomLEDFunctionWorker; + TCustomLEDMultiStateFunctionWorkerClass = class of TCustomLEDMultiStateFunctionWorker; TCustomLEDFunctionProvider = class(TInterfacedObject, ILEDFunctionProvider) @@ -38,26 +39,27 @@ type function GetDisplayName: string; virtual; abstract; function GetUID: string; virtual; abstract; - function CreateWorker(ASettings: ILEDFunctionWorkerSettings): ILEDFunctionWorker; virtual; abstract; + function CreateWorker(ASettings: ILEDFunctionWorkerSettings; const APreviousState: string = ''): ILEDFunctionWorker; virtual; abstract; end; TCustomMultiStateLEDFunction = class(TCustomLEDFunction, ILEDMultiStateFunction) private FStates: TInterfaceList; + FProviderUID: string; protected procedure RegisterStates; virtual; abstract; function RegisterState(AState: ILEDState): ILEDState; virtual; - function GetWorkerClass: TCustomLEDFunctionWorkerClass; virtual; abstract; - function DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; virtual; + function GetWorkerClass: TCustomLEDMultiStateFunctionWorkerClass; virtual; abstract; + function DoCreateWorker(ASettings: ILEDFunctionWorkerSettings; const APreviousState: string = ''): TCustomLEDFunctionWorker; virtual; protected - function CreateWorker(ASettings: ILEDFunctionWorkerSettings): ILEDFunctionWorker; override; + function CreateWorker(ASettings: ILEDFunctionWorkerSettings; const APreviousState: string = ''): ILEDFunctionWorker; override; { ILEDMultiStateFunction } function GetEnumerator: ILEDStateEnumerator; virtual; public - constructor Create; + constructor Create(const AProviderUID: string); destructor Destroy; override; end; @@ -65,25 +67,44 @@ type TCustomLEDFunctionWorker = class(TInterfacedObject, ILEDFunctionWorker) private FObservers: TInterfaceList; - FStates: TInterfaceList; + FProviderUID: string; + FFunctionUID: string; protected - procedure RegisterStates(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); virtual; - function FindState(const AUID: string): ILEDStateWorker; virtual; - procedure NotifyObservers; virtual; property Observers: TInterfaceList read FObservers; - property States: TInterfaceList read FStates; protected { ILEDFunctionWorker } procedure Attach(AObserver: ILEDFunctionObserver); virtual; procedure Detach(AObserver: ILEDFunctionObserver); virtual; + function GetProviderUID: string; virtual; + function GetFunctionUID: string; virtual; + function GetCurrentState: ILEDStateWorker; virtual; abstract; public - constructor Create; overload; virtual; - constructor Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); overload; virtual; + constructor Create(const AProviderUID, AFunctionUID: string); + destructor Destroy; override; + end; + + TCustomLEDMultiStateFunctionWorker = class(TCustomLEDFunctionWorker) + private + FStates: TInterfaceList; + FCurrentStateLock: TCriticalSection; + FCurrentState: ILEDStateWorker; + protected + procedure RegisterStates(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); virtual; + function FindState(const AUID: string): ILEDStateWorker; virtual; + + procedure SetCurrentState(const AUID: string; ANotifyObservers: Boolean = True); overload; virtual; + procedure SetCurrentState(AState: ILEDStateWorker; ANotifyObservers: Boolean = True); overload; virtual; + + property States: TInterfaceList read FStates; + protected + function GetCurrentState: ILEDStateWorker; override; + public + constructor Create(const AProviderUID, AFunctionUID: string; AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings; const APreviousState: string = ''); virtual; destructor Destroy; override; end; @@ -125,11 +146,13 @@ uses { TCustomMultiStateLEDFunction } -constructor TCustomMultiStateLEDFunction.Create; +constructor TCustomMultiStateLEDFunction.Create(const AProviderUID: string); begin inherited Create; FStates := TInterfaceList.Create; + FProviderUID := AProviderUID; + RegisterStates; end; @@ -155,39 +178,31 @@ begin end; -function TCustomMultiStateLEDFunction.CreateWorker(ASettings: ILEDFunctionWorkerSettings): ILEDFunctionWorker; +function TCustomMultiStateLEDFunction.CreateWorker(ASettings: ILEDFunctionWorkerSettings; const APreviousState: string): ILEDFunctionWorker; begin - Result := DoCreateWorker(ASettings); + Result := DoCreateWorker(ASettings, APreviousState); end; -function TCustomMultiStateLEDFunction.DoCreateWorker(ASettings: ILEDFunctionWorkerSettings): TCustomLEDFunctionWorker; +function TCustomMultiStateLEDFunction.DoCreateWorker(ASettings: ILEDFunctionWorkerSettings; const APreviousState: string): TCustomLEDFunctionWorker; begin - Result := GetWorkerClass.Create(Self, ASettings); + Result := GetWorkerClass.Create(FProviderUID, GetUID, Self, ASettings, APreviousState); end; { TCustomLEDFunctionWorker } -constructor TCustomLEDFunctionWorker.Create; +constructor TCustomLEDFunctionWorker.Create(const AProviderUID, AFunctionUID: string); begin inherited Create; FObservers := TInterfaceList.Create; -end; - - -constructor TCustomLEDFunctionWorker.Create(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); -begin - Create; - - FStates := TInterfaceList.Create; - RegisterStates(AStates, ASettings); + FProviderUID := AProviderUID; + FFunctionUID := AFunctionUID; end; destructor TCustomLEDFunctionWorker.Destroy; begin - FreeAndNil(FStates); FreeAndNil(FObservers); inherited Destroy; @@ -207,7 +222,57 @@ begin end; -procedure TCustomLEDFunctionWorker.RegisterStates(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); +function TCustomLEDFunctionWorker.GetProviderUID: string; +begin + Result := FProviderUID; +end; + + +function TCustomLEDFunctionWorker.GetFunctionUID: string; +begin + Result := FFunctionUID; +end; + + +procedure TCustomLEDFunctionWorker.NotifyObservers; +var + observer: IInterface; + +begin + for observer in Observers do + (observer as ILEDFunctionObserver).ObserveUpdate(Self); +end; + + +{ TCustomLEDMultiStateFunctionWorker } +constructor TCustomLEDMultiStateFunctionWorker.Create(const AProviderUID, AFunctionUID: string; AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings; const APreviousState: string); +begin + inherited Create(AProviderUID, AFunctionUID); + + FCurrentStateLock := TCriticalSection.Create; + + FStates := TInterfaceList.Create; + RegisterStates(AStates, ASettings); + + if Length(APreviousState) > 0 then + FCurrentState := FindState(APreviousState); + + { Make sure we have a default state } + if (not Assigned(FCurrentState)) and (States.Count > 0) then + SetCurrentState((States[0] as ILEDStateWorker), False); +end; + + +destructor TCustomLEDMultiStateFunctionWorker.Destroy; +begin + FreeAndNil(FCurrentStateLock); + FreeAndNil(FStates); + + inherited Destroy; +end; + + +procedure TCustomLEDMultiStateFunctionWorker.RegisterStates(AStates: ILEDMultiStateFunction; ASettings: ILEDFunctionWorkerSettings); var state: ILEDState; color: TLEDColor; @@ -223,7 +288,7 @@ begin end; -function TCustomLEDFunctionWorker.FindState(const AUID: string): ILEDStateWorker; +function TCustomLEDMultiStateFunctionWorker.FindState(const AUID: string): ILEDStateWorker; var state: IInterface; @@ -241,13 +306,37 @@ begin end; -procedure TCustomLEDFunctionWorker.NotifyObservers; -var - observer: IInterface; - +procedure TCustomLEDMultiStateFunctionWorker.SetCurrentState(const AUID: string; ANotifyObservers: Boolean); begin - for observer in Observers do - (observer as ILEDFunctionObserver).ObserveUpdate(Self); + SetCurrentState(FindState(AUID), ANotifyObservers); +end; + + +procedure TCustomLEDMultiStateFunctionWorker.SetCurrentState(AState: ILEDStateWorker; ANotifyObservers: Boolean); +begin + FCurrentStateLock.Acquire; + try + if AState <> FCurrentState then + begin + FCurrentState := AState; + + if ANotifyObservers then + NotifyObservers; + end; + finally + FCurrentStateLock.Release; + end; +end; + + +function TCustomLEDMultiStateFunctionWorker.GetCurrentState: ILEDStateWorker; +begin + FCurrentStateLock.Acquire; + try + Result := FCurrentState; + finally + FCurrentStateLock.Release; + end; end; diff --git a/G940LEDControl/Units/LEDFunctionIntf.pas b/G940LEDControl/Units/LEDFunctionIntf.pas index 81bba41..b4ce881 100644 --- a/G940LEDControl/Units/LEDFunctionIntf.pas +++ b/G940LEDControl/Units/LEDFunctionIntf.pas @@ -29,7 +29,7 @@ type function GetDisplayName: string; function GetUID: string; - function CreateWorker(ASettings: ILEDFunctionWorkerSettings): ILEDFunctionWorker; + function CreateWorker(ASettings: ILEDFunctionWorkerSettings; const APreviousState: string = ''): ILEDFunctionWorker; end; @@ -50,6 +50,9 @@ type procedure Attach(AObserver: ILEDFunctionObserver); procedure Detach(AObserver: ILEDFunctionObserver); + function GetProviderUID: string; + function GetFunctionUID: string; + function GetCurrentState: ILEDStateWorker; end; diff --git a/G940LEDControl/Units/LEDStateConsumer.pas b/G940LEDControl/Units/LEDStateConsumer.pas index f7c1912..d20f05b 100644 --- a/G940LEDControl/Units/LEDStateConsumer.pas +++ b/G940LEDControl/Units/LEDStateConsumer.pas @@ -30,7 +30,7 @@ type function Initialize: Boolean; override; procedure Cleanup; override; - function CreateWorker(AProfileButton: TProfileButton): ILEDFunctionWorker; + function CreateWorker(AProfileButton: TProfileButton; const APreviousState: string): ILEDFunctionWorker; property ButtonWorkers: TInterfaceList read FButtonWorkers; property ButtonColors: TInterfaceList read FButtonColors; @@ -49,6 +49,7 @@ type implementation uses + Generics.Collections, System.SysUtils, Winapi.Windows, @@ -95,7 +96,7 @@ begin end; -function TLEDStateConsumer.CreateWorker(AProfileButton: TProfileButton): ILEDFunctionWorker; +function TLEDStateConsumer.CreateWorker(AProfileButton: TProfileButton; const APreviousState: string): ILEDFunctionWorker; var provider: ILEDFunctionProvider; ledFunction: ILEDFunction; @@ -108,7 +109,7 @@ begin begin ledFunction := provider.Find(AProfileButton.FunctionUID); if Assigned(ledFunction) then - Result := ledFunction.CreateWorker(TProfileButtonWorkerSettings.Create(AProfileButton)); + Result := ledFunction.CreateWorker(TProfileButtonWorkerSettings.Create(AProfileButton), APreviousState); end; end; @@ -175,26 +176,55 @@ end; procedure TLEDStateConsumer.TMLoadProfile(var Msg: TOmniMessage); + + function GetFunctionKey(const AProviderUID, AFunctionUID: string): string; inline; + begin + Result := AProviderUID + '|' + AFunctionUID; + end; + + var oldWorkers: TInterfaceList; + oldStates: TDictionary; oldWorker: IInterface; profile: TProfile; buttonIndex: Integer; worker: ILEDFunctionWorker; + state: ILEDStateWorker; + previousState: string; + button: TProfileButton; + functionKey: string; begin profile := Msg.MsgData; - { Keep a copy of the old workers until all the new ones are initialized, - so we don't get unneccessary SimConnect reconnects. } - oldWorkers := TInterfaceList.Create; + oldStates := nil; + oldWorkers := nil; try + oldStates := TDictionary.Create; + oldWorkers := TInterfaceList.Create; + + { Keep a copy of the old workers until all the new ones are initialized, + so we don't get unneccessary SimConnect reconnects. } for oldWorker in ButtonWorkers do begin if Assigned(oldWorker) then begin - (oldWorker as ILEDFunctionWorker).Detach(Self); - oldWorkers.Add(oldWorker); + worker := (oldWorker as ILEDFunctionWorker); + try + worker.Detach(Self); + oldWorkers.Add(worker); + + { Keep the current state as well, to prevent the LEDs from flickering } + state := worker.GetCurrentState; + try + oldStates.AddOrSetValue(GetFunctionKey(worker.GetProviderUID, worker.GetFunctionUID), state.GetUID); + finally + state := nil; + end; + finally + worker := nil; + end; end; end; @@ -204,7 +234,14 @@ begin begin if profile.HasButton(buttonIndex) then begin - worker := CreateWorker(profile.Buttons[buttonIndex]) as ILEDFunctionWorker; + button := profile.Buttons[buttonIndex]; + + previousState := ''; + functionKey := GetFunctionKey(button.ProviderUID, button.FunctionUID); + if oldStates.ContainsKey(functionKey) then + previousState := oldStates[functionKey]; + + worker := CreateWorker(button, previousState) as ILEDFunctionWorker; ButtonWorkers.Add(worker); if Assigned(worker) then @@ -214,6 +251,7 @@ begin end; finally FreeAndNil(oldWorkers); + FreeAndNil(oldStates); end; Changed; diff --git a/G940LEDControl/Units/Profile.pas b/G940LEDControl/Units/Profile.pas index a4a1c6a..1389b6a 100644 --- a/G940LEDControl/Units/Profile.pas +++ b/G940LEDControl/Units/Profile.pas @@ -28,6 +28,8 @@ type constructor Create; destructor Destroy; override; + procedure Assign(Source: TPersistent); override; + procedure ClearStateColors; function GetStateColor(const AStateUID: string; out AValue: TLEDColor): Boolean; procedure SetStateColor(const AStateUID: string; const AValue: TLEDColor); @@ -43,20 +45,24 @@ type TProfile = class(TPersistent) private FName: string; + FIsTemporary: Boolean; FButtons: TProfileButtonList; function GetButton(Index: Integer): TProfileButton; function GetButtonCount: Integer; protected - function Load(AReader: IX2PersistReader): Boolean; + procedure Load(AReader: IX2PersistReader); procedure Save(AWriter: IX2PersistWriter); public constructor Create; destructor Destroy; override; + procedure Assign(Source: TPersistent); override; + function HasButton(AIndex: Integer): Boolean; property Name: string read FName write FName; + property IsTemporary: Boolean read FIsTemporary write FIsTemporary; property ButtonCount: Integer read GetButtonCount; property Buttons[Index: Integer]: TProfileButton read GetButton; @@ -65,6 +71,8 @@ type TProfileList = class(TObjectList) public + function Find(const AName: string): TProfile; + procedure Load(AReader: IX2PersistReader); procedure Save(AWriter: IX2PersistWriter); end; @@ -84,6 +92,7 @@ const KeyProviderUID = 'ProviderUID'; KeyFunctionUID = 'FunctionUID'; + KeyIsTemporary = 'IsTemporary'; { TProfileButton } @@ -103,6 +112,27 @@ begin end; +procedure TProfileButton.Assign(Source: TPersistent); +var + sourceButton: TProfileButton; + stateUID: string; + +begin + if Source is TProfileButton then + begin + sourceButton := TProfileButton(Source); + + FProviderUID := sourceButton.ProviderUID; + FFunctionUID := sourceButton.FunctionUID; + + FStateColors.Clear; + for stateUID in sourceButton.StateColors.Keys do + SetStateColor(stateUID, sourceButton.StateColors[stateUID]); + end else + inherited Assign(Source); +end; + + procedure TProfileButton.ClearStateColors; begin FStateColors.Clear; @@ -192,22 +222,44 @@ begin end; -function TProfile.Load(AReader: IX2PersistReader): Boolean; +procedure TProfile.Assign(Source: TPersistent); +var + sourceProfile: TProfile; + buttonIndex: Integer; + +begin + if Source is TProfile then + begin + sourceProfile := TProfile(Source); + + FName := sourceProfile.Name; + FIsTemporary := sourceProfile.IsTemporary; + + FButtons.Clear; + for buttonIndex := 0 to Pred(sourceProfile.ButtonCount) do + Buttons[buttonIndex].Assign(sourceProfile.Buttons[buttonIndex]); + end else + inherited Assign(Source); +end; + + +procedure TProfile.Load(AReader: IX2PersistReader); var buttonIndex: Integer; button: TProfileButton; begin - Result := False; buttonIndex := 0; + if not AReader.ReadBoolean(KeyIsTemporary, FIsTemporary) then + FIsTemporary := False; + while AReader.BeginSection(SectionButton + IntToStr(buttonIndex)) do try button := TProfileButton.Create; if button.Load(AReader) then begin FButtons.Add(button); - Result := True; end else FreeAndNil(button); finally @@ -222,6 +274,8 @@ var buttonIndex: Integer; begin + AWriter.WriteBoolean(KeyIsTemporary, IsTemporary); + for buttonIndex := 0 to Pred(FButtons.Count) do begin if AWriter.BeginSection(SectionButton + IntToStr(buttonIndex)) then @@ -272,6 +326,22 @@ end; { TProfileList } +function TProfileList.Find(const AName: string): TProfile; +var + profile: TProfile; + +begin + Result := nil; + + for profile in Self do + if SameText(profile.Name, AName) then + begin + Result := profile; + break; + end; +end; + + procedure TProfileList.Load(AReader: IX2PersistReader); var profiles: TStringList; @@ -291,11 +361,9 @@ begin try profile := TProfile.Create; profile.Name := profileName; + profile.Load(AReader); - if profile.Load(AReader) then - Add(profile) - else - FreeAndNil(profile); + Add(profile); finally AReader.EndSection; end; diff --git a/G940LEDControl/Units/Settings.pas b/G940LEDControl/Units/Settings.pas index 14c9d4a..c1fd303 100644 --- a/G940LEDControl/Units/Settings.pas +++ b/G940LEDControl/Units/Settings.pas @@ -9,6 +9,7 @@ type private FCheckUpdates: Boolean; FHasCheckUpdates: Boolean; + FActiveProfile: string; procedure SetCheckUpdates(const Value: Boolean); public @@ -17,6 +18,8 @@ type property CheckUpdates: Boolean read FCheckUpdates write SetCheckUpdates; property HasCheckUpdates: Boolean read FHasCheckUpdates; + + property ActiveProfile: string read FActiveProfile write FActiveProfile; end; @@ -25,6 +28,7 @@ const SectionSettings = 'Settings'; KeyCheckUpdates = 'CheckUpdates'; + KeyActiveProfile = 'ActiveProfile'; { TSettings } @@ -37,6 +41,9 @@ begin try if AReader.ReadBoolean(KeyCheckUpdates, value) then CheckUpdates := value; + + if not AReader.ReadString(KeyActiveProfile, FActiveProfile) then + FActiveProfile := ''; finally AReader.EndSection; end; @@ -48,6 +55,7 @@ begin if AWriter.BeginSection(SectionSettings) then try AWriter.WriteBoolean(KeyCheckUpdates, CheckUpdates); + AWriter.WriteString(KeyActiveProfile, ActiveProfile); finally AWriter.EndSection; end; diff --git a/G940LEDControl/Units/StaticLEDFunction.pas b/G940LEDControl/Units/StaticLEDFunction.pas index 1bdced1..c3cb7df 100644 --- a/G940LEDControl/Units/StaticLEDFunction.pas +++ b/G940LEDControl/Units/StaticLEDFunction.pas @@ -25,7 +25,7 @@ type function GetDisplayName: string; override; function GetUID: string; override; - function CreateWorker(ASettings: ILEDFunctionWorkerSettings): ILEDFunctionWorker; override; + function CreateWorker(ASettings: ILEDFunctionWorkerSettings; const APreviousState: string = ''): ILEDFunctionWorker; override; public constructor Create(AColor: TLEDColor); end; @@ -46,7 +46,7 @@ type protected function GetCurrentState: ILEDStateWorker; override; public - constructor Create(AColor: TLEDColor); + constructor Create(const AProviderUID, AFunctionUID: string; AColor: TLEDColor); end; @@ -94,16 +94,16 @@ begin end; -function TStaticLEDFunction.CreateWorker(ASettings: ILEDFunctionWorkerSettings): ILEDFunctionWorker; +function TStaticLEDFunction.CreateWorker(ASettings: ILEDFunctionWorkerSettings; const APreviousState: string): ILEDFunctionWorker; begin - Result := TStaticLEDFunctionWorker.Create(FColor); + Result := TStaticLEDFunctionWorker.Create(StaticProviderUID, GetUID, FColor); end; { TStaticLEDFunctionWorker } -constructor TStaticLEDFunctionWorker.Create(AColor: TLEDColor); +constructor TStaticLEDFunctionWorker.Create(const AProviderUID, AFunctionUID: string; AColor: TLEDColor); begin - inherited Create; + inherited Create(AProviderUID, AFunctionUID); FState := TLEDStateWorker.Create('', TLEDColorPool.GetColor(AColor)); end; From 72121a83ec6cdc871ee104a250410e05f494e3fc Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Sun, 24 Feb 2013 21:00:48 +0000 Subject: [PATCH 16/16] Bugfixes pre 1.0 release --- G940LEDControl/Forms/MainFrm.dfm | 16 ++++++++-------- G940LEDControl/Forms/MainFrm.pas | 3 +++ G940LEDControl/G940LEDControl.dproj | 2 +- G940LEDControl/Units/Profile.pas | 6 +++++- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/G940LEDControl/Forms/MainFrm.dfm b/G940LEDControl/Forms/MainFrm.dfm index 8fdca19..e3e52ee 100644 --- a/G940LEDControl/Forms/MainFrm.dfm +++ b/G940LEDControl/Forms/MainFrm.dfm @@ -280,7 +280,7 @@ object MainForm: TMainForm Top = 67 Width = 41 Height = 41 - Caption = 'P1' + Caption = 'P&1' TabOrder = 3 end object btnP2: TButton @@ -288,7 +288,7 @@ object MainForm: TMainForm Top = 114 Width = 41 Height = 41 - Caption = 'P2' + Caption = 'P&2' TabOrder = 4 end object btnP3: TButton @@ -296,7 +296,7 @@ object MainForm: TMainForm Top = 161 Width = 41 Height = 41 - Caption = 'P3' + Caption = 'P&3' TabOrder = 5 end object btnP4: TButton @@ -304,7 +304,7 @@ object MainForm: TMainForm Top = 208 Width = 41 Height = 41 - Caption = 'P4' + Caption = 'P&4' TabOrder = 6 end object btnP5: TButton @@ -312,7 +312,7 @@ object MainForm: TMainForm Top = 255 Width = 41 Height = 41 - Caption = 'P5' + Caption = 'P&5' TabOrder = 7 end object btnP6: TButton @@ -320,7 +320,7 @@ object MainForm: TMainForm Top = 302 Width = 41 Height = 41 - Caption = 'P6' + Caption = 'P&6' TabOrder = 8 end object btnP7: TButton @@ -328,7 +328,7 @@ object MainForm: TMainForm Top = 349 Width = 41 Height = 41 - Caption = 'P7' + Caption = 'P&7' TabOrder = 9 end object btnP8: TButton @@ -336,7 +336,7 @@ object MainForm: TMainForm Top = 396 Width = 41 Height = 41 - Caption = 'P8' + Caption = 'P&8' TabOrder = 10 end object cmbProfiles: TComboBox diff --git a/G940LEDControl/Forms/MainFrm.pas b/G940LEDControl/Forms/MainFrm.pas index 3652440..b5ddb43 100644 --- a/G940LEDControl/Forms/MainFrm.pas +++ b/G940LEDControl/Forms/MainFrm.pas @@ -851,6 +851,9 @@ begin if InputQuery('Save profile as', 'Save this profile as:', name) then begin existingProfile := Profiles.Find(name); + if existingProfile = profile then + existingProfile := nil; + if Assigned(existingProfile) then begin case MessageBox(Self.Handle, PChar(Format('A profile named "%s" exists, do you want to overwrite it?', [name])), diff --git a/G940LEDControl/G940LEDControl.dproj b/G940LEDControl/G940LEDControl.dproj index 37832db..df155fb 100644 --- a/G940LEDControl/G940LEDControl.dproj +++ b/G940LEDControl/G940LEDControl.dproj @@ -8,7 +8,7 @@ VCL 13.4 True - Release + Debug Win32 1 Application diff --git a/G940LEDControl/Units/Profile.pas b/G940LEDControl/Units/Profile.pas index 1389b6a..dfd588d 100644 --- a/G940LEDControl/Units/Profile.pas +++ b/G940LEDControl/Units/Profile.pas @@ -261,7 +261,10 @@ begin begin FButtons.Add(button); end else + begin + FButtons.Add(nil); FreeAndNil(button); + end; finally AReader.EndSection; Inc(buttonIndex); @@ -280,7 +283,8 @@ begin begin if AWriter.BeginSection(SectionButton + IntToStr(buttonIndex)) then try - FButtons[buttonIndex].Save(AWriter); + if Assigned(FButtons[buttonIndex]) then + FButtons[buttonIndex].Save(AWriter); finally AWriter.EndSection; end;