From bd679cffa4e7e03011a70b6f2a4f846ae1de7937 Mon Sep 17 00:00:00 2001 From: Marcus O'Flaherty Date: Sun, 12 Jul 2026 20:48:23 +0000 Subject: [PATCH] * workaround: clear LoadConfig slow control value after receipt because JSON in slow control values breaks web controls on the DAQ Control page * Add escaping to logging messages in Services.cpp as they need to be parsable as JSON by postgres * Add State variable to service discovery broadcast, change type from enum to bitmap (though still int effectively). * Add a button to clear state in SlowControlCollection, add methods to set each bit and clear error+warning bits, updating SD broadcast with new bitmap. * Bugfix to SlowControlCollection - ChangeConfig alert carries no payload. --- src/ServiceDiscovery/Services.cpp | 17 +++++- src/ServiceDiscovery/Services.h | 1 + src/ServiceDiscovery/ServicesBackend.cpp | 2 +- .../SlowControlCollection.cpp | 59 +++++++++++++++---- src/ServiceDiscovery/SlowControlCollection.h | 8 ++- 5 files changed, 74 insertions(+), 13 deletions(-) diff --git a/src/ServiceDiscovery/Services.cpp b/src/ServiceDiscovery/Services.cpp index 2ee0575..bf90741 100644 --- a/src/ServiceDiscovery/Services.cpp +++ b/src/ServiceDiscovery/Services.cpp @@ -1231,6 +1231,8 @@ std::string Services::LoadConfigSlowControlFunc(const char* control){ ret <<"Loaded config "<SetValue(""); return ret.str(); @@ -1250,12 +1252,14 @@ void Services::BufferThread(Thread_args* args){ // merge into a batch bool first=true; for(LogMsg& msg : *m_args->logging_buf){ + m_args->local_merge_buf += std::string(first ? "" : ",") + "{\"topic\":\"LOGGING\"" + ",\"time\":\""+TimeStringFromUnixMs(msg.timestamp)+"\"" + ",\"device\":\""+ msg.device +"\"" + ",\"severity\":"+std::to_string(int(msg.severity)) - + ",\"message\":\"" + msg. message + "\"" + // we need to escape any " or \ symbols in the user message + + ",\"message\":\"" + JsonEscape(msg.message) + "\"" + ",\"repeats\":"+std::to_string(msg.repeats)+"}"; first=false; } @@ -1271,6 +1275,7 @@ void Services::BufferThread(Thread_args* args){ first=true; for(std::pair& msg : *m_args->monitoring_buf){ + m_args->local_merge_buf += std::string(first ? "" : ",") + "{\"topic\":\"MONITORING\"" + ",\"time\":\""+TimeStringFromUnixMs(msg.second.timestamp)+"\"" @@ -1303,6 +1308,16 @@ void Services::BufferThread(Thread_args* args){ return; } +std::string Services::JsonEscape(std::string s){ + // TODO is there a more efficient way to do this... + std::string out; + for(char& a : s){ + if(a=='"' || a=='\\') out.push_back('\\'); + out.push_back(a); + } + return out; +} + std::string Services::GetLocalConfig(){ return m_local_config; diff --git a/src/ServiceDiscovery/Services.h b/src/ServiceDiscovery/Services.h index d94f944..d41ba0c 100644 --- a/src/ServiceDiscovery/Services.h +++ b/src/ServiceDiscovery/Services.h @@ -119,6 +119,7 @@ namespace ToolFramework { std::string PrintSlowControlVariables(); std::string GetDeviceName(); void SetVerbose(bool in); + static std::string JsonEscape(std::string s); template T GetSlowControlValue(std::string name){ return (*sc_vars)[name]->GetValue(); diff --git a/src/ServiceDiscovery/ServicesBackend.cpp b/src/ServiceDiscovery/ServicesBackend.cpp index 9b0e4c5..2c50868 100644 --- a/src/ServiceDiscovery/ServicesBackend.cpp +++ b/src/ServiceDiscovery/ServicesBackend.cpp @@ -1117,7 +1117,7 @@ bool ServicesBackend::Ready(int timeout){ std::chrono::milliseconds time_left = std::chrono::duration_cast(end-std::chrono::steady_clock::now()); while(time_left>std::chrono::milliseconds{100}){ //std::cout<<"sending test query with time_left: "<SetValue("N/A"); - Add("State",SlowControlElementType(INFO),0,0,false,false); - SC_vars["State"]->SetValue((int)State::Inactive); Add("Config",SlowControlElementType(INFO),0,0,false,false); SC_vars["Config"]->SetValue((int)ConfigState::Unconfigured); + Add("State",SlowControlElementType(INFO),0,0,false,false); + SC_vars["State"]->SetValue(0); + Add("ClearState",SlowControlElementType(BUTTON), + [this](const char*) -> std::string { ClearState(); return "OK";}, + 0,false); + + // add state to the service discovery broadcast; default is no flags (not active, no error, no warning) + m_util->AddPort("State",0); + return true; } @@ -423,26 +430,27 @@ void SlowControlCollection::Thread(Thread_args* arg){ error = !((*(args->alert_functions))[iss.str()](iss.str().c_str(), payload.c_str())); } catch(...){ - error = true; + error = true; } if(iss.str() == "LoadConfig"){ if(error)(*args->SC_vars)["Config"]->SetValue((int)ConfigState::LoadFail); else (*args->SC_vars)["Config"]->SetValue((int)ConfigState::LoadEnd); } - else if(iss.str() == "ChangeConfig"){ - if(error)(*args->SC_vars)["Config"]->SetValue((int)ConfigState::ChangeFail); - else (*args->SC_vars)["Config"]->SetValue((int)ConfigState::ChangeEnd); - (*args->SC_vars)["NewConfig"]->SetValue(0); - } } - else + else { try{ error=!((*(args->alert_functions))[iss.str()](iss.str().c_str(), 0)); } catch(...){ error = true; } + if(iss.str() == "ChangeConfig"){ + if(error)(*args->SC_vars)["Config"]->SetValue((int)ConfigState::ChangeFail); + else (*args->SC_vars)["Config"]->SetValue((int)ConfigState::ChangeEnd); + (*args->SC_vars)["NewConfig"]->SetValue(0); + } + } if(error) std::cerr<<"alert fucntion failed: "<Print(); + reply=SCC->Print(); //printf("reply=%s\n", reply.c_str()); return true; } @@ -782,3 +790,34 @@ bool SlowControlCollection::Ready(int timeout_ms){ return timed_locker.try_lock_for(std::chrono::milliseconds(timeout_ms)); } +void SlowControlCollection::SetActive(bool active){ + int mask = 1 << (int)State::Active; + m_state = (active ? m_state | mask : m_state & ~mask); + m_util->AddPort("State",m_state); // update service discovery broadcast value + SC_vars["State"]->SetValue(m_state); // update slow control value + return; +} + +void SlowControlCollection::SetError(bool error){ + int mask = 1 << (int)State::Error; + m_state = (error ? m_state | mask : m_state & ~mask); + m_util->AddPort("State",m_state); + SC_vars["State"]->SetValue(m_state); + return; +} + +void SlowControlCollection::SetWarning(bool warn){ + int mask = 1 << (int)State::Warning; + m_state = (warn ? m_state | mask : m_state & ~mask); + m_util->AddPort("State",m_state); + SC_vars["State"]->SetValue(m_state); + return; +} + +void SlowControlCollection::ClearState(){ + int mask = (1 << (int)State::Warning) | (1 << (int)State::Error); + m_state = m_state & ~mask; + m_util->AddPort("State",m_state); + SC_vars["State"]->SetValue(m_state); + return; +} diff --git a/src/ServiceDiscovery/SlowControlCollection.h b/src/ServiceDiscovery/SlowControlCollection.h index 670b15a..c2a43b1 100644 --- a/src/ServiceDiscovery/SlowControlCollection.h +++ b/src/ServiceDiscovery/SlowControlCollection.h @@ -12,7 +12,8 @@ namespace ToolFramework{ typedef std::function AlertFunction; enum class ConfigState { Unconfigured=0, LoadStart=1, LoadEnd=2, LoadFail=3, ChangeStart=4, ChangeEnd=5, ChangeFail=6}; - enum class State { Active=0, Inactive=1, Warning=2, Error=3}; + // N.B. m_state is a bitmask, these values represent bit numbers; bit 0 is active, bit 1 is warning... + enum class State { Active=0, Warning=1, Error=2 }; class SlowControlCollection; @@ -63,6 +64,10 @@ namespace ToolFramework{ void TestingEnable(); void TestingDisable(); bool Ready(int timeout_ms); + void SetActive(bool active); + void SetError(bool error); + void SetWarning(bool warn); + void ClearState(); template T GetValue(std::string name){ if(!SC_vars.count(name)) return T{}; @@ -85,6 +90,7 @@ namespace ToolFramework{ bool m_alerts_receive; bool m_alerts_send; bool m_testing = false; + int m_state = 0; static void Thread(Thread_args* arg); void Unpack(std::string in, std::map &out, std::string header="");