#include #include "minicurl.hh" #include #include "navmon.hh" #include "fmt/format.h" #include "fmt/printf.h" #include "ext/powerblog/h2o-pp.hh" #include #include "CLI/CLI.hpp" #include "version.hh" static char program[]="galmonmon"; using namespace std; extern const char* g_gitHash; /* Monitoring the satellites for sensible alerts. A satellite transitions state if: * galmon is up * galmon is up to date * We are seeing recent messages for the satellite * For more than 60 seconds this state is different than it was A satellite becomes 'unobserved' if: * galmon is up * galmon is up to date * We haven't seen a message since an hour A satellite becomes observed again if we have seen it for > 60 seconds. At startup, every satellite assumes, without alert, the status we find for it, which is not a transition. */ class StateKeeper { public: typedef std::variant var_t; void setBoolNames(string_view name, string_view offName, string_view onName); std::optional reportState(string_view thing, string_view name, var_t state, const std::string& state_text=""); std::optional getState(string_view thing, string_view name); std::optional getPrevState(string_view thing, string_view name); struct State { var_t state; time_t since; string text; }; std::optional getFullState(string_view thing, string_view name); std::optional getPrevFullState(string_view thing, string_view name); private: struct Names { string offName, onName; }; map names; struct ThingState { std::optional prev, live, provisional; }; map > states; private: std::string getName(string_view name, var_t state) { if(auto ptr = std::get_if(&state)) { if(*ptr) { return names[(string)name].onName; } else return names[(string)name].offName; } if(auto ptr = std::get_if(&state)) { return *ptr; } if(auto ptr = std::get_if(&state)) { return to_string(*ptr); } return "?"; } }; void StateKeeper::setBoolNames(string_view name, string_view offName, string_view onName) { names[(string)name].offName = offName; names[(string)name].onName = onName; } std::optional StateKeeper::getState(string_view thing, string_view name) { if(states.count((string)thing) && states[(string)thing].count((string) name) && states[(string)thing][(string)name].live) { return getName(name, states[(string)thing][(string)name].live->state); } return std::optional(); } std::optional StateKeeper::getFullState(string_view thing, string_view name) { if(states.count((string)thing) && states[(string)thing].count((string) name) && states[(string)thing][(string)name].live) { return states[(string)thing][(string)name].live; } return std::optional(); } std::optional StateKeeper::getPrevState(string_view thing, string_view name) { if(states.count((string)thing) && states[(string)thing].count((string) name) && states[(string)thing][(string)name].prev) { return getName(name, states[(string)thing][(string)name].prev->state); } return std::optional(); } std::optional StateKeeper::getPrevFullState(string_view thing, string_view name) { if(states.count((string)thing) && states[(string)thing].count((string) name) && states[(string)thing][(string)name].prev) { return states[(string)thing][(string)name].prev; } return std::optional(); } std::optional StateKeeper::reportState(string_view thing, string_view name, var_t newstate, const std::string& state_text) { auto& state = states[(string)thing][(string)name]; std::optional ret; time_t now = time(0); if(!state.live) { // we had no state yet state.live = State{newstate, now, state_text}; state.provisional.reset(); // for good measure return ret; } else if(state.live->state == newstate) { // confirmation of current state state.live->text = state_text; // update text perhaps state.provisional.reset(); return ret; } else if(!state.provisional) { // new provisional state state.provisional = State{newstate, now, state_text}; return ret; } else { if(now - state.provisional->since > 60) { // provisional state has been confirmed state.prev = state.live; state.live = state.provisional; state.provisional.reset(); return getName(name, state.live->state); } } return ret; } StateKeeper g_sk; void sendTweet(const string& tweet) { string etweet = tweet; //system((string("twurl -X POST /1.1/statuses/update.json -d \"media_ids=1215649475231997953&status=")+etweet+"\" >> twitter.log").c_str()); if(system((string("twurl -X POST /1.1/statuses/update.json -d \"status=")+etweet+"\" >> twitter.log").c_str()) < 0) { cout<<"Problem tweeting!"< tst; auto observers = nlohmann::json::parse(mc.getURL(url+"observers.json")); // sendTweet("Galmonmon " +string(g_gitHash)+ " started, " + std::to_string(observers.size()) +" observers seen"); cout<<("Galmonmon " +string(g_gitHash)+ " started, " + std::to_string(observers.size()) +" observers seen")< 30) { cout<<"Galmon at "< sbasHealthChange; for(auto iter = j.begin(); iter != j.end(); ++iter) { if(iter.value().count("health")) { string name = sbasName(atoi(iter.key().c_str())); sbasHealthChange = g_sk.reportState(name, "sbas-health", (string)iter.value()["health"]); // cout<<"Setting state for "<< name <<" to "<< (string)iter.value()["health"] << endl; if(sbasHealthChange) { ostringstream out; out<<"✈️ augmentation system "< tooOldChange; if(gnssid == 2) tooOldChange = g_sk.reportState(fullName, "eph-too-old", sv["eph-age-m"] > 105, fmt::sprintf("%.2f", (double)sv["eph-age-m"])); else tooOldChange = g_sk.reportState(fullName, "eph-too-old", sv["eph-age-m"] > 140, fmt::sprintf("%.2f", (double)sv["eph-age-m"])); auto seenChange = g_sk.reportState(fullName, "silent", notseen); auto sisaChange = g_sk.reportState(fullName, "sisa", (double) sv["sisa-m"], (string)sv["sisa"]); double ephdisco = sv.count("latest-disco") ? (double)sv["latest-disco"] : -1.0; auto ephdiscochange = g_sk.reportState(fullName, "eph-disco", ephdisco); if(ephdisco == -1.0) ephdiscochange.reset(); double timedisco = sv.count("time-disco") ? fabs((double)sv["time-disco"]) : 0.0; auto timediscochange = g_sk.reportState(fullName, "time-disco", timedisco); /* cout<text <<" minutes"; } if(seenChange) out<< *seenChange<<" "; if(ephdiscochange && (gnssid ==0 || gnssid == 2) && ephdisco > 1.45) { if(ephdisco > 10) out< 5) out< 2.5)) { if(timedisco > 10) out< 5) out<text<<", old: " << prevState->text; if(get(state->state) > 3 || get(prevState->state) > 3) out << tmp.str(); else cout<<"Not reporting: "<