diff --git a/configfiles/Dummy/ToolChainConfig b/configfiles/Dummy/ToolChainConfig index ba9a9f2..e19f771 100644 --- a/configfiles/Dummy/ToolChainConfig +++ b/configfiles/Dummy/ToolChainConfig @@ -45,6 +45,7 @@ mon_port 5000 # remote multicast port to send monitoring messa multicast_send_period 5000 # logging & monitoring messages will be sent as a batch once per this period alarm_cooldown_ms 1000 # alarms with the same device and message will be limited to this rate mon_merge_period 5000 # monitoring messages with the same device and subject will be limited to this rate +MTU 1500 # MTU of interface used for logging/monitoring ##### Tools To Add ##### Tools_File configfiles/Dummy/ToolsConfig # list of tools to run and their config files diff --git a/configfiles/template/ToolChainConfig b/configfiles/template/ToolChainConfig index b4dd22b..21a9768 100644 --- a/configfiles/template/ToolChainConfig +++ b/configfiles/template/ToolChainConfig @@ -45,6 +45,7 @@ mon_port 5000 # remote multicast port to send monitoring messa multicast_send_period 5000 # logging & monitoring messages will be sent as a batch once per this period alarm_cooldown_ms 1000 # alarms with the same device and message will be limited to this rate mon_merge_period 5000 # monitoring messages with the same device and subject will be limited to this rate +MTU 1500 # MTU of interface used for logging/monitoring ##### Tools To Add ##### Tools_File configfiles/ToolsConfig # list of tools to run and their config files diff --git a/src/RemoteControl/RemoteControl.cpp b/src/RemoteControl/RemoteControl.cpp index 20a7ddc..6997e66 100644 --- a/src/RemoteControl/RemoteControl.cpp +++ b/src/RemoteControl/RemoteControl.cpp @@ -5,6 +5,8 @@ #include "ServiceDiscovery.h" #include "zmq.hpp" +#include +#include "zstd_helpers.h" #include // uuid class #include // generators @@ -30,6 +32,7 @@ int main(int argc, char** argv){ zmq::context_t context(3); + ZSTD_DCtx* zstd_dctx = ZSTD_createDCtx(); //std::string address(argv[1]); // std::stringstream tmp (argv[2]); @@ -236,14 +239,17 @@ int main(int argc, char** argv){ zmq::message_t receive; if(ServiceSend.recv(&receive)){ - std::istringstream iss(static_cast(receive.data())); - std::string answer; - answer=iss.str(); + if(!ZstdDecompress(zstd_dctx, (char*)receive.data(), receive.size(), answer)){ + std::cerr<<"failed to decompress reply!: "<("msg_type")=="Command Reply") std::cout<("msg_value")<("msg_type")=="Command Reply") std::cout<("msg_value"))<(receive.data())); - std::string answer; - answer=iss.str(); - - Store rr; - rr.JsonParser(answer); - if(rr.Get("msg_type")=="Command Reply") std::cout<("msg_value")<("msg_type")=="Command Reply") std::cout<("msg_value")<InitThreadedReceiver(m_context, sc_port, 100, new_service, alert_receive_port, alerts_receive, alert_send_port, alerts_send); m_backend_client.SetUp(m_context); @@ -65,7 +67,7 @@ bool Services::Init(Store &m_variables, zmq::context_t* context_in, SlowControlC sc_vars->Add("LoadConfig",SlowControlElementType(COMMAND),std::bind(&Services::LoadConfigSlowControlFunc, this, std::placeholders::_1),0,false,false); AlertSubscribe("LoadConfig", std::bind(&Services::LoadConfigAlertFunc, this, std::placeholders::_1, std::placeholders::_2)); - sc_vars->Add("LocalConfig",SlowControlElementType(INFO),std::bind(&Services::SCLocalConfig, this, std::placeholders::_1),0,false,false); + sc_vars->Add("LocalConfig",SlowControlElementType(INFO),0,std::bind(&Services::SCLocalConfig, this, std::placeholders::_1),false,true); // FIXME hidden until Control page supports JSON if(!m_variables.Get("service_name",m_name)) m_name="test_service"; @@ -106,6 +108,13 @@ bool Services::Init(Store &m_variables, zmq::context_t* context_in, SlowControlC return false; } + // fewer, larger packets are better for network performance, so we batch logging and monitoring messages. + // on the other hand, if packet size exceeds the MTU, they will fragment, increasing packets and reducing reliability + // so, try to batch up to the MTU size. In order to do that, we need to know the MTU. + //std::set interfaces = GetInterfaces(); + //if(interfaces.size()) MSS_SIZE = GetMTU(interfaces[??]); // but which interface? + // just get MTU from config variable. *sigh* + return true; } @@ -978,24 +987,35 @@ bool Services::SendLog(const std::string& message, LogLevel severity, const std: const std::string& name = (device=="") ? m_name : device; - // FIXME we should be able to relax this check if compression is enabled... - if((message.length()+name.length())>MAX_MSG_SIZE){ - if(m_verbose) std::cerr<<"Logging message is too long!"< locker(logging_buf_mtx); + // merge identical logging messages back-to-back if(logging_buf.size() && name==logging_buf.back().device && message==logging_buf.back().message){ ++logging_buf.back().repeats; return true; } - // grab timestamp at time of call if 0 - time_t ts = (timestamp!=0) ? timestamp : time(nullptr)*1000; + // reject if this message is too big to fit in a UDP datagram even with compression + size_t compressed_bytes = ZSTD_compressBound(message.length()+name.length()); + if(ZSTD_isError(compressed_bytes) || compressed_bytes > MAX_MSG_SIZE){ + if(m_verbose) std::cerr<<"Logging message is too long!"< MSS_SIZE){ + BatchAndSendMulticast(&thread_args, false, true); // don't try to lock logging buffer, we've got it + } logging_buf.emplace_back(message, severity, name, ts); + thread_args.logging_batch_bytes += compressed_bytes; + return true; } @@ -1017,24 +1037,38 @@ bool Services::SendMonitoringData(const std::string& json_data, const std::strin const std::string& name = (device=="") ? m_name : device; - if((json_data.length()+name.length()+subject.length())>MAX_MSG_SIZE){ - if(m_verbose) std::cerr<<"Monitoring message is too long!"< locker(monitoring_buf_mtx); - // take first of repeated monitoring sends within buffer period + // only accept the first of repeated monitoring sends within buffer period auto it = monitoring_buf.find(name+subject); if(it!=monitoring_buf.end() && (ts - it->second.timestamp) MAX_MSG_SIZE){ + if(m_verbose) std::cerr<<"Monitoring message is too long!"< MSS_SIZE){ + BatchAndSendMulticast(&thread_args, true, false); // don't try to lock monitoring buffer, we've got it + } + monitoring_buf.emplace(std::piecewise_construct, std::forward_as_tuple(name+subject), std::forward_as_tuple(json_data, subject, name, ts)); + thread_args.monitoring_batch_bytes += compressed_bytes; + return true; } @@ -1062,8 +1096,9 @@ bool Services::SendROOTplotMulticast(const std::string& plot_name, const std::st + ", \"lifetime\":"+std::to_string(lifetime) + ", \"data\":"+ json_data+"}"; - if(cmd_string.length()>MAX_UDP_PACKET_SIZE){ - if(m_verbose) std::cerr<<"ROOT plot json is too long! Maximum length may be MAX_UDP_PACKET_SIZE bytes"< MAX_UDP_PACKET_SIZE){ + if(m_verbose) std::cerr<<"ROOT plot json is too long!"<SetValue(1); m_base_config_id = base_config_id; m_run_mode_config_id = run_mode_config_id; + m_testing = testing; + } return true; } -std::string Services::LoadConfigSlowControlFunc(const char* control){ +std::string Services::LoadConfigSlowControlFunc(const char* payload){ - std::string payload = (*sc_vars)[control]->GetValue(); - Store tmp; - tmp.JsonParser(payload); - uint64_t base_config_id=0; - uint64_t run_mode_config_id=0; - - short count = 0; - std::stringstream ret; - - - tmp.Get("Base",base_config_id); - tmp.Get("RunMode",run_mode_config_id); - - if(run_mode_config_id!=m_run_mode_config_id || base_config_id!=m_base_config_id){ - - while(count<5){ - if(!GetCachedDeviceConfig(m_local_config, base_config_id, run_mode_config_id, config_devicename)){ - usleep(100000); - count++; - } - else count=99; - } - - if(count==5){ - ret <<"Failed to load config "<SetValue(1); - m_base_config_id = base_config_id; - m_run_mode_config_id = run_mode_config_id; - ret <<"Loaded config "<(args); + BatchAndSendMulticast(m_args, true, true); + std::this_thread::sleep_until(m_args->last_send+m_args->multicast_send_period_ms); + return; + +} + +bool Services::BatchAndSendMulticast(BufferThreadArgs* m_args, bool log_lock, bool mon_lock){ m_args->last_send = std::chrono::steady_clock::now(); m_args->local_merge_buf.clear(); - std::unique_lock locker(*m_args->logging_buf_mtx); + std::unique_lock locker(*m_args->logging_buf_mtx, std::defer_lock); + if(log_lock) locker.lock(); // merge into a batch bool first=true; @@ -1274,13 +1290,16 @@ void Services::BufferThread(Thread_args* args){ } // send - if(m_args->local_merge_buf.empty() || m_args->services->SendLog(m_args->local_merge_buf)){ - m_args->logging_buf->clear(); // FIXME do we not clear on error...? does it depend on the error...? + if(!m_args->local_merge_buf.empty()){ + m_args->services->SendLog(m_args->local_merge_buf); + m_args->logging_buf->clear(); + m_args->logging_batch_bytes = 0; } // repeat for monitoring messages m_args->local_merge_buf.clear(); - locker = std::unique_lock(*m_args->monitoring_buf_mtx); + locker = std::unique_lock(*m_args->monitoring_buf_mtx, std::defer_lock); + if(mon_lock) locker.lock(); first=true; for(std::pair& msg : *m_args->monitoring_buf){ @@ -1295,8 +1314,10 @@ void Services::BufferThread(Thread_args* args){ } // send - if(m_args->local_merge_buf.empty() || m_args->services->SendMonitoringData(m_args->local_merge_buf)){ - m_args->monitoring_buf->clear(); // FIXME do we not clear on error...? does it depend on the error...? + if(!m_args->local_merge_buf.empty()){ + m_args->services->SendMonitoringData(m_args->local_merge_buf); + m_args->monitoring_buf->clear(); + m_args->monitoring_batch_bytes = 0; } // our other sevice task: prune the alarm buffer. @@ -1309,12 +1330,7 @@ void Services::BufferThread(Thread_args* args){ else ++it; } - // release mtx - locker.unlock(); - - std::this_thread::sleep_until(m_args->last_send+m_args->multicast_send_period_ms); - - return; + return true; } std::string Services::JsonEscape(std::string s){ @@ -1333,9 +1349,9 @@ std::string Services::GetLocalConfig(){ } -std::string Services::SCLocalConfig(const char* data){ +std::string Services::SCLocalConfig(const char*){ - return "["+std::to_string(m_base_config_id)+","+std::to_string(m_run_mode_config_id)+"]: "+m_local_config; + return "base: "+std::to_string(m_base_config_id)+", runmode:"+std::to_string(m_run_mode_config_id)+", config: "+m_local_config; } @@ -1365,7 +1381,10 @@ bool Services::SetChangeConfigFunc(std::function func){ // 1. allgood = AlertSubscribe("ChangeConfig", [this, func](const char*, const char*) -> bool{ + bool old_testing = sc_vars->GetTesting(); + sc_vars->SetTesting(m_testing); if(func(m_local_config)) return true; + sc_vars->SetTesting(old_testing); // on error, revert sc_vars->SetWarning(true); std::cerr<<"ChangeConfig Error"< func){ [this, func](const char*) -> std::string { (*sc_vars)["Config"]->SetValue((int)ConfigState::ChangeStart); bool ok = func(m_local_config); + int new_state; if(!ok){ - std::cerr<<"ChangeConfig Error"<SetWarning(true); - } - int new_state = ok ? (int)ConfigState::ChangeEnd : (int)ConfigState::ChangeFail; + new_state = (int)ConfigState::ChangeFail; + std::cerr<<"ChangeConfig Error"<SetWarning(true); + } else { + new_state = (int)ConfigState::ChangeEnd; + sc_vars->SetTesting(m_testing); + } (*sc_vars)["Config"]->SetValue(new_state); (*sc_vars)["NewConfig"]->SetValue(0); return (ok ? "OK" : "Error"); - }, - 0, - false); // this version will not be locked during non-testing runs, - // since it only allows loading configurations in line with the current run type. + }, // setter + 0, // getter + false, // not locked during non-testing runs, as it only allows loading configurations in line with the current run type + false); // not hidden // 3. allgood = allgood && sc_vars->Add("ChangeToConfig", COMMAND, - [this, func](const char* name) -> std::string { + [this, func](const char* payload) -> std::string { (*sc_vars)["Config"]->SetValue((int)ConfigState::ChangeStart); - bool ok = func((*sc_vars)[name]->GetValue()); + bool ok = func(payload); int new_state = ok ? (int)ConfigState::ChangeEnd : (int)ConfigState::ChangeFail; (*sc_vars)["Config"]->SetValue(new_state); + if(ok && m_local_config.compare(payload) !=0){ + m_local_config = payload; + m_base_config_id = 0; + m_run_mode_config_id = 0; + } if(!ok){ - sc_vars->SetWarning(true); - std::cerr<<"ChangeConfig Error"<SetWarning(true); + std::cerr<<"ChangeConfig Error"<