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 | 130 | if(shade.linkedRemoteCount == 0) return; | |
| 55 | 126 | w.beginList(proto_field_t::linkedRemotes); | |
| 56 | 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 | 56 | if(group.linkedShadeCount == 0) return; | |
| 78 | 56 | w.beginList(proto_field_t::linkedShades); | |
| 79 | 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 | 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 | 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 | 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 | 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 | 149 | const proto_field_info_t &f = protoField(static_cast<proto_field_t>(slot)); | |
| 144 | 149 | const char kind[2] = {f.kind, '\0'}; | |
| 145 | 149 | json.beginArray(); | |
| 146 | 149 | json.addElem(f.key); | |
| 147 | 149 | json.addElem(f.name); | |
| 148 | 149 | json.addElem(kind); | |
| 149 | 149 | json.addElem(f.def); | |
| 150 | 149 | json.endArray(); | |
| 151 | 149 | return true; | |
| 152 | } | ||
| 153 | 97 | case sync_section_t::rooms: { | |
| 154 | room_snapshot_t room; | ||
| 155 | 97 | if(!src.room(slot, room)) return false; | |
| 156 | 50 | w.beginItem(); | |
| 157 | 50 | protoRoom(w, room); | |
| 158 | 50 | w.endItem(); | |
| 159 | 50 | return true; | |
| 160 | } | ||
| 161 | 220 | case sync_section_t::shades: { | |
| 162 | shade_snapshot_t shade; | ||
| 163 | 220 | if(!src.shade(slot, shade)) return false; | |
| 164 | 129 | w.beginItem(); | |
| 165 | 129 | protoShade(w, shade); | |
| 166 | 129 | w.endItem(); | |
| 167 | 129 | return true; | |
| 168 | } | ||
| 169 | 103 | case sync_section_t::groups: { | |
| 170 | group_snapshot_t group; | ||
| 171 | 103 | if(!src.group(slot, group)) return false; | |
| 172 | 55 | w.beginItem(); | |
| 173 | 55 | protoGroup(w, group); | |
| 174 | 55 | w.endItem(); | |
| 175 | 55 | return true; | |
| 176 | } | ||
| 177 | 42 | case sync_section_t::repeaters: { | |
| 178 | 42 | uint32_t address = 0; | |
| 179 | 42 | if(!src.repeater(slot, address)) return false; | |
| 180 | 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 | 49 | if(!cur.active()) return 0; | |
| 194 | 43 | BufferedJsonFormatter json; | |
| 195 | 43 | json.setBuffer(buff, size); | |
| 196 | 43 | json.logOverflow(false); | |
| 197 | 43 | ProtoWriter w(json); | |
| 198 | 43 | beginFrame(json, proto_type_t::sync); | |
| 199 | 43 | bool any = false; | |
| 200 | 491 | auto fits = [&json, size]() { return !json.overflowed() && json.length() + SYNC_CLOSE_RESERVE < size; }; | |
| 201 | 83 | while(cur.active()) { | |
| 202 | 77 | if(cur.section == sync_section_t::header || cur.section == sync_section_t::status) { | |
| 203 | 12 | const json_mark_t m = json.mark(); | |
| 204 | 12 | w.beginObject(sectionField(cur.section)); | |
| 205 | 12 | if(cur.section == sync_section_t::header) src.header(w); | |
| 206 | 6 | else src.status(w); | |
| 207 | 12 | w.endObject(); | |
| 208 | 12 | if(!fits()) { | |
| 209 | ✗ | json.rollback(m); | |
| 210 | // Only fixed size values, so this can't happen short of a bug. | ||
| 211 | 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 | 65 | const uint8_t slots = sectionSlots(cur.section); | |
| 221 | 714 | while(cur.index < slots) { | |
| 222 | 686 | const json_mark_t m = json.mark(); | |
| 223 | 686 | if(!open) w.beginList(sectionField(cur.section)); | |
| 224 | 686 | if(!writeItem(src, w, cur.section, cur.index)) { | |
| 225 | 207 | json.rollback(m); | |
| 226 | 207 | cur.index++; | |
| 227 | 208 | continue; | |
| 228 | } | ||
| 229 | 479 | if(!fits()) { | |
| 230 | 38 | json.rollback(m); | |
| 231 | 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 | 65 | if(open) w.endList(); | |
| 244 | 65 | if(full) break; | |
| 245 | 28 | nextSection(cur); | |
| 246 | } | ||
| 247 | 43 | endFrame(json); | |
| 248 | 43 | return json.length(); | |
| 249 | } | ||
| 250 |