Lesen und Schreiben von DT/DATE/TOD/TIME-Variablen
Voraussetzungen:
- TwinCAT v2.11 Build >= 2034;
- Delphi 7.0 + + Update 7.1 oder höher;
- TcAdsDLL.DLL;
- TcAdsDEF.pas und TcAdsAPI.pas, enthalten in der Datei delphi_adsdll_api_units.zip, falls Sie den Quelltext selbst übersetzen möchten;
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:
- TDateTime: 64 Bit Fließkommazahl. Beinhaltet das Datum (ganzzahliger Teil) und die Uhrzeit (Bruchteil). Der ganzzahlige Anteil repräsentiert die Anzahl der vergangenen Tage seit dem 30.12.1899. Der Bruchteil gibt die Uhrzeit an (Bruchteil eines 24-Stunden Tages). Durch die Kodierung der Fließkommazahl ist die Auflösung bei sehr großen Werten kleiner (kleinere Anzahl der Nachkommastellen, die zur Verfügung stehen).
- TDate: 64 Bit Fließkommazahl, TDateTime-Aliastyp, beinhaltet nur das Datum (ganzzahliger Teil). Anzahl der vergangenen Tage seit dem 30.12.1899;
- TTime: 64 Bit Fließkommazahl, TDateTime-Aliastyp, beinhaltet nur die Uhrzeit (Bruchteil eines 24 Stunden Tages);
- Longword, 32 Bit Zählwert, in unserer Applikation soll er die Zeitdauer in Millisekunden Auflösung beinhalten (keine Uhrzeit);
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:
- DT bzw. DATE_AND_TIME: vorzeichenloser 32 Bit Zählwert, beinhaltet das Datum und Uhrzeit, Anzahl der vergangenen Sekunden seit dem 01.01.1970, Auflösung in Sekunden;
- DATE: vorzeichenloser 32 Bit Zählwert, beinhaltet nur das Datum, Anzahl der vergangenen Sekunden seit dem 01.01.1970. Auflösung in Sekunden;
- TOD bzw TIME_OF_DAY: vorzeichenloser 32 Bit Zählwert, beinhaltet nur die Uhrzeit. Anzahl der vergangenen Millisekunden seit dem Anfang des Tages (00:00Uhr), Auflösung in Millisekunden;
- TIME, vorzeichenloser 32 Bit Zählwert, beinhaltet die Zeitdauer (keine Uhrzeit), Auflösung in Millisekunden;
Beim Schreiben der Werte in die TwinCAT SPS wird folgende Typkonvertierung/Mapping durchgeführt:
- TDateTimePicker.DateTime property (TDateTime) -> DT bzw. DATE_AND_TIME
- TDateTimePicker.Date property (TDate) -> DATE
- TDateTimePicker.Time property (TTime) -> TOD bzw. TIME_OF_DAY
- TEdit.Text property (Longword) -> TIME
Beim Lesen der Werte aus der TwinCAT SPS wird die Typkonvertierung in die umgekehrte Richtung durchgeführt.
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.
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 | |
Delphi 7 oder höher (classic) |