src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
11 / 11
100.0%
Branches:
3 / 4
75.0%
Functions:
3 / 3
100.0%

ModelEditor.h
Line Branch Exec Source
1 #ifndef MODEL_EDITOR_H
2 #define MODEL_EDITOR_H
3 #include <stddef.h>
4 #include <stdint.h>
5 #include "SomfyFrame.h"
6 #include "ShadeTypes.h"
7
8 // The rules ModelEditor applies when the shade model is edited: which id, sort
9 // order and remote address a new room, shade or group gets, how ids go into
10 // and out of a list, and which GPIO pins a shade may not use. Kept free of the
11 // shade model so it can be unit tested on the PC (pio test -e native). The
12 // edits themselves are applied on loop() (SomfyJson.cpp), since only loop()
13 // changes the shade model (docs/adr/0002-loop-task-is-the-only-writer-of-the-shade-model.md).
14
15 // The edits, one per edit route.
16 enum class model_edit_type_t : uint8_t {
17 add_room, add_shade, add_group,
18 save_room, save_shade, save_group,
19 delete_room, delete_shade, delete_group,
20 link_remote, unlink_remote,
21 link_to_group, unlink_from_group,
22 link_repeater, unlink_repeater,
23 set_paired,
24 room_order, shade_order, group_order
25 };
26 // What an edit did, sent in the edit op's reply. id is the room,
27 // shade or group it added or changed, 0 when none. shadeId is the shade a
28 // group gained or lost. code is the validation error of a shade that wasn't
29 // saved, 0 otherwise. notFound: it was refused for an id that doesn't exist.
30 struct edit_result_t {
31 bool ok = true;
32 bool notFound = false;
33 uint8_t id = 0;
34 uint8_t shadeId = 0;
35 int8_t code = 0;
36 const char *desc = "";
37 2 static edit_result_t error(const char *desc, int8_t code = 0) {
38 2 edit_result_t r;
39 2 r.ok = false;
40 2 r.desc = desc;
41 2 r.code = code;
42 2 return r;
43 }
44 1 static edit_result_t missing(const char *desc) {
45 1 edit_result_t r = error(desc);
46 1 r.notFound = true;
47 1 return r;
48 }
49 };
50 // Why an add, or the read that offers one, is refused when every slot of its
51 // kind is taken.
52 #define EDIT_ROOMS_FULL "Maximum number of rooms exceeded."
53 #define EDIT_SHADES_FULL "Maximum number of shades exceeded."
54 #define EDIT_GROUPS_FULL "Maximum number of groups exceeded."
55
56 // The lists an edit can put in a new order.
57 enum class sort_list_t : uint8_t { none, rooms, shades, groups };
58 // What protocol 2 clients are sent once an edit has gone through: the whole
59 // items it changed (0 for none), a list's new order, the repeaters. Deletes
60 // send their removals as they happen, so they need nothing here.
61 struct edit_changes_t {
62 uint8_t shadeId = 0;
63 uint8_t groupId = 0;
64 uint8_t roomId = 0;
65 sort_list_t order = sort_list_t::none;
66 bool repeaters = false;
67 // A delete: the id of what was removed.
68 uint8_t removedShadeId = 0;
69 uint8_t removedGroupId = 0;
70 uint8_t removedRoomId = 0;
71 };
72 edit_changes_t editChanges(model_edit_type_t type, const edit_result_t &result);
73
74 // The lowest id from 1 to lastId that isn't in usedIds, or 0 when every one
75 // is taken.
76 uint8_t firstFreeId(const uint8_t *usedIds, size_t count, uint8_t lastId);
77 // The sort order that puts a new item after every existing one.
78 int8_t nextSortOrder(const int8_t *orders, size_t count);
79 // candidate, or the next address after it that isn't in used. No two shades or
80 // groups may share a remote address.
81 uint32_t freeRemoteAddress(uint32_t candidate, const uint32_t *used, size_t count);
82 // The remote address a new shade or group with that id is offered: the
83 // starting address plus its id, or the next free one after it.
84 uint32_t offeredRemoteAddress(uint32_t startingAddress, uint8_t id, const uint32_t *used, size_t count);
85
86 // A list of ids or addresses with 0 for an empty slot, kept packed at the front.
87 // Adding one that is already there changes nothing and succeeds; false when the
88 // list is full.
89 bool addToList(uint8_t *list, size_t size, uint8_t item);
90 bool addToList(uint32_t *list, size_t size, uint32_t item);
91 // Removes every copy and packs the rest to the front. False when it wasn't there.
92 bool removeFromList(uint8_t *list, size_t size, uint8_t item);
93 bool removeFromList(uint32_t *list, size_t size, uint32_t item);
94 // Packs the items to the front, keeping their order.
95 void packList(uint8_t *list, size_t size);
96
97 // Who else drives a GPIO pin.
98 struct pin_users_t {
99 bool (*uses)(uint8_t pin, void *ctx) = nullptr;
100 void *ctx = nullptr;
101
3/4
✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 50 times.
55 bool usesPin(uint8_t pin) const { return this->uses && this->uses(pin, this->ctx); }
102 };
103 #define PIN_CONFLICT_RADIO -10
104 #define PIN_CONFLICT_ETHERNET -11
105 #define PIN_CONFLICT_SHADE -12
106 // Whether a shade of type driven over proto may use these pins (255 for none):
107 // 0, or which of the radio, the Ethernet PHY or another shade already uses one.
108 // Only the pins the shade type and protocol actually drive are checked.
109 int8_t gpioPinConflict(shade_types type, radio_proto proto, uint8_t upPin, uint8_t downPin, uint8_t myPin,
110 const pin_users_t &radio, const pin_users_t &ethernet, const pin_users_t &otherShades);
111 #endif
112