1 #include "numeric_threshold.hpp"
2 
3 #include <phosphor-logging/log.hpp>
4 
5 NumericThreshold::NumericThreshold(
6     boost::asio::io_context& ioc, const std::string& triggerIdIn,
7     Sensors sensorsIn,
8     std::vector<std::unique_ptr<interfaces::TriggerAction>> actionsIn,
9     Milliseconds dwellTimeIn, numeric::Direction directionIn,
10     double thresholdValueIn, numeric::Type typeIn,
11     std::unique_ptr<interfaces::Clock> clockIn) :
12     ioc(ioc),
13     triggerId(triggerIdIn), actions(std::move(actionsIn)),
14     dwellTime(dwellTimeIn), direction(directionIn),
15     thresholdValue(thresholdValueIn), type(typeIn), clock(std::move(clockIn))
16 {
17     for (const auto& sensor : sensorsIn)
18     {
19         sensorDetails.emplace(sensor, makeDetails(sensor->getName()));
20     }
21 }
22 
23 void NumericThreshold::initialize()
24 {
25     ThresholdOperations::initialize(this);
26 }
27 
28 void NumericThreshold::updateSensors(Sensors newSensors)
29 {
30     ThresholdOperations::updateSensors(this, std::move(newSensors));
31 }
32 
33 NumericThreshold::ThresholdDetail&
34     NumericThreshold::getDetails(const interfaces::Sensor& sensor)
35 {
36     return ThresholdOperations::getDetails(this, sensor);
37 }
38 
39 std::shared_ptr<NumericThreshold::ThresholdDetail>
40     NumericThreshold::makeDetails(const std::string& sensorName)
41 {
42     return std::make_shared<ThresholdDetail>(sensorName, ioc);
43 }
44 
45 void NumericThreshold::sensorUpdated(interfaces::Sensor& sensor,
46                                      Milliseconds timestamp, double value)
47 {
48     auto& details = getDetails(sensor);
49     auto& prevValue = details.prevValue;
50     auto& prevDirection = details.prevDirection;
51     auto& dwell = details.dwell;
52     auto& timer = details.timer;
53 
54     if (!prevValue)
55     {
56         prevValue = value;
57         return;
58     }
59 
60     bool crossedDecreasing =
61         thresholdValue < prevValue && thresholdValue > value;
62     bool crossedIncreasing =
63         thresholdValue > prevValue && thresholdValue < value;
64 
65     if (!crossedDecreasing && !crossedIncreasing && thresholdValue == prevValue)
66     {
67         crossedDecreasing = prevDirection == numeric::Direction::decreasing &&
68                             thresholdValue > value;
69         crossedIncreasing = prevDirection == numeric::Direction::increasing &&
70                             thresholdValue < value;
71     }
72 
73     if (dwell && (crossedIncreasing || crossedDecreasing))
74     {
75         timer.cancel();
76         dwell = false;
77     }
78     if ((direction == numeric::Direction::decreasing && crossedDecreasing) ||
79         (direction == numeric::Direction::increasing && crossedIncreasing) ||
80         (direction == numeric::Direction::either &&
81          (crossedIncreasing || crossedDecreasing)))
82     {
83         startTimer(details, value);
84     }
85 
86     prevDirection = value > prevValue   ? numeric::Direction::increasing
87                     : value < prevValue ? numeric::Direction::decreasing
88                                         : numeric::Direction::either;
89     prevValue = value;
90 }
91 
92 void NumericThreshold::startTimer(NumericThreshold::ThresholdDetail& details,
93                                   double value)
94 {
95     auto& sensorName = details.getSensorName();
96     auto& dwell = details.dwell;
97     auto& timer = details.timer;
98 
99     if (dwellTime == Milliseconds::zero())
100     {
101         commit(sensorName, value);
102     }
103     else
104     {
105         dwell = true;
106         timer.expires_after(dwellTime);
107         timer.async_wait([this, &sensorName, &dwell,
108                           value](const boost::system::error_code ec) {
109             if (ec)
110             {
111                 phosphor::logging::log<phosphor::logging::level::DEBUG>(
112                     "Timer has been canceled");
113                 return;
114             }
115             commit(sensorName, value);
116             dwell = false;
117         });
118     }
119 }
120 
121 void NumericThreshold::commit(const std::string& sensorName, double value)
122 {
123     Milliseconds timestamp = clock->systemTimestamp();
124     for (const auto& action : actions)
125     {
126         action->commit(triggerId, std::nullopt, sensorName, timestamp, value);
127     }
128 }
129 
130 LabeledThresholdParam NumericThreshold::getThresholdParam() const
131 {
132     return numeric::LabeledThresholdParam(type, dwellTime.count(), direction,
133                                           thresholdValue);
134 }
135