Search code examples
plctwincat

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


Solution

  • 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!