Reputation: 390
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
Reputation: 390
I think that there is no such (known) way to do it. For that reason closing this thread.
Upvotes: 0
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