Example: File access from the PLC
System requirements:
- TwinCAT 2.9;
This example illustrates use of the PLC function blocks from the TcSystem.Lib for file access. A new function block, FB_FileCopy, is implemented with the aid of the existing function blocks. This block can be used, for instance, to copy binary data on the local TwinCAT PC or from a remote TwinCAT PC to the local TwinCAT PC. The function block cannot be used to access network drives. A rising edge at the bExecute input of the FB_FileCopy block results in execution of the following steps.
a) Open the source and destination files;
b) Read the source file into a buffer;
c) Write the bytes that have been read from the buffer into the destination file;
d) Check whether the end of the source file has been reached. If not, then repeat b) and c). If yes, then jump to e);
e) Close the source and destination files;
The file is copied one segment at a time. In this example, the size of the buffer has been specified as 1000 bytes, but this can be modified. The complete source code for the example project can be unpacked from here: File access from the PLC.
The PLC program:
PROGRAM MAIN
VAR
fbFileCopy : FB_FileCopy;
bCopy : BOOL;
bBusy : BOOL;
bError : BOOL;
nErrId : UDINT;
END_VAR
In this example, the file Settings.txt is copied from the Temp directory of a remote TwinCAT PC having network address "172.16.2.209.1.1" to the TwinCAT directory on the local TwinCAT PC.
The FB_FileCopy Function Block
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
Implementation:
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
The complete source code for the example project can be unpacked from here: File access from the PLC.