Skip to content

Commit

Permalink
Allow to configure system time from HTTP API
Browse files Browse the repository at this point in the history
  • Loading branch information
dshil committed Dec 3, 2024
1 parent 2f507a4 commit 3724f8b
Show file tree
Hide file tree
Showing 14 changed files with 268 additions and 13 deletions.
1 change: 1 addition & 0 deletions components/ocs_algo/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ idf_component_register(
"string_ops.cpp"
"uri_ops.cpp"
"storage_ops.cpp"
"time_ops.cpp"

REQUIRES
"ocs_storage"
Expand Down
25 changes: 25 additions & 0 deletions components/ocs_algo/test/test_time_ops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,30 @@ TEST_CASE("Time ops: after: check back in time", "[ocs_algo], [time_ops]") {
}
}

TEST_CASE("Time ops: parse time: valid input", "[ocs_algo], [time_ops]") {
const char* str = "1640295065";
const auto timestamp = TimeOps::parse_time(str);
TEST_ASSERT_TRUE(timestamp.has_value());
TEST_ASSERT_EQUAL(timestamp.value(), 1640295065);
}

TEST_CASE("Time ops: parse time: invalid input", "[ocs_algo], [time_ops]") {
const char* str = "123Invalid string";
const auto timestamp = TimeOps::parse_time(str);
TEST_ASSERT_FALSE(timestamp.has_value());
}

TEST_CASE("Time ops: parse time: empty input", "[ocs_algo], [time_ops]") {
const char* str = "";
const auto timestamp = TimeOps::parse_time(str);
TEST_ASSERT_FALSE(timestamp.has_value());
}

TEST_CASE("Time ops: parse time: overflow", "[ocs_algo], [time_ops]") {
const char* str = "144029506500000";
const auto timestamp = TimeOps::parse_time(str);
TEST_ASSERT_FALSE(timestamp.has_value());
}

} // namespace algo
} // namespace ocs
36 changes: 36 additions & 0 deletions components/ocs_algo/time_ops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
*/

#include <cerrno>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <sys/time.h>

#include "ocs_algo/time_ops.h"
#include "ocs_core/log.h"
Expand All @@ -33,5 +35,39 @@ std::optional<time_t> TimeOps::get_time() {
return timestamp;
}

std::optional<time_t> TimeOps::parse_time(const char* str) {
const auto value = strtol(str, nullptr, 10);

if (value == 0) {
return std::nullopt;
}

if (value == LONG_MIN || value == LONG_MAX) {
return std::nullopt;
}

return value;
}

status::StatusCode TimeOps::set_time(const char* str, time_t start_point) {
const auto timestamp = parse_time(str);
if (!timestamp) {
return status::StatusCode::InvalidArg;
}

if (*timestamp < start_point) {
return status::StatusCode::InvalidArg;
}

const timeval time_value = { *timestamp, 0 };
if (settimeofday(&time_value, nullptr) == -1) {
ocs_loge(log_tag, "settimeofday(): %s", std::strerror(errno));

return status::StatusCode::Error;
}

return status::StatusCode::OK;
}

} // namespace algo
} // namespace ocs
10 changes: 9 additions & 1 deletion components/ocs_algo/time_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <optional>

#include "ocs_core/time.h"
#include "ocs_status/code.h"

namespace ocs {
namespace algo {
Expand Down Expand Up @@ -44,8 +45,15 @@ struct TimeOps {
return true;
}

//! Return UNIX time.
//! Return system time.
static std::optional<time_t> get_time();

//! Parse system time from @p str.
static std::optional<time_t> parse_time(const char* str);

//! Set the system time from @p str, making sure that the set time is greater
//! than @p start_point.
static status::StatusCode set_time(const char* str, time_t start_point);
};

} // namespace algo
Expand Down
2 changes: 1 addition & 1 deletion components/ocs_fmt/json/fanout_formatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ status::StatusCode FanoutFormatter::format(cJSON* json) {
}

return status::StatusCode::OK;
} // namespace status::StatusCodeFanoutFormatter::format(cJSON*json)
}

void FanoutFormatter::add(IFormatter& formatter) {
formatters_.emplace_back(&formatter);
Expand Down
12 changes: 10 additions & 2 deletions components/ocs_fmt/json/time_formatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,30 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#include "ocs_fmt/json/time_formatter.h"
#include "ocs_algo/time_ops.h"
#include "ocs_fmt/json/cjson_object_formatter.h"
#include "ocs_fmt/json/time_formatter.h"

namespace ocs {
namespace fmt {
namespace json {

TimeFormatter::TimeFormatter(time_t start_point)
: start_point_(start_point) {
}

status::StatusCode TimeFormatter::format(cJSON* json) {
CjsonObjectFormatter formatter(json);

const auto timestamp = algo::TimeOps::get_time();
auto timestamp = algo::TimeOps::get_time();
if (!timestamp) {
return status::StatusCode::Error;
}

if (*timestamp < start_point_) {
*timestamp = -1;
}

if (!formatter.add_number_cs("timestamp", *timestamp)) {
return status::StatusCode::NoMem;
}
Expand Down
14 changes: 13 additions & 1 deletion components/ocs_fmt/json/time_formatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#pragma once

#include <ctime>

#include "ocs_core/noncopyable.h"
#include "ocs_fmt/json/iformatter.h"

Expand All @@ -17,8 +19,18 @@ namespace json {

class TimeFormatter : public IFormatter, public core::NonCopyable<> {
public:
//! Format UNIX time to @p json.
//! Initialize.
//!
//! @params
//! - @p start_point - the current system time will only be added to the JSON if
//! it's greater than this value.
explicit TimeFormatter(time_t start_point);

//! Format system time to @p json.
status::StatusCode format(cJSON* json) override;

private:
const time_t start_point_ { -1 };
};

} // namespace json
Expand Down
2 changes: 2 additions & 0 deletions components/ocs_pipeline/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ idf_component_register(
"httpserver/system_handler.cpp"
"httpserver/system_state_handler.cpp"
"httpserver/web_gui_pipeline.cpp"
"httpserver/time_handler.cpp"
"httpserver/time_pipeline.cpp"

"jsonfmt/data_pipeline.cpp"
"jsonfmt/ap_network_formatter.cpp"
Expand Down
67 changes: 67 additions & 0 deletions components/ocs_pipeline/httpserver/time_handler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2024, Open Control Systems authors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#include "ocs_algo/time_ops.h"
#include "ocs_algo/uri_ops.h"
#include "ocs_pipeline/httpserver/time_handler.h"

namespace ocs {
namespace pipeline {
namespace httpserver {

TimeHandler::TimeHandler(http::Server& server, time_t start_point) {
server.add_GET("/api/v1/system/time", [start_point](httpd_req_t* req) {
const auto values = algo::UriOps::parse_query(req->uri);
const auto it = values.find("value");

if (it == values.end()) {
const auto timestamp = algo::TimeOps::get_time();
if (!timestamp) {
return status::StatusCode::Error;
}

auto err = httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
if (err != ESP_OK) {
return status::StatusCode::Error;
}

err = httpd_resp_send(req, std::to_string(*timestamp).c_str(),
HTTPD_RESP_USE_STRLEN);
if (err != ESP_OK) {
return status::StatusCode::Error;
}

return status::StatusCode::OK;
}

char buf[it->second.size() + 1];
memset(buf, 0, sizeof(buf));
memcpy(buf, it->second.data(), it->second.size());

const auto code = algo::TimeOps::set_time(buf, start_point);
if (code != status::StatusCode::OK) {
return code;
}

auto err = httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
if (err != ESP_OK) {
return status::StatusCode::Error;
}

err = httpd_resp_send(req, "OK", HTTPD_RESP_USE_STRLEN);
if (err != ESP_OK) {
return status::StatusCode::Error;
}

return status::StatusCode::OK;
});
}

} // namespace httpserver
} // namespace pipeline
} // namespace ocs
32 changes: 32 additions & 0 deletions components/ocs_pipeline/httpserver/time_handler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2024, Open Control Systems authors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#pragma once

#include <ctime>

#include "ocs_core/noncopyable.h"
#include "ocs_http/server.h"

namespace ocs {
namespace pipeline {
namespace httpserver {

class TimeHandler : public core::NonCopyable<> {
public:
//! Initialize.
//!
//! @params
//! - @p server to register HTTP endpoint.
//! - @p start_point - time set via the HTTP API should be greater than this value.
TimeHandler(http::Server& server, time_t start_point);
};

} // namespace httpserver
} // namespace pipeline
} // namespace ocs
34 changes: 34 additions & 0 deletions components/ocs_pipeline/httpserver/time_pipeline.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2024, Open Control Systems authors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#include "freertos/FreeRTOSConfig.h"

#include "ocs_fmt/json/time_formatter.h"
#include "ocs_pipeline/httpserver/time_pipeline.h"

namespace ocs {
namespace pipeline {
namespace httpserver {

TimePipeline::TimePipeline(http::Server& server,
fmt::json::FanoutFormatter& telemetry_formatter,
fmt::json::FanoutFormatter& registration_formatter,
time_t start_point) {
formatter_.reset(new (std::nothrow) fmt::json::TimeFormatter(start_point));
configASSERT(formatter_);

telemetry_formatter.add(*formatter_);
registration_formatter.add(*formatter_);

handler_.reset(new (std::nothrow) TimeHandler(server, start_point));
configASSERT(handler_);
}

} // namespace httpserver
} // namespace pipeline
} // namespace ocs
38 changes: 38 additions & 0 deletions components/ocs_pipeline/httpserver/time_pipeline.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2024, Open Control Systems authors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#pragma once

#include <ctime>
#include <memory>

#include "ocs_core/noncopyable.h"
#include "ocs_fmt/json/fanout_formatter.h"
#include "ocs_http/server.h"
#include "ocs_pipeline/httpserver/time_handler.h"

namespace ocs {
namespace pipeline {
namespace httpserver {

class TimePipeline : public core::NonCopyable<> {
public:
//! Initialize.
TimePipeline(http::Server& server,
fmt::json::FanoutFormatter& telemetry_formatter,
fmt::json::FanoutFormatter& registration_formatter,
time_t start_point);

private:
std::unique_ptr<fmt::json::IFormatter> formatter_;
std::unique_ptr<TimeHandler> handler_;
};

} // namespace httpserver
} // namespace pipeline
} // namespace ocs
6 changes: 0 additions & 6 deletions components/ocs_pipeline/jsonfmt/data_pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

#include "freertos/FreeRTOSConfig.h"

#include "ocs_fmt/json/time_formatter.h"
#include "ocs_pipeline/jsonfmt/data_pipeline.h"

namespace ocs {
Expand All @@ -20,16 +19,11 @@ DataPipeline::DataPipeline(core::IClock& clock,
scheduler::ITaskScheduler& task_scheduler,
system::FanoutRebootHandler& reboot_handler,
const system::DeviceInfo& device_info) {
time_formatter_.reset(new (std::nothrow) fmt::json::TimeFormatter());
configASSERT(time_formatter_);

telemetry_formatter_.reset(new (std::nothrow) TelemetryFormatter());
configASSERT(telemetry_formatter_);
telemetry_formatter_->get_fanout_formatter().add(*time_formatter_);

registration_formatter_.reset(new (std::nothrow) RegistrationFormatter(device_info));
configASSERT(registration_formatter_);
registration_formatter_->get_fanout_formatter().add(*time_formatter_);

system_counter_storage_ = storage_builder.make("system_counter");
configASSERT(system_counter_storage_);
Expand Down
Loading

0 comments on commit 3724f8b

Please sign in to comment.