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.
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.
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: