GCC Code Coverage Report


./
File: modules/io/matrix/matrices_reader.cpp
Date: 2025-01-21 16:21:04
Lines:
0/200
0.0%
Functions:
0/16
0.0%
Branches:
0/342
0.0%

Line Branch Exec Source
1 /************************************************************************
2 *
3 * Copyright (C) 2017-2023 IRCAD France
4 * Copyright (C) 2017-2020 IHU Strasbourg
5 *
6 * This file is part of Sight.
7 *
8 * Sight is free software: you can redistribute it and/or modify it under
9 * the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * Sight is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with Sight. If not, see <https://www.gnu.org/licenses/>.
20 *
21 ***********************************************************************/
22
23 #include "matrices_reader.hpp"
24
25 #include <core/com/signal.hpp>
26 #include <core/com/signal.hxx>
27 #include <core/com/signals.hpp>
28 #include <core/com/slot.hpp>
29 #include <core/com/slot.hxx>
30 #include <core/com/slots.hpp>
31 #include <core/com/slots.hxx>
32 #include <core/location/single_file.hpp>
33 #include <core/location/single_folder.hpp>
34
35 #include <service/macros.hpp>
36
37 #include <ui/__/dialog/location.hpp>
38 #include <ui/__/dialog/message.hpp>
39
40 #include <boost/tokenizer.hpp>
41
42 #include <cmath>
43 #include <filesystem>
44 #include <fstream>
45
46 namespace sight::module::io::matrix
47 {
48
49 static const core::com::slots::key_t START_READING = "start_reading";
50 static const core::com::slots::key_t STOP_READING = "stop_reading";
51 static const core::com::slots::key_t PAUSE = "pause";
52 static const core::com::slots::key_t TOGGLE_LOOP_MODE = "toggle_loop_mode";
53
54 static const core::com::slots::key_t READ_NEXT = "readNext";
55 static const core::com::slots::key_t READ_PREVIOUS = "readPrevious";
56 static const core::com::slots::key_t SET_STEP = "set_step";
57
58 //------------------------------------------------------------------------------
59
60 matrices_reader::matrices_reader() noexcept
61 {
62 new_slot(START_READING, &matrices_reader::start_reading, this);
63 new_slot(STOP_READING, &matrices_reader::stop_reading, this);
64 new_slot(PAUSE, &matrices_reader::pause, this);
65 new_slot(TOGGLE_LOOP_MODE, &matrices_reader::toggle_loop_mode, this);
66
67 new_slot(READ_NEXT, &matrices_reader::read_next, this);
68 new_slot(READ_PREVIOUS, &matrices_reader::read_previous, this);
69 new_slot(SET_STEP, &matrices_reader::set_step, this);
70 }
71
72 //------------------------------------------------------------------------------
73
74 matrices_reader::~matrices_reader() noexcept
75 {
76 if(nullptr != m_filestream)
77 {
78 m_filestream->close();
79 delete m_filestream;
80 }
81 }
82
83 //------------------------------------------------------------------------------
84
85 sight::io::service::path_type_t matrices_reader::get_path_type() const
86 {
87 return sight::io::service::file;
88 }
89
90 //------------------------------------------------------------------------------
91
92 void matrices_reader::configuring()
93 {
94 sight::io::service::reader::configuring();
95
96 service::config_t config = this->get_config();
97
98 m_fps = config.get<unsigned int>("fps", 30);
99 SIGHT_ASSERT("Fps setting is set to " << m_fps << " but should be > 0.", m_fps > 0);
100
101 m_use_timelapse = config.get<bool>("useTimelapse", m_use_timelapse);
102
103 m_create_new_ts = config.get<bool>("createTimestamp", m_create_new_ts);
104
105 m_one_shot = config.get<bool>("oneShot", m_one_shot);
106
107 m_step = config.get<std::uint64_t>("step", m_step);
108 SIGHT_ASSERT("Step value is set to " << m_step << " but should be > 0.", m_step > 0);
109 m_step_changed = m_step;
110
111 m_loop_matrix = config.get<bool>("loop", m_loop_matrix);
112 }
113
114 //------------------------------------------------------------------------------
115
116 void matrices_reader::starting()
117 {
118 m_worker = core::thread::worker::make();
119 }
120
121 //------------------------------------------------------------------------------
122
123 void matrices_reader::open_location_dialog()
124 {
125 static auto default_directory = std::make_shared<core::location::single_folder>();
126 sight::ui::dialog::location dialog_file;
127 dialog_file.set_title(m_window_title.empty() ? "Choose a csv file to read" : m_window_title);
128 dialog_file.set_default_location(default_directory);
129 dialog_file.set_option(ui::dialog::location::read);
130 dialog_file.set_type(ui::dialog::location::single_file);
131 dialog_file.add_filter(".csv file", "*.csv");
132
133 auto result = std::dynamic_pointer_cast<core::location::single_file>(dialog_file.show());
134 if(result)
135 {
136 default_directory->set_folder(result->get_file().parent_path());
137 dialog_file.save_default_location(default_directory);
138 this->set_file(result->get_file());
139
140 if(nullptr != m_filestream)
141 {
142 m_filestream->close();
143 }
144
145 m_filestream = new std::ifstream(this->get_file().string());
146 }
147 else
148 {
149 this->clear_locations();
150 }
151 }
152
153 //------------------------------------------------------------------------------
154
155 void matrices_reader::stopping()
156 {
157 this->stop_reading();
158 m_worker->stop();
159 }
160
161 //------------------------------------------------------------------------------
162
163 void matrices_reader::updating()
164 {
165 }
166
167 //------------------------------------------------------------------------------
168
169 void matrices_reader::read_previous()
170 {
171 if(m_one_shot)
172 {
173 if(m_ts_matrices_count - m_step >= m_step_changed)
174 {
175 // Compute difference between a possible step change in set_step() slot and the current step value
176 const std::int64_t shift = static_cast<std::int64_t>(m_step_changed) - static_cast<std::int64_t>(m_step);
177 const std::int64_t shifted = static_cast<std::int64_t>(m_ts_matrices_count) - shift;
178
179 // m_tsMatricesCount is pointing to previous matrix,so -1 = present matrix
180 m_ts_matrices_count = static_cast<std::size_t>(shifted) - (2 * m_step);
181 m_step = m_step_changed;
182
183 m_timer->stop();
184 m_timer->start();
185 }
186 else
187 {
188 sight::ui::dialog::message::show(
189 "MatricesReader",
190 "No previous Matrices."
191 );
192 }
193 }
194 }
195
196 //------------------------------------------------------------------------------
197
198 void matrices_reader::read_next()
199 {
200 if(m_one_shot)
201 {
202 // Compute difference between a possible step change in set_step() slot and the current step value
203 const std::int64_t shift = static_cast<std::int64_t>(m_step_changed) - static_cast<std::int64_t>(m_step);
204 const std::int64_t shifted = static_cast<std::int64_t>(m_ts_matrices_count) + shift;
205
206 if(shifted < static_cast<std::int64_t>(m_ts_matrices.size()))
207 {
208 // Update matrix position index
209 m_ts_matrices_count = static_cast<std::size_t>(shifted);
210 m_step = m_step_changed;
211
212 m_timer->stop();
213 m_timer->start();
214 }
215 else
216 {
217 sight::ui::dialog::message::show(
218 "MatricesReader",
219 "No more matrices to read."
220 );
221 }
222 }
223 }
224
225 //------------------------------------------------------------------------------
226
227 void matrices_reader::set_step(int _step, std::string _key)
228 {
229 if(_key == "step")
230 {
231 SIGHT_ASSERT("Needed step value (" << _step << ") should be > 0.", _step > 0);
232
233 // Save the changed step value
234 m_step_changed = static_cast<std::uint64_t>(_step);
235 }
236 else
237 {
238 SIGHT_WARN("Only 'step' key is supported (current key value is : '" << _key << "').");
239 }
240 }
241
242 //------------------------------------------------------------------------------
243
244 void matrices_reader::start_reading()
245 {
246 if(m_timer)
247 {
248 this->stop_reading();
249 }
250
251 if(!this->has_location_defined())
252 {
253 this->open_location_dialog();
254 }
255
256 if(this->has_location_defined())
257 {
258 if(nullptr == m_filestream)
259 {
260 std::string file = this->get_file().string();
261
262 m_filestream = new std::ifstream(file);
263 }
264
265 if(m_filestream->is_open())
266 {
267 std::string line;
268 while(std::getline(*m_filestream, line))
269 {
270 // parse the cvs file with tokenizer
271 const boost::char_separator<char> sep(", ;");
272 const boost::tokenizer<boost::char_separator<char> > tok {line, sep};
273
274 // nb of 4x4 matrices = nb of elements - 1 (timestamp) / 16.
275 const auto nb_of_elements = std::distance(tok.begin(), tok.end());
276 if(nb_of_elements < 17)
277 {
278 SIGHT_WARN("Too few elements(" << nb_of_elements << ") to convert this csv line into matrices");
279 continue;
280 }
281
282 const auto nb_of_matrices = static_cast<unsigned int>((nb_of_elements - 1) / 16);
283
284 const auto matrix_tl = m_matrix_tl.lock();
285 matrix_tl->init_pool_size(nb_of_matrices);
286
287 time_stamped_matrices current_ts_mat;
288
289 boost::tokenizer<boost::char_separator<char> >::iterator iter = tok.begin();
290 current_ts_mat.timestamp = std::stod(iter.current_token());
291
292 ++iter;
293
294 for(unsigned int m = 0 ; m < nb_of_matrices ; ++m)
295 {
296 std::array<float, 16> mat {};
297 for(float& i : mat)
298 {
299 i = std::stof(iter.current_token());
300
301 if(iter != tok.end())
302 {
303 ++iter;
304 }
305 }
306
307 current_ts_mat.matrices.push_back(mat);
308 }
309
310 m_ts_matrices.push_back(current_ts_mat);
311 }
312
313 m_filestream->close();
314 }
315 else
316 {
317 SIGHT_ERROR("The csv file '" + this->get_file().string() + "' can not be openned.");
318 }
319
320 if(m_one_shot)
321 {
322 m_timer = m_worker->create_timer();
323 m_timer->set_one_shot(true);
324 m_timer->set_function([this](auto&& ...){read_matrices();});
325 m_timer->set_duration(std::chrono::milliseconds(0));
326 m_timer->start();
327 }
328 else
329 {
330 m_timer = m_worker->create_timer();
331
332 core::thread::timer::time_duration_t duration;
333 if(m_use_timelapse)
334 {
335 m_timer->set_one_shot(true);
336 if(m_ts_matrices.size() >= 2)
337 {
338 duration =
339 std::chrono::milliseconds(
340 static_cast<std::uint64_t>(m_ts_matrices[1].timestamp
341 - m_ts_matrices[0].timestamp)
342 );
343 }
344 else
345 {
346 // Only one matrix to read, might as well just read it ASAP.
347 duration = std::chrono::milliseconds(0);
348 }
349 }
350 else
351 {
352 duration = std::chrono::milliseconds(1000 / m_fps);
353 }
354
355 m_timer->set_function([this](auto&& ...){read_matrices();});
356 m_timer->set_duration(duration);
357 m_timer->start();
358 }
359
360 m_is_playing = true;
361 }
362 }
363
364 //------------------------------------------------------------------------------
365
366 void matrices_reader::stop_reading()
367 {
368 m_is_playing = false;
369
370 if(m_timer)
371 {
372 if(m_timer->is_running())
373 {
374 m_timer->stop();
375 }
376
377 m_timer.reset();
378 }
379
380 if(nullptr != m_filestream)
381 {
382 m_filestream->close();
383 delete m_filestream;
384 m_filestream = nullptr;
385 }
386
387 if(!m_ts_matrices.empty())
388 {
389 m_ts_matrices.clear();
390 }
391
392 m_ts_matrices_count = 0;
393
394 //clear the timeline
395 const auto matrix_tl = m_matrix_tl.lock();
396 matrix_tl->clear_timeline();
397
398 auto sig = matrix_tl->signal<data::timeline::signals::cleared_t>(data::timeline::signals::CLEARED);
399 sig->async_emit();
400 }
401
402 //------------------------------------------------------------------------------
403
404 void matrices_reader::pause()
405 {
406 m_is_paused = !m_is_paused;
407 if(m_timer)
408 {
409 m_is_paused ? m_timer->stop() : m_timer->start();
410 }
411 }
412
413 //------------------------------------------------------------------------------
414
415 void matrices_reader::read_matrices()
416 {
417 if(!m_is_paused && m_ts_matrices_count < m_ts_matrices.size())
418 {
419 const auto t_start = core::clock::get_time_in_milli_sec();
420 const auto matrix_tl = m_matrix_tl.lock();
421
422 time_stamped_matrices current_matrices = m_ts_matrices[m_ts_matrices_count];
423
424 core::clock::type timestamp = NAN;
425
426 if(m_create_new_ts)
427 {
428 timestamp = core::clock::get_time_in_milli_sec();
429 }
430 else
431 {
432 timestamp = current_matrices.timestamp;
433 }
434
435 // Push matrix in timeline
436 SPTR(data::matrix_tl::buffer_t) matrix_buf;
437 matrix_buf = matrix_tl->create_buffer(timestamp);
438 matrix_tl->push_object(matrix_buf);
439
440 SIGHT_DEBUG("Reading matrix index " << m_ts_matrices_count << " with timestamp " << timestamp);
441 for(unsigned int i = 0 ; i < current_matrices.matrices.size() ; ++i)
442 {
443 matrix_buf->set_element(current_matrices.matrices[i], i);
444 }
445
446 if(m_use_timelapse && (m_ts_matrices_count + m_step) < m_ts_matrices.size())
447 {
448 const auto elapsed_time = core::clock::get_time_in_milli_sec() - t_start;
449 const std::size_t current_matrix = m_ts_matrices_count;
450 const double current_time = m_ts_matrices[current_matrix].timestamp + elapsed_time;
451 double next_duration = m_ts_matrices[m_ts_matrices_count + m_step].timestamp - current_time;
452
453 // If the next matrix delay is already passed, drop the matrices and check the next one.
454 while(next_duration < elapsed_time && (m_ts_matrices_count + m_step) < m_ts_matrices.size())
455 {
456 next_duration = m_ts_matrices[m_ts_matrices_count + m_step].timestamp - current_time;
457 m_ts_matrices_count += m_step;
458 SIGHT_DEBUG("Skipping a matrix");
459 }
460
461 // If it is the last matrix array: stop the timer or loop
462 if((m_ts_matrices_count + m_step) == m_ts_matrices.size())
463 {
464 m_timer->stop();
465 if(m_loop_matrix)
466 {
467 matrix_tl->clear_timeline();
468 m_ts_matrices_count = 0;
469 core::thread::timer::time_duration_t duration = std::chrono::milliseconds(1000 / m_fps);
470 m_timer->set_duration(duration);
471 m_timer->start();
472 }
473 }
474 else
475 {
476 next_duration = m_ts_matrices[m_ts_matrices_count + m_step].timestamp
477 - current_time;
478 core::thread::timer::time_duration_t duration =
479 std::chrono::milliseconds(static_cast<std::int64_t>(next_duration));
480 m_timer->stop();
481 m_timer->set_duration(duration);
482 m_timer->start();
483 }
484 }
485
486 //Notify
487 data::timeline::signals::pushed_t::sptr sig;
488 sig = matrix_tl->signal<data::timeline::signals::pushed_t>(
489 data::timeline::signals::PUSHED
490 );
491 sig->async_emit(timestamp);
492
493 m_ts_matrices_count += m_step;
494 }
495 else if(!m_is_paused && m_loop_matrix)
496 {
497 const auto matrix_tl = m_matrix_tl.lock();
498 matrix_tl->clear_timeline();
499 m_ts_matrices_count = 0;
500 }
501 }
502
503 // -----------------------------------------------------------------------------
504
505 void matrices_reader::toggle_loop_mode()
506 {
507 m_loop_matrix = !m_loop_matrix;
508 }
509
510 //------------------------------------------------------------------------------
511
512 } // namespace sight::module::io::matrix
513