1 /**
2 * Copyright © 2021 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "count_state_floor.hpp"
17
18 #include "../manager.hpp"
19 #include "../zone.hpp"
20 #include "action.hpp"
21 #include "group.hpp"
22 #include "sdeventplus.hpp"
23
24 namespace phosphor::fan::control::json
25 {
26
CountStateFloor(const json & jsonObj,const std::vector<Group> & groups)27 CountStateFloor::CountStateFloor(const json& jsonObj,
28 const std::vector<Group>& groups) :
29 ActionBase(jsonObj, groups)
30 {
31 setCount(jsonObj);
32 setState(jsonObj);
33 setFloor(jsonObj);
34 setDelayTime(jsonObj);
35 }
36
run(Zone & zone)37 void CountStateFloor::run(Zone& zone)
38 {
39 auto countReached = doCount();
40
41 if (_delayTime == std::chrono::seconds::zero())
42 {
43 // If no delay time configured, can immediately update the hold.
44 zone.setFloorHold(getUniqueName(), _floor, countReached);
45 return;
46 }
47
48 if (!countReached)
49 {
50 if (_timer && _timer->isEnabled())
51 {
52 record("Stopping delay timer");
53 _timer->setEnabled(false);
54 }
55
56 zone.setFloorHold(getUniqueName(), _floor, countReached);
57 return;
58 }
59
60 // The count has been reached and a delay is configured, so either:
61 // 1. This hold has already been set, so don't need to do anything else.
62 // 2. The timer hasn't been started yet, so start it (May need to create
63 // it first).
64 // 3. The timer is already running, don't need to do anything else.
65 // When the timer expires, then count again and set the hold.
66
67 if (zone.hasFloorHold(getUniqueName()))
68 {
69 return;
70 }
71
72 if (!_timer)
73 {
74 _timer = std::make_unique<Timer>(
75 util::SDEventPlus::getEvent(), [&zone, this](Timer&) {
76 zone.setFloorHold(getUniqueName(), _floor, doCount());
77 });
78 }
79
80 if (!_timer->isEnabled())
81 {
82 record("Starting delay timer");
83 _timer->restartOnce(_delayTime);
84 }
85 }
86
doCount()87 bool CountStateFloor::doCount()
88 {
89 size_t numAtState = 0;
90
91 for (const auto& group : _groups)
92 {
93 for (const auto& member : group.getMembers())
94 {
95 try
96 {
97 if (Manager::getObjValueVariant(member, group.getInterface(),
98 group.getProperty()) == _state)
99 {
100 numAtState++;
101 if (numAtState >= _count)
102 {
103 return true;
104 }
105 }
106 }
107 catch (const std::out_of_range& oore)
108 {
109 // Default to property not equal when not found
110 }
111 }
112 }
113
114 return false;
115 }
116
setCount(const json & jsonObj)117 void CountStateFloor::setCount(const json& jsonObj)
118 {
119 if (!jsonObj.contains("count"))
120 {
121 throw ActionParseError{ActionBase::getName(),
122 "Missing required count value"};
123 }
124 _count = jsonObj["count"].get<size_t>();
125 }
126
setState(const json & jsonObj)127 void CountStateFloor::setState(const json& jsonObj)
128 {
129 if (!jsonObj.contains("state"))
130 {
131 throw ActionParseError{ActionBase::getName(),
132 "Missing required state value"};
133 }
134 _state = getJsonValue(jsonObj["state"]);
135 }
136
setFloor(const json & jsonObj)137 void CountStateFloor::setFloor(const json& jsonObj)
138 {
139 if (!jsonObj.contains("floor"))
140 {
141 throw ActionParseError{ActionBase::getName(),
142 "Missing required floor value"};
143 }
144 _floor = jsonObj["floor"].get<uint64_t>();
145 }
146
setDelayTime(const json & jsonObj)147 void CountStateFloor::setDelayTime(const json& jsonObj)
148 {
149 if (jsonObj.contains("delay"))
150 {
151 _delayTime = std::chrono::seconds(jsonObj["delay"].get<size_t>());
152 }
153 }
154
155 } // namespace phosphor::fan::control::json
156