blob: 7307e7fb7f8e84a489902ae0c7d8005bd9af7231 [file] [log] [blame]
Matthew Barth0c4b1572020-10-22 14:39:46 -05001/**
2 * Copyright © 2020 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#pragma once
17
18#include "types.hpp"
19
20#include <fmt/format.h>
21
22#include <nlohmann/json.hpp>
23#include <phosphor-logging/log.hpp>
24
25#include <functional>
26#include <iterator>
27#include <map>
28#include <memory>
29#include <numeric>
30
31namespace phosphor::fan::control::json
32{
33
34using json = nlohmann::json;
35using namespace phosphor::logging;
36
37/**
38 * @brief Function used in creating action objects
39 *
40 * @param[in] jsonObj - JSON object for the action
41 *
42 * Creates an action object given the JSON configuration
43 */
44template <typename T>
45std::unique_ptr<T> createAction(const json& jsonObj)
46{
47 return std::make_unique<T>(jsonObj);
48}
49
50/**
51 * @class ActionBase - Base action object
52 *
53 * Base class for fan control's event actions
54 */
55class ActionBase
56{
57 public:
58 ActionBase() = delete;
59 ActionBase(const ActionBase&) = delete;
60 ActionBase(ActionBase&&) = delete;
61 ActionBase& operator=(const ActionBase&) = delete;
62 ActionBase& operator=(ActionBase&&) = delete;
63 virtual ~ActionBase() = default;
64 explicit ActionBase(const std::string& name) : _name(name)
65 {}
66
67 /**
68 * @brief Get the action function to perform
69 *
70 * An action function is a function associated to the derived action object
71 * that performs a specific task against fan control that's configured by
72 * a user.
73 *
74 * @return Action function
75 */
76 virtual const Action getAction() = 0;
77
78 /**
79 * @brief Get the action's name
80 *
81 * @return Name of the action
82 */
83 inline const auto& getName() const
84 {
85 return _name;
86 }
87
88 private:
89 /* The action's name that is used within the JSON configuration */
90 const std::string _name;
91};
92
93/**
94 * @class ActionFactory - Factory for actions
95 *
96 * Factory that registers and retrieves actions based on a given name.
97 */
98class ActionFactory
99{
100 public:
101 ActionFactory() = delete;
102 ActionFactory(const ActionFactory&) = delete;
103 ActionFactory(ActionFactory&&) = delete;
104 ActionFactory& operator=(const ActionFactory&) = delete;
105 ActionFactory& operator=(ActionFactory&&) = delete;
106 ~ActionFactory() = default;
107
108 /**
109 * @brief Registers an action
110 *
111 * Registers an action as being available for configuration use. The action
112 * is registered by its name and a function used to create the action
113 * object. An action fails to be registered when another action of the same
114 * name has already been registered. Actions with the same name would cause
115 * undefined behavior, therefore are not allowed.
116 *
117 * Actions are registered prior to starting main().
118 *
119 * @param[in] name - Name of the action to register
120 *
121 * @return The action was registered, otherwise an exception is thrown.
122 */
123 template <typename T>
124 static bool regAction(const std::string& name)
125 {
126 auto it = actions.find(name);
127 if (it == actions.end())
128 {
129 actions[name] = &createAction<T>;
130 }
131 else
132 {
133 log<level::ERR>(
134 fmt::format("Action '{}' is already registered", name).c_str());
135 throw std::runtime_error("Actions with the same name found");
136 }
137
138 return true;
139 }
140
141 /**
142 * @brief Gets a registered action's object
143 *
144 * Gets a registered action's object of a given name from the JSON
145 * configuration data provided.
146 *
147 * @param[in] name - Name of the action to create/get
148 * @param[in] jsonObj - JSON object for the action
149 *
150 * @return Pointer to the action object.
151 */
152 static std::unique_ptr<ActionBase> getAction(const std::string& name,
153 const json& jsonObj)
154 {
155 auto it = actions.find(name);
156 if (it != actions.end())
157 {
158 return it->second(jsonObj);
159 }
160 else
161 {
162 // Construct list of available actions
163 auto acts = std::accumulate(
164 std::next(actions.begin()), actions.end(),
165 actions.begin()->first, [](auto list, auto act) {
166 return std::move(list) + ", " + act.first;
167 });
168 log<level::ERR>(
169 fmt::format("Action '{}' is not registered", name).c_str(),
170 entry("AVAILABLE_ACTIONS=%s", acts.c_str()));
171 throw std::runtime_error("Unsupported action name given");
172 }
173 }
174
175 private:
176 /* Map to store the available actions and their creation functions */
177 static inline std::map<
178 std::string, std::function<std::unique_ptr<ActionBase>(const json&)>>
179 actions;
180};
181
182/**
183 * @class ActionRegister - Registers an action class
184 *
185 * Base action registration class that is extended by an action object so
186 * that action is registered and available for use.
187 */
188template <typename T>
189class ActionRegister
190{
191 public:
192 ActionRegister(const ActionRegister&) = delete;
193 ActionRegister(ActionRegister&&) = delete;
194 ActionRegister& operator=(const ActionRegister&) = delete;
195 ActionRegister& operator=(ActionRegister&&) = delete;
196 virtual ~ActionRegister() = default;
197 ActionRegister()
198 {
199 // Templates instantiated when used, need to assign a value
200 // here so the compiler doesnt remove it
201 registered = true;
202 }
203
204 private:
205 /* Register actions in the factory */
206 static inline bool registered = ActionFactory::regAction<T>(T::name);
207};
208
209} // namespace phosphor::fan::control::json