在 TwinCAT 3 中写入 CSV 会无意中跳过行,并且不确定原因

问题描述 投票:0回答:1

我正在使用 Beckhoff 的 PLC 并在 TwinCat3 中工作。

我一直在尝试实现逻辑,以在测试完成后将实验室测试的试验数据写入 csv。目前,我尝试执行此操作的方法是首先将数据存储在内存中(在变量

aTrialDataBuffer
中,如下所示),然后测试完成后,将该数据以 .csv 格式写入磁盘。

我当前的问题是测试数据完美地存储在内存缓冲区中,没有任何跳过的迭代,但是在事后查看 .csv 时,只有大约每 4-6 行最终被记录。我知道让

FB_FileOpen
FB_FileClose
FB_FilePuts
正常工作有一些微妙之处,并且一直在遵循有关 SO 的一些先前提出的问题的指南:

如何在 TwinCAT 中以 Excel 或 CSV 格式保存 PLC 中的数据?

TwinCAT 3:写入文件

但是,我一生都无法弄清楚为什么会发生这种跳行。我也将程序置于调试模式,并注意到

FB_FilePuts
似乎被调用而没有跳过任何迭代(通过手动检查字符串参数)。我怀疑我仍然错过了如何正确使用
bExecute
标志的一个小技巧,但未能解决该问题。任何帮助将不胜感激。代码在下面。

FUNCTION_BLOCK CalibrationBlockLogger
VAR_INPUT
    sFileName : STRING; 
END_VAR
VAR_OUTPUT
    bBusy : BOOL := FALSE; 
    bError : BOOL := FALSE; 
END_VAR
VAR CONSTANT
    nMaxBufferSize : UDINT := 6000000;
END_VAR
VAR
    cDefaultHeader : T_MaxString := 'TimeLow, TimeHigh, EncoderDegrees, CommandVoltage, CommandTorque, GripForce, GripThreshold, SequenceState, CurrentTestIndex, TestIsLive $n'; 
    sDefaultDirectory : T_MaxString := 'C:\Users\njanne\Documents\DDTestLogs\'; 
    bWriteToDisk : BOOL := FALSE; 
    eFileWriteState : (IDLE, OPEN_FILE, WAIT_FOR_OPEN, WRITE_TO_FILE, WAIT_FOR_WRITE, CLOSE_FILE, WAIT_FOR_CLOSE, ERROR); 
    fbFileOpen : FB_FileOpen; 
    fbFilePuts : FB_FilePuts; 
    fbFileClose : FB_FileClose; 
    fbFormatString : FB_FormatString2; 
    nFileHandle : UINT; 
    bHeaderWriteComplete : BOOL := FALSE; 
    nWriteIndex : UDINT := 0; 
    
    // For formatting data into a string. 
    sDataFormatStr : STRING := '%d, %d, %f, %f, %f, %f, %f, %s, %d, %d $n'; 
    sTempString : STRING(600); 
    
    aTrialDataBuffer : ARRAY[0..nMaxBufferSize] OF ST_CalibTrialStep; 
    nStepsSoFar : UDINT := 0; // Current number of steps in the database. 
    
END_VAR
--------------------------------------
IF bWriteToDisk THEN // externally set to TRUE once the test is complete, and is set FALSE once the file closes in this state machine. 

    // If it's time to write to disk, we need to empty the buffer.
    CASE eFileWriteState OF 
        
        IDLE: 
            // Get everything ready. 
            nFileHandle := 0; 
            bBusy := TRUE; 
            eFileWriteState := OPEN_FILE; 
    
        OPEN_FILE: 
        
            // Opens a file for writing at the end of the file (append). 
            // If the file does not exist, a new file is created. 
            fbFileOpen(bExecute := FALSE); 
            fbFileOpen(
                bExecute        := TRUE, 
                sNetID          := '',
                sPathName       := CONCAT(sDefaultDirectory, sFilename), 
                nMode           := FOPEN_MODEAPPEND OR FOPEN_MODETEXT); 

            eFileWriteState := WAIT_FOR_OPEN; 

        WAIT_FOR_OPEN: 
            fbFileOpen(bExecute := FALSE); 
            IF fbFileOpen.bError THEN
                eFileWriteState := ERROR; 
            ELSIF NOT fbFileOpen.bBusy THEN
                nFileHandle := fbFileOpen.hFile; 
                eFileWriteState := WRITE_TO_FILE; 
            END_IF

        WRITE_TO_FILE: 
            // First check if we need to write the header. 
            IF NOT bHeaderWriteComplete THEN
                fbFilePuts(bExecute := FALSE); 
                fbFilePuts(
                    bExecute        := TRUE, 
                    sNetId          := '', 
                    hFile           := nFileHandle, 
                    sLine           := cDefaultHeader); 
                    
                eFileWriteState := WAIT_FOR_WRITE; 
                bHeaderWriteComplete := TRUE; 
            ELSE
                // If the header is already written, then we're going for data. 
                // We need to first format the string.      
                fbFormatString(
                    pFormatString := ADR(sDataFormatStr), 
                    arg1 := F_UDINT(aTrialDataBuffer[nWriteIndex].nTimestepLow), 
                    arg2 := F_UDINT(aTrialDataBuffer[nWriteIndex].nTimeStepHigh), 
                    arg3 := F_REAL(aTrialDataBuffer[nWriteIndex].rEncoderDegrees), 
                    arg4 := F_REAL(aTrialDataBuffer[nWriteIndex].rCommandVoltage), 
                    arg5 := F_REAL(aTrialDataBuffer[nWriteIndex].rCommandTorque), 
                    arg6 := F_REAL(aTrialDataBuffer[nWriteIndex].rGripForce),  
                    arg7 := F_REAL(aTrialDataBuffer[nWriteIndex].rCurrentGripThreshold), 
                    arg8 := F_STRING(aTrialDataBuffer[nWriteIndex].sSequenceStateString),
                    arg9 := F_INT(aTrialDataBuffer[nWriteIndex].nCurrentTestIndex), 
                    //arg10 := F_INT(aTrialDataBuffer[nWriteIndex].nTestIsLive), 
                    arg10 := F_UDINT(nWriteIndex), // TODO: DELETE
                    pDstString := ADR(sTempString), 
                    nDstSize := SIZEOF(sTempString)); 
                
                fbFilePuts(bExecute := FALSE); 
                fbFilePuts(
                    bExecute        := TRUE, 
                    sNetId          := '', 
                    hFile           := nFileHandle, 
                    sLine           := sTempString); 
                    
                // Update the next iteration if we need to write more. 
                nWriteIndex := nWriteIndex + 1; 
                eFileWriteState := WAIT_FOR_WRITE; 
            END_IF
            
        WAIT_FOR_WRITE: 
            fbFilePuts(bExecute := FALSE); 
            IF fbFilePuts.bError THEN
                eFileWriteState := ERROR; 
            ELSIF NOT fbFileClose.bBusy THEN
                // Success, data was written. 
                // Check if we need to write anything more. 
                IF nWriteIndex = nStepsSoFar THEN
                    eFileWriteState := CLOSE_FILE; 
                ELSE
                    eFileWriteState := WRITE_TO_FILE; // Go back to writer. 
                END_IF
            END_IF
            
        CLOSE_FILE: 
            fbFileClose(bExecute := FALSE); 
            fbFileClose(
                bExecute    := TRUE, 
                sNetId      := '', 
                hFile       := nFileHandle); 
                
            eFileWriteState := WAIT_FOR_CLOSE;
            
        WAIT_FOR_CLOSE: 
            fbFileClose(bExecute := FALSE); 
            IF fbFileClose.bError THEN 
                eFileWriteState := ERROR; 
                
            ELSIF NOT fbFileClose.bBusy THEN
                nFileHandle := 0; 
                eFileWriteState := IDLE; 
                
                // Also reset everything so that a new test can overwrite.  
                nStepsSoFar := 0; 
                nWriteIndex := 0; 
                bHeaderWriteComplete := FALSE; 
                bBusy := FALSE; 
                bWriteToDisk := FALSE; 
                
            END_IF
                
        ERROR: 
            // Error, clear the handle and go back to idle. 
            nFileHandle := 0; 
            eFileWriteState := IDLE; 
            bError := TRUE; 
    
    END_CASE

END_IF

FWIW,新数据被添加到

aTrialDataBuffer
中,并且
nStepsSoFar
通过在测试期间调用的一个小方法来递增:

METHOD addStep : BOOL
VAR_INPUT
    stNewStep : ST_CalibTrialStep; // The incoming step to be added to memory
END_VAR
-------------------------
// We need to simply copy over all the fields into the new position. 
// And then, update the counter. 
MEMCPY(
    destAddr := ADR(aTrialDataBuffer[nStepsSoFar]), 
    srcAddr := ADR(stNewStep), 
    n := SIZEOF(ST_CalibTrialStep)); 
    
nStepsSoFar := nStepsSoFar + 1; 

我也在父程序中调用连续写入的功能块,以满足状态机的更新。

plc twincat codesys
1个回答
0
投票

您需要监视

bBusy
fbFilePuts
位以了解写入何时完成以及可以继续。我想也许你在
fbFileClose.bBusy
中错误地使用了
WAIT_FOR_WRITE

更正代码:

    WAIT_FOR_WRITE: 
        fbFilePuts(bExecute := FALSE); 
        IF fbFilePuts.bError THEN
            eFileWriteState := ERROR; 
        ELSIF NOT fbFilePuts.bBusy THEN
            //    ^^^^^^^^^^^^^^^^
            // Success, data was written. 
            // Check if we need to write anything more. 
            IF nWriteIndex = nStepsSoFar THEN
                eFileWriteState := CLOSE_FILE; 
            ELSE
                eFileWriteState := WRITE_TO_FILE; // Go back to writer. 
            END_IF
        END_IF 
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.