Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
    npro  
 Modern all-safe Rust Network Protocol library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls
git clone https://npro.rs/repo/npro
 
root / assets / tc-wmbedtlsmsvc.svg
Author[]Andy Green <andy@warmcat.com> 2026-04-04 09:10 UTC
Committer[]Andy Green <andy@warmcat.com> 2026-04-05 05:56 UTC
Treed7b83d43823787cf29e54f737c387e106e977d6a   Raw Patch
 
dont broadcast identical messages
dont broadcast identical messages
diff --git a/CMakeLists.txt b/CMakeLists.txt index 338079f..544fff3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,6 +120,21 @@ if (LWS_OPENSSL_INCLUDE_DIRS) set(CMAKE_C_FLAGS "-I\"${LWS_OPENSSL_INCLUDE_DIRS}\" ${CMAKE_C_FLAGS}") endif() +find_package(OpenSSL) +if (OPENSSL_FOUND) + include_directories("${OPENSSL_INCLUDE_DIR}") +endif() + +if (APPLE AND EXISTS "/opt/homebrew/opt/openssl/include") + include_directories("/opt/homebrew/opt/openssl/include") + link_directories("/opt/homebrew/opt/openssl/lib") +endif() + +if (APPLE AND EXISTS "/opt/homebrew/opt/openssl@3/include") + include_directories("/opt/homebrew/opt/openssl@3/include") + link_directories("/opt/homebrew/opt/openssl@3/lib") +endif() + set(CMAKE_C_FLAGS "-DBUILD_INFO=\\\"${LWS_BUILD_HASH}\\\" ${CMAKE_C_FLAGS}") diff --git a/assets/sai.js b/assets/sai.js index 65c30e0..df4e768 100644 --- a/assets/sai.js +++ b/assets/sai.js @@ -1923,6 +1923,20 @@ function ws_open_sai() if (platformsArray) { last_builder_list = platformsArray; + + /* Ensure PCONs exist in topology even if omitted by the server payload */ + last_builder_list.forEach(b => { + if (b.pcon && !pcon_topology[b.pcon]) { + pcon_topology[b.pcon] = { + name: b.pcon, + on: 1, /* Default to on so it shows green if unknown */ + type: "auto-discovered", + depends_on: "", + children: [] + }; + } + }); + const container = document.getElementById("sai_builders"); if (container) renderPconHierarchy(container); } @@ -2148,7 +2162,7 @@ function ws_open_sai() const url_task_uuid = urlParams.get('task'); if (url_task_uuid === jso.t.uuid && - document.getElementById("sai_sticky")) + document.getElementById("sai_sticky")) { document.getElementById("sai_sticky").innerHTML = "<div class=\"taskinfo\" id=\"taskinfo-" + san(jso.t.uuid) + "\">" + @@ -2156,25 +2170,26 @@ function ws_open_sai() "</div>"; - s = "<table><td colspan=\"3\"><pre><table class=\"scrollogs\"><tr>" + - "<td class=\"atop\">" + - "<div id=\"dlogsn\" class=\"dlogsn\">" + lines + "</div></td>" + - "<td class=\"atop\">" + - "<div id=\"dlogst\" class=\"dlogst\">" + times + "</div></td>" + - "<td class=\"atop\"><div id=\"dlogs\" class=\"dlogs\">" + - "<span id=\"logs\" class=\"nowrap\">" + logs + - "</span>"+ - "</div></td></tr></table></pre>"; + s = "<table><td colspan=\"3\"><pre><table class=\"scrollogs\"><tr>" + + "<td class=\"atop\">" + + "<div id=\"dlogsn\" class=\"dlogsn\">" + lines + "</div></td>" + + "<td class=\"atop\">" + + "<div id=\"dlogst\" class=\"dlogst\">" + times + "</div></td>" + + "<td class=\"atop\"><div id=\"dlogs\" class=\"dlogs\">" + + "<span id=\"logs\" class=\"nowrap\">" + logs + + "</span>"+ + "</div></td></tr></table></pre>"; - if (document.getElementById("sai_overview")) { - document.getElementById("sai_overview").innerHTML = s; - logs_pending = times_pending = lines_pending = ""; + if (document.getElementById("sai_overview")) { + document.getElementById("sai_overview").innerHTML = s; + logs_pending = times_pending = lines_pending = ""; - if (document.getElementById("esr-" + jso.e.uuid)) - document.getElementById("esr-" + jso.e.uuid).innerHTML = - sai_event_summary_render(jso, now_ut, 1); + if (document.getElementById("esr-" + jso.e.uuid)) + document.getElementById("esr-" + jso.e.uuid).innerHTML = + sai_event_summary_render(jso, now_ut, 1); - } + } + } update_summary_and_progress(jso.e.uuid); if (document.getElementById("rebuild-" + san(jso.t.uuid))) { @@ -2206,9 +2221,12 @@ function ws_open_sai() document.getElementById("dlogsn").innerHTML = ""; document.getElementById("dlogst").innerHTML = ""; - document.getElementById("logs").innerHTML = ""; + document.getElementById("dlogs").innerHTML = "<span id=\"logs\" class=\"nowrap\"></span>"; lines = times = logs = ""; lines_pending = times_pending = logs_pending = ""; + segment_stack = []; + seg_counter = 0; + window.held_start_line = null; logAnsiState = {}; tfirst = 0; lli = 1; @@ -2379,61 +2397,92 @@ function ws_open_sai() default: s_logs = "<span class=\"tty1\">" + s + "</span>"; break; } + var eval_line = text_line; + if (window.pending_log_line && idx === 0) { + eval_line = window.pending_log_line + text_line; + } + + if (has_nl === '') { + window.pending_log_line = eval_line; + } else if (idx === 0) { + window.pending_log_line = ""; + } + var skip_push = false; var skip_render = false; - var match_fail = jso.channel === 1 ? text_line.match(/Test\s+#(\d+):\s+.*(Failed|\*\*\*)/i) : null; - var is_fail = match_fail || (jso.channel === 1 && /test failed/i.test(text_line)); + var match_fail = (jso.channel === 1 || jso.channel === 2) ? eval_line.match(/Test\s+#(\d+):\s+.*(Failed|\*\*\*|Timeout)/i) : null; + var is_fail = match_fail || ((jso.channel === 1 || jso.channel === 2) && /test failed/i.test(eval_line)); - if (jso.channel === 1) { + if (jso.channel === 1 || jso.channel === 2) { if (window.held_start_line) { if (is_fail) { + var is_same_test = false; + if (match_fail) { + var start_match = window.held_start_line.text.match(/Start\s+(\d+):/i); + if (start_match && start_match[1] === match_fail[1]) { + is_same_test = true; + } + } + // If the failing test isn't the one that just started, it must be running in parallel. + // Flush the unrelated valid 'Start' line out into the parent CTest boundary first. + if (!is_same_test) { + logs += window.held_start_line.s_logs; logs_pending += window.held_start_line.s_logs; + if (window.held_start_line.li) { + lines += window.held_start_line.en; lines_pending += window.held_start_line.en; + times += window.held_start_line.tn; times_pending += window.held_start_line.tn; + } + window.held_start_line = null; + } + while (segment_stack.length > 1) pop_segment(); - push_segment(text_line, true); + push_segment(eval_line, true); skip_push = true; } - logs += window.held_start_line.s_logs; logs_pending += window.held_start_line.s_logs; - if (window.held_start_line.li) { - lines += window.held_start_line.en; lines_pending += window.held_start_line.en; - times += window.held_start_line.tn; times_pending += window.held_start_line.tn; + if (window.held_start_line) { + logs += window.held_start_line.s_logs; logs_pending += window.held_start_line.s_logs; + if (window.held_start_line.li) { + lines += window.held_start_line.en; lines_pending += window.held_start_line.en; + times += window.held_start_line.tn; times_pending += window.held_start_line.tn; + } + window.held_start_line = null; } - window.held_start_line = null; } - if (/^\s*Start\s+\d+:/i.test(text_line)) { - window.held_start_line = { text: text_line, s_logs: s_logs, en: en, tn: tn, li: li }; + if (/^\s*Start\s+\d+:/i.test(eval_line)) { + window.held_start_line = { text: eval_line, s_logs: s_logs, en: en, tn: tn, li: li }; skip_render = true; } } if (jso.channel === 3) { - if (/^>saib>\s+Starting task step/.test(text_line)) { + if (/^>saib>\s+Starting task step/.test(eval_line)) { while (segment_stack.length > 0) pop_segment(); - push_segment(text_line, true); - } else if (/^>saib>\s+Step \d+:/.test(text_line) && segment_stack.length > 0) { + push_segment(eval_line, true); + } else if (/^>saib>\s+Step \d+:/.test(eval_line) && segment_stack.length > 0) { var pseg = segment_stack[0]; var phdr = document.getElementById("hdr-seg-" + pseg.id); - if (phdr) phdr.querySelector('.seg-title').innerText = text_line; + if (phdr) phdr.querySelector('.seg-title').innerText = eval_line; } else { while (segment_stack.length > 1) pop_segment(); } } - if (jso.channel === 1) { + if (jso.channel === 1 || jso.channel === 2) { if (skip_push) { // Fold logic successfully handled during lookahead execution - } else if (match_fail || /test failed/i.test(text_line)) { + } else if (match_fail || /test failed/i.test(eval_line)) { while (segment_stack.length > 1) pop_segment(); - push_segment(text_line, true); - } else if (/^\d+% tests passed/i.test(text_line) || /Total Test time/i.test(text_line) || /The following tests FAILED:/i.test(text_line) || /Errors while running CTest/i.test(text_line)) { + push_segment(eval_line, true); + } else if (/^\d+% tests passed/i.test(eval_line) || /Total Test time/i.test(eval_line) || /The following tests FAILED:/i.test(eval_line) || /Errors while running CTest/i.test(eval_line)) { while (segment_stack.length > 1) pop_segment(); - } else if (/^\d+\/\d+\s+Test\s+#\d+:/i.test(text_line)) { + } else if (/^\d+\/\d+\s+Test\s+#\d+:/i.test(eval_line)) { while (segment_stack.length > 1) pop_segment(); } } - var text_lower = text_line.toLowerCase(); - if (text_lower.includes("error:") || text_lower.includes("fatal:") || /error\s\d+:/.test(text_lower)) { + var text_lower = eval_line.toLowerCase(); + if (is_fail || text_lower.includes("error:") || text_lower.includes("fatal:") || /error\s\d+:/.test(text_lower)) { for (var si = 0; si < segment_stack.length; si++) { var sobj = segment_stack[si]; sobj.error_count++; diff --git a/src/builder/b-nspawn.c b/src/builder/b-nspawn.c index 7a7f42a..b11ab5c 100644 --- a/src/builder/b-nspawn.c +++ b/src/builder/b-nspawn.c @@ -523,11 +523,15 @@ saib_spawn_script(struct sai_nspawn *ns) ns->inp); #endif - lws_strncpy(one_step, ns->task->script, sizeof(one_step)); + n = lws_snprintf(one_step, sizeof(one_step), "%s\n", ns->task->script); + if (n < 1) + return -1; - if (saib_log_chunk_create(op->ns, one_step, strlen(one_step), 3)) + if (saib_log_chunk_create(ns, one_step, strlen(one_step), 3)) return -1; + one_step[n - 1] = '\0'; /* trim off the CR; n is always at least 1 */ + #if defined(WIN32) if (_sopen_s(&fd, ns->script_path, _O_CREAT | _O_TRUNC | _O_WRONLY, _SH_DENYNO, _S_IWRITE)) diff --git a/src/builder/b-sai.c b/src/builder/b-sai.c index 501ba17..ac7f12d 100644 --- a/src/builder/b-sai.c +++ b/src/builder/b-sai.c @@ -384,6 +384,13 @@ app_system_state_nf(lws_state_manager_t *mgr, lws_state_notify_link_t *link, return 1; } + if (builder.power_controller_name) { + lws_start_foreach_dll(struct lws_dll2 *, d, builder.sai_plat_owner.head) { + struct sai_plat *sp = lws_container_of(d, struct sai_plat, sai_plat_list); + sp->pcon = builder.power_controller_name; + } lws_end_foreach_dll(d); + } + /* * For each platform... */ @@ -547,6 +554,7 @@ saib_app_run(int argc, const char **argv) return 1; } + /* * We need to sample the true uid / gid we should use inside * the mountpoint for sai:nobody or sai:sai, by looking at @@ -642,14 +650,12 @@ saib_app_run(int argc, const char **argv) /* ... and our vhost... */ - builder.context = lws_create_context(&info); - if (!builder.context) { + builder.vhost = lws_create_vhost(builder.context, &info); + if (!builder.vhost) { lwsl_err("lws init failed\n"); return 1; } - /* ... and our vhost... */ - while (!lws_service(builder.context, 0) && !interrupted) ; diff --git a/src/server/s-ws-builder.c b/src/server/s-ws-builder.c index 1738aa6..070fddb 100644 --- a/src/server/s-ws-builder.c +++ b/src/server/s-ws-builder.c @@ -775,11 +775,15 @@ sais_ws_json_rx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf, size_t b char q[1024]; lws_snprintf(q, sizeof(q), - "INSERT INTO builders (name, platform, last_seen, peer_ip, sai_hash, lws_hash, windows) " - "VALUES ('%s', '%s', %llu, '%s', '%s', '%s', %d) " - "ON CONFLICT(name) DO UPDATE SET last_seen=excluded.last_seen, " + "INSERT INTO builders (name, platform, pcon, last_seen, peer_ip, sai_hash, lws_hash, windows) " + "VALUES ('%s', '%s', %s%s%s, %llu, '%s', '%s', '%s', %d) " + "ON CONFLICT(name) DO UPDATE SET pcon=COALESCE(NULLIF(excluded.pcon, ''), pcon), last_seen=excluded.last_seen, " "peer_ip=excluded.peer_ip, sai_hash=excluded.sai_hash, lws_hash=excluded.lws_hash", - build->name, build->platform, (unsigned long long)lws_now_secs(), + build->name, build->platform, + build->pcon ? "'" : "NULL", + build->pcon ? build->pcon : "", + build->pcon ? "'" : "", + (unsigned long long)lws_now_secs(), pss->peer_ip, build->sai_hash, build->lws_hash, build->windows); if (sai_sqlite3_statement(vhd->server.pdb, q, "upsert builder")) @@ -801,7 +805,7 @@ sais_ws_json_rx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf, size_t b lws_strncpy(host, build->name, sizeof(host)); lws_snprintf(q, sizeof(q), - "UPDATE builders SET pcon = (SELECT pcon_name FROM pcon_builders WHERE builder_name = '%s') " + "UPDATE builders SET pcon = COALESCE((SELECT pcon_name FROM pcon_builders WHERE builder_name = '%s'), pcon) " "WHERE name = '%s' OR name LIKE '%s.%%'", host, build->name, build->name); lwsl_notice("%s: Syncing pcon for host '%s' (plat '%s'): %s\n", __func__, host, build->name, q); diff --git a/src/server/s-ws-web.c b/src/server/s-ws-web.c index 6fe1033..0584aa5 100644 --- a/src/server/s-ws-web.c +++ b/src/server/s-ws-web.c @@ -179,29 +179,70 @@ sais_list_pcons(struct vhd *vhd) lws_wsmsg_info_t info; size_t w; lws_struct_json_serialize_result_t r; + char pcon_query[128]; + sqlite3_stmt *pcon_stmt; sai_power_managed_builders_t pmb; sai_power_controller_t *pc; memset(&pmb, 0, sizeof(pmb)); - /* Query PCONs from DB using schema map */ - if (lws_struct_sq3_deserialize(vhd->server.pdb, NULL, "name ", - lsm_schema_sq3_map_power_controller, - &pmb.power_controllers, &ac, 0, 100)) { - /* It's okay if empty */ + /* + * We use raw sqlite queries for PCON deserialization to bypass + * complex inner/outer lws_struct_sq3 memory management edge cases that truncates rows. + */ + lws_snprintf(pcon_query, sizeof(pcon_query), + "SELECT name, type, depends_on, state FROM power_controllers ORDER BY name LIMIT 100"); + + if (sqlite3_prepare_v2(vhd->server.pdb, pcon_query, -1, &pcon_stmt, NULL) == SQLITE_OK) { + while (sqlite3_step(pcon_stmt) == SQLITE_ROW) { + const char *name = (const char *)sqlite3_column_text(pcon_stmt, 0); + if (name) { + pc = lwsac_use_zero(&ac, sizeof(*pc), 2048); + if (pc) { + lws_strncpy(pc->name, name, sizeof(pc->name)); + + const char *type = (const char *)sqlite3_column_text(pcon_stmt, 1); + if (type) lws_strncpy(pc->type, type, sizeof(pc->type)); + + const char *depends_on = (const char *)sqlite3_column_text(pcon_stmt, 2); + if (depends_on) lws_strncpy(pc->depends_on, depends_on, sizeof(pc->depends_on)); + + pc->on = (char)sqlite3_column_int(pcon_stmt, 3); + + lws_dll2_add_tail(&pc->list, &pmb.power_controllers); + } + } + } + sqlite3_finalize(pcon_stmt); } /* Iterate PCONs and populate controlled builders */ lws_start_foreach_dll(struct lws_dll2 *, d, pmb.power_controllers.head) { pc = lws_container_of(d, sai_power_controller_t, list); - char filter[128]; - - /* Map 'builder_name' column to 'name' field in struct */ - lws_snprintf(filter, sizeof(filter), "and pcon_name = '%s'", pc->name); - lws_struct_sq3_deserialize(vhd->server.pdb, filter, "builder_name ", - lsm_schema_sq3_map_controlled_builder, - &pc->controlled_builders_owner, &ac, 0, 100); + char query[256]; + sqlite3_stmt *stmt; + + /* Manually query builders mapped to this pcon to avoid LWS struct nested 0-row ac free bug */ + lws_snprintf(query, sizeof(query), + "SELECT builder_name FROM pcon_builders WHERE pcon_name = '%s' " + "ORDER BY builder_name LIMIT 100", pc->name); + + if (sqlite3_prepare_v2(vhd->server.pdb, query, -1, &stmt, NULL) == SQLITE_OK) { + while (sqlite3_step(stmt) == SQLITE_ROW) { + const char *bname = (const char *)sqlite3_column_text(stmt, 0); + if (bname) { + sai_controlled_builder_t *c = + lwsac_use_zero(&ac, sizeof(*c), 2048); + if (c) { + lws_strncpy(c->name, bname, sizeof(c->name)); + lws_dll2_add_tail(&c->list, + &pc->controlled_builders_owner); + } + } + } + sqlite3_finalize(stmt); + } } lws_end_foreach_dll(d); diff --git a/src/web/w-comms.c b/src/web/w-comms.c index 3bdf56c..d6fc7a2 100644 --- a/src/web/w-comms.c +++ b/src/web/w-comms.c @@ -539,6 +539,11 @@ http_resp: lws_dll2_remove(&pss->subs_list); lws_sul_cancel(&pss->sul_logcache); + for (n = 0; n < 3; n++) { + if (pss->last_bps[n]) + free(pss->last_bps[n]); + } + lwsac_free(&pss->logs_ac); break; diff --git a/src/web/w-private.h b/src/web/w-private.h index 54b1b00..bd27cd7 100644 --- a/src/web/w-private.h +++ b/src/web/w-private.h @@ -110,6 +110,9 @@ struct pss { uint64_t artifact_offset; uint64_t artifact_length; + char *last_bps[3]; + size_t last_bps_len[3]; + unsigned int spa_failed:1; unsigned int dry:1; unsigned int frag:1; @@ -233,5 +236,9 @@ int saiw_browser_broadcast_queue_builders(struct vhd *vhd, struct pss *pss); int saiw_browser_broadcast_queue_pcons(struct vhd *vhd, struct pss *pss); +int +saiw_browser_broadcast_queue_pcon_energy(struct vhd *vhd, struct pss *pss, sai_pcon_energy_report_t *energy); + +extern const lws_struct_map_t lsm_schema_pcon_energy[]; diff --git a/src/web/w-ws-browser.c b/src/web/w-ws-browser.c index 2a8b30e..8082b74 100644 --- a/src/web/w-ws-browser.c +++ b/src/web/w-ws-browser.c @@ -1126,20 +1126,135 @@ so_finish: return 0; } +struct sai_dyn_buf { + char *buf; + size_t len; + size_t alloc; +}; + +static int +sai_dyn_buf_ensure(struct sai_dyn_buf *d, size_t needed) +{ + if (d->len + needed <= d->alloc) + return 0; + size_t na = d->alloc ? d->alloc * 2 : 4096; + while (d->len + needed > na) + na *= 2; + char *nb = realloc(d->buf, na); + if (!nb) + return 1; + d->buf = nb; + d->alloc = na; + return 0; +} + +static inline int +sai_dyn_buf_append(struct sai_dyn_buf *d, const void *p, size_t len) +{ + if (sai_dyn_buf_ensure(d, len)) + return 1; + memcpy(d->buf + d->len, p, len); + d->len += len; + return 0; +} + +static int +saiw_dedup_and_queue(struct pss *pss, int idx, struct sai_dyn_buf *d) +{ + int changed = 1; + + /* check if we changed versus last payload */ + if (pss->last_bps[idx] && pss->last_bps_len[idx] == d->len - LWS_PRE && + !memcmp(pss->last_bps[idx], d->buf + LWS_PRE, d->len - LWS_PRE)) { + changed = 0; + } else { + free(pss->last_bps[idx]); + pss->last_bps[idx] = malloc(d->len - LWS_PRE); + if (pss->last_bps[idx]) { + memcpy(pss->last_bps[idx], d->buf + LWS_PRE, d->len - LWS_PRE); + pss->last_bps_len[idx] = d->len - LWS_PRE; + } + } + + if (changed) + saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, d->buf + LWS_PRE, + d->len - LWS_PRE, + lws_write_ws_flags(LWS_WRITE_TEXT, 1, 1)); + + free(d->buf); + d->buf = NULL; + return 0; +} + +int +saiw_browser_broadcast_queue_pcon_energy(struct vhd *vhd, struct pss *pss, sai_pcon_energy_report_t *energy) +{ + struct sai_dyn_buf d; + char buf[1024]; + lws_struct_serialize_t *js; + lws_struct_json_serialize_result_t r; + size_t w; + + if (!vhd || !energy) + return 0; + + memset(&d, 0, sizeof(d)); + + /* Reserve LWS_PRE header space */ + memset(buf, 0, LWS_PRE); + if (sai_dyn_buf_append(&d, buf, LWS_PRE)) + return 1; + + js = lws_struct_json_serialize_create( + lsm_schema_pcon_energy, + LWS_ARRAY_SIZE(lsm_schema_pcon_energy), + 0, energy); + if (!js) { + free(d.buf); + return 1; + } + + do { + r = lws_struct_json_serialize(js, (uint8_t *)buf, sizeof(buf), &w); + + if (w && sai_dyn_buf_append(&d, buf, w)) { + lws_struct_json_serialize_destroy(&js); + free(d.buf); + return 1; + } + + if (r == LSJS_RESULT_ERROR) { + lws_struct_json_serialize_destroy(&js); + free(d.buf); + return 1; + } + } while (r == LSJS_RESULT_CONTINUE); + + lws_struct_json_serialize_destroy(&js); + + return saiw_dedup_and_queue(pss, 2, &d); +} + int saiw_browser_broadcast_queue_pcons(struct vhd *vhd, struct pss *pss) { - char buf[4096 + LWS_PRE], *start = buf + LWS_PRE, *p = start, - *end = buf + sizeof(buf); + struct sai_dyn_buf d; + char buf[1024]; /* temp buffer for serialization before append */ lws_struct_serialize_t *js; sai_power_managed_builders_t pmb; lws_struct_json_serialize_result_t r; size_t w; - char fi = 1; if (!vhd || !vhd->pcons) return 0; + memset(&d, 0, sizeof(d)); + + /* Reserve LWS_PRE header space */ + memset(buf, 0, LWS_PRE); + if (sai_dyn_buf_append(&d, buf, LWS_PRE)) + return 1; + memset(&pmb, 0, sizeof(pmb)); pmb.power_controllers = vhd->pcons_owner; @@ -1147,51 +1262,61 @@ saiw_browser_broadcast_queue_pcons(struct vhd *vhd, struct pss *pss) lsm_schema_power_managed_builders, LWS_ARRAY_SIZE(lsm_schema_power_managed_builders), 0, &pmb); - if (!js) + if (!js) { + free(d.buf); return 1; + } do { - r = lws_struct_json_serialize(js, (uint8_t *)p, - lws_ptr_diff_size_t(end, p), &w); - p += w; + r = lws_struct_json_serialize(js, (uint8_t *)buf, sizeof(buf), &w); - switch (r) { - case LSJS_RESULT_FINISH: - case LSJS_RESULT_CONTINUE: - saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start, - lws_ptr_diff_size_t(p, start), - lws_write_ws_flags(LWS_WRITE_TEXT, fi, r == LSJS_RESULT_FINISH)); - fi = 0; - p = start; - break; - case LSJS_RESULT_ERROR: + if (sai_dyn_buf_append(&d, buf, w)) { lws_struct_json_serialize_destroy(&js); + free(d.buf); + return 1; + } + + if (r == LSJS_RESULT_ERROR) { + lws_struct_json_serialize_destroy(&js); + free(d.buf); return 1; } } while (r == LSJS_RESULT_CONTINUE); lws_struct_json_serialize_destroy(&js); - return 0; + return saiw_dedup_and_queue(pss, 1, &d); } int saiw_browser_broadcast_queue_builders(struct vhd *vhd, struct pss *pss) { saiw_browser_broadcast_queue_pcons(vhd, pss); - char buf[4096 + LWS_PRE], *start = buf + LWS_PRE, *p = start, - *end = buf + sizeof(buf); + struct sai_dyn_buf d; + char buf[1024]; /* temp buffer for serialization before append */ lws_struct_serialize_t *js; char esc[256]; lws_dll2_t *walk = NULL; - char fi = 1, subsequent; + char subsequent; size_t w; + int n; - p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), + memset(&d, 0, sizeof(d)); + + /* Reserve LWS_PRE header space */ + memset(buf, 0, LWS_PRE); + if (sai_dyn_buf_append(&d, buf, LWS_PRE)) + return 1; + + n = lws_snprintf(buf, sizeof(buf), "{\"schema\":\"com.warmcat.sai.builders\"," " \"alang\":\"%s\"," " \"builders\":[", lws_sql_purify(esc, pss->alang, sizeof(esc) - 1)); + if (sai_dyn_buf_append(&d, buf, (size_t)n)) { + free(d.buf); + return 1; + } if (vhd && vhd->builders) walk = lws_dll2_get_head(&vhd->builders_owner); @@ -1211,66 +1336,58 @@ saiw_browser_broadcast_queue_builders(struct vhd *vhd, struct pss *pss) 0, b); if (!js) { lwsac_unreference(&vhd->builders); + free(d.buf); return 1; } do { if (subsequent && start_of_this_builder) { - *p++ = ','; + if (sai_dyn_buf_append(&d, ",", 1)) { + lws_struct_json_serialize_destroy(&js); + lwsac_unreference(&vhd->builders); + free(d.buf); + return 1; + } start_of_this_builder = 0; } - r = lws_struct_json_serialize(js, (uint8_t *)p, lws_ptr_diff_size_t(end, p) - 2, &w); - p += w; + r = lws_struct_json_serialize(js, (uint8_t *)buf, sizeof(buf), &w); + + if (w && sai_dyn_buf_append(&d, buf, w)) { + lws_struct_json_serialize_destroy(&js); + lwsac_unreference(&vhd->builders); + free(d.buf); + return 1; + } switch (r) { - case LSJS_RESULT_CONTINUE: - saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start, - lws_ptr_diff_size_t(p, start), - lws_write_ws_flags(LWS_WRITE_TEXT, fi, 0)); - fi = 0; - p = start; - break; case LSJS_RESULT_ERROR: lws_struct_json_serialize_destroy(&js); lwsac_unreference(&vhd->builders); + free(d.buf); return 1; + case LSJS_RESULT_CONTINUE: case LSJS_RESULT_FINISH: - lws_struct_json_serialize_destroy(&js); break; } } while (r == LSJS_RESULT_CONTINUE); + lws_struct_json_serialize_destroy(&js); + subsequent = 1; walk = walk->next; - - if (walk && lws_ptr_diff_size_t(end, p) < 512) { - /* No room for another builder, fragment now */ - saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start, - lws_ptr_diff_size_t(p, start), - lws_write_ws_flags(LWS_WRITE_TEXT, fi, 0)); - fi = 0; - p = start; - } } - if (lws_ptr_diff_size_t(end, p) < 16) { - saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start, - lws_ptr_diff_size_t(p, start), - lws_write_ws_flags(LWS_WRITE_TEXT, fi, 0)); - fi = 0; - p = start; + n = lws_snprintf(buf, sizeof(buf), " \n]}"); + if (sai_dyn_buf_append(&d, buf, (size_t)n)) { + lwsac_unreference(&vhd->builders); + free(d.buf); + return 1; } - p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), " \n]}"); - - saiw_ws_browser_queue_REQUIRES_LWS_PRE(pss, start, - lws_ptr_diff_size_t(p, start), - lws_write_ws_flags(LWS_WRITE_TEXT, fi, 1)); - lwsac_unreference(&vhd->builders); - return 0; + return saiw_dedup_and_queue(pss, 0, &d); } /* diff --git a/src/web/w-ws-server.c b/src/web/w-ws-server.c index 2261a9e..97036b4 100644 --- a/src/web/w-ws-server.c +++ b/src/web/w-ws-server.c @@ -127,9 +127,6 @@ saiw_lp_rx(void *userobj, const uint8_t *buf, size_t len, int flags) switch (m->a.top_schema_index) { case SAIS_WS_WEBSRV_RX_LOADREPORT: case SAIS_WS_WEBSRV_RX_TASKACTIVITY: - case SAIS_WS_WEBSRV_RX_SAI_BUILDERS: - case SAIS_WS_WEBSRV_RX_POWER_MANAGED_BUILDERS: - case SAIS_WS_WEBSRV_RX_PCON_ENERGY: saiw_ws_broadcast_browsers_REQUIRES_LWS_PRE(vhd, p, rem, lws_write_ws_flags(LWS_WRITE_TEXT, is_start, @@ -151,9 +148,6 @@ saiw_lp_rx(void *userobj, const uint8_t *buf, size_t len, int flags) switch (m->a.top_schema_index) { case SAIS_WS_WEBSRV_RX_TASKCHANGE: case SAIS_WS_WEBSRV_RX_EVENTCHANGE: - case SAIS_WS_WEBSRV_RX_SAI_BUILDERS: - case SAIS_WS_WEBSRV_RX_POWER_MANAGED_BUILDERS: - case SAIS_WS_WEBSRV_RX_PCON_ENERGY: case SAIS_WS_WEBSRV_RX_LOADREPORT: case SAIS_WS_WEBSRV_RX_TASKACTIVITY: saiw_ws_broadcast_browsers_REQUIRES_LWS_PRE(vhd, p, consumed, @@ -232,6 +226,14 @@ saiw_lp_rx(void *userobj, const uint8_t *buf, size_t len, int flags) } lws_end_foreach_dll(p); break; + case SAIS_WS_WEBSRV_RX_PCON_ENERGY: + lws_start_foreach_dll(struct lws_dll2 *, p, vhd->browsers.head) { + struct pss *pss = lws_container_of(p, struct pss, same); + + saiw_browser_broadcast_queue_pcon_energy(pss->vhd, pss, (sai_pcon_energy_report_t *)m->a.dest); + } lws_end_foreach_dll(p); + break; + case SAIS_WS_WEBSRV_RX_OVERVIEW: lwsl_notice("%s: force overview\n", __func__); lws_start_foreach_dll(struct lws_dll2 *, p, vhd->browsers.head) {
Page fetched 0s ago, creation time: 10ms (vhost etag hits: 0%, cache hits: 0%)