Quick Start

Das folgende Kapitel ermöglicht einen Schnelleinstieg in die Function TwinCAT 3 IoT OCPP. In dieser Anleitung setzen Sie einen OCPP-Client und einen OCPP-Server auf und lassen diese in vereinfachter Form miteinander kommunizieren. Weitergehende Funktionalitäten können aus den weiteren Beispielprojekten entnommen werden.

Quick Start 1:

Beispielhafte Umsetzung

In diesem Quick Start wird aus Vereinfachungsgründen auf Security verzichtet. In realen Applikationen sollte die sichere Implementierung immer ein zentraler Bestandteil der Betrachtungen sein.

Initialisierung OCPP-Client

Bei der Initialisierung kann entweder der Funktionsbaustein direkt genutzt werden oder ein bereits bestehendes TcCOM-Objekt verknüpft werden. In diesem Getting Started wird die Initialisierung über den Funktionsbaustein durchgeführt.

    // Client param
    stClientParam      : ST_OCPP1_ClientParam :=(sHost:='127.0.0.1', nPort:=8080, sIdentity:='TestIdent', sPath:='ocpp', eDebugLevel:=E_OCPP_DebugLevel.MessageLogFile, eTraceLevel:=TcTraceLevel.tlVerbose); 
    fbClient           : FB_OCPP1_Client(stClientParam);
    eAction            : E_OCPP1_Action;
    hMessageId         : T_OCPP_MessageId;

Der OCPP-Client verbindet sich in diesem Fall mit dem ebenfalls auf demselben Gerät unter Port 8080 laufenden OCPP-Server. Zu Demonstrationszwecken werden sowohl das Logfile angelegt als auch das Trace-Level hochgestellt. Dies ist im Regelbetrieb nicht unbedingt notwendig. Einzelheiten zum Logging finden Sie im Kapitel Logging.

Initialisierung OCPP-Server

    // Server param
    stServerParam       : ST_OCPP1_ServerParam :=
                           (nPort:=8080,
                            eAuthMode:=E_OCPP1_AuthenticationMode.None,
                            eEncryptionMode := E_OCPP_EncryptionMode.None,
                            eDebugLevel:=E_OCPP_DebugLevel.MessageLogFile,
                            eTraceLevel:=TcTraceLevel.tlVerbose);
    fbOcppServer        : FB_OCPP1_Server(stServerParam);
    eAction             : E_OCPP1_Action;
    hMessageId          : T_OCPP_MessageId;
    hStationId          : UDINT;

Der vorliegende OCPP-Server läuft ebenfalls auf dem lokalen Gerät, auf Port 8080. Auch hier werden zu Demonstrationszwecken Logfile und Trace-Level eingestellt.

Grundaufbau Beispielprojekt

In diesem Getting Started wird ein TwinCAT-Projekt verwendet in dem sowohl OCPP-Client als auch OCPP-Server in einem separaten SPS-Projekt betrieben werden. Die Aktionen gehen vom OCPP-Client aus. In einem realen Szenario werden selbstverständlich auch Server-seitig Aktionen ausgelöst.

Quick Start 2:

Implementierung OCPP-Client

Das Beispiel umfasst auf Seite des Clients zwei verschiedene Abläufe. Zum einen kann eine einfache StatusNotification abgesendet werden:

// Send StatusNotification
IF bSendStatus THEN
    bSendStatus:=FALSE;
    eError:=E_OCPP1_ChargePointError.HighTemperature;
    eStatus:=E_OCPP1_ChargePointStatus.SuspendedEV;
    eAction:=E_OCPP1_Action.StatusNotification;
END_IF

Über die Parameter eError und eStatus können die mit der StatusNotification versendeten Werte angepasst werden. Zum anderen wird ein abgespeckter Ablauf zum simulierten Laden umgesetzt:

// Charging sample
CASE nState OF
    0: 
        IF bStartCharging THEN
            bStartCharging:=FALSE;
            eAction:=E_OCPP1_Action.Authorize;
            nState:=nState+1;
        END_IF
    1:    
        IF bAuthorized THEN
            bAuthorized:=FALSE;
            nMeterStart:=nMeterStop;
            eAction:=E_OCPP1_Action.StartTransaction;
            nState:=nState+1;
        END_IF
    2:
        IF bChargingStarted THEN
            //10 seconds charging simulation
            timerCharge(IN:=TRUE);
            IF timerCharge.Q THEN
                bChargingStarted:=FALSE;
                timerCharge(IN:=FALSE);
                nMeterStop:=nMeterStart+10;
                eAction:=E_OCPP1_Action.StopTransaction;
                nState:=nState+1;
            END_IF
        END_IF
    3:     
        IF bChargingStopped THEN
            bChargingStopped:=FALSE;
            nState:=0;
        END_IF
END_CASE

Wenn das Charging Sample über bStartCharging gestartet wird, ruft der Client zunächst die Authorize-Methode auf. Im Anschluss an ein erfolgreiches Authorize wird dann die StartTransaction-Methode ausgeführt. Nach erfolgreichem StartTransaction wird dann 10 Sekunden lang „geladen“ und anschließend ein StopTransaction ausgeführt.

Die spezifische OCPP-Kommunikation wird über eine Case-Anweisung umgesetzt. In dieser Case-Anweisung werden die einzelnen OCPP-Kommandos implementiert:

// Different OCPP commands implemented for OCPP client CASE eAction OF
    E_OCPP1_Action.None:
        IF NOT fbClient.bError AND fbClient.bValid THEN 
            fbClient.PollRequest(hMessageId=> hMessageId, eAction=> eAction );
        ELSE 
            // Implement error handling here        
        END_IF        
    E_OCPP1_Action.Authorize:
        IF fbClient.SendAuthorize(sIdTag:=sIdTag, eStatus => stIdTagInfo.eStatus) THEN
            IF stIdTagInfo.eStatus = E_OCPP1_AuthorizationStatus.Accepted THEN
                bAuthorized := TRUE;
                eAction    := E_OCPP1_Action.None;
            END_IF
        END_IF
    E_OCPP1_Action.StartTransaction:
        IF fbClient.SendStartTransaction(sIdTag:=sIdTag, nConnectorId:=nConnectorId, nMeterStart:=nMeterStart, eStatus => stIdTagInfo.eStatus, nTransactionId => nTransactionId) THEN
            IF stIdTagInfo.eStatus = E_OCPP1_AuthorizationStatus.Accepted THEN
                bChargingStarted := TRUE;
                eAction    := E_OCPP1_Action.None;
            END_IF
        END_IF
    E_OCPP1_Action.StopTransaction:
        IF fbClient.SendStopTransaction(nConnectorId:=nConnectorId, nMeterStop:=nMeterStop, eStatus => stIdTagInfo.eStatus) THEN
            bChargingStopped := TRUE;
            eAction    := E_OCPP1_Action.None;
        END_IF
    E_OCPP1_Action.StatusNotification:
        IF fbClient.SendStatusNotification(nConnectorId:=nConnectorId, eError, eStatus) THEN
            eAction    := E_OCPP1_Action.None;
        END_IF
    // Implement here more functionalities like: 
    // E_OCPP1_Action.TriggerMessage:
ELSE
    eAction := E_OCPP1_Action.None;
    // not implemented... 
END_CASE

Hier sind nur die für das Beispiel verwendeten Funktionen implementiert. Weitere Funktionen würden eine Erweiterung der Case-Anweisung bedeuten.

Implementierung OCPP-Server

Auf Seite des OCPP-Servers werden die vom Client gesendeten Befehle empfangen und die passende Antwort geschickt:

CASE eAction OF
    E_OCPP1_Action.None:
        IF NOT fbOcppServer.bError AND fbOcppServer.bValid THEN
            fbOcppServer.PollRequest(eAction => eAction, hMessageId => hMessageId, hStationId => hStationId);
        ELSE
            // Implement error handling here
        END_IF
    E_OCPP1_Action.BootNotification:
        IF fbOcppServer.RecvBootNotification(hStationId, hMessageId=>hMessageId,             
            sModel                => stBootNotification.sModel,
            sVendor                => stBootNotification.sVendor,
            sChargeBoxSerial    => stBootNotification.sChargeBoxSerial,
            sChargePointSerial    => stBootNotification.sChargePointSerial,
            sFirmwareVersion    => stBootNotification.sFirmwareVersion,
            sMeterSerial        => stBootNotification.sMeterSerial,
            sMeterType            => stBootNotification.sMeterType)
        THEN
            fbOcppServer.RespBootNotification(hStationId, hMessageId, E_OCPP1_RegistrationStatus.Accepted);
            eAction := E_OCPP1_Action.None;
        END_IF
    E_OCPP1_Action.Heartbeat:
        IF fbOcppServer.RecvHeartbeat(hStationId) THEN
            fbOcppServer.RespHeartbeat(hStationId:=hStationId,hMessageId:=hMessageId);
            eAction := E_OCPP1_Action.None;
        END_IF
    E_OCPP1_Action.Authorize:
        IF fbOcppServer.RecvAuthorize(hStationId, sIdTag => sIdTag) THEN
            IF sIdTag='Test123' THEN
                fbOcppServer.RespAuthorize(hStationId:=hStationId, hMessageId:=hMessageId, E_OCPP1_AuthorizationStatus.Accepted);
                eAction := E_OCPP1_Action.None;
            END_IF
        END_IF
    E_OCPP1_Action.StartTransaction:
        IF fbOcppServer.RecvStartTransaction(hStationId, hMessageId => hMessageId, sIdTag => sIdTag, nConnectorId => nConnectorId, nMeterStart => nMeterStart, nTimestamp => nTimestamp) THEN
            nTransactionId:=nTransactionId+1;
            fbOcppServer.RespStartTransaction(hStationId, hMessageId, E_OCPP1_AuthorizationStatus.Accepted, nTransactionId:=nTransactionId);
            eAction := E_OCPP1_Action.None;
        END_IF
    E_OCPP1_Action.StopTransaction:
        IF fbOcppServer.RecvStopTransaction(hStationId, hMessageId => hMessageId, sIdTag => sIdTag, nTransactionId => nTransactionIdRecv, nConnectorId => nConnectorId, nMeterStop => nMeterStop, nTimestamp => nTimestamp, eReason => eReason) THEN
            fbOcppServer.RespStopTransaction(hStationId, hMessageId, E_OCPP1_AuthorizationStatus.Accepted);
            eAction := E_OCPP1_Action.None;
        END_IF
    E_OCPP1_Action.StatusNotification:
        IF fbOcppServer.RecvStatusNotification(hStationId, hMessageId => hMessageId, nConnectorId => nConnectorIdStatus, eError => eError, eStatus => eStatus) THEN
            fbOcppServer.RespStatusNotification(hStationId, hMessageId);
            eAction := E_OCPP1_Action.None;
        END_IF
END_CASE

Hier sind im Vergleich zum Client-Sample noch zwei Funktionen mehr implementiert. Der Heartbeat wird vom Client intern abgeschickt und kann über ein Property am Funktionsbaustein konfiguriert werden. Die BootNotification wird automatisch beim Aufstarten des Clients versendet und muss dementsprechend ebenfalls im Server verarbeitet werden.

OCPP-Nachrichten in Server und Client können auch in die andere Richtung implementiert werden. Der Server kann Nachrichten in Richtung des Clients schicken. Andersrum kann dann der Client diese Nachrichten vom Server empfangen.

Das Logfile sieht nach einmaliger Durchführung des Client-Samples (Senden einer StatusNotification und Ausführen des Charging Samples) wie folgt aus:

Quick Start 3:
Quick Start 4: