Hallo,
ich bin Anfänger im programmieren von Threads und das lesen von den vielen Themen erzeugt macht einem schnell klar, dass man hier viel falsch machen kann. Deshalb frage ich hier lieber mal nach.
Die Aufgabe:
Ich habe ein Thread das im Execute-Teil kontinuierlich eine Digitalkarte ausliest.
Delphi-Quellcode:
procedure TthrIO.execute;
begin
while not Terminated do
begin
Sleep(50);
_MeasureDigIn;
end;
end;
procedure TthrIO._MeasureDigIn;
{*******************************************************************************
Liest die Anzahl definierter Digitaleingänge ein und stellt Istwerte als
Longint-Werte blockweise zur Verfügung
_iDigIn[0] = FirstportXX,
_iDigIn[1] = SecondportXX, ...
*******************************************************************************}
var
iPort, // Port A, B, CL, CH = 1, 2, 3, 4, ...
iULStat : Integer;
iValue : TiWerteArr;
begin
for iPort := 1 to iMaxPort do
begin
iULStat := cbDIn (iAdr, GetPortType(iPort), iValue[iBlock]);
if iULStat > 0 then inc(iFailedDIn);
end;
_iDigIn := iValue;
end;
function TthrIO.GetDigIn(iKan:Integer):Boolean;
{*******************************************************************************
Abruf der Einzelwerte von extern
Der Gesamtwert für Kanal 0..23 liegt in _iDigIn[0]
24..47 in _iDigIn[1], usw
*******************************************************************************}
var
iPort : Integer;
iVal : Longint;
begin
Result := false;
if succ(iKan)>iDigIn then exit;
iPort := trunc(iKan/24);
iVal := _iDigIn[iPort];
Result:=odd(iVal shr iKan);
end;
Parallel soll es aber auch möglich sein, Digitalausgänge zu setzen. Ich hatte hier verschiedene Ansätze
1. Der direkte Zugriff auf einzelne Kanäle am Thread vorbei
Delphi-Quellcode:
procedure TthrIO.SetDigOut(iKan:Integer; bValue:Boolean);
{*******************************************************************************
Setzt je nach Kanal den entsprechenden Port-Wert, der dann im Thread kontinu-
ierlich ans Geraet gesendet wird. Dabei wird b erücksichtigt, dass u.U. schon
Ports für Digitaleingänge reserviert sind
*******************************************************************************}
var
iULStat : Integer;
begin
iULStat := cbDBitOut (iAdr, FIRSTPORTA, iKan, Integer(bValue));
if iULStat > 0 then inc(iFailedDOut);
end;
Wohl nicht die saubere Art bei Threads
2. Karte bietet auch Möglichkeit alle DigOuts mit einem Integer zu setzen. Diesen dann wieder in Execute beschreiben
Delphi-Quellcode:
TthrIO.execute;
begin
while not Terminated do
begin
Sleep(50);
_SetDigOut;
_MeasureDigIn;
end;
end;
procedure TthrIO._SetDigOut;
{*******************************************************************************
Setzt die Digitalausgänge blockweise über Excute
_iDigOutWerte[0] = FirstportXX,
_iDigOutWerte[1] = SecondportXX, ...
*******************************************************************************}
var
iPort, // Port A, B, CL, CH = 1, 2, 3, 4, ...
iPortType,
iULStat : Integer;
begin
for iPort := 1 to iMaxPortDigOut do
begin
// Nur Senden, wenn sich Werte geändert haben
if _iDigOutWerte[Pred(iPort)] <> iDigOutWerte[Pred(iPort)] then
begin
iPortType := GetPortType(iMaxPortDigIn + iPort);
iULStat := cbDOut (iAdr, iPortType, iDigOutWerte[iPort-1]);
if iULStat <> 0 then
inc(iFailedDOut)
else
_iDigOutWerte[Pred(iPort)] := iDigOutWerte[Pred(iPort)];
end;
end;
end;
procedure TthrIO.SetDigOut(iKan:Integer; bValue:Boolean);
{*******************************************************************************
Setzt je nach Kanal den entsprechenden Port-Wert, der dann im Thread kontinu-
ierlich ans Geraet gesendet wird. Dabei wird b erücksichtigt, dass u.U. schon
Ports für Digitaleingänge reserviert sind
*******************************************************************************}
var
iPort,
iPortType,
iULStat,
iKorr : Integer;
begin
iKan := iKan + iDigIn;
iPort := pred(GetPort(iKan));
iKorr := iKan - GetShl(iPort+1) - (iKan div 24) * 24;
iPort := iPort - iMaxPortDigIn;
if bValue then
iDigOutWerte[iPort] := iDigOutWerte[iPort] or (1 shl iKorr)
else
iDigOutWerte[iPort] := iDigOutWerte[iPort] and (not (1 shl iKorr));
end
Nachteil dieser Methode: Ausgänge werden nicht in der Reihenfolge gesetzt, wie es das Programm macht. Im Gegenteil: Da Karte mit 8bit Ports arbeitet, werden Kanäle im 1. Port immer vor denen im 2. gesetzt. Eben auch dann, wenn ich z.B. zuerst Ausgang 9 und dann 4 gesetzt habe.
3. Möglichkeit:
Ich hänge neue Werte in einer Liste immer hinten an und der Thread arbeitet diese wieder von oben nach unten ab und lösche erfolgreich gesetzte Ausgänge in der Liste wieder.
Delphi-Quellcode:
TValDigOut = record
Kan : integer;
Value : Boolean;
end;
TArrValDigOut = Array of TValDigOut;
procedure TthrIO._SetDigOut;
{*******************************************************************************
Setzt jeden Digitalausgang der Reihe nach wie sie gesetzt wurden
*******************************************************************************}
var
iULStat : Integer;
procedure DeleteArrayIndex(var X: TArrValDigOut; Index: Integer);
begin
Finalize(X[Index]);
System.Move(X[succ(Index)], X[Index], (high(X) - Index) * succ(SizeOf(TValDigOut)));
SetLength(X, high(X));
end;
begin
while high(DigOutWerte) > 0 do
begin
iULStat := cbDBitOut (iAdr, FIRSTPORTA, DigOutWerte[0].Kan, Integer(DigOutWerte[0].Value));
if iULStat <> 0 then
inc(iFailedDOut)
else
DeleteArrayIndex(DigOutWerte, 0);
end;
end;
procedure TthrIO.SetDigOut(iKan:Integer; bValue:Boolean);
{*******************************************************************************
Schreibt den zu setzenden Kanal in eine Liste die dann _SetDigOut abarbeitet
*******************************************************************************}
begin
SetLength(DigOutWerte, succ(length(DigOutWerte)));
with DigOutWerte[high(DigOutWerte)] do
begin
Kan := iKan;
Value := bValue;
end;
end;
Hier beginnt aber meine Unsicherheit, ob das was ich hier mach auch Threadsicher ist bzw. was ich machen muss, damit es es wird.
Viele Grüße
Gerd