Overte C++ Documentation
task/src/task/Config.h
1 //
2 // Config.h
3 // render/src/render
4 //
5 // Created by Zach Pomerantz on 1/6/2016.
6 // Copyright 2016 High Fidelity, Inc.
7 //
8 // Distributed under the Apache License, Version 2.0.
9 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10 //
11 
12 #ifndef hifi_task_Config_h
13 #define hifi_task_Config_h
14 
15 #include <chrono>
16 
17 #include <QtCore/qobject.h>
18 #include <QtCore/qjsondocument.h>
19 #include <QtCore/qjsonobject.h>
20 #include <QtCore/qjsonvalue.h>
21 #include <QtCore/QRegularExpression>
22 #include <shared/JSONHelpers.h>
23 
24 #include "SettingHandle.h"
25 
26 namespace task {
27 
28 class JobConcept;
29 
30 template <class C> class PersistentConfig : public C {
31 public:
32  const QString DEFAULT = "Default";
33  const QString NONE = "None";
34 
35  PersistentConfig() = delete;
36  PersistentConfig(const QStringList& path) :
37  _preset(path, DEFAULT) { }
38  PersistentConfig(const QStringList& path, bool enabled) : C(enabled),
39  _preset(path, enabled ? DEFAULT : NONE) { }
40 
41  QStringList getPresetList() {
42  if (_presets.empty()) {
43  setPresetList(QJsonObject());
44  }
45  return _presets.keys();
46  }
47 
48  virtual void setPresetList(const QJsonObject& list) override {
49  assert(_presets.empty());
50 
51  _default = toJsonValue(*this).toObject().toVariantMap();
52 
53  _presets.unite(list.toVariantMap());
54  if (C::isEnabled()) {
55  _presets.insert(DEFAULT, _default);
56  }
57  _presets.insert(NONE, QVariantMap{{ "enabled", false }});
58 
59  auto preset = _preset.get();
60  if (preset != _preset.getDefault() && _presets.contains(preset)) {
61  // Load the persisted configuration
62  C::load(_presets.value(preset).toMap());
63  }
64  }
65 
66  QString getPreset() { return _preset.get(); }
67 
68  void setPreset(const QString& preset) {
69  _preset.set(preset);
70  if (_presets.contains(preset)) {
71  // Always start back at default to remain deterministic
72  QVariantMap config = _default;
73  QVariantMap presetConfig = _presets[preset].toMap();
74  for (auto it = presetConfig.cbegin(); it != presetConfig.cend(); it++) {
75  config.insert(it.key(), it.value());
76  }
77  C::load(config);
78  }
79  }
80 
81 protected:
82  QMultiMap<QString, QVariant> _default;
83  QMultiMap<QString, QVariant> _presets;
85 };
86 
87 class JobConfig;
88 
89 class TConfigProxy {
90 public:
91  using Config = JobConfig;
92 };
93 
94 /*@jsdoc
95  * @namespace Workload
96  *
97  * @hifi-interface
98  * @hifi-client-entity
99  * @hifi-avatar
100  *
101  * @property {number} cpuRunTime - <em>Read-only.</em>
102  * @property {boolean} enabled
103  * @property {number} branch
104  */
105 // A default Config is always on; to create an enableable Config, use the ctor JobConfig(bool enabled)
106 class JobConfig : public QObject {
107  Q_OBJECT
108  Q_PROPERTY(double cpuRunTime READ getCPURunTime NOTIFY newStats()) //ms
109  Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY dirtyEnabled())
110  Q_PROPERTY(int branch READ getBranch WRITE setBranch NOTIFY dirtyEnabled)
111 
112  double _msCPURunTime{ 0.0 };
113 
114 protected:
115  friend class TaskConfig;
116 
117  bool _isEnabled{ true };
118 
119  uint8_t _branch { 0 };
120 public:
121  bool _isTask{ false };
122  bool _isSwitch{ false };
123 
124  using Persistent = PersistentConfig<JobConfig>;
125 
126  JobConfig() = default;
127  JobConfig(bool enabled): _isEnabled{ enabled } {}
128  ~JobConfig();
129 
130  bool isEnabled() const { return _isEnabled; }
131  void setEnabled(bool enable);
132 
133  virtual void setPresetList(const QJsonObject& object);
134 
135  /*@jsdoc
136  * @function Workload.toJSON
137  * @returns {string}
138  */
139  // This must be named toJSON to integrate with the global scripting JSON object
140  Q_INVOKABLE QString toJSON() { return QJsonDocument(toJsonValue(*this).toObject()).toJson(QJsonDocument::Compact); }
141 
142  /*@jsdoc
143  * @function Workload.load
144  * @param {object} map
145  */
146  Q_INVOKABLE void load(const QVariantMap& map) { qObjectFromJsonValue(QJsonObject::fromVariantMap(map), *this); emit loaded(); }
147 
148  // Running Time measurement
149  // The new stats signal is emitted once per run time of a job when stats (cpu runtime) are updated
150  void setCPURunTime(const std::chrono::nanoseconds& runtime) { _msCPURunTime = std::chrono::duration<double, std::milli>(runtime).count(); emit newStats(); }
151  double getCPURunTime() const { return _msCPURunTime; }
152 
153  /*@jsdoc
154  * @function Workload.getConfig
155  * @param {string} name
156  * @returns {object}
157  */
158  // Get a sub job config through task.getConfig(path)
159  // where path can be:
160  // - <job_name> search for the first job named job_name traversing the the sub graph of task and jobs (from this task as root)
161  // - <parent_name>.[<sub_parent_names>.]<job_name>
162  // Allowing to first look for the parent_name job (from this task as root) and then search from there for the
163  // optional sub_parent_names and finally from there looking for the job_name (assuming every job in the path were found)
164  //
165  // getter for qml integration, prefer the templated getter
166  Q_INVOKABLE QObject* getConfig(const QString& name) { return getConfig<TConfigProxy>(name.toStdString()); }
167 
168  // getter for cpp (strictly typed), prefer this getter
169  JobConfig* getRootConfig(const std::string& jobPath, std::string& jobName) const;
170  JobConfig* getJobConfig(const std::string& jobPath) const;
171  template <class T> typename T::Config* getConfig(std::string jobPath = "") const {
172  return dynamic_cast<typename T::Config*>(getJobConfig(jobPath));
173  }
174 
175  // Describe the node graph data connections of the associated Job/Task
176  /*@jsdoc
177  * @function Workload.isTask
178  * @returns {boolean}
179  */
180  Q_INVOKABLE bool isTask() const { return _isTask; }
181 
182  /*@jsdoc
183  * @function Workload.isSwitch
184  * @returns {boolean}
185  */
186  Q_INVOKABLE bool isSwitch() const { return _isSwitch; }
187 
188  /*@jsdoc
189  * @function Workload.getSubConfigs
190  * @returns {object[]}
191  */
192  Q_INVOKABLE QObjectList getSubConfigs() const {
193  auto list = findChildren<JobConfig*>(QRegularExpression(".*"), Qt::FindDirectChildrenOnly);
194  QObjectList returned;
195  for (int i = 0; i < list.size(); i++) {
196  returned.push_back(list[i]);
197  }
198  return returned;
199  }
200 
201  /*@jsdoc
202  * @function Workload.getNumSubs
203  * @returns {number}
204  */
205  Q_INVOKABLE int getNumSubs() const { return getSubConfigs().size(); }
206 
207  /*@jsdoc
208  * @function Workload.getSubConfig
209  * @param {number} index
210  * @returns {object}
211  */
212  Q_INVOKABLE QObject* getSubConfig(int i) const {
213  auto subs = getSubConfigs();
214  return ((i < 0 || i >= subs.size()) ? nullptr : subs[i]);
215  }
216 
217  void connectChildConfig(std::shared_ptr<JobConfig> childConfig, const std::string& name);
218  void transferChildrenConfigs(std::shared_ptr<JobConfig> source);
219 
220  JobConcept* _jobConcept;
221 
222  uint8_t getBranch() const { return _branch; }
223  void setBranch(uint8_t index);
224 
225 public slots:
226 
227  /*@jsdoc
228  * @function Workload.load
229  * @param {object} json
230  */
231  void load(const QJsonObject& val) { qObjectFromJsonValue(val, *this); emit loaded(); }
232 
233  /*@jsdoc
234  * @function Workload.refresh
235  */
236  void refresh();
237 
238 signals:
239 
240  /*@jsdoc
241  * @function Workload.loaded
242  * @returns {Signal}
243  */
244  void loaded();
245 
246  /*@jsdoc
247  * @function Workload.newStats
248  * @returns {Signal}
249  */
250  void newStats();
251 
252  /*@jsdoc
253  * @function Workload.dirtyEnabled
254  * @returns {Signal}
255  */
256  void dirtyEnabled();
257 };
258 
259 using QConfigPointer = std::shared_ptr<JobConfig>;
260 
261 }
262 
263 #endif // hifi_task_Config_h