blob: b18a07146583f5a0b4477e0677889e2e7d5c8a63 [file] [log] [blame]
Tomáš Pecka339bc672020-11-11 15:59:03 +01001/*
2 * Copyright (C) 2016-2020 CESNET, https://photonics.cesnet.cz/
3 *
4 * Written by Tomáš Pecka <tomas.pecka@fit.cvut.cz>
5 *
6 */
7
8#include <utility>
9#include "IETFHardware.h"
10#include "utils/log.h"
11#include "utils/time.h"
12
13using namespace std::literals;
14
15namespace {
16
Tomáš Pecka83b62e12020-12-16 14:50:49 +010017static const std::string ietfHardwareStatePrefix = "/ietf-hardware:hardware";
Tomáš Pecka339bc672020-11-11 15:59:03 +010018
19/** @brief Constructs a full XPath for a specific component */
20std::string xpathForComponent(const std::string& componentName)
21{
22 return ietfHardwareStatePrefix + "/component[name='" + componentName + "']/";
23}
24
25/** @brief Prefix all properties from values DataTree with a component name (calculated from @p componentName) and push them into the DataTree */
26void addComponent(velia::ietf_hardware::DataTree& res, const std::string& componentName, const std::optional<std::string>& parent, const velia::ietf_hardware::DataTree& values)
27{
28 auto componentPrefix = xpathForComponent(componentName);
29
30 if (parent) {
31 res[componentPrefix + "parent"] = *parent;
32 }
33 for (const auto& [k, v] : values) {
34 res[componentPrefix + k] = v;
35 }
36}
37
38/** @brief Write a sensor-data @p value for a component @p componentName and push it into the @p res DataTree */
39void addSensorValue(velia::ietf_hardware::DataTree& res, const std::string& componentName, const std::string& value)
40{
41 const auto componentPrefix = xpathForComponent(componentName);
42 res[componentPrefix + "sensor-data/value"] = value;
43}
44}
45
46namespace velia::ietf_hardware {
47
48IETFHardware::IETFHardware() = default;
49
50IETFHardware::~IETFHardware() = default;
51
52std::map<std::string, std::string> IETFHardware::process()
53{
54 std::map<std::string, std::string> res;
55
56 for (auto& dataReader : m_callbacks) {
57 res.merge(dataReader());
58 }
59
60 res[ietfHardwareStatePrefix + "/last-change"] = velia::utils::yangTimeFormat(std::chrono::system_clock::now());
61 return res;
62}
63
64void IETFHardware::registerDataReader(const DataReader& callable)
65{
66 m_callbacks.push_back(callable);
67}
68
69/** @brief A namespace containing predefined data readers for IETFHardware class.
70 * @see IETFHardware for more information
71 */
72namespace data_reader {
73
74DataReader::DataReader(std::string componentName, std::optional<std::string> parent)
75 : m_componentName(std::move(componentName))
76 , m_parent(std::move(parent))
77{
78}
79
80/** @brief Constructs a component without any sensor-data and provide only the static data @p dataTree passed via constructor.
81 * @param componentName the name of the component in the resulting tree
82 * @param parent The component in YANG model has a link to parent. Specify who is the parent.
83 * @param dataTree static data to insert into the resulting tree. The dataTree keys should only contain the YANG node name, not full XPath. The full XPath is constructed from @componentName and the map key.
84 */
85StaticData::StaticData(std::string componentName, std::optional<std::string> parent, DataTree dataTree)
86 : DataReader(std::move(componentName), std::move(parent))
87{
88 addComponent(m_staticData,
89 m_componentName,
90 m_parent,
91 dataTree);
92}
93
94DataTree StaticData::operator()() const { return m_staticData; }
95
96Fans::Fans(std::string componentName, std::optional<std::string> parent, std::shared_ptr<sysfs::HWMon> hwmon, unsigned fanChannelsCount)
97 : DataReader(std::move(componentName), std::move(parent))
98 , m_hwmon(std::move(hwmon))
99 , m_fanChannelsCount(fanChannelsCount)
100{
101 // fans
102 addComponent(m_staticData,
103 m_componentName,
104 m_parent,
105 DataTree {
106 {"class", "iana-hardware:module"}, // FIXME: Read (or pass via constructor) additional properties (mfg, model, ...). They should be in the fans' tray EEPROM.
107 });
108
109 for (unsigned i = 1; i <= m_fanChannelsCount; i++) {
110 // fans -> fan_i
111 addComponent(m_staticData,
112 m_componentName + ":fan" + std::to_string(i),
113 m_componentName,
114 DataTree {
115 {"class", "iana-hardware:fan"},
116 });
117
118 // fans -> fan_i -> sensor-data
119 addComponent(m_staticData,
120 m_componentName + ":fan" + std::to_string(i) + ":rpm",
121 m_componentName + ":fan" + std::to_string(i),
122 DataTree {
123 {"class", "iana-hardware:sensor"},
124 {"sensor-data/value-type", "rpm"},
125 {"sensor-data/value-scale", "units"},
126 {"sensor-data/value-precision", "0"},
127 {"sensor-data/oper-status", "ok"},
128 });
129 }
130}
131
132DataTree Fans::operator()() const
133{
134 DataTree res(m_staticData);
135
Tomáš Pecka339bc672020-11-11 15:59:03 +0100136 for (unsigned i = 1; i <= m_fanChannelsCount; i++) {
137 const auto sensorComponentName = m_componentName + ":fan" + std::to_string(i) + ":rpm";
138 const auto attribute = "fan"s + std::to_string(i) + "_input";
139
Václav Kubernátb0939dd2021-04-28 04:08:48 +0200140 addSensorValue(res, sensorComponentName, std::to_string(m_hwmon->attribute(attribute)));
Tomáš Pecka339bc672020-11-11 15:59:03 +0100141 }
142
143 return res;
144}
145
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200146std::string getSysfsFilename(const SensorType type, int sysfsChannelNr)
147{
148 switch (type) {
149 case SensorType::Temperature:
150 return "temp"s + std::to_string(sysfsChannelNr) + "_input";
Václav Kubernát97e5ea12021-03-24 00:36:57 +0100151 case SensorType::Current:
152 return "curr"s + std::to_string(sysfsChannelNr) + "_input";
153 case SensorType::Power:
154 return "power"s + std::to_string(sysfsChannelNr) + "_input";
155 case SensorType::VoltageAC:
156 case SensorType::VoltageDC:
157 return "in"s + std::to_string(sysfsChannelNr) + "_input";
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200158 }
159
160 __builtin_unreachable();
161}
162
163template <SensorType TYPE> const DataTree sysfsStaticData;
164template <> const DataTree sysfsStaticData<SensorType::Temperature> = {
165 {"class", "iana-hardware:sensor"},
166 {"sensor-data/value-type", "celsius"},
167 {"sensor-data/value-scale", "milli"},
168 {"sensor-data/value-precision", "0"},
169 {"sensor-data/oper-status", "ok"},
170};
Václav Kubernát97e5ea12021-03-24 00:36:57 +0100171template <> const DataTree sysfsStaticData<SensorType::Current> = {
172 {"class", "iana-hardware:sensor"},
173 {"sensor-data/value-type", "amperes"},
174 {"sensor-data/value-scale", "milli"},
175 {"sensor-data/value-precision", "0"},
176 {"sensor-data/oper-status", "ok"},
177};
178template <> const DataTree sysfsStaticData<SensorType::Power> = {
179 {"class", "iana-hardware:sensor"},
180 {"sensor-data/value-type", "watts"},
181 {"sensor-data/value-scale", "micro"},
182 {"sensor-data/value-precision", "0"},
183 {"sensor-data/oper-status", "ok"},
184};
185template <> const DataTree sysfsStaticData<SensorType::VoltageAC> = {
186 {"class", "iana-hardware:sensor"},
187 {"sensor-data/value-type", "volts-AC"},
188 {"sensor-data/value-scale", "micro"},
189 {"sensor-data/value-precision", "0"},
190 {"sensor-data/oper-status", "ok"}
191};
192template <> const DataTree sysfsStaticData<SensorType::VoltageDC> = {
193 {"class", "iana-hardware:sensor"},
194 {"sensor-data/value-type", "volts-DC"},
195 {"sensor-data/value-scale", "micro"},
196 {"sensor-data/value-precision", "0"},
197 {"sensor-data/oper-status", "ok"}
198};
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200199
200template <SensorType TYPE>
201SysfsValue<TYPE>::SysfsValue(std::string componentName, std::optional<std::string> parent, std::shared_ptr<sysfs::HWMon> hwmon, int sysfsChannelNr)
Tomáš Pecka339bc672020-11-11 15:59:03 +0100202 : DataReader(std::move(componentName), std::move(parent))
203 , m_hwmon(std::move(hwmon))
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200204 , m_sysfsFile(getSysfsFilename(TYPE, sysfsChannelNr))
Tomáš Pecka339bc672020-11-11 15:59:03 +0100205{
206 addComponent(m_staticData,
207 m_componentName,
208 m_parent,
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200209 sysfsStaticData<TYPE>);
Tomáš Pecka339bc672020-11-11 15:59:03 +0100210}
211
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200212template <SensorType TYPE>
213DataTree SysfsValue<TYPE>::operator()() const
Tomáš Pecka339bc672020-11-11 15:59:03 +0100214{
215 DataTree res(m_staticData);
216
Václav Kubernát0dee6b92021-04-13 09:14:04 +0200217 int64_t sensorValue = m_hwmon->attribute(m_sysfsFile);
Tomáš Pecka339bc672020-11-11 15:59:03 +0100218 addSensorValue(res, m_componentName, std::to_string(sensorValue));
219
220 return res;
221}
222
Václav Kubernát97e5ea12021-03-24 00:36:57 +0100223template struct SysfsValue<SensorType::Current>;
224template struct SysfsValue<SensorType::Power>;
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200225template struct SysfsValue<SensorType::Temperature>;
Václav Kubernát97e5ea12021-03-24 00:36:57 +0100226template struct SysfsValue<SensorType::VoltageAC>;
227template struct SysfsValue<SensorType::VoltageDC>;
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200228
Tomáš Pecka339bc672020-11-11 15:59:03 +0100229EMMC::EMMC(std::string componentName, std::optional<std::string> parent, std::shared_ptr<sysfs::EMMC> emmc)
230 : DataReader(std::move(componentName), std::move(parent))
231 , m_emmc(std::move(emmc))
232{
233 auto emmcAttrs = m_emmc->attributes();
234
235 // date is specified in MM/YYYY format (source: kernel core/mmc.c) and mfg-date is unfortunately of type yang:date-and-time
236 std::string mfgDate = emmcAttrs.at("date");
237 mfgDate = mfgDate.substr(3, 4) + "-" + mfgDate.substr(0, 2) + "-01T00:00:00Z";
238
239 addComponent(m_staticData,
240 m_componentName,
241 m_parent,
242 DataTree {
243 {"class", "iana-hardware:module"},
244 {"mfg-date", mfgDate},
245 {"serial-num", emmcAttrs.at("serial")},
246 {"model-name", emmcAttrs.at("name")},
247 });
248
249 addComponent(m_staticData,
250 m_componentName + ":lifetime",
251 m_componentName,
252 DataTree {
253 {"class", "iana-hardware:sensor"},
254 {"sensor-data/value-type", "other"},
255 {"sensor-data/value-scale", "units"},
256 {"sensor-data/value-precision", "0"},
257 {"sensor-data/oper-status", "ok"},
258 {"sensor-data/units-display", "percent"s},
259 });
260}
261
262DataTree EMMC::operator()() const
263{
264 DataTree res(m_staticData);
265
266 auto emmcAttrs = m_emmc->attributes();
267 addSensorValue(res, m_componentName + ":lifetime", emmcAttrs.at("life_time"));
268
269 return res;
270}
271}
272}