blob: bc013e5b8883eea46d32461ba7925af94970dffb [file] [log] [blame]
#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;
}