Line data Source code
1 : /**
2 : * @file TimingHardwareManagerBase.cpp
3 : *
4 : * This is part of the DUNE DAQ Software Suite, copyright 2020.
5 : * Licensing/copyright details are in the COPYING file that you should have
6 : * received with this code.
7 : */
8 :
9 : #include "TimingHardwareManagerBase.hpp"
10 :
11 : #include "timinglibs/dal/TimingHardwareManagerBase.hpp"
12 : #include "timinglibs/dal/TimingFanoutDevice.hpp"
13 :
14 : #include "iomanager/IOManager.hpp"
15 : #include "logging/Logging.hpp"
16 : #include "timing/definitions.hpp"
17 :
18 : #include "timing/FanoutDesign.hpp"
19 : #include "timing/MuxDesignInterface.hpp"
20 :
21 : #include "timing/timingfirmware/Nljs.hpp"
22 : #include "timing/timingfirmware/Structs.hpp"
23 : #include "appfwk/ConfigurationManager.hpp"
24 :
25 : #include <memory>
26 : #include <string>
27 : #include <utility>
28 : #include <vector>
29 :
30 : #define TRACE_NAME "TimingHardwareManagerBase" // NOLINT
31 :
32 : namespace dunedaq {
33 :
34 0 : DUNE_DAQ_SERIALIZABLE(timinglibs::timingcmd::TimingHwCmd, "TimingHwCmd");
35 0 : DUNE_DAQ_SERIALIZABLE(nlohmann::json, "JSON");
36 :
37 : namespace timinglibs {
38 :
39 0 : TimingHardwareManagerBase::TimingHardwareManagerBase(const std::string& name)
40 : : dunedaq::appfwk::DAQModule(name)
41 0 : , m_hw_cmd_connection("timing_cmds")
42 0 : , m_hw_command_receiver(nullptr)
43 0 : , m_gather_interval(1e6)
44 0 : , m_gather_interval_debug(10e6)
45 0 : , m_monitored_device_name_master("")
46 0 : , m_monitored_device_name_endpoint("")
47 0 : , m_monitored_device_name_hsi("")
48 0 : , m_received_hw_commands_counter{ 0 }
49 0 : , m_accepted_hw_commands_counter{ 0 }
50 0 : , m_rejected_hw_commands_counter{ 0 }
51 0 : , m_failed_hw_commands_counter{ 0 }
52 0 : , m_endpoint_scan_threads_clean_up_thread(nullptr)
53 : {
54 : // register_command("start", &TimingHardwareManagerBase::do_start);
55 : // register_command("stop", &TimingHardwareManagerBase::do_stop);
56 0 : register_command("scrap", &TimingHardwareManagerBase::do_scrap);
57 0 : }
58 :
59 : void
60 0 : TimingHardwareManagerBase::init(std::shared_ptr<appfwk::ConfigurationManager> mcfg)
61 : {
62 0 : auto mod_config = mcfg->get_dal<timinglibs::dal::TimingHardwareManagerBase>(get_name());
63 0 : m_params = mod_config->get_configuration();
64 :
65 : // set up queues
66 0 : for (auto con : mod_config->get_inputs())
67 : {
68 0 : if (con->get_data_type() == datatype_to_string<timingcmd::TimingHwCmd>()) {
69 0 : m_hw_cmd_connection = con->UID();
70 0 : TLOG() << "m_hw_cmd_connection: " << m_hw_cmd_connection;
71 : }
72 : }
73 :
74 0 : try
75 : {
76 0 : m_hw_command_receiver = iomanager::IOManager::get()->get_receiver<timingcmd::TimingHwCmd>(m_hw_cmd_connection);
77 0 : } catch (const ers::Issue& excpt) {
78 0 : throw InvalidQueueFatalError(ERS_HERE, get_name(), "input", excpt);
79 0 : }
80 :
81 0 : m_endpoint_scan_threads_clean_up_thread = std::make_unique<dunedaq::utilities::ReusableThread>(0);
82 0 : }
83 :
84 : void
85 0 : TimingHardwareManagerBase::conf(const CommandData_t& /*data*/)
86 : {
87 0 : m_received_hw_commands_counter = 0;
88 0 : m_accepted_hw_commands_counter = 0;
89 0 : m_rejected_hw_commands_counter = 0;
90 0 : m_failed_hw_commands_counter = 0;
91 :
92 0 : m_gather_interval = m_params->get_gather_interval();
93 0 : m_gather_interval_debug = m_params->get_gather_interval_debug();
94 :
95 0 : m_monitored_device_name_master = m_params->get_monitored_device_name_master();
96 0 : for (auto fanout : m_params->get_monitored_device_names_fanout())
97 : {
98 0 : TLOG_DEBUG(3) << fanout->get_device() << ": device, slot: " << fanout->get_fanout_slot() << std::endl;
99 0 : m_monitored_device_names_fanout.emplace(fanout->get_fanout_slot(), fanout->get_device());
100 : }
101 :
102 0 : m_monitored_device_name_endpoint = m_params->get_monitored_device_name_endpoint();
103 0 : m_monitored_device_name_hsi = m_params->get_monitored_device_name_hsi();
104 :
105 0 : configure_uhal(m_params); // configure hw ipbus connection
106 :
107 0 : m_hw_command_receiver->add_callback(std::bind(&TimingHardwareManagerBase::process_hardware_command, this, std::placeholders::_1));
108 :
109 0 : m_run_endpoint_scan_cleanup_thread.store(true);
110 0 : m_endpoint_scan_threads_clean_up_thread->set_work(&TimingHardwareManagerBase::clean_endpoint_scan_threads, this);
111 0 : }
112 :
113 : void
114 0 : TimingHardwareManagerBase::do_scrap(const CommandData_t& /*data*/)
115 : {
116 0 : m_hw_command_receiver->remove_callback();
117 :
118 0 : auto time_of_scrap = std::chrono::high_resolution_clock::now();
119 0 : while(m_command_threads.size())
120 : {
121 0 : auto now = std::chrono::high_resolution_clock::now();
122 0 : auto ms_since_scrap = std::chrono::duration_cast<std::chrono::milliseconds>(now - time_of_scrap);
123 0 : TLOG_DEBUG(0) << "Have been waiting for " << ms_since_scrap.count() << " ms for " << m_command_threads.size() << " command threads to finish...";
124 0 : std::this_thread::sleep_for(std::chrono::microseconds(250000));
125 : }
126 0 : m_run_endpoint_scan_cleanup_thread.store(false);
127 :
128 0 : stop_hw_mon_gathering();
129 :
130 0 : scrap_uhal();
131 :
132 0 : m_command_threads.clear();
133 0 : m_info_gatherers.clear();
134 0 : m_timing_hw_cmd_map_.clear();
135 0 : m_hw_device_map.clear();
136 0 : m_connection_manager.reset();
137 0 : }
138 :
139 : const timing::TimingNode*
140 0 : TimingHardwareManagerBase::get_timing_device_plain(const std::string& device_name)
141 : {
142 :
143 0 : if (!device_name.compare("")) {
144 0 : std::stringstream message;
145 0 : message << "UHAL device name is an empty string";
146 0 : throw UHALDeviceNameIssue(ERS_HERE, message.str());
147 0 : }
148 :
149 0 : if (auto hw_device_entry = m_hw_device_map.find(device_name); hw_device_entry != m_hw_device_map.end()) {
150 0 : return dynamic_cast<const timing::TimingNode*>(&hw_device_entry->second->getNode(""));
151 : } else {
152 0 : TLOG_DEBUG(0) << get_name() << ": hw device interface for: " << device_name
153 0 : << " does not exist. I will try to create it.";
154 :
155 0 : try {
156 0 : std::lock_guard<std::mutex> hw_device_map_guard(m_hw_device_map_mutex);
157 0 : m_hw_device_map.emplace(device_name,
158 0 : std::make_unique<uhal::HwInterface>(m_connection_manager->getDevice(device_name)));
159 0 : } catch (const uhal::exception::ConnectionUIDDoesNotExist& exception) {
160 0 : std::stringstream message;
161 0 : message << "UHAL device name not " << device_name << " in connections file";
162 0 : throw UHALDeviceNameIssue(ERS_HERE, message.str(), exception);
163 0 : }
164 :
165 0 : TLOG_DEBUG(0) << get_name() << ": hw device interface for: " << device_name << " successfully created.";
166 :
167 0 : return dynamic_cast<const timing::TimingNode*>(&m_hw_device_map.find(device_name)->second->getNode(""));
168 : }
169 : }
170 :
171 : void
172 0 : TimingHardwareManagerBase::gather_monitor_data(InfoGatherer& gatherer)
173 : {
174 0 : auto device_name = gatherer.get_device_name();
175 :
176 0 : while (gatherer.run_gathering()) {
177 :
178 : // collect the data from the hardware
179 0 : try {
180 0 : auto design = get_timing_device<const timing::TopDesignInterface*>(device_name);
181 :
182 0 : gatherer.collect_info_from_device(*design);
183 0 : } catch (const std::exception& excpt) {
184 0 : ers::warning(FailedToCollectOpMonInfo(ERS_HERE, device_name, excpt));
185 0 : }
186 :
187 0 : auto prev_gather_time = std::chrono::steady_clock::now();
188 0 : auto next_gather_time = prev_gather_time + std::chrono::microseconds(gatherer.get_gather_interval());
189 :
190 : // check running_flag periodically
191 0 : auto slice_period = std::chrono::microseconds(10000);
192 0 : auto next_slice_gather_time = prev_gather_time + slice_period;
193 :
194 0 : bool break_flag = false;
195 0 : while (next_gather_time > next_slice_gather_time + slice_period) {
196 0 : if (!gatherer.run_gathering()) {
197 0 : TLOG_DEBUG(0) << "while waiting to gather data, negative run gatherer flag detected.";
198 0 : break_flag = true;
199 0 : break;
200 : }
201 0 : std::this_thread::sleep_until(next_slice_gather_time);
202 0 : next_slice_gather_time = next_slice_gather_time + slice_period;
203 : }
204 0 : if (break_flag == false) {
205 0 : std::this_thread::sleep_until(next_gather_time);
206 : }
207 : }
208 0 : }
209 :
210 : void
211 0 : TimingHardwareManagerBase::register_info_gatherer(uint gather_interval, const std::string& device_name, int op_mon_level)
212 : {
213 0 : std::string gatherer_name = device_name + "_level_" + std::to_string(op_mon_level);
214 0 : if (m_info_gatherers.find(gatherer_name) == m_info_gatherers.end()) {
215 0 : std::unique_ptr<InfoGatherer> gatherer = std::make_unique<InfoGatherer>(
216 0 : std::bind(&TimingHardwareManagerBase::gather_monitor_data, this, std::placeholders::_1),
217 : gather_interval,
218 : device_name,
219 0 : op_mon_level);
220 :
221 0 : TLOG_DEBUG(0) << "Registering info gatherer: " << gatherer_name;
222 0 : m_info_gatherers.emplace(std::make_pair(gatherer_name, std::move(gatherer)));
223 0 : } else {
224 0 : TLOG() << "Skipping registration of " << gatherer_name << ". Already exists.";
225 : }
226 0 : }
227 :
228 : void
229 0 : TimingHardwareManagerBase::start_hw_mon_gathering(const std::string& device_name)
230 : {
231 : // start all gatherers if no device name is given
232 0 : if (!device_name.compare("")) {
233 0 : TLOG_DEBUG(0) << get_name() << " Starting all info gatherers";
234 0 : for (auto it = m_info_gatherers.begin(); it != m_info_gatherers.end(); ++it)
235 0 : it->second.get()->start_gathering_thread();
236 : } else {
237 : // find gatherer for suppled device name and start it
238 0 : bool gatherer_found=false;
239 0 : for (auto it = m_info_gatherers.lower_bound(device_name); it != m_info_gatherers.end(); ++it) {
240 0 : TLOG_DEBUG(0) << get_name() << " Starting info gatherer: " << it->first;
241 0 : it->second.get()->start_gathering_thread();
242 0 : gatherer_found=true;
243 : }
244 0 : if (!gatherer_found) ers::warning(AttemptedToControlNonExantInfoGatherer(ERS_HERE, "start", device_name));
245 : }
246 0 : }
247 :
248 : void
249 0 : TimingHardwareManagerBase::stop_hw_mon_gathering(const std::string& device_name)
250 : {
251 : // stop all gatherers if no device name is given
252 0 : if (!device_name.compare("")) {
253 0 : TLOG_DEBUG(0) << get_name() << " Stopping all info gatherers";
254 0 : for (auto it = m_info_gatherers.begin(); it != m_info_gatherers.end(); ++it)
255 0 : it->second.get()->stop_gathering_thread();
256 : } else {
257 : // find gatherer for suppled device name and stop it
258 0 : bool gatherer_found=false;
259 0 : for (auto it = m_info_gatherers.lower_bound(device_name); it != m_info_gatherers.end(); ++it) {
260 0 : TLOG_DEBUG(0) << get_name() << " Stopping info gatherer: " << it->first;
261 0 : it->second.get()->stop_gathering_thread();
262 0 : gatherer_found=true;
263 : }
264 0 : if (!gatherer_found) ers::warning(AttemptedToControlNonExantInfoGatherer(ERS_HERE, "stop", device_name));
265 : }
266 0 : }
267 :
268 : std::vector<std::string>
269 0 : TimingHardwareManagerBase::check_hw_mon_gatherer_is_running(const std::string& device_name)
270 : {
271 0 : std::vector<std::string> running_gatherers;
272 0 : for (auto it = m_info_gatherers.lower_bound(device_name); it != m_info_gatherers.end(); ++it)
273 : {
274 0 : TLOG_DEBUG(0) << get_name() << " Checking run state of info gatherer: " << it->first << ", and the state is " << it->second.get()->run_gathering();
275 0 : if (it->second.get()->run_gathering())
276 : {
277 0 : running_gatherers.push_back(it->first);
278 : }
279 : }
280 0 : return running_gatherers;
281 0 : }
282 :
283 : // cmd stuff
284 :
285 : void
286 0 : TimingHardwareManagerBase::process_hardware_command(timingcmd::TimingHwCmd& timing_hw_cmd)
287 : {
288 0 : std::ostringstream starting_stream;
289 0 : starting_stream << ": Executing process_hardware_command() callback.";
290 0 : TLOG_DEBUG(0) << get_name() << starting_stream.str();
291 :
292 0 : ++m_received_hw_commands_counter;
293 :
294 0 : TLOG_DEBUG(0) << get_name() << ": Received hardware command #" << m_received_hw_commands_counter.load()
295 0 : << ", it is of type: " << timing_hw_cmd.id << ", targeting device: " << timing_hw_cmd.device << ", with payload: " << timing_hw_cmd.payload.dump();
296 :
297 0 : std::string hw_cmd_name = timing_hw_cmd.id;
298 0 : if (auto cmd = m_timing_hw_cmd_map_.find(hw_cmd_name); cmd != m_timing_hw_cmd_map_.end()) {
299 :
300 0 : ++m_accepted_hw_commands_counter;
301 :
302 0 : TLOG_DEBUG(0) << "Found hw cmd: " << hw_cmd_name;
303 0 : try {
304 0 : std::invoke(cmd->second, timing_hw_cmd);
305 0 : } catch (const std::exception& exception) {
306 0 : ers::error(FailedToExecuteHardwareCommand(ERS_HERE, hw_cmd_name, timing_hw_cmd.device, exception));
307 0 : ++m_failed_hw_commands_counter;
308 0 : }
309 : } else {
310 0 : ers::error(InvalidHardwareCommandID(ERS_HERE, hw_cmd_name));
311 0 : ++m_rejected_hw_commands_counter;
312 : }
313 :
314 0 : std::ostringstream exiting_stream;
315 0 : exiting_stream << ": Finished executing process_hardware_command() callback. Received " << m_received_hw_commands_counter.load()
316 0 : << " commands";
317 0 : TLOG_DEBUG(0) << get_name() << exiting_stream.str();
318 0 : }
319 :
320 : // common commands
321 : void
322 0 : TimingHardwareManagerBase::io_reset(const timingcmd::TimingHwCmd& hw_cmd)
323 : {
324 0 : timingcmd::IOResetCmdPayload cmd_payload;
325 0 : timingcmd::from_json(hw_cmd.payload, cmd_payload);
326 :
327 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device << " io reset";
328 :
329 : // io reset disrupts hw mon gathering, so stop if running
330 0 : auto running_hw_gatherers = check_hw_mon_gatherer_is_running(hw_cmd.device);
331 0 : for (auto& gatherer: running_hw_gatherers)
332 : {
333 0 : stop_hw_mon_gathering(gatherer);
334 : }
335 :
336 0 : auto design = get_timing_device<const timing::TopDesignInterface*>(hw_cmd.device);
337 :
338 0 : if (cmd_payload.soft) {
339 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device << " soft io reset";
340 0 : design->soft_reset_io();
341 0 : } else if (!cmd_payload.clock_config.empty()) {
342 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device
343 0 : << " io reset, with supplied clk file: " << cmd_payload.clock_config;
344 0 : design->reset_io(cmd_payload.clock_config);
345 : }
346 : else
347 : {
348 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device
349 0 : << " io reset, with supplied clk source: " << cmd_payload.clock_source;
350 0 : design->reset_io(static_cast<timing::ClockSource>(cmd_payload.clock_source));
351 : }
352 :
353 : // if hw mon gathering was running previously, start it again
354 0 : for (auto& gatherer: running_hw_gatherers)
355 : {
356 0 : start_hw_mon_gathering(gatherer);
357 : }
358 0 : }
359 :
360 : void
361 0 : TimingHardwareManagerBase::print_status(const timingcmd::TimingHwCmd& hw_cmd)
362 : {
363 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device << " print status";
364 :
365 0 : auto design = get_timing_device<const timing::TopDesignInterface*>(hw_cmd.device);
366 0 : TLOG() << std::endl << design->get_status();
367 0 : }
368 :
369 : // master commands
370 : void
371 0 : TimingHardwareManagerBase::set_timestamp(const timingcmd::TimingHwCmd& hw_cmd)
372 : {
373 0 : timingcmd::SyncTimestampPayload cmd_payload;
374 0 : timingcmd::from_json(hw_cmd.payload, cmd_payload);
375 :
376 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device
377 0 : << " set timestamp, with supplied ts source: " << cmd_payload.timestamp_source;
378 :
379 0 : auto design = get_timing_device<const timing::MasterDesignInterface*>(hw_cmd.device);
380 0 : design->sync_timestamp(static_cast<timing::TimestampSource>(cmd_payload.timestamp_source));
381 0 : }
382 :
383 : void
384 0 : TimingHardwareManagerBase::master_endpoint_scan(const timingcmd::TimingHwCmd& hw_cmd)
385 : {
386 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device << " master_endpoint_scan";
387 :
388 0 : std::stringstream command_thread_uid;
389 0 : auto t = std::time(nullptr);
390 0 : auto tm = *std::localtime(&t);
391 0 : command_thread_uid << "enpoint_scan_cmd_at_" << std::put_time(&tm, "%d-%m-%Y %H-%M-%S") << "_cmd_num_" << m_accepted_hw_commands_counter.load();
392 :
393 0 : if (m_command_threads.size() > 5)
394 : {
395 0 : ers::warning(TooManyEndpointScanThreadsQueued(ERS_HERE, m_command_threads.size()));
396 : }
397 : else
398 : {
399 0 : TLOG_DEBUG(1) << "Queuing: " << command_thread_uid.str();
400 :
401 0 : auto thread_key = command_thread_uid.str();
402 0 : std::unique_lock map_lock(m_command_threads_map_mutex);
403 :
404 0 : m_command_threads.emplace(thread_key, std::make_unique<std::thread>(std::bind(&TimingHardwareManagerBase::perform_endpoint_scan, this, hw_cmd)));
405 0 : }
406 0 : }
407 :
408 0 : void TimingHardwareManagerBase::perform_endpoint_scan(const timingcmd::TimingHwCmd& hw_cmd)
409 : {
410 0 : timingcmd::TimingMasterEndpointScanPayload cmd_payload;
411 0 : timingcmd::from_json(hw_cmd.payload, cmd_payload);
412 :
413 0 : for (auto& endpoint_location : cmd_payload.endpoints)
414 : {
415 0 : auto endpoint_address = endpoint_location.address;
416 0 : auto fanout_slot = endpoint_location.fanout_slot;
417 0 : auto sfp_slot = endpoint_location.sfp_slot;
418 :
419 0 : std::unique_lock<std::mutex> master_sfp_lock(master_sfp_mutex);
420 :
421 0 : TLOG_DEBUG(1) << get_name() << ": " << hw_cmd.device << " master_endpoint_scan starting: ept adr: " << endpoint_address << ", ept sfp: " << sfp_slot << ", fanout slot: " << fanout_slot;
422 :
423 0 : auto master_design = get_timing_device<const timing::MasterDesignInterface*>(hw_cmd.device);
424 0 : try
425 : {
426 : //master_design->get_master_node_plain()->switch_endpoint_sfp(endpoint_address, true);
427 :
428 0 : if (sfp_slot >= 0)
429 : {
430 0 : if (fanout_slot >= 0)
431 : {
432 : // configure fanout/FIB
433 0 : try
434 : {
435 0 : get_timing_device<const timing::MuxDesignInterface*>(m_monitored_device_names_fanout.at(fanout_slot))->switch_mux(sfp_slot);
436 : }
437 0 : catch(const UHALDeviceClassIssue& e)
438 : {
439 0 : ers::error(e);
440 0 : continue;
441 0 : }
442 :
443 : // slot 0 for board without multiple data tx paths, e.g. FMC, TLU
444 0 : if (fanout_slot != 0)
445 : {
446 : // configure GIB/MIB
447 0 : try
448 : {
449 0 : get_timing_device<const timing::MuxDesignInterface*>(hw_cmd.device)->switch_mux(fanout_slot-1);
450 : }
451 0 : catch(const UHALDeviceClassIssue& e)
452 : {
453 0 : ers::error(e);
454 0 : continue;
455 0 : }
456 : }
457 : }
458 : else
459 : {
460 0 : dynamic_cast<const timing::MuxDesignInterface*>(master_design)->switch_mux(sfp_slot);
461 : }
462 : }
463 :
464 0 : auto scan_result = master_design->get_master_node_plain()->scan_endpoint(endpoint_address, true);
465 0 : if (scan_result.alive)
466 : {
467 0 : auto current_rtt = scan_result.round_trip_time;
468 0 : ers::info(EndpointRTTMeasurement(ERS_HERE,fanout_slot,sfp_slot,endpoint_address,current_rtt));
469 0 : if (m_monitored_endpoints_round_trip_times.count(endpoint_address))
470 : {
471 0 : auto previous_rtt = m_monitored_endpoints_round_trip_times[endpoint_address];
472 0 : if (previous_rtt != current_rtt)
473 : {
474 : //TLOG() << "New round trip time for endpoint " << endpoint_address << " measured. Previous: "
475 : // << m_monitored_endpoints_round_trip_times[endpoint_address] << ", current: " << current_rtt;
476 0 : ers::warning(ChangedEndpointRTTMeasurement(ERS_HERE,fanout_slot,sfp_slot,endpoint_address,current_rtt,previous_rtt));
477 : }
478 : }
479 : else
480 : {
481 : //TLOG() << "First measured round trip time for endpoint " << endpoint_address << " is: " << current_rtt;
482 : }
483 0 : m_monitored_endpoints_round_trip_times[endpoint_address]=current_rtt;
484 : }
485 : else
486 : {
487 0 : ers::error(EndpointUnresponsive(ERS_HERE,fanout_slot,sfp_slot,endpoint_address));
488 : //TLOG() << endpoint_address << " endpoint was not alive...";
489 : }
490 : //master_design->get_master_node_plain()->switch_endpoint_sfp(endpoint_address, false);
491 : }
492 0 : catch(std::exception& e)
493 : {
494 0 : ers::error(EndpointScanFailure(ERS_HERE,e));
495 0 : master_design->get_master_node_plain()->switch_endpoint_sfp(endpoint_address, false);
496 0 : }
497 0 : }
498 0 : }
499 :
500 0 : void TimingHardwareManagerBase::clean_endpoint_scan_threads()
501 : {
502 0 : TLOG_DEBUG(0) << "Entering clean_endpoint_scan_threads()";
503 0 : bool break_flag = false;
504 0 : while (!break_flag)
505 : {
506 0 : for (auto& thread : m_command_threads)
507 : {
508 0 : if (thread.second->joinable())
509 : {
510 0 : std::unique_lock map_lock(m_command_threads_map_mutex);
511 0 : TLOG_DEBUG(2) << thread.first << " thread ready. Cleaning up.";
512 0 : thread.second->join();
513 0 : m_command_threads.erase(thread.first);
514 0 : }
515 : }
516 :
517 0 : auto prev_clean_time = std::chrono::steady_clock::now();
518 0 : auto next_clean_time = prev_clean_time + std::chrono::milliseconds(30);
519 :
520 : // check running_flag periodically
521 0 : auto flag_check_period = std::chrono::milliseconds(1);
522 0 : auto next_flag_check_time = prev_clean_time + flag_check_period;
523 :
524 0 : while (next_clean_time > next_flag_check_time + flag_check_period) {
525 0 : if (!m_run_endpoint_scan_cleanup_thread.load()) {
526 0 : TLOG_DEBUG(2) << "while waiting to clean up endpoint scan threads, negative run gatherer flag detected.";
527 0 : break_flag = true;
528 0 : break;
529 : }
530 0 : std::this_thread::sleep_until(next_flag_check_time);
531 0 : next_flag_check_time = next_flag_check_time + flag_check_period;
532 : }
533 0 : if (break_flag == false) {
534 0 : std::this_thread::sleep_until(next_clean_time);
535 : }
536 : }
537 0 : TLOG_DEBUG(0) << "Exiting clean_endpoint_scan_threads()";
538 0 : }
539 :
540 : // master commands
541 : void
542 0 : TimingHardwareManagerBase::set_endpoint_delay(const timingcmd::TimingHwCmd& hw_cmd)
543 : {
544 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device << " set endpoint delay";
545 :
546 0 : timingcmd::TimingMasterSetEndpointDelayCmdPayload cmd_payload;
547 0 : timingcmd::from_json(hw_cmd.payload, cmd_payload);
548 :
549 0 : auto design = get_timing_device<const timing::MasterDesignInterface*>(hw_cmd.device);
550 0 : design->apply_endpoint_delay(cmd_payload.address, cmd_payload.coarse_delay, cmd_payload.fine_delay, cmd_payload.phase_delay, cmd_payload.measure_rtt, cmd_payload.control_sfp, cmd_payload.sfp_mux);
551 0 : }
552 :
553 : void
554 0 : TimingHardwareManagerBase::send_fl_cmd(const timingcmd::TimingHwCmd& hw_cmd)
555 : {
556 0 : timingcmd::TimingMasterSendFLCmdCmdPayload cmd_payload;
557 0 : timingcmd::from_json(hw_cmd.payload, cmd_payload);
558 :
559 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device << " send fl cmd. Payload: " << hw_cmd.payload.dump()
560 0 : << ", parsed data: " << cmd_payload.fl_cmd_id
561 0 : << ", " << cmd_payload.channel
562 0 : << ", " << cmd_payload.number_of_commands_to_send;
563 :
564 0 : auto design = get_timing_device<const timing::MasterDesignInterface*>(hw_cmd.device);
565 0 : design->get_master_node_plain()->send_fl_cmd(cmd_payload.fl_cmd_id, cmd_payload.channel, cmd_payload.number_of_commands_to_send);
566 0 : }
567 :
568 : // endpoint commands
569 : void
570 0 : TimingHardwareManagerBase::endpoint_enable(const timingcmd::TimingHwCmd& hw_cmd)
571 : {
572 0 : timingcmd::TimingEndpointConfigureCmdPayload cmd_payload;
573 0 : timingcmd::from_json(hw_cmd.payload, cmd_payload);
574 :
575 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device << " ept enable, adr: " << cmd_payload.address
576 0 : << ", part: " << cmd_payload.partition;
577 :
578 0 : auto design = get_timing_device<const timing::EndpointDesignInterface*>(hw_cmd.device);
579 0 : design->get_endpoint_node_plain(cmd_payload.endpoint_id)->enable(cmd_payload.address, cmd_payload.partition);
580 0 : }
581 :
582 : void
583 0 : TimingHardwareManagerBase::endpoint_disable(const timingcmd::TimingHwCmd& hw_cmd)
584 : {
585 0 : timingcmd::TimingEndpointCmdPayload cmd_payload;
586 0 : timingcmd::from_json(hw_cmd.payload, cmd_payload);
587 :
588 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device << " ept disable";
589 :
590 0 : auto design = get_timing_device<const timing::EndpointDesignInterface*>(hw_cmd.device);
591 0 : design->get_endpoint_node_plain(cmd_payload.endpoint_id)->disable();
592 0 : }
593 :
594 : void
595 0 : TimingHardwareManagerBase::endpoint_reset(const timingcmd::TimingHwCmd& hw_cmd)
596 : {
597 0 : timingcmd::TimingEndpointConfigureCmdPayload cmd_payload;
598 0 : timingcmd::from_json(hw_cmd.payload, cmd_payload);
599 :
600 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device << " ept reset, adr: " << cmd_payload.address
601 0 : << ", part: " << cmd_payload.partition;
602 :
603 0 : auto design = get_timing_device<const timing::EndpointDesignInterface*>(hw_cmd.device);
604 0 : design->get_endpoint_node_plain(cmd_payload.endpoint_id)->reset(cmd_payload.address, cmd_payload.partition);
605 0 : }
606 :
607 : // hsi commands
608 : void
609 0 : TimingHardwareManagerBase::hsi_reset(const timingcmd::TimingHwCmd& hw_cmd)
610 : {
611 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device << " hsi reset";
612 :
613 0 : auto design = get_timing_device<const timing::HSIDesignInterface*>(hw_cmd.device);
614 0 : design->get_hsi_node().reset_hsi();
615 0 : }
616 :
617 : void
618 0 : TimingHardwareManagerBase::hsi_configure(const timingcmd::TimingHwCmd& hw_cmd)
619 : {
620 0 : timingcmd::HSIConfigureCmdPayload cmd_payload;
621 0 : timingcmd::from_json(hw_cmd.payload, cmd_payload);
622 :
623 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device << " hsi configure";
624 :
625 0 : auto design = get_timing_device<const timing::HSIDesignInterface*>(hw_cmd.device);
626 0 : design->configure_hsi(
627 : cmd_payload.data_source, cmd_payload.rising_edge_mask, cmd_payload.falling_edge_mask, cmd_payload.invert_edge_mask, cmd_payload.random_rate);
628 0 : }
629 :
630 : void
631 0 : TimingHardwareManagerBase::hsi_start(const timingcmd::TimingHwCmd& hw_cmd)
632 : {
633 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device << " hsi start";
634 :
635 0 : auto design = get_timing_device<const timing::HSIDesignInterface*>(hw_cmd.device);
636 0 : design->get_hsi_node().start_hsi();
637 0 : }
638 :
639 : void
640 0 : TimingHardwareManagerBase::hsi_stop(const timingcmd::TimingHwCmd& hw_cmd)
641 : {
642 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device << " hsi stop";
643 :
644 0 : auto design = get_timing_device<const timing::HSIDesignInterface*>(hw_cmd.device);
645 0 : design->get_hsi_node().stop_hsi();
646 0 : }
647 :
648 : void
649 0 : TimingHardwareManagerBase::hsi_print_status(const timingcmd::TimingHwCmd& hw_cmd)
650 : {
651 0 : TLOG_DEBUG(0) << get_name() << ": " << hw_cmd.device << " hsi print status";
652 :
653 0 : auto design = get_timing_device<const timing::HSIDesignInterface*>(hw_cmd.device);
654 0 : TLOG() << std::endl << design->get_hsi_node().get_status();
655 0 : }
656 :
657 : } // namespace timinglibs
658 : } // namespace dunedaq
|