Fast logging with data buffer
In order to log data in a database at millisecond intervals, the data must first be consolidated before it is transferred to the database via the TwinCAT Database Server. These data buffers can vary in size according to requirements. In the sample, 100 data samples are combined in a buffer before they are transferred with the TwinCAT Database Server. To avoid gaps during the write process, several buffers must be created in which the data samples are combined. In the sample, a total of 20 buffers are created using a 2-dimensional array.
Data sample
Definition:
TYPE ST_Data :
STRUCT
Timestamp : LINT;
fAM : LREAL;
fPeak : LREAL;
fPulse : LREAL;
fSawtooth : LREAL;
fSine : LREAL;
fSquare : LREAL;
fStairs : LREAL;
fTriangular : LREAL;
END_STRUCT
END_TYPE
Each cycle fills one element of the data buffer. In the sample this happens at 10 ms intervals. Thus a buffer contains data of a period of 1 s. If a buffer is filled with 100 elements, a further array indicates that the 100 elements can now be transferred with the function block FB_PLCDBCmdEvt. To this end, the entire buffer can be transferred to the function block. Each individual element is then transferred from the TwinCAT Database Server to the database. This sample can also be implemented with other function blocks. Note that not all function blocks support arrays.
Extract from the function block FB_Record_tbl_Signals
( "State Machine" => State: Recording)
…
2://Recording
bRecording := TRUE;
//Fill buffer
stData[nWriteBufferIndex, nWriteIndex].Timestamp := nTimestamp;
stData[nWriteBufferIndex, nWriteIndex].fAM := fAM;
stData[nWriteBufferIndex, nWriteIndex].fPeak := fPeak;
stData[nWriteBufferIndex, nWriteIndex].fPulse := fPulse;
stData[nWriteBufferIndex, nWriteIndex].fSawtooth := fSawtooth;
stData[nWriteBufferIndex, nWriteIndex].fSine := fSine;
stData[nWriteBufferIndex, nWriteIndex].fSquare := fSquare;
stData[nWriteBufferIndex, nWriteIndex].fStairs := fStairs;
stData[nWriteBufferIndex, nWriteIndex].fTriangular := fTriangular;
//Set buffer index
nWriteIndex := nWriteIndex + 1;
IF nWriteIndex = 100 THEN
nWriteIndex := 0;
aWriteSQL[nWriteBufferIndex]:= TRUE;
nWriteBufferIndex := nWriteBufferIndex + 1;
IF nWriteBufferIndex = 20 THEN
nWriteBufferIndex := 0;
END_IF
IF aWriteSQL[nWriteBufferIndex] THEN
nState := 255;
RETURN;
END_IF
END_IF
//Write buffer element (100 samples) to database
IF aWriteSQL[nSQLIndex] THEN
IF fbPLCDBCmd.Execute(nDBID, ADR(sCmd), SIZEOF(sCmd),
ADR(stData[nSQLIndex,0]), SIZEOF(stData[nSQLIndex,0]) * 100,
ADR(aPara), SIZEOF(aPara)) THEN
IF fbPLCDBCmd.bError THEN
nState := 255;
ELSE
nRecords := nRecords + 100;
aWriteSQL[nSQLIndex] := FALSE;
nSQLIndex := nSQLIndex + 1;
IF nSQLIndex = 20 THEN
nSQLIndex := 0;
END_IF
IF NOT bRecord THEN
bRecording := FALSE;
nState := 0;
END_IF
END_IF
END_IF
END_IF
….
Appendix:
In this best practice example, a function generator block is used to generate various signals that can be logged in a database. The syntax of the INSERT command is generally valid, but has been specifically tested with an MS SQL database. The ZIP file attached below contains the complete program code in Tnzip format.
Download: TF6420_BestPractise_Buffer.zip