Beispiel: Dateizugriff aus der SPS
Systemvoraussetzungen:
- TwinCAT 2.9;
In diesem Beispiel wird die Anwendung der SPS-Funktionsbausteine für den Dateizugriff aus der TcSystem.Lib vorgestellt. Mit Hilfe der vorhandenen Funktionsbausteine wurde ein neuer Funktionsbaustein FB_FileCopy realisiert. Mit diesem Baustein können Binärdateien z.B. auf dem lokalen TwinCAT PC oder von einem Remote-TwinCAT PC auf den lokalen TwinCAT PC kopiert werden. Mit den Funktionsbausteinen kann nicht auf Netzwerklaufwerke zugegriffen werden. Bei einer steigenden Flanke am bExecute-Eingang des FB_FileCopy-Bausteines werden folgende Schritte ausgeführt.
a) Öffnen der Quell- und Ziel-Datei;
b) Lesen der Quell-Datei in einen Puffer;
c) Schreiben der gelesenen Bytes aus dem Puffer in die Ziel-Datei;
d) Überprüfen, ob das Ende der Quell-Datei erreicht wurde. Wenn nicht dann b) und c) wiederholen. Wenn ja, dann zu e) springen;
e) Schließen der Quell- und Ziel-Datei;
Die Datei wird stückweise kopiert. Die Größe des Puffers wurde im Beispiel auf 1000 Byte festgelegt, kann aber geändert werden. Die kompletten Sourcen zu dem Beispielprojekt können hier entpackt werden: Dateizugriff aus der SPS.
Das SPS-Programm:
PROGRAM MAIN
VAR
fbFileCopy : FB_FileCopy;
bCopy : BOOL;
bBusy : BOOL;
bError : BOOL;
nErrId : UDINT;
END_VAR
Im Beispiel wird die Settings.txt-Datei aus dem Temp-Ordner von einem Remote-TwinCAT-PC mit der Netzwerkadresse: "172.16.2.209.1.1" in den TwinCAT-Ordner auf dem lokalen TwinCAT PC kopiert.
Der FB_FileCopy-Funktionsbaustein
Interface:
FUNCTION_BLOCK FB_FileCopy
VAR_INPUT
sSrcNetId : T_AmsNetId;(* TwinCAT network address of the source file *)
sSrcPathName : T_MaxString;(* Source file path and name *)
sDestNetId : T_AmsNetId;(* TwinCAT network address of the destination file *)
sDestPathName : T_MaxString; (* Destination file path and name *)
bExecute : BOOL; (* Rising edge start fb execution *)
tTimeOut : TIME := DEFAULT_ADS_TIMEOUT;(* Max. ADS timeout time *)
END_VAR
VAR_OUTPUT
bBusy :BOOL;(* TRUE => File copy execution in progress, FALSE => File copy execution idle *)
bError :BOOL;(* TRUE => Error, FALSE => No error *)
nErrId :UDINT;(* Error code *)
END_VAR
VAR
fbFileOpen :FB_FileOpen;
fbFileClose :FB_FileClose;
fbFileRead :FB_FileRead;
fbFileWrite :FB_FileWrite;
hSrcFile :UINT := 0;(* File handle of the source file *)
hDestFile :UINT := 0;(* File handle of the destination file *)
Step :DWORD;
RisingEdge :R_TRIG;
buffRead :ARRAY[1..1000] OF BYTE;(* Buffer *)
cbReadLength :UDINT := 0;
END_VAR
Implementierung:
RisingEdge(CLK:=bExecute);
CASE Step OF
0: (* Idle state *)
IF RisingEdge.Q THEN
bBusy := TRUE;
bError:= FALSE;
nErrId:=0;
Step := 1;
cbReadLength:=0;
hSrcFile:=0;
hDestFile:=0;
END_IF
1: (* Open source file *)
fbFileOpen( bExecute := FALSE );
fbFileOpen( sNetId := sSrcNetId, sPathName := sSrcPathName,
nMode := FOPEN_MODEREAD OR FOPEN_MODEBINARY,
ePath := PATH_GENERIC, tTimeout := tTimeOut, bExecute := TRUE );
Step := Step + 1;
2:
fbFileOpen( bExecute := FALSE );
IF NOT fbFileOpen.bBusy THEN
IF fbFileOpen.bError THEN
nErrId := fbFileOpen.nErrId;
bError := TRUE;
Step := 50;
ELSE
hSrcFile := fbFileOpen.hFile;
Step := Step + 1;
END_IF
END_IF
3: (* Open destination file *)
fbFileOpen( bExecute := FALSE );
fbFileOpen( sNetId := sDestNetId, sPathName := sDestPathName,
nMode := FOPEN_MODEWRITE OR FOPEN_MODEBINARY,
ePath := PATH_GENERIC, tTimeout := tTimeOut, bExecute := TRUE );
Step := Step+1;
4:
fbFileOpen( bExecute := FALSE );
IF NOT fbFileOpen.bBusy THEN
IF fbFileOpen.bError THEN
nErrId := fbFileOpen.nErrId;
bError := TRUE;
Step := 50;
ELSE
hDestFile := fbFileOpen.hFile;
Step := Step + 1;
END_IF
END_IF
5: (* Read data from source file *)
cbReadLength := 0;
fbFileRead( bExecute:= FALSE );
fbFileRead( sNetId:=sSrcNetId, hFile:=hSrcFile,
pReadBuff:= ADR(buffRead), cbReadLen:= SIZEOF(buffRead),
bExecute:=TRUE, tTimeout:=tTimeOut );
Step := Step + 1;
6:
fbFileRead( bExecute:= FALSE );
IF NOT fbFileRead.bBusy THEN
IF fbFileRead.bError THEN
nErrId := fbFileRead.nErrId;
bError := TRUE;
Step := 50;
ELSE
cbReadLength := fbFileRead.cbRead;
Step := Step + 1;
END_IF
END_IF
7: (* Write data to destination file *)
fbFileWrite( bExecute := FALSE );
fbFileWrite( sNetId:=sDestNetId, hFile:=hDestFile,
pWriteBuff:= ADR(buffRead), cbWriteLen:= cbReadLength,
bExecute:=TRUE, tTimeout:=tTimeOut );
Step := Step + 1;
8:
fbFileWrite( bExecute := FALSE );
IF NOT fbFileWrite.bBusy THEN
IF fbFileWrite.bError THEN
nErrId := fbFileWrite.nErrId;
bError := TRUE;
Step := 50;
ELSE
IF fbFileRead.bEOF THEN (* Check if the EOF flag ist set *)
Step := 50;(* Cleanup: close the destination and source files *)
ELSE
Step := 5; (* Repeat reading/writing *)
END_IF
END_IF
END_IF
30: (* Close the destination file *)
fbFileClose( bExecute := FALSE );
fbFileClose( sNetId:=sDestNetId, hFile:=hDestFile, bExecute:=TRUE, tTimeout:=tTimeOut );
Step := Step + 1;
31:
fbFileClose( bExecute := FALSE );
IF NOT fbFileClose.bBusy THEN
IF fbFileClose.bError THEN
nErrId := fbFileClose.nErrId;
bError := TRUE;
END_IF
Step := 50;
hDestFile := 0;
END_IF
40: (* Close source file *)
fbFileClose( bExecute := FALSE );
fbFileClose( sNetId:=sSrcNetId, hFile:=hSrcFile, bExecute:=TRUE, tTimeout:=tTimeOut );
Step := Step + 1;
41:
fbFileClose( bExecute := FALSE );
IF NOT fbFileClose.bBusy THEN
IF fbFileClose.bError THEN
nErrId := fbFileClose.nErrId;
bError := TRUE;
END_IF
Step := 50;
hSrcFile := 0;
END_IF
50: (* Error or ready => Cleanup *)
IF ( hDestFile <> 0 ) THEN
Step := 30; (* Close the destination file*)
ELSIF (hSrcFile <> 0 ) THEN
Step := 40; (* Close the source file *)
ELSE
Step := 0; (* Ready *)
bBusy := FALSE;
END_IF
END_CASE
Die kompletten Sourcen zu dem Beispielprojekt können hier entpackt werden: Dateizugriff aus der SPS.