Tutorial zum Web-IO Digital:
Web-IO Digital mit Delphi über
Binary-Sockets ansprechen
Als einfach zu erlernende Hochsprache bietet Delphi alles, was zum Programmieren von TCP/IP-Anwendungen nötig ist. Damit ist Delphi auch ein beliebtes Hilfsmittel, um Anwendungen zu erstellen, die mit dem Web-IO Digital über Binary-Sockets kommunizieren. Zusätzliche Treiber oder DLLs werden nicht benötigt.
Mit dem folgenden Delphi-Programmbeispiel können Sie Ihr Web-IO Digital mit seinen Inputs und Outputs in einer Windows-Anwendung über den Binary-Socket-Modus ansprechen.
Vorbereitungen
- Web-IO mit Spannung versorgen und IOs verdrahten
- Web-IO mit dem Netzwerk verbinden
- IP-Adressen vergeben
Beim Web-IO im Bereich Kommunikationswege >> Socket-API die BINARY1-Sockets aktivieren, Input-Trigger anhaken und die Outputs zum Schalten freigeben
Bei der Benennung der einzelnen Objekte ist es hilfreich, sinngebende Namen zu verwenden. In diesem Beispiel beschreibt der erste Teil des Namens die Art des Objektes und der zweite Teil die Funktion.
Für die Abwicklung der TCP-Kommunikation wird die TIdTCPClient-Komponente von Indy genutzt, die Bestandteil der Delphi-Komponenten ist.
Die Binär-Strukturen
Für den Binary-Zugriff müssen die benötigten Binär-Strukturen definiert werden, über die mit dem Web-IO kommuniziert werden soll. Eine detaillierte Beschreibung dieser Strukturen finden Sie in der Binary-Kurzübersicht oder im Programmierhandbuch zum Web-IO.
Die Struktur EADriverMit ihren vier 16-Bit Variablen ist EADriver die Grundstruktur, die auch Bestandteil aller weiteren Binary-Strukturen ist.
Type
READriver=packed record
Start_1:word;
Start_2:word;
StructType:word;
StructLength:word;
end;
RWriteRegister=packed record
EADriver : READriver;
Amount:word;
Value:word;
end;
RSetBits=packed record
EADriver : READriver;
Mask:word;
Value:word;
end ;
RRegisterState=packed record
EADriver : READriver;
DriverID : word;
InputValue : word;
OutputValue : word;
end;
RReadCounter=packed record
EADriver : READriver;
CounterIndex : word;
end;
RCounter=packed record
EADriver : READriver;
CounterIndex: word;
CounterValue: longword;
end;
RAllCounter=packed record
EADriver : READriver;
CounterNoOf: word;
CounterValue: array [0..11]of longword;
end;
ROptions=packed record
EADriver : READriver;
Version : longword;
Options : longword;
end;
Bei den Strukturen ist es wichtig, dass die Einzelvariablen bündig
im Speicher hintereinander liegen. Das handhabt Delphi nicht
automatisch so, insbesondere dann, wenn in einer Struktur Variablen
unterschiedlicher Größe zusammengefasst sind. Um dennoch eine bündige
Speicherbelegung zu gewährleisten, wird über
=packed record
festgelegt, dass
zwischen den Einzelvariablen keine ungenutzten Speicherstellen liegen.
Programmstart
Einrichten der BedienelementeUm asynchron auftretende Ereignisse wie den Datenempfang zu behandeln, wird vorbereitend ein entsprechender Thread definiert, angelegt und bei Programmstart gestartet.
Die Gruppe mit den Bedienelementen für das Web-IO wird zunächst für die Bedienung gesperrt. Sobald eine Verbindung zustande kommt, werden alle Elemente freigeschaltet, welche eine sinnvolle Ausführung besitzen.
.....
.....
private
{ Private-Deklarationen }
public
procedure ClientSocketThreadRun(Sender: TIdThreadComponent);
{ Public-Deklarationen }
end;
var
webio_binary_client: Twebio_binary_client;
ClientSocketThread : TIdThreadComponent;
implementation
{$R *.DFM}
procedure Twebio_binary_client.FormCreate(Sender: TObject);
begin
ClientSocketThread := TIdThreadComponent.Create();
ClientSocketThread.onRun := ClientSocketThreadRun;
StatusBar1.SimpleText := 'No Connection';
bt_disconnect.Enabled := False;
gb_io.Enabled := False;
end;
Verbindungskontrolle
Einleiten der VerbindungDurch Eingabe der IP-Adresse des Web-IO in das Textfeld ed_ip und Klick auf den Button bt_connect wird der Verbindungsaufbau gestartet.
procedure Twebio_binary_client.bt_connectClick(Sender: TObject);
begin
if ed_ip.Text <> '' then
begin
ClientSocket.Host := ed_ip.Text;
ClientSocket.Port := strtoint(ed_port.Text);
ClientSocket.Connect;
end;
end;
Verbindung kommt zustande
Sobald das Web-IO die Verbindung annimmt, führt das ClientSocket-Steuerelement die entsprechende Prozedur aus. In der Statuszeile wird das Zustandekommen der Verbindung angezeigt, die Bedienelemente werden zur Benutzung freigegeben und der Disconnect-Button wird bedienbar.
Über Senden der Struktur Options an das Web-IO wird dieses angewiesen, beim Setzen eines Outputs den veränderten Schaltzustand über die Struktur RegisterState zu übermitteln.
procedure Twebio_binary_client.bt_disconnectClick(Sender: TObject);
begin
ClientSocket.Disconnect;
end;
procedure Twebio_binary_client.ClientSocketConnected(Sender: TObject);
var
Options : structOptions;
SendBuffer : TIdBytes;
begin
ClientSocketThread.Active := true;
StatusBar1.SimpleText := 'Connected to ' + ed_ip.Text;
bt_connect.Enabled := False;
bt_disconnect.Enabled := True;
gb_io.Enabled := True;
Options.EADriver.Start_1 := 0;
Options.EADriver.Start_2 := 0;
Options.EADriver.StructType := $1F0;
Options.EADriver.StructLength := $10;
Options.Version := 1;
Options.Options := 1;
SendBuffer := RawToBytes(Options, Options.EADriver.StructLength);
ClientSocket.IOHandler.Write(SendBuffer);
end;
Sender der Binary-Strukturen
Das Indy TidTCPClient-Steuerelement kann Strukturen nicht
direkt versenden. Deshalb muss jede Struktur vor dem Senden zunächst mittels RawToBytes
in ein Byte-Array konvertiert werden.
Trennen der Verbindung
Die Verbindung bleibt solange bestehen, bis sie vom Benutzer durch Klick auf den Disconnect-Button beendet wird oder das Web-IO die Verbindung beendet.
procedure Twebio_ascii_client.bt_disconnectClick(Sender: TObject);
begin
ClientSocket.Disconnect;
end;
Auch in diesem Fall ruft das ClientSocket-Steuerelement eine entsprechende Prozedur auf.
procedure Twebio_ascii_client.ClientSocketDisconnected(Sender: TObject);
begin
ClientSocketThread.Active := false;
ClientSocket.Disconnect;
StatusBar1.SimpleText := 'No Connection';
bt_connect.Enabled := True;
bt_disconnect.Enabled := False;
gb_io.Enabled := False;
end;
Bedienung und Kommunikation von Client-Seite
Sobald eine Verbindung mit dem Web-IO zustande gekommen ist, kann der Anwender durch Bedienung der entsprechenden Programmelemente Binary-Strukturen an das Web-IO senden.
Setzen der OutputsDas Setzen der Outputs wird dem Anwender über zwei Checkboxen cb_outputx ermöglicht. Das Programm nutzt dazu das MouseUP-Ereignis dieses Objektes. Wird ein MouseUp, also ein Loslassen der Output-Checkbox, registriert, führt das Programm die entsprechende Prozedur aus und gibt - je nachdem, ob die Checkbox gesetzt ist oder nicht - die Struktur SetBit ausgefüllt mit den passenden Werten an das Web-IO weiter.
procedure Twebio_binary_client.cb_outputMouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
SetBits : structSetBits;
SendBuffer : TIdBytes;
begin
SetBits.EADriver.Start_1 := 0;
SetBits.EADriver.Start_2 := 0;
SetBits.EADriver.StructType := $09;
SetBits.EADriver.StructLength := $0C;
if sender = cb_output0 then
begin
SetBits.Mask := 1;
if cb_output0.Checked then
SetBits.Value := 1
else
SetBits.Value := 0;
end;
if sender = cb_output1 then
begin
SetBits.Mask := 2;
if cb_output1.Checked then
SetBits.Value := 2
else
SetBits.Value := 0;
end;
SendBuffer := RawToBytes(SetBits, SetBits.EADriver.StructLength);
ClientSocket.IOHandler.Write(SendBuffer);
end;
Output/Input-Statusabfragen
Den Status der Outputs und Inputs kann der Anwender durch Anklicken des zugehörigen Buttons anfordern.
procedure Twebio_binary_client.bt_outputs_readClick(Sender: TObject);
var
EADriver : structEADriver;
SendBuffer : TIdBytes;
begin
EADriver.Start_1 := 0;
EADriver.Start_2 := 0;
EADriver.StructType := $21;
EADriver.StructLength := $08;
SendBuffer := RawToBytes(EADriver, EADriver.StructLength);
ClientSocket.IOHandler.Write(SendBuffer);
end;
Über Senden der Struktur RegisterRequest werden die Schaltzustände von Inputs und Outputs angefordert. Das Web-IO beantwortet diese Anfrage mit der Struktur RegisterState.
Sollen nur die Inputs abgefragt werden, wird das durch Klicken auf den bt_inputs-Button ausgelöst. Hierzu wird die Struktur ReadRegister versendet, was das Web-IO mit der Struktur WriteRegister beantwortet.
procedure Twebio_binary_client.bt_inputs_readClick(Sender: TObject);
var
EADriver : structEADriver;
SendBuffer : TIdBytes;
begin
EADriver.Start_1 := 0;
EADriver.Start_2 := 0;
EADriver.StructType := $01;
EADriver.StructLength := $08;
SendBuffer := RawToBytes(EADriver, EADriver.StructLength);
ClientSocket.IOHandler.Write(SendBuffer);
end;
Counter abfragen/löschen
Auch die Zählerstände der Input-Counter lassen sich abfragen bzw. löschen. Dazu wird die Struktur ReadCounter bzw. ReadClearCounter versendet, wobei mit CounterIndex die Nummer des Counters übergeben wird. Das Web-IO antwortet mit der Struktur Counter.
procedure Twebio_binary_client.bt_counter_readClick(Sender: TObject);
var
ReadCounter : structReadCounter;
SendBuffer : TIdBytes;
begin
ReadCounter.EADriver.Start_1 := 0;
ReadCounter.EADriver.Start_2 := 0;
ReadCounter.EADriver.StructType := $B0;
ReadCounter.EADriver.StructLength := $0A;
if sender = bt_counter_read0 then ReadCounter.CounterIndex := 0;
if sender = bt_counter_read1 then ReadCounter.CounterIndex := 1;
SendBuffer := RawToBytes(ReadCounter, ReadCounter.EADriver.StructLength);
ClientSocket.IOHandler.Write(SendBuffer);
end;
procedure Twebio_binary_client.bt_counter_clearClick(Sender: TObject);
var
ReadCounter : structReadCounter;
SendBuffer : TIdBytes;
begin
ReadCounter.EADriver.Start_1 := 0;
ReadCounter.EADriver.Start_2 := 0;
ReadCounter.EADriver.StructType := $C0;
ReadCounter.EADriver.StructLength := $0A;
if sender = bt_counter_clear0 then ReadCounter.CounterIndex := 0;
if sender = bt_counter_clear1 then ReadCounter.CounterIndex := 1;
SendBuffer := RawToBytes(ReadCounter, ReadCounter.EADriver.StructLength);
ClientSocket.IOHandler.Write(SendBuffer);
end;
Über die Struktur ReadAllCounter bzw. ReadClearAllCounter lassen sich auch alle Counter zeitgleich lesen bzw. löschen. Das Web-IO antwortet mit der Struktur AllCounter.
procedure Twebio_binary_client.bt_counter_readallClick(Sender: TObject);
var
EADriver : structEADriver;
SendBuffer : TIdBytes;
begin
EADriver.Start_1 := 0;
EADriver.Start_2 := 0;
EADriver.StructType := $B1;
EADriver.StructLength := $08;
SendBuffer := RawToBytes(EADriver, EADriver.StructLength);
ClientSocket.IOHandler.Write(SendBuffer);
end;
procedure Twebio_binary_client.bt_counter_clearallClick(Sender: TObject);
var
EADriver : structEADriver;
SendBuffer : TIdBytes;
begin
EADriver.Start_1 := 0;
EADriver.Start_2 := 0;
EADriver.StructType := $C1;
EADriver.StructLength := $08;
SendBuffer := RawToBytes(EADriver, EADriver.StructLength);
ClientSocket.IOHandler.Write(SendBuffer);
end;
Datenempfang vom Web-IO
Auswerten und Anzeigen der empfangenen DatenDas Web-IO sendet je nach Anfrage oder auslösendem Ereignis die
passende Struktur zurück. Bei Datenempfang wird die entsprechende
Callback-Prozedur aufgerufen. Zur Auswertung befüllen die ersten 8
Byte des empfangenen Byte-Array
über BytesToRaw
zunächst eine EADriver-Struktur.
Um welchen Strukturtyp es sich handelt, erkennt
die Anwendung über die Variable EADriver.StructType.
-
EADriver.StructType = 8
Struktur WriteRegister für den Status der Inputs
-
EADriver.StructType = 31 (hex.)
Struktur RegisterState für den Status der Inputs und Outputs
-
EADriver.StructType = B4 (hex.)
Struktur Counter für den Wert einzelner Counter
-
EADriver.StructType = B5 (hex.)
Struktur AllCounter für den Wert aller Counter
Zur Auswertung befüllt das empfangenen Byte-Array
über BytesToRaw
nun die passende Struktur.
Die so übergebenen Werte werden dann ausgewertet und angezeigt. Für die Inputs und Outputs wird über WriteRegister.Value, RegisterStae.InputValue und RegisterStae.outputValue das Bit-Muster aller Inputs bzw. Outputs übergeben.
procedure Twebio_binary_client.ClientSocketThreadRun(Sender: TIdThreadComponent);
var
ReceiveBuffer : TIdBytes;
EADriver : structEADriver;
WriteRegister : structWriteRegister;
RegisterState : structRegisterState;
Counter : structCounter;
AllCounter : structAllCounter;
begin
while not ClientSocket.IOHandler.InputBufferIsEmpty do
begin
SetLength(ReceiveBuffer, length(ReceiveBuffer)+1);
ReceiveBuffer[length(ReceiveBuffer)-1] := ClientSocket.IOHandler.ReadByte;
end;
if length(ReceiveBuffer) > 7 then
BytesToRaw(ReceiveBuffer, EADriver, 8);
case EADriver.StructType of
$08 : begin
if length(ReceiveBuffer) >= EADriver.StructLength then
begin
BytesToRaw(ReceiveBuffer, WriteRegister, EADriver.StructLength);
if WriteRegister.Value and 1 = 1 then
cb_input0.Checked := True
else
cb_input0.Checked := False;
if WriteRegister.Value and 2 = 2 then
cb_input1.Checked := True
else
cb_input1.Checked := False;
end;
end;
$31 : begin
if length(ReceiveBuffer) >= EADriver.StructLength then
begin
BytesToRaw(ReceiveBuffer, RegisterState, EADriver.StructLength);
if RegisterState.InputValue and 1 = 1 then
cb_input0.Checked := True
else
cb_input0.Checked := False;
if RegisterState.InputValue and 2 = 2 then
cb_input1.Checked := True
else
cb_input1.Checked := False;
if RegisterState.OutputValue and 1 = 1 then
cb_output0.Checked := True
else
cb_output0.Checked := False;
if RegisterState.OutputValue and 2 = 2 then
cb_output1.Checked := True
else
cb_output1.Checked := False;
end;
end;
$B4 : begin
if length(ReceiveBuffer) >= EADriver.StructLength then
begin
BytesToRaw(ReceiveBuffer, Counter, EADriver.StructLength);
if Counter.CounterIndex = 0 then
ed_counter0.Text := IntToStr(Counter.CounterValue);
if Counter.CounterIndex = 1 then
ed_counter1.Text := IntToStr(Counter.CounterValue);
end;
end;
$B5 : begin
if length(ReceiveBuffer) >= EADriver.StructLength then
begin
BytesToRaw(ReceiveBuffer, AllCounter, EADriver.StructLength);
ed_counter0.Text := IntToStr(AllCounter.CounterValue[0]);
ed_counter1.Text := IntToStr(AllCounter.CounterValue[1]);
end;
end;
end;
end;
Polling
Zyklisches Abfragen bestimmter WerteUm auch eine automatische Aktualisierung der Anzeige zu ermöglichen, wird ein Timer benutzt.
In Abhängigkeit der Checkboxen für Output-, Input- und Counter-Polling werden die entsprechenden Informationen im eingestellten Intervall vom Web-IO abgerufen.
procedure Twebio_binary_client.timer_pollingTimer(Sender: TObject);
begin
if ClientSocket.Connected and cb_output_polling.Checked then
bt_outputs_readClick(self);
if ClientSocket.Connected and cb_input_polling.Checked then
bt_inputs_readClick(self);
if ClientSocket.Connected and cb_counter_polling.Checked then
bt_counter_readallClick(self);
end;
Das gewünschte Intervall kann in das entsprechende Textfeld eingegeben werden. Bei Änderung wird das Timer-Intervall dann automatisch angepasst.
procedure Twebio_binary_client.ed_intervalChange(Sender: TObject);
begin
timer_polling.Interval := strtoint(ed_interval.Text);
end;
Das Beispielprogramm unterstützt alle gängigen Funktionen des Web-IO im Binary-Socket-Modus, optimiert für das Web-IO 2x Digital Input, 2x Digital Output. Für die anderen Web-IO-Modelle müssen ggf. Anpassungen am Programm vorgenommen werden. Eine detaillierte Beschreibung zu den Binär-Strukturen finden Sie in der Binary-Kurzübersicht oder im Programmierhandbuch zum Web-IO.