I want to export some data from my PLC by writing it into a text-file and saving it to an USB-Stick. I managed to create the text file, but I can't write anything.
I use functions from the TwinCAT standard-libraries in the following code:
PROGRAM P_WriteFile
VAR
nStateP : INT := 1;
fbOpenFile : FB_FileOpen; // open or create file
fbWriteFile : FB_FilePuts; // write to file
fbCloseFile : FB_FileClose; // Close file
sPath : STRING := '\Hard Disk2\foobar.txt'; // target path
sAmsNetID : STRING := '1.23.34.456.1.1';
sOutput : STRING := 'foo';
bDone : BOOL;
END_VAR
CASE nStateP OF
1:
// open/create file
fbOpenFile(sNetId := sAmsNetID, sPathName := sPath, nMode := 2, bExecute := TRUE, tTimeout := INT_TO_TIME(200), bBusy =>, bError => , nErrId =>, hFile => );
IF fbOpenFile.bBusy THEN
nStateP := 2;
END_IF
2:
// write to file
IF NOT fbOpenFile.bError THEN
fbWriteFile(sNetId := sAmsNetID, hFile := fbOpenFile.hFile, sLine := sOutput, bExecute := TRUE, tTimeout := INT_TO_TIME(200), bBusy =>, bError =>, nErrId =>);
fbOpenFile(bExecute := FALSE);
END_IF
IF fbWriteFile.bBusy THEN
nStateP := 3;
END_IF
3:
// Close file
IF NOT fbWriteFile.bBusy AND NOT fbWriteFile.bError THEN
fbCloseFile(sNetId := sAmsNetID, hFile := fbOpenFile.hFile, bExecute := TRUE, tTimeout := INT_TO_TIME(200), bBusy =>, bError =>, nErrId =>);
END_IF
IF fbWriteFile.bBusy THEN
nStateP := 4;
END_IF
4:
IF NOT fbCloseFile.bBusy AND NOT fbCloseFile.bError THEN
bDone := TRUE;
nStateP := 1;
ELSE
bDone := FALSE;
END_IF
END_CASE
The program enters all states, but the result is an empty text file, which I can't open on the control Panel. ("A sharing Violation occured while accessing \Hard Disk2\foobar.txt")
Also the bBusy - variable of the functions (e.g. FB_FileOpen.bBusy) don't change back to 'FALSE'.
It would be great if anyone could help me! Thanks :)
Generally: What the busy-flag is telling you is that the function block is currently busy doing the operation that you are requesting the FB to do. This means that you should not change state of your state-machine when it is busy, but the other way around. You should also check whether the operation was successful or not (by looking at the bError-flag) prior to going to the next step. As long as the function block that you are calling is busy (bBusy = true), you call the function block with the bExecute-flag set to low. What I usually do is to set this up as two separate stages for opening, such as:
Some sort of pseudo-code:
Step1_Open:
FBOPENFILE(bExecute=TRUE)...
GOTO STEP2_OPEN
Step2_Open:
FBOPENFILE(bExecute=FALSE)
IF NOT FBOPENFILE.bBusy AND NOT FBOPENFILE.bError THEN
GOTO Step3_StartWrite
END_IF
Step3_StartWrite
FBWRITEFILE(bExecute=TRUE)
GOTO STEP4_WRITEFILE
Step4_Writefile:
FBWRITEFILEFILE(bExecute=FALSE)
IF NOT FBWRITEFILEFILE.bBusy AND NOT FBWRITEFILEFILE.bError THEN
NEXT STEP
END_IF
...and so forth...
So in your example your stage 2 is very critical. You should not close the file until the writing is finished, which it will be as soon as bBusy is false. What you're basically doing is to close down the file while it's still writing it! Also, you can remove the "fbOpenFile(bExecute := FALSE);" in this stage, because as soon as you have (successfully) opened the file and have a file handle, you don't need to make any more calls to this function block anymore.
Other thoughts:
Is the sAmsNetId the local one of your computer? If it's the local one I don't think you need to provide it.
I've written my own file-writer which I've been using for quite some time and which is working. The code for it is:
fbRisingEdge(CLK := bExecute);
CASE eFileWriteStep OF
E_FileWriteStep.IDLE :
IF fbRisingEdge.Q THEN
nFileHandle := 0;
bBusy := TRUE;
eFileWriteStep := E_FileWriteStep.OPEN;
nFileWriteSubStep := 0;
END_IF
E_FileWriteStep.OPEN :
CASE nFileWriteSubStep OF
0 :
fbFileOpen(sPathName := sPathName, bExecute := FALSE);
fbFileOpen(sPathName := sPathName, bExecute := TRUE);
nFileWriteSubStep := nFileWriteSubStep + 1;
1 :
fbFileOpen(bExecute := FALSE);
IF NOT fbFileOpen.bBusy THEN
IF fbFileOpen.bError THEN
bError := TRUE;
eFileWriteStep := E_FileWriteStep.CLEAN;
nFileWriteSubStep := 0;
ELSE
nFileHandle := fbFileOpen.hFile;
eFileWriteStep := E_FileWriteStep.WRITE;
nFileWriteSubStep := 0;
END_IF
END_IF
END_CASE
E_FileWriteStep.WRITE :
CASE nFileWriteSubStep OF
0 :
fbFileWrite(bExecute := FALSE);
fbFileWrite(hFile := nFileHandle,
pWriteBuff := aFileData,
cbWriteLen := UDINT_TO_UINT(UPPER_BOUND(aFileData, 1)),
bExecute := TRUE);
nFileWriteSubStep := nFileWriteSubStep + 1;
1 :
fbFileWrite(bExecute := FALSE);
IF NOT fbFileWrite.bBusy THEN
IF fbFileWrite.bError THEN
bError := TRUE;
eFileWriteStep := E_FileWriteStep.CLEAN;
ELSE
eFileWriteStep := E_FileWriteStep.CLEAN;
nBytesWritten := fbFileWrite.cbWrite;
END_IF
nFileWriteSubStep := 0;
END_IF
END_CASE
E_FileWriteStep.CLOSE :
CASE nFileWriteSubStep OF
0 :
fbFileClose(bExecute := FALSE);
fbFileClose(hFile := nFileHandle, bExecute := TRUE);
nFileWriteSubStep := 1;
1 :
fbFileClose(bExecute := FALSE);
IF NOT fbFileClose.bBusy THEN
IF fbFileClose.bError THEN
bError := TRUE;
END_IF
eFileWriteStep := E_FileWriteStep.CLEAN;
nFileHandle := 0;
nFileWriteSubStep := 0;
END_IF
END_CASE
E_FileWriteStep.CLEAN :
IF nFileHandle <> 0 THEN
eFileWriteStep := E_FileWriteStep.CLOSE;
nFileWriteSubStep := 0;
ELSE
eFileWriteStep := E_FileWriteStep.IDLE;
bBusy := FALSE;
END_IF
END_CASE
You activate the function block by the rising edge at the beginning. The data to be written is provided by an array of bytes (aFileData). At the end of this state machine you also have some cleaning code plus eventual error-handling. In this code you can also see how I make sure that the previous step succeeds before I go on to the next step.
Good luck!