Hallo zusammen,
ich
handle die komplette RS232-Kommunikation in einem Thread.
Ziel ist:
- Der Thread puffert alle eingehenden Befehle und sendet den nächsten erst, wenn der vorhergehende geantwortet hat oder keine Antwort erwartet wird
- Der Thread wartet z.B, auf eine bestimmte Antwort. Das kann auch bedeutet, dass er laufend einen Status abfragt, bis der gewünschte Status erreicht ist.
- Der Thread schickt den Befehl noch mal, wenn keine Antwort innerhalb eines definierten Timeouts kommt
Momentan habe ich aber das Problem, dass hin und wieder Antworten verloren gehen. Deshalb die Frage ob an meinem Konstrukt prinzipiell was falsch ist. Dieses sieht stark vereinfacht so aus:
Delphi-Quellcode:
unit Unit1;
interface
uses
Classes,
AdPort;
type
TThreadString =
procedure(Value:
String)
of object;
TMyComThread =
class(TThread)
private
FCommand:
String;
FCommandList: TStringList;
FComPort: TApdComPort;
FBuf:
String;
FBusy: Boolean;
FResponse:
String;
FSyncResponse: TThreadString;
procedure Execute;
procedure SyncResponseEvent;
procedure SetResponse(
const Value:
String);
protected
procedure ComPortTriggerAvail(CP: TObject; Count: Word);
public
property Response:
String write SetResponse;
property SyncResponse: TThreadString
read FSyncResponse
write FSyncResponse;
constructor Create(sEinst:
String);
destructor Destroy;
override;
procedure SendCommand(sValue:
String);
end;
implementation
procedure TMyComThread.ComPortTriggerAvail(CP: TObject; Count: Word);
var
i: Integer;
cBuf: Char;
begin
for i := 1
to Count
do
begin
cBuf := Char(FComPort.GetChar);
if cBuf = #13
then
begin
Response := FBuf;
FBuf := '
';
FBusy := False;
end
else
FBuf := FBuf + cBuf;
end;
end;
constructor TMyComThread.Create(sEinst:
String);
begin
FComPort := TApdComPort.Create(
nil);
FComPort.OnTriggerAvail := ComPortTriggerAvail;
end;
destructor TMyComThread.Destroy;
begin
FComPort.Open := False;
FComPort.Free;
FCommandList.Free;
inherited;
end;
procedure TMyComThread.Execute;
begin
FComPort.Open := True;
FBusy := False;
while not Terminated
do
begin
if not FBusy
and (FCommandList.Count > 0)
then
begin
FCommand := FCommandList[0];
FCommandList.Delete(0);
FComPort.PutString(AnsiString(FCommand + #13 + #10));
FBusy := True;
end;
TThread.Sleep(50);
end;
end;
procedure TMyComThread.SendCommand(sValue:
String);
begin
FCommandList.Add(sValue);
end;
procedure TMyComThread.SetResponse(
const Value:
String);
begin
FResponse := Value;
Synchronize(SyncResponseEvent);
end;
procedure TMyComThread.SyncResponseEvent;
begin
if Assigned(FSyncResponse)
then
FSyncResponse(FResponse);
end;
end.
und im Main Thread
Delphi-Quellcode:
Com := TMyComThread.Create (sEinst);
Com.SyncResponse:= SyncResponse;
procedure TForm1.SyncResponse(sMsg:
String);
begin
Edit1.Text:= sMsg;
end;
procedure TForm1.Button1ClickSender: TObject);
begin
Com.SendCommand(Edit2.Text);
end;
Ich habe aus dem Code alles wie Timeout-Überwachung, unterbrochene Verbindung, wiederholtes senden, falsches Kommando, Kommando ohne Rückgabewert, korrektes beenden, .... raus gelassen
Es geht mir hauptsächlich darum
- Kann bei der Datenübergabe von Com.SendCommand(Edit2.Text);
was schief gehen
- Ist sichergestellt dass ComPortTriggerAvail
innerhalb des Threads läuft oder ist der von Haus aus an den Main-Thread gebunden? Wie könnte ich die Daten Threadsave aus TApdComPort empfangen
- Ist die Rückgabe der Antwort über Synchronize(SyncResponseEvent);
eine gute Wahl
- Oder macht der Einsatz von TCriticalSection
statt Synchronize
Sinn
Vor allem bei 2. bin ich mir unsicher.
Um Verzögerungen aus dem Main-Thread zu verhindern, schreibe ich die Responses in TForm ebenfalls in eine Liste und arbeite die dann ab.
Grüße
Gerd