Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 194 additions & 2 deletions src/api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@
// GPL-3.0 License

#include "api.h"
#include "mongoose.h"

#include <MicroOcpp/Debug.h>
#include <MicroOcpp/Core/Memory.h>
#include <MicroOcpp/Model/ConnectorBase/EvseId.h>
#include <MicroOcpp/Model/Authorization/IdToken.h>

#include "evse.h"

//simple matching function; takes * as a wildcard
bool str_match(const char *query, const char *pattern) {
size_t qi = 0, pi = 0;

while (query[qi] && pattern[pi]) {
if (query[qi] == pattern[pi]) {
while (pattern[pi]) {
if (query[qi] && query[qi] == pattern[pi]) {
qi++;
pi++;
} else if (pattern[pi] == '*') {
Expand Down Expand Up @@ -155,3 +159,191 @@ int mocpp_api_call(const char *endpoint, MicroOcpp::Method method, const char *b

return status;
}

int mocpp_api2_call(const char *uri_raw, size_t uri_raw_len, MicroOcpp::Method method, const char *query_raw, size_t query_raw_len, char *resp_body, size_t resp_body_size) {

snprintf(resp_body, resp_body_size, "%s", "");

struct mg_str uri = mg_str_n(uri_raw, uri_raw_len);
struct mg_str query = mg_str_n(query_raw, query_raw_len);

int evse_id = -1;
int connector_id = -1;

unsigned int num;
struct mg_str evse_id_str = mg_http_var(query, mg_str("evse_id"));
if (evse_id_str.buf) {
if (!mg_str_to_num(evse_id_str, 10, &num, sizeof(num)) || num < 1 || num >= MO_NUM_EVSEID) {
snprintf(resp_body, resp_body_size, "invalid connector_id");
return 400;
}
evse_id = (int)num;
}

struct mg_str connector_id_str = mg_http_var(query, mg_str("connector_id"));
if (connector_id_str.buf) {
if (!mg_str_to_num(connector_id_str, 10, &num, sizeof(num)) || num != 1) {
snprintf(resp_body, resp_body_size, "invalid connector_id");
return 400;
}
connector_id = (int)num;
}

if (mg_match(uri, mg_str("/plugin"), NULL)) {
if (method != MicroOcpp::Method::POST) {
return 405;
}
if (evse_id < 0) {
snprintf(resp_body, resp_body_size, "no action taken");
return 200;
} else {
snprintf(resp_body, resp_body_size, "%s", connectors[evse_id-1].getEvPlugged() ? "EV already plugged" : "plugged in EV");
connectors[evse_id-1].setEvPlugged(true);
connectors[evse_id-1].setEvReady(true);
connectors[evse_id-1].setEvseReady(true);
return 200;
}
} else if (mg_match(uri, mg_str("/plugout"), NULL)) {
if (method != MicroOcpp::Method::POST) {
return 405;
}
if (evse_id < 0) {
snprintf(resp_body, resp_body_size, "no action taken");
return 200;
} else {
snprintf(resp_body, resp_body_size, "%s", connectors[evse_id-1].getEvPlugged() ? "EV already unplugged" : "unplug EV");
connectors[evse_id-1].setEvPlugged(false);
connectors[evse_id-1].setEvReady(false);
connectors[evse_id-1].setEvseReady(false);
return 200;
}
} else if (mg_match(uri, mg_str("/end"), NULL)) {
if (method != MicroOcpp::Method::POST) {
return 405;
}
bool trackEvReady = false;
for (size_t i = 0; i < connectors.size(); i++) {
trackEvReady |= connectors[i].getEvReady();
connectors[i].setEvReady(false);
}
snprintf(resp_body, resp_body_size, "%s", trackEvReady ? "suspended EV" : "EV already suspended");
return 200;
} else if (mg_match(uri, mg_str("/state"), NULL)) {
if (method != MicroOcpp::Method::POST) {
return 405;
}
struct mg_str ready_str = mg_http_var(query, mg_str("ready"));
bool ready = true;
if (ready_str.buf) {
if (mg_match(ready_str, mg_str("true"), NULL)) {
ready = true;
} else if (mg_match(ready_str, mg_str("false"), NULL)) {
ready = false;
} else {
snprintf(resp_body, resp_body_size, "invalid ready");
return 400;
}
}
bool trackEvReady = false;
for (size_t i = 0; i < connectors.size(); i++) {
if (connectors[i].getEvPlugged()) {
bool trackEvReady = connectors[i].getEvReady();
connectors[i].setEvReady(ready);
snprintf(resp_body, resp_body_size, "%s, %s", ready ? "EV suspended" : "EV not suspended", trackEvReady ? "suspended before" : "not suspended before");
return 200;
}
}
snprintf(resp_body, resp_body_size, "no action taken - EV not plugged");
return 200;
} else if (mg_match(uri, mg_str("/authorize"), NULL)) {
if (method != MicroOcpp::Method::POST) {
return 405;
}
struct mg_str id = mg_http_var(query, mg_str("id"));
if (!id.buf) {
snprintf(resp_body, resp_body_size, "missing id");
return 400;
}
struct mg_str type = mg_http_var(query, mg_str("type"));
if (!id.buf) {
snprintf(resp_body, resp_body_size, "missing type");
return 400;
}

int ret;
char id_buf [MO_IDTOKEN_LEN_MAX + 1];
ret = snprintf(id_buf, sizeof(id_buf), "%.*s", (int)id.len, id.buf);
if (ret < 0 || ret >= sizeof(id_buf)) {
snprintf(resp_body, resp_body_size, "invalid id");
return 400;
}
char type_buf [128];
ret = snprintf(type_buf, sizeof(type_buf), "%.*s", (int)type.len, type.buf);
if (ret < 0 || ret >= sizeof(type_buf)) {
snprintf(resp_body, resp_body_size, "invalid type");
return 400;
}

if (evse_id <= 0) {
snprintf(resp_body, resp_body_size, "invalid evse_id");
return 400;
}

bool trackAuthActive = connectors[evse_id-1].getSessionIdTag();

if (!connectors[evse_id-1].presentNfcTag(id_buf, type_buf)) {
snprintf(resp_body, resp_body_size, "invalid id and / or type");
return 400;
}

bool authActive = connectors[evse_id-1].getSessionIdTag();

snprintf(resp_body, resp_body_size, "%s",
!trackAuthActive && authActive ? "authorize in progress" :
trackAuthActive && !authActive ? "unauthorize in progress" :
trackAuthActive && authActive ? "no action taken (EVSE still authorized)" :
"no action taken (EVSE not authorized)");

return 200;
} else if (mg_match(uri, mg_str("/memory/info"), NULL)) {
#if MO_OVERRIDE_ALLOCATION && MO_ENABLE_HEAP_PROFILER
{
if (method != MicroOcpp::Method::GET) {
return 405;
}

int ret = mo_mem_write_stats_json(resp_body, resp_body_size);
if (ret < 0 || ret >= resp_body_size) {
snprintf(resp_body, resp_body_size, "internal error");
return 500;
}

return 200;
}
#else
{
snprintf(resp_body, resp_body_size, "memory profiler disabled");
return 404;
}
#endif
} else if (mg_match(uri, mg_str("/memory/reset"), NULL)) {
#if MO_OVERRIDE_ALLOCATION && MO_ENABLE_HEAP_PROFILER
{
if (method != MicroOcpp::Method::POST) {
return 405;
}

MO_MEM_RESET();
return 200;
}
#else
{
snprintf(resp_body, resp_body_size, "memory profiler disabled");
return 404;
}
#endif

}

return 404;
}
2 changes: 2 additions & 0 deletions src/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ enum class Method {

int mocpp_api_call(const char *endpoint, MicroOcpp::Method method, const char *body, char *resp_body, size_t resp_body_size);

int mocpp_api2_call(const char *endpoint, size_t endpoint_len, MicroOcpp::Method method, const char *query, size_t query_len, char *resp_body, size_t resp_body_size);

#endif
46 changes: 30 additions & 16 deletions src/evse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <MicroOcpp/Model/Transactions/Transaction.h>
#include <MicroOcpp/Model/Transactions/TransactionService.h>
#include <MicroOcpp/Model/Variables/VariableService.h>
#include <MicroOcpp/Model/Authorization/IdToken.h>
#include <MicroOcpp/Operations/StatusNotification.h>
#include <MicroOcpp/Version.h>
#include <MicroOcpp/Debug.h>
Expand Down Expand Up @@ -146,41 +147,54 @@ void Evse::loop() {

}

void Evse::presentNfcTag(const char *uid_cstr) {
if (!uid_cstr) {
void Evse::presentNfcTag(const char *uid) {
if (!uid) {
MO_DBG_ERR("invalid argument");
return;
}
std::string uid = uid_cstr;

#if MO_ENABLE_V201
if (auto context = getOcppContext()) {
if (context->getVersion().major == 2) {
if (auto txService = context->getModel().getTransactionService()) {
if (auto evse = txService->getEvse(connectorId)) {
if (evse->getTransaction() && evse->getTransaction()->isAuthorizationActive) {
evse->endAuthorization(MicroOcpp::IdToken(uid_cstr, MicroOcpp::IdToken::Type::KeyCode));
} else {
evse->beginAuthorization(MicroOcpp::IdToken(uid_cstr, MicroOcpp::IdToken::Type::KeyCode));
}
return;
}
}
presentNfcTag(uid, "ISO14443");
return;
}
}
#endif

if (isTransactionActive(connectorId)) {
if (!uid.compare(getTransactionIdTag(connectorId))) {
endTransaction(uid.c_str(), "Local", connectorId);
if (!strcmp(uid, getTransactionIdTag(connectorId))) {
endTransaction(uid, "Local", connectorId);
} else {
MO_DBG_INFO("RFID card denied");
}
} else {
beginTransaction(uid.c_str(), connectorId);
beginTransaction(uid, connectorId);
}
}

#if MO_ENABLE_V201
bool Evse::presentNfcTag(const char *uid, const char *type) {

MicroOcpp::IdToken idToken {nullptr, MicroOcpp::IdToken::Type::UNDEFINED, "Simulator"};
if (!idToken.parseCstr(uid, type)) {
return false;
}

if (auto txService = getOcppContext()->getModel().getTransactionService()) {
if (auto evse = txService->getEvse(connectorId)) {
if (evse->getTransaction() && evse->getTransaction()->isAuthorizationActive) {
evse->endAuthorization(idToken);
} else {
evse->beginAuthorization(idToken);
}
return true;
}
}
return false;
}
#endif

void Evse::setEvPlugged(bool plugged) {
if (!trackEvPluggedBool) return;
trackEvPluggedBool->setBool(plugged);
Expand Down
5 changes: 5 additions & 0 deletions src/evse.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <array>
#include <string>
#include <MicroOcpp/Core/Configuration.h>
#include <MicroOcpp/Version.h>

#define SIMULATOR_FN MO_FILENAME_PREFIX "simulator.jsn"

Expand Down Expand Up @@ -41,6 +42,10 @@ class Evse {

void presentNfcTag(const char *uid);

#if MO_ENABLE_V201
bool presentNfcTag(const char *uid, const char *type);
#endif //MO_ENABLE_V201

void setEvPlugged(bool plugged);

bool getEvPlugged();
Expand Down
24 changes: 21 additions & 3 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <MicroOcpp.h>
#include <MicroOcpp/Core/Context.h>
#include <MicroOcpp/Core/FilesystemUtils.h>
#include "evse.h"
#include "api.h"

Expand Down Expand Up @@ -128,6 +129,10 @@ void app_loop() {

#if MO_NETLIB == MO_NETLIB_MONGOOSE

#ifndef MO_SIM_ENDPOINT_URL
#define MO_SIM_ENDPOINT_URL "http://0.0.0.0:8000" //URL to forward to mg_http_listen(). Will be ignored if the URL field exists in api.jsn
#endif

int main() {

#if MBEDTLS_PLATFORM_MEMORY
Expand All @@ -143,12 +148,23 @@ int main() {
mg_log_set(MG_LL_INFO);
mg_mgr_init(&mgr);

mg_http_listen(&mgr, "0.0.0.0:8000", http_serve, NULL); // Create listening connection

auto filesystem = MicroOcpp::makeDefaultFilesystemAdapter(MicroOcpp::FilesystemOpt::Use_Mount_FormatOnFail);

load_ocpp_version(filesystem);

struct mg_str api_cert = mg_file_read(&mg_fs_posix, MO_FILENAME_PREFIX "api_cert.pem");
struct mg_str api_key = mg_file_read(&mg_fs_posix, MO_FILENAME_PREFIX "api_key.pem");

auto api_settings_doc = MicroOcpp::FilesystemUtils::loadJson(filesystem, MO_FILENAME_PREFIX "api.jsn", "Simulator");
if (!api_settings_doc) {
api_settings_doc = MicroOcpp::makeJsonDoc("Simulator", 0);
}
JsonObject api_settings = api_settings_doc->as<JsonObject>();

const char *api_url = api_settings["url"] | MO_SIM_ENDPOINT_URL;

mg_http_listen(&mgr, api_url, http_serve, (void*)api_url); // Create listening connection

osock = new MicroOcpp::MOcppMongooseClient(&mgr,
"ws://echo.websocket.events",
"charger-01",
Expand All @@ -160,7 +176,7 @@ int main() {
MicroOcpp::ProtocolVersion{1,6}
);

server_initialize(osock);
server_initialize(osock, api_cert.buf ? api_cert.buf : "", api_key.buf ? api_key.buf : "", api_settings["user"] | "", api_settings["pass"] | "");
app_setup(*osock, filesystem);

setOnResetExecute([] (bool isHard) {
Expand Down Expand Up @@ -191,6 +207,8 @@ int main() {

delete osock;
mg_mgr_free(&mgr);
free(api_cert.buf);
free(api_key.buf);
return 0;
}

Expand Down
Loading
Loading