From 50580144ac057316c7add1a485f4ad9ceff3b9eb Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Sun, 24 Feb 2013 20:09:47 +0000 Subject: [PATCH] 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 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 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;