Message Logger

In diesem Szenario wird der PLC Expert Mode beispielhaft für einen Message Logger in der SPS gezeigt. In dem Beispielprogramm wird mit den Funktionsbausteinen des TwinCAT Database Servers ein Funktionsbaustein erstellt, welcher verschiedene Methoden zum Erzeugen und Auslesen von Meldungen bereitstellt. Die Datenbank, in der die Meldungen abgelegt werden, wird aus der SPS heraus erzeugt. Im MAIN-Programm wird eine beispielhafte Verwendung des erzeugten Bausteins implementiert. Alle 7 Tage wird eine neue Datenbankdatei erzeugt. Drei verschiedene Meldungen können gesendet werden. Zusätzlich besteht die Möglichkeit die letzte Meldung bzw. alle Meldungen aus einem bestimmten Zeitraum abzurufen.

Message Logger 1:

Kategorie

PLC Expert Mode

Verwendete Datenbank

MS Compact

Kompatible Datenbanken

Mit kleineren Anpassungen für alle unterstützten Datenbanktypen anwendbar

Verwendete SPS Funktionsbausteine

FB_PLCDBCreateEvt, FB_PLCDBCmdEvt, FB_PLCDBWriteEvt, FB_PLCDBReadEvt

Verwendete SPS Bibliotheken

Tc3_Database, Tc3_Eventlogger

Download

TF6420_Sample2_ErrorLogger.zip

FB_ErrorLogger

CreateErrorLogDB (Methode)

Die Methode CreateErrorLogDB erzeugt eine MS-Compact-Datenbankdatei und erstellt die Tabelle, in die die Meldungen abgespeichert werden.

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 (Methode)

Mit der Methode AddErrorEntry können verschiedene Meldungen in die Datenbank geschrieben werden.

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 (Methode)

Mit der Methode ReadLastError kann der aktuellste (letzte) Eintrag aus der Datenbank ausgelesen werden.

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 (Methode)

Wenn alle Meldungen eines bestimmten Zeitraums ausgelesen werden sollen, kann dies mit der Methode GetErrorTimerange erfolgen.

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 Methode)

In der privaten Methode _SetResultInfo wird das Nachrichten Interface I_Message vom TwinCAT Eventlogger ausgewertet.

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