Lamp-Da 0.1
A compact lantern project
Loading...
Searching...
No Matches
group_type.hpp
Go to the documentation of this file.
1#ifndef MODE_GROUP_H
2#define MODE_GROUP_H
3
9#include <cstdint>
10#include <utility>
11#include <optional>
12#include <tuple>
13#include <array>
14
16
21
22namespace lampda::modes {
23
25template<typename AllModes> static constexpr bool verifyGroup()
26{
27 constexpr bool inheritsFromBasicMode = details::allOf<AllModes>::inheritsFromBasicMode;
28 static_assert(inheritsFromBasicMode, "All modes must inherit from modes::BasicMode!");
29
30 constexpr bool stateDefaultConstructible = details::allOf<AllModes>::stateDefaultConstructible;
31 static_assert(stateDefaultConstructible, "All states must be default constructible!");
32
33 bool acc = false;
34 acc |= !inheritsFromBasicMode;
35 acc |= !stateDefaultConstructible;
36 return acc;
37}
38
40template<typename AllModes, bool earlyFail = verifyGroup<AllModes>()> struct GroupTy
41{
42 // tuple helper
43 using SelfTy = GroupTy<AllModes>;
44 using AllModesTy = AllModes;
45 using AllStatesTy = details::StateTyFrom<AllModes>;
46 static constexpr uint8_t nbModes {std::tuple_size_v<AllModesTy>};
47
48 // last mode index must not collide with modes::store::noModeIndex
49 static_assert(nbModes < 32, "Maximum of 31 modes has been exceeded.");
50
51 template<uint8_t Idx> using ModeAtRaw = std::tuple_element_t<Idx, AllModesTy>;
52 template<uint8_t Idx> using ModeAt = std::conditional_t<!earlyFail, ModeAtRaw<Idx>, BasicMode>;
53
54 // required to support group-level context
55 using HasAnyMode = details::anyOf<AllModesTy, earlyFail>;
56 static constexpr bool hasSunsetAnimation = HasAnyMode::hasSunsetAnimation;
57 static constexpr bool hasBrightCallback = HasAnyMode::hasBrightCallback;
58 static constexpr bool hasSystemCallbacks = HasAnyMode::hasSystemCallbacks;
59 static constexpr bool requireUserThread = HasAnyMode::requireUserThread;
60 static constexpr bool hasCustomRamp = HasAnyMode::hasCustomRamp;
61 static constexpr bool hasButtonCustomUI = HasAnyMode::hasButtonCustomUI;
62
63 // useful for runtime tests of mode properties
64 static constexpr auto everySunsetCallback = HasAnyMode::everySunsetCallback;
65 static constexpr auto everyBrightCallback = HasAnyMode::everyBrightCallback;
66 static constexpr auto everySystemCallbacks = HasAnyMode::everySystemCallbacks;
67 static constexpr auto everyRequireUserThread = HasAnyMode::everyRequireUserThread;
68 static constexpr auto everyCustomRamp = HasAnyMode::everyCustomRamp;
69 static constexpr auto everyButtonCustomUI = HasAnyMode::everyButtonCustomUI;
70
71 // constructors
72 GroupTy() = delete;
73 GroupTy(const GroupTy&) = delete;
74 GroupTy& operator=(const GroupTy&) = delete;
75
77 template<typename CallBack> static void LMBD_INLINE dispatch_mode(auto& ctx, CallBack&& cb)
78 {
79 uint8_t modeId = ctx.get_active_mode(nbModes);
80
81 details::unroll<nbModes>([&](auto Idx) LMBD_INLINE {
82 if (Idx == modeId)
83 {
84 cb(context_as<ModeAt<Idx>>(ctx));
85 }
86 });
87 }
88
90 template<bool systemCallbacksOnly, typename CallBack> static void LMBD_INLINE foreach_mode(auto& ctx, CallBack&& cb)
91 {
92 if constexpr (systemCallbacksOnly)
93 {
94 details::unroll<nbModes>([&](auto Idx) LMBD_INLINE {
95 if constexpr (ModeAt<Idx>::hasSystemCallbacks)
96 {
97 cb(context_as<ModeAt<Idx>>(ctx));
98 }
99 });
100 }
101 else
102 {
103 details::unroll<nbModes>([&](auto Idx) LMBD_INLINE {
104 cb(context_as<ModeAt<Idx>>(ctx));
105 });
106 }
107 }
108
110 template<bool systemCallbacksOnly, typename CallBack> static void LMBD_INLINE iterate_mode(auto& ctx, CallBack&& cb)
111 {
112 if constexpr (systemCallbacksOnly)
113 {
114 details::unroll<nbModes>([&](auto Idx) LMBD_INLINE {
115 if constexpr (ModeAt<Idx>::hasSystemCallbacks)
116 {
117 cb(context_as<ModeAt<Idx>>(ctx), Idx);
118 }
119 });
120 }
121 else
122 {
123 details::unroll<nbModes>([&](auto Idx) LMBD_INLINE {
124 cb(context_as<ModeAt<Idx>>(ctx), Idx);
125 });
126 }
127 }
128
129 //
130 // store
131 //
132
133 // persistent values
134 enum class Store : uint16_t
135 {
136 rampMemory,
137 indexMemory
138 };
139
140 static constexpr uint32_t storeId = modes::store::derivateStoreId<modes::store::hash("GroupTyStoreId"), AllModesTy>;
141
142 //
143 // state
144 //
145
146 struct StateTy
147 {
148 AllStatesTy modeStates;
149 std::array<uint8_t, nbModes> customRampMemory;
150 std::array<uint8_t, nbModes> customIndexMemory;
151
153 void LMBD_INLINE save_ramps(auto& ctx, uint8_t modeId)
154 {
155 assert(modeId < nbModes);
156 if constexpr (ctx.hasCustomRamp)
157 {
158 ctx.state.customRampMemory[modeId] = ctx.get_active_custom_ramp();
159 ctx.state.customIndexMemory[modeId] = ctx.get_active_custom_index();
160 }
161 }
162
164 void LMBD_INLINE load_ramps(auto& ctx, uint8_t modeId)
165 {
166 assert(modeId < nbModes);
167 if constexpr (ctx.hasCustomRamp)
168 {
169 ctx.set_active_custom_ramp(ctx.state.customRampMemory[modeId]);
170 ctx.set_active_custom_index(ctx.state.customIndexMemory[modeId]);
171 }
172 }
173 };
174
176 template<typename Mode> static auto* LMBD_INLINE getStateOf(auto& manager)
177 {
178 using StateTy = typename Mode::StateTy;
179 using OptionalTy = std::optional<StateTy>;
180
181 StateTy* substate = nullptr;
182 details::unroll<nbModes>([&](auto Idx) LMBD_INLINE {
183 using ModeHere = ModeAt<Idx>;
184 constexpr bool isHere = std::is_same_v<ModeHere, Mode>;
185
186 if constexpr (isHere)
187 {
188 auto* state = manager.template getStateGroupOf<SelfTy>();
189 if (state)
190 {
191 OptionalTy& opt = std::get<OptionalTy>(state->modeStates);
192 if (!opt.has_value())
193 {
194 opt.emplace(); // all StateTy must be default-contructible :)
195 }
196
197 StateTy& stateHere = *opt;
198 substate = &stateHere;
199 }
200 }
201 });
202
203 assert(substate != nullptr && "this should not have happened!");
204 return substate;
205 }
206
207 //
208 // navigation
209 //
210
212 static constexpr bool isGroupManager = false;
214 static constexpr bool isModeManager = true;
215
217 static void next_mode(auto& ctx)
218 {
219 uint8_t modeIdBefore = ctx.get_active_mode(nbModes);
220 ctx.set_active_mode(modeIdBefore + 1, nbModes);
221 }
222
224 static void enter_mode(auto& ctx)
225 {
226 // restore brightness before entering a mode
227 ctx.lamp.setBrightness(logic::brightness::get_saved_brightness(), false, false, true);
228
229 // set ramps if they exist
230 uint8_t modeIdAfter = ctx.get_active_mode(nbModes);
231 ctx.state.load_ramps(ctx, modeIdAfter);
232
233 // start new mode we switched to
234 dispatch_mode(ctx, [](auto mode) {
235 mode.on_enter_mode();
236 });
237 }
238
240 static void quit_mode(auto& ctx)
241 {
242 // save ramps if they exist
243 uint8_t modeIdBefore = ctx.get_active_mode(nbModes);
244 // save ramps if they exist
245 ctx.state.save_ramps(ctx, modeIdBefore);
246
247 // start new mode we switched to
248 dispatch_mode(ctx, [](auto mode) {
249 mode.on_exit_mode();
250 });
251 }
252
253 //
254 // all the callbacks
255 //
256
258 static void loop(auto& ctx)
259 {
260 dispatch_mode(ctx, [](auto mode) {
261 mode.loop();
262 });
263 }
264
266 static void sunset_update(auto& ctx, float progress)
267 {
268 dispatch_mode(ctx, [&](auto mode) {
269 mode.sunset_update(progress);
270 });
271 }
272
274 static void brightness_update(auto& ctx, brightness_t brightness)
275 {
276 dispatch_mode(ctx, [&](auto mode) {
277 mode.brightness_update(brightness);
278 });
279 }
280
282 static void custom_ramp_update(auto& ctx, uint8_t rampValue)
283 {
284 dispatch_mode(ctx, [&](auto mode) {
285 mode.custom_ramp_update(rampValue);
286 });
287 }
288
290 static bool custom_click(auto& ctx, uint8_t nbClick)
291 {
292 bool retVal = false;
293 dispatch_mode(ctx, [&](auto mode) {
294 retVal = mode.custom_click(nbClick);
295 });
296 return retVal;
297 }
298
300 static bool custom_hold(auto& ctx, uint8_t nbClickAndHold, bool isEndOfHoldEvent, uint32_t holdDuration)
301 {
302 bool retVal = false;
303 dispatch_mode(ctx, [&](auto mode) {
304 retVal = mode.custom_hold(nbClickAndHold, isEndOfHoldEvent, holdDuration);
305 });
306 return retVal;
307 }
308
309 static std::array<uint8_t, nbModes> get_custom_ramp_default_value(auto& ctx)
310 {
311 std::array<uint8_t, nbModes> defaultValues;
312 defaultValues.fill(0);
313
314 iterate_mode<false>(ctx, [&defaultValues](auto mode, const size_t index) {
315 if constexpr (mode.hasCustomRamp)
316 {
317 defaultValues[index] = mode.get_custom_ramp_default_value();
318 }
319 });
320 return defaultValues;
321 }
322
324 static void power_on_sequence(auto& ctx)
325 {
326 foreach_mode<true>(ctx, [](auto mode) {
327 mode.power_on_sequence();
328 });
329 }
330
332 static void power_off_sequence(auto& ctx)
333 {
334 foreach_mode<true>(ctx, [](auto mode) {
335 mode.power_off_sequence();
336 });
337 }
338
340 static void write_parameters(auto& ctx)
341 {
342 foreach_mode<true>(ctx, [](auto mode) {
343 mode.write_parameters();
344 });
345 }
346
348 static void read_parameters(auto& ctx)
349 {
350 foreach_mode<true>(ctx, [](auto mode) {
351 mode.read_parameters();
352 });
353 }
354
356 static void user_thread(auto& ctx)
357 {
358 dispatch_mode(ctx, [](auto mode) {
359 mode.user_thread();
360 });
361 }
362};
363
369template<typename... Modes> using GroupFor = GroupTy<std::tuple<Modes...>>;
370
371} // namespace lampda::modes
372
373#endif
Define assertions helpers.
ContextTy and associated definitions.
modes::ManagerFor and associated definitions
Basic interface types to implement custom user modes.
brightness_t get_saved_brightness()
Return the saved brightness level. This shoudl be the prefered option in all computations.
Definition: brightness_handle.cpp:64
Contains basic interface types to implement custom user modes.
Definition: control_fixed_modes.hpp:12
GroupTy< std::tuple< Modes... > > GroupFor
Group together many different modes::BasicMode.
Definition: group_type.hpp:369
static auto context_as(auto &ctx)
Bind provided context to another modes::BasicMode.
Definition: context_type.hpp:25
void brightness_update(const brightness_t brightness)
Called when the system changes the LED strip brightness.
Definition: default_behavior.hpp:59
void user_thread()
Called at each tick of the secondary thread.
Definition: default_behavior.hpp:206
void read_parameters()
Called when system wants to read parameters from filesystem.
Definition: default_behavior.hpp:96
void power_on_sequence()
Called when the system powers on (must be non blocking function!)
Definition: default_behavior.hpp:34
void write_parameters()
Called when system wants to write parameters to filesystem.
Definition: default_behavior.hpp:90
void power_off_sequence()
Called when the system powers off (must be non blocking function!)
Definition: default_behavior.hpp:45
void loop()
Called at each tick of the main loop.
Definition: default_behavior.hpp:186
uint16_t brightness_t
Define the type of the brightness parameters.
Definition: constants.h:147
Definition: group_type.hpp:147
std::array< uint8_t, nbModes > customIndexMemory
Store the active index for each mode.
Definition: group_type.hpp:150
std::array< uint8_t, nbModes > customRampMemory
Store the ramp value for each mode.
Definition: group_type.hpp:149
AllStatesTy modeStates
Store the current group state.
Definition: group_type.hpp:148
void LMBD_INLINE save_ramps(auto &ctx, uint8_t modeId)
If a mode signaled that it has a ramp, save it.
Definition: group_type.hpp:153
void LMBD_INLINE load_ramps(auto &ctx, uint8_t modeId)
If a mode signaled that it has a ramp, load it.
Definition: group_type.hpp:164
Define templated tools to analyze the manager objects.