Lesen und Schreiben von DT/DATE/TOD/TIME-Variablen

Voraussetzungen:

Aufgabenstellung

Das Datum und/oder Uhrzeit und eine Zeitdauer soll aus der Delphi-Applikation in die TwinCAT SPS geschrieben bzw. aus der TwinCAT SPS in die Delphi-Applikation eingelesen werden. Beim Schreiben der Werte in die SPS wird die in der visuellen Komponente TDateTimePicker eingestellte Uhrzeit und/oder Datum zuerst in das passende SPS-Format konvertiert und dann in die entsprechende SPS-Variable geschrieben. Die gelesenen Werte aus der TwinCAT SPS werden wiederum in das passenden Delphi-Format konvertiert und durch die TDateTimePicker-Komponente angezeigt. Die Zeitdauer soll in Millisekunden von einem TEdit control angezeigt bzw. editiert werden können.

In der Delphi-Applikation stehen unter anderem folgende Datentypen zur Verfügung:

Die Verwendung des TDateTime-Datentyps ermöglicht die Verwendung der zur Verfügung stehenden Delphi-Zeitkonvertierungsfunktionen. Insbesondere die zwei Konvertierungsfunktionen DateTimeToUnix() und UnixToDateTime(). Sie können aber stattdessen genauso einen 32  Bit Integer Wert oder einen anderen Datentyp verwenden, dort die Uhrzeit und Datum passend kodieren, konvertieren und dann in die SPS schreiben. Die unten vorgestellte Typkonvertierung/Mapping der Datentypen soll nicht als Vorschrift angesehen werden.

In der TwinCAT SPS stehen folgende Datentypen zur Verfügung:

Beim Schreiben der Werte in die TwinCAT SPS wird folgende Typkonvertierung/Mapping durchgeführt:

Beim Lesen der Werte aus der TwinCAT SPS wird die Typkonvertierung in die umgekehrte Richtung durchgeführt.

Lesen und Schreiben von DT/DATE/TOD/TIME-Variablen 1:

Je nachdem, ob die Pick->Date- oder Pick->Time-Option angewählt wurde kann das Datum bzw. die Uhrzeit in der visuellen TDateTimePicker-Komponente eingestellt werden.

Lesen und Schreiben von DT/DATE/TOD/TIME-Variablen 2:

Die Zeitdauer in Millisekunden kann über das TEdit control im unteren Bereich eingestellt werden.

Beim Mausklick auf den entsprechenden Befehl-Button wird die Uhrzeit und/oder Datum oder die Zeitdauer in die TwinCAT-SPS geschrieben bzw. aus der SPS gelesen und angezeigt.

Das SPS-Programm

In MAIN wurde von jedem der SPS-Typen (Datum/Zeit) jeweils eine SPS-Variable deklariert. Die Delphi-Applikation soll die Werte schreiben oder lesen.

PROGRAM MAIN
VAR
    vDateAndTime    : DATE_AND_TIME;
    vDate       : DATE;
    vTimeOfDay      : TIME_OF_DAY;
    vTime       : TIME;
END_VAR
;

Delphi 7 Programm

Die Deklarationen der benutzten TcAdsDLL-Funktionen befinden sich in den Pascal-Units TcAdsDEF.pas und TcAdsAPI.pas. Diese wurden über eine Uses-Klausel in das Projekt eingebunden. Die Unit: DateUtils beinhaltet die benötigten Zeitkonvertierungsfunktionen und muss ebenfalls eingebunden werden.

unit Unit1;
interface
uses
  TcAdsDEF, TcAdsAPI, Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, Grids, Calendar, StdCtrls, DateUtils, ExtCtrls;
type
  TForm1 = class(TForm)
    Button2: TButton;
    Button3: TButton;
    StatusBar1: TStatusBar;
    Button4: TButton;
    Button5: TButton;
    Button6: TButton;
    Button7: TButton;
    Button9: TButton;
    Button8: TButton;
    Edit1: TEdit;
    UpDown1: TUpDown;
    RadioGroup1: TRadioGroup;
    DateTimePicker1: TDateTimePicker;
    Label2: TLabel;
    procedure Button2Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
    procedure Button8Click(Sender: TObject);
    procedure Button9Click(Sender: TObject);
    procedure RadioGroup1Click(Sender: TObject);
  private
    { Private declarations }
    LocalAddr     : TAmsAddr;
    ServerAddr    : TAmsAddr;
    adsResult     : Longint;
    hDateAndTime  : Longword;
    hDate     : Longword;
    hTimeOfDay    : Longword;
    hTime     : Longword;
    function AdsCreateVarHandle( const name : AnsiString ) : Longword;
    function AdsReleaseVarHandle( hVar : Longword ) : boolean;
    function AdsWriteUI32( hVar : Longword; value : Longword ) : boolean;
    function AdsReadUI32( hVar : Longword; var value : Longword ) : boolean;
  public
    { Public declarations }
  end;
var
  Form1: TForm1;

Aufbauen der Ads-Clientverbindung zur TwinCAT SPS

Die Delphi-Applikation baut beim Start eine Ads-Clientverbindung zur TwinCAT SPS (erstes Laufzeitsystem, Port: 801) auf und holt Handles für die 4 SPS-Variablen. Beim Beenden der Applikation werden die Handles freigegeben und zum Schluss die Ads-Clientverbindung geschlossen. Der Code zum Holen und Freigeben der Variablen-Handles wurde in zwei internen Hilfsfunktionen gekapselt: AdsCreateVarHandle() und AdsReleaseVarHandle().

//////////////////////////////////////////////////////////////////
// Create ads client connection, create PLC variable handles
procedure TForm1.FormCreate(Sender: TObject);
var   ClientPort:Word;
begin
   StatusBar1.SimplePanel := true;
   ClientPort:= AdsPortOpen();
   if ClientPort = 0 then {error!}
      ShowMessage( 'AdsPortOpen() error!' )
   else  {OK}
   begin
      adsResult:=AdsGetLocalAddress( @LocalAddr );
      if adsResult = 0 then {OK}
      begin
     ServerAddr.netId:=LocalAddr.netId;{connect to the PLC on the local PC}
     ServerAddr.port:=801; {first RTS}
     StatusBar1.SimpleText:=Format('Client NetId:%d.%d.%d.%d.%d.%d  Client Port:%d,  Server Port:%d',[
         LocalAddr.netId.b[0], LocalAddr.netId.b[1], LocalAddr.netId.b[2],
         LocalAddr.netId.b[3], LocalAddr.netId.b[4], LocalAddr.netId.b[5],
          ClientPort, ServerAddr.port]);
     hDateAndTime := AdsCreateVarHandle( 'MAIN.vDateAndTime' );
     hDate := AdsCreateVarHandle( 'MAIN.vDate' );
     hTimeOfDay := AdsCreateVarHandle( 'MAIN.vTimeOfDay' );
     hTime := AdsCreateVarHandle( 'MAIN.vTime' );
      end
      else
     ShowMessageFmt('AdsGetLocalAddress() error:%d', [adsResult]);
   end;
end;

Trennung der Ads-Clientverbindung zur TwinCAT SPS

//////////////////////////////////////////////////////////////////
// Release PLC variable handles, close ads connection
procedure TForm1.FormDestroy(Sender: TObject);
begin
   AdsReleaseVarHandle( hDateAndTime );
   AdsReleaseVarHandle( hDate );
   AdsReleaseVarHandle( hTimeOfDay );
   AdsReleaseVarHandle( hTime );
   adsResult := AdsPortClose();
   if AdsResult > 0 then
      ShowMessageFmt('AdsPortClose() error:%d', [adsResult]);
end;

Handle der SPS-Variablen holen

/////////////////////////////////////////////////////////////////
// Create PLC variable handle
function TForm1.AdsCreateVarHandle( const name : AnsiString ) : Longword;
var hVar : Longword;
begin
   adsResult := AdsSyncReadWriteReq( @serverAddr, ADSIGRP_SYM_HNDBYNAME, 0, sizeof(hVar), @hVar, Length(name)+1, @name[1] );
   if adsResult <> 0 then
   begin
      ShowMessageFmt( 'AdsSyncReadWriteReq(ADSIGRP_SYM_HNDBYNAME) error: %d', [adsResult] );
      AdsCreateVarHandle := 0;
   end
   else
      AdsCreateVarHandle := hVar;
end;

Handle der SPS-Variablen freigeben

//////////////////////////////////////////////////////////////////
// Release PLC variable handle
function TForm1.AdsReleaseVarHandle( hVar : Longword ) : boolean;
begin
   adsResult := AdsSyncWriteReq( @serverAddr, ADSIGRP_RELEASE_SYMHND, hVar, 0, Nil );
   if adsResult <> 0 then
      ShowMessageFmt( 'AdsSyncReadWriteReq(ADSIGRP_RELEASE_SYMHND) error: %d', [adsResult] );
   AdsReleaseVarHandle := adsResult = 0;
end;

Wert in die SPS schreiben (32 bit unsigned integer)

In der TwinCAT SPS werden die Zeitdatentypen intern in vorzeichenlose 32 Bit Zählwerten gehalten/abgebildet. Nach der Konvertierung in das Zeitformat der SPS müssen die Werte als vorzeichenlose 32 Bit Zahl in die SPS geschrieben werden. Der Code zum Schreiben und Lesen der 32 Bit Werte wurde in zwei internen Hilfsfunktionen gekapselt: AdsWriteUI32() und AdsReadUI32().

//////////////////////////////////////////////////////////////////
// Write unsigned 32 bit integer value to the PLC
function TForm1.AdsWriteUI32( hVar : Longword; value : Longword ) : boolean;
begin
   adsResult := AdsSyncWriteReq( @ServerAddr, ADSIGRP_SYM_VALBYHND, hVar, sizeof(value), @value );
   if adsResult <> 0 then
      ShowMessageFmt('AdsSyncWriteReq(ADSIGRP_SYM_VALBYHND) error:%d', [adsResult]);
   AdsWriteUI32 := adsResult = 0;
end;

Wert aus der SPS lesen (32 bit unsigned integer)

//////////////////////////////////////////////////////////////////
// Read unsigned 32 bit integer value from the PLC
function TForm1.AdsReadUI32( hVar : Longword; var value : Longword ) : boolean;
begin
   adsResult := AdsSyncReadReq( @ServerAddr, ADSIGRP_SYM_VALBYHND, hVar, sizeof(value), @value );
   if adsResult <> 0 then
      ShowMessageFmt('AdsSyncReadReq(ADSIGRP_SYM_VALBYHND) error:%d', [adsResult]);
   AdsReadUI32 := adsResult = 0;
end;

DATE_AND_TIME in die SPS schreiben

//////////////////////////////////////////////////////////////////
// Convert TDateTime -> DATE_AND_TIME
// Write DATE_AND_TIME value to the PLC
procedure TForm1.Button2Click(Sender: TObject);
var
   seconds : Longword;
   unix : Int64;
   dt : TDateTime;
begin
   // Get TDateTime value from TDateTimePicker component
   dt := DateTimePicker1.DateTime;
   // Truncate milliseconds (prevents from rounding up)
   dt := RecodeMillisecond( dt, 0 );
   // Convert TDateTime -> Unix time, 64 bit, seconds since 1.1.1970-00:00:00
   unix := DateTimeToUnix( dt );
   // Convert 64 bit -> 32 bit, seconds since 1.1.1970-00:00:00
   seconds := unix;
   // Wite DATE_AND_TIME value to the PLCAdsWriteUI32( hDateAndTime, seconds );
end;

DATE_AND_TIME aus der SPS lesen

//////////////////////////////////////////////////////////////////
// Read DATE_AND_TIME value from the PLC
// Convert DATE_AND_TIME -> TDateTime
procedure TForm1.Button3Click(Sender: TObject);
var
   seconds : Longword;
   unix : Int64;
   dt : TDateTime;
begin
   // Read DATE_AND_TIME value from PLC, 32 bit, seconds since 1.1.1970-00:00:00
   if AdsReadUI32( hDateAndTime, seconds ) then
   begin
      // Create Unix time, 64 bit, seconds since 1.1.1970-00:00:00
      unix := seconds;
      // Convert Unix time -> TDateTime
      dt := UnixToDateTime( unix );
      // Assign TDateTime value to the TDateTimePicker component
      DateTimePicker1.DateTime := dt;
   end;
end;

DATE in die SPS schreiben

//////////////////////////////////////////////////////////////////
// Convert TDate -> DATE
// Write DATE value to the PLC
procedure TForm1.Button4Click(Sender: TObject);
var
   seconds : Longword;
   unix  : Int64;
   d : TDate;
begin
   // Get TDate value from TDateTimePicker component
   d := DateOf( DateTimePicker1.DateTime );

   // Convert TDate -> Unix time, 64 bit, seconds since 1.1.1970-00:00:00
   unix := DateTimeToUnix( d );
   // Convert 64 bit -> 32 bit, seconds since 1.1.1970-00:00:00
   seconds := unix;
   // Wite DATE value to the PLCAdsWriteUI32( hDate, seconds );
end;

DATE aus der SPS lesen

//////////////////////////////////////////////////////////////////
// Read DATE value from the PLC
// Convert DATE -> TDate
procedure TForm1.Button5Click(Sender: TObject);
var
   seconds : Longword;
   unix  : Int64;
   d : TDate;
begin
   // Read DATE value from PLC, 32 bit, seconds since 1.1.1970-00:00:00
   if AdsReadUI32( hDate, seconds ) then
   begin
      // Create Unix time, 64 bit, seconds since 1.1.1970-00:00:00
      unix := seconds;
      // Convert Unix time -> TDate
      d := DateOf( UnixToDateTime( unix ) );
      // Assign TDate value to the TDateTimePicker component
      DateTimePicker1.Date := d;
   end;
end;

TIME_OF_DAY in die SPS schreiben

//////////////////////////////////////////////////////////////////
// Convert TTime -> TIME_OF_DAY
// Write TIME_OF_DAY value to the PLC
procedure TForm1.Button6Click(Sender: TObject);
var
   milliseconds : Longword;
   t : TTime;
begin
   // Get TTime value from TDateTimePicker component
   t := TimeOf(DateTimePicker1.DateTime);
   // Truncate milliseconds (prevents from rounding up)
   t := RecodeMillisecond( t, 0 );
   // Convert TTime -> Milliseconds of the day, 32 bit
   milliseconds := MillisecondOfTheDay( t );
   // Write TIME_OF_DAY value to the PLCAdsWriteUI32( hTimeOfDay, milliseconds );
end;

TIME_OF_DAY aus der SPS lesen

//////////////////////////////////////////////////////////////////
// Read TIME_OF_DAY value from the PLC
// Convert TIME_OF_DAY -> TTime
procedure TForm1.Button7Click(Sender: TObject);
var
   milliseconds : Longword;
   unix : Int64;
   t   : TTime;
begin
   // Read TIME_OF_DAY value from PLC, 32 bit, milliseconds of the day
   if AdsReadUI32( hTimeOfDay, milliseconds ) then
   begin
      // Create Unix time, 64 bit, seconds since 1.1.1970-00:00:00
      unix := milliseconds Div 1000;
      // Convert Unix time -> TTime
      t := TimeOf( UnixToDateTime( unix ) );
      // Assign TTime value to the TDateTimePicker component
      DateTimePicker1.Time := t;
   end;
end;

TIME (Zeitdauer) in die SPS schreiben

//////////////////////////////////////////////////////////////////
// Write TIME value to the PLC
procedure TForm1.Button8Click(Sender: TObject);
var milliseconds : Longword;
begin
   milliseconds := UpDown1.Position;
   AdsWriteUI32( hTime, milliseconds );
end;

TIME (Zeitdauer) aus der SPS lesen

//////////////////////////////////////////////////////////////////
// Read TIME value from the PLC
procedure TForm1.Button9Click(Sender: TObject);
var milliseconds : Longword;
begin
   if AdsReadUI32( hTime, milliseconds ) then
      UpDown1.Position := milliseconds;
end;
end.

Sprache / IDE

Beispielprogram auspacken

Delphi XE2

Sample09.exe

Delphi 7 oder höher (classic)

Sample09.exe