Message logger

This scenario example illustrates the PLC Expert Mode for a Message Logger in the PLC. In the sample program the function blocks of the TwinCAT Database Server are used to create a function block, which provides various methods for generating and reading messages. The database in which the messages are stored is created from the PLC. A sample application of the created function block is implemented in the MAIN program. A new database file is created every 7 days. Three different messages can be sent. In addition it is possible to call up the last message or all messages from a particular interval.

Message logger 1:

Category

PLC Expert Mode

Database used

MS Compact

Compatible databases

Can be used with minor amendments for all supported database types

PLC function blocks used

FB_PLCDBCreateEvt, FB_PLCDBCmdEvt, FB_PLCDBWriteEvt, FB_PLCDBReadEvt

PLC libraries used

Tc3_Database, Tc3_Eventlogger

Download

TF6420_Sample2_ErrorLogger.zip

FB_ErrorLogger

CreateErrorLogDB (method)

The CreateErrorLogDB method creates an MS Compact database file and the table in which the messages are stored.

METHOD CreateErrorLogDB : BOOL
VAR_INPUT
    sDBName : T_MaxString;
END_VAR
CASE nState_CreateDB OF
    0:
        stDBConfig.sServer := CONCAT(CONCAT(sDBPath, sDBName), '.sdf');
        stDBConfig.bAuthentification := FALSE;
                
        nState_CreateDB := 1;
    1:
        IF fbPLCDBCreate.Database(pDatabaseConfig:= ADR(stDBConfig), 
            cbDatabaseConfig:= SIZEOF(stDBConfig), 
            bCreateXMLConfig:= FALSE, pDBID:= 0) THEN
            ipResultEvt := fbPLCDBCreate.ipTcResult;
            IF fbPLCDBCreate.bError THEN
                nState_CreateDB := 100;
            ELSE
                nState_CreateDB := 2;
            END_IF            
        END_IF
    2:
        IF fbConfigTcDBSrv.Create(pTcDBSrvConfig:= ADR(stDBConfig), 
            cbTcDBSrvConfig:= SIZEOF(stDBConfig), bTemporary:= TRUE,
             pConfigID:= ADR(nDBID) ) THEN
            ipResultEvt := fbConfigTcDBSrv.ipTcResult;
            IF fbConfigTcDBSrv.bError THEN
                nState_CreateDB := 100;
            ELSE
                nState_CreateDB := 3;
            END_IF
        END_IF
    3:
        arrTableColumns[0].sName := 'ID';
        arrTableColumns[0].eType := E_ColumnType.BigInt;
        arrTableColumns[0].nLength := 8;
        arrTableColumns[0].sProperty := 'IDENTITY(1,1)';
    
        arrTableColumns[1].sName := 'Timestamp';
        arrTableColumns[1].eType := E_ColumnType.DateTime;
        arrTableColumns[1].nLength := 4;
        
        arrTableColumns[2].sName := 'Severity';
        arrTableColumns[2].eType := E_ColumnType.NVarChar;
        arrTableColumns[2].nLength := 10;
        
        arrTableColumns[3].sName := 'ErrorCode';
        arrTableColumns[3].eType := E_ColumnType.Integer;
        arrTableColumns[3].nLength := 4;
        
        arrTableColumns[4].sName := 'Message';
        arrTableColumns[4].eType := E_ColumnType.NVarChar;
        arrTableColumns[4].nLength := 255;
                
        IF fbPLCDBCreate.Table(hDBID:= nDBID, sTableName:= sTableName, 
            pTableCfg:= ADR(arrTableColumns),
            cbTableCfg:= SIZEOF(arrTableColumns)) THEN
            ipResultEvt := fbPLCDBCreate.ipTcResultEvent;
            nState_CreateDB := 100;            
        END_IF
    100:
        IF _SetResultInfo(1033) THEN
            IF NOT bError THEN
                _bHasCreated := TRUE;
            END_IF

            nState_CreateDB := 0;
        END_IF
END_CASE

CreateErrorLogDB := nState_CreateDB = 0;

AddErrorEntry (method)

The AddErrorEntry method can be used to write different messages into the database.

METHOD AddErrorEntry : BOOL
VAR_INPUT
    tTimestamp : DT;
    eSeverity : E_Severity;
    nErrCode : UDINT;
    sMessage : T_MaxString;
END_VAR
CASE nState_AddEntry OF
    0:        
        ipResultEvt := fbPLCDBWrite.ipTcResult;

        stError.tTimestamp := tTimestamp;
        CASE eSeverity OF
            TcEventSeverity.Info:
                stError.sSeverity := 'Info';
            TcEventSeverity.Warning:
                stError.sSeverity := 'Warning';
            TcEventSeverity.Verbose:
                stError.sSeverity := 'Verbose';
            TcEventSeverity.Critical:
                stError.sSeverity := 'Critical';
            TcEventSeverity.Error:
                stError.sSeverity := 'Error';    
        END_CASE
         stError.nErrCode := nErrCode;
         stError.sMsg := sMessage;
        
        arrColumns[0] := 'Timestamp';
        arrColumns[1] := 'ErrorCode';
        arrColumns[2] := 'Severity';        
        arrColumns[3] := 'Message';
 
         nState_AddEntry := 1;
    1:
        IF fbPLCDBWrite.WriteStruct(
            hDBID:= nDBID, 
            sTableName:= sTableName, 
            pRecord:= ADR(stError), 
            cbRecord:= SIZEOF(stError), 
            pColumnNames:= ADR(arrColumns), 
            cbColumnNames:= SIZEOF(arrColumns)) THEN
            
            nState_AddEntry := 100;
        END_IF
    100:
        IF _SetResultInfo(1033) THEN
            nState_AddEntry := 0;
        END_IF
END_CASE

AddErrorEntry := nState_AddEntry = 0;

ReadLastError (method)

The method ReadLastError can be used to read the latest (last) entry from the database.

METHOD ReadLastError : BOOL
VAR_OUTPUT
    tTimestamp : DT;
    sSeverity : STRING(10);
    nErrCode : UDINT;
    sMessage : T_MaxString;
END_VAR
CASE nState_ReadLastEntry OF
    0:                
        ipResultEvt := fbPLCDBRead.ipTcResult;
    
        arrColumns[0] := 'Timestamp';
        arrColumns[1] := 'ErrorCode';
        arrColumns[2] := 'Severity';        
        arrColumns[3] := 'Message';

         nState_ReadLastEntry := 1;
    1:
        IF fbPLCDBRead.ReadStruct(
            hDBID:= nDBID, 
            sTableName:= sTableName, 
            pColumnNames:= ADR(arrColumns), 
            cbColumnNames:= SIZEOF(arrColumns), 
            sOrderByColumn:= 'ID', 
            eOrderType:= E_OrderType.DESC, 
            nStartIndex:= 0, 
            nRecordCount:= 1, 
            pData:= ADR(stReadData), 
            cbData:= SIZEOF(stReadData)) THEN
                        
            nState_ReadLastEntry := 100;
        END_IF
    100:
        IF _SetResultInfo(1033) THEN
            IF NOT fbPLCDBRead.bError THEN
                tTimestamp := stReadData.tTimestamp;
                sSeverity := stReadData.sSeverity;
                nErrCode := stReadData.nErrCode;
                sMessage := stReadData.sMsg;
            END_IF
        
            nState_ReadLastEntry := 0;        
        END_IF
END_CASE

ReadLastError := nState_ReadLastEntry = 0;

GetErrorTimerange (method)

The method GetErrorTimerange can be used to read all messages from a particular interval.

METHOD GetErrorTimerange : BOOL
VAR_INPUT
    tStartTimestamp : DT;
    tEndTimestamp : DT;
    nStartIndex : UDINT;
END_VAR
VAR_OUTPUT    
    nErrorCount: UDINT;
    arrErrors : ARRAY [0..10] OF ST_ErrorEntry;
END_VAR
CASE nState_ErrorTimerange OF
    0:                
        ipResultEvt := fbPLCDBRead.ipTcResult;
    
        stSearchData.dtStartTimestamp := tStartTimestamp;
        stSearchData.dtEndTimestamp := tEndTimestamp;
        
        sCmd := 'SELECT Timestamp, ErrorCode, Severity, Message FROM
         tbl_Errors WHERE Timestamp >= {start} AND Timestamp <= {end}';

        arrParameter[0].sParaName := 'start';
        arrParameter[0].eParaType := E_ExpParameterType.DateTime;
        arrParameter[0].nParaSize := 4;
        
        arrParameter[1].sParaName := 'end';
        arrParameter[1].eParaType := E_ExpParameterType.DateTime;
        arrParameter[1].nParaSize := 4;
        
        nState_ErrorTimerange := 1;
    1:
        IF fbPLCDBCmd.ExecuteDataReturn(
            hDBID:= nDBID, 
            pExpression:= ADR(sCmd), 
            cbExpression:= SIZEOF(sCmd), 
            pData:= ADR(stSearchData), 
            cbData:= SIZEOF(stSearchData), 
            pParameter:= ADR(arrParameter), 
            cbParameter:= SIZEOF(arrParameter), 
            nStartIndex:= nStartIndex, 
            nRecordCount:= 10, 
            pReturnData:= ADR(arrErrs), 
            cbReturnData:= SIZEOF(arrErrs), 
            pRecords:= ADR(nErrCount)) THEN
            
            nState_ErrorTimerange := 100;
        END_IF
    100:
        IF _SetResultInfo(1033) THEN
            nErrorCount := nErrCount;
            arrErrors := arrErrs;
        
            nState_ErrorTimerange := 0;        
        END_IF
END_CASE

GetErrorTimerange := nState_ErrorTimerange = 0;

_SetResultInfo (private method)

The I_Message message interface is evaluated by the TwinCAT EventLogger in the private _SetResultInfo method.

METHOD _SetResultInfo : BOOL
VAR_INPUT
    nLangId : INT := 1033;
END_VAR
_SetResultInfo := FALSE;

CASE nState_SetResInfo OF
    0:
        IF ipResultEvt.RequestEventText(nLangId, EventText, SIZEOF(EventText)) THEN
            nState_SetResInfo := 1;
        END_IF
    1:
        IF ipResultEvt.RequestEventClassName(nLangId, EventClassName, SIZEOF(EventClassName)) THEN
            EventSourcePath := ipResultEvt.ipSourceInfo.sName;
            EventId := ipResultEvt.nEventId;

            bError := (ipResultEvt.eSeverity = TcEventSeverity.Error) OR
                     (ipResultEvt.eSeverity = TcEventSeverity.Critical);

            nState_SetResInfo:=0;
            _SetResultInfo := TRUE;
        END_IF
END_CASE