// SPDX-License-Identifier: Apache-2.0 // Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace po = boost::program_options; ocpp::v16::ChargePoint* charge_point; bool running = false; std::mutex m; int main(int argc, char* argv[]) { po::options_description desc("OCPP charge point"); desc.add_options()("help,h", "produce help message"); desc.add_options()("maindir", po::value(), "set main dir in which the schemas folder resides"); desc.add_options()("conf", po::value(), "charge point config relative to maindir"); desc.add_options()("logconf", po::value(), "The path to a custom logging.ini"); po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); if (vm.count("help") != 0) { std::cout << desc << "\n"; return 1; } std::string maindir = "."; if (vm.count("maindir") != 0) { maindir = vm["maindir"].as(); } const auto database_path = "/tmp/ocpp"; const auto share_path = fs::path(maindir) / "share" / "everest" / "modules" / "OCPP"; // initialize logging as early as possible auto logging_config = share_path / "logging.ini"; if (vm.count("logconf") != 0) { logging_config = fs::path(vm["logconf"].as()); } Everest::Logging::init(logging_config.string(), "charge_point"); std::string conf = "config.json"; if (vm.count("conf") != 0) { conf = vm["conf"].as(); } fs::path config_path = share_path / conf; if (!fs::exists(config_path)) { EVLOG_error << "Could not find config at: " << config_path; return 1; } std::ifstream ifs(config_path.c_str()); std::string config_file((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); auto json_config = json::parse(config_file); json_config["Internal"]["LogMessagesFormat"][0] = "console_detailed"; auto user_config_path = fs::path("/tmp") / "user_config.json"; if (fs::exists(user_config_path)) { std::ifstream ifs(user_config_path.c_str()); std::string user_config_file((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); auto user_config = json::parse(user_config_file); json_config.merge_patch(user_config); } else { std::ofstream fs(user_config_path.c_str()); auto user_config = json::object(); fs << user_config << std::endl; fs.close(); } const fs::path sql_init_path = share_path / "core_migrations"; // create the cso_path const fs::path cso_path = "/tmp/client/cso"; if (!fs::exists(cso_path)) { fs::create_directories(cso_path); } const fs::path csms_path = "/tmp/client/csms"; if (!fs::exists(csms_path)) { fs::create_directories(csms_path); } const fs::path ca_v2g_path = "/tmp/certs/ca/v2g/"; if (!fs::exists(ca_v2g_path)) { fs::create_directories(ca_v2g_path); } const fs::path ca_mo_path = "/tmp/certs/ca/mo/"; if (!fs::exists(ca_mo_path)) { fs::create_directories(ca_mo_path); } if (!fs::is_regular_file("/tmp/certs/ca/v2g/V2G_CA_BUNDLE.pem")) { fsstd::ofstream("/tmp/certs/ca/v2g/V2G_CA_BUNDLE.pem"); } if (!fs::is_regular_file("/tmp/certs/ca/mo/MO_CA_BUNDLE.pem")) { fsstd::ofstream("/tmp/certs/ca/mo//MO_CA_BUNDLE.pem"); } ocpp::SecurityConfiguration secConfig; secConfig.csms_ca_bundle = fs::path("/tmp/certs/ca/v2g/V2G_CA_BUNDLE.pem"); secConfig.mf_ca_bundle = fs::path("/tmp/certs/ca/v2g/V2G_CA_BUNDLE.pem"); secConfig.v2g_ca_bundle = fs::path("/tmp/certs/ca/v2g/V2G_CA_BUNDLE.pem"); secConfig.mo_ca_bundle = fs::path("/tmp/certs/ca/mo/MO_CA_BUNDLE.pem"); secConfig.csms_leaf_cert_directory = fs::path("/tmp/client/csms/"); secConfig.csms_leaf_key_directory = fs::path("/tmp/client/csms/"); secConfig.secc_leaf_cert_directory = fs::path("/tmp/client/cso/"); secConfig.secc_leaf_key_directory = fs::path("/tmp/client/cso/"); charge_point = new ocpp::v16::ChargePoint(json_config.dump(), share_path, user_config_path, database_path, sql_init_path, fs::path("/tmp"), nullptr, secConfig); /************************************** START REGISTERING CALLBACKS **************************************/ charge_point->register_enable_evse_callback([](std::int32_t connector) { std::cout << "Callback: " << "Enabling chargepoint at connector#" << connector; return true; }); charge_point->register_disable_evse_callback([](std::int32_t connector) { std::cout << "Callback: " << "Disabling chargepoint at connector#" << connector; return true; }); charge_point->register_pause_charging_callback([](std::int32_t connector) { std::cout << "Callback: " << "Pausing charging at connector#" << connector; return true; }); charge_point->register_resume_charging_callback([](std::int32_t connector) { std::cout << "Callback: " << "Resuming charging at connector#" << connector; return true; }); charge_point->register_provide_token_callback( [](const std::string& id_token, const std::vector& referenced_connectors, bool prevalidated) { std::cout << "Callback: " << "Received token " << id_token << " for further validation" << std::endl; }); charge_point->register_stop_transaction_callback([](std::int32_t connector, ocpp::v16::Reason reason) { std::cout << "Callback: " << "Stopping transaction with at connector#" << connector << " with reason: " << ocpp::v16::conversions::reason_to_string(reason); return true; }); charge_point->register_reserve_now_callback([](std::int32_t reservation_id, std::int32_t connector, ocpp::DateTime expiryDate, ocpp::CiString<20> idTag, std::optional> parent_id) { std::cout << "Callback: " << "Reserving connector# " << connector << " for id token: " << idTag.get() << " until " << expiryDate; return ocpp::v16::ReservationStatus::Accepted; }); charge_point->register_cancel_reservation_callback([](std::int32_t reservation_id) { std::cout << "Callback: " << "Cancelling reservation with id: " << reservation_id; return true; }); charge_point->register_unlock_connector_callback([](std::int32_t connector) { std::cout << "Callback: " << "Unlock connector#" << connector; return ocpp::v16::UnlockStatus::Unlocked; }); charge_point->register_upload_diagnostics_callback([](const ocpp::v16::GetDiagnosticsRequest& request) { std::cout << "Callback: " << "Uploading Diagnostics file" << std::endl; ocpp::v16::GetLogResponse r; r.status = ocpp::v16::LogStatusEnumType::Accepted; return r; }); charge_point->register_update_firmware_callback([](const ocpp::v16::UpdateFirmwareRequest msg) { std::cout << "Callback: " << "Updating Firmware" << std::endl; }); charge_point->register_signed_update_firmware_callback([](ocpp::v16::SignedUpdateFirmwareRequest msg) { std::cout << "Callback: " << "Updating Signed Firmware" << std::endl; return ocpp::v16::UpdateFirmwareStatusEnumType::Accepted; }); charge_point->register_upload_logs_callback([](ocpp::v16::GetLogRequest req) { std::cout << "Callback: " << "Uploading Logs" << std::endl; ocpp::v16::GetLogResponse r; r.status = ocpp::v16::LogStatusEnumType::Accepted; return r; }); charge_point->register_set_connection_timeout_callback([](std::int32_t connection_timeout) { std::cout << "Callback: " << "Setting connection timeout" << std::endl; }); charge_point->register_reset_callback([](const ocpp::v16::ResetType& reset_type) { std::cout << "Callback: " << "Exectuting: " << ocpp::v16::conversions::reset_type_to_string(reset_type) << " reset" << std::endl; return true; }); charge_point->register_set_system_time_callback([](const std::string& system_time) { std::cout << "Callback: " << "Setting system time to " << system_time; }); charge_point->register_signal_set_charging_profiles_callback([]() { std::cout << "Callback: " << "Setting charging profiles" << std::endl; }); charge_point->register_transaction_updated_callback([](const std::int32_t connector, const std::string& session_id, const std::int32_t transaction_id, const ocpp::v16::IdTagInfo& id_tag_info) { std::cout << "Callback: Transaction updated at connector# " << connector << " and transaction id: " << transaction_id << std::endl; }); /************************************** STOP REGISTERING CALLBACKS **************************************/ charge_point->start(); { std::lock_guard lk(m); running = true; } std::thread t1([&] { std::int32_t value = 0; bool transaction_running = false; while (!std::cin.fail() && running) { std::string command; std::cin >> command; std::cout << std::endl; std::string uuid; if (command == "auth") { auto result = charge_point->authorize_id_token(ocpp::CiString<20>(std::string("DEADBEEF"))); } else if (command == "start_transaction") { if (!transaction_running) { uuid = boost::lexical_cast(boost::uuids::random_generator()()); charge_point->on_session_started(1, uuid, ocpp::SessionStartedReason::EVConnected, std::nullopt); const auto result = charge_point->authorize_id_token(ocpp::CiString<20>(std::string("DEADBEEF"))); if (result.id_tag_info.status == ocpp::v16::AuthorizationStatus::Accepted) { charge_point->on_transaction_started(1, uuid, "DEADBEEF", 0, std::nullopt, ocpp::DateTime(), std::nullopt); charge_point->on_resume_charging(1); transaction_running = true; } } } else if (command == "stop_transaction") { if (transaction_running) { charge_point->on_transaction_stopped(1, uuid, ocpp::v16::Reason::Local, ocpp::DateTime(), 2500, std::nullopt, std::nullopt); charge_point->on_session_stopped(1, uuid); transaction_running = false; } else { std::cout << "No transaction is currently running. Use command \"start_transaction\" first" << std::endl; } } else if (command == "end") { running = false; } else if (command == "help") { std::cout << "You can run the following commands:\n" << "\t- start_transaction : Starts a transaction with token \"DEADBEEF\"\n" << "\t- stop_transaction : Stops a transaction if currently running\n" << "\t- auth : Authorizes the token \"DEADBEEF\"\n" << "\t- end : Stop execution of the program\n" << std::endl; } else { std::cout << "Received unknown command" << std::endl; } } }); t1.join(); std::cout << "Goodbye" << std::endl; return 0; }