先生,我是初级TwinCAT用户,我想将PLC的数据记录为Excel或csv格式?

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

我必须从 Beckhoff PLC 管理的模拟器中记录 JobNo、处理时间、JobID、过程编号等数据,并将这些数据以 CSV 或 excel 文件的形式写入。 例如,如果 JobNo 为 1,则在处理完所有数据后,应将其记录在相应的列下,当遇到下一个作业时,将数据记录在下一行。以下应该是 excel 文件的样子

职位号 处理时间 职位编号 工艺编号
1 20 100 200
2 40 101 320

到目前为止,我已经关注了 Beckhoff 的在线资源,甚至联系了他们的支持团队,但是我从他们那里得到的代码导致了某种错误。我也在一些论坛上写下了我的问题,但我在那里得到的答案太复杂和高级了。如果你能提供一个文件写入程序的例子会很有帮助 以下是我为此目的使用的所有 DUT 和 GVL 以及主要代码: 名为 ST_data

的 DUT
TYPE ST_data :
STRUCT
    JOB_ID          : STRING(255);
    Processing_Time : STRING(255);
    Setup_Time      : STRING(255);
    Due_Date        : STRING(255);
END_STRUCT
END_TYPE

GVL 名为 GVL

{attribute 'qualified_only'}
VAR_GLOBAL
    MACHINE : ARRAY[1..50] OF ST_data;
    sen5 : BOOL;
    sen2 : BOOL;
    state : BOOL;
    count :INT;
    hit: BOOL;
    tdelta : ULINT;
    tstart : ULINT;
    tend : ULINT;
    setuptime : LREAL;
END_VAR

MAIN 程序代码 局部变量--------------------------------------------

PROGRAM MAIN
VAR

    FB_FileOpen: FB_FileOpen;
    FB_FileWrite: FB_FileWrite;
    FB_FileClose: FB_FileClose;
    
    hFile: UINT;
    sPathName: T_MaxString;
    sWriteBuffer : STRING(5000);
    sBufferTemp : STRING(1000); // Temporary string that will hold the string that needs to be added to the full string
    bBuffTemp : BOOL ; //check if the strings have been concatinated
    sFormat : STRING(255);
    
    state: INT;
    bWrite: BOOL;
    NT_GetTime: NT_GetTime;
    
    bFill: BOOL;
    i: INT;
    FB_FormatString2: FB_FormatString2;
    
    trigger: BOOL;
END_VAR

代码-------------------------------------------- ------------------

//counter code
//==========================================================================================
IF GVL.sen5 = FALSE AND GVL.hit = FALSE THEN
    GVL.count := GVL.count +1;
    GVL.hit := TRUE;
ELSIF GVL.sen5 = TRUE AND GVL.hit = TRUE THEN
    GVL.hit := FALSE;
END_IF

IF gvl.sen5= TRUE THEN
    GVL.tstart := F_GetSystemTime();
END_IF
IF GVL.sen2 = TRUE THEN
    GVL.tend := F_GetsystemTime();
    //trigger := TRUE; // this will trigger the file writing commands
END_IF
GVL.tdelta := GVL.tend - GVL.tstart;
GVL.setuptime := GVL.tdelta*EXPT(10,-7);
//Entering some value in array
//==========================================================================================
IF bFill THEN
    FOR i := 1 TO 50 BY 1 DO
        GVL.MACHINE[i].Due_Date :='10823'; //WORD_TO_STRING(NT_GetTime.TIMESTR.wDayOfWeek);
        GVL.MACHINE[i].JOB_ID := INT_TO_STRING(GVL.count) ;//INT_TO_STRING(i);
        GVL.MACHINE[i].Processing_Time := '125';//WORD_TO_STRING(NT_GetTime.TIMESTR.wMinute);
        GVL.MACHINE[i].Setup_Time := LREAL_TO_STRING(GVL.setuptime);//WORD_TO_STRING(NT_GetTime.TIMESTR.wSecond);
    END_FOR
    bFill := FALSE;
END_IF


//Functioin block to get local time
//===========================================
IF NT_GetTime.start AND NOT NT_GetTime.BUSY THEN
    NT_GetTime.START :=FALSE;
ELSE
    NT_GetTime.START := TRUE;
END_IF
NT_GetTime(
    NETID:='' , 
    START:= , 
    TMOUT:= , 
    BUSY=> , 
    ERR=> , 
    ERRID=> , 
    TIMESTR=> );
//CASE STATEMENT to handle writing
//===========================================
CASE state OF
    0:
        IF bWrite THEN // switch to true or false to control the execution
            State :=5;
            bWrite := FALSE;
        END_IF
    5:// creating the file 
        sPathName := CONCAT('C:\Users\Manjot Sanghera\Desktop\CSVRECORD',WORD_TO_STRING(NT_GetTime.TIMESTR.wDay));
        sPathName := CONCAT(sPathName,'.');
        sPathName := CONCAT(sPathName,WORD_TO_STRING(NT_GetTime.TIMESTR.wHour));
        //sPathName := CONCAT(sPathName,'.');
        //sPathName := CONCAT(sPathName,WORD_TO_STRING(NT_GetTime.TIMESTR.wMinute));
        sPathName := CONCAT(sPathName,'_TestFile.csv');
        State := 10;
        FB_FileOpen.bExecute := TRUE;
        
    10:
        FB_FileOpen.bExecute := TRUE;
        IF NOT FB_FileOpen.bBusy AND NOT FB_FileOpen.bError THEN
            FB_FileOpen.bExecute := FALSE;
            State := 15;
        END_IF
    15 :
        sWriteBuffer :='Due_Date, Job_ID, Processin_Time,Setup_Time $n';
        sFormat :='%s, %s, %s, %s $n';
        FOR i := 1 TO 50 BY 1 DO // loop is required so that you can loop through each line/row/arrayelement
            FB_FormatString2(
                pFormatString:=  ADR(sFormat), 
                arg1:= F_STRING(GVL.MACHINE[i].Due_Date), 
                arg2:= F_STRING(GVL.MACHINE[i].JOB_ID), 
                arg3:= F_STRING(GVL.MACHINE[i].Processing_Time), 
                arg4:= F_STRING(GVL.MACHINE[i].Setup_Time),  
                pDstString:= ADR(sBufferTemp) , 
                nDstSize:= SIZEOF(sBufferTemp), 
                bError=> , 
                nErrId=> );
            bBuffTemp := CONCAT2(   pSrcString1 := ADR(sWriteBuffer),//main string 
                                    pSrcString2 := ADR(sBufferTemp), //String to be added
                                    pDstString  := ADR(sWriteBuffer),//destinatioin of the string
                                    nDstSize := SIZEOF(sWriteBuffer));//size of the final string
        END_FOR
        //sWriteBuffer := 'Job_ID__|__Processing_Time__|__SetupTime';
        State := 20;
        FB_FileWrite.bExecute := TRUE;
    20:
        FB_FileWrite.bExecute := TRUE;
        IF NOT FB_FileWrite.bBusy AND NOT FB_FileWrite.bError THEN
            FB_FileWrite.bExecute := FALSE;
            State := 30;
            FB_FileClose.bExecute := TRUE;
        END_IF
    30:
        IF NOT FB_FileClose.bBusy AND NOT FB_FileClose.bError THEN
            FB_FileClose.bExecute := FALSE;
            State:= 0;
        END_IF      
END_CASE

//OPEN, WRITE, CLOSE FILE
//===========================================
FB_FileOpen(
    sNetId:= '', 
    sPathName:= sPathName , 
    nMode:= FOPEN_MODEWRITE OR FOPEN_MODEPLUS, 
    ePath:= PATH_GENERIC, 
    bExecute:= , 
    tTimeout:= , 
    bBusy=> , 
    bError=> , 
    nErrId=> , 
    hFile=> hFile);
    
FB_FileWrite(
    sNetId:= '', 
    hFile:= hFile, 
    pWriteBuff:= ADR(sWriteBuffer) , 
    cbWriteLen:= SIZEOF(sWriteBuffer), 
    bExecute:= , 
    tTimeout:= , 
    bBusy=> , 
    bError=> , 
    nErrId=> , 
    cbWrite=> );
    
FB_FileClose(
    sNetId:= '', 
    hFile:= hFile  , 
    bExecute:= , 
    tTimeout:= , 
    bBusy=> , 
    bError=> , 
    nErrId=> );

程序的预期功能 counter 对对象进行计数,并将 JobID、Process No 和其他值的值填充到表的第一行中,并且仅在第二个作业的这些值可用后才起作用

plc twincat
1个回答
0
投票

根据评论,这是一个程序,其中包含用于写入文件的 FB 和实现演示以将新数据发送到文件的 MAIN。数据以 CSV 格式写入。您可以修改要写入文件的内容,什么文件,数据格式是什么,......我希望这应该足以解决问题。

由于你是新手,这里是文件夹结构的图像,因为我使用了方法。以下是关于 Object Oriented Programming with TwinCAT 3

的更多信息

文件写入功能块实现: 变量声明:

FUNCTION_BLOCK FB_FileWriter
VAR_INPUT
    sFilePath   : T_MaxString;
    sFileName   : T_MaxString;
    sNetId      : STRING;   // NetId of the target, leave empty for local
END_VAR
VAR
    fbFileOpen  : FB_FileOpen;
    fbFilePuts  : FB_FilePuts;
    fbFileClose : FB_FileClose;
    nFileHandle : UINT;
    
    arrBuffer   : ARRAY[0..100] OF T_MaxString;
    eFileWriteState : (IDLE, OPEN_FILE, WRITE_TO_FILE, CLOSE_FILE, ERROR);
END_VAR

身体:

CASE eFileWriteState OF
    IDLE:
        // Make sure all the file writing FBs are ready for use (trigger on execute)
        Init();
        
        // We have pending data to be written
        IF arrBuffer[0] <> '' THEN
            eFileWriteState := OPEN_FILE;
        END_IF
        
    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    := TRUE,
            sNetId      := sNetId,
            sPathName   := CONCAT(sFilePath, sFileName),
            nMode       := FOPEN_MODEAPPEND);
            
        IF fbFileOpen.bError THEN
            eFileWriteState := ERROR;
        ELSIF NOT fbFileOpen.bBusy THEN
            nFileHandle := fbFileOpen.hFile;
            fbFileOpen(bExecute := FALSE);
            eFileWriteState := WRITE_TO_FILE;
        END_IF
    
    WRITE_TO_FILE:
        fbFilePuts(
            bExecute    := TRUE,
            sNetId      := sNetId,
            hFile       := nFileHandle,
            sLine       := arrBuffer[0]);
        IF fbFilePuts.bError THEN
            eFileWriteState := ERROR;
        ELSIF NOT fbFileClose.bBusy THEN
            // Sucess, data was written
            // Rotate the buffer and close the file
            RotateBuffer();
            eFileWriteState := CLOSE_FILE; 
        END_IF
    
    CLOSE_FILE:
        fbFileClose(
            bExecute := TRUE,
            sNetId      := sNetId,
            hFile       := nFileHandle);
            
        IF fbFileClose.bError THEN
            eFileWriteState := ERROR;
        ELSIF NOT fbFileClose.bBusy THEN
            nFileHandle := 0;
            eFileWriteState := IDLE;
        END_IF      
    
    ERROR:
        // Error, clear the handle and go back to idle
        nFileHandle := 0;
        eFileWriteState := IDLE;
    END_CASE

方法:

初始化所有FB的方法:

METHOD PRIVATE Init : BOOL

fbFileClose(bExecute := FALSE);
fbFileOpen(bExecute := FALSE);
fbFilePuts(bExecute := FALSE);

向缓冲区插入新数据的方法

METHOD PRIVATE InsertToBuffer
VAR_INPUT
    value   : STRING;
END_VAR
VAR
    nBufferIndex    : INT;
END_VAR

FOR nBufferIndex := 0 TO 100 BY 1 DO
    IF arrBuffer[nBufferIndex] = '' THEN
        // We found the free spot in the buffer, insert the value to this place
        arrBuffer[nBufferIndex] := value;
        EXIT;
    END_IF
END_FOR

当前数据片写入后轮换缓冲区中数据的方法。

METHOD PRIVATE RotateBuffer

VAR
    nIndex  : int := 0;
END_VAR

FOR nIndex := 0 TO 99 BY 1 DO
    // We can exit when we reached the empty slot in the buffer
    IF arrBuffer[nIndex] = '' THEN
        EXIT;
    END_IF
    // FIFO, removing first element in the array copying the next, etc...
    arrBuffer[nIndex] := arrBuffer[nIndex+1];
END_FOR

被调用以写入目标文件的公共方法

// Write to the destination file
METHOD WriteToFile : BOOL
VAR_INPUT
    message : T_MaxString;
END_VAR

InsertToBuffer(value := message);

结构定义:

TYPE ST_MyData :
STRUCT
    Id          : DINT;
    Value       : DINT;
    Timestamp   : T_MaxString; 
END_STRUCT
END_TYPE

主要变量减速:

PROGRAM MAIN
VAR
    fbWriteFile     : FB_FileWriter;
    
    arrTestData     : ARRAY[0..10] OF ST_MyData;
    nIteration      : INT;
    bStartNewJob    : BOOL;
    fbFormatString  : FB_FormatString;
    
    fbGetCurrentTaskIndex   : GETCURTASKINDEX;
END_VAR

VAR CONSTANT
    cHeader : STRING := 'ID;VALUE;TIMESTAMP';
END_VAR

身体:

fbWriteFile(
    sFileName   := 'Test.txt',
    sFilePath   := 'C:\',
    sNetId      := '');
    
// Handle this differently, but just creating the header on first PLC cycle for demo purpose
fbGetCurrentTaskIndex();
IF _TaskInfo[fbGetCurrentTaskIndex.index].FirstCycle THEN
    fbWriteFile.WriteToFile(message := cHeader);
END_IF
    
IF bStartNewJob THEN
    // Making up some fake data to store it
    arrTestData[nIteration].Id := nIteration;                   // just some made up id
    arrTestData[nIteration].Value := 5*nIteration;              // just some made up value
    arrTestData[nIteration].Timestamp := '2023.27.04_10:00';    // just some fake timestamp
    
    // Create the full line by using the FB_FormatString, way easier than CONCAT
    fbFormatString(
        sFormat := '%d;%d;%s$n',
        arg1    := F_DINT(arrTestData[nIteration].Id),
        arg2    := F_DINT(arrTestData[nIteration].Value),
        arg3    := F_STRING(arrTestData[nIteration].Timestamp));

    // Send the created line to the file writer
    fbWriteFile.WriteToFile(message := fbFormatString.sOut);

    // Increasing the counter for new, different data
    IF nIteration <10 THEN
        nIteration := nIteration + 1;
    ELSE
        nIteration := 0;
    END_IF
    
    // And finally, clear the trigger
    bStartNewJob := FALSE;      
END_IF

最终结果。

第一次循环后的初始状态,创建了标题:

© www.soinside.com 2019 - 2024. All rights reserved.