W&T verbindet
Interfaces für TCP/IP, Ethernet, RS-232, RS-485, USB, 20mA, Glas- und Kunststoff-LWL, http, SNMP, OPC, Modbus TCP, I/O digital, I/O analog, ISA, PCI

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 C Sharp Web-IO steuern

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


Bedienelemente Delphi

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 EADriver

Mit 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 Bedienelemente

Um 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 Verbindung

Durch 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 Outputs

Das 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 Daten

Das 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 Werte

Um 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.

Programmbeispiel herunterladen


Produkte