18 std::ostringstream ss;
20 ss << std::fixed << std::setprecision(1) << seconds <<
"s";
21 }
else if (seconds < 3600.0) {
22 int m =
static_cast<int>(seconds) / 60;
23 double s = seconds - m * 60;
24 ss << m <<
"m " << std::fixed << std::setprecision(0) << s <<
"s";
26 int h =
static_cast<int>(seconds) / 3600;
27 int m = (
static_cast<int>(seconds) % 3600) / 60;
28 ss << h <<
"h " << m <<
"m";
35 auto pos = path.find_last_of(
'/');
36 if (pos != std::string::npos && pos + 1 < path.size())
37 return path.substr(pos + 1);
45 std::ostringstream body;
46 body << name <<
": " << summary.
sequences <<
" sequences, "
47 << time_str <<
" wall time";
48 if (summary.
ranks > 1)
49 body <<
" (" << summary.
ranks <<
" ranks)";
53 std::ostringstream cmd;
54 cmd <<
"curl -s --max-time 10"
55 <<
" -H \"Title: ADS-IDAC Complete\""
56 <<
" -H \"Tags: white_check_mark\""
57 <<
" -d \"" << body.str() <<
"\""
58 <<
" ntfy.sh/" << topic
59 <<
" > /dev/null 2>&1";
61 int rc = std::system(cmd.str().c_str());
63 std::cerr <<
"[notify] ntfy.sh notification failed (curl exit " << rc <<
")" << std::endl;
65 std::cout <<
"[notify] Sent ntfy.sh notification to topic '" << topic <<
"'" << std::endl;
73 std::ostringstream
json;
74 json <<
"{\"embeds\":[{\"title\":\"ADS-IDAC Simulation Complete\","
75 <<
"\"color\":5763719,"
77 <<
"{\"name\":\"Project\",\"value\":\"" << name <<
"\",\"inline\":true},"
78 <<
"{\"name\":\"Sequences\",\"value\":\"" << summary.
sequences <<
"\",\"inline\":true},"
79 <<
"{\"name\":\"Wall Time\",\"value\":\"" << time_str <<
"\",\"inline\":true},"
80 <<
"{\"name\":\"Ranks\",\"value\":\"" << summary.
ranks <<
"\",\"inline\":true},"
81 <<
"{\"name\":\"Solver\",\"value\":\"" << summary.
solver <<
"\",\"inline\":true},"
82 <<
"{\"name\":\"Truncation\",\"value\":\"T=" << std::fixed << std::setprecision(1)
87 std::ostringstream cmd;
88 cmd <<
"curl -s --max-time 10"
89 <<
" -H \"Content-Type: application/json\""
90 <<
" -d '" <<
json.str() <<
"'"
92 <<
" > /dev/null 2>&1";
94 int rc = std::system(cmd.str().c_str());
96 std::cerr <<
"[notify] Discord notification failed (curl exit " << rc <<
")" << std::endl;
98 std::cout <<
"[notify] Sent Discord notification" << std::endl;
103 if (gethostname(buf,
sizeof(buf)) != 0)
104 std::snprintf(buf,
sizeof(buf),
"unknown");
105 size_t h = std::hash<std::string>{}(buf);
107 std::snprintf(topic,
sizeof(topic),
"ads%04x",
static_cast<unsigned>(h & 0xFFFF));
void send_completion_notification(const RunSummary &summary)
Send completion notifications to all configured targets. Non-fatal: prints a warning if curl fails,...
static std::string project_name(const std::string &path)
Extract the last path component as the project name.
std::string notify_discord_webhook
static std::string format_time(double seconds)
Format wall time as human-readable string.
static void send_ntfy(const std::string &topic, const RunSummary &summary)
static void send_discord(const std::string &webhook_url, const RunSummary &summary)
std::string auto_ntfy_topic()
Generate a default ntfy topic name: "ads" + 4 hex chars from hostname hash. Example: "ads7f3a".
std::string notify_ntfy_topic
Global notification targets (set by Scheduler::parseOptions).
Send completion notifications via ntfy.sh or Discord webhooks.
Summary of a completed simulation run.