src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
5 / 5
100.0%
Branches:
14 / 14
100.0%
Functions:
2 / 2
100.0%

LoopQueue.h
Line Branch Exec Source
1 #ifndef LOOP_QUEUE_H
2 #define LOOP_QUEUE_H
3 #include <stdint.h>
4 #include "ShadeRequests.h"
5 #include "ProtoFrame.h"
6
7 // The one queue through which other tasks hand work to loop(), which alone
8 // changes the shade model (docs/adr/0002-loop-task-is-the-only-writer-of-the-shade-model.md).
9 // The items and the hold rule are kept free of FreeRTOS so they can be unit
10 // tested on the PC (pio test -e native).
11
12 // Big enough for Home Assistant firing a command at every shade at once.
13 #define LOOP_QUEUE_SIZE 32
14
15 // What a restore_shades item restores, as bits of restoreParts.
16 #define RESTORE_SETTINGS 0x01
17 #define RESTORE_SHADES 0x02
18 #define RESTORE_NETWORK 0x04
19 #define RESTORE_TRANSCEIVER 0x08
20 #define RESTORE_REPEATERS 0x10
21 #define RESTORE_MQTT 0x20
22
23 enum class loop_item_type_t : uint8_t {
24 shade_request, // run by ShadeRequests
25 restore_shades, // restore from /shades.tmp, then reboot
26 model_edit, // apply an edit with ModelEditor
27 proto_read, // answer a protocol 2 read op from the shade model
28 proto_device // run a protocol 2 settings op or the WiFi scan (DeviceOps)
29 };
30 struct model_edit_t;
31 struct settings_save_t;
32 struct loop_item_t {
33 loop_item_type_t type = loop_item_type_t::shade_request;
34 shade_request_t request; // shade_request
35 uint8_t restoreParts = 0; // restore_shades: RESTORE_* bits
36 model_edit_t *edit = nullptr; // model_edit: loop() runs and frees it
37 // shade_request, proto_read and proto_device from a protocol 2 client:
38 // loop() replies once it has run.
39 proto_reply_to_t replyTo;
40 uint8_t op = 0; // proto_read and proto_device: the op's type
41 uint8_t groupId = 0; // proto_read: groupOptions' group
42 uint8_t section = 0; // proto_device: getSettings' settings_section_t
43 settings_save_t *save = nullptr; // proto_device: saveSettings' save, which loop() frees
44 };
45
46 // Whether the item sends anything on air.
47 30 inline bool loopItemUsesRadio(const loop_item_t &item) {
48
4/4
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 8 times.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 4 times.
30 return item.type == loop_item_type_t::shade_request && shadeRequestUsesRadio(item.request.type);
49 }
50 // Whether the item at the head of the queue waits for the radio. An HTTP or
51 // MQTT client was already told it was accepted, so it stays queued until the
52 // radio has room rather than have the radio drop it; a protocol 2 client gets
53 // its reply once it has run. While TX is suspended nothing gets through
54 // anyway so nothing is held back then, except an unpair, which waits until TX
55 // resumes rather than leave the shade paired. Everything queued
56 // behind it waits too, protocol 2 replies included, so a frequency scan
57 // started after an unpair delays them until it ends.
58 48 inline bool holdLoopItem(const loop_item_t &item, bool txSuspended, bool txHasRoom) {
59
6/6
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 18 times.
48 if(txSuspended) return item.type == loop_item_type_t::shade_request && item.request.type == shade_request_type_t::unpair;
60
4/4
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 9 times.
24 return loopItemUsesRadio(item) && !txHasRoom;
61 }
62
63 class LoopQueue {
64 public:
65 void begin();
66 // Hands the item to loop() without waiting, so it is safe from any task,
67 // including async_tcp. False when the queue is full. first puts it
68 // ahead of everything waiting: an item that ends a frequency scan, which
69 // an unpair at the head waits for (holdLoopItem).
70 bool submit(const loop_item_t &item, bool first = false);
71 // Runs the waiting items in order. Only from loop().
72 void drain();
73 private:
74 void *queue = nullptr;
75 };
76 #endif
77