Skip to content

Commit b36ff88

Browse files
committed
support StopTxOnEvUnplug, -InvalidId, field reason
1 parent b9648c4 commit b36ff88

9 files changed

Lines changed: 87 additions & 32 deletions

File tree

src/ArduinoOcpp/MessagesV16/CiStrings.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515
#define CiString255TypeLen 255
1616
#define CiString500TypeLen 500
1717

18+
//specified by OCPP
1819
#define IDTAG_LEN_MAX CiString20TypeLen
1920
#define CONF_KEYLEN_MAX CiString50TypeLen
2021

22+
//not specified by OCPP
23+
#define REASON_LEN_MAX CiString25TypeLen
24+
2125
#endif

src/ArduinoOcpp/MessagesV16/RemoteStopTransaction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ std::unique_ptr<DynamicJsonDocument> RemoteStopTransaction::createConf(){
3232
auto connIter = cpStatusService->getConnector(i);
3333
if (connIter->getTransactionId() == transactionId) {
3434
canStopTransaction = true;
35-
connIter->endSession();
35+
connIter->endSession("Remote");
3636
}
3737
}
3838
}

src/ArduinoOcpp/MessagesV16/Reset.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ void Reset::processReq(JsonObject payload) {
2121
* Process the application data here. Note: you have to implement the device reset procedure in your client code. You have to set
2222
* a onSendConfListener in which you initiate a reset (e.g. calling ESP.reset() )
2323
*/
24-
//const char *type = payload["type"] | "Invalid";
24+
bool isHard = !strcmp(payload["type"] | "undefined", "Hard");
2525

2626
if (ocppModel && ocppModel->getChargePointStatusService()) {
2727
auto cpsService = ocppModel->getChargePointStatusService();
2828
int connId = 0;
2929
for (int i = 0; i < cpsService->getNumConnectors(); i++) {
3030
auto connector = cpsService->getConnector(connId);
3131
if (connector) {
32-
connector->endSession();
32+
connector->endSession(isHard ? "HardReset" : "SoftReset");
3333
}
3434
}
3535
}

src/ArduinoOcpp/MessagesV16/StartTransaction.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ StartTransaction::StartTransaction(int connectorId, const char *idTag) : connect
2121
AO_DBG_ERR("Format violation");
2222
}
2323

24-
const char* StartTransaction::getOcppOperationType(){
24+
const char* StartTransaction::getOcppOperationType() {
2525
return "StartTransaction";
2626
}
2727

@@ -95,17 +95,18 @@ void StartTransaction::processConf(JsonObject payload) {
9595
if (ocppModel)
9696
connector = ocppModel->getConnectorStatus(connectorId);
9797

98-
if (connector){
98+
if (connector) {
9999
if (transactionRev == connector->getTransactionWriteCount()) {
100-
100+
101101
if (!strcmp(idTagInfoStatus, "Accepted")) {
102102
AO_DBG_INFO("Request has been accepted");
103-
connector->setTransactionId(transactionId);
104103
} else {
105104
AO_DBG_INFO("Request has been denied. Reason: %s", idTagInfoStatus);
106-
//connector->setTransactionId(-1);
107-
connector->endSession(); //something is wrong with the idTag. Abort session
105+
AO_DBG_DEBUG("Set txId despite rejection");
106+
connector->setIdTagInvalidated();
108107
}
108+
109+
connector->setTransactionId(transactionId);
109110
}
110111
connector->setTransactionIdSync(transactionId);
111112

@@ -123,7 +124,7 @@ void StartTransaction::processReq(JsonObject payload) {
123124

124125
}
125126

126-
std::unique_ptr<DynamicJsonDocument> StartTransaction::createConf(){
127+
std::unique_ptr<DynamicJsonDocument> StartTransaction::createConf() {
127128
auto doc = std::unique_ptr<DynamicJsonDocument>(new DynamicJsonDocument(JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(2)));
128129
JsonObject payload = doc->to<JsonObject>();
129130

src/ArduinoOcpp/MessagesV16/StopTransaction.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010

1111
using ArduinoOcpp::Ocpp16::StopTransaction;
1212

13-
StopTransaction::StopTransaction(int connectorId) : connectorId(connectorId) {
14-
13+
StopTransaction::StopTransaction(int connectorId, const char *reason) : connectorId(connectorId) {
14+
if (reason) {
15+
snprintf(this->reason, REASON_LEN_MAX, "%s", reason);
16+
}
1517
}
1618

1719
const char* StopTransaction::getOcppOperationType(){
@@ -44,7 +46,7 @@ void StopTransaction::initiate() {
4446
}
4547

4648
std::unique_ptr<DynamicJsonDocument> StopTransaction::createReq() {
47-
auto doc = std::unique_ptr<DynamicJsonDocument>(new DynamicJsonDocument(JSON_OBJECT_SIZE(4) + (JSONDATE_LENGTH + 1)));
49+
auto doc = std::unique_ptr<DynamicJsonDocument>(new DynamicJsonDocument(JSON_OBJECT_SIZE(5) + (JSONDATE_LENGTH + 1) + (REASON_LEN_MAX + 1)));
4850
JsonObject payload = doc->to<JsonObject>();
4951

5052
if (meterStop >= 0)
@@ -61,6 +63,10 @@ std::unique_ptr<DynamicJsonDocument> StopTransaction::createReq() {
6163
payload["transactionId"] = connector->getTransactionIdSync();
6264
}
6365

66+
if (reason[0] != '\0') {
67+
payload["reason"] = reason;
68+
}
69+
6470
return doc;
6571
}
6672

src/ArduinoOcpp/MessagesV16/StopTransaction.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <ArduinoOcpp/Core/OcppMessage.h>
99
#include <ArduinoOcpp/Core/OcppTime.h>
10+
#include <ArduinoOcpp/MessagesV16/CiStrings.h>
1011

1112
namespace ArduinoOcpp {
1213
namespace Ocpp16 {
@@ -16,9 +17,10 @@ class StopTransaction : public OcppMessage {
1617
int connectorId = 1;
1718
int meterStop = -1;
1819
OcppTimestamp otimestamp;
20+
char reason [REASON_LEN_MAX] {'\0'};
1921
public:
2022

21-
StopTransaction(int connectorId);
23+
StopTransaction(int connectorId, const char *reason = nullptr);
2224

2325
const char* getOcppOperationType();
2426

src/ArduinoOcpp/MessagesV16/UnlockConnector.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ void UnlockConnector::processReq(JsonObject payload) {
2828

2929
auto connector = ocppModel->getConnectorStatus(connectorId);
3030

31+
connector->endSession("UnlockCommand");
32+
3133
std::function<bool()> unlockConnector = connector->getOnUnlockConnector();
3234
if (unlockConnector != nullptr) {
3335
cbDefined = true;
@@ -38,8 +40,6 @@ void UnlockConnector::processReq(JsonObject payload) {
3840
}
3941

4042
cbUnlockSuccessful = unlockConnector();
41-
42-
//success
4343
}
4444

4545
std::unique_ptr<DynamicJsonDocument> UnlockConnector::createConf(){

src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.cpp

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ ConnectorStatus::ConnectorStatus(OcppModel& context, int connectorId)
3535

3636
connectionTimeOut = declareConfiguration<int>("ConnectionTimeOut", 30, CONFIGURATION_FN, true, true, true, false);
3737
minimumStatusDuration = declareConfiguration<int>("MinimumStatusDuration", 0, CONFIGURATION_FN, true, true, true, false);
38+
stopTransactionOnInvalidId = declareConfiguration<const char*>("StopTransactionOnInvalidId", "true", CONFIGURATION_FN, true, true, false, false);
39+
stopTransactionOnEVSideDisconnect = declareConfiguration<const char*>("StopTransactionOnEVSideDisconnect", "true", CONFIGURATION_FN, true, true, false, false);
3840

3941
if (!sIdTag || !transactionId || !availability) {
4042
AO_DBG_ERR("Cannot declare sessionIdTag, transactionId or availability");
@@ -89,12 +91,13 @@ OcppEvseState ConnectorStatus::inferenceStatus() {
8991
return OcppEvseState::Preparing;
9092
} else {
9193
//Transaction is currently running
94+
if ((connectorEnergizedSampler && !connectorEnergizedSampler()) ||
95+
idTagInvalidated) {
96+
return OcppEvseState::SuspendedEVSE;
97+
}
9298
if (evRequestsEnergySampler && !evRequestsEnergySampler()) {
9399
return OcppEvseState::SuspendedEV;
94100
}
95-
if (connectorEnergizedSampler && !connectorEnergizedSampler()) {
96-
return OcppEvseState::SuspendedEVSE;
97-
}
98101
return OcppEvseState::Charging;
99102
}
100103
}
@@ -105,6 +108,10 @@ bool ConnectorStatus::ocppPermitsCharge() {
105108
return false;
106109
}
107110

111+
if (idTagInvalidated) {
112+
return false;
113+
}
114+
108115
OcppEvseState state = inferenceStatus();
109116

110117
return state == OcppEvseState::Charging ||
@@ -117,19 +124,26 @@ OcppMessage *ConnectorStatus::loop() {
117124
*availability = AVAILABILITY_INOPERATIVE;
118125
saveState();
119126
}
127+
128+
if (connectorPluggedSampler) {
129+
if (getTransactionId() >= 0 && !connectorPluggedSampler()) {
130+
if (!*stopTransactionOnEVSideDisconnect || strcmp(*stopTransactionOnEVSideDisconnect, "false")) {
131+
endSession("EVDisconnected");
132+
}
133+
}
134+
}
120135

121136
/*
122137
* Check conditions for start or stop transaction
123138
*/
124139
if (connectorPluggedSampler) { //only supported with connectorPluggedSampler
125140
if (getTransactionId() >= 0) {
126141
//check condition for StopTransaction
127-
if (!connectorPluggedSampler() ||
128-
!session) {
142+
if (!session) {
129143
AO_DBG_DEBUG("Session mngt: txId=%i, connectorPlugged=%d, session=%d",
130144
getTransactionId(), connectorPluggedSampler(), session);
131145
AO_DBG_INFO("Session mngt: trigger StopTransaction");
132-
return new StopTransaction(connectorId);
146+
return new StopTransaction(connectorId, endReason[0] != '\0' ? endReason : nullptr);
133147
}
134148
} else {
135149
//check condition for StartTransaction
@@ -163,7 +177,7 @@ OcppMessage *ConnectorStatus::loop() {
163177
if (inferencedStatus != currentStatus) {
164178
currentStatus = inferencedStatus;
165179
t_statusTransition = ao_tick_ms();
166-
AO_DBG_DEBUG("Status changed%s", *minimumStatusDuration > 0 ? ", will report delayed", "");
180+
AO_DBG_DEBUG("Status changed%s", *minimumStatusDuration ? ", will report delayed" : "");
167181
}
168182

169183
if (reportedStatus != currentStatus &&
@@ -200,23 +214,43 @@ void ConnectorStatus::beginSession(const char *sessionIdTag) {
200214
sIdTag->setValue(idTag, IDTAG_LEN_MAX + 1);
201215
saveState();
202216
session = true;
217+
idTagInvalidated = false;
218+
219+
memset(endReason, '\0', REASON_LEN_MAX + 1);
203220

204221
connectionTimeOutListen = true;
205222
connectionTimeOutTimestamp = ao_tick_ms();
206223
}
207224

208-
void ConnectorStatus::endSession() {
209-
AO_DBG_DEBUG("End session with idTag %s", idTag);
225+
void ConnectorStatus::endSession(const char *reason) {
226+
AO_DBG_DEBUG("End session with idTag %s for reason %s, %s previous reason",
227+
idTag, reason ? reason : "undefined",
228+
endReason[0] == '\0' ? "no" : "overruled by");
210229
if (session) {
211230
memset(idTag, '\0', IDTAG_LEN_MAX + 1);
212231
*sIdTag = "";
213232
saveState();
214233
}
215234
session = false;
216235

236+
if (reason && endReason[0] == '\0') {
237+
snprintf(endReason, REASON_LEN_MAX + 1, "%s", reason);
238+
}
239+
217240
connectionTimeOutListen = false;
218241
}
219242

243+
void ConnectorStatus::setIdTagInvalidated() {
244+
if (session) {
245+
idTagInvalidated = true;
246+
if (!*stopTransactionOnInvalidId || strcmp(*stopTransactionOnInvalidId, "false")) {
247+
endSession("DeAuthorized");
248+
}
249+
} else {
250+
AO_DBG_WARN("Cannot invalidate IdTag outside of session");
251+
}
252+
}
253+
220254
const char *ConnectorStatus::getSessionIdTag() {
221255
return session ? idTag : nullptr;
222256
}

src/ArduinoOcpp/Tasks/ChargePointStatus/ConnectorStatus.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,17 @@ class ConnectorStatus {
2828

2929
const int connectorId;
3030

31-
std::shared_ptr<Configuration<int>> availability {nullptr};
31+
std::shared_ptr<Configuration<int>> availability;
3232

3333
bool session = false;
3434
char idTag [IDTAG_LEN_MAX + 1] = {'\0'};
35-
std::shared_ptr<Configuration<const char *>> sIdTag {nullptr};
36-
std::shared_ptr<Configuration<int>> transactionId {nullptr};
35+
bool idTagInvalidated {false}; //if StartTransaction.conf() has status != "Accepted"
36+
std::shared_ptr<Configuration<const char *>> sIdTag;
37+
std::shared_ptr<Configuration<int>> transactionId;
3738
int transactionIdSync = -1;
39+
char endReason [REASON_LEN_MAX + 1] = {'\0'};
3840

39-
std::shared_ptr<Configuration<int>> connectionTimeOut {nullptr}; //in seconds
41+
std::shared_ptr<Configuration<int>> connectionTimeOut; //in seconds
4042
bool connectionTimeOutListen {false};
4143
ulong connectionTimeOutTimestamp {0}; //in milliseconds
4244

@@ -47,11 +49,17 @@ class ConnectorStatus {
4749
const char *getErrorCode();
4850

4951
OcppEvseState currentStatus = OcppEvseState::NOT_SET;
50-
std::shared_ptr<Configuration<int>> minimumStatusDuration {nullptr}; //in seconds
52+
std::shared_ptr<Configuration<int>> minimumStatusDuration; //in seconds
5153
OcppEvseState reportedStatus = OcppEvseState::NOT_SET;
5254
ulong t_statusTransition = 0;
5355

56+
//std::function<std::unique_ptr<OcppMessage>()> startTransactionBehavior;
57+
//std::function<std::unique_ptr<OcppMessage>(const char* stopReason)> stopTransactionBehavior;
58+
5459
std::function<bool()> onUnlockConnector {nullptr};
60+
61+
std::shared_ptr<Configuration<const char*>> stopTransactionOnInvalidId;
62+
std::shared_ptr<Configuration<const char*>> stopTransactionOnEVSideDisconnect;
5563
public:
5664
ConnectorStatus(OcppModel& context, int connectorId);
5765

@@ -66,15 +74,15 @@ class ConnectorStatus {
6674
* (given by ConnectorPluggedSampler and no error code)
6775
*/
6876
void beginSession(const char *idTag);
69-
void endSession();
77+
void endSession(const char *reason = nullptr);
78+
void setIdTagInvalidated(); //if StartTransaction.conf() has status != "Accepted"
7079
const char *getSessionIdTag();
7180
int getTransactionId();
7281
int getTransactionIdSync();
7382
uint16_t getTransactionWriteCount();
7483
void setTransactionId(int id);
7584
void setTransactionIdSync(int id);
7685

77-
7886
int getAvailability();
7987
void setAvailability(bool available);
8088
void setAuthorizationProvider(std::function<const char *()> authorization);

0 commit comments

Comments
 (0)