TwinCAT I/O Ring 3 OCX Delphi-Applikation

Die Sourcen zu diesem Beispiel können sie hier entpacken: TwinCAT I/O Ring 3 Ocx: Delphi-Applikation.

TwinCAT I/O Ring 3 OCX Delphi-Applikation 1:

Systemvoraussetzungen:

Beschreibung

Aus der Delphi-Applikation soll das Mapping von dem Prozeßabbild der Ein-/Ausgänge einer zusätzlichen Task im TwinCAT System Manager getriggert werden. Die Zykluszeit beträgt 100ms und wird mit Hilfe eines Multimedia-Timers erzeugt. Die Onlinewerte der Prozessabbilder werden in zwei ListBoxen  angezeigt. Über den IO Reset-Button kann ein Reset der TwinCAT IO-Geräte durchgeführt werden. Das Mapping der Eingänge und Ausgänge kann über die Buttons Start IO cycle und Stop IO cycle gestartet bzw. gestoppt werden. Eventuelle Fehlermeldungen werden in einer weiteren ListBox ausgegeben. 

Die Task-Konfiguration in TwinCAT System Manager

Für die zusätzliche Task wurde im TwinCAT System Manager die Portnummer 301 konfiguriert.

TwinCAT I/O Ring 3 OCX Delphi-Applikation 2:

Das Prozessabbild hat folgende Größe:

Eingangsabbild:        2 Byte;

Ausgangsabbild:       8 Byte;     

TwinCAT I/O Ring 3 OCX Delphi-Applikation 3:

Die Portnummer und die Bytegröße des Prozessabbildes wurden in der TCatIoOcxDelphiUnit.pas-Unit als Konstanten definiert und müssen bei anderen Werten entsprechend geändert werden.

Einbinden der TCatIoOcx ActiveX-Komponente

Um die ActiveX-Komponente TCatIoOcx in Delphi-Applikationen benutzen zu können muß diese in die Komponentenpalette eingebuden werden. ActiveX-Komponenten können über den Menuebefehl: Component->Import ActiveX Control... eingebunden werden. Wählen Sie aus der Liste der installierten Komponenten das TCatIoOcx ActiveX Control Module aus und bestätigen mit Install... Bestätigen Sie dann die nachfolgenden Dialogfenster. Beim Erfolg befindet sich das TCatIoOcx in der Komponentenpalette der ActiveX-Komponenten.

TwinCAT I/O Ring 3 OCX Delphi-Applikation 4:

Die Applikation

In der Event-Funktion FormCreate wird die Methode TCatIoOcxOpen aufgerufen. Beim Erfolg liefert diese Funktion eine Null und es wird eine Verbindung zum TwinCAT I/O Subsystem aufgebaut. Ein Fehler wird in der ListBox an den Benutzer ausgegeben.  Beim Beenden der Anwendung muß die Verbindung zum TwinCAT I/O Subsystem abgebaut werden. Dies geschieht in der  Event-Funktion FormDestroy, dabei wird die Methode TCatIoOcxClose aufgerufen. Wurde die Verbindung erfolgreich aufgebaut, dann werden zwei weitere Methoden aufgerufen: TCatIoOcxGetInputPtr und TCatIoOcxGetOutputPtr. Diese Methoden liefern Zeiger auf die Prozeßabbilder der Ein-/Ausgänge. Über diese Zeiger kann auf die Prozeßdaten der Eingänge lesend und der Ausgänge schreibend zugegriffen werden. In unserem Beispiel wird aber nicht über diese Zeiger auf die Prozeßdaten zugegriffen, sondern über zwei Datenpuffer: InputImage und OutputImage. Bei der Definition der Datenpuffer muß das 4 Byte-Alignment beachtet werden.

D.h. für jedes angefangene  DWord der Prozeßdaten müssen 4 Byte Datenpuffer reserviert werden. In userem Beispiel ist der Datenpuffer der Eingänge 4  Byte ( tatsächliche Größe 2 Byte ) und der Ausgänge 12 Byte ( tatsächliche Größe 8 Byte ) groß. Die Datenpuffer dürfen größer, nicht aber kleiner sein.

unit TCatIoOcxDelphiUnit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, Grids, OleCtrls, TCATIOOCXLib_TLB;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Timer1: TTimer;
    StartButton: TButton;
    StopButton: TButton;
    Label1: TLabel;
    Button1: TButton;
    Label2: TLabel;
    Label3: TLabel;
    ListBox2: TListBox;
    ListBox3: TListBox;
    TCatIoOcx1: TTCatIoOcx;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure StartButtonClick(Sender: TObject);
    procedure StopButtonClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    procedure InitControls();
    procedure CalculateNewOutputs(pData   : Pointer; cbSize   :integer);
    procedure ViewData( var ListBox : TListBox; pData   : Pointer; cbSize   :integer);
  public
    { Public declarations }
  end;

const
 TASK_1_PORTNUMBER = 301;
    MAX_INPUT_IMAGE_BYTESIZE = 2;
    MAX_OUTPUT_IMAGE_BYTESIZE = 8;

type
    TInputImage       = ARRAY[ 0..MAX_INPUT_IMAGE_BYTESIZE DIV 4 ] Of Integer;
    TOutputImage      = ARRAY[ 0..MAX_OUTPUT_IMAGE_BYTESIZE DIV 4 ] Of Integer;

var
    Form1: TForm1;
InputImage    :TInputImage;
OutputImage       :TOutputImage;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var Result  : integer;
    InPtr, OutPtr :Integer;
begin
    InitControls();

    Result := TCatIoOcx1.TCatIoOcxOpen();
    if ( Result <> 0 ) then
    ListBox1.Items.Insert(0, Format('TCatIoOcxOpen() error:%d', [ Result ] ) )
    else
    begin
    {get/initialize pointer to the input image}
    Result := TCatIoOcx1.TCatIoOcxGetInputPtr( TASK_1_PORTNUMBER, InPtr, MAX_INPUT_IMAGE_BYTESIZE );
    if (  Result <> 0 ) then
        ListBox1.Items.Insert(0, Format('TCatIoOcxGetInputPtr() error:%d', [ Result ] ) );

    {get/initialize pointer to the output image}
    Result := TCatIoOcx1.TCatIoOcxGetOutputPtr( TASK_1_PORTNUMBER, OutPtr, MAX_OUTPUT_IMAGE_BYTESIZE );
    if (  Result <> 0 ) Then
        ListBox1.Items.Insert(0, Format('TCatIoOcxGetOutputPtr() error:%d', [ Result ] ) );
    end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
var Result : integer;
begin
    Result := TCatIoOcx1.TCatIoOcxClose();
    if ( Result <> 0 ) then
    MessageBox(0, 'TCatIoOcxClose() error!', 'Error', MB_OK);
end;

Wurde der Multimedia-Timer aktiviert, dann wird zyklisch die Timer1Timer-Ereignisroutine aufgerufen. In dieser Routine werden jedesmal folgende Methoden aufgerufen:

procedure TForm1.Timer1Timer(Sender: TObject);
var Result : integer;
begin
    try

    {Update input image}
      Result := TCatIoOcx1.TCatIoOcxInputUpdate( TASK_1_PORTNUMBER );
    if ( Result <> 0 ) then
        ListBox1.Items.Insert(0, Format('TCatIoOcxInputUpdate() error:%d', [ Result ] ) );

    {read input values}
      Result := TCatIoOcx1.TCatIoOcxGetInputData( TASK_1_PORTNUMBER,  InputImage[0], MAX_INPUT_IMAGE_BYTESIZE );
    if ( Result <> 0 ) then
        ListBox1.Items.Insert(0, Format('TCatIoOcxGetInputData() error:%d', [ Result ] ) );

    {View inputs}
    ViewData( ListBox2, @InputImage, MAX_INPUT_IMAGE_BYTESIZE );

    {Calculate new output values (running light)}
    CalculateNewOutputs( @OutputImage, MAX_OUTPUT_IMAGE_BYTESIZE );

    {write output values}
      Result := TCatIoOcx1.TCatIoOcxSetOutputData( TASK_1_PORTNUMBER,  OutputImage[0], MAX_OUTPUT_IMAGE_BYTESIZE );
    if ( Result <> 0 ) then
        ListBox1.Items.Insert(0, Format('TCatIoOcxSetOutputData() error:%d', [ Result ] ) );

    {Update output image}
      Result := TCatIoOcx1.TCatIoOcxOutputUpdate( TASK_1_PORTNUMBER );
    if ( Result <> 0 ) then
        ListBox1.Items.Insert(0, Format('TCatIoOcxOutputUpdate() error:%d', [ Result ] ) );

    {View outputs}
    ViewData( ListBox3, @OutputImage, MAX_OUTPUT_IMAGE_BYTESIZE );

    except
    Timer1.Enabled := false;
    ListBox1.Items.Insert(0, 'TCatIoOcx exception error:%d' );
    end;

end;

Die Routinen zum aktivieren/deaktivieren des Timers:

procedure TForm1.StartButtonClick(Sender: TObject);
begin
    StartButton.Enabled := false;
    StopButton.Enabled := true;
    Timer1.Enabled := true;
end;

procedure TForm1.StopButtonClick(Sender: TObject);
begin
    StartButton.Enabled := true;
    StopButton.Enabled := false;
    Timer1.Enabled := false;
end;

Die Routine für den IO-Reset:

procedure TForm1.Button1Click(Sender: TObject);
var Result : integer;
begin
    Result := TCatIoOcx1.TCatIoOcxReset();
    if ( Result <> 0 ) Then
    ListBox1.Items.Insert( 0, Format('TCatIoOcxReset() error:%d', [ Result ] ) );
end;
procedure TForm1.InitControls();
var Row   :integer;
begin
    StartButton.Enabled := true;
    StopButton.Enabled := false;

    Timer1.Enabled := false;
    Timer1.Interval := 100; {100 ms}

    for Row:= 0 To MAX_INPUT_IMAGE_BYTESIZE - 1 do
    ListBox2.Items.Add( Format( 'Byte[%d]', [Row] ) );

    for Row:= 0 To MAX_OUTPUT_IMAGE_BYTESIZE - 1 do
    ListBox3.Items.Add( Format( 'Byte[%d]', [Row] ) );
end;

procedure TForm1.CalculateNewOutputs(pData   : Pointer; cbSize   :integer);
var i:integer;
    pByte : ^Byte;
begin
    if ( pData <> NIL ) And (cbSize > 0) then
    begin
    for i:= 0 to  cbSize - 1 do
    begin
        pByte := Pointer(Integer(pData) + i);
        if ( pByte^ = 0 ) then pByte^ := 1
        else  pByte^ := pByte^ shl 1;
    end;
    end;
end;

procedure TForm1.ViewData( var ListBox : TListBox; pData   : Pointer; cbSize   :integer );
var
  ByteOff    : Integer;
  pByte      : ^Byte;
begin
    if ( pData <> NIL ) And (cbSize > 0) then
    begin
    for ByteOff := 0 to cbSize - 1 do
    begin
        pByte:=  Pointer( integer(pData) + ByteOff );
        ListBox.Items.Strings[ByteOff] := Format('Byte[%d] Value:0x%x',[ ByteOff, pByte^ ] );
    end;
    end;
end;



end.

Die Sourcen zu diesem Beispiel können sie hier entpacken: TwinCAT I/O Ring 3 Ocx: Delphi-Applikation.