1
0
mirror of synced 2024-11-22 10:03:51 +00:00

Refactored: ILEDColor to ILEDStateColor (to prevent conflicts with the TLEDColor enum)

Reanimated: LEDStateConsumer and G940LEDStateConsumer; all LED state colors (including flashing) work and update properly after changing the profile
- FSX functions crash horribly
This commit is contained in:
Mark van Renswoude 2013-02-19 22:22:15 +00:00
parent b66659cd95
commit f1b9a63fda
15 changed files with 305 additions and 862 deletions

View File

@ -19,9 +19,7 @@ uses
pngimage,
X2UtPersistIntf,
LEDFunctionMap,
LEDStateConsumer,
LEDStateProvider,
Profile;
@ -95,7 +93,6 @@ type
procedure FormCreate(Sender: TObject);
procedure btnRetryClick(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure FunctionComboBoxChange(Sender: TObject);
procedure lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType);
procedure btnCheckUpdatesClick(Sender: TObject);
procedure LEDButtonClick(Sender: TObject);
@ -109,15 +106,8 @@ type
FProfiles: TProfileList;
FActiveProfile: TProfile;
FLoadingProfiles: Boolean;
// FStateConsumerTask: IOmniTaskControl;
FStateConsumerTask: IOmniTaskControl;
protected
// procedure ReadFunctions(AReader: IX2PersistReader; AComboBoxes: TComboBoxArray);
// procedure ReadFSXExtra(AReader: IX2PersistReader);
// procedure ReadAutoUpdate(AReader: IX2PersistReader);
// procedure WriteFunctions(AWriter: IX2PersistWriter; AComboBoxes: TComboBoxArray);
// procedure WriteFSXExtra(AWriter: IX2PersistWriter);
// procedure WriteAutoUpdate(AWriter: IX2PersistWriter);
procedure FindLEDControls;
procedure LoadProfiles;
procedure SaveProfiles;
@ -129,11 +119,6 @@ type
procedure SetDeviceState(const AMessage: string; AFound: Boolean);
// procedure SetFSXToggleZoomButton(const ADeviceGUID: TGUID; AButtonIndex: Integer; const ADisplayText: string);
// procedure InitializeStateProvider(AProviderClass: TLEDStateProviderClass);
// procedure FinalizeStateProvider;
// procedure UpdateMapping;
procedure CheckForUpdatesThread(const ATask: IOmniTask);
procedure CheckForUpdates(AReportNoUpdates: Boolean);
@ -150,7 +135,7 @@ type
property ActiveProfile: TProfile read FActiveProfile;
property EventMonitor: TOmniEventMonitor read FEventMonitor;
property Profiles: TProfileList read FProfiles;
// property StateConsumerTask: IOmniTaskControl read FStateConsumerTask;
property StateConsumerTask: IOmniTaskControl read FStateConsumerTask;
end;
@ -170,7 +155,6 @@ uses
ButtonFunctionFrm,
ConfigConversion,
FSXLEDStateProvider,
G940LEDStateConsumer,
LEDColorIntf,
LEDFunctionIntf,
@ -193,28 +177,13 @@ const
TEXT_STATE_FOUND = 'Connected';
type
TComboBoxFunctionConsumer = class(TInterfacedObject, IFunctionConsumer)
private
FComboBox: TComboBoxEx;
protected
{ IFunctionConsumer }
procedure SetCategory(const ACategory: string);
procedure AddFunction(AFunction: Integer; const ADescription: string);
property ComboBox: TComboBoxEx read FComboBox;
public
constructor Create(AComboBox: TComboBoxEx);
end;
{ TMainForm }
procedure TMainForm.FormCreate(Sender: TObject);
//var
// consumer: IOmniWorker;
//
var
consumer: IOmniWorker;
begin
lblVersion.Caption := App.Version.FormatVersion(False);
@ -222,12 +191,12 @@ begin
FEventMonitor := TOmniEventMonitor.Create(Self);
// consumer := TG940LEDStateConsumer.Create;
// FStateConsumerTask := FEventMonitor.Monitor(CreateTask(consumer)).MsgWait;
consumer := TG940LEDStateConsumer.Create;
FStateConsumerTask := FEventMonitor.Monitor(CreateTask(consumer)).MsgWait;
EventMonitor.OnTaskMessage := EventMonitorMessage;
EventMonitor.OnTaskTerminated := EventMonitorTerminated;
// StateConsumerTask.Run;
StateConsumerTask.Run;
FindLEDControls;
@ -235,6 +204,7 @@ begin
FProfiles := TProfileList.Create(True);
LoadProfiles;
FStateConsumerTask.Comm.Send(TM_LOADPROFILE, ActiveProfile);
// LoadFunctions(TFSXLEDStateProvider, FFSXComboBoxes);
// LoadDefaultProfile;
end;
@ -549,6 +519,7 @@ begin
if TButtonFunctionForm.Execute(ActiveProfile, buttonIndex) then
begin
UpdateButton(ActiveProfile, buttonIndex);
FStateConsumerTask.Comm.Send(TM_LOADPROFILE, ActiveProfile);
SaveProfiles;
end;
end;
@ -657,20 +628,20 @@ end;
procedure TMainForm.EventMonitorMessage(const task: IOmniTaskControl; const msg: TOmniMessage);
begin
case msg.MsgID of
MSG_NOTIFY_DEVICESTATE: HandleDeviceStateMessage(task, msg);
MSG_RUN_IN_MAINTHREAD: HandleRunInMainThreadMessage(task, msg);
MSG_PROVIDER_KILLED: HandleProviderKilled(task, msg);
TM_NOTIFY_DEVICESTATE: HandleDeviceStateMessage(task, msg);
// MSG_RUN_IN_MAINTHREAD: HandleRunInMainThreadMessage(task, msg);
// MSG_PROVIDER_KILLED: HandleProviderKilled(task, msg);
MSG_UPDATE:
if MessageBox(Self.Handle, 'An update is available on the G940 LED Control website.'#13#10'Do you want to go there now?',
'Update available', MB_YESNO or MB_ICONINFORMATION) = ID_YES then
ShellExecute(Self.Handle, 'open', PChar('http://g940.x2software.net/#download'), nil, nil, SW_SHOWNORMAL);
MSG_NOUPDATE:
if msg.MsgData.AsBoolean then
MessageBox(Self.Handle, 'You are using the latest version.', 'No update available', MB_OK or MB_ICONINFORMATION)
else
MessageBox(Self.Handle, 'Failed to check for updates. Maybe try again later?', 'Uh-oh', MB_OK or MB_ICONWARNING);
// MSG_NOUPDATE:
// if msg.MsgData.AsBoolean then
// MessageBox(Self.Handle, 'You are using the latest version.', 'No update available', MB_OK or MB_ICONINFORMATION)
// else
// MessageBox(Self.Handle, 'Failed to check for updates. Maybe try again later?', 'Uh-oh', MB_OK or MB_ICONWARNING);
end;
end;
@ -748,53 +719,9 @@ begin
end;
procedure TMainForm.FunctionComboBoxChange(Sender: TObject);
var
comboBox: TComboBoxEx;
begin
comboBox := TComboBoxEx(Sender);
if comboBox.ItemIndex > -1 then
begin
if not Assigned(comboBox.ItemsEx[comboBox.ItemIndex].Data) then
comboBox.ItemIndex := Succ(comboBox.ItemIndex);
end;
end;
procedure TMainForm.lblLinkLinkClick(Sender: TObject; const Link: string; LinkType: TSysLinkType);
begin
ShellExecute(Self.Handle, 'open', PChar(Link), nil, nil, SW_SHOWNORMAL);
end;
{ TComboBoxFunctionConsumer }
constructor TComboBoxFunctionConsumer.Create(AComboBox: TComboBoxEx);
begin
inherited Create;
FComboBox := AComboBox;
end;
procedure TComboBoxFunctionConsumer.SetCategory(const ACategory: string);
begin
with ComboBox.ItemsEx.Add do
begin
Caption := ACategory;
Data := nil;
end;
end;
procedure TComboBoxFunctionConsumer.AddFunction(AFunction: Integer; const ADescription: string);
begin
with ComboBox.ItemsEx.Add do
begin
Caption := ADescription;
Indent := 1;
Data := Pointer(AFunction);
end;
end;
end.

View File

@ -5,11 +5,8 @@ uses
MainFrm in 'Forms\MainFrm.pas' {MainForm},
LogiJoystickDLL in '..\Shared\LogiJoystickDLL.pas',
SimConnect in '..\Shared\SimConnect.pas',
FSXLEDStateProvider in 'Units\FSXLEDStateProvider.pas',
G940LEDStateConsumer in 'Units\G940LEDStateConsumer.pas',
LEDFunctionMap in 'Units\LEDFunctionMap.pas',
LEDStateConsumer in 'Units\LEDStateConsumer.pas',
LEDStateProvider in 'Units\LEDStateProvider.pas',
LEDColorIntf in 'Units\LEDColorIntf.pas',
LEDColor in 'Units\LEDColor.pas',
LEDFunctionIntf in 'Units\LEDFunctionIntf.pas',

View File

@ -165,11 +165,8 @@
</DCCReference>
<DCCReference Include="..\Shared\LogiJoystickDLL.pas"/>
<DCCReference Include="..\Shared\SimConnect.pas"/>
<DCCReference Include="Units\FSXLEDStateProvider.pas"/>
<DCCReference Include="Units\G940LEDStateConsumer.pas"/>
<DCCReference Include="Units\LEDFunctionMap.pas"/>
<DCCReference Include="Units\LEDStateConsumer.pas"/>
<DCCReference Include="Units\LEDStateProvider.pas"/>
<DCCReference Include="Units\LEDColorIntf.pas"/>
<DCCReference Include="Units\LEDColor.pas"/>
<DCCReference Include="Units\LEDFunctionIntf.pas"/>

View File

@ -12,23 +12,24 @@ const
type
TLEDColorDynArray = array of TLEDColor;
TStaticLEDColorDynArray = array of TStaticLEDColor;
TDynamicLEDColor = class(TCustomDynamicLEDColor)
TDynamicLEDColor = class(TCustomLEDStateDynamicColor)
private
FCycleColors: TLEDColorDynArray;
FCycleColors: TStaticLEDColorDynArray;
FCycleIndex: Integer;
FTickInterval: Integer;
FTickCount: Integer;
protected
{ ILEDState }
function GetColor: TLEDColor; override;
function GetCurrentColor: TStaticLEDColor; override;
{ ITickLEDState }
procedure Reset; override;
procedure Tick; override;
public
constructor Create(ACycleColors: TLEDColorDynArray; ATickInterval: Integer = TICKINTERVAL_NORMAL);
constructor Create(ACycleColors: TStaticLEDColorDynArray; ATickInterval: Integer = TICKINTERVAL_NORMAL);
end;
@ -39,7 +40,7 @@ uses
{ TDynamicLEDState }
constructor TDynamicLEDColor.Create(ACycleColors: TLEDColorDynArray; ATickInterval: Integer);
constructor TDynamicLEDColor.Create(ACycleColors: TStaticLEDColorDynArray; ATickInterval: Integer);
begin
inherited Create;
@ -49,16 +50,22 @@ begin
FCycleColors := ACycleColors;
FCycleIndex := Low(FCycleColors);
FTickInterval := ATickInterval;
FTickCount := 0;
Reset;
end;
function TDynamicLEDColor.GetColor: TLEDColor;
function TDynamicLEDColor.GetCurrentColor: TStaticLEDColor;
begin
Result := FCycleColors[FCycleIndex];
end;
procedure TDynamicLEDColor.Reset;
begin
FCycleIndex := 0;
end;
procedure TDynamicLEDColor.Tick;
begin
Inc(FTickCount);

View File

@ -8,46 +8,31 @@ uses
OtlComm,
OtlTaskControl,
LEDFunctionMap,
LEDStateConsumer,
LEDStateProvider;
LEDStateConsumer;
const
MSG_FINDTHROTTLEDEVICE = MSG_CONSUMER_OFFSET + 1;
MSG_NOTIFY_DEVICESTATE = MSG_CONSUMER_OFFSET + 2;
MSG_TIMER_BLINK = MSG_CONSUMER_OFFSET + 3;
TM_FINDTHROTTLEDEVICE = 2001;
TM_NOTIFY_DEVICESTATE = 2002;
TIMER_BLINK = TIMER_CONSUMER_OFFSET + 1;
type
TG940LEDStateConsumer = class(TLEDStateConsumer)
private
FDirectInput: IDirectInput8;
FThrottleDevice: IDirectInputDevice8;
FRed: Byte;
FGreen: Byte;
FBlinkTimerStarted: Boolean;
FBlinkCounter: Integer;
protected
procedure MsgFindThrottleDevice(var msg: TOmniMessage); message MSG_FINDTHROTTLEDEVICE;
procedure MsgTimerBlink(var msg: TOmniMessage); message MSG_TIMER_BLINK;
procedure MsgFindThrottleDevice(var msg: TOmniMessage); message TM_FINDTHROTTLEDEVICE;
protected
function Initialize: Boolean; override;
procedure ResetLEDState; override;
procedure LEDStateChanged(ALEDIndex: Integer; AState: TLEDState); override;
procedure Changed; override;
procedure StartBlinkTimer;
procedure StopBlinkTimer;
procedure FindThrottleDevice;
procedure FoundThrottleDevice(ADeviceGUID: TGUID);
procedure SetDeviceState(AState: Integer);
procedure Update; override;
property DirectInput: IDirectInput8 read FDirectInput;
property ThrottleDevice: IDirectInputDevice8 read FThrottleDevice;
end;
@ -58,8 +43,8 @@ const
DEVICESTATE_FOUND = 1;
DEVICESTATE_NOTFOUND = 2;
EXIT_ERROR_LOGIJOYSTICKDLL = EXIT_CONSUMER_OFFSET + 1;
EXIT_ERROR_DIRECTINPUT = EXIT_CONSUMER_OFFSET + 2;
EXIT_ERROR_LOGIJOYSTICKDLL = 9001;
EXIT_ERROR_DIRECTINPUT = 9002;
implementation
@ -68,15 +53,18 @@ uses
Windows,
OtlCommon,
OtlTask,
LEDColorIntf,
LogiJoystickDLL;
const
BLINK_INTERVAL = 500;
G940_BUTTONCOUNT = 8;
(*
type
TRunInMainThreadSetLEDs = class(TOmniWaitableValue, IRunInMainThread)
private
@ -89,6 +77,7 @@ type
public
constructor Create(ADevice: IDirectInputDevice8; ARed, AGreen: Byte);
end;
*)
function EnumDevicesProc(var lpddi: TDIDeviceInstanceW; pvRef: Pointer): BOOL; stdcall;
@ -134,10 +123,11 @@ begin
end;
Result := True;
Task.Comm.OtherEndpoint.Send(MSG_FINDTHROTTLEDEVICE);
Task.Comm.OtherEndpoint.Send(TM_FINDTHROTTLEDEVICE);
end;
{
procedure TG940LEDStateConsumer.ResetLEDState;
begin
FRed := 0;
@ -200,36 +190,18 @@ begin
inherited;
end;
}
{
procedure TG940LEDStateConsumer.Changed;
begin
inherited;
if Assigned(ThrottleDevice) then
{ Logitech SDK will not change the color outside of the main thread }
{ Logitech SDK will not change the color outside of the main thread
RunInMainThread(TRunInMainThreadSetLEDs.Create(ThrottleDevice, FRed, FGreen), Destroying);
end;
procedure TG940LEDStateConsumer.StartBlinkTimer;
begin
if FBlinkTimerStarted then
exit;
FBlinkCounter := 0;
Task.SetTimer(TIMER_BLINK, BLINK_INTERVAL, MSG_TIMER_BLINK);
FBlinkTimerStarted := True;
end;
procedure TG940LEDStateConsumer.StopBlinkTimer;
begin
if not FBlinkTimerStarted then
exit;
Task.ClearTimer(TIMER_BLINK);
FBlinkTimerStarted := False;
end;
}
procedure TG940LEDStateConsumer.FindThrottleDevice;
@ -243,7 +215,7 @@ begin
if not Assigned(ThrottleDevice) then
SetDeviceState(DEVICESTATE_NOTFOUND)
else
Changed;
Update;
end;
@ -256,7 +228,54 @@ end;
procedure TG940LEDStateConsumer.SetDeviceState(AState: Integer);
begin
Task.Comm.Send(MSG_NOTIFY_DEVICESTATE, AState);
Task.Comm.Send(TM_NOTIFY_DEVICESTATE, AState);
end;
procedure TG940LEDStateConsumer.Update;
procedure SetBit(var AMask: Byte; ABit: Integer); inline;
begin
AMask := AMask or (1 shl ABit)
end;
var
red: Byte;
green: Byte;
buttonIndex: Integer;
buttonColor: TStaticLEDColor;
begin
if not Assigned(ThrottleDevice) then
exit;
red := 0;
green := 0;
for buttonIndex := 0 to Pred(G940_BUTTONCOUNT) do
begin
if buttonIndex >= ButtonColors.Count then
buttonColor := lcOff
else
buttonColor := (ButtonColors[buttonIndex] as ILEDStateColor).GetCurrentColor;
case buttonColor of
lcGreen:
SetBit(green, buttonIndex);
lcAmber:
begin
SetBit(green, buttonIndex);
SetBit(red, buttonIndex);
end;
lcRed:
SetBit(red, buttonIndex);
end;
end;
SetLEDs(ThrottleDevice, red, green);
end;
@ -266,52 +285,8 @@ begin
end;
procedure TG940LEDStateConsumer.MsgTimerBlink(var msg: TOmniMessage);
var
warningState: TLEDState;
errorState: TLEDState;
ledIndex: Integer;
state: TLEDState;
begin
Inc(FBlinkCounter);
if FBlinkCounter > 3 then
FBlinkCounter := 0;
warningState := lsOff;
errorState := lsOff;
{ Error lights blink twice as fast }
if (FBlinkCounter in [0, 1]) then
warningState := lsAmber;
if (FBlinkCounter in [0, 2]) then
errorState := lsRed;
if StateMap.FindFirst([lsWarning, lsError], ledIndex, state) then
begin
BeginUpdate;
try
repeat
case state of
lsWarning:
if StateMap.GetState(ledIndex) <> warningState then
LEDStateChanged(ledIndex, warningState);
lsError:
if StateMap.GetState(ledIndex) <> errorState then
LEDStateChanged(ledIndex, errorState);
end;
until not StateMap.FindNext([lsWarning, lsError], ledIndex, state);
finally
EndUpdate;
end;
end else
StopBlinkTimer;
end;
{ TRunInMainThreadSetLEDs }
(*
constructor TRunInMainThreadSetLEDs.Create(ADevice: IDirectInputDevice8; ARed, AGreen: Byte);
begin
inherited Create;
@ -326,5 +301,6 @@ procedure TRunInMainThreadSetLEDs.Execute;
begin
SetLEDs(FDevice, FRed, FGreen);
end;
*)
end.

View File

@ -8,16 +8,17 @@ uses
type
TCustomLEDColor = class(TInterfacedObject, ILEDColor)
TCustomLEDStateColor = class(TInterfacedObject, ILEDStateColor)
protected
{ ILEDState }
function GetColor: TLEDColor; virtual; abstract;
function GetCurrentColor: TStaticLEDColor; virtual; abstract;
end;
TCustomDynamicLEDColor = class(TCustomLEDColor, IDynamicLEDColor)
TCustomLEDStateDynamicColor = class(TCustomLEDStateColor, ILEDStateDynamicColor)
protected
{ ITickLEDState }
procedure Reset; virtual; abstract;
procedure Tick; virtual; abstract;
end;

View File

@ -11,14 +11,15 @@ type
ILEDColor = interface
ILEDStateColor = interface
['{B40DF462-B660-4002-A6B9-DD30AC69E8DB}']
function GetColor: TLEDColor;
function GetCurrentColor: TStaticLEDColor;
end;
IDynamicLEDColor = interface(ILEDColor)
ILEDStateDynamicColor = interface(ILEDStateColor)
['{9770E851-580D-4803-9979-0C608CB108A0}']
procedure Reset;
procedure Tick;
end;

View File

@ -8,13 +8,13 @@ uses
type
TLEDColorPool = class(TObject)
private
FStates: array[TLEDColor] of ILEDColor;
FStates: array[TLEDColor] of ILEDStateColor;
protected
class function Instance: TLEDColorPool;
function DoGetColor(AColor: TLEDColor): ILEDColor;
function DoGetColor(AColor: TLEDColor): ILEDStateColor;
public
class function GetColor(AColor: TLEDColor): ILEDColor; overload;
class function GetColor(AColor: TLEDColor): ILEDStateColor; overload;
end;
@ -31,7 +31,7 @@ var
{ TLEDStatePool }
class function TLEDColorPool.GetColor(AColor: TLEDColor): ILEDColor;
class function TLEDColorPool.GetColor(AColor: TLEDColor): ILEDStateColor;
begin
Result := Instance.DoGetColor(AColor);
end;
@ -46,9 +46,9 @@ begin
end;
function TLEDColorPool.DoGetColor(AColor: TLEDColor): ILEDColor;
function TLEDColorPool.DoGetColor(AColor: TLEDColor): ILEDStateColor;
function GetFlashingCycle(AColor: TLEDColor): TLEDColorDynArray;
function GetFlashingCycle(AColor: TLEDColor): TStaticLEDColorDynArray;
begin
SetLength(Result, 2);
Result[0] := AColor;
@ -56,16 +56,16 @@ function TLEDColorPool.DoGetColor(AColor: TLEDColor): ILEDColor;
end;
var
state: ILEDColor;
state: ILEDStateColor;
begin
if not Assigned(FStates[AColor]) then
begin
case AColor of
lcOff: state := TStaticLEDColor.Create(lcOff);
lcGreen: state := TStaticLEDColor.Create(lcGreen);
lcAmber: state := TStaticLEDColor.Create(lcAmber);
lcRed: state := TStaticLEDColor.Create(lcRed);
lcOff: state := TLEDStateStaticColor.Create(lcOff);
lcGreen: state := TLEDStateStaticColor.Create(lcGreen);
lcAmber: state := TLEDStateStaticColor.Create(lcAmber);
lcRed: state := TLEDStateStaticColor.Create(lcRed);
lcFlashingGreenFast: state := TDynamicLEDColor.Create(GetFlashingCycle(lcGreen), TICKINTERVAL_FAST);
lcFlashingGreenNormal: state := TDynamicLEDColor.Create(GetFlashingCycle(lcGreen), TICKINTERVAL_NORMAL);

View File

@ -1,228 +0,0 @@
unit LEDFunctionMap;
interface
uses
Classes,
SyncObjs,
X2UtHashes;
type
TLEDState = (lsOff, lsGreen, lsAmber, lsRed, lsWarning, lsError);
TLEDStateSet = set of TLEDState;
TLEDFunctionMap = class(TObject)
private
FFunctions: TX2IIHash;
public
constructor Create;
destructor Destroy; override;
procedure Clear;
procedure SetFunction(ALEDIndex, AFunction: Integer);
function GetFunction(ALEDIndex: Integer): Integer;
function HasFunction(AFunction: Integer): Boolean; overload;
function HasFunction(AFunctions: array of Integer): Boolean; overload;
function FindFirst(AFunction: Integer; out ALEDIndex: Integer): Boolean;
function FindNext(AFunction: Integer; out ALEDIndex: Integer): Boolean;
end;
TLEDStateMap = class(TObject)
private
FStates: TX2IIHash;
public
constructor Create;
destructor Destroy; override;
procedure Clear;
function GetState(ALEDIndex: Integer; ADefault: TLEDState = lsGreen): TLEDState;
function SetState(ALEDIndex: Integer; AState: TLEDState): Boolean;
function HasStates(AStates: TLEDStateSet): Boolean;
function FindFirst(AStates: TLEDStateSet; out ALEDIndex: Integer; out AState: TLEDState): Boolean;
function FindNext(AStates: TLEDStateSet; out ALEDIndex: Integer; out AState: TLEDState): Boolean;
end;
const
FUNCTION_NONE = 0;
FUNCTION_OFF = 1;
FUNCTION_RED = 2;
FUNCTION_AMBER = 3;
FUNCTION_GREEN = 4;
{ Note: if this offset ever changes, make sure to write a conversion for
existing configurations. And probably reserve a bit more. }
FUNCTION_PROVIDER_OFFSET = FUNCTION_GREEN;
implementation
uses
SysUtils;
{ TLEDFunctionMap }
constructor TLEDFunctionMap.Create;
begin
inherited;
FFunctions := TX2IIHash.Create;
end;
destructor TLEDFunctionMap.Destroy;
begin
FreeAndNil(FFunctions);
inherited;
end;
procedure TLEDFunctionMap.Clear;
begin
FFunctions.Clear;
end;
procedure TLEDFunctionMap.SetFunction(ALEDIndex, AFunction: Integer);
begin
FFunctions[ALEDIndex] := AFunction;
end;
function TLEDFunctionMap.GetFunction(ALEDIndex: Integer): Integer;
begin
Result := FFunctions[ALEDIndex];
end;
function TLEDFunctionMap.HasFunction(AFunctions: array of Integer): Boolean;
var
functionNo: Integer;
begin
Result := False;
for functionNo in AFunctions do
begin
Result := HasFunction(functionNo);
if Result then
break;
end;
end;
function TLEDFunctionMap.HasFunction(AFunction: Integer): Boolean;
var
ledIndex: Integer;
begin
Result := FindFirst(AFunction, ledIndex);
end;
function TLEDFunctionMap.FindFirst(AFunction: Integer; out ALEDIndex: Integer): Boolean;
begin
FFunctions.First;
Result := FindNext(AFunction, ALEDIndex);
end;
function TLEDFunctionMap.FindNext(AFunction: Integer; out ALEDIndex: Integer): Boolean;
begin
Result := False;
while FFunctions.Next do
begin
if FFunctions.CurrentValue = AFunction then
begin
ALEDIndex := FFunctions.CurrentKey;
Result := True;
break;
end;
end;
end;
{ TLEDStateMap }
constructor TLEDStateMap.Create;
begin
inherited;
FStates := TX2IIHash.Create;
end;
destructor TLEDStateMap.Destroy;
begin
FreeAndNil(FStates);
inherited;
end;
procedure TLEDStateMap.Clear;
begin
FStates.Clear;
end;
function TLEDStateMap.GetState(ALEDIndex: Integer; ADefault: TLEDState): TLEDState;
begin
Result := ADefault;
if FStates.Exists(ALEDIndex) then
Result := TLEDState(FStates[ALEDIndex]);
end;
function TLEDStateMap.SetState(ALEDIndex: Integer; AState: TLEDState): Boolean;
begin
if FStates.Exists(ALEDIndex) then
Result := (FStates[ALEDIndex] <> Ord(AState))
else
Result := True;
if Result then
FStates[ALEDIndex] := Ord(AState);
end;
function TLEDStateMap.HasStates(AStates: TLEDStateSet): Boolean;
var
ledIndex: Integer;
state: TLEDState;
begin
Result := FindFirst(AStates, ledIndex, state);
end;
function TLEDStateMap.FindFirst(AStates: TLEDStateSet; out ALEDIndex: Integer; out AState: TLEDState): Boolean;
begin
FStates.First;
Result := FindNext(AStates, ALEDIndex, AState);
end;
function TLEDStateMap.FindNext(AStates: TLEDStateSet; out ALEDIndex: Integer; out AState: TLEDState): Boolean;
begin
Result := False;
while FStates.Next do
if TLEDState(FStates.CurrentValue) in AStates then
begin
ALEDIndex := FStates.CurrentKey;
AState := TLEDState(FStates.CurrentValue);
Result := True;
break;
end;
end;
end.

View File

@ -33,12 +33,12 @@ type
TLEDStateWorker = class(TCustomLEDState, ILEDStateWorker)
private
FColor: ILEDColor;
FColor: ILEDStateColor;
protected
{ ILEDStateWorker }
function GetColor: ILEDColor;
function GetColor: ILEDStateColor;
public
constructor Create(const AUID: string; AColor: ILEDColor);
constructor Create(const AUID: string; AColor: ILEDStateColor);
end;
@ -83,7 +83,7 @@ end;
{ TLEDStateWorker }
constructor TLEDStateWorker.Create(const AUID: string; AColor: ILEDColor);
constructor TLEDStateWorker.Create(const AUID: string; AColor: ILEDStateColor);
begin
inherited Create(AUID);
@ -91,7 +91,7 @@ begin
end;
function TLEDStateWorker.GetColor: ILEDColor;
function TLEDStateWorker.GetColor: ILEDStateColor;
begin
Result := FColor;
end;

View File

@ -2,29 +2,22 @@ unit LEDStateConsumer;
interface
uses
System.Classes,
OtlComm,
OtlCommon,
OtlTaskControl,
LEDFunctionMap,
LEDStateProvider;
LEDColorIntf,
LEDFunctionIntf,
Profile;
const
MSG_CLEAR_FUNCTIONS = 1001;
MSG_SET_FUNCTION = 1002;
MSG_INITIALIZE_PROVIDER = 1003;
MSG_FINALIZE_PROVIDER = 1004;
MSG_PROCESS_MESSAGES = 1005;
MSG_FINALIZE = 1006;
TM_LOADPROFILE = 1001;
TM_TICK = 1002;
MSG_PROVIDER_KILLED = 1007;
MSG_RUN_IN_MAINTHREAD = 1008;
MSG_CONSUMER_OFFSET = MSG_RUN_IN_MAINTHREAD;
TIMER_PROCESSMESSAGES = 1001;
TIMER_CONSUMER_OFFSET = TIMER_PROCESSMESSAGES;
TIMER_TICK = 101;
type
@ -34,318 +27,217 @@ type
end;
TLEDStateConsumer = class(TOmniWorker, ILEDStateConsumer)
TLEDStateConsumer = class(TOmniWorker)
private
FFunctionMap: TLEDFunctionMap;
FStateMap: TLEDStateMap;
FProvider: TLEDStateProvider;
FTimerSet: Boolean;
FChanged: Boolean;
FUpdateCount: Integer;
FDestroying: Boolean;
FButtonWorkers: TInterfaceList;
FButtonColors: TInterfaceList;
FHasTickTimer: Boolean;
protected
procedure MsgClearFunctions(var msg: TOmniMessage); message MSG_CLEAR_FUNCTIONS;
procedure MsgSetFunction(var msg: TOmniMessage); message MSG_SET_FUNCTION;
procedure MsgInitializeProvider(var msg: TOmniMessage); message MSG_INITIALIZE_PROVIDER;
procedure MsgFinalizeProvider(var msg: TOmniMessage); message MSG_FINALIZE_PROVIDER;
procedure MsgProcessMessages(var msg: TOmniMessage); message MSG_PROCESS_MESSAGES;
procedure MsgFinalize(var msg: TOmniMessage); message MSG_FINALIZE;
function Initialize: Boolean; override;
procedure Cleanup; override;
procedure InitializeProvider(AProviderClass: TLEDStateProviderClass);
procedure FinalizeProvider;
function CreateWorker(AProfileButton: TProfileButton): ILEDFunctionWorker;
procedure RunInMainThread(AExecutor: IRunInMainThread; AWait: Boolean = False);
procedure InitializeLEDState; virtual;
procedure ResetLEDState; virtual;
procedure LEDStateChanged(ALEDIndex: Integer; AState: TLEDState); virtual;
property ButtonWorkers: TInterfaceList read FButtonWorkers;
property ButtonColors: TInterfaceList read FButtonColors;
property HasTickTimer: Boolean read FHasTickTimer;
protected
procedure Changed; virtual;
{ ILEDStateConsumer }
function GetFunctionMap: TLEDFunctionMap;
procedure SetStateByFunction(AFunction: Integer; AState: TLEDState);
property Destroying: Boolean read FDestroying;
property FunctionMap: TLEDFunctionMap read GetFunctionMap;
property StateMap: TLEDStateMap read FStateMap;
property Provider: TLEDStateProvider read FProvider;
property UpdateCount: Integer read FUpdateCount write FUpdateCount;
public
constructor Create;
procedure BeginUpdate;
procedure EndUpdate;
procedure Update; virtual; abstract;
protected
procedure TMLoadProfile(var Msg: TOmniMessage); message TM_LOADPROFILE;
procedure TMTick(var Msg: TOmniMessage); message TM_TICK;
end;
procedure ClearFunctions(AConsumer: IOmniTaskControl);
procedure SetFunction(AConsumer: IOmniTaskControl; ALEDIndex, AFunction: Integer);
procedure InitializeStateProvider(AConsumer: IOmniTaskControl; AProviderClass: TLEDStateProviderClass);
procedure FinalizeStateProvider(AConsumer: IOmniTaskControl);
procedure Finalize(AConsumer: IOmniTaskControl);
implementation
uses
SysUtils,
Windows;
System.SysUtils,
Winapi.Windows,
LEDFunctionRegistry,
LEDStateIntf;
const
G940_LED_COUNT = 8;
INTERVAL_TICK = 500;
type
TProfileButtonWorkerSettings = class(TInterfacedObject, ILEDFunctionWorkerSettings)
private
FProfileButton: TProfileButton;
protected
{ ILEDFunctionWorkerSettings }
function GetStateColor(const AUID: string; out AColor: TLEDColor): Boolean;
property ProfileButton: TProfileButton read FProfileButton;
public
constructor Create(AProfileButton: TProfileButton);
end;
{ TLEDStateConsumer }
constructor TLEDStateConsumer.Create;
function TLEDStateConsumer.Initialize: Boolean;
begin
inherited;
Result := inherited Initialize;
if not Result then
exit;
FFunctionMap := TLEDFunctionMap.Create;
FStateMap := TLEDStateMap.Create;
InitializeLEDState;
FButtonWorkers := TInterfaceList.Create;
FButtonColors := TInterfaceList.Create;
end;
procedure TLEDStateConsumer.Cleanup;
begin
inherited;
FreeAndNil(FButtonColors);
FreeAndNil(FButtonWorkers);
FreeAndNil(FStateMap);
FreeAndNil(FFunctionMap);
inherited Cleanup;
end;
procedure TLEDStateConsumer.BeginUpdate;
begin
if FUpdateCount = 0 then
FChanged := False;
Inc(FUpdateCount);
end;
procedure TLEDStateConsumer.EndUpdate;
begin
if FUpdateCount > 0 then
Dec(FUpdateCount);
if (FUpdateCount = 0) and FChanged then
Changed;
end;
function TLEDStateConsumer.GetFunctionMap: TLEDFunctionMap;
begin
Result := FFunctionMap;
end;
procedure TLEDStateConsumer.SetStateByFunction(AFunction: Integer; AState: TLEDState);
function TLEDStateConsumer.CreateWorker(AProfileButton: TProfileButton): ILEDFunctionWorker;
var
ledIndex: Integer;
provider: ILEDFunctionProvider;
ledFunction: ILEDFunction;
begin
if FunctionMap.FindFirst(AFunction, ledIndex) then
repeat
if StateMap.SetState(ledIndex, AState) then
LEDStateChanged(ledIndex, AState);
until not FunctionMap.FindNext(AFunction, ledIndex);
end;
Result := nil;
procedure TLEDStateConsumer.MsgClearFunctions(var msg: TOmniMessage);
provider := TLEDFunctionRegistry.Find(AProfileButton.ProviderUID);
if Assigned(provider) then
begin
FunctionMap.Clear;
ledFunction := provider.Find(AProfileButton.FunctionUID);
if Assigned(ledFunction) then
Result := ledFunction.CreateWorker(TProfileButtonWorkerSettings.Create(AProfileButton));
end;
procedure TLEDStateConsumer.MsgSetFunction(var msg: TOmniMessage);
var
values: TOmniValueContainer;
begin
values := msg.MsgData.AsArray;
FunctionMap.SetFunction(values[0], values[1]);
end;
procedure TLEDStateConsumer.MsgInitializeProvider(var msg: TOmniMessage);
begin
InitializeProvider(TLEDStateProviderClass(msg.MsgData.AsPointer));
end;
procedure TLEDStateConsumer.MsgFinalizeProvider(var msg: TOmniMessage);
begin
FinalizeProvider;
end;
procedure TLEDStateConsumer.MsgProcessMessages(var msg: TOmniMessage);
begin
BeginUpdate;
try
Provider.ProcessMessages;
if Provider.Terminated then
begin
FinalizeProvider;
Task.Comm.Send(MSG_PROVIDER_KILLED, '');
end;
finally
EndUpdate;
end;
end;
procedure TLEDStateConsumer.MsgFinalize(var msg: TOmniMessage);
begin
FDestroying := True;
FinalizeProvider;
Task.Terminate;
end;
procedure TLEDStateConsumer.InitializeProvider(AProviderClass: TLEDStateProviderClass);
begin
FinalizeProvider;
FProvider := AProviderClass.Create(Self);
try
Provider.Initialize;
if Provider.ProcessMessagesInterval > -1 then
begin
Task.SetTimer(TIMER_PROCESSMESSAGES, Provider.ProcessMessagesInterval, MSG_PROCESS_MESSAGES);
FTimerSet := True;
end;
InitializeLEDState;
except
on E:Exception do
begin
FProvider := nil;
Task.Comm.Send(MSG_PROVIDER_KILLED, E.Message);
end;
end;
end;
procedure TLEDStateConsumer.FinalizeProvider;
begin
if Assigned(Provider) then
begin
if FTimerSet then
begin
Task.ClearTimer(TIMER_PROCESSMESSAGES);
FTimerSet := False;
end;
Provider.Terminate;
Provider.Finalize;
FreeAndNil(FProvider);
StateMap.Clear;
ResetLEDState;
end;
end;
procedure TLEDStateConsumer.RunInMainThread(AExecutor: IRunInMainThread; AWait: Boolean);
begin
Task.Comm.Send(MSG_RUN_IN_MAINTHREAD, AExecutor);
if AWait then
AExecutor.WaitFor(INFINITE);
end;
procedure TLEDStateConsumer.InitializeLEDState;
var
ledIndex: Integer;
state: TLEDState;
newState: TLEDState;
begin
BeginUpdate;
try
ResetLEDState;
for ledIndex := 0 to Pred(G940_LED_COUNT) do
begin
state := StateMap.GetState(ledIndex, lsGreen);
newState := state;
case FunctionMap.GetFunction(ledIndex) of
FUNCTION_OFF: newState := lsOff;
FUNCTION_RED: newState := lsRed;
FUNCTION_AMBER: newState := lsAmber;
FUNCTION_GREEN: newState := lsGreen;
end;
if state <> newState then
LEDStateChanged(ledIndex, newState);
end;
finally
EndUpdate;
end;
end;
procedure TLEDStateConsumer.ResetLEDState;
begin
if UpdateCount = 0 then
Changed
else
FChanged := True;
end;
procedure TLEDStateConsumer.LEDStateChanged(ALEDIndex: Integer; AState: TLEDState);
begin
if UpdateCount = 0 then
Changed
else
FChanged := True;
end;
procedure TLEDStateConsumer.Changed;
var
hasDynamicColors: Boolean;
buttonIndex: Integer;
state: ILEDStateWorker;
color: ILEDStateColor;
dynamicColor: ILEDStateDynamicColor;
begin
FChanged := False;
hasDynamicColors := False;
ButtonColors.Clear;
for buttonIndex := 0 to Pred(ButtonWorkers.Count) do
begin
color := nil;
if Assigned(ButtonWorkers[buttonIndex]) then
begin
state := (ButtonWorkers[buttonIndex] as ILEDFunctionWorker).GetCurrentState;
if Assigned(state) then
begin
color := state.GetColor;
if Assigned(color) then
begin
if (hasDynamicColors = False) and Supports(color, ILEDStateDynamicColor, dynamicColor) then
begin
{ If the tick timer isn't currently running, there were no
dynamic colors before. Reset each dynamic colors now. }
if not HasTickTimer then
dynamicColor.Reset;
hasDynamicColors := True;
end;
ButtonColors.Add(color as ILEDStateColor);
end;
end;
end;
if not Assigned(color) then
ButtonColors.Add(nil);
end;
if hasDynamicColors <> HasTickTimer then
begin
if hasDynamicColors then
Task.SetTimer(TIMER_TICK, INTERVAL_TICK, TM_TICK)
else
Task.ClearTimer(TIMER_TICK);
end;
Update;
end;
{ Helpers }
procedure ClearFunctions(AConsumer: IOmniTaskControl);
procedure TLEDStateConsumer.TMLoadProfile(var Msg: TOmniMessage);
var
profile: TProfile;
buttonIndex: Integer;
begin
AConsumer.Comm.Send(MSG_CLEAR_FUNCTIONS);
profile := Msg.MsgData;
ButtonWorkers.Clear;
for buttonIndex := 0 to Pred(profile.ButtonCount) do
begin
if profile.HasButton(buttonIndex) then
ButtonWorkers.Add(CreateWorker(profile.Buttons[buttonIndex]) as ILEDFunctionWorker)
else
ButtonWorkers.Add(nil);
end;
Changed;
end;
procedure SetFunction(AConsumer: IOmniTaskControl; ALEDIndex, AFunction: Integer);
procedure TLEDStateConsumer.TMTick(var Msg: TOmniMessage);
var
buttonIndex: Integer;
checkButtonIndex: Integer;
alreadyTicked: Boolean;
color: ILEDStateColor;
dynamicColor: ILEDStateDynamicColor;
begin
AConsumer.Comm.Send(MSG_SET_FUNCTION, [ALEDIndex, AFunction]);
// (MvR) 19-2-2013: I could pass a tick count to Tick() so that they can all use modulus to blink synchronously... think about it.
for buttonIndex := 0 to Pred(ButtonColors.Count) do
begin
alreadyTicked := False;
color := (ButtonColors[buttonIndex] as ILEDStateColor);
if Supports(color, ILEDStateDynamicColor, dynamicColor) then
begin
{ Check if this color has already been ticked }
for checkButtonIndex := Pred(buttonIndex) downto 0 do
if (ButtonColors[checkButtonIndex] as ILEDStateColor) = color then
begin
alreadyTicked := True;
break;
end;
if not alreadyTicked then
dynamicColor.Tick;
end;
end;
Update;
end;
procedure InitializeStateProvider(AConsumer: IOmniTaskControl; AProviderClass: TLEDStateProviderClass);
{ TProfileButtonWorkerSettings }
constructor TProfileButtonWorkerSettings.Create(AProfileButton: TProfileButton);
begin
AConsumer.Comm.Send(MSG_INITIALIZE_PROVIDER, Pointer(AProviderClass));
inherited Create;
FProfileButton := AProfileButton;
end;
procedure FinalizeStateProvider(AConsumer: IOmniTaskControl);
function TProfileButtonWorkerSettings.GetStateColor(const AUID: string; out AColor: TLEDColor): Boolean;
begin
AConsumer.Comm.Send(MSG_FINALIZE_PROVIDER);
end;
procedure Finalize(AConsumer: IOmniTaskControl);
begin
AConsumer.Comm.Send(MSG_FINALIZE);
Result := ProfileButton.GetStateColor(AUID, AColor);
end;
end.

View File

@ -21,7 +21,7 @@ type
ILEDStateWorker = interface(ICustomLEDState)
['{0361CBD5-E64E-4972-A8A4-D5FE0B0DFB1C}']
function GetColor: ILEDColor;
function GetColor: ILEDStateColor;
end;

View File

@ -1,127 +0,0 @@
unit LEDStateProvider;
interface
uses
Classes,
SyncObjs,
SysUtils,
LEDFunctionMap;
type
EInitializeError = class(Exception);
ILEDStateConsumer = interface
['{6E630C92-7C5C-4D16-8BED-AE27559FA584}']
function GetFunctionMap: TLEDFunctionMap;
procedure SetStateByFunction(AFunction: Integer; AState: TLEDState);
property FunctionMap: TLEDFunctionMap read GetFunctionMap;
end;
IFunctionConsumer = interface
['{97B47A29-BA7F-4C48-934D-EB66D2741647}']
procedure SetCategory(const ACategory: string);
procedure AddFunction(AFunction: Integer; const ADescription: string);
end;
TLEDStateProvider = class(TObject)
private
FConsumer: ILEDStateConsumer;
FTerminated: Boolean;
protected
procedure Execute; virtual; abstract;
function GetProcessMessagesInterval: Integer; virtual;
property Consumer: ILEDStateConsumer read FConsumer;
public
class procedure EnumFunctions(AConsumer: IFunctionConsumer); virtual;
constructor Create(AConsumer: ILEDStateConsumer); virtual;
destructor Destroy; override;
procedure Initialize; virtual;
procedure Finalize; virtual;
procedure ProcessMessages; virtual;
procedure Terminate; virtual;
property ProcessMessagesInterval: Integer read GetProcessMessagesInterval;
property Terminated: Boolean read FTerminated;
end;
TLEDStateProviderClass = class of TLEDStateProvider;
const
EXIT_SUCCESS = 0;
EXIT_ERROR = 1;
EXIT_CONSUMER_OFFSET = 100;
EXIT_PROVIDER_OFFSET = 200;
implementation
const
CATEGORY_STATIC = 'Static';
FUNCTION_DESC_OFF = 'Light off';
FUNCTION_DESC_GREEN = 'Green';
FUNCTION_DESC_AMBER = 'Amber';
FUNCTION_DESC_RED = 'Red';
{ TCustomLEDStateProvider }
class procedure TLEDStateProvider.EnumFunctions(AConsumer: IFunctionConsumer);
begin
AConsumer.SetCategory(CATEGORY_STATIC);
AConsumer.AddFunction(FUNCTION_OFF, FUNCTION_DESC_OFF);
AConsumer.AddFunction(FUNCTION_GREEN, FUNCTION_DESC_GREEN);
AConsumer.AddFunction(FUNCTION_AMBER, FUNCTION_DESC_AMBER);
AConsumer.AddFunction(FUNCTION_RED, FUNCTION_DESC_RED);
end;
constructor TLEDStateProvider.Create(AConsumer: ILEDStateConsumer);
begin
inherited Create;
FConsumer := AConsumer;
end;
destructor TLEDStateProvider.Destroy;
begin
inherited;
end;
procedure TLEDStateProvider.Initialize;
begin
end;
procedure TLEDStateProvider.Finalize;
begin
end;
procedure TLEDStateProvider.ProcessMessages;
begin
end;
procedure TLEDStateProvider.Terminate;
begin
FTerminated := True;
end;
function TLEDStateProvider.GetProcessMessagesInterval: Integer;
begin
Result := -1;
end;
end.

View File

@ -3,6 +3,7 @@ unit Profile;
interface
uses
Generics.Collections,
System.Classes,
X2UtPersistIntf,
@ -12,7 +13,7 @@ uses
type
TProfileButtonStateColors = TDictionary<string,TLEDColor>;
TProfileButton = class(TObject)
TProfileButton = class(TPersistent)
private
FProviderUID: string;
FFunctionUID: string;
@ -39,7 +40,7 @@ type
TProfileButtonList = class(TObjectList<TProfileButton>);
TProfile = class(TObject)
TProfile = class(TPersistent)
private
FName: string;
FButtons: TProfileButtonList;
@ -71,8 +72,7 @@ type
implementation
uses
Classes,
SysUtils,
System.SysUtils,
LEDResources;

View File

@ -7,13 +7,13 @@ uses
type
TStaticLEDColor = class(TCustomLEDColor)
TLEDStateStaticColor = class(TCustomLEDStateColor)
private
FColor: TLEDColor;
FColor: TStaticLEDColor;
protected
function GetColor: TLEDColor; override;
function GetCurrentColor: TStaticLEDColor; override;
public
constructor Create(AColor: TLEDColor);
constructor Create(AColor: TStaticLEDColor);
end;
@ -21,7 +21,7 @@ implementation
{ TStaticLEDState }
constructor TStaticLEDColor.Create(AColor: TLEDColor);
constructor TLEDStateStaticColor.Create(AColor: TStaticLEDColor);
begin
inherited Create;
@ -29,7 +29,7 @@ begin
end;
function TStaticLEDColor.GetColor: TLEDColor;
function TLEDStateStaticColor.GetCurrentColor: TStaticLEDColor;
begin
Result := FColor;
end;