3 #define TRACEMF_USE_VERBATIM 1
4 #include "otsdaq/ARTDAQSupervisor/ARTDAQSupervisor.hh"
6 #include "artdaq-core/Utilities/configureMessageFacility.hh"
7 #include "artdaq/BuildInfo/GetPackageBuildInfo.hh"
8 #include "artdaq/DAQdata/Globals.hh"
9 #include "artdaq/ExternalComms/MakeCommanderPlugin.hh"
10 #include "cetlib_except/exception.h"
11 #include "fhiclcpp/make_ParameterSet.h"
12 #include "otsdaq/ARTDAQSupervisor/ARTDAQSupervisorTRACEController.h"
14 #include "artdaq-core/Utilities/ExceptionHandler.hh"
16 #include <boost/exception/all.hpp>
17 #include <boost/filesystem.hpp>
25 #include "otsdaq/ConfigurationInterface/ConfigurationInterface.h"
26 #include "otsdaq/Macros/StringMacros.h"
27 #include "otsdaq/TableCore/TableBase.h"
29 #define OUT_ON_ERR_SIZE 2000
35 const std::string ARTDAQSupervisor::ARTDAQ_SYSVAR_NAMESPACE =
"artdaq";
37 #define FAKE_CONFIG_NAME "ots_config"
38 #define DAQINTERFACE_PORT \
39 std::atoi(__ENV__("ARTDAQ_BASE_PORT")) + \
40 (partition_ * std::atoi(__ENV__("ARTDAQ_PORTS_PER_PARTITION")))
43 static std::unordered_map<int, struct sigaction> old_actions =
44 std::unordered_map<int, struct sigaction>();
45 static bool sighandler_init =
false;
46 static void signal_handler(
int signum)
49 #if TRACE_REVNUM < 1459
50 TRACE_STREAMER(TLVL_ERROR, &(
"ARTDAQsupervisor")[0], 0, 0, 0)
52 TRACE_STREAMER(TLVL_ERROR, TLOG2(
"ARTDAQsupervisor", 0), 0)
54 <<
"A signal of type " << signum
55 <<
" was caught by ARTDAQSupervisor. Shutting down DAQInterface, "
56 "then proceeding with default handlers!";
62 pthread_sigmask(SIG_UNBLOCK, NULL, &set);
63 pthread_sigmask(SIG_UNBLOCK, &set, NULL);
65 #if TRACE_REVNUM < 1459
66 TRACE_STREAMER(TLVL_ERROR, &(
"ARTDAQsupervisor")[0], 0, 0, 0)
68 TRACE_STREAMER(TLVL_ERROR, TLOG2(
"ARTDAQsupervisor", 0), 0)
70 <<
"Calling default signal handler";
73 sigaction(signum, &old_actions[signum], NULL);
74 kill(getpid(), signum);
80 sigaction(SIGINT, &old_actions[SIGINT], NULL);
81 kill(getpid(), SIGINT);
87 static std::mutex sighandler_mutex;
88 std::unique_lock<std::mutex> lk(sighandler_mutex);
92 sighandler_init =
true;
94 std::vector<int> signals = {
105 for(
auto signal : signals)
107 struct sigaction old_action;
108 sigaction(signal, NULL, &old_action);
112 if(old_action.sa_handler != SIG_IGN)
114 struct sigaction action;
115 action.sa_handler = signal_handler;
116 sigemptyset(&action.sa_mask);
117 for(
auto sigblk : signals)
119 sigaddset(&action.sa_mask, sigblk);
125 sigaction(signal, &action, NULL);
126 old_actions[signal] = old_action;
133 ARTDAQSupervisor::ARTDAQSupervisor(xdaq::ApplicationStub* stub)
135 , daqinterface_ptr_(NULL)
136 , partition_(getSupervisorProperty(
"partition", 0))
137 , daqinterface_state_(
"notrunning")
138 , runner_thread_(nullptr)
140 __SUP_COUT__ <<
"Constructor." << __E__;
143 init_sighandler(
this);
150 auto settings_file = __ENV__(
"DAQINTERFACE_SETTINGS");
151 std::ofstream of(settings_file, std::ios::trunc);
152 const int openErrno = errno;
153 if(!of.is_open() || of.fail())
155 __SS__ <<
"Failed to open DAQINTERFACE_SETTINGS file '" << settings_file
156 <<
"' for writing: " << strerror(openErrno) << __E__;
161 setenv(
"DAQINTERFACE_PARTITION_NUMBER", std::to_string(partition_).c_str(), 1);
162 auto logfileName = std::string(__ENV__(
"OTSDAQ_LOG_DIR")) +
163 "/DAQInteface/DAQInterface_partition" +
164 std::to_string(partition_) +
".log";
165 setenv(
"DAQINTERFACE_LOGFILE", logfileName.c_str(), 1);
167 o <<
"log_directory: "
168 << getSupervisorProperty(
"log_directory", std::string(__ENV__(
"OTSDAQ_LOG_DIR")))
172 const std::string record_directory = getSupervisorProperty(
173 "record_directory", ARTDAQTableBase::ARTDAQ_FCL_PATH +
"/run_records/");
174 mkdir(record_directory.c_str(), 0755);
175 o <<
"record_directory: " << record_directory << std::endl;
178 o <<
"package_hashes_to_save: "
179 << getSupervisorProperty(
"package_hashes_to_save",
"[artdaq]") << std::endl;
181 o <<
"spack_root_for_bash_scripts: "
182 << getSupervisorProperty(
"spack_root_for_bash_scripts",
183 std::string(__ENV__(
"SPACK_ROOT")))
185 o <<
"boardreader timeout: " << getSupervisorProperty(
"boardreader_timeout", 30)
187 o <<
"eventbuilder timeout: " << getSupervisorProperty(
"eventbuilder_timeout", 30)
189 o <<
"datalogger timeout: " << getSupervisorProperty(
"datalogger_timeout", 30)
191 o <<
"dispatcher timeout: " << getSupervisorProperty(
"dispatcher_timeout", 30)
194 if(!getSupervisorProperty(
"advanced_memory_usage",
false))
196 o <<
"max_fragment_size_bytes: "
197 << getSupervisorProperty(
"max_fragment_size_bytes", 1048576) << std::endl;
199 o <<
"transfer_plugin_to_use: "
200 << getSupervisorProperty(
"transfer_plugin_to_use",
"TCPSocket") << std::endl;
201 if(getSupervisorProperty(
"transfer_plugin_from_brs",
"") !=
"")
203 o <<
"transfer_plugin_from_brs: "
204 << getSupervisorProperty(
"transfer_plugin_from_brs",
"") << std::endl;
206 if(getSupervisorProperty(
"transfer_plugin_from_ebs",
"") !=
"")
208 o <<
"transfer_plugin_from_ebs: "
209 << getSupervisorProperty(
"transfer_plugin_from_ebs",
"") << std::endl;
211 if(getSupervisorProperty(
"transfer_plugin_from_dls",
"") !=
"")
213 o <<
"transfer_plugin_from_dls: "
214 << getSupervisorProperty(
"transfer_plugin_from_dls",
"") << std::endl;
216 o <<
"all_events_to_all_dispatchers: " << std::boolalpha
217 << getSupervisorProperty(
"all_events_to_all_dispatchers",
true) << std::endl;
218 if(getSupervisorProperty(
"data_directory_override",
"") !=
"")
220 o <<
"data_directory_override: "
221 << getSupervisorProperty(
"data_directory_override",
"") << std::endl;
223 o <<
"max_configurations_to_list: "
224 << getSupervisorProperty(
"max_configurations_to_list", 10) << std::endl;
225 o <<
"disable_unique_rootfile_labels: "
226 << getSupervisorProperty(
"disable_unique_rootfile_labels",
false) << std::endl;
227 o <<
"use_messageviewer: " << std::boolalpha
228 << getSupervisorProperty(
"use_messageviewer",
false) << std::endl;
229 o <<
"use_messagefacility: " << std::boolalpha
230 << getSupervisorProperty(
"use_messagefacility",
true) << std::endl;
231 o <<
"fake_messagefacility: " << std::boolalpha
232 << getSupervisorProperty(
"fake_messagefacility",
false) << std::endl;
233 o <<
"kill_existing_processes: " << std::boolalpha
234 << getSupervisorProperty(
"kill_existing_processes",
true) << std::endl;
235 o <<
"advanced_memory_usage: " << std::boolalpha
236 << getSupervisorProperty(
"advanced_memory_usage",
false) << std::endl;
237 o <<
"strict_fragment_id_mode: " << std::boolalpha
238 << getSupervisorProperty(
"strict_fragment_id_mode",
false) << std::endl;
239 o <<
"disable_private_network_bookkeeping: " << std::boolalpha
240 << getSupervisorProperty(
"disable_private_network_bookkeeping",
false) << std::endl;
241 o <<
"allowed_processors: "
242 << getSupervisorProperty(
243 "allowed_processors",
246 if(getSupervisorProperty(
"partition_label_format",
"") !=
248 o <<
"partition_label_format: "
249 << getSupervisorProperty(
"partition_label_format",
"") << std::endl;
251 __COUT_MULTI__(0, o.str());
257 if(CorePropertySupervisorBase::theTRACEController_)
259 __SUP_COUT__ <<
"Destroying TRACE Controller..." << __E__;
260 delete CorePropertySupervisorBase::
262 CorePropertySupervisorBase::theTRACEController_ =
nullptr;
264 CorePropertySupervisorBase::theTRACEController_ =
267 ->setSupervisorPtr(
this);
269 __SUP_COUT__ <<
"Constructed." << __E__;
273 ARTDAQSupervisor::~ARTDAQSupervisor(
void)
275 __SUP_COUT__ <<
"Destructor." << __E__;
278 __SUP_COUT__ <<
"Calling Py_Finalize()" << __E__;
284 __SUP_COUT__ <<
"Destroying TRACE Controller..." << __E__;
289 __SUP_COUT__ <<
"Destructed." << __E__;
293 void ARTDAQSupervisor::destroy(
void)
295 __SUP_COUT__ <<
"Destroying..." << __E__;
297 if(daqinterface_ptr_ != NULL)
299 __SUP_COUT__ <<
"Calling recover transition" << __E__;
300 std::lock_guard<std::recursive_mutex> lk(daqinterface_pythonMutex_);
302 PyObjectGuard pName(PyUnicode_FromString(
"do_recover"));
304 PyObject_CallMethodObjArgs(daqinterface_ptr_, pName.get(), NULL));
306 __SUP_COUT__ <<
"Making sure that correct state has been reached" << __E__;
308 while(daqinterface_state_ !=
"stopped")
311 __SUP_COUT__ <<
"State is " << daqinterface_state_
312 <<
", waiting 1s and retrying..." << __E__;
317 Py_XDECREF(daqinterface_ptr_);
318 daqinterface_ptr_ = NULL;
321 __SUP_COUT__ <<
"Flusing printouts" << __E__;
324 PyRun_SimpleString(R
"(
326 sys.stdout = sys.__stdout__
327 sys.stderr = sys.__stderr__
333 stringIO_out_ =
nullptr;
334 stringIO_err_ =
nullptr;
336 __SUP_COUT__ <<
"Thread and garbage cleanup" << __E__;
339 "import threading; [t.join() for t in threading.enumerate() if t is not "
340 "threading.main_thread() and not isinstance(t, threading._DummyThread)]");
341 PyRun_SimpleString(
"import gc; gc.collect()");
345 __SUP_COUT__ <<
"Destroyed." << __E__;
349 void ARTDAQSupervisor::init(
void)
353 __SUP_COUT__ <<
"Initializing..." << __E__;
355 std::lock_guard<std::recursive_mutex> lk(daqinterface_pythonMutex_);
358 artdaq::configureMessageFacility(
"ARTDAQSupervisor");
359 __SUP_COUT__ <<
"artdaq MF configured." << __E__;
362 char* daqinterface_dir = getenv(
"ARTDAQ_DAQINTERFACE_DIR");
363 if(daqinterface_dir == NULL)
365 __SS__ <<
"ARTDAQ_DAQINTERFACE_DIR environment variable not set! This "
366 "means that DAQInterface has not been setup!"
372 __SUP_COUT__ <<
"Initializing Python" << __E__;
378 "from io import StringIO\n"
381 " def __init__(self, real, buf):\n"
382 " self.real = real\n"
384 " def write(self, data):\n"
385 " self.real.write(data)\n"
386 " self.buf.write(data)\n"
387 " def flush(self):\n"
388 " self.real.flush()\n"
389 " self.buf.flush()\n"
391 "tee_buffer = StringIO()\n"
392 "sys.stdout = TeeOut(sys.stdout, tee_buffer)\n"
393 "sys.stderr = TeeOut(sys.stderr, tee_buffer)\n");
395 __SUP_COUT__ <<
"Adding DAQInterface directory to PYTHON_PATH" << __E__;
396 PyObject* sysPath = PySys_GetObject(
398 PyObjectGuard programName(PyUnicode_FromString(daqinterface_dir));
399 PyList_Append(sysPath, programName.get());
401 __SUP_COUT__ <<
"Creating Module name" << __E__;
402 PyObjectGuard pName(PyUnicode_FromString(
"rc.control.daqinterface"));
405 __SUP_COUT__ <<
"Importing module" << __E__;
406 PyObjectGuard pModule(PyImport_Import(pName.get()));
408 if(pModule.get() == NULL)
410 std::string err = capturePyErr(
"import rc.control.daqinterface");
411 __SS__ <<
"Failed to load rc.control.daqinterface. Python Exception: "
417 __SUP_COUT__ <<
"Loading python module dictionary" << __E__;
418 PyObject* pDict = PyModule_GetDict(
422 std::string err = capturePyErr(
"module dict");
423 __SS__ <<
"Unable to load module dictionary. Python Exception: "
429 __SUP_COUT__ <<
"Getting DAQInterface object pointer" << __E__;
430 PyObject* di_obj_raw = PyDict_GetItemString(
431 pDict,
"DAQInterface");
432 if(di_obj_raw == NULL)
434 std::string err = capturePyErr(
"DAQInterface lookup");
435 __SS__ <<
"Unable to find 'DAQInterface' in module dictionary. "
440 Py_INCREF(di_obj_raw);
441 PyObjectGuard di_obj_ptr(di_obj_raw);
443 __SUP_COUT__ <<
"Filling out DAQInterface args struct" << __E__;
444 PyObjectGuard pArgs(PyTuple_New(0));
446 PyObjectGuard kwargs(Py_BuildValue(
"{s:s, s:s, s:i, s:i, s:s, s:s}",
460 __SUP_COUT__ <<
"Calling DAQInterface Object Constructor" << __E__;
463 PyObjectGuard sys(PyImport_ImportModule(
"sys"));
464 PyObjectGuard io(PyImport_ImportModule(
"io"));
471 stringIO_out_ = PyObject_CallMethod(io.get(),
"StringIO", NULL);
472 stringIO_err_ = PyObject_CallMethod(io.get(),
"StringIO", NULL);
479 PyObject_SetAttrString(sys.get(),
"stdout", stringIO_out_);
480 PyObject_SetAttrString(sys.get(),
"stderr", stringIO_err_);
486 PyImport_AddModule(
"__main__");
487 PyObject* globals = PyModule_GetDict(mainmod);
490 PyDict_GetItemString(globals,
"tee_buffer");
496 PyObject_Call(di_obj_ptr.get(), pArgs.get(), kwargs.get());
497 if(checkPythonError(daqinterface_ptr_))
499 std::string err = capturePyErr(
"DAQInterface constructor");
500 __SS__ <<
"DAQInterface constructor failed. Python Exception: "
509 PyObject_CallMethod(sys.get(),
"does_not_exist", NULL));
514 PyObjectGuard err_text(
515 PyObject_CallMethod(stringIO_err_,
"getvalue", NULL));
517 __COUT__ <<
"Captured stderr:\n"
518 << PyUnicode_AsUTF8(err_text.get()) <<
"\n";
520 __COUT__ <<
"Capture of stderr failed.";
551 initArtdaqSystemVariables();
553 __SUP_COUT__ <<
"Initialized." << __E__;
557 void ARTDAQSupervisor::transitionConfiguring(toolbox::Event::Reference )
559 __SUP_COUTT__ <<
"transitionConfiguring" << __E__;
561 loadArtdaqSystemVariables();
564 if(RunControlStateMachine::getIterationIndex() == 0 &&
565 RunControlStateMachine::getSubIterationIndex() == 0)
567 thread_error_message_ =
"";
568 thread_progress_bar_.resetProgressBar(0);
569 last_thread_progress_update_ = time(0);
571 CoreSupervisorBase::configureInit();
574 std::thread(&ARTDAQSupervisor::configuringThread,
this).detach();
576 __SUP_COUT__ <<
"Configuring thread started." << __E__;
578 RunControlStateMachine::
579 indicateIterationWork();
583 std::string errorMessage;
585 std::lock_guard<std::mutex> lock(
587 errorMessage = thread_error_message_;
589 int progress = thread_progress_bar_.
read();
590 __SUP_COUTVS__(2, errorMessage);
591 __SUP_COUTVS__(2, progress);
592 __SUP_COUTVS__(2, thread_progress_bar_.
isComplete());
595 if(errorMessage ==
"" &&
596 time(0) - last_thread_progress_update_ > 600)
598 __SUP_SS__ <<
"There has been no update from the configuration thread for "
599 << (time(0) - last_thread_progress_update_)
600 <<
" seconds, assuming something is wrong and giving up! "
601 <<
"Last progress received was " << progress << __E__;
602 errorMessage = ss.str();
609 if(errorMessage ==
"")
611 auto subapps = getSubappInfo();
612 for(
auto& subapp : subapps)
614 if(subapp.status == RunControlStateMachine::FAILED_STATE_NAME)
616 __SUP_SS__ <<
"Component '" << subapp.name
617 <<
"' entered Failed state during configuration! "
618 <<
"(url: " << subapp.url <<
")" << __E__;
619 errorMessage = ss.str();
620 __SUP_COUT_ERR__ <<
"\n" << ss.str();
626 if(errorMessage !=
"")
628 __SUP_SS__ <<
"Error was caught in configuring thread: " << errorMessage
630 __SUP_COUT_ERR__ <<
"\n" << ss.str();
632 theStateMachine_.setErrorMessage(ss.str());
633 throw toolbox::fsm::exception::Exception(
636 "CoreSupervisorBase::transitionConfiguring" ,
644 __SUP_COUTT__ <<
"Not done yet..." << __E__;
648 RunControlStateMachine::
649 indicateIterationWork();
651 if(last_thread_progress_read_ != progress)
653 last_thread_progress_read_ = progress;
654 last_thread_progress_update_ = time(0);
661 __SUP_COUT_INFO__ <<
"Complete configuring transition!" << __E__;
662 __SUP_COUTV__(getProcessInfo_());
670 void ARTDAQSupervisor::configuringThread()
673 std::string uid = theConfigurationManager_
674 ->
getNode(ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME +
675 "/" + CorePropertySupervisorBase::getSupervisorUID() +
676 "/" +
"LinkToSupervisorTable")
679 __COUT__ <<
"Supervisor uid is " << uid <<
", getting supervisor table node" << __E__;
681 const std::string mfSubject_ = supervisorClassNoNamespace_ +
"-" + uid;
685 thread_progress_bar_.
step();
687 set_thread_message_(
"ConfigGen");
689 auto info = ARTDAQTableBase::extractARTDAQInfo(
693 getSupervisorProperty(
"max_fragment_size_bytes", 8888),
694 getSupervisorProperty(
"routing_timeout_ms", 1999),
695 getSupervisorProperty(
"routing_retry_count", 12),
696 &thread_progress_bar_);
699 if(info.processes.count(ARTDAQTableBase::ARTDAQAppType::BoardReader) == 0)
701 __GEN_SS__ <<
"There must be at least one enabled BoardReader!" << __E__;
704 if(info.processes.count(ARTDAQTableBase::ARTDAQAppType::EventBuilder) == 0)
706 __GEN_SS__ <<
"There must be at least one enabled EventBuilder!" << __E__;
710 thread_progress_bar_.
step();
711 set_thread_message_(
"Writing boot.txt");
713 __GEN_COUT__ <<
"Writing boot.txt" << __E__;
715 int debugLevel = theSupervisorNode.
getNode(
"DAQInterfaceDebugLevel").
getValue<
int>();
716 std::string setupScript = theSupervisorNode.
getNode(
"DAQSetupScript").
getValue();
719 std::string bootContent =
723 for(
auto& builder : info.processes[ARTDAQTableBase::ARTDAQAppType::EventBuilder])
724 label_to_proc_type_map_[builder.label] =
"EventBuilder";
725 for(
auto& logger : info.processes[ARTDAQTableBase::ARTDAQAppType::DataLogger])
726 label_to_proc_type_map_[logger.label] =
"DataLogger";
727 for(
auto& dispatcher : info.processes[ARTDAQTableBase::ARTDAQAppType::Dispatcher])
728 label_to_proc_type_map_[dispatcher.label] =
"Dispatcher";
729 for(
auto& rmanager : info.processes[ARTDAQTableBase::ARTDAQAppType::RoutingManager])
730 label_to_proc_type_map_[rmanager.label] =
"RoutingManager";
741 thread_progress_bar_.
step();
742 set_thread_message_(
"Writing Fhicl Files");
744 __GEN_COUT__ <<
"Building configuration directory" << __E__;
746 boost::system::error_code ignored;
751 for(
auto& reader : info.processes[ARTDAQTableBase::ARTDAQAppType::BoardReader])
753 symlink(ARTDAQTableBase::getFlatFHICLFilename(
754 ARTDAQTableBase::ARTDAQAppType::BoardReader, reader.label)
757 reader.label +
".fcl")
760 for(
auto& builder : info.processes[ARTDAQTableBase::ARTDAQAppType::EventBuilder])
762 symlink(ARTDAQTableBase::getFlatFHICLFilename(
763 ARTDAQTableBase::ARTDAQAppType::EventBuilder, builder.label)
766 builder.label +
".fcl")
769 for(
auto& logger : info.processes[ARTDAQTableBase::ARTDAQAppType::DataLogger])
771 symlink(ARTDAQTableBase::getFlatFHICLFilename(
772 ARTDAQTableBase::ARTDAQAppType::DataLogger, logger.label)
775 logger.label +
".fcl")
778 for(
auto& dispatcher : info.processes[ARTDAQTableBase::ARTDAQAppType::Dispatcher])
780 symlink(ARTDAQTableBase::getFlatFHICLFilename(
781 ARTDAQTableBase::ARTDAQAppType::Dispatcher, dispatcher.label)
784 dispatcher.label +
".fcl")
787 for(
auto& rmanager : info.processes[ARTDAQTableBase::ARTDAQAppType::RoutingManager])
789 symlink(ARTDAQTableBase::getFlatFHICLFilename(
790 ARTDAQTableBase::ARTDAQAppType::RoutingManager, rmanager.label)
793 rmanager.label +
".fcl")
797 thread_progress_bar_.
step();
802 std::lock_guard<std::recursive_mutex> lk(daqinterface_pythonMutex_);
804 if(daqinterface_state_ !=
"stopped" && daqinterface_state_ !=
"")
806 __GEN_SS__ <<
"Cannot configure DAQInterface because it is in the wrong state"
807 <<
" (" << daqinterface_state_ <<
" != stopped)!" << __E__;
811 if(daqinterface_ptr_ ==
nullptr)
813 __GEN_SS__ <<
"DAQInterface is not initialized. "
814 "Check earlier Python import/constructor errors (e.g. syntax) "
822 set_thread_message_(
"Calling setdaqcomps");
823 __GEN_COUT__ <<
"Calling setdaqcomps" << __E__;
825 std::lock_guard<std::recursive_mutex> lk(daqinterface_pythonMutex_);
827 __GEN_COUT__ <<
"Status before setdaqcomps: " << daqinterface_state_ << __E__;
829 PyObjectGuard pName1(PyUnicode_FromString(
"setdaqcomps"));
831 PyObjectGuard readerDict(PyDict_New());
832 for(
auto& reader : info.processes[ARTDAQTableBase::ARTDAQAppType::BoardReader])
835 label_to_proc_type_map_[reader.label] =
"BoardReader";
836 PyObjectGuard readerName(PyUnicode_FromString(reader.label.c_str()));
838 int list_size = reader.allowed_processors !=
"" ? 4 : 3;
840 PyObjectGuard readerData(PyList_New(list_size));
841 PyObject* readerHost = PyUnicode_FromString(reader.hostname.c_str());
842 PyObject* readerPort = PyUnicode_FromString(
"-1");
843 PyObject* readerSubsystem =
844 PyUnicode_FromString(std::to_string(reader.subsystem).c_str());
845 PyList_SetItem(readerData.get(), 0, readerHost);
846 PyList_SetItem(readerData.get(), 1, readerPort);
847 PyList_SetItem(readerData.get(), 2, readerSubsystem);
848 if(reader.allowed_processors !=
"")
850 PyObject* readerAllowedProcessors =
851 PyUnicode_FromString(reader.allowed_processors.c_str());
852 PyList_SetItem(readerData.get(), 3, readerAllowedProcessors);
854 PyDict_SetItem(readerDict.get(), readerName.get(), readerData.get());
856 PyObjectGuard res1(PyObject_CallMethodObjArgs(
857 daqinterface_ptr_, pName1.get(), readerDict.get(), NULL));
858 __COUT_MULTI_LBL__(0, captureStderrAndStdout_(
"setdaqcomps"),
"setdaqcomps");
860 if(checkPythonError(res1.get()))
862 std::string err_msg = capturePyErr(
"setdaqcomps");
863 __GEN_SS__ <<
"Error calling setdaqcomps: " << err_msg << __E__;
868 __GEN_COUT__ <<
"Status after setdaqcomps: " << daqinterface_state_ << __E__;
871 thread_progress_bar_.
step();
874 set_thread_message_(
"Calling do_boot");
875 __GEN_COUT_INFO__ <<
"Calling do_boot" << __E__;
876 std::string doBootOutput =
"";
878 std::lock_guard<std::recursive_mutex> lk(daqinterface_pythonMutex_);
880 __GEN_COUT__ <<
"Status before boot: " << daqinterface_state_ << __E__;
883 PyObjectGuard pNameBoot(PyUnicode_FromString(
"do_boot"));
884 PyObjectGuard pBootArgs(PyUnicode_FromString(
888 PyObjectGuard resBoot1(PyObject_CallMethodObjArgs(
889 daqinterface_ptr_, pNameBoot.get(), pBootArgs.get(), NULL));
891 doBootOutput = captureStderrAndStdout_(
"do_boot");
892 __COUT_MULTI_LBL__(0, doBootOutput,
"do_boot");
894 if(checkPythonError(resBoot1.get()))
898 std::string err1 = capturePyErr(
"do_boot");
900 __GEN_COUT_INFO__ <<
"Error on first boot attempt: " << err1
901 <<
". Recovering and retrying..." << __E__;
904 PyObjectGuard pNameRecover(PyUnicode_FromString(
"do_recover"));
905 PyObjectGuard resRecover(
906 PyObject_CallMethodObjArgs(daqinterface_ptr_, pNameRecover.get(), NULL));
907 __COUT_MULTI_LBL__(0, captureStderrAndStdout_(
"do_recover"),
"do_recover");
909 if(checkPythonError(resRecover.get()))
912 std::string errRec = capturePyErr(
"do_recover");
914 std::stringstream oss;
915 oss <<
"Error calling recover transition!!!! " << errRec;
916 if(doBootOutput.size() > OUT_ON_ERR_SIZE)
917 oss <<
"... last " << OUT_ON_ERR_SIZE
918 <<
" chars: " << doBootOutput.substr(doBootOutput.size() - 1000);
923 __GEN_SS__ << oss.str() << __E__;
928 thread_progress_bar_.
step();
929 set_thread_message_(
"Calling do_boot (retry)");
930 __GEN_COUT_INFO__ <<
"Calling do_boot again" << __E__;
933 PyObjectGuard resBoot2(PyObject_CallMethodObjArgs(
934 daqinterface_ptr_, pNameBoot.get(), pBootArgs.get(), NULL));
936 doBootOutput = captureStderrAndStdout_(
"do_boot (retry)");
937 __COUT_MULTI_LBL__(0, doBootOutput,
"do_boot (retry)");
939 if(checkPythonError(resBoot2.get()))
942 std::string err2 = capturePyErr(
"do_boot retry");
944 std::stringstream oss;
945 oss <<
"Error calling boot transition (2nd try): " << err2;
946 if(doBootOutput.size() > OUT_ON_ERR_SIZE)
947 oss <<
"... last " << OUT_ON_ERR_SIZE
948 <<
" chars: " << doBootOutput.substr(doBootOutput.size() - 1000);
952 __GEN_SS__ << oss.str() << __E__;
958 if(daqinterface_state_ !=
"booted")
960 std::cout <<
"Do boot output on error: \n" << doBootOutput << __E__;
961 __GEN_SS__ <<
"DAQInterface boot transition failed! "
962 <<
"Status after boot attempt: " << daqinterface_state_ << __E__;
964 if(doBootOutput.size() > OUT_ON_ERR_SIZE)
965 ss <<
"... last " << OUT_ON_ERR_SIZE
966 <<
" characters: " << doBootOutput.substr(doBootOutput.size() - 1000);
971 __GEN_COUT__ <<
"Status after boot: " << daqinterface_state_ << __E__;
974 thread_progress_bar_.
step();
977 set_thread_message_(
"Calling do_config");
978 __GEN_COUT_INFO__ <<
"Calling do_config" << __E__;
979 std::string doConfigOutput =
"";
981 std::lock_guard<std::recursive_mutex> lk(daqinterface_pythonMutex_);
983 __GEN_COUT__ <<
"Status before config: " << daqinterface_state_ << __E__;
988 PyObjectGuard pName3(PyUnicode_FromString(
"do_config"));
990 PyObjectGuard pArg(Py_BuildValue(
"[s]", FAKE_CONFIG_NAME));
993 PyObjectGuard res3(PyObject_CallMethodObjArgs(
994 daqinterface_ptr_, pName3.get(), pArg.get(), NULL));
997 if(checkPythonError(res3.get()))
1000 std::string err = capturePyErr(
"do_config");
1003 doConfigOutput = captureStderrAndStdout_(
"do_config");
1005 __GEN_SS__ <<
"Error calling config transition: " << err << __E__;
1010 doConfigOutput = captureStderrAndStdout_(
"do_config");
1011 __COUT_MULTI_LBL__(0, doConfigOutput,
"do_config");
1015 PyObjectGuard strRes(PyObject_Str(res3.get()));
1016 const char* res_cstr =
"";
1019 res_cstr = PyUnicode_AsUTF8(strRes.get());
1022 __SUP_COUTT__ <<
"do_config result=" << (res_cstr ? res_cstr :
"N/A")
1027 if(daqinterface_state_ !=
"ready")
1029 __GEN_SS__ <<
"DAQInterface config transition failed!" << __E__
1030 <<
"Supervisor state: \"" << daqinterface_state_
1031 <<
"\" != \"ready\" " << __E__;
1032 auto doConfigOutput_recover_i =
1033 doConfigOutput.find(
"RECOVER transition underway");
1034 if(doConfigOutput_recover_i == std::string::npos)
1035 ss << doConfigOutput;
1036 else if(doConfigOutput_recover_i >
1038 ss <<
"... tail of " << OUT_ON_ERR_SIZE <<
" characters before recovery: "
1039 << doConfigOutput.substr(
1040 doConfigOutput_recover_i - OUT_ON_ERR_SIZE +
1041 std::string(
"RECOVER transition underway").size(),
1044 ss << doConfigOutput.substr(
1046 doConfigOutput_recover_i +
1047 std::string(
"RECOVER transition underway").size());
1050 __GEN_COUT__ <<
"Status after config: " << daqinterface_state_ << __E__;
1054 set_thread_message_(
"Configured");
1055 __GEN_COUT_INFO__ <<
"Configured." << __E__;
1058 catch(
const std::runtime_error& e)
1060 set_thread_message_(
"ERROR");
1061 __SS__ <<
"Error was caught while configuring: " << e.what() << __E__;
1062 __COUT_ERR__ <<
"\n" << ss.str();
1063 std::lock_guard<std::mutex> lock(thread_mutex_);
1064 thread_error_message_ = ss.str();
1068 set_thread_message_(
"ERROR");
1069 __SS__ <<
"Unknown error was caught while configuring. Please checked the logs."
1071 __COUT_ERR__ <<
"\n" << ss.str();
1073 artdaq::ExceptionHandler(artdaq::ExceptionHandlerRethrow::no, ss.str());
1075 std::lock_guard<std::mutex> lock(thread_mutex_);
1076 thread_error_message_ = ss.str();
1083 set_thread_message_(
"Halting");
1084 __SUP_COUT__ <<
"Halting..." << __E__;
1089 std::unique_lock<std::recursive_mutex> lk(daqinterface_pythonMutex_,
1093 __COUTS__(50) <<
"Do not have python lock for halt. tries=" << tries << __E__;
1097 __COUTS__(50) <<
"Have python lock!" << __E__;
1101 __SUP_COUT__ <<
"Status before halt: " << daqinterface_state_ << __E__;
1103 if(daqinterface_state_ ==
"running")
1106 PyObjectGuard pName(PyUnicode_FromString(
"do_stop_running"));
1108 PyObject_CallMethodObjArgs(daqinterface_ptr_, pName.get(), NULL));
1110 0, captureStderrAndStdout_(
"do_stop_running"),
"do_stop_running");
1112 if(res.get() == NULL)
1114 std::string err = capturePyErr();
1115 __SS__ <<
"Error calling DAQ Interface stop transition: " << err
1123 if(daqinterface_state_ ==
"stopped" || daqinterface_state_ ==
"")
1125 __SUP_COUT__ <<
"DAQInterface already stopped, skipping Shutdown command."
1130 PyObjectGuard pName(PyUnicode_FromString(
"do_command"));
1131 PyObjectGuard pArg(PyUnicode_FromString(
"Shutdown"));
1132 PyObjectGuard res(PyObject_CallMethodObjArgs(
1133 daqinterface_ptr_, pName.get(), pArg.get(), NULL));
1135 0, captureStderrAndStdout_(
"do_command Shutdown"),
"do_command Shutdown");
1137 if(checkPythonError(res.get()))
1139 std::string err = capturePyErr(
"do_command Shutdown");
1140 __SS__ <<
"Error calling DAQ Interface halt transition: " << err << __E__;
1146 __SUP_COUT__ <<
"Status after halt: " << daqinterface_state_ << __E__;
1152 __SUP_SS__ <<
"Failed to acquire python lock for halting after " << tries
1153 <<
" tries, giving up! Is it possible the configure thread is stuck?"
1158 __SUP_COUT__ <<
"Halted." << __E__;
1159 set_thread_message_(
"Halted");
1161 catch(
const std::runtime_error& e)
1163 const std::string transitionName =
"Halting";
1165 if(theStateMachine_.getProvenanceStateName() ==
1166 RunControlStateMachine::FAILED_STATE_NAME ||
1167 theStateMachine_.getProvenanceStateName() ==
1168 RunControlStateMachine::HALTED_STATE_NAME)
1170 __SUP_COUT_INFO__ <<
"Error was caught while halting (but ignoring because "
1171 "previous state was '"
1172 << RunControlStateMachine::FAILED_STATE_NAME
1173 <<
"'): " << e.what() << __E__;
1177 __SUP_SS__ <<
"Error was caught while " << transitionName <<
": " << e.what()
1179 __SUP_COUT_ERR__ <<
"\n" << ss.str();
1180 theStateMachine_.setErrorMessage(ss.str());
1181 throw toolbox::fsm::exception::Exception(
1182 "Transition Error" ,
1184 "ARTDAQSupervisorBase::transition" + transitionName ,
1192 const std::string transitionName =
"Halting";
1194 if(theStateMachine_.getProvenanceStateName() ==
1195 RunControlStateMachine::FAILED_STATE_NAME ||
1196 theStateMachine_.getProvenanceStateName() ==
1197 RunControlStateMachine::HALTED_STATE_NAME)
1199 __SUP_COUT_INFO__ <<
"Unknown error was caught while halting (but ignoring "
1200 "because previous state was '"
1201 << RunControlStateMachine::FAILED_STATE_NAME <<
"')." << __E__;
1205 __SUP_SS__ <<
"Unknown error was caught while " << transitionName
1206 <<
". Please checked the logs." << __E__;
1207 __SUP_COUT_ERR__ <<
"\n" << ss.str();
1208 theStateMachine_.setErrorMessage(ss.str());
1210 artdaq::ExceptionHandler(artdaq::ExceptionHandlerRethrow::no, ss.str());
1212 throw toolbox::fsm::exception::Exception(
1213 "Transition Error" ,
1215 "ARTDAQSupervisorBase::transition" + transitionName ,
1226 set_thread_message_(
"Initializing");
1227 __SUP_COUT__ <<
"Initializing..." << __E__;
1229 __SUP_COUT__ <<
"Initialized." << __E__;
1230 set_thread_message_(
"Initialized");
1232 catch(
const std::runtime_error& e)
1234 __SS__ <<
"Error was caught while Initializing: " << e.what() << __E__;
1239 __SS__ <<
"Unknown error was caught while Initializing. Please checked the logs."
1241 artdaq::ExceptionHandler(artdaq::ExceptionHandlerRethrow::no, ss.str());
1246 void ARTDAQSupervisor::transitionPausing(toolbox::Event::Reference )
1249 set_thread_message_(
"Pausing");
1250 __SUP_COUT__ <<
"Pausing..." << __E__;
1251 std::lock_guard<std::recursive_mutex> lk(daqinterface_pythonMutex_);
1254 __SUP_COUT__ <<
"Status before pause: " << daqinterface_state_ << __E__;
1256 PyObjectGuard pName(PyUnicode_FromString(
"do_command"));
1257 PyObjectGuard pArg(PyUnicode_FromString(
"Pause"));
1259 PyObject_CallMethodObjArgs(daqinterface_ptr_, pName.get(), pArg.get(), NULL));
1261 0, captureStderrAndStdout_(
"do_command Pause"),
"do_command Pause");
1263 if(checkPythonError(res.get()))
1265 std::string err = capturePyErr(
"do_command Pause");
1266 __SS__ <<
"Error calling DAQ Interface Pause transition: " << err << __E__;
1271 __SUP_COUT__ <<
"Status after pause: " << daqinterface_state_ << __E__;
1273 __SUP_COUT__ <<
"Paused." << __E__;
1274 set_thread_message_(
"Paused");
1276 catch(
const std::runtime_error& e)
1278 __SS__ <<
"Error was caught while Pausing: " << e.what() << __E__;
1283 __SS__ <<
"Unknown error was caught while Pausing. Please checked the logs." << __E__;
1284 artdaq::ExceptionHandler(artdaq::ExceptionHandlerRethrow::no, ss.str());
1289 void ARTDAQSupervisor::transitionResuming(toolbox::Event::Reference )
1292 set_thread_message_(
"Resuming");
1293 __SUP_COUT__ <<
"Resuming..." << __E__;
1294 std::lock_guard<std::recursive_mutex> lk(daqinterface_pythonMutex_);
1297 __SUP_COUT__ <<
"Status before resume: " << daqinterface_state_ << __E__;
1298 PyObjectGuard pName(PyUnicode_FromString(
"do_command"));
1299 PyObjectGuard pArg(PyUnicode_FromString(
"Resume"));
1301 PyObject_CallMethodObjArgs(daqinterface_ptr_, pName.get(), pArg.get(), NULL));
1303 0, captureStderrAndStdout_(
"do_command Resume"),
"do_command Resume");
1305 if(checkPythonError(res.get()))
1307 std::string err = capturePyErr(
"do_command Resume");
1308 __SS__ <<
"Error calling DAQ Interface Resume transition: " << err << __E__;
1313 __SUP_COUT__ <<
"Status after resume: " << daqinterface_state_ << __E__;
1314 __SUP_COUT__ <<
"Resumed." << __E__;
1315 set_thread_message_(
"Resumed");
1317 catch(
const std::runtime_error& e)
1319 __SS__ <<
"Error was caught while Resuming: " << e.what() << __E__;
1324 __SS__ <<
"Unknown error was caught while Resuming. Please checked the logs."
1326 artdaq::ExceptionHandler(artdaq::ExceptionHandlerRethrow::no, ss.str());
1331 void ARTDAQSupervisor::transitionStarting(toolbox::Event::Reference )
1334 __SUP_COUT__ <<
"transitionStarting" << __E__;
1342 const unsigned int startIteration = RunControlStateMachine::getIterationIndex();
1344 if(startIteration == 0)
1347 __SUP_COUT_INFO__ <<
"Step 0: idle, waiting for DTCs to SoftReset." << __E__;
1348 RunControlStateMachine::indicateIterationWork();
1352 if(startIteration == 1 && RunControlStateMachine::getSubIterationIndex() == 0)
1355 thread_error_message_ =
"";
1356 thread_progress_bar_.resetProgressBar(0);
1357 last_thread_progress_update_ = time(0);
1359 std::thread(&ARTDAQSupervisor::startingThread,
this).detach();
1361 __SUP_COUT_INFO__ <<
"Step 1: artdaq starting thread launched." << __E__;
1366 RunControlStateMachine::indicateSubIterationWork();
1370 if(startIteration == 1)
1372 std::string errorMessage;
1374 std::lock_guard<std::mutex> lock(
1376 errorMessage = thread_error_message_;
1378 int progress = thread_progress_bar_.
read();
1379 __SUP_COUTV__(errorMessage);
1380 __SUP_COUTV__(progress);
1381 __SUP_COUTV__(thread_progress_bar_.
isComplete());
1383 if(errorMessage ==
"" && time(0) - last_thread_progress_update_ > 600)
1385 __SUP_SS__ <<
"There has been no update from the start thread for "
1386 << (time(0) - last_thread_progress_update_)
1387 <<
" seconds, assuming something is wrong and giving up! "
1388 <<
"Last progress received was " << progress << __E__;
1389 errorMessage = ss.str();
1392 if(errorMessage !=
"")
1394 __SUP_SS__ <<
"Error was caught in starting thread: " << errorMessage
1396 __SUP_COUT_ERR__ <<
"\n" << ss.str();
1398 theStateMachine_.setErrorMessage(ss.str());
1399 throw toolbox::fsm::exception::Exception(
1400 "Transition Error" ,
1402 "CoreSupervisorBase::transitionStarting" ,
1410 __SUP_COUT__ <<
"Step 1: artdaq not done yet..." << __E__;
1412 RunControlStateMachine::indicateSubIterationWork();
1414 if(last_thread_progress_read_ != progress)
1416 last_thread_progress_read_ = progress;
1417 last_thread_progress_update_ = time(0);
1427 __SUP_COUT_INFO__ <<
"Step 1: artdaq starting transition completed!" << __E__;
1428 __SUP_COUTV__(getProcessInfo_());
1429 RunControlStateMachine::indicateIterationWork();
1439 catch(
const std::runtime_error& e)
1441 __SS__ <<
"Error was caught while Starting: " << e.what() << __E__;
1446 __SS__ <<
"Unknown error was caught while Starting. Please checked the logs."
1448 artdaq::ExceptionHandler(artdaq::ExceptionHandlerRethrow::no, ss.str());
1453 void ARTDAQSupervisor::startingThread()
1456 std::string uid = theConfigurationManager_
1457 ->
getNode(ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME +
1458 "/" + CorePropertySupervisorBase::getSupervisorUID() +
1459 "/" +
"LinkToSupervisorTable")
1462 __COUT__ <<
"Supervisor uid is " << uid <<
", getting supervisor table node" << __E__;
1463 const std::string mfSubject_ = supervisorClassNoNamespace_ +
"-" + uid;
1464 __GEN_COUT__ <<
"Starting..." << __E__;
1465 set_thread_message_(
"Starting");
1467 thread_progress_bar_.
step();
1470 std::lock_guard<std::recursive_mutex> lk(daqinterface_pythonMutex_);
1472 __GEN_COUT__ <<
"Status before start: " << daqinterface_state_ << __E__;
1473 auto runNumber = SOAPUtilities::translate(theStateMachine_.getCurrentMessage())
1475 .getValue(
"RunNumber");
1477 thread_progress_bar_.
step();
1479 __GEN_COUT_INFO__ <<
"Calling do_start_running" << __E__;
1480 PyObjectGuard pName(PyUnicode_FromString(
"do_start_running"));
1481 int run_number = std::stoi(runNumber);
1482 PyObjectGuard pStateArgs(PyLong_FromLong(run_number));
1483 PyObjectGuard res(PyObject_CallMethodObjArgs(
1484 daqinterface_ptr_, pName.get(), pStateArgs.get(), NULL));
1485 std::string doStartOutput;
1487 thread_progress_bar_.
step();
1489 if(checkPythonError(res.get()))
1491 std::string err = capturePyErr(
"do_start_running");
1492 doStartOutput = captureStderrAndStdout_(
"do_start_running");
1493 __SS__ <<
"Error calling start transition: " << err << __E__;
1494 if(doStartOutput.size() > OUT_ON_ERR_SIZE)
1495 ss <<
"... last " << OUT_ON_ERR_SIZE <<
" characters: "
1496 << doStartOutput.substr(doStartOutput.size() - OUT_ON_ERR_SIZE);
1498 ss << doStartOutput;
1502 doStartOutput = captureStderrAndStdout_(
"do_start_running");
1503 __COUT_MULTI_LBL__(0, doStartOutput,
"do_start_running");
1506 thread_progress_bar_.
step();
1508 __GEN_COUT__ <<
"Status after start: " << daqinterface_state_ << __E__;
1509 if(daqinterface_state_ !=
"running")
1511 __SS__ <<
"DAQInterface start transition failed!" << __E__
1512 <<
"DAQInterface state: \"" << daqinterface_state_
1513 <<
"\" != \"running\" " << __E__;
1514 if(doStartOutput.size() > OUT_ON_ERR_SIZE)
1515 ss <<
"... last " << OUT_ON_ERR_SIZE <<
" characters: "
1516 << doStartOutput.substr(doStartOutput.size() - OUT_ON_ERR_SIZE);
1518 ss << doStartOutput;
1522 thread_progress_bar_.
step();
1525 set_thread_message_(
"Started");
1526 thread_progress_bar_.
step();
1528 __GEN_COUT_INFO__ <<
"Started." << __E__;
1532 catch(
const std::runtime_error& e)
1534 __SS__ <<
"Error was caught while Starting: " << e.what() << __E__;
1535 __COUT_ERR__ <<
"\n" << ss.str();
1536 std::lock_guard<std::mutex> lock(thread_mutex_);
1537 thread_error_message_ = ss.str();
1541 __SS__ <<
"Unknown error was caught while Starting. Please checked the logs."
1543 __COUT_ERR__ <<
"\n" << ss.str();
1545 artdaq::ExceptionHandler(artdaq::ExceptionHandlerRethrow::no, ss.str());
1547 std::lock_guard<std::mutex> lock(thread_mutex_);
1548 thread_error_message_ = ss.str();
1552 void ARTDAQSupervisor::transitionStopping(toolbox::Event::Reference )
1555 __SUP_COUT__ <<
"Stopping..." << __E__;
1556 set_thread_message_(
"Stopping");
1557 std::lock_guard<std::recursive_mutex> lk(daqinterface_pythonMutex_);
1559 __SUP_COUT__ <<
"Status before stop: " << daqinterface_state_ << __E__;
1560 PyObjectGuard pName(PyUnicode_FromString(
"do_stop_running"));
1561 PyObjectGuard res(PyObject_CallMethodObjArgs(daqinterface_ptr_, pName.get(), NULL));
1562 __COUT_MULTI_LBL__(0, captureStderrAndStdout_(
"do_stop_running"),
"do_stop_running");
1564 if(checkPythonError(res.get()))
1566 std::string err = capturePyErr(
"do_stop_running");
1567 __SS__ <<
"Error calling DAQ Interface stop transition: " << err << __E__;
1571 __SUP_COUT__ <<
"Status after stop: " << daqinterface_state_ << __E__;
1572 __SUP_COUT__ <<
"Stopped." << __E__;
1573 set_thread_message_(
"Stopped");
1575 catch(
const std::runtime_error& e)
1577 __SS__ <<
"Error was caught while Stopping: " << e.what() << __E__;
1582 __SS__ <<
"Unknown error was caught while Stopping. Please checked the logs."
1584 artdaq::ExceptionHandler(artdaq::ExceptionHandlerRethrow::no, ss.str());
1589 void ots::ARTDAQSupervisor::enteringError(toolbox::Event::Reference )
1591 __SUP_COUT__ <<
"Entering error recovery state" << __E__;
1592 std::lock_guard<std::recursive_mutex> lk(daqinterface_pythonMutex_);
1594 __SUP_COUT__ <<
"Status before error: " << daqinterface_state_ << __E__;
1596 PyObjectGuard pName(PyUnicode_FromString(
"do_recover"));
1597 PyObjectGuard res(PyObject_CallMethodObjArgs(daqinterface_ptr_, pName.get(), NULL));
1598 __COUT_MULTI_LBL__(0, captureStderrAndStdout_(
"do_recover"),
"do_recover");
1600 if(checkPythonError(res.get()))
1602 std::string err = capturePyErr(
"do_recover");
1604 __SUP_COUT_WARN__ <<
"Error calling DAQ Interface recover transition: " << err
1610 __SUP_COUT__ <<
"Status after error: " << daqinterface_state_ << __E__;
1611 __SUP_COUT__ <<
"EnteringError DONE." << __E__;
1615 std::vector<SupervisorInfo::SubappInfo> ots::ARTDAQSupervisor::getSubappInfo(
void)
1617 auto apps = getAndParseProcessInfo_();
1619 std::map<int, SupervisorInfo::SubappInfo> subapp_infos;
1620 for(
auto& app : apps)
1624 info.
name = app.label;
1625 info.detail =
"Rank " + std::to_string(app.rank) +
", subsystem " +
1626 std::to_string(app.subsystem);
1627 info.lastStatusTime = time(0);
1628 info.progress = 100;
1629 info.status = artdaqStateToOtsState(app.state);
1630 info.url =
"http://" + app.host +
":" + std::to_string(app.port) +
"/RPC2";
1631 info.class_name =
"ARTDAQ " + labelToProcType_(app.label);
1633 subapp_infos[app.rank] = info;
1636 std::vector<SupervisorInfo::SubappInfo> output;
1637 for(
auto& [rank, info] : subapp_infos)
1639 output.push_back(info);
1648 bool ots::ARTDAQSupervisor::checkPythonError(PyObject* result)
1650 if(result == NULL || PyErr_Occurred())
1661 std::string ots::ARTDAQSupervisor::capturePyErr(std::string label )
1663 std::string err_msg =
"Unknown Python Error";
1664 PyObject * pType, *pValue, *pTraceback;
1665 PyErr_Fetch(&pType, &pValue, &pTraceback);
1666 PyErr_NormalizeException(&pType, &pValue, &pTraceback);
1671 PyObjectGuard traceback_module(PyImport_ImportModule(
"traceback"));
1672 if(traceback_module.get() != NULL)
1674 PyObjectGuard format_exception(
1675 PyObject_GetAttrString(traceback_module.get(),
"format_exception"));
1676 if(format_exception.get() != NULL)
1678 PyObjectGuard formatted(
1679 PyObject_CallFunctionObjArgs(format_exception.get(),
1681 pValue ? pValue : Py_None,
1682 pTraceback ? pTraceback : Py_None,
1684 if(formatted.get() != NULL)
1687 PyObjectGuard empty_string(PyUnicode_FromString(
""));
1688 PyObjectGuard joined(
1689 PyUnicode_Join(empty_string.get(), formatted.get()));
1690 if(joined.get() != NULL)
1692 const char* traceback_cstr = PyUnicode_AsUTF8(joined.get());
1694 err_msg = traceback_cstr;
1701 if(err_msg ==
"Unknown Python Error" && pValue != NULL)
1703 PyObjectGuard pStr(PyObject_Str(pValue));
1704 if(pStr.get() != NULL)
1706 const char* error_cstr = PyUnicode_AsUTF8(pStr.get());
1708 err_msg = error_cstr;
1715 Py_XDECREF(pTraceback);
1719 err_msg = label +
":\n" + err_msg;
1725 std::string ots::ARTDAQSupervisor::captureStderrAndStdout_(std::string label )
1730 if(PyErr_Occurred())
1735 std::string outString =
"";
1736 PyObjectGuard out(PyObject_CallMethod(stringIO_out_,
"getvalue", NULL));
1738 if(checkPythonError(out.get()))
1741 capturePyErr(
"captureStderrAndStdout getvalue");
1745 const char* text = PyUnicode_AsUTF8(out.get());
1747 return text ? text :
"";
1750 void ots::ARTDAQSupervisor::getDAQState_()
1752 __SUP_COUTS__(50) <<
"Getting DAQInterface python lock" << __E__;
1753 std::lock_guard<std::recursive_mutex> lk(daqinterface_pythonMutex_);
1754 __SUP_COUTS__(50) <<
"Have DAQInterface python lock" << __E__;
1756 if(daqinterface_ptr_ == NULL)
1758 daqinterface_state_ =
"";
1759 __SUP_COUT_WARN__ <<
"daqinterface_ptr_ is not initialized!" << __E__;
1764 PyObjectGuard pName(PyUnicode_FromString(
"state"));
1765 PyObjectGuard pArg(PyUnicode_FromString(
"DAQInterface"));
1776 PyObject_CallMethodObjArgs(daqinterface_ptr_, pName.get(), pArg.get(), NULL));
1778 if(checkPythonError(res.get()))
1783 std::string err_msg = capturePyErr(
"state");
1785 std::ostringstream ss;
1786 ss <<
"Attempt n " << tries
1787 <<
". Error calling 'state'. Python Exception: " << err_msg;
1791 __COUT_ERR__ << ss.str() << __E__;
1792 daqinterface_state_ =
"ERROR";
1796 __COUT__ << ss.str() << __E__;
1805 PyObjectGuard strRes(PyObject_Str(res.get()));
1808 daqinterface_state_ = std::string(PyUnicode_AsUTF8(strRes.get()));
1813 daqinterface_state_ =
"UNKNOWN";
1816 __SUP_COUTS__(20) <<
"getDAQState_ state=" << daqinterface_state_ << __E__;
1824 std::string ots::ARTDAQSupervisor::getProcessInfo_(
void)
1826 __SUP_COUTS__(50) <<
"Getting DAQInterface state lock" << __E__;
1827 std::lock_guard<std::recursive_mutex> lk(daqinterface_pythonMutex_);
1828 __SUP_COUTS__(50) <<
"Have DAQInterface state lock" << __E__;
1830 if(daqinterface_ptr_ ==
nullptr)
1835 PyObjectGuard pName(PyUnicode_FromString(
"artdaq_process_info"));
1836 PyObjectGuard pArg(PyUnicode_FromString(
"DAQInterface"));
1837 PyObjectGuard pArg2(PyBool_FromLong(
true));
1838 PyObjectGuard res(PyObject_CallMethodObjArgs(
1839 daqinterface_ptr_, pName.get(), pArg.get(), pArg2.get(), NULL));
1841 if(checkPythonError(res.get()))
1843 std::string err = capturePyErr(
"artdaq_process_info");
1844 __SS__ <<
"Error calling artdaq_process_info function: " << err << __E__;
1849 std::lock_guard<std::mutex> lock(daqinterface_statusMutex_);
1850 daqinterface_status_ = std::string(PyUnicode_AsUTF8(res.get()));
1851 return daqinterface_status_;
1854 std::string ots::ARTDAQSupervisor::artdaqStateToOtsState(std::string state)
1856 if(state ==
"nonexistent" || state ==
"nonexistant")
1857 return RunControlStateMachine::INITIAL_STATE_NAME;
1858 if(state ==
"Ready")
1859 return "Configured";
1860 if(state ==
"Running")
1861 return RunControlStateMachine::RUNNING_STATE_NAME;
1862 if(state ==
"Paused")
1863 return RunControlStateMachine::PAUSED_STATE_NAME;
1864 if(state ==
"Stopped")
1865 return RunControlStateMachine::HALTED_STATE_NAME;
1867 TLOG(TLVL_WARNING) <<
"Unrecognized state name " << state;
1868 return RunControlStateMachine::FAILED_STATE_NAME;
1871 std::string ots::ARTDAQSupervisor::labelToProcType_(std::string label)
1873 if(label_to_proc_type_map_.count(label))
1875 return label_to_proc_type_map_[label];
1882 std::list<ots::ARTDAQSupervisor::DAQInterfaceProcessInfo>
1883 ots::ARTDAQSupervisor::getAndParseProcessInfo_()
1885 std::list<ots::ARTDAQSupervisor::DAQInterfaceProcessInfo> output;
1890 std::unique_lock<std::recursive_mutex> lk(daqinterface_pythonMutex_,
1894 __COUTS__(50) <<
"Do not have python lock." << __E__;
1895 std::lock_guard<std::mutex> lock(daqinterface_statusMutex_);
1896 info = daqinterface_status_;
1900 __COUTS__(50) <<
"Have python lock!" << __E__;
1901 info = getProcessInfo_();
1903 __COUTVS__(20, info);
1905 auto procs = tokenize_(info);
1914 std::regex re(
"(.*?) at ([^:]*):(\\d+) \\(subsystem (\\d+), rank (\\d+)\\): (.*)");
1916 for(
auto& proc : procs)
1919 if(std::regex_match(proc, match, re))
1921 DAQInterfaceProcessInfo info;
1923 info.label = match[1];
1924 info.host = match[2];
1925 info.port = std::stoi(match[3]);
1926 info.subsystem = std::stoi(match[4]);
1927 info.rank = std::stoi(match[5]);
1928 info.state = match[6];
1930 output.push_back(info);
1938 std::unique_ptr<artdaq::CommanderInterface>>>
1939 ots::ARTDAQSupervisor::makeCommandersFromProcessInfo()
1942 std::pair<DAQInterfaceProcessInfo, std::unique_ptr<artdaq::CommanderInterface>>>
1944 auto infos = getAndParseProcessInfo_();
1946 for(
auto& info : infos)
1948 artdaq::Commandable cm;
1949 fhicl::ParameterSet ps;
1951 ps.put<std::string>(
"commanderPluginType",
"xmlrpc");
1952 ps.put<
int>(
"id", info.port);
1953 ps.put<std::string>(
"server_url", info.host);
1955 output.emplace_back(std::make_pair<DAQInterfaceProcessInfo,
1956 std::unique_ptr<artdaq::CommanderInterface>>(
1957 std::move(info), artdaq::MakeCommanderPlugin(ps, cm)));
1972 std::set<std::string> ots::ARTDAQSupervisor::getConfiguredArtdaqHosts(
void)
1974 std::set<std::string> hosts;
1979 supervisorNode,
false ,
false );
1980 for(
const auto& typeProcs : info.processes)
1981 for(
const auto& proc : typeProcs.second)
1982 if(proc.status && !proc.hostname.empty())
1983 hosts.insert(proc.hostname);
1985 catch(
const std::exception& e)
1987 __SUP_COUT_ERR__ <<
"Failed to extract configured artdaq hosts: " << e.what()
1993 <<
"Failed to extract configured artdaq hosts (unknown exception)." << __E__;
2001 std::list<std::string> ots::ARTDAQSupervisor::tokenize_(std::string
const& input)
2004 std::list<std::string> output;
2006 while(pos != std::string::npos && pos < input.size())
2008 auto newpos = input.find(
'\n', pos);
2009 if(newpos != std::string::npos)
2011 output.emplace_back(input, pos, newpos - pos);
2017 output.emplace_back(input, pos);
2026 void ots::ARTDAQSupervisor::daqinterfaceRunner_()
2029 TLOG(TLVL_TRACE) <<
"Runner thread starting";
2030 runner_running_ =
true;
2031 while(runner_running_)
2033 if(daqinterface_ptr_ != NULL)
2035 std::unique_lock<std::recursive_mutex> lk(daqinterface_pythonMutex_);
2037 std::string state_before = daqinterface_state_;
2039 __SUP_COUTS__(2) <<
"Runner state_before=" << state_before
2040 <<
" state now=" << daqinterface_state_
2041 <<
" ?= running, ready, or booted" << __E__;
2043 if(daqinterface_state_ ==
"running" || daqinterface_state_ ==
"ready" ||
2044 daqinterface_state_ ==
"booted")
2048 TLOG(TLVL_TRACE) <<
"Calling DAQInterface::check_proc_heartbeats";
2049 PyObjectGuard pName(PyUnicode_FromString(
"check_proc_heartbeats"));
2051 PyObject_CallMethodObjArgs(daqinterface_ptr_, pName.get(), NULL));
2052 __COUT_MULTI_LBL__(1,
2053 captureStderrAndStdout_(
"check_proc_heartbeats"),
2054 "check_proc_heartbeats");
2056 <<
"Done with DAQInterface::check_proc_heartbeats call";
2058 if(res.get() == NULL)
2060 runner_running_ =
false;
2061 std::string err = capturePyErr(
"check_proc_heartbeats");
2062 __SS__ <<
"Error calling check_proc_heartbeats function: " << err
2068 catch(cet::exception& ex)
2070 runner_running_ =
false;
2071 std::string err = capturePyErr(
"check_proc_heartbeats");
2072 __SS__ <<
"An cet::exception occurred while calling "
2073 "check_proc_heartbeats function "
2074 << ex.explain_self() <<
": " << err << __E__;
2078 catch(std::exception& ex)
2080 runner_running_ =
false;
2081 std::string err = capturePyErr(
"check_proc_heartbeats");
2082 __SS__ <<
"An std::exception occurred while calling "
2083 "check_proc_heartbeats function: "
2084 << ex.what() <<
"\n\n"
2091 runner_running_ =
false;
2092 std::string err = capturePyErr(
"check_proc_heartbeats");
2093 __SS__ <<
"An unknown Error occurred while calling "
2094 "check_proc_heartbeats function: "
2102 if(daqinterface_state_ != state_before)
2104 runner_running_ =
false;
2106 __SS__ <<
"DAQInterface state unexpectedly changed from "
2107 << state_before <<
" to " << daqinterface_state_
2108 <<
". Check supervisor log file for more info!" << __E__;
2116 __SUP_COUT__ <<
"daqinterface_ptr_ is null" << __E__;
2121 runner_running_ =
false;
2122 TLOG(TLVL_TRACE) <<
"Runner thread complete";
2126 __SS__ <<
"An error occurred in "
2127 "start_runner_/daqinterfaceRunner_ thread "
2133 catch(
const std::runtime_error& e)
2135 ss <<
"Here is the error: " << e.what() << __E__;
2139 ss <<
"Unexpected error!" << __E__;
2141 __COUT_ERR__ << ss.str();
2144 std::lock_guard<std::mutex> lock(
2146 thread_error_message_ = ss.str();
2149 theStateMachine_.setErrorMessage(ss.str());
2151 sendAsyncExceptionToGateway(
2159 void ots::ARTDAQSupervisor::stop_runner_()
2161 runner_running_ =
false;
2162 if(runner_thread_ && runner_thread_->joinable())
2164 runner_thread_->join();
2165 runner_thread_.reset(
nullptr);
2170 void ots::ARTDAQSupervisor::start_runner_()
2174 std::make_unique<std::thread>(&ots::ARTDAQSupervisor::daqinterfaceRunner_,
this);
2178 std::string ARTDAQSupervisor::getServiceDataFilePath()
const
2180 return StringMacros::getPersistentSystemVariablesFilePath();
2184 void ARTDAQSupervisor::initArtdaqSystemVariables()
2186 loadArtdaqSystemVariables();
2188 auto& ns = StringMacros::systemVariables_[ARTDAQ_SYSVAR_NAMESPACE];
2189 __SUP_COUT__ <<
"Artdaq system variables initialized: "
2194 void ARTDAQSupervisor::loadArtdaqSystemVariables()
2197 __SUP_COUT__ <<
"Loaded artdaq system variables from " << getServiceDataFilePath()
2200 __SUP_COUT__ <<
"No persisted artdaq system variables file found at "
2201 << getServiceDataFilePath() << __E__;
2205 void ARTDAQSupervisor::saveArtdaqSystemVariables()
2207 std::string filePath = getServiceDataFilePath();
2208 std::ofstream file(filePath);
2211 __SUP_SS__ <<
"Failed to open file for writing artdaq system variables: "
2212 << filePath << __E__;
2216 for(
auto& [key, value] : StringMacros::systemVariables_[ARTDAQ_SYSVAR_NAMESPACE])
2217 file << key <<
"=" << value <<
"\n";
2219 __SUP_COUT__ <<
"Saved artdaq system variables to " << filePath << __E__;
2223 void ARTDAQSupervisor::forceSupervisorPropertyValues(
void)
2225 CorePropertySupervisorBase::addSupervisorProperty(
2226 CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
2227 "getSystemVariables | getJsonDocuments");
2232 cgicc::Cgicc& cgiIn,
2237 __SUP_COUT__ <<
"ARTDAQSupervisor request: " << requestType << __E__;
2239 if(requestType ==
"getSystemVariables")
2241 for(
auto& [key, value] : StringMacros::systemVariables_[ARTDAQ_SYSVAR_NAMESPACE])
2242 xmlOut.addTextElementToData(
"artdaq_" + key, value);
2244 else if(requestType ==
"setSystemVariable")
2251 xmlOut.addTextElementToData(
"Error",
"Variable key must not be empty.");
2255 if(!std::isalnum(c) && c !=
'_')
2257 xmlOut.addTextElementToData(
2259 "Variable key must contain only alphanumeric characters and "
2264 StringMacros::systemVariables_[ARTDAQ_SYSVAR_NAMESPACE][key] = value;
2265 saveArtdaqSystemVariables();
2267 __SUP_COUT__ <<
"Set artdaq system variable " << key <<
" = " << value << __E__;
2268 xmlOut.addTextElementToData(
"Success",
"Variable '" + key +
"' set.");
2270 else if(requestType ==
"getJsonDocuments")
2272 auto* ifc = ConfigurationInterface::getInstance();
2274 std::set<std::string> allTableNames = ifc->getAllTableNames();
2276 for(
const auto& tableName : allTableNames)
2278 if(tableName.find(TableBase::JSON_DOC_PREPEND) != 0)
2281 std::string docName = tableName.substr(TableBase::JSON_DOC_PREPEND.size());
2284 std::set<TableVersion> versions = ifc->getVersions(&tmpTable);
2286 std::string versionList;
2287 for(
const auto& v : versions)
2289 if(!versionList.empty())
2291 versionList += v.toString();
2294 xmlOut.addTextElementToData(
"jsonDoc_name", docName);
2295 xmlOut.addTextElementToData(
"jsonDoc_versions", versionList);
2300 __SUP_SS__ <<
"Unknown request type '" << requestType <<
"' for ARTDAQSupervisor."
2302 __SUP_COUT__ << ss.str();
2303 xmlOut.addTextElementToData(
"Error", ss.str());
2306 catch(
const std::runtime_error& e)
2308 __SUP_SS__ <<
"Error handling request '" << requestType <<
"': " << e.what() << __E__;
2309 __SUP_COUT_ERR__ << ss.str();
2310 xmlOut.addTextElementToData(
"Error", ss.str());
2314 __SUP_SS__ <<
"Unknown error handling request '" << requestType <<
"'." << __E__;
2315 __SUP_COUT_ERR__ << ss.str();
2316 xmlOut.addTextElementToData(
"Error", ss.str());
void request(const std::string &requestType, cgicc::Cgicc &cgiIn, HttpXmlDocument &xmlOut, const WebUsers::RequestUserInfo &userInfo) override
virtual void transitionHalting(toolbox::Event::Reference event) override
virtual void transitionInitializing(toolbox::Event::Reference event) override
static const std::string ARTDAQ_FCL_PATH
Tree-path rule is, if the last link in the path is a group link with a specified group ID,...
static std::string getBootFileContentFromInfo(const ARTDAQInfo &info, const std::string &setupScript, int debugLevel)
static std::string postData(cgicc::Cgicc &cgi, const std::string &needle)
ConfigurationTree getNode(const std::string &nodeString, bool doNotThrowOnBrokenUIDLinks=false) const
"root/parent/parent/"
ConfigurationTree getNode(const std::string &nodeName, bool doNotThrowOnBrokenUIDLinks=false) const
navigating between nodes
const std::string & getValueAsString(bool returnLinkTableValue=false) const
void getValue(T &value) const
ITRACEController * theTRACEController_
only define for an app that receives a command
bool isComplete()
get functions
int read()
if stepsToComplete==0, then define any progress as 50%, thread safe
void complete()
declare complete, thread safe
defines used also by OtsConfigurationWizardSupervisor
void INIT_MF(const char *name)
static std::string setToString(const std::set< T > &setToReturn, const std::string &delimeter=", ")
setToString ~
static bool loadPersistentSystemVariables(void)
loads persisted 'artdaq' namespace systemVariables_; returns false if no file found
static std::string mapToString(const std::map< std::string, T > &mapToReturn, const std::string &primaryDelimeter=", ", const std::string &secondaryDelimeter=": ")
std::string name
Also key in map.