Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
37
tools/EVerest-main/modules/Misc/System/BUILD.bazel
Normal file
37
tools/EVerest-main/modules/Misc/System/BUILD.bazel
Normal file
@@ -0,0 +1,37 @@
|
||||
load("//modules:module.bzl", "cc_everest_module")
|
||||
|
||||
IMPLS = [
|
||||
"main",
|
||||
]
|
||||
|
||||
cc_everest_module(
|
||||
name = "System",
|
||||
impls = IMPLS,
|
||||
deps = [
|
||||
"//lib:run_application",
|
||||
"//lib/everest/timer:libtimer",
|
||||
"@com_github_HowardHinnant_date//:date",
|
||||
],
|
||||
)
|
||||
|
||||
# Install script files alongside the binary
|
||||
genrule(
|
||||
name = "system_scripts",
|
||||
srcs = [
|
||||
"constants.env",
|
||||
"diagnostics_uploader.sh",
|
||||
"firmware_updater.sh",
|
||||
"signed_firmware_downloader.sh",
|
||||
"signed_firmware_installer.sh",
|
||||
],
|
||||
outs = [
|
||||
"System/constants.env",
|
||||
"System/diagnostics_uploader.sh",
|
||||
"System/firmware_updater.sh",
|
||||
"System/signed_firmware_downloader.sh",
|
||||
"System/signed_firmware_installer.sh",
|
||||
],
|
||||
cmd = "mkdir -p $(RULEDIR)/System && " +
|
||||
"cp $(SRCS) $(RULEDIR)/System/",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
39
tools/EVerest-main/modules/Misc/System/CMakeLists.txt
Normal file
39
tools/EVerest-main/modules/Misc/System/CMakeLists.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
#
|
||||
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
# template version 3
|
||||
#
|
||||
|
||||
# module setup:
|
||||
# - ${MODULE_NAME}: module name
|
||||
ev_setup_cpp_module()
|
||||
|
||||
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
|
||||
# insert your custom targets and additional config variables here
|
||||
target_link_libraries(${MODULE_NAME}
|
||||
PRIVATE
|
||||
date::date
|
||||
date::date-tz
|
||||
everest::timer
|
||||
everest::run_application
|
||||
)
|
||||
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
|
||||
|
||||
target_sources(${MODULE_NAME}
|
||||
PRIVATE
|
||||
"main/systemImpl.cpp"
|
||||
)
|
||||
|
||||
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
|
||||
# insert other things like install cmds etc here
|
||||
target_compile_features(${MODULE_NAME} PRIVATE cxx_std_17)
|
||||
|
||||
install(
|
||||
PROGRAMS
|
||||
constants.env
|
||||
diagnostics_uploader.sh
|
||||
firmware_updater.sh
|
||||
signed_firmware_downloader.sh
|
||||
signed_firmware_installer.sh
|
||||
DESTINATION "${EVEREST_MODULE_INSTALL_PREFIX}/${MODULE_NAME}"
|
||||
)
|
||||
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
|
||||
15
tools/EVerest-main/modules/Misc/System/System.cpp
Normal file
15
tools/EVerest-main/modules/Misc/System/System.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#include "System.hpp"
|
||||
|
||||
namespace module {
|
||||
|
||||
void System::init() {
|
||||
invoke_init(*p_main);
|
||||
}
|
||||
|
||||
void System::ready() {
|
||||
invoke_ready(*p_main);
|
||||
}
|
||||
|
||||
} // namespace module
|
||||
67
tools/EVerest-main/modules/Misc/System/System.hpp
Normal file
67
tools/EVerest-main/modules/Misc/System/System.hpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef SYSTEM_HPP
|
||||
#define SYSTEM_HPP
|
||||
|
||||
//
|
||||
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
// template version 2
|
||||
//
|
||||
|
||||
#include "ld-ev.hpp"
|
||||
|
||||
// headers for provided interface implementations
|
||||
#include <generated/interfaces/system/Implementation.hpp>
|
||||
|
||||
// headers for required interface implementations
|
||||
#include <generated/interfaces/kvs/Interface.hpp>
|
||||
|
||||
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
|
||||
// insert your custom include headers here
|
||||
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
|
||||
|
||||
namespace module {
|
||||
|
||||
struct Conf {
|
||||
double DefaultRetries;
|
||||
double DefaultRetryInterval;
|
||||
int ResetDelay;
|
||||
};
|
||||
|
||||
class System : public Everest::ModuleBase {
|
||||
public:
|
||||
System() = delete;
|
||||
System(const ModuleInfo& info, std::unique_ptr<systemImplBase> p_main,
|
||||
std::vector<std::unique_ptr<kvsIntf>> r_store, Conf& config) :
|
||||
ModuleBase(info), p_main(std::move(p_main)), r_store(std::move(r_store)), config(config){};
|
||||
|
||||
const std::unique_ptr<systemImplBase> p_main;
|
||||
const std::vector<std::unique_ptr<kvsIntf>> r_store;
|
||||
const Conf& config;
|
||||
|
||||
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
|
||||
// insert your public definitions here
|
||||
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
|
||||
|
||||
protected:
|
||||
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
|
||||
// insert your protected definitions here
|
||||
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
|
||||
|
||||
private:
|
||||
friend class LdEverest;
|
||||
void init();
|
||||
void ready();
|
||||
|
||||
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
|
||||
// insert your private definitions here
|
||||
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
|
||||
};
|
||||
|
||||
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
|
||||
// insert other definitions here
|
||||
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
|
||||
|
||||
} // namespace module
|
||||
|
||||
#endif // SYSTEM_HPP
|
||||
27
tools/EVerest-main/modules/Misc/System/constants.env
Normal file
27
tools/EVerest-main/modules/Misc/System/constants.env
Normal file
@@ -0,0 +1,27 @@
|
||||
CONNECTION_TIMEOUT=20
|
||||
|
||||
DOWNLOADED="Downloaded"
|
||||
DOWNLOADING="Downloading"
|
||||
DOWNLOAD_FAILED="DownloadFailed"
|
||||
DOWNLOAD_SCHEDULED="DownloadScheduled"
|
||||
DOWNLOAD_PAUSED="DownloadPaused"
|
||||
|
||||
IDLE="Idle"
|
||||
|
||||
INSTALLATION_FAILED="InstallationFailed"
|
||||
INSTALLING="Installing"
|
||||
INSTALLED="Installed"
|
||||
INSTALL_REBOOTING="InstallRebooting"
|
||||
INSTALL_SCHEDULED="InstallScheduled"
|
||||
INSTALL_VERIFICATION_FAILED="InstallVerificationFailed"
|
||||
|
||||
PERMISSION_DENIED="PermissionDenied"
|
||||
NOT_SUPPORTED_OPERATION="NotSupportedOperation"
|
||||
BAD_MESSAGE="BadMessage"
|
||||
|
||||
INVALID_SIGNATURE="InvalidSignature"
|
||||
SIGNATURE_VERIFIED="SignatureVerified"
|
||||
|
||||
UPLOADED="Uploaded"
|
||||
UPLOAD_FAILURE="UploadFailure"
|
||||
UPLOADING="Uploading"
|
||||
21
tools/EVerest-main/modules/Misc/System/diagnostics_uploader.sh
Executable file
21
tools/EVerest-main/modules/Misc/System/diagnostics_uploader.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
. "${1}"
|
||||
|
||||
echo "$UPLOADING"
|
||||
sleep 2
|
||||
curl --progress-bar --ssl --connect-timeout "$CONNECTION_TIMEOUT" -T "${4}" "${2}"
|
||||
curl_exit_code=$?
|
||||
if [[ $curl_exit_code -eq 0 ]]; then
|
||||
echo "$UPLOADED"
|
||||
elif [[ $curl_exit_code -eq 67 ]] || [[ $curl_exit_code -eq 35 ]] || [[ $curl_exit_code -eq 69 ]] ||
|
||||
[[ $curl_exit_code -eq 9 ]]; then
|
||||
echo "$PERMISSION_DENIED"
|
||||
elif [[ $curl_exit_code -eq 3 ]] || [[ $curl_exit_code -eq 6 ]] || [[ $curl_exit_code -eq 10 ]] ||
|
||||
[[ $curl_exit_code -eq 87 ]]; then
|
||||
echo "$BAD_MESSAGE"
|
||||
elif [[ $curl_exit_code -eq 1 ]]; then
|
||||
echo "$NOT_SUPPORTED_OPERATION"
|
||||
else
|
||||
echo "$UPLOAD_FAILURE"
|
||||
fi
|
||||
20
tools/EVerest-main/modules/Misc/System/docs/index.rst
Normal file
20
tools/EVerest-main/modules/Misc/System/docs/index.rst
Normal file
@@ -0,0 +1,20 @@
|
||||
.. _everest_modules_handwritten_System:
|
||||
|
||||
.. ******
|
||||
.. System
|
||||
.. ******
|
||||
|
||||
This module implements system wide operations.
|
||||
|
||||
Currently this includes the following commands:
|
||||
|
||||
- Log Uploads
|
||||
- Firmware Updates
|
||||
- Setting of System time
|
||||
|
||||
Corresponding variables signal the state of Log Uploads and Firmware Updates.
|
||||
|
||||
Integration in EVerest
|
||||
======================
|
||||
|
||||
This module provides implementation for the system interface. It does not require any other modules.
|
||||
21
tools/EVerest-main/modules/Misc/System/firmware_updater.sh
Executable file
21
tools/EVerest-main/modules/Misc/System/firmware_updater.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
. "${1}"
|
||||
|
||||
echo "$DOWNLOADING"
|
||||
curl --progress-bar --ssl --connect-timeout "$CONNECTION_TIMEOUT" "${2}" -o "${3}"
|
||||
curl_exit_code=$?
|
||||
sleep 2
|
||||
if [[ $curl_exit_code -eq 0 ]]; then
|
||||
echo "$DOWNLOADED"
|
||||
else
|
||||
echo "$DOWNLOAD_FAILED"
|
||||
fi
|
||||
sleep 2
|
||||
|
||||
if [[ $curl_exit_code -eq 0 ]]; then
|
||||
echo "$INSTALLING"
|
||||
sleep 2
|
||||
echo "$INSTALLED"
|
||||
sleep 2
|
||||
fi
|
||||
475
tools/EVerest-main/modules/Misc/System/main/systemImpl.cpp
Normal file
475
tools/EVerest-main/modules/Misc/System/main/systemImpl.cpp
Normal file
@@ -0,0 +1,475 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "systemImpl.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <utils/date.hpp>
|
||||
|
||||
#include <everest/run_application/run_application.hpp>
|
||||
|
||||
using namespace everest::run_application;
|
||||
|
||||
namespace module {
|
||||
namespace main {
|
||||
|
||||
const std::string CONSTANTS = "constants.env";
|
||||
const std::string DIAGNOSTICS_UPLOADER = "diagnostics_uploader.sh";
|
||||
const std::string FIRMWARE_UPDATER = "firmware_updater.sh";
|
||||
const std::string SIGNED_FIRMWARE_DOWNLOADER = "signed_firmware_downloader.sh";
|
||||
const std::string SIGNED_FIRMWARE_INSTALLER = "signed_firmware_installer.sh";
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// FIXME (aw): this function needs to be refactored into some kind of utility library
|
||||
fs::path create_temp_file(const fs::path& dir, const std::string& prefix) {
|
||||
const std::string fn_template = (dir / prefix).string() + "XXXXXX" + std::string(1, '\0');
|
||||
std::vector<char> fn_template_buffer{fn_template.begin(), fn_template.end()};
|
||||
|
||||
// mkstemp needs to have at least 6 XXXXXX at the end and it will replace these
|
||||
// with a valid file name
|
||||
auto fd = mkstemp(fn_template_buffer.data());
|
||||
|
||||
if (fd == -1) {
|
||||
EVLOG_error << "Failed to create temporary file at: " << fn_template;
|
||||
return {};
|
||||
}
|
||||
|
||||
// close the file descriptor
|
||||
close(fd);
|
||||
|
||||
return fn_template_buffer.data();
|
||||
}
|
||||
|
||||
void systemImpl::init() {
|
||||
this->scripts_path = mod->info.paths.libexec;
|
||||
this->log_upload_running = false;
|
||||
this->firmware_download_running = false;
|
||||
this->firmware_installation_running = false;
|
||||
this->standard_firmware_update_running = false;
|
||||
this->boot_reason_key = "ocpp_boot_reason";
|
||||
}
|
||||
|
||||
void systemImpl::ready() {
|
||||
}
|
||||
|
||||
void systemImpl::standard_firmware_update(const types::system::FirmwareUpdateRequest& firmware_update_request) {
|
||||
|
||||
this->standard_firmware_update_running = true;
|
||||
EVLOG_info << "Starting firmware update";
|
||||
// create temporary file
|
||||
const auto date_time = Everest::Date::to_rfc3339(date::utc_clock::now());
|
||||
|
||||
const auto firmware_file_path = create_temp_file(fs::temp_directory_path(), "firmware-" + date_time);
|
||||
|
||||
if (firmware_file_path.empty()) {
|
||||
EVLOG_error << "Firmware update ignored, cannot write temporary file.";
|
||||
publish_firmware_update_status({types::system::FirmwareUpdateStatusEnum::DownloadFailed});
|
||||
return;
|
||||
}
|
||||
|
||||
const auto constants = this->scripts_path / CONSTANTS;
|
||||
|
||||
this->update_firmware_thread = std::thread([this, firmware_update_request, firmware_file_path, constants]() {
|
||||
const auto firmware_updater = this->scripts_path / FIRMWARE_UPDATER;
|
||||
|
||||
const std::vector<std::string> args = {constants.string(), firmware_update_request.location,
|
||||
firmware_file_path.string()};
|
||||
int32_t retries = 0;
|
||||
const auto total_retries = firmware_update_request.retries.value_or(this->mod->config.DefaultRetries);
|
||||
const auto retry_interval =
|
||||
firmware_update_request.retry_interval_s.value_or(this->mod->config.DefaultRetryInterval);
|
||||
|
||||
auto firmware_status_enum = types::system::FirmwareUpdateStatusEnum::DownloadFailed;
|
||||
types::system::FirmwareUpdateStatus firmware_status;
|
||||
firmware_status.request_id = -1;
|
||||
firmware_status.firmware_update_status = firmware_status_enum;
|
||||
|
||||
while (firmware_status.firmware_update_status == types::system::FirmwareUpdateStatusEnum::DownloadFailed &&
|
||||
retries < total_retries) {
|
||||
retries += 1;
|
||||
run_application(firmware_updater.string(), args, [this, &firmware_status](const std::string& output_line) {
|
||||
firmware_status.firmware_update_status =
|
||||
types::system::string_to_firmware_update_status_enum(output_line);
|
||||
this->publish_firmware_update_status(firmware_status);
|
||||
return CmdControl::Continue;
|
||||
});
|
||||
if (firmware_status.firmware_update_status == types::system::FirmwareUpdateStatusEnum::DownloadFailed &&
|
||||
retries < total_retries) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(retry_interval));
|
||||
}
|
||||
if (firmware_status.firmware_update_status == types::system::FirmwareUpdateStatusEnum::Installed and
|
||||
!this->mod->r_store.empty()) {
|
||||
this->mod->r_store.at(0)->call_store(boot_reason_key,
|
||||
boot_reason_to_string(types::system::BootReason::FirmwareUpdate));
|
||||
}
|
||||
}
|
||||
this->standard_firmware_update_running = false;
|
||||
});
|
||||
this->update_firmware_thread.detach();
|
||||
}
|
||||
|
||||
types::system::UpdateFirmwareResponse
|
||||
systemImpl::handle_standard_firmware_update(const types::system::FirmwareUpdateRequest& firmware_update_request) {
|
||||
|
||||
if (!this->standard_firmware_update_running) {
|
||||
if (firmware_update_request.retrieve_timestamp.has_value() &&
|
||||
Everest::Date::from_rfc3339(firmware_update_request.retrieve_timestamp.value()) > date::utc_clock::now()) {
|
||||
const auto retrieve_timestamp =
|
||||
Everest::Date::from_rfc3339(firmware_update_request.retrieve_timestamp.value());
|
||||
this->standard_update_firmware_timer.at(
|
||||
[this, retrieve_timestamp, firmware_update_request]() {
|
||||
this->standard_firmware_update(firmware_update_request);
|
||||
},
|
||||
retrieve_timestamp);
|
||||
EVLOG_info << "Download for firmware scheduled for: " << Everest::Date::to_rfc3339(retrieve_timestamp);
|
||||
} else {
|
||||
// start download immediately
|
||||
this->update_firmware_thread = std::thread(
|
||||
[this, firmware_update_request]() { this->standard_firmware_update(firmware_update_request); });
|
||||
this->update_firmware_thread.detach();
|
||||
}
|
||||
return types::system::UpdateFirmwareResponse::Accepted;
|
||||
} else {
|
||||
EVLOG_info << "Not starting firmware update because firmware update process already running";
|
||||
return types::system::UpdateFirmwareResponse::Rejected;
|
||||
}
|
||||
}
|
||||
|
||||
types::system::UpdateFirmwareResponse
|
||||
systemImpl::handle_signed_fimware_update(const types::system::FirmwareUpdateRequest& firmware_update_request) {
|
||||
|
||||
if (!firmware_update_request.signing_certificate.has_value()) {
|
||||
EVLOG_warning << "Signing certificate is missing in FirmwareUpdateRequest";
|
||||
return types::system::UpdateFirmwareResponse::Rejected;
|
||||
}
|
||||
if (!firmware_update_request.signature.has_value()) {
|
||||
EVLOG_warning << "Signature is missing in FirmwareUpdateRequest";
|
||||
return types::system::UpdateFirmwareResponse::Rejected;
|
||||
}
|
||||
|
||||
EVLOG_info << "Executing signed firmware update download callback";
|
||||
|
||||
if (firmware_update_request.retrieve_timestamp.has_value() &&
|
||||
Everest::Date::from_rfc3339(firmware_update_request.retrieve_timestamp.value()) > date::utc_clock::now()) {
|
||||
const auto retrieve_timestamp = Everest::Date::from_rfc3339(firmware_update_request.retrieve_timestamp.value());
|
||||
this->signed_firmware_update_download_timer.at(
|
||||
[this, retrieve_timestamp, firmware_update_request]() {
|
||||
this->download_signed_firmware(firmware_update_request);
|
||||
},
|
||||
retrieve_timestamp);
|
||||
EVLOG_info << "Download for firmware scheduled for: " << Everest::Date::to_rfc3339(retrieve_timestamp);
|
||||
types::system::FirmwareUpdateStatus firmware_update_status;
|
||||
firmware_update_status.request_id = firmware_update_request.request_id;
|
||||
firmware_update_status.firmware_update_status = types::system::FirmwareUpdateStatusEnum::DownloadScheduled;
|
||||
this->publish_firmware_update_status(firmware_update_status);
|
||||
} else {
|
||||
// start download immediately
|
||||
this->update_firmware_thread =
|
||||
std::thread([this, firmware_update_request]() { this->download_signed_firmware(firmware_update_request); });
|
||||
this->update_firmware_thread.detach();
|
||||
}
|
||||
|
||||
if (this->firmware_download_running) {
|
||||
return types::system::UpdateFirmwareResponse::AcceptedCanceled;
|
||||
} else if (this->firmware_installation_running) {
|
||||
return types::system::UpdateFirmwareResponse::Rejected;
|
||||
} else {
|
||||
return types::system::UpdateFirmwareResponse::Accepted;
|
||||
}
|
||||
}
|
||||
|
||||
void systemImpl::download_signed_firmware(const types::system::FirmwareUpdateRequest& firmware_update_request) {
|
||||
|
||||
if (!firmware_update_request.signing_certificate.has_value()) {
|
||||
EVLOG_warning << "Signing certificate is missing in FirmwareUpdateRequest";
|
||||
this->publish_firmware_update_status(
|
||||
{types::system::FirmwareUpdateStatusEnum::DownloadFailed, firmware_update_request.request_id});
|
||||
return;
|
||||
}
|
||||
if (!firmware_update_request.signature.has_value()) {
|
||||
EVLOG_warning << "Signature is missing in FirmwareUpdateRequest";
|
||||
this->publish_firmware_update_status(
|
||||
{types::system::FirmwareUpdateStatusEnum::DownloadFailed, firmware_update_request.request_id});
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->firmware_download_running) {
|
||||
EVLOG_info
|
||||
<< "Received Firmware update request and firmware update already running - cancelling firmware update";
|
||||
this->interrupt_firmware_download.exchange(true);
|
||||
EVLOG_info << "Waiting for other firmware download to finish...";
|
||||
std::unique_lock<std::mutex> lk(this->firmware_update_mutex);
|
||||
this->firmware_update_cv.wait(lk, [this]() { return !this->firmware_download_running; });
|
||||
EVLOG_info << "Previous Firmware download finished!";
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lg(this->firmware_update_mutex);
|
||||
EVLOG_info << "Starting Firmware update";
|
||||
this->interrupt_firmware_download.exchange(false);
|
||||
this->firmware_download_running = true;
|
||||
|
||||
// // create temporary file
|
||||
const auto date_time = Everest::Date::to_rfc3339(date::utc_clock::now());
|
||||
const auto firmware_file_path = create_temp_file(fs::temp_directory_path(), "signed_firmware-" + date_time);
|
||||
|
||||
const auto firmware_downloader = this->scripts_path / SIGNED_FIRMWARE_DOWNLOADER;
|
||||
const auto constants = this->scripts_path / CONSTANTS;
|
||||
|
||||
const std::vector<std::string> download_args = {
|
||||
constants.string(), firmware_update_request.location, firmware_file_path.string(),
|
||||
firmware_update_request.signature.value(), firmware_update_request.signing_certificate.value()};
|
||||
int32_t retries = 0;
|
||||
const auto total_retries = firmware_update_request.retries.value_or(this->mod->config.DefaultRetries);
|
||||
const auto retry_interval =
|
||||
firmware_update_request.retry_interval_s.value_or(this->mod->config.DefaultRetryInterval);
|
||||
|
||||
auto firmware_status_enum = types::system::FirmwareUpdateStatusEnum::DownloadFailed;
|
||||
types::system::FirmwareUpdateStatus firmware_status;
|
||||
firmware_status.request_id = firmware_update_request.request_id;
|
||||
firmware_status.firmware_update_status = firmware_status_enum;
|
||||
|
||||
while (firmware_status.firmware_update_status == types::system::FirmwareUpdateStatusEnum::DownloadFailed &&
|
||||
retries < total_retries && !this->interrupt_firmware_download) {
|
||||
run_application(
|
||||
firmware_downloader.string(), download_args, [this, &firmware_status](const std::string& output_line) {
|
||||
firmware_status.firmware_update_status =
|
||||
types::system::string_to_firmware_update_status_enum(output_line);
|
||||
this->publish_firmware_update_status(firmware_status);
|
||||
if (this->interrupt_firmware_download) {
|
||||
EVLOG_info << "Updating firmware was interrupted, terminating firmware update script, requestId: "
|
||||
<< firmware_status.request_id;
|
||||
return CmdControl::Terminate;
|
||||
}
|
||||
return CmdControl::Continue;
|
||||
});
|
||||
retries += 1;
|
||||
if (firmware_status.firmware_update_status == types::system::FirmwareUpdateStatusEnum::DownloadFailed &&
|
||||
retries < total_retries) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(retry_interval));
|
||||
}
|
||||
}
|
||||
if (firmware_status.firmware_update_status == types::system::FirmwareUpdateStatusEnum::SignatureVerified) {
|
||||
this->initialize_firmware_installation(firmware_update_request, firmware_file_path);
|
||||
}
|
||||
|
||||
this->firmware_download_running = false;
|
||||
this->firmware_update_cv.notify_one();
|
||||
EVLOG_info << "Firmware update thread finished";
|
||||
}
|
||||
|
||||
void systemImpl::initialize_firmware_installation(const types::system::FirmwareUpdateRequest& firmware_update_request,
|
||||
const fs::path& firmware_file_path) {
|
||||
if (firmware_update_request.install_timestamp.has_value() &&
|
||||
Everest::Date::from_rfc3339(firmware_update_request.install_timestamp.value()) > date::utc_clock::now()) {
|
||||
const auto install_timestamp = Everest::Date::from_rfc3339(firmware_update_request.install_timestamp.value());
|
||||
this->signed_firmware_update_install_timer.at(
|
||||
[this, firmware_update_request, firmware_file_path]() {
|
||||
this->install_signed_firmware(firmware_update_request, firmware_file_path);
|
||||
},
|
||||
install_timestamp);
|
||||
EVLOG_info << "Installation for firmware scheduled for: " << Everest::Date::to_rfc3339(install_timestamp);
|
||||
types::system::FirmwareUpdateStatus firmware_update_status;
|
||||
firmware_update_status.request_id = firmware_update_request.request_id;
|
||||
firmware_update_status.firmware_update_status = types::system::FirmwareUpdateStatusEnum::InstallScheduled;
|
||||
this->publish_firmware_update_status(firmware_update_status);
|
||||
} else {
|
||||
// start installation immediately
|
||||
this->update_firmware_thread = std::thread([this, firmware_update_request, firmware_file_path]() {
|
||||
this->install_signed_firmware(firmware_update_request, firmware_file_path);
|
||||
});
|
||||
this->update_firmware_thread.detach();
|
||||
}
|
||||
}
|
||||
|
||||
void systemImpl::install_signed_firmware(const types::system::FirmwareUpdateRequest& firmware_update_request,
|
||||
const fs::path& firmware_file_path) {
|
||||
auto firmware_status_enum = types::system::FirmwareUpdateStatusEnum::Installing;
|
||||
types::system::FirmwareUpdateStatus firmware_status;
|
||||
firmware_status.request_id = firmware_update_request.request_id;
|
||||
firmware_status.firmware_update_status = firmware_status_enum;
|
||||
if (!this->firmware_installation_running) {
|
||||
this->firmware_installation_running = true;
|
||||
const auto firmware_installer = this->scripts_path / SIGNED_FIRMWARE_INSTALLER;
|
||||
const auto constants = this->scripts_path / CONSTANTS;
|
||||
const std::vector<std::string> install_args = {constants.string()};
|
||||
run_application(firmware_installer.string(), install_args,
|
||||
[this, &firmware_status](const std::string& output_line) {
|
||||
firmware_status.firmware_update_status =
|
||||
types::system::string_to_firmware_update_status_enum(output_line);
|
||||
this->publish_firmware_update_status(firmware_status);
|
||||
return CmdControl::Continue;
|
||||
});
|
||||
if (firmware_status.firmware_update_status == types::system::FirmwareUpdateStatusEnum::Installed) {
|
||||
if (!this->mod->r_store.empty()) {
|
||||
this->mod->r_store.at(0)->call_store(boot_reason_key,
|
||||
boot_reason_to_string(types::system::BootReason::FirmwareUpdate));
|
||||
}
|
||||
|
||||
auto reset_type = types::system::ResetType::Hard;
|
||||
bool firmware_installation_running_copy = this->firmware_installation_running;
|
||||
this->handle_reset(reset_type, firmware_installation_running_copy);
|
||||
}
|
||||
} else {
|
||||
firmware_status.firmware_update_status = types::system::FirmwareUpdateStatusEnum::InstallationFailed;
|
||||
this->publish_firmware_update_status(firmware_status);
|
||||
}
|
||||
}
|
||||
|
||||
types::system::UpdateFirmwareResponse
|
||||
systemImpl::handle_update_firmware(types::system::FirmwareUpdateRequest& firmware_update_request) {
|
||||
if (firmware_update_request.request_id == -1) {
|
||||
return this->handle_standard_firmware_update(firmware_update_request);
|
||||
} else {
|
||||
return this->handle_signed_fimware_update(firmware_update_request);
|
||||
}
|
||||
};
|
||||
|
||||
void systemImpl::handle_allow_firmware_installation() {
|
||||
// TODO: implement me
|
||||
}
|
||||
|
||||
types::system::UploadLogsResponse
|
||||
systemImpl::handle_upload_logs(types::system::UploadLogsRequest& upload_logs_request) {
|
||||
|
||||
types::system::UploadLogsResponse response;
|
||||
|
||||
if (this->log_upload_running) {
|
||||
response.upload_logs_status = types::system::UploadLogsStatus::AcceptedCanceled;
|
||||
} else {
|
||||
response.upload_logs_status = types::system::UploadLogsStatus::Accepted;
|
||||
}
|
||||
|
||||
const auto date_time = Everest::Date::to_rfc3339(date::utc_clock::now());
|
||||
// TODO(piet): consider start time and end time
|
||||
const auto diagnostics_file_path = create_temp_file(fs::temp_directory_path(), "diagnostics-" + date_time);
|
||||
const auto diagnostics_file_name = diagnostics_file_path.filename().string();
|
||||
|
||||
response.file_name = diagnostics_file_name;
|
||||
|
||||
const auto fake_diagnostics_file = json({{"diagnostics", {{"key", "value"}}}});
|
||||
std::ofstream diagnostics_file(diagnostics_file_path.c_str());
|
||||
diagnostics_file << fake_diagnostics_file.dump();
|
||||
|
||||
this->upload_logs_thread = std::thread([this, upload_logs_request, diagnostics_file_name, diagnostics_file_path]() {
|
||||
if (this->log_upload_running) {
|
||||
EVLOG_info << "Received Log upload request and log upload already running - cancelling current upload";
|
||||
this->interrupt_log_upload.exchange(true);
|
||||
EVLOG_info << "Waiting for other log upload to finish...";
|
||||
std::unique_lock<std::mutex> lk(this->log_upload_mutex);
|
||||
this->log_upload_cv.wait(lk, [this]() { return !this->log_upload_running; });
|
||||
EVLOG_info << "Previous Log upload finished!";
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lg(this->log_upload_mutex);
|
||||
EVLOG_info << "Starting upload of log file";
|
||||
this->interrupt_log_upload.exchange(false);
|
||||
this->log_upload_running = true;
|
||||
const auto diagnostics_uploader = this->scripts_path / DIAGNOSTICS_UPLOADER;
|
||||
const auto constants = this->scripts_path / CONSTANTS;
|
||||
|
||||
std::vector<std::string> args = {constants.string(), upload_logs_request.location, diagnostics_file_name,
|
||||
diagnostics_file_path.string()};
|
||||
bool uploaded = false;
|
||||
int32_t retries = 0;
|
||||
const auto total_retries = upload_logs_request.retries.value_or(this->mod->config.DefaultRetries);
|
||||
const auto retry_interval =
|
||||
upload_logs_request.retry_interval_s.value_or(this->mod->config.DefaultRetryInterval);
|
||||
|
||||
types::system::LogStatus log_status;
|
||||
while (!uploaded && retries < total_retries && !this->interrupt_log_upload) {
|
||||
retries += 1;
|
||||
log_status.request_id = upload_logs_request.request_id.value_or(-1);
|
||||
run_application(diagnostics_uploader.string(), args, [this, &log_status](const std::string& output_line) {
|
||||
if (output_line == "Uploaded") {
|
||||
log_status.log_status = types::system::string_to_log_status_enum(output_line);
|
||||
} else if (output_line == "UploadFailure" || output_line == "PermissionDenied" ||
|
||||
output_line == "BadMessage" || output_line == "NotSupportedOperation") {
|
||||
log_status.log_status = types::system::LogStatusEnum::UploadFailure;
|
||||
} else {
|
||||
log_status.log_status = types::system::LogStatusEnum::Uploading;
|
||||
}
|
||||
this->publish_log_status(log_status);
|
||||
if (this->interrupt_log_upload) {
|
||||
return CmdControl::Terminate;
|
||||
}
|
||||
return CmdControl::Continue;
|
||||
});
|
||||
if (this->interrupt_log_upload) {
|
||||
EVLOG_info << "Uploading Logs was interrupted, terminating upload script, requestId: "
|
||||
<< log_status.request_id;
|
||||
// N01.FR.20
|
||||
log_status.log_status = types::system::LogStatusEnum::AcceptedCanceled;
|
||||
this->publish_log_status(log_status);
|
||||
} else if (log_status.log_status != types::system::LogStatusEnum::Uploaded && retries < total_retries) {
|
||||
// command finished, but neither interrupted nor uploaded
|
||||
std::this_thread::sleep_for(std::chrono::seconds(retry_interval));
|
||||
} else {
|
||||
uploaded = true;
|
||||
}
|
||||
}
|
||||
this->log_upload_running = false;
|
||||
this->log_upload_cv.notify_one();
|
||||
EVLOG_info << "Log upload thread finished";
|
||||
});
|
||||
this->upload_logs_thread.detach();
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
bool systemImpl::handle_is_reset_allowed(types::system::ResetType& type) {
|
||||
// Right now we dont want to reject a reset ever
|
||||
return true;
|
||||
}
|
||||
|
||||
void systemImpl::handle_reset(types::system::ResetType& type, bool& scheduled) {
|
||||
// let the actual work be done by a worker thread, which can also delay it
|
||||
// a little bit (if configured) to allow e.g. clean shutdown of communication
|
||||
// channels in parallel when this call returns
|
||||
std::thread([this, type, scheduled] {
|
||||
EVLOG_info << "Reset request received: " << type << ", " << (scheduled ? "" : "not ") << "scheduled";
|
||||
if (!this->mod->r_store.empty() and !this->mod->r_store.at(0)->call_exists(boot_reason_key)) {
|
||||
this->mod->r_store.at(0)->call_store(boot_reason_key,
|
||||
boot_reason_to_string(types::system::BootReason::RemoteReset));
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(this->mod->config.ResetDelay));
|
||||
|
||||
if (type == types::system::ResetType::Soft) {
|
||||
EVLOG_info << "Performing soft reset now.";
|
||||
kill(getpid(), SIGINT);
|
||||
} else {
|
||||
EVLOG_info << "Performing hard reset now.";
|
||||
kill(getpid(), SIGINT); // FIXME(piet): Define appropriate behavior for hard reset
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
bool systemImpl::handle_set_system_time(std::string& timestamp) {
|
||||
// your code for cmd set_system_time goes here
|
||||
return true;
|
||||
};
|
||||
|
||||
types::system::BootReason systemImpl::handle_get_boot_reason() {
|
||||
if (this->mod->r_store.empty()) {
|
||||
return types::system::BootReason::PowerUp;
|
||||
}
|
||||
auto reason_variant = this->mod->r_store.at(0)->call_load(boot_reason_key);
|
||||
auto* reason = std::get_if<std::string>(&reason_variant);
|
||||
std::string final_reason{boot_reason_to_string(types::system::BootReason::PowerUp)};
|
||||
if (reason != nullptr) {
|
||||
final_reason = *reason;
|
||||
}
|
||||
this->mod->r_store.at(0)->call_delete(boot_reason_key);
|
||||
return types::system::string_to_boot_reason(final_reason);
|
||||
}
|
||||
|
||||
} // namespace main
|
||||
} // namespace module
|
||||
152
tools/EVerest-main/modules/Misc/System/main/systemImpl.hpp
Normal file
152
tools/EVerest-main/modules/Misc/System/main/systemImpl.hpp
Normal file
@@ -0,0 +1,152 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef MAIN_SYSTEM_IMPL_HPP
|
||||
#define MAIN_SYSTEM_IMPL_HPP
|
||||
|
||||
//
|
||||
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
// template version 3
|
||||
//
|
||||
|
||||
#include <generated/interfaces/system/Implementation.hpp>
|
||||
|
||||
#include "../System.hpp"
|
||||
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
// insert your custom include headers here
|
||||
#include <filesystem>
|
||||
|
||||
#include <everest/timer.hpp>
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
|
||||
namespace module {
|
||||
namespace main {
|
||||
|
||||
struct Conf {};
|
||||
|
||||
class systemImpl : public systemImplBase {
|
||||
public:
|
||||
systemImpl() = delete;
|
||||
systemImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<System>& mod, Conf& config) :
|
||||
systemImplBase(ev, "main"), mod(mod), config(config){};
|
||||
|
||||
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
|
||||
// insert your public definitions here
|
||||
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
|
||||
|
||||
protected:
|
||||
// command handler functions (virtual)
|
||||
virtual types::system::UpdateFirmwareResponse
|
||||
handle_update_firmware(types::system::FirmwareUpdateRequest& firmware_update_request) override;
|
||||
virtual void handle_allow_firmware_installation() override;
|
||||
virtual types::system::UploadLogsResponse
|
||||
handle_upload_logs(types::system::UploadLogsRequest& upload_logs_request) override;
|
||||
virtual bool handle_is_reset_allowed(types::system::ResetType& type) override;
|
||||
virtual void handle_reset(types::system::ResetType& type, bool& scheduled) override;
|
||||
virtual bool handle_set_system_time(std::string& timestamp) override;
|
||||
virtual types::system::BootReason handle_get_boot_reason() override;
|
||||
|
||||
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
|
||||
// insert your protected definitions here
|
||||
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
|
||||
|
||||
private:
|
||||
const Everest::PtrContainer<System>& mod;
|
||||
const Conf& config;
|
||||
|
||||
virtual void init() override;
|
||||
virtual void ready() override;
|
||||
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
// insert your private definitions here
|
||||
|
||||
std::filesystem::path scripts_path;
|
||||
|
||||
std::atomic<bool> interrupt_firmware_download;
|
||||
std::atomic<bool> interrupt_log_upload;
|
||||
|
||||
bool log_upload_running;
|
||||
bool standard_firmware_update_running;
|
||||
bool firmware_download_running;
|
||||
std::atomic<bool> firmware_installation_running;
|
||||
|
||||
std::condition_variable log_upload_cv;
|
||||
std::condition_variable firmware_update_cv;
|
||||
|
||||
std::mutex log_upload_mutex;
|
||||
std::mutex firmware_update_mutex;
|
||||
|
||||
std::thread update_firmware_thread;
|
||||
std::thread upload_logs_thread;
|
||||
|
||||
Everest::SteadyTimer standard_update_firmware_timer;
|
||||
Everest::SteadyTimer signed_firmware_update_download_timer;
|
||||
Everest::SteadyTimer signed_firmware_update_install_timer;
|
||||
|
||||
std::string boot_reason_key;
|
||||
|
||||
/**
|
||||
* @brief Executes a standard firmware update using the given \p firmware_update_request
|
||||
*
|
||||
* @param firmware_update_request
|
||||
*/
|
||||
void standard_firmware_update(const types::system::FirmwareUpdateRequest& firmware_update_request);
|
||||
|
||||
/**
|
||||
* @brief Handles the given \p firmware_update_request . If firmware update is already running, the request will be
|
||||
* rejected. If the download should not be started in the future it starts the download and installation of the
|
||||
* firmware immediately, otherwise this method sets a timer for the download accordingly.
|
||||
*
|
||||
* @param firmware_update_request
|
||||
* @return types::system::UpdateFirmwareResponse
|
||||
*/
|
||||
types::system::UpdateFirmwareResponse
|
||||
handle_standard_firmware_update(const types::system::FirmwareUpdateRequest& firmware_update_request);
|
||||
|
||||
/**
|
||||
* @brief Handles the given \p firmware_update_request. If the download should not be started in the future it
|
||||
* starts the download and installation of the firmware immediately, otherwise this method sets a timer for the
|
||||
* download accordingly.
|
||||
*
|
||||
* @param firmware_update_request
|
||||
*/
|
||||
types::system::UpdateFirmwareResponse
|
||||
handle_signed_fimware_update(const types::system::FirmwareUpdateRequest& firmware_update_request);
|
||||
|
||||
/**
|
||||
* @brief Handles the download of the firmware specified in the given \p firmware_update_request . If a download is
|
||||
* already running, this method will interrupt the download process and restart it.
|
||||
*
|
||||
* @param firmware_update_request
|
||||
*/
|
||||
void download_signed_firmware(const types::system::FirmwareUpdateRequest& firmware_update_request);
|
||||
|
||||
/**
|
||||
* @brief Initializes the firmware installation by starting it immediately or if specified in the \p
|
||||
* firmware_update_request it schedules it for the future.
|
||||
*
|
||||
* @param firmware_update_request
|
||||
* @param firmware_file_path
|
||||
*/
|
||||
void initialize_firmware_installation(const types::system::FirmwareUpdateRequest& firmware_update_request,
|
||||
const std::filesystem::path& firmware_file_path);
|
||||
|
||||
/**
|
||||
* @brief Executes the installation of the firmware specified in the given \p firmware_update_request .
|
||||
*
|
||||
* @param firmware_update_reqeust
|
||||
* @param firmware_file_path
|
||||
*/
|
||||
void install_signed_firmware(const types::system::FirmwareUpdateRequest& firmware_update_reqeust,
|
||||
const std::filesystem::path& firmware_file_path);
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
};
|
||||
|
||||
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
|
||||
// insert other definitions here
|
||||
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
|
||||
|
||||
} // namespace main
|
||||
} // namespace module
|
||||
|
||||
#endif // MAIN_SYSTEM_IMPL_HPP
|
||||
33
tools/EVerest-main/modules/Misc/System/manifest.yaml
Normal file
33
tools/EVerest-main/modules/Misc/System/manifest.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
description: This module implements system wide operations
|
||||
config:
|
||||
DefaultRetries:
|
||||
description: Specifies how many times Charge Point tries to upload or download files on previous failure.
|
||||
type: number
|
||||
default: 1
|
||||
DefaultRetryInterval:
|
||||
description: >-
|
||||
Specifies in seconds after which time a retry of an upload or download on previous failure may be attempted.
|
||||
type: number
|
||||
default: 1
|
||||
ResetDelay:
|
||||
description: >-
|
||||
When receiving a reset request, then the actual execution can be delayed by this amount of time (given in seconds).
|
||||
This might be necessary, for example, when the reset request arrives via the network and the call acknowledgement
|
||||
should be given some time to travel the return path to the caller.
|
||||
Defaults to zero, which means that the reset is executed directly without delay.
|
||||
type: integer
|
||||
minimum: 0
|
||||
default: 0
|
||||
provides:
|
||||
main:
|
||||
description: Implements the system interface
|
||||
interface: system
|
||||
requires:
|
||||
store:
|
||||
interface: kvs
|
||||
min_connections: 0
|
||||
max_connections: 1
|
||||
metadata:
|
||||
license: https://opensource.org/licenses/Apache-2.0
|
||||
authors:
|
||||
- Piet Gömpel
|
||||
30
tools/EVerest-main/modules/Misc/System/signed_firmware_downloader.sh
Executable file
30
tools/EVerest-main/modules/Misc/System/signed_firmware_downloader.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
. "${1}"
|
||||
|
||||
SIGNATURE_VALIDATION_DIR=$(mktemp -d /tmp/signature_validation_XXXXX)
|
||||
sleep 2
|
||||
echo "$DOWNLOADING"
|
||||
|
||||
sleep 2
|
||||
curl --progress-bar --ssl --connect-timeout "$CONNECTION_TIMEOUT" "${2}" -o "${3}"
|
||||
curl_exit_code=$?
|
||||
sleep 2
|
||||
if [[ $curl_exit_code -eq 0 ]]; then
|
||||
echo "$DOWNLOADED"
|
||||
echo -e "${4}" >"$SIGNATURE_VALIDATION_DIR/firmware_signature.base64"
|
||||
echo -e "${5}" >"$SIGNATURE_VALIDATION_DIR/firmware_cert.pem"
|
||||
openssl x509 -pubkey -noout -in "$SIGNATURE_VALIDATION_DIR/firmware_cert.pem" >"$SIGNATURE_VALIDATION_DIR/pubkey.pem"
|
||||
openssl base64 -d -in "$SIGNATURE_VALIDATION_DIR/firmware_signature.base64" -out "$SIGNATURE_VALIDATION_DIR/firmware_signature.sha256"
|
||||
r=$(openssl dgst -sha256 -verify "$SIGNATURE_VALIDATION_DIR/pubkey.pem" -signature "$SIGNATURE_VALIDATION_DIR/firmware_signature.sha256" "${3}")
|
||||
|
||||
if [ "$r" = "Verified OK" ]; then
|
||||
echo "$SIGNATURE_VERIFIED"
|
||||
else
|
||||
echo "$INVALID_SIGNATURE"
|
||||
fi
|
||||
else
|
||||
echo "$DOWNLOAD_FAILED"
|
||||
fi
|
||||
|
||||
rm -rf "$SIGNATURE_VALIDATION_DIR"
|
||||
7
tools/EVerest-main/modules/Misc/System/signed_firmware_installer.sh
Executable file
7
tools/EVerest-main/modules/Misc/System/signed_firmware_installer.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
. "${1}"
|
||||
|
||||
echo "$INSTALLING"
|
||||
sleep 2
|
||||
echo "$INSTALLED"
|
||||
Reference in New Issue
Block a user