src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
192 / 202
95.0%
Branches:
108 / 154
70.1%
Functions:
12 / 13
92.3%

ProtoSync.cpp
Line Branch Exec Source
1 #include "esp_log.h"
2 #include "ProtoSync.h"
3 #include "ProtoFrame.h"
4
5 static const char *TAG = "Proto";
6
7 /*********************************************************************
8 * Items
9 ********************************************************************/
10 50 void protoRoom(ProtoWriter &w, const room_snapshot_t &room) {
11 50 w.add(proto_field_t::roomId, room.roomId);
12 50 w.add(proto_field_t::name, room.name);
13 50 w.add(proto_field_t::sortOrder, room.sortOrder);
14 50 }
15 131 void protoShadeState(ProtoWriter &w, const shade_snapshot_t &shade) {
16 131 w.add(proto_field_t::shadeId, shade.shadeId);
17 131 w.add(proto_field_t::position, shade.position);
18 131 w.add(proto_field_t::target, shade.target);
19 131 w.add(proto_field_t::direction, shade.direction);
20 131 w.add(proto_field_t::myPos, shade.myPos);
21 131 w.add(proto_field_t::tiltPosition, shade.tiltPosition);
22 131 w.add(proto_field_t::tiltTarget, shade.tiltTarget);
23 131 w.add(proto_field_t::tiltDirection, shade.tiltDirection);
24 131 w.add(proto_field_t::myTiltPos, shade.myTiltPos);
25 131 w.add(proto_field_t::flags, shade.flags);
26 131 w.add(proto_field_t::lastRollingCode, shade.lastRollingCode);
27 131 }
28 130 void protoShade(ProtoWriter &w, const shade_snapshot_t &shade) {
29 130 protoShadeState(w, shade);
30 130 w.add(proto_field_t::roomId, shade.roomId);
31 130 w.add(proto_field_t::name, shade.name);
32 130 w.add(proto_field_t::remoteAddress, shade.remoteAddress);
33 130 w.add(proto_field_t::upTime, shade.upTime);
34 130 w.add(proto_field_t::downTime, shade.downTime);
35 130 w.add(proto_field_t::tiltTime, shade.tiltTime);
36 130 w.add(proto_field_t::stepSize, shade.stepSize);
37 130 w.add(proto_field_t::paired, shade.paired);
38 130 w.add(proto_field_t::tiltType, shade.tiltType);
39 130 w.add(proto_field_t::shadeType, shade.shadeType);
40 130 w.add(proto_field_t::bitLength, shade.bitLength);
41 130 w.add(proto_field_t::proto, shade.proto);
42 130 w.add(proto_field_t::flipCommands, shade.flipCommands);
43 130 w.add(proto_field_t::flipPosition, shade.flipPosition);
44 130 w.add(proto_field_t::inGroup, shade.inGroup);
45 130 w.add(proto_field_t::sunSensor, shade.sunSensor);
46 130 w.add(proto_field_t::light, shade.light);
47 130 w.add(proto_field_t::simMy, shade.simMy);
48 130 w.add(proto_field_t::repeats, shade.repeats);
49 130 w.add(proto_field_t::sortOrder, shade.sortOrder);
50 130 w.add(proto_field_t::gpioUp, shade.gpioUp);
51 130 w.add(proto_field_t::gpioDown, shade.gpioDown);
52 130 w.add(proto_field_t::gpioMy, shade.gpioMy);
53 130 w.add(proto_field_t::gpioLLTrigger, shade.gpioLLTrigger);
54
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 126 times.
130 if(shade.linkedRemoteCount == 0) return;
55 126 w.beginList(proto_field_t::linkedRemotes);
56
2/2
✓ Branch 0 taken 882 times.
✓ Branch 1 taken 126 times.
1008 for(uint8_t i = 0; i < shade.linkedRemoteCount; i++) {
57 882 w.beginItem();
58 882 w.add(proto_field_t::remoteAddress, shade.linkedRemotes[i].remoteAddress);
59 882 w.add(proto_field_t::lastRollingCode, shade.linkedRemotes[i].lastRollingCode);
60 882 w.endItem();
61 }
62 126 w.endList();
63 }
64 56 void protoGroup(ProtoWriter &w, const group_snapshot_t &group) {
65 56 w.add(proto_field_t::groupId, group.groupId);
66 56 w.add(proto_field_t::roomId, group.roomId);
67 56 w.add(proto_field_t::name, group.name);
68 56 w.add(proto_field_t::remoteAddress, group.remoteAddress);
69 56 w.add(proto_field_t::lastRollingCode, group.lastRollingCode);
70 56 w.add(proto_field_t::bitLength, group.bitLength);
71 56 w.add(proto_field_t::proto, group.proto);
72 56 w.add(proto_field_t::sunSensor, group.sunSensor);
73 56 w.add(proto_field_t::flipCommands, group.flipCommands);
74 56 w.add(proto_field_t::flags, group.flags);
75 56 w.add(proto_field_t::repeats, group.repeats);
76 56 w.add(proto_field_t::sortOrder, group.sortOrder);
77
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
56 if(group.linkedShadeCount == 0) return;
78 56 w.beginList(proto_field_t::linkedShades);
79
2/2
✓ Branch 1 taken 1763 times.
✓ Branch 2 taken 56 times.
1819 for(uint8_t i = 0; i < group.linkedShadeCount; i++) w.item(group.linkedShades[i]);
80 56 w.endList();
81 }
82 ✗ void protoGroupState(ProtoWriter &w, uint8_t groupId, uint8_t flags, int8_t direction) {
83 ✗ w.add(proto_field_t::groupId, groupId);
84 ✗ w.add(proto_field_t::flags, flags);
85 ✗ w.add(proto_field_t::direction, direction);
86 ✗ }
87
88 /*********************************************************************
89 * Sync
90 ********************************************************************/
91 // What closing a frame cut short takes: the list's ], the }} of d and the
92 // frame, and the terminator.
93 #define SYNC_CLOSE_RESERVE 4
94
95 20 sync_cursor_t beginSync(bool withSchema) {
96 20 sync_cursor_t cur;
97
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 7 times.
20 cur.section = withSchema ? sync_section_t::types : sync_section_t::header;
98 20 cur.index = 0;
99 20 return cur;
100 }
101
102 179 static proto_field_t sectionField(sync_section_t section) {
103
10/10
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 40 times.
✓ Branch 6 taken 40 times.
✓ Branch 7 taken 58 times.
✓ Branch 8 taken 24 times.
✓ Branch 9 taken 6 times.
179 switch(section) {
104 1 case sync_section_t::types: return proto_field_t::types;
105 2 case sync_section_t::fields: return proto_field_t::fields;
106 1 case sync_section_t::errors: return proto_field_t::errors;
107 1 case sync_section_t::feeds: return proto_field_t::feeds;
108 6 case sync_section_t::header: return proto_field_t::header;
109 40 case sync_section_t::rooms: return proto_field_t::rooms;
110 40 case sync_section_t::shades: return proto_field_t::shades;
111 58 case sync_section_t::groups: return proto_field_t::groups;
112 24 case sync_section_t::repeaters: return proto_field_t::repeaters;
113 6 default: return proto_field_t::status;
114 }
115 }
116 65 static uint8_t sectionSlots(sync_section_t section) {
117
8/9
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 34 times.
✓ Branch 6 taken 13 times.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
65 switch(section) {
118 1 case sync_section_t::types: return protoTypeCount();
119 2 case sync_section_t::fields: return static_cast<uint8_t>(proto_field_t::count);
120 1 case sync_section_t::errors: return protoErrorCount();
121 1 case sync_section_t::feeds: return protoFeedCount();
122 7 case sync_section_t::rooms: return SOMFY_MAX_ROOMS;
123 34 case sync_section_t::shades: return SOMFY_MAX_SHADES;
124 13 case sync_section_t::groups: return SOMFY_MAX_GROUPS;
125 6 case sync_section_t::repeaters: return SOMFY_MAX_REPEATERS;
126 ✗ default: return 0;
127 }
128 }
129 75 static void namedRow(JsonFormatter &json, const proto_named_t &row) {
130 75 json.beginArray();
131 75 json.addElem(row.number);
132 75 json.addElem(row.name);
133 75 json.endArray();
134 75 }
135 // Writes the item in slot of a list section. False when the slot is empty.
136 686 static bool writeItem(SyncSource &src, ProtoWriter &w, sync_section_t section, uint8_t slot) {
137 686 JsonFormatter &json = w.json;
138
8/9
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 149 times.
✓ Branch 4 taken 97 times.
✓ Branch 5 taken 220 times.
✓ Branch 6 taken 103 times.
✓ Branch 7 taken 42 times.
✗ Branch 8 not taken.
686 switch(section) {
139 66 case sync_section_t::types: namedRow(json, protoTypeAt(slot)); return true;
140 7 case sync_section_t::errors: namedRow(json, protoErrorAt(slot)); return true;
141 2 case sync_section_t::feeds: namedRow(json, protoFeedAt(slot)); return true;
142 149 case sync_section_t::fields: {
143
1/2
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
149 const proto_field_info_t &f = protoField(static_cast<proto_field_t>(slot));
144 149 const char kind[2] = {f.kind, '\0'};
145
1/2
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
149 json.beginArray();
146
1/2
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
149 json.addElem(f.key);
147
1/2
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
149 json.addElem(f.name);
148
1/2
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
149 json.addElem(kind);
149
1/2
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
149 json.addElem(f.def);
150
1/2
✓ Branch 1 taken 149 times.
✗ Branch 2 not taken.
149 json.endArray();
151 149 return true;
152 }
153 97 case sync_section_t::rooms: {
154 room_snapshot_t room;
155
3/4
✓ Branch 1 taken 97 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 47 times.
✓ Branch 4 taken 50 times.
97 if(!src.room(slot, room)) return false;
156
1/2
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
50 w.beginItem();
157
1/2
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
50 protoRoom(w, room);
158
1/2
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
50 w.endItem();
159 50 return true;
160 }
161 220 case sync_section_t::shades: {
162 shade_snapshot_t shade;
163
3/4
✓ Branch 1 taken 220 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 91 times.
✓ Branch 4 taken 129 times.
220 if(!src.shade(slot, shade)) return false;
164
1/2
✓ Branch 1 taken 129 times.
✗ Branch 2 not taken.
129 w.beginItem();
165
1/2
✓ Branch 1 taken 129 times.
✗ Branch 2 not taken.
129 protoShade(w, shade);
166
1/2
✓ Branch 1 taken 129 times.
✗ Branch 2 not taken.
129 w.endItem();
167 129 return true;
168 }
169 103 case sync_section_t::groups: {
170 group_snapshot_t group;
171
3/4
✓ Branch 1 taken 103 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 55 times.
103 if(!src.group(slot, group)) return false;
172
1/2
✓ Branch 1 taken 55 times.
✗ Branch 2 not taken.
55 w.beginItem();
173
1/2
✓ Branch 1 taken 55 times.
✗ Branch 2 not taken.
55 protoGroup(w, group);
174
1/2
✓ Branch 1 taken 55 times.
✗ Branch 2 not taken.
55 w.endItem();
175 55 return true;
176 }
177 42 case sync_section_t::repeaters: {
178 42 uint32_t address = 0;
179
3/4
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
✓ Branch 4 taken 21 times.
42 if(!src.repeater(slot, address)) return false;
180
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 w.item(address);
181 21 return true;
182 }
183 ✗ default:
184 ✗ return false;
185 }
186 }
187 40 static void nextSection(sync_cursor_t &cur) {
188 40 cur.section = static_cast<sync_section_t>(static_cast<uint8_t>(cur.section) + 1);
189 40 cur.index = 0;
190 40 }
191
192 49 size_t writeSyncFrame(SyncSource &src, sync_cursor_t &cur, char *buff, size_t size) {
193
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 43 times.
49 if(!cur.active()) return 0;
194 43 BufferedJsonFormatter json;
195
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
43 json.setBuffer(buff, size);
196 43 json.logOverflow(false);
197 43 ProtoWriter w(json);
198
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
43 beginFrame(json, proto_type_t::sync);
199 43 bool any = false;
200
3/4
✓ Branch 1 taken 453 times.
✓ Branch 2 taken 38 times.
✓ Branch 4 taken 453 times.
✗ Branch 5 not taken.
491 auto fits = [&json, size]() { return !json.overflowed() && json.length() + SYNC_CLOSE_RESERVE < size; };
201
2/2
✓ Branch 1 taken 77 times.
✓ Branch 2 taken 6 times.
83 while(cur.active()) {
202
4/4
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 65 times.
77 if(cur.section == sync_section_t::header || cur.section == sync_section_t::status) {
203 12 const json_mark_t m = json.mark();
204
1/2
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 w.beginObject(sectionField(cur.section));
205
3/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
12 if(cur.section == sync_section_t::header) src.header(w);
206
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 else src.status(w);
207
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 w.endObject();
208
2/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
12 if(!fits()) {
209
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ json.rollback(m);
210 // Only fixed size values, so this can't happen short of a bug.
211
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
12 if(!any) { ESP_LOGE(TAG, "Sync section %u doesn't fit in %u bytes", static_cast<uint8_t>(cur.section), (unsigned)size); nextSection(cur); continue; }
212 ✗ break;
213 }
214 12 any = true;
215 12 nextSection(cur);
216 12 continue;
217 12 }
218 65 bool open = false;
219 65 bool full = false;
220
1/2
✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
65 const uint8_t slots = sectionSlots(cur.section);
221
2/2
✓ Branch 0 taken 686 times.
✓ Branch 1 taken 28 times.
714 while(cur.index < slots) {
222 686 const json_mark_t m = json.mark();
223
3/4
✓ Branch 0 taken 167 times.
✓ Branch 1 taken 519 times.
✓ Branch 4 taken 167 times.
✗ Branch 5 not taken.
686 if(!open) w.beginList(sectionField(cur.section));
224
3/4
✓ Branch 1 taken 686 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 207 times.
✓ Branch 4 taken 479 times.
686 if(!writeItem(src, w, cur.section, cur.index)) {
225
1/2
✓ Branch 1 taken 207 times.
✗ Branch 2 not taken.
207 json.rollback(m);
226 207 cur.index++;
227 208 continue;
228 }
229
3/4
✓ Branch 1 taken 479 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 38 times.
✓ Branch 4 taken 441 times.
479 if(!fits()) {
230
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 json.rollback(m);
231
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 37 times.
38 if(!any) {
232 ESP_LOGE(TAG, "Sync item %u of section %u doesn't fit in %u bytes, leaving it out", cur.index, static_cast<uint8_t>(cur.section), (unsigned)size);
233 1 cur.index++;
234 1 continue;
235 }
236 37 full = true;
237 37 break;
238 }
239 441 open = true;
240 441 any = true;
241 441 cur.index++;
242 }
243
3/4
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 12 times.
✓ Branch 3 taken 53 times.
✗ Branch 4 not taken.
65 if(open) w.endList();
244
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 28 times.
65 if(full) break;
245 28 nextSection(cur);
246 }
247
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
43 endFrame(json);
248
1/2
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
43 return json.length();
249 }
250