ziga
ziga

Reputation: 390

"File.Flush()" alternative in TwinCAT3

I was wondering if there is there a way to flush the data that is to be written to a file other than closing the file in TwinCAT3?

Usually there is a .Flush() method that would move all the data from the internal buffers to the files, but in TwinCAT there is no such method and from what I saw, the only way for the content to appear is to close the file. In CodeSys there is such a function, but since that would only work for local files I was wondering if there is another way to do it in TwinCAT3. Currently, my flush method closes the file and re-opens it without the outside world knowing that the files was closed, but it feels a bit "hacky". Another thing this would be a problem for is, if the file was opened in FOPEN_MODEWRITE OR FOPEN_MODEPLUS I would also need to re-open it in a mode that does not overwrite the content, which in turn changes the access mode from the primary mode provided when opened.

I never really thought about this, but making tests for a library, I would validate my "write" functions by reading the file and assert the content, but until the files is re-opened the reading would return empty content:

METHOD Test06_WriteLine_FileOpened_Append_ExpectNoError_ExpectOldAndNewContent
VAR_INST
    _fileHandler: FB_FileHandler;
    content     : Tc2_System.T_MaxString;
    state       : INT;
    WRITE_TEST_CONTENT_OVERWRITE_EMPTY_FILE     : Tc2_System.T_MaxString := 'Some random text...';
    WRITE_TEST_CONTENT_APPEND                   : Tc2_System.T_MaxString := 'Some more text....';
    WRITE_TEST_CONTENT_OVERWRITE_EXTISTING      : Tc2_System.T_MaxString := 'Only this text should exist...';
    EXPECTED_RESULT_APPEND                      : Tc2_System.T_MaxString := 
        Tc2_Standard.CONCAT(WRITE_TEST_CONTENT_OVERWRITE_EMPTY_FILE, WRITE_TEST_CONTENT_APPEND);
END_VAR

IF TcUnit.TEST_ORDERED('Test06_WriteLine_FileOpened_Append_ExpectNoError_ExpectOldAndNewContent') THEN
    CASE state OF
        0:
            IF _fileHandler.Open(FILE_PATH, NEW_TEXT_FILE_NAME, E_FileOpenMode.TEXT_READ_WRITE_APPEND, NET_ID) THEN
                state := state + 1;
            END_IF
            
        1:
            IF  _fileHandler.WriteLine(WRITE_TEST_CONTENT_APPEND)  THEN
                AssertFalse(_filehandler.Error, 
                    Tc2_Standard.CONCAT('Error writing content: ',_fileHandler.AdsError.Message));
                state := state + 1;
            END_IF
        
        2: 
            // Flush the content so that it appears in the file to be read with .Read()
            IF _fileHandler.Flush()  THEN
                state := state + 1;
            END_IF
            
        3:
            IF _fileHandler.Read(content) THEN
                AssertFalse(_filehandler.Error, 
                    Tc2_Standard.CONCAT('Error writing content: ',_fileHandler.AdsError.Message));
                AssertEquals_STRING(EXPECTED_RESULT_APPEND, content, 
                        'Written content differs.');
                state := state + 1;
            END_IF
            
        4:
            IF _fileHandler.Close() THEN
                TcUnit.TEST_FINISHED();
            END_IF
    END_CASE
END_IF

I am sure there is a way, but it seems to be hidden in the FB_FileClose I think.

I am sure passing the handle from TC3 to CodeSys is not a valid thing to do, correct? I started wondering about this as I was writing.

Upvotes: 1

Views: 98

Answers (2)

ziga
ziga

Reputation: 390

I think that there is no such (known) way to do it. For that reason closing this thread.

Upvotes: 0

Ludo
Ludo

Reputation: 11

I think FB_FileWrite() would do the trick. Beckhoff FB_FileWrite

I use this functionality to log data in a task running at 500uS. Open/Create a file with FB_FileOpen(). Use the same handle for FB_FileWrite() and trigger the execute (it needs an positive flank to start and a false to finish) each time you want to write data to the file.

Example of the cyclic call to the FB's

  // Function - Open file
    s_fbFileOpen(
        sNetId := '',
        sPathName := s_sSubDirPath,
        ePath := PATH_GENERIC,
        bExecute := s_bExecuteOpenFile,
        tTimeout := DEFAULT_ADS_TIMEOUT,
        hFile => s_nFileHandle);

  // Function - Write File
        s_fbFileWrite(
            sNetId := '',
            hFile := s_nFileHandleDest,
            pWriteBuff := ADR(s_arrbReadWriteBuffer),
            cbWriteLen := s_nNrOfBytesToCopy,
            bExecute := s_bExecuteWriteFile,
            tTimeout := DEFAULT_ADS_TIMEOUT);

Example sequencer code:

// File Handling
// ------------------------------------
// Create File
    E_FB_FILEHANDLING_STATE.START_CREATE_OPEN_FILE:
        s_fbFileOpen.nMode := FOPEN_MODEAPPEND OR FOPEN_MODEPLUS OR FOPEN_MODEBINARY;                                           // Configure opening mode
        s_bExecuteOpenFile := TRUE;                                                                                             // Activate creation of file by opening
        IF s_fbFileOpen.bBusy THEN                                                                                              // If creation of file is busy
            s_eSequence := E_FB_FILEHANDLING_STATE.WAIT_CREATE_OPEN_FILE;                                                       // Then transition to the next step
        END_IF

    E_FB_FILEHANDLING_STATE.WAIT_CREATE_OPEN_FILE:
        s_sSubDirPath := CONCAT(iq_stSET.sPathName,iq_stSET.sFileName);
        s_bExecuteOpenFile := FALSE;                                                                                            // Deactivation of creation of file by opening
        IF NOT s_fbFileOpen.bBusy THEN                                                                                          // If creation of file by opening has finished
            IF s_fbFileOpen.bError THEN                                                                                         // If there was an error detected
                s_bStaError := TRUE;
                s_nStaErrorID := s_fbFileOpen.nErrId;
                s_eSequence := E_FB_FILEHANDLING_STATE.IDLE;                                                                    // Then set the error indications and abort creation of file
            ELSE
                s_eSequence := E_FB_FILEHANDLING_STATE.START_CREATE_CLOSE_FILE;                                                 // Else creation of file is succesfull and file can be closed
            END_IF
        END_IF

Keep in mind that the actual accessing of the file takes time depending on your hardware. In our case we write to a SATA harddisk and we had to create a memory buffer for approx. 200ms of data. The access and write time to the file was approx 100ms. In our case we then write the buffer as a large 'chunk' of data at one go into the file. Hope this helps.

Regards

Ludo

Upvotes: 0

Related Questions