Skip to content

Commit

Permalink
Persistent v20.1 Variable and Transactions store (#379)
Browse files Browse the repository at this point in the history
* add persistent Variable store

* add persistent transactions queue

* add further tests

* update changelog

* fix unit tests

* fix unit tests

* fix compiler warning

* rename built-in VariableContainer implementations

* remove leftover debug message

* update WS creds storage location

* pin MOSim version

* update changelog
  • Loading branch information
matth-x authored Nov 3, 2024
1 parent fecac09 commit 4b89f46
Show file tree
Hide file tree
Showing 30 changed files with 2,804 additions and 620 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
with:
repository: matth-x/MicroOcppSimulator
path: MicroOcppSimulator
ref: feature/remote-api
ref: 20e8da7b76a3f5f3e50facab018a457ecdbcca59
submodules: 'recursive'
- name: Clean MicroOcpp submodule
run: |
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
- UnlockConnector port for OCPP 2.0.1 ([#371](https://github.com/matth-x/MicroOcpp/pull/371))
- More APIs ported to OCPP 2.0.1 ([#371](https://github.com/matth-x/MicroOcpp/pull/371))
- Support for AuthorizeRemoteTxRequests ([#373](https://github.com/matth-x/MicroOcpp/pull/373))
- Persistent Variable and Tx store for OCPP 2.0.1 ([#379](https://github.com/matth-x/MicroOcpp/pull/379))

### Removed

Expand Down
3 changes: 2 additions & 1 deletion src/MicroOcpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ void mocpp_initialize(Connection& connection, const char *bootNotificationCreden
model.setVariableService(std::unique_ptr<VariableService>(
new VariableService(*context, filesystem)));
model.setTransactionService(std::unique_ptr<TransactionService>(
new TransactionService(*context)));
new TransactionService(*context, filesystem, MO_NUM_EVSEID)));
model.setRemoteControlService(std::unique_ptr<RemoteControlService>(
new RemoteControlService(*context, MO_NUM_EVSEID)));
} else
Expand Down Expand Up @@ -852,6 +852,7 @@ void setEvReadyInput(std::function<bool()> evReadyInput, unsigned int connectorI
evse->setEvReadyInput(evReadyInput);
}
}
return;
}
#endif
auto connector = context->getModel().getConnector(connectorId);
Expand Down
8 changes: 5 additions & 3 deletions src/MicroOcpp/Model/Authorization/IdToken.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ IdToken::IdToken(const char *token, Type type, const char *memoryTag) : MemoryMa
MO_DBG_ERR("invalid token");
*idToken = '\0';
}
} else {
*idToken = '\0';
}
}

Expand Down Expand Up @@ -63,11 +65,11 @@ bool IdToken::parseCstr(const char *token, const char *typeCstr) {
}

const char *IdToken::get() const {
return *idToken ? idToken : nullptr;;
return idToken;
}

const char *IdToken::getTypeCstr() const {
const char *res = nullptr;
const char *res = "";
switch (type) {
case Type::UNDEFINED:
MO_DBG_ERR("internal error");
Expand Down Expand Up @@ -98,7 +100,7 @@ const char *IdToken::getTypeCstr() const {
break;
}

return res ? res : "";
return res;
}

bool IdToken::equals(const IdToken& other) {
Expand Down
2 changes: 0 additions & 2 deletions src/MicroOcpp/Model/ConnectorBase/Connector.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
#define MO_TXRECORD_SIZE 4 //no. of tx to hold on flash storage
#endif

#define MAX_TX_CNT 100000U //upper limit of txNr (internal usage). Must be at least 2*MO_TXRECORD_SIZE+1

#ifndef MO_REPORT_NOERROR
#define MO_REPORT_NOERROR 0
#endif
Expand Down
78 changes: 44 additions & 34 deletions src/MicroOcpp/Model/Metering/MeterValuesV201.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,46 @@ bool MeteringServiceEvse::existsMeasurand(const char *measurand, size_t len) {
return false;
}

namespace MicroOcpp {
namespace Ocpp201 {

bool validateSelectString(const char *csl, void *userPtr) {
auto mService = static_cast<MeteringService*>(userPtr);

bool isValid = true;
const char *l = csl; //the beginning of an entry of the comma-separated list
const char *r = l; //one place after the last character of the entry beginning with l
while (*l) {
if (*l == ',') {
l++;
continue;
}
r = l + 1;
while (*r != '\0' && *r != ',') {
r++;
}
bool found = false;
for (size_t evseId = 0; evseId < MO_NUM_EVSEID && mService->getEvse(evseId); evseId++) {
if (mService->getEvse(evseId)->existsMeasurand(l, (size_t) (r - l))) {
found = true;
break;
}
}
if (!found) {
isValid = false;
MO_DBG_WARN("could not find metering device for %.*s", (int) (r - l), l);
break;
}
l = r;
}
return isValid;
}

} //namespace Ocpp201
} //namespace MicroOcpp

using namespace MicroOcpp::Ocpp201;

MeteringService::MeteringService(Model& model, size_t numEvses) {

auto varService = model.getVariableService();
Expand All @@ -298,40 +338,10 @@ MeteringService::MeteringService(Model& model, size_t numEvses) {
varService->declareVariable<const char*>("SampledDataCtrlr", "TxEndedMeasurands", "");
varService->declareVariable<const char*>("AlignedDataCtrlr", "AlignedDataMeasurands", "");

std::function<bool(const char*)> validateSelectString = [this] (const char *csl) {
bool isValid = true;
const char *l = csl; //the beginning of an entry of the comma-separated list
const char *r = l; //one place after the last character of the entry beginning with l
while (*l) {
if (*l == ',') {
l++;
continue;
}
r = l + 1;
while (*r != '\0' && *r != ',') {
r++;
}
bool found = false;
for (size_t evseId = 0; evseId < MO_NUM_EVSEID && evses[evseId]; evseId++) {
if (evses[evseId]->existsMeasurand(l, (size_t) (r - l))) {
found = true;
break;
}
}
if (!found) {
isValid = false;
MO_DBG_WARN("could not find metering device for %.*s", (int) (r - l), l);
break;
}
l = r;
}
return isValid;
};

varService->registerValidator<const char*>("SampledDataCtrlr", "TxStartedMeasurands", validateSelectString);
varService->registerValidator<const char*>("SampledDataCtrlr", "TxUpdatedMeasurands", validateSelectString);
varService->registerValidator<const char*>("SampledDataCtrlr", "TxEndedMeasurands", validateSelectString);
varService->registerValidator<const char*>("AlignedDataCtrlr", "AlignedDataMeasurands", validateSelectString);
varService->registerValidator<const char*>("SampledDataCtrlr", "TxStartedMeasurands", validateSelectString, this);
varService->registerValidator<const char*>("SampledDataCtrlr", "TxUpdatedMeasurands", validateSelectString, this);
varService->registerValidator<const char*>("SampledDataCtrlr", "TxEndedMeasurands", validateSelectString, this);
varService->registerValidator<const char*>("AlignedDataCtrlr", "AlignedDataMeasurands", validateSelectString, this);

for (size_t evseId = 0; evseId < std::min(numEvses, (size_t)MO_NUM_EVSEID); evseId++) {
evses[evseId] = new MeteringServiceEvse(model, evseId);
Expand Down
29 changes: 20 additions & 9 deletions src/MicroOcpp/Model/RemoteControl/RemoteControlService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ UnlockStatus RemoteControlServiceEvse::unlockConnector() {
if (tx->started && !tx->stopped && tx->isAuthorized) {
return UnlockStatus_OngoingAuthorizedTransaction;
} else {
evse->abortTransaction(Ocpp201::Transaction::StopReason::Other,Ocpp201::TransactionEventTriggerReason::UnlockCommand);
evse->abortTransaction(Ocpp201::Transaction::StoppedReason::Other,Ocpp201::TransactionEventTriggerReason::UnlockCommand);
}
}
}
Expand Down Expand Up @@ -102,7 +102,7 @@ RemoteControlServiceEvse *RemoteControlService::getEvse(unsigned int evseId) {
return evses[evseId];
}

RequestStartStopStatus RemoteControlService::requestStartTransaction(unsigned int evseId, unsigned int remoteStartId, IdToken idToken, std::shared_ptr<Ocpp201::Transaction>& transactionOut) {
RequestStartStopStatus RemoteControlService::requestStartTransaction(unsigned int evseId, unsigned int remoteStartId, IdToken idToken, char *transactionIdOut, size_t transactionIdBufSize) {

TransactionService *txService = context.getModel().getTransactionService();
if (!txService) {
Expand All @@ -116,21 +116,32 @@ RequestStartStopStatus RemoteControlService::requestStartTransaction(unsigned in
return RequestStartStopStatus_Rejected;
}

transactionOut = evse->getTransaction();

if (!evse->beginAuthorization(idToken, authorizeRemoteStart->getBool())) {
MO_DBG_INFO("EVSE still occupied with pending tx");
if (auto tx = evse->getTransaction()) {
auto ret = snprintf(transactionIdOut, transactionIdBufSize, "%s", tx->transactionId);
if (ret < 0 || (size_t)ret >= transactionIdBufSize) {
MO_DBG_ERR("internal error");
return RequestStartStopStatus_Rejected;
}
}
return RequestStartStopStatus_Rejected;
}

auto tx = evse->getTransaction();
if (!tx) {
MO_DBG_ERR("internal error");
return RequestStartStopStatus_Rejected;
}

transactionOut = evse->getTransaction();
if (!transactionOut) {
auto ret = snprintf(transactionIdOut, transactionIdBufSize, "%s", tx->transactionId);
if (ret < 0 || (size_t)ret >= transactionIdBufSize) {
MO_DBG_ERR("internal error");
return RequestStartStopStatus_Rejected;
}

transactionOut->remoteStartId = remoteStartId;
transactionOut->notifyRemoteStartId = true;
tx->remoteStartId = remoteStartId;
tx->notifyRemoteStartId = true;

return RequestStartStopStatus_Accepted;
}
Expand All @@ -148,7 +159,7 @@ RequestStartStopStatus RemoteControlService::requestStopTransaction(const char *
for (unsigned int evseId = 0; evseId < MO_NUM_EVSEID; evseId++) {
if (auto evse = txService->getEvse(evseId)) {
if (evse->getTransaction() && !strcmp(evse->getTransaction()->transactionId, transactionId)) {
success = evse->abortTransaction(Ocpp201::Transaction::StopReason::Remote, Ocpp201::TransactionEventTriggerReason::RemoteStop);
success = evse->abortTransaction(Ocpp201::Transaction::StoppedReason::Remote, Ocpp201::TransactionEventTriggerReason::RemoteStop);
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/MicroOcpp/Model/RemoteControl/RemoteControlService.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class RemoteControlService : public MemoryManaged {

RemoteControlServiceEvse *getEvse(unsigned int evseId);

RequestStartStopStatus requestStartTransaction(unsigned int evseId, unsigned int remoteStartId, IdToken idToken, std::shared_ptr<Ocpp201::Transaction>& transactionOut); //ChargingProfile, GroupIdToken not supported yet
RequestStartStopStatus requestStartTransaction(unsigned int evseId, unsigned int remoteStartId, IdToken idToken, char *transactionIdOut, size_t transactionIdBufSize); //ChargingProfile, GroupIdToken not supported yet

RequestStartStopStatus requestStopTransaction(const char *transactionId);
};
Expand Down
4 changes: 2 additions & 2 deletions src/MicroOcpp/Model/Reset/ResetService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ ResetService::~ResetService() {

ResetService::Evse::Evse(Context& context, ResetService& resetService, unsigned int evseId) : context(context), resetService(resetService), evseId(evseId) {
auto varService = context.getModel().getVariableService();
varService->declareVariable<bool>(ComponentId("EVSE", evseId >= 1 ? evseId : -1), "AllowReset", true, MO_VARIABLE_FN, Variable::Mutability::ReadOnly);
varService->declareVariable<bool>(ComponentId("EVSE", evseId >= 1 ? evseId : -1), "AllowReset", true, Variable::Mutability::ReadOnly, false);
}

void ResetService::Evse::loop() {
Expand Down Expand Up @@ -303,7 +303,7 @@ ResetStatus ResetService::initiateReset(ResetType type, unsigned int evseId) {
if (tx->active) {
//Tx in progress. Check behavior
if (type == ResetType_Immediate) {
txService->getEvse(eId)->abortTransaction(Transaction::StopReason::ImmediateReset, TransactionEventTriggerReason::ResetCommand);
txService->getEvse(eId)->abortTransaction(Transaction::StoppedReason::ImmediateReset, TransactionEventTriggerReason::ResetCommand);
} else {
scheduled = true;
break;
Expand Down
Loading

0 comments on commit 4b89f46

Please sign in to comment.