diff --git a/assets/sai.css b/assets/sai.css
index c44e92f..5ba27f8 100644
--- a/assets/sai.css
+++ b/assets/sai.css
@@ -811,3 +811,22 @@ img.branch {
transition: opacity 0.3s;
}
+@keyframes flash-slow {
+ 50% { opacity: 0.8; }
+}
+@keyframes flash-medium {
+ 50% { opacity: 0.8; }
+}
+@keyframes flash-fast {
+ 50% { opacity: 0.8; }
+}
+
+.activity-1 {
+ animation: flash-slow 4s infinite;
+}
+.activity-2 {
+ animation: flash-medium 2s infinite;
+}
+.activity-3 {
+ animation: flash-fast 1s infinite;
+}
diff --git a/assets/sai.js b/assets/sai.js
index b658494..a2084e8 100644
--- a/assets/sai.js
+++ b/assets/sai.js
@@ -394,7 +394,21 @@ var lang_zhs = "{" +
"\"%{pf}前创建, 创作时间: %{ct}ms \"" +
"}}";
-var logs = "", redpend = 0, gitohashi_integ = 0, authd = 0, exptimer, auth_user = "";
+var logs = "", redpend = 0, gitohashi_integ = 0, authd = 0, exptimer, auth_user = "",
+ ongoing_task_activities = {};
+
+function update_task_activities() {
+ for (const uuid in ongoing_task_activities) {
+ const el = document.getElementById("taskstate_" + uuid);
+ if (el) {
+ const cat = ongoing_task_activities[uuid];
+ el.classList.remove("activity-1", "activity-2", "activity-3");
+ if (cat > 0) {
+ el.classList.add("activity-" + cat);
+ }
+ }
+ }
+}
function expiry()
{
@@ -1517,6 +1531,16 @@ function ws_open_sai()
break;
+ case "com.warmcat.sai.taskactivity":
+ ongoing_task_activities = {};
+ if (jso.activity) {
+ for (var i = 0; i < jso.activity.length; i++) {
+ var act = jso.activity[i];
+ ongoing_task_activities[act.uuid] = act.cat;
+ }
+ }
+ break;
+
case "com-warmcat-sai-logs":
var s1 = atob(jso.log), s = hsanitize(s1), li,
en = "", yo, dh, ce, tn = "";
@@ -1681,6 +1705,7 @@ window.addEventListener("load", function() {
}
setInterval(function() {
+ update_task_activities();
var locked = document.body.scrollHeight -
document.body.clientHeight <= document.body.scrollTop + 1;
diff --git a/build.sh b/build.sh
new file mode 100644
index 0000000..b61cda0
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+set -e
+git clone https://github.com/warmcat/libwebsockets.git
+cd libwebsockets
+mkdir build
+cd build
+cmake .. -DLWS_WITH_JOSE=1
+sudo make install
+cd ../..
+mkdir build
+cd build
+cmake ..
+make -j8
diff --git a/src/server/s-comms.c b/src/server/s-comms.c
index 9c9daf0..5b669ab 100644
--- a/src/server/s-comms.c
+++ b/src/server/s-comms.c
@@ -528,6 +528,9 @@ callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user,
lws_sul_schedule(vhd->context, 0, &vhd->sul_central,
sais_central_cb, 500 * LWS_US_PER_MS);
+ lws_sul_schedule(vhd->context, 0, &vhd->sul_activity,
+ sais_activity_cb, 1 * LWS_US_PER_SEC);
+
break;
case LWS_CALLBACK_PROTOCOL_DESTROY:
diff --git a/src/server/s-private.h b/src/server/s-private.h
index 8aa84ca..5850bab 100644
--- a/src/server/s-private.h
+++ b/src/server/s-private.h
@@ -173,6 +173,12 @@ typedef struct sais_sqlite_cache {
int refcount;
} sais_sqlite_cache_t;
+typedef struct sai_ongoing_task {
+ lws_dll2_t list;
+ char uuid[65];
+ lws_usec_t last_log_timestamp;
+} sai_ongoing_task_t;
+
typedef struct sais_plat {
lws_dll2_t list;
@@ -192,6 +198,7 @@ struct vhd {
struct lws_dll2_owner sai_powers;
struct lws_dll2_owner pending_plats;
lws_dll2_owner_t powering_up_list; /* sai_powering_up_plat_t */
+ lws_dll2_owner_t ongoing_tasks; /* sai_ongoing_task_t */
struct lwsac *ac_plats;
@@ -201,6 +208,7 @@ struct vhd {
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;
@@ -282,6 +290,9 @@ sais_subs_request_writeable(struct vhd *vhd, const char *task_uuid);
void
sais_central_cb(lws_sorted_usec_list_t *sul);
+void
+sais_activity_cb(lws_sorted_usec_list_t *sul);
+
int
sais_task_reset(struct vhd *vhd, const char *task_uuid);
diff --git a/src/server/s-task.c b/src/server/s-task.c
index e5a4332..72b6575 100644
--- a/src/server/s-task.c
+++ b/src/server/s-task.c
@@ -267,6 +267,19 @@ sais_set_task_state(struct vhd *vhd, const char *builder_name,
sais_event_db_close(vhd, (sqlite3 **)&e->pdb);
lwsac_free(&ac);
+ if (state == SAIES_SUCCESS || state == SAIES_FAIL || state == SAIES_CANCELLED) {
+ sai_ongoing_task_t *ot = NULL;
+ lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1, vhd->ongoing_tasks.head) {
+ ot = lws_container_of(p, sai_ongoing_task_t, list);
+
+ if (!strcmp(ot->uuid, task_uuid)) {
+ lws_dll2_remove(&ot->list);
+ free(ot);
+ break;
+ }
+ } lws_end_foreach_dll_safe(p, p1);
+ }
+
return 0;
bail:
@@ -858,3 +871,55 @@ bail:
return -1;
}
+
+void
+sais_activity_cb(lws_sorted_usec_list_t *sul)
+{
+ struct vhd *vhd = lws_container_of(sul, struct vhd, sul_activity);
+ char *p, *start, *end;
+ sai_ongoing_task_t *ot;
+ lws_usec_t now;
+ int cat, first = 1;
+
+ p = start = malloc(8192);
+ if (!p)
+ return;
+ end = start + 8192;
+
+ p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
+ "{\"schema\":\"com.warmcat.sai.taskactivity\","
+ "\"activity\":[");
+
+ now = lws_now_usecs();
+
+ lws_start_foreach_dll(struct lws_dll2 *, d, vhd->ongoing_tasks.head) {
+ ot = lws_container_of(d, sai_ongoing_task_t, list);
+
+ if (now - ot->last_log_timestamp > 10 * LWS_USEC_PER_SEC)
+ cat = 1;
+ else if (now - ot->last_log_timestamp > 3 * LWS_US_PER_SEC)
+ cat = 2;
+ else
+ cat = 3;
+
+ if (!first)
+ *p++ = ',';
+
+ p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
+ "{\"uuid\":\"%s\",\"cat\":%d}",
+ ot->uuid, cat);
+ first = 0;
+
+ } lws_end_foreach_dll(d);
+
+ *p++ = ']';
+ *p++ = '}';
+
+ if (lws_ptr_diff(p, start) > 48)
+ sais_websrv_broadcast(vhd->h_ss_websrv, start, lws_ptr_diff_size_t(p, start));
+
+ free(start);
+
+ lws_sul_schedule(vhd->context, 0, &vhd->sul_activity,
+ sais_activity_cb, 1 * LWS_US_PER_SEC);
+}
diff --git a/src/server/s-ws-builder.c b/src/server/s-ws-builder.c
index 729a8a8..43d23db 100644
--- a/src/server/s-ws-builder.c
+++ b/src/server/s-ws-builder.c
@@ -192,6 +192,7 @@ static void
sais_log_to_db(struct vhd *vhd, sai_log_t *log)
{
sais_logcache_pertask_t *lcpt = NULL;
+ sai_ongoing_task_t *ot;
sai_log_t *hlog;
/*
@@ -212,6 +213,8 @@ sais_log_to_db(struct vhd *vhd, sai_log_t *log)
* Create a pertask and add it to the vhd list of them
*/
lcpt = malloc(sizeof(*lcpt));
+ if (!lcpt)
+ return;
memset(lcpt, 0, sizeof(*lcpt));
lws_strncpy(lcpt->uuid, log->task_uuid, sizeof(lcpt->uuid));
lws_dll2_add_tail(&lcpt->list, &vhd->tasklog_cache);
@@ -237,6 +240,31 @@ sais_log_to_db(struct vhd *vhd, sai_log_t *log)
lws_sul_schedule(vhd->context, 0, &vhd->sul_logcache,
sais_dump_logs_to_db, 250 * LWS_US_PER_MS);
+
+ /*
+ * Update ongoing task activity
+ */
+
+ ot = NULL;
+ lws_start_foreach_dll(struct lws_dll2 *, p, vhd->ongoing_tasks.head) {
+ sai_ongoing_task_t *fot = lws_container_of(p, sai_ongoing_task_t, list);
+
+ if (!strcmp(fot->uuid, log->task_uuid)) {
+ ot = fot;
+ break;
+ }
+ } lws_end_foreach_dll(p);
+
+ if (!ot) {
+ ot = malloc(sizeof(*ot));
+ if (!ot)
+ return;
+ memset(ot, 0, sizeof(*ot));
+ lws_strncpy(ot->uuid, log->task_uuid, sizeof(ot->uuid));
+ lws_dll2_add_tail(&ot->list, &vhd->ongoing_tasks);
+ }
+
+ ot->last_log_timestamp = lws_now_usecs();
}
sai_plat_t *
diff --git a/src/web/w-comms.c b/src/web/w-comms.c
index d21ad1b..095ac6c 100644
--- a/src/web/w-comms.c
+++ b/src/web/w-comms.c
@@ -99,6 +99,8 @@ const lws_struct_map_t lsm_schema_json_map[] = {
/* shares struct */ "sai-tasklogs"),
LSM_SCHEMA (sai_load_report_t, NULL, lsm_load_report_members,
"com.warmcat.sai.loadreport"),
+ LSM_SCHEMA (sai_browse_rx_evinfo_t, NULL, lsm_websrv_evinfo,
+ "com.warmcat.sai.taskactivity"),
};
size_t lsm_schema_json_map_array_size = LWS_ARRAY_SIZE(lsm_schema_json_map);
diff --git a/src/web/w-websrv.c b/src/web/w-websrv.c
index ccc96c1..5601a4e 100644
--- a/src/web/w-websrv.c
+++ b/src/web/w-websrv.c
@@ -47,6 +47,7 @@ enum {
SAIS_WS_WEBSRV_RX_OVERVIEW, /* deleted or added event */
SAIS_WS_WEBSRV_RX_TASKLOGS, /* new logs for task (ratelimited) */
SAIS_WS_WEBSRV_RX_LOADREPORT, /* builder's cpu load report */
+ SAIS_WS_WEBSRV_RX_TASKACTIVITY,
};
/*
@@ -176,6 +177,10 @@ saiw_lp_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
saiw_ws_broadcast_raw(vhd, buf, len, 2,
lws_write_ws_flags(LWS_WRITE_TEXT, flags & LWSSS_FLAG_SOM, flags & LWSSS_FLAG_EOM));
break;
+ case SAIS_WS_WEBSRV_RX_TASKACTIVITY:
+ saiw_ws_broadcast_raw(vhd, buf, len, 0,
+ lws_write_ws_flags(LWS_WRITE_TEXT, flags & LWSSS_FLAG_SOM, flags & LWSSS_FLAG_EOM));
+ break;
}
cleanup_parse_allocs: