diff --git a/assets/sai.css b/assets/sai.css
index c3b8e64..b45379d 100644
--- a/assets/sai.css
+++ b/assets/sai.css
@@ -660,6 +660,11 @@ div.ibuil.power-stay {
border: 2px solid green;
}
+div.ibuil.power-stay-dep {
+ border: 2px solid blue;
+}
+
+
div.ibuilctr {
font-weight: normal;
font-size: 9pt;
@@ -811,6 +816,7 @@ img.branch {
.context-menu {
position: absolute;
background-color: #f9f9f9;
+ font-size: 8pt;
border: 1px solid #ccc;
padding: 5px;
z-index: 3000;
diff --git a/assets/sai.js b/assets/sai.js
index c0c6e72..94f7473 100644
--- a/assets/sai.js
+++ b/assets/sai.js
@@ -1030,8 +1030,10 @@ function createBuilderDiv(plat) {
if (!plat.power_managed)
platDiv.className += " power-unmanaged";
else
- if (plat.stay_on)
+ if (plat.stay_on !== 0)
platDiv.className += " power-stay";
+ else
+ platDiv.className += " power-stay-dep";
}
if (plat.powering_up)
platDiv.className += " powering-up";
@@ -1055,7 +1057,7 @@ function createBuilderDiv(plat) {
`<div class="res-bar"><div class="res-bar-inner res-bar-ram w-0"></div></div>` +
`<div class="res-bar"><div class="res-bar-inner res-bar-disk w-0"></div></div>` +
`</div>`;
- innerHTML += `${plat.peer_ip}`;
+ innerHTML += `${plat.peer_ip}` + " " + plat.stay_on;
// `<div class="server-state">` +
// `Slots: ${plat.s_avail_slots}, In-flight: ${plat.s_inflight_count}<br>` +
// `Last Reject: ${plat.s_last_rej_task_uuid ? plat.s_last_rej_task_uuid.substring(0, 8) : 'none'}` + "</div>" +
@@ -1075,22 +1077,10 @@ function createBuilderDiv(plat) {
const menuItems = [
{ label: `<b>SAI:</b> ${plat.sai_hash}` },
{ label: `<b>LWS:</b> ${plat.lws_hash}` },
- /*
- {
- label: "Update SAI",
- callback: () => {
- const rebuildMsg = {
- schema: "com.warmcat.sai.rebuild",
- builder_name: plat.name
- };
- sai.send(JSON.stringify(rebuildMsg));
- }
- }
- */
];
if (plat.power_managed && authd) {
- if (plat.stay_on) {
+ if (plat.stay_on !== 0) {
menuItems.push({
label: "Release Stay",
callback: () => {
diff --git a/src/builder/b-nspawn.c b/src/builder/b-nspawn.c
index 33e58a1..43faa18 100644
--- a/src/builder/b-nspawn.c
+++ b/src/builder/b-nspawn.c
@@ -336,6 +336,7 @@ skip:
saib_queue_task_status_update(ns->sp, ns->spm, ns->task->uuid,
(unsigned int)ns->retcode,
SAI_TASK_REASON_DESTROYED);
+ saib_reassess_idle_situation();
builder.ram_reserved_kib -= ns->task->est_peak_mem_kib;
builder.disk_reserved_kib -= ns->task->est_disk_kib;
@@ -361,6 +362,7 @@ fail:
saib_queue_task_status_update(ns->sp, ns->spm, ns->task->uuid,
(unsigned int)ns->retcode,
SAI_TASK_REASON_DESTROYED);
+ saib_reassess_idle_situation();
builder.ram_reserved_kib -= ns->task->est_peak_mem_kib;
builder.disk_reserved_kib -= ns->task->est_disk_kib;
diff --git a/src/builder/b-power.c b/src/builder/b-power.c
index 5238ac2..6d0f9f3 100644
--- a/src/builder/b-power.c
+++ b/src/builder/b-power.c
@@ -31,31 +31,11 @@
extern struct lws_spawn_piped *lsp_suspender;
-/*
- * This is used to check with sai-power if we should stay up (due to the power
- * being turned on manually)
- */
-
-
-LWS_SS_USER_TYPEDEF
- char payload[200];
- size_t size;
- size_t pos;
-} saib_power_stay_t;
-
-
-static lws_ss_state_return_t
-saib_power_stay_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
+int
+saib_reassess_idle_situation()
{
char in_use = 0;
- if (len < 1)
- return 0;
-
- builder.stay = *buf == '1';
-
-// lwsl_err("%s: stay %d\n", __func__, builder.stay);
-
if (builder.stay) {
/*
* We need to deal with finding we have been manually powered-on.
@@ -63,57 +43,86 @@ saib_power_stay_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
*/
lws_sul_cancel(&builder.sul_idle);
- // lwsl_warn("%s: %s: stay applied: cancelled idle grace time\n",
- // __func__, builder.host);
- } else {
-
- /*
- * If any plat on this builder has tasks, just
- * leave it
- */
- lws_start_foreach_dll_safe(struct lws_dll2 *, mp, mp1,
- builder.sai_plat_owner.head) {
- struct sai_plat *sp = lws_container_of(mp, struct sai_plat,
- sai_plat_list);
+ lwsl_warn("%s: %s: stay applied: cancelled idle grace time\n",
+ __func__, builder.host);
- if (sp->nspawn_owner.head) {
- lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, sp->nspawn_owner.head) {
- struct sai_nspawn *xns = lws_container_of(d, struct sai_nspawn, list);
+ return 0;
+ }
- lwsl_notice("%s: ongoing task: %s\n", __func__, xns->task->uuid);
+ /*
+ * If any plat on this builder has tasks, just leave it
+ */
+ lws_start_foreach_dll_safe(struct lws_dll2 *, mp, mp1,
+ builder.sai_plat_owner.head) {
+ struct sai_plat *sp = lws_container_of(mp, struct sai_plat,
+ sai_plat_list);
- } lws_end_foreach_dll_safe(d, d1);
+ if (sp->nspawn_owner.head) {
+ lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
+ sp->nspawn_owner.head) {
+ struct sai_nspawn *xns = lws_container_of(d,
+ struct sai_nspawn, list);
- in_use = 1;
- }
+ lwsl_notice("%s: ongoing task: %s\n", __func__,
+ xns->task->uuid);
- } lws_end_foreach_dll_safe(mp, mp1);
+ } lws_end_foreach_dll_safe(d, d1);
- if (in_use) {
- lwsl_warn("%s: cancelling idle grace time as ongoing task steps\n", __func__);
lws_sul_cancel(&builder.sul_idle);
-
- return 0;
+ in_use = 1;
}
- /*
- * if no ongoing tasks, and we want to go OFF, then start
- * the idle grace timer. This will get cancelled if
- * we start a task during the grace time, otherwise it will
- * expire and do the power-off or suspend
- */
-
- if (lws_dll2_is_detached(&builder.sul_idle.list)) {
- lwsl_warn("%s: %s: no stay: starting idle grace time\n",
- __func__, builder.host);
- lws_sul_schedule(builder.context, 0, &builder.sul_idle,
- sul_idle_cb, SAI_IDLE_GRACE_US);
- }
+ } lws_end_foreach_dll_safe(mp, mp1);
+
+ if (in_use) {
+ lwsl_warn("%s: cancelling idle grace time as ongoing task steps\n", __func__);
+
+ return 0;
+ }
+
+ /*
+ * if no ongoing tasks, and we want to go OFF, then start
+ * the idle grace timer. This will get cancelled if
+ * we start a task during the grace time, otherwise it will
+ * expire and do the power-off or suspend
+ */
+
+ if (lws_dll2_is_detached(&builder.sul_idle.list)) {
+ lwsl_warn("%s: %s: no stay: starting idle grace time\n",
+ __func__, builder.host);
+ lws_sul_schedule(builder.context, 0, &builder.sul_idle,
+ sul_idle_cb, SAI_IDLE_GRACE_US);
}
return 0;
}
+/*
+ * This is used to check with sai-power if we should stay up (due to the power
+ * being turned on manually)
+ */
+
+
+LWS_SS_USER_TYPEDEF
+ char payload[200];
+ size_t size;
+ size_t pos;
+} saib_power_stay_t;
+
+
+static lws_ss_state_return_t
+saib_power_stay_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
+{
+ if (len < 1)
+ return 0;
+
+ builder.stay = *buf != '0';
+
+ saib_reassess_idle_situation();
+
+ return 0;
+}
+
LWS_SS_INFO("sai_power", saib_power_stay_t)
.rx = saib_power_stay_rx,
};
@@ -352,6 +361,8 @@ saib_power_init(void)
return 1;
}
+ saib_reassess_idle_situation();
+
return 0;
}
diff --git a/src/builder/b-private.h b/src/builder/b-private.h
index 52b0426..d868c6c 100644
--- a/src/builder/b-private.h
+++ b/src/builder/b-private.h
@@ -330,6 +330,8 @@ extern int
saib_deletion_init(const char *argv0);
extern void
suspender_destroy(void);
+int
+saib_reassess_idle_situation(void);
#if defined(__APPLE__)
int
diff --git a/src/builder/b-suspender.c b/src/builder/b-suspender.c
index d88cef7..4f7b60e 100644
--- a/src/builder/b-suspender.c
+++ b/src/builder/b-suspender.c
@@ -64,7 +64,7 @@ saib_suspender_get_pipe(void)
#if defined(__linux__)
int fd = lws_spawn_get_fd_stdxxx(lsp_suspender, 0);
#else
-#if defined(__APPLE__)
+#if defined(__APPLE__) || defined(__NetBSD__)
int fd = builder.pipe_suspender_wr;
#else
int fd = 2;
@@ -172,7 +172,7 @@ saib_suspender_fork(const char *path)
return 1;
}
#endif
-#if defined(__APPLE__)
+#if defined(__APPLE__) || defined(__NetBSD__)
{
int pfd[2];
pid_t pid;
diff --git a/src/builder/b-task.c b/src/builder/b-task.c
index d5e8d94..f26b948 100644
--- a/src/builder/b-task.c
+++ b/src/builder/b-task.c
@@ -721,6 +721,7 @@ saib_consider_allocating_task(struct sai_plat_server *spm, lws_struct_args_t *a,
lwsl_warn("%s: server offered task that's already running\n", __func__);
saib_queue_task_status_update(sp, spm, task->uuid, 0,
SAI_TASK_REASON_DUPE);
+ saib_reassess_idle_situation();
return 0;
}
@@ -736,6 +737,7 @@ saib_consider_allocating_task(struct sai_plat_server *spm, lws_struct_args_t *a,
if (saib_queue_task_status_update(sp, spm, task->uuid, 0,
SAI_TASK_REASON_BUSY))
return -1;
+ saib_reassess_idle_situation();
return 0;
}
@@ -775,6 +777,7 @@ saib_consider_allocating_task(struct sai_plat_server *spm, lws_struct_args_t *a,
lws_dll2_add_tail(&ns->list, &sp->nspawn_owner);
+ saib_reassess_idle_situation();
/*
* If we're using sai-device, sort out the log proxy
diff --git a/src/power/CMakeLists.txt b/src/power/CMakeLists.txt
index 2842700..5942b29 100644
--- a/src/power/CMakeLists.txt
+++ b/src/power/CMakeLists.txt
@@ -7,6 +7,8 @@ set(SRCS
p-comms.c
p-smartplug.c
p-api.c
+ p-utils.c
+ p-tasmota-monitor.c
../common/struct-metadata.c
)
diff --git a/src/power/p-api.c b/src/power/p-api.c
index d16c255..2dbde16 100644
--- a/src/power/p-api.c
+++ b/src/power/p-api.c
@@ -49,21 +49,24 @@ saip_sul_action_power_off(struct lws_sorted_usec_list *sul)
{
saip_server_plat_t *sp = lws_container_of(sul,
saip_server_plat_t, sul_delay_off);
+ lws_ss_state_return_t r;
+ saip_pcon_t *pc;
- if (!sp->power_off_url) {
- lwsl_notice("%s: no power_off_url for %s\n", __func__, sp->host);
- return;
- }
- if (!sp->ss_tasmota_off) {
- lwsl_notice("%s: no power_off ss for %s\n", __func__, sp->host);
+ if (!sp->pcon_list.owner) {
+ lwsl_notice("%s: no power-controller ss for %s\n", __func__, sp->host);
return;
}
- lwsl_warn("%s: powering off host %s\n", __func__, sp->host);
+ pc = lws_container_of(sp->pcon_list.owner, saip_pcon_t,
+ controlled_plats_owner);
+
saip_notify_server_power_state(sp->host, 0, 1);
- if (lws_ss_client_connect(sp->ss_tasmota_off))
- lwsl_ss_err(sp->ss_tasmota_off, "failed to connect tasmota OFF secure stream");
+ lwsl_warn("%s: powering OFF host %s via power-control %s\n", __func__, sp->host, pc->name);
+
+ r = lws_ss_client_connect(pc->ss_tasmota_off);
+ if (r)
+ lwsl_ss_err(pc->ss_tasmota_off, "failed to connect tasmota OFF secure stream: %d", r);
}
saip_server_plat_t *
@@ -87,8 +90,8 @@ find_platform(struct sai_power *pwr, const char *host)
void
saip_notify_server_stay_state(const char *plat_name, int stay_on)
{
- saip_server_t *sps;
sai_stay_state_update_t *ssu;
+ saip_server_t *sps;
/* Find the first (usually only) configured sai-server connection */
if (!power.sai_server_owner.head) {
@@ -113,6 +116,7 @@ saip_notify_server_stay_state(const char *plat_name, int stay_on)
/* The per-connection user object for the server link is a saip_server_link_t */
{
saip_server_link_t *pss = (saip_server_link_t *)lws_ss_to_user_object(sps->ss);
+
lws_dll2_add_tail(&ssu->list, &pss->stay_state_update_owner);
}
@@ -133,28 +137,19 @@ saip_set_stay(const char *builder_name, int stay_on)
if (!sp)
return;
+ sps = lws_container_of(power.sai_server_owner.head, saip_server_t, list);
+ pss = (saip_server_link_t *)lws_ss_to_user_object(sps->ss);
sp->stay = (char)stay_on;
- saip_notify_server_stay_state(builder_name, stay_on);
+ saip_notify_server_stay_state(builder_name, stay_on | sp->needed);
- if (stay_on) {
- if (sp->power_on_mac) {
- saip_notify_server_power_state(sp->host, 1, 0);
- write(lws_spawn_get_fd_stdxxx(lsp_wol, 0),
- sp->power_on_mac, strlen(sp->power_on_mac));
- }
- if (sp->power_on_url) {
- if (lws_ss_client_connect(sp->ss_tasmota_on))
- lwsl_notice("%s: tasmota connect failed\n", __func__);
- saip_notify_server_power_state(sp->host, 1, 0);
- }
- } else {
+ if (stay_on | sp->needed)
+ saip_builder_bringup(sps, sp, pss);
+ else
/*
* power-off is delayed, so we just set the stay flag...
* but let's cancel any pending power-off
*/
lws_sul_cancel(&sp->sul_delay_off);
- }
-
/* Find the first (usually only) configured sai-server connection */
if (!power.sai_server_owner.head) {
@@ -162,9 +157,6 @@ saip_set_stay(const char *builder_name, int stay_on)
return;
}
- sps = lws_container_of(power.sai_server_owner.head, saip_server_t, list);
- pss = (saip_server_link_t *)lws_ss_to_user_object(sps->ss);
-
saip_queue_stay_info(sps, sp, pss);
}
@@ -280,7 +272,7 @@ local_srv_state(void *userobj, void *sh, lws_ss_constate_t state,
if (sp)
g->size = (size_t)lws_snprintf(g->payload, sizeof(g->payload),
- "%c", '0' + sp->stay);
+ "%c", '0' + (sp->stay | sp->needed));
else
g->size = (size_t)lws_snprintf(g->payload, sizeof(g->payload),
"unknown host %s", pn);
@@ -308,16 +300,20 @@ local_srv_state(void *userobj, void *sh, lws_ss_constate_t state,
sp->stay = 1;
goto bail;
}
- if (!sp->power_on_url) {
- g->size = (size_t)lws_snprintf(g->payload, sizeof(g->payload),
- "no power-on-url entry for %s", pn);
- goto bail;
- }
- if (lws_ss_client_connect(sp->ss_tasmota_on)) {
- lwsl_ss_err(sp->ss_tasmota_off, "failed to connect tasmota ON secure stream");
+ if (sp->pcon_list.owner) {
+ saip_pcon_t *pc = lws_container_of(sp->pcon_list.owner,
+ saip_pcon_t,
+ controlled_plats_owner);
+ if (lws_ss_client_connect(pc->ss_tasmota_on)) {
+ lwsl_ss_err(pc->ss_tasmota_on, "failed to connect tasmota ON secure stream");
+ g->size = (size_t)lws_snprintf(g->payload, sizeof(g->payload),
+ "power-on ss failed create %s", sp->host);
+ goto bail;
+ }
+ } else {
g->size = (size_t)lws_snprintf(g->payload, sizeof(g->payload),
- "power-on ss failed create %s", sp->host);
+ "no power-controller entry for %s", pn);
goto bail;
}
diff --git a/src/power/p-comms.c b/src/power/p-comms.c
index 8508078..afd8fa0 100644
--- a/src/power/p-comms.c
+++ b/src/power/p-comms.c
@@ -107,20 +107,25 @@ saip_queue_stay_info(saip_server_t *sps, saip_server_plat_t *sp, saip_server_lin
}
int
-saip_builder_bringup(saip_server_t *sps, saip_server_plat_t *sp, saip_server_link_t *pss)
+saip_builder_bringup(saip_server_t *sps, saip_server_plat_t *sp,
+ saip_server_link_t *pss)
{
saip_notify_server_power_state(sp->name, 1, 0);
- if (!strcmp(sp->power_on_type, "wol")) {
+ if (sp->power_on_type && !strcmp(sp->power_on_type, "wol")) {
lwsl_notice("%s: triggering WOL\n", __func__);
write(lws_spawn_get_fd_stdxxx(lsp_wol, 0),
sp->power_on_mac, strlen(sp->power_on_mac));
}
- if (!strcmp(sp->power_on_type, "tasmota")) {
- lwsl_ss_notice(sp->ss_tasmota_on, "starting tasmota");
- if (lws_ss_client_connect(sp->ss_tasmota_on))
- lwsl_ss_err(sp->ss_tasmota_on, "failed to connect tasmota ON secure stream");
+ if (sp->pcon_list.owner) {
+ saip_pcon_t *pc = lws_container_of(sp->pcon_list.owner,
+ saip_pcon_t,
+ controlled_plats_owner);
+
+ lwsl_ss_notice(pc->ss_tasmota_on, "starting tasmota");
+ if (lws_ss_client_connect(pc->ss_tasmota_on))
+ lwsl_ss_err(pc->ss_tasmota_on, "failed to connect tasmota ON secure stream");
}
return saip_queue_stay_info(sps, sp, pss);
@@ -137,8 +142,8 @@ saip_m_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
lws_struct_args_t a;
struct lejp_ctx ctx;
- lwsl_info("%s: len %d, flags: %d (saip_server_t %p)\n", __func__, (int)len, flags, (void *)sps);
- lwsl_hexdump_info(buf, len);
+ lwsl_notice("%s: len %d, flags: %d (saip_server_t %p)\n", __func__, (int)len, flags, (void *)sps);
+ lwsl_hexdump_notice(buf, len);
memset(&a, 0, sizeof(a));
a.map_st[0] = lsm_schema_stay;
@@ -149,6 +154,8 @@ saip_m_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
if (lejp_parse(&ctx, (uint8_t *)buf, (int)len) >= 0 && a.dest) {
sai_stay_t *stay = (sai_stay_t *)a.dest;
+ // {"schema":"com.warmcat.sai.power.stay","builder_name":"ubuntu_rpi4","stay_on":1}
+
lwsl_warn("%s: received stay %s: %d\n", __func__, stay->builder_name, stay->stay_on);
saip_set_stay(stay->builder_name, stay->stay_on);
@@ -157,6 +164,8 @@ saip_m_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
}
lwsac_free(&a.ac);
+ /* starting position is that no server-plat is needed */
+
lws_start_foreach_dll(struct lws_dll2 *, px, sps->sai_plat_owner.head) {
saip_server_plat_t *sp = lws_container_of(px, saip_server_plat_t, list);
sp->needed = 0;
@@ -189,10 +198,9 @@ saip_m_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
lws_start_foreach_dll(struct lws_dll2 *, px1, sp->dependencies_owner.head) {
saip_server_plat_t *sp1 = lws_container_of(px1, saip_server_plat_t, dependencies_list);
- if (!strcmp(sp1->name, plat)) {
- sp->needed = 2;
- break;
- }
+ lwsl_notice("%s: setting %s as needed dep\n", __func__, sp1->name);
+ sp1->needed = 2;
+ saip_set_stay(sp1->name, sp1->stay);
} lws_end_foreach_dll(px1);
@@ -206,6 +214,27 @@ saip_m_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
} lws_end_foreach_dll(px);
}
+ /*
+ * Cascade dependencies up the platforms
+ */
+
+ lws_start_foreach_dll(struct lws_dll2 *, px, sps->sai_plat_owner.head) {
+ saip_server_plat_t *sp = lws_container_of(px, saip_server_plat_t, list);
+
+ if (sp->needed) {
+ lws_start_foreach_dll(struct lws_dll2 *, py, sp->dependencies_owner.head) {
+ saip_server_plat_t *spd = lws_container_of(py, saip_server_plat_t, dependencies_list);
+
+ spd->needed |= 2;
+
+ } lws_end_foreach_dll(py);
+ }
+ } lws_end_foreach_dll(px);
+
+ /*
+ * Bringup any directly needed or needed by dependency builders
+ */
+
lws_start_foreach_dll(struct lws_dll2 *, px, sps->sai_plat_owner.head) {
saip_server_plat_t *sp = lws_container_of(px, saip_server_plat_t, list);
@@ -252,7 +281,6 @@ saip_m_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len,
if (!js)
lwsl_ss_warn(lws_ss_from_user(pss), "Failed to serialize managed builder");
else
- /* crashes here walking pmb */
if (lws_struct_json_serialize(js, buf, *len, len) == LSJS_RESULT_FINISH)
*flags = LWSSS_FLAG_SOM | LWSSS_FLAG_EOM;
diff --git a/src/power/p-conf.c b/src/power/p-conf.c
index 2cd50a6..11ffd96 100644
--- a/src/power/p-conf.c
+++ b/src/power/p-conf.c
@@ -33,6 +33,10 @@ static const char * const paths_global[] = {
"perms",
"wol-if",
"servers[].url",
+ "servers[].power-control[].name",
+ "servers[].power-control[].type",
+ "servers[].power-control[].url",
+ "servers[].power-control[]",
"servers[].platforms[].name",
"servers[].platforms[].host",
"servers[].platforms[].depends",
@@ -43,6 +47,7 @@ static const char * const paths_global[] = {
"servers[].platforms[].power-off.type",
"servers[].platforms[].power-off.url",
"servers[].platforms[].power-off",
+ "servers[].platforms[].power-control",
"servers[].platforms[]",
"servers[]"
};
@@ -51,6 +56,10 @@ enum enum_paths_global {
LEJPM_PERMS,
LEJPM_WOL_IF,
LEJPM_SERVERS_URL,
+ LEJPM_SERVERS_POWER_CONTROL_NAME,
+ LEJPM_SERVERS_POWER_CONTROL_TYPE,
+ LEJPM_SERVERS_POWER_CONTROL_URL,
+ LEJPM_SERVERS_POWER_CONTROL,
LEJPM_SERVERS_PLATFORMS_NAME,
LEJPM_SERVERS_PLATFORMS_HOST,
LEJPM_SERVERS_PLATFORMS_DEPENDS,
@@ -61,6 +70,7 @@ enum enum_paths_global {
LEJPM_SERVERS_PLATFORMS_POWER_OFF_TYPE,
LEJPM_SERVERS_PLATFORMS_POWER_OFF_URL,
LEJPM_SERVERS_PLATFORMS_POWER_OFF,
+ LEJPM_SERVERS_PLATFORMS_POWER_CONTROL,
LEJPM_SERVERS_PLATFORMS,
LEJPM_SERVERS
};
@@ -69,6 +79,7 @@ static signed char
saip_conf_global_cb(struct lejp_ctx *ctx, char reason)
{
struct jpargs *a = (struct jpargs *)ctx->user;
+ saip_pcon_t *pc;
const char **pp = NULL;
#if 0
@@ -100,6 +111,18 @@ saip_conf_global_cb(struct lejp_ctx *ctx, char reason)
&a->power->sai_server_owner);
break;
+ case LEJPM_SERVERS_POWER_CONTROL:
+ /*
+ * Create the saip_pcon_t object and bind to the server
+ */
+ a->sai_pcon = lwsac_use_zero(&a->power->ac_conf_head,
+ sizeof(*a->sai_pcon), 4096);
+ if (!a->sai_pcon)
+ return -1;
+
+ lws_dll2_add_tail(&a->sai_pcon->list,
+ &a->power->sai_pcon_owner);
+ break;
case LEJPM_SERVERS_PLATFORMS:
/*
@@ -138,6 +161,18 @@ saip_conf_global_cb(struct lejp_ctx *ctx, char reason)
pp = &a->power->wol_if;
break;
+ case LEJPM_SERVERS_POWER_CONTROL_NAME:
+ pp = &a->sai_pcon->name;
+ break;
+
+ case LEJPM_SERVERS_POWER_CONTROL_TYPE:
+ pp = &a->sai_pcon->type;
+ break;
+
+ case LEJPM_SERVERS_POWER_CONTROL_URL:
+ pp = &a->sai_pcon->url;
+ break;
+
case LEJPM_SERVERS_URL:
pp = &a->sai_server->url;
lwsl_user("%s: server url %.*s\n", __func__, ctx->npos, ctx->buf);
@@ -175,6 +210,14 @@ saip_conf_global_cb(struct lejp_ctx *ctx, char reason)
pp = &a->sai_server_plat->power_off_url;
break;
+ case LEJPM_SERVERS_PLATFORMS_POWER_CONTROL:
+ pc = saip_pcon_by_name(a->power, ctx->buf);
+ if (pc)
+ lws_dll2_add_tail(&a->sai_server_plat->pcon_list,
+ &pc->controlled_plats_owner);
+
+ return 0;
+
default:
return 0;
}
diff --git a/src/power/p-private.h b/src/power/p-private.h
index 822e39a..2388ddc 100644
--- a/src/power/p-private.h
+++ b/src/power/p-private.h
@@ -52,19 +52,53 @@ typedef enum {
} cursor_phase_t;
+typedef struct tasmota_data {
+ unsigned int voltage_v;
+ unsigned int current_ma;
+ unsigned int active_power_w;
+ unsigned int apparent_power_va;
+ unsigned int reactive_power_var;
+ unsigned int power_factor_scaled_1000;
+ unsigned int energy_today_wh;
+ unsigned int energy_yesterday_wh;
+ unsigned int energy_total_wh;
+} tasmota_data_t;
+
+typedef struct tasmota_parse {
+ tasmota_data_t td;
+ struct lws_tokenize ts;
+ uint16_t match;
+ uint8_t s;
+} tasmota_parse_t;
+
+typedef struct saip_pcon {
+ struct lws_dll2 list;
+
+ lws_dll2_owner_t controlled_plats_owner; /* saip_server_plat_t */
+
+ const char *name;
+ const char *type;
+ const char *url;
+ char url_on[128];
+ char url_off[128];
+ char url_monitor[128];
+
+ struct lws_ss_handle *ss_tasmota_on;
+ struct lws_ss_handle *ss_tasmota_off;
+ struct lws_ss_handle *ss_tasmota_monitor;
+} saip_pcon_t;
struct saip_ws_pss;
typedef struct saip_server_plat {
- struct lws_dll2 list;
- lws_dll2_owner_t dependencies_owner;
+ lws_dll2_t list;
lws_dll2_t dependencies_list;
+ lws_dll2_t pcon_list; /* saip_pcon->controlled_plats_owner */
- lws_sorted_usec_list_t sul_delay_off;
+ lws_dll2_owner_t dependencies_owner; /* saip_server_plat_t->dependencies_list */
- struct lws_ss_handle *ss_tasmota_on;
- struct lws_ss_handle *ss_tasmota_off;
+ lws_sorted_usec_list_t sul_delay_off;
const char *name;
const char *host;
@@ -81,7 +115,7 @@ typedef struct saip_server_plat {
} saip_server_plat_t;
typedef struct saip_server {
- struct lws_dll2 list;
+ lws_dll2_t list;
lws_dll2_owner_t sai_plat_owner; /* list of platforms we offer */
@@ -98,6 +132,8 @@ typedef struct saip_server {
struct sai_power {
lws_dll2_owner_t sai_server_owner; /* servers we connect to */
+ lws_dll2_owner_t sai_pcon_owner; /* saip_pcon_t */
+
struct lwsac *ac_conf_head;
struct lws_context *context;
struct lws_vhost *vhost;
@@ -125,6 +161,7 @@ struct jpargs {
saip_server_t *sai_server;
saip_server_plat_t *sai_server_plat;
+ saip_pcon_t *sai_pcon;
sai_plat_server_ref_t *mref;
@@ -158,4 +195,13 @@ saip_notify_server_power_state(const char *plat_name, int up, int down);
void
saip_set_stay(const char *builder_name, int stay_on);
int
-saip_queue_stay_info(saip_server_t *sps, saip_server_plat_t *sp, saip_server_link_t *pss);
+saip_queue_stay_info(saip_server_t *sps, saip_server_plat_t *sp,
+ saip_server_link_t *pss);
+saip_pcon_t *
+saip_pcon_by_name(struct sai_power *power, const char *name);
+
+int
+parse_tasmota_status(tasmota_parse_t *tp);
+int
+saip_builder_bringup(saip_server_t *sps, saip_server_plat_t *sp,
+ saip_server_link_t *pss);
diff --git a/src/power/p-sai.c b/src/power/p-sai.c
index cf9ee95..cab332a 100644
--- a/src/power/p-sai.c
+++ b/src/power/p-sai.c
@@ -369,26 +369,26 @@ int main(int argc, const char **argv)
return 1;
}
- info.wol_if = power.wol_if;
+ info.wol_if = power.wol_if;
if (power.wol_if)
lwsl_notice("%s: WOL bound to interface %s\n", __func__, power.wol_if);
info.pprotocols = pprotocols;
//info.uid = 883;
- info.pt_serv_buf_size = 32 * 1024;
- info.rlimit_nofile = 20000;
- info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
+ info.pt_serv_buf_size = 32 * 1024;
+ info.rlimit_nofile = 20000;
+ info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
signal(SIGINT, sigint_handler);
- info.pss_policies_json = default_ss_policy;
- info.fd_limit_per_thread = 1 + 256 + 1;
+ info.pss_policies_json = default_ss_policy;
+ info.fd_limit_per_thread = 1 + 256 + 1;
/* hook up our lws_system state notifier */
- nl.name = "sai-power";
- nl.notify_cb = app_system_state_nf;
- info.register_notifier_list = app_notifier_list;
+ nl.name = "sai-power";
+ nl.notify_cb = app_system_state_nf;
+ info.register_notifier_list = app_notifier_list;
/* create the lws context */
@@ -406,25 +406,35 @@ int main(int argc, const char **argv)
/* let's create any needed tasmota ss */
- lws_start_foreach_dll(struct lws_dll2 *, px, power.sai_server_owner.head) {
- saip_server_t *s = lws_container_of(px, saip_server_t, list);
-
- lws_start_foreach_dll(struct lws_dll2 *, px1, s->sai_plat_owner.head) {
- saip_server_plat_t *sp = lws_container_of(px1, saip_server_plat_t, list);
-
- if (!strcmp(sp->power_on_type, "tasmota") && sp->power_on_url &&
- lws_ss_create(power.context, 0, &ssi_saip_smartplug_t,
- (void *)sp->power_on_url, &sp->ss_tasmota_on, NULL, NULL)) {
- lwsl_err("%s: failed to create ON smartplug secure stream\n", __func__);
- }
-
- if (!strcmp(sp->power_off_type, "tasmota") && sp->power_off_url &&
- lws_ss_create(power.context, 0, &ssi_saip_smartplug_t,
- (void *)sp->power_off_url, &sp->ss_tasmota_off, NULL, NULL)) {
- lwsl_err("%s: failed to create OFF smartplug secure stream\n", __func__);
- }
+ lws_start_foreach_dll(struct lws_dll2 *, px, power.sai_pcon_owner.head) {
+ saip_pcon_t *pc = lws_container_of(px, saip_pcon_t, list);
+
+ if (!strcmp(pc->type, "tasmota") && pc->url) {
+ lws_snprintf(pc->url_on, sizeof(pc->url_on),
+ "%s/cm?cmnd=Power%%20On", pc->url);
+ if (lws_ss_create(power.context, 0, &ssi_saip_smartplug_t,
+ (void *)pc->url_on,
+ &pc->ss_tasmota_on, NULL, NULL))
+ lwsl_err("%s: %s: failed to create ON smartplug secure stream %s\n",
+ __func__, pc->name, pc->url_on);
+
+ lws_snprintf(pc->url_off, sizeof(pc->url_off),
+ "%s/cm?cmnd=Power%%20Off", pc->url);
+ if (lws_ss_create(power.context, 0, &ssi_saip_smartplug_t,
+ (void *)pc->url_off,
+ &pc->ss_tasmota_off, NULL, NULL))
+ lwsl_err("%s: %s: failed to create OFF smartplug secure stream %s\n",
+ __func__, pc->name, pc->url_off);
+
+ lws_snprintf(pc->url_monitor, sizeof(pc->url_monitor),
+ "%s?m=1", pc->url);
+ if (lws_ss_create(power.context, 0, &ssi_saip_smartplug_t,
+ (void *)pc->url_monitor,
+ &pc->ss_tasmota_monitor, NULL, NULL))
+ lwsl_err("%s: %s: failed to create MONITOR smartplug secure stream %s\n",
+ __func__, pc->name, pc->url_monitor);
+ }
- } lws_end_foreach_dll(px1);
} lws_end_foreach_dll(px);
diff --git a/src/power/p-tasmota-monitor.c b/src/power/p-tasmota-monitor.c
new file mode 100644
index 0000000..a6ebcad
--- /dev/null
+++ b/src/power/p-tasmota-monitor.c
@@ -0,0 +1,156 @@
+/*
+ * sai-power com-warmcat-sai client protocol implementation
+ *
+ * Copyright (C) 2019 - 2025 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <libwebsockets.h>
+#include <string.h>
+#include <signal.h>
+
+#include "p-private.h"
+
+static const char *tokens[] = {
+ "Voltage",
+ "Current",
+ "Active",
+ "Power",
+ "Apparent",
+ "Reactive",
+ "Factor",
+ "Energy",
+ "Today",
+ "Yesterday",
+ "Total",
+};
+
+enum {
+ TOKORD_VOLTAGE,
+ TOKORD_CURRENT,
+ TOKORD_ACTIVE,
+ TOKORD_POWER,
+ TOKORD_APPARENT,
+ TOKORD_REACTIVE,
+ TOKORD_FACTOR,
+ TOKORD_ENERGY,
+ TOKORD_TODAY,
+ TOKORD_YESTERDAY,
+ TOKORD_TOTAL
+};
+
+int
+parse_tasmota_status(tasmota_parse_t *tp)
+{
+ lws_tokenize_elem e;
+ unsigned int *i;
+ char *p;
+ int n;
+
+ do {
+ e = lws_tokenize(&tp->ts);
+
+ if (e == LWS_TOKZE_DELIMITER) {
+ switch (tp->ts.token[0]) {
+ case '<':
+ tp->s |= 1u;
+ break;
+ case '>':
+ tp->s &= (uint8_t)~1u;
+ continue;
+ case '{':
+ tp->s |= 2u;
+ break;
+ case '}':
+ tp->s &= (uint8_t)~2u;
+ continue;
+ case '&':
+ tp->s |= 4u;
+ break;
+ case ';':
+ tp->s &= (uint8_t)~4u;
+ continue;
+ }
+ }
+
+ if (tp->s)
+ continue;
+
+ switch (e) {
+ case LWS_TOKZE_ENDED:
+ return 1;
+
+ case LWS_TOKZE_TOKEN:
+ for (n = 0; n < (int)LWS_ARRAY_SIZE(tokens); n++) {
+ if (strlen(tokens[n]) == tp->ts.token_len &&
+ !strcmp(tokens[n], tp->ts.token)) {
+ tp->match = (uint16_t)((tp->match << 8) | n);
+ break;
+ }
+ }
+
+ if (n == LWS_ARRAY_SIZE(tokens))
+ continue;
+ break;
+
+ case LWS_TOKZE_INTEGER:
+ if ((tp->match & 0xff) == TOKORD_VOLTAGE)
+ tp->td.voltage_v = (unsigned int)atoi(tp->ts.token);
+ if ((tp->match >> 8) == TOKORD_ACTIVE && (tp->match & 0xff) == TOKORD_POWER)
+ tp->td.active_power_w = (unsigned int)atoi(tp->ts.token);
+ if ((tp->match >> 8) == TOKORD_APPARENT && (tp->match & 0xff) == TOKORD_POWER)
+ tp->td.apparent_power_va = (unsigned int)atoi(tp->ts.token);
+ if ((tp->match >> 8) == TOKORD_REACTIVE && (tp->match & 0xff) == TOKORD_POWER)
+ tp->td.reactive_power_var = (unsigned int)atoi(tp->ts.token);
+ break;
+
+ case LWS_TOKZE_FLOAT:
+ i = NULL;
+
+ if ((tp->match & 0xff) == TOKORD_CURRENT)
+ i = &tp->td.current_ma;
+ if ((tp->match >> 8) == TOKORD_POWER && (tp->match & 0xff) == TOKORD_FACTOR)
+ i = &tp->td.power_factor_scaled_1000;
+ if ((tp->match >> 8) == TOKORD_ENERGY && (tp->match & 0xff) == TOKORD_TODAY)
+ i = &tp->td.energy_today_wh;
+ if ((tp->match >> 8) == TOKORD_ENERGY && (tp->match & 0xff) == TOKORD_YESTERDAY)
+ i = &tp->td.energy_yesterday_wh;
+ if ((tp->match >> 8) == TOKORD_ENERGY && (tp->match & 0xff) == TOKORD_TOTAL)
+ i = &tp->td.energy_total_wh;
+
+ if (i) {
+ *i = 1000 * (unsigned int)atoi(tp->ts.token);
+ p = strchr(tp->ts.token, '.');
+ if (p++) {
+ static const unsigned int mu[] = { 0, 100, 10, 1 };
+
+ n = (int)strlen(p);
+ if (n > 3)
+ n = 3;
+ *i += (unsigned int)atoi(p) * mu[n];
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ } while (e > 0);
+
+ return e;
+}
+
diff --git a/src/power/p-utils.c b/src/power/p-utils.c
new file mode 100644
index 0000000..6846dc8
--- /dev/null
+++ b/src/power/p-utils.c
@@ -0,0 +1,41 @@
+/*
+ * sai-power com-warmcat-sai client protocol implementation
+ *
+ * Copyright (C) 2019 - 2025 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <libwebsockets.h>
+#include <string.h>
+#include <signal.h>
+
+#include "p-private.h"
+
+
+saip_pcon_t *
+saip_pcon_by_name(struct sai_power *power, const char *name)
+{
+ lws_start_foreach_dll(struct lws_dll2 *, p, power->sai_pcon_owner.head) {
+ saip_pcon_t *pc = lws_container_of(p, saip_pcon_t, list);
+
+ if (!strcmp(pc->name, name))
+ return pc;
+
+ } lws_end_foreach_dll(p);
+
+ return NULL;
+}
diff --git a/src/server/s-comms.c b/src/server/s-comms.c
index ee3f039..59a36ca 100644
--- a/src/server/s-comms.c
+++ b/src/server/s-comms.c
@@ -437,7 +437,6 @@ s_callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user,
if (!strcmp((char *)start, "/power")) {
lwsl_notice("%s: ESTABLISHED: power connection\n", __func__);
- pss->wsi = wsi;
pss->is_power = 1;
lws_dll2_add_head(&pss->same, &vhd->sai_powers);
sais_platforms_with_tasks_pending(vhd);
@@ -521,6 +520,12 @@ s_callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user,
break;
}
+ if (pss->is_power || pss->stay_owner.head) {
+ lwsl_notice("%s: going down power tx path\n", __func__);
+
+ return sais_power_tx(vhd, pss, buf, sizeof(buf));
+ }
+
return sais_ws_json_tx_builder(vhd, pss, buf, sizeof(buf));
default:
diff --git a/src/server/s-power.c b/src/server/s-power.c
index b5c48c5..a941e3f 100644
--- a/src/server/s-power.c
+++ b/src/server/s-power.c
@@ -73,10 +73,13 @@ sais_power_rx(struct vhd *vhd, struct pss *pss, uint8_t *buf,
switch (a.top_schema_index) {
case 0: /* powerstate */
ps = (sai_power_state_t *)a.dest;
+ lwsl_notice("%s: powerstate received: %d %d\n", __func__, ps->powering_up, ps->powering_down);
if (ps->powering_up) {
lwsl_notice("sai-power is powering up: %s\n", ps->host);
sais_set_builder_power_state(vhd, ps->host, 1, 0);
- } else if (ps->powering_down) {
+ break;
+ }
+ if (ps->powering_down) {
lwsl_notice("sai-power is powering down: %s\n", ps->host);
sais_set_builder_power_state(vhd, ps->host, 0, 1);
}
@@ -150,9 +153,95 @@ sais_power_rx(struct vhd *vhd, struct pss *pss, uint8_t *buf,
break;
}
+ default:
+ lwsl_warn("%s: unknown schema\n", __func__);
+ break;
}
lwsac_free(&a.ac);
return 0;
}
+
+int
+sais_power_tx(struct vhd *vhd, struct pss *pss, uint8_t *buf, size_t bl)
+{
+ uint8_t *start = buf + LWS_PRE, *p = start, *end = p + bl - LWS_PRE - 1;
+ enum lws_write_protocol flags;
+ char diff = 0;
+ size_t w;
+ int n;
+
+ if (pss->stay_owner.head) {
+ /*
+ * Pending stay message to send to power
+ */
+ sai_stay_t *s = lws_container_of(pss->stay_owner.head,
+ sai_stay_t, list);
+ lws_struct_serialize_t *js;
+
+ js = lws_struct_json_serialize_create(lsm_schema_stay,
+ LWS_ARRAY_SIZE(lsm_schema_stay), 0, s);
+ if (!js) {
+ lwsl_warn("%s: failed to serialize stay\n", __func__);
+ return 1;
+ }
+
+ n = (int)lws_struct_json_serialize(js, p, lws_ptr_diff_size_t(end, p), &w);
+ lws_struct_json_serialize_destroy(&js);
+
+ lwsl_wsi_notice(pss->wsi, "%s: server issuing stay notice\n", __func__);
+ sai_dump_stderr((char *)start, w);
+
+ lws_dll2_remove(&s->list);
+ free(s);
+
+ flags = lws_write_ws_flags(LWS_WRITE_TEXT, 1, 1);
+
+ if (lws_write(pss->wsi, start, w, flags) < 0)
+ return -1;
+
+ lws_callback_on_writable(pss->wsi);
+ return 0;
+ }
+
+ n = 0;
+ lws_start_foreach_dll(struct lws_dll2 *, px, vhd->pending_plats.head) {
+ sais_plat_t *pl = lws_container_of(px, sais_plat_t, list);
+ size_t m;
+
+ if (n)
+ *p++ = ',';
+ m = strlen(pl->plat);
+ if (lws_ptr_diff_size_t(end, p) < m + 2)
+ break;
+ memcpy(p, pl->plat, m);
+ p += m;
+ *p = '\0';
+ n = 1;
+
+ } lws_end_foreach_dll(px);
+
+ /*
+ * Don't resend the same status over and over
+ */
+
+ if (strncmp(pss->last_power_report, (const char *)start, lws_ptr_diff_size_t(p, start) + 1)) {
+ diff = 1;
+ memcpy(pss->last_power_report, start, lws_ptr_diff_size_t(p, start) + 1);
+ }
+
+ if (diff /* && start != p */) {
+ lwsl_notice("%s: detected jobs for %.*s\n", __func__,
+ (int)lws_ptr_diff_size_t(p, start), start);
+
+ if (lws_write(pss->wsi, start, lws_ptr_diff_size_t(p, start),
+ LWS_WRITE_TEXT) < 0)
+ return -1;
+
+ lws_callback_on_writable(pss->wsi);
+ }
+
+ return 0;
+}
+
diff --git a/src/server/s-private.h b/src/server/s-private.h
index 4b9d2c1..14f1a88 100644
--- a/src/server/s-private.h
+++ b/src/server/s-private.h
@@ -210,37 +210,37 @@ typedef struct sais_plat {
} sais_plat_t;
struct vhd {
- struct lws_context *context;
- struct lws_vhost *vhost;
+ struct lws_context *context;
+ struct lws_vhost *vhost;
- struct lws_ss_handle *h_ss_websrv; /* server */
+ sais_t server;
+
+ struct lws_ss_handle *h_ss_websrv; /* server */
/* pss lists */
- struct lws_dll2_owner builders;
- struct lws_dll2_owner sai_powers;
- struct lws_dll2_owner pending_plats;
- lws_dll2_owner_t powering_up_list; /* sai_powering_up_plat_t */
+ struct lws_dll2_owner builders;
+ struct lws_dll2_owner sai_powers;
+ struct lws_dll2_owner pending_plats;
+ lws_dll2_owner_t powering_up_list; /* sai_powering_up_plat_t */
- struct lwsac *ac_plats;
+ struct lwsac *ac_plats;
- const char *sqlite3_path_lhs;
- sqlite3 *pdb_metrics;
+ const char *sqlite3_path_lhs;
+ sqlite3 *pdb_metrics;
- lws_dll2_owner_t sqlite3_cache; /* sais_sqlite_cache_t */
- lws_dll2_owner_t tasklog_cache;
- lws_sorted_usec_list_t sul_logcache;
- lws_sorted_usec_list_t sul_central; /* background task allocation sul */
- lws_sorted_usec_list_t sul_activity; /* activity broadcast sul */
+ lws_dll2_owner_t sqlite3_cache; /* sais_sqlite_cache_t */
+ lws_dll2_owner_t tasklog_cache;
+ lws_sorted_usec_list_t sul_logcache;
+ lws_sorted_usec_list_t sul_central; /* background task allocation sul */
+ lws_sorted_usec_list_t sul_activity; /* activity broadcast sul */
- lws_usec_t last_check_abandoned_tasks;
+ lws_usec_t last_check_abandoned_tasks;
- const char *notification_key;
- unsigned int task_abandoned_timeout_mins;
+ const char *notification_key;
+ unsigned int task_abandoned_timeout_mins;
unsigned int browser_viewer_count;
unsigned int viewers_are_present:1;
-
- sais_t server;
};
extern struct lws_context *
@@ -440,3 +440,6 @@ sais_metrics_db_init(struct vhd *vhd);
int
sais_metrics_db_prune(struct vhd *vhd, const char *key);
+
+int
+sais_power_tx(struct vhd *vhd, struct pss *pss, uint8_t *buf, size_t bl);
diff --git a/src/server/s-ws-builder.c b/src/server/s-ws-builder.c
index 9657609..7f7c472 100644
--- a/src/server/s-ws-builder.c
+++ b/src/server/s-ws-builder.c
@@ -298,24 +298,33 @@ sais_set_builder_power_state(struct vhd *vhd, const char *name, int up, int down
sai_power_state_t *ps = NULL;
sai_plat_t *live_builder = sais_builder_from_host(vhd, name);
- if (live_builder && up)
+ if (live_builder && up) {
+ lwsl_notice("%s: live builder so killing up\n", __func__);
up = 0;
+ }
- if (!live_builder && down)
+ if (!live_builder && down) {
+ lwsl_notice("%s: no live builder so killing down\n", __func__);
down = 0;
+ }
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
vhd->server.power_state_owner.head) {
ps = lws_container_of(p, sai_power_state_t, list);
if (!strcmp(ps->host, name)) {
- if (live_builder && ps->powering_up)
+ if (live_builder && ps->powering_up) {
+ lwsl_notice("%s: live builder so removing powering_up\n", __func__);
ps->powering_up = 0;
+ }
- if (!live_builder && ps->powering_down)
+ if (!live_builder && ps->powering_down) {
+ lwsl_notice("%s: no live builder so killing powering_down\n", __func__);
ps->powering_down = 0;
+ }
if (!ps->powering_up && !ps->powering_down) {
+ lwsl_notice("%s: nothing left to do for power state change, removing\n", __func__);
lws_dll2_remove(&ps->list);
free(ps);
}
@@ -345,7 +354,8 @@ sais_set_builder_power_state(struct vhd *vhd, const char *name, int up, int down
if (!ps->powering_up && !ps->powering_down) {
lws_dll2_remove(&ps->list);
free(ps);
- }
+ } else
+ lwsl_notice("%s: added ps with %d %d\n", __func__, up, down);
}
sais_list_builders(vhd);
@@ -1358,27 +1368,6 @@ sais_ws_json_tx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf,
goto send_json;
}
- if (pss->stay_owner.head) {
- /*
- * Pending stay message to send
- */
- sai_stay_t *s = lws_container_of(pss->stay_owner.head,
- sai_stay_t, list);
-
- js = lws_struct_json_serialize_create(lsm_schema_stay,
- LWS_ARRAY_SIZE(lsm_schema_stay), 0, s);
- if (!js)
- return 1;
-
- n = (int)lws_struct_json_serialize(js, p, lws_ptr_diff_size_t(end, p), &w);
- lws_struct_json_serialize_destroy(&js);
-
- lws_dll2_remove(&s->list);
- free(s);
-
- goto send_json;
- }
-
if (pss->rebuild_owner.head) {
/*
* Pending rebuild message to send
@@ -1444,50 +1433,6 @@ sais_ws_json_tx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf,
goto send_json;
}
- if (pss->is_power) {
- char diff = 0;
-
- n = 0;
- lws_start_foreach_dll(struct lws_dll2 *, px, vhd->pending_plats.head) {
- sais_plat_t *pl = lws_container_of(px, sais_plat_t, list);
- size_t m;
-
- if (n)
- *p++ = ',';
- m = strlen(pl->plat);
- if (lws_ptr_diff_size_t(end, p) < m + 2)
- break;
- memcpy(p, pl->plat, m);
- p += m;
- *p = '\0';
- n = 1;
-
- } lws_end_foreach_dll(px);
-
- /*
- * Don't resend the same status over and over
- */
-
- if (strncmp(pss->last_power_report, (const char *)start, lws_ptr_diff_size_t(p, start) + 1)) {
- diff = 1;
- memcpy(pss->last_power_report, start, lws_ptr_diff_size_t(p, start) + 1);
- }
-
- if (diff && start != p) {
- lwsl_notice("%s: detected jobs for %.*s\n", __func__,
- (int)lws_ptr_diff_size_t(p, start), start);
-
- if (lws_write(pss->wsi, start, lws_ptr_diff_size_t(p, start),
- LWS_WRITE_TEXT) < 0)
- return -1;
-
- lws_callback_on_writable(pss->wsi);
-
- return 0;
- }
- }
-
-
if (!pss->issue_task_owner.head)
return 0; /* nothing to send */
diff --git a/src/server/s-ws-web.c b/src/server/s-ws-web.c
index a4595a8..cdc2d90 100644
--- a/src/server/s-ws-web.c
+++ b/src/server/s-ws-web.c
@@ -194,10 +194,15 @@ sais_list_builders(struct vhd *vhd)
lws_start_foreach_dll(struct lws_dll2 *, p, vhd->server.power_state_owner.head) {
sai_power_state_t *ps = lws_container_of(p, sai_power_state_t, list);
- size_t host_len = strlen(ps->host);
+ size_t host_len = strlen(ps->host), pl = strlen(builder_from_db->name);
- if (!strncmp(builder_from_db->name, ps->host, host_len) &&
- builder_from_db->name[host_len] == '.') {
+ lwsl_notice("%s: %s vs %s\n", __func__, builder_from_db->name, ps->host);
+
+ if ((!strncmp(builder_from_db->name, ps->host, host_len) &&
+ builder_from_db->name[host_len] == '.') || (pl > host_len &&
+ !strncmp(builder_from_db->name + (pl - host_len), ps->host, host_len)))
+ {
+ lwsl_notice("%s: adjusting powering_ %d %d\n", __func__, ps->powering_up, ps->powering_down);
builder_from_db->powering_up = ps->powering_up;
builder_from_db->powering_down = ps->powering_down;
break;
@@ -456,8 +461,10 @@ websrvss_ws_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
{
sai_stay_t *stay = (sai_stay_t *)a.dest;
+ lwsl_notice("%s: stay received from web\n", __func__);
+
lws_start_foreach_dll(struct lws_dll2 *, p,
- m->vhd->sai_powers.head) {
+ m->vhd->sai_powers.head) {
struct pss *pss_power = lws_container_of(p, struct pss, same);
sai_stay_t *s;
@@ -466,6 +473,7 @@ websrvss_ws_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
*s = *stay;
lws_dll2_add_tail(&s->list, &pss_power->stay_owner);
lws_callback_on_writable(pss_power->wsi);
+ lwsl_wsi_notice(pss_power->wsi, "queued stay on power conn");
}
} lws_end_foreach_dll(p);
diff --git a/src/web/w-ws-browser.c b/src/web/w-ws-browser.c
index 63bc32d..07b1397 100644
--- a/src/web/w-ws-browser.c
+++ b/src/web/w-ws-browser.c
@@ -518,8 +518,12 @@ saiw_ws_json_rx_browser(struct vhd *vhd, struct pss *pss, uint8_t *buf,
break;
case SAIM_WS_BROWSER_RX_STAY:
- if (!sais_conn_auth(pss))
+ if (!sais_conn_auth(pss)) {
+ lwsl_err("%s: stay didn't like auth\n", __func__);
goto auth_error;
+ }
+
+ lwsl_notice("%s: web: received stay req\n", __func__);
/*
* User is asking us to set or release a stay on a builder
@@ -1224,14 +1228,12 @@ b_finish:
* when we go out of scope...
*/
- lwsl_warn("%s: wwwwwwwwwwww PREPARE_TASKINFO: one_task %p\n", __func__, sch->one_task);
-
task_reply.event = sch->one_event;
task_reply.task = sch->one_task;
sch->one_task->rebuildable = (sch->one_task->state == SAIES_FAIL ||
sch->one_task->state == SAIES_CANCELLED) &&
(lws_now_secs() - (sch->one_task->started +
- (sch->one_task->duration / 1000000)) < 24 * 3600);
+ (sch->one_task->duration / 1000000)) < 24 * 3600);
task_reply.auth_secs = (int)(pss->authorized ? pss->expiry_unix_time - lws_now_secs() : 0);
task_reply.authorized = pss->authorized;
lws_strncpy(task_reply.auth_user, pss->auth_user,
@@ -1306,9 +1308,7 @@ b_finish:
return 0;
}
- lwsl_notice("%s: wwwwwwwwwww TASKINFO\n", __func__);
-
- sai_dump_stderr((const char *)start, lws_ptr_diff_size_t(p, start));
+// sai_dump_stderr((const char *)start, lws_ptr_diff_size_t(p, start));
break;
case WSS_SEND_ARTIFACT_INFO: