-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathGlobalConfig.h
266 lines (244 loc) · 12.8 KB
/
GlobalConfig.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
#ifndef GLOBAL_CONFIG_H
#define GLOBAL_CONFIG_H
#include <cstdint>
#include <cstring>
#include <ArduinoJson.h>
#include <Seeed_FS.h>
#include "SD/Seeed_SD.h"
#include "SharedResource.h"
/**
* @brief GlobalConfigで使用するJson Key一覧です
*/
namespace GlobalConfigKeys {
static constexpr char* Identifier = "identifier";
static constexpr char* Date = "date";
static constexpr char* Time = "time";
static constexpr char* UseWiFi = "useWiFi";
static constexpr char* ApSsid = "apSsid";
static constexpr char* ApPassWord = "apPassword";
static constexpr char* ApTimeoutMs = "apTimeoutMs";
static constexpr char* UseAmbient = "useAmbient";
static constexpr char* AmbientIntervalMs = "AmbientIntervalMs";
static constexpr char* AmbientChannelId = "ambientChanelId";
static constexpr char* AmbientWriteKey = "ambientWriteKey";
static constexpr char* GroveTaskFps = "groveTaskFps";
static constexpr char* ButtonTaskFps = "buttonTaskFps";
static constexpr char* UiTaskFps = "uiTaskFps";
static constexpr char* WifiTaskFps = "wifiTaskFps";
static constexpr char* GroveTaskPrintSerial = "groveTaskPrintSerial";
static constexpr char* GroveTaskPrintFile = "groveTaskPrintFile";
static constexpr char* BrightnessHoldMs = "brightnessHoldMs";
static constexpr char* BrightnessTransitionMs = "brightnessTransitionMs";
}
/**
* @brief GlobalConfigで使用する初期値一覧です
*/
namespace GlobalConfigDefaultValues {
static constexpr char* Identifier = "WFH Monitor";
static constexpr char* Date = __DATE__;
static constexpr char* Time = __TIME__;
static constexpr bool UseWiFi = false;
static constexpr char* ApSsid = "your ap ssid";
static constexpr char* ApPassWord = "your ap password";
static constexpr uint32_t ApTimeoutMs = 30000;
static constexpr bool UseAmbient = true;
static constexpr uint32_t AmbientIntervalMs = 60000;
static constexpr uint32_t AmbientChannelId = 0;
static constexpr char* AmbientWriteKey = "your writekey";
static constexpr uint32_t GroveTaskFps = 1;
static constexpr uint32_t ButtonTaskFps = 60;
static constexpr uint32_t UiTaskFps = 30;
static constexpr uint32_t WifiTaskFps = 1;
static constexpr bool GroveTaskPrintSerial = false;
static constexpr bool GroveTaskPrintFile = false;
static constexpr uint32_t BrightnessHoldMs = 4000;
static constexpr uint32_t BrightnessTransitionMs = 2000;
}
/**
* @brief WFH Terminalの設定データのInit/Read/Modify/Save/Loadを行うクラスです
* @note TaskBaseを継承したクラスで操作する場合はSharedResouceクラスでラップして処理すること、また配置にはCPU DataCacheを考慮すること
*
* @tparam N 管理ファイルに使用する領域、実際にはこの倍の領域が使用されます
*/
template<int N>
class GlobalConfig {
public:
/*
* @brief Construct a new Global Config object
*
* @param sharedSd SDカードのペリフェラル
* @param configPath Globalな設定の保存先として使うFilePath
*/
GlobalConfig(SharedResource<SDFS>& sharedSd, const char* configPath): sharedSd(sharedSd), baseFilePath(configPath) {}
/**
* @brief Destroy the Global Config object
*/
virtual ~GlobalConfig(void) {}
/**
* @brief 指定された値をconfigVolatileに書き込みます
*
* @tparam T 書き込むデータの型
* @param isOverwrite すでに値がセットされている場合でも上書きする場合はtrue
* @param key セット対象のKey
* @param value セットする値
*/
template<typename T>
void write(bool isOverwrite, const char* key, T& value) {
if (isOverwrite || !this->configVolatile.containsKey(key)) {
this->configVolatile[key] = value;
}
}
/**
* @brief 指定されたKeyの値を読み出します
*
* @tparam T 読み出す型
* @param key 読み出し対象のKey
* @param value 読みだしたデータの書き込み先、Keyが存在しない場合は操作しません
* @return true 読み出し成功
* @return false 読み出し失敗
*/
template<typename T>
bool read(const char* key, T& value) {
// Keyが存在しない
if (!this->configVolatile.containsKey(key)) {
return false;
}
// 値を読み出す
value = this->configVolatile[key];
return true;
}
/**
* @brief 指定されたKeyのポインタを取得します。文字列を読み出す場合などに利用してください
* @note 読みだしたPointerのLifetimeは必要最低限にとどめてください。読みだした時点のスコープ以上に広げないことを推奨します
*
* @tparam T 読み出す型
* @param key 読み出し対象のKey
* @return const T* 読み出し失敗
*/
template<typename T>
const T* getReadPtr(const char* key) {
// Keyが存在しない
if (!this->configVolatile.containsKey(key)) {
return nullptr;
}
return this->configVolatile[key];
}
/**
* @brief すべての値を初期値で上書きします
* @param isMigrate 存在しない値のみを上書きする場合はtrue
*
* @note 完全新規のconfigを生成する場合はisMigrate=falseで実行する
*/
void init(bool isMigrate) {
// 一通り必要な初期値をセット
// TODO: もう少しいい感じに書けるかも。Keys/DefaultValuesを内包する型パラメータの指定は考察が必要
this->write(!isMigrate, GlobalConfigKeys::Identifier , GlobalConfigDefaultValues::Identifier);
this->write(!isMigrate, GlobalConfigKeys::Date , GlobalConfigDefaultValues::Date);
this->write(!isMigrate, GlobalConfigKeys::Time , GlobalConfigDefaultValues::Time);
this->write(!isMigrate, GlobalConfigKeys::UseWiFi , GlobalConfigDefaultValues::UseWiFi);
this->write(!isMigrate, GlobalConfigKeys::ApSsid , GlobalConfigDefaultValues::ApSsid);
this->write(!isMigrate, GlobalConfigKeys::ApPassWord , GlobalConfigDefaultValues::ApPassWord);
this->write(!isMigrate, GlobalConfigKeys::ApTimeoutMs , GlobalConfigDefaultValues::ApTimeoutMs);
this->write(!isMigrate, GlobalConfigKeys::UseAmbient , GlobalConfigDefaultValues::UseAmbient);
this->write(!isMigrate, GlobalConfigKeys::AmbientIntervalMs , GlobalConfigDefaultValues::AmbientIntervalMs);
this->write(!isMigrate, GlobalConfigKeys::AmbientChannelId , GlobalConfigDefaultValues::AmbientChannelId);
this->write(!isMigrate, GlobalConfigKeys::AmbientWriteKey , GlobalConfigDefaultValues::AmbientWriteKey);
this->write(!isMigrate, GlobalConfigKeys::GroveTaskFps , GlobalConfigDefaultValues::GroveTaskFps);
this->write(!isMigrate, GlobalConfigKeys::ButtonTaskFps , GlobalConfigDefaultValues::ButtonTaskFps);
this->write(!isMigrate, GlobalConfigKeys::UiTaskFps , GlobalConfigDefaultValues::UiTaskFps);
this->write(!isMigrate, GlobalConfigKeys::WifiTaskFps , GlobalConfigDefaultValues::WifiTaskFps);
this->write(!isMigrate, GlobalConfigKeys::GroveTaskPrintSerial , GlobalConfigDefaultValues::GroveTaskPrintSerial);
this->write(!isMigrate, GlobalConfigKeys::GroveTaskPrintFile , GlobalConfigDefaultValues::GroveTaskPrintFile);
this->write(!isMigrate, GlobalConfigKeys::BrightnessHoldMs , GlobalConfigDefaultValues::BrightnessHoldMs);
this->write(!isMigrate, GlobalConfigKeys::BrightnessTransitionMs , GlobalConfigDefaultValues::BrightnessTransitionMs);
// migrationでなければNonVolatile側にも反映(this->clear()対策)
if (!isMigrate) {
this->configNonVolatile = this->configVolatile;
}
}
/**
* @brief init後の値、もしくはload/saveした後の値に巻き戻します
*/
void clear(void) {
this->configVolatile = this->configNonVolatile;
}
/**
* @brief configの内容をSD Cardから読み出します
*
* @param filePath 読み込み先、省略した場合はconstructorで指定したパスに書き込みます
* @return true 読み出し成功
* @return false 読み出し失敗
*/
bool load(const char* filePath, DeserializationError& deserializeError) {
const char* path = (filePath == nullptr) ? this->baseFilePath : filePath;
// baseFilePath == nullptrの対策
if (path == nullptr) {
deserializeError = DeserializationError::InvalidInput;
return false;
}
// take mutex & crititcal sectionで読み込みは行う
bool result = false;
this->sharedSd.operateCritial([&](SDFS& sd) {
File f = sd.open(path, FILE_READ);
// Fileが開けなければ失敗
if (!f) {
deserializeError = DeserializationError::InvalidInput;
return;
}
// configNonVolatileに読み出す
deserializeError = deserializeJson(this->configNonVolatile, f);
// file Handleはもう不要
f.close();
// DeserializeErrorが発生していれば終了
if (deserializeError != DeserializationError::Ok) return;
// 成功していればconfigVolatileの内容を上書き
this->configVolatile = this->configNonVolatile;
// versionが異なる場合のMigrationも実施しておく
this->init(true);
// 成功
result = true;
});
return result;
}
/**
* @brief 現在のconfigの内容をSD Cardに不揮発化します
*
* @param filePath 書き込み先、省略した場合はconstructorで指定したパスに書き込みます
* @return true 保存成功
* @return false 保存失敗
*/
bool save(const char* filePath) {
const char* path = (filePath == nullptr) ? this->baseFilePath : filePath;
// baseFilePath == nullptrの対策
if (path == nullptr) {
return false;
}
// take mutex & crititcal sectionで書き込みは行う
bool result = false;
this->sharedSd.operateCritial([&](SDFS& sd) {
File f = sd.open(path, FILE_WRITE);
// Fileが開けなければ失敗
if (!f) return;
// Date/Timeを最新ビルドのものに更新する
this->write(true, GlobalConfigKeys::Date , GlobalConfigDefaultValues::Date);
this->write(true, GlobalConfigKeys::Time , GlobalConfigDefaultValues::Time);
// configVolatileの内容を不揮発化する
const size_t byteWritten = serializeJson(this->configVolatile, f);
// File Handleはもう不要
f.close();
// 1byteも書けていなければ失敗
if (byteWritten == 0) return;
// 成功していればconfigNonVolatileの内容を上書き
this->configNonVolatile = this->configVolatile;
// 成功
result = true;
});
return result;
}
protected:
const char* baseFilePath;
SharedResource<SDFS>& sharedSd; /**< Semaphore, CriticalSectionの制定可能なSD Peripheral */
StaticJsonDocument<N> configVolatile; /**< 動作中に書き換わる領域 */
StaticJsonDocument<N> configNonVolatile; /**< Load時、またSave後に不揮発化されているオリジナルデータを格納する */
};
#endif /* GLOBAL_CONFIG_H */