From af21a23e2f40b8d10c0b5f94328e607c022159a1 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 30 Mar 2021 11:10:41 +0200 Subject: [PATCH 001/696] support compiler flags --- src/Variants.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Variants.h b/src/Variants.h index 4951a823..dd91cc14 100644 --- a/src/Variants.h +++ b/src/Variants.h @@ -5,12 +5,16 @@ #ifndef VARIANTS_H #define VARIANTS_H - +#ifndef DEBUG_OUT #define DEBUG_OUT false //Print debug messages on Serial. Gives more insights about inner processes +#endif + +#ifndef TRAFFIC_OUT #define TRAFFIC_OUT true //Print OCPP communication on Serial. Gives insights about communication with the Central System -//#define PROD -#define USE_FACADE true +#endif -//#define OCPP_SERVER //comment out if this should be compiled as server <--- needs to be implemented again +#ifndef USE_FACADE +#define USE_FACADE true +#endif #endif From af7e441d228e34ee6cc67cb2611650fa98894660 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 30 Mar 2021 15:52:50 +0200 Subject: [PATCH 002/696] decouple from concrete WS class --- src/ArduinoOcpp.cpp | 8 ++++++-- src/ArduinoOcpp/Core/OcppEngine.cpp | 10 ++++------ src/ArduinoOcpp/Core/OcppEngine.h | 4 ++-- src/ArduinoOcpp/Core/OcppSocket.cpp | 8 ++++++++ src/ArduinoOcpp/Core/OcppSocket.h | 6 ++++++ 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 1ef50a86..75b9a135 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -9,6 +9,7 @@ #if USE_FACADE #include +#include #include #include #include @@ -25,6 +26,7 @@ namespace ArduinoOcpp { namespace Facade { WebSocketsClient webSocket; +OcppSocket *ocppSocket; MeteringService *meteringService; PowerSampler powerSampler; @@ -118,7 +120,9 @@ void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url) { // consider connection disconnected if pong is not received 2 times webSocket.enableHeartbeat(15000, 3000, 2); //comment this one out to for specific OCPP servers - ocppEngine_initialize(&webSocket, 2048); //default JSON document size = 2048 + ocppSocket = new EspWiFi::OcppClientSocket(&webSocket); + + ocppEngine_initialize(ocppSocket); smartChargingService = new SmartChargingService(16.0f, OCPP_NUMCONNECTORS); //default charging limit: 16A chargePointStatusService = new ChargePointStatusService(&webSocket, OCPP_NUMCONNECTORS); //Constructor adds instance to ocppEngine in constructor @@ -134,7 +138,7 @@ void OCPP_loop() { return; } - webSocket.loop(); //mandatory + //webSocket.loop(); //moved to Core/OcppSocket ocppEngine_loop(); //mandatory if (onLimitChange != NULL) { diff --git a/src/ArduinoOcpp/Core/OcppEngine.cpp b/src/ArduinoOcpp/Core/OcppEngine.cpp index 040ea063..a42c0ae8 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.cpp +++ b/src/ArduinoOcpp/Core/OcppEngine.cpp @@ -28,6 +28,7 @@ int JSON_DOC_SIZE; //size_t removePayload(const char *src, size_t src_size, char *dst, size_t dst_size); +OcppSocket *ocppSock; OcppConnection *mConnection; } //end namespace OcppEngine @@ -35,12 +36,8 @@ OcppConnection *mConnection; using namespace ArduinoOcpp::OcppEngine; using namespace ArduinoOcpp::EspWiFi; -void ocppEngine_initialize(WebSocketsClient *ws, int DEFAULT_JSON_DOC_SIZE){ - wsocket = ws; - JSON_DOC_SIZE = DEFAULT_JSON_DOC_SIZE; //TODO Find another approach where the Doc size is determined dynamically - - OcppClientSocket *ocppSock = new OcppClientSocket(ws); - +void ocppEngine_initialize(OcppSocket *ocppSocket){ + ocppSock = ocppSocket; mConnection = new OcppConnection(ocppSock); } @@ -49,6 +46,7 @@ void initiateOcppOperation(OcppOperation *o) { } void ocppEngine_loop() { + ocppSock->loop(); mConnection->loop(); } diff --git a/src/ArduinoOcpp/Core/OcppEngine.h b/src/ArduinoOcpp/Core/OcppEngine.h index 5d5f3531..6eab0648 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.h +++ b/src/ArduinoOcpp/Core/OcppEngine.h @@ -6,16 +6,16 @@ #define OCPPENGINE_H #include -#include #include +#include #include #include #include namespace ArduinoOcpp { -void ocppEngine_initialize(WebSocketsClient *webSocket, int DEFAULT_JSON_DOC_SIZE); +void ocppEngine_initialize(OcppSocket *ocppSocket); #if 0 diff --git a/src/ArduinoOcpp/Core/OcppSocket.cpp b/src/ArduinoOcpp/Core/OcppSocket.cpp index c0090a34..41c74371 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.cpp +++ b/src/ArduinoOcpp/Core/OcppSocket.cpp @@ -16,6 +16,10 @@ OcppClientSocket::OcppClientSocket(WebSocketsClient *wsock) : wsock(wsock) { } +void OcppClientSocket::loop() { + wsock->loop(); +} + bool OcppClientSocket::sendTXT(String &out) { return wsock->sendTXT(out); } @@ -63,6 +67,10 @@ OcppServerSocket::~OcppServerSocket() { OcppServer::getInstance()->removeReceiveTXTcallback(this->ip_addr); } +void OcppServerSocket::loop() { + //nothing here. The client must call the EspWiFi server loop function +} + bool OcppServerSocket::sendTXT(String &out) { return OcppServer::getInstance()->sendTXT(ip_addr, out); } diff --git a/src/ArduinoOcpp/Core/OcppSocket.h b/src/ArduinoOcpp/Core/OcppSocket.h index 31d7f1b5..c3d5f182 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.h +++ b/src/ArduinoOcpp/Core/OcppSocket.h @@ -17,6 +17,8 @@ class OcppSocket { OcppSocket(); virtual ~OcppSocket() = default; + virtual void loop() = 0; + virtual bool sendTXT(String &out) = 0; virtual void setReceiveTXTcallback(ReceiveTXTcallback &receiveTXT) = 0; //ReceiveTXTcallback is defined in OcppServer.h @@ -32,6 +34,8 @@ class OcppClientSocket : public OcppSocket { //OcppClientSocket(ReceiveTXTcallback &receiveTXT, std::shared_ptr wsock); OcppClientSocket(WebSocketsClient *wsock); + void loop(); + bool sendTXT(String &out); void setReceiveTXTcallback(ReceiveTXTcallback &receiveTXT); @@ -44,6 +48,8 @@ class OcppServerSocket : public OcppSocket { OcppServerSocket(IPAddress &ip_addr); ~OcppServerSocket(); + void loop(); + bool sendTXT(String &out); void setReceiveTXTcallback(ReceiveTXTcallback &receiveTXT); From 85f7d03b9ff64a422d975e0ecb7c4b7f7264f6fe Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 30 Mar 2021 16:57:25 +0200 Subject: [PATCH 003/696] support receiving custom msgs --- .../SimpleOcppOperationFactory.cpp | 41 +++++++++++++++++-- src/ArduinoOcpp/SimpleOcppOperationFactory.h | 4 ++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index af06639c..919b73e3 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -21,7 +21,7 @@ #include #include #include -#if !USE_FACADE +#if 0 #include "UpdateFirmware.h" #include "FirmwareStatusNotification.h" #endif @@ -107,6 +107,38 @@ void setOnUpdateFirmwareReceiveRequestListener(OnReceiveReqListener listener) { onUpdateFirmwareReceiveReq = listener; } +struct CustomOcppMessageCreatorEntry { + const char *messageType; + OcppMessageCreator creator; + OnReceiveReqListener onReceiveReq; +}; + +std::vector customMessagesRegistry; +void registerCustomOcppMessage(const char *messageType, OcppMessageCreator ocppMessageCreator, OnReceiveReqListener onReceiveReq) { + customMessagesRegistry.erase(std::remove_if(customMessagesRegistry.begin(), + customMessagesRegistry.end(), + [messageType](CustomOcppMessageCreatorEntry &el) { + return !strcmp(messageType, el.messageType); + }), + customMessagesRegistry.end()); + + CustomOcppMessageCreatorEntry entry; + entry.messageType = messageType; + entry.creator = ocppMessageCreator; + entry.onReceiveReq = onReceiveReq; + + customMessagesRegistry.push_back(entry); +} + +CustomOcppMessageCreatorEntry *makeCustomOcppMessage(const char *messageType) { + for (auto it = customMessagesRegistry.begin(); it != customMessagesRegistry.end(); ++it) { + if (!strcmp(it->messageType, messageType)) { + return &(*it); + } + } + return NULL; +} + OcppOperation* makeFromTriggerMessage(JsonObject payload) { //int connectorID = payload["connectorId"]; <-- not used in this implementation @@ -130,7 +162,10 @@ OcppOperation *makeOcppOperation(const char *messageType) { OcppOperation *operation = makeOcppOperation(); OcppMessage *msg = NULL; - if (!strcmp(messageType, "Authorize")) { + if (CustomOcppMessageCreatorEntry *entry = makeCustomOcppMessage(messageType)) { + msg = entry->creator(); + operation->setOnReceiveReqListener(entry->onReceiveReq); + } else if (!strcmp(messageType, "Authorize")) { msg = new Ocpp16::Authorize(); operation->setOnReceiveReqListener(onAuthorizeRequest); } else if (!strcmp(messageType, "BootNotification")) { @@ -177,7 +212,7 @@ OcppOperation *makeOcppOperation(const char *messageType) { if (onResetSendConf == NULL) Serial.print(F("[SimpleOcppOperationFactory] Warning: Reset is without effect when the sendConf listener is not set. Set a listener which resets your device.\n")); operation->setOnSendConfListener(onResetSendConf); -#if !USE_FACADE +#if 0 } else if (!strcmp(messageType, "UpdateFirmware")) { msg = new UpdateFirmware(); if (onUpdateFirmwareReceiveReq == NULL) diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.h b/src/ArduinoOcpp/SimpleOcppOperationFactory.h index 633041ea..32064a8d 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.h +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.h @@ -11,6 +11,8 @@ namespace ArduinoOcpp { +typedef std::function OcppMessageCreator; + OcppOperation* makeFromTriggerMessage(JsonObject payload); OcppOperation* makeFromJson(JsonDocument *request); @@ -21,6 +23,8 @@ OcppOperation* makeOcppOperation(OcppMessage *msg); OcppOperation *makeOcppOperation(const char *actionCode); +void registerCustomOcppMessage(const char *messageType, OcppMessageCreator ocppMessageCreator, OnReceiveReqListener onReceiveReq = NULL); + void setOnAuthorizeRequestListener(OnReceiveReqListener onReceiveReq); void setOnBootNotificationRequestListener(OnReceiveReqListener onReceiveReq); void setOnTargetValuesRequestListener(OnReceiveReqListener onReceiveReq); From 0ce1e4e8886a40c92da20b290195f2da02d49a26 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 15 Apr 2021 08:32:46 +0200 Subject: [PATCH 004/696] temorary deactivate_flash switch --- src/ArduinoOcpp/Core/Configuration.cpp | 4 ++++ src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index 866bd667..2d31651f 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -749,6 +749,7 @@ std::shared_ptr>> getAllConfig } bool configuration_init() { +#ifndef AO_DEACTIVATE_FLASH SPIFFSConfig cfg; cfg.setAutoFormat(true); SPIFFS.setConfig(cfg); @@ -884,10 +885,12 @@ bool configuration_init() { file.close(); Serial.println(F("[Configuration] Initialization successful\n")); +#endif //ndef AO_DEACTIVATE_FLASH return true; } bool configuration_save() { +#ifndef AO_DEACTIVATE_FLASH SPIFFS.remove(CONFIGURATION_FN); @@ -942,6 +945,7 @@ bool configuration_save() { file.close(); Serial.print(F("[Configuration] Saving config successful\n")); +#endif //ndef AO_DEACTIVATE_FLASH return true; } diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index ae65081c..4407230f 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -261,6 +261,7 @@ ChargingProfile *SmartChargingService::updateProfileStack(JsonObject *json){ } bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile *chargingProfile) { +#ifndef AO_DEACTIVATE_FLASH String profileFN = PROFILE_FN_PREFIX; @@ -311,11 +312,12 @@ bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile // END DEBUG } +#endif //ndef AO_DEACTIVATE_FLASH return true; } bool SmartChargingService::loadProfiles() { - +#ifndef AO_DEACTIVATE_FLASH const int N_PURPOSES = 3; ChargingProfilePurposeType purposes[N_PURPOSES] = {ChargingProfilePurposeType::ChargePointMaxProfile, ChargingProfilePurposeType::TxDefaultProfile, ChargingProfilePurposeType::TxProfile}; @@ -425,5 +427,6 @@ bool SmartChargingService::loadProfiles() { } } +#endif //ndef AO_DEACTIVATE_FLASH return true; } From 9bd449af7ea4bb043ab264ad706b4edca746f6ba Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 15 Apr 2021 09:28:28 +0200 Subject: [PATCH 005/696] removed unnecessary specifier --- src/ArduinoOcpp/Core/Configuration.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index 2d31651f..290897f3 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -159,7 +159,7 @@ Configuration::Configuration(JsonObject storedKeyValuePair) : AbstractConfigu } template -T Configuration::operator=(T newVal) noexcept { +T Configuration::operator=(T newVal) { if (hasWritePermission()) { if (value != newVal) { From dd98de04c800bd48c87bfc13da1f7e20190586f6 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 15 Apr 2021 09:33:08 +0200 Subject: [PATCH 006/696] fixed include and zero initialization --- src/ArduinoOcpp/Core/OcppOperationTimeout.h | 1 + src/ArduinoOcpp/Core/OcppServer.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/Core/OcppOperationTimeout.h b/src/ArduinoOcpp/Core/OcppOperationTimeout.h index 64e48845..8a0d5493 100644 --- a/src/ArduinoOcpp/Core/OcppOperationTimeout.h +++ b/src/ArduinoOcpp/Core/OcppOperationTimeout.h @@ -6,6 +6,7 @@ #define OCPPOPERATIONTIMEOUT_H #include +#include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/Core/OcppServer.cpp b/src/ArduinoOcpp/Core/OcppServer.cpp index 13a884c9..50826d61 100644 --- a/src/ArduinoOcpp/Core/OcppServer.cpp +++ b/src/ArduinoOcpp/Core/OcppServer.cpp @@ -121,7 +121,7 @@ void OcppServer::setReceiveTXTcallback(IPAddress &ip_addr, ReceiveTXTcallback &c (*result).processTXT = callback; } else { //no route found. Add new one - ReceiveTXTroute route {0}; + ReceiveTXTroute route {}; route.ip_addr = ip_addr; route.processTXT = callback; receiveTXTrouting.push_back(route); From 630b297d334bc1c6db1ef9634670a7b5e32b184c Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 15 Apr 2021 16:08:59 +0200 Subject: [PATCH 007/696] use std::function for callbacks --- src/ArduinoOcpp.cpp | 54 +++++++++++-------- src/ArduinoOcpp.h | 27 +++++----- .../Metering/ConnectorMeterValuesRecorder.h | 10 ++-- .../Tasks/Metering/MeteringService.h | 8 ++- .../SmartCharging/SmartChargingService.h | 3 +- 5 files changed, 62 insertions(+), 40 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 75b9a135..11239876 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -31,7 +31,7 @@ OcppSocket *ocppSocket; MeteringService *meteringService; PowerSampler powerSampler; EnergySampler energySampler; -bool (*evRequestsEnergySampler)() = NULL; +std::function evRequestsEnergySampler = NULL; //bool (*evRequestsEnergySampler)() = NULL; bool evRequestsEnergyLastState = false; SmartChargingService *smartChargingService; ChargePointStatusService *chargePointStatusService; @@ -170,43 +170,43 @@ void OCPP_loop() { } -void setPowerActiveImportSampler(float power()) { +void setPowerActiveImportSampler(std::function power) { powerSampler = power; meteringService->setPowerSampler(OCPP_ID_OF_CONNECTOR, powerSampler); //connectorId=1 } -void setEnergyActiveImportSampler(float energy()) { +void setEnergyActiveImportSampler(std::function energy) { energySampler = energy; meteringService->setEnergySampler(OCPP_ID_OF_CONNECTOR, energySampler); //connectorId=1 } -void setEvRequestsEnergySampler(bool evRequestsEnergy()) { +void setEvRequestsEnergySampler(std::function evRequestsEnergy) { evRequestsEnergySampler = evRequestsEnergy; } -void setOnChargingRateLimitChange(void chargingRateChanged(float limit)) { +void setOnChargingRateLimitChange(std::function chargingRateChanged) { onLimitChange = chargingRateChanged; smartChargingService->setOnLimitChange(onLimitChange); } -void setOnSetChargingProfileRequest(void listener(JsonObject payload)) { - setOnSetChargingProfileRequestListener(listener); +void setOnSetChargingProfileRequest(OnReceiveReqListener onReceiveReq) { + setOnSetChargingProfileRequestListener(onReceiveReq); } -void setOnRemoteStartTransactionSendConf(void listener(JsonObject payload)) { - setOnRemoteStartTransactionSendConfListener(listener); +void setOnRemoteStartTransactionSendConf(OnSendConfListener onSendConf) { + setOnRemoteStartTransactionSendConfListener(onSendConf); } -void setOnRemoteStopTransactionSendConf(void listener(JsonObject payload)) { - setOnRemoteStopTransactionSendConfListener(listener); +void setOnRemoteStopTransactionSendConf(OnSendConfListener onSendConf) { + setOnRemoteStopTransactionSendConfListener(onSendConf); } -void setOnResetSendConf(void listener(JsonObject payload)) { - setOnResetSendConfListener(listener); +void setOnResetSendConf(OnSendConfListener onSendConf) { + setOnResetSendConfListener(onSendConf); } -void authorize(String &idTag, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError) { +void authorize(String &idTag, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, Timeout *timeout) { OcppOperation *authorize = makeOcppOperation( new Authorize(idTag)); initiateOcppOperation(authorize); @@ -218,10 +218,13 @@ void authorize(String &idTag, OnReceiveConfListener onConf, OnAbortListener onAb authorize->setOnTimeoutListener(onTimeout); if (onError) authorize->setOnReceiveErrorListener(onError); - authorize->setTimeout(new FixedTimeout(20000)); + if (timeout) + authorize->setTimeout(timeout); + else + authorize->setTimeout(new FixedTimeout(20000)); } -void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError) { +void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, Timeout *timeout) { OcppOperation *bootNotification = makeOcppOperation( new BootNotification(chargePointModel, chargePointVendor)); initiateOcppOperation(bootNotification); @@ -233,7 +236,10 @@ void bootNotification(String chargePointModel, String chargePointVendor, OnRecei bootNotification->setOnTimeoutListener(onTimeout); if (onError) bootNotification->setOnReceiveErrorListener(onError); - bootNotification->setTimeout(new SuppressedTimeout()); + if (timeout) + bootNotification->setTimeout(timeout); + else + bootNotification->setTimeout(new SuppressedTimeout()); } void bootNotification(String &chargePointModel, String &chargePointVendor, String &chargePointSerialNumber, OnReceiveConfListener onConf) { @@ -244,7 +250,7 @@ void bootNotification(String &chargePointModel, String &chargePointVendor, Strin bootNotification->setTimeout(new SuppressedTimeout()); } -void startTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError) { +void startTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, Timeout *timeout) { OcppOperation *startTransaction = makeOcppOperation( new StartTransaction(OCPP_ID_OF_CONNECTOR)); initiateOcppOperation(startTransaction); @@ -256,7 +262,10 @@ void startTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnT startTransaction->setOnTimeoutListener(onTimeout); if (onError) startTransaction->setOnReceiveErrorListener(onError); - startTransaction->setTimeout(new FixedTimeout(20000)); + if (timeout) + startTransaction->setTimeout(timeout); + else + startTransaction->setTimeout(new FixedTimeout(20000)); } void startTransaction(String &idTag, OnReceiveConfListener onConf) { @@ -267,7 +276,7 @@ void startTransaction(String &idTag, OnReceiveConfListener onConf) { startTransaction->setTimeout(new FixedTimeout(20000)); } -void stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError) { +void stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, Timeout *timeout) { OcppOperation *stopTransaction = makeOcppOperation( new StopTransaction(OCPP_ID_OF_CONNECTOR)); initiateOcppOperation(stopTransaction); @@ -279,7 +288,10 @@ void stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTi stopTransaction->setOnTimeoutListener(onTimeout); if (onError) stopTransaction->setOnReceiveErrorListener(onError); - stopTransaction->setTimeout(new SuppressedTimeout()); + if (timeout) + stopTransaction->setTimeout(timeout); + else + stopTransaction->setTimeout(new SuppressedTimeout()); } //void startEvDrawsEnergy() { diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 9ea5dc62..44f19b5f 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -6,6 +6,7 @@ #define ARDUINOOCPP_H #include +#include #include "Variants.h" @@ -16,6 +17,8 @@ using ArduinoOcpp::OnAbortListener; using ArduinoOcpp::OnTimeoutListener; using ArduinoOcpp::OnReceiveErrorListener; +using ArduinoOcpp::Timeout; + #if USE_FACADE void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url); @@ -32,11 +35,11 @@ void OCPP_loop(); * Set the callbacks once in your setup() function. */ -void setPowerActiveImportSampler(float power()); +void setPowerActiveImportSampler(std::function power); -void setEnergyActiveImportSampler(float energy()); +void setEnergyActiveImportSampler(std::function energy); -void setEvRequestsEnergySampler(bool evRequestsEnergy()); +void setEvRequestsEnergySampler(std::function evRequestsEnergy); /* * React on calls by the library's internal functions @@ -47,7 +50,7 @@ void setEvRequestsEnergySampler(bool evRequestsEnergy()); * Set the callbacks once in your setup() function. */ -void setOnChargingRateLimitChange(void chargingRateChanged(float limit)); +void setOnChargingRateLimitChange(std::function chargingRateChanged); /* * React on CS-initiated operations @@ -59,13 +62,13 @@ void setOnChargingRateLimitChange(void chargingRateChanged(float limit)); * Set the callbacks once in your setup() function. */ -void setOnSetChargingProfileRequest(void listener(JsonObject payload)); //optional +void setOnSetChargingProfileRequest(OnReceiveReqListener onReceiveReq); //optional -void setOnRemoteStartTransactionSendConf(void listener(JsonObject payload)); //important, energize the power plug here +void setOnRemoteStartTransactionSendConf(OnSendConfListener onSendConf); //important, energize the power plug here -void setOnRemoteStopTransactionSendConf(void listener(JsonObject payload)); //important, de-energize the power plug here +void setOnRemoteStopTransactionSendConf(OnSendConfListener onSendConf); //important, de-energize the power plug here -void setOnResetSendConf(void listener(JsonObject payload)); //important, reset your device here (i.e. call ESP.reset();) +void setOnResetSendConf(OnSendConfListener onSendConf); //important, reset your device here (i.e. call ESP.reset();) /* * Perform CP-initiated operations @@ -80,13 +83,13 @@ void setOnResetSendConf(void listener(JsonObject payload)); //important, reset y * in any case. */ -void authorize(String &idTag, OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL); +void authorize(String &idTag, OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL, Timeout *timeout = NULL); -void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL); +void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL, Timeout *timeout = NULL); -void startTransaction(OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL); +void startTransaction(OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL, Timeout *timeout = NULL); -void stopTransaction(OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL); +void stopTransaction(OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL, Timeout *timeout = NULL); /* * Provide hardware-related information II diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h index d5c98a02..807b932b 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h @@ -16,8 +16,10 @@ namespace ArduinoOcpp { -typedef float (*PowerSampler)(); -typedef float (*EnergySampler)(); +//typedef float (*PowerSampler)(); +//typedef float (*EnergySampler)(); +typedef std::function PowerSampler; +typedef std::function EnergySampler; class ConnectorMeterValuesRecorder { private: @@ -30,8 +32,8 @@ class ConnectorMeterValuesRecorder { float lastPower; int lastTransactionId = -1; - float (*powerSampler)() = NULL; - float (*energySampler)() = NULL; + PowerSampler powerSampler = NULL; + EnergySampler energySampler = NULL; //ulong MeterValueSampleInterval = 60; //will be overwritten (see constructor) //ulong MeterValuesSampledDataMaxLength = 4; //will be overwritten (see constructor) diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h index d0289725..0cb47af6 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h @@ -19,8 +19,12 @@ namespace ArduinoOcpp { -typedef float (*PowerSampler)(); -typedef float (*EnergySampler)(); +//typedef float (*PowerSampler)(); +//typedef float (*EnergySampler)(); + +typedef std::function PowerSampler; +typedef std::function EnergySampler; + class MeteringService { private: diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h index 72fb72d6..19953dad 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h @@ -14,7 +14,8 @@ namespace ArduinoOcpp { -typedef void (*OnLimitChange)(float newLimit); +//typedef void (*OnLimitChange)(float newLimit); +typedef std::function OnLimitChange; class SmartChargingService { private: From 27866ea375ac5068be7a931de05cf38b015048d1 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 15 Apr 2021 16:48:47 +0200 Subject: [PATCH 008/696] added include functional --- src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h | 2 ++ src/ArduinoOcpp/Tasks/Metering/MeteringService.h | 2 ++ src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h | 1 + 3 files changed, 5 insertions(+) diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h index 807b932b..e57227f4 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h @@ -10,6 +10,8 @@ //#define METER_VALUES_SAMPLED_DATA_MAX_LENGTH 4 //after 4 measurements, send the values to the CS #include +#include + #include #include #include diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h index 0cb47af6..b18b9c88 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h @@ -11,6 +11,8 @@ #include #include +#include + #include #include #include diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h index 19953dad..1b207ac8 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h @@ -9,6 +9,7 @@ #include #include +#include #include From d073ca6369f64986093583356ad1effff40b9a22 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 21 Apr 2021 14:24:30 +0200 Subject: [PATCH 009/696] offline behavior improvements --- .../MessagesV16/RemoteStartTransaction.cpp | 79 +++++++++--------- .../MessagesV16/RemoteStartTransaction.h | 1 - .../MessagesV16/StopTransaction.cpp | 80 +++++++++---------- src/ArduinoOcpp/MessagesV16/StopTransaction.h | 1 + 4 files changed, 84 insertions(+), 77 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp index 1d083d18..4a06e454 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -14,56 +14,63 @@ RemoteStartTransaction::RemoteStartTransaction() { } -const char* RemoteStartTransaction::getOcppOperationType(){ +const char* RemoteStartTransaction::getOcppOperationType() { return "RemoteStartTransaction"; } void RemoteStartTransaction::processReq(JsonObject payload) { - idTag = String(payload["idTag"].as()); - connectorId = payload["connectorId"] | -1; + connectorId = payload["connectorId"] | -1; + + if (payload.containsKey("idTag")) { + const char *idTag = payload["idTag"] | "Invalid"; + ChargePointStatusService *cpService = getChargePointStatusService(); + if (cpService != NULL) { + cpService->authorize(idTag); + } + } } DynamicJsonDocument* RemoteStartTransaction::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); - JsonObject payload = doc->to(); - - bool canStartTransaction = false; - if (connectorId >= 1) { - //connectorId specified for given connector, try to start Transaction here - ConnectorStatus *connector = getConnectorStatus(connectorId); - if (connector != NULL){ - if (connector->getTransactionId() < 0) { - canStartTransaction = true; - } - } - } else { - //connectorId not specified. Find free connector - if (getChargePointStatusService() != NULL) { - for (int i = 1; i < getChargePointStatusService()->getNumConnectors(); i++) { - ConnectorStatus *connIter = getConnectorStatus(i); - if (connIter->getTransactionId() < 0) { - canStartTransaction = true; + DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); + JsonObject payload = doc->to(); + + bool canStartTransaction = false; + if (connectorId >= 1) { + //connectorId specified for given connector, try to start Transaction here + ConnectorStatus *connector = getConnectorStatus(connectorId); + if (connector != NULL){ + if (connector->getTransactionId() < 0) { + canStartTransaction = true; + } + } + } else { + //connectorId not specified. Find free connector + if (getChargePointStatusService() != NULL) { + for (int i = 1; i < getChargePointStatusService()->getNumConnectors(); i++) { + ConnectorStatus *connIter = getConnectorStatus(i); + if (connIter->getTransactionId() < 0) { + canStartTransaction = true; + } + } } - } } - } - if (canStartTransaction){ - payload["status"] = "Accepted"; - } else { - payload["status"] = "Rejected"; - } - - return doc; + if (canStartTransaction){ + payload["status"] = "Accepted"; + } else { + payload["status"] = "Rejected"; + } + + return doc; } DynamicJsonDocument* RemoteStartTransaction::createReq() { - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); - JsonObject payload = doc->to(); + DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); + JsonObject payload = doc->to(); - payload["idTag"] = "fefed1d19876"; + payload["idTag"] = "fefed1d19876"; - return doc; + return doc; } void RemoteStartTransaction::processConf(JsonObject payload){ diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h index 1005a744..e817d261 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h @@ -14,7 +14,6 @@ namespace Ocpp16 { class RemoteStartTransaction : public OcppMessage { private: - String idTag; int connectorId; public: RemoteStartTransaction(); diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp index 37637e49..4b5cf3cc 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp @@ -23,65 +23,65 @@ const char* StopTransaction::getOcppOperationType(){ DynamicJsonDocument* StopTransaction::createReq() { - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + (JSONDATE_LENGTH + 1)); - JsonObject payload = doc->to(); + DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + (JSONDATE_LENGTH + 1)); + JsonObject payload = doc->to(); - float meterStop = 0.0f; - if (getMeteringService() != NULL) { - meterStop = getMeteringService()->readEnergyActiveImportRegister(connectorId); - } + float meterStop = 0.0f; + if (getMeteringService() != NULL) { + meterStop = getMeteringService()->readEnergyActiveImportRegister(connectorId); + } - payload["meterStop"] = meterStop; //TODO meterStart is required to be in Wh, but measuring unit is probably inconsistent in implementation - char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; - getJsonDateStringFromSystemTime(timestamp, JSONDATE_LENGTH); - payload["timestamp"] = timestamp; + payload["meterStop"] = meterStop; //TODO meterStart is required to be in Wh, but measuring unit is probably inconsistent in implementation + char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; + getJsonDateStringFromSystemTime(timestamp, JSONDATE_LENGTH); + payload["timestamp"] = timestamp; - int transactionId = -1; - if (getConnectorStatus(connectorId) != NULL) - transactionId = getConnectorStatus(connectorId)->getTransactionId(); + ConnectorStatus *connector = getConnectorStatus(connectorId); + if (connector != NULL) { + if (connector->getTransactionId() >= 0) { + //Connector is in a transaction (transactionId > 0) or in an undefinded state (transactionId == 0). - payload["transactionId"] = transactionId; + //End the charging session in each module. - if (getConnectorStatus(connectorId) != NULL) - getConnectorStatus(connectorId)->stopEnergyOffer(); + transactionId = connector->getTransactionId(); - return doc; -} - -void StopTransaction::processConf(JsonObject payload) { + connector->stopEnergyOffer(); + connector->stopTransaction(); + connector->unauthorize(); - //no need to process anything here + SmartChargingService *scService = getSmartChargingService(); + if (scService != NULL){ + scService->endChargingNow(); + } + } // else: Connector says that is is not involved in a transaction (anymore). It has been ended before, so do nothing + } - ConnectorStatus *connector = getConnectorStatus(connectorId); + payload["transactionId"] = transactionId; - //cpStatusService->stopEnergyOffer(); //No. This should remain in createReq - if (connector != NULL) { - connector->stopTransaction(); //TODO maybe better in createReq - connector->unauthorize(); - } + return doc; +} - SmartChargingService *scService = getSmartChargingService(); - if (scService != NULL){ - scService->endChargingNow(); - } +void StopTransaction::processConf(JsonObject payload) { - if (DEBUG_OUT) Serial.print(F("[StopTransaction] Request has been accepted!\n")); + //no need to process anything here + if (DEBUG_OUT) Serial.print(F("[StopTransaction] Request has been accepted!\n")); + } void StopTransaction::processReq(JsonObject payload) { - /** - * Ignore Contents of this Req-message, because this is for debug purposes only - */ + /** + * Ignore Contents of this Req-message, because this is for debug purposes only + */ } DynamicJsonDocument* StopTransaction::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(2 * JSON_OBJECT_SIZE(1)); - JsonObject payload = doc->to(); + DynamicJsonDocument* doc = new DynamicJsonDocument(2 * JSON_OBJECT_SIZE(1)); + JsonObject payload = doc->to(); - JsonObject idTagInfo = payload.createNestedObject("idTagInfo"); - idTagInfo["status"] = "Accepted"; + JsonObject idTagInfo = payload.createNestedObject("idTagInfo"); + idTagInfo["status"] = "Accepted"; - return doc; + return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.h b/src/ArduinoOcpp/MessagesV16/StopTransaction.h index 0d348115..0ee1c3e2 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.h @@ -13,6 +13,7 @@ namespace Ocpp16 { class StopTransaction : public OcppMessage { private: int connectorId = 1; + int transactionId = -1; public: StopTransaction(); From 41a44349b7f72fe7f8c20f0f4bb305b16fef17a3 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 21 Apr 2021 14:35:58 +0200 Subject: [PATCH 010/696] fix commit --- src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp index 4a06e454..e015798c 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp @@ -22,7 +22,7 @@ void RemoteStartTransaction::processReq(JsonObject payload) { connectorId = payload["connectorId"] | -1; if (payload.containsKey("idTag")) { - const char *idTag = payload["idTag"] | "Invalid"; + String idTag = payload["idTag"] | String("Invalid"); ChargePointStatusService *cpService = getChargePointStatusService(); if (cpService != NULL) { cpService->authorize(idTag); From daebde4f40dabadb3b32d967f57f452cee569c88 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 23 Apr 2021 11:44:09 +0200 Subject: [PATCH 011/696] decouple from WebSocket library --- src/ArduinoOcpp.cpp | 18 ++++++++++++++++-- src/ArduinoOcpp.h | 2 ++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 11239876..daf56859 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -96,7 +96,7 @@ using namespace ArduinoOcpp::Ocpp16; void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url) { if (OCPP_initialized) { - Serial.print(F("[SingleConnectorEvseFacade] Error: cannot call OCPP_initialize() two times! If you want to reconfigure the library, please restart your ESP\n")); + Serial.print(F("[ArduinoOcpp] Error: cannot call OCPP_initialize() two times! If you want to reconfigure the library, please restart your ESP\n")); return; } @@ -122,6 +122,20 @@ void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url) { ocppSocket = new EspWiFi::OcppClientSocket(&webSocket); + OCPP_initialize(ocppSocket); +} + +void OCPP_initialize(OcppSocket *ocppSocket) { + if (OCPP_initialized) { + Serial.print(F("[ArduinoOcpp] Error: cannot call OCPP_initialize() two times! If you want to reconfigure the library, please restart your ESP\n")); + return; + } + + if (!ocppSocket) { + Serial.print(F("[ArduinoOcpp] OCPP_initialize(ocppSocket): ocppSocket cannot be NULL!\n")); + return; + } + ocppEngine_initialize(ocppSocket); smartChargingService = new SmartChargingService(16.0f, OCPP_NUMCONNECTORS); //default charging limit: 16A @@ -133,7 +147,7 @@ void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url) { void OCPP_loop() { if (!OCPP_initialized) { - Serial.print(F("[SingleConnectorEvseFacade] Error: you must call OCPP_initialize before calling the loop() function!\n")); + Serial.print(F("[ArduinoOcpp] Error: you must call OCPP_initialize before calling the loop() function!\n")); delay(200); //Prevent this error message from flooding the Serial monitor. return; } diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 44f19b5f..d5586d4b 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -23,6 +23,8 @@ using ArduinoOcpp::Timeout; void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url); +void OCPP_initialize(ArduinoOcpp::OcppSocket *ocppSocket); + void OCPP_loop(); /* From ceef93a3cc2bae0a1087c4d020bc6317cce60791 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 6 May 2021 10:22:37 +0200 Subject: [PATCH 012/696] suppress SPIFFS on ESP32 --- src/ArduinoOcpp/Core/Configuration.cpp | 6 +++--- .../Tasks/SmartCharging/SmartChargingService.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index 290897f3..0186f081 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -749,7 +749,7 @@ std::shared_ptr>> getAllConfig } bool configuration_init() { -#ifndef AO_DEACTIVATE_FLASH +#if !defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) SPIFFSConfig cfg; cfg.setAutoFormat(true); SPIFFS.setConfig(cfg); @@ -890,7 +890,7 @@ bool configuration_init() { } bool configuration_save() { -#ifndef AO_DEACTIVATE_FLASH +#if !defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) SPIFFS.remove(CONFIGURATION_FN); diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index 4407230f..84d3e728 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -261,7 +261,7 @@ ChargingProfile *SmartChargingService::updateProfileStack(JsonObject *json){ } bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile *chargingProfile) { -#ifndef AO_DEACTIVATE_FLASH +#if !defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) String profileFN = PROFILE_FN_PREFIX; @@ -317,7 +317,7 @@ bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile } bool SmartChargingService::loadProfiles() { -#ifndef AO_DEACTIVATE_FLASH +#if !defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) const int N_PURPOSES = 3; ChargingProfilePurposeType purposes[N_PURPOSES] = {ChargingProfilePurposeType::ChargePointMaxProfile, ChargingProfilePurposeType::TxDefaultProfile, ChargingProfilePurposeType::TxProfile}; From 8eac0ba3f16fecc6dc4d2fb7ea3b060252d5634f Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 10 May 2021 15:42:15 +0200 Subject: [PATCH 013/696] fix unitialized-warning --- src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp index dce7f21d..718c9faa 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -80,7 +80,7 @@ float maximum(float f1, float f2){ } bool ChargingSchedule::inferenceLimit(time_t t, time_t startOfCharging, float *limit, time_t *nextChange) { - time_t basis; //point in time to which schedule-related times are relative + time_t basis = 0; //point in time to which schedule-related times are relative *nextChange = INFINITY_THLD; //defaulted to Infinity switch (chargingProfileKind) { case (ChargingProfileKindType::Absolute): From 90661b8cd3c7bbb468e57a0e87b6f85898814621 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 10 May 2021 15:43:03 +0200 Subject: [PATCH 014/696] add ESP32 in config file --- platformio.ini | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/platformio.ini b/platformio.ini index f61ee1c6..8c9ae48b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,21 +10,40 @@ ; https://docs.platformio.org/en/latest/projectconf/index.html [platformio] -default_envs = nodemcuv2 +default_envs = esp32-development-board -[env:nodemcuv2] -platform = espressif8266 +[common] framework = arduino -board = nodemcuv2 - -; Library options lib_deps = ArduinoJson@6.17.2 WebSockets@2.2.0 ivanseidel/LinkedList @ 0.0.0-alpha+sha.dac3874d28 Time@1.6 - -; Serial Monitor options +build_flags = + -D USE_FACADE=true monitor_speed = 115200 +[env:nodemcuv2] +platform = espressif8266 +board = nodemcuv2 +framework = ${common.framework} +lib_deps = ${common.lib_deps} +monitor_speed = ${common.monitor_speed} +build_flags = + ${common.build_flags} + -D DEBUG_OUT=true + -D TRAFFIC_OUT=true + +[env:esp32-development-board] +platform = espressif32 +board = esp-wrover-kit +framework = ${common.framework} +lib_deps = ${common.lib_deps} +monitor_speed = ${common.monitor_speed} +build_flags = + ${common.build_flags} + -D DEBUG_OUT=true + -D TRAFFIC_OUT=true +upload_speed = 921600 + ; From 919424e4b6414d13d34528b654f28b8a913d4bfc Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 10 May 2021 15:43:20 +0200 Subject: [PATCH 015/696] add ESP32 SPIFFS --- min_spiffs.csv | 6 ++++++ src/ArduinoOcpp/Core/Configuration.cpp | 17 +++++++++++++++-- .../SmartCharging/SmartChargingService.cpp | 8 ++++++-- 3 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 min_spiffs.csv diff --git a/min_spiffs.csv b/min_spiffs.csv new file mode 100644 index 00000000..0b6a9ffd --- /dev/null +++ b/min_spiffs.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x1E0000, +app1, app, ota_1, 0x1F0000,0x1E0000, +spiffs, data, spiffs, 0x3D0000,0x30000, diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index 0186f081..1815db14 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -10,7 +10,11 @@ #include #include +#if defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) +#include "SPIFFS.h" +#else #include +#endif #define MAX_FILE_SIZE 4000 #define MAX_CONFIGURATIONS 50 @@ -749,7 +753,15 @@ std::shared_ptr>> getAllConfig } bool configuration_init() { -#if !defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) +#ifndef AO_DEACTIVATE_FLASH + +#if defined(ESP32) + if(!SPIFFS.begin(true)){ + Serial.println("An Error has occurred while mounting SPIFFS"); + return false; + } +#else + //ESP8266 SPIFFSConfig cfg; cfg.setAutoFormat(true); SPIFFS.setConfig(cfg); @@ -758,6 +770,7 @@ bool configuration_init() { Serial.print(F("[Configuration] Unable to initialize: unable to mount FS\n")); return false; } +#endif File file = SPIFFS.open(CONFIGURATION_FN, "r"); @@ -890,7 +903,7 @@ bool configuration_init() { } bool configuration_save() { -#if !defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) +#ifndef AO_DEACTIVATE_FLASH SPIFFS.remove(CONFIGURATION_FN); diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index 84d3e728..0d4945b1 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -9,6 +9,10 @@ #include +#if defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) +#include "SPIFFS.h" +#endif + #define SINGLE_CONNECTOR_ID 1 #define PROFILE_FN_PREFIX "/ocpp-" @@ -261,7 +265,7 @@ ChargingProfile *SmartChargingService::updateProfileStack(JsonObject *json){ } bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile *chargingProfile) { -#if !defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) +#ifndef AO_DEACTIVATE_FLASH String profileFN = PROFILE_FN_PREFIX; @@ -317,7 +321,7 @@ bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile } bool SmartChargingService::loadProfiles() { -#if !defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) +#ifndef AO_DEACTIVATE_FLASH const int N_PURPOSES = 3; ChargingProfilePurposeType purposes[N_PURPOSES] = {ChargingProfilePurposeType::ChargePointMaxProfile, ChargingProfilePurposeType::TxDefaultProfile, ChargingProfilePurposeType::TxProfile}; From 5d923faedd075080ea1e12a04b9696229268801c Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 17 May 2021 11:46:34 +0200 Subject: [PATCH 016/696] add authorization info to facade --- src/ArduinoOcpp.cpp | 4 ++++ src/ArduinoOcpp.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index daf56859..d6452b20 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -320,4 +320,8 @@ int getTransactionId() { return chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->getTransactionId(); } +bool existsUnboundIdTag() { + return chargePointStatusService->existsUnboundAuthorization(); +} + #endif diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index d5586d4b..1864db95 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -112,5 +112,7 @@ void stopTransaction(OnReceiveConfListener onConf = NULL, OnAbortListener onAbor int getTransactionId(); //returns the ID of the current transaction. Returns -1 if called before or after an transaction +bool existsUnboundIdTag(); //returns if the user has given a valid Ocpp Charging Card which is not used for a transaction yet + #endif #endif From 6005a024449fff4069075266c8d580ec15bd62da Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 17 May 2021 13:57:46 +0200 Subject: [PATCH 017/696] more specific reaction on RemoteStopTransaction --- src/ArduinoOcpp.cpp | 4 ++++ src/ArduinoOcpp.h | 1 + src/ArduinoOcpp/SimpleOcppOperationFactory.cpp | 8 +++++++- src/ArduinoOcpp/SimpleOcppOperationFactory.h | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index d6452b20..beeef2f7 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -212,6 +212,10 @@ void setOnRemoteStartTransactionSendConf(OnSendConfListener onSendConf) { setOnRemoteStartTransactionSendConfListener(onSendConf); } +void setOnRemoteStopTransactionReceiveReq(OnReceiveReqListener onReceiveReq) { + setOnRemoteStopTransactionReceiveRequestListener(onReceiveReq); +} + void setOnRemoteStopTransactionSendConf(OnSendConfListener onSendConf) { setOnRemoteStopTransactionSendConfListener(onSendConf); } diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 1864db95..6dd63ead 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -69,6 +69,7 @@ void setOnSetChargingProfileRequest(OnReceiveReqListener onReceiveReq); //option void setOnRemoteStartTransactionSendConf(OnSendConfListener onSendConf); //important, energize the power plug here void setOnRemoteStopTransactionSendConf(OnSendConfListener onSendConf); //important, de-energize the power plug here +void setOnRemoteStopTransactionReceiveReq(OnReceiveReqListener onSendConf); //optional, to de-energize the power plug immediately void setOnResetSendConf(OnSendConfListener onSendConf); //important, reset your device here (i.e. call ESP.reset();) diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 919b73e3..db4c26d0 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -72,6 +72,11 @@ void setOnRemoteStartTransactionSendConfListener(OnSendConfListener listener){ onRemoteStartTransactionSendConf = listener; } +OnReceiveReqListener onRemoteStopTransactionReceiveRequest; +void setOnRemoteStopTransactionReceiveRequestListener(OnReceiveReqListener listener){ + onRemoteStopTransactionReceiveRequest = listener; +} + OnSendConfListener onRemoteStopTransactionSendConf; void setOnRemoteStopTransactionSendConfListener(OnSendConfListener listener){ onRemoteStopTransactionSendConf = listener; @@ -197,7 +202,8 @@ OcppOperation *makeOcppOperation(const char *messageType) { } else if (!strcmp(messageType, "RemoteStopTransaction")) { msg = new Ocpp16::RemoteStopTransaction(); if (onRemoteStopTransactionSendConf == NULL) - Serial.print(F("[SimpleOcppOperationFactory] Warning: RemoteStopTransaction is without effect when the sendConf listener is not set. Set a listener which initiates the StopTransaction operation.\n")); + Serial.print(F("[SimpleOcppOperationFactory] Warning: RemoteStopTransaction is without effect when no sendConf listener is set. Set a listener which initiates the StopTransaction operation.\n")); + operation->setOnReceiveReqListener(onRemoteStopTransactionReceiveRequest); operation->setOnSendConfListener(onRemoteStopTransactionSendConf); } else if (!strcmp(messageType, "ChangeConfiguration")) { msg = new Ocpp16::ChangeConfiguration(); diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.h b/src/ArduinoOcpp/SimpleOcppOperationFactory.h index 32064a8d..8e23e26d 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.h +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.h @@ -33,6 +33,7 @@ void setOnStartTransactionRequestListener(OnReceiveReqListener onReceiveReq); void setOnTriggerMessageRequestListener(OnReceiveReqListener onReceiveReq); void setOnRemoteStartTransactionReceiveRequestListener(OnReceiveReqListener onReceiveReq); void setOnRemoteStartTransactionSendConfListener(OnSendConfListener onSendConf); +void setOnRemoteStopTransactionReceiveRequestListener(OnReceiveReqListener onReceiveReq); void setOnRemoteStopTransactionSendConfListener(OnSendConfListener onSendConf); void setOnChangeConfigurationReceiveRequestListener(OnReceiveReqListener onReceiveReq); void setOnChangeConfigurationSendConfListener(OnSendConfListener onSendConf); From d4bacbe62aa7d689cba375e2023958c278c55752 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 17 May 2021 17:50:51 +0200 Subject: [PATCH 018/696] LittleFS in Configuration; typo --- src/ArduinoOcpp.cpp | 3 ++- src/ArduinoOcpp.h | 2 +- src/ArduinoOcpp/Core/Configuration.cpp | 20 +++++++++++--------- src/ArduinoOcpp/Core/Configuration.h | 2 +- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index beeef2f7..1ca28e4c 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -100,7 +100,8 @@ void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url) { return; } - configuration_init(); //call before each other library call + const bool FORMAT_FS_ON_FAIL = true; + configuration_init(FORMAT_FS_ON_FAIL); //call before each other library call // server address, port and URL webSocket.begin(CS_hostname, CS_port, CS_url, "ocpp1.6"); diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 6dd63ead..aeb885a6 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -69,7 +69,7 @@ void setOnSetChargingProfileRequest(OnReceiveReqListener onReceiveReq); //option void setOnRemoteStartTransactionSendConf(OnSendConfListener onSendConf); //important, energize the power plug here void setOnRemoteStopTransactionSendConf(OnSendConfListener onSendConf); //important, de-energize the power plug here -void setOnRemoteStopTransactionReceiveReq(OnReceiveReqListener onSendConf); //optional, to de-energize the power plug immediately +void setOnRemoteStopTransactionReceiveReq(OnReceiveReqListener onReceiveReq); //optional, to de-energize the power plug immediately void setOnResetSendConf(OnSendConfListener onSendConf); //important, reset your device here (i.e. call ESP.reset();) diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index 1815db14..dc15ab9c 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -11,9 +11,11 @@ #include #if defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) -#include "SPIFFS.h" +#include +#define USE_FS LITTLEFS #else #include +#define USE_FS SPIFFS #endif #define MAX_FILE_SIZE 4000 @@ -22,7 +24,7 @@ #define KEY_MAXLEN 60 #define STRING_VAL_MAXLEN 500 -#define CONFIGURATION_FN "/esp8266-ocpp.cnf" +#define CONFIGURATION_FN "/arduino-ocpp.cnf" #define DEV_CONF true @@ -752,18 +754,18 @@ std::shared_ptr>> getAllConfig return result; } -bool configuration_init() { +bool configuration_init(bool formatOnFail) { #ifndef AO_DEACTIVATE_FLASH #if defined(ESP32) - if(!SPIFFS.begin(true)){ - Serial.println("An Error has occurred while mounting SPIFFS"); + if(!LITTLEFS.begin(formatOnFail)) { + Serial.println("[Configuration] An Error has occurred while mounting LITTLEFS"); return false; } #else //ESP8266 SPIFFSConfig cfg; - cfg.setAutoFormat(true); + cfg.setAutoFormat(formatOnFail); SPIFFS.setConfig(cfg); if (!SPIFFS.begin()) { @@ -772,7 +774,7 @@ bool configuration_init() { } #endif - File file = SPIFFS.open(CONFIGURATION_FN, "r"); + File file = USE_FS.open(CONFIGURATION_FN, "r"); if (!file) { Serial.print(F("[Configuration] Unable to initialize: could not open configuration file\n")); @@ -905,9 +907,9 @@ bool configuration_init() { bool configuration_save() { #ifndef AO_DEACTIVATE_FLASH - SPIFFS.remove(CONFIGURATION_FN); + USE_FS.remove(CONFIGURATION_FN); - File file = SPIFFS.open(CONFIGURATION_FN, "w"); + File file = USE_FS.open(CONFIGURATION_FN, "w"); if (!file) { Serial.print(F("[Configuration] Unable to save: could not open configuration file\n")); diff --git a/src/ArduinoOcpp/Core/Configuration.h b/src/ArduinoOcpp/Core/Configuration.h index 9849e927..b00ea301 100644 --- a/src/ArduinoOcpp/Core/Configuration.h +++ b/src/ArduinoOcpp/Core/Configuration.h @@ -116,7 +116,7 @@ std::shared_ptr> getConfiguration_string(const char std::shared_ptr getConfiguration(const char *key); std::shared_ptr>> getAllConfigurations(); -bool configuration_init(); +bool configuration_init(bool formatOnFail); bool configuration_save(); } //end namespace ArduinoOcpp From f95f29c11fd0c23507146453a4cb48e3ee010cf6 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 19 May 2021 10:54:32 +0200 Subject: [PATCH 019/696] add LittleFS support --- src/ArduinoOcpp/Core/Configuration.cpp | 4 ++-- .../Tasks/SmartCharging/SmartChargingService.cpp | 14 +++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index dc15ab9c..7461ae93 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -769,7 +769,7 @@ bool configuration_init(bool formatOnFail) { SPIFFS.setConfig(cfg); if (!SPIFFS.begin()) { - Serial.print(F("[Configuration] Unable to initialize: unable to mount FS\n")); + Serial.print(F("[Configuration] Unable to initialize: unable to mount SPIFFS\n")); return false; } #endif @@ -788,7 +788,7 @@ bool configuration_init(bool formatOnFail) { } Serial.print(F("[Configuration] DEBUG: end configuration file\n")); file.close(); - file = SPIFFS.open(CONFIGURATION_FN, "r"); + file = USE_FS.open(CONFIGURATION_FN, "r"); if (!file) { Serial.print(F("[Configuration] Unable to initialize: could not open configuration file\n")); diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index 0d4945b1..3ba06f26 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -10,7 +10,11 @@ #include #if defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) -#include "SPIFFS.h" +#include +#define USE_FS LITTLEFS +#else +#include +#define USE_FS SPIFFS #endif #define SINGLE_CONNECTOR_ID 1 @@ -284,9 +288,9 @@ bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile profileFN += chargingProfile->getStackLevel(); profileFN += PROFILE_FN_SUFFIX; - SPIFFS.remove(profileFN); + USE_FS.remove(profileFN); - File file = SPIFFS.open(profileFN, "w"); + File file = USE_FS.open(profileFN, "w"); if (!file) { Serial.print(F("[SmartChargingService] Unable to save: could not save profile: ")); @@ -308,7 +312,7 @@ bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile // BEGIN DEBUG if (DEBUG_OUT) { - file = SPIFFS.open(profileFN, "r"); + file = USE_FS.open(profileFN, "r"); Serial.println(file.readStringUntil('\n')); @@ -351,7 +355,7 @@ bool SmartChargingService::loadProfiles() { profileFN += iLevel; profileFN += PROFILE_FN_SUFFIX; - File file = SPIFFS.open(profileFN, "r"); + File file = USE_FS.open(profileFN, "r"); if (file) { if (DEBUG_OUT) Serial.print(F("[SmartChargingService] Load profile from file: ")); From 6dc6165e211b39e19f366a8e652a7fbd250a855b Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 22 May 2021 13:06:49 +0200 Subject: [PATCH 020/696] extensible file management --- src/ArduinoOcpp.cpp | 13 +- src/ArduinoOcpp.h | 7 +- src/ArduinoOcpp/Core/Configuration.cpp | 1004 +++-------------- src/ArduinoOcpp/Core/Configuration.h | 118 +- .../Core/ConfigurationContainer.cpp | 74 ++ src/ArduinoOcpp/Core/ConfigurationContainer.h | 54 + .../Core/ConfigurationContainerFlash.cpp | 209 ++++ .../Core/ConfigurationContainerFlash.h | 28 + .../Core/ConfigurationKeyValue.cpp | 416 +++++++ src/ArduinoOcpp/Core/ConfigurationKeyValue.h | 126 +++ src/ArduinoOcpp/Core/ConfigurationOptions.h | 42 + .../MessagesV16/ChangeConfiguration.cpp | 176 +-- .../MessagesV16/GetConfiguration.cpp | 33 +- .../ChargePointStatus/ConnectorStatus.cpp | 200 ++-- .../SmartCharging/SmartChargingService.cpp | 23 +- .../SmartCharging/SmartChargingService.h | 6 +- 16 files changed, 1355 insertions(+), 1174 deletions(-) create mode 100644 src/ArduinoOcpp/Core/ConfigurationContainer.cpp create mode 100644 src/ArduinoOcpp/Core/ConfigurationContainer.h create mode 100644 src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp create mode 100644 src/ArduinoOcpp/Core/ConfigurationContainerFlash.h create mode 100644 src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp create mode 100644 src/ArduinoOcpp/Core/ConfigurationKeyValue.h create mode 100644 src/ArduinoOcpp/Core/ConfigurationOptions.h diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 1ca28e4c..cc2559ab 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -94,15 +94,12 @@ using namespace ArduinoOcpp; using namespace ArduinoOcpp::Facade; using namespace ArduinoOcpp::Ocpp16; -void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url) { +void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, ArduinoOcpp::FilesystemOpt fsOpt) { if (OCPP_initialized) { Serial.print(F("[ArduinoOcpp] Error: cannot call OCPP_initialize() two times! If you want to reconfigure the library, please restart your ESP\n")); return; } - const bool FORMAT_FS_ON_FAIL = true; - configuration_init(FORMAT_FS_ON_FAIL); //call before each other library call - // server address, port and URL webSocket.begin(CS_hostname, CS_port, CS_url, "ocpp1.6"); @@ -123,10 +120,10 @@ void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url) { ocppSocket = new EspWiFi::OcppClientSocket(&webSocket); - OCPP_initialize(ocppSocket); + OCPP_initialize(ocppSocket, fsOpt); } -void OCPP_initialize(OcppSocket *ocppSocket) { +void OCPP_initialize(OcppSocket *ocppSocket, ArduinoOcpp::FilesystemOpt fsOpt) { if (OCPP_initialized) { Serial.print(F("[ArduinoOcpp] Error: cannot call OCPP_initialize() two times! If you want to reconfigure the library, please restart your ESP\n")); return; @@ -136,10 +133,12 @@ void OCPP_initialize(OcppSocket *ocppSocket) { Serial.print(F("[ArduinoOcpp] OCPP_initialize(ocppSocket): ocppSocket cannot be NULL!\n")); return; } + + configuration_init(fsOpt); //call before each other library call ocppEngine_initialize(ocppSocket); - smartChargingService = new SmartChargingService(16.0f, OCPP_NUMCONNECTORS); //default charging limit: 16A + smartChargingService = new SmartChargingService(16.0f, OCPP_NUMCONNECTORS, fsOpt); //default charging limit: 16A chargePointStatusService = new ChargePointStatusService(&webSocket, OCPP_NUMCONNECTORS); //Constructor adds instance to ocppEngine in constructor meteringService = new MeteringService(&webSocket, OCPP_NUMCONNECTORS); diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index aeb885a6..543ab408 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -7,6 +7,7 @@ #include #include +#include #include "Variants.h" @@ -21,9 +22,9 @@ using ArduinoOcpp::Timeout; #if USE_FACADE -void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url); +void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail); -void OCPP_initialize(ArduinoOcpp::OcppSocket *ocppSocket); +void OCPP_initialize(ArduinoOcpp::OcppSocket *ocppSocket, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail); void OCPP_loop(); diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index 7461ae93..80c830e7 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -3,11 +3,13 @@ // MIT License #include +//#include +#include #include #include -#include +#include #include #if defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) @@ -18,508 +20,9 @@ #define USE_FS SPIFFS #endif -#define MAX_FILE_SIZE 4000 -#define MAX_CONFIGURATIONS 50 - -#define KEY_MAXLEN 60 -#define STRING_VAL_MAXLEN 500 - -#define CONFIGURATION_FN "/arduino-ocpp.cnf" - -#define DEV_CONF true - namespace ArduinoOcpp { -AbstractConfiguration::AbstractConfiguration() { - -} - -AbstractConfiguration::AbstractConfiguration(JsonObject storedKeyValuePair) { - if (storedKeyValuePair["key"].as().is()) { - setKey(storedKeyValuePair["key"]); - } else { - Serial.print(F("[AbstractConfiguration] Type mismatch: cannot construct with given storedKeyValuePair\n")); - } -} - -AbstractConfiguration::~AbstractConfiguration() { - if (key != NULL) { - free(key); - } - key = NULL; -} - -void AbstractConfiguration::printKey() { - if (key != NULL) { - Serial.print(key); - } -} - -size_t AbstractConfiguration::getStorageHeaderJsonCapacity() { - return JSON_OBJECT_SIZE(1) - + key_size + 1; //TODO key_size already considers 0-terminator. Is "+1" really necessary here? -} - -void AbstractConfiguration::storeStorageHeader(JsonObject keyValuePair) { - if (toBeRemovedFlag) return; - keyValuePair["key"] = key; -} - -size_t AbstractConfiguration::getOcppMsgHeaderJsonCapacity() { - return JSON_OBJECT_SIZE(2) - + key_size + 1; //TODO key_size already considers 0-terminator. Is "+1" really necessary here? -} - -void AbstractConfiguration::storeOcppMsgHeader(JsonObject keyValuePair) { - if (toBeRemovedFlag) return; - keyValuePair["key"] = key; - if (writepermission) { - keyValuePair["readonly"] = "false"; - } else { - keyValuePair["readonly"] = "true"; - } -} - -bool AbstractConfiguration::isValid() { - return initializedValue && key != NULL && key_size > 0 && !toBeRemovedFlag; -} - -bool AbstractConfiguration::setKey(const char *newKey) { - if (key != NULL || key_size > 0) { - Serial.print(F("[AbstractConfiguration] Cannot change key or set key twice! Keep old value\n")); - return false; - } - - key_size = strlen(newKey) + 1; //plus 0-terminator - - if (key_size > KEY_MAXLEN + 1) { - Serial.print(F("[AbstractConfiguration] in setKey(): Maximal key length exceeded: ")); - Serial.print(newKey); - Serial.print(F(". Abort\n")); - return false; - } else if (key_size <= 1) { - Serial.print(F("[AbstractConfiguration] in setKey(): Null or empty key not allowed! Abort\n")); - return false; - } - - key = (char *) malloc(sizeof(char) * key_size); - if (!key) { - Serial.print(F("[AbstractConfiguration] in setKey(): Could not allocate key\n")); - return false; - } - - strcpy(key, newKey); - - return true; -} - -void AbstractConfiguration::revokeWritePermission() { - writepermission = false; -} - -bool AbstractConfiguration::hasWritePermission() { - return writepermission; -} - -void AbstractConfiguration::requireRebootWhenChanged() { - rebootRequiredWhenChanged = true; -} - -bool AbstractConfiguration::requiresRebootWhenChanged() { - return rebootRequiredWhenChanged; -} - -bool AbstractConfiguration::toBeRemoved() { - return toBeRemovedFlag; -} - -void AbstractConfiguration::setToBeRemoved() { - toBeRemovedFlag = true; -} - -void AbstractConfiguration::resetToBeRemovedFlag() { - toBeRemovedFlag = false; -} - -uint16_t AbstractConfiguration::getValueRevision() { - return value_revision; -} - -int AbstractConfiguration::compareKey(const char *other) { - return strcmp(key, other); -} - - -template -Configuration::Configuration() { - -} - -template -Configuration::Configuration(JsonObject storedKeyValuePair) : AbstractConfiguration(storedKeyValuePair) { - if (storedKeyValuePair["value"].as().is()) { - initializeValue(storedKeyValuePair["value"].as().as()); - } else { - Serial.print(F("[Configuration] Type mismatch: cannot deserialize Json to given Type T\n")); - } -} - -template -T Configuration::operator=(T newVal) { - - if (hasWritePermission()) { - if (value != newVal) { - dirty = true; - value_revision++; - } - value = newVal; - initializedValue = true; - resetToBeRemovedFlag(); - } else { - Serial.print(F("[Configuration] Tried to override read-only configuration: ")); - printKey(); - Serial.println(); - } - return value; -} - -//template -//Configuration::operator T() { -// if (!initializedValue) { -// Serial.print(F("[Configuration] Tried to access value without preceeding initialization: ")); -// printKey(); -// Serial.println(); -// } -// return value; -//} - -template -bool Configuration::isValid() { - return AbstractConfiguration::isValid(); -} - -//template -//Configuration::operator bool() { -// return isValid(); -//} - -template -void Configuration::initializeValue(T init) { - if (initializedValue) { - Serial.print(F("[Configuration] Tried to initialize value twice for: ")); - printKey(); - Serial.println(); - return; - } - - *this = init; - - if (DEBUG_OUT) Serial.print(F("[Configuration] Initialized config object. Key = ")); - if (DEBUG_OUT) printKey(); - if (DEBUG_OUT) Serial.print(F(", value = ")); - if (DEBUG_OUT) Serial.println(value); -} - -template -std::shared_ptr Configuration::toJsonStorageEntry() { - if (!isValid() || toBeRemoved()) { - return NULL; - } - size_t capacity = getStorageHeaderJsonCapacity() - + JSON_OBJECT_SIZE(3); //type, header, value - - std::shared_ptr doc = std::make_shared(capacity); - JsonObject keyValuePair = doc->to(); - keyValuePair["type"] = getSerializedTypeSpecifier(); - storeStorageHeader(keyValuePair); - keyValuePair["value"] = value; - return doc; -} - -//template<> -//std::shared_ptr Configuration::toJsonStorageEntry() { -// if (!isValid() || toBeRemoved()) { -// return NULL; -// } -// -// /* -// * need to enforce at least one decimal place -// */ -// String serialized = String(value, 6); //6 decimal places, i.e. value = 42.1234 --> serialized = 42.123400 -// unsigned int trunicated_len = serialized.length(); //without trailing 0 -// char *trunicated_value = (char *) malloc(sizeof(char) * (trunicated_len + 1)); //malloc includes trailing 0 -// serialized.toCharArray(trunicated_value, trunicated_len + 1); -// while (trunicated_len > 1) { -// unsigned int index_last = trunicated_len - 1; -// if (trunicated_value[index_last] == '0' && trunicated_value[index_last - 1] != '.') { -// trunicated_value[index_last] = '\0'; -// trunicated_len--; -// } else { -// break; -// } -// } -// -// size_t capacity = getStorageHeaderJsonCapacity() -// + JSON_OBJECT_SIZE(1) -// + trunicated_len + 1; //value -// -// std::shared_ptr doc = std::make_shared(capacity); -// JsonObject keyValuePair = doc->to(); -// storeStorageHeader(keyValuePair); -// keyValuePair["value"] = trunicated_value; -// free(trunicated_value); -// return doc; -//} - -template<> -const char *Configuration::getSerializedTypeSpecifier() { - return "int"; -} - -template<> -const char *Configuration::getSerializedTypeSpecifier() { - return "float"; -} - -template -std::shared_ptr Configuration::toJsonOcppMsgEntry() { - if (!isValid() || toBeRemoved()) { - return NULL; - } - size_t capacity = getOcppMsgHeaderJsonCapacity() - + JSON_OBJECT_SIZE(1); // value - - std::shared_ptr doc = std::make_shared(capacity); - JsonObject keyValuePair = doc->to(); - storeOcppMsgHeader(keyValuePair); - keyValuePair["value"] = value; - return doc; -} - -Configuration::Configuration() { - -} - -Configuration::Configuration(JsonObject storedKeyValuePair) : AbstractConfiguration(storedKeyValuePair) { - if (storedKeyValuePair["value"].as().is()) { - const char *storedValue = storedKeyValuePair["value"].as().as(); - if (storedValue) { - size_t storedValueSize = strlen(storedValue) + 1; - storedValueSize++; - initializeValue(storedValue, storedValueSize); - } else { - Serial.print(F("[Configuration] Stored value is empty\n")); - } - } else { - Serial.print(F("[Configuration] Type mismatch: cannot deserialize Json to given Type T\n")); - } -} - -Configuration::~Configuration() { - if (value != NULL) { - free(value); - value = NULL; - } - if (valueReadOnlyCopy != NULL) { - free(valueReadOnlyCopy); - valueReadOnlyCopy = NULL; - } -} - -bool Configuration::setValue(const char *new_value, size_t buffsize) { - - if (!hasWritePermission()) { - Serial.print(F("[Configuration] Tried to override read-only configuration: ")); - printKey(); - Serial.println(); - return false; - } - - if (!new_value) { - Serial.print(F("[Configuration] in setValue(): Argument is null! No change\n")); - return false; - } - - //"ab" - //buffsize=5 - //a != 0 -> checkedBuffsize = 1 - //b != 0 -> checkedBuffsize = 2 - //0 == 0 -> checkedBuffsize = 3 - //break - size_t checkedBuffsize = 0; - for (size_t i = 0; i < buffsize; i++) { - if (new_value[i] != '\0') { - checkedBuffsize++; - } else { - checkedBuffsize++; - break; - } - } - - if (checkedBuffsize <= 0) { - Serial.print(F("[Configuration] in setValue(): buffsize is <= 0! No change\n")); - return false; - } - - if (checkedBuffsize > STRING_VAL_MAXLEN + 1) { - Serial.print(F("[Configuration] in setValue(): Maximal value length exceeded! Abort\n")); - return false; - } - - bool value_changed = true; - if (checkedBuffsize == value_size) { - value_changed = false; - for (size_t i = 0; i < value_size; i++) { - if (value[i] != new_value[i]) { - value_changed = true; - break; - } - } - - } - - if (value_changed) { - - if (value != NULL) { - free(value); - value = NULL; - } - - if (valueReadOnlyCopy != NULL) { - free(valueReadOnlyCopy); - valueReadOnlyCopy = NULL; - } - - value_size = checkedBuffsize; - - value = (char *) malloc (sizeof(char) * value_size); - valueReadOnlyCopy = (char *) malloc (sizeof(char) * value_size); - if (!value || !valueReadOnlyCopy) { - Serial.print(F("[Configuration] in setValue(): Could not allocate value or value copy\n")); - - if (value != NULL) { - free(value); - value = NULL; - } - - if (valueReadOnlyCopy != NULL) { - free(valueReadOnlyCopy); - valueReadOnlyCopy = NULL; - } - - value_size = 0; - return false; - } - - strncpy(value, new_value, value_size); - strncpy(valueReadOnlyCopy, new_value, value_size); - - value[value_size-1] = '\0'; - valueReadOnlyCopy[value_size-1] = '\0'; - - value_revision++; - } - - resetToBeRemovedFlag(); - initializedValue = true; - return true; -} - -String &Configuration::operator=(String &newVal) { - if (!setValue(newVal.c_str(), newVal.length() + 1)) { - Serial.print(F("[Configuration] Setting value in operator= was unsuccessful!\n")); - } - return newVal; -} - -Configuration::operator const char*() { - strncpy(valueReadOnlyCopy, value, value_size); - return valueReadOnlyCopy; -} - -//Configuration::operator String() { -// return String(value); -//} - -bool Configuration::isValid() { - return AbstractConfiguration::isValid() && value != NULL && valueReadOnlyCopy != NULL && value_size > 0; -} - -//Configuration::operator bool() { -// return isValid(); -//} - -size_t Configuration::getBuffsize() { - return value_size; -} - -bool Configuration::initializeValue(const char *initval, size_t initsize) { - if (initializedValue) { - Serial.print(F("[Configuration] Tried to initialize value twice for: ")); - printKey(); - Serial.println(); - return false; - } - - if (DEBUG_OUT) Serial.print(F("[Configuration] Initializing config object. Key = ")); - if (DEBUG_OUT) printKey(); - if (DEBUG_OUT) Serial.print(F(", value = ")); - if (DEBUG_OUT) Serial.println(initval); - - return setValue(initval, initsize); -} - -std::shared_ptr Configuration::toJsonStorageEntry() { - if (!isValid() || toBeRemoved()) { - return NULL; - } - - size_t capacity = getStorageHeaderJsonCapacity() - + JSON_OBJECT_SIZE(3) //type, header, value - + value_size + 1; //TODO value_size already considers 0-terminator. Is "+1" really necessary here? - - std::shared_ptr doc = std::make_shared(capacity); - JsonObject keyValuePair = doc->to(); - keyValuePair["type"] = getSerializedTypeSpecifier(); - storeStorageHeader(keyValuePair); - keyValuePair["value"] = value; - return doc; -} - -std::shared_ptr Configuration::toJsonOcppMsgEntry() { - if (!isValid() || toBeRemoved()) { - return NULL; - } - - size_t capacity = getOcppMsgHeaderJsonCapacity() - + JSON_OBJECT_SIZE(2) //header, value - + value_size + 1; //TODO value_size already considers 0-terminator. Is "+1" really necessary here? - - std::shared_ptr doc = std::make_shared(capacity); - JsonObject keyValuePair = doc->to(); - storeOcppMsgHeader(keyValuePair); - keyValuePair["value"] = value; - return doc; -} - -//LinkedList configuration_all = LinkedList(); - -LinkedList abc = LinkedList(); - -LinkedList>> configuration_ints = LinkedList>>(); -LinkedList>> configuration_floats = LinkedList>>(); -LinkedList>> configuration_strings = LinkedList>>(); - -template -std::shared_ptr> getConfiguration(LinkedList>> *collection, const char *key) { - - for (int i = 0; i < collection->size(); i++) { - if (!collection->get(i)->compareKey(key)) { - return collection->get(i); - } - } - return NULL; -} +FilesystemOpt configurationFilesystemOpt = FilesystemOpt::Use_Mount_FormatOnFail; template std::shared_ptr> createConfiguration(const char *key, T value) { @@ -531,437 +34,260 @@ std::shared_ptr> createConfiguration(const char *key, T value) return NULL; } - configuration->initializeValue(value); + *configuration = value; return configuration; } -std::shared_ptr> createConfiguration(const char *key, const char *value, size_t buffsize) { +std::shared_ptr> createConfiguration(const char *key, const char *value) { std::shared_ptr> configuration = std::make_shared>(); if (!configuration->setKey(key)) { - Serial.print(F("[Configuration] In createConfiguration(key, const char*, ...) : Cannot set key! Abort\n")); + Serial.print(F("[Configuration] In createConfiguration(key, const char*) : Cannot set key! Abort\n")); return NULL; } - configuration->initializeValue(value, buffsize); + if (!configuration->setValue(value, strlen(value) + 1)) { + Serial.print(F("[Configuration] In createConfiguration(key, const char*) : Cannot set value! Abort\n")); + return NULL; + } return configuration; } -std::shared_ptr removeConfiguration(const char *key) { - - std::shared_ptr configuration; - - configuration = getConfiguration(&configuration_ints, key); - if (configuration) { - if (configuration->hasWritePermission()) - configuration->setToBeRemoved(); - return configuration; - } - - configuration = getConfiguration(&configuration_floats, key); - if (configuration) { - if (configuration->hasWritePermission()) - configuration->setToBeRemoved(); - return configuration; - } - - configuration = getConfiguration(&configuration_strings, key); - if (configuration) { - if (configuration->hasWritePermission()) - configuration->setToBeRemoved(); - return configuration; +std::shared_ptr createConfigurationContainer(const char *filename) { + if (configurationFilesystemOpt.accessAllowed()) { + return std::static_pointer_cast(std::make_shared(filename)); + } else { + return std::static_pointer_cast(std::make_shared(filename)); } - - return NULL; } -std::shared_ptr> declareConfiguration(const char *key, int defaultValue, bool writePermission, bool rebootRequiredWhenChanged) { - //already existent? --> stored in last session --> do set default content, but set writepermission flag +std::vector> configurationContainers; - std::shared_ptr> configuration = getConfiguration(&configuration_ints, key); +std::shared_ptr getContainer(const char *filename) { + std::vector>::iterator container = std::find_if(configurationContainers.begin(), configurationContainers.end(), + [filename](std::shared_ptr &elem) { + return !strcmp(elem->getFilename(), filename); + }); - if (!configuration) { - configuration = createConfiguration(key, defaultValue); - if (!configuration) { - Serial.print(F("[Configuration] In declareConfiguration(key, int, ...) : Cannot find configuration stored from previous session and cannot create new one! Abort\n")); - return NULL; - } - configuration_ints.add(configuration); - } else if (configuration->toBeRemoved()) { //corner case - *configuration = defaultValue; + if (container != configurationContainers.end()) { + return *container; + } else { + return NULL; } - - if (!writePermission) - configuration->revokeWritePermission(); - - if (rebootRequiredWhenChanged) - configuration->requireRebootWhenChanged(); - - return configuration; } -std::shared_ptr> declareConfiguration(const char *key, float defaultValue, bool writePermission, bool rebootRequiredWhenChanged) { +template +std::shared_ptr> declareConfiguration(const char *key, T defaultValue, const char *filename, bool remotePeerCanWrite, bool remotePeerCanRead, bool localClientCanWrite, bool rebootRequiredWhenChanged) { //already existent? --> stored in last session --> do set default content, but set writepermission flag + + std::shared_ptr container = getContainer(filename); + + if (!container) { + Serial.print(F("[Configuration] declareConfiguration: init new configurations container on flash filesystem: ")); + Serial.println(filename); - std::shared_ptr> configuration = getConfiguration(&configuration_floats, key); + container = createConfigurationContainer(filename); + configurationContainers.push_back(container); - if (!configuration) { - configuration = createConfiguration(key, defaultValue); - if (!configuration) { - Serial.print(F("[Configuration] In declareConfiguration(key, float, ...) : Cannot find configuration stored from previous session and cannot create new one! Abort\n")); - return NULL; + if (!container->load()) { + Serial.print(F("[Configuration] Cannot load file contents. Path will be overwritten\n")); } - configuration_floats.add(configuration); - } else if (configuration->toBeRemoved()) { //corner case - *configuration = defaultValue; } - if (!writePermission) - configuration->revokeWritePermission(); - - if (rebootRequiredWhenChanged) - configuration->requireRebootWhenChanged(); - - return configuration; -} + std::shared_ptr configuration = container->getConfiguration(key); -std::shared_ptr> declareConfiguration(const char *key, const char *defaultValue, bool writePermission, bool rebootRequiredWhenChanged) { - //already existent? --> stored in last session --> do set default content, but set writepermission flag + if (configuration && strcmp(configuration->getSerializedType(), SerializedType::get())) { + Serial.println(F("[Configuration] Error declaring configuration: conflicting declared types. Override previous declaration")); + container->removeConfiguration(configuration); + configuration->setToBeRemoved(); + configuration = NULL; + } - std::shared_ptr> configuration = getConfiguration(&configuration_strings, key); + std::shared_ptr> configurationConcrete = std::static_pointer_cast>(configuration); - if (!configuration) { - configuration = createConfiguration(key, defaultValue, strlen(defaultValue) + 1); + if (configurationConcrete && configurationConcrete->toBeRemoved()) { + (*configurationConcrete) = defaultValue; + } + + if (!configurationConcrete) { + configurationConcrete = createConfiguration(key, defaultValue); + configuration = std::static_pointer_cast(configurationConcrete); if (!configuration) { - Serial.print(F("[Configuration] In declareConfiguration(key, const char *, ...) : Cannot find configuration stored from previous session and cannot create new one! Abort\n")); + Serial.print(F("[Configuration] In declareConfiguration(key, int, ...) : Cannot find configuration stored from previous session and cannot create new one! Abort\n")); return NULL; } - - configuration_strings.add(configuration); - } else if (configuration->toBeRemoved()) { //corner case - configuration->setValue(defaultValue, strlen(defaultValue) + 1); + container->addConfiguration(configuration); } - if (!writePermission) - configuration->revokeWritePermission(); - + if (!remotePeerCanWrite) + configuration->revokePermissionRemotePeerCanWrite(); + if (!remotePeerCanRead) + configuration->revokePermissionRemotePeerCanRead(); + if (!localClientCanWrite) + configuration->revokePermissionLocalClientCanWrite(); if (rebootRequiredWhenChanged) configuration->requireRebootWhenChanged(); - - return configuration; + + return configurationConcrete; } -std::shared_ptr> setConfiguration(const char *key, int value) { +namespace Ocpp16 { - std::shared_ptr> configuration = getConfiguration(&configuration_ints, key); +/* +template +std::shared_ptr> changeConfiguration(const char *key, T value) { - if (configuration) { - *configuration = value; - } else { - configuration = createConfiguration(key, value); - if (!configuration) { - Serial.print(F("[Configuration] In setConfiguration(key, int) : Cannot neither find configuration nor create new one! Abort\n")); - return NULL; + std::shared_ptr> config = NULL; + + for (std::vector>::iterator container = configurationContainers.begin(); container != configurationContainers.end(); container++) { + + config = std::static_pointer_cast>(*container)->getConfiguration(key); + if (config) { + break; } - configuration_ints.add(configuration); } - return configuration; -} - -std::shared_ptr> setConfiguration(const char *key, float value) { - - std::shared_ptr> configuration = getConfiguration(&configuration_floats, key); + if (config) { + //found configuration - if (configuration) { - *configuration = value; - } else { - configuration = createConfiguration(key, value); - if (!configuration) { - Serial.print(F("[Configuration] In setConfiguration(key, float) : Cannot neither find configuration nor create new one! Abort\n")); + if (!config->permissionRemotePeerCanWrite()) { return NULL; } - configuration_floats.add(configuration); - } - - return configuration; -} - -std::shared_ptr> setConfiguration(const char *key, const char *value, size_t buffsize) { - std::shared_ptr> configuration = getConfiguration(&configuration_strings, key); + *config = value; - if (configuration) { - configuration->setValue(value, buffsize); } else { - configuration = createConfiguration(key, value, buffsize); - if (!configuration) { - Serial.print(F("[Configuration] In setConfiguration(key, const char* value, buffsize) : Cannot neither find configuration nor create new one! Abort\n")); + //did not find configuration. Create new one + config = createConfiguration(key, value); + + if (!config) { + Serial.print(F("[Configuration] In setConfiguration(key, int) : Cannot neither find configuration nor create new one! Abort\n")); return NULL; } - configuration_strings.add(configuration); - } - - return configuration; -} -std::shared_ptr> getConfiguration_Int(const char *key) { - return getConfiguration(&configuration_ints, key); -} + std::shared_ptr containerDefault = getContainer(CONFIGURATION_FN); -std::shared_ptr> getConfiguration_Float(const char *key) { - return getConfiguration(&configuration_floats, key); -} + if (containerDefault) { + containerDefault->addConfiguration(config); + } //else: There is no container. Probably this controller does not need persistency at all + } -std::shared_ptr> getConfiguration_string(const char *key) { - return getConfiguration(&configuration_strings, key); + return config; } +*/ -std::shared_ptr getConfiguration(const char *key) { //TODO maybe use iterator like "getAllConfigurations"? +std::shared_ptr getConfiguration(const char *key) { std::shared_ptr result = NULL; - result = getConfiguration_Int(key); - if (result) - return result; - - result = getConfiguration_Float(key); - if (result) - return result; - - result = getConfiguration_string(key); - if (result) - return result; - + for (auto container = configurationContainers.begin(); container != configurationContainers.end(); container++) { + result = (*container)->getConfiguration(key); + if (result) + return result; + } return NULL; } -std::shared_ptr>> getAllConfigurations() { //TODO maybe change to iterator? - auto result = std::make_shared>>(); - - for (int i = 0; i < configuration_ints.size(); i++) - result->add(configuration_ints.get(i)); +std::shared_ptr>> getAllConfigurations() { //TODO maybe change to iterator? + auto result = std::make_shared>>(); - for (int i = 0; i < configuration_floats.size(); i++) - result->add(configuration_floats.get(i)); - - for (int i = 0; i < configuration_strings.size(); i++) - result->add(configuration_strings.get(i)); + for (auto container = configurationContainers.begin(); container != configurationContainers.end(); container++) { + for (auto config = (*container)->configurationsIteratorBegin(); config != (*container)->configurationsIteratorEnd(); config++) { + if ((*config)->permissionRemotePeerCanRead()) { + result->push_back(*config); + } + } + } return result; } -bool configuration_init(bool formatOnFail) { +} //end namespace Ocpp16 + +bool configuration_init(FilesystemOpt fsOpt) { + bool loadRoutineSuccessful = false; #ifndef AO_DEACTIVATE_FLASH + configurationFilesystemOpt = fsOpt; + + if (fsOpt.mustMount()) { #if defined(ESP32) - if(!LITTLEFS.begin(formatOnFail)) { - Serial.println("[Configuration] An Error has occurred while mounting LITTLEFS"); - return false; - } + if(!LITTLEFS.begin(fsOpt.formatOnFail())) { + Serial.println("[Configuration] An Error has occurred while mounting LITTLEFS"); + loadRoutineSuccessful = false; + } #else - //ESP8266 - SPIFFSConfig cfg; - cfg.setAutoFormat(formatOnFail); - SPIFFS.setConfig(cfg); - - if (!SPIFFS.begin()) { - Serial.print(F("[Configuration] Unable to initialize: unable to mount SPIFFS\n")); - return false; - } -#endif - - File file = USE_FS.open(CONFIGURATION_FN, "r"); - - if (!file) { - Serial.print(F("[Configuration] Unable to initialize: could not open configuration file\n")); - return false; - } - -#if 0 - Serial.print(F("[Configuration] DEBUG: dump configuration file contents: \n")); - while (file.available()) { - Serial.write(file.read()); - } - Serial.print(F("[Configuration] DEBUG: end configuration file\n")); - file.close(); - file = USE_FS.open(CONFIGURATION_FN, "r"); - - if (!file) { - Serial.print(F("[Configuration] Unable to initialize: could not open configuration file\n")); - return false; - } + //ESP8266 + SPIFFSConfig cfg; + cfg.setAutoFormat(fsOpt.formatOnFail()); + SPIFFS.setConfig(cfg); + + if (!SPIFFS.begin()) { + Serial.print(F("[Configuration] Unable to initialize: unable to mount SPIFFS\n")); + loadRoutineSuccessful = false; + } #endif + } //end fs mount - if (!file.available()) { - Serial.print(F("[Configuration] Unable to initialize: empty file\n")); - file.close(); - return false; - } - - int file_size = file.size(); - - if (file_size < 2) { - Serial.print(F("[Configuration] Unable to initialize: too short for json\n")); - file.close(); - return false; - } else if (file_size > MAX_FILE_SIZE) { - Serial.print(F("[Configuration] Unable to initialize: filesize is too long!\n")); - file.close(); - return false; - } - - String token = file.readStringUntil('\n'); - if (!token.equals("content-type:esp8266-ocpp_configuration_file")) { - Serial.print(F("[Configuration] Unable to initialize: unrecognized configuration file format\n")); - file.close(); - return false; - } - - token = file.readStringUntil('\n'); - if (!token.equals("version:1.0")) { - Serial.print(F("[Configuration] Unable to initialize: unsupported version\n")); - file.close(); - return false; - } - - token = file.readStringUntil(':'); - if (!token.equals("configurations_len")) { - Serial.print(F("[Configuration] Unable to initialize: missing length statement\n")); - file.close(); - return false; - } - - token = file.readStringUntil('\n'); - int configurations_len = token.toInt(); - if (configurations_len <= 0) { - Serial.print(F("[Configuration] Initialization: Empty configuration\n")); - file.close(); - return true; - } - if (configurations_len > MAX_CONFIGURATIONS) { - Serial.print(F("[Configuration] Unable to initialize: configurations_len is too big!\n")); - file.close(); - return false; - } - - size_t jsonCapacity = file_size + JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(configurations_len) + configurations_len * JSON_OBJECT_SIZE(3); - - if (DEBUG_OUT) Serial.print(F("[Configuration] Config capacity = ")); - if (DEBUG_OUT) Serial.print(jsonCapacity); - if (DEBUG_OUT) Serial.print(F("\n")); - - DynamicJsonDocument config(jsonCapacity); - - DeserializationError error = deserializeJson(config, file); - if (error) { - Serial.println(F("[Configuration] Unable to initialize: config file deserialization failed: ")); - Serial.print(error.c_str()); - Serial.print(F("\n")); - file.close(); - return false; + std::shared_ptr containerDefault = NULL; + for (auto container = configurationContainers.begin(); container != configurationContainers.end(); container++) { + if (!strcmp((*container)->getFilename(), CONFIGURATION_FN)) { + containerDefault = (*container); + break; + } } - JsonArray configurations = config["configurations"]; - for (int i = 0; i < configurations.size(); i++) { - JsonObject pair = configurations[i]; - const char *type = pair["type"] | "Undefined"; - - //backwards compatibility. Will be removed in a few months - if (!strcmp(type, "Undefined")) { - if (pair["value"].as().is()){ - type = "int"; - } else if (pair["value"].as().is()){ - type = "float"; - } else if (pair["value"].as().is()){ - type = "string"; - } - } //end backwards compatibility - - if (!strcmp(type, "int")){ - std::shared_ptr> configuration = std::make_shared>(pair); - configuration_ints.add(configuration); - } else if (!strcmp(type, "float")){ - std::shared_ptr> configuration = std::make_shared>(pair); - configuration_floats.add(configuration); - } else if (!strcmp(type, "string")){ - std::shared_ptr> configuration = std::make_shared>(pair); - configuration_strings.add(configuration); - } else { - Serial.print(F("[Configuration] Initialization fault: could not read key-value pair ")); - Serial.print(pair["key"].as()); - Serial.print(F("\n")); + if (containerDefault) { + Serial.print(F("[Configuration] Found default container before calling configuration_init(). If you added\n" \ + " the container manually, please ensure to call load(). If not, it is a hint\n" \ + " that declareConfiguration() was called too early\n")); + } else { + containerDefault = createConfigurationContainer(CONFIGURATION_FN); + if (!containerDefault->load()) { + Serial.print(F("[Configuration] Loading default configurations file failed!\n")); + loadRoutineSuccessful = false; } + configurationContainers.push_back(containerDefault); } - file.close(); - Serial.println(F("[Configuration] Initialization successful\n")); #endif //ndef AO_DEACTIVATE_FLASH - return true; + return loadRoutineSuccessful; } bool configuration_save() { + bool success = true; #ifndef AO_DEACTIVATE_FLASH - USE_FS.remove(CONFIGURATION_FN); - - File file = USE_FS.open(CONFIGURATION_FN, "w"); - - if (!file) { - Serial.print(F("[Configuration] Unable to save: could not open configuration file\n")); - return false; - } - - size_t jsonCapacity = JSON_OBJECT_SIZE(1); //configurations - - std::shared_ptr>> allConfigurations = getAllConfigurations(); - - int numEntries = allConfigurations->size(); - - file.print("content-type:esp8266-ocpp_configuration_file\n"); - file.print("version:1.0\n"); - file.print("configurations_len:"); - file.print(numEntries, DEC); - file.print("\n"); - - LinkedList> entries = LinkedList>(); - - for (int i = 0; i < allConfigurations->size(); i++) { - std::shared_ptr entry = allConfigurations->get(i)->toJsonStorageEntry(); - if (entry) - entries.add(entry); - } - jsonCapacity += JSON_ARRAY_SIZE(entries.size()); //length of configurations - for (int i = 0; i < entries.size(); i++) { - jsonCapacity += entries.get(i)->capacity(); + for (auto container = configurationContainers.begin(); container != configurationContainers.end(); container++) { + if (!(*container)->save()) { + success = false; + } } - DynamicJsonDocument config(jsonCapacity); - - JsonArray configurations = config.createNestedArray("configurations"); - - for (int i = 0; i < entries.size(); i++) { - configurations.add(entries.get(i)->as()); - } +#endif //ndef AO_DEACTIVATE_FLASH + return success; +} - // Serialize JSON to file - if (serializeJson(config, file) == 0) { - Serial.println(F("[Configuration] Unable to save: Could not serialize JSON\n")); - file.close(); - return false; - } +template std::shared_ptr> createConfiguration(const char *key, int value); +template std::shared_ptr> createConfiguration(const char *key, float value); +template std::shared_ptr> createConfiguration(const char *key, const char * value); +//template std::shared_ptr> createConfiguration(const char *key, String value); - //success - file.close(); - Serial.print(F("[Configuration] Saving config successful\n")); +template std::shared_ptr> declareConfiguration(const char *key, int defaultValue, const char *filename, bool remotePeerCanWrite, bool remotePeerCanRead, bool localClientCanWrite, bool rebootRequiredWhenChanged); +template std::shared_ptr> declareConfiguration(const char *key, float defaultValue, const char *filename, bool remotePeerCanWrite, bool remotePeerCanRead, bool localClientCanWrite, bool rebootRequiredWhenChanged); +template std::shared_ptr> declareConfiguration(const char *key, const char *defaultValue, const char *filename, bool remotePeerCanWrite, bool remotePeerCanRead, bool localClientCanWrite, bool rebootRequiredWhenChanged); +//template std::shared_ptr> declareConfiguration(const char *key, String defaultValue, const char *filename, bool remotePeerCanWrite, bool remotePeerCanRead, bool localClientCanWrite, bool rebootRequiredWhenChanged); -#endif //ndef AO_DEACTIVATE_FLASH - return true; -} +/* +template std::shared_ptr> Ocpp16::changeConfiguration(const char *key, int value); +template std::shared_ptr> Ocpp16::changeConfiguration(const char *key, float value); +template std::shared_ptr> Ocpp16::changeConfiguration(const char *key, const char *value); +//template std::shared_ptr> Ocpp16::changeConfiguration(const char *key, String value); +*/ } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Core/Configuration.h b/src/ArduinoOcpp/Core/Configuration.h index b00ea301..b2d587da 100644 --- a/src/ArduinoOcpp/Core/Configuration.h +++ b/src/ArduinoOcpp/Core/Configuration.h @@ -1,122 +1,30 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License #ifndef CONFIGURATION_H #define CONFIGURATION_H -#include -#include -#include +#include +#include -namespace ArduinoOcpp { - -class AbstractConfiguration { -private: - char *key = NULL; - size_t key_size = 0; // key=NULL --> key_size = 0; key = "" --> key_size = 1; key = "A" --> key_size = 2 - bool rebootRequiredWhenChanged = false; - bool toBeRemovedFlag = false; - bool writepermission = true; //write permission for ChangeConfiguration operation -protected: - uint16_t value_revision = 0; //number of changes of subclass-member "value". This will be important for the client to detect if there was a change - bool initializedValue = false; - bool dirty = false; - - AbstractConfiguration(); - AbstractConfiguration(JsonObject storedKeyValuePair); - size_t getStorageHeaderJsonCapacity(); - void storeStorageHeader(JsonObject keyValuePair); - size_t getOcppMsgHeaderJsonCapacity(); - void storeOcppMsgHeader(JsonObject keyValuePair); - bool isValid(); -public: - virtual ~AbstractConfiguration(); - bool setKey(const char *key); - void printKey(); - void revokeWritePermission(); - bool hasWritePermission(); - void requireRebootWhenChanged(); - bool requiresRebootWhenChanged(); - bool toBeRemoved(); - void setToBeRemoved(); - void resetToBeRemovedFlag(); - //idea: commit(); //probably better than global commit(): what if configurations will be split into multiple files in future? +#define CONFIGURATION_FN "/arduino-ocpp.cnf" - uint16_t getValueRevision(); - int compareKey(const char *other); - - virtual std::shared_ptr toJsonStorageEntry() = 0; - virtual std::shared_ptr toJsonOcppMsgEntry() = 0; -}; +namespace ArduinoOcpp { template -class Configuration : public AbstractConfiguration { -private: - T value; - const char *getSerializedTypeSpecifier(); //returns "int" or "float" as written to the configuration Json file -public: - Configuration(); - Configuration(JsonObject storedKeyValuePair); - T operator=(T newVal); - operator T() { //TODO move to Configuration.cpp - if (!initializedValue) { - Serial.print(F("[Configuration] Tried to access value without preceeding initialization: ")); - printKey(); - Serial.println(); - } - return value; - } - bool isValid(); - //operator bool(); - - void initializeValue(T init); - - std::shared_ptr toJsonStorageEntry(); - std::shared_ptr toJsonOcppMsgEntry(); -}; - -template <> -class Configuration : public AbstractConfiguration { -private: - char *value = NULL; - char *valueReadOnlyCopy = NULL; - size_t value_size = 0; - const char *getSerializedTypeSpecifier() {return "string";} -public: - Configuration(); - Configuration(JsonObject storedKeyValuePair); - ~Configuration(); - bool setValue(const char *newVal, size_t buffsize); - String &operator=(String &newVal); - operator const char*(); - //operator String(); - bool isValid(); - //operator bool(); - size_t getBuffsize(); - - bool initializeValue(const char *initval, size_t initsize); - - std::shared_ptr toJsonStorageEntry(); - std::shared_ptr toJsonOcppMsgEntry(); -}; - -std::shared_ptr> declareConfiguration(const char *key, int defaultValue, bool writePermission = true, bool rebootRequiredWhenChanged = false); -std::shared_ptr> declareConfiguration(const char *key, float defaultValue, bool writePermission = true, bool rebootRequiredWhenChanged = false); -std::shared_ptr> declareConfiguration(const char *key, const char *defaultValue, bool writePermission = true, bool rebootRequiredWhenChanged = false); +std::shared_ptr> declareConfiguration(const char *key, T defaultValue, const char *filename = CONFIGURATION_FN, bool remotePeerCanWrite = true, bool remotePeerCanRead = true, bool localClientCanWrite = true, bool rebootRequiredWhenChanged = false); -std::shared_ptr> setConfiguration(const char *key, int value); -std::shared_ptr> setConfiguration(const char *key, float value); -std::shared_ptr> setConfiguration(const char *key, const char *value, size_t buffsize); +namespace Ocpp16 { -std::shared_ptr> getConfiguration_Int(const char *key); -std::shared_ptr> getConfiguration_Float(const char *key); -std::shared_ptr> getConfiguration_string(const char *key); +// template +// std::shared_ptr> changeConfiguration(const char *key, T value); -std::shared_ptr getConfiguration(const char *key); -std::shared_ptr>> getAllConfigurations(); + std::shared_ptr getConfiguration(const char *key); + std::shared_ptr>> getAllConfigurations(); +} -bool configuration_init(bool formatOnFail); +bool configuration_init(FilesystemOpt fsOpt = FilesystemOpt::Use_Mount_FormatOnFail); bool configuration_save(); } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Core/ConfigurationContainer.cpp b/src/ArduinoOcpp/Core/ConfigurationContainer.cpp new file mode 100644 index 00000000..12de79f5 --- /dev/null +++ b/src/ArduinoOcpp/Core/ConfigurationContainer.cpp @@ -0,0 +1,74 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include + +namespace ArduinoOcpp { + +std::shared_ptr ConfigurationContainer::getConfiguration(const char *key) { + for (std::vector>::iterator configuration = configurations.begin(); configuration != configurations.end(); configuration++) { + if ((*configuration)->keyEquals(key)) { + return *configuration; + } + } + return NULL; +} + +bool ConfigurationContainer::removeConfiguration(std::shared_ptr configuration) { + + auto config = configurations.begin(); + auto config_rev = configurations_revision.begin(); + while (config != configurations.end()) { + if ((*config) == configuration) { + configurations.erase(config); + if (config_rev != configurations_revision.end()) + configurations_revision.erase(config_rev); + return true; + } + config++; + if (config_rev != configurations_revision.end()) + config_rev++; + } + + return false; +} + +void ConfigurationContainer::addConfiguration(std::shared_ptr configuration) { + configurations.push_back(configuration); +} + +bool ConfigurationContainer::configurationsUpdated() { + bool updated = false; + + auto config = configurations.begin(); + auto config_rev = configurations_revision.begin(); + while (config != configurations.end()) { + if (config_rev == configurations_revision.end()) { + //vectors are not the same length -> added configurations + updated = true; + break; + } + + if ((*config)->getValueRevision() != *config_rev) { + updated = true; + break; + } + + config++; + config_rev++; + } + + if (updated) { + configurations_revision.erase(config_rev, configurations_revision.end()); + + while (config != configurations.end()) { + configurations_revision.push_back((*config)->getValueRevision()); + config++; + } + } + + return updated; +} + +} //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Core/ConfigurationContainer.h b/src/ArduinoOcpp/Core/ConfigurationContainer.h new file mode 100644 index 00000000..70a76002 --- /dev/null +++ b/src/ArduinoOcpp/Core/ConfigurationContainer.h @@ -0,0 +1,54 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef CONFIGURATIONCONTAINER_H +#define CONFIGURATIONCONTAINER_H + +#include +#include + +#include + +namespace ArduinoOcpp { + +class ConfigurationContainer { +private: + std::vector configurations_revision; + const char *filename; + +protected: + std::vector> configurations; + + ConfigurationContainer(const char *filename) : filename(filename) { } + + //Checks if configurations_revision is equal to (for all) configurations->getValueRevision(). If not, it refreshes the record + bool configurationsUpdated(); +public: + virtual ~ConfigurationContainer() = default; + + virtual bool load() = 0; + + virtual bool save() = 0; + + const char *getFilename() {return filename;}; + + std::shared_ptr getConfiguration(const char *key); + std::vector>::iterator configurationsIteratorBegin() {return configurations.begin();} + std::vector>::iterator configurationsIteratorEnd() {return configurations.end();} + bool removeConfiguration(std::shared_ptr configuration); + void addConfiguration(std::shared_ptr configuration); +}; + +class ConfigurationContainerVolatile : public ConfigurationContainer { +public: + ConfigurationContainerVolatile(const char *filename) : ConfigurationContainer(filename) { } + + bool load() {return true;} + + bool save() {return true;} +}; + +} //end namespace ArduinoOcpp + +#endif diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp new file mode 100644 index 00000000..abc56ea0 --- /dev/null +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp @@ -0,0 +1,209 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include + +#if defined(ESP32) +#define USE_FS LITTLEFS +#else +#define USE_FS SPIFFS +#endif + +#if USE_FS == LITTLEFS +#include +#elif USE_FS == SPIFFS +#include +#else +#error "FS not supported" +#endif + +#define MAX_FILE_SIZE 4000 +#define MAX_CONFIGURATIONS 50 + +namespace ArduinoOcpp { + +bool ConfigurationContainerFlash::load() { +#ifndef AO_DEACTIVATE_FLASH + + if (configurations.size() > 0) { + Serial.print(F("[ConfigurationsContainerFlash] Error: declared configurations before calling container->load().\n" \ + " All previously declared values won't be written back\n")); + } + + File file = USE_FS.open(getFilename(), "r"); + + if (!file) { + Serial.print(F("[Configuration] Unable to initialize: could not open configuration file\n")); + return false; + } + + if (!file.available()) { + Serial.print(F("[Configuration] Unable to initialize: empty file\n")); + file.close(); + return false; + } + + int file_size = file.size(); + + if (file_size < 2) { + Serial.print(F("[Configuration] Unable to initialize: too short for json\n")); + file.close(); + return false; + } else if (file_size > MAX_FILE_SIZE) { + Serial.print(F("[Configuration] Unable to initialize: filesize is too long!\n")); + file.close(); + return false; + } + + String token = file.readStringUntil('\n'); + if (!token.equals("content-type:arduino-ocpp_configuration_file")) { + Serial.print(F("[Configuration] Unable to initialize: unrecognized configuration file format\n")); + file.close(); + return false; + } + + token = file.readStringUntil('\n'); + if (!token.equals("version:1.0")) { + Serial.print(F("[Configuration] Unable to initialize: unsupported version\n")); + file.close(); + return false; + } + + token = file.readStringUntil(':'); + if (!token.equals("configurations_len")) { + Serial.print(F("[Configuration] Unable to initialize: missing length statement\n")); + file.close(); + return false; + } + + token = file.readStringUntil('\n'); + int configurations_len = token.toInt(); + if (configurations_len <= 0) { + Serial.print(F("[Configuration] Initialization: Empty configuration\n")); + file.close(); + return true; + } + if (configurations_len > MAX_CONFIGURATIONS) { + Serial.print(F("[Configuration] Unable to initialize: configurations_len is too big!\n")); + file.close(); + return false; + } + + size_t jsonCapacity = file_size + JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(configurations_len) + configurations_len * JSON_OBJECT_SIZE(5); + + if (DEBUG_OUT) Serial.print(F("[Configuration] Config capacity = ")); + if (DEBUG_OUT) Serial.print(jsonCapacity); + if (DEBUG_OUT) Serial.print(F("\n")); + + DynamicJsonDocument configDoc(jsonCapacity); + + DeserializationError error = deserializeJson(configDoc, file); + if (error) { + Serial.println(F("[Configuration] Unable to initialize: config file deserialization failed: ")); + Serial.print(error.c_str()); + Serial.print(F("\n")); + file.close(); + return false; + } + + JsonArray configurationsArray = configDoc["configurations"]; + for (int i = 0; i < configurationsArray.size(); i++) { + JsonObject config = configurationsArray[i]; + const char *type = config["type"] | "Undefined"; + + std::shared_ptr configuration = NULL; + + if (!strcmp(type, SerializedType::get())){ + configuration = std::make_shared>(config); + } else if (!strcmp(type, SerializedType::get())){ + configuration = std::make_shared>(config); + } else if (!strcmp(type, SerializedType::get())){ + configuration = std::make_shared>(config); +// } else if (!strcmp(type, SerializedType::get())){ +// configuration = std::make_shared>(config); + } + + if (configuration) { + configurations.push_back(configuration); + } else { + Serial.print(F("[Configuration] Initialization fault: could not read key-value pair ")); + Serial.print(config["key"].as()); + Serial.print(F(" of type ")); + Serial.print(config["type"].as()); + Serial.print(F("\n")); + } + } + + file.close(); + + configurationsUpdated(); + + Serial.println(F("[Configuration] Initialization successful\n")); +#endif //ndef AO_DEACTIVATE_FLASH + return true; +} + +bool ConfigurationContainerFlash::save() { +#ifndef AO_DEACTIVATE_FLASH + + if (!configurationsUpdated()) { + return true; //nothing to be done + } + + USE_FS.remove(getFilename()); + + File file = USE_FS.open(getFilename(), "w"); + + if (!file) { + Serial.print(F("[Configuration] Unable to save: could not open configuration file\n")); + return false; + } + + size_t jsonCapacity = JSON_OBJECT_SIZE(1); //configurations + + size_t numEntries = configurations.size(); + + file.print("content-type:arduino-ocpp_configuration_file\n"); + file.print("version:1.0\n"); + file.print("configurations_len:"); + file.print(numEntries, DEC); + file.print("\n"); + + std::vector> entries; + + for (auto config = configurations.begin(); config != configurations.end(); config++) { + std::shared_ptr entry = (*config)->toJsonStorageEntry(); + if (entry) + entries.push_back(entry); + } + + jsonCapacity += JSON_ARRAY_SIZE(entries.size()); //length of configurations + for (auto entry = entries.begin(); entry != entries.end(); entry++) { + jsonCapacity += (*entry)->capacity(); + } + + DynamicJsonDocument configDoc(jsonCapacity); + + JsonArray configurationsArray = configDoc.createNestedArray("configurations"); + + for (auto entry = entries.begin(); entry != entries.end(); entry++) { + configurationsArray.add((*entry)->as()); + } + + // Serialize JSON to file + if (serializeJson(configDoc, file) == 0) { + Serial.println(F("[Configuration] Unable to save: Could not serialize JSON\n")); + file.close(); + return false; + } + + //success + file.close(); + Serial.print(F("[Configuration] Saving configDoc successful\n")); + +#endif //ndef AO_DEACTIVATE_FLASH + return true; +} + +} //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.h b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.h new file mode 100644 index 00000000..cc1471e4 --- /dev/null +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.h @@ -0,0 +1,28 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef CONFIGURATIONCONTAINERFLASH_H +#define CONFIGURATIONCONTAINERFLASH_H + +#include + +namespace ArduinoOcpp { + +class ConfigurationContainerFlash : public ConfigurationContainer { + + +public: + ConfigurationContainerFlash(const char *filename) : ConfigurationContainer(filename) { } + + ~ConfigurationContainerFlash() = default; + + bool load(); + + bool save(); + +}; + +} //end namespace ArduinoOcpp + +#endif diff --git a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp new file mode 100644 index 00000000..74fd0ad5 --- /dev/null +++ b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp @@ -0,0 +1,416 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include + +#include + +#include +#include +#include + +#if defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) +#include +#define USE_FS LITTLEFS +#else +#include +#define USE_FS SPIFFS +#endif + +#define KEY_MAXLEN 60 +#define STRING_VAL_MAXLEN 500 + +namespace ArduinoOcpp { + +AbstractConfiguration::AbstractConfiguration() { + +} + +AbstractConfiguration::AbstractConfiguration(JsonObject storedKeyValuePair) { + if (storedKeyValuePair["key"].as().is()) { + setKey(storedKeyValuePair["key"]); + } else { + Serial.print(F("[AbstractConfiguration] Type mismatch: cannot construct with given storedKeyValuePair\n")); + } +} + +AbstractConfiguration::~AbstractConfiguration() { + if (key != NULL) { + free(key); + } + key = NULL; +} + +void AbstractConfiguration::printKey() { + if (key != NULL) { + Serial.print(key); + } +} + +size_t AbstractConfiguration::getStorageHeaderJsonCapacity() { + return JSON_OBJECT_SIZE(1) //key + + key_size + 1; //TODO key_size already considers 0-terminator. Is "+1" really necessary here? +} + +void AbstractConfiguration::storeStorageHeader(JsonObject keyValuePair) { + if (toBeRemovedFlag) return; + keyValuePair["key"] = key; +} + +size_t AbstractConfiguration::getOcppMsgHeaderJsonCapacity() { + return JSON_OBJECT_SIZE(2) //key + readonly value + + key_size + 1; //TODO key_size already considers 0-terminator. Is "+1" really necessary here? +} + +void AbstractConfiguration::storeOcppMsgHeader(JsonObject keyValuePair) { + if (toBeRemovedFlag) return; + keyValuePair["key"] = key; + if (remotePeerCanWrite) { + keyValuePair["readonly"] = "false"; + } else { + keyValuePair["readonly"] = "true"; + } +} + +bool AbstractConfiguration::isValid() { + return initializedValue && key != NULL && key_size > 0 && !toBeRemovedFlag; +} + +bool AbstractConfiguration::setKey(const char *newKey) { + if (key != NULL || key_size > 0) { + Serial.print(F("[AbstractConfiguration] Cannot change key or set key twice! Keep old value\n")); + return false; + } + + key_size = strlen(newKey) + 1; //plus 0-terminator + + if (key_size > KEY_MAXLEN + 1) { + Serial.print(F("[AbstractConfiguration] in setKey(): Maximal key length exceeded: ")); + Serial.print(newKey); + Serial.print(F(". Abort\n")); + return false; + } else if (key_size <= 1) { + Serial.print(F("[AbstractConfiguration] in setKey(): Null or empty key not allowed! Abort\n")); + return false; + } + + key = (char *) malloc(sizeof(char) * key_size); + if (!key) { + Serial.print(F("[AbstractConfiguration] in setKey(): Could not allocate key\n")); + return false; + } + + strcpy(key, newKey); + + return true; +} + +void AbstractConfiguration::requireRebootWhenChanged() { + rebootRequiredWhenChanged = true; +} + +bool AbstractConfiguration::requiresRebootWhenChanged() { + return rebootRequiredWhenChanged; +} + +bool AbstractConfiguration::toBeRemoved() { + return toBeRemovedFlag; +} + +void AbstractConfiguration::setToBeRemoved() { + toBeRemovedFlag = true; +} + +void AbstractConfiguration::resetToBeRemovedFlag() { + toBeRemovedFlag = false; +} + +uint16_t AbstractConfiguration::getValueRevision() { + return value_revision; +} + +bool AbstractConfiguration::keyEquals(const char *other) { + return !strcmp(key, other); +} + +template +Configuration::Configuration() { + +} + +Configuration::Configuration() { + +} + +template +Configuration::Configuration(JsonObject storedKeyValuePair) : AbstractConfiguration(storedKeyValuePair) { + if (storedKeyValuePair["value"].as().is()) { + this->operator=(storedKeyValuePair["value"].as().as()); + } else { + Serial.print(F("[Configuration] Type mismatch: cannot deserialize Json to given Type T\n")); + } +} + +template +const T &Configuration::operator=(const T & newVal) { + + if (permissionLocalClientCanWrite() || !initializedValue) { + if (DEBUG_OUT && !initializedValue) { + Serial.print(F("[Configuration] Initialized config object. Key = ")); + printKey(); + Serial.print(F(", value = ")); + Serial.println(newVal); + } + initializedValue = true; + if (value != newVal) { + value_revision++; + } + value = newVal; + resetToBeRemovedFlag(); + } else { + Serial.print(F("[Configuration] Tried to override read-only configuration: ")); + printKey(); + Serial.println(); + } + return newVal; +} + +template +bool Configuration::isValid() { + return AbstractConfiguration::isValid(); +} + +template +size_t Configuration::getValueJsonCapacity() { + return JSON_OBJECT_SIZE(1); +} + +//template<> +//size_t Configuration::getValueJsonCapacity() { +// return JSON_OBJECT_SIZE(1) + value.length() + 1; +//} + +size_t Configuration::getValueJsonCapacity() { + return JSON_OBJECT_SIZE(1) + value_size; +} + +template +std::shared_ptr Configuration::toJsonStorageEntry() { + if (!isValid() || toBeRemoved()) { + return NULL; + } + size_t capacity = getStorageHeaderJsonCapacity() + + getValueJsonCapacity() + + JSON_OBJECT_SIZE(3); //type, header, value + + std::shared_ptr doc = std::make_shared(capacity); + JsonObject keyValuePair = doc->to(); + keyValuePair["type"] = SerializedType::get(); + storeStorageHeader(keyValuePair); + keyValuePair["value"] = value; + return doc; +} + +template +std::shared_ptr Configuration::toJsonOcppMsgEntry() { + if (!isValid() || toBeRemoved()) { + return NULL; + } + size_t capacity = getOcppMsgHeaderJsonCapacity() + + getValueJsonCapacity() + + JSON_OBJECT_SIZE(2); // header, value + + std::shared_ptr doc = std::make_shared(capacity); + JsonObject keyValuePair = doc->to(); + storeOcppMsgHeader(keyValuePair); + keyValuePair["value"] = value; + return doc; +} + +std::shared_ptr Configuration::toJsonStorageEntry() { + if (!isValid() || toBeRemoved()) { + return NULL; + } + size_t capacity = getStorageHeaderJsonCapacity() + + getValueJsonCapacity() + + JSON_OBJECT_SIZE(3); //type, header, value + + std::shared_ptr doc = std::make_shared(capacity); + JsonObject keyValuePair = doc->to(); + keyValuePair["type"] = SerializedType::get(); + storeStorageHeader(keyValuePair); + keyValuePair["value"] = value; + return doc; +} + +std::shared_ptr Configuration::toJsonOcppMsgEntry() { + if (!isValid() || toBeRemoved()) { + return NULL; + } + size_t capacity = getOcppMsgHeaderJsonCapacity() + + getValueJsonCapacity() + + JSON_OBJECT_SIZE(2); // header, value + + std::shared_ptr doc = std::make_shared(capacity); + JsonObject keyValuePair = doc->to(); + storeOcppMsgHeader(keyValuePair); + keyValuePair["value"] = value; + return doc; +} + +Configuration::Configuration(JsonObject storedKeyValuePair) : AbstractConfiguration(storedKeyValuePair) { + if (storedKeyValuePair["value"].as().is()) { + const char *storedValue = storedKeyValuePair["value"].as().as(); + if (storedValue) { + size_t storedValueSize = strlen(storedValue) + 1; + storedValueSize++; + setValue(storedValue, storedValueSize); + } else { + Serial.print(F("[Configuration] Stored value is empty\n")); + } + } else { + Serial.print(F("[Configuration] Type mismatch: cannot deserialize Json to given Type T\n")); + } +} + +Configuration::~Configuration() { + if (value != NULL) { + free(value); + value = NULL; + } + if (valueReadOnlyCopy != NULL) { + free(valueReadOnlyCopy); + valueReadOnlyCopy = NULL; + } +} + +bool Configuration::setValue(const char *new_value, size_t buffsize) { + if (!permissionLocalClientCanWrite() && initializedValue) { + Serial.print(F("[Configuration] Tried to override read-only configuration: ")); + printKey(); + Serial.println(); + return false; + } + + if (!new_value) { + Serial.print(F("[Configuration] in setValue(): Argument is null! No change\n")); + return false; + } + + //"ab" + //buffsize=5 + //a != 0 -> checkedBuffsize = 1 + //b != 0 -> checkedBuffsize = 2 + //0 == 0 -> checkedBuffsize = 3 + //break + size_t checkedBuffsize = 0; + for (size_t i = 0; i < buffsize; i++) { + if (new_value[i] != '\0') { + checkedBuffsize++; + } else { + checkedBuffsize++; + break; + } + } + + if (checkedBuffsize <= 0) { + Serial.print(F("[Configuration] in setValue(): buffsize is <= 0! No change\n")); + return false; + } + + if (checkedBuffsize > STRING_VAL_MAXLEN + 1) { + Serial.print(F("[Configuration] in setValue(): Maximal value length exceeded! Abort\n")); + return false; + } + + bool value_changed = true; + if (checkedBuffsize == value_size) { + value_changed = false; + for (size_t i = 0; i < value_size; i++) { + if (value[i] != new_value[i]) { + value_changed = true; + break; + } + } + + } + + if (value_changed || !initializedValue) { + + if (value != NULL) { + free(value); + value = NULL; + } + + if (valueReadOnlyCopy != NULL) { + free(valueReadOnlyCopy); + valueReadOnlyCopy = NULL; + } + + value_size = checkedBuffsize; + + value = (char *) malloc (sizeof(char) * value_size); + valueReadOnlyCopy = (char *) malloc (sizeof(char) * value_size); + if (!value || !valueReadOnlyCopy) { + Serial.print(F("[Configuration] in setValue(): Could not allocate value or value copy\n")); + + if (value != NULL) { + free(value); + value = NULL; + } + + if (valueReadOnlyCopy != NULL) { + free(valueReadOnlyCopy); + valueReadOnlyCopy = NULL; + } + + value_size = 0; + return false; + } + + strncpy(value, new_value, value_size); + strncpy(valueReadOnlyCopy, new_value, value_size); + + value[value_size-1] = '\0'; + valueReadOnlyCopy[value_size-1] = '\0'; + + value_revision++; + } + + if (DEBUG_OUT && !initializedValue) { + Serial.print(F("[Configuration] Initialized config object. Key = ")); + printKey(); + Serial.print(F(", value = ")); + Serial.println(value); + } + initializedValue = true; + resetToBeRemovedFlag(); + return true; +} + +const char *Configuration::operator=(const char *newVal) { + if (!setValue(newVal, strlen(newVal) + 1)) { + Serial.print(F("[Configuration] Setting value in operator= was unsuccessful!\n")); + } + return newVal; +} + +Configuration::operator const char*() { + strncpy(valueReadOnlyCopy, value, value_size); + return valueReadOnlyCopy; +} + +bool Configuration::isValid() { + return AbstractConfiguration::isValid() && value != NULL && valueReadOnlyCopy != NULL && value_size > 0; +} + +size_t Configuration::getBuffsize() { + return value_size; +} + +template class Configuration; +template class Configuration; +template class Configuration; + +} //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Core/ConfigurationKeyValue.h b/src/ArduinoOcpp/Core/ConfigurationKeyValue.h new file mode 100644 index 00000000..422654c0 --- /dev/null +++ b/src/ArduinoOcpp/Core/ConfigurationKeyValue.h @@ -0,0 +1,126 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef CONFIGURATIONKEYVALUE_H +#define CONFIGURATIONKEYVALUE_H + +#include +#include + +namespace ArduinoOcpp { + +class AbstractConfiguration { +private: + char *key = NULL; + size_t key_size = 0; // key=NULL --> key_size = 0; key = "" --> key_size = 1; key = "A" --> key_size = 2 + + bool rebootRequiredWhenChanged = false; + + bool remotePeerCanWrite = true; + bool remotePeerCanRead = true; + bool localClientCanWrite = true; +// const char *serializedAccessControl(); +// void loadFromSerializedAccessControl(const char *access); + + bool toBeRemovedFlag = false; +protected: + uint16_t value_revision = 0; //number of changes of subclass-member "value". This will be important for the client to detect if there was a change + bool initializedValue = false; + + AbstractConfiguration(); + AbstractConfiguration(JsonObject storedKeyValuePair); + size_t getStorageHeaderJsonCapacity(); + void storeStorageHeader(JsonObject keyValuePair); + size_t getOcppMsgHeaderJsonCapacity(); + void storeOcppMsgHeader(JsonObject keyValuePair); + bool isValid(); + + bool permissionLocalClientCanWrite() {return localClientCanWrite;} +public: + virtual ~AbstractConfiguration(); + bool setKey(const char *key); + void printKey(); + + void requireRebootWhenChanged(); + bool requiresRebootWhenChanged(); + bool toBeRemoved(); + void setToBeRemoved(); + void resetToBeRemovedFlag(); + + uint16_t getValueRevision(); + bool keyEquals(const char *other); + + virtual std::shared_ptr toJsonStorageEntry() = 0; + virtual std::shared_ptr toJsonOcppMsgEntry() = 0; + + virtual const char *getSerializedType() = 0; + + bool permissionRemotePeerCanWrite() {return remotePeerCanWrite;} + bool permissionRemotePeerCanRead() {return remotePeerCanRead;} + void revokePermissionRemotePeerCanWrite() {remotePeerCanWrite = false;} + void revokePermissionRemotePeerCanRead() {remotePeerCanRead = false;} + void revokePermissionLocalClientCanWrite() {localClientCanWrite = false;} +}; + + +template +struct SerializedType { + static const char *get() {return "undefined";} +}; + +template<> struct SerializedType {static const char *get() {return "int";}}; +template<> struct SerializedType {static const char *get() {return "float";}}; +template<> struct SerializedType {static const char *get() {return "string";}}; + +template +class Configuration : public AbstractConfiguration { +private: + T value; + size_t getValueJsonCapacity(); +public: + Configuration(); + Configuration(JsonObject storedKeyValuePair); + const T &operator=(const T & newVal); + operator T() { //TODO move to Configuration.cpp + if (!initializedValue) { + Serial.print(F("[Configuration] Tried to access value without preceeding initialization: ")); + printKey(); + Serial.println(); + } + return value; + } + bool isValid(); + + std::shared_ptr toJsonStorageEntry(); + std::shared_ptr toJsonOcppMsgEntry(); + + const char *getSerializedType() {return SerializedType::get();} //returns "int" or "float" as written to the configuration Json file +}; + +template <> +class Configuration : public AbstractConfiguration { +private: + char *value = NULL; + char *valueReadOnlyCopy = NULL; + size_t value_size = 0; + size_t getValueJsonCapacity(); +public: + Configuration(); + Configuration(JsonObject storedKeyValuePair); + ~Configuration(); + bool setValue(const char *newVal, size_t buffsize); + const char *operator=(const char *newVal); + operator const char*(); + bool isValid(); + size_t getBuffsize(); + + std::shared_ptr toJsonStorageEntry(); + std::shared_ptr toJsonOcppMsgEntry(); + + const char *getSerializedType() {return SerializedType::get();} +}; + +} //end namespace ArduinoOcpp + +#endif diff --git a/src/ArduinoOcpp/Core/ConfigurationOptions.h b/src/ArduinoOcpp/Core/ConfigurationOptions.h new file mode 100644 index 00000000..7406a551 --- /dev/null +++ b/src/ArduinoOcpp/Core/ConfigurationOptions.h @@ -0,0 +1,42 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef CONFIGURATIONOPTIONS_H +#define CONFIGURATIONOPTIONS_H + +namespace ArduinoOcpp { + +class FilesystemOpt{ +private: + bool use = false; + bool mount = false; + bool formatFsOnFail = false; +public: + enum Mode : uint8_t {Deactivate, Use, Use_Mount, Use_Mount_FormatOnFail}; + + FilesystemOpt() = default; + FilesystemOpt(Mode mode) { + switch (mode) { + case (FilesystemOpt::Use_Mount_FormatOnFail): + formatFsOnFail = true; + //fallthrough + case (FilesystemOpt::Use_Mount): + mount = true; + //fallthrough + case (FilesystemOpt::Use): + use = true; + break; + default: + break; + } + } + + bool accessAllowed() {return use;} + bool mustMount() {return mount;} + bool formatOnFail() {return formatFsOnFail;} +}; + +} //end namespace ArduinoOcpp + +#endif diff --git a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp index 121cf2a7..58977458 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -32,84 +32,112 @@ void ChangeConfiguration::processReq(JsonObject payload) { return; } - std::shared_ptr configuration = NULL; - - if (value.is()) { //Not enough. SteVe always sends numerical values as strings. Must also handle this - configuration = setConfiguration(key.c_str(), value.as()); - } else if (value.is()) { - configuration = setConfiguration(key.c_str(), value.as()); - } else if (value.is()) { //SteVe sends numerical values as strings. Check for that first - - const char *value_string = value.as(); - size_t value_buffsize = strlen(value_string) + 1; - - /* - * Try to interpret input as number - */ - - int nDigits = 0, nNonDigits = 0, nDots = 0, nSign = 0; //"-1.234" has 4 digits, 0 nonDigits, 1 dot and 1 sign. Don't allow comma as seperator. Don't allow e-expressions (e.g. 1.23e-7) - int numInt = 0; - float numFloat = 0.f; - float numFloatTranslate = 1.f; - for (size_t i = 0; i < value_buffsize - 1; i++) { //exclude terminating \0 - char c = value_string[i]; - if (c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7' || c == '8' || c == '9') { - nDigits++; - numInt *= 10; - numInt += c - '0'; - numFloat *= 10.f; - numFloat += (float) (c - '0'); - if (nDots != 0) { - numFloatTranslate *= 10.f; + std::shared_ptr configuration = getConfiguration(key.c_str()); + + if (configuration) { + if (!configuration->permissionRemotePeerCanWrite()) { + readOnly = true; + Serial.print(F("[ChangeConfiguration] Trying to override readonly value!\n")); + return; + } + + if (!strcmp(configuration->getSerializedType(), SerializedType::get()) && value.is()) { + std::shared_ptr> configurationConcrete = std::static_pointer_cast>(configuration); + *configurationConcrete = value.as(); + } else if (!strcmp(configuration->getSerializedType(), SerializedType::get()) && value.is()) { + std::shared_ptr> configurationConcrete = std::static_pointer_cast>(configuration); + *configurationConcrete = value.as(); + } else if (!strcmp(configuration->getSerializedType(), SerializedType::get()) && value.is()) { + std::shared_ptr> configurationConcrete = std::static_pointer_cast>(configuration); + *configurationConcrete = value.as(); +// } else if (!strcmp(configuration->getSerializedType(), SerializedType::get()) && value.is()) { +// std::shared_ptr> configurationConcrete = std::static_pointer_cast>(configuration); +// *configurationConcrete = value.as(); + } else { + err = true; + Serial.print(F("[ChangeConfiguration] Value has incompatible type!\n")); + return; + } + } else { + //configuration does not exist yet. Create new entry + + if (value.is()) { //Not enough. SteVe always sends numerical values as strings. Must also handle this + configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), value.as())); + } else if (value.is()) { + configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), value.as())); + } else if (value.is()) { //SteVe sends numerical values as strings. Check for that first + + const char *value_string = value.as(); + size_t value_buffsize = strlen(value_string) + 1; + + /* + * Try to interpret input as number + */ + + int nDigits = 0, nNonDigits = 0, nDots = 0, nSign = 0; //"-1.234" has 4 digits, 0 nonDigits, 1 dot and 1 sign. Don't allow comma as seperator. Don't allow e-expressions (e.g. 1.23e-7) + int numInt = 0; + float numFloat = 0.f; + float numFloatTranslate = 1.f; + for (size_t i = 0; i < value_buffsize - 1; i++) { //exclude terminating \0 + char c = value_string[i]; + if (c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7' || c == '8' || c == '9') { + nDigits++; + numInt *= 10; + numInt += c - '0'; + numFloat *= 10.f; + numFloat += (float) (c - '0'); + if (nDots != 0) { + numFloatTranslate *= 10.f; + } + } else if (c == '.') { + nDots++; + } else if (i == 0 && c == '-') { + nSign++; + } else { + nNonDigits++; } - } else if (c == '.') { - nDots++; - } else if (i == 0 && c == '-') { - nSign++; - } else { - nNonDigits++; } - } - int INT_MAXDIGITS; //plausibility check: this allows a numerical range of (-999,999,999 to 999,999,999), or (-9,999 to 9,999) respectively - if (sizeof(int) == 4UL) - INT_MAXDIGITS = 9; - else - INT_MAXDIGITS = 4; - - bool isString = false; - - if (nNonDigits == 0 && nDigits > 0 && nSign <= 1 && nDots == 0) { - //integer - if (nDigits > INT_MAXDIGITS) { - Serial.print(F("[ChangeConfiguration] Integer overflow! key = ")); - Serial.print(key.c_str()); - Serial.print(F(", value = ")); - Serial.println(value_string); - err = true; - return; - } else { + int INT_MAXDIGITS; //plausibility check: this allows a numerical range of (-999,999,999 to 999,999,999), or (-9,999 to 9,999) respectively + if (sizeof(int) == 4UL) + INT_MAXDIGITS = 9; + else + INT_MAXDIGITS = 4; + + bool isString = false; + + if (nNonDigits == 0 && nDigits > 0 && nSign <= 1 && nDots == 0) { + //integer + if (nDigits > INT_MAXDIGITS) { + Serial.print(F("[ChangeConfiguration] Integer overflow! key = ")); + Serial.print(key.c_str()); + Serial.print(F(", value = ")); + Serial.println(value_string); + err = true; + return; + } else { + if (nSign == 1) { + int neg = -numInt; + numInt = neg; + } + configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), numInt)); + } + } else if (nNonDigits == 0 && nDigits > 0 && nSign <= 1 && nDots == 1) { + //float + // no overflow check + numFloat /= numFloatTranslate; // "1." <-- numFlTrans = 1.f; "-1.234" <-- numFlTrans = 1000.f + if (nSign == 1) { - int neg = -numInt; - numInt = neg; + numFloat *= -1.f; } - configuration = setConfiguration(key.c_str(), numInt); - } - } else if (nNonDigits == 0 && nDigits > 0 && nSign <= 1 && nDots == 1) { - //float - // no overflow check - numFloat /= numFloatTranslate; // "1." <-- numFlTrans = 1.f; "-1.234" <-- numFlTrans = 1000.f - if (nSign == 1) { - numFloat *= -1.f; + configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), numFloat)); + } else { + //It's really a string + configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), value_string)); } - - configuration = setConfiguration(key.c_str(), numFloat); - } else { - //It's really a string - configuration = setConfiguration(key.c_str(), value_string, value_buffsize); } - } + } //end if (configuration) if (!configuration) { err = true; @@ -117,12 +145,6 @@ void ChangeConfiguration::processReq(JsonObject payload) { return; } - if (!configuration->hasWritePermission()) { - readOnly = true; - Serial.print(F("[ChangeConfiguration] Tried to set readOnly value. Ignore\n")); - return; - } - //success configuration_save(); //TODO encapsulate in AbstractConfiguration: will split configurations in multiple files. Only write back modified data diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp index d04fe007..66355d94 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -10,7 +10,7 @@ using ArduinoOcpp::Ocpp16::GetConfiguration; GetConfiguration::GetConfiguration() { - keys = LinkedList(); + } GetConfiguration::~GetConfiguration() { @@ -31,35 +31,34 @@ void GetConfiguration::processReq(JsonObject payload) { DynamicJsonDocument* GetConfiguration::createConf(){ - std::shared_ptr>> configurationKeys; - LinkedList unknownKeys = LinkedList(); + std::shared_ptr>> configurationKeys; + std::vector unknownKeys; if (keys.size() <= 0){ //return all existing keys configurationKeys = getAllConfigurations(); } else { //only return keys that were searched using the "key" parameter - configurationKeys = std::make_shared>>(); for (int i = 0; i < keys.size(); i++) { std::shared_ptr entry = getConfiguration(keys.get(i).c_str()); if (entry) - configurationKeys->add(entry); + configurationKeys->push_back(entry); else - unknownKeys.add(keys.get(i).c_str()); + unknownKeys.push_back(keys.get(i).c_str()); } } size_t capacity = 0; - LinkedList> configurationKeysJson = LinkedList>(); + std::vector> configurationKeysJson; - for (int i = 0; i < configurationKeys->size(); i++) { - std::shared_ptr entry = configurationKeys->get(i)->toJsonOcppMsgEntry(); + for (auto confKey = configurationKeys->begin(); confKey != configurationKeys->end(); confKey++) { + std::shared_ptr entry = (*confKey)->toJsonOcppMsgEntry(); if (entry) { - configurationKeysJson.add(entry); + configurationKeysJson.push_back(entry); capacity += entry->capacity(); } } - for (int i = 0; i < unknownKeys.size(); i++) { - capacity += unknownKeys.get(i).length() + 1; + for (auto unknownKey = unknownKeys.begin(); unknownKey != unknownKeys.end(); unknownKey++) { + capacity += unknownKey->length() + 1; } capacity += JSON_OBJECT_SIZE(2) //configurationKey, unknownKey @@ -72,15 +71,15 @@ DynamicJsonDocument* GetConfiguration::createConf(){ JsonArray jsonConfigurationKey = payload.createNestedArray("configurationKey"); for (int i = 0; i < configurationKeys->size(); i++) { - jsonConfigurationKey.add(configurationKeysJson.get(i)->as()); + jsonConfigurationKey.add(configurationKeysJson.at(i)->as()); } if (unknownKeys.size() > 0) { if (DEBUG_OUT) Serial.print(F("My unknown keys are: \n")); JsonArray jsonUnknownKey = payload.createNestedArray("unknownKey"); - for (int i = 0; i < unknownKeys.size(); i++) { - if (DEBUG_OUT) Serial.println(unknownKeys.get(i)); - jsonUnknownKey.add(unknownKeys.get(i)); + for (auto unknownKey = unknownKeys.begin(); unknownKey != unknownKeys.end(); unknownKey++) { + if (DEBUG_OUT) Serial.println(*unknownKey); + jsonUnknownKey.add(*unknownKey); } } diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 798ee1e0..3ef2ab92 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -13,168 +13,132 @@ using namespace ArduinoOcpp::Ocpp16; ConnectorStatus::ConnectorStatus(int connectorId) : connectorId(connectorId) { - //Set default transaction ID in memory - String key = "OCPP_STATE_TRANSACTION_ID_CONNECTOR_"; - String id = String(connectorId, DEC); - key += id; - transactionId = declareConfiguration(key.c_str(), -1); - if (!transactionId) { - Serial.print(F("[ConnectorStatus] Error! Cannot declare transactionId\n")); - } - //if (!declareConfiguration(key.c_str(), transactionId)) { - // Serial.print(F("[ConnectorStatus] Warning: cannot set default transaction ID to -1 in configurations!\n")); - //} + //Set default transaction ID in memory + String key = "OCPP_STATE_TRANSACTION_ID_CONNECTOR_"; + String id = String(connectorId, DEC); + key += id; + transactionId = declareConfiguration(key.c_str(), -1, CONFIGURATION_FN, false, false, true, false); + if (!transactionId) { + Serial.print(F("[ConnectorStatus] Error! Cannot declare transactionId\n")); + } } OcppEvseState ConnectorStatus::inferenceStatus() { - /* - * Handle special case: This is the ConnectorStatus for the whole CP (i.e. connectorId=0) --> only states Available, Unavailable, Faulted are possible - */ - if (connectorId == 0) { - return OcppEvseState::Available; //no support for Unavailable or Faulted at the moment - } - - if (!authorized && !getChargePointStatusService()->existsUnboundAuthorization()) { - return OcppEvseState::Available; - } else if (!transactionRunning) { - return OcppEvseState::Preparing; - } else { - //Transaction is currently running - if (!evDrawsEnergy) { - return OcppEvseState::SuspendedEV; + /* + * Handle special case: This is the ConnectorStatus for the whole CP (i.e. connectorId=0) --> only states Available, Unavailable, Faulted are possible + */ + if (connectorId == 0) { + return OcppEvseState::Available; //no support for Unavailable or Faulted at the moment } - if (!evseOffersEnergy) { - return OcppEvseState::SuspendedEVSE; + + if (!authorized && !getChargePointStatusService()->existsUnboundAuthorization()) { + return OcppEvseState::Available; + } else if (!transactionRunning) { + return OcppEvseState::Preparing; + } else { + //Transaction is currently running + if (!evDrawsEnergy) { + return OcppEvseState::SuspendedEV; + } + if (!evseOffersEnergy) { + return OcppEvseState::SuspendedEVSE; + } + return OcppEvseState::Charging; } - return OcppEvseState::Charging; - } } StatusNotification *ConnectorStatus::loop() { - OcppEvseState inferencedStatus = inferenceStatus(); - - if (inferencedStatus != currentStatus) { - currentStatus = inferencedStatus; - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Status changed\n")); - - //fire StatusNotification - //TODO check for online condition: Only inform CS about status change if CP is online - //TODO check for too short duration condition: Only inform CS about status change if it lasted for longer than MinimumStatusDuration - return new StatusNotification(connectorId, currentStatus); - } - - return NULL; + OcppEvseState inferencedStatus = inferenceStatus(); + + if (inferencedStatus != currentStatus) { + currentStatus = inferencedStatus; + if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Status changed\n")); + + //fire StatusNotification + //TODO check for online condition: Only inform CS about status change if CP is online + //TODO check for too short duration condition: Only inform CS about status change if it lasted for longer than MinimumStatusDuration + return new StatusNotification(connectorId, currentStatus); + } + + return NULL; } void ConnectorStatus::authorize(String &idTag){ - this->idTag = String(idTag); - authorize(); + this->idTag = String(idTag); + authorize(); } void ConnectorStatus::authorize(){ - if (authorized == true){ - if (DEBUG_OUT) Serial.print(F("[ConnectorStatus] Warning: authorized twice or didn't unauthorize before\n")); - } - authorized = true; -} - -/*boolean ConnectorStatus::requestAuthorization() { - ChargePointStatusService *cpss = getChargePointStatusService(); - if (cpss->existsUnboundAuthorization()) { - //authorize Transaction - this->idTag = String(cpss->getUnboundIdTag()); - authorized = true; - cpss->bindAuthorization(this->idTag, connectorId); - return true; - } else { - return false; + if (authorized == true){ + if (DEBUG_OUT) Serial.print(F("[ConnectorStatus] Warning: authorized twice or didn't unauthorize before\n")); } -}*/ + authorized = true; +} String &ConnectorStatus::getIdTag() { - return idTag; + return idTag; } void ConnectorStatus::unauthorize(){ - if (authorized == false){ - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: unauthorized twice or didn't authorize before\n")); - } - authorized = false; - idTag = String('\0'); + if (authorized == false){ + if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: unauthorized twice or didn't authorize before\n")); + } + authorized = false; + idTag = String('\0'); } void ConnectorStatus::startTransaction(int transId){ - if (transactionRunning == true){ - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: started transaction twice or didn't stop transaction before\n")); - } - *transactionId = transId; - transactionRunning = true; + if (transactionRunning == true){ + if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: started transaction twice or didn't stop transaction before\n")); + } + *transactionId = transId; + transactionRunning = true; - saveState(); + saveState(); } void ConnectorStatus::stopTransaction(){ - if (transactionRunning == false){ - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: stopped transaction twice or didn't start transaction before\n")); - } - transactionRunning = false; - *transactionId = -1; + if (transactionRunning == false){ + if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: stopped transaction twice or didn't start transaction before\n")); + } + transactionRunning = false; + *transactionId = -1; - saveState(); + saveState(); } int ConnectorStatus::getTransactionId() { - return *transactionId; + return *transactionId; } void ConnectorStatus::startEvDrawsEnergy(){ - if (evDrawsEnergy == true){ - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: startEvDrawsEnergy called twice or didn't call stopEvDrawsEnergy before\n")); - } - evDrawsEnergy = true; + if (evDrawsEnergy == true){ + if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: startEvDrawsEnergy called twice or didn't call stopEvDrawsEnergy before\n")); + } + evDrawsEnergy = true; } void ConnectorStatus::stopEvDrawsEnergy(){ - if (evDrawsEnergy == false){ - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: stopEvDrawsEnergy called twice or didn't call startEvDrawsEnergy before\n")); - } - evDrawsEnergy = false; + if (evDrawsEnergy == false){ + if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: stopEvDrawsEnergy called twice or didn't call startEvDrawsEnergy before\n")); + } + evDrawsEnergy = false; } void ConnectorStatus::startEnergyOffer(){ - if (evseOffersEnergy == true){ - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: startEnergyOffer called twice or didn't call stopEnergyOffer before\n")); - } - evseOffersEnergy = true; + if (evseOffersEnergy == true){ + if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: startEnergyOffer called twice or didn't call stopEnergyOffer before\n")); + } + evseOffersEnergy = true; } void ConnectorStatus::stopEnergyOffer(){ - if (evseOffersEnergy == false){ - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: stopEnergyOffer called twice or didn't call startEnergyOffer before\n")); - } - evseOffersEnergy = false; + if (evseOffersEnergy == false){ + if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: stopEnergyOffer called twice or didn't call startEnergyOffer before\n")); + } + evseOffersEnergy = false; } void ConnectorStatus::saveState() { - //String key = "OCPP_STATE_TRANSACTION_ID_CONNECTOR_"; - //String id = String(connectorId, DEC); - //key += id; - //if (setConfiguration_Int(key.c_str(), transactionId)) { - // configuration_save(); - //} else { - // Serial.print(F("[ConnectorStatus] Warning: cannot save transaction ID in flash memory!\n")); - //} configuration_save(); } - -/* -void ConnectorStatus::recoverState() { - String key = "OCPP_STATE_TRANSACTION_ID_CONNECTOR_"; - String id = String(connectorId, DEC); - key += id; - int val = 0; - if (getConfiguration_Int(key.c_str(), &val)) { - transactionId = val; - } -} -*/ diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index 3ba06f26..ea21fdb6 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -26,8 +26,8 @@ using namespace::ArduinoOcpp; -SmartChargingService::SmartChargingService(float chargeLimit, int numConnectors) - : DEFAULT_CHARGE_LIMIT(chargeLimit) { +SmartChargingService::SmartChargingService(float chargeLimit, int numConnectors, FilesystemOpt filesystemOpt) + : DEFAULT_CHARGE_LIMIT(chargeLimit), filesystemOpt(filesystemOpt) { if (numConnectors > 2) { Serial.print(F("[SmartChargingService] Error: Unfortunately, multiple connectors are not implemented in SmartChargingService yet. Only connector 1 will receive charging limits\n")); @@ -44,7 +44,7 @@ SmartChargingService::SmartChargingService(float chargeLimit, int numConnectors) TxProfile[i] = NULL; } setSmartChargingService(this); //in OcppEngine.cpp - declareConfiguration("ChargeProfileMaxStackLevel", CHARGEPROFILEMAXSTACKLEVEL, false); + declareConfiguration("ChargeProfileMaxStackLevel", CHARGEPROFILEMAXSTACKLEVEL, CONFIGURATION_FN, false, true, false, true); loadProfiles(); } @@ -242,15 +242,16 @@ ChargingProfile *SmartChargingService::updateProfileStack(JsonObject *json){ ChargingProfile **profilePurposeStack; //select which stack this profile belongs to due to its purpose switch (chargingProfile->getChargingProfilePurpose()) { - case (ChargingProfilePurposeType::ChargePointMaxProfile): - profilePurposeStack = ChargePointMaxProfile; - break; case (ChargingProfilePurposeType::TxDefaultProfile): profilePurposeStack = TxDefaultProfile; break; case (ChargingProfilePurposeType::TxProfile): profilePurposeStack = TxProfile; break; + default: + //case (ChargingProfilePurposeType::ChargePointMaxProfile): + profilePurposeStack = ChargePointMaxProfile; + break; } if (profilePurposeStack[stackLevel] != NULL){ @@ -271,6 +272,11 @@ ChargingProfile *SmartChargingService::updateProfileStack(JsonObject *json){ bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile *chargingProfile) { #ifndef AO_DEACTIVATE_FLASH + if (!filesystemOpt.accessAllowed()) { + if (DEBUG_OUT) Serial.println(F("[SmartChargingService] Prohibit access to FS")); + return true; + } + String profileFN = PROFILE_FN_PREFIX; switch (chargingProfile->getChargingProfilePurpose()) { @@ -326,6 +332,11 @@ bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile bool SmartChargingService::loadProfiles() { #ifndef AO_DEACTIVATE_FLASH + if (!filesystemOpt.accessAllowed()) { + if (DEBUG_OUT) Serial.println(F("[SmartChargingService] Prohibit access to FS")); + return true; + } + const int N_PURPOSES = 3; ChargingProfilePurposeType purposes[N_PURPOSES] = {ChargingProfilePurposeType::ChargePointMaxProfile, ChargingProfilePurposeType::TxDefaultProfile, ChargingProfilePurposeType::TxProfile}; diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h index 1b207ac8..1fa07b79 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -12,6 +12,7 @@ #include #include +#include namespace ArduinoOcpp { @@ -32,11 +33,12 @@ class SmartChargingService { bool chargingSessionIsActive; ChargingProfile *updateProfileStack(JsonObject *json); + FilesystemOpt filesystemOpt; bool writeProfileToFlash(JsonObject *json, ChargingProfile *chargingProfile); bool loadProfiles(); public: - SmartChargingService(float chargeLimit, int numConnectors); + SmartChargingService(float chargeLimit, int numConnectors, FilesystemOpt filesystemOpt = FilesystemOpt::Use_Mount_FormatOnFail); void beginCharging(time_t t, int transactionID); void beginChargingNow(); void endChargingNow(); From cff065cc381026a23d66df575b2036e84f8ed184 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 22 May 2021 13:34:56 +0200 Subject: [PATCH 021/696] fix read key from FS --- platformio.ini | 8 ++++++-- src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/platformio.ini b/platformio.ini index 8c9ae48b..917d6e3b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -35,15 +35,19 @@ build_flags = -D TRAFFIC_OUT=true [env:esp32-development-board] -platform = espressif32 +platform = espressif32@1.11.1 board = esp-wrover-kit framework = ${common.framework} -lib_deps = ${common.lib_deps} +lib_deps = + ${common.lib_deps} + lorol/LittleFS_esp32@1.0.5 monitor_speed = ${common.monitor_speed} build_flags = ${common.build_flags} -D DEBUG_OUT=true -D TRAFFIC_OUT=true + -DCONFIG_LITTLEFS_FOR_IDF_3_2 +build_partitions = min_spiffs.csv upload_speed = 921600 ; diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp index 66355d94..99c3ae86 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp @@ -10,7 +10,7 @@ using ArduinoOcpp::Ocpp16::GetConfiguration; GetConfiguration::GetConfiguration() { - + keys = LinkedList(); } GetConfiguration::~GetConfiguration() { From a7628263951197524194e19a2ee9d9268ff5d481 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 22 May 2021 13:58:17 +0200 Subject: [PATCH 022/696] fix keys null pointer --- src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp index 99c3ae86..6123e01e 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp @@ -37,6 +37,7 @@ DynamicJsonDocument* GetConfiguration::createConf(){ if (keys.size() <= 0){ //return all existing keys configurationKeys = getAllConfigurations(); } else { //only return keys that were searched using the "key" parameter + configurationKeys = std::make_shared>>(); for (int i = 0; i < keys.size(); i++) { std::shared_ptr entry = getConfiguration(keys.get(i).c_str()); if (entry) From f25a91b7dc3868baa33821a36ded3e895bcd8b64 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 22 May 2021 16:08:08 +0200 Subject: [PATCH 023/696] ChangeConfiguration type safe parsing --- .../MessagesV16/ChangeConfiguration.cpp | 192 ++++++++++-------- .../MessagesV16/ChangeConfiguration.h | 2 +- .../MessagesV16/GetConfiguration.h | 2 +- 3 files changed, 112 insertions(+), 84 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp index 58977458..9eef3135 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp @@ -32,6 +32,100 @@ void ChangeConfiguration::processReq(JsonObject payload) { return; } + /* + * Parse value + */ + bool isInt = false; + int numInt = -1; + bool isFloat = false; + float numFloat = -1.0f; + bool isString = false; + const char *value_string = '\0'; + + if (value.is()) { //Not enough. SteVe always sends numerical values as strings. Must also handle this + isInt = true; + numInt = value.as(); + //configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), value.as())); + } else if (value.is()) { + isFloat = true; + numFloat = value.as(); + //configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), value.as())); + } else if (value.is()) { //SteVe sends numerical values as strings. Check for that first + isString = true; + value_string = value.as(); + size_t value_buffsize = strlen(value_string) + 1; + + /* + * Try to interpret input as number + */ + + int nDigits = 0, nNonDigits = 0, nDots = 0, nSign = 0; //"-1.234" has 4 digits, 0 nonDigits, 1 dot and 1 sign. Don't allow comma as seperator. Don't allow e-expressions (e.g. 1.23e-7) + numInt = 0; + numFloat = 0.f; + float numFloatTranslate = 1.f; + for (size_t i = 0; i < value_buffsize - 1; i++) { //exclude terminating \0 + char c = value_string[i]; + if (c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7' || c == '8' || c == '9') { + nDigits++; + numInt *= 10; + numInt += c - '0'; + numFloat *= 10.f; + numFloat += (float) (c - '0'); + if (nDots != 0) { + numFloatTranslate *= 10.f; + } + } else if (c == '.') { + nDots++; + } else if (i == 0 && c == '-') { + nSign++; + } else { + nNonDigits++; + } + } + + int INT_MAXDIGITS; //plausibility check: this allows a numerical range of (-999,999,999 to 999,999,999), or (-9,999 to 9,999) respectively + if (sizeof(int) >= 4UL) + INT_MAXDIGITS = 9; + else + INT_MAXDIGITS = 4; + + //bool isString = false; + + if (nNonDigits == 0 && nDigits > 0 && nSign <= 1 && nDots == 0) { + //integer + if (nDigits > INT_MAXDIGITS) { + Serial.print(F("[ChangeConfiguration] Integer overflow! key = ")); + Serial.print(key.c_str()); + Serial.print(F(", value = ")); + Serial.println(value_string); + err = true; + return; + } else { + if (nSign == 1) { + int neg = -numInt; + numInt = neg; + } + //configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), numInt)); + isInt = true; + } + } else if (nNonDigits == 0 && nDigits > 0 && nSign <= 1 && nDots == 1) { + //float + // no overflow check + numFloat /= numFloatTranslate; // "1." <-- numFlTrans = 1.f; "-1.234" <-- numFlTrans = 1000.f + + if (nSign == 1) { + numFloat *= -1.f; + } + + //configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), numFloat)); + isFloat = true; + } else { + //only string + isString = true; + //configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), value_string)); + } + } + std::shared_ptr configuration = getConfiguration(key.c_str()); if (configuration) { @@ -41,15 +135,15 @@ void ChangeConfiguration::processReq(JsonObject payload) { return; } - if (!strcmp(configuration->getSerializedType(), SerializedType::get()) && value.is()) { + if (!strcmp(configuration->getSerializedType(), SerializedType::get()) && isInt) { std::shared_ptr> configurationConcrete = std::static_pointer_cast>(configuration); - *configurationConcrete = value.as(); - } else if (!strcmp(configuration->getSerializedType(), SerializedType::get()) && value.is()) { + *configurationConcrete = numInt; + } else if (!strcmp(configuration->getSerializedType(), SerializedType::get()) && isFloat) { std::shared_ptr> configurationConcrete = std::static_pointer_cast>(configuration); - *configurationConcrete = value.as(); - } else if (!strcmp(configuration->getSerializedType(), SerializedType::get()) && value.is()) { + *configurationConcrete = numFloat; + } else if (!strcmp(configuration->getSerializedType(), SerializedType::get()) && isString) { std::shared_ptr> configurationConcrete = std::static_pointer_cast>(configuration); - *configurationConcrete = value.as(); + *configurationConcrete = value_string; // } else if (!strcmp(configuration->getSerializedType(), SerializedType::get()) && value.is()) { // std::shared_ptr> configurationConcrete = std::static_pointer_cast>(configuration); // *configurationConcrete = value.as(); @@ -60,82 +154,16 @@ void ChangeConfiguration::processReq(JsonObject payload) { } } else { //configuration does not exist yet. Create new entry - - if (value.is()) { //Not enough. SteVe always sends numerical values as strings. Must also handle this - configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), value.as())); - } else if (value.is()) { - configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), value.as())); - } else if (value.is()) { //SteVe sends numerical values as strings. Check for that first - - const char *value_string = value.as(); - size_t value_buffsize = strlen(value_string) + 1; - - /* - * Try to interpret input as number - */ - - int nDigits = 0, nNonDigits = 0, nDots = 0, nSign = 0; //"-1.234" has 4 digits, 0 nonDigits, 1 dot and 1 sign. Don't allow comma as seperator. Don't allow e-expressions (e.g. 1.23e-7) - int numInt = 0; - float numFloat = 0.f; - float numFloatTranslate = 1.f; - for (size_t i = 0; i < value_buffsize - 1; i++) { //exclude terminating \0 - char c = value_string[i]; - if (c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7' || c == '8' || c == '9') { - nDigits++; - numInt *= 10; - numInt += c - '0'; - numFloat *= 10.f; - numFloat += (float) (c - '0'); - if (nDots != 0) { - numFloatTranslate *= 10.f; - } - } else if (c == '.') { - nDots++; - } else if (i == 0 && c == '-') { - nSign++; - } else { - nNonDigits++; - } - } - - int INT_MAXDIGITS; //plausibility check: this allows a numerical range of (-999,999,999 to 999,999,999), or (-9,999 to 9,999) respectively - if (sizeof(int) == 4UL) - INT_MAXDIGITS = 9; - else - INT_MAXDIGITS = 4; - - bool isString = false; - - if (nNonDigits == 0 && nDigits > 0 && nSign <= 1 && nDots == 0) { - //integer - if (nDigits > INT_MAXDIGITS) { - Serial.print(F("[ChangeConfiguration] Integer overflow! key = ")); - Serial.print(key.c_str()); - Serial.print(F(", value = ")); - Serial.println(value_string); - err = true; - return; - } else { - if (nSign == 1) { - int neg = -numInt; - numInt = neg; - } - configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), numInt)); - } - } else if (nNonDigits == 0 && nDigits > 0 && nSign <= 1 && nDots == 1) { - //float - // no overflow check - numFloat /= numFloatTranslate; // "1." <-- numFlTrans = 1.f; "-1.234" <-- numFlTrans = 1000.f - - if (nSign == 1) { - numFloat *= -1.f; - } - - configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), numFloat)); - } else { - //It's really a string - configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), value_string)); - } + if (isInt) { + configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), numInt)); + } else if (isFloat) { + configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), numFloat)); + } else if (isString) { + configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), value_string)); + } else { + err = true; + Serial.print(F("[ChangeConfiguration] Could not parse value!\n")); + return; } } //end if (configuration) diff --git a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h index f61ab0e6..68c023e8 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h +++ b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.h b/src/ArduinoOcpp/MessagesV16/GetConfiguration.h index dcd9a656..28d31a95 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.h +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License From c96ab0a37453ca9eefb3700a3b0eb4eeb17c09a3 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 25 May 2021 13:47:16 +0200 Subject: [PATCH 024/696] change A to kW --- src/ArduinoOcpp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index cc2559ab..e24d06e3 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -138,7 +138,7 @@ void OCPP_initialize(OcppSocket *ocppSocket, ArduinoOcpp::FilesystemOpt fsOpt) { ocppEngine_initialize(ocppSocket); - smartChargingService = new SmartChargingService(16.0f, OCPP_NUMCONNECTORS, fsOpt); //default charging limit: 16A + smartChargingService = new SmartChargingService(11000.0f, OCPP_NUMCONNECTORS, fsOpt); //default charging limit: 11kW chargePointStatusService = new ChargePointStatusService(&webSocket, OCPP_NUMCONNECTORS); //Constructor adds instance to ocppEngine in constructor meteringService = new MeteringService(&webSocket, OCPP_NUMCONNECTORS); From 86dec107d20648f717321c15dd1e7c48739b8eab Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 28 May 2021 17:39:20 +0200 Subject: [PATCH 025/696] added own time keeping --- src/ArduinoOcpp/Core/OcppTime.cpp | 197 ++++++++++++++++++++++++++++++ src/ArduinoOcpp/Core/OcppTime.h | 93 ++++++++++++++ 2 files changed, 290 insertions(+) create mode 100644 src/ArduinoOcpp/Core/OcppTime.cpp create mode 100644 src/ArduinoOcpp/Core/OcppTime.h diff --git a/src/ArduinoOcpp/Core/OcppTime.cpp b/src/ArduinoOcpp/Core/OcppTime.cpp new file mode 100644 index 00000000..598fc38c --- /dev/null +++ b/src/ArduinoOcpp/Core/OcppTime.cpp @@ -0,0 +1,197 @@ +// matth-x/ESP8266-OCPP +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include + +namespace ArduinoOcpp { + +OcppTimestamp::OcppTimestamp() { + +} + +int noDays(int month, int year) { + return (month == 0 || month == 2 || month == 4 || month == 6 || month == 7 || month == 9 || month == 11) ? 31 : + ((month == 3 || month == 5 || month == 8 || month == 10) ? 30 : + ((year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? 29 : 28)); +} + +bool OcppTimestamp::setTime(const char *jsonDateString) { + + const int JSONDATE_MINLENGTH = 19; + + if (strlen(jsonDateString) < JSONDATE_MINLENGTH){ + return false; + } + + if (!isdigit(jsonDateString[0]) || //2 + !isdigit(jsonDateString[1]) || //0 + !isdigit(jsonDateString[2]) || //1 + !isdigit(jsonDateString[3]) || //3 + jsonDateString[4] != '-' || //- + !isdigit(jsonDateString[5]) || //0 + !isdigit(jsonDateString[6]) || //2 + jsonDateString[7] != '-' || //- + !isdigit(jsonDateString[8]) || //0 + !isdigit(jsonDateString[9]) || //1 + jsonDateString[10] != 'T' || //T + !isdigit(jsonDateString[11]) || //2 + !isdigit(jsonDateString[12]) || //0 + jsonDateString[13] != ':' || //: + !isdigit(jsonDateString[14]) || //5 + !isdigit(jsonDateString[15]) || //3 + jsonDateString[16] != ':' || //: + !isdigit(jsonDateString[17]) || //3 + !isdigit(jsonDateString[18])) { //2 + //ignore subsequent characters + return false; + } + + int year = (jsonDateString[0] - '0') * 1000 + + (jsonDateString[1] - '0') * 100 + + (jsonDateString[2] - '0') * 10 + + (jsonDateString[3] - '0'); + int month = (jsonDateString[5] - '0') * 10 + + (jsonDateString[6] - '0') - 1; + int day = (jsonDateString[8] - '0') * 10 + + (jsonDateString[9] - '0') - 1; + int hour = (jsonDateString[11] - '0') * 10 + + (jsonDateString[12] - '0'); + int minute = (jsonDateString[14] - '0') * 10 + + (jsonDateString[15] - '0'); + int second = (jsonDateString[17] - '0') * 10 + + (jsonDateString[18] - '0'); + //ignore fractals + + if (year < 1970 || year >= 2038 || + month < 0 || month >= 12 || + day < 0 || day >= noDays(month, year) || + hour < 0 || hour >= 24 || + minute < 0 || minute >= 60 || + second < 0 || second > 60) { //tolerate leap seconds -- (23:59:60) can be a valid time + return false; + } + + this->year = year; + this->month = month; + this->day = day; + this->hour = hour; + this->minute = minute; + this->second = second; + + return true; +} + +bool OcppTimestamp::toJsonString(char *jsonDateString, size_t buffsize) { + if (buffsize < OCPP_JSONDATE_LENGTH + 1) return false; + + jsonDateString[0] = ((char) ((year / 1000) % 10)) + '0'; + jsonDateString[1] = ((char) ((year / 100) % 10)) + '0'; + jsonDateString[2] = ((char) ((year / 10) % 10)) + '0'; + jsonDateString[3] = ((char) ((year / 1) % 10)) + '0'; + jsonDateString[4] = '-'; + jsonDateString[5] = ((char) ((month / 10) % 10)) + '0'; + jsonDateString[6] = ((char) ((month / 1) % 10)) + '0'; + jsonDateString[7] = '-'; + jsonDateString[8] = ((char) ((day / 10) % 10)) + '0'; + jsonDateString[9] = ((char) ((day / 1) % 10)) + '0'; + jsonDateString[10] = 'T'; + jsonDateString[11] = ((char) ((hour / 10) % 10)) + '0'; + jsonDateString[12] = ((char) ((hour / 1) % 10)) + '0'; + jsonDateString[13] = ':'; + jsonDateString[14] = ((char) ((minute / 10) % 10)) + '0'; + jsonDateString[15] = ((char) ((minute / 1) % 10)) + '0'; + jsonDateString[16] = ':'; + jsonDateString[17] = ((char) ((second / 10) % 10)) + '0'; + jsonDateString[18] = ((char) ((second / 1) % 10)) + '0'; + jsonDateString[19] = '.'; + jsonDateString[20] = '0'; //ignore fractals + jsonDateString[21] = '0'; + jsonDateString[22] = '0'; + jsonDateString[23] = 'Z'; + + return true; +} + +OcppTimestamp OcppTimestamp::operator+(int secs) { + OcppTimestamp res = *this; + res.second += secs; + + if (res.second >= 0 && res.second < 60) return res; + + res.minute += res.second / 60; + res.second %= 60; + if (res.second < 0) { + res.minute--; + res.second += 60; + } + + if (res.minute >= 0 && res.minute < 60) return res; + + res.hour += res.minute / 60; + res.minute %= 60; + if (res.minute < 0) { + res.hour--; + res.minute += 60; + } + + if (res.hour >= 0 && res.hour < 24) return res; + + res.day += res.hour / 24; + res.hour %= 24; + if (res.hour < 0) { + res.day--; + res.hour += 24; + } + + while (res.day >= noDays(res.month, res.year)) { + res.day -= noDays(res.month, res.year); + res.month++; + + if (res.month >= 12) { + res.month -= 12; + res.year++; + } + } + + while (res.day < 0) { + res.month--; + if (res.month < 0) { + res.month += 12; + res.year--; + } + res.day += noDays(res.month, res.year); + } + + return res; +}; + +OcppTimestamp OcppTimestamp::operator-(int secs) { + return this->operator+(-secs); +} + +bool OcppTime::setOcppTime(const char* jsonDateString) { + + OcppTimestamp timestamp = OcppTimestamp(); + + if (!timestamp.setTime(jsonDateString)) { + return false; + } + + system_basetime = system_clock(); + ocpp_basetime = timestamp; + + return true; +} + +otime_t OcppTime::getOcppTimeScalar() { + return system_clock(); +} + +OcppTimestamp OcppTime::createTimestamp(otime_t scalar) { + OcppTimestamp res = ocpp_basetime + (scalar - system_basetime); + + return res; +} + +} diff --git a/src/ArduinoOcpp/Core/OcppTime.h b/src/ArduinoOcpp/Core/OcppTime.h new file mode 100644 index 00000000..07c4d634 --- /dev/null +++ b/src/ArduinoOcpp/Core/OcppTime.h @@ -0,0 +1,93 @@ +// matth-x/ESP8266-OCPP +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef OCPPTIME_H +#define OCPPTIME_H + +#include + +#include + +#define OCPP_JSONDATE_LENGTH 24 + +namespace ArduinoOcpp { + +typedef int32_t otime_t; //requires 32bit signed integer or bigger + +class OcppTimestamp { +private: + /* + * Internal representation of the current time. The initial values correspond to UNIX-time 0. January + * corresponds to month 0 and the first day in the month is day 0. + */ + int16_t year = 1970; + int16_t month = 0; + int16_t day = 0; + int32_t hour = 0; + int32_t minute = 0; + int32_t second = 0; + +public: + + OcppTimestamp(); + + /** + * Expects a date string like + * 2020-10-01T20:53:32.486Z + * + * as generated in JavaScript by calling toJSON() on a Date object + * + * Only processes the first 19 characters. The subsequent are ignored until terminating 0. + * + * Has a semi-sophisticated type check included. Will return true on successful time set and false if + * the given string is not a JSON Date string. + * + * jsonDateString: 0-terminated string + */ + bool setTime(const char* jsonDateString); + + bool toJsonString(char *out, size_t buffsize); + + OcppTimestamp operator+(int secs); + + OcppTimestamp operator-(int secs); + +}; + +class OcppTime { +private: + + OcppTimestamp ocpp_basetime; + otime_t system_basetime = 0; //your system clock's time at the moment when the OCPP server's time was taken + + std::function system_clock = [] () {return (otime_t) 0;}; + +public: + + OcppTime(std::function system_clock); + + otime_t getOcppTimeScalar(); //returns current time of the OCPP server in non-UNIX but signed integer format. t2 - t1 is the time difference in seconds. + OcppTimestamp createTimestamp(otime_t scalar); //creates a timestamp in a JSON-serializable format. createTimestamp(getOcppTimeScalar()) will return the current OCPP time + + /** + * Expects a date string like + * 2020-10-01T20:53:32.486Z + * + * as generated in JavaScript by calling toJSON() on a Date object + * + * Only processes the first 19 characters. The subsequent are ignored until terminating 0. + * + * Has a semi-sophisticated type check included. Will return true on successful time set and false if + * the given string is not a JSON Date string. + * + * jsonDateString: 0-terminated string + */ + bool setOcppTime(const char* jsonDateString); + + +}; + +} + +#endif From d5ff84ec1d48e2cda2027c5cfdfd6ad35103226f Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 29 May 2021 10:25:55 +0200 Subject: [PATCH 026/696] distribute OCPP time to modules --- src/ArduinoOcpp/Core/OcppTime.cpp | 24 ++++++++++++++++++- src/ArduinoOcpp/Core/OcppTime.h | 15 +++++++++--- .../Metering/ConnectorMeterValuesRecorder.cpp | 6 ++--- .../Metering/ConnectorMeterValuesRecorder.h | 6 +++-- .../Tasks/Metering/MeteringService.cpp | 6 ++--- .../Tasks/Metering/MeteringService.h | 4 ++-- .../SmartCharging/SmartChargingService.cpp | 4 ++-- .../SmartCharging/SmartChargingService.h | 4 +++- 8 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/ArduinoOcpp/Core/OcppTime.cpp b/src/ArduinoOcpp/Core/OcppTime.cpp index 598fc38c..87da9c79 100644 --- a/src/ArduinoOcpp/Core/OcppTime.cpp +++ b/src/ArduinoOcpp/Core/OcppTime.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -6,6 +6,24 @@ namespace ArduinoOcpp { +namespace Clocks { + +ulong lastClockReading = 0; +otime_t lastClockValue = 0; + +/* + * Basic clock implementation. Works if millis() is exact enough for you and if device doesn't go in sleep mode. + */ +OcppClock DEFAULT_CLOCK = [] () { + ulong tReading = (millis() - lastClockReading) / 1000UL; + if (tReading > 0) { + lastClockValue += tReading; + lastClockReading += tReading * 1000UL; + } + return lastClockValue;}; +} //end namespace Clocks + + OcppTimestamp::OcppTimestamp() { } @@ -170,6 +188,10 @@ OcppTimestamp OcppTimestamp::operator-(int secs) { return this->operator+(-secs); } +OcppTime::OcppTime(OcppClock system_clock) : system_clock(system_clock) { + +} + bool OcppTime::setOcppTime(const char* jsonDateString) { OcppTimestamp timestamp = OcppTimestamp(); diff --git a/src/ArduinoOcpp/Core/OcppTime.h b/src/ArduinoOcpp/Core/OcppTime.h index 07c4d634..3bc4b9ab 100644 --- a/src/ArduinoOcpp/Core/OcppTime.h +++ b/src/ArduinoOcpp/Core/OcppTime.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -14,6 +14,15 @@ namespace ArduinoOcpp { typedef int32_t otime_t; //requires 32bit signed integer or bigger +typedef std::function OcppClock; + +namespace Clocks { + +/* + * Basic clock implementation. Works if millis() is exact enough for you and if device doesn't go in sleep mode. + */ +extern OcppClock DEFAULT_CLOCK; +} //end namespace Clocks class OcppTimestamp { private: @@ -61,11 +70,11 @@ class OcppTime { OcppTimestamp ocpp_basetime; otime_t system_basetime = 0; //your system clock's time at the moment when the OCPP server's time was taken - std::function system_clock = [] () {return (otime_t) 0;}; + OcppClock system_clock = [] () {return (otime_t) 0;}; public: - OcppTime(std::function system_clock); + OcppTime(OcppClock system_clock); otime_t getOcppTimeScalar(); //returns current time of the OCPP server in non-UNIX but signed integer format. t2 - t1 is the time difference in seconds. OcppTimestamp createTimestamp(otime_t scalar); //creates a timestamp in a JSON-serializable format. createTimestamp(getOcppTimeScalar()) will return the current OCPP time diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index 295ef9a3..1a6e8bee 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -8,8 +8,8 @@ using namespace ArduinoOcpp; using namespace ArduinoOcpp::Ocpp16; -ConnectorMeterValuesRecorder::ConnectorMeterValuesRecorder(int connectorId) - : connectorId(connectorId) { +ConnectorMeterValuesRecorder::ConnectorMeterValuesRecorder(int connectorId, OcppTime *ocppTime) + : connectorId(connectorId), ocppTime(ocppTime) { sampleTimestamp = LinkedList(); energy = LinkedList(); power = LinkedList(); diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h index e57227f4..bf74a645 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -15,6 +15,7 @@ #include #include #include +#include namespace ArduinoOcpp { @@ -26,6 +27,7 @@ typedef std::function EnergySampler; class ConnectorMeterValuesRecorder { private: const int connectorId; + OcppTime *ocppTime; LinkedList sampleTimestamp; LinkedList energy; @@ -46,7 +48,7 @@ class ConnectorMeterValuesRecorder { Ocpp16::MeterValues *toMeterValues(); void clear(); public: - ConnectorMeterValuesRecorder(int connectorId); + ConnectorMeterValuesRecorder(int connectorId, OcppTime *ocppTime); Ocpp16::MeterValues *loop(); diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp index add86252..811a944f 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -13,12 +13,12 @@ using namespace ArduinoOcpp; using namespace ArduinoOcpp::Ocpp16; -MeteringService::MeteringService(WebSocketsClient *webSocket, int numConn) +MeteringService::MeteringService(WebSocketsClient *webSocket, int numConn, OcppTime *ocppTime) : webSocket(webSocket), numConnectors(numConn) { connectors = (ConnectorMeterValuesRecorder**) malloc(numConn * sizeof(ConnectorMeterValuesRecorder*)); for (int i = 0; i < numConn; i++) { - connectors[i] = new ConnectorMeterValuesRecorder(i); + connectors[i] = new ConnectorMeterValuesRecorder(i, ocppTime); } setMeteringSerivce(this); //make MeteringService available through Ocpp Engine diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h index b18b9c88..c7a8a892 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -34,7 +34,7 @@ class MeteringService { const int numConnectors; ConnectorMeterValuesRecorder **connectors; public: - MeteringService(WebSocketsClient *webSocket, int numConnectors); + MeteringService(WebSocketsClient *webSocket, int numConnectors, OcppTime *ocppTime); ~MeteringService(); diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index ea21fdb6..51fc247b 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -26,8 +26,8 @@ using namespace::ArduinoOcpp; -SmartChargingService::SmartChargingService(float chargeLimit, int numConnectors, FilesystemOpt filesystemOpt) - : DEFAULT_CHARGE_LIMIT(chargeLimit), filesystemOpt(filesystemOpt) { +SmartChargingService::SmartChargingService(float chargeLimit, int numConnectors, OcppTime *ocppTime, FilesystemOpt filesystemOpt) + : DEFAULT_CHARGE_LIMIT(chargeLimit), ocppTime(ocppTime), filesystemOpt(filesystemOpt) { if (numConnectors > 2) { Serial.print(F("[SmartChargingService] Error: Unfortunately, multiple connectors are not implemented in SmartChargingService yet. Only connector 1 will receive charging limits\n")); diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h index 1fa07b79..68b2c425 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h @@ -13,6 +13,7 @@ #include #include +#include namespace ArduinoOcpp { @@ -21,6 +22,7 @@ typedef std::function OnLimitChange; class SmartChargingService { private: + OcppTime *ocppTime; const float DEFAULT_CHARGE_LIMIT; ChargingProfile *ChargePointMaxProfile[CHARGEPROFILEMAXSTACKLEVEL]; ChargingProfile *TxDefaultProfile[CHARGEPROFILEMAXSTACKLEVEL]; @@ -38,7 +40,7 @@ class SmartChargingService { bool loadProfiles(); public: - SmartChargingService(float chargeLimit, int numConnectors, FilesystemOpt filesystemOpt = FilesystemOpt::Use_Mount_FormatOnFail); + SmartChargingService(float chargeLimit, int numConnectors, OcppTime *ocppTime, FilesystemOpt filesystemOpt = FilesystemOpt::Use_Mount_FormatOnFail); void beginCharging(time_t t, int transactionID); void beginChargingNow(); void endChargingNow(); From 6d901653279934ee76f1017586dede7eaac39355 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 29 May 2021 10:26:30 +0200 Subject: [PATCH 027/696] update facade --- src/ArduinoOcpp.cpp | 10 ++++++---- src/ArduinoOcpp.h | 5 +++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index e24d06e3..bba5b5c4 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -36,6 +36,7 @@ bool evRequestsEnergyLastState = false; SmartChargingService *smartChargingService; ChargePointStatusService *chargePointStatusService; OnLimitChange onLimitChange; +OcppTime *ocppTime; #define OCPP_NUMCONNECTORS 2 #define OCPP_ID_OF_CONNECTOR 1 @@ -94,7 +95,7 @@ using namespace ArduinoOcpp; using namespace ArduinoOcpp::Facade; using namespace ArduinoOcpp::Ocpp16; -void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, ArduinoOcpp::FilesystemOpt fsOpt) { +void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, ArduinoOcpp::FilesystemOpt fsOpt, ArduinoOcpp::OcppClock system_time) { if (OCPP_initialized) { Serial.print(F("[ArduinoOcpp] Error: cannot call OCPP_initialize() two times! If you want to reconfigure the library, please restart your ESP\n")); return; @@ -123,7 +124,7 @@ void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, Arduin OCPP_initialize(ocppSocket, fsOpt); } -void OCPP_initialize(OcppSocket *ocppSocket, ArduinoOcpp::FilesystemOpt fsOpt) { +void OCPP_initialize(OcppSocket *ocppSocket, ArduinoOcpp::FilesystemOpt fsOpt, ArduinoOcpp::OcppClock system_time) { if (OCPP_initialized) { Serial.print(F("[ArduinoOcpp] Error: cannot call OCPP_initialize() two times! If you want to reconfigure the library, please restart your ESP\n")); return; @@ -137,10 +138,11 @@ void OCPP_initialize(OcppSocket *ocppSocket, ArduinoOcpp::FilesystemOpt fsOpt) { configuration_init(fsOpt); //call before each other library call ocppEngine_initialize(ocppSocket); + ocppTime = new OcppTime(system_time); - smartChargingService = new SmartChargingService(11000.0f, OCPP_NUMCONNECTORS, fsOpt); //default charging limit: 11kW + smartChargingService = new SmartChargingService(11000.0f, OCPP_NUMCONNECTORS, ocppTime, fsOpt); //default charging limit: 11kW chargePointStatusService = new ChargePointStatusService(&webSocket, OCPP_NUMCONNECTORS); //Constructor adds instance to ocppEngine in constructor - meteringService = new MeteringService(&webSocket, OCPP_NUMCONNECTORS); + meteringService = new MeteringService(&webSocket, OCPP_NUMCONNECTORS, ocppTime); OCPP_initialized = true; } diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 543ab408..9f317ea5 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "Variants.h" @@ -22,9 +23,9 @@ using ArduinoOcpp::Timeout; #if USE_FACADE -void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail); +void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, ArduinoOcpp::OcppClock system_time = ArduinoOcpp::Clocks::DEFAULT_CLOCK); -void OCPP_initialize(ArduinoOcpp::OcppSocket *ocppSocket, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail); +void OCPP_initialize(ArduinoOcpp::OcppSocket *ocppSocket, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, ArduinoOcpp::OcppClock system_time = ArduinoOcpp::Clocks::DEFAULT_CLOCK); void OCPP_loop(); From 254216085f965e1b5a5c6e308adfd8892b2a96ef Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 31 May 2021 00:50:12 +0200 Subject: [PATCH 028/696] removed dependencies, hardened transactions --- src/ArduinoOcpp.cpp | 35 +- src/ArduinoOcpp.h | 6 +- .../Core/ConfigurationContainerFlash.cpp | 3 +- src/ArduinoOcpp/Core/OcppConnection.cpp | 2 +- src/ArduinoOcpp/Core/OcppConnection.h | 2 +- src/ArduinoOcpp/Core/OcppEngine.cpp | 16 +- src/ArduinoOcpp/Core/OcppEngine.h | 7 +- src/ArduinoOcpp/Core/OcppError.h | 2 +- src/ArduinoOcpp/Core/OcppMessage.cpp | 6 +- src/ArduinoOcpp/Core/OcppMessage.h | 4 +- src/ArduinoOcpp/Core/OcppOperation.cpp | 14 +- src/ArduinoOcpp/Core/OcppOperation.h | 6 +- src/ArduinoOcpp/Core/OcppOperationTimeout.cpp | 2 +- src/ArduinoOcpp/Core/OcppOperationTimeout.h | 2 +- src/ArduinoOcpp/Core/OcppServer.cpp | 2 +- src/ArduinoOcpp/Core/OcppServer.h | 2 +- src/ArduinoOcpp/Core/OcppSocket.cpp | 2 +- src/ArduinoOcpp/Core/OcppSocket.h | 2 +- src/ArduinoOcpp/Core/OcppTime.cpp | 191 +++-- src/ArduinoOcpp/Core/OcppTime.h | 37 +- src/ArduinoOcpp/MessagesV16/Authorize.cpp | 4 +- src/ArduinoOcpp/MessagesV16/Authorize.h | 2 +- .../MessagesV16/BootNotification.cpp | 12 +- .../MessagesV16/BootNotification.h | 2 +- src/ArduinoOcpp/MessagesV16/DataTransfer.cpp | 2 +- src/ArduinoOcpp/MessagesV16/DataTransfer.h | 2 +- .../MessagesV16/GetConfiguration.cpp | 16 +- .../MessagesV16/GetConfiguration.h | 13 +- src/ArduinoOcpp/MessagesV16/Heartbeat.cpp | 20 +- src/ArduinoOcpp/MessagesV16/Heartbeat.h | 2 +- src/ArduinoOcpp/MessagesV16/MeterValues.cpp | 146 ++-- src/ArduinoOcpp/MessagesV16/MeterValues.h | 15 +- .../MessagesV16/RemoteStartTransaction.cpp | 2 +- .../MessagesV16/RemoteStartTransaction.h | 2 +- .../MessagesV16/RemoteStopTransaction.cpp | 2 +- .../MessagesV16/RemoteStopTransaction.h | 2 +- src/ArduinoOcpp/MessagesV16/Reset.cpp | 2 +- src/ArduinoOcpp/MessagesV16/Reset.h | 2 +- .../MessagesV16/SetChargingProfile.cpp | 8 +- .../MessagesV16/SetChargingProfile.h | 2 +- .../MessagesV16/StartTransaction.cpp | 142 ++-- .../MessagesV16/StartTransaction.h | 7 +- .../MessagesV16/StatusNotification.cpp | 185 +++-- .../MessagesV16/StatusNotification.h | 24 +- .../MessagesV16/StopTransaction.cpp | 49 +- src/ArduinoOcpp/MessagesV16/StopTransaction.h | 25 +- .../MessagesV16/TriggerMessage.cpp | 2 +- src/ArduinoOcpp/MessagesV16/TriggerMessage.h | 2 +- .../SimpleOcppOperationFactory.cpp | 2 +- src/ArduinoOcpp/SimpleOcppOperationFactory.h | 2 +- .../ChargePointStatusService.cpp | 105 +-- .../ChargePointStatusService.h | 47 +- .../ChargePointStatus/ConnectorStatus.cpp | 39 +- .../Tasks/ChargePointStatus/ConnectorStatus.h | 8 +- .../Tasks/ChargePointStatus/OcppEvseState.h | 2 +- .../Metering/ConnectorMeterValuesRecorder.cpp | 23 +- .../Metering/ConnectorMeterValuesRecorder.h | 14 +- .../Tasks/Metering/MeteringService.cpp | 62 +- .../Tasks/Metering/MeteringService.h | 2 - .../SmartCharging/SmartChargingModel.cpp | 604 ++++++++-------- .../Tasks/SmartCharging/SmartChargingModel.h | 172 ++--- .../SmartCharging/SmartChargingService.cpp | 668 +++++++++--------- .../SmartCharging/SmartChargingService.h | 55 +- src/ArduinoOcpp/TimeHelper.cpp | 4 + src/ArduinoOcpp/TimeHelper.h | 3 + 65 files changed, 1558 insertions(+), 1291 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index bba5b5c4..ed9ca66f 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -33,6 +33,8 @@ PowerSampler powerSampler; EnergySampler energySampler; std::function evRequestsEnergySampler = NULL; //bool (*evRequestsEnergySampler)() = NULL; bool evRequestsEnergyLastState = false; +std::function connectorEnergizedSampler = NULL; +bool connectorEnergizedLastState = false; SmartChargingService *smartChargingService; ChargePointStatusService *chargePointStatusService; OnLimitChange onLimitChange; @@ -95,7 +97,7 @@ using namespace ArduinoOcpp; using namespace ArduinoOcpp::Facade; using namespace ArduinoOcpp::Ocpp16; -void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, ArduinoOcpp::FilesystemOpt fsOpt, ArduinoOcpp::OcppClock system_time) { +void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, float V_eff, ArduinoOcpp::FilesystemOpt fsOpt, ArduinoOcpp::OcppClock system_time) { if (OCPP_initialized) { Serial.print(F("[ArduinoOcpp] Error: cannot call OCPP_initialize() two times! If you want to reconfigure the library, please restart your ESP\n")); return; @@ -121,10 +123,10 @@ void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, Arduin ocppSocket = new EspWiFi::OcppClientSocket(&webSocket); - OCPP_initialize(ocppSocket, fsOpt); + OCPP_initialize(ocppSocket, V_eff, fsOpt); } -void OCPP_initialize(OcppSocket *ocppSocket, ArduinoOcpp::FilesystemOpt fsOpt, ArduinoOcpp::OcppClock system_time) { +void OCPP_initialize(OcppSocket *ocppSocket, float V_eff, ArduinoOcpp::FilesystemOpt fsOpt, ArduinoOcpp::OcppClock system_time) { if (OCPP_initialized) { Serial.print(F("[ArduinoOcpp] Error: cannot call OCPP_initialize() two times! If you want to reconfigure the library, please restart your ESP\n")); return; @@ -138,10 +140,12 @@ void OCPP_initialize(OcppSocket *ocppSocket, ArduinoOcpp::FilesystemOpt fsOpt, A configuration_init(fsOpt); //call before each other library call ocppEngine_initialize(ocppSocket); + ocppTime = new OcppTime(system_time); + ocppEngine_setOcppTime(ocppTime); - smartChargingService = new SmartChargingService(11000.0f, OCPP_NUMCONNECTORS, ocppTime, fsOpt); //default charging limit: 11kW - chargePointStatusService = new ChargePointStatusService(&webSocket, OCPP_NUMCONNECTORS); //Constructor adds instance to ocppEngine in constructor + smartChargingService = new SmartChargingService(11000.0f, V_eff, OCPP_NUMCONNECTORS, ocppTime, fsOpt); //default charging limit: 11kW + chargePointStatusService = new ChargePointStatusService(&webSocket, OCPP_NUMCONNECTORS, ocppTime); //Constructor adds instance to ocppEngine in constructor meteringService = new MeteringService(&webSocket, OCPP_NUMCONNECTORS, ocppTime); OCPP_initialized = true; @@ -154,6 +158,9 @@ void OCPP_loop() { return; } + Serial.print('.'); + delay(70); + //webSocket.loop(); //moved to Core/OcppSocket ocppEngine_loop(); //mandatory @@ -184,6 +191,21 @@ void OCPP_loop() { chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->stopEvDrawsEnergy(); } + bool connectorEnergizedNewState = true; + if (connectorEnergizedSampler != NULL) { + connectorEnergizedNewState = connectorEnergizedSampler(); + } else { + connectorEnergizedNewState = getTransactionId() >= 0; + } + + if (!connectorEnergizedLastState && connectorEnergizedNewState) { + connectorEnergizedLastState = true; + chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->startEnergyOffer(); + } else if (connectorEnergizedLastState && !connectorEnergizedNewState) { + connectorEnergizedLastState = false; + chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->stopEnergyOffer(); + } + } void setPowerActiveImportSampler(std::function power) { @@ -198,7 +220,10 @@ void setEnergyActiveImportSampler(std::function energy) { void setEvRequestsEnergySampler(std::function evRequestsEnergy) { evRequestsEnergySampler = evRequestsEnergy; +} +void setConnectorEnergizedSampler(std::function connectorEnergized) { + connectorEnergizedSampler = connectorEnergized; } void setOnChargingRateLimitChange(std::function chargingRateChanged) { diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 9f317ea5..603a7b1a 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -23,9 +23,9 @@ using ArduinoOcpp::Timeout; #if USE_FACADE -void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, ArduinoOcpp::OcppClock system_time = ArduinoOcpp::Clocks::DEFAULT_CLOCK); +void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, float V_eff = 230.f /*German grid*/, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, ArduinoOcpp::OcppClock system_time = ArduinoOcpp::Clocks::DEFAULT_CLOCK); -void OCPP_initialize(ArduinoOcpp::OcppSocket *ocppSocket, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, ArduinoOcpp::OcppClock system_time = ArduinoOcpp::Clocks::DEFAULT_CLOCK); +void OCPP_initialize(ArduinoOcpp::OcppSocket *ocppSocket, float V_eff = 230.f /*German grid*/, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, ArduinoOcpp::OcppClock system_time = ArduinoOcpp::Clocks::DEFAULT_CLOCK); void OCPP_loop(); @@ -45,6 +45,8 @@ void setEnergyActiveImportSampler(std::function energy); void setEvRequestsEnergySampler(std::function evRequestsEnergy); +void setConnectorEnergizedSampler(std::function connectorEnergized); + /* * React on calls by the library's internal functions * diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp index abc56ea0..edf1adfd 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp @@ -108,8 +108,7 @@ bool ConfigurationContainerFlash::load() { } JsonArray configurationsArray = configDoc["configurations"]; - for (int i = 0; i < configurationsArray.size(); i++) { - JsonObject config = configurationsArray[i]; + for (JsonObject config : configurationsArray) { const char *type = config["type"] | "Undefined"; std::shared_ptr configuration = NULL; diff --git a/src/ArduinoOcpp/Core/OcppConnection.cpp b/src/ArduinoOcpp/Core/OcppConnection.cpp index 36d55634..f87410d5 100644 --- a/src/ArduinoOcpp/Core/OcppConnection.cpp +++ b/src/ArduinoOcpp/Core/OcppConnection.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/Core/OcppConnection.h b/src/ArduinoOcpp/Core/OcppConnection.h index a99ce78b..c2ae413e 100644 --- a/src/ArduinoOcpp/Core/OcppConnection.h +++ b/src/ArduinoOcpp/Core/OcppConnection.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/Core/OcppEngine.cpp b/src/ArduinoOcpp/Core/OcppEngine.cpp index a42c0ae8..72fa4df1 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.cpp +++ b/src/ArduinoOcpp/Core/OcppEngine.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -18,6 +18,7 @@ OcppSocket *ocppSocket; SmartChargingService *ocppEngine_smartChargingService; ChargePointStatusService *ocppEngine_chargePointStatusService; MeteringService *ocppEngine_meteringService; +OcppTime *ocppEngine_ocppTime; std::vector initiatedOcppOperations; std::vector receivedOcppOperations; @@ -43,6 +44,7 @@ void ocppEngine_initialize(OcppSocket *ocppSocket){ void initiateOcppOperation(OcppOperation *o) { mConnection->initiateOcppOperation(o); + o->setInitiated(); } void ocppEngine_loop() { @@ -440,6 +442,18 @@ MeteringService* getMeteringService() { return ocppEngine_meteringService; } +void ocppEngine_setOcppTime(OcppTime *ocppTime) { + ocppEngine_ocppTime = ocppTime; +} + +OcppTime *getOcppTime() { + if (ocppEngine_ocppTime == NULL) { + Serial.print(F("[OcppEngine] Error: in OcppEngine, there is no ocppEngine_ocppTime set, but it is accessed!\n")); + //no error catch + } + return ocppEngine_ocppTime; +} + #if 0 //moved to OcppConnection /* diff --git a/src/ArduinoOcpp/Core/OcppEngine.h b/src/ArduinoOcpp/Core/OcppEngine.h index 6eab0648..e6f6aff9 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.h +++ b/src/ArduinoOcpp/Core/OcppEngine.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -52,6 +53,10 @@ void setMeteringSerivce(MeteringService *meteringService); MeteringService* getMeteringService(); +void ocppEngine_setOcppTime(OcppTime *ocppTime); + +OcppTime *getOcppTime(); + } //end namespace ArduinoOcpp #endif diff --git a/src/ArduinoOcpp/Core/OcppError.h b/src/ArduinoOcpp/Core/OcppError.h index d58c1f5c..0827d886 100644 --- a/src/ArduinoOcpp/Core/OcppError.h +++ b/src/ArduinoOcpp/Core/OcppError.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/Core/OcppMessage.cpp b/src/ArduinoOcpp/Core/OcppMessage.cpp index cd6c63c9..d141fe84 100644 --- a/src/ArduinoOcpp/Core/OcppMessage.cpp +++ b/src/ArduinoOcpp/Core/OcppMessage.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -15,6 +15,10 @@ const char* OcppMessage::getOcppOperationType(){ return "CustomOperation"; } +void OcppMessage::initiate() { + //callback after initiateOcppOperation(anyMsg) +} + DynamicJsonDocument* OcppMessage::createReq() { Serial.print(F("[OcppMessage] Unsupported operation: createReq() is not implemented!\n")); return new DynamicJsonDocument(0); diff --git a/src/ArduinoOcpp/Core/OcppMessage.h b/src/ArduinoOcpp/Core/OcppMessage.h index 48c09ac8..bef98996 100644 --- a/src/ArduinoOcpp/Core/OcppMessage.h +++ b/src/ArduinoOcpp/Core/OcppMessage.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -36,6 +36,8 @@ class OcppMessage { virtual const char* getOcppOperationType(); + virtual void initiate(); + /** * Create the payload for the respective OCPP message * diff --git a/src/ArduinoOcpp/Core/OcppOperation.cpp b/src/ArduinoOcpp/Core/OcppOperation.cpp index ff0b7008..e34665de 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.cpp +++ b/src/ArduinoOcpp/Core/OcppOperation.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -122,7 +122,7 @@ boolean OcppOperation::sendReq(OcppSocket *ocppSocket){ serializeJson(requestJson, out); #if DEBUG_OUT - if (printReqCounter > 10000) { + if (printReqCounter > 5000) { printReqCounter = 0; Serial.print(F("[OcppOperation] Send requirement: ")); Serial.print(out); @@ -141,7 +141,7 @@ boolean OcppOperation::sendReq(OcppSocket *ocppSocket){ // timeout_active = true; // timeout_start = millis(); // } - if (TRAFFIC_OUT) Serial.print(F("[OcppOperation] Send requirement: ")); + if (TRAFFIC_OUT) Serial.print(F("[OcppOperation] Sent requirement (success): ")); if (TRAFFIC_OUT) Serial.println(out); retry_start = millis(); } else { @@ -321,6 +321,14 @@ boolean OcppOperation::sendConf(OcppSocket *ocppSocket){ return wsSuccess; } +void OcppOperation::setInitiated() { + if (ocppMessage) { + ocppMessage->initiate(); + } else { + Serial.print(F("[OcppOperation] Error: called setInitiated without corresponding OcppOperation!\n")); + } +} + void OcppOperation::setOnReceiveConfListener(OnReceiveConfListener onReceiveConf){ if (onReceiveConf) onReceiveConfListener = onReceiveConf; diff --git a/src/ArduinoOcpp/Core/OcppOperation.h b/src/ArduinoOcpp/Core/OcppOperation.h index ff8ed50b..ba955195 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.h +++ b/src/ArduinoOcpp/Core/OcppOperation.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -50,7 +50,7 @@ class OcppOperation { ulong retry_start = 0; ulong retry_interval_mult = 1; // RETRY_INTERVAL * retry_interval_mult gives longer periods with each iteration #if DEBUG_OUT - uint16_t printReqCounter = -1; + uint16_t printReqCounter = 0; #endif public: @@ -107,6 +107,8 @@ class OcppOperation { */ boolean sendConf(OcppSocket *ocppSocket); + void setInitiated(); + void setOnReceiveConfListener(OnReceiveConfListener onReceiveConf); /** diff --git a/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp b/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp index d9d4e81f..b80790ce 100644 --- a/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp +++ b/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/Core/OcppOperationTimeout.h b/src/ArduinoOcpp/Core/OcppOperationTimeout.h index 8a0d5493..f6281d84 100644 --- a/src/ArduinoOcpp/Core/OcppOperationTimeout.h +++ b/src/ArduinoOcpp/Core/OcppOperationTimeout.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/Core/OcppServer.cpp b/src/ArduinoOcpp/Core/OcppServer.cpp index 50826d61..82cceee6 100644 --- a/src/ArduinoOcpp/Core/OcppServer.cpp +++ b/src/ArduinoOcpp/Core/OcppServer.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/Core/OcppServer.h b/src/ArduinoOcpp/Core/OcppServer.h index 9d0269bb..c60727df 100644 --- a/src/ArduinoOcpp/Core/OcppServer.h +++ b/src/ArduinoOcpp/Core/OcppServer.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/Core/OcppSocket.cpp b/src/ArduinoOcpp/Core/OcppSocket.cpp index 41c74371..4aeef4c0 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.cpp +++ b/src/ArduinoOcpp/Core/OcppSocket.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/Core/OcppSocket.h b/src/ArduinoOcpp/Core/OcppSocket.h index c3d5f182..259c360d 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.h +++ b/src/ArduinoOcpp/Core/OcppSocket.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/Core/OcppTime.cpp b/src/ArduinoOcpp/Core/OcppTime.cpp index 87da9c79..f1d95d06 100644 --- a/src/ArduinoOcpp/Core/OcppTime.cpp +++ b/src/ArduinoOcpp/Core/OcppTime.cpp @@ -100,19 +100,19 @@ bool OcppTimestamp::setTime(const char *jsonDateString) { return true; } -bool OcppTimestamp::toJsonString(char *jsonDateString, size_t buffsize) { - if (buffsize < OCPP_JSONDATE_LENGTH + 1) return false; +bool OcppTimestamp::toJsonString(char *jsonDateString, size_t buffsize) const { + if (buffsize < JSONDATE_LENGTH + 1) return false; jsonDateString[0] = ((char) ((year / 1000) % 10)) + '0'; jsonDateString[1] = ((char) ((year / 100) % 10)) + '0'; jsonDateString[2] = ((char) ((year / 10) % 10)) + '0'; jsonDateString[3] = ((char) ((year / 1) % 10)) + '0'; jsonDateString[4] = '-'; - jsonDateString[5] = ((char) ((month / 10) % 10)) + '0'; - jsonDateString[6] = ((char) ((month / 1) % 10)) + '0'; + jsonDateString[5] = ((char) (((month + 1) / 10) % 10)) + '0'; + jsonDateString[6] = ((char) (((month + 1) / 1) % 10)) + '0'; jsonDateString[7] = '-'; - jsonDateString[8] = ((char) ((day / 10) % 10)) + '0'; - jsonDateString[9] = ((char) ((day / 1) % 10)) + '0'; + jsonDateString[8] = ((char) (((day + 1) / 10) % 10)) + '0'; + jsonDateString[9] = ((char) (((day + 1) / 1) % 10)) + '0'; jsonDateString[10] = 'T'; jsonDateString[11] = ((char) ((hour / 10) % 10)) + '0'; jsonDateString[12] = ((char) ((hour / 1) % 10)) + '0'; @@ -131,63 +131,151 @@ bool OcppTimestamp::toJsonString(char *jsonDateString, size_t buffsize) { return true; } -OcppTimestamp OcppTimestamp::operator+(int secs) { - OcppTimestamp res = *this; - res.second += secs; +OcppTimestamp &OcppTimestamp::operator+=(int secs) { - if (res.second >= 0 && res.second < 60) return res; + second += secs; - res.minute += res.second / 60; - res.second %= 60; - if (res.second < 0) { - res.minute--; - res.second += 60; + if (second >= 0 && second < 60) return *this; + + minute += second / 60; + second %= 60; + if (second < 0) { + minute--; + second += 60; } - if (res.minute >= 0 && res.minute < 60) return res; + if (minute >= 0 && minute < 60) return *this; - res.hour += res.minute / 60; - res.minute %= 60; - if (res.minute < 0) { - res.hour--; - res.minute += 60; + hour += minute / 60; + minute %= 60; + if (minute < 0) { + hour--; + minute += 60; } - if (res.hour >= 0 && res.hour < 24) return res; + if (hour >= 0 && hour < 24) return *this; - res.day += res.hour / 24; - res.hour %= 24; - if (res.hour < 0) { - res.day--; - res.hour += 24; + day += hour / 24; + hour %= 24; + if (hour < 0) { + day--; + hour += 24; } - while (res.day >= noDays(res.month, res.year)) { - res.day -= noDays(res.month, res.year); - res.month++; + while (day >= noDays(month, year)) { + day -= noDays(month, year); + month++; - if (res.month >= 12) { - res.month -= 12; - res.year++; + if (month >= 12) { + month -= 12; + year++; } } - while (res.day < 0) { - res.month--; - if (res.month < 0) { - res.month += 12; - res.year--; + while (day < 0) { + month--; + if (month < 0) { + month += 12; + year--; } - res.day += noDays(res.month, res.year); + day += noDays(month, year); } - return res; + return *this; }; -OcppTimestamp OcppTimestamp::operator-(int secs) { - return this->operator+(-secs); +OcppTimestamp &OcppTimestamp::operator-=(int secs) { + return operator+=(-secs); +} + +otime_t OcppTimestamp::operator-(const OcppTimestamp &rhs) const { + //dt = rhs - ocpp_base + + int16_t year_base, year_end; + if (year <= rhs.year) { + year_base = year; + year_end = rhs.year; + } else { + year_base = rhs.year; + year_end = year; + } + + int16_t lhsDays = day; + int16_t rhsDays = rhs.day; + + for (int16_t iy = year_base; iy <= year_end; iy++) { + for (int16_t im = 0; im < 12; im++) { + if (year > iy || (year == iy && month > im)) { + lhsDays += noDays(im, iy); + } + if (rhs.year > iy || (rhs.year == iy && rhs.month > im)) { + rhsDays += noDays(im, iy); + } + } + } + + otime_t dt = (lhsDays - rhsDays) * (24 * 3600) + (hour - rhs.hour) * 3600 + (minute - rhs.minute) * 60 + second - rhs.second; + return dt; +} + +OcppTimestamp &OcppTimestamp::operator=(const OcppTimestamp &rhs) { + year = rhs.year; + month = rhs.month; + day = rhs.day; + hour = rhs.hour; + minute = rhs.minute; + second = rhs.second; + + return *this; +} + +OcppTimestamp operator+(const OcppTimestamp &lhs, int secs) { + OcppTimestamp res = lhs; + res += secs; + return res; +} + +OcppTimestamp operator-(const OcppTimestamp &lhs, int secs) { + return operator+(lhs, -secs); +} + +bool operator==(const OcppTimestamp &lhs, const OcppTimestamp &rhs) { + return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day && lhs.hour == rhs.hour && lhs.minute == rhs.minute && lhs.second == rhs.second; } +bool operator!=(const OcppTimestamp &lhs, const OcppTimestamp &rhs) { + return !(lhs == rhs); +} + +bool operator<(const OcppTimestamp &lhs, const OcppTimestamp &rhs) { + if (lhs.year != rhs.year) + return lhs.year < rhs.year; + if (lhs.month != rhs.month) + return lhs.month < rhs.month; + if (lhs.day != rhs.day) + return lhs.day < rhs.day; + if (lhs.hour != rhs.hour) + return lhs.hour < rhs.hour; + if (lhs.minute != rhs.minute) + return lhs.minute < rhs.minute; + if (lhs.second != rhs.second) + return lhs.second < rhs.second; + return false; +} + +bool operator<=(const OcppTimestamp &lhs, const OcppTimestamp &rhs) { + return lhs < rhs || lhs == rhs; +} + +bool operator>(const OcppTimestamp &lhs, const OcppTimestamp &rhs) { + return rhs < lhs; +} + +bool operator>=(const OcppTimestamp &lhs, const OcppTimestamp &rhs) { + return rhs <= lhs; +} + + OcppTime::OcppTime(OcppClock system_clock) : system_clock(system_clock) { } @@ -202,7 +290,11 @@ bool OcppTime::setOcppTime(const char* jsonDateString) { system_basetime = system_clock(); ocpp_basetime = timestamp; - + ocppTimeIsSet = true; + + currentTime = ocpp_basetime; + previousUpdate = system_basetime; + return true; } @@ -210,10 +302,23 @@ otime_t OcppTime::getOcppTimeScalar() { return system_clock(); } +const OcppTimestamp &OcppTime::getOcppTimestampNow() { + otime_t tNow = system_clock(); + if (previousUpdate != tNow) { + currentTime += (tNow - previousUpdate); + previousUpdate = tNow; + } + return currentTime; +} + OcppTimestamp OcppTime::createTimestamp(otime_t scalar) { OcppTimestamp res = ocpp_basetime + (scalar - system_basetime); return res; } +otime_t OcppTime::toOcppTimeScalar(const OcppTimestamp &otimestamp) { + return otimestamp.operator-(ocpp_basetime); +} + } diff --git a/src/ArduinoOcpp/Core/OcppTime.h b/src/ArduinoOcpp/Core/OcppTime.h index 3bc4b9ab..173fddf2 100644 --- a/src/ArduinoOcpp/Core/OcppTime.h +++ b/src/ArduinoOcpp/Core/OcppTime.h @@ -9,13 +9,16 @@ #include -#define OCPP_JSONDATE_LENGTH 24 namespace ArduinoOcpp { typedef int32_t otime_t; //requires 32bit signed integer or bigger +#define OTIME_MAX ((otime_t) INT32_MAX) typedef std::function OcppClock; +#define INFINITY_THLD (OTIME_MAX - ((otime_t) (400 * 24 * 3600))) //Upper limiter for valid time range. From this value on, a scalar time means "infinity". It's 400 days before the "year 2038" problem. +#define JSONDATE_LENGTH 24 + namespace Clocks { /* @@ -41,6 +44,9 @@ class OcppTimestamp { OcppTimestamp(); + OcppTimestamp(int16_t year, int16_t month, int16_t day, int32_t hour, int32_t minute, int32_t second) : + year(year), month(month), day(day), hour(hour), minute(minute), second(second) { }; + /** * Expects a date string like * 2020-10-01T20:53:32.486Z @@ -56,28 +62,47 @@ class OcppTimestamp { */ bool setTime(const char* jsonDateString); - bool toJsonString(char *out, size_t buffsize); + bool toJsonString(char *out, size_t buffsize) const; + + OcppTimestamp &operator=(const OcppTimestamp &rhs); + + OcppTimestamp &operator+=(int secs); + OcppTimestamp &operator-=(int secs); - OcppTimestamp operator+(int secs); + otime_t operator-(const OcppTimestamp &rhs) const; - OcppTimestamp operator-(int secs); + friend OcppTimestamp operator+(const OcppTimestamp &lhs, int secs); + friend OcppTimestamp operator-(const OcppTimestamp &lhs, int secs); + friend bool operator==(const OcppTimestamp &lhs, const OcppTimestamp &rhs); + friend bool operator!=(const OcppTimestamp &lhs, const OcppTimestamp &rhs); + friend bool operator<(const OcppTimestamp &lhs, const OcppTimestamp &rhs); + friend bool operator<=(const OcppTimestamp &lhs, const OcppTimestamp &rhs); + friend bool operator>(const OcppTimestamp &lhs, const OcppTimestamp &rhs); + friend bool operator>=(const OcppTimestamp &lhs, const OcppTimestamp &rhs); }; + class OcppTime { private: - OcppTimestamp ocpp_basetime; + OcppTimestamp ocpp_basetime = OcppTimestamp(); otime_t system_basetime = 0; //your system clock's time at the moment when the OCPP server's time was taken + bool ocppTimeIsSet = false; OcppClock system_clock = [] () {return (otime_t) 0;}; + OcppTimestamp currentTime = OcppTimestamp(); + otime_t previousUpdate = -1; + public: OcppTime(OcppClock system_clock); otime_t getOcppTimeScalar(); //returns current time of the OCPP server in non-UNIX but signed integer format. t2 - t1 is the time difference in seconds. + const OcppTimestamp &getOcppTimestampNow(); OcppTimestamp createTimestamp(otime_t scalar); //creates a timestamp in a JSON-serializable format. createTimestamp(getOcppTimeScalar()) will return the current OCPP time + otime_t toOcppTimeScalar(const OcppTimestamp &otimestamp); /** * Expects a date string like @@ -94,7 +119,7 @@ class OcppTime { */ bool setOcppTime(const char* jsonDateString); - + bool isValid() {return ocppTimeIsSet;} }; } diff --git a/src/ArduinoOcpp/MessagesV16/Authorize.cpp b/src/ArduinoOcpp/MessagesV16/Authorize.cpp index 418aa2d4..f05c6732 100644 --- a/src/ArduinoOcpp/MessagesV16/Authorize.cpp +++ b/src/ArduinoOcpp/MessagesV16/Authorize.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -9,7 +9,7 @@ using ArduinoOcpp::Ocpp16::Authorize; Authorize::Authorize() { - idTag = String("fefed1d19876"); //Use a default payload. In the typical use case of this library, you probably you don't even need Authorization at all + this->idTag = String("A0-00-00-00"); //Use a default payload. In the typical use case of this library, you probably you don't even need Authorization at all } Authorize::Authorize(String &idTag) { diff --git a/src/ArduinoOcpp/MessagesV16/Authorize.h b/src/ArduinoOcpp/MessagesV16/Authorize.h index 6c65d99d..bbd91af2 100644 --- a/src/ArduinoOcpp/MessagesV16/Authorize.h +++ b/src/ArduinoOcpp/MessagesV16/Authorize.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp index 29b89945..d09a5e2a 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -8,7 +8,7 @@ #include #include -#include +#include using ArduinoOcpp::Ocpp16::BootNotification; @@ -60,7 +60,10 @@ DynamicJsonDocument* BootNotification::createReq() { void BootNotification::processConf(JsonObject payload){ const char* currentTime = payload["currentTime"] | "Invalid"; if (strcmp(currentTime, "Invalid")) { - if (!setTimeFromJsonDateString(currentTime)) { + OcppTime *ocppTime = getOcppTime(); + if (ocppTime && ocppTime->setOcppTime(currentTime)) { + //success + } else { Serial.print(F("[BootNotification] Error reading time string. Expect format like 2020-02-01T20:53:32.486Z\n")); } } else { @@ -90,9 +93,6 @@ void BootNotification::processReq(JsonObject payload){ DynamicJsonDocument* BootNotification::createConf(){ DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(3) + (JSONDATE_LENGTH + 1)); JsonObject payload = doc->to(); - //char currentTime[JSONDATE_LENGTH + 1] = {'\0'}; - //getJsonDateStringFromSystemTime(currentTime, JSONDATE_LENGTH); - //payload["currentTime"] = currentTime; //currentTime payload["currentTime"] = "2019-11-01T11:59:55.123Z"; payload["interval"] = 86400; //heartbeat send interval - not relevant for JSON variant of OCPP so send dummy value that likely won't break payload["status"] = "Accepted"; diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.h b/src/ArduinoOcpp/MessagesV16/BootNotification.h index 982f0367..29aec34e 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.h +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp b/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp index 25fd5efc..d86aac25 100644 --- a/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp +++ b/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/MessagesV16/DataTransfer.h b/src/ArduinoOcpp/MessagesV16/DataTransfer.h index b76053c7..ed3723a6 100644 --- a/src/ArduinoOcpp/MessagesV16/DataTransfer.h +++ b/src/ArduinoOcpp/MessagesV16/DataTransfer.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp index 6123e01e..3a7035c1 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp @@ -10,11 +10,11 @@ using ArduinoOcpp::Ocpp16::GetConfiguration; GetConfiguration::GetConfiguration() { - keys = LinkedList(); + keys = std::vector(); } GetConfiguration::~GetConfiguration() { - keys.clear(); + //keys.clear(); } const char* GetConfiguration::getOcppOperationType(){ @@ -25,7 +25,7 @@ void GetConfiguration::processReq(JsonObject payload) { JsonArray requestedKeys = payload["key"]; for (uint16_t i = 0; i < requestedKeys.size(); i++) { - keys.add(requestedKeys[i].as()); + keys.push_back(requestedKeys[i].as()); } } @@ -34,16 +34,16 @@ DynamicJsonDocument* GetConfiguration::createConf(){ std::shared_ptr>> configurationKeys; std::vector unknownKeys; - if (keys.size() <= 0){ //return all existing keys + if (keys.size() == 0){ //return all existing keys configurationKeys = getAllConfigurations(); } else { //only return keys that were searched using the "key" parameter configurationKeys = std::make_shared>>(); - for (int i = 0; i < keys.size(); i++) { - std::shared_ptr entry = getConfiguration(keys.get(i).c_str()); + for (size_t i = 0; i < keys.size(); i++) { + std::shared_ptr entry = getConfiguration(keys.at(i).c_str()); if (entry) configurationKeys->push_back(entry); else - unknownKeys.push_back(keys.get(i).c_str()); + unknownKeys.push_back(keys.at(i).c_str()); } } @@ -71,7 +71,7 @@ DynamicJsonDocument* GetConfiguration::createConf(){ JsonObject payload = doc->to(); JsonArray jsonConfigurationKey = payload.createNestedArray("configurationKey"); - for (int i = 0; i < configurationKeys->size(); i++) { + for (size_t i = 0; i < configurationKeys->size(); i++) { jsonConfigurationKey.add(configurationKeysJson.at(i)->as()); } diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.h b/src/ArduinoOcpp/MessagesV16/GetConfiguration.h index 28d31a95..d1ea510c 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.h +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.h @@ -8,23 +8,22 @@ #include #include -#include namespace ArduinoOcpp { namespace Ocpp16 { class GetConfiguration : public OcppMessage { private: - LinkedList keys; + std::vector keys; public: - GetConfiguration(); - ~GetConfiguration(); + GetConfiguration(); + ~GetConfiguration(); - const char* getOcppOperationType(); + const char* getOcppOperationType(); - void processReq(JsonObject payload); + void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + DynamicJsonDocument* createConf(); }; diff --git a/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp b/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp index 40afe3e4..6671aeb5 100644 --- a/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp +++ b/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp @@ -1,9 +1,9 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License #include -#include +#include #include using ArduinoOcpp::Ocpp16::Heartbeat; @@ -18,7 +18,7 @@ const char* Heartbeat::getOcppOperationType(){ DynamicJsonDocument* Heartbeat::createReq() { DynamicJsonDocument *doc = new DynamicJsonDocument(0); - JsonObject payload = doc->to(); + //JsonObject payload = doc->to(); /* * Empty payload */ @@ -29,7 +29,9 @@ void Heartbeat::processConf(JsonObject payload) { const char* currentTime = payload["currentTime"] | "Invalid"; if (strcmp(currentTime, "Invalid")) { - if (setTimeFromJsonDateString(currentTime)) { + OcppTime *ocppTime = getOcppTime(); + if (ocppTime && ocppTime->setOcppTime(currentTime)) { + //success if (DEBUG_OUT) Serial.print(F("[Heartbeat] Request has been accepted!\n")); } else { Serial.print(F("[Heartbeat] Request accepted. But Error reading time string. Expect format like 2020-02-01T20:53:32.486Z\n")); @@ -52,9 +54,13 @@ DynamicJsonDocument* Heartbeat::createConf(){ DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + (JSONDATE_LENGTH + 1)); JsonObject payload = doc->to(); - char currentTime[JSONDATE_LENGTH + 1] = {'\0'}; - getJsonDateStringFromSystemTime(currentTime, JSONDATE_LENGTH); - payload["currentTime"] = currentTime; + OcppTime *ocppTime = getOcppTime(); + if (ocppTime) { + const OcppTimestamp otimestamp = ocppTime->getOcppTimestampNow(); + char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; + otimestamp.toJsonString(timestamp, JSONDATE_LENGTH + 1); + payload["currentTime"] = timestamp; + } return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/Heartbeat.h b/src/ArduinoOcpp/MessagesV16/Heartbeat.h index e0d8abfe..6a390a34 100644 --- a/src/ArduinoOcpp/MessagesV16/Heartbeat.h +++ b/src/ArduinoOcpp/MessagesV16/Heartbeat.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp index af5f614d..22716b5a 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -12,42 +12,29 @@ using ArduinoOcpp::Ocpp16::MeterValues; //can only be used for echo server debugging MeterValues::MeterValues() { - sampleTime = LinkedList(); //not used in server mode but needed to keep the destructor simple - energy = LinkedList(); - power = LinkedList(); + sampleTime = std::vector(); //not used in server mode but needed to keep the destructor simple + energy = std::vector(); + power = std::vector(); } -//MeterValues::MeterValues(LinkedList *sample_t, LinkedList *energyRegister) { -// sampleTime = LinkedList(); -// energy = LinkedList(); -// power = LinkedList(); //remains empty -// for (int i = 0; i < energyRegister->size(); i++) { -// sampleTime.add(sample_t->get(i)); -// energy.add((float) energyRegister->get(i)); -// } -//} - -MeterValues::MeterValues(LinkedList *sample_t, LinkedList *energyRegister, LinkedList *pwr, int connectorId, int transactionId) +MeterValues::MeterValues(std::vector *sampleTime, std::vector *energy, std::vector *power, int connectorId, int transactionId) : connectorId(connectorId), transactionId(transactionId) { - sampleTime = LinkedList(); - energy = LinkedList(); - power = LinkedList(); - for (int i = 0; i < sample_t->size(); i++) - sampleTime.add(sample_t->get(i)); - if (energyRegister != NULL) { - for (int i = 0; i < energyRegister->size(); i++) - energy.add(energyRegister->get(i)); - } - if (pwr != NULL) { - for (int i = 0; i < pwr->size(); i++) - power.add(pwr->get(i)); - } + if (sampleTime) + this->sampleTime = std::vector(*sampleTime); + else + this->sampleTime = std::vector(); + if (energy) + this->energy = std::vector(*energy); + else + this->energy = std::vector(); + if (power) + this->power = std::vector(*power); + else + this->power = std::vector(); } MeterValues::~MeterValues(){ - sampleTime.clear(); - energy.clear(); - power.clear(); + } const char* MeterValues::getOcppOperationType(){ @@ -56,67 +43,68 @@ const char* MeterValues::getOcppOperationType(){ DynamicJsonDocument* MeterValues::createReq() { - int numEntries = sampleTime.size(); - - DynamicJsonDocument *doc = new DynamicJsonDocument( - JSON_OBJECT_SIZE(2) //connectorID, transactionId - + JSON_ARRAY_SIZE(numEntries) //metervalue array - + numEntries * JSON_OBJECT_SIZE(1) //sampledValue - + numEntries * (JSON_OBJECT_SIZE(1) + (JSONDATE_LENGTH + 1)) //timestamp - + numEntries * JSON_ARRAY_SIZE(2) //sampledValue - + 2 * numEntries * JSON_OBJECT_SIZE(1) //value // why are these taken by two? - + 2 * numEntries * JSON_OBJECT_SIZE(1) //measurand // - + 2 * numEntries * JSON_OBJECT_SIZE(1) //unit // - + 230); //"safety space" - JsonObject payload = doc->to(); - - payload["connectorId"] = connectorId; - JsonArray meterValues = payload.createNestedArray("meterValue"); - for (int i = 0; i < sampleTime.size(); i++) { - JsonObject meterValue = meterValues.createNestedObject(); - char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; - getJsonDateStringFromGivenTime(timestamp, JSONDATE_LENGTH + 1, sampleTime.get(i)); - meterValue["timestamp"] = timestamp; - JsonArray sampledValue = meterValue.createNestedArray("sampledValue"); - if (energy.size() - 1 >= i) { - JsonObject sampledValue_1 = sampledValue.createNestedObject(); - sampledValue_1["value"] = energy.get(i); - sampledValue_1["measurand"] = "Energy.Active.Import.Register"; - sampledValue_1["unit"] = "Wh"; - } - if (power.size() - 1 >= i) { - JsonObject sampledValue_2 = sampledValue.createNestedObject(); - sampledValue_2["value"] = power.get(i); - sampledValue_2["measurand"] = "Power.Active.Import"; - sampledValue_2["unit"] = "W"; + int numEntries = sampleTime.size(); + + DynamicJsonDocument *doc = new DynamicJsonDocument( + JSON_OBJECT_SIZE(2) //connectorID, transactionId + + JSON_ARRAY_SIZE(numEntries) //metervalue array + + numEntries * JSON_OBJECT_SIZE(1) //sampledValue + + numEntries * (JSON_OBJECT_SIZE(1) + (JSONDATE_LENGTH + 1)) //timestamp + + numEntries * JSON_ARRAY_SIZE(2) //sampledValue + + 2 * numEntries * JSON_OBJECT_SIZE(1) //value // why are these taken by two? + + 2 * numEntries * JSON_OBJECT_SIZE(1) //measurand // + + 2 * numEntries * JSON_OBJECT_SIZE(1) //unit // + + 230); //"safety space" + JsonObject payload = doc->to(); + + payload["connectorId"] = connectorId; + JsonArray meterValues = payload.createNestedArray("meterValue"); + for (size_t i = 0; i < sampleTime.size(); i++) { + JsonObject meterValue = meterValues.createNestedObject(); + char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; + OcppTimestamp otimestamp = sampleTime.at(i); + otimestamp.toJsonString(timestamp, JSONDATE_LENGTH + 1); + meterValue["timestamp"] = timestamp; + JsonArray sampledValue = meterValue.createNestedArray("sampledValue"); + if (energy.size() >= i + 1) { + JsonObject sampledValue_1 = sampledValue.createNestedObject(); + sampledValue_1["value"] = energy.at(i); + sampledValue_1["measurand"] = "Energy.Active.Import.Register"; + sampledValue_1["unit"] = "Wh"; + } + if (power.size() >= i + 1) { + JsonObject sampledValue_2 = sampledValue.createNestedObject(); + sampledValue_2["value"] = power.at(i); + sampledValue_2["measurand"] = "Power.Active.Import"; + sampledValue_2["unit"] = "W"; + } } - } - if (transactionId >= 0) { - payload["transactionId"] = transactionId; - } + if (transactionId >= 0) { + payload["transactionId"] = transactionId; + } - return doc; + return doc; } void MeterValues::processConf(JsonObject payload) { - if (DEBUG_OUT) Serial.print(F("[MeterValues] Request has been confirmed!\n")); + if (DEBUG_OUT) Serial.print(F("[MeterValues] Request has been confirmed!\n")); } void MeterValues::processReq(JsonObject payload) { - /** - * Ignore Contents of this Req-message, because this is for debug purposes only - */ + /** + * Ignore Contents of this Req-message, because this is for debug purposes only + */ } DynamicJsonDocument* MeterValues::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(0); - JsonObject payload = doc->to(); - /* - * empty payload - */ - return doc; + DynamicJsonDocument* doc = new DynamicJsonDocument(0); + //JsonObject payload = doc->to(); + /* + * empty payload + */ + return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.h b/src/ArduinoOcpp/MessagesV16/MeterValues.h index 9081d9d9..9eb5b43f 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.h +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -6,8 +6,7 @@ #define METERVALUES_H #include -#include -#include +#include namespace ArduinoOcpp { namespace Ocpp16 { @@ -15,17 +14,17 @@ namespace Ocpp16 { class MeterValues : public OcppMessage { private: - LinkedList sampleTime; - LinkedList power; - LinkedList energy; + std::vector sampleTime; + std::vector power; + std::vector energy; int connectorId = 0; int transactionId = -1; public: - MeterValues(LinkedList *sampleTime, LinkedList *energy); + MeterValues(std::vector *sampleTime, std::vector *energy); - MeterValues(LinkedList *sampleTime, LinkedList *energy, LinkedList *power, int connectorId, int transactionId); + MeterValues(std::vector *sampleTime, std::vector *energy, std::vector *power, int connectorId, int transactionId); MeterValues(); //for debugging only. Make this for the server pendant diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp index e015798c..9642e0fa 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp @@ -68,7 +68,7 @@ DynamicJsonDocument* RemoteStartTransaction::createReq() { DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); JsonObject payload = doc->to(); - payload["idTag"] = "fefed1d19876"; + payload["idTag"] = "A0-00-00-00"; return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h index e817d261..d2e21ff7 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp index ab509e23..fa626524 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h index fafc5f2b..d2baed91 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/MessagesV16/Reset.cpp b/src/ArduinoOcpp/MessagesV16/Reset.cpp index c9a1f288..53875d5a 100644 --- a/src/ArduinoOcpp/MessagesV16/Reset.cpp +++ b/src/ArduinoOcpp/MessagesV16/Reset.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/MessagesV16/Reset.h b/src/ArduinoOcpp/MessagesV16/Reset.h index fb5410e2..eff3a252 100644 --- a/src/ArduinoOcpp/MessagesV16/Reset.h +++ b/src/ArduinoOcpp/MessagesV16/Reset.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp index 00050350..97bc2094 100644 --- a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp +++ b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -19,11 +19,11 @@ const char* SetChargingProfile::getOcppOperationType(){ void SetChargingProfile::processReq(JsonObject payload) { - int connectorID = payload["connectorId"]; + //int connectorID = payload["connectorId"]; - JsonObject csChargingProfiles = payload["csChargingProfiles"]; + JsonObject csChargingProfiles = payload["csChargingProfiles"]; - smartChargingService->updateChargingProfile(&csChargingProfiles); + smartChargingService->updateChargingProfile(&csChargingProfiles); } DynamicJsonDocument* SetChargingProfile::createConf(){ //TODO review diff --git a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h index 9241daa1..c97f6c85 100644 --- a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h +++ b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp index 60bfc918..5cad09d3 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp @@ -1,9 +1,8 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License #include -#include #include #include @@ -11,84 +10,107 @@ using ArduinoOcpp::Ocpp16::StartTransaction; -/* -StartTransaction::StartTransaction() { - if (getChargePointStatusService() != NULL) { - if (!getChargePointStatusService()->getIdTag().isEmpty()) { - idTag = String(getChargePointStatusService()->getIdTag()); - } - } - if (idTag.isEmpty()) - idTag = String("fefed1d19876"); //Use a default payload. In the typical use case of this library, you probably you don't even need Authorization at all -} - -StartTransaction::StartTransaction(String &idTag) { - this->idTag = String(idTag); -}*/ - StartTransaction::StartTransaction(int connectorId) : connectorId(connectorId) { - ChargePointStatusService *cpss = getChargePointStatusService(); - if (cpss != NULL) { - if (cpss->existsUnboundAuthorization()) { - this->idTag = String(cpss->getUnboundIdTag()); - } else { - //The CP is not authorized. Try anyway, let the CS decide what to do ... - this->idTag = String("fefed1d19876"); - } - } else { - this->idTag = String("fefed1d19876"); //Use a default payload. In the typical use case of this library, you probably you don't even need Authorization at all - } + this->idTag = String('\0'); } StartTransaction::StartTransaction(int connectorId, String &idTag) : connectorId(connectorId) { - this->idTag = String(idTag); + this->idTag = String(idTag); } const char* StartTransaction::getOcppOperationType(){ return "StartTransaction"; } -DynamicJsonDocument* StartTransaction::createReq() { - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(5) + (JSONDATE_LENGTH + 1) + (idTag.length() + 1)); - JsonObject payload = doc->to(); - - payload["connectorId"] = connectorId; - MeteringService* meteringService = getMeteringService(); - if (meteringService != NULL) { - payload["meterStart"] = meteringService->readEnergyActiveImportRegister(connectorId); - } - char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; - getJsonDateStringFromGivenTime(timestamp, JSONDATE_LENGTH + 1, now()); - payload["timestamp"] = timestamp; - payload["idTag"] = idTag; - - return doc; -} +void StartTransaction::initiate() { + MeteringService* meteringService = getMeteringService(); + if (meteringService != NULL) { + meterStart = meteringService->readEnergyActiveImportRegister(connectorId); + } -void StartTransaction::processConf(JsonObject payload) { + OcppTime *ocppTime = getOcppTime(); + if (ocppTime) { + otimestamp = ocppTime->getOcppTimestampNow(); + } else { + otimestamp = MIN_TIME; + } - const char* idTagInfoStatus = payload["idTagInfo"]["status"] | "Invalid"; - int transactionId = payload["transactionId"] | -1; + ChargePointStatusService *cpss = getChargePointStatusService(); + if (cpss != NULL) { + if (cpss->existsUnboundAuthorization()) { + this->idTag = String(cpss->getUnboundIdTag()); + } else { + //The CP is not authorized. Try anyway, let the CS decide what to do ... + this->idTag = String("A0-00-00-00"); + } + } else { + this->idTag = String("A0-00-00-00"); //Use a default payload. In the typical use case of this library, you probably you don't even need Authorization at all + } - if (!strcmp(idTagInfoStatus, "Accepted")) { - if (DEBUG_OUT) Serial.print(F("[StartTransaction] Request has been accepted!\n")); + if (idTag.isEmpty()) { + ChargePointStatusService *cpss = getChargePointStatusService(); + if (cpss != NULL && cpss->existsUnboundAuthorization()) { + this->idTag = String(cpss->getUnboundIdTag()); + } else { + //The CP is not authorized. Try anyway, let the CS decide what to do ... + this->idTag = String("A0-00-00-00"); //Use a default payload. In the typical use case of this library, you probably you don't even need Authorization at all + } + } ChargePointStatusService *cpStatusService = getChargePointStatusService(); + if (cpStatusService) { + cpStatusService->bindAuthorization(connectorId); + } + ConnectorStatus *connector = getConnectorStatus(connectorId); - if (cpStatusService != NULL && connector != NULL){ - cpStatusService->bindAuthorization(idTag, connectorId); - connector->startTransaction(transactionId); - connector->startEnergyOffer(); + if (connector != NULL){ + connector->setTransactionId(0); //pending + } + + + if (DEBUG_OUT) Serial.println(F("[StartTransaction] StartTransaction initiated!")); +} + +DynamicJsonDocument* StartTransaction::createReq() { + DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(5) + (JSONDATE_LENGTH + 1) + (idTag.length() + 1)); + JsonObject payload = doc->to(); + + payload["connectorId"] = connectorId; + if (meterStart >= 0.f) { + payload["meterStart"] = meterStart; } - SmartChargingService *scService = getSmartChargingService(); - if (scService != NULL){ - scService->beginChargingNow(); + if (otimestamp > MIN_TIME) { + char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; + otimestamp.toJsonString(timestamp, JSONDATE_LENGTH + 1); + payload["timestamp"] = timestamp; } - } else { - Serial.print(F("[StartTransaction] Request has been denied!\n")); - } + payload["idTag"] = idTag; + + return doc; +} + +void StartTransaction::processConf(JsonObject payload) { + + const char* idTagInfoStatus = payload["idTagInfo"]["status"] | "Invalid"; + int transactionId = payload["transactionId"] | -1; + + if (!strcmp(idTagInfoStatus, "Accepted")) { + if (DEBUG_OUT) Serial.print(F("[StartTransaction] Request has been accepted!\n")); + + ConnectorStatus *connector = getConnectorStatus(connectorId); + if (connector){ + connector->setTransactionId(transactionId); + } + } else { + Serial.print(F("[StartTransaction] Request has been denied!\n")); + ConnectorStatus *connector = getConnectorStatus(connectorId); + if (connector){ + connector->setTransactionId(-1); + connector->unauthorize(); + } + } } diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.h b/src/ArduinoOcpp/MessagesV16/StartTransaction.h index 7479d190..f70feb67 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -6,6 +6,7 @@ #define STARTTRANSACTION_H #include +#include #include @@ -15,6 +16,8 @@ namespace Ocpp16 { class StartTransaction : public OcppMessage { private: int connectorId = 1; + float meterStart = -1.0f; + OcppTimestamp otimestamp; String idTag = String('\0'); public: StartTransaction(int connectorId); @@ -23,6 +26,8 @@ class StartTransaction : public OcppMessage { const char* getOcppOperationType(); + void initiate(); + DynamicJsonDocument* createReq(); void processConf(JsonObject payload); diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp index 196ed23b..06d72603 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp @@ -1,62 +1,57 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License #include +#include #include #include using ArduinoOcpp::Ocpp16::StatusNotification; -StatusNotification::StatusNotification(int connectorId, OcppEvseState currentStatus) - : connectorId(connectorId), currentStatus(currentStatus) { - - if (!getJsonDateStringFromSystemTime(timestamp, JSONDATE_LENGTH)){ - Serial.print(F("[StatusNotification] Error reading time string. Expect format like 2020-02-01T20:53:32.486Z\n")); - } - - if (DEBUG_OUT) { - Serial.print(F("[StatusNotification] New StatusNotification with timestamp ")); - Serial.print(timestamp); - Serial.print(". New Status: "); - } - - switch (currentStatus) { - case (OcppEvseState::Available): - if (DEBUG_OUT) Serial.print(F("Available\n")); - break; - case (OcppEvseState::Preparing): - if (DEBUG_OUT) Serial.print(F("Preparing\n")); - break; - case (OcppEvseState::Charging): - if (DEBUG_OUT) Serial.print(F("Charging\n")); - break; - case (OcppEvseState::SuspendedEVSE): - if (DEBUG_OUT) Serial.print(F("SuspendedEVSE\n")); - break; - case (OcppEvseState::SuspendedEV): - if (DEBUG_OUT) Serial.print(F("SuspendedEV\n")); - break; - case (OcppEvseState::Finishing): - if (DEBUG_OUT) Serial.print(F("Finishing\n")); - break; - case (OcppEvseState::Reserved): - if (DEBUG_OUT) Serial.print(F("Reserved\n")); - break; - case (OcppEvseState::Unavailable): - if (DEBUG_OUT) Serial.print(F("Unavailable\n")); - break; - case (OcppEvseState::Faulted): - if (DEBUG_OUT) Serial.print(F("Faulted\n")); - break; - case (OcppEvseState::NOT_SET): - Serial.print(F("NOT_SET\n")); - break; - default: - Serial.print(F("[Error, invalid status code]\n")); - break; - } +StatusNotification::StatusNotification(int connectorId, OcppEvseState currentStatus, const OcppTimestamp &otimestamp) + : connectorId(connectorId), currentStatus(currentStatus), otimestamp(otimestamp) { + + if (DEBUG_OUT) { + Serial.print(F("[StatusNotification] New StatusNotification. New Status: ")); + } + + switch (currentStatus) { + case (OcppEvseState::Available): + if (DEBUG_OUT) Serial.print(F("Available\n")); + break; + case (OcppEvseState::Preparing): + if (DEBUG_OUT) Serial.print(F("Preparing\n")); + break; + case (OcppEvseState::Charging): + if (DEBUG_OUT) Serial.print(F("Charging\n")); + break; + case (OcppEvseState::SuspendedEVSE): + if (DEBUG_OUT) Serial.print(F("SuspendedEVSE\n")); + break; + case (OcppEvseState::SuspendedEV): + if (DEBUG_OUT) Serial.print(F("SuspendedEV\n")); + break; + case (OcppEvseState::Finishing): + if (DEBUG_OUT) Serial.print(F("Finishing\n")); + break; + case (OcppEvseState::Reserved): + if (DEBUG_OUT) Serial.print(F("Reserved\n")); + break; + case (OcppEvseState::Unavailable): + if (DEBUG_OUT) Serial.print(F("Unavailable\n")); + break; + case (OcppEvseState::Faulted): + if (DEBUG_OUT) Serial.print(F("Faulted\n")); + break; + case (OcppEvseState::NOT_SET): + Serial.print(F("NOT_SET\n")); + break; + default: + Serial.print(F("[Error, invalid status code]\n")); + break; + } } const char* StatusNotification::getOcppOperationType(){ @@ -65,49 +60,51 @@ const char* StatusNotification::getOcppOperationType(){ //TODO if the status has changed again when sendReq() is called, abort the operation completely (note: if req is already sent, stick with listening to conf). The OcppEvseStateService will enqueue a new operation itself DynamicJsonDocument* StatusNotification::createReq() { - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + (JSONDATE_LENGTH + 1)); - JsonObject payload = doc->to(); - - payload["connectorId"] = connectorId; - payload["errorCode"] = "NoError"; //No error diagnostics support - - switch (currentStatus) { - case (OcppEvseState::Available): - payload["status"] = "Available"; - break; - case (OcppEvseState::Preparing): - payload["status"] = "Preparing"; - break; - case (OcppEvseState::Charging): - payload["status"] = "Charging"; - break; - case (OcppEvseState::SuspendedEVSE): - payload["status"] = "SuspendedEVSE"; - break; - case (OcppEvseState::SuspendedEV): - payload["status"] = "SuspendedEV"; - break; - case (OcppEvseState::Finishing): - payload["status"] = "Finishing"; - break; - case (OcppEvseState::Reserved): - payload["status"] = "Reserved"; - break; - case (OcppEvseState::Unavailable): - payload["status"] = "Unavailable"; - break; - case (OcppEvseState::Faulted): - payload["status"] = "Faulted"; - break; - default: - payload["status"] = "NOT_SET"; - Serial.print(F("[StatusNotification] Error: Sending status NOT_SET!\n")); - break; - } - - payload["timestamp"] = timestamp; - - return doc; + DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + (JSONDATE_LENGTH + 1)); + JsonObject payload = doc->to(); + + payload["connectorId"] = connectorId; + payload["errorCode"] = "NoError"; //No error diagnostics support + + switch (currentStatus) { + case (OcppEvseState::Available): + payload["status"] = "Available"; + break; + case (OcppEvseState::Preparing): + payload["status"] = "Preparing"; + break; + case (OcppEvseState::Charging): + payload["status"] = "Charging"; + break; + case (OcppEvseState::SuspendedEVSE): + payload["status"] = "SuspendedEVSE"; + break; + case (OcppEvseState::SuspendedEV): + payload["status"] = "SuspendedEV"; + break; + case (OcppEvseState::Finishing): + payload["status"] = "Finishing"; + break; + case (OcppEvseState::Reserved): + payload["status"] = "Reserved"; + break; + case (OcppEvseState::Unavailable): + payload["status"] = "Unavailable"; + break; + case (OcppEvseState::Faulted): + payload["status"] = "Faulted"; + break; + default: + payload["status"] = "NOT_SET"; + Serial.print(F("[StatusNotification] Error: Sending status NOT_SET!\n")); + break; + } + + char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; + otimestamp.toJsonString(timestamp, JSONDATE_LENGTH + 1); + payload["timestamp"] = timestamp; + + return doc; } @@ -122,7 +119,7 @@ void StatusNotification::processConf(JsonObject payload) { * For debugging only */ StatusNotification::StatusNotification() { - + otimestamp = OcppTimestamp(); } /* @@ -137,6 +134,6 @@ void StatusNotification::processReq(JsonObject payload) { */ DynamicJsonDocument* StatusNotification::createConf(){ DynamicJsonDocument* doc = new DynamicJsonDocument(0); - JsonObject payload = doc->to(); + //JsonObject payload = doc->to(); return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.h b/src/ArduinoOcpp/MessagesV16/StatusNotification.h index 06cea405..629c9d4b 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.h +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -6,31 +6,31 @@ #define STATUSNOTIFICATION_H #include -#include #include +#include namespace ArduinoOcpp { namespace Ocpp16 { class StatusNotification : public OcppMessage { private: - int connectorId = 1; - OcppEvseState currentStatus = OcppEvseState::NOT_SET; - char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; + int connectorId = 1; + OcppEvseState currentStatus = OcppEvseState::NOT_SET; + OcppTimestamp otimestamp; public: - StatusNotification(int connectorId, OcppEvseState currentStatus); + StatusNotification(int connectorId, OcppEvseState currentStatus, const OcppTimestamp &otimestamp); - StatusNotification(); + StatusNotification(); - const char* getOcppOperationType(); + const char* getOcppOperationType(); - DynamicJsonDocument* createReq(); + DynamicJsonDocument* createReq(); - void processConf(JsonObject payload); + void processConf(JsonObject payload); - void processReq(JsonObject payload); + void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + DynamicJsonDocument* createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp index 4b5cf3cc..59d58585 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -21,39 +21,42 @@ const char* StopTransaction::getOcppOperationType(){ return "StopTransaction"; } -DynamicJsonDocument* StopTransaction::createReq() { - - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + (JSONDATE_LENGTH + 1)); - JsonObject payload = doc->to(); +void StopTransaction::initiate() { - float meterStop = 0.0f; if (getMeteringService() != NULL) { meterStop = getMeteringService()->readEnergyActiveImportRegister(connectorId); } - payload["meterStop"] = meterStop; //TODO meterStart is required to be in Wh, but measuring unit is probably inconsistent in implementation - char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; - getJsonDateStringFromSystemTime(timestamp, JSONDATE_LENGTH); - payload["timestamp"] = timestamp; + OcppTime *ocppTime = getOcppTime(); + if (ocppTime) { + otimestamp = ocppTime->getOcppTimestampNow(); + } else { + otimestamp = MIN_TIME; + } ConnectorStatus *connector = getConnectorStatus(connectorId); - if (connector != NULL) { - if (connector->getTransactionId() >= 0) { - //Connector is in a transaction (transactionId > 0) or in an undefinded state (transactionId == 0). + if (connector != NULL){ + transactionId = connector->getTransactionId(); //for req message + connector->setTransactionId(-1); //immediate end of transaction + connector->unauthorize(); + } - //End the charging session in each module. + if (DEBUG_OUT) Serial.println(F("[StartTransaction] StopTransaction initiated!")); - transactionId = connector->getTransactionId(); +} + +DynamicJsonDocument* StopTransaction::createReq() { + + DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + (JSONDATE_LENGTH + 1)); + JsonObject payload = doc->to(); - connector->stopEnergyOffer(); - connector->stopTransaction(); - connector->unauthorize(); + if (meterStop >= 0.f) + payload["meterStop"] = meterStop; //TODO meterStart is required to be in Wh, but measuring unit is probably inconsistent in implementation - SmartChargingService *scService = getSmartChargingService(); - if (scService != NULL){ - scService->endChargingNow(); - } - } // else: Connector says that is is not involved in a transaction (anymore). It has been ended before, so do nothing + if (otimestamp > MIN_TIME) { + char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; + otimestamp.toJsonString(timestamp, JSONDATE_LENGTH + 1); + payload["timestamp"] = timestamp; } payload["transactionId"] = transactionId; diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.h b/src/ArduinoOcpp/MessagesV16/StopTransaction.h index 0ee1c3e2..7df83ccd 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -6,28 +6,33 @@ #define STOPTRANSACTION_H #include +#include namespace ArduinoOcpp { namespace Ocpp16 { class StopTransaction : public OcppMessage { private: - int connectorId = 1; - int transactionId = -1; + int connectorId = 1; + float meterStop = -1.0f; + OcppTimestamp otimestamp; + int transactionId = -1; public: - StopTransaction(); + StopTransaction(); - StopTransaction(int connectorId); + StopTransaction(int connectorId); - const char* getOcppOperationType(); + const char* getOcppOperationType(); - DynamicJsonDocument* createReq(); + void initiate(); - void processConf(JsonObject payload); + DynamicJsonDocument* createReq(); - void processReq(JsonObject payload); + void processConf(JsonObject payload); - DynamicJsonDocument* createConf(); + void processReq(JsonObject payload); + + DynamicJsonDocument* createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp index 9590ede8..98fe9e99 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h index 89bd6dd4..c8c269b6 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index db4c26d0..90ebeb3f 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.h b/src/ArduinoOcpp/SimpleOcppOperationFactory.h index 8e23e26d..4aa2aabd 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.h +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index 29726f7a..9653ea55 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -14,85 +14,92 @@ using namespace ArduinoOcpp; using namespace ArduinoOcpp::Ocpp16; -ChargePointStatusService::ChargePointStatusService(WebSocketsClient *webSocket, int numConn) - : webSocket(webSocket), numConnectors(numConn) { +ChargePointStatusService::ChargePointStatusService(WebSocketsClient *webSocket, int numConn, OcppTime *ocppTime) + : webSocket(webSocket), numConnectors(numConn), ocppTime(ocppTime) { - connectors = (ConnectorStatus**) malloc(numConn * sizeof(ConnectorStatus*)); - for (int i = 0; i < numConn; i++) { - connectors[i] = new ConnectorStatus(i); - } + connectors = (ConnectorStatus**) malloc(numConn * sizeof(ConnectorStatus*)); + for (int i = 0; i < numConn; i++) { + connectors[i] = new ConnectorStatus(i, ocppTime); + } - setChargePointStatusService(this); + setChargePointStatusService(this); } ChargePointStatusService::~ChargePointStatusService() { - for (int i = 0; i < numConnectors; i++) { - delete connectors[i]; - } - free(connectors); + for (int i = 0; i < numConnectors; i++) { + delete connectors[i]; + } + free(connectors); } void ChargePointStatusService::loop() { - if (!booted) return; - for (int i = 0; i < numConnectors; i++){ - StatusNotification *statusNotificationMsg = connectors[i]->loop(); - if (statusNotificationMsg != NULL) { - OcppOperation *statusNotification = makeOcppOperation(statusNotificationMsg); - initiateOcppOperation(statusNotification); + if (!booted) return; + for (int i = 0; i < numConnectors; i++){ + StatusNotification *statusNotificationMsg = connectors[i]->loop(); + if (statusNotificationMsg != NULL) { + OcppOperation *statusNotification = makeOcppOperation(statusNotificationMsg); + initiateOcppOperation(statusNotification); + } } - } } ConnectorStatus *ChargePointStatusService::getConnector(int connectorId) { - if (connectorId < 0 || connectorId >= numConnectors) { - Serial.print(F("[ChargePointStatusService] Error in getConnector(connectorId): connectorId is out of bounds\n")); - return NULL; - } + if (connectorId < 0 || connectorId >= numConnectors) { + Serial.print(F("[ChargePointStatusService] Error in getConnector(connectorId): connectorId is out of bounds\n")); + return NULL; + } - return connectors[connectorId]; + return connectors[connectorId]; } void ChargePointStatusService::authorize(String &idTag){ - this->idTag = String(idTag); - authorize(); + this->idTag = String(idTag); + authorize(); } void ChargePointStatusService::authorize(){ - if (authorized == true){ - if (DEBUG_OUT) Serial.print(F("[ChargePointStatusService] Warning: authorized twice or didn't unauthorize before\n")); - } - authorized = true; + if (authorized == true){ + if (DEBUG_OUT) Serial.print(F("[ChargePointStatusService] Warning: authorized twice or didn't unauthorize before\n")); + } + authorized = true; } void ChargePointStatusService::boot() { - booted = true; + booted = true; } String &ChargePointStatusService::getUnboundIdTag() { - return idTag; + return idTag; +} + +void ChargePointStatusService::invalidateUnboundIdTag() { + authorized = false; + idTag = String('\0'); } boolean ChargePointStatusService::existsUnboundAuthorization() { - return authorized; + return authorized; } -void ChargePointStatusService::bindAuthorization(String &idTag, int connectorId) { - if (connectorId < 0 || connectorId >= numConnectors) { - Serial.print(F("[ChargePointStatusService] Error in bindAuthorization(connectorId): connectorId is out of bounds\n")); - return; - } - if (DEBUG_OUT) Serial.print(F("[ChargePointStatusService] Connector ")); - if (DEBUG_OUT) Serial.print(connectorId); - if (DEBUG_OUT) Serial.print(F(" occupies idTag ")); - if (DEBUG_OUT) Serial.print(idTag); - if (DEBUG_OUT) Serial.print(F("\n")); - - connectors[connectorId]->authorize(idTag); - - authorized = false; - idTag = String('\0'); +void ChargePointStatusService::bindAuthorization(int connectorId) { + if (connectorId < 0 || connectorId >= numConnectors) { + Serial.print(F("[ChargePointStatusService] Error in bindAuthorization(connectorId): connectorId is out of bounds\n")); + return; + } + if (!authorized) { + if (DEBUG_OUT) Serial.print(F("[ChargePointStatusService] Authorize connector though there is no unbound ID tag\n")); + } + if (DEBUG_OUT) Serial.print(F("[ChargePointStatusService] Connector ")); + if (DEBUG_OUT) Serial.print(connectorId); + if (DEBUG_OUT) Serial.print(F(" occupies idTag ")); + if (DEBUG_OUT) Serial.print(idTag); + if (DEBUG_OUT) Serial.print(F("\n")); + + connectors[connectorId]->authorize(idTag); + + authorized = false; } int ChargePointStatusService::getNumConnectors() { - return numConnectors; + return numConnectors; } diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h index 02adc190..da770d2c 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -6,7 +6,6 @@ #define CHARGEPOINTSTATUSSERVICE_H #include -#include #include @@ -14,31 +13,33 @@ namespace ArduinoOcpp { class ChargePointStatusService { private: - WebSocketsClient *webSocket; - const int numConnectors; - ConnectorStatus **connectors; + WebSocketsClient *webSocket; + const int numConnectors; + OcppTime *ocppTime; + ConnectorStatus **connectors; - bool booted = false; + bool booted = false; - boolean authorized = false; - String idTag = String('\0'); + boolean authorized = false; + String idTag = String('\0'); public: - ChargePointStatusService(WebSocketsClient *webSocket, int numConnectors); - - ~ChargePointStatusService(); - - void loop(); - - void authorize(String &idTag); - void authorize(); - void boot(); - String &getUnboundIdTag(); - boolean existsUnboundAuthorization(); - void bindAuthorization(String &idTag, int connectorId); - - ConnectorStatus *getConnector(int connectorId); - int getNumConnectors(); + ChargePointStatusService(WebSocketsClient *webSocket, int numConnectors, OcppTime *ocppTime); + + ~ChargePointStatusService(); + + void loop(); + + void authorize(String &idTag); + void authorize(); + void boot(); + String &getUnboundIdTag(); + void invalidateUnboundIdTag(); + boolean existsUnboundAuthorization(); + void bindAuthorization(int connectorId); + + ConnectorStatus *getConnector(int connectorId); + int getNumConnectors(); }; } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 3ef2ab92..81fc513b 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -11,7 +11,7 @@ using namespace ArduinoOcpp; using namespace ArduinoOcpp::Ocpp16; -ConnectorStatus::ConnectorStatus(int connectorId) : connectorId(connectorId) { +ConnectorStatus::ConnectorStatus(int connectorId, OcppTime *ocppTime) : connectorId(connectorId), ocppTime(ocppTime) { //Set default transaction ID in memory String key = "OCPP_STATE_TRANSACTION_ID_CONNECTOR_"; @@ -33,17 +33,17 @@ OcppEvseState ConnectorStatus::inferenceStatus() { if (!authorized && !getChargePointStatusService()->existsUnboundAuthorization()) { return OcppEvseState::Available; - } else if (!transactionRunning) { + } else if (((int) *transactionId) < 0) { return OcppEvseState::Preparing; } else { //Transaction is currently running if (!evDrawsEnergy) { - return OcppEvseState::SuspendedEV; + return OcppEvseState::SuspendedEV; } if (!evseOffersEnergy) { - return OcppEvseState::SuspendedEVSE; + return OcppEvseState::SuspendedEVSE; } - return OcppEvseState::Charging; + return OcppEvseState::Charging; } } @@ -57,7 +57,7 @@ StatusNotification *ConnectorStatus::loop() { //fire StatusNotification //TODO check for online condition: Only inform CS about status change if CP is online //TODO check for too short duration condition: Only inform CS about status change if it lasted for longer than MinimumStatusDuration - return new StatusNotification(connectorId, currentStatus); + return new StatusNotification(connectorId, currentStatus, ocppTime->getOcppTimestampNow()); } return NULL; @@ -87,30 +87,17 @@ void ConnectorStatus::unauthorize(){ idTag = String('\0'); } -void ConnectorStatus::startTransaction(int transId){ - if (transactionRunning == true){ - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: started transaction twice or didn't stop transaction before\n")); - } - *transactionId = transId; - transactionRunning = true; - - saveState(); -} - -void ConnectorStatus::stopTransaction(){ - if (transactionRunning == false){ - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: stopped transaction twice or didn't start transaction before\n")); - } - transactionRunning = false; - *transactionId = -1; - - saveState(); -} - int ConnectorStatus::getTransactionId() { return *transactionId; } +void ConnectorStatus::setTransactionId(int id) { + int prevTxId = *transactionId; + *transactionId = id; + if (id != 0 || prevTxId > 0) + saveState(); +} + void ConnectorStatus::startEvDrawsEnergy(){ if (evDrawsEnergy == true){ if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: startEvDrawsEnergy called twice or didn't call stopEvDrawsEnergy before\n")); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index ea4ac88e..eeeda178 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -14,6 +14,7 @@ namespace ArduinoOcpp { class ConnectorStatus { private: const int connectorId; + OcppTime *ocppTime; bool authorized = false; String idTag = String('\0'); @@ -24,16 +25,15 @@ class ConnectorStatus { bool evseOffersEnergy = false; OcppEvseState currentStatus = OcppEvseState::NOT_SET; public: - ConnectorStatus(int connectorId); + ConnectorStatus(int connectorId, OcppTime *ocppTime); //boolean requestAuthorization(); void authorize(); void authorize(String &idTag); void unauthorize(); String &getIdTag(); - void startTransaction(int transactionId); - void stopTransaction(); int getTransactionId(); + void setTransactionId(int id); void boot(); void startEvDrawsEnergy(); void stopEvDrawsEnergy(); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h index 16bcf987..e05f79b8 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index 1a6e8bee..c3f766ba 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -10,9 +10,9 @@ using namespace ArduinoOcpp::Ocpp16; ConnectorMeterValuesRecorder::ConnectorMeterValuesRecorder(int connectorId, OcppTime *ocppTime) : connectorId(connectorId), ocppTime(ocppTime) { - sampleTimestamp = LinkedList(); - energy = LinkedList(); - power = LinkedList(); + sampleTimestamp = std::vector(); + energy = std::vector(); + power = std::vector(); MeterValueSampleInterval = declareConfiguration("MeterValueSampleInterval", 60); MeterValuesSampledDataMaxLength = declareConfiguration("MeterValuesSampledDataMaxLength", 4); @@ -20,15 +20,16 @@ ConnectorMeterValuesRecorder::ConnectorMeterValuesRecorder(int connectorId, Ocpp void ConnectorMeterValuesRecorder::takeSample() { if (energySampler != NULL || powerSampler != NULL) { - sampleTimestamp.add(now()); + if (!ocppTime->isValid()) return; + sampleTimestamp.push_back(ocppTime->getOcppTimestampNow()); } if (energySampler != NULL) { - energy.add(energySampler()); + energy.push_back(energySampler()); } if (powerSampler != NULL) { - power.add(powerSampler()); + power.push_back(powerSampler()); } } @@ -52,16 +53,16 @@ MeterValues *ConnectorMeterValuesRecorder::loop() { * If no powerSampler is available, estimate the energy consumption taking the Charging Schedule and CP Status * into account. */ - if (now() >= (time_t) *MeterValueSampleInterval + lastSampleTime) { + if (millis() - lastSampleTime >= (ulong) (*MeterValueSampleInterval * 1000)) { takeSample(); - lastSampleTime = now(); + lastSampleTime = millis(); } /* * Is the value buffer already full? If yes, return MeterValues message */ - if (sampleTimestamp.size() >= *MeterValuesSampledDataMaxLength) { + if (((int) sampleTimestamp.size()) >= (int) *MeterValuesSampledDataMaxLength) { MeterValues *result = toMeterValues(); return result; } @@ -71,8 +72,8 @@ MeterValues *ConnectorMeterValuesRecorder::loop() { MeterValues *ConnectorMeterValuesRecorder::toMeterValues() { if (sampleTimestamp.size() == 0) { - //anything wrong here. Discard - Serial.print(F("[ConnectorMeterValuesRecorder] Try to send MeterValues without any data point. Ignore\n")); + //Switching from Non-Transaction to Transaction (or vice versa) without sample. Or anything wrong here. Discard + if (DEBUG_OUT) Serial.print(F("[ConnectorMeterValuesRecorder] Try to send MeterValues without any data point. Ignore\n")); clear(); return NULL; } diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h index bf74a645..1f26522f 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h @@ -9,10 +9,8 @@ //#define METER_VALUES_SAMPLED_DATA_MAX_LENGTH 4 //after 4 measurements, send the values to the CS -#include #include -#include #include #include #include @@ -29,23 +27,21 @@ class ConnectorMeterValuesRecorder { const int connectorId; OcppTime *ocppTime; - LinkedList sampleTimestamp; - LinkedList energy; - LinkedList power; - time_t lastSampleTime = 0; //0 means not charging right now + std::vector sampleTimestamp; + std::vector energy; + std::vector power; + ulong lastSampleTime = 0; //0 means not charging right now float lastPower; int lastTransactionId = -1; PowerSampler powerSampler = NULL; EnergySampler energySampler = NULL; - //ulong MeterValueSampleInterval = 60; //will be overwritten (see constructor) - //ulong MeterValuesSampledDataMaxLength = 4; //will be overwritten (see constructor) std::shared_ptr> MeterValueSampleInterval = NULL; std::shared_ptr> MeterValuesSampledDataMaxLength = NULL; void takeSample(); - Ocpp16::MeterValues *toMeterValues(); + Ocpp16::MeterValues *toMeterValues(); //returns message if connector has captured enough samples void clear(); public: ConnectorMeterValuesRecorder(int connectorId, OcppTime *ocppTime); diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp index 811a944f..ad0b4f2e 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp @@ -16,53 +16,53 @@ using namespace ArduinoOcpp::Ocpp16; MeteringService::MeteringService(WebSocketsClient *webSocket, int numConn, OcppTime *ocppTime) : webSocket(webSocket), numConnectors(numConn) { - connectors = (ConnectorMeterValuesRecorder**) malloc(numConn * sizeof(ConnectorMeterValuesRecorder*)); - for (int i = 0; i < numConn; i++) { - connectors[i] = new ConnectorMeterValuesRecorder(i, ocppTime); - } + connectors = (ConnectorMeterValuesRecorder**) malloc(numConn * sizeof(ConnectorMeterValuesRecorder*)); + for (int i = 0; i < numConn; i++) { + connectors[i] = new ConnectorMeterValuesRecorder(i, ocppTime); + } - setMeteringSerivce(this); //make MeteringService available through Ocpp Engine + setMeteringSerivce(this); //make MeteringService available through Ocpp Engine } MeteringService::~MeteringService() { - for (int i = 0; i < numConnectors; i++) { - delete connectors[i]; - } - free(connectors); + for (int i = 0; i < numConnectors; i++) { + delete connectors[i]; + } + free(connectors); } void MeteringService::loop(){ - for (int i = 0; i < numConnectors; i++){ - MeterValues *meterValuesMsg = connectors[i]->loop(); - if (meterValuesMsg != NULL) { - OcppOperation *meterValues = makeOcppOperation(meterValuesMsg); - meterValues->setTimeout(new FixedTimeout(120000)); - initiateOcppOperation(meterValues); + for (int i = 0; i < numConnectors; i++){ + MeterValues *meterValuesMsg = connectors[i]->loop(); + if (meterValuesMsg != NULL) { + OcppOperation *meterValues = makeOcppOperation(meterValuesMsg); + meterValues->setTimeout(new FixedTimeout(120000)); + initiateOcppOperation(meterValues); + } } - } } void MeteringService::setPowerSampler(int connectorId, PowerSampler ps){ - if (connectorId < 0 || connectorId >= numConnectors) { - Serial.print(F("[MeteringService] Error in setPowerSampler(): connectorId is out of bounds\n")); - return; - } - connectors[connectorId]->setPowerSampler(ps); + if (connectorId < 0 || connectorId >= numConnectors) { + Serial.print(F("[MeteringService] Error in setPowerSampler(): connectorId is out of bounds\n")); + return; + } + connectors[connectorId]->setPowerSampler(ps); } void MeteringService::setEnergySampler(int connectorId, EnergySampler es){ - if (connectorId < 0 || connectorId >= numConnectors) { - Serial.print(F("[MeteringService] Error in setEnergySampler(): connectorId is out of bounds\n")); - return; - } - connectors[connectorId]->setEnergySampler(es); + if (connectorId < 0 || connectorId >= numConnectors) { + Serial.print(F("[MeteringService] Error in setEnergySampler(): connectorId is out of bounds\n")); + return; + } + connectors[connectorId]->setEnergySampler(es); } float MeteringService::readEnergyActiveImportRegister(int connectorId) { - if (connectorId < 0 || connectorId >= numConnectors) { - Serial.print(F("[MeteringService] Error in readEnergyActiveImportRegister(): connectorId is out of bounds\n")); - return 0.f; - } - return connectors[connectorId]->readEnergyActiveImportRegister(); + if (connectorId < 0 || connectorId >= numConnectors) { + Serial.print(F("[MeteringService] Error in readEnergyActiveImportRegister(): connectorId is out of bounds\n")); + return 0.f; + } + return connectors[connectorId]->readEnergyActiveImportRegister(); } diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h index c7a8a892..464ce892 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h @@ -9,11 +9,9 @@ //#define METER_VALUES_SAMPLED_DATA_MAX_LENGTH 4 //after 4 measurements, send the values to the CS -#include #include #include -#include #include #include diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp index 718c9faa..cc208945 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp @@ -3,260 +3,275 @@ // MIT License #include +#include #include #include using namespace ArduinoOcpp; +const OcppTimestamp ArduinoOcpp::MIN_TIME = OcppTimestamp(2010, 0, 0, 0, 0, 0); +const OcppTimestamp ArduinoOcpp::MAX_TIME = OcppTimestamp(2037, 0, 0, 0, 0, 0); + ChargingSchedulePeriod::ChargingSchedulePeriod(JsonObject *json){ - startPeriod = (*json)["startPeriod"]; - limit = (*json)["limit"]; //one fractural digit at most - numberPhases = (*json)["numberPhases"] | -1; + startPeriod = (*json)["startPeriod"]; + limit = (*json)["limit"]; //one fractural digit at most + numberPhases = (*json)["numberPhases"] | -1; } int ChargingSchedulePeriod::getStartPeriod(){ - return this->startPeriod; + return this->startPeriod; } float ChargingSchedulePeriod::getLimit(){ - return this->limit; + return this->limit; } int ChargingSchedulePeriod::getNumberPhases(){ - Serial.print(F("[SmartChargingModel] Unsupported operation: ChargingSchedulePeriod::getNumberPhases(); No phase control implemented")); - return this->numberPhases; + Serial.print(F("[SmartChargingModel] Unsupported operation: ChargingSchedulePeriod::getNumberPhases(); No phase control implemented")); + return this->numberPhases; } void ChargingSchedulePeriod::printPeriod(){ - Serial.print(F(" startPeriod: ")); - Serial.print(startPeriod); - Serial.print(F("\n")); + Serial.print(F(" startPeriod: ")); + Serial.print(startPeriod); + Serial.print(F("\n")); - Serial.print(F(" limit: ")); - Serial.print(limit); - Serial.print(F("\n")); + Serial.print(F(" limit: ")); + Serial.print(limit); + Serial.print(F("\n")); - Serial.print(F(" numberPhases: ")); - Serial.print(numberPhases); - Serial.print(F("\n")); + Serial.print(F(" numberPhases: ")); + Serial.print(numberPhases); + Serial.print(F("\n")); } ChargingSchedule::ChargingSchedule(JsonObject *json, ChargingProfileKindType chargingProfileKind, RecurrencyKindType recurrencyKind) : chargingProfileKind(chargingProfileKind) , recurrencyKind(recurrencyKind) { - duration = (*json)["duration"] | -1; - if (!getTimeFromJsonDateString((*json)["startSchedule"] | "1970-01-01T00:00:00.000Z", &startSchedule)){ - //non-success - } - schedulingUnit = (*json)["scheduleUnit"] | 'W'; //either 'A' or 'W' - - chargingSchedulePeriod = LinkedList(); - JsonArray periodJsonArray = (*json)["chargingSchedulePeriod"]; - for (JsonObject periodJson : periodJsonArray) { - ChargingSchedulePeriod *period = new ChargingSchedulePeriod(&periodJson); - chargingSchedulePeriod.add(period); - } - - //Expecting sorted list of periods but specification doesn't garantuee it - chargingSchedulePeriod.sort([] (ChargingSchedulePeriod *&p1, ChargingSchedulePeriod *&p2) { - return p1->getStartPeriod() - p2->getStartPeriod(); - }); - - minChargingRate = (*json)["minChargingRate"] | -1.0f; + duration = (*json)["duration"] | -1; + startSchedule = OcppTimestamp(); + if (!startSchedule.setTime((*json)["startSchedule"] | "Invalid")) { + //non-success + startSchedule = MIN_TIME; + } + const char *unit = (*json)["scheduleUnit"] | "__Invalid"; + if (unit[0] == 'a' || unit[0] == 'A') { + schedulingUnit = 'A'; + } else { + schedulingUnit = 'W'; + } + + chargingSchedulePeriod = std::vector(); + JsonArray periodJsonArray = (*json)["chargingSchedulePeriod"]; + for (JsonObject periodJson : periodJsonArray) { + ChargingSchedulePeriod *period = new ChargingSchedulePeriod(&periodJson); + chargingSchedulePeriod.push_back(period); + } + + //Expecting sorted list of periods but specification doesn't garantuee it + std::sort(chargingSchedulePeriod.begin(), chargingSchedulePeriod.end(), [] (ChargingSchedulePeriod *p1, ChargingSchedulePeriod *p2) { + return p1->getStartPeriod() < p2->getStartPeriod(); + }); + + minChargingRate = (*json)["minChargingRate"] | -1.0f; } ChargingSchedule::~ChargingSchedule(){ - for (int i = 0; i < chargingSchedulePeriod.size(); i++) { - delete chargingSchedulePeriod.get(i); - } - chargingSchedulePeriod.clear(); + for (size_t i = 0; i < chargingSchedulePeriod.size(); i++) { + delete chargingSchedulePeriod.at(i); + } } float maximum(float f1, float f2){ - if (f1 < f2) { - return f2; - } else { - return f1; - } + if (f1 < f2) { + return f2; + } else { + return f1; + } } -bool ChargingSchedule::inferenceLimit(time_t t, time_t startOfCharging, float *limit, time_t *nextChange) { - time_t basis = 0; //point in time to which schedule-related times are relative - *nextChange = INFINITY_THLD; //defaulted to Infinity - switch (chargingProfileKind) { - case (ChargingProfileKindType::Absolute): - //check if schedule is not valid yet but begins in future - if (startSchedule > t) { - //not valid YET - *nextChange = startSchedule; - return false; - } - //If charging profile is absolute, prefer startSchedule as basis. If absent, use chargingStart instead. If absent, no - //behaviour is defined - if (startSchedule > 0) { - basis = startSchedule; - } else if (startOfCharging > 0 && startOfCharging < t) { - basis = startOfCharging; - } else { - Serial.print(F("[SmartChargingModel] Undefined behaviour: Inferencing limit from absolute profile, but neither startSchedule, nor start of charging are set! Abort\n")); - return false; - } - break; - case (ChargingProfileKindType::Recurring): - if (recurrencyKind == RecurrencyKindType::Daily) { - basis = t - ((t - startSchedule) % SECS_PER_DAY); - *nextChange = basis + SECS_PER_DAY; //constrain nextChange to basis + one day - } else if (recurrencyKind == RecurrencyKindType::Weekly) { - basis = t - ((t - startSchedule) % SECS_PER_WEEK); - *nextChange = basis + SECS_PER_WEEK; - } else { - Serial.print(F("[SmartChargingModel] Undefined behaviour: Recurring ChargingProfile but no RecurrencyKindType set! Assume \"Daily\"")); - basis = t - ((t - startSchedule) % SECS_PER_DAY); - *nextChange = basis + SECS_PER_DAY; - } - break; - case (ChargingProfileKindType::Relative): - //assumed, that it is relative to start of charging - //start of charging must be before t or equal to t - if (startOfCharging > t) { - //Relative charging profiles only work with a currently active charging session which is not the case here +bool ChargingSchedule::inferenceLimit(const OcppTimestamp &t, const OcppTimestamp &startOfCharging, float *limit, OcppTimestamp *nextChange) { + OcppTimestamp basis = OcppTimestamp(); //point in time to which schedule-related times are relative + *nextChange = MAX_TIME; //defaulted to Infinity + switch (chargingProfileKind) { + case (ChargingProfileKindType::Absolute): + //check if schedule is not valid yet but begins in future + if (startSchedule > t) { + //not valid YET + *nextChange = startSchedule; + return false; + } + //If charging profile is absolute, prefer startSchedule as basis. If absent, use chargingStart instead. If absent, no + //behaviour is defined + if (startSchedule > MIN_TIME) { + basis = startSchedule; + } else if (startOfCharging > MIN_TIME && startOfCharging < t) { + basis = startOfCharging; + } else { + Serial.print(F("[SmartChargingModel] Undefined behaviour: Inferencing limit from absolute profile, but neither startSchedule, nor start of charging are set! Abort\n")); + return false; + } + break; + case (ChargingProfileKindType::Recurring): + if (recurrencyKind == RecurrencyKindType::Daily) { + basis = t - ((t - startSchedule) % (24 * 3600)); + *nextChange = basis + (24 * 3600); //constrain nextChange to basis + one day + } else if (recurrencyKind == RecurrencyKindType::Weekly) { + basis = t - ((t - startSchedule) % (7 * 24 * 3600)); + *nextChange = basis + (7 * 24 * 3600); + } else { + Serial.print(F("[SmartChargingModel] Undefined behaviour: Recurring ChargingProfile but no RecurrencyKindType set! Assume \"Daily\"")); + basis = t - ((t - startSchedule) % (24 * 3600)); + *nextChange = basis + (24 * 3600); + } + break; + case (ChargingProfileKindType::Relative): + //assumed, that it is relative to start of charging + //start of charging must be before t or equal to t + if (startOfCharging > t) { + //Relative charging profiles only work with a currently active charging session which is not the case here + return false; + } + basis = startOfCharging; + break; + } + + if (t < basis) { //check for error + Serial.print(F("[SmartChargingModel] Error in SchmartChargingModel::inferenceLimit: time basis is smaller than t, but t must be >= basis\n")); return false; - } - basis = startOfCharging; - break; - } - - if (t < basis) { //check for error - Serial.print(F("[SmartChargingModel] Error in SchmartChargingModel::inferenceLimit: time basis is smaller than t, but t must be >= basis\n")); - return false; - } - - time_t t_toBasis = t - basis; + } + + int t_toBasis = t - basis; + + if (duration > 0){ + //duration is set + + //check if duration is exceeded and if yes, abort inferencing limit + //if no, the duration is an upper limit for the validity of the schedule + if (t_toBasis >= duration) { //"duration" is given relative to basis + return false; + } else { + if (*nextChange - basis > duration) { + *nextChange = basis + duration; + } + } + } - if (duration > 0){ - //duration is set + /* + * Work through the ChargingProfilePeriods here. If the right period was found, assign the limit parameter from it + * and make nextChange equal the beginning of the following period. If the right period is the last one, nextChange + * will remain the time determined before. + */ + float limit_res = -1.0f; //If limit_res is still -1 after the loop, the inference process failed + for (auto period = chargingSchedulePeriod.begin(); period != chargingSchedulePeriod.end(); period++) { + if ((*period)->getStartPeriod() > t_toBasis) { + // found the first period that comes after t_toBasis. + *nextChange = basis + (*period)->getStartPeriod(); + break; //The currently valid limit was set the iteration before + } + limit_res = (*period)->getLimit(); - //check if duration is exceeded and if yes, abort inferencing limit - //if no, the duration is an upper limit for the validity of the schedule - if (t_toBasis >= duration) { //"duration" is given relative to basis - return false; - } else { - *nextChange = minimum(*nextChange, basis + duration); } - } - - /* - * Work through the ChargingProfilePeriods here. If the right period was found, assign the limit parameter from it - * and make nextChange equal the beginning of the following period. If the right period is the last one, nextChange - * will remain the time determined before. - */ - float limit_res = -1.0f; //If limit_res is still -1 after the loop, the inference process failed - for (int i = 0; i < chargingSchedulePeriod.size(); i++){ - if (chargingSchedulePeriod.get(i)->getStartPeriod() > t_toBasis) { - // found the first period that comes after t_toBasis. - *nextChange = basis + chargingSchedulePeriod.get(i)->getStartPeriod(); - break; //The currently valid limit was set the iteration before + + if (limit_res >= 0.0f) { + *limit = maximum(limit_res, minChargingRate); + return true; + } else { + return false; //No limit was found. Either there is no ChargingProfilePeriod, or each period begins after t_toBasis } - limit_res = chargingSchedulePeriod.get(i)->getLimit(); - } - - if (limit_res >= 0.0f) { - *limit = maximum(limit_res, minChargingRate); - return true; - } else { - return false; //No limit was found. Either there is no ChargingProfilePeriod, or each period begins after t_toBasis - } } void ChargingSchedule::printSchedule(){ - Serial.print(F(" duration: ")); - Serial.print(duration); - Serial.print(F("\n")); - - Serial.print(F(" startSchedule: ")); - Serial.print(startSchedule); - Serial.print(F("\n")); + Serial.print(F(" duration: ")); + Serial.print(duration); + Serial.print(F("\n")); + + Serial.print(F(" startSchedule: ")); + char tmp[JSONDATE_LENGTH + 1] = {'\0'}; + startSchedule.toJsonString(tmp, JSONDATE_LENGTH + 1); + Serial.print(tmp); + Serial.print(F("\n")); - Serial.print(F(" schedulingUnit: ")); - Serial.print(schedulingUnit); - Serial.print(F("\n")); + Serial.print(F(" schedulingUnit: ")); + Serial.print(schedulingUnit); + Serial.print(F("\n")); - for (int i = 0; i < chargingSchedulePeriod.size(); i++){ - chargingSchedulePeriod.get(i)->printPeriod(); - } + for (auto period = chargingSchedulePeriod.begin(); period != chargingSchedulePeriod.end(); period++) { + (*period)->printPeriod(); + } - Serial.print(F(" minChargingRate: ")); - Serial.print(minChargingRate); - Serial.print(F("\n")); + Serial.print(F(" minChargingRate: ")); + Serial.print(minChargingRate); + Serial.print(F("\n")); } ChargingProfile::ChargingProfile(JsonObject *json){ - chargingProfileId = (*json)["chargingProfileId"]; - transactionId = (*json)["transactionId"] | -1; - stackLevel = (*json)["stackLevel"]; + chargingProfileId = (*json)["chargingProfileId"]; + transactionId = (*json)["transactionId"] | -1; + stackLevel = (*json)["stackLevel"]; + + if (DEBUG_OUT) { + Serial.print(F("[SmartChargingModel] ChargingProfile created with chargingProfileId = ")); + Serial.print(chargingProfileId); + Serial.print(F("\n")); + } + + const char *chargingProfilePurposeStr = (*json)["chargingProfilePurpose"] | "Invalid"; + if (DEBUG_OUT) { + Serial.print(F("[SmartChargingModel] chargingProfilePurposeStr=")); + Serial.print(chargingProfilePurposeStr); + Serial.print(F("\n")); + } + if (!strcmp(chargingProfilePurposeStr, "ChargePointMaxProfile")) { + chargingProfilePurpose = ChargingProfilePurposeType::ChargePointMaxProfile; + } else if (!strcmp(chargingProfilePurposeStr, "TxDefaultProfile")) { + chargingProfilePurpose = ChargingProfilePurposeType::TxDefaultProfile; + //} else if (!strcmp(chargingProfilePurposeStr, "TxProfile")) { + } else { + chargingProfilePurpose = ChargingProfilePurposeType::TxProfile; + } + const char *chargingProfileKindStr = (*json)["chargingProfileKind"] | "Invalid"; + if (!strcmp(chargingProfileKindStr, "Absolute")) { + chargingProfileKind = ChargingProfileKindType::Absolute; + } else if (!strcmp(chargingProfileKindStr, "Recurring")) { + chargingProfileKind = ChargingProfileKindType::Recurring; + //} else if (!strcmp(chargingProfileKindStr, "Relative")) { + } else { + chargingProfileKind = ChargingProfileKindType::Relative; + } + const char *recurrencyKindStr = (*json)["recurrencyKind"] | "Invalid"; + if (DEBUG_OUT) { + Serial.print(F("[SmartChargingModel] recurrencyKindStr=")); + Serial.print(recurrencyKindStr); + Serial.print('\n'); + } + if (!strcmp(recurrencyKindStr, "Daily")) { + recurrencyKind = RecurrencyKindType::Daily; + } else if (!strcmp(recurrencyKindStr, "Weekly")) { + recurrencyKind = RecurrencyKindType::Weekly; + } else { + recurrencyKind = RecurrencyKindType::NOT_SET; //not part of OCPP 1.6 + } - if (DEBUG_OUT) { - Serial.print(F("[SmartChargingModel] ChargingProfile created with chargingProfileId = ")); - Serial.print(chargingProfileId); - Serial.print(F("\n")); - } - - const char *chargingProfilePurposeStr = (*json)["chargingProfilePurpose"] | "Invalid"; - if (DEBUG_OUT) { - Serial.print(F("[SmartChargingModel] chargingProfilePurposeStr=")); - Serial.print(chargingProfilePurposeStr); - Serial.print(F("\n")); - } - if (!strcmp(chargingProfilePurposeStr, "ChargePointMaxProfile")) { - chargingProfilePurpose = ChargingProfilePurposeType::ChargePointMaxProfile; - } else if (!strcmp(chargingProfilePurposeStr, "TxDefaultProfile")) { - chargingProfilePurpose = ChargingProfilePurposeType::TxDefaultProfile; - //} else if (!strcmp(chargingProfilePurposeStr, "TxProfile")) { - } else { - chargingProfilePurpose = ChargingProfilePurposeType::TxProfile; - } - const char *chargingProfileKindStr = (*json)["chargingProfileKind"] | "Invalid"; - if (!strcmp(chargingProfileKindStr, "Absolute")) { - chargingProfileKind = ChargingProfileKindType::Absolute; - } else if (!strcmp(chargingProfileKindStr, "Recurring")) { - chargingProfileKind = ChargingProfileKindType::Recurring; - //} else if (!strcmp(chargingProfileKindStr, "Relative")) { - } else { - chargingProfileKind = ChargingProfileKindType::Relative; - } - const char *recurrencyKindStr = (*json)["recurrencyKind"] | "Invalid"; - if (DEBUG_OUT) { - Serial.print(F("[SmartChargingModel] recurrencyKindStr=")); - Serial.print(recurrencyKindStr); - Serial.print('\n'); - } - if (!strcmp(recurrencyKindStr, "Daily")) { - recurrencyKind = RecurrencyKindType::Daily; - } else if (!strcmp(recurrencyKindStr, "Weekly")) { - recurrencyKind = RecurrencyKindType::Weekly; - } else { - recurrencyKind = RecurrencyKindType::NOT_SET; //not part of OCPP 1.6 - } - - const char *validFromStr = (*json)["validFrom"] | "1970-01-01T00:00:00.000Z"; - if (!getTimeFromJsonDateString(validFromStr, &validFrom)){ - //non-success - Serial.print(F("[SmartChargingModel] Error reading validFrom. Expect format like 2020-02-01T20:53:32.486Z. Assume year 1970 for now\n")); - } - - const char *validToStr = (*json)["validTo"] | INFINITY_STRING; - if (!getTimeFromJsonDateString(validToStr, &validTo)){ - //non-success - Serial.print(F("[SmartChargingModel] Error reading validTo. Expect format like 2020-02-01T20:53:32.486Z. Assume year 2037 for now\n")); - } - - JsonObject schedule = (*json)["chargingSchedule"]; - chargingSchedule = new ChargingSchedule(&schedule, chargingProfileKind, recurrencyKind); + if (!validFrom.setTime((*json)["validFrom"] | "Invalid")) { + //non-success + Serial.print(F("[SmartChargingModel] Error reading validFrom. Expect format like 2020-02-01T20:53:32.486Z. Assume unlimited validity\n")); + validFrom = MIN_TIME; + } + + if (!validTo.setTime((*json)["validTo"] | "Invalid")) { + //non-success + Serial.print(F("[SmartChargingModel] Error reading validTo. Expect format like 2020-02-01T20:53:32.486Z. Assume unlimited validity\n")); + validTo = MIN_TIME; + } + + JsonObject schedule = (*json)["chargingSchedule"]; + chargingSchedule = new ChargingSchedule(&schedule, chargingProfileKind, recurrencyKind); } ChargingProfile::~ChargingProfile(){ - delete chargingSchedule; + delete chargingSchedule; } /** @@ -267,113 +282,114 @@ ChargingProfile::~ChargingProfile(){ * = remainder: 2 0 1 2 0 1 2 0 1 */ int modulo(int dividend, int divisor){ - int remainder = dividend; - while (remainder < 0) { - remainder += divisor; - } - while (remainder >= divisor) { - remainder -= divisor; - } - return remainder; + int remainder = dividend; + while (remainder < 0) { + remainder += divisor; + } + while (remainder >= divisor) { + remainder -= divisor; + } + return remainder; } -bool ChargingProfile::inferenceLimit(time_t t, time_t startOfCharging, float *limit, time_t *nextChange){ - if (t > validTo && validTo > 0) { - *nextChange = INFINITY_THLD; - return false; //no limit defined - } - if (t < validFrom) { - *nextChange = validFrom; - return false; //no limit defined - } - - return chargingSchedule->inferenceLimit(t, startOfCharging, limit, nextChange); +bool ChargingProfile::inferenceLimit(const OcppTimestamp &t, const OcppTimestamp &startOfCharging, float *limit, OcppTimestamp *nextChange){ + if (t > validTo && validTo > MIN_TIME) { + *nextChange = MAX_TIME; + return false; //no limit defined + } + if (t < validFrom) { + *nextChange = validFrom; + return false; //no limit defined + } + + return chargingSchedule->inferenceLimit(t, startOfCharging, limit, nextChange); } -bool ChargingProfile::inferenceLimit(time_t t, float *limit, time_t *nextChange){ - return inferenceLimit(t, INFINITY_THLD, limit, nextChange); +bool ChargingProfile::inferenceLimit(const OcppTimestamp &t, float *limit, OcppTimestamp *nextChange){ + return inferenceLimit(t, MAX_TIME, limit, nextChange); } bool ChargingProfile::checkTransactionId(int chargingSessionTransactionID) { - if (transactionId >= 0 && chargingSessionTransactionID >= 0){ - //Transaction IDs are valid - if (chargingProfilePurpose == ChargingProfilePurposeType::TxProfile //only then a transactionId can restrict the limits - && transactionId != chargingSessionTransactionID) { - return false; + if (transactionId >= 0 && chargingSessionTransactionID >= 0){ + //Transaction IDs are valid + if (chargingProfilePurpose == ChargingProfilePurposeType::TxProfile //only then a transactionId can restrict the limits + && transactionId != chargingSessionTransactionID) { + return false; + } } - } - return true; + return true; } int ChargingProfile::getStackLevel(){ - return stackLevel; + return stackLevel; } ChargingProfilePurposeType ChargingProfile::getChargingProfilePurpose(){ - return chargingProfilePurpose; + return chargingProfilePurpose; } void ChargingProfile::printProfile(){ - Serial.print(F(" chargingProfileId: ")); - Serial.print(chargingProfileId); - Serial.print(F("\n")); - - Serial.print(F(" transactionId: ")); - Serial.print(transactionId); - Serial.print(F("\n")); - - Serial.print(F(" stackLevel: ")); - Serial.print(stackLevel); - Serial.print(F("\n")); - - Serial.print(F(" chargingProfilePurpose: ")); - switch (chargingProfilePurpose) { - case (ChargingProfilePurposeType::ChargePointMaxProfile): - Serial.print(F("ChargePointMaxProfile")); - break; - case (ChargingProfilePurposeType::TxDefaultProfile): - Serial.print(F("TxDefaultProfile")); - break; - case (ChargingProfilePurposeType::TxProfile): - Serial.print(F("TxProfile")); - break; - } - Serial.print(F("\n")); - - Serial.print(F(" chargingProfileKind: ")); - switch (chargingProfileKind) { - case (ChargingProfileKindType::Absolute): - Serial.print(F("Absolute")); - break; - case (ChargingProfileKindType::Recurring): - Serial.print(F("Recurring")); - break; - case (ChargingProfileKindType::Relative): - Serial.print(F("Relative")); - break; - } - Serial.print(F("\n")); - - Serial.print(F(" recurrencyKind: ")); - switch (recurrencyKind) { - case (RecurrencyKindType::Daily): - Serial.print(F("Daily")); - break; - case (RecurrencyKindType::Weekly): - Serial.print(F("Weekly")); - break; - case (RecurrencyKindType::NOT_SET): - Serial.print(F("NOT_SET")); - break; - } - Serial.print(F("\n")); - - Serial.print(F(" validFrom: ")); - printTime(validFrom); - Serial.print(F("\n")); - - Serial.print(F(" validTo: ")); - printTime(validTo); - Serial.print(F("\n")); - chargingSchedule->printSchedule(); + Serial.print(F(" chargingProfileId: ")); + Serial.print(chargingProfileId); + Serial.print(F("\n")); + + Serial.print(F(" transactionId: ")); + Serial.print(transactionId); + Serial.print(F("\n")); + + Serial.print(F(" stackLevel: ")); + Serial.print(stackLevel); + Serial.print(F("\n")); + + Serial.print(F(" chargingProfilePurpose: ")); + switch (chargingProfilePurpose) { + case (ChargingProfilePurposeType::ChargePointMaxProfile): + Serial.print(F("ChargePointMaxProfile")); + break; + case (ChargingProfilePurposeType::TxDefaultProfile): + Serial.print(F("TxDefaultProfile")); + break; + case (ChargingProfilePurposeType::TxProfile): + Serial.print(F("TxProfile")); + break; + } + Serial.print(F("\n")); + + Serial.print(F(" chargingProfileKind: ")); + switch (chargingProfileKind) { + case (ChargingProfileKindType::Absolute): + Serial.print(F("Absolute")); + break; + case (ChargingProfileKindType::Recurring): + Serial.print(F("Recurring")); + break; + case (ChargingProfileKindType::Relative): + Serial.print(F("Relative")); + break; + } + Serial.print(F("\n")); + + Serial.print(F(" recurrencyKind: ")); + switch (recurrencyKind) { + case (RecurrencyKindType::Daily): + Serial.print(F("Daily")); + break; + case (RecurrencyKindType::Weekly): + Serial.print(F("Weekly")); + break; + case (RecurrencyKindType::NOT_SET): + Serial.print(F("NOT_SET")); + break; + } + Serial.print(F("\n")); + + Serial.print(F(" validFrom: ")); + char tmp[JSONDATE_LENGTH + 1] = {'\0'}; + validFrom.toJsonString(tmp, JSONDATE_LENGTH + 1); + Serial.print(F("\n")); + + Serial.print(F(" validTo: ")); + validTo.toJsonString(tmp, JSONDATE_LENGTH + 1); + Serial.print(F("\n")); + chargingSchedule->printSchedule(); } diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h index b8bb44d4..3bad2a2e 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h @@ -1,120 +1,122 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License #ifndef SMARTCHARGINGMODEL_H #define SMARTCHARGINGMODEL_H -#include #include -#include +#include namespace ArduinoOcpp { +extern const OcppTimestamp MIN_TIME; +extern const OcppTimestamp MAX_TIME; + enum class ChargingProfilePurposeType { - ChargePointMaxProfile, - TxDefaultProfile, - TxProfile + ChargePointMaxProfile, + TxDefaultProfile, + TxProfile }; enum class ChargingProfileKindType { - Absolute, - Recurring, - Relative + Absolute, + Recurring, + Relative }; enum class RecurrencyKindType { - NOT_SET, //not part of OCPP 1.6 - Daily, - Weekly + NOT_SET, //not part of OCPP 1.6 + Daily, + Weekly }; class ChargingSchedulePeriod { private: - int startPeriod; - float limit; //one fractural digit at most - int numberPhases = -1; + int startPeriod; + float limit; //one fractural digit at most + int numberPhases = -1; public: - ChargingSchedulePeriod(JsonObject *json); - int getStartPeriod(); - float getLimit(); - int getNumberPhases(); - void printPeriod(); + ChargingSchedulePeriod(JsonObject *json); + int getStartPeriod(); + float getLimit(); + int getNumberPhases(); + void printPeriod(); }; class ChargingSchedule { private: - int duration = -1; - time_t startSchedule; - char schedulingUnit; //either 'A' or 'W' - LinkedList chargingSchedulePeriod; - float minChargingRate = -1.0f; - - ChargingProfileKindType chargingProfileKind; //copied from ChargingProfile to increase cohesion of limit inferencing methods - RecurrencyKindType recurrencyKind; //copied from ChargingProfile to increase cohesion of limit inferencing methods + int duration = -1; + OcppTimestamp startSchedule; + char schedulingUnit; //either 'A' or 'W' + std::vector chargingSchedulePeriod; + float minChargingRate = -1.0f; + + ChargingProfileKindType chargingProfileKind; //copied from ChargingProfile to increase cohesion of limit inferencing methods + RecurrencyKindType recurrencyKind; //copied from ChargingProfile to increase cohesion of limit inferencing methods public: - ChargingSchedule(JsonObject *json, ChargingProfileKindType chargingProfileKind, RecurrencyKindType recurrencyKind); - ~ChargingSchedule(); - - /** - * limit: output parameter - * nextChange: output parameter - * - * returns if charging profile defines a limit at time t - * if true, limit and nextChange will be set according to this Schedule - * if false, only nextChange will be set - */ - bool inferenceLimit(time_t t, time_t startOfCharging, float *limit, time_t *nextChange); - - /* - * print on console - */ - void printSchedule(); + ChargingSchedule(JsonObject *json, ChargingProfileKindType chargingProfileKind, RecurrencyKindType recurrencyKind); + ~ChargingSchedule(); + + /** + * limit: output parameter + * nextChange: output parameter + * + * returns if charging profile defines a limit at time t + * if true, limit and nextChange will be set according to this Schedule + * if false, only nextChange will be set + */ + bool inferenceLimit(const OcppTimestamp &t, const OcppTimestamp &startOfCharging, float *limit, OcppTimestamp *nextChange); + + /* + * print on console + */ + void printSchedule(); }; class ChargingProfile { private: - int chargingProfileId; - int transactionId = -1; - int stackLevel; - ChargingProfilePurposeType chargingProfilePurpose; - ChargingProfileKindType chargingProfileKind; //copied to ChargingSchedule to increase cohesion of limit inferencing methods - RecurrencyKindType recurrencyKind; // copied to ChargingSchedule to increase cohesion - time_t validFrom; - time_t validTo; - ChargingSchedule *chargingSchedule; + int chargingProfileId = -1; + int transactionId = -1; + int stackLevel = 0; + ChargingProfilePurposeType chargingProfilePurpose; + ChargingProfileKindType chargingProfileKind; //copied to ChargingSchedule to increase cohesion of limit inferencing methods + RecurrencyKindType recurrencyKind; // copied to ChargingSchedule to increase cohesion + OcppTimestamp validFrom; + OcppTimestamp validTo; + ChargingSchedule *chargingSchedule; public: - ChargingProfile(JsonObject *json); - ~ChargingProfile(); - - /** - * limit: output parameter - * nextChange: output parameter - * - * returns if charging profile defines a limit at time t - * if true, limit and nextChange will be set according to this Schedule - * if false, only nextChange will be set - */ - bool inferenceLimit(time_t t, time_t startOfCharging, float *limit, time_t *nextChange); - - /* - * Simpler function if startOfCharging is not available. Caution: This likely will differ from inference with startOfCharging - */ - bool inferenceLimit(time_t t, float *limit, time_t *nextChange); - - /* - * Check if this profile belongs to transaction with ID transId - */ - bool checkTransactionId(int transId); - - int getStackLevel(); - - ChargingProfilePurposeType getChargingProfilePurpose(); - - /* - * print on console - */ - void printProfile(); + ChargingProfile(JsonObject *json); + ~ChargingProfile(); + + /** + * limit: output parameter + * nextChange: output parameter + * + * returns if charging profile defines a limit at time t + * if true, limit and nextChange will be set according to this Schedule + * if false, only nextChange will be set + */ + bool inferenceLimit(const OcppTimestamp &t, const OcppTimestamp &startOfCharging, float *limit, OcppTimestamp *nextChange); + + /* + * Simpler function if startOfCharging is not available. Caution: This likely will differ from inference with startOfCharging + */ + bool inferenceLimit(const OcppTimestamp &t, float *limit, OcppTimestamp *nextChange); + + /* + * Check if this profile belongs to transaction with ID transId + */ + bool checkTransactionId(int transId); + + int getStackLevel(); + + ChargingProfilePurposeType getChargingProfilePurpose(); + + /* + * print on console + */ + void printProfile(); }; } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index 51fc247b..a86cdf22 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -26,75 +26,74 @@ using namespace::ArduinoOcpp; -SmartChargingService::SmartChargingService(float chargeLimit, int numConnectors, OcppTime *ocppTime, FilesystemOpt filesystemOpt) - : DEFAULT_CHARGE_LIMIT(chargeLimit), ocppTime(ocppTime), filesystemOpt(filesystemOpt) { +SmartChargingService::SmartChargingService(float chargeLimit, float V_eff, int numConnectors, OcppTime *ocppTime, FilesystemOpt filesystemOpt) + : DEFAULT_CHARGE_LIMIT(chargeLimit), V_eff(V_eff), ocppTime(ocppTime), filesystemOpt(filesystemOpt) { - if (numConnectors > 2) { - Serial.print(F("[SmartChargingService] Error: Unfortunately, multiple connectors are not implemented in SmartChargingService yet. Only connector 1 will receive charging limits\n")); - } - - limitBeforeChange = -1.0f; - nextChange = INFINITY_THLD; - chargingSessionStart = INFINITY_THLD; - chargingSessionTransactionID = -1; - chargingSessionIsActive = false; - for (int i = 0; i < CHARGEPROFILEMAXSTACKLEVEL; i++) { - ChargePointMaxProfile[i] = NULL; - TxDefaultProfile[i] = NULL; - TxProfile[i] = NULL; - } - setSmartChargingService(this); //in OcppEngine.cpp - declareConfiguration("ChargeProfileMaxStackLevel", CHARGEPROFILEMAXSTACKLEVEL, CONFIGURATION_FN, false, true, false, true); + if (numConnectors > 2) { + Serial.print(F("[SmartChargingService] Error: Unfortunately, multiple connectors are not implemented in SmartChargingService yet. Only connector 1 will receive charging limits\n")); + } + + limitBeforeChange = -1.0f; + nextChange = MIN_TIME; + chargingSessionStart = MAX_TIME; + chargingSessionTransactionID = -1; + //chargingSessionIsActive = false; + for (int i = 0; i < CHARGEPROFILEMAXSTACKLEVEL; i++) { + ChargePointMaxProfile[i] = NULL; + TxDefaultProfile[i] = NULL; + TxProfile[i] = NULL; + } + setSmartChargingService(this); //in OcppEngine.cpp + declareConfiguration("ChargeProfileMaxStackLevel", CHARGEPROFILEMAXSTACKLEVEL, CONFIGURATION_FN, false, true, false, true); - loadProfiles(); + loadProfiles(); } void SmartChargingService::loop(){ - /** - * check if to call onLimitChange - */ - if (now() >= nextChange){ - float limit = -1.0f; - time_t validTo = 0; - inferenceLimit(now(), &limit, &validTo); - if (DEBUG_OUT) Serial.print(F("[SmartChargingService] New Limit! Values: {scheduled at = ")); - if (DEBUG_OUT) printTime(nextChange); - if (DEBUG_OUT) Serial.print(F(", nextChange = ")); - if (DEBUG_OUT) printTime(validTo); - if (DEBUG_OUT) Serial.print(F(", limit = ")); - if (DEBUG_OUT) Serial.print(limit); - if (DEBUG_OUT) Serial.print(F("}\n")); - nextChange = validTo; - if (limit != limitBeforeChange){ - if (onLimitChange != NULL) { - onLimitChange(limit); - } - } - limitBeforeChange = limit; - - //ChargePointStatusService: - ChargePointStatusService *cpStatusService = getChargePointStatusService(); - if (cpStatusService != NULL) { - if (limit > 0.0f) { - //cpStatusService->getConnector(SINGLE_CONNECTOR_ID)->startEnergyOffer(); //No, the client implementation should decide what to do here! TODO review if needed - //cpStatusService->startEnergyOffer(); - } else { - //cpStatusService->getConnector(SINGLE_CONNECTOR_ID)->stopEnergyOffer(); //No, the client implementation should decide what to do here! TODO review if needed - //cpStatusService->stopEnergyOffer(); - } + + refreshChargingSessionState(); + + /** + * check if to call onLimitChange + */ + if (ocppTime->getOcppTimestampNow() >= nextChange){ + OcppTimestamp tNow = ocppTime->getOcppTimestampNow(); + float limit = -1.0f; + OcppTimestamp validTo = OcppTimestamp(); + inferenceLimit(tNow, &limit, &validTo); + + if (DEBUG_OUT) { + Serial.print(F("[SmartChargingService] New Limit! Values: {scheduled at = ")); + + char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; + nextChange.toJsonString(timestamp, JSONDATE_LENGTH + 1); + Serial.print(timestamp); + Serial.print(F(", nextChange = ")); + validTo.toJsonString(timestamp, JSONDATE_LENGTH + 1); + Serial.print(timestamp); + Serial.print(F(", limit = ")); + Serial.print(limit); + Serial.print(F("}\n")); + } + nextChange = validTo; + if (limit != limitBeforeChange){ + if (onLimitChange != NULL) { + onLimitChange(limit); + } + } + limitBeforeChange = limit; } - } } float SmartChargingService::inferenceLimitNow(){ - float limit = 0.0f; - time_t validTo; //not needed - inferenceLimit(now(), &limit, &validTo); - return limit; + float limit = 0.0f; + OcppTimestamp validTo = OcppTimestamp(); //not needed + inferenceLimit(ocppTime->getOcppTimestampNow(), &limit, &validTo); + return limit; } void SmartChargingService::setOnLimitChange(OnLimitChange onLtChg){ - onLimitChange = onLtChg; + onLimitChange = onLtChg; } /** @@ -102,98 +101,119 @@ void SmartChargingService::setOnLimitChange(OnLimitChange onLtChg){ * account if the next Profile will be a prevailing one. If the profile at time t ends before any * other profile engages, the end of this profile will be written into validToOutParam. */ -void SmartChargingService::inferenceLimit(time_t t, float *limitOutParam, time_t *validToOutParam){ - time_t validToMin = INFINITY_THLD; - /* - * TxProfile rules over TxDefaultProfile. ChargePointMaxProfile rules over both of them - * - * if (TxProfile is present) - * take limit from TxProfile with the highest stackLevel - * else - * take limit from TxDefaultProfile with the highest stackLevel - * take maximum from ChargePointMaxProfile with the highest stackLevel - * return minimum(limit, maximum from ChargePointMaxProfile) - */ - - //evaluate limit from TxProfiles - float limit_tx = 0.0f; - bool limit_defined_tx = false; - for (int i = CHARGEPROFILEMAXSTACKLEVEL - 1; i >= 0; i--){ - if (TxProfile[i] == NULL) continue; - if (!TxProfile[i]->checkTransactionId(chargingSessionTransactionID)) continue; - time_t nextChange = INFINITY_THLD; - limit_defined_tx = TxProfile[i]->inferenceLimit(t, chargingSessionStart, &limit_tx, &nextChange); - validToMin = minimum(validToMin, nextChange); //nextChange is always >= t here - if (limit_defined_tx) { - //The valid profile with the highest stack level is found. It prevails over any other so end this loop. - break; +void SmartChargingService::inferenceLimit(const OcppTimestamp &t, float *limitOutParam, OcppTimestamp *validToOutParam){ + OcppTimestamp validToMin = MAX_TIME; + /* + * TxProfile rules over TxDefaultProfile. ChargePointMaxProfile rules over both of them + * + * if (TxProfile is present) + * take limit from TxProfile with the highest stackLevel + * else + * take limit from TxDefaultProfile with the highest stackLevel + * take maximum from ChargePointMaxProfile with the highest stackLevel + * return minimum(limit, maximum from ChargePointMaxProfile) + */ + + //evaluate limit from TxProfiles + float limit_tx = 0.0f; + bool limit_defined_tx = false; + for (int i = CHARGEPROFILEMAXSTACKLEVEL - 1; i >= 0; i--){ + if (TxProfile[i] == NULL) continue; + if (!TxProfile[i]->checkTransactionId(chargingSessionTransactionID)) continue; + OcppTimestamp nextChange = MAX_TIME; + limit_defined_tx = TxProfile[i]->inferenceLimit(t, chargingSessionStart, &limit_tx, &nextChange); + if (nextChange < validToMin) + validToMin = nextChange; //nextChange is always >= t here + if (limit_defined_tx) { + //The valid profile with the highest stack level is found. It prevails over any other so end this loop. + break; + } } - } - //evaluate limit from TxDefaultProfiles - float limit_txdef = 0.0f; - bool limit_defined_txdef = false; - for (int i = CHARGEPROFILEMAXSTACKLEVEL - 1; i >= 0; i--){ - if (TxDefaultProfile[i] == NULL) continue; - if (!TxDefaultProfile[i]->checkTransactionId(chargingSessionTransactionID)) continue; //this doesn't do anything on TxDefaultProfiles and could be deleted - time_t nextChange = INFINITY_THLD; - limit_defined_txdef = TxDefaultProfile[i]->inferenceLimit(t, chargingSessionStart, &limit_txdef, &nextChange); - validToMin = minimum(validToMin, nextChange); //nextChange is always >= t here - if (limit_defined_txdef) { - //The valid profile with the highest stack level is found. It prevails over any other so end this loop. - break; + //evaluate limit from TxDefaultProfiles + float limit_txdef = 0.0f; + bool limit_defined_txdef = false; + for (int i = CHARGEPROFILEMAXSTACKLEVEL - 1; i >= 0; i--){ + if (TxDefaultProfile[i] == NULL) continue; + //if (!TxDefaultProfile[i]->checkTransactionId(chargingSessionTransactionID)) continue; //this doesn't do anything on TxDefaultProfiles and could be deleted + OcppTimestamp nextChange = MAX_TIME; + limit_defined_txdef = TxDefaultProfile[i]->inferenceLimit(t, chargingSessionStart, &limit_txdef, &nextChange); + if (nextChange < validToMin) + validToMin = nextChange; //nextChange is always >= t here + if (limit_defined_txdef) { + //The valid profile with the highest stack level is found. It prevails over any other so end this loop. + break; + } } - } - //evaluate limit from ChargePointMaxProfile - float limit_cpmax = 0.0f; - bool limit_defined_cpmax = false; - for (int i = CHARGEPROFILEMAXSTACKLEVEL - 1; i >= 0; i--){ - if (ChargePointMaxProfile[i] == NULL) continue; - if (!ChargePointMaxProfile[i]->checkTransactionId(chargingSessionTransactionID)) continue; //this doesn't do anything on ChargePointMaxProfiles and could be deleted - time_t nextChange = INFINITY_THLD; - limit_defined_cpmax = ChargePointMaxProfile[i]->inferenceLimit(t, chargingSessionStart, &limit_cpmax, &nextChange); - validToMin = minimum(validToMin, nextChange); - if (limit_defined_cpmax) { - //The valid profile with the highest stack level is found. It prevails over any other so end this loop. - break; + //evaluate limit from ChargePointMaxProfile + float limit_cpmax = 0.0f; + bool limit_defined_cpmax = false; + for (int i = CHARGEPROFILEMAXSTACKLEVEL - 1; i >= 0; i--){ + if (ChargePointMaxProfile[i] == NULL) continue; + //if (!ChargePointMaxProfile[i]->checkTransactionId(chargingSessionTransactionID)) continue; //this doesn't do anything on ChargePointMaxProfiles and could be deleted + OcppTimestamp nextChange = MAX_TIME; + limit_defined_cpmax = ChargePointMaxProfile[i]->inferenceLimit(t, chargingSessionStart, &limit_cpmax, &nextChange); + if (nextChange < validToMin) + validToMin = nextChange; //nextChange is always >= t here + if (limit_defined_cpmax) { + //The valid profile with the highest stack level is found. It prevails over any other so end this loop. + break; + } } - } - *validToOutParam = validToMin; //validTo output parameter has successfully been determined here + *validToOutParam = validToMin; //validTo output parameter has successfully been determined here - //choose which limit to set according to specification - bool applicable_profile_found = false; - if (limit_defined_txdef){ - *limitOutParam = limit_txdef; - applicable_profile_found = true; - } - if (limit_defined_tx){ - *limitOutParam = limit_tx; - applicable_profile_found = true; - } - if (limit_defined_cpmax){ - //Warning: This block MUST be rewritten when multiple connector support is introduced - if (applicable_profile_found) { - if (limit_cpmax < *limitOutParam){ - //TxProfile or TxDefaultProfile exceeds the maximum for the whole CP - *limitOutParam = limit_cpmax; - } //else: TxProfile or TxDefaultProfile are within their boundary. Do nothing - } else { - //No TxProfile or TxDefaultProfile found. The limit is set to the maximum of the CP - *limitOutParam = limit_cpmax; + //choose which limit to set according to specification + bool applicable_profile_found = false; + if (limit_defined_txdef){ + *limitOutParam = limit_txdef; + applicable_profile_found = true; + } + if (limit_defined_tx){ + *limitOutParam = limit_tx; + applicable_profile_found = true; + } + if (limit_defined_cpmax){ + //Warning: This block MUST be rewritten when multiple connector support is introduced + if (applicable_profile_found) { + if (limit_cpmax < *limitOutParam){ + //TxProfile or TxDefaultProfile exceeds the maximum for the whole CP + *limitOutParam = limit_cpmax; + } //else: TxProfile or TxDefaultProfile are within their boundary. Do nothing + } else { + //No TxProfile or TxDefaultProfile found. The limit is set to the maximum of the CP + *limitOutParam = limit_cpmax; + } + applicable_profile_found = true; + } + if (!applicable_profile_found) { + *limitOutParam = DEFAULT_CHARGE_LIMIT; } - applicable_profile_found = true; - } - if (!applicable_profile_found) { - *limitOutParam = DEFAULT_CHARGE_LIMIT; - } } void SmartChargingService::writeOutCompositeSchedule(JsonObject *json){ - Serial.print(F("[SmartChargingService] Unsupported Operation: SmartChargingService::writeOutCompositeSchedule\n")); + Serial.print(F("[SmartChargingService] Unsupported Operation: SmartChargingService::writeOutCompositeSchedule\n")); } +void SmartChargingService::refreshChargingSessionState() { + int currentTxId = getChargePointStatusService()->getConnector(SINGLE_CONNECTOR_ID)->getTransactionId(); + + if (currentTxId != chargingSessionTransactionID) { + //transition! + + if (chargingSessionTransactionID != 0 && currentTxId >= 0) { + chargingSessionStart = ocppTime->getOcppTimestampNow(); + } else if (chargingSessionTransactionID >= 0 && currentTxId < 0) { + chargingSessionStart = MAX_TIME; + } + + nextChange = ocppTime->getOcppTimestampNow(); + chargingSessionTransactionID = currentTxId; + } +} + +/* void SmartChargingService::beginCharging(time_t t, int transactionID){ if (chargingSessionIsActive) { Serial.print(F("[SmartChargingService] Error: begin new Charging session though there is already running one!\n")); @@ -218,234 +238,250 @@ void SmartChargingService::endChargingNow(){ chargingSessionIsActive = false; nextChange = now(); } +*/ void SmartChargingService::updateChargingProfile(JsonObject *json) { - ChargingProfile *pointer = updateProfileStack(json); - if (pointer) - writeProfileToFlash(json, pointer); + ChargingProfile *pointer = updateProfileStack(json); + if (pointer) + writeProfileToFlash(json, pointer); } ChargingProfile *SmartChargingService::updateProfileStack(JsonObject *json){ - ChargingProfile *chargingProfile = new ChargingProfile(json); + ChargingProfile *chargingProfile = new ChargingProfile(json); - if (DEBUG_OUT) { - Serial.print(F("[SmartChargingService] Charging Profile internal model\n")); - chargingProfile->printProfile(); - } + if (DEBUG_OUT) { + Serial.print(F("[SmartChargingService] Charging Profile internal model\n")); + chargingProfile->printProfile(); + } - int stackLevel = chargingProfile->getStackLevel(); - if (stackLevel >= CHARGEPROFILEMAXSTACKLEVEL) { - Serial.print(F("[SmartChargingService] Error: Stacklevel of Charging Profile is greater than CHARGEPROFILEMAXSTACKLEVEL\n")); - stackLevel = CHARGEPROFILEMAXSTACKLEVEL - 1; - } + int stackLevel = chargingProfile->getStackLevel(); + if (stackLevel >= CHARGEPROFILEMAXSTACKLEVEL || stackLevel < 0) { + Serial.print(F("[SmartChargingService] Error: Stacklevel of Charging Profile is smaller or greater than CHARGEPROFILEMAXSTACKLEVEL\n")); + stackLevel = CHARGEPROFILEMAXSTACKLEVEL - 1; + } - ChargingProfile **profilePurposeStack; //select which stack this profile belongs to due to its purpose - - switch (chargingProfile->getChargingProfilePurpose()) { - case (ChargingProfilePurposeType::TxDefaultProfile): - profilePurposeStack = TxDefaultProfile; - break; - case (ChargingProfilePurposeType::TxProfile): - profilePurposeStack = TxProfile; - break; - default: - //case (ChargingProfilePurposeType::ChargePointMaxProfile): - profilePurposeStack = ChargePointMaxProfile; - break; - } - - if (profilePurposeStack[stackLevel] != NULL){ - delete profilePurposeStack[stackLevel]; - } + ChargingProfile **profilePurposeStack; //select which stack this profile belongs to due to its purpose - profilePurposeStack[stackLevel] = chargingProfile; + switch (chargingProfile->getChargingProfilePurpose()) { + case (ChargingProfilePurposeType::TxDefaultProfile): + profilePurposeStack = TxDefaultProfile; + break; + case (ChargingProfilePurposeType::TxProfile): + profilePurposeStack = TxProfile; + break; + default: + //case (ChargingProfilePurposeType::ChargePointMaxProfile): + profilePurposeStack = ChargePointMaxProfile; + break; + } + + if (profilePurposeStack[stackLevel] != NULL){ + delete profilePurposeStack[stackLevel]; + } - /** - * Invalidate the last limit inference by setting the nextChange to now. By the next loop()-call, the limit - * and nextChange will be recalculated and onLimitChanged will be called. - */ - nextChange = now(); + profilePurposeStack[stackLevel] = chargingProfile; - return chargingProfile; + /** + * Invalidate the last limit inference by setting the nextChange to now. By the next loop()-call, the limit + * and nextChange will be recalculated and onLimitChanged will be called. + */ + nextChange = ocppTime->getOcppTimestampNow(); + + return chargingProfile; } bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile *chargingProfile) { #ifndef AO_DEACTIVATE_FLASH - if (!filesystemOpt.accessAllowed()) { - if (DEBUG_OUT) Serial.println(F("[SmartChargingService] Prohibit access to FS")); - return true; - } - - String profileFN = PROFILE_FN_PREFIX; - - switch (chargingProfile->getChargingProfilePurpose()) { - case (ChargingProfilePurposeType::ChargePointMaxProfile): - profileFN += "ChargePointMaxProfile-"; - break; - case (ChargingProfilePurposeType::TxDefaultProfile): - profileFN += "TxDefaultProfile-"; - break; - case (ChargingProfilePurposeType::TxProfile): - profileFN += "TxProfile-"; - break; - } + if (!filesystemOpt.accessAllowed()) { + if (DEBUG_OUT) Serial.println(F("[SmartChargingService] Prohibit access to FS")); + return true; + } + + String profileFN = PROFILE_FN_PREFIX; - profileFN += chargingProfile->getStackLevel(); - profileFN += PROFILE_FN_SUFFIX; + switch (chargingProfile->getChargingProfilePurpose()) { + case (ChargingProfilePurposeType::ChargePointMaxProfile): + profileFN += "CpMaxProfile-"; + break; + case (ChargingProfilePurposeType::TxDefaultProfile): + profileFN += "TxDefProfile-"; + break; + case (ChargingProfilePurposeType::TxProfile): + profileFN += "TxProfile-"; + break; + } - USE_FS.remove(profileFN); + profileFN += chargingProfile->getStackLevel(); + profileFN += PROFILE_FN_SUFFIX; - File file = USE_FS.open(profileFN, "w"); + USE_FS.remove(profileFN); - if (!file) { - Serial.print(F("[SmartChargingService] Unable to save: could not save profile: ")); - Serial.println(profileFN); - return false; - } + File file = USE_FS.open(profileFN, "w"); - // Serialize JSON to file - if (serializeJson(*json, file) == 0) { - Serial.println(F("[SmartChargingService] Unable to save: Could not serialize JSON for profile: ")); - Serial.println(profileFN); - file.close(); - return false; - } + if (!file) { + Serial.print(F("[SmartChargingService] Unable to save: could not save profile: ")); + Serial.println(profileFN); + return false; + } - //success - file.close(); - if (DEBUG_OUT) Serial.print(F("[SmartChargingService] Saving profile successful\n")); + // Serialize JSON to file + if (serializeJson(*json, file) == 0) { + Serial.println(F("[SmartChargingService] Unable to save: Could not serialize JSON for profile: ")); + Serial.println(profileFN); + file.close(); + return false; + } - // BEGIN DEBUG - if (DEBUG_OUT) { - file = USE_FS.open(profileFN, "r"); + //success + file.close(); - Serial.println(file.readStringUntil('\n')); + if (DEBUG_OUT) Serial.print(F("[SmartChargingService] Saving profile successful\n")); - file.close(); - // END DEBUG - } + // BEGIN DEBUG + if (DEBUG_OUT) { + file = USE_FS.open(profileFN, "r"); + + Serial.println(file.readStringUntil('\n')); + + file.close(); + // END DEBUG + } #endif //ndef AO_DEACTIVATE_FLASH - return true; + return true; } bool SmartChargingService::loadProfiles() { + + bool success = true; + #ifndef AO_DEACTIVATE_FLASH if (!filesystemOpt.accessAllowed()) { if (DEBUG_OUT) Serial.println(F("[SmartChargingService] Prohibit access to FS")); return true; } - - const int N_PURPOSES = 3; - ChargingProfilePurposeType purposes[N_PURPOSES] = {ChargingProfilePurposeType::ChargePointMaxProfile, ChargingProfilePurposeType::TxDefaultProfile, ChargingProfilePurposeType::TxProfile}; - for (int iStack = 0; iStack < N_PURPOSES; iStack++) { - ChargingProfile **profilePurposeStack; //select which stack this profile belongs to due to its purpose - String profileFnPurpose; + ChargingProfilePurposeType purposes[] = {ChargingProfilePurposeType::ChargePointMaxProfile, ChargingProfilePurposeType::TxDefaultProfile, ChargingProfilePurposeType::TxProfile}; - switch (purposes[iStack]) { - case (ChargingProfilePurposeType::ChargePointMaxProfile): - profilePurposeStack = ChargePointMaxProfile; - profileFnPurpose = "ChargePointMaxProfile-"; - break; - case (ChargingProfilePurposeType::TxDefaultProfile): - profilePurposeStack = TxDefaultProfile; - profileFnPurpose = "TxDefaultProfile-"; - break; - case (ChargingProfilePurposeType::TxProfile): - profilePurposeStack = TxProfile; - profileFnPurpose = "TxProfile-"; - break; - } + for (const ChargingProfilePurposeType purpose : purposes) { + //ChargingProfile **profilePurposeStack; //select which stack this profile belongs to due to its purpose + String profileFnPurpose = String('\0'); - for (int iLevel = 0; iLevel < CHARGEPROFILEMAXSTACKLEVEL; iLevel++) { - - String profileFN = PROFILE_FN_PREFIX; - profileFN += profileFnPurpose; - profileFN += iLevel; - profileFN += PROFILE_FN_SUFFIX; + switch (purpose) { + case (ChargingProfilePurposeType::ChargePointMaxProfile): + //profilePurposeStack = ChargePointMaxProfile; + profileFnPurpose = "CpMaxProfile-"; + break; + case (ChargingProfilePurposeType::TxDefaultProfile): + //profilePurposeStack = TxDefaultProfile; + profileFnPurpose = "TxDefProfile-"; + break; + case (ChargingProfilePurposeType::TxProfile): + //profilePurposeStack = TxProfile; + profileFnPurpose = "TxProfile-"; + break; + } - File file = USE_FS.open(profileFN, "r"); + for (int iLevel = 0; iLevel < CHARGEPROFILEMAXSTACKLEVEL; iLevel++) { - if (file) { - if (DEBUG_OUT) Serial.print(F("[SmartChargingService] Load profile from file: ")); - if (DEBUG_OUT) Serial.println(profileFN); - } else { - continue; //There is not a profile on the stack iStack with stacklevel iLevel. Normal case, just continue. - } + String profileFN = PROFILE_FN_PREFIX; + profileFN += profileFnPurpose; + profileFN += iLevel; + profileFN += PROFILE_FN_SUFFIX; - if (!file.available()) { - Serial.print(F("[SmartChargingService] Unable to initialize: empty file for profile: ")); - Serial.println(profileFN); - file.close(); - return false; - } + if (!USE_FS.exists(profileFN)) { + continue; //There is not a profile on the stack iStack with stacklevel iLevel. Normal case, just continue. + } + + File file = USE_FS.open(profileFN, "r"); - int file_size = file.size(); + if (file) { + if (DEBUG_OUT) Serial.print(F("[SmartChargingService] Load profile from file: ")); + if (DEBUG_OUT) Serial.println(profileFN); + } else { + Serial.print(F("[SmartChargingService] Unable to initialize: could not open file for profile: ")); + Serial.println(profileFN); + success = false; + continue; + } - if (file_size < 2) { - Serial.print(F("[SmartChargingService] Unable to initialize: too short for json: ")); - Serial.println(profileFN); - continue; - } - - size_t capacity = 2*file_size; - if (capacity < PROFILE_CUSTOM_CAPACITY) - capacity = PROFILE_CUSTOM_CAPACITY; - if (capacity > PROFILE_MAX_CAPACITY) - capacity = PROFILE_MAX_CAPACITY; - - while (capacity <= PROFILE_MAX_CAPACITY) { - bool increaseCapacity = false; - bool error = true; - - DynamicJsonDocument profileDoc(capacity); - - DeserializationError jsonError = deserializeJson(profileDoc, file); - switch (jsonError.code()) { - case DeserializationError::Ok: - error = false; - break; - case DeserializationError::InvalidInput: - Serial.print(F("[SmartChargingService] Unable to initialize: Invalid json in file: ")); + if (!file.available()) { + Serial.print(F("[SmartChargingService] Unable to initialize: empty file for profile: ")); Serial.println(profileFN); - break; - case DeserializationError::NoMemory: - increaseCapacity = true; - error = false; - break; - default: - Serial.print(F("[SmartChargingService] Unable to initialize: Error in file: ")); + file.close(); + success = false; + continue; + } + + int file_size = file.size(); + + if (file_size < 2) { + Serial.print(F("[SmartChargingService] Unable to initialize: too short for json: ")); Serial.println(profileFN); + success = false; + continue; + } + + size_t capacity = 2*file_size; + if (capacity < PROFILE_CUSTOM_CAPACITY) + capacity = PROFILE_CUSTOM_CAPACITY; + if (capacity > PROFILE_MAX_CAPACITY) + capacity = PROFILE_MAX_CAPACITY; + + while (capacity <= PROFILE_MAX_CAPACITY) { + bool increaseCapacity = false; + bool error = true; + + DynamicJsonDocument profileDoc(capacity); + + DeserializationError jsonError = deserializeJson(profileDoc, file); + switch (jsonError.code()) { + case DeserializationError::Ok: + error = false; + break; + case DeserializationError::InvalidInput: + Serial.print(F("[SmartChargingService] Unable to initialize: Invalid json in file: ")); + Serial.println(profileFN); + success = false; + break; + case DeserializationError::NoMemory: + increaseCapacity = true; + error = false; + break; + default: + Serial.print(F("[SmartChargingService] Unable to initialize: Error in file: ")); + Serial.println(profileFN); + success = false; + break; + } + + if (error) { + break; + } + + if (increaseCapacity) { + capacity *= 3; + capacity /= 2; + file.seek(0, SeekSet); //rewind file to beginning + if (DEBUG_OUT) Serial.print(F("[SmartChargingService] Initialization: increase JsonCapacity to ")); + if (DEBUG_OUT) Serial.print(capacity, DEC); + if (DEBUG_OUT) Serial.print(F("for file: ")); + if (DEBUG_OUT) Serial.println(profileFN); + continue; + } + + JsonObject profileJson = profileDoc.as(); + updateProfileStack(&profileJson); + + profileDoc.clear(); break; - } - - if (error) { - break; - } - - if (increaseCapacity) { - capacity *= 1.5; - file.seek(0, SeekSet); //rewind file to beginning - if (DEBUG_OUT) Serial.print(F("[SmartChargingService] Initialization: increase JsonCapacity to ")); - if (DEBUG_OUT) Serial.print(capacity, DEC); - if (DEBUG_OUT) Serial.print(F("for file: ")); - if (DEBUG_OUT) Serial.println(profileFN); - continue; - } - - JsonObject profileJson = profileDoc.as(); - updateProfileStack(&profileJson); - - profileDoc.clear(); - break; - } + } - file.close(); - } + file.close(); + } } #endif //ndef AO_DEACTIVATE_FLASH - return true; + return success; } diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h index 68b2c425..5a246719 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h @@ -8,7 +8,6 @@ #define CHARGEPROFILEMAXSTACKLEVEL 20 #include -#include #include #include @@ -22,34 +21,36 @@ typedef std::function OnLimitChange; class SmartChargingService { private: - OcppTime *ocppTime; - const float DEFAULT_CHARGE_LIMIT; - ChargingProfile *ChargePointMaxProfile[CHARGEPROFILEMAXSTACKLEVEL]; - ChargingProfile *TxDefaultProfile[CHARGEPROFILEMAXSTACKLEVEL]; - ChargingProfile *TxProfile[CHARGEPROFILEMAXSTACKLEVEL]; - OnLimitChange onLimitChange; - float limitBeforeChange; - time_t nextChange; - time_t chargingSessionStart; - int chargingSessionTransactionID; - bool chargingSessionIsActive; - - ChargingProfile *updateProfileStack(JsonObject *json); - FilesystemOpt filesystemOpt; - bool writeProfileToFlash(JsonObject *json, ChargingProfile *chargingProfile); - bool loadProfiles(); + const float DEFAULT_CHARGE_LIMIT; + const float V_eff; //use for approximation: chargingLimit in A * V_eff = chargingLimit in W + OcppTime *ocppTime; + ChargingProfile *ChargePointMaxProfile[CHARGEPROFILEMAXSTACKLEVEL]; + ChargingProfile *TxDefaultProfile[CHARGEPROFILEMAXSTACKLEVEL]; + ChargingProfile *TxProfile[CHARGEPROFILEMAXSTACKLEVEL]; + OnLimitChange onLimitChange = NULL; + float limitBeforeChange; + OcppTimestamp nextChange; + OcppTimestamp chargingSessionStart; + int chargingSessionTransactionID; + void refreshChargingSessionState(); + //bool chargingSessionIsActive; + + ChargingProfile *updateProfileStack(JsonObject *json); + FilesystemOpt filesystemOpt; + bool writeProfileToFlash(JsonObject *json, ChargingProfile *chargingProfile); + bool loadProfiles(); public: - SmartChargingService(float chargeLimit, int numConnectors, OcppTime *ocppTime, FilesystemOpt filesystemOpt = FilesystemOpt::Use_Mount_FormatOnFail); - void beginCharging(time_t t, int transactionID); - void beginChargingNow(); - void endChargingNow(); - void updateChargingProfile(JsonObject *json); - void inferenceLimit(time_t t, float *limit, time_t *validTo); - float inferenceLimitNow(); - void setOnLimitChange(OnLimitChange onLimitChange); - void writeOutCompositeSchedule(JsonObject *json); - void loop(); + SmartChargingService(float chargeLimit, float V_eff, int numConnectors, OcppTime *ocppTime, FilesystemOpt filesystemOpt = FilesystemOpt::Use_Mount_FormatOnFail); + //void beginCharging(const OcppTimestamp &t, int transactionID); + //void beginChargingNow(); + //void endChargingNow(); + void updateChargingProfile(JsonObject *json); + void inferenceLimit(const OcppTimestamp &t, float *limit, OcppTimestamp *validTo); + float inferenceLimitNow(); + void setOnLimitChange(OnLimitChange onLimitChange); + void writeOutCompositeSchedule(JsonObject *json); + void loop(); }; } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/TimeHelper.cpp b/src/ArduinoOcpp/TimeHelper.cpp index 031d74d8..4e6b8e8d 100644 --- a/src/ArduinoOcpp/TimeHelper.cpp +++ b/src/ArduinoOcpp/TimeHelper.cpp @@ -8,6 +8,8 @@ #include #include +#if 0 + namespace ArduinoOcpp { /** @@ -234,3 +236,5 @@ void printTime(time_t t) { } } //end namespace ArduinoOcpp + +#endif diff --git a/src/ArduinoOcpp/TimeHelper.h b/src/ArduinoOcpp/TimeHelper.h index 30d59c82..c85ff709 100644 --- a/src/ArduinoOcpp/TimeHelper.h +++ b/src/ArduinoOcpp/TimeHelper.h @@ -5,6 +5,8 @@ #ifndef TIMEHELPER_H #define TIMEHELPER_H +#if 0 + #include //Infinity begins in the end of year 2035 @@ -34,3 +36,4 @@ void printTime(time_t t); } //end namespace ArduinoOcpp #endif +#endif From ff6fbbdaa20049a7ec7a7c42365bd51d48c0175d Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 31 May 2021 09:45:38 +0200 Subject: [PATCH 029/696] ESP32 support --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 35c4a344..22529e57 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # ArduinoOcpp -OCPP 1.6J Smart Charging client for the ESP8266 - -ESP32 & more coming soon +OCPP 1.6J Smart Charging client for the ESP8266 and the ESP32 (more coming soon) Website: [www.arduino-ocpp.com](https://www.arduino-ocpp.com) @@ -125,8 +123,6 @@ To get started quickly without EVSE hardware, you can use the charge point simul - [bblanchon/ArduinoJSON](https://github.com/bblanchon/ArduinoJson) (Please use version 6.17.2) - [Links2004/arduinoWebSockets](https://github.com/Links2004/arduinoWebSockets) -- [ivanseidel/LinkedList](https://github.com/ivanseidel/LinkedList) -- [PaulStoffregen/Time](https://github.com/PaulStoffregen/Time) In case you use PlatformIO, you can copy all dependencies from `platformio.ini` into your own configuration file. Alternatively, you can install the full library with dependencies by adding `matth-x/ArduinoOcpp` in the PIO library manager. @@ -166,7 +162,7 @@ In case you use PlatformIO, you can copy all dependencies from `platformio.ini` - [x] introduce proper offline behavior and package loss / fault detection - [x] handle fragmented input messages correctly - [x] add support for multiple power connectors -- [ ] add support for the ESP32 +- [x] add support for the ESP32 - [ ] reach full compliance to OCPP 1.6 Smart Charging Profile - [ ] **get ready for OCPP 2.0.1** From 073eb1933da7824e39db3d18a302b7f7ef9ab7ff Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 31 May 2021 19:14:52 +0200 Subject: [PATCH 030/696] keeping track of TxID when offline --- platformio.ini | 2 -- src/ArduinoOcpp.cpp | 16 ++++++---- src/ArduinoOcpp/Core/OcppConnection.cpp | 1 + src/ArduinoOcpp/Core/OcppEngine.cpp | 1 - src/ArduinoOcpp/Core/OcppMessage.cpp | 2 +- src/ArduinoOcpp/MessagesV16/MeterValues.cpp | 7 +++-- .../MessagesV16/StartTransaction.cpp | 29 +++++++++---------- .../MessagesV16/StartTransaction.h | 1 + .../MessagesV16/StopTransaction.cpp | 8 +++-- src/ArduinoOcpp/MessagesV16/StopTransaction.h | 1 - .../ChargePointStatusService.cpp | 4 +++ .../ChargePointStatusService.h | 1 + .../ChargePointStatus/ConnectorStatus.cpp | 13 +++++++++ .../Tasks/ChargePointStatus/ConnectorStatus.h | 4 +++ 14 files changed, 59 insertions(+), 31 deletions(-) diff --git a/platformio.ini b/platformio.ini index 917d6e3b..373aaa54 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,8 +17,6 @@ framework = arduino lib_deps = ArduinoJson@6.17.2 WebSockets@2.2.0 - ivanseidel/LinkedList @ 0.0.0-alpha+sha.dac3874d28 - Time@1.6 build_flags = -D USE_FACADE=true monitor_speed = 115200 diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index ed9ca66f..c1be21d6 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -44,6 +44,7 @@ OcppTime *ocppTime; #define OCPP_ID_OF_CONNECTOR 1 #define OCPP_ID_OF_CP 0 boolean OCPP_initialized = false; +boolean OCPP_booted = false; //if BootNotification succeeded #if 0 //moved to OcppConnection /* @@ -158,12 +159,17 @@ void OCPP_loop() { return; } - Serial.print('.'); - delay(70); - //webSocket.loop(); //moved to Core/OcppSocket ocppEngine_loop(); //mandatory + if (!OCPP_booted) { + if (chargePointStatusService->isBooted()) { + OCPP_booted = true; + } else { + return; //wait until the first BootNotification succeeded + } + } + if (onLimitChange != NULL) { smartChargingService->loop(); //optional } @@ -310,7 +316,7 @@ void startTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnT if (timeout) startTransaction->setTimeout(timeout); else - startTransaction->setTimeout(new FixedTimeout(20000)); + startTransaction->setTimeout(new SuppressedTimeout()); } void startTransaction(String &idTag, OnReceiveConfListener onConf) { @@ -318,7 +324,7 @@ void startTransaction(String &idTag, OnReceiveConfListener onConf) { new StartTransaction(OCPP_ID_OF_CONNECTOR, idTag)); initiateOcppOperation(startTransaction); startTransaction->setOnReceiveConfListener(onConf); - startTransaction->setTimeout(new FixedTimeout(20000)); + startTransaction->setTimeout(new SuppressedTimeout()); } void stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, Timeout *timeout) { diff --git a/src/ArduinoOcpp/Core/OcppConnection.cpp b/src/ArduinoOcpp/Core/OcppConnection.cpp index f87410d5..f3d08f3a 100644 --- a/src/ArduinoOcpp/Core/OcppConnection.cpp +++ b/src/ArduinoOcpp/Core/OcppConnection.cpp @@ -100,6 +100,7 @@ void OcppConnection::initiateOcppOperation(OcppOperation *o){ return; } initiatedOcppOperations.push_back(o); + o->setInitiated(); } bool OcppConnection::processOcppSocketInputTXT(const char* payload, size_t length) { diff --git a/src/ArduinoOcpp/Core/OcppEngine.cpp b/src/ArduinoOcpp/Core/OcppEngine.cpp index 72fa4df1..21028187 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.cpp +++ b/src/ArduinoOcpp/Core/OcppEngine.cpp @@ -44,7 +44,6 @@ void ocppEngine_initialize(OcppSocket *ocppSocket){ void initiateOcppOperation(OcppOperation *o) { mConnection->initiateOcppOperation(o); - o->setInitiated(); } void ocppEngine_loop() { diff --git a/src/ArduinoOcpp/Core/OcppMessage.cpp b/src/ArduinoOcpp/Core/OcppMessage.cpp index d141fe84..c1fb047e 100644 --- a/src/ArduinoOcpp/Core/OcppMessage.cpp +++ b/src/ArduinoOcpp/Core/OcppMessage.cpp @@ -16,7 +16,7 @@ const char* OcppMessage::getOcppOperationType(){ } void OcppMessage::initiate() { - //callback after initiateOcppOperation(anyMsg) + //called after initiateOcppOperation(anyMsg) } DynamicJsonDocument* OcppMessage::createReq() { diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp index 22716b5a..211fd822 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp @@ -80,8 +80,11 @@ DynamicJsonDocument* MeterValues::createReq() { } } - if (transactionId >= 0) { - payload["transactionId"] = transactionId; + ConnectorStatus *connector = getConnectorStatus(connectorId); + if (connector) { + if (connector->getTransactionIdSync() >= 0) { + payload["transactionId"] = connector->getTransactionIdSync(); + } } return doc; diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp index 5cad09d3..06df06db 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp @@ -35,18 +35,6 @@ void StartTransaction::initiate() { otimestamp = MIN_TIME; } - ChargePointStatusService *cpss = getChargePointStatusService(); - if (cpss != NULL) { - if (cpss->existsUnboundAuthorization()) { - this->idTag = String(cpss->getUnboundIdTag()); - } else { - //The CP is not authorized. Try anyway, let the CS decide what to do ... - this->idTag = String("A0-00-00-00"); - } - } else { - this->idTag = String("A0-00-00-00"); //Use a default payload. In the typical use case of this library, you probably you don't even need Authorization at all - } - if (idTag.isEmpty()) { ChargePointStatusService *cpss = getChargePointStatusService(); if (cpss != NULL && cpss->existsUnboundAuthorization()) { @@ -64,10 +52,13 @@ void StartTransaction::initiate() { ConnectorStatus *connector = getConnectorStatus(connectorId); if (connector != NULL){ + if (connector->getTransactionId() >= 0) { + Serial.print(F("[StartTransaction] Warning: started transaction while OCPP already presumes a running transaction\n")); + } connector->setTransactionId(0); //pending + transactionRev = connector->getTransactionWriteCount(); } - if (DEBUG_OUT) Serial.println(F("[StartTransaction] StartTransaction initiated!")); } @@ -101,14 +92,20 @@ void StartTransaction::processConf(JsonObject payload) { ConnectorStatus *connector = getConnectorStatus(connectorId); if (connector){ - connector->setTransactionId(transactionId); + if (transactionRev == connector->getTransactionWriteCount()) { + connector->setTransactionId(transactionId); + } + connector->setTransactionIdSync(transactionId); } } else { Serial.print(F("[StartTransaction] Request has been denied!\n")); ConnectorStatus *connector = getConnectorStatus(connectorId); if (connector){ - connector->setTransactionId(-1); - connector->unauthorize(); + if (transactionRev == connector->getTransactionWriteCount()) { + connector->setTransactionId(-1); + connector->unauthorize(); + } + connector->setTransactionIdSync(-1); } } } diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.h b/src/ArduinoOcpp/MessagesV16/StartTransaction.h index f70feb67..a08c787b 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.h @@ -19,6 +19,7 @@ class StartTransaction : public OcppMessage { float meterStart = -1.0f; OcppTimestamp otimestamp; String idTag = String('\0'); + uint16_t transactionRev = 0; public: StartTransaction(int connectorId); diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp index 59d58585..b6482908 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp @@ -36,7 +36,6 @@ void StopTransaction::initiate() { ConnectorStatus *connector = getConnectorStatus(connectorId); if (connector != NULL){ - transactionId = connector->getTransactionId(); //for req message connector->setTransactionId(-1); //immediate end of transaction connector->unauthorize(); } @@ -58,8 +57,11 @@ DynamicJsonDocument* StopTransaction::createReq() { otimestamp.toJsonString(timestamp, JSONDATE_LENGTH + 1); payload["timestamp"] = timestamp; } - - payload["transactionId"] = transactionId; + ConnectorStatus *connector = getConnectorStatus(connectorId); + if (connector != NULL){ + payload["transactionId"] = connector->getTransactionIdSync(); + connector->setTransactionIdSync(-1); + } return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.h b/src/ArduinoOcpp/MessagesV16/StopTransaction.h index 7df83ccd..2dee80a2 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.h @@ -16,7 +16,6 @@ class StopTransaction : public OcppMessage { int connectorId = 1; float meterStop = -1.0f; OcppTimestamp otimestamp; - int transactionId = -1; public: StopTransaction(); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index 9653ea55..52ce7f83 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -68,6 +68,10 @@ void ChargePointStatusService::boot() { booted = true; } +bool ChargePointStatusService::isBooted() { + return booted; +} + String &ChargePointStatusService::getUnboundIdTag() { return idTag; } diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h index da770d2c..fc562bdf 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h @@ -33,6 +33,7 @@ class ChargePointStatusService { void authorize(String &idTag); void authorize(); void boot(); + bool isBooted(); String &getUnboundIdTag(); void invalidateUnboundIdTag(); boolean existsUnboundAuthorization(); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 81fc513b..324ac3dc 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -21,6 +21,7 @@ ConnectorStatus::ConnectorStatus(int connectorId, OcppTime *ocppTime) : connecto if (!transactionId) { Serial.print(F("[ConnectorStatus] Error! Cannot declare transactionId\n")); } + transactionIdSync = *transactionId; } OcppEvseState ConnectorStatus::inferenceStatus() { @@ -91,6 +92,18 @@ int ConnectorStatus::getTransactionId() { return *transactionId; } +int ConnectorStatus::getTransactionIdSync() { + return transactionIdSync; +} + +void ConnectorStatus::setTransactionIdSync(int id) { + transactionIdSync = id; +} + +uint16_t ConnectorStatus::getTransactionWriteCount() { + return transactionId->getValueRevision(); +} + void ConnectorStatus::setTransactionId(int id) { int prevTxId = *transactionId; *transactionId = id; diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index eeeda178..c53b2d34 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -21,6 +21,7 @@ class ConnectorStatus { bool transactionRunning = false; //int transactionId = -1; std::shared_ptr> transactionId = NULL; + int transactionIdSync = -1; bool evDrawsEnergy = false; bool evseOffersEnergy = false; OcppEvseState currentStatus = OcppEvseState::NOT_SET; @@ -33,7 +34,10 @@ class ConnectorStatus { void unauthorize(); String &getIdTag(); int getTransactionId(); + int getTransactionIdSync(); + uint16_t getTransactionWriteCount(); void setTransactionId(int id); + void setTransactionIdSync(int id); void boot(); void startEvDrawsEnergy(); void stopEvDrawsEnergy(); From 225b3d94b2821e25c7307cb990a905c9df5b7b5d Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 2 Jun 2021 17:28:33 +0200 Subject: [PATCH 031/696] make built-in WS optional --- src/ArduinoOcpp.cpp | 8 +++++-- src/ArduinoOcpp.h | 4 ++++ .../Core/ConfigurationContainerFlash.cpp | 2 +- src/ArduinoOcpp/Core/OcppConnection.h | 1 - src/ArduinoOcpp/Core/OcppEngine.cpp | 2 -- src/ArduinoOcpp/Core/OcppError.h | 1 - src/ArduinoOcpp/Core/OcppMessage.h | 2 -- src/ArduinoOcpp/Core/OcppServer.cpp | 4 ++++ src/ArduinoOcpp/Core/OcppServer.h | 10 ++++++++- src/ArduinoOcpp/Core/OcppSocket.cpp | 9 ++++---- src/ArduinoOcpp/Core/OcppSocket.h | 15 +++++++++---- src/ArduinoOcpp/MessagesV16/TriggerMessage.h | 2 -- .../ChargePointStatusService.cpp | 4 ++-- .../ChargePointStatusService.h | 5 +---- .../Tasks/Metering/MeteringService.cpp | 4 ++-- .../Tasks/Metering/MeteringService.h | 5 +---- src/EEPROMLayout.h | 21 ------------------- 17 files changed, 46 insertions(+), 53 deletions(-) delete mode 100644 src/EEPROMLayout.h diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index c1be21d6..28e96a35 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -25,7 +25,9 @@ namespace ArduinoOcpp { namespace Facade { +#ifndef AO_CUSTOM_WS WebSocketsClient webSocket; +#endif OcppSocket *ocppSocket; MeteringService *meteringService; @@ -98,6 +100,7 @@ using namespace ArduinoOcpp; using namespace ArduinoOcpp::Facade; using namespace ArduinoOcpp::Ocpp16; +#ifndef AO_CUSTOM_WS void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, float V_eff, ArduinoOcpp::FilesystemOpt fsOpt, ArduinoOcpp::OcppClock system_time) { if (OCPP_initialized) { Serial.print(F("[ArduinoOcpp] Error: cannot call OCPP_initialize() two times! If you want to reconfigure the library, please restart your ESP\n")); @@ -126,6 +129,7 @@ void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, float OCPP_initialize(ocppSocket, V_eff, fsOpt); } +#endif void OCPP_initialize(OcppSocket *ocppSocket, float V_eff, ArduinoOcpp::FilesystemOpt fsOpt, ArduinoOcpp::OcppClock system_time) { if (OCPP_initialized) { @@ -146,8 +150,8 @@ void OCPP_initialize(OcppSocket *ocppSocket, float V_eff, ArduinoOcpp::Filesyste ocppEngine_setOcppTime(ocppTime); smartChargingService = new SmartChargingService(11000.0f, V_eff, OCPP_NUMCONNECTORS, ocppTime, fsOpt); //default charging limit: 11kW - chargePointStatusService = new ChargePointStatusService(&webSocket, OCPP_NUMCONNECTORS, ocppTime); //Constructor adds instance to ocppEngine in constructor - meteringService = new MeteringService(&webSocket, OCPP_NUMCONNECTORS, ocppTime); + chargePointStatusService = new ChargePointStatusService(OCPP_NUMCONNECTORS, ocppTime); //Constructor adds instance to ocppEngine in constructor + meteringService = new MeteringService(OCPP_NUMCONNECTORS, ocppTime); OCPP_initialized = true; } diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 603a7b1a..9605ddf8 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -23,8 +23,12 @@ using ArduinoOcpp::Timeout; #if USE_FACADE +#ifndef AO_CUSTOM_WS +//uses links2004/WebSockets library void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, float V_eff = 230.f /*German grid*/, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, ArduinoOcpp::OcppClock system_time = ArduinoOcpp::Clocks::DEFAULT_CLOCK); +#endif +//Lets you use your own WebSocket implementation void OCPP_initialize(ArduinoOcpp::OcppSocket *ocppSocket, float V_eff = 230.f /*German grid*/, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, ArduinoOcpp::OcppClock system_time = ArduinoOcpp::Clocks::DEFAULT_CLOCK); void OCPP_loop(); diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp index edf1adfd..5f088683 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp @@ -138,7 +138,7 @@ bool ConfigurationContainerFlash::load() { configurationsUpdated(); - Serial.println(F("[Configuration] Initialization successful\n")); + if (DEBUG_OUT) Serial.println(F("[Configuration] Initialization successful\n")); #endif //ndef AO_DEACTIVATE_FLASH return true; } diff --git a/src/ArduinoOcpp/Core/OcppConnection.h b/src/ArduinoOcpp/Core/OcppConnection.h index c2ae413e..c5e29f1b 100644 --- a/src/ArduinoOcpp/Core/OcppConnection.h +++ b/src/ArduinoOcpp/Core/OcppConnection.h @@ -7,7 +7,6 @@ #include #include -#include #include #include diff --git a/src/ArduinoOcpp/Core/OcppEngine.cpp b/src/ArduinoOcpp/Core/OcppEngine.cpp index 21028187..8978287c 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.cpp +++ b/src/ArduinoOcpp/Core/OcppEngine.cpp @@ -13,7 +13,6 @@ namespace ArduinoOcpp { namespace OcppEngine { -WebSocketsClient *wsocket; OcppSocket *ocppSocket; SmartChargingService *ocppEngine_smartChargingService; ChargePointStatusService *ocppEngine_chargePointStatusService; @@ -35,7 +34,6 @@ OcppConnection *mConnection; } //end namespace OcppEngine using namespace ArduinoOcpp::OcppEngine; -using namespace ArduinoOcpp::EspWiFi; void ocppEngine_initialize(OcppSocket *ocppSocket){ ocppSock = ocppSocket; diff --git a/src/ArduinoOcpp/Core/OcppError.h b/src/ArduinoOcpp/Core/OcppError.h index 0827d886..cc019b66 100644 --- a/src/ArduinoOcpp/Core/OcppError.h +++ b/src/ArduinoOcpp/Core/OcppError.h @@ -6,7 +6,6 @@ #define OCPPERROR_H #include -#include #include diff --git a/src/ArduinoOcpp/Core/OcppMessage.h b/src/ArduinoOcpp/Core/OcppMessage.h index bef98996..623fe1bf 100644 --- a/src/ArduinoOcpp/Core/OcppMessage.h +++ b/src/ArduinoOcpp/Core/OcppMessage.h @@ -19,7 +19,6 @@ #define OCPPMESSAGE_H #include - #include namespace ArduinoOcpp { @@ -70,7 +69,6 @@ class OcppMessage { virtual const char *getErrorCode() {return NULL;} //NULL means no error virtual const char *getErrorDescription() {return "";} virtual DynamicJsonDocument *getErrorDetails() {return createEmptyDocument();} - }; diff --git a/src/ArduinoOcpp/Core/OcppServer.cpp b/src/ArduinoOcpp/Core/OcppServer.cpp index 82cceee6..c7d592a3 100644 --- a/src/ArduinoOcpp/Core/OcppServer.cpp +++ b/src/ArduinoOcpp/Core/OcppServer.cpp @@ -6,6 +6,8 @@ #include +#ifndef AO_CUSTOM_WS + using namespace ArduinoOcpp::EspWiFi; OcppServer::OcppServer() { @@ -153,3 +155,5 @@ bool OcppServer::sendTXT(IPAddress &ip_addr, String &out) { return wsockServer.sendTXT(mClient, out); } + +#endif //ndef AO_CUSTOM_WS diff --git a/src/ArduinoOcpp/Core/OcppServer.h b/src/ArduinoOcpp/Core/OcppServer.h index c60727df..e551a589 100644 --- a/src/ArduinoOcpp/Core/OcppServer.h +++ b/src/ArduinoOcpp/Core/OcppServer.h @@ -6,13 +6,20 @@ #define OCPPSERVER_H #include -#include +#include namespace ArduinoOcpp { typedef uint8_t WsClient; typedef std::function ReceiveTXTcallback; +} //end namespace ArduinoOcpp + +#ifndef AO_CUSTOM_WS + +#include + +namespace ArduinoOcpp { namespace EspWiFi { struct ReceiveTXTroute { @@ -46,4 +53,5 @@ class OcppServer { } //end namespace EspWiFi } //end namespace ArduinoOcpp +#endif //ndef AO_CUSTOM_WS #endif diff --git a/src/ArduinoOcpp/Core/OcppSocket.cpp b/src/ArduinoOcpp/Core/OcppSocket.cpp index 4aeef4c0..273c0bb5 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.cpp +++ b/src/ArduinoOcpp/Core/OcppSocket.cpp @@ -5,12 +5,11 @@ #include #include +#ifndef AO_CUSTOM_WS + using namespace ArduinoOcpp; -using namespace ArduinoOcpp::EspWiFi; -OcppSocket::OcppSocket() { - -} +using namespace ArduinoOcpp::EspWiFi; OcppClientSocket::OcppClientSocket(WebSocketsClient *wsock) : wsock(wsock) { @@ -78,3 +77,5 @@ bool OcppServerSocket::sendTXT(String &out) { void OcppServerSocket::setReceiveTXTcallback(ReceiveTXTcallback &callback) { OcppServer::getInstance()->setReceiveTXTcallback(ip_addr, callback); } + +#endif diff --git a/src/ArduinoOcpp/Core/OcppSocket.h b/src/ArduinoOcpp/Core/OcppSocket.h index 259c360d..72d2ed9f 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.h +++ b/src/ArduinoOcpp/Core/OcppSocket.h @@ -5,16 +5,14 @@ #ifndef OCPPSOCKET_H #define OCPPSOCKET_H -#include -#include - +#include #include namespace ArduinoOcpp { class OcppSocket { public: - OcppSocket(); + OcppSocket() = default; virtual ~OcppSocket() = default; virtual void loop() = 0; @@ -24,6 +22,14 @@ class OcppSocket { virtual void setReceiveTXTcallback(ReceiveTXTcallback &receiveTXT) = 0; //ReceiveTXTcallback is defined in OcppServer.h }; +} //end namespace ArduinoOcpp + +#ifndef AO_CUSTOM_WS + +#include +#include + +namespace ArduinoOcpp { namespace EspWiFi { class OcppClientSocket : public OcppSocket { @@ -57,4 +63,5 @@ class OcppServerSocket : public OcppSocket { } //end namespace EspWiFi } //end namespace ArduinoOcpp +#endif //ndef AO_CUSTOM_WS #endif diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h index c8c269b6..b3dfe41d 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h @@ -5,8 +5,6 @@ #ifndef TRIGGERMESSAGE_H #define TRIGGERMESSAGE_H -#include - #include #include #include diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index 52ce7f83..f6417597 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -14,8 +14,8 @@ using namespace ArduinoOcpp; using namespace ArduinoOcpp::Ocpp16; -ChargePointStatusService::ChargePointStatusService(WebSocketsClient *webSocket, int numConn, OcppTime *ocppTime) - : webSocket(webSocket), numConnectors(numConn), ocppTime(ocppTime) { +ChargePointStatusService::ChargePointStatusService(int numConn, OcppTime *ocppTime) + : numConnectors(numConn), ocppTime(ocppTime) { connectors = (ConnectorStatus**) malloc(numConn * sizeof(ConnectorStatus*)); for (int i = 0; i < numConn; i++) { diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h index fc562bdf..a34a56ec 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h @@ -5,15 +5,12 @@ #ifndef CHARGEPOINTSTATUSSERVICE_H #define CHARGEPOINTSTATUSSERVICE_H -#include - #include namespace ArduinoOcpp { class ChargePointStatusService { private: - WebSocketsClient *webSocket; const int numConnectors; OcppTime *ocppTime; ConnectorStatus **connectors; @@ -24,7 +21,7 @@ class ChargePointStatusService { String idTag = String('\0'); public: - ChargePointStatusService(WebSocketsClient *webSocket, int numConnectors, OcppTime *ocppTime); + ChargePointStatusService(int numConnectors, OcppTime *ocppTime); ~ChargePointStatusService(); diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp index ad0b4f2e..4191ea62 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp @@ -13,8 +13,8 @@ using namespace ArduinoOcpp; using namespace ArduinoOcpp::Ocpp16; -MeteringService::MeteringService(WebSocketsClient *webSocket, int numConn, OcppTime *ocppTime) - : webSocket(webSocket), numConnectors(numConn) { +MeteringService::MeteringService(int numConn, OcppTime *ocppTime) + : numConnectors(numConn) { connectors = (ConnectorMeterValuesRecorder**) malloc(numConn * sizeof(ConnectorMeterValuesRecorder*)); for (int i = 0; i < numConn; i++) { diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h index 464ce892..0617b57c 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h @@ -9,10 +9,8 @@ //#define METER_VALUES_SAMPLED_DATA_MAX_LENGTH 4 //after 4 measurements, send the values to the CS -#include #include -#include #include #include @@ -28,11 +26,10 @@ typedef std::function EnergySampler; class MeteringService { private: - WebSocketsClient *webSocket; const int numConnectors; ConnectorMeterValuesRecorder **connectors; public: - MeteringService(WebSocketsClient *webSocket, int numConnectors, OcppTime *ocppTime); + MeteringService(int numConnectors, OcppTime *ocppTime); ~MeteringService(); diff --git a/src/EEPROMLayout.h b/src/EEPROMLayout.h deleted file mode 100644 index 2b38f2d1..00000000 --- a/src/EEPROMLayout.h +++ /dev/null @@ -1,21 +0,0 @@ -// matth-x/ESP8266-OCPP -// Copyright Matthias Akstaller 2019 - 2021 -// MIT License - -/* - * Deprecated. Switched to file system - */ - -#ifndef EEPROMLAYOUT_H -#define EEPROMLAYOUT_H - -#define WS_URL_PREFIX_MAXLENGTH 100 -#define MEM_INIT_INDICATOR 2604053329UL // just a random "magic number" - -typedef struct { - float energyActiveImportRegister; - char ws_url_prefix[WS_URL_PREFIX_MAXLENGTH]; - ulong mem_init_indicator; //set this field to MEM_INIT_INDICATOR before write back, so future accesses can determine that the memory is initialized -} EEPROM_Data; - -#endif From 34b8e179aa1b2429c867be02b8ef0c23a08f4dad Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 2 Jun 2021 18:29:40 +0200 Subject: [PATCH 032/696] updated examples --- .../OneConnector-EVSE/OneConnector_EVSE.ino | 6 +++--- .../OneConnector_HW_integration.h | 2 +- .../OneConnector_HW_integration.ino | 4 ++-- .../OneConnector_EVSE.ino | 21 +++++++++++++++++-- .../OneConnector_HW_integration.h | 2 +- .../Simulation_without_HW/Simulated_HW.ino | 21 ++++++++++++------- 6 files changed, 40 insertions(+), 16 deletions(-) diff --git a/examples/OneConnector-EVSE/OneConnector_EVSE.ino b/examples/OneConnector-EVSE/OneConnector_EVSE.ino index 84d5692a..67d94f7f 100644 --- a/examples/OneConnector-EVSE/OneConnector_EVSE.ino +++ b/examples/OneConnector-EVSE/OneConnector_EVSE.ino @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -11,8 +11,8 @@ #include #include -//ESP8266-OCPP modules -#include +//ArduinoOcpp modules +#include //GPIO-based HW integration (used as example) #include "OneConnector_HW_integration.h" diff --git a/examples/OneConnector-EVSE/OneConnector_HW_integration.h b/examples/OneConnector-EVSE/OneConnector_HW_integration.h index 3e030d0a..9bcadfc4 100644 --- a/examples/OneConnector-EVSE/OneConnector_HW_integration.h +++ b/examples/OneConnector-EVSE/OneConnector_HW_integration.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/examples/OneConnector-EVSE/OneConnector_HW_integration.ino b/examples/OneConnector-EVSE/OneConnector_HW_integration.ino index 438708b4..192c3f78 100644 --- a/examples/OneConnector-EVSE/OneConnector_HW_integration.ino +++ b/examples/OneConnector-EVSE/OneConnector_HW_integration.ino @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -8,7 +8,7 @@ #include -#include +#include /* * Demonstration video: https://youtu.be/hfTh9GjG-N4 diff --git a/examples/Simulation_without_HW/OneConnector_EVSE.ino b/examples/Simulation_without_HW/OneConnector_EVSE.ino index 49d433f4..ca204e4a 100644 --- a/examples/Simulation_without_HW/OneConnector_EVSE.ino +++ b/examples/Simulation_without_HW/OneConnector_EVSE.ino @@ -8,16 +8,22 @@ #include +#if defined(ESP32) +#include +#else #include #include +ESP8266WiFiMulti WiFiMulti; +#endif //ArduinoOcpp modules #include +#include +#include //Simulated HW integration (used as example) #include "OneConnector_HW_integration.h" -ESP8266WiFiMulti WiFiMulti; #define STASSID "YOUR_WLAN_SSID" #define STAPSK "YOUR_WLAN_PW" @@ -63,13 +69,24 @@ void setup() { delay(300); } +#if defined (ESP32) + WiFi.begin(STASSID, STAPSK); + + Serial.print(F("[main] Wait for WiFi: ")); + while (!WiFi.isConnected()) { + Serial.print('.'); + delay(1000); + } + Serial.print(F(" connected!\n")); +#else WiFiMulti.addAP(STASSID, STAPSK); while (WiFiMulti.run() != WL_CONNECTED) { delay(100); } +#endif - OCPP_initialize(OCPP_HOST, OCPP_PORT, OCPP_URL); + OCPP_initialize(OCPP_HOST, OCPP_PORT, OCPP_URL, ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail); setPowerActiveImportSampler([]() { return EVSE_readChargeRate(); //from the GPIO-charger example diff --git a/examples/Simulation_without_HW/OneConnector_HW_integration.h b/examples/Simulation_without_HW/OneConnector_HW_integration.h index 728b5be4..e9007058 100644 --- a/examples/Simulation_without_HW/OneConnector_HW_integration.h +++ b/examples/Simulation_without_HW/OneConnector_HW_integration.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License diff --git a/examples/Simulation_without_HW/Simulated_HW.ino b/examples/Simulation_without_HW/Simulated_HW.ino index 9577a60f..9305d13a 100644 --- a/examples/Simulation_without_HW/Simulated_HW.ino +++ b/examples/Simulation_without_HW/Simulated_HW.ino @@ -84,20 +84,27 @@ void EVSE_loop() { void onEvPlug() { Serial.print(F("[EVSE] EV plugged\n")); String fixedIdTag = String("abcdef123456789"); // e.g. idTag = readRFIDTag(); - authorize(fixedIdTag, [](JsonObject confMsg) { - startTransaction([](JsonObject conf) { - transactionRunning = true; - evRequestsEnergy = true; - Serial.print(F("[EVSE] Successfully authorized and started transaction\n")); + if (getTransactionId() < 0) { + authorize(fixedIdTag, [](JsonObject confMsg) { + startTransaction([](JsonObject conf) { + transactionRunning = true; + evRequestsEnergy = true; + Serial.print(F("[EVSE] Successfully authorized and started transaction\n")); + }); }); - }); + } else { + transactionRunning = true; + evRequestsEnergy = true; + Serial.print(F("[EVSE] Successfully authorized and started transaction II\n")); + } } void onEvUnplug() { Serial.print(F("[EVSE] EV unplugged\n")); transactionRunning = false; evRequestsEnergy = false; - stopTransaction(); + if (getTransactionId() >= 0) + stopTransaction(); } float EVSE_readChargeRate() { //estimation for EVSEs without power meter From c42c55c44b83fb5118eb19b996e2ac1251389c21 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 2 Jun 2021 18:31:41 +0200 Subject: [PATCH 033/696] removed deprecated files --- src/ArduinoOcpp/TimeHelper.cpp | 240 ---------------------------- src/ArduinoOcpp/TimeHelper.h | 39 ----- src/ESP8266-OCPP.cpp | 283 --------------------------------- src/ESP8266-OCPP.h | 109 ------------- src/EVSE.h | 51 ------ 5 files changed, 722 deletions(-) delete mode 100644 src/ArduinoOcpp/TimeHelper.cpp delete mode 100644 src/ArduinoOcpp/TimeHelper.h delete mode 100644 src/ESP8266-OCPP.cpp delete mode 100644 src/ESP8266-OCPP.h delete mode 100644 src/EVSE.h diff --git a/src/ArduinoOcpp/TimeHelper.cpp b/src/ArduinoOcpp/TimeHelper.cpp deleted file mode 100644 index 4e6b8e8d..00000000 --- a/src/ArduinoOcpp/TimeHelper.cpp +++ /dev/null @@ -1,240 +0,0 @@ -// matth-x/ESP8266-OCPP -// Copyright Matthias Akstaller 2019 - 2021 -// MIT License - -#include - -#include -#include -#include - -#if 0 - -namespace ArduinoOcpp { - -/** - * Expects a date string like - * 2020-10-01T20:53:32.486Z - * - * as generated in JavaScript by calling toJSON() on a Date object - * - * Only processes the first 19 characters. The subsequent are ignored until terminating 0. - * - * Has a semi-sophisticated type check included. Will return true on successful time set and false if - * the given string is not a JSON Date string. - * - * jsonDateString: 0-terminated string - */ -bool setTimeFromJsonDateString(const char* jsonDateString){ - time_t mDate = 0; - bool success = getTimeFromJsonDateString(jsonDateString, &mDate); - if (success) { - setTime(mDate); - } - return success; -} - -bool getTimeFromJsonDateString(const char* jsonDateString, time_t *result){ - - const int JSONDATE_MINLENGTH = 19; - - if (strlen(jsonDateString) < JSONDATE_MINLENGTH){ - return false; - } - - - if (!isdigit(jsonDateString[0]) || //2 - !isdigit(jsonDateString[1]) || //0 - !isdigit(jsonDateString[2]) || //1 - !isdigit(jsonDateString[3]) || //3 - jsonDateString[4] != '-' || //- - !isdigit(jsonDateString[5]) || //0 - !isdigit(jsonDateString[6]) || //2 - jsonDateString[7] != '-' || //- - !isdigit(jsonDateString[8]) || //0 - !isdigit(jsonDateString[9]) || //1 - jsonDateString[10] != 'T' || //T - !isdigit(jsonDateString[11]) || //2 - !isdigit(jsonDateString[12]) || //0 - jsonDateString[13] != ':' || //: - !isdigit(jsonDateString[14]) || //5 - !isdigit(jsonDateString[15]) || //3 - jsonDateString[16] != ':' || //: - !isdigit(jsonDateString[17]) || //3 - !isdigit(jsonDateString[18])) { //2 - //ignore subsequent characters - return false; - } - int year = (jsonDateString[0] - '0') * 1000 + - (jsonDateString[1] - '0') * 100 + - (jsonDateString[2] - '0') * 10 + - (jsonDateString[3] - '0'); - int month = (jsonDateString[5] - '0') * 10 + - (jsonDateString[6] - '0'); - int day = (jsonDateString[8] - '0') * 10 + - (jsonDateString[9] - '0'); - int hour = (jsonDateString[11] - '0') * 10 + - (jsonDateString[12] - '0'); - int minute = (jsonDateString[14] - '0') * 10 + - (jsonDateString[15] - '0'); - int second = (jsonDateString[17] - '0') * 10 + - (jsonDateString[18] - '0'); - //ignore fractals - - if (year < 1970 || year > 2100 || - month < 1 || month > 12 || - day < 1 || day > 31 || - hour < 0 || hour > 23 || - minute < 0 || minute > 59 || - second < 0 || second > 59) { - return false; - } - - /* - * not-so-sophisticated type-check successfully passed - */ - - TimeElements calendar = {}; - calendar.Second = second; - calendar.Minute = minute; - calendar.Hour = hour; - //uint8_t Wday; // day of week, sunday is day 1 - calendar.Day = day; - calendar.Month = month; - calendar.Year = CalendarYrToTm(year); // CalendarYrToTm(Y) = Y - 1970 - - *result = makeTime(calendar); - - return true; -} - -/** - * Writes the system time into jsonDateString. - * - * jsonDateString: output buffer. Must be JSONDATE_LENGTH + 1 characters long (+1 for the 0-terminator). - * Must be 0-initialized. - * bufflength: number of chars in jsonDateString - * Warning: writes up to bufflength characters into the buffer - * Returns true on success, false otherwise - * - * Example usage: - * char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; //timestamp must not be shorter - * if (!getJsonDateStringFromSystemTime(timestamp, JSONDATE_LENGTH)){ - * //catch error - * } - * json_document["timestamp"] = timestamp; - */ -bool getJsonDateStringFromSystemTime(char* jsonDateString, int bufflength){ - return getJsonDateStringFromGivenTime(jsonDateString, bufflength, now()); -} - -/** - * Writes the system time into jsonDateString. - * - * jsonDateString: output buffer. Must be JSONDATE_LENGTH + 1 characters long (+1 for the 0-terminator). - * Must be 0-initialized. - * bufflength: number of chars in jsonDateString - * Warning: writes up to bufflength characters into the buffer - * Returns true on success, false otherwise - * - * Example usage: - * char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; //timestamp must not be shorter - * if (!getJsonDateStringFromSystemTime(timestamp, JSONDATE_LENGTH)){ - * //catch error - * } - * json_document["timestamp"] = timestamp; - */ -bool getJsonDateStringFromGivenTime(char* jsonDateString, int bufflength, time_t t){ - if (bufflength < JSONDATE_LENGTH) return false; - - jsonDateString[0] = ((char) ((year(t) / 1000) % 10)) + '0'; - jsonDateString[1] = ((char) ((year(t) / 100) % 10)) + '0'; - jsonDateString[2] = ((char) ((year(t) / 10) % 10)) + '0'; - jsonDateString[3] = ((char) ((year(t) / 1) % 10)) + '0'; - jsonDateString[4] = '-'; - jsonDateString[5] = ((char) ((month(t) / 10) % 10)) + '0'; - jsonDateString[6] = ((char) ((month(t) / 1) % 10)) + '0'; - jsonDateString[7] = '-'; - jsonDateString[8] = ((char) ((day(t) / 10) % 10)) + '0'; - jsonDateString[9] = ((char) ((day(t) / 1) % 10)) + '0'; - jsonDateString[10] = 'T'; - jsonDateString[11] = ((char) ((hour(t) / 10) % 10)) + '0'; - jsonDateString[12] = ((char) ((hour(t) / 1) % 10)) + '0'; - jsonDateString[13] = ':'; - jsonDateString[14] = ((char) ((minute(t) / 10) % 10)) + '0'; - jsonDateString[15] = ((char) ((minute(t) / 1) % 10)) + '0'; - jsonDateString[16] = ':'; - jsonDateString[17] = ((char) ((second(t) / 10) % 10)) + '0'; - jsonDateString[18] = ((char) ((second(t) / 1) % 10)) + '0'; - - /* - * Determining the following characters is a little bit difficult. The format is not specified by - * OCPP and depends strongly on the internal implementation of the server this client should be - * connected to. - * - * However, the OCPP-J 1.6 document suggests to stick to the JSON Date format - * 2013-02-01T20:53:32.486Z - * - * Other possibilities are commented out and could be reused if necessary - */ - - /* 5 fractal digits and timezone specification - jsonDateString[19] = '.'; - jsonDateString[20] = '0'; //ignore fractals - jsonDateString[21] = '0'; - jsonDateString[22] = '0'; - jsonDateString[23] = '0'; - jsonDateString[24] = '0'; - jsonDateString[25] = '0'; - jsonDateString[26] = '+'; - jsonDateString[27] = '0'; //timezone set to +00:00 - jsonDateString[28] = '0'; - jsonDateString[29] = ':'; - jsonDateString[30] = '0'; - jsonDateString[31] = '0'; */ - - /* No fractal digits and timezone specification - jsonDateString[19] = '+'; - jsonDateString[20] = '0'; //ignore fractals - jsonDateString[21] = '0'; - jsonDateString[22] = ':'; - jsonDateString[23] = '0'; - jsonDateString[24] = '0'; */ - - - /* JSON Date variant. 3 fractal digits and subsequent 'Z' */ - - jsonDateString[19] = '.'; - jsonDateString[20] = '0'; //ignore fractals - jsonDateString[21] = '0'; - jsonDateString[22] = '0'; - jsonDateString[23] = 'Z'; - - return true; -} - -time_t minimum(time_t t1, time_t t2) { - if (t1 < t2) { - return t1; - } else { - return t2; - } -} - -void printTime(time_t t) { - Serial.print(year(t)); - Serial.print(F("-")); - Serial.print(month(t)); - Serial.print(F("-")); - Serial.print(day(t)); - Serial.print(F(", ")); - Serial.print(hour(t)); - Serial.print(F(":")); - Serial.print(minute(t)); - Serial.print(F(":")); - Serial.print(second(t)); -} - -} //end namespace ArduinoOcpp - -#endif diff --git a/src/ArduinoOcpp/TimeHelper.h b/src/ArduinoOcpp/TimeHelper.h deleted file mode 100644 index c85ff709..00000000 --- a/src/ArduinoOcpp/TimeHelper.h +++ /dev/null @@ -1,39 +0,0 @@ -// matth-x/ESP8266-OCPP -// Copyright Matthias Akstaller 2019 - 2021 -// MIT License - -#ifndef TIMEHELPER_H -#define TIMEHELPER_H - -#if 0 - -#include - -//Infinity begins in the end of year 2035 -//... it is close to the "year 2038 problem" that occurs if signed longs were used for time_t but has some space -//left to prevent overflows if something is added to INFINITY -//As long as it is garantueed that time_t uses unsigned longs, CalendarYrToTm(2036) could be raised to CalenderYrToTm(2100) -#define INFINITY_THLD ((time_t) (((time_t) SECS_PER_YEAR) * ((time_t) CalendarYrToTm(2036)))) -#define INFINITY_STRING "2036-01-01T00:00:00.000Z" - - -#define JSONDATE_LENGTH 24 //JSON Date -//#define JSONDATE_LENGTH 32 //ISO 8601 - -namespace ArduinoOcpp { - -bool setTimeFromJsonDateString(const char* jsonDateString); - -bool getJsonDateStringFromSystemTime(char* jsonDateString, int bufflength); - -bool getJsonDateStringFromGivenTime(char* jsonDateString, int bufflength, time_t t); - -bool getTimeFromJsonDateString(const char* jsonDateString, time_t *result); - -time_t minimum(time_t t1, time_t t2); - -void printTime(time_t t); - -} //end namespace ArduinoOcpp -#endif -#endif diff --git a/src/ESP8266-OCPP.cpp b/src/ESP8266-OCPP.cpp deleted file mode 100644 index 54c38422..00000000 --- a/src/ESP8266-OCPP.cpp +++ /dev/null @@ -1,283 +0,0 @@ -// matth-x/ESP8266-OCPP -// Copyright Matthias Akstaller 2019 - 2021 -// MIT License - -#if 0 //DEPRECATED! Everything has moved to ArduinoOcpp.cpp - -#include "ESP8266-OCPP.h" - -#include "Variants.h" - -#if USE_FACADE - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -WebSocketsClient webSocket; - -MeteringService *meteringService; -PowerSampler powerSampler; -EnergySampler energySampler; -bool (*evRequestsEnergySampler)() = NULL; -bool evRequestsEnergyLastState = false; -SmartChargingService *smartChargingService; -ChargePointStatusService *chargePointStatusService; -OnLimitChange onLimitChange; - -#define OCPP_NUMCONNECTORS 2 -#define OCPP_ID_OF_CONNECTOR 1 -#define OCPP_ID_OF_CP 0 -boolean OCPP_initialized = false; - -/* - Called by Websocket library on incoming message on the internet link -*/ -void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { - - switch (type) { - case WStype_DISCONNECTED: - Serial.print(F("[WSc] Disconnected!\n")); - break; - case WStype_CONNECTED: - Serial.printf("[WSc] Connected to url: %s\n", payload); - break; - case WStype_TEXT: - if (DEBUG_OUT || TRAFFIC_OUT) Serial.printf("[WSc] get text: %s\n", payload); - - if (!processWebSocketEvent((const char *) payload, length)) { //forward message to OcppEngine - Serial.print(F("[WSc] Processing WebSocket input event failed!\n")); - } - break; - case WStype_FRAGMENT_TEXT_START: //fragments are not supported - Serial.print(F("[WSc] Fragments are not supported\n")); - if (!processWebSocketUnsupportedEvent((const char *) payload, length)) { //forward message to OcppEngine - Serial.print(F("[WSc] Processing WebSocket input event failed!\n")); - } - break; - case WStype_BIN: - Serial.print(F("[WSc] Incoming binary data stream not supported")); - break; - case WStype_PING: - // pong will be send automatically - Serial.print(F("[WSc] get ping\n")); - break; - case WStype_PONG: - // answer to a ping we send - Serial.print(F("[WSc] get pong\n")); - break; - default: - Serial.print(F("[WSc] Unsupported WebSocket event type\n")); - break; - } -} - -void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url) { - if (OCPP_initialized) { - Serial.print(F("[SingleConnectorEvseFacade] Error: cannot call OCPP_initialize() two times! If you want to reconfigure the library, please restart your ESP\n")); - return; - } - - configuration_init(); //call before each other library call - - // server address, port and URL - webSocket.begin(CS_hostname, CS_port, CS_url, "ocpp1.6"); - - // event handler - webSocket.onEvent(webSocketEvent); - - // use HTTP Basic Authorization this is optional remove if not needed - // webSocket.setAuthorization("user", "Password"); - - // try ever 5000 again if connection has failed - webSocket.setReconnectInterval(5000); - - // start heartbeat (optional) - // ping server every 15000 ms - // expect pong from server within 3000 ms - // consider connection disconnected if pong is not received 2 times - webSocket.enableHeartbeat(15000, 3000, 2); //comment this one out to for specific OCPP servers - - ocppEngine_initialize(&webSocket, 2048); //default JSON document size = 2048 - - smartChargingService = new SmartChargingService(16.0f, OCPP_NUMCONNECTORS); //default charging limit: 16A - chargePointStatusService = new ChargePointStatusService(&webSocket, OCPP_NUMCONNECTORS); //Constructor adds instance to ocppEngine in constructor - meteringService = new MeteringService(&webSocket, OCPP_NUMCONNECTORS); - - OCPP_initialized = true; -} - -void OCPP_loop() { - if (!OCPP_initialized) { - Serial.print(F("[SingleConnectorEvseFacade] Error: you must call OCPP_initialize before calling the loop() function!\n")); - delay(200); //Prevent this error message from flooding the Serial monitor. - return; - } - - webSocket.loop(); //mandatory - ocppEngine_loop(); //mandatory - - if (onLimitChange != NULL) { - smartChargingService->loop(); //optional - } - - chargePointStatusService->loop(); //optional - - if (powerSampler != NULL || energySampler != NULL) { - meteringService->loop(); //optional - } - - bool evRequestsEnergyNewState = true; - if (evRequestsEnergySampler != NULL) { - evRequestsEnergyNewState = evRequestsEnergySampler(); - } else { - if (powerSampler != NULL) { - evRequestsEnergyNewState = powerSampler() >= 5.f; - } - } - - if (!evRequestsEnergyLastState && evRequestsEnergyNewState) { - evRequestsEnergyLastState = true; - chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->startEvDrawsEnergy(); - } else if (evRequestsEnergyLastState && !evRequestsEnergyNewState) { - evRequestsEnergyLastState = false; - chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->stopEvDrawsEnergy(); - } - -} - -void setPowerActiveImportSampler(float power()) { - powerSampler = power; - meteringService->setPowerSampler(OCPP_ID_OF_CONNECTOR, powerSampler); //connectorId=1 -} - -void setEnergyActiveImportSampler(float energy()) { - energySampler = energy; - meteringService->setEnergySampler(OCPP_ID_OF_CONNECTOR, energySampler); //connectorId=1 -} - -void setEvRequestsEnergySampler(bool evRequestsEnergy()) { - evRequestsEnergySampler = evRequestsEnergy; - -} - -void setOnChargingRateLimitChange(void chargingRateChanged(float limit)) { - onLimitChange = chargingRateChanged; - smartChargingService->setOnLimitChange(onLimitChange); -} - -void setOnSetChargingProfileRequest(void listener(JsonObject payload)) { - setOnSetChargingProfileRequestListener(listener); -} - -void setOnRemoteStartTransactionSendConf(void listener(JsonObject payload)) { - setOnRemoteStartTransactionSendConfListener(listener); -} - -void setOnRemoteStopTransactionSendConf(void listener(JsonObject payload)) { - setOnRemoteStopTransactionSendConfListener(listener); -} - -void setOnResetSendConf(void listener(JsonObject payload)) { - setOnResetSendConfListener(listener); -} - -void authorize(String &idTag, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError) { - OcppOperation *authorize = makeOcppOperation(&webSocket, - new Authorize(idTag)); - initiateOcppOperation(authorize); - if (onConf) - authorize->setOnReceiveConfListener(onConf); - if (onAbort) - authorize->setOnAbortListener(onAbort); - if (onTimeout) - authorize->setOnTimeoutListener(onTimeout); - if (onError) - authorize->setOnReceiveErrorListener(onError); - authorize->setTimeout(new FixedTimeout(20000)); -} - -void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError) { - OcppOperation *bootNotification = makeOcppOperation(&webSocket, - new BootNotification(chargePointModel, chargePointVendor)); - initiateOcppOperation(bootNotification); - if (onConf) - bootNotification->setOnReceiveConfListener(onConf); - if (onAbort) - bootNotification->setOnAbortListener(onAbort); - if (onTimeout) - bootNotification->setOnTimeoutListener(onTimeout); - if (onError) - bootNotification->setOnReceiveErrorListener(onError); - bootNotification->setTimeout(new SuppressedTimeout()); -} - -void bootNotification(String &chargePointModel, String &chargePointVendor, String &chargePointSerialNumber, OnReceiveConfListener onConf) { - OcppOperation *bootNotification = makeOcppOperation(&webSocket, - new BootNotification(chargePointModel, chargePointVendor, chargePointSerialNumber)); - initiateOcppOperation(bootNotification); - bootNotification->setOnReceiveConfListener(onConf); - bootNotification->setTimeout(new SuppressedTimeout()); -} - -void startTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError) { - OcppOperation *startTransaction = makeOcppOperation(&webSocket, - new StartTransaction(OCPP_ID_OF_CONNECTOR)); - initiateOcppOperation(startTransaction); - if (onConf) - startTransaction->setOnReceiveConfListener(onConf); - if (onAbort) - startTransaction->setOnAbortListener(onAbort); - if (onTimeout) - startTransaction->setOnTimeoutListener(onTimeout); - if (onError) - startTransaction->setOnReceiveErrorListener(onError); - startTransaction->setTimeout(new FixedTimeout(20000)); -} - -void startTransaction(String &idTag, OnReceiveConfListener onConf) { - OcppOperation *startTransaction = makeOcppOperation(&webSocket, - new StartTransaction(OCPP_ID_OF_CONNECTOR, idTag)); - initiateOcppOperation(startTransaction); - startTransaction->setOnReceiveConfListener(onConf); - startTransaction->setTimeout(new FixedTimeout(20000)); -} - -void stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError) { - OcppOperation *stopTransaction = makeOcppOperation(&webSocket, - new StopTransaction(OCPP_ID_OF_CONNECTOR)); - initiateOcppOperation(stopTransaction); - if (onConf) - stopTransaction->setOnReceiveConfListener(onConf); - if (onAbort) - stopTransaction->setOnAbortListener(onAbort); - if (onTimeout) - stopTransaction->setOnTimeoutListener(onTimeout); - if (onError) - stopTransaction->setOnReceiveErrorListener(onError); - stopTransaction->setTimeout(new SuppressedTimeout()); -} - -//void startEvDrawsEnergy() { -// chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->startEvDrawsEnergy(); -//} - -//void stopEvDrawsEnergy() { -// chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->stopEvDrawsEnergy(); -//} - -int getTransactionId() { - return chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->getTransactionId(); -} - -#endif -#endif diff --git a/src/ESP8266-OCPP.h b/src/ESP8266-OCPP.h deleted file mode 100644 index 74c7e06b..00000000 --- a/src/ESP8266-OCPP.h +++ /dev/null @@ -1,109 +0,0 @@ -// matth-x/ESP8266-OCPP -// Copyright Matthias Akstaller 2019 - 2021 -// MIT License - -#ifndef ESP8266OCPP_H -#define ESP8266OCPP_H - -#include "ArduinoOcpp.h" -#if 0 //DEPRECATED! Everything was moved to ArduinoOcpp.h - -#include - -#include "Variants.h" - -#if USE_FACADE - - -void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url); - -void OCPP_loop(); - -/* - * Provide hardware-related information to the library - * - * The library needs a way to obtain information from your charger that changes over time. The - * library calls following callbacks regularily (if they were set) to refresh its internal - * status. - * - * Set the callbacks once in your setup() function. - */ - -void setPowerActiveImportSampler(float power()); - -void setEnergyActiveImportSampler(float energy()); - -void setEvRequestsEnergySampler(bool evRequestsEnergy()); - -/* - * React on calls by the library's internal functions - * - * The library needs to set parameters on your charger on a regular basis. The library calls - * following callbacks regularily (if they were set) to perform updates on your charger. - * - * Set the callbacks once in your setup() function. - */ - -void setOnChargingRateLimitChange(void chargingRateChanged(float limit)); - -/* - * React on CS-initiated operations - * - * You can define custom behaviour in your integration which is executed every time the library - * receives a CS-initiated operation. The following functions set a callback function for the - * respective event. The library executes the callbacks always after its internal routines. - * - * Set the callbacks once in your setup() function. - */ - -void setOnSetChargingProfileRequest(void listener(JsonObject payload)); //optional - -void setOnRemoteStartTransactionSendConf(void listener(JsonObject payload)); //important, energize the power plug here - -void setOnRemoteStopTransactionSendConf(void listener(JsonObject payload)); //important, de-energize the power plug here - -void setOnResetSendConf(void listener(JsonObject payload)); //important, reset your device here (i.e. call ESP.reset();) - -/* - * Perform CP-initiated operations - * - * Use following functions to send OCPP messages. Each function will adapt the library's - * internal state appropriatley (e.g. after a successful StartTransaction request the library - * will store the transactionId and send a StatusNotification). - * - * On receipt of the .conf() response the library calls the callback function - * "OnReceiveConfListener onConf" and passes the OCPP payload to it. The following functions - * are non-blocking. Your application code will immediately resume with the subsequent code - * in any case. - */ - -void authorize(String &idTag, OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL); - -void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL); - -void startTransaction(OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL); - -void stopTransaction(OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL); - -/* - * Provide hardware-related information II - * - * When the EVSE state changes, you must notify the library in order to make StatusNotification - * work properly. - * - * Call these functions in your integration. - */ - -//void startEvDrawsEnergy(); //<-- please use setEvRequestsEnergySampler(bool evRequestsEnergy()); - -//void stopEvDrawsEnergy(); //<-- please use setEvRequestsEnergySampler(bool evRequestsEnergy()); - -/* - * Access information about the internal state of the library - */ - -int getTransactionId(); //returns the ID of the current transaction. Returns -1 if called before or after an transaction - -#endif -#endif -#endif diff --git a/src/EVSE.h b/src/EVSE.h deleted file mode 100644 index b5347c1d..00000000 --- a/src/EVSE.h +++ /dev/null @@ -1,51 +0,0 @@ -// matth-x/ESP8266-OCPP -// Copyright Matthias Akstaller 2019 - 2021 -// MIT License - -#include -#if !USE_FACADE - -#ifndef EVSE_H -#define EVSE_H - -#include - -typedef void (*OnEvPlug)(); -typedef void (*OnEvUnplug)(); -typedef void (*OnProvideAuthorization)(); -typedef void (*OnRevokeAuthorization)(); - -typedef void (*OnBoot)(); - -//typedef void (*OnEvRequestsCharge)(); -//typedef void (*OnEvSuspended)(); - - - -void EVSE_initialize(); - -void EVSE_setChargingLimit(float limit); -void EVSE_startEnergyOffer(); -void EVSE_stopEnergyOffer(); -float EVSE_readChargeRate(); -float EVSE_readEnergyRegister(); - -bool EVSE_EvRequestsCharge(); - -bool EVSE_EvIsPlugged(); -bool EVSE_authorizationProvided(); - -void EVSE_setOnEvPlug(OnEvPlug onEvPlug); -void EVSE_setOnEvUnplug(OnEvUnplug onEvUnplug); -void EVSE_setOnProvideAuthorization(OnProvideAuthorization onProvideAuthorization); -void EVSE_setOnRevokeAuthorization(OnRevokeAuthorization onRevokeAuthorization); -void EVSE_setOnBoot(OnBoot onBoot); -void EVSE_loop(); - -void EVSE_getChargePointSerialNumber(String &out); -char *EVSE_getChargePointVendor(); -char *EVSE_getChargePointModel(); - -#endif - -#endif From 00483d52c740f01aca215d0a2cdcb78df085f267 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 2 Jun 2021 18:33:47 +0200 Subject: [PATCH 034/696] make debug build_flags optional --- platformio.ini | 8 ++++---- src/ArduinoOcpp/Core/ConfigurationContainer.cpp | 1 + src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp | 1 + src/ArduinoOcpp/Core/OcppConnection.cpp | 1 + src/ArduinoOcpp/Core/OcppEngine.cpp | 2 ++ src/ArduinoOcpp/Core/OcppMessage.cpp | 2 ++ src/ArduinoOcpp/Core/OcppOperationTimeout.cpp | 2 ++ src/ArduinoOcpp/Core/OcppTime.cpp | 1 + src/ArduinoOcpp/MessagesV16/Heartbeat.cpp | 1 + src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp | 2 -- src/Variants.h | 10 +++++++++- 11 files changed, 24 insertions(+), 7 deletions(-) diff --git a/platformio.ini b/platformio.ini index 373aaa54..668266a7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -29,8 +29,8 @@ lib_deps = ${common.lib_deps} monitor_speed = ${common.monitor_speed} build_flags = ${common.build_flags} - -D DEBUG_OUT=true - -D TRAFFIC_OUT=true + -DAO_DEBUG_OUT + -DAO_TRAFFIC_OUT [env:esp32-development-board] platform = espressif32@1.11.1 @@ -42,8 +42,8 @@ lib_deps = monitor_speed = ${common.monitor_speed} build_flags = ${common.build_flags} - -D DEBUG_OUT=true - -D TRAFFIC_OUT=true + -DAO_DEBUG_OUT + -DAO_TRAFFIC_OUT -DCONFIG_LITTLEFS_FOR_IDF_3_2 build_partitions = min_spiffs.csv upload_speed = 921600 diff --git a/src/ArduinoOcpp/Core/ConfigurationContainer.cpp b/src/ArduinoOcpp/Core/ConfigurationContainer.cpp index 12de79f5..e735d1f5 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainer.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainer.cpp @@ -3,6 +3,7 @@ // MIT License #include +#include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp index 5f088683..94f9c0a7 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp @@ -3,6 +3,7 @@ // MIT License #include +#include #if defined(ESP32) #define USE_FS LITTLEFS diff --git a/src/ArduinoOcpp/Core/OcppConnection.cpp b/src/ArduinoOcpp/Core/OcppConnection.cpp index f3d08f3a..6db6238b 100644 --- a/src/ArduinoOcpp/Core/OcppConnection.cpp +++ b/src/ArduinoOcpp/Core/OcppConnection.cpp @@ -7,6 +7,7 @@ #include #include +#include #define HEAP_GUARD 2000UL //will not accept JSON messages if it will result in less than HEAP_GUARD free bytes in heap diff --git a/src/ArduinoOcpp/Core/OcppEngine.cpp b/src/ArduinoOcpp/Core/OcppEngine.cpp index 8978287c..7214a1b7 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.cpp +++ b/src/ArduinoOcpp/Core/OcppEngine.cpp @@ -10,6 +10,8 @@ #include +#include + namespace ArduinoOcpp { namespace OcppEngine { diff --git a/src/ArduinoOcpp/Core/OcppMessage.cpp b/src/ArduinoOcpp/Core/OcppMessage.cpp index c1fb047e..ebd7e1db 100644 --- a/src/ArduinoOcpp/Core/OcppMessage.cpp +++ b/src/ArduinoOcpp/Core/OcppMessage.cpp @@ -4,6 +4,8 @@ #include +#include + using ArduinoOcpp::OcppMessage; OcppMessage::OcppMessage(){} diff --git a/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp b/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp index b80790ce..1a6a37a4 100644 --- a/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp +++ b/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp @@ -4,6 +4,8 @@ #include +#include + using namespace ArduinoOcpp; void Timeout::setOnTimeoutListener(OnTimeoutListener onTimeout) { diff --git a/src/ArduinoOcpp/Core/OcppTime.cpp b/src/ArduinoOcpp/Core/OcppTime.cpp index f1d95d06..f972d9b1 100644 --- a/src/ArduinoOcpp/Core/OcppTime.cpp +++ b/src/ArduinoOcpp/Core/OcppTime.cpp @@ -3,6 +3,7 @@ // MIT License #include +#include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp b/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp index 6671aeb5..c8f45def 100644 --- a/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp +++ b/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp @@ -5,6 +5,7 @@ #include #include #include +#include using ArduinoOcpp::Ocpp16::Heartbeat; diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp index 4191ea62..36a2eafc 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp @@ -8,8 +8,6 @@ #include #include -#include - using namespace ArduinoOcpp; using namespace ArduinoOcpp::Ocpp16; diff --git a/src/Variants.h b/src/Variants.h index dd91cc14..cf483a51 100644 --- a/src/Variants.h +++ b/src/Variants.h @@ -1,4 +1,4 @@ -// matth-x/ESP8266-OCPP +// matth-x/ArduinoOcpp // Copyright Matthias Akstaller 2019 - 2021 // MIT License @@ -6,11 +6,19 @@ #define VARIANTS_H #ifndef DEBUG_OUT +#ifdef AO_DEBUG_OUT +#define DEBUG_OUT true //Print debug messages on Serial. Gives more insights about inner processes +#else #define DEBUG_OUT false //Print debug messages on Serial. Gives more insights about inner processes #endif +#endif #ifndef TRAFFIC_OUT +#ifdef AO_TRAFFIC_OUT #define TRAFFIC_OUT true //Print OCPP communication on Serial. Gives insights about communication with the Central System +#else +#define TRAFFIC_OUT false +#endif #endif #ifndef USE_FACADE From ad15e02a9f69fe7b782de026e8f08ae1973067f1 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 2 Jun 2021 19:27:35 +0200 Subject: [PATCH 035/696] remove dependencies --- library.json | 13 +++++-------- library.properties | 4 ++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/library.json b/library.json index 4f94db3a..960e5907 100644 --- a/library.json +++ b/library.json @@ -1,8 +1,8 @@ { "name": "ArduinoOcpp", "version": "0.0.1", - "description": "OCPP 1.6 Client for the ESP8266 (ESP32 and more coming soon)", - "keywords": "OCPP, 1.6, OCPP 1.6, Smart Energy, Smart Charging, client, ESP8266, Arduino", + "description": "OCPP 1.6 Client for the ESP8266 and ESP32 (more coming soon)", + "keywords": "OCPP, 1.6, OCPP 1.6, Smart Energy, Smart Charging, client, ESP8266, ESP32, Arduino", "repository": { "type": "git", @@ -20,12 +20,10 @@ "homepage": "https://www.arduino-ocpp.com", "dependencies": { "ArduinoJson": "6.17.2", - "WebSockets": "2.2.0", - "ivanseidel/LinkedList": "0.0.0-alpha+sha.dac3874d28", - "Time": "1.6" + "WebSockets": "2.2.0" }, "frameworks": "arduino", - "platforms": "espressif8266", + "platforms": "espressif8266, espressif32", "export": { "include": @@ -39,8 +37,7 @@ "exclude": [ "src/OneConnector*", - "src/Simulated_HW*", - "src/OcppServer*" + "src/Simulated_HW*" ] } } \ No newline at end of file diff --git a/library.properties b/library.properties index 35c1d8e5..bad6b924 100644 --- a/library.properties +++ b/library.properties @@ -2,8 +2,8 @@ name=ArduinoOcpp version=0.0.1 author=Matthias Akstaller maintainer=Matthias Akstaller -sentence=OCPP 1.6 Client for the ESP8266 (ESP32 and more coming soon) +sentence=OCPP 1.6 Client for the ESP8266 and ESP32 (more coming soon) paragraph= category=Communication url=https://github.com/matth-x/ArduinoOcpp/ -architectures=espressif8266 +architectures=espressif8266, espressif32 From 082692232c21f4e1f8b1a0c9d54c3b1b77c7f2c1 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 2 Jun 2021 21:26:16 +0200 Subject: [PATCH 036/696] update ESP32 example --- examples/ESP32/main.ino | 97 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 examples/ESP32/main.ino diff --git a/examples/ESP32/main.ino b/examples/ESP32/main.ino new file mode 100644 index 00000000..008cc97e --- /dev/null +++ b/examples/ESP32/main.ino @@ -0,0 +1,97 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include +#include + +#include + +#define STASSID "YOUR_WLAN_SSID" +#define STAPSK "YOUR_WLAN_PW" + +#define OCPP_HOST "wss://echo.websocket.org" +#define OCPP_PORT 80 +#define OCPP_URL "wss://echo.websocket.org/" + +void setup() { + + /* + * Initialize Serial and WiFi + */ + + Serial.begin(115200); + Serial.setDebugOutput(true); + + WiFi.begin(STASSID, STAPSK); + + Serial.print(F("[main] Wait for WiFi: ")); + while (!WiFi.isConnected()) { + Serial.print('.'); + delay(1000); + } + Serial.print(F(" connected!\n")); + + /* + * Initialize the OCPP library + */ + OCPP_initialize(OCPP_HOST, OCPP_PORT, OCPP_URL); + + /* + * Integrate OCPP functionality. You can leave out the following part if your EVSE doesn't need it. + */ + setPowerActiveImportSampler([]() { + //measure the input power of the EVSE here and return the value in Watts + return 0.f; + }); + + setOnChargingRateLimitChange([](float limit) { + //set the SAE J1772 Control Pilot value here + Serial.print(F("[main] Smart Charging allows maximum charge rate: ")); + Serial.println(limit); + }); + + setEvRequestsEnergySampler([]() { + //return true if the EV is in state "Ready for charging" (see https://en.wikipedia.org/wiki/SAE_J1772#Control_Pilot) + return false; + }); + + //... see ArduinoOcpp.h for more settings + + /* + * Notify the Central System that this station is ready + */ + bootNotification("My Charging Station", "My company name"); +} + +void loop() { + + /* + * Do all OCPP stuff (process WebSocket input, send recorded meter values to Central System, etc.) + */ + OCPP_loop(); + + /* + * Detect if something physical happened at your EVSE and trigger the corresponding OCPP messages + */ + if (/* RFID chip detected? */ false) { + String idTag = "my-id-tag"; //e.g. idTag = RFID.readIdTag(); + authorize(idTag); + } + + if (/* EV plugged in? */ false) { + startTransaction([] (JsonObject payload) { + //Callback: Central System has answered. Energize your EV plug inside this callback and flash a confirmation light if you want. + Serial.print(F("[main] Started OCPP transaction. EV plug energized\n")); + }); + } + + if (/* EV plugged out? */ false) { + stopTransaction([] (JsonObject payload) { + //Callback: Central System has answered. Energize your EV plug inside this callback and flash a confirmation light if you want. + Serial.print(F("[main] Started OCPP transaction. EV plug energized\n")); + }); + } + + //... see ArduinoOcpp.h for more possibilities +} From dbf66f9582aa527e69776efa0b404903b2daad3a Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 2 Jun 2021 21:36:11 +0200 Subject: [PATCH 037/696] removed ESP32 port hint --- examples/ESP32/README.md | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 examples/ESP32/README.md diff --git a/examples/ESP32/README.md b/examples/ESP32/README.md deleted file mode 100644 index 10f700cf..00000000 --- a/examples/ESP32/README.md +++ /dev/null @@ -1,3 +0,0 @@ -#### ESP32 port progress - -The ESP32 port is almost finished. Only the persistent storage is still to be worked on. Please clone the branch `develop` and set the build flag `AO_DEACTIVATE_FLASH`. Alternatively, you can search for the `#ifndef AO_DEACTIVATE_FLASH` statements in `Configuration.cpp` and `SmartChargingService.cpp` and delete everything inside their scope. From 7bf31b4dedc2e67db2bbb198f0e6c71979222b41 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 4 Jun 2021 20:49:47 +0200 Subject: [PATCH 038/696] examples update --- examples/ESP32/main.ino | 7 +++++++ library.json | 38 ++++++++++++++++++++++++++++++++++---- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/examples/ESP32/main.ino b/examples/ESP32/main.ino index 008cc97e..29ce396a 100644 --- a/examples/ESP32/main.ino +++ b/examples/ESP32/main.ino @@ -14,6 +14,13 @@ #define OCPP_PORT 80 #define OCPP_URL "wss://echo.websocket.org/" +// +//// Settings which worked for my SteVe instance +// +//#define OCPP_HOST "my.instance.com" +//#define OCPP_PORT 80 +//#define OCPP_URL "ws://my.instance.com/steve/websocket/CentralSystemService/gpio-based-charger" + void setup() { /* diff --git a/library.json b/library.json index 960e5907..7b40354e 100644 --- a/library.json +++ b/library.json @@ -1,8 +1,8 @@ { "name": "ArduinoOcpp", - "version": "0.0.1", + "version": "0.0.2", "description": "OCPP 1.6 Client for the ESP8266 and ESP32 (more coming soon)", - "keywords": "OCPP, 1.6, OCPP 1.6, Smart Energy, Smart Charging, client, ESP8266, ESP32, Arduino", + "keywords": "OCPP, 1.6, OCPP 1.6, Smart Energy, Smart Charging, client, ESP8266, ESP32, Arduino, EVSE, Charge Point", "repository": { "type": "git", @@ -32,12 +32,42 @@ "examples/*", "platformio.ini", "library.json", - "LICENSE" + "LICENSE", + "min_spiffs.csv" ], "exclude": [ "src/OneConnector*", - "src/Simulated_HW*" + "src/Simulated_HW*", + "src/sdkconfig*" + ] + }, + + "examples": [ + { + "name": "ESP32 / ESP8266 OCPP connection", + "base": "examples/ESP32", + "files": [ + "main.ino" + ] + }, + { + "name": "ESP8266 OCPP test client", + "base": "examples/Simulation_without_HW", + "files": [ + "OneConnector_EVSE.ino", + "OneConnector_HW_integration.h", + "Simulated_HW.ino" + ] + }, + { + "name": "ESP8266 GPIO-based EVSE", + "base": "examples/OneConnector-EVSE", + "files": [ + "OneConnector_EVSE.ino", + "OneConnector_HW_integration.h", + "OneConnector_HW_integration.ino" ] } + ] } \ No newline at end of file From 7597e7d8d6e3d932b8ad8ffb622ccd1ebb1e54d0 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 5 Jun 2021 16:12:53 +0200 Subject: [PATCH 039/696] open for custom filesystems --- examples/ESP32/main.ino | 6 +++--- src/ArduinoOcpp/Core/Configuration.cpp | 6 +++++- src/ArduinoOcpp/Core/Configuration.h | 3 +++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/examples/ESP32/main.ino b/examples/ESP32/main.ino index 29ce396a..86629424 100644 --- a/examples/ESP32/main.ino +++ b/examples/ESP32/main.ino @@ -93,10 +93,10 @@ void loop() { }); } - if (/* EV plugged out? */ false) { + if (/* EV unplugged? */ false) { stopTransaction([] (JsonObject payload) { - //Callback: Central System has answered. Energize your EV plug inside this callback and flash a confirmation light if you want. - Serial.print(F("[main] Started OCPP transaction. EV plug energized\n")); + //Callback: Central System has answered. De-energize EV plug here. + Serial.print(F("[main] Stopped OCPP transaction. EV plug de-energized\n")); }); } diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index 80c830e7..798141c0 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -4,7 +4,7 @@ #include //#include -#include + #include @@ -66,6 +66,10 @@ std::shared_ptr createConfigurationContainer(const char std::vector> configurationContainers; +void addConfigurationContainer(std::shared_ptr container) { + configurationContainers.push_back(container); +} + std::shared_ptr getContainer(const char *filename) { std::vector>::iterator container = std::find_if(configurationContainers.begin(), configurationContainers.end(), [filename](std::shared_ptr &elem) { diff --git a/src/ArduinoOcpp/Core/Configuration.h b/src/ArduinoOcpp/Core/Configuration.h index b2d587da..50222601 100644 --- a/src/ArduinoOcpp/Core/Configuration.h +++ b/src/ArduinoOcpp/Core/Configuration.h @@ -7,6 +7,7 @@ #include #include +#include #define CONFIGURATION_FN "/arduino-ocpp.cnf" @@ -15,6 +16,8 @@ namespace ArduinoOcpp { template std::shared_ptr> declareConfiguration(const char *key, T defaultValue, const char *filename = CONFIGURATION_FN, bool remotePeerCanWrite = true, bool remotePeerCanRead = true, bool localClientCanWrite = true, bool rebootRequiredWhenChanged = false); +void addConfigurationContainer(std::shared_ptr container); + namespace Ocpp16 { // template From 1acf8380d31ac94b442d9d52fed9a3e4992178e5 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 16 Jun 2021 12:10:22 +0200 Subject: [PATCH 040/696] switches for debug output --- platformio.ini | 4 ++-- src/ArduinoOcpp/Core/OcppSocket.cpp | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/platformio.ini b/platformio.ini index 668266a7..d508fed1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,8 +15,8 @@ default_envs = esp32-development-board [common] framework = arduino lib_deps = - ArduinoJson@6.17.2 - WebSockets@2.2.0 + bblanchon/ArduinoJson@6.17.2 + links2004/WebSockets@2.2.0 build_flags = -D USE_FACADE=true monitor_speed = 115200 diff --git a/src/ArduinoOcpp/Core/OcppSocket.cpp b/src/ArduinoOcpp/Core/OcppSocket.cpp index 273c0bb5..7a8d19f1 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.cpp +++ b/src/ArduinoOcpp/Core/OcppSocket.cpp @@ -27,32 +27,32 @@ void OcppClientSocket::setReceiveTXTcallback(ReceiveTXTcallback &callback) { wsock->onEvent([callback](WStype_t type, uint8_t * payload, size_t length) { switch (type) { case WStype_DISCONNECTED: - Serial.print(F("[OcppClientSocket] Disconnected!\n")); + if (DEBUG_OUT) Serial.print(F("[OcppClientSocket] Disconnected!\n")); break; case WStype_CONNECTED: - Serial.printf("[OcppClientSocket] Connected to url: %s\n", payload); + if (DEBUG_OUT) Serial.printf("[OcppClientSocket] Connected to url: %s\n", payload); break; case WStype_TEXT: if (DEBUG_OUT || TRAFFIC_OUT) Serial.printf("[OcppClientSocket] get text: %s\n", payload); if (!callback((const char *) payload, length)) { //forward message to OcppEngine - Serial.print(F("[OcppClientSocket] Processing WebSocket input event failed!\n")); + if (DEBUG_OUT) Serial.print(F("[OcppClientSocket] Processing WebSocket input event failed!\n")); } break; case WStype_BIN: - Serial.print(F("[OcppClientSocket] Incoming binary data stream not supported")); + if (DEBUG_OUT) Serial.print(F("[OcppClientSocket] Incoming binary data stream not supported")); break; case WStype_PING: // pong will be send automatically - Serial.print(F("[OcppClientSocket] get ping\n")); + if (DEBUG_OUT || TRAFFIC_OUT) Serial.print(F("[OcppClientSocket] get ping\n")); break; case WStype_PONG: // answer to a ping we send - Serial.print(F("[OcppClientSocket] get pong\n")); + if (DEBUG_OUT || TRAFFIC_OUT) Serial.print(F("[OcppClientSocket] get pong\n")); break; case WStype_FRAGMENT_TEXT_START: //fragments are not supported default: - Serial.print(F("[OcppClientSocket] Unsupported WebSocket event type\n")); + if (DEBUG_OUT) Serial.print(F("[OcppClientSocket] Unsupported WebSocket event type\n")); break; } }); From 81ac726f061b1fdc0fe043467b3bc09dee59818e Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sun, 20 Jun 2021 13:13:22 +0200 Subject: [PATCH 041/696] add firmware update messages --- README.md | 5 +++ .../FirmwareStatusNotification.cpp | 30 ++++++++++++++ .../MessagesV16/FirmwareStatusNotification.h | 34 ++++++++++++++++ .../MessagesV16/UpdateFirmware.cpp | 40 +++++++++++++++++++ src/ArduinoOcpp/MessagesV16/UpdateFirmware.h | 32 +++++++++++++++ .../SimpleOcppOperationFactory.cpp | 12 ++---- 6 files changed, 145 insertions(+), 8 deletions(-) create mode 100644 src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp create mode 100644 src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h create mode 100644 src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp create mode 100644 src/ArduinoOcpp/MessagesV16/UpdateFirmware.h diff --git a/README.md b/README.md index 22529e57..03b08976 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,11 @@ In case you use PlatformIO, you can copy all dependencies from `platformio.ini` | `SetChargingProfile` | :heavy_check_mark: | | **Remote trigger profile** | | `TriggerMessage` | :heavy_check_mark: | +| **Firmware management** | +| `GetDiagnostics` | | | :heavy_multiplication_x: | +| `DiagnosticsStatusNotification` | | | :heavy_multiplication_x: | +| `FirmwareStatusNotification` | :heavy_check_mark: | +| `UpdateFirmware` | :heavy_check_mark: | ## Next development steps diff --git a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp new file mode 100644 index 00000000..0c6ac0ae --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp @@ -0,0 +1,30 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include + +using ArduinoOcpp::Ocpp16::FirmwareStatusNotification; + +FirmwareStatusNotification::FirmwareStatusNotification() { + status = String("Idle"); // TriggerMessage will use this constructor. Should replace with actual value later +} + +FirmwareStatusNotification::FirmwareStatusNotification(String &status) { + this->status = String(status); +} + +FirmwareStatusNotification::FirmwareStatusNotification(const char *status) { + this->status = status; +} + +DynamicJsonDocument* FirmwareStatusNotification::createReq() { + DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + (status.length() + 1)); + JsonObject payload = doc->to(); + payload["status"] = status; + return doc; +} + +void FirmwareStatusNotification::processConf(JsonObject payload){ + // no payload, nothing to do +} diff --git a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h new file mode 100644 index 00000000..48f87b9a --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h @@ -0,0 +1,34 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include + +#ifndef FIRMWARESTATUSNOTIFICATION_H +#define FIRMWARESTATUSNOTIFICATION_H + +namespace ArduinoOcpp { +namespace Ocpp16 { + +class FirmwareStatusNotification : public OcppMessage { +private: + String status; +public: + FirmwareStatusNotification(); + + FirmwareStatusNotification(String &status); + + FirmwareStatusNotification(const char *status); + + const char* getOcppOperationType() {return "FirmwareStatusNotification"; } + + DynamicJsonDocument* createReq(); + + void processConf(JsonObject payload); + +}; + +} //end namespace Ocpp16 +} //end namespace ArduinoOcpp + +#endif diff --git a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp new file mode 100644 index 00000000..b46aac1b --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp @@ -0,0 +1,40 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include +#include + +using ArduinoOcpp::Ocpp16::UpdateFirmware; + +UpdateFirmware::UpdateFirmware() { + +} + +void UpdateFirmware::processReq(JsonObject payload) { + /* + * Process the application data here. Note: you have to implement the FW update procedure in your client code. You have to set + * a onSendConfListener in which you initiate a FW update (e.g. calling ESPhttpUpdate.update(...) ) + */ + + //check location URL. Maybe introduce Same-Origin-Policy? + if (!payload["location"]) { + formatError = true; + } + + //check the integrity of retrieveDate + const char *retrieveDate = payload["retrieveDate"] | "Invalid"; + + OcppTimestamp parser = OcppTimestamp(); + + if (!parser.setTime(retrieveDate)) { + formatError = true; + } +} + +DynamicJsonDocument* UpdateFirmware::createConf(){ + DynamicJsonDocument* doc = new DynamicJsonDocument(0); + JsonObject payload = doc->to(); + + return doc; +} \ No newline at end of file diff --git a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h new file mode 100644 index 00000000..df9c3750 --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h @@ -0,0 +1,32 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef UPDATEFIRMWARE_H +#define UPDATEFIRMWARE_H + +#include + +namespace ArduinoOcpp { +namespace Ocpp16 { + +class UpdateFirmware : public OcppMessage { +private: + const char *location = "Invalid"; + bool formatError = false; +public: + UpdateFirmware(); + + const char* getOcppOperationType() {return "UpdateFirmware";} + + void processReq(JsonObject payload); + + DynamicJsonDocument* createConf(); + + const char *getErrorCode() {if (formatError) return "FormationViolation"; else return NULL;} +}; + +} //end namespace Ocpp16 +} //end namespace ArduinoOcpp + +#endif diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 90ebeb3f..596dcb9a 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -21,10 +21,8 @@ #include #include #include -#if 0 -#include "UpdateFirmware.h" -#include "FirmwareStatusNotification.h" -#endif +#include +#include #include @@ -218,15 +216,13 @@ OcppOperation *makeOcppOperation(const char *messageType) { if (onResetSendConf == NULL) Serial.print(F("[SimpleOcppOperationFactory] Warning: Reset is without effect when the sendConf listener is not set. Set a listener which resets your device.\n")); operation->setOnSendConfListener(onResetSendConf); -#if 0 } else if (!strcmp(messageType, "UpdateFirmware")) { - msg = new UpdateFirmware(); + msg = new Ocpp16::UpdateFirmware(); if (onUpdateFirmwareReceiveReq == NULL) Serial.print(F("[SimpleOcppOperationFactory] Warning: UpdateFirmware is without effect when the receiveReq listener is not set. Please implement a FW update routine for your device.\n")); operation->setOnReceiveReqListener(onUpdateFirmwareReceiveReq); } else if (!strcmp(messageType, "FirmwareStatusNotification")) { - msg = new FirmwareStatusNotification(); -#endif + msg = new Ocpp16::FirmwareStatusNotification(); } else { Serial.println(F("[SimpleOcppOperationFactory] Operation not supported")); msg = new NotImplemented(); From eb1c43848bfb072000a0e10b21d693c53624e29e Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 24 Jun 2021 14:38:53 +0200 Subject: [PATCH 042/696] allow access to configurations --- src/ArduinoOcpp/Core/Configuration.cpp | 8 ++++++++ src/ArduinoOcpp/Core/Configuration.h | 3 +++ 2 files changed, 11 insertions(+) diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index 798141c0..6f59d9c2 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -70,6 +70,14 @@ void addConfigurationContainer(std::shared_ptr container configurationContainers.push_back(container); } +std::vector>::iterator getConfigurationContainersBegin() { + return configurationContainers.begin(); +} + +std::vector>::iterator getConfigurationContainersEnd() { + return configurationContainers.end(); +} + std::shared_ptr getContainer(const char *filename) { std::vector>::iterator container = std::find_if(configurationContainers.begin(), configurationContainers.end(), [filename](std::shared_ptr &elem) { diff --git a/src/ArduinoOcpp/Core/Configuration.h b/src/ArduinoOcpp/Core/Configuration.h index 50222601..deefbf4a 100644 --- a/src/ArduinoOcpp/Core/Configuration.h +++ b/src/ArduinoOcpp/Core/Configuration.h @@ -17,6 +17,9 @@ template std::shared_ptr> declareConfiguration(const char *key, T defaultValue, const char *filename = CONFIGURATION_FN, bool remotePeerCanWrite = true, bool remotePeerCanRead = true, bool localClientCanWrite = true, bool rebootRequiredWhenChanged = false); void addConfigurationContainer(std::shared_ptr container); +std::vector>::iterator getConfigurationContainersBegin(); +std::vector>::iterator getConfigurationContainersEnd(); + namespace Ocpp16 { From 290fa8288600282d16e5041bd5a8a3f9be7abec1 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 30 Jul 2021 12:11:31 +0200 Subject: [PATCH 043/696] add Unlock Connector operation --- README.md | 2 +- src/ArduinoOcpp.cpp | 9 +++ src/ArduinoOcpp.h | 7 ++- .../MessagesV16/UnlockConnector.cpp | 55 +++++++++++++++++++ src/ArduinoOcpp/MessagesV16/UnlockConnector.h | 33 +++++++++++ .../SimpleOcppOperationFactory.cpp | 3 + .../ChargePointStatus/ConnectorStatus.cpp | 8 +++ .../Tasks/ChargePointStatus/ConnectorStatus.h | 4 ++ 8 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp create mode 100644 src/ArduinoOcpp/MessagesV16/UnlockConnector.h diff --git a/README.md b/README.md index 03b08976..5397fb98 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ In case you use PlatformIO, you can copy all dependencies from `platformio.ini` | `StartTransaction` | :heavy_check_mark: | | `StatusNotification` | :heavy_check_mark: | | `StopTransaction` | :heavy_check_mark: | -| `UnlockConnector` | | | :heavy_multiplication_x: | +| `UnlockConnector` | :heavy_check_mark: | | **Smart charging profile** | | `ClearChargingProfile` | | pushed soon | | `GetCompositeSchedule` | | | :heavy_multiplication_x: | diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 28e96a35..a6e9ffe0 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -241,6 +241,15 @@ void setOnChargingRateLimitChange(std::function chargingRateChanged smartChargingService->setOnLimitChange(onLimitChange); } +void setOnUnlockConnector(std::function unlockConnector) { + ConnectorStatus *connector = getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (connector) { + connector->setOnUnlockConnector(unlockConnector); + } else { + Serial.print(F("[ArduinoOcpp] Error: called setOnUnlockConnector before initializing the library!\n")); + } +} + void setOnSetChargingProfileRequest(OnReceiveReqListener onReceiveReq) { setOnSetChargingProfileRequestListener(onReceiveReq); } diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 9605ddf8..88a7c214 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -54,14 +54,17 @@ void setConnectorEnergizedSampler(std::function connectorEnergized); /* * React on calls by the library's internal functions * - * The library needs to set parameters on your charger on a regular basis. The library calls - * following callbacks regularily (if they were set) to perform updates on your charger. + * The library needs to set parameters on your charger on a regular basis or perform + * functions triggered by the central system. The library uses the following callbacks + * (if they were set) to perform updates or functions on your charger. * * Set the callbacks once in your setup() function. */ void setOnChargingRateLimitChange(std::function chargingRateChanged); +void setOnUnlockConnector(std::function unlockConnector); //true: success, false: failure + /* * React on CS-initiated operations * diff --git a/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp b/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp new file mode 100644 index 00000000..b81f8104 --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp @@ -0,0 +1,55 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include +#include +#include + +#include + +using ArduinoOcpp::Ocpp16::UnlockConnector; + +UnlockConnector::UnlockConnector() { + +} + +const char* UnlockConnector::getOcppOperationType(){ + return "UnlockConnector"; +} + +void UnlockConnector::processReq(JsonObject payload) { + + int connectorId = payload["connectorId"] | -1; + + ConnectorStatus *connector = getConnectorStatus(connectorId); + if (!connector) { + err = true; + return; + } + + std::function unlockConnector = connector->getOnUnlockConnector(); + if (unlockConnector != NULL) { + cbDefined = true; + } else { + cbDefined = false; + return; + } + + cbUnlockSuccessful = unlockConnector(); + + //success +} + +DynamicJsonDocument* UnlockConnector::createConf(){ + DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); + JsonObject payload = doc->to(); + if (err || !cbDefined) { + payload["status"] = "NotSupported"; + } else if (cbUnlockSuccessful) { + payload["status"] = "Unlocked"; + } else { + payload["status"] = "UnlockFailed"; + } + return doc; +} diff --git a/src/ArduinoOcpp/MessagesV16/UnlockConnector.h b/src/ArduinoOcpp/MessagesV16/UnlockConnector.h new file mode 100644 index 00000000..b0c70daf --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/UnlockConnector.h @@ -0,0 +1,33 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef UNLOCKCONNECTOR_H +#define UNLOCKCONNECTOR_H + +#include + +#include + +namespace ArduinoOcpp { +namespace Ocpp16 { + +class UnlockConnector : public OcppMessage { +private: + bool err = false; + bool cbDefined = false; + bool cbUnlockSuccessful = false; +public: + UnlockConnector(); + + const char* getOcppOperationType(); + + void processReq(JsonObject payload); + + DynamicJsonDocument* createConf(); + +}; + +} //end namespace Ocpp16 +} //end namespace ArduinoOcpp +#endif diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 596dcb9a..78d36b04 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -223,6 +224,8 @@ OcppOperation *makeOcppOperation(const char *messageType) { operation->setOnReceiveReqListener(onUpdateFirmwareReceiveReq); } else if (!strcmp(messageType, "FirmwareStatusNotification")) { msg = new Ocpp16::FirmwareStatusNotification(); + } else if (!strcmp(messageType, "UnlockConnector")) { + msg = new Ocpp16::UnlockConnector(); } else { Serial.println(F("[SimpleOcppOperationFactory] Operation not supported")); msg = new NotImplemented(); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 324ac3dc..0350d54a 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -142,3 +142,11 @@ void ConnectorStatus::stopEnergyOffer(){ void ConnectorStatus::saveState() { configuration_save(); } + +void ConnectorStatus::setOnUnlockConnector(std::function unlockConnector) { + this->onUnlockConnector = unlockConnector; +} + +std::function ConnectorStatus::getOnUnlockConnector() { + return this->onUnlockConnector; +} diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index c53b2d34..c6168f95 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -25,6 +25,7 @@ class ConnectorStatus { bool evDrawsEnergy = false; bool evseOffersEnergy = false; OcppEvseState currentStatus = OcppEvseState::NOT_SET; + std::function onUnlockConnector = NULL; public: ConnectorStatus(int connectorId, OcppTime *ocppTime); @@ -50,6 +51,9 @@ class ConnectorStatus { Ocpp16::StatusNotification *loop(); OcppEvseState inferenceStatus(); + + void setOnUnlockConnector(std::function unlockConnector); + std::function getOnUnlockConnector(); }; } //end namespace ArduinoOcpp From e45ca28b8c719a103698127912579fc9591ffa16 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 30 Jul 2021 13:18:27 +0200 Subject: [PATCH 044/696] add StatusNotification statuses --- src/ArduinoOcpp.cpp | 18 +++++++++++++++++ src/ArduinoOcpp.h | 4 ++++ .../ChargePointStatus/ConnectorStatus.cpp | 20 +++++++++++++++++-- .../Tasks/ChargePointStatus/ConnectorStatus.h | 4 ++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index a6e9ffe0..d6edb256 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -236,6 +236,24 @@ void setConnectorEnergizedSampler(std::function connectorEnergized) { connectorEnergizedSampler = connectorEnergized; } +void setConnectorPluggedSampler(std::function connectorPlugged) { + ConnectorStatus *connector = getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (connector) { + connector->setConnectorPluggedSampler(connectorPlugged); + } else { + Serial.print(F("[ArduinoOcpp] Error: called setConnectorPluggedSampler before initializing the library!\n")); + } +} + +void setConnectorFaultedSampler(std::function connectorFaulted) { + ConnectorStatus *connector = getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (connector) { + connector->setConnectorFaultedSampler(connectorFaulted); + } else { + Serial.print(F("[ArduinoOcpp] Error: called setConnectorFaultedSampler before initializing the library!\n")); + } +} + void setOnChargingRateLimitChange(std::function chargingRateChanged) { onLimitChange = chargingRateChanged; smartChargingService->setOnLimitChange(onLimitChange); diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 88a7c214..e4bfea23 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -51,6 +51,10 @@ void setEvRequestsEnergySampler(std::function evRequestsEnergy); void setConnectorEnergizedSampler(std::function connectorEnergized); +void setConnectorPluggedSampler(std::function connectorPlugged); + +void setConnectorFaultedSampler(std::function connectorFailed); + /* * React on calls by the library's internal functions * diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 0350d54a..9ddc8ddd 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -32,9 +32,17 @@ OcppEvseState ConnectorStatus::inferenceStatus() { return OcppEvseState::Available; //no support for Unavailable or Faulted at the moment } - if (!authorized && !getChargePointStatusService()->existsUnboundAuthorization()) { +// if (!authorized && !getChargePointStatusService()->existsUnboundAuthorization()) { +// return OcppEvseState::Available; +// } else if (((int) *transactionId) < 0) { +// return OcppEvseState::Preparing; + if (connectorFaultedSampler != NULL && connectorFaultedSampler()) { + return OcppEvseState::Faulted; + } else if (!authorized && !getChargePointStatusService()->existsUnboundAuthorization() && + ((int) *transactionId) < 0 && + (connectorPluggedSampler == NULL || !connectorPluggedSampler()) ) { return OcppEvseState::Available; - } else if (((int) *transactionId) < 0) { + } else if (((int) *transactionId) <= 0) { return OcppEvseState::Preparing; } else { //Transaction is currently running @@ -139,6 +147,14 @@ void ConnectorStatus::stopEnergyOffer(){ evseOffersEnergy = false; } +void ConnectorStatus::setConnectorPluggedSampler(std::function connectorPlugged) { + this->connectorPluggedSampler = connectorPlugged; +} + +void ConnectorStatus::setConnectorFaultedSampler(std::function connectorFaulted) { + this->connectorFaultedSampler = connectorFaulted; +} + void ConnectorStatus::saveState() { configuration_save(); } diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index c6168f95..006338e9 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -24,6 +24,8 @@ class ConnectorStatus { int transactionIdSync = -1; bool evDrawsEnergy = false; bool evseOffersEnergy = false; + std::function connectorPluggedSampler = NULL; + std::function connectorFaultedSampler = NULL; OcppEvseState currentStatus = OcppEvseState::NOT_SET; std::function onUnlockConnector = NULL; public: @@ -44,6 +46,8 @@ class ConnectorStatus { void stopEvDrawsEnergy(); void startEnergyOffer(); void stopEnergyOffer(); + void setConnectorPluggedSampler(std::function connectorPlugged); + void setConnectorFaultedSampler(std::function connectorFaulted); void saveState(); //void recoverState(); From cb04ab6f16e383386ac71055ff095e595e669115 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 30 Jul 2021 15:06:14 +0200 Subject: [PATCH 045/696] add further fields to BootNotification --- src/ArduinoOcpp.cpp | 18 ++++ src/ArduinoOcpp.h | 3 + .../MessagesV16/BootNotification.cpp | 88 ++++++++++--------- .../MessagesV16/BootNotification.h | 30 ++++--- 4 files changed, 86 insertions(+), 53 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index d6edb256..876e8cd9 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -332,6 +332,24 @@ void bootNotification(String &chargePointModel, String &chargePointVendor, Strin bootNotification->setTimeout(new SuppressedTimeout()); } +void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, Timeout *timeout) { + OcppOperation *bootNotification = makeOcppOperation( + new BootNotification(payload)); + initiateOcppOperation(bootNotification); + if (onConf) + bootNotification->setOnReceiveConfListener(onConf); + if (onAbort) + bootNotification->setOnAbortListener(onAbort); + if (onTimeout) + bootNotification->setOnTimeoutListener(onTimeout); + if (onError) + bootNotification->setOnReceiveErrorListener(onError); + if (timeout) + bootNotification->setTimeout(timeout); + else + bootNotification->setTimeout(new SuppressedTimeout()); +} + void startTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, Timeout *timeout) { OcppOperation *startTransaction = makeOcppOperation( new StartTransaction(OCPP_ID_OF_CONNECTOR)); diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index e4bfea23..b5db8750 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -105,6 +105,9 @@ void authorize(String &idTag, OnReceiveConfListener onConf = NULL, OnAbortListen void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL, Timeout *timeout = NULL); +//The OCPP operation will include the given payload without modifying it. The library will delete the payload object by itself. +void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL, Timeout *timeout = NULL); + void startTransaction(OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL, Timeout *timeout = NULL); void stopTransaction(OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL, Timeout *timeout = NULL); diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp index d09a5e2a..a86173c5 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp @@ -34,54 +34,62 @@ BootNotification::BootNotification(String &cpModel, String &cpVendor, String &cp firmwareVersion = String(fwVersion); } +BootNotification::BootNotification(DynamicJsonDocument *payload) { + this->overridePayload = payload; +} + const char* BootNotification::getOcppOperationType(){ return "BootNotification"; } DynamicJsonDocument* BootNotification::createReq() { - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(4) - + chargePointModel.length() + 1 - + chargePointVendor.length() + 1 - + chargePointSerialNumber.length() + 1 - + firmwareVersion.length() + 1); - JsonObject payload = doc->to(); - payload["chargePointModel"] = chargePointModel; - payload["chargePointVendor"] = chargePointVendor; - if (!chargePointSerialNumber.isEmpty()) { - payload["chargePointSerialNumber"] = chargePointSerialNumber; - } - if (!firmwareVersion.isEmpty()) { - payload["firmwareVersion"] = firmwareVersion; - } - return doc; + if (overridePayload != NULL) { + return overridePayload; + } + + DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + + chargePointModel.length() + 1 + + chargePointVendor.length() + 1 + + chargePointSerialNumber.length() + 1 + + firmwareVersion.length() + 1); + JsonObject payload = doc->to(); + payload["chargePointModel"] = chargePointModel; + payload["chargePointVendor"] = chargePointVendor; + if (!chargePointSerialNumber.isEmpty()) { + payload["chargePointSerialNumber"] = chargePointSerialNumber; + } + if (!firmwareVersion.isEmpty()) { + payload["firmwareVersion"] = firmwareVersion; + } + return doc; } void BootNotification::processConf(JsonObject payload){ - const char* currentTime = payload["currentTime"] | "Invalid"; - if (strcmp(currentTime, "Invalid")) { - OcppTime *ocppTime = getOcppTime(); - if (ocppTime && ocppTime->setOcppTime(currentTime)) { - //success + const char* currentTime = payload["currentTime"] | "Invalid"; + if (strcmp(currentTime, "Invalid")) { + OcppTime *ocppTime = getOcppTime(); + if (ocppTime && ocppTime->setOcppTime(currentTime)) { + //success + } else { + Serial.print(F("[BootNotification] Error reading time string. Expect format like 2020-02-01T20:53:32.486Z\n")); + } } else { - Serial.print(F("[BootNotification] Error reading time string. Expect format like 2020-02-01T20:53:32.486Z\n")); + Serial.print(F("[BootNotification] Error reading time string. Missing attribute currentTime of type string\n")); } - } else { - Serial.print(F("[BootNotification] Error reading time string. Missing attribute currentTime of type string\n")); - } - - //int interval = payload["interval"] | 86400; //not used in this implementation + + //int interval = payload["interval"] | 86400; //not used in this implementation - const char* status = payload["status"] | "Invalid"; + const char* status = payload["status"] | "Invalid"; - if (!strcmp(status, "Accepted")) { - if (DEBUG_OUT) Serial.print(F("[BootNotification] Request has been accepted!\n")); - if (getChargePointStatusService() != NULL) { - getChargePointStatusService()->boot(); + if (!strcmp(status, "Accepted")) { + if (DEBUG_OUT) Serial.print(F("[BootNotification] Request has been accepted!\n")); + if (getChargePointStatusService() != NULL) { + getChargePointStatusService()->boot(); + } + } else { + Serial.print(F("[BootNotification] Request unsuccessful!\n")); } - } else { - Serial.print(F("[BootNotification] Request unsuccessful!\n")); - } } void BootNotification::processReq(JsonObject payload){ @@ -91,10 +99,10 @@ void BootNotification::processReq(JsonObject payload){ } DynamicJsonDocument* BootNotification::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(3) + (JSONDATE_LENGTH + 1)); - JsonObject payload = doc->to(); - payload["currentTime"] = "2019-11-01T11:59:55.123Z"; - payload["interval"] = 86400; //heartbeat send interval - not relevant for JSON variant of OCPP so send dummy value that likely won't break - payload["status"] = "Accepted"; - return doc; + DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(3) + (JSONDATE_LENGTH + 1)); + JsonObject payload = doc->to(); + payload["currentTime"] = "2019-11-01T11:59:55.123Z"; + payload["interval"] = 86400; //heartbeat send interval - not relevant for JSON variant of OCPP so send dummy value that likely won't break + payload["status"] = "Accepted"; + return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.h b/src/ArduinoOcpp/MessagesV16/BootNotification.h index 29aec34e..4a443853 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.h +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.h @@ -13,28 +13,32 @@ namespace Ocpp16 { class BootNotification : public OcppMessage { private: - String chargePointModel = String('\0'); - String chargePointVendor = String('\0'); - String chargePointSerialNumber = String('\0'); - String firmwareVersion = String('\0'); + String chargePointModel = String('\0'); + String chargePointVendor = String('\0'); + String chargePointSerialNumber = String('\0'); + String firmwareVersion = String('\0'); + + DynamicJsonDocument *overridePayload = NULL; public: - BootNotification(); + BootNotification(); + + BootNotification(String &chargePointModel, String &chargePointVendor); - BootNotification(String &chargePointModel, String &chargePointVendor); + BootNotification(String &chargePointModel, String &chargePointVendor, String &chargePointSerialNumber); - BootNotification(String &chargePointModel, String &chargePointVendor, String &chargePointSerialNumber); + BootNotification(String &chargePointModel, String &chargePointVendor, String &chargePointSerialNumber, String &firmwareVersion); - BootNotification(String &chargePointModel, String &chargePointVendor, String &chargePointSerialNumber, String &firmwareVersion); + BootNotification(DynamicJsonDocument *payload); - const char* getOcppOperationType(); + const char* getOcppOperationType(); - DynamicJsonDocument* createReq(); + DynamicJsonDocument* createReq(); - void processConf(JsonObject payload); + void processConf(JsonObject payload); - void processReq(JsonObject payload); + void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + DynamicJsonDocument* createConf(); }; } //end namespace Ocpp16 From 735f11f12b129503291d13e11d3b58ef75fbb139 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 2 Aug 2021 13:43:41 +0200 Subject: [PATCH 046/696] extend error status mechanism --- src/ArduinoOcpp.cpp | 15 +++++++++--- src/ArduinoOcpp.h | 4 +++- .../MessagesV16/StatusNotification.cpp | 10 +++++--- .../MessagesV16/StatusNotification.h | 3 ++- .../ChargePointStatus/ConnectorStatus.cpp | 23 +++++++++++++++---- .../Tasks/ChargePointStatus/ConnectorStatus.h | 7 ++++-- 6 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 876e8cd9..48fc358f 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -245,12 +245,21 @@ void setConnectorPluggedSampler(std::function connectorPlugged) { } } -void setConnectorFaultedSampler(std::function connectorFaulted) { +//void setConnectorFaultedSampler(std::function connectorFaulted) { +// ConnectorStatus *connector = getConnectorStatus(OCPP_ID_OF_CONNECTOR); +// if (connector) { +// connector->setConnectorFaultedSampler(connectorFaulted); +// } else { +// Serial.print(F("[ArduinoOcpp] Error: called setConnectorFaultedSampler before initializing the library!\n")); +// } +//} + +void addConnectorErrorCodeSampler(std::function connectorErrorCode) { ConnectorStatus *connector = getConnectorStatus(OCPP_ID_OF_CONNECTOR); if (connector) { - connector->setConnectorFaultedSampler(connectorFaulted); + connector->addConnectorErrorCodeSampler(connectorErrorCode); } else { - Serial.print(F("[ArduinoOcpp] Error: called setConnectorFaultedSampler before initializing the library!\n")); + Serial.print(F("[ArduinoOcpp] Error: called addConnectorErrorCodeSampler before initializing the library!\n")); } } diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index b5db8750..46500d11 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -53,7 +53,9 @@ void setConnectorEnergizedSampler(std::function connectorEnergized); void setConnectorPluggedSampler(std::function connectorPlugged); -void setConnectorFaultedSampler(std::function connectorFailed); +//void setConnectorFaultedSampler(std::function connectorFailed); + +void addConnectorErrorCodeSampler(std::function connectorErrorCode); /* * React on calls by the library's internal functions diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp index 06d72603..72c01ec3 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp @@ -10,8 +10,8 @@ using ArduinoOcpp::Ocpp16::StatusNotification; -StatusNotification::StatusNotification(int connectorId, OcppEvseState currentStatus, const OcppTimestamp &otimestamp) - : connectorId(connectorId), currentStatus(currentStatus), otimestamp(otimestamp) { +StatusNotification::StatusNotification(int connectorId, OcppEvseState currentStatus, const OcppTimestamp &otimestamp, const char *errorCode) + : connectorId(connectorId), currentStatus(currentStatus), otimestamp(otimestamp), errorCode(errorCode) { if (DEBUG_OUT) { Serial.print(F("[StatusNotification] New StatusNotification. New Status: ")); @@ -64,7 +64,11 @@ DynamicJsonDocument* StatusNotification::createReq() { JsonObject payload = doc->to(); payload["connectorId"] = connectorId; - payload["errorCode"] = "NoError"; //No error diagnostics support + if (errorCode != NULL) { + payload["errorCode"] = errorCode; + } else { + payload["errorCode"] = "NoError"; + } switch (currentStatus) { case (OcppEvseState::Available): diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.h b/src/ArduinoOcpp/MessagesV16/StatusNotification.h index 629c9d4b..cd832aff 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.h +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.h @@ -17,8 +17,9 @@ class StatusNotification : public OcppMessage { int connectorId = 1; OcppEvseState currentStatus = OcppEvseState::NOT_SET; OcppTimestamp otimestamp; + const char *errorCode = NULL; //NULL is equivalent to "NoError" public: - StatusNotification(int connectorId, OcppEvseState currentStatus, const OcppTimestamp &otimestamp); + StatusNotification(int connectorId, OcppEvseState currentStatus, const OcppTimestamp &otimestamp, const char *errorCode = NULL); StatusNotification(); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 9ddc8ddd..b8f3046b 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -36,7 +36,8 @@ OcppEvseState ConnectorStatus::inferenceStatus() { // return OcppEvseState::Available; // } else if (((int) *transactionId) < 0) { // return OcppEvseState::Preparing; - if (connectorFaultedSampler != NULL && connectorFaultedSampler()) { + //if (connectorFaultedSampler != NULL && connectorFaultedSampler()) { + if (getErrorCode() != NULL) { return OcppEvseState::Faulted; } else if (!authorized && !getChargePointStatusService()->existsUnboundAuthorization() && ((int) *transactionId) < 0 && @@ -66,12 +67,22 @@ StatusNotification *ConnectorStatus::loop() { //fire StatusNotification //TODO check for online condition: Only inform CS about status change if CP is online //TODO check for too short duration condition: Only inform CS about status change if it lasted for longer than MinimumStatusDuration - return new StatusNotification(connectorId, currentStatus, ocppTime->getOcppTimestampNow()); + return new StatusNotification(connectorId, currentStatus, ocppTime->getOcppTimestampNow(), getErrorCode()); } return NULL; } +const char *ConnectorStatus::getErrorCode() { + for (auto s = connectorErrorCodeSamplers.begin(); s != connectorErrorCodeSamplers.end(); s++) { + const char *err = s->operator()(); + if (err != NULL) { + return err; + } + } + return NULL; +} + void ConnectorStatus::authorize(String &idTag){ this->idTag = String(idTag); authorize(); @@ -151,8 +162,12 @@ void ConnectorStatus::setConnectorPluggedSampler(std::function connector this->connectorPluggedSampler = connectorPlugged; } -void ConnectorStatus::setConnectorFaultedSampler(std::function connectorFaulted) { - this->connectorFaultedSampler = connectorFaulted; +//void ConnectorStatus::setConnectorFaultedSampler(std::function connectorFaulted) { +// this->connectorFaultedSampler = connectorFaulted; +//} + +void ConnectorStatus::addConnectorErrorCodeSampler(std::function connectorErrorCode) { + this->connectorErrorCodeSamplers.push_back(connectorErrorCode); } void ConnectorStatus::saveState() { diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index 006338e9..04ef0db8 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -25,7 +25,9 @@ class ConnectorStatus { bool evDrawsEnergy = false; bool evseOffersEnergy = false; std::function connectorPluggedSampler = NULL; - std::function connectorFaultedSampler = NULL; + //std::function connectorFaultedSampler = NULL; + std::vector> connectorErrorCodeSamplers; + const char *getErrorCode(); OcppEvseState currentStatus = OcppEvseState::NOT_SET; std::function onUnlockConnector = NULL; public: @@ -47,7 +49,8 @@ class ConnectorStatus { void startEnergyOffer(); void stopEnergyOffer(); void setConnectorPluggedSampler(std::function connectorPlugged); - void setConnectorFaultedSampler(std::function connectorFaulted); + //void setConnectorFaultedSampler(std::function connectorFaulted); + void addConnectorErrorCodeSampler(std::function connectorErrorCode); void saveState(); //void recoverState(); From b643a5be769ecbc1a3af239be8e776bf301a2db9 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 2 Aug 2021 16:00:59 +0200 Subject: [PATCH 047/696] add OCPP Heartbeat pinging --- src/ArduinoOcpp.cpp | 5 ++++ .../Tasks/Heartbeat/HeartbeatService.cpp | 28 +++++++++++++++++++ .../Tasks/Heartbeat/HeartbeatService.h | 27 ++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp create mode 100644 src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 48fc358f..28268d7c 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,7 @@ std::function connectorEnergizedSampler = NULL; bool connectorEnergizedLastState = false; SmartChargingService *smartChargingService; ChargePointStatusService *chargePointStatusService; +HeartbeatService *heartbeatService; OnLimitChange onLimitChange; OcppTime *ocppTime; @@ -152,6 +154,7 @@ void OCPP_initialize(OcppSocket *ocppSocket, float V_eff, ArduinoOcpp::Filesyste smartChargingService = new SmartChargingService(11000.0f, V_eff, OCPP_NUMCONNECTORS, ocppTime, fsOpt); //default charging limit: 11kW chargePointStatusService = new ChargePointStatusService(OCPP_NUMCONNECTORS, ocppTime); //Constructor adds instance to ocppEngine in constructor meteringService = new MeteringService(OCPP_NUMCONNECTORS, ocppTime); + heartbeatService = new HeartbeatService(); OCPP_initialized = true; } @@ -184,6 +187,8 @@ void OCPP_loop() { meteringService->loop(); //optional } + heartbeatService->loop(); + bool evRequestsEnergyNewState = true; if (evRequestsEnergySampler != NULL) { evRequestsEnergyNewState = evRequestsEnergySampler(); diff --git a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp new file mode 100644 index 00000000..22f16807 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp @@ -0,0 +1,28 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include +#include +#include +#include + +using namespace ArduinoOcpp; + +HeartbeatService::HeartbeatService() { + heartbeatInterval = declareConfiguration("HeartbeatInterval", 86400); + lastHeartbeat = millis(); +} + +void HeartbeatService::loop() { + ulong hbInterval = *heartbeatInterval; + hbInterval *= 1000UL; //conversion s -> ms + ulong now = millis(); + + if (now - lastHeartbeat >= hbInterval) { + lastHeartbeat = now; + + OcppOperation *heartbeat = makeOcppOperation("Heartbeat"); + initiateOcppOperation(heartbeat); + } +} diff --git a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h new file mode 100644 index 00000000..5e85b157 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h @@ -0,0 +1,27 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef HEARTBEATSERVICE_H +#define HEARTBEATSERVICE_H + +#include +#include + +namespace ArduinoOcpp { + +class HeartbeatService { +private: + + ulong lastHeartbeat; + std::shared_ptr> heartbeatInterval; + +public: + HeartbeatService(); + + void loop(); +}; + +} + +#endif From 74127dc25ae893b7bf884dfe43541ea1fc426923 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 2 Aug 2021 16:35:06 +0200 Subject: [PATCH 048/696] add ClearChargingProfile --- README.md | 2 +- .../MessagesV16/ClearChargingProfile.cpp | 83 +++++++++++++++++++ .../MessagesV16/ClearChargingProfile.h | 28 +++++++ .../SimpleOcppOperationFactory.cpp | 3 + .../SmartCharging/SmartChargingModel.cpp | 4 + .../Tasks/SmartCharging/SmartChargingModel.h | 2 + .../SmartCharging/SmartChargingService.cpp | 55 ++++++++++++ .../SmartCharging/SmartChargingService.h | 1 + 8 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp create mode 100644 src/ArduinoOcpp/MessagesV16/ClearChargingProfile.h diff --git a/README.md b/README.md index 5397fb98..3ed9a520 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ In case you use PlatformIO, you can copy all dependencies from `platformio.ini` | `StopTransaction` | :heavy_check_mark: | | `UnlockConnector` | :heavy_check_mark: | | **Smart charging profile** | -| `ClearChargingProfile` | | pushed soon | +| `ClearChargingProfile` | :heavy_check_mark: | | `GetCompositeSchedule` | | | :heavy_multiplication_x: | | `SetChargingProfile` | :heavy_check_mark: | | **Remote trigger profile** | diff --git a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp new file mode 100644 index 00000000..f77cc979 --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp @@ -0,0 +1,83 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include +#include +#include + +#include + +using ArduinoOcpp::Ocpp16::ClearChargingProfile; + +ClearChargingProfile::ClearChargingProfile() { + +} + +const char* ClearChargingProfile::getOcppOperationType(){ + return "ClearChargingProfile"; +} + +void ClearChargingProfile::processReq(JsonObject payload) { + + std::function filter = [payload = payload] + (int chargingProfileId, int connectorId, ChargingProfilePurposeType chargingProfilePurpose, int stackLevel) { + + if (payload.containsKey("id")) { + if (chargingProfileId != payload["id"] | -1) { + return false; + } + } + + if (payload.containsKey("connectorId")) { + Serial.print(F("[ClearChargingProfile] Smart Charging does not implement multiple connectors yet. Ignore connectorId\n")); + } + + if (payload.containsKey("chargingProfilePurpose")) { + switch (chargingProfilePurpose) { + case ChargingProfilePurposeType::ChargePointMaxProfile: + if (strcmp(payload["chargingProfilePurpose"] | "INVALID", "ChargePointMaxProfile")) { + return false; + } + break; + case ChargingProfilePurposeType::TxDefaultProfile: + if (strcmp(payload["chargingProfilePurpose"] | "INVALID", "TxDefaultProfile")) { + return false; + } + break; + case ChargingProfilePurposeType::TxProfile: + if (strcmp(payload["chargingProfilePurpose"] | "INVALID", "TxProfile")) { + return false; + } + break; + } + } + + if (payload.containsKey("stackLevel")) { + if (stackLevel != payload["stackLevel"] | -1) { + return false; + } + } + + return true; + }; + + SmartChargingService *smartChargingService = getSmartChargingService(); + if (!smartChargingService) { + Serial.println(F("[ClearChargingProfile] SmartChargingService not initialized! Ignore request")); + return; + } + + matchingProfilesFound = smartChargingService->clearChargingProfile(filter); +} + +DynamicJsonDocument* ClearChargingProfile::createConf(){ + DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); + JsonObject payload = doc->to(); + if (matchingProfilesFound) + payload["status"] = "Accepted"; + else + payload["status"] = "Unknown"; + + return doc; +} diff --git a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.h b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.h new file mode 100644 index 00000000..25c503f0 --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.h @@ -0,0 +1,28 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef CLEARCHARGINGPROFILE_H +#define CLEARCHARGINGPROFILE_H + +#include + +namespace ArduinoOcpp { +namespace Ocpp16 { + +class ClearChargingProfile : public OcppMessage { +private: + bool matchingProfilesFound = false; +public: + ClearChargingProfile(); + + const char* getOcppOperationType(); + + void processReq(JsonObject payload); + + DynamicJsonDocument* createConf(); +}; + +} //end namespace Ocpp16 +} //end namespace ArduinoOcpp +#endif diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 78d36b04..5735bb3a 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -226,6 +227,8 @@ OcppOperation *makeOcppOperation(const char *messageType) { msg = new Ocpp16::FirmwareStatusNotification(); } else if (!strcmp(messageType, "UnlockConnector")) { msg = new Ocpp16::UnlockConnector(); + } else if (!strcmp(messageType, "ClearChargingProfile")) { + msg = new Ocpp16::ClearChargingProfile(); } else { Serial.println(F("[SimpleOcppOperationFactory] Operation not supported")); msg = new NotImplemented(); diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp index cc208945..d085af1d 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp @@ -328,6 +328,10 @@ ChargingProfilePurposeType ChargingProfile::getChargingProfilePurpose(){ return chargingProfilePurpose; } +int ChargingProfile::getChargingProfileId() { + return chargingProfileId; +} + void ChargingProfile::printProfile(){ Serial.print(F(" chargingProfileId: ")); Serial.print(chargingProfileId); diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h index 3bad2a2e..d47ecfb8 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h @@ -113,6 +113,8 @@ class ChargingProfile { ChargingProfilePurposeType getChargingProfilePurpose(); + int getChargingProfileId(); + /* * print on console */ diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index a86cdf22..baa2c040 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -290,6 +290,61 @@ ChargingProfile *SmartChargingService::updateProfileStack(JsonObject *json){ return chargingProfile; } +bool SmartChargingService::clearChargingProfile(std::function filter) { + int nMatches = 0; + + ChargingProfile **profileStacks [] = {ChargePointMaxProfile, TxDefaultProfile, TxProfile}; + + for (ChargingProfile **profileStack : profileStacks) { + for (int iLevel = 0; iLevel < CHARGEPROFILEMAXSTACKLEVEL; iLevel++) { + ChargingProfile *chargingProfile = profileStack[iLevel]; + if (chargingProfile == NULL) + continue; + + // -1: multiple connectors are not supported yet for Smart Charging + bool tbCleared = filter(chargingProfile->getChargingProfileId(), -1, chargingProfile->getChargingProfilePurpose(), iLevel); + + if (tbCleared) { + nMatches++; + +#ifndef AO_DEACTIVATE_FLASH + if (filesystemOpt.accessAllowed()) { + String profileFN = PROFILE_FN_PREFIX; + + switch (chargingProfile->getChargingProfilePurpose()) { + case (ChargingProfilePurposeType::ChargePointMaxProfile): + profileFN += "CpMaxProfile-"; + break; + case (ChargingProfilePurposeType::TxDefaultProfile): + profileFN += "TxDefProfile-"; + break; + case (ChargingProfilePurposeType::TxProfile): + profileFN += "TxProfile-"; + break; + } + + profileFN += chargingProfile->getStackLevel(); + profileFN += PROFILE_FN_SUFFIX; + + USE_FS.remove(profileFN); + } else { + if (DEBUG_OUT) Serial.println(F("[SmartChargingService] Prohibit access to FS")); + } +#endif + delete chargingProfile; + } + } + } + + /** + * Invalidate the last limit inference by setting the nextChange to now. By the next loop()-call, the limit + * and nextChange will be recalculated and onLimitChanged will be called. + */ + nextChange = ocppTime->getOcppTimestampNow(); + + return nMatches > 0; +} + bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile *chargingProfile) { #ifndef AO_DEACTIVATE_FLASH diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h index 5a246719..6ee0e2d0 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h @@ -46,6 +46,7 @@ class SmartChargingService { //void beginChargingNow(); //void endChargingNow(); void updateChargingProfile(JsonObject *json); + bool clearChargingProfile(std::function filter); void inferenceLimit(const OcppTimestamp &t, float *limit, OcppTimestamp *validTo); float inferenceLimitNow(); void setOnLimitChange(OnLimitChange onLimitChange); From 5479ef10535d54092c537f1159143de9eb58619a Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 5 Aug 2021 11:15:39 +0200 Subject: [PATCH 049/696] fixed allocation and warning --- src/ArduinoOcpp/MessagesV16/BootNotification.cpp | 9 ++++++++- src/ArduinoOcpp/MessagesV16/BootNotification.h | 2 ++ src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp | 4 ++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp index a86173c5..2d6390ff 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp @@ -38,6 +38,11 @@ BootNotification::BootNotification(DynamicJsonDocument *payload) { this->overridePayload = payload; } +BootNotification::~BootNotification() { + if (overridePayload != NULL) + delete overridePayload; +} + const char* BootNotification::getOcppOperationType(){ return "BootNotification"; } @@ -45,7 +50,9 @@ const char* BootNotification::getOcppOperationType(){ DynamicJsonDocument* BootNotification::createReq() { if (overridePayload != NULL) { - return overridePayload; + DynamicJsonDocument *result = new DynamicJsonDocument(overridePayload->capacity()); + result->set(overridePayload); + return result; } DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(4) diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.h b/src/ArduinoOcpp/MessagesV16/BootNotification.h index 4a443853..185afb7b 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.h +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.h @@ -22,6 +22,8 @@ class BootNotification : public OcppMessage { public: BootNotification(); + ~BootNotification(); + BootNotification(String &chargePointModel, String &chargePointVendor); BootNotification(String &chargePointModel, String &chargePointVendor, String &chargePointSerialNumber); diff --git a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp index f77cc979..7108d44f 100644 --- a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp +++ b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp @@ -24,7 +24,7 @@ void ClearChargingProfile::processReq(JsonObject payload) { (int chargingProfileId, int connectorId, ChargingProfilePurposeType chargingProfilePurpose, int stackLevel) { if (payload.containsKey("id")) { - if (chargingProfileId != payload["id"] | -1) { + if (chargingProfileId != (payload["id"] | -1)) { return false; } } @@ -54,7 +54,7 @@ void ClearChargingProfile::processReq(JsonObject payload) { } if (payload.containsKey("stackLevel")) { - if (stackLevel != payload["stackLevel"] | -1) { + if (stackLevel != (payload["stackLevel"] | -1)) { return false; } } From 1af840be2965d030b429b3df80b3f72b8e7de652 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 5 Aug 2021 14:13:45 +0200 Subject: [PATCH 050/696] fix argument type --- src/ArduinoOcpp/MessagesV16/BootNotification.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp index 2d6390ff..ac35dd07 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp @@ -51,7 +51,8 @@ DynamicJsonDocument* BootNotification::createReq() { if (overridePayload != NULL) { DynamicJsonDocument *result = new DynamicJsonDocument(overridePayload->capacity()); - result->set(overridePayload); + JsonObject payloadCast = result->as(); + result->set(payloadCast); return result; } From db2ca33e3fce0f25abed8e0a38b482eb480ee1df Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 5 Aug 2021 16:11:23 +0200 Subject: [PATCH 051/696] add Reset listener --- src/ArduinoOcpp.cpp | 4 ++++ src/ArduinoOcpp.h | 1 + src/ArduinoOcpp/SimpleOcppOperationFactory.cpp | 10 ++++++++-- src/ArduinoOcpp/SimpleOcppOperationFactory.h | 1 + 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 28268d7c..fd255538 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -302,6 +302,10 @@ void setOnResetSendConf(OnSendConfListener onSendConf) { setOnResetSendConfListener(onSendConf); } +void setOnResetReceiveReq(OnReceiveReqListener onReceiveReq) { + setOnResetReceiveRequestListener(onReceiveReq); +} + void authorize(String &idTag, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, Timeout *timeout) { OcppOperation *authorize = makeOcppOperation( new Authorize(idTag)); diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 46500d11..fd7b9fca 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -89,6 +89,7 @@ void setOnRemoteStopTransactionSendConf(OnSendConfListener onSendConf); //import void setOnRemoteStopTransactionReceiveReq(OnReceiveReqListener onReceiveReq); //optional, to de-energize the power plug immediately void setOnResetSendConf(OnSendConfListener onSendConf); //important, reset your device here (i.e. call ESP.reset();) +void setOnResetReceiveReq(OnReceiveReqListener onReceiveReq); //alternative: start reset timer here /* * Perform CP-initiated operations diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 5735bb3a..f15e238a 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -102,6 +102,11 @@ void setOnGetConfigurationSendConfListener(OnSendConfListener listener){ onGetConfigurationSendConf = listener; } +OnSendConfListener onResetReceiveReq; +void setOnResetReceiveRequestListener(OnReceiveReqListener listener) { + onResetReceiveReq = listener; +} + OnSendConfListener onResetSendConf; void setOnResetSendConfListener(OnSendConfListener listener){ onResetSendConf = listener; @@ -215,8 +220,9 @@ OcppOperation *makeOcppOperation(const char *messageType) { operation->setOnSendConfListener(onGetConfigurationSendConf); } else if (!strcmp(messageType, "Reset")) { msg = new Ocpp16::Reset(); - if (onResetSendConf == NULL) - Serial.print(F("[SimpleOcppOperationFactory] Warning: Reset is without effect when the sendConf listener is not set. Set a listener which resets your device.\n")); + if (onResetSendConf == NULL && onResetReceiveReq == NULL) + Serial.print(F("[SimpleOcppOperationFactory] Warning: Reset is without effect when the sendConf and receiveReq listener is not set. Set a listener which resets your device.\n")); + operation->setOnReceiveReqListener(onResetReceiveReq); operation->setOnSendConfListener(onResetSendConf); } else if (!strcmp(messageType, "UpdateFirmware")) { msg = new Ocpp16::UpdateFirmware(); diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.h b/src/ArduinoOcpp/SimpleOcppOperationFactory.h index 4aa2aabd..532c13a9 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.h +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.h @@ -39,6 +39,7 @@ void setOnChangeConfigurationReceiveRequestListener(OnReceiveReqListener onRecei void setOnChangeConfigurationSendConfListener(OnSendConfListener onSendConf); void setOnGetConfigurationReceiveRequestListener(OnReceiveReqListener onReceiveReq); void setOnGetConfigurationSendConfListener(OnSendConfListener onSendConf); +void setOnResetReceiveRequestListener(OnReceiveReqListener onReceiveReq); void setOnResetSendConfListener(OnSendConfListener onSendConf); void setOnUpdateFirmwareReceiveRequestListener(OnReceiveReqListener onReceiveReq); From 35fb1b634a4cd46b660dbe673398e78618973383 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 12 Aug 2021 10:43:48 +0200 Subject: [PATCH 052/696] use copy constructor --- src/ArduinoOcpp/MessagesV16/BootNotification.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp index ac35dd07..7384daec 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp @@ -50,9 +50,7 @@ const char* BootNotification::getOcppOperationType(){ DynamicJsonDocument* BootNotification::createReq() { if (overridePayload != NULL) { - DynamicJsonDocument *result = new DynamicJsonDocument(overridePayload->capacity()); - JsonObject payloadCast = result->as(); - result->set(payloadCast); + DynamicJsonDocument *result = new DynamicJsonDocument(*overridePayload); return result; } From ea0be6af01a4711f829da20f2bf55b5ea5fd5078 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 24 Aug 2021 17:57:07 +0200 Subject: [PATCH 053/696] Firmware update initial commit --- src/ArduinoOcpp/Core/OcppEngine.cpp | 19 +- src/ArduinoOcpp/Core/OcppEngine.h | 5 + .../FirmwareStatusNotification.cpp | 44 ++- .../MessagesV16/FirmwareStatusNotification.h | 17 +- .../FirmwareManagement/FirmwareService.cpp | 290 ++++++++++++++++++ .../FirmwareManagement/FirmwareService.h | 94 ++++++ 6 files changed, 455 insertions(+), 14 deletions(-) create mode 100644 src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp create mode 100644 src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h diff --git a/src/ArduinoOcpp/Core/OcppEngine.cpp b/src/ArduinoOcpp/Core/OcppEngine.cpp index 7214a1b7..3add98f6 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.cpp +++ b/src/ArduinoOcpp/Core/OcppEngine.cpp @@ -16,9 +16,10 @@ namespace ArduinoOcpp { namespace OcppEngine { OcppSocket *ocppSocket; -SmartChargingService *ocppEngine_smartChargingService; -ChargePointStatusService *ocppEngine_chargePointStatusService; -MeteringService *ocppEngine_meteringService; +SmartChargingService *ocppEngine_smartChargingService = NULL; +ChargePointStatusService *ocppEngine_chargePointStatusService = NULL; +MeteringService *ocppEngine_meteringService = NULL; +FirmwareService *ocppEngine_firmwareService = NULL; OcppTime *ocppEngine_ocppTime; std::vector initiatedOcppOperations; @@ -441,6 +442,18 @@ MeteringService* getMeteringService() { return ocppEngine_meteringService; } +void setFirmwareService(FirmwareService *fwService) { + ocppEngine_firmwareService = fwService; +} + +FirmwareService *getFirmwareService() { + if (ocppEngine_firmwareService == NULL) { + Serial.print(F("[OcppEngine] Error: in OcppEngine, there is no ocppEngine_firmwareService set, but it is accessed!\n")); + //no error catch + } + return ocppEngine_firmwareService; +} + void ocppEngine_setOcppTime(OcppTime *ocppTime) { ocppEngine_ocppTime = ocppTime; } diff --git a/src/ArduinoOcpp/Core/OcppEngine.h b/src/ArduinoOcpp/Core/OcppEngine.h index e6f6aff9..0c1d75f9 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.h +++ b/src/ArduinoOcpp/Core/OcppEngine.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace ArduinoOcpp { @@ -53,6 +54,10 @@ void setMeteringSerivce(MeteringService *meteringService); MeteringService* getMeteringService(); +void setFirmwareService(FirmwareService *firmwareService); + +FirmwareService *getFirmwareService(); + void ocppEngine_setOcppTime(OcppTime *ocppTime); OcppTime *getOcppTime(); diff --git a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp index 0c6ac0ae..133d3218 100644 --- a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp @@ -3,25 +3,55 @@ // MIT License #include +#include +#include using ArduinoOcpp::Ocpp16::FirmwareStatusNotification; FirmwareStatusNotification::FirmwareStatusNotification() { - status = String("Idle"); // TriggerMessage will use this constructor. Should replace with actual value later + //status = String("Idle"); // TriggerMessage will use this constructor. Should replace with actual value later + FirmwareService *fwService = getFirmwareService(); + if (fwService) { + status = fwService->getFirmwareStatus(); + } else { + status = FirmwareStatus::Idle; + } } -FirmwareStatusNotification::FirmwareStatusNotification(String &status) { - this->status = String(status); +FirmwareStatusNotification::FirmwareStatusNotification(FirmwareStatus status) { } -FirmwareStatusNotification::FirmwareStatusNotification(const char *status) { - this->status = status; +const char *FirmwareStatusNotification::cstrFromFwStatus(FirmwareStatus status) { + switch (status) { + case (FirmwareStatus::Downloaded): + return "Downloaded"; + break; + case (FirmwareStatus::DownloadFailed): + return "DownloadFailed"; + break; + case (FirmwareStatus::Downloading): + return "Downloading"; + break; + case (FirmwareStatus::Idle): + return "Idle"; + break; + case (FirmwareStatus::InstallationFailed): + return "InstallationFailed"; + break; + case (FirmwareStatus::Installing): + return "Installing"; + break; + case (FirmwareStatus::Installed): + return "Installed"; + break; + } + return NULL; //cannot be reached } DynamicJsonDocument* FirmwareStatusNotification::createReq() { - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + (status.length() + 1)); + DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); JsonObject payload = doc->to(); - payload["status"] = status; + payload["status"] = cstrFromFwStatus(status); return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h index 48f87b9a..68101cdc 100644 --- a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h +++ b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h @@ -10,15 +10,24 @@ namespace ArduinoOcpp { namespace Ocpp16 { +enum class FirmwareStatus { + Downloaded, + DownloadFailed, + Downloading, + Idle, + InstallationFailed, + Installing, + Installed +}; + class FirmwareStatusNotification : public OcppMessage { private: - String status; + FirmwareStatus status; + static const char *cstrFromFwStatus(FirmwareStatus status); public: FirmwareStatusNotification(); - FirmwareStatusNotification(String &status); - - FirmwareStatusNotification(const char *status); + FirmwareStatusNotification(FirmwareStatus status); const char* getOcppOperationType() {return "FirmwareStatusNotification"; } diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp new file mode 100644 index 00000000..87676997 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -0,0 +1,290 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include +#include +#include +#include +#include +#include + +using namespace ArduinoOcpp; +using ArduinoOcpp::Ocpp16::FirmwareStatus; + +FirmwareService::FirmwareService(const char *buildNumber) : buildNumber(buildNumber) { + previousBuildNumber = declareConfiguration("BUILD_NUMBER", "", CONFIGURATION_FN, false, false, true, false); +} + +void FirmwareService::loop() { + OcppOperation *notification = getFirmwareStatusNotification(); + if (notification) { + initiateOcppOperation(notification); + } + + if (millis() - timestampTransition < delayTransition) { + return; + } + + FirmwareStatus status = getFirmwareStatus(); + OcppTimestamp timestampNow = getOcppTime()->getOcppTimestampNow(); + if (retries > 0 && timestampNow >= retreiveDate) { + + if (!downloadIssued) { + downloadIssued = true; + if (onDownload != NULL) { + onDownload(location); + timestampTransition = millis(); + delayTransition = 30000; + } + return; + } + + const int DOWNLOAD_TIMEOUT = 120; + if ((downloadIssued && timestampNow - retreiveDate >= DOWNLOAD_TIMEOUT) + || (downloadStatusSampler != NULL && downloadStatusSampler() == DownloadStatus::DownloadFailed)) { + + Serial.println(F("[FirmwareService] Download timeout or failed! Retry")); + if (retryInterval < DOWNLOAD_TIMEOUT) + retreiveDate = timestampNow; + else + retreiveDate += retryInterval; + retries--; + downloadIssued = false; + installationIssued = false; + + timestampTransition = millis(); + delayTransition = 10000; + return; + } + + if (downloadStatusSampler != NULL && downloadStatusSampler() == DownloadStatus::NotDownloaded) { + return; + } + + if (!installationIssued) { + bool ongoingTx = false; + ChargePointStatusService *cpStatus = getChargePointStatusService(); + if (cpStatus) { + for (int i = 0; i < cpStatus->getNumConnectors(); i++) { + ConnectorStatus *connector = cpStatus->getConnector(i); + if (connector && connector->getTransactionId() >= 0) { + ongoingTx = true; + break; + } + } + } + + if (!ongoingTx) { + installationIssued = true; + + if (onInstall != NULL) { + onInstall(location); + } else { + Serial.println(F("[FirmwareService] onInstall must be set! (see setOnInstall). Will abort")); + } + + timestampTransition = millis(); + delayTransition = 20000; + } + + return; + } + + const int INSTALLATION_TIMEOUT = 120; + if ((installationIssued && timestampNow - retreiveDate >= INSTALLATION_TIMEOUT + DOWNLOAD_TIMEOUT) + || (installationStatusSampler != NULL && installationStatusSampler() == InstallationStatus::InstallationFailed)) { + + Serial.println(F("[FirmwareService] Installation timeout or failed! Retry")); + if (retryInterval < INSTALLATION_TIMEOUT + DOWNLOAD_TIMEOUT) + retreiveDate = timestampNow; + else + retreiveDate += retryInterval; + retries--; + downloadIssued = false; + installationIssued = false; + + timestampTransition = millis(); + delayTransition = 10000; + return; + } + + if (installationStatusSampler != NULL && installationStatusSampler() == InstallationStatus::NotInstalled) { + return; + } + + } +} + +void FirmwareService::scheduleFirmwareUpdate(String &location, OcppTimestamp retreiveDate, int retries, ulong retryInterval) { + this->location = String(location); + this->retreiveDate = retreiveDate; + this->retries = retries; + this->retryInterval = retryInterval; + + downloadIssued = false; + installationIssued = false; +} + +FirmwareStatus FirmwareService::getFirmwareStatus() { + if (installationIssued) { + if (installationStatusSampler != NULL) { + if (installationStatusSampler() == InstallationStatus::Installed) { + return FirmwareStatus::Installed; + } else if (installationStatusSampler() == InstallationStatus::InstallationFailed) { + return FirmwareStatus::InstallationFailed; + } + } + if (onInstall != NULL) + return FirmwareStatus::Installing; + } + + if (downloadIssued) { + if (downloadStatusSampler != NULL) { + if (downloadStatusSampler() == DownloadStatus::Downloaded) { + return FirmwareStatus::Downloaded; + } else if (downloadStatusSampler() == DownloadStatus::DownloadFailed) { + return FirmwareStatus::DownloadFailed; + } + } + if (onDownload != NULL) + return FirmwareStatus::Downloading; + } + + return FirmwareStatus::Idle; +} + +OcppOperation *FirmwareService::getFirmwareStatusNotification() { + /* + * Check if FW has been updated previously + */ + size_t buildNoSize = previousBuildNumber->getBuffsize(); + if (!strncmp(buildNumber, *previousBuildNumber, buildNoSize)) { + //new FW + previousBuildNumber->setValue(buildNumber, strlen(buildNumber)); + + lastReportedStatus = FirmwareStatus::Installed; + OcppMessage *fwNotificationMsg = new Ocpp16::FirmwareStatusNotification(lastReportedStatus); + OcppOperation *fwNotification = makeOcppOperation(fwNotificationMsg); + return fwNotification; + } + + if (getFirmwareStatus() != lastReportedStatus) { + lastReportedStatus = getFirmwareStatus(); + if (lastReportedStatus != FirmwareStatus::Idle) { + OcppMessage *fwNotificationMsg = new Ocpp16::FirmwareStatusNotification(lastReportedStatus); + OcppOperation *fwNotification = makeOcppOperation(fwNotificationMsg); + return fwNotification; + } + } + + return NULL; +} + +void FirmwareService::setOnDownload(std::function onDownload) { + this->onDownload = onDownload; +} + +void FirmwareService::setDownloadStatusSampler(std::function downloadStatusSampler) { + this->downloadStatusSampler = downloadStatusSampler; +} + +void FirmwareService::setOnInstall(std::function onInstall) { + this->onInstall = onInstall; +} + +void FirmwareService::setInstallationStatusSampler(std::function installationStatusSampler) { + this->installationStatusSampler = installationStatusSampler; +} + +#if !defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) +#if defined(ESP32) + +#include + +FirmwareService *makeFirmwareService(const char *buildNumber) { + FirmwareService *fwService = new FirmwareService(buildNumber); + + fwService->setOnInstall([fwService = fwService] (String &location) { + + fwService->setInstallationStatusSampler([](){return InstallationStatus::NotInstalled;}); + + WiFiClientSecure client; + //client.setCACert(rootCACertificate); + client.setTimeout(60); //in seconds + + // httpUpdate.setLedPin(LED_BUILTIN, HIGH); + t_httpUpdate_return ret = httpUpdate.update(client, location.c_str()); + + switch (ret) { + case HTTP_UPDATE_FAILED: + fwService->setInstallationStatusSampler([](){return InstallationStatus::InstallationFailed;}); + Serial.printf("[main] HTTP_UPDATE_FAILED Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str()); + break; + case HTTP_UPDATE_NO_UPDATES: + fwService->setInstallationStatusSampler([](){return InstallationStatus::InstallationFailed;}); + Serial.println(F("[main] HTTP_UPDATE_NO_UPDATES")); + break; + case HTTP_UPDATE_OK: + fwService->setInstallationStatusSampler([](){return InstallationStatus::Installed;}); + Serial.println(F("[main] HTTP_UPDATE_OK")); + ESP.restart(); + break; + } + + return true; + }); + + fwService->setInstallationStatusSampler([fwService = fwService] () { + return InstallationStatus::NotInstalled; + }); + + return fwService; +} + +#endif //defined(ESP32) +#if defined(ESP8266) + +#include + +FirmwareService *makeFirmwareService(const char *buildNumber) { + FirmwareService *fwService = new FirmwareService(buildNumber); + + fwService->setOnInstall([fwService = fwService] (String &location) { + + WiFiClientSecure client; + //client.setCACert(rootCACertificate); + client.setTimeout(60); //in seconds + + //ESPhttpUpdate.setLedPin(downloadStatusLedPin); + + HTTPUpdateResult ret = ESPhttpUpdate.update(client, location.c_str()); + + switch (ret) { + case HTTP_UPDATE_FAILED: + fwService->setInstallationStatusSampler([](){return InstallationStatus::InstallationFailed;}); + Serial.printf("[main] HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); + break; + case HTTP_UPDATE_NO_UPDATES: + fwService->setInstallationStatusSampler([](){return InstallationStatus::InstallationFailed;}); + Serial.println(F("[main] HTTP_UPDATE_NO_UPDATES")); + break; + case HTTP_UPDATE_OK: + fwService->setInstallationStatusSampler([](){return InstallationStatus::Installed;}); + Serial.println(F("[main] HTTP_UPDATE_OK")); + ESP.restart(); + break; + } + + return true; + }); + + fwService->setInstallationStatusSampler([fwService = fwService] () { + return InstallationStatus::NotInstalled; + }); + + return fwService; +} + +#endif //defined(ESP8266) +#endif //!defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h new file mode 100644 index 00000000..2023487e --- /dev/null +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h @@ -0,0 +1,94 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef FIRMWARESERVICE_H +#define FIRMWARESERVICE_H + +#include +#include + +#include +#include +#include +#include + +namespace ArduinoOcpp { + +enum class DownloadStatus { + NotDownloaded, // == before download or during download + Downloaded, + DownloadFailed +}; + +enum class InstallationStatus { + NotInstalled, // == before installation or during installation + Installed, + InstallationFailed +}; + +class FirmwareService { +private: + std::shared_ptr> previousBuildNumber = NULL; + const char *buildNumber = NULL; + + DownloadStatus previousDownloadStatus = DownloadStatus::NotDownloaded; + std::function downloadStatusSampler = NULL; + bool downloadIssued = false; + + InstallationStatus previousInstallationStatus = InstallationStatus::NotInstalled; + std::function installationStatusSampler = NULL; + bool installationIssued = false; + + bool updateFwEngaged = false; + + Ocpp16::FirmwareStatus lastReportedStatus = Ocpp16::FirmwareStatus::Idle; + + String location = String('\0'); + OcppTimestamp retreiveDate = OcppTimestamp(); + int retries = 0; + ulong retryInterval = 0; + + std::function onDownload = NULL; + std::function onInstall = NULL; + + ulong delayTransition = 0; + ulong timestampTransition = 0; + +public: + FirmwareService(const char *buildNumber); + + void loop(); + + void scheduleFirmwareUpdate(String &location, OcppTimestamp retreiveDate, int retries = 1, ulong retryInterval = 0); + + Ocpp16::FirmwareStatus getFirmwareStatus(); + + OcppOperation *getFirmwareStatusNotification(); + + void setOnDownload(std::function onDownload); + + void setDownloadStatusSampler(std::function downloadStatusSampler); + + void setOnInstall(std::function onInstall); + + void setInstallationStatusSampler(std::function installationStatusSampler); +}; + +} //endif namespace ArduinoOcpp + +#if !defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) +#if defined(ESP32) || defined(ESP8266) + +namespace ArduinoOcpp { +namespace EspWiFi { + +FirmwareService *makeFirmwareService(const char *buildNumber); + +} +} + +#endif //!defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) +#endif //AO_CUSTOM_UPDATER + +#endif From 457dfb2e09958a54ec3f7cd56372927d41e1e2c1 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 25 Aug 2021 16:57:40 +0200 Subject: [PATCH 054/696] refined update procedure --- .../FirmwareManagement/FirmwareService.cpp | 210 ++++++++++++------ .../FirmwareManagement/FirmwareService.h | 19 +- 2 files changed, 159 insertions(+), 70 deletions(-) diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index 87676997..e861a9c5 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -13,7 +13,7 @@ using namespace ArduinoOcpp; using ArduinoOcpp::Ocpp16::FirmwareStatus; FirmwareService::FirmwareService(const char *buildNumber) : buildNumber(buildNumber) { - previousBuildNumber = declareConfiguration("BUILD_NUMBER", "", CONFIGURATION_FN, false, false, true, false); + previousBuildNumber = declareConfiguration("BUILD_NUMBER", buildNumber, CONFIGURATION_FN, false, false, true, false); } void FirmwareService::loop() { @@ -26,43 +26,68 @@ void FirmwareService::loop() { return; } - FirmwareStatus status = getFirmwareStatus(); OcppTimestamp timestampNow = getOcppTime()->getOcppTimestampNow(); if (retries > 0 && timestampNow >= retreiveDate) { - if (!downloadIssued) { - downloadIssued = true; + //if (!downloadIssued) { + if (stage == UpdateStage::Idle) { + if (onDownload == NULL) { + stage = UpdateStage::AfterDownload; + } else { + downloadIssued = true; + stage = UpdateStage::AwaitDownload; + timestampTransition = millis(); + delayTransition = 5000; //delay between state "Downloading" and actually starting the download + return; + } + } + + //if (!onDownloadCalled) { + if (stage == UpdateStage::AwaitDownload) { + stage = UpdateStage::Downloading; if (onDownload != NULL) { onDownload(location); timestampTransition = millis(); - delayTransition = 30000; + delayTransition = 30000; //give the download at least 30s + return; } - return; } const int DOWNLOAD_TIMEOUT = 120; - if ((downloadIssued && timestampNow - retreiveDate >= DOWNLOAD_TIMEOUT) - || (downloadStatusSampler != NULL && downloadStatusSampler() == DownloadStatus::DownloadFailed)) { - - Serial.println(F("[FirmwareService] Download timeout or failed! Retry")); - if (retryInterval < DOWNLOAD_TIMEOUT) - retreiveDate = timestampNow; - else - retreiveDate += retryInterval; - retries--; - downloadIssued = false; - installationIssued = false; + //if (downloadIssued && !installationIssued && + if (stage == UpdateStage::Downloading) { - timestampTransition = millis(); - delayTransition = 10000; - return; - } + //check if client reports download to be finished + if (downloadStatusSampler != NULL && downloadStatusSampler() == DownloadStatus::Downloaded) { + stage = UpdateStage::AfterDownload; + } + + //if client doesn't report download state, assume download to be finished (at least 30s download time have passed until here) + if (downloadStatusSampler == NULL) { + stage = UpdateStage::AfterDownload; + } + + //check for timeout or error condition + if (timestampNow - retreiveDate >= DOWNLOAD_TIMEOUT + || (downloadStatusSampler != NULL && downloadStatusSampler() == DownloadStatus::DownloadFailed)) { + + Serial.println(F("[FirmwareService] Download timeout or failed! Retry")); + if (retryInterval < DOWNLOAD_TIMEOUT) + retreiveDate = timestampNow; + else + retreiveDate += retryInterval; + retries--; + resetStage(); + + timestampTransition = millis(); + delayTransition = 10000; + return; + } - if (downloadStatusSampler != NULL && downloadStatusSampler() == DownloadStatus::NotDownloaded) { - return; } - if (!installationIssued) { + //if (!installationIssued) { + if (stage == UpdateStage::AfterDownload) { bool ongoingTx = false; ChargePointStatusService *cpStatus = getChargePointStatusService(); if (cpStatus) { @@ -76,43 +101,70 @@ void FirmwareService::loop() { } if (!ongoingTx) { + stage = UpdateStage::AwaitInstallation; installationIssued = true; - if (onInstall != NULL) { - onInstall(location); - } else { - Serial.println(F("[FirmwareService] onInstall must be set! (see setOnInstall). Will abort")); - } - timestampTransition = millis(); - delayTransition = 20000; + delayTransition = 10000; } return; } - const int INSTALLATION_TIMEOUT = 120; - if ((installationIssued && timestampNow - retreiveDate >= INSTALLATION_TIMEOUT + DOWNLOAD_TIMEOUT) - || (installationStatusSampler != NULL && installationStatusSampler() == InstallationStatus::InstallationFailed)) { + if (stage == UpdateStage::AwaitInstallation) { + stage = UpdateStage::Installing; - Serial.println(F("[FirmwareService] Installation timeout or failed! Retry")); - if (retryInterval < INSTALLATION_TIMEOUT + DOWNLOAD_TIMEOUT) - retreiveDate = timestampNow; - else - retreiveDate += retryInterval; - retries--; - downloadIssued = false; - installationIssued = false; + if (onInstall != NULL) { + onInstall(location); //should restart the device on success + } else { + Serial.println(F("[FirmwareService] onInstall must be set! (see setOnInstall). Will abort")); + } timestampTransition = millis(); - delayTransition = 10000; + delayTransition = 40000; return; } - - if (installationStatusSampler != NULL && installationStatusSampler() == InstallationStatus::NotInstalled) { - return; + + if (stage == UpdateStage::Installing) { + + //check if client reports installation to be finished + if (installationStatusSampler != NULL && installationStatusSampler() == InstallationStatus::Installed) { + //Client should reboot during onInstall. If not, client is responsible to reboot at a later point + resetStage(); + retries = 0; //Success. End of update routine + return; + } + + //if client doesn't report installation state, assume download to be finished (at least 40s installation time have passed until here) + if (installationStatusSampler == NULL) { + resetStage(); + retries = 0; //End of update routine. Client must reboot on its own + return; + } + + //check for timeout or error condition + const int INSTALLATION_TIMEOUT = 120; + if ((timestampNow - retreiveDate >= INSTALLATION_TIMEOUT + DOWNLOAD_TIMEOUT) + || (installationStatusSampler != NULL && installationStatusSampler() == InstallationStatus::InstallationFailed)) { + + Serial.println(F("[FirmwareService] Installation timeout or failed! Retry")); + if (retryInterval < INSTALLATION_TIMEOUT + DOWNLOAD_TIMEOUT) + retreiveDate = timestampNow; + else + retreiveDate += retryInterval; + retries--; + resetStage(); + + timestampTransition = millis(); + delayTransition = 10000; + return; + } } - + + //should never reach this code + Serial.println(F("[FirmwareService] Unconsidered special case occured. Firmware update failed")); + retries = 0; + resetStage(); } } @@ -122,8 +174,7 @@ void FirmwareService::scheduleFirmwareUpdate(String &location, OcppTimestamp ret this->retries = retries; this->retryInterval = retryInterval; - downloadIssued = false; - installationIssued = false; + resetStage(); } FirmwareStatus FirmwareService::getFirmwareStatus() { @@ -156,22 +207,27 @@ FirmwareStatus FirmwareService::getFirmwareStatus() { OcppOperation *FirmwareService::getFirmwareStatusNotification() { /* - * Check if FW has been updated previously + * Check if FW has been updated previously, but only once */ - size_t buildNoSize = previousBuildNumber->getBuffsize(); - if (!strncmp(buildNumber, *previousBuildNumber, buildNoSize)) { - //new FW - previousBuildNumber->setValue(buildNumber, strlen(buildNumber)); - - lastReportedStatus = FirmwareStatus::Installed; - OcppMessage *fwNotificationMsg = new Ocpp16::FirmwareStatusNotification(lastReportedStatus); - OcppOperation *fwNotification = makeOcppOperation(fwNotificationMsg); - return fwNotification; + if (!checkedSuccessfulFwUpdate) { + checkedSuccessfulFwUpdate = true; + + size_t buildNoSize = previousBuildNumber->getBuffsize(); + if (!strncmp(buildNumber, *previousBuildNumber, buildNoSize)) { + //new FW + previousBuildNumber->setValue(buildNumber, strlen(buildNumber)); + configuration_save(); + + lastReportedStatus = FirmwareStatus::Installed; + OcppMessage *fwNotificationMsg = new Ocpp16::FirmwareStatusNotification(lastReportedStatus); + OcppOperation *fwNotification = makeOcppOperation(fwNotificationMsg); + return fwNotification; + } } if (getFirmwareStatus() != lastReportedStatus) { lastReportedStatus = getFirmwareStatus(); - if (lastReportedStatus != FirmwareStatus::Idle) { + if (lastReportedStatus != FirmwareStatus::Idle && lastReportedStatus != FirmwareStatus::Installed) { OcppMessage *fwNotificationMsg = new Ocpp16::FirmwareStatusNotification(lastReportedStatus); OcppOperation *fwNotification = makeOcppOperation(fwNotificationMsg); return fwNotification; @@ -197,6 +253,12 @@ void FirmwareService::setInstallationStatusSampler(std::functioninstallationStatusSampler = installationStatusSampler; } +void FirmwareService::resetStage() { + stage = UpdateStage::Idle; + downloadIssued = false; + installationIssued = false; +} + #if !defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) #if defined(ESP32) @@ -205,11 +267,29 @@ void FirmwareService::setInstallationStatusSampler(std::functionsetOnDownload([] (String &location) { + //download the new binary + //... + return true; + }); + + fwService->setDownloadStatusSampler([fwService = fwService] () { + //report the download progress + //... + return DownloadStatus::NotDownloaded; + }); +#endif //separate download phase + fwService->setOnInstall([fwService = fwService] (String &location) { fwService->setInstallationStatusSampler([](){return InstallationStatus::NotInstalled;}); - WiFiClientSecure client; + WiFiClient client; + //WiFiClientSecure client; //client.setCACert(rootCACertificate); client.setTimeout(60); //in seconds @@ -242,8 +322,7 @@ FirmwareService *makeFirmwareService(const char *buildNumber) { return fwService; } -#endif //defined(ESP32) -#if defined(ESP8266) +#elif defined(ESP8266) #include @@ -251,8 +330,9 @@ FirmwareService *makeFirmwareService(const char *buildNumber) { FirmwareService *fwService = new FirmwareService(buildNumber); fwService->setOnInstall([fwService = fwService] (String &location) { - - WiFiClientSecure client; + + WiFiClient client; + //WiFiClientSecure client; //client.setCACert(rootCACertificate); client.setTimeout(60); //in seconds diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h index 2023487e..641b2fe4 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h @@ -27,22 +27,20 @@ enum class InstallationStatus { InstallationFailed }; + class FirmwareService { private: std::shared_ptr> previousBuildNumber = NULL; const char *buildNumber = NULL; - DownloadStatus previousDownloadStatus = DownloadStatus::NotDownloaded; std::function downloadStatusSampler = NULL; bool downloadIssued = false; - InstallationStatus previousInstallationStatus = InstallationStatus::NotInstalled; std::function installationStatusSampler = NULL; bool installationIssued = false; - bool updateFwEngaged = false; - Ocpp16::FirmwareStatus lastReportedStatus = Ocpp16::FirmwareStatus::Idle; + bool checkedSuccessfulFwUpdate = false; String location = String('\0'); OcppTimestamp retreiveDate = OcppTimestamp(); @@ -55,6 +53,17 @@ class FirmwareService { ulong delayTransition = 0; ulong timestampTransition = 0; + enum class UpdateStage { + Idle, + AwaitDownload, + Downloading, + AfterDownload, + AwaitInstallation, + Installing + } stage; + + void resetStage(); + public: FirmwareService(const char *buildNumber); @@ -88,7 +97,7 @@ FirmwareService *makeFirmwareService(const char *buildNumber); } } +#endif //defined(ESP32) || defined(ESP8266) #endif //!defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) -#endif //AO_CUSTOM_UPDATER #endif From 5273776d4aa72e64de8956d7210887038605c5aa Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 14 Sep 2021 14:05:57 +0200 Subject: [PATCH 055/696] add local energy management functionality --- .../MessagesV16/SetChargingProfile.cpp | 33 ++++++- .../MessagesV16/SetChargingProfile.h | 20 +++- .../SmartCharging/SmartChargingModel.cpp | 97 +++++++++++++++++++ .../Tasks/SmartCharging/SmartChargingModel.h | 13 +++ .../SmartCharging/SmartChargingService.cpp | 22 +++++ .../SmartCharging/SmartChargingService.h | 1 + 6 files changed, 177 insertions(+), 9 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp index 97bc2094..22229979 100644 --- a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp +++ b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp @@ -13,6 +13,16 @@ SetChargingProfile::SetChargingProfile(SmartChargingService *smartChargingServic } +SetChargingProfile::SetChargingProfile(DynamicJsonDocument *payloadToClient) + : payloadToClient(payloadToClient) { + +} + +SetChargingProfile::~SetChargingProfile() { + if (payloadToClient != NULL) + delete payloadToClient; +} + const char* SetChargingProfile::getOcppOperationType(){ return "SetChargingProfile"; } @@ -27,8 +37,23 @@ void SetChargingProfile::processReq(JsonObject payload) { } DynamicJsonDocument* SetChargingProfile::createConf(){ //TODO review - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); - JsonObject payload = doc->to(); - payload["status"] = "Accepted"; - return doc; + DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); + JsonObject payload = doc->to(); + payload["status"] = "Accepted"; + return doc; +} + +DynamicJsonDocument* SetChargingProfile::createReq() { + if (payloadToClient != NULL) { + DynamicJsonDocument *result = new DynamicJsonDocument(*payloadToClient); + return result; + } + return NULL; +} + +void SetChargingProfile::processConf(JsonObject payload) { + const char* status = payload["status"] | "Invalid"; + if (strcmp(status, "Accepted")) { + Serial.println(F("[SetChargingProfile] Send profile: rejected by client!")); + } } diff --git a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h index c97f6c85..3599b61c 100644 --- a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h +++ b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h @@ -13,15 +13,25 @@ namespace Ocpp16 { class SetChargingProfile : public OcppMessage { private: - SmartChargingService *smartChargingService; + SmartChargingService *smartChargingService = NULL; + + DynamicJsonDocument *payloadToClient = NULL; public: - SetChargingProfile(SmartChargingService *smartChargingService); + SetChargingProfile(SmartChargingService *smartChargingService); + + SetChargingProfile(DynamicJsonDocument *payloadToClient); + + ~SetChargingProfile(); + + const char* getOcppOperationType(); + + void processReq(JsonObject payload); - const char* getOcppOperationType(); + DynamicJsonDocument* createConf(); - void processReq(JsonObject payload); + DynamicJsonDocument* createReq(); - DynamicJsonDocument* createConf(); + void processConf(JsonObject payload); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp index d085af1d..0963ff49 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp @@ -18,12 +18,23 @@ ChargingSchedulePeriod::ChargingSchedulePeriod(JsonObject *json){ limit = (*json)["limit"]; //one fractural digit at most numberPhases = (*json)["numberPhases"] | -1; } +ChargingSchedulePeriod::ChargingSchedulePeriod(int startPeriod, float limit){ + this->startPeriod = startPeriod; + this->limit = limit; + numberPhases = -1; //support later +} int ChargingSchedulePeriod::getStartPeriod(){ return this->startPeriod; } float ChargingSchedulePeriod::getLimit(){ return this->limit; } +void ChargingSchedulePeriod::scale(float factor){ + limit *= factor; +} +void ChargingSchedulePeriod::add(float value){ + limit += value; +} int ChargingSchedulePeriod::getNumberPhases(){ Serial.print(F("[SmartChargingModel] Unsupported operation: ChargingSchedulePeriod::getNumberPhases(); No phase control implemented")); return this->numberPhases; @@ -75,6 +86,40 @@ ChargingSchedule::ChargingSchedule(JsonObject *json, ChargingProfileKindType cha minChargingRate = (*json)["minChargingRate"] | -1.0f; } +ChargingSchedule::ChargingSchedule(ChargingSchedule &other) { + chargingProfileKind = other.chargingProfileKind; + recurrencyKind = other.recurrencyKind; + duration = other.duration; + startSchedule = other.startSchedule; + schedulingUnit = other.schedulingUnit; + + chargingSchedulePeriod = std::vector(); + + for (auto period = other.chargingSchedulePeriod.begin(); period != other.chargingSchedulePeriod.end(); period++) { + ChargingSchedulePeriod *p = new ChargingSchedulePeriod(**period); + chargingSchedulePeriod.push_back(p); + } + + //Expecting sorted list of periods but specification doesn't garantuee it + std::sort(chargingSchedulePeriod.begin(), chargingSchedulePeriod.end(), [] (ChargingSchedulePeriod *p1, ChargingSchedulePeriod *p2) { + return p1->getStartPeriod() < p2->getStartPeriod(); + }); + + minChargingRate = other.minChargingRate; +} + +ChargingSchedule::ChargingSchedule(OcppTimestamp &startT, int duration) { + //create empty but valid Charging Schedule + this->duration = duration; + startSchedule = startT; + schedulingUnit = 'W'; //either 'A' or 'W' + std::vector chargingSchedulePeriod = std::vector(); + float minChargingRate = 0.f; + + chargingProfileKind = ChargingProfileKindType::Absolute; //copied from ChargingProfile to increase cohesion of limit inferencing methods + recurrencyKind = RecurrencyKindType::NOT_SET; //copied from ChargingProfile to increase cohesion of limit inferencing methods +} + ChargingSchedule::~ChargingSchedule(){ for (size_t i = 0; i < chargingSchedulePeriod.size(); i++) { delete chargingSchedulePeriod.at(i); @@ -180,6 +225,58 @@ bool ChargingSchedule::inferenceLimit(const OcppTimestamp &t, const OcppTimestam } } +bool ChargingSchedule::addChargingSchedulePeriod(ChargingSchedulePeriod *period) { + if (period == NULL) return false; + + if (period->getStartPeriod() >= duration) { + return false; + } + + chargingSchedulePeriod.push_back(period); + return true; +} + +void ChargingSchedule::scale(float factor) { + for (auto p = chargingSchedulePeriod.begin(); p != chargingSchedulePeriod.end(); p++) { + (*p)->scale(factor); + } +} + +void ChargingSchedule::translate(float offset) { + for (auto p = chargingSchedulePeriod.begin(); p != chargingSchedulePeriod.end(); p++) { + (*p)->add(offset); + } +} + +DynamicJsonDocument *ChargingSchedule::toJsonDocument() { + size_t capacity = 0; + capacity += JSON_OBJECT_SIZE(5); //no of fields of ChargingSchedule + capacity += JSONDATE_LENGTH + 1; //startSchedule + capacity += 2; //scheduling unit will be saved as string + capacity += JSON_ARRAY_SIZE(chargingSchedulePeriod.size()) + chargingSchedulePeriod.size() * JSON_OBJECT_SIZE(3); + + DynamicJsonDocument *result = new DynamicJsonDocument(capacity); + JsonObject payload = result->to(); + if (duration >= 0) + payload["duration"] = duration; + char startScheduleJson [JSONDATE_LENGTH + 1] = {'\0'}; + startSchedule.toJsonString(startScheduleJson, JSONDATE_LENGTH + 1); + payload["startSchedule"] = startScheduleJson; + payload["schedulingUnit"] = schedulingUnit; + JsonArray periodArray = payload.createNestedArray("chargingSchedulePeriod"); + for (auto period = chargingSchedulePeriod.begin(); period != chargingSchedulePeriod.end(); period++) { + JsonObject entry = periodArray.createNestedObject(); + entry["startPeriod"] = (*period)->getStartPeriod(); + entry["limit"] = (*period)->getLimit(); + if ((*period)->getNumberPhases() >= 0) { + entry["numberPhases"] = (*period)->getNumberPhases(); + } + } + if (minChargingRate >= 0) + payload["minChargeRate"] = minChargingRate; + + return result; +} void ChargingSchedule::printSchedule(){ Serial.print(F(" duration: ")); diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h index d47ecfb8..f9eb7338 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h @@ -38,8 +38,11 @@ class ChargingSchedulePeriod { int numberPhases = -1; public: ChargingSchedulePeriod(JsonObject *json); + ChargingSchedulePeriod(int startPeriod, float limit); int getStartPeriod(); float getLimit(); + void scale(float factor); + void add(float value); int getNumberPhases(); void printPeriod(); }; @@ -56,6 +59,8 @@ class ChargingSchedule { RecurrencyKindType recurrencyKind; //copied from ChargingProfile to increase cohesion of limit inferencing methods public: ChargingSchedule(JsonObject *json, ChargingProfileKindType chargingProfileKind, RecurrencyKindType recurrencyKind); + ChargingSchedule(ChargingSchedule &other); + ChargingSchedule(OcppTimestamp &startSchedule, int duration); ~ChargingSchedule(); /** @@ -68,6 +73,13 @@ class ChargingSchedule { */ bool inferenceLimit(const OcppTimestamp &t, const OcppTimestamp &startOfCharging, float *limit, OcppTimestamp *nextChange); + bool addChargingSchedulePeriod(ChargingSchedulePeriod *period); + + void scale(float factor); + void translate(float offset); + + DynamicJsonDocument *toJsonDocument(); + /* * print on console */ @@ -87,6 +99,7 @@ class ChargingProfile { ChargingSchedule *chargingSchedule; public: ChargingProfile(JsonObject *json); + ChargingProfile(ChargingSchedule *schedule, int chargingProfileId); //For Energy management. Can be extended with more parameters later ~ChargingProfile(); /** diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index baa2c040..53ac6002 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -196,6 +196,28 @@ void SmartChargingService::writeOutCompositeSchedule(JsonObject *json){ Serial.print(F("[SmartChargingService] Unsupported Operation: SmartChargingService::writeOutCompositeSchedule\n")); } +ChargingSchedule *SmartChargingService::getCompositeSchedule(int connectorId, otime_t duration){ + OcppTime *ocppTime = getOcppTime(); + OcppTimestamp startSchedule = OcppTimestamp(); + if (ocppTime) { + startSchedule = ocppTime->getOcppTimestampNow(); + } + ChargingSchedule *result = new ChargingSchedule(startSchedule, duration); + OcppTimestamp periodBegin = OcppTimestamp(startSchedule); + OcppTimestamp periodStop = OcppTimestamp(startSchedule); + while (periodBegin - startSchedule < duration) { + float limit = 0.f; + inferenceLimit(periodBegin, &limit, &periodStop); + ChargingSchedulePeriod *p = new ChargingSchedulePeriod(periodBegin - startSchedule, limit); + if (!result->addChargingSchedulePeriod(p)) { + delete p; + break; + } + periodBegin = periodStop; + } + return result; +} + void SmartChargingService::refreshChargingSessionState() { int currentTxId = getChargePointStatusService()->getConnector(SINGLE_CONNECTOR_ID)->getTransactionId(); diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h index 6ee0e2d0..561bde6d 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h @@ -50,6 +50,7 @@ class SmartChargingService { void inferenceLimit(const OcppTimestamp &t, float *limit, OcppTimestamp *validTo); float inferenceLimitNow(); void setOnLimitChange(OnLimitChange onLimitChange); + ChargingSchedule *getCompositeSchedule(int connectorId, otime_t duration); void writeOutCompositeSchedule(JsonObject *json); void loop(); }; From bef5b574fe7cc2d9fb115c3f5e132e45951199d4 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 15 Sep 2021 16:46:09 +0200 Subject: [PATCH 056/696] improved range checking --- src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp index 0963ff49..b0fe0a4b 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp @@ -31,12 +31,15 @@ float ChargingSchedulePeriod::getLimit(){ } void ChargingSchedulePeriod::scale(float factor){ limit *= factor; + if (limit < 0.f) + limit *= -1.f; } void ChargingSchedulePeriod::add(float value){ limit += value; + if (limit < 0.f) + limit = 0; } int ChargingSchedulePeriod::getNumberPhases(){ - Serial.print(F("[SmartChargingModel] Unsupported operation: ChargingSchedulePeriod::getNumberPhases(); No phase control implemented")); return this->numberPhases; } From 73cb9e0a99111c218eb8426de14d8dca07a0f557 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 16 Sep 2021 11:49:04 +0200 Subject: [PATCH 057/696] custom meter reading --- src/ArduinoOcpp/SimpleOcppOperationFactory.cpp | 6 ++++++ src/ArduinoOcpp/SimpleOcppOperationFactory.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index f15e238a..50126724 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -117,6 +117,11 @@ void setOnUpdateFirmwareReceiveRequestListener(OnReceiveReqListener listener) { onUpdateFirmwareReceiveReq = listener; } +OnReceiveReqListener onMeterValuesReceiveReq = NULL; +void setOnMeterValuesReceiveRequestListener(OnReceiveReqListener listener) { + onMeterValuesReceiveReq = listener; +} + struct CustomOcppMessageCreatorEntry { const char *messageType; OcppMessageCreator creator; @@ -185,6 +190,7 @@ OcppOperation *makeOcppOperation(const char *messageType) { msg = new Ocpp16::Heartbeat(); } else if (!strcmp(messageType, "MeterValues")) { msg = new Ocpp16::MeterValues(); + operation->setOnReceiveReqListener(onMeterValuesReceiveReq); } else if (!strcmp(messageType, "SetChargingProfile")) { msg = new Ocpp16::SetChargingProfile(getSmartChargingService()); operation->setOnReceiveReqListener(onSetChargingProfileRequest); diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.h b/src/ArduinoOcpp/SimpleOcppOperationFactory.h index 532c13a9..1ebaeabe 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.h +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.h @@ -42,6 +42,7 @@ void setOnGetConfigurationSendConfListener(OnSendConfListener onSendConf); void setOnResetReceiveRequestListener(OnReceiveReqListener onReceiveReq); void setOnResetSendConfListener(OnSendConfListener onSendConf); void setOnUpdateFirmwareReceiveRequestListener(OnReceiveReqListener onReceiveReq); +void setOnMeterValuesReceiveRequestListener(OnReceiveReqListener onReceiveReq); } //end namespace ArduinoOcpp #endif From eca234cfd42a2750c9bbd8814c5158d411991fda Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 16 Sep 2021 13:03:35 +0200 Subject: [PATCH 058/696] define static variable --- src/ArduinoOcpp/Core/OcppServer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ArduinoOcpp/Core/OcppServer.cpp b/src/ArduinoOcpp/Core/OcppServer.cpp index c7d592a3..fa6010fa 100644 --- a/src/ArduinoOcpp/Core/OcppServer.cpp +++ b/src/ArduinoOcpp/Core/OcppServer.cpp @@ -98,6 +98,8 @@ void OcppServer::wsockEvent(WsClient num, WStype_t type, uint8_t * payload, size } } +OcppServer::OcppServer *instance = NULL; + OcppServer *OcppServer::getInstance() { if (!instance){ instance = new OcppServer(); From d84ee678fb64998d70e368d99ee733de6b4525c2 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 16 Sep 2021 13:06:10 +0200 Subject: [PATCH 059/696] fixed definition --- src/ArduinoOcpp/Core/OcppServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/Core/OcppServer.cpp b/src/ArduinoOcpp/Core/OcppServer.cpp index fa6010fa..c47d5d6e 100644 --- a/src/ArduinoOcpp/Core/OcppServer.cpp +++ b/src/ArduinoOcpp/Core/OcppServer.cpp @@ -98,7 +98,7 @@ void OcppServer::wsockEvent(WsClient num, WStype_t type, uint8_t * payload, size } } -OcppServer::OcppServer *instance = NULL; +OcppServer *OcppServer::instance = NULL; OcppServer *OcppServer::getInstance() { if (!instance){ From 07561a524a4f54680b2b1c3ef7c99d2d63eed0c0 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 16 Sep 2021 15:59:19 +0200 Subject: [PATCH 060/696] improved server-side time provider --- .../MessagesV16/BootNotification.cpp | 18 ++++- src/ArduinoOcpp/MessagesV16/Heartbeat.cpp | 66 +++++++++++-------- 2 files changed, 54 insertions(+), 30 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp index 7384daec..e024d28a 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp @@ -107,7 +107,23 @@ void BootNotification::processReq(JsonObject payload){ DynamicJsonDocument* BootNotification::createConf(){ DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(3) + (JSONDATE_LENGTH + 1)); JsonObject payload = doc->to(); - payload["currentTime"] = "2019-11-01T11:59:55.123Z"; + + //safety mechanism; in some test setups the library has to answer BootNotifications without valid system time + OcppTimestamp ocppTimeReference = OcppTimestamp(2019,10,0,11,59,55); + OcppTimestamp ocppSelect = ocppTimeReference; + OcppTime *ocppTime = getOcppTime(); + if (ocppTime) { + OcppTimestamp ocppNow = ocppTime->getOcppTimestampNow(); + if (ocppNow > ocppTimeReference) { + //time has already been set + ocppSelect = ocppNow; + } + } + + char ocppNowJson [JSONDATE_LENGTH + 1] = {'\0'}; + ocppSelect.toJsonString(ocppNowJson, JSONDATE_LENGTH + 1); + payload["currentTime"] = ocppNowJson; + payload["interval"] = 86400; //heartbeat send interval - not relevant for JSON variant of OCPP so send dummy value that likely won't break payload["status"] = "Accepted"; return doc; diff --git a/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp b/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp index c8f45def..b4ece933 100644 --- a/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp +++ b/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp @@ -18,50 +18,58 @@ const char* Heartbeat::getOcppOperationType(){ } DynamicJsonDocument* Heartbeat::createReq() { - DynamicJsonDocument *doc = new DynamicJsonDocument(0); - //JsonObject payload = doc->to(); - /* - * Empty payload - */ - return doc; + DynamicJsonDocument *doc = new DynamicJsonDocument(0); + //JsonObject payload = doc->to(); + /* + * Empty payload + */ + return doc; } void Heartbeat::processConf(JsonObject payload) { - const char* currentTime = payload["currentTime"] | "Invalid"; - if (strcmp(currentTime, "Invalid")) { - OcppTime *ocppTime = getOcppTime(); - if (ocppTime && ocppTime->setOcppTime(currentTime)) { - //success - if (DEBUG_OUT) Serial.print(F("[Heartbeat] Request has been accepted!\n")); + const char* currentTime = payload["currentTime"] | "Invalid"; + if (strcmp(currentTime, "Invalid")) { + OcppTime *ocppTime = getOcppTime(); + if (ocppTime && ocppTime->setOcppTime(currentTime)) { + //success + if (DEBUG_OUT) Serial.print(F("[Heartbeat] Request has been accepted!\n")); + } else { + Serial.print(F("[Heartbeat] Request accepted. But Error reading time string. Expect format like 2020-02-01T20:53:32.486Z\n")); + } } else { - Serial.print(F("[Heartbeat] Request accepted. But Error reading time string. Expect format like 2020-02-01T20:53:32.486Z\n")); + Serial.print(F("[Heartbeat] Request denied. Missing field currentTime. Expect format like 2020-02-01T20:53:32.486Z\n")); } - } else { - Serial.print(F("[Heartbeat] Request denied. Missing field currentTime. Expect format like 2020-02-01T20:53:32.486Z\n")); - } } void Heartbeat::processReq(JsonObject payload) { - /** - * Ignore Contents of this Req-message, because this is for debug purposes only - */ + /** + * Ignore Contents of this Req-message, because this is for debug purposes only + */ } DynamicJsonDocument* Heartbeat::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + (JSONDATE_LENGTH + 1)); - JsonObject payload = doc->to(); + DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + (JSONDATE_LENGTH + 1)); + JsonObject payload = doc->to(); + + //safety mechanism; in some test setups the library could have to answer Heartbeats without valid system time + OcppTimestamp ocppTimeReference = OcppTimestamp(2019,10,0,11,59,55); + OcppTimestamp ocppSelect = ocppTimeReference; + OcppTime *ocppTime = getOcppTime(); + if (ocppTime) { + OcppTimestamp ocppNow = ocppTime->getOcppTimestampNow(); + if (ocppNow > ocppTimeReference) { + //time has already been set + ocppSelect = ocppNow; + } + } - OcppTime *ocppTime = getOcppTime(); - if (ocppTime) { - const OcppTimestamp otimestamp = ocppTime->getOcppTimestampNow(); - char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; - otimestamp.toJsonString(timestamp, JSONDATE_LENGTH + 1); - payload["currentTime"] = timestamp; - } + char ocppNowJson [JSONDATE_LENGTH + 1] = {'\0'}; + ocppSelect.toJsonString(ocppNowJson, JSONDATE_LENGTH + 1); + payload["currentTime"] = ocppNowJson; - return doc; + return doc; } From 2f2d4f5c478851b15987a045f6a777be318fdde3 Mon Sep 17 00:00:00 2001 From: Matthias Akstaller <63792403+matth-x@users.noreply.github.com> Date: Sun, 19 Sep 2021 10:16:21 +0200 Subject: [PATCH 061/696] Create Readme.md --- examples/OneConnector-EVSE/Readme.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 examples/OneConnector-EVSE/Readme.md diff --git a/examples/OneConnector-EVSE/Readme.md b/examples/OneConnector-EVSE/Readme.md new file mode 100644 index 00000000..8c069a41 --- /dev/null +++ b/examples/OneConnector-EVSE/Readme.md @@ -0,0 +1,3 @@ +#### How to run this example + +You can find a description of how this example works in the source files. To compile this example, you need to copy all three files into your project folder. From 7b445798ce8ddb8ce24bfc544fdfbc20061d5344 Mon Sep 17 00:00:00 2001 From: Matthias Akstaller <63792403+matth-x@users.noreply.github.com> Date: Sun, 19 Sep 2021 10:17:01 +0200 Subject: [PATCH 062/696] Create Readme.md --- examples/Simulation_without_HW/Readme.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 examples/Simulation_without_HW/Readme.md diff --git a/examples/Simulation_without_HW/Readme.md b/examples/Simulation_without_HW/Readme.md new file mode 100644 index 00000000..8c069a41 --- /dev/null +++ b/examples/Simulation_without_HW/Readme.md @@ -0,0 +1,3 @@ +#### How to run this example + +You can find a description of how this example works in the source files. To compile this example, you need to copy all three files into your project folder. From cd808b1d4faed5ad2e4796fabcbf44d83e62809f Mon Sep 17 00:00:00 2001 From: Matthias Akstaller <63792403+matth-x@users.noreply.github.com> Date: Sun, 19 Sep 2021 10:42:49 +0200 Subject: [PATCH 063/696] Update README.md --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 22529e57..2f723378 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ # ArduinoOcpp -OCPP 1.6J Smart Charging client for the ESP8266 and the ESP32 (more coming soon) +OCPP-J 1.6 client for the ESP8266 and the ESP32 (more coming soon) -Website: [www.arduino-ocpp.com](https://www.arduino-ocpp.com) +Reference usage: [OpenEVSE](https://github.com/OpenEVSE/ESP32_WiFi_V4.x/blob/master/src/ocpp.cpp) PlatformIO package: [ArduinoOcpp](https://platformio.org/lib/show/11975/ArduinoOcpp) -_Former repo name: ESP8266-OCPP_ +Website: [www.arduino-ocpp.com](https://www.arduino-ocpp.com) + +Full compatibility with the ESP8266 and the ESP32 on the Arduino platform. Need a **FreeRTOS** version? Please [contact me](https://github.com/matth-x/ArduinoOcpp#further-help) ## Make your EVSE ready for OCPP :car::electric_plug::battery: @@ -15,7 +17,7 @@ You can build an OCPP Charge Point controller using the popular, Wi-Fi enabled m :heavy_check_mark: Tested with two further (proprietary) central systems -:heavy_check_mark: Already integrated in two charging stations (including a ClipperCreek Inc. station) +:heavy_check_mark: Integrated and tested in many charging stations (including a ClipperCreek Inc. station) ### Features From 640064a3de542f792cbae1b23d5014b53d74ebe4 Mon Sep 17 00:00:00 2001 From: Matthias Akstaller <63792403+matth-x@users.noreply.github.com> Date: Sun, 19 Sep 2021 11:24:09 +0200 Subject: [PATCH 064/696] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2f723378..ab87c46e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ArduinoOcpp +# Icon   ArduinoOcpp OCPP-J 1.6 client for the ESP8266 and the ESP32 (more coming soon) Reference usage: [OpenEVSE](https://github.com/OpenEVSE/ESP32_WiFi_V4.x/blob/master/src/ocpp.cpp) @@ -7,7 +7,7 @@ PlatformIO package: [ArduinoOcpp](https://platformio.org/lib/show/11975/ArduinoO Website: [www.arduino-ocpp.com](https://www.arduino-ocpp.com) -Full compatibility with the ESP8266 and the ESP32 on the Arduino platform. Need a **FreeRTOS** version? Please [contact me](https://github.com/matth-x/ArduinoOcpp#further-help) +Full compatibility with the Arduino platform. Need a **FreeRTOS** version? Please [contact me](https://github.com/matth-x/ArduinoOcpp#further-help) ## Make your EVSE ready for OCPP :car::electric_plug::battery: From bdf4a929ed15e30776b69f037df5bdc05d6b6a1f Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 5 Oct 2021 16:40:35 +0200 Subject: [PATCH 065/696] save HeartbeatInterval in BootNotification --- src/ArduinoOcpp/MessagesV16/BootNotification.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp index e024d28a..bf0df08d 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -84,7 +85,16 @@ void BootNotification::processConf(JsonObject payload){ Serial.print(F("[BootNotification] Error reading time string. Missing attribute currentTime of type string\n")); } - //int interval = payload["interval"] | 86400; //not used in this implementation + int interval = payload["interval"] | -1; + + //only write if in valid range + if (interval >= 1) { + std::shared_ptr> intervalConf = declareConfiguration("HeartbeatInterval", 86400); + if (intervalConf && interval != *intervalConf) { + *intervalConf = interval; + configuration_save(); + } + } const char* status = payload["status"] | "Invalid"; From 2c23c7a7774037ff4069f09e68cfd3999ada915f Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 6 Oct 2021 14:55:52 +0200 Subject: [PATCH 066/696] Fixed error message when populating FS --- src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp index 94f9c0a7..9f601657 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp @@ -40,9 +40,9 @@ bool ConfigurationContainerFlash::load() { } if (!file.available()) { - Serial.print(F("[Configuration] Unable to initialize: empty file\n")); + Serial.print(F("[Configuration] Populate FS: create configuration file\n")); file.close(); - return false; + return true; } int file_size = file.size(); From 830ec1a5af825abfd37e71e3c680d55efaf0a55a Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 6 Oct 2021 17:09:36 +0200 Subject: [PATCH 067/696] FW management complete --- src/ArduinoOcpp.cpp | 18 +++++++++++++ .../FirmwareStatusNotification.cpp | 3 +-- .../FirmwareManagement/FirmwareService.cpp | 26 ++++++++++++++++--- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index fd255538..d875a416 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,7 @@ bool connectorEnergizedLastState = false; SmartChargingService *smartChargingService; ChargePointStatusService *chargePointStatusService; HeartbeatService *heartbeatService; +FirmwareService *firmwareService = NULL; OnLimitChange onLimitChange; OcppTime *ocppTime; @@ -156,6 +158,13 @@ void OCPP_initialize(OcppSocket *ocppSocket, float V_eff, ArduinoOcpp::Filesyste meteringService = new MeteringService(OCPP_NUMCONNECTORS, ocppTime); heartbeatService = new HeartbeatService(); +#if !defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) + firmwareService = EspWiFi::makeFirmwareService("12345789"); //instantiate FW service + ESP installation routine +#else + firmwareService = new FirmwareService("12345678"); //only instantiate FW service +#endif + setFirmwareService(firmwareService); + OCPP_initialized = true; } @@ -189,6 +198,10 @@ void OCPP_loop() { heartbeatService->loop(); + if (firmwareService != NULL) { + firmwareService->loop(); + } + bool evRequestsEnergyNewState = true; if (evRequestsEnergySampler != NULL) { evRequestsEnergyNewState = evRequestsEnergySampler(); @@ -428,4 +441,9 @@ bool existsUnboundIdTag() { return chargePointStatusService->existsUnboundAuthorization(); } +bool isAvailable() { + return (chargePointStatusService->getConnector(OCPP_ID_OF_CP)->getAvailability() != AVAILABILITY_INOPERATIVE) + && (chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->getAvailability() != AVAILABILITY_INOPERATIVE); +} + #endif diff --git a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp index 133d3218..8920d8ce 100644 --- a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp @@ -9,7 +9,6 @@ using ArduinoOcpp::Ocpp16::FirmwareStatusNotification; FirmwareStatusNotification::FirmwareStatusNotification() { - //status = String("Idle"); // TriggerMessage will use this constructor. Should replace with actual value later FirmwareService *fwService = getFirmwareService(); if (fwService) { status = fwService->getFirmwareStatus(); @@ -18,7 +17,7 @@ FirmwareStatusNotification::FirmwareStatusNotification() { } } -FirmwareStatusNotification::FirmwareStatusNotification(FirmwareStatus status) { +FirmwareStatusNotification::FirmwareStatusNotification(FirmwareStatus status) : status(status) { } const char *FirmwareStatusNotification::cstrFromFwStatus(FirmwareStatus status) { diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index e861a9c5..f8486d7d 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -31,6 +31,7 @@ void FirmwareService::loop() { //if (!downloadIssued) { if (stage == UpdateStage::Idle) { + if (DEBUG_OUT) Serial.println(F("[FirmwareService] Start update!")); if (onDownload == NULL) { stage = UpdateStage::AfterDownload; } else { @@ -44,6 +45,7 @@ void FirmwareService::loop() { //if (!onDownloadCalled) { if (stage == UpdateStage::AwaitDownload) { + if (DEBUG_OUT) Serial.println(F("[FirmwareService] Start Download!")); stage = UpdateStage::Downloading; if (onDownload != NULL) { onDownload(location); @@ -88,6 +90,7 @@ void FirmwareService::loop() { //if (!installationIssued) { if (stage == UpdateStage::AfterDownload) { + if (DEBUG_OUT) Serial.println(F("[FirmwareService] After download!")); bool ongoingTx = false; ChargePointStatusService *cpStatus = getChargePointStatusService(); if (cpStatus) { @@ -112,6 +115,7 @@ void FirmwareService::loop() { } if (stage == UpdateStage::AwaitInstallation) { + if (DEBUG_OUT) Serial.println(F("[FirmwareService] Installing!")); stage = UpdateStage::Installing; if (onInstall != NULL) { @@ -174,6 +178,20 @@ void FirmwareService::scheduleFirmwareUpdate(String &location, OcppTimestamp ret this->retries = retries; this->retryInterval = retryInterval; + if (DEBUG_OUT) { + Serial.print(F("[FirmwareService] Scheduled FW update!\n")); + Serial.print(F(" location = ")); + Serial.println(this->location); + Serial.print(F(" retrieveDate = ")); + char dbuf [JSONDATE_LENGTH + 1] = {'\0'}; + this->retreiveDate.toJsonString(dbuf, JSONDATE_LENGTH + 1); + Serial.println(dbuf); + Serial.print(F(" retries = ")); + Serial.print(this->retries); + Serial.print(F(", retryInterval = ")); + Serial.println(this->retryInterval); + } + resetStage(); } @@ -213,9 +231,9 @@ OcppOperation *FirmwareService::getFirmwareStatusNotification() { checkedSuccessfulFwUpdate = true; size_t buildNoSize = previousBuildNumber->getBuffsize(); - if (!strncmp(buildNumber, *previousBuildNumber, buildNoSize)) { + if (strncmp(buildNumber, *previousBuildNumber, buildNoSize)) { //new FW - previousBuildNumber->setValue(buildNumber, strlen(buildNumber)); + previousBuildNumber->setValue(buildNumber, strlen(buildNumber) + 1); configuration_save(); lastReportedStatus = FirmwareStatus::Installed; @@ -264,7 +282,7 @@ void FirmwareService::resetStage() { #include -FirmwareService *makeFirmwareService(const char *buildNumber) { +FirmwareService *EspWiFi::makeFirmwareService(const char *buildNumber) { FirmwareService *fwService = new FirmwareService(buildNumber); /* @@ -326,7 +344,7 @@ FirmwareService *makeFirmwareService(const char *buildNumber) { #include -FirmwareService *makeFirmwareService(const char *buildNumber) { +FirmwareService *EspWiFi::makeFirmwareService(const char *buildNumber) { FirmwareService *fwService = new FirmwareService(buildNumber); fwService->setOnInstall([fwService = fwService] (String &location) { From 264e5f09dc562671be1a818913d649317e40adc9 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 6 Oct 2021 17:11:40 +0200 Subject: [PATCH 068/696] UpdateFirmware adaptations --- src/ArduinoOcpp.h | 2 + .../MessagesV16/UpdateFirmware.cpp | 51 ++++++++++++------- src/ArduinoOcpp/MessagesV16/UpdateFirmware.h | 6 ++- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index fd7b9fca..ab6b8088 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -136,5 +136,7 @@ int getTransactionId(); //returns the ID of the current transaction. Returns -1 bool existsUnboundIdTag(); //returns if the user has given a valid Ocpp Charging Card which is not used for a transaction yet +bool isAvailable(); //if the charge point is operative or inoperative + #endif #endif diff --git a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp index b46aac1b..9464d3ea 100644 --- a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp +++ b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp @@ -3,7 +3,7 @@ // MIT License #include -#include +#include using ArduinoOcpp::Ocpp16::UpdateFirmware; @@ -13,28 +13,41 @@ UpdateFirmware::UpdateFirmware() { void UpdateFirmware::processReq(JsonObject payload) { /* - * Process the application data here. Note: you have to implement the FW update procedure in your client code. You have to set - * a onSendConfListener in which you initiate a FW update (e.g. calling ESPhttpUpdate.update(...) ) - */ - - //check location URL. Maybe introduce Same-Origin-Policy? - if (!payload["location"]) { - formatError = true; - } - - //check the integrity of retrieveDate - const char *retrieveDate = payload["retrieveDate"] | "Invalid"; - - OcppTimestamp parser = OcppTimestamp(); - - if (!parser.setTime(retrieveDate)) { - formatError = true; - } + * Process the application data here. Note: you have to implement the FW update procedure in your client code. You have to set + * a onSendConfListener in which you initiate a FW update (e.g. calling ESPhttpUpdate.update(...) ) + */ + + const char *loc = payload["location"] | ""; + location = String(loc); + //check location URL. Maybe introduce Same-Origin-Policy? + if (location.isEmpty()) { + formatError = true; + Serial.println(F("[UpdateFirmware] Could not read location. Abort")); + return; + } + + //check the integrity of retrieveDate + const char *retrieveDateRaw = payload["retrieveDate"] | "Invalid"; + if (!retreiveDate.setTime(retrieveDateRaw)) { + formatError = true; + Serial.println(F("[UpdateFirmware] Could not read retrieveDate. Abort")); + return; + } + + retries = payload["retries"] | 1; + retryInterval = payload["retryInterval"] | 180; } DynamicJsonDocument* UpdateFirmware::createConf(){ DynamicJsonDocument* doc = new DynamicJsonDocument(0); JsonObject payload = doc->to(); - + + FirmwareService *fwService = getFirmwareService(); + if (fwService != NULL) { + fwService->scheduleFirmwareUpdate(location, retreiveDate, retries, retryInterval); + } else { + Serial.println(F("[UpdateFirmware] FirmwareService has not been initialized before! Please have a look at ArduinoOcpp.cpp for an example. Abort")); + } + return doc; } \ No newline at end of file diff --git a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h index df9c3750..fcb86634 100644 --- a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h +++ b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h @@ -6,13 +6,17 @@ #define UPDATEFIRMWARE_H #include +#include namespace ArduinoOcpp { namespace Ocpp16 { class UpdateFirmware : public OcppMessage { private: - const char *location = "Invalid"; + String location = String('\0'); + OcppTimestamp retreiveDate = OcppTimestamp(); + int retries = 1; + ulong retryInterval = 180; bool formatError = false; public: UpdateFirmware(); From 6c1ed0703e73d849b615782b4ab14bfbe781288b Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 6 Oct 2021 17:14:34 +0200 Subject: [PATCH 069/696] added ConnectorAvailability --- README.md | 2 +- .../MessagesV16/ChangeAvailability.cpp | 70 +++++++++++++++++++ .../MessagesV16/ChangeAvailability.h | 29 ++++++++ .../ChargePointStatus/ConnectorStatus.cpp | 45 ++++++++++-- .../Tasks/ChargePointStatus/ConnectorStatus.h | 8 +++ .../Tasks/ChargePointStatus/OcppEvseState.h | 4 +- 6 files changed, 149 insertions(+), 9 deletions(-) create mode 100644 src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp create mode 100644 src/ArduinoOcpp/MessagesV16/ChangeAvailability.h diff --git a/README.md b/README.md index 3ed9a520..c2e55382 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ In case you use PlatformIO, you can copy all dependencies from `platformio.ini` | **Core profile** | | `Authorize` | :heavy_check_mark: | | `BootNotification` | :heavy_check_mark: | -| `ChangeAvailability` | | | :heavy_multiplication_x: | +| `ChangeAvailability` | :heavy_check_mark: | | `ChangeConfiguration` | :heavy_check_mark: | | `ClearCache` | | | :heavy_multiplication_x: | | `DataTransfer` | :heavy_check_mark: | diff --git a/src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp b/src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp new file mode 100644 index 00000000..54b6be68 --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp @@ -0,0 +1,70 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include +#include + +#include + +using ArduinoOcpp::Ocpp16::ChangeAvailability; + +ChangeAvailability::ChangeAvailability() { + +} + +const char* ChangeAvailability::getOcppOperationType(){ + return "ChangeAvailability"; +} + +void ChangeAvailability::processReq(JsonObject payload) { + int connectorId = payload["connectorId"] | -1; + ChargePointStatusService *cpStatus = getChargePointStatusService(); + if (!cpStatus) { + return; + } + if (connectorId < 0 || connectorId >= cpStatus->getNumConnectors()) { + return; + } + + const char *type = payload["type"] | "INVALID"; + bool available = false; + + if (!strcmp(type, "Operative")) { + accepted = true; + available = true; + } else if (!strcmp(type, "Inoperative")) { + accepted = true; + available = false; + } else { + return; + } + + if (connectorId == 0) { + for (int i = 0; i < cpStatus->getNumConnectors(); i++) { + cpStatus->getConnector(i)->setAvailability(available); + if (cpStatus->getConnector(i)->getAvailability() == AVAILABILITY_INOPERATIVE_SCHEDULED) { + scheduled = true; + } + } + } else { + cpStatus->getConnector(connectorId)->setAvailability(available); + if (cpStatus->getConnector(connectorId)->getAvailability() == AVAILABILITY_INOPERATIVE_SCHEDULED) { + scheduled = true; + } + } +} + +DynamicJsonDocument* ChangeAvailability::createConf(){ + DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); + JsonObject payload = doc->to(); + if (!accepted) { + payload["status"] = "Rejected"; + } else if (scheduled) { + payload["status"] = "Scheduled"; + } else { + payload["status"] = "Accepted"; + } + + return doc; +} diff --git a/src/ArduinoOcpp/MessagesV16/ChangeAvailability.h b/src/ArduinoOcpp/MessagesV16/ChangeAvailability.h new file mode 100644 index 00000000..26b4ffa0 --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/ChangeAvailability.h @@ -0,0 +1,29 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef CHANGEAVAILABILITY_H +#define CHANGEAVAILABILITY_H + +#include + +namespace ArduinoOcpp { +namespace Ocpp16 { + +class ChangeAvailability : public OcppMessage { +private: + bool scheduled = false; + bool accepted = false; +public: + ChangeAvailability(); + + const char* getOcppOperationType(); + + void processReq(JsonObject payload); + + DynamicJsonDocument* createConf(); +}; + +} //end namespace Ocpp16 +} //end namespace ArduinoOcpp +#endif diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index b8f3046b..36489d78 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -14,12 +14,15 @@ using namespace ArduinoOcpp::Ocpp16; ConnectorStatus::ConnectorStatus(int connectorId, OcppTime *ocppTime) : connectorId(connectorId), ocppTime(ocppTime) { //Set default transaction ID in memory - String key = "OCPP_STATE_TRANSACTION_ID_CONNECTOR_"; + String keyTx = "OCPP_STATE_TRANSACTION_ID_CONNECTOR_"; + String keyAvailability = "OCPP_STATE_AVAILABILITY_CONNECTOR_"; String id = String(connectorId, DEC); - key += id; - transactionId = declareConfiguration(key.c_str(), -1, CONFIGURATION_FN, false, false, true, false); - if (!transactionId) { - Serial.print(F("[ConnectorStatus] Error! Cannot declare transactionId\n")); + keyTx += id; + keyAvailability += id; + transactionId = declareConfiguration(keyTx.c_str(), -1, CONFIGURATION_FN, false, false, true, false); + availability = declareConfiguration(keyAvailability.c_str(), AVAILABILITY_OPERATIVE, CONFIGURATION_FN, false, false, true, false); + if (!transactionId || !availability) { + Serial.print(F("[ConnectorStatus] Error! Cannot declare transactionId or availability!\n")); } transactionIdSync = *transactionId; } @@ -29,7 +32,13 @@ OcppEvseState ConnectorStatus::inferenceStatus() { * Handle special case: This is the ConnectorStatus for the whole CP (i.e. connectorId=0) --> only states Available, Unavailable, Faulted are possible */ if (connectorId == 0) { - return OcppEvseState::Available; //no support for Unavailable or Faulted at the moment + if (getErrorCode() != NULL) { + return OcppEvseState::Faulted; + } else if (*availability == AVAILABILITY_INOPERATIVE) { + return OcppEvseState::Unavailable; + } else { + return OcppEvseState::Available; + } } // if (!authorized && !getChargePointStatusService()->existsUnboundAuthorization()) { @@ -39,6 +48,8 @@ OcppEvseState ConnectorStatus::inferenceStatus() { //if (connectorFaultedSampler != NULL && connectorFaultedSampler()) { if (getErrorCode() != NULL) { return OcppEvseState::Faulted; + } else if (*availability == AVAILABILITY_INOPERATIVE) { + return OcppEvseState::Unavailable; } else if (!authorized && !getChargePointStatusService()->existsUnboundAuthorization() && ((int) *transactionId) < 0 && (connectorPluggedSampler == NULL || !connectorPluggedSampler()) ) { @@ -58,6 +69,11 @@ OcppEvseState ConnectorStatus::inferenceStatus() { } StatusNotification *ConnectorStatus::loop() { + if (getTransactionId() <= 0 && *availability == AVAILABILITY_INOPERATIVE_SCHEDULED) { + *availability = AVAILABILITY_INOPERATIVE; + saveState(); + } + OcppEvseState inferencedStatus = inferenceStatus(); if (inferencedStatus != currentStatus) { @@ -130,6 +146,23 @@ void ConnectorStatus::setTransactionId(int id) { saveState(); } +int ConnectorStatus::getAvailability() { + return *availability; +} + +void ConnectorStatus::setAvailability(bool available) { + if (available) { + *availability = AVAILABILITY_OPERATIVE; + } else { + if (getTransactionId() > 0) { + *availability = AVAILABILITY_INOPERATIVE_SCHEDULED; + } else { + *availability = AVAILABILITY_INOPERATIVE; + } + } + saveState(); +} + void ConnectorStatus::startEvDrawsEnergy(){ if (evDrawsEnergy == true){ if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: startEvDrawsEnergy called twice or didn't call stopEvDrawsEnergy before\n")); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index 04ef0db8..944e99e9 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -9,6 +9,10 @@ #include #include +#define AVAILABILITY_OPERATIVE 2 +#define AVAILABILITY_INOPERATIVE_SCHEDULED 1 +#define AVAILABILITY_INOPERATIVE 0 + namespace ArduinoOcpp { class ConnectorStatus { @@ -16,6 +20,8 @@ class ConnectorStatus { const int connectorId; OcppTime *ocppTime; + std::shared_ptr> availability = NULL; + bool authorized = false; String idTag = String('\0'); bool transactionRunning = false; @@ -43,6 +49,8 @@ class ConnectorStatus { uint16_t getTransactionWriteCount(); void setTransactionId(int id); void setTransactionIdSync(int id); + int getAvailability(); + void setAvailability(bool available); void boot(); void startEvDrawsEnergy(); void stopEvDrawsEnergy(); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h index e05f79b8..538b00d3 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h @@ -15,8 +15,8 @@ enum class OcppEvseState { SuspendedEV, Finishing, //not supported by this client Reserved, //not supported by this client - Unavailable, //not supported by this client - Faulted, //not supported by this client + Unavailable, + Faulted, NOT_SET //not part of OCPP 1.6 }; From 532f9e6ec0111d7821964d61dff1eaac3fe63978 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 6 Oct 2021 17:16:16 +0200 Subject: [PATCH 070/696] fixed include --- src/ArduinoOcpp/Core/Configuration.cpp | 8 +++++++- src/ArduinoOcpp/SimpleOcppOperationFactory.cpp | 7 +++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index 6f59d9c2..63f66455 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -219,8 +219,13 @@ std::shared_ptr>> getAllConfi } //end namespace Ocpp16 +bool configuration_inited = false; + bool configuration_init(FilesystemOpt fsOpt) { - bool loadRoutineSuccessful = false; + if (configuration_inited) + return true; //configuration_init() already called; tolerate multiple calls so user can use this store for + //credentials outside ArduinoOcpp which need to be loaded before OCPP_initialize() + bool loadRoutineSuccessful = true; #ifndef AO_DEACTIVATE_FLASH configurationFilesystemOpt = fsOpt; @@ -267,6 +272,7 @@ bool configuration_init(FilesystemOpt fsOpt) { #endif //ndef AO_DEACTIVATE_FLASH + configuration_inited = loadRoutineSuccessful; return loadRoutineSuccessful; } diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 50126724..7dee358e 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -232,8 +233,8 @@ OcppOperation *makeOcppOperation(const char *messageType) { operation->setOnSendConfListener(onResetSendConf); } else if (!strcmp(messageType, "UpdateFirmware")) { msg = new Ocpp16::UpdateFirmware(); - if (onUpdateFirmwareReceiveReq == NULL) - Serial.print(F("[SimpleOcppOperationFactory] Warning: UpdateFirmware is without effect when the receiveReq listener is not set. Please implement a FW update routine for your device.\n")); + //if (onUpdateFirmwareReceiveReq == NULL) + // Serial.print(F("[SimpleOcppOperationFactory] Warning: UpdateFirmware is without effect when the receiveReq listener is not set. Please implement a FW update routine for your device.\n")); operation->setOnReceiveReqListener(onUpdateFirmwareReceiveReq); } else if (!strcmp(messageType, "FirmwareStatusNotification")) { msg = new Ocpp16::FirmwareStatusNotification(); @@ -241,6 +242,8 @@ OcppOperation *makeOcppOperation(const char *messageType) { msg = new Ocpp16::UnlockConnector(); } else if (!strcmp(messageType, "ClearChargingProfile")) { msg = new Ocpp16::ClearChargingProfile(); + } else if (!strcmp(messageType, "ChangeAvailability")) { + msg = new Ocpp16::ChangeAvailability(); } else { Serial.println(F("[SimpleOcppOperationFactory] Operation not supported")); msg = new NotImplemented(); From 3a78f31e258fd992c3b28d6709ff844ad9784f17 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 13 Oct 2021 13:01:40 +0200 Subject: [PATCH 071/696] make EVSE unavailable during FW update --- .../FirmwareManagement/FirmwareService.cpp | 21 ++++++++++++------- .../FirmwareManagement/FirmwareService.h | 2 ++ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index f8486d7d..b13a24c7 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -104,6 +104,11 @@ void FirmwareService::loop() { } if (!ongoingTx) { + if (cpStatus) { + ConnectorStatus *evse = cpStatus->getConnector(0); + availabilityRestore = (evse->getAvailability() == AVAILABILITY_OPERATIVE); + evse->setAvailability(false); + } stage = UpdateStage::AwaitInstallation; installationIssued = true; @@ -132,17 +137,17 @@ void FirmwareService::loop() { if (stage == UpdateStage::Installing) { //check if client reports installation to be finished - if (installationStatusSampler != NULL && installationStatusSampler() == InstallationStatus::Installed) { + if ((installationStatusSampler != NULL && installationStatusSampler() == InstallationStatus::Installed) + //if client doesn't report installation state, assume download to be finished (at least 40s installation time have passed until here) + || (installationStatusSampler == NULL)) { //Client should reboot during onInstall. If not, client is responsible to reboot at a later point - resetStage(); - retries = 0; //Success. End of update routine - return; - } - - //if client doesn't report installation state, assume download to be finished (at least 40s installation time have passed until here) - if (installationStatusSampler == NULL) { resetStage(); retries = 0; //End of update routine. Client must reboot on its own + if (availabilityRestore) { + if (getChargePointStatusService() && getChargePointStatusService()->getConnector(0)) { + getChargePointStatusService()->getConnector(0)->setAvailability(true); + } + } return; } diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h index 641b2fe4..6b7247cf 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h @@ -64,6 +64,8 @@ class FirmwareService { void resetStage(); + bool availabilityRestore = false; + public: FirmwareService(const char *buildNumber); From 50a948a6fe1b42a5bb802186b527ad931e9b6673 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 13 Oct 2021 13:09:27 +0200 Subject: [PATCH 072/696] update examples --- examples/EVCC/main.ino | 478 ++++++++++++++++++ .../OneConnector_EVSE.ino | 107 ---- .../OneConnector_HW_integration.h | 18 - .../Simulation_without_HW/Simulated_HW.ino | 123 ----- 4 files changed, 478 insertions(+), 248 deletions(-) create mode 100644 examples/EVCC/main.ino delete mode 100644 examples/Simulation_without_HW/OneConnector_EVSE.ino delete mode 100644 examples/Simulation_without_HW/OneConnector_HW_integration.h delete mode 100644 examples/Simulation_without_HW/Simulated_HW.ino diff --git a/examples/EVCC/main.ino b/examples/EVCC/main.ino new file mode 100644 index 00000000..39e15890 --- /dev/null +++ b/examples/EVCC/main.ino @@ -0,0 +1,478 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License +// +// +// IMPORTANT: Please add all files from this example folder to your project! +// +// This is a complete EVCC (Electric Vehicle Communications Controller) codebase. An EVCC +// is responsible for all communication over the internet, i.e. to your OCPP server. You +// can flash this program onto an ESP32, build it into your EVSE and start charging +// with OCPP-connectivity. +// +// The pin mapping and additional comments can be found in this file. Maybe you need to +// adapt a few things before deployment. +// + + +#include +#include //please install tzapu/WiFiManager + +#include +#include //load and save settings of WiFi captive portal + +/* + * Pin mapping part I + * Interface to the SECC (Supply Equipment Communication Controller, e.g. the SAE J1772 module) + */ +#define AMPERAGE_PIN 4 //modulated as PWM + +#define EV_PLUG_PIN 14 +#if defined(ESP32) +#define EV_PLUGGED HIGH +#define EV_UNPLUGGED LOW +#elif defined(ESP8266) +#define EV_PLUGGED LOW +#define EV_UNPLUGGED HIGH +#endif + +#define OCPP_CHARGE_PERMISSION_PIN 5 +#define OCPP_CHARGE_PERMITTED HIGH +#define OCPP_CHARGE_FORBIDDEN LOW + +#define EV_CHARGE_PIN 12 +#if defined(ESP32) +#define EV_CHARGING LOW +#define EV_SUSPENDED HIGH +#elif defined(ESP8266) +#define EV_CHARGING HIGH +#define EV_SUSPENDED LOW +#endif + +#define OCPP_AVAILABILITY_PIN 0 +#define OCPP_AVAILABLE HIGH +#define OCPP_UNAVAILABLE LOW + +#define EVSE_GROUND_FAULT_PIN 13 +#define EVSE_GROUND_FAULTED HIGH +#define EVSE_GROUND_CLEAR LOW + +/* + * Pin mapping part II + * Internal LED of ESP8266/ESP32 + additional ESP8266 development board LED + */ + +#define SERVER_CONNECT_LED 16 +#define SERVER_CONNECT_ON LOW +#define SERVER_CONNECT_OFF HIGH + +#define CHARGE_PERMISSION_LED 2 +#if defined(ESP32) +#define CHARGE_PERMISSION_ON HIGH +#define CHARGE_PERMISSION_OFF LOW +#elif defined(ESP8266) +#define CHARGE_PERMISSION_ON LOW +#define CHARGE_PERMISSION_OFF HIGH +#endif + +#if DEBUG_OUT +#define PRINT(...) Serial.print(__VA_ARGS__) +#define PRINTLN(...) Serial.println(__VA_ARGS__) +#endif + +int evPlugged = EV_UNPLUGGED; + +bool booted = false; +ulong scheduleReboot = 0; //0 = no reboot scheduled; otherwise reboot scheduled in X ms +ulong reboot_timestamp = 0; //timestamp of the triggering event; if scheduleReboot=0, the timestamp has no meaning + +// ============ CAPTIVE PORTAL +#define CAPTIVE_PORTAL_TIMEOUT 30000 + +struct Ocpp_URL { + bool isTLS = true; + String host = String('\0'); + uint16_t port = 443; + String url = String('\0'); + bool parse(String &url); +}; + +Ocpp_URL ocppUrlParsed = Ocpp_URL(); + +bool runWiFiManager(); +// ============ END CAPTIVE PORTAL + +void setup() { + + /* + * Initialize peripherals + */ + Serial.begin(115200); + Serial.setDebugOutput(true); + pinMode(EV_PLUG_PIN, INPUT); + pinMode(EV_CHARGE_PIN, INPUT); + pinMode(EVSE_GROUND_FAULT_PIN, INPUT); + pinMode(OCPP_CHARGE_PERMISSION_PIN, OUTPUT); + digitalWrite(OCPP_CHARGE_PERMISSION_PIN, OCPP_CHARGE_FORBIDDEN); + pinMode(OCPP_AVAILABILITY_PIN, OUTPUT); + digitalWrite(OCPP_AVAILABILITY_PIN, OCPP_UNAVAILABLE); + pinMode(CHARGE_PERMISSION_LED, OUTPUT); + digitalWrite(CHARGE_PERMISSION_LED, CHARGE_PERMISSION_OFF); + + pinMode(AMPERAGE_PIN, OUTPUT); +#if defined(ESP32) + pinMode(AMPERAGE_PIN, OUTPUT); + ledcSetup(0, 1000, 8); //channel=0, freq=1000Hz, range=(2^8)-1 + ledcAttachPin(AMPERAGE_PIN, 0); + ledcWrite(AMPERAGE_PIN, 256); //256 is constant +3.3V DC +#elif defined(ESP8266) + analogWriteRange(255); //range=(2^8)-1 + analogWriteFreq(1000); //freq=1000Hz + analogWrite(AMPERAGE_PIN, 256); //256 is constant +3.3V DC +#else +#error Can only run on ESP8266 and ESP32 on the Arduino platform! +#endif + + pinMode(SERVER_CONNECT_LED, OUTPUT); + digitalWrite(SERVER_CONNECT_LED, SERVER_CONNECT_ON); //signal device reboot + delay(100); + digitalWrite(SERVER_CONNECT_LED, SERVER_CONNECT_OFF); + + /* + * You can use ArduinoOcpp's internal configurations store for credentials other than those which are + * specified by OCPP. To use it before ArduinoOcpp is initialized, you need to call configuration_init() + */ + ArduinoOcpp::configuration_init(ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail); + + /* + * WiFiManager opens a captive portal, lets the user enter the WiFi credentials and provides a settings + * page for the OCPP connection + */ + if (!runWiFiManager()) { + //couldn't connect + PRINTLN(F("[main] Couldn't connect to WiFi after multiple attempts")); + delay(30000); + ESP.restart(); + } + + std::shared_ptr> ocppUrl = ArduinoOcpp::declareConfiguration( + "ocppUrl", "", CONFIGURATION_FN, false, false, true, true); + std::shared_ptr> CA_cert = ArduinoOcpp::declareConfiguration( + "CA_cert", "", CONFIGURATION_FN, false, false, true, true); + std::shared_ptr> httpAuthentication = ArduinoOcpp::declareConfiguration( + "httpAuthentication", "", CONFIGURATION_FN, false, false, true, true); + + String ocppUrlString = String(*ocppUrl); + if (!ocppUrlParsed.parse(ocppUrlString)) { + PRINTLN(F("[main] Invalid OCPP URL. Restart.")); + delay(10000); + ESP.restart(); + } + + PRINT(F("[main] host, port, URL: ")); + PRINT(ocppUrlParsed.host); + PRINT(F(", ")); + PRINT(ocppUrlParsed.port); + PRINT(F(", ")); + PRINTLN(ocppUrlParsed.url); + + /* + * Initialize ArduinoOcpp framework. + */ + WebSocketsClient *wSock = new WebSocketsClient(); + wSock->setReconnectInterval(5000); + wSock->enableHeartbeat(15000, 3000, 2); + + if (httpAuthentication->getBuffsize() > 1) { + wSock->setAuthorization(*httpAuthentication); + } + + if (ocppUrlParsed.isTLS) { + if (CA_cert->getBuffsize() > 0) { + wSock->beginSslWithCA(ocppUrlParsed.host.c_str(), ocppUrlParsed.port, ocppUrlParsed.url.c_str(), *CA_cert, "ocpp1.6"); + } else { + wSock->beginSSL(ocppUrlParsed.host, ocppUrlParsed.port, ocppUrlParsed.url, "", "ocpp1.6"); + } + } else { + wSock->begin(ocppUrlParsed.host, ocppUrlParsed.port, ocppUrlParsed.url, "ocpp1.6"); + } + + ArduinoOcpp::EspWiFi::OcppClientSocket *oSock = new ArduinoOcpp::EspWiFi::OcppClientSocket(wSock); + OCPP_initialize(oSock, + /* Grid voltage */ 230.f, + ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, + ArduinoOcpp::Clocks::DEFAULT_CLOCK); + + /* + * Integrate OCPP functionality. You can leave out the following part if your EVSE doesn't need it. + */ + setEnergyActiveImportSampler([]() { + //read the energy input register of the EVSE here and return the value in Wh + /* + * Approximated value. TODO: Replace with real reading + */ + static ulong lastSampled = millis(); + static float energyMeter = 0.f; + if (getTransactionId() > 0 && digitalRead(EV_CHARGE_PIN) == EV_CHARGING) + energyMeter += ((float) millis() - lastSampled) * 0.003f; //increase by 0.003Wh per ms (~ 10.8kWh per h) + lastSampled = millis(); + return energyMeter; + }); + + setOnChargingRateLimitChange([](float limit) { + //set the SAE J1772 Control Pilot value here + PRINT(F("[main] Smart Charging allows maximum charge rate: ")); + PRINTLN(limit); + float amps = limit / 230.f; + if (amps > 51.f) + amps = 51.f; + + int pwmVal; + if (amps < 6.f) { + pwmVal = 256; // = constant +3.3V DC + } else { + pwmVal = (int) (4.26667f * amps); + } + +#if defined(ESP32) + ledcWrite(AMPERAGE_PIN, pwmVal); +#elif defined(ESP8266) + analogWrite(AMPERAGE_PIN, pwmVal); +#else +#endif + }); + + setEvRequestsEnergySampler([]() { + //return true if the EV is in state "Ready for charging" (see https://en.wikipedia.org/wiki/SAE_J1772#Control_Pilot) + return digitalRead(EV_CHARGE_PIN) == EV_CHARGING; + }); + + addConnectorErrorCodeSampler([] () { +// if (digitalRead(EVSE_GROUND_FAULT_PIN) != EVSE_GROUND_CLEAR) { +// return "GroundFault"; +// } else { + return (const char *) NULL; +// } + }); + + setOnRemoteStartTransactionSendConf([] (JsonObject payload) { + if (!strcmp(payload["status"], "Accepted")) + startTransaction(); + }); + + setOnRemoteStopTransactionSendConf([] (JsonObject payload) { + stopTransaction(); + }); + + setOnResetSendConf([] (JsonObject payload) { + if (getTransactionId() >= 0) + stopTransaction(); + + reboot_timestamp = millis(); + scheduleReboot = 5000; + booted = false; + }); + + //... see ArduinoOcpp.h for more settings + + /* + * Notify the Central System that this station is ready + */ + bootNotification("My Charging Station", "My company name", [] (JsonObject payload) { + const char *status = payload["status"] | "INVALID"; + if (!strcmp(status, "Accepted")) { + booted = true; + digitalWrite(SERVER_CONNECT_LED, SERVER_CONNECT_ON); + } else { + //retry sending the BootNotification + delay(60000); + ESP.restart(); + } + }); +} + +void loop() { + + /* + * Do all OCPP stuff (process WebSocket input, send recorded meter values to Central System, etc.) + */ + OCPP_loop(); + + /* + * Detect if something physical happened at your EVSE and trigger the corresponding OCPP messages + */ + if (/* RFID chip detected? */ false) { + String idTag = "my-id-tag"; //e.g. idTag = RFID.readIdTag(); + authorize(idTag); + } + + if (getTransactionId() > 0) { + digitalWrite(OCPP_CHARGE_PERMISSION_PIN, OCPP_CHARGE_PERMITTED); + digitalWrite(CHARGE_PERMISSION_LED, CHARGE_PERMISSION_ON); + } else { + digitalWrite(OCPP_CHARGE_PERMISSION_PIN, OCPP_CHARGE_FORBIDDEN); + digitalWrite(CHARGE_PERMISSION_LED, CHARGE_PERMISSION_OFF); + } + + if (!booted) + return; + if (scheduleReboot > 0 && millis() - reboot_timestamp >= scheduleReboot) { + ESP.restart(); + } + + if (digitalRead(EV_PLUG_PIN) == EV_PLUGGED && evPlugged == EV_UNPLUGGED && getTransactionId() >= 0) { + //transition unplugged -> plugged; Case A: transaction has already been initiated + evPlugged = EV_PLUGGED; + } else if (digitalRead(EV_PLUG_PIN) == EV_PLUGGED && evPlugged == EV_UNPLUGGED && isAvailable()) { + //transition unplugged -> plugged; Case B: no transaction running; start transaction + evPlugged = EV_PLUGGED; + + startTransaction(); + } else if (digitalRead(EV_PLUG_PIN) == EV_UNPLUGGED && evPlugged == EV_PLUGGED) { + //transition plugged -> unplugged + evPlugged = EV_UNPLUGGED; + + if (getTransactionId() >= 0) + stopTransaction(); + } + + //... see ArduinoOcpp.h for more possibilities +} + +bool runWiFiManager() { + + /* + * Initialize WiFi and start captive portal to set connection credentials + */ + WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP + + std::shared_ptr> ocppUrl = ArduinoOcpp::declareConfiguration( + "ocppUrl", "", CONFIGURATION_FN, false, false, true, true); + std::shared_ptr> CA_cert = ArduinoOcpp::declareConfiguration( + "CA_cert", "", CONFIGURATION_FN, false, false, true, true); + std::shared_ptr> httpAuthentication = ArduinoOcpp::declareConfiguration( + "httpAuthentication", "", CONFIGURATION_FN, false, false, true, true); + + WiFiManager wifiManager; + wifiManager.setTitle("EVSE configuration portal"); + wifiManager.setParamsPage(true); + WiFiManagerParameter ocppUrlParam ("ocppUrl", "OCPP 1.6 Server URL + Charge Box ID", *ocppUrl, 150,"placeholder=\"wss://<domain>:<port>/<path>/<chargeBoxId>\""); + wifiManager.addParameter(&ocppUrlParam); + + WiFiManagerParameter divider("

"); + wifiManager.addParameter(÷r); + + WiFiManagerParameter caCertParam ("caCert", "CA Certificate", "", 150,"placeholder=\"Paste here or leave blank\""); + wifiManager.addParameter(&caCertParam); + WiFiManagerParameter caCertSavePararm ("
"); + wifiManager.addParameter(&caCertSavePararm); + + WiFiManagerParameter httpAuthenticationParam ("httpAuthentication", "HTTP-authentication token", "", 150,"placeholder=\"e.g.: aKu0Q4NjMRC9yO3wRo08\""); + wifiManager.addParameter(&httpAuthenticationParam); + WiFiManagerParameter httpAuthenticationSavePararm ("
"); + wifiManager.addParameter(&httpAuthenticationSavePararm); + + wifiManager.setSaveParamsCallback([&ocppUrlParam = ocppUrlParam, &ocppUrl = ocppUrl, &wifiManager = wifiManager, &CA_cert = CA_cert, &caCertParam = caCertParam, &httpAuthentication = httpAuthentication, &httpAuthenticationParam = httpAuthenticationParam] () { + String newOcppUrl = String(ocppUrlParam.getValue()); + newOcppUrl.trim(); + Ocpp_URL newOcppUrlParsed = Ocpp_URL(); + if (newOcppUrlParsed.parse(newOcppUrl)) { + //success + *ocppUrl = newOcppUrl.c_str(); + } + + bool caCertSave = false; + String caCertChangedParam = String ("caCertSave"); + if(wifiManager.server->hasArg(caCertChangedParam)) { + String caCertChanged = String(wifiManager.server->arg(caCertChangedParam)); + if (caCertChanged.length() > 0) { + caCertSave = caCertChanged.charAt(0) == '1' || caCertChanged.charAt(0) == 't' || caCertChanged.charAt(0) == 'o'; + } + } + if (caCertSave) { + *CA_cert = caCertParam.getValue(); + } + + bool httpAuthenticationSave = false; + String authChangedParam = String ("httpAuthenticationSave"); + if(wifiManager.server->hasArg(authChangedParam)) { + String authChanged = String(wifiManager.server->arg(authChangedParam)); + if (authChanged.length() > 0) { + httpAuthenticationSave = authChanged.charAt(0) == '1' || authChanged.charAt(0) == 't' || authChanged.charAt(0) == 'o'; + } + } + if (httpAuthenticationSave) { + *httpAuthentication = httpAuthenticationParam.getValue(); + } + ArduinoOcpp::configuration_save(); + }); + + wifiManager.setDarkMode(true); + + //wifiManager.setConfigPortalTimeout(CAPATITIVE_PORTAL_TIMEOUT / 1000); //if nobody logs in to the portal, continue after timeout + wifiManager.setTimeout(CAPTIVE_PORTAL_TIMEOUT / 1000); //if nobody logs in to the portal, continue after timeout + wifiManager.setConnectTimeout(CAPTIVE_PORTAL_TIMEOUT / 1000); + //wifiManager.setSaveConnect(true); + wifiManager.setAPClientCheck(true); // avoid timeout if client connected to softap + PRINTLN(F("[main] Start capatitive portal")); + + if (wifiManager.startConfigPortal("EVSE_Maintenance_Portal", "myEvseController")) { + return true; + } else { + return wifiManager.autoConnect("EVSE_Maintenance_Portal", "myEvseController"); + } +} + +bool Ocpp_URL::parse(String &ocppUrl) { + String inputUrl = String(ocppUrl); // wss://host.com:433/path + inputUrl.trim(); + + if (ocppUrl.isEmpty()) { + return false; + } + url = inputUrl; + + inputUrl.toLowerCase(); + + if (inputUrl.startsWith("wss://")) { + isTLS = true; + port = 443; + } else if (inputUrl.startsWith("ws://")) { + isTLS = false; + port = 80; + } else { + return false; + } + inputUrl = inputUrl.substring(inputUrl.indexOf("://") + strlen("://")); // host.com:433/path + if (inputUrl.isEmpty()) { + return false; + } + + host = String('\0'); + for (uint i = 0; i < inputUrl.length(); i++) { + if (inputUrl.charAt(i) == ':') { //case host.com:433/path + host = inputUrl.substring(0, i); + uint16_t inputPort = 0; + for (unsigned int j = i + 1; j < inputUrl.length(); j++) { + if (isDigit(inputUrl.charAt(j))) { + inputPort *= (uint16_t) 10; + inputPort += inputUrl.charAt(j) - '0'; + } else { + break; + } + } + if (inputPort > 0) { + port = inputPort; + } + break; + } else if (inputUrl.charAt(i) == '/') { //case host.com/path + host = inputUrl.substring(0, i); + break; + } + } + if (host.isEmpty()) { + host = inputUrl; + } + + return true; +} diff --git a/examples/Simulation_without_HW/OneConnector_EVSE.ino b/examples/Simulation_without_HW/OneConnector_EVSE.ino deleted file mode 100644 index ca204e4a..00000000 --- a/examples/Simulation_without_HW/OneConnector_EVSE.ino +++ /dev/null @@ -1,107 +0,0 @@ -// matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 -// MIT License - -#include - -#include - -#include - -#if defined(ESP32) -#include -#else -#include -#include -ESP8266WiFiMulti WiFiMulti; -#endif - -//ArduinoOcpp modules -#include -#include -#include - -//Simulated HW integration (used as example) -#include "OneConnector_HW_integration.h" - - -#define STASSID "YOUR_WLAN_SSID" -#define STAPSK "YOUR_WLAN_PW" - -// WebSocket echo server test - -#define OCPP_HOST "wss://echo.websocket.org" -#define OCPP_PORT 80 -#define OCPP_URL "wss://echo.websocket.org/" - -// -//// Settings which worked for my SteVe instance -// -//#define OCPP_HOST "my.instance.com" -//#define OCPP_PORT 80 -//#define OCPP_URL "ws://my.instance.com/steve/websocket/CentralSystemService/gpio-based-charger" - -#define LED 2 -#define LED_ON LOW -#define LED_OFF HIGH - -void setup() { - - Serial.begin(115200); - Serial.setDebugOutput(true); - - pinMode(LED, OUTPUT); - - Serial.println(); - Serial.println(); - Serial.println(); - - for (uint8_t t = 4; t > 0; t--) { - Serial.printf("[SETUP] BOOT WAIT %d...\n", t); - Serial.flush(); - digitalWrite(LED, LED_ON); - delay(100); - digitalWrite(LED, LED_OFF); - delay(300); - digitalWrite(LED, LED_ON); - delay(300); - digitalWrite(LED, LED_OFF); - delay(300); - } - -#if defined (ESP32) - WiFi.begin(STASSID, STAPSK); - - Serial.print(F("[main] Wait for WiFi: ")); - while (!WiFi.isConnected()) { - Serial.print('.'); - delay(1000); - } - Serial.print(F(" connected!\n")); -#else - WiFiMulti.addAP(STASSID, STAPSK); - - while (WiFiMulti.run() != WL_CONNECTED) { - delay(100); - } -#endif - - OCPP_initialize(OCPP_HOST, OCPP_PORT, OCPP_URL, ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail); - - setPowerActiveImportSampler([]() { - return EVSE_readChargeRate(); //from the GPIO-charger example - }); - - setOnChargingRateLimitChange([](float limit) { - EVSE_setChargingLimit(limit); //from the GPIO-charger example - }); - - EVSE_initialize(); - -} - -void loop() { - OCPP_loop(); - - EVSE_loop(); //refers to the GPIO-charger example -} diff --git a/examples/Simulation_without_HW/OneConnector_HW_integration.h b/examples/Simulation_without_HW/OneConnector_HW_integration.h deleted file mode 100644 index e9007058..00000000 --- a/examples/Simulation_without_HW/OneConnector_HW_integration.h +++ /dev/null @@ -1,18 +0,0 @@ -// matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 -// MIT License - -#ifndef ONE_CONNECTOR_HW_INTEGRATION -#define ONE_CONNECTOR_HW_INTEGRATION - -void EVSE_initialize(); - -void EVSE_loop(); - -float EVSE_readChargeRate(); - -void EVSE_setChargingLimit(float limit); - -bool EVSE_evRequestsEnergy(); - -#endif diff --git a/examples/Simulation_without_HW/Simulated_HW.ino b/examples/Simulation_without_HW/Simulated_HW.ino deleted file mode 100644 index 9305d13a..00000000 --- a/examples/Simulation_without_HW/Simulated_HW.ino +++ /dev/null @@ -1,123 +0,0 @@ -// matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 -// MIT License - -#include - -#include "OneConnector_HW_integration.h" - -#include - -#include - -/* - * This module simulates user interaction at an EVSE. It runs a routine in which a - * simulated user connects an EV to the EVSE connector, authorizes herself and - * unplugs the EV again. This routine is repeated over and over again. - * - * The goal of this module is to allow the exploration of this library without the - * need to write test code before. - */ - -void onEvPlug(); -void onEvUnplug(); - -bool evIsPlugged = false; -bool transactionRunning = false; -bool evRequestsEnergy = false; -bool evseIsBooted = false; - -const ulong T_PLUG = 10000; //after ... ms, user plugs EV. This simulation is configured to have authorize the - //charging session with a fixed idTag. This is often a good choice for private chargers -const ulong T_FULL = 30000; //after ... ms, EV is fully charged and doesn't request energy anymore -const ulong T_UNPLUG = 40000; //after ... ms, user unplugs EV -const ulong T_RESET = 60000; //after ... ms, the test routine start over again - -ulong t; - -float chargingLimit = 3680.f; // = 230V * 16A - -void EVSE_initialize() { - Serial.print(F("[EVSE] Simulated_EVSE is being initialized ... ")); - - bootNotification("Greatest EVSE vendor", "Simulated CP model", [] (JsonObject confMsg) { - //This callback is executed when the .conf() response from the central system arrives - Serial.print(F("BootNotification was answered. Central System clock: ")); - Serial.println(confMsg["currentTime"].as()); - - evseIsBooted = true; - t = millis(); - }); - - Serial.print(F("ready. Wait for BootNotification.conf(), then start\n")); -} - -void EVSE_loop() { - if (!evseIsBooted) return; - - if (!evIsPlugged && millis() - t >= T_PLUG && millis() - t < T_FULL) { - evIsPlugged = true; - onEvPlug(); - } - - if (evRequestsEnergy && millis() - t >= T_FULL && millis() - t < T_UNPLUG) { - evRequestsEnergy = false; - } - - if (evIsPlugged && millis() - t >= T_UNPLUG && millis() - t < T_RESET) { - evIsPlugged = false; - onEvUnplug(); - } - - if (millis() - t >= T_RESET) { - t = millis(); - evIsPlugged = false; - evRequestsEnergy = false; - transactionRunning = false; - Serial.print(F("\n[EVSE] --- Start test routines ---\n")); - Serial.print(F("[EVSE] INFO: free heap (kB): ")); - Serial.println(ESP.getFreeHeap()); - return; - } -} - -void onEvPlug() { - Serial.print(F("[EVSE] EV plugged\n")); - String fixedIdTag = String("abcdef123456789"); // e.g. idTag = readRFIDTag(); - if (getTransactionId() < 0) { - authorize(fixedIdTag, [](JsonObject confMsg) { - startTransaction([](JsonObject conf) { - transactionRunning = true; - evRequestsEnergy = true; - Serial.print(F("[EVSE] Successfully authorized and started transaction\n")); - }); - }); - } else { - transactionRunning = true; - evRequestsEnergy = true; - Serial.print(F("[EVSE] Successfully authorized and started transaction II\n")); - } -} - -void onEvUnplug() { - Serial.print(F("[EVSE] EV unplugged\n")); - transactionRunning = false; - evRequestsEnergy = false; - if (getTransactionId() >= 0) - stopTransaction(); -} - -float EVSE_readChargeRate() { //estimation for EVSEs without power meter - if (evIsPlugged && evRequestsEnergy) { //example - return chargingLimit; - } else { - return 0.0f; - } -} - -void EVSE_setChargingLimit(float limit) { - Serial.print(F("[EVSE] New charging limit set. Got ")); - Serial.print(limit); - Serial.print(F("\n")); - chargingLimit = limit; -} From 5597c415466baf493d13b2c6bbc72efd6d0ba5e6 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 13 Oct 2021 14:52:50 +0200 Subject: [PATCH 073/696] update examples --- examples/ESP32/README.md | 3 --- examples/{EVCC => SECC}/main.ino | 29 ++++++++++++++--------------- 2 files changed, 14 insertions(+), 18 deletions(-) delete mode 100644 examples/ESP32/README.md rename examples/{EVCC => SECC}/main.ino (94%) diff --git a/examples/ESP32/README.md b/examples/ESP32/README.md deleted file mode 100644 index 10f700cf..00000000 --- a/examples/ESP32/README.md +++ /dev/null @@ -1,3 +0,0 @@ -#### ESP32 port progress - -The ESP32 port is almost finished. Only the persistent storage is still to be worked on. Please clone the branch `develop` and set the build flag `AO_DEACTIVATE_FLASH`. Alternatively, you can search for the `#ifndef AO_DEACTIVATE_FLASH` statements in `Configuration.cpp` and `SmartChargingService.cpp` and delete everything inside their scope. diff --git a/examples/EVCC/main.ino b/examples/SECC/main.ino similarity index 94% rename from examples/EVCC/main.ino rename to examples/SECC/main.ino index 39e15890..a921fb79 100644 --- a/examples/EVCC/main.ino +++ b/examples/SECC/main.ino @@ -3,20 +3,19 @@ // MIT License // // -// IMPORTANT: Please add all files from this example folder to your project! +// IMPORTANT: Please add tzapu/WiFiManager@0.16.0 to the libdeps of your project! // -// This is a complete EVCC (Electric Vehicle Communications Controller) codebase. An EVCC -// is responsible for all communication over the internet, i.e. to your OCPP server. You -// can flash this program onto an ESP32, build it into your EVSE and start charging -// with OCPP-connectivity. +// This sketch demonstrates a complete OCPP Wi-Fi module of an EVSE. You can flash +// an ESP8266/ESP32 with this program, build it into your EVSE and start charging with +// OCPP-connectivity. // -// The pin mapping and additional comments can be found in this file. Maybe you need to -// adapt a few things before deployment. +// The pin mapping and additional comments can be found in this file. You probably need +// to adapt a few things before deployment. // #include -#include //please install tzapu/WiFiManager +#include //please install tzapu/WiFiManager@0.16.0 #include #include //load and save settings of WiFi captive portal @@ -27,7 +26,7 @@ */ #define AMPERAGE_PIN 4 //modulated as PWM -#define EV_PLUG_PIN 14 +#define EV_PLUG_PIN 14 // Input pin | Read if an EV is connected to the EVSE #if defined(ESP32) #define EV_PLUGGED HIGH #define EV_UNPLUGGED LOW @@ -36,11 +35,11 @@ #define EV_UNPLUGGED HIGH #endif -#define OCPP_CHARGE_PERMISSION_PIN 5 +#define OCPP_CHARGE_PERMISSION_PIN 5 // Output pin | Signal if OCPP allows / forbids energy flow #define OCPP_CHARGE_PERMITTED HIGH #define OCPP_CHARGE_FORBIDDEN LOW -#define EV_CHARGE_PIN 12 +#define EV_CHARGE_PIN 12 // Input pin | Read if EV requests energy (corresponds to SAE J1772 State C) #if defined(ESP32) #define EV_CHARGING LOW #define EV_SUSPENDED HIGH @@ -49,11 +48,11 @@ #define EV_SUSPENDED LOW #endif -#define OCPP_AVAILABILITY_PIN 0 +#define OCPP_AVAILABILITY_PIN 0 // Output pin | Signal if this EVSE is out of order (set by Central System) #define OCPP_AVAILABLE HIGH #define OCPP_UNAVAILABLE LOW -#define EVSE_GROUND_FAULT_PIN 13 +#define EVSE_GROUND_FAULT_PIN 13 // Input pin | Read ground fault detector #define EVSE_GROUND_FAULTED HIGH #define EVSE_GROUND_CLEAR LOW @@ -62,11 +61,11 @@ * Internal LED of ESP8266/ESP32 + additional ESP8266 development board LED */ -#define SERVER_CONNECT_LED 16 +#define SERVER_CONNECT_LED 16 // Output pin | Signal if connection to OCPP server has succeeded #define SERVER_CONNECT_ON LOW #define SERVER_CONNECT_OFF HIGH -#define CHARGE_PERMISSION_LED 2 +#define CHARGE_PERMISSION_LED 2 // Output pin | Signal if OCPP allows / forbids energy flow #if defined(ESP32) #define CHARGE_PERMISSION_ON HIGH #define CHARGE_PERMISSION_OFF LOW From c99645f1160203cde3e91cdf7f931a78ff74297a Mon Sep 17 00:00:00 2001 From: Matthias Akstaller <63792403+matth-x@users.noreply.github.com> Date: Wed, 13 Oct 2021 16:58:45 +0200 Subject: [PATCH 074/696] readme for example --- examples/SECC/README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 examples/SECC/README.md diff --git a/examples/SECC/README.md b/examples/SECC/README.md new file mode 100644 index 00000000..529b2171 --- /dev/null +++ b/examples/SECC/README.md @@ -0,0 +1,20 @@ +# Simple EVSE OCPP communication controller + +With this sketch you can use an ESP8266/ESP32 as the central control unit of an EVSE. It controls the HW peripherals of the EVSE and connects the charge point to an OCPP server. When booting the EVSE, it opens a web dashboard for setting the Wi-Fi and OCPP credentials in the field (thanks to tzapu's WiFiManager). + +## Architecture + +The program can connect to an SAE J1772 driver using the GPIO pins of the ESP. Furthermore, it can show the online status and charge point availability status via status LEDs. Displays, RFID readers or energy meters are not supported, though for your project you can integrate further HW components of course. + +You can find the interface descriptions in `main.ino`. Please be aware that although this program modulates the maximum charge rate with PWM analogous to the SAE J1772 standard, the PWM signal does not reflect the EVSE state. The latter is encoded via the other GPIO pins. + +## Usage + +- Create an empty project in the PlatformIO IDE. +- Copy the `platformio.ini` from ArduinoOcpp's root directory to your source directory. Add `tzapu/WiFiManager@0.16.0` to the libdeps of the `platformio.ini`. +- Copy the `main.ino` from this example folder into your source directory. Adapt the pinout settings if needed. +- Finished. You should be able to compile and upload the sketch onto your ESP. + +## Standalone mode + +If you just want to check out ArduinoOcpp you can run this sketch on an ESP without any peripherals. The pinout of the ESP8266-NodeMCU board is already configured correctly in `main.ino` so that board would be the most convenient for testing. From 5f445e2eb01761a2e5b5e2a1b781a950b0cb9177 Mon Sep 17 00:00:00 2001 From: Matthias Akstaller <63792403+matth-x@users.noreply.github.com> Date: Wed, 13 Oct 2021 17:03:29 +0200 Subject: [PATCH 075/696] obsolete --- examples/Simulation_without_HW/Readme.md | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 examples/Simulation_without_HW/Readme.md diff --git a/examples/Simulation_without_HW/Readme.md b/examples/Simulation_without_HW/Readme.md deleted file mode 100644 index 8c069a41..00000000 --- a/examples/Simulation_without_HW/Readme.md +++ /dev/null @@ -1,3 +0,0 @@ -#### How to run this example - -You can find a description of how this example works in the source files. To compile this example, you need to copy all three files into your project folder. From 34df823f2f347a7e7017ab10119a62cff0cb969d Mon Sep 17 00:00:00 2001 From: Matthias Akstaller <63792403+matth-x@users.noreply.github.com> Date: Wed, 13 Oct 2021 17:16:14 +0200 Subject: [PATCH 076/696] new example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e25b8adc..d732c6c2 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ void setOnSetChargingProfileRequest(void listener(JsonObject payload)); You can also process the original payload from the CS using the `payload` object. -To get started quickly without EVSE hardware, you can use the charge point simulation in `examples/Simulation_without_HW/` as a starting point. It establishes a connection to a WebSocket echo server (`wss://echo.websocket.org/`). This works because the library can answer its own requests to make testing easier. You can also run the simulation against the URL of your central system. `Simulated_HW.ino` simulates the user interaction at the EVSE. It repeats its test routine over and over again. You can watch it in your device monitor! +To get started quickly with or without EVSE hardware, you can flash the sketch in `examples/SECC` onto your ESP. That example mimics a full OCPP communications controller as it would look like in a real charging station. You can build a charger prototype based on that example or just view the internal state using the device monitor. ## Dependencies From 8331cc6fb722e8bbb5b43661c93b3590f2747df6 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 14 Oct 2021 15:32:13 +0200 Subject: [PATCH 077/696] minor changes --- examples/SECC/README.md | 6 +++--- platformio.ini | 4 ---- src/ArduinoOcpp/Core/OcppSocket.cpp | 2 ++ src/ArduinoOcpp/MessagesV16/StatusNotification.cpp | 12 ++++++------ 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/examples/SECC/README.md b/examples/SECC/README.md index 529b2171..be44e908 100644 --- a/examples/SECC/README.md +++ b/examples/SECC/README.md @@ -1,10 +1,10 @@ # Simple EVSE OCPP communication controller -With this sketch you can use an ESP8266/ESP32 as the central control unit of an EVSE. It controls the HW peripherals of the EVSE and connects the charge point to an OCPP server. When booting the EVSE, it opens a web dashboard for setting the Wi-Fi and OCPP credentials in the field (thanks to tzapu's WiFiManager). +With this sketch you can use an ESP8266/ESP32 as the central control unit of an EVSE. It controls the HW peripherals of the EVSE and connects the charge point to an OCPP server. When booting up the EVSE, it opens a maintenance web dashboard for setting the Wi-Fi and OCPP credentials (thanks to tzapu's WiFiManager). ## Architecture -The program can connect to an SAE J1772 driver using the GPIO pins of the ESP. Furthermore, it can show the online status and charge point availability status via status LEDs. Displays, RFID readers or energy meters are not supported, though for your project you can integrate further HW components of course. +The communication controller can connect to an SAE J1772 driver using the GPIO pins of the ESP. Furthermore, it can show the online status and charge point availability status via status LEDs. Displays, RFID readers or energy meters are not supported, though for your project you can integrate further HW components of course. You can find the interface descriptions in `main.ino`. Please be aware that although this program modulates the maximum charge rate with PWM analogous to the SAE J1772 standard, the PWM signal does not reflect the EVSE state. The latter is encoded via the other GPIO pins. @@ -17,4 +17,4 @@ You can find the interface descriptions in `main.ino`. Please be aware that alth ## Standalone mode -If you just want to check out ArduinoOcpp you can run this sketch on an ESP without any peripherals. The pinout of the ESP8266-NodeMCU board is already configured correctly in `main.ino` so that board would be the most convenient for testing. +If you just want to check out ArduinoOcpp you can run this sketch on an ESP without any peripherals. The pinout of the ESP8266-NodeMCU board is already fully configured in `main.ino` so that board is the most convenient to start testing. diff --git a/platformio.ini b/platformio.ini index d508fed1..9fdff69f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -4,8 +4,6 @@ ; ; PlatformIO Project Configuration File ; -; Preconfigured for the NodeMCU v2 with the ESP8266. Please adapt this file according to your needs. -; ; How to use the platformio.ini: ; https://docs.platformio.org/en/latest/projectconf/index.html @@ -47,5 +45,3 @@ build_flags = -DCONFIG_LITTLEFS_FOR_IDF_3_2 build_partitions = min_spiffs.csv upload_speed = 921600 - -; diff --git a/src/ArduinoOcpp/Core/OcppSocket.cpp b/src/ArduinoOcpp/Core/OcppSocket.cpp index 7a8d19f1..90a7bee9 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.cpp +++ b/src/ArduinoOcpp/Core/OcppSocket.cpp @@ -71,6 +71,8 @@ void OcppServerSocket::loop() { } bool OcppServerSocket::sendTXT(String &out) { + if (DEBUG_OUT) Serial.print(F("[OcppServerSocket] Send TXT: ")); + if (DEBUG_OUT) Serial.println(out); return OcppServer::getInstance()->sendTXT(ip_addr, out); } diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp index 72c01ec3..66da8bd5 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp @@ -113,9 +113,9 @@ DynamicJsonDocument* StatusNotification::createReq() { void StatusNotification::processConf(JsonObject payload) { - /* - * Empty payload - */ + /* + * Empty payload + */ } @@ -137,7 +137,7 @@ void StatusNotification::processReq(JsonObject payload) { * For debugging only */ DynamicJsonDocument* StatusNotification::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(0); - //JsonObject payload = doc->to(); - return doc; + DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); + JsonObject payload = doc->to(); + return doc; } From 716cfb80d5b21cf1c38787588f7da5cd506115f6 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 15 Oct 2021 17:43:41 +0200 Subject: [PATCH 078/696] set unavailable earlier --- .../Tasks/FirmwareManagement/FirmwareService.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index b13a24c7..75d9ecd4 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -32,6 +32,13 @@ void FirmwareService::loop() { //if (!downloadIssued) { if (stage == UpdateStage::Idle) { if (DEBUG_OUT) Serial.println(F("[FirmwareService] Start update!")); + if (getChargePointStatusService()) { + ConnectorStatus *evse = getChargePointStatusService()->getConnector(0); + if (!availabilityRestore) { + availabilityRestore = (evse->getAvailability() == AVAILABILITY_OPERATIVE); + } + evse->setAvailability(false); + } if (onDownload == NULL) { stage = UpdateStage::AfterDownload; } else { @@ -104,11 +111,6 @@ void FirmwareService::loop() { } if (!ongoingTx) { - if (cpStatus) { - ConnectorStatus *evse = cpStatus->getConnector(0); - availabilityRestore = (evse->getAvailability() == AVAILABILITY_OPERATIVE); - evse->setAvailability(false); - } stage = UpdateStage::AwaitInstallation; installationIssued = true; From 414eba692dfa45e5bb76dc62eb1bfeaba09fa8a9 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 18 Oct 2021 17:13:20 +0200 Subject: [PATCH 079/696] updated definition --- .../Tasks/FirmwareManagement/FirmwareService.cpp | 2 +- .../Tasks/FirmwareManagement/FirmwareService.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index 75d9ecd4..a22e43d7 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -179,7 +179,7 @@ void FirmwareService::loop() { } } -void FirmwareService::scheduleFirmwareUpdate(String &location, OcppTimestamp retreiveDate, int retries, ulong retryInterval) { +void FirmwareService::scheduleFirmwareUpdate(String &location, OcppTimestamp retreiveDate, int retries, unsigned int retryInterval) { this->location = String(location); this->retreiveDate = retreiveDate; this->retries = retries; diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h index 6b7247cf..b6b7b37d 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h @@ -45,7 +45,7 @@ class FirmwareService { String location = String('\0'); OcppTimestamp retreiveDate = OcppTimestamp(); int retries = 0; - ulong retryInterval = 0; + unsigned int retryInterval = 0; std::function onDownload = NULL; std::function onInstall = NULL; @@ -66,17 +66,17 @@ class FirmwareService { bool availabilityRestore = false; + OcppOperation *getFirmwareStatusNotification(); + public: FirmwareService(const char *buildNumber); void loop(); - void scheduleFirmwareUpdate(String &location, OcppTimestamp retreiveDate, int retries = 1, ulong retryInterval = 0); + void scheduleFirmwareUpdate(String &location, OcppTimestamp retreiveDate, int retries = 1, unsigned int retryInterval = 0); Ocpp16::FirmwareStatus getFirmwareStatus(); - OcppOperation *getFirmwareStatusNotification(); - void setOnDownload(std::function onDownload); void setDownloadStatusSampler(std::function downloadStatusSampler); From 1725e89a7522afe80ea8148ad0519e382f8c92d5 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 19 Oct 2021 16:41:30 +0200 Subject: [PATCH 080/696] add diagnostics / log upload --- README.md | 4 +- src/ArduinoOcpp.cpp | 9 + src/ArduinoOcpp/Core/OcppEngine.cpp | 13 ++ src/ArduinoOcpp/Core/OcppEngine.h | 5 + .../DiagnosticsStatusNotification.cpp | 50 ++++++ .../DiagnosticsStatusNotification.h | 40 +++++ .../MessagesV16/GetDiagnostics.cpp | 71 ++++++++ src/ArduinoOcpp/MessagesV16/GetDiagnostics.h | 40 +++++ .../MessagesV16/UpdateFirmware.cpp | 2 +- .../SimpleOcppOperationFactory.cpp | 6 + .../Tasks/Diagnostics/DiagnosticsService.cpp | 162 ++++++++++++++++++ .../Tasks/Diagnostics/DiagnosticsService.h | 71 ++++++++ 12 files changed, 470 insertions(+), 3 deletions(-) create mode 100644 src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp create mode 100644 src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h create mode 100644 src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp create mode 100644 src/ArduinoOcpp/MessagesV16/GetDiagnostics.h create mode 100644 src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp create mode 100644 src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h diff --git a/README.md b/README.md index d732c6c2..8fb42255 100644 --- a/README.md +++ b/README.md @@ -156,8 +156,8 @@ In case you use PlatformIO, you can copy all dependencies from `platformio.ini` | **Remote trigger profile** | | `TriggerMessage` | :heavy_check_mark: | | **Firmware management** | -| `GetDiagnostics` | | | :heavy_multiplication_x: | -| `DiagnosticsStatusNotification` | | | :heavy_multiplication_x: | +| `GetDiagnostics` | :heavy_check_mark: | +| `DiagnosticsStatusNotification` | :heavy_check_mark: | | `FirmwareStatusNotification` | :heavy_check_mark: | | `UpdateFirmware` | :heavy_check_mark: | diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index d875a416..a4c6a1ed 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,7 @@ SmartChargingService *smartChargingService; ChargePointStatusService *chargePointStatusService; HeartbeatService *heartbeatService; FirmwareService *firmwareService = NULL; +DiagnosticsService *diagnosticsServce = NULL; OnLimitChange onLimitChange; OcppTime *ocppTime; @@ -165,6 +167,13 @@ void OCPP_initialize(OcppSocket *ocppSocket, float V_eff, ArduinoOcpp::Filesyste #endif setFirmwareService(firmwareService); +#if !defined(AO_CUSTOM_DIAGNOSTICS) && !defined(AO_CUSTOM_WEBSOCKET) + diagnosticsServce = EspWiFi::makeDiagnosticsService(); //will only return "Rejected" because logging is not implemented yet +#else + diagnosticsServce = new DiagnosticsService(); +#endif + setDiagnosticsService(diagnosticsServce); + OCPP_initialized = true; } diff --git a/src/ArduinoOcpp/Core/OcppEngine.cpp b/src/ArduinoOcpp/Core/OcppEngine.cpp index 3add98f6..7ee65391 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.cpp +++ b/src/ArduinoOcpp/Core/OcppEngine.cpp @@ -20,6 +20,7 @@ SmartChargingService *ocppEngine_smartChargingService = NULL; ChargePointStatusService *ocppEngine_chargePointStatusService = NULL; MeteringService *ocppEngine_meteringService = NULL; FirmwareService *ocppEngine_firmwareService = NULL; +DiagnosticsService *ocppEngine_diagnosticsService = NULL; OcppTime *ocppEngine_ocppTime; std::vector initiatedOcppOperations; @@ -454,6 +455,18 @@ FirmwareService *getFirmwareService() { return ocppEngine_firmwareService; } +void setDiagnosticsService(DiagnosticsService *diagnosticsService) { + ocppEngine_diagnosticsService = diagnosticsService; +} + +DiagnosticsService *getDiagnosticsService() { + if (ocppEngine_diagnosticsService == NULL) { + Serial.print(F("[OcppEngine] Error: in OcppEngine, there is no ocppEngine_diagnosticsService set, but it is accessed!\n")); + //no error catch + } + return ocppEngine_diagnosticsService; +} + void ocppEngine_setOcppTime(OcppTime *ocppTime) { ocppEngine_ocppTime = ocppTime; } diff --git a/src/ArduinoOcpp/Core/OcppEngine.h b/src/ArduinoOcpp/Core/OcppEngine.h index 0c1d75f9..2f3a3b62 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.h +++ b/src/ArduinoOcpp/Core/OcppEngine.h @@ -14,6 +14,7 @@ #include #include #include +#include namespace ArduinoOcpp { @@ -58,6 +59,10 @@ void setFirmwareService(FirmwareService *firmwareService); FirmwareService *getFirmwareService(); +void setDiagnosticsService(DiagnosticsService *diagnosticsService); + +DiagnosticsService *getDiagnosticsService(); + void ocppEngine_setOcppTime(OcppTime *ocppTime); OcppTime *getOcppTime(); diff --git a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp new file mode 100644 index 00000000..048b5e21 --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp @@ -0,0 +1,50 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include +#include +#include + +using ArduinoOcpp::Ocpp16::DiagnosticsStatusNotification; + +DiagnosticsStatusNotification::DiagnosticsStatusNotification() { + DiagnosticsService *diagService = getDiagnosticsService(); + if (diagService) { + status = diagService->getDiagnosticsStatus(); + } else { + status = DiagnosticsStatus::Idle; + } +} + +DiagnosticsStatusNotification::DiagnosticsStatusNotification(DiagnosticsStatus status) : status(status) { +} + +const char *DiagnosticsStatusNotification::cstrFromFwStatus(DiagnosticsStatus status) { + switch (status) { + case (DiagnosticsStatus::Idle): + return "Idle"; + break; + case (DiagnosticsStatus::Uploaded): + return "Downloaded"; + break; + case (DiagnosticsStatus::UploadFailed): + return "DownloadFailed"; + break; + case (DiagnosticsStatus::Uploading): + return "Downloading"; + break; + } + return NULL; //cannot be reached +} + +DynamicJsonDocument* DiagnosticsStatusNotification::createReq() { + DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); + JsonObject payload = doc->to(); + payload["status"] = cstrFromFwStatus(status); + return doc; +} + +void DiagnosticsStatusNotification::processConf(JsonObject payload){ + // no payload, nothing to do +} diff --git a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h new file mode 100644 index 00000000..c47519d5 --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h @@ -0,0 +1,40 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include + +#ifndef DIAGNOSTICSSTATUSNOTIFICATION_H +#define DIAGNOSTICSSTATUSNOTIFICATION_H + +namespace ArduinoOcpp { +namespace Ocpp16 { + +enum class DiagnosticsStatus { + Idle, + Uploaded, + UploadFailed, + Uploading +}; + +class DiagnosticsStatusNotification : public OcppMessage { +private: + DiagnosticsStatus status; + static const char *cstrFromFwStatus(DiagnosticsStatus status); +public: + DiagnosticsStatusNotification(); + + DiagnosticsStatusNotification(DiagnosticsStatus status); + + const char* getOcppOperationType() {return "DiagnosticsStatusNotification"; } + + DynamicJsonDocument* createReq(); + + void processConf(JsonObject payload); + +}; + +} //end namespace Ocpp16 +} //end namespace ArduinoOcpp + +#endif diff --git a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp new file mode 100644 index 00000000..bec49fde --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp @@ -0,0 +1,71 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include +#include + +using ArduinoOcpp::Ocpp16::GetDiagnostics; + +GetDiagnostics::GetDiagnostics() { + +} + +void GetDiagnostics::processReq(JsonObject payload) { + /* + * Process the application data here. Note: you have to implement the FW update procedure in your client code. You have to set + * a onSendConfListener in which you initiate a FW update (e.g. calling ESPhttpUpdate.update(...) ) + */ + + const char *loc = payload["location"] | ""; + location = String(loc); + //check location URL. Maybe introduce Same-Origin-Policy? + if (location.isEmpty()) { + formatError = true; + Serial.println(F("[GetDiagnostics] Could not read location. Abort")); + return; + } + + retries = payload["retries"] | 1; + retryInterval = payload["retryInterval"] | 180; + + //check the integrity of startTime + if (payload.containsKey("startTime")) { + const char *startTimeRaw = payload["startTime"] | "Invalid"; + if (!startTime.setTime(startTimeRaw)) { + formatError = true; + Serial.println(F("[GetDiagnostics] Could not read startTime. Abort")); + return; + } + } + + //check the integrity of stopTime + if (payload.containsKey("startTime")) { + const char *stopTimeRaw = payload["stopTime"] | "Invalid"; + if (!stopTime.setTime(stopTimeRaw)) { + formatError = true; + Serial.println(F("[GetDiagnostics] Could not read stopTime. Abort")); + return; + } + } +} + +DynamicJsonDocument* GetDiagnostics::createConf(){ + DiagnosticsService *diagService = getDiagnosticsService(); + if (diagService != NULL) { + fileName = diagService->requestDiagnosticsUpload(location, retries, retryInterval, startTime, stopTime); + } else { + Serial.println(F("[GetDiagnostics] DiagnosticsService has not been initialized before! Please have a look at ArduinoOcpp.cpp for an example. Abort")); + } + + if (fileName.isEmpty()) { + DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); + JsonObject payload = doc->to(); + return doc; + } else { + DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + fileName.length() + 1); + JsonObject payload = doc->to(); + payload["fileName"] = fileName; + return doc; + } +} diff --git a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h new file mode 100644 index 00000000..06e50a9f --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h @@ -0,0 +1,40 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef GETDIAGNOSTICS_H +#define GETDIAGNOSTICS_H + +#include +#include + +namespace ArduinoOcpp { +namespace Ocpp16 { + +class GetDiagnostics : public OcppMessage { +private: + String location = String('\0'); + int retries = 1; + ulong retryInterval = 180; + OcppTimestamp startTime = OcppTimestamp(); + OcppTimestamp stopTime = OcppTimestamp(); + + String fileName = String('\0'); + + bool formatError = false; +public: + GetDiagnostics(); + + const char* getOcppOperationType() {return "GetDiagnostics";} + + void processReq(JsonObject payload); + + DynamicJsonDocument* createConf(); + + const char *getErrorCode() {if (formatError) return "FormationViolation"; else return NULL;} +}; + +} //end namespace Ocpp16 +} //end namespace ArduinoOcpp + +#endif diff --git a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp index 9464d3ea..f98b27d2 100644 --- a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp +++ b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp @@ -39,7 +39,7 @@ void UpdateFirmware::processReq(JsonObject payload) { } DynamicJsonDocument* UpdateFirmware::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(0); + DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); JsonObject payload = doc->to(); FirmwareService *fwService = getFirmwareService(); diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 7dee358e..8c42fd02 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include #include @@ -238,6 +240,10 @@ OcppOperation *makeOcppOperation(const char *messageType) { operation->setOnReceiveReqListener(onUpdateFirmwareReceiveReq); } else if (!strcmp(messageType, "FirmwareStatusNotification")) { msg = new Ocpp16::FirmwareStatusNotification(); + } else if (!strcmp(messageType, "GetDiagnostics")) { + msg = new Ocpp16::GetDiagnostics(); + } else if (!strcmp(messageType, "DiagnosticsStatusNotification")) { + msg = new Ocpp16::DiagnosticsStatusNotification(); } else if (!strcmp(messageType, "UnlockConnector")) { msg = new Ocpp16::UnlockConnector(); } else if (!strcmp(messageType, "ClearChargingProfile")) { diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp new file mode 100644 index 00000000..ebb7e3f6 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp @@ -0,0 +1,162 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include +#include +#include + +using namespace ArduinoOcpp; +using Ocpp16::DiagnosticsStatus; + +void DiagnosticsService::loop() { + OcppOperation *notification = getDiagnosticsStatusNotification(); + if (notification) { + initiateOcppOperation(notification); + } + + const OcppTimestamp& timestampNow = getOcppTime()->getOcppTimestampNow(); + if (retries > 0 && timestampNow >= nextTry) { + + if (!uploadIssued) { + if (onUpload != NULL) { + onUpload(location, startTime, stopTime); + uploadIssued = true; + } else { + Serial.println(F("[DiagnosticsService] onUpload must be set! (see setOnUpload). Will abort")); + retries = 0; + uploadIssued = false; + } + } + + if (uploadIssued) { + if (uploadStatusSampler != NULL && uploadStatusSampler() == UploadStatus::Uploaded) { + //success! + uploadIssued = false; + retries = 0; + } + + //check if maximum time elapsed or failed + const int UPLOAD_TIMEOUT = 60; + if (timestampNow - nextTry >= UPLOAD_TIMEOUT + || (uploadStatusSampler != NULL && uploadStatusSampler() == UploadStatus::UploadFailed)) { + //maximum upload time elapsed or failed + + if (uploadStatusSampler == NULL) { + //No way to find out if failed. But maximum time has elapsed. Assume success + uploadIssued = false; + retries = 0; + } else { + //either we have UploadFailed status or (NotDownloaded + timeout) here + Serial.println(F("[DiagnosticsService] Upload timeout or failed!")); + const int TRANSITION_DELAY = 10; + if (retryInterval <= UPLOAD_TIMEOUT + TRANSITION_DELAY) { + nextTry = timestampNow; + nextTry += TRANSITION_DELAY; //wait for another 10 seconds + } else { + nextTry += retryInterval; + } + retries--; + } + } + } //end if (uploadIssued) + } //end try upload +} + +//timestamps before year 2021 will be treated as "undefined" +String DiagnosticsService::requestDiagnosticsUpload(String &location, int retries, unsigned int retryInterval, OcppTimestamp startTime, OcppTimestamp stopTime) { + if (onUpload == NULL) //maybe add further plausibility checks + return String('\0'); + + this->location = location; + this->retries = retries; + this->retryInterval = retryInterval; + this->startTime = startTime; + + OcppTimestamp stopMin = OcppTimestamp(2021,0,0,0,0,0); + if (stopTime >= stopMin) { + this->stopTime = stopTime; + } else { + OcppTimestamp newStop = getOcppTime()->getOcppTimestampNow(); + newStop += 3600 * 24 * 365; //set new stop time one year in future + this->stopTime = newStop; + } + + if (DEBUG_OUT) { + Serial.print(F("[DiagnosticsService] Scheduled Diagnostics upload!\n")); + Serial.print(F(" location = ")); + Serial.println(this->location); + Serial.print(F(" retries = ")); + Serial.print(this->retries); + Serial.print(F(", retryInterval = ")); + Serial.println(this->retryInterval); + Serial.print(F(" startTime = ")); + char dbuf [JSONDATE_LENGTH + 1] = {'\0'}; + this->startTime.toJsonString(dbuf, JSONDATE_LENGTH + 1); + Serial.print(F(" stopTime = ")); + this->stopTime.toJsonString(dbuf, JSONDATE_LENGTH + 1); + Serial.println(dbuf); + } + + nextTry = getOcppTime()->getOcppTimestampNow(); + nextTry += 10; //wait for 10s before upload + uploadIssued = false; + + return "diagnostics.log"; +} + + +DiagnosticsStatus DiagnosticsService::getDiagnosticsStatus() { + if (uploadIssued) { + if (uploadStatusSampler != NULL) { + switch (uploadStatusSampler()) { + case UploadStatus::NotUploaded: + return DiagnosticsStatus::Uploading; + case UploadStatus::Uploaded: + return DiagnosticsStatus::Uploaded; + case UploadStatus::UploadFailed: + return DiagnosticsStatus::UploadFailed; + } + } + return DiagnosticsStatus::Uploading; + } + return DiagnosticsStatus::Idle; +} + +OcppOperation *DiagnosticsService::getDiagnosticsStatusNotification() { + + if (getDiagnosticsStatus() != lastReportedStatus) { + lastReportedStatus = getDiagnosticsStatus(); + if (lastReportedStatus != DiagnosticsStatus::Idle) { + OcppMessage *diagNotificationMsg = new Ocpp16::DiagnosticsStatusNotification(lastReportedStatus); + OcppOperation *diagNotification = makeOcppOperation(diagNotificationMsg); + return diagNotification; + } + } + + return NULL; +} + +void DiagnosticsService::setOnUpload(std::function onUpload) { + this->onUpload = onUpload; +} + +void DiagnosticsService::setOnUploadStatusSampler(std::function uploadStatusSampler) { + this->uploadStatusSampler = uploadStatusSampler; +} + +#if !defined(AO_CUSTOM_DIAGNOSTICS) && !defined(AO_CUSTOM_WEBSOCKET) +#if defined(ESP32) || defined(ESP8266) + +DiagnosticsService *EspWiFi::makeDiagnosticsService() { + DiagnosticsService *diagService = new DiagnosticsService(); + + /* + * add onUpload and uploadStatusSampler when logging was implemented + */ + + return diagService; +} + +#endif //defined(ESP32) || defined(ESP8266) +#endif //!defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h new file mode 100644 index 00000000..050adf65 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h @@ -0,0 +1,71 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef DIAGNOSTICSSERVICE_H +#define DIAGNOSTICSSERVICE_H + +#include +#include +#include +#include +#include + +namespace ArduinoOcpp { + +enum class UploadStatus { + NotUploaded, + Uploaded, + UploadFailed +}; + +class DiagnosticsService { +private: + String location = String('\0'); + int retries = 0; + unsigned int retryInterval = 0; + OcppTimestamp startTime = OcppTimestamp(); + OcppTimestamp stopTime = OcppTimestamp(); + + OcppTimestamp nextTry = OcppTimestamp(); + + std::function onUpload = NULL; + std::function uploadStatusSampler = NULL; + bool uploadIssued = false; + + OcppOperation *getDiagnosticsStatusNotification(); + + Ocpp16::DiagnosticsStatus lastReportedStatus = Ocpp16::DiagnosticsStatus::Idle; + +public: + DiagnosticsService() = default; + + void loop(); + + //timestamps before year 2021 will be treated as "undefined" + //returns empty String if onUpload is missing or upload cannot be scheduled for another reason + //returns fileName of diagnostics file to be uploaded if upload has been scheduled + String requestDiagnosticsUpload(String &location, int retries = 1, unsigned int retryInterval = 0, OcppTimestamp startTime = OcppTimestamp(), OcppTimestamp stopTime = OcppTimestamp()); + + Ocpp16::DiagnosticsStatus getDiagnosticsStatus(); + + void setOnUpload(std::function onUpload); + + void setOnUploadStatusSampler(std::function uploadStatusSampler); +}; + +#if !defined(AO_CUSTOM_DIAGNOSTICS) && !defined(AO_CUSTOM_WEBSOCKET) +#if defined(ESP32) || defined(ESP8266) + +namespace EspWiFi { + +DiagnosticsService *makeDiagnosticsService(); + +} + +#endif //defined(ESP32) || defined(ESP8266) +#endif //!defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) + +} //end namespace ArduinoOcpp + +#endif From 1077b5bf6789cba5695e73a3096d2e49fa36bace Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 21 Oct 2021 15:56:56 +0200 Subject: [PATCH 081/696] add dbg messages --- .../Tasks/Diagnostics/DiagnosticsService.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp index ebb7e3f6..61a15363 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp @@ -20,6 +20,7 @@ void DiagnosticsService::loop() { if (!uploadIssued) { if (onUpload != NULL) { + if (DEBUG_OUT) Serial.println(F("[DiagnosticsService] call onUpload")); onUpload(location, startTime, stopTime); uploadIssued = true; } else { @@ -32,6 +33,7 @@ void DiagnosticsService::loop() { if (uploadIssued) { if (uploadStatusSampler != NULL && uploadStatusSampler() == UploadStatus::Uploaded) { //success! + if (DEBUG_OUT) Serial.println(F("[DiagnosticsService] end update routine (by status)")); uploadIssued = false; retries = 0; } @@ -44,6 +46,7 @@ void DiagnosticsService::loop() { if (uploadStatusSampler == NULL) { //No way to find out if failed. But maximum time has elapsed. Assume success + if (DEBUG_OUT) Serial.println(F("[DiagnosticsService] end update routine (by timer)")); uploadIssued = false; retries = 0; } else { @@ -93,6 +96,7 @@ String DiagnosticsService::requestDiagnosticsUpload(String &location, int retrie Serial.print(F(" startTime = ")); char dbuf [JSONDATE_LENGTH + 1] = {'\0'}; this->startTime.toJsonString(dbuf, JSONDATE_LENGTH + 1); + Serial.println(dbuf); Serial.print(F(" stopTime = ")); this->stopTime.toJsonString(dbuf, JSONDATE_LENGTH + 1); Serial.println(dbuf); @@ -102,6 +106,13 @@ String DiagnosticsService::requestDiagnosticsUpload(String &location, int retrie nextTry += 10; //wait for 10s before upload uploadIssued = false; + if (DEBUG_OUT) { + Serial.print(F("[DiagnosticsService] initial try at ")); + char dbuf [JSONDATE_LENGTH + 1] = {'\0'}; + nextTry.toJsonString(dbuf, JSONDATE_LENGTH + 1); + Serial.println(dbuf); + } + return "diagnostics.log"; } From 1b62dd523faf4731db910f686115312f335e066a Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 21 Oct 2021 16:05:59 +0200 Subject: [PATCH 082/696] add diagnostics to loop --- src/ArduinoOcpp.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index a4c6a1ed..ef88e07a 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -211,6 +211,10 @@ void OCPP_loop() { firmwareService->loop(); } + if (diagnosticsServce != NULL) { + diagnosticsServce->loop(); + } + bool evRequestsEnergyNewState = true; if (evRequestsEnergySampler != NULL) { evRequestsEnergyNewState = evRequestsEnergySampler(); From 743968eeb486f92ed5ca9c742e548fda9082189c Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 21 Oct 2021 17:16:18 +0200 Subject: [PATCH 083/696] fixed status codes --- .../MessagesV16/DiagnosticsStatusNotification.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp index 048b5e21..c68cf535 100644 --- a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp @@ -26,13 +26,13 @@ const char *DiagnosticsStatusNotification::cstrFromFwStatus(DiagnosticsStatus st return "Idle"; break; case (DiagnosticsStatus::Uploaded): - return "Downloaded"; + return "Uploaded"; break; case (DiagnosticsStatus::UploadFailed): - return "DownloadFailed"; + return "UploadFailed"; break; case (DiagnosticsStatus::Uploading): - return "Downloading"; + return "Uploading"; break; } return NULL; //cannot be reached From 4acf8a8a17c25cb1bd5d94f4033c78825319bb8d Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 26 Oct 2021 16:23:54 +0200 Subject: [PATCH 084/696] prepare for updated PIO package --- examples/{ESP32 => ESP}/main.ino | 33 ++++++++++++++++++- library.json | 24 ++++---------- .../Tasks/Diagnostics/DiagnosticsService.cpp | 2 +- 3 files changed, 40 insertions(+), 19 deletions(-) rename examples/{ESP32 => ESP}/main.ino (77%) diff --git a/examples/ESP32/main.ino b/examples/ESP/main.ino similarity index 77% rename from examples/ESP32/main.ino rename to examples/ESP/main.ino index 86629424..af2f56b6 100644 --- a/examples/ESP32/main.ino +++ b/examples/ESP/main.ino @@ -3,7 +3,15 @@ // MIT License #include +#if defined(ESP8266) +#include +#include +ESP8266WiFiMulti WiFiMulti; +#elif defined(ESP32) #include +#else +#error only ESP32 or ESP8266 supported at the moment +#endif #include @@ -30,13 +38,25 @@ void setup() { Serial.begin(115200); Serial.setDebugOutput(true); - WiFi.begin(STASSID, STAPSK); + Serial.print(F("[main] Wait for WiFi: ")); +#if defined(ESP8266) + WiFiMulti.addAP(STASSID, STAPSK); + while (WiFiMulti.run() != WL_CONNECTED) { + Serial.print('.'); + delay(1000); + } +#elif defined(ESP32) + WiFi.begin(STASSID, STAPSK); Serial.print(F("[main] Wait for WiFi: ")); while (!WiFi.isConnected()) { Serial.print('.'); delay(1000); } +#else +#error only ESP32 or ESP8266 supported at the moment +#endif + Serial.print(F(" connected!\n")); /* @@ -78,6 +98,17 @@ void loop() { */ OCPP_loop(); + /* + * Get transaction state of OCPP + */ + if (getTransactionId() > 0) { + //transaction running with txID given by getTransactionId() + } else if (getTransactionId() == 0) { + //transaction initiation is pending, i.e. startTransaction() was already sent, but hasn't come back yet. + } else { + //no transaction running at the moment + } + /* * Detect if something physical happened at your EVSE and trigger the corresponding OCPP messages */ diff --git a/library.json b/library.json index 7b40354e..c9346d18 100644 --- a/library.json +++ b/library.json @@ -1,7 +1,7 @@ { "name": "ArduinoOcpp", - "version": "0.0.2", - "description": "OCPP 1.6 Client for the ESP8266 and ESP32 (more coming soon)", + "version": "0.0.3", + "description": "OCPP 1.6 Client for the ESP8266 and ESP32", "keywords": "OCPP, 1.6, OCPP 1.6, Smart Energy, Smart Charging, client, ESP8266, ESP32, Arduino, EVSE, Charge Point", "repository": { @@ -46,27 +46,17 @@ "examples": [ { "name": "ESP32 / ESP8266 OCPP connection", - "base": "examples/ESP32", + "base": "examples/ESP", "files": [ "main.ino" ] }, { - "name": "ESP8266 OCPP test client", - "base": "examples/Simulation_without_HW", - "files": [ - "OneConnector_EVSE.ino", - "OneConnector_HW_integration.h", - "Simulated_HW.ino" - ] - }, - { - "name": "ESP8266 GPIO-based EVSE", - "base": "examples/OneConnector-EVSE", + "name": "ESP GPIO-based Supply Equipment Communications Controller (SECC)", + "base": "examples/SECC", "files": [ - "OneConnector_EVSE.ino", - "OneConnector_HW_integration.h", - "OneConnector_HW_integration.ino" + "main.ino", + "README.md" ] } ] diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp index 61a15363..640cb8db 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp @@ -103,7 +103,7 @@ String DiagnosticsService::requestDiagnosticsUpload(String &location, int retrie } nextTry = getOcppTime()->getOcppTimestampNow(); - nextTry += 10; //wait for 10s before upload + nextTry += 5; //wait for 5s before upload uploadIssued = false; if (DEBUG_OUT) { From a5f1aa3d23b4a474ed8628ea83c34a4a58b31748 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 4 Nov 2021 14:59:27 +0100 Subject: [PATCH 085/696] FW service: remove buildNumber from ctor --- src/ArduinoOcpp.cpp | 2 +- .../MessagesV16/ChangeConfiguration.cpp | 2 +- .../Tasks/FirmwareManagement/FirmwareService.cpp | 14 ++++++++++---- .../Tasks/FirmwareManagement/FirmwareService.h | 4 +++- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index ef88e07a..e8113e7f 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -163,7 +163,7 @@ void OCPP_initialize(OcppSocket *ocppSocket, float V_eff, ArduinoOcpp::Filesyste #if !defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) firmwareService = EspWiFi::makeFirmwareService("12345789"); //instantiate FW service + ESP installation routine #else - firmwareService = new FirmwareService("12345678"); //only instantiate FW service + firmwareService = new FirmwareService(); //only instantiate FW service #endif setFirmwareService(firmwareService); diff --git a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp index 9eef3135..9050cb9a 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp @@ -40,7 +40,7 @@ void ChangeConfiguration::processReq(JsonObject payload) { bool isFloat = false; float numFloat = -1.0f; bool isString = false; - const char *value_string = '\0'; + const char *value_string = "\0"; if (value.is()) { //Not enough. SteVe always sends numerical values as strings. Must also handle this isInt = true; diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index a22e43d7..17804838 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -12,8 +12,12 @@ using namespace ArduinoOcpp; using ArduinoOcpp::Ocpp16::FirmwareStatus; -FirmwareService::FirmwareService(const char *buildNumber) : buildNumber(buildNumber) { +void FirmwareService::setBuildNumber(const char *buildNumber) { + if (buildNumber == NULL) + return; + this->buildNumber = buildNumber; previousBuildNumber = declareConfiguration("BUILD_NUMBER", buildNumber, CONFIGURATION_FN, false, false, true, false); + checkedSuccessfulFwUpdate = false; //--> CS will be notified } void FirmwareService::loop() { @@ -234,7 +238,7 @@ OcppOperation *FirmwareService::getFirmwareStatusNotification() { /* * Check if FW has been updated previously, but only once */ - if (!checkedSuccessfulFwUpdate) { + if (!checkedSuccessfulFwUpdate && buildNumber != NULL && previousBuildNumber != NULL) { checkedSuccessfulFwUpdate = true; size_t buildNoSize = previousBuildNumber->getBuffsize(); @@ -290,7 +294,8 @@ void FirmwareService::resetStage() { #include FirmwareService *EspWiFi::makeFirmwareService(const char *buildNumber) { - FirmwareService *fwService = new FirmwareService(buildNumber); + FirmwareService *fwService = new FirmwareService(); + fwService->setBuildNumber(buildNumber); /* * example of how to integrate a separate download phase (optional) @@ -352,7 +357,8 @@ FirmwareService *EspWiFi::makeFirmwareService(const char *buildNumber) { #include FirmwareService *EspWiFi::makeFirmwareService(const char *buildNumber) { - FirmwareService *fwService = new FirmwareService(buildNumber); + FirmwareService *fwService = new FirmwareService(); + fwService->setBuildNumber(buildNumber); fwService->setOnInstall([fwService = fwService] (String &location) { diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h index b6b7b37d..1426d365 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h @@ -69,7 +69,9 @@ class FirmwareService { OcppOperation *getFirmwareStatusNotification(); public: - FirmwareService(const char *buildNumber); + FirmwareService() { } + + void setBuildNumber(const char *buildNumber); void loop(); From c43e84f1a5528787eed1c407d3256d8df0fe873b Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 4 Nov 2021 17:26:08 +0100 Subject: [PATCH 086/696] fixed unnecessary allocation of empty payloads --- src/ArduinoOcpp/Core/OcppMessage.cpp | 4 +++- src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp | 4 ++-- src/ArduinoOcpp/MessagesV16/Heartbeat.cpp | 2 +- src/ArduinoOcpp/MessagesV16/MeterValues.cpp | 2 +- src/ArduinoOcpp/MessagesV16/StatusNotification.cpp | 4 ++-- src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp | 4 ++-- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/ArduinoOcpp/Core/OcppMessage.cpp b/src/ArduinoOcpp/Core/OcppMessage.cpp index ebd7e1db..43f34c23 100644 --- a/src/ArduinoOcpp/Core/OcppMessage.cpp +++ b/src/ArduinoOcpp/Core/OcppMessage.cpp @@ -23,7 +23,9 @@ void OcppMessage::initiate() { DynamicJsonDocument* OcppMessage::createReq() { Serial.print(F("[OcppMessage] Unsupported operation: createReq() is not implemented!\n")); - return new DynamicJsonDocument(0); + DynamicJsonDocument* doc = new DynamicJsonDocument(0); + doc->to(); + return doc; } void OcppMessage::processConf(JsonObject payload) { diff --git a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp index bec49fde..4dab701c 100644 --- a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp @@ -59,8 +59,8 @@ DynamicJsonDocument* GetDiagnostics::createConf(){ } if (fileName.isEmpty()) { - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); - JsonObject payload = doc->to(); + DynamicJsonDocument* doc = new DynamicJsonDocument(0); + doc->to(); return doc; } else { DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + fileName.length() + 1); diff --git a/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp b/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp index b4ece933..836e2f8b 100644 --- a/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp +++ b/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp @@ -19,7 +19,7 @@ const char* Heartbeat::getOcppOperationType(){ DynamicJsonDocument* Heartbeat::createReq() { DynamicJsonDocument *doc = new DynamicJsonDocument(0); - //JsonObject payload = doc->to(); + doc->to(); /* * Empty payload */ diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp index 211fd822..fa09c03a 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp @@ -105,7 +105,7 @@ void MeterValues::processReq(JsonObject payload) { DynamicJsonDocument* MeterValues::createConf(){ DynamicJsonDocument* doc = new DynamicJsonDocument(0); - //JsonObject payload = doc->to(); + doc->to(); /* * empty payload */ diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp index 66da8bd5..00de7d07 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp @@ -137,7 +137,7 @@ void StatusNotification::processReq(JsonObject payload) { * For debugging only */ DynamicJsonDocument* StatusNotification::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); - JsonObject payload = doc->to(); + DynamicJsonDocument* doc = new DynamicJsonDocument(0); + doc->to(); return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp index f98b27d2..e7767802 100644 --- a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp +++ b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp @@ -39,8 +39,8 @@ void UpdateFirmware::processReq(JsonObject payload) { } DynamicJsonDocument* UpdateFirmware::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); - JsonObject payload = doc->to(); + DynamicJsonDocument* doc = new DynamicJsonDocument(0); + doc->to(); FirmwareService *fwService = getFirmwareService(); if (fwService != NULL) { From 469167e997f2427a6308430631645c573753828f Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 10 Nov 2021 17:35:01 +0100 Subject: [PATCH 087/696] update dependencies --- README.md | 2 +- platformio.ini | 8 ++++---- src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp | 2 +- .../Tasks/SmartCharging/SmartChargingModel.cpp | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8fb42255..5b2339be 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ To get started quickly with or without EVSE hardware, you can flash the sketch i ## Dependencies -- [bblanchon/ArduinoJSON](https://github.com/bblanchon/ArduinoJson) (Please use version 6.17.2) +- [bblanchon/ArduinoJSON](https://github.com/bblanchon/ArduinoJson) - [Links2004/arduinoWebSockets](https://github.com/Links2004/arduinoWebSockets) In case you use PlatformIO, you can copy all dependencies from `platformio.ini` into your own configuration file. Alternatively, you can install the full library with dependencies by adding `matth-x/ArduinoOcpp` in the PIO library manager. diff --git a/platformio.ini b/platformio.ini index 9fdff69f..3fad0479 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,14 +13,14 @@ default_envs = esp32-development-board [common] framework = arduino lib_deps = - bblanchon/ArduinoJson@6.17.2 - links2004/WebSockets@2.2.0 + bblanchon/ArduinoJson@6.18.5 + links2004/WebSockets@2.3.6 build_flags = -D USE_FACADE=true monitor_speed = 115200 [env:nodemcuv2] -platform = espressif8266 +platform = espressif8266@2.6.3 board = nodemcuv2 framework = ${common.framework} lib_deps = ${common.lib_deps} @@ -31,7 +31,7 @@ build_flags = -DAO_TRAFFIC_OUT [env:esp32-development-board] -platform = espressif32@1.11.1 +platform = espressif32@3.3.2 board = esp-wrover-kit framework = ${common.framework} lib_deps = diff --git a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp index 74fd0ad5..cf0d721f 100644 --- a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp @@ -19,7 +19,7 @@ #endif #define KEY_MAXLEN 60 -#define STRING_VAL_MAXLEN 500 +#define STRING_VAL_MAXLEN 2000 //allow TLS certificates in ... namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp index b0fe0a4b..ee661f33 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp @@ -117,7 +117,7 @@ ChargingSchedule::ChargingSchedule(OcppTimestamp &startT, int duration) { startSchedule = startT; schedulingUnit = 'W'; //either 'A' or 'W' std::vector chargingSchedulePeriod = std::vector(); - float minChargingRate = 0.f; + //float minChargingRate = 0.f; chargingProfileKind = ChargingProfileKindType::Absolute; //copied from ChargingProfile to increase cohesion of limit inferencing methods recurrencyKind = RecurrencyKindType::NOT_SET; //copied from ChargingProfile to increase cohesion of limit inferencing methods From 57cc97006796ee45fec374b8c8f597271e4514bc Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sun, 14 Nov 2021 23:56:07 +0100 Subject: [PATCH 088/696] update dependencies fixes --- examples/ESP/{main.ino => main.cpp} | 0 ...eConnector_EVSE.ino => OneConnector_EVSE.cpp} | 0 ...ation.ino => OneConnector_HW_integration.cpp} | 0 examples/SECC/{main.ino => main.cpp} | 16 ++++++++++++++-- 4 files changed, 14 insertions(+), 2 deletions(-) rename examples/ESP/{main.ino => main.cpp} (100%) rename examples/OneConnector-EVSE/{OneConnector_EVSE.ino => OneConnector_EVSE.cpp} (100%) rename examples/OneConnector-EVSE/{OneConnector_HW_integration.ino => OneConnector_HW_integration.cpp} (100%) rename examples/SECC/{main.ino => main.cpp} (96%) diff --git a/examples/ESP/main.ino b/examples/ESP/main.cpp similarity index 100% rename from examples/ESP/main.ino rename to examples/ESP/main.cpp diff --git a/examples/OneConnector-EVSE/OneConnector_EVSE.ino b/examples/OneConnector-EVSE/OneConnector_EVSE.cpp similarity index 100% rename from examples/OneConnector-EVSE/OneConnector_EVSE.ino rename to examples/OneConnector-EVSE/OneConnector_EVSE.cpp diff --git a/examples/OneConnector-EVSE/OneConnector_HW_integration.ino b/examples/OneConnector-EVSE/OneConnector_HW_integration.cpp similarity index 100% rename from examples/OneConnector-EVSE/OneConnector_HW_integration.ino rename to examples/OneConnector-EVSE/OneConnector_HW_integration.cpp diff --git a/examples/SECC/main.ino b/examples/SECC/main.cpp similarity index 96% rename from examples/SECC/main.ino rename to examples/SECC/main.cpp index a921fb79..de8a621b 100644 --- a/examples/SECC/main.ino +++ b/examples/SECC/main.cpp @@ -188,9 +188,21 @@ void setup() { if (ocppUrlParsed.isTLS) { if (CA_cert->getBuffsize() > 0) { + //TODOS: ESP8266: limit BearSSL buffsize + configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov"); //alternatively: settimeofday(&de, &tz); + PRINT(F("[main] Wait for NTP time (used for certificate validation) ")); + time_t now = time(nullptr); + while (now < 8 * 3600 * 2) { + delay(500); + PRINT('.'); + now = time(nullptr); + } + PRINT(F(" finished. Unix timestamp is ")); + PRINTLN(now); + wSock->beginSslWithCA(ocppUrlParsed.host.c_str(), ocppUrlParsed.port, ocppUrlParsed.url.c_str(), *CA_cert, "ocpp1.6"); } else { - wSock->beginSSL(ocppUrlParsed.host, ocppUrlParsed.port, ocppUrlParsed.url, "", "ocpp1.6"); + wSock->beginSSL(ocppUrlParsed.host.c_str(), ocppUrlParsed.port, ocppUrlParsed.url.c_str(), NULL, "ocpp1.6"); } } else { wSock->begin(ocppUrlParsed.host, ocppUrlParsed.port, ocppUrlParsed.url, "ocpp1.6"); @@ -361,7 +373,7 @@ bool runWiFiManager() { WiFiManagerParameter divider("

"); wifiManager.addParameter(÷r); - WiFiManagerParameter caCertParam ("caCert", "CA Certificate", "", 150,"placeholder=\"Paste here or leave blank\""); + WiFiManagerParameter caCertParam ("caCert", "CA Certificate", "", 1500,"placeholder=\"Paste here or leave blank\""); wifiManager.addParameter(&caCertParam); WiFiManagerParameter caCertSavePararm ("
"); wifiManager.addParameter(&caCertSavePararm); From ad3f2c15f23908e8a0397c69ce56739f46e28f0e Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 19 Nov 2021 18:44:47 +0100 Subject: [PATCH 089/696] fix compiler warnings --- examples/SECC/README.md | 2 ++ src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp | 2 +- .../Tasks/FirmwareManagement/FirmwareService.cpp | 10 +++++----- .../Tasks/SmartCharging/SmartChargingService.cpp | 2 +- .../Tasks/SmartCharging/SmartChargingService.h | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/examples/SECC/README.md b/examples/SECC/README.md index be44e908..a6ba7315 100644 --- a/examples/SECC/README.md +++ b/examples/SECC/README.md @@ -15,6 +15,8 @@ You can find the interface descriptions in `main.ino`. Please be aware that alth - Copy the `main.ino` from this example folder into your source directory. Adapt the pinout settings if needed. - Finished. You should be able to compile and upload the sketch onto your ESP. +When booting, the ESP opens up the Wi-Fi configuration portal. Please connect your PC to the network with the SSID `EVSE_Maintenance_Portal` within the first 30s of the boot routine of the ESP (the portal has a timeout). The passphrase is `myEvseController`. + ## Standalone mode If you just want to check out ArduinoOcpp you can run this sketch on an ESP without any peripherals. The pinout of the ESP8266-NodeMCU board is already fully configured in `main.ino` so that board is the most convenient to start testing. diff --git a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp index 7108d44f..e39a7173 100644 --- a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp +++ b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp @@ -20,7 +20,7 @@ const char* ClearChargingProfile::getOcppOperationType(){ void ClearChargingProfile::processReq(JsonObject payload) { - std::function filter = [payload = payload] + std::function filter = [payload] (int chargingProfileId, int connectorId, ChargingProfilePurposeType chargingProfilePurpose, int stackLevel) { if (payload.containsKey("id")) { diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index 17804838..ee6ae7d6 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -307,14 +307,14 @@ FirmwareService *EspWiFi::makeFirmwareService(const char *buildNumber) { return true; }); - fwService->setDownloadStatusSampler([fwService = fwService] () { + fwService->setDownloadStatusSampler([] () { //report the download progress //... return DownloadStatus::NotDownloaded; }); #endif //separate download phase - fwService->setOnInstall([fwService = fwService] (String &location) { + fwService->setOnInstall([fwService] (String &location) { fwService->setInstallationStatusSampler([](){return InstallationStatus::NotInstalled;}); @@ -345,7 +345,7 @@ FirmwareService *EspWiFi::makeFirmwareService(const char *buildNumber) { return true; }); - fwService->setInstallationStatusSampler([fwService = fwService] () { + fwService->setInstallationStatusSampler([] () { return InstallationStatus::NotInstalled; }); @@ -360,7 +360,7 @@ FirmwareService *EspWiFi::makeFirmwareService(const char *buildNumber) { FirmwareService *fwService = new FirmwareService(); fwService->setBuildNumber(buildNumber); - fwService->setOnInstall([fwService = fwService] (String &location) { + fwService->setOnInstall([fwService] (String &location) { WiFiClient client; //WiFiClientSecure client; @@ -390,7 +390,7 @@ FirmwareService *EspWiFi::makeFirmwareService(const char *buildNumber) { return true; }); - fwService->setInstallationStatusSampler([fwService = fwService] () { + fwService->setInstallationStatusSampler([] () { return InstallationStatus::NotInstalled; }); diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index 53ac6002..583b07b5 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -312,7 +312,7 @@ ChargingProfile *SmartChargingService::updateProfileStack(JsonObject *json){ return chargingProfile; } -bool SmartChargingService::clearChargingProfile(std::function filter) { +bool SmartChargingService::clearChargingProfile(const std::function& filter) { int nMatches = 0; ChargingProfile **profileStacks [] = {ChargePointMaxProfile, TxDefaultProfile, TxProfile}; diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h index 561bde6d..b436a9ea 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h @@ -46,7 +46,7 @@ class SmartChargingService { //void beginChargingNow(); //void endChargingNow(); void updateChargingProfile(JsonObject *json); - bool clearChargingProfile(std::function filter); + bool clearChargingProfile(const std::function& filter); void inferenceLimit(const OcppTimestamp &t, float *limit, OcppTimestamp *validTo); float inferenceLimitNow(); void setOnLimitChange(OnLimitChange onLimitChange); From 11419e384ccd23df15f7ef9b8d7d9dd1264ddd89 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 1 Dec 2021 15:54:59 +0100 Subject: [PATCH 090/696] adopt more C++ mem mgt. features, tidy up --- library.json | 7 +- platformio.ini | 3 +- src/ArduinoOcpp.cpp | 64 +- src/ArduinoOcpp.h | 10 +- src/ArduinoOcpp/Core/OcppConnection.cpp | 244 ++++---- src/ArduinoOcpp/Core/OcppConnection.h | 16 +- src/ArduinoOcpp/Core/OcppEngine.cpp | 423 +------------ src/ArduinoOcpp/Core/OcppEngine.h | 19 +- src/ArduinoOcpp/Core/OcppOperation.cpp | 558 +++++++++--------- src/ArduinoOcpp/Core/OcppOperation.h | 92 +-- .../MessagesV16/TriggerMessage.cpp | 35 +- src/ArduinoOcpp/MessagesV16/TriggerMessage.h | 12 +- .../SimpleOcppOperationFactory.cpp | 255 ++++---- src/ArduinoOcpp/SimpleOcppOperationFactory.h | 12 +- .../ChargePointStatusService.cpp | 8 +- .../Tasks/Diagnostics/DiagnosticsService.cpp | 22 +- .../Tasks/Diagnostics/DiagnosticsService.h | 2 +- .../FirmwareManagement/FirmwareService.cpp | 42 +- .../FirmwareManagement/FirmwareService.h | 2 +- .../Tasks/Heartbeat/HeartbeatService.cpp | 4 +- .../Tasks/Metering/MeteringService.cpp | 8 +- 21 files changed, 694 insertions(+), 1144 deletions(-) diff --git a/library.json b/library.json index c9346d18..06a129ae 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "ArduinoOcpp", - "version": "0.0.3", + "version": "0.0.4", "description": "OCPP 1.6 Client for the ESP8266 and ESP32", "keywords": "OCPP, 1.6, OCPP 1.6, Smart Energy, Smart Charging, client, ESP8266, ESP32, Arduino, EVSE, Charge Point", "repository": @@ -39,7 +39,8 @@ [ "src/OneConnector*", "src/Simulated_HW*", - "src/sdkconfig*" + "src/sdkconfig*", + "examples/SECC/WiFiManager*" ] }, @@ -52,7 +53,7 @@ ] }, { - "name": "ESP GPIO-based Supply Equipment Communications Controller (SECC)", + "name": "ESP GPIO-based Supply Equipment Communications Controller SECC", "base": "examples/SECC", "files": [ "main.ino", diff --git a/platformio.ini b/platformio.ini index 3fad0479..f1b00615 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,7 +13,8 @@ default_envs = esp32-development-board [common] framework = arduino lib_deps = - bblanchon/ArduinoJson@6.18.5 +; bblanchon/ArduinoJson@6.18.5 + https://github.com/bblanchon/ArduinoJson.git#b06bbd9d2a9b8e6eb0d48602e107c53e1aa9689b links2004/WebSockets@2.3.6 build_flags = -D USE_FACADE=true diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index e8113e7f..7d527cdc 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -161,7 +161,7 @@ void OCPP_initialize(OcppSocket *ocppSocket, float V_eff, ArduinoOcpp::Filesyste heartbeatService = new HeartbeatService(); #if !defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) - firmwareService = EspWiFi::makeFirmwareService("12345789"); //instantiate FW service + ESP installation routine + firmwareService = EspWiFi::makeFirmwareService("1234578901"); //instantiate FW service + ESP installation routine #else firmwareService = new FirmwareService(); //only instantiate FW service #endif @@ -332,10 +332,9 @@ void setOnResetReceiveReq(OnReceiveReqListener onReceiveReq) { setOnResetReceiveRequestListener(onReceiveReq); } -void authorize(String &idTag, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, Timeout *timeout) { - OcppOperation *authorize = makeOcppOperation( +void authorize(String &idTag, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { + auto authorize = makeOcppOperation( new Authorize(idTag)); - initiateOcppOperation(authorize); if (onConf) authorize->setOnReceiveConfListener(onConf); if (onAbort) @@ -345,15 +344,15 @@ void authorize(String &idTag, OnReceiveConfListener onConf, OnAbortListener onAb if (onError) authorize->setOnReceiveErrorListener(onError); if (timeout) - authorize->setTimeout(timeout); + authorize->setTimeout(std::move(timeout)); else - authorize->setTimeout(new FixedTimeout(20000)); + authorize->setTimeout(std::unique_ptr(new FixedTimeout(20000))); + initiateOcppOperation(std::move(authorize)); } -void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, Timeout *timeout) { - OcppOperation *bootNotification = makeOcppOperation( +void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { + auto bootNotification = makeOcppOperation( new BootNotification(chargePointModel, chargePointVendor)); - initiateOcppOperation(bootNotification); if (onConf) bootNotification->setOnReceiveConfListener(onConf); if (onAbort) @@ -363,23 +362,23 @@ void bootNotification(String chargePointModel, String chargePointVendor, OnRecei if (onError) bootNotification->setOnReceiveErrorListener(onError); if (timeout) - bootNotification->setTimeout(timeout); + bootNotification->setTimeout(std::move(timeout)); else - bootNotification->setTimeout(new SuppressedTimeout()); + bootNotification->setTimeout(std::unique_ptr (new SuppressedTimeout())); + initiateOcppOperation(std::move(bootNotification)); } void bootNotification(String &chargePointModel, String &chargePointVendor, String &chargePointSerialNumber, OnReceiveConfListener onConf) { - OcppOperation *bootNotification = makeOcppOperation( + auto bootNotification = makeOcppOperation( new BootNotification(chargePointModel, chargePointVendor, chargePointSerialNumber)); - initiateOcppOperation(bootNotification); bootNotification->setOnReceiveConfListener(onConf); - bootNotification->setTimeout(new SuppressedTimeout()); + bootNotification->setTimeout(std::unique_ptr (new SuppressedTimeout())); + initiateOcppOperation(std::move(bootNotification)); } -void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, Timeout *timeout) { - OcppOperation *bootNotification = makeOcppOperation( +void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { + auto bootNotification = makeOcppOperation( new BootNotification(payload)); - initiateOcppOperation(bootNotification); if (onConf) bootNotification->setOnReceiveConfListener(onConf); if (onAbort) @@ -389,15 +388,15 @@ void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf if (onError) bootNotification->setOnReceiveErrorListener(onError); if (timeout) - bootNotification->setTimeout(timeout); + bootNotification->setTimeout(std::move(timeout)); else - bootNotification->setTimeout(new SuppressedTimeout()); + bootNotification->setTimeout(std::unique_ptr(new SuppressedTimeout())); + initiateOcppOperation(std::move(bootNotification)); } -void startTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, Timeout *timeout) { - OcppOperation *startTransaction = makeOcppOperation( +void startTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { + auto startTransaction = makeOcppOperation( new StartTransaction(OCPP_ID_OF_CONNECTOR)); - initiateOcppOperation(startTransaction); if (onConf) startTransaction->setOnReceiveConfListener(onConf); if (onAbort) @@ -407,23 +406,23 @@ void startTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnT if (onError) startTransaction->setOnReceiveErrorListener(onError); if (timeout) - startTransaction->setTimeout(timeout); + startTransaction->setTimeout(std::move(timeout)); else - startTransaction->setTimeout(new SuppressedTimeout()); + startTransaction->setTimeout(std::unique_ptr(new SuppressedTimeout())); + initiateOcppOperation(std::move(startTransaction)); } void startTransaction(String &idTag, OnReceiveConfListener onConf) { - OcppOperation *startTransaction = makeOcppOperation( + auto startTransaction = makeOcppOperation( new StartTransaction(OCPP_ID_OF_CONNECTOR, idTag)); - initiateOcppOperation(startTransaction); startTransaction->setOnReceiveConfListener(onConf); - startTransaction->setTimeout(new SuppressedTimeout()); + startTransaction->setTimeout(std::unique_ptr(new SuppressedTimeout())); + initiateOcppOperation(std::move(startTransaction)); } -void stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, Timeout *timeout) { - OcppOperation *stopTransaction = makeOcppOperation( +void stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { + auto stopTransaction = makeOcppOperation( new StopTransaction(OCPP_ID_OF_CONNECTOR)); - initiateOcppOperation(stopTransaction); if (onConf) stopTransaction->setOnReceiveConfListener(onConf); if (onAbort) @@ -433,9 +432,10 @@ void stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTi if (onError) stopTransaction->setOnReceiveErrorListener(onError); if (timeout) - stopTransaction->setTimeout(timeout); + stopTransaction->setTimeout(std::move(timeout)); else - stopTransaction->setTimeout(new SuppressedTimeout()); + stopTransaction->setTimeout(std::unique_ptr(new SuppressedTimeout())); + initiateOcppOperation(std::move(stopTransaction)); } //void startEvDrawsEnergy() { diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index ab6b8088..43eba1d8 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -104,16 +104,16 @@ void setOnResetReceiveReq(OnReceiveReqListener onReceiveReq); //alternative: sta * in any case. */ -void authorize(String &idTag, OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL, Timeout *timeout = NULL); +void authorize(String &idTag, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); -void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL, Timeout *timeout = NULL); +void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); //The OCPP operation will include the given payload without modifying it. The library will delete the payload object by itself. -void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL, Timeout *timeout = NULL); +void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); -void startTransaction(OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL, Timeout *timeout = NULL); +void startTransaction(OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); -void stopTransaction(OnReceiveConfListener onConf = NULL, OnAbortListener onAbort = NULL, OnTimeoutListener onTimeout = NULL, OnReceiveErrorListener onError = NULL, Timeout *timeout = NULL); +void stopTransaction(OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); /* * Provide hardware-related information II diff --git a/src/ArduinoOcpp/Core/OcppConnection.cpp b/src/ArduinoOcpp/Core/OcppConnection.cpp index 6db6238b..5a6faa18 100644 --- a/src/ArduinoOcpp/Core/OcppConnection.cpp +++ b/src/ArduinoOcpp/Core/OcppConnection.cpp @@ -24,22 +24,19 @@ OcppConnection::OcppConnection(OcppSocket *ocppSock) : ocppSock(ocppSock) { } void OcppConnection::loop() { + /** - * Work through the initiatedOcppOperations queue. Start with the first element by calling req() on it. If - * the operation is (still) pending, it will return false and therefore each other queued element must - * wait until this operation is over. If an ocppOperation is finished, it returns true on a req() call, - * can be dequeued and the following ocppOperation is processed. - */ + * Work through the initiatedOcppOperations queue. Start with the first element by calling req() on it. If + * the operation is (still) pending, it will return false and therefore each other queued element must + * wait until this operation is over. If an ocppOperation is finished, it returns true on a req() call, + * can be dequeued and the following ocppOperation is processed. + */ auto operation = initiatedOcppOperations.begin(); while (operation != initiatedOcppOperations.end()){ - boolean timeout = (*operation)->sendReq(ocppSock); //The only reason to dequeue elements here is when a timeout occurs. Normally - if (timeout){ //the Conf msg processing routine dequeues finished elements - delete *operation; + boolean timeout = (*operation)->sendReq(*ocppSock); //The only reason to dequeue elements here is when a timeout occurs. Normally + if (timeout){ //the Conf msg processing routine dequeues finished elements operation = initiatedOcppOperations.erase(operation); - - //TODO Review: are all recources freed here? - //go on with the next element in the queue, which is now at initiatedOcppOperations[0] } else { //there is one operation pending right now, so quit this while-loop. break; @@ -65,7 +62,6 @@ void OcppConnection::loop() { if (timer->isExceeded()) { Serial.print(F("[OcppEngine] Discarding operation due to timeout: ")); (*operation)->print_debug(); - delete *operation; operation = initiatedOcppOperations.erase(operation); } else { ++operation; @@ -79,171 +75,154 @@ void OcppConnection::loop() { operation = receivedOcppOperations.begin(); while (operation != receivedOcppOperations.end()){ - boolean success = (*operation)->sendConf(ocppSock); + boolean success = (*operation)->sendConf(*ocppSock); if (success){ - delete *operation; operation = receivedOcppOperations.erase(operation); - - //TODO Review: are all recources freed here? - //go on with the next element in the queue, which is now at receivedOcppOperations[i] } else { //There will be another attempt to send this conf message in a future loop call. //Go on with the next element in the queue, which is now at receivedOcppOperations[i+1] - ++operation; //TODO review: this makes conf's out-of-order. But if the first Op fails because of lacking RAM, this could save the device. + ++operation; //TODO review: this makes confs out-of-order. But if the first Op fails because of lacking RAM, this could save the device. } } } -void OcppConnection::initiateOcppOperation(OcppOperation *o){ - if (!o->isFullyConfigured()){ - Serial.printf("[OcppEngine] initiateOcppOperation(op) was called without the operation being configured and ready to send. Discard operation!\n"); - delete o; - return; - } - initiatedOcppOperations.push_back(o); - o->setInitiated(); +void OcppConnection::initiateOcppOperation(std::unique_ptr o){ + if (!o) { + Serial.printf("[OcppEngine] initiateOcppOperation(op) was called with null. Ignore\n"); + return; + } + if (!o->isFullyConfigured()){ + Serial.printf("[OcppEngine] initiateOcppOperation(op) was called without the operation being configured and ready to send. Discard operation!\n"); + return; //o gets destroyed + } + o->setInitiated(); + initiatedOcppOperations.push_back(std::move(o)); } bool OcppConnection::processOcppSocketInputTXT(const char* payload, size_t length) { - boolean deserializationSuccess = false; + boolean deserializationSuccess = false; - DynamicJsonDocument *doc = NULL; - size_t capacity = length + 100; + auto doc = std::unique_ptr{nullptr}; + size_t capacity = length + 100; - DeserializationError err = DeserializationError::NoMemory; - while (capacity + HEAP_GUARD < ESP.getFreeHeap() && err == DeserializationError::NoMemory) { - if (doc){ - delete doc; - doc = NULL; - } - doc = new DynamicJsonDocument(capacity); - err = deserializeJson(*doc, payload, length); + DeserializationError err = DeserializationError::NoMemory; + while (capacity + HEAP_GUARD < ESP.getFreeHeap() && err == DeserializationError::NoMemory) { + doc = std::unique_ptr(new DynamicJsonDocument(capacity)); + err = deserializeJson(*doc, payload, length); - capacity /= 2; - capacity *= 3; - } - - //TODO insert validateRpcHeader at suitable position - - switch (err.code()) { - case DeserializationError::Ok: - if (doc != NULL){ - int messageTypeId = (*doc)[0]; - switch(messageTypeId) { - case MESSAGE_TYPE_CALL: - handleReqMessage(doc); - deserializationSuccess = true; - break; - case MESSAGE_TYPE_CALLRESULT: - handleConfMessage(doc); - deserializationSuccess = true; - break; - case MESSAGE_TYPE_CALLERROR: - handleErrMessage(doc); - deserializationSuccess = true; - break; - default: - Serial.print(F("[OcppEngine] Invalid OCPP message! (though JSON has successfully been deserialized)\n")); - break; - } - } else { //unlikely corner case - Serial.print(F("[OcppEngine] Deserialization is okay but doc is Null\n")); - } - break; - case DeserializationError::InvalidInput: - Serial.print(F("[OcppEngine] Invalid input! Not a JSON\n")); - break; - case DeserializationError::NoMemory: - { - if (DEBUG_OUT) Serial.print(F("[OcppEngine] Error: Not enough memory in heap! Input length = ")); - if (DEBUG_OUT) Serial.print(length); - if (DEBUG_OUT) Serial.print(F(", free heap = ")); - if (DEBUG_OUT) Serial.println(ESP.getFreeHeap()); - - /* - * If websocket input is of message type MESSAGE_TYPE_CALL, send back a message of type MESSAGE_TYPE_CALLERROR. - * Then the communication counterpart knows that this operation failed. - * If the input type is MESSAGE_TYPE_CALLRESULT, it can be ignored. This controller will automatically resend the corresponding requirement message. - */ - - if (doc){ - delete doc; - doc = NULL; - } + capacity /= 2; + capacity *= 3; + } - doc = new DynamicJsonDocument(200); - char onlyRpcHeader[200]; - size_t onlyRpcHeader_len = removePayload(payload, length, onlyRpcHeader, 200); - DeserializationError err2 = deserializeJson(*doc, onlyRpcHeader, onlyRpcHeader_len); - if (err2.code() == DeserializationError::Ok) { - int messageTypeId2 = (*doc)[0] | -1; - if (messageTypeId2 == MESSAGE_TYPE_CALL) { - deserializationSuccess = true; - OcppOperation *op = makeOcppOperation(new OutOfMemory(ESP.getFreeHeap(), length)); - handleReqMessage(doc, op); + //TODO insert validateRpcHeader at suitable position + + switch (err.code()) { + case DeserializationError::Ok: + if (doc != nullptr){ + int messageTypeId = (*doc)[0] | -1; + switch(messageTypeId) { + case MESSAGE_TYPE_CALL: + handleReqMessage(*doc); + deserializationSuccess = true; + break; + case MESSAGE_TYPE_CALLRESULT: + handleConfMessage(*doc); + deserializationSuccess = true; + break; + case MESSAGE_TYPE_CALLERROR: + handleErrMessage(*doc); + deserializationSuccess = true; + break; + default: + Serial.print(F("[OcppEngine] Invalid OCPP message! (though JSON has successfully been deserialized)\n")); + break; + } + } else { //unlikely corner case + Serial.print(F("[OcppEngine] Deserialization is okay but doc is nullptr\n")); } - } - } - break; - default: - Serial.print(F("[OcppEngine] Deserialization failed: ")); - Serial.println(err.c_str()); - break; - } - - if (doc) { - delete doc; - } + break; + case DeserializationError::InvalidInput: + Serial.print(F("[OcppEngine] Invalid input! Not a JSON\n")); + break; + case DeserializationError::NoMemory: + { + if (DEBUG_OUT) Serial.print(F("[OcppEngine] Error: Not enough memory in heap! Input length = ")); + if (DEBUG_OUT) Serial.print(length); + if (DEBUG_OUT) Serial.print(F(", free heap = ")); + if (DEBUG_OUT) Serial.println(ESP.getFreeHeap()); + + /* + * If websocket input is of message type MESSAGE_TYPE_CALL, send back a message of type MESSAGE_TYPE_CALLERROR. + * Then the communication counterpart knows that this operation failed. + * If the input type is MESSAGE_TYPE_CALLRESULT, it can be ignored. This controller will automatically resend the corresponding request message. + */ + + doc = std::unique_ptr(new DynamicJsonDocument(200)); + char onlyRpcHeader[200]; + size_t onlyRpcHeader_len = removePayload(payload, length, onlyRpcHeader, sizeof(onlyRpcHeader)); + DeserializationError err2 = deserializeJson(*doc, onlyRpcHeader, onlyRpcHeader_len); + if (err2.code() == DeserializationError::Ok) { + int messageTypeId2 = (*doc)[0] | -1; + if (messageTypeId2 == MESSAGE_TYPE_CALL) { + deserializationSuccess = true; + auto op = makeOcppOperation(new OutOfMemory(ESP.getFreeHeap(), length)); + handleReqMessage(*doc, std::move(op)); + } + } + } + break; + default: + Serial.print(F("[OcppEngine] Deserialization failed: ")); + Serial.println(err.c_str()); + break; + } - return deserializationSuccess; + return deserializationSuccess; } /** - * call conf() on each element of the queue. Start with first element. On successful message delivery, - * delete the element from the list. Try all the pending OCPP Operations until the right one is found. - * - * This function could result in improper behavior in Charging Stations, because messages are not - * guaranteed to be received and therefore processed in the right order. - */ -void OcppConnection::handleConfMessage(JsonDocument *json) { + * call conf() on each element of the queue. Start with first element. On successful message delivery, + * delete the element from the list. Try all the pending OCPP Operations until the right one is found. + * + * This function could result in improper behavior in Charging Stations, because messages are not + * guaranteed to be received and therefore processed in the right order. + */ +void OcppConnection::handleConfMessage(JsonDocument& json) { for (auto operation = initiatedOcppOperations.begin(); operation != initiatedOcppOperations.end(); ++operation) { boolean success = (*operation)->receiveConf(json); //maybe rename to "consumed"? if (success) { - delete *operation; initiatedOcppOperations.erase(operation); return; } } - //didn't find matching OcppOperation - Serial.print(F("[OcppEngine] Received CALLRESULT doesn't match any pending operation!\n")); + //didn't find matching OcppOperation + Serial.print(F("[OcppEngine] Received CALLRESULT doesn't match any pending operation!\n")); } -void OcppConnection::handleReqMessage(JsonDocument *json) { - OcppOperation *req = makeFromJson(json); - if (req == NULL) { +void OcppConnection::handleReqMessage(JsonDocument& json) { + auto req = makeFromJson(json); + if (req == nullptr) { Serial.print(F("[OcppEngine] Couldn't make OppOperation from Request. Ignore request.\n")); return; } - handleReqMessage(json, req); + handleReqMessage(json, std::move(req)); } -void OcppConnection::handleReqMessage(JsonDocument *json, OcppOperation *op) { - if (op == NULL) { +void OcppConnection::handleReqMessage(JsonDocument& json, std::unique_ptr op) { + if (op == nullptr) { Serial.print(F("[OcppEngine] handleReqMessage: invalid argument\n")); return; } - receivedOcppOperations.push_back(op);; //enqueue so loop() plans conf sending op->receiveReq(json); //"fire" the operation + receivedOcppOperations.push_back(std::move(op)); //enqueue so loop() plans conf sending } -void OcppConnection::handleErrMessage(JsonDocument *json) { +void OcppConnection::handleErrMessage(JsonDocument& json) { for (auto operation = initiatedOcppOperations.begin(); operation != initiatedOcppOperations.end(); ++operation){ boolean discardOperation = (*operation)->receiveError(json); //maybe rename to "consumed"? - if (discardOperation){ - //TODO Review: are all recources freed here? - delete *operation; + if (discardOperation) { initiatedOcppOperations.erase(operation); return; } @@ -285,4 +264,3 @@ size_t removePayload(const char *src, size_t src_size, char *dst, size_t dst_siz res_len++; return res_len; } - diff --git a/src/ArduinoOcpp/Core/OcppConnection.h b/src/ArduinoOcpp/Core/OcppConnection.h index c5e29f1b..aac367ef 100644 --- a/src/ArduinoOcpp/Core/OcppConnection.h +++ b/src/ArduinoOcpp/Core/OcppConnection.h @@ -17,22 +17,22 @@ namespace ArduinoOcpp { class OcppConnection { private: OcppSocket *ocppSock; - std::deque initiatedOcppOperations; - std::deque receivedOcppOperations; + std::deque> initiatedOcppOperations; + std::deque> receivedOcppOperations; - void handleConfMessage(JsonDocument *json); - void handleReqMessage(JsonDocument *json); - void handleReqMessage(JsonDocument *json, OcppOperation *op); - void handleErrMessage(JsonDocument *json); + void handleConfMessage(JsonDocument& json); + void handleReqMessage(JsonDocument& json); + void handleReqMessage(JsonDocument& json, std::unique_ptr op); + void handleErrMessage(JsonDocument& json); public: OcppConnection(OcppSocket *ocppSock); void loop(); - void initiateOcppOperation(OcppOperation *o); + void initiateOcppOperation(std::unique_ptr o); bool processOcppSocketInputTXT(const char* payload, size_t length); }; } //end namespace ArduinoOcpp -#endif \ No newline at end of file +#endif diff --git a/src/ArduinoOcpp/Core/OcppEngine.cpp b/src/ArduinoOcpp/Core/OcppEngine.cpp index 7ee65391..9a170c72 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.cpp +++ b/src/ArduinoOcpp/Core/OcppEngine.cpp @@ -15,22 +15,12 @@ namespace ArduinoOcpp { namespace OcppEngine { -OcppSocket *ocppSocket; -SmartChargingService *ocppEngine_smartChargingService = NULL; -ChargePointStatusService *ocppEngine_chargePointStatusService = NULL; -MeteringService *ocppEngine_meteringService = NULL; -FirmwareService *ocppEngine_firmwareService = NULL; -DiagnosticsService *ocppEngine_diagnosticsService = NULL; -OcppTime *ocppEngine_ocppTime; - -std::vector initiatedOcppOperations; -std::vector receivedOcppOperations; - -int JSON_DOC_SIZE; - -#define HEAP_GUARD 2000UL //will not accept JSON messages if it will result in less than HEAP_GUARD free bytes in heap - -//size_t removePayload(const char *src, size_t src_size, char *dst, size_t dst_size); +SmartChargingService *ocppEngine_smartChargingService{nullptr}; +ChargePointStatusService *ocppEngine_chargePointStatusService{nullptr}; +MeteringService *ocppEngine_meteringService{nullptr}; +FirmwareService *ocppEngine_firmwareService{nullptr}; +DiagnosticsService *ocppEngine_diagnosticsService{nullptr}; +OcppTime *ocppEngine_ocppTime{nullptr}; OcppSocket *ocppSock; OcppConnection *mConnection; @@ -44,8 +34,8 @@ void ocppEngine_initialize(OcppSocket *ocppSocket){ mConnection = new OcppConnection(ocppSock); } -void initiateOcppOperation(OcppOperation *o) { - mConnection->initiateOcppOperation(o); +void initiateOcppOperation(std::unique_ptr o) { + mConnection->initiateOcppOperation(std::move(o)); } void ocppEngine_loop() { @@ -53,355 +43,12 @@ void ocppEngine_loop() { mConnection->loop(); } -#if 0 -/** - * Process raw application data coming from the websocket stream - */ -bool processWebSocketEvent(const char* payload, size_t length){ - - boolean deserializationSuccess = false; - - DynamicJsonDocument *doc = NULL; - size_t capacity = length + 100; - - DeserializationError err = DeserializationError::NoMemory; - while (capacity + HEAP_GUARD < ESP.getFreeHeap() && err == DeserializationError::NoMemory) { - if (doc){ - delete doc; - doc = NULL; - } - doc = new DynamicJsonDocument(capacity); - err = deserializeJson(*doc, payload, length); - - capacity /= 2; - capacity *= 3; - } - - //TODO insert validateRpcHeader at suitable position - - switch (err.code()) { - case DeserializationError::Ok: - if (doc != NULL){ - int messageTypeId = (*doc)[0]; - switch(messageTypeId) { - case MESSAGE_TYPE_CALL: - handleReqMessage(doc); - deserializationSuccess = true; - break; - case MESSAGE_TYPE_CALLRESULT: - handleConfMessage(doc); - deserializationSuccess = true; - break; - case MESSAGE_TYPE_CALLERROR: - handleErrMessage(doc); - deserializationSuccess = true; - break; - default: - Serial.print(F("[OcppEngine] Invalid OCPP message! (though JSON has successfully been deserialized)\n")); - break; - } - } else { //unlikely corner case - Serial.print(F("[OcppEngine] Deserialization is okay but doc is Null\n")); - } - break; - case DeserializationError::InvalidInput: - Serial.print(F("[OcppEngine] Invalid input! Not a JSON\n")); - break; - case DeserializationError::NoMemory: - { - if (DEBUG_OUT) Serial.print(F("[OcppEngine] Error: Not enough memory in heap! Input length = ")); - if (DEBUG_OUT) Serial.print(length); - if (DEBUG_OUT) Serial.print(F(", free heap = ")); - if (DEBUG_OUT) Serial.println(ESP.getFreeHeap()); - - /* - * If websocket input is of message type MESSAGE_TYPE_CALL, send back a message of type MESSAGE_TYPE_CALLERROR. - * Then the communication counterpart knows that this operation failed. - * If the input type is MESSAGE_TYPE_CALLRESULT, it can be ignored. This controller will automatically resend the corresponding requirement message. - */ - - if (doc){ - delete doc; - doc = NULL; - } - - doc = new DynamicJsonDocument(200); - char onlyRpcHeader[200]; - size_t onlyRpcHeader_len = removePayload(payload, length, onlyRpcHeader, 200); - DeserializationError err2 = deserializeJson(*doc, onlyRpcHeader, onlyRpcHeader_len); - if (err2.code() == DeserializationError::Ok) { - int messageTypeId2 = (*doc)[0] | -1; - if (messageTypeId2 == MESSAGE_TYPE_CALL) { - deserializationSuccess = true; - OcppOperation *op = makeOcppOperation(wsocket, new OutOfMemory(ESP.getFreeHeap(), length)); - handleReqMessage(doc, op); - } - } - } - break; - default: - Serial.print(F("[OcppEngine] Deserialization failed: ")); - Serial.println(err.c_str()); - break; - } - - if (doc) { - delete doc; - } - - return deserializationSuccess; -} - -/** - * Send back error code - */ -boolean processWebSocketUnsupportedEvent(const char* payload, size_t length){ - bool deserializationSuccess = false; - DynamicJsonDocument *doc = new DynamicJsonDocument(200); - char onlyRpcHeader[200]; - size_t onlyRpcHeader_len = removePayload(payload, length, onlyRpcHeader, 200); - DeserializationError err = deserializeJson(*doc, onlyRpcHeader, onlyRpcHeader_len); - if (err.code() == DeserializationError::Ok) { - int messageTypeId = (*doc)[0]; - if (messageTypeId == MESSAGE_TYPE_CALL) { - deserializationSuccess = true; - OcppOperation *op = makeOcppOperation(wsocket, new WebSocketError("This controller does not support WebSocket fragments")); - handleReqMessage(doc, op); - } - } else { - Serial.print(F("[OcppEngine] Deserialization of unsupported WebSocket event failed: ")); - Serial.println(err.c_str()); - } - - if (doc) delete doc; - return deserializationSuccess; -} - -void handleReqMessage(JsonDocument *json){ - OcppOperation *req = makeFromJson(wsocket, json); - if (req == NULL) { - Serial.print(F("[OcppEngine] Couldn't make OppOperation from Request. Ignore request.\n")); - return; - } - handleReqMessage(json, req); -} - -void handleReqMessage(JsonDocument *json, OcppOperation *req) { - if (req == NULL) { - Serial.print(F("[OcppEngine] handleReqMessage: invalid argument\n")); - return; - } - receivedOcppOperations.push_back(req);; //enqueue so loop() plans conf sending - req->receiveReq(json); //"fire" the operation -} - -/** - * call conf() on each element of the queue. Start with first element. On successful message delivery, - * delete the element from the list. Try all the pending OCPP Operations until the right one is found. - * - * This function could result in improper behavior in Charging Stations, because messages are not - * guaranteed to be received and therefore processed in the right order. - */ -void handleConfMessage(JsonDocument *json){ - #if 0 - for (int i = 0; i < initiatedOcppOperations.size(); i++){ - OcppOperation *el = initiatedOcppOperations.at(i); - boolean success = el->receiveConf(json); //maybe rename to "consumed"? - if (success){ - initiatedOcppOperations.(i); - - //TODO Review: are all recources freed here? - delete el; - return; - } - } - #endif - - for (auto operation = initiatedOcppOperations.begin(); operation != initiatedOcppOperations.end(); ++operation) { - boolean success = (*operation)->receiveConf(json); //maybe rename to "consumed"? - if (success) { - delete *operation; - initiatedOcppOperations.erase(operation); - return; - } - } - - //didn't find matching OcppOperation - Serial.print(F("[OcppEngine] Received CALLRESULT doesn't match any pending operation!\n")); -} - - -void handleErrMessage(JsonDocument *json){ - #if 0 - for (int i = 0; i < initiatedOcppOperations.size(); i++){ - OcppOperation *el = initiatedOcppOperations.get(i); - boolean discardOperation = el->receiveError(json); //maybe rename to "consumed"? - if (discardOperation){ - initiatedOcppOperations.remove(i); - - //TODO Review: are all recources freed here? - delete el; - return; - } - } - #endif - - for (auto operation = initiatedOcppOperations.begin(); operation != initiatedOcppOperations.end(); ++operation){ - boolean discardOperation = (*operation)->receiveError(json); //maybe rename to "consumed"? - if (discardOperation){ - //TODO Review: are all recources freed here? - delete *operation; - initiatedOcppOperations.erase(operation); - return; - } - } - - //No OcppOperation was aborted because of the error message - if (DEBUG_OUT) Serial.print(F("[OcppEngine] Received CALLERROR did not abort a pending operation\n")); -} - - -void initiateOcppOperation(OcppOperation *o){ - if (!o->isFullyConfigured()){ - Serial.printf("[OcppEngine] initiateOcppOperation(op) was called without the operation being configured and ready to send. Discard operation!\n"); - delete o; - return; - } - initiatedOcppOperations.push_back(o); -} - -void ocppEngine_loop(){ - - /** - * Work through the initiatedOcppOperations queue. Start with the first element by calling req() on it. If - * the operation is (still) pending, it will return false and therefore each other queued element must - * wait until this operation is over. If an ocppOperation is finished, it returns true on a req() call, - * can be dequeued and the following ocppOperation is processed. - */ - - #if 0 - while (initiatedOcppOperations.size() > 0){ - OcppOperation *el = initiatedOcppOperations.get(0); - boolean timeout = el->sendReq(); //The only reason to dequeue elements here is when a timeout occurs. Normally - if (timeout){ //the Conf msg processing routine dequeues finished elements - initiatedOcppOperations.remove(0); - - //TODO Review: are all recources freed here? - delete el; - //go on with the next element in the queue, which is now at initiatedOcppOperations[0] - } else { - //there is one operation pending right now, so quit this while-loop. - break; - } - } - #endif - - auto operation = initiatedOcppOperations.begin(); - while (operation != initiatedOcppOperations.end()){ - boolean timeout = (*operation)->sendReq(); //The only reason to dequeue elements here is when a timeout occurs. Normally - if (timeout){ //the Conf msg processing routine dequeues finished elements - delete *operation; - operation = initiatedOcppOperations.erase(operation); - - //TODO Review: are all recources freed here? - //go on with the next element in the queue, which is now at initiatedOcppOperations[0] - } else { - //there is one operation pending right now, so quit this while-loop. - break; - } - } - - /* - * Activate timeout detection on the msgs other than the first in the queue. - */ - - #if 0 - for (int i = 1; i < initiatedOcppOperations.size(); i++) { - Timeout *timer = initiatedOcppOperations.get(i)->getTimeout(); - if (timer) - timer->tick(false); //false: did not send a frame prior to calling tick - if (timer->isExceeded()) { - Serial.print(F("[OcppEngine] Discarding operation due to timeout: ")); - initiatedOcppOperations.get(i)->print_debug(); - initiatedOcppOperations.remove(i); - delete initiatedOcppOperations.get(i); - } - } - #endif - - operation = initiatedOcppOperations.begin(); - while (operation != initiatedOcppOperations.end()) { - if (operation == initiatedOcppOperations.begin()){ //jump over the first element - ++operation; - continue; - } - Timeout *timer = (*operation)->getTimeout(); - if (!timer) { - ++operation; //no timeouts, nothing to do in this iteration - continue; - } - timer->tick(false); //false: did not send a frame prior to calling tick - if (timer->isExceeded()) { - Serial.print(F("[OcppEngine] Discarding operation due to timeout: ")); - (*operation)->print_debug(); - delete *operation; - operation = initiatedOcppOperations.erase(operation); - } else { - ++operation; - } - } - - /** - * Work through the receivedOcppOperations queue. Start with the first element by calling conf() on it. - * If an ocppOperation is finished, it returns true on a conf() call, and is dequeued. - */ - - #if 0 - int i = 0; - while (i < receivedOcppOperations.size()){ - OcppOperation *el = receivedOcppOperations.get(i); - boolean success = el->sendConf(); - if (success){ - receivedOcppOperations.remove(i); - - //TODO Review: are all recources freed here? - delete el; - //go on with the next element in the queue, which is now at receivedOcppOperations[i] - } else { - //There will be another attempt to send this conf message in a future loop call. - //Go on with the next element in the queue, which is now at receivedOcppOperations[i+1] - i++; - } - } - #endif - - operation = receivedOcppOperations.begin(); - while (operation != receivedOcppOperations.end()){ - boolean success = (*operation)->sendConf(); - if (success){ - delete *operation; - operation = receivedOcppOperations.erase(operation); - - //TODO Review: are all recources freed here? - //go on with the next element in the queue, which is now at receivedOcppOperations[i] - } else { - //There will be another attempt to send this conf message in a future loop call. - //Go on with the next element in the queue, which is now at receivedOcppOperations[i+1] - ++operation; //TODO review: this makes conf's out-of-order. But if the first Op fails because of lacking RAM, this could save the device. - } - } - - -} - -#endif - void setSmartChargingService(SmartChargingService *scs) { ocppEngine_smartChargingService = scs; } SmartChargingService* getSmartChargingService(){ - if (ocppEngine_smartChargingService == NULL) { + if (ocppEngine_smartChargingService == nullptr) { Serial.print(F("[OcppEngine] Error: in OcppEngine, there is no smartChargingService set, but it is accessed!\n")); //no error catch } @@ -413,7 +60,7 @@ void setChargePointStatusService(ChargePointStatusService *cpss){ } ChargePointStatusService *getChargePointStatusService(){ - if (ocppEngine_chargePointStatusService == NULL) { + if (ocppEngine_chargePointStatusService == nullptr) { Serial.print(F("[OcppEngine] Error: in OcppEngine, there is no chargePointStatusService set, but it is accessed!\n")); //no error catch } @@ -421,10 +68,10 @@ ChargePointStatusService *getChargePointStatusService(){ } ConnectorStatus *getConnectorStatus(int connectorId) { - if (getChargePointStatusService() == NULL) return NULL; + if (getChargePointStatusService() == nullptr) return nullptr; ConnectorStatus *result = getChargePointStatusService()->getConnector(connectorId); - if (result == NULL) { + if (result == nullptr) { Serial.print(F("[OcppEngine] Error in getConnectorStatus(): cannot fetch connector with given connectorId!\n")); //no error catch } @@ -432,11 +79,11 @@ ConnectorStatus *getConnectorStatus(int connectorId) { } void setMeteringSerivce(MeteringService *meteringService) { -ocppEngine_meteringService = meteringService; + ocppEngine_meteringService = meteringService; } MeteringService* getMeteringService() { - if (ocppEngine_meteringService == NULL) { + if (ocppEngine_meteringService == nullptr) { Serial.print(F("[OcppEngine] Error: in OcppEngine, there is no ocppEngine_meteringService set, but it is accessed!\n")); //no error catch } @@ -448,7 +95,7 @@ void setFirmwareService(FirmwareService *fwService) { } FirmwareService *getFirmwareService() { - if (ocppEngine_firmwareService == NULL) { + if (ocppEngine_firmwareService == nullptr) { Serial.print(F("[OcppEngine] Error: in OcppEngine, there is no ocppEngine_firmwareService set, but it is accessed!\n")); //no error catch } @@ -460,7 +107,7 @@ void setDiagnosticsService(DiagnosticsService *diagnosticsService) { } DiagnosticsService *getDiagnosticsService() { - if (ocppEngine_diagnosticsService == NULL) { + if (ocppEngine_diagnosticsService == nullptr) { Serial.print(F("[OcppEngine] Error: in OcppEngine, there is no ocppEngine_diagnosticsService set, but it is accessed!\n")); //no error catch } @@ -472,47 +119,11 @@ void ocppEngine_setOcppTime(OcppTime *ocppTime) { } OcppTime *getOcppTime() { - if (ocppEngine_ocppTime == NULL) { + if (ocppEngine_ocppTime == nullptr) { Serial.print(F("[OcppEngine] Error: in OcppEngine, there is no ocppEngine_ocppTime set, but it is accessed!\n")); //no error catch } return ocppEngine_ocppTime; } - -#if 0 //moved to OcppConnection -/* - * Tries to recover the Ocpp-Operation header from a broken message. - * - * Example input: - * [2, "75705e50-682d-404e-b400-1bca33d41e19", "ChangeConfiguration", {"key":"now the msg breaks... - * - * The Json library returns an error code when trying to deserialize that broken message. This - * function searches for the first occurence of the character '{' and writes "}]" after it. - * - * Example output: - * [2, "75705e50-682d-404e-b400-1bca33d41e19", "ChangeConfiguration", {}] - * - */ -size_t removePayload(const char *src, size_t src_size, char *dst, size_t dst_size) { - size_t res_len = 0; - for (size_t i = 0; i < src_size && i < dst_size-3; i++) { - if (src[i] == '\0'){ - //no payload found within specified range. Cancel execution - break; - } - dst[i] = src[i]; - if (src[i] == '{') { - dst[i+1] = '}'; - dst[i+2] = ']'; - res_len = i+3; - break; - } - } - dst[res_len] = '\0'; - res_len++; - return res_len; -} - -#endif } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Core/OcppEngine.h b/src/ArduinoOcpp/Core/OcppEngine.h index 2f3a3b62..cb83d438 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.h +++ b/src/ArduinoOcpp/Core/OcppEngine.h @@ -20,24 +20,7 @@ namespace ArduinoOcpp { void ocppEngine_initialize(OcppSocket *ocppSocket); -#if 0 - -bool processWebSocketEvent(const char* payload, size_t length); - -//WebSocket fragments are not supported. This function sends a meaningful error response -boolean processWebSocketUnsupportedEvent(const char* payload, size_t length); - -void handleConfMessage(JsonDocument *json); - -void handleReqMessage(JsonDocument *json); - -void handleReqMessage(JsonDocument *json, OcppOperation *op); - -void handleErrMessage(JsonDocument *json); - -#endif - -void initiateOcppOperation(OcppOperation *o); +void initiateOcppOperation(std::unique_ptr o); void ocppEngine_loop(); diff --git a/src/ArduinoOcpp/Core/OcppOperation.cpp b/src/ArduinoOcpp/Core/OcppOperation.cpp index e34665de..9b99532c 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.cpp +++ b/src/ArduinoOcpp/Core/OcppOperation.cpp @@ -10,368 +10,344 @@ int unique_id_counter = 531531531; using namespace ArduinoOcpp; -OcppOperation::OcppOperation(OcppMessage *ocppMessage) : ocppMessage(ocppMessage) { - +OcppOperation::OcppOperation(std::unique_ptr msg) : ocppMessage(std::move(msg)) { + } OcppOperation::OcppOperation() { - ocppMessage = NULL; + } OcppOperation::~OcppOperation(){ - if (ocppMessage != NULL) - delete ocppMessage; - if (timeout) - delete timeout; + } -void OcppOperation::setOcppMessage(OcppMessage *msg){ - ocppMessage = msg; +void OcppOperation::setOcppMessage(std::unique_ptr msg){ + ocppMessage = std::move(msg); } -void OcppOperation::setTimeout(Timeout *to){ - if (!to){ - Serial.print(F("[OcppOperation] in setTimeout(): passed NULL! Ignore\n")); - return; - } - if (timeout) - delete timeout; - timeout = to; - timeout->setOnTimeoutListener(onTimeoutListener); - timeout->setOnAbortListener(onAbortListener); +void OcppOperation::setTimeout(std::unique_ptr to){ + if (!to){ + Serial.print(F("[OcppOperation] in setTimeout(): passed nullptr! Ignore\n")); + return; + } + timeout = std::move(to); + timeout->setOnTimeoutListener(onTimeoutListener); + timeout->setOnAbortListener(onAbortListener); } Timeout *OcppOperation::getTimeout() { - return timeout; + return timeout.get(); } -void OcppOperation::setMessageID(String &id){ - if (messageID.length() > 0){ - Serial.print(F("[OcppOperation] OcppOperation (class): MessageID is set twice or is set after first usage!\n")); - //return; <-- Just letting the id being overwritten probably doesn't cause further errors... - } - messageID = String(id); +void OcppOperation::setMessageID(const String &id){ + if (messageID.length() > 0){ + Serial.print(F("[OcppOperation] OcppOperation (class): MessageID is set twice or is set after first usage!\n")); + //return; <-- Just letting the id being overwritten probably doesn't cause further errors... + } + messageID = id; } -String &OcppOperation::getMessageID() { - if (messageID.isEmpty()) { - messageID = String(unique_id_counter++); - } - return messageID; +const String *OcppOperation::getMessageID() { + if (messageID.isEmpty()) { + messageID = String(unique_id_counter++); + } + return &messageID; } -boolean OcppOperation::sendReq(OcppSocket *ocppSocket){ - - /* - * timeout behaviour - * - * TODO This is possibly a duplication of the timeout behavior in ocppEngine_loop(). Check if responsibility for timeout can be left to OcppEngine only - * - * if timeout, print out error message and treat this operation as completed (-> return true) - */ - //if (timeout_active && millis() - timeout_start >= TIMEOUT_CANCEL){ - if (timeout->isExceeded()) { - //cancel this operation - Serial.print(F("[OcppOperation] ")); - Serial.print(ocppMessage->getOcppOperationType()); - Serial.print(F(" has timed out! Discard operation\n")); - return true; - } - - /* - * retry behaviour - * - * if retry, run the rest of this function, i.e. resend the message. If not, just return false - */ - if (millis() <= retry_start + RETRY_INTERVAL * retry_interval_mult) { - //NO retry - return false; - } - // Do retry. Increase timer by factor 2 - if (RETRY_INTERVAL * retry_interval_mult * 2 <= RETRY_INTERVAL_MAX) - retry_interval_mult *= 2; - - /* - * Create the OCPP message - */ - DynamicJsonDocument *requestPayload = ocppMessage->createReq(); - if (!requestPayload) { - onAbortListener(); - return true; - } - - /* - * Create OCPP-J Remote Procedure Call header - */ - size_t json_buffsize = JSON_ARRAY_SIZE(4) + (getMessageID().length() + 1) + requestPayload->capacity(); - DynamicJsonDocument requestJson(json_buffsize); - - requestJson.add(MESSAGE_TYPE_CALL); //MessageType - requestJson.add(getMessageID()); //Unique message ID - requestJson.add(ocppMessage->getOcppOperationType()); //Action - requestJson.add(*requestPayload); //Payload - - /* - * Serialize and send. Destroy serialization and JSON object. - * - * If sending was successful, start timer - * - * Return that this function must be called again (-> false) - */ - String out; - serializeJson(requestJson, out); +boolean OcppOperation::sendReq(OcppSocket& ocppSocket){ + + /* + * timeout behaviour + * + * if timeout, print out error message and treat this operation as completed (-> return true) + */ + if (timeout->isExceeded()) { + //cancel this operation + Serial.print(F("[OcppOperation] ")); + Serial.print(ocppMessage->getOcppOperationType()); + Serial.print(F(" has timed out! Discard operation\n")); + return true; + } + + /* + * retry behaviour + * + * if retry, run the rest of this function, i.e. resend the message. If not, just return false + */ + if (millis() <= retry_start + RETRY_INTERVAL * retry_interval_mult) { + //NO retry + return false; + } + // Do retry. Increase timer by factor 2 + if (RETRY_INTERVAL * retry_interval_mult * 2 <= RETRY_INTERVAL_MAX) + retry_interval_mult *= 2; + + /* + * Create the OCPP message + */ + auto requestPayload = std::unique_ptr(ocppMessage->createReq()); + if (!requestPayload) { + onAbortListener(); + return true; + } + + /* + * Create OCPP-J Remote Procedure Call header + */ + size_t json_buffsize = JSON_ARRAY_SIZE(4) + (getMessageID()->length() + 1) + requestPayload->capacity(); + DynamicJsonDocument requestJson(json_buffsize); + + requestJson.add(MESSAGE_TYPE_CALL); //MessageType + requestJson.add(*getMessageID()); //Unique message ID + requestJson.add(ocppMessage->getOcppOperationType()); //Action + requestJson.add(*requestPayload); //Payload + + /* + * Serialize and send. Destroy serialization and JSON object. + * + * If sending was successful, start timer + * + * Return that this function must be called again (-> false) + */ + String out {'\0'}; + serializeJson(requestJson, out); #if DEBUG_OUT - if (printReqCounter > 5000) { - printReqCounter = 0; - Serial.print(F("[OcppOperation] Send requirement: ")); - Serial.print(out); - Serial.print(F("\n")); - } - printReqCounter++; + if (printReqCounter > 5000) { + printReqCounter = 0; + Serial.print(F("[OcppOperation] Send requirement: ")); + Serial.print(out); + Serial.print(F("\n")); + } + printReqCounter++; #endif - - bool success = ocppSocket->sendTXT(out); - - timeout->tick(success); - - if (success) { - //success -// if (!timeout_active) { -// timeout_active = true; -// timeout_start = millis(); -// } - if (TRAFFIC_OUT) Serial.print(F("[OcppOperation] Sent requirement (success): ")); - if (TRAFFIC_OUT) Serial.println(out); - retry_start = millis(); - } else { - //ocppSocket is not able to put any data on TCP stack. Maybe because we're offline - retry_start = 0; - retry_interval_mult = 1; - } - - requestJson.clear(); - delete requestPayload; - return false; -} + + bool success = ocppSocket.sendTXT(out); + + timeout->tick(success); + + if (success) { + if (TRAFFIC_OUT) Serial.print(F("[OcppOperation] Sent requirement (success): ")); + if (TRAFFIC_OUT) Serial.println(out); + retry_start = millis(); + } else { + //ocppSocket is not able to put any data on TCP stack. Maybe because we're offline + retry_start = 0; + retry_interval_mult = 1; + } -boolean OcppOperation::receiveConf(JsonDocument *confJson){ //TODO add something like "JsonObject conf = confJson->as();" and test - /* - * check if messageIDs match. If yes, continue with this function. If not, return false for message not consumed - */ - if (!getMessageID().equals((*confJson)[1].as())){ return false; - } - - /* - * Hand the payload over to the OcppMessage object - */ - JsonObject payload = (*confJson)[2]; - ocppMessage->processConf(payload); - - /* - * Hand the payload over to the onReceiveConf Callback - */ - onReceiveConfListener(payload); - - /* - * return true as this message has been consumed - */ - return true; } -boolean OcppOperation::receiveError(JsonDocument *confJson){ //TODO add something like "JsonObject conf = confJson->as();" and test - /* - * check if messageIDs match. If yes, continue with this function. If not, return false for message not consumed - */ - if (!getMessageID().equals((*confJson)[1].as())){ - return false; - } - - /* - * Hand the error over to the OcppMessage object - */ - const char *errorCode = (*confJson)[2]; - const char *errorDescription = (*confJson)[3]; - JsonObject errorDetails = (*confJson)[4]; - bool abortOperation = ocppMessage->processErr(errorCode, errorDescription, errorDetails); - - if (abortOperation) { - onReceiveErrorListener(errorCode, errorDescription, errorDetails); - onAbortListener(); - } else { - //restart operation - timeout->restart(); - retry_start = 0; - retry_interval_mult = 1; - } - - return abortOperation; -} +boolean OcppOperation::receiveConf(JsonDocument& confJson){ + /* + * check if messageIDs match. If yes, continue with this function. If not, return false for message not consumed + */ + if (!getMessageID()->equals(confJson[1].as())){ + return false; + } -boolean OcppOperation::receiveReq(JsonDocument *reqJson){ //TODO add something like "JsonObject req = reqJson->as();" and test - - String reqId = (*reqJson)[1]; - setMessageID(reqId);//(*reqJson)[1]); + /* + * Hand the payload over to the OcppMessage object + */ + JsonObject payload = confJson[2]; + ocppMessage->processConf(payload); - //TODO What if client receives requests two times? Can happen if previous conf is lost. In the Smart Charging Profile - // it probably doesn't matter to repeat an operation on the EVSE. Change in future? - - /* - * Hand the payload over to the OcppOperation object - */ - JsonObject payload = (*reqJson)[3]; - ocppMessage->processReq(payload); - - /* - * Hand the payload over to the first Callback. It is a callback that notifies the client that request has been processed in the OCPP-library - */ + /* + * Hand the payload over to the onReceiveConf Callback + */ + onReceiveConfListener(payload); + + /* + * return true as this message has been consumed + */ + return true; +} - onReceiveReqListener(payload); +boolean OcppOperation::receiveError(JsonDocument& confJson){ + /* + * check if messageIDs match. If yes, continue with this function. If not, return false for message not consumed + */ + if (!getMessageID()->equals(confJson[1].as())){ + return false; + } - reqExecuted = true; //ensure that the conf is only sent after the req has been executed + /* + * Hand the error over to the OcppMessage object + */ + const char *errorCode = confJson[2]; + const char *errorDescription = confJson[3]; + JsonObject errorDetails = confJson[4]; + bool abortOperation = ocppMessage->processErr(errorCode, errorDescription, errorDetails); + + if (abortOperation) { + onReceiveErrorListener(errorCode, errorDescription, errorDetails); + onAbortListener(); + } else { + //restart operation + timeout->restart(); + retry_start = 0; + retry_interval_mult = 1; + } - return true; //true because everything was successful. If there will be an error check in future, this value becomes more reasonable + return abortOperation; } -boolean OcppOperation::sendConf(OcppSocket *ocppSocket){ - - if (!reqExecuted) { - //wait until req has been executed - return false; - } - - /* - * Create the OCPP message - */ - DynamicJsonDocument *confJson = NULL; - DynamicJsonDocument *confPayload = ocppMessage->createConf(); - DynamicJsonDocument *errorDetails = NULL; +boolean OcppOperation::receiveReq(JsonDocument& reqJson){ - bool operationSuccess = ocppMessage->getErrorCode() == NULL && confPayload != NULL; - - if (operationSuccess) { - // operation did not fail + String reqId = reqJson[1]; + setMessageID(reqId); + //TODO What if client receives requests two times? Can happen if previous conf is lost. In the Smart Charging Profile + // it probably doesn't matter to repeat an operation on the EVSE. Change in future? + + /* + * Hand the payload over to the OcppOperation object + */ + JsonObject payload = reqJson[3]; + ocppMessage->processReq(payload); + /* - * Create OCPP-J Remote Procedure Call header - */ - size_t json_buffsize = JSON_ARRAY_SIZE(3) + (getMessageID().length() + 1) + confPayload->capacity(); - confJson = new DynamicJsonDocument(json_buffsize); - - confJson->add(MESSAGE_TYPE_CALLRESULT); //MessageType - confJson->add(getMessageID()); //Unique message ID - confJson->add(*confPayload); //Payload - } else { - //operation failure. Send error message instead - - const char *errorCode = ocppMessage->getErrorCode(); - const char *errorDescription = ocppMessage->getErrorDescription(); - errorDetails = ocppMessage->getErrorDetails(); - if (!errorCode) { //catch corner case when payload is null but errorCode is not set too! - errorCode = "GenericError"; - errorDescription = "Could not create payload (createConf() returns Null)"; - errorDetails = createEmptyDocument(); + * Hand the payload over to the first Callback. It is a callback that notifies the client that request has been processed in the OCPP-library + */ + onReceiveReqListener(payload); + + reqExecuted = true; //ensure that the conf is only sent after the req has been executed + + return true; //true because everything was successful. If there will be an error check in future, this value becomes more reasonable +} + +boolean OcppOperation::sendConf(OcppSocket& ocppSocket){ + + if (!reqExecuted) { + //wait until req has been executed + return false; } /* - * Create OCPP-J Remote Procedure Call header + * Create the OCPP message */ - size_t json_buffsize = JSON_ARRAY_SIZE(5) - + (getMessageID().length() + 1) - + strlen(errorCode) + 1 - + strlen(errorDescription) + 1 - + errorDetails->capacity(); - confJson = new DynamicJsonDocument(json_buffsize); - - confJson->add(MESSAGE_TYPE_CALLERROR); //MessageType - confJson->add(getMessageID()); //Unique message ID - confJson->add(errorCode); - confJson->add(errorDescription); - confJson->add(*errorDetails); //Error description - } - - /* - * Serialize and send. Destroy serialization and JSON object. - */ - String out; - serializeJson(*confJson, out); - boolean wsSuccess = ocppSocket->sendTXT(out); - - if (wsSuccess) { + std::unique_ptr confJson = nullptr; + std::unique_ptr confPayload = std::unique_ptr(ocppMessage->createConf()); + std::unique_ptr errorDetails = nullptr; + + bool operationSuccess = ocppMessage->getErrorCode() == nullptr && confPayload != nullptr; + if (operationSuccess) { - if (DEBUG_OUT || TRAFFIC_OUT) Serial.print(F("[OcppOperation] (Conf) JSON message: ")); - if (DEBUG_OUT || TRAFFIC_OUT) Serial.println(out); - onSendConfListener(confPayload->as()); + + /* + * Create OCPP-J Remote Procedure Call header + */ + size_t json_buffsize = JSON_ARRAY_SIZE(3) + (getMessageID()->length() + 1) + confPayload->capacity(); + confJson = std::unique_ptr(new DynamicJsonDocument(json_buffsize)); + + confJson->add(MESSAGE_TYPE_CALLRESULT); //MessageType + confJson->add(*getMessageID()); //Unique message ID + confJson->add(*confPayload); //Payload } else { - Serial.print(F("[OcppOperation] (Conf) Error occured! JSON CallError message: ")); - Serial.print(out); - Serial.print('\n'); - onAbortListener(); + //operation failure. Send error message instead + + const char *errorCode = ocppMessage->getErrorCode(); + const char *errorDescription = ocppMessage->getErrorDescription(); + errorDetails = std::unique_ptr(ocppMessage->getErrorDetails()); + if (!errorCode) { //catch corner case when payload is null but errorCode is not set too! + errorCode = "GenericError"; + errorDescription = "Could not create payload (createConf() returns Null)"; + errorDetails = std::unique_ptr(createEmptyDocument()); + } + + /* + * Create OCPP-J Remote Procedure Call header + */ + size_t json_buffsize = JSON_ARRAY_SIZE(5) + + (getMessageID()->length() + 1) + + strlen(errorCode) + 1 + + strlen(errorDescription) + 1 + + errorDetails->capacity(); + confJson = std::unique_ptr(new DynamicJsonDocument(json_buffsize)); + + confJson->add(MESSAGE_TYPE_CALLERROR); //MessageType + confJson->add(*getMessageID()); //Unique message ID + confJson->add(errorCode); + confJson->add(errorDescription); + confJson->add(*errorDetails); //Error description } - } - - if (confJson) - delete confJson; - if (confPayload) - delete confPayload; - if (errorDetails) - delete errorDetails; - - return wsSuccess; + + /* + * Serialize and send. Destroy serialization and JSON object. + */ + String out {'\0'}; + serializeJson(*confJson, out); + boolean wsSuccess = ocppSocket.sendTXT(out); + + if (wsSuccess) { + if (operationSuccess) { + if (DEBUG_OUT || TRAFFIC_OUT) Serial.print(F("[OcppOperation] (Conf) JSON message: ")); + if (DEBUG_OUT || TRAFFIC_OUT) Serial.println(out); + onSendConfListener(confPayload->as()); + } else { + Serial.print(F("[OcppOperation] (Conf) Error occured! JSON CallError message: ")); + Serial.print(out); + Serial.print('\n'); + onAbortListener(); + } + } + + return wsSuccess; } void OcppOperation::setInitiated() { - if (ocppMessage) { - ocppMessage->initiate(); - } else { - Serial.print(F("[OcppOperation] Error: called setInitiated without corresponding OcppOperation!\n")); - } + if (ocppMessage) { + ocppMessage->initiate(); + } else { + Serial.print(F("[OcppOperation] Error: called setInitiated without corresponding OcppOperation!\n")); + } } void OcppOperation::setOnReceiveConfListener(OnReceiveConfListener onReceiveConf){ - if (onReceiveConf) - onReceiveConfListener = onReceiveConf; + if (onReceiveConf) + onReceiveConfListener = onReceiveConf; } /** * Sets a Listener that is called after this machine processed a request by the communication counterpart */ void OcppOperation::setOnReceiveReqListener(OnReceiveReqListener onReceiveReq){ - if (onReceiveReq) - onReceiveReqListener = onReceiveReq; + if (onReceiveReq) + onReceiveReqListener = onReceiveReq; } void OcppOperation::setOnSendConfListener(OnSendConfListener onSendConf){ - if (onSendConf) - onSendConfListener = onSendConf; + if (onSendConf) + onSendConfListener = onSendConf; } void OcppOperation::setOnTimeoutListener(OnTimeoutListener onTimeout) { - if (onTimeout) - onTimeoutListener = onTimeout; + if (onTimeout) + onTimeoutListener = onTimeout; } void OcppOperation::setOnReceiveErrorListener(OnReceiveErrorListener onReceiveError) { - if (onReceiveError) - onReceiveErrorListener = onReceiveError; + if (onReceiveError) + onReceiveErrorListener = onReceiveError; } void OcppOperation::setOnAbortListener(OnAbortListener onAbort) { - if (onAbort) - onAbortListener = onAbort; + if (onAbort) + onAbortListener = onAbort; } boolean OcppOperation::isFullyConfigured(){ - return ocppMessage != NULL; + return ocppMessage != nullptr; } void OcppOperation::print_debug() { - Serial.print(F("[OcppOperation] I am a ")); - if (ocppMessage) { - Serial.print(ocppMessage->getOcppOperationType()); - } else { - Serial.print(F("NULL")); - } - Serial.print(F("\n")); + Serial.print(F("[OcppOperation] I am a ")); + if (ocppMessage) { + Serial.print(ocppMessage->getOcppOperationType()); + } else { + Serial.print(F("nullptr")); + } + Serial.print(F("\n")); } diff --git a/src/ArduinoOcpp/Core/OcppOperation.h b/src/ArduinoOcpp/Core/OcppOperation.h index ba955195..92814607 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.h +++ b/src/ArduinoOcpp/Core/OcppOperation.h @@ -21,62 +21,62 @@ namespace ArduinoOcpp { -typedef std::function OnReceiveConfListener; -typedef std::function OnReceiveReqListener; -typedef std::function OnSendConfListener; -//typedef std::function OnTimeoutListener; //in OcppOperationTimeout. Workaround for circle include. Fix by extracting type definitions to new source file -typedef std::function OnReceiveErrorListener; //will be called if OCPP communication partner returns error code -//typedef std::function OnAbortListener; //will be called whenever the engine will stop trying to execute the operation normallythere is a timeout or error (onAbort = onTimeout || onReceiveError) +using OnReceiveConfListener = std::function; +using OnReceiveReqListener = std::function; +using OnSendConfListener = std::function; +//using OnTimeoutListener = std::function; //in OcppOperationTimeout. Workaround for circle include. Fix by extracting type definitions to new source file +using OnReceiveErrorListener = std::function; //will be called if OCPP communication partner returns error code +//using OnAbortListener = std::function; //will be called whenever the engine will stop trying to execute the operation normallythere is a timeout or error (onAbort = onTimeout || onReceiveError) class OcppOperation { private: - String messageID; - OcppMessage *ocppMessage = NULL; - String &getMessageID(); - void setMessageID(String &id); - OnReceiveConfListener onReceiveConfListener = [] (JsonObject payload) {}; - OnReceiveReqListener onReceiveReqListener = [] (JsonObject payload) {}; - OnSendConfListener onSendConfListener = [] (JsonObject payload) {}; - OnTimeoutListener onTimeoutListener = [] () {}; - OnReceiveErrorListener onReceiveErrorListener = [] (const char *code, const char *description, JsonObject details) {}; - OnAbortListener onAbortListener = [] () {}; - boolean reqExecuted = false; - - Timeout *timeout = new OfflineSensitiveTimeout(40000); - - const ulong RETRY_INTERVAL = 3000; //in ms; first retry after ... ms; second retry after 2 * ... ms; third after 4 ... - const ulong RETRY_INTERVAL_MAX = 20000; //in ms; - ulong retry_start = 0; - ulong retry_interval_mult = 1; // RETRY_INTERVAL * retry_interval_mult gives longer periods with each iteration + String messageID{'\0'}; + std::unique_ptr ocppMessage; + const String *getMessageID(); + void setMessageID(const String &id); + OnReceiveConfListener onReceiveConfListener = [] (JsonObject payload) {}; + OnReceiveReqListener onReceiveReqListener = [] (JsonObject payload) {}; + OnSendConfListener onSendConfListener = [] (JsonObject payload) {}; + OnTimeoutListener onTimeoutListener = [] () {}; + OnReceiveErrorListener onReceiveErrorListener = [] (const char *code, const char *description, JsonObject details) {}; + OnAbortListener onAbortListener = [] () {}; + boolean reqExecuted = false; + + std::unique_ptr timeout{new OfflineSensitiveTimeout(40000)}; + + const ulong RETRY_INTERVAL = 3000; //in ms; first retry after ... ms; second retry after 2 * ... ms; third after 4 ... + const ulong RETRY_INTERVAL_MAX = 20000; //in ms; + ulong retry_start = 0; + ulong retry_interval_mult = 1; // RETRY_INTERVAL * retry_interval_mult gives longer periods with each iteration #if DEBUG_OUT - uint16_t printReqCounter = 0; + uint16_t printReqCounter = 0; #endif public: - OcppOperation(OcppMessage *ocppMessage); + OcppOperation(std::unique_ptr msg); - OcppOperation(); + OcppOperation(); - ~OcppOperation(); + ~OcppOperation(); - void setOcppMessage(OcppMessage *msg); + void setOcppMessage(std::unique_ptr msg); - void setTimeout(Timeout *timeout); + void setTimeout(std::unique_ptr timeout); - Timeout *getTimeout(); + Timeout *getTimeout(); - /** - * Sends the message(s) that belong to the OCPP Operation. This function puts a JSON message on the lower protocol layer. - * - * For instance operation Authorize: sends Authorize.req(idTag) - * - * This function is usually called multiple times by the Arduino loop(). On first call, the request is initially sent. In the - * succeeding calls, the implementers decide to either resend the request, or do nothing as the operation is still pending. When - * the operation is completed (for example when conf() has been called), return true. When the operation is still pending, return - * false. - */ - boolean sendReq(OcppSocket *ocppSocket); + /** + * Sends the message(s) that belong to the OCPP Operation. This function puts a JSON message on the lower protocol layer. + * + * For instance operation Authorize: sends Authorize.req(idTag) + * + * This function is usually called multiple times by the Arduino loop(). On first call, the request is initially sent. In the + * succeeding calls, the implementers decide to either resend the request, or do nothing as the operation is still pending. When + * the operation is completed (for example when conf() has been called), return true. When the operation is still pending, return + * false. + */ + boolean sendReq(OcppSocket& ocppSocket); /** * Decides if message belongs to this operation instance and if yes, proccesses it. For example, multiple instances of an @@ -84,28 +84,28 @@ class OcppOperation { * * Returns true if JSON object has been consumed, false otherwise. */ - boolean receiveConf(JsonDocument *json); + boolean receiveConf(JsonDocument& json); /** * Decides if message belongs to this operation instance and if yes, notifies the OcppMessage object about the CallError. * * Returns true if JSON object has been consumed, false otherwise. */ - boolean receiveError(JsonDocument *json); + boolean receiveError(JsonDocument& json); /** * Processes the request in the JSON document. Returns true on success, false on error. * * Returns false if the request doesn't belong to the corresponding operation instance */ - boolean receiveReq(JsonDocument *json); + boolean receiveReq(JsonDocument& json); /** * After processing a request sent by the communication counterpart, this function sends a confirmation * message. Returns true on success, false otherwise. Returns also true if a CallError has successfully * been sent */ - boolean sendConf(OcppSocket *ocppSocket); + boolean sendConf(OcppSocket& ocppSocket); void setInitiated(); diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp index 98fe9e99..11d7ad44 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp @@ -10,7 +10,7 @@ using ArduinoOcpp::Ocpp16::TriggerMessage; TriggerMessage::TriggerMessage() { - statusMessage = "NotImplemented"; //default value if anything goes wrong + statusMessage = "NotImplemented"; //default value if anything goes wrong } const char* TriggerMessage::getOcppOperationType(){ @@ -19,24 +19,25 @@ const char* TriggerMessage::getOcppOperationType(){ void TriggerMessage::processReq(JsonObject payload) { - Serial.print(F("[TriggerMessage] Warning: TriggerMessage is not tested!\n")); + Serial.print(F("[TriggerMessage] Warning: TriggerMessage is not tested!\n")); - triggeredOperation = makeFromTriggerMessage(payload); - if (triggeredOperation != NULL) { - statusMessage = "Accepted"; - } else { - Serial.print(F("[TriggerMessage] Couldn't make OppOperation from TriggerMessage. Ignore request.\n")); - statusMessage = "NotImplemented"; - } + triggeredOperation = makeFromTriggerMessage(payload); + if (triggeredOperation != NULL) { + statusMessage = "Accepted"; + } else { + Serial.print(F("[TriggerMessage] Couldn't make OppOperation from TriggerMessage. Ignore request.\n")); + statusMessage = "NotImplemented"; + } } DynamicJsonDocument* TriggerMessage::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + strlen(statusMessage)); - JsonObject payload = doc->to(); - - payload["status"] = statusMessage; - - initiateOcppOperation(triggeredOperation); - - return doc; + DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + strlen(statusMessage)); + JsonObject payload = doc->to(); + + payload["status"] = statusMessage; + + if (triggeredOperation) //from the second createConf()-try on, do not initiate further OCPP ops + initiateOcppOperation(std::move(triggeredOperation)); + + return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h index b3dfe41d..c550313d 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h @@ -14,16 +14,16 @@ namespace Ocpp16 { class TriggerMessage : public OcppMessage { private: - OcppOperation *triggeredOperation; - const char *statusMessage; + std::unique_ptr triggeredOperation; + const char *statusMessage; public: - TriggerMessage(); + TriggerMessage(); - const char* getOcppOperationType(); + const char* getOcppOperationType(); - void processReq(JsonObject payload); + void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + DynamicJsonDocument* createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 8c42fd02..52a98efc 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -37,90 +37,90 @@ namespace ArduinoOcpp { OnReceiveReqListener onAuthorizeRequest; void setOnAuthorizeRequestListener(OnReceiveReqListener listener){ - onAuthorizeRequest = listener; + onAuthorizeRequest = listener; } OnReceiveReqListener onBootNotificationRequest; void setOnBootNotificationRequestListener(OnReceiveReqListener listener){ - onBootNotificationRequest = listener; + onBootNotificationRequest = listener; } OnReceiveReqListener onTargetValuesRequest; void setOnTargetValuesRequestListener(OnReceiveReqListener listener) { - onTargetValuesRequest = listener; + onTargetValuesRequest = listener; } OnReceiveReqListener onSetChargingProfileRequest; void setOnSetChargingProfileRequestListener(OnReceiveReqListener listener){ - onSetChargingProfileRequest = listener; + onSetChargingProfileRequest = listener; } OnReceiveReqListener onStartTransactionRequest; void setOnStartTransactionRequestListener(OnReceiveReqListener listener){ - onStartTransactionRequest = listener; + onStartTransactionRequest = listener; } OnReceiveReqListener onTriggerMessageRequest; void setOnTriggerMessageRequestListener(OnReceiveReqListener listener){ - onTriggerMessageRequest = listener; + onTriggerMessageRequest = listener; } OnReceiveReqListener onRemoteStartTransactionReceiveRequest; void setOnRemoteStartTransactionReceiveRequestListener(OnReceiveReqListener listener) { - onRemoteStartTransactionReceiveRequest = listener; + onRemoteStartTransactionReceiveRequest = listener; } OnSendConfListener onRemoteStartTransactionSendConf; void setOnRemoteStartTransactionSendConfListener(OnSendConfListener listener){ - onRemoteStartTransactionSendConf = listener; + onRemoteStartTransactionSendConf = listener; } OnReceiveReqListener onRemoteStopTransactionReceiveRequest; void setOnRemoteStopTransactionReceiveRequestListener(OnReceiveReqListener listener){ - onRemoteStopTransactionReceiveRequest = listener; + onRemoteStopTransactionReceiveRequest = listener; } OnSendConfListener onRemoteStopTransactionSendConf; void setOnRemoteStopTransactionSendConfListener(OnSendConfListener listener){ - onRemoteStopTransactionSendConf = listener; + onRemoteStopTransactionSendConf = listener; } OnSendConfListener onChangeConfigurationReceiveReq; void setOnChangeConfigurationReceiveRequestListener(OnReceiveReqListener listener){ - onChangeConfigurationReceiveReq = listener; + onChangeConfigurationReceiveReq = listener; } OnSendConfListener onChangeConfigurationSendConf; void setOnChangeConfigurationSendConfListener(OnSendConfListener listener){ - onChangeConfigurationSendConf = listener; + onChangeConfigurationSendConf = listener; } OnSendConfListener onGetConfigurationReceiveReq; void setOnGetConfigurationReceiveReqListener(OnSendConfListener listener){ - onGetConfigurationReceiveReq = listener; + onGetConfigurationReceiveReq = listener; } OnSendConfListener onGetConfigurationSendConf; void setOnGetConfigurationSendConfListener(OnSendConfListener listener){ - onGetConfigurationSendConf = listener; + onGetConfigurationSendConf = listener; } OnSendConfListener onResetReceiveReq; void setOnResetReceiveRequestListener(OnReceiveReqListener listener) { - onResetReceiveReq = listener; + onResetReceiveReq = listener; } OnSendConfListener onResetSendConf; void setOnResetSendConfListener(OnSendConfListener listener){ - onResetSendConf = listener; + onResetSendConf = listener; } OnReceiveReqListener onUpdateFirmwareReceiveReq; void setOnUpdateFirmwareReceiveRequestListener(OnReceiveReqListener listener) { - onUpdateFirmwareReceiveReq = listener; + onUpdateFirmwareReceiveReq = listener; } -OnReceiveReqListener onMeterValuesReceiveReq = NULL; +OnReceiveReqListener onMeterValuesReceiveReq; void setOnMeterValuesReceiveRequestListener(OnReceiveReqListener listener) { onMeterValuesReceiveReq = listener; } @@ -154,129 +154,128 @@ CustomOcppMessageCreatorEntry *makeCustomOcppMessage(const char *messageType) { return &(*it); } } - return NULL; + return nullptr; } -OcppOperation* makeFromTriggerMessage(JsonObject payload) { +std::unique_ptr makeFromTriggerMessage(JsonObject payload) { - //int connectorID = payload["connectorId"]; <-- not used in this implementation - const char *messageType = payload["requestedMessage"]; + //int connectorID = payload["connectorId"]; <-- not used in this implementation + const char *messageType = payload["requestedMessage"]; - if (DEBUG_OUT) { - Serial.print(F("[SimpleOcppOperationFactory] makeFromTriggerMessage for message type ")); - Serial.print(messageType); - Serial.print(F("\n")); - } + if (DEBUG_OUT) { + Serial.print(F("[SimpleOcppOperationFactory] makeFromTriggerMessage for message type ")); + Serial.print(messageType); + Serial.print(F("\n")); + } - return makeOcppOperation(messageType); + return makeOcppOperation(messageType); } -OcppOperation *makeFromJson(JsonDocument *json) { - const char* messageType = (*json)[2]; - return makeOcppOperation(messageType); +std::unique_ptr makeFromJson(const JsonDocument& json) { + const char* messageType = json[2]; + return makeOcppOperation(messageType); } -OcppOperation *makeOcppOperation(const char *messageType) { - OcppOperation *operation = makeOcppOperation(); - OcppMessage *msg = NULL; - - if (CustomOcppMessageCreatorEntry *entry = makeCustomOcppMessage(messageType)) { - msg = entry->creator(); - operation->setOnReceiveReqListener(entry->onReceiveReq); - } else if (!strcmp(messageType, "Authorize")) { - msg = new Ocpp16::Authorize(); - operation->setOnReceiveReqListener(onAuthorizeRequest); - } else if (!strcmp(messageType, "BootNotification")) { - msg = new Ocpp16::BootNotification(); - operation->setOnReceiveReqListener(onBootNotificationRequest); - } else if (!strcmp(messageType, "Heartbeat")) { - msg = new Ocpp16::Heartbeat(); - } else if (!strcmp(messageType, "MeterValues")) { - msg = new Ocpp16::MeterValues(); - operation->setOnReceiveReqListener(onMeterValuesReceiveReq); - } else if (!strcmp(messageType, "SetChargingProfile")) { - msg = new Ocpp16::SetChargingProfile(getSmartChargingService()); - operation->setOnReceiveReqListener(onSetChargingProfileRequest); - } else if (!strcmp(messageType, "StatusNotification")) { - msg = new Ocpp16::StatusNotification(); - } else if (!strcmp(messageType, "StartTransaction")) { - msg = new Ocpp16::StartTransaction(1); //connectorId 1 - operation->setOnReceiveReqListener(onStartTransactionRequest); - } else if (!strcmp(messageType, "StopTransaction")) { - msg = new Ocpp16::StopTransaction(); - } else if (!strcmp(messageType, "TriggerMessage")) { - msg = new Ocpp16::TriggerMessage(); - operation->setOnReceiveReqListener(onTriggerMessageRequest); - } else if (!strcmp(messageType, "RemoteStartTransaction")) { - msg = new Ocpp16::RemoteStartTransaction(); - operation->setOnReceiveReqListener(onRemoteStartTransactionReceiveRequest); - if (onRemoteStartTransactionSendConf == NULL) - Serial.print(F("[SimpleOcppOperationFactory] Warning: RemoteStartTransaction is without effect when the sendConf listener is not set. Set a listener which initiates the StartTransaction operation.\n")); - operation->setOnSendConfListener(onRemoteStartTransactionSendConf); - } else if (!strcmp(messageType, "RemoteStopTransaction")) { - msg = new Ocpp16::RemoteStopTransaction(); - if (onRemoteStopTransactionSendConf == NULL) - Serial.print(F("[SimpleOcppOperationFactory] Warning: RemoteStopTransaction is without effect when no sendConf listener is set. Set a listener which initiates the StopTransaction operation.\n")); - operation->setOnReceiveReqListener(onRemoteStopTransactionReceiveRequest); - operation->setOnSendConfListener(onRemoteStopTransactionSendConf); - } else if (!strcmp(messageType, "ChangeConfiguration")) { - msg = new Ocpp16::ChangeConfiguration(); - operation->setOnReceiveReqListener(onChangeConfigurationReceiveReq); - operation->setOnSendConfListener(onChangeConfigurationSendConf); - } else if (!strcmp(messageType, "GetConfiguration")) { - msg = new Ocpp16::GetConfiguration(); - operation->setOnReceiveReqListener(onGetConfigurationReceiveReq); - operation->setOnSendConfListener(onGetConfigurationSendConf); - } else if (!strcmp(messageType, "Reset")) { - msg = new Ocpp16::Reset(); - if (onResetSendConf == NULL && onResetReceiveReq == NULL) - Serial.print(F("[SimpleOcppOperationFactory] Warning: Reset is without effect when the sendConf and receiveReq listener is not set. Set a listener which resets your device.\n")); - operation->setOnReceiveReqListener(onResetReceiveReq); - operation->setOnSendConfListener(onResetSendConf); - } else if (!strcmp(messageType, "UpdateFirmware")) { - msg = new Ocpp16::UpdateFirmware(); - //if (onUpdateFirmwareReceiveReq == NULL) - // Serial.print(F("[SimpleOcppOperationFactory] Warning: UpdateFirmware is without effect when the receiveReq listener is not set. Please implement a FW update routine for your device.\n")); - operation->setOnReceiveReqListener(onUpdateFirmwareReceiveReq); - } else if (!strcmp(messageType, "FirmwareStatusNotification")) { - msg = new Ocpp16::FirmwareStatusNotification(); - } else if (!strcmp(messageType, "GetDiagnostics")) { - msg = new Ocpp16::GetDiagnostics(); - } else if (!strcmp(messageType, "DiagnosticsStatusNotification")) { - msg = new Ocpp16::DiagnosticsStatusNotification(); - } else if (!strcmp(messageType, "UnlockConnector")) { - msg = new Ocpp16::UnlockConnector(); - } else if (!strcmp(messageType, "ClearChargingProfile")) { - msg = new Ocpp16::ClearChargingProfile(); - } else if (!strcmp(messageType, "ChangeAvailability")) { - msg = new Ocpp16::ChangeAvailability(); - } else { - Serial.println(F("[SimpleOcppOperationFactory] Operation not supported")); - msg = new NotImplemented(); - } - - if (msg == NULL) { - delete operation; - return NULL; - } else { - operation->setOcppMessage(msg); - return operation; - } +std::unique_ptr makeOcppOperation(const char *messageType) { + auto operation = makeOcppOperation(); + auto msg = std::unique_ptr{nullptr}; + + if (CustomOcppMessageCreatorEntry *entry = makeCustomOcppMessage(messageType)) { + msg = std::unique_ptr(entry->creator()); + operation->setOnReceiveReqListener(entry->onReceiveReq); + } else if (!strcmp(messageType, "Authorize")) { + msg = std::unique_ptr(new Ocpp16::Authorize()); + operation->setOnReceiveReqListener(onAuthorizeRequest); + } else if (!strcmp(messageType, "BootNotification")) { + msg = std::unique_ptr(new Ocpp16::BootNotification()); + operation->setOnReceiveReqListener(onBootNotificationRequest); + } else if (!strcmp(messageType, "Heartbeat")) { + msg = std::unique_ptr(new Ocpp16::Heartbeat()); + } else if (!strcmp(messageType, "MeterValues")) { + msg = std::unique_ptr(new Ocpp16::MeterValues()); + operation->setOnReceiveReqListener(onMeterValuesReceiveReq); + } else if (!strcmp(messageType, "SetChargingProfile")) { + msg = std::unique_ptr(new Ocpp16::SetChargingProfile(getSmartChargingService())); + operation->setOnReceiveReqListener(onSetChargingProfileRequest); + } else if (!strcmp(messageType, "StatusNotification")) { + msg = std::unique_ptr(new Ocpp16::StatusNotification()); + } else if (!strcmp(messageType, "StartTransaction")) { + msg = std::unique_ptr(new Ocpp16::StartTransaction(1)); //connectorId 1 + operation->setOnReceiveReqListener(onStartTransactionRequest); + } else if (!strcmp(messageType, "StopTransaction")) { + msg = std::unique_ptr(new Ocpp16::StopTransaction()); + } else if (!strcmp(messageType, "TriggerMessage")) { + msg = std::unique_ptr(new Ocpp16::TriggerMessage()); + operation->setOnReceiveReqListener(onTriggerMessageRequest); + } else if (!strcmp(messageType, "RemoteStartTransaction")) { + msg = std::unique_ptr(new Ocpp16::RemoteStartTransaction()); + operation->setOnReceiveReqListener(onRemoteStartTransactionReceiveRequest); + if (onRemoteStartTransactionSendConf == nullptr) + Serial.print(F("[SimpleOcppOperationFactory] Warning: RemoteStartTransaction is without effect when the sendConf listener is not set. Set a listener which initiates the StartTransaction operation.\n")); + operation->setOnSendConfListener(onRemoteStartTransactionSendConf); + } else if (!strcmp(messageType, "RemoteStopTransaction")) { + msg = std::unique_ptr(new Ocpp16::RemoteStopTransaction()); + if (onRemoteStopTransactionSendConf == nullptr) + Serial.print(F("[SimpleOcppOperationFactory] Warning: RemoteStopTransaction is without effect when no sendConf listener is set. Set a listener which initiates the StopTransaction operation.\n")); + operation->setOnReceiveReqListener(onRemoteStopTransactionReceiveRequest); + operation->setOnSendConfListener(onRemoteStopTransactionSendConf); + } else if (!strcmp(messageType, "ChangeConfiguration")) { + msg = std::unique_ptr(new Ocpp16::ChangeConfiguration()); + operation->setOnReceiveReqListener(onChangeConfigurationReceiveReq); + operation->setOnSendConfListener(onChangeConfigurationSendConf); + } else if (!strcmp(messageType, "GetConfiguration")) { + msg = std::unique_ptr(new Ocpp16::GetConfiguration()); + operation->setOnReceiveReqListener(onGetConfigurationReceiveReq); + operation->setOnSendConfListener(onGetConfigurationSendConf); + } else if (!strcmp(messageType, "Reset")) { + msg = std::unique_ptr(new Ocpp16::Reset()); + if (onResetSendConf == nullptr && onResetReceiveReq == nullptr) + Serial.print(F("[SimpleOcppOperationFactory] Warning: Reset is without effect when the sendConf and receiveReq listener is not set. Set a listener which resets your device.\n")); + operation->setOnReceiveReqListener(onResetReceiveReq); + operation->setOnSendConfListener(onResetSendConf); + } else if (!strcmp(messageType, "UpdateFirmware")) { + msg = std::unique_ptr(new Ocpp16::UpdateFirmware()); + //if (onUpdateFirmwareReceiveReq == nullptr) + // Serial.print(F("[SimpleOcppOperationFactory] Warning: UpdateFirmware is without effect when the receiveReq listener is not set. Please implement a FW update routine for your device.\n")); + operation->setOnReceiveReqListener(onUpdateFirmwareReceiveReq); + } else if (!strcmp(messageType, "FirmwareStatusNotification")) { + msg = std::unique_ptr(new Ocpp16::FirmwareStatusNotification()); + } else if (!strcmp(messageType, "GetDiagnostics")) { + msg = std::unique_ptr(new Ocpp16::GetDiagnostics()); + } else if (!strcmp(messageType, "DiagnosticsStatusNotification")) { + msg = std::unique_ptr(new Ocpp16::DiagnosticsStatusNotification()); + } else if (!strcmp(messageType, "UnlockConnector")) { + msg = std::unique_ptr(new Ocpp16::UnlockConnector()); + } else if (!strcmp(messageType, "ClearChargingProfile")) { + msg = std::unique_ptr(new Ocpp16::ClearChargingProfile()); + } else if (!strcmp(messageType, "ChangeAvailability")) { + msg = std::unique_ptr(new Ocpp16::ChangeAvailability()); + } else { + Serial.println(F("[SimpleOcppOperationFactory] Operation not supported")); + msg = std::unique_ptr(new NotImplemented()); + } + + if (msg == nullptr) { + return nullptr; + } else { + operation->setOcppMessage(std::move(msg)); + return operation; + } } -OcppOperation* makeOcppOperation(OcppMessage *msg){ - if (msg == NULL) { - Serial.print(F("[SimpleOcppOperationFactory] in makeOcppOperation(webSocket, ocppMessage): ocppMessage is null!\n")); - return NULL; - } - OcppOperation *operation = makeOcppOperation(); - operation->setOcppMessage(msg); - return operation; +std::unique_ptr makeOcppOperation(OcppMessage *msg){ + if (msg == nullptr) { + Serial.print(F("[SimpleOcppOperationFactory] in makeOcppOperation(webSocket, ocppMessage): ocppMessage is null!\n")); + return nullptr; + } + auto operation = makeOcppOperation(); + operation->setOcppMessage(std::unique_ptr(msg)); + return operation; } -OcppOperation* makeOcppOperation(){ - OcppOperation *result = new OcppOperation(); - return result; +std::unique_ptr makeOcppOperation(){ + auto result = std::unique_ptr(new OcppOperation()); + return result; } } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.h b/src/ArduinoOcpp/SimpleOcppOperationFactory.h index 1ebaeabe..e3266ec8 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.h +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.h @@ -11,17 +11,17 @@ namespace ArduinoOcpp { -typedef std::function OcppMessageCreator; +using OcppMessageCreator = std::function; -OcppOperation* makeFromTriggerMessage(JsonObject payload); +std::unique_ptr makeFromTriggerMessage(JsonObject payload); -OcppOperation* makeFromJson(JsonDocument *request); +std::unique_ptr makeFromJson(const JsonDocument& request); -OcppOperation* makeOcppOperation(); +std::unique_ptr makeOcppOperation(); -OcppOperation* makeOcppOperation(OcppMessage *msg); +std::unique_ptr makeOcppOperation(OcppMessage *msg); -OcppOperation *makeOcppOperation(const char *actionCode); +std::unique_ptr makeOcppOperation(const char *actionCode); void registerCustomOcppMessage(const char *messageType, OcppMessageCreator ocppMessageCreator, OnReceiveReqListener onReceiveReq = NULL); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index f6417597..434122e0 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -36,9 +36,9 @@ void ChargePointStatusService::loop() { if (!booted) return; for (int i = 0; i < numConnectors; i++){ StatusNotification *statusNotificationMsg = connectors[i]->loop(); - if (statusNotificationMsg != NULL) { - OcppOperation *statusNotification = makeOcppOperation(statusNotificationMsg); - initiateOcppOperation(statusNotification); + if (statusNotificationMsg != nullptr) { + auto statusNotification = makeOcppOperation(statusNotificationMsg); + initiateOcppOperation(std::move(statusNotification)); } } } @@ -46,7 +46,7 @@ void ChargePointStatusService::loop() { ConnectorStatus *ChargePointStatusService::getConnector(int connectorId) { if (connectorId < 0 || connectorId >= numConnectors) { Serial.print(F("[ChargePointStatusService] Error in getConnector(connectorId): connectorId is out of bounds\n")); - return NULL; + return nullptr; } return connectors[connectorId]; diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp index 640cb8db..babc1b1a 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp @@ -10,16 +10,16 @@ using namespace ArduinoOcpp; using Ocpp16::DiagnosticsStatus; void DiagnosticsService::loop() { - OcppOperation *notification = getDiagnosticsStatusNotification(); + auto notification = getDiagnosticsStatusNotification(); if (notification) { - initiateOcppOperation(notification); + initiateOcppOperation(std::move(notification)); } const OcppTimestamp& timestampNow = getOcppTime()->getOcppTimestampNow(); if (retries > 0 && timestampNow >= nextTry) { if (!uploadIssued) { - if (onUpload != NULL) { + if (onUpload != nullptr) { if (DEBUG_OUT) Serial.println(F("[DiagnosticsService] call onUpload")); onUpload(location, startTime, stopTime); uploadIssued = true; @@ -31,7 +31,7 @@ void DiagnosticsService::loop() { } if (uploadIssued) { - if (uploadStatusSampler != NULL && uploadStatusSampler() == UploadStatus::Uploaded) { + if (uploadStatusSampler != nullptr && uploadStatusSampler() == UploadStatus::Uploaded) { //success! if (DEBUG_OUT) Serial.println(F("[DiagnosticsService] end update routine (by status)")); uploadIssued = false; @@ -41,10 +41,10 @@ void DiagnosticsService::loop() { //check if maximum time elapsed or failed const int UPLOAD_TIMEOUT = 60; if (timestampNow - nextTry >= UPLOAD_TIMEOUT - || (uploadStatusSampler != NULL && uploadStatusSampler() == UploadStatus::UploadFailed)) { + || (uploadStatusSampler != nullptr && uploadStatusSampler() == UploadStatus::UploadFailed)) { //maximum upload time elapsed or failed - if (uploadStatusSampler == NULL) { + if (uploadStatusSampler == nullptr) { //No way to find out if failed. But maximum time has elapsed. Assume success if (DEBUG_OUT) Serial.println(F("[DiagnosticsService] end update routine (by timer)")); uploadIssued = false; @@ -68,7 +68,7 @@ void DiagnosticsService::loop() { //timestamps before year 2021 will be treated as "undefined" String DiagnosticsService::requestDiagnosticsUpload(String &location, int retries, unsigned int retryInterval, OcppTimestamp startTime, OcppTimestamp stopTime) { - if (onUpload == NULL) //maybe add further plausibility checks + if (onUpload == nullptr) //maybe add further plausibility checks return String('\0'); this->location = location; @@ -119,7 +119,7 @@ String DiagnosticsService::requestDiagnosticsUpload(String &location, int retrie DiagnosticsStatus DiagnosticsService::getDiagnosticsStatus() { if (uploadIssued) { - if (uploadStatusSampler != NULL) { + if (uploadStatusSampler != nullptr) { switch (uploadStatusSampler()) { case UploadStatus::NotUploaded: return DiagnosticsStatus::Uploading; @@ -134,18 +134,18 @@ DiagnosticsStatus DiagnosticsService::getDiagnosticsStatus() { return DiagnosticsStatus::Idle; } -OcppOperation *DiagnosticsService::getDiagnosticsStatusNotification() { +std::unique_ptr DiagnosticsService::getDiagnosticsStatusNotification() { if (getDiagnosticsStatus() != lastReportedStatus) { lastReportedStatus = getDiagnosticsStatus(); if (lastReportedStatus != DiagnosticsStatus::Idle) { OcppMessage *diagNotificationMsg = new Ocpp16::DiagnosticsStatusNotification(lastReportedStatus); - OcppOperation *diagNotification = makeOcppOperation(diagNotificationMsg); + auto diagNotification = makeOcppOperation(diagNotificationMsg); return diagNotification; } } - return NULL; + return nullptr; } void DiagnosticsService::setOnUpload(std::function onUpload) { diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h index 050adf65..178e8a4e 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h @@ -33,7 +33,7 @@ class DiagnosticsService { std::function uploadStatusSampler = NULL; bool uploadIssued = false; - OcppOperation *getDiagnosticsStatusNotification(); + std::unique_ptr getDiagnosticsStatusNotification(); Ocpp16::DiagnosticsStatus lastReportedStatus = Ocpp16::DiagnosticsStatus::Idle; diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index ee6ae7d6..9ca96807 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -13,7 +13,7 @@ using namespace ArduinoOcpp; using ArduinoOcpp::Ocpp16::FirmwareStatus; void FirmwareService::setBuildNumber(const char *buildNumber) { - if (buildNumber == NULL) + if (buildNumber == nullptr) return; this->buildNumber = buildNumber; previousBuildNumber = declareConfiguration("BUILD_NUMBER", buildNumber, CONFIGURATION_FN, false, false, true, false); @@ -21,9 +21,9 @@ void FirmwareService::setBuildNumber(const char *buildNumber) { } void FirmwareService::loop() { - OcppOperation *notification = getFirmwareStatusNotification(); + auto notification = getFirmwareStatusNotification(); if (notification) { - initiateOcppOperation(notification); + initiateOcppOperation(std::move(notification)); } if (millis() - timestampTransition < delayTransition) { @@ -43,7 +43,7 @@ void FirmwareService::loop() { } evse->setAvailability(false); } - if (onDownload == NULL) { + if (onDownload == nullptr) { stage = UpdateStage::AfterDownload; } else { downloadIssued = true; @@ -58,7 +58,7 @@ void FirmwareService::loop() { if (stage == UpdateStage::AwaitDownload) { if (DEBUG_OUT) Serial.println(F("[FirmwareService] Start Download!")); stage = UpdateStage::Downloading; - if (onDownload != NULL) { + if (onDownload != nullptr) { onDownload(location); timestampTransition = millis(); delayTransition = 30000; //give the download at least 30s @@ -71,18 +71,18 @@ void FirmwareService::loop() { if (stage == UpdateStage::Downloading) { //check if client reports download to be finished - if (downloadStatusSampler != NULL && downloadStatusSampler() == DownloadStatus::Downloaded) { + if (downloadStatusSampler != nullptr && downloadStatusSampler() == DownloadStatus::Downloaded) { stage = UpdateStage::AfterDownload; } //if client doesn't report download state, assume download to be finished (at least 30s download time have passed until here) - if (downloadStatusSampler == NULL) { + if (downloadStatusSampler == nullptr) { stage = UpdateStage::AfterDownload; } //check for timeout or error condition if (timestampNow - retreiveDate >= DOWNLOAD_TIMEOUT - || (downloadStatusSampler != NULL && downloadStatusSampler() == DownloadStatus::DownloadFailed)) { + || (downloadStatusSampler != nullptr && downloadStatusSampler() == DownloadStatus::DownloadFailed)) { Serial.println(F("[FirmwareService] Download timeout or failed! Retry")); if (retryInterval < DOWNLOAD_TIMEOUT) @@ -129,7 +129,7 @@ void FirmwareService::loop() { if (DEBUG_OUT) Serial.println(F("[FirmwareService] Installing!")); stage = UpdateStage::Installing; - if (onInstall != NULL) { + if (onInstall != nullptr) { onInstall(location); //should restart the device on success } else { Serial.println(F("[FirmwareService] onInstall must be set! (see setOnInstall). Will abort")); @@ -143,9 +143,9 @@ void FirmwareService::loop() { if (stage == UpdateStage::Installing) { //check if client reports installation to be finished - if ((installationStatusSampler != NULL && installationStatusSampler() == InstallationStatus::Installed) + if ((installationStatusSampler != nullptr && installationStatusSampler() == InstallationStatus::Installed) //if client doesn't report installation state, assume download to be finished (at least 40s installation time have passed until here) - || (installationStatusSampler == NULL)) { + || (installationStatusSampler == nullptr)) { //Client should reboot during onInstall. If not, client is responsible to reboot at a later point resetStage(); retries = 0; //End of update routine. Client must reboot on its own @@ -160,7 +160,7 @@ void FirmwareService::loop() { //check for timeout or error condition const int INSTALLATION_TIMEOUT = 120; if ((timestampNow - retreiveDate >= INSTALLATION_TIMEOUT + DOWNLOAD_TIMEOUT) - || (installationStatusSampler != NULL && installationStatusSampler() == InstallationStatus::InstallationFailed)) { + || (installationStatusSampler != nullptr && installationStatusSampler() == InstallationStatus::InstallationFailed)) { Serial.println(F("[FirmwareService] Installation timeout or failed! Retry")); if (retryInterval < INSTALLATION_TIMEOUT + DOWNLOAD_TIMEOUT) @@ -208,37 +208,37 @@ void FirmwareService::scheduleFirmwareUpdate(String &location, OcppTimestamp ret FirmwareStatus FirmwareService::getFirmwareStatus() { if (installationIssued) { - if (installationStatusSampler != NULL) { + if (installationStatusSampler != nullptr) { if (installationStatusSampler() == InstallationStatus::Installed) { return FirmwareStatus::Installed; } else if (installationStatusSampler() == InstallationStatus::InstallationFailed) { return FirmwareStatus::InstallationFailed; } } - if (onInstall != NULL) + if (onInstall != nullptr) return FirmwareStatus::Installing; } if (downloadIssued) { - if (downloadStatusSampler != NULL) { + if (downloadStatusSampler != nullptr) { if (downloadStatusSampler() == DownloadStatus::Downloaded) { return FirmwareStatus::Downloaded; } else if (downloadStatusSampler() == DownloadStatus::DownloadFailed) { return FirmwareStatus::DownloadFailed; } } - if (onDownload != NULL) + if (onDownload != nullptr) return FirmwareStatus::Downloading; } return FirmwareStatus::Idle; } -OcppOperation *FirmwareService::getFirmwareStatusNotification() { +std::unique_ptr FirmwareService::getFirmwareStatusNotification() { /* * Check if FW has been updated previously, but only once */ - if (!checkedSuccessfulFwUpdate && buildNumber != NULL && previousBuildNumber != NULL) { + if (!checkedSuccessfulFwUpdate && buildNumber != nullptr && previousBuildNumber != nullptr) { checkedSuccessfulFwUpdate = true; size_t buildNoSize = previousBuildNumber->getBuffsize(); @@ -249,7 +249,7 @@ OcppOperation *FirmwareService::getFirmwareStatusNotification() { lastReportedStatus = FirmwareStatus::Installed; OcppMessage *fwNotificationMsg = new Ocpp16::FirmwareStatusNotification(lastReportedStatus); - OcppOperation *fwNotification = makeOcppOperation(fwNotificationMsg); + auto fwNotification = makeOcppOperation(fwNotificationMsg); return fwNotification; } } @@ -258,12 +258,12 @@ OcppOperation *FirmwareService::getFirmwareStatusNotification() { lastReportedStatus = getFirmwareStatus(); if (lastReportedStatus != FirmwareStatus::Idle && lastReportedStatus != FirmwareStatus::Installed) { OcppMessage *fwNotificationMsg = new Ocpp16::FirmwareStatusNotification(lastReportedStatus); - OcppOperation *fwNotification = makeOcppOperation(fwNotificationMsg); + auto fwNotification = makeOcppOperation(fwNotificationMsg); return fwNotification; } } - return NULL; + return nullptr; } void FirmwareService::setOnDownload(std::function onDownload) { diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h index 1426d365..120c97de 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h @@ -66,7 +66,7 @@ class FirmwareService { bool availabilityRestore = false; - OcppOperation *getFirmwareStatusNotification(); + std::unique_ptr getFirmwareStatusNotification(); public: FirmwareService() { } diff --git a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp index 22f16807..a8552caf 100644 --- a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp +++ b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp @@ -22,7 +22,7 @@ void HeartbeatService::loop() { if (now - lastHeartbeat >= hbInterval) { lastHeartbeat = now; - OcppOperation *heartbeat = makeOcppOperation("Heartbeat"); - initiateOcppOperation(heartbeat); + auto heartbeat = makeOcppOperation("Heartbeat"); + initiateOcppOperation(std::move(heartbeat)); } } diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp index 36a2eafc..3dd78f04 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp @@ -33,10 +33,10 @@ void MeteringService::loop(){ for (int i = 0; i < numConnectors; i++){ MeterValues *meterValuesMsg = connectors[i]->loop(); - if (meterValuesMsg != NULL) { - OcppOperation *meterValues = makeOcppOperation(meterValuesMsg); - meterValues->setTimeout(new FixedTimeout(120000)); - initiateOcppOperation(meterValues); + if (meterValuesMsg != nullptr) { + auto meterValues = makeOcppOperation(meterValuesMsg); + meterValues->setTimeout(std::unique_ptr{new FixedTimeout(120000)}); + initiateOcppOperation(std::move(meterValues)); } } } From 1ca99693f4a4c586b70d8f2f7d7f01a2ce0cfbb9 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 14 Dec 2021 22:49:21 +0100 Subject: [PATCH 091/696] more mem mgt techniques, OCPP_deinit(), refactor --- examples/SECC/main.cpp | 19 +- min_spiffs.csv | 6 - platformio.ini | 2 +- src/ArduinoOcpp.cpp | 393 ++++++++++-------- src/ArduinoOcpp.h | 12 +- .../Core/ConfigurationKeyValue.cpp | 69 +-- src/ArduinoOcpp/Core/ConfigurationKeyValue.h | 27 +- src/ArduinoOcpp/Core/OcppConnection.cpp | 20 +- src/ArduinoOcpp/Core/OcppConnection.h | 14 +- src/ArduinoOcpp/Core/OcppEngine.cpp | 129 +----- src/ArduinoOcpp/Core/OcppEngine.h | 51 +-- src/ArduinoOcpp/Core/OcppError.h | 8 +- src/ArduinoOcpp/Core/OcppMessage.cpp | 29 +- src/ArduinoOcpp/Core/OcppMessage.h | 92 ++-- src/ArduinoOcpp/Core/OcppModel.cpp | 101 +++++ src/ArduinoOcpp/Core/OcppModel.h | 63 +++ src/ArduinoOcpp/Core/OcppOperation.cpp | 15 + src/ArduinoOcpp/Core/OcppOperation.h | 20 +- src/ArduinoOcpp/Core/OcppOperationCallbacks.h | 23 + src/ArduinoOcpp/Core/OcppOperationTimeout.h | 4 +- src/ArduinoOcpp/Core/OcppTime.cpp | 5 +- src/ArduinoOcpp/Core/OcppTime.h | 6 +- src/ArduinoOcpp/MessagesV16/Authorize.cpp | 27 +- src/ArduinoOcpp/MessagesV16/Authorize.h | 16 +- .../MessagesV16/BootNotification.cpp | 37 +- .../MessagesV16/BootNotification.h | 5 +- .../MessagesV16/ChangeAvailability.cpp | 11 +- .../MessagesV16/ChangeAvailability.h | 2 +- .../MessagesV16/ChangeConfiguration.cpp | 22 +- .../MessagesV16/ChangeConfiguration.h | 2 +- .../MessagesV16/ClearChargingProfile.cpp | 11 +- .../MessagesV16/ClearChargingProfile.h | 2 +- src/ArduinoOcpp/MessagesV16/DataTransfer.cpp | 13 +- src/ArduinoOcpp/MessagesV16/DataTransfer.h | 10 +- .../DiagnosticsStatusNotification.cpp | 20 +- .../DiagnosticsStatusNotification.h | 22 +- .../FirmwareStatusNotification.cpp | 20 +- .../MessagesV16/FirmwareStatusNotification.h | 26 +- .../MessagesV16/GetConfiguration.cpp | 6 +- .../MessagesV16/GetConfiguration.h | 2 +- .../MessagesV16/GetDiagnostics.cpp | 17 +- src/ArduinoOcpp/MessagesV16/GetDiagnostics.h | 28 +- src/ArduinoOcpp/MessagesV16/Heartbeat.cpp | 30 +- src/ArduinoOcpp/MessagesV16/Heartbeat.h | 12 +- src/ArduinoOcpp/MessagesV16/MeterValues.cpp | 37 +- src/ArduinoOcpp/MessagesV16/MeterValues.h | 30 +- .../MessagesV16/RemoteStartTransaction.cpp | 28 +- .../MessagesV16/RemoteStartTransaction.h | 14 +- .../MessagesV16/RemoteStopTransaction.cpp | 47 ++- .../MessagesV16/RemoteStopTransaction.h | 10 +- src/ArduinoOcpp/MessagesV16/Reset.cpp | 27 +- src/ArduinoOcpp/MessagesV16/Reset.h | 8 +- .../MessagesV16/SetChargingProfile.cpp | 33 +- .../MessagesV16/SetChargingProfile.h | 13 +- .../MessagesV16/StartTransaction.cpp | 51 +-- .../MessagesV16/StartTransaction.h | 26 +- .../MessagesV16/StatusNotification.cpp | 15 +- .../MessagesV16/StatusNotification.h | 10 +- .../MessagesV16/StopTransaction.cpp | 33 +- src/ArduinoOcpp/MessagesV16/StopTransaction.h | 4 +- .../MessagesV16/TriggerMessage.cpp | 14 +- src/ArduinoOcpp/MessagesV16/TriggerMessage.h | 8 +- .../MessagesV16/UnlockConnector.cpp | 32 +- src/ArduinoOcpp/MessagesV16/UnlockConnector.h | 3 +- .../MessagesV16/UpdateFirmware.cpp | 16 +- src/ArduinoOcpp/MessagesV16/UpdateFirmware.h | 2 +- .../SimpleOcppOperationFactory.cpp | 52 ++- src/ArduinoOcpp/SimpleOcppOperationFactory.h | 3 +- .../ChargePointStatusService.cpp | 16 +- .../ChargePointStatusService.h | 7 +- .../ChargePointStatus/ConnectorStatus.cpp | 37 +- .../Tasks/ChargePointStatus/ConnectorStatus.h | 24 +- .../Tasks/Diagnostics/DiagnosticsService.cpp | 17 +- .../Tasks/Diagnostics/DiagnosticsService.h | 17 +- .../Tasks/Diagnostics/DiagnosticsStatus.h | 20 + .../FirmwareManagement/FirmwareService.cpp | 36 +- .../FirmwareManagement/FirmwareService.h | 12 +- .../Tasks/FirmwareManagement/FirmwareStatus.h | 23 + .../Tasks/Heartbeat/HeartbeatService.cpp | 9 +- .../Tasks/Heartbeat/HeartbeatService.h | 8 +- .../Metering/ConnectorMeterValuesRecorder.cpp | 47 ++- .../Metering/ConnectorMeterValuesRecorder.h | 26 +- .../Tasks/Metering/MeteringService.cpp | 15 +- .../Tasks/Metering/MeteringService.h | 26 +- .../SmartCharging/SmartChargingModel.cpp | 12 +- .../Tasks/SmartCharging/SmartChargingModel.h | 5 +- .../SmartCharging/SmartChargingService.cpp | 63 +-- .../SmartCharging/SmartChargingService.h | 13 +- 88 files changed, 1302 insertions(+), 1096 deletions(-) delete mode 100644 min_spiffs.csv create mode 100644 src/ArduinoOcpp/Core/OcppModel.cpp create mode 100644 src/ArduinoOcpp/Core/OcppModel.h create mode 100644 src/ArduinoOcpp/Core/OcppOperationCallbacks.h create mode 100644 src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsStatus.h create mode 100644 src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareStatus.h diff --git a/examples/SECC/main.cpp b/examples/SECC/main.cpp index de8a621b..ce5f3a2c 100644 --- a/examples/SECC/main.cpp +++ b/examples/SECC/main.cpp @@ -79,6 +79,9 @@ #define PRINTLN(...) Serial.println(__VA_ARGS__) #endif +WebSocketsClient wSock; +ArduinoOcpp::EspWiFi::OcppClientSocket oSock{&wSock}; + int evPlugged = EV_UNPLUGGED; bool booted = false; @@ -178,12 +181,11 @@ void setup() { /* * Initialize ArduinoOcpp framework. */ - WebSocketsClient *wSock = new WebSocketsClient(); - wSock->setReconnectInterval(5000); - wSock->enableHeartbeat(15000, 3000, 2); + wSock.setReconnectInterval(5000); + wSock.enableHeartbeat(15000, 3000, 2); if (httpAuthentication->getBuffsize() > 1) { - wSock->setAuthorization(*httpAuthentication); + wSock.setAuthorization(*httpAuthentication); } if (ocppUrlParsed.isTLS) { @@ -200,15 +202,14 @@ void setup() { PRINT(F(" finished. Unix timestamp is ")); PRINTLN(now); - wSock->beginSslWithCA(ocppUrlParsed.host.c_str(), ocppUrlParsed.port, ocppUrlParsed.url.c_str(), *CA_cert, "ocpp1.6"); + wSock.beginSslWithCA(ocppUrlParsed.host.c_str(), ocppUrlParsed.port, ocppUrlParsed.url.c_str(), *CA_cert, "ocpp1.6"); } else { - wSock->beginSSL(ocppUrlParsed.host.c_str(), ocppUrlParsed.port, ocppUrlParsed.url.c_str(), NULL, "ocpp1.6"); + wSock.beginSSL(ocppUrlParsed.host.c_str(), ocppUrlParsed.port, ocppUrlParsed.url.c_str(), NULL, "ocpp1.6"); } } else { - wSock->begin(ocppUrlParsed.host, ocppUrlParsed.port, ocppUrlParsed.url, "ocpp1.6"); + wSock.begin(ocppUrlParsed.host, ocppUrlParsed.port, ocppUrlParsed.url, "ocpp1.6"); } - ArduinoOcpp::EspWiFi::OcppClientSocket *oSock = new ArduinoOcpp::EspWiFi::OcppClientSocket(wSock); OCPP_initialize(oSock, /* Grid voltage */ 230.f, ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, @@ -383,7 +384,7 @@ bool runWiFiManager() { WiFiManagerParameter httpAuthenticationSavePararm ("
"); wifiManager.addParameter(&httpAuthenticationSavePararm); - wifiManager.setSaveParamsCallback([&ocppUrlParam = ocppUrlParam, &ocppUrl = ocppUrl, &wifiManager = wifiManager, &CA_cert = CA_cert, &caCertParam = caCertParam, &httpAuthentication = httpAuthentication, &httpAuthenticationParam = httpAuthenticationParam] () { + wifiManager.setSaveParamsCallback([&ocppUrlParam, ocppUrl, &wifiManager, CA_cert, &caCertParam, httpAuthentication, &httpAuthenticationParam] () { String newOcppUrl = String(ocppUrlParam.getValue()); newOcppUrl.trim(); Ocpp_URL newOcppUrlParsed = Ocpp_URL(); diff --git a/min_spiffs.csv b/min_spiffs.csv deleted file mode 100644 index 0b6a9ffd..00000000 --- a/min_spiffs.csv +++ /dev/null @@ -1,6 +0,0 @@ -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x5000, -otadata, data, ota, 0xe000, 0x2000, -app0, app, ota_0, 0x10000, 0x1E0000, -app1, app, ota_1, 0x1F0000,0x1E0000, -spiffs, data, spiffs, 0x3D0000,0x30000, diff --git a/platformio.ini b/platformio.ini index f1b00615..eb8da828 100644 --- a/platformio.ini +++ b/platformio.ini @@ -44,5 +44,5 @@ build_flags = -DAO_DEBUG_OUT -DAO_TRAFFIC_OUT -DCONFIG_LITTLEFS_FOR_IDF_3_2 -build_partitions = min_spiffs.csv +board_build.partitions = no_ota.csv upload_speed = 921600 diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 7d527cdc..818aeaf5 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -9,7 +9,7 @@ #if USE_FACADE #include -#include +#include #include #include #include @@ -23,82 +23,30 @@ #include #include #include -#include namespace ArduinoOcpp { namespace Facade { #ifndef AO_CUSTOM_WS -WebSocketsClient webSocket; +WebSocketsClient *webSocket {nullptr}; +OcppSocket *ocppSocket {nullptr}; #endif -OcppSocket *ocppSocket; - -MeteringService *meteringService; -PowerSampler powerSampler; -EnergySampler energySampler; -std::function evRequestsEnergySampler = NULL; //bool (*evRequestsEnergySampler)() = NULL; -bool evRequestsEnergyLastState = false; -std::function connectorEnergizedSampler = NULL; -bool connectorEnergizedLastState = false; -SmartChargingService *smartChargingService; -ChargePointStatusService *chargePointStatusService; -HeartbeatService *heartbeatService; -FirmwareService *firmwareService = NULL; -DiagnosticsService *diagnosticsServce = NULL; -OnLimitChange onLimitChange; -OcppTime *ocppTime; + +PowerSampler powerSampler {nullptr}; +std::function evRequestsEnergySampler {nullptr}; +bool evRequestsEnergyLastState {false}; +std::function connectorEnergizedSampler {nullptr}; +bool connectorEnergizedLastState {false}; + +OcppEngine *ocppEngine {nullptr}; +FilesystemOpt fileSystemOpt {}; +float voltage_eff {230.f}; #define OCPP_NUMCONNECTORS 2 #define OCPP_ID_OF_CONNECTOR 1 #define OCPP_ID_OF_CP 0 -boolean OCPP_initialized = false; boolean OCPP_booted = false; //if BootNotification succeeded -#if 0 //moved to OcppConnection -/* - Called by Websocket library on incoming message on the internet link -*/ -void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) { - - switch (type) { - case WStype_DISCONNECTED: - Serial.print(F("[WSc] Disconnected!\n")); - break; - case WStype_CONNECTED: - Serial.printf("[WSc] Connected to url: %s\n", payload); - break; - case WStype_TEXT: - if (DEBUG_OUT || TRAFFIC_OUT) Serial.printf("[WSc] get text: %s\n", payload); - - if (!processWebSocketEvent((const char *) payload, length)) { //forward message to OcppEngine - Serial.print(F("[WSc] Processing WebSocket input event failed!\n")); - } - break; - case WStype_FRAGMENT_TEXT_START: //fragments are not supported - Serial.print(F("[WSc] Fragments are not supported\n")); - if (!processWebSocketUnsupportedEvent((const char *) payload, length)) { //forward message to OcppEngine - Serial.print(F("[WSc] Processing WebSocket input event failed!\n")); - } - break; - case WStype_BIN: - Serial.print(F("[WSc] Incoming binary data stream not supported")); - break; - case WStype_PING: - // pong will be send automatically - Serial.print(F("[WSc] get ping\n")); - break; - case WStype_PONG: - // answer to a ping we send - Serial.print(F("[WSc] get pong\n")); - break; - default: - Serial.print(F("[WSc] Unsupported WebSocket event type\n")); - break; - } -} - -#endif //moved to OcppConnection - } //end namespace ArduinoOcpp::Facade } //end namespace ArduinoOcpp @@ -108,132 +56,140 @@ using namespace ArduinoOcpp::Ocpp16; #ifndef AO_CUSTOM_WS void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, float V_eff, ArduinoOcpp::FilesystemOpt fsOpt, ArduinoOcpp::OcppClock system_time) { - if (OCPP_initialized) { + if (ocppEngine) { Serial.print(F("[ArduinoOcpp] Error: cannot call OCPP_initialize() two times! If you want to reconfigure the library, please restart your ESP\n")); return; } - // server address, port and URL - webSocket.begin(CS_hostname, CS_port, CS_url, "ocpp1.6"); - - // event handler - //webSocket.onEvent(webSocketEvent); //will be set in ocppEngine_initialize() + if (!webSocket) + webSocket = new WebSocketsClient(); - // use HTTP Basic Authorization this is optional remove if not needed - // webSocket.setAuthorization("user", "Password"); + // server address, port and URL + webSocket->begin(CS_hostname, CS_port, CS_url, "ocpp1.6"); // try ever 5000 again if connection has failed - webSocket.setReconnectInterval(5000); + webSocket->setReconnectInterval(5000); // start heartbeat (optional) // ping server every 15000 ms // expect pong from server within 3000 ms // consider connection disconnected if pong is not received 2 times - webSocket.enableHeartbeat(15000, 3000, 2); //comment this one out to for specific OCPP servers + webSocket->enableHeartbeat(15000, 3000, 2); //comment this one out to for specific OCPP servers - ocppSocket = new EspWiFi::OcppClientSocket(&webSocket); + delete ocppSocket; + ocppSocket = new EspWiFi::OcppClientSocket(webSocket); - OCPP_initialize(ocppSocket, V_eff, fsOpt); + OCPP_initialize(*ocppSocket, V_eff, fsOpt); } #endif -void OCPP_initialize(OcppSocket *ocppSocket, float V_eff, ArduinoOcpp::FilesystemOpt fsOpt, ArduinoOcpp::OcppClock system_time) { - if (OCPP_initialized) { +void OCPP_initialize(OcppSocket& ocppSocket, float V_eff, ArduinoOcpp::FilesystemOpt fsOpt, ArduinoOcpp::OcppClock system_time) { + if (ocppEngine) { Serial.print(F("[ArduinoOcpp] Error: cannot call OCPP_initialize() two times! If you want to reconfigure the library, please restart your ESP\n")); return; } - if (!ocppSocket) { - Serial.print(F("[ArduinoOcpp] OCPP_initialize(ocppSocket): ocppSocket cannot be NULL!\n")); - return; - } + voltage_eff = V_eff; + fileSystemOpt = fsOpt; - configuration_init(fsOpt); //call before each other library call - - ocppEngine_initialize(ocppSocket); + configuration_init(fileSystemOpt); //call before each other library call - ocppTime = new OcppTime(system_time); - ocppEngine_setOcppTime(ocppTime); + ocppEngine = new OcppEngine(ocppSocket, system_time); + auto& model = ocppEngine->getOcppModel(); - smartChargingService = new SmartChargingService(11000.0f, V_eff, OCPP_NUMCONNECTORS, ocppTime, fsOpt); //default charging limit: 11kW - chargePointStatusService = new ChargePointStatusService(OCPP_NUMCONNECTORS, ocppTime); //Constructor adds instance to ocppEngine in constructor - meteringService = new MeteringService(OCPP_NUMCONNECTORS, ocppTime); - heartbeatService = new HeartbeatService(); + model.setChargePointStatusService(std::unique_ptr( + new ChargePointStatusService(*ocppEngine, OCPP_NUMCONNECTORS))); + model.setHeartbeatService(std::unique_ptr( + new HeartbeatService(*ocppEngine))); #if !defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) - firmwareService = EspWiFi::makeFirmwareService("1234578901"); //instantiate FW service + ESP installation routine + model.setFirmwareService(std::unique_ptr( + EspWiFi::makeFirmwareService(*ocppEngine, "1234578901"))); //instantiate FW service + ESP installation routine #else - firmwareService = new FirmwareService(); //only instantiate FW service + model.setFirmwareService(std::unique_ptr( + new FirmwareService(*ocppEngine))); //only instantiate FW service #endif - setFirmwareService(firmwareService); #if !defined(AO_CUSTOM_DIAGNOSTICS) && !defined(AO_CUSTOM_WEBSOCKET) - diagnosticsServce = EspWiFi::makeDiagnosticsService(); //will only return "Rejected" because logging is not implemented yet + model.setDiagnosticsService(std::unique_ptr( + EspWiFi::makeDiagnosticsService(*ocppEngine))); //will only return "Rejected" because logging is not implemented yet #else - diagnosticsServce = new DiagnosticsService(); + model.setDiagnosticsService(std::unique_ptr( + new DiagnosticsService(*ocppEngine))); #endif - setDiagnosticsService(diagnosticsServce); - OCPP_initialized = true; + ocppEngine->setRunOcppTasks(false); //prevent OCPP classes from doing anything while booting +} + +void OCPP_deinitialize() { + Serial.println(F("[ArduinoOcpp] called OCPP_deinitialize: still experimental function. If you find problems, it would be great if you publish them on the GitHub page")); + + delete ocppEngine; + ocppEngine = nullptr; + +#ifndef AO_CUSTOM_WS + delete ocppSocket; + ocppSocket = nullptr; + delete webSocket; + webSocket = nullptr; +#endif + + simpleOcppFactory_deinitialize(); + + powerSampler = nullptr; + evRequestsEnergySampler = nullptr; + evRequestsEnergyLastState = false; + connectorEnergizedSampler = nullptr; + connectorEnergizedLastState = false; + + fileSystemOpt = FilesystemOpt(); + voltage_eff = 230.f; + + OCPP_booted = false; } void OCPP_loop() { - if (!OCPP_initialized) { + if (!ocppEngine) { Serial.print(F("[ArduinoOcpp] Error: you must call OCPP_initialize before calling the loop() function!\n")); delay(200); //Prevent this error message from flooding the Serial monitor. return; } - //webSocket.loop(); //moved to Core/OcppSocket - ocppEngine_loop(); //mandatory + ocppEngine->loop(); + + auto& model = ocppEngine->getOcppModel(); if (!OCPP_booted) { - if (chargePointStatusService->isBooted()) { + auto csService = model.getChargePointStatusService(); + if (!csService || csService->isBooted()) { OCPP_booted = true; + ocppEngine->setRunOcppTasks(true); } else { return; //wait until the first BootNotification succeeded } } - if (onLimitChange != NULL) { - smartChargingService->loop(); //optional - } - - chargePointStatusService->loop(); //optional - - if (powerSampler != NULL || energySampler != NULL) { - meteringService->loop(); //optional - } - - heartbeatService->loop(); - - if (firmwareService != NULL) { - firmwareService->loop(); - } - - if (diagnosticsServce != NULL) { - diagnosticsServce->loop(); - } - bool evRequestsEnergyNewState = true; - if (evRequestsEnergySampler != NULL) { + if (evRequestsEnergySampler != nullptr) { evRequestsEnergyNewState = evRequestsEnergySampler(); } else { - if (powerSampler != NULL) { - evRequestsEnergyNewState = powerSampler() >= 5.f; + if (powerSampler != nullptr) { + evRequestsEnergyNewState = powerSampler() >= 50.f; } } if (!evRequestsEnergyLastState && evRequestsEnergyNewState) { evRequestsEnergyLastState = true; - chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->startEvDrawsEnergy(); + if (model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)) + model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)->startEvDrawsEnergy(); } else if (evRequestsEnergyLastState && !evRequestsEnergyNewState) { evRequestsEnergyLastState = false; - chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->stopEvDrawsEnergy(); + if (model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)) + model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)->stopEvDrawsEnergy(); } bool connectorEnergizedNewState = true; - if (connectorEnergizedSampler != NULL) { + if (connectorEnergizedSampler != nullptr) { connectorEnergizedNewState = connectorEnergizedSampler(); } else { connectorEnergizedNewState = getTransactionId() >= 0; @@ -241,22 +197,41 @@ void OCPP_loop() { if (!connectorEnergizedLastState && connectorEnergizedNewState) { connectorEnergizedLastState = true; - chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->startEnergyOffer(); + if (model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)) + model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)->startEnergyOffer(); } else if (connectorEnergizedLastState && !connectorEnergizedNewState) { connectorEnergizedLastState = false; - chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->stopEnergyOffer(); + if (model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)) + model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)->stopEnergyOffer(); } } void setPowerActiveImportSampler(std::function power) { + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in setPowerActiveImportSampler(): You must call OCPP_initialize() before. Ignore")); + return; + } powerSampler = power; - meteringService->setPowerSampler(OCPP_ID_OF_CONNECTOR, powerSampler); //connectorId=1 + auto& model = ocppEngine->getOcppModel(); + if (!model.getMeteringService()) { + model.setMeteringSerivce(std::unique_ptr( + new MeteringService(*ocppEngine, OCPP_NUMCONNECTORS))); + } + model.getMeteringService()->setPowerSampler(OCPP_ID_OF_CONNECTOR, powerSampler); //connectorId=1 } void setEnergyActiveImportSampler(std::function energy) { - energySampler = energy; - meteringService->setEnergySampler(OCPP_ID_OF_CONNECTOR, energySampler); //connectorId=1 + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in setEnergyActiveImportSampler(): You must call OCPP_initialize() before. Ignore")); + return; + } + auto& model = ocppEngine->getOcppModel(); + if (!model.getMeteringService()) { + model.setMeteringSerivce(std::unique_ptr( + new MeteringService(*ocppEngine, OCPP_NUMCONNECTORS))); + } + model.getMeteringService()->setEnergySampler(OCPP_ID_OF_CONNECTOR, energy); //connectorId=1 } void setEvRequestsEnergySampler(std::function evRequestsEnergy) { @@ -268,44 +243,55 @@ void setConnectorEnergizedSampler(std::function connectorEnergized) { } void setConnectorPluggedSampler(std::function connectorPlugged) { - ConnectorStatus *connector = getConnectorStatus(OCPP_ID_OF_CONNECTOR); - if (connector) { - connector->setConnectorPluggedSampler(connectorPlugged); - } else { - Serial.print(F("[ArduinoOcpp] Error: called setConnectorPluggedSampler before initializing the library!\n")); + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in setConnectorPluggedSampler(): You must call OCPP_initialize() before. Ignore")); + return; } + auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (!connector) { + Serial.println(F("[ArduinoOcpp.cpp] in setConnectorPluggedSampler(): Could not find connector. Ignore")); + return; + } + connector->setConnectorPluggedSampler(connectorPlugged); } -//void setConnectorFaultedSampler(std::function connectorFaulted) { -// ConnectorStatus *connector = getConnectorStatus(OCPP_ID_OF_CONNECTOR); -// if (connector) { -// connector->setConnectorFaultedSampler(connectorFaulted); -// } else { -// Serial.print(F("[ArduinoOcpp] Error: called setConnectorFaultedSampler before initializing the library!\n")); -// } -//} - void addConnectorErrorCodeSampler(std::function connectorErrorCode) { - ConnectorStatus *connector = getConnectorStatus(OCPP_ID_OF_CONNECTOR); - if (connector) { - connector->addConnectorErrorCodeSampler(connectorErrorCode); - } else { - Serial.print(F("[ArduinoOcpp] Error: called addConnectorErrorCodeSampler before initializing the library!\n")); + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in addConnectorErrorCodeSampler(): You must call OCPP_initialize() before. Ignore")); + return; } + auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (!connector) { + Serial.println(F("[ArduinoOcpp.cpp] in addConnectorErrorCodeSampler(): Could not find connector. Ignore")); + return; + } + connector->addConnectorErrorCodeSampler(connectorErrorCode); } void setOnChargingRateLimitChange(std::function chargingRateChanged) { - onLimitChange = chargingRateChanged; - smartChargingService->setOnLimitChange(onLimitChange); + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in setOnChargingRateLimitChange(): You must call OCPP_initialize() before. Ignore")); + return; + } + auto& model = ocppEngine->getOcppModel(); + if (!model.getSmartChargingService()) { + model.setSmartChargingService(std::unique_ptr( + new SmartChargingService(*ocppEngine, 11000.0f, voltage_eff, OCPP_NUMCONNECTORS, fileSystemOpt))); //default charging limit: 11kW + } + model.getSmartChargingService()->setOnLimitChange(chargingRateChanged); } void setOnUnlockConnector(std::function unlockConnector) { - ConnectorStatus *connector = getConnectorStatus(OCPP_ID_OF_CONNECTOR); - if (connector) { - connector->setOnUnlockConnector(unlockConnector); - } else { - Serial.print(F("[ArduinoOcpp] Error: called setOnUnlockConnector before initializing the library!\n")); + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in setOnUnlockConnector(): You must call OCPP_initialize() before. Ignore")); + return; } + auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (!connector) { + Serial.println(F("[ArduinoOcpp.cpp] in setOnUnlockConnector(): Could not find connector. Ignore")); + return; + } + connector->setOnUnlockConnector(unlockConnector); } void setOnSetChargingProfileRequest(OnReceiveReqListener onReceiveReq) { @@ -333,6 +319,10 @@ void setOnResetReceiveReq(OnReceiveReqListener onReceiveReq) { } void authorize(String &idTag, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in authorize(): You must call OCPP_initialize() before. Ignore")); + return; + } auto authorize = makeOcppOperation( new Authorize(idTag)); if (onConf) @@ -347,10 +337,14 @@ void authorize(String &idTag, OnReceiveConfListener onConf, OnAbortListener onAb authorize->setTimeout(std::move(timeout)); else authorize->setTimeout(std::unique_ptr(new FixedTimeout(20000))); - initiateOcppOperation(std::move(authorize)); + ocppEngine->initiateOperation(std::move(authorize)); } void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in bootNotification(): You must call OCPP_initialize() before. Ignore")); + return; + } auto bootNotification = makeOcppOperation( new BootNotification(chargePointModel, chargePointVendor)); if (onConf) @@ -365,18 +359,26 @@ void bootNotification(String chargePointModel, String chargePointVendor, OnRecei bootNotification->setTimeout(std::move(timeout)); else bootNotification->setTimeout(std::unique_ptr (new SuppressedTimeout())); - initiateOcppOperation(std::move(bootNotification)); + ocppEngine->initiateOperation(std::move(bootNotification)); } void bootNotification(String &chargePointModel, String &chargePointVendor, String &chargePointSerialNumber, OnReceiveConfListener onConf) { + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in bootNotification(): You must call OCPP_initialize() before. Ignore")); + return; + } auto bootNotification = makeOcppOperation( new BootNotification(chargePointModel, chargePointVendor, chargePointSerialNumber)); bootNotification->setOnReceiveConfListener(onConf); bootNotification->setTimeout(std::unique_ptr (new SuppressedTimeout())); - initiateOcppOperation(std::move(bootNotification)); + ocppEngine->initiateOperation(std::move(bootNotification)); } void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in bootNotification(): You must call OCPP_initialize() before. Ignore")); + return; + } auto bootNotification = makeOcppOperation( new BootNotification(payload)); if (onConf) @@ -391,10 +393,14 @@ void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf bootNotification->setTimeout(std::move(timeout)); else bootNotification->setTimeout(std::unique_ptr(new SuppressedTimeout())); - initiateOcppOperation(std::move(bootNotification)); + ocppEngine->initiateOperation(std::move(bootNotification)); } void startTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in startTransaction(): You must call OCPP_initialize() before. Ignore")); + return; + } auto startTransaction = makeOcppOperation( new StartTransaction(OCPP_ID_OF_CONNECTOR)); if (onConf) @@ -409,18 +415,26 @@ void startTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnT startTransaction->setTimeout(std::move(timeout)); else startTransaction->setTimeout(std::unique_ptr(new SuppressedTimeout())); - initiateOcppOperation(std::move(startTransaction)); + ocppEngine->initiateOperation(std::move(startTransaction)); } void startTransaction(String &idTag, OnReceiveConfListener onConf) { + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in startTransaction(): You must call OCPP_initialize() before. Ignore")); + return; + } auto startTransaction = makeOcppOperation( new StartTransaction(OCPP_ID_OF_CONNECTOR, idTag)); startTransaction->setOnReceiveConfListener(onConf); startTransaction->setTimeout(std::unique_ptr(new SuppressedTimeout())); - initiateOcppOperation(std::move(startTransaction)); + ocppEngine->initiateOperation(std::move(startTransaction)); } void stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in stopTransaction(): You must call OCPP_initialize() before. Ignore")); + return; + } auto stopTransaction = makeOcppOperation( new StopTransaction(OCPP_ID_OF_CONNECTOR)); if (onConf) @@ -435,28 +449,49 @@ void stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTi stopTransaction->setTimeout(std::move(timeout)); else stopTransaction->setTimeout(std::unique_ptr(new SuppressedTimeout())); - initiateOcppOperation(std::move(stopTransaction)); + ocppEngine->initiateOperation(std::move(stopTransaction)); } -//void startEvDrawsEnergy() { -// chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->startEvDrawsEnergy(); -//} - -//void stopEvDrawsEnergy() { -// chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->stopEvDrawsEnergy(); -//} - int getTransactionId() { - return chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->getTransactionId(); + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in getTransactionId(): You must call OCPP_initialize() before")); + return -1; + } + auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (!connector) { + Serial.println(F("[ArduinoOcpp.cpp] in getTransactionId(): Could not find connector")); + return -1; + } + return connector->getTransactionId(); } bool existsUnboundIdTag() { - return chargePointStatusService->existsUnboundAuthorization(); + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in existsUnboundIdTag(): You must call OCPP_initialize() before")); + return false; + } + auto csService = ocppEngine->getOcppModel().getChargePointStatusService(); + if (!csService) { + Serial.println(F("[ArduinoOcpp.cpp] in existsUnboundIdTag(): Could not find connector")); + return false; + } + return csService->existsUnboundAuthorization(); } bool isAvailable() { - return (chargePointStatusService->getConnector(OCPP_ID_OF_CP)->getAvailability() != AVAILABILITY_INOPERATIVE) - && (chargePointStatusService->getConnector(OCPP_ID_OF_CONNECTOR)->getAvailability() != AVAILABILITY_INOPERATIVE); + if (!ocppEngine) { + Serial.println(F("[ArduinoOcpp.cpp] in isAvailable(): You must call OCPP_initialize() before")); + return true; //assume "true" as default state + } + auto& model = ocppEngine->getOcppModel(); + auto chargePoint = model.getConnectorStatus(OCPP_ID_OF_CP); + auto connector = model.getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (!chargePoint || !connector) { + Serial.println(F("[ArduinoOcpp.cpp] in isAvailable(): Could not find connector")); + return true; //assume "true" as default state + } + return (chargePoint->getAvailability() != AVAILABILITY_INOPERATIVE) + && (connector->getAvailability() != AVAILABILITY_INOPERATIVE); } #endif diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 43eba1d8..05b57e9b 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -5,10 +5,13 @@ #ifndef ARDUINOOCPP_H #define ARDUINOOCPP_H -#include -#include +#include + #include #include +#include +#include +#include #include "Variants.h" @@ -29,7 +32,10 @@ void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, float #endif //Lets you use your own WebSocket implementation -void OCPP_initialize(ArduinoOcpp::OcppSocket *ocppSocket, float V_eff = 230.f /*German grid*/, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, ArduinoOcpp::OcppClock system_time = ArduinoOcpp::Clocks::DEFAULT_CLOCK); +void OCPP_initialize(ArduinoOcpp::OcppSocket& ocppSocket, float V_eff = 230.f /*German grid*/, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, ArduinoOcpp::OcppClock system_time = ArduinoOcpp::Clocks::DEFAULT_CLOCK); + +//experimental; More testing required (help needed: it would be awesome if you can you publish your evaluation results on the GitHub page) +void OCPP_deinitialize(); void OCPP_loop(); diff --git a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp index cf0d721f..2325a83d 100644 --- a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp @@ -27,7 +27,7 @@ AbstractConfiguration::AbstractConfiguration() { } -AbstractConfiguration::AbstractConfiguration(JsonObject storedKeyValuePair) { +AbstractConfiguration::AbstractConfiguration(JsonObject &storedKeyValuePair) { if (storedKeyValuePair["key"].as().is()) { setKey(storedKeyValuePair["key"]); } else { @@ -36,14 +36,14 @@ AbstractConfiguration::AbstractConfiguration(JsonObject storedKeyValuePair) { } AbstractConfiguration::~AbstractConfiguration() { - if (key != NULL) { + if (key != nullptr) { free(key); } - key = NULL; + key = nullptr; } void AbstractConfiguration::printKey() { - if (key != NULL) { + if (key != nullptr) { Serial.print(key); } } @@ -53,7 +53,7 @@ size_t AbstractConfiguration::getStorageHeaderJsonCapacity() { + key_size + 1; //TODO key_size already considers 0-terminator. Is "+1" really necessary here? } -void AbstractConfiguration::storeStorageHeader(JsonObject keyValuePair) { +void AbstractConfiguration::storeStorageHeader(JsonObject &keyValuePair) { if (toBeRemovedFlag) return; keyValuePair["key"] = key; } @@ -63,7 +63,7 @@ size_t AbstractConfiguration::getOcppMsgHeaderJsonCapacity() { + key_size + 1; //TODO key_size already considers 0-terminator. Is "+1" really necessary here? } -void AbstractConfiguration::storeOcppMsgHeader(JsonObject keyValuePair) { +void AbstractConfiguration::storeOcppMsgHeader(JsonObject &keyValuePair) { if (toBeRemovedFlag) return; keyValuePair["key"] = key; if (remotePeerCanWrite) { @@ -74,11 +74,11 @@ void AbstractConfiguration::storeOcppMsgHeader(JsonObject keyValuePair) { } bool AbstractConfiguration::isValid() { - return initializedValue && key != NULL && key_size > 0 && !toBeRemovedFlag; + return initializedValue && key != nullptr && key_size > 0 && !toBeRemovedFlag; } bool AbstractConfiguration::setKey(const char *newKey) { - if (key != NULL || key_size > 0) { + if (key != nullptr || key_size > 0) { Serial.print(F("[AbstractConfiguration] Cannot change key or set key twice! Keep old value\n")); return false; } @@ -144,9 +144,10 @@ Configuration::Configuration() { } template -Configuration::Configuration(JsonObject storedKeyValuePair) : AbstractConfiguration(storedKeyValuePair) { - if (storedKeyValuePair["value"].as().is()) { - this->operator=(storedKeyValuePair["value"].as().as()); +Configuration::Configuration(JsonObject &storedKeyValuePair) : AbstractConfiguration(storedKeyValuePair) { + auto jsonEntry = storedKeyValuePair["value"].as(); + if (jsonEntry.is()) { + this->operator=(jsonEntry.as()); } else { Serial.print(F("[Configuration] Type mismatch: cannot deserialize Json to given Type T\n")); } @@ -176,6 +177,16 @@ const T &Configuration::operator=(const T & newVal) { return newVal; } +template +Configuration::operator T() { + if (!initializedValue) { +// Serial.print(F("[Configuration] Tried to access value without preceeding initialization: ")); +// printKey(); +// Serial.println(); + } + return value; +} + template bool Configuration::isValid() { return AbstractConfiguration::isValid(); @@ -198,7 +209,7 @@ size_t Configuration::getValueJsonCapacity() { template std::shared_ptr Configuration::toJsonStorageEntry() { if (!isValid() || toBeRemoved()) { - return NULL; + return nullptr; } size_t capacity = getStorageHeaderJsonCapacity() + getValueJsonCapacity() @@ -215,7 +226,7 @@ std::shared_ptr Configuration::toJsonStorageEntry() { template std::shared_ptr Configuration::toJsonOcppMsgEntry() { if (!isValid() || toBeRemoved()) { - return NULL; + return nullptr; } size_t capacity = getOcppMsgHeaderJsonCapacity() + getValueJsonCapacity() @@ -230,7 +241,7 @@ std::shared_ptr Configuration::toJsonOcppMsgEntry() { std::shared_ptr Configuration::toJsonStorageEntry() { if (!isValid() || toBeRemoved()) { - return NULL; + return nullptr; } size_t capacity = getStorageHeaderJsonCapacity() + getValueJsonCapacity() @@ -246,7 +257,7 @@ std::shared_ptr Configuration::toJsonStorageE std::shared_ptr Configuration::toJsonOcppMsgEntry() { if (!isValid() || toBeRemoved()) { - return NULL; + return nullptr; } size_t capacity = getOcppMsgHeaderJsonCapacity() + getValueJsonCapacity() @@ -259,7 +270,7 @@ std::shared_ptr Configuration::toJsonOcppMsgE return doc; } -Configuration::Configuration(JsonObject storedKeyValuePair) : AbstractConfiguration(storedKeyValuePair) { +Configuration::Configuration(JsonObject &storedKeyValuePair) : AbstractConfiguration(storedKeyValuePair) { if (storedKeyValuePair["value"].as().is()) { const char *storedValue = storedKeyValuePair["value"].as().as(); if (storedValue) { @@ -275,13 +286,13 @@ Configuration::Configuration(JsonObject storedKeyValuePair) : Abst } Configuration::~Configuration() { - if (value != NULL) { + if (value != nullptr) { free(value); - value = NULL; + value = nullptr; } - if (valueReadOnlyCopy != NULL) { + if (valueReadOnlyCopy != nullptr) { free(valueReadOnlyCopy); - valueReadOnlyCopy = NULL; + valueReadOnlyCopy = nullptr; } } @@ -338,14 +349,14 @@ bool Configuration::setValue(const char *new_value, size_t buffsiz if (value_changed || !initializedValue) { - if (value != NULL) { + if (value != nullptr) { free(value); - value = NULL; + value = nullptr; } - if (valueReadOnlyCopy != NULL) { + if (valueReadOnlyCopy != nullptr) { free(valueReadOnlyCopy); - valueReadOnlyCopy = NULL; + valueReadOnlyCopy = nullptr; } value_size = checkedBuffsize; @@ -355,14 +366,14 @@ bool Configuration::setValue(const char *new_value, size_t buffsiz if (!value || !valueReadOnlyCopy) { Serial.print(F("[Configuration] in setValue(): Could not allocate value or value copy\n")); - if (value != NULL) { + if (value != nullptr) { free(value); - value = NULL; + value = nullptr; } - if (valueReadOnlyCopy != NULL) { + if (valueReadOnlyCopy != nullptr) { free(valueReadOnlyCopy); - valueReadOnlyCopy = NULL; + valueReadOnlyCopy = nullptr; } value_size = 0; @@ -402,7 +413,7 @@ Configuration::operator const char*() { } bool Configuration::isValid() { - return AbstractConfiguration::isValid() && value != NULL && valueReadOnlyCopy != NULL && value_size > 0; + return AbstractConfiguration::isValid() && value != nullptr && valueReadOnlyCopy != nullptr && value_size > 0; } size_t Configuration::getBuffsize() { diff --git a/src/ArduinoOcpp/Core/ConfigurationKeyValue.h b/src/ArduinoOcpp/Core/ConfigurationKeyValue.h index 422654c0..d33fe3e8 100644 --- a/src/ArduinoOcpp/Core/ConfigurationKeyValue.h +++ b/src/ArduinoOcpp/Core/ConfigurationKeyValue.h @@ -12,8 +12,8 @@ namespace ArduinoOcpp { class AbstractConfiguration { private: - char *key = NULL; - size_t key_size = 0; // key=NULL --> key_size = 0; key = "" --> key_size = 1; key = "A" --> key_size = 2 + char *key = nullptr; + size_t key_size = 0; // key=nullptr --> key_size = 0; key = "" --> key_size = 1; key = "A" --> key_size = 2 bool rebootRequiredWhenChanged = false; @@ -29,11 +29,11 @@ class AbstractConfiguration { bool initializedValue = false; AbstractConfiguration(); - AbstractConfiguration(JsonObject storedKeyValuePair); + AbstractConfiguration(JsonObject &storedKeyValuePair); size_t getStorageHeaderJsonCapacity(); - void storeStorageHeader(JsonObject keyValuePair); + void storeStorageHeader(JsonObject &keyValuePair); size_t getOcppMsgHeaderJsonCapacity(); - void storeOcppMsgHeader(JsonObject keyValuePair); + void storeOcppMsgHeader(JsonObject &keyValuePair); bool isValid(); bool permissionLocalClientCanWrite() {return localClientCanWrite;} @@ -80,16 +80,9 @@ class Configuration : public AbstractConfiguration { size_t getValueJsonCapacity(); public: Configuration(); - Configuration(JsonObject storedKeyValuePair); + Configuration(JsonObject &storedKeyValuePair); const T &operator=(const T & newVal); - operator T() { //TODO move to Configuration.cpp - if (!initializedValue) { - Serial.print(F("[Configuration] Tried to access value without preceeding initialization: ")); - printKey(); - Serial.println(); - } - return value; - } + operator T(); bool isValid(); std::shared_ptr toJsonStorageEntry(); @@ -101,13 +94,13 @@ class Configuration : public AbstractConfiguration { template <> class Configuration : public AbstractConfiguration { private: - char *value = NULL; - char *valueReadOnlyCopy = NULL; + char *value = nullptr; + char *valueReadOnlyCopy = nullptr; size_t value_size = 0; size_t getValueJsonCapacity(); public: Configuration(); - Configuration(JsonObject storedKeyValuePair); + Configuration(JsonObject &storedKeyValuePair); ~Configuration(); bool setValue(const char *newVal, size_t buffsize); const char *operator=(const char *newVal); diff --git a/src/ArduinoOcpp/Core/OcppConnection.cpp b/src/ArduinoOcpp/Core/OcppConnection.cpp index 5a6faa18..d7906d65 100644 --- a/src/ArduinoOcpp/Core/OcppConnection.cpp +++ b/src/ArduinoOcpp/Core/OcppConnection.cpp @@ -3,7 +3,8 @@ // MIT License #include - +#include +#include #include #include @@ -15,15 +16,15 @@ size_t removePayload(const char *src, size_t src_size, char *dst, size_t dst_siz using namespace ArduinoOcpp; -OcppConnection::OcppConnection(OcppSocket *ocppSock) : ocppSock(ocppSock) { +OcppConnection::OcppConnection(OcppSocket& ocppSock, std::shared_ptr baseModel) : baseModel{baseModel} { ReceiveTXTcallback callback = [this] (const char *payload, size_t length) { return this->processOcppSocketInputTXT(payload, length); }; - ocppSock->setReceiveTXTcallback(callback); + ocppSock.setReceiveTXTcallback(callback); } -void OcppConnection::loop() { +void OcppConnection::loop(OcppSocket& ocppSock) { /** * Work through the initiatedOcppOperations queue. Start with the first element by calling req() on it. If @@ -34,7 +35,7 @@ void OcppConnection::loop() { auto operation = initiatedOcppOperations.begin(); while (operation != initiatedOcppOperations.end()){ - boolean timeout = (*operation)->sendReq(*ocppSock); //The only reason to dequeue elements here is when a timeout occurs. Normally + boolean timeout = (*operation)->sendReq(ocppSock); //The only reason to dequeue elements here is when a timeout occurs. Normally if (timeout){ //the Conf msg processing routine dequeues finished elements operation = initiatedOcppOperations.erase(operation); } else { @@ -75,7 +76,7 @@ void OcppConnection::loop() { operation = receivedOcppOperations.begin(); while (operation != receivedOcppOperations.end()){ - boolean success = (*operation)->sendConf(*ocppSock); + boolean success = (*operation)->sendConf(ocppSock); if (success){ operation = receivedOcppOperations.erase(operation); } else { @@ -202,12 +203,12 @@ void OcppConnection::handleConfMessage(JsonDocument& json) { } void OcppConnection::handleReqMessage(JsonDocument& json) { - auto req = makeFromJson(json); - if (req == nullptr) { + auto op = makeFromJson(json); + if (op == nullptr) { Serial.print(F("[OcppEngine] Couldn't make OppOperation from Request. Ignore request.\n")); return; } - handleReqMessage(json, std::move(req)); + handleReqMessage(json, std::move(op)); } void OcppConnection::handleReqMessage(JsonDocument& json, std::unique_ptr op) { @@ -215,6 +216,7 @@ void OcppConnection::handleReqMessage(JsonDocument& json, std::unique_ptrsetOcppModel(baseModel); op->receiveReq(json); //"fire" the operation receivedOcppOperations.push_back(std::move(op)); //enqueue so loop() plans conf sending } diff --git a/src/ArduinoOcpp/Core/OcppConnection.h b/src/ArduinoOcpp/Core/OcppConnection.h index aac367ef..b3427613 100644 --- a/src/ArduinoOcpp/Core/OcppConnection.h +++ b/src/ArduinoOcpp/Core/OcppConnection.h @@ -9,14 +9,16 @@ #include #include -#include -#include - namespace ArduinoOcpp { +class OcppModel; +class OcppSocket; +class OcppOperation; + class OcppConnection { private: - OcppSocket *ocppSock; + std::shared_ptr baseModel; + std::deque> initiatedOcppOperations; std::deque> receivedOcppOperations; @@ -25,9 +27,9 @@ class OcppConnection { void handleReqMessage(JsonDocument& json, std::unique_ptr op); void handleErrMessage(JsonDocument& json); public: - OcppConnection(OcppSocket *ocppSock); + OcppConnection(OcppSocket& oSock, std::shared_ptr baseModel); - void loop(); + void loop(OcppSocket& oSock); void initiateOcppOperation(std::unique_ptr o); diff --git a/src/ArduinoOcpp/Core/OcppEngine.cpp b/src/ArduinoOcpp/Core/OcppEngine.cpp index 9a170c72..5f082ce7 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.cpp +++ b/src/ArduinoOcpp/Core/OcppEngine.cpp @@ -3,127 +3,40 @@ // MIT License #include -#include - +#include #include -#include - -#include +#include #include -namespace ArduinoOcpp { -namespace OcppEngine { - -SmartChargingService *ocppEngine_smartChargingService{nullptr}; -ChargePointStatusService *ocppEngine_chargePointStatusService{nullptr}; -MeteringService *ocppEngine_meteringService{nullptr}; -FirmwareService *ocppEngine_firmwareService{nullptr}; -DiagnosticsService *ocppEngine_diagnosticsService{nullptr}; -OcppTime *ocppEngine_ocppTime{nullptr}; - -OcppSocket *ocppSock; -OcppConnection *mConnection; - -} //end namespace OcppEngine - -using namespace ArduinoOcpp::OcppEngine; - -void ocppEngine_initialize(OcppSocket *ocppSocket){ - ocppSock = ocppSocket; - mConnection = new OcppConnection(ocppSock); -} - -void initiateOcppOperation(std::unique_ptr o) { - mConnection->initiateOcppOperation(std::move(o)); -} - -void ocppEngine_loop() { - ocppSock->loop(); - mConnection->loop(); -} - -void setSmartChargingService(SmartChargingService *scs) { - ocppEngine_smartChargingService = scs; -} - -SmartChargingService* getSmartChargingService(){ - if (ocppEngine_smartChargingService == nullptr) { - Serial.print(F("[OcppEngine] Error: in OcppEngine, there is no smartChargingService set, but it is accessed!\n")); - //no error catch - } - return ocppEngine_smartChargingService; -} - -void setChargePointStatusService(ChargePointStatusService *cpss){ - ocppEngine_chargePointStatusService = cpss; -} - -ChargePointStatusService *getChargePointStatusService(){ - if (ocppEngine_chargePointStatusService == nullptr) { - Serial.print(F("[OcppEngine] Error: in OcppEngine, there is no chargePointStatusService set, but it is accessed!\n")); - //no error catch - } - return ocppEngine_chargePointStatusService; -} - -ConnectorStatus *getConnectorStatus(int connectorId) { - if (getChargePointStatusService() == nullptr) return nullptr; +using namespace ArduinoOcpp; - ConnectorStatus *result = getChargePointStatusService()->getConnector(connectorId); - if (result == nullptr) { - Serial.print(F("[OcppEngine] Error in getConnectorStatus(): cannot fetch connector with given connectorId!\n")); - //no error catch - } - return result; -} +OcppEngine *ArduinoOcpp::defaultOcppEngine = nullptr; -void setMeteringSerivce(MeteringService *meteringService) { - ocppEngine_meteringService = meteringService; +OcppEngine::OcppEngine(OcppSocket& ocppSocket, const OcppClock& system_clock) + : oSock(ocppSocket), oModel{std::make_shared(system_clock)}, oConn{oSock, oModel} { + defaultOcppEngine = this; } -MeteringService* getMeteringService() { - if (ocppEngine_meteringService == nullptr) { - Serial.print(F("[OcppEngine] Error: in OcppEngine, there is no ocppEngine_meteringService set, but it is accessed!\n")); - //no error catch - } - return ocppEngine_meteringService; +OcppEngine::~OcppEngine() { + defaultOcppEngine = nullptr; } -void setFirmwareService(FirmwareService *fwService) { - ocppEngine_firmwareService = fwService; -} +void OcppEngine::loop() { + oSock.loop(); + oConn.loop(oSock); -FirmwareService *getFirmwareService() { - if (ocppEngine_firmwareService == nullptr) { - Serial.print(F("[OcppEngine] Error: in OcppEngine, there is no ocppEngine_firmwareService set, but it is accessed!\n")); - //no error catch - } - return ocppEngine_firmwareService; + if (runOcppTasks) + oModel->loop(); } -void setDiagnosticsService(DiagnosticsService *diagnosticsService) { - ocppEngine_diagnosticsService = diagnosticsService; -} - -DiagnosticsService *getDiagnosticsService() { - if (ocppEngine_diagnosticsService == nullptr) { - Serial.print(F("[OcppEngine] Error: in OcppEngine, there is no ocppEngine_diagnosticsService set, but it is accessed!\n")); - //no error catch - } - return ocppEngine_diagnosticsService; -} - -void ocppEngine_setOcppTime(OcppTime *ocppTime) { - ocppEngine_ocppTime = ocppTime; -} - -OcppTime *getOcppTime() { - if (ocppEngine_ocppTime == nullptr) { - Serial.print(F("[OcppEngine] Error: in OcppEngine, there is no ocppEngine_ocppTime set, but it is accessed!\n")); - //no error catch +void OcppEngine::initiateOperation(std::unique_ptr op) { + if (op) { + op->setOcppModel(oModel); + oConn.initiateOcppOperation(std::move(op)); } - return ocppEngine_ocppTime; } -} //end namespace ArduinoOcpp +OcppModel& OcppEngine::getOcppModel() { + return *oModel; +} diff --git a/src/ArduinoOcpp/Core/OcppEngine.h b/src/ArduinoOcpp/Core/OcppEngine.h index cb83d438..c2ff6db1 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.h +++ b/src/ArduinoOcpp/Core/OcppEngine.h @@ -5,50 +5,35 @@ #ifndef OCPPENGINE_H #define OCPPENGINE_H -#include - -#include -#include +#include #include -#include -#include -#include -#include -#include namespace ArduinoOcpp { -void ocppEngine_initialize(OcppSocket *ocppSocket); - -void initiateOcppOperation(std::unique_ptr o); - -void ocppEngine_loop(); - -void setSmartChargingService(SmartChargingService *scs); - -SmartChargingService* getSmartChargingService(); - -void setChargePointStatusService(ChargePointStatusService *cpss); - -ChargePointStatusService *getChargePointStatusService(); - -ConnectorStatus *getConnectorStatus(int connectorId); - -void setMeteringSerivce(MeteringService *meteringService); +class OcppSocket; +class OcppModel; -MeteringService* getMeteringService(); +class OcppEngine { +private: + OcppSocket& oSock; + std::shared_ptr oModel; + OcppConnection oConn; -void setFirmwareService(FirmwareService *firmwareService); + bool runOcppTasks = true; +public: + OcppEngine(OcppSocket& ocppSocket, const OcppClock& system_clock); + ~OcppEngine(); -FirmwareService *getFirmwareService(); + void loop(); -void setDiagnosticsService(DiagnosticsService *diagnosticsService); + void setRunOcppTasks(bool enable) {runOcppTasks = enable;} -DiagnosticsService *getDiagnosticsService(); + void initiateOperation(std::unique_ptr op); -void ocppEngine_setOcppTime(OcppTime *ocppTime); + OcppModel& getOcppModel(); +}; -OcppTime *getOcppTime(); +extern OcppEngine *defaultOcppEngine; } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Core/OcppError.h b/src/ArduinoOcpp/Core/OcppError.h index cc019b66..70e97274 100644 --- a/src/ArduinoOcpp/Core/OcppError.h +++ b/src/ArduinoOcpp/Core/OcppError.h @@ -7,7 +7,7 @@ #include -#include +#include namespace ArduinoOcpp { @@ -30,8 +30,8 @@ class OutOfMemory : public OcppMessage { const char *getErrorDescription() { return "Too little free memory on the controller. Operation denied"; } - DynamicJsonDocument *getErrorDetails() { - DynamicJsonDocument *errDoc = new DynamicJsonDocument(JSON_OBJECT_SIZE(2)); + std::unique_ptr getErrorDetails() { + auto errDoc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(2))); JsonObject err = errDoc->to(); err["free_heap"] = freeHeap; err["msg_length"] = msgLen; @@ -53,4 +53,4 @@ class WebSocketError : public OcppMessage { }; } //end namespace ArduinoOcpp -#endif \ No newline at end of file +#endif diff --git a/src/ArduinoOcpp/Core/OcppMessage.cpp b/src/ArduinoOcpp/Core/OcppMessage.cpp index 43f34c23..08cdf793 100644 --- a/src/ArduinoOcpp/Core/OcppMessage.cpp +++ b/src/ArduinoOcpp/Core/OcppMessage.cpp @@ -8,24 +8,29 @@ using ArduinoOcpp::OcppMessage; -OcppMessage::OcppMessage(){} +OcppMessage::OcppMessage() {} -OcppMessage::~OcppMessage(){} +OcppMessage::~OcppMessage() {} const char* OcppMessage::getOcppOperationType(){ Serial.print(F("[OcppMessage] Unsupported operation: getOcppOperationType() is not implemented!\n")); return "CustomOperation"; } +void OcppMessage::setOcppModel(std::shared_ptr ocppModel) { + if (!ocppModelInitialized) { //prevent the ocppModel from being overwritten + this->ocppModel = ocppModel; //can still be nullptr + ocppModelInitialized = true; + } +} + void OcppMessage::initiate() { //called after initiateOcppOperation(anyMsg) } -DynamicJsonDocument* OcppMessage::createReq() { +std::unique_ptr OcppMessage::createReq() { Serial.print(F("[OcppMessage] Unsupported operation: createReq() is not implemented!\n")); - DynamicJsonDocument* doc = new DynamicJsonDocument(0); - doc->to(); - return doc; + return nullptr; } void OcppMessage::processConf(JsonObject payload) { @@ -36,13 +41,13 @@ void OcppMessage::processReq(JsonObject payload) { Serial.print(F("[OcppMessage] Unsupported operation: processReq() is not implemented!\n")); } -DynamicJsonDocument* OcppMessage::createConf() { +std::unique_ptr OcppMessage::createConf() { Serial.print(F("[OcppMessage] Unsupported operation: createConf() is not implemented!\n")); - return NULL; + return nullptr; } -DynamicJsonDocument *ArduinoOcpp::createEmptyDocument() { - DynamicJsonDocument *emptyDoc = new DynamicJsonDocument(0); - emptyDoc->to(); - return emptyDoc; +std::unique_ptr ArduinoOcpp::createEmptyDocument() { + auto emptyDoc = std::unique_ptr(new DynamicJsonDocument(0)); + emptyDoc->to(); + return std::move(emptyDoc); } diff --git a/src/ArduinoOcpp/Core/OcppMessage.h b/src/ArduinoOcpp/Core/OcppMessage.h index 623fe1bf..0e0c2fb5 100644 --- a/src/ArduinoOcpp/Core/OcppMessage.h +++ b/src/ArduinoOcpp/Core/OcppMessage.h @@ -18,58 +18,64 @@ #ifndef OCPPMESSAGE_H #define OCPPMESSAGE_H - #include +#include +#include namespace ArduinoOcpp { -DynamicJsonDocument *createEmptyDocument(); +std::unique_ptr createEmptyDocument(); + +class OcppModel; class OcppMessage { private: - + bool ocppModelInitialized = false; +protected: + std::shared_ptr ocppModel; public: + OcppMessage(); + + virtual ~OcppMessage(); + + virtual const char* getOcppOperationType(); + + void setOcppModel(std::shared_ptr ocppModel); + + virtual void initiate(); + + /** + * Create the payload for the respective OCPP message + * + * For instance operation Authorize: creates Authorize.req(idTag) + * + * This function is usually called multiple times by the Arduino loop(). On first call, the request is initially sent. In the + * succeeding calls, the implementers decide to either recreate the request, or do nothing as the operation is still pending. + */ + virtual std::unique_ptr createReq(); + - OcppMessage(); - - virtual ~OcppMessage(); - - virtual const char* getOcppOperationType(); - - virtual void initiate(); - - /** - * Create the payload for the respective OCPP message - * - * For instance operation Authorize: creates Authorize.req(idTag) - * - * This function is usually called multiple times by the Arduino loop(). On first call, the request is initially sent. In the - * succeeding calls, the implementers decide to either recreate the request, or do nothing as the operation is still pending. - */ - virtual DynamicJsonDocument* createReq(); - - - virtual void processConf(JsonObject payload); - - /* - * returns if the operation must be aborted - */ - virtual bool processErr(const char *code, const char *description, JsonObject details) { return true;} - - /** - * Processes the request in the JSON document. - */ - virtual void processReq(JsonObject payload); - - /** - * After successfully processing a request sent by the communication counterpart, this function creates the payload for a confirmation - * message. - */ - virtual DynamicJsonDocument* createConf(); - - virtual const char *getErrorCode() {return NULL;} //NULL means no error - virtual const char *getErrorDescription() {return "";} - virtual DynamicJsonDocument *getErrorDetails() {return createEmptyDocument();} + virtual void processConf(JsonObject payload); + /* + * returns if the operation must be aborted + */ + virtual bool processErr(const char *code, const char *description, JsonObject details) { return true;} + + /** + * Processes the request in the JSON document. + */ + virtual void processReq(JsonObject payload); + + /** + * After successfully processing a request sent by the communication counterpart, this function creates the payload for a confirmation + * message. + */ + virtual std::unique_ptr createConf(); + + virtual const char *getErrorCode() {return nullptr;} //nullptr means no error + virtual const char *getErrorDescription() {return "";} + virtual std::unique_ptr getErrorDetails() {return createEmptyDocument();} + }; } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Core/OcppModel.cpp b/src/ArduinoOcpp/Core/OcppModel.cpp new file mode 100644 index 00000000..155a770d --- /dev/null +++ b/src/ArduinoOcpp/Core/OcppModel.cpp @@ -0,0 +1,101 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ArduinoOcpp; + +OcppModel::OcppModel(const OcppClock& system_clock) + : ocppTime{system_clock} { + +} + +OcppModel::~OcppModel() = default; + +void OcppModel::loop() { + if (chargePointStatusService) + chargePointStatusService->loop(); + + if (smartChargingService) + smartChargingService->loop(); + + if (heartbeatService) + heartbeatService->loop(); + + if (meteringService) + meteringService->loop(); + + if (diagnosticsService) + diagnosticsService->loop(); + + if (firmwareService) + firmwareService->loop(); +} + +void OcppModel::setSmartChargingService(std::unique_ptr scs) { + smartChargingService = std::move(scs); +} + +SmartChargingService* OcppModel::getSmartChargingService() const { + return smartChargingService.get(); +} + +void OcppModel::setChargePointStatusService(std::unique_ptr cpss){ + chargePointStatusService = std::move(cpss); +} + +ChargePointStatusService *OcppModel::getChargePointStatusService() const { + return chargePointStatusService.get(); +} + +ConnectorStatus *OcppModel::getConnectorStatus(int connectorId) const { + if (getChargePointStatusService() == nullptr) return nullptr; + + auto result = getChargePointStatusService()->getConnector(connectorId); + if (result == nullptr) { + if (DEBUG_OUT) Serial.println(F("[OcppModel] in getConnectorStatus(): cannot fetch connector with given connectorId. Return nullptr")); + //no error catch + } + return result; +} + +void OcppModel::setMeteringSerivce(std::unique_ptr ms) { + meteringService = std::move(ms); +} + +MeteringService* OcppModel::getMeteringService() const { + return meteringService.get(); +} + +void OcppModel::setFirmwareService(std::unique_ptr fws) { + firmwareService = std::move(fws); +} + +FirmwareService *OcppModel::getFirmwareService() const { + return firmwareService.get(); +} + +void OcppModel::setDiagnosticsService(std::unique_ptr ds) { + diagnosticsService = std::move(ds); +} + +DiagnosticsService *OcppModel::getDiagnosticsService() const { + return diagnosticsService.get(); +} + +void OcppModel::setHeartbeatService(std::unique_ptr hs) { + heartbeatService = std::move(hs); +} + +OcppTime& OcppModel::getOcppTime() { + return ocppTime; +} diff --git a/src/ArduinoOcpp/Core/OcppModel.h b/src/ArduinoOcpp/Core/OcppModel.h new file mode 100644 index 00000000..5a8c156c --- /dev/null +++ b/src/ArduinoOcpp/Core/OcppModel.h @@ -0,0 +1,63 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef OCPPMODEL_H +#define OCPPMODEL_H + +#include + +#include + +namespace ArduinoOcpp { + +class SmartChargingService; +class ChargePointStatusService; +class ConnectorStatus; +class MeteringService; +class FirmwareService; +class DiagnosticsService; +class HeartbeatService; + +class OcppModel { +private: + std::unique_ptr smartChargingService; + std::unique_ptr chargePointStatusService; + std::unique_ptr meteringService; + std::unique_ptr firmwareService; + std::unique_ptr diagnosticsService; + std::unique_ptr heartbeatService; + OcppTime ocppTime; + +public: + OcppModel(const OcppClock& system_clock); + OcppModel() = delete; + OcppModel(const OcppModel& rhs) = delete; + ~OcppModel(); + + void loop(); + + void setSmartChargingService(std::unique_ptr scs); + SmartChargingService* getSmartChargingService() const; + + void setChargePointStatusService(std::unique_ptr cpss); + ChargePointStatusService *getChargePointStatusService() const; + ConnectorStatus *getConnectorStatus(int connectorId) const; + + void setMeteringSerivce(std::unique_ptr meteringService); + MeteringService* getMeteringService() const; + + void setFirmwareService(std::unique_ptr firmwareService); + FirmwareService *getFirmwareService() const; + + void setDiagnosticsService(std::unique_ptr diagnosticsService); + DiagnosticsService *getDiagnosticsService() const; + + void setHeartbeatService(std::unique_ptr heartbeatService); + + OcppTime &getOcppTime(); +}; + +} //end namespace ArduinoOcpp + +#endif diff --git a/src/ArduinoOcpp/Core/OcppOperation.cpp b/src/ArduinoOcpp/Core/OcppOperation.cpp index 9b99532c..1e2d9b5a 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.cpp +++ b/src/ArduinoOcpp/Core/OcppOperation.cpp @@ -3,6 +3,9 @@ // MIT License #include +#include +#include +#include #include @@ -26,6 +29,18 @@ void OcppOperation::setOcppMessage(std::unique_ptr msg){ ocppMessage = std::move(msg); } +void OcppOperation::setOcppModel(std::shared_ptr oModel) { + if (!ocppMessage) { + Serial.print(F("[OcppOperation] Please ensure that setOcppModel() will be called after setOcppMessage(). Abort")); + return; + } + if (!oModel) { + Serial.print(F("[OcppOperation] in setOcppModel(): passed nullptr! Ignore\n")); + return; + } + ocppMessage->setOcppModel(oModel); +} + void OcppOperation::setTimeout(std::unique_ptr to){ if (!to){ Serial.print(F("[OcppOperation] in setTimeout(): passed nullptr! Ignore\n")); diff --git a/src/ArduinoOcpp/Core/OcppOperation.h b/src/ArduinoOcpp/Core/OcppOperation.h index 92814607..0eab3513 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.h +++ b/src/ArduinoOcpp/Core/OcppOperation.h @@ -9,25 +9,17 @@ #define MESSAGE_TYPE_CALLRESULT 3 #define MESSAGE_TYPE_CALLERROR 4 -#include +#include -#include - -#include -#include -#include +#include #include namespace ArduinoOcpp { -using OnReceiveConfListener = std::function; -using OnReceiveReqListener = std::function; -using OnSendConfListener = std::function; -//using OnTimeoutListener = std::function; //in OcppOperationTimeout. Workaround for circle include. Fix by extracting type definitions to new source file -using OnReceiveErrorListener = std::function; //will be called if OCPP communication partner returns error code -//using OnAbortListener = std::function; //will be called whenever the engine will stop trying to execute the operation normallythere is a timeout or error (onAbort = onTimeout || onReceiveError) - +class OcppMessage; +class OcppModel; +class OcppSocket; class OcppOperation { private: @@ -62,6 +54,8 @@ class OcppOperation { void setOcppMessage(std::unique_ptr msg); + void setOcppModel(std::shared_ptr oModel); + void setTimeout(std::unique_ptr timeout); Timeout *getTimeout(); diff --git a/src/ArduinoOcpp/Core/OcppOperationCallbacks.h b/src/ArduinoOcpp/Core/OcppOperationCallbacks.h new file mode 100644 index 00000000..6daeb3ea --- /dev/null +++ b/src/ArduinoOcpp/Core/OcppOperationCallbacks.h @@ -0,0 +1,23 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef OCPP_OPERATION_CALLBACKS +#define OCPP_OPERATION_CALLBACKS + +#include + +#include + +namespace ArduinoOcpp { + +using OnReceiveConfListener = std::function; +using OnReceiveReqListener = std::function; +using OnSendConfListener = std::function; +//using OnTimeoutListener = std::function; //in OcppOperationTimeout. Workaround for circle include. Fix by extracting type definitions to new source file +using OnReceiveErrorListener = std::function; //will be called if OCPP communication partner returns error code +//using OnAbortListener = std::function; //will be called whenever the engine will stop trying to execute the operation normallythere is a timeout or error (onAbort = onTimeout || onReceiveError) + + +} //end namespace ArduinoOcpp +#endif diff --git a/src/ArduinoOcpp/Core/OcppOperationTimeout.h b/src/ArduinoOcpp/Core/OcppOperationTimeout.h index f6281d84..4fe6cd05 100644 --- a/src/ArduinoOcpp/Core/OcppOperationTimeout.h +++ b/src/ArduinoOcpp/Core/OcppOperationTimeout.h @@ -10,8 +10,8 @@ namespace ArduinoOcpp { -typedef std::function OnTimeoutListener; -typedef std::function OnAbortListener; +using OnTimeoutListener = std::function; +using OnAbortListener = std::function; class Timeout { private: diff --git a/src/ArduinoOcpp/Core/OcppTime.cpp b/src/ArduinoOcpp/Core/OcppTime.cpp index f972d9b1..4859b3b8 100644 --- a/src/ArduinoOcpp/Core/OcppTime.cpp +++ b/src/ArduinoOcpp/Core/OcppTime.cpp @@ -7,6 +7,9 @@ namespace ArduinoOcpp { +const OcppTimestamp MIN_TIME = OcppTimestamp(2010, 0, 0, 0, 0, 0); +const OcppTimestamp MAX_TIME = OcppTimestamp(2037, 0, 0, 0, 0, 0); + namespace Clocks { ulong lastClockReading = 0; @@ -277,7 +280,7 @@ bool operator>=(const OcppTimestamp &lhs, const OcppTimestamp &rhs) { } -OcppTime::OcppTime(OcppClock system_clock) : system_clock(system_clock) { +OcppTime::OcppTime(const OcppClock& system_clock) : system_clock(system_clock) { } diff --git a/src/ArduinoOcpp/Core/OcppTime.h b/src/ArduinoOcpp/Core/OcppTime.h index 173fddf2..aef6202a 100644 --- a/src/ArduinoOcpp/Core/OcppTime.h +++ b/src/ArduinoOcpp/Core/OcppTime.h @@ -9,7 +9,6 @@ #include - namespace ArduinoOcpp { typedef int32_t otime_t; //requires 32bit signed integer or bigger @@ -82,6 +81,8 @@ class OcppTimestamp { friend bool operator>=(const OcppTimestamp &lhs, const OcppTimestamp &rhs); }; +extern const OcppTimestamp MIN_TIME; +extern const OcppTimestamp MAX_TIME; class OcppTime { private: @@ -97,7 +98,8 @@ class OcppTime { public: - OcppTime(OcppClock system_clock); + OcppTime(const OcppClock& system_clock); + //OcppTime(const OcppTime& ocppTime) = default; otime_t getOcppTimeScalar(); //returns current time of the OCPP server in non-UNIX but signed integer format. t2 - t1 is the time difference in seconds. const OcppTimestamp &getOcppTimestampNow(); diff --git a/src/ArduinoOcpp/MessagesV16/Authorize.cpp b/src/ArduinoOcpp/MessagesV16/Authorize.cpp index f05c6732..971adba8 100644 --- a/src/ArduinoOcpp/MessagesV16/Authorize.cpp +++ b/src/ArduinoOcpp/MessagesV16/Authorize.cpp @@ -3,8 +3,10 @@ // MIT License #include -#include -#include "Variants.h" +#include +#include + +#include using ArduinoOcpp::Ocpp16::Authorize; @@ -12,7 +14,7 @@ Authorize::Authorize() { this->idTag = String("A0-00-00-00"); //Use a default payload. In the typical use case of this library, you probably you don't even need Authorization at all } -Authorize::Authorize(String &idTag) { +Authorize::Authorize(const String &idTag) { this->idTag = String(idTag); } @@ -20,11 +22,11 @@ const char* Authorize::getOcppOperationType(){ return "Authorize"; } -DynamicJsonDocument* Authorize::createReq() { - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + (idTag.length() + 1)); - JsonObject payload = doc->to(); - payload["idTag"] = idTag; - return doc; +std::unique_ptr Authorize::createReq() { + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + (idTag.length() + 1))); + JsonObject payload = doc->to(); + payload["idTag"] = idTag; + return doc; } void Authorize::processConf(JsonObject payload){ @@ -33,9 +35,8 @@ void Authorize::processConf(JsonObject payload){ if (idTagInfo.equals("Accepted")) { if (DEBUG_OUT) Serial.print(F("[Authorize] Request has been accepted!\n")); - ChargePointStatusService *cpStatusService = getChargePointStatusService(); - if (cpStatusService != NULL){ - cpStatusService->authorize(idTag); + if (ocppModel && ocppModel->getChargePointStatusService()) { + ocppModel->getChargePointStatusService()->authorize(idTag); } } else { @@ -49,8 +50,8 @@ void Authorize::processReq(JsonObject payload){ */ } -DynamicJsonDocument* Authorize::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(2 * JSON_OBJECT_SIZE(1)); +std::unique_ptr Authorize::createConf(){ + auto doc = std::unique_ptr(new DynamicJsonDocument(2 * JSON_OBJECT_SIZE(1))); JsonObject payload = doc->to(); JsonObject idTagInfo = payload.createNestedObject("idTagInfo"); idTagInfo["status"] = "Accepted"; diff --git a/src/ArduinoOcpp/MessagesV16/Authorize.h b/src/ArduinoOcpp/MessagesV16/Authorize.h index bbd91af2..cdc14637 100644 --- a/src/ArduinoOcpp/MessagesV16/Authorize.h +++ b/src/ArduinoOcpp/MessagesV16/Authorize.h @@ -12,21 +12,21 @@ namespace Ocpp16 { class Authorize : public OcppMessage { private: - String idTag; + String idTag; public: - Authorize(); + Authorize(); - Authorize(String &idTag); + Authorize(const String &idTag); - const char* getOcppOperationType(); + const char* getOcppOperationType(); - DynamicJsonDocument* createReq(); + std::unique_ptr createReq(); - void processConf(JsonObject payload); + void processConf(JsonObject payload); - void processReq(JsonObject payload); + void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); }; diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp index bf0df08d..b475a479 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp @@ -5,11 +5,11 @@ #include #include -#include +#include +#include #include #include -#include using ArduinoOcpp::Ocpp16::BootNotification; @@ -40,7 +40,7 @@ BootNotification::BootNotification(DynamicJsonDocument *payload) { } BootNotification::~BootNotification() { - if (overridePayload != NULL) + if (overridePayload != nullptr) delete overridePayload; } @@ -48,18 +48,18 @@ const char* BootNotification::getOcppOperationType(){ return "BootNotification"; } -DynamicJsonDocument* BootNotification::createReq() { +std::unique_ptr BootNotification::createReq() { - if (overridePayload != NULL) { - DynamicJsonDocument *result = new DynamicJsonDocument(*overridePayload); + if (overridePayload != nullptr) { + auto result = std::unique_ptr(new DynamicJsonDocument(*overridePayload)); return result; } - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + chargePointModel.length() + 1 + chargePointVendor.length() + 1 + chargePointSerialNumber.length() + 1 - + firmwareVersion.length() + 1); + + firmwareVersion.length() + 1)); JsonObject payload = doc->to(); payload["chargePointModel"] = chargePointModel; payload["chargePointVendor"] = chargePointVendor; @@ -75,11 +75,10 @@ DynamicJsonDocument* BootNotification::createReq() { void BootNotification::processConf(JsonObject payload){ const char* currentTime = payload["currentTime"] | "Invalid"; if (strcmp(currentTime, "Invalid")) { - OcppTime *ocppTime = getOcppTime(); - if (ocppTime && ocppTime->setOcppTime(currentTime)) { - //success + if (ocppModel && ocppModel->getOcppTime().setOcppTime(currentTime)) { + //success } else { - Serial.print(F("[BootNotification] Error reading time string. Expect format like 2020-02-01T20:53:32.486Z\n")); + Serial.print(F("[BootNotification] Error reading time string. Expect format like 2020-02-01T20:53:32.486Z\n")); } } else { Serial.print(F("[BootNotification] Error reading time string. Missing attribute currentTime of type string\n")); @@ -100,8 +99,8 @@ void BootNotification::processConf(JsonObject payload){ if (!strcmp(status, "Accepted")) { if (DEBUG_OUT) Serial.print(F("[BootNotification] Request has been accepted!\n")); - if (getChargePointStatusService() != NULL) { - getChargePointStatusService()->boot(); + if (ocppModel && ocppModel->getChargePointStatusService() != nullptr) { + ocppModel->getChargePointStatusService()->boot(); } } else { Serial.print(F("[BootNotification] Request unsuccessful!\n")); @@ -114,16 +113,16 @@ void BootNotification::processReq(JsonObject payload){ */ } -DynamicJsonDocument* BootNotification::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(3) + (JSONDATE_LENGTH + 1)); +std::unique_ptr BootNotification::createConf(){ + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(3) + (JSONDATE_LENGTH + 1))); JsonObject payload = doc->to(); //safety mechanism; in some test setups the library has to answer BootNotifications without valid system time OcppTimestamp ocppTimeReference = OcppTimestamp(2019,10,0,11,59,55); OcppTimestamp ocppSelect = ocppTimeReference; - OcppTime *ocppTime = getOcppTime(); - if (ocppTime) { - OcppTimestamp ocppNow = ocppTime->getOcppTimestampNow(); + if (ocppModel) { + auto& ocppTime = ocppModel->getOcppTime(); + OcppTimestamp ocppNow = ocppTime.getOcppTimestampNow(); if (ocppNow > ocppTimeReference) { //time has already been set ocppSelect = ocppNow; diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.h b/src/ArduinoOcpp/MessagesV16/BootNotification.h index 185afb7b..ed2a12e6 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.h +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.h @@ -7,7 +7,6 @@ #include - namespace ArduinoOcpp { namespace Ocpp16 { @@ -34,13 +33,13 @@ class BootNotification : public OcppMessage { const char* getOcppOperationType(); - DynamicJsonDocument* createReq(); + std::unique_ptr createReq(); void processConf(JsonObject payload); void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp b/src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp index 54b6be68..27c9780e 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp +++ b/src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp @@ -3,7 +3,8 @@ // MIT License #include -#include +#include +#include #include @@ -19,10 +20,10 @@ const char* ChangeAvailability::getOcppOperationType(){ void ChangeAvailability::processReq(JsonObject payload) { int connectorId = payload["connectorId"] | -1; - ChargePointStatusService *cpStatus = getChargePointStatusService(); - if (!cpStatus) { + if (!ocppModel || !ocppModel->getChargePointStatusService()) { return; } + auto cpStatus = ocppModel->getChargePointStatusService(); if (connectorId < 0 || connectorId >= cpStatus->getNumConnectors()) { return; } @@ -55,8 +56,8 @@ void ChangeAvailability::processReq(JsonObject payload) { } } -DynamicJsonDocument* ChangeAvailability::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); +std::unique_ptr ChangeAvailability::createConf(){ + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); JsonObject payload = doc->to(); if (!accepted) { payload["status"] = "Rejected"; diff --git a/src/ArduinoOcpp/MessagesV16/ChangeAvailability.h b/src/ArduinoOcpp/MessagesV16/ChangeAvailability.h index 26b4ffa0..34f1c3e6 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeAvailability.h +++ b/src/ArduinoOcpp/MessagesV16/ChangeAvailability.h @@ -21,7 +21,7 @@ class ChangeAvailability : public OcppMessage { void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp index 9050cb9a..e0173db7 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp @@ -183,15 +183,15 @@ void ChangeConfiguration::processReq(JsonObject payload) { //success } -DynamicJsonDocument* ChangeConfiguration::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); - JsonObject payload = doc->to(); - if (err || readOnly) { - payload["status"] = "Rejected"; - } else if (rebootRequired) { - payload["status"] = "RebootRequired"; - } else { - payload["status"] = "Accepted"; - } - return doc; +std::unique_ptr ChangeConfiguration::createConf(){ + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); + JsonObject payload = doc->to(); + if (err || readOnly) { + payload["status"] = "Rejected"; + } else if (rebootRequired) { + payload["status"] = "RebootRequired"; + } else { + payload["status"] = "Accepted"; + } + return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h index 68c023e8..9997098e 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h +++ b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h @@ -24,7 +24,7 @@ class ChangeConfiguration : public OcppMessage { void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); }; diff --git a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp index e39a7173..35a894f7 100644 --- a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp +++ b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp @@ -3,8 +3,8 @@ // MIT License #include +#include #include -#include #include @@ -62,17 +62,16 @@ void ClearChargingProfile::processReq(JsonObject payload) { return true; }; - SmartChargingService *smartChargingService = getSmartChargingService(); - if (!smartChargingService) { + if (!ocppModel || !ocppModel->getSmartChargingService()) { Serial.println(F("[ClearChargingProfile] SmartChargingService not initialized! Ignore request")); return; } - matchingProfilesFound = smartChargingService->clearChargingProfile(filter); + matchingProfilesFound = ocppModel->getSmartChargingService()->clearChargingProfile(filter); } -DynamicJsonDocument* ClearChargingProfile::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); +std::unique_ptr ClearChargingProfile::createConf(){ + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); JsonObject payload = doc->to(); if (matchingProfilesFound) payload["status"] = "Accepted"; diff --git a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.h b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.h index 25c503f0..988db366 100644 --- a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.h +++ b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.h @@ -20,7 +20,7 @@ class ClearChargingProfile : public OcppMessage { void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp b/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp index d86aac25..654303d2 100644 --- a/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp +++ b/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp @@ -3,7 +3,6 @@ // MIT License #include -#include #include using ArduinoOcpp::Ocpp16::DataTransfer; @@ -16,12 +15,12 @@ const char* DataTransfer::getOcppOperationType(){ return "DataTransfer"; } -DynamicJsonDocument* DataTransfer::createReq() { - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(2) + (msg.length() + 1)); - JsonObject payload = doc->to(); - payload["vendorId"] = "CustomVendor"; - payload["data"] = msg; - return doc; +std::unique_ptr DataTransfer::createReq() { + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(2) + (msg.length() + 1))); + JsonObject payload = doc->to(); + payload["vendorId"] = "CustomVendor"; + payload["data"] = msg; + return doc; } void DataTransfer::processConf(JsonObject payload){ diff --git a/src/ArduinoOcpp/MessagesV16/DataTransfer.h b/src/ArduinoOcpp/MessagesV16/DataTransfer.h index ed3723a6..68aafe5c 100644 --- a/src/ArduinoOcpp/MessagesV16/DataTransfer.h +++ b/src/ArduinoOcpp/MessagesV16/DataTransfer.h @@ -14,14 +14,14 @@ class DataTransfer : public OcppMessage { private: String msg; public: - DataTransfer(String &msg); + DataTransfer(String &msg); - const char* getOcppOperationType(); + const char* getOcppOperationType(); - DynamicJsonDocument* createReq(); - - void processConf(JsonObject payload); + std::unique_ptr createReq(); + void processConf(JsonObject payload); + }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp index c68cf535..1f83d5a8 100644 --- a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp @@ -4,20 +4,22 @@ #include #include +#include #include using ArduinoOcpp::Ocpp16::DiagnosticsStatusNotification; DiagnosticsStatusNotification::DiagnosticsStatusNotification() { - DiagnosticsService *diagService = getDiagnosticsService(); - if (diagService) { - status = diagService->getDiagnosticsStatus(); + if (defaultOcppEngine && defaultOcppEngine->getOcppModel().getDiagnosticsService()) { + auto diagnosticsService = defaultOcppEngine->getOcppModel().getDiagnosticsService(); + status = diagnosticsService->getDiagnosticsStatus(); } else { status = DiagnosticsStatus::Idle; } } DiagnosticsStatusNotification::DiagnosticsStatusNotification(DiagnosticsStatus status) : status(status) { + } const char *DiagnosticsStatusNotification::cstrFromFwStatus(DiagnosticsStatus status) { @@ -35,14 +37,14 @@ const char *DiagnosticsStatusNotification::cstrFromFwStatus(DiagnosticsStatus st return "Uploading"; break; } - return NULL; //cannot be reached + return nullptr; //cannot be reached } -DynamicJsonDocument* DiagnosticsStatusNotification::createReq() { - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); - JsonObject payload = doc->to(); - payload["status"] = cstrFromFwStatus(status); - return doc; +std::unique_ptr DiagnosticsStatusNotification::createReq() { + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); + JsonObject payload = doc->to(); + payload["status"] = cstrFromFwStatus(status); + return doc; } void DiagnosticsStatusNotification::processConf(JsonObject payload){ diff --git a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h index c47519d5..356efff2 100644 --- a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h +++ b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h @@ -3,6 +3,7 @@ // MIT License #include +#include #ifndef DIAGNOSTICSSTATUSNOTIFICATION_H #define DIAGNOSTICSSTATUSNOTIFICATION_H @@ -10,27 +11,20 @@ namespace ArduinoOcpp { namespace Ocpp16 { -enum class DiagnosticsStatus { - Idle, - Uploaded, - UploadFailed, - Uploading -}; - class DiagnosticsStatusNotification : public OcppMessage { private: - DiagnosticsStatus status; - static const char *cstrFromFwStatus(DiagnosticsStatus status); + DiagnosticsStatus status; + static const char *cstrFromFwStatus(DiagnosticsStatus status); public: - DiagnosticsStatusNotification(); + DiagnosticsStatusNotification(); - DiagnosticsStatusNotification(DiagnosticsStatus status); + DiagnosticsStatusNotification(DiagnosticsStatus status); - const char* getOcppOperationType() {return "DiagnosticsStatusNotification"; } + const char* getOcppOperationType() {return "DiagnosticsStatusNotification"; } - DynamicJsonDocument* createReq(); + std::unique_ptr createReq(); - void processConf(JsonObject payload); + void processConf(JsonObject payload); }; diff --git a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp index 8920d8ce..9ace381f 100644 --- a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp @@ -4,20 +4,22 @@ #include #include +#include #include using ArduinoOcpp::Ocpp16::FirmwareStatusNotification; FirmwareStatusNotification::FirmwareStatusNotification() { - FirmwareService *fwService = getFirmwareService(); - if (fwService) { - status = fwService->getFirmwareStatus(); + if (defaultOcppEngine && defaultOcppEngine->getOcppModel().getFirmwareService()) { + auto firmwareService = defaultOcppEngine->getOcppModel().getFirmwareService(); + status = firmwareService->getFirmwareStatus(); } else { status = FirmwareStatus::Idle; } } -FirmwareStatusNotification::FirmwareStatusNotification(FirmwareStatus status) : status(status) { +FirmwareStatusNotification::FirmwareStatusNotification(FirmwareStatus status) : status{status} { + } const char *FirmwareStatusNotification::cstrFromFwStatus(FirmwareStatus status) { @@ -47,11 +49,11 @@ const char *FirmwareStatusNotification::cstrFromFwStatus(FirmwareStatus status) return NULL; //cannot be reached } -DynamicJsonDocument* FirmwareStatusNotification::createReq() { - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); - JsonObject payload = doc->to(); - payload["status"] = cstrFromFwStatus(status); - return doc; +std::unique_ptr FirmwareStatusNotification::createReq() { + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); + JsonObject payload = doc->to(); + payload["status"] = cstrFromFwStatus(status); + return doc; } void FirmwareStatusNotification::processConf(JsonObject payload){ diff --git a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h index 68101cdc..16f4add0 100644 --- a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h +++ b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h @@ -4,36 +4,28 @@ #include +#include + #ifndef FIRMWARESTATUSNOTIFICATION_H #define FIRMWARESTATUSNOTIFICATION_H namespace ArduinoOcpp { namespace Ocpp16 { -enum class FirmwareStatus { - Downloaded, - DownloadFailed, - Downloading, - Idle, - InstallationFailed, - Installing, - Installed -}; - class FirmwareStatusNotification : public OcppMessage { private: - FirmwareStatus status; - static const char *cstrFromFwStatus(FirmwareStatus status); + FirmwareStatus status; + static const char *cstrFromFwStatus(FirmwareStatus status); public: - FirmwareStatusNotification(); + FirmwareStatusNotification(); - FirmwareStatusNotification(FirmwareStatus status); + FirmwareStatusNotification(FirmwareStatus status); - const char* getOcppOperationType() {return "FirmwareStatusNotification"; } + const char* getOcppOperationType() {return "FirmwareStatusNotification"; } - DynamicJsonDocument* createReq(); + std::unique_ptr createReq(); - void processConf(JsonObject payload); + void processConf(JsonObject payload); }; diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp index 3a7035c1..f6db00aa 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp @@ -14,7 +14,7 @@ GetConfiguration::GetConfiguration() { } GetConfiguration::~GetConfiguration() { - //keys.clear(); + } const char* GetConfiguration::getOcppOperationType(){ @@ -29,7 +29,7 @@ void GetConfiguration::processReq(JsonObject payload) { } } -DynamicJsonDocument* GetConfiguration::createConf(){ +std::unique_ptr GetConfiguration::createConf(){ std::shared_ptr>> configurationKeys; std::vector unknownKeys; @@ -66,7 +66,7 @@ DynamicJsonDocument* GetConfiguration::createConf(){ + JSON_ARRAY_SIZE(configurationKeysJson.size()) + JSON_ARRAY_SIZE(unknownKeys.size()); - DynamicJsonDocument* doc = new DynamicJsonDocument(capacity); + auto doc = std::unique_ptr(new DynamicJsonDocument(capacity)); JsonObject payload = doc->to(); diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.h b/src/ArduinoOcpp/MessagesV16/GetConfiguration.h index d1ea510c..509a4baf 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.h +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.h @@ -23,7 +23,7 @@ class GetConfiguration : public OcppMessage { void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); }; diff --git a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp index 4dab701c..74fe3ce1 100644 --- a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp @@ -3,7 +3,8 @@ // MIT License #include -#include +#include +#include using ArduinoOcpp::Ocpp16::GetDiagnostics; @@ -50,20 +51,18 @@ void GetDiagnostics::processReq(JsonObject payload) { } } -DynamicJsonDocument* GetDiagnostics::createConf(){ - DiagnosticsService *diagService = getDiagnosticsService(); - if (diagService != NULL) { - fileName = diagService->requestDiagnosticsUpload(location, retries, retryInterval, startTime, stopTime); +std::unique_ptr GetDiagnostics::createConf(){ + if (ocppModel && ocppModel->getDiagnosticsService()) { + fileName = ocppModel->getDiagnosticsService()->requestDiagnosticsUpload(location, retries, retryInterval, startTime, stopTime); } else { Serial.println(F("[GetDiagnostics] DiagnosticsService has not been initialized before! Please have a look at ArduinoOcpp.cpp for an example. Abort")); + return nullptr; } if (fileName.isEmpty()) { - DynamicJsonDocument* doc = new DynamicJsonDocument(0); - doc->to(); - return doc; + return createEmptyDocument(); } else { - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + fileName.length() + 1); + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + fileName.length() + 1)); JsonObject payload = doc->to(); payload["fileName"] = fileName; return doc; diff --git a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h index 06e50a9f..8c7cd03c 100644 --- a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h +++ b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h @@ -13,25 +13,25 @@ namespace Ocpp16 { class GetDiagnostics : public OcppMessage { private: - String location = String('\0'); - int retries = 1; - ulong retryInterval = 180; - OcppTimestamp startTime = OcppTimestamp(); - OcppTimestamp stopTime = OcppTimestamp(); - - String fileName = String('\0'); - - bool formatError = false; + String location = String('\0'); + int retries = 1; + ulong retryInterval = 180; + OcppTimestamp startTime = OcppTimestamp(); + OcppTimestamp stopTime = OcppTimestamp(); + + String fileName = String('\0'); + + bool formatError = false; public: - GetDiagnostics(); + GetDiagnostics(); - const char* getOcppOperationType() {return "GetDiagnostics";} + const char* getOcppOperationType() {return "GetDiagnostics";} - void processReq(JsonObject payload); + void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); - const char *getErrorCode() {if (formatError) return "FormationViolation"; else return NULL;} + const char *getErrorCode() {if (formatError) return "FormationViolation"; else return nullptr;} }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp b/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp index 836e2f8b..7b6abeb0 100644 --- a/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp +++ b/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp @@ -3,7 +3,7 @@ // MIT License #include -#include +#include #include #include @@ -17,30 +17,23 @@ const char* Heartbeat::getOcppOperationType(){ return "Heartbeat"; } -DynamicJsonDocument* Heartbeat::createReq() { - DynamicJsonDocument *doc = new DynamicJsonDocument(0); - doc->to(); - /* - * Empty payload - */ - return doc; +std::unique_ptr Heartbeat::createReq() { + return createEmptyDocument(); } void Heartbeat::processConf(JsonObject payload) { const char* currentTime = payload["currentTime"] | "Invalid"; if (strcmp(currentTime, "Invalid")) { - OcppTime *ocppTime = getOcppTime(); - if (ocppTime && ocppTime->setOcppTime(currentTime)) { - //success - if (DEBUG_OUT) Serial.print(F("[Heartbeat] Request has been accepted!\n")); + if (ocppModel && ocppModel->getOcppTime().setOcppTime(currentTime)) { + //success + if (DEBUG_OUT) Serial.print(F("[Heartbeat] Request has been accepted!\n")); } else { - Serial.print(F("[Heartbeat] Request accepted. But Error reading time string. Expect format like 2020-02-01T20:53:32.486Z\n")); + Serial.print(F("[Heartbeat] Request accepted. But Error reading time string. Expect format like 2020-02-01T20:53:32.486Z\n")); } } else { Serial.print(F("[Heartbeat] Request denied. Missing field currentTime. Expect format like 2020-02-01T20:53:32.486Z\n")); } - } void Heartbeat::processReq(JsonObject payload) { @@ -51,16 +44,15 @@ void Heartbeat::processReq(JsonObject payload) { } -DynamicJsonDocument* Heartbeat::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + (JSONDATE_LENGTH + 1)); +std::unique_ptr Heartbeat::createConf(){ + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + (JSONDATE_LENGTH + 1))); JsonObject payload = doc->to(); //safety mechanism; in some test setups the library could have to answer Heartbeats without valid system time OcppTimestamp ocppTimeReference = OcppTimestamp(2019,10,0,11,59,55); OcppTimestamp ocppSelect = ocppTimeReference; - OcppTime *ocppTime = getOcppTime(); - if (ocppTime) { - OcppTimestamp ocppNow = ocppTime->getOcppTimestampNow(); + if (ocppModel) { + auto& ocppNow = ocppModel->getOcppTime().getOcppTimestampNow(); if (ocppNow > ocppTimeReference) { //time has already been set ocppSelect = ocppNow; diff --git a/src/ArduinoOcpp/MessagesV16/Heartbeat.h b/src/ArduinoOcpp/MessagesV16/Heartbeat.h index 6a390a34..77a8cba1 100644 --- a/src/ArduinoOcpp/MessagesV16/Heartbeat.h +++ b/src/ArduinoOcpp/MessagesV16/Heartbeat.h @@ -14,17 +14,17 @@ namespace Ocpp16 { class Heartbeat : public OcppMessage { public: - Heartbeat(); + Heartbeat(); - const char* getOcppOperationType(); + const char* getOcppOperationType(); - DynamicJsonDocument* createReq(); + std::unique_ptr createReq(); - void processConf(JsonObject payload); + void processConf(JsonObject payload); - void processReq(JsonObject payload); + void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp index fa09c03a..902d501c 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp @@ -3,8 +3,8 @@ // MIT License #include -#include -#include +#include +#include #include @@ -12,25 +12,17 @@ using ArduinoOcpp::Ocpp16::MeterValues; //can only be used for echo server debugging MeterValues::MeterValues() { - sampleTime = std::vector(); //not used in server mode but needed to keep the destructor simple - energy = std::vector(); - power = std::vector(); + } -MeterValues::MeterValues(std::vector *sampleTime, std::vector *energy, std::vector *power, int connectorId, int transactionId) - : connectorId(connectorId), transactionId(transactionId) { +MeterValues::MeterValues(const std::vector *sampleTime, const std::vector *energy, const std::vector *power, int connectorId, int transactionId) + : connectorId{connectorId}, transactionId{transactionId} { if (sampleTime) this->sampleTime = std::vector(*sampleTime); - else - this->sampleTime = std::vector(); if (energy) this->energy = std::vector(*energy); - else - this->energy = std::vector(); if (power) this->power = std::vector(*power); - else - this->power = std::vector(); } MeterValues::~MeterValues(){ @@ -41,11 +33,11 @@ const char* MeterValues::getOcppOperationType(){ return "MeterValues"; } -DynamicJsonDocument* MeterValues::createReq() { +std::unique_ptr MeterValues::createReq() { int numEntries = sampleTime.size(); - DynamicJsonDocument *doc = new DynamicJsonDocument( + auto doc = std::unique_ptr(new DynamicJsonDocument( JSON_OBJECT_SIZE(2) //connectorID, transactionId + JSON_ARRAY_SIZE(numEntries) //metervalue array + numEntries * JSON_OBJECT_SIZE(1) //sampledValue @@ -54,7 +46,7 @@ DynamicJsonDocument* MeterValues::createReq() { + 2 * numEntries * JSON_OBJECT_SIZE(1) //value // why are these taken by two? + 2 * numEntries * JSON_OBJECT_SIZE(1) //measurand // + 2 * numEntries * JSON_OBJECT_SIZE(1) //unit // - + 230); //"safety space" + + 230)); //"safety space" JsonObject payload = doc->to(); payload["connectorId"] = connectorId; @@ -80,8 +72,8 @@ DynamicJsonDocument* MeterValues::createReq() { } } - ConnectorStatus *connector = getConnectorStatus(connectorId); - if (connector) { + if (ocppModel && ocppModel->getConnectorStatus(connectorId)) { + auto connector = ocppModel->getConnectorStatus(connectorId); if (connector->getTransactionIdSync() >= 0) { payload["transactionId"] = connector->getTransactionIdSync(); } @@ -103,11 +95,6 @@ void MeterValues::processReq(JsonObject payload) { } -DynamicJsonDocument* MeterValues::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(0); - doc->to(); - /* - * empty payload - */ - return doc; +std::unique_ptr MeterValues::createConf(){ + return createEmptyDocument(); } diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.h b/src/ArduinoOcpp/MessagesV16/MeterValues.h index 9eb5b43f..ec0ddcfa 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.h +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.h @@ -5,7 +5,7 @@ #ifndef METERVALUES_H #define METERVALUES_H -#include +#include #include namespace ArduinoOcpp { @@ -14,31 +14,29 @@ namespace Ocpp16 { class MeterValues : public OcppMessage { private: - std::vector sampleTime; - std::vector power; - std::vector energy; + std::vector sampleTime; + std::vector power; + std::vector energy; - int connectorId = 0; - int transactionId = -1; + int connectorId = 0; + int transactionId = -1; public: - MeterValues(std::vector *sampleTime, std::vector *energy); + MeterValues(const std::vector *sampleTime, const std::vector *energy, const std::vector *power, int connectorId, int transactionId); - MeterValues(std::vector *sampleTime, std::vector *energy, std::vector *power, int connectorId, int transactionId); + MeterValues(); //for debugging only. Make this for the server pendant - MeterValues(); //for debugging only. Make this for the server pendant + ~MeterValues(); - ~MeterValues(); + const char* getOcppOperationType(); - const char* getOcppOperationType(); + std::unique_ptr createReq(); - DynamicJsonDocument* createReq(); + void processConf(JsonObject payload); - void processConf(JsonObject payload); + void processReq(JsonObject payload); - void processReq(JsonObject payload); - - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp index 9642e0fa..65d4b440 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp @@ -4,7 +4,7 @@ #include -#include +#include #include #include @@ -23,33 +23,33 @@ void RemoteStartTransaction::processReq(JsonObject payload) { if (payload.containsKey("idTag")) { String idTag = payload["idTag"] | String("Invalid"); - ChargePointStatusService *cpService = getChargePointStatusService(); - if (cpService != NULL) { - cpService->authorize(idTag); + if (ocppModel && ocppModel->getChargePointStatusService()) { + ocppModel->getChargePointStatusService()->authorize(idTag); } } } -DynamicJsonDocument* RemoteStartTransaction::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); +std::unique_ptr RemoteStartTransaction::createConf(){ + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); JsonObject payload = doc->to(); bool canStartTransaction = false; if (connectorId >= 1) { //connectorId specified for given connector, try to start Transaction here - ConnectorStatus *connector = getConnectorStatus(connectorId); - if (connector != NULL){ + if (ocppModel && ocppModel->getConnectorStatus(connectorId)){ + auto connector = ocppModel->getConnectorStatus(connectorId); if (connector->getTransactionId() < 0) { canStartTransaction = true; } } } else { //connectorId not specified. Find free connector - if (getChargePointStatusService() != NULL) { - for (int i = 1; i < getChargePointStatusService()->getNumConnectors(); i++) { - ConnectorStatus *connIter = getConnectorStatus(i); + if (ocppModel && ocppModel->getChargePointStatusService()) { + auto cpStatusService = ocppModel->getChargePointStatusService(); + for (int i = 1; i < cpStatusService->getNumConnectors(); i++) { + auto connIter = cpStatusService->getConnector(i); if (connIter->getTransactionId() < 0) { - canStartTransaction = true; + canStartTransaction = true; } } } @@ -64,8 +64,8 @@ DynamicJsonDocument* RemoteStartTransaction::createConf(){ return doc; } -DynamicJsonDocument* RemoteStartTransaction::createReq() { - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); +std::unique_ptr RemoteStartTransaction::createReq() { + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); JsonObject payload = doc->to(); payload["idTag"] = "A0-00-00-00"; diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h index d2e21ff7..6e59a047 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h @@ -14,19 +14,19 @@ namespace Ocpp16 { class RemoteStartTransaction : public OcppMessage { private: - int connectorId; + int connectorId; public: - RemoteStartTransaction(); + RemoteStartTransaction(); - const char* getOcppOperationType(); + const char* getOcppOperationType(); - DynamicJsonDocument* createReq(); + std::unique_ptr createReq(); - void processConf(JsonObject payload); + void processConf(JsonObject payload); - void processReq(JsonObject payload); + void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp index fa626524..53befcf1 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp @@ -2,10 +2,10 @@ // Copyright Matthias Akstaller 2019 - 2021 // MIT License -#include "Variants.h" +#include #include -#include +#include #include using ArduinoOcpp::Ocpp16::RemoteStopTransaction; @@ -19,29 +19,30 @@ const char* RemoteStopTransaction::getOcppOperationType(){ } void RemoteStopTransaction::processReq(JsonObject payload) { - transactionId = payload["transactionId"] | -1; + transactionId = payload["transactionId"] | -1; } -DynamicJsonDocument* RemoteStopTransaction::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); - JsonObject payload = doc->to(); - - bool canStopTransaction = false; - - if (getChargePointStatusService() != NULL) { - for (int i = 0; i < getChargePointStatusService()->getNumConnectors(); i++) { - ConnectorStatus *connIter = getConnectorStatus(i); - if (connIter->getTransactionId() == transactionId) { - canStopTransaction = true; - } +std::unique_ptr RemoteStopTransaction::createConf(){ + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); + JsonObject payload = doc->to(); + + bool canStopTransaction = false; + + if (ocppModel && ocppModel->getChargePointStatusService()) { + auto cpStatusService = ocppModel->getChargePointStatusService(); + for (int i = 0; i < cpStatusService->getNumConnectors(); i++) { + auto connIter = cpStatusService->getConnector(i); + if (connIter->getTransactionId() == transactionId) { + canStopTransaction = true; + } + } } - } - if (canStopTransaction){ - payload["status"] = "Accepted"; - } else { - payload["status"] = "Rejected"; - } - - return doc; + if (canStopTransaction){ + payload["status"] = "Accepted"; + } else { + payload["status"] = "Rejected"; + } + + return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h index d2baed91..eff1469c 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h @@ -14,15 +14,15 @@ namespace Ocpp16 { class RemoteStopTransaction : public OcppMessage { private: - int transactionId; + int transactionId; public: - RemoteStopTransaction(); + RemoteStopTransaction(); - const char* getOcppOperationType(); + const char* getOcppOperationType(); - void processReq(JsonObject payload); + void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/Reset.cpp b/src/ArduinoOcpp/MessagesV16/Reset.cpp index 53875d5a..415fdea9 100644 --- a/src/ArduinoOcpp/MessagesV16/Reset.cpp +++ b/src/ArduinoOcpp/MessagesV16/Reset.cpp @@ -3,7 +3,6 @@ // MIT License #include -#include using ArduinoOcpp::Ocpp16::Reset; @@ -16,19 +15,19 @@ const char* Reset::getOcppOperationType(){ } void Reset::processReq(JsonObject payload) { - /* - * Process the application data here. Note: you have to implement the device reset procedure in your client code. You have to set - * a onSendConfListener in which you initiate a reset (e.g. calling ESP.reset() ) - */ - const char *type = payload["type"] | "Invalid"; - if (!strcmp(type, "Hard")){ - Serial.print(F("[Reset] Warning: received request to perform hard reset, but this implementation is only capable of soft reset!\n")); - } + /* + * Process the application data here. Note: you have to implement the device reset procedure in your client code. You have to set + * a onSendConfListener in which you initiate a reset (e.g. calling ESP.reset() ) + */ + const char *type = payload["type"] | "Invalid"; + if (!strcmp(type, "Hard")){ + Serial.print(F("[Reset] Warning: received request to perform hard reset, but this implementation is only capable of soft reset!\n")); + } } -DynamicJsonDocument* Reset::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); - JsonObject payload = doc->to(); - payload["status"] = "Accepted"; - return doc; +std::unique_ptr Reset::createConf(){ + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); + JsonObject payload = doc->to(); + payload["status"] = "Accepted"; + return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/Reset.h b/src/ArduinoOcpp/MessagesV16/Reset.h index eff3a252..32addfa7 100644 --- a/src/ArduinoOcpp/MessagesV16/Reset.h +++ b/src/ArduinoOcpp/MessagesV16/Reset.h @@ -14,13 +14,13 @@ namespace Ocpp16 { class Reset : public OcppMessage { public: - Reset(); + Reset(); - const char* getOcppOperationType(); + const char* getOcppOperationType(); - void processReq(JsonObject payload); + void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp index 22229979..b5c5e54f 100644 --- a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp +++ b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp @@ -2,25 +2,25 @@ // Copyright Matthias Akstaller 2019 - 2021 // MIT License -#include "Variants.h" - #include +#include +#include + +#include using ArduinoOcpp::Ocpp16::SetChargingProfile; -SetChargingProfile::SetChargingProfile(SmartChargingService *smartChargingService) - : smartChargingService(smartChargingService) { +SetChargingProfile::SetChargingProfile() { } -SetChargingProfile::SetChargingProfile(DynamicJsonDocument *payloadToClient) - : payloadToClient(payloadToClient) { +SetChargingProfile::SetChargingProfile(std::unique_ptr payloadToClient) + : payloadToClient{std::move(payloadToClient)} { } SetChargingProfile::~SetChargingProfile() { - if (payloadToClient != NULL) - delete payloadToClient; + } const char* SetChargingProfile::getOcppOperationType(){ @@ -33,22 +33,25 @@ void SetChargingProfile::processReq(JsonObject payload) { JsonObject csChargingProfiles = payload["csChargingProfiles"]; - smartChargingService->updateChargingProfile(&csChargingProfiles); + if (ocppModel && ocppModel->getSmartChargingService()) { + auto smartChargingService = ocppModel->getSmartChargingService(); + smartChargingService->updateChargingProfile(&csChargingProfiles); + } } -DynamicJsonDocument* SetChargingProfile::createConf(){ //TODO review - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); +std::unique_ptr SetChargingProfile::createConf(){ //TODO review + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); JsonObject payload = doc->to(); payload["status"] = "Accepted"; return doc; } -DynamicJsonDocument* SetChargingProfile::createReq() { - if (payloadToClient != NULL) { - DynamicJsonDocument *result = new DynamicJsonDocument(*payloadToClient); +std::unique_ptr SetChargingProfile::createReq() { + if (payloadToClient != nullptr) { + auto result = std::unique_ptr(new DynamicJsonDocument(*payloadToClient)); return result; } - return NULL; + return nullptr; } void SetChargingProfile::processConf(JsonObject payload) { diff --git a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h index 3599b61c..6e99e69a 100644 --- a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h +++ b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h @@ -6,20 +6,17 @@ #define SETCHARGINGPROFILE_H #include -#include namespace ArduinoOcpp { namespace Ocpp16 { class SetChargingProfile : public OcppMessage { private: - SmartChargingService *smartChargingService = NULL; - - DynamicJsonDocument *payloadToClient = NULL; + std::unique_ptr payloadToClient; public: - SetChargingProfile(SmartChargingService *smartChargingService); + SetChargingProfile(); - SetChargingProfile(DynamicJsonDocument *payloadToClient); + SetChargingProfile(std::unique_ptr payloadToClient); ~SetChargingProfile(); @@ -27,9 +24,9 @@ class SetChargingProfile : public OcppMessage { void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); - DynamicJsonDocument* createReq(); + std::unique_ptr createReq(); void processConf(JsonObject payload); }; diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp index 06df06db..641efaf5 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp @@ -3,7 +3,8 @@ // MIT License #include -#include +#include +#include #include #include @@ -23,35 +24,33 @@ const char* StartTransaction::getOcppOperationType(){ } void StartTransaction::initiate() { - MeteringService* meteringService = getMeteringService(); - if (meteringService != NULL) { + if (ocppModel && ocppModel->getMeteringService()) { + auto meteringService = ocppModel->getMeteringService(); meterStart = meteringService->readEnergyActiveImportRegister(connectorId); } - OcppTime *ocppTime = getOcppTime(); - if (ocppTime) { - otimestamp = ocppTime->getOcppTimestampNow(); + if (ocppModel) { + otimestamp = ocppModel->getOcppTime().getOcppTimestampNow(); } else { otimestamp = MIN_TIME; } if (idTag.isEmpty()) { - ChargePointStatusService *cpss = getChargePointStatusService(); - if (cpss != NULL && cpss->existsUnboundAuthorization()) { - this->idTag = String(cpss->getUnboundIdTag()); + if (ocppModel && ocppModel->getChargePointStatusService() + && ocppModel->getChargePointStatusService()->existsUnboundAuthorization()) { + this->idTag = String(ocppModel->getChargePointStatusService()->getUnboundIdTag()); } else { //The CP is not authorized. Try anyway, let the CS decide what to do ... this->idTag = String("A0-00-00-00"); //Use a default payload. In the typical use case of this library, you probably you don't even need Authorization at all } } - ChargePointStatusService *cpStatusService = getChargePointStatusService(); - if (cpStatusService) { - cpStatusService->bindAuthorization(connectorId); + if (ocppModel && ocppModel->getChargePointStatusService()) { + ocppModel->getChargePointStatusService()->bindAuthorization(connectorId); } - ConnectorStatus *connector = getConnectorStatus(connectorId); - if (connector != NULL){ + if (ocppModel && ocppModel->getConnectorStatus(connectorId)){ + auto connector = ocppModel->getConnectorStatus(connectorId); if (connector->getTransactionId() >= 0) { Serial.print(F("[StartTransaction] Warning: started transaction while OCPP already presumes a running transaction\n")); } @@ -62,8 +61,8 @@ void StartTransaction::initiate() { if (DEBUG_OUT) Serial.println(F("[StartTransaction] StartTransaction initiated!")); } -DynamicJsonDocument* StartTransaction::createReq() { - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(5) + (JSONDATE_LENGTH + 1) + (idTag.length() + 1)); +std::unique_ptr StartTransaction::createReq() { + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(5) + (JSONDATE_LENGTH + 1) + (idTag.length() + 1))); JsonObject payload = doc->to(); payload["connectorId"] = connectorId; @@ -87,10 +86,13 @@ void StartTransaction::processConf(JsonObject payload) { const char* idTagInfoStatus = payload["idTagInfo"]["status"] | "Invalid"; int transactionId = payload["transactionId"] | -1; + ConnectorStatus *connector = nullptr; + if (ocppModel) + connector = ocppModel->getConnectorStatus(connectorId); + if (!strcmp(idTagInfoStatus, "Accepted")) { if (DEBUG_OUT) Serial.print(F("[StartTransaction] Request has been accepted!\n")); - ConnectorStatus *connector = getConnectorStatus(connectorId); if (connector){ if (transactionRev == connector->getTransactionWriteCount()) { connector->setTransactionId(transactionId); @@ -99,7 +101,6 @@ void StartTransaction::processConf(JsonObject payload) { } } else { Serial.print(F("[StartTransaction] Request has been denied!\n")); - ConnectorStatus *connector = getConnectorStatus(connectorId); if (connector){ if (transactionRev == connector->getTransactionWriteCount()) { connector->setTransactionId(-1); @@ -119,13 +120,13 @@ void StartTransaction::processReq(JsonObject payload) { } -DynamicJsonDocument* StartTransaction::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(2)); - JsonObject payload = doc->to(); +std::unique_ptr StartTransaction::createConf(){ + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(2))); + JsonObject payload = doc->to(); - JsonObject idTagInfo = payload.createNestedObject("idTagInfo"); - idTagInfo["status"] = "Accepted"; - payload["transactionId"] = 123456; //sample data for debug purpose + JsonObject idTagInfo = payload.createNestedObject("idTagInfo"); + idTagInfo["status"] = "Accepted"; + payload["transactionId"] = 123456; //sample data for debug purpose - return doc; + return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.h b/src/ArduinoOcpp/MessagesV16/StartTransaction.h index a08c787b..e29488a2 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.h @@ -15,27 +15,27 @@ namespace Ocpp16 { class StartTransaction : public OcppMessage { private: - int connectorId = 1; - float meterStart = -1.0f; - OcppTimestamp otimestamp; - String idTag = String('\0'); - uint16_t transactionRev = 0; + int connectorId = 1; + float meterStart = -1.0f; + OcppTimestamp otimestamp; + String idTag = String('\0'); + uint16_t transactionRev = 0; public: - StartTransaction(int connectorId); + StartTransaction(int connectorId); - StartTransaction(int connectorId, String &idTag); + StartTransaction(int connectorId, String &idTag); - const char* getOcppOperationType(); + const char* getOcppOperationType(); - void initiate(); + void initiate(); - DynamicJsonDocument* createReq(); + std::unique_ptr createReq(); - void processConf(JsonObject payload); + void processConf(JsonObject payload); - void processReq(JsonObject payload); + void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp index 00de7d07..1f409f8e 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp @@ -3,7 +3,6 @@ // MIT License #include -#include #include #include @@ -59,12 +58,12 @@ const char* StatusNotification::getOcppOperationType(){ } //TODO if the status has changed again when sendReq() is called, abort the operation completely (note: if req is already sent, stick with listening to conf). The OcppEvseStateService will enqueue a new operation itself -DynamicJsonDocument* StatusNotification::createReq() { - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + (JSONDATE_LENGTH + 1)); +std::unique_ptr StatusNotification::createReq() { + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + (JSONDATE_LENGTH + 1))); JsonObject payload = doc->to(); payload["connectorId"] = connectorId; - if (errorCode != NULL) { + if (errorCode != nullptr) { payload["errorCode"] = errorCode; } else { payload["errorCode"] = "NoError"; @@ -123,7 +122,7 @@ void StatusNotification::processConf(JsonObject payload) { * For debugging only */ StatusNotification::StatusNotification() { - otimestamp = OcppTimestamp(); + } /* @@ -136,8 +135,6 @@ void StatusNotification::processReq(JsonObject payload) { /* * For debugging only */ -DynamicJsonDocument* StatusNotification::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(0); - doc->to(); - return doc; +std::unique_ptr StatusNotification::createConf(){ + return createEmptyDocument(); } diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.h b/src/ArduinoOcpp/MessagesV16/StatusNotification.h index cd832aff..bccd1458 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.h +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.h @@ -6,8 +6,8 @@ #define STATUSNOTIFICATION_H #include -#include #include +#include namespace ArduinoOcpp { namespace Ocpp16 { @@ -17,21 +17,21 @@ class StatusNotification : public OcppMessage { int connectorId = 1; OcppEvseState currentStatus = OcppEvseState::NOT_SET; OcppTimestamp otimestamp; - const char *errorCode = NULL; //NULL is equivalent to "NoError" + const char *errorCode = nullptr; //nullptr is equivalent to "NoError" public: - StatusNotification(int connectorId, OcppEvseState currentStatus, const OcppTimestamp &otimestamp, const char *errorCode = NULL); + StatusNotification(int connectorId, OcppEvseState currentStatus, const OcppTimestamp &otimestamp, const char *errorCode = nullptr); StatusNotification(); const char* getOcppOperationType(); - DynamicJsonDocument* createReq(); + std::unique_ptr createReq(); void processConf(JsonObject payload); void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp index b6482908..04c991a9 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp @@ -3,7 +3,8 @@ // MIT License #include -#include +#include +#include #include #include @@ -23,30 +24,28 @@ const char* StopTransaction::getOcppOperationType(){ void StopTransaction::initiate() { - if (getMeteringService() != NULL) { - meterStop = getMeteringService()->readEnergyActiveImportRegister(connectorId); + if (ocppModel && ocppModel->getMeteringService()) { + auto meteringService = ocppModel->getMeteringService(); + meterStop = meteringService->readEnergyActiveImportRegister(connectorId); } - OcppTime *ocppTime = getOcppTime(); - if (ocppTime) { - otimestamp = ocppTime->getOcppTimestampNow(); + if (ocppModel) { + otimestamp = ocppModel->getOcppTime().getOcppTimestampNow(); } else { otimestamp = MIN_TIME; } - ConnectorStatus *connector = getConnectorStatus(connectorId); - if (connector != NULL){ + if (ocppModel && ocppModel->getConnectorStatus(connectorId)){ + auto connector = ocppModel->getConnectorStatus(connectorId); connector->setTransactionId(-1); //immediate end of transaction connector->unauthorize(); } if (DEBUG_OUT) Serial.println(F("[StartTransaction] StopTransaction initiated!")); - } -DynamicJsonDocument* StopTransaction::createReq() { - - DynamicJsonDocument *doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + (JSONDATE_LENGTH + 1)); +std::unique_ptr StopTransaction::createReq() { + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + (JSONDATE_LENGTH + 1))); JsonObject payload = doc->to(); if (meterStop >= 0.f) @@ -57,8 +56,9 @@ DynamicJsonDocument* StopTransaction::createReq() { otimestamp.toJsonString(timestamp, JSONDATE_LENGTH + 1); payload["timestamp"] = timestamp; } - ConnectorStatus *connector = getConnectorStatus(connectorId); - if (connector != NULL){ + + if (ocppModel && ocppModel->getConnectorStatus(connectorId)){ + auto connector = ocppModel->getConnectorStatus(connectorId); payload["transactionId"] = connector->getTransactionIdSync(); connector->setTransactionIdSync(-1); } @@ -71,7 +71,6 @@ void StopTransaction::processConf(JsonObject payload) { //no need to process anything here if (DEBUG_OUT) Serial.print(F("[StopTransaction] Request has been accepted!\n")); - } @@ -81,8 +80,8 @@ void StopTransaction::processReq(JsonObject payload) { */ } -DynamicJsonDocument* StopTransaction::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(2 * JSON_OBJECT_SIZE(1)); +std::unique_ptr StopTransaction::createConf(){ + auto doc = std::unique_ptr(new DynamicJsonDocument(2 * JSON_OBJECT_SIZE(1))); JsonObject payload = doc->to(); JsonObject idTagInfo = payload.createNestedObject("idTagInfo"); diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.h b/src/ArduinoOcpp/MessagesV16/StopTransaction.h index 2dee80a2..2a9777e3 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.h @@ -25,13 +25,13 @@ class StopTransaction : public OcppMessage { void initiate(); - DynamicJsonDocument* createReq(); + std::unique_ptr createReq(); void processConf(JsonObject payload); void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp index 11d7ad44..245182b4 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp @@ -19,25 +19,25 @@ const char* TriggerMessage::getOcppOperationType(){ void TriggerMessage::processReq(JsonObject payload) { - Serial.print(F("[TriggerMessage] Warning: TriggerMessage is not tested!\n")); + Serial.println(F("[TriggerMessage] Warning: TriggerMessage is not tested!")); triggeredOperation = makeFromTriggerMessage(payload); - if (triggeredOperation != NULL) { + if (triggeredOperation != nullptr) { statusMessage = "Accepted"; } else { - Serial.print(F("[TriggerMessage] Couldn't make OppOperation from TriggerMessage. Ignore request.\n")); + Serial.println(F("[TriggerMessage] Couldn't make OppOperation from TriggerMessage. Ignore request.")); statusMessage = "NotImplemented"; } } -DynamicJsonDocument* TriggerMessage::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + strlen(statusMessage)); +std::unique_ptr TriggerMessage::createConf(){ + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + strlen(statusMessage))); JsonObject payload = doc->to(); payload["status"] = statusMessage; - if (triggeredOperation) //from the second createConf()-try on, do not initiate further OCPP ops - initiateOcppOperation(std::move(triggeredOperation)); + if (triggeredOperation && defaultOcppEngine) //from the second createConf()-try on, do not initiate further OCPP ops + defaultOcppEngine->initiateOperation(std::move(triggeredOperation)); return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h index c550313d..19e408db 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h @@ -6,10 +6,14 @@ #define TRIGGERMESSAGE_H #include -#include #include +#include + namespace ArduinoOcpp { + +class OcppOperation; + namespace Ocpp16 { class TriggerMessage : public OcppMessage { @@ -23,7 +27,7 @@ class TriggerMessage : public OcppMessage { void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp b/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp index b81f8104..51e29f6b 100644 --- a/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp +++ b/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp @@ -3,7 +3,7 @@ // MIT License #include -#include +#include #include #include @@ -22,17 +22,19 @@ void UnlockConnector::processReq(JsonObject payload) { int connectorId = payload["connectorId"] | -1; - ConnectorStatus *connector = getConnectorStatus(connectorId); - if (!connector) { + if (!ocppModel || !ocppModel->getConnectorStatus(connectorId)) { err = true; return; } + auto connector = ocppModel->getConnectorStatus(connectorId); + std::function unlockConnector = connector->getOnUnlockConnector(); - if (unlockConnector != NULL) { + if (unlockConnector != nullptr) { cbDefined = true; } else { cbDefined = false; + if (DEBUG_OUT) Serial.println(F("[UnlockConnector] Unlock CB undefined")); return; } @@ -41,15 +43,15 @@ void UnlockConnector::processReq(JsonObject payload) { //success } -DynamicJsonDocument* UnlockConnector::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(JSON_OBJECT_SIZE(1)); - JsonObject payload = doc->to(); - if (err || !cbDefined) { - payload["status"] = "NotSupported"; - } else if (cbUnlockSuccessful) { - payload["status"] = "Unlocked"; - } else { - payload["status"] = "UnlockFailed"; - } - return doc; +std::unique_ptr UnlockConnector::createConf(){ + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); + JsonObject payload = doc->to(); + if (err || !cbDefined) { + payload["status"] = "NotSupported"; + } else if (cbUnlockSuccessful) { + payload["status"] = "Unlocked"; + } else { + payload["status"] = "UnlockFailed"; + } + return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/UnlockConnector.h b/src/ArduinoOcpp/MessagesV16/UnlockConnector.h index b0c70daf..ca1ef349 100644 --- a/src/ArduinoOcpp/MessagesV16/UnlockConnector.h +++ b/src/ArduinoOcpp/MessagesV16/UnlockConnector.h @@ -24,8 +24,7 @@ class UnlockConnector : public OcppMessage { void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); - + std::unique_ptr createConf(); }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp index e7767802..1d962d94 100644 --- a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp +++ b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp @@ -3,7 +3,8 @@ // MIT License #include -#include +#include +#include using ArduinoOcpp::Ocpp16::UpdateFirmware; @@ -38,16 +39,13 @@ void UpdateFirmware::processReq(JsonObject payload) { retryInterval = payload["retryInterval"] | 180; } -DynamicJsonDocument* UpdateFirmware::createConf(){ - DynamicJsonDocument* doc = new DynamicJsonDocument(0); - doc->to(); - - FirmwareService *fwService = getFirmwareService(); - if (fwService != NULL) { +std::unique_ptr UpdateFirmware::createConf(){ + if (ocppModel && ocppModel->getFirmwareService()) { + auto fwService = ocppModel->getFirmwareService(); fwService->scheduleFirmwareUpdate(location, retreiveDate, retries, retryInterval); } else { Serial.println(F("[UpdateFirmware] FirmwareService has not been initialized before! Please have a look at ArduinoOcpp.cpp for an example. Abort")); } - return doc; -} \ No newline at end of file + return createEmptyDocument(); +} diff --git a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h index fcb86634..73c8c30e 100644 --- a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h +++ b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h @@ -25,7 +25,7 @@ class UpdateFirmware : public OcppMessage { void processReq(JsonObject payload); - DynamicJsonDocument* createConf(); + std::unique_ptr createConf(); const char *getErrorCode() {if (formatError) return "FormationViolation"; else return NULL;} }; diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 52a98efc..fd7d8b24 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -2,7 +2,7 @@ // Copyright Matthias Akstaller 2019 - 2021 // MIT License -#include "Variants.h" +#include #include @@ -29,100 +29,139 @@ #include #include -#include - #include +#include + namespace ArduinoOcpp { +class TBDeiniter { +public: + virtual ~TBDeiniter() = default; +}; + +std::vector> toBeDeinitialized; + +template +class TBDeiniterConcrete : public TBDeiniter { +private: + T& obj; +public: + TBDeiniterConcrete(T& obj) : obj(obj) { } + ~TBDeiniterConcrete() {obj = nullptr;} +}; + +template +void deinit_afterwards(T& obj) { + toBeDeinitialized.emplace_back(std::unique_ptr{new TBDeiniterConcrete{obj}}); +} + OnReceiveReqListener onAuthorizeRequest; void setOnAuthorizeRequestListener(OnReceiveReqListener listener){ onAuthorizeRequest = listener; + deinit_afterwards(onAuthorizeRequest); } OnReceiveReqListener onBootNotificationRequest; void setOnBootNotificationRequestListener(OnReceiveReqListener listener){ onBootNotificationRequest = listener; + deinit_afterwards(onBootNotificationRequest); } OnReceiveReqListener onTargetValuesRequest; void setOnTargetValuesRequestListener(OnReceiveReqListener listener) { onTargetValuesRequest = listener; + deinit_afterwards(onTargetValuesRequest); } OnReceiveReqListener onSetChargingProfileRequest; void setOnSetChargingProfileRequestListener(OnReceiveReqListener listener){ onSetChargingProfileRequest = listener; + deinit_afterwards(onSetChargingProfileRequest); } OnReceiveReqListener onStartTransactionRequest; void setOnStartTransactionRequestListener(OnReceiveReqListener listener){ onStartTransactionRequest = listener; + deinit_afterwards(onStartTransactionRequest); } OnReceiveReqListener onTriggerMessageRequest; void setOnTriggerMessageRequestListener(OnReceiveReqListener listener){ onTriggerMessageRequest = listener; + deinit_afterwards(onTriggerMessageRequest); } OnReceiveReqListener onRemoteStartTransactionReceiveRequest; void setOnRemoteStartTransactionReceiveRequestListener(OnReceiveReqListener listener) { onRemoteStartTransactionReceiveRequest = listener; + deinit_afterwards(onRemoteStartTransactionReceiveRequest); } OnSendConfListener onRemoteStartTransactionSendConf; void setOnRemoteStartTransactionSendConfListener(OnSendConfListener listener){ onRemoteStartTransactionSendConf = listener; + deinit_afterwards(onRemoteStartTransactionSendConf); } OnReceiveReqListener onRemoteStopTransactionReceiveRequest; void setOnRemoteStopTransactionReceiveRequestListener(OnReceiveReqListener listener){ onRemoteStopTransactionReceiveRequest = listener; + deinit_afterwards(onRemoteStopTransactionReceiveRequest); } OnSendConfListener onRemoteStopTransactionSendConf; void setOnRemoteStopTransactionSendConfListener(OnSendConfListener listener){ onRemoteStopTransactionSendConf = listener; + deinit_afterwards(onRemoteStopTransactionSendConf); } OnSendConfListener onChangeConfigurationReceiveReq; void setOnChangeConfigurationReceiveRequestListener(OnReceiveReqListener listener){ onChangeConfigurationReceiveReq = listener; + deinit_afterwards(onChangeConfigurationReceiveReq); } OnSendConfListener onChangeConfigurationSendConf; void setOnChangeConfigurationSendConfListener(OnSendConfListener listener){ onChangeConfigurationSendConf = listener; + deinit_afterwards(onChangeConfigurationSendConf); } OnSendConfListener onGetConfigurationReceiveReq; void setOnGetConfigurationReceiveReqListener(OnSendConfListener listener){ onGetConfigurationReceiveReq = listener; + deinit_afterwards(onGetConfigurationReceiveReq); } OnSendConfListener onGetConfigurationSendConf; void setOnGetConfigurationSendConfListener(OnSendConfListener listener){ onGetConfigurationSendConf = listener; + deinit_afterwards(onGetConfigurationSendConf); } OnSendConfListener onResetReceiveReq; void setOnResetReceiveRequestListener(OnReceiveReqListener listener) { onResetReceiveReq = listener; + deinit_afterwards(onResetReceiveReq); } OnSendConfListener onResetSendConf; void setOnResetSendConfListener(OnSendConfListener listener){ onResetSendConf = listener; + deinit_afterwards(onResetSendConf); } OnReceiveReqListener onUpdateFirmwareReceiveReq; void setOnUpdateFirmwareReceiveRequestListener(OnReceiveReqListener listener) { onUpdateFirmwareReceiveReq = listener; + deinit_afterwards(onUpdateFirmwareReceiveReq); } OnReceiveReqListener onMeterValuesReceiveReq; void setOnMeterValuesReceiveRequestListener(OnReceiveReqListener listener) { onMeterValuesReceiveReq = listener; + deinit_afterwards(onMeterValuesReceiveReq); } struct CustomOcppMessageCreatorEntry { @@ -148,6 +187,11 @@ void registerCustomOcppMessage(const char *messageType, OcppMessageCreator ocppM customMessagesRegistry.push_back(entry); } +void simpleOcppFactory_deinitialize() { + customMessagesRegistry.clear(); + toBeDeinitialized.clear(); +} + CustomOcppMessageCreatorEntry *makeCustomOcppMessage(const char *messageType) { for (auto it = customMessagesRegistry.begin(); it != customMessagesRegistry.end(); ++it) { if (!strcmp(it->messageType, messageType)) { @@ -195,7 +239,7 @@ std::unique_ptr makeOcppOperation(const char *messageType) { msg = std::unique_ptr(new Ocpp16::MeterValues()); operation->setOnReceiveReqListener(onMeterValuesReceiveReq); } else if (!strcmp(messageType, "SetChargingProfile")) { - msg = std::unique_ptr(new Ocpp16::SetChargingProfile(getSmartChargingService())); + msg = std::unique_ptr(new Ocpp16::SetChargingProfile()); operation->setOnReceiveReqListener(onSetChargingProfileRequest); } else if (!strcmp(messageType, "StatusNotification")) { msg = std::unique_ptr(new Ocpp16::StatusNotification()); diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.h b/src/ArduinoOcpp/SimpleOcppOperationFactory.h index e3266ec8..5ce5010d 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.h +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.h @@ -6,7 +6,6 @@ #define SIMPLEOCPPOPERATIONFACTORY_H #include - #include namespace ArduinoOcpp { @@ -44,5 +43,7 @@ void setOnResetSendConfListener(OnSendConfListener onSendConf); void setOnUpdateFirmwareReceiveRequestListener(OnReceiveReqListener onReceiveReq); void setOnMeterValuesReceiveRequestListener(OnReceiveReqListener onReceiveReq); +void simpleOcppFactory_deinitialize(); + } //end namespace ArduinoOcpp #endif diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index 434122e0..6a6bb332 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -2,27 +2,23 @@ // Copyright Matthias Akstaller 2019 - 2021 // MIT License -#include "Variants.h" +#include #include -#include #include #include #include using namespace ArduinoOcpp; -using namespace ArduinoOcpp::Ocpp16; -ChargePointStatusService::ChargePointStatusService(int numConn, OcppTime *ocppTime) - : numConnectors(numConn), ocppTime(ocppTime) { +ChargePointStatusService::ChargePointStatusService(OcppEngine& context, int numConn) + : context(context), numConnectors{numConn} { connectors = (ConnectorStatus**) malloc(numConn * sizeof(ConnectorStatus*)); for (int i = 0; i < numConn; i++) { - connectors[i] = new ConnectorStatus(i, ocppTime); + connectors[i] = new ConnectorStatus(context.getOcppModel(), i); } - - setChargePointStatusService(this); } ChargePointStatusService::~ChargePointStatusService() { @@ -35,10 +31,10 @@ ChargePointStatusService::~ChargePointStatusService() { void ChargePointStatusService::loop() { if (!booted) return; for (int i = 0; i < numConnectors; i++){ - StatusNotification *statusNotificationMsg = connectors[i]->loop(); + auto statusNotificationMsg = connectors[i]->loop(); if (statusNotificationMsg != nullptr) { auto statusNotification = makeOcppOperation(statusNotificationMsg); - initiateOcppOperation(std::move(statusNotification)); + context.initiateOperation(std::move(statusNotification)); } } } diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h index a34a56ec..bb7e31a1 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h @@ -9,10 +9,13 @@ namespace ArduinoOcpp { +class OcppEngine; + class ChargePointStatusService { private: + OcppEngine& context; + const int numConnectors; - OcppTime *ocppTime; ConnectorStatus **connectors; bool booted = false; @@ -21,7 +24,7 @@ class ChargePointStatusService { String idTag = String('\0'); public: - ChargePointStatusService(int numConnectors, OcppTime *ocppTime); + ChargePointStatusService(OcppEngine& context, int numConnectors); ~ChargePointStatusService(); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 36489d78..efd5b5d6 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -4,14 +4,19 @@ #include +#include #include -#include +#include + +#include + #include using namespace ArduinoOcpp; using namespace ArduinoOcpp::Ocpp16; -ConnectorStatus::ConnectorStatus(int connectorId, OcppTime *ocppTime) : connectorId(connectorId), ocppTime(ocppTime) { +ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) + : context(context), connectorId{connectorId} { //Set default transaction ID in memory String keyTx = "OCPP_STATE_TRANSACTION_ID_CONNECTOR_"; @@ -32,7 +37,7 @@ OcppEvseState ConnectorStatus::inferenceStatus() { * Handle special case: This is the ConnectorStatus for the whole CP (i.e. connectorId=0) --> only states Available, Unavailable, Faulted are possible */ if (connectorId == 0) { - if (getErrorCode() != NULL) { + if (getErrorCode() != nullptr) { return OcppEvseState::Faulted; } else if (*availability == AVAILABILITY_INOPERATIVE) { return OcppEvseState::Unavailable; @@ -41,18 +46,20 @@ OcppEvseState ConnectorStatus::inferenceStatus() { } } + auto cpStatusService = context.getChargePointStatusService(); + // if (!authorized && !getChargePointStatusService()->existsUnboundAuthorization()) { // return OcppEvseState::Available; // } else if (((int) *transactionId) < 0) { // return OcppEvseState::Preparing; - //if (connectorFaultedSampler != NULL && connectorFaultedSampler()) { - if (getErrorCode() != NULL) { + //if (connectorFaultedSampler != nullptr && connectorFaultedSampler()) { + if (getErrorCode() != nullptr) { return OcppEvseState::Faulted; } else if (*availability == AVAILABILITY_INOPERATIVE) { return OcppEvseState::Unavailable; - } else if (!authorized && !getChargePointStatusService()->existsUnboundAuthorization() && + } else if (!authorized && cpStatusService && !cpStatusService->existsUnboundAuthorization() && ((int) *transactionId) < 0 && - (connectorPluggedSampler == NULL || !connectorPluggedSampler()) ) { + (connectorPluggedSampler == nullptr || !connectorPluggedSampler()) ) { return OcppEvseState::Available; } else if (((int) *transactionId) <= 0) { return OcppEvseState::Preparing; @@ -68,13 +75,13 @@ OcppEvseState ConnectorStatus::inferenceStatus() { } } -StatusNotification *ConnectorStatus::loop() { +OcppMessage *ConnectorStatus::loop() { if (getTransactionId() <= 0 && *availability == AVAILABILITY_INOPERATIVE_SCHEDULED) { *availability = AVAILABILITY_INOPERATIVE; saveState(); } - OcppEvseState inferencedStatus = inferenceStatus(); + auto inferencedStatus = inferenceStatus(); if (inferencedStatus != currentStatus) { currentStatus = inferencedStatus; @@ -83,20 +90,20 @@ StatusNotification *ConnectorStatus::loop() { //fire StatusNotification //TODO check for online condition: Only inform CS about status change if CP is online //TODO check for too short duration condition: Only inform CS about status change if it lasted for longer than MinimumStatusDuration - return new StatusNotification(connectorId, currentStatus, ocppTime->getOcppTimestampNow(), getErrorCode()); + return new StatusNotification(connectorId, currentStatus, context.getOcppTime().getOcppTimestampNow(), getErrorCode()); } - return NULL; + return nullptr; } const char *ConnectorStatus::getErrorCode() { for (auto s = connectorErrorCodeSamplers.begin(); s != connectorErrorCodeSamplers.end(); s++) { const char *err = s->operator()(); - if (err != NULL) { + if (err != nullptr) { return err; } } - return NULL; + return nullptr; } void ConnectorStatus::authorize(String &idTag){ @@ -195,10 +202,6 @@ void ConnectorStatus::setConnectorPluggedSampler(std::function connector this->connectorPluggedSampler = connectorPlugged; } -//void ConnectorStatus::setConnectorFaultedSampler(std::function connectorFaulted) { -// this->connectorFaultedSampler = connectorFaulted; -//} - void ConnectorStatus::addConnectorErrorCodeSampler(std::function connectorErrorCode) { this->connectorErrorCodeSamplers.push_back(connectorErrorCode); } diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index 944e99e9..3f9527d9 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -5,9 +5,8 @@ #ifndef CONNECTOR_STATUS #define CONNECTOR_STATUS -#include #include -#include +#include #define AVAILABILITY_OPERATIVE 2 #define AVAILABILITY_INOPERATIVE_SCHEDULED 1 @@ -15,29 +14,33 @@ namespace ArduinoOcpp { +class OcppModel; +class OcppMessage; + class ConnectorStatus { private: + OcppModel& context; + const int connectorId; - OcppTime *ocppTime; - std::shared_ptr> availability = NULL; + std::shared_ptr> availability = nullptr; bool authorized = false; String idTag = String('\0'); bool transactionRunning = false; //int transactionId = -1; - std::shared_ptr> transactionId = NULL; + std::shared_ptr> transactionId = nullptr; int transactionIdSync = -1; bool evDrawsEnergy = false; bool evseOffersEnergy = false; - std::function connectorPluggedSampler = NULL; - //std::function connectorFaultedSampler = NULL; + std::function connectorPluggedSampler = nullptr; + //std::function connectorFaultedSampler = nullptr; std::vector> connectorErrorCodeSamplers; const char *getErrorCode(); OcppEvseState currentStatus = OcppEvseState::NOT_SET; - std::function onUnlockConnector = NULL; + std::function onUnlockConnector = nullptr; public: - ConnectorStatus(int connectorId, OcppTime *ocppTime); + ConnectorStatus(OcppModel& context, int connectorId); //boolean requestAuthorization(); void authorize(); @@ -57,13 +60,12 @@ class ConnectorStatus { void startEnergyOffer(); void stopEnergyOffer(); void setConnectorPluggedSampler(std::function connectorPlugged); - //void setConnectorFaultedSampler(std::function connectorFaulted); void addConnectorErrorCodeSampler(std::function connectorErrorCode); void saveState(); //void recoverState(); - Ocpp16::StatusNotification *loop(); + OcppMessage *loop(); OcppEvseState inferenceStatus(); diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp index babc1b1a..f7f0e39b 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp @@ -4,18 +4,23 @@ #include #include +#include #include +#include + +#include + using namespace ArduinoOcpp; using Ocpp16::DiagnosticsStatus; void DiagnosticsService::loop() { auto notification = getDiagnosticsStatusNotification(); if (notification) { - initiateOcppOperation(std::move(notification)); + context.initiateOperation(std::move(notification)); } - const OcppTimestamp& timestampNow = getOcppTime()->getOcppTimestampNow(); + const auto& timestampNow = context.getOcppModel().getOcppTime().getOcppTimestampNow(); if (retries > 0 && timestampNow >= nextTry) { if (!uploadIssued) { @@ -80,7 +85,7 @@ String DiagnosticsService::requestDiagnosticsUpload(String &location, int retrie if (stopTime >= stopMin) { this->stopTime = stopTime; } else { - OcppTimestamp newStop = getOcppTime()->getOcppTimestampNow(); + auto newStop = context.getOcppModel().getOcppTime().getOcppTimestampNow(); newStop += 3600 * 24 * 365; //set new stop time one year in future this->stopTime = newStop; } @@ -102,7 +107,7 @@ String DiagnosticsService::requestDiagnosticsUpload(String &location, int retrie Serial.println(dbuf); } - nextTry = getOcppTime()->getOcppTimestampNow(); + nextTry = context.getOcppModel().getOcppTime().getOcppTimestampNow(); nextTry += 5; //wait for 5s before upload uploadIssued = false; @@ -159,8 +164,8 @@ void DiagnosticsService::setOnUploadStatusSampler(std::function #if !defined(AO_CUSTOM_DIAGNOSTICS) && !defined(AO_CUSTOM_WEBSOCKET) #if defined(ESP32) || defined(ESP8266) -DiagnosticsService *EspWiFi::makeDiagnosticsService() { - DiagnosticsService *diagService = new DiagnosticsService(); +DiagnosticsService *EspWiFi::makeDiagnosticsService(OcppEngine& context) { + auto diagService = new DiagnosticsService(context); /* * add onUpload and uploadStatusSampler when logging was implemented diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h index 178e8a4e..0e0b750d 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h @@ -7,9 +7,9 @@ #include #include +#include #include -#include -#include +#include namespace ArduinoOcpp { @@ -19,8 +19,13 @@ enum class UploadStatus { UploadFailed }; +class OcppEngine; +class OcppOperation; + class DiagnosticsService { private: + OcppEngine& context; + String location = String('\0'); int retries = 0; unsigned int retryInterval = 0; @@ -29,8 +34,8 @@ class DiagnosticsService { OcppTimestamp nextTry = OcppTimestamp(); - std::function onUpload = NULL; - std::function uploadStatusSampler = NULL; + std::function onUpload = nullptr; + std::function uploadStatusSampler = nullptr; bool uploadIssued = false; std::unique_ptr getDiagnosticsStatusNotification(); @@ -38,7 +43,7 @@ class DiagnosticsService { Ocpp16::DiagnosticsStatus lastReportedStatus = Ocpp16::DiagnosticsStatus::Idle; public: - DiagnosticsService() = default; + DiagnosticsService(OcppEngine& context) : context(context) { } void loop(); @@ -59,7 +64,7 @@ class DiagnosticsService { namespace EspWiFi { -DiagnosticsService *makeDiagnosticsService(); +DiagnosticsService *makeDiagnosticsService(OcppEngine& context); } diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsStatus.h b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsStatus.h new file mode 100644 index 00000000..f3500e67 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsStatus.h @@ -0,0 +1,20 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef DIAGNOSTICS_STATUS +#define DIAGNOSTICS_STATUS + +namespace ArduinoOcpp { +namespace Ocpp16 { + +enum class DiagnosticsStatus { + Idle, + Uploaded, + UploadFailed, + Uploading +}; + +} +} //end namespace ArduinoOcpp +#endif diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index 9ca96807..28798ca1 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -3,11 +3,13 @@ // MIT License #include +#include +#include +#include #include #include + #include -#include -#include using namespace ArduinoOcpp; using ArduinoOcpp::Ocpp16::FirmwareStatus; @@ -23,21 +25,24 @@ void FirmwareService::setBuildNumber(const char *buildNumber) { void FirmwareService::loop() { auto notification = getFirmwareStatusNotification(); if (notification) { - initiateOcppOperation(std::move(notification)); + context.initiateOperation(std::move(notification)); } if (millis() - timestampTransition < delayTransition) { return; } - OcppTimestamp timestampNow = getOcppTime()->getOcppTimestampNow(); + OcppTimestamp timestampNow = context.getOcppModel().getOcppTime().getOcppTimestampNow(); if (retries > 0 && timestampNow >= retreiveDate) { + auto cpStatusService = context.getOcppModel().getChargePointStatusService(); + //if (!downloadIssued) { if (stage == UpdateStage::Idle) { if (DEBUG_OUT) Serial.println(F("[FirmwareService] Start update!")); - if (getChargePointStatusService()) { - ConnectorStatus *evse = getChargePointStatusService()->getConnector(0); + + if (cpStatusService) { + ConnectorStatus *evse = cpStatusService->getConnector(0); if (!availabilityRestore) { availabilityRestore = (evse->getAvailability() == AVAILABILITY_OPERATIVE); } @@ -103,10 +108,9 @@ void FirmwareService::loop() { if (stage == UpdateStage::AfterDownload) { if (DEBUG_OUT) Serial.println(F("[FirmwareService] After download!")); bool ongoingTx = false; - ChargePointStatusService *cpStatus = getChargePointStatusService(); - if (cpStatus) { - for (int i = 0; i < cpStatus->getNumConnectors(); i++) { - ConnectorStatus *connector = cpStatus->getConnector(i); + if (cpStatusService) { + for (int i = 0; i < cpStatusService->getNumConnectors(); i++) { + auto connector = cpStatusService->getConnector(i); if (connector && connector->getTransactionId() >= 0) { ongoingTx = true; break; @@ -150,8 +154,8 @@ void FirmwareService::loop() { resetStage(); retries = 0; //End of update routine. Client must reboot on its own if (availabilityRestore) { - if (getChargePointStatusService() && getChargePointStatusService()->getConnector(0)) { - getChargePointStatusService()->getConnector(0)->setAvailability(true); + if (cpStatusService && cpStatusService->getConnector(0)) { + cpStatusService->getConnector(0)->setAvailability(true); } } return; @@ -293,8 +297,8 @@ void FirmwareService::resetStage() { #include -FirmwareService *EspWiFi::makeFirmwareService(const char *buildNumber) { - FirmwareService *fwService = new FirmwareService(); +FirmwareService *EspWiFi::makeFirmwareService(OcppEngine& context, const char *buildNumber) { + FirmwareService *fwService = new FirmwareService(context); fwService->setBuildNumber(buildNumber); /* @@ -356,8 +360,8 @@ FirmwareService *EspWiFi::makeFirmwareService(const char *buildNumber) { #include -FirmwareService *EspWiFi::makeFirmwareService(const char *buildNumber) { - FirmwareService *fwService = new FirmwareService(); +FirmwareService *EspWiFi::makeFirmwareService(OcppEngine& context, const char *buildNumber) { + FirmwareService *fwService = new FirmwareService(context); fwService->setBuildNumber(buildNumber); fwService->setOnInstall([fwService] (String &location) { diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h index 120c97de..1f96df77 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h @@ -7,10 +7,10 @@ #include #include +#include -#include #include -#include +#include #include namespace ArduinoOcpp { @@ -27,9 +27,13 @@ enum class InstallationStatus { InstallationFailed }; +class OcppEngine; +class OcppOperation; class FirmwareService { private: + OcppEngine& context; + std::shared_ptr> previousBuildNumber = NULL; const char *buildNumber = NULL; @@ -69,7 +73,7 @@ class FirmwareService { std::unique_ptr getFirmwareStatusNotification(); public: - FirmwareService() { } + FirmwareService(OcppEngine& context) : context(context) { } void setBuildNumber(const char *buildNumber); @@ -96,7 +100,7 @@ class FirmwareService { namespace ArduinoOcpp { namespace EspWiFi { -FirmwareService *makeFirmwareService(const char *buildNumber); +FirmwareService *makeFirmwareService(OcppEngine& context, const char *buildNumber); } } diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareStatus.h b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareStatus.h new file mode 100644 index 00000000..fe3afa32 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareStatus.h @@ -0,0 +1,23 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2021 +// MIT License + +#ifndef FIRMWARE_STATUS +#define FIRMWARE_STATUS + +namespace ArduinoOcpp { +namespace Ocpp16 { + +enum class FirmwareStatus { + Downloaded, + DownloadFailed, + Downloading, + Idle, + InstallationFailed, + Installing, + Installed +}; + +} +} //end namespace ArduinoOcpp +#endif diff --git a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp index a8552caf..1e2acc6e 100644 --- a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp +++ b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp @@ -3,13 +3,14 @@ // MIT License #include -#include -#include #include +#include +#include +#include using namespace ArduinoOcpp; -HeartbeatService::HeartbeatService() { +HeartbeatService::HeartbeatService(OcppEngine& context) : context(context) { heartbeatInterval = declareConfiguration("HeartbeatInterval", 86400); lastHeartbeat = millis(); } @@ -23,6 +24,6 @@ void HeartbeatService::loop() { lastHeartbeat = now; auto heartbeat = makeOcppOperation("Heartbeat"); - initiateOcppOperation(std::move(heartbeat)); + context.initiateOperation(std::move(heartbeat)); } } diff --git a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h index 5e85b157..32fcde46 100644 --- a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h +++ b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h @@ -5,19 +5,21 @@ #ifndef HEARTBEATSERVICE_H #define HEARTBEATSERVICE_H -#include -#include +#include namespace ArduinoOcpp { +class OcppEngine; + class HeartbeatService { private: + OcppEngine& context; ulong lastHeartbeat; std::shared_ptr> heartbeatInterval; public: - HeartbeatService(); + HeartbeatService(OcppEngine& context); void loop(); }; diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index c3f766ba..b1289d30 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -3,13 +3,18 @@ // MIT License #include -#include +#include +#include +#include +#include + +#include using namespace ArduinoOcpp; using namespace ArduinoOcpp::Ocpp16; -ConnectorMeterValuesRecorder::ConnectorMeterValuesRecorder(int connectorId, OcppTime *ocppTime) - : connectorId(connectorId), ocppTime(ocppTime) { +ConnectorMeterValuesRecorder::ConnectorMeterValuesRecorder(OcppModel& context, int connectorId) + : context(context), connectorId{connectorId} { sampleTimestamp = std::vector(); energy = std::vector(); power = std::vector(); @@ -19,29 +24,29 @@ ConnectorMeterValuesRecorder::ConnectorMeterValuesRecorder(int connectorId, Ocpp } void ConnectorMeterValuesRecorder::takeSample() { - if (energySampler != NULL || powerSampler != NULL) { - if (!ocppTime->isValid()) return; - sampleTimestamp.push_back(ocppTime->getOcppTimestampNow()); + if (energySampler != nullptr || powerSampler != nullptr) { + if (!context.getOcppTime().isValid()) return; + sampleTimestamp.push_back(context.getOcppTime().getOcppTimestampNow()); } - if (energySampler != NULL) { + if (energySampler != nullptr) { energy.push_back(energySampler()); } - if (powerSampler != NULL) { + if (powerSampler != nullptr) { power.push_back(powerSampler()); } } -MeterValues *ConnectorMeterValuesRecorder::loop() { +OcppMessage *ConnectorMeterValuesRecorder::loop() { /* * First: check if there was a transaction break (i.e. transaction either started or stopped; transactionId changed) */ - ConnectorStatus *connector = getConnectorStatus(connectorId); - if (connector->getTransactionId() != lastTransactionId) { + auto connector = context.getConnectorStatus(connectorId); + if (connector && connector->getTransactionId() != lastTransactionId) { //transaction break occured! - MeterValues *result = toMeterValues(); + auto result = toMeterValues(); lastTransactionId = connector->getTransactionId(); return result; } @@ -63,37 +68,37 @@ MeterValues *ConnectorMeterValuesRecorder::loop() { * Is the value buffer already full? If yes, return MeterValues message */ if (((int) sampleTimestamp.size()) >= (int) *MeterValuesSampledDataMaxLength) { - MeterValues *result = toMeterValues(); + auto result = toMeterValues(); return result; } - return NULL; //successful method completition. Currently there is no reason to send a MeterValues Msg. + return nullptr; //successful method completition. Currently there is no reason to send a MeterValues Msg. } -MeterValues *ConnectorMeterValuesRecorder::toMeterValues() { +OcppMessage *ConnectorMeterValuesRecorder::toMeterValues() { if (sampleTimestamp.size() == 0) { //Switching from Non-Transaction to Transaction (or vice versa) without sample. Or anything wrong here. Discard if (DEBUG_OUT) Serial.print(F("[ConnectorMeterValuesRecorder] Try to send MeterValues without any data point. Ignore\n")); clear(); - return NULL; + return nullptr; } //decide which measurands to send. If a measurand is missing at at least one point in time, omit that measurand completely if (energy.size() == sampleTimestamp.size() && power.size() == sampleTimestamp.size()) { - MeterValues *result = new MeterValues(&sampleTimestamp, &energy, &power, connectorId, lastTransactionId); + auto result = new MeterValues(&sampleTimestamp, &energy, &power, connectorId, lastTransactionId); clear(); return result; } if (energy.size() == sampleTimestamp.size() && power.size() != sampleTimestamp.size()) { - MeterValues *result = new MeterValues(&sampleTimestamp, &energy, NULL, connectorId, lastTransactionId); + auto result = new MeterValues(&sampleTimestamp, &energy, nullptr, connectorId, lastTransactionId); clear(); return result; } if (energy.size() != sampleTimestamp.size() && power.size() == sampleTimestamp.size()) { - MeterValues *result = new MeterValues(&sampleTimestamp, NULL, &power, connectorId, lastTransactionId); + auto result = new MeterValues(&sampleTimestamp, nullptr, &power, connectorId, lastTransactionId); clear(); return result; } @@ -102,7 +107,7 @@ MeterValues *ConnectorMeterValuesRecorder::toMeterValues() { Serial.print(F("[ConnectorMeterValuesRecorder] Invalid data set. Discard data set and restard recording.\n")); clear(); - return NULL; + return nullptr; } void ConnectorMeterValuesRecorder::clear() { @@ -120,7 +125,7 @@ void ConnectorMeterValuesRecorder::setEnergySampler(EnergySampler es){ } float ConnectorMeterValuesRecorder::readEnergyActiveImportRegister() { - if (energySampler != NULL) { + if (energySampler != nullptr) { return energySampler(); } else { Serial.print(F("[ConnectorMeterValuesRecorder] Called readEnergyActiveImportRegister(), but no energySampler or handling strategy set!\n")); diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h index 1f26522f..0152694f 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h @@ -10,22 +10,24 @@ //#define METER_VALUES_SAMPLED_DATA_MAX_LENGTH 4 //after 4 measurements, send the values to the CS #include +#include -#include -#include -#include +#include namespace ArduinoOcpp { -//typedef float (*PowerSampler)(); -//typedef float (*EnergySampler)(); -typedef std::function PowerSampler; -typedef std::function EnergySampler; +using PowerSampler = std::function; +using EnergySampler = std::function; + +class OcppModel; +class OcppTimestamp; +class OcppMessage; class ConnectorMeterValuesRecorder { private: + OcppModel& context; + const int connectorId; - OcppTime *ocppTime; std::vector sampleTimestamp; std::vector energy; @@ -41,12 +43,12 @@ class ConnectorMeterValuesRecorder { std::shared_ptr> MeterValuesSampledDataMaxLength = NULL; void takeSample(); - Ocpp16::MeterValues *toMeterValues(); //returns message if connector has captured enough samples + OcppMessage *toMeterValues(); //returns message if connector has captured enough samples void clear(); public: - ConnectorMeterValuesRecorder(int connectorId, OcppTime *ocppTime); + ConnectorMeterValuesRecorder(OcppModel& context, int connectorId); - Ocpp16::MeterValues *loop(); + OcppMessage *loop(); void setPowerSampler(PowerSampler powerSampler); @@ -56,4 +58,4 @@ class ConnectorMeterValuesRecorder { }; } //end namespace ArduinoOcpp -#endif \ No newline at end of file +#endif diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp index 3dd78f04..828ffdee 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp @@ -3,23 +3,18 @@ // MIT License #include -#include -#include #include #include using namespace ArduinoOcpp; -using namespace ArduinoOcpp::Ocpp16; -MeteringService::MeteringService(int numConn, OcppTime *ocppTime) - : numConnectors(numConn) { +MeteringService::MeteringService(OcppEngine& context, int numConn) + : context(context), numConnectors{numConn} { connectors = (ConnectorMeterValuesRecorder**) malloc(numConn * sizeof(ConnectorMeterValuesRecorder*)); for (int i = 0; i < numConn; i++) { - connectors[i] = new ConnectorMeterValuesRecorder(i, ocppTime); + connectors[i] = new ConnectorMeterValuesRecorder(context.getOcppModel(), i); } - - setMeteringSerivce(this); //make MeteringService available through Ocpp Engine } MeteringService::~MeteringService() { @@ -32,11 +27,11 @@ MeteringService::~MeteringService() { void MeteringService::loop(){ for (int i = 0; i < numConnectors; i++){ - MeterValues *meterValuesMsg = connectors[i]->loop(); + auto meterValuesMsg = connectors[i]->loop(); if (meterValuesMsg != nullptr) { auto meterValues = makeOcppOperation(meterValuesMsg); meterValues->setTimeout(std::unique_ptr{new FixedTimeout(120000)}); - initiateOcppOperation(std::move(meterValues)); + context.initiateOperation(std::move(meterValues)); } } } diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h index 0617b57c..9318c6dd 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h @@ -17,29 +17,29 @@ namespace ArduinoOcpp { -//typedef float (*PowerSampler)(); -//typedef float (*EnergySampler)(); - -typedef std::function PowerSampler; -typedef std::function EnergySampler; +using PowerSampler = std::function; +using EnergySampler = std::function; +class OcppEngine; class MeteringService { private: - const int numConnectors; - ConnectorMeterValuesRecorder **connectors; + OcppEngine& context; + + const int numConnectors; + ConnectorMeterValuesRecorder **connectors; public: - MeteringService(int numConnectors, OcppTime *ocppTime); + MeteringService(OcppEngine& context, int numConnectors); - ~MeteringService(); + ~MeteringService(); - void loop(); + void loop(); - void setPowerSampler(int connectorId, PowerSampler powerSampler); + void setPowerSampler(int connectorId, PowerSampler powerSampler); - void setEnergySampler(int connectorId, EnergySampler energySampler); + void setEnergySampler(int connectorId, EnergySampler energySampler); - float readEnergyActiveImportRegister(int connectorId); + float readEnergyActiveImportRegister(int connectorId); }; } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp index ee661f33..266f593e 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp @@ -3,42 +3,44 @@ // MIT License #include -#include #include #include using namespace ArduinoOcpp; -const OcppTimestamp ArduinoOcpp::MIN_TIME = OcppTimestamp(2010, 0, 0, 0, 0, 0); -const OcppTimestamp ArduinoOcpp::MAX_TIME = OcppTimestamp(2037, 0, 0, 0, 0, 0); - ChargingSchedulePeriod::ChargingSchedulePeriod(JsonObject *json){ startPeriod = (*json)["startPeriod"]; limit = (*json)["limit"]; //one fractural digit at most numberPhases = (*json)["numberPhases"] | -1; } + ChargingSchedulePeriod::ChargingSchedulePeriod(int startPeriod, float limit){ this->startPeriod = startPeriod; this->limit = limit; numberPhases = -1; //support later } + int ChargingSchedulePeriod::getStartPeriod(){ return this->startPeriod; } + float ChargingSchedulePeriod::getLimit(){ return this->limit; } + void ChargingSchedulePeriod::scale(float factor){ limit *= factor; if (limit < 0.f) limit *= -1.f; } + void ChargingSchedulePeriod::add(float value){ limit += value; if (limit < 0.f) limit = 0; } + int ChargingSchedulePeriod::getNumberPhases(){ return this->numberPhases; } @@ -111,7 +113,7 @@ ChargingSchedule::ChargingSchedule(ChargingSchedule &other) { minChargingRate = other.minChargingRate; } -ChargingSchedule::ChargingSchedule(OcppTimestamp &startT, int duration) { +ChargingSchedule::ChargingSchedule(const OcppTimestamp &startT, int duration) { //create empty but valid Charging Schedule this->duration = duration; startSchedule = startT; diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h index f9eb7338..0b11ef52 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h @@ -10,9 +10,6 @@ namespace ArduinoOcpp { -extern const OcppTimestamp MIN_TIME; -extern const OcppTimestamp MAX_TIME; - enum class ChargingProfilePurposeType { ChargePointMaxProfile, TxDefaultProfile, @@ -60,7 +57,7 @@ class ChargingSchedule { public: ChargingSchedule(JsonObject *json, ChargingProfileKindType chargingProfileKind, RecurrencyKindType recurrencyKind); ChargingSchedule(ChargingSchedule &other); - ChargingSchedule(OcppTimestamp &startSchedule, int duration); + ChargingSchedule(const OcppTimestamp &startSchedule, int duration); ~ChargingSchedule(); /** diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index 583b07b5..a88e1066 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -2,10 +2,12 @@ // Copyright Matthias Akstaller 2019 - 2021 // MIT License -#include "Variants.h" +#include #include #include +#include +#include #include @@ -26,8 +28,8 @@ using namespace::ArduinoOcpp; -SmartChargingService::SmartChargingService(float chargeLimit, float V_eff, int numConnectors, OcppTime *ocppTime, FilesystemOpt filesystemOpt) - : DEFAULT_CHARGE_LIMIT(chargeLimit), V_eff(V_eff), ocppTime(ocppTime), filesystemOpt(filesystemOpt) { +SmartChargingService::SmartChargingService(OcppEngine& context, float chargeLimit, float V_eff, int numConnectors, FilesystemOpt filesystemOpt) + : context(context), DEFAULT_CHARGE_LIMIT{chargeLimit}, V_eff{V_eff}, filesystemOpt{filesystemOpt} { if (numConnectors > 2) { Serial.print(F("[SmartChargingService] Error: Unfortunately, multiple connectors are not implemented in SmartChargingService yet. Only connector 1 will receive charging limits\n")); @@ -43,7 +45,6 @@ SmartChargingService::SmartChargingService(float chargeLimit, float V_eff, int n TxDefaultProfile[i] = NULL; TxProfile[i] = NULL; } - setSmartChargingService(this); //in OcppEngine.cpp declareConfiguration("ChargeProfileMaxStackLevel", CHARGEPROFILEMAXSTACKLEVEL, CONFIGURATION_FN, false, true, false, true); loadProfiles(); @@ -56,8 +57,8 @@ void SmartChargingService::loop(){ /** * check if to call onLimitChange */ - if (ocppTime->getOcppTimestampNow() >= nextChange){ - OcppTimestamp tNow = ocppTime->getOcppTimestampNow(); + if (context.getOcppModel().getOcppTime().getOcppTimestampNow() >= nextChange){ + auto& tNow = context.getOcppModel().getOcppTime().getOcppTimestampNow(); float limit = -1.0f; OcppTimestamp validTo = OcppTimestamp(); inferenceLimit(tNow, &limit, &validTo); @@ -88,7 +89,8 @@ void SmartChargingService::loop(){ float SmartChargingService::inferenceLimitNow(){ float limit = 0.0f; OcppTimestamp validTo = OcppTimestamp(); //not needed - inferenceLimit(ocppTime->getOcppTimestampNow(), &limit, &validTo); + auto& tNow = context.getOcppModel().getOcppTime().getOcppTimestampNow(); + inferenceLimit(tNow, &limit, &validTo); return limit; } @@ -197,11 +199,7 @@ void SmartChargingService::writeOutCompositeSchedule(JsonObject *json){ } ChargingSchedule *SmartChargingService::getCompositeSchedule(int connectorId, otime_t duration){ - OcppTime *ocppTime = getOcppTime(); - OcppTimestamp startSchedule = OcppTimestamp(); - if (ocppTime) { - startSchedule = ocppTime->getOcppTimestampNow(); - } + auto& startSchedule = context.getOcppModel().getOcppTime().getOcppTimestampNow(); ChargingSchedule *result = new ChargingSchedule(startSchedule, duration); OcppTimestamp periodBegin = OcppTimestamp(startSchedule); OcppTimestamp periodStop = OcppTimestamp(startSchedule); @@ -219,49 +217,26 @@ ChargingSchedule *SmartChargingService::getCompositeSchedule(int connectorId, ot } void SmartChargingService::refreshChargingSessionState() { - int currentTxId = getChargePointStatusService()->getConnector(SINGLE_CONNECTOR_ID)->getTransactionId(); + int currentTxId = -1; + if (context.getOcppModel().getConnectorStatus(SINGLE_CONNECTOR_ID)) { + auto connector = context.getOcppModel().getConnectorStatus(SINGLE_CONNECTOR_ID); + currentTxId = connector->getTransactionId(); + } if (currentTxId != chargingSessionTransactionID) { //transition! if (chargingSessionTransactionID != 0 && currentTxId >= 0) { - chargingSessionStart = ocppTime->getOcppTimestampNow(); + chargingSessionStart = context.getOcppModel().getOcppTime().getOcppTimestampNow(); } else if (chargingSessionTransactionID >= 0 && currentTxId < 0) { chargingSessionStart = MAX_TIME; } - nextChange = ocppTime->getOcppTimestampNow(); + nextChange = context.getOcppModel().getOcppTime().getOcppTimestampNow(); chargingSessionTransactionID = currentTxId; } } -/* -void SmartChargingService::beginCharging(time_t t, int transactionID){ - if (chargingSessionIsActive) { - Serial.print(F("[SmartChargingService] Error: begin new Charging session though there is already running one!\n")); - } - chargingSessionStart = t; - chargingSessionTransactionID = transactionID; - chargingSessionIsActive = true; - nextChange = minimum(t, nextChange); -} - -void SmartChargingService::beginChargingNow(){ - beginCharging(now(), -1); -} - -void SmartChargingService::endChargingNow(){ - if (!chargingSessionIsActive) { - Serial.print(F("[SmartChargingService] Error: end Charging session but there isn't running one!\n")); - } - - chargingSessionStart = INFINITY_THLD; - chargingSessionTransactionID = -1; - chargingSessionIsActive = false; - nextChange = now(); -} -*/ - void SmartChargingService::updateChargingProfile(JsonObject *json) { ChargingProfile *pointer = updateProfileStack(json); if (pointer) @@ -307,7 +282,7 @@ ChargingProfile *SmartChargingService::updateProfileStack(JsonObject *json){ * Invalidate the last limit inference by setting the nextChange to now. By the next loop()-call, the limit * and nextChange will be recalculated and onLimitChanged will be called. */ - nextChange = ocppTime->getOcppTimestampNow(); + nextChange = context.getOcppModel().getOcppTime().getOcppTimestampNow(); return chargingProfile; } @@ -362,7 +337,7 @@ bool SmartChargingService::clearChargingProfile(const std::functiongetOcppTimestampNow(); + nextChange = context.getOcppModel().getOcppTime().getOcppTimestampNow(); return nMatches > 0; } diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h index b436a9ea..a37c2ad3 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h @@ -16,14 +16,16 @@ namespace ArduinoOcpp { -//typedef void (*OnLimitChange)(float newLimit); -typedef std::function OnLimitChange; +using OnLimitChange = std::function; + +class OcppEngine; class SmartChargingService { private: + OcppEngine& context; + const float DEFAULT_CHARGE_LIMIT; const float V_eff; //use for approximation: chargingLimit in A * V_eff = chargingLimit in W - OcppTime *ocppTime; ChargingProfile *ChargePointMaxProfile[CHARGEPROFILEMAXSTACKLEVEL]; ChargingProfile *TxDefaultProfile[CHARGEPROFILEMAXSTACKLEVEL]; ChargingProfile *TxProfile[CHARGEPROFILEMAXSTACKLEVEL]; @@ -41,10 +43,7 @@ class SmartChargingService { bool loadProfiles(); public: - SmartChargingService(float chargeLimit, float V_eff, int numConnectors, OcppTime *ocppTime, FilesystemOpt filesystemOpt = FilesystemOpt::Use_Mount_FormatOnFail); - //void beginCharging(const OcppTimestamp &t, int transactionID); - //void beginChargingNow(); - //void endChargingNow(); + SmartChargingService(OcppEngine& context, float chargeLimit, float V_eff, int numConnectors, FilesystemOpt filesystemOpt = FilesystemOpt::Use_Mount_FormatOnFail); void updateChargingProfile(JsonObject *json); bool clearChargingProfile(const std::function& filter); void inferenceLimit(const OcppTimestamp &t, float *limit, OcppTimestamp *validTo); From 343f9cad49363ed7c08d1857eacc6f5b93157914 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 22 Dec 2021 16:41:00 +0100 Subject: [PATCH 092/696] support for Finishing (StatusNotification) --- platformio.ini | 2 +- .../Tasks/ChargePointStatus/ConnectorStatus.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index eb8da828..521d2043 100644 --- a/platformio.ini +++ b/platformio.ini @@ -44,5 +44,5 @@ build_flags = -DAO_DEBUG_OUT -DAO_TRAFFIC_OUT -DCONFIG_LITTLEFS_FOR_IDF_3_2 -board_build.partitions = no_ota.csv +board_build.partitions = min_spiffs.csv upload_speed = 921600 diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index efd5b5d6..6fa48136 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -62,6 +62,13 @@ OcppEvseState ConnectorStatus::inferenceStatus() { (connectorPluggedSampler == nullptr || !connectorPluggedSampler()) ) { return OcppEvseState::Available; } else if (((int) *transactionId) <= 0) { + if (connectorPluggedSampler != nullptr && connectorPluggedSampler() && + (currentStatus == OcppEvseState::Finishing || + currentStatus == OcppEvseState::Charging || + currentStatus == OcppEvseState::SuspendedEV || + currentStatus == OcppEvseState::SuspendedEVSE)) { + return OcppEvseState::Finishing; + } return OcppEvseState::Preparing; } else { //Transaction is currently running From c81aee16bc1bc96a1374e8ecb68ff1206d92e556 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 17 Jan 2022 14:09:28 +0100 Subject: [PATCH 093/696] update dependencies --- examples/SECC/main.cpp | 2 +- library.json | 20 +++++++++++++++++--- platformio.ini | 3 +-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/examples/SECC/main.cpp b/examples/SECC/main.cpp index ce5f3a2c..e1e3907e 100644 --- a/examples/SECC/main.cpp +++ b/examples/SECC/main.cpp @@ -15,7 +15,7 @@ #include -#include //please install tzapu/WiFiManager@0.16.0 +#include //please install https://github.com/tzapu/WiFiManager.git #include #include //load and save settings of WiFi captive portal diff --git a/library.json b/library.json index 06a129ae..9f1303df 100644 --- a/library.json +++ b/library.json @@ -18,10 +18,24 @@ ], "license": "MIT", "homepage": "https://www.arduino-ocpp.com", - "dependencies": { - "ArduinoJson": "6.17.2", - "WebSockets": "2.2.0" + "dependencies": [ + { + "owner": "bblanchon", + "name": "ArduinoJson", + "version": "6.19.1" }, + { + "owner": "links2004", + "name": "WebSockets", + "version": "2.3.6" + }, + { + "owner": "lorol", + "name": "LittleFS_esp32", + "version": "1.0.5", + "platforms": ["espressif32"] + } + ], "frameworks": "arduino", "platforms": "espressif8266, espressif32", diff --git a/platformio.ini b/platformio.ini index 521d2043..b83b49b9 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,8 +13,7 @@ default_envs = esp32-development-board [common] framework = arduino lib_deps = -; bblanchon/ArduinoJson@6.18.5 - https://github.com/bblanchon/ArduinoJson.git#b06bbd9d2a9b8e6eb0d48602e107c53e1aa9689b + bblanchon/ArduinoJson@6.19.1 links2004/WebSockets@2.3.6 build_flags = -D USE_FACADE=true From caf147890537589c4164c395a04f836833ccd4ff Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 17 Jan 2022 16:53:31 +0100 Subject: [PATCH 094/696] add missing include --- src/ArduinoOcpp.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 05b57e9b..cc0dda78 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -6,6 +6,7 @@ #define ARDUINOOCPP_H #include +#include #include #include From 4b40a752252a71004030ddcb133b1850d6214564 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 18 Jan 2022 17:30:51 +0100 Subject: [PATCH 095/696] remove server include --- src/ArduinoOcpp/Core/OcppServer.h | 7 +++---- src/ArduinoOcpp/Core/OcppSocket.cpp | 3 ++- src/ArduinoOcpp/Core/OcppSocket.h | 6 ++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/ArduinoOcpp/Core/OcppServer.h b/src/ArduinoOcpp/Core/OcppServer.h index e551a589..913426db 100644 --- a/src/ArduinoOcpp/Core/OcppServer.h +++ b/src/ArduinoOcpp/Core/OcppServer.h @@ -1,17 +1,16 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef OCPPSERVER_H #define OCPPSERVER_H #include -#include +#include namespace ArduinoOcpp { -typedef uint8_t WsClient; -typedef std::function ReceiveTXTcallback; +using WsClient = uint8_t; } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Core/OcppSocket.cpp b/src/ArduinoOcpp/Core/OcppSocket.cpp index 90a7bee9..05634e49 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.cpp +++ b/src/ArduinoOcpp/Core/OcppSocket.cpp @@ -1,8 +1,9 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include +#include #include #ifndef AO_CUSTOM_WS diff --git a/src/ArduinoOcpp/Core/OcppSocket.h b/src/ArduinoOcpp/Core/OcppSocket.h index 72d2ed9f..9e5f1544 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.h +++ b/src/ArduinoOcpp/Core/OcppSocket.h @@ -1,15 +1,17 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef OCPPSOCKET_H #define OCPPSOCKET_H #include -#include +#include namespace ArduinoOcpp { +using ReceiveTXTcallback = std::function; + class OcppSocket { public: OcppSocket() = default; From 1821702228642f9650d924758ed0b88a192b8142 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 20 Jan 2022 12:18:36 +0100 Subject: [PATCH 096/696] update copyright notice --- LICENSE | 2 +- examples/OneConnector-EVSE/OneConnector_EVSE.cpp | 2 +- examples/OneConnector-EVSE/OneConnector_HW_integration.cpp | 2 +- examples/OneConnector-EVSE/OneConnector_HW_integration.h | 2 +- examples/SECC/main.cpp | 2 +- library.json | 7 ++++--- platformio.ini | 2 +- src/ArduinoOcpp.cpp | 2 +- src/ArduinoOcpp.h | 2 +- src/ArduinoOcpp/Core/Configuration.cpp | 2 +- src/ArduinoOcpp/Core/Configuration.h | 2 +- src/ArduinoOcpp/Core/ConfigurationContainer.cpp | 2 +- src/ArduinoOcpp/Core/ConfigurationContainer.h | 2 +- src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp | 2 +- src/ArduinoOcpp/Core/ConfigurationContainerFlash.h | 2 +- src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp | 2 +- src/ArduinoOcpp/Core/ConfigurationKeyValue.h | 2 +- src/ArduinoOcpp/Core/ConfigurationOptions.h | 2 +- src/ArduinoOcpp/Core/OcppConnection.cpp | 2 +- src/ArduinoOcpp/Core/OcppConnection.h | 2 +- src/ArduinoOcpp/Core/OcppEngine.cpp | 2 +- src/ArduinoOcpp/Core/OcppEngine.h | 2 +- src/ArduinoOcpp/Core/OcppError.h | 2 +- src/ArduinoOcpp/Core/OcppMessage.cpp | 2 +- src/ArduinoOcpp/Core/OcppMessage.h | 2 +- src/ArduinoOcpp/Core/OcppModel.cpp | 2 +- src/ArduinoOcpp/Core/OcppModel.h | 2 +- src/ArduinoOcpp/Core/OcppOperation.cpp | 2 +- src/ArduinoOcpp/Core/OcppOperation.h | 2 +- src/ArduinoOcpp/Core/OcppOperationCallbacks.h | 2 +- src/ArduinoOcpp/Core/OcppOperationTimeout.cpp | 2 +- src/ArduinoOcpp/Core/OcppOperationTimeout.h | 2 +- src/ArduinoOcpp/Core/OcppServer.cpp | 2 +- src/ArduinoOcpp/Core/OcppTime.cpp | 2 +- src/ArduinoOcpp/Core/OcppTime.h | 2 +- src/ArduinoOcpp/MessagesV16/Authorize.cpp | 2 +- src/ArduinoOcpp/MessagesV16/Authorize.h | 2 +- src/ArduinoOcpp/MessagesV16/BootNotification.cpp | 2 +- src/ArduinoOcpp/MessagesV16/BootNotification.h | 2 +- src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp | 2 +- src/ArduinoOcpp/MessagesV16/ChangeAvailability.h | 2 +- src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp | 2 +- src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h | 2 +- src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp | 2 +- src/ArduinoOcpp/MessagesV16/ClearChargingProfile.h | 2 +- src/ArduinoOcpp/MessagesV16/DataTransfer.cpp | 2 +- src/ArduinoOcpp/MessagesV16/DataTransfer.h | 2 +- .../MessagesV16/DiagnosticsStatusNotification.cpp | 2 +- .../MessagesV16/DiagnosticsStatusNotification.h | 2 +- src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp | 2 +- src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h | 2 +- src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp | 2 +- src/ArduinoOcpp/MessagesV16/GetConfiguration.h | 2 +- src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp | 2 +- src/ArduinoOcpp/MessagesV16/GetDiagnostics.h | 2 +- src/ArduinoOcpp/MessagesV16/Heartbeat.cpp | 2 +- src/ArduinoOcpp/MessagesV16/Heartbeat.h | 2 +- src/ArduinoOcpp/MessagesV16/MeterValues.cpp | 2 +- src/ArduinoOcpp/MessagesV16/MeterValues.h | 2 +- src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp | 2 +- src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h | 2 +- src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp | 2 +- src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h | 2 +- src/ArduinoOcpp/MessagesV16/Reset.cpp | 2 +- src/ArduinoOcpp/MessagesV16/Reset.h | 2 +- src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp | 2 +- src/ArduinoOcpp/MessagesV16/SetChargingProfile.h | 2 +- src/ArduinoOcpp/MessagesV16/StartTransaction.cpp | 2 +- src/ArduinoOcpp/MessagesV16/StartTransaction.h | 2 +- src/ArduinoOcpp/MessagesV16/StatusNotification.cpp | 2 +- src/ArduinoOcpp/MessagesV16/StatusNotification.h | 2 +- src/ArduinoOcpp/MessagesV16/StopTransaction.cpp | 2 +- src/ArduinoOcpp/MessagesV16/StopTransaction.h | 2 +- src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp | 2 +- src/ArduinoOcpp/MessagesV16/TriggerMessage.h | 2 +- src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp | 2 +- src/ArduinoOcpp/MessagesV16/UnlockConnector.h | 2 +- src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp | 2 +- src/ArduinoOcpp/MessagesV16/UpdateFirmware.h | 2 +- src/ArduinoOcpp/SimpleOcppOperationFactory.cpp | 2 +- src/ArduinoOcpp/SimpleOcppOperationFactory.h | 2 +- .../Tasks/ChargePointStatus/ChargePointStatusService.cpp | 2 +- .../Tasks/ChargePointStatus/ChargePointStatusService.h | 2 +- .../Tasks/ChargePointStatus/ConnectorStatus.cpp | 2 +- src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h | 2 +- src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h | 2 +- src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp | 2 +- src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h | 2 +- src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsStatus.h | 2 +- .../Tasks/FirmwareManagement/FirmwareService.cpp | 2 +- src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h | 2 +- src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareStatus.h | 2 +- src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp | 2 +- src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h | 2 +- .../Tasks/Metering/ConnectorMeterValuesRecorder.cpp | 2 +- .../Tasks/Metering/ConnectorMeterValuesRecorder.h | 2 +- src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp | 2 +- src/ArduinoOcpp/Tasks/Metering/MeteringService.h | 2 +- src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp | 2 +- src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h | 2 +- .../Tasks/SmartCharging/SmartChargingService.cpp | 2 +- src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h | 2 +- src/Variants.h | 2 +- 103 files changed, 106 insertions(+), 105 deletions(-) diff --git a/LICENSE b/LICENSE index 9ce535eb..c24c6219 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 - 2021 matth-x +Copyright (c) 2019 - 2022 matth-x Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/examples/OneConnector-EVSE/OneConnector_EVSE.cpp b/examples/OneConnector-EVSE/OneConnector_EVSE.cpp index 67d94f7f..3743b730 100644 --- a/examples/OneConnector-EVSE/OneConnector_EVSE.cpp +++ b/examples/OneConnector-EVSE/OneConnector_EVSE.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/examples/OneConnector-EVSE/OneConnector_HW_integration.cpp b/examples/OneConnector-EVSE/OneConnector_HW_integration.cpp index 192c3f78..e1b318a3 100644 --- a/examples/OneConnector-EVSE/OneConnector_HW_integration.cpp +++ b/examples/OneConnector-EVSE/OneConnector_HW_integration.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/examples/OneConnector-EVSE/OneConnector_HW_integration.h b/examples/OneConnector-EVSE/OneConnector_HW_integration.h index 9bcadfc4..3bcd5519 100644 --- a/examples/OneConnector-EVSE/OneConnector_HW_integration.h +++ b/examples/OneConnector-EVSE/OneConnector_HW_integration.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License /* diff --git a/examples/SECC/main.cpp b/examples/SECC/main.cpp index e1e3907e..70b9ff04 100644 --- a/examples/SECC/main.cpp +++ b/examples/SECC/main.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License // // diff --git a/library.json b/library.json index 9f1303df..5231cdff 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "ArduinoOcpp", - "version": "0.0.4", + "version": "0.0.5", "description": "OCPP 1.6 Client for the ESP8266 and ESP32", "keywords": "OCPP, 1.6, OCPP 1.6, Smart Energy, Smart Charging, client, ESP8266, ESP32, Arduino, EVSE, Charge Point", "repository": @@ -23,7 +23,7 @@ "owner": "bblanchon", "name": "ArduinoJson", "version": "6.19.1" - }, + }, { "owner": "links2004", "name": "WebSockets", @@ -54,7 +54,8 @@ "src/OneConnector*", "src/Simulated_HW*", "src/sdkconfig*", - "examples/SECC/WiFiManager*" + "examples/SECC/WiFiManager*", + "src/main.cpp" ] }, diff --git a/platformio.ini b/platformio.ini index b83b49b9..cc2f6196 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,5 +1,5 @@ ; matth-x/ArduinoOcpp -; Copyright Matthias Akstaller 2019 - 2021 +; Copyright Matthias Akstaller 2019 - 2022 ; MIT License ; ; PlatformIO Project Configuration File diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 818aeaf5..5fac0edf 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include "ArduinoOcpp.h" diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index cc0dda78..a649e606 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef ARDUINOOCPP_H diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index 63f66455..ce091fc8 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Core/Configuration.h b/src/ArduinoOcpp/Core/Configuration.h index deefbf4a..f597d408 100644 --- a/src/ArduinoOcpp/Core/Configuration.h +++ b/src/ArduinoOcpp/Core/Configuration.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef CONFIGURATION_H diff --git a/src/ArduinoOcpp/Core/ConfigurationContainer.cpp b/src/ArduinoOcpp/Core/ConfigurationContainer.cpp index e735d1f5..246b6b56 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainer.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainer.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Core/ConfigurationContainer.h b/src/ArduinoOcpp/Core/ConfigurationContainer.h index 70a76002..1074ff96 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainer.h +++ b/src/ArduinoOcpp/Core/ConfigurationContainer.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef CONFIGURATIONCONTAINER_H diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp index 9f601657..358a2fbb 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.h b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.h index cc1471e4..e401ab94 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.h +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef CONFIGURATIONCONTAINERFLASH_H diff --git a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp index 2325a83d..20b59810 100644 --- a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Core/ConfigurationKeyValue.h b/src/ArduinoOcpp/Core/ConfigurationKeyValue.h index d33fe3e8..07aa5843 100644 --- a/src/ArduinoOcpp/Core/ConfigurationKeyValue.h +++ b/src/ArduinoOcpp/Core/ConfigurationKeyValue.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef CONFIGURATIONKEYVALUE_H diff --git a/src/ArduinoOcpp/Core/ConfigurationOptions.h b/src/ArduinoOcpp/Core/ConfigurationOptions.h index 7406a551..b95ce96e 100644 --- a/src/ArduinoOcpp/Core/ConfigurationOptions.h +++ b/src/ArduinoOcpp/Core/ConfigurationOptions.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef CONFIGURATIONOPTIONS_H diff --git a/src/ArduinoOcpp/Core/OcppConnection.cpp b/src/ArduinoOcpp/Core/OcppConnection.cpp index d7906d65..e9d0a2b1 100644 --- a/src/ArduinoOcpp/Core/OcppConnection.cpp +++ b/src/ArduinoOcpp/Core/OcppConnection.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Core/OcppConnection.h b/src/ArduinoOcpp/Core/OcppConnection.h index b3427613..daabf22c 100644 --- a/src/ArduinoOcpp/Core/OcppConnection.h +++ b/src/ArduinoOcpp/Core/OcppConnection.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef OCPPCONNECTION_H diff --git a/src/ArduinoOcpp/Core/OcppEngine.cpp b/src/ArduinoOcpp/Core/OcppEngine.cpp index 5f082ce7..86171658 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.cpp +++ b/src/ArduinoOcpp/Core/OcppEngine.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Core/OcppEngine.h b/src/ArduinoOcpp/Core/OcppEngine.h index c2ff6db1..70b8853c 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.h +++ b/src/ArduinoOcpp/Core/OcppEngine.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef OCPPENGINE_H diff --git a/src/ArduinoOcpp/Core/OcppError.h b/src/ArduinoOcpp/Core/OcppError.h index 70e97274..f29514e3 100644 --- a/src/ArduinoOcpp/Core/OcppError.h +++ b/src/ArduinoOcpp/Core/OcppError.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef OCPPERROR_H diff --git a/src/ArduinoOcpp/Core/OcppMessage.cpp b/src/ArduinoOcpp/Core/OcppMessage.cpp index 08cdf793..2e0f1115 100644 --- a/src/ArduinoOcpp/Core/OcppMessage.cpp +++ b/src/ArduinoOcpp/Core/OcppMessage.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Core/OcppMessage.h b/src/ArduinoOcpp/Core/OcppMessage.h index 0e0c2fb5..ab88d345 100644 --- a/src/ArduinoOcpp/Core/OcppMessage.h +++ b/src/ArduinoOcpp/Core/OcppMessage.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License /** diff --git a/src/ArduinoOcpp/Core/OcppModel.cpp b/src/ArduinoOcpp/Core/OcppModel.cpp index 155a770d..40bd8d38 100644 --- a/src/ArduinoOcpp/Core/OcppModel.cpp +++ b/src/ArduinoOcpp/Core/OcppModel.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Core/OcppModel.h b/src/ArduinoOcpp/Core/OcppModel.h index 5a8c156c..e0ae10b8 100644 --- a/src/ArduinoOcpp/Core/OcppModel.h +++ b/src/ArduinoOcpp/Core/OcppModel.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef OCPPMODEL_H diff --git a/src/ArduinoOcpp/Core/OcppOperation.cpp b/src/ArduinoOcpp/Core/OcppOperation.cpp index 1e2d9b5a..85b23020 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.cpp +++ b/src/ArduinoOcpp/Core/OcppOperation.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Core/OcppOperation.h b/src/ArduinoOcpp/Core/OcppOperation.h index 0eab3513..369744b6 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.h +++ b/src/ArduinoOcpp/Core/OcppOperation.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef OCPPOPERATION_H diff --git a/src/ArduinoOcpp/Core/OcppOperationCallbacks.h b/src/ArduinoOcpp/Core/OcppOperationCallbacks.h index 6daeb3ea..a79bb513 100644 --- a/src/ArduinoOcpp/Core/OcppOperationCallbacks.h +++ b/src/ArduinoOcpp/Core/OcppOperationCallbacks.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef OCPP_OPERATION_CALLBACKS diff --git a/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp b/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp index 1a6a37a4..f314d1ed 100644 --- a/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp +++ b/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Core/OcppOperationTimeout.h b/src/ArduinoOcpp/Core/OcppOperationTimeout.h index 4fe6cd05..391b665d 100644 --- a/src/ArduinoOcpp/Core/OcppOperationTimeout.h +++ b/src/ArduinoOcpp/Core/OcppOperationTimeout.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef OCPPOPERATIONTIMEOUT_H diff --git a/src/ArduinoOcpp/Core/OcppServer.cpp b/src/ArduinoOcpp/Core/OcppServer.cpp index c47d5d6e..b7b2c166 100644 --- a/src/ArduinoOcpp/Core/OcppServer.cpp +++ b/src/ArduinoOcpp/Core/OcppServer.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Core/OcppTime.cpp b/src/ArduinoOcpp/Core/OcppTime.cpp index 4859b3b8..2e4397be 100644 --- a/src/ArduinoOcpp/Core/OcppTime.cpp +++ b/src/ArduinoOcpp/Core/OcppTime.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Core/OcppTime.h b/src/ArduinoOcpp/Core/OcppTime.h index aef6202a..3f2624e0 100644 --- a/src/ArduinoOcpp/Core/OcppTime.h +++ b/src/ArduinoOcpp/Core/OcppTime.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef OCPPTIME_H diff --git a/src/ArduinoOcpp/MessagesV16/Authorize.cpp b/src/ArduinoOcpp/MessagesV16/Authorize.cpp index 971adba8..2f7a3798 100644 --- a/src/ArduinoOcpp/MessagesV16/Authorize.cpp +++ b/src/ArduinoOcpp/MessagesV16/Authorize.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/Authorize.h b/src/ArduinoOcpp/MessagesV16/Authorize.h index cdc14637..3edb1709 100644 --- a/src/ArduinoOcpp/MessagesV16/Authorize.h +++ b/src/ArduinoOcpp/MessagesV16/Authorize.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef AUTHORIZE_H diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp index b475a479..ceb4c1ac 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.h b/src/ArduinoOcpp/MessagesV16/BootNotification.h index ed2a12e6..4cb87799 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.h +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef BOOTNOTIFICATION_H diff --git a/src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp b/src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp index 27c9780e..0d3687ec 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp +++ b/src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/ChangeAvailability.h b/src/ArduinoOcpp/MessagesV16/ChangeAvailability.h index 34f1c3e6..c8f088c7 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeAvailability.h +++ b/src/ArduinoOcpp/MessagesV16/ChangeAvailability.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef CHANGEAVAILABILITY_H diff --git a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp index e0173db7..ec2754bf 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h index 9997098e..944e5630 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h +++ b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef CHANGECONFIGURATION_H diff --git a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp index 35a894f7..89faeb50 100644 --- a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp +++ b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.h b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.h index 988db366..43c14c54 100644 --- a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.h +++ b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef CLEARCHARGINGPROFILE_H diff --git a/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp b/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp index 654303d2..2e064738 100644 --- a/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp +++ b/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/DataTransfer.h b/src/ArduinoOcpp/MessagesV16/DataTransfer.h index 68aafe5c..9ed141fd 100644 --- a/src/ArduinoOcpp/MessagesV16/DataTransfer.h +++ b/src/ArduinoOcpp/MessagesV16/DataTransfer.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef DATATRANSFER_H diff --git a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp index 1f83d5a8..590ca538 100644 --- a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h index 356efff2..75d04681 100644 --- a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h +++ b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp index 9ace381f..5737b790 100644 --- a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h index 16f4add0..7f2a882f 100644 --- a/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h +++ b/src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp index f6db00aa..fea58b3a 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.h b/src/ArduinoOcpp/MessagesV16/GetConfiguration.h index 509a4baf..ec2c0187 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.h +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef GETCONFIGURATION_H diff --git a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp index 74fe3ce1..a2509384 100644 --- a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h index 8c7cd03c..5225a5b7 100644 --- a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h +++ b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef GETDIAGNOSTICS_H diff --git a/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp b/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp index 7b6abeb0..fbf0e20e 100644 --- a/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp +++ b/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/Heartbeat.h b/src/ArduinoOcpp/MessagesV16/Heartbeat.h index 77a8cba1..7d0b3d60 100644 --- a/src/ArduinoOcpp/MessagesV16/Heartbeat.h +++ b/src/ArduinoOcpp/MessagesV16/Heartbeat.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef HEARTBEAT_H diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp index 902d501c..cb09d97c 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.h b/src/ArduinoOcpp/MessagesV16/MeterValues.h index ec0ddcfa..35160909 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.h +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef METERVALUES_H diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp index 65d4b440..fda65b89 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h index 6e59a047..5a470292 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef REMOTESTARTTRANSACTION_H diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp index 53befcf1..0dac0018 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h index eff1469c..bbb05dc0 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef REMOTESTOPTRANSACTION_H diff --git a/src/ArduinoOcpp/MessagesV16/Reset.cpp b/src/ArduinoOcpp/MessagesV16/Reset.cpp index 415fdea9..d380db52 100644 --- a/src/ArduinoOcpp/MessagesV16/Reset.cpp +++ b/src/ArduinoOcpp/MessagesV16/Reset.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/Reset.h b/src/ArduinoOcpp/MessagesV16/Reset.h index 32addfa7..2405e028 100644 --- a/src/ArduinoOcpp/MessagesV16/Reset.h +++ b/src/ArduinoOcpp/MessagesV16/Reset.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef RESET_H diff --git a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp index b5c5e54f..9a8678e2 100644 --- a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp +++ b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h index 6e99e69a..1ab388e7 100644 --- a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h +++ b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef SETCHARGINGPROFILE_H diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp index 641efaf5..719f0cf5 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.h b/src/ArduinoOcpp/MessagesV16/StartTransaction.h index e29488a2..eb9e582c 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef STARTTRANSACTION_H diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp index 1f409f8e..3c6a2e22 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.h b/src/ArduinoOcpp/MessagesV16/StatusNotification.h index bccd1458..fb226a8c 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.h +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef STATUSNOTIFICATION_H diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp index 04c991a9..f2132a58 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.h b/src/ArduinoOcpp/MessagesV16/StopTransaction.h index 2a9777e3..ed556591 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef STOPTRANSACTION_H diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp index 245182b4..da8404c0 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h index 19e408db..7d9cacbd 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef TRIGGERMESSAGE_H diff --git a/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp b/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp index 51e29f6b..17006456 100644 --- a/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp +++ b/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/UnlockConnector.h b/src/ArduinoOcpp/MessagesV16/UnlockConnector.h index ca1ef349..f07da4d7 100644 --- a/src/ArduinoOcpp/MessagesV16/UnlockConnector.h +++ b/src/ArduinoOcpp/MessagesV16/UnlockConnector.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef UNLOCKCONNECTOR_H diff --git a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp index 1d962d94..bc5580a0 100644 --- a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp +++ b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h index 73c8c30e..82dcd98a 100644 --- a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h +++ b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef UPDATEFIRMWARE_H diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index fd7d8b24..097e52ab 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.h b/src/ArduinoOcpp/SimpleOcppOperationFactory.h index 5ce5010d..c31bb115 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.h +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef SIMPLEOCPPOPERATIONFACTORY_H diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index 6a6bb332..e1a279fb 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h index bb7e31a1..72ccfea8 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef CHARGEPOINTSTATUSSERVICE_H diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 6fa48136..b4d2f36b 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index 3f9527d9..a411001a 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef CONNECTOR_STATUS diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h index 538b00d3..00a49152 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef OCPP_EVSE_STATE diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp index f7f0e39b..9e6a5e65 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h index 0e0b750d..23d5c1b2 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef DIAGNOSTICSSERVICE_H diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsStatus.h b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsStatus.h index f3500e67..f9e7188a 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsStatus.h +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsStatus.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef DIAGNOSTICS_STATUS diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index 28798ca1..07d51958 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h index 1f96df77..ed16a18d 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef FIRMWARESERVICE_H diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareStatus.h b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareStatus.h index fe3afa32..e6157654 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareStatus.h +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareStatus.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef FIRMWARE_STATUS diff --git a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp index 1e2acc6e..89c3a71b 100644 --- a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp +++ b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h index 32fcde46..e6f8afe0 100644 --- a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h +++ b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef HEARTBEATSERVICE_H diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index b1289d30..2b526d8b 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h index 0152694f..af90dc74 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef CONNECTOR_METER_VALUES_RECORDER diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp index 828ffdee..bb023804 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h index 9318c6dd..00a8d2d4 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef METERINGSERVICE_H diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp index 266f593e..3d887344 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h index 0b11ef52..7d3fc139 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef SMARTCHARGINGMODEL_H diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index a88e1066..3ab2de35 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h index a37c2ad3..d26506ee 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef SMARTCHARGINGSERVICE_H diff --git a/src/Variants.h b/src/Variants.h index cf483a51..17d4cc88 100644 --- a/src/Variants.h +++ b/src/Variants.h @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #ifndef VARIANTS_H From 6e130e46021e8f2cd64c2f9b5e427b6c94f00d15 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 20 Jan 2022 12:24:36 +0100 Subject: [PATCH 097/696] remove facade selector --- src/ArduinoOcpp.cpp | 4 ---- src/ArduinoOcpp.h | 3 --- src/Variants.h | 4 ---- 3 files changed, 11 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 5fac0edf..1b1dcd82 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -6,8 +6,6 @@ #include "Variants.h" -#if USE_FACADE - #include #include #include @@ -493,5 +491,3 @@ bool isAvailable() { return (chargePoint->getAvailability() != AVAILABILITY_INOPERATIVE) && (connector->getAvailability() != AVAILABILITY_INOPERATIVE); } - -#endif diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index a649e606..23f47ed3 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -25,8 +25,6 @@ using ArduinoOcpp::OnReceiveErrorListener; using ArduinoOcpp::Timeout; -#if USE_FACADE - #ifndef AO_CUSTOM_WS //uses links2004/WebSockets library void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, float V_eff = 230.f /*German grid*/, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, ArduinoOcpp::OcppClock system_time = ArduinoOcpp::Clocks::DEFAULT_CLOCK); @@ -146,4 +144,3 @@ bool existsUnboundIdTag(); //returns if the user has given a valid Ocpp Charging bool isAvailable(); //if the charge point is operative or inoperative #endif -#endif diff --git a/src/Variants.h b/src/Variants.h index 17d4cc88..cb17375b 100644 --- a/src/Variants.h +++ b/src/Variants.h @@ -21,8 +21,4 @@ #endif #endif -#ifndef USE_FACADE -#define USE_FACADE true -#endif - #endif From 287218e569f51e61af177b1dc18efc94f326e46f Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 20 Jan 2022 12:57:00 +0100 Subject: [PATCH 098/696] custom fw and diag option via facade --- src/ArduinoOcpp.cpp | 14 ++++++++++++++ src/ArduinoOcpp.h | 29 +++++++++++++++++------------ 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 1b1dcd82..a2719945 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -491,3 +491,17 @@ bool isAvailable() { return (chargePoint->getAvailability() != AVAILABILITY_INOPERATIVE) && (connector->getAvailability() != AVAILABILITY_INOPERATIVE); } + +#if defined(AO_CUSTOM_UPDATER) || defined(AO_CUSTOM_WEBSOCKET) +FirmwareService *getFirmwareService() { + auto& model = ocppEngine->getOcppModel(); + return model.getFirmwareService(); +} +#endif + +#if defined(AO_CUSTOM_DIAGNOSTICS) || defined(AO_CUSTOM_WEBSOCKET) +DiagnosticsService *getDiagnosticsService() { + auto& model = ocppEngine->getOcppModel(); + return model.getDiagnosticsService(); +} +#endif diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 23f47ed3..b166d97b 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -121,26 +121,31 @@ void startTransaction(OnReceiveConfListener onConf = nullptr, OnAbortListener on void stopTransaction(OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); /* - * Provide hardware-related information II - * - * When the EVSE state changes, you must notify the library in order to make StatusNotification - * work properly. - * - * Call these functions in your integration. + * Access information about the internal state of the library */ -//void startEvDrawsEnergy(); //<-- please use setEvRequestsEnergySampler(bool evRequestsEnergy()); +int getTransactionId(); //returns the ID of the current transaction. Returns -1 if called before or after an transaction -//void stopEvDrawsEnergy(); //<-- please use setEvRequestsEnergySampler(bool evRequestsEnergy()); +bool existsUnboundIdTag(); //returns if the user has given a valid Ocpp Charging Card which is not used for a transaction yet + +bool isAvailable(); //if the charge point is operative or inoperative /* - * Access information about the internal state of the library + * Configure the device management */ -int getTransactionId(); //returns the ID of the current transaction. Returns -1 if called before or after an transaction +#if defined(AO_CUSTOM_UPDATER) || defined(AO_CUSTOM_WEBSOCKET) +#include +using ArduinoOcpp::FirmwareService; -bool existsUnboundIdTag(); //returns if the user has given a valid Ocpp Charging Card which is not used for a transaction yet +FirmwareService *getFirmwareService(); +#endif -bool isAvailable(); //if the charge point is operative or inoperative +#if defined(AO_CUSTOM_DIAGNOSTICS) || defined(AO_CUSTOM_WEBSOCKET) +#include +using ArduinoOcpp::DiagnosticsService; + +DiagnosticsService *getDiagnosticsService(); +#endif #endif From 4a623e0f1e622badebaccfd65aa30da734a95819 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 21 Jan 2022 17:39:05 +0100 Subject: [PATCH 099/696] build flags fix, confusing debug msg fix --- src/ArduinoOcpp.cpp | 12 +++++------ src/ArduinoOcpp.h | 20 +++++++++++-------- src/ArduinoOcpp/Core/Configuration.cpp | 8 +++++--- .../Core/ConfigurationContainerFlash.cpp | 9 ++++++++- .../Tasks/Diagnostics/DiagnosticsService.cpp | 4 ++-- .../Tasks/Diagnostics/DiagnosticsService.h | 4 ++-- .../FirmwareManagement/FirmwareService.cpp | 4 ++-- .../FirmwareManagement/FirmwareService.h | 4 ++-- .../SmartCharging/SmartChargingService.cpp | 8 ++++++-- 9 files changed, 45 insertions(+), 28 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index a2719945..2a966584 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -100,7 +100,7 @@ void OCPP_initialize(OcppSocket& ocppSocket, float V_eff, ArduinoOcpp::Filesyste model.setHeartbeatService(std::unique_ptr( new HeartbeatService(*ocppEngine))); -#if !defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) +#if !defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WS) model.setFirmwareService(std::unique_ptr( EspWiFi::makeFirmwareService(*ocppEngine, "1234578901"))); //instantiate FW service + ESP installation routine #else @@ -108,7 +108,7 @@ void OCPP_initialize(OcppSocket& ocppSocket, float V_eff, ArduinoOcpp::Filesyste new FirmwareService(*ocppEngine))); //only instantiate FW service #endif -#if !defined(AO_CUSTOM_DIAGNOSTICS) && !defined(AO_CUSTOM_WEBSOCKET) +#if !defined(AO_CUSTOM_DIAGNOSTICS) && !defined(AO_CUSTOM_WS) model.setDiagnosticsService(std::unique_ptr( EspWiFi::makeDiagnosticsService(*ocppEngine))); //will only return "Rejected" because logging is not implemented yet #else @@ -492,15 +492,15 @@ bool isAvailable() { && (connector->getAvailability() != AVAILABILITY_INOPERATIVE); } -#if defined(AO_CUSTOM_UPDATER) || defined(AO_CUSTOM_WEBSOCKET) -FirmwareService *getFirmwareService() { +#if defined(AO_CUSTOM_UPDATER) || defined(AO_CUSTOM_WS) +ArduinoOcpp::FirmwareService *getFirmwareService() { auto& model = ocppEngine->getOcppModel(); return model.getFirmwareService(); } #endif -#if defined(AO_CUSTOM_DIAGNOSTICS) || defined(AO_CUSTOM_WEBSOCKET) -DiagnosticsService *getDiagnosticsService() { +#if defined(AO_CUSTOM_DIAGNOSTICS) || defined(AO_CUSTOM_WS) +ArduinoOcpp::DiagnosticsService *getDiagnosticsService() { auto& model = ocppEngine->getOcppModel(); return model.getDiagnosticsService(); } diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index b166d97b..9f0ca249 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -134,18 +134,22 @@ bool isAvailable(); //if the charge point is operative or inoperative * Configure the device management */ -#if defined(AO_CUSTOM_UPDATER) || defined(AO_CUSTOM_WEBSOCKET) +#if defined(AO_CUSTOM_UPDATER) || defined(AO_CUSTOM_WS) #include -using ArduinoOcpp::FirmwareService; - -FirmwareService *getFirmwareService(); +// You need to configure this object if FW updates are relevant for you. This project already +// brings a simple configuration for the ESP32 and ESP8266 for prototyping purposes, however +// for the productive system you will have to develop a configuration targeting the specific +// OCPP backend. +// See ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h +ArduinoOcpp::FirmwareService *getFirmwareService(); #endif -#if defined(AO_CUSTOM_DIAGNOSTICS) || defined(AO_CUSTOM_WEBSOCKET) +#if defined(AO_CUSTOM_DIAGNOSTICS) || defined(AO_CUSTOM_WS) #include -using ArduinoOcpp::DiagnosticsService; - -DiagnosticsService *getDiagnosticsService(); +// This library implements the OCPP messaging side of Diagnostics, but no logging or the +// log upload to your backend. +// To integrate Diagnostics, see ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h +ArduinoOcpp::DiagnosticsService *getDiagnosticsService(); #endif #endif diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index ce091fc8..e2bb5e1a 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -258,9 +258,11 @@ bool configuration_init(FilesystemOpt fsOpt) { } if (containerDefault) { - Serial.print(F("[Configuration] Found default container before calling configuration_init(). If you added\n" \ - " the container manually, please ensure to call load(). If not, it is a hint\n" \ - " that declareConfiguration() was called too early\n")); + if (DEBUG_OUT) { + Serial.print(F("[Configuration] Found default container before calling configuration_init(). If you added\n" \ + " the container manually, please ensure to call load(). If not, it is a hint\n" \ + " that declareConfiguration() was called too early\n")); + } } else { containerDefault = createConfigurationContainer(CONFIGURATION_FN); if (!containerDefault->load()) { diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp index 358a2fbb..96b2a6ac 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp @@ -32,6 +32,11 @@ bool ConfigurationContainerFlash::load() { " All previously declared values won't be written back\n")); } + if (!USE_FS.exists(getFilename())) { + if (DEBUG_OUT) Serial.print(F("[Configuration] Populate FS: create configuration file\n")); + return true; + } + File file = USE_FS.open(getFilename(), "r"); if (!file) { @@ -151,7 +156,9 @@ bool ConfigurationContainerFlash::save() { return true; //nothing to be done } - USE_FS.remove(getFilename()); + if (USE_FS.exists(getFilename())) { + USE_FS.remove(getFilename()); + } File file = USE_FS.open(getFilename(), "w"); diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp index 9e6a5e65..894810a4 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp @@ -161,7 +161,7 @@ void DiagnosticsService::setOnUploadStatusSampler(std::function this->uploadStatusSampler = uploadStatusSampler; } -#if !defined(AO_CUSTOM_DIAGNOSTICS) && !defined(AO_CUSTOM_WEBSOCKET) +#if !defined(AO_CUSTOM_DIAGNOSTICS) && !defined(AO_CUSTOM_WS) #if defined(ESP32) || defined(ESP8266) DiagnosticsService *EspWiFi::makeDiagnosticsService(OcppEngine& context) { @@ -175,4 +175,4 @@ DiagnosticsService *EspWiFi::makeDiagnosticsService(OcppEngine& context) { } #endif //defined(ESP32) || defined(ESP8266) -#endif //!defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) +#endif //!defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WS) diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h index 23d5c1b2..e1ba7a51 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h @@ -59,7 +59,7 @@ class DiagnosticsService { void setOnUploadStatusSampler(std::function uploadStatusSampler); }; -#if !defined(AO_CUSTOM_DIAGNOSTICS) && !defined(AO_CUSTOM_WEBSOCKET) +#if !defined(AO_CUSTOM_DIAGNOSTICS) && !defined(AO_CUSTOM_WS) #if defined(ESP32) || defined(ESP8266) namespace EspWiFi { @@ -69,7 +69,7 @@ DiagnosticsService *makeDiagnosticsService(OcppEngine& context); } #endif //defined(ESP32) || defined(ESP8266) -#endif //!defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) +#endif //!defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WS) } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index 07d51958..87e86184 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -292,7 +292,7 @@ void FirmwareService::resetStage() { installationIssued = false; } -#if !defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) +#if !defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WS) #if defined(ESP32) #include @@ -402,4 +402,4 @@ FirmwareService *EspWiFi::makeFirmwareService(OcppEngine& context, const char *b } #endif //defined(ESP8266) -#endif //!defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) +#endif //!defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WS) diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h index ed16a18d..14bda9a5 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h @@ -94,7 +94,7 @@ class FirmwareService { } //endif namespace ArduinoOcpp -#if !defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) +#if !defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WS) #if defined(ESP32) || defined(ESP8266) namespace ArduinoOcpp { @@ -106,6 +106,6 @@ FirmwareService *makeFirmwareService(OcppEngine& context, const char *buildNumbe } #endif //defined(ESP32) || defined(ESP8266) -#endif //!defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WEBSOCKET) +#endif //!defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WS) #endif diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index 3ab2de35..a4e8543f 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -323,7 +323,9 @@ bool SmartChargingService::clearChargingProfile(const std::functiongetStackLevel(); profileFN += PROFILE_FN_SUFFIX; - USE_FS.remove(profileFN); + if (USE_FS.exists(profileFN)) { + USE_FS.remove(profileFN); + } } else { if (DEBUG_OUT) Serial.println(F("[SmartChargingService] Prohibit access to FS")); } @@ -367,7 +369,9 @@ bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile profileFN += chargingProfile->getStackLevel(); profileFN += PROFILE_FN_SUFFIX; - USE_FS.remove(profileFN); + if (USE_FS.exists(profileFN)) { + USE_FS.remove(profileFN); + } File file = USE_FS.open(profileFN, "w"); From edb7c1eeb759991e65c52d3fe8bf98d5e8a9c1c3 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 28 Jan 2022 14:39:41 +0100 Subject: [PATCH 100/696] compatibility fixes --- examples/ESP/main.cpp | 6 +- .../OneConnector-EVSE/OneConnector_EVSE.cpp | 93 --------- .../OneConnector_HW_integration.cpp | 192 ------------------ .../OneConnector_HW_integration.h | 33 --- examples/OneConnector-EVSE/Readme.md | 3 - examples/SECC/README.md | 2 +- examples/SECC/main.cpp | 7 +- examples/SECC/platformio.ini | 21 ++ library.json | 6 +- platformio.ini | 5 - .../MessagesV16/BootNotification.cpp | 2 +- 11 files changed, 33 insertions(+), 337 deletions(-) delete mode 100644 examples/OneConnector-EVSE/OneConnector_EVSE.cpp delete mode 100644 examples/OneConnector-EVSE/OneConnector_HW_integration.cpp delete mode 100644 examples/OneConnector-EVSE/OneConnector_HW_integration.h delete mode 100644 examples/OneConnector-EVSE/Readme.md create mode 100644 examples/SECC/platformio.ini diff --git a/examples/ESP/main.cpp b/examples/ESP/main.cpp index af2f56b6..5f2a25d3 100644 --- a/examples/ESP/main.cpp +++ b/examples/ESP/main.cpp @@ -1,5 +1,5 @@ // matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2021 +// Copyright Matthias Akstaller 2019 - 2022 // MIT License #include @@ -18,9 +18,9 @@ ESP8266WiFiMulti WiFiMulti; #define STASSID "YOUR_WLAN_SSID" #define STAPSK "YOUR_WLAN_PW" -#define OCPP_HOST "wss://echo.websocket.org" +#define OCPP_HOST "echo.websocket.events" #define OCPP_PORT 80 -#define OCPP_URL "wss://echo.websocket.org/" +#define OCPP_URL "ws://echo.websocket.events/" // //// Settings which worked for my SteVe instance diff --git a/examples/OneConnector-EVSE/OneConnector_EVSE.cpp b/examples/OneConnector-EVSE/OneConnector_EVSE.cpp deleted file mode 100644 index 3743b730..00000000 --- a/examples/OneConnector-EVSE/OneConnector_EVSE.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2022 -// MIT License - -#include - -#include - -#include - -#include -#include - -//ArduinoOcpp modules -#include - -//GPIO-based HW integration (used as example) -#include "OneConnector_HW_integration.h" - -ESP8266WiFiMulti WiFiMulti; - -#define STASSID "YOUR_WLAN_SSID" -#define STAPSK "YOUR_WLAN_PW" - -// WebSocket echo server test - -#define OCPP_HOST "wss://echo.websocket.org" -#define OCPP_PORT 80 -#define OCPP_URL "wss://echo.websocket.org/" - -// -//// Settings which worked for my SteVe instance -// -//#define OCPP_HOST "my.instance.com" -//#define OCPP_PORT 80 -//#define OCPP_URL "ws://my.instance.com/steve/websocket/CentralSystemService/gpio-based-charger" - -#define LED 2 -#define LED_ON LOW -#define LED_OFF HIGH - -void setup() { - - Serial.begin(115200); - Serial.setDebugOutput(true); - - pinMode(LED, OUTPUT); - - Serial.println(); - Serial.println(); - Serial.println(); - - for (uint8_t t = 4; t > 0; t--) { - Serial.printf("[SETUP] BOOT WAIT %d...\n", t); - Serial.flush(); - digitalWrite(LED, LED_ON); - delay(100); - digitalWrite(LED, LED_OFF); - delay(300); - digitalWrite(LED, LED_ON); - delay(300); - digitalWrite(LED, LED_OFF); - delay(300); - } - - WiFiMulti.addAP(STASSID, STAPSK); - - while (WiFiMulti.run() != WL_CONNECTED) { - delay(100); - } - - OCPP_initialize(OCPP_HOST, OCPP_PORT, OCPP_URL); - - setPowerActiveImportSampler([]() { - return EVSE_readChargeRate(); //from the GPIO-charger example - }); - - setOnChargingRateLimitChange([](float limit) { - EVSE_setChargingLimit(limit); //from the GPIO-charger example - }); - - setEvRequestsEnergySampler([]() { - return EVSE_evRequestsEnergy(); - }); - - EVSE_initialize(); -} - -void loop() { - OCPP_loop(); - - EVSE_loop(); //refers to the GPIO-charger example -} diff --git a/examples/OneConnector-EVSE/OneConnector_HW_integration.cpp b/examples/OneConnector-EVSE/OneConnector_HW_integration.cpp deleted file mode 100644 index e1b318a3..00000000 --- a/examples/OneConnector-EVSE/OneConnector_HW_integration.cpp +++ /dev/null @@ -1,192 +0,0 @@ -// matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2022 -// MIT License - -#include - -#include "OneConnector_HW_integration.h" - -#include - -#include - -/* - * Demonstration video: https://youtu.be/hfTh9GjG-N4 - * - * The simple EVSE uses GPIO for the communication between the ESP8266 and the peripherals. The sketch was - * written and tested on the NodeMCU v3. The following pin-mapping shows how the NodeMCU was connected - * to its peripherals. Feel free to adapt it according to your own setup. - * - * Peripheral | IN/OUT | Pin name | GPIO No | Meaning of HIGH / LOW - * ----------------------------------------------------------------------- - * EV_PLUG INPUT D5 14 HIGH: EV is plugged - * AUTHORIZATION INPUT D6 12 HIGH: Charging session is authorized - * EV_ENERGY_REQUEST INPUT D7 13 HIGH: The EV is ready to charge and requests the EVSE to offer energy - * EVSE_ENERGY_OFFER OUTPUT D1 5 HIGH: The relay is closed, the charging wire supplies energy - */ - -#define EV_PLUG 14 -#define EV_PLUGGED HIGH -#define AUTHORIZATION 12 -#define AUTHORIZATION_PROVIDED HIGH -#define EV_ENERGY_REQUEST 13 -#define EV_REQUESTS_ENERGY HIGH - -#define EVSE_RELAY 5 -#define EVSE_OFFERS_ENERGY HIGH -#define EVSE_DENIES_ENERGY LOW - -void onEvPlug(); -void onEvUnplug(); -void onProvideAuthorization(); -void onRevokeAuthorization(); - -bool evIsPlugged = false; -bool transactionRunning = false; -bool authorizationProvided = false; -bool successfullyAuthorized = false; -bool evRequestsEnergy = false; -bool evseIsBooted = false; - - -float chargingLimit = 3680.f; // = 230V * 16A - -void EVSE_initialize() { - Serial.print(F("[EVSE] Simple_EVSE is being initialized ... ")); - pinMode(EVSE_RELAY, OUTPUT); - pinMode(EV_PLUG, INPUT); - pinMode(AUTHORIZATION, INPUT); - pinMode(EV_ENERGY_REQUEST, INPUT); - - bootNotification("GPIO-based CP model", "Greatest EVSE vendor", [] (JsonObject confMsg) { - //This callback is executed when the .conf() response from the central system arrives - Serial.print(F("BootNotification was answered. Central System clock: ")); - Serial.println(confMsg["currentTime"].as()); - - evseIsBooted = true; - }); - - Serial.print(F("ready. Wait for BootNotification.conf(), then start\n")); -} - -void EVSE_loop() { - if (!evseIsBooted) return; - - if (digitalRead(EV_PLUG) == EV_PLUGGED) { - if (!evIsPlugged) { - evIsPlugged = true; - onEvPlug(); - } - } else { - if (evIsPlugged) { - evIsPlugged = false; - onEvUnplug(); - } - } - - if (digitalRead(AUTHORIZATION) == AUTHORIZATION_PROVIDED) { - if (!authorizationProvided) { - authorizationProvided = true; - onProvideAuthorization(); - } - } else { - if (authorizationProvided) { - authorizationProvided = false; - onRevokeAuthorization(); - } - } - - bool previousState = evRequestsEnergy; - - evRequestsEnergy = digitalRead(EV_ENERGY_REQUEST) == EV_REQUESTS_ENERGY; - - if (previousState != evRequestsEnergy && transactionRunning) { - if (evRequestsEnergy) - Serial.print(F("[EVSE] EV requests energy. Send Status Notification\n")); - else - Serial.print(F("[EVSE] EV does not request energy anymore. Send Status Notification\n")); - } - -} - -void onEvPlug() { - Serial.print(F("[EVSE] EV plugged\n")); - if (successfullyAuthorized) { - startTransaction([](JsonObject conf) { - transactionRunning = true; - digitalWrite(EVSE_RELAY, EVSE_OFFERS_ENERGY); - }); - } -} - -void onEvUnplug() { - Serial.print(F("[EVSE] EV unplugged\n")); - if (transactionRunning) { - stopTransaction(); - } - digitalWrite(EVSE_RELAY, EVSE_DENIES_ENERGY); - transactionRunning = false; - successfullyAuthorized = false; -} - -void onProvideAuthorization() { - Serial.print(F("[EVSE] User authorized charging session\n")); - String idTag = String("abcdef123456789"); // e.g. idTag = readRFIDTag(); - - /* - * Demonstrate the use of all event listeners that come with sending OCPP operations. At the beginning, you probably only need - * the first listener which is called when the Central System has responded with a .conf() msg. - */ - authorize(idTag, [](JsonObject conf) { //onReceiveConfListener - //execute when the .conf() response from the central system arrives (optional but very likely necessary for your integration) - successfullyAuthorized = true; - - if (evIsPlugged) { - startTransaction([](JsonObject conf) { - transactionRunning = true; - digitalWrite(EVSE_RELAY, EVSE_OFFERS_ENERGY); - }); - } - }, []() { //onAbortListener (optional) - //execute when the OCPP engine stops running this operation for any reason - Serial.print(F("[EVSE] Could not authorize charging session! Aborted\n")); - }, []() { //onTimeoutListener (optional) - //execute when the operation is not answered until the timeout expires (e.g. because the Wi-Fi network lost internet connection) - Serial.print(F("[EVSE] Could not authorize charging session! Reason: timeout\n")); - }, [](const char *code, const char *description, JsonObject details) { //onReceiveErrorListener (optional) - //execute when the Central System returns a CallError - Serial.print(F("[EVSE] Could not authorize charging session! Reason: received OCPP error: ")); - Serial.println(code); - }); -} - -void onRevokeAuthorization() { - Serial.print(F("[EVSE] User revoked charging session authorization\n")); - successfullyAuthorized = false; -} - -float EVSE_readChargeRate() { //estimation for EVSEs without power meter - if (evIsPlugged && evRequestsEnergy) { //example - return chargingLimit; - } else { - return 0.0f; - } -} - -void EVSE_setChargingLimit(float limit) { - Serial.print(F("[EVSE] New charging limit set. Got ")); - Serial.println(limit); - chargingLimit = limit; - - if (chargingLimit <= 0.f) { - digitalWrite(EVSE_RELAY, EVSE_DENIES_ENERGY); - } else { - if (transactionRunning) { - digitalWrite(EVSE_RELAY, EVSE_OFFERS_ENERGY); - } - } -} - -bool EVSE_evRequestsEnergy() { - return evRequestsEnergy; -} diff --git a/examples/OneConnector-EVSE/OneConnector_HW_integration.h b/examples/OneConnector-EVSE/OneConnector_HW_integration.h deleted file mode 100644 index 3bcd5519..00000000 --- a/examples/OneConnector-EVSE/OneConnector_HW_integration.h +++ /dev/null @@ -1,33 +0,0 @@ -// matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2022 -// MIT License - -/* - * Demonstration video: https://youtu.be/hfTh9GjG-N4 - * - * The simple EVSE uses GPIO for the communication between the ESP8266 and the peripherals. The sketch was - * written and tested on the NodeMCU v3. The following pin-mapping shows how the NodeMCU was connected - * to its peripherals. Feel free to adapt it according to your own setup. - * - * Peripheral | IN/OUT | Pin name | GPIO No | Meaning of HIGH / LOW - * ----------------------------------------------------------------------- - * EV_PLUG INPUT D5 14 HIGH: EV is plugged - * AUTHORIZATION INPUT D6 12 HIGH: Charging session is authorized - * EV_ENERGY_REQUEST INPUT D7 13 HIGH: The EV is ready to charge and requests the EVSE to offer energy - * EVSE_ENERGY_OFFER OUTPUT D1 5 HIGH: The relay is closed, the charging wire supplies energy - */ - -#ifndef ONE_CONNECTOR_HW_INTEGRATION -#define ONE_CONNECTOR_HW_INTEGRATION - -void EVSE_initialize(); - -void EVSE_loop(); - -float EVSE_readChargeRate(); - -void EVSE_setChargingLimit(float limit); - -bool EVSE_evRequestsEnergy(); - -#endif diff --git a/examples/OneConnector-EVSE/Readme.md b/examples/OneConnector-EVSE/Readme.md deleted file mode 100644 index 8c069a41..00000000 --- a/examples/OneConnector-EVSE/Readme.md +++ /dev/null @@ -1,3 +0,0 @@ -#### How to run this example - -You can find a description of how this example works in the source files. To compile this example, you need to copy all three files into your project folder. diff --git a/examples/SECC/README.md b/examples/SECC/README.md index a6ba7315..4d9eeaa5 100644 --- a/examples/SECC/README.md +++ b/examples/SECC/README.md @@ -11,7 +11,7 @@ You can find the interface descriptions in `main.ino`. Please be aware that alth ## Usage - Create an empty project in the PlatformIO IDE. -- Copy the `platformio.ini` from ArduinoOcpp's root directory to your source directory. Add `tzapu/WiFiManager@0.16.0` to the libdeps of the `platformio.ini`. +- Copy the `platformio.ini` from this directory to your root directory. Alternatively, just add `https://github.com/tzapu/WiFiManager.git` to the lib_deps of your `platformio.ini`. - Copy the `main.ino` from this example folder into your source directory. Adapt the pinout settings if needed. - Finished. You should be able to compile and upload the sketch onto your ESP. diff --git a/examples/SECC/main.cpp b/examples/SECC/main.cpp index 70b9ff04..b6245f0f 100644 --- a/examples/SECC/main.cpp +++ b/examples/SECC/main.cpp @@ -3,7 +3,7 @@ // MIT License // // -// IMPORTANT: Please add tzapu/WiFiManager@0.16.0 to the libdeps of your project! +// IMPORTANT: Please add https://github.com/tzapu/WiFiManager.git to the lib_deps of your project! // // This sketch demonstrates a complete OCPP Wi-Fi module of an EVSE. You can flash // an ESP8266/ESP32 with this program, build it into your EVSE and start charging with @@ -15,7 +15,7 @@ #include -#include //please install https://github.com/tzapu/WiFiManager.git +#include //please add to lib_deps: https://github.com/tzapu/WiFiManager.git #include #include //load and save settings of WiFi captive portal @@ -77,6 +77,9 @@ #if DEBUG_OUT #define PRINT(...) Serial.print(__VA_ARGS__) #define PRINTLN(...) Serial.println(__VA_ARGS__) +#else +#define PRINT(...) +#define PRINTLN(...) #endif WebSocketsClient wSock; diff --git a/examples/SECC/platformio.ini b/examples/SECC/platformio.ini new file mode 100644 index 00000000..3c7a94b0 --- /dev/null +++ b/examples/SECC/platformio.ini @@ -0,0 +1,21 @@ +; matth-x/ArduinoOcpp +; Copyright Matthias Akstaller 2019 - 2022 +; MIT License + +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino +lib_deps = + https://github.com/matth-x/ArduinoOcpp.git + https://github.com/tzapu/WiFiManager.git +monitor_speed = 115200 + +[env:nodemcuv2] +platform = espressif8266@2.6.3 +board = nodemcuv2 +framework = arduino +lib_deps = + https://github.com/matth-x/ArduinoOcpp.git + https://github.com/tzapu/WiFiManager.git +monitor_speed = 115200 diff --git a/library.json b/library.json index 5231cdff..a715ca11 100644 --- a/library.json +++ b/library.json @@ -46,15 +46,13 @@ "examples/*", "platformio.ini", "library.json", - "LICENSE", - "min_spiffs.csv" + "LICENSE" ], "exclude": [ - "src/OneConnector*", - "src/Simulated_HW*", "src/sdkconfig*", "examples/SECC/WiFiManager*", + "examples/CompatibilityTest", "src/main.cpp" ] }, diff --git a/platformio.ini b/platformio.ini index cc2f6196..2f9baa58 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,11 +1,6 @@ ; matth-x/ArduinoOcpp ; Copyright Matthias Akstaller 2019 - 2022 ; MIT License -; -; PlatformIO Project Configuration File -; -; How to use the platformio.ini: -; https://docs.platformio.org/en/latest/projectconf/index.html [platformio] default_envs = esp32-development-board diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp index ceb4c1ac..0d341b15 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp @@ -118,7 +118,7 @@ std::unique_ptr BootNotification::createConf(){ JsonObject payload = doc->to(); //safety mechanism; in some test setups the library has to answer BootNotifications without valid system time - OcppTimestamp ocppTimeReference = OcppTimestamp(2019,10,0,11,59,55); + OcppTimestamp ocppTimeReference = OcppTimestamp(2022,0,27,11,59,55); OcppTimestamp ocppSelect = ocppTimeReference; if (ocppModel) { auto& ocppTime = ocppModel->getOcppTime(); From 313d65b73c5a7bfaff1d97b5ded9b667501806f0 Mon Sep 17 00:00:00 2001 From: Matthias Akstaller <63792403+matth-x@users.noreply.github.com> Date: Fri, 28 Jan 2022 15:04:27 +0100 Subject: [PATCH 101/696] update Usage and Dependencies section --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5b2339be..80e03591 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ For simple chargers, the application logic + HW integration is far below 1000 LO ## Usage guide -Please take `OneConnector_EVSE.ino` (in the `examples/OneConnector-EVSE/` folder) as starting point for you first project. It is an integration of a simple GPIO-based charger with one connector. See `OneConnector_HW_integration.ino` in which the charger functions are mapped onto the OCPP library to get a feeling for how to use this library in practice. In this guide, I give a brief overview of the key concepts. +Please take `examples/ESP/main.cpp` as the starting point for you first project. It is a minimal example which shows how to establish an OCPP connection and how to start and stop charging sessions. In this guide, I give a brief overview of the key concepts. - To get the library running, you have to install all dependencies (see the list below). @@ -123,8 +123,8 @@ To get started quickly with or without EVSE hardware, you can flash the sketch i ## Dependencies -- [bblanchon/ArduinoJSON](https://github.com/bblanchon/ArduinoJson) -- [Links2004/arduinoWebSockets](https://github.com/Links2004/arduinoWebSockets) +- [bblanchon/ArduinoJSON](https://github.com/bblanchon/ArduinoJson) (please upgrade to version `6.19.1`) +- [Links2004/arduinoWebSockets](https://github.com/Links2004/arduinoWebSockets) (please upgrade to version `2.3.6`) In case you use PlatformIO, you can copy all dependencies from `platformio.ini` into your own configuration file. Alternatively, you can install the full library with dependencies by adding `matth-x/ArduinoOcpp` in the PIO library manager. From 7fd0c10fc2acd4b62417436cb37fb8835b30fee9 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 28 Jan 2022 15:15:20 +0100 Subject: [PATCH 102/696] adapt build flags --- platformio.ini | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/platformio.ini b/platformio.ini index 2f9baa58..7600185e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,8 +10,6 @@ framework = arduino lib_deps = bblanchon/ArduinoJson@6.19.1 links2004/WebSockets@2.3.6 -build_flags = - -D USE_FACADE=true monitor_speed = 115200 [env:nodemcuv2] @@ -21,9 +19,8 @@ framework = ${common.framework} lib_deps = ${common.lib_deps} monitor_speed = ${common.monitor_speed} build_flags = - ${common.build_flags} - -DAO_DEBUG_OUT - -DAO_TRAFFIC_OUT + -DAO_DEBUG_OUT ; flood the serial monitor with information about the internal state + -DAO_TRAFFIC_OUT ; print the OCPP communication to the serial monitor [env:esp32-development-board] platform = espressif32@3.3.2 @@ -34,9 +31,8 @@ lib_deps = lorol/LittleFS_esp32@1.0.5 monitor_speed = ${common.monitor_speed} build_flags = - ${common.build_flags} - -DAO_DEBUG_OUT - -DAO_TRAFFIC_OUT + -DAO_DEBUG_OUT ; flood the serial monitor with information about the internal state + -DAO_TRAFFIC_OUT ; print the OCPP communication to the serial monitor -DCONFIG_LITTLEFS_FOR_IDF_3_2 board_build.partitions = min_spiffs.csv upload_speed = 921600 From 687ecbbeaf30cc4ef39108f7fa557431fd8cd086 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 28 Jan 2022 16:25:10 +0100 Subject: [PATCH 103/696] publish to PIO registry (v0.0.6) --- library.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/library.json b/library.json index a715ca11..cc40f14c 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "ArduinoOcpp", - "version": "0.0.5", + "version": "0.0.6", "description": "OCPP 1.6 Client for the ESP8266 and ESP32", "keywords": "OCPP, 1.6, OCPP 1.6, Smart Energy, Smart Charging, client, ESP8266, ESP32, Arduino, EVSE, Charge Point", "repository": @@ -46,6 +46,7 @@ "examples/*", "platformio.ini", "library.json", + "README.md", "LICENSE" ], "exclude": @@ -59,17 +60,18 @@ "examples": [ { - "name": "ESP32 / ESP8266 OCPP connection", + "name": "Basic OCPP connection", "base": "examples/ESP", "files": [ - "main.ino" + "main.cpp" ] }, { "name": "ESP GPIO-based Supply Equipment Communications Controller SECC", "base": "examples/SECC", "files": [ - "main.ino", + "main.cpp", + "platformio.ini", "README.md" ] } From e742bc760950731309c9c9ce1e4391a69ac2da82 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 28 Jan 2022 16:45:20 +0100 Subject: [PATCH 104/696] update Readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 80e03591..bbdeb48a 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ For simple chargers, the application logic + HW integration is far below 1000 LO ## Usage guide -Please take `examples/ESP/main.cpp` as the starting point for you first project. It is a minimal example which shows how to establish an OCPP connection and how to start and stop charging sessions. In this guide, I give a brief overview of the key concepts. +Please take `examples/ESP/main.cpp` as the starting point for your first project. It is a minimal example which shows how to establish an OCPP connection and how to start and stop charging sessions. In this guide, I give a brief overview of the key concepts. - To get the library running, you have to install all dependencies (see the list below). From 004b60ba19d81d0466766c28e768436e24173033 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 1 Feb 2022 17:57:03 +0100 Subject: [PATCH 105/696] commit configurations delete immediately --- .../Core/ConfigurationKeyValue.cpp | 2 ++ src/ArduinoOcpp/Core/ConfigurationKeyValue.h | 2 +- .../MessagesV16/ChangeConfiguration.cpp | 26 ++++++++++++++++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp index 20b59810..ab3b7b2f 100644 --- a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp @@ -120,10 +120,12 @@ bool AbstractConfiguration::toBeRemoved() { void AbstractConfiguration::setToBeRemoved() { toBeRemovedFlag = true; + value_revision++; } void AbstractConfiguration::resetToBeRemovedFlag() { toBeRemovedFlag = false; + value_revision++; } uint16_t AbstractConfiguration::getValueRevision() { diff --git a/src/ArduinoOcpp/Core/ConfigurationKeyValue.h b/src/ArduinoOcpp/Core/ConfigurationKeyValue.h index 07aa5843..422d3142 100644 --- a/src/ArduinoOcpp/Core/ConfigurationKeyValue.h +++ b/src/ArduinoOcpp/Core/ConfigurationKeyValue.h @@ -25,7 +25,7 @@ class AbstractConfiguration { bool toBeRemovedFlag = false; protected: - uint16_t value_revision = 0; //number of changes of subclass-member "value". This will be important for the client to detect if there was a change + uint16_t value_revision = 0; //number of memory-relevant changes of subclass-member "value" (deleting counts too). This will be important for the client to detect if there was a change bool initializedValue = false; AbstractConfiguration(); diff --git a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp index ec2754bf..284dfbc6 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp @@ -32,6 +32,27 @@ void ChangeConfiguration::processReq(JsonObject payload) { return; } + /* + * Delete entry if value field is empty string + */ + if (value.is()) { + const char *value_string = value.as(); + if (value_string == nullptr || value_string[0] == '\0') { + auto configuration = getConfiguration(key.c_str()); + if (configuration) { + if (configuration->permissionRemotePeerCanWrite()) { + configuration->setToBeRemoved(); + configuration_save(); + rebootRequired = true; + } else { + readOnly = true; + Serial.print(F("[ChangeConfiguration] Trying to delete readonly value!\n")); + } + } + return; //delete operator but nothing to delete --> ignore operation + } + } + /* * Parse value */ @@ -40,7 +61,7 @@ void ChangeConfiguration::processReq(JsonObject payload) { bool isFloat = false; float numFloat = -1.0f; bool isString = false; - const char *value_string = "\0"; + const char *value_string = ""; if (value.is()) { //Not enough. SteVe always sends numerical values as strings. Must also handle this isInt = true; @@ -152,6 +173,9 @@ void ChangeConfiguration::processReq(JsonObject payload) { Serial.print(F("[ChangeConfiguration] Value has incompatible type!\n")); return; } + + configuration->resetToBeRemovedFlag(); + } else { //configuration does not exist yet. Create new entry if (isInt) { From e1b304636b1183e31e324fa31efbe3ea659a0886 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 2 Feb 2022 10:00:44 +0100 Subject: [PATCH 106/696] fix field name --- src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp index 3d887344..2d1fd8b3 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp @@ -69,7 +69,7 @@ ChargingSchedule::ChargingSchedule(JsonObject *json, ChargingProfileKindType cha //non-success startSchedule = MIN_TIME; } - const char *unit = (*json)["scheduleUnit"] | "__Invalid"; + const char *unit = (*json)["chargingRateUnit"] | "__Invalid"; if (unit[0] == 'a' || unit[0] == 'A') { schedulingUnit = 'A'; } else { From f925f1b51e81df4f72d9f4885983e0396130189e Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 2 Feb 2022 10:45:31 +0100 Subject: [PATCH 107/696] print deny reason --- src/ArduinoOcpp/MessagesV16/Authorize.cpp | 5 +++-- src/ArduinoOcpp/MessagesV16/StartTransaction.cpp | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/Authorize.cpp b/src/ArduinoOcpp/MessagesV16/Authorize.cpp index 2f7a3798..5e9e207f 100644 --- a/src/ArduinoOcpp/MessagesV16/Authorize.cpp +++ b/src/ArduinoOcpp/MessagesV16/Authorize.cpp @@ -30,7 +30,7 @@ std::unique_ptr Authorize::createReq() { } void Authorize::processConf(JsonObject payload){ - String idTagInfo = payload["idTagInfo"]["status"] | "Invalid"; + String idTagInfo = payload["idTagInfo"]["status"] | "not specified"; if (idTagInfo.equals("Accepted")) { if (DEBUG_OUT) Serial.print(F("[Authorize] Request has been accepted!\n")); @@ -40,7 +40,8 @@ void Authorize::processConf(JsonObject payload){ } } else { - Serial.print(F("[Authorize] Request has been denied!")); + Serial.print(F("[Authorize] Request has been denied! Reason: ")); + Serial.println(idTagInfo); } } diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp index 719f0cf5..ad079f89 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp @@ -83,7 +83,7 @@ std::unique_ptr StartTransaction::createReq() { void StartTransaction::processConf(JsonObject payload) { - const char* idTagInfoStatus = payload["idTagInfo"]["status"] | "Invalid"; + const char* idTagInfoStatus = payload["idTagInfo"]["status"] | "not specified"; int transactionId = payload["transactionId"] | -1; ConnectorStatus *connector = nullptr; @@ -100,7 +100,8 @@ void StartTransaction::processConf(JsonObject payload) { connector->setTransactionIdSync(transactionId); } } else { - Serial.print(F("[StartTransaction] Request has been denied!\n")); + Serial.print(F("[StartTransaction] Request has been denied! Reason: ")); + Serial.println(idTagInfoStatus); if (connector){ if (transactionRev == connector->getTransactionWriteCount()) { connector->setTransactionId(-1); From 1a896fa401791d4863bcd4978e5963ffd2916156 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 3 Feb 2022 09:36:04 +0100 Subject: [PATCH 108/696] meter start and stop as integer --- src/ArduinoOcpp/MessagesV16/StartTransaction.cpp | 4 ++-- src/ArduinoOcpp/MessagesV16/StartTransaction.h | 2 +- src/ArduinoOcpp/MessagesV16/StopTransaction.cpp | 4 ++-- src/ArduinoOcpp/MessagesV16/StopTransaction.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp index ad079f89..de3473b8 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp @@ -26,7 +26,7 @@ const char* StartTransaction::getOcppOperationType(){ void StartTransaction::initiate() { if (ocppModel && ocppModel->getMeteringService()) { auto meteringService = ocppModel->getMeteringService(); - meterStart = meteringService->readEnergyActiveImportRegister(connectorId); + meterStart = (int) meteringService->readEnergyActiveImportRegister(connectorId); } if (ocppModel) { @@ -66,7 +66,7 @@ std::unique_ptr StartTransaction::createReq() { JsonObject payload = doc->to(); payload["connectorId"] = connectorId; - if (meterStart >= 0.f) { + if (meterStart >= 0) { payload["meterStart"] = meterStart; } diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.h b/src/ArduinoOcpp/MessagesV16/StartTransaction.h index eb9e582c..f7344eef 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.h @@ -16,7 +16,7 @@ namespace Ocpp16 { class StartTransaction : public OcppMessage { private: int connectorId = 1; - float meterStart = -1.0f; + int meterStart = -1; OcppTimestamp otimestamp; String idTag = String('\0'); uint16_t transactionRev = 0; diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp index f2132a58..29d59e75 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp @@ -26,7 +26,7 @@ void StopTransaction::initiate() { if (ocppModel && ocppModel->getMeteringService()) { auto meteringService = ocppModel->getMeteringService(); - meterStop = meteringService->readEnergyActiveImportRegister(connectorId); + meterStop = (int) meteringService->readEnergyActiveImportRegister(connectorId); } if (ocppModel) { @@ -48,7 +48,7 @@ std::unique_ptr StopTransaction::createReq() { auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + (JSONDATE_LENGTH + 1))); JsonObject payload = doc->to(); - if (meterStop >= 0.f) + if (meterStop >= 0) payload["meterStop"] = meterStop; //TODO meterStart is required to be in Wh, but measuring unit is probably inconsistent in implementation if (otimestamp > MIN_TIME) { diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.h b/src/ArduinoOcpp/MessagesV16/StopTransaction.h index ed556591..b7a45827 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.h @@ -14,7 +14,7 @@ namespace Ocpp16 { class StopTransaction : public OcppMessage { private: int connectorId = 1; - float meterStop = -1.0f; + int meterStop = -1; OcppTimestamp otimestamp; public: StopTransaction(); From 2025fca422e73caf447c5a86b6357aecf0e9c6e0 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 3 Feb 2022 14:13:44 +0100 Subject: [PATCH 109/696] minor modifications --- examples/SECC/platformio.ini | 2 +- src/ArduinoOcpp/Core/OcppOperation.cpp | 2 +- src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/SECC/platformio.ini b/examples/SECC/platformio.ini index 3c7a94b0..5453178e 100644 --- a/examples/SECC/platformio.ini +++ b/examples/SECC/platformio.ini @@ -3,7 +3,7 @@ ; MIT License [env:esp32dev] -platform = espressif32 +platform = espressif32@3.3.2 board = esp32dev framework = arduino lib_deps = diff --git a/src/ArduinoOcpp/Core/OcppOperation.cpp b/src/ArduinoOcpp/Core/OcppOperation.cpp index 85b23020..e1153807 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.cpp +++ b/src/ArduinoOcpp/Core/OcppOperation.cpp @@ -9,7 +9,7 @@ #include -int unique_id_counter = 531531531; +int unique_id_counter = 1000000; using namespace ArduinoOcpp; diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index 87e86184..bda4e456 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -106,7 +106,7 @@ void FirmwareService::loop() { //if (!installationIssued) { if (stage == UpdateStage::AfterDownload) { - if (DEBUG_OUT) Serial.println(F("[FirmwareService] After download!")); + //if (DEBUG_OUT) Serial.println(F("[FirmwareService] After download!")); bool ongoingTx = false; if (cpStatusService) { for (int i = 0; i < cpStatusService->getNumConnectors(); i++) { From 26e586c53a4dad83a9e1673768a4421484ffe2b4 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 3 Feb 2022 17:19:21 +0100 Subject: [PATCH 110/696] introduce isolated debug output stream --- src/ArduinoOcpp/Debug.h | 66 ++++++++++++++++++++++++++++++++++++++ src/ArduinoOcpp/Platform.h | 23 +++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 src/ArduinoOcpp/Debug.h create mode 100644 src/ArduinoOcpp/Platform.h diff --git a/src/ArduinoOcpp/Debug.h b/src/ArduinoOcpp/Debug.h new file mode 100644 index 00000000..3fa33715 --- /dev/null +++ b/src/ArduinoOcpp/Debug.h @@ -0,0 +1,66 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef AO_DEBUG_H +#define AO_DEBUG_H + +#include + +#define AO_DL_NONE 0x00 //suppress all output to the console +#define AO_DL_ERROR 0x01 //report failures +#define AO_DL_WARN 0x02 //report observed or assumed inconsistent state +#define AO_DL_INFO 0x03 //make internal state apparent +#define AO_DL_DEBUG 0x04 //relevant info for debugging +#define AO_DL_VERBOSE 0x05 //all output + +#ifndef AO_DBG_LEVEL +#define AO_DBG_LEVEL AO_DL_INFO //default +#endif + +#define AO_DBG(level, X) \ + do { \ + AO_CONSOLE_PRINTF("[AO] %s (%s:%i): ",level, __FILE__,__LINE__); \ + AO_CONSOLE_PRINTF X; \ + AO_CONSOLE_PRINTF("\n"); \ + } while (0); + +#if AO_DBG_LEVEL >= AO_DL_ERROR +#define AO_DBG_ERR(...) AO_DBG("ERROR",(__VA_ARGS__)) +#else +#define AO_DBG_ERR(...) +#endif + +#if AO_DBG_LEVEL >= AO_DL_WARN +#define AO_DBG_WARN(...) AO_DBG("warning",(__VA_ARGS__)) +#else +#define AO_DBG_WARN(...) +#endif + +#if AO_DBG_LEVEL >= AO_DL_INFO +#define AO_DBG_INFO(...) AO_DBG("info",(__VA_ARGS__)) +#else +#define AO_DBG_INFO(...) +#endif + +#if AO_DBG_LEVEL >= AO_DL_DEBUG +#define AO_DBG_DEBUG(...) AO_DBG("debug",(__VA_ARGS__)) +#else +#define AO_DBG_DEBUG(...) +#endif + +#if AO_DBG_LEVEL >= AO_DL_VERBOSE +#define AO_DBG_VERBOSE(...) AO_DBG("verbose",(__VA_ARGS__)) +#else +#define AO_DBG_VERBOSE(...) +#endif + +#ifdef AO_TRAFFIC_OUT +#define AO_DBG_TRAFFIC_OUT(...) AO_CONSOLE_PRINTF("[AO] To WS lib: %s\n",__VA_ARGS__) +#define AO_DBG_TRAFFIC_IN(...) AO_CONSOLE_PRINTF("[AO] From WS lib: %s\n",__VA_ARGS__) +#else +#define AO_DBG_TRAFFIC_OUT(...) +#define AO_DBG_TRAFFIC_IN(...) +#endif + +#endif diff --git a/src/ArduinoOcpp/Platform.h b/src/ArduinoOcpp/Platform.h new file mode 100644 index 00000000..6df713b8 --- /dev/null +++ b/src/ArduinoOcpp/Platform.h @@ -0,0 +1,23 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef AO_PLATFORM_H +#define AO_PLATFORM_H + +#ifndef AO_CONSOLE_PRINTF +//begin with Arduino support, add more later +#include +#ifndef AO_USE_SERIAL +#define AO_USE_SERIAL Serial +#endif + +#define AO_CONSOLE_PRINTF(X, ...) AO_USE_SERIAL.printf_P(PSTR(X), ##__VA_ARGS__) +#endif + +//#ifndef ao_tick_ms +//#include +//#define ao_tick_ms millis +//#endif + +#endif From 5699696775b2bd8d8b1b6a70d68dd81cb4bfaccd Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 3 Feb 2022 17:23:24 +0100 Subject: [PATCH 111/696] integrate log levels --- src/ArduinoOcpp.cpp | 58 +++++++++--------- src/ArduinoOcpp/Core/Configuration.cpp | 60 +++++++++---------- src/ArduinoOcpp/Core/OcppOperation.cpp | 38 ++++-------- src/ArduinoOcpp/Core/OcppOperation.h | 5 +- .../SimpleOcppOperationFactory.cpp | 26 ++++---- .../FirmwareManagement/FirmwareService.cpp | 2 + 6 files changed, 82 insertions(+), 107 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 2a966584..7387d5de 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -4,8 +4,6 @@ #include "ArduinoOcpp.h" -#include "Variants.h" - #include #include #include @@ -22,6 +20,8 @@ #include #include +#include + namespace ArduinoOcpp { namespace Facade { @@ -55,7 +55,7 @@ using namespace ArduinoOcpp::Ocpp16; #ifndef AO_CUSTOM_WS void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, float V_eff, ArduinoOcpp::FilesystemOpt fsOpt, ArduinoOcpp::OcppClock system_time) { if (ocppEngine) { - Serial.print(F("[ArduinoOcpp] Error: cannot call OCPP_initialize() two times! If you want to reconfigure the library, please restart your ESP\n")); + AO_DBG_WARN("Can't be called two times. Either restart ESP, or call OCPP_deinitialize() before"); return; } @@ -83,7 +83,7 @@ void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, float void OCPP_initialize(OcppSocket& ocppSocket, float V_eff, ArduinoOcpp::FilesystemOpt fsOpt, ArduinoOcpp::OcppClock system_time) { if (ocppEngine) { - Serial.print(F("[ArduinoOcpp] Error: cannot call OCPP_initialize() two times! If you want to reconfigure the library, please restart your ESP\n")); + AO_DBG_WARN("Can't be called two times. To change the credentials, either restart ESP, or call OCPP_deinitialize() before"); return; } @@ -120,7 +120,7 @@ void OCPP_initialize(OcppSocket& ocppSocket, float V_eff, ArduinoOcpp::Filesyste } void OCPP_deinitialize() { - Serial.println(F("[ArduinoOcpp] called OCPP_deinitialize: still experimental function. If you find problems, it would be great if you publish them on the GitHub page")); + AO_DBG_DEBUG("Still experimental function. If you find problems, it would be great if you publish them on the GitHub page"); delete ocppEngine; ocppEngine = nullptr; @@ -148,8 +148,8 @@ void OCPP_deinitialize() { void OCPP_loop() { if (!ocppEngine) { - Serial.print(F("[ArduinoOcpp] Error: you must call OCPP_initialize before calling the loop() function!\n")); - delay(200); //Prevent this error message from flooding the Serial monitor. + AO_DBG_WARN("Please call OCPP_initialize before"); + delay(200); //Prevent this message from flooding the Serial monitor. return; } @@ -207,7 +207,7 @@ void OCPP_loop() { void setPowerActiveImportSampler(std::function power) { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in setPowerActiveImportSampler(): You must call OCPP_initialize() before. Ignore")); + AO_DBG_ERR("Please call OCPP_initialize before"); return; } powerSampler = power; @@ -221,7 +221,7 @@ void setPowerActiveImportSampler(std::function power) { void setEnergyActiveImportSampler(std::function energy) { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in setEnergyActiveImportSampler(): You must call OCPP_initialize() before. Ignore")); + AO_DBG_ERR("Please call OCPP_initialize before"); return; } auto& model = ocppEngine->getOcppModel(); @@ -242,12 +242,12 @@ void setConnectorEnergizedSampler(std::function connectorEnergized) { void setConnectorPluggedSampler(std::function connectorPlugged) { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in setConnectorPluggedSampler(): You must call OCPP_initialize() before. Ignore")); + AO_DBG_ERR("Please call OCPP_initialize before"); return; } auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); if (!connector) { - Serial.println(F("[ArduinoOcpp.cpp] in setConnectorPluggedSampler(): Could not find connector. Ignore")); + AO_DBG_ERR("Could not find connector. Ignore"); return; } connector->setConnectorPluggedSampler(connectorPlugged); @@ -255,12 +255,12 @@ void setConnectorPluggedSampler(std::function connectorPlugged) { void addConnectorErrorCodeSampler(std::function connectorErrorCode) { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in addConnectorErrorCodeSampler(): You must call OCPP_initialize() before. Ignore")); + AO_DBG_ERR("Please call OCPP_initialize before"); return; } auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); if (!connector) { - Serial.println(F("[ArduinoOcpp.cpp] in addConnectorErrorCodeSampler(): Could not find connector. Ignore")); + AO_DBG_ERR("Could not find connector. Ignore"); return; } connector->addConnectorErrorCodeSampler(connectorErrorCode); @@ -268,7 +268,7 @@ void addConnectorErrorCodeSampler(std::function connectorErrorCo void setOnChargingRateLimitChange(std::function chargingRateChanged) { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in setOnChargingRateLimitChange(): You must call OCPP_initialize() before. Ignore")); + AO_DBG_ERR("Please call OCPP_initialize before"); return; } auto& model = ocppEngine->getOcppModel(); @@ -281,12 +281,12 @@ void setOnChargingRateLimitChange(std::function chargingRateChanged void setOnUnlockConnector(std::function unlockConnector) { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in setOnUnlockConnector(): You must call OCPP_initialize() before. Ignore")); + AO_DBG_ERR("Please call OCPP_initialize before"); return; } auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); if (!connector) { - Serial.println(F("[ArduinoOcpp.cpp] in setOnUnlockConnector(): Could not find connector. Ignore")); + AO_DBG_ERR("Could not find connector. Ignore"); return; } connector->setOnUnlockConnector(unlockConnector); @@ -318,7 +318,7 @@ void setOnResetReceiveReq(OnReceiveReqListener onReceiveReq) { void authorize(String &idTag, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in authorize(): You must call OCPP_initialize() before. Ignore")); + AO_DBG_ERR("Please call OCPP_initialize before"); return; } auto authorize = makeOcppOperation( @@ -340,7 +340,7 @@ void authorize(String &idTag, OnReceiveConfListener onConf, OnAbortListener onAb void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in bootNotification(): You must call OCPP_initialize() before. Ignore")); + AO_DBG_ERR("Please call OCPP_initialize before"); return; } auto bootNotification = makeOcppOperation( @@ -362,7 +362,7 @@ void bootNotification(String chargePointModel, String chargePointVendor, OnRecei void bootNotification(String &chargePointModel, String &chargePointVendor, String &chargePointSerialNumber, OnReceiveConfListener onConf) { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in bootNotification(): You must call OCPP_initialize() before. Ignore")); + AO_DBG_ERR("Please call OCPP_initialize before"); return; } auto bootNotification = makeOcppOperation( @@ -374,7 +374,7 @@ void bootNotification(String &chargePointModel, String &chargePointVendor, Strin void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in bootNotification(): You must call OCPP_initialize() before. Ignore")); + AO_DBG_ERR("Please call OCPP_initialize before"); return; } auto bootNotification = makeOcppOperation( @@ -396,7 +396,7 @@ void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf void startTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in startTransaction(): You must call OCPP_initialize() before. Ignore")); + AO_DBG_ERR("Please call OCPP_initialize before"); return; } auto startTransaction = makeOcppOperation( @@ -418,7 +418,7 @@ void startTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnT void startTransaction(String &idTag, OnReceiveConfListener onConf) { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in startTransaction(): You must call OCPP_initialize() before. Ignore")); + AO_DBG_ERR("Please call OCPP_initialize before"); return; } auto startTransaction = makeOcppOperation( @@ -430,7 +430,7 @@ void startTransaction(String &idTag, OnReceiveConfListener onConf) { void stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in stopTransaction(): You must call OCPP_initialize() before. Ignore")); + AO_DBG_ERR("Please call OCPP_initialize before"); return; } auto stopTransaction = makeOcppOperation( @@ -452,12 +452,12 @@ void stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTi int getTransactionId() { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in getTransactionId(): You must call OCPP_initialize() before")); + AO_DBG_WARN("Please call OCPP_initialize before"); return -1; } auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); if (!connector) { - Serial.println(F("[ArduinoOcpp.cpp] in getTransactionId(): Could not find connector")); + AO_DBG_ERR("Could not find connector. Ignore"); return -1; } return connector->getTransactionId(); @@ -465,12 +465,12 @@ int getTransactionId() { bool existsUnboundIdTag() { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in existsUnboundIdTag(): You must call OCPP_initialize() before")); + AO_DBG_WARN("Please call OCPP_initialize before"); return false; } auto csService = ocppEngine->getOcppModel().getChargePointStatusService(); if (!csService) { - Serial.println(F("[ArduinoOcpp.cpp] in existsUnboundIdTag(): Could not find connector")); + AO_DBG_ERR("Could not find connector. Ignore"); return false; } return csService->existsUnboundAuthorization(); @@ -478,14 +478,14 @@ bool existsUnboundIdTag() { bool isAvailable() { if (!ocppEngine) { - Serial.println(F("[ArduinoOcpp.cpp] in isAvailable(): You must call OCPP_initialize() before")); + AO_DBG_WARN("Please call OCPP_initialize before"); return true; //assume "true" as default state } auto& model = ocppEngine->getOcppModel(); auto chargePoint = model.getConnectorStatus(OCPP_ID_OF_CP); auto connector = model.getConnectorStatus(OCPP_ID_OF_CONNECTOR); if (!chargePoint || !connector) { - Serial.println(F("[ArduinoOcpp.cpp] in isAvailable(): Could not find connector")); + AO_DBG_ERR("Could not find connector. Ignore"); return true; //assume "true" as default state } return (chargePoint->getAvailability() != AVAILABILITY_INOPERATIVE) diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index e2bb5e1a..13682816 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -3,10 +3,7 @@ // MIT License #include -//#include - - -#include +#include #include #include @@ -30,8 +27,8 @@ std::shared_ptr> createConfiguration(const char *key, T value) std::shared_ptr> configuration = std::make_shared>(); if (!configuration->setKey(key)) { - Serial.print(F("[Configuration] In createConfiguration(key, T, ...) : Cannot set key! Abort\n")); - return NULL; + AO_DBG_ERR("Cannot set key! Abort"); + return nullptr; } *configuration = value; @@ -44,13 +41,13 @@ std::shared_ptr> createConfiguration(const char *key std::shared_ptr> configuration = std::make_shared>(); if (!configuration->setKey(key)) { - Serial.print(F("[Configuration] In createConfiguration(key, const char*) : Cannot set key! Abort\n")); - return NULL; + AO_DBG_ERR("Cannot set key! Abort"); + return nullptr; } if (!configuration->setValue(value, strlen(value) + 1)) { - Serial.print(F("[Configuration] In createConfiguration(key, const char*) : Cannot set value! Abort\n")); - return NULL; + AO_DBG_ERR("Cannot set value! Abort"); + return nullptr; } return configuration; @@ -87,7 +84,7 @@ std::shared_ptr getContainer(const char *filename) { if (container != configurationContainers.end()) { return *container; } else { - return NULL; + return nullptr; } } @@ -98,24 +95,23 @@ std::shared_ptr> declareConfiguration(const char *key, T defaul std::shared_ptr container = getContainer(filename); if (!container) { - Serial.print(F("[Configuration] declareConfiguration: init new configurations container on flash filesystem: ")); - Serial.println(filename); + AO_DBG_INFO("init new configurations container on flash filesystem: %s", filename); container = createConfigurationContainer(filename); configurationContainers.push_back(container); if (!container->load()) { - Serial.print(F("[Configuration] Cannot load file contents. Path will be overwritten\n")); + AO_DBG_WARN("Cannot load file contents. Path will be overwritten"); } } std::shared_ptr configuration = container->getConfiguration(key); if (configuration && strcmp(configuration->getSerializedType(), SerializedType::get())) { - Serial.println(F("[Configuration] Error declaring configuration: conflicting declared types. Override previous declaration")); + AO_DBG_ERR("conflicting declared types. Override previous declaration"); container->removeConfiguration(configuration); configuration->setToBeRemoved(); - configuration = NULL; + configuration = nullptr; } std::shared_ptr> configurationConcrete = std::static_pointer_cast>(configuration); @@ -129,8 +125,8 @@ std::shared_ptr> declareConfiguration(const char *key, T defaul configuration = std::static_pointer_cast(configurationConcrete); if (!configuration) { - Serial.print(F("[Configuration] In declareConfiguration(key, int, ...) : Cannot find configuration stored from previous session and cannot create new one! Abort\n")); - return NULL; + AO_DBG_ERR("Cannot find configuration stored from previous session and cannot create new one! Abort"); + return nullptr; } container->addConfiguration(configuration); } @@ -153,7 +149,7 @@ namespace Ocpp16 { template std::shared_ptr> changeConfiguration(const char *key, T value) { - std::shared_ptr> config = NULL; + std::shared_ptr> config = nullptr; for (std::vector>::iterator container = configurationContainers.begin(); container != configurationContainers.end(); container++) { @@ -167,7 +163,7 @@ std::shared_ptr> changeConfiguration(const char *key, T value) //found configuration if (!config->permissionRemotePeerCanWrite()) { - return NULL; + return nullptr; } *config = value; @@ -177,8 +173,8 @@ std::shared_ptr> changeConfiguration(const char *key, T value) config = createConfiguration(key, value); if (!config) { - Serial.print(F("[Configuration] In setConfiguration(key, int) : Cannot neither find configuration nor create new one! Abort\n")); - return NULL; + AO_DBG_ERR("Cannot neither find configuration nor create new one! Abort"); + return nullptr; } std::shared_ptr containerDefault = getContainer(CONFIGURATION_FN); @@ -193,14 +189,14 @@ std::shared_ptr> changeConfiguration(const char *key, T value) */ std::shared_ptr getConfiguration(const char *key) { - std::shared_ptr result = NULL; + std::shared_ptr result = nullptr; for (auto container = configurationContainers.begin(); container != configurationContainers.end(); container++) { result = (*container)->getConfiguration(key); if (result) return result; } - return NULL; + return nullptr; } std::shared_ptr>> getAllConfigurations() { //TODO maybe change to iterator? @@ -233,7 +229,7 @@ bool configuration_init(FilesystemOpt fsOpt) { if (fsOpt.mustMount()) { #if defined(ESP32) if(!LITTLEFS.begin(fsOpt.formatOnFail())) { - Serial.println("[Configuration] An Error has occurred while mounting LITTLEFS"); + AO_DBG_ERR("Error while mounting LITTLEFS"); loadRoutineSuccessful = false; } #else @@ -243,13 +239,13 @@ bool configuration_init(FilesystemOpt fsOpt) { SPIFFS.setConfig(cfg); if (!SPIFFS.begin()) { - Serial.print(F("[Configuration] Unable to initialize: unable to mount SPIFFS\n")); + AO_DBG_ERR("Unable to initialize: unable to mount SPIFFS"); loadRoutineSuccessful = false; } #endif } //end fs mount - std::shared_ptr containerDefault = NULL; + std::shared_ptr containerDefault = nullptr; for (auto container = configurationContainers.begin(); container != configurationContainers.end(); container++) { if (!strcmp((*container)->getFilename(), CONFIGURATION_FN)) { containerDefault = (*container); @@ -258,15 +254,13 @@ bool configuration_init(FilesystemOpt fsOpt) { } if (containerDefault) { - if (DEBUG_OUT) { - Serial.print(F("[Configuration] Found default container before calling configuration_init(). If you added\n" \ - " the container manually, please ensure to call load(). If not, it is a hint\n" \ - " that declareConfiguration() was called too early\n")); - } + AO_DBG_DEBUG("Found default container before calling configuration_init(). If you added\n" \ + " the container manually, please ensure to call load(). If not, it is a hint\n" \ + " that declareConfiguration() was called too early\n"); } else { containerDefault = createConfigurationContainer(CONFIGURATION_FN); if (!containerDefault->load()) { - Serial.print(F("[Configuration] Loading default configurations file failed!\n")); + AO_DBG_ERR("Loading default configurations file failed"); loadRoutineSuccessful = false; } configurationContainers.push_back(containerDefault); diff --git a/src/ArduinoOcpp/Core/OcppOperation.cpp b/src/ArduinoOcpp/Core/OcppOperation.cpp index e1153807..10791a78 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.cpp +++ b/src/ArduinoOcpp/Core/OcppOperation.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include int unique_id_counter = 1000000; @@ -31,11 +31,11 @@ void OcppOperation::setOcppMessage(std::unique_ptr msg){ void OcppOperation::setOcppModel(std::shared_ptr oModel) { if (!ocppMessage) { - Serial.print(F("[OcppOperation] Please ensure that setOcppModel() will be called after setOcppMessage(). Abort")); + AO_DBG_ERR("Must be called after setOcppMessage(). Abort"); return; } if (!oModel) { - Serial.print(F("[OcppOperation] in setOcppModel(): passed nullptr! Ignore\n")); + AO_DBG_ERR("Passed nullptr. Ignore"); return; } ocppMessage->setOcppModel(oModel); @@ -43,7 +43,7 @@ void OcppOperation::setOcppModel(std::shared_ptr oModel) { void OcppOperation::setTimeout(std::unique_ptr to){ if (!to){ - Serial.print(F("[OcppOperation] in setTimeout(): passed nullptr! Ignore\n")); + AO_DBG_ERR("Passed nullptr! Ignore"); return; } timeout = std::move(to); @@ -57,7 +57,7 @@ Timeout *OcppOperation::getTimeout() { void OcppOperation::setMessageID(const String &id){ if (messageID.length() > 0){ - Serial.print(F("[OcppOperation] OcppOperation (class): MessageID is set twice or is set after first usage!\n")); + AO_DBG_WARN("MessageID is set twice or is set after first usage!"); //return; <-- Just letting the id being overwritten probably doesn't cause further errors... } messageID = id; @@ -79,9 +79,7 @@ boolean OcppOperation::sendReq(OcppSocket& ocppSocket){ */ if (timeout->isExceeded()) { //cancel this operation - Serial.print(F("[OcppOperation] ")); - Serial.print(ocppMessage->getOcppOperationType()); - Serial.print(F(" has timed out! Discard operation\n")); + AO_DBG_INFO("%s has timed out! Discard operation", ocppMessage->getOcppOperationType()); return true; } @@ -128,23 +126,18 @@ boolean OcppOperation::sendReq(OcppSocket& ocppSocket){ String out {'\0'}; serializeJson(requestJson, out); -#if DEBUG_OUT if (printReqCounter > 5000) { printReqCounter = 0; - Serial.print(F("[OcppOperation] Send requirement: ")); - Serial.print(out); - Serial.print(F("\n")); + AO_DBG_DEBUG("Try to send requirement: %s", out.c_str()); } printReqCounter++; -#endif bool success = ocppSocket.sendTXT(out); timeout->tick(success); if (success) { - if (TRAFFIC_OUT) Serial.print(F("[OcppOperation] Sent requirement (success): ")); - if (TRAFFIC_OUT) Serial.println(out); + AO_DBG_TRAFFIC_OUT(out.c_str()); retry_start = millis(); } else { //ocppSocket is not able to put any data on TCP stack. Maybe because we're offline @@ -298,13 +291,10 @@ boolean OcppOperation::sendConf(OcppSocket& ocppSocket){ if (wsSuccess) { if (operationSuccess) { - if (DEBUG_OUT || TRAFFIC_OUT) Serial.print(F("[OcppOperation] (Conf) JSON message: ")); - if (DEBUG_OUT || TRAFFIC_OUT) Serial.println(out); + AO_DBG_TRAFFIC_OUT(out.c_str()); onSendConfListener(confPayload->as()); } else { - Serial.print(F("[OcppOperation] (Conf) Error occured! JSON CallError message: ")); - Serial.print(out); - Serial.print('\n'); + AO_DBG_WARN("Operation failed. JSON CallError message: %s", out.c_str()); onAbortListener(); } } @@ -316,7 +306,7 @@ void OcppOperation::setInitiated() { if (ocppMessage) { ocppMessage->initiate(); } else { - Serial.print(F("[OcppOperation] Error: called setInitiated without corresponding OcppOperation!\n")); + AO_DBG_ERR("Missing ocppMessage instance"); } } @@ -358,11 +348,9 @@ boolean OcppOperation::isFullyConfigured(){ } void OcppOperation::print_debug() { - Serial.print(F("[OcppOperation] I am a ")); if (ocppMessage) { - Serial.print(ocppMessage->getOcppOperationType()); + AO_CONSOLE_PRINTF("OcppOperation of type %s\n", ocppMessage->getOcppOperationType()); } else { - Serial.print(F("nullptr")); + AO_CONSOLE_PRINTF("OcppOperation (no type)\n"); } - Serial.print(F("\n")); } diff --git a/src/ArduinoOcpp/Core/OcppOperation.h b/src/ArduinoOcpp/Core/OcppOperation.h index 369744b6..43898df5 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.h +++ b/src/ArduinoOcpp/Core/OcppOperation.h @@ -13,8 +13,6 @@ #include -#include - namespace ArduinoOcpp { class OcppMessage; @@ -41,9 +39,8 @@ class OcppOperation { const ulong RETRY_INTERVAL_MAX = 20000; //in ms; ulong retry_start = 0; ulong retry_interval_mult = 1; // RETRY_INTERVAL * retry_interval_mult gives longer periods with each iteration -#if DEBUG_OUT + uint16_t printReqCounter = 0; -#endif public: OcppOperation(std::unique_ptr msg); diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 097e52ab..6e6e8a80 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -2,8 +2,6 @@ // Copyright Matthias Akstaller 2019 - 2022 // MIT License -#include - #include #include @@ -29,6 +27,8 @@ #include #include +#include + #include #include @@ -206,11 +206,7 @@ std::unique_ptr makeFromTriggerMessage(JsonObject payload) { //int connectorID = payload["connectorId"]; <-- not used in this implementation const char *messageType = payload["requestedMessage"]; - if (DEBUG_OUT) { - Serial.print(F("[SimpleOcppOperationFactory] makeFromTriggerMessage for message type ")); - Serial.print(messageType); - Serial.print(F("\n")); - } + AO_DBG_INFO("execute for message type %s", messageType); return makeOcppOperation(messageType); } @@ -254,13 +250,13 @@ std::unique_ptr makeOcppOperation(const char *messageType) { } else if (!strcmp(messageType, "RemoteStartTransaction")) { msg = std::unique_ptr(new Ocpp16::RemoteStartTransaction()); operation->setOnReceiveReqListener(onRemoteStartTransactionReceiveRequest); - if (onRemoteStartTransactionSendConf == nullptr) - Serial.print(F("[SimpleOcppOperationFactory] Warning: RemoteStartTransaction is without effect when the sendConf listener is not set. Set a listener which initiates the StartTransaction operation.\n")); + if (onRemoteStartTransactionSendConf == nullptr) + AO_DBG_WARN("RemoteStartTransaction is without effect when the sendConf listener is not set. Set a listener which initiates the StartTransaction operation."); operation->setOnSendConfListener(onRemoteStartTransactionSendConf); } else if (!strcmp(messageType, "RemoteStopTransaction")) { msg = std::unique_ptr(new Ocpp16::RemoteStopTransaction()); - if (onRemoteStopTransactionSendConf == nullptr) - Serial.print(F("[SimpleOcppOperationFactory] Warning: RemoteStopTransaction is without effect when no sendConf listener is set. Set a listener which initiates the StopTransaction operation.\n")); + if (onRemoteStopTransactionSendConf == nullptr) + AO_DBG_WARN("RemoteStopTransaction is without effect when no sendConf listener is set. Set a listener which initiates the StopTransaction operation."); operation->setOnReceiveReqListener(onRemoteStopTransactionReceiveRequest); operation->setOnSendConfListener(onRemoteStopTransactionSendConf); } else if (!strcmp(messageType, "ChangeConfiguration")) { @@ -274,13 +270,11 @@ std::unique_ptr makeOcppOperation(const char *messageType) { } else if (!strcmp(messageType, "Reset")) { msg = std::unique_ptr(new Ocpp16::Reset()); if (onResetSendConf == nullptr && onResetReceiveReq == nullptr) - Serial.print(F("[SimpleOcppOperationFactory] Warning: Reset is without effect when the sendConf and receiveReq listener is not set. Set a listener which resets your device.\n")); + AO_DBG_WARN("Reset is without effect when the sendConf and receiveReq listener is not set. Set a listener which resets your device."); operation->setOnReceiveReqListener(onResetReceiveReq); operation->setOnSendConfListener(onResetSendConf); } else if (!strcmp(messageType, "UpdateFirmware")) { msg = std::unique_ptr(new Ocpp16::UpdateFirmware()); - //if (onUpdateFirmwareReceiveReq == nullptr) - // Serial.print(F("[SimpleOcppOperationFactory] Warning: UpdateFirmware is without effect when the receiveReq listener is not set. Please implement a FW update routine for your device.\n")); operation->setOnReceiveReqListener(onUpdateFirmwareReceiveReq); } else if (!strcmp(messageType, "FirmwareStatusNotification")) { msg = std::unique_ptr(new Ocpp16::FirmwareStatusNotification()); @@ -295,7 +289,7 @@ std::unique_ptr makeOcppOperation(const char *messageType) { } else if (!strcmp(messageType, "ChangeAvailability")) { msg = std::unique_ptr(new Ocpp16::ChangeAvailability()); } else { - Serial.println(F("[SimpleOcppOperationFactory] Operation not supported")); + AO_DBG_WARN("Operation not supported"); msg = std::unique_ptr(new NotImplemented()); } @@ -309,7 +303,7 @@ std::unique_ptr makeOcppOperation(const char *messageType) { std::unique_ptr makeOcppOperation(OcppMessage *msg){ if (msg == nullptr) { - Serial.print(F("[SimpleOcppOperationFactory] in makeOcppOperation(webSocket, ocppMessage): ocppMessage is null!\n")); + AO_DBG_ERR("msg is null"); return nullptr; } auto operation = makeOcppOperation(); diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index bda4e456..9bcc6a7b 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -11,6 +11,8 @@ #include +#include + using namespace ArduinoOcpp; using ArduinoOcpp::Ocpp16::FirmwareStatus; From 668e34b9f2dfe80876493cf0ef7f97b47eaebcd5 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 8 Feb 2022 15:20:54 +0100 Subject: [PATCH 112/696] add more connector state sampling --- src/ArduinoOcpp.cpp | 74 ++++++------------- .../ChargePointStatus/ConnectorStatus.cpp | 46 ++++-------- .../Tasks/ChargePointStatus/ConnectorStatus.h | 11 +-- 3 files changed, 39 insertions(+), 92 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 7387d5de..b3476fd9 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -30,12 +30,6 @@ WebSocketsClient *webSocket {nullptr}; OcppSocket *ocppSocket {nullptr}; #endif -PowerSampler powerSampler {nullptr}; -std::function evRequestsEnergySampler {nullptr}; -bool evRequestsEnergyLastState {false}; -std::function connectorEnergizedSampler {nullptr}; -bool connectorEnergizedLastState {false}; - OcppEngine *ocppEngine {nullptr}; FilesystemOpt fileSystemOpt {}; float voltage_eff {230.f}; @@ -133,12 +127,6 @@ void OCPP_deinitialize() { #endif simpleOcppFactory_deinitialize(); - - powerSampler = nullptr; - evRequestsEnergySampler = nullptr; - evRequestsEnergyLastState = false; - connectorEnergizedSampler = nullptr; - connectorEnergizedLastState = false; fileSystemOpt = FilesystemOpt(); voltage_eff = 230.f; @@ -167,42 +155,6 @@ void OCPP_loop() { } } - bool evRequestsEnergyNewState = true; - if (evRequestsEnergySampler != nullptr) { - evRequestsEnergyNewState = evRequestsEnergySampler(); - } else { - if (powerSampler != nullptr) { - evRequestsEnergyNewState = powerSampler() >= 50.f; - } - } - - if (!evRequestsEnergyLastState && evRequestsEnergyNewState) { - evRequestsEnergyLastState = true; - if (model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)) - model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)->startEvDrawsEnergy(); - } else if (evRequestsEnergyLastState && !evRequestsEnergyNewState) { - evRequestsEnergyLastState = false; - if (model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)) - model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)->stopEvDrawsEnergy(); - } - - bool connectorEnergizedNewState = true; - if (connectorEnergizedSampler != nullptr) { - connectorEnergizedNewState = connectorEnergizedSampler(); - } else { - connectorEnergizedNewState = getTransactionId() >= 0; - } - - if (!connectorEnergizedLastState && connectorEnergizedNewState) { - connectorEnergizedLastState = true; - if (model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)) - model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)->startEnergyOffer(); - } else if (connectorEnergizedLastState && !connectorEnergizedNewState) { - connectorEnergizedLastState = false; - if (model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)) - model.getConnectorStatus(OCPP_ID_OF_CONNECTOR)->stopEnergyOffer(); - } - } void setPowerActiveImportSampler(std::function power) { @@ -210,13 +162,13 @@ void setPowerActiveImportSampler(std::function power) { AO_DBG_ERR("Please call OCPP_initialize before"); return; } - powerSampler = power; + auto& model = ocppEngine->getOcppModel(); if (!model.getMeteringService()) { model.setMeteringSerivce(std::unique_ptr( new MeteringService(*ocppEngine, OCPP_NUMCONNECTORS))); } - model.getMeteringService()->setPowerSampler(OCPP_ID_OF_CONNECTOR, powerSampler); //connectorId=1 + model.getMeteringService()->setPowerSampler(OCPP_ID_OF_CONNECTOR, power); //connectorId=1 } void setEnergyActiveImportSampler(std::function energy) { @@ -233,11 +185,29 @@ void setEnergyActiveImportSampler(std::function energy) { } void setEvRequestsEnergySampler(std::function evRequestsEnergy) { - evRequestsEnergySampler = evRequestsEnergy; + if (!ocppEngine) { + AO_DBG_ERR("Please call OCPP_initialize before"); + return; + } + auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (!connector) { + AO_DBG_ERR("Could not find connector. Ignore"); + return; + } + connector->setEvRequestsEnergySampler(evRequestsEnergy); } void setConnectorEnergizedSampler(std::function connectorEnergized) { - connectorEnergizedSampler = connectorEnergized; + if (!ocppEngine) { + AO_DBG_ERR("Please call OCPP_initialize before"); + return; + } + auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (!connector) { + AO_DBG_ERR("Could not find connector. Ignore"); + return; + } + connector->setConnectorEnergizedSampler(connectorEnergized); } void setConnectorPluggedSampler(std::function connectorPlugged) { diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index b4d2f36b..9eb8334e 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -10,7 +10,7 @@ #include -#include +#include using namespace ArduinoOcpp; using namespace ArduinoOcpp::Ocpp16; @@ -72,13 +72,13 @@ OcppEvseState ConnectorStatus::inferenceStatus() { return OcppEvseState::Preparing; } else { //Transaction is currently running - if (!evDrawsEnergy) { + if (evRequestsEnergySampler && !evRequestsEnergySampler()) { return OcppEvseState::SuspendedEV; } - if (!evseOffersEnergy) { + if (connectorEnergizedSampler && !connectorEnergizedSampler()) { return OcppEvseState::SuspendedEVSE; } - return OcppEvseState::Charging; + return OcppEvseState::Charging; } } @@ -92,7 +92,7 @@ OcppMessage *ConnectorStatus::loop() { if (inferencedStatus != currentStatus) { currentStatus = inferencedStatus; - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Status changed\n")); + AO_DBG_DEBUG("Status changed"); //fire StatusNotification //TODO check for online condition: Only inform CS about status change if CP is online @@ -120,7 +120,7 @@ void ConnectorStatus::authorize(String &idTag){ void ConnectorStatus::authorize(){ if (authorized == true){ - if (DEBUG_OUT) Serial.print(F("[ConnectorStatus] Warning: authorized twice or didn't unauthorize before\n")); + AO_DBG_WARN("Authorized twice or didn't unauthorize before"); } authorized = true; } @@ -131,7 +131,7 @@ String &ConnectorStatus::getIdTag() { void ConnectorStatus::unauthorize(){ if (authorized == false){ - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: unauthorized twice or didn't authorize before\n")); + AO_DBG_WARN("Unauthorized twice or didn't authorize before"); } authorized = false; idTag = String('\0'); @@ -177,36 +177,16 @@ void ConnectorStatus::setAvailability(bool available) { saveState(); } -void ConnectorStatus::startEvDrawsEnergy(){ - if (evDrawsEnergy == true){ - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: startEvDrawsEnergy called twice or didn't call stopEvDrawsEnergy before\n")); - } - evDrawsEnergy = true; -} - -void ConnectorStatus::stopEvDrawsEnergy(){ - if (evDrawsEnergy == false){ - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: stopEvDrawsEnergy called twice or didn't call startEvDrawsEnergy before\n")); - } - evDrawsEnergy = false; -} - -void ConnectorStatus::startEnergyOffer(){ - if (evseOffersEnergy == true){ - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: startEnergyOffer called twice or didn't call stopEnergyOffer before\n")); - } - evseOffersEnergy = true; +void ConnectorStatus::setConnectorPluggedSampler(std::function connectorPlugged) { + this->connectorPluggedSampler = connectorPlugged; } -void ConnectorStatus::stopEnergyOffer(){ - if (evseOffersEnergy == false){ - if (DEBUG_OUT) Serial.print(F("[OcppEvseStateService] Warning: stopEnergyOffer called twice or didn't call startEnergyOffer before\n")); - } - evseOffersEnergy = false; +void ConnectorStatus::setEvRequestsEnergySampler(std::function evRequestsEnergy) { + this->evRequestsEnergySampler = evRequestsEnergy; } -void ConnectorStatus::setConnectorPluggedSampler(std::function connectorPlugged) { - this->connectorPluggedSampler = connectorPlugged; +void ConnectorStatus::setConnectorEnergizedSampler(std::function connectorEnergized) { + this->connectorEnergizedSampler = connectorEnergizedSampler; } void ConnectorStatus::addConnectorErrorCodeSampler(std::function connectorErrorCode) { diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index a411001a..b1c7eae2 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -31,10 +31,9 @@ class ConnectorStatus { //int transactionId = -1; std::shared_ptr> transactionId = nullptr; int transactionIdSync = -1; - bool evDrawsEnergy = false; - bool evseOffersEnergy = false; std::function connectorPluggedSampler = nullptr; - //std::function connectorFaultedSampler = nullptr; + std::function evRequestsEnergySampler {nullptr}; + std::function connectorEnergizedSampler {nullptr}; std::vector> connectorErrorCodeSamplers; const char *getErrorCode(); OcppEvseState currentStatus = OcppEvseState::NOT_SET; @@ -55,11 +54,9 @@ class ConnectorStatus { int getAvailability(); void setAvailability(bool available); void boot(); - void startEvDrawsEnergy(); - void stopEvDrawsEnergy(); - void startEnergyOffer(); - void stopEnergyOffer(); void setConnectorPluggedSampler(std::function connectorPlugged); + void setEvRequestsEnergySampler(std::function evRequestsEnergy); + void setConnectorEnergizedSampler(std::function connectorEnergized); void addConnectorErrorCodeSampler(std::function connectorErrorCode); void saveState(); From 0f5f9a383416f6933477954d56a6fd81f0e9c3c7 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 14 Feb 2022 17:22:34 +0100 Subject: [PATCH 113/696] integrate log levels --- src/ArduinoOcpp/Core/OcppMessage.cpp | 12 ++++++------ src/ArduinoOcpp/Core/OcppModel.cpp | 4 ++-- src/ArduinoOcpp/Core/OcppOperation.cpp | 2 +- src/ArduinoOcpp/Core/OcppSocket.cpp | 21 ++++++++++----------- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/ArduinoOcpp/Core/OcppMessage.cpp b/src/ArduinoOcpp/Core/OcppMessage.cpp index 2e0f1115..f33af47c 100644 --- a/src/ArduinoOcpp/Core/OcppMessage.cpp +++ b/src/ArduinoOcpp/Core/OcppMessage.cpp @@ -4,7 +4,7 @@ #include -#include +#include using ArduinoOcpp::OcppMessage; @@ -13,7 +13,7 @@ OcppMessage::OcppMessage() {} OcppMessage::~OcppMessage() {} const char* OcppMessage::getOcppOperationType(){ - Serial.print(F("[OcppMessage] Unsupported operation: getOcppOperationType() is not implemented!\n")); + AO_DBG_ERR("Unsupported operation: getOcppOperationType() is not implemented"); return "CustomOperation"; } @@ -29,20 +29,20 @@ void OcppMessage::initiate() { } std::unique_ptr OcppMessage::createReq() { - Serial.print(F("[OcppMessage] Unsupported operation: createReq() is not implemented!\n")); + AO_DBG_ERR("Unsupported operation: createReq() is not implemented"); return nullptr; } void OcppMessage::processConf(JsonObject payload) { - Serial.print(F("[OcppMessage] Unsupported operation: processConf() is not implemented!\n")); + AO_DBG_ERR("Unsupported operation: processConf() is not implemented"); } void OcppMessage::processReq(JsonObject payload) { - Serial.print(F("[OcppMessage] Unsupported operation: processReq() is not implemented!\n")); + AO_DBG_ERR("Unsupported operation: processReq() is not implemented"); } std::unique_ptr OcppMessage::createConf() { - Serial.print(F("[OcppMessage] Unsupported operation: createConf() is not implemented!\n")); + AO_DBG_ERR("Unsupported operation: createConf() is not implemented"); return nullptr; } diff --git a/src/ArduinoOcpp/Core/OcppModel.cpp b/src/ArduinoOcpp/Core/OcppModel.cpp index 40bd8d38..ec6a78fc 100644 --- a/src/ArduinoOcpp/Core/OcppModel.cpp +++ b/src/ArduinoOcpp/Core/OcppModel.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include using namespace ArduinoOcpp; @@ -62,7 +62,7 @@ ConnectorStatus *OcppModel::getConnectorStatus(int connectorId) const { auto result = getChargePointStatusService()->getConnector(connectorId); if (result == nullptr) { - if (DEBUG_OUT) Serial.println(F("[OcppModel] in getConnectorStatus(): cannot fetch connector with given connectorId. Return nullptr")); + AO_DBG_ERR("Cannot fetch connector with given connectorId. Return nullptr"); //no error catch } return result; diff --git a/src/ArduinoOcpp/Core/OcppOperation.cpp b/src/ArduinoOcpp/Core/OcppOperation.cpp index 10791a78..3d44642f 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.cpp +++ b/src/ArduinoOcpp/Core/OcppOperation.cpp @@ -99,7 +99,7 @@ boolean OcppOperation::sendReq(OcppSocket& ocppSocket){ /* * Create the OCPP message */ - auto requestPayload = std::unique_ptr(ocppMessage->createReq()); + auto requestPayload = ocppMessage->createReq(); if (!requestPayload) { onAbortListener(); return true; diff --git a/src/ArduinoOcpp/Core/OcppSocket.cpp b/src/ArduinoOcpp/Core/OcppSocket.cpp index 05634e49..f8601150 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.cpp +++ b/src/ArduinoOcpp/Core/OcppSocket.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #ifndef AO_CUSTOM_WS @@ -28,32 +28,32 @@ void OcppClientSocket::setReceiveTXTcallback(ReceiveTXTcallback &callback) { wsock->onEvent([callback](WStype_t type, uint8_t * payload, size_t length) { switch (type) { case WStype_DISCONNECTED: - if (DEBUG_OUT) Serial.print(F("[OcppClientSocket] Disconnected!\n")); + AO_DBG_INFO("Disconnected"); break; case WStype_CONNECTED: - if (DEBUG_OUT) Serial.printf("[OcppClientSocket] Connected to url: %s\n", payload); + AO_DBG_INFO("Connected to url: %s\n", payload); break; case WStype_TEXT: - if (DEBUG_OUT || TRAFFIC_OUT) Serial.printf("[OcppClientSocket] get text: %s\n", payload); + AO_DBG_TRAFFIC_IN(payload); if (!callback((const char *) payload, length)) { //forward message to OcppEngine - if (DEBUG_OUT) Serial.print(F("[OcppClientSocket] Processing WebSocket input event failed!\n")); + AO_DBG_WARN("Processing WebSocket input event failed"); } break; case WStype_BIN: - if (DEBUG_OUT) Serial.print(F("[OcppClientSocket] Incoming binary data stream not supported")); + AO_DBG_WARN("Binary data stream not supported"); break; case WStype_PING: // pong will be send automatically - if (DEBUG_OUT || TRAFFIC_OUT) Serial.print(F("[OcppClientSocket] get ping\n")); + AO_DBG_TRAFFIC_IN("WS ping"); break; case WStype_PONG: // answer to a ping we send - if (DEBUG_OUT || TRAFFIC_OUT) Serial.print(F("[OcppClientSocket] get pong\n")); + AO_DBG_TRAFFIC_IN("WS pong"); break; case WStype_FRAGMENT_TEXT_START: //fragments are not supported default: - if (DEBUG_OUT) Serial.print(F("[OcppClientSocket] Unsupported WebSocket event type\n")); + AO_DBG_WARN("Unsupported WebSocket event type"); break; } }); @@ -72,8 +72,7 @@ void OcppServerSocket::loop() { } bool OcppServerSocket::sendTXT(String &out) { - if (DEBUG_OUT) Serial.print(F("[OcppServerSocket] Send TXT: ")); - if (DEBUG_OUT) Serial.println(out); + AO_DBG_TRAFFIC_OUT(out.c_str()); return OcppServer::getInstance()->sendTXT(ip_addr, out); } From f2e8e29f73ce73cc77087c6c19f07e4f3e196ece Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 15 Feb 2022 17:53:23 +0100 Subject: [PATCH 114/696] add user sessions, more HW abstraction --- examples/ESP/main.cpp | 23 ++--- examples/SECC/main.cpp | 15 +-- src/ArduinoOcpp.cpp | 96 ++++++++++++------- src/ArduinoOcpp.h | 29 ++++-- .../Core/ConfigurationContainerFlash.cpp | 4 +- src/ArduinoOcpp/Core/OcppConnection.cpp | 34 +++---- src/ArduinoOcpp/Core/OcppSocket.cpp | 2 +- src/ArduinoOcpp/MessagesV16/Authorize.cpp | 32 ++++--- src/ArduinoOcpp/MessagesV16/Authorize.h | 7 +- .../MessagesV16/BootNotification.cpp | 17 +--- .../MessagesV16/BootNotification.h | 2 - src/ArduinoOcpp/MessagesV16/CiStrings.h | 20 ++++ .../DiagnosticsStatusNotification.cpp | 4 +- .../DiagnosticsStatusNotification.h | 2 +- .../MessagesV16/RemoteStartTransaction.cpp | 48 ++++++---- .../MessagesV16/RemoteStartTransaction.h | 4 +- .../MessagesV16/RemoteStopTransaction.cpp | 3 +- .../MessagesV16/StartTransaction.cpp | 78 +++++++-------- .../MessagesV16/StartTransaction.h | 7 +- .../MessagesV16/StatusNotification.cpp | 96 ++++++------------- .../MessagesV16/StopTransaction.cpp | 21 ++-- src/ArduinoOcpp/MessagesV16/StopTransaction.h | 1 - src/ArduinoOcpp/Platform.h | 5 + .../SimpleOcppOperationFactory.cpp | 4 +- .../ChargePointStatusService.cpp | 72 +++----------- .../ChargePointStatusService.h | 12 +-- .../ChargePointStatus/ConnectorStatus.cpp | 74 ++++++++++---- .../Tasks/ChargePointStatus/ConnectorStatus.h | 32 +++++-- .../SmartCharging/SmartChargingModel.cpp | 4 +- 29 files changed, 385 insertions(+), 363 deletions(-) create mode 100644 src/ArduinoOcpp/MessagesV16/CiStrings.h diff --git a/examples/ESP/main.cpp b/examples/ESP/main.cpp index 5f2a25d3..ba74e531 100644 --- a/examples/ESP/main.cpp +++ b/examples/ESP/main.cpp @@ -48,7 +48,6 @@ void setup() { } #elif defined(ESP32) WiFi.begin(STASSID, STAPSK); - Serial.print(F("[main] Wait for WiFi: ")); while (!WiFi.isConnected()) { Serial.print('.'); delay(1000); @@ -99,35 +98,33 @@ void loop() { OCPP_loop(); /* - * Get transaction state of OCPP + * Check internal OCPP state and bind EVSE hardware to it */ - if (getTransactionId() > 0) { - //transaction running with txID given by getTransactionId() - } else if (getTransactionId() == 0) { - //transaction initiation is pending, i.e. startTransaction() was already sent, but hasn't come back yet. + if (ocppPermitsCharge()) { + //OCPP set up and transaction running. Energize the EV plug here } else { - //no transaction running at the moment + //No transaction running at the moment. De-energize EV plug } /* * Detect if something physical happened at your EVSE and trigger the corresponding OCPP messages */ if (/* RFID chip detected? */ false) { - String idTag = "my-id-tag"; //e.g. idTag = RFID.readIdTag(); + const char *idTag = "my-id-tag"; //e.g. idTag = RFID.readIdTag(); authorize(idTag); } if (/* EV plugged in? */ false) { - startTransaction([] (JsonObject payload) { - //Callback: Central System has answered. Energize your EV plug inside this callback and flash a confirmation light if you want. - Serial.print(F("[main] Started OCPP transaction. EV plug energized\n")); + startTransaction("my-id-tag", [] (JsonObject payload) { + //Callback: Central System has answered. Could flash a confirmation light here. + Serial.print(F("[main] Started OCPP transaction\n")); }); } if (/* EV unplugged? */ false) { stopTransaction([] (JsonObject payload) { - //Callback: Central System has answered. De-energize EV plug here. - Serial.print(F("[main] Stopped OCPP transaction. EV plug de-energized\n")); + //Callback: Central System has answered. + Serial.print(F("[main] Stopped OCPP transaction\n")); }); } diff --git a/examples/SECC/main.cpp b/examples/SECC/main.cpp index b6245f0f..40160cab 100644 --- a/examples/SECC/main.cpp +++ b/examples/SECC/main.cpp @@ -270,15 +270,6 @@ void setup() { // } }); - setOnRemoteStartTransactionSendConf([] (JsonObject payload) { - if (!strcmp(payload["status"], "Accepted")) - startTransaction(); - }); - - setOnRemoteStopTransactionSendConf([] (JsonObject payload) { - stopTransaction(); - }); - setOnResetSendConf([] (JsonObject payload) { if (getTransactionId() >= 0) stopTransaction(); @@ -317,11 +308,11 @@ void loop() { * Detect if something physical happened at your EVSE and trigger the corresponding OCPP messages */ if (/* RFID chip detected? */ false) { - String idTag = "my-id-tag"; //e.g. idTag = RFID.readIdTag(); + const char *idTag = "my-id-tag"; //e.g. idTag = RFID.readIdTag(); authorize(idTag); } - if (getTransactionId() > 0) { + if (ocppPermitsCharge()) { digitalWrite(OCPP_CHARGE_PERMISSION_PIN, OCPP_CHARGE_PERMITTED); digitalWrite(CHARGE_PERMISSION_LED, CHARGE_PERMISSION_ON); } else { @@ -342,7 +333,7 @@ void loop() { //transition unplugged -> plugged; Case B: no transaction running; start transaction evPlugged = EV_PLUGGED; - startTransaction(); + startTransaction("my-id-tag"); } else if (digitalRead(EV_PLUG_PIN) == EV_UNPLUGGED && evPlugged == EV_PLUGGED) { //transition plugged -> unplugged evPlugged = EV_UNPLUGGED; diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index b3476fd9..8c15f95d 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -267,7 +268,7 @@ void setOnSetChargingProfileRequest(OnReceiveReqListener onReceiveReq) { } void setOnRemoteStartTransactionSendConf(OnSendConfListener onSendConf) { - setOnRemoteStartTransactionSendConfListener(onSendConf); + setOnRemoteStartTransactionSendConfListener(onSendConf); } void setOnRemoteStopTransactionReceiveReq(OnReceiveReqListener onReceiveReq) { @@ -286,11 +287,15 @@ void setOnResetReceiveReq(OnReceiveReqListener onReceiveReq) { setOnResetReceiveRequestListener(onReceiveReq); } -void authorize(String &idTag, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { +void authorize(const char *idTag, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { AO_DBG_ERR("Please call OCPP_initialize before"); return; } + if (!idTag || strnlen(idTag, IDTAG_LEN_MAX + 2) > IDTAG_LEN_MAX) { + AO_DBG_ERR("idTag format violation. Expect c-style string with at most %u characters", IDTAG_LEN_MAX); + return; + } auto authorize = makeOcppOperation( new Authorize(idTag)); if (onConf) @@ -330,18 +335,6 @@ void bootNotification(String chargePointModel, String chargePointVendor, OnRecei ocppEngine->initiateOperation(std::move(bootNotification)); } -void bootNotification(String &chargePointModel, String &chargePointVendor, String &chargePointSerialNumber, OnReceiveConfListener onConf) { - if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); - return; - } - auto bootNotification = makeOcppOperation( - new BootNotification(chargePointModel, chargePointVendor, chargePointSerialNumber)); - bootNotification->setOnReceiveConfListener(onConf); - bootNotification->setTimeout(std::unique_ptr (new SuppressedTimeout())); - ocppEngine->initiateOperation(std::move(bootNotification)); -} - void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { AO_DBG_ERR("Please call OCPP_initialize before"); @@ -364,13 +357,17 @@ void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf ocppEngine->initiateOperation(std::move(bootNotification)); } -void startTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { +void startTransaction(const char *idTag, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { AO_DBG_ERR("Please call OCPP_initialize before"); return; } + if (!idTag || strnlen(idTag, IDTAG_LEN_MAX + 2) > IDTAG_LEN_MAX) { + AO_DBG_ERR("idTag format violation. Expect c-style string with at most %u characters", IDTAG_LEN_MAX); + return; + } auto startTransaction = makeOcppOperation( - new StartTransaction(OCPP_ID_OF_CONNECTOR)); + new StartTransaction(OCPP_ID_OF_CONNECTOR, idTag)); if (onConf) startTransaction->setOnReceiveConfListener(onConf); if (onAbort) @@ -386,18 +383,6 @@ void startTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnT ocppEngine->initiateOperation(std::move(startTransaction)); } -void startTransaction(String &idTag, OnReceiveConfListener onConf) { - if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); - return; - } - auto startTransaction = makeOcppOperation( - new StartTransaction(OCPP_ID_OF_CONNECTOR, idTag)); - startTransaction->setOnReceiveConfListener(onConf); - startTransaction->setTimeout(std::unique_ptr(new SuppressedTimeout())); - ocppEngine->initiateOperation(std::move(startTransaction)); -} - void stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { AO_DBG_ERR("Please call OCPP_initialize before"); @@ -433,17 +418,17 @@ int getTransactionId() { return connector->getTransactionId(); } -bool existsUnboundIdTag() { +bool ocppPermitsCharge() { if (!ocppEngine) { AO_DBG_WARN("Please call OCPP_initialize before"); return false; } - auto csService = ocppEngine->getOcppModel().getChargePointStatusService(); - if (!csService) { + auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (!connector) { AO_DBG_ERR("Could not find connector. Ignore"); return false; } - return csService->existsUnboundAuthorization(); + return connector->ocppPermitsCharge(); } bool isAvailable() { @@ -462,6 +447,53 @@ bool isAvailable() { && (connector->getAvailability() != AVAILABILITY_INOPERATIVE); } +void beginSession(const char *idTag) { + if (!ocppEngine) { + AO_DBG_ERR("Please call OCPP_initialize before"); + return; + } + if (!idTag || strnlen(idTag, IDTAG_LEN_MAX + 2) > IDTAG_LEN_MAX) { + AO_DBG_ERR("idTag format violation. Expect c-style string with at most %u characters", IDTAG_LEN_MAX); + return; + } + auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (!connector) { + AO_DBG_ERR("Could not find connector. Ignore"); + return; + } + connector->beginSession(idTag); +} + +void endSession() { + if (!ocppEngine) { + AO_DBG_ERR("Please call OCPP_initialize before"); + return; + } + auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (!connector) { + AO_DBG_ERR("Could not find connector. Ignore"); + return; + } + connector->endSession(); +} + +bool isInSession() { + return getSessionIdTag() != nullptr; +} + +const char *getSessionIdTag() { + if (!ocppEngine) { + AO_DBG_ERR("Please call OCPP_initialize before"); + return nullptr; + } + auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (!connector) { + AO_DBG_ERR("Could not find connector. Ignore"); + return nullptr; + } + return connector->getSessionIdTag(); +} + #if defined(AO_CUSTOM_UPDATER) || defined(AO_CUSTOM_WS) ArduinoOcpp::FirmwareService *getFirmwareService() { auto& model = ocppEngine->getOcppModel(); diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 9f0ca249..7478bae0 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -14,8 +14,6 @@ #include #include -#include "Variants.h" - using ArduinoOcpp::OnReceiveConfListener; using ArduinoOcpp::OnReceiveReqListener; using ArduinoOcpp::OnSendConfListener; @@ -88,7 +86,7 @@ void setOnUnlockConnector(std::function unlockConnector); //true: succes void setOnSetChargingProfileRequest(OnReceiveReqListener onReceiveReq); //optional -void setOnRemoteStartTransactionSendConf(OnSendConfListener onSendConf); //important, energize the power plug here +void setOnRemoteStartTransactionSendConf(OnSendConfListener onSendConf); //important, energize the power plug here and capture the idTag void setOnRemoteStopTransactionSendConf(OnSendConfListener onSendConf); //important, de-energize the power plug here void setOnRemoteStopTransactionReceiveReq(OnReceiveReqListener onReceiveReq); //optional, to de-energize the power plug immediately @@ -109,14 +107,14 @@ void setOnResetReceiveReq(OnReceiveReqListener onReceiveReq); //alternative: sta * in any case. */ -void authorize(String &idTag, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); +void authorize(const char *idTag, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); -//The OCPP operation will include the given payload without modifying it. The library will delete the payload object by itself. +//The OCPP operation will include the given payload without modifying it. The library will delete the payload object after successful transmission. void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); -void startTransaction(OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); +void startTransaction(const char *idTag, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); void stopTransaction(OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); @@ -126,10 +124,27 @@ void stopTransaction(OnReceiveConfListener onConf = nullptr, OnAbortListener onA int getTransactionId(); //returns the ID of the current transaction. Returns -1 if called before or after an transaction -bool existsUnboundIdTag(); //returns if the user has given a valid Ocpp Charging Card which is not used for a transaction yet +bool ocppPermitsCharge(); bool isAvailable(); //if the charge point is operative or inoperative +/* + * Session management + * + * A session begins when the EV user is authenticated against the OCPP system and intends to start the charging process. + * The session ends as soon as the authentication expires or as soon as the EV user does not intend to charge anymore. + * + * A session means that the EVSE does not need further input from the EV user or OCPP backend to start a transaction. + */ + +void beginSession(const char *idTag); + +void endSession(); + +bool isInSession(); + +const char *getSessionIdTag(); + /* * Configure the device management */ diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp index 96b2a6ac..7c042e92 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp @@ -117,7 +117,7 @@ bool ConfigurationContainerFlash::load() { for (JsonObject config : configurationsArray) { const char *type = config["type"] | "Undefined"; - std::shared_ptr configuration = NULL; + std::shared_ptr configuration = nullptr; if (!strcmp(type, SerializedType::get())){ configuration = std::make_shared>(config); @@ -144,7 +144,7 @@ bool ConfigurationContainerFlash::load() { configurationsUpdated(); - if (DEBUG_OUT) Serial.println(F("[Configuration] Initialization successful\n")); + if (DEBUG_OUT) Serial.println(F("[Configuration] Initialization successful")); #endif //ndef AO_DEACTIVATE_FLASH return true; } diff --git a/src/ArduinoOcpp/Core/OcppConnection.cpp b/src/ArduinoOcpp/Core/OcppConnection.cpp index e9d0a2b1..eae7a209 100644 --- a/src/ArduinoOcpp/Core/OcppConnection.cpp +++ b/src/ArduinoOcpp/Core/OcppConnection.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #define HEAP_GUARD 2000UL //will not accept JSON messages if it will result in less than HEAP_GUARD free bytes in heap @@ -61,7 +61,7 @@ void OcppConnection::loop(OcppSocket& ocppSock) { } timer->tick(false); //false: did not send a frame prior to calling tick if (timer->isExceeded()) { - Serial.print(F("[OcppEngine] Discarding operation due to timeout: ")); + AO_DBG_INFO("Discarding operation due to timeout:"); (*operation)->print_debug(); operation = initiatedOcppOperations.erase(operation); } else { @@ -89,11 +89,11 @@ void OcppConnection::loop(OcppSocket& ocppSock) { void OcppConnection::initiateOcppOperation(std::unique_ptr o){ if (!o) { - Serial.printf("[OcppEngine] initiateOcppOperation(op) was called with null. Ignore\n"); + AO_DBG_ERR("Called with null. Ignore"); return; } if (!o->isFullyConfigured()){ - Serial.printf("[OcppEngine] initiateOcppOperation(op) was called without the operation being configured and ready to send. Discard operation!\n"); + AO_DBG_ERR("Called without the operation being configured and ready to send. Discard operation!"); return; //o gets destroyed } o->setInitiated(); @@ -108,7 +108,7 @@ bool OcppConnection::processOcppSocketInputTXT(const char* payload, size_t lengt size_t capacity = length + 100; DeserializationError err = DeserializationError::NoMemory; - while (capacity + HEAP_GUARD < ESP.getFreeHeap() && err == DeserializationError::NoMemory) { + while (capacity + HEAP_GUARD < ao_avail_heap() && err == DeserializationError::NoMemory) { doc = std::unique_ptr(new DynamicJsonDocument(capacity)); err = deserializeJson(*doc, payload, length); @@ -136,22 +136,19 @@ bool OcppConnection::processOcppSocketInputTXT(const char* payload, size_t lengt deserializationSuccess = true; break; default: - Serial.print(F("[OcppEngine] Invalid OCPP message! (though JSON has successfully been deserialized)\n")); + AO_DBG_WARN("Invalid OCPP message! (though JSON has successfully been deserialized)"); break; } } else { //unlikely corner case - Serial.print(F("[OcppEngine] Deserialization is okay but doc is nullptr\n")); + AO_DBG_ERR("Deserialization is okay but doc is nullptr"); } break; case DeserializationError::InvalidInput: - Serial.print(F("[OcppEngine] Invalid input! Not a JSON\n")); + AO_DBG_WARN("Invalid input! Not a JSON"); break; case DeserializationError::NoMemory: { - if (DEBUG_OUT) Serial.print(F("[OcppEngine] Error: Not enough memory in heap! Input length = ")); - if (DEBUG_OUT) Serial.print(length); - if (DEBUG_OUT) Serial.print(F(", free heap = ")); - if (DEBUG_OUT) Serial.println(ESP.getFreeHeap()); + AO_DBG_WARN("OOP! Incoming operation exceeds reserved heap. Input length = %lu, free heap = %lu", length, ao_avail_heap()); /* * If websocket input is of message type MESSAGE_TYPE_CALL, send back a message of type MESSAGE_TYPE_CALLERROR. @@ -167,15 +164,14 @@ bool OcppConnection::processOcppSocketInputTXT(const char* payload, size_t lengt int messageTypeId2 = (*doc)[0] | -1; if (messageTypeId2 == MESSAGE_TYPE_CALL) { deserializationSuccess = true; - auto op = makeOcppOperation(new OutOfMemory(ESP.getFreeHeap(), length)); + auto op = makeOcppOperation(new OutOfMemory(ao_avail_heap(), length)); handleReqMessage(*doc, std::move(op)); } } } break; default: - Serial.print(F("[OcppEngine] Deserialization failed: ")); - Serial.println(err.c_str()); + AO_DBG_WARN("Deserialization failed: %s", err.c_str()); break; } @@ -199,13 +195,13 @@ void OcppConnection::handleConfMessage(JsonDocument& json) { } //didn't find matching OcppOperation - Serial.print(F("[OcppEngine] Received CALLRESULT doesn't match any pending operation!\n")); + AO_DBG_WARN("Received CALLRESULT doesn't match any pending operation"); } void OcppConnection::handleReqMessage(JsonDocument& json) { auto op = makeFromJson(json); if (op == nullptr) { - Serial.print(F("[OcppEngine] Couldn't make OppOperation from Request. Ignore request.\n")); + AO_DBG_WARN("Couldn't make OppOperation from Request. Ignore request"); return; } handleReqMessage(json, std::move(op)); @@ -213,7 +209,7 @@ void OcppConnection::handleReqMessage(JsonDocument& json) { void OcppConnection::handleReqMessage(JsonDocument& json, std::unique_ptr op) { if (op == nullptr) { - Serial.print(F("[OcppEngine] handleReqMessage: invalid argument\n")); + AO_DBG_ERR("Invalid argument"); return; } op->setOcppModel(baseModel); @@ -231,7 +227,7 @@ void OcppConnection::handleErrMessage(JsonDocument& json) { } //No OcppOperation was aborted because of the error message - if (DEBUG_OUT) Serial.print(F("[OcppEngine] Received CALLERROR did not abort a pending operation\n")); + AO_DBG_WARN("Received CALLERROR did not abort a pending operation"); } /* diff --git a/src/ArduinoOcpp/Core/OcppSocket.cpp b/src/ArduinoOcpp/Core/OcppSocket.cpp index f8601150..7755c411 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.cpp +++ b/src/ArduinoOcpp/Core/OcppSocket.cpp @@ -31,7 +31,7 @@ void OcppClientSocket::setReceiveTXTcallback(ReceiveTXTcallback &callback) { AO_DBG_INFO("Disconnected"); break; case WStype_CONNECTED: - AO_DBG_INFO("Connected to url: %s\n", payload); + AO_DBG_INFO("Connected to url: %s", payload); break; case WStype_TEXT: AO_DBG_TRAFFIC_IN(payload); diff --git a/src/ArduinoOcpp/MessagesV16/Authorize.cpp b/src/ArduinoOcpp/MessagesV16/Authorize.cpp index 5e9e207f..4227f4ff 100644 --- a/src/ArduinoOcpp/MessagesV16/Authorize.cpp +++ b/src/ArduinoOcpp/MessagesV16/Authorize.cpp @@ -6,16 +6,21 @@ #include #include -#include +#include using ArduinoOcpp::Ocpp16::Authorize; -Authorize::Authorize() { - this->idTag = String("A0-00-00-00"); //Use a default payload. In the typical use case of this library, you probably you don't even need Authorization at all -} +//Authorize::Authorize() { +// snprintf(this->idTag, IDTAG_LEN_MAX + 1, "A0-00-00-00"); //Use a default payload. In the typical use case of this library, you probably you don't even need Authorization at all +//} -Authorize::Authorize(const String &idTag) { - this->idTag = String(idTag); +Authorize::Authorize(const char *idTagIn) { + if (idTagIn && strnlen(idTagIn, IDTAG_LEN_MAX + 2) <= IDTAG_LEN_MAX) { + snprintf(idTag, IDTAG_LEN_MAX + 1, "%s", idTagIn); + } else { + AO_DBG_WARN("Format violation of idTag. Use default idTag"); + snprintf(idTag, IDTAG_LEN_MAX + 1, "A0-00-00-00"); + } } const char* Authorize::getOcppOperationType(){ @@ -23,25 +28,22 @@ const char* Authorize::getOcppOperationType(){ } std::unique_ptr Authorize::createReq() { - auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + (idTag.length() + 1))); + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + (IDTAG_LEN_MAX + 1))); JsonObject payload = doc->to(); payload["idTag"] = idTag; return doc; } void Authorize::processConf(JsonObject payload){ - String idTagInfo = payload["idTagInfo"]["status"] | "not specified"; + const char *idTagInfo = payload["idTagInfo"]["status"] | "not specified"; - if (idTagInfo.equals("Accepted")) { - if (DEBUG_OUT) Serial.print(F("[Authorize] Request has been accepted!\n")); + if (!strcmp(idTagInfo, "Accepted")) { + AO_DBG_INFO("Request has been accepted"); - if (ocppModel && ocppModel->getChargePointStatusService()) { - ocppModel->getChargePointStatusService()->authorize(idTag); - } + //TODO add entry in offline auth cache } else { - Serial.print(F("[Authorize] Request has been denied! Reason: ")); - Serial.println(idTagInfo); + AO_DBG_INFO("Request has been denied. Reason: %s", idTagInfo); } } diff --git a/src/ArduinoOcpp/MessagesV16/Authorize.h b/src/ArduinoOcpp/MessagesV16/Authorize.h index 3edb1709..83ee7ee5 100644 --- a/src/ArduinoOcpp/MessagesV16/Authorize.h +++ b/src/ArduinoOcpp/MessagesV16/Authorize.h @@ -6,17 +6,18 @@ #define AUTHORIZE_H #include +#include namespace ArduinoOcpp { namespace Ocpp16 { class Authorize : public OcppMessage { private: - String idTag; + char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; public: - Authorize(); +// Authorize(); - Authorize(const String &idTag); + Authorize(const char *idTag); const char* getOcppOperationType(); diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp index 0d341b15..3bdd990e 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp @@ -2,12 +2,11 @@ // Copyright Matthias Akstaller 2019 - 2022 // MIT License -#include - #include #include #include #include +#include #include @@ -22,12 +21,6 @@ BootNotification::BootNotification(String &cpModel, String &cpVendor) { chargePointVendor = String(cpVendor); } -BootNotification::BootNotification(String &cpModel, String &cpVendor, String &cpSerialNumber) { - chargePointModel = String(cpModel); - chargePointVendor = String(cpVendor); - chargePointSerialNumber = String(cpSerialNumber); -} - BootNotification::BootNotification(String &cpModel, String &cpVendor, String &cpSerialNumber, String &fwVersion) { chargePointModel = String(cpModel); chargePointVendor = String(cpVendor); @@ -78,10 +71,10 @@ void BootNotification::processConf(JsonObject payload){ if (ocppModel && ocppModel->getOcppTime().setOcppTime(currentTime)) { //success } else { - Serial.print(F("[BootNotification] Error reading time string. Expect format like 2020-02-01T20:53:32.486Z\n")); + AO_DBG_ERR("Time string format violation. Expect format like 2022-02-01T20:53:32.486Z"); } } else { - Serial.print(F("[BootNotification] Error reading time string. Missing attribute currentTime of type string\n")); + AO_DBG_ERR("Missing attribute currentTime"); } int interval = payload["interval"] | -1; @@ -98,12 +91,12 @@ void BootNotification::processConf(JsonObject payload){ const char* status = payload["status"] | "Invalid"; if (!strcmp(status, "Accepted")) { - if (DEBUG_OUT) Serial.print(F("[BootNotification] Request has been accepted!\n")); + AO_DBG_INFO("Request has been accepted"); if (ocppModel && ocppModel->getChargePointStatusService() != nullptr) { ocppModel->getChargePointStatusService()->boot(); } } else { - Serial.print(F("[BootNotification] Request unsuccessful!\n")); + AO_DBG_WARN("Request unsuccessful"); } } diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.h b/src/ArduinoOcpp/MessagesV16/BootNotification.h index 4cb87799..20ad9247 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.h +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.h @@ -25,8 +25,6 @@ class BootNotification : public OcppMessage { BootNotification(String &chargePointModel, String &chargePointVendor); - BootNotification(String &chargePointModel, String &chargePointVendor, String &chargePointSerialNumber); - BootNotification(String &chargePointModel, String &chargePointVendor, String &chargePointSerialNumber, String &firmwareVersion); BootNotification(DynamicJsonDocument *payload); diff --git a/src/ArduinoOcpp/MessagesV16/CiStrings.h b/src/ArduinoOcpp/MessagesV16/CiStrings.h new file mode 100644 index 00000000..1989a4bd --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/CiStrings.h @@ -0,0 +1,20 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +/* + * A collection of the fixed-length string types in the OCPP specification + */ + +#ifndef CI_STRINGS_H +#define CI_STRINGS_H + +#define CiString20TypeLen 20 +#define CiString25TypeLen 25 +#define CiString50TypeLen 50 +#define CiString255TypeLen 255 +#define CiString500TypeLen 500 + +#define IDTAG_LEN_MAX CiString20TypeLen + +#endif diff --git a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp index 590ca538..c53187f1 100644 --- a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp @@ -22,7 +22,7 @@ DiagnosticsStatusNotification::DiagnosticsStatusNotification(DiagnosticsStatus s } -const char *DiagnosticsStatusNotification::cstrFromFwStatus(DiagnosticsStatus status) { +const char *DiagnosticsStatusNotification::cstrFromStatus(DiagnosticsStatus status) { switch (status) { case (DiagnosticsStatus::Idle): return "Idle"; @@ -43,7 +43,7 @@ const char *DiagnosticsStatusNotification::cstrFromFwStatus(DiagnosticsStatus st std::unique_ptr DiagnosticsStatusNotification::createReq() { auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); JsonObject payload = doc->to(); - payload["status"] = cstrFromFwStatus(status); + payload["status"] = cstrFromStatus(status); return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h index 75d04681..abf7db09 100644 --- a/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h +++ b/src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.h @@ -14,7 +14,7 @@ namespace Ocpp16 { class DiagnosticsStatusNotification : public OcppMessage { private: DiagnosticsStatus status; - static const char *cstrFromFwStatus(DiagnosticsStatus status); + static const char *cstrFromStatus(DiagnosticsStatus status); public: DiagnosticsStatusNotification(); diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp index fda65b89..4100b291 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include using ArduinoOcpp::Ocpp16::RemoteStartTransaction; @@ -21,24 +21,35 @@ const char* RemoteStartTransaction::getOcppOperationType() { void RemoteStartTransaction::processReq(JsonObject payload) { connectorId = payload["connectorId"] | -1; - if (payload.containsKey("idTag")) { - String idTag = payload["idTag"] | String("Invalid"); - if (ocppModel && ocppModel->getChargePointStatusService()) { - ocppModel->getChargePointStatusService()->authorize(idTag); - } + const char *idTagIn = payload["idTag"] | ""; + size_t len = strnlen(idTagIn, IDTAG_LEN_MAX + 2); + if (len <= IDTAG_LEN_MAX) { + snprintf(idTag, IDTAG_LEN_MAX + 1, "%s", idTagIn); + } + + if (payload.containsKey("chargingProfile")) { + AO_DBG_WARN("chargingProfile via RmtStartTransaction not supported yet"); } } std::unique_ptr RemoteStartTransaction::createConf(){ auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); JsonObject payload = doc->to(); + + if (*idTag == '\0') { + AO_DBG_WARN("idTag format violation"); + payload["status"] = "Rejected"; + return doc; + } bool canStartTransaction = false; if (connectorId >= 1) { //connectorId specified for given connector, try to start Transaction here if (ocppModel && ocppModel->getConnectorStatus(connectorId)){ auto connector = ocppModel->getConnectorStatus(connectorId); - if (connector->getTransactionId() < 0) { + if (connector->getTransactionId() < 0 && + connector->getAvailability() == AVAILABILITY_OPERATIVE && + connector->getSessionIdTag() == nullptr) { canStartTransaction = true; } } @@ -48,16 +59,27 @@ std::unique_ptr RemoteStartTransaction::createConf(){ auto cpStatusService = ocppModel->getChargePointStatusService(); for (int i = 1; i < cpStatusService->getNumConnectors(); i++) { auto connIter = cpStatusService->getConnector(i); - if (connIter->getTransactionId() < 0) { - canStartTransaction = true; + if (connIter->getTransactionId() < 0 && + connIter->getAvailability() == AVAILABILITY_OPERATIVE && + connIter->getSessionIdTag() == nullptr) { + canStartTransaction = true; + connectorId = i; + break; } } } } if (canStartTransaction){ + if (ocppModel && ocppModel->getConnectorStatus(connectorId)) { + auto connector = ocppModel->getConnectorStatus(connectorId); + + connector->beginSession(idTag); + } + payload["status"] = "Accepted"; } else { + AO_DBG_INFO("No connector to start transaction"); payload["status"] = "Rejected"; } @@ -74,11 +96,5 @@ std::unique_ptr RemoteStartTransaction::createReq() { } void RemoteStartTransaction::processConf(JsonObject payload){ - String status = payload["status"] | "Invalid"; - - if (status.equals("Accepted")) { - if (DEBUG_OUT) Serial.print(F("[RemoteStartTransaction] Request has been accepted!\n")); - } else { - Serial.print(F("[RemoteStartTransaction] Request has been denied!")); - } + } diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h index 5a470292..f8026d6b 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h @@ -6,8 +6,7 @@ #define REMOTESTARTTRANSACTION_H #include - -#include +#include namespace ArduinoOcpp { namespace Ocpp16 { @@ -15,6 +14,7 @@ namespace Ocpp16 { class RemoteStartTransaction : public OcppMessage { private: int connectorId; + char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; public: RemoteStartTransaction(); diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp index 0dac0018..e637d9a1 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp @@ -33,7 +33,8 @@ std::unique_ptr RemoteStopTransaction::createConf(){ for (int i = 0; i < cpStatusService->getNumConnectors(); i++) { auto connIter = cpStatusService->getConnector(i); if (connIter->getTransactionId() == transactionId) { - canStopTransaction = true; + canStopTransaction = true; + connIter->endSession(); } } } diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp index de3473b8..62e05590 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp @@ -6,17 +6,19 @@ #include #include #include - -#include +#include using ArduinoOcpp::Ocpp16::StartTransaction; StartTransaction::StartTransaction(int connectorId) : connectorId(connectorId) { - this->idTag = String('\0'); + } -StartTransaction::StartTransaction(int connectorId, String &idTag) : connectorId(connectorId) { - this->idTag = String(idTag); +StartTransaction::StartTransaction(int connectorId, const char *idTag) : connectorId(connectorId) { + if (idTag && strnlen(idTag, IDTAG_LEN_MAX + 2) <= IDTAG_LEN_MAX) + snprintf(this->idTag, IDTAG_LEN_MAX + 1, "%s", idTag); + else + AO_DBG_ERR("Format violation"); } const char* StartTransaction::getOcppOperationType(){ @@ -35,34 +37,37 @@ void StartTransaction::initiate() { otimestamp = MIN_TIME; } - if (idTag.isEmpty()) { - if (ocppModel && ocppModel->getChargePointStatusService() - && ocppModel->getChargePointStatusService()->existsUnboundAuthorization()) { - this->idTag = String(ocppModel->getChargePointStatusService()->getUnboundIdTag()); + if (ocppModel && ocppModel->getConnectorStatus(connectorId)) { + auto connector = ocppModel->getConnectorStatus(connectorId); + + if (*idTag == '\0') { + const char *sessionIdTag = connector->getSessionIdTag(); + if (sessionIdTag) { + snprintf(idTag, IDTAG_LEN_MAX + 1, "%s", sessionIdTag); + } else { + AO_DBG_WARN("Try to start transaction without providing idTag. Initialize session with default idTag"); + connector->beginSession(nullptr); + sessionIdTag = connector->getSessionIdTag(); //returns default idTag now + if (sessionIdTag) + snprintf(idTag, IDTAG_LEN_MAX + 1, "%s", sessionIdTag); + } } else { - //The CP is not authorized. Try anyway, let the CS decide what to do ... - this->idTag = String("A0-00-00-00"); //Use a default payload. In the typical use case of this library, you probably you don't even need Authorization at all + //idTag has been overriden + connector->beginSession(idTag); } - } - - if (ocppModel && ocppModel->getChargePointStatusService()) { - ocppModel->getChargePointStatusService()->bindAuthorization(connectorId); - } - if (ocppModel && ocppModel->getConnectorStatus(connectorId)){ - auto connector = ocppModel->getConnectorStatus(connectorId); if (connector->getTransactionId() >= 0) { - Serial.print(F("[StartTransaction] Warning: started transaction while OCPP already presumes a running transaction\n")); + AO_DBG_WARN("Started transaction while OCPP already presumes a running transaction"); } connector->setTransactionId(0); //pending transactionRev = connector->getTransactionWriteCount(); } - if (DEBUG_OUT) Serial.println(F("[StartTransaction] StartTransaction initiated!")); + AO_DBG_INFO("StartTransaction initiated"); } std::unique_ptr StartTransaction::createReq() { - auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(5) + (JSONDATE_LENGTH + 1) + (idTag.length() + 1))); + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(5) + (JSONDATE_LENGTH + 1) + (IDTAG_LEN_MAX + 1))); JsonObject payload = doc->to(); payload["connectorId"] = connectorId; @@ -89,27 +94,24 @@ void StartTransaction::processConf(JsonObject payload) { ConnectorStatus *connector = nullptr; if (ocppModel) connector = ocppModel->getConnectorStatus(connectorId); - - if (!strcmp(idTagInfoStatus, "Accepted")) { - if (DEBUG_OUT) Serial.print(F("[StartTransaction] Request has been accepted!\n")); - - if (connector){ - if (transactionRev == connector->getTransactionWriteCount()) { + + if (connector){ + if (transactionRev == connector->getTransactionWriteCount()) { + + if (!strcmp(idTagInfoStatus, "Accepted")) { + AO_DBG_INFO("Request has been accepted"); connector->setTransactionId(transactionId); + } else { + AO_DBG_INFO("Request has been denied. Reason: %s", idTagInfoStatus); + //connector->setTransactionId(-1); + connector->endSession(); //something is wrong with the idTag. Abort session } - connector->setTransactionIdSync(transactionId); - } - } else { - Serial.print(F("[StartTransaction] Request has been denied! Reason: ")); - Serial.println(idTagInfoStatus); - if (connector){ - if (transactionRev == connector->getTransactionWriteCount()) { - connector->setTransactionId(-1); - connector->unauthorize(); - } - connector->setTransactionIdSync(-1); } + connector->setTransactionIdSync(transactionId); + + AO_DBG_DEBUG("Local txId = %i, remote txId = %i", connector->getTransactionId(), connector->getTransactionIdSync()); } + } diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.h b/src/ArduinoOcpp/MessagesV16/StartTransaction.h index f7344eef..b756cdec 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.h @@ -7,8 +7,7 @@ #include #include - -#include +#include namespace ArduinoOcpp { namespace Ocpp16 { @@ -18,12 +17,12 @@ class StartTransaction : public OcppMessage { int connectorId = 1; int meterStart = -1; OcppTimestamp otimestamp; - String idTag = String('\0'); + char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; uint16_t transactionRev = 0; public: StartTransaction(int connectorId); - StartTransaction(int connectorId, String &idTag); + StartTransaction(int connectorId, const char *idTag); const char* getOcppOperationType(); diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp index 3c6a2e22..bd8a74b2 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp @@ -3,55 +3,48 @@ // MIT License #include -#include +#include #include using ArduinoOcpp::Ocpp16::StatusNotification; -StatusNotification::StatusNotification(int connectorId, OcppEvseState currentStatus, const OcppTimestamp &otimestamp, const char *errorCode) - : connectorId(connectorId), currentStatus(currentStatus), otimestamp(otimestamp), errorCode(errorCode) { - - if (DEBUG_OUT) { - Serial.print(F("[StatusNotification] New StatusNotification. New Status: ")); - } - - switch (currentStatus) { +//helper function +namespace ArduinoOcpp { +namespace Ocpp16 { +const char *cstrFromOcppEveState(OcppEvseState state) { + switch (state) { case (OcppEvseState::Available): - if (DEBUG_OUT) Serial.print(F("Available\n")); - break; + return "Available"; case (OcppEvseState::Preparing): - if (DEBUG_OUT) Serial.print(F("Preparing\n")); - break; + return "Preparing"; case (OcppEvseState::Charging): - if (DEBUG_OUT) Serial.print(F("Charging\n")); - break; + return "Charging"; case (OcppEvseState::SuspendedEVSE): - if (DEBUG_OUT) Serial.print(F("SuspendedEVSE\n")); - break; + return "SuspendedEVSE"; case (OcppEvseState::SuspendedEV): - if (DEBUG_OUT) Serial.print(F("SuspendedEV\n")); - break; + return "SuspendedEV"; case (OcppEvseState::Finishing): - if (DEBUG_OUT) Serial.print(F("Finishing\n")); - break; + return "Finishing"; case (OcppEvseState::Reserved): - if (DEBUG_OUT) Serial.print(F("Reserved\n")); - break; + return "Reserved"; case (OcppEvseState::Unavailable): - if (DEBUG_OUT) Serial.print(F("Unavailable\n")); - break; + return "Unavailable"; case (OcppEvseState::Faulted): - if (DEBUG_OUT) Serial.print(F("Faulted\n")); - break; - case (OcppEvseState::NOT_SET): - Serial.print(F("NOT_SET\n")); - break; + return "Faulted"; default: - Serial.print(F("[Error, invalid status code]\n")); - break; + AO_DBG_ERR("OcppEvseState not specified"); + case (OcppEvseState::NOT_SET): + return "NOT_SET"; } } +}} //end namespaces + +StatusNotification::StatusNotification(int connectorId, OcppEvseState currentStatus, const OcppTimestamp &otimestamp, const char *errorCode) + : connectorId(connectorId), currentStatus(currentStatus), otimestamp(otimestamp), errorCode(errorCode) { + + AO_DBG_INFO("New status: %s", cstrFromOcppEveState(currentStatus)); +} const char* StatusNotification::getOcppOperationType(){ return "StatusNotification"; @@ -65,43 +58,14 @@ std::unique_ptr StatusNotification::createReq() { payload["connectorId"] = connectorId; if (errorCode != nullptr) { payload["errorCode"] = errorCode; + } else if (currentStatus == OcppEvseState::NOT_SET) { + AO_DBG_ERR("Reporting undefined status"); + payload["errorCode"] = "InternalError"; } else { payload["errorCode"] = "NoError"; } - - switch (currentStatus) { - case (OcppEvseState::Available): - payload["status"] = "Available"; - break; - case (OcppEvseState::Preparing): - payload["status"] = "Preparing"; - break; - case (OcppEvseState::Charging): - payload["status"] = "Charging"; - break; - case (OcppEvseState::SuspendedEVSE): - payload["status"] = "SuspendedEVSE"; - break; - case (OcppEvseState::SuspendedEV): - payload["status"] = "SuspendedEV"; - break; - case (OcppEvseState::Finishing): - payload["status"] = "Finishing"; - break; - case (OcppEvseState::Reserved): - payload["status"] = "Reserved"; - break; - case (OcppEvseState::Unavailable): - payload["status"] = "Unavailable"; - break; - case (OcppEvseState::Faulted): - payload["status"] = "Faulted"; - break; - default: - payload["status"] = "NOT_SET"; - Serial.print(F("[StatusNotification] Error: Sending status NOT_SET!\n")); - break; - } + + payload["status"] = cstrFromOcppEveState(currentStatus); char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; otimestamp.toJsonString(timestamp, JSONDATE_LENGTH + 1); diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp index 29d59e75..094888c3 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp @@ -6,14 +6,10 @@ #include #include #include -#include +#include using ArduinoOcpp::Ocpp16::StopTransaction; -StopTransaction::StopTransaction() { - -} - StopTransaction::StopTransaction(int connectorId) : connectorId(connectorId) { } @@ -38,10 +34,13 @@ void StopTransaction::initiate() { if (ocppModel && ocppModel->getConnectorStatus(connectorId)){ auto connector = ocppModel->getConnectorStatus(connectorId); connector->setTransactionId(-1); //immediate end of transaction - connector->unauthorize(); + if (connector->getSessionIdTag()) { + AO_DBG_DEBUG("Ending EV user session triggered by StopTransaction"); + connector->endSession(); + } } - if (DEBUG_OUT) Serial.println(F("[StartTransaction] StopTransaction initiated!")); + AO_DBG_INFO("StopTransaction initiated!"); } std::unique_ptr StopTransaction::createReq() { @@ -60,7 +59,6 @@ std::unique_ptr StopTransaction::createReq() { if (ocppModel && ocppModel->getConnectorStatus(connectorId)){ auto connector = ocppModel->getConnectorStatus(connectorId); payload["transactionId"] = connector->getTransactionIdSync(); - connector->setTransactionIdSync(-1); } return doc; @@ -68,9 +66,12 @@ std::unique_ptr StopTransaction::createReq() { void StopTransaction::processConf(JsonObject payload) { - //no need to process anything here + if (ocppModel && ocppModel->getConnectorStatus(connectorId)){ + auto connector = ocppModel->getConnectorStatus(connectorId); + connector->setTransactionIdSync(-1); + } - if (DEBUG_OUT) Serial.print(F("[StopTransaction] Request has been accepted!\n")); + AO_DBG_INFO("Request has been accepted!"); } diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.h b/src/ArduinoOcpp/MessagesV16/StopTransaction.h index b7a45827..a1b861bd 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.h @@ -17,7 +17,6 @@ class StopTransaction : public OcppMessage { int meterStop = -1; OcppTimestamp otimestamp; public: - StopTransaction(); StopTransaction(int connectorId); diff --git a/src/ArduinoOcpp/Platform.h b/src/ArduinoOcpp/Platform.h index 6df713b8..27d33b75 100644 --- a/src/ArduinoOcpp/Platform.h +++ b/src/ArduinoOcpp/Platform.h @@ -20,4 +20,9 @@ //#define ao_tick_ms millis //#endif +#ifndef ao_avail_heap +#include +#define ao_avail_heap ESP.getFreeHeap +#endif + #endif diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 6e6e8a80..1e4b0563 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -224,7 +224,7 @@ std::unique_ptr makeOcppOperation(const char *messageType) { msg = std::unique_ptr(entry->creator()); operation->setOnReceiveReqListener(entry->onReceiveReq); } else if (!strcmp(messageType, "Authorize")) { - msg = std::unique_ptr(new Ocpp16::Authorize()); + msg = std::unique_ptr(new Ocpp16::Authorize("A0-00-00-00")); //send default idTag operation->setOnReceiveReqListener(onAuthorizeRequest); } else if (!strcmp(messageType, "BootNotification")) { msg = std::unique_ptr(new Ocpp16::BootNotification()); @@ -243,7 +243,7 @@ std::unique_ptr makeOcppOperation(const char *messageType) { msg = std::unique_ptr(new Ocpp16::StartTransaction(1)); //connectorId 1 operation->setOnReceiveReqListener(onStartTransactionRequest); } else if (!strcmp(messageType, "StopTransaction")) { - msg = std::unique_ptr(new Ocpp16::StopTransaction()); + msg = std::unique_ptr(new Ocpp16::StopTransaction(1)); //connectorId 1 } else if (!strcmp(messageType, "TriggerMessage")) { msg = std::unique_ptr(new Ocpp16::TriggerMessage()); operation->setOnReceiveReqListener(onTriggerMessageRequest); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index e1a279fb..1d04ad24 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -2,36 +2,32 @@ // Copyright Matthias Akstaller 2019 - 2022 // MIT License -#include - #include #include #include +#include + #include using namespace ArduinoOcpp; ChargePointStatusService::ChargePointStatusService(OcppEngine& context, int numConn) - : context(context), numConnectors{numConn} { - - connectors = (ConnectorStatus**) malloc(numConn * sizeof(ConnectorStatus*)); + : context(context) { + for (int i = 0; i < numConn; i++) { - connectors[i] = new ConnectorStatus(context.getOcppModel(), i); + connectors.push_back(std::unique_ptr(new ConnectorStatus(context.getOcppModel(), i))); } } ChargePointStatusService::~ChargePointStatusService() { - for (int i = 0; i < numConnectors; i++) { - delete connectors[i]; - } - free(connectors); + } void ChargePointStatusService::loop() { if (!booted) return; - for (int i = 0; i < numConnectors; i++){ - auto statusNotificationMsg = connectors[i]->loop(); + for (auto connector = connectors.begin(); connector != connectors.end(); connector++) { + auto statusNotificationMsg = (*connector)->loop(); if (statusNotificationMsg != nullptr) { auto statusNotification = makeOcppOperation(statusNotificationMsg); context.initiateOperation(std::move(statusNotification)); @@ -40,24 +36,12 @@ void ChargePointStatusService::loop() { } ConnectorStatus *ChargePointStatusService::getConnector(int connectorId) { - if (connectorId < 0 || connectorId >= numConnectors) { - Serial.print(F("[ChargePointStatusService] Error in getConnector(connectorId): connectorId is out of bounds\n")); + if (connectorId < 0 || connectorId >= connectors.size()) { + AO_DBG_ERR("connectorId is out of bounds"); return nullptr; } - return connectors[connectorId]; -} - -void ChargePointStatusService::authorize(String &idTag){ - this->idTag = String(idTag); - authorize(); -} - -void ChargePointStatusService::authorize(){ - if (authorized == true){ - if (DEBUG_OUT) Serial.print(F("[ChargePointStatusService] Warning: authorized twice or didn't unauthorize before\n")); - } - authorized = true; + return connectors.at(connectorId).get(); } void ChargePointStatusService::boot() { @@ -68,38 +52,6 @@ bool ChargePointStatusService::isBooted() { return booted; } -String &ChargePointStatusService::getUnboundIdTag() { - return idTag; -} - -void ChargePointStatusService::invalidateUnboundIdTag() { - authorized = false; - idTag = String('\0'); -} - -boolean ChargePointStatusService::existsUnboundAuthorization() { - return authorized; -} - -void ChargePointStatusService::bindAuthorization(int connectorId) { - if (connectorId < 0 || connectorId >= numConnectors) { - Serial.print(F("[ChargePointStatusService] Error in bindAuthorization(connectorId): connectorId is out of bounds\n")); - return; - } - if (!authorized) { - if (DEBUG_OUT) Serial.print(F("[ChargePointStatusService] Authorize connector though there is no unbound ID tag\n")); - } - if (DEBUG_OUT) Serial.print(F("[ChargePointStatusService] Connector ")); - if (DEBUG_OUT) Serial.print(connectorId); - if (DEBUG_OUT) Serial.print(F(" occupies idTag ")); - if (DEBUG_OUT) Serial.print(idTag); - if (DEBUG_OUT) Serial.print(F("\n")); - - connectors[connectorId]->authorize(idTag); - - authorized = false; -} - int ChargePointStatusService::getNumConnectors() { - return numConnectors; + return connectors.size(); } diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h index 72ccfea8..5013b8fe 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h @@ -15,14 +15,10 @@ class ChargePointStatusService { private: OcppEngine& context; - const int numConnectors; - ConnectorStatus **connectors; + std::vector> connectors; bool booted = false; - boolean authorized = false; - String idTag = String('\0'); - public: ChargePointStatusService(OcppEngine& context, int numConnectors); @@ -30,14 +26,8 @@ class ChargePointStatusService { void loop(); - void authorize(String &idTag); - void authorize(); void boot(); bool isBooted(); - String &getUnboundIdTag(); - void invalidateUnboundIdTag(); - boolean existsUnboundAuthorization(); - void bindAuthorization(int connectorId); ConnectorStatus *getConnector(int connectorId); int getNumConnectors(); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 9eb8334e..0909f532 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include #include @@ -57,11 +59,11 @@ OcppEvseState ConnectorStatus::inferenceStatus() { return OcppEvseState::Faulted; } else if (*availability == AVAILABILITY_INOPERATIVE) { return OcppEvseState::Unavailable; - } else if (!authorized && cpStatusService && !cpStatusService->existsUnboundAuthorization() && - ((int) *transactionId) < 0 && + } else if (!session && + getTransactionId() < 0 && (connectorPluggedSampler == nullptr || !connectorPluggedSampler()) ) { return OcppEvseState::Available; - } else if (((int) *transactionId) <= 0) { + } else if (getTransactionId() <= 0) { if (connectorPluggedSampler != nullptr && connectorPluggedSampler() && (currentStatus == OcppEvseState::Finishing || currentStatus == OcppEvseState::Charging || @@ -82,12 +84,48 @@ OcppEvseState ConnectorStatus::inferenceStatus() { } } +bool ConnectorStatus::ocppPermitsCharge() { + if (connectorId == 0) { + AO_DBG_WARN("not supported for connectorId == 0"); + return false; + } + + OcppEvseState state = inferenceStatus(); + + return state == OcppEvseState::Charging || + state == OcppEvseState::SuspendedEV || + state == OcppEvseState::SuspendedEVSE; +} + OcppMessage *ConnectorStatus::loop() { if (getTransactionId() <= 0 && *availability == AVAILABILITY_INOPERATIVE_SCHEDULED) { *availability = AVAILABILITY_INOPERATIVE; saveState(); } + /* + * Check conditions for start or stop transaction + */ + if (connectorPluggedSampler) { //only supported with connectorPluggedSampler + if (getTransactionId() >= 0) { + //check condition for StopTransaction + if (!connectorPluggedSampler() || + !session) { + AO_DBG_INFO("Session mngt: trigger StopTransaction"); + return new StopTransaction(connectorId); + } + } else { + //check condition for StartTransaction + if (connectorPluggedSampler() && + session && + !getErrorCode() && + *availability == AVAILABILITY_OPERATIVE) { + AO_DBG_INFO("Session mngt: trigger StartTransaction"); + return new StartTransaction(connectorId); + } + } + } + auto inferencedStatus = inferenceStatus(); if (inferencedStatus != currentStatus) { @@ -113,28 +151,24 @@ const char *ConnectorStatus::getErrorCode() { return nullptr; } -void ConnectorStatus::authorize(String &idTag){ - this->idTag = String(idTag); - authorize(); -} - -void ConnectorStatus::authorize(){ - if (authorized == true){ - AO_DBG_WARN("Authorized twice or didn't unauthorize before"); +void ConnectorStatus::beginSession(const char *sessionIdTag) { + if (!sessionIdTag || *sessionIdTag == '\0') { + //input string is empty + snprintf(idTag, IDTAG_LEN_MAX + 1, "A0-00-00-00"); + } else { + snprintf(idTag, IDTAG_LEN_MAX + 1, "%s", sessionIdTag); } - authorized = true; + session = true; } -String &ConnectorStatus::getIdTag() { - return idTag; +void ConnectorStatus::endSession() { + if (session) + memset(idTag, '\0', IDTAG_LEN_MAX + 1); + session = false; } -void ConnectorStatus::unauthorize(){ - if (authorized == false){ - AO_DBG_WARN("Unauthorized twice or didn't authorize before"); - } - authorized = false; - idTag = String('\0'); +const char *ConnectorStatus::getSessionIdTag() { + return session ? idTag : nullptr; } int ConnectorStatus::getTransactionId() { diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index b1c7eae2..a1be7c16 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -7,6 +7,7 @@ #include #include +#include #define AVAILABILITY_OPERATIVE 2 #define AVAILABILITY_INOPERATIVE_SCHEDULED 1 @@ -25,12 +26,11 @@ class ConnectorStatus { std::shared_ptr> availability = nullptr; - bool authorized = false; - String idTag = String('\0'); - bool transactionRunning = false; - //int transactionId = -1; + bool session = false; + char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; std::shared_ptr> transactionId = nullptr; int transactionIdSync = -1; + std::function connectorPluggedSampler = nullptr; std::function evRequestsEnergySampler {nullptr}; std::function connectorEnergizedSampler {nullptr}; @@ -41,19 +41,29 @@ class ConnectorStatus { public: ConnectorStatus(OcppModel& context, int connectorId); - //boolean requestAuthorization(); - void authorize(); - void authorize(String &idTag); - void unauthorize(); - String &getIdTag(); + /* + * Relation Session <-> Transaction + * Session: the EV user is authorized and the OCPP transaction can start immediately without + * further confirmation by the user or the OCPP backend + * Transaction: started by "StartTransaction" and stopped by "StopTransaction". + * + * A session (between the EV user and the OCPP system) is on of two prerequisites to start a + * transaction. The other prerequisite is that the EV is properly connected to the EVSE + * (given by ConnectorPluggedSampler and no error code) + */ + void beginSession(const char *idTag); + void endSession(); + const char *getSessionIdTag(); int getTransactionId(); int getTransactionIdSync(); uint16_t getTransactionWriteCount(); void setTransactionId(int id); void setTransactionIdSync(int id); + + int getAvailability(); void setAvailability(bool available); - void boot(); + void setAuthorizationProvider(std::function authorization); void setConnectorPluggedSampler(std::function connectorPlugged); void setEvRequestsEnergySampler(std::function evRequestsEnergy); void setConnectorEnergizedSampler(std::function connectorEnergized); @@ -66,6 +76,8 @@ class ConnectorStatus { OcppEvseState inferenceStatus(); + bool ocppPermitsCharge(); + void setOnUnlockConnector(std::function unlockConnector); std::function getOnUnlockConnector(); }; diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp index 2d1fd8b3..3ecebb94 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp @@ -267,7 +267,9 @@ DynamicJsonDocument *ChargingSchedule::toJsonDocument() { char startScheduleJson [JSONDATE_LENGTH + 1] = {'\0'}; startSchedule.toJsonString(startScheduleJson, JSONDATE_LENGTH + 1); payload["startSchedule"] = startScheduleJson; - payload["schedulingUnit"] = schedulingUnit; + char chargingRateUnit_str [2] = {'\0'}; + chargingRateUnit_str[0] = schedulingUnit; + payload["chargingRateUnit"] = chargingRateUnit_str; JsonArray periodArray = payload.createNestedArray("chargingSchedulePeriod"); for (auto period = chargingSchedulePeriod.begin(); period != chargingSchedulePeriod.end(); period++) { JsonObject entry = periodArray.createNestedObject(); From 27a1586569d3e69765358a46672fc9574d57109f Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 16 Feb 2022 12:10:52 +0100 Subject: [PATCH 115/696] report triggered StatusNotifcation --- src/ArduinoOcpp/MessagesV16/GetDiagnostics.h | 2 +- .../MessagesV16/StatusNotification.cpp | 41 ++++++++++++++++--- .../MessagesV16/StatusNotification.h | 4 +- .../MessagesV16/TriggerMessage.cpp | 29 +++++++++---- src/ArduinoOcpp/MessagesV16/TriggerMessage.h | 7 ++-- .../SimpleOcppOperationFactory.cpp | 14 +------ src/ArduinoOcpp/SimpleOcppOperationFactory.h | 4 +- 7 files changed, 69 insertions(+), 32 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h index 5225a5b7..03b152d8 100644 --- a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h +++ b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h @@ -31,7 +31,7 @@ class GetDiagnostics : public OcppMessage { std::unique_ptr createConf(); - const char *getErrorCode() {if (formatError) return "FormationViolation"; else return nullptr;} + const char *getErrorCode() {return formatError ? "FormationViolation" : nullptr;} }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp index bd8a74b2..72cdff6a 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp @@ -3,6 +3,8 @@ // MIT License #include +#include +#include #include #include @@ -50,6 +52,39 @@ const char* StatusNotification::getOcppOperationType(){ return "StatusNotification"; } +void StatusNotification::initiate() { + //set the most recent EVSE status, but only if it hasn't been specified by the constructor before + if (currentStatus != OcppEvseState::NOT_SET) { + return; + } + + if (ocppModel && ocppModel->getChargePointStatusService()) { + auto cpsService = ocppModel->getChargePointStatusService(); + if (connectorId < 0 || connectorId >= cpsService->getNumConnectors()) { + if (cpsService->getNumConnectors() == 2) { + //special case: EVSE has exactly 1 physical connector -> take status of the connector + connectorId = 1; + } else { + //generic EVSE: take status of the whole EVSE + connectorId = 0; + } + } + auto connector = cpsService->getConnector(connectorId); + if (connector) { + currentStatus = connector->inferenceStatus(); + } + } + + if (ocppModel) { + otimestamp = ocppModel->getOcppTime().getOcppTimestampNow(); + } else { + otimestamp = MIN_TIME; + } + if (currentStatus == OcppEvseState::NOT_SET) { + AO_DBG_ERR("Could not determine EVSE status"); + } +} + //TODO if the status has changed again when sendReq() is called, abort the operation completely (note: if req is already sent, stick with listening to conf). The OcppEvseStateService will enqueue a new operation itself std::unique_ptr StatusNotification::createReq() { auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + (JSONDATE_LENGTH + 1))); @@ -81,11 +116,7 @@ void StatusNotification::processConf(JsonObject payload) { */ } - -/* - * For debugging only - */ -StatusNotification::StatusNotification() { +StatusNotification::StatusNotification(int connectorId) : connectorId(connectorId) { } diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.h b/src/ArduinoOcpp/MessagesV16/StatusNotification.h index fb226a8c..6bac84f1 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.h +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.h @@ -21,10 +21,12 @@ class StatusNotification : public OcppMessage { public: StatusNotification(int connectorId, OcppEvseState currentStatus, const OcppTimestamp &otimestamp, const char *errorCode = nullptr); - StatusNotification(); + StatusNotification(int connectorId = -1); const char* getOcppOperationType(); + void initiate(); + std::unique_ptr createReq(); void processConf(JsonObject payload); diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp index da8404c0..4477dfa3 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp @@ -3,9 +3,11 @@ // MIT License #include -#include +#include +#include #include -#include +#include +#include using ArduinoOcpp::Ocpp16::TriggerMessage; @@ -19,19 +21,32 @@ const char* TriggerMessage::getOcppOperationType(){ void TriggerMessage::processReq(JsonObject payload) { - Serial.println(F("[TriggerMessage] Warning: TriggerMessage is not tested!")); + AO_DBG_INFO("Warning: TriggerMessage is not fully tested"); + + const char *requestedMessage = payload["requestedMessage"] | "Invalid"; + const int connectorId = payload["connectorId"] | 0; + + if (ocppModel && ocppModel->getChargePointStatusService()) { + if (connectorId >= ocppModel->getChargePointStatusService()->getNumConnectors()) { + formatError = true; + } + } + + if (!formatError) { + AO_DBG_INFO("Execute for message type %s, connectorId = %i", requestedMessage, connectorId); + triggeredOperation = makeOcppOperation(requestedMessage, connectorId); + } - triggeredOperation = makeFromTriggerMessage(payload); - if (triggeredOperation != nullptr) { + if (triggeredOperation) { statusMessage = "Accepted"; } else { - Serial.println(F("[TriggerMessage] Couldn't make OppOperation from TriggerMessage. Ignore request.")); + AO_DBG_WARN("Could not make OppOperation from TriggerMessage. Ignore request"); statusMessage = "NotImplemented"; } } std::unique_ptr TriggerMessage::createConf(){ - auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + strlen(statusMessage))); + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); JsonObject payload = doc->to(); payload["status"] = statusMessage; diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h index 7d9cacbd..057625cb 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h @@ -6,9 +6,6 @@ #define TRIGGERMESSAGE_H #include -#include - -#include namespace ArduinoOcpp { @@ -20,6 +17,8 @@ class TriggerMessage : public OcppMessage { private: std::unique_ptr triggeredOperation; const char *statusMessage; + + bool formatError = false; public: TriggerMessage(); @@ -28,6 +27,8 @@ class TriggerMessage : public OcppMessage { void processReq(JsonObject payload); std::unique_ptr createConf(); + + const char *getErrorCode() {return formatError ? "FormationViolation" : nullptr;} }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 1e4b0563..9743ebe0 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -201,22 +201,12 @@ CustomOcppMessageCreatorEntry *makeCustomOcppMessage(const char *messageType) { return nullptr; } -std::unique_ptr makeFromTriggerMessage(JsonObject payload) { - - //int connectorID = payload["connectorId"]; <-- not used in this implementation - const char *messageType = payload["requestedMessage"]; - - AO_DBG_INFO("execute for message type %s", messageType); - - return makeOcppOperation(messageType); -} - std::unique_ptr makeFromJson(const JsonDocument& json) { const char* messageType = json[2]; return makeOcppOperation(messageType); } -std::unique_ptr makeOcppOperation(const char *messageType) { +std::unique_ptr makeOcppOperation(const char *messageType, int connectorId) { auto operation = makeOcppOperation(); auto msg = std::unique_ptr{nullptr}; @@ -238,7 +228,7 @@ std::unique_ptr makeOcppOperation(const char *messageType) { msg = std::unique_ptr(new Ocpp16::SetChargingProfile()); operation->setOnReceiveReqListener(onSetChargingProfileRequest); } else if (!strcmp(messageType, "StatusNotification")) { - msg = std::unique_ptr(new Ocpp16::StatusNotification()); + msg = std::unique_ptr(new Ocpp16::StatusNotification(connectorId)); } else if (!strcmp(messageType, "StartTransaction")) { msg = std::unique_ptr(new Ocpp16::StartTransaction(1)); //connectorId 1 operation->setOnReceiveReqListener(onStartTransactionRequest); diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.h b/src/ArduinoOcpp/SimpleOcppOperationFactory.h index c31bb115..3a09b5b8 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.h +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.h @@ -12,15 +12,13 @@ namespace ArduinoOcpp { using OcppMessageCreator = std::function; -std::unique_ptr makeFromTriggerMessage(JsonObject payload); - std::unique_ptr makeFromJson(const JsonDocument& request); std::unique_ptr makeOcppOperation(); std::unique_ptr makeOcppOperation(OcppMessage *msg); -std::unique_ptr makeOcppOperation(const char *actionCode); +std::unique_ptr makeOcppOperation(const char *actionCode, int connectorId = -1); void registerCustomOcppMessage(const char *messageType, OcppMessageCreator ocppMessageCreator, OnReceiveReqListener onReceiveReq = NULL); From a50ab09359c388cdcf18be72979f6af6e2d20c8d Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 16 Feb 2022 13:11:15 +0100 Subject: [PATCH 116/696] isolate millis function --- src/ArduinoOcpp/Core/OcppOperation.cpp | 5 +- src/ArduinoOcpp/Core/OcppOperationTimeout.cpp | 15 ++-- src/ArduinoOcpp/Core/OcppTime.cpp | 6 +- src/ArduinoOcpp/Core/OcppTime.h | 2 +- src/ArduinoOcpp/Platform.h | 8 +-- .../FirmwareManagement/FirmwareService.cpp | 69 +++++++++---------- .../Tasks/Heartbeat/HeartbeatService.cpp | 5 +- .../Metering/ConnectorMeterValuesRecorder.cpp | 14 ++-- 8 files changed, 62 insertions(+), 62 deletions(-) diff --git a/src/ArduinoOcpp/Core/OcppOperation.cpp b/src/ArduinoOcpp/Core/OcppOperation.cpp index 3d44642f..4c2f988c 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.cpp +++ b/src/ArduinoOcpp/Core/OcppOperation.cpp @@ -7,6 +7,7 @@ #include #include +#include #include int unique_id_counter = 1000000; @@ -88,7 +89,7 @@ boolean OcppOperation::sendReq(OcppSocket& ocppSocket){ * * if retry, run the rest of this function, i.e. resend the message. If not, just return false */ - if (millis() <= retry_start + RETRY_INTERVAL * retry_interval_mult) { + if (ao_tick_ms() <= retry_start + RETRY_INTERVAL * retry_interval_mult) { //NO retry return false; } @@ -138,7 +139,7 @@ boolean OcppOperation::sendReq(OcppSocket& ocppSocket){ if (success) { AO_DBG_TRAFFIC_OUT(out.c_str()); - retry_start = millis(); + retry_start = ao_tick_ms(); } else { //ocppSocket is not able to put any data on TCP stack. Maybe because we're offline retry_start = 0; diff --git a/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp b/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp index f314d1ed..1a315586 100644 --- a/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp +++ b/src/ArduinoOcpp/Core/OcppOperationTimeout.cpp @@ -3,8 +3,7 @@ // MIT License #include - -#include +#include using namespace ArduinoOcpp; @@ -46,15 +45,15 @@ FixedTimeout::FixedTimeout(ulong TIMEOUT_DURATION) : TIMEOUT_DURATION(TIMEOUT_DU void FixedTimeout::timerTick(bool sendingSuccessful) { if (!timeout_active) { timeout_active = true; - timeout_start = millis(); + timeout_start = ao_tick_ms(); } } void FixedTimeout::timerRestart() { - timeout_start = millis(); + timeout_start = ao_tick_ms(); timeout_active = false; } bool FixedTimeout::timerIsExceeded() { - return timeout_active && millis() - timeout_start >= TIMEOUT_DURATION; + return timeout_active && ao_tick_ms() - timeout_start >= TIMEOUT_DURATION; } @@ -63,7 +62,7 @@ OfflineSensitiveTimeout::OfflineSensitiveTimeout(ulong TIMEOUT_DURATION) : TIMEO } void OfflineSensitiveTimeout::timerTick(bool sendingSuccessful) { - ulong t = millis(); + ulong t = ao_tick_ms(); if (!timeout_active) { timeout_active = true; @@ -78,11 +77,11 @@ void OfflineSensitiveTimeout::timerTick(bool sendingSuccessful) { last_tick = t; } void OfflineSensitiveTimeout::timerRestart() { - ulong t = millis(); + ulong t = ao_tick_ms(); timeout_start = t; last_tick = t; timeout_active = false; } bool OfflineSensitiveTimeout::timerIsExceeded() { - return timeout_active && millis() - timeout_start >= TIMEOUT_DURATION; + return timeout_active && ao_tick_ms() - timeout_start >= TIMEOUT_DURATION; } diff --git a/src/ArduinoOcpp/Core/OcppTime.cpp b/src/ArduinoOcpp/Core/OcppTime.cpp index 2e4397be..3c58e4ba 100644 --- a/src/ArduinoOcpp/Core/OcppTime.cpp +++ b/src/ArduinoOcpp/Core/OcppTime.cpp @@ -3,7 +3,7 @@ // MIT License #include -#include +#include namespace ArduinoOcpp { @@ -16,10 +16,10 @@ ulong lastClockReading = 0; otime_t lastClockValue = 0; /* - * Basic clock implementation. Works if millis() is exact enough for you and if device doesn't go in sleep mode. + * Basic clock implementation. Works if ao_tick_ms() is exact enough for you and if device doesn't go in sleep mode. */ OcppClock DEFAULT_CLOCK = [] () { - ulong tReading = (millis() - lastClockReading) / 1000UL; + ulong tReading = (ao_tick_ms() - lastClockReading) / 1000UL; if (tReading > 0) { lastClockValue += tReading; lastClockReading += tReading * 1000UL; diff --git a/src/ArduinoOcpp/Core/OcppTime.h b/src/ArduinoOcpp/Core/OcppTime.h index 3f2624e0..5bc85444 100644 --- a/src/ArduinoOcpp/Core/OcppTime.h +++ b/src/ArduinoOcpp/Core/OcppTime.h @@ -21,7 +21,7 @@ typedef std::function OcppClock; namespace Clocks { /* - * Basic clock implementation. Works if millis() is exact enough for you and if device doesn't go in sleep mode. + * Basic clock implementation. Works if ao_tick_ms() is exact enough for you and if device doesn't go in sleep mode. */ extern OcppClock DEFAULT_CLOCK; } //end namespace Clocks diff --git a/src/ArduinoOcpp/Platform.h b/src/ArduinoOcpp/Platform.h index 27d33b75..2d1fad88 100644 --- a/src/ArduinoOcpp/Platform.h +++ b/src/ArduinoOcpp/Platform.h @@ -15,10 +15,10 @@ #define AO_CONSOLE_PRINTF(X, ...) AO_USE_SERIAL.printf_P(PSTR(X), ##__VA_ARGS__) #endif -//#ifndef ao_tick_ms -//#include -//#define ao_tick_ms millis -//#endif +#ifndef ao_tick_ms +#include +#define ao_tick_ms millis +#endif #ifndef ao_avail_heap #include diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index 9bcc6a7b..5217f7ac 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -11,7 +11,8 @@ #include -#include +#include +#include using namespace ArduinoOcpp; using ArduinoOcpp::Ocpp16::FirmwareStatus; @@ -30,7 +31,7 @@ void FirmwareService::loop() { context.initiateOperation(std::move(notification)); } - if (millis() - timestampTransition < delayTransition) { + if (ao_tick_ms() - timestampTransition < delayTransition) { return; } @@ -41,7 +42,7 @@ void FirmwareService::loop() { //if (!downloadIssued) { if (stage == UpdateStage::Idle) { - if (DEBUG_OUT) Serial.println(F("[FirmwareService] Start update!")); + AO_DBG_INFO("Start update"); if (cpStatusService) { ConnectorStatus *evse = cpStatusService->getConnector(0); @@ -55,7 +56,7 @@ void FirmwareService::loop() { } else { downloadIssued = true; stage = UpdateStage::AwaitDownload; - timestampTransition = millis(); + timestampTransition = ao_tick_ms(); delayTransition = 5000; //delay between state "Downloading" and actually starting the download return; } @@ -63,11 +64,11 @@ void FirmwareService::loop() { //if (!onDownloadCalled) { if (stage == UpdateStage::AwaitDownload) { - if (DEBUG_OUT) Serial.println(F("[FirmwareService] Start Download!")); + AO_DBG_INFO("Start download"); stage = UpdateStage::Downloading; if (onDownload != nullptr) { onDownload(location); - timestampTransition = millis(); + timestampTransition = ao_tick_ms(); delayTransition = 30000; //give the download at least 30s return; } @@ -91,7 +92,7 @@ void FirmwareService::loop() { if (timestampNow - retreiveDate >= DOWNLOAD_TIMEOUT || (downloadStatusSampler != nullptr && downloadStatusSampler() == DownloadStatus::DownloadFailed)) { - Serial.println(F("[FirmwareService] Download timeout or failed! Retry")); + AO_DBG_INFO("Download timeout or failed! Retry"); if (retryInterval < DOWNLOAD_TIMEOUT) retreiveDate = timestampNow; else @@ -99,7 +100,7 @@ void FirmwareService::loop() { retries--; resetStage(); - timestampTransition = millis(); + timestampTransition = ao_tick_ms(); delayTransition = 10000; return; } @@ -108,7 +109,6 @@ void FirmwareService::loop() { //if (!installationIssued) { if (stage == UpdateStage::AfterDownload) { - //if (DEBUG_OUT) Serial.println(F("[FirmwareService] After download!")); bool ongoingTx = false; if (cpStatusService) { for (int i = 0; i < cpStatusService->getNumConnectors(); i++) { @@ -124,7 +124,7 @@ void FirmwareService::loop() { stage = UpdateStage::AwaitInstallation; installationIssued = true; - timestampTransition = millis(); + timestampTransition = ao_tick_ms(); delayTransition = 10000; } @@ -132,16 +132,16 @@ void FirmwareService::loop() { } if (stage == UpdateStage::AwaitInstallation) { - if (DEBUG_OUT) Serial.println(F("[FirmwareService] Installing!")); + AO_DBG_INFO("Installing"); stage = UpdateStage::Installing; if (onInstall != nullptr) { onInstall(location); //should restart the device on success } else { - Serial.println(F("[FirmwareService] onInstall must be set! (see setOnInstall). Will abort")); + AO_DBG_WARN("onInstall must be set! (see setOnInstall). Will abort"); } - timestampTransition = millis(); + timestampTransition = ao_tick_ms(); delayTransition = 40000; return; } @@ -168,7 +168,7 @@ void FirmwareService::loop() { if ((timestampNow - retreiveDate >= INSTALLATION_TIMEOUT + DOWNLOAD_TIMEOUT) || (installationStatusSampler != nullptr && installationStatusSampler() == InstallationStatus::InstallationFailed)) { - Serial.println(F("[FirmwareService] Installation timeout or failed! Retry")); + AO_DBG_INFO("Installation timeout or failed! Retry"); if (retryInterval < INSTALLATION_TIMEOUT + DOWNLOAD_TIMEOUT) retreiveDate = timestampNow; else @@ -176,14 +176,14 @@ void FirmwareService::loop() { retries--; resetStage(); - timestampTransition = millis(); + timestampTransition = ao_tick_ms(); delayTransition = 10000; return; } } //should never reach this code - Serial.println(F("[FirmwareService] Unconsidered special case occured. Firmware update failed")); + AO_DBG_ERR("Firmware update failed"); retries = 0; resetStage(); } @@ -195,19 +195,18 @@ void FirmwareService::scheduleFirmwareUpdate(String &location, OcppTimestamp ret this->retries = retries; this->retryInterval = retryInterval; - if (DEBUG_OUT) { - Serial.print(F("[FirmwareService] Scheduled FW update!\n")); - Serial.print(F(" location = ")); - Serial.println(this->location); - Serial.print(F(" retrieveDate = ")); - char dbuf [JSONDATE_LENGTH + 1] = {'\0'}; - this->retreiveDate.toJsonString(dbuf, JSONDATE_LENGTH + 1); - Serial.println(dbuf); - Serial.print(F(" retries = ")); - Serial.print(this->retries); - Serial.print(F(", retryInterval = ")); - Serial.println(this->retryInterval); - } + char dbuf [JSONDATE_LENGTH + 1] = {'\0'}; + this->retreiveDate.toJsonString(dbuf, JSONDATE_LENGTH + 1); + + AO_DBG_INFO("Scheduled FW update!\n" \ + " location = %s\n" \ + " retrieveDate = %s\n" \ + " retries = %i" \ + ", retryInterval = %u", + this->location, + dbuf, + this->retries, + this->retryInterval); resetStage(); } @@ -335,15 +334,15 @@ FirmwareService *EspWiFi::makeFirmwareService(OcppEngine& context, const char *b switch (ret) { case HTTP_UPDATE_FAILED: fwService->setInstallationStatusSampler([](){return InstallationStatus::InstallationFailed;}); - Serial.printf("[main] HTTP_UPDATE_FAILED Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str()); + AO_DBG_WARN("HTTP_UPDATE_FAILED Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str()); break; case HTTP_UPDATE_NO_UPDATES: fwService->setInstallationStatusSampler([](){return InstallationStatus::InstallationFailed;}); - Serial.println(F("[main] HTTP_UPDATE_NO_UPDATES")); + AO_DBG_WARN("HTTP_UPDATE_NO_UPDATES"); break; case HTTP_UPDATE_OK: fwService->setInstallationStatusSampler([](){return InstallationStatus::Installed;}); - Serial.println(F("[main] HTTP_UPDATE_OK")); + AO_DBG_INFO("HTTP_UPDATE_OK"); ESP.restart(); break; } @@ -380,15 +379,15 @@ FirmwareService *EspWiFi::makeFirmwareService(OcppEngine& context, const char *b switch (ret) { case HTTP_UPDATE_FAILED: fwService->setInstallationStatusSampler([](){return InstallationStatus::InstallationFailed;}); - Serial.printf("[main] HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); + AO_DBG_WARN("HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); break; case HTTP_UPDATE_NO_UPDATES: fwService->setInstallationStatusSampler([](){return InstallationStatus::InstallationFailed;}); - Serial.println(F("[main] HTTP_UPDATE_NO_UPDATES")); + AO_DBG_WARN("HTTP_UPDATE_NO_UPDATES"); break; case HTTP_UPDATE_OK: fwService->setInstallationStatusSampler([](){return InstallationStatus::Installed;}); - Serial.println(F("[main] HTTP_UPDATE_OK")); + AO_DBG_INFO("HTTP_UPDATE_OK"); ESP.restart(); break; } diff --git a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp index 89c3a71b..5e04fa63 100644 --- a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp +++ b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp @@ -7,18 +7,19 @@ #include #include #include +#include using namespace ArduinoOcpp; HeartbeatService::HeartbeatService(OcppEngine& context) : context(context) { heartbeatInterval = declareConfiguration("HeartbeatInterval", 86400); - lastHeartbeat = millis(); + lastHeartbeat = ao_tick_ms(); } void HeartbeatService::loop() { ulong hbInterval = *heartbeatInterval; hbInterval *= 1000UL; //conversion s -> ms - ulong now = millis(); + ulong now = ao_tick_ms(); if (now - lastHeartbeat >= hbInterval) { lastHeartbeat = now; diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index 2b526d8b..3fd2fe58 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -7,8 +7,8 @@ #include #include #include - -#include +#include +#include using namespace ArduinoOcpp; using namespace ArduinoOcpp::Ocpp16; @@ -58,9 +58,9 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { * If no powerSampler is available, estimate the energy consumption taking the Charging Schedule and CP Status * into account. */ - if (millis() - lastSampleTime >= (ulong) (*MeterValueSampleInterval * 1000)) { + if (ao_tick_ms() - lastSampleTime >= (ulong) (*MeterValueSampleInterval * 1000)) { takeSample(); - lastSampleTime = millis(); + lastSampleTime = ao_tick_ms(); } @@ -78,7 +78,7 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { OcppMessage *ConnectorMeterValuesRecorder::toMeterValues() { if (sampleTimestamp.size() == 0) { //Switching from Non-Transaction to Transaction (or vice versa) without sample. Or anything wrong here. Discard - if (DEBUG_OUT) Serial.print(F("[ConnectorMeterValuesRecorder] Try to send MeterValues without any data point. Ignore\n")); + AO_DBG_DEBUG("Suggested to send MeterValues without any data point. Ignore"); clear(); return nullptr; } @@ -104,7 +104,7 @@ OcppMessage *ConnectorMeterValuesRecorder::toMeterValues() { } //Maybe the energy sampler or power sampler was set during recording. Discard recorded data. - Serial.print(F("[ConnectorMeterValuesRecorder] Invalid data set. Discard data set and restard recording.\n")); + AO_DBG_WARN("Invalid data set. Discard data set and restart recording"); clear(); return nullptr; @@ -128,7 +128,7 @@ float ConnectorMeterValuesRecorder::readEnergyActiveImportRegister() { if (energySampler != nullptr) { return energySampler(); } else { - Serial.print(F("[ConnectorMeterValuesRecorder] Called readEnergyActiveImportRegister(), but no energySampler or handling strategy set!\n")); + AO_DBG_DEBUG("Called readEnergyActiveImportRegister(), but no energySampler or handling strategy set"); return 0.f; } } From b0068b7b7f749b36ece16e555da3dc074b99d83d Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 16 Feb 2022 16:03:09 +0100 Subject: [PATCH 117/696] persistent session store --- src/ArduinoOcpp/Core/OcppConnection.cpp | 2 +- .../ChargePointStatus/ConnectorStatus.cpp | 29 +++++++++++++++---- .../Tasks/ChargePointStatus/ConnectorStatus.h | 1 + .../FirmwareManagement/FirmwareService.cpp | 2 +- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/ArduinoOcpp/Core/OcppConnection.cpp b/src/ArduinoOcpp/Core/OcppConnection.cpp index eae7a209..204a8288 100644 --- a/src/ArduinoOcpp/Core/OcppConnection.cpp +++ b/src/ArduinoOcpp/Core/OcppConnection.cpp @@ -148,7 +148,7 @@ bool OcppConnection::processOcppSocketInputTXT(const char* payload, size_t lengt break; case DeserializationError::NoMemory: { - AO_DBG_WARN("OOP! Incoming operation exceeds reserved heap. Input length = %lu, free heap = %lu", length, ao_avail_heap()); + AO_DBG_WARN("OOP! Incoming operation exceeds reserved heap. Input length = %zu, free heap = %u", length, ao_avail_heap()); /* * If websocket input is of message type MESSAGE_TYPE_CALL, send back a message of type MESSAGE_TYPE_CALLERROR. diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 0909f532..a9ce8aee 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -21,15 +21,23 @@ ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) : context(context), connectorId{connectorId} { //Set default transaction ID in memory + String keySession = "AO_SID_CONN_"; String keyTx = "OCPP_STATE_TRANSACTION_ID_CONNECTOR_"; String keyAvailability = "OCPP_STATE_AVAILABILITY_CONNECTOR_"; String id = String(connectorId, DEC); + keySession += id; keyTx += id; keyAvailability += id; + sIdTag = declareConfiguration(keySession.c_str(), "", CONFIGURATION_FN, false, false, true, false); transactionId = declareConfiguration(keyTx.c_str(), -1, CONFIGURATION_FN, false, false, true, false); availability = declareConfiguration(keyAvailability.c_str(), AVAILABILITY_OPERATIVE, CONFIGURATION_FN, false, false, true, false); - if (!transactionId || !availability) { - Serial.print(F("[ConnectorStatus] Error! Cannot declare transactionId or availability!\n")); + if (!sIdTag || !transactionId || !availability) { + AO_DBG_ERR("Cannot declare sessionIdTag, transactionId or availability"); + } + if (sIdTag->getBuffsize() > 0 && (*sIdTag)[0] != '\0') { + snprintf(idTag, min((size_t) (IDTAG_LEN_MAX + 1), sIdTag->getBuffsize()), "%s", ((const char *) *sIdTag)); + session = true; + AO_DBG_DEBUG("Load session idTag at initialization"); } transactionIdSync = *transactionId; } @@ -48,8 +56,8 @@ OcppEvseState ConnectorStatus::inferenceStatus() { } } - auto cpStatusService = context.getChargePointStatusService(); - +// auto cpStatusService = context.getChargePointStatusService(); +// // if (!authorized && !getChargePointStatusService()->existsUnboundAuthorization()) { // return OcppEvseState::Available; // } else if (((int) *transactionId) < 0) { @@ -111,6 +119,8 @@ OcppMessage *ConnectorStatus::loop() { //check condition for StopTransaction if (!connectorPluggedSampler() || !session) { + AO_DBG_DEBUG("Session mngt: txId=%i, connectorPlugged=%d, session=%d", + getTransactionId(), connectorPluggedSampler(), session); AO_DBG_INFO("Session mngt: trigger StopTransaction"); return new StopTransaction(connectorId); } @@ -120,6 +130,8 @@ OcppMessage *ConnectorStatus::loop() { session && !getErrorCode() && *availability == AVAILABILITY_OPERATIVE) { + AO_DBG_DEBUG("Session mngt: txId=%i, connectorPlugged=%d, session=%d", + getTransactionId(), connectorPluggedSampler(), session); AO_DBG_INFO("Session mngt: trigger StartTransaction"); return new StartTransaction(connectorId); } @@ -152,18 +164,25 @@ const char *ConnectorStatus::getErrorCode() { } void ConnectorStatus::beginSession(const char *sessionIdTag) { + AO_DBG_DEBUG("Begin session with idTag %s, overwriting idTag %s", sessionIdTag, idTag); if (!sessionIdTag || *sessionIdTag == '\0') { //input string is empty snprintf(idTag, IDTAG_LEN_MAX + 1, "A0-00-00-00"); } else { snprintf(idTag, IDTAG_LEN_MAX + 1, "%s", sessionIdTag); } + sIdTag->setValue(idTag, IDTAG_LEN_MAX + 1); + saveState(); session = true; } void ConnectorStatus::endSession() { - if (session) + AO_DBG_DEBUG("End session with idTag %s", idTag); + if (session) { memset(idTag, '\0', IDTAG_LEN_MAX + 1); + *sIdTag = ""; + saveState(); + } session = false; } diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index a1be7c16..330a24d3 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -28,6 +28,7 @@ class ConnectorStatus { bool session = false; char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; + std::shared_ptr> sIdTag = nullptr; std::shared_ptr> transactionId = nullptr; int transactionIdSync = -1; diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index 5217f7ac..3b1d224f 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -203,7 +203,7 @@ void FirmwareService::scheduleFirmwareUpdate(String &location, OcppTimestamp ret " retrieveDate = %s\n" \ " retries = %i" \ ", retryInterval = %u", - this->location, + this->location.c_str(), dbuf, this->retries, this->retryInterval); From 844377f0a6c12b65ef119e1810bc40d34fc03b7e Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 17 Feb 2022 17:19:35 +0100 Subject: [PATCH 118/696] integrate config ConnectionTimeOut --- .../ChargePointStatus/ConnectorStatus.cpp | 19 +++++++++++++++++++ .../Tasks/ChargePointStatus/ConnectorStatus.h | 14 +++++++++----- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index a9ce8aee..234fac27 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -31,6 +31,7 @@ ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) sIdTag = declareConfiguration(keySession.c_str(), "", CONFIGURATION_FN, false, false, true, false); transactionId = declareConfiguration(keyTx.c_str(), -1, CONFIGURATION_FN, false, false, true, false); availability = declareConfiguration(keyAvailability.c_str(), AVAILABILITY_OPERATIVE, CONFIGURATION_FN, false, false, true, false); + connectionTimeOut = declareConfiguration("ConnectionTimeOut", 30, CONFIGURATION_FN, true, true, true, false); if (!sIdTag || !transactionId || !availability) { AO_DBG_ERR("Cannot declare sessionIdTag, transactionId or availability"); } @@ -138,6 +139,19 @@ OcppMessage *ConnectorStatus::loop() { } } + if (connectionTimeOutListen) { + if (getTransactionId() >= 0 || !session) { + AO_DBG_DEBUG("Session mngt: release connectionTimeOut"); + connectionTimeOutListen = false; + } else { + if (connectionTimeOutTimestamp - ao_tick_ms() >= ((ulong) *connectionTimeOut) * 1000UL) { + AO_DBG_INFO("Session mngt: timeout"); + endSession(); + connectionTimeOutListen = false; + } + } + } + auto inferencedStatus = inferenceStatus(); if (inferencedStatus != currentStatus) { @@ -174,6 +188,9 @@ void ConnectorStatus::beginSession(const char *sessionIdTag) { sIdTag->setValue(idTag, IDTAG_LEN_MAX + 1); saveState(); session = true; + + connectionTimeOutListen = true; + connectionTimeOutTimestamp = ao_tick_ms(); } void ConnectorStatus::endSession() { @@ -184,6 +201,8 @@ void ConnectorStatus::endSession() { saveState(); } session = false; + + connectionTimeOutListen = false; } const char *ConnectorStatus::getSessionIdTag() { diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index 330a24d3..9df5614b 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -24,21 +24,25 @@ class ConnectorStatus { const int connectorId; - std::shared_ptr> availability = nullptr; + std::shared_ptr> availability {nullptr}; bool session = false; char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; - std::shared_ptr> sIdTag = nullptr; - std::shared_ptr> transactionId = nullptr; + std::shared_ptr> sIdTag {nullptr}; + std::shared_ptr> transactionId {nullptr}; int transactionIdSync = -1; - std::function connectorPluggedSampler = nullptr; + std::shared_ptr> connectionTimeOut {nullptr}; //in seconds + bool connectionTimeOutListen {false}; + ulong connectionTimeOutTimestamp {0}; //in milliseconds + + std::function connectorPluggedSampler {nullptr}; std::function evRequestsEnergySampler {nullptr}; std::function connectorEnergizedSampler {nullptr}; std::vector> connectorErrorCodeSamplers; const char *getErrorCode(); OcppEvseState currentStatus = OcppEvseState::NOT_SET; - std::function onUnlockConnector = nullptr; + std::function onUnlockConnector {nullptr}; public: ConnectorStatus(OcppModel& context, int connectorId); From c694040ffeb5362452a004b00abf2ddf2a629f6a Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 17 Feb 2022 17:21:17 +0100 Subject: [PATCH 119/696] Reset.req stops all transactions --- src/ArduinoOcpp/MessagesV16/Reset.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/ArduinoOcpp/MessagesV16/Reset.cpp b/src/ArduinoOcpp/MessagesV16/Reset.cpp index d380db52..b4b4a391 100644 --- a/src/ArduinoOcpp/MessagesV16/Reset.cpp +++ b/src/ArduinoOcpp/MessagesV16/Reset.cpp @@ -3,6 +3,8 @@ // MIT License #include +#include +#include using ArduinoOcpp::Ocpp16::Reset; @@ -23,6 +25,17 @@ void Reset::processReq(JsonObject payload) { if (!strcmp(type, "Hard")){ Serial.print(F("[Reset] Warning: received request to perform hard reset, but this implementation is only capable of soft reset!\n")); } + + if (ocppModel && ocppModel->getChargePointStatusService()) { + auto cpsService = ocppModel->getChargePointStatusService(); + unsigned int connId = 0; + for (unsigned int i = 0; i < cpsService->getNumConnectors(); i++) { + auto connector = cpsService->getConnector(connId); + if (connector) { + connector->endSession(); + } + } + } } std::unique_ptr Reset::createConf(){ From 7e114530615f07a50f39b365593ee2ea55ced727 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 18 Feb 2022 16:13:32 +0100 Subject: [PATCH 120/696] integrate log levels --- .../MessagesV16/ChangeConfiguration.cpp | 22 ++++---- .../MessagesV16/ChangeConfiguration.h | 2 - .../MessagesV16/ClearChargingProfile.cpp | 5 +- src/ArduinoOcpp/MessagesV16/DataTransfer.cpp | 6 +-- .../MessagesV16/GetConfiguration.cpp | 4 +- .../MessagesV16/GetConfiguration.h | 2 - .../MessagesV16/GetDiagnostics.cpp | 9 ++-- src/ArduinoOcpp/MessagesV16/Heartbeat.cpp | 8 +-- src/ArduinoOcpp/MessagesV16/MeterValues.cpp | 5 +- src/ArduinoOcpp/MessagesV16/Reset.cpp | 3 -- src/ArduinoOcpp/MessagesV16/Reset.h | 2 - .../MessagesV16/SetChargingProfile.cpp | 5 +- .../MessagesV16/UnlockConnector.cpp | 5 +- src/ArduinoOcpp/MessagesV16/UnlockConnector.h | 2 - .../MessagesV16/UpdateFirmware.cpp | 7 +-- .../Tasks/Diagnostics/DiagnosticsService.cpp | 53 +++++++++---------- .../Tasks/Metering/MeteringService.cpp | 29 ++++------ .../Tasks/Metering/MeteringService.h | 17 ++---- 18 files changed, 75 insertions(+), 111 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp index 284dfbc6..2b5cfca8 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp @@ -4,8 +4,7 @@ #include #include - -#include +#include using ArduinoOcpp::Ocpp16::ChangeConfiguration; @@ -21,14 +20,14 @@ void ChangeConfiguration::processReq(JsonObject payload) { String key = payload["key"]; if (key.isEmpty()) { err = true; - Serial.print(F("[ChangeConfiguration] Could not read key!\n")); + AO_DBG_WARN("Could not read key"); return; } JsonVariant value = payload["value"]; if (value.isNull()) { err = true; - Serial.print(F("[ChangeConfiguration] Message is lacking value!\n")); + AO_DBG_WARN("Message is lacking value"); return; } @@ -46,7 +45,7 @@ void ChangeConfiguration::processReq(JsonObject payload) { rebootRequired = true; } else { readOnly = true; - Serial.print(F("[ChangeConfiguration] Trying to delete readonly value!\n")); + AO_DBG_WARN("Trying to delete readonly value"); } } return; //delete operator but nothing to delete --> ignore operation @@ -115,10 +114,7 @@ void ChangeConfiguration::processReq(JsonObject payload) { if (nNonDigits == 0 && nDigits > 0 && nSign <= 1 && nDots == 0) { //integer if (nDigits > INT_MAXDIGITS) { - Serial.print(F("[ChangeConfiguration] Integer overflow! key = ")); - Serial.print(key.c_str()); - Serial.print(F(", value = ")); - Serial.println(value_string); + AO_DBG_WARN("Integer overflow! key = %s, value = %s", key.c_str(), value_string); err = true; return; } else { @@ -152,7 +148,7 @@ void ChangeConfiguration::processReq(JsonObject payload) { if (configuration) { if (!configuration->permissionRemotePeerCanWrite()) { readOnly = true; - Serial.print(F("[ChangeConfiguration] Trying to override readonly value!\n")); + AO_DBG_WARN("Trying to override readonly value"); return; } @@ -170,7 +166,7 @@ void ChangeConfiguration::processReq(JsonObject payload) { // *configurationConcrete = value.as(); } else { err = true; - Serial.print(F("[ChangeConfiguration] Value has incompatible type!\n")); + AO_DBG_WARN("Value has incompatible type"); return; } @@ -186,14 +182,14 @@ void ChangeConfiguration::processReq(JsonObject payload) { configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), value_string)); } else { err = true; - Serial.print(F("[ChangeConfiguration] Could not parse value!\n")); + AO_DBG_WARN("Could not parse value"); return; } } //end if (configuration) if (!configuration) { err = true; - Serial.print(F("[ChangeConfiguration] Could not deserialize value or set configuration!\n")); + AO_DBG_WARN("Could not deserialize value or set configuration"); return; } diff --git a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h index 944e5630..19c4a1c2 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h +++ b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.h @@ -5,8 +5,6 @@ #ifndef CHANGECONFIGURATION_H #define CHANGECONFIGURATION_H -#include - #include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp index 89faeb50..5dced006 100644 --- a/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp +++ b/src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -30,7 +31,7 @@ void ClearChargingProfile::processReq(JsonObject payload) { } if (payload.containsKey("connectorId")) { - Serial.print(F("[ClearChargingProfile] Smart Charging does not implement multiple connectors yet. Ignore connectorId\n")); + AO_DBG_WARN("Smart Charging does not implement multiple connectors yet. Ignore connectorId"); } if (payload.containsKey("chargingProfilePurpose")) { @@ -63,7 +64,7 @@ void ClearChargingProfile::processReq(JsonObject payload) { }; if (!ocppModel || !ocppModel->getSmartChargingService()) { - Serial.println(F("[ClearChargingProfile] SmartChargingService not initialized! Ignore request")); + AO_DBG_ERR("SmartChargingService not initialized! Ignore request"); return; } diff --git a/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp b/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp index 2e064738..af8f1238 100644 --- a/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp +++ b/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp @@ -3,7 +3,7 @@ // MIT License #include -#include +#include using ArduinoOcpp::Ocpp16::DataTransfer; @@ -27,8 +27,8 @@ void DataTransfer::processConf(JsonObject payload){ String status = payload["status"] | "Invalid"; if (status.equals("Accepted")) { - if (DEBUG_OUT) Serial.print(F("[DataTransfer] Request has been accepted!\n")); + AO_DBG_DEBUG("Request has been accepted"); } else { - Serial.print(F("[DataTransfer] Request has been denied!")); + AO_DBG_INFO("Request has been denied"); } } diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp index fea58b3a..dfb15f37 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp @@ -6,6 +6,7 @@ #include #include +#include using ArduinoOcpp::Ocpp16::GetConfiguration; @@ -76,10 +77,9 @@ std::unique_ptr GetConfiguration::createConf(){ } if (unknownKeys.size() > 0) { - if (DEBUG_OUT) Serial.print(F("My unknown keys are: \n")); JsonArray jsonUnknownKey = payload.createNestedArray("unknownKey"); for (auto unknownKey = unknownKeys.begin(); unknownKey != unknownKeys.end(); unknownKey++) { - if (DEBUG_OUT) Serial.println(*unknownKey); + AO_DBG_DEBUG("Unknown key: %s", unknownKey->c_str()) jsonUnknownKey.add(*unknownKey); } } diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.h b/src/ArduinoOcpp/MessagesV16/GetConfiguration.h index ec2c0187..959c52eb 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.h +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.h @@ -5,8 +5,6 @@ #ifndef GETCONFIGURATION_H #define GETCONFIGURATION_H -#include - #include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp index a2509384..e251d8f4 100644 --- a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp @@ -5,6 +5,7 @@ #include #include #include +#include using ArduinoOcpp::Ocpp16::GetDiagnostics; @@ -23,7 +24,7 @@ void GetDiagnostics::processReq(JsonObject payload) { //check location URL. Maybe introduce Same-Origin-Policy? if (location.isEmpty()) { formatError = true; - Serial.println(F("[GetDiagnostics] Could not read location. Abort")); + AO_DBG_WARN("Could not read location. Abort"); return; } @@ -35,7 +36,7 @@ void GetDiagnostics::processReq(JsonObject payload) { const char *startTimeRaw = payload["startTime"] | "Invalid"; if (!startTime.setTime(startTimeRaw)) { formatError = true; - Serial.println(F("[GetDiagnostics] Could not read startTime. Abort")); + AO_DBG_WARN("Could not read startTime. Abort"); return; } } @@ -45,7 +46,7 @@ void GetDiagnostics::processReq(JsonObject payload) { const char *stopTimeRaw = payload["stopTime"] | "Invalid"; if (!stopTime.setTime(stopTimeRaw)) { formatError = true; - Serial.println(F("[GetDiagnostics] Could not read stopTime. Abort")); + AO_DBG_WARN("Could not read stopTime. Abort"); return; } } @@ -55,7 +56,7 @@ std::unique_ptr GetDiagnostics::createConf(){ if (ocppModel && ocppModel->getDiagnosticsService()) { fileName = ocppModel->getDiagnosticsService()->requestDiagnosticsUpload(location, retries, retryInterval, startTime, stopTime); } else { - Serial.println(F("[GetDiagnostics] DiagnosticsService has not been initialized before! Please have a look at ArduinoOcpp.cpp for an example. Abort")); + AO_DBG_WARN("DiagnosticsService has not been initialized before! Please have a look at ArduinoOcpp.cpp for an example. Abort"); return nullptr; } diff --git a/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp b/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp index fbf0e20e..d019079c 100644 --- a/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp +++ b/src/ArduinoOcpp/MessagesV16/Heartbeat.cpp @@ -4,8 +4,8 @@ #include #include +#include #include -#include using ArduinoOcpp::Ocpp16::Heartbeat; @@ -27,12 +27,12 @@ void Heartbeat::processConf(JsonObject payload) { if (strcmp(currentTime, "Invalid")) { if (ocppModel && ocppModel->getOcppTime().setOcppTime(currentTime)) { //success - if (DEBUG_OUT) Serial.print(F("[Heartbeat] Request has been accepted!\n")); + AO_DBG_DEBUG("Request has been accepted"); } else { - Serial.print(F("[Heartbeat] Request accepted. But Error reading time string. Expect format like 2020-02-01T20:53:32.486Z\n")); + AO_DBG_WARN("Could not read time string. Expect format like 2020-02-01T20:53:32.486Z"); } } else { - Serial.print(F("[Heartbeat] Request denied. Missing field currentTime. Expect format like 2020-02-01T20:53:32.486Z\n")); + AO_DBG_WARN("Missing field currentTime. Expect format like 2020-02-01T20:53:32.486Z"); } } diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp index cb09d97c..22d228ca 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp @@ -5,8 +5,7 @@ #include #include #include - -#include +#include using ArduinoOcpp::Ocpp16::MeterValues; @@ -83,7 +82,7 @@ std::unique_ptr MeterValues::createReq() { } void MeterValues::processConf(JsonObject payload) { - if (DEBUG_OUT) Serial.print(F("[MeterValues] Request has been confirmed!\n")); + AO_DBG_DEBUG("Request has been confirmed"); } diff --git a/src/ArduinoOcpp/MessagesV16/Reset.cpp b/src/ArduinoOcpp/MessagesV16/Reset.cpp index b4b4a391..b4988c46 100644 --- a/src/ArduinoOcpp/MessagesV16/Reset.cpp +++ b/src/ArduinoOcpp/MessagesV16/Reset.cpp @@ -22,9 +22,6 @@ void Reset::processReq(JsonObject payload) { * a onSendConfListener in which you initiate a reset (e.g. calling ESP.reset() ) */ const char *type = payload["type"] | "Invalid"; - if (!strcmp(type, "Hard")){ - Serial.print(F("[Reset] Warning: received request to perform hard reset, but this implementation is only capable of soft reset!\n")); - } if (ocppModel && ocppModel->getChargePointStatusService()) { auto cpsService = ocppModel->getChargePointStatusService(); diff --git a/src/ArduinoOcpp/MessagesV16/Reset.h b/src/ArduinoOcpp/MessagesV16/Reset.h index 2405e028..e7dcfa83 100644 --- a/src/ArduinoOcpp/MessagesV16/Reset.h +++ b/src/ArduinoOcpp/MessagesV16/Reset.h @@ -7,8 +7,6 @@ #include -#include - namespace ArduinoOcpp { namespace Ocpp16 { diff --git a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp index 9a8678e2..da4c18d0 100644 --- a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp +++ b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp @@ -5,8 +5,7 @@ #include #include #include - -#include +#include using ArduinoOcpp::Ocpp16::SetChargingProfile; @@ -57,6 +56,6 @@ std::unique_ptr SetChargingProfile::createReq() { void SetChargingProfile::processConf(JsonObject payload) { const char* status = payload["status"] | "Invalid"; if (strcmp(status, "Accepted")) { - Serial.println(F("[SetChargingProfile] Send profile: rejected by client!")); + AO_DBG_WARN("Send profile: rejected by client"); } } diff --git a/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp b/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp index 17006456..9b464087 100644 --- a/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp +++ b/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp @@ -5,8 +5,7 @@ #include #include #include - -#include +#include using ArduinoOcpp::Ocpp16::UnlockConnector; @@ -34,7 +33,7 @@ void UnlockConnector::processReq(JsonObject payload) { cbDefined = true; } else { cbDefined = false; - if (DEBUG_OUT) Serial.println(F("[UnlockConnector] Unlock CB undefined")); + AO_DBG_WARN("Unlock CB undefined"); return; } diff --git a/src/ArduinoOcpp/MessagesV16/UnlockConnector.h b/src/ArduinoOcpp/MessagesV16/UnlockConnector.h index f07da4d7..d65dc559 100644 --- a/src/ArduinoOcpp/MessagesV16/UnlockConnector.h +++ b/src/ArduinoOcpp/MessagesV16/UnlockConnector.h @@ -5,8 +5,6 @@ #ifndef UNLOCKCONNECTOR_H #define UNLOCKCONNECTOR_H -#include - #include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp index bc5580a0..edc5e511 100644 --- a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp +++ b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp @@ -5,6 +5,7 @@ #include #include #include +#include using ArduinoOcpp::Ocpp16::UpdateFirmware; @@ -23,7 +24,7 @@ void UpdateFirmware::processReq(JsonObject payload) { //check location URL. Maybe introduce Same-Origin-Policy? if (location.isEmpty()) { formatError = true; - Serial.println(F("[UpdateFirmware] Could not read location. Abort")); + AO_DBG_WARN("Could not read location. Abort"); return; } @@ -31,7 +32,7 @@ void UpdateFirmware::processReq(JsonObject payload) { const char *retrieveDateRaw = payload["retrieveDate"] | "Invalid"; if (!retreiveDate.setTime(retrieveDateRaw)) { formatError = true; - Serial.println(F("[UpdateFirmware] Could not read retrieveDate. Abort")); + AO_DBG_WARN("Could not read retrieveDate. Abort"); return; } @@ -44,7 +45,7 @@ std::unique_ptr UpdateFirmware::createConf(){ auto fwService = ocppModel->getFirmwareService(); fwService->scheduleFirmwareUpdate(location, retreiveDate, retries, retryInterval); } else { - Serial.println(F("[UpdateFirmware] FirmwareService has not been initialized before! Please have a look at ArduinoOcpp.cpp for an example. Abort")); + AO_DBG_ERR("FirmwareService has not been initialized before! Please have a look at ArduinoOcpp.cpp for an example. Abort"); } return createEmptyDocument(); diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp index 894810a4..50d6fe24 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp @@ -6,11 +6,10 @@ #include #include #include +#include #include -#include - using namespace ArduinoOcpp; using Ocpp16::DiagnosticsStatus; @@ -25,11 +24,11 @@ void DiagnosticsService::loop() { if (!uploadIssued) { if (onUpload != nullptr) { - if (DEBUG_OUT) Serial.println(F("[DiagnosticsService] call onUpload")); + AO_DBG_DEBUG("Call onUpload"); onUpload(location, startTime, stopTime); uploadIssued = true; } else { - Serial.println(F("[DiagnosticsService] onUpload must be set! (see setOnUpload). Will abort")); + AO_DBG_ERR("onUpload must be set! (see setOnUpload). Will abort"); retries = 0; uploadIssued = false; } @@ -38,7 +37,7 @@ void DiagnosticsService::loop() { if (uploadIssued) { if (uploadStatusSampler != nullptr && uploadStatusSampler() == UploadStatus::Uploaded) { //success! - if (DEBUG_OUT) Serial.println(F("[DiagnosticsService] end update routine (by status)")); + AO_DBG_DEBUG("end upload routine (by status)") uploadIssued = false; retries = 0; } @@ -51,12 +50,12 @@ void DiagnosticsService::loop() { if (uploadStatusSampler == nullptr) { //No way to find out if failed. But maximum time has elapsed. Assume success - if (DEBUG_OUT) Serial.println(F("[DiagnosticsService] end update routine (by timer)")); + AO_DBG_DEBUG("end upload routine (by timer)"); uploadIssued = false; retries = 0; } else { //either we have UploadFailed status or (NotDownloaded + timeout) here - Serial.println(F("[DiagnosticsService] Upload timeout or failed!")); + AO_DBG_WARN("Upload timeout or failed"); const int TRANSITION_DELAY = 10; if (retryInterval <= UPLOAD_TIMEOUT + TRANSITION_DELAY) { nextTry = timestampNow; @@ -90,33 +89,29 @@ String DiagnosticsService::requestDiagnosticsUpload(String &location, int retrie this->stopTime = newStop; } - if (DEBUG_OUT) { - Serial.print(F("[DiagnosticsService] Scheduled Diagnostics upload!\n")); - Serial.print(F(" location = ")); - Serial.println(this->location); - Serial.print(F(" retries = ")); - Serial.print(this->retries); - Serial.print(F(", retryInterval = ")); - Serial.println(this->retryInterval); - Serial.print(F(" startTime = ")); - char dbuf [JSONDATE_LENGTH + 1] = {'\0'}; - this->startTime.toJsonString(dbuf, JSONDATE_LENGTH + 1); - Serial.println(dbuf); - Serial.print(F(" stopTime = ")); - this->stopTime.toJsonString(dbuf, JSONDATE_LENGTH + 1); - Serial.println(dbuf); - } + char dbuf [JSONDATE_LENGTH + 1] = {'\0'}; + char dbuf2 [JSONDATE_LENGTH + 1] = {'\0'}; + this->startTime.toJsonString(dbuf, JSONDATE_LENGTH + 1); + this->stopTime.toJsonString(dbuf2, JSONDATE_LENGTH + 1); + + AO_DBG_INFO("Scheduled Diagnostics upload!\n" \ + " location = %s\n" \ + " retries = %i" \ + ", retryInterval = %u" \ + " startTime = %s\n" \ + " stopTime = %s", + this->location.c_str(), + this->retries, + this->retryInterval, + dbuf, + dbuf2); nextTry = context.getOcppModel().getOcppTime().getOcppTimestampNow(); nextTry += 5; //wait for 5s before upload uploadIssued = false; - if (DEBUG_OUT) { - Serial.print(F("[DiagnosticsService] initial try at ")); - char dbuf [JSONDATE_LENGTH + 1] = {'\0'}; - nextTry.toJsonString(dbuf, JSONDATE_LENGTH + 1); - Serial.println(dbuf); - } + nextTry.toJsonString(dbuf, JSONDATE_LENGTH + 1); + AO_DBG_DEBUG("Initial try at %s", dbuf); return "diagnostics.log"; } diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp index bb023804..4452a6de 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp @@ -5,28 +5,21 @@ #include #include #include +#include using namespace ArduinoOcpp; MeteringService::MeteringService(OcppEngine& context, int numConn) - : context(context), numConnectors{numConn} { - - connectors = (ConnectorMeterValuesRecorder**) malloc(numConn * sizeof(ConnectorMeterValuesRecorder*)); - for (int i = 0; i < numConn; i++) { - connectors[i] = new ConnectorMeterValuesRecorder(context.getOcppModel(), i); - } -} + : context(context) { -MeteringService::~MeteringService() { - for (int i = 0; i < numConnectors; i++) { - delete connectors[i]; + for (int i = 0; i < numConn; i++) { + connectors.push_back(std::unique_ptr(new ConnectorMeterValuesRecorder(context.getOcppModel(), i))); } - free(connectors); } void MeteringService::loop(){ - for (int i = 0; i < numConnectors; i++){ + for (int i = 0; i < connectors.size(); i++){ auto meterValuesMsg = connectors[i]->loop(); if (meterValuesMsg != nullptr) { auto meterValues = makeOcppOperation(meterValuesMsg); @@ -37,24 +30,24 @@ void MeteringService::loop(){ } void MeteringService::setPowerSampler(int connectorId, PowerSampler ps){ - if (connectorId < 0 || connectorId >= numConnectors) { - Serial.print(F("[MeteringService] Error in setPowerSampler(): connectorId is out of bounds\n")); + if (connectorId < 0 || connectorId >= connectors.size()) { + AO_DBG_ERR("connectorId is out of bounds"); return; } connectors[connectorId]->setPowerSampler(ps); } void MeteringService::setEnergySampler(int connectorId, EnergySampler es){ - if (connectorId < 0 || connectorId >= numConnectors) { - Serial.print(F("[MeteringService] Error in setEnergySampler(): connectorId is out of bounds\n")); + if (connectorId < 0 || connectorId >= connectors.size()) { + AO_DBG_ERR("connectorId is out of bounds"); return; } connectors[connectorId]->setEnergySampler(es); } float MeteringService::readEnergyActiveImportRegister(int connectorId) { - if (connectorId < 0 || connectorId >= numConnectors) { - Serial.print(F("[MeteringService] Error in readEnergyActiveImportRegister(): connectorId is out of bounds\n")); + if (connectorId < 0 || connectorId >= connectors.size()) { + AO_DBG_ERR("connectorId is out of bounds"); return 0.f; } return connectors[connectorId]->readEnergyActiveImportRegister(); diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h index 00a8d2d4..360a654b 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h @@ -5,34 +5,25 @@ #ifndef METERINGSERVICE_H #define METERINGSERVICE_H -//#define METER_VALUE_SAMPLE_INTERVAL 60 //in seconds - -//#define METER_VALUES_SAMPLED_DATA_MAX_LENGTH 4 //after 4 measurements, send the values to the CS - #include -#include - #include namespace ArduinoOcpp { -using PowerSampler = std::function; -using EnergySampler = std::function; +using PowerSampler = std::function; //in Watts (W) +using EnergySampler = std::function; //in Watt-hours (Wh) class OcppEngine; class MeteringService { private: OcppEngine& context; - - const int numConnectors; - ConnectorMeterValuesRecorder **connectors; + + std::vector> connectors; public: MeteringService(OcppEngine& context, int numConnectors); - ~MeteringService(); - void loop(); void setPowerSampler(int connectorId, PowerSampler powerSampler); From 423e6105269d8f94295a882402f04667ca1e8663 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 18 Feb 2022 16:13:58 +0100 Subject: [PATCH 121/696] integrate NumberOfConnectors --- .../Tasks/ChargePointStatus/ChargePointStatusService.cpp | 8 +++++++- .../Tasks/ChargePointStatus/ChargePointStatusService.h | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index 1d04ad24..d70abc93 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -12,12 +13,17 @@ using namespace ArduinoOcpp; -ChargePointStatusService::ChargePointStatusService(OcppEngine& context, int numConn) +ChargePointStatusService::ChargePointStatusService(OcppEngine& context, unsigned int numConn) : context(context) { for (int i = 0; i < numConn; i++) { connectors.push_back(std::unique_ptr(new ConnectorStatus(context.getOcppModel(), i))); } + + + std::shared_ptr> numberOfConnectors = + declareConfiguration("NumberOfConnectors", numConn, CONFIGURATION_FN, false, true, true, false); + *numberOfConnectors = numConn; } ChargePointStatusService::~ChargePointStatusService() { diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h index 5013b8fe..737e5333 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h @@ -20,7 +20,7 @@ class ChargePointStatusService { bool booted = false; public: - ChargePointStatusService(OcppEngine& context, int numConnectors); + ChargePointStatusService(OcppEngine& context, unsigned int numConnectors); ~ChargePointStatusService(); From 2506fac7d82ce061fe39a1751720b703c0c71525 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 19 Feb 2022 15:12:24 +0100 Subject: [PATCH 122/696] log levels, revise mem management --- .../SmartCharging/SmartChargingModel.cpp | 279 ++++++------------ .../Tasks/SmartCharging/SmartChargingModel.h | 23 +- .../SmartCharging/SmartChargingService.cpp | 15 +- 3 files changed, 113 insertions(+), 204 deletions(-) diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp index 3ecebb94..78d0da95 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp @@ -3,16 +3,16 @@ // MIT License #include -#include +#include #include using namespace ArduinoOcpp; -ChargingSchedulePeriod::ChargingSchedulePeriod(JsonObject *json){ - startPeriod = (*json)["startPeriod"]; - limit = (*json)["limit"]; //one fractural digit at most - numberPhases = (*json)["numberPhases"] | -1; +ChargingSchedulePeriod::ChargingSchedulePeriod(JsonObject &json){ + startPeriod = json["startPeriod"]; + limit = json["limit"]; //one fractural digit at most + numberPhases = json["numberPhases"] | -1; } ChargingSchedulePeriod::ChargingSchedulePeriod(int startPeriod, float limit){ @@ -46,49 +46,45 @@ int ChargingSchedulePeriod::getNumberPhases(){ } void ChargingSchedulePeriod::printPeriod(){ - Serial.print(F(" startPeriod: ")); - Serial.print(startPeriod); - Serial.print(F("\n")); - - Serial.print(F(" limit: ")); - Serial.print(limit); - Serial.print(F("\n")); - - Serial.print(F(" numberPhases: ")); - Serial.print(numberPhases); - Serial.print(F("\n")); + AO_DBG_INFO("CHARGING SCHEDULE PERIOD:\n" \ + " startPeriod: %i\n" \ + " limit: %f\n" \ + " numberPhases: %i\n", + startPeriod, + limit, + numberPhases); } -ChargingSchedule::ChargingSchedule(JsonObject *json, ChargingProfileKindType chargingProfileKind, RecurrencyKindType recurrencyKind) +ChargingSchedule::ChargingSchedule(JsonObject &json, ChargingProfileKindType chargingProfileKind, RecurrencyKindType recurrencyKind) : chargingProfileKind(chargingProfileKind) , recurrencyKind(recurrencyKind) { - duration = (*json)["duration"] | -1; + duration = json["duration"] | -1; startSchedule = OcppTimestamp(); - if (!startSchedule.setTime((*json)["startSchedule"] | "Invalid")) { + if (!startSchedule.setTime(json["startSchedule"] | "Invalid")) { //non-success startSchedule = MIN_TIME; } - const char *unit = (*json)["chargingRateUnit"] | "__Invalid"; + const char *unit = json["chargingRateUnit"] | "__Invalid"; if (unit[0] == 'a' || unit[0] == 'A') { - schedulingUnit = 'A'; + chargingRateUnit = ChargingRateUnitType::Amp; } else { - schedulingUnit = 'W'; + chargingRateUnit = ChargingRateUnitType::Watt; } - chargingSchedulePeriod = std::vector(); - JsonArray periodJsonArray = (*json)["chargingSchedulePeriod"]; + JsonArray periodJsonArray = json["chargingSchedulePeriod"]; for (JsonObject periodJson : periodJsonArray) { - ChargingSchedulePeriod *period = new ChargingSchedulePeriod(&periodJson); - chargingSchedulePeriod.push_back(period); + auto period = std::unique_ptr(new ChargingSchedulePeriod(periodJson)); + chargingSchedulePeriod.push_back(std::move(period)); } //Expecting sorted list of periods but specification doesn't garantuee it - std::sort(chargingSchedulePeriod.begin(), chargingSchedulePeriod.end(), [] (ChargingSchedulePeriod *p1, ChargingSchedulePeriod *p2) { - return p1->getStartPeriod() < p2->getStartPeriod(); + std::sort(chargingSchedulePeriod.begin(), chargingSchedulePeriod.end(), + [] (const std::unique_ptr &p1, const std::unique_ptr &p2) { + return p1->getStartPeriod() < p2->getStartPeriod(); }); - minChargingRate = (*json)["minChargingRate"] | -1.0f; + minChargingRate = json["minChargingRate"] | -1.0f; } ChargingSchedule::ChargingSchedule(ChargingSchedule &other) { @@ -96,18 +92,17 @@ ChargingSchedule::ChargingSchedule(ChargingSchedule &other) { recurrencyKind = other.recurrencyKind; duration = other.duration; startSchedule = other.startSchedule; - schedulingUnit = other.schedulingUnit; - - chargingSchedulePeriod = std::vector(); + chargingRateUnit = other.chargingRateUnit; for (auto period = other.chargingSchedulePeriod.begin(); period != other.chargingSchedulePeriod.end(); period++) { - ChargingSchedulePeriod *p = new ChargingSchedulePeriod(**period); - chargingSchedulePeriod.push_back(p); + auto p = std::unique_ptr(new ChargingSchedulePeriod(**period)); + chargingSchedulePeriod.push_back(std::move(p)); } //Expecting sorted list of periods but specification doesn't garantuee it - std::sort(chargingSchedulePeriod.begin(), chargingSchedulePeriod.end(), [] (ChargingSchedulePeriod *p1, ChargingSchedulePeriod *p2) { - return p1->getStartPeriod() < p2->getStartPeriod(); + std::sort(chargingSchedulePeriod.begin(), chargingSchedulePeriod.end(), + [] (const std::unique_ptr &p1, const std::unique_ptr &p2) { + return p1->getStartPeriod() < p2->getStartPeriod(); }); minChargingRate = other.minChargingRate; @@ -117,28 +112,13 @@ ChargingSchedule::ChargingSchedule(const OcppTimestamp &startT, int duration) { //create empty but valid Charging Schedule this->duration = duration; startSchedule = startT; - schedulingUnit = 'W'; //either 'A' or 'W' - std::vector chargingSchedulePeriod = std::vector(); + chargingRateUnit = ChargingRateUnitType::Watt; //float minChargingRate = 0.f; chargingProfileKind = ChargingProfileKindType::Absolute; //copied from ChargingProfile to increase cohesion of limit inferencing methods recurrencyKind = RecurrencyKindType::NOT_SET; //copied from ChargingProfile to increase cohesion of limit inferencing methods } -ChargingSchedule::~ChargingSchedule(){ - for (size_t i = 0; i < chargingSchedulePeriod.size(); i++) { - delete chargingSchedulePeriod.at(i); - } -} - -float maximum(float f1, float f2){ - if (f1 < f2) { - return f2; - } else { - return f1; - } -} - bool ChargingSchedule::inferenceLimit(const OcppTimestamp &t, const OcppTimestamp &startOfCharging, float *limit, OcppTimestamp *nextChange) { OcppTimestamp basis = OcppTimestamp(); //point in time to which schedule-related times are relative *nextChange = MAX_TIME; //defaulted to Infinity @@ -157,7 +137,7 @@ bool ChargingSchedule::inferenceLimit(const OcppTimestamp &t, const OcppTimestam } else if (startOfCharging > MIN_TIME && startOfCharging < t) { basis = startOfCharging; } else { - Serial.print(F("[SmartChargingModel] Undefined behaviour: Inferencing limit from absolute profile, but neither startSchedule, nor start of charging are set! Abort\n")); + AO_DBG_ERR("Absolute profile, but neither startSchedule, nor start of charging are set. Undefined behavior, abort"); return false; } break; @@ -169,7 +149,7 @@ bool ChargingSchedule::inferenceLimit(const OcppTimestamp &t, const OcppTimestam basis = t - ((t - startSchedule) % (7 * 24 * 3600)); *nextChange = basis + (7 * 24 * 3600); } else { - Serial.print(F("[SmartChargingModel] Undefined behaviour: Recurring ChargingProfile but no RecurrencyKindType set! Assume \"Daily\"")); + AO_DBG_ERR("Recurring ChargingProfile but no RecurrencyKindType set. Undefined behavior, assume 'Daily'"); basis = t - ((t - startSchedule) % (24 * 3600)); *nextChange = basis + (24 * 3600); } @@ -186,7 +166,7 @@ bool ChargingSchedule::inferenceLimit(const OcppTimestamp &t, const OcppTimestam } if (t < basis) { //check for error - Serial.print(F("[SmartChargingModel] Error in SchmartChargingModel::inferenceLimit: time basis is smaller than t, but t must be >= basis\n")); + AO_DBG_ERR("time basis is smaller than t, but t must be >= basis"); return false; } @@ -223,21 +203,21 @@ bool ChargingSchedule::inferenceLimit(const OcppTimestamp &t, const OcppTimestam } if (limit_res >= 0.0f) { - *limit = maximum(limit_res, minChargingRate); + *limit = std::max(limit_res, minChargingRate); return true; } else { return false; //No limit was found. Either there is no ChargingProfilePeriod, or each period begins after t_toBasis } } -bool ChargingSchedule::addChargingSchedulePeriod(ChargingSchedulePeriod *period) { - if (period == NULL) return false; +bool ChargingSchedule::addChargingSchedulePeriod(std::unique_ptr period) { + if (!period) return false; if (period->getStartPeriod() >= duration) { return false; } - chargingSchedulePeriod.push_back(period); + chargingSchedulePeriod.push_back(std::move(period)); return true; } @@ -257,7 +237,6 @@ DynamicJsonDocument *ChargingSchedule::toJsonDocument() { size_t capacity = 0; capacity += JSON_OBJECT_SIZE(5); //no of fields of ChargingSchedule capacity += JSONDATE_LENGTH + 1; //startSchedule - capacity += 2; //scheduling unit will be saved as string capacity += JSON_ARRAY_SIZE(chargingSchedulePeriod.size()) + chargingSchedulePeriod.size() * JSON_OBJECT_SIZE(3); DynamicJsonDocument *result = new DynamicJsonDocument(capacity); @@ -267,9 +246,7 @@ DynamicJsonDocument *ChargingSchedule::toJsonDocument() { char startScheduleJson [JSONDATE_LENGTH + 1] = {'\0'}; startSchedule.toJsonString(startScheduleJson, JSONDATE_LENGTH + 1); payload["startSchedule"] = startScheduleJson; - char chargingRateUnit_str [2] = {'\0'}; - chargingRateUnit_str[0] = schedulingUnit; - payload["chargingRateUnit"] = chargingRateUnit_str; + payload["chargingRateUnit"] = chargingRateUnit == (ChargingRateUnitType::Amp) ? "A" : "W"; JsonArray periodArray = payload.createNestedArray("chargingSchedulePeriod"); for (auto period = chargingSchedulePeriod.begin(); period != chargingSchedulePeriod.end(); period++) { JsonObject entry = periodArray.createNestedObject(); @@ -286,47 +263,33 @@ DynamicJsonDocument *ChargingSchedule::toJsonDocument() { } void ChargingSchedule::printSchedule(){ - Serial.print(F(" duration: ")); - Serial.print(duration); - Serial.print(F("\n")); - - Serial.print(F(" startSchedule: ")); + char tmp[JSONDATE_LENGTH + 1] = {'\0'}; startSchedule.toJsonString(tmp, JSONDATE_LENGTH + 1); - Serial.print(tmp); - Serial.print(F("\n")); - Serial.print(F(" schedulingUnit: ")); - Serial.print(schedulingUnit); - Serial.print(F("\n")); + AO_DBG_INFO("CHARGING SCHEDULE:\n" \ + " duration: %i\n" \ + " startSchedule: %s\n" \ + " chargingRateUnit: %s\n" \ + " minChargingRate: %f\n", + duration, + tmp, + chargingRateUnit == (ChargingRateUnitType::Amp) ? "A" : + chargingRateUnit == (ChargingRateUnitType::Watt) ? "W" : "Error", + minChargingRate); for (auto period = chargingSchedulePeriod.begin(); period != chargingSchedulePeriod.end(); period++) { (*period)->printPeriod(); } - - Serial.print(F(" minChargingRate: ")); - Serial.print(minChargingRate); - Serial.print(F("\n")); } -ChargingProfile::ChargingProfile(JsonObject *json){ +ChargingProfile::ChargingProfile(JsonObject &json){ - chargingProfileId = (*json)["chargingProfileId"]; - transactionId = (*json)["transactionId"] | -1; - stackLevel = (*json)["stackLevel"]; - - if (DEBUG_OUT) { - Serial.print(F("[SmartChargingModel] ChargingProfile created with chargingProfileId = ")); - Serial.print(chargingProfileId); - Serial.print(F("\n")); - } + chargingProfileId = json["chargingProfileId"] | -1; + transactionId = json["transactionId"] | -1; + stackLevel = json["stackLevel"] | 0; - const char *chargingProfilePurposeStr = (*json)["chargingProfilePurpose"] | "Invalid"; - if (DEBUG_OUT) { - Serial.print(F("[SmartChargingModel] chargingProfilePurposeStr=")); - Serial.print(chargingProfilePurposeStr); - Serial.print(F("\n")); - } + const char *chargingProfilePurposeStr = json["chargingProfilePurpose"] | "Invalid"; if (!strcmp(chargingProfilePurposeStr, "ChargePointMaxProfile")) { chargingProfilePurpose = ChargingProfilePurposeType::ChargePointMaxProfile; } else if (!strcmp(chargingProfilePurposeStr, "TxDefaultProfile")) { @@ -335,7 +298,7 @@ ChargingProfile::ChargingProfile(JsonObject *json){ } else { chargingProfilePurpose = ChargingProfilePurposeType::TxProfile; } - const char *chargingProfileKindStr = (*json)["chargingProfileKind"] | "Invalid"; + const char *chargingProfileKindStr = json["chargingProfileKind"] | "Invalid"; if (!strcmp(chargingProfileKindStr, "Absolute")) { chargingProfileKind = ChargingProfileKindType::Absolute; } else if (!strcmp(chargingProfileKindStr, "Recurring")) { @@ -344,12 +307,7 @@ ChargingProfile::ChargingProfile(JsonObject *json){ } else { chargingProfileKind = ChargingProfileKindType::Relative; } - const char *recurrencyKindStr = (*json)["recurrencyKind"] | "Invalid"; - if (DEBUG_OUT) { - Serial.print(F("[SmartChargingModel] recurrencyKindStr=")); - Serial.print(recurrencyKindStr); - Serial.print('\n'); - } + const char *recurrencyKindStr = json["recurrencyKind"] | "Invalid"; if (!strcmp(recurrencyKindStr, "Daily")) { recurrencyKind = RecurrencyKindType::Daily; } else if (!strcmp(recurrencyKindStr, "Weekly")) { @@ -358,42 +316,22 @@ ChargingProfile::ChargingProfile(JsonObject *json){ recurrencyKind = RecurrencyKindType::NOT_SET; //not part of OCPP 1.6 } - if (!validFrom.setTime((*json)["validFrom"] | "Invalid")) { + AO_DBG_DEBUG("Deserialize JSON: chargingProfileId=%i, chargingProfilePurpose=%s, recurrencyKind=%s", chargingProfileId, chargingProfilePurposeStr, recurrencyKindStr); + + if (!validFrom.setTime(json["validFrom"] | "Invalid")) { //non-success - Serial.print(F("[SmartChargingModel] Error reading validFrom. Expect format like 2020-02-01T20:53:32.486Z. Assume unlimited validity\n")); + AO_DBG_ERR("validFrom format violation. Expect format like 2022-02-01T20:53:32.486Z. Assume unlimited validity"); validFrom = MIN_TIME; } - if (!validTo.setTime((*json)["validTo"] | "Invalid")) { + if (!validTo.setTime(json["validTo"] | "Invalid")) { //non-success - Serial.print(F("[SmartChargingModel] Error reading validTo. Expect format like 2020-02-01T20:53:32.486Z. Assume unlimited validity\n")); + AO_DBG_ERR("validTo format violation. Expect format like 2022-02-01T20:53:32.486Z. Assume unlimited validity"); validTo = MIN_TIME; } - JsonObject schedule = (*json)["chargingSchedule"]; - chargingSchedule = new ChargingSchedule(&schedule, chargingProfileKind, recurrencyKind); -} - -ChargingProfile::~ChargingProfile(){ - delete chargingSchedule; -} - -/** - * Modulo, the mathematical way - * - * dividend: -4 -3 -2 -1 0 1 2 3 4 - * % divisor: 3 3 3 3 3 3 3 3 3 - * = remainder: 2 0 1 2 0 1 2 0 1 - */ -int modulo(int dividend, int divisor){ - int remainder = dividend; - while (remainder < 0) { - remainder += divisor; - } - while (remainder >= divisor) { - remainder -= divisor; - } - return remainder; + JsonObject schedule = json["chargingSchedule"]; + chargingSchedule = std::unique_ptr(new ChargingSchedule(schedule, chargingProfileKind, recurrencyKind)); } bool ChargingProfile::inferenceLimit(const OcppTimestamp &t, const OcppTimestamp &startOfCharging, float *limit, OcppTimestamp *nextChange){ @@ -437,67 +375,36 @@ int ChargingProfile::getChargingProfileId() { } void ChargingProfile::printProfile(){ - Serial.print(F(" chargingProfileId: ")); - Serial.print(chargingProfileId); - Serial.print(F("\n")); - - Serial.print(F(" transactionId: ")); - Serial.print(transactionId); - Serial.print(F("\n")); - - Serial.print(F(" stackLevel: ")); - Serial.print(stackLevel); - Serial.print(F("\n")); - - Serial.print(F(" chargingProfilePurpose: ")); - switch (chargingProfilePurpose) { - case (ChargingProfilePurposeType::ChargePointMaxProfile): - Serial.print(F("ChargePointMaxProfile")); - break; - case (ChargingProfilePurposeType::TxDefaultProfile): - Serial.print(F("TxDefaultProfile")); - break; - case (ChargingProfilePurposeType::TxProfile): - Serial.print(F("TxProfile")); - break; - } - Serial.print(F("\n")); - - Serial.print(F(" chargingProfileKind: ")); - switch (chargingProfileKind) { - case (ChargingProfileKindType::Absolute): - Serial.print(F("Absolute")); - break; - case (ChargingProfileKindType::Recurring): - Serial.print(F("Recurring")); - break; - case (ChargingProfileKindType::Relative): - Serial.print(F("Relative")); - break; - } - Serial.print(F("\n")); - - Serial.print(F(" recurrencyKind: ")); - switch (recurrencyKind) { - case (RecurrencyKindType::Daily): - Serial.print(F("Daily")); - break; - case (RecurrencyKindType::Weekly): - Serial.print(F("Weekly")); - break; - case (RecurrencyKindType::NOT_SET): - Serial.print(F("NOT_SET")); - break; - } - Serial.print(F("\n")); - Serial.print(F(" validFrom: ")); char tmp[JSONDATE_LENGTH + 1] = {'\0'}; validFrom.toJsonString(tmp, JSONDATE_LENGTH + 1); - Serial.print(F("\n")); + char tmp2[JSONDATE_LENGTH + 1] = {'\0'}; + validTo.toJsonString(tmp2, JSONDATE_LENGTH + 1); + + AO_DBG_INFO("CHARGING PROFILE:\n" \ + " chargingProfileId: %i\n" \ + " transactionId: %i\n" \ + " stackLevel: %i\n" \ + " chargingProfilePurpose: %s\n" \ + " chargingProfileKind: %s\n" \ + " recurrencyKind: %s\n" \ + " validFrom: %s\n" \ + " validTo: %s\n", + chargingProfileId, + transactionId, + stackLevel, + chargingProfilePurpose == (ChargingProfilePurposeType::ChargePointMaxProfile) ? "ChargePointMaxProfile" : + chargingProfilePurpose == (ChargingProfilePurposeType::TxDefaultProfile) ? "TxDefaultProfile" : + chargingProfilePurpose == (ChargingProfilePurposeType::TxProfile) ? "TxProfile" : "Error", + chargingProfileKind == (ChargingProfileKindType::Absolute) ? "Absolute" : + chargingProfileKind == (ChargingProfileKindType::Recurring) ? "Recurring" : + chargingProfileKind == (ChargingProfileKindType::Relative) ? "Relative" : "Error", + recurrencyKind == (RecurrencyKindType::Daily) ? "Daily" : + recurrencyKind == (RecurrencyKindType::Weekly) ? "Weekly" : + recurrencyKind == (RecurrencyKindType::NOT_SET) ? "NOT_SET" : "Error", + tmp, + tmp2 + ); - Serial.print(F(" validTo: ")); - validTo.toJsonString(tmp, JSONDATE_LENGTH + 1); - Serial.print(F("\n")); chargingSchedule->printSchedule(); } diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h index 7d3fc139..f97c4617 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h @@ -7,6 +7,7 @@ #include #include +#include namespace ArduinoOcpp { @@ -28,13 +29,18 @@ enum class RecurrencyKindType { Weekly }; +enum class ChargingRateUnitType { + Watt, + Amp +}; + class ChargingSchedulePeriod { private: int startPeriod; float limit; //one fractural digit at most int numberPhases = -1; public: - ChargingSchedulePeriod(JsonObject *json); + ChargingSchedulePeriod(JsonObject &json); ChargingSchedulePeriod(int startPeriod, float limit); int getStartPeriod(); float getLimit(); @@ -48,17 +54,16 @@ class ChargingSchedule { private: int duration = -1; OcppTimestamp startSchedule; - char schedulingUnit; //either 'A' or 'W' - std::vector chargingSchedulePeriod; + ChargingRateUnitType chargingRateUnit; + std::vector> chargingSchedulePeriod; float minChargingRate = -1.0f; ChargingProfileKindType chargingProfileKind; //copied from ChargingProfile to increase cohesion of limit inferencing methods RecurrencyKindType recurrencyKind; //copied from ChargingProfile to increase cohesion of limit inferencing methods public: - ChargingSchedule(JsonObject *json, ChargingProfileKindType chargingProfileKind, RecurrencyKindType recurrencyKind); + ChargingSchedule(JsonObject &json, ChargingProfileKindType chargingProfileKind, RecurrencyKindType recurrencyKind); ChargingSchedule(ChargingSchedule &other); ChargingSchedule(const OcppTimestamp &startSchedule, int duration); - ~ChargingSchedule(); /** * limit: output parameter @@ -70,7 +75,7 @@ class ChargingSchedule { */ bool inferenceLimit(const OcppTimestamp &t, const OcppTimestamp &startOfCharging, float *limit, OcppTimestamp *nextChange); - bool addChargingSchedulePeriod(ChargingSchedulePeriod *period); + bool addChargingSchedulePeriod(std::unique_ptr period); void scale(float factor); void translate(float offset); @@ -93,11 +98,9 @@ class ChargingProfile { RecurrencyKindType recurrencyKind; // copied to ChargingSchedule to increase cohesion OcppTimestamp validFrom; OcppTimestamp validTo; - ChargingSchedule *chargingSchedule; + std::unique_ptr chargingSchedule; public: - ChargingProfile(JsonObject *json); - ChargingProfile(ChargingSchedule *schedule, int chargingProfileId); //For Energy management. Can be extended with more parameters later - ~ChargingProfile(); + ChargingProfile(JsonObject &json); /** * limit: output parameter diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index a4e8543f..9611f1ac 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -8,8 +8,8 @@ #include #include #include - #include +#include #if defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) #include @@ -206,9 +206,8 @@ ChargingSchedule *SmartChargingService::getCompositeSchedule(int connectorId, ot while (periodBegin - startSchedule < duration) { float limit = 0.f; inferenceLimit(periodBegin, &limit, &periodStop); - ChargingSchedulePeriod *p = new ChargingSchedulePeriod(periodBegin - startSchedule, limit); - if (!result->addChargingSchedulePeriod(p)) { - delete p; + auto p = std::unique_ptr(new ChargingSchedulePeriod(periodBegin - startSchedule, limit)); + if (!result->addChargingSchedulePeriod(std::move(p))) { break; } periodBegin = periodStop; @@ -244,16 +243,16 @@ void SmartChargingService::updateChargingProfile(JsonObject *json) { } ChargingProfile *SmartChargingService::updateProfileStack(JsonObject *json){ - ChargingProfile *chargingProfile = new ChargingProfile(json); + ChargingProfile *chargingProfile = new ChargingProfile(*json); - if (DEBUG_OUT) { - Serial.print(F("[SmartChargingService] Charging Profile internal model\n")); + if (AO_DBG_LEVEL >= AO_DL_INFO) { + AO_DBG_INFO("Charging Profile internal model:"); chargingProfile->printProfile(); } int stackLevel = chargingProfile->getStackLevel(); if (stackLevel >= CHARGEPROFILEMAXSTACKLEVEL || stackLevel < 0) { - Serial.print(F("[SmartChargingService] Error: Stacklevel of Charging Profile is smaller or greater than CHARGEPROFILEMAXSTACKLEVEL\n")); + AO_DBG_ERR("Stacklevel of Charging Profile is smaller or greater than CHARGEPROFILEMAXSTACKLEVEL"); stackLevel = CHARGEPROFILEMAXSTACKLEVEL - 1; } From d0afaf82ecbf8373312309671711401aac118cba Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 21 Feb 2022 16:05:33 +0100 Subject: [PATCH 123/696] integrate log levels --- .../Core/ConfigurationContainerFlash.cpp | 50 +++++------ .../Core/ConfigurationKeyValue.cpp | 83 +++++++++---------- .../SmartCharging/SmartChargingService.cpp | 78 +++++------------ .../SmartCharging/SmartChargingService.h | 2 - 4 files changed, 83 insertions(+), 130 deletions(-) diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp index 7c042e92..d10adbf3 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp @@ -3,7 +3,7 @@ // MIT License #include -#include +#include #if defined(ESP32) #define USE_FS LITTLEFS @@ -28,24 +28,24 @@ bool ConfigurationContainerFlash::load() { #ifndef AO_DEACTIVATE_FLASH if (configurations.size() > 0) { - Serial.print(F("[ConfigurationsContainerFlash] Error: declared configurations before calling container->load().\n" \ - " All previously declared values won't be written back\n")); + AO_DBG_ERR("Error: declared configurations before calling container->load(). " \ + "All previously declared values won't be written back"); } if (!USE_FS.exists(getFilename())) { - if (DEBUG_OUT) Serial.print(F("[Configuration] Populate FS: create configuration file\n")); + AO_DBG_DEBUG("Populate FS: create configuration file"); return true; } File file = USE_FS.open(getFilename(), "r"); if (!file) { - Serial.print(F("[Configuration] Unable to initialize: could not open configuration file\n")); + AO_DBG_ERR("Unable to initialize: could not open configuration file %s", getFilename()); return false; } if (!file.available()) { - Serial.print(F("[Configuration] Populate FS: create configuration file\n")); + AO_DBG_DEBUG("Populate FS: create configuration file"); file.close(); return true; } @@ -53,32 +53,32 @@ bool ConfigurationContainerFlash::load() { int file_size = file.size(); if (file_size < 2) { - Serial.print(F("[Configuration] Unable to initialize: too short for json\n")); + AO_DBG_ERR("Unable to initialize: too short for json"); file.close(); return false; } else if (file_size > MAX_FILE_SIZE) { - Serial.print(F("[Configuration] Unable to initialize: filesize is too long!\n")); + AO_DBG_ERR("Unable to initialize: filesize is too long"); file.close(); return false; } String token = file.readStringUntil('\n'); if (!token.equals("content-type:arduino-ocpp_configuration_file")) { - Serial.print(F("[Configuration] Unable to initialize: unrecognized configuration file format\n")); + AO_DBG_ERR("Unable to initialize: unrecognized configuration file format"); file.close(); return false; } token = file.readStringUntil('\n'); if (!token.equals("version:1.0")) { - Serial.print(F("[Configuration] Unable to initialize: unsupported version\n")); + AO_DBG_ERR("Unable to initialize: unsupported version"); file.close(); return false; } token = file.readStringUntil(':'); if (!token.equals("configurations_len")) { - Serial.print(F("[Configuration] Unable to initialize: missing length statement\n")); + AO_DBG_ERR("Unable to initialize: missing length statement"); file.close(); return false; } @@ -86,29 +86,25 @@ bool ConfigurationContainerFlash::load() { token = file.readStringUntil('\n'); int configurations_len = token.toInt(); if (configurations_len <= 0) { - Serial.print(F("[Configuration] Initialization: Empty configuration\n")); + AO_DBG_ERR("Unable to initialize: empty configuration"); file.close(); return true; } if (configurations_len > MAX_CONFIGURATIONS) { - Serial.print(F("[Configuration] Unable to initialize: configurations_len is too big!\n")); + AO_DBG_ERR("Unable to initialize: configurations_len is too big"); file.close(); return false; } size_t jsonCapacity = file_size + JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(configurations_len) + configurations_len * JSON_OBJECT_SIZE(5); - if (DEBUG_OUT) Serial.print(F("[Configuration] Config capacity = ")); - if (DEBUG_OUT) Serial.print(jsonCapacity); - if (DEBUG_OUT) Serial.print(F("\n")); + AO_DBG_DEBUG("Config capacity = %zu", jsonCapacity); DynamicJsonDocument configDoc(jsonCapacity); DeserializationError error = deserializeJson(configDoc, file); if (error) { - Serial.println(F("[Configuration] Unable to initialize: config file deserialization failed: ")); - Serial.print(error.c_str()); - Serial.print(F("\n")); + AO_DBG_ERR("Unable to initialize: config file deserialization failed: %s", error.c_str()); file.close(); return false; } @@ -125,18 +121,12 @@ bool ConfigurationContainerFlash::load() { configuration = std::make_shared>(config); } else if (!strcmp(type, SerializedType::get())){ configuration = std::make_shared>(config); -// } else if (!strcmp(type, SerializedType::get())){ -// configuration = std::make_shared>(config); } if (configuration) { configurations.push_back(configuration); } else { - Serial.print(F("[Configuration] Initialization fault: could not read key-value pair ")); - Serial.print(config["key"].as()); - Serial.print(F(" of type ")); - Serial.print(config["type"].as()); - Serial.print(F("\n")); + AO_DBG_ERR("Initialization fault: could not read key-value pair %s of type %s", config["key"].as(), config["type"].as()); } } @@ -144,7 +134,7 @@ bool ConfigurationContainerFlash::load() { configurationsUpdated(); - if (DEBUG_OUT) Serial.println(F("[Configuration] Initialization successful")); + AO_DBG_DEBUG("Initialization successful"); #endif //ndef AO_DEACTIVATE_FLASH return true; } @@ -163,7 +153,7 @@ bool ConfigurationContainerFlash::save() { File file = USE_FS.open(getFilename(), "w"); if (!file) { - Serial.print(F("[Configuration] Unable to save: could not open configuration file\n")); + AO_DBG_ERR("Unable to save: could not open configuration file %s", getFilename()); return false; } @@ -200,14 +190,14 @@ bool ConfigurationContainerFlash::save() { // Serialize JSON to file if (serializeJson(configDoc, file) == 0) { - Serial.println(F("[Configuration] Unable to save: Could not serialize JSON\n")); + AO_DBG_ERR("Unable to save: Could not serialize JSON"); file.close(); return false; } //success file.close(); - Serial.print(F("[Configuration] Saving configDoc successful\n")); + AO_DBG_DEBUG("Saving configDoc successful"); #endif //ndef AO_DEACTIVATE_FLASH return true; diff --git a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp index ab3b7b2f..776924ff 100644 --- a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp @@ -3,8 +3,7 @@ // MIT License #include - -#include +#include #include #include @@ -31,7 +30,7 @@ AbstractConfiguration::AbstractConfiguration(JsonObject &storedKeyValuePair) { if (storedKeyValuePair["key"].as().is()) { setKey(storedKeyValuePair["key"]); } else { - Serial.print(F("[AbstractConfiguration] Type mismatch: cannot construct with given storedKeyValuePair\n")); + AO_DBG_ERR("Type mismatch"); } } @@ -43,9 +42,8 @@ AbstractConfiguration::~AbstractConfiguration() { } void AbstractConfiguration::printKey() { - if (key != nullptr) { - Serial.print(key); - } + if (key) + AO_CONSOLE_PRINTF("%s", key); } size_t AbstractConfiguration::getStorageHeaderJsonCapacity() { @@ -79,25 +77,23 @@ bool AbstractConfiguration::isValid() { bool AbstractConfiguration::setKey(const char *newKey) { if (key != nullptr || key_size > 0) { - Serial.print(F("[AbstractConfiguration] Cannot change key or set key twice! Keep old value\n")); + AO_DBG_ERR("Cannot change key or set key twice! Keep old value"); return false; } key_size = strlen(newKey) + 1; //plus 0-terminator if (key_size > KEY_MAXLEN + 1) { - Serial.print(F("[AbstractConfiguration] in setKey(): Maximal key length exceeded: ")); - Serial.print(newKey); - Serial.print(F(". Abort\n")); + AO_DBG_ERR("Maximal key length exceeded: %s. Abort", newKey); return false; } else if (key_size <= 1) { - Serial.print(F("[AbstractConfiguration] in setKey(): Null or empty key not allowed! Abort\n")); + AO_DBG_ERR("Null or empty key not allowed! Abort"); return false; } key = (char *) malloc(sizeof(char) * key_size); if (!key) { - Serial.print(F("[AbstractConfiguration] in setKey(): Could not allocate key\n")); + AO_DBG_ERR("Could not allocate key"); return false; } @@ -151,19 +147,30 @@ Configuration::Configuration(JsonObject &storedKeyValuePair) : AbstractConfig if (jsonEntry.is()) { this->operator=(jsonEntry.as()); } else { - Serial.print(F("[Configuration] Type mismatch: cannot deserialize Json to given Type T\n")); + AO_DBG_ERR("Type mismatch: cannot deserialize Json to given type"); } } +//helper functions +void printValue(int value) { + AO_CONSOLE_PRINTF("%i", value); +} + +void printValue(float value) { + AO_CONSOLE_PRINTF("%f", value); +} + template const T &Configuration::operator=(const T & newVal) { if (permissionLocalClientCanWrite() || !initializedValue) { - if (DEBUG_OUT && !initializedValue) { - Serial.print(F("[Configuration] Initialized config object. Key = ")); + if (AO_DBG_LEVEL >= AO_DL_DEBUG && !initializedValue) { + AO_DBG_DEBUG("Initialized config object:"); + AO_CONSOLE_PRINTF("[AO] > Key = "); printKey(); - Serial.print(F(", value = ")); - Serial.println(newVal); + AO_CONSOLE_PRINTF(", value = "); + printValue(newVal); + AO_CONSOLE_PRINTF("\n"); } initializedValue = true; if (value != newVal) { @@ -172,20 +179,16 @@ const T &Configuration::operator=(const T & newVal) { value = newVal; resetToBeRemovedFlag(); } else { - Serial.print(F("[Configuration] Tried to override read-only configuration: ")); + AO_DBG_ERR("Tried to override read-only configuration:"); + AO_CONSOLE_PRINTF("[AO] > Key = "); printKey(); - Serial.println(); + AO_CONSOLE_PRINTF("\n"); } return newVal; } template Configuration::operator T() { - if (!initializedValue) { -// Serial.print(F("[Configuration] Tried to access value without preceeding initialization: ")); -// printKey(); -// Serial.println(); - } return value; } @@ -199,11 +202,6 @@ size_t Configuration::getValueJsonCapacity() { return JSON_OBJECT_SIZE(1); } -//template<> -//size_t Configuration::getValueJsonCapacity() { -// return JSON_OBJECT_SIZE(1) + value.length() + 1; -//} - size_t Configuration::getValueJsonCapacity() { return JSON_OBJECT_SIZE(1) + value_size; } @@ -280,10 +278,10 @@ Configuration::Configuration(JsonObject &storedKeyValuePair) : Abs storedValueSize++; setValue(storedValue, storedValueSize); } else { - Serial.print(F("[Configuration] Stored value is empty\n")); + AO_DBG_WARN("Stored value is empty"); } } else { - Serial.print(F("[Configuration] Type mismatch: cannot deserialize Json to given Type T\n")); + AO_DBG_ERR("Type mismatch: cannot deserialize Json to given type"); } } @@ -300,14 +298,13 @@ Configuration::~Configuration() { bool Configuration::setValue(const char *new_value, size_t buffsize) { if (!permissionLocalClientCanWrite() && initializedValue) { - Serial.print(F("[Configuration] Tried to override read-only configuration: ")); - printKey(); - Serial.println(); + AO_DBG_ERR("Tried to override read-only configuration:"); + AO_CONSOLE_PRINTF("[AO] > Key = "); return false; } if (!new_value) { - Serial.print(F("[Configuration] in setValue(): Argument is null! No change\n")); + AO_DBG_ERR("Argument is null. No change"); return false; } @@ -328,12 +325,12 @@ bool Configuration::setValue(const char *new_value, size_t buffsiz } if (checkedBuffsize <= 0) { - Serial.print(F("[Configuration] in setValue(): buffsize is <= 0! No change\n")); + AO_DBG_ERR("buffsize is <= 0 -- No change"); return false; } if (checkedBuffsize > STRING_VAL_MAXLEN + 1) { - Serial.print(F("[Configuration] in setValue(): Maximal value length exceeded! Abort\n")); + AO_DBG_ERR("Maximum value length exceeded. Abort"); return false; } @@ -366,7 +363,7 @@ bool Configuration::setValue(const char *new_value, size_t buffsiz value = (char *) malloc (sizeof(char) * value_size); valueReadOnlyCopy = (char *) malloc (sizeof(char) * value_size); if (!value || !valueReadOnlyCopy) { - Serial.print(F("[Configuration] in setValue(): Could not allocate value or value copy\n")); + AO_DBG_ERR("Could not allocate value or value copy"); if (value != nullptr) { free(value); @@ -391,11 +388,11 @@ bool Configuration::setValue(const char *new_value, size_t buffsiz value_revision++; } - if (DEBUG_OUT && !initializedValue) { - Serial.print(F("[Configuration] Initialized config object. Key = ")); + if (AO_DBG_LEVEL >= AO_DL_DEBUG && !initializedValue) { + AO_DBG_DEBUG("Initialized config object:"); + AO_CONSOLE_PRINTF("[AO] > Key = "); printKey(); - Serial.print(F(", value = ")); - Serial.println(value); + AO_CONSOLE_PRINTF(", value = %s\n", value); } initializedValue = true; resetToBeRemovedFlag(); @@ -404,7 +401,7 @@ bool Configuration::setValue(const char *new_value, size_t buffsiz const char *Configuration::operator=(const char *newVal) { if (!setValue(newVal, strlen(newVal) + 1)) { - Serial.print(F("[Configuration] Setting value in operator= was unsuccessful!\n")); + AO_DBG_ERR("Setting value in operator= was unsuccessful"); } return newVal; } diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index 9611f1ac..cd9ba7fe 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -2,8 +2,6 @@ // Copyright Matthias Akstaller 2019 - 2022 // MIT License -#include - #include #include #include @@ -32,14 +30,13 @@ SmartChargingService::SmartChargingService(OcppEngine& context, float chargeLimi : context(context), DEFAULT_CHARGE_LIMIT{chargeLimit}, V_eff{V_eff}, filesystemOpt{filesystemOpt} { if (numConnectors > 2) { - Serial.print(F("[SmartChargingService] Error: Unfortunately, multiple connectors are not implemented in SmartChargingService yet. Only connector 1 will receive charging limits\n")); + AO_DBG_ERR("Only one connector supported at the moment"); } limitBeforeChange = -1.0f; nextChange = MIN_TIME; chargingSessionStart = MAX_TIME; chargingSessionTransactionID = -1; - //chargingSessionIsActive = false; for (int i = 0; i < CHARGEPROFILEMAXSTACKLEVEL; i++) { ChargePointMaxProfile[i] = NULL; TxDefaultProfile[i] = NULL; @@ -63,19 +60,15 @@ void SmartChargingService::loop(){ OcppTimestamp validTo = OcppTimestamp(); inferenceLimit(tNow, &limit, &validTo); - if (DEBUG_OUT) { - Serial.print(F("[SmartChargingService] New Limit! Values: {scheduled at = ")); - - char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; - nextChange.toJsonString(timestamp, JSONDATE_LENGTH + 1); - Serial.print(timestamp); - Serial.print(F(", nextChange = ")); - validTo.toJsonString(timestamp, JSONDATE_LENGTH + 1); - Serial.print(timestamp); - Serial.print(F(", limit = ")); - Serial.print(limit); - Serial.print(F("}\n")); - } +#if (AO_DBG_LEVEL >= AO_DL_INFO) + char timestamp1[JSONDATE_LENGTH + 1] = {'\0'}; + nextChange.toJsonString(timestamp1, JSONDATE_LENGTH + 1); + char timestamp2[JSONDATE_LENGTH + 1] = {'\0'}; + validTo.toJsonString(timestamp2, JSONDATE_LENGTH + 1); + AO_DBG_INFO("New limit for connector 1, scheduled at = %s, nextChange = %s, limit = %f", + timestamp1, timestamp2, limit); +#endif + nextChange = validTo; if (limit != limitBeforeChange){ if (onLimitChange != NULL) { @@ -194,10 +187,6 @@ void SmartChargingService::inferenceLimit(const OcppTimestamp &t, float *limitOu } } -void SmartChargingService::writeOutCompositeSchedule(JsonObject *json){ - Serial.print(F("[SmartChargingService] Unsupported Operation: SmartChargingService::writeOutCompositeSchedule\n")); -} - ChargingSchedule *SmartChargingService::getCompositeSchedule(int connectorId, otime_t duration){ auto& startSchedule = context.getOcppModel().getOcppTime().getOcppTimestampNow(); ChargingSchedule *result = new ChargingSchedule(startSchedule, duration); @@ -326,7 +315,7 @@ bool SmartChargingService::clearChargingProfile(const std::function Date: Mon, 21 Feb 2022 16:06:22 +0100 Subject: [PATCH 124/696] minor corrections --- src/ArduinoOcpp/Core/Configuration.cpp | 9 --------- src/ArduinoOcpp/Core/OcppMessage.cpp | 2 +- src/ArduinoOcpp/SimpleOcppOperationFactory.cpp | 4 ---- .../Tasks/ChargePointStatus/ConnectorStatus.cpp | 2 +- 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index 13682816..3c78e975 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -290,18 +290,9 @@ bool configuration_save() { template std::shared_ptr> createConfiguration(const char *key, int value); template std::shared_ptr> createConfiguration(const char *key, float value); template std::shared_ptr> createConfiguration(const char *key, const char * value); -//template std::shared_ptr> createConfiguration(const char *key, String value); template std::shared_ptr> declareConfiguration(const char *key, int defaultValue, const char *filename, bool remotePeerCanWrite, bool remotePeerCanRead, bool localClientCanWrite, bool rebootRequiredWhenChanged); template std::shared_ptr> declareConfiguration(const char *key, float defaultValue, const char *filename, bool remotePeerCanWrite, bool remotePeerCanRead, bool localClientCanWrite, bool rebootRequiredWhenChanged); template std::shared_ptr> declareConfiguration(const char *key, const char *defaultValue, const char *filename, bool remotePeerCanWrite, bool remotePeerCanRead, bool localClientCanWrite, bool rebootRequiredWhenChanged); -//template std::shared_ptr> declareConfiguration(const char *key, String defaultValue, const char *filename, bool remotePeerCanWrite, bool remotePeerCanRead, bool localClientCanWrite, bool rebootRequiredWhenChanged); - -/* -template std::shared_ptr> Ocpp16::changeConfiguration(const char *key, int value); -template std::shared_ptr> Ocpp16::changeConfiguration(const char *key, float value); -template std::shared_ptr> Ocpp16::changeConfiguration(const char *key, const char *value); -//template std::shared_ptr> Ocpp16::changeConfiguration(const char *key, String value); -*/ } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Core/OcppMessage.cpp b/src/ArduinoOcpp/Core/OcppMessage.cpp index f33af47c..40781d1f 100644 --- a/src/ArduinoOcpp/Core/OcppMessage.cpp +++ b/src/ArduinoOcpp/Core/OcppMessage.cpp @@ -49,5 +49,5 @@ std::unique_ptr OcppMessage::createConf() { std::unique_ptr ArduinoOcpp::createEmptyDocument() { auto emptyDoc = std::unique_ptr(new DynamicJsonDocument(0)); emptyDoc->to(); - return std::move(emptyDoc); + return emptyDoc; } diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 9743ebe0..b4cf0ad9 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -240,13 +240,9 @@ std::unique_ptr makeOcppOperation(const char *messageType, int co } else if (!strcmp(messageType, "RemoteStartTransaction")) { msg = std::unique_ptr(new Ocpp16::RemoteStartTransaction()); operation->setOnReceiveReqListener(onRemoteStartTransactionReceiveRequest); - if (onRemoteStartTransactionSendConf == nullptr) - AO_DBG_WARN("RemoteStartTransaction is without effect when the sendConf listener is not set. Set a listener which initiates the StartTransaction operation."); operation->setOnSendConfListener(onRemoteStartTransactionSendConf); } else if (!strcmp(messageType, "RemoteStopTransaction")) { msg = std::unique_ptr(new Ocpp16::RemoteStopTransaction()); - if (onRemoteStopTransactionSendConf == nullptr) - AO_DBG_WARN("RemoteStopTransaction is without effect when no sendConf listener is set. Set a listener which initiates the StopTransaction operation."); operation->setOnReceiveReqListener(onRemoteStopTransactionReceiveRequest); operation->setOnSendConfListener(onRemoteStopTransactionSendConf); } else if (!strcmp(messageType, "ChangeConfiguration")) { diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 234fac27..f1bb127e 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -144,7 +144,7 @@ OcppMessage *ConnectorStatus::loop() { AO_DBG_DEBUG("Session mngt: release connectionTimeOut"); connectionTimeOutListen = false; } else { - if (connectionTimeOutTimestamp - ao_tick_ms() >= ((ulong) *connectionTimeOut) * 1000UL) { + if (ao_tick_ms() - connectionTimeOutTimestamp >= ((ulong) *connectionTimeOut) * 1000UL) { AO_DBG_INFO("Session mngt: timeout"); endSession(); connectionTimeOutListen = false; From 0ad8a32675a1d92ba082659dfe41fa46806e6e00 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 21 Feb 2022 16:10:36 +0100 Subject: [PATCH 125/696] report triggered MeterValues --- src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp | 15 +++++++++++++-- .../Metering/ConnectorMeterValuesRecorder.cpp | 9 ++++----- .../Metering/ConnectorMeterValuesRecorder.h | 2 +- .../Tasks/Metering/MeteringService.cpp | 16 ++++++++++++++++ src/ArduinoOcpp/Tasks/Metering/MeteringService.h | 3 +++ 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp index 4477dfa3..25bb2b4a 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -27,14 +28,24 @@ void TriggerMessage::processReq(JsonObject payload) { const int connectorId = payload["connectorId"] | 0; if (ocppModel && ocppModel->getChargePointStatusService()) { - if (connectorId >= ocppModel->getChargePointStatusService()->getNumConnectors()) { + if (connectorId < 0 || connectorId >= ocppModel->getChargePointStatusService()->getNumConnectors()) { formatError = true; } } if (!formatError) { AO_DBG_INFO("Execute for message type %s, connectorId = %i", requestedMessage, connectorId); - triggeredOperation = makeOcppOperation(requestedMessage, connectorId); + if (!strcmp(requestedMessage, "MeterValues")) { + if (ocppModel && ocppModel->getMeteringService()) { + triggeredOperation = ocppModel->getMeteringService()->retrieveMeterValues(connectorId); + } + + if (!triggeredOperation) { + formatError = true; + } + } else { + triggeredOperation = makeOcppOperation(requestedMessage, connectorId); + } } if (triggeredOperation) { diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index 3fd2fe58..60df807d 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -46,7 +46,9 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { auto connector = context.getConnectorStatus(connectorId); if (connector && connector->getTransactionId() != lastTransactionId) { //transaction break occured! - auto result = toMeterValues(); + OcppMessage *result {nullptr}; + if (sampleTimestamp.size() > 0) + result = toMeterValues(); lastTransactionId = connector->getTransactionId(); return result; } @@ -77,10 +79,7 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { OcppMessage *ConnectorMeterValuesRecorder::toMeterValues() { if (sampleTimestamp.size() == 0) { - //Switching from Non-Transaction to Transaction (or vice versa) without sample. Or anything wrong here. Discard - AO_DBG_DEBUG("Suggested to send MeterValues without any data point. Ignore"); - clear(); - return nullptr; + AO_DBG_WARN("Creating MeterValues without any content"); } //decide which measurands to send. If a measurand is missing at at least one point in time, omit that measurand completely diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h index af90dc74..9448e964 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h @@ -43,12 +43,12 @@ class ConnectorMeterValuesRecorder { std::shared_ptr> MeterValuesSampledDataMaxLength = NULL; void takeSample(); - OcppMessage *toMeterValues(); //returns message if connector has captured enough samples void clear(); public: ConnectorMeterValuesRecorder(OcppModel& context, int connectorId); OcppMessage *loop(); + OcppMessage *toMeterValues(); void setPowerSampler(PowerSampler powerSampler); diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp index 4452a6de..295626ca 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp @@ -52,3 +52,19 @@ float MeteringService::readEnergyActiveImportRegister(int connectorId) { } return connectors[connectorId]->readEnergyActiveImportRegister(); } + +std::unique_ptr MeteringService::retrieveMeterValues(int connectorId) { + if (connectorId < 0 || connectorId >= connectors.size()) { + AO_DBG_ERR("connectorId out of bounds. Ignore"); + return nullptr; + } + auto& connector = connectors.at(connectorId); + if (connector.get()) { + auto msg = connector->toMeterValues(); + auto meterValues = makeOcppOperation(msg); + meterValues->setTimeout(std::unique_ptr{new FixedTimeout(120000)}); + return meterValues; + } + AO_DBG_ERR("Could not find connector"); + return nullptr; +} diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h index 360a654b..5b4dcab7 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h @@ -15,6 +15,7 @@ using PowerSampler = std::function; //in Watts (W) using EnergySampler = std::function; //in Watt-hours (Wh) class OcppEngine; +class OcppOperation; class MeteringService { private: @@ -31,6 +32,8 @@ class MeteringService { void setEnergySampler(int connectorId, EnergySampler energySampler); float readEnergyActiveImportRegister(int connectorId); + + std::unique_ptr retrieveMeterValues(int connectorId); //returns all recorded MeterValues and deletes own records }; } //end namespace ArduinoOcpp From d568cd26059e36a2508a464eb8c78977f976e5fd Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 22 Feb 2022 15:42:10 +0100 Subject: [PATCH 126/696] use fixed size c_str and replace String --- platformio.ini | 4 +- src/ArduinoOcpp.cpp | 5 +- src/ArduinoOcpp.h | 4 +- src/ArduinoOcpp/Core/OcppOperation.cpp | 24 ++--- src/ArduinoOcpp/Core/OcppOperation.h | 6 +- src/ArduinoOcpp/Core/OcppServer.cpp | 4 +- src/ArduinoOcpp/Core/OcppServer.h | 2 +- src/ArduinoOcpp/Core/OcppSocket.cpp | 6 +- src/ArduinoOcpp/Core/OcppSocket.h | 6 +- .../MessagesV16/BootNotification.cpp | 30 +++--- .../MessagesV16/BootNotification.h | 18 ++-- .../MessagesV16/ChangeConfiguration.cpp | 26 ++---- src/ArduinoOcpp/MessagesV16/CiStrings.h | 1 + src/ArduinoOcpp/MessagesV16/DataTransfer.cpp | 8 +- src/ArduinoOcpp/MessagesV16/DataTransfer.h | 4 +- .../MessagesV16/GetConfiguration.cpp | 10 +- .../MessagesV16/GetConfiguration.h | 3 +- .../MessagesV16/GetDiagnostics.cpp | 6 +- src/ArduinoOcpp/MessagesV16/GetDiagnostics.h | 4 +- src/ArduinoOcpp/MessagesV16/Reset.cpp | 2 +- .../MessagesV16/UpdateFirmware.cpp | 4 +- src/ArduinoOcpp/MessagesV16/UpdateFirmware.h | 2 +- .../ChargePointStatus/ConnectorStatus.cpp | 24 +++-- .../Tasks/Diagnostics/DiagnosticsService.cpp | 6 +- .../Tasks/Diagnostics/DiagnosticsService.h | 10 +- .../FirmwareManagement/FirmwareService.cpp | 14 +-- .../FirmwareManagement/FirmwareService.h | 12 +-- .../SmartCharging/SmartChargingService.cpp | 91 ++++++++----------- 28 files changed, 160 insertions(+), 176 deletions(-) diff --git a/platformio.ini b/platformio.ini index 7600185e..b0f58e72 100644 --- a/platformio.ini +++ b/platformio.ini @@ -19,7 +19,7 @@ framework = ${common.framework} lib_deps = ${common.lib_deps} monitor_speed = ${common.monitor_speed} build_flags = - -DAO_DEBUG_OUT ; flood the serial monitor with information about the internal state + -D AO_DBG_LEVEL=AO_DL_INFO ; flood the serial monitor with information about the internal state -DAO_TRAFFIC_OUT ; print the OCPP communication to the serial monitor [env:esp32-development-board] @@ -31,7 +31,7 @@ lib_deps = lorol/LittleFS_esp32@1.0.5 monitor_speed = ${common.monitor_speed} build_flags = - -DAO_DEBUG_OUT ; flood the serial monitor with information about the internal state + -D AO_DBG_LEVEL=AO_DL_INFO ; flood the serial monitor with information about the internal state -DAO_TRAFFIC_OUT ; print the OCPP communication to the serial monitor -DCONFIG_LITTLEFS_FOR_IDF_3_2 board_build.partitions = min_spiffs.csv diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 8c15f95d..1f19a983 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include @@ -48,7 +47,7 @@ using namespace ArduinoOcpp::Facade; using namespace ArduinoOcpp::Ocpp16; #ifndef AO_CUSTOM_WS -void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, float V_eff, ArduinoOcpp::FilesystemOpt fsOpt, ArduinoOcpp::OcppClock system_time) { +void OCPP_initialize(const char *CS_hostname, uint16_t CS_port, const char *CS_url, float V_eff, ArduinoOcpp::FilesystemOpt fsOpt, ArduinoOcpp::OcppClock system_time) { if (ocppEngine) { AO_DBG_WARN("Can't be called two times. Either restart ESP, or call OCPP_deinitialize() before"); return; @@ -313,7 +312,7 @@ void authorize(const char *idTag, OnReceiveConfListener onConf, OnAbortListener ocppEngine->initiateOperation(std::move(authorize)); } -void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { +void bootNotification(const char *chargePointModel, const char *chargePointVendor, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { AO_DBG_ERR("Please call OCPP_initialize before"); return; diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 7478bae0..aba69669 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -25,7 +25,7 @@ using ArduinoOcpp::Timeout; #ifndef AO_CUSTOM_WS //uses links2004/WebSockets library -void OCPP_initialize(String CS_hostname, uint16_t CS_port, String CS_url, float V_eff = 230.f /*German grid*/, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, ArduinoOcpp::OcppClock system_time = ArduinoOcpp::Clocks::DEFAULT_CLOCK); +void OCPP_initialize(const char *CS_hostname, uint16_t CS_port, const char *CS_url, float V_eff = 230.f /*German grid*/, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, ArduinoOcpp::OcppClock system_time = ArduinoOcpp::Clocks::DEFAULT_CLOCK); #endif //Lets you use your own WebSocket implementation @@ -109,7 +109,7 @@ void setOnResetReceiveReq(OnReceiveReqListener onReceiveReq); //alternative: sta void authorize(const char *idTag, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); -void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); +void bootNotification(const char *chargePointModel, const char *chargePointVendor, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); //The OCPP operation will include the given payload without modifying it. The library will delete the payload object after successful transmission. void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); diff --git a/src/ArduinoOcpp/Core/OcppOperation.cpp b/src/ArduinoOcpp/Core/OcppOperation.cpp index 4c2f988c..b0e99e99 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.cpp +++ b/src/ArduinoOcpp/Core/OcppOperation.cpp @@ -56,17 +56,19 @@ Timeout *OcppOperation::getTimeout() { return timeout.get(); } -void OcppOperation::setMessageID(const String &id){ - if (messageID.length() > 0){ +void OcppOperation::setMessageID(const std::string &id){ + if (!messageID.empty()){ AO_DBG_WARN("MessageID is set twice or is set after first usage!"); - //return; <-- Just letting the id being overwritten probably doesn't cause further errors... } messageID = id; } -const String *OcppOperation::getMessageID() { - if (messageID.isEmpty()) { - messageID = String(unique_id_counter++); +const std::string *OcppOperation::getMessageID() { + if (messageID.empty()) { + char id_str [16] = {'\0'}; + sprintf(id_str, "%d", unique_id_counter++); + messageID = std::string {id_str}; + //messageID = std::to_string(unique_id_counter++); } return &messageID; } @@ -124,7 +126,7 @@ boolean OcppOperation::sendReq(OcppSocket& ocppSocket){ * * Return that this function must be called again (-> false) */ - String out {'\0'}; + std::string out {}; serializeJson(requestJson, out); if (printReqCounter > 5000) { @@ -153,7 +155,7 @@ boolean OcppOperation::receiveConf(JsonDocument& confJson){ /* * check if messageIDs match. If yes, continue with this function. If not, return false for message not consumed */ - if (!getMessageID()->equals(confJson[1].as())){ + if (*getMessageID() != confJson[1].as()){ return false; } @@ -178,7 +180,7 @@ boolean OcppOperation::receiveError(JsonDocument& confJson){ /* * check if messageIDs match. If yes, continue with this function. If not, return false for message not consumed */ - if (!getMessageID()->equals(confJson[1].as())){ + if (*getMessageID() != confJson[1].as()){ return false; } @@ -205,7 +207,7 @@ boolean OcppOperation::receiveError(JsonDocument& confJson){ boolean OcppOperation::receiveReq(JsonDocument& reqJson){ - String reqId = reqJson[1]; + std::string reqId = reqJson[1]; setMessageID(reqId); //TODO What if client receives requests two times? Can happen if previous conf is lost. In the Smart Charging Profile @@ -286,7 +288,7 @@ boolean OcppOperation::sendConf(OcppSocket& ocppSocket){ /* * Serialize and send. Destroy serialization and JSON object. */ - String out {'\0'}; + std::string out {}; serializeJson(*confJson, out); boolean wsSuccess = ocppSocket.sendTXT(out); diff --git a/src/ArduinoOcpp/Core/OcppOperation.h b/src/ArduinoOcpp/Core/OcppOperation.h index 43898df5..fd93e96a 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.h +++ b/src/ArduinoOcpp/Core/OcppOperation.h @@ -21,10 +21,10 @@ class OcppSocket; class OcppOperation { private: - String messageID{'\0'}; + std::string messageID {}; std::unique_ptr ocppMessage; - const String *getMessageID(); - void setMessageID(const String &id); + const std::string *getMessageID(); + void setMessageID(const std::string &id); OnReceiveConfListener onReceiveConfListener = [] (JsonObject payload) {}; OnReceiveReqListener onReceiveReqListener = [] (JsonObject payload) {}; OnSendConfListener onSendConfListener = [] (JsonObject payload) {}; diff --git a/src/ArduinoOcpp/Core/OcppServer.cpp b/src/ArduinoOcpp/Core/OcppServer.cpp index b7b2c166..c58c4938 100644 --- a/src/ArduinoOcpp/Core/OcppServer.cpp +++ b/src/ArduinoOcpp/Core/OcppServer.cpp @@ -139,7 +139,7 @@ void OcppServer::removeReceiveTXTcallback(IPAddress &ip_addr) { }), receiveTXTrouting.end()); } -bool OcppServer::sendTXT(IPAddress &ip_addr, String &out) { +bool OcppServer::sendTXT(IPAddress &ip_addr, std::string &out) { WsClient mClient; @@ -155,7 +155,7 @@ bool OcppServer::sendTXT(IPAddress &ip_addr, String &out) { return false; } - return wsockServer.sendTXT(mClient, out); + return wsockServer.sendTXT(mClient, out.c_str(), out.length()); } #endif //ndef AO_CUSTOM_WS diff --git a/src/ArduinoOcpp/Core/OcppServer.h b/src/ArduinoOcpp/Core/OcppServer.h index 913426db..64b43afb 100644 --- a/src/ArduinoOcpp/Core/OcppServer.h +++ b/src/ArduinoOcpp/Core/OcppServer.h @@ -47,7 +47,7 @@ class OcppServer { void removeReceiveTXTcallback(IPAddress &ip_addr); - bool sendTXT(IPAddress &ip_addr, String &out); + bool sendTXT(IPAddress &ip_addr, std::string &out); }; } //end namespace EspWiFi diff --git a/src/ArduinoOcpp/Core/OcppSocket.cpp b/src/ArduinoOcpp/Core/OcppSocket.cpp index 7755c411..b0ec2cde 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.cpp +++ b/src/ArduinoOcpp/Core/OcppSocket.cpp @@ -20,8 +20,8 @@ void OcppClientSocket::loop() { wsock->loop(); } -bool OcppClientSocket::sendTXT(String &out) { - return wsock->sendTXT(out); +bool OcppClientSocket::sendTXT(std::string &out) { + return wsock->sendTXT(out.c_str(), out.length()); } void OcppClientSocket::setReceiveTXTcallback(ReceiveTXTcallback &callback) { @@ -71,7 +71,7 @@ void OcppServerSocket::loop() { //nothing here. The client must call the EspWiFi server loop function } -bool OcppServerSocket::sendTXT(String &out) { +bool OcppServerSocket::sendTXT(std::string &out) { AO_DBG_TRAFFIC_OUT(out.c_str()); return OcppServer::getInstance()->sendTXT(ip_addr, out); } diff --git a/src/ArduinoOcpp/Core/OcppSocket.h b/src/ArduinoOcpp/Core/OcppSocket.h index 9e5f1544..58661fcd 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.h +++ b/src/ArduinoOcpp/Core/OcppSocket.h @@ -19,7 +19,7 @@ class OcppSocket { virtual void loop() = 0; - virtual bool sendTXT(String &out) = 0; + virtual bool sendTXT(std::string &out) = 0; virtual void setReceiveTXTcallback(ReceiveTXTcallback &receiveTXT) = 0; //ReceiveTXTcallback is defined in OcppServer.h }; @@ -44,7 +44,7 @@ class OcppClientSocket : public OcppSocket { void loop(); - bool sendTXT(String &out); + bool sendTXT(std::string &out); void setReceiveTXTcallback(ReceiveTXTcallback &receiveTXT); }; @@ -58,7 +58,7 @@ class OcppServerSocket : public OcppSocket { void loop(); - bool sendTXT(String &out); + bool sendTXT(std::string &out); void setReceiveTXTcallback(ReceiveTXTcallback &receiveTXT); }; diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp index 3bdd990e..ab5a2309 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp @@ -16,16 +16,16 @@ BootNotification::BootNotification() { } -BootNotification::BootNotification(String &cpModel, String &cpVendor) { - chargePointModel = String(cpModel); - chargePointVendor = String(cpVendor); +BootNotification::BootNotification(const char *cpModel, const char *cpVendor) { + snprintf(chargePointModel, CP_MODEL_LEN_MAX + 1, "%s", cpModel); + snprintf(chargePointVendor, CP_VENDOR_LEN_MAX + 1, "%s", cpVendor); } -BootNotification::BootNotification(String &cpModel, String &cpVendor, String &cpSerialNumber, String &fwVersion) { - chargePointModel = String(cpModel); - chargePointVendor = String(cpVendor); - chargePointSerialNumber = String(cpSerialNumber); - firmwareVersion = String(fwVersion); +BootNotification::BootNotification(const char *cpModel, const char *cpSerialNumber, const char *cpVendor, const char *fwVersion) { + snprintf(chargePointModel, CP_MODEL_LEN_MAX + 1, "%s", cpModel); + snprintf(chargePointSerialNumber, CP_SERIALNUMBER_LEN_MAX + 1, "%s", cpSerialNumber); + snprintf(chargePointVendor, CP_VENDOR_LEN_MAX + 1, "%s", cpVendor); + snprintf(firmwareVersion, FW_VERSION_LEN_MAX + 1, "%s", fwVersion); } BootNotification::BootNotification(DynamicJsonDocument *payload) { @@ -49,17 +49,17 @@ std::unique_ptr BootNotification::createReq() { } auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(4) - + chargePointModel.length() + 1 - + chargePointVendor.length() + 1 - + chargePointSerialNumber.length() + 1 - + firmwareVersion.length() + 1)); + + strlen(chargePointModel) + 1 + + strlen(chargePointVendor) + 1 + + strlen(chargePointSerialNumber) + 1 + + strlen(firmwareVersion) + 1)); JsonObject payload = doc->to(); payload["chargePointModel"] = chargePointModel; - payload["chargePointVendor"] = chargePointVendor; - if (!chargePointSerialNumber.isEmpty()) { + if (chargePointSerialNumber[0]) { payload["chargePointSerialNumber"] = chargePointSerialNumber; } - if (!firmwareVersion.isEmpty()) { + payload["chargePointVendor"] = chargePointVendor; + if (firmwareVersion[0]) { payload["firmwareVersion"] = firmwareVersion; } return doc; diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.h b/src/ArduinoOcpp/MessagesV16/BootNotification.h index 20ad9247..54449802 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.h +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.h @@ -6,16 +6,22 @@ #define BOOTNOTIFICATION_H #include +#include + +#define CP_MODEL_LEN_MAX CiString20TypeLen +#define CP_SERIALNUMBER_LEN_MAX CiString25TypeLen +#define CP_VENDOR_LEN_MAX CiString20TypeLen +#define FW_VERSION_LEN_MAX CiString50TypeLen namespace ArduinoOcpp { namespace Ocpp16 { class BootNotification : public OcppMessage { private: - String chargePointModel = String('\0'); - String chargePointVendor = String('\0'); - String chargePointSerialNumber = String('\0'); - String firmwareVersion = String('\0'); + char chargePointModel [CP_MODEL_LEN_MAX + 1] = {'\0'}; + char chargePointSerialNumber [CP_SERIALNUMBER_LEN_MAX + 1] = {'\0'}; + char chargePointVendor [CP_VENDOR_LEN_MAX + 1] = {'\0'}; + char firmwareVersion [FW_VERSION_LEN_MAX + 1] = {'\0'}; DynamicJsonDocument *overridePayload = NULL; public: @@ -23,9 +29,9 @@ class BootNotification : public OcppMessage { ~BootNotification(); - BootNotification(String &chargePointModel, String &chargePointVendor); + BootNotification(const char *chargePointModel, const char *chargePointVendor); - BootNotification(String &chargePointModel, String &chargePointVendor, String &chargePointSerialNumber, String &firmwareVersion); + BootNotification(const char *chargePointModel, const char *chargePointSerialNumber, const char *chargePointVendor, const char *firmwareVersion); BootNotification(DynamicJsonDocument *payload); diff --git a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp index 2b5cfca8..ac03a762 100644 --- a/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp @@ -17,8 +17,8 @@ const char* ChangeConfiguration::getOcppOperationType(){ } void ChangeConfiguration::processReq(JsonObject payload) { - String key = payload["key"]; - if (key.isEmpty()) { + const char *key = payload["key"] | ""; + if (strlen(key) < 2) { err = true; AO_DBG_WARN("Could not read key"); return; @@ -37,7 +37,7 @@ void ChangeConfiguration::processReq(JsonObject payload) { if (value.is()) { const char *value_string = value.as(); if (value_string == nullptr || value_string[0] == '\0') { - auto configuration = getConfiguration(key.c_str()); + auto configuration = getConfiguration(key); if (configuration) { if (configuration->permissionRemotePeerCanWrite()) { configuration->setToBeRemoved(); @@ -65,11 +65,9 @@ void ChangeConfiguration::processReq(JsonObject payload) { if (value.is()) { //Not enough. SteVe always sends numerical values as strings. Must also handle this isInt = true; numInt = value.as(); - //configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), value.as())); } else if (value.is()) { isFloat = true; numFloat = value.as(); - //configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), value.as())); } else if (value.is()) { //SteVe sends numerical values as strings. Check for that first isString = true; value_string = value.as(); @@ -109,12 +107,10 @@ void ChangeConfiguration::processReq(JsonObject payload) { else INT_MAXDIGITS = 4; - //bool isString = false; - if (nNonDigits == 0 && nDigits > 0 && nSign <= 1 && nDots == 0) { //integer if (nDigits > INT_MAXDIGITS) { - AO_DBG_WARN("Integer overflow! key = %s, value = %s", key.c_str(), value_string); + AO_DBG_WARN("Integer overflow! key = %s, value = %s", key, value_string); err = true; return; } else { @@ -122,7 +118,6 @@ void ChangeConfiguration::processReq(JsonObject payload) { int neg = -numInt; numInt = neg; } - //configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), numInt)); isInt = true; } } else if (nNonDigits == 0 && nDigits > 0 && nSign <= 1 && nDots == 1) { @@ -134,16 +129,14 @@ void ChangeConfiguration::processReq(JsonObject payload) { numFloat *= -1.f; } - //configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), numFloat)); isFloat = true; } else { //only string isString = true; - //configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), value_string)); } } - std::shared_ptr configuration = getConfiguration(key.c_str()); + std::shared_ptr configuration = getConfiguration(key); if (configuration) { if (!configuration->permissionRemotePeerCanWrite()) { @@ -161,9 +154,6 @@ void ChangeConfiguration::processReq(JsonObject payload) { } else if (!strcmp(configuration->getSerializedType(), SerializedType::get()) && isString) { std::shared_ptr> configurationConcrete = std::static_pointer_cast>(configuration); *configurationConcrete = value_string; -// } else if (!strcmp(configuration->getSerializedType(), SerializedType::get()) && value.is()) { -// std::shared_ptr> configurationConcrete = std::static_pointer_cast>(configuration); -// *configurationConcrete = value.as(); } else { err = true; AO_DBG_WARN("Value has incompatible type"); @@ -175,11 +165,11 @@ void ChangeConfiguration::processReq(JsonObject payload) { } else { //configuration does not exist yet. Create new entry if (isInt) { - configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), numInt)); + configuration = std::static_pointer_cast(declareConfiguration(key, numInt)); } else if (isFloat) { - configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), numFloat)); + configuration = std::static_pointer_cast(declareConfiguration(key, numFloat)); } else if (isString) { - configuration = std::static_pointer_cast(declareConfiguration(key.c_str(), value_string)); + configuration = std::static_pointer_cast(declareConfiguration(key, value_string)); } else { err = true; AO_DBG_WARN("Could not parse value"); diff --git a/src/ArduinoOcpp/MessagesV16/CiStrings.h b/src/ArduinoOcpp/MessagesV16/CiStrings.h index 1989a4bd..a6a13209 100644 --- a/src/ArduinoOcpp/MessagesV16/CiStrings.h +++ b/src/ArduinoOcpp/MessagesV16/CiStrings.h @@ -16,5 +16,6 @@ #define CiString500TypeLen 500 #define IDTAG_LEN_MAX CiString20TypeLen +#define CONF_KEYLEN_MAX CiString50TypeLen #endif diff --git a/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp b/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp index af8f1238..ee544883 100644 --- a/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp +++ b/src/ArduinoOcpp/MessagesV16/DataTransfer.cpp @@ -7,8 +7,8 @@ using ArduinoOcpp::Ocpp16::DataTransfer; -DataTransfer::DataTransfer(String &msg) { - this->msg = String(msg); +DataTransfer::DataTransfer(const std::string &msg) { + this->msg = msg; } const char* DataTransfer::getOcppOperationType(){ @@ -24,9 +24,9 @@ std::unique_ptr DataTransfer::createReq() { } void DataTransfer::processConf(JsonObject payload){ - String status = payload["status"] | "Invalid"; + std::string status = payload["status"] | "Invalid"; - if (status.equals("Accepted")) { + if (status == "Accepted") { AO_DBG_DEBUG("Request has been accepted"); } else { AO_DBG_INFO("Request has been denied"); diff --git a/src/ArduinoOcpp/MessagesV16/DataTransfer.h b/src/ArduinoOcpp/MessagesV16/DataTransfer.h index 9ed141fd..f4bdf741 100644 --- a/src/ArduinoOcpp/MessagesV16/DataTransfer.h +++ b/src/ArduinoOcpp/MessagesV16/DataTransfer.h @@ -12,9 +12,9 @@ namespace Ocpp16 { class DataTransfer : public OcppMessage { private: - String msg; + std::string msg {}; public: - DataTransfer(String &msg); + DataTransfer(const std::string &msg); const char* getOcppOperationType(); diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp index dfb15f37..16737d44 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp @@ -11,11 +11,7 @@ using ArduinoOcpp::Ocpp16::GetConfiguration; GetConfiguration::GetConfiguration() { - keys = std::vector(); -} -GetConfiguration::~GetConfiguration() { - } const char* GetConfiguration::getOcppOperationType(){ @@ -25,15 +21,15 @@ const char* GetConfiguration::getOcppOperationType(){ void GetConfiguration::processReq(JsonObject payload) { JsonArray requestedKeys = payload["key"]; - for (uint16_t i = 0; i < requestedKeys.size(); i++) { - keys.push_back(requestedKeys[i].as()); + for (size_t i = 0; i < requestedKeys.size(); i++) { + keys.push_back(requestedKeys[i].as()); } } std::unique_ptr GetConfiguration::createConf(){ std::shared_ptr>> configurationKeys; - std::vector unknownKeys; + std::vector unknownKeys; if (keys.size() == 0){ //return all existing keys configurationKeys = getAllConfigurations(); diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.h b/src/ArduinoOcpp/MessagesV16/GetConfiguration.h index 959c52eb..679b27b8 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.h +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.h @@ -12,10 +12,9 @@ namespace Ocpp16 { class GetConfiguration : public OcppMessage { private: - std::vector keys; + std::vector keys; public: GetConfiguration(); - ~GetConfiguration(); const char* getOcppOperationType(); diff --git a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp index e251d8f4..18289acf 100644 --- a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp @@ -20,9 +20,9 @@ void GetDiagnostics::processReq(JsonObject payload) { */ const char *loc = payload["location"] | ""; - location = String(loc); + location = loc; //check location URL. Maybe introduce Same-Origin-Policy? - if (location.isEmpty()) { + if (location.empty()) { formatError = true; AO_DBG_WARN("Could not read location. Abort"); return; @@ -60,7 +60,7 @@ std::unique_ptr GetDiagnostics::createConf(){ return nullptr; } - if (fileName.isEmpty()) { + if (fileName.empty()) { return createEmptyDocument(); } else { auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + fileName.length() + 1)); diff --git a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h index 03b152d8..69ed0d0f 100644 --- a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h +++ b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.h @@ -13,13 +13,13 @@ namespace Ocpp16 { class GetDiagnostics : public OcppMessage { private: - String location = String('\0'); + std::string location {}; int retries = 1; ulong retryInterval = 180; OcppTimestamp startTime = OcppTimestamp(); OcppTimestamp stopTime = OcppTimestamp(); - String fileName = String('\0'); + std::string fileName {}; bool formatError = false; public: diff --git a/src/ArduinoOcpp/MessagesV16/Reset.cpp b/src/ArduinoOcpp/MessagesV16/Reset.cpp index b4988c46..e51d4659 100644 --- a/src/ArduinoOcpp/MessagesV16/Reset.cpp +++ b/src/ArduinoOcpp/MessagesV16/Reset.cpp @@ -21,7 +21,7 @@ void Reset::processReq(JsonObject payload) { * Process the application data here. Note: you have to implement the device reset procedure in your client code. You have to set * a onSendConfListener in which you initiate a reset (e.g. calling ESP.reset() ) */ - const char *type = payload["type"] | "Invalid"; + //const char *type = payload["type"] | "Invalid"; if (ocppModel && ocppModel->getChargePointStatusService()) { auto cpsService = ocppModel->getChargePointStatusService(); diff --git a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp index edc5e511..078f73b4 100644 --- a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp +++ b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp @@ -20,9 +20,9 @@ void UpdateFirmware::processReq(JsonObject payload) { */ const char *loc = payload["location"] | ""; - location = String(loc); + location = loc; //check location URL. Maybe introduce Same-Origin-Policy? - if (location.isEmpty()) { + if (location.empty()) { formatError = true; AO_DBG_WARN("Could not read location. Abort"); return; diff --git a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h index 82dcd98a..edc5a5df 100644 --- a/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h +++ b/src/ArduinoOcpp/MessagesV16/UpdateFirmware.h @@ -13,7 +13,7 @@ namespace Ocpp16 { class UpdateFirmware : public OcppMessage { private: - String location = String('\0'); + std::string location {}; OcppTimestamp retreiveDate = OcppTimestamp(); int retries = 1; ulong retryInterval = 180; diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index f1bb127e..532aecd5 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -21,16 +22,17 @@ ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) : context(context), connectorId{connectorId} { //Set default transaction ID in memory - String keySession = "AO_SID_CONN_"; - String keyTx = "OCPP_STATE_TRANSACTION_ID_CONNECTOR_"; - String keyAvailability = "OCPP_STATE_AVAILABILITY_CONNECTOR_"; - String id = String(connectorId, DEC); - keySession += id; - keyTx += id; - keyAvailability += id; - sIdTag = declareConfiguration(keySession.c_str(), "", CONFIGURATION_FN, false, false, true, false); - transactionId = declareConfiguration(keyTx.c_str(), -1, CONFIGURATION_FN, false, false, true, false); - availability = declareConfiguration(keyAvailability.c_str(), AVAILABILITY_OPERATIVE, CONFIGURATION_FN, false, false, true, false); + char key [CONF_KEYLEN_MAX + 1] = {'\0'}; + + snprintf(key, CONF_KEYLEN_MAX + 1, "AO_SID_CONN_%d", connectorId); + sIdTag = declareConfiguration(key, "", CONFIGURATION_FN, false, false, true, false); + + snprintf(key, CONF_KEYLEN_MAX + 1, "AO_TXID_CONN_%d", connectorId); + transactionId = declareConfiguration(key, -1, CONFIGURATION_FN, false, false, true, false); + + snprintf(key, CONF_KEYLEN_MAX + 1, "AO_AVAIL_CONN_%d", connectorId); + availability = declareConfiguration(key, AVAILABILITY_OPERATIVE, CONFIGURATION_FN, false, false, true, false); + connectionTimeOut = declareConfiguration("ConnectionTimeOut", 30, CONFIGURATION_FN, true, true, true, false); if (!sIdTag || !transactionId || !availability) { AO_DBG_ERR("Cannot declare sessionIdTag, transactionId or availability"); @@ -38,6 +40,8 @@ ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) if (sIdTag->getBuffsize() > 0 && (*sIdTag)[0] != '\0') { snprintf(idTag, min((size_t) (IDTAG_LEN_MAX + 1), sIdTag->getBuffsize()), "%s", ((const char *) *sIdTag)); session = true; + connectionTimeOutTimestamp = ao_tick_ms(); + connectionTimeOutListen = true; AO_DBG_DEBUG("Load session idTag at initialization"); } transactionIdSync = *transactionId; diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp index 50d6fe24..2f293c83 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp @@ -71,9 +71,9 @@ void DiagnosticsService::loop() { } //timestamps before year 2021 will be treated as "undefined" -String DiagnosticsService::requestDiagnosticsUpload(String &location, int retries, unsigned int retryInterval, OcppTimestamp startTime, OcppTimestamp stopTime) { +std::string DiagnosticsService::requestDiagnosticsUpload(const std::string &location, int retries, unsigned int retryInterval, OcppTimestamp startTime, OcppTimestamp stopTime) { if (onUpload == nullptr) //maybe add further plausibility checks - return String('\0'); + return nullptr; this->location = location; this->retries = retries; @@ -148,7 +148,7 @@ std::unique_ptr DiagnosticsService::getDiagnosticsStatusNotificat return nullptr; } -void DiagnosticsService::setOnUpload(std::function onUpload) { +void DiagnosticsService::setOnUpload(std::function onUpload) { this->onUpload = onUpload; } diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h index e1ba7a51..f551a134 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h @@ -26,7 +26,7 @@ class DiagnosticsService { private: OcppEngine& context; - String location = String('\0'); + std::string location {}; int retries = 0; unsigned int retryInterval = 0; OcppTimestamp startTime = OcppTimestamp(); @@ -34,7 +34,7 @@ class DiagnosticsService { OcppTimestamp nextTry = OcppTimestamp(); - std::function onUpload = nullptr; + std::function onUpload = nullptr; std::function uploadStatusSampler = nullptr; bool uploadIssued = false; @@ -48,13 +48,13 @@ class DiagnosticsService { void loop(); //timestamps before year 2021 will be treated as "undefined" - //returns empty String if onUpload is missing or upload cannot be scheduled for another reason + //returns empty std::string if onUpload is missing or upload cannot be scheduled for another reason //returns fileName of diagnostics file to be uploaded if upload has been scheduled - String requestDiagnosticsUpload(String &location, int retries = 1, unsigned int retryInterval = 0, OcppTimestamp startTime = OcppTimestamp(), OcppTimestamp stopTime = OcppTimestamp()); + std::string requestDiagnosticsUpload(const std::string &location, int retries = 1, unsigned int retryInterval = 0, OcppTimestamp startTime = OcppTimestamp(), OcppTimestamp stopTime = OcppTimestamp()); Ocpp16::DiagnosticsStatus getDiagnosticsStatus(); - void setOnUpload(std::function onUpload); + void setOnUpload(std::function onUpload); void setOnUploadStatusSampler(std::function uploadStatusSampler); }; diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index 3b1d224f..b361cc5c 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -189,8 +189,8 @@ void FirmwareService::loop() { } } -void FirmwareService::scheduleFirmwareUpdate(String &location, OcppTimestamp retreiveDate, int retries, unsigned int retryInterval) { - this->location = String(location); +void FirmwareService::scheduleFirmwareUpdate(const std::string &location, OcppTimestamp retreiveDate, int retries, unsigned int retryInterval) { + this->location = location; this->retreiveDate = retreiveDate; this->retries = retries; this->retryInterval = retryInterval; @@ -271,7 +271,7 @@ std::unique_ptr FirmwareService::getFirmwareStatusNotification() return nullptr; } -void FirmwareService::setOnDownload(std::function onDownload) { +void FirmwareService::setOnDownload(std::function onDownload) { this->onDownload = onDownload; } @@ -279,7 +279,7 @@ void FirmwareService::setDownloadStatusSampler(std::function d this->downloadStatusSampler = downloadStatusSampler; } -void FirmwareService::setOnInstall(std::function onInstall) { +void FirmwareService::setOnInstall(std::function onInstall) { this->onInstall = onInstall; } @@ -306,7 +306,7 @@ FirmwareService *EspWiFi::makeFirmwareService(OcppEngine& context, const char *b * example of how to integrate a separate download phase (optional) */ #if 0 //separate download phase - fwService->setOnDownload([] (String &location) { + fwService->setOnDownload([] (const std::string &location) { //download the new binary //... return true; @@ -319,7 +319,7 @@ FirmwareService *EspWiFi::makeFirmwareService(OcppEngine& context, const char *b }); #endif //separate download phase - fwService->setOnInstall([fwService] (String &location) { + fwService->setOnInstall([fwService] (const std::string &location) { fwService->setInstallationStatusSampler([](){return InstallationStatus::NotInstalled;}); @@ -365,7 +365,7 @@ FirmwareService *EspWiFi::makeFirmwareService(OcppEngine& context, const char *b FirmwareService *fwService = new FirmwareService(context); fwService->setBuildNumber(buildNumber); - fwService->setOnInstall([fwService] (String &location) { + fwService->setOnInstall([fwService] (const std::string &location) { WiFiClient client; //WiFiClientSecure client; diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h index 14bda9a5..3af1eca7 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h @@ -46,13 +46,13 @@ class FirmwareService { Ocpp16::FirmwareStatus lastReportedStatus = Ocpp16::FirmwareStatus::Idle; bool checkedSuccessfulFwUpdate = false; - String location = String('\0'); + std::string location {}; OcppTimestamp retreiveDate = OcppTimestamp(); int retries = 0; unsigned int retryInterval = 0; - std::function onDownload = NULL; - std::function onInstall = NULL; + std::function onDownload = NULL; + std::function onInstall = NULL; ulong delayTransition = 0; ulong timestampTransition = 0; @@ -79,15 +79,15 @@ class FirmwareService { void loop(); - void scheduleFirmwareUpdate(String &location, OcppTimestamp retreiveDate, int retries = 1, unsigned int retryInterval = 0); + void scheduleFirmwareUpdate(const std::string &location, OcppTimestamp retreiveDate, int retries = 1, unsigned int retryInterval = 0); Ocpp16::FirmwareStatus getFirmwareStatus(); - void setOnDownload(std::function onDownload); + void setOnDownload(std::function onDownload); void setDownloadStatusSampler(std::function downloadStatusSampler); - void setOnInstall(std::function onInstall); + void setOnInstall(std::function onInstall); void setInstallationStatusSampler(std::function installationStatusSampler); }; diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index cd9ba7fe..95ad2b6d 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -21,6 +21,7 @@ #define PROFILE_FN_PREFIX "/ocpp-" #define PROFILE_FN_SUFFIX ".cnf" +#define PROFILE_FN_MAXSIZE 30 #define PROFILE_CUSTOM_CAPACITY 500 #define PROFILE_MAX_CAPACITY 4000 @@ -294,25 +295,22 @@ bool SmartChargingService::clearChargingProfile(const std::functiongetChargingProfilePurpose()) { case (ChargingProfilePurposeType::ChargePointMaxProfile): - profileFN += "CpMaxProfile-"; + snprintf(fn, PROFILE_FN_MAXSIZE, PROFILE_FN_PREFIX "CpMaxProfile-%d" PROFILE_FN_SUFFIX, chargingProfile->getStackLevel()); break; case (ChargingProfilePurposeType::TxDefaultProfile): - profileFN += "TxDefProfile-"; + snprintf(fn, PROFILE_FN_MAXSIZE, PROFILE_FN_PREFIX "TxDefProfile-%d" PROFILE_FN_SUFFIX, chargingProfile->getStackLevel()); break; case (ChargingProfilePurposeType::TxProfile): - profileFN += "TxProfile-"; + snprintf(fn, PROFILE_FN_MAXSIZE, PROFILE_FN_PREFIX "TxProfile-%d" PROFILE_FN_SUFFIX, chargingProfile->getStackLevel()); break; } - profileFN += chargingProfile->getStackLevel(); - profileFN += PROFILE_FN_SUFFIX; - - if (USE_FS.exists(profileFN)) { - USE_FS.remove(profileFN); + if (USE_FS.exists(fn)) { + USE_FS.remove(fn); } } else { AO_DBG_DEBUG("Prohibit access to FS"); @@ -339,38 +337,35 @@ bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile AO_DBG_DEBUG("Prohibit access to FS"); return true; } - - String profileFN = PROFILE_FN_PREFIX; + + char fn [PROFILE_FN_MAXSIZE] = {'\0'}; switch (chargingProfile->getChargingProfilePurpose()) { case (ChargingProfilePurposeType::ChargePointMaxProfile): - profileFN += "CpMaxProfile-"; + snprintf(fn, PROFILE_FN_MAXSIZE, PROFILE_FN_PREFIX "CpMaxProfile-%d" PROFILE_FN_SUFFIX, chargingProfile->getStackLevel()); break; case (ChargingProfilePurposeType::TxDefaultProfile): - profileFN += "TxDefProfile-"; + snprintf(fn, PROFILE_FN_MAXSIZE, PROFILE_FN_PREFIX "TxDefProfile-%d" PROFILE_FN_SUFFIX, chargingProfile->getStackLevel()); break; case (ChargingProfilePurposeType::TxProfile): - profileFN += "TxProfile-"; + snprintf(fn, PROFILE_FN_MAXSIZE, PROFILE_FN_PREFIX "TxProfile-%d" PROFILE_FN_SUFFIX, chargingProfile->getStackLevel()); break; } - profileFN += chargingProfile->getStackLevel(); - profileFN += PROFILE_FN_SUFFIX; - - if (USE_FS.exists(profileFN)) { - USE_FS.remove(profileFN); + if (USE_FS.exists(fn)) { + USE_FS.remove(fn); } - File file = USE_FS.open(profileFN, "w"); + File file = USE_FS.open(fn, "w"); if (!file) { - AO_DBG_ERR("Unable to save: could not save profile: %s", profileFN.c_str()); + AO_DBG_ERR("Unable to save: could not save profile: %s", fn); return false; } // Serialize JSON to file if (serializeJson(*json, file) == 0) { - AO_DBG_ERR("Unable to save: could not serialize JSON for profile: %s", profileFN.c_str()); + AO_DBG_ERR("Unable to save: could not serialize JSON for profile: %s", fn); file.close(); return false; } @@ -396,48 +391,40 @@ bool SmartChargingService::loadProfiles() { ChargingProfilePurposeType purposes[] = {ChargingProfilePurposeType::ChargePointMaxProfile, ChargingProfilePurposeType::TxDefaultProfile, ChargingProfilePurposeType::TxProfile}; - for (const ChargingProfilePurposeType purpose : purposes) { - //ChargingProfile **profilePurposeStack; //select which stack this profile belongs to due to its purpose - String profileFnPurpose = String('\0'); + char fn [PROFILE_FN_MAXSIZE] = {'\0'}; - switch (purpose) { - case (ChargingProfilePurposeType::ChargePointMaxProfile): - //profilePurposeStack = ChargePointMaxProfile; - profileFnPurpose = "CpMaxProfile-"; - break; - case (ChargingProfilePurposeType::TxDefaultProfile): - //profilePurposeStack = TxDefaultProfile; - profileFnPurpose = "TxDefProfile-"; - break; - case (ChargingProfilePurposeType::TxProfile): - //profilePurposeStack = TxProfile; - profileFnPurpose = "TxProfile-"; - break; - } + for (const ChargingProfilePurposeType purpose : purposes) { for (int iLevel = 0; iLevel < CHARGEPROFILEMAXSTACKLEVEL; iLevel++) { - String profileFN = PROFILE_FN_PREFIX; - profileFN += profileFnPurpose; - profileFN += iLevel; - profileFN += PROFILE_FN_SUFFIX; + switch (purpose) { + case (ChargingProfilePurposeType::ChargePointMaxProfile): + snprintf(fn, PROFILE_FN_MAXSIZE, PROFILE_FN_PREFIX "CpMaxProfile-%d" PROFILE_FN_SUFFIX, iLevel); + break; + case (ChargingProfilePurposeType::TxDefaultProfile): + snprintf(fn, PROFILE_FN_MAXSIZE, PROFILE_FN_PREFIX "TxDefProfile-%d" PROFILE_FN_SUFFIX, iLevel); + break; + case (ChargingProfilePurposeType::TxProfile): + snprintf(fn, PROFILE_FN_MAXSIZE, PROFILE_FN_PREFIX "TxProfile-%d" PROFILE_FN_SUFFIX, iLevel); + break; + } - if (!USE_FS.exists(profileFN)) { + if (!USE_FS.exists(fn)) { continue; //There is not a profile on the stack iStack with stacklevel iLevel. Normal case, just continue. } - File file = USE_FS.open(profileFN, "r"); + File file = USE_FS.open(fn, "r"); if (file) { - AO_DBG_DEBUG("Load profile from file: %s", profileFN.c_str()); + AO_DBG_DEBUG("Load profile from file: %s", fn); } else { - AO_DBG_ERR("Unable to initialize: could not open file for profile: %s", profileFN.c_str()); + AO_DBG_ERR("Unable to initialize: could not open file for profile: %s", fn); success = false; continue; } if (!file.available()) { - AO_DBG_ERR("Unable to initialize: empty file for profile: %s", profileFN.c_str()); + AO_DBG_ERR("Unable to initialize: empty file for profile: %s", fn); file.close(); success = false; continue; @@ -446,7 +433,7 @@ bool SmartChargingService::loadProfiles() { int file_size = file.size(); if (file_size < 2) { - AO_DBG_ERR("Unable to initialize: too short for json: %s", profileFN.c_str()); + AO_DBG_ERR("Unable to initialize: too short for json: %s", fn); success = false; continue; } @@ -469,7 +456,7 @@ bool SmartChargingService::loadProfiles() { error = false; break; case DeserializationError::InvalidInput: - AO_DBG_ERR("Unable to initialize: invalid json in file: %s", profileFN.c_str()); + AO_DBG_ERR("Unable to initialize: invalid json in file: %s", fn); success = false; break; case DeserializationError::NoMemory: @@ -477,7 +464,7 @@ bool SmartChargingService::loadProfiles() { error = false; break; default: - AO_DBG_ERR("Unable to initialize: error in file: %s", profileFN.c_str()); + AO_DBG_ERR("Unable to initialize: error in file: %s", fn); success = false; break; } @@ -490,7 +477,7 @@ bool SmartChargingService::loadProfiles() { capacity *= 3; capacity /= 2; file.seek(0, SeekSet); //rewind file to beginning - AO_DBG_DEBUG("Initialization: increase JsonCapacity to %zu for file: %s", capacity, profileFN.c_str()); + AO_DBG_DEBUG("Initialization: increase JsonCapacity to %zu for file: %s", capacity, fn); continue; } From b603f482e5c0c01e48967aad510f624d85b76c06 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 23 Feb 2022 14:54:46 +0100 Subject: [PATCH 127/696] fix FormatViolation message if no meter exists --- src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp | 16 ++++++++++++---- src/ArduinoOcpp/MessagesV16/TriggerMessage.h | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp index 25bb2b4a..75047946 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp @@ -36,15 +36,21 @@ void TriggerMessage::processReq(JsonObject payload) { if (!formatError) { AO_DBG_INFO("Execute for message type %s, connectorId = %i", requestedMessage, connectorId); if (!strcmp(requestedMessage, "MeterValues")) { + //special case MeterValues needs unique handling if (ocppModel && ocppModel->getMeteringService()) { triggeredOperation = ocppModel->getMeteringService()->retrieveMeterValues(connectorId); - } - if (!triggeredOperation) { - formatError = true; + if (!triggeredOperation) { + formatError = true; + } + } else { + AO_DBG_WARN("MeteringService not initialized"); } } else { triggeredOperation = makeOcppOperation(requestedMessage, connectorId); + if (!triggeredOperation) { + statusMessage = "NotImplemented"; + } } } @@ -52,7 +58,9 @@ void TriggerMessage::processReq(JsonObject payload) { statusMessage = "Accepted"; } else { AO_DBG_WARN("Could not make OppOperation from TriggerMessage. Ignore request"); - statusMessage = "NotImplemented"; + if (!statusMessage) { + statusMessage = "Rejected"; + } } } diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h index 057625cb..b1ea7594 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h @@ -16,7 +16,7 @@ namespace Ocpp16 { class TriggerMessage : public OcppMessage { private: std::unique_ptr triggeredOperation; - const char *statusMessage; + const char *statusMessage {nullptr}; bool formatError = false; public: From 6b10764821e329be6e9e8a67ad64795c57fdc784 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 23 Feb 2022 14:55:59 +0100 Subject: [PATCH 128/696] value-field as string --- src/ArduinoOcpp/MessagesV16/MeterValues.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp index 22d228ca..4f04992b 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp @@ -36,15 +36,19 @@ std::unique_ptr MeterValues::createReq() { int numEntries = sampleTime.size(); + const size_t VALUE_MAXPRECISION = 10; + const size_t VALUE_MAXSIZE = VALUE_MAXPRECISION + 7; + char value_str [VALUE_MAXSIZE] = {'\0'}; + auto doc = std::unique_ptr(new DynamicJsonDocument( - JSON_OBJECT_SIZE(2) //connectorID, transactionId + JSON_OBJECT_SIZE(3) //connectorID, transactionId, meterValue entry + JSON_ARRAY_SIZE(numEntries) //metervalue array - + numEntries * JSON_OBJECT_SIZE(1) //sampledValue + + numEntries * JSON_OBJECT_SIZE(1) //sampledValue entry + numEntries * (JSON_OBJECT_SIZE(1) + (JSONDATE_LENGTH + 1)) //timestamp + numEntries * JSON_ARRAY_SIZE(2) //sampledValue - + 2 * numEntries * JSON_OBJECT_SIZE(1) //value // why are these taken by two? - + 2 * numEntries * JSON_OBJECT_SIZE(1) //measurand // - + 2 * numEntries * JSON_OBJECT_SIZE(1) //unit // + + 2 * numEntries * (JSON_OBJECT_SIZE(1) + VALUE_MAXSIZE) //value + + 2 * numEntries * JSON_OBJECT_SIZE(1) //measurand + + 2 * numEntries * JSON_OBJECT_SIZE(1) //unit + 230)); //"safety space" JsonObject payload = doc->to(); @@ -59,13 +63,15 @@ std::unique_ptr MeterValues::createReq() { JsonArray sampledValue = meterValue.createNestedArray("sampledValue"); if (energy.size() >= i + 1) { JsonObject sampledValue_1 = sampledValue.createNestedObject(); - sampledValue_1["value"] = energy.at(i); + snprintf(value_str, VALUE_MAXSIZE, "%.*g", VALUE_MAXPRECISION, energy.at(i)); + sampledValue_1["value"] = value_str; sampledValue_1["measurand"] = "Energy.Active.Import.Register"; sampledValue_1["unit"] = "Wh"; } if (power.size() >= i + 1) { JsonObject sampledValue_2 = sampledValue.createNestedObject(); - sampledValue_2["value"] = power.at(i); + snprintf(value_str, VALUE_MAXSIZE, "%.*g", VALUE_MAXPRECISION, power.at(i)); + sampledValue_2["value"] = value_str; sampledValue_2["measurand"] = "Power.Active.Import"; sampledValue_2["unit"] = "W"; } From cbf8dbe0b19041acdef6b376d9e8e5549389261f Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 24 Feb 2022 09:45:27 +0100 Subject: [PATCH 129/696] fix JSON data type --- src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp index 776924ff..5ab975c2 100644 --- a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp @@ -65,9 +65,9 @@ void AbstractConfiguration::storeOcppMsgHeader(JsonObject &keyValuePair) { if (toBeRemovedFlag) return; keyValuePair["key"] = key; if (remotePeerCanWrite) { - keyValuePair["readonly"] = "false"; + keyValuePair["readonly"] = false; } else { - keyValuePair["readonly"] = "true"; + keyValuePair["readonly"] = true; } } From 902315381ccbccbe8a5673a8e87ebba5f9c8a239 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 24 Feb 2022 14:42:43 +0100 Subject: [PATCH 130/696] fix format --- src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp index 5ab975c2..0cee24e3 100644 --- a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp @@ -223,19 +223,30 @@ std::shared_ptr Configuration::toJsonStorageEntry() { return doc; } +int toCStringValue(char *buf, size_t length, int value) { + return snprintf(buf, length, "%d", value); +} + +int toCStringValue(char *buf, size_t length, float value) { + return snprintf(buf, length, "%.*g", length >= 7 ? length - 7 : 0, value); +} + template std::shared_ptr Configuration::toJsonOcppMsgEntry() { if (!isValid() || toBeRemoved()) { return nullptr; } + const size_t VALUE_MAXSIZE = 50; size_t capacity = getOcppMsgHeaderJsonCapacity() - + getValueJsonCapacity() + + getValueJsonCapacity() + VALUE_MAXSIZE + JSON_OBJECT_SIZE(2); // header, value std::shared_ptr doc = std::make_shared(capacity); JsonObject keyValuePair = doc->to(); storeOcppMsgHeader(keyValuePair); - keyValuePair["value"] = value; + char value_str [VALUE_MAXSIZE] = {'\0'}; + toCStringValue(value_str, VALUE_MAXSIZE, value); + keyValuePair["value"] = value_str; return doc; } From 71cf33b53f9f2e02e4febd78feaab74baabcfad3 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 1 Mar 2022 17:15:03 +0100 Subject: [PATCH 131/696] integrate config key SupportedFeatureProfiles --- .../ChargePointStatusService.cpp | 19 +++++++++++++++++++ .../Tasks/Diagnostics/DiagnosticsService.cpp | 13 +++++++++++++ .../Tasks/Diagnostics/DiagnosticsService.h | 2 +- .../FirmwareManagement/FirmwareService.cpp | 12 ++++++++++++ .../FirmwareManagement/FirmwareService.h | 2 +- .../SmartCharging/SmartChargingService.cpp | 10 ++++++++++ 6 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index d70abc93..40d43c60 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -24,6 +24,25 @@ ChargePointStatusService::ChargePointStatusService(OcppEngine& context, unsigned std::shared_ptr> numberOfConnectors = declareConfiguration("NumberOfConnectors", numConn, CONFIGURATION_FN, false, true, true, false); *numberOfConnectors = numConn; + + const char *fpId = "Core,RemoteTrigger"; + const char *fpIdCore = "Core"; + const char *fpIdRTrigger = "RemoteTrigger"; + auto fProfile = declareConfiguration("SupportedFeatureProfiles",fpId, CONFIGURATION_FN, false, true, true, false); + if (!strstr(*fProfile, fpIdCore)) { + auto fProfilePlus = std::string(*fProfile); + if (!fProfilePlus.empty() && fProfilePlus.back() != ',') + fProfilePlus += ","; + fProfilePlus += fpIdCore; + fProfile->setValue(fProfilePlus.c_str(), fProfilePlus.length() + 1); + } + if (!strstr(*fProfile, fpIdRTrigger)) { + auto fProfilePlus = std::string(*fProfile); + if (!fProfilePlus.empty() && fProfilePlus.back() != ',') + fProfilePlus += ","; + fProfilePlus += fpIdRTrigger; + fProfile->setValue(fProfilePlus.c_str(), fProfilePlus.length() + 1); + } } ChargePointStatusService::~ChargePointStatusService() { diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp index 2f293c83..2a2cc386 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -13,6 +14,18 @@ using namespace ArduinoOcpp; using Ocpp16::DiagnosticsStatus; +DiagnosticsService::DiagnosticsService(OcppEngine& context) : context(context) { + const char *fpId = "FirmwareManagement"; + auto fProfile = declareConfiguration("SupportedFeatureProfiles",fpId, CONFIGURATION_FN, false, true, true, false); + if (!strstr(*fProfile, fpId)) { + auto fProfilePlus = std::string(*fProfile); + if (!fProfilePlus.empty() && fProfilePlus.back() != ',') + fProfilePlus += ","; + fProfilePlus += fpId; + fProfile->setValue(fProfilePlus.c_str(), fProfilePlus.length() + 1); + } +} + void DiagnosticsService::loop() { auto notification = getDiagnosticsStatusNotification(); if (notification) { diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h index f551a134..0906c448 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h @@ -43,7 +43,7 @@ class DiagnosticsService { Ocpp16::DiagnosticsStatus lastReportedStatus = Ocpp16::DiagnosticsStatus::Idle; public: - DiagnosticsService(OcppEngine& context) : context(context) { } + DiagnosticsService(OcppEngine& context); void loop(); diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index b361cc5c..bd1e215d 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -17,6 +17,18 @@ using namespace ArduinoOcpp; using ArduinoOcpp::Ocpp16::FirmwareStatus; +FirmwareService::FirmwareService(OcppEngine& context) : context(context) { + const char *fpId = "FirmwareManagement"; + auto fProfile = declareConfiguration("SupportedFeatureProfiles",fpId, CONFIGURATION_FN, false, true, true, false); + if (!strstr(*fProfile, fpId)) { + auto fProfilePlus = std::string(*fProfile); + if (!fProfilePlus.empty() && fProfilePlus.back() != ',') + fProfilePlus += ","; + fProfilePlus += fpId; + fProfile->setValue(fProfilePlus.c_str(), fProfilePlus.length() + 1); + } +} + void FirmwareService::setBuildNumber(const char *buildNumber) { if (buildNumber == nullptr) return; diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h index 3af1eca7..6e8c145a 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h @@ -73,7 +73,7 @@ class FirmwareService { std::unique_ptr getFirmwareStatusNotification(); public: - FirmwareService(OcppEngine& context) : context(context) { } + FirmwareService(OcppEngine& context); void setBuildNumber(const char *buildNumber); diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index 95ad2b6d..1d900516 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -45,6 +45,16 @@ SmartChargingService::SmartChargingService(OcppEngine& context, float chargeLimi } declareConfiguration("ChargeProfileMaxStackLevel", CHARGEPROFILEMAXSTACKLEVEL, CONFIGURATION_FN, false, true, false, true); + const char *fpId = "SmartCharging"; + auto fProfile = declareConfiguration("SupportedFeatureProfiles",fpId, CONFIGURATION_FN, false, true, true, false); + if (!strstr(*fProfile, fpId)) { + auto fProfilePlus = std::string(*fProfile); + if (!fProfilePlus.empty() && fProfilePlus.back() != ',') + fProfilePlus += ","; + fProfilePlus += fpId; + fProfile->setValue(fProfilePlus.c_str(), fProfilePlus.length() + 1); + } + loadProfiles(); } From 3653bf99ce7e9e94ce2e30b119ee0b6074772724 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 3 Mar 2022 00:44:11 +0100 Subject: [PATCH 132/696] handle whole CP in TriggerMessage --- .../MessagesV16/TriggerMessage.cpp | 79 +++++++++++-------- src/ArduinoOcpp/MessagesV16/TriggerMessage.h | 8 +- .../ChargePointStatusService.cpp | 2 +- .../Metering/ConnectorMeterValuesRecorder.cpp | 45 ++++++++++- .../Metering/ConnectorMeterValuesRecorder.h | 4 +- .../Tasks/Metering/MeteringService.cpp | 14 ++-- .../Tasks/Metering/MeteringService.h | 4 +- 7 files changed, 108 insertions(+), 48 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp index 75047946..3ab778be 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp @@ -12,56 +12,66 @@ using ArduinoOcpp::Ocpp16::TriggerMessage; -TriggerMessage::TriggerMessage() { - statusMessage = "NotImplemented"; //default value if anything goes wrong -} - const char* TriggerMessage::getOcppOperationType(){ return "TriggerMessage"; } void TriggerMessage::processReq(JsonObject payload) { - AO_DBG_INFO("Warning: TriggerMessage is not fully tested"); - const char *requestedMessage = payload["requestedMessage"] | "Invalid"; - const int connectorId = payload["connectorId"] | 0; + const int connectorId = payload["connectorId"] | -1; - if (ocppModel && ocppModel->getChargePointStatusService()) { - if (connectorId < 0 || connectorId >= ocppModel->getChargePointStatusService()->getNumConnectors()) { - formatError = true; - } - } + AO_DBG_INFO("Execute for message type %s, connectorId = %i", requestedMessage, connectorId); - if (!formatError) { - AO_DBG_INFO("Execute for message type %s, connectorId = %i", requestedMessage, connectorId); - if (!strcmp(requestedMessage, "MeterValues")) { - //special case MeterValues needs unique handling - if (ocppModel && ocppModel->getMeteringService()) { - triggeredOperation = ocppModel->getMeteringService()->retrieveMeterValues(connectorId); + statusMessage = "Rejected"; - if (!triggeredOperation) { - formatError = true; + if (!strcmp(requestedMessage, "MeterValues")) { + if (ocppModel && ocppModel->getMeteringService()) { + auto mService = ocppModel->getMeteringService(); + if (connectorId < 0) { + auto nConnectors = mService->getNumConnectors(); + for (decltype(nConnectors) i = 0; i < nConnectors; i++) { + triggeredOperations.push_back(mService->takeMeterValuesNow(i)); } + } else if (connectorId < mService->getNumConnectors()) { + triggeredOperations.push_back(mService->takeMeterValuesNow(connectorId)); } else { - AO_DBG_WARN("MeteringService not initialized"); + errorCode = "PropertyConstraintViolation"; } - } else { - triggeredOperation = makeOcppOperation(requestedMessage, connectorId); - if (!triggeredOperation) { - statusMessage = "NotImplemented"; + } + } else if (!strcmp(requestedMessage, "StatusNotification")) { + if (ocppModel && ocppModel->getChargePointStatusService()) { + auto cpsService = ocppModel->getChargePointStatusService(); + if (connectorId < 0) { + auto nConnectors = cpsService->getNumConnectors(); + for (decltype(nConnectors) i = 0; i < nConnectors; i++) { + triggeredOperations.push_back(makeOcppOperation(requestedMessage, i)); + } + } else if (connectorId < cpsService->getNumConnectors()) { + triggeredOperations.push_back(makeOcppOperation(requestedMessage, connectorId)); + } else { + errorCode = "PropertyConstraintViolation"; } } + } else { + auto msg = makeOcppOperation(requestedMessage, connectorId); + if (msg) { + triggeredOperations.push_back(std::move(msg)); + } else { + statusMessage = "NotImplemented"; + } } - if (triggeredOperation) { + if (!triggeredOperations.empty()) { statusMessage = "Accepted"; } else { - AO_DBG_WARN("Could not make OppOperation from TriggerMessage. Ignore request"); - if (!statusMessage) { - statusMessage = "Rejected"; + if (errorCode) { + AO_DBG_ERR("errorCode: %s", errorCode); + } else { + AO_DBG_WARN("TriggerMessage denied. statusMessage: %s", statusMessage); } } + } std::unique_ptr TriggerMessage::createConf(){ @@ -69,9 +79,14 @@ std::unique_ptr TriggerMessage::createConf(){ JsonObject payload = doc->to(); payload["status"] = statusMessage; - - if (triggeredOperation && defaultOcppEngine) //from the second createConf()-try on, do not initiate further OCPP ops - defaultOcppEngine->initiateOperation(std::move(triggeredOperation)); + + if (defaultOcppEngine) { + auto op = triggeredOperations.begin(); + while (op != triggeredOperations.end()) { + defaultOcppEngine->initiateOperation(std::move(triggeredOperations.front())); + op = triggeredOperations.erase(op); + } + } return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h index b1ea7594..ef0bf316 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h @@ -15,12 +15,12 @@ namespace Ocpp16 { class TriggerMessage : public OcppMessage { private: - std::unique_ptr triggeredOperation; + std::vector> triggeredOperations; const char *statusMessage {nullptr}; - bool formatError = false; + const char *errorCode = nullptr; public: - TriggerMessage(); + TriggerMessage() = default; const char* getOcppOperationType(); @@ -28,7 +28,7 @@ class TriggerMessage : public OcppMessage { std::unique_ptr createConf(); - const char *getErrorCode() {return formatError ? "FormationViolation" : nullptr;} + const char *getErrorCode() {return errorCode;} }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index 40d43c60..2e3d6d2a 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -22,7 +22,7 @@ ChargePointStatusService::ChargePointStatusService(OcppEngine& context, unsigned std::shared_ptr> numberOfConnectors = - declareConfiguration("NumberOfConnectors", numConn, CONFIGURATION_FN, false, true, true, false); + declareConfiguration("NumberOfConnectors", numConn >= 1 ? numConn - 1 : 0, CONFIGURATION_FN, false, true, false, false); *numberOfConnectors = numConn; const char *fpId = "Core,RemoteTrigger"; diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index 60df807d..0eaa0d09 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -40,15 +40,19 @@ void ConnectorMeterValuesRecorder::takeSample() { OcppMessage *ConnectorMeterValuesRecorder::loop() { + if (*MeterValueSampleInterval < 1) { + //Metering off by definition + clear(); + return nullptr; + } + /* * First: check if there was a transaction break (i.e. transaction either started or stopped; transactionId changed) */ auto connector = context.getConnectorStatus(connectorId); if (connector && connector->getTransactionId() != lastTransactionId) { //transaction break occured! - OcppMessage *result {nullptr}; - if (sampleTimestamp.size() > 0) - result = toMeterValues(); + auto result = toMeterValues(); lastTransactionId = connector->getTransactionId(); return result; } @@ -79,7 +83,9 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { OcppMessage *ConnectorMeterValuesRecorder::toMeterValues() { if (sampleTimestamp.size() == 0) { - AO_DBG_WARN("Creating MeterValues without any content"); + AO_DBG_DEBUG("Checking if to send MeterValues ... No"); + clear(); + return nullptr; } //decide which measurands to send. If a measurand is missing at at least one point in time, omit that measurand completely @@ -109,6 +115,37 @@ OcppMessage *ConnectorMeterValuesRecorder::toMeterValues() { return nullptr; } +OcppMessage *ConnectorMeterValuesRecorder::takeMeterValuesNow() { + + if (!energySampler && !powerSampler) { + return nullptr; + } + + decltype(sampleTimestamp) t_now; + decltype(energy) e_now; + decltype(power) p_now; + + if (context.getOcppTime().isValid()) { + t_now.push_back(context.getOcppTime().getOcppTimestampNow()); + } + + if (energySampler) { + e_now.push_back(energySampler()); + } + + if (powerSampler) { + p_now.push_back(powerSampler()); + } + + int txId_now = -1; + auto connector = context.getConnectorStatus(connectorId); + if (connector) { + txId_now = connector->getTransactionId(); + } + + return new MeterValues(&t_now, &e_now, &p_now, connectorId, txId_now); +} + void ConnectorMeterValuesRecorder::clear() { sampleTimestamp.clear(); energy.clear(); diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h index 9448e964..9976067d 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h @@ -43,18 +43,20 @@ class ConnectorMeterValuesRecorder { std::shared_ptr> MeterValuesSampledDataMaxLength = NULL; void takeSample(); + OcppMessage *toMeterValues(); void clear(); public: ConnectorMeterValuesRecorder(OcppModel& context, int connectorId); OcppMessage *loop(); - OcppMessage *toMeterValues(); void setPowerSampler(PowerSampler powerSampler); void setEnergySampler(EnergySampler energySampler); float readEnergyActiveImportRegister(); + + OcppMessage *takeMeterValuesNow(); }; } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp index 295626ca..6260a55c 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp @@ -53,17 +53,21 @@ float MeteringService::readEnergyActiveImportRegister(int connectorId) { return connectors[connectorId]->readEnergyActiveImportRegister(); } -std::unique_ptr MeteringService::retrieveMeterValues(int connectorId) { +std::unique_ptr MeteringService::takeMeterValuesNow(int connectorId) { if (connectorId < 0 || connectorId >= connectors.size()) { AO_DBG_ERR("connectorId out of bounds. Ignore"); return nullptr; } auto& connector = connectors.at(connectorId); if (connector.get()) { - auto msg = connector->toMeterValues(); - auto meterValues = makeOcppOperation(msg); - meterValues->setTimeout(std::unique_ptr{new FixedTimeout(120000)}); - return meterValues; + auto msg = connector->takeMeterValuesNow(); + if (msg) { + auto meterValues = makeOcppOperation(msg); + meterValues->setTimeout(std::unique_ptr{new FixedTimeout(120000)}); + return meterValues; + } + AO_DBG_DEBUG("Did not take any samples for connectorId %d", connectorId); + return nullptr; } AO_DBG_ERR("Could not find connector"); return nullptr; diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h index 5b4dcab7..035db5f6 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h @@ -33,7 +33,9 @@ class MeteringService { float readEnergyActiveImportRegister(int connectorId); - std::unique_ptr retrieveMeterValues(int connectorId); //returns all recorded MeterValues and deletes own records + std::unique_ptr takeMeterValuesNow(int connectorId); //snapshot of all meters now + + int getNumConnectors() {return connectors.size();} }; } //end namespace ArduinoOcpp From c7a5874064ae6e99db1626a74027c09e5b9d1030 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 3 Mar 2022 12:00:13 +0100 Subject: [PATCH 133/696] add ClearCache message type --- README.md | 2 +- src/ArduinoOcpp/MessagesV16/ClearCache.cpp | 27 +++++++++++++++++++ src/ArduinoOcpp/MessagesV16/ClearCache.h | 26 ++++++++++++++++++ .../SimpleOcppOperationFactory.cpp | 3 +++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 src/ArduinoOcpp/MessagesV16/ClearCache.cpp create mode 100644 src/ArduinoOcpp/MessagesV16/ClearCache.h diff --git a/README.md b/README.md index bbdeb48a..b2e1f7fb 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ In case you use PlatformIO, you can copy all dependencies from `platformio.ini` | `BootNotification` | :heavy_check_mark: | | `ChangeAvailability` | :heavy_check_mark: | | `ChangeConfiguration` | :heavy_check_mark: | -| `ClearCache` | | | :heavy_multiplication_x: | +| `ClearCache` | :heavy_check_mark: | | `DataTransfer` | :heavy_check_mark: | | `GetConfiguration` | :heavy_check_mark: | | `Heartbeat` | :heavy_check_mark: | diff --git a/src/ArduinoOcpp/MessagesV16/ClearCache.cpp b/src/ArduinoOcpp/MessagesV16/ClearCache.cpp new file mode 100644 index 00000000..d761c135 --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/ClearCache.cpp @@ -0,0 +1,27 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#include +#include + +using ArduinoOcpp::Ocpp16::ClearCache; + +ClearCache::ClearCache() { + +} + +const char* ClearCache::getOcppOperationType(){ + return "ClearCache"; +} + +void ClearCache::processReq(JsonObject payload) { + AO_DBG_WARN("Authorization Cache not supported - ClearCache is without effect"); +} + +std::unique_ptr ClearCache::createConf(){ + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); + JsonObject payload = doc->to(); + payload["status"] = "Accepted"; //"Accepted", because the intended postcondition is true + return doc; +} diff --git a/src/ArduinoOcpp/MessagesV16/ClearCache.h b/src/ArduinoOcpp/MessagesV16/ClearCache.h new file mode 100644 index 00000000..ea166580 --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/ClearCache.h @@ -0,0 +1,26 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef CLEARCACHE_H +#define CLEARCACHE_H + +#include + +namespace ArduinoOcpp { +namespace Ocpp16 { + +class ClearCache : public OcppMessage { +public: + ClearCache(); + + const char* getOcppOperationType(); + + void processReq(JsonObject payload); + + std::unique_ptr createConf(); +}; + +} //end namespace Ocpp16 +} //end namespace ArduinoOcpp +#endif diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index b4cf0ad9..d4deeb38 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -274,6 +275,8 @@ std::unique_ptr makeOcppOperation(const char *messageType, int co msg = std::unique_ptr(new Ocpp16::ClearChargingProfile()); } else if (!strcmp(messageType, "ChangeAvailability")) { msg = std::unique_ptr(new Ocpp16::ChangeAvailability()); + } else if (!strcmp(messageType, "ClearCache")) { + msg = std::unique_ptr(new Ocpp16::ClearCache()); } else { AO_DBG_WARN("Operation not supported"); msg = std::unique_ptr(new NotImplemented()); From f905484a33e8ff059760762b824a10511d386aa0 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 3 Mar 2022 13:27:43 +0100 Subject: [PATCH 134/696] make read-only configurations non-persistent --- src/ArduinoOcpp/Core/Configuration.cpp | 54 +++---------------- src/ArduinoOcpp/Core/Configuration.h | 4 +- .../ChargePointStatusService.cpp | 5 +- .../Tasks/Diagnostics/DiagnosticsService.cpp | 2 +- .../FirmwareManagement/FirmwareService.cpp | 2 +- .../Metering/ConnectorMeterValuesRecorder.cpp | 2 +- .../SmartCharging/SmartChargingService.cpp | 4 +- 7 files changed, 16 insertions(+), 57 deletions(-) diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index 3c78e975..d933e264 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -54,10 +54,15 @@ std::shared_ptr> createConfiguration(const char *key } std::shared_ptr createConfigurationContainer(const char *filename) { - if (configurationFilesystemOpt.accessAllowed()) { - return std::static_pointer_cast(std::make_shared(filename)); - } else { + //create non-persistent Configuration store (i.e. lives only in RAM) if + // - Flash FS usage is switched off OR + // - Filename starts with "/volatile" + if (!configurationFilesystemOpt.accessAllowed() || + !strncmp(filename, CONFIGURATION_VOLATILE, strlen(CONFIGURATION_VOLATILE))) { return std::static_pointer_cast(std::make_shared(filename)); + } else { + //create persistent Configuration store. This is the normal case + return std::static_pointer_cast(std::make_shared(filename)); } } @@ -145,49 +150,6 @@ std::shared_ptr> declareConfiguration(const char *key, T defaul namespace Ocpp16 { -/* -template -std::shared_ptr> changeConfiguration(const char *key, T value) { - - std::shared_ptr> config = nullptr; - - for (std::vector>::iterator container = configurationContainers.begin(); container != configurationContainers.end(); container++) { - - config = std::static_pointer_cast>(*container)->getConfiguration(key); - if (config) { - break; - } - } - - if (config) { - //found configuration - - if (!config->permissionRemotePeerCanWrite()) { - return nullptr; - } - - *config = value; - - } else { - //did not find configuration. Create new one - config = createConfiguration(key, value); - - if (!config) { - AO_DBG_ERR("Cannot neither find configuration nor create new one! Abort"); - return nullptr; - } - - std::shared_ptr containerDefault = getContainer(CONFIGURATION_FN); - - if (containerDefault) { - containerDefault->addConfiguration(config); - } //else: There is no container. Probably this controller does not need persistency at all - } - - return config; -} -*/ - std::shared_ptr getConfiguration(const char *key) { std::shared_ptr result = nullptr; diff --git a/src/ArduinoOcpp/Core/Configuration.h b/src/ArduinoOcpp/Core/Configuration.h index f597d408..da85327d 100644 --- a/src/ArduinoOcpp/Core/Configuration.h +++ b/src/ArduinoOcpp/Core/Configuration.h @@ -10,6 +10,7 @@ #include #define CONFIGURATION_FN "/arduino-ocpp.cnf" +#define CONFIGURATION_VOLATILE "/volatile" namespace ArduinoOcpp { @@ -23,9 +24,6 @@ std::vector>::iterator getConfigurationC namespace Ocpp16 { -// template -// std::shared_ptr> changeConfiguration(const char *key, T value); - std::shared_ptr getConfiguration(const char *key); std::shared_ptr>> getAllConfigurations(); } diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index 2e3d6d2a..22aaa061 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -22,13 +22,12 @@ ChargePointStatusService::ChargePointStatusService(OcppEngine& context, unsigned std::shared_ptr> numberOfConnectors = - declareConfiguration("NumberOfConnectors", numConn >= 1 ? numConn - 1 : 0, CONFIGURATION_FN, false, true, false, false); - *numberOfConnectors = numConn; + declareConfiguration("NumberOfConnectors", numConn >= 1 ? numConn - 1 : 0, CONFIGURATION_VOLATILE, false, true, false, false); const char *fpId = "Core,RemoteTrigger"; const char *fpIdCore = "Core"; const char *fpIdRTrigger = "RemoteTrigger"; - auto fProfile = declareConfiguration("SupportedFeatureProfiles",fpId, CONFIGURATION_FN, false, true, true, false); + auto fProfile = declareConfiguration("SupportedFeatureProfiles",fpId, CONFIGURATION_VOLATILE, false, true, true, false); if (!strstr(*fProfile, fpIdCore)) { auto fProfilePlus = std::string(*fProfile); if (!fProfilePlus.empty() && fProfilePlus.back() != ',') diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp index 2a2cc386..8540b4d6 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp @@ -16,7 +16,7 @@ using Ocpp16::DiagnosticsStatus; DiagnosticsService::DiagnosticsService(OcppEngine& context) : context(context) { const char *fpId = "FirmwareManagement"; - auto fProfile = declareConfiguration("SupportedFeatureProfiles",fpId, CONFIGURATION_FN, false, true, true, false); + auto fProfile = declareConfiguration("SupportedFeatureProfiles",fpId, CONFIGURATION_VOLATILE, false, true, true, false); if (!strstr(*fProfile, fpId)) { auto fProfilePlus = std::string(*fProfile); if (!fProfilePlus.empty() && fProfilePlus.back() != ',') diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp index bd1e215d..e32b1d11 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp @@ -19,7 +19,7 @@ using ArduinoOcpp::Ocpp16::FirmwareStatus; FirmwareService::FirmwareService(OcppEngine& context) : context(context) { const char *fpId = "FirmwareManagement"; - auto fProfile = declareConfiguration("SupportedFeatureProfiles",fpId, CONFIGURATION_FN, false, true, true, false); + auto fProfile = declareConfiguration("SupportedFeatureProfiles",fpId, CONFIGURATION_VOLATILE, false, true, true, false); if (!strstr(*fProfile, fpId)) { auto fProfilePlus = std::string(*fProfile); if (!fProfilePlus.empty() && fProfilePlus.back() != ',') diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index 0eaa0d09..659bd265 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -20,7 +20,7 @@ ConnectorMeterValuesRecorder::ConnectorMeterValuesRecorder(OcppModel& context, i power = std::vector(); MeterValueSampleInterval = declareConfiguration("MeterValueSampleInterval", 60); - MeterValuesSampledDataMaxLength = declareConfiguration("MeterValuesSampledDataMaxLength", 4); + MeterValuesSampledDataMaxLength = declareConfiguration("MeterValuesSampledDataMaxLength", 4, CONFIGURATION_VOLATILE, false, true, false, false); } void ConnectorMeterValuesRecorder::takeSample() { diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index 1d900516..b4cd67ce 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -43,10 +43,10 @@ SmartChargingService::SmartChargingService(OcppEngine& context, float chargeLimi TxDefaultProfile[i] = NULL; TxProfile[i] = NULL; } - declareConfiguration("ChargeProfileMaxStackLevel", CHARGEPROFILEMAXSTACKLEVEL, CONFIGURATION_FN, false, true, false, true); + declareConfiguration("ChargeProfileMaxStackLevel", CHARGEPROFILEMAXSTACKLEVEL, CONFIGURATION_VOLATILE, false, true, false, false); const char *fpId = "SmartCharging"; - auto fProfile = declareConfiguration("SupportedFeatureProfiles",fpId, CONFIGURATION_FN, false, true, true, false); + auto fProfile = declareConfiguration("SupportedFeatureProfiles",fpId, CONFIGURATION_VOLATILE, false, true, true, false); if (!strstr(*fProfile, fpId)) { auto fProfilePlus = std::string(*fProfile); if (!fProfilePlus.empty() && fProfilePlus.back() != ',') From 448455ce7e88df7fdda93a11158af22d8671ab28 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 4 Mar 2022 18:21:32 +0100 Subject: [PATCH 135/696] allow console output via custom implementation --- src/ArduinoOcpp/Debug.h | 15 +++++++++++++-- src/ArduinoOcpp/Platform.cpp | 24 ++++++++++++++++++++++++ src/ArduinoOcpp/Platform.h | 30 ++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 src/ArduinoOcpp/Platform.cpp diff --git a/src/ArduinoOcpp/Debug.h b/src/ArduinoOcpp/Debug.h index 3fa33715..78ece3c8 100644 --- a/src/ArduinoOcpp/Debug.h +++ b/src/ArduinoOcpp/Debug.h @@ -56,8 +56,19 @@ #endif #ifdef AO_TRAFFIC_OUT -#define AO_DBG_TRAFFIC_OUT(...) AO_CONSOLE_PRINTF("[AO] To WS lib: %s\n",__VA_ARGS__) -#define AO_DBG_TRAFFIC_IN(...) AO_CONSOLE_PRINTF("[AO] From WS lib: %s\n",__VA_ARGS__) + +#define AO_DBG_TRAFFIC_OUT(...) \ + do { \ + AO_CONSOLE_PRINTF("[AO] To WS lib: %s",__VA_ARGS__); \ + AO_CONSOLE_PRINTF("\n"); \ + } while (0) + +#define AO_DBG_TRAFFIC_IN(...) \ + do { \ + AO_CONSOLE_PRINTF("[AO] From WS lib: %s",__VA_ARGS__); \ + AO_CONSOLE_PRINTF("\n"); \ + } while (0) + #else #define AO_DBG_TRAFFIC_OUT(...) #define AO_DBG_TRAFFIC_IN(...) diff --git a/src/ArduinoOcpp/Platform.cpp b/src/ArduinoOcpp/Platform.cpp new file mode 100644 index 00000000..d8932513 --- /dev/null +++ b/src/ArduinoOcpp/Platform.cpp @@ -0,0 +1,24 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#include + +#ifdef AO_CUSTOM_CONSOLE + +namespace ArduinoOcpp { +void (*ao_console_out_impl)(const char *msg) = nullptr; +} + +void ArduinoOcpp::ao_console_out(const char *msg) { + if (ao_console_out_impl) { + ao_console_out_impl(msg); + } +} + +void ao_set_console_out(void (*console_out)(const char *msg)) { + ArduinoOcpp::ao_console_out_impl = console_out; + console_out("[AO] console initialized\n"); +} + +#endif diff --git a/src/ArduinoOcpp/Platform.h b/src/ArduinoOcpp/Platform.h index 2d1fad88..25f9f322 100644 --- a/src/ArduinoOcpp/Platform.h +++ b/src/ArduinoOcpp/Platform.h @@ -5,6 +5,36 @@ #ifndef AO_PLATFORM_H #define AO_PLATFORM_H +#ifdef AO_CUSTOM_CONSOLE + +#ifndef AO_CUSTOM_CONSOLE_MAXMSGSIZE +#define AO_CUSTOM_CONSOLE_MAXMSGSIZE 128 +#endif + +void ao_set_console_out(void (*console_out)(const char *msg)); + +namespace ArduinoOcpp { +void ao_console_out(const char *msg); +} +#define AO_CONSOLE_PRINTF(X, ...) \ + do { \ + char msg [AO_CUSTOM_CONSOLE_MAXMSGSIZE]; \ + snprintf(msg, AO_CUSTOM_CONSOLE_MAXMSGSIZE, X, ##__VA_ARGS__); \ + sprintf(msg + AO_CUSTOM_CONSOLE_MAXMSGSIZE - 7, " [...]"); \ + ao_console_out(msg); \ + } while (0) +#else +#define ao_set_console_out(X) \ + do { \ + X("[AO] CONSOLE ERROR: ao_set_console_out ignored if AO_CUSTOM_CONSOLE " \ + "not defined\n"); \ + char msg [100]; \ + snprintf(msg, 100, " > see %s:%i",__FILE__,__LINE__); \ + X(msg); \ + X("\n > see ArduinoOcpp/Platform.h\n"); \ + } while (0) +#endif + #ifndef AO_CONSOLE_PRINTF //begin with Arduino support, add more later #include From 4dd6e0b2f7e704df3bfbd3b6314a37e02df473aa Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 7 Mar 2022 09:56:07 +0100 Subject: [PATCH 136/696] minor changes --- README.md | 1 + src/ArduinoOcpp.cpp | 2 +- src/ArduinoOcpp/Core/OcppOperation.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b2e1f7fb..27842b7e 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,7 @@ In case you use PlatformIO, you can copy all dependencies from `platformio.ini` - [x] add support for multiple power connectors - [x] add support for the ESP32 - [ ] reach full compliance to OCPP 1.6 Smart Charging Profile +- [ ] integrate Authorization Cache - [ ] **get ready for OCPP 2.0.1** ## Further help diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 1f19a983..60f8b841 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -482,7 +482,7 @@ bool isInSession() { const char *getSessionIdTag() { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_WARN("Please call OCPP_initialize before"); return nullptr; } auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); diff --git a/src/ArduinoOcpp/Core/OcppOperation.cpp b/src/ArduinoOcpp/Core/OcppOperation.cpp index b0e99e99..52a9f7e2 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.cpp +++ b/src/ArduinoOcpp/Core/OcppOperation.cpp @@ -131,7 +131,7 @@ boolean OcppOperation::sendReq(OcppSocket& ocppSocket){ if (printReqCounter > 5000) { printReqCounter = 0; - AO_DBG_DEBUG("Try to send requirement: %s", out.c_str()); + AO_DBG_DEBUG("Try to send request: %s", out.c_str()); } printReqCounter++; From 3e289b86427c290d5181ba159206a0749d91b5cb Mon Sep 17 00:00:00 2001 From: Matthias Akstaller <63792403+matth-x@users.noreply.github.com> Date: Mon, 7 Mar 2022 12:38:12 +0100 Subject: [PATCH 137/696] update API documentation --- README.md | 71 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 27842b7e..77a7c436 100644 --- a/README.md +++ b/README.md @@ -17,19 +17,18 @@ You can build an OCPP Charge Point controller using the popular, Wi-Fi enabled m :heavy_check_mark: Tested with two further (proprietary) central systems -:heavy_check_mark: Integrated and tested in many charging stations (including a ClipperCreek Inc. station) +:heavy_check_mark: Integrated and tested in many charging stations ### Features -- lets you initiate all supported OCPP operations (see the table at the bottom of this page) -- responds to requests from the central system and notifies your client code by listeners -- manages the EVSE data model as specified by OCPP and does a lot of the paperwork. For example, it sends `StatusNotification` or `MeterValues` messages by itself. +- handles the OCPP communication with the charging network +- implements the standard OCPP charging process +- provides an API for the integration of the hardware modules of your PCB +- works with any TLS implementation and WebSocket library. E.g. + - Arduino networking stack: [Links2004/arduinoWebSockets](https://github.com/Links2004/arduinoWebSockets), or + - generic embedded systems: [Mongoose Networking Library](https://github.com/cesanta/mongoose) -You still have the responsibility (or freedom) to design the application logic of the charger and to integrate the HW components. This library doesn't - -- define physical reactions on the messages from the central system (CS). For example, when you initiate a `StartTransaction` request which the CS accepts, the library stores the new EVSE status including the `transactionId`, but lets you define which action to take. - -For simple chargers, the application logic + HW integration is far below 1000 LOCs. +For simple chargers, the necessary hardware and internet integration is usually far below 1000 LOCs. ## Usage guide @@ -41,9 +40,10 @@ Please take `examples/ESP/main.cpp` as the starting point for your first project - In your project's `main` file, include `ArduinoOcpp.h`. This gives you a simple access to all functions. -- Before establishing an OCPP connection you have to ensure that your device has access to a Wi-Fi access point. All debug messages are printed on the standard serial (i.e. `Serial.print(F("debug msg"))`). +- Before establishing an OCPP connection you have to ensure that your device has access to a Wi-Fi access point. All debug messages are printed on the standard serial (i.e. `Serial.print("debug msg")`). To redirect debug messages, please refer to `src/ArduinoOcpp/Platform.h`. - To connect to your OCPP Central System, call `OCPP_initialize(String OCPP_HOST, uint16_t OCPP_PORT, String OCPP_URL)`. You need to insert the address parameters according to the configuration of your central system. Internally, the library passes these parameters to the WebSocket object without further alteration. + - To secure the connection with TLS, you have to configure the WebSocket. Please take `examples/SECC/main.cpp` as an example. - In your `setup()` function, you can add the configuration functions from `ArduinoOcpp.h` to properly integrate your hardware. All configuration functions are documented in `ArduinoOcpp.h`. For example, to integrate the energy meter of your EVSE, add @@ -55,9 +55,11 @@ setEnergyActiveImportSampler([]() { - Add `OCPP_loop()` to your `loop()` function. -- There are a couple of OCPP operations you can initialize on your EVSE. For example, to send a `Boot Notification`, use the function +### Sending OCPP operations + +There are a couple of OCPP operations you can initialize on your EVSE. For example, to send a `Boot Notification`, use the function ```cpp -void bootNotification(String chargePointModel, String chargePointVendor, OnReceiveConfListener onConf = NULL, ...)` +void bootNotification(const char *chargePointModel, const char *chargePointVendor, OnReceiveConfListener onConf = nullptr, ...)` ``` In practice, it looks like this: @@ -67,12 +69,13 @@ void setup() { ... //other code including the initialization of Wi-Fi and OCPP - bootNotification("GPIO-based CP model", "Greatest EVSE vendor", [] (JsonObject confMsg) { + bootNotification("My CP model name", "My company name", [] (JsonObject confMsg) { //This callback is executed when the .conf() response from the central system arrives Serial.print(F("BootNotification was answered. Central System clock: ")); - Serial.println(confMsg["currentTime"].as()); - - evseIsBooted = true; //notify your hardware that the BootNotification.conf() has arrived + Serial.println(confMsg["currentTime"].as()); //"currentTime" is a field of the central system response + + //Notify your hardare that the BootNotification.conf() has arrived. E.g.: + //evseIsBooted = true; }); ... //rest of setup() function; executed immediately as bootNotification() is non-blocking @@ -89,37 +92,52 @@ For your first EVSE integration, the `onReceiveConfListener` is probably suffici - `onReceiveErrorListener`: will be called when the Central System returns a CallError. Again, each error also triggers the `onAbortListener`. -Following example shows the correct usage of all listeners. - +The following example shows the correct usage of all listeners. ```cpp authorize(idTag, [](JsonObject conf) { //onReceiveConfListener (optional but very likely necessary for your integration) successfullyAuthorized = true; //example client code - ... //further client code, e.g. call startTransaction() + ... //further client code, e.g. beginSession(idTag); }, []() { //onAbortListener (optional) - Serial.print(F("[EVSE] Could not authorize charging session! Aborted\n")); //flash error light etc. + Serial.print(F("[EVSE] Could not authorize charging session. Aborted\n")); //flash error light etc. }, []() { //onTimeoutListener (optional) - Serial.print(F("[EVSE] Could not authorize charging session! Reason: timeout\n")); + Serial.print(F("[EVSE] Could not authorize charging session. Reason: timeout\n")); }, [](const char *code, const char *description, JsonObject details) { //onReceiveErrorListener (optional) - Serial.print(F("[EVSE] Could not authorize charging session! Reason: received OCPP error: ")); + Serial.print(F("[EVSE] Could not authorize charging session. Reason: received OCPP error: ")); Serial.println(code); }); ``` -The library also reacts on CS-initiated operations. You can add your own behavior there too. For example, when you want to flash a LED on receipt of a `Set Charging Profile` request, use the following function. +### Receiving OCPP operations + +The library also reacts on CS-initiated operations. You can add your own behavior there too. For example, to flash a LED on receipt of a `Set Charging Profile` request, use the following function. ```cpp -void setOnSetChargingProfileRequest(void listener(JsonObject payload)); +setOnSetChargingProfileRequest([] (JsonObject payload) { + //... +}); ``` You can also process the original payload from the CS using the `payload` object. -To get started quickly with or without EVSE hardware, you can flash the sketch in `examples/SECC` onto your ESP. That example mimics a full OCPP communications controller as it would look like in a real charging station. You can build a charger prototype based on that example or just view the internal state using the device monitor. +### Transaction and Session management + +In practice, there are a couple of prerequisites for OCPP-transactions. The library abstracts them into two conditions: +1) The electric connection between the EVSE and EV is established and safe +2) The user is identified, authorized and shows the intention to charge the vehicle + +Your hardware integration is fully responsible for Condition (1). To make the OCPP library aware about the safe connection, set a callback function with `setConnectorPluggedSampler(std::function connectorPlugged)`. Condition (2) is handled by both the hardware integration and the OCPP library. To make the library aware about Condition (2) use the functions `beginSession(const char *idTag)` and `endSession()`. Note that internally, the OCPP library uses the same functions if it receives a `RemoteStartTransaction` request, or stops a transaction for any reason. + +In return, the function `bool ocppPermitsCharge()` notifies your hardware implementation if the OCPP transaction is engaged and the EVSE can charge the EV ultimately. + +Alternatively, you can manage OCPP transactions by yourself using `startTransaction(const char *idTag)` and `stopTransaction()`. + +*To get started quickly with or without EVSE hardware, you can flash the sketch in `examples/SECC` onto your ESP. That example mimics a full OCPP communications controller as it would look like in a real charging station. You can build a charger prototype based on that example or just view the internal state using the device monitor.* ## Dependencies @@ -163,9 +181,6 @@ In case you use PlatformIO, you can copy all dependencies from `platformio.ini` ## Next development steps -- [x] introduce a timeout mechanism -- [x] some refactoring steps (e.g. separate RPC header from OCPP payload creation) -- [x] add facade for rapid integration - [x] introduce proper offline behavior and package loss / fault detection - [x] handle fragmented input messages correctly - [x] add support for multiple power connectors From 17a03b328ff493a0fbbec22b0647bb1a85d485d9 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 7 Mar 2022 14:04:16 +0100 Subject: [PATCH 138/696] remove obsolete headers, update PIO version --- library.json | 4 ++-- library.properties | 2 +- .../Core/ConfigurationContainer.cpp | 1 - src/ArduinoOcpp/Core/OcppEngine.cpp | 2 -- src/ArduinoOcpp/Core/OcppOperationTimeout.h | 1 - src/ArduinoOcpp/Core/OcppServer.cpp | 12 ++++++++-- src/ArduinoOcpp/Core/OcppSocket.h | 1 - src/ArduinoOcpp/Core/OcppTime.h | 2 -- .../MessagesV16/GetConfiguration.cpp | 2 -- src/ArduinoOcpp/MessagesV16/Heartbeat.h | 2 -- .../MessagesV16/RemoteStopTransaction.cpp | 2 -- .../MessagesV16/RemoteStopTransaction.h | 2 -- .../Tasks/Diagnostics/DiagnosticsService.h | 1 - .../FirmwareManagement/FirmwareService.h | 1 - src/Variants.h | 24 ------------------- 15 files changed, 13 insertions(+), 46 deletions(-) delete mode 100644 src/Variants.h diff --git a/library.json b/library.json index cc40f14c..63c040ff 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "ArduinoOcpp", - "version": "0.0.6", + "version": "0.1.0", "description": "OCPP 1.6 Client for the ESP8266 and ESP32", "keywords": "OCPP, 1.6, OCPP 1.6, Smart Energy, Smart Charging, client, ESP8266, ESP32, Arduino, EVSE, Charge Point", "repository": @@ -67,7 +67,7 @@ ] }, { - "name": "ESP GPIO-based Supply Equipment Communications Controller SECC", + "name": "EVSE Communications Controller", "base": "examples/SECC", "files": [ "main.cpp", diff --git a/library.properties b/library.properties index bad6b924..7e944f3f 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ArduinoOcpp -version=0.0.1 +version=0.1.0 author=Matthias Akstaller maintainer=Matthias Akstaller sentence=OCPP 1.6 Client for the ESP8266 and ESP32 (more coming soon) diff --git a/src/ArduinoOcpp/Core/ConfigurationContainer.cpp b/src/ArduinoOcpp/Core/ConfigurationContainer.cpp index 246b6b56..9b3418b1 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainer.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainer.cpp @@ -3,7 +3,6 @@ // MIT License #include -#include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/Core/OcppEngine.cpp b/src/ArduinoOcpp/Core/OcppEngine.cpp index 86171658..d8c297c8 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.cpp +++ b/src/ArduinoOcpp/Core/OcppEngine.cpp @@ -7,8 +7,6 @@ #include #include -#include - using namespace ArduinoOcpp; OcppEngine *ArduinoOcpp::defaultOcppEngine = nullptr; diff --git a/src/ArduinoOcpp/Core/OcppOperationTimeout.h b/src/ArduinoOcpp/Core/OcppOperationTimeout.h index 391b665d..0ab58503 100644 --- a/src/ArduinoOcpp/Core/OcppOperationTimeout.h +++ b/src/ArduinoOcpp/Core/OcppOperationTimeout.h @@ -5,7 +5,6 @@ #ifndef OCPPOPERATIONTIMEOUT_H #define OCPPOPERATIONTIMEOUT_H -#include #include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/Core/OcppServer.cpp b/src/ArduinoOcpp/Core/OcppServer.cpp index c58c4938..2614a66d 100644 --- a/src/ArduinoOcpp/Core/OcppServer.cpp +++ b/src/ArduinoOcpp/Core/OcppServer.cpp @@ -3,14 +3,22 @@ // MIT License #include - -#include +#include #ifndef AO_CUSTOM_WS +#define DEBUG_OUT (AO_DBG_LEVEL >= AO_DL_INFO) + +#ifdef AO_TRAFFIC_OUT +#define TRAFFIC_OUT true +#else +#define TRAFFIC_OUT false +#endif + using namespace ArduinoOcpp::EspWiFi; OcppServer::OcppServer() { + AO_DBG_WARN("OCPP Server only suitable for tests at the moment"); wsockServer.begin(); wsockServer.onEvent([this](WsClient num, WStype_t type, uint8_t * payload, size_t length) { this->wsockEvent(num, type, payload, length); diff --git a/src/ArduinoOcpp/Core/OcppSocket.h b/src/ArduinoOcpp/Core/OcppSocket.h index 58661fcd..feda4ffb 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.h +++ b/src/ArduinoOcpp/Core/OcppSocket.h @@ -5,7 +5,6 @@ #ifndef OCPPSOCKET_H #define OCPPSOCKET_H -#include #include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/Core/OcppTime.h b/src/ArduinoOcpp/Core/OcppTime.h index 5bc85444..b9b2cb0a 100644 --- a/src/ArduinoOcpp/Core/OcppTime.h +++ b/src/ArduinoOcpp/Core/OcppTime.h @@ -7,8 +7,6 @@ #include -#include - namespace ArduinoOcpp { typedef int32_t otime_t; //requires 32bit signed integer or bigger diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp index 16737d44..ccd4c57f 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp @@ -2,8 +2,6 @@ // Copyright Matthias Akstaller 2019 - 2022 // MIT License -#include - #include #include #include diff --git a/src/ArduinoOcpp/MessagesV16/Heartbeat.h b/src/ArduinoOcpp/MessagesV16/Heartbeat.h index 7d0b3d60..cfde53c7 100644 --- a/src/ArduinoOcpp/MessagesV16/Heartbeat.h +++ b/src/ArduinoOcpp/MessagesV16/Heartbeat.h @@ -7,8 +7,6 @@ #include -#include - namespace ArduinoOcpp { namespace Ocpp16 { diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp index e637d9a1..761b486d 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp @@ -2,8 +2,6 @@ // Copyright Matthias Akstaller 2019 - 2022 // MIT License -#include - #include #include #include diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h index bbb05dc0..c8a03a93 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.h @@ -7,8 +7,6 @@ #include -#include - namespace ArduinoOcpp { namespace Ocpp16 { diff --git a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h index 0906c448..72f9e37b 100644 --- a/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h +++ b/src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.h @@ -5,7 +5,6 @@ #ifndef DIAGNOSTICSSERVICE_H #define DIAGNOSTICSSERVICE_H -#include #include #include #include diff --git a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h index 6e8c145a..fb9ed2a1 100644 --- a/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h +++ b/src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.h @@ -5,7 +5,6 @@ #ifndef FIRMWARESERVICE_H #define FIRMWARESERVICE_H -#include #include #include diff --git a/src/Variants.h b/src/Variants.h deleted file mode 100644 index cb17375b..00000000 --- a/src/Variants.h +++ /dev/null @@ -1,24 +0,0 @@ -// matth-x/ArduinoOcpp -// Copyright Matthias Akstaller 2019 - 2022 -// MIT License - -#ifndef VARIANTS_H -#define VARIANTS_H - -#ifndef DEBUG_OUT -#ifdef AO_DEBUG_OUT -#define DEBUG_OUT true //Print debug messages on Serial. Gives more insights about inner processes -#else -#define DEBUG_OUT false //Print debug messages on Serial. Gives more insights about inner processes -#endif -#endif - -#ifndef TRAFFIC_OUT -#ifdef AO_TRAFFIC_OUT -#define TRAFFIC_OUT true //Print OCPP communication on Serial. Gives insights about communication with the Central System -#else -#define TRAFFIC_OUT false -#endif -#endif - -#endif From d4f705caf10bcdd1311512664a2d8c15c4a2db5d Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 8 Mar 2022 11:34:31 +0100 Subject: [PATCH 139/696] change log level --- src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp index 78d0da95..fd34dd7c 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp @@ -320,13 +320,13 @@ ChargingProfile::ChargingProfile(JsonObject &json){ if (!validFrom.setTime(json["validFrom"] | "Invalid")) { //non-success - AO_DBG_ERR("validFrom format violation. Expect format like 2022-02-01T20:53:32.486Z. Assume unlimited validity"); + AO_DBG_DEBUG("validFrom undefined. Expect format like 2022-02-01T20:53:32.486Z. Assume unlimited validity"); validFrom = MIN_TIME; } if (!validTo.setTime(json["validTo"] | "Invalid")) { //non-success - AO_DBG_ERR("validTo format violation. Expect format like 2022-02-01T20:53:32.486Z. Assume unlimited validity"); + AO_DBG_DEBUG("validTo undefined. Expect format like 2022-02-01T20:53:32.486Z. Assume unlimited validity"); validTo = MIN_TIME; } From 569240fb6f8fd43571012f252b651e11a7b6000e Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 9 Mar 2022 18:01:00 +0100 Subject: [PATCH 140/696] further configuration keys --- .../Tasks/ChargePointStatus/ChargePointStatusService.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index 22aaa061..c0cbe8cf 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -42,6 +42,12 @@ ChargePointStatusService::ChargePointStatusService(OcppEngine& context, unsigned fProfilePlus += fpIdRTrigger; fProfile->setValue(fProfilePlus.c_str(), fProfilePlus.length() + 1); } + + /* + * Further configuration keys which correspond to the Core profile + */ + declareConfiguration("AuthorizeRemoteTxRequests","false",CONFIGURATION_VOLATILE,false,true,false,false); + declareConfiguration("GetConfigurationMaxKeys",30,CONFIGURATION_VOLATILE,false,true,false,false); } ChargePointStatusService::~ChargePointStatusService() { From d36ccd5cdc5fb260e05e5e3f5513f45c61b7c6fc Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 16 Mar 2022 18:28:44 +0100 Subject: [PATCH 141/696] add generic MeterValue support --- src/ArduinoOcpp.cpp | 33 +++- src/ArduinoOcpp.h | 3 + src/ArduinoOcpp/MessagesV16/MeterValues.cpp | 71 +++------ src/ArduinoOcpp/MessagesV16/MeterValues.h | 8 +- .../MessagesV16/StartTransaction.cpp | 2 +- .../MessagesV16/StartTransaction.h | 2 +- .../MessagesV16/StopTransaction.cpp | 2 +- src/ArduinoOcpp/MessagesV16/StopTransaction.h | 2 +- .../Metering/ConnectorMeterValuesRecorder.cpp | 87 +++++------ .../Metering/ConnectorMeterValuesRecorder.h | 19 ++- src/ArduinoOcpp/Tasks/Metering/MeterValue.h | 61 ++++++++ .../Tasks/Metering/MeteringService.cpp | 10 +- .../Tasks/Metering/MeteringService.h | 5 +- src/ArduinoOcpp/Tasks/Metering/SampledValue.h | 143 ++++++++++++++++++ 14 files changed, 327 insertions(+), 121 deletions(-) create mode 100644 src/ArduinoOcpp/Tasks/Metering/MeterValue.h create mode 100644 src/ArduinoOcpp/Tasks/Metering/SampledValue.h diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 60f8b841..b32ae275 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -168,7 +168,16 @@ void setPowerActiveImportSampler(std::function power) { model.setMeteringSerivce(std::unique_ptr( new MeteringService(*ocppEngine, OCPP_NUMCONNECTORS))); } - model.getMeteringService()->setPowerSampler(OCPP_ID_OF_CONNECTOR, power); //connectorId=1 + SampledValueProperties meterProperties; + meterProperties.setMeasurand("Power.Active.Import"); + meterProperties.setUnit("W"); + auto mvs = std::unique_ptr>>( + new SampledValueSamplerConcrete>( + meterProperties, + power + )); + model.getMeteringService()->addMeterValueSampler(OCPP_ID_OF_CONNECTOR, std::move(mvs)); //connectorId=1 + model.getMeteringService()->setPowerSampler(OCPP_ID_OF_CONNECTOR, power); } void setEnergyActiveImportSampler(std::function energy) { @@ -181,7 +190,27 @@ void setEnergyActiveImportSampler(std::function energy) { model.setMeteringSerivce(std::unique_ptr( new MeteringService(*ocppEngine, OCPP_NUMCONNECTORS))); } - model.getMeteringService()->setEnergySampler(OCPP_ID_OF_CONNECTOR, energy); //connectorId=1 + SampledValueProperties meterProperties; + meterProperties.setMeasurand("Energy.Active.Import.Register"); + meterProperties.setUnit("Wh"); + auto mvs = std::unique_ptr>>( + new SampledValueSamplerConcrete>( + meterProperties, energy)); + model.getMeteringService()->addMeterValueSampler(OCPP_ID_OF_CONNECTOR, std::move(mvs)); //connectorId=1 + model.getMeteringService()->setEnergySampler(OCPP_ID_OF_CONNECTOR, energy); +} + +void addMeterValueSampler(std::unique_ptr meterValueSampler) { + if (!ocppEngine) { + AO_DBG_ERR("Please call OCPP_initialize before"); + return; + } + auto& model = ocppEngine->getOcppModel(); + if (!model.getMeteringService()) { + model.setMeteringSerivce(std::unique_ptr( + new MeteringService(*ocppEngine, OCPP_NUMCONNECTORS))); + } + model.getMeteringService()->addMeterValueSampler(OCPP_ID_OF_CONNECTOR, std::move(meterValueSampler)); //connectorId=1 } void setEvRequestsEnergySampler(std::function evRequestsEnergy) { diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index aba69669..65ca8ca7 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -13,6 +13,7 @@ #include #include #include +#include using ArduinoOcpp::OnReceiveConfListener; using ArduinoOcpp::OnReceiveReqListener; @@ -50,6 +51,8 @@ void setPowerActiveImportSampler(std::function power); void setEnergyActiveImportSampler(std::function energy); +void addMeterValueSampler(std::unique_ptr meterValueSampler); + void setEvRequestsEnergySampler(std::function evRequestsEnergy); void setConnectorEnergizedSampler(std::function connectorEnergized); diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp index 4f04992b..26c60593 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include using ArduinoOcpp::Ocpp16::MeterValues; @@ -14,14 +15,12 @@ MeterValues::MeterValues() { } -MeterValues::MeterValues(const std::vector *sampleTime, const std::vector *energy, const std::vector *power, int connectorId, int transactionId) +MeterValues::MeterValues(const std::vector>& meterValue, int connectorId, int transactionId) : connectorId{connectorId}, transactionId{transactionId} { - if (sampleTime) - this->sampleTime = std::vector(*sampleTime); - if (energy) - this->energy = std::vector(*energy); - if (power) - this->power = std::vector(*power); + + for (auto value = meterValue.begin(); value != meterValue.end(); value++) { + this->meterValue.push_back(std::unique_ptr(new MeterValue(**value))); + } } MeterValues::~MeterValues(){ @@ -34,49 +33,22 @@ const char* MeterValues::getOcppOperationType(){ std::unique_ptr MeterValues::createReq() { - int numEntries = sampleTime.size(); - - const size_t VALUE_MAXPRECISION = 10; - const size_t VALUE_MAXSIZE = VALUE_MAXPRECISION + 7; - char value_str [VALUE_MAXSIZE] = {'\0'}; - - auto doc = std::unique_ptr(new DynamicJsonDocument( - JSON_OBJECT_SIZE(3) //connectorID, transactionId, meterValue entry - + JSON_ARRAY_SIZE(numEntries) //metervalue array - + numEntries * JSON_OBJECT_SIZE(1) //sampledValue entry - + numEntries * (JSON_OBJECT_SIZE(1) + (JSONDATE_LENGTH + 1)) //timestamp - + numEntries * JSON_ARRAY_SIZE(2) //sampledValue - + 2 * numEntries * (JSON_OBJECT_SIZE(1) + VALUE_MAXSIZE) //value - + 2 * numEntries * JSON_OBJECT_SIZE(1) //measurand - + 2 * numEntries * JSON_OBJECT_SIZE(1) //unit - + 230)); //"safety space" - JsonObject payload = doc->to(); + size_t capacity = 0; - payload["connectorId"] = connectorId; - JsonArray meterValues = payload.createNestedArray("meterValue"); - for (size_t i = 0; i < sampleTime.size(); i++) { - JsonObject meterValue = meterValues.createNestedObject(); - char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; - OcppTimestamp otimestamp = sampleTime.at(i); - otimestamp.toJsonString(timestamp, JSONDATE_LENGTH + 1); - meterValue["timestamp"] = timestamp; - JsonArray sampledValue = meterValue.createNestedArray("sampledValue"); - if (energy.size() >= i + 1) { - JsonObject sampledValue_1 = sampledValue.createNestedObject(); - snprintf(value_str, VALUE_MAXSIZE, "%.*g", VALUE_MAXPRECISION, energy.at(i)); - sampledValue_1["value"] = value_str; - sampledValue_1["measurand"] = "Energy.Active.Import.Register"; - sampledValue_1["unit"] = "Wh"; - } - if (power.size() >= i + 1) { - JsonObject sampledValue_2 = sampledValue.createNestedObject(); - snprintf(value_str, VALUE_MAXSIZE, "%.*g", VALUE_MAXPRECISION, power.at(i)); - sampledValue_2["value"] = value_str; - sampledValue_2["measurand"] = "Power.Active.Import"; - sampledValue_2["unit"] = "W"; - } + std::vector> entries; + for (auto value = meterValue.begin(); value != meterValue.end(); value++) { + auto entry = (*value)->toJson(); + capacity += entry->capacity(); + entries.push_back(std::move(entry)); } + capacity += JSON_OBJECT_SIZE(3); + capacity += JSON_ARRAY_SIZE(entries.size()); + + auto doc = std::unique_ptr(new DynamicJsonDocument(capacity + 100)); //TODO remove safety space + auto payload = doc->to(); + payload["connectorId"] = connectorId; + if (ocppModel && ocppModel->getConnectorStatus(connectorId)) { auto connector = ocppModel->getConnectorStatus(connectorId); if (connector->getTransactionIdSync() >= 0) { @@ -84,6 +56,11 @@ std::unique_ptr MeterValues::createReq() { } } + auto meterValueJson = payload.createNestedArray("meterValue"); + for (auto entry = entries.begin(); entry != entries.end(); entry++) { + meterValueJson.add(**entry); + } + return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.h b/src/ArduinoOcpp/MessagesV16/MeterValues.h index 35160909..2e0bfcab 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.h +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.h @@ -7,22 +7,20 @@ #include #include +#include namespace ArduinoOcpp { namespace Ocpp16 { class MeterValues : public OcppMessage { private: - - std::vector sampleTime; - std::vector power; - std::vector energy; + std::vector> meterValue; int connectorId = 0; int transactionId = -1; public: - MeterValues(const std::vector *sampleTime, const std::vector *energy, const std::vector *power, int connectorId, int transactionId); + MeterValues(const std::vector>& meterValue, int connectorId, int transactionId); MeterValues(); //for debugging only. Make this for the server pendant diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp index 62e05590..c6941c51 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp @@ -28,7 +28,7 @@ const char* StartTransaction::getOcppOperationType(){ void StartTransaction::initiate() { if (ocppModel && ocppModel->getMeteringService()) { auto meteringService = ocppModel->getMeteringService(); - meterStart = (int) meteringService->readEnergyActiveImportRegister(connectorId); + meterStart = meteringService->readEnergyActiveImportRegister(connectorId); } if (ocppModel) { diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.h b/src/ArduinoOcpp/MessagesV16/StartTransaction.h index b756cdec..6390a0f1 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.h @@ -15,7 +15,7 @@ namespace Ocpp16 { class StartTransaction : public OcppMessage { private: int connectorId = 1; - int meterStart = -1; + int32_t meterStart = -1; OcppTimestamp otimestamp; char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; uint16_t transactionRev = 0; diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp index 094888c3..52f91627 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp @@ -22,7 +22,7 @@ void StopTransaction::initiate() { if (ocppModel && ocppModel->getMeteringService()) { auto meteringService = ocppModel->getMeteringService(); - meterStop = (int) meteringService->readEnergyActiveImportRegister(connectorId); + meterStop = meteringService->readEnergyActiveImportRegister(connectorId); } if (ocppModel) { diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.h b/src/ArduinoOcpp/MessagesV16/StopTransaction.h index a1b861bd..d662cf4d 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.h @@ -14,7 +14,7 @@ namespace Ocpp16 { class StopTransaction : public OcppMessage { private: int connectorId = 1; - int meterStop = -1; + int32_t meterStop = -1; OcppTimestamp otimestamp; public: diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index 659bd265..1d71648f 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -15,27 +15,27 @@ using namespace ArduinoOcpp::Ocpp16; ConnectorMeterValuesRecorder::ConnectorMeterValuesRecorder(OcppModel& context, int connectorId) : context(context), connectorId{connectorId} { - sampleTimestamp = std::vector(); - energy = std::vector(); - power = std::vector(); MeterValueSampleInterval = declareConfiguration("MeterValueSampleInterval", 60); MeterValuesSampledDataMaxLength = declareConfiguration("MeterValuesSampledDataMaxLength", 4, CONFIGURATION_VOLATILE, false, true, false, false); } void ConnectorMeterValuesRecorder::takeSample() { - if (energySampler != nullptr || powerSampler != nullptr) { - if (!context.getOcppTime().isValid()) return; - sampleTimestamp.push_back(context.getOcppTime().getOcppTimestampNow()); - } + if (meterValueSamplers.empty()) return; - if (energySampler != nullptr) { - energy.push_back(energySampler()); + std::unique_ptr sample; + if (context.getOcppTime().isValid()) { + sample.reset(new MeterValue(context.getOcppTime().getOcppTimestampNow())); + } + if (!sample) { + return; } - if (powerSampler != nullptr) { - power.push_back(powerSampler()); + for (auto mvs = meterValueSamplers.begin(); mvs != meterValueSamplers.end(); mvs++) { + sample->addSampledValue((*mvs)->takeValue()); } + + meterValue.push_back(std::move(sample)); } OcppMessage *ConnectorMeterValuesRecorder::loop() { @@ -73,7 +73,7 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { /* * Is the value buffer already full? If yes, return MeterValues message */ - if (((int) sampleTimestamp.size()) >= (int) *MeterValuesSampledDataMaxLength) { + if (((int) meterValue.size()) >= (int) *MeterValuesSampledDataMaxLength) { auto result = toMeterValues(); return result; } @@ -82,59 +82,35 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { } OcppMessage *ConnectorMeterValuesRecorder::toMeterValues() { - if (sampleTimestamp.size() == 0) { + if (meterValue.empty()) { AO_DBG_DEBUG("Checking if to send MeterValues ... No"); clear(); return nullptr; - } - - //decide which measurands to send. If a measurand is missing at at least one point in time, omit that measurand completely - - if (energy.size() == sampleTimestamp.size() && power.size() == sampleTimestamp.size()) { - auto result = new MeterValues(&sampleTimestamp, &energy, &power, connectorId, lastTransactionId); - clear(); - return result; - } - - if (energy.size() == sampleTimestamp.size() && power.size() != sampleTimestamp.size()) { - auto result = new MeterValues(&sampleTimestamp, &energy, nullptr, connectorId, lastTransactionId); - clear(); - return result; - } - - if (energy.size() != sampleTimestamp.size() && power.size() == sampleTimestamp.size()) { - auto result = new MeterValues(&sampleTimestamp, nullptr, &power, connectorId, lastTransactionId); + } else { + auto result = new MeterValues(meterValue, connectorId, lastTransactionId); clear(); return result; } - - //Maybe the energy sampler or power sampler was set during recording. Discard recorded data. - AO_DBG_WARN("Invalid data set. Discard data set and restart recording"); - clear(); - - return nullptr; } OcppMessage *ConnectorMeterValuesRecorder::takeMeterValuesNow() { - if (!energySampler && !powerSampler) { + if (meterValueSamplers.empty()) { return nullptr; } - decltype(sampleTimestamp) t_now; - decltype(energy) e_now; - decltype(power) p_now; + std::unique_ptr value; if (context.getOcppTime().isValid()) { - t_now.push_back(context.getOcppTime().getOcppTimestampNow()); + value.reset(new MeterValue(context.getOcppTime().getOcppTimestampNow())); } - if (energySampler) { - e_now.push_back(energySampler()); + if (!value) { + return nullptr; } - if (powerSampler) { - p_now.push_back(powerSampler()); + for (auto mvs = meterValueSamplers.begin(); mvs != meterValueSamplers.end(); mvs++) { + value->addSampledValue((*mvs)->takeValue()); } int txId_now = -1; @@ -143,24 +119,29 @@ OcppMessage *ConnectorMeterValuesRecorder::takeMeterValuesNow() { txId_now = connector->getTransactionId(); } - return new MeterValues(&t_now, &e_now, &p_now, connectorId, txId_now); + decltype(meterValue) mv_now; + mv_now.push_back(std::move(value)); + + return new MeterValues(mv_now, connectorId, txId_now); } void ConnectorMeterValuesRecorder::clear() { - sampleTimestamp.clear(); - energy.clear(); - power.clear(); + meterValue.clear(); } void ConnectorMeterValuesRecorder::setPowerSampler(PowerSampler ps){ - this->powerSampler = ps; + this->powerSampler = ps; } void ConnectorMeterValuesRecorder::setEnergySampler(EnergySampler es){ - this->energySampler = es; + this->energySampler = es; +} + +void ConnectorMeterValuesRecorder::addMeterValueSampler(std::unique_ptr meterValueSampler) { + meterValueSamplers.push_back(std::move(meterValueSampler)); } -float ConnectorMeterValuesRecorder::readEnergyActiveImportRegister() { +int32_t ConnectorMeterValuesRecorder::readEnergyActiveImportRegister() { if (energySampler != nullptr) { return energySampler(); } else { diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h index 9976067d..d65de801 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h @@ -12,6 +12,7 @@ #include #include +#include #include namespace ArduinoOcpp { @@ -29,18 +30,18 @@ class ConnectorMeterValuesRecorder { const int connectorId; - std::vector sampleTimestamp; - std::vector energy; - std::vector power; + std::vector> meterValue; + ulong lastSampleTime = 0; //0 means not charging right now float lastPower; int lastTransactionId = -1; - PowerSampler powerSampler = NULL; - EnergySampler energySampler = NULL; + PowerSampler powerSampler = nullptr; + EnergySampler energySampler = nullptr; + std::vector> meterValueSamplers; - std::shared_ptr> MeterValueSampleInterval = NULL; - std::shared_ptr> MeterValuesSampledDataMaxLength = NULL; + std::shared_ptr> MeterValueSampleInterval = nullptr; + std::shared_ptr> MeterValuesSampledDataMaxLength = nullptr; void takeSample(); OcppMessage *toMeterValues(); @@ -54,7 +55,9 @@ class ConnectorMeterValuesRecorder { void setEnergySampler(EnergySampler energySampler); - float readEnergyActiveImportRegister(); + void addMeterValueSampler(std::unique_ptr meterValueSampler); + + int32_t readEnergyActiveImportRegister(); OcppMessage *takeMeterValuesNow(); }; diff --git a/src/ArduinoOcpp/Tasks/Metering/MeterValue.h b/src/ArduinoOcpp/Tasks/Metering/MeterValue.h new file mode 100644 index 00000000..6945fa8c --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Metering/MeterValue.h @@ -0,0 +1,61 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef METERVALUE_H +#define METERVALUE_H + +#include +#include +#include +#include + +namespace ArduinoOcpp { + +class MeterValue { +private: + OcppTimestamp timestamp; + std::vector> sampledValue; +public: + MeterValue(OcppTimestamp timestamp) : timestamp(timestamp) { } + MeterValue(const MeterValue& other) { + timestamp = other.timestamp; + for (auto value = other.sampledValue.begin(); value != other.sampledValue.end(); value++) { + sampledValue.push_back(std::unique_ptr((*value)->clone())); + } + } + + void addSampledValue(std::unique_ptr sample) {sampledValue.push_back(std::move(sample));} + + std::unique_ptr toJson() { + size_t capacity = 0; + std::vector> entries; + for (auto sample = sampledValue.begin(); sample != sampledValue.end(); sample++) { + auto json = (*sample)->toJson(); + capacity += json->capacity(); + entries.push_back(std::move(json)); + } + + capacity += JSON_ARRAY_SIZE(entries.size()); + capacity += JSONDATE_LENGTH + 1; + capacity += JSON_OBJECT_SIZE(2); + + auto result = std::unique_ptr(new DynamicJsonDocument(capacity + 100)); //TODO remove safety space + auto jsonPayload = result->to(); + + char timestampStr [JSONDATE_LENGTH + 1] = {'\0'}; + if (!timestamp.toJsonString(timestampStr, JSONDATE_LENGTH + 1)) { + return nullptr; + } + jsonPayload["timestamp"] = timestampStr; + auto jsonMeterValue = jsonPayload.createNestedArray("sampledValue"); + for (auto entry = entries.begin(); entry != entries.end(); entry++) { + jsonMeterValue.add(**entry); + } + return std::move(result); + } +}; + +} + +#endif diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp index 6260a55c..6232aa34 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp @@ -45,7 +45,15 @@ void MeteringService::setEnergySampler(int connectorId, EnergySampler es){ connectors[connectorId]->setEnergySampler(es); } -float MeteringService::readEnergyActiveImportRegister(int connectorId) { +void MeteringService::addMeterValueSampler(int connectorId, std::unique_ptr meterValueSampler) { + if (connectorId < 0 || connectorId >= connectors.size()) { + AO_DBG_ERR("connectorId is out of bounds"); + return; + } + connectors[connectorId]->addMeterValueSampler(std::move(meterValueSampler)); +} + +int32_t MeteringService::readEnergyActiveImportRegister(int connectorId) { if (connectorId < 0 || connectorId >= connectors.size()) { AO_DBG_ERR("connectorId is out of bounds"); return 0.f; diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h index 035db5f6..526629fb 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h @@ -8,6 +8,7 @@ #include #include +#include namespace ArduinoOcpp { @@ -31,7 +32,9 @@ class MeteringService { void setEnergySampler(int connectorId, EnergySampler energySampler); - float readEnergyActiveImportRegister(int connectorId); + void addMeterValueSampler(int connectorId, std::unique_ptr meterValueSampler); + + int32_t readEnergyActiveImportRegister(int connectorId); std::unique_ptr takeMeterValuesNow(int connectorId); //snapshot of all meters now diff --git a/src/ArduinoOcpp/Tasks/Metering/SampledValue.h b/src/ArduinoOcpp/Tasks/Metering/SampledValue.h new file mode 100644 index 00000000..f2a3adb9 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Metering/SampledValue.h @@ -0,0 +1,143 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef SAMPLEDVALUE_H +#define SAMPLEDVALUE_H + +#include +#include + +namespace ArduinoOcpp { + +template +class SampledValueDeSerializer { +public: + static T deserialize(const char *str); + static std::string serialize(const T& val); + static int32_t toInteger(const T& val); +}; + +template <> +class SampledValueDeSerializer { +public: + static int32_t deserialize(const char *str) {return 42;} + static std::string serialize(const int32_t& val) { + char str [12] = {'\0'}; + snprintf(str, 12, "%d", val); + return std::string(str); + } + static int32_t toInteger(const int32_t& val) {return val;} +}; + +class SampledValueProperties { +private: + std::string format; + std::string measurand; + std::string phase; + std::string location; + std::string unit; + + const std::string& getFormat() const {return format;} + const std::string& getMeasurand() const {return measurand;} + const std::string& getPhase() const {return phase;} + const std::string& getLocation() const {return location;} + const std::string& getUnit() const {return unit;} + friend class SampledValue; //will be able to retreive these parameters + +public: + SampledValueProperties() { } + SampledValueProperties(const SampledValueProperties& other) : + format(other.format), + measurand(other.measurand), + phase(other.phase), + location(other.location), + unit(other.unit) { } + ~SampledValueProperties() = default; + + void setFormat(const char *format) {this->format = format;} + void setMeasurand(const char *measurand) {this->measurand = measurand;} + void setPhase(const char *phase) {this->phase = phase;} + void setLocation(const char *location) {this->location = location;} + void setUnit(const char *unit) {this->unit = unit;} +}; + +class SampledValue { +protected: + const SampledValueProperties& properties; + virtual std::string serializeValue() = 0; +public: + SampledValue(const SampledValueProperties& properties) : properties(properties) { } + SampledValue(const SampledValue& other) : properties(other.properties) { } + virtual ~SampledValue() = default; + + std::unique_ptr toJson() { + auto value = serializeValue(); + size_t capacity = 0; + capacity += JSON_OBJECT_SIZE(7); + capacity += value.length() + 1 + + properties.getFormat().length() + 1 + + properties.getMeasurand().length() + 1 + + properties.getPhase().length() + 1 + + properties.getLocation().length() + 1 + + properties.getUnit().length() + 1; + auto result = std::unique_ptr(new DynamicJsonDocument(capacity + 100)); //TODO remove safety space + auto payload = result->to(); + payload["value"] = value; + if (!properties.getFormat().empty()) + payload["format"] = properties.getFormat(); + if (!properties.getMeasurand().empty()) + payload["measurand"] = properties.getMeasurand(); + if (!properties.getPhase().empty()) + payload["phase"] = properties.getPhase(); + if (!properties.getLocation().empty()) + payload["location"] = properties.getLocation(); + if (!properties.getUnit().empty()) + payload["unit"] = properties.getUnit(); + return std::move(result); + } + + virtual std::unique_ptr clone() = 0; + + virtual int32_t toInteger() = 0; +}; + +template +class SampledValueConcrete : public SampledValue { +private: + const T value; +public: + SampledValueConcrete(const SampledValueProperties& properties, const T&& value) : SampledValue(properties), value(value) { } + SampledValueConcrete(const SampledValueConcrete& other) : SampledValue(other), value(other.value) { } + ~SampledValueConcrete() = default; + + std::string serializeValue() override {return DeSerializer::serialize(value);} + + std::unique_ptr clone() override {return std::unique_ptr>(new SampledValueConcrete(*this));} + + int32_t toInteger() override { return DeSerializer::toInteger(value);} +}; + +class SampledValueSampler { +protected: + SampledValueProperties properties; +public: + SampledValueSampler(SampledValueProperties properties) : properties(properties) { } + virtual ~SampledValueSampler() = default; + virtual std::unique_ptr takeValue() = 0; +}; + +template +class SampledValueSamplerConcrete : public SampledValueSampler { +private: + std::function sampler; +public: + SampledValueSamplerConcrete(SampledValueProperties properties, std::function sampler) : SampledValueSampler(properties), sampler(sampler) { } + std::unique_ptr takeValue() override { + return std::unique_ptr>(new SampledValueConcrete(properties, sampler())); + } +}; + +} + +#endif From 4a632d9de74a24010726b8f2c6d64348c4c09d69 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 23 Mar 2022 18:46:00 +0100 Subject: [PATCH 142/696] include STL headers explicitly --- platformio.ini | 2 +- src/ArduinoOcpp.h | 1 + src/ArduinoOcpp/Core/Configuration.h | 3 +++ src/ArduinoOcpp/Core/OcppEngine.h | 1 + src/ArduinoOcpp/Core/OcppOperationCallbacks.h | 1 + src/ArduinoOcpp/Core/OcppSocket.h | 1 + src/ArduinoOcpp/MessagesV16/GetConfiguration.h | 2 ++ src/ArduinoOcpp/MessagesV16/MeterValues.h | 2 ++ src/ArduinoOcpp/MessagesV16/TriggerMessage.h | 2 ++ src/ArduinoOcpp/SimpleOcppOperationFactory.h | 2 ++ .../Tasks/ChargePointStatus/ChargePointStatusService.cpp | 1 + .../Tasks/ChargePointStatus/ChargePointStatusService.h | 2 ++ src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h | 4 ++++ src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h | 1 + src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h | 1 + src/ArduinoOcpp/Tasks/Metering/MeteringService.h | 2 ++ src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h | 1 + 17 files changed, 28 insertions(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index b0f58e72..cb523e3d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -23,7 +23,7 @@ build_flags = -DAO_TRAFFIC_OUT ; print the OCPP communication to the serial monitor [env:esp32-development-board] -platform = espressif32@3.3.2 +platform = espressif32@3.5.0 board = esp-wrover-kit framework = ${common.framework} lib_deps = diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index aba69669..6fbaec92 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -7,6 +7,7 @@ #include #include +#include #include #include diff --git a/src/ArduinoOcpp/Core/Configuration.h b/src/ArduinoOcpp/Core/Configuration.h index da85327d..4097bc1a 100644 --- a/src/ArduinoOcpp/Core/Configuration.h +++ b/src/ArduinoOcpp/Core/Configuration.h @@ -9,6 +9,9 @@ #include #include +#include +#include + #define CONFIGURATION_FN "/arduino-ocpp.cnf" #define CONFIGURATION_VOLATILE "/volatile" diff --git a/src/ArduinoOcpp/Core/OcppEngine.h b/src/ArduinoOcpp/Core/OcppEngine.h index 70b8853c..aa01b613 100644 --- a/src/ArduinoOcpp/Core/OcppEngine.h +++ b/src/ArduinoOcpp/Core/OcppEngine.h @@ -7,6 +7,7 @@ #include #include +#include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/Core/OcppOperationCallbacks.h b/src/ArduinoOcpp/Core/OcppOperationCallbacks.h index a79bb513..957458a6 100644 --- a/src/ArduinoOcpp/Core/OcppOperationCallbacks.h +++ b/src/ArduinoOcpp/Core/OcppOperationCallbacks.h @@ -6,6 +6,7 @@ #define OCPP_OPERATION_CALLBACKS #include +#include #include diff --git a/src/ArduinoOcpp/Core/OcppSocket.h b/src/ArduinoOcpp/Core/OcppSocket.h index feda4ffb..f0c9c087 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.h +++ b/src/ArduinoOcpp/Core/OcppSocket.h @@ -6,6 +6,7 @@ #define OCPPSOCKET_H #include +#include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.h b/src/ArduinoOcpp/MessagesV16/GetConfiguration.h index 679b27b8..3cfc0127 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.h +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.h @@ -7,6 +7,8 @@ #include +#include + namespace ArduinoOcpp { namespace Ocpp16 { diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.h b/src/ArduinoOcpp/MessagesV16/MeterValues.h index 35160909..6549a3a6 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.h +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.h @@ -8,6 +8,8 @@ #include #include +#include + namespace ArduinoOcpp { namespace Ocpp16 { diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h index ef0bf316..eb6128aa 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.h +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.h @@ -7,6 +7,8 @@ #include +#include + namespace ArduinoOcpp { class OcppOperation; diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.h b/src/ArduinoOcpp/SimpleOcppOperationFactory.h index 3a09b5b8..c9ad0726 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.h +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.h @@ -7,6 +7,8 @@ #include #include +#include +#include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index 22aaa061..fd8a2c0e 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -9,6 +9,7 @@ #include +#include #include using namespace ArduinoOcpp; diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h index 737e5333..ce95f59e 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h @@ -7,6 +7,8 @@ #include +#include + namespace ArduinoOcpp { class OcppEngine; diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index 9df5614b..804a712e 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -9,6 +9,10 @@ #include #include +#include +#include +#include + #define AVAILABILITY_OPERATIVE 2 #define AVAILABILITY_INOPERATIVE_SCHEDULED 1 #define AVAILABILITY_INOPERATIVE 0 diff --git a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h index e6f8afe0..a684c620 100644 --- a/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h +++ b/src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.h @@ -6,6 +6,7 @@ #define HEARTBEATSERVICE_H #include +#include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h index 9976067d..e496d223 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h @@ -11,6 +11,7 @@ #include #include +#include #include diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h index 035db5f6..2bbb8ad7 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h @@ -6,6 +6,8 @@ #define METERINGSERVICE_H #include +#include +#include #include diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h index f97c4617..d909e6a7 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace ArduinoOcpp { From bc260a02cbfe513d0961ee7f1915a4c26222a204 Mon Sep 17 00:00:00 2001 From: Matthias Akstaller <63792403+matth-x@users.noreply.github.com> Date: Wed, 23 Mar 2022 18:50:09 +0100 Subject: [PATCH 143/696] Update README.md --- README.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/README.md b/README.md index 77a7c436..4abc7edf 100644 --- a/README.md +++ b/README.md @@ -125,18 +125,6 @@ setOnSetChargingProfileRequest([] (JsonObject payload) { You can also process the original payload from the CS using the `payload` object. -### Transaction and Session management - -In practice, there are a couple of prerequisites for OCPP-transactions. The library abstracts them into two conditions: -1) The electric connection between the EVSE and EV is established and safe -2) The user is identified, authorized and shows the intention to charge the vehicle - -Your hardware integration is fully responsible for Condition (1). To make the OCPP library aware about the safe connection, set a callback function with `setConnectorPluggedSampler(std::function connectorPlugged)`. Condition (2) is handled by both the hardware integration and the OCPP library. To make the library aware about Condition (2) use the functions `beginSession(const char *idTag)` and `endSession()`. Note that internally, the OCPP library uses the same functions if it receives a `RemoteStartTransaction` request, or stops a transaction for any reason. - -In return, the function `bool ocppPermitsCharge()` notifies your hardware implementation if the OCPP transaction is engaged and the EVSE can charge the EV ultimately. - -Alternatively, you can manage OCPP transactions by yourself using `startTransaction(const char *idTag)` and `stopTransaction()`. - *To get started quickly with or without EVSE hardware, you can flash the sketch in `examples/SECC` onto your ESP. That example mimics a full OCPP communications controller as it would look like in a real charging station. You can build a charger prototype based on that example or just view the internal state using the device monitor.* ## Dependencies From a8bb275b945d9f3f5c1bcb4d4c870594367f54be Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 28 Mar 2022 14:17:55 +0200 Subject: [PATCH 144/696] ESP8266 fix --- platformio.ini | 1 + src/ArduinoOcpp/Core/OcppOperationTimeout.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/platformio.ini b/platformio.ini index cb523e3d..a04c0917 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,6 +21,7 @@ monitor_speed = ${common.monitor_speed} build_flags = -D AO_DBG_LEVEL=AO_DL_INFO ; flood the serial monitor with information about the internal state -DAO_TRAFFIC_OUT ; print the OCPP communication to the serial monitor + -D ARDUINOJSON_ENABLE_STD_STRING=1 [env:esp32-development-board] platform = espressif32@3.5.0 diff --git a/src/ArduinoOcpp/Core/OcppOperationTimeout.h b/src/ArduinoOcpp/Core/OcppOperationTimeout.h index 0ab58503..25060537 100644 --- a/src/ArduinoOcpp/Core/OcppOperationTimeout.h +++ b/src/ArduinoOcpp/Core/OcppOperationTimeout.h @@ -7,6 +7,8 @@ #include +#include + namespace ArduinoOcpp { using OnTimeoutListener = std::function; From df0af455f5c8dc7cc59850ed14cc582c8922d487 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 28 Mar 2022 14:18:52 +0200 Subject: [PATCH 145/696] add TLS example --- examples/ESP-TLS/main.cpp | 143 +++++++++++++++++++++++++ library.json | 12 ++- src/ArduinoOcpp/Core/Configuration.cpp | 2 +- 3 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 examples/ESP-TLS/main.cpp diff --git a/examples/ESP-TLS/main.cpp b/examples/ESP-TLS/main.cpp new file mode 100644 index 00000000..5e9be62e --- /dev/null +++ b/examples/ESP-TLS/main.cpp @@ -0,0 +1,143 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#include +#if defined(ESP8266) +#include +#include +ESP8266WiFiMulti WiFiMulti; +#elif defined(ESP32) +#include +#else +#error only ESP32 or ESP8266 supported at the moment +#endif + +#include +#include //need for setting TLS credentials + +#define STASSID "YOUR_WIFI_SSID" +#define STAPSK "YOUR_WIFI_PW" + +#define OCPP_HOST "echo.websocket.events" +#define OCPP_PORT 443 +#define OCPP_URL "wss://echo.websocket.events/" + +/* + * OCPP Security Profile 2: TLS with Basic Authentication + * + * Example credentials from the OCPP-JSON document (p. 16) + */ +#define OCPP_AUTH_ID "AL1000" +#define OCPP_AUTH_KEY "0001020304050607FFFFFFFFFFFFFFFFFFFFFFFF" + +const char ENDPOINT_CA_CERT[] PROGMEM = R"EOF( +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +)EOF"; + +WebSocketsClient wsockSecure {}; +ArduinoOcpp::EspWiFi::OcppClientSocket osockSecure {&wsockSecure}; + +void setup() { + + /* + * Initialize Serial and WiFi + */ + + Serial.begin(115200); + + Serial.print(F("[main] Wait for WiFi ")); + +#if defined(ESP8266) + WiFiMulti.addAP(STASSID, STAPSK); + while (WiFiMulti.run() != WL_CONNECTED) { + Serial.print('.'); + delay(1000); + } +#elif defined(ESP32) + WiFi.begin(STASSID, STAPSK); + while (!WiFi.isConnected()) { + Serial.print('.'); + delay(1000); + } +#endif + + Serial.print(F(" connected\n")); + + /* + * Set system time (required for Certificate validation) + */ + configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov"); //alternatively: settimeofday(&de, &tz); + Serial.print(F("[main] Wait for NTP time (used for certificate validation) ")); + time_t now = time(nullptr); + while (now < 8 * 3600 * 2) { + delay(1000); + Serial.print('.'); + now = time(nullptr); + } + Serial.printf(" finished. Unix timestamp is %lu\n", now); + + /* + * Connect to OCPP Central System (using OCPP Security Profile 2: TLS with Basic Authentication ) + */ + wsockSecure.beginSslWithCA(OCPP_HOST, + OCPP_PORT, + OCPP_URL, + ENDPOINT_CA_CERT, "ocpp1.6"); + wsockSecure.setReconnectInterval(5000); + wsockSecure.enableHeartbeat(15000, 3000, 2); + wsockSecure.setAuthorization(OCPP_AUTH_ID, OCPP_AUTH_KEY); // => Authorization: Basic QUwxMDAwOgABAgMEBQYH//////////////// + OCPP_initialize(osockSecure); + + /* + * ... see ArduinoOcpp.h for how to integrate the EVSE hardware. + * + * This example only showcases the TLS connection. For examples about the HW integration, + * please see the other examples + */ + + /* + * Notify the Central System that this station is ready + */ + bootNotification("My Charging Station", "My company name"); +} + +void loop() { + + /* + * Execute all charge point routines and handle WebSocket + */ + OCPP_loop(); + + //... see ArduinoOcpp.h and the other examples for how to integrate the EVSE hardware. +} diff --git a/library.json b/library.json index 63c040ff..d31232fa 100644 --- a/library.json +++ b/library.json @@ -43,7 +43,9 @@ "include": [ "src/*", - "examples/*", + "examples/ESP/*", + "examples/ESP-TLS/*", + "examples/SECC/*", "platformio.ini", "library.json", "README.md", @@ -53,7 +55,6 @@ [ "src/sdkconfig*", "examples/SECC/WiFiManager*", - "examples/CompatibilityTest", "src/main.cpp" ] }, @@ -66,6 +67,13 @@ "main.cpp" ] }, + { + "name": "OCPP Security Profile 2", + "base": "examples/ESP-TLS", + "files": [ + "main.cpp" + ] + }, { "name": "EVSE Communications Controller", "base": "examples/SECC", diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index d933e264..21e27f0e 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -100,7 +100,7 @@ std::shared_ptr> declareConfiguration(const char *key, T defaul std::shared_ptr container = getContainer(filename); if (!container) { - AO_DBG_INFO("init new configurations container on flash filesystem: %s", filename); + AO_DBG_INFO("init new configurations container: %s", filename); container = createConfigurationContainer(filename); configurationContainers.push_back(container); From 3b302d74b34b78b9791cb42c59c1c037c52bef52 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 28 Mar 2022 15:32:39 +0200 Subject: [PATCH 146/696] update example, fix compiler warnings --- examples/ESP/main.cpp | 27 +++++++++++++------ src/ArduinoOcpp/MessagesV16/Reset.cpp | 4 +-- .../ChargePointStatusService.cpp | 4 +-- .../Tasks/Metering/MeteringService.cpp | 10 +++---- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/examples/ESP/main.cpp b/examples/ESP/main.cpp index ba74e531..b402082e 100644 --- a/examples/ESP/main.cpp +++ b/examples/ESP/main.cpp @@ -15,8 +15,8 @@ ESP8266WiFiMulti WiFiMulti; #include -#define STASSID "YOUR_WLAN_SSID" -#define STAPSK "YOUR_WLAN_PW" +#define STASSID "YOUR_WIFI_SSID" +#define STAPSK "YOUR_WIFI_PW" #define OCPP_HOST "echo.websocket.events" #define OCPP_PORT 80 @@ -110,19 +110,30 @@ void loop() { * Detect if something physical happened at your EVSE and trigger the corresponding OCPP messages */ if (/* RFID chip detected? */ false) { - const char *idTag = "my-id-tag"; //e.g. idTag = RFID.readIdTag(); - authorize(idTag); + const char *idTag = "0123456789abcd"; //e.g. idTag = RFID.readIdTag(); + authorize(idTag, [] (JsonObject response) { + //check if user with idTag is authorized + if (!strcmp("Accepted", response["idTagInfo"]["status"] | "Invalid")){ + Serial.println(F("[main] User is authorized to start charging")); + } else { + Serial.printf("[main] Authorize denied. Reason: %s\n", response["idTagInfo"]["status"] | ""); + } + }); + Serial.printf("[main] Authorizing user with idTag %s\n", idTag); } - if (/* EV plugged in? */ false) { - startTransaction("my-id-tag", [] (JsonObject payload) { + if (/* EV plugged in and user authorized? */ false) { + const char *idTag = "0123456789abcd"; //e.g. authorized idTag from above + startTransaction(idTag, [] (JsonObject response) { //Callback: Central System has answered. Could flash a confirmation light here. - Serial.print(F("[main] Started OCPP transaction\n")); + Serial.printf("[main] Started OCPP transaction. Status: %s, transactionId: %u\n", + response["idTagInfo"]["status"] | "Invalid", + response["transactionId"] | -1); }); } if (/* EV unplugged? */ false) { - stopTransaction([] (JsonObject payload) { + stopTransaction([] (JsonObject response) { //Callback: Central System has answered. Serial.print(F("[main] Stopped OCPP transaction\n")); }); diff --git a/src/ArduinoOcpp/MessagesV16/Reset.cpp b/src/ArduinoOcpp/MessagesV16/Reset.cpp index e51d4659..04de2f51 100644 --- a/src/ArduinoOcpp/MessagesV16/Reset.cpp +++ b/src/ArduinoOcpp/MessagesV16/Reset.cpp @@ -25,8 +25,8 @@ void Reset::processReq(JsonObject payload) { if (ocppModel && ocppModel->getChargePointStatusService()) { auto cpsService = ocppModel->getChargePointStatusService(); - unsigned int connId = 0; - for (unsigned int i = 0; i < cpsService->getNumConnectors(); i++) { + int connId = 0; + for (int i = 0; i < cpsService->getNumConnectors(); i++) { auto connector = cpsService->getConnector(connId); if (connector) { connector->endSession(); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index fd8a2c0e..80123922 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -17,7 +17,7 @@ using namespace ArduinoOcpp; ChargePointStatusService::ChargePointStatusService(OcppEngine& context, unsigned int numConn) : context(context) { - for (int i = 0; i < numConn; i++) { + for (unsigned int i = 0; i < numConn; i++) { connectors.push_back(std::unique_ptr(new ConnectorStatus(context.getOcppModel(), i))); } @@ -61,7 +61,7 @@ void ChargePointStatusService::loop() { } ConnectorStatus *ChargePointStatusService::getConnector(int connectorId) { - if (connectorId < 0 || connectorId >= connectors.size()) { + if (connectorId < 0 || connectorId >= (int) connectors.size()) { AO_DBG_ERR("connectorId is out of bounds"); return nullptr; } diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp index 6260a55c..75088b3a 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp @@ -19,7 +19,7 @@ MeteringService::MeteringService(OcppEngine& context, int numConn) void MeteringService::loop(){ - for (int i = 0; i < connectors.size(); i++){ + for (unsigned int i = 0; i < connectors.size(); i++){ auto meterValuesMsg = connectors[i]->loop(); if (meterValuesMsg != nullptr) { auto meterValues = makeOcppOperation(meterValuesMsg); @@ -30,7 +30,7 @@ void MeteringService::loop(){ } void MeteringService::setPowerSampler(int connectorId, PowerSampler ps){ - if (connectorId < 0 || connectorId >= connectors.size()) { + if (connectorId < 0 || connectorId >= (int) connectors.size()) { AO_DBG_ERR("connectorId is out of bounds"); return; } @@ -38,7 +38,7 @@ void MeteringService::setPowerSampler(int connectorId, PowerSampler ps){ } void MeteringService::setEnergySampler(int connectorId, EnergySampler es){ - if (connectorId < 0 || connectorId >= connectors.size()) { + if (connectorId < 0 || connectorId >= (int) connectors.size()) { AO_DBG_ERR("connectorId is out of bounds"); return; } @@ -46,7 +46,7 @@ void MeteringService::setEnergySampler(int connectorId, EnergySampler es){ } float MeteringService::readEnergyActiveImportRegister(int connectorId) { - if (connectorId < 0 || connectorId >= connectors.size()) { + if (connectorId < 0 || connectorId >= (int) connectors.size()) { AO_DBG_ERR("connectorId is out of bounds"); return 0.f; } @@ -54,7 +54,7 @@ float MeteringService::readEnergyActiveImportRegister(int connectorId) { } std::unique_ptr MeteringService::takeMeterValuesNow(int connectorId) { - if (connectorId < 0 || connectorId >= connectors.size()) { + if (connectorId < 0 || connectorId >= (int) connectors.size()) { AO_DBG_ERR("connectorId out of bounds. Ignore"); return nullptr; } From c57e6015a11a15aab2117ee9d6f6c798a87e7b77 Mon Sep 17 00:00:00 2001 From: Matthias Akstaller <63792403+matth-x@users.noreply.github.com> Date: Mon, 28 Mar 2022 19:23:08 +0200 Subject: [PATCH 147/696] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4abc7edf..5329de96 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ Full compatibility with the Arduino platform. Need a **FreeRTOS** version? Pleas You can build an OCPP Charge Point controller using the popular, Wi-Fi enabled microcontrollers ESP8266, ESP32 and comparable. This library allows your EVSE to communicate with an OCPP Central System and to participate in your Charging Network. -:heavy_check_mark: Works with [SteVe](https://github.com/RWTH-i5-IDSG/steve) +:heavy_check_mark: Works with [SteVe](https://github.com/RWTH-i5-IDSG/steve) and [The Mobility House OCPP package](https://github.com/mobilityhouse/ocpp) -:heavy_check_mark: Tested with two further (proprietary) central systems +:heavy_check_mark: Passed compatibility tests with further commercial Central Systems :heavy_check_mark: Integrated and tested in many charging stations @@ -23,7 +23,7 @@ You can build an OCPP Charge Point controller using the popular, Wi-Fi enabled m - handles the OCPP communication with the charging network - implements the standard OCPP charging process -- provides an API for the integration of the hardware modules of your PCB +- provides an API for the integration of the hardware modules of your EVSE - works with any TLS implementation and WebSocket library. E.g. - Arduino networking stack: [Links2004/arduinoWebSockets](https://github.com/Links2004/arduinoWebSockets), or - generic embedded systems: [Mongoose Networking Library](https://github.com/cesanta/mongoose) From 4bbf32de3fe2014ca74490e769ea5c22149ad45b Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 2 Apr 2022 12:12:11 +0200 Subject: [PATCH 148/696] add support for MinimumStatusDuration --- .../ChargePointStatus/ConnectorStatus.cpp | 18 +++++++++++++----- .../Tasks/ChargePointStatus/ConnectorStatus.h | 5 +++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 532aecd5..cf304af9 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -34,6 +34,8 @@ ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) availability = declareConfiguration(key, AVAILABILITY_OPERATIVE, CONFIGURATION_FN, false, false, true, false); connectionTimeOut = declareConfiguration("ConnectionTimeOut", 30, CONFIGURATION_FN, true, true, true, false); + minimumStatusDuration = declareConfiguration("MinimumStatusDuration", 0, CONFIGURATION_FN, true, true, true, false); + if (!sIdTag || !transactionId || !availability) { AO_DBG_ERR("Cannot declare sessionIdTag, transactionId or availability"); } @@ -160,12 +162,18 @@ OcppMessage *ConnectorStatus::loop() { if (inferencedStatus != currentStatus) { currentStatus = inferencedStatus; - AO_DBG_DEBUG("Status changed"); + t_statusTransition = ao_tick_ms(); + AO_DBG_DEBUG("Status changed%s", *minimumStatusDuration > 0 ? ", will report delayed", ""); + } + + if (reportedStatus != currentStatus && + (*minimumStatusDuration <= 0 || //MinimumStatusDuration disabled + ao_tick_ms() - t_statusTransition >= ((ulong) *minimumStatusDuration) * 1000UL)) { + reportedStatus = currentStatus; + OcppTimestamp reportedTimestamp = context.getOcppTime().getOcppTimestampNow(); + reportedTimestamp -= (ao_tick_ms() - t_statusTransition) / 1000UL; - //fire StatusNotification - //TODO check for online condition: Only inform CS about status change if CP is online - //TODO check for too short duration condition: Only inform CS about status change if it lasted for longer than MinimumStatusDuration - return new StatusNotification(connectorId, currentStatus, context.getOcppTime().getOcppTimestampNow(), getErrorCode()); + return new StatusNotification(connectorId, reportedStatus, reportedTimestamp, getErrorCode()); } return nullptr; diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index 804a712e..6a1879dc 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -45,7 +45,12 @@ class ConnectorStatus { std::function connectorEnergizedSampler {nullptr}; std::vector> connectorErrorCodeSamplers; const char *getErrorCode(); + OcppEvseState currentStatus = OcppEvseState::NOT_SET; + std::shared_ptr> minimumStatusDuration {nullptr}; //in seconds + OcppEvseState reportedStatus = OcppEvseState::NOT_SET; + ulong t_statusTransition = 0; + std::function onUnlockConnector {nullptr}; public: ConnectorStatus(OcppModel& context, int connectorId); From 9b0ca14d931d016cf945bdfdb3d98c850da93107 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 4 Apr 2022 11:42:39 +0200 Subject: [PATCH 149/696] initial commit --- CMakeLists.txt | 59 ++++++++++ README.md | 102 +----------------- src/ArduinoOcpp.cpp | 4 +- src/ArduinoOcpp/Core/Configuration.cpp | 9 +- .../Core/ConfigurationContainerFlash.cpp | 2 + .../Core/ConfigurationKeyValue.cpp | 8 -- src/ArduinoOcpp/Core/OcppConnection.cpp | 10 +- src/ArduinoOcpp/Core/OcppOperation.cpp | 14 +-- src/ArduinoOcpp/Core/OcppOperation.h | 14 +-- src/ArduinoOcpp/Core/OcppTime.cpp | 1 + .../MessagesV16/StatusNotification.cpp | 1 + src/ArduinoOcpp/Platform.h | 2 + .../SimpleOcppOperationFactory.cpp | 2 +- .../ChargePointStatus/ConnectorStatus.cpp | 2 +- .../SmartCharging/SmartChargingModel.cpp | 1 + .../SmartCharging/SmartChargingService.cpp | 4 +- src/ao_opts.h | 31 ++++++ src/ao_opts_impl.c | 29 +++++ 18 files changed, 154 insertions(+), 141 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 src/ao_opts.h create mode 100644 src/ao_opts_impl.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..be8c56fc --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,59 @@ +set(AO_SRC + src/ArduinoOcpp/Core/Configuration.cpp + src/ArduinoOcpp/Core/ConfigurationContainer.cpp + src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp + src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp + src/ArduinoOcpp/Core/OcppConnection.cpp + src/ArduinoOcpp/Core/OcppEngine.cpp + src/ArduinoOcpp/Core/OcppMessage.cpp + src/ArduinoOcpp/Core/OcppModel.cpp + src/ArduinoOcpp/Core/OcppOperation.cpp + src/ArduinoOcpp/Core/OcppOperationTimeout.cpp + src/ArduinoOcpp/Core/OcppServer.cpp + src/ArduinoOcpp/Core/OcppSocket.cpp + src/ArduinoOcpp/Core/OcppTime.cpp + src/ArduinoOcpp/MessagesV16/Authorize.cpp + src/ArduinoOcpp/MessagesV16/BootNotification.cpp + src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp + src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp + src/ArduinoOcpp/MessagesV16/ClearCache.cpp + src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp + src/ArduinoOcpp/MessagesV16/DataTransfer.cpp + src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp + src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp + src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp + src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp + src/ArduinoOcpp/MessagesV16/Heartbeat.cpp + src/ArduinoOcpp/MessagesV16/MeterValues.cpp + src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp + src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp + src/ArduinoOcpp/MessagesV16/Reset.cpp + src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp + src/ArduinoOcpp/MessagesV16/StartTransaction.cpp + src/ArduinoOcpp/MessagesV16/StatusNotification.cpp + src/ArduinoOcpp/MessagesV16/StopTransaction.cpp + src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp + src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp + src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp + src/ArduinoOcpp/Platform.cpp + src/ArduinoOcpp/SimpleOcppOperationFactory.cpp + src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp + src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp + src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp + src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp + src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp + src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp + src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp + src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp + src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp + src/ArduinoOcpp.cpp + src/ao_opts_impl.c +) + +idf_component_register(SRCS ${AO_SRC} + INCLUDE_DIRS "./src") + +target_compile_options(${COMPONENT_TARGET} PUBLIC + -DAO_CUSTOM_WS + -DAO_CUSTOM_CONSOLE + -DAO_DEACTIVATE_FLASH) diff --git a/README.md b/README.md index 5329de96..35e19a87 100644 --- a/README.md +++ b/README.md @@ -32,107 +32,7 @@ For simple chargers, the necessary hardware and internet integration is usually ## Usage guide -Please take `examples/ESP/main.cpp` as the starting point for your first project. It is a minimal example which shows how to establish an OCPP connection and how to start and stop charging sessions. In this guide, I give a brief overview of the key concepts. - -- To get the library running, you have to install all dependencies (see the list below). - - - In case you use PlatformIO, you can just add `matth-x/ArduinoOcpp` to your project using the PIO library manager. - -- In your project's `main` file, include `ArduinoOcpp.h`. This gives you a simple access to all functions. - -- Before establishing an OCPP connection you have to ensure that your device has access to a Wi-Fi access point. All debug messages are printed on the standard serial (i.e. `Serial.print("debug msg")`). To redirect debug messages, please refer to `src/ArduinoOcpp/Platform.h`. - -- To connect to your OCPP Central System, call `OCPP_initialize(String OCPP_HOST, uint16_t OCPP_PORT, String OCPP_URL)`. You need to insert the address parameters according to the configuration of your central system. Internally, the library passes these parameters to the WebSocket object without further alteration. - - To secure the connection with TLS, you have to configure the WebSocket. Please take `examples/SECC/main.cpp` as an example. - -- In your `setup()` function, you can add the configuration functions from `ArduinoOcpp.h` to properly integrate your hardware. All configuration functions are documented in `ArduinoOcpp.h`. For example, to integrate the energy meter of your EVSE, add - -```cpp -setEnergyActiveImportSampler([]() { - return yourEVSE_readEnergyMeter(); -}); -``` - -- Add `OCPP_loop()` to your `loop()` function. - -### Sending OCPP operations - -There are a couple of OCPP operations you can initialize on your EVSE. For example, to send a `Boot Notification`, use the function -```cpp -void bootNotification(const char *chargePointModel, const char *chargePointVendor, OnReceiveConfListener onConf = nullptr, ...)` -``` - -In practice, it looks like this: - -```cpp -void setup() { - - ... //other code including the initialization of Wi-Fi and OCPP - - bootNotification("My CP model name", "My company name", [] (JsonObject confMsg) { - //This callback is executed when the .conf() response from the central system arrives - Serial.print(F("BootNotification was answered. Central System clock: ")); - Serial.println(confMsg["currentTime"].as()); //"currentTime" is a field of the central system response - - //Notify your hardare that the BootNotification.conf() has arrived. E.g.: - //evseIsBooted = true; - }); - - ... //rest of setup() function; executed immediately as bootNotification() is non-blocking -} -``` - -The parameters `chargePointModel` and `chargePointVendor` are equivalent to the parameters in the `Boot Notification` as defined by the OCPP specification. The last parameter `OnReceiveConfListener onConf` is a callback function which the library executes when the central system has processed the operation and the ESP has received the `.conf()` response. Here you can add your device-specific behavior, e.g. flash a confirmation LED or unlock the connectors. If you don't need it, the last parameter is optional. - -For your first EVSE integration, the `onReceiveConfListener` is probably sufficient. For advanced EVSE projects, the other listeners likely become relevant: - -- `onAbortListener`: will be called whenever the engine stops trying to finish an operation normally which was initiated by this device. - -- `onTimeoutListener`: will be executed when the operation is not answered until the timeout expires. Note that timeouts also trigger the `onAbortListener`. - -- `onReceiveErrorListener`: will be called when the Central System returns a CallError. Again, each error also triggers the `onAbortListener`. - -The following example shows the correct usage of all listeners. - -```cpp -authorize(idTag, [](JsonObject conf) { - //onReceiveConfListener (optional but very likely necessary for your integration) - successfullyAuthorized = true; //example client code - ... //further client code, e.g. beginSession(idTag); -}, []() { - //onAbortListener (optional) - Serial.print(F("[EVSE] Could not authorize charging session. Aborted\n")); //flash error light etc. -}, []() { - //onTimeoutListener (optional) - Serial.print(F("[EVSE] Could not authorize charging session. Reason: timeout\n")); -}, [](const char *code, const char *description, JsonObject details) { - //onReceiveErrorListener (optional) - Serial.print(F("[EVSE] Could not authorize charging session. Reason: received OCPP error: ")); - Serial.println(code); -}); - -``` - -### Receiving OCPP operations - -The library also reacts on CS-initiated operations. You can add your own behavior there too. For example, to flash a LED on receipt of a `Set Charging Profile` request, use the following function. - -```cpp -setOnSetChargingProfileRequest([] (JsonObject payload) { - //... -}); -``` - -You can also process the original payload from the CS using the `payload` object. - -*To get started quickly with or without EVSE hardware, you can flash the sketch in `examples/SECC` onto your ESP. That example mimics a full OCPP communications controller as it would look like in a real charging station. You can build a charger prototype based on that example or just view the internal state using the device monitor.* - -## Dependencies - -- [bblanchon/ArduinoJSON](https://github.com/bblanchon/ArduinoJson) (please upgrade to version `6.19.1`) -- [Links2004/arduinoWebSockets](https://github.com/Links2004/arduinoWebSockets) (please upgrade to version `2.3.6`) - -In case you use PlatformIO, you can copy all dependencies from `platformio.ini` into your own configuration file. Alternatively, you can install the full library with dependencies by adding `matth-x/ArduinoOcpp` in the PIO library manager. +**This feature branch is WIP. A usage guide will follow.** ## Supported operations diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 60f8b841..e90985cb 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -37,7 +37,7 @@ float voltage_eff {230.f}; #define OCPP_NUMCONNECTORS 2 #define OCPP_ID_OF_CONNECTOR 1 #define OCPP_ID_OF_CP 0 -boolean OCPP_booted = false; //if BootNotification succeeded +bool OCPP_booted = false; //if BootNotification succeeded } //end namespace ArduinoOcpp::Facade } //end namespace ArduinoOcpp @@ -137,7 +137,7 @@ void OCPP_deinitialize() { void OCPP_loop() { if (!ocppEngine) { AO_DBG_WARN("Please call OCPP_initialize before"); - delay(200); //Prevent this message from flooding the Serial monitor. + //delay(200); //Prevent this message from flooding the Serial monitor. return; } diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index 21e27f0e..cbd75a0a 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -7,16 +7,9 @@ #include #include +#include #include -#if defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) -#include -#define USE_FS LITTLEFS -#else -#include -#define USE_FS SPIFFS -#endif - namespace ArduinoOcpp { FilesystemOpt configurationFilesystemOpt = FilesystemOpt::Use_Mount_FormatOnFail; diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp index d10adbf3..4d0a260d 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp @@ -11,6 +11,7 @@ #define USE_FS SPIFFS #endif +#ifndef AO_DEACTIVATE_FLASH #if USE_FS == LITTLEFS #include #elif USE_FS == SPIFFS @@ -18,6 +19,7 @@ #else #error "FS not supported" #endif +#endif #define MAX_FILE_SIZE 4000 #define MAX_CONFIGURATIONS 50 diff --git a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp index 0cee24e3..f25abddb 100644 --- a/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp @@ -9,14 +9,6 @@ #include #include -#if defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) -#include -#define USE_FS LITTLEFS -#else -#include -#define USE_FS SPIFFS -#endif - #define KEY_MAXLEN 60 #define STRING_VAL_MAXLEN 2000 //allow TLS certificates in ... diff --git a/src/ArduinoOcpp/Core/OcppConnection.cpp b/src/ArduinoOcpp/Core/OcppConnection.cpp index 204a8288..15c10ce3 100644 --- a/src/ArduinoOcpp/Core/OcppConnection.cpp +++ b/src/ArduinoOcpp/Core/OcppConnection.cpp @@ -35,7 +35,7 @@ void OcppConnection::loop(OcppSocket& ocppSock) { auto operation = initiatedOcppOperations.begin(); while (operation != initiatedOcppOperations.end()){ - boolean timeout = (*operation)->sendReq(ocppSock); //The only reason to dequeue elements here is when a timeout occurs. Normally + bool timeout = (*operation)->sendReq(ocppSock); //The only reason to dequeue elements here is when a timeout occurs. Normally if (timeout){ //the Conf msg processing routine dequeues finished elements operation = initiatedOcppOperations.erase(operation); } else { @@ -76,7 +76,7 @@ void OcppConnection::loop(OcppSocket& ocppSock) { operation = receivedOcppOperations.begin(); while (operation != receivedOcppOperations.end()){ - boolean success = (*operation)->sendConf(ocppSock); + bool success = (*operation)->sendConf(ocppSock); if (success){ operation = receivedOcppOperations.erase(operation); } else { @@ -102,7 +102,7 @@ void OcppConnection::initiateOcppOperation(std::unique_ptr o){ bool OcppConnection::processOcppSocketInputTXT(const char* payload, size_t length) { - boolean deserializationSuccess = false; + bool deserializationSuccess = false; auto doc = std::unique_ptr{nullptr}; size_t capacity = length + 100; @@ -187,7 +187,7 @@ bool OcppConnection::processOcppSocketInputTXT(const char* payload, size_t lengt */ void OcppConnection::handleConfMessage(JsonDocument& json) { for (auto operation = initiatedOcppOperations.begin(); operation != initiatedOcppOperations.end(); ++operation) { - boolean success = (*operation)->receiveConf(json); //maybe rename to "consumed"? + bool success = (*operation)->receiveConf(json); //maybe rename to "consumed"? if (success) { initiatedOcppOperations.erase(operation); return; @@ -219,7 +219,7 @@ void OcppConnection::handleReqMessage(JsonDocument& json, std::unique_ptrreceiveError(json); //maybe rename to "consumed"? + bool discardOperation = (*operation)->receiveError(json); //maybe rename to "consumed"? if (discardOperation) { initiatedOcppOperations.erase(operation); return; diff --git a/src/ArduinoOcpp/Core/OcppOperation.cpp b/src/ArduinoOcpp/Core/OcppOperation.cpp index 52a9f7e2..035ac8c5 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.cpp +++ b/src/ArduinoOcpp/Core/OcppOperation.cpp @@ -73,7 +73,7 @@ const std::string *OcppOperation::getMessageID() { return &messageID; } -boolean OcppOperation::sendReq(OcppSocket& ocppSocket){ +bool OcppOperation::sendReq(OcppSocket& ocppSocket){ /* * timeout behaviour @@ -151,7 +151,7 @@ boolean OcppOperation::sendReq(OcppSocket& ocppSocket){ return false; } -boolean OcppOperation::receiveConf(JsonDocument& confJson){ +bool OcppOperation::receiveConf(JsonDocument& confJson){ /* * check if messageIDs match. If yes, continue with this function. If not, return false for message not consumed */ @@ -176,7 +176,7 @@ boolean OcppOperation::receiveConf(JsonDocument& confJson){ return true; } -boolean OcppOperation::receiveError(JsonDocument& confJson){ +bool OcppOperation::receiveError(JsonDocument& confJson){ /* * check if messageIDs match. If yes, continue with this function. If not, return false for message not consumed */ @@ -205,7 +205,7 @@ boolean OcppOperation::receiveError(JsonDocument& confJson){ return abortOperation; } -boolean OcppOperation::receiveReq(JsonDocument& reqJson){ +bool OcppOperation::receiveReq(JsonDocument& reqJson){ std::string reqId = reqJson[1]; setMessageID(reqId); @@ -229,7 +229,7 @@ boolean OcppOperation::receiveReq(JsonDocument& reqJson){ return true; //true because everything was successful. If there will be an error check in future, this value becomes more reasonable } -boolean OcppOperation::sendConf(OcppSocket& ocppSocket){ +bool OcppOperation::sendConf(OcppSocket& ocppSocket){ if (!reqExecuted) { //wait until req has been executed @@ -290,7 +290,7 @@ boolean OcppOperation::sendConf(OcppSocket& ocppSocket){ */ std::string out {}; serializeJson(*confJson, out); - boolean wsSuccess = ocppSocket.sendTXT(out); + bool wsSuccess = ocppSocket.sendTXT(out); if (wsSuccess) { if (operationSuccess) { @@ -346,7 +346,7 @@ void OcppOperation::setOnAbortListener(OnAbortListener onAbort) { onAbortListener = onAbort; } -boolean OcppOperation::isFullyConfigured(){ +bool OcppOperation::isFullyConfigured(){ return ocppMessage != nullptr; } diff --git a/src/ArduinoOcpp/Core/OcppOperation.h b/src/ArduinoOcpp/Core/OcppOperation.h index fd93e96a..4ca7b5ad 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.h +++ b/src/ArduinoOcpp/Core/OcppOperation.h @@ -31,7 +31,7 @@ class OcppOperation { OnTimeoutListener onTimeoutListener = [] () {}; OnReceiveErrorListener onReceiveErrorListener = [] (const char *code, const char *description, JsonObject details) {}; OnAbortListener onAbortListener = [] () {}; - boolean reqExecuted = false; + bool reqExecuted = false; std::unique_ptr timeout{new OfflineSensitiveTimeout(40000)}; @@ -67,7 +67,7 @@ class OcppOperation { * the operation is completed (for example when conf() has been called), return true. When the operation is still pending, return * false. */ - boolean sendReq(OcppSocket& ocppSocket); + bool sendReq(OcppSocket& ocppSocket); /** * Decides if message belongs to this operation instance and if yes, proccesses it. For example, multiple instances of an @@ -75,28 +75,28 @@ class OcppOperation { * * Returns true if JSON object has been consumed, false otherwise. */ - boolean receiveConf(JsonDocument& json); + bool receiveConf(JsonDocument& json); /** * Decides if message belongs to this operation instance and if yes, notifies the OcppMessage object about the CallError. * * Returns true if JSON object has been consumed, false otherwise. */ - boolean receiveError(JsonDocument& json); + bool receiveError(JsonDocument& json); /** * Processes the request in the JSON document. Returns true on success, false on error. * * Returns false if the request doesn't belong to the corresponding operation instance */ - boolean receiveReq(JsonDocument& json); + bool receiveReq(JsonDocument& json); /** * After processing a request sent by the communication counterpart, this function sends a confirmation * message. Returns true on success, false otherwise. Returns also true if a CallError has successfully * been sent */ - boolean sendConf(OcppSocket& ocppSocket); + bool sendConf(OcppSocket& ocppSocket); void setInitiated(); @@ -126,7 +126,7 @@ class OcppOperation { */ void setOnAbortListener(OnAbortListener onAbort); - boolean isFullyConfigured(); + bool isFullyConfigured(); void print_debug(); }; diff --git a/src/ArduinoOcpp/Core/OcppTime.cpp b/src/ArduinoOcpp/Core/OcppTime.cpp index 3c58e4ba..d44dc4f8 100644 --- a/src/ArduinoOcpp/Core/OcppTime.cpp +++ b/src/ArduinoOcpp/Core/OcppTime.cpp @@ -4,6 +4,7 @@ #include #include +#include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp index 72cdff6a..8776e60e 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp @@ -36,6 +36,7 @@ const char *cstrFromOcppEveState(OcppEvseState state) { return "Faulted"; default: AO_DBG_ERR("OcppEvseState not specified"); + __attribute__ ((fallthrough)); case (OcppEvseState::NOT_SET): return "NOT_SET"; } diff --git a/src/ArduinoOcpp/Platform.h b/src/ArduinoOcpp/Platform.h index 25f9f322..66cb0d97 100644 --- a/src/ArduinoOcpp/Platform.h +++ b/src/ArduinoOcpp/Platform.h @@ -5,6 +5,8 @@ #ifndef AO_PLATFORM_H #define AO_PLATFORM_H +#include + #ifdef AO_CUSTOM_CONSOLE #ifndef AO_CUSTOM_CONSOLE_MAXMSGSIZE diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index d4deeb38..1b1ca459 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -31,7 +31,7 @@ #include #include - +#include #include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 532aecd5..05504484 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -38,7 +38,7 @@ ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) AO_DBG_ERR("Cannot declare sessionIdTag, transactionId or availability"); } if (sIdTag->getBuffsize() > 0 && (*sIdTag)[0] != '\0') { - snprintf(idTag, min((size_t) (IDTAG_LEN_MAX + 1), sIdTag->getBuffsize()), "%s", ((const char *) *sIdTag)); + snprintf(idTag, std::min((size_t) (IDTAG_LEN_MAX + 1), sIdTag->getBuffsize()), "%s", ((const char *) *sIdTag)); session = true; connectionTimeOutTimestamp = ao_tick_ms(); connectionTimeOutListen = true; diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp index fd34dd7c..8a326cfe 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp @@ -6,6 +6,7 @@ #include #include +#include using namespace ArduinoOcpp; diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index b4cd67ce..8b28483e 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -9,13 +9,15 @@ #include #include -#if defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) +#ifndef AO_DEACTIVATE_FLASH +#if defined(ESP32) #include #define USE_FS LITTLEFS #else #include #define USE_FS SPIFFS #endif +#endif #define SINGLE_CONNECTOR_ID 1 diff --git a/src/ao_opts.h b/src/ao_opts.h new file mode 100644 index 00000000..a95f1cad --- /dev/null +++ b/src/ao_opts.h @@ -0,0 +1,31 @@ +#ifndef AOOPTS_H +#define AOOPTS_H + +#ifdef __cplusplus +extern "C" { +#endif + +long ao_tick_ms_impl(); +//unsigned int32_t ao_avail_heap_impl(); + +#ifdef __cplusplus +} +#endif + +#ifndef ao_tick_ms +#define ao_tick_ms ao_tick_ms_impl +#endif + +#ifndef ao_avail_heap +#define ao_avail_heap() 20000 +#endif + +//#ifndef AO_CONSOLE_PRINTF +//#define AO_CONSOLE_PRINTF(...) ESP_LOGI("[ocpp]", __VA_ARGS__) +//#endif + +#ifndef AO_CUSTOM_CONSOLE_MAXMSGSIZE +#define AO_CUSTOM_CONSOLE_MAXMSGSIZE 500 +#endif + +#endif diff --git a/src/ao_opts_impl.c b/src/ao_opts_impl.c new file mode 100644 index 00000000..61c0dae8 --- /dev/null +++ b/src/ao_opts_impl.c @@ -0,0 +1,29 @@ +#include "ao_opts.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +long ao_tick_ms_impl() { + return xTaskGetTickCount() / configTICK_RATE_HZ; +} + +#ifdef _cplusplus +} +#endif From 1480c97de7fd86d3cbd77d0999323a3bbdb8ca80 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 4 Apr 2022 12:18:58 +0200 Subject: [PATCH 150/696] update documentation --- README.md | 51 +++++++---------------------------------------- src/ArduinoOcpp.h | 9 +++++++++ 2 files changed, 16 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 5329de96..e485854a 100644 --- a/README.md +++ b/README.md @@ -32,30 +32,22 @@ For simple chargers, the necessary hardware and internet integration is usually ## Usage guide -Please take `examples/ESP/main.cpp` as the starting point for your first project. It is a minimal example which shows how to establish an OCPP connection and how to start and stop charging sessions. In this guide, I give a brief overview of the key concepts. +Please take `examples/ESP/main.cpp` as the starting point for your first project. It is a minimal example which shows how to establish an OCPP connection and how to start and stop charging sessions. This guide explains the concepts for a minimal integration. -- To get the library running, you have to install all dependencies (see the list below). +- To get the library running, you have to install all dependencies (see the list below). In case you use PlatformIO, you can just add `matth-x/ArduinoOcpp` to your project using the PIO library manager. - - In case you use PlatformIO, you can just add `matth-x/ArduinoOcpp` to your project using the PIO library manager. - -- In your project's `main` file, include `ArduinoOcpp.h`. This gives you a simple access to all functions. +- In your project's `main` file, include `ArduinoOcpp.h`. - Before establishing an OCPP connection you have to ensure that your device has access to a Wi-Fi access point. All debug messages are printed on the standard serial (i.e. `Serial.print("debug msg")`). To redirect debug messages, please refer to `src/ArduinoOcpp/Platform.h`. - To connect to your OCPP Central System, call `OCPP_initialize(String OCPP_HOST, uint16_t OCPP_PORT, String OCPP_URL)`. You need to insert the address parameters according to the configuration of your central system. Internally, the library passes these parameters to the WebSocket object without further alteration. - - To secure the connection with TLS, you have to configure the WebSocket. Please take `examples/SECC/main.cpp` as an example. + - To secure the connection with TLS, you have to configure the WebSocket. Please take `examples/ESP-TLS/main.cpp` as an example. -- In your `setup()` function, you can add the configuration functions from `ArduinoOcpp.h` to properly integrate your hardware. All configuration functions are documented in `ArduinoOcpp.h`. For example, to integrate the energy meter of your EVSE, add - -```cpp -setEnergyActiveImportSampler([]() { - return yourEVSE_readEnergyMeter(); -}); -``` +- In your `setup()` function, you can add the configuration functions from `ArduinoOcpp.h` to properly integrate your hardware. For example, the library needs access to the energy meter. All configuration functions are documented in `ArduinoOcpp.h`. - Add `OCPP_loop()` to your `loop()` function. -### Sending OCPP operations +**Sending OCPP operations** There are a couple of OCPP operations you can initialize on your EVSE. For example, to send a `Boot Notification`, use the function ```cpp @@ -84,36 +76,7 @@ void setup() { The parameters `chargePointModel` and `chargePointVendor` are equivalent to the parameters in the `Boot Notification` as defined by the OCPP specification. The last parameter `OnReceiveConfListener onConf` is a callback function which the library executes when the central system has processed the operation and the ESP has received the `.conf()` response. Here you can add your device-specific behavior, e.g. flash a confirmation LED or unlock the connectors. If you don't need it, the last parameter is optional. -For your first EVSE integration, the `onReceiveConfListener` is probably sufficient. For advanced EVSE projects, the other listeners likely become relevant: - -- `onAbortListener`: will be called whenever the engine stops trying to finish an operation normally which was initiated by this device. - -- `onTimeoutListener`: will be executed when the operation is not answered until the timeout expires. Note that timeouts also trigger the `onAbortListener`. - -- `onReceiveErrorListener`: will be called when the Central System returns a CallError. Again, each error also triggers the `onAbortListener`. - -The following example shows the correct usage of all listeners. - -```cpp -authorize(idTag, [](JsonObject conf) { - //onReceiveConfListener (optional but very likely necessary for your integration) - successfullyAuthorized = true; //example client code - ... //further client code, e.g. beginSession(idTag); -}, []() { - //onAbortListener (optional) - Serial.print(F("[EVSE] Could not authorize charging session. Aborted\n")); //flash error light etc. -}, []() { - //onTimeoutListener (optional) - Serial.print(F("[EVSE] Could not authorize charging session. Reason: timeout\n")); -}, [](const char *code, const char *description, JsonObject details) { - //onReceiveErrorListener (optional) - Serial.print(F("[EVSE] Could not authorize charging session. Reason: received OCPP error: ")); - Serial.println(code); -}); - -``` - -### Receiving OCPP operations +**Receiving OCPP operations** The library also reacts on CS-initiated operations. You can add your own behavior there too. For example, to flash a LED on receipt of a `Set Charging Profile` request, use the following function. diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 6fbaec92..bdf5dbec 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -106,6 +106,15 @@ void setOnResetReceiveReq(OnReceiveReqListener onReceiveReq); //alternative: sta * "OnReceiveConfListener onConf" and passes the OCPP payload to it. The following functions * are non-blocking. Your application code will immediately resume with the subsequent code * in any case. + * + * For your first EVSE integration, the `onReceiveConfListener` is probably sufficient. For + * advanced EVSE projects, the other listeners likely become relevant: + * - `onAbortListener`: will be called whenever the engine stops trying to finish an operation + * normally which was initiated by this device. + * - `onTimeoutListener`: will be executed when the operation is not answered until the timeout + * expires. Note that timeouts also trigger the `onAbortListener`. + * - `onReceiveErrorListener`: will be called when the Central System returns a CallError. + * Again, each error also triggers the `onAbortListener`. */ void authorize(const char *idTag, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); From b36ff888e5693fa41e46fdbea57d504abba9acdd Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 5 Apr 2022 17:30:14 +0200 Subject: [PATCH 151/696] support StopTxOnEvUnplug, -InvalidId, field reason --- src/ArduinoOcpp/MessagesV16/CiStrings.h | 4 ++ .../MessagesV16/RemoteStopTransaction.cpp | 2 +- src/ArduinoOcpp/MessagesV16/Reset.cpp | 4 +- .../MessagesV16/StartTransaction.cpp | 15 +++--- .../MessagesV16/StopTransaction.cpp | 12 +++-- src/ArduinoOcpp/MessagesV16/StopTransaction.h | 4 +- .../MessagesV16/UnlockConnector.cpp | 4 +- .../ChargePointStatus/ConnectorStatus.cpp | 52 +++++++++++++++---- .../Tasks/ChargePointStatus/ConnectorStatus.h | 22 +++++--- 9 files changed, 87 insertions(+), 32 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/CiStrings.h b/src/ArduinoOcpp/MessagesV16/CiStrings.h index a6a13209..2d84cdd7 100644 --- a/src/ArduinoOcpp/MessagesV16/CiStrings.h +++ b/src/ArduinoOcpp/MessagesV16/CiStrings.h @@ -15,7 +15,11 @@ #define CiString255TypeLen 255 #define CiString500TypeLen 500 +//specified by OCPP #define IDTAG_LEN_MAX CiString20TypeLen #define CONF_KEYLEN_MAX CiString50TypeLen +//not specified by OCPP +#define REASON_LEN_MAX CiString25TypeLen + #endif diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp index 761b486d..461398b8 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp @@ -32,7 +32,7 @@ std::unique_ptr RemoteStopTransaction::createConf(){ auto connIter = cpStatusService->getConnector(i); if (connIter->getTransactionId() == transactionId) { canStopTransaction = true; - connIter->endSession(); + connIter->endSession("Remote"); } } } diff --git a/src/ArduinoOcpp/MessagesV16/Reset.cpp b/src/ArduinoOcpp/MessagesV16/Reset.cpp index 04de2f51..9e751ca6 100644 --- a/src/ArduinoOcpp/MessagesV16/Reset.cpp +++ b/src/ArduinoOcpp/MessagesV16/Reset.cpp @@ -21,7 +21,7 @@ void Reset::processReq(JsonObject payload) { * Process the application data here. Note: you have to implement the device reset procedure in your client code. You have to set * a onSendConfListener in which you initiate a reset (e.g. calling ESP.reset() ) */ - //const char *type = payload["type"] | "Invalid"; + bool isHard = !strcmp(payload["type"] | "undefined", "Hard"); if (ocppModel && ocppModel->getChargePointStatusService()) { auto cpsService = ocppModel->getChargePointStatusService(); @@ -29,7 +29,7 @@ void Reset::processReq(JsonObject payload) { for (int i = 0; i < cpsService->getNumConnectors(); i++) { auto connector = cpsService->getConnector(connId); if (connector) { - connector->endSession(); + connector->endSession(isHard ? "HardReset" : "SoftReset"); } } } diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp index 62e05590..d05c908f 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp @@ -21,7 +21,7 @@ StartTransaction::StartTransaction(int connectorId, const char *idTag) : connect AO_DBG_ERR("Format violation"); } -const char* StartTransaction::getOcppOperationType(){ +const char* StartTransaction::getOcppOperationType() { return "StartTransaction"; } @@ -95,17 +95,18 @@ void StartTransaction::processConf(JsonObject payload) { if (ocppModel) connector = ocppModel->getConnectorStatus(connectorId); - if (connector){ + if (connector) { if (transactionRev == connector->getTransactionWriteCount()) { - + if (!strcmp(idTagInfoStatus, "Accepted")) { AO_DBG_INFO("Request has been accepted"); - connector->setTransactionId(transactionId); } else { AO_DBG_INFO("Request has been denied. Reason: %s", idTagInfoStatus); - //connector->setTransactionId(-1); - connector->endSession(); //something is wrong with the idTag. Abort session + AO_DBG_DEBUG("Set txId despite rejection"); + connector->setIdTagInvalidated(); } + + connector->setTransactionId(transactionId); } connector->setTransactionIdSync(transactionId); @@ -123,7 +124,7 @@ void StartTransaction::processReq(JsonObject payload) { } -std::unique_ptr StartTransaction::createConf(){ +std::unique_ptr StartTransaction::createConf() { auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(2))); JsonObject payload = doc->to(); diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp index 094888c3..17af6b60 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp @@ -10,8 +10,10 @@ using ArduinoOcpp::Ocpp16::StopTransaction; -StopTransaction::StopTransaction(int connectorId) : connectorId(connectorId) { - +StopTransaction::StopTransaction(int connectorId, const char *reason) : connectorId(connectorId) { + if (reason) { + snprintf(this->reason, REASON_LEN_MAX, "%s", reason); + } } const char* StopTransaction::getOcppOperationType(){ @@ -44,7 +46,7 @@ void StopTransaction::initiate() { } std::unique_ptr StopTransaction::createReq() { - auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + (JSONDATE_LENGTH + 1))); + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(5) + (JSONDATE_LENGTH + 1) + (REASON_LEN_MAX + 1))); JsonObject payload = doc->to(); if (meterStop >= 0) @@ -61,6 +63,10 @@ std::unique_ptr StopTransaction::createReq() { payload["transactionId"] = connector->getTransactionIdSync(); } + if (reason[0] != '\0') { + payload["reason"] = reason; + } + return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.h b/src/ArduinoOcpp/MessagesV16/StopTransaction.h index a1b861bd..58dbf9b3 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.h @@ -7,6 +7,7 @@ #include #include +#include namespace ArduinoOcpp { namespace Ocpp16 { @@ -16,9 +17,10 @@ class StopTransaction : public OcppMessage { int connectorId = 1; int meterStop = -1; OcppTimestamp otimestamp; + char reason [REASON_LEN_MAX] {'\0'}; public: - StopTransaction(int connectorId); + StopTransaction(int connectorId, const char *reason = nullptr); const char* getOcppOperationType(); diff --git a/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp b/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp index 9b464087..a7418c44 100644 --- a/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp +++ b/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp @@ -28,6 +28,8 @@ void UnlockConnector::processReq(JsonObject payload) { auto connector = ocppModel->getConnectorStatus(connectorId); + connector->endSession("UnlockCommand"); + std::function unlockConnector = connector->getOnUnlockConnector(); if (unlockConnector != nullptr) { cbDefined = true; @@ -38,8 +40,6 @@ void UnlockConnector::processReq(JsonObject payload) { } cbUnlockSuccessful = unlockConnector(); - - //success } std::unique_ptr UnlockConnector::createConf(){ diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index cf304af9..6d27b36d 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -35,6 +35,8 @@ ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) connectionTimeOut = declareConfiguration("ConnectionTimeOut", 30, CONFIGURATION_FN, true, true, true, false); minimumStatusDuration = declareConfiguration("MinimumStatusDuration", 0, CONFIGURATION_FN, true, true, true, false); + stopTransactionOnInvalidId = declareConfiguration("StopTransactionOnInvalidId", "true", CONFIGURATION_FN, true, true, false, false); + stopTransactionOnEVSideDisconnect = declareConfiguration("StopTransactionOnEVSideDisconnect", "true", CONFIGURATION_FN, true, true, false, false); if (!sIdTag || !transactionId || !availability) { AO_DBG_ERR("Cannot declare sessionIdTag, transactionId or availability"); @@ -89,12 +91,13 @@ OcppEvseState ConnectorStatus::inferenceStatus() { return OcppEvseState::Preparing; } else { //Transaction is currently running + if ((connectorEnergizedSampler && !connectorEnergizedSampler()) || + idTagInvalidated) { + return OcppEvseState::SuspendedEVSE; + } if (evRequestsEnergySampler && !evRequestsEnergySampler()) { return OcppEvseState::SuspendedEV; } - if (connectorEnergizedSampler && !connectorEnergizedSampler()) { - return OcppEvseState::SuspendedEVSE; - } return OcppEvseState::Charging; } } @@ -105,6 +108,10 @@ bool ConnectorStatus::ocppPermitsCharge() { return false; } + if (idTagInvalidated) { + return false; + } + OcppEvseState state = inferenceStatus(); return state == OcppEvseState::Charging || @@ -117,6 +124,14 @@ OcppMessage *ConnectorStatus::loop() { *availability = AVAILABILITY_INOPERATIVE; saveState(); } + + if (connectorPluggedSampler) { + if (getTransactionId() >= 0 && !connectorPluggedSampler()) { + if (!*stopTransactionOnEVSideDisconnect || strcmp(*stopTransactionOnEVSideDisconnect, "false")) { + endSession("EVDisconnected"); + } + } + } /* * Check conditions for start or stop transaction @@ -124,12 +139,11 @@ OcppMessage *ConnectorStatus::loop() { if (connectorPluggedSampler) { //only supported with connectorPluggedSampler if (getTransactionId() >= 0) { //check condition for StopTransaction - if (!connectorPluggedSampler() || - !session) { + if (!session) { AO_DBG_DEBUG("Session mngt: txId=%i, connectorPlugged=%d, session=%d", getTransactionId(), connectorPluggedSampler(), session); AO_DBG_INFO("Session mngt: trigger StopTransaction"); - return new StopTransaction(connectorId); + return new StopTransaction(connectorId, endReason[0] != '\0' ? endReason : nullptr); } } else { //check condition for StartTransaction @@ -163,7 +177,7 @@ OcppMessage *ConnectorStatus::loop() { if (inferencedStatus != currentStatus) { currentStatus = inferencedStatus; t_statusTransition = ao_tick_ms(); - AO_DBG_DEBUG("Status changed%s", *minimumStatusDuration > 0 ? ", will report delayed", ""); + AO_DBG_DEBUG("Status changed%s", *minimumStatusDuration ? ", will report delayed" : ""); } if (reportedStatus != currentStatus && @@ -200,13 +214,18 @@ void ConnectorStatus::beginSession(const char *sessionIdTag) { sIdTag->setValue(idTag, IDTAG_LEN_MAX + 1); saveState(); session = true; + idTagInvalidated = false; + + memset(endReason, '\0', REASON_LEN_MAX + 1); connectionTimeOutListen = true; connectionTimeOutTimestamp = ao_tick_ms(); } -void ConnectorStatus::endSession() { - AO_DBG_DEBUG("End session with idTag %s", idTag); +void ConnectorStatus::endSession(const char *reason) { + AO_DBG_DEBUG("End session with idTag %s for reason %s, %s previous reason", + idTag, reason ? reason : "undefined", + endReason[0] == '\0' ? "no" : "overruled by"); if (session) { memset(idTag, '\0', IDTAG_LEN_MAX + 1); *sIdTag = ""; @@ -214,9 +233,24 @@ void ConnectorStatus::endSession() { } session = false; + if (reason && endReason[0] == '\0') { + snprintf(endReason, REASON_LEN_MAX + 1, "%s", reason); + } + connectionTimeOutListen = false; } +void ConnectorStatus::setIdTagInvalidated() { + if (session) { + idTagInvalidated = true; + if (!*stopTransactionOnInvalidId || strcmp(*stopTransactionOnInvalidId, "false")) { + endSession("DeAuthorized"); + } + } else { + AO_DBG_WARN("Cannot invalidate IdTag outside of session"); + } +} + const char *ConnectorStatus::getSessionIdTag() { return session ? idTag : nullptr; } diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index 6a1879dc..fb9d3ff0 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -28,15 +28,17 @@ class ConnectorStatus { const int connectorId; - std::shared_ptr> availability {nullptr}; + std::shared_ptr> availability; bool session = false; char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; - std::shared_ptr> sIdTag {nullptr}; - std::shared_ptr> transactionId {nullptr}; + bool idTagInvalidated {false}; //if StartTransaction.conf() has status != "Accepted" + std::shared_ptr> sIdTag; + std::shared_ptr> transactionId; int transactionIdSync = -1; + char endReason [REASON_LEN_MAX + 1] = {'\0'}; - std::shared_ptr> connectionTimeOut {nullptr}; //in seconds + std::shared_ptr> connectionTimeOut; //in seconds bool connectionTimeOutListen {false}; ulong connectionTimeOutTimestamp {0}; //in milliseconds @@ -47,11 +49,17 @@ class ConnectorStatus { const char *getErrorCode(); OcppEvseState currentStatus = OcppEvseState::NOT_SET; - std::shared_ptr> minimumStatusDuration {nullptr}; //in seconds + std::shared_ptr> minimumStatusDuration; //in seconds OcppEvseState reportedStatus = OcppEvseState::NOT_SET; ulong t_statusTransition = 0; + //std::function()> startTransactionBehavior; + //std::function(const char* stopReason)> stopTransactionBehavior; + std::function onUnlockConnector {nullptr}; + + std::shared_ptr> stopTransactionOnInvalidId; + std::shared_ptr> stopTransactionOnEVSideDisconnect; public: ConnectorStatus(OcppModel& context, int connectorId); @@ -66,7 +74,8 @@ class ConnectorStatus { * (given by ConnectorPluggedSampler and no error code) */ void beginSession(const char *idTag); - void endSession(); + void endSession(const char *reason = nullptr); + void setIdTagInvalidated(); //if StartTransaction.conf() has status != "Accepted" const char *getSessionIdTag(); int getTransactionId(); int getTransactionIdSync(); @@ -74,7 +83,6 @@ class ConnectorStatus { void setTransactionId(int id); void setTransactionIdSync(int id); - int getAvailability(); void setAvailability(bool available); void setAuthorizationProvider(std::function authorization); From 38bdce6d91ee1060100be84c6cea53bb4acaa166 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 8 Apr 2022 17:09:31 +0200 Subject: [PATCH 152/696] begin with c facade --- CMakeLists.txt | 3 ++- README.md | 4 ++++ src/ArduinoOcpp_c.cpp | 22 ++++++++++++++++++++++ src/ArduinoOcpp_c.h | 18 ++++++++++++++++++ src/ao_opts_impl.c | 2 +- 5 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 src/ArduinoOcpp_c.cpp create mode 100644 src/ArduinoOcpp_c.h diff --git a/CMakeLists.txt b/CMakeLists.txt index be8c56fc..176999e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,11 +47,12 @@ set(AO_SRC src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp src/ArduinoOcpp.cpp + src/ArduinoOcpp_c.cpp src/ao_opts_impl.c ) idf_component_register(SRCS ${AO_SRC} - INCLUDE_DIRS "./src") + INCLUDE_DIRS "./src" "${PROJECT_DIR}/include") target_compile_options(${COMPONENT_TARGET} PUBLIC -DAO_CUSTOM_WS diff --git a/README.md b/README.md index 35e19a87..facb1c3c 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,10 @@ For simple chargers, the necessary hardware and internet integration is usually **This feature branch is WIP. A usage guide will follow.** +## Dependencies + +- [bblanchon/ArduinoJson](https://github.com/bblanchon/ArduinoJson) + ## Supported operations | Operation name | supported | in progress | not supported | diff --git a/src/ArduinoOcpp_c.cpp b/src/ArduinoOcpp_c.cpp new file mode 100644 index 00000000..7400cc89 --- /dev/null +++ b/src/ArduinoOcpp_c.cpp @@ -0,0 +1,22 @@ +#include "ArduinoOcpp_c.h" +#include "ArduinoOcpp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void ao_initialize() { + //OCPP_initialize("echo.websocket.events", 80, "ws://echo.websocket.events/"); +} + +void ao_loop() { + OCPP_loop(); +} + +void ao_bootNotification() { + bootNotification("model", "vendor"); +} + +#ifdef __cplusplus +} +#endif diff --git a/src/ArduinoOcpp_c.h b/src/ArduinoOcpp_c.h new file mode 100644 index 00000000..54eedd24 --- /dev/null +++ b/src/ArduinoOcpp_c.h @@ -0,0 +1,18 @@ +#ifndef ARDUINOOCPP_C_H +#define ARDUINOOCPP_C_H + +#ifdef __cplusplus +extern "C" { +#endif + +void ao_initialize(); + +void ao_loop(); + +void ao_bootNotification(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/ao_opts_impl.c b/src/ao_opts_impl.c index 61c0dae8..41839036 100644 --- a/src/ao_opts_impl.c +++ b/src/ao_opts_impl.c @@ -24,6 +24,6 @@ long ao_tick_ms_impl() { return xTaskGetTickCount() / configTICK_RATE_HZ; } -#ifdef _cplusplus +#ifdef __cplusplus } #endif From 0f1f23616dc4d631b9b634febfe68b8e67478db3 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sun, 10 Apr 2022 18:04:24 +0200 Subject: [PATCH 153/696] add ocpp socket type and callbacks in c adapter --- src/ArduinoOcpp/Platform.h | 2 +- .../SimpleOcppOperationFactory.cpp | 6 --- src/ArduinoOcpp/SimpleOcppOperationFactory.h | 1 - src/ArduinoOcpp_c.cpp | 37 +++++++++++++++---- src/ArduinoOcpp_c.h | 16 +++++++- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/ArduinoOcpp/Platform.h b/src/ArduinoOcpp/Platform.h index 66cb0d97..319239d1 100644 --- a/src/ArduinoOcpp/Platform.h +++ b/src/ArduinoOcpp/Platform.h @@ -23,7 +23,7 @@ void ao_console_out(const char *msg); char msg [AO_CUSTOM_CONSOLE_MAXMSGSIZE]; \ snprintf(msg, AO_CUSTOM_CONSOLE_MAXMSGSIZE, X, ##__VA_ARGS__); \ sprintf(msg + AO_CUSTOM_CONSOLE_MAXMSGSIZE - 7, " [...]"); \ - ao_console_out(msg); \ + ArduinoOcpp::ao_console_out(msg); \ } while (0) #else #define ao_set_console_out(X) \ diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 1b1ca459..590818e5 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -69,12 +69,6 @@ void setOnBootNotificationRequestListener(OnReceiveReqListener listener){ deinit_afterwards(onBootNotificationRequest); } -OnReceiveReqListener onTargetValuesRequest; -void setOnTargetValuesRequestListener(OnReceiveReqListener listener) { - onTargetValuesRequest = listener; - deinit_afterwards(onTargetValuesRequest); -} - OnReceiveReqListener onSetChargingProfileRequest; void setOnSetChargingProfileRequestListener(OnReceiveReqListener listener){ onSetChargingProfileRequest = listener; diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.h b/src/ArduinoOcpp/SimpleOcppOperationFactory.h index c9ad0726..875b90ad 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.h +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.h @@ -26,7 +26,6 @@ void registerCustomOcppMessage(const char *messageType, OcppMessageCreator ocppM void setOnAuthorizeRequestListener(OnReceiveReqListener onReceiveReq); void setOnBootNotificationRequestListener(OnReceiveReqListener onReceiveReq); -void setOnTargetValuesRequestListener(OnReceiveReqListener onReceiveReq); void setOnSetChargingProfileRequestListener(OnReceiveReqListener onReceiveReq); void setOnStartTransactionRequestListener(OnReceiveReqListener onReceiveReq); void setOnTriggerMessageRequestListener(OnReceiveReqListener onReceiveReq); diff --git a/src/ArduinoOcpp_c.cpp b/src/ArduinoOcpp_c.cpp index 7400cc89..11f9969d 100644 --- a/src/ArduinoOcpp_c.cpp +++ b/src/ArduinoOcpp_c.cpp @@ -1,22 +1,43 @@ #include "ArduinoOcpp_c.h" #include "ArduinoOcpp.h" -#ifdef __cplusplus -extern "C" { -#endif +#include + +ArduinoOcpp::OcppSocket *ocppSocket = nullptr; -void ao_initialize() { +extern "C" void ao_initialize(AO_OcppSocket *osock) { //OCPP_initialize("echo.websocket.events", 80, "ws://echo.websocket.events/"); + if (!osock) { + AO_DBG_ERR("osock is null"); + } + + ocppSocket = reinterpret_cast(osock); + + OCPP_initialize(*ocppSocket); } -void ao_loop() { +extern "C" void ao_loop() { OCPP_loop(); } -void ao_bootNotification() { +extern "C" void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation) { bootNotification("model", "vendor"); } -#ifdef __cplusplus -} +#ifndef AO_RECEIVE_PAYLOAD_BUFSIZE +#define AO_RECEIVE_PAYLOAD_BUFSIZE 1024 #endif + +char ao_recv_payload_buff [AO_RECEIVE_PAYLOAD_BUFSIZE] = {'\0'}; + +extern "C" void ao_onResetRequest(OnOcppMessage onRequest) { + OnReceiveReqListener cb = [onRequest] (JsonObject payload) { + auto len = serializeJson(payload, ao_recv_payload_buff, AO_RECEIVE_PAYLOAD_BUFSIZE); + if (len <= 0) { + AO_DBG_WARN("Received payload buffer exceeded. Continue without payload"); + } + onRequest(len > 0 ? ao_recv_payload_buff : nullptr, len); + }; + setOnResetReceiveReq(cb); +} + diff --git a/src/ArduinoOcpp_c.h b/src/ArduinoOcpp_c.h index 54eedd24..5faf07a9 100644 --- a/src/ArduinoOcpp_c.h +++ b/src/ArduinoOcpp_c.h @@ -1,15 +1,27 @@ #ifndef ARDUINOOCPP_C_H #define ARDUINOOCPP_C_H +#include + +struct AO_OcppSocket; +typedef struct AO_OcppSocket AO_OcppSocket; + +typedef void (*OnOcppMessage) (const char *payload, size_t len); +typedef void (*OnOcppAbort) (); +typedef void (*OnOcppTimeout) (); +typedef void (*OnOcppError) (const char *code, const char *description, const char *details_json, size_t details_len); + #ifdef __cplusplus extern "C" { #endif -void ao_initialize(); +void ao_initialize(AO_OcppSocket *osock); void ao_loop(); -void ao_bootNotification(); +void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation); + +void ao_onResetRequest(OnOcppMessage onRequest); #ifdef __cplusplus } From 7d451a2dc9c58b61fec3e494e352b0597bee8ac3 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 18 Apr 2022 09:45:10 +0200 Subject: [PATCH 154/696] added functions --- src/ArduinoOcpp_c.cpp | 4 ++-- src/ArduinoOcpp_c.h | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/ArduinoOcpp_c.cpp b/src/ArduinoOcpp_c.cpp index 11f9969d..ad0f0701 100644 --- a/src/ArduinoOcpp_c.cpp +++ b/src/ArduinoOcpp_c.cpp @@ -5,7 +5,7 @@ ArduinoOcpp::OcppSocket *ocppSocket = nullptr; -extern "C" void ao_initialize(AO_OcppSocket *osock) { +extern "C" void ao_initialize(AOcppSocket *osock) { //OCPP_initialize("echo.websocket.events", 80, "ws://echo.websocket.events/"); if (!osock) { AO_DBG_ERR("osock is null"); @@ -20,7 +20,7 @@ extern "C" void ao_loop() { OCPP_loop(); } -extern "C" void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation) { +extern "C" void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { bootNotification("model", "vendor"); } diff --git a/src/ArduinoOcpp_c.h b/src/ArduinoOcpp_c.h index 5faf07a9..7efb3878 100644 --- a/src/ArduinoOcpp_c.h +++ b/src/ArduinoOcpp_c.h @@ -3,8 +3,8 @@ #include -struct AO_OcppSocket; -typedef struct AO_OcppSocket AO_OcppSocket; +struct AOcppSocket; +typedef struct AOcppSocket AOcppSocket; typedef void (*OnOcppMessage) (const char *payload, size_t len); typedef void (*OnOcppAbort) (); @@ -15,11 +15,17 @@ typedef void (*OnOcppError) (const char *code, const char *description, const extern "C" { #endif -void ao_initialize(AO_OcppSocket *osock); +void ao_initialize(AOcppSocket *osock); void ao_loop(); -void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation); +void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); + +void ao_authorize(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); + +void ao_startTransaction(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); + +void ao_stopTransaction(OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); void ao_onResetRequest(OnOcppMessage onRequest); From 4af1e7908764304a06daeac2b8b2c6cebb852673 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 19 Apr 2022 14:59:14 +0200 Subject: [PATCH 155/696] All function declarations for c-facade --- src/ArduinoOcpp_c.h | 73 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/src/ArduinoOcpp_c.h b/src/ArduinoOcpp_c.h index 7efb3878..fb74c201 100644 --- a/src/ArduinoOcpp_c.h +++ b/src/ArduinoOcpp_c.h @@ -11,14 +11,65 @@ typedef void (*OnOcppAbort) (); typedef void (*OnOcppTimeout) (); typedef void (*OnOcppError) (const char *code, const char *description, const char *details_json, size_t details_len); +typedef float (*SamplerFloat)(); +typedef int (*SamplerInt)(); +typedef bool (*SamplerBool)(); +typedef const char* (*SamplerString)(); + #ifdef __cplusplus extern "C" { #endif void ao_initialize(AOcppSocket *osock); +void ao_deinitialize(); + void ao_loop(); +/* + * Feed lib with HW related data + */ + +void ao_setPowerActiveImportSampler(SamplerFloat power); + +void ao_setEnergyActiveImportSampler(SamplerFloat energy); + +void ao_setEvRequestsEnergySampler(SamplerBool evRequestsEnergy); + +void ao_setConnectorEnergizedSampler(SamplerBool connectorEnergized); + +void ao_setConnectorPluggedSampler(SamplerBool connectorPlugged); + +//void setConnectorFaultedSampler(SamplerBool connectorFailed); + +void ao_addConnectorErrorCodeSampler(SamplerString connectorErrorCode); + +/* + * Execute HW related operations on EVSE + */ + +void ao_onChargingRateLimitChange(void (*chargingRateChanged)(float)); + +void ao_onUnlockConnector(SamplerBool unlockConnector); //true: success, false: failure + +/* + * Generic listeners for OCPP operations initiated by Central System + */ + +void ao_onSetChargingProfileRequest(OnOcppMessage onRequest); //optional + +void ao_onRemoteStartTransactionSendConf(OnOcppMessage onSendConf); //important, energize the power plug here and capture the idTag + +void ao_onRemoteStopTransactionSendConf(OnOcppMessage onSendConf); //important, de-energize the power plug here +void ao_onRemoteStopTransactionRequest(OnOcppMessage onRequest); //optional, to de-energize the power plug immediately + +void ao_onResetSendConf(OnOcppMessage onSendConf); //important, reset your device here (i.e. call ESP.reset();) +void ao_onResetRequest(OnOcppMessage onRequest); //alternative: start reset timer here + +/* + * Initiate OCPP operations + */ + void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); void ao_authorize(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); @@ -27,7 +78,27 @@ void ao_startTransaction(const char *idTag, OnOcppMessage onConfirmation, OnOcpp void ao_stopTransaction(OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); -void ao_onResetRequest(OnOcppMessage onRequest); +/* + * Access OCPP state + */ + +int ao_getTransactionId(); //returns the ID of the current transaction. Returns -1 if called before or after an transaction + +bool ao_ocppPermitsCharge(); + +bool ao_isAvailable(); //if the charge point is operative or inoperative + +/* + * Charging session management + */ + +void ao_beginSession(const char *idTag); + +void ao_endSession(); + +bool ao_isInSession(); + +const char *ao_getSessionIdTag(); #ifdef __cplusplus } From 5bd8b4c9f328e9beddbc669807f651865a8f2598 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 29 Apr 2022 17:56:05 +0200 Subject: [PATCH 156/696] debug out fixes --- CMakeLists.txt | 3 ++- src/ArduinoOcpp/Platform.h | 7 ++++--- src/ArduinoOcpp_c.cpp | 4 ++++ src/ArduinoOcpp_c.h | 2 ++ src/ao_opts.h | 2 +- src/ao_opts_impl.c | 3 ++- 6 files changed, 15 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 176999e2..210f3d11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,4 +57,5 @@ idf_component_register(SRCS ${AO_SRC} target_compile_options(${COMPONENT_TARGET} PUBLIC -DAO_CUSTOM_WS -DAO_CUSTOM_CONSOLE - -DAO_DEACTIVATE_FLASH) + -DAO_DEACTIVATE_FLASH + -DAO_DBG_LEVEL=AO_DL_DEBUG) diff --git a/src/ArduinoOcpp/Platform.h b/src/ArduinoOcpp/Platform.h index 319239d1..f6dfb757 100644 --- a/src/ArduinoOcpp/Platform.h +++ b/src/ArduinoOcpp/Platform.h @@ -10,7 +10,7 @@ #ifdef AO_CUSTOM_CONSOLE #ifndef AO_CUSTOM_CONSOLE_MAXMSGSIZE -#define AO_CUSTOM_CONSOLE_MAXMSGSIZE 128 +#define AO_CUSTOM_CONSOLE_MAXMSGSIZE 196 #endif void ao_set_console_out(void (*console_out)(const char *msg)); @@ -21,8 +21,9 @@ void ao_console_out(const char *msg); #define AO_CONSOLE_PRINTF(X, ...) \ do { \ char msg [AO_CUSTOM_CONSOLE_MAXMSGSIZE]; \ - snprintf(msg, AO_CUSTOM_CONSOLE_MAXMSGSIZE, X, ##__VA_ARGS__); \ - sprintf(msg + AO_CUSTOM_CONSOLE_MAXMSGSIZE - 7, " [...]"); \ + if (snprintf(msg, AO_CUSTOM_CONSOLE_MAXMSGSIZE, X, ##__VA_ARGS__) < 0) { \ + sprintf(msg + AO_CUSTOM_CONSOLE_MAXMSGSIZE - 7, " [...]"); \ + } \ ArduinoOcpp::ao_console_out(msg); \ } while (0) #else diff --git a/src/ArduinoOcpp_c.cpp b/src/ArduinoOcpp_c.cpp index ad0f0701..4511f1ac 100644 --- a/src/ArduinoOcpp_c.cpp +++ b/src/ArduinoOcpp_c.cpp @@ -20,6 +20,10 @@ extern "C" void ao_loop() { OCPP_loop(); } +extern "C" void ao_set_console_out_c(void (*console_out)(const char *msg)) { + ao_set_console_out(console_out); +} + extern "C" void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { bootNotification("model", "vendor"); } diff --git a/src/ArduinoOcpp_c.h b/src/ArduinoOcpp_c.h index fb74c201..c44db5fc 100644 --- a/src/ArduinoOcpp_c.h +++ b/src/ArduinoOcpp_c.h @@ -26,6 +26,8 @@ void ao_deinitialize(); void ao_loop(); +void ao_set_console_out_c(void (*console_out)(const char *msg)); + /* * Feed lib with HW related data */ diff --git a/src/ao_opts.h b/src/ao_opts.h index a95f1cad..8f76097f 100644 --- a/src/ao_opts.h +++ b/src/ao_opts.h @@ -25,7 +25,7 @@ long ao_tick_ms_impl(); //#endif #ifndef AO_CUSTOM_CONSOLE_MAXMSGSIZE -#define AO_CUSTOM_CONSOLE_MAXMSGSIZE 500 +#define AO_CUSTOM_CONSOLE_MAXMSGSIZE 192 #endif #endif diff --git a/src/ao_opts_impl.c b/src/ao_opts_impl.c index 41839036..5238ad01 100644 --- a/src/ao_opts_impl.c +++ b/src/ao_opts_impl.c @@ -21,7 +21,8 @@ extern "C" { #endif long ao_tick_ms_impl() { - return xTaskGetTickCount() / configTICK_RATE_HZ; + //return xTaskGetTickCount() / configTICK_RATE_HZ; + return xTaskGetTickCount() * (1000 / configTICK_RATE_HZ); } #ifdef __cplusplus From 1d8946dcaf4eb8a62ab3cbf96fbebfc46c3a6ad7 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sun, 1 May 2022 21:54:35 +0200 Subject: [PATCH 157/696] adapt c-style callback functions for AO --- src/ArduinoOcpp_c.cpp | 32 ++++++++++++++++++++++++++++---- src/ao_opts.h | 2 +- src/ao_opts_impl.c | 4 ++-- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/ArduinoOcpp_c.cpp b/src/ArduinoOcpp_c.cpp index 4511f1ac..7b50aa69 100644 --- a/src/ArduinoOcpp_c.cpp +++ b/src/ArduinoOcpp_c.cpp @@ -24,16 +24,40 @@ extern "C" void ao_set_console_out_c(void (*console_out)(const char *msg)) { ao_set_console_out(console_out); } -extern "C" void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { - bootNotification("model", "vendor"); -} - #ifndef AO_RECEIVE_PAYLOAD_BUFSIZE #define AO_RECEIVE_PAYLOAD_BUFSIZE 1024 #endif char ao_recv_payload_buff [AO_RECEIVE_PAYLOAD_BUFSIZE] = {'\0'}; +std::function wrapCstyleOcppCb(OnOcppMessage cb) { + return [cb] (JsonObject payload) { + auto len = serializeJson(payload, ao_recv_payload_buff, AO_RECEIVE_PAYLOAD_BUFSIZE); + if (len <= 0) { + AO_DBG_WARN("Received payload buffer exceeded. Continue without payload"); + } + cb(len > 0 ? ao_recv_payload_buff : nullptr, len); + }; +} + +std::function wrapCstyleOcppCb(void (*cb)()) { + return cb; +} + +ArduinoOcpp::OnReceiveErrorListener wrapCstyleOcppCb(OnOcppError cb) { + return [cb] (const char *code, const char *description, JsonObject details) { + auto len = serializeJson(details, ao_recv_payload_buff, AO_RECEIVE_PAYLOAD_BUFSIZE); + if (len <= 0) { + AO_DBG_WARN("Received payload buffer exceeded. Continue without payload"); + } + cb(code, description, len > 0 ? ao_recv_payload_buff : "", len); + }; +} + +extern "C" void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { + bootNotification("model", "vendor", wrapCstyleOcppCb(onConfirmation), wrapCstyleOcppCb(onAbort), wrapCstyleOcppCb(onTimeout), wrapCstyleOcppCb(onError)); +} + extern "C" void ao_onResetRequest(OnOcppMessage onRequest) { OnReceiveReqListener cb = [onRequest] (JsonObject payload) { auto len = serializeJson(payload, ao_recv_payload_buff, AO_RECEIVE_PAYLOAD_BUFSIZE); diff --git a/src/ao_opts.h b/src/ao_opts.h index 8f76097f..5a19cede 100644 --- a/src/ao_opts.h +++ b/src/ao_opts.h @@ -5,7 +5,7 @@ extern "C" { #endif -long ao_tick_ms_impl(); +unsigned long ao_tick_ms_impl(); //unsigned int32_t ao_avail_heap_impl(); #ifdef __cplusplus diff --git a/src/ao_opts_impl.c b/src/ao_opts_impl.c index 5238ad01..ce1b6559 100644 --- a/src/ao_opts_impl.c +++ b/src/ao_opts_impl.c @@ -20,9 +20,9 @@ extern "C" { #endif -long ao_tick_ms_impl() { +unsigned long ao_tick_ms_impl() { //return xTaskGetTickCount() / configTICK_RATE_HZ; - return xTaskGetTickCount() * (1000 / configTICK_RATE_HZ); + return (xTaskGetTickCount() * 1000UL) / configTICK_RATE_HZ; } #ifdef __cplusplus From 8eb95d06dfc90d26af8daeebea3f0a9653fa5c84 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 3 May 2022 16:00:00 +0200 Subject: [PATCH 158/696] Customizable meter values format --- src/ArduinoOcpp/MessagesV16/MeterValues.cpp | 7 +- src/ArduinoOcpp/MessagesV16/MeterValues.h | 2 +- .../MessagesV16/TriggerMessage.cpp | 4 +- .../Metering/ConnectorMeterValuesRecorder.cpp | 228 +++++++++++------- .../Metering/ConnectorMeterValuesRecorder.h | 34 ++- src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp | 108 +++++++++ src/ArduinoOcpp/Tasks/Metering/MeterValue.h | 52 ++-- .../Tasks/Metering/MeteringService.cpp | 4 +- .../Tasks/Metering/MeteringService.h | 2 +- .../Tasks/Metering/SampledValue.cpp | 67 +++++ src/ArduinoOcpp/Tasks/Metering/SampledValue.h | 56 ++--- 11 files changed, 389 insertions(+), 175 deletions(-) create mode 100644 src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp create mode 100644 src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp index 26c60593..97863a93 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp @@ -15,12 +15,9 @@ MeterValues::MeterValues() { } -MeterValues::MeterValues(const std::vector>& meterValue, int connectorId, int transactionId) - : connectorId{connectorId}, transactionId{transactionId} { +MeterValues::MeterValues(std::vector>&& meterValue, int connectorId, int transactionId) + : meterValue{std::move(meterValue)}, connectorId{connectorId}, transactionId{transactionId} { - for (auto value = meterValue.begin(); value != meterValue.end(); value++) { - this->meterValue.push_back(std::unique_ptr(new MeterValue(**value))); - } } MeterValues::~MeterValues(){ diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.h b/src/ArduinoOcpp/MessagesV16/MeterValues.h index 5fba8009..24f4be0e 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.h +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.h @@ -22,7 +22,7 @@ class MeterValues : public OcppMessage { int transactionId = -1; public: - MeterValues(const std::vector>& meterValue, int connectorId, int transactionId); + MeterValues(std::vector>&& meterValue, int connectorId, int transactionId); MeterValues(); //for debugging only. Make this for the server pendant diff --git a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp index 3ab778be..3673a589 100644 --- a/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp +++ b/src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp @@ -31,10 +31,10 @@ void TriggerMessage::processReq(JsonObject payload) { if (connectorId < 0) { auto nConnectors = mService->getNumConnectors(); for (decltype(nConnectors) i = 0; i < nConnectors; i++) { - triggeredOperations.push_back(mService->takeMeterValuesNow(i)); + triggeredOperations.push_back(mService->takeTriggeredMeterValues(i)); } } else if (connectorId < mService->getNumConnectors()) { - triggeredOperations.push_back(mService->takeMeterValuesNow(connectorId)); + triggeredOperations.push_back(mService->takeTriggeredMeterValues(connectorId)); } else { errorCode = "PropertyConstraintViolation"; } diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index 1d71648f..ffc6b91f 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -16,117 +16,162 @@ using namespace ArduinoOcpp::Ocpp16; ConnectorMeterValuesRecorder::ConnectorMeterValuesRecorder(OcppModel& context, int connectorId) : context(context), connectorId{connectorId} { - MeterValueSampleInterval = declareConfiguration("MeterValueSampleInterval", 60); + auto MeterValuesSampledData = declareConfiguration( + "MeterValuesSampledData", + "Energy.Active.Import.Register,Power.Active.Import", + CONFIGURATION_FN, + true,true,true,false + ); MeterValuesSampledDataMaxLength = declareConfiguration("MeterValuesSampledDataMaxLength", 4, CONFIGURATION_VOLATILE, false, true, false, false); -} - -void ConnectorMeterValuesRecorder::takeSample() { - if (meterValueSamplers.empty()) return; - - std::unique_ptr sample; - if (context.getOcppTime().isValid()) { - sample.reset(new MeterValue(context.getOcppTime().getOcppTimestampNow())); - } - if (!sample) { - return; - } - - for (auto mvs = meterValueSamplers.begin(); mvs != meterValueSamplers.end(); mvs++) { - sample->addSampledValue((*mvs)->takeValue()); - } - - meterValue.push_back(std::move(sample)); + MeterValueSampleInterval = declareConfiguration("MeterValueSampleInterval", 60); + + auto StopTxnSampledData = declareConfiguration( + "StopTxnSampledData", + "", + CONFIGURATION_FN, + true,true,true,false + ); + StopTxnSampledDataMaxLength = declareConfiguration("StopTxnSampledDataMaxLength", 4, CONFIGURATION_VOLATILE, false, true, false, false); + + auto MeterValuesAlignedData = declareConfiguration( + "MeterValuesAlignedData", + "Energy.Active.Import.Register,Power.Active.Import", + CONFIGURATION_FN, + true,true,true,false + ); + MeterValuesAlignedDataMaxLength = declareConfiguration("MeterValuesAlignedDataMaxLength", 4, CONFIGURATION_VOLATILE, false, true, false, false); + ClockAlignedDataInterval = declareConfiguration("ClockAlignedDataInterval", 0); + + auto StopTxnAlignedData = declareConfiguration( + "StopTxnAlignedData", + "", + CONFIGURATION_FN, + true,true,true,false + ); + StopTxnAlignedDataMaxLength = declareConfiguration("StopTxnAlignedDataMaxLength", 4, CONFIGURATION_VOLATILE, false, true, false, false); + + sampledDataBuilder = std::unique_ptr(new MeterValueBuilder(samplers, MeterValuesSampledData)); AO_DBG_DEBUG("After MeterValuesSampledData"); + alignedDataBuilder = std::unique_ptr(new MeterValueBuilder(samplers, MeterValuesAlignedData)); AO_DBG_DEBUG("After MeterValuesAlignedData"); + stopTxnSampledDataBuilder = std::unique_ptr(new MeterValueBuilder(samplers, StopTxnSampledData)); AO_DBG_DEBUG("After StopTxnSampledData"); + stopTxnAlignedDataBuilder = std::unique_ptr(new MeterValueBuilder(samplers, StopTxnAlignedData)); AO_DBG_DEBUG("After StopTxnAlignedData"); } OcppMessage *ConnectorMeterValuesRecorder::loop() { - if (*MeterValueSampleInterval < 1) { - //Metering off by definition - clear(); - return nullptr; - } - - /* - * First: check if there was a transaction break (i.e. transaction either started or stopped; transactionId changed) - */ - auto connector = context.getConnectorStatus(connectorId); - if (connector && connector->getTransactionId() != lastTransactionId) { - //transaction break occured! - auto result = toMeterValues(); - lastTransactionId = connector->getTransactionId(); - return result; - } - - /* - * Calculate energy consumption which finally should be reportet to the Central Station in a MeterValues.req. - * This code uses the EVSE's own energy register, if available (i.e. if energySampler is set). Otherwise it - * uses the power sampler. - * If no powerSampler is available, estimate the energy consumption taking the Charging Schedule and CP Status - * into account. - */ - if (ao_tick_ms() - lastSampleTime >= (ulong) (*MeterValueSampleInterval * 1000)) { - takeSample(); - lastSampleTime = ao_tick_ms(); + if (*ClockAlignedDataInterval >= 1) { + + if (alignedData.size() >= *MeterValuesAlignedDataMaxLength) { + auto meterValues = new MeterValues(std::move(alignedData), connectorId, -1); + alignedData.clear(); + return meterValues; + } + + auto& timestampNow = context.getOcppTime().getOcppTimestampNow(); + auto dt = nextAlignedTime - timestampNow; + if (dt <= 0 || //normal case: interval elapsed + dt > *ClockAlignedDataInterval) { //special case: clock has been adjusted or first run + + AO_DBG_DEBUG("Clock aligned measurement %ds: %s", dt, + abs(dt) <= 60 ? + "in time (tolerance <= 60s)" : "off, e.g. because of first run. Ignore"); abs(-123); + if (abs(dt) <= 60) { //is measurement still "clock-aligned"? + auto alignedMeterValues = alignedDataBuilder->takeSample(context.getOcppTime().getOcppTimestampNow(), ReadingContext::SampleClock); + if (alignedMeterValues) { + alignedData.push_back(std::move(alignedMeterValues)); + } + + if (stopTxnAlignedData.size() + 1 < (size_t) (*StopTxnAlignedDataMaxLength)) { + //ensure that collection keeps one free data slot for final value at StopTransaction + auto alignedStopTx = stopTxnAlignedDataBuilder->takeSample(context.getOcppTime().getOcppTimestampNow(), ReadingContext::SampleClock); + if (alignedStopTx) { + stopTxnAlignedData.push_back(std::move(alignedStopTx)); + } + } + } + + OcppTimestamp midnightBase = OcppTimestamp(2010,0,0,0,0,0); + auto intervall = timestampNow - midnightBase; + intervall %= 3600 * 24; + OcppTimestamp midnight = timestampNow - intervall; + intervall += *ClockAlignedDataInterval; + if (intervall >= 3600 * 24) { + //next measurement is tomorrow; set to precisely 00:00 + nextAlignedTime = midnight; + nextAlignedTime += 3600 * 24; + } else { + intervall /= *ClockAlignedDataInterval; + nextAlignedTime = midnight + (intervall * *ClockAlignedDataInterval); + } + } + } else { + alignedData.clear(); + stopTxnAlignedData.clear(); } + if (*MeterValueSampleInterval >= 1) { + //record periodic tx data + + if (sampledData.size() >= *MeterValuesSampledDataMaxLength) { + auto meterValues = new MeterValues(std::move(sampledData), connectorId, lastTransactionId); + sampledData.clear(); + return meterValues; + } + + auto connector = context.getConnectorStatus(connectorId); + if (connector && connector->getTransactionId() != lastTransactionId) { + //transaction break + MeterValues *meterValues = nullptr; + if (!sampledData.empty()) { + meterValues = new MeterValues(std::move(sampledData), connectorId, lastTransactionId); + sampledData.clear(); + } + lastTransactionId = connector->getTransactionId(); + lastSampleTime = ao_tick_ms(); + return meterValues; + } + + if (ao_tick_ms() - lastSampleTime >= (ulong) (*MeterValueSampleInterval * 1000)) { + auto sampleMeterValues = sampledDataBuilder->takeSample(context.getOcppTime().getOcppTimestampNow(), ReadingContext::SamplePeriodic); + if (sampleMeterValues) { + sampledData.push_back(std::move(sampleMeterValues)); + } + + if (stopTxnSampledData.size() + 1 < (size_t) (*StopTxnSampledDataMaxLength)) { + //ensure that collection keeps one free data slot for final value at StopTransaction + auto sampleStopTx = stopTxnSampledDataBuilder->takeSample(context.getOcppTime().getOcppTimestampNow(), ReadingContext::SamplePeriodic); + if (sampleStopTx) { + stopTxnSampledData.push_back(std::move(sampleStopTx)); + } + } + lastSampleTime = ao_tick_ms(); + } - /* - * Is the value buffer already full? If yes, return MeterValues message - */ - if (((int) meterValue.size()) >= (int) *MeterValuesSampledDataMaxLength) { - auto result = toMeterValues(); - return result; + } else { + sampledData.clear(); + stopTxnSampledData.clear(); } return nullptr; //successful method completition. Currently there is no reason to send a MeterValues Msg. } -OcppMessage *ConnectorMeterValuesRecorder::toMeterValues() { - if (meterValue.empty()) { - AO_DBG_DEBUG("Checking if to send MeterValues ... No"); - clear(); - return nullptr; - } else { - auto result = new MeterValues(meterValue, connectorId, lastTransactionId); - clear(); - return result; - } -} - -OcppMessage *ConnectorMeterValuesRecorder::takeMeterValuesNow() { - - if (meterValueSamplers.empty()) { - return nullptr; - } +OcppMessage *ConnectorMeterValuesRecorder::takeTriggeredMeterValues() { - std::unique_ptr value; + auto sample = sampledDataBuilder->takeSample(context.getOcppTime().getOcppTimestampNow(), ReadingContext::Trigger); - if (context.getOcppTime().isValid()) { - value.reset(new MeterValue(context.getOcppTime().getOcppTimestampNow())); - } - - if (!value) { + if (!sample) { return nullptr; } - for (auto mvs = meterValueSamplers.begin(); mvs != meterValueSamplers.end(); mvs++) { - value->addSampledValue((*mvs)->takeValue()); - } - int txId_now = -1; auto connector = context.getConnectorStatus(connectorId); if (connector) { txId_now = connector->getTransactionId(); } - decltype(meterValue) mv_now; - mv_now.push_back(std::move(value)); - - return new MeterValues(mv_now, connectorId, txId_now); -} + decltype(sampledData) mv_now; + mv_now.push_back(std::move(sample)); -void ConnectorMeterValuesRecorder::clear() { - meterValue.clear(); + return new MeterValues(std::move(mv_now), connectorId, txId_now); } void ConnectorMeterValuesRecorder::setPowerSampler(PowerSampler ps){ @@ -138,14 +183,17 @@ void ConnectorMeterValuesRecorder::setEnergySampler(EnergySampler es){ } void ConnectorMeterValuesRecorder::addMeterValueSampler(std::unique_ptr meterValueSampler) { - meterValueSamplers.push_back(std::move(meterValueSampler)); + if (!meterValueSampler->getMeasurand().compare("Energy.Active.Import.Register")) { + energySamplerIndex = samplers.size(); + } + samplers.push_back(std::move(meterValueSampler)); } int32_t ConnectorMeterValuesRecorder::readEnergyActiveImportRegister() { - if (energySampler != nullptr) { - return energySampler(); + if (energySamplerIndex >= 0 && energySamplerIndex < samplers.size()) { + return samplers[energySamplerIndex]->takeValue(ReadingContext::NOT_SET)->toInteger(); } else { AO_DBG_DEBUG("Called readEnergyActiveImportRegister(), but no energySampler or handling strategy set"); - return 0.f; + return 0; } } diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h index 1148ee7e..502724d1 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h @@ -30,23 +30,39 @@ class ConnectorMeterValuesRecorder { OcppModel& context; const int connectorId; + + std::vector> sampledData; + std::vector> alignedData; + std::vector> stopTxnSampledData; + std::vector> stopTxnAlignedData; + + std::unique_ptr sampledDataBuilder; + std::unique_ptr alignedDataBuilder; + std::unique_ptr stopTxnSampledDataBuilder; + std::unique_ptr stopTxnAlignedDataBuilder; - std::vector> meterValue; + std::shared_ptr> sampledDataSelect; + std::shared_ptr> alignedDataSelect; + std::shared_ptr> stopTxnSampledDataSelect; + std::shared_ptr> stopTxnAlignedDataSelect; ulong lastSampleTime = 0; //0 means not charging right now + OcppTimestamp nextAlignedTime; float lastPower; int lastTransactionId = -1; PowerSampler powerSampler = nullptr; EnergySampler energySampler = nullptr; - std::vector> meterValueSamplers; + std::vector> samplers; + int energySamplerIndex {-1}; - std::shared_ptr> MeterValueSampleInterval = nullptr; - std::shared_ptr> MeterValuesSampledDataMaxLength = nullptr; + std::shared_ptr> MeterValueSampleInterval; + std::shared_ptr> MeterValuesSampledDataMaxLength; + std::shared_ptr> StopTxnSampledDataMaxLength; - void takeSample(); - OcppMessage *toMeterValues(); - void clear(); + std::shared_ptr> ClockAlignedDataInterval; + std::shared_ptr> MeterValuesAlignedDataMaxLength; + std::shared_ptr> StopTxnAlignedDataMaxLength; public: ConnectorMeterValuesRecorder(OcppModel& context, int connectorId); @@ -60,7 +76,9 @@ class ConnectorMeterValuesRecorder { int32_t readEnergyActiveImportRegister(); - OcppMessage *takeMeterValuesNow(); + OcppMessage *takeTriggeredMeterValues(); + + OcppMessage *getStopTransactionData(); }; } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp b/src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp new file mode 100644 index 00000000..e46edd1d --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp @@ -0,0 +1,108 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#include +#include +#include + +using ArduinoOcpp::MeterValue; +using ArduinoOcpp::MeterValueBuilder; + +MeterValue::MeterValue(const MeterValue& other) { + timestamp = other.timestamp; + for (auto value = other.sampledValue.begin(); value != other.sampledValue.end(); value++) { + sampledValue.push_back(std::unique_ptr((*value)->clone())); + } +} + +std::unique_ptr MeterValue::toJson() { + size_t capacity = 0; + std::vector> entries; + for (auto sample = sampledValue.begin(); sample != sampledValue.end(); sample++) { + auto json = (*sample)->toJson(); + capacity += json->capacity(); + entries.push_back(std::move(json)); + } + + capacity += JSON_ARRAY_SIZE(entries.size()); + capacity += JSONDATE_LENGTH + 1; + capacity += JSON_OBJECT_SIZE(2); + + auto result = std::unique_ptr(new DynamicJsonDocument(capacity + 100)); //TODO remove safety space + auto jsonPayload = result->to(); + + char timestampStr [JSONDATE_LENGTH + 1] = {'\0'}; + if (!timestamp.toJsonString(timestampStr, JSONDATE_LENGTH + 1)) { + return nullptr; + } + jsonPayload["timestamp"] = timestampStr; + auto jsonMeterValue = jsonPayload.createNestedArray("sampledValue"); + for (auto entry = entries.begin(); entry != entries.end(); entry++) { + jsonMeterValue.add(**entry); + } + return std::move(result); +} + +MeterValueBuilder::MeterValueBuilder(const std::vector> &samplers, + std::shared_ptr> samplers_select) : + samplers(samplers), + select(samplers_select) { + + updateObservedSamplers(); + select_observe = select->getValueRevision(); +} + +void MeterValueBuilder::updateObservedSamplers() { + + if (select_mask.size() != samplers.size()) { + select_mask.resize(samplers.size(), false); + select_n = 0; + } + + auto selectStr = select->operator const char *(); + size_t sl = 0, sr = 0; + while (selectStr && sl < select->getBuffsize()) { + while (sr < select->getBuffsize()) { + if (selectStr[sr] == ',') { + break; + } + sr++; + } + + if (sr != sl + 1) { + for (size_t i = 0; i < samplers.size(); i++) { + if (!strncmp(samplers[i]->getMeasurand().c_str(), selectStr + sl, sr - sl)) { + select_mask[i] = true; + select_n++; + } + } + } + + sr++; + sl = sr; + } +} + +std::unique_ptr MeterValueBuilder::takeSample(const OcppTimestamp& timestamp, const ReadingContext& context) { + if (select_observe != select->getValueRevision() || //OCPP server has changed configuration about which measurands to take + samplers.size() != select_mask.size()) { //Client has added another Measurand; synchronize lists + AO_DBG_DEBUG("Updating observed samplers due to config change or samplers added"); + updateObservedSamplers(); + select_observe = select->getValueRevision(); + } + + if (select_n == 0) { + return nullptr; + } + + auto sample = std::unique_ptr(new MeterValue(timestamp)); + + for (size_t i = 0; i < select_mask.size(); i++) { + if (select_mask[i]) { + sample->addSampledValue(samplers[i]->takeValue(context)); + } + } + + return sample; +} diff --git a/src/ArduinoOcpp/Tasks/Metering/MeterValue.h b/src/ArduinoOcpp/Tasks/Metering/MeterValue.h index 6945fa8c..d9ba7a2b 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeterValue.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeterValue.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -18,42 +19,27 @@ class MeterValue { std::vector> sampledValue; public: MeterValue(OcppTimestamp timestamp) : timestamp(timestamp) { } - MeterValue(const MeterValue& other) { - timestamp = other.timestamp; - for (auto value = other.sampledValue.begin(); value != other.sampledValue.end(); value++) { - sampledValue.push_back(std::unique_ptr((*value)->clone())); - } - } + MeterValue(const MeterValue& other); void addSampledValue(std::unique_ptr sample) {sampledValue.push_back(std::move(sample));} - std::unique_ptr toJson() { - size_t capacity = 0; - std::vector> entries; - for (auto sample = sampledValue.begin(); sample != sampledValue.end(); sample++) { - auto json = (*sample)->toJson(); - capacity += json->capacity(); - entries.push_back(std::move(json)); - } - - capacity += JSON_ARRAY_SIZE(entries.size()); - capacity += JSONDATE_LENGTH + 1; - capacity += JSON_OBJECT_SIZE(2); - - auto result = std::unique_ptr(new DynamicJsonDocument(capacity + 100)); //TODO remove safety space - auto jsonPayload = result->to(); - - char timestampStr [JSONDATE_LENGTH + 1] = {'\0'}; - if (!timestamp.toJsonString(timestampStr, JSONDATE_LENGTH + 1)) { - return nullptr; - } - jsonPayload["timestamp"] = timestampStr; - auto jsonMeterValue = jsonPayload.createNestedArray("sampledValue"); - for (auto entry = entries.begin(); entry != entries.end(); entry++) { - jsonMeterValue.add(**entry); - } - return std::move(result); - } + std::unique_ptr toJson(); +}; + +class MeterValueBuilder { +private: + const std::vector> &samplers; + std::shared_ptr> select; + std::vector select_mask; + unsigned int select_n {0}; + decltype(select->getValueRevision()) select_observe; + + void updateObservedSamplers(); +public: + MeterValueBuilder(const std::vector> &samplers, + std::shared_ptr> samplers_select); + + std::unique_ptr takeSample(const OcppTimestamp& timestamp, const ReadingContext& context); }; } diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp index c8e95419..97c2a792 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp @@ -61,14 +61,14 @@ int32_t MeteringService::readEnergyActiveImportRegister(int connectorId) { return connectors[connectorId]->readEnergyActiveImportRegister(); } -std::unique_ptr MeteringService::takeMeterValuesNow(int connectorId) { +std::unique_ptr MeteringService::takeTriggeredMeterValues(int connectorId) { if (connectorId < 0 || connectorId >= (int) connectors.size()) { AO_DBG_ERR("connectorId out of bounds. Ignore"); return nullptr; } auto& connector = connectors.at(connectorId); if (connector.get()) { - auto msg = connector->takeMeterValuesNow(); + auto msg = connector->takeTriggeredMeterValues(); if (msg) { auto meterValues = makeOcppOperation(msg); meterValues->setTimeout(std::unique_ptr{new FixedTimeout(120000)}); diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h index 25814b37..f82700a3 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h @@ -38,7 +38,7 @@ class MeteringService { int32_t readEnergyActiveImportRegister(int connectorId); - std::unique_ptr takeMeterValuesNow(int connectorId); //snapshot of all meters now + std::unique_ptr takeTriggeredMeterValues(int connectorId); //snapshot of all meters now int getNumConnectors() {return connectors.size();} }; diff --git a/src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp b/src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp new file mode 100644 index 00000000..75227b11 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp @@ -0,0 +1,67 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#include +#include + +using ArduinoOcpp::SampledValue; + +//helper function +namespace ArduinoOcpp { +namespace Ocpp16 { +const char *cstrFromReadingContext(ReadingContext context) { + switch (context) { + case (ReadingContext::InterruptionBegin): + return "Interruption.Begin"; + case (ReadingContext::InterruptionEnd): + return "Interruption.End"; + case (ReadingContext::Other): + return "Other"; + case (ReadingContext::SampleClock): + return "Sample.Clock"; + case (ReadingContext::SamplePeriodic): + return "Sample.Periodic"; + case (ReadingContext::TransactionBegin): + return "Transaction.Begin"; + case (ReadingContext::TransactionEnd): + return "Transaction.End"; + case (ReadingContext::Trigger): + return "Trigger"; + default: + AO_DBG_ERR("ReadingContext not specified"); + case (ReadingContext::NOT_SET): + return nullptr; + } +} + +}} //end namespaces + +std::unique_ptr SampledValue::toJson() { + auto value = serializeValue(); + size_t capacity = 0; + capacity += JSON_OBJECT_SIZE(8); + capacity += value.length() + 1 + + properties.getFormat().length() + 1 + + properties.getMeasurand().length() + 1 + + properties.getPhase().length() + 1 + + properties.getLocation().length() + 1 + + properties.getUnit().length() + 1; + auto result = std::unique_ptr(new DynamicJsonDocument(capacity + 100)); //TODO remove safety space + auto payload = result->to(); + payload["value"] = value; + auto context_cstr = Ocpp16::cstrFromReadingContext(context); + if (context_cstr) + payload["context"] = context_cstr; + if (!properties.getFormat().empty()) + payload["format"] = properties.getFormat(); + if (!properties.getMeasurand().empty()) + payload["measurand"] = properties.getMeasurand(); + if (!properties.getPhase().empty()) + payload["phase"] = properties.getPhase(); + if (!properties.getLocation().empty()) + payload["location"] = properties.getLocation(); + if (!properties.getUnit().empty()) + payload["unit"] = properties.getUnit(); + return std::move(result); +} diff --git a/src/ArduinoOcpp/Tasks/Metering/SampledValue.h b/src/ArduinoOcpp/Tasks/Metering/SampledValue.h index f2a3adb9..c6a4d7ec 100644 --- a/src/ArduinoOcpp/Tasks/Metering/SampledValue.h +++ b/src/ArduinoOcpp/Tasks/Metering/SampledValue.h @@ -39,7 +39,6 @@ class SampledValueProperties { std::string unit; const std::string& getFormat() const {return format;} - const std::string& getMeasurand() const {return measurand;} const std::string& getPhase() const {return phase;} const std::string& getLocation() const {return location;} const std::string& getUnit() const {return unit;} @@ -57,45 +56,35 @@ class SampledValueProperties { void setFormat(const char *format) {this->format = format;} void setMeasurand(const char *measurand) {this->measurand = measurand;} + const std::string& getMeasurand() const {return measurand;} void setPhase(const char *phase) {this->phase = phase;} void setLocation(const char *location) {this->location = location;} void setUnit(const char *unit) {this->unit = unit;} }; +enum class ReadingContext { + InterruptionBegin, + InterruptionEnd, + Other, + SampleClock, + SamplePeriodic, + TransactionBegin, + TransactionEnd, + Trigger, + NOT_SET +}; + class SampledValue { protected: const SampledValueProperties& properties; + const ReadingContext context; virtual std::string serializeValue() = 0; public: - SampledValue(const SampledValueProperties& properties) : properties(properties) { } - SampledValue(const SampledValue& other) : properties(other.properties) { } + SampledValue(const SampledValueProperties& properties, ReadingContext context) : properties(properties), context(context) { } + SampledValue(const SampledValue& other) : properties(other.properties), context(other.context) { } virtual ~SampledValue() = default; - std::unique_ptr toJson() { - auto value = serializeValue(); - size_t capacity = 0; - capacity += JSON_OBJECT_SIZE(7); - capacity += value.length() + 1 - + properties.getFormat().length() + 1 - + properties.getMeasurand().length() + 1 - + properties.getPhase().length() + 1 - + properties.getLocation().length() + 1 - + properties.getUnit().length() + 1; - auto result = std::unique_ptr(new DynamicJsonDocument(capacity + 100)); //TODO remove safety space - auto payload = result->to(); - payload["value"] = value; - if (!properties.getFormat().empty()) - payload["format"] = properties.getFormat(); - if (!properties.getMeasurand().empty()) - payload["measurand"] = properties.getMeasurand(); - if (!properties.getPhase().empty()) - payload["phase"] = properties.getPhase(); - if (!properties.getLocation().empty()) - payload["location"] = properties.getLocation(); - if (!properties.getUnit().empty()) - payload["unit"] = properties.getUnit(); - return std::move(result); - } + std::unique_ptr toJson(); virtual std::unique_ptr clone() = 0; @@ -107,7 +96,7 @@ class SampledValueConcrete : public SampledValue { private: const T value; public: - SampledValueConcrete(const SampledValueProperties& properties, const T&& value) : SampledValue(properties), value(value) { } + SampledValueConcrete(const SampledValueProperties& properties, ReadingContext context, const T&& value) : SampledValue(properties, context), value(value) { } SampledValueConcrete(const SampledValueConcrete& other) : SampledValue(other), value(other.value) { } ~SampledValueConcrete() = default; @@ -124,7 +113,8 @@ class SampledValueSampler { public: SampledValueSampler(SampledValueProperties properties) : properties(properties) { } virtual ~SampledValueSampler() = default; - virtual std::unique_ptr takeValue() = 0; + virtual std::unique_ptr takeValue(ReadingContext context) = 0; + const std::string& getMeasurand() {return properties.getMeasurand();}; }; template @@ -133,11 +123,11 @@ class SampledValueSamplerConcrete : public SampledValueSampler { std::function sampler; public: SampledValueSamplerConcrete(SampledValueProperties properties, std::function sampler) : SampledValueSampler(properties), sampler(sampler) { } - std::unique_ptr takeValue() override { - return std::unique_ptr>(new SampledValueConcrete(properties, sampler())); + std::unique_ptr takeValue(ReadingContext context) override { + return std::unique_ptr>(new SampledValueConcrete(properties, context, sampler())); } }; -} +} //end namespace ArduinoOcpp #endif From 0a3d89b7ff4e79561cb2cacc58b8336008978b80 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sun, 8 May 2022 18:52:58 +0200 Subject: [PATCH 159/696] complete C facade definitions --- src/ArduinoOcpp.h | 2 +- src/ArduinoOcpp_c.cpp | 107 +++++++++++++++++++++++++++++++++++++----- src/ArduinoOcpp_c.h | 4 -- 3 files changed, 95 insertions(+), 18 deletions(-) diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 6fbaec92..18882796 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -30,7 +30,7 @@ void OCPP_initialize(const char *CS_hostname, uint16_t CS_port, const char *CS_u #endif //Lets you use your own WebSocket implementation -void OCPP_initialize(ArduinoOcpp::OcppSocket& ocppSocket, float V_eff = 230.f /*German grid*/, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, ArduinoOcpp::OcppClock system_time = ArduinoOcpp::Clocks::DEFAULT_CLOCK); +void OCPP_initialize(ArduinoOcpp::OcppSocket& ocppSocket, float V_eff = 230.f /*European grid*/, ArduinoOcpp::FilesystemOpt fsOpt = ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, ArduinoOcpp::OcppClock system_time = ArduinoOcpp::Clocks::DEFAULT_CLOCK); //experimental; More testing required (help needed: it would be awesome if you can you publish your evaluation results on the GitHub page) void OCPP_deinitialize(); diff --git a/src/ArduinoOcpp_c.cpp b/src/ArduinoOcpp_c.cpp index 7b50aa69..b44dceb4 100644 --- a/src/ArduinoOcpp_c.cpp +++ b/src/ArduinoOcpp_c.cpp @@ -10,6 +10,7 @@ extern "C" void ao_initialize(AOcppSocket *osock) { if (!osock) { AO_DBG_ERR("osock is null"); } + AO_DBG_ERR("no error"); ocppSocket = reinterpret_cast(osock); @@ -30,7 +31,7 @@ extern "C" void ao_set_console_out_c(void (*console_out)(const char *msg)) { char ao_recv_payload_buff [AO_RECEIVE_PAYLOAD_BUFSIZE] = {'\0'}; -std::function wrapCstyleOcppCb(OnOcppMessage cb) { +std::function adaptCb(OnOcppMessage cb) { return [cb] (JsonObject payload) { auto len = serializeJson(payload, ao_recv_payload_buff, AO_RECEIVE_PAYLOAD_BUFSIZE); if (len <= 0) { @@ -40,11 +41,11 @@ std::function wrapCstyleOcppCb(OnOcppMessage cb) { }; } -std::function wrapCstyleOcppCb(void (*cb)()) { +std::function adaptCb(void (*cb)()) { return cb; } -ArduinoOcpp::OnReceiveErrorListener wrapCstyleOcppCb(OnOcppError cb) { +ArduinoOcpp::OnReceiveErrorListener adaptCb(OnOcppError cb) { return [cb] (const char *code, const char *description, JsonObject details) { auto len = serializeJson(details, ao_recv_payload_buff, AO_RECEIVE_PAYLOAD_BUFSIZE); if (len <= 0) { @@ -54,18 +55,98 @@ ArduinoOcpp::OnReceiveErrorListener wrapCstyleOcppCb(OnOcppError cb) { }; } -extern "C" void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { - bootNotification("model", "vendor", wrapCstyleOcppCb(onConfirmation), wrapCstyleOcppCb(onAbort), wrapCstyleOcppCb(onTimeout), wrapCstyleOcppCb(onError)); +std::function adaptCb(SamplerBool cb) { + return cb; +} + +std::function adaptCb(SamplerString cb) { + return cb; +} + +void ao_setEvRequestsEnergySampler(SamplerBool evRequestsEnergy) { + setEvRequestsEnergySampler(adaptCb(evRequestsEnergy)); +} + +void ao_setConnectorEnergizedSampler(SamplerBool connectorEnergized) { + setConnectorEnergizedSampler(adaptCb(connectorEnergized)); +} + +void ao_setConnectorPluggedSampler(SamplerBool connectorPlugged) { + setConnectorPluggedSampler(adaptCb(connectorPlugged)); +} + +void ao_addConnectorErrorCodeSampler(SamplerString connectorErrorCode) { + addConnectorErrorCodeSampler(adaptCb(connectorErrorCode)); +} + +void ao_onChargingRateLimitChange(void (*chargingRateChanged)(float)) { + setOnChargingRateLimitChange(chargingRateChanged); +} + +void ao_onUnlockConnector(SamplerBool unlockConnector) { + setOnUnlockConnector(adaptCb(unlockConnector)); +} + +void ao_onRemoteStartTransactionSendConf(OnOcppMessage onSendConf) { + setOnRemoteStopTransactionSendConf(adaptCb(onSendConf)); +} + +void ao_onRemoteStopTransactionSendConf(OnOcppMessage onSendConf) { + setOnRemoteStopTransactionSendConf(adaptCb(onSendConf)); +} + +void ao_onRemoteStopTransactionRequest(OnOcppMessage onRequest) { + setOnRemoteStopTransactionReceiveReq(adaptCb(onRequest)); +} + +void ao_onResetSendConf(OnOcppMessage onSendConf) { + setOnResetSendConf(adaptCb(onSendConf)); } extern "C" void ao_onResetRequest(OnOcppMessage onRequest) { - OnReceiveReqListener cb = [onRequest] (JsonObject payload) { - auto len = serializeJson(payload, ao_recv_payload_buff, AO_RECEIVE_PAYLOAD_BUFSIZE); - if (len <= 0) { - AO_DBG_WARN("Received payload buffer exceeded. Continue without payload"); - } - onRequest(len > 0 ? ao_recv_payload_buff : nullptr, len); - }; - setOnResetReceiveReq(cb); + setOnResetReceiveReq(adaptCb(onRequest)); +} + +extern "C" void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { + bootNotification("model", "vendor", adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); +} + +void ao_authorize(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { + authorize(idTag, adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); +} + +void ao_startTransaction(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { + startTransaction(idTag, adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); +} + +void ao_stopTransaction(OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { + stopTransaction(adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); } +int ao_getTransactionId() { + return getTransactionId(); +} + +bool ao_ocppPermitsCharge() { + return ocppPermitsCharge(); +} + +bool ao_isAvailable() { + return isAvailable(); +} + +void ao_beginSession(const char *idTag) { + return beginSession(idTag); +} + +void ao_endSession() { + return endSession(); +} + +bool ao_isInSession() { + return isInSession(); +} + +const char *ao_getSessionIdTag() { + return getSessionIdTag(); +} diff --git a/src/ArduinoOcpp_c.h b/src/ArduinoOcpp_c.h index c44db5fc..b36af90a 100644 --- a/src/ArduinoOcpp_c.h +++ b/src/ArduinoOcpp_c.h @@ -42,8 +42,6 @@ void ao_setConnectorEnergizedSampler(SamplerBool connectorEnergized); void ao_setConnectorPluggedSampler(SamplerBool connectorPlugged); -//void setConnectorFaultedSampler(SamplerBool connectorFailed); - void ao_addConnectorErrorCodeSampler(SamplerString connectorErrorCode); /* @@ -58,8 +56,6 @@ void ao_onUnlockConnector(SamplerBool unlockConnector); //true: success, false: * Generic listeners for OCPP operations initiated by Central System */ -void ao_onSetChargingProfileRequest(OnOcppMessage onRequest); //optional - void ao_onRemoteStartTransactionSendConf(OnOcppMessage onSendConf); //important, energize the power plug here and capture the idTag void ao_onRemoteStopTransactionSendConf(OnOcppMessage onSendConf); //important, de-energize the power plug here From c22495379dbcba7a2a0e06ed4e74a5e68466cafc Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 18 May 2022 16:40:26 +0200 Subject: [PATCH 160/696] Async meter measurments and connector unlock --- src/ArduinoOcpp.cpp | 8 +-- src/ArduinoOcpp.h | 3 +- src/ArduinoOcpp/Core/OcppOperation.cpp | 15 +++--- src/ArduinoOcpp/Core/PollResult.h | 51 +++++++++++++++++++ src/ArduinoOcpp/MessagesV16/MeterValues.cpp | 3 ++ .../MessagesV16/StartTransaction.cpp | 10 +++- .../MessagesV16/StartTransaction.h | 3 +- .../MessagesV16/StopTransaction.cpp | 11 +++- src/ArduinoOcpp/MessagesV16/StopTransaction.h | 3 +- .../MessagesV16/UnlockConnector.cpp | 25 +++++---- src/ArduinoOcpp/MessagesV16/UnlockConnector.h | 5 +- .../ChargePointStatus/ConnectorStatus.cpp | 4 +- .../Tasks/ChargePointStatus/ConnectorStatus.h | 7 +-- .../Metering/ConnectorMeterValuesRecorder.cpp | 12 ++--- .../Metering/ConnectorMeterValuesRecorder.h | 2 +- src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp | 10 ++-- src/ArduinoOcpp/Tasks/Metering/MeterValue.h | 2 +- .../Tasks/Metering/MeteringService.cpp | 4 +- .../Tasks/Metering/MeteringService.h | 2 +- .../Tasks/Metering/SampledValue.cpp | 3 ++ src/ArduinoOcpp/Tasks/Metering/SampledValue.h | 33 +++++++----- 21 files changed, 151 insertions(+), 65 deletions(-) create mode 100644 src/ArduinoOcpp/Core/PollResult.h diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index b32ae275..d8ffe4d1 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -174,7 +174,7 @@ void setPowerActiveImportSampler(std::function power) { auto mvs = std::unique_ptr>>( new SampledValueSamplerConcrete>( meterProperties, - power + [power] (ReadingContext) {return power();} )); model.getMeteringService()->addMeterValueSampler(OCPP_ID_OF_CONNECTOR, std::move(mvs)); //connectorId=1 model.getMeteringService()->setPowerSampler(OCPP_ID_OF_CONNECTOR, power); @@ -195,7 +195,9 @@ void setEnergyActiveImportSampler(std::function energy) { meterProperties.setUnit("Wh"); auto mvs = std::unique_ptr>>( new SampledValueSamplerConcrete>( - meterProperties, energy)); + meterProperties, + [energy] (ReadingContext) {return energy();} + )); model.getMeteringService()->addMeterValueSampler(OCPP_ID_OF_CONNECTOR, std::move(mvs)); //connectorId=1 model.getMeteringService()->setEnergySampler(OCPP_ID_OF_CONNECTOR, energy); } @@ -278,7 +280,7 @@ void setOnChargingRateLimitChange(std::function chargingRateChanged model.getSmartChargingService()->setOnLimitChange(chargingRateChanged); } -void setOnUnlockConnector(std::function unlockConnector) { +void setOnUnlockConnector(std::function()> unlockConnector) { if (!ocppEngine) { AO_DBG_ERR("Please call OCPP_initialize before"); return; diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 59953387..15b3ee91 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -14,6 +14,7 @@ #include #include #include +#include #include using ArduinoOcpp::OnReceiveConfListener; @@ -76,7 +77,7 @@ void addConnectorErrorCodeSampler(std::function connectorErrorCo void setOnChargingRateLimitChange(std::function chargingRateChanged); -void setOnUnlockConnector(std::function unlockConnector); //true: success, false: failure +void setOnUnlockConnector(std::function()> unlockConnector); //true: success, false: failure /* * React on CS-initiated operations diff --git a/src/ArduinoOcpp/Core/OcppOperation.cpp b/src/ArduinoOcpp/Core/OcppOperation.cpp index 52a9f7e2..9f651caa 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.cpp +++ b/src/ArduinoOcpp/Core/OcppOperation.cpp @@ -104,8 +104,7 @@ boolean OcppOperation::sendReq(OcppSocket& ocppSocket){ */ auto requestPayload = ocppMessage->createReq(); if (!requestPayload) { - onAbortListener(); - return true; + return false; } /* @@ -240,12 +239,16 @@ boolean OcppOperation::sendConf(OcppSocket& ocppSocket){ * Create the OCPP message */ std::unique_ptr confJson = nullptr; - std::unique_ptr confPayload = std::unique_ptr(ocppMessage->createConf()); + std::unique_ptr confPayload = ocppMessage->createConf(); std::unique_ptr errorDetails = nullptr; - bool operationSuccess = ocppMessage->getErrorCode() == nullptr && confPayload != nullptr; + bool operationFailure = ocppMessage->getErrorCode() != nullptr; + + if (!operationFailure && !confPayload) { + return false; //confirmation message still pending + } - if (operationSuccess) { + if (!operationFailure) { /* * Create OCPP-J Remote Procedure Call header @@ -293,7 +296,7 @@ boolean OcppOperation::sendConf(OcppSocket& ocppSocket){ boolean wsSuccess = ocppSocket.sendTXT(out); if (wsSuccess) { - if (operationSuccess) { + if (!operationFailure) { AO_DBG_TRAFFIC_OUT(out.c_str()); onSendConfListener(confPayload->as()); } else { diff --git a/src/ArduinoOcpp/Core/PollResult.h b/src/ArduinoOcpp/Core/PollResult.h new file mode 100644 index 00000000..a21324f8 --- /dev/null +++ b/src/ArduinoOcpp/Core/PollResult.h @@ -0,0 +1,51 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef POLLRESULT_H +#define POLLRESULT_H + +#include +#include + +namespace ArduinoOcpp { + +template +class PollResult { +private: + bool ready; + T value; +public: + PollResult() : ready(false) {} + PollResult(T&& value) : ready(true), value(value) {} + PollResult(const PollResult&) = delete; + PollResult& operator =(const PollResult&) = delete; + PollResult& operator =(const PollResult&& other) { + ready = other.ready; + value = std::move(other.value); + return *this; + } + PollResult(PollResult&& other) : ready(other.ready), value(std::move(other.value)) {} + T&& toValue() { + if (!ready) { + AO_DBG_ERR("Not ready"); + (void)0; + } + ready = false; + return std::move(value); + } + T& getValue() const { + if (!ready) { + AO_DBG_ERR("Not ready"); + (void)0; + } + return *value; + } + operator bool() const {return ready;} + + static PollResult Await() {return PollResult();} +}; + +} + +#endif diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp index 97863a93..87559f96 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp @@ -35,6 +35,9 @@ std::unique_ptr MeterValues::createReq() { std::vector> entries; for (auto value = meterValue.begin(); value != meterValue.end(); value++) { auto entry = (*value)->toJson(); + if (!entry) { + return nullptr; + } capacity += entry->capacity(); entries.push_back(std::move(entry)); } diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp index 339aa8b1..f7f3ec57 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp @@ -67,12 +67,18 @@ void StartTransaction::initiate() { } std::unique_ptr StartTransaction::createReq() { + + if (meterStart && !*meterStart) { + //meterStart not ready yet + return nullptr; + } + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(5) + (JSONDATE_LENGTH + 1) + (IDTAG_LEN_MAX + 1))); JsonObject payload = doc->to(); payload["connectorId"] = connectorId; - if (meterStart >= 0) { - payload["meterStart"] = meterStart; + if (meterStart && *meterStart) { + payload["meterStart"] = meterStart->toInteger(); } if (otimestamp > MIN_TIME) { diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.h b/src/ArduinoOcpp/MessagesV16/StartTransaction.h index 6390a0f1..153da753 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace ArduinoOcpp { namespace Ocpp16 { @@ -15,7 +16,7 @@ namespace Ocpp16 { class StartTransaction : public OcppMessage { private: int connectorId = 1; - int32_t meterStart = -1; + std::unique_ptr meterStart {nullptr}; OcppTimestamp otimestamp; char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; uint16_t transactionRev = 0; diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp index 1f5c002c..6466a85c 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp @@ -46,11 +46,18 @@ void StopTransaction::initiate() { } std::unique_ptr StopTransaction::createReq() { + + if (meterStop && !*meterStop) { + //meterStop not ready yet + return nullptr; + } + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(5) + (JSONDATE_LENGTH + 1) + (REASON_LEN_MAX + 1))); JsonObject payload = doc->to(); - if (meterStop >= 0) - payload["meterStop"] = meterStop; //TODO meterStart is required to be in Wh, but measuring unit is probably inconsistent in implementation + if (meterStop && *meterStop) { + payload["meterStop"] = meterStop->toInteger(); + } if (otimestamp > MIN_TIME) { char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.h b/src/ArduinoOcpp/MessagesV16/StopTransaction.h index 6268b8f4..08a91957 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace ArduinoOcpp { namespace Ocpp16 { @@ -15,7 +16,7 @@ namespace Ocpp16 { class StopTransaction : public OcppMessage { private: int connectorId = 1; - int32_t meterStop = -1; + std::unique_ptr meterStop {nullptr}; OcppTimestamp otimestamp; char reason [REASON_LEN_MAX] {'\0'}; public: diff --git a/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp b/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp index a7418c44..e883c89c 100644 --- a/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp +++ b/src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp @@ -19,7 +19,7 @@ const char* UnlockConnector::getOcppOperationType(){ void UnlockConnector::processReq(JsonObject payload) { - int connectorId = payload["connectorId"] | -1; + auto connectorId = payload["connectorId"] | -1; if (!ocppModel || !ocppModel->getConnectorStatus(connectorId)) { err = true; @@ -30,24 +30,29 @@ void UnlockConnector::processReq(JsonObject payload) { connector->endSession("UnlockCommand"); - std::function unlockConnector = connector->getOnUnlockConnector(); + unlockConnector = connector->getOnUnlockConnector(); if (unlockConnector != nullptr) { - cbDefined = true; + cbUnlockResult = unlockConnector(); } else { - cbDefined = false; AO_DBG_WARN("Unlock CB undefined"); - return; } - - cbUnlockSuccessful = unlockConnector(); } -std::unique_ptr UnlockConnector::createConf(){ +std::unique_ptr UnlockConnector::createConf() { + if (unlockConnector) { + if (!cbUnlockResult) { + cbUnlockResult = unlockConnector(); + if (!cbUnlockResult) { + return nullptr; //no result yet - delay confirmation response + } + } + } + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); JsonObject payload = doc->to(); - if (err || !cbDefined) { + if (err || !unlockConnector) { payload["status"] = "NotSupported"; - } else if (cbUnlockSuccessful) { + } else if (cbUnlockResult.toValue()) { payload["status"] = "Unlocked"; } else { payload["status"] = "UnlockFailed"; diff --git a/src/ArduinoOcpp/MessagesV16/UnlockConnector.h b/src/ArduinoOcpp/MessagesV16/UnlockConnector.h index d65dc559..2659fd2b 100644 --- a/src/ArduinoOcpp/MessagesV16/UnlockConnector.h +++ b/src/ArduinoOcpp/MessagesV16/UnlockConnector.h @@ -6,6 +6,7 @@ #define UNLOCKCONNECTOR_H #include +#include namespace ArduinoOcpp { namespace Ocpp16 { @@ -13,8 +14,8 @@ namespace Ocpp16 { class UnlockConnector : public OcppMessage { private: bool err = false; - bool cbDefined = false; - bool cbUnlockSuccessful = false; + std::function ()> unlockConnector; + PollResult cbUnlockResult; public: UnlockConnector(); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 6d27b36d..185ff4b5 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -315,10 +315,10 @@ void ConnectorStatus::saveState() { configuration_save(); } -void ConnectorStatus::setOnUnlockConnector(std::function unlockConnector) { +void ConnectorStatus::setOnUnlockConnector(std::function()> unlockConnector) { this->onUnlockConnector = unlockConnector; } -std::function ConnectorStatus::getOnUnlockConnector() { +std::function()> ConnectorStatus::getOnUnlockConnector() { return this->onUnlockConnector; } diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index fb9d3ff0..8c9a8962 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -56,7 +57,7 @@ class ConnectorStatus { //std::function()> startTransactionBehavior; //std::function(const char* stopReason)> stopTransactionBehavior; - std::function onUnlockConnector {nullptr}; + std::function()> onUnlockConnector {nullptr}; std::shared_ptr> stopTransactionOnInvalidId; std::shared_ptr> stopTransactionOnEVSideDisconnect; @@ -100,8 +101,8 @@ class ConnectorStatus { bool ocppPermitsCharge(); - void setOnUnlockConnector(std::function unlockConnector); - std::function getOnUnlockConnector(); + void setOnUnlockConnector(std::function()> unlockConnector); + std::function()> getOnUnlockConnector(); }; } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index ffc6b91f..56620cd5 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -50,10 +50,10 @@ ConnectorMeterValuesRecorder::ConnectorMeterValuesRecorder(OcppModel& context, i ); StopTxnAlignedDataMaxLength = declareConfiguration("StopTxnAlignedDataMaxLength", 4, CONFIGURATION_VOLATILE, false, true, false, false); - sampledDataBuilder = std::unique_ptr(new MeterValueBuilder(samplers, MeterValuesSampledData)); AO_DBG_DEBUG("After MeterValuesSampledData"); - alignedDataBuilder = std::unique_ptr(new MeterValueBuilder(samplers, MeterValuesAlignedData)); AO_DBG_DEBUG("After MeterValuesAlignedData"); - stopTxnSampledDataBuilder = std::unique_ptr(new MeterValueBuilder(samplers, StopTxnSampledData)); AO_DBG_DEBUG("After StopTxnSampledData"); - stopTxnAlignedDataBuilder = std::unique_ptr(new MeterValueBuilder(samplers, StopTxnAlignedData)); AO_DBG_DEBUG("After StopTxnAlignedData"); + sampledDataBuilder = std::unique_ptr(new MeterValueBuilder(samplers, MeterValuesSampledData)); + alignedDataBuilder = std::unique_ptr(new MeterValueBuilder(samplers, MeterValuesAlignedData)); + stopTxnSampledDataBuilder = std::unique_ptr(new MeterValueBuilder(samplers, StopTxnSampledData)); + stopTxnAlignedDataBuilder = std::unique_ptr(new MeterValueBuilder(samplers, StopTxnAlignedData)); } OcppMessage *ConnectorMeterValuesRecorder::loop() { @@ -189,9 +189,9 @@ void ConnectorMeterValuesRecorder::addMeterValueSampler(std::unique_ptr ConnectorMeterValuesRecorder::readEnergyActiveImportRegister() { if (energySamplerIndex >= 0 && energySamplerIndex < samplers.size()) { - return samplers[energySamplerIndex]->takeValue(ReadingContext::NOT_SET)->toInteger(); + return samplers[energySamplerIndex]->takeValue(ReadingContext::NOT_SET); } else { AO_DBG_DEBUG("Called readEnergyActiveImportRegister(), but no energySampler or handling strategy set"); return 0; diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h index 502724d1..a145438b 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h @@ -74,7 +74,7 @@ class ConnectorMeterValuesRecorder { void addMeterValueSampler(std::unique_ptr meterValueSampler); - int32_t readEnergyActiveImportRegister(); + std::unique_ptr readEnergyActiveImportRegister(); OcppMessage *takeTriggeredMeterValues(); diff --git a/src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp b/src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp index e46edd1d..27fe8904 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp @@ -9,18 +9,14 @@ using ArduinoOcpp::MeterValue; using ArduinoOcpp::MeterValueBuilder; -MeterValue::MeterValue(const MeterValue& other) { - timestamp = other.timestamp; - for (auto value = other.sampledValue.begin(); value != other.sampledValue.end(); value++) { - sampledValue.push_back(std::unique_ptr((*value)->clone())); - } -} - std::unique_ptr MeterValue::toJson() { size_t capacity = 0; std::vector> entries; for (auto sample = sampledValue.begin(); sample != sampledValue.end(); sample++) { auto json = (*sample)->toJson(); + if (!json) { + return nullptr; + } capacity += json->capacity(); entries.push_back(std::move(json)); } diff --git a/src/ArduinoOcpp/Tasks/Metering/MeterValue.h b/src/ArduinoOcpp/Tasks/Metering/MeterValue.h index d9ba7a2b..450f5d5b 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeterValue.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeterValue.h @@ -19,7 +19,7 @@ class MeterValue { std::vector> sampledValue; public: MeterValue(OcppTimestamp timestamp) : timestamp(timestamp) { } - MeterValue(const MeterValue& other); + MeterValue(const MeterValue& other) = delete; void addSampledValue(std::unique_ptr sample) {sampledValue.push_back(std::move(sample));} diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp index 97c2a792..c5522f78 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp @@ -53,10 +53,10 @@ void MeteringService::addMeterValueSampler(int connectorId, std::unique_ptraddMeterValueSampler(std::move(meterValueSampler)); } -int32_t MeteringService::readEnergyActiveImportRegister(int connectorId) { +std::unique_ptr MeteringService::readEnergyActiveImportRegister(int connectorId) { if (connectorId < 0 || connectorId >= connectors.size()) { AO_DBG_ERR("connectorId is out of bounds"); - return 0.f; + return nullptr; } return connectors[connectorId]->readEnergyActiveImportRegister(); } diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h index f82700a3..4b51defe 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h @@ -36,7 +36,7 @@ class MeteringService { void addMeterValueSampler(int connectorId, std::unique_ptr meterValueSampler); - int32_t readEnergyActiveImportRegister(int connectorId); + std::unique_ptr readEnergyActiveImportRegister(int connectorId); std::unique_ptr takeTriggeredMeterValues(int connectorId); //snapshot of all meters now diff --git a/src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp b/src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp index 75227b11..4a570829 100644 --- a/src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp @@ -39,6 +39,9 @@ const char *cstrFromReadingContext(ReadingContext context) { std::unique_ptr SampledValue::toJson() { auto value = serializeValue(); + if (value.empty()) { + return nullptr; + } size_t capacity = 0; capacity += JSON_OBJECT_SIZE(8); capacity += value.length() + 1 diff --git a/src/ArduinoOcpp/Tasks/Metering/SampledValue.h b/src/ArduinoOcpp/Tasks/Metering/SampledValue.h index c6a4d7ec..7debf013 100644 --- a/src/ArduinoOcpp/Tasks/Metering/SampledValue.h +++ b/src/ArduinoOcpp/Tasks/Metering/SampledValue.h @@ -14,20 +14,22 @@ template class SampledValueDeSerializer { public: static T deserialize(const char *str); - static std::string serialize(const T& val); - static int32_t toInteger(const T& val); + static bool ready(T& val); + static std::string serialize(T& val); + static int32_t toInteger(T& val); }; template <> -class SampledValueDeSerializer { +class SampledValueDeSerializer { // example class public: - static int32_t deserialize(const char *str) {return 42;} - static std::string serialize(const int32_t& val) { + static int32_t deserialize(const char *str) {return strtol(str, nullptr,10);} + static bool ready(int32_t& val) {return true;} //int32_t is always valid + static std::string serialize(int32_t& val) { char str [12] = {'\0'}; snprintf(str, 12, "%d", val); return std::string(str); } - static int32_t toInteger(const int32_t& val) {return val;} + static int32_t toInteger(int32_t& val) {return val;} }; class SampledValueProperties { @@ -74,6 +76,10 @@ enum class ReadingContext { NOT_SET }; +namespace Ocpp16 { +const char *cstrFromReadingContext(ReadingContext context); +} + class SampledValue { protected: const SampledValueProperties& properties; @@ -86,23 +92,22 @@ class SampledValue { std::unique_ptr toJson(); - virtual std::unique_ptr clone() = 0; - + virtual operator bool() = 0; virtual int32_t toInteger() = 0; }; template class SampledValueConcrete : public SampledValue { private: - const T value; + T value; public: SampledValueConcrete(const SampledValueProperties& properties, ReadingContext context, const T&& value) : SampledValue(properties, context), value(value) { } SampledValueConcrete(const SampledValueConcrete& other) : SampledValue(other), value(other.value) { } ~SampledValueConcrete() = default; - std::string serializeValue() override {return DeSerializer::serialize(value);} + operator bool() override {return DeSerializer::ready(value);} - std::unique_ptr clone() override {return std::unique_ptr>(new SampledValueConcrete(*this));} + std::string serializeValue() override {return DeSerializer::serialize(value);} int32_t toInteger() override { return DeSerializer::toInteger(value);} }; @@ -120,11 +125,11 @@ class SampledValueSampler { template class SampledValueSamplerConcrete : public SampledValueSampler { private: - std::function sampler; + std::function sampler; public: - SampledValueSamplerConcrete(SampledValueProperties properties, std::function sampler) : SampledValueSampler(properties), sampler(sampler) { } + SampledValueSamplerConcrete(SampledValueProperties properties, std::function sampler) : SampledValueSampler(properties), sampler(sampler) { } std::unique_ptr takeValue(ReadingContext context) override { - return std::unique_ptr>(new SampledValueConcrete(properties, context, sampler())); + return std::unique_ptr>(new SampledValueConcrete(properties, context, sampler(context))); } }; From 9ef142b334b30328249ccbe8805fafa11e2c4611 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 19 May 2022 16:23:48 +0200 Subject: [PATCH 161/696] Continuous Integration --- .github/workflows/pio.yaml | 47 +++++++++++++++++++ README.md | 3 ++ .../Core/ConfigurationContainerFlash.cpp | 12 ++--- 3 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/pio.yaml diff --git a/.github/workflows/pio.yaml b/.github/workflows/pio.yaml new file mode 100644 index 00000000..ab6248b7 --- /dev/null +++ b/.github/workflows/pio.yaml @@ -0,0 +1,47 @@ +name: PlatformIO CI + +on: + push: + branches: + - develop + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + example: [examples/ESP/main.cpp, examples/ESP-TLS/main.cpp, examples/SECC/main.cpp] + include: + - example: examples/SECC/main.cpp + dashboard-extra: --lib="/tmp/tzapu/WiFiManager" + + steps: + - uses: actions/checkout@v2 + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + - name: Cache PlatformIO + uses: actions/cache@v2 + with: + path: ~/.platformio + key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} + - name: Set up Python + uses: actions/setup-python@v2 + - name: Install PlatformIO + run: | + python -m pip install --upgrade pip + pip install --upgrade platformio + - name: Install library dependencies + run: pio pkg install + - name: Extra dependencies for SECC example + if: ${{ matrix.dashboard-extra }} + run: git clone https://github.com/tzapu/WiFiManager.git /tmp/tzapu/WiFiManager + - name: Run PlatformIO + run: pio ci --lib="." --project-conf=platformio.ini ${{ matrix.dashboard-extra }} + env: + PLATFORMIO_CI_SRC: ${{ matrix.example }} \ No newline at end of file diff --git a/README.md b/README.md index e485854a..44b2988e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ # Icon   ArduinoOcpp + +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/matth-x/ArduinoOcpp/PlatformIO%20CI?logo=github)](https://github.com/matth-x/ArduinoOcpp/actions) + OCPP-J 1.6 client for the ESP8266 and the ESP32 (more coming soon) Reference usage: [OpenEVSE](https://github.com/OpenEVSE/ESP32_WiFi_V4.x/blob/master/src/ocpp.cpp) diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp index d10adbf3..f8740bff 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp @@ -5,18 +5,12 @@ #include #include -#if defined(ESP32) +#if defined(ESP32) && !defined(AO_DEACTIVATE_FLASH) +#include #define USE_FS LITTLEFS #else -#define USE_FS SPIFFS -#endif - -#if USE_FS == LITTLEFS -#include -#elif USE_FS == SPIFFS #include -#else -#error "FS not supported" +#define USE_FS SPIFFS #endif #define MAX_FILE_SIZE 4000 From 786bc975c52248287f4fef6d3fc64e62217e9424 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 26 May 2022 20:59:31 +0200 Subject: [PATCH 162/696] Decoupled filesystem driver --- CMakeLists.txt | 8 +- src/ArduinoOcpp.cpp | 6 +- src/ArduinoOcpp/Core/Configuration.cpp | 68 ++--- src/ArduinoOcpp/Core/Configuration.h | 7 +- .../Core/ConfigurationContainerFlash.cpp | 180 ++++++------ .../Core/ConfigurationContainerFlash.h | 7 +- src/ArduinoOcpp/Core/FilesystemAdapter.cpp | 274 ++++++++++++++++++ src/ArduinoOcpp/Core/FilesystemAdapter.h | 103 +++++++ src/ArduinoOcpp/Core/OcppConnection.cpp | 2 +- .../MessagesV16/GetConfiguration.cpp | 6 +- 10 files changed, 520 insertions(+), 141 deletions(-) create mode 100644 src/ArduinoOcpp/Core/FilesystemAdapter.cpp create mode 100644 src/ArduinoOcpp/Core/FilesystemAdapter.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 210f3d11..f0307395 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,13 +49,17 @@ set(AO_SRC src/ArduinoOcpp.cpp src/ArduinoOcpp_c.cpp src/ao_opts_impl.c + src/ArduinoOcpp/Core/FilesystemAdapter.cpp ) idf_component_register(SRCS ${AO_SRC} - INCLUDE_DIRS "./src" "${PROJECT_DIR}/include") + INCLUDE_DIRS "./src" "${PROJECT_DIR}/include" + PRIV_REQUIRES spiffs) target_compile_options(${COMPONENT_TARGET} PUBLIC -DAO_CUSTOM_WS -DAO_CUSTOM_CONSOLE -DAO_DEACTIVATE_FLASH - -DAO_DBG_LEVEL=AO_DL_DEBUG) + -DAO_USE_FILEAPI=ESPIDF_SPIFFS + -DAO_DBG_LEVEL=AO_DL_DEBUG + -DAO_TRAFFIC_OUT) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index e90985cb..c96f7985 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -83,8 +84,11 @@ void OCPP_initialize(OcppSocket& ocppSocket, float V_eff, ArduinoOcpp::Filesyste voltage_eff = V_eff; fileSystemOpt = fsOpt; + + std::shared_ptr filesystem = EspWiFi::makeDefaultFilesystemAdapter(fileSystemOpt); + AO_DBG_DEBUG("filesystem %s", filesystem ? "loaded" : "error"); - configuration_init(fileSystemOpt); //call before each other library call + configuration_init(filesystem); //call before each other library call ocppEngine = new OcppEngine(ocppSocket, system_time); auto& model = ocppEngine->getOcppModel(); diff --git a/src/ArduinoOcpp/Core/Configuration.cpp b/src/ArduinoOcpp/Core/Configuration.cpp index cbd75a0a..4dfe8bde 100644 --- a/src/ArduinoOcpp/Core/Configuration.cpp +++ b/src/ArduinoOcpp/Core/Configuration.cpp @@ -12,7 +12,7 @@ namespace ArduinoOcpp { -FilesystemOpt configurationFilesystemOpt = FilesystemOpt::Use_Mount_FormatOnFail; +std::shared_ptr filesystem; template std::shared_ptr> createConfiguration(const char *key, T value) { @@ -46,16 +46,16 @@ std::shared_ptr> createConfiguration(const char *key return configuration; } -std::shared_ptr createConfigurationContainer(const char *filename) { +std::unique_ptr createConfigurationContainer(const char *filename) { //create non-persistent Configuration store (i.e. lives only in RAM) if // - Flash FS usage is switched off OR // - Filename starts with "/volatile" - if (!configurationFilesystemOpt.accessAllowed() || + if (!filesystem || !strncmp(filename, CONFIGURATION_VOLATILE, strlen(CONFIGURATION_VOLATILE))) { - return std::static_pointer_cast(std::make_shared(filename)); + return std::unique_ptr(new ConfigurationContainerVolatile(filename)); } else { - //create persistent Configuration store. This is the normal case - return std::static_pointer_cast(std::make_shared(filename)); + //create persistent Configuration store. This is the normal caseS + return std::unique_ptr(new ConfigurationContainerFlash(filesystem, filename)); } } @@ -154,8 +154,10 @@ std::shared_ptr getConfiguration(const char *key) { return nullptr; } -std::shared_ptr>> getAllConfigurations() { //TODO maybe change to iterator? - auto result = std::make_shared>>(); +std::unique_ptr>> getAllConfigurations() { //TODO maybe change to iterator? + auto result = std::unique_ptr>>( + new std::vector>() + ); for (auto container = configurationContainers.begin(); container != configurationContainers.end(); container++) { for (auto config = (*container)->configurationsIteratorBegin(); config != (*container)->configurationsIteratorEnd(); config++) { @@ -172,33 +174,17 @@ std::shared_ptr>> getAllConfi bool configuration_inited = false; -bool configuration_init(FilesystemOpt fsOpt) { +bool configuration_init(std::shared_ptr _filesystem) { if (configuration_inited) return true; //configuration_init() already called; tolerate multiple calls so user can use this store for //credentials outside ArduinoOcpp which need to be loaded before OCPP_initialize() - bool loadRoutineSuccessful = true; -#ifndef AO_DEACTIVATE_FLASH - - configurationFilesystemOpt = fsOpt; + + filesystem = _filesystem; - if (fsOpt.mustMount()) { -#if defined(ESP32) - if(!LITTLEFS.begin(fsOpt.formatOnFail())) { - AO_DBG_ERR("Error while mounting LITTLEFS"); - loadRoutineSuccessful = false; - } -#else - //ESP8266 - SPIFFSConfig cfg; - cfg.setAutoFormat(fsOpt.formatOnFail()); - SPIFFS.setConfig(cfg); - - if (!SPIFFS.begin()) { - AO_DBG_ERR("Unable to initialize: unable to mount SPIFFS"); - loadRoutineSuccessful = false; - } -#endif - } //end fs mount + if (!filesystem) { + configuration_inited = true; + return true; //no filesystem, nothing can go wrong + } std::shared_ptr containerDefault = nullptr; for (auto container = configurationContainers.begin(); container != configurationContainers.end(); container++) { @@ -208,29 +194,28 @@ bool configuration_init(FilesystemOpt fsOpt) { } } + bool success = true; + if (containerDefault) { - AO_DBG_DEBUG("Found default container before calling configuration_init(). If you added\n" \ - " the container manually, please ensure to call load(). If not, it is a hint\n" \ - " that declareConfiguration() was called too early\n"); + AO_DBG_DEBUG("Found default container before calling configuration_init(). If you added"); + AO_DBG_DEBUG(" > the container manually, please ensure to call load(). If not, it is a hint"); + AO_DBG_DEBUG(" > that declareConfiguration() was called too early"); + (void)0; } else { containerDefault = createConfigurationContainer(CONFIGURATION_FN); if (!containerDefault->load()) { AO_DBG_ERR("Loading default configurations file failed"); - loadRoutineSuccessful = false; + success = false; } configurationContainers.push_back(containerDefault); } - -#endif //ndef AO_DEACTIVATE_FLASH - configuration_inited = loadRoutineSuccessful; - return loadRoutineSuccessful; + configuration_inited = success; + return success; } bool configuration_save() { bool success = true; -#ifndef AO_DEACTIVATE_FLASH - for (auto container = configurationContainers.begin(); container != configurationContainers.end(); container++) { if (!(*container)->save()) { @@ -238,7 +223,6 @@ bool configuration_save() { } } -#endif //ndef AO_DEACTIVATE_FLASH return success; } diff --git a/src/ArduinoOcpp/Core/Configuration.h b/src/ArduinoOcpp/Core/Configuration.h index 4097bc1a..e7d9f911 100644 --- a/src/ArduinoOcpp/Core/Configuration.h +++ b/src/ArduinoOcpp/Core/Configuration.h @@ -8,11 +8,12 @@ #include #include #include +#include #include #include -#define CONFIGURATION_FN "/arduino-ocpp.cnf" +#define CONFIGURATION_FN (AO_FILENAME_PREFIX "/arduino-ocpp.cnf") #define CONFIGURATION_VOLATILE "/volatile" namespace ArduinoOcpp { @@ -28,10 +29,10 @@ std::vector>::iterator getConfigurationC namespace Ocpp16 { std::shared_ptr getConfiguration(const char *key); - std::shared_ptr>> getAllConfigurations(); + std::unique_ptr>> getAllConfigurations(); } -bool configuration_init(FilesystemOpt fsOpt = FilesystemOpt::Use_Mount_FormatOnFail); +bool configuration_init(std::shared_ptr filesytem); bool configuration_save(); } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp index 4d0a260d..b656144d 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp @@ -5,6 +5,8 @@ #include #include +#include + #if defined(ESP32) #define USE_FS LITTLEFS #else @@ -23,95 +25,85 @@ #define MAX_FILE_SIZE 4000 #define MAX_CONFIGURATIONS 50 +#define MAX_CONFJSON_CAPACITY 4000 namespace ArduinoOcpp { bool ConfigurationContainerFlash::load() { -#ifndef AO_DEACTIVATE_FLASH + + if (!filesystem) { + return false; + } if (configurations.size() > 0) { AO_DBG_ERR("Error: declared configurations before calling container->load(). " \ "All previously declared values won't be written back"); + (void)0; } - if (!USE_FS.exists(getFilename())) { + size_t file_size = 0; + if (filesystem->stat(getFilename(), &file_size) != 0 // file does not exist + || file_size == 0) { // file exists, but empty AO_DBG_DEBUG("Populate FS: create configuration file"); - return true; + return save(); } - File file = USE_FS.open(getFilename(), "r"); + if (file_size > MAX_FILE_SIZE) { + AO_DBG_ERR("Unable to initialize: filesize is too long"); + return false; + } + + auto file = filesystem->open(getFilename(), "r"); if (!file) { AO_DBG_ERR("Unable to initialize: could not open configuration file %s", getFilename()); return false; } - if (!file.available()) { - AO_DBG_DEBUG("Populate FS: create configuration file"); - file.close(); - return true; - } + auto jsonCapacity = std::max(file_size, (size_t) 256); + DynamicJsonDocument doc {0}; + DeserializationError err = DeserializationError::NoMemory; - int file_size = file.size(); + while (err == DeserializationError::NoMemory) { + if (jsonCapacity > MAX_CONFJSON_CAPACITY) { + AO_DBG_ERR("JSON capacity exceeded"); + return false; + } - if (file_size < 2) { - AO_DBG_ERR("Unable to initialize: too short for json"); - file.close(); - return false; - } else if (file_size > MAX_FILE_SIZE) { - AO_DBG_ERR("Unable to initialize: filesize is too long"); - file.close(); - return false; - } + AO_DBG_DEBUG("Configs JSON capacity: %zu", jsonCapacity); - String token = file.readStringUntil('\n'); - if (!token.equals("content-type:arduino-ocpp_configuration_file")) { - AO_DBG_ERR("Unable to initialize: unrecognized configuration file format"); - file.close(); - return false; + doc = DynamicJsonDocument(jsonCapacity); + ArduinoJsonFileAdapter file_adapt {file.get()}; + err = deserializeJson(doc, file_adapt); + + jsonCapacity *= 3; + jsonCapacity /= 2; + file->seek(0); } - token = file.readStringUntil('\n'); - if (!token.equals("version:1.0")) { - AO_DBG_ERR("Unable to initialize: unsupported version"); - file.close(); + if (err) { + AO_DBG_ERR("Unable to initialize: config file deserialization failed: %s", err.c_str()); return false; } - token = file.readStringUntil(':'); - if (!token.equals("configurations_len")) { - AO_DBG_ERR("Unable to initialize: missing length statement"); - file.close(); + JsonObject configHeader = doc["head"]; + + if (strcmp(configHeader["content-type"] | "Invalid", "ao_configuration_file")) { + AO_DBG_ERR("Unable to initialize: unrecognized configuration file format"); return false; } - token = file.readStringUntil('\n'); - int configurations_len = token.toInt(); - if (configurations_len <= 0) { - AO_DBG_ERR("Unable to initialize: empty configuration"); - file.close(); - return true; - } - if (configurations_len > MAX_CONFIGURATIONS) { - AO_DBG_ERR("Unable to initialize: configurations_len is too big"); - file.close(); + if (strcmp(configHeader["version"] | "Invalid", "1.1")) { + AO_DBG_ERR("Unable to initialize: unsupported version"); return false; } - - size_t jsonCapacity = file_size + JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(configurations_len) + configurations_len * JSON_OBJECT_SIZE(5); - - AO_DBG_DEBUG("Config capacity = %zu", jsonCapacity); - - DynamicJsonDocument configDoc(jsonCapacity); - - DeserializationError error = deserializeJson(configDoc, file); - if (error) { - AO_DBG_ERR("Unable to initialize: config file deserialization failed: %s", error.c_str()); - file.close(); + + JsonArray configurationsArray = doc["configurations"]; + if (configurationsArray.size() > MAX_CONFIGURATIONS) { + AO_DBG_ERR("Unable to initialize: configurations_len is too big (=%zu)", configurationsArray.size()); return false; } - JsonArray configurationsArray = configDoc["configurations"]; for (JsonObject config : configurationsArray) { const char *type = config["type"] | "Undefined"; @@ -132,49 +124,46 @@ bool ConfigurationContainerFlash::load() { } } - file.close(); - configurationsUpdated(); - AO_DBG_DEBUG("Initialization successful"); -#endif //ndef AO_DEACTIVATE_FLASH + AO_DBG_DEBUG("Initialization finished"); return true; } bool ConfigurationContainerFlash::save() { -#ifndef AO_DEACTIVATE_FLASH + + if (!filesystem) { + return false; + } if (!configurationsUpdated()) { return true; //nothing to be done } - if (USE_FS.exists(getFilename())) { - USE_FS.remove(getFilename()); + size_t file_size = 0; + if (filesystem->stat(getFilename(), &file_size) == 0) { + filesystem->remove(getFilename()); } - File file = USE_FS.open(getFilename(), "w"); - + auto file = filesystem->open(getFilename(), "w"); if (!file) { AO_DBG_ERR("Unable to save: could not open configuration file %s", getFilename()); return false; } - size_t jsonCapacity = JSON_OBJECT_SIZE(1); //configurations - - size_t numEntries = configurations.size(); - - file.print("content-type:arduino-ocpp_configuration_file\n"); - file.print("version:1.0\n"); - file.print("configurations_len:"); - file.print(numEntries, DEC); - file.print("\n"); + size_t jsonCapacity = 2 * JSON_OBJECT_SIZE(2); //head + configurations + head payload std::vector> entries; for (auto config = configurations.begin(); config != configurations.end(); config++) { std::shared_ptr entry = (*config)->toJsonStorageEntry(); - if (entry) + if (entry) { entries.push_back(entry); + } + if (entries.size() >= MAX_CONFIGURATIONS) { + AO_DBG_ERR("Max No of configratuions exceeded. Crop configs file (by FCFS)"); + break; + } } jsonCapacity += JSON_ARRAY_SIZE(entries.size()); //length of configurations @@ -182,26 +171,43 @@ bool ConfigurationContainerFlash::save() { jsonCapacity += (*entry)->capacity(); } - DynamicJsonDocument configDoc(jsonCapacity); + jsonCapacity = std::max(jsonCapacity, (size_t) 256); + DynamicJsonDocument doc {0}; + bool jsonDocOverflow = true; - JsonArray configurationsArray = configDoc.createNestedArray("configurations"); + while (jsonDocOverflow) { + if (jsonCapacity > MAX_CONFJSON_CAPACITY) { + AO_DBG_ERR("JSON capacity exceeded"); + return false; + } - for (auto entry = entries.begin(); entry != entries.end(); entry++) { - configurationsArray.add((*entry)->as()); - } + file->seek(0); - // Serialize JSON to file - if (serializeJson(configDoc, file) == 0) { - AO_DBG_ERR("Unable to save: Could not serialize JSON"); - file.close(); - return false; + doc = DynamicJsonDocument(jsonCapacity); + JsonObject head = doc.createNestedObject("head"); + head["content-type"] = "ao_configuration_file"; + head["version"] = "1.1"; + + JsonArray configurationsArray = doc.createNestedArray("configurations"); + for (auto entry = entries.begin(); entry != entries.end(); entry++) { + configurationsArray.add((*entry)->as()); + } + + ArduinoJsonFileAdapter file_adapt {file.get()}; + size_t written = serializeJson(doc, file_adapt); + + jsonCapacity *= 3; + jsonCapacity /= 2; + jsonDocOverflow = doc.overflowed(); + + if (!jsonDocOverflow && written < 20) { //plausibility check + AO_DBG_ERR("Config serialization: unkown error for file %s", getFilename()); + return false; + } } //success - file.close(); - AO_DBG_DEBUG("Saving configDoc successful"); - -#endif //ndef AO_DEACTIVATE_FLASH + AO_DBG_DEBUG("Saving configurations finished"); return true; } diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.h b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.h index e401ab94..865756de 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.h +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.h @@ -6,14 +6,15 @@ #define CONFIGURATIONCONTAINERFLASH_H #include +#include namespace ArduinoOcpp { class ConfigurationContainerFlash : public ConfigurationContainer { - - + std::shared_ptr filesystem; public: - ConfigurationContainerFlash(const char *filename) : ConfigurationContainer(filename) { } + ConfigurationContainerFlash(std::shared_ptr filesystem, const char *filename) : + ConfigurationContainer(filename), filesystem(filesystem) { } ~ConfigurationContainerFlash() = default; diff --git a/src/ArduinoOcpp/Core/FilesystemAdapter.cpp b/src/ArduinoOcpp/Core/FilesystemAdapter.cpp new file mode 100644 index 00000000..d35789be --- /dev/null +++ b/src/ArduinoOcpp/Core/FilesystemAdapter.cpp @@ -0,0 +1,274 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#include +#include //FilesystemOpt +#include + +//#ifndef AO_DEACTIVATE_FLASH +#if 1 + +//Set default parameters; assume usage with Arduino if no build flags are present +#ifndef AO_USE_FILEAPI +#if defined(ESP32) +#define AO_USE_FILEAPI ARDUINO_LITTLEFS +#else +#define AO_USE_FILEAPI ARDUINO_SPIFFS +#endif +#endif //ndef AO_USE_FILEAPI + +/* + * Platform specific implementations. Currently supported: + * - Arduino LittleFs + * - Arduino SPIFFS + * - ESP-IDF SPIFFS + * + * You can add support for any file system by passing custom adapters to the initialize + * function of ArduinoOcpp + */ + +#if AO_USE_FILEAPI == ARDUINO_LITTLEFS +#include +#define USE_FS LITTLEFS +#elif AO_USE_FILEAPI == ARDUINO_SPIFFS +#include +#define USE_FS SPIFFS +#elif AO_USE_FILEAPI == ESPIDF_SPIFFS +#include +#include "esp_spiffs.h" +#endif + + +#if AO_USE_FILEAPI == ARDUINO_LITTLEFS || AO_USE_FILEAPI == ARDUINO_SPIFFS + +namespace ArduinoOcpp { +namespace EspWiFi { + +class ArduinoFileAdapter : public FileAdapter { + File file; +public: + ArduinoFileAdapter(File&& file) : file(file) {} + + ~ArduinoFileAdapter() { + if (file) { + file.close(); + } + } + + int read() override; + size_t read(char *buf, size_t len) override; + size_t write(const char *buf, size_t len) override; + size_t seek(size_t offset) override; +}; + +class ArduinoFilesystemAdapter : public FilesystemAdapter { +private: + bool valid = false; + FilesystemOpt config; +public: + ArduinoFilesystemAdapter(FilesystemOpt config) : config(config) { + valid = true; + + if (config.mustMount()) { +#if AO_USE_FILEAPI == ARDUINO_LITTLEFS + if(!USE_FS.begin(config.formatOnFail())) { + AO_DBG_ERR("Error while mounting LITTLEFS"); + valid = false; + } +#elif AO_USE_FILEAPI == ARDUINO_SPIFFS + //ESP8266 + SPIFFSConfig cfg; + cfg.setAutoFormat(config.formatOnFail()); + SPIFFS.setConfig(cfg); + + if (!SPIFFS.begin()) { + AO_DBG_ERR("Unable to initialize: unable to mount SPIFFS"); + valid = false; + } +#else +#error +#endif + } //end if mustMount() + } + + ~ArduinoFilesystemAdapter() { + if (config.mustMount()) { + USE_FS.end(); + } + } + + operator bool() {return valid;} + + int stat(const char *path, size_t *size) override { + if (!USE_FS.exists(path)) { + return -1; + } + File f = USE_FS.open(path, "r"); + if (!f) { + return -1; + } + + int status = -1; + if (f.isFile()) { + size = f.size(); + status = 0; + } else { + //fetch more information for directory when ArduinoOcpp also uses them + //status = 0; + } + + f.close(); + return status; + } + + std::unique_ptr open(const char *fn, const char *mode) override { + File file = USE_FS.open(fn, mode); + if (file && file.isFile()) { + return std::unique_ptr(new ArduinoFileAdapter(std::move(file))); + } else { + return nullptr; + } + } + bool remove(const char *fn) override { + return USE_FS.remove(fn); + }; +}; + +std::unique_ptr makeDefaultFilesystemAdapter(FilesystemOpt config) { + + if (!config.accessAllowed()) { + AO_DBG_DEBUG("Access to Arduino FS not allowed by config"); + return nullptr; + } + + auto fs = std::unique_ptr( + new ArduinoFilesystemAdapter(config) + ); + + if (*fs) { + return fs; + } else { + return nullptr; + } +} + +} //end namespace EspWiFi +} //end namespace ArduinoOcpp + +#elif AO_USE_FILEAPI == ESPIDF_SPIFFS + +namespace ArduinoOcpp { +namespace EspWiFi { + +class EspIdfFileAdapter : public FileAdapter { + FILE *file {nullptr}; +public: + EspIdfFileAdapter(FILE *file) : file(file) {} + + ~EspIdfFileAdapter() { + fclose(file); + } + + size_t read(char *buf, size_t len) override { + return fread(buf, 1, len, file); + } + + size_t write(const char *buf, size_t len) override { + return fwrite(buf, 1, len, file); + } + + size_t seek(size_t offset) override { + return fseek(file, offset, SEEK_SET); + } + + int read() { + return fgetc(file); + } +}; + +class EspIdfFilesystemAdapter : public FilesystemAdapter { +public: + FilesystemOpt config; +public: + EspIdfFilesystemAdapter(FilesystemOpt config) : config(config) { } + + ~EspIdfFilesystemAdapter() { + if (config.mustMount()) { + esp_vfs_spiffs_unregister("ao"); //partition label + AO_DBG_DEBUG("SPIFFS unmounted"); + } + } + + int stat(const char *path, size_t *size) override { + struct ::stat st; + auto ret = ::stat(path, &st); + if (ret == 0) { + *size = st.st_size; + } + return ret; + } + + std::unique_ptr open(const char *fn, const char *mode) override { + auto file = fopen(fn, mode); + if (file) { + return std::unique_ptr(new EspIdfFileAdapter(std::move(file))); + } else { + AO_DBG_DEBUG("Failed to open file path %s", fn); + return nullptr; + } + } + + bool remove(const char *fn) override { + return unlink(fn) == 0; + } +}; + +std::unique_ptr makeDefaultFilesystemAdapter(FilesystemOpt config) { + + if (!config.accessAllowed()) { + AO_DBG_DEBUG("Access to ESP-IDF SPIFFS not allowed by config"); + return nullptr; + } + + bool mounted = true; + + if (config.mustMount()) { + mounted = false; + + esp_vfs_spiffs_conf_t conf = { + .base_path = AO_FILENAME_PREFIX, + .partition_label = "ao", //also see deconstructor + .max_files = 5, + .format_if_mount_failed = config.formatOnFail() + }; + + esp_err_t ret = esp_vfs_spiffs_register(&conf); + + if (ret == ESP_OK) { + mounted = true; + AO_DBG_DEBUG("SPIFFS mounted"); + } else { + if (ret == ESP_FAIL) { + AO_DBG_ERR("Failed to mount or format filesystem"); + } else if (ret == ESP_ERR_NOT_FOUND) { + AO_DBG_ERR("Failed to find SPIFFS partition"); + } else { + AO_DBG_ERR("Failed to initialize SPIFFS (%s)", esp_err_to_name(ret)); + } + } + } + + if (mounted) { + return std::unique_ptr(new EspIdfFilesystemAdapter(config)); + } else { + return nullptr; + } +} + +} //end namespace EspWiFi +} //end namespace ArduinoOcpp + +#endif + +#endif diff --git a/src/ArduinoOcpp/Core/FilesystemAdapter.h b/src/ArduinoOcpp/Core/FilesystemAdapter.h new file mode 100644 index 00000000..e32f7779 --- /dev/null +++ b/src/ArduinoOcpp/Core/FilesystemAdapter.h @@ -0,0 +1,103 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef AO_FILESYSTEMADAPTER_H +#define AO_FILESYSTEMADAPTER_H + +#ifndef AO_FILENAME_PREFIX +#define AO_FILENAME_PREFIX "/ao_store" +#endif + +#define ARDUINO_LITTLEFS 1 +#define ARDUINO_SPIFFS 2 +#define ESPIDF_SPIFFS 3 + +#include + +namespace ArduinoOcpp { + +class FileAdapter { +public: + virtual ~FileAdapter() = default; + virtual size_t read(char *buf, size_t len) = 0; + virtual size_t write(const char *buf, size_t len) = 0; + virtual size_t seek(size_t offset) = 0; + // virtual void close() = 0; implemented in deconstructor + + virtual int read() = 0; +}; + +class ArduinoJsonFileAdapter { +private: + FileAdapter *file; +public: + ArduinoJsonFileAdapter(FileAdapter *file) : file(file) { } + + size_t readBytes(char *buf, size_t len) { + return file->read(buf, len); + } + + int read() { + return file->read(); + } + + size_t write(const uint8_t *buf, size_t len) { + return file->write((const char*) buf, len); + } + + size_t write(uint8_t c) { + return file->write((const char*) &c, 1); + } +}; + +class FilesystemAdapter { +public: + virtual ~FilesystemAdapter() = default; + virtual int stat(const char *path, size_t *size) = 0; + virtual std::unique_ptr open(const char *fn, const char *mode) = 0; + virtual bool remove(const char *fn) = 0; +}; + +} //end namespace ArduinoOcpp + +//#ifndef AO_DEACTIVATE_FLASH +#if 1 + +//Set default parameters; assume usage with Arduino if no build flags are present +#ifndef AO_USE_FILEAPI +#if defined(ESP32) +#define AO_USE_FILEAPI ARDUINO_LITTLEFS +#else +#define AO_USE_FILEAPI ARDUINO_SPIFFS +#endif +#endif //ndef AO_USE_FILEAPI + +/* + * Platform specific implementations. Currently supported: + * - Arduino LittleFs + * - Arduino SPIFFS + * - ESP-IDF SPIFFS + * + * You can add support for any file system by passing custom adapters to the initialize + * function of ArduinoOcpp + */ + +#if AO_USE_FILEAPI == ARDUINO_LITTLEFS || \ + AO_USE_FILEAPI == ARDUINO_SPIFFS || \ + AO_USE_FILEAPI == ESPIDF_SPIFFS + +#include + +namespace ArduinoOcpp { +namespace EspWiFi { + +std::unique_ptr makeDefaultFilesystemAdapter(FilesystemOpt config); + +} //end namespace EspWiFi +} //end namespace ArduinoOcpp +#endif + +#endif //ndef AO_DEACTIVATE_FLASH + +#endif diff --git a/src/ArduinoOcpp/Core/OcppConnection.cpp b/src/ArduinoOcpp/Core/OcppConnection.cpp index 15c10ce3..310f5d0c 100644 --- a/src/ArduinoOcpp/Core/OcppConnection.cpp +++ b/src/ArduinoOcpp/Core/OcppConnection.cpp @@ -112,8 +112,8 @@ bool OcppConnection::processOcppSocketInputTXT(const char* payload, size_t lengt doc = std::unique_ptr(new DynamicJsonDocument(capacity)); err = deserializeJson(*doc, payload, length); - capacity /= 2; capacity *= 3; + capacity /= 2; } //TODO insert validateRpcHeader at suitable position diff --git a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp index ccd4c57f..3989137a 100644 --- a/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp @@ -26,13 +26,15 @@ void GetConfiguration::processReq(JsonObject payload) { std::unique_ptr GetConfiguration::createConf(){ - std::shared_ptr>> configurationKeys; + std::unique_ptr>> configurationKeys; std::vector unknownKeys; if (keys.size() == 0){ //return all existing keys configurationKeys = getAllConfigurations(); } else { //only return keys that were searched using the "key" parameter - configurationKeys = std::make_shared>>(); + configurationKeys = std::unique_ptr>>( + new std::vector>() + ); for (size_t i = 0; i < keys.size(); i++) { std::shared_ptr entry = getConfiguration(keys.at(i).c_str()); if (entry) From 6c02f43bd50ca59149c6de8f2a0bfeffa6c38d91 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 1 Jun 2022 10:07:54 +0200 Subject: [PATCH 163/696] Add loopback OCPP socket --- src/ArduinoOcpp/Core/OcppSocket.h | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/ArduinoOcpp/Core/OcppSocket.h b/src/ArduinoOcpp/Core/OcppSocket.h index f0c9c087..f1fb6db7 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.h +++ b/src/ArduinoOcpp/Core/OcppSocket.h @@ -24,6 +24,23 @@ class OcppSocket { virtual void setReceiveTXTcallback(ReceiveTXTcallback &receiveTXT) = 0; //ReceiveTXTcallback is defined in OcppServer.h }; +class OcppEchoSocket : public OcppSocket { +private: + ReceiveTXTcallback receiveTXT; +public: + void loop() override { } + bool sendTXT(std::string &out) override { + if (receiveTXT) { + return receiveTXT(out.c_str(), out.length()); + } else { + return false; + } + } + void setReceiveTXTcallback(ReceiveTXTcallback &receiveTXT) override { + this->receiveTXT = receiveTXT; + } +}; + } //end namespace ArduinoOcpp #ifndef AO_CUSTOM_WS @@ -36,10 +53,8 @@ namespace EspWiFi { class OcppClientSocket : public OcppSocket { private: - //std::shared_ptr wsock; WebSocketsClient *wsock; public: - //OcppClientSocket(ReceiveTXTcallback &receiveTXT, std::shared_ptr wsock); OcppClientSocket(WebSocketsClient *wsock); void loop(); From a1bcd53f84096af4747bef5274f0331d31023b57 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 1 Jun 2022 10:20:37 +0200 Subject: [PATCH 164/696] OCMF support, connector lock integration --- src/ArduinoOcpp.cpp | 26 +++ src/ArduinoOcpp.h | 21 ++- .../MessagesV16/StartTransaction.cpp | 3 +- .../ChargePointStatus/ConnectorStatus.cpp | 177 +++++++++++++----- .../Tasks/ChargePointStatus/ConnectorStatus.h | 27 ++- .../TransactionPrerequisites.h | 27 +++ 6 files changed, 227 insertions(+), 54 deletions(-) create mode 100644 src/ArduinoOcpp/Tasks/ChargePointStatus/TransactionPrerequisites.h diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index d8ffe4d1..f488c258 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -293,6 +293,32 @@ void setOnUnlockConnector(std::function()> unlockConnector) { connector->setOnUnlockConnector(unlockConnector); } +void setConnectorLock(std::function lockConnector) { + if (!ocppEngine) { + AO_DBG_ERR("Please call OCPP_initialize before"); + return; + } + auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (!connector) { + AO_DBG_ERR("Could not find connector. Ignore"); + return; + } + connector->setConnectorLock(lockConnector); +} + +void setTxBasedMeterUpdate(std::function updateTxState) { + if (!ocppEngine) { + AO_DBG_ERR("Please call OCPP_initialize before"); + return; + } + auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); + if (!connector) { + AO_DBG_ERR("Could not find connector. Ignore"); + return; + } + connector->setTxBasedMeterUpdate(updateTxState); +} + void setOnSetChargingProfileRequest(OnReceiveReqListener onReceiveReq) { setOnSetChargingProfileRequestListener(onReceiveReq); } diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 15b3ee91..c22b2088 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -15,6 +15,7 @@ #include #include #include +#include #include using ArduinoOcpp::OnReceiveConfListener; @@ -77,7 +78,25 @@ void addConnectorErrorCodeSampler(std::function connectorErrorCo void setOnChargingRateLimitChange(std::function chargingRateChanged); -void setOnUnlockConnector(std::function()> unlockConnector); //true: success, false: failure +//Set a Cb to mechanically unlock the connector. Called for the OCPP operation "UnlockConnector" +//Return values: true on success, false on failure, PollResult::Await if not known yet +//Continues to call the Cb as long as it returns PollResult::Await +void setOnUnlockConnector(std::function()> unlockConnector); + +//Set a Cb for setting the state of the connector lock. Called in the course of normal transactions +//Return values: - TxEnableState::Active if connector is locked and ready for transaction +// - TxEnableState::Inactive if connector lock is released +// - TxEnableState::Pending otherwise, e.g. if transitioning between the states +//Called periodically +void setConnectorLock(std::function lockConnector); + +//Set a Cb to update transaction-based energy measurements with the most recent transaction state. +//This allows energy meters (e.g. based on OCMF) to take their measruements right before and after a transaction +//Return values: - TxEnableState::Active if the energy meter confirmed to be in the transaction-state +// - TxEnableState::Inactive if the energy meter has transitioned into a non-transaction-state +// - TxEnableState::Pending otherwise, e.g. if transitioning between the states +//Called periodically +void setTxBasedMeterUpdate(std::function updateTxState); /* * React on CS-initiated operations diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp index f7f3ec57..bee6d6cf 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp @@ -136,7 +136,8 @@ std::unique_ptr StartTransaction::createConf() { JsonObject idTagInfo = payload.createNestedObject("idTagInfo"); idTagInfo["status"] = "Accepted"; - payload["transactionId"] = 123456; //sample data for debug purpose + static int uniqueTxId = 1000; + payload["transactionId"] = uniqueTxId++; //sample data for debug purpose return doc; } diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 185ff4b5..eb90a08f 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -37,6 +37,8 @@ ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) minimumStatusDuration = declareConfiguration("MinimumStatusDuration", 0, CONFIGURATION_FN, true, true, true, false); stopTransactionOnInvalidId = declareConfiguration("StopTransactionOnInvalidId", "true", CONFIGURATION_FN, true, true, false, false); stopTransactionOnEVSideDisconnect = declareConfiguration("StopTransactionOnEVSideDisconnect", "true", CONFIGURATION_FN, true, true, false, false); + localAuthorizeOffline = declareConfiguration("LocalAuthorizeOffline", "false", CONFIGURATION_FN, true, true, false, false); + localPreAuthorize = declareConfiguration("LocalPreAuthorize", "false", CONFIGURATION_FN, true, true, false, false); if (!sIdTag || !transactionId || !availability) { AO_DBG_ERR("Cannot declare sessionIdTag, transactionId or availability"); @@ -49,6 +51,29 @@ ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) AO_DBG_DEBUG("Load session idTag at initialization"); } transactionIdSync = *transactionId; + + /* + * Initialize standard EVSE behavior. + * By default, transactions are triggered by a valid IdTag (+ connected plug as soon as set) + * The default necessary steps before starting a transaction are + * - lock the connector (if handler is set) + * - instruct the OCMF meter to begin a transaction (if OCMF meter handler is set) + */ + txTriggerConditions.push_back([this] () -> TxCondition { + return getSessionIdTag() == nullptr ? TxCondition::Inactive : TxCondition::Active; + }); + txEnableSequence.push_back([this] (TxCondition cond) -> TxEnableState { + if (onOcmfMeterPollTx) { + return onOcmfMeterPollTx(cond); + } + return cond == TxCondition::Active ? TxEnableState::Active : TxEnableState::Inactive; + }); + txEnableSequence.push_back([this] (TxCondition cond) -> TxEnableState { + if (onConnectorLockPollTx) { + return onConnectorLockPollTx(cond); + } + return cond == TxCondition::Active ? TxEnableState::Active : TxEnableState::Inactive; + }); } OcppEvseState ConnectorStatus::inferenceStatus() { @@ -65,31 +90,16 @@ OcppEvseState ConnectorStatus::inferenceStatus() { } } -// auto cpStatusService = context.getChargePointStatusService(); -// -// if (!authorized && !getChargePointStatusService()->existsUnboundAuthorization()) { -// return OcppEvseState::Available; -// } else if (((int) *transactionId) < 0) { -// return OcppEvseState::Preparing; - //if (connectorFaultedSampler != nullptr && connectorFaultedSampler()) { if (getErrorCode() != nullptr) { return OcppEvseState::Faulted; } else if (*availability == AVAILABILITY_INOPERATIVE) { return OcppEvseState::Unavailable; - } else if (!session && - getTransactionId() < 0 && - (connectorPluggedSampler == nullptr || !connectorPluggedSampler()) ) { - return OcppEvseState::Available; - } else if (getTransactionId() <= 0) { - if (connectorPluggedSampler != nullptr && connectorPluggedSampler() && - (currentStatus == OcppEvseState::Finishing || - currentStatus == OcppEvseState::Charging || - currentStatus == OcppEvseState::SuspendedEV || - currentStatus == OcppEvseState::SuspendedEVSE)) { - return OcppEvseState::Finishing; - } - return OcppEvseState::Preparing; - } else { + } else if (getTransactionId() == 0 && // i.e. Tx pending or EVSE offline. Check if offline Tx is OFF + !(*localAuthorizeOffline && strcmp(*localAuthorizeOffline, "false")) && + !(*localPreAuthorize && strcmp(*localPreAuthorize, "false"))) { + //All modes for offline Tx are off + return OcppEvseState::Preparing; //see other Preparing case + } else if (getTransactionId() >= 0) { //Transaction is currently running if ((connectorEnergizedSampler && !connectorEnergizedSampler()) || idTagInvalidated) { @@ -99,7 +109,32 @@ OcppEvseState ConnectorStatus::inferenceStatus() { return OcppEvseState::SuspendedEV; } return OcppEvseState::Charging; + } else if (txEnable == TxEnableState::Inactive) { + return OcppEvseState::Available; + } else if (txEnable == TxEnableState::Pending || + txEnable == TxEnableState::Active) { //reached if Tx init is delayed + + if (txEnable == TxEnableState::Active) { // TODO verify if actually possible + AO_DBG_VERBOSE("Infered Active"); // + (void)0; // + } // + + /* + * Either in Preparing or Finishing state. Only way to know is from previous state + */ + const auto previous = currentStatus; + if (previous == OcppEvseState::Finishing || + previous == OcppEvseState::Charging || + previous == OcppEvseState::SuspendedEV || + previous == OcppEvseState::SuspendedEVSE) { + return OcppEvseState::Finishing; + } else { + return OcppEvseState::Preparing; + } } + + AO_DBG_VERBOSE("Cannot infere status"); + return OcppEvseState::Faulted; //internal error } bool ConnectorStatus::ocppPermitsCharge() { @@ -120,7 +155,7 @@ bool ConnectorStatus::ocppPermitsCharge() { } OcppMessage *ConnectorStatus::loop() { - if (getTransactionId() <= 0 && *availability == AVAILABILITY_INOPERATIVE_SCHEDULED) { + if (getTransactionId() < 0 && *availability == AVAILABILITY_INOPERATIVE_SCHEDULED) { *availability = AVAILABILITY_INOPERATIVE; saveState(); } @@ -133,29 +168,72 @@ OcppMessage *ConnectorStatus::loop() { } } + auto txTrigger = txTriggerConditions.empty() ? TxCondition::Inactive : TxCondition::Active; + txEnable = TxEnableState::Inactive; + + if (*availability == AVAILABILITY_INOPERATIVE) { + txTrigger = TxCondition::Inactive; + } + + if (txTrigger == TxCondition::Active) { + for (auto trigger = txTriggerConditions.begin(); trigger != txTriggerConditions.end(); trigger++) { + auto result = trigger->operator()(); + if (result == TxCondition::Active) { + txEnable = TxEnableState::Pending; + } else { + txTrigger = TxCondition::Inactive; + } + } + } + + if (txTrigger == TxCondition::Active) { + txEnable = TxEnableState::Active; + + for (auto step = txEnableSequence.rbegin(); step != txEnableSequence.rend(); step++) { + auto result = step->operator()(TxCondition::Active); + if (result != TxEnableState::Active) { + txEnable = TxEnableState::Pending; + break; + } + } + } else { + for (auto step = txEnableSequence.begin(); step != txEnableSequence.end(); step++) { + auto result = step->operator()(TxCondition::Inactive); + if (result != TxEnableState::Inactive) { + txEnable = TxEnableState::Pending; + break; + } + } + } + + /* * Check conditions for start or stop transaction */ - if (connectorPluggedSampler) { //only supported with connectorPluggedSampler + if (txEnable == TxEnableState::Active) { + //check if not in transaction yet + if (getTransactionId() < 0 && + !getErrorCode()) { + //start Transaction + + AO_DBG_DEBUG("Session mngt: txId=%i, connectorPlugged = %s, session=%d", + getTransactionId(), + connectorPluggedSampler ? (connectorPluggedSampler() ? "plugged" : "unplugged") : "undefined", + session); + AO_DBG_INFO("Session mngt: trigger StartTransaction"); + return new StartTransaction(connectorId); + } + } else { + //check if still in transaction if (getTransactionId() >= 0) { - //check condition for StopTransaction - if (!session) { - AO_DBG_DEBUG("Session mngt: txId=%i, connectorPlugged=%d, session=%d", - getTransactionId(), connectorPluggedSampler(), session); - AO_DBG_INFO("Session mngt: trigger StopTransaction"); - return new StopTransaction(connectorId, endReason[0] != '\0' ? endReason : nullptr); - } - } else { - //check condition for StartTransaction - if (connectorPluggedSampler() && - session && - !getErrorCode() && - *availability == AVAILABILITY_OPERATIVE) { - AO_DBG_DEBUG("Session mngt: txId=%i, connectorPlugged=%d, session=%d", - getTransactionId(), connectorPluggedSampler(), session); - AO_DBG_INFO("Session mngt: trigger StartTransaction"); - return new StartTransaction(connectorId); - } + //stop transaction + + AO_DBG_DEBUG("Session mngt: txId=%i, connectorPlugged = %s, session=%d", + getTransactionId(), + connectorPluggedSampler ? (connectorPluggedSampler() ? "plugged" : "unplugged") : "undefined", + session); + AO_DBG_INFO("Session mngt: trigger StopTransaction"); + return new StopTransaction(connectorId, endReason[0] != '\0' ? endReason : nullptr); } } @@ -172,10 +250,10 @@ OcppMessage *ConnectorStatus::loop() { } } - auto inferencedStatus = inferenceStatus(); + auto inferedStatus = inferenceStatus(); - if (inferencedStatus != currentStatus) { - currentStatus = inferencedStatus; + if (inferedStatus != currentStatus) { + currentStatus = inferedStatus; t_statusTransition = ao_tick_ms(); AO_DBG_DEBUG("Status changed%s", *minimumStatusDuration ? ", will report delayed" : ""); } @@ -297,6 +375,9 @@ void ConnectorStatus::setAvailability(bool available) { void ConnectorStatus::setConnectorPluggedSampler(std::function connectorPlugged) { this->connectorPluggedSampler = connectorPlugged; + txTriggerConditions.push_back([this] () -> TxCondition { + return connectorPluggedSampler() ? TxCondition::Active : TxCondition::Inactive; + }); } void ConnectorStatus::setEvRequestsEnergySampler(std::function evRequestsEnergy) { @@ -322,3 +403,11 @@ void ConnectorStatus::setOnUnlockConnector(std::function()> unl std::function()> ConnectorStatus::getOnUnlockConnector() { return this->onUnlockConnector; } + +void ConnectorStatus::setConnectorLock(std::function onConnectorLockPollTx) { + this->onConnectorLockPollTx = onConnectorLockPollTx; +} + +void ConnectorStatus::setTxBasedMeterUpdate(std::function onOcmfMeterPollTx) { + this->onOcmfMeterPollTx = onOcmfMeterPollTx; +} diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index 8c9a8962..940e1d35 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -2,10 +2,11 @@ // Copyright Matthias Akstaller 2019 - 2022 // MIT License -#ifndef CONNECTOR_STATUS -#define CONNECTOR_STATUS +#ifndef CONNECTORSTATUS_H +#define CONNECTORSTATUS_H #include +#include #include #include #include @@ -43,9 +44,9 @@ class ConnectorStatus { bool connectionTimeOutListen {false}; ulong connectionTimeOutTimestamp {0}; //in milliseconds - std::function connectorPluggedSampler {nullptr}; - std::function evRequestsEnergySampler {nullptr}; - std::function connectorEnergizedSampler {nullptr}; + std::function connectorPluggedSampler; + std::function evRequestsEnergySampler; + std::function connectorEnergizedSampler; std::vector> connectorErrorCodeSamplers; const char *getErrorCode(); @@ -54,13 +55,20 @@ class ConnectorStatus { OcppEvseState reportedStatus = OcppEvseState::NOT_SET; ulong t_statusTransition = 0; - //std::function()> startTransactionBehavior; - //std::function(const char* stopReason)> stopTransactionBehavior; + std::function()> onUnlockConnector; - std::function()> onUnlockConnector {nullptr}; + std::function onConnectorLockPollTx; + std::function onOcmfMeterPollTx; + + std::vector> txTriggerConditions; + std::vector> txEnableSequence; + TxEnableState txEnable {TxEnableState::Inactive}; // = Result of Trigger and Enable Sequence std::shared_ptr> stopTransactionOnInvalidId; std::shared_ptr> stopTransactionOnEVSideDisconnect; + std::shared_ptr> unlockConnectorOnEVSideDisconnect; + std::shared_ptr> localAuthorizeOffline; + std::shared_ptr> localPreAuthorize; public: ConnectorStatus(OcppModel& context, int connectorId); @@ -103,6 +111,9 @@ class ConnectorStatus { void setOnUnlockConnector(std::function()> unlockConnector); std::function()> getOnUnlockConnector(); + + void setConnectorLock(std::function lockConnector); + void setTxBasedMeterUpdate(std::function updateTxBasedMeter); }; } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/TransactionPrerequisites.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/TransactionPrerequisites.h new file mode 100644 index 00000000..d73b3310 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/TransactionPrerequisites.h @@ -0,0 +1,27 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef TXPREREQUISITES_H +#define TXPREREQUISITES_H + +namespace ArduinoOcpp { + +/* + * Type definitions for extending the transaction initiation process + */ + +enum class TxCondition { + Active, + Inactive +}; + +enum class TxEnableState { + Active, + Inactive, + Pending +}; + +} + +#endif From 322a7c85d16dac2e4f5bc9348b6f2f1adbb69217 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 7 Jun 2022 17:41:23 +0200 Subject: [PATCH 165/696] add energy and power meter cb --- src/ArduinoOcpp_c.cpp | 18 ++++++++++++++++++ src/ArduinoOcpp_c.h | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/ArduinoOcpp_c.cpp b/src/ArduinoOcpp_c.cpp index b44dceb4..7a027a88 100644 --- a/src/ArduinoOcpp_c.cpp +++ b/src/ArduinoOcpp_c.cpp @@ -63,6 +63,24 @@ std::function adaptCb(SamplerString cb) { return cb; } +std::function adaptCb(SamplerFloat cb) { + return cb; +} + +std::function adaptCb(SamplerInt cb) { + return cb; +} + +void ao_setPowerActiveImportSampler(SamplerFloat power) { + setPowerActiveImportSampler(adaptCb(power)); +} + +void ao_setEnergyActiveImportSampler(SamplerInt energy) { + setEnergyActiveImportSampler([energy] () -> float { + return (float) energy(); + }); +} + void ao_setEvRequestsEnergySampler(SamplerBool evRequestsEnergy) { setEvRequestsEnergySampler(adaptCb(evRequestsEnergy)); } diff --git a/src/ArduinoOcpp_c.h b/src/ArduinoOcpp_c.h index b36af90a..568cfa7a 100644 --- a/src/ArduinoOcpp_c.h +++ b/src/ArduinoOcpp_c.h @@ -34,7 +34,7 @@ void ao_set_console_out_c(void (*console_out)(const char *msg)); void ao_setPowerActiveImportSampler(SamplerFloat power); -void ao_setEnergyActiveImportSampler(SamplerFloat energy); +void ao_setEnergyActiveImportSampler(SamplerInt energy); void ao_setEvRequestsEnergySampler(SamplerBool evRequestsEnergy); From b01683658c82c0e3093a520ba83953b36ea7f9df Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 8 Jun 2022 10:25:15 +0200 Subject: [PATCH 166/696] explicit custom updater --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0307395..862c2398 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,7 @@ idf_component_register(SRCS ${AO_SRC} target_compile_options(${COMPONENT_TARGET} PUBLIC -DAO_CUSTOM_WS -DAO_CUSTOM_CONSOLE + -DAO_CUSTOM_UPDATER -DAO_DEACTIVATE_FLASH -DAO_USE_FILEAPI=ESPIDF_SPIFFS -DAO_DBG_LEVEL=AO_DL_DEBUG From 597d04ac7fa707f7f42b2e551661626ad1345fc9 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 17 Jun 2022 14:44:24 +0200 Subject: [PATCH 167/696] BootNotification with all fields --- src/ArduinoOcpp_c.cpp | 11 +++++++++++ src/ArduinoOcpp_c.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/ArduinoOcpp_c.cpp b/src/ArduinoOcpp_c.cpp index 7a027a88..855689c0 100644 --- a/src/ArduinoOcpp_c.cpp +++ b/src/ArduinoOcpp_c.cpp @@ -129,6 +129,17 @@ extern "C" void ao_bootNotification(const char *chargePointModel, const char *ch bootNotification("model", "vendor", adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); } +void ao_bootNotification_full(const char *payloadJson, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { + DynamicJsonDocument *payload = new DynamicJsonDocument(JSON_OBJECT_SIZE(9) + 230 + 9); // BootNotification has at most 9 attributes with at most 230 chars + null terminators + auto err = deserializeJson(*payload, payloadJson); + if (err) { + AO_DBG_ERR("Could not process input: %s", err.c_str()); + (void)0; + } + + bootNotification(payload, adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); +} + void ao_authorize(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { authorize(idTag, adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); } diff --git a/src/ArduinoOcpp_c.h b/src/ArduinoOcpp_c.h index 568cfa7a..16e45b39 100644 --- a/src/ArduinoOcpp_c.h +++ b/src/ArduinoOcpp_c.h @@ -70,6 +70,8 @@ void ao_onResetRequest(OnOcppMessage onRequest); //alternative: start reset time void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); +void ao_bootNotification_full(const char *payloadJson, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); + void ao_authorize(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); void ao_startTransaction(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); From eca45824a47c7a5513b2104e06d3720adcf70c3a Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 18 Jun 2022 11:53:27 +0200 Subject: [PATCH 168/696] update examples --- examples/ESP-TLS/main.cpp | 2 +- examples/SECC/README.md | 2 +- examples/SECC/main.cpp | 116 ++++++++++++++++++++------------------ 3 files changed, 62 insertions(+), 58 deletions(-) diff --git a/examples/ESP-TLS/main.cpp b/examples/ESP-TLS/main.cpp index 5e9be62e..0bb11e53 100644 --- a/examples/ESP-TLS/main.cpp +++ b/examples/ESP-TLS/main.cpp @@ -87,8 +87,8 @@ void setup() { #elif defined(ESP32) WiFi.begin(STASSID, STAPSK); while (!WiFi.isConnected()) { - Serial.print('.'); delay(1000); + Serial.print('.'); } #endif diff --git a/examples/SECC/README.md b/examples/SECC/README.md index 4d9eeaa5..e1f7a7b5 100644 --- a/examples/SECC/README.md +++ b/examples/SECC/README.md @@ -15,7 +15,7 @@ You can find the interface descriptions in `main.ino`. Please be aware that alth - Copy the `main.ino` from this example folder into your source directory. Adapt the pinout settings if needed. - Finished. You should be able to compile and upload the sketch onto your ESP. -When booting, the ESP opens up the Wi-Fi configuration portal. Please connect your PC to the network with the SSID `EVSE_Maintenance_Portal` within the first 30s of the boot routine of the ESP (the portal has a timeout). The passphrase is `myEvseController`. +When booting, the ESP opens up the Wi-Fi configuration portal. Please connect your PC to the network with the SSID `EVSE-Config` within the first 30s of the boot routine of the ESP (the portal has a timeout). The passphrase is `evse1234`. ## Standalone mode diff --git a/examples/SECC/main.cpp b/examples/SECC/main.cpp index 40160cab..446dc6fa 100644 --- a/examples/SECC/main.cpp +++ b/examples/SECC/main.cpp @@ -74,11 +74,13 @@ #define CHARGE_PERMISSION_OFF HIGH #endif -#if DEBUG_OUT +#if !defined(SECC_NO_DEBUG) #define PRINT(...) Serial.print(__VA_ARGS__) +#define PRINTF(...) Serial.printf(__VA_ARGS__) #define PRINTLN(...) Serial.println(__VA_ARGS__) #else #define PRINT(...) +#define PRINTF(...) #define PRINTLN(...) #endif @@ -92,14 +94,15 @@ ulong scheduleReboot = 0; //0 = no reboot scheduled; otherwise reboot scheduled ulong reboot_timestamp = 0; //timestamp of the triggering event; if scheduleReboot=0, the timestamp has no meaning // ============ CAPTIVE PORTAL -#define CAPTIVE_PORTAL_TIMEOUT 30000 +#define CAPTIVE_PORTAL_TIMEOUT 60 //in seconds +#define WIFI_CONNECTION_TIMEOUT 30 //in seconds struct Ocpp_URL { bool isTLS = true; String host = String('\0'); uint16_t port = 443; String url = String('\0'); - bool parse(String &url); + bool parse(String& url); }; Ocpp_URL ocppUrlParsed = Ocpp_URL(); @@ -113,7 +116,9 @@ void setup() { * Initialize peripherals */ Serial.begin(115200); +#if !defined(SECC_NO_DEBUG) Serial.setDebugOutput(true); +#endif pinMode(EV_PLUG_PIN, INPUT); pinMode(EV_CHARGE_PIN, INPUT); pinMode(EVSE_GROUND_FAULT_PIN, INPUT); @@ -174,12 +179,10 @@ void setup() { ESP.restart(); } - PRINT(F("[main] host, port, URL: ")); - PRINT(ocppUrlParsed.host); - PRINT(F(", ")); - PRINT(ocppUrlParsed.port); - PRINT(F(", ")); - PRINTLN(ocppUrlParsed.url); + PRINTF("[main] host, port, URL: %s, %hu, %s\n", + ocppUrlParsed.host.isEmpty() ? "undefined" : ocppUrlParsed.host.c_str(), + ocppUrlParsed.port, + ocppUrlParsed.url.isEmpty() ? "undefined" : ocppUrlParsed.url.c_str()); /* * Initialize ArduinoOcpp framework. @@ -202,21 +205,19 @@ void setup() { PRINT('.'); now = time(nullptr); } - PRINT(F(" finished. Unix timestamp is ")); - PRINTLN(now); + PRINTF(" finished. Unix timestamp is %lu\n", now); wSock.beginSslWithCA(ocppUrlParsed.host.c_str(), ocppUrlParsed.port, ocppUrlParsed.url.c_str(), *CA_cert, "ocpp1.6"); } else { - wSock.beginSSL(ocppUrlParsed.host.c_str(), ocppUrlParsed.port, ocppUrlParsed.url.c_str(), NULL, "ocpp1.6"); + wSock.beginSSL(ocppUrlParsed.host.c_str(), ocppUrlParsed.port, ocppUrlParsed.url.c_str(), nullptr, "ocpp1.6"); } } else { wSock.begin(ocppUrlParsed.host, ocppUrlParsed.port, ocppUrlParsed.url, "ocpp1.6"); } OCPP_initialize(oSock, - /* Grid voltage */ 230.f, - ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail, - ArduinoOcpp::Clocks::DEFAULT_CLOCK); + 230.f, //European grid voltage + ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail); /* * Integrate OCPP functionality. You can leave out the following part if your EVSE doesn't need it. @@ -224,24 +225,26 @@ void setup() { setEnergyActiveImportSampler([]() { //read the energy input register of the EVSE here and return the value in Wh /* - * Approximated value. TODO: Replace with real reading + * Approximated value. Replace with real reading */ static ulong lastSampled = millis(); static float energyMeter = 0.f; - if (getTransactionId() > 0 && digitalRead(EV_CHARGE_PIN) == EV_CHARGING) - energyMeter += ((float) millis() - lastSampled) * 0.003f; //increase by 0.003Wh per ms (~ 10.8kWh per h) + if (getTransactionId() > 0) + energyMeter += ((float) (millis() - lastSampled)) * 0.003f; //increase by 0.003Wh per ms (~ 10.8kWh per h) lastSampled = millis(); return energyMeter; }); setOnChargingRateLimitChange([](float limit) { //set the SAE J1772 Control Pilot value here - PRINT(F("[main] Smart Charging allows maximum charge rate: ")); - PRINTLN(limit); - float amps = limit / 230.f; + const float voltage = 230.f; // European grid + const uint nPhases = 1; //one, two or three phase charging + float amps = limit / (voltage * (float) nPhases); if (amps > 51.f) amps = 51.f; + PRINTF("[main] Smart Charging allows maximum charge rate: %iW; convert to Control Pilot amerage: %.2fA\n", (int) limit, amps); + int pwmVal; if (amps < 6.f) { pwmVal = 256; // = constant +3.3V DC @@ -253,7 +256,6 @@ void setup() { ledcWrite(AMPERAGE_PIN, pwmVal); #elif defined(ESP8266) analogWrite(AMPERAGE_PIN, pwmVal); -#else #endif }); @@ -263,19 +265,20 @@ void setup() { }); addConnectorErrorCodeSampler([] () { -// if (digitalRead(EVSE_GROUND_FAULT_PIN) != EVSE_GROUND_CLEAR) { -// return "GroundFault"; -// } else { - return (const char *) NULL; -// } + //Uncomment if Ground fault pin is used + //if (digitalRead(EVSE_GROUND_FAULT_PIN) != EVSE_GROUND_CLEAR) { + // return "GroundFault"; + //} + return (const char *) nullptr; }); - setOnResetSendConf([] (JsonObject payload) { + setOnResetSendConf([] (JsonObject confirmation) { if (getTransactionId() >= 0) stopTransaction(); + PRINTLN(F("[main] Execute reset command")); reboot_timestamp = millis(); - scheduleReboot = 5000; + scheduleReboot = 5000; //reboot will be executed in loop() booted = false; }); @@ -284,15 +287,14 @@ void setup() { /* * Notify the Central System that this station is ready */ - bootNotification("My Charging Station", "My company name", [] (JsonObject payload) { - const char *status = payload["status"] | "INVALID"; - if (!strcmp(status, "Accepted")) { + bootNotification("My Charging Station", "My company name", [] (JsonObject response) { + if (response["status"].as().equals("Accepted")) { booted = true; digitalWrite(SERVER_CONNECT_LED, SERVER_CONNECT_ON); } else { - //retry sending the BootNotification - delay(60000); - ESP.restart(); + //Wait for the connection retry + reboot_timestamp = millis(); + scheduleReboot = 60000; //wait for 60s until reboot; reboot will be executed in loop() } }); } @@ -304,47 +306,49 @@ void loop() { */ OCPP_loop(); - /* - * Detect if something physical happened at your EVSE and trigger the corresponding OCPP messages - */ + //NFC reader integration example if (/* RFID chip detected? */ false) { const char *idTag = "my-id-tag"; //e.g. idTag = RFID.readIdTag(); authorize(idTag); } if (ocppPermitsCharge()) { + //EVSE is in a charging session and charging is permitted by the OCPP server digitalWrite(OCPP_CHARGE_PERMISSION_PIN, OCPP_CHARGE_PERMITTED); digitalWrite(CHARGE_PERMISSION_LED, CHARGE_PERMISSION_ON); } else { + //Charging is not allowed due to OCPP rules digitalWrite(OCPP_CHARGE_PERMISSION_PIN, OCPP_CHARGE_FORBIDDEN); digitalWrite(CHARGE_PERMISSION_LED, CHARGE_PERMISSION_OFF); } - if (!booted) - return; if (scheduleReboot > 0 && millis() - reboot_timestamp >= scheduleReboot) { ESP.restart(); } - if (digitalRead(EV_PLUG_PIN) == EV_PLUGGED && evPlugged == EV_UNPLUGGED && getTransactionId() >= 0) { - //transition unplugged -> plugged; Case A: transaction has already been initiated - evPlugged = EV_PLUGGED; - } else if (digitalRead(EV_PLUG_PIN) == EV_PLUGGED && evPlugged == EV_UNPLUGGED && isAvailable()) { - //transition unplugged -> plugged; Case B: no transaction running; start transaction - evPlugged = EV_PLUGGED; + if (!booted) { + return; + } + auto readEvPlugged = digitalRead(EV_PLUG_PIN); + if (evPlugged == EV_UNPLUGGED && readEvPlugged == EV_PLUGGED //transition from unplugged to plugged + && getTransactionId() < 0 //no transaction yet + && isAvailable()) { //EVSE is in operative mode startTransaction("my-id-tag"); - } else if (digitalRead(EV_PLUG_PIN) == EV_UNPLUGGED && evPlugged == EV_PLUGGED) { - //transition plugged -> unplugged - evPlugged = EV_UNPLUGGED; - - if (getTransactionId() >= 0) - stopTransaction(); + } else if (evPlugged == EV_PLUGGED && readEvPlugged == EV_UNPLUGGED //transition from plugged to unplugged + && getTransactionId() >= 0) { //need to stop transaction + stopTransaction(); } + evPlugged = readEvPlugged; //... see ArduinoOcpp.h for more possibilities } + + +// ### End of the OCPP integration. The following code integrates the +// ### WiFi-Manager into this example sketch. + bool runWiFiManager() { /* @@ -416,16 +420,16 @@ bool runWiFiManager() { wifiManager.setDarkMode(true); //wifiManager.setConfigPortalTimeout(CAPATITIVE_PORTAL_TIMEOUT / 1000); //if nobody logs in to the portal, continue after timeout - wifiManager.setTimeout(CAPTIVE_PORTAL_TIMEOUT / 1000); //if nobody logs in to the portal, continue after timeout - wifiManager.setConnectTimeout(CAPTIVE_PORTAL_TIMEOUT / 1000); + wifiManager.setTimeout(CAPTIVE_PORTAL_TIMEOUT); //if nobody logs in to the portal, continue after timeout + wifiManager.setConnectTimeout(WIFI_CONNECTION_TIMEOUT); //wifiManager.setSaveConnect(true); wifiManager.setAPClientCheck(true); // avoid timeout if client connected to softap PRINTLN(F("[main] Start capatitive portal")); - if (wifiManager.startConfigPortal("EVSE_Maintenance_Portal", "myEvseController")) { + if (wifiManager.startConfigPortal("EVSE-Config", "evse1234")) { return true; } else { - return wifiManager.autoConnect("EVSE_Maintenance_Portal", "myEvseController"); + return wifiManager.autoConnect("EVSE-Config", "evse1234"); } } From f2956cbb7fbf5b240c07cca372cca785c8c63416 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 18 Jun 2022 12:18:19 +0200 Subject: [PATCH 169/696] update Status documentation --- .../MessagesV16/StatusNotification.cpp | 2 +- .../ChargePointStatus/ConnectorStatus.cpp | 1 + .../Tasks/ChargePointStatus/OcppEvseState.h | 20 +++++++++---------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp index 72cdff6a..89350484 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp @@ -45,7 +45,7 @@ const char *cstrFromOcppEveState(OcppEvseState state) { StatusNotification::StatusNotification(int connectorId, OcppEvseState currentStatus, const OcppTimestamp &otimestamp, const char *errorCode) : connectorId(connectorId), currentStatus(currentStatus), otimestamp(otimestamp), errorCode(errorCode) { - AO_DBG_INFO("New status: %s", cstrFromOcppEveState(currentStatus)); + AO_DBG_INFO("New status: %s (connectorId %d)", cstrFromOcppEveState(currentStatus), connectorId); } const char* StatusNotification::getOcppOperationType(){ diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index eb90a08f..0a8d67fa 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -37,6 +37,7 @@ ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) minimumStatusDuration = declareConfiguration("MinimumStatusDuration", 0, CONFIGURATION_FN, true, true, true, false); stopTransactionOnInvalidId = declareConfiguration("StopTransactionOnInvalidId", "true", CONFIGURATION_FN, true, true, false, false); stopTransactionOnEVSideDisconnect = declareConfiguration("StopTransactionOnEVSideDisconnect", "true", CONFIGURATION_FN, true, true, false, false); + unlockConnectorOnEVSideDisconnect = declareConfiguration("UnlockConnectorOnEVSideDisconnect", "true", CONFIGURATION_FN, true, true, false, false); localAuthorizeOffline = declareConfiguration("LocalAuthorizeOffline", "false", CONFIGURATION_FN, true, true, false, false); localPreAuthorize = declareConfiguration("LocalPreAuthorize", "false", CONFIGURATION_FN, true, true, false, false); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h index 00a49152..a3d9964a 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/OcppEvseState.h @@ -8,16 +8,16 @@ namespace ArduinoOcpp { enum class OcppEvseState { - Available, - Preparing, - Charging, - SuspendedEVSE, - SuspendedEV, - Finishing, //not supported by this client - Reserved, //not supported by this client - Unavailable, - Faulted, - NOT_SET //not part of OCPP 1.6 + Available, + Preparing, + Charging, + SuspendedEVSE, + SuspendedEV, + Finishing, + Reserved, + Unavailable, + Faulted, + NOT_SET //internal value for "undefined" }; } //end namespace ArduinoOcpp From a45d3a822c031394e50b52324a9c291feb4a2e01 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 18 Jun 2022 23:07:39 +0200 Subject: [PATCH 170/696] more Smart Charging Configuration keys --- src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp | 3 +++ src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index b4cd67ce..0dd66ba8 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -44,6 +44,9 @@ SmartChargingService::SmartChargingService(OcppEngine& context, float chargeLimi TxProfile[i] = NULL; } declareConfiguration("ChargeProfileMaxStackLevel", CHARGEPROFILEMAXSTACKLEVEL, CONFIGURATION_VOLATILE, false, true, false, false); + declareConfiguration("ChargingScheduleAllowedChargingRateUnit ", "Power", CONFIGURATION_VOLATILE, false, true, false, false); + declareConfiguration("ChargingScheduleMaxPeriods", CHARGINGSCHEDULEMAXPERIODS, CONFIGURATION_VOLATILE, false, true, false, false); + declareConfiguration("MaxChargingProfilesInstalled", MAXCHARGINGPROFILESINSTALLED, CONFIGURATION_VOLATILE, false, true, false, false); const char *fpId = "SmartCharging"; auto fProfile = declareConfiguration("SupportedFeatureProfiles",fpId, CONFIGURATION_VOLATILE, false, true, true, false); diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h index 53546204..8dd10dc6 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h @@ -5,7 +5,9 @@ #ifndef SMARTCHARGINGSERVICE_H #define SMARTCHARGINGSERVICE_H -#define CHARGEPROFILEMAXSTACKLEVEL 20 +#define CHARGEPROFILEMAXSTACKLEVEL 8 +#define CHARGINGSCHEDULEMAXPERIODS 24 +#define MAXCHARGINGPROFILESINSTALLED 10 #include #include From 5f3a729d62f0337aaba20905596d91d56f09b088 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sun, 19 Jun 2022 11:16:49 +0200 Subject: [PATCH 171/696] cache BootNotification credentials --- src/ArduinoOcpp.cpp | 10 ++- .../MessagesV16/BootNotification.cpp | 74 +++++++++---------- .../MessagesV16/BootNotification.h | 17 ++--- .../ChargePointStatusService.cpp | 23 ++++++ .../ChargePointStatusService.h | 5 ++ 5 files changed, 78 insertions(+), 51 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index f488c258..9add110d 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -374,8 +374,14 @@ void bootNotification(const char *chargePointModel, const char *chargePointVendo AO_DBG_ERR("Please call OCPP_initialize before"); return; } + + auto credentials = std::unique_ptr(new DynamicJsonDocument( + JSON_OBJECT_SIZE(2) + strlen(chargePointModel) + strlen(chargePointVendor) + 2)); + (*credentials)["chargePointModel"] = (char*) chargePointModel; + (*credentials)["chargePointVendor"] = (char*) chargePointVendor; + auto bootNotification = makeOcppOperation( - new BootNotification(chargePointModel, chargePointVendor)); + new BootNotification(std::move(credentials))); if (onConf) bootNotification->setOnReceiveConfListener(onConf); if (onAbort) @@ -397,7 +403,7 @@ void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf return; } auto bootNotification = makeOcppOperation( - new BootNotification(payload)); + new BootNotification(std::unique_ptr(payload))); if (onConf) bootNotification->setOnReceiveConfListener(onConf); if (onAbort) diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp index ab5a2309..3bae171e 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.cpp @@ -13,56 +13,56 @@ using ArduinoOcpp::Ocpp16::BootNotification; BootNotification::BootNotification() { - -} - -BootNotification::BootNotification(const char *cpModel, const char *cpVendor) { - snprintf(chargePointModel, CP_MODEL_LEN_MAX + 1, "%s", cpModel); - snprintf(chargePointVendor, CP_VENDOR_LEN_MAX + 1, "%s", cpVendor); -} - -BootNotification::BootNotification(const char *cpModel, const char *cpSerialNumber, const char *cpVendor, const char *fwVersion) { - snprintf(chargePointModel, CP_MODEL_LEN_MAX + 1, "%s", cpModel); - snprintf(chargePointSerialNumber, CP_SERIALNUMBER_LEN_MAX + 1, "%s", cpSerialNumber); - snprintf(chargePointVendor, CP_VENDOR_LEN_MAX + 1, "%s", cpVendor); - snprintf(firmwareVersion, FW_VERSION_LEN_MAX + 1, "%s", fwVersion); -} - -BootNotification::BootNotification(DynamicJsonDocument *payload) { - this->overridePayload = payload; + } -BootNotification::~BootNotification() { - if (overridePayload != nullptr) - delete overridePayload; +BootNotification::BootNotification(std::unique_ptr payload) : credentials(std::move(payload)) { + } const char* BootNotification::getOcppOperationType(){ return "BootNotification"; } +void BootNotification::initiate() { + if (credentials && + ocppModel && ocppModel->getChargePointStatusService()) { + auto cpStatus = ocppModel->getChargePointStatusService(); + cpStatus->setChargePointCredentials(*credentials); + credentials.release(); + } +} + std::unique_ptr BootNotification::createReq() { - if (overridePayload != nullptr) { - auto result = std::unique_ptr(new DynamicJsonDocument(*overridePayload)); - return result; - } + if (ocppModel && ocppModel->getChargePointStatusService()) { + auto cpStatus = ocppModel->getChargePointStatusService(); + const auto& cpCredentials = cpStatus->getChargePointCredentials(); + + std::unique_ptr doc; + size_t capacity = JSON_OBJECT_SIZE(9) + cpCredentials.size(); + DeserializationError err = DeserializationError::NoMemory; + while (err == DeserializationError::NoMemory) { + doc.reset(new DynamicJsonDocument(capacity)); + err = deserializeJson(*doc, cpCredentials); + + capacity *= 3; + capacity /= 2; + } - auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(4) - + strlen(chargePointModel) + 1 - + strlen(chargePointVendor) + 1 - + strlen(chargePointSerialNumber) + 1 - + strlen(firmwareVersion) + 1)); - JsonObject payload = doc->to(); - payload["chargePointModel"] = chargePointModel; - if (chargePointSerialNumber[0]) { - payload["chargePointSerialNumber"] = chargePointSerialNumber; + if (!err) { + return doc; + } else { + AO_DBG_ERR("could not parse stored credentials: %s", err.c_str()); + } } - payload["chargePointVendor"] = chargePointVendor; - if (firmwareVersion[0]) { - payload["firmwareVersion"] = firmwareVersion; + + if (credentials) { + return std::unique_ptr(new DynamicJsonDocument(*credentials)); } - return doc; + + AO_DBG_ERR("payload undefined"); + return createEmptyDocument(); } void BootNotification::processConf(JsonObject payload){ diff --git a/src/ArduinoOcpp/MessagesV16/BootNotification.h b/src/ArduinoOcpp/MessagesV16/BootNotification.h index 54449802..5f33364a 100644 --- a/src/ArduinoOcpp/MessagesV16/BootNotification.h +++ b/src/ArduinoOcpp/MessagesV16/BootNotification.h @@ -18,25 +18,18 @@ namespace Ocpp16 { class BootNotification : public OcppMessage { private: - char chargePointModel [CP_MODEL_LEN_MAX + 1] = {'\0'}; - char chargePointSerialNumber [CP_SERIALNUMBER_LEN_MAX + 1] = {'\0'}; - char chargePointVendor [CP_VENDOR_LEN_MAX + 1] = {'\0'}; - char firmwareVersion [FW_VERSION_LEN_MAX + 1] = {'\0'}; - - DynamicJsonDocument *overridePayload = NULL; + std::unique_ptr credentials; public: BootNotification(); - ~BootNotification(); - - BootNotification(const char *chargePointModel, const char *chargePointVendor); + ~BootNotification() = default; - BootNotification(const char *chargePointModel, const char *chargePointSerialNumber, const char *chargePointVendor, const char *firmwareVersion); - - BootNotification(DynamicJsonDocument *payload); + BootNotification(std::unique_ptr payload); const char* getOcppOperationType(); + void initiate(); + std::unique_ptr createReq(); void processConf(JsonObject payload); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index 7a43ac50..ce616d72 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -86,3 +86,26 @@ bool ChargePointStatusService::isBooted() { int ChargePointStatusService::getNumConnectors() { return connectors.size(); } + +void ChargePointStatusService::setChargePointCredentials(DynamicJsonDocument &credentials) { + if (!credentials.is()) { + AO_DBG_ERR("Payload must be JSON object"); + cpCredentials.clear(); + return; + } + auto written = serializeJson(credentials, cpCredentials); + if (written <= 2) { + AO_DBG_ERR("Could not parse CP credentials: %s", written == 2 ? "format violation" : "invalid JSON"); + cpCredentials.clear(); + return; + } + //success +} + +std::string& ChargePointStatusService::getChargePointCredentials() { + if (cpCredentials.size() <= 2) { + cpCredentials = "{}"; + } + + return cpCredentials; +} diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h index ce95f59e..7526d352 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h @@ -21,6 +21,8 @@ class ChargePointStatusService { bool booted = false; + std::string cpCredentials; + public: ChargePointStatusService(OcppEngine& context, unsigned int numConnectors); @@ -33,6 +35,9 @@ class ChargePointStatusService { ConnectorStatus *getConnector(int connectorId); int getNumConnectors(); + + void setChargePointCredentials(DynamicJsonDocument &credentials); + std::string& getChargePointCredentials(); }; } //end namespace ArduinoOcpp From 0d870bb448af3147e8e50c3517ff6b5ab6f01b2a Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 20 Jun 2022 23:24:23 +0200 Subject: [PATCH 172/696] fix ClearChargingProfile not removed --- src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h | 6 +++--- .../Tasks/SmartCharging/SmartChargingService.cpp | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h index d909e6a7..9a107e8c 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h @@ -94,9 +94,9 @@ class ChargingProfile { int chargingProfileId = -1; int transactionId = -1; int stackLevel = 0; - ChargingProfilePurposeType chargingProfilePurpose; - ChargingProfileKindType chargingProfileKind; //copied to ChargingSchedule to increase cohesion of limit inferencing methods - RecurrencyKindType recurrencyKind; // copied to ChargingSchedule to increase cohesion + ChargingProfilePurposeType chargingProfilePurpose {ChargingProfilePurposeType::TxProfile}; + ChargingProfileKindType chargingProfileKind {ChargingProfileKindType::Relative}; //copied to ChargingSchedule to increase cohesion of limit inferencing methods + RecurrencyKindType recurrencyKind {RecurrencyKindType::NOT_SET}; // copied to ChargingSchedule to increase cohesion OcppTimestamp validFrom; OcppTimestamp validTo; std::unique_ptr chargingSchedule; diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index 0dd66ba8..08e752ae 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -330,6 +330,7 @@ bool SmartChargingService::clearChargingProfile(const std::function Date: Mon, 20 Jun 2022 23:27:08 +0200 Subject: [PATCH 173/696] fix SuspendedEVSE state --- src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 0a8d67fa..f61b5f91 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -386,7 +386,7 @@ void ConnectorStatus::setEvRequestsEnergySampler(std::function evRequest } void ConnectorStatus::setConnectorEnergizedSampler(std::function connectorEnergized) { - this->connectorEnergizedSampler = connectorEnergizedSampler; + this->connectorEnergizedSampler = connectorEnergized; } void ConnectorStatus::addConnectorErrorCodeSampler(std::function connectorErrorCode) { From d29698ec281a7e6e3620a1c7b08ad2cd94e4af3f Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 20 Jun 2022 23:27:27 +0200 Subject: [PATCH 174/696] update BootNotification facade call --- src/ArduinoOcpp.cpp | 20 +++----------------- src/ArduinoOcpp.h | 2 +- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 9add110d..c2cd64a2 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -380,30 +380,16 @@ void bootNotification(const char *chargePointModel, const char *chargePointVendo (*credentials)["chargePointModel"] = (char*) chargePointModel; (*credentials)["chargePointVendor"] = (char*) chargePointVendor; - auto bootNotification = makeOcppOperation( - new BootNotification(std::move(credentials))); - if (onConf) - bootNotification->setOnReceiveConfListener(onConf); - if (onAbort) - bootNotification->setOnAbortListener(onAbort); - if (onTimeout) - bootNotification->setOnTimeoutListener(onTimeout); - if (onError) - bootNotification->setOnReceiveErrorListener(onError); - if (timeout) - bootNotification->setTimeout(std::move(timeout)); - else - bootNotification->setTimeout(std::unique_ptr (new SuppressedTimeout())); - ocppEngine->initiateOperation(std::move(bootNotification)); + bootNotification(std::move(credentials), onConf, onAbort, onTimeout, onError, std::move(timeout)); } -void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { +void bootNotification(std::unique_ptr payload, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { AO_DBG_ERR("Please call OCPP_initialize before"); return; } auto bootNotification = makeOcppOperation( - new BootNotification(std::unique_ptr(payload))); + new BootNotification(std::move(payload))); if (onConf) bootNotification->setOnReceiveConfListener(onConf); if (onAbort) diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index c22b2088..c24ed0f4 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -145,7 +145,7 @@ void authorize(const char *idTag, OnReceiveConfListener onConf = nullptr, OnAbor void bootNotification(const char *chargePointModel, const char *chargePointVendor, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); //The OCPP operation will include the given payload without modifying it. The library will delete the payload object after successful transmission. -void bootNotification(DynamicJsonDocument *payload, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); +void bootNotification(std::unique_ptr payload, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); void startTransaction(const char *idTag, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); From 235202fcd1c774c8f41ac178bd32d0e0d55dc3fb Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 25 Jun 2022 00:32:46 +0200 Subject: [PATCH 175/696] improved Reset integration --- src/ArduinoOcpp.cpp | 4 ++ src/ArduinoOcpp/MessagesV16/Reset.cpp | 20 ++++-- src/ArduinoOcpp/MessagesV16/Reset.h | 2 + .../ChargePointStatusService.cpp | 65 ++++++++++++++++++- .../ChargePointStatusService.h | 31 +++++++++ .../ChargePointStatus/ConnectorStatus.cpp | 12 ++++ .../Tasks/ChargePointStatus/ConnectorStatus.h | 3 + 7 files changed, 129 insertions(+), 8 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index c2cd64a2..7f5333fc 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -110,6 +110,10 @@ void OCPP_initialize(OcppSocket& ocppSocket, float V_eff, ArduinoOcpp::Filesyste new DiagnosticsService(*ocppEngine))); #endif +#if !defined(AO_CUSTOM_RESET) + model.getChargePointStatusService()->setExecuteReset(EspWiFi::makeDefaultResetFn()); +#endif + ocppEngine->setRunOcppTasks(false); //prevent OCPP classes from doing anything while booting } diff --git a/src/ArduinoOcpp/MessagesV16/Reset.cpp b/src/ArduinoOcpp/MessagesV16/Reset.cpp index 9e751ca6..0e54e8ef 100644 --- a/src/ArduinoOcpp/MessagesV16/Reset.cpp +++ b/src/ArduinoOcpp/MessagesV16/Reset.cpp @@ -25,19 +25,25 @@ void Reset::processReq(JsonObject payload) { if (ocppModel && ocppModel->getChargePointStatusService()) { auto cpsService = ocppModel->getChargePointStatusService(); - int connId = 0; - for (int i = 0; i < cpsService->getNumConnectors(); i++) { - auto connector = cpsService->getConnector(connId); - if (connector) { - connector->endSession(isHard ? "HardReset" : "SoftReset"); + if (!cpsService->getPreReset() || cpsService->getPreReset()(isHard) || isHard) { + resetAccepted = true; + cpsService->initiateReset(isHard); + int connId = 0; + for (int i = 0; i < cpsService->getNumConnectors(); i++) { + auto connector = cpsService->getConnector(connId); + if (connector) { + connector->endSession(isHard ? "HardReset" : "SoftReset"); + } } } + } else { + resetAccepted = true; //assume that onReceiveReset is set } } -std::unique_ptr Reset::createConf(){ +std::unique_ptr Reset::createConf() { auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); JsonObject payload = doc->to(); - payload["status"] = "Accepted"; + payload["status"] = resetAccepted ? "Accepted" : "Rejected"; return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/Reset.h b/src/ArduinoOcpp/MessagesV16/Reset.h index e7dcfa83..7693dddb 100644 --- a/src/ArduinoOcpp/MessagesV16/Reset.h +++ b/src/ArduinoOcpp/MessagesV16/Reset.h @@ -11,6 +11,8 @@ namespace ArduinoOcpp { namespace Ocpp16 { class Reset : public OcppMessage { +private: + bool resetAccepted {false}; public: Reset(); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp index ce616d72..7ac60c4a 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp @@ -12,6 +12,8 @@ #include #include +#define RESET_DELAY 15000 + using namespace ArduinoOcpp; ChargePointStatusService::ChargePointStatusService(OcppEngine& context, unsigned int numConn) @@ -20,7 +22,6 @@ ChargePointStatusService::ChargePointStatusService(OcppEngine& context, unsigned for (unsigned int i = 0; i < numConn; i++) { connectors.push_back(std::unique_ptr(new ConnectorStatus(context.getOcppModel(), i))); } - std::shared_ptr> numberOfConnectors = declareConfiguration("NumberOfConnectors", numConn >= 1 ? numConn - 1 : 0, CONFIGURATION_VOLATILE, false, true, false, false); @@ -44,6 +45,8 @@ ChargePointStatusService::ChargePointStatusService(OcppEngine& context, unsigned fProfile->setValue(fProfilePlus.c_str(), fProfilePlus.length() + 1); } + resetRetries = declareConfiguration("ResetRetries", 2, CONFIGURATION_FN, true, true, false, false); + /* * Further configuration keys which correspond to the Core profile */ @@ -64,6 +67,25 @@ void ChargePointStatusService::loop() { context.initiateOperation(std::move(statusNotification)); } } + + if (outstandingResetRetries > 0 && ao_tick_ms() - t_resetRetry >= RESET_DELAY) { + t_resetRetry = ao_tick_ms(); + outstandingResetRetries--; + if (executeReset) { + AO_DBG_INFO("Reset device"); + executeReset(isHardReset); + } else { + AO_DBG_ERR("No Reset function set! Abort"); + outstandingResetRetries = 0; + } + AO_DBG_ERR("Reset device failure. %s", outstandingResetRetries == 0 ? "Abort" : "Retry"); + + if (outstandingResetRetries <= 0) { + for (auto connector = connectors.begin(); connector != connectors.end(); connector++) { + (*connector)->setRebooting(false); + } + } + } } ConnectorStatus *ChargePointStatusService::getConnector(int connectorId) { @@ -109,3 +131,44 @@ std::string& ChargePointStatusService::getChargePointCredentials() { return cpCredentials; } + +void ChargePointStatusService::setPreReset(std::function preReset) { + this->preReset = preReset; +} + +std::function ChargePointStatusService::getPreReset() { + return this->preReset; +} + +void ChargePointStatusService::setExecuteReset(std::function executeReset) { + this->executeReset = executeReset; +} + +std::function ChargePointStatusService::getExecuteReset() { + return this->executeReset; +} + +void ChargePointStatusService::initiateReset(bool isHard) { + isHardReset = isHard; + outstandingResetRetries = 1 + *resetRetries; //one initial try + no. of retries + if (outstandingResetRetries > 5) { + AO_DBG_ERR("no. of reset trials exceeds 5"); + outstandingResetRetries = 5; + } + t_resetRetry = ao_tick_ms(); + + for (auto connector = connectors.begin(); connector != connectors.end(); connector++) { + (*connector)->setRebooting(true); + } +} + +#if !defined(AO_CUSTOM_RESET) +#if defined(ESP32) || defined(ESP8266) +std::function ArduinoOcpp::EspWiFi::makeDefaultResetFn() { + return [] (bool isHard) { + AO_DBG_DEBUG("Perform ESP reset"); + ESP.restart(); + }; +} +#endif //defined(ESP32) || defined(ESP8266) +#endif //!defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WS) diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h index 7526d352..2add4e7c 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h @@ -23,6 +23,14 @@ class ChargePointStatusService { std::string cpCredentials; + std::function preReset; //true: reset is possible; false: reject reset; Await: need more time to determine + std::function executeReset; //please disconnect WebSocket (AO remains initialized), shut down device and restart with normal initialization routine; on failure reconnect WebSocket + uint outstandingResetRetries = 0; //0 = do not reset device + bool isHardReset = false; + ulong t_resetRetry; + + std::shared_ptr> resetRetries; + public: ChargePointStatusService(OcppEngine& context, unsigned int numConnectors); @@ -38,7 +46,30 @@ class ChargePointStatusService { void setChargePointCredentials(DynamicJsonDocument &credentials); std::string& getChargePointCredentials(); + + void setPreReset(std::function preReset); + std::function getPreReset(); + + void setExecuteReset(std::function executeReset); + std::function getExecuteReset(); + + void initiateReset(bool isHard); }; } //end namespace ArduinoOcpp + +#if !defined(AO_CUSTOM_RESET) +#if defined(ESP32) || defined(ESP8266) + +namespace ArduinoOcpp { +namespace EspWiFi { + +std::function makeDefaultResetFn(); + +} +} + +#endif //defined(ESP32) || defined(ESP8266) +#endif //!defined(AO_CUSTOM_UPDATER) && !defined(AO_CUSTOM_WS) + #endif diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index f61b5f91..e4682138 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -86,6 +86,8 @@ OcppEvseState ConnectorStatus::inferenceStatus() { return OcppEvseState::Faulted; } else if (*availability == AVAILABILITY_INOPERATIVE) { return OcppEvseState::Unavailable; + } else if (rebooting) { + return OcppEvseState::Unavailable; } else { return OcppEvseState::Available; } @@ -95,6 +97,8 @@ OcppEvseState ConnectorStatus::inferenceStatus() { return OcppEvseState::Faulted; } else if (*availability == AVAILABILITY_INOPERATIVE) { return OcppEvseState::Unavailable; + } else if (rebooting && getTransactionId() < 0) { + return OcppEvseState::Unavailable; } else if (getTransactionId() == 0 && // i.e. Tx pending or EVSE offline. Check if offline Tx is OFF !(*localAuthorizeOffline && strcmp(*localAuthorizeOffline, "false")) && !(*localPreAuthorize && strcmp(*localPreAuthorize, "false"))) { @@ -334,6 +338,10 @@ const char *ConnectorStatus::getSessionIdTag() { return session ? idTag : nullptr; } +uint16_t ConnectorStatus::getSessionWriteCount() { + return sIdTag->getValueRevision(); +} + int ConnectorStatus::getTransactionId() { return *transactionId; } @@ -374,6 +382,10 @@ void ConnectorStatus::setAvailability(bool available) { saveState(); } +void ConnectorStatus::setRebooting(bool rebooting) { + this->rebooting = rebooting; +} + void ConnectorStatus::setConnectorPluggedSampler(std::function connectorPlugged) { this->connectorPluggedSampler = connectorPlugged; txTriggerConditions.push_back([this] () -> TxCondition { diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index 940e1d35..ec5492ba 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -31,6 +31,7 @@ class ConnectorStatus { const int connectorId; std::shared_ptr> availability; + bool rebooting = false; //report connector inoperative and reject new charging sessions bool session = false; char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; @@ -86,6 +87,7 @@ class ConnectorStatus { void endSession(const char *reason = nullptr); void setIdTagInvalidated(); //if StartTransaction.conf() has status != "Accepted" const char *getSessionIdTag(); + uint16_t getSessionWriteCount(); int getTransactionId(); int getTransactionIdSync(); uint16_t getTransactionWriteCount(); @@ -94,6 +96,7 @@ class ConnectorStatus { int getAvailability(); void setAvailability(bool available); + void setRebooting(bool rebooting); void setAuthorizationProvider(std::function authorization); void setConnectorPluggedSampler(std::function connectorPlugged); void setEvRequestsEnergySampler(std::function evRequestsEnergy); From 0041934bc22da9448f1f89b4b2aa1dd47435203c Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sun, 26 Jun 2022 17:53:44 +0200 Subject: [PATCH 176/696] RmtTx charging profile support --- .../MessagesV16/RemoteStartTransaction.cpp | 55 ++++++++++-- .../MessagesV16/RemoteStartTransaction.h | 5 ++ .../MessagesV16/SetChargingProfile.cpp | 2 +- .../SmartCharging/SmartChargingModel.cpp | 35 +++++--- .../Tasks/SmartCharging/SmartChargingModel.h | 4 +- .../SmartCharging/SmartChargingService.cpp | 88 +++++++++++++++---- .../SmartCharging/SmartChargingService.h | 14 ++- 7 files changed, 158 insertions(+), 45 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp index 4100b291..39835d7a 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp @@ -5,7 +5,9 @@ #include #include +#include #include +#include #include using ArduinoOcpp::Ocpp16::RemoteStartTransaction; @@ -27,20 +29,28 @@ void RemoteStartTransaction::processReq(JsonObject payload) { snprintf(idTag, IDTAG_LEN_MAX + 1, "%s", idTagIn); } + if (*idTag == '\0') { + AO_DBG_WARN("idTag format violation"); + errorCode = "FormationViolation"; + } + if (payload.containsKey("chargingProfile")) { - AO_DBG_WARN("chargingProfile via RmtStartTransaction not supported yet"); + AO_DBG_INFO("Setting Charging profile via RemoteStartTransaction"); + + JsonObject chargingProfile = payload["chargingProfile"]; + if ((chargingProfile["chargingProfileId"] | -1) < 0) { + AO_DBG_WARN("RemoteStartTx profile requires non-negative chargingProfileId"); + errorCode = chargingProfile.containsKey("chargingProfileId") ? + "PropertyConstraintViolation" : "FormationViolation"; + } + chargingProfileDoc = DynamicJsonDocument(chargingProfile.memoryUsage()); //copy TxProfile + chargingProfileDoc.set(chargingProfile); } } std::unique_ptr RemoteStartTransaction::createConf(){ auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); JsonObject payload = doc->to(); - - if (*idTag == '\0') { - AO_DBG_WARN("idTag format violation"); - payload["status"] = "Rejected"; - return doc; - } bool canStartTransaction = false; if (connectorId >= 1) { @@ -70,13 +80,42 @@ std::unique_ptr RemoteStartTransaction::createConf(){ } } - if (canStartTransaction){ + if (canStartTransaction) { + + auto sRmtProfileId = declareConfiguration("AO_SRMTPROFILEID_CONN_1", -1, CONFIGURATION_FN, false, false, true, false); + + if (ocppModel && ocppModel->getSmartChargingService()) { + auto scService = ocppModel->getSmartChargingService(); + + if (*sRmtProfileId >= 0) { + int clearProfileId = *sRmtProfileId; + bool ret = scService->clearChargingProfile([clearProfileId](int id, int, ChargingProfilePurposeType, int) { + return id == clearProfileId; + }); + + *sRmtProfileId = -1; + AO_DBG_DEBUG("Cleared Charging Profile from previous RemoteStartTx: %s", ret ? "success" : "already cleared"); + configuration_save(); + } + } + if (ocppModel && ocppModel->getConnectorStatus(connectorId)) { auto connector = ocppModel->getConnectorStatus(connectorId); connector->beginSession(idTag); } + if (!chargingProfileDoc.isNull() + && (ocppModel && ocppModel->getSmartChargingService())) { + auto scService = ocppModel->getSmartChargingService(); + + JsonObject chargingProfile = chargingProfileDoc.as(); + scService->setChargingProfile(chargingProfile); + *sRmtProfileId = chargingProfile["chargingProfileId"].as(); + AO_DBG_DEBUG("Charging Profile from RemoteStartTx set"); + configuration_save(); + } + payload["status"] = "Accepted"; } else { AO_DBG_INFO("No connector to start transaction"); diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h index f8026d6b..f3d077b6 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.h @@ -15,6 +15,9 @@ class RemoteStartTransaction : public OcppMessage { private: int connectorId; char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; + DynamicJsonDocument chargingProfileDoc {0}; + + const char *errorCode {nullptr}; public: RemoteStartTransaction(); @@ -27,6 +30,8 @@ class RemoteStartTransaction : public OcppMessage { void processReq(JsonObject payload); std::unique_ptr createConf(); + + const char *getErrorCode() {return errorCode;} }; } //end namespace Ocpp16 diff --git a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp index da4c18d0..5006130a 100644 --- a/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp +++ b/src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp @@ -34,7 +34,7 @@ void SetChargingProfile::processReq(JsonObject payload) { if (ocppModel && ocppModel->getSmartChargingService()) { auto smartChargingService = ocppModel->getSmartChargingService(); - smartChargingService->updateChargingProfile(&csChargingProfiles); + smartChargingService->setChargingProfile(csChargingProfiles); } } diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp index fd34dd7c..1ddf7c94 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp @@ -46,7 +46,7 @@ int ChargingSchedulePeriod::getNumberPhases(){ } void ChargingSchedulePeriod::printPeriod(){ - AO_DBG_INFO("CHARGING SCHEDULE PERIOD:\n" \ + AO_DBG_VERBOSE("CHARGING SCHEDULE PERIOD:\n" \ " startPeriod: %i\n" \ " limit: %f\n" \ " numberPhases: %i\n", @@ -267,7 +267,7 @@ void ChargingSchedule::printSchedule(){ char tmp[JSONDATE_LENGTH + 1] = {'\0'}; startSchedule.toJsonString(tmp, JSONDATE_LENGTH + 1); - AO_DBG_INFO("CHARGING SCHEDULE:\n" \ + AO_DBG_VERBOSE("CHARGING SCHEDULE:\n" \ " duration: %i\n" \ " startSchedule: %s\n" \ " chargingRateUnit: %s\n" \ @@ -351,15 +351,28 @@ bool ChargingProfile::inferenceLimit(const OcppTimestamp &t, float *limit, OcppT return inferenceLimit(t, MAX_TIME, limit, nextChange); } -bool ChargingProfile::checkTransactionId(int chargingSessionTransactionID) { - if (transactionId >= 0 && chargingSessionTransactionID >= 0){ - //Transaction IDs are valid - if (chargingProfilePurpose == ChargingProfilePurposeType::TxProfile //only then a transactionId can restrict the limits - && transactionId != chargingSessionTransactionID) { - return false; - } +bool ChargingProfile::checkTransactionAssignment(int txId, int profileId) { + if (chargingProfilePurpose != ChargingProfilePurposeType::TxProfile) { + AO_DBG_ERR("assignment only exists for TxProfiles"); + return true; //does not apply to this profile -> no restriction } - return true; + + if (txId <= 0 && profileId < 0) { + //no search parameters set + return true; + } + + if (chargingProfileId >= 0 && profileId >= 0) { //profileIDs are valid + return chargingProfileId == profileId; //return if they match (case of remote charging profiles) + } + + if (transactionId > 0 && txId > 0) { //txIDs are valid + return transactionId == txId; //return if they do match + } + + AO_DBG_ERR("Check error"); + //neither txIds nor profileIDs apply + return false; } int ChargingProfile::getStackLevel(){ @@ -381,7 +394,7 @@ void ChargingProfile::printProfile(){ char tmp2[JSONDATE_LENGTH + 1] = {'\0'}; validTo.toJsonString(tmp2, JSONDATE_LENGTH + 1); - AO_DBG_INFO("CHARGING PROFILE:\n" \ + AO_DBG_VERBOSE("CHARGING PROFILE:\n" \ " chargingProfileId: %i\n" \ " transactionId: %i\n" \ " stackLevel: %i\n" \ diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h index 9a107e8c..2cb8b37d 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.h @@ -119,9 +119,9 @@ class ChargingProfile { bool inferenceLimit(const OcppTimestamp &t, float *limit, OcppTimestamp *nextChange); /* - * Check if this profile belongs to transaction with ID transId + * Check if this profile belongs to transaction with ID txId or idTag alternatively */ - bool checkTransactionId(int transId); + bool checkTransactionAssignment(int txId, int profileId); int getStackLevel(); diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index 08e752ae..52e3cd04 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -37,7 +37,11 @@ SmartChargingService::SmartChargingService(OcppEngine& context, float chargeLimi limitBeforeChange = -1.0f; nextChange = MIN_TIME; chargingSessionStart = MAX_TIME; + char max_timestamp [JSONDATE_LENGTH + 1] = {'\0'}; + chargingSessionStart.toJsonString(max_timestamp, JSONDATE_LENGTH + 1); + txStartTime = declareConfiguration("AO_TXSTARTTIME_CONN_1", max_timestamp, CONFIGURATION_FN, false, false, true, false); chargingSessionTransactionID = -1; + sRmtProfileId = declareConfiguration("AO_SRMTPROFILEID_CONN_1", -1, CONFIGURATION_FN, false, false, true, false); for (int i = 0; i < CHARGEPROFILEMAXSTACKLEVEL; i++) { ChargePointMaxProfile[i] = NULL; TxDefaultProfile[i] = NULL; @@ -126,9 +130,11 @@ void SmartChargingService::inferenceLimit(const OcppTimestamp &t, float *limitOu //evaluate limit from TxProfiles float limit_tx = 0.0f; bool limit_defined_tx = false; - for (int i = CHARGEPROFILEMAXSTACKLEVEL - 1; i >= 0; i--){ - if (TxProfile[i] == NULL) continue; - if (!TxProfile[i]->checkTransactionId(chargingSessionTransactionID)) continue; + for (int i = CHARGEPROFILEMAXSTACKLEVEL - 1; i >= 0; i--) { + if (!TxProfile[i]) + continue; + if (!TxProfile[i]->checkTransactionAssignment(chargingSessionTransactionID, *sRmtProfileId)) + continue; OcppTimestamp nextChange = MAX_TIME; limit_defined_tx = TxProfile[i]->inferenceLimit(t, chargingSessionStart, &limit_tx, &nextChange); if (nextChange < validToMin) @@ -144,7 +150,6 @@ void SmartChargingService::inferenceLimit(const OcppTimestamp &t, float *limitOu bool limit_defined_txdef = false; for (int i = CHARGEPROFILEMAXSTACKLEVEL - 1; i >= 0; i--){ if (TxDefaultProfile[i] == NULL) continue; - //if (!TxDefaultProfile[i]->checkTransactionId(chargingSessionTransactionID)) continue; //this doesn't do anything on TxDefaultProfiles and could be deleted OcppTimestamp nextChange = MAX_TIME; limit_defined_txdef = TxDefaultProfile[i]->inferenceLimit(t, chargingSessionStart, &limit_txdef, &nextChange); if (nextChange < validToMin) @@ -160,7 +165,6 @@ void SmartChargingService::inferenceLimit(const OcppTimestamp &t, float *limitOu bool limit_defined_cpmax = false; for (int i = CHARGEPROFILEMAXSTACKLEVEL - 1; i >= 0; i--){ if (ChargePointMaxProfile[i] == NULL) continue; - //if (!ChargePointMaxProfile[i]->checkTransactionId(chargingSessionTransactionID)) continue; //this doesn't do anything on ChargePointMaxProfiles and could be deleted OcppTimestamp nextChange = MAX_TIME; limit_defined_cpmax = ChargePointMaxProfile[i]->inferenceLimit(t, chargingSessionStart, &limit_cpmax, &nextChange); if (nextChange < validToMin) @@ -219,34 +223,80 @@ ChargingSchedule *SmartChargingService::getCompositeSchedule(int connectorId, ot } void SmartChargingService::refreshChargingSessionState() { - int currentTxId = -1; - if (context.getOcppModel().getConnectorStatus(SINGLE_CONNECTOR_ID)) { - auto connector = context.getOcppModel().getConnectorStatus(SINGLE_CONNECTOR_ID); - currentTxId = connector->getTransactionId(); + if (!context.getOcppModel().getConnectorStatus(SINGLE_CONNECTOR_ID)) { + return; //charging session state does not apply + } + + auto connector = context.getOcppModel().getConnectorStatus(SINGLE_CONNECTOR_ID); + + if (!chargingSessionStateInitialized) { + chargingSessionStateInitialized = true; + + chargingSessionStart.setTime(*txStartTime); + chargingSessionTransactionID = connector->getTransactionId(); + sessionIdTagRev = connector->getSessionWriteCount(); + sRmtProfileIdRev = sRmtProfileId->getValueRevision(); + + //fuzzy check if session engaged at reboot (during first loop run) + auto chargingSessionStartCheck = MAX_TIME; + chargingSessionStartCheck -= 1000000; + if (chargingSessionStart >= chargingSessionStartCheck) { + //charging session start lies in future -> null-value -> no charging session before reboot + chargingSessionTransactionID = -1; + } } - if (currentTxId != chargingSessionTransactionID) { + if (connector->getTransactionId() != chargingSessionTransactionID) { //transition! - if (chargingSessionTransactionID != 0 && currentTxId >= 0) { + bool txStartUpdated = false; + if (chargingSessionTransactionID != 0 && connector->getTransactionId() >= 0) { chargingSessionStart = context.getOcppModel().getOcppTime().getOcppTimestampNow(); - } else if (chargingSessionTransactionID >= 0 && currentTxId < 0) { + txStartUpdated = true; + } else if (chargingSessionTransactionID >= 0 && connector->getTransactionId() < 0) { chargingSessionStart = MAX_TIME; + txStartUpdated = true; + } + + if (txStartUpdated) { + char timestamp [JSONDATE_LENGTH + 1] = {'\0'}; + chargingSessionStart.toJsonString(timestamp, JSONDATE_LENGTH + 1); + *txStartTime = timestamp; + configuration_save(); } nextChange = context.getOcppModel().getOcppTime().getOcppTimestampNow(); - chargingSessionTransactionID = currentTxId; } + + if (*sRmtProfileId >= 0 && //Remote profile set? Check if to delete + (!connector->getSessionIdTag() //Always delete Rmt profile if there is no session + || (sessionIdTagRev != connector->getSessionWriteCount() && sRmtProfileIdRev == sRmtProfileId->getValueRevision()))) { + //Alternaternively delete if session state has been overwritten + + //after RemoteTx session expired, clean charging profile + int clearProfileId = *sRmtProfileId; + bool ret = clearChargingProfile([clearProfileId] (int id, int, ChargingProfilePurposeType, int) { + return id == clearProfileId; + }); + + AO_DBG_DEBUG("Clearing RmtTx Charging Profile after session expiry: %s", ret ? "success" : "already cleared"); + + *sRmtProfileId = -1; + } + + chargingSessionTransactionID = connector->getTransactionId(); + sessionIdTagRev = connector->getSessionWriteCount(); + sRmtProfileIdRev = sRmtProfileId->getValueRevision(); } -void SmartChargingService::updateChargingProfile(JsonObject *json) { +void SmartChargingService::setChargingProfile(JsonObject json) { ChargingProfile *pointer = updateProfileStack(json); if (pointer) writeProfileToFlash(json, pointer); } -ChargingProfile *SmartChargingService::updateProfileStack(JsonObject *json){ - ChargingProfile *chargingProfile = new ChargingProfile(*json); +ChargingProfile *SmartChargingService::updateProfileStack(JsonObject json){ + ChargingProfile *chargingProfile = new ChargingProfile(json); if (AO_DBG_LEVEL >= AO_DL_INFO) { AO_DBG_INFO("Charging Profile internal model:"); @@ -344,7 +394,7 @@ bool SmartChargingService::clearChargingProfile(const std::function 0; } -bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile *chargingProfile) { +bool SmartChargingService::writeProfileToFlash(JsonObject json, ChargingProfile *chargingProfile) { #ifndef AO_DEACTIVATE_FLASH if (!filesystemOpt.accessAllowed()) { @@ -378,7 +428,7 @@ bool SmartChargingService::writeProfileToFlash(JsonObject *json, ChargingProfile } // Serialize JSON to file - if (serializeJson(*json, file) == 0) { + if (serializeJson(json, file) == 0) { AO_DBG_ERR("Unable to save: could not serialize JSON for profile: %s", fn); file.close(); return false; @@ -496,7 +546,7 @@ bool SmartChargingService::loadProfiles() { } JsonObject profileJson = profileDoc.as(); - updateProfileStack(&profileJson); + updateProfileStack(profileJson); profileDoc.clear(); break; diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h index 8dd10dc6..ef288ee6 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.h @@ -13,7 +13,7 @@ #include #include -#include +#include #include namespace ArduinoOcpp { @@ -34,18 +34,24 @@ class SmartChargingService { OnLimitChange onLimitChange = NULL; float limitBeforeChange; OcppTimestamp nextChange; + + bool chargingSessionStateInitialized {false}; + std::shared_ptr> txStartTime; OcppTimestamp chargingSessionStart; int chargingSessionTransactionID; + std::shared_ptr> sRmtProfileId; + uint16_t sRmtProfileIdRev {0}; + uint16_t sessionIdTagRev {0}; void refreshChargingSessionState(); - ChargingProfile *updateProfileStack(JsonObject *json); + ChargingProfile *updateProfileStack(JsonObject json); FilesystemOpt filesystemOpt; - bool writeProfileToFlash(JsonObject *json, ChargingProfile *chargingProfile); + bool writeProfileToFlash(JsonObject json, ChargingProfile *chargingProfile); bool loadProfiles(); public: SmartChargingService(OcppEngine& context, float chargeLimit, float V_eff, int numConnectors, FilesystemOpt filesystemOpt = FilesystemOpt::Use_Mount_FormatOnFail); - void updateChargingProfile(JsonObject *json); + void setChargingProfile(JsonObject json); bool clearChargingProfile(const std::function& filter); void inferenceLimit(const OcppTimestamp &t, float *limit, OcppTimestamp *validTo); float inferenceLimitNow(); From 9bb7a703b204607f2a11d23632f18c82e23537ef Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sun, 26 Jun 2022 19:28:47 +0200 Subject: [PATCH 177/696] retain C facade --- CMakeLists.txt | 66 --------------- README.md | 66 ++++++++++++++- src/ArduinoOcpp_c.cpp | 181 ------------------------------------------ src/ArduinoOcpp_c.h | 107 ------------------------- src/ao_opts.h | 31 -------- src/ao_opts_impl.c | 30 ------- 6 files changed, 64 insertions(+), 417 deletions(-) delete mode 100644 CMakeLists.txt delete mode 100644 src/ArduinoOcpp_c.cpp delete mode 100644 src/ArduinoOcpp_c.h delete mode 100644 src/ao_opts.h delete mode 100644 src/ao_opts_impl.c diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 862c2398..00000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,66 +0,0 @@ -set(AO_SRC - src/ArduinoOcpp/Core/Configuration.cpp - src/ArduinoOcpp/Core/ConfigurationContainer.cpp - src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp - src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp - src/ArduinoOcpp/Core/OcppConnection.cpp - src/ArduinoOcpp/Core/OcppEngine.cpp - src/ArduinoOcpp/Core/OcppMessage.cpp - src/ArduinoOcpp/Core/OcppModel.cpp - src/ArduinoOcpp/Core/OcppOperation.cpp - src/ArduinoOcpp/Core/OcppOperationTimeout.cpp - src/ArduinoOcpp/Core/OcppServer.cpp - src/ArduinoOcpp/Core/OcppSocket.cpp - src/ArduinoOcpp/Core/OcppTime.cpp - src/ArduinoOcpp/MessagesV16/Authorize.cpp - src/ArduinoOcpp/MessagesV16/BootNotification.cpp - src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp - src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp - src/ArduinoOcpp/MessagesV16/ClearCache.cpp - src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp - src/ArduinoOcpp/MessagesV16/DataTransfer.cpp - src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp - src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp - src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp - src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp - src/ArduinoOcpp/MessagesV16/Heartbeat.cpp - src/ArduinoOcpp/MessagesV16/MeterValues.cpp - src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp - src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp - src/ArduinoOcpp/MessagesV16/Reset.cpp - src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp - src/ArduinoOcpp/MessagesV16/StartTransaction.cpp - src/ArduinoOcpp/MessagesV16/StatusNotification.cpp - src/ArduinoOcpp/MessagesV16/StopTransaction.cpp - src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp - src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp - src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp - src/ArduinoOcpp/Platform.cpp - src/ArduinoOcpp/SimpleOcppOperationFactory.cpp - src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp - src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp - src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp - src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp - src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp - src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp - src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp - src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp - src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp - src/ArduinoOcpp.cpp - src/ArduinoOcpp_c.cpp - src/ao_opts_impl.c - src/ArduinoOcpp/Core/FilesystemAdapter.cpp -) - -idf_component_register(SRCS ${AO_SRC} - INCLUDE_DIRS "./src" "${PROJECT_DIR}/include" - PRIV_REQUIRES spiffs) - -target_compile_options(${COMPONENT_TARGET} PUBLIC - -DAO_CUSTOM_WS - -DAO_CUSTOM_CONSOLE - -DAO_CUSTOM_UPDATER - -DAO_DEACTIVATE_FLASH - -DAO_USE_FILEAPI=ESPIDF_SPIFFS - -DAO_DBG_LEVEL=AO_DL_DEBUG - -DAO_TRAFFIC_OUT) diff --git a/README.md b/README.md index facb1c3c..44b2988e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ # Icon   ArduinoOcpp + +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/matth-x/ArduinoOcpp/PlatformIO%20CI?logo=github)](https://github.com/matth-x/ArduinoOcpp/actions) + OCPP-J 1.6 client for the ESP8266 and the ESP32 (more coming soon) Reference usage: [OpenEVSE](https://github.com/OpenEVSE/ESP32_WiFi_V4.x/blob/master/src/ocpp.cpp) @@ -32,11 +35,70 @@ For simple chargers, the necessary hardware and internet integration is usually ## Usage guide -**This feature branch is WIP. A usage guide will follow.** +Please take `examples/ESP/main.cpp` as the starting point for your first project. It is a minimal example which shows how to establish an OCPP connection and how to start and stop charging sessions. This guide explains the concepts for a minimal integration. + +- To get the library running, you have to install all dependencies (see the list below). In case you use PlatformIO, you can just add `matth-x/ArduinoOcpp` to your project using the PIO library manager. + +- In your project's `main` file, include `ArduinoOcpp.h`. + +- Before establishing an OCPP connection you have to ensure that your device has access to a Wi-Fi access point. All debug messages are printed on the standard serial (i.e. `Serial.print("debug msg")`). To redirect debug messages, please refer to `src/ArduinoOcpp/Platform.h`. + +- To connect to your OCPP Central System, call `OCPP_initialize(String OCPP_HOST, uint16_t OCPP_PORT, String OCPP_URL)`. You need to insert the address parameters according to the configuration of your central system. Internally, the library passes these parameters to the WebSocket object without further alteration. + - To secure the connection with TLS, you have to configure the WebSocket. Please take `examples/ESP-TLS/main.cpp` as an example. + +- In your `setup()` function, you can add the configuration functions from `ArduinoOcpp.h` to properly integrate your hardware. For example, the library needs access to the energy meter. All configuration functions are documented in `ArduinoOcpp.h`. + +- Add `OCPP_loop()` to your `loop()` function. + +**Sending OCPP operations** + +There are a couple of OCPP operations you can initialize on your EVSE. For example, to send a `Boot Notification`, use the function +```cpp +void bootNotification(const char *chargePointModel, const char *chargePointVendor, OnReceiveConfListener onConf = nullptr, ...)` +``` + +In practice, it looks like this: + +```cpp +void setup() { + + ... //other code including the initialization of Wi-Fi and OCPP + + bootNotification("My CP model name", "My company name", [] (JsonObject confMsg) { + //This callback is executed when the .conf() response from the central system arrives + Serial.print(F("BootNotification was answered. Central System clock: ")); + Serial.println(confMsg["currentTime"].as()); //"currentTime" is a field of the central system response + + //Notify your hardare that the BootNotification.conf() has arrived. E.g.: + //evseIsBooted = true; + }); + + ... //rest of setup() function; executed immediately as bootNotification() is non-blocking +} +``` + +The parameters `chargePointModel` and `chargePointVendor` are equivalent to the parameters in the `Boot Notification` as defined by the OCPP specification. The last parameter `OnReceiveConfListener onConf` is a callback function which the library executes when the central system has processed the operation and the ESP has received the `.conf()` response. Here you can add your device-specific behavior, e.g. flash a confirmation LED or unlock the connectors. If you don't need it, the last parameter is optional. + +**Receiving OCPP operations** + +The library also reacts on CS-initiated operations. You can add your own behavior there too. For example, to flash a LED on receipt of a `Set Charging Profile` request, use the following function. + +```cpp +setOnSetChargingProfileRequest([] (JsonObject payload) { + //... +}); +``` + +You can also process the original payload from the CS using the `payload` object. + +*To get started quickly with or without EVSE hardware, you can flash the sketch in `examples/SECC` onto your ESP. That example mimics a full OCPP communications controller as it would look like in a real charging station. You can build a charger prototype based on that example or just view the internal state using the device monitor.* ## Dependencies -- [bblanchon/ArduinoJson](https://github.com/bblanchon/ArduinoJson) +- [bblanchon/ArduinoJSON](https://github.com/bblanchon/ArduinoJson) (please upgrade to version `6.19.1`) +- [Links2004/arduinoWebSockets](https://github.com/Links2004/arduinoWebSockets) (please upgrade to version `2.3.6`) + +In case you use PlatformIO, you can copy all dependencies from `platformio.ini` into your own configuration file. Alternatively, you can install the full library with dependencies by adding `matth-x/ArduinoOcpp` in the PIO library manager. ## Supported operations diff --git a/src/ArduinoOcpp_c.cpp b/src/ArduinoOcpp_c.cpp deleted file mode 100644 index 855689c0..00000000 --- a/src/ArduinoOcpp_c.cpp +++ /dev/null @@ -1,181 +0,0 @@ -#include "ArduinoOcpp_c.h" -#include "ArduinoOcpp.h" - -#include - -ArduinoOcpp::OcppSocket *ocppSocket = nullptr; - -extern "C" void ao_initialize(AOcppSocket *osock) { - //OCPP_initialize("echo.websocket.events", 80, "ws://echo.websocket.events/"); - if (!osock) { - AO_DBG_ERR("osock is null"); - } - AO_DBG_ERR("no error"); - - ocppSocket = reinterpret_cast(osock); - - OCPP_initialize(*ocppSocket); -} - -extern "C" void ao_loop() { - OCPP_loop(); -} - -extern "C" void ao_set_console_out_c(void (*console_out)(const char *msg)) { - ao_set_console_out(console_out); -} - -#ifndef AO_RECEIVE_PAYLOAD_BUFSIZE -#define AO_RECEIVE_PAYLOAD_BUFSIZE 1024 -#endif - -char ao_recv_payload_buff [AO_RECEIVE_PAYLOAD_BUFSIZE] = {'\0'}; - -std::function adaptCb(OnOcppMessage cb) { - return [cb] (JsonObject payload) { - auto len = serializeJson(payload, ao_recv_payload_buff, AO_RECEIVE_PAYLOAD_BUFSIZE); - if (len <= 0) { - AO_DBG_WARN("Received payload buffer exceeded. Continue without payload"); - } - cb(len > 0 ? ao_recv_payload_buff : nullptr, len); - }; -} - -std::function adaptCb(void (*cb)()) { - return cb; -} - -ArduinoOcpp::OnReceiveErrorListener adaptCb(OnOcppError cb) { - return [cb] (const char *code, const char *description, JsonObject details) { - auto len = serializeJson(details, ao_recv_payload_buff, AO_RECEIVE_PAYLOAD_BUFSIZE); - if (len <= 0) { - AO_DBG_WARN("Received payload buffer exceeded. Continue without payload"); - } - cb(code, description, len > 0 ? ao_recv_payload_buff : "", len); - }; -} - -std::function adaptCb(SamplerBool cb) { - return cb; -} - -std::function adaptCb(SamplerString cb) { - return cb; -} - -std::function adaptCb(SamplerFloat cb) { - return cb; -} - -std::function adaptCb(SamplerInt cb) { - return cb; -} - -void ao_setPowerActiveImportSampler(SamplerFloat power) { - setPowerActiveImportSampler(adaptCb(power)); -} - -void ao_setEnergyActiveImportSampler(SamplerInt energy) { - setEnergyActiveImportSampler([energy] () -> float { - return (float) energy(); - }); -} - -void ao_setEvRequestsEnergySampler(SamplerBool evRequestsEnergy) { - setEvRequestsEnergySampler(adaptCb(evRequestsEnergy)); -} - -void ao_setConnectorEnergizedSampler(SamplerBool connectorEnergized) { - setConnectorEnergizedSampler(adaptCb(connectorEnergized)); -} - -void ao_setConnectorPluggedSampler(SamplerBool connectorPlugged) { - setConnectorPluggedSampler(adaptCb(connectorPlugged)); -} - -void ao_addConnectorErrorCodeSampler(SamplerString connectorErrorCode) { - addConnectorErrorCodeSampler(adaptCb(connectorErrorCode)); -} - -void ao_onChargingRateLimitChange(void (*chargingRateChanged)(float)) { - setOnChargingRateLimitChange(chargingRateChanged); -} - -void ao_onUnlockConnector(SamplerBool unlockConnector) { - setOnUnlockConnector(adaptCb(unlockConnector)); -} - -void ao_onRemoteStartTransactionSendConf(OnOcppMessage onSendConf) { - setOnRemoteStopTransactionSendConf(adaptCb(onSendConf)); -} - -void ao_onRemoteStopTransactionSendConf(OnOcppMessage onSendConf) { - setOnRemoteStopTransactionSendConf(adaptCb(onSendConf)); -} - -void ao_onRemoteStopTransactionRequest(OnOcppMessage onRequest) { - setOnRemoteStopTransactionReceiveReq(adaptCb(onRequest)); -} - -void ao_onResetSendConf(OnOcppMessage onSendConf) { - setOnResetSendConf(adaptCb(onSendConf)); -} - -extern "C" void ao_onResetRequest(OnOcppMessage onRequest) { - setOnResetReceiveReq(adaptCb(onRequest)); -} - -extern "C" void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { - bootNotification("model", "vendor", adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); -} - -void ao_bootNotification_full(const char *payloadJson, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { - DynamicJsonDocument *payload = new DynamicJsonDocument(JSON_OBJECT_SIZE(9) + 230 + 9); // BootNotification has at most 9 attributes with at most 230 chars + null terminators - auto err = deserializeJson(*payload, payloadJson); - if (err) { - AO_DBG_ERR("Could not process input: %s", err.c_str()); - (void)0; - } - - bootNotification(payload, adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); -} - -void ao_authorize(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { - authorize(idTag, adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); -} - -void ao_startTransaction(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { - startTransaction(idTag, adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); -} - -void ao_stopTransaction(OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { - stopTransaction(adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); -} - -int ao_getTransactionId() { - return getTransactionId(); -} - -bool ao_ocppPermitsCharge() { - return ocppPermitsCharge(); -} - -bool ao_isAvailable() { - return isAvailable(); -} - -void ao_beginSession(const char *idTag) { - return beginSession(idTag); -} - -void ao_endSession() { - return endSession(); -} - -bool ao_isInSession() { - return isInSession(); -} - -const char *ao_getSessionIdTag() { - return getSessionIdTag(); -} diff --git a/src/ArduinoOcpp_c.h b/src/ArduinoOcpp_c.h deleted file mode 100644 index 16e45b39..00000000 --- a/src/ArduinoOcpp_c.h +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef ARDUINOOCPP_C_H -#define ARDUINOOCPP_C_H - -#include - -struct AOcppSocket; -typedef struct AOcppSocket AOcppSocket; - -typedef void (*OnOcppMessage) (const char *payload, size_t len); -typedef void (*OnOcppAbort) (); -typedef void (*OnOcppTimeout) (); -typedef void (*OnOcppError) (const char *code, const char *description, const char *details_json, size_t details_len); - -typedef float (*SamplerFloat)(); -typedef int (*SamplerInt)(); -typedef bool (*SamplerBool)(); -typedef const char* (*SamplerString)(); - -#ifdef __cplusplus -extern "C" { -#endif - -void ao_initialize(AOcppSocket *osock); - -void ao_deinitialize(); - -void ao_loop(); - -void ao_set_console_out_c(void (*console_out)(const char *msg)); - -/* - * Feed lib with HW related data - */ - -void ao_setPowerActiveImportSampler(SamplerFloat power); - -void ao_setEnergyActiveImportSampler(SamplerInt energy); - -void ao_setEvRequestsEnergySampler(SamplerBool evRequestsEnergy); - -void ao_setConnectorEnergizedSampler(SamplerBool connectorEnergized); - -void ao_setConnectorPluggedSampler(SamplerBool connectorPlugged); - -void ao_addConnectorErrorCodeSampler(SamplerString connectorErrorCode); - -/* - * Execute HW related operations on EVSE - */ - -void ao_onChargingRateLimitChange(void (*chargingRateChanged)(float)); - -void ao_onUnlockConnector(SamplerBool unlockConnector); //true: success, false: failure - -/* - * Generic listeners for OCPP operations initiated by Central System - */ - -void ao_onRemoteStartTransactionSendConf(OnOcppMessage onSendConf); //important, energize the power plug here and capture the idTag - -void ao_onRemoteStopTransactionSendConf(OnOcppMessage onSendConf); //important, de-energize the power plug here -void ao_onRemoteStopTransactionRequest(OnOcppMessage onRequest); //optional, to de-energize the power plug immediately - -void ao_onResetSendConf(OnOcppMessage onSendConf); //important, reset your device here (i.e. call ESP.reset();) -void ao_onResetRequest(OnOcppMessage onRequest); //alternative: start reset timer here - -/* - * Initiate OCPP operations - */ - -void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); - -void ao_bootNotification_full(const char *payloadJson, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); - -void ao_authorize(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); - -void ao_startTransaction(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); - -void ao_stopTransaction(OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); - -/* - * Access OCPP state - */ - -int ao_getTransactionId(); //returns the ID of the current transaction. Returns -1 if called before or after an transaction - -bool ao_ocppPermitsCharge(); - -bool ao_isAvailable(); //if the charge point is operative or inoperative - -/* - * Charging session management - */ - -void ao_beginSession(const char *idTag); - -void ao_endSession(); - -bool ao_isInSession(); - -const char *ao_getSessionIdTag(); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/ao_opts.h b/src/ao_opts.h deleted file mode 100644 index 5a19cede..00000000 --- a/src/ao_opts.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef AOOPTS_H -#define AOOPTS_H - -#ifdef __cplusplus -extern "C" { -#endif - -unsigned long ao_tick_ms_impl(); -//unsigned int32_t ao_avail_heap_impl(); - -#ifdef __cplusplus -} -#endif - -#ifndef ao_tick_ms -#define ao_tick_ms ao_tick_ms_impl -#endif - -#ifndef ao_avail_heap -#define ao_avail_heap() 20000 -#endif - -//#ifndef AO_CONSOLE_PRINTF -//#define AO_CONSOLE_PRINTF(...) ESP_LOGI("[ocpp]", __VA_ARGS__) -//#endif - -#ifndef AO_CUSTOM_CONSOLE_MAXMSGSIZE -#define AO_CUSTOM_CONSOLE_MAXMSGSIZE 192 -#endif - -#endif diff --git a/src/ao_opts_impl.c b/src/ao_opts_impl.c deleted file mode 100644 index ce1b6559..00000000 --- a/src/ao_opts_impl.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "ao_opts.h" - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -unsigned long ao_tick_ms_impl() { - //return xTaskGetTickCount() / configTICK_RATE_HZ; - return (xTaskGetTickCount() * 1000UL) / configTICK_RATE_HZ; -} - -#ifdef __cplusplus -} -#endif From 8be2cdbf7a39a692dcc713eddcee2b650ea1662c Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sun, 26 Jun 2022 21:33:08 +0200 Subject: [PATCH 178/696] fix compilation errors --- src/ArduinoOcpp/Core/FilesystemAdapter.cpp | 24 ++++++++++++------- .../MessagesV16/StatusNotification.cpp | 3 ++- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/ArduinoOcpp/Core/FilesystemAdapter.cpp b/src/ArduinoOcpp/Core/FilesystemAdapter.cpp index d35789be..e8dad634 100644 --- a/src/ArduinoOcpp/Core/FilesystemAdapter.cpp +++ b/src/ArduinoOcpp/Core/FilesystemAdapter.cpp @@ -56,10 +56,18 @@ class ArduinoFileAdapter : public FileAdapter { } } - int read() override; - size_t read(char *buf, size_t len) override; - size_t write(const char *buf, size_t len) override; - size_t seek(size_t offset) override; + int read() override { + return file.read(); + }; + size_t read(char *buf, size_t len) override { + return file.readBytes(buf, len); + } + size_t write(const char *buf, size_t len) override { + return file.printf("%.*s", len, buf); + } + size_t seek(size_t offset) override { + return file.seek(offset); + } }; class ArduinoFilesystemAdapter : public FilesystemAdapter { @@ -110,8 +118,8 @@ class ArduinoFilesystemAdapter : public FilesystemAdapter { } int status = -1; - if (f.isFile()) { - size = f.size(); + if (!f.isDirectory()) { + *size = f.size(); status = 0; } else { //fetch more information for directory when ArduinoOcpp also uses them @@ -124,7 +132,7 @@ class ArduinoFilesystemAdapter : public FilesystemAdapter { std::unique_ptr open(const char *fn, const char *mode) override { File file = USE_FS.open(fn, mode); - if (file && file.isFile()) { + if (file && !file.isDirectory()) { return std::unique_ptr(new ArduinoFileAdapter(std::move(file))); } else { return nullptr; @@ -146,7 +154,7 @@ std::unique_ptr makeDefaultFilesystemAdapter(FilesystemOpt co new ArduinoFilesystemAdapter(config) ); - if (*fs) { + if (fs) { return fs; } else { return nullptr; diff --git a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp index c563a8dd..9c405a33 100644 --- a/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp +++ b/src/ArduinoOcpp/MessagesV16/StatusNotification.cpp @@ -36,7 +36,8 @@ const char *cstrFromOcppEveState(OcppEvseState state) { return "Faulted"; default: AO_DBG_ERR("OcppEvseState not specified"); - __attribute__ ((fallthrough)); + (void)0; + /* fall through */ case (OcppEvseState::NOT_SET): return "NOT_SET"; } From bbab921fca8feb69b4733088bec72d3885ebcc30 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sun, 26 Jun 2022 21:45:27 +0200 Subject: [PATCH 179/696] remove includes --- .../Core/ConfigurationContainerFlash.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp index 3aa078e8..0eea713d 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp @@ -7,21 +7,6 @@ #include -#if defined(ESP32) -#define USE_FS LITTLEFS -#else -#define USE_FS SPIFFS -#endif - -#ifndef AO_DEACTIVATE_FLASH -#if USE_FS == LITTLEFS -#include -#elif USE_FS == SPIFFS -#include -#define USE_FS SPIFFS -#endif -#endif - #define MAX_FILE_SIZE 4000 #define MAX_CONFIGURATIONS 50 #define MAX_CONFJSON_CAPACITY 4000 From 07f6c0caeda1f73c6c5b51385d0138dc2fe11046 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 27 Jun 2022 00:10:49 +0200 Subject: [PATCH 180/696] fix filesystem access --- examples/SECC/main.cpp | 7 ++++++- src/ArduinoOcpp.cpp | 3 ++- src/ArduinoOcpp/Core/FilesystemAdapter.cpp | 9 +++++---- src/ArduinoOcpp/Core/FilesystemAdapter.h | 2 +- .../Tasks/SmartCharging/SmartChargingService.cpp | 4 ++-- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/examples/SECC/main.cpp b/examples/SECC/main.cpp index 446dc6fa..1ca19e35 100644 --- a/examples/SECC/main.cpp +++ b/examples/SECC/main.cpp @@ -151,8 +151,13 @@ void setup() { /* * You can use ArduinoOcpp's internal configurations store for credentials other than those which are * specified by OCPP. To use it before ArduinoOcpp is initialized, you need to call configuration_init() + * + * This snippet also shows how to integrate a custom filesystem. Just subclass FilesystemAdapter and pass + * it to the library */ - ArduinoOcpp::configuration_init(ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail); + std::shared_ptr filesystem = ArduinoOcpp::EspWiFi::makeDefaultFilesystemAdapter(ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail); + ArduinoOcpp::configuration_init(filesystem); + filesystem = nullptr; /* * WiFiManager opens a captive portal, lets the user enter the WiFi credentials and provides a settings diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 4a9b6bf1..781fc9c1 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -32,6 +32,7 @@ OcppSocket *ocppSocket {nullptr}; #endif OcppEngine *ocppEngine {nullptr}; +std::shared_ptr filesystem; FilesystemOpt fileSystemOpt {}; float voltage_eff {230.f}; @@ -85,7 +86,7 @@ void OCPP_initialize(OcppSocket& ocppSocket, float V_eff, ArduinoOcpp::Filesyste voltage_eff = V_eff; fileSystemOpt = fsOpt; - std::shared_ptr filesystem = EspWiFi::makeDefaultFilesystemAdapter(fileSystemOpt); + filesystem = EspWiFi::makeDefaultFilesystemAdapter(fileSystemOpt); AO_DBG_DEBUG("filesystem %s", filesystem ? "loaded" : "error"); configuration_init(filesystem); //call before each other library call diff --git a/src/ArduinoOcpp/Core/FilesystemAdapter.cpp b/src/ArduinoOcpp/Core/FilesystemAdapter.cpp index e8dad634..1d3d720e 100644 --- a/src/ArduinoOcpp/Core/FilesystemAdapter.cpp +++ b/src/ArduinoOcpp/Core/FilesystemAdapter.cpp @@ -83,6 +83,8 @@ class ArduinoFilesystemAdapter : public FilesystemAdapter { if(!USE_FS.begin(config.formatOnFail())) { AO_DBG_ERR("Error while mounting LITTLEFS"); valid = false; + } else { + AO_DBG_DEBUG("LittleFS mount success"); } #elif AO_USE_FILEAPI == ARDUINO_SPIFFS //ESP8266 @@ -150,11 +152,10 @@ std::unique_ptr makeDefaultFilesystemAdapter(FilesystemOpt co return nullptr; } - auto fs = std::unique_ptr( - new ArduinoFilesystemAdapter(config) - ); + auto fs_concrete = new ArduinoFilesystemAdapter(config); + auto fs = std::unique_ptr(fs_concrete); - if (fs) { + if (*fs_concrete) { return fs; } else { return nullptr; diff --git a/src/ArduinoOcpp/Core/FilesystemAdapter.h b/src/ArduinoOcpp/Core/FilesystemAdapter.h index e32f7779..93c6af27 100644 --- a/src/ArduinoOcpp/Core/FilesystemAdapter.h +++ b/src/ArduinoOcpp/Core/FilesystemAdapter.h @@ -6,7 +6,7 @@ #define AO_FILESYSTEMADAPTER_H #ifndef AO_FILENAME_PREFIX -#define AO_FILENAME_PREFIX "/ao_store" +#define AO_FILENAME_PREFIX "" #endif #define ARDUINO_LITTLEFS 1 diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp index 79c564d4..b2dbbe84 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp @@ -300,8 +300,8 @@ void SmartChargingService::setChargingProfile(JsonObject json) { ChargingProfile *SmartChargingService::updateProfileStack(JsonObject json){ ChargingProfile *chargingProfile = new ChargingProfile(json); - if (AO_DBG_LEVEL >= AO_DL_INFO) { - AO_DBG_INFO("Charging Profile internal model:"); + if (AO_DBG_LEVEL >= AO_DL_VERBOSE) { + AO_DBG_VERBOSE("Charging Profile internal model:"); chargingProfile->printProfile(); } From 0e5dab47de87b08ff4ff7fe356bbe8f5dc489e4d Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 27 Jun 2022 11:35:31 +0200 Subject: [PATCH 181/696] restore files --- .github/workflows/pio.yaml | 47 ---------- CMakeLists.txt | 67 ++++++++++++++ README.md | 66 +------------- src/ArduinoOcpp/Platform.h | 2 + src/ArduinoOcpp_c.cpp | 181 +++++++++++++++++++++++++++++++++++++ src/ArduinoOcpp_c.h | 107 ++++++++++++++++++++++ src/ao_opts.h | 31 +++++++ src/ao_opts_impl.c | 30 ++++++ 8 files changed, 420 insertions(+), 111 deletions(-) delete mode 100644 .github/workflows/pio.yaml create mode 100644 CMakeLists.txt create mode 100644 src/ArduinoOcpp_c.cpp create mode 100644 src/ArduinoOcpp_c.h create mode 100644 src/ao_opts.h create mode 100644 src/ao_opts_impl.c diff --git a/.github/workflows/pio.yaml b/.github/workflows/pio.yaml deleted file mode 100644 index ab6248b7..00000000 --- a/.github/workflows/pio.yaml +++ /dev/null @@ -1,47 +0,0 @@ -name: PlatformIO CI - -on: - push: - branches: - - develop - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - matrix: - example: [examples/ESP/main.cpp, examples/ESP-TLS/main.cpp, examples/SECC/main.cpp] - include: - - example: examples/SECC/main.cpp - dashboard-extra: --lib="/tmp/tzapu/WiFiManager" - - steps: - - uses: actions/checkout@v2 - - name: Cache pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - name: Cache PlatformIO - uses: actions/cache@v2 - with: - path: ~/.platformio - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - - name: Set up Python - uses: actions/setup-python@v2 - - name: Install PlatformIO - run: | - python -m pip install --upgrade pip - pip install --upgrade platformio - - name: Install library dependencies - run: pio pkg install - - name: Extra dependencies for SECC example - if: ${{ matrix.dashboard-extra }} - run: git clone https://github.com/tzapu/WiFiManager.git /tmp/tzapu/WiFiManager - - name: Run PlatformIO - run: pio ci --lib="." --project-conf=platformio.ini ${{ matrix.dashboard-extra }} - env: - PLATFORMIO_CI_SRC: ${{ matrix.example }} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..56345c10 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,67 @@ +set(AO_SRC + src/ArduinoOcpp/Core/Configuration.cpp + src/ArduinoOcpp/Core/ConfigurationContainer.cpp + src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp + src/ArduinoOcpp/Core/ConfigurationKeyValue.cpp + src/ArduinoOcpp/Core/OcppConnection.cpp + src/ArduinoOcpp/Core/OcppEngine.cpp + src/ArduinoOcpp/Core/OcppMessage.cpp + src/ArduinoOcpp/Core/OcppModel.cpp + src/ArduinoOcpp/Core/OcppOperation.cpp + src/ArduinoOcpp/Core/OcppOperationTimeout.cpp + src/ArduinoOcpp/Core/OcppServer.cpp + src/ArduinoOcpp/Core/OcppSocket.cpp + src/ArduinoOcpp/Core/OcppTime.cpp + src/ArduinoOcpp/MessagesV16/Authorize.cpp + src/ArduinoOcpp/MessagesV16/BootNotification.cpp + src/ArduinoOcpp/MessagesV16/ChangeAvailability.cpp + src/ArduinoOcpp/MessagesV16/ChangeConfiguration.cpp + src/ArduinoOcpp/MessagesV16/ClearCache.cpp + src/ArduinoOcpp/MessagesV16/ClearChargingProfile.cpp + src/ArduinoOcpp/MessagesV16/DataTransfer.cpp + src/ArduinoOcpp/MessagesV16/DiagnosticsStatusNotification.cpp + src/ArduinoOcpp/MessagesV16/FirmwareStatusNotification.cpp + src/ArduinoOcpp/MessagesV16/GetConfiguration.cpp + src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp + src/ArduinoOcpp/MessagesV16/Heartbeat.cpp + src/ArduinoOcpp/MessagesV16/MeterValues.cpp + src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp + src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp + src/ArduinoOcpp/MessagesV16/Reset.cpp + src/ArduinoOcpp/MessagesV16/SetChargingProfile.cpp + src/ArduinoOcpp/MessagesV16/StartTransaction.cpp + src/ArduinoOcpp/MessagesV16/StatusNotification.cpp + src/ArduinoOcpp/MessagesV16/StopTransaction.cpp + src/ArduinoOcpp/MessagesV16/TriggerMessage.cpp + src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp + src/ArduinoOcpp/MessagesV16/UpdateFirmware.cpp + src/ArduinoOcpp/Platform.cpp + src/ArduinoOcpp/SimpleOcppOperationFactory.cpp + src/ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.cpp + src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp + src/ArduinoOcpp/Tasks/Diagnostics/DiagnosticsService.cpp + src/ArduinoOcpp/Tasks/FirmwareManagement/FirmwareService.cpp + src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp + src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp + src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp + src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp + src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp + src/ArduinoOcpp.cpp + src/ArduinoOcpp_c.cpp + src/ao_opts_impl.c + src/ArduinoOcpp/Core/FilesystemAdapter.cpp +) + +idf_component_register(SRCS ${AO_SRC} + INCLUDE_DIRS "./src" "${PROJECT_DIR}/include" + PRIV_REQUIRES spiffs) + +target_compile_options(${COMPONENT_TARGET} PUBLIC + -DAO_CUSTOM_WS + -DAO_CUSTOM_CONSOLE + -DAO_CUSTOM_UPDATER + -DAO_DEACTIVATE_FLASH + -DAO_USE_FILEAPI=ESPIDF_SPIFFS + -DAO_DBG_LEVEL=AO_DL_DEBUG + -DAO_TRAFFIC_OUT + -DAO_FILENAME_PREFIX="/ao_store") diff --git a/README.md b/README.md index 44b2988e..facb1c3c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,4 @@ # Icon   ArduinoOcpp - -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/matth-x/ArduinoOcpp/PlatformIO%20CI?logo=github)](https://github.com/matth-x/ArduinoOcpp/actions) - OCPP-J 1.6 client for the ESP8266 and the ESP32 (more coming soon) Reference usage: [OpenEVSE](https://github.com/OpenEVSE/ESP32_WiFi_V4.x/blob/master/src/ocpp.cpp) @@ -35,70 +32,11 @@ For simple chargers, the necessary hardware and internet integration is usually ## Usage guide -Please take `examples/ESP/main.cpp` as the starting point for your first project. It is a minimal example which shows how to establish an OCPP connection and how to start and stop charging sessions. This guide explains the concepts for a minimal integration. - -- To get the library running, you have to install all dependencies (see the list below). In case you use PlatformIO, you can just add `matth-x/ArduinoOcpp` to your project using the PIO library manager. - -- In your project's `main` file, include `ArduinoOcpp.h`. - -- Before establishing an OCPP connection you have to ensure that your device has access to a Wi-Fi access point. All debug messages are printed on the standard serial (i.e. `Serial.print("debug msg")`). To redirect debug messages, please refer to `src/ArduinoOcpp/Platform.h`. - -- To connect to your OCPP Central System, call `OCPP_initialize(String OCPP_HOST, uint16_t OCPP_PORT, String OCPP_URL)`. You need to insert the address parameters according to the configuration of your central system. Internally, the library passes these parameters to the WebSocket object without further alteration. - - To secure the connection with TLS, you have to configure the WebSocket. Please take `examples/ESP-TLS/main.cpp` as an example. - -- In your `setup()` function, you can add the configuration functions from `ArduinoOcpp.h` to properly integrate your hardware. For example, the library needs access to the energy meter. All configuration functions are documented in `ArduinoOcpp.h`. - -- Add `OCPP_loop()` to your `loop()` function. - -**Sending OCPP operations** - -There are a couple of OCPP operations you can initialize on your EVSE. For example, to send a `Boot Notification`, use the function -```cpp -void bootNotification(const char *chargePointModel, const char *chargePointVendor, OnReceiveConfListener onConf = nullptr, ...)` -``` - -In practice, it looks like this: - -```cpp -void setup() { - - ... //other code including the initialization of Wi-Fi and OCPP - - bootNotification("My CP model name", "My company name", [] (JsonObject confMsg) { - //This callback is executed when the .conf() response from the central system arrives - Serial.print(F("BootNotification was answered. Central System clock: ")); - Serial.println(confMsg["currentTime"].as()); //"currentTime" is a field of the central system response - - //Notify your hardare that the BootNotification.conf() has arrived. E.g.: - //evseIsBooted = true; - }); - - ... //rest of setup() function; executed immediately as bootNotification() is non-blocking -} -``` - -The parameters `chargePointModel` and `chargePointVendor` are equivalent to the parameters in the `Boot Notification` as defined by the OCPP specification. The last parameter `OnReceiveConfListener onConf` is a callback function which the library executes when the central system has processed the operation and the ESP has received the `.conf()` response. Here you can add your device-specific behavior, e.g. flash a confirmation LED or unlock the connectors. If you don't need it, the last parameter is optional. - -**Receiving OCPP operations** - -The library also reacts on CS-initiated operations. You can add your own behavior there too. For example, to flash a LED on receipt of a `Set Charging Profile` request, use the following function. - -```cpp -setOnSetChargingProfileRequest([] (JsonObject payload) { - //... -}); -``` - -You can also process the original payload from the CS using the `payload` object. - -*To get started quickly with or without EVSE hardware, you can flash the sketch in `examples/SECC` onto your ESP. That example mimics a full OCPP communications controller as it would look like in a real charging station. You can build a charger prototype based on that example or just view the internal state using the device monitor.* +**This feature branch is WIP. A usage guide will follow.** ## Dependencies -- [bblanchon/ArduinoJSON](https://github.com/bblanchon/ArduinoJson) (please upgrade to version `6.19.1`) -- [Links2004/arduinoWebSockets](https://github.com/Links2004/arduinoWebSockets) (please upgrade to version `2.3.6`) - -In case you use PlatformIO, you can copy all dependencies from `platformio.ini` into your own configuration file. Alternatively, you can install the full library with dependencies by adding `matth-x/ArduinoOcpp` in the PIO library manager. +- [bblanchon/ArduinoJson](https://github.com/bblanchon/ArduinoJson) ## Supported operations diff --git a/src/ArduinoOcpp/Platform.h b/src/ArduinoOcpp/Platform.h index 58cdd372..f6dfb757 100644 --- a/src/ArduinoOcpp/Platform.h +++ b/src/ArduinoOcpp/Platform.h @@ -5,6 +5,8 @@ #ifndef AO_PLATFORM_H #define AO_PLATFORM_H +#include + #ifdef AO_CUSTOM_CONSOLE #ifndef AO_CUSTOM_CONSOLE_MAXMSGSIZE diff --git a/src/ArduinoOcpp_c.cpp b/src/ArduinoOcpp_c.cpp new file mode 100644 index 00000000..855689c0 --- /dev/null +++ b/src/ArduinoOcpp_c.cpp @@ -0,0 +1,181 @@ +#include "ArduinoOcpp_c.h" +#include "ArduinoOcpp.h" + +#include + +ArduinoOcpp::OcppSocket *ocppSocket = nullptr; + +extern "C" void ao_initialize(AOcppSocket *osock) { + //OCPP_initialize("echo.websocket.events", 80, "ws://echo.websocket.events/"); + if (!osock) { + AO_DBG_ERR("osock is null"); + } + AO_DBG_ERR("no error"); + + ocppSocket = reinterpret_cast(osock); + + OCPP_initialize(*ocppSocket); +} + +extern "C" void ao_loop() { + OCPP_loop(); +} + +extern "C" void ao_set_console_out_c(void (*console_out)(const char *msg)) { + ao_set_console_out(console_out); +} + +#ifndef AO_RECEIVE_PAYLOAD_BUFSIZE +#define AO_RECEIVE_PAYLOAD_BUFSIZE 1024 +#endif + +char ao_recv_payload_buff [AO_RECEIVE_PAYLOAD_BUFSIZE] = {'\0'}; + +std::function adaptCb(OnOcppMessage cb) { + return [cb] (JsonObject payload) { + auto len = serializeJson(payload, ao_recv_payload_buff, AO_RECEIVE_PAYLOAD_BUFSIZE); + if (len <= 0) { + AO_DBG_WARN("Received payload buffer exceeded. Continue without payload"); + } + cb(len > 0 ? ao_recv_payload_buff : nullptr, len); + }; +} + +std::function adaptCb(void (*cb)()) { + return cb; +} + +ArduinoOcpp::OnReceiveErrorListener adaptCb(OnOcppError cb) { + return [cb] (const char *code, const char *description, JsonObject details) { + auto len = serializeJson(details, ao_recv_payload_buff, AO_RECEIVE_PAYLOAD_BUFSIZE); + if (len <= 0) { + AO_DBG_WARN("Received payload buffer exceeded. Continue without payload"); + } + cb(code, description, len > 0 ? ao_recv_payload_buff : "", len); + }; +} + +std::function adaptCb(SamplerBool cb) { + return cb; +} + +std::function adaptCb(SamplerString cb) { + return cb; +} + +std::function adaptCb(SamplerFloat cb) { + return cb; +} + +std::function adaptCb(SamplerInt cb) { + return cb; +} + +void ao_setPowerActiveImportSampler(SamplerFloat power) { + setPowerActiveImportSampler(adaptCb(power)); +} + +void ao_setEnergyActiveImportSampler(SamplerInt energy) { + setEnergyActiveImportSampler([energy] () -> float { + return (float) energy(); + }); +} + +void ao_setEvRequestsEnergySampler(SamplerBool evRequestsEnergy) { + setEvRequestsEnergySampler(adaptCb(evRequestsEnergy)); +} + +void ao_setConnectorEnergizedSampler(SamplerBool connectorEnergized) { + setConnectorEnergizedSampler(adaptCb(connectorEnergized)); +} + +void ao_setConnectorPluggedSampler(SamplerBool connectorPlugged) { + setConnectorPluggedSampler(adaptCb(connectorPlugged)); +} + +void ao_addConnectorErrorCodeSampler(SamplerString connectorErrorCode) { + addConnectorErrorCodeSampler(adaptCb(connectorErrorCode)); +} + +void ao_onChargingRateLimitChange(void (*chargingRateChanged)(float)) { + setOnChargingRateLimitChange(chargingRateChanged); +} + +void ao_onUnlockConnector(SamplerBool unlockConnector) { + setOnUnlockConnector(adaptCb(unlockConnector)); +} + +void ao_onRemoteStartTransactionSendConf(OnOcppMessage onSendConf) { + setOnRemoteStopTransactionSendConf(adaptCb(onSendConf)); +} + +void ao_onRemoteStopTransactionSendConf(OnOcppMessage onSendConf) { + setOnRemoteStopTransactionSendConf(adaptCb(onSendConf)); +} + +void ao_onRemoteStopTransactionRequest(OnOcppMessage onRequest) { + setOnRemoteStopTransactionReceiveReq(adaptCb(onRequest)); +} + +void ao_onResetSendConf(OnOcppMessage onSendConf) { + setOnResetSendConf(adaptCb(onSendConf)); +} + +extern "C" void ao_onResetRequest(OnOcppMessage onRequest) { + setOnResetReceiveReq(adaptCb(onRequest)); +} + +extern "C" void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { + bootNotification("model", "vendor", adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); +} + +void ao_bootNotification_full(const char *payloadJson, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { + DynamicJsonDocument *payload = new DynamicJsonDocument(JSON_OBJECT_SIZE(9) + 230 + 9); // BootNotification has at most 9 attributes with at most 230 chars + null terminators + auto err = deserializeJson(*payload, payloadJson); + if (err) { + AO_DBG_ERR("Could not process input: %s", err.c_str()); + (void)0; + } + + bootNotification(payload, adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); +} + +void ao_authorize(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { + authorize(idTag, adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); +} + +void ao_startTransaction(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { + startTransaction(idTag, adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); +} + +void ao_stopTransaction(OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { + stopTransaction(adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); +} + +int ao_getTransactionId() { + return getTransactionId(); +} + +bool ao_ocppPermitsCharge() { + return ocppPermitsCharge(); +} + +bool ao_isAvailable() { + return isAvailable(); +} + +void ao_beginSession(const char *idTag) { + return beginSession(idTag); +} + +void ao_endSession() { + return endSession(); +} + +bool ao_isInSession() { + return isInSession(); +} + +const char *ao_getSessionIdTag() { + return getSessionIdTag(); +} diff --git a/src/ArduinoOcpp_c.h b/src/ArduinoOcpp_c.h new file mode 100644 index 00000000..16e45b39 --- /dev/null +++ b/src/ArduinoOcpp_c.h @@ -0,0 +1,107 @@ +#ifndef ARDUINOOCPP_C_H +#define ARDUINOOCPP_C_H + +#include + +struct AOcppSocket; +typedef struct AOcppSocket AOcppSocket; + +typedef void (*OnOcppMessage) (const char *payload, size_t len); +typedef void (*OnOcppAbort) (); +typedef void (*OnOcppTimeout) (); +typedef void (*OnOcppError) (const char *code, const char *description, const char *details_json, size_t details_len); + +typedef float (*SamplerFloat)(); +typedef int (*SamplerInt)(); +typedef bool (*SamplerBool)(); +typedef const char* (*SamplerString)(); + +#ifdef __cplusplus +extern "C" { +#endif + +void ao_initialize(AOcppSocket *osock); + +void ao_deinitialize(); + +void ao_loop(); + +void ao_set_console_out_c(void (*console_out)(const char *msg)); + +/* + * Feed lib with HW related data + */ + +void ao_setPowerActiveImportSampler(SamplerFloat power); + +void ao_setEnergyActiveImportSampler(SamplerInt energy); + +void ao_setEvRequestsEnergySampler(SamplerBool evRequestsEnergy); + +void ao_setConnectorEnergizedSampler(SamplerBool connectorEnergized); + +void ao_setConnectorPluggedSampler(SamplerBool connectorPlugged); + +void ao_addConnectorErrorCodeSampler(SamplerString connectorErrorCode); + +/* + * Execute HW related operations on EVSE + */ + +void ao_onChargingRateLimitChange(void (*chargingRateChanged)(float)); + +void ao_onUnlockConnector(SamplerBool unlockConnector); //true: success, false: failure + +/* + * Generic listeners for OCPP operations initiated by Central System + */ + +void ao_onRemoteStartTransactionSendConf(OnOcppMessage onSendConf); //important, energize the power plug here and capture the idTag + +void ao_onRemoteStopTransactionSendConf(OnOcppMessage onSendConf); //important, de-energize the power plug here +void ao_onRemoteStopTransactionRequest(OnOcppMessage onRequest); //optional, to de-energize the power plug immediately + +void ao_onResetSendConf(OnOcppMessage onSendConf); //important, reset your device here (i.e. call ESP.reset();) +void ao_onResetRequest(OnOcppMessage onRequest); //alternative: start reset timer here + +/* + * Initiate OCPP operations + */ + +void ao_bootNotification(const char *chargePointModel, const char *chargePointVendor, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); + +void ao_bootNotification_full(const char *payloadJson, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); + +void ao_authorize(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); + +void ao_startTransaction(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); + +void ao_stopTransaction(OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError); + +/* + * Access OCPP state + */ + +int ao_getTransactionId(); //returns the ID of the current transaction. Returns -1 if called before or after an transaction + +bool ao_ocppPermitsCharge(); + +bool ao_isAvailable(); //if the charge point is operative or inoperative + +/* + * Charging session management + */ + +void ao_beginSession(const char *idTag); + +void ao_endSession(); + +bool ao_isInSession(); + +const char *ao_getSessionIdTag(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/ao_opts.h b/src/ao_opts.h new file mode 100644 index 00000000..5a19cede --- /dev/null +++ b/src/ao_opts.h @@ -0,0 +1,31 @@ +#ifndef AOOPTS_H +#define AOOPTS_H + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned long ao_tick_ms_impl(); +//unsigned int32_t ao_avail_heap_impl(); + +#ifdef __cplusplus +} +#endif + +#ifndef ao_tick_ms +#define ao_tick_ms ao_tick_ms_impl +#endif + +#ifndef ao_avail_heap +#define ao_avail_heap() 20000 +#endif + +//#ifndef AO_CONSOLE_PRINTF +//#define AO_CONSOLE_PRINTF(...) ESP_LOGI("[ocpp]", __VA_ARGS__) +//#endif + +#ifndef AO_CUSTOM_CONSOLE_MAXMSGSIZE +#define AO_CUSTOM_CONSOLE_MAXMSGSIZE 192 +#endif + +#endif diff --git a/src/ao_opts_impl.c b/src/ao_opts_impl.c new file mode 100644 index 00000000..ce1b6559 --- /dev/null +++ b/src/ao_opts_impl.c @@ -0,0 +1,30 @@ +#include "ao_opts.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned long ao_tick_ms_impl() { + //return xTaskGetTickCount() / configTICK_RATE_HZ; + return (xTaskGetTickCount() * 1000UL) / configTICK_RATE_HZ; +} + +#ifdef __cplusplus +} +#endif From 6701e7a5fcda87ffb6c8b81b27e92c1e5974d211 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 27 Jun 2022 12:07:07 +0200 Subject: [PATCH 182/696] missing includes; fix compilation issues --- CMakeLists.txt | 3 +++ src/ArduinoOcpp/MessagesV16/UnlockConnector.h | 1 + .../Tasks/Metering/ConnectorMeterValuesRecorder.cpp | 2 +- src/ArduinoOcpp/Tasks/Metering/MeterValue.h | 1 + src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp | 1 + src/ArduinoOcpp/Tasks/Metering/SampledValue.h | 1 + src/ArduinoOcpp_c.cpp | 2 +- 7 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 56345c10..cc4ae294 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,8 @@ set(AO_SRC src/ArduinoOcpp/Tasks/Heartbeat/HeartbeatService.cpp src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp + src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp + src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingService.cpp src/ArduinoOcpp.cpp @@ -60,6 +62,7 @@ target_compile_options(${COMPONENT_TARGET} PUBLIC -DAO_CUSTOM_WS -DAO_CUSTOM_CONSOLE -DAO_CUSTOM_UPDATER + -DAO_CUSTOM_RESET -DAO_DEACTIVATE_FLASH -DAO_USE_FILEAPI=ESPIDF_SPIFFS -DAO_DBG_LEVEL=AO_DL_DEBUG diff --git a/src/ArduinoOcpp/MessagesV16/UnlockConnector.h b/src/ArduinoOcpp/MessagesV16/UnlockConnector.h index 2659fd2b..a8b37220 100644 --- a/src/ArduinoOcpp/MessagesV16/UnlockConnector.h +++ b/src/ArduinoOcpp/MessagesV16/UnlockConnector.h @@ -7,6 +7,7 @@ #include #include +#include namespace ArduinoOcpp { namespace Ocpp16 { diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index 56620cd5..5cc05a98 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -73,7 +73,7 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { AO_DBG_DEBUG("Clock aligned measurement %ds: %s", dt, abs(dt) <= 60 ? - "in time (tolerance <= 60s)" : "off, e.g. because of first run. Ignore"); abs(-123); + "in time (tolerance <= 60s)" : "off, e.g. because of first run. Ignore"); if (abs(dt) <= 60) { //is measurement still "clock-aligned"? auto alignedMeterValues = alignedDataBuilder->takeSample(context.getOcppTime().getOcppTimestampNow(), ReadingContext::SampleClock); if (alignedMeterValues) { diff --git a/src/ArduinoOcpp/Tasks/Metering/MeterValue.h b/src/ArduinoOcpp/Tasks/Metering/MeterValue.h index 450f5d5b..541360d5 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeterValue.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeterValue.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp b/src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp index 4a570829..3ab56895 100644 --- a/src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp @@ -30,6 +30,7 @@ const char *cstrFromReadingContext(ReadingContext context) { return "Trigger"; default: AO_DBG_ERR("ReadingContext not specified"); + /* fall through */ case (ReadingContext::NOT_SET): return nullptr; } diff --git a/src/ArduinoOcpp/Tasks/Metering/SampledValue.h b/src/ArduinoOcpp/Tasks/Metering/SampledValue.h index 7debf013..f808bbe7 100644 --- a/src/ArduinoOcpp/Tasks/Metering/SampledValue.h +++ b/src/ArduinoOcpp/Tasks/Metering/SampledValue.h @@ -7,6 +7,7 @@ #include #include +#include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp_c.cpp b/src/ArduinoOcpp_c.cpp index 855689c0..bf5c6cc6 100644 --- a/src/ArduinoOcpp_c.cpp +++ b/src/ArduinoOcpp_c.cpp @@ -137,7 +137,7 @@ void ao_bootNotification_full(const char *payloadJson, OnOcppMessage onConfirmat (void)0; } - bootNotification(payload, adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); + bootNotification(std::unique_ptr(payload), adaptCb(onConfirmation), adaptCb(onAbort), adaptCb(onTimeout), adaptCb(onError)); } void ao_authorize(const char *idTag, OnOcppMessage onConfirmation, OnOcppAbort onAbort, OnOcppTimeout onTimeout, OnOcppError onError) { From 48ddb71a7857cf2dbcf50c8fafcdde3694ad1792 Mon Sep 17 00:00:00 2001 From: Matthias Akstaller <63792403+matth-x@users.noreply.github.com> Date: Tue, 28 Jun 2022 11:08:38 +0200 Subject: [PATCH 183/696] Update documentation --- README.md | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index e485854a..793edd6a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ # Icon   ArduinoOcpp + +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/matth-x/ArduinoOcpp/PlatformIO%20CI?logo=github)](https://github.com/matth-x/ArduinoOcpp/actions) + OCPP-J 1.6 client for the ESP8266 and the ESP32 (more coming soon) Reference usage: [OpenEVSE](https://github.com/OpenEVSE/ESP32_WiFi_V4.x/blob/master/src/ocpp.cpp) @@ -34,18 +37,15 @@ For simple chargers, the necessary hardware and internet integration is usually Please take `examples/ESP/main.cpp` as the starting point for your first project. It is a minimal example which shows how to establish an OCPP connection and how to start and stop charging sessions. This guide explains the concepts for a minimal integration. -- To get the library running, you have to install all dependencies (see the list below). In case you use PlatformIO, you can just add `matth-x/ArduinoOcpp` to your project using the PIO library manager. +- To install the dependencies, see the list below for a manual installation or add `matth-x/ArduinoOcpp` to your project using the PIO library manager. -- In your project's `main` file, include `ArduinoOcpp.h`. +- In your project's `main` file, include `ArduinoOcpp.h` and the Wi-Fi library. Initialize Wi-Fi and the Serial output. -- Before establishing an OCPP connection you have to ensure that your device has access to a Wi-Fi access point. All debug messages are printed on the standard serial (i.e. `Serial.print("debug msg")`). To redirect debug messages, please refer to `src/ArduinoOcpp/Platform.h`. +- To connect to the OCPP Central System, call `OCPP_initialize(const char *host, uint16_t port, const char *url)`. For a secure connection with TLS, you need to configure the WebSocket in advance. Please take `examples/ESP-TLS/main.cpp` as an example. -- To connect to your OCPP Central System, call `OCPP_initialize(String OCPP_HOST, uint16_t OCPP_PORT, String OCPP_URL)`. You need to insert the address parameters according to the configuration of your central system. Internally, the library passes these parameters to the WebSocket object without further alteration. - - To secure the connection with TLS, you have to configure the WebSocket. Please take `examples/ESP-TLS/main.cpp` as an example. +- In `setup()`, configure ArduinoOcpp with the hardware drivers. You can leave that part out for the first connection test. Please refer to `ArduinoOcpp.h` for a documentation about the supported EVSE peripherals. -- In your `setup()` function, you can add the configuration functions from `ArduinoOcpp.h` to properly integrate your hardware. For example, the library needs access to the energy meter. All configuration functions are documented in `ArduinoOcpp.h`. - -- Add `OCPP_loop()` to your `loop()` function. +- In `loop()`, add `OCPP_loop()`. **Sending OCPP operations** @@ -58,7 +58,6 @@ In practice, it looks like this: ```cpp void setup() { - ... //other code including the initialization of Wi-Fi and OCPP bootNotification("My CP model name", "My company name", [] (JsonObject confMsg) { @@ -66,8 +65,7 @@ void setup() { Serial.print(F("BootNotification was answered. Central System clock: ")); Serial.println(confMsg["currentTime"].as()); //"currentTime" is a field of the central system response - //Notify your hardare that the BootNotification.conf() has arrived. E.g.: - //evseIsBooted = true; + //evseIsBooted = true; <-- Example: Notify your hardare that the BootNotification.conf() has arrived }); ... //rest of setup() function; executed immediately as bootNotification() is non-blocking @@ -78,15 +76,15 @@ The parameters `chargePointModel` and `chargePointVendor` are equivalent to the **Receiving OCPP operations** -The library also reacts on CS-initiated operations. You can add your own behavior there too. For example, to flash a LED on receipt of a `Set Charging Profile` request, use the following function. +You can also add customized behavior to incoming OCPP messages. For example, to flash an LED on receipt of a `Set Charging Profile` request, use the following function. ```cpp setOnSetChargingProfileRequest([] (JsonObject payload) { - //... + //... will be executed every time this EVSE receives a new Charging Profile }); ``` -You can also process the original payload from the CS using the `payload` object. +Using the `payload` object you can access the original payload from the CS. *To get started quickly with or without EVSE hardware, you can flash the sketch in `examples/SECC` onto your ESP. That example mimics a full OCPP communications controller as it would look like in a real charging station. You can build a charger prototype based on that example or just view the internal state using the device monitor.* @@ -132,13 +130,9 @@ In case you use PlatformIO, you can copy all dependencies from `platformio.ini` ## Next development steps -- [x] introduce proper offline behavior and package loss / fault detection -- [x] handle fragmented input messages correctly -- [x] add support for multiple power connectors -- [x] add support for the ESP32 -- [ ] reach full compliance to OCPP 1.6 Smart Charging Profile +- [x] reach full compliance to OCPP 1.6 Smart Charging Profile - [ ] integrate Authorization Cache -- [ ] **get ready for OCPP 2.0.1** +- [ ] **get ready for OCPP 2.0.1 and ISO 15118** ## Further help From 67a32ec8fdd1dbcb7bf30fc66af834e9bade809e Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 28 Jun 2022 11:11:43 +0200 Subject: [PATCH 184/696] integrate GetCompositeSchedule --- README.md | 80 +++++----------- .../MessagesV16/GetCompositeSchedule.cpp | 95 +++++++++++++++++++ .../MessagesV16/GetCompositeSchedule.h | 36 +++++++ .../MessagesV16/RemoteStartTransaction.cpp | 1 + .../SimpleOcppOperationFactory.cpp | 3 + .../Metering/ConnectorMeterValuesRecorder.cpp | 2 +- .../SmartCharging/SmartChargingModel.cpp | 5 +- 7 files changed, 164 insertions(+), 58 deletions(-) create mode 100644 src/ArduinoOcpp/MessagesV16/GetCompositeSchedule.cpp create mode 100644 src/ArduinoOcpp/MessagesV16/GetCompositeSchedule.h diff --git a/README.md b/README.md index 44b2988e..fac6eacf 100644 --- a/README.md +++ b/README.md @@ -10,18 +10,18 @@ PlatformIO package: [ArduinoOcpp](https://platformio.org/lib/show/11975/ArduinoO Website: [www.arduino-ocpp.com](https://www.arduino-ocpp.com) -Full compatibility with the Arduino platform. Need a **FreeRTOS** version? Please [contact me](https://github.com/matth-x/ArduinoOcpp#further-help) +Fully integrated into the Arduino platform. Compatible with ESP-IDF and generic FreeRTOS ## Make your EVSE ready for OCPP :car::electric_plug::battery: You can build an OCPP Charge Point controller using the popular, Wi-Fi enabled microcontrollers ESP8266, ESP32 and comparable. This library allows your EVSE to communicate with an OCPP Central System and to participate in your Charging Network. -:heavy_check_mark: Works with [SteVe](https://github.com/RWTH-i5-IDSG/steve) and [The Mobility House OCPP package](https://github.com/mobilityhouse/ocpp) - -:heavy_check_mark: Passed compatibility tests with further commercial Central Systems +:heavy_check_mark: Works with [SteVe](https://github.com/RWTH-i5-IDSG/steve), [The Mobility House OCPP package](https://github.com/mobilityhouse/ocpp) and further commercial Central Systems :heavy_check_mark: Integrated and tested in many charging stations +:heavy_check_mark: Eligible for **public chargers**. Complies also with the legal requirements of the German Ladesäulenverordnung (LSV) + ### Features - handles the OCPP communication with the charging network @@ -37,18 +37,15 @@ For simple chargers, the necessary hardware and internet integration is usually Please take `examples/ESP/main.cpp` as the starting point for your first project. It is a minimal example which shows how to establish an OCPP connection and how to start and stop charging sessions. This guide explains the concepts for a minimal integration. -- To get the library running, you have to install all dependencies (see the list below). In case you use PlatformIO, you can just add `matth-x/ArduinoOcpp` to your project using the PIO library manager. +- To install the dependencies, see the list below for a manual installation or add `matth-x/ArduinoOcpp` to your project using the PIO library manager. -- In your project's `main` file, include `ArduinoOcpp.h`. +- In your project's `main` file, include `ArduinoOcpp.h` and the Wi-Fi library. Initialize Wi-Fi and the Serial output. -- Before establishing an OCPP connection you have to ensure that your device has access to a Wi-Fi access point. All debug messages are printed on the standard serial (i.e. `Serial.print("debug msg")`). To redirect debug messages, please refer to `src/ArduinoOcpp/Platform.h`. +- To connect to the OCPP Central System, call `OCPP_initialize(const char *host, uint16_t port, const char *url)`. For a secure connection with TLS, you need to configure the WebSocket in advance. Please take `examples/ESP-TLS/main.cpp` as an example. -- To connect to your OCPP Central System, call `OCPP_initialize(String OCPP_HOST, uint16_t OCPP_PORT, String OCPP_URL)`. You need to insert the address parameters according to the configuration of your central system. Internally, the library passes these parameters to the WebSocket object without further alteration. - - To secure the connection with TLS, you have to configure the WebSocket. Please take `examples/ESP-TLS/main.cpp` as an example. +- In `setup()`, configure ArduinoOcpp with the hardware drivers. You can leave that part out for the first connection test. Please refer to `ArduinoOcpp.h` for a documentation about the supported EVSE peripherals. -- In your `setup()` function, you can add the configuration functions from `ArduinoOcpp.h` to properly integrate your hardware. For example, the library needs access to the energy meter. All configuration functions are documented in `ArduinoOcpp.h`. - -- Add `OCPP_loop()` to your `loop()` function. +- In `loop()`, add `OCPP_loop()`. **Sending OCPP operations** @@ -61,7 +58,6 @@ In practice, it looks like this: ```cpp void setup() { - ... //other code including the initialization of Wi-Fi and OCPP bootNotification("My CP model name", "My company name", [] (JsonObject confMsg) { @@ -69,8 +65,7 @@ void setup() { Serial.print(F("BootNotification was answered. Central System clock: ")); Serial.println(confMsg["currentTime"].as()); //"currentTime" is a field of the central system response - //Notify your hardare that the BootNotification.conf() has arrived. E.g.: - //evseIsBooted = true; + //evseIsBooted = true; <-- Example: Notify your hardare that the BootNotification.conf() has arrived }); ... //rest of setup() function; executed immediately as bootNotification() is non-blocking @@ -81,67 +76,44 @@ The parameters `chargePointModel` and `chargePointVendor` are equivalent to the **Receiving OCPP operations** -The library also reacts on CS-initiated operations. You can add your own behavior there too. For example, to flash a LED on receipt of a `Set Charging Profile` request, use the following function. +You can also add customized behavior to incoming OCPP messages. For example, to flash an LED on receipt of a `Set Charging Profile` request, use the following function. ```cpp setOnSetChargingProfileRequest([] (JsonObject payload) { - //... + //... will be executed every time this EVSE receives a new Charging Profile }); ``` -You can also process the original payload from the CS using the `payload` object. +Using the `payload` object you can access the original payload from the CS. *To get started quickly with or without EVSE hardware, you can flash the sketch in `examples/SECC` onto your ESP. That example mimics a full OCPP communications controller as it would look like in a real charging station. You can build a charger prototype based on that example or just view the internal state using the device monitor.* ## Dependencies +Mandatory: + - [bblanchon/ArduinoJSON](https://github.com/bblanchon/ArduinoJson) (please upgrade to version `6.19.1`) + +If compiled with the Arduino integration: + - [Links2004/arduinoWebSockets](https://github.com/Links2004/arduinoWebSockets) (please upgrade to version `2.3.6`) In case you use PlatformIO, you can copy all dependencies from `platformio.ini` into your own configuration file. Alternatively, you can install the full library with dependencies by adding `matth-x/ArduinoOcpp` in the PIO library manager. ## Supported operations -| Operation name | supported | in progress | not supported | -| -------------- | :---------: | :-----------: | :-------------: | -| **Core profile** | -| `Authorize` | :heavy_check_mark: | -| `BootNotification` | :heavy_check_mark: | -| `ChangeAvailability` | :heavy_check_mark: | -| `ChangeConfiguration` | :heavy_check_mark: | -| `ClearCache` | :heavy_check_mark: | -| `DataTransfer` | :heavy_check_mark: | -| `GetConfiguration` | :heavy_check_mark: | -| `Heartbeat` | :heavy_check_mark: | -| `MeterValues` | :heavy_check_mark: | -| `RemoteStartTransaction` | :heavy_check_mark: | -| `RemoteStopTransaction` | :heavy_check_mark: | -| `Reset` | :heavy_check_mark: | -| `StartTransaction` | :heavy_check_mark: | -| `StatusNotification` | :heavy_check_mark: | -| `StopTransaction` | :heavy_check_mark: | -| `UnlockConnector` | :heavy_check_mark: | -| **Smart charging profile** | -| `ClearChargingProfile` | :heavy_check_mark: | -| `GetCompositeSchedule` | | | :heavy_multiplication_x: | -| `SetChargingProfile` | :heavy_check_mark: | -| **Remote trigger profile** | -| `TriggerMessage` | :heavy_check_mark: | -| **Firmware management** | -| `GetDiagnostics` | :heavy_check_mark: | -| `DiagnosticsStatusNotification` | :heavy_check_mark: | -| `FirmwareStatusNotification` | :heavy_check_mark: | -| `UpdateFirmware` | :heavy_check_mark: | +| Feature profile | supported | in progress | +| -------------- | :---------: | :-----------: | +| **Core** | :heavy_check_mark: | +| **Smart charging** | :heavy_check_mark: | +| **Remote trigger** | :heavy_check_mark: | +| **Firmware management** | :heavy_check_mark: | ## Next development steps -- [x] introduce proper offline behavior and package loss / fault detection -- [x] handle fragmented input messages correctly -- [x] add support for multiple power connectors -- [x] add support for the ESP32 -- [ ] reach full compliance to OCPP 1.6 Smart Charging Profile +- [x] reach full compliance to OCPP 1.6 Smart Charging Profile - [ ] integrate Authorization Cache -- [ ] **get ready for OCPP 2.0.1** +- [ ] **get ready for OCPP 2.0.1 and ISO 15118** ## Further help diff --git a/src/ArduinoOcpp/MessagesV16/GetCompositeSchedule.cpp b/src/ArduinoOcpp/MessagesV16/GetCompositeSchedule.cpp new file mode 100644 index 00000000..96399201 --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/GetCompositeSchedule.cpp @@ -0,0 +1,95 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#include +#include +#include +#include +#include + +#include + +using ArduinoOcpp::Ocpp16::GetCompositeSchedule; + +GetCompositeSchedule::GetCompositeSchedule() { + +} + +const char* GetCompositeSchedule::getOcppOperationType(){ + return "GetCompositeSchedule"; +} + +void GetCompositeSchedule::processReq(JsonObject payload) { + + connectorId = payload["connectorId"] | -1; + duration = payload["duration"] | 0; + auto unitString = payload["chargingRateUnit"] | "W"; + + if (unitString[0] == 'A' || unitString[0] == 'a') { + chargingRateUnit = ChargingRateUnitType::Amp; + } else if (unitString[0] == 'W' || unitString[0] == 'w') { + chargingRateUnit = ChargingRateUnitType::Watt; + } else { + errorCode = "PropertyConstraintViolation"; + } + + if (ocppModel && ocppModel->getChargePointStatusService()) { + if (connectorId >= ocppModel->getChargePointStatusService()->getNumConnectors()) { + errorCode = "PropertyConstraintViolation"; + } + } + + if (connectorId < 0 || !payload.containsKey("duration")) { + errorCode = "FormatViolation"; + } + + if (!ocppModel || !ocppModel->getSmartChargingService()) { + AO_DBG_ERR("SmartChargingService not initialized! Ignore request"); + errorCode = "NotSupported"; + } +} + +std::unique_ptr GetCompositeSchedule::createConf(){ + if (!ocppModel || !ocppModel->getSmartChargingService()) { + return nullptr; + } + + auto scService = ocppModel->getSmartChargingService(); + ChargingSchedule *composite = scService->getCompositeSchedule(connectorId, duration); + DynamicJsonDocument *compositeJson {nullptr}; + + if (composite) { + compositeJson = composite->toJsonDocument(); + } + + std::unique_ptr doc; + + if (compositeJson) { + doc.reset(new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + JSONDATE_LENGTH + 1 + compositeJson->capacity())); + JsonObject payload = doc->to(); + payload["status"] = "Accepted"; + if (connectorId > 0) + payload["connectorId"] = connectorId; + + char scheduleStart [JSONDATE_LENGTH + 1] {'\0'}; + auto startSchedule = (*compositeJson)["startSchedule"] | ""; + if (startSchedule[0] != '\0') { + strncpy(scheduleStart, startSchedule, JSONDATE_LENGTH + 1); + } else { + ocppModel->getOcppTime().getOcppTimestampNow().toJsonString(scheduleStart, JSONDATE_LENGTH + 1); + } + payload["scheduleStart"] = scheduleStart; + + payload["chargingSchedule"] = *compositeJson; + } else { + doc.reset(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); + JsonObject payload = doc->to(); + payload["status"] = "Rejected"; + } + + delete compositeJson; + delete composite; + + return doc; +} diff --git a/src/ArduinoOcpp/MessagesV16/GetCompositeSchedule.h b/src/ArduinoOcpp/MessagesV16/GetCompositeSchedule.h new file mode 100644 index 00000000..00482796 --- /dev/null +++ b/src/ArduinoOcpp/MessagesV16/GetCompositeSchedule.h @@ -0,0 +1,36 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef GETCOMPOSITESCHEDULE_H +#define GETCOMPOSITESCHEDULE_H + +#include +#include +#include + +namespace ArduinoOcpp { +namespace Ocpp16 { + +class GetCompositeSchedule : public OcppMessage { +private: + int connectorId {-1}; + otime_t duration {0}; + ChargingRateUnitType chargingRateUnit {ChargingRateUnitType::Watt}; + + const char *errorCode {nullptr}; +public: + GetCompositeSchedule(); + + const char* getOcppOperationType(); + + void processReq(JsonObject payload); + + std::unique_ptr createConf(); + + const char *getErrorCode() {return errorCode;} +}; + +} //end namespace Ocpp16 +} //end namespace ArduinoOcpp +#endif diff --git a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp index 39835d7a..864035d0 100644 --- a/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/RemoteStartTransaction.cpp @@ -92,6 +92,7 @@ std::unique_ptr RemoteStartTransaction::createConf(){ bool ret = scService->clearChargingProfile([clearProfileId](int id, int, ChargingProfilePurposeType, int) { return id == clearProfileId; }); + (void)ret; *sRmtProfileId = -1; AO_DBG_DEBUG("Cleared Charging Profile from previous RemoteStartTx: %s", ret ? "success" : "already cleared"); diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 590818e5..274ac806 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -214,6 +215,8 @@ std::unique_ptr makeOcppOperation(const char *messageType, int co } else if (!strcmp(messageType, "BootNotification")) { msg = std::unique_ptr(new Ocpp16::BootNotification()); operation->setOnReceiveReqListener(onBootNotificationRequest); + } else if (!strcmp(messageType, "GetCompositeSchedule")) { + msg = std::unique_ptr(new Ocpp16::GetCompositeSchedule()); } else if (!strcmp(messageType, "Heartbeat")) { msg = std::unique_ptr(new Ocpp16::Heartbeat()); } else if (!strcmp(messageType, "MeterValues")) { diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index 56620cd5..5cc05a98 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -73,7 +73,7 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { AO_DBG_DEBUG("Clock aligned measurement %ds: %s", dt, abs(dt) <= 60 ? - "in time (tolerance <= 60s)" : "off, e.g. because of first run. Ignore"); abs(-123); + "in time (tolerance <= 60s)" : "off, e.g. because of first run. Ignore"); if (abs(dt) <= 60) { //is measurement still "clock-aligned"? auto alignedMeterValues = alignedDataBuilder->takeSample(context.getOcppTime().getOcppTimestampNow(), ReadingContext::SampleClock); if (alignedMeterValues) { diff --git a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp index 2fd2a28b..1a4b469e 100644 --- a/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp +++ b/src/ArduinoOcpp/Tasks/SmartCharging/SmartChargingModel.cpp @@ -371,9 +371,8 @@ bool ChargingProfile::checkTransactionAssignment(int txId, int profileId) { return transactionId == txId; //return if they do match } - AO_DBG_ERR("Check error"); - //neither txIds nor profileIDs apply - return false; + AO_DBG_DEBUG("Neither txIds nor profileIDs apply"); + return true; } int ChargingProfile::getStackLevel(){ From f1e289454c860129ef248f96e74be6968e2b791f Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 28 Jun 2022 11:20:45 +0200 Subject: [PATCH 185/696] add interface for custom meters --- src/ArduinoOcpp_c.cpp | 19 +++++++++++++++++++ src/ArduinoOcpp_c.h | 2 ++ 2 files changed, 21 insertions(+) diff --git a/src/ArduinoOcpp_c.cpp b/src/ArduinoOcpp_c.cpp index bf5c6cc6..03fbc26a 100644 --- a/src/ArduinoOcpp_c.cpp +++ b/src/ArduinoOcpp_c.cpp @@ -81,6 +81,25 @@ void ao_setEnergyActiveImportSampler(SamplerInt energy) { }); } +void ao_addMeterValueSampler_Int(SamplerInt sampler, const char *measurand, const char *phase, const char *unit) { + + ArduinoOcpp::SampledValueProperties properties; + if (measurand) + properties.setMeasurand(measurand); + if (phase) + properties.setPhase(phase); + if (unit) + properties.setUnit(unit); + + auto adaptSampler = [sampler] (ArduinoOcpp::ReadingContext) -> int32_t { + return sampler(); + }; + + auto reader = new ArduinoOcpp::SampledValueSamplerConcrete>(properties, adaptSampler); + addMeterValueSampler( + std::unique_ptr>>(reader)); +} + void ao_setEvRequestsEnergySampler(SamplerBool evRequestsEnergy) { setEvRequestsEnergySampler(adaptCb(evRequestsEnergy)); } diff --git a/src/ArduinoOcpp_c.h b/src/ArduinoOcpp_c.h index 16e45b39..9f4235e8 100644 --- a/src/ArduinoOcpp_c.h +++ b/src/ArduinoOcpp_c.h @@ -36,6 +36,8 @@ void ao_setPowerActiveImportSampler(SamplerFloat power); void ao_setEnergyActiveImportSampler(SamplerInt energy); +void ao_addMeterValueSampler_Int(SamplerInt sampler, const char *measurand, const char *phase, const char *unit); + void ao_setEvRequestsEnergySampler(SamplerBool evRequestsEnergy); void ao_setConnectorEnergizedSampler(SamplerBool connectorEnergized); From 3e103b076fe8aff51f93c76adeada7ebea81323f Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 4 Jul 2022 18:05:54 +0200 Subject: [PATCH 186/696] send txData in StopTransaction --- examples/SECC/main.cpp | 1 - .../MessagesV16/StartTransaction.cpp | 2 +- .../MessagesV16/StopTransaction.cpp | 28 ++++++++++++- src/ArduinoOcpp/MessagesV16/StopTransaction.h | 6 ++- .../Metering/ConnectorMeterValuesRecorder.cpp | 39 +++++++++++++++++-- .../Metering/ConnectorMeterValuesRecorder.h | 5 ++- .../Tasks/Metering/MeteringService.cpp | 14 ++++++- .../Tasks/Metering/MeteringService.h | 4 +- 8 files changed, 85 insertions(+), 14 deletions(-) diff --git a/examples/SECC/main.cpp b/examples/SECC/main.cpp index 1ca19e35..97d4cf25 100644 --- a/examples/SECC/main.cpp +++ b/examples/SECC/main.cpp @@ -157,7 +157,6 @@ void setup() { */ std::shared_ptr filesystem = ArduinoOcpp::EspWiFi::makeDefaultFilesystemAdapter(ArduinoOcpp::FilesystemOpt::Use_Mount_FormatOnFail); ArduinoOcpp::configuration_init(filesystem); - filesystem = nullptr; /* * WiFiManager opens a captive portal, lets the user enter the WiFi credentials and provides a settings diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp index bee6d6cf..fa501eca 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp @@ -28,7 +28,7 @@ const char* StartTransaction::getOcppOperationType() { void StartTransaction::initiate() { if (ocppModel && ocppModel->getMeteringService()) { auto meteringService = ocppModel->getMeteringService(); - meterStart = meteringService->readEnergyActiveImportRegister(connectorId); + meterStart = meteringService->readTxEnergyMeter(connectorId, ReadingContext::TransactionBegin); } if (ocppModel) { diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp index 6466a85c..7b214cd7 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include using ArduinoOcpp::Ocpp16::StopTransaction; @@ -24,7 +25,8 @@ void StopTransaction::initiate() { if (ocppModel && ocppModel->getMeteringService()) { auto meteringService = ocppModel->getMeteringService(); - meterStop = meteringService->readEnergyActiveImportRegister(connectorId); + meterStop = meteringService->readTxEnergyMeter(connectorId, ReadingContext::TransactionEnd); + transactionData = meteringService->createStopTxMeterData(connectorId); } if (ocppModel) { @@ -52,7 +54,27 @@ std::unique_ptr StopTransaction::createReq() { return nullptr; } - auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(5) + (JSONDATE_LENGTH + 1) + (REASON_LEN_MAX + 1))); + std::vector> txDataJson; + size_t txDataJson_size = 0; + for (auto mv = transactionData.begin(); mv != transactionData.end(); mv++) { + auto mvJson = (*mv)->toJson(); + if (!mvJson) { + return nullptr; + } + txDataJson_size += mvJson->capacity(); + txDataJson.emplace_back(std::move(mvJson)); + } + + DynamicJsonDocument txDataDoc = DynamicJsonDocument(JSON_ARRAY_SIZE(txDataJson.size()) + txDataJson_size); + for (auto mvJson = txDataJson.begin(); mvJson != txDataJson.end(); mvJson++) { + txDataDoc.add(**mvJson); + } + + auto doc = std::unique_ptr(new DynamicJsonDocument( + JSON_OBJECT_SIZE(6) + //total of 6 fields + (JSONDATE_LENGTH + 1) + //timestamp string + (REASON_LEN_MAX + 1) + //reason string + txDataDoc.capacity())); JsonObject payload = doc->to(); if (meterStop && *meterStop) { @@ -74,6 +96,8 @@ std::unique_ptr StopTransaction::createReq() { payload["reason"] = reason; } + payload["transactionData"] = txDataDoc; + return doc; } diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.h b/src/ArduinoOcpp/MessagesV16/StopTransaction.h index 08a91957..eaaacde8 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.h @@ -8,9 +8,12 @@ #include #include #include -#include namespace ArduinoOcpp { + +class SampledValue; +class MeterValue; + namespace Ocpp16 { class StopTransaction : public OcppMessage { @@ -19,6 +22,7 @@ class StopTransaction : public OcppMessage { std::unique_ptr meterStop {nullptr}; OcppTimestamp otimestamp; char reason [REASON_LEN_MAX] {'\0'}; + std::vector> transactionData; public: StopTransaction(int connectorId, const char *reason = nullptr); diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index 5cc05a98..8d68985c 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -120,6 +120,17 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { auto connector = context.getConnectorStatus(connectorId); if (connector && connector->getTransactionId() != lastTransactionId) { //transaction break + + //take a transaction-related sample which is aligned to the transaction break + if (connector->getTransactionId() >= 0 && lastTransactionId < 0) { + auto sampleStartTx = stopTxnSampledDataBuilder->takeSample(context.getOcppTime().getOcppTimestampNow(), ReadingContext::TransactionBegin); + if (sampleStartTx) { + stopTxnSampledData.push_back(std::move(sampleStartTx)); + } + } else if (connector->getTransactionId() >= 0 && lastTransactionId > 0) { + AO_DBG_ERR("Cannot switch txId"); + } + MeterValues *meterValues = nullptr; if (!sampledData.empty()) { meterValues = new MeterValues(std::move(sampledData), connectorId, lastTransactionId); @@ -189,11 +200,31 @@ void ConnectorMeterValuesRecorder::addMeterValueSampler(std::unique_ptr ConnectorMeterValuesRecorder::readEnergyActiveImportRegister() { +std::unique_ptr ConnectorMeterValuesRecorder::readTxEnergyMeter(ReadingContext reason) { if (energySamplerIndex >= 0 && energySamplerIndex < samplers.size()) { - return samplers[energySamplerIndex]->takeValue(ReadingContext::NOT_SET); + return samplers[energySamplerIndex]->takeValue(reason); } else { - AO_DBG_DEBUG("Called readEnergyActiveImportRegister(), but no energySampler or handling strategy set"); - return 0; + AO_DBG_DEBUG("Called readTxEnergyMeter(), but no energySampler or handling strategy set"); + return nullptr; + } +} + +std::vector> ConnectorMeterValuesRecorder::createStopTxMeterData() { + + //create final StopTxSample + if (*MeterValueSampleInterval >= 1) { //... only if sampled Meter Values are activated + auto sampleStopTx = stopTxnSampledDataBuilder->takeSample(context.getOcppTime().getOcppTimestampNow(), ReadingContext::TransactionEnd); + if (sampleStopTx) { + stopTxnSampledData.push_back(std::move(sampleStopTx)); + } } + + //concatenate sampled and aligned meter data; clear all StopTX data in this object + auto res{std::move(stopTxnSampledData)}; + res.insert(res.end(), std::make_move_iterator(stopTxnAlignedData.begin()), + std::make_move_iterator(stopTxnAlignedData.end())); + stopTxnSampledData.clear(); //make vectors defined after moving from them + stopTxnAlignedData.clear(); + + return std::move(res); } diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h index a145438b..7377ffed 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h @@ -74,11 +74,12 @@ class ConnectorMeterValuesRecorder { void addMeterValueSampler(std::unique_ptr meterValueSampler); - std::unique_ptr readEnergyActiveImportRegister(); + std::unique_ptr readTxEnergyMeter(ReadingContext context); OcppMessage *takeTriggeredMeterValues(); - OcppMessage *getStopTransactionData(); + std::vector> createStopTxMeterData(); + }; } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp index c5522f78..f7567cec 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.cpp @@ -53,12 +53,12 @@ void MeteringService::addMeterValueSampler(int connectorId, std::unique_ptraddMeterValueSampler(std::move(meterValueSampler)); } -std::unique_ptr MeteringService::readEnergyActiveImportRegister(int connectorId) { +std::unique_ptr MeteringService::readTxEnergyMeter(int connectorId, ReadingContext context) { if (connectorId < 0 || connectorId >= connectors.size()) { AO_DBG_ERR("connectorId is out of bounds"); return nullptr; } - return connectors[connectorId]->readEnergyActiveImportRegister(); + return connectors[connectorId]->readTxEnergyMeter(context); } std::unique_ptr MeteringService::takeTriggeredMeterValues(int connectorId) { @@ -80,3 +80,13 @@ std::unique_ptr MeteringService::takeTriggeredMeterValues(int con AO_DBG_ERR("Could not find connector"); return nullptr; } + +std::vector> MeteringService::createStopTxMeterData(int connectorId) { + if (connectorId < 0 || connectorId >= connectors.size()) { + AO_DBG_ERR("connectorId is out of bounds"); + return std::vector>(); + } + auto& connector = connectors[connectorId]; + + return connector->createStopTxMeterData(); +} diff --git a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h index 4b51defe..bad4ca29 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeteringService.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeteringService.h @@ -36,10 +36,12 @@ class MeteringService { void addMeterValueSampler(int connectorId, std::unique_ptr meterValueSampler); - std::unique_ptr readEnergyActiveImportRegister(int connectorId); + std::unique_ptr readTxEnergyMeter(int connectorId, ReadingContext reason); std::unique_ptr takeTriggeredMeterValues(int connectorId); //snapshot of all meters now + std::vector> createStopTxMeterData(int connectorId); + int getNumConnectors() {return connectors.size();} }; From 707ef4b2ee20fff29aa7f4184291f813a3da98a3 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 4 Jul 2022 18:36:49 +0200 Subject: [PATCH 187/696] fix data types --- .../Tasks/Metering/ConnectorMeterValuesRecorder.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index 8d68985c..2633828a 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -60,7 +60,7 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { if (*ClockAlignedDataInterval >= 1) { - if (alignedData.size() >= *MeterValuesAlignedDataMaxLength) { + if (alignedData.size() >= (size_t) *MeterValuesAlignedDataMaxLength) { auto meterValues = new MeterValues(std::move(alignedData), connectorId, -1); alignedData.clear(); return meterValues; @@ -111,7 +111,7 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { if (*MeterValueSampleInterval >= 1) { //record periodic tx data - if (sampledData.size() >= *MeterValuesSampledDataMaxLength) { + if (sampledData.size() >= (size_t) *MeterValuesSampledDataMaxLength) { auto meterValues = new MeterValues(std::move(sampledData), connectorId, lastTransactionId); sampledData.clear(); return meterValues; @@ -201,7 +201,7 @@ void ConnectorMeterValuesRecorder::addMeterValueSampler(std::unique_ptr ConnectorMeterValuesRecorder::readTxEnergyMeter(ReadingContext reason) { - if (energySamplerIndex >= 0 && energySamplerIndex < samplers.size()) { + if (energySamplerIndex >= 0 && (size_t) energySamplerIndex < samplers.size()) { return samplers[energySamplerIndex]->takeValue(reason); } else { AO_DBG_DEBUG("Called readTxEnergyMeter(), but no energySampler or handling strategy set"); @@ -220,7 +220,7 @@ std::vector> ConnectorMeterValuesRecorder::createSto } //concatenate sampled and aligned meter data; clear all StopTX data in this object - auto res{std::move(stopTxnSampledData)}; + decltype(stopTxnSampledData) res {std::move(stopTxnSampledData)}; res.insert(res.end(), std::make_move_iterator(stopTxnAlignedData.begin()), std::make_move_iterator(stopTxnAlignedData.end())); stopTxnSampledData.clear(); //make vectors defined after moving from them From bbbc1f74d66943aa3574ecfe950a087a3b47214d Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 4 Jul 2022 19:37:57 +0200 Subject: [PATCH 188/696] update facade --- src/ArduinoOcpp.cpp | 33 +++++++++++++++++++++++++++++++++ src/ArduinoOcpp.h | 3 +-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 781fc9c1..a9495203 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -224,6 +224,39 @@ void addMeterValueSampler(std::unique_ptr meterValueSampler model.getMeteringService()->addMeterValueSampler(OCPP_ID_OF_CONNECTOR, std::move(meterValueSampler)); //connectorId=1 } +void addMeterValueSampler(std::function value, const char *measurand, const char *unit, const char *location, const char *phase) { + if (!ocppEngine) { + AO_DBG_ERR("Please call OCPP_initialize before"); + return; + } + + if (!value) { + AO_DBG_ERR("value undefined"); + return; + } + + if (!measurand) { + measurand = "Energy.Active.Import.Register"; + AO_DBG_WARN("Measurand unspecified; assume %s", measurand); + } + + SampledValueProperties properties; + properties.setMeasurand(measurand); //mandatory for AO + + if (unit) + properties.setUnit(unit); + if (location) + properties.setLocation(location); + if (phase) + properties.setPhase(phase); + + auto valueSampler = std::unique_ptr>>( + new ArduinoOcpp::SampledValueSamplerConcrete>( + properties, + [value] (ArduinoOcpp::ReadingContext) -> int32_t {return value();})); + addMeterValueSampler(std::move(valueSampler)); +} + void setEvRequestsEnergySampler(std::function evRequestsEnergy) { if (!ocppEngine) { AO_DBG_ERR("Please call OCPP_initialize before"); diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 40014f94..c02c19af 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -55,6 +55,7 @@ void setPowerActiveImportSampler(std::function power); void setEnergyActiveImportSampler(std::function energy); void addMeterValueSampler(std::unique_ptr meterValueSampler); +void addMeterValueSampler(std::function value, const char *measurand = nullptr, const char *unit = nullptr, const char *location = nullptr, const char *phase = nullptr); void setEvRequestsEnergySampler(std::function evRequestsEnergy); @@ -62,8 +63,6 @@ void setConnectorEnergizedSampler(std::function connectorEnergized); void setConnectorPluggedSampler(std::function connectorPlugged); -//void setConnectorFaultedSampler(std::function connectorFailed); - void addConnectorErrorCodeSampler(std::function connectorErrorCode); /* From e8202a9d68ebe724c097128dc795dbe8f86f4a14 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 4 Jul 2022 23:03:20 +0200 Subject: [PATCH 189/696] omit txData in stopTx if empty --- src/ArduinoOcpp/MessagesV16/StopTransaction.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp index 7b214cd7..69a85f27 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp @@ -96,7 +96,9 @@ std::unique_ptr StopTransaction::createReq() { payload["reason"] = reason; } - payload["transactionData"] = txDataDoc; + if (!transactionData.empty()) { + payload["transactionData"] = txDataDoc; + } return doc; } From 8741300eea920b884ebf2c71eb6fe12f3c98799b Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Tue, 5 Jul 2022 16:35:35 +0200 Subject: [PATCH 190/696] remove obsolete txId tracking --- src/ArduinoOcpp/MessagesV16/MeterValues.cpp | 4 ++-- src/ArduinoOcpp/MessagesV16/MeterValues.h | 3 +-- .../Tasks/Metering/ConnectorMeterValuesRecorder.cpp | 8 ++++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp index 87559f96..cb7b01cb 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp @@ -15,8 +15,8 @@ MeterValues::MeterValues() { } -MeterValues::MeterValues(std::vector>&& meterValue, int connectorId, int transactionId) - : meterValue{std::move(meterValue)}, connectorId{connectorId}, transactionId{transactionId} { +MeterValues::MeterValues(std::vector>&& meterValue, int connectorId) + : meterValue{std::move(meterValue)}, connectorId{connectorId} { } diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.h b/src/ArduinoOcpp/MessagesV16/MeterValues.h index 24f4be0e..898899ba 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.h +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.h @@ -19,10 +19,9 @@ class MeterValues : public OcppMessage { std::vector> meterValue; int connectorId = 0; - int transactionId = -1; public: - MeterValues(std::vector>&& meterValue, int connectorId, int transactionId); + MeterValues(std::vector>&& meterValue, int connectorId); MeterValues(); //for debugging only. Make this for the server pendant diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index 2633828a..a856e2ba 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -61,7 +61,7 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { if (*ClockAlignedDataInterval >= 1) { if (alignedData.size() >= (size_t) *MeterValuesAlignedDataMaxLength) { - auto meterValues = new MeterValues(std::move(alignedData), connectorId, -1); + auto meterValues = new MeterValues(std::move(alignedData), connectorId); alignedData.clear(); return meterValues; } @@ -112,7 +112,7 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { //record periodic tx data if (sampledData.size() >= (size_t) *MeterValuesSampledDataMaxLength) { - auto meterValues = new MeterValues(std::move(sampledData), connectorId, lastTransactionId); + auto meterValues = new MeterValues(std::move(sampledData), connectorId); sampledData.clear(); return meterValues; } @@ -133,7 +133,7 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { MeterValues *meterValues = nullptr; if (!sampledData.empty()) { - meterValues = new MeterValues(std::move(sampledData), connectorId, lastTransactionId); + meterValues = new MeterValues(std::move(sampledData), connectorId); sampledData.clear(); } lastTransactionId = connector->getTransactionId(); @@ -182,7 +182,7 @@ OcppMessage *ConnectorMeterValuesRecorder::takeTriggeredMeterValues() { decltype(sampledData) mv_now; mv_now.push_back(std::move(sample)); - return new MeterValues(std::move(mv_now), connectorId, txId_now); + return new MeterValues(std::move(mv_now), connectorId); } void ConnectorMeterValuesRecorder::setPowerSampler(PowerSampler ps){ From de19d2a00446b25d53d82e6f96584308f726eaa9 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 7 Jul 2022 11:01:32 +0200 Subject: [PATCH 191/696] fix missing debug message null check --- src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 8c809ff3..67f44dff 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -287,7 +287,7 @@ const char *ConnectorStatus::getErrorCode() { } void ConnectorStatus::beginSession(const char *sessionIdTag) { - AO_DBG_DEBUG("Begin session with idTag %s, overwriting idTag %s", sessionIdTag, idTag); + AO_DBG_DEBUG("Begin session with idTag %s, overwriting idTag %s", sessionIdTag != nullptr ? sessionIdTag : "", idTag); if (!sessionIdTag || *sessionIdTag == '\0') { //input string is empty snprintf(idTag, IDTAG_LEN_MAX + 1, "A0-00-00-00"); From cba8cf2172fcb618cad095b4a49f3f00024d2021 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 7 Jul 2022 11:30:02 +0200 Subject: [PATCH 192/696] update StartTx trigger --- src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 67f44dff..ac671131 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -61,7 +61,9 @@ ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) * - instruct the OCMF meter to begin a transaction (if OCMF meter handler is set) */ txTriggerConditions.push_back([this] () -> TxCondition { - return getSessionIdTag() == nullptr ? TxCondition::Inactive : TxCondition::Active; + if (!session) + return TxCondition::Inactive; + return getSessionIdTag() ? TxCondition::Active : TxCondition::Inactive; }); txEnableSequence.push_back([this] (TxCondition cond) -> TxEnableState { if (onOcmfMeterPollTx) { From cf3f90530e9900a5f897f0085e5aa99fffe0110d Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 14 Jul 2022 10:20:12 +0200 Subject: [PATCH 193/696] add energy meter timeout; small changes --- .../MessagesV16/GetCompositeSchedule.cpp | 5 ++++- .../MessagesV16/GetDiagnostics.cpp | 2 +- src/ArduinoOcpp/MessagesV16/MeterValues.cpp | 19 +++++++++++++++---- src/ArduinoOcpp/MessagesV16/MeterValues.h | 4 ++++ .../MessagesV16/StartTransaction.cpp | 11 ++++++++++- .../MessagesV16/StartTransaction.h | 1 + .../MessagesV16/StopTransaction.cpp | 11 ++++++++++- src/ArduinoOcpp/MessagesV16/StopTransaction.h | 1 + .../ChargePointStatus/ConnectorStatus.cpp | 5 ++--- src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp | 5 ++--- 10 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/GetCompositeSchedule.cpp b/src/ArduinoOcpp/MessagesV16/GetCompositeSchedule.cpp index 96399201..686dc864 100644 --- a/src/ArduinoOcpp/MessagesV16/GetCompositeSchedule.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetCompositeSchedule.cpp @@ -52,7 +52,10 @@ void GetCompositeSchedule::processReq(JsonObject payload) { std::unique_ptr GetCompositeSchedule::createConf(){ if (!ocppModel || !ocppModel->getSmartChargingService()) { - return nullptr; + auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(1))); + JsonObject payload = doc->to(); + payload["status"] = "Rejected"; + return doc; } auto scService = ocppModel->getSmartChargingService(); diff --git a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp index 18289acf..6dd5fd72 100644 --- a/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp +++ b/src/ArduinoOcpp/MessagesV16/GetDiagnostics.cpp @@ -57,7 +57,7 @@ std::unique_ptr GetDiagnostics::createConf(){ fileName = ocppModel->getDiagnosticsService()->requestDiagnosticsUpload(location, retries, retryInterval, startTime, stopTime); } else { AO_DBG_WARN("DiagnosticsService has not been initialized before! Please have a look at ArduinoOcpp.cpp for an example. Abort"); - return nullptr; + return createEmptyDocument(); } if (fileName.empty()) { diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp index cb7b01cb..b9edaf99 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.cpp +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.cpp @@ -10,6 +10,8 @@ using ArduinoOcpp::Ocpp16::MeterValues; +#define ENERGY_METER_TIMEOUT_MS 30 * 1000 //after waiting for 30s, send MeterValues without missing readings + //can only be used for echo server debugging MeterValues::MeterValues() { @@ -28,6 +30,10 @@ const char* MeterValues::getOcppOperationType(){ return "MeterValues"; } +void MeterValues::initiate() { + emTimeout = ao_tick_ms(); +} + std::unique_ptr MeterValues::createReq() { size_t capacity = 0; @@ -35,11 +41,16 @@ std::unique_ptr MeterValues::createReq() { std::vector> entries; for (auto value = meterValue.begin(); value != meterValue.end(); value++) { auto entry = (*value)->toJson(); - if (!entry) { - return nullptr; + if (entry) { + capacity += entry->capacity(); + entries.push_back(std::move(entry)); + } else { + if (ao_tick_ms() - emTimeout < ENERGY_METER_TIMEOUT_MS) { + return nullptr; + } else { + AO_DBG_ERR("Energy meter timeout!"); + } } - capacity += entry->capacity(); - entries.push_back(std::move(entry)); } capacity += JSON_OBJECT_SIZE(3); diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.h b/src/ArduinoOcpp/MessagesV16/MeterValues.h index 898899ba..3b477d15 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.h +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.h @@ -20,6 +20,8 @@ class MeterValues : public OcppMessage { int connectorId = 0; + ulong emTimeout = 0; + public: MeterValues(std::vector>&& meterValue, int connectorId); @@ -29,6 +31,8 @@ class MeterValues : public OcppMessage { const char* getOcppOperationType(); + void initiate() override; + std::unique_ptr createReq(); void processConf(JsonObject payload); diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp index fa501eca..a52229e7 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp @@ -10,6 +10,8 @@ using ArduinoOcpp::Ocpp16::StartTransaction; +#define ENERGY_METER_TIMEOUT_MS 30 * 1000 //after waiting for 30s, send StartTx without start reading + StartTransaction::StartTransaction(int connectorId) : connectorId(connectorId) { } @@ -63,6 +65,8 @@ void StartTransaction::initiate() { transactionRev = connector->getTransactionWriteCount(); } + emTimeout = ao_tick_ms(); + AO_DBG_INFO("StartTransaction initiated"); } @@ -70,7 +74,9 @@ std::unique_ptr StartTransaction::createReq() { if (meterStart && !*meterStart) { //meterStart not ready yet - return nullptr; + if (ao_tick_ms() - emTimeout < ENERGY_METER_TIMEOUT_MS) { + return nullptr; + } } auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(5) + (JSONDATE_LENGTH + 1) + (IDTAG_LEN_MAX + 1))); @@ -79,6 +85,9 @@ std::unique_ptr StartTransaction::createReq() { payload["connectorId"] = connectorId; if (meterStart && *meterStart) { payload["meterStart"] = meterStart->toInteger(); + } else { + AO_DBG_ERR("Energy meter timeout"); + payload["meterStart"] = -1; } if (otimestamp > MIN_TIME) { diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.h b/src/ArduinoOcpp/MessagesV16/StartTransaction.h index 153da753..c06b8715 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.h @@ -20,6 +20,7 @@ class StartTransaction : public OcppMessage { OcppTimestamp otimestamp; char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; uint16_t transactionRev = 0; + ulong emTimeout = 0; public: StartTransaction(int connectorId); diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp index 69a85f27..d110e7e4 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp @@ -11,6 +11,8 @@ using ArduinoOcpp::Ocpp16::StopTransaction; +#define ENERGY_METER_TIMEOUT_MS 60 * 1000 //after waiting for 60s, send StopTx without start reading + StopTransaction::StopTransaction(int connectorId, const char *reason) : connectorId(connectorId) { if (reason) { snprintf(this->reason, REASON_LEN_MAX, "%s", reason); @@ -44,6 +46,8 @@ void StopTransaction::initiate() { } } + emTimeout = ao_tick_ms(); + AO_DBG_INFO("StopTransaction initiated!"); } @@ -51,7 +55,9 @@ std::unique_ptr StopTransaction::createReq() { if (meterStop && !*meterStop) { //meterStop not ready yet - return nullptr; + if (ao_tick_ms() - emTimeout < ENERGY_METER_TIMEOUT_MS) { + return nullptr; + } } std::vector> txDataJson; @@ -79,6 +85,9 @@ std::unique_ptr StopTransaction::createReq() { if (meterStop && *meterStop) { payload["meterStop"] = meterStop->toInteger(); + } else { + AO_DBG_ERR("Energy meter timeout"); + payload["meterStart"] = -1; } if (otimestamp > MIN_TIME) { diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.h b/src/ArduinoOcpp/MessagesV16/StopTransaction.h index eaaacde8..5e7e2918 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.h @@ -23,6 +23,7 @@ class StopTransaction : public OcppMessage { OcppTimestamp otimestamp; char reason [REASON_LEN_MAX] {'\0'}; std::vector> transactionData; + ulong emTimeout = 0; public: StopTransaction(int connectorId, const char *reason = nullptr); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index ac671131..50df0d9a 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -183,9 +183,8 @@ OcppMessage *ConnectorStatus::loop() { } if (txTrigger == TxCondition::Active) { - for (auto trigger = txTriggerConditions.begin(); trigger != txTriggerConditions.end(); trigger++) { - auto result = trigger->operator()(); - if (result == TxCondition::Active) { + for (auto trigger : txTriggerConditions) { + if (trigger() == TxCondition::Active) { txEnable = TxEnableState::Pending; } else { txTrigger = TxCondition::Inactive; diff --git a/src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp b/src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp index 27fe8904..658547fb 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp @@ -29,10 +29,9 @@ std::unique_ptr MeterValue::toJson() { auto jsonPayload = result->to(); char timestampStr [JSONDATE_LENGTH + 1] = {'\0'}; - if (!timestamp.toJsonString(timestampStr, JSONDATE_LENGTH + 1)) { - return nullptr; + if (timestamp.toJsonString(timestampStr, JSONDATE_LENGTH + 1)) { + jsonPayload["timestamp"] = timestampStr; } - jsonPayload["timestamp"] = timestampStr; auto jsonMeterValue = jsonPayload.createNestedArray("sampledValue"); for (auto entry = entries.begin(); entry != entries.end(); entry++) { jsonMeterValue.add(**entry); From f647e2fa5ff18a68b8e651a4ba11bfd88c985c03 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 7 Jul 2022 11:30:02 +0200 Subject: [PATCH 194/696] update StartTx trigger --- src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index 67f44dff..ac671131 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -61,7 +61,9 @@ ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) * - instruct the OCMF meter to begin a transaction (if OCMF meter handler is set) */ txTriggerConditions.push_back([this] () -> TxCondition { - return getSessionIdTag() == nullptr ? TxCondition::Inactive : TxCondition::Active; + if (!session) + return TxCondition::Inactive; + return getSessionIdTag() ? TxCondition::Active : TxCondition::Inactive; }); txEnableSequence.push_back([this] (TxCondition cond) -> TxEnableState { if (onOcmfMeterPollTx) { From fbeed4fa0865022f22e11e29bd7533097c7ec770 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Fri, 29 Jul 2022 14:50:03 +0200 Subject: [PATCH 195/696] encapsulate JSON file read / write --- .../Core/ConfigurationContainerFlash.cpp | 1 + src/ArduinoOcpp/Core/FilesystemAdapter.cpp | 12 +- src/ArduinoOcpp/Core/FilesystemAdapter.h | 27 +--- src/ArduinoOcpp/Core/FilesystemUtils.cpp | 121 ++++++++++++++++++ src/ArduinoOcpp/Core/FilesystemUtils.h | 46 +++++++ 5 files changed, 181 insertions(+), 26 deletions(-) create mode 100644 src/ArduinoOcpp/Core/FilesystemUtils.cpp create mode 100644 src/ArduinoOcpp/Core/FilesystemUtils.h diff --git a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp index 0eea713d..46b4f2f2 100644 --- a/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp +++ b/src/ArduinoOcpp/Core/ConfigurationContainerFlash.cpp @@ -3,6 +3,7 @@ // MIT License #include +#include #include #include diff --git a/src/ArduinoOcpp/Core/FilesystemAdapter.cpp b/src/ArduinoOcpp/Core/FilesystemAdapter.cpp index 1d3d720e..c43c89c1 100644 --- a/src/ArduinoOcpp/Core/FilesystemAdapter.cpp +++ b/src/ArduinoOcpp/Core/FilesystemAdapter.cpp @@ -135,6 +135,7 @@ class ArduinoFilesystemAdapter : public FilesystemAdapter { std::unique_ptr open(const char *fn, const char *mode) override { File file = USE_FS.open(fn, mode); if (file && !file.isDirectory()) { + AO_DBG_DEBUG("File open successful: %s", fn); return std::unique_ptr(new ArduinoFileAdapter(std::move(file))); } else { return nullptr; @@ -145,7 +146,13 @@ class ArduinoFilesystemAdapter : public FilesystemAdapter { }; }; -std::unique_ptr makeDefaultFilesystemAdapter(FilesystemOpt config) { +std::weak_ptr filesystemCache; + +std::shared_ptr makeDefaultFilesystemAdapter(FilesystemOpt config) { + + if (auto cached = filesystemCache.lock()) { + return cached; + } if (!config.accessAllowed()) { AO_DBG_DEBUG("Access to Arduino FS not allowed by config"); @@ -153,7 +160,8 @@ std::unique_ptr makeDefaultFilesystemAdapter(FilesystemOpt co } auto fs_concrete = new ArduinoFilesystemAdapter(config); - auto fs = std::unique_ptr(fs_concrete); + auto fs = std::shared_ptr(fs_concrete); + filesystemCache = fs; if (*fs_concrete) { return fs; diff --git a/src/ArduinoOcpp/Core/FilesystemAdapter.h b/src/ArduinoOcpp/Core/FilesystemAdapter.h index 93c6af27..ec7e6e34 100644 --- a/src/ArduinoOcpp/Core/FilesystemAdapter.h +++ b/src/ArduinoOcpp/Core/FilesystemAdapter.h @@ -9,6 +9,8 @@ #define AO_FILENAME_PREFIX "" #endif +#define MAX_PATH_SIZE 30 + #define ARDUINO_LITTLEFS 1 #define ARDUINO_SPIFFS 2 #define ESPIDF_SPIFFS 3 @@ -28,29 +30,6 @@ class FileAdapter { virtual int read() = 0; }; -class ArduinoJsonFileAdapter { -private: - FileAdapter *file; -public: - ArduinoJsonFileAdapter(FileAdapter *file) : file(file) { } - - size_t readBytes(char *buf, size_t len) { - return file->read(buf, len); - } - - int read() { - return file->read(); - } - - size_t write(const uint8_t *buf, size_t len) { - return file->write((const char*) buf, len); - } - - size_t write(uint8_t c) { - return file->write((const char*) &c, 1); - } -}; - class FilesystemAdapter { public: virtual ~FilesystemAdapter() = default; @@ -92,7 +71,7 @@ class FilesystemAdapter { namespace ArduinoOcpp { namespace EspWiFi { -std::unique_ptr makeDefaultFilesystemAdapter(FilesystemOpt config); +std::shared_ptr makeDefaultFilesystemAdapter(FilesystemOpt config); } //end namespace EspWiFi } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Core/FilesystemUtils.cpp b/src/ArduinoOcpp/Core/FilesystemUtils.cpp new file mode 100644 index 00000000..390b6d6a --- /dev/null +++ b/src/ArduinoOcpp/Core/FilesystemUtils.cpp @@ -0,0 +1,121 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#include +#include +#include //FilesystemOpt +#include + +#define MAX_JSON_CAPACITY 4096 + +using namespace ArduinoOcpp; + +std::unique_ptr FilesystemUtils::loadJson(std::shared_ptr filesystem, const char *fn) { + if (!filesystem || !fn || *fn == '\0') { + AO_DBG_ERR("Format error"); + return nullptr; + } + + if (strnlen(fn, MAX_PATH_SIZE) >= MAX_PATH_SIZE) { + AO_DBG_ERR("Fn too long: %.*s", MAX_PATH_SIZE, fn); + return nullptr; + } + + size_t fsize = 0; + if (filesystem->stat(fn, &fsize) != 0) { + AO_DBG_DEBUG("File does not exist: %s", fn); + return nullptr; + } + + if (fsize < 2) { + AO_DBG_ERR("File too small for JSON, collect %s", fn); + filesystem->remove(fn); + return nullptr; + } + + auto file = filesystem->open(fn, "r"); + if (!file) { + AO_DBG_ERR("Could not open file %s", fn); + return nullptr; + } + + size_t capacity = (3 * fsize) / 2; + if (capacity < 32) { + capacity = 32; + } + if (capacity > MAX_JSON_CAPACITY) { + capacity = MAX_JSON_CAPACITY; + } + + auto doc = std::unique_ptr(nullptr); + DeserializationError err = DeserializationError::NoMemory; + ArduinoJsonFileAdapter fileReader {file.get()}; + + while (err == DeserializationError::NoMemory && capacity <= MAX_JSON_CAPACITY) { + + doc.reset(new DynamicJsonDocument(capacity)); + err = deserializeJson(*doc, fileReader); + + capacity *= 3; + capacity /= 2; + + file->seek(0); //rewind file to beginning + } + + if (err) { + AO_DBG_ERR("Error deserializing file %s: %s", fn, err.c_str()); + //skip this file + return nullptr; + } + + AO_DBG_DEBUG("Loaded JSON file: %s", fn); + serializeJson(*doc, Serial); + Serial.println(); + return doc; +} + +bool FilesystemUtils::storeJson(std::shared_ptr filesystem, const char *fn, const DynamicJsonDocument& doc) { + if (!filesystem || !fn || *fn == '\0') { + AO_DBG_ERR("Format error"); + return false; + } + + if (strnlen(fn, MAX_PATH_SIZE) >= MAX_PATH_SIZE) { + AO_DBG_ERR("Fn too long: %.*s", MAX_PATH_SIZE, fn); + return false; + } + + if (doc.isNull() || doc.overflowed()) { + AO_DBG_ERR("Invalid JSON %s", fn); + return false; + } + + size_t file_size = 0; + if (filesystem->stat(fn, &file_size) == 0) { + filesystem->remove(fn); + } + + auto file = filesystem->open(fn, "w"); + if (!file) { + AO_DBG_ERR("Could not open file %s", fn); + return false; + } + + ArduinoJsonFileAdapter fileWriter {file.get()}; + + size_t written = serializeJson(doc, fileWriter); + written = serializeJson(doc, Serial); + + if (written < 2) { + AO_DBG_ERR("Error writing file %s", fn); + if (filesystem->stat(fn, &file_size) == 0) { + AO_DBG_DEBUG("Collect invalid file %s", fn); + filesystem->remove(fn); + } + return false; + } + + AO_DBG_DEBUG("Wrote JSON file: %s", fn); + return true; +} diff --git a/src/ArduinoOcpp/Core/FilesystemUtils.h b/src/ArduinoOcpp/Core/FilesystemUtils.h new file mode 100644 index 00000000..13af63cf --- /dev/null +++ b/src/ArduinoOcpp/Core/FilesystemUtils.h @@ -0,0 +1,46 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef AO_FILESYSTEMUTILS_H +#define AO_FILESYSTEMUTILS_H + +#include +#include +#include + +namespace ArduinoOcpp { + +class ArduinoJsonFileAdapter { +private: + FileAdapter *file; +public: + ArduinoJsonFileAdapter(FileAdapter *file) : file(file) { } + + size_t readBytes(char *buf, size_t len) { + return file->read(buf, len); + } + + int read() { + return file->read(); + } + + size_t write(const uint8_t *buf, size_t len) { + return file->write((const char*) buf, len); + } + + size_t write(uint8_t c) { + return file->write((const char*) &c, 1); + } +}; + +namespace FilesystemUtils { + +std::unique_ptr loadJson(std::shared_ptr filesystem, const char *fn); +bool storeJson(std::shared_ptr filesystem, const char *fn, const DynamicJsonDocument& doc); + +} + +} + +#endif From a9b4f3d3384ed488d079db8ffbc3e7a97a37232a Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 3 Aug 2022 17:12:46 +0200 Subject: [PATCH 196/696] internet connection loss simulation --- src/ArduinoOcpp/Core/OcppSocket.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ArduinoOcpp/Core/OcppSocket.h b/src/ArduinoOcpp/Core/OcppSocket.h index f1fb6db7..c9db90e1 100644 --- a/src/ArduinoOcpp/Core/OcppSocket.h +++ b/src/ArduinoOcpp/Core/OcppSocket.h @@ -27,9 +27,14 @@ class OcppSocket { class OcppEchoSocket : public OcppSocket { private: ReceiveTXTcallback receiveTXT; + + bool connected = true; //for simulating connection losses public: void loop() override { } bool sendTXT(std::string &out) override { + if (!connected) { + return true; + } if (receiveTXT) { return receiveTXT(out.c_str(), out.length()); } else { @@ -39,6 +44,9 @@ class OcppEchoSocket : public OcppSocket { void setReceiveTXTcallback(ReceiveTXTcallback &receiveTXT) override { this->receiveTXT = receiveTXT; } + + void setConnected(bool connected) {this->connected = connected;} + bool isConnected() {return connected;} }; } //end namespace ArduinoOcpp From 59c2912b9b0dcdab492eba5030acff6711de64ce Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 8 Aug 2022 10:56:11 +0200 Subject: [PATCH 197/696] MeterValue restore from flash --- src/ArduinoOcpp/MessagesV16/MeterValues.h | 4 ++- .../Metering/ConnectorMeterValuesRecorder.cpp | 8 +---- .../Metering/ConnectorMeterValuesRecorder.h | 5 --- src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp | 32 ++++++++++++++++++ src/ArduinoOcpp/Tasks/Metering/MeterValue.h | 2 ++ .../Tasks/Metering/SampledValue.cpp | 33 +++++++++++++++++-- src/ArduinoOcpp/Tasks/Metering/SampledValue.h | 15 +++++++-- 7 files changed, 82 insertions(+), 17 deletions(-) diff --git a/src/ArduinoOcpp/MessagesV16/MeterValues.h b/src/ArduinoOcpp/MessagesV16/MeterValues.h index 898899ba..99e2d077 100644 --- a/src/ArduinoOcpp/MessagesV16/MeterValues.h +++ b/src/ArduinoOcpp/MessagesV16/MeterValues.h @@ -7,11 +7,13 @@ #include #include -#include #include namespace ArduinoOcpp { + +class MeterValue; + namespace Ocpp16 { class MeterValues : public OcppMessage { diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp index a856e2ba..63ee89e1 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp @@ -60,7 +60,7 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() { if (*ClockAlignedDataInterval >= 1) { - if (alignedData.size() >= (size_t) *MeterValuesAlignedDataMaxLength) { + if (alignedData.size() >= (size_t) *MeterValuesAlignedDataMaxLength) { auto meterValues = new MeterValues(std::move(alignedData), connectorId); alignedData.clear(); return meterValues; @@ -173,12 +173,6 @@ OcppMessage *ConnectorMeterValuesRecorder::takeTriggeredMeterValues() { return nullptr; } - int txId_now = -1; - auto connector = context.getConnectorStatus(connectorId); - if (connector) { - txId_now = connector->getTransactionId(); - } - decltype(sampledData) mv_now; mv_now.push_back(std::move(sample)); diff --git a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h index 7377ffed..d2d2d151 100644 --- a/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h +++ b/src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.h @@ -5,10 +5,6 @@ #ifndef CONNECTOR_METER_VALUES_RECORDER #define CONNECTOR_METER_VALUES_RECORDER -//#define METER_VALUE_SAMPLE_INTERVAL 60 //in seconds - -//#define METER_VALUES_SAMPLED_DATA_MAX_LENGTH 4 //after 4 measurements, send the values to the CS - #include #include #include @@ -22,7 +18,6 @@ using PowerSampler = std::function; using EnergySampler = std::function; class OcppModel; -class OcppTimestamp; class OcppMessage; class ConnectorMeterValuesRecorder { diff --git a/src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp b/src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp index 27fe8904..c8be2032 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/MeterValue.cpp @@ -102,3 +102,35 @@ std::unique_ptr MeterValueBuilder::takeSample(const OcppTimestamp& t return sample; } + +std::unique_ptr MeterValueBuilder::deserializeSample(const JsonObject mvJson) { + + OcppTimestamp timestamp; + bool ret = timestamp.setTime(mvJson["timestamp"] | "Invalid"); + if (!ret) { + AO_DBG_ERR("invalid timestamp"); + return nullptr; + } + + auto sample = std::unique_ptr(new MeterValue(timestamp)); + + JsonArray sampledValue = mvJson["sampledValue"]; + for (JsonObject svJson : sampledValue) { //for each sampled value, search sampler with matching measurand type + const char *measurand = svJson["measurand"] | "Invalid"; + for (auto& sampler : samplers) { + if (!sampler->getMeasurand().compare(measurand)) { + //found correct sampler + auto dVal = sampler->deserializeValue(svJson); + if (dVal) { + sample->addSampledValue(std::move(dVal)); + } else { + AO_DBG_ERR("deserialization error"); + } + break; + } + } + } + + AO_DBG_VERBOSE("deserialized MV"); + return sample; +} diff --git a/src/ArduinoOcpp/Tasks/Metering/MeterValue.h b/src/ArduinoOcpp/Tasks/Metering/MeterValue.h index 450f5d5b..c89565d3 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeterValue.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeterValue.h @@ -40,6 +40,8 @@ class MeterValueBuilder { std::shared_ptr> samplers_select); std::unique_ptr takeSample(const OcppTimestamp& timestamp, const ReadingContext& context); + + std::unique_ptr deserializeSample(const JsonObject mvJson); }; } diff --git a/src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp b/src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp index 4a570829..702de16e 100644 --- a/src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp +++ b/src/ArduinoOcpp/Tasks/Metering/SampledValue.cpp @@ -10,7 +10,7 @@ using ArduinoOcpp::SampledValue; //helper function namespace ArduinoOcpp { namespace Ocpp16 { -const char *cstrFromReadingContext(ReadingContext context) { +const char *serializeReadingContext(ReadingContext context) { switch (context) { case (ReadingContext::InterruptionBegin): return "Interruption.Begin"; @@ -34,7 +34,36 @@ const char *cstrFromReadingContext(ReadingContext context) { return nullptr; } } +ReadingContext deserializeReadingContext(const char *context) { + if (!context) { + AO_DBG_ERR("Invalid argument"); + return ReadingContext::NOT_SET; + } + + if (!strcmp(context, "NOT_SET")) { + AO_DBG_DEBUG("Deserialize Null-ReadingContext"); + return ReadingContext::NOT_SET; + } else if (!strcmp(context, "Sample.Periodic")) { + return ReadingContext::SamplePeriodic; + } else if (!strcmp(context, "Sample.Clock")) { + return ReadingContext::SampleClock; + } else if (!strcmp(context, "Transaction.Begin")) { + return ReadingContext::TransactionBegin; + } else if (!strcmp(context, "Transaction.End")) { + return ReadingContext::TransactionEnd; + } else if (!strcmp(context, "Other")) { + return ReadingContext::Other; + } else if (!strcmp(context, "Interruption.Begin")) { + return ReadingContext::InterruptionBegin; + } else if (!strcmp(context, "Interruption.End")) { + return ReadingContext::InterruptionEnd; + } else if (!strcmp(context, "Trigger")) { + return ReadingContext::Trigger; + } + AO_DBG_ERR("ReadingContext not specified %.10s", context); + return ReadingContext::NOT_SET; +} }} //end namespaces std::unique_ptr SampledValue::toJson() { @@ -53,7 +82,7 @@ std::unique_ptr SampledValue::toJson() { auto result = std::unique_ptr(new DynamicJsonDocument(capacity + 100)); //TODO remove safety space auto payload = result->to(); payload["value"] = value; - auto context_cstr = Ocpp16::cstrFromReadingContext(context); + auto context_cstr = Ocpp16::serializeReadingContext(context); if (context_cstr) payload["context"] = context_cstr; if (!properties.getFormat().empty()) diff --git a/src/ArduinoOcpp/Tasks/Metering/SampledValue.h b/src/ArduinoOcpp/Tasks/Metering/SampledValue.h index 7debf013..f5750f8c 100644 --- a/src/ArduinoOcpp/Tasks/Metering/SampledValue.h +++ b/src/ArduinoOcpp/Tasks/Metering/SampledValue.h @@ -77,7 +77,8 @@ enum class ReadingContext { }; namespace Ocpp16 { -const char *cstrFromReadingContext(ReadingContext context); +const char *serializeReadingContext(ReadingContext context); +ReadingContext deserializeReadingContext(const char *serialized); } class SampledValue { @@ -119,6 +120,7 @@ class SampledValueSampler { SampledValueSampler(SampledValueProperties properties) : properties(properties) { } virtual ~SampledValueSampler() = default; virtual std::unique_ptr takeValue(ReadingContext context) = 0; + virtual std::unique_ptr deserializeValue(JsonObject svJson) = 0; const std::string& getMeasurand() {return properties.getMeasurand();}; }; @@ -129,7 +131,16 @@ class SampledValueSamplerConcrete : public SampledValueSampler { public: SampledValueSamplerConcrete(SampledValueProperties properties, std::function sampler) : SampledValueSampler(properties), sampler(sampler) { } std::unique_ptr takeValue(ReadingContext context) override { - return std::unique_ptr>(new SampledValueConcrete(properties, context, sampler(context))); + return std::unique_ptr>(new SampledValueConcrete( + properties, + context, + sampler(context))); + } + std::unique_ptr deserializeValue(JsonObject svJson) override { + return std::unique_ptr>(new SampledValueConcrete( + properties, + Ocpp16::deserializeReadingContext(svJson["context"] | "NOT_SET"), + DeSerializer::deserialize(svJson["value"] | ""))); } }; From 2225250c252300154370519afafd398167aab3d0 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 18 Aug 2022 15:37:02 +0200 Subject: [PATCH 198/696] atomic transaction operations --- src/ArduinoOcpp.cpp | 102 +++-- src/ArduinoOcpp.h | 8 +- src/ArduinoOcpp/Core/OcppMessage.h | 4 +- src/ArduinoOcpp/Core/OcppModel.cpp | 9 + src/ArduinoOcpp/Core/OcppModel.h | 5 + src/ArduinoOcpp/Core/OcppOperation.cpp | 15 + src/ArduinoOcpp/Core/OcppOperation.h | 2 + .../MessagesV16/StartTransaction.cpp | 121 +++--- .../MessagesV16/StartTransaction.h | 17 +- .../MessagesV16/StopTransaction.cpp | 93 +++-- src/ArduinoOcpp/MessagesV16/StopTransaction.h | 20 +- .../SimpleOcppOperationFactory.cpp | 4 +- .../ChargePointStatus/ConnectorStatus.cpp | 349 ++++++++---------- .../Tasks/ChargePointStatus/ConnectorStatus.h | 32 +- .../TransactionPrerequisites.h | 7 +- .../Transactions/OrderedOperationsQueue.cpp | 29 ++ .../Transactions/OrderedOperationsQueue.h | 28 ++ .../Tasks/Transactions/Transaction.cpp | 218 +++++++++++ .../Tasks/Transactions/Transaction.h | 196 ++++++++++ .../Tasks/Transactions/TransactionProcess.cpp | 113 ++++++ .../Tasks/Transactions/TransactionProcess.h | 46 +++ .../Transactions/TransactionSequence.cpp | 29 ++ .../Tasks/Transactions/TransactionSequence.h | 28 ++ .../Tasks/Transactions/TransactionService.cpp | 74 ++++ .../Tasks/Transactions/TransactionService.h | 44 +++ .../Tasks/Transactions/TransactionStore.cpp | 308 ++++++++++++++++ .../Tasks/Transactions/TransactionStore.h | 61 +++ 27 files changed, 1589 insertions(+), 373 deletions(-) create mode 100644 src/ArduinoOcpp/Tasks/Transactions/OrderedOperationsQueue.cpp create mode 100644 src/ArduinoOcpp/Tasks/Transactions/OrderedOperationsQueue.h create mode 100644 src/ArduinoOcpp/Tasks/Transactions/Transaction.cpp create mode 100644 src/ArduinoOcpp/Tasks/Transactions/Transaction.h create mode 100644 src/ArduinoOcpp/Tasks/Transactions/TransactionProcess.cpp create mode 100644 src/ArduinoOcpp/Tasks/Transactions/TransactionProcess.h create mode 100644 src/ArduinoOcpp/Tasks/Transactions/TransactionSequence.cpp create mode 100644 src/ArduinoOcpp/Tasks/Transactions/TransactionSequence.h create mode 100644 src/ArduinoOcpp/Tasks/Transactions/TransactionService.cpp create mode 100644 src/ArduinoOcpp/Tasks/Transactions/TransactionService.h create mode 100644 src/ArduinoOcpp/Tasks/Transactions/TransactionStore.cpp create mode 100644 src/ArduinoOcpp/Tasks/Transactions/TransactionStore.h diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index a9495203..6b29a231 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ float voltage_eff {230.f}; #define OCPP_ID_OF_CONNECTOR 1 #define OCPP_ID_OF_CP 0 bool OCPP_booted = false; //if BootNotification succeeded +bool enteredLoop = false; //if loop() is called the first time } //end namespace ArduinoOcpp::Facade } //end namespace ArduinoOcpp @@ -94,6 +96,8 @@ void OCPP_initialize(OcppSocket& ocppSocket, float V_eff, ArduinoOcpp::Filesyste ocppEngine = new OcppEngine(ocppSocket, system_time); auto& model = ocppEngine->getOcppModel(); + model.setTransactionService(std::unique_ptr( + new TransactionService(*ocppEngine, OCPP_NUMCONNECTORS, filesystem))); model.setChargePointStatusService(std::unique_ptr( new ChargePointStatusService(*ocppEngine, OCPP_NUMCONNECTORS))); model.setHeartbeatService(std::unique_ptr( @@ -123,8 +127,7 @@ void OCPP_initialize(OcppSocket& ocppSocket, float V_eff, ArduinoOcpp::Filesyste } void OCPP_deinitialize() { - AO_DBG_DEBUG("Still experimental function. If you find problems, it would be great if you publish them on the GitHub page"); - + delete ocppEngine; ocppEngine = nullptr; @@ -141,15 +144,21 @@ void OCPP_deinitialize() { voltage_eff = 230.f; OCPP_booted = false; + enteredLoop = false; } void OCPP_loop() { if (!ocppEngine) { AO_DBG_WARN("Please call OCPP_initialize before"); - //delay(200); //Prevent this message from flooding the Serial monitor. return; } + if (!enteredLoop) { + enteredLoop = true; + configuration_save(); + ocppEngine->getOcppModel().getTransactionService()->initiateRestoredOperations(); + } + ocppEngine->loop(); auto& model = ocppEngine->getOcppModel(); @@ -168,7 +177,7 @@ void OCPP_loop() { void setPowerActiveImportSampler(std::function power) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } @@ -191,7 +200,7 @@ void setPowerActiveImportSampler(std::function power) { void setEnergyActiveImportSampler(std::function energy) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } auto& model = ocppEngine->getOcppModel(); @@ -213,7 +222,7 @@ void setEnergyActiveImportSampler(std::function energy) { void addMeterValueSampler(std::unique_ptr meterValueSampler) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } auto& model = ocppEngine->getOcppModel(); @@ -226,7 +235,7 @@ void addMeterValueSampler(std::unique_ptr meterValueSampler void addMeterValueSampler(std::function value, const char *measurand, const char *unit, const char *location, const char *phase) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } @@ -259,7 +268,7 @@ void addMeterValueSampler(std::function value, const char *measurand void setEvRequestsEnergySampler(std::function evRequestsEnergy) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); @@ -272,7 +281,7 @@ void setEvRequestsEnergySampler(std::function evRequestsEnergy) { void setConnectorEnergizedSampler(std::function connectorEnergized) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); @@ -285,7 +294,7 @@ void setConnectorEnergizedSampler(std::function connectorEnergized) { void setConnectorPluggedSampler(std::function connectorPlugged) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); @@ -298,7 +307,7 @@ void setConnectorPluggedSampler(std::function connectorPlugged) { void addConnectorErrorCodeSampler(std::function connectorErrorCode) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); @@ -311,7 +320,7 @@ void addConnectorErrorCodeSampler(std::function connectorErrorCo void setOnChargingRateLimitChange(std::function chargingRateChanged) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } auto& model = ocppEngine->getOcppModel(); @@ -324,7 +333,7 @@ void setOnChargingRateLimitChange(std::function chargingRateChanged void setOnUnlockConnector(std::function()> unlockConnector) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); @@ -335,9 +344,9 @@ void setOnUnlockConnector(std::function()> unlockConnector) { connector->setOnUnlockConnector(unlockConnector); } -void setConnectorLock(std::function lockConnector) { +void setConnectorLock(std::function lockConnector) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); @@ -348,9 +357,9 @@ void setConnectorLock(std::functionsetConnectorLock(lockConnector); } -void setTxBasedMeterUpdate(std::function updateTxState) { +void setTxBasedMeterUpdate(std::function updateTxState) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); @@ -387,7 +396,7 @@ void setOnResetReceiveReq(OnReceiveReqListener onReceiveReq) { void authorize(const char *idTag, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } if (!idTag || strnlen(idTag, IDTAG_LEN_MAX + 2) > IDTAG_LEN_MAX) { @@ -413,7 +422,7 @@ void authorize(const char *idTag, OnReceiveConfListener onConf, OnAbortListener void bootNotification(const char *chargePointModel, const char *chargePointVendor, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } @@ -427,7 +436,7 @@ void bootNotification(const char *chargePointModel, const char *chargePointVendo void bootNotification(std::unique_ptr payload, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } auto bootNotification = makeOcppOperation( @@ -447,17 +456,30 @@ void bootNotification(std::unique_ptr payload, OnReceiveCon ocppEngine->initiateOperation(std::move(bootNotification)); } -void startTransaction(const char *idTag, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { +bool startTransaction(const char *idTag, OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); - return; + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before + return false; } if (!idTag || strnlen(idTag, IDTAG_LEN_MAX + 2) > IDTAG_LEN_MAX) { AO_DBG_ERR("idTag format violation. Expect c-style string with at most %u characters", IDTAG_LEN_MAX); - return; + return false; + } + auto transaction = ocppEngine->getOcppModel().getTransactionService()->getTransactionStore().getActiveTransaction(OCPP_ID_OF_CONNECTOR); + if (!transaction) { + AO_DBG_ERR("Transaction buffer full"); + return false; + } + + if (transaction->isRunning()) { + AO_DBG_ERR("Called StartTx while still in transaction. Please call StopTx"); + return false; } + + transaction->setIdTag(idTag); + auto startTransaction = makeOcppOperation( - new StartTransaction(OCPP_ID_OF_CONNECTOR, idTag)); + new StartTransaction(transaction)); if (onConf) startTransaction->setOnReceiveConfListener(onConf); if (onAbort) @@ -471,15 +493,31 @@ void startTransaction(const char *idTag, OnReceiveConfListener onConf, OnAbortLi else startTransaction->setTimeout(std::unique_ptr(new SuppressedTimeout())); ocppEngine->initiateOperation(std::move(startTransaction)); + + return true; } -void stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { +bool stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTimeoutListener onTimeout, OnReceiveErrorListener onError, std::unique_ptr timeout) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); - return; + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before + return false; } + + auto transaction = ocppEngine->getOcppModel().getTransactionService()->getTransactionStore().getActiveTransaction(OCPP_ID_OF_CONNECTOR); + if (!transaction || !transaction->isRunning()) { + AO_DBG_ERR("No running Tx to stop"); + return false; + } + + const char *idTag = transaction->getIdTag(); + if (idTag) { + transaction->setStopIdTag(idTag); + } + + transaction->setStopReason("Local"); + auto stopTransaction = makeOcppOperation( - new StopTransaction(OCPP_ID_OF_CONNECTOR)); + new StopTransaction(transaction)); if (onConf) stopTransaction->setOnReceiveConfListener(onConf); if (onAbort) @@ -493,6 +531,8 @@ void stopTransaction(OnReceiveConfListener onConf, OnAbortListener onAbort, OnTi else stopTransaction->setTimeout(std::unique_ptr(new SuppressedTimeout())); ocppEngine->initiateOperation(std::move(stopTransaction)); + + return true; } int getTransactionId() { @@ -539,7 +579,7 @@ bool isAvailable() { void beginSession(const char *idTag) { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } if (!idTag || strnlen(idTag, IDTAG_LEN_MAX + 2) > IDTAG_LEN_MAX) { @@ -556,7 +596,7 @@ void beginSession(const char *idTag) { void endSession() { if (!ocppEngine) { - AO_DBG_ERR("Please call OCPP_initialize before"); + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before return; } auto connector = ocppEngine->getOcppModel().getConnectorStatus(OCPP_ID_OF_CONNECTOR); diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index c02c19af..d8ffae91 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -87,7 +87,7 @@ void setOnUnlockConnector(std::function()> unlockC // - TxEnableState::Inactive if connector lock is released // - TxEnableState::Pending otherwise, e.g. if transitioning between the states //Called periodically -void setConnectorLock(std::function lockConnector); +void setConnectorLock(std::function lockConnector); //Set a Cb to update transaction-based energy measurements with the most recent transaction state. //This allows energy meters (e.g. based on OCMF) to take their measruements right before and after a transaction @@ -95,7 +95,7 @@ void setConnectorLock(std::function updateTxState); +void setTxBasedMeterUpdate(std::function updateTxState); /* * React on CS-initiated operations @@ -146,9 +146,9 @@ void bootNotification(const char *chargePointModel, const char *chargePointVendo //The OCPP operation will include the given payload without modifying it. The library will delete the payload object after successful transmission. void bootNotification(std::unique_ptr payload, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); -void startTransaction(const char *idTag, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); +bool startTransaction(const char *idTag, OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); -void stopTransaction(OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); +bool stopTransaction(OnReceiveConfListener onConf = nullptr, OnAbortListener onAbort = nullptr, OnTimeoutListener onTimeout = nullptr, OnReceiveErrorListener onError = nullptr, std::unique_ptr timeout = nullptr); /* * Access information about the internal state of the library diff --git a/src/ArduinoOcpp/Core/OcppMessage.h b/src/ArduinoOcpp/Core/OcppMessage.h index ab88d345..5b0cb3cd 100644 --- a/src/ArduinoOcpp/Core/OcppMessage.h +++ b/src/ArduinoOcpp/Core/OcppMessage.h @@ -26,6 +26,7 @@ namespace ArduinoOcpp { std::unique_ptr createEmptyDocument(); class OcppModel; +class TransactionRPC; class OcppMessage { private: @@ -75,7 +76,8 @@ class OcppMessage { virtual const char *getErrorCode() {return nullptr;} //nullptr means no error virtual const char *getErrorDescription() {return "";} virtual std::unique_ptr getErrorDetails() {return createEmptyDocument();} - + + virtual TransactionRPC *getTransactionSync() {return nullptr;} }; } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Core/OcppModel.cpp b/src/ArduinoOcpp/Core/OcppModel.cpp index ec6a78fc..13925ffb 100644 --- a/src/ArduinoOcpp/Core/OcppModel.cpp +++ b/src/ArduinoOcpp/Core/OcppModel.cpp @@ -3,6 +3,7 @@ // MIT License #include +#include #include #include #include @@ -41,6 +42,14 @@ void OcppModel::loop() { firmwareService->loop(); } +void OcppModel::setTransactionService(std::unique_ptr ts) { + transactionService = std::move(ts); +} + +TransactionService *OcppModel::getTransactionService() { + return transactionService.get(); +} + void OcppModel::setSmartChargingService(std::unique_ptr scs) { smartChargingService = std::move(scs); } diff --git a/src/ArduinoOcpp/Core/OcppModel.h b/src/ArduinoOcpp/Core/OcppModel.h index e0ae10b8..2b7a351a 100644 --- a/src/ArduinoOcpp/Core/OcppModel.h +++ b/src/ArduinoOcpp/Core/OcppModel.h @@ -11,6 +11,7 @@ namespace ArduinoOcpp { +class TransactionService; class SmartChargingService; class ChargePointStatusService; class ConnectorStatus; @@ -21,6 +22,7 @@ class HeartbeatService; class OcppModel { private: + std::unique_ptr transactionService; std::unique_ptr smartChargingService; std::unique_ptr chargePointStatusService; std::unique_ptr meteringService; @@ -37,6 +39,9 @@ class OcppModel { void loop(); + void setTransactionService(std::unique_ptr transactionService); + TransactionService *getTransactionService(); + void setSmartChargingService(std::unique_ptr scs); SmartChargingService* getSmartChargingService() const; diff --git a/src/ArduinoOcpp/Core/OcppOperation.cpp b/src/ArduinoOcpp/Core/OcppOperation.cpp index 723baeab..18183dcb 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.cpp +++ b/src/ArduinoOcpp/Core/OcppOperation.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -118,6 +119,15 @@ bool OcppOperation::sendReq(OcppSocket& ocppSocket){ requestJson.add(ocppMessage->getOcppOperationType()); //Action requestJson.add(*requestPayload); //Payload + /* + * Transaction safety: before sending the message, store the assigned msgId so this action + * can be replayed when booted the next time + */ + auto txSync = ocppMessage->getTransactionSync(); + if (txSync) { + txSync->requestWithMsgId(unique_id_counter); + } + /* * Serialize and send. Destroy serialization and JSON object. * @@ -353,6 +363,11 @@ bool OcppOperation::isFullyConfigured(){ return ocppMessage != nullptr; } +void OcppOperation::rebaseMsgId(int msgIdCounter) { + unique_id_counter = msgIdCounter; + getMessageID(); //apply msgIdCounter to this operation +} + void OcppOperation::print_debug() { if (ocppMessage) { AO_CONSOLE_PRINTF("OcppOperation of type %s\n", ocppMessage->getOcppOperationType()); diff --git a/src/ArduinoOcpp/Core/OcppOperation.h b/src/ArduinoOcpp/Core/OcppOperation.h index 4ca7b5ad..a3c44de6 100644 --- a/src/ArduinoOcpp/Core/OcppOperation.h +++ b/src/ArduinoOcpp/Core/OcppOperation.h @@ -128,6 +128,8 @@ class OcppOperation { bool isFullyConfigured(); + void rebaseMsgId(int msgIdCounter); //workaround; remove when random UUID msg IDs are introduced + void print_debug(); }; diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp index fa501eca..9e9d91ca 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.cpp @@ -6,19 +6,16 @@ #include #include #include +#include +#include #include using ArduinoOcpp::Ocpp16::StartTransaction; +using ArduinoOcpp::TransactionRPC; -StartTransaction::StartTransaction(int connectorId) : connectorId(connectorId) { - -} -StartTransaction::StartTransaction(int connectorId, const char *idTag) : connectorId(connectorId) { - if (idTag && strnlen(idTag, IDTAG_LEN_MAX + 2) <= IDTAG_LEN_MAX) - snprintf(this->idTag, IDTAG_LEN_MAX + 1, "%s", idTag); - else - AO_DBG_ERR("Format violation"); +StartTransaction::StartTransaction(std::shared_ptr transaction) : transaction(transaction) { + } const char* StartTransaction::getOcppOperationType() { @@ -26,41 +23,28 @@ const char* StartTransaction::getOcppOperationType() { } void StartTransaction::initiate() { - if (ocppModel && ocppModel->getMeteringService()) { - auto meteringService = ocppModel->getMeteringService(); - meterStart = meteringService->readTxEnergyMeter(connectorId, ReadingContext::TransactionBegin); - } - - if (ocppModel) { - otimestamp = ocppModel->getOcppTime().getOcppTimestampNow(); - } else { - otimestamp = MIN_TIME; - } + if (ocppModel && transaction && !transaction->getStartRpcSync().isRequested()) { + //fill out tx data if not happened before - if (ocppModel && ocppModel->getConnectorStatus(connectorId)) { - auto connector = ocppModel->getConnectorStatus(connectorId); - - if (*idTag == '\0') { - const char *sessionIdTag = connector->getSessionIdTag(); - if (sessionIdTag) { - snprintf(idTag, IDTAG_LEN_MAX + 1, "%s", sessionIdTag); + auto meteringService = ocppModel->getMeteringService(); + if (transaction->getMeterStart() < 0 && meteringService) { + auto meterStart = meteringService->readTxEnergyMeter(transaction->getConnectorId(), ReadingContext::TransactionBegin); + if (meterStart && *meterStart) { + transaction->setMeterStart(meterStart->toInteger()); } else { - AO_DBG_WARN("Try to start transaction without providing idTag. Initialize session with default idTag"); - connector->beginSession(nullptr); - sessionIdTag = connector->getSessionIdTag(); //returns default idTag now - if (sessionIdTag) - snprintf(idTag, IDTAG_LEN_MAX + 1, "%s", sessionIdTag); + AO_DBG_ERR("MeterStart undefined"); } - } else { - //idTag has been overriden - connector->beginSession(idTag); } - if (connector->getTransactionId() >= 0) { - AO_DBG_WARN("Started transaction while OCPP already presumes a running transaction"); + if (transaction->getStartTimestamp() <= MIN_TIME) { + transaction->setStartTimestamp(ocppModel->getOcppTime().getOcppTimestampNow()); } - connector->setTransactionId(0); //pending - transactionRev = connector->getTransactionWriteCount(); + + auto seqNr = ocppModel->getTransactionService()->getTransactionSequence().reserveSeqNr(); + AO_DBG_DEBUG("Reserved seqNr inside StartTx: %u", seqNr); + transaction->getStartRpcSync().setRequested(seqNr); + + transaction->commit(); } AO_DBG_INFO("StartTransaction initiated"); @@ -68,59 +52,52 @@ void StartTransaction::initiate() { std::unique_ptr StartTransaction::createReq() { - if (meterStart && !*meterStart) { - //meterStart not ready yet - return nullptr; - } - - auto doc = std::unique_ptr(new DynamicJsonDocument(JSON_OBJECT_SIZE(5) + (JSONDATE_LENGTH + 1) + (IDTAG_LEN_MAX + 1))); + auto doc = std::unique_ptr(new DynamicJsonDocument( + JSON_OBJECT_SIZE(5) + + (IDTAG_LEN_MAX + 1) + + (JSONDATE_LENGTH + 1))); + JsonObject payload = doc->to(); - payload["connectorId"] = connectorId; - if (meterStart && *meterStart) { - payload["meterStart"] = meterStart->toInteger(); + payload["connectorId"] = transaction->getConnectorId(); + + if (transaction->getIdTag() && *transaction->getIdTag()) { + payload["idTag"] = (char*) transaction->getIdTag(); } - if (otimestamp > MIN_TIME) { + if (transaction->isMeterStartDefined()) { + payload["meterStart"] = transaction->getMeterStart(); + } + + if (transaction->getStartTimestamp() > MIN_TIME) { char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; - otimestamp.toJsonString(timestamp, JSONDATE_LENGTH + 1); + transaction->getStartTimestamp().toJsonString(timestamp, JSONDATE_LENGTH + 1); payload["timestamp"] = timestamp; } - payload["idTag"] = idTag; - return doc; } void StartTransaction::processConf(JsonObject payload) { const char* idTagInfoStatus = payload["idTagInfo"]["status"] | "not specified"; - int transactionId = payload["transactionId"] | -1; - - ConnectorStatus *connector = nullptr; - if (ocppModel) - connector = ocppModel->getConnectorStatus(connectorId); - - if (connector) { - if (transactionRev == connector->getTransactionWriteCount()) { - - if (!strcmp(idTagInfoStatus, "Accepted")) { - AO_DBG_INFO("Request has been accepted"); - } else { - AO_DBG_INFO("Request has been denied. Reason: %s", idTagInfoStatus); - AO_DBG_DEBUG("Set txId despite rejection"); - connector->setIdTagInvalidated(); - } - - connector->setTransactionId(transactionId); - } - connector->setTransactionIdSync(transactionId); - - AO_DBG_DEBUG("Local txId = %i, remote txId = %i", connector->getTransactionId(), connector->getTransactionIdSync()); + if (!strcmp(idTagInfoStatus, "Accepted")) { + AO_DBG_INFO("Request has been accepted"); + } else { + AO_DBG_INFO("Request has been denied. Reason: %s", idTagInfoStatus); + transaction->setIdTagDeauthorized(); } + int transactionId = payload["transactionId"] | -1; + transaction->setTransactionId(transactionId); + + transaction->getStartRpcSync().confirm(); + transaction->commit(); } +TransactionRPC *StartTransaction::getTransactionSync() { + return transaction ? &transaction->getStartRpcSync() : nullptr; +} void StartTransaction::processReq(JsonObject payload) { diff --git a/src/ArduinoOcpp/MessagesV16/StartTransaction.h b/src/ArduinoOcpp/MessagesV16/StartTransaction.h index 153da753..1445148f 100644 --- a/src/ArduinoOcpp/MessagesV16/StartTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StartTransaction.h @@ -11,19 +11,20 @@ #include namespace ArduinoOcpp { + +class Transaction; +class TransactionRPC; + namespace Ocpp16 { class StartTransaction : public OcppMessage { private: - int connectorId = 1; - std::unique_ptr meterStart {nullptr}; - OcppTimestamp otimestamp; - char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; - uint16_t transactionRev = 0; + std::shared_ptr transaction; public: - StartTransaction(int connectorId); - StartTransaction(int connectorId, const char *idTag); + StartTransaction(std::shared_ptr transaction); + + StartTransaction() = default; //for debugging only. Make this for the server pendant const char* getOcppOperationType(); @@ -33,6 +34,8 @@ class StartTransaction : public OcppMessage { void processConf(JsonObject payload); + TransactionRPC *getTransactionSync() override; + void processReq(JsonObject payload); std::unique_ptr createConf(); diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp index 69a85f27..ddf5b566 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.cpp @@ -7,53 +7,59 @@ #include #include #include +#include +#include #include using ArduinoOcpp::Ocpp16::StopTransaction; +using ArduinoOcpp::TransactionRPC; + +StopTransaction::StopTransaction(std::shared_ptr transaction) + : transaction(transaction) { + +} + +StopTransaction::StopTransaction(std::shared_ptr transaction, std::vector> transactionData) + : transaction(transaction), transactionData(std::move(transactionData)) { -StopTransaction::StopTransaction(int connectorId, const char *reason) : connectorId(connectorId) { - if (reason) { - snprintf(this->reason, REASON_LEN_MAX, "%s", reason); - } } +StopTransaction::StopTransaction() { } + const char* StopTransaction::getOcppOperationType(){ return "StopTransaction"; } void StopTransaction::initiate() { - if (ocppModel && ocppModel->getMeteringService()) { - auto meteringService = ocppModel->getMeteringService(); - meterStop = meteringService->readTxEnergyMeter(connectorId, ReadingContext::TransactionEnd); - transactionData = meteringService->createStopTxMeterData(connectorId); - } + if (ocppModel && transaction && !transaction->getStopRpcSync().isRequested()) { + //fill out tx data if not happened before - if (ocppModel) { - otimestamp = ocppModel->getOcppTime().getOcppTimestampNow(); - } else { - otimestamp = MIN_TIME; - } + auto meteringService = ocppModel->getMeteringService(); + if (transaction->getMeterStop() < 0 && meteringService) { + auto meterStop = meteringService->readTxEnergyMeter(transaction->getConnectorId(), ReadingContext::TransactionEnd); + if (meterStop && *meterStop) { + transaction->setMeterStop(meterStop->toInteger()); + } else { + AO_DBG_ERR("MeterStop undefined"); + } + } - if (ocppModel && ocppModel->getConnectorStatus(connectorId)){ - auto connector = ocppModel->getConnectorStatus(connectorId); - connector->setTransactionId(-1); //immediate end of transaction - if (connector->getSessionIdTag()) { - AO_DBG_DEBUG("Ending EV user session triggered by StopTransaction"); - connector->endSession(); + if (transaction->getStopTimestamp() <= MIN_TIME) { + transaction->setStopTimestamp(ocppModel->getOcppTime().getOcppTimestampNow()); } - } + auto seqNr = ocppModel->getTransactionService()->getTransactionSequence().reserveSeqNr(); + AO_DBG_DEBUG("Reserved seqNr inside StopTx: %u", seqNr); + transaction->getStopRpcSync().setRequested(seqNr); + + transaction->commit(); + } AO_DBG_INFO("StopTransaction initiated!"); } std::unique_ptr StopTransaction::createReq() { - if (meterStop && !*meterStop) { - //meterStop not ready yet - return nullptr; - } - std::vector> txDataJson; size_t txDataJson_size = 0; for (auto mv = transactionData.begin(); mv != transactionData.end(); mv++) { @@ -72,28 +78,30 @@ std::unique_ptr StopTransaction::createReq() { auto doc = std::unique_ptr(new DynamicJsonDocument( JSON_OBJECT_SIZE(6) + //total of 6 fields + (IDTAG_LEN_MAX + 1) + //stop idTag (JSONDATE_LENGTH + 1) + //timestamp string (REASON_LEN_MAX + 1) + //reason string txDataDoc.capacity())); JsonObject payload = doc->to(); - if (meterStop && *meterStop) { - payload["meterStop"] = meterStop->toInteger(); + if (transaction->getStopIdTag() && *transaction->getStopIdTag()) { + payload["idTag"] = (char*) transaction->getStopIdTag(); } - if (otimestamp > MIN_TIME) { - char timestamp[JSONDATE_LENGTH + 1] = {'\0'}; - otimestamp.toJsonString(timestamp, JSONDATE_LENGTH + 1); - payload["timestamp"] = timestamp; + if (transaction->isMeterStopDefined()) { + payload["meterStop"] = transaction->getMeterStop(); } - - if (ocppModel && ocppModel->getConnectorStatus(connectorId)){ - auto connector = ocppModel->getConnectorStatus(connectorId); - payload["transactionId"] = connector->getTransactionIdSync(); + + if (transaction->getStopTimestamp() > MIN_TIME) { + char timestamp [JSONDATE_LENGTH + 1] = {'\0'}; + transaction->getStopTimestamp().toJsonString(timestamp, JSONDATE_LENGTH + 1); + payload["timestamp"] = timestamp; } - if (reason[0] != '\0') { - payload["reason"] = reason; + payload["transactionId"] = transaction->getTransactionId(); + + if (transaction->getStopReason() && *transaction->getStopReason()) { + payload["reason"] = (char*) transaction->getStopReason(); } if (!transactionData.empty()) { @@ -105,14 +113,17 @@ std::unique_ptr StopTransaction::createReq() { void StopTransaction::processConf(JsonObject payload) { - if (ocppModel && ocppModel->getConnectorStatus(connectorId)){ - auto connector = ocppModel->getConnectorStatus(connectorId); - connector->setTransactionIdSync(-1); + if (transaction) { + transaction->getStopRpcSync().confirm(); + transaction->commit(); } AO_DBG_INFO("Request has been accepted!"); } +TransactionRPC *StopTransaction::getTransactionSync() { + return transaction ? &transaction->getStopRpcSync() : nullptr; +} void StopTransaction::processReq(JsonObject payload) { /** diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.h b/src/ArduinoOcpp/MessagesV16/StopTransaction.h index eaaacde8..da40c680 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.h @@ -14,18 +14,24 @@ namespace ArduinoOcpp { class SampledValue; class MeterValue; +class Transaction; +class TransactionRPC; + namespace Ocpp16 { class StopTransaction : public OcppMessage { private: - int connectorId = 1; - std::unique_ptr meterStop {nullptr}; - OcppTimestamp otimestamp; - char reason [REASON_LEN_MAX] {'\0'}; + std::shared_ptr transaction; std::vector> transactionData; public: - StopTransaction(int connectorId, const char *reason = nullptr); + //StopTransaction(int connectorId, const char *reason = nullptr); + + StopTransaction(std::shared_ptr transaction); + + StopTransaction(std::shared_ptr transaction, std::vector> transactionData); + + StopTransaction(); //for debugging only. Make this for the server pendant const char* getOcppOperationType(); @@ -35,6 +41,10 @@ class StopTransaction : public OcppMessage { void processConf(JsonObject payload); + bool processErr(const char *code, const char *description, JsonObject details) { return false;} + + TransactionRPC *getTransactionSync() override; + void processReq(JsonObject payload); std::unique_ptr createConf(); diff --git a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp index 274ac806..9a825b85 100644 --- a/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp +++ b/src/ArduinoOcpp/SimpleOcppOperationFactory.cpp @@ -228,10 +228,10 @@ std::unique_ptr makeOcppOperation(const char *messageType, int co } else if (!strcmp(messageType, "StatusNotification")) { msg = std::unique_ptr(new Ocpp16::StatusNotification(connectorId)); } else if (!strcmp(messageType, "StartTransaction")) { - msg = std::unique_ptr(new Ocpp16::StartTransaction(1)); //connectorId 1 + msg = std::unique_ptr(new Ocpp16::StartTransaction()); operation->setOnReceiveReqListener(onStartTransactionRequest); } else if (!strcmp(messageType, "StopTransaction")) { - msg = std::unique_ptr(new Ocpp16::StopTransaction(1)); //connectorId 1 + msg = std::unique_ptr(new Ocpp16::StopTransaction()); } else if (!strcmp(messageType, "TriggerMessage")) { msg = std::unique_ptr(new Ocpp16::TriggerMessage()); operation->setOnReceiveReqListener(onTriggerMessageRequest); diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp index ac671131..5ba7dd26 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -19,19 +20,11 @@ using namespace ArduinoOcpp; using namespace ArduinoOcpp::Ocpp16; ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) - : context(context), connectorId{connectorId} { + : context(context), connectorId{connectorId}, txProcess(connectorId) { - //Set default transaction ID in memory - char key [CONF_KEYLEN_MAX + 1] = {'\0'}; - - snprintf(key, CONF_KEYLEN_MAX + 1, "AO_SID_CONN_%d", connectorId); - sIdTag = declareConfiguration(key, "", CONFIGURATION_FN, false, false, true, false); - - snprintf(key, CONF_KEYLEN_MAX + 1, "AO_TXID_CONN_%d", connectorId); - transactionId = declareConfiguration(key, -1, CONFIGURATION_FN, false, false, true, false); - - snprintf(key, CONF_KEYLEN_MAX + 1, "AO_AVAIL_CONN_%d", connectorId); - availability = declareConfiguration(key, AVAILABILITY_OPERATIVE, CONFIGURATION_FN, false, false, true, false); + char availabilityKey [CONF_KEYLEN_MAX + 1] = {'\0'}; + snprintf(availabilityKey, CONF_KEYLEN_MAX + 1, "AO_AVAIL_CONN_%d", connectorId); + availability = declareConfiguration(availabilityKey, AVAILABILITY_OPERATIVE, CONFIGURATION_FN, false, false, true, false); connectionTimeOut = declareConfiguration("ConnectionTimeOut", 30, CONFIGURATION_FN, true, true, true, false); minimumStatusDuration = declareConfiguration("MinimumStatusDuration", 0, CONFIGURATION_FN, true, true, true, false); @@ -41,17 +34,9 @@ ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) localAuthorizeOffline = declareConfiguration("LocalAuthorizeOffline", "false", CONFIGURATION_FN, true, true, false, false); localPreAuthorize = declareConfiguration("LocalPreAuthorize", "false", CONFIGURATION_FN, true, true, false, false); - if (!sIdTag || !transactionId || !availability) { - AO_DBG_ERR("Cannot declare sessionIdTag, transactionId or availability"); + if (!availability) { + AO_DBG_ERR("Cannot declare availability"); } - if (sIdTag->getBuffsize() > 0 && (*sIdTag)[0] != '\0') { - snprintf(idTag, std::min((size_t) (IDTAG_LEN_MAX + 1), sIdTag->getBuffsize()), "%s", ((const char *) *sIdTag)); - session = true; - connectionTimeOutTimestamp = ao_tick_ms(); - connectionTimeOutListen = true; - AO_DBG_DEBUG("Load session idTag at initialization"); - } - transactionIdSync = *transactionId; /* * Initialize standard EVSE behavior. @@ -60,22 +45,26 @@ ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId) * - lock the connector (if handler is set) * - instruct the OCMF meter to begin a transaction (if OCMF meter handler is set) */ - txTriggerConditions.push_back([this] () -> TxCondition { - if (!session) - return TxCondition::Inactive; - return getSessionIdTag() ? TxCondition::Active : TxCondition::Inactive; + txProcess.addTrigger([this] () -> TxTrigger { + auto transaction = this->context.getTransactionService()->getTransactionStore().getActiveTransaction(this->connectorId); + + if (transaction && transaction->isInSession() && transaction->isActive()) { + return TxTrigger::Active; + } else { + return TxTrigger::Inactive; + } }); - txEnableSequence.push_back([this] (TxCondition cond) -> TxEnableState { + txProcess.addEnableStep([this] (TxTrigger cond) -> TxEnableState { if (onOcmfMeterPollTx) { return onOcmfMeterPollTx(cond); } - return cond == TxCondition::Active ? TxEnableState::Active : TxEnableState::Inactive; + return cond == TxTrigger::Active ? TxEnableState::Active : TxEnableState::Inactive; }); - txEnableSequence.push_back([this] (TxCondition cond) -> TxEnableState { + txProcess.addEnableStep([this] (TxTrigger cond) -> TxEnableState { if (onConnectorLockPollTx) { return onConnectorLockPollTx(cond); } - return cond == TxCondition::Active ? TxEnableState::Active : TxEnableState::Inactive; + return cond == TxTrigger::Active ? TxEnableState::Active : TxEnableState::Inactive; }); } @@ -95,37 +84,30 @@ OcppEvseState ConnectorStatus::inferenceStatus() { } } + auto transaction = context.getTransactionService()->getTransactionStore().getActiveTransaction(connectorId); + if (getErrorCode() != nullptr) { return OcppEvseState::Faulted; + } else if (!transaction) { //won't start new transactions if cached tx queue is full + return OcppEvseState::Unavailable; } else if (*availability == AVAILABILITY_INOPERATIVE) { return OcppEvseState::Unavailable; - } else if (rebooting && getTransactionId() < 0) { + } else if (rebooting && !transaction->isRunning()) { return OcppEvseState::Unavailable; - } else if (getTransactionId() == 0 && // i.e. Tx pending or EVSE offline. Check if offline Tx is OFF - !(*localAuthorizeOffline && strcmp(*localAuthorizeOffline, "false")) && - !(*localPreAuthorize && strcmp(*localPreAuthorize, "false"))) { - //All modes for offline Tx are off - return OcppEvseState::Preparing; //see other Preparing case - } else if (getTransactionId() >= 0) { + } else if (transaction->isRunning()) { //Transaction is currently running if ((connectorEnergizedSampler && !connectorEnergizedSampler()) || - idTagInvalidated) { + !transaction->isActive() || //will forbid charging + transaction->isIdTagDeauthorized()) { return OcppEvseState::SuspendedEVSE; } if (evRequestsEnergySampler && !evRequestsEnergySampler()) { return OcppEvseState::SuspendedEV; } return OcppEvseState::Charging; - } else if (txEnable == TxEnableState::Inactive) { + } else if (!txProcess.existsActiveTrigger() && txProcess.getState() == TxEnableState::Inactive) { return OcppEvseState::Available; - } else if (txEnable == TxEnableState::Pending || - txEnable == TxEnableState::Active) { //reached if Tx init is delayed - - if (txEnable == TxEnableState::Active) { // TODO verify if actually possible - AO_DBG_VERBOSE("Infered Active"); // - (void)0; // - } // - + } else { /* * Either in Preparing or Finishing state. Only way to know is from previous state */ @@ -139,7 +121,7 @@ OcppEvseState ConnectorStatus::inferenceStatus() { return OcppEvseState::Preparing; } } - + AO_DBG_VERBOSE("Cannot infere status"); return OcppEvseState::Faulted; //internal error } @@ -150,112 +132,99 @@ bool ConnectorStatus::ocppPermitsCharge() { return false; } - if (idTagInvalidated) { - return false; - } + auto transaction = context.getTransactionService()->getTransactionStore().getActiveTransaction(connectorId); - OcppEvseState state = inferenceStatus(); - - return state == OcppEvseState::Charging || - state == OcppEvseState::SuspendedEV || - state == OcppEvseState::SuspendedEVSE; + return transaction && + transaction->isRunning() && + transaction->isActive() && + !getErrorCode() && + !transaction->isIdTagDeauthorized(); } OcppMessage *ConnectorStatus::loop() { - if (getTransactionId() < 0 && *availability == AVAILABILITY_INOPERATIVE_SCHEDULED) { + + auto transaction = context.getTransactionService()->getTransactionStore().getActiveTransaction(connectorId); + + if ((!transaction || !transaction->isRunning()) && *availability == AVAILABILITY_INOPERATIVE_SCHEDULED) { *availability = AVAILABILITY_INOPERATIVE; saveState(); } - - if (connectorPluggedSampler) { - if (getTransactionId() >= 0 && !connectorPluggedSampler()) { - if (!*stopTransactionOnEVSideDisconnect || strcmp(*stopTransactionOnEVSideDisconnect, "false")) { - endSession("EVDisconnected"); + + if (transaction) { //begin exclusively transaction-related operations + + if (connectorPluggedSampler) { + if (transaction->isRunning() && transaction->isInSession() && !connectorPluggedSampler()) { + if (!*stopTransactionOnEVSideDisconnect || strcmp(*stopTransactionOnEVSideDisconnect, "false")) { + AO_DBG_DEBUG("Stop Tx due to EV disconnect"); + transaction->setStopReason("EVDisconnected"); + transaction->endSession(); + transaction->commit(); + } } } - } - auto txTrigger = txTriggerConditions.empty() ? TxCondition::Inactive : TxCondition::Active; - txEnable = TxEnableState::Inactive; - - if (*availability == AVAILABILITY_INOPERATIVE) { - txTrigger = TxCondition::Inactive; - } + if (transaction->isInSession() && + !transaction->getStartRpcSync().isRequested() && + transaction->getSessionTimestamp() > MIN_TIME && + connectionTimeOut && *connectionTimeOut > 0 && + context.getOcppTime().getOcppTimestampNow() - transaction->getSessionTimestamp() >= (otime_t) *connectionTimeOut) { + + AO_DBG_INFO("Session mngt: timeout"); + transaction->endSession(); + transaction->commit(); + } - if (txTrigger == TxCondition::Active) { - for (auto trigger = txTriggerConditions.begin(); trigger != txTriggerConditions.end(); trigger++) { - auto result = trigger->operator()(); - if (result == TxCondition::Active) { - txEnable = TxEnableState::Pending; - } else { - txTrigger = TxCondition::Inactive; + if (transaction->isInSession() && transaction->isIdTagDeauthorized()) { + if (!*stopTransactionOnInvalidId || strcmp(*stopTransactionOnInvalidId, "false")) { + AO_DBG_DEBUG("DeAuthorize session"); + transaction->setStopReason("DeAuthorized"); + transaction->endSession(); + transaction->commit(); } } - } - if (txTrigger == TxCondition::Active) { - txEnable = TxEnableState::Active; + txProcess.evaluateProcessSteps(transaction->getTxNr()); + auto txEnable = txProcess.getState(); - for (auto step = txEnableSequence.rbegin(); step != txEnableSequence.rend(); step++) { - auto result = step->operator()(TxCondition::Active); - if (result != TxEnableState::Active) { - txEnable = TxEnableState::Pending; - break; + /* + * Check conditions for start or stop transaction + */ + + if (txEnable == TxEnableState::Active) { + + if (transaction->isPreparing() && !getErrorCode()) { + //start Transaction + + AO_DBG_INFO("Session mngt: trigger StartTransaction"); + //return new StartTransaction(connectorId); + auto seqNr = context.getTransactionService()->getTransactionSequence().reserveSeqNr(); + AO_DBG_DEBUG("Reserved SeqNr %u", seqNr); + transaction->getStartRpcSync().setRequested(seqNr); + transaction->commit(); + return new StartTransaction(transaction); } - } - } else { - for (auto step = txEnableSequence.begin(); step != txEnableSequence.end(); step++) { - auto result = step->operator()(TxCondition::Inactive); - if (result != TxEnableState::Inactive) { - txEnable = TxEnableState::Pending; - break; + } else if (transaction->isRunning()) { + + if (transaction->isInSession()) { + AO_DBG_DEBUG("Tx process not active"); + transaction->endSession(); + transaction->commit(); } - } - } + if (txEnable == TxEnableState::Inactive) { + //stop transaction + auto seqNr = context.getTransactionService()->getTransactionSequence().reserveSeqNr(); + transaction->getStopRpcSync().setRequested(seqNr); + transaction->commit(); - /* - * Check conditions for start or stop transaction - */ - if (txEnable == TxEnableState::Active) { - //check if not in transaction yet - if (getTransactionId() < 0 && - !getErrorCode()) { - //start Transaction - - AO_DBG_DEBUG("Session mngt: txId=%i, connectorPlugged = %s, session=%d", - getTransactionId(), - connectorPluggedSampler ? (connectorPluggedSampler() ? "plugged" : "unplugged") : "undefined", - session); - AO_DBG_INFO("Session mngt: trigger StartTransaction"); - return new StartTransaction(connectorId); - } - } else { - //check if still in transaction - if (getTransactionId() >= 0) { - //stop transaction - - AO_DBG_DEBUG("Session mngt: txId=%i, connectorPlugged = %s, session=%d", - getTransactionId(), - connectorPluggedSampler ? (connectorPluggedSampler() ? "plugged" : "unplugged") : "undefined", - session); - AO_DBG_INFO("Session mngt: trigger StopTransaction"); - return new StopTransaction(connectorId, endReason[0] != '\0' ? endReason : nullptr); - } - } + AO_DBG_INFO("Session mngt: trigger StopTransaction"); - if (connectionTimeOutListen) { - if (getTransactionId() >= 0 || !session) { - AO_DBG_DEBUG("Session mngt: release connectionTimeOut"); - connectionTimeOutListen = false; - } else { - if (ao_tick_ms() - connectionTimeOutTimestamp >= ((ulong) *connectionTimeOut) * 1000UL) { - AO_DBG_INFO("Session mngt: timeout"); - endSession(); - connectionTimeOutListen = false; + AO_DBG_DEBUG("Reserved SeqNr %u", seqNr); + + return new StopTransaction(transaction); } } - } + } //end transaction-related operations auto inferedStatus = inferenceStatus(); @@ -289,82 +258,92 @@ const char *ConnectorStatus::getErrorCode() { } void ConnectorStatus::beginSession(const char *sessionIdTag) { - AO_DBG_DEBUG("Begin session with idTag %s, overwriting idTag %s", sessionIdTag != nullptr ? sessionIdTag : "", idTag); + + auto transaction = context.getTransactionService()->getTransactionStore().getActiveTransaction(connectorId); + + if (!transaction) { + AO_DBG_ERR("Could not allocate Tx"); + return; + } + + AO_DBG_DEBUG("Begin session with idTag %s, overwriting idTag %s", sessionIdTag != nullptr ? sessionIdTag : "", transaction->getIdTag()); if (!sessionIdTag || *sessionIdTag == '\0') { //input string is empty - snprintf(idTag, IDTAG_LEN_MAX + 1, "A0-00-00-00"); + transaction->setIdTag("A0-00-00-00"); } else { - snprintf(idTag, IDTAG_LEN_MAX + 1, "%s", sessionIdTag); + transaction->setIdTag(sessionIdTag); } - sIdTag->setValue(idTag, IDTAG_LEN_MAX + 1); - saveState(); - session = true; - idTagInvalidated = false; - memset(endReason, '\0', REASON_LEN_MAX + 1); + transaction->setSessionTimestamp(context.getOcppTime().getOcppTimestampNow()); - connectionTimeOutListen = true; - connectionTimeOutTimestamp = ao_tick_ms(); + transaction->commit(); } void ConnectorStatus::endSession(const char *reason) { - AO_DBG_DEBUG("End session with idTag %s for reason %s, %s previous reason", - idTag, reason ? reason : "undefined", - endReason[0] == '\0' ? "no" : "overruled by"); - if (session) { - memset(idTag, '\0', IDTAG_LEN_MAX + 1); - *sIdTag = ""; - saveState(); - } - session = false; - if (reason && endReason[0] == '\0') { - snprintf(endReason, REASON_LEN_MAX + 1, "%s", reason); + auto transaction = context.getTransactionService()->getTransactionStore().getActiveTransaction(connectorId); + + if (!transaction) { + AO_DBG_ERR("Could not allocate Tx"); + return; } - connectionTimeOutListen = false; -} + if (transaction->isInSession()) { + AO_DBG_DEBUG("End session with idTag %s for reason %s, %s previous reason", + transaction->getIdTag(), reason ? reason : "undefined", + transaction->getStopReason() == '\0' ? "no" : "overruled by"); -void ConnectorStatus::setIdTagInvalidated() { - if (session) { - idTagInvalidated = true; - if (!*stopTransactionOnInvalidId || strcmp(*stopTransactionOnInvalidId, "false")) { - endSession("DeAuthorized"); + if (reason) { + transaction->setStopReason(reason); } - } else { - AO_DBG_WARN("Cannot invalidate IdTag outside of session"); + transaction->endSession(); + transaction->commit(); } } const char *ConnectorStatus::getSessionIdTag() { - return session ? idTag : nullptr; -} -uint16_t ConnectorStatus::getSessionWriteCount() { - return sIdTag->getValueRevision(); -} + auto transaction = context.getTransactionService()->getTransactionStore().getActiveTransaction(connectorId); -int ConnectorStatus::getTransactionId() { - return *transactionId; -} + if (!transaction) { + return nullptr; + } -int ConnectorStatus::getTransactionIdSync() { - return transactionIdSync; + return transaction->isInSession() ? transaction->getIdTag() : nullptr; } -void ConnectorStatus::setTransactionIdSync(int id) { - transactionIdSync = id; +uint16_t ConnectorStatus::getSessionWriteCount() { + auto transaction = context.getTransactionService()->getTransactionStore().getActiveTransaction(connectorId); + + return transaction ? transaction->getTxNr() : 0; } -uint16_t ConnectorStatus::getTransactionWriteCount() { - return transactionId->getValueRevision(); +int ConnectorStatus::getTransactionId() { + auto transaction = context.getTransactionService()->getTransactionStore().getActiveTransaction(connectorId); + + if (!transaction) { + return -1; + } + + if (transaction->isRunning()) { + if (transaction->getStartRpcSync().isConfirmed()) { + return transaction->getTransactionId(); + } else { + return 0; + } + } else { + return -1; + } } -void ConnectorStatus::setTransactionId(int id) { - int prevTxId = *transactionId; - *transactionId = id; - if (id != 0 || prevTxId > 0) - saveState(); +int ConnectorStatus::getTransactionIdSync() { + auto transaction = context.getTransactionService()->getTransactionStore().getTransactionSync(connectorId); + + if (transaction) { + return transaction->getTransactionId(); + } else { + return -1; + } } int ConnectorStatus::getAvailability() { @@ -390,8 +369,8 @@ void ConnectorStatus::setRebooting(bool rebooting) { void ConnectorStatus::setConnectorPluggedSampler(std::function connectorPlugged) { this->connectorPluggedSampler = connectorPlugged; - txTriggerConditions.push_back([this] () -> TxCondition { - return connectorPluggedSampler() ? TxCondition::Active : TxCondition::Inactive; + txProcess.addTrigger([this] () -> TxTrigger { + return connectorPluggedSampler() ? TxTrigger::Active : TxTrigger::Inactive; }); } @@ -419,10 +398,10 @@ std::function()> ConnectorStatus::getOnUnlockConnector() { return this->onUnlockConnector; } -void ConnectorStatus::setConnectorLock(std::function onConnectorLockPollTx) { +void ConnectorStatus::setConnectorLock(std::function onConnectorLockPollTx) { this->onConnectorLockPollTx = onConnectorLockPollTx; } -void ConnectorStatus::setTxBasedMeterUpdate(std::function onOcmfMeterPollTx) { +void ConnectorStatus::setTxBasedMeterUpdate(std::function onOcmfMeterPollTx) { this->onOcmfMeterPollTx = onOcmfMeterPollTx; } diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h index ec5492ba..43ddc867 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -23,6 +24,7 @@ namespace ArduinoOcpp { class OcppModel; class OcppMessage; +class Transaction; class ConnectorStatus { private: @@ -33,18 +35,6 @@ class ConnectorStatus { std::shared_ptr> availability; bool rebooting = false; //report connector inoperative and reject new charging sessions - bool session = false; - char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; - bool idTagInvalidated {false}; //if StartTransaction.conf() has status != "Accepted" - std::shared_ptr> sIdTag; - std::shared_ptr> transactionId; - int transactionIdSync = -1; - char endReason [REASON_LEN_MAX + 1] = {'\0'}; - - std::shared_ptr> connectionTimeOut; //in seconds - bool connectionTimeOutListen {false}; - ulong connectionTimeOutTimestamp {0}; //in milliseconds - std::function connectorPluggedSampler; std::function evRequestsEnergySampler; std::function connectorEnergizedSampler; @@ -58,13 +48,12 @@ class ConnectorStatus { std::function()> onUnlockConnector; - std::function onConnectorLockPollTx; - std::function onOcmfMeterPollTx; + std::function onConnectorLockPollTx; + std::function onOcmfMeterPollTx; - std::vector> txTriggerConditions; - std::vector> txEnableSequence; - TxEnableState txEnable {TxEnableState::Inactive}; // = Result of Trigger and Enable Sequence + TransactionProcess txProcess; + std::shared_ptr> connectionTimeOut; //in seconds std::shared_ptr> stopTransactionOnInvalidId; std::shared_ptr> stopTransactionOnEVSideDisconnect; std::shared_ptr> unlockConnectorOnEVSideDisconnect; @@ -85,14 +74,10 @@ class ConnectorStatus { */ void beginSession(const char *idTag); void endSession(const char *reason = nullptr); - void setIdTagInvalidated(); //if StartTransaction.conf() has status != "Accepted" const char *getSessionIdTag(); uint16_t getSessionWriteCount(); int getTransactionId(); int getTransactionIdSync(); - uint16_t getTransactionWriteCount(); - void setTransactionId(int id); - void setTransactionIdSync(int id); int getAvailability(); void setAvailability(bool available); @@ -104,7 +89,6 @@ class ConnectorStatus { void addConnectorErrorCodeSampler(std::function connectorErrorCode); void saveState(); - //void recoverState(); OcppMessage *loop(); @@ -115,8 +99,8 @@ class ConnectorStatus { void setOnUnlockConnector(std::function()> unlockConnector); std::function()> getOnUnlockConnector(); - void setConnectorLock(std::function lockConnector); - void setTxBasedMeterUpdate(std::function updateTxBasedMeter); + void setConnectorLock(std::function lockConnector); + void setTxBasedMeterUpdate(std::function updateTxBasedMeter); }; } //end namespace ArduinoOcpp diff --git a/src/ArduinoOcpp/Tasks/ChargePointStatus/TransactionPrerequisites.h b/src/ArduinoOcpp/Tasks/ChargePointStatus/TransactionPrerequisites.h index d73b3310..9a503718 100644 --- a/src/ArduinoOcpp/Tasks/ChargePointStatus/TransactionPrerequisites.h +++ b/src/ArduinoOcpp/Tasks/ChargePointStatus/TransactionPrerequisites.h @@ -11,7 +11,12 @@ namespace ArduinoOcpp { * Type definitions for extending the transaction initiation process */ -enum class TxCondition { +enum class TxPrecondition { + Active, + Inactive +}; + +enum class TxTrigger { Active, Inactive }; diff --git a/src/ArduinoOcpp/Tasks/Transactions/OrderedOperationsQueue.cpp b/src/ArduinoOcpp/Tasks/Transactions/OrderedOperationsQueue.cpp new file mode 100644 index 00000000..e72d0c73 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Transactions/OrderedOperationsQueue.cpp @@ -0,0 +1,29 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#include +#include +#include + +using namespace ArduinoOcpp; + +void OrderedOperationsQueue::addOcppOperation(uint seqNr, std::unique_ptr op) { + operations.push_back(std::pair>{seqNr, std::move(op)}); +} + +void OrderedOperationsQueue::sort(uint seqEnd) { + std::sort(operations.begin(), operations.end(), [seqEnd] (std::pair>& a, std::pair>& b) { + uint da = (seqEnd - a.first + MAX_TXEVENT_CNT) % MAX_TXEVENT_CNT; //distance between a to seqEnd + uint db = (seqEnd - b.first + MAX_TXEVENT_CNT) % MAX_TXEVENT_CNT; + return da > db; //sort descending by distance to seqEnd <=> sort ascending by seqNr + }); +} + +void OrderedOperationsQueue::moveTo(std::vector>& dst) { + for (auto el = operations.begin(); el != operations.end(); el++) { + dst.emplace_back(std::move(el->second)); + } + + operations.clear(); +} diff --git a/src/ArduinoOcpp/Tasks/Transactions/OrderedOperationsQueue.h b/src/ArduinoOcpp/Tasks/Transactions/OrderedOperationsQueue.h new file mode 100644 index 00000000..52a9563f --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Transactions/OrderedOperationsQueue.h @@ -0,0 +1,28 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef OPERATIONSQUEUE_H +#define OPERATIONSQUEUE_H + +#include +#include +#include + +namespace ArduinoOcpp { + +class OrderedOperationsQueue { +private: + std::vector>> operations; +public: + + void addOcppOperation(uint seqNr, std::unique_ptr op); + + void sort(uint seqEnd); //sort and take seqEnd as largest order number + + void moveTo(std::vector>& dst); //move contents of operations to dst +}; + +} + +#endif diff --git a/src/ArduinoOcpp/Tasks/Transactions/Transaction.cpp b/src/ArduinoOcpp/Tasks/Transactions/Transaction.cpp new file mode 100644 index 00000000..337a2d66 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Transactions/Transaction.cpp @@ -0,0 +1,218 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#include +#include +#include +#include + +#include + +using namespace ArduinoOcpp; + +void TransactionRPC::requestWithMsgId(int msgId) { + context.setInitiatedMsgId(seqNr, msgId); +} + +bool TransactionRPC::serializeSessionState(JsonObject rpc) { + rpc["requested"] = requested; + if (requested) { + rpc["seqNr"] = seqNr; + } + rpc["confirmed"] = confirmed; + return true; +} + +bool Transaction::serializeSessionState(DynamicJsonDocument& out) { + out = DynamicJsonDocument(1024); + JsonObject state = out.to(); + + JsonObject sessionState = state.createNestedObject("session"); + if (session.idTag[0] != '\0') { + sessionState["idTag"] = session.idTag; + } + if (session.timestamp > MIN_TIME) { + char timeStr [JSONDATE_LENGTH + 1] = {'\0'}; + session.timestamp.toJsonString(timeStr, JSONDATE_LENGTH + 1); + sessionState["timestamp"] = timeStr; + } + if (session.txProfileId >= 0) { + sessionState["txProfileId"] = session.txProfileId; + } + if (!session.active) { + sessionState["active"] = session.active; + } + + JsonObject txStart = state.createNestedObject("start"); + + JsonObject txStartRPC = txStart.createNestedObject("rpc"); + if (!start.rpc.serializeSessionState(txStartRPC)) { + return false; + } + + JsonObject txStartClientSide = txStart.createNestedObject("client"); + + if (start.client.timestamp > MIN_TIME) { + char timeStr [JSONDATE_LENGTH + 1] = {'\0'}; + start.client.timestamp.toJsonString(timeStr, JSONDATE_LENGTH + 1); + txStartClientSide["timestamp"] = timeStr; + } + + if (start.client.meter >= 0) { + txStartClientSide["meter"] = start.client.meter; + } + + + if (start.rpc.confirmed) { + JsonObject txStartServerSide = txStart.createNestedObject("server"); + txStartServerSide["transactionId"] = start.server.transactionId; + txStartServerSide["authorized"] = start.server.authorized; + } + + JsonObject txStop = state.createNestedObject("stop"); + + JsonObject txStopRPC = txStop.createNestedObject("rpc"); + if (!stop.rpc.serializeSessionState(txStopRPC)) { + return false; + } + + JsonObject txStopClientSide = txStop.createNestedObject("client"); + + if (stop.client.timestamp > MIN_TIME) { + char timeStr [JSONDATE_LENGTH + 1] = {'\0'}; + stop.client.timestamp.toJsonString(timeStr, JSONDATE_LENGTH + 1); + txStopClientSide["timestamp"] = timeStr; + } + + if (stop.client.meter >= 0) { + txStopClientSide["meter"] = stop.client.meter; + } + + if (stop.client.idTag[0] != '\0') { + txStopClientSide["idTag"] = stop.client.idTag; + } + + if (stop.client.reason[0] != '\0') { + txStopClientSide["reason"] = stop.client.reason; + } + + //if (stop.rpc.confirmed) { + // JsonObject txStopServerSide = txStop.createNestedObject("server"); + //} + + if (out.overflowed()) { + AO_DBG_ERR("JSON capacity exceeded"); + return false; + } + + return true; +} + +bool TransactionRPC::deserializeSessionState(JsonObject rpc) { + if (rpc["requested"] | false) { + requested = true; + seqNr = rpc["seqNr"] | 0; + } + if (rpc["confirmed"] | false) { + confirmed = true; + } + return true; +} + +bool Transaction::deserializeSessionState(JsonObject state) { + + JsonObject sessionState = state["session"]; + + if (sessionState.containsKey("idTag")) { + if (snprintf(session.idTag, sizeof(session.idTag), "%s", sessionState["idTag"] | "") < 0) { + AO_DBG_ERR("Read err"); + return false; + } + } + if (sessionState.containsKey("timestamp")) { + session.timestamp.setTime(sessionState["timestamp"] | "Invalid"); + } + if (sessionState.containsKey("txProfileId")) { + session.txProfileId = sessionState["txProfileId"] | -1; + } + if (sessionState.containsKey("active")) { + session.active = sessionState["active"] | true; + } + + JsonObject txStart = state["start"]; + + if (txStart.containsKey("rpc")) { + JsonObject txStartRPC = txStart["rpc"]; + AO_DBG_DEBUG("Deserialize RPC. Raw:") + serializeJson(txStartRPC, Serial); + Serial.println(); + if (!start.rpc.deserializeSessionState(txStartRPC)) { + return false; + } + } + + JsonObject txStartClientSide = txStart["client"]; + + if (txStartClientSide.containsKey("timestamp")) { + start.client.timestamp.setTime(txStartClientSide["timestamp"] | "Invalid"); + } + + if (txStartClientSide.containsKey("meter")) { + start.client.meter = txStartClientSide["meter"] | 0; + } + + if (start.rpc.confirmed) { + JsonObject txStartServerSide = txStart["server"]; + start.server.transactionId = txStartServerSide["transactionId"] | -1; + start.server.authorized = txStartServerSide["authorized"] | false; + } + + JsonObject txStop = state["stop"]; + + if (txStop.containsKey("rpc")) { + JsonObject txStopRPC = txStop["rpc"]; + if (!stop.rpc.deserializeSessionState(txStopRPC)) { + return false; + } + } + + JsonObject txStopClientSide = txStop["client"]; + + if (txStopClientSide.containsKey("timestamp")) { + stop.client.timestamp.setTime(txStopClientSide["timestamp"] | "Invalid"); + } + + if (txStopClientSide.containsKey("meter")) { + stop.client.meter = txStopClientSide["meter"] | 0; + } + + if (txStopClientSide.containsKey("idTag")) { + if (snprintf(stop.client.idTag, sizeof(stop.client.idTag), "%s", txStopClientSide["idTag"] | "") < 0) { + AO_DBG_ERR("Read err"); + return false; + } + } + + if (txStopClientSide.containsKey("reason")) { + if (snprintf(stop.client.reason, sizeof(stop.client.reason), "%s", txStopClientSide["reason"] | "") < 0) { + AO_DBG_ERR("Read err"); + return false; + } + } + + AO_DBG_DEBUG("DUMP TX"); + AO_DBG_DEBUG("Session | idTag %s", session.idTag); + AO_DBG_DEBUG("Start RPC | req: %i, seq: %u, conf: %i", start.rpc.requested, start.rpc.seqNr, start.rpc.confirmed); + AO_DBG_DEBUG("Stop RPC | req: %i, seq: %u, conf: %i", stop.rpc.requested, stop.rpc.seqNr, stop.rpc.confirmed); + + //if (stop.rpc.confirmed) { + // JsonObject txStopServerSide = txStop["server"]; + //} + + return true; +} + +bool Transaction::commit() { + return context.getTransactionStore().commit(this); +} diff --git a/src/ArduinoOcpp/Tasks/Transactions/Transaction.h b/src/ArduinoOcpp/Tasks/Transactions/Transaction.h new file mode 100644 index 00000000..f5336b1b --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Transactions/Transaction.h @@ -0,0 +1,196 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef TRANSACTION_H +#define TRANSACTION_H + +#include +#include +#include +#include + +namespace ArduinoOcpp { + +/* + * A transaction is initiated by the client (charging station) and processed by the server (central system). + * The client side of a transaction is all data that is generated or collected at the charging station. The + * server side is all transaction data that is assigned by the central system. + * + * "ClientTransaction" is short for the client-side data, and the same goes for "ServerTranaction". The rest + * of the terminology is documented in OCPP 1.6 Specification - Edition 2, sections 3.6, 4.8, 4.10 and 5.11. + */ + +class TransactionService; + +class TransactionRPC { +private: + friend class Transaction; + + TransactionService& context; + + uint seqNr = 0; + bool requested = false; + bool confirmed = false; + + bool serializeSessionState(JsonObject out); + bool deserializeSessionState(JsonObject in); +public: + TransactionRPC(TransactionService& context) : context(context) { } + + void requestWithMsgId(int msgId); + + void setRequested(uint seqNr) { + this->seqNr = seqNr; + this->requested = true; + } + bool isRequested() {return requested;} + uint getSeqNr() {return seqNr;} + void confirm() {confirmed = true;} + bool isConfirmed() {return confirmed;} + bool isCompleted() {return isRequested() && isConfirmed();} +}; + +class ClientTransactionStart { +private: + friend class Transaction; + + OcppTimestamp timestamp = MIN_TIME; //timestamp of StartTx; can be set before actually initiating + int32_t meter = -1; //meterStart of StartTx +}; + +class ServerTransactionStart { +private: + friend class Transaction; + + bool authorized = true; //authorization status; only valid if confirmed = true + int transactionId = -1; //only valid if confirmed = true +}; + +class TransactionStart { +private: + friend class Transaction; + + TransactionRPC rpc; + ClientTransactionStart client; + ServerTransactionStart server; +public: + TransactionStart(TransactionService& context) : rpc(context) { } +}; + +class ClientTransactionStop { +private: + friend class Transaction; + + char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; + OcppTimestamp timestamp = MIN_TIME; + int32_t meter = -1; + char reason [REASON_LEN_MAX + 1] = {'\0'}; +}; + +class ServerTransactionStop { +//no data at the moment +}; + +class TransactionStop { +private: + friend class Transaction; + + TransactionRPC rpc; + ClientTransactionStop client; + ServerTransactionStop server; +public: + TransactionStop(TransactionService& context) : rpc(context) { } +}; + +class ChargingSession { +private: + friend class Transaction; + + char idTag [IDTAG_LEN_MAX + 1] = {'\0'}; + OcppTimestamp timestamp = MIN_TIME; + int txProfileId = -1; + + bool active = true; //true: ignore + //false before StartTx init: abort + //false between StartTx init and StopTx init: end + //false after StopTx init: ignore +}; + +class Transaction { +private: + TransactionService& context; + + ChargingSession session; //data that exists before the tx + TransactionStart start; + TransactionStop stop; + + int connectorId = -1; + uint txNr = 0; //only valid if session.connectorId is >= 0 +public: + Transaction(TransactionService& context, uint connectorId, uint txNr) : + context(context), + start(context), + stop(context), + connectorId(connectorId), + txNr(txNr) {} + + bool serializeSessionState(DynamicJsonDocument& out); + bool deserializeSessionState(JsonObject in); + + int getConnectorId() {return connectorId;} + void setConnectorId(uint connectorId) {this->connectorId = connectorId;} + uint getTxNr() {return txNr;} //only valid if getConnectorId() >= 0 + void setTxNr(uint txNr) {this->txNr = txNr;} + + TransactionRPC& getStartRpcSync() {return start.rpc;} + + TransactionRPC& getStopRpcSync() {return stop.rpc;} + + bool isAborted() {return !start.rpc.requested && !session.active;} + bool isCompleted() {return start.rpc.isRequested() && start.rpc.isConfirmed() && + stop.rpc.isRequested() && stop.rpc.isConfirmed();} + bool isPending() {return !isAborted() && !isCompleted();} + bool isPreparing() {return session.active && !start.rpc.isRequested() && !stop.rpc.isRequested();} + bool isRunning() {return start.rpc.isRequested() && !stop.rpc.isRequested();} + bool isActive() {return session.active && !stop.rpc.isRequested();} + bool isInSession() {return isActive() && *session.idTag;} + + const char *getIdTag() {return session.idTag;} //only for testing in StartTx.req + void setIdTag(const char *idTag) {snprintf(session.idTag, IDTAG_LEN_MAX + 1, "%s", idTag);} + OcppTimestamp& getSessionTimestamp() {return session.timestamp;} + void setSessionTimestamp(OcppTimestamp timestamp) {session.timestamp = timestamp;} + + const char *getStopReason() {return stop.client.reason;} + void setStopReason(const char *reason) {snprintf(stop.client.reason, REASON_LEN_MAX + 1, "%s", reason);} + void endSession() {session.active = false;} + + void setIdTagDeauthorized() {start.server.authorized = false;} + bool isIdTagDeauthorized() {return start.rpc.isConfirmed() && !start.server.authorized;} + + int getTransactionId() {return start.server.transactionId;} + void setTransactionId(int transactionId) {start.server.transactionId = transactionId;} + + void setMeterStart(int32_t meter) {start.client.meter = meter;} + bool isMeterStartDefined() {return start.client.meter >= 0;} //should introduce extra variable later + int32_t getMeterStart() {return start.client.meter;} + + void setStartTimestamp(OcppTimestamp timestamp) {start.client.timestamp = timestamp;} + OcppTimestamp& getStartTimestamp() {return start.client.timestamp;} + + void setMeterStop(int32_t meter) {stop.client.meter = meter;} + bool isMeterStopDefined() {return stop.client.meter >= 0;} //should introduce extra variable later + int32_t getMeterStop() {return stop.client.meter;} + + void setStopTimestamp(OcppTimestamp timestamp) {stop.client.timestamp = timestamp;} + OcppTimestamp& getStopTimestamp() {return stop.client.timestamp;} + + const char *getStopIdTag() {return stop.client.idTag;} //only for testing in StartTx.req + void setStopIdTag(const char *idTag) {snprintf(stop.client.idTag, IDTAG_LEN_MAX + 1, "%s", idTag);} + + bool commit(); +}; + +} + +#endif diff --git a/src/ArduinoOcpp/Tasks/Transactions/TransactionProcess.cpp b/src/ArduinoOcpp/Tasks/Transactions/TransactionProcess.cpp new file mode 100644 index 00000000..fe31d2dd --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Transactions/TransactionProcess.cpp @@ -0,0 +1,113 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#include + +#include + +#define AO_TXPROC_FN AO_FILENAME_PREFIX "/txproc.cnf" + +using namespace ArduinoOcpp; + +TransactionProcess::TransactionProcess(uint connectorId) { + char key [30] = {'\0'}; + if (snprintf(key, 30, "AO_txNrRef_%u", connectorId) < 0) { + AO_DBG_ERR("Invalid key"); + (void)0; + } + txNrRef = declareConfiguration(key, 0, AO_TXPROC_FN, false, false, true, false); + if (!txNrRef || *txNrRef < 0) { + AO_DBG_ERR("Initialization failure"); + } +} + +/* + * Evaluate the preconditions and triggers. If all are Active, then execute the transaction enable sequence. + * That is an ordered sequence of preparation steps which must be true before the transaction can start. When + * the transaction is finished, disable the preparation steps in reversed order. + * + * txEnable is the output variable. See getState() for getting the result. + */ +void TransactionProcess::evaluateProcessSteps(uint txNr) { + +#if AO_DBG_LEVEL >= AO_DL_DEBUG + //print transitions to debug console + TxEnableState txEnableBefore = txEnable; +#endif + + //Check Tx preconditions: Tx is only possible if all of them are true; search for a false precondition + auto txPrecondition = TxPrecondition::Active; + for (auto cond : txPreconditions) { + if (cond() != TxPrecondition::Active) { + txPrecondition = TxPrecondition::Inactive; + break; + } + } + + //Check Tx triggers: all of them need to be true to trigger a tx; prepare that search here + auto txTrigger = txTriggers.empty() ? TxTrigger::Inactive : TxTrigger::Active; + if (txPrecondition != TxPrecondition::Active) { //Only trigger a tx if the preconditions are met + txTrigger = TxTrigger::Inactive; + } + + //Check if the current process is obsolete + if (txNrRef && (uint) *txNrRef != txNr) { + txTrigger = TxTrigger::Inactive; + } + + //Determine if + // - No trigger is active -> activeTriggerExists = false, txTrigger = Inactive + // - All triggers are active -> activeTriggerExists = true, txTrigger = Active + // - Some are active, some not -> activeTriggerExists = true, txTrigger = Inactive + activeTriggerExists = false; + for (auto trigger = txTriggers.begin(); trigger != txTriggers.end(); trigger++) { + auto result = trigger->operator()(); + if (result == TxTrigger::Active) { + activeTriggerExists = true; + } else { + txTrigger = TxTrigger::Inactive; + } + } + + //Also check if all devices on the charger are enabled for the Tx + if (txTrigger == TxTrigger::Active) { //Search for an unready device + txEnable = TxEnableState::Active; + + for (auto step = txEnableSequence.rbegin(); step != txEnableSequence.rend(); step++) { + auto result = step->operator()(TxTrigger::Active); + if (result != TxEnableState::Active) { + txEnable = TxEnableState::Pending; + break; + } + } + } else { //Search for a device that is still enabled + txEnable = TxEnableState::Inactive; + + for (auto step = txEnableSequence.begin(); step != txEnableSequence.end(); step++) { + auto result = step->operator()(TxTrigger::Inactive); + if (result != TxEnableState::Inactive) { + txEnable = TxEnableState::Pending; + break; + } + } + } + + //If the current process is obsolete: Check if updating the tx reference is possible + if (txNrRef && (uint) *txNrRef != txNr) { + if (txEnable == TxEnableState::Inactive) { + AO_DBG_DEBUG("Upgrade to next tx: %u", txNr); + *txNrRef = txNr; + configuration_save(); + } + } + +#if AO_DBG_LEVEL >= AO_DL_DEBUG + if (txEnableBefore != txEnable) { + AO_DBG_DEBUG("Transition from %s to %s", + txEnableBefore == TxEnableState::Active ? "Active" : txEnableBefore == TxEnableState::Inactive ? "Inactive" : "Pending", + txEnable == TxEnableState::Active ? "Active" : txEnable == TxEnableState::Inactive ? "Inactive" : "Pending"); + } +#endif + +} diff --git a/src/ArduinoOcpp/Tasks/Transactions/TransactionProcess.h b/src/ArduinoOcpp/Tasks/Transactions/TransactionProcess.h new file mode 100644 index 00000000..6240bb24 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Transactions/TransactionProcess.h @@ -0,0 +1,46 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef TRANSACTIONPROCESS_H +#define TRANSACTIONPROCESS_H + +#include +#include + +#include +#include + +namespace ArduinoOcpp { + +class TransactionProcess { +private: + + std::vector> txPreconditions; + std::vector> txTriggers; + std::vector> txEnableSequence; + + bool activeTriggerExists = false; + TxEnableState txEnable {TxEnableState::Inactive}; // = Result of Trigger and Enable Sequence + + std::shared_ptr> txNrRef; + +public: + + TransactionProcess(uint connectorId); + + void addPrecondition(std::function fn) {txPreconditions.push_back(fn);} + + void addTrigger(std::function fn) {txTriggers.push_back(fn);} + + void addEnableStep(std::function fn) {txEnableSequence.push_back(fn);} + + void evaluateProcessSteps(uint txNr); + + bool existsActiveTrigger() {return activeTriggerExists;} + TxEnableState getState() {return txEnable;} +}; + +} + +#endif diff --git a/src/ArduinoOcpp/Tasks/Transactions/TransactionSequence.cpp b/src/ArduinoOcpp/Tasks/Transactions/TransactionSequence.cpp new file mode 100644 index 00000000..b6809892 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Transactions/TransactionSequence.cpp @@ -0,0 +1,29 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#include +#include + +#include + +#define AO_TXSEQ_FN AO_FILENAME_PREFIX "/txseq.cnf" + +using namespace ArduinoOcpp; + +TransactionSequence::TransactionSequence() { + + seqEnd = declareConfiguration("AO_SEQEND", 0, AO_TXSEQ_FN, false, false, true, false); +} + +uint TransactionSequence::getSeqEnd() { + return *seqEnd; +} + +uint TransactionSequence::reserveSeqNr() { + uint seq = (uint) *seqEnd; + uint seqIncr = (seq + 1U) % MAX_TXEVENT_CNT; + *seqEnd = seqIncr; + configuration_save(); + return seq; +} diff --git a/src/ArduinoOcpp/Tasks/Transactions/TransactionSequence.h b/src/ArduinoOcpp/Tasks/Transactions/TransactionSequence.h new file mode 100644 index 00000000..76856ce0 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Transactions/TransactionSequence.h @@ -0,0 +1,28 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef TRANSACTIONSEQUENCE_H +#define TRANSACTIONSEQUENCE_H + +#include +#include + +#define MAX_TXEVENT_CNT 100000U + +namespace ArduinoOcpp { + +class TransactionSequence { +private: + std::shared_ptr> seqEnd; //one place after last event +public: + TransactionSequence(); + + uint getSeqEnd(); + + uint reserveSeqNr(); +}; + +} + +#endif diff --git a/src/ArduinoOcpp/Tasks/Transactions/TransactionService.cpp b/src/ArduinoOcpp/Tasks/Transactions/TransactionService.cpp new file mode 100644 index 00000000..b5dc1320 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Transactions/TransactionService.cpp @@ -0,0 +1,74 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#include +#include +#include +#include +#include +#include +#include + +using namespace ArduinoOcpp; + +#define AO_TXSERV_FN AO_FILENAME_PREFIX "/txserv.cnf" + +TransactionService::TransactionService(OcppEngine& context, uint nConnectors, std::shared_ptr filesystem) : + context(context), + txStore(*this, nConnectors, filesystem) { + + lastInitiatedSeqNr = declareConfiguration("LastSeqNr", MAX_TXEVENT_CNT, AO_TXSERV_FN, false, false, true, false); + lastInitiatedMsgCnt = declareConfiguration("LastMsgId", 1100000, AO_TXSERV_FN, false, false, true, false); + + txStore.restorePendingTransactions(); + txStore.submitPendingOperations(); +} + +void TransactionService::addRestoredOperation(uint seqNr, std::unique_ptr o) { + if (restoredOpsInitiated) { + AO_DBG_ERR("Can only restore at initialization. Abort"); + return; + } + if (seqNr == (uint) *lastInitiatedSeqNr) { + AO_DBG_DEBUG("Rebase msgIds: %i", (int) *lastInitiatedMsgCnt); + o->rebaseMsgId(*lastInitiatedMsgCnt); + } + restoredOps.addOcppOperation(seqNr, std::move(o)); +} + +void TransactionService::initiateRestoredOperations() { + if (restoredOpsInitiated) { + AO_DBG_ERR("Called two times. Abort"); + return; + } + + restoredOps.sort(txSequence.getSeqEnd()); + + std::vector> res; + + restoredOps.moveTo(res); + + for (auto operation = res.begin(); operation != res.end(); operation++) { + context.initiateOperation(std::move(*operation)); + } + res.clear(); + + restoredOpsInitiated = true; +} + +void TransactionService::setInitiatedMsgId(uint seqNr, int msgId) { + if (seqNr >= MAX_TXEVENT_CNT) { + AO_DBG_ERR("Invalid params"); + return; + } + if (*lastInitiatedSeqNr == seqNr && *lastInitiatedMsgCnt == msgId) { + //Nothing to update + return; + } + + AO_DBG_DEBUG("Set tx-related msg Id"); + *lastInitiatedSeqNr = seqNr; + *lastInitiatedMsgCnt = msgId; + configuration_save(); +} diff --git a/src/ArduinoOcpp/Tasks/Transactions/TransactionService.h b/src/ArduinoOcpp/Tasks/Transactions/TransactionService.h new file mode 100644 index 00000000..93cd63c3 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Transactions/TransactionService.h @@ -0,0 +1,44 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef TRANSACTIONSERVICE_H +#define TRANSACTIONSERVICE_H + +#include +#include +#include +#include + +namespace ArduinoOcpp { + +class OcppEngine; +class FilesystemAdapter; +class OcppOperation; + +class TransactionService { +private: + OcppEngine& context; + + TransactionSequence txSequence; + TransactionStore txStore; + OrderedOperationsQueue restoredOps;//restore pending operations from flash in case of a power loss + bool restoredOpsInitiated = false; + + std::shared_ptr> lastInitiatedSeqNr; + std::shared_ptr> lastInitiatedMsgCnt; //use this msgId for the current initiated operation +public: + TransactionService(OcppEngine& context, uint nConnectors, std::shared_ptr filesystem); + + void addRestoredOperation(uint seqNr, std::unique_ptr op); + void initiateRestoredOperations(); + + void setInitiatedMsgId(uint seqNr, int msgId); + + TransactionStore& getTransactionStore() {return txStore;} + TransactionSequence& getTransactionSequence() {return txSequence;} +}; + +} + +#endif diff --git a/src/ArduinoOcpp/Tasks/Transactions/TransactionStore.cpp b/src/ArduinoOcpp/Tasks/Transactions/TransactionStore.cpp new file mode 100644 index 00000000..8deeea6d --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Transactions/TransactionStore.cpp @@ -0,0 +1,308 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ArduinoOcpp; + +#ifndef AO_TXSTORE_DIR +#define AO_TXSTORE_DIR AO_FILENAME_PREFIX "/" +#endif + +#define AO_TXSTORE_META_FN AO_FILENAME_PREFIX "/txstore.jsn" + +#define MAX_QUEUE_SIZE 20U +#define MAX_TX_CNT 100000U + +ConnectorTransactionStore::ConnectorTransactionStore(TransactionService& context, uint connectorId, std::shared_ptr filesystem) : + context(context), + connectorId(connectorId), + filesystem(filesystem) { + + char key [30] = {'\0'}; + if (snprintf(key, 30, "AO_txBegin_%u", connectorId) < 0) { + AO_DBG_ERR("Invalid key"); + (void)0; + } + txBegin = declareConfiguration(key, 0, AO_TXSTORE_META_FN, false, false, true, false); +} + +void ConnectorTransactionStore::restorePendingTransactions() { + + if (!filesystem) { + AO_DBG_ERR("FS error"); + return; + } + + if (!txBegin || *txBegin < 0 || *txBegin > MAX_TX_CNT) { + AO_DBG_ERR("Invalid state"); + return; + } + + uint tx = *txBegin; + txEnd = tx; + + const uint MISSES_LIMIT = 3; + uint misses = 0; + + while (misses < MISSES_LIMIT) { //search until region without txs found + + char fn [MAX_PATH_SIZE] = {'\0'}; + auto ret = snprintf(fn, MAX_PATH_SIZE, AO_TXSTORE_DIR "tx" "-%u-%u.jsn", connectorId, tx); + if (ret < 0 || ret >= MAX_PATH_SIZE) { + AO_DBG_ERR("fn error: %i", ret); + break; //all files have same length + } + + auto doc = FilesystemUtils::loadJson(filesystem, fn); + + if (!doc) { + misses++; + tx++; + tx %= MAX_TX_CNT; + continue; + } + + std::shared_ptr transaction = std::make_shared(context, connectorId, tx); + JsonObject txJson = doc->as(); + if (!transaction->deserializeSessionState(txJson)) { + AO_DBG_ERR("Deserialization error"); + misses++; + tx++; + tx %= MAX_TX_CNT; + continue; + } + + if (!transaction->isPending()) { + AO_DBG_DEBUG("Drop aborted / finished tx"); + tx++; + tx %= MAX_TX_CNT; + continue; //normal behavior => don't increment misses + } + + transactions.push_back(std::move(transaction)); + + tx++; + tx %= MAX_TX_CNT; + txEnd = tx; + misses = 0; + + if (transactions.size() > MAX_QUEUE_SIZE) { //allow to exceed MAX_QUEUE_SIZE by 1 + AO_DBG_ERR("txBegin out of sync"); + break; + } + } + + AO_DBG_DEBUG("Restored %zu transactions", transactions.size()); +} + +void ConnectorTransactionStore::submitPendingOperations() { + for (auto& tx : transactions) { + + auto& startRpc = tx->getStartRpcSync(); + if (startRpc.isRequested() && !startRpc.isConfirmed()) { + //startTx has been initiated, but not been confirmed yet + AO_DBG_DEBUG("Restore StartTx, connectorId=%i, seqNr=%u", connectorId, startRpc.getSeqNr()); + + auto startOp = makeOcppOperation(new Ocpp16::StartTransaction(tx)); + context.addRestoredOperation(startRpc.getSeqNr(), std::move(startOp)); + } + + auto& stopRpc = tx->getStopRpcSync(); + if (stopRpc.isRequested() && !stopRpc.isConfirmed()) { + //stopTx has been initiated, but not been confirmed yet + AO_DBG_DEBUG("Restore StopTx, connectorId=%i, seqNr=%u", connectorId, stopRpc.getSeqNr()); + + //... add stop data + + auto stopOp = makeOcppOperation(new Ocpp16::StopTransaction(tx)); + context.addRestoredOperation(stopRpc.getSeqNr(), std::move(stopOp)); + } + } +} + +std::shared_ptr ConnectorTransactionStore::makeTransaction() { + + if (!filesystem) { + AO_DBG_ERR("Invalid state"); + return nullptr; + } + + if (transactions.size() >= MAX_QUEUE_SIZE) { + AO_DBG_WARN("Queue full"); + return nullptr; + } + + auto transaction = std::make_shared(context, connectorId, txEnd); + txEnd++; + txEnd %= MAX_TX_CNT; + + transactions.push_back(transaction); + + if (!commit(transaction.get())) { + std::remove_if(transactions.begin(), transactions.end(), + [&transaction] (const std::shared_ptr& el) { + return el == transaction; + } + ); + txEnd--; + txEnd += MAX_TX_CNT; + txEnd %= MAX_TX_CNT; + return nullptr; + } + + return transaction; +} + +std::shared_ptr ConnectorTransactionStore::getActiveTransaction() { + + if (!transactions.empty()) { + auto& tx = transactions.back(); + if (tx->isActive() || tx->isRunning()) { + return tx; + } + } + + AO_DBG_DEBUG("Make new Tx"); + + auto tx = makeTransaction(); + if (tx) { + tx->setConnectorId(connectorId); + } + + return tx; +} + +std::shared_ptr ConnectorTransactionStore::getTransactionSync() { + + if (!transactions.empty()) { + auto& tx = transactions.front(); + if (tx->isRunning()) { + return tx; + } + } + + return nullptr; +} + +bool ConnectorTransactionStore::commit(Transaction *transaction) { + if (transactions.empty()) { + AO_DBG_ERR("Dangling transaction"); + return false; + } + + auto found = transactions.end(); + if (transaction == (transactions.end() - 1)->get()) { //optimization: check if committing the last container element + found = transactions.end() - 1; + } else { + for (auto entry = transactions.begin(); entry != transactions.end(); entry++) { + if (transaction == entry->get()) { + found = entry; + break; + } + } + } + + if (found == transactions.end()) { + AO_DBG_ERR("Dangling transaction"); + return false; + } + + //confirmed that transaction points to an element in the transactions container + + char fn [MAX_PATH_SIZE] = {'\0'}; + auto ret = snprintf(fn, MAX_PATH_SIZE, AO_TXSTORE_DIR "tx" "-%u-%u.jsn", connectorId, transaction->getTxNr()); + if (ret < 0 || ret >= MAX_PATH_SIZE) { + AO_DBG_ERR("fn error: %i", ret); + return false; //all files have same length + } + + DynamicJsonDocument txDoc {0}; + if (!transaction->serializeSessionState(txDoc)) { + AO_DBG_ERR("Serialization error"); + return false; + } + + if (!FilesystemUtils::storeJson(filesystem, fn, txDoc)) { + AO_DBG_ERR("FS error"); + return false; + } + + //Data committed to memory; now update meta structures + + if (!transaction->isPending()) { + + AO_DBG_DEBUG("Drop completed transaction"); + transactions.erase(found); + transaction = nullptr; + auto beginNew = txEnd; + if (!transactions.empty()) { + beginNew = transactions.front()->getTxNr(); + } + if (beginNew != (uint) *txBegin) { + *txBegin = beginNew; + configuration_save(); + } + } + + //success + return true; +} + +TransactionStore::TransactionStore(TransactionService& context, uint nConnectors, std::shared_ptr filesystem) { + + for (uint i = 0; i < nConnectors; i++) { + connectors.push_back(std::unique_ptr( + new ConnectorTransactionStore(context, i, filesystem))); + } +} + +std::shared_ptr TransactionStore::getActiveTransaction(uint connectorId) { + if (connectorId >= connectors.size()) { + AO_DBG_ERR("Invalid connectorId"); + return nullptr; + } + return connectors[connectorId]->getActiveTransaction(); +} + +std::shared_ptr TransactionStore::getTransactionSync(uint connectorId) { + if (connectorId >= connectors.size()) { + AO_DBG_ERR("Invalid connectorId"); + return nullptr; + } + return connectors[connectorId]->getTransactionSync(); +} + +bool TransactionStore::commit(Transaction *transaction) { + if (!transaction) { + AO_DBG_ERR("Invalid arg"); + return false; + } + auto connectorId = transaction->getConnectorId(); + if (connectorId < 0 || connectorId >= connectors.size()) { + AO_DBG_ERR("Invalid tx"); + return false; + } + return connectors[connectorId]->commit(transaction); +} + +void TransactionStore::restorePendingTransactions() { + for (auto& connector : connectors) { + connector->restorePendingTransactions(); + } +} + +void TransactionStore::submitPendingOperations() { + for (auto& connector : connectors) { + connector->submitPendingOperations(); + } +} diff --git a/src/ArduinoOcpp/Tasks/Transactions/TransactionStore.h b/src/ArduinoOcpp/Tasks/Transactions/TransactionStore.h new file mode 100644 index 00000000..cfffdfa4 --- /dev/null +++ b/src/ArduinoOcpp/Tasks/Transactions/TransactionStore.h @@ -0,0 +1,61 @@ +// matth-x/ArduinoOcpp +// Copyright Matthias Akstaller 2019 - 2022 +// MIT License + +#ifndef TRANSACTIONSTORE_H +#define TRANSACTIONSTORE_H + +#include +#include +#include +#include +#include + +namespace ArduinoOcpp { + +class TransactionService; +class ConnectorTransactionSequence; + +class ConnectorTransactionStore { +private: + TransactionService& context; + const uint connectorId; + + std::shared_ptr filesystem; + std::shared_ptr> txBegin; //The Tx file names are consecutively numbered; first number + uint txEnd; //one place after last number + + std::deque> transactions; + + std::shared_ptr makeTransaction(); + +public: + ConnectorTransactionStore(TransactionService& context, uint nConnectors, std::shared_ptr filesystem); + + std::shared_ptr getActiveTransaction(); + std::shared_ptr getTransactionSync(); + bool commit(Transaction *transaction); + + void restorePendingTransactions(); + void submitPendingOperations(); +}; + +class TransactionStore { +private: + std::vector> connectors; + +public: + TransactionStore(TransactionService& context, uint nConnectors, std::shared_ptr filesystem); + + //std::shared_ptr makeTransaction(uint connectorId); + std::shared_ptr getActiveTransaction(uint connectorId); + std::shared_ptr getTransactionSync(uint connectorId); //fron element of the tx queue; tx which is being executed at the server now + bool commit(Transaction *transaction); + + void restorePendingTransactions(); + void submitPendingOperations(); +}; + +} + +#endif From f833b1fc2687d6b42bec2588ae34ab9a7a456779 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Thu, 25 Aug 2022 21:14:10 +0200 Subject: [PATCH 199/696] fix signature --- src/ArduinoOcpp/Tasks/Transactions/OrderedOperationsQueue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ArduinoOcpp/Tasks/Transactions/OrderedOperationsQueue.cpp b/src/ArduinoOcpp/Tasks/Transactions/OrderedOperationsQueue.cpp index e72d0c73..c622af73 100644 --- a/src/ArduinoOcpp/Tasks/Transactions/OrderedOperationsQueue.cpp +++ b/src/ArduinoOcpp/Tasks/Transactions/OrderedOperationsQueue.cpp @@ -13,7 +13,7 @@ void OrderedOperationsQueue::addOcppOperation(uint seqNr, std::unique_ptr>& a, std::pair>& b) { + std::sort(operations.begin(), operations.end(), [seqEnd] (const std::pair>& a, const std::pair>& b) { uint da = (seqEnd - a.first + MAX_TXEVENT_CNT) % MAX_TXEVENT_CNT; //distance between a to seqEnd uint db = (seqEnd - b.first + MAX_TXEVENT_CNT) % MAX_TXEVENT_CNT; return da > db; //sort descending by distance to seqEnd <=> sort ascending by seqNr From 7681ec7666dc80f076bf45d31ced1f4a4e4418cd Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 27 Aug 2022 23:51:52 +0200 Subject: [PATCH 200/696] allow access to internals via facade --- src/ArduinoOcpp.cpp | 9 +++++++++ src/ArduinoOcpp.h | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 6b29a231..1de0dc77 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -637,3 +637,12 @@ ArduinoOcpp::DiagnosticsService *getDiagnosticsService() { return model.getDiagnosticsService(); } #endif + +OcppEngine *getOcppEngine() { + if (!ocppEngine) { + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before + return nullptr; + } + + return ocppEngine; +} diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index d8ffae91..d72cdbdf 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -199,4 +199,12 @@ ArduinoOcpp::FirmwareService *getFirmwareService(); ArduinoOcpp::DiagnosticsService *getDiagnosticsService(); #endif +namespace ArduinoOcpp { +class OcppEngine; +} + +//Get access to internal functions and data structures. The returned OcppEngine object allows +//you to bypass the facace functions of this header and implement custom functionality. +ArduinoOcpp::OcppEngine *getOcppEngine(); + #endif From 5f1a8c5d2c84caf6dd2669aa09e0e89de1708df3 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sun, 28 Aug 2022 12:22:16 +0200 Subject: [PATCH 201/696] remove superfluous debug messages --- src/ArduinoOcpp/Core/FilesystemUtils.cpp | 3 +-- src/ArduinoOcpp/Tasks/Transactions/Transaction.cpp | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/ArduinoOcpp/Core/FilesystemUtils.cpp b/src/ArduinoOcpp/Core/FilesystemUtils.cpp index 390b6d6a..27056d73 100644 --- a/src/ArduinoOcpp/Core/FilesystemUtils.cpp +++ b/src/ArduinoOcpp/Core/FilesystemUtils.cpp @@ -70,8 +70,7 @@ std::unique_ptr FilesystemUtils::loadJson(std::shared_ptr Date: Sun, 28 Aug 2022 12:22:47 +0200 Subject: [PATCH 202/696] allow to initialize with multiple connectors --- src/ArduinoOcpp.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 1de0dc77..3b2902ea 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -37,7 +37,10 @@ std::shared_ptr filesystem; FilesystemOpt fileSystemOpt {}; float voltage_eff {230.f}; +#ifndef OCPP_NUMCONNECTORS #define OCPP_NUMCONNECTORS 2 +#endif + #define OCPP_ID_OF_CONNECTOR 1 #define OCPP_ID_OF_CP 0 bool OCPP_booted = false; //if BootNotification succeeded From a4fef8d7f4a51fe2206d7e43ff9375b5ad3a8aa2 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sun, 28 Aug 2022 15:01:52 +0200 Subject: [PATCH 203/696] remove superfluous console output --- src/ArduinoOcpp/Core/FilesystemUtils.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ArduinoOcpp/Core/FilesystemUtils.cpp b/src/ArduinoOcpp/Core/FilesystemUtils.cpp index 27056d73..8ed689f8 100644 --- a/src/ArduinoOcpp/Core/FilesystemUtils.cpp +++ b/src/ArduinoOcpp/Core/FilesystemUtils.cpp @@ -70,7 +70,7 @@ std::unique_ptr FilesystemUtils::loadJson(std::shared_ptr filesystem, c ArduinoJsonFileAdapter fileWriter {file.get()}; size_t written = serializeJson(doc, fileWriter); - written = serializeJson(doc, Serial); if (written < 2) { AO_DBG_ERR("Error writing file %s", fn); From 154828c2a3174e7cb6da7fed6243a436b10b02d8 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sat, 27 Aug 2022 23:51:52 +0200 Subject: [PATCH 204/696] allow access to internals via facade --- src/ArduinoOcpp.cpp | 9 +++++++++ src/ArduinoOcpp.h | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 781fc9c1..78a15f2c 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -564,3 +564,12 @@ ArduinoOcpp::DiagnosticsService *getDiagnosticsService() { return model.getDiagnosticsService(); } #endif + +OcppEngine *getOcppEngine() { + if (!ocppEngine) { + AO_DBG_ERR("OCPP uninitialized"); //please call OCPP_initialize before + return nullptr; + } + + return ocppEngine; +} diff --git a/src/ArduinoOcpp.h b/src/ArduinoOcpp.h index 40014f94..72775ed9 100644 --- a/src/ArduinoOcpp.h +++ b/src/ArduinoOcpp.h @@ -200,4 +200,12 @@ ArduinoOcpp::FirmwareService *getFirmwareService(); ArduinoOcpp::DiagnosticsService *getDiagnosticsService(); #endif +namespace ArduinoOcpp { +class OcppEngine; +} + +//Get access to internal functions and data structures. The returned OcppEngine object allows +//you to bypass the facace functions of this header and implement custom functionality. +ArduinoOcpp::OcppEngine *getOcppEngine(); + #endif From 18355d0ea32db7ca836167ca82bfb8b435ff83b6 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Sun, 28 Aug 2022 12:22:47 +0200 Subject: [PATCH 205/696] allow to initialize with multiple connectors --- src/ArduinoOcpp.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 78a15f2c..8e79fc7a 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -36,7 +36,10 @@ std::shared_ptr filesystem; FilesystemOpt fileSystemOpt {}; float voltage_eff {230.f}; +#ifndef OCPP_NUMCONNECTORS #define OCPP_NUMCONNECTORS 2 +#endif + #define OCPP_ID_OF_CONNECTOR 1 #define OCPP_ID_OF_CP 0 bool OCPP_booted = false; //if BootNotification succeeded From 766963219b0187658212f87627cd493c96d6b808 Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Wed, 7 Sep 2022 10:22:59 +0200 Subject: [PATCH 206/696] access to internals from C facade --- src/ArduinoOcpp.cpp | 6 +++++- src/ArduinoOcpp/Core/FilesystemAdapter.cpp | 3 +-- src/ArduinoOcpp/Core/FilesystemAdapter.h | 3 +-- src/ArduinoOcpp_c.cpp | 4 ++++ src/ArduinoOcpp_c.h | 9 +++++++++ 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/ArduinoOcpp.cpp b/src/ArduinoOcpp.cpp index 8e79fc7a..d65a754b 100644 --- a/src/ArduinoOcpp.cpp +++ b/src/ArduinoOcpp.cpp @@ -89,7 +89,11 @@ void OCPP_initialize(OcppSocket& ocppSocket, float V_eff, ArduinoOcpp::Filesyste voltage_eff = V_eff; fileSystemOpt = fsOpt; - filesystem = EspWiFi::makeDefaultFilesystemAdapter(fileSystemOpt); +#ifndef AO_DEACTIVATE_FLASH + std::shared_ptr filesystem = EspWiFi::makeDefaultFilesystemAdapter(fileSystemOpt); +#else + std::shared_ptr filesystem; +#endif AO_DBG_DEBUG("filesystem %s", filesystem ? "loaded" : "error"); configuration_init(filesystem); //call before each other library call diff --git a/src/ArduinoOcpp/Core/FilesystemAdapter.cpp b/src/ArduinoOcpp/Core/FilesystemAdapter.cpp index 1d3d720e..51e1e251 100644 --- a/src/ArduinoOcpp/Core/FilesystemAdapter.cpp +++ b/src/ArduinoOcpp/Core/FilesystemAdapter.cpp @@ -6,8 +6,7 @@ #include //FilesystemOpt #include -//#ifndef AO_DEACTIVATE_FLASH -#if 1 +#ifndef AO_DEACTIVATE_FLASH //Set default parameters; assume usage with Arduino if no build flags are present #ifndef AO_USE_FILEAPI diff --git a/src/ArduinoOcpp/Core/FilesystemAdapter.h b/src/ArduinoOcpp/Core/FilesystemAdapter.h index 93c6af27..8fd7c9c0 100644 --- a/src/ArduinoOcpp/Core/FilesystemAdapter.h +++ b/src/ArduinoOcpp/Core/FilesystemAdapter.h @@ -61,8 +61,7 @@ class FilesystemAdapter { } //end namespace ArduinoOcpp -//#ifndef AO_DEACTIVATE_FLASH -#if 1 +#ifndef AO_DEACTIVATE_FLASH //Set default parameters; assume usage with Arduino if no build flags are present #ifndef AO_USE_FILEAPI diff --git a/src/ArduinoOcpp_c.cpp b/src/ArduinoOcpp_c.cpp index 03fbc26a..69ae46e0 100644 --- a/src/ArduinoOcpp_c.cpp +++ b/src/ArduinoOcpp_c.cpp @@ -198,3 +198,7 @@ bool ao_isInSession() { const char *ao_getSessionIdTag() { return getSessionIdTag(); } + +OcppHandle *getOcppHandle() { + return reinterpret_cast(getOcppEngine()); +} diff --git a/src/ArduinoOcpp_c.h b/src/ArduinoOcpp_c.h index 9f4235e8..3fe0a0b0 100644 --- a/src/ArduinoOcpp_c.h +++ b/src/ArduinoOcpp_c.h @@ -6,6 +6,9 @@ struct AOcppSocket; typedef struct AOcppSocket AOcppSocket; +struct OcppHandle; +typedef struct OcppHandle OcppHandle; + typedef void (*OnOcppMessage) (const char *payload, size_t len); typedef void (*OnOcppAbort) (); typedef void (*OnOcppTimeout) (); @@ -102,6 +105,12 @@ bool ao_isInSession(); const char *ao_getSessionIdTag(); +/* + * Get access to the internals + */ + +OcppHandle *getOcppHandle(); + #ifdef __cplusplus } #endif From 10795aad7e124917a0336b4d98fd51b47c197453 Mon Sep 17 00:00:00 2001 From: Matthias Akstaller <63792403+matth-x@users.noreply.github.com> Date: Sat, 10 Sep 2022 09:43:29 +0200 Subject: [PATCH 207/696] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 793edd6a..4e3b4c82 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ void setup() { Serial.print(F("BootNotification was answered. Central System clock: ")); Serial.println(confMsg["currentTime"].as()); //"currentTime" is a field of the central system response - //evseIsBooted = true; <-- Example: Notify your hardare that the BootNotification.conf() has arrived + //evseIsBooted = true; <-- Example: Notify your hardware that the BootNotification.conf() has arrived }); ... //rest of setup() function; executed immediately as bootNotification() is non-blocking From 5643ea004a324fa7fbf7380e83475e4bd558486d Mon Sep 17 00:00:00 2001 From: matth-x <63792403+matth-x@users.noreply.github.com> Date: Mon, 19 Sep 2022 22:47:50 +0200 Subject: [PATCH 208/696] add missing include statements --- src/ArduinoOcpp/MessagesV16/StopTransaction.h | 1 + src/ArduinoOcpp/Tasks/Metering/MeterValue.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ArduinoOcpp/MessagesV16/StopTransaction.h b/src/ArduinoOcpp/MessagesV16/StopTransaction.h index eaaacde8..c74e4601 100644 --- a/src/ArduinoOcpp/MessagesV16/StopTransaction.h +++ b/src/ArduinoOcpp/MessagesV16/StopTransaction.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace ArduinoOcpp { diff --git a/src/ArduinoOcpp/Tasks/Metering/MeterValue.h b/src/ArduinoOcpp/Tasks/Metering/MeterValue.h index 450f5d5b..541360d5 100644 --- a/src/ArduinoOcpp/Tasks/Metering/MeterValue.h +++ b/src/ArduinoOcpp/Tasks/Metering/MeterValue.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace ArduinoOcpp { From c8bdc6a140736222aa7f1092427be72e6d4a8e1c Mon Sep 17 00:00:00 2001 From: Matthias Akstaller <63792403+matth-x@users.noreply.github.com> Date: Wed, 21 Sep 2022 11:13:13 +0200 Subject: [PATCH 209/696] roll back dependency --- examples/SECC/platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/SECC/platformio.ini b/examples/SECC/platformio.ini index 5453178e..72b36270 100644 --- a/examples/SECC/platformio.ini +++ b/examples/SECC/platformio.ini @@ -8,7 +8,7 @@ board = esp32dev framework = arduino lib_deps = https://github.com/matth-x/ArduinoOcpp.git - https://github.com/tzapu/WiFiManager.git + https://github.com/tzapu/WiFiManager.git#4dcf0cc78bb031351d65e0f9b9271569df8720ed monitor_speed = 115200 [env:nodemcuv2] @@ -17,5 +17,5 @@ board = nodemcuv2 framework = arduino lib_deps = https://github.com/matth-x/ArduinoOcpp.git - https://github.com/tzapu/WiFiManager.git + https://github.com/tzapu/WiFiManager.git#4dcf0cc78bb031351d65e0f9b9271569df8720ed monitor_speed = 115200 From 0a8fbfbb10e86223803a231f42f9d683d8d55fff Mon Sep 17 00:00:00 2001 From: Matthias Akstaller <63792403+matth-x@users.noreply.github.com> Date: Wed, 21 Sep 2022 11:19:15 +0200 Subject: [PATCH 210/696] Exclude SECC example --- .github/workflows/pio.yaml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/pio.yaml b/.github/workflows/pio.yaml index ab6248b7..819bc365 100644 --- a/.github/workflows/pio.yaml +++ b/.github/workflows/pio.yaml @@ -11,10 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - example: [examples/ESP/main.cpp, examples/ESP-TLS/main.cpp, examples/SECC/main.cpp] - include: - - example: examples/SECC/main.cpp - dashboard-extra: --lib="/tmp/tzapu/WiFiManager" + example: [examples/ESP/main.cpp, examples/ESP-TLS/main.cpp] steps: - uses: actions/checkout@v2 @@ -38,10 +35,7 @@ jobs: pip install --upgrade platformio - name: Install library dependencies run: pio pkg install - - name: Extra dependencies for SECC example - if: ${{ matrix.dashboard-extra }} - run: git clone https://github.com/tzapu/WiFiManager.git /tmp/tzapu/WiFiManager - name: Run PlatformIO run: pio ci --lib="." --project-conf=platformio.ini ${{ matrix.dashboard-extra }} env: - PLATFORMIO_CI_SRC: ${{ matrix.example }} \ No newline at end of file + PLATFORMIO_CI_SRC: ${{ matrix.example }} From 579c67adffd43477a302e94dcc8846303682e255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Gr=C3=BCnberg?= Date: Tue, 27 Sep 2022 09:47:25 +0200 Subject: [PATCH 211/696] sample workflow for platform independent compilation --- .github/workflows/platformless.yml | 23 +++++++++++++++++++++++ .github/workflows/tests.yml | 19 +++++++++++++++++++ src/ArduinoOcpp_c.cpp | 2 ++ src/patch.cpp | 9 +++++++++ 4 files changed, 53 insertions(+) create mode 100644 .github/workflows/platformless.yml create mode 100644 .github/workflows/tests.yml create mode 100644 src/patch.cpp diff --git a/.github/workflows/platformless.yml b/.github/workflows/platformless.yml new file mode 100644 index 00000000..60764468 --- /dev/null +++ b/.github/workflows/platformless.yml @@ -0,0 +1,23 @@ +name: Platform independent compilation + +on: + push: + branches: + - feature/freertos + +jobs: + + compile-tests: + name: Compile + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v3 + - name: get gcc compiler + run: | + sudo apt update + sudo apt install build-essential + - name: Get ArduinoJson + run: wget -Uri https://github.com/bblanchon/ArduinoJson/releases/download/v6.19.4/ArduinoJson-v6.19.4.h -O ./src/ArduinoJson.h + - name: Compile + run: g++ -I ./src -g $(find ./src -type f -iregex ".*\.cpp") -DAO_CUSTOM_WS -DAO_CUSTOM_CONSOLE -DAO_CUSTOM_UPDATER -DAO_CUSTOM_RESET -DAO_DEACTIVATE_FLASH -DAO_USE_FILEAPI=ESPIDF_SPIFFS -DAO_DBG_LEVEL=AO_DL_DEBUG -DAO_TRAFFIC_OUT -DAO_FILENAME_PREFIX='"/ao_store"' -o ./output -Wall diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..d14d3fe9 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,19 @@ +name: Unit tests + +on: + push: + branches: + - test-integration-disabled + +jobs: + + compile-tests: + name: Compile Tests + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v3 + - name: Compile tests + run: g++ -Wall test/unitTests/000-unitTestsMain.cpp -o test/compiledTests + - name: Run tests + run: ./test/compiledTests \ No newline at end of file diff --git a/src/ArduinoOcpp_c.cpp b/src/ArduinoOcpp_c.cpp index 69ae46e0..b4642ccf 100644 --- a/src/ArduinoOcpp_c.cpp +++ b/src/ArduinoOcpp_c.cpp @@ -1,3 +1,4 @@ +#if 0 #include "ArduinoOcpp_c.h" #include "ArduinoOcpp.h" @@ -202,3 +203,4 @@ const char *ao_getSessionIdTag() { OcppHandle *getOcppHandle() { return reinterpret_cast(getOcppEngine()); } +#endif diff --git a/src/patch.cpp b/src/patch.cpp new file mode 100644 index 00000000..2f87613c --- /dev/null +++ b/src/patch.cpp @@ -0,0 +1,9 @@ +#include + +unsigned long ao_tick_ms_impl() { + return 0; +} + +int main() { + return 0; +} From 62813473b49c38ddea0491f014381ffa918da7d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Gr=C3=BCnberg?= Date: Tue, 27 Sep 2022 08:48:59 +0000 Subject: [PATCH 212/696] update gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..015a4214 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.devcontainer +ArduinoJson.h \ No newline at end of file From 03e2b3e0323d4f98496b7bd23f4bdb8817442e79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Gr=C3=BCnberg?= Date: Tue, 27 Sep 2022 08:52:04 +0000 Subject: [PATCH 213/696] test workflow --- .github/workflows/platformless.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/platformless.yml b/.github/workflows/platformless.yml index 60764468..e07500c0 100644 --- a/.github/workflows/platformless.yml +++ b/.github/workflows/platformless.yml @@ -20,4 +20,4 @@ jobs: - name: Get ArduinoJson run: wget -Uri https://github.com/bblanchon/ArduinoJson/releases/download/v6.19.4/ArduinoJson-v6.19.4.h -O ./src/ArduinoJson.h - name: Compile - run: g++ -I ./src -g $(find ./src -type f -iregex ".*\.cpp") -DAO_CUSTOM_WS -DAO_CUSTOM_CONSOLE -DAO_CUSTOM_UPDATER -DAO_CUSTOM_RESET -DAO_DEACTIVATE_FLASH -DAO_USE_FILEAPI=ESPIDF_SPIFFS -DAO_DBG_LEVEL=AO_DL_DEBUG -DAO_TRAFFIC_OUT -DAO_FILENAME_PREFIX='"/ao_store"' -o ./output -Wall + run: g++ -std=c++14 -I ./src -g $(find ./src -type f -iregex ".*\.cpp") -DAO_CUSTOM_WS -DAO_CUSTOM_CONSOLE -DAO_CUSTOM_UPDATER -DAO_CUSTOM_RESET -DAO_DEACTIVATE_FLASH -DAO_USE_FILEAPI=ESPIDF_SPIFFS -DAO_DBG_LEVEL=AO_DL_DEBUG -DAO_TRAFFIC_OUT -DAO_FILENAME_PREFIX='"/ao_store"' -o ./output -Wall From 0fbbd8de8e035a99c7c26a1ca6f49a19c8d57823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Gr=C3=BCnberg?= Date: Tue, 27 Sep 2022 08:52:37 +0000 Subject: [PATCH 214/696] update gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 015a4214..8f15d4c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .devcontainer -ArduinoJson.h \ No newline at end of file +ArduinoJson.h +workflow.ps1 \ No newline at end of file From 17af159ef507cc573fa90d9f5b74ba9cffa5f795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Gr=C3=BCnberg?= Date: Tue, 27 Sep 2022 08:55:11 +0000 Subject: [PATCH 215/696] update gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8f15d4c9..0b81cddd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .devcontainer ArduinoJson.h -workflow.ps1 \ No newline at end of file +workflow.sh \ No newline at end of file From ee21d06da8c21f32ab66f3687c0b6cde3bbe9172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Gr=C3=BCnberg?= Date: Tue, 27 Sep 2022 09:00:26 +0000 Subject: [PATCH 216/696] test workflow --- .github/workflows/platformless.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/platformless.yml b/.github/workflows/platformless.yml index e07500c0..4b22b20a 100644 --- a/.github/workflows/platformless.yml +++ b/.github/workflows/platformless.yml @@ -17,6 +17,7 @@ jobs: run: | sudo apt update sudo apt install build-essential + g++ --version - name: Get ArduinoJson run: wget -Uri https://github.com/bblanchon/ArduinoJson/releases/download/v6.19.4/ArduinoJson-v6.19.4.h -O ./src/ArduinoJson.h - name: Compile From 47168202b7f5fbad83c72808070a54b8f3ae44fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Gr=C3=BCnberg?= Date: Tue, 27 Sep 2022 09:34:30 +0000 Subject: [PATCH 217/696] added info for g++ compiler version --- .github/workflows/platformless.yml | 4 +++- .gitignore | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/platformless.yml b/.github/workflows/platformless.yml index 4b22b20a..83f266c9 100644 --- a/.github/workflows/platformless.yml +++ b/.github/workflows/platformless.yml @@ -17,8 +17,10 @@ jobs: run: | sudo apt update sudo apt install build-essential + sudo apt -y install gcc-9 g++-9 g++ --version + echo "g++ version must be 9.4.0" - name: Get ArduinoJson run: wget -Uri https://github.com/bblanchon/ArduinoJson/releases/download/v6.19.4/ArduinoJson-v6.19.4.h -O ./src/ArduinoJson.h - name: Compile - run: g++ -std=c++14 -I ./src -g $(find ./src -type f -iregex ".*\.cpp") -DAO_CUSTOM_WS -DAO_CUSTOM_CONSOLE -DAO_CUSTOM_UPDATER -DAO_CUSTOM_RESET -DAO_DEACTIVATE_FLASH -DAO_USE_FILEAPI=ESPIDF_SPIFFS -DAO_DBG_LEVEL=AO_DL_DEBUG -DAO_TRAFFIC_OUT -DAO_FILENAME_PREFIX='"/ao_store"' -o ./output -Wall + run: g++ -std=c++14 -I ./src -g $(find ./src -type f -iregex ".*\.cpp") -DAO_CUSTOM_WS -DAO_CUSTOM_CONSOLE -DAO_CUSTOM_UPDATER -DAO_CUSTOM_RESET -DAO_DEACTIVATE_FLASH -DAO_USE_FILEAPI=ESPIDF_SPIFFS -DAO_DBG_LEVEL=AO_DL_DEBUG -DAO_TRAFFIC_OUT -DAO_FILENAME_PREFIX='"/ao_store"' -o ./output.exe -Wall diff --git a/.gitignore b/.gitignore index 0b81cddd..83d29ad3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .devcontainer ArduinoJson.h -workflow.sh \ No newline at end of file +workflow.sh +output.exe \ No newline at end of file From c672d19fce9fa753deb203cad6cda753a8dbffad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Gr=C3=BCnberg?= Date: Tue, 27 Sep 2022 09:44:13 +0000 Subject: [PATCH 218/696] added test files --- tests/000-unitTestsMain.cpp | 12 + tests/catch2/catch.hpp | 17969 ++++++++++++++++++++++++++++++++ tests/exampleCheckNumbers.cpp | 13 + 3 files changed, 17994 insertions(+) create mode 100644 tests/000-unitTestsMain.cpp create mode 100644 tests/catch2/catch.hpp create mode 100644 tests/exampleCheckNumbers.cpp diff --git a/tests/000-unitTestsMain.cpp b/tests/000-unitTestsMain.cpp new file mode 100644 index 00000000..71a6c970 --- /dev/null +++ b/tests/000-unitTestsMain.cpp @@ -0,0 +1,12 @@ +//000- prefix is for the main-file to always be on top in folder view + +// Powershell to compile tests and run them: +// g++ -Wall test/unitTests/000-unitTestsMain.cpp -o ./test/compiledTests ; ./test/compiledTests ; Remove-Item -Path ./test/compiledTests.exe + +//use the main function provided by catch2 +#define CATCH_CONFIG_MAIN +//include the catch2 library +#include "../catch2/catch.hpp" + +//include the unit tests +#include "./exampleCheckNumbers.cpp" \ No newline at end of file diff --git a/tests/catch2/catch.hpp b/tests/catch2/catch.hpp new file mode 100644 index 00000000..07efa655 --- /dev/null +++ b/tests/catch2/catch.hpp @@ -0,0 +1,17969 @@ +/* + * Catch v2.13.9 + * Generated: 2022-04-12 22:37:23.260201 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 9 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html +#ifdef __APPLE__ +# include +# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +# define CATCH_PLATFORM_MAC +# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) && !defined(__CUDACC__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +# endif + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#if defined(_MSC_VER) + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +# if !defined(__clang__) // Handle Clang masquerading for msvc + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL + +// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) +# endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # include + # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept { return file[0] == '\0'; } + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; + + private: + static constexpr char const* const s_empty = ""; + + char const* m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; + + StringRef( char const* rawChars ) noexcept; + + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } + + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const*; + + public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; + + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; + + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } + + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + }; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch + +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template