blob: c2a037164b683505eb56ba54fa246b2e72ca6548 [file] [log] [blame]
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +02001#include "report.hpp"
2
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +01003#include "messages/collect_trigger_id.hpp"
4#include "messages/trigger_presence_changed_ind.hpp"
5#include "messages/update_report_ind.hpp"
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +02006#include "report_manager.hpp"
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +01007#include "utils/clock.hpp"
Krzysztof Grobelny51497a02021-11-09 14:56:22 +01008#include "utils/contains.hpp"
Szymon Dompke1cdd7e42022-06-08 14:43:13 +02009#include "utils/dbus_path_utils.hpp"
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +010010#include "utils/ensure.hpp"
Wludzik, Jozefe2362792020-10-27 17:23:55 +010011#include "utils/transform.hpp"
12
13#include <phosphor-logging/log.hpp>
Wludzik, Jozefb1ff1f62020-10-23 13:20:52 +020014#include <sdbusplus/vtable.hpp>
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020015
Szymon Dompke3eb56862021-09-20 15:32:04 +020016#include <limits>
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020017#include <numeric>
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +010018#include <optional>
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020019
20Report::Report(boost::asio::io_context& ioc,
21 const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010022 const std::string& reportId, const std::string& reportName,
Szymon Dompke3eb56862021-09-20 15:32:04 +020023 const ReportingType reportingTypeIn,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +010024 std::vector<ReportAction> reportActionsIn,
Szymon Dompke3eb56862021-09-20 15:32:04 +020025 const Milliseconds intervalIn, const uint64_t appendLimitIn,
26 const ReportUpdates reportUpdatesIn,
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +020027 interfaces::ReportManager& reportManager,
Wludzik, Jozefe2362792020-10-27 17:23:55 +010028 interfaces::JsonStorage& reportStorageIn,
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +020029 std::vector<std::shared_ptr<interfaces::Metric>> metricsIn,
Szymon Dompkefdb06a12022-02-11 11:04:44 +010030 const interfaces::ReportFactory& reportFactory,
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +010031 const bool enabledIn, std::unique_ptr<interfaces::Clock> clock,
32 Readings readingsIn) :
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010033 id(reportId),
Szymon Dompke1cdd7e42022-06-08 14:43:13 +020034 path(utils::pathAppend(utils::constants::reportDirPath, id)),
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010035 name(reportName), reportingType(reportingTypeIn), interval(intervalIn),
Szymon Dompkefdb06a12022-02-11 11:04:44 +010036 reportActions(reportActionsIn.begin(), reportActionsIn.end()),
Szymon Dompke3eb56862021-09-20 15:32:04 +020037 sensorCount(getSensorCount(metricsIn)),
38 appendLimit(deduceAppendLimit(appendLimitIn)),
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +010039 reportUpdates(reportUpdatesIn), readings(std::move(readingsIn)),
40 readingsBuffer(std::get<1>(readings),
41 deduceBufferSize(reportUpdates, reportingType)),
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000042 objServer(objServer), metrics(std::move(metricsIn)), timer(ioc),
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +010043 triggerIds(collectTriggerIds(ioc)), reportStorage(reportStorageIn),
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +020044 clock(std::move(clock)), messanger(ioc)
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +020045{
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000046 readingParameters =
47 toReadingParameters(utils::transform(metrics, [](const auto& metric) {
48 return metric->dumpConfiguration();
49 }));
50
51 readingParametersPastVersion =
52 utils::transform(readingParameters, [](const auto& item) {
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010053 const auto& [sensorData, operationType, id, collectionTimeScope,
54 collectionDuration] = item;
55
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000056 return ReadingParametersPastVersion::value_type(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010057 std::get<0>(sensorData.front()), operationType, id,
58 std::get<1>(sensorData.front()));
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +000059 });
60
Szymon Dompkefdb06a12022-02-11 11:04:44 +010061 reportActions.insert(ReportAction::logToMetricReportsCollection);
62
Wludzik, Jozefe2362792020-10-27 17:23:55 +010063 deleteIface = objServer->add_unique_interface(
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010064 getPath(), deleteIfaceName,
65 [this, &ioc, &reportManager](auto& dbusIface) {
Wludzik, Jozefe2362792020-10-27 17:23:55 +010066 dbusIface.register_method("Delete", [this, &ioc, &reportManager] {
67 if (persistency)
68 {
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +010069 persistency = false;
70
71 reportIface->signal_property("Persistency");
Wludzik, Jozefe2362792020-10-27 17:23:55 +010072 }
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +010073
Wludzik, Jozefe2362792020-10-27 17:23:55 +010074 boost::asio::post(ioc, [this, &reportManager] {
75 reportManager.removeReport(this);
76 });
77 });
78 });
79
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +020080 errorMessages = verify();
81 state.set<ReportFlags::enabled, ReportFlags::valid>(enabledIn,
82 errorMessages.empty());
83
Szymon Dompkefdb06a12022-02-11 11:04:44 +010084 reportIface = makeReportInterface(reportFactory);
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +020085 persistency = storeConfiguration();
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +010086
87 messanger.on_receive<messages::TriggerPresenceChangedInd>(
88 [this](const auto& msg) {
89 const auto oldSize = triggerIds.size();
90
91 if (msg.presence == messages::Presence::Exist)
92 {
93 if (utils::contains(msg.reportIds, id))
94 {
95 triggerIds.insert(msg.triggerId);
96 }
97 else if (!utils::contains(msg.reportIds, id))
98 {
99 triggerIds.erase(msg.triggerId);
100 }
101 }
102 else if (msg.presence == messages::Presence::Removed)
103 {
104 triggerIds.erase(msg.triggerId);
105 }
106
107 if (triggerIds.size() != oldSize)
108 {
Szymon Dompke1cdd7e42022-06-08 14:43:13 +0200109 reportIface->signal_property("Triggers");
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +0100110 }
111 });
112
113 messanger.on_receive<messages::UpdateReportInd>([this](const auto& msg) {
114 if (utils::contains(msg.reportIds, id))
115 {
116 updateReadings();
117 }
118 });
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200119}
120
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100121Report::~Report()
122{
123 if (persistency)
124 {
125 if (shouldStoreMetricValues())
126 {
127 storeConfiguration();
128 }
129 }
130 else
131 {
132 reportStorage.remove(reportFileName());
133 }
134}
135
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200136void Report::activate()
137{
138 for (auto& metric : this->metrics)
139 {
140 metric->initialize();
141 }
142
143 scheduleTimer();
144
145 if (reportIface)
146 {
147 reportIface->signal_property("errors");
148 }
149}
150
151void Report::deactivate()
152{
153 for (auto& metric : metrics)
154 {
155 metric->deinitialize();
156 }
157
158 unregisterFromMetrics = nullptr;
159 timer.cancel();
160
161 if (reportIface)
162 {
163 reportIface->signal_property("Errors");
164 }
165}
166
Szymon Dompke3eb56862021-09-20 15:32:04 +0200167uint64_t Report::getSensorCount(
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100168 const std::vector<std::shared_ptr<interfaces::Metric>>& metrics)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200169{
170 uint64_t sensorCount = 0;
171 for (auto& metric : metrics)
172 {
173 sensorCount += metric->sensorCount();
174 }
175 return sensorCount;
176}
177
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100178std::optional<uint64_t>
179 Report::deduceAppendLimit(const uint64_t appendLimitIn) const
Szymon Dompke3eb56862021-09-20 15:32:04 +0200180{
181 if (appendLimitIn == std::numeric_limits<uint64_t>::max())
182 {
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100183 return std::nullopt;
Szymon Dompke3eb56862021-09-20 15:32:04 +0200184 }
185 else
186 {
187 return appendLimitIn;
188 }
189}
190
191uint64_t Report::deduceBufferSize(const ReportUpdates reportUpdatesIn,
192 const ReportingType reportingTypeIn) const
193{
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100194 if (reportUpdatesIn == ReportUpdates::overwrite ||
195 reportingTypeIn == ReportingType::onRequest)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200196 {
197 return sensorCount;
198 }
199 else
200 {
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100201 return appendLimit.value_or(sensorCount);
Szymon Dompke3eb56862021-09-20 15:32:04 +0200202 }
203}
204
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100205void Report::setReadingBuffer(const ReportUpdates newReportUpdates)
206{
Krzysztof Grobelnya8182be2022-07-04 11:26:20 +0200207 const auto newBufferSize =
208 deduceBufferSize(newReportUpdates, reportingType);
209 if (readingsBuffer.size() != newBufferSize)
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100210 {
Krzysztof Grobelnya8182be2022-07-04 11:26:20 +0200211 readingsBuffer.clearAndResize(newBufferSize);
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100212 }
213}
214
Szymon Dompke3eb56862021-09-20 15:32:04 +0200215void Report::setReportUpdates(const ReportUpdates newReportUpdates)
216{
217 if (reportUpdates != newReportUpdates)
218 {
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100219 setReadingBuffer(newReportUpdates);
Szymon Dompke3eb56862021-09-20 15:32:04 +0200220 reportUpdates = newReportUpdates;
221 }
222}
223
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100224std::unique_ptr<sdbusplus::asio::dbus_interface>
225 Report::makeReportInterface(const interfaces::ReportFactory& reportFactory)
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000226{
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100227 auto dbusIface =
228 objServer->add_unique_interface(getPath(), reportIfaceName);
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200229 dbusIface->register_property_rw<bool>(
230 "Enabled", sdbusplus::vtable::property_::emits_change,
231 [this](bool newVal, auto& oldValue) {
232 if (newVal != state.get<ReportFlags::enabled>())
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200233 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200234 state.set<ReportFlags::enabled>(oldValue = newVal);
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200235
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200236 persistency = storeConfiguration();
237 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100238 return 1;
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200239 },
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200240 [this](const auto&) { return state.get<ReportFlags::enabled>(); });
241 dbusIface->register_property_r<
242 std::vector<std::tuple<std::string, std::string>>>(
243 "ErrorMessages", sdbusplus::vtable::property_::emits_change,
244 [this](const auto&) {
245 return utils::transform(errorMessages, [](const auto& em) {
246 return std::tuple<std::string, std::string>(
247 utils::enumToString(em.error), em.arg0);
248 });
249 });
250 dbusIface->register_property_rw<uint64_t>(
251 "Interval", sdbusplus::vtable::property_::emits_change,
252 [this](uint64_t newVal, auto& oldVal) {
253 const Milliseconds newValT{newVal};
254 if (newValT < ReportManager::minInterval &&
255 newValT != Milliseconds{0})
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000256 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200257 throw sdbusplus::exception::SdBusError(
258 static_cast<int>(std::errc::invalid_argument),
259 "Invalid interval");
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000260 }
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200261
262 if (newValT != interval)
263 {
264 oldVal = newVal;
265 interval = newValT;
266
267 errorMessages = verify();
268 if (state.set<ReportFlags::valid>(errorMessages.empty()) ==
269 StateEvent::active)
270 {
271 scheduleTimer();
272 }
273
274 persistency = storeConfiguration();
275 }
276 return 1;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000277 },
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000278 [this](const auto&) { return interval.count(); });
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200279 dbusIface->register_property_rw<bool>(
280 "Persistency", sdbusplus::vtable::property_::emits_change,
281 [this](bool newVal, auto& oldVal) {
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000282 if (newVal == persistency)
283 {
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100284 return 1;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000285 }
286 if (newVal)
287 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200288 persistency = oldVal = storeConfiguration();
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000289 }
290 else
291 {
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100292 reportStorage.remove(reportFileName());
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200293 persistency = oldVal = false;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000294 }
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100295 return 1;
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000296 },
297 [this](const auto&) { return persistency; });
298
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100299 dbusIface->register_property_r("Readings", readings,
300 sdbusplus::vtable::property_::emits_change,
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000301 [this](const auto&) { return readings; });
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200302 dbusIface->register_property_rw<std::string>(
303 "ReportingType", sdbusplus::vtable::property_::emits_change,
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100304 [this](auto newVal, auto& oldVal) {
305 ReportingType tmp = utils::toReportingType(newVal);
306 if (tmp != reportingType)
307 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200308 reportingType = tmp;
309 oldVal = std::move(newVal);
310
311 errorMessages = verify();
312 if (state.set<ReportFlags::valid>(errorMessages.empty()) ==
313 StateEvent::active)
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100314 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200315 scheduleTimer();
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100316 }
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100317
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100318 persistency = storeConfiguration();
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200319
320 setReadingBuffer(reportUpdates);
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100321 }
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100322 return 1;
323 },
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100324 [this](const auto&) { return utils::enumToString(reportingType); });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000325 dbusIface->register_property_r(
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000326 "ReadingParameters", readingParametersPastVersion,
327 sdbusplus::vtable::property_::const_,
328 [this](const auto&) { return readingParametersPastVersion; });
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100329 dbusIface->register_property_rw(
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000330 "ReadingParametersFutureVersion", readingParameters,
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100331 sdbusplus::vtable::property_::emits_change,
332 [this, &reportFactory](auto newVal, auto& oldVal) {
Szymon Dompke32305f12022-07-05 15:37:21 +0200333 auto labeledMetricParams =
334 reportFactory.convertMetricParams(newVal);
335 ReportManager::verifyMetricParameters(labeledMetricParams);
336 reportFactory.updateMetrics(metrics,
337 state.get<ReportFlags::enabled>(),
338 labeledMetricParams);
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100339 readingParameters = toReadingParameters(
340 utils::transform(metrics, [](const auto& metric) {
341 return metric->dumpConfiguration();
342 }));
343 persistency = storeConfiguration();
344 oldVal = std::move(newVal);
345 return 1;
346 },
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000347 [this](const auto&) { return readingParameters; });
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200348 dbusIface->register_property_r<bool>(
349 "EmitsReadingsUpdate", sdbusplus::vtable::property_::none,
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100350 [this](const auto&) {
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100351 return reportActions.contains(ReportAction::emitsReadingsUpdate);
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100352 });
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200353 dbusIface->register_property_r<std::string>(
354 "Name", sdbusplus::vtable::property_::const_,
355 [this](const auto&) { return name; });
356 dbusIface->register_property_r<bool>(
357 "LogToMetricReportsCollection", sdbusplus::vtable::property_::const_,
358 [this](const auto&) {
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100359 return reportActions.contains(
360 ReportAction::logToMetricReportsCollection);
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100361 });
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200362 dbusIface->register_property_rw<std::vector<std::string>>(
363 "ReportActions", sdbusplus::vtable::property_::emits_change,
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100364 [this](auto newVal, auto& oldVal) {
365 auto tmp = utils::transform<std::unordered_set>(
366 newVal, [](const auto& reportAction) {
367 return utils::toReportAction(reportAction);
368 });
369 tmp.insert(ReportAction::logToMetricReportsCollection);
370
371 if (tmp != reportActions)
372 {
373 reportActions = tmp;
374 persistency = storeConfiguration();
375 oldVal = std::move(newVal);
376 }
377 return 1;
378 },
379 [this](const auto&) {
380 return utils::transform<std::vector>(
381 reportActions, [](const auto reportAction) {
382 return utils::enumToString(reportAction);
383 });
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100384 });
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100385 dbusIface->register_property_r(
386 "AppendLimit", appendLimit.value_or(sensorCount),
387 sdbusplus::vtable::property_::emits_change,
388 [this](const auto&) { return appendLimit.value_or(sensorCount); });
Szymon Dompke3eb56862021-09-20 15:32:04 +0200389 dbusIface->register_property_rw(
390 "ReportUpdates", std::string(),
391 sdbusplus::vtable::property_::emits_change,
392 [this](auto newVal, auto& oldVal) {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100393 setReportUpdates(utils::toReportUpdates(newVal));
Szymon Dompke3eb56862021-09-20 15:32:04 +0200394 oldVal = newVal;
Szymon Dompke3e2cc9d2021-12-14 12:00:52 +0100395 return 1;
Szymon Dompke3eb56862021-09-20 15:32:04 +0200396 },
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100397 [this](const auto&) { return utils::enumToString(reportUpdates); });
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100398 dbusIface->register_property_r(
Szymon Dompke1cdd7e42022-06-08 14:43:13 +0200399 "Triggers", std::vector<sdbusplus::message::object_path>{},
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100400 sdbusplus::vtable::property_::emits_change, [this](const auto&) {
Szymon Dompke1cdd7e42022-06-08 14:43:13 +0200401 return utils::transform<std::vector>(
402 triggerIds, [](const auto& triggerId) {
403 return utils::pathAppend(utils::constants::triggerDirPath,
404 triggerId);
405 });
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100406 });
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000407 dbusIface->register_method("Update", [this] {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100408 if (reportingType == ReportingType::onRequest)
Krzysztof Grobelny85db8bd2021-05-28 12:13:23 +0000409 {
410 updateReadings();
411 }
412 });
413 constexpr bool skipPropertiesChangedSignal = true;
414 dbusIface->initialize(skipPropertiesChangedSignal);
415 return dbusIface;
416}
417
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100418void Report::timerProcForPeriodicReport(boost::system::error_code ec,
419 Report& self)
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200420{
421 if (ec)
422 {
423 return;
424 }
425
426 self.updateReadings();
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100427 self.scheduleTimerForPeriodicReport(self.interval);
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200428}
429
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100430void Report::timerProcForOnChangeReport(boost::system::error_code ec,
431 Report& self)
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200432{
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100433 if (ec)
434 {
435 return;
436 }
437
438 const auto ensure =
439 utils::Ensure{[&self] { self.onChangeContext = std::nullopt; }};
440
441 self.onChangeContext.emplace(self);
442
443 const auto steadyTimestamp = self.clock->steadyTimestamp();
444
445 for (auto& metric : self.metrics)
446 {
447 metric->updateReadings(steadyTimestamp);
448 }
449
450 self.scheduleTimerForOnChangeReport();
451}
452
453void Report::scheduleTimerForPeriodicReport(Milliseconds timerInterval)
454{
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200455 timer.expires_after(timerInterval);
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100456 timer.async_wait([this](boost::system::error_code ec) {
457 timerProcForPeriodicReport(ec, *this);
458 });
459}
460
461void Report::scheduleTimerForOnChangeReport()
462{
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100463 constexpr Milliseconds timerInterval{100};
464
465 timer.expires_after(timerInterval);
466 timer.async_wait([this](boost::system::error_code ec) {
467 timerProcForOnChangeReport(ec, *this);
468 });
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200469}
470
471void Report::updateReadings()
472{
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200473 if (!state.isActive())
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000474 {
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200475 return;
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000476 }
Krzysztof Grobelnyc8e3a642020-10-23 12:29:16 +0200477
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100478 if (reportUpdates == ReportUpdates::overwrite ||
479 reportingType == ReportingType::onRequest)
Szymon Dompke3eb56862021-09-20 15:32:04 +0200480 {
481 readingsBuffer.clear();
482 }
Krzysztof Grobelnydcc4e192021-03-08 09:09:34 +0000483
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200484 for (const auto& metric : metrics)
485 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200486 if (!state.isActive())
487 {
488 break;
489 }
490
Szymon Dompke3eb56862021-09-20 15:32:04 +0200491 for (const auto& [id, metadata, value, timestamp] :
Krzysztof Grobelny9e8da542022-02-17 10:40:16 +0100492 metric->getUpdatedReadings())
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200493 {
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100494 if (reportUpdates == ReportUpdates::appendStopsWhenFull &&
Szymon Dompke3eb56862021-09-20 15:32:04 +0200495 readingsBuffer.isFull())
496 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200497 state.set<ReportFlags::enabled>(false);
498 reportIface->signal_property("Enabled");
Szymon Dompke3eb56862021-09-20 15:32:04 +0200499 break;
500 }
501 readingsBuffer.emplace(id, metadata, value, timestamp);
Lukasz Kazmierczak7e098e92021-09-16 15:59:56 +0200502 }
503 }
Szymon Dompke3eb56862021-09-20 15:32:04 +0200504
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100505 std::get<0>(readings) =
Krzysztof Grobelny51f0fd52021-12-28 16:32:08 +0100506 std::chrono::duration_cast<Milliseconds>(clock->systemTimestamp())
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100507 .count();
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100508
Szymon Dompkefdb06a12022-02-11 11:04:44 +0100509 if (utils::contains(reportActions, ReportAction::emitsReadingsUpdate))
510 {
511 reportIface->signal_property("Readings");
512 }
Wludzik, Jozefcb88cfd2020-09-28 16:38:57 +0200513}
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100514
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100515bool Report::shouldStoreMetricValues() const
516{
517 return reportingType != ReportingType::onRequest &&
518 reportUpdates == ReportUpdates::appendStopsWhenFull;
519}
520
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100521bool Report::storeConfiguration() const
522{
523 try
524 {
525 nlohmann::json data;
526
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200527 data["Enabled"] = state.get<ReportFlags::enabled>();
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100528 data["Version"] = reportVersion;
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100529 data["Id"] = id;
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100530 data["Name"] = name;
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100531 data["ReportingType"] = utils::toUnderlying(reportingType);
532 data["ReportActions"] =
533 utils::transform(reportActions, [](const auto reportAction) {
534 return utils::toUnderlying(reportAction);
535 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100536 data["Interval"] = interval.count();
Krzysztof Grobelnye6c417c2022-02-02 17:25:53 +0100537 data["AppendLimit"] =
538 appendLimit.value_or(std::numeric_limits<uint64_t>::max());
Krzysztof Grobelny51497a02021-11-09 14:56:22 +0100539 data["ReportUpdates"] = utils::toUnderlying(reportUpdates);
Krzysztof Grobelnyd2238192020-12-02 09:27:28 +0000540 data["ReadingParameters"] =
541 utils::transform(metrics, [](const auto& metric) {
542 return metric->dumpConfiguration();
543 });
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100544
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100545 if (shouldStoreMetricValues())
546 {
547 data["MetricValues"] = utils::toLabeledReadings(readings);
548 }
549
550 reportStorage.store(reportFileName(), data);
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100551 }
552 catch (const std::exception& e)
553 {
554 phosphor::logging::log<phosphor::logging::level::ERR>(
555 "Failed to store a report in storage",
Wludzik, Jozef982c5b52021-01-02 12:05:21 +0100556 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
Wludzik, Jozefe2362792020-10-27 17:23:55 +0100557 return false;
558 }
559
560 return true;
561}
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100562
Krzysztof Grobelny493e62e2022-02-14 10:55:50 +0100563interfaces::JsonStorage::FilePath Report::reportFileName() const
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100564{
565 return interfaces::JsonStorage::FilePath{
566 std::to_string(std::hash<std::string>{}(id))};
567}
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100568
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +0100569std::unordered_set<std::string>
570 Report::collectTriggerIds(boost::asio::io_context& ioc) const
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100571{
Krzysztof Grobelnye6d48872022-02-08 13:41:30 +0100572 utils::Messanger tmp(ioc);
573
574 auto result = std::unordered_set<std::string>();
575
576 tmp.on_receive<messages::CollectTriggerIdResp>(
577 [&result](const auto& msg) { result.insert(msg.triggerId); });
578
579 tmp.send(messages::CollectTriggerIdReq{id});
580
581 return result;
Szymon Dompkeb4ef22e2022-02-07 15:15:12 +0100582}
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100583
584void Report::metricUpdated()
585{
586 if (onChangeContext)
587 {
588 onChangeContext->metricUpdated();
589 return;
590 }
591
592 updateReadings();
593}
594
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200595void Report::scheduleTimer()
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100596{
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100597 switch (reportingType)
598 {
599 case ReportingType::periodic:
600 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200601 unregisterFromMetrics = nullptr;
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100602 scheduleTimerForPeriodicReport(interval);
603 break;
604 }
605 case ReportingType::onChange:
606 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200607 if (!unregisterFromMetrics)
608 {
609 unregisterFromMetrics = [this] {
610 for (auto& metric : metrics)
611 {
612 metric->unregisterFromUpdates(*this);
613 }
614 };
615
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100616 for (auto& metric : metrics)
617 {
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200618 metric->registerForUpdates(*this);
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100619 }
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200620 }
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100621
622 bool isTimerRequired = false;
623
624 for (auto& metric : metrics)
625 {
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100626 if (metric->isTimerRequired())
627 {
628 isTimerRequired = true;
629 }
630 }
631
632 if (isTimerRequired)
633 {
634 scheduleTimerForOnChangeReport();
635 }
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200636 else
637 {
638 timer.cancel();
639 }
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100640 break;
641 }
642 default:
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200643 unregisterFromMetrics = nullptr;
644 timer.cancel();
Krzysztof Grobelnyf7ea2992022-01-27 11:04:58 +0100645 break;
646 }
647}
Krzysztof Grobelny973b4bb2022-04-25 17:07:27 +0200648
649std::vector<ErrorMessage> Report::verify() const
650{
651 std::vector<ErrorMessage> result;
652
653 if ((reportingType == ReportingType::periodic &&
654 interval == Milliseconds{0}) ||
655 (reportingType != ReportingType::periodic &&
656 interval != Milliseconds{0}))
657 {
658 result.emplace_back(ErrorType::propertyConflict, "Interval");
659 result.emplace_back(ErrorType::propertyConflict, "ReportingType");
660 }
661
662 return result;
663}