l.voe
l.voe

Reputation: 93

TwinCAT 3: Write to File

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 :)

Upvotes: 4

Views: 10670

Answers (2)

pboedker
pboedker

Reputation: 533

The main problem in your program is that you are calling the function blocks only once, as if they were normal functions. You must call the function blocks continuously, and check for when they have finished their specific function.
When calling them continuously, you should first wait for the Busy flag going high and then wait for it going low (and without error). This will be easiest if you have a state for both waiting situation and calls the function block in both states.
Remember, that these file operation function blocks are working together with the Windows system and might take some time to finish their job.

BTW, your code example might actually work, if you add a 'NOT fbOpenFile.bBusy' criteria to the first IF in state 2. But the program would be easier to read/debug if you use two states for starting and finishing for each of the FB's.

Upvotes: 0

Jakob
Jakob

Reputation: 1540

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!

Upvotes: 5

Related Questions