| #include "tlbmc/collector/gpio_collector.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/log/log.h" |
| #include "absl/memory/memory.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "g3/macros.h" |
| #include "thread/thread.h" |
| #include "nlohmann/json.hpp" |
| #include "gpio_config.pb.h" |
| #include "tlbmc/hal/gpio/gpio.h" |
| #include "tlbmc/hal/gpio/gpio_monitor.h" |
| |
| namespace milotic_tlbmc { |
| |
| absl::Status GpioCollector::SetGpio(absl::string_view gpio_name, bool value) { |
| auto it = output_gpios_.find(gpio_name); |
| if (it == output_gpios_.end()) { |
| return absl::NotFoundError(absl::StrCat("GPIO not found: ", gpio_name)); |
| } |
| return it->second->SetValue(value); |
| } |
| |
| absl::StatusOr<std::unique_ptr<GpioCollector>> GpioCollector::Create( |
| const Params& params) { |
| std::shared_ptr<boost::asio::io_context> io_context = |
| std::make_shared<boost::asio::io_context>(); |
| boost::asio::executor_work_guard<boost::asio::io_context::executor_type> |
| work_guard(boost::asio::make_work_guard(*io_context)); |
| |
| absl::flat_hash_map<std::string, std::unique_ptr<Gpio>> input_gpios; |
| absl::flat_hash_map<std::string, std::unique_ptr<Gpio>> output_gpios; |
| absl::flat_hash_map<std::string, std::unique_ptr<GpioMonitor>> gpio_monitors; |
| for (const auto& gpio_config : params.gpio_configs.gpio_configs()) { |
| switch (gpio_config.request_type_case()) { |
| case GpioConfig::kGpioDirection: { |
| ECCLESIA_ASSIGN_OR_RETURN(std::unique_ptr<Gpio> gpio, |
| Gpio::Create(gpio_config)); |
| switch (gpio_config.gpio_direction()) { |
| case GPIO_DIRECTION_INPUT: |
| input_gpios[gpio->GetGpioName()] = std::move(gpio); |
| break; |
| case GPIO_DIRECTION_OUTPUT: |
| output_gpios[gpio->GetGpioName()] = std::move(gpio); |
| break; |
| default: |
| return absl::InvalidArgumentError(absl::StrCat( |
| "Unsupported GPIO direction: ", gpio_config.gpio_direction())); |
| } |
| break; |
| } |
| case GpioConfig::kGpioEventConfig: { |
| ECCLESIA_ASSIGN_OR_RETURN(std::unique_ptr<GpioMonitor> gpio_monitor, |
| GpioMonitor::Create(gpio_config, io_context)); |
| gpio_monitors[gpio_monitor->GetGpioName()] = std::move(gpio_monitor); |
| break; |
| } |
| default: |
| return absl::InvalidArgumentError( |
| absl::StrCat("Unsupported GPIO request type: ", |
| gpio_config.request_type_case())); |
| } |
| } |
| |
| // io_context should start running after all gpio_monitors are created. |
| auto thread_manager = std::make_unique<GpioThreadManager>(params.clock); |
| thread_manager->threads.push_back( |
| params.thread_factory->New([io_context]() { io_context->run(); })); |
| thread_manager->work_guards.push_back(std::move(work_guard)); |
| thread_manager->io_contexts.push_back(std::move(io_context)); |
| |
| return absl::WrapUnique( |
| new GpioCollector(std::move(thread_manager), std::move(input_gpios), |
| std::move(output_gpios), std::move(gpio_monitors))); |
| } |
| |
| nlohmann::json GpioCollector::ToJson() const { |
| nlohmann::json::object_t response; |
| nlohmann::json::array_t input_gpios; |
| nlohmann::json::array_t output_gpios; |
| nlohmann::json::array_t monitored_gpios; |
| |
| for (const auto& [name, input_gpio] : input_gpios_) { |
| nlohmann::json::object_t gpio_json; |
| absl::StatusOr<bool> gpio_value = input_gpio->GetValue(); |
| |
| gpio_json["Name"] = name; |
| if (gpio_value.ok()) { |
| gpio_json["Value"] = *gpio_value; |
| } else { |
| LOG(ERROR) << "Failed to get GPIO value for " << name << " : " |
| << gpio_value.status(); |
| gpio_json["Value"] = nullptr; |
| } |
| input_gpios.push_back(gpio_json); |
| } |
| |
| for (const auto& [name, output_gpio] : output_gpios_) { |
| nlohmann::json::object_t gpio_json; |
| gpio_json["Name"] = name; |
| output_gpios.push_back(gpio_json); |
| } |
| |
| for (const auto& [name, gpio_monitor] : gpio_monitors_) { |
| nlohmann::json::object_t gpio_json; |
| absl::StatusOr<bool> gpio_value = gpio_monitor->GetGpioValue(); |
| |
| gpio_json["Name"] = name; |
| if (gpio_value.ok()) { |
| gpio_json["Value"] = *gpio_value; |
| } else { |
| LOG(ERROR) << "Failed to get GPIO value for " << name << " : " |
| << gpio_value.status(); |
| gpio_json["Value"] = nullptr; |
| } |
| monitored_gpios.push_back(gpio_json); |
| } |
| |
| response["Input"] = input_gpios; |
| response["Output"] = output_gpios; |
| response["Monitoring"] = monitored_gpios; |
| |
| return response; |
| } |
| |
| nlohmann::json EmptyGpioCollector::GetSchedulerStats() const { |
| return nlohmann::json::parse("{\"Warning\": \"EmptyGpioCollector used.\"}"); |
| } |
| |
| nlohmann::json EmptyGpioCollector::ToJson() const { |
| return nlohmann::json::parse("{\"Warning\": \"EmptyGpioCollector used.\"}"); |
| } |
| |
| std::unique_ptr<EmptyGpioCollector> EmptyGpioCollector::Create() { |
| return std::make_unique<EmptyGpioCollector>(); |
| } |
| |
| GpioThreadManager::~GpioThreadManager() { |
| // Stop the scheduler first. |
| task_scheduler->Stop(); |
| |
| // Finally join all threads. |
| for (const std::shared_ptr<boost::asio::io_context>& io_context : |
| io_contexts) { |
| io_context->stop(); |
| } |
| |
| for (const std::unique_ptr<ecclesia::ThreadInterface>& thread : threads) { |
| thread->Join(); |
| } |
| } |
| |
| } // namespace milotic_tlbmc |