unit SerialComThreadU;
interface
uses
System.Sysutils, System.Generics.Collections,
System.Classes, System.SyncObjs;
type
TCommandQueue =
class
strict private
FCommands: TQueue<
string>;
FSignal: TSimpleEvent;
strict protected
property Commands: TQueue<
string>
read FCommands;
property Signal: TSimpleEvent
read FSignal;
public
constructor Create;
destructor Destroy;
override;
function HasCommands: boolean;
function Pop:
string;
procedure Push(
const aCommand:
string);
procedure SetEvent;
procedure WaitFor;
end;
TSerialComThread =
class(TThread)
strict private
type
TThreadErrorEvent =
procedure (sender: TSerialComThread;
const ErrMsg:
string; isFatal: boolean);
TThreadProgress =
procedure (sender: TSerialComThread;
const Command:
string);
var
FCommands: TCommandQueue;
FOnProgress: TThreadProgress;
FOnThreadError: TThreadErrorEvent;
procedure CloseSerialPort;
procedure InitializeSerialPort;
function MakeErrorMsg(aException:
Exception):
string;
function MoreWorkAvailable: boolean;
procedure ProcessOneCommand;
procedure ReportError(
const aErrorMsg:
string;
aIsFatalError: boolean = false);
procedure WaitForWork;
strict protected
procedure DoProgress(
const aCommand:
string);
procedure DoThreadError(
const aErrorMsg:
string; aIsFatalError: boolean =
false);
property Commands: TCommandQueue
read FCommands;
protected
procedure Execute;
override;
procedure TerminatedSet;
override;
public
constructor Create;
destructor Destroy;
override;
procedure AddCommand(
const aCommand:
string);
property OnProgress: TThreadProgress
read FOnProgress
write FOnProgress;
property OnThreadError: TThreadErrorEvent
read FOnThreadError
write
FOnThreadError;
end;
implementation
resourcestring
SThreadErrorMask =
'
%s ist auf eine Ausnahme vom Type %s gelaufen.'+SLinebreak+'
%s';
const
ERROR_IS_FATAL = true;
{== TSerialComThread ==================================================}
constructor TSerialComThread.Create;
begin
FCommands := TCommandQueue.Create();
inherited Create(false);
end;
destructor TSerialComThread.Destroy;
begin
FCommands.Free;
inherited Destroy;
end;
procedure TSerialComThread.AddCommand(
const aCommand:
string);
begin
Commands.Push(aCommand);
end;
procedure TSerialComThread.CloseSerialPort;
begin
// TODO -cMM: TSerialComThread.CloseSerialPort implement
end;
procedure TSerialComThread.DoProgress(
const aCommand:
string);
begin
if Assigned(FOnProgress)
then
Synchronize(
procedure begin
FOnProgress(Self, aCommand);
end);
end;
procedure TSerialComThread.DoThreadError(
const aErrorMsg:
string;
aIsFatalError: boolean = false);
begin
if Assigned(FOnThreadError)
then
Synchronize(
procedure begin
FOnThreadError(Self, aErrorMsg, aIsFatalError);
end);
end;
procedure TSerialComThread.Execute;
begin
try
InitializeSerialPort;
try
while not Terminated
do begin
WaitForWork;
while not Terminated
and MoreWorkAvailable
do
ProcessOneCommand;
end;
finally
CloseSerialPort;
end;
except
On E:
Exception do
ReportError(MakeErrorMsg(E), ERROR_IS_FATAL);
end;
end;
procedure TSerialComThread.InitializeSerialPort;
begin
// TODO -cMM: TSerialComThread.InitializeSerialPort implement
end;
function TSerialComThread.MakeErrorMsg(aException:
Exception):
string;
begin
Result := Format(SThreadErrorMask,
[Classname, aException.ClassName, aException.
Message]);
end;
function TSerialComThread.MoreWorkAvailable: boolean;
begin
Result := Commands.HasCommands;
end;
procedure TSerialComThread.ProcessOneCommand;
var
LCommand:
string;
begin
LCommand := Commands.Pop;
try
// TODO : send command over the port
DoProgress(LCommand);
except
on E:
Exception do
ReportError(MakeErrorMsg(E),
not ERROR_IS_FATAL);
end;
end;
procedure TSerialComThread.ReportError(
const aErrorMsg:
string; aIsFatalError:
boolean = false);
begin
DoThreadError(aErrorMsg, aIsFatalError);
end;
procedure TSerialComThread.TerminatedSet;
begin
inherited;
Commands.SetEvent;
end;
procedure TSerialComThread.WaitForWork;
begin
Commands.Waitfor;
end;
{== TCommandQueue =====================================================}
constructor TCommandQueue.Create;
begin
inherited Create;
FCommands := TQueue<
string>.Create();
FSignal := TSimpleEvent.Create();
end;
destructor TCommandQueue.Destroy;
begin
FreeAndNil(FSignal);
FreeAndNil(FCommands);
inherited Destroy;
end;
function TCommandQueue.HasCommands: boolean;
begin
MonitorEnter(self);
try
Result:= Commands.Count > 0;
finally
MonitorExit(self);
end;
end;
function TCommandQueue.Pop:
string;
begin
MonitorEnter(self);
try
Result := Commands.Dequeue;
finally
MonitorExit(self);
end;
end;
procedure TCommandQueue.Push(
const aCommand:
string);
begin
MonitorEnter(self);
try
Commands.Enqueue(aCommand);
Signal.SetEvent;
finally
MonitorExit(self);
end;
end;
procedure TCommandQueue.SetEvent;
begin
Signal.SetEvent;
end;
procedure TCommandQueue.WaitFor;
begin
Signal.WaitFor;
Signal.ResetEvent;
end;
end.