| #pragma once |
| #include <boost/algorithm/string/join.hpp> |
| #include <boost/asio.hpp> |
| |
| #include <filesystem> |
| #include <iostream> |
| #include <optional> |
| #include <system_error> |
| |
| namespace nvme |
| { |
| static constexpr const char* sensorType = "NVME1000"; |
| } // namespace nvme |
| |
| inline std::filesystem::path deriveRootBusPath(int busNumber) |
| { |
| return "/sys/bus/i2c/devices/i2c-" + std::to_string(busNumber) + |
| "/mux_device"; |
| } |
| |
| inline std::optional<int> deriveRootBus(std::optional<int> busNumber) |
| { |
| if (!busNumber) |
| { |
| return std::nullopt; |
| } |
| |
| std::filesystem::path muxPath = deriveRootBusPath(*busNumber); |
| |
| if (!std::filesystem::is_symlink(muxPath)) |
| { |
| return *busNumber; |
| } |
| |
| std::string rootName = std::filesystem::read_symlink(muxPath).filename(); |
| size_t dash = rootName.find('-'); |
| if (dash == std::string::npos) |
| { |
| std::cerr << "Error finding root bus for " << rootName << "\n"; |
| return std::nullopt; |
| } |
| |
| return std::stoi(rootName.substr(0, dash)); |
| } |
| |
| inline std::optional<std::string> |
| extractOneFromTail(std::string::const_reverse_iterator& rbegin, |
| const std::string::const_reverse_iterator& rend) |
| { |
| std::string name; |
| auto curr = rbegin; |
| // remove the ending '/'s |
| while (rbegin != rend && *rbegin == '/') |
| { |
| rbegin++; |
| } |
| if (rbegin == rend) |
| { |
| return std::nullopt; |
| } |
| curr = rbegin++; |
| |
| // extract word |
| while (rbegin != rend && *rbegin != '/') |
| { |
| rbegin++; |
| } |
| if (rbegin == rend) |
| { |
| return std::nullopt; |
| } |
| name.append(rbegin.base(), curr.base()); |
| return {name}; |
| } |
| |
| // a path of |
| // "/xyz/openbmc_project/inventory/system/board/{prod}/{nvme}/{substruct}..." |
| // will generates a sensor name {prod}_{nvme}_{substruct}... |
| inline std::optional<std::string> |
| createSensorNameFromPath(const std::string& path) |
| { |
| if (path.empty()) |
| { |
| return std::nullopt; |
| } |
| auto rbegin = path.crbegin(); |
| std::vector<std::string> names; |
| do |
| { |
| auto name = extractOneFromTail(rbegin, path.crend()); |
| if (!name) |
| { |
| return std::nullopt; |
| } |
| if (*name == "board") |
| { |
| break; |
| } |
| |
| if (!names.empty() && names.back().starts_with(*name)) |
| { |
| // Skip potential duplicate prefix |
| continue; |
| } |
| names.push_back(*name); |
| } while (rbegin != path.rend()); |
| |
| std::reverse(names.begin(), names.end()); |
| auto sensorName = boost::algorithm::join(names, "_"); |
| return sensorName; |
| } |
| |
| // Function to update NVMe temp sensor |
| |
| // Function type for fetching ctemp which encaplucated in a structure of T. |
| // The fetcher function take a callback as input to process the result. |
| template <class T> |
| using ctemp_fetch_t = |
| std::function<void(std::function<void(const std::error_code&, T)>&&)>; |
| |
| // Function type for processing ctemp out the structure of type T. |
| // The process function will update the properties based on input data. |
| template <class T> |
| using ctemp_process_t = |
| std::function<void(const std::error_code& error, T data)>; |
| |
| template <class T> |
| void pollCtemp( |
| const std::weak_ptr<boost::asio::steady_timer>& timer, |
| std::chrono::duration<double, std::milli> delay, |
| const std::function<void(std::function<void(const std::error_code&, T)>&&)>& |
| dataFetcher, |
| const std::function<void(const std::error_code& error, T data)>& |
| dataProcessor); |
| |
| namespace detail |
| { |
| |
| template <class T> |
| void updateCtemp(std::weak_ptr<boost::asio::steady_timer> timer, |
| std::chrono::duration<double, std::milli> delay, |
| ctemp_process_t<T> dataProcessor, ctemp_fetch_t<T> dataFetcher, |
| const std::error_code& error, T data) |
| { |
| if (timer.expired()) |
| { |
| dataProcessor(std::make_error_code(std::errc::operation_canceled), |
| data); |
| return; |
| } |
| |
| dataProcessor(error, data); |
| ::pollCtemp(std::move(timer), delay, dataFetcher, dataProcessor); |
| } |
| |
| template <class T> |
| void pollCtemp(std::weak_ptr<boost::asio::steady_timer> timer, |
| std::chrono::duration<double, std::milli> delay, |
| ctemp_fetch_t<T> dataFetcher, ctemp_process_t<T> dataProcessor, |
| const boost::system::error_code errorCode) |
| { |
| if (errorCode == boost::asio::error::operation_aborted || timer.expired()) |
| { |
| std::cerr << "poll loop has been cancelled\n"; |
| return; |
| } |
| if (errorCode) |
| { |
| std::cerr << errorCode.message() << "\n"; |
| ::pollCtemp(std::move(timer), delay, dataFetcher, dataProcessor); |
| return; |
| } |
| |
| dataFetcher(std::bind_front(detail::updateCtemp<T>, std::move(timer), delay, |
| dataProcessor, dataFetcher)); |
| } |
| |
| } // namespace detail |
| |
| template <class T> |
| void pollCtemp( |
| const std::weak_ptr<boost::asio::steady_timer>& weakTimer, |
| std::chrono::duration<double, std::milli> delay, |
| const std::function<void(std::function<void(const std::error_code&, T)>&&)>& |
| dataFetcher, |
| const std::function<void(const std::error_code& error, T data)>& |
| dataProcessor) |
| { |
| auto timer = weakTimer.lock(); |
| if (!timer) |
| { |
| return; |
| } |
| timer->expires_from_now( |
| std::chrono::duration_cast<std::chrono::milliseconds>(delay)); |
| timer->async_wait(std::bind_front(detail::pollCtemp<T>, timer, delay, |
| dataFetcher, dataProcessor)); |
| } |
| |
| // Strips space padding from a NVMe string, replaces invalid characters with |
| // spaces. |
| static inline std::string nvmeString(const char* data, size_t size) |
| { |
| std::string s(data, size); |
| |
| size_t n = s.find_last_not_of(' '); |
| if (n == std::string::npos) |
| { |
| s.clear(); |
| } |
| else |
| { |
| s.resize(n + 1); |
| } |
| |
| for (auto& c : s) |
| { |
| if (c < 0x20 || c > 0x7e) |
| { |
| c = ' '; |
| } |
| } |
| return s; |
| } |
| |
| static inline uint64_t char128ToUint64(const unsigned char* data) |
| { |
| int i = 0; |
| uint64_t result = 0; |
| |
| for (i = 0; i < 16; i++) |
| { |
| result *= 256; |
| result += data[15 - i]; |
| } |
| return result; |
| } |