diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 27a29794..65c42f41 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(iso15118_chargepoint) add_subdirectory(iso15118_centralsystem) add_subdirectory(quick_start_centralsystem) add_subdirectory(quick_start_chargepoint) +add_subdirectory(quick_start_cs_lc_hybrid) add_subdirectory(quick_start_localcontroller) add_subdirectory(remote_chargepoint) add_subdirectory(security_centralsystem) diff --git a/examples/README.md b/examples/README.md index e828f844..2138e6e3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -7,6 +7,7 @@ The following examples are available for OCPP 1.6 standard : * [Quick start Central System example](./quick_start_centralsystem/README.md) * [Quick start Charge Point example](./quick_start_chargepoint/README.md) * [Quick start Local Controller example](./quick_start_localcontroller/README.md) +* [Quick start Hybrid Central System / Local Controller example](./quick_start_cs_lc_hybrid/README.md) * [Remote Charge Point example](./remote_chargepoint/README.md) The following examples are available for OCPP 1.6 security extensions : diff --git a/examples/common/DefaultCentralSystemEventsHandler.h b/examples/common/DefaultCentralSystemEventsHandler.h index 369a3d71..8be0c741 100644 --- a/examples/common/DefaultCentralSystemEventsHandler.h +++ b/examples/common/DefaultCentralSystemEventsHandler.h @@ -260,7 +260,7 @@ class DefaultCentralSystemEventsHandler : public ocpp::centralsystem::ICentralSy /** @brief Remove a charge point from the connected charge points */ void removeChargePoint(const std::string& identifier); - private: + protected: /** @brief Path to the V2G root CA */ std::filesystem::path m_iso_v2g_root_ca; /** @brief Path to the MO root CA */ diff --git a/examples/common/DefaultChargePointEventsHandler.cpp b/examples/common/DefaultChargePointEventsHandler.cpp index 8d1cdd6b..e871ced0 100644 --- a/examples/common/DefaultChargePointEventsHandler.cpp +++ b/examples/common/DefaultChargePointEventsHandler.cpp @@ -166,10 +166,28 @@ bool DefaultChargePointEventsHandler::getMeterValue( /** @copydoc bool IChargePointEventsHandler::remoteStartTransactionRequested(unsigned int, const std::string&) */ bool DefaultChargePointEventsHandler::remoteStartTransactionRequested(unsigned int connector_id, const std::string& id_tag) { + bool ret = false; cout << "Remote start transaction : " << connector_id << " - " << id_tag << endl; - m_remote_start_pending[connector_id - 1u] = true; - m_remote_start_id_tag[connector_id - 1u] = id_tag; - return true; + if (connector_id != 0) + { + m_remote_start_pending[connector_id - 1u] = true; + m_remote_start_id_tag[connector_id - 1u] = id_tag; + ret = true; + } + else + { + for (size_t i = 1; i <= m_config.ocppConfig().numberOfConnectors(); i++) + { + if (m_chargepoint->getConnectorStatus(i) < ChargePointStatus::Charging) + { + m_remote_start_pending[i - 1u] = true; + m_remote_start_id_tag[i - 1u] = id_tag; + ret = true; + break; + } + } + } + return ret; } /** @copydoc bool IChargePointEventsHandler::remoteStopTransactionRequested(unsigned int) */ diff --git a/examples/common/config/LocalControllerConfig.h b/examples/common/config/LocalControllerConfig.h index 8679aa50..0471ec6d 100644 --- a/examples/common/config/LocalControllerConfig.h +++ b/examples/common/config/LocalControllerConfig.h @@ -90,6 +90,19 @@ class LocalControllerConfig : public ocpp::config::ILocalControllerConfig /** @brief Disconnect from Charge Point on Central System disconnection */ bool disconnectFromCpWhenCsDisconnected() const override { return getBool("DisconnectFromCpWhenCsDisconnected"); } + // Unused from Central System configuration interface + + /** @brief Boot notification retry interval */ + std::chrono::seconds bootNotificationRetryInterval() const override + { + return get("BootNotificationRetryInterval"); + } + /** @brief Heartbeat interval */ + std::chrono::seconds heartbeatInterval() const override { return get("HeartbeatInterval"); } + /** @brief If this variable set to true, then the Central System supports ISO 15118 plug and charge messages via the DataTransfer mechanism as + described in this application note. */ + bool iso15118PnCEnabled() const override { return getBool("Iso15118PnCEnabled"); } + private: /** @brief Configuration file */ ocpp::helpers::IniFile& m_config; diff --git a/examples/quick_start_cs_lc_hybrid/CMakeLists.txt b/examples/quick_start_cs_lc_hybrid/CMakeLists.txt new file mode 100644 index 00000000..58e282a5 --- /dev/null +++ b/examples/quick_start_cs_lc_hybrid/CMakeLists.txt @@ -0,0 +1,25 @@ +###################################################### +# Quick start local controller example project # +###################################################### + +# Executable target +add_executable(quick_start_cs_lc_hybrid + main.cpp + HybridCentralSystemEventsHandler.cpp +) + +# Additionnal libraries path +target_link_directories(quick_start_cs_lc_hybrid PRIVATE ${BIN_DIR}) + +# Dependencies +target_link_libraries(quick_start_cs_lc_hybrid + examples_common + localcontroller +) + + +# Copy to binary directory +ADD_CUSTOM_COMMAND(TARGET quick_start_cs_lc_hybrid + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/config/quick_start_cs_lc_hybrid.ini ${BIN_DIR}/ +) diff --git a/examples/quick_start_cs_lc_hybrid/HybridCentralSystemEventsHandler.cpp b/examples/quick_start_cs_lc_hybrid/HybridCentralSystemEventsHandler.cpp new file mode 100644 index 00000000..cb20489c --- /dev/null +++ b/examples/quick_start_cs_lc_hybrid/HybridCentralSystemEventsHandler.cpp @@ -0,0 +1,153 @@ +/* +MIT License + +Copyright (c) 2020 Cedric Jimenez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "HybridCentralSystemEventsHandler.h" +#include "StringHelpers.h" + +#include +#include + +using namespace std; + +/** @brief Constructor */ +HybridCentralSystemEventsHandler::HybridCentralSystemEventsHandler(LocalControllerConfig& config, + std::filesystem::path iso_v2g_root_ca, + std::filesystem::path iso_mo_root_ca, + bool set_pending_status) + : DefaultCentralSystemEventsHandler(iso_v2g_root_ca, iso_mo_root_ca, set_pending_status), m_config(config) +{ +} + +/** @brief Destructor */ +HybridCentralSystemEventsHandler::~HybridCentralSystemEventsHandler() { } + +// ICentralSystemEventsHandler interface + +/** @copydoc bool ICentralSystemEventsHandler::chargePointConnected(std::shared_ptr) */ +void HybridCentralSystemEventsHandler::chargePointConnected(std::shared_ptr chargepoint) +{ + cout << "Charge point [" << chargepoint->identifier() << "] connected" << endl; + auto iter_chargepoint = m_chargepoints.find(chargepoint->identifier()); + if (iter_chargepoint == m_chargepoints.end()) + { + auto iter_forwarded = m_fowarded_chargepoints.find(chargepoint->identifier()); + if (iter_forwarded == m_fowarded_chargepoints.end()) + { + // Charge point ended with "lc" are forwared to Central System + if (ocpp::helpers::endsWith(chargepoint->identifier(), "lc")) + { + // Create Local Controller proxy + auto proxy = ocpp::localcontroller::IChargePointProxy::createFrom(chargepoint, m_config); + + // Open connection to the Central System + ocpp::websockets::IWebsocketClient::Credentials credentials; + credentials.accept_untrusted_certificates = false; + credentials.allow_expired_certificates = false; + credentials.allow_selfsigned_certificates = false; + credentials.skip_server_name_check = false; + credentials.encoded_pem_certificates = false; + credentials.tls12_cipher_list = m_config.tlsv12CipherList(); + credentials.tls13_cipher_list = m_config.tlsv13CipherList(); + credentials.server_certificate_ca = m_config.tlsServerCertificateCa(); + credentials.client_certificate = m_config.tlsClientCertificate(); + credentials.client_certificate_private_key = m_config.tlsClientCertificatePrivateKey(); + credentials.client_certificate_private_key_passphrase = m_config.tlsClientCertificatePrivateKeyPassphrase(); + if (!proxy->centralSystemProxy()->connect(m_config.connexionUrl(), credentials)) + { + cout << "Forwarded Charge point [" << proxy->identifier() << "] unable to start connection to Central System" << endl; + proxy.reset(); + } + else + { + m_fowarded_chargepoints[proxy->identifier()] = + std::make_shared(*this, proxy); + } + } + else + { + m_chargepoints[chargepoint->identifier()] = + std::shared_ptr(new ChargePointRequestHandler(*this, chargepoint)); + } + } + else + { + cout << "Charge point [" << chargepoint->identifier() << "] already forwarded" << endl; + chargepoint.reset(); + } + } + else + { + cout << "Charge point [" << chargepoint->identifier() << "] already connected" << endl; + chargepoint.reset(); + } +} + +/** @brief Remove a charge point from the forwarded charge points */ +void HybridCentralSystemEventsHandler::removeForwardedChargePoint(const std::string& identifier) +{ + std::thread t( + [this, identifier = identifier] + { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + m_fowarded_chargepoints.erase(identifier); + }); + t.detach(); +} + +/** @brief Constructor */ +HybridCentralSystemEventsHandler::LocalControllerProxyEventsHandler::LocalControllerProxyEventsHandler( + HybridCentralSystemEventsHandler& event_handler, std::shared_ptr& chargepoint) + : m_event_handler(event_handler), m_chargepoint(chargepoint) +{ + m_chargepoint->registerListener(*this); +} + +/** @brief Destructor */ +HybridCentralSystemEventsHandler::LocalControllerProxyEventsHandler::~LocalControllerProxyEventsHandler() { } + +/** @brief Called to notify the disconnection of the charge point */ +void HybridCentralSystemEventsHandler::LocalControllerProxyEventsHandler::disconnectedFromChargePoint() +{ + cout << "Forwarded Charge Point [" << m_chargepoint->identifier() << "] disconnected!" << endl; + m_event_handler.removeForwardedChargePoint(m_chargepoint->identifier()); +} + +/** @brief Called to notify the connection to the central system */ +void HybridCentralSystemEventsHandler::LocalControllerProxyEventsHandler::connectedToCentralSystem() +{ + cout << "Forwarded Charge Point [" << m_chargepoint->identifier() << "] connected to Central System!" << endl; +} + +/** @brief Called to notify the failure of the connection to the central system */ +void HybridCentralSystemEventsHandler::LocalControllerProxyEventsHandler::failedToConnectToCentralSystem() + +{ + cout << "Forwarded Charge Point [" << m_chargepoint->identifier() << "] failed to connect to Central System!" << endl; +} + +/** @brief Called to notify the disconnection from the central system */ +void HybridCentralSystemEventsHandler::LocalControllerProxyEventsHandler::disconnectedFromCentralSystem() +{ + cout << "Forwarded Charge Point [" << m_chargepoint->identifier() << "] disconnected from Central System!" << endl; +} diff --git a/examples/quick_start_cs_lc_hybrid/HybridCentralSystemEventsHandler.h b/examples/quick_start_cs_lc_hybrid/HybridCentralSystemEventsHandler.h new file mode 100644 index 00000000..c6f12c97 --- /dev/null +++ b/examples/quick_start_cs_lc_hybrid/HybridCentralSystemEventsHandler.h @@ -0,0 +1,99 @@ +/* +MIT License + +Copyright (c) 2020 Cedric Jimenez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef HYBRIDCENTRALSYSTEMEVENTSHANDLER_H +#define HYBRIDCENTRALSYSTEMEVENTSHANDLER_H + +#include "DefaultCentralSystemEventsHandler.h" +#include "IChargePointProxy.h" +#include "LocalControllerConfig.h" + +/** @brief Hybrid central system event handlers implementation for the examples */ +class HybridCentralSystemEventsHandler : public DefaultCentralSystemEventsHandler +{ + public: + /** @brief Constructor */ + HybridCentralSystemEventsHandler(LocalControllerConfig& config, + std::filesystem::path iso_v2g_root_ca = "", + std::filesystem::path iso_mo_root_ca = "", + bool set_pending_status = false); + + /** @brief Destructor */ + virtual ~HybridCentralSystemEventsHandler(); + + // ICentralSystemEventsHandler interface + + /** @copydoc bool ICentralSystemEventsHandler::chargePointConnected(std::shared_ptr) */ + void chargePointConnected(std::shared_ptr chargepoint) override; + + /** @brief Handle events from local controller proxys */ + class LocalControllerProxyEventsHandler : public ocpp::localcontroller::ILocalControllerProxyEventsHandler + { + public: + /** @brief Constructor */ + LocalControllerProxyEventsHandler(HybridCentralSystemEventsHandler& event_handler, + std::shared_ptr& chargepoint); + + /** @brief Destructor */ + virtual ~LocalControllerProxyEventsHandler(); + + /** @brief Get the charge point proxy */ + std::shared_ptr proxy() { return m_chargepoint; } + + // ocpp::localcontroller::ILocalControllerProxyEventsHandler interface + + /** @brief Called to notify the disconnection of the charge point */ + void disconnectedFromChargePoint() override; + + /** @brief Called to notify the connection to the central system */ + void connectedToCentralSystem() override; + + /** @brief Called to notify the failure of the connection to the central system */ + void failedToConnectToCentralSystem() override; + + /** @brief Called to notify the disconnection from the central system */ + void disconnectedFromCentralSystem() override; + + private: + /** @brief Event handler */ + HybridCentralSystemEventsHandler& m_event_handler; + + /** @brief Charge point proxy */ + std::shared_ptr m_chargepoint; + }; + + /** @brief Get the list of the forwarded charge points */ + std::map>& forwardedChargePoints() { return m_fowarded_chargepoints; } + + /** @brief Remove a charge point from the forwarded charge points */ + void removeForwardedChargePoint(const std::string& identifier); + + private: + /** @brief Configuration */ + LocalControllerConfig& m_config; + /** @brief Forwared charge points */ + std::map> m_fowarded_chargepoints; +}; + +#endif // HYBRIDCENTRALSYSTEMEVENTSHANDLER_H diff --git a/examples/quick_start_cs_lc_hybrid/README.md b/examples/quick_start_cs_lc_hybrid/README.md new file mode 100644 index 00000000..8fd379ee --- /dev/null +++ b/examples/quick_start_cs_lc_hybrid/README.md @@ -0,0 +1,31 @@ +# Quick start Hybrid Central System / Local Controller example + +## Description + +This example simulates a central system which can act as a local controller and which accepts any charge point. + +When the charge point identifier ends with "lc", it forwards all the requests from the Central System to the Charge Point and from the Charge Point to the Central System. + +Other wise it acts as a central system. + +The hybrid central system loops on its connected charge points. For each charge point it simulates the following operations : + +* Get configuration + +There is a 5s break between loop iterations. + +## Command line + +quick_start_localcontroller [-w working_dir] [-r] + +* -w : Working directory where to store the configuration file (Default = current directory) +* -r : Reset all the OCPP persistent data + +## Quick start with existing examples + +1. Modify the ListenUrl parameter in the configuration file of the Quick Start Central System example to : ```ListenUrl=wss://127.0.0.1:8081/openocpp/``` +2. Start the Quick Start Central System Example +3. Start the Quick Start Hybrid Central System / Local Controller Example +4. Start the Quick Start Charge Point Example + +The Charge Point will be connected to the Central System through the Local Controller. diff --git a/examples/quick_start_cs_lc_hybrid/config/quick_start_cs_lc_hybrid.ini b/examples/quick_start_cs_lc_hybrid/config/quick_start_cs_lc_hybrid.ini new file mode 100644 index 00000000..67ee9166 --- /dev/null +++ b/examples/quick_start_cs_lc_hybrid/config/quick_start_cs_lc_hybrid.ini @@ -0,0 +1,24 @@ +[LocalController] +DatabasePath=./quick_start_cs_lc_hybrid.db +JsonSchemasPath=../../schemas/ +ListenUrl=wss://127.0.0.1:8080/openocpp/ +ConnexionUrl=wss://127.0.0.1:8081/openocpp/ +CallRequestTimeout=2000 +WebSocketPingInterval=30 +BootNotificationRetryInterval=30 +HeartbeatInterval=3600 +HttpBasicAuthent=false +Tlsv12CipherList=ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:PSK-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:PSK-AES128-GCM-SHA256 +Tlsv13CipherList=TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384 +TlsEcdhCurve=prime256v1 +TlsServerCertificate=../../examples/certificates/open-ocpp_central-system.crt +TlsServerCertificatePrivateKey=../../examples/certificates/open-ocpp_central-system.key +TlsServerCertificatePrivateKeyPassphrase= +TlsServerCertificateCa=../../examples/certificates/open-ocpp_ca.crt +TlsClientCertificateAuthent=true +TlsClientCertificate=../../examples/certificates/open-ocpp_charge-point.crt +TlsClientCertificatePrivateKey=../../examples/certificates/open-ocpp_charge-point.key +TlsClientCertificatePrivateKeyPassphrase= +LogMaxEntriesCount=2000 +DisconnectFromCpWhenCsDisconnected=true +Iso15118PnCEnabled=false diff --git a/examples/quick_start_cs_lc_hybrid/main.cpp b/examples/quick_start_cs_lc_hybrid/main.cpp new file mode 100644 index 00000000..207d79fc --- /dev/null +++ b/examples/quick_start_cs_lc_hybrid/main.cpp @@ -0,0 +1,182 @@ +/* +MIT License + +Copyright (c) 2020 Cedric Jimenez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "HybridCentralSystemEventsHandler.h" +#include "ICentralSystem.h" +#include "LocalControllerDemoConfig.h" + +#include +#include +#include +#include + +using namespace ocpp::centralsystem; +using namespace ocpp::types; +using namespace ocpp::messages; + +/** @brief Entry point */ +int main(int argc, char* argv[]) +{ + // Default parameters + std::string working_dir = ""; + bool reset_all = false; + + // Check parameters + if (argc > 1) + { + const char* param = nullptr; + bool bad_param = false; + argv++; + while ((argc != 1) && !bad_param) + { + if (strcmp(*argv, "-h") == 0) + { + bad_param = true; + } + else if ((strcmp(*argv, "-w") == 0) && (argc > 1)) + { + argv++; + argc--; + working_dir = *argv; + } + else if (strcmp(*argv, "-r") == 0) + { + reset_all = true; + } + else + { + param = *argv; + bad_param = true; + } + + // Next param + argc--; + argv++; + } + if (bad_param) + { + if (param) + { + std::cout << "Invalid parameter : " << param << std::endl; + } + std::cout << "Usage : quick_start_cs_lc_hybrid [-w working_dir] [-r]" << std::endl; + std::cout << " -w : Working directory where to store the configuration file (Default = current directory)" << std::endl; + std::cout << " -r : Reset all the OCPP persistent data" << std::endl; + return 1; + } + } + + std::cout << "Starting local controller with :" << std::endl; + std::cout << " - working_dir = " << working_dir << std::endl; + + // Configuration + std::filesystem::path path(working_dir); + path /= "quick_start_cs_lc_hybrid.ini"; + LocalControllerDemoConfig config(path.string()); + + // Event handler + HybridCentralSystemEventsHandler event_handler(config.stackConfig()); + + // Instanciate central system + std::unique_ptr central_system = ICentralSystem::create(config.stackConfig(), event_handler); + if (reset_all) + { + central_system->resetData(); + } + central_system->start(); + + // From now on the stack is alive :) + + // App loop + while (true) + { + // Wait for at least 1 connected charge point + while ((event_handler.chargePoints().size() == 0) && (event_handler.forwardedChargePoints().size() == 0)) + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + } + std::this_thread::sleep_for(std::chrono::seconds(5)); + + // For each connected charge point + for (auto& iter_chargepoint : event_handler.chargePoints()) + { + auto chargepoint = iter_chargepoint.second->proxy(); + + std::cout << "---------------------------------------------" << std::endl; + std::cout << "Charge point : " << chargepoint->identifier() << std::endl; + std::cout << "---------------------------------------------" << std::endl; + + std::cout << "Read whole charge point configuration..." << std::endl; + + std::vector keys; + std::vector config_keys; + std::vector unknown_keys; + if (chargepoint->getConfiguration(keys, config_keys, unknown_keys)) + { + std::cout << "Configuration keys :" << std::endl; + for (const KeyValue& key_value : config_keys) + { + std::cout << " - " << key_value.key.str() << " = " << (key_value.value.isSet() ? key_value.value.value().str() : "") + << " " << (key_value.readonly ? "(read-only)" : "") << std::endl; + } + } + else + { + std::cout << "Failed!" << std::endl; + } + } + + // For each forwarded charge point + for (auto& iter_chargepoint : event_handler.forwardedChargePoints()) + { + auto chargepoint = iter_chargepoint.second->proxy(); + + std::cout << "---------------------------------------------" << std::endl; + std::cout << "Forwarded charge point : " << chargepoint->identifier() << std::endl; + std::cout << "---------------------------------------------" << std::endl; + + std::cout << "Read whole charge point configuration..." << std::endl; + + GetConfigurationReq req; + GetConfigurationConf resp; + std::string error; + std::string message; + if (chargepoint->call(req, resp, error, message)) + { + std::cout << "Configuration keys :" << std::endl; + for (const KeyValue& key_value : resp.configurationKey.value()) + { + std::cout << " - " << key_value.key.str() << " = " << (key_value.value.isSet() ? key_value.value.value().str() : "") + << " " << (key_value.readonly ? "(read-only)" : "") << std::endl; + } + } + else + { + std::cout << "Failed!" << std::endl; + } + } + } + + return 0; +} diff --git a/src/centralsystem/chargepoint/ChargePointHandler.h b/src/centralsystem/chargepoint/ChargePointHandler.h index 930e66a5..20cc1bdf 100644 --- a/src/centralsystem/chargepoint/ChargePointHandler.h +++ b/src/centralsystem/chargepoint/ChargePointHandler.h @@ -16,8 +16,8 @@ You should have received a copy of the GNU Lesser General Public License along with OpenOCPP. If not, see . */ -#ifndef OPENOCPP_CHARGEPOINTHANDLER_H -#define OPENOCPP_CHARGEPOINTHANDLER_H +#ifndef OPENOCPP_CS_CHARGEPOINTHANDLER_H +#define OPENOCPP_CS_CHARGEPOINTHANDLER_H #include "Authorize.h" #include "BootNotification.h" @@ -329,4 +329,4 @@ class ChargePointHandler } // namespace centralsystem } // namespace ocpp -#endif // OPENOCPP_CHARGEPOINTHANDLER_H +#endif // OPENOCPP_CS_CHARGEPOINTHANDLER_H diff --git a/src/centralsystem/chargepoint/ChargePointProxy.cpp b/src/centralsystem/chargepoint/ChargePointProxy.cpp index b9a1f09f..fe61281d 100644 --- a/src/centralsystem/chargepoint/ChargePointProxy.cpp +++ b/src/centralsystem/chargepoint/ChargePointProxy.cpp @@ -69,6 +69,7 @@ ChargePointProxy::ChargePointProxy(ICentralSystem& m_msg_dispatcher(messages_validator), m_msg_sender(*m_rpc, messages_converter, messages_validator, stack_config.callRequestTimeout()), m_handler(m_identifier, messages_converter, m_msg_dispatcher, stack_config), + m_messages_validator(messages_validator), m_messages_converter(messages_converter) { m_rpc->registerSpy(*this); @@ -76,7 +77,10 @@ ChargePointProxy::ChargePointProxy(ICentralSystem& } /** @brief Destructor */ -ChargePointProxy::~ChargePointProxy() { } +ChargePointProxy::~ChargePointProxy() +{ + m_rpc->unregisterSpy(*this); +} // ICentralSystem::IChargePoint interface diff --git a/src/centralsystem/chargepoint/ChargePointProxy.h b/src/centralsystem/chargepoint/ChargePointProxy.h index 2c26eb35..39ac625a 100644 --- a/src/centralsystem/chargepoint/ChargePointProxy.h +++ b/src/centralsystem/chargepoint/ChargePointProxy.h @@ -16,8 +16,8 @@ You should have received a copy of the GNU Lesser General Public License along with OpenOCPP. If not, see . */ -#ifndef OPENOCPP_CHARGEPOINTPROXY_H -#define OPENOCPP_CHARGEPOINTPROXY_H +#ifndef OPENOCPP_CS_CHARGEPOINTPROXY_H +#define OPENOCPP_CS_CHARGEPOINTPROXY_H #include "ChargePointHandler.h" #include "GenericMessageSender.h" @@ -309,6 +309,17 @@ class ChargePointProxy : public ICentralSystem::IChargePoint, public ocpp::rpc:: /** @copydoc void IRpc::ISpy::rcpMessageSent(const std::string& msg) */ void rcpMessageSent(const std::string& msg) override; + // Accessors + + /** @brief RPC connection */ + std::shared_ptr& rpcClient() { return m_rpc; } + + /** @brief Messages validator */ + const ocpp::messages::MessagesValidator& messagesValidator() { return m_messages_validator; } + + /** @brief Messages converters */ + ocpp::messages::MessagesConverter& messagesConverter() { return m_messages_converter; } + private: /** @brief Central System instance associated to the charge point */ ICentralSystem& m_central_system; @@ -322,8 +333,10 @@ class ChargePointProxy : public ICentralSystem::IChargePoint, public ocpp::rpc:: ocpp::messages::GenericMessageSender m_msg_sender; /** @brief Request handler */ ChargePointHandler m_handler; + /** @brief Messages validator */ + const ocpp::messages::MessagesValidator& m_messages_validator; /** @brief Messages converters */ - const ocpp::messages::MessagesConverter& m_messages_converter; + ocpp::messages::MessagesConverter& m_messages_converter; /** @brief User request handler */ IChargePointRequestHandler* m_user_handler; @@ -400,4 +413,4 @@ class ChargePointProxy : public ICentralSystem::IChargePoint, public ocpp::rpc:: } // namespace centralsystem } // namespace ocpp -#endif // OPENOCPP_CHARGEPOINTPROXY_H +#endif // OPENOCPP_CS_CHARGEPOINTPROXY_H diff --git a/src/localcontroller/CMakeLists.txt b/src/localcontroller/CMakeLists.txt index 1152b7ca..c140e405 100644 --- a/src/localcontroller/CMakeLists.txt +++ b/src/localcontroller/CMakeLists.txt @@ -25,12 +25,5 @@ target_include_directories(localcontroller PRIVATE . # Dependencies target_link_libraries(localcontroller - config - database - messages - rpc - helpers - log - version - x509 + centralsystem ) diff --git a/src/localcontroller/chargepoint/ChargePointHandler.cpp b/src/localcontroller/chargepoint/ChargePointHandler.cpp index 63efdd15..bb4f0fb4 100644 --- a/src/localcontroller/chargepoint/ChargePointHandler.cpp +++ b/src/localcontroller/chargepoint/ChargePointHandler.cpp @@ -56,35 +56,40 @@ ChargePointHandler::ChargePointHandler(const std::string& m_identifier(identifier), m_central_system(central_system) { - msg_dispatcher.registerHandler(AUTHORIZE_ACTION, *dynamic_cast*>(this)); - msg_dispatcher.registerHandler(BOOT_NOTIFICATION_ACTION, - *dynamic_cast*>(this)); - msg_dispatcher.registerHandler(DATA_TRANSFER_ACTION, *dynamic_cast*>(this)); + msg_dispatcher.registerHandler(AUTHORIZE_ACTION, *dynamic_cast*>(this), true); + msg_dispatcher.registerHandler( + BOOT_NOTIFICATION_ACTION, *dynamic_cast*>(this), true); + msg_dispatcher.registerHandler( + DATA_TRANSFER_ACTION, *dynamic_cast*>(this), true); msg_dispatcher.registerHandler( DIAGNOSTIC_STATUS_NOTIFICATION_ACTION, - *dynamic_cast*>(this)); + *dynamic_cast*>(this), + true); msg_dispatcher.registerHandler( FIRMWARE_STATUS_NOTIFICATION_ACTION, - *dynamic_cast*>(this)); - msg_dispatcher.registerHandler(HEARTBEAT_ACTION, *dynamic_cast*>(this)); - msg_dispatcher.registerHandler(METER_VALUES_ACTION, *dynamic_cast*>(this)); - msg_dispatcher.registerHandler(START_TRANSACTION_ACTION, - *dynamic_cast*>(this)); - msg_dispatcher.registerHandler(STATUS_NOTIFICATION_ACTION, - *dynamic_cast*>(this)); - msg_dispatcher.registerHandler(STOP_TRANSACTION_ACTION, - *dynamic_cast*>(this)); + *dynamic_cast*>(this), + true); + msg_dispatcher.registerHandler(HEARTBEAT_ACTION, *dynamic_cast*>(this), true); + msg_dispatcher.registerHandler(METER_VALUES_ACTION, *dynamic_cast*>(this), true); + msg_dispatcher.registerHandler( + START_TRANSACTION_ACTION, *dynamic_cast*>(this), true); + msg_dispatcher.registerHandler( + STATUS_NOTIFICATION_ACTION, *dynamic_cast*>(this), true); + msg_dispatcher.registerHandler( + STOP_TRANSACTION_ACTION, *dynamic_cast*>(this), true); msg_dispatcher.registerHandler(LOG_STATUS_NOTIFICATION_ACTION, - *dynamic_cast*>(this)); + *dynamic_cast*>(this), + true); + msg_dispatcher.registerHandler(SECURITY_EVENT_NOTIFICATION_ACTION, + *dynamic_cast*>(this), + true); msg_dispatcher.registerHandler( - SECURITY_EVENT_NOTIFICATION_ACTION, - *dynamic_cast*>(this)); - msg_dispatcher.registerHandler(SIGN_CERTIFICATE_ACTION, - *dynamic_cast*>(this)); + SIGN_CERTIFICATE_ACTION, *dynamic_cast*>(this), true); msg_dispatcher.registerHandler( SIGNED_FIRMWARE_STATUS_NOTIFICATION_ACTION, - *dynamic_cast*>(this)); + *dynamic_cast*>(this), + true); } /** @brief Destructor */ ChargePointHandler::~ChargePointHandler() { } diff --git a/src/localcontroller/chargepoint/ChargePointHandler.h b/src/localcontroller/chargepoint/ChargePointHandler.h index aef92032..cf159155 100644 --- a/src/localcontroller/chargepoint/ChargePointHandler.h +++ b/src/localcontroller/chargepoint/ChargePointHandler.h @@ -16,8 +16,8 @@ You should have received a copy of the GNU Lesser General Public License along with OpenOCPP. If not, see . */ -#ifndef OPENOCPP_CHARGEPOINTHANDLER_H -#define OPENOCPP_CHARGEPOINTHANDLER_H +#ifndef OPENOCPP_LC_CHARGEPOINTHANDLER_H +#define OPENOCPP_LC_CHARGEPOINTHANDLER_H #include "Authorize.h" #include "BootNotification.h" @@ -282,4 +282,4 @@ class ChargePointHandler } // namespace localcontroller } // namespace ocpp -#endif // OPENOCPP_CHARGEPOINTHANDLER_H +#endif // OPENOCPP_LC_CHARGEPOINTHANDLER_H diff --git a/src/localcontroller/chargepoint/ChargePointProxy.cpp b/src/localcontroller/chargepoint/ChargePointProxy.cpp index e1603ed1..b90ccfc3 100644 --- a/src/localcontroller/chargepoint/ChargePointProxy.cpp +++ b/src/localcontroller/chargepoint/ChargePointProxy.cpp @@ -17,6 +17,8 @@ along with OpenOCPP. If not, see . */ #include "ChargePointProxy.h" +#include "../centralsystem/chargepoint/ChargePointProxy.h" +#include "CentralSystemProxy.h" #include "ILocalControllerConfig.h" #include "MessagesConverter.h" @@ -28,6 +30,39 @@ namespace ocpp namespace localcontroller { +/** @brief Instanciate local controller's charge point proxy from a central system's charge point proxy */ +std::shared_ptr IChargePointProxy::createFrom( + std::shared_ptr& central_system_proxy, + const ocpp::config::ILocalControllerConfig& stack_config) +{ + std::shared_ptr proxy; + + // Instanciation can only be done from centralsytem::ChargePointProxy instance + ocpp::centralsystem::ChargePointProxy* cs_proxy = dynamic_cast(central_system_proxy.get()); + if (cs_proxy) + { + // Create associated Central System proxy + CentralSystemProxy* centralsystem = + new CentralSystemProxy(cs_proxy->identifier(), cs_proxy->messagesValidator(), cs_proxy->messagesConverter(), stack_config); + + // Create the proxy + proxy = std::shared_ptr(new ChargePointProxy(cs_proxy->identifier(), + cs_proxy->rpcClient(), + cs_proxy->messagesValidator(), + cs_proxy->messagesConverter(), + stack_config, + std::shared_ptr(centralsystem))); + + // Associate both + centralsystem->setChargePointProxy(proxy); + + // Release original instance + central_system_proxy.reset(); + } + + return proxy; +} + /** @brief Constructor */ ChargePointProxy::ChargePointProxy(const std::string& identifier, std::shared_ptr rpc, diff --git a/src/localcontroller/chargepoint/ChargePointProxy.h b/src/localcontroller/chargepoint/ChargePointProxy.h index 20df7796..4d565064 100644 --- a/src/localcontroller/chargepoint/ChargePointProxy.h +++ b/src/localcontroller/chargepoint/ChargePointProxy.h @@ -16,8 +16,8 @@ You should have received a copy of the GNU Lesser General Public License along with OpenOCPP. If not, see . */ -#ifndef OPENOCPP_CHARGEPOINTPROXY_H -#define OPENOCPP_CHARGEPOINTPROXY_H +#ifndef OPENOCPP_LC_CHARGEPOINTPROXY_H +#define OPENOCPP_LC_CHARGEPOINTPROXY_H #include "ChargePointHandler.h" #include "GenericMessageSender.h" @@ -509,4 +509,4 @@ class ChargePointProxy : public IChargePointProxy, public ocpp::rpc::IRpc::IList } // namespace localcontroller } // namespace ocpp -#endif // OPENOCPP_CHARGEPOINTPROXY_H +#endif // OPENOCPP_LC_CHARGEPOINTPROXY_H diff --git a/src/localcontroller/interface/IChargePointProxy.h b/src/localcontroller/interface/IChargePointProxy.h index eda19630..7a997e71 100644 --- a/src/localcontroller/interface/IChargePointProxy.h +++ b/src/localcontroller/interface/IChargePointProxy.h @@ -19,6 +19,9 @@ along with OpenOCPP. If not, see . #ifndef OPENOCPP_ICHARGEPOINTPROXY_H #define OPENOCPP_ICHARGEPOINTPROXY_H +#include "ICentralSystem.h" +#include "ILocalControllerConfig.h" + #include "CancelReservation.h" #include "CertificateSigned.h" #include "ChangeAvailability.h" @@ -62,6 +65,15 @@ class IChargePointProxy /** @brief Destructor */ virtual ~IChargePointProxy() { } + /** + * @brief Instanciate local controller's charge point proxy from a central system's charge point proxy + * @param central_system_proxy Central system's charge point proxy + * @param stack_config Stack configuration for local controller + */ + static std::shared_ptr createFrom( + std::shared_ptr& central_system_proxy, + const ocpp::config::ILocalControllerConfig& stack_config); + /** * @brief Get the IP address of the charge point * @return IP address of the charge point diff --git a/src/localcontroller/interface/ILocalControllerConfig.h b/src/localcontroller/interface/ILocalControllerConfig.h index 4ab1c0a8..846b67ae 100644 --- a/src/localcontroller/interface/ILocalControllerConfig.h +++ b/src/localcontroller/interface/ILocalControllerConfig.h @@ -19,8 +19,7 @@ along with OpenOCPP. If not, see . #ifndef OPENOCPP_ILOCALCONTROLLERCONFIG_H #define OPENOCPP_ILOCALCONTROLLERCONFIG_H -#include -#include +#include "ICentralSystemConfig.h" namespace ocpp { @@ -28,55 +27,26 @@ namespace config { /** @brief Interface to retrieve stack internal configuration for the Local Controller role */ -class ILocalControllerConfig +class ILocalControllerConfig : public ICentralSystemConfig { public: /** @brief Destructor */ virtual ~ILocalControllerConfig() { } - // Paths - - /** @brief Path to the database to store persistent data */ - virtual std::string databasePath() const = 0; - /** @brief Path to the JSON schemas to validate the messages */ - virtual std::string jsonSchemasPath() const = 0; - - // Communication parameters - - /** @brief Listen URL */ - virtual std::string listenUrl() const = 0; - /** @brief Call request timeout */ - virtual std::chrono::milliseconds callRequestTimeout() const = 0; - /** @brief Websocket PING interval */ - virtual std::chrono::seconds webSocketPingInterval() const = 0; - /** @brief Enable HTTP basic authentication */ - virtual bool httpBasicAuthent() const = 0; - /** @brief Cipher list to use for TLSv1.2 connections */ - virtual std::string tlsv12CipherList() const = 0; - /** @brief Cipher list to use for TLSv1.3 connections */ - virtual std::string tlsv13CipherList() const = 0; - /** @brief ECDH curve to use for TLS connections */ - virtual std::string tlsEcdhCurve() const = 0; - /** @brief Server certificate */ - virtual std::string tlsServerCertificate() const = 0; - /** @brief Server certificate's private key */ - virtual std::string tlsServerCertificatePrivateKey() const = 0; - /** @brief Server certificate's private key passphrase */ - virtual std::string tlsServerCertificatePrivateKeyPassphrase() const = 0; - /** @brief Certification Authority signing chain for the server certificate */ - virtual std::string tlsServerCertificateCa() const = 0; - /** @brief Enable client authentication using certificate */ - virtual bool tlsClientCertificateAuthent() const = 0; - - // Log - - /** @brief Maximum number of entries in the log (0 = no logs in database) */ - virtual unsigned int logMaxEntriesCount() const = 0; - // Behavior /** @brief Disconnect from Charge Point on Central System disconnection */ virtual bool disconnectFromCpWhenCsDisconnected() const = 0; + + // Unused from Central System configuration interface + + /** @brief Boot notification retry interval */ + std::chrono::seconds bootNotificationRetryInterval() const override { return std::chrono::seconds(0); } + /** @brief Heartbeat interval */ + std::chrono::seconds heartbeatInterval() const override { return std::chrono::seconds(0); } + /** @brief If this variable set to true, then the Central System supports ISO 15118 plug and charge messages via the DataTransfer mechanism as + described in this application note. */ + bool iso15118PnCEnabled() const override { return false; } }; } // namespace config diff --git a/src/rpc/IRpc.h b/src/rpc/IRpc.h index 84dcaee2..951e30ef 100644 --- a/src/rpc/IRpc.h +++ b/src/rpc/IRpc.h @@ -77,6 +77,12 @@ class IRpc */ virtual void registerSpy(ISpy& spy) = 0; + /** + * @brief Unregister a spy from the RPC exchanges + * @param spy Spy object + */ + virtual void unregisterSpy(ISpy& spy) = 0; + /** @brief Interface for the RPC listeners */ class IListener { diff --git a/src/rpc/RpcBase.cpp b/src/rpc/RpcBase.cpp index a6e647bd..4a90493c 100644 --- a/src/rpc/RpcBase.cpp +++ b/src/rpc/RpcBase.cpp @@ -153,7 +153,13 @@ void RpcBase::registerListener(IRpc::IListener& listener) /** @copydoc void IRpc::registerSpy(ISpy&) */ void RpcBase::registerSpy(IRpc::ISpy& spy) { - m_spies.push_back(&spy); + m_spies.insert(&spy); +} + +/** @copydoc void IRpc::unregisterSpy(ISpy&) */ +void RpcBase::unregisterSpy(IRpc::ISpy& spy) +{ + m_spies.erase(&spy); } // RpcBase interface diff --git a/src/rpc/RpcBase.h b/src/rpc/RpcBase.h index 9994e454..d6bc4f4a 100644 --- a/src/rpc/RpcBase.h +++ b/src/rpc/RpcBase.h @@ -26,7 +26,7 @@ along with OpenOCPP. If not, see . #include #include #include -#include +#include namespace ocpp { @@ -61,6 +61,9 @@ class RpcBase : public IRpc /** @copydoc void IRpc::registerSpy(ISpy&) */ void registerSpy(IRpc::ISpy& spy) override; + /** @copydoc void IRpc::unregisterSpy(ISpy&) */ + void unregisterSpy(IRpc::ISpy& spy) override; + protected: /** @brief Start RPC operations */ void start(); @@ -124,7 +127,7 @@ class RpcBase : public IRpc /** @brief RPC listener */ IRpc::IListener* m_rpc_listener; /** @brief RPC spies */ - std::vector m_spies; + std::unordered_set m_spies; /** @brief Transaction id */ int m_transaction_id; /** @brief Mutex for concurrent call access */ diff --git a/tests/stubs/RpcStub.h b/tests/stubs/RpcStub.h index 611c38e2..eb1a0959 100755 --- a/tests/stubs/RpcStub.h +++ b/tests/stubs/RpcStub.h @@ -57,6 +57,15 @@ class RpcStub : public IRpc /** @copydoc void IRpc::registerSpy(ISpy&) */ void registerSpy(IRpc::ISpy& spy) override { m_spy = &spy; } + /** @copydoc void IRpc::unregisterSpy(ISpy&) */ + void unregisterSpy(IRpc::ISpy& spy) override + { + if (m_spy == &spy) + { + m_spy = nullptr; + } + } + // API /** @brief Set the connectivity state */