• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

paulmthompson / WhiskerToolbox / 17381870552

01 Sep 2025 02:59PM UTC coverage: 71.982% (+0.04%) from 71.945%
17381870552

push

github

paulmthompson
can specify key name for media that is loaded

0 of 4 new or added lines in 1 file covered. (0.0%)

4 existing lines in 1 file now uncovered.

31764 of 44128 relevant lines covered (71.98%)

1396.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

52.02
/src/DataManager/DataManager.cpp
1
#include "DataManager.hpp"
2

3
#include "AnalogTimeSeries/Analog_Time_Series.hpp"
4
#include "ConcreteDataFactory.hpp"
5
#include "DigitalTimeSeries/Digital_Event_Series.hpp"
6
#include "DigitalTimeSeries/Digital_Interval_Series.hpp"
7
#include "IO/LoaderRegistration.hpp"
8
#include "IO/LoaderRegistry.hpp"
9
#include "Lines/Line_Data.hpp"
10
#include "Masks/Mask_Data.hpp"
11
#include "Media/MediaDataFactory.hpp"
12
#include "Points/Point_Data.hpp"
13
#include "Tensors/Tensor_Data.hpp"
14

15
// Media includes - now from separate MediaData library
16
//#include "Media/Image_Data.hpp"
17
#include "Media/Media_Data.hpp"
18
//#include "Media/Video_Data.hpp"
19

20
#include "AnalogTimeSeries/IO/JSON/Analog_Time_Series_JSON.hpp"
21
#include "DigitalTimeSeries/IO/CSV/Digital_Interval_Series_CSV.hpp"
22
#include "DigitalTimeSeries/IO/JSON/Digital_Event_Series_JSON.hpp"
23
#include "DigitalTimeSeries/IO/JSON/Digital_Interval_Series_JSON.hpp"
24
#include "Lines/IO/JSON/Line_Data_JSON.hpp"
25
#include "Masks/IO/JSON/Mask_Data_JSON.hpp"
26
#ifdef ENABLE_OPENCV
27
#include "Media/IO/JSON/Image_Data_JSON.hpp"
28
#endif
29
#include "Points/IO/JSON/Point_Data_JSON.hpp"
30
#include "Tensors/IO/numpy/Tensor_Data_numpy.hpp"
31
#include "utils/TableView/TableRegistry.hpp"
32

33
#include "loaders/binary_loaders.hpp"
34

35
#include "TimeFrame/TimeFrame.hpp"
36

37
#include "nlohmann/json.hpp"
38

39
#include "transforms/TransformPipeline.hpp"
40
#include "transforms/TransformRegistry.hpp"
41
#include "utils/string_manip.hpp"
42

43
#include "Entity/EntityRegistry.hpp"
44

45
#include <filesystem>
46
#include <fstream>
47
#include <iostream>
48
#include <optional>
49
#include <regex>
50

51
using namespace nlohmann;
52

53
/**
54
 * @brief Try loading data using new registry system first, fallback to legacy if needed
55
 */
56
bool tryRegistryThenLegacyLoad(
4✔
57
        DataManager * dm,
58
        std::string const & file_path,
59
        DM_DataType data_type,
60
        nlohmann::json const & item,
61
        std::string const & name,
62
        std::vector<DataInfo> & data_info_list,
63
        DataFactory * factory) {
64
    // Extract format if available
65
    if (item.contains("format")) {
4✔
66
        std::string const format = item["format"];
4✔
67

68
        // Try registry system first
69
        LoaderRegistry & registry = LoaderRegistry::getInstance();
4✔
70
        if (registry.isFormatSupported(format, toIODataType(data_type))) {
4✔
71
            std::cout << "Using registry loader for " << name << " (format: " << format << ")" << std::endl;
4✔
72

73
            LoadResult result = registry.tryLoad(format, toIODataType(data_type), file_path, item, factory);
4✔
74
            if (result.success) {
4✔
75
                // Handle data setting and post-loading setup based on data type
76
                switch (data_type) {
3✔
77
                    case DM_DataType::Line: {
2✔
78
                        // Set the LineData in DataManager
79
                        if (std::holds_alternative<std::shared_ptr<LineData>>(result.data)) {
2✔
80
                            auto line_data = std::get<std::shared_ptr<LineData>>(result.data);
2✔
81

82
                            // Set up identity context
83
                            if (line_data) {
2✔
84
                                line_data->setIdentityContext(name, dm->getEntityRegistry());
2✔
85
                                line_data->rebuildAllEntityIds();
2✔
86
                            }
87

88
                            dm->setData<LineData>(name, line_data, TimeKey("time"));
2✔
89

90
                            std::string const color = item.value("color", "0000FF");
2✔
91
                            data_info_list.push_back({name, "LineData", color});
6✔
92
                        }
2✔
93
                        break;
2✔
94
                    }
95
                    case DM_DataType::Mask: {
1✔
96
                        // Set the MaskData in DataManager
97
                        if (std::holds_alternative<std::shared_ptr<MaskData>>(result.data)) {
1✔
98
                            auto mask_data = std::get<std::shared_ptr<MaskData>>(result.data);
1✔
99

100
                            dm->setData<MaskData>(name, mask_data, TimeKey("time"));
1✔
101

102
                            std::string const color = item.value("color", "0000FF");
1✔
103
                            data_info_list.push_back({name, "MaskData", color});
3✔
104

105
                        }
1✔
106
                        break;
1✔
107
                    }
108
                    // Add other data types as they get plugin support...
109
                    default:
×
110
                        std::cerr << "Registry loaded unsupported data type: " << static_cast<int>(data_type) << std::endl;
×
111
                        return false;
×
112
                }
113

114
                return true;
3✔
115
            } else {
116
                std::cout << "Registry loading failed for " << name << ": " << result.error_message
117
                          << ", falling back to legacy loader" << std::endl;
1✔
118
            }
119
        }
4✔
120
    }
4✔
121

122
    return false;// Indicates we should use legacy loading
1✔
123
}
9✔
124

125
DataManager::DataManager() {
227✔
126
    _times[TimeKey("time")] = std::make_shared<TimeFrame>();
227✔
127
    _data["media"] = std::make_shared<EmptyMediaData>();
681✔
128

129
    setTimeKey("media", TimeKey("time"));
681✔
130
    _output_path = std::filesystem::current_path();
227✔
131

132
    // Initialize TableRegistry
133
    _table_registry = std::make_unique<TableRegistry>(*this);
227✔
134

135
    // Initialize EntityRegistry
136
    _entity_registry = std::make_unique<EntityRegistry>();
227✔
137

138
    // Register all available loaders
139
    static bool loaders_registered = false;
140
    if (!loaders_registered) {
227✔
141
        registerAllLoaders();
126✔
142
        loaders_registered = true;
126✔
143
    }
144
}
227✔
145

146
DataManager::~DataManager() = default;
227✔
147

148
void DataManager::reset() {
×
149
    std::cout << "DataManager: Resetting to initial state..." << std::endl;
×
150

151
    // Clear all data objects except media (which we'll reset)
152
    _data.clear();
×
153

154
    // Reset media to a fresh empty MediaData object
155
    _data["media"] = std::make_shared<EmptyMediaData>();
×
156

157
    // Clear all TimeFrame objects except the default "time" frame
158
    _times.clear();
×
159

160
    // Recreate the default "time" TimeFrame
161
    _times[TimeKey("time")] = std::make_shared<TimeFrame>();
×
162

163
    // Clear all data-to-timeframe mappings and recreate the default media mapping
164
    _time_frames.clear();
×
165
    setTimeKey("media", TimeKey("time"));
×
166

167

168
    // Reset current time
169
    _current_time = 0;
×
170

171
    // Notify observers that the state has changed
172
    _notifyObservers();
×
173

174
    std::cout << "DataManager: Reset complete. Default 'time' frame and 'media' data restored." << std::endl;
×
175

176
    // Reset entity registry for a new session context
177
    if (_entity_registry) {
×
178
        _entity_registry->clear();
×
179
    }
180
}
×
181

182
bool DataManager::setTime(TimeKey const & key, std::shared_ptr<TimeFrame> timeframe, bool overwrite) {
237✔
183

184
    if (!timeframe) {
237✔
185
        std::cerr << "Error: Cannot register a nullptr TimeFrame for key: " << key << std::endl;
1✔
186
        return false;
1✔
187
    }
188

189
    if (_times.find(key) != _times.end()) {
236✔
190
        if (!overwrite) {
6✔
191
            std::cerr << "Error: Time key already exists in DataManager: " << key << std::endl;
2✔
192
            return false;
2✔
193
        }
194
    }
195

196
    _times[key] = std::move(timeframe);
234✔
197

198
    // Move ptr to new time frame to all data that hold
199
    for (auto const & [data_key, data]: _data) {
469✔
200
        if (_time_frames.find(data_key) != _time_frames.end()) {
235✔
201
            auto time_key = _time_frames[data_key];
235✔
202
            if (time_key == key) {
235✔
203
                std::visit([this, timeframe](auto & x) {
8✔
204
                    x->setTimeFrame(timeframe);
4✔
205
                },
4✔
206
                           data);
207
            }
208
        }
235✔
209
    }
210

211
    return true;
234✔
212
}
213

214
std::shared_ptr<TimeFrame> DataManager::getTime() {
8✔
215
    return _times[TimeKey("time")];
8✔
216
};
217

218
std::shared_ptr<TimeFrame> DataManager::getTime(TimeKey const & key) {
179✔
219
    if (_times.find(key) != _times.end()) {
179✔
220
        return _times[key];
177✔
221
    }
222
    return nullptr;
2✔
223
};
224

225
TimeIndexAndFrame DataManager::getCurrentIndexAndFrame(TimeKey const & key) {
×
226
    if (_times.find(key) != _times.end()) {
×
227
        return {TimeFrameIndex(_current_time), _times[key]};
×
228
    }
229
    return {TimeFrameIndex(_current_time), nullptr};
×
230
}
×
231

232
bool DataManager::removeTime(TimeKey const & key) {
×
233
    if (_times.find(key) == _times.end()) {
×
234
        std::cerr << "Error: could not find time key in DataManager: " << key << std::endl;
×
235
        return false;
×
236
    }
237

238
    auto it = _times.find(key);
×
239
    _times.erase(it);
×
240
    return true;
×
241
}
242

243
bool DataManager::setTimeKey(std::string const & data_key, TimeKey const & time_key) {
788✔
244
    if (_data.find(data_key) == _data.end()) {
788✔
245
        std::cerr << "Error: Data key not found in DataManager: " << data_key << std::endl;
3✔
246
        return false;
3✔
247
    }
248

249
    if (_times.find(time_key) == _times.end()) {
785✔
250
        std::cerr << "Error: Time key not found in DataManager: " << time_key << std::endl;
1✔
251
        return false;
1✔
252
    }
253

254
    _time_frames[data_key] = time_key;
784✔
255

256
    if (_data.find(data_key) != _data.end()) {
784✔
257
        auto data = _data[data_key];
784✔
258
        std::visit([this, time_key](auto & x) {
1,568✔
259
            x->setTimeFrame(this->_times[time_key]);
784✔
260
        },
784✔
261
                   data);
262
    }
784✔
263
    return true;
784✔
264
}
265

266
TimeKey DataManager::getTimeKey(std::string const & data_key) {
205✔
267
    // check if data_key exists
268
    if (_data.find(data_key) == _data.end()) {
205✔
269
        std::cerr << "Error: Data key not found in DataManager: " << data_key << std::endl;
1✔
270
        return TimeKey("");
1✔
271
    }
272

273
    // check if data key has time frame
274
    if (_time_frames.find(data_key) == _time_frames.end()) {
204✔
275
        std::cerr << "Error: Data key "
276
                  << data_key
277
                  << " exists, but not assigned to a TimeFrame" << std::endl;
×
278
        return TimeKey("");
×
279
    }
280

281
    return _time_frames[data_key];
204✔
282
}
283

284
std::vector<TimeKey> DataManager::getTimeFrameKeys() {
8✔
285
    std::vector<TimeKey> keys;
8✔
286
    keys.reserve(_times.size());
8✔
287
    for (auto const & [key, value]: _times) {
24✔
288

289
        keys.push_back(key);
16✔
290
    }
291
    return keys;
8✔
292
}
×
293

294
int DataManager::addCallbackToData(std::string const & key, ObserverCallback callback) {
7✔
295

296
    int id = -1;
7✔
297

298
    if (_data.find(key) != _data.end()) {
7✔
299
        auto data = _data[key];
6✔
300

301
        id = std::visit([callback](auto & x) {
12✔
302
            return x.get()->addObserver(callback);
6✔
303
        },
304
                        data);
305
    }
6✔
306

307
    return id;
7✔
308
}
309

310
bool DataManager::removeCallbackFromData(std::string const & key, int callback_id) {
4✔
311
    if (_data.find(key) != _data.end()) {
4✔
312
        auto data = _data[key];
3✔
313

314
        std::visit([callback_id](auto & x) {
6✔
315
            x.get()->removeObserver(callback_id);
3✔
316
        },
3✔
317
                   data);
318

319
        return true;
3✔
320
    }
3✔
321

322
    return false;
1✔
323
}
324

325
void DataManager::addObserver(ObserverCallback callback) {
6✔
326
    _observers.push_back(std::move(callback));
6✔
327
}
6✔
328

329
void DataManager::_notifyObservers() {
555✔
330
    for (auto & observer: _observers) {
563✔
331
        observer();
8✔
332
    }
333
}
555✔
334

335
// ===== Table Registry accessors =====
336
TableRegistry * DataManager::getTableRegistry() {
47✔
337
    return _table_registry.get();
47✔
338
}
339

340
TableRegistry const * DataManager::getTableRegistry() const {
×
341
    return _table_registry.get();
×
342
}
343

344
// ===== Table observer channel =====
345
int DataManager::addTableObserver(TableObserver callback) {
×
346
    if (!callback) return -1;
×
347
    int id = _next_table_observer_id++;
×
348
    _table_observers[id] = std::move(callback);
×
349
    return id;
×
350
}
351

352
bool DataManager::removeTableObserver(int callback_id) {
×
353
    return _table_observers.erase(callback_id) > 0;
×
354
}
355

356
void DataManager::notifyTableObservers(TableEvent const & ev) {
43✔
357
    for (auto const & [id, cb]: _table_observers) {
43✔
358
        (void) id;
359
        cb(ev);
×
360
    }
361
}
43✔
362

363
// Provide C-style bridge for TableRegistry to call
364
void DataManager__NotifyTableObservers(DataManager & dm, TableEvent const & ev) {
43✔
365
    dm.notifyTableObservers(ev);
43✔
366
}
43✔
367

368
std::vector<std::string> DataManager::getAllKeys() {
5✔
369
    std::vector<std::string> keys;
5✔
370
    keys.reserve(_data.size());
5✔
371
    for (auto const & [key, value]: _data) {
17✔
372

373
        keys.push_back(key);
12✔
374
    }
375
    return keys;
5✔
376
}
×
377

378
std::optional<DataTypeVariant> DataManager::getDataVariant(std::string const & key) {
74✔
379
    if (_data.find(key) != _data.end()) {
74✔
380
        return _data[key];
74✔
381
    }
382
    return std::nullopt;
×
383
}
384

385
void DataManager::setData(std::string const & key, DataTypeVariant data, TimeKey const & time_key) {
75✔
386
    _data[key] = data;
75✔
387
    setTimeKey(key, time_key);
75✔
388
    _notifyObservers();
75✔
389
}
75✔
390

391
std::optional<std::string> processFilePath(
12✔
392
        std::string const & file_path,
393
        std::filesystem::path const & base_path) {
394
    std::filesystem::path full_path = file_path;
12✔
395

396
    // Check for wildcard character
397
    if (file_path.find('*') != std::string::npos) {
12✔
398
        // Convert wildcard pattern to regex
399
        std::string const pattern = std::regex_replace(full_path.string(), std::regex("\\*"), ".*");
×
400
        std::regex const regex_pattern(pattern);
×
401

402
        // Iterate through the directory to find matching files
403
        for (auto const & entry: std::filesystem::directory_iterator(base_path)) {
×
404
            std::cout << "Checking " << entry.path().string() << " with full path " << full_path << std::endl;
×
405
            if (std::regex_match(entry.path().string(), regex_pattern)) {
×
406
                std::cout << "Loading file " << entry.path().string() << std::endl;
×
407
                return entry.path().string();
×
408
            }
409
        }
×
410
        return std::nullopt;
×
411
    } else {
×
412
        // Check if the file path is relative
413
        if (!std::filesystem::path(file_path).is_absolute()) {
12✔
414
            full_path = base_path / file_path;
×
415
        }
416
        // Check for the presence of the file
417
        if (std::filesystem::exists(full_path)) {
12✔
418
            std::cout << "Loading file " << full_path.string() << std::endl;
6✔
419
            return full_path.string();
6✔
420
        } else {
421
            return std::nullopt;
6✔
422
        }
423
    }
424
}
12✔
425

426
bool checkRequiredFields(json const & item, std::vector<std::string> const & requiredFields) {
12✔
427
    for (auto const & field: requiredFields) {
48✔
428
        if (!item.contains(field)) {
36✔
429
            std::cerr << "Error: Missing required field \"" << field << "\" in JSON item." << std::endl;
×
430
            return false;
×
431
        }
432
    }
433
    return true;
12✔
434
}
435

436
void checkOptionalFields(json const & item, std::vector<std::string> const & optionalFields) {
×
437
    for (auto const & field: optionalFields) {
×
438
        if (!item.contains(field)) {
×
439
            std::cout << "Warning: Optional field \"" << field << "\" is missing in JSON item." << std::endl;
×
440
        }
441
    }
442
}
×
443

444
DM_DataType stringToDataType(std::string const & data_type_str) {
12✔
445
    if (data_type_str == "video") return DM_DataType::Video;
12✔
446
    if (data_type_str == "images") return DM_DataType::Images;
12✔
447
    if (data_type_str == "points") return DM_DataType::Points;
12✔
448
    if (data_type_str == "mask") return DM_DataType::Mask;
12✔
449
    if (data_type_str == "line") return DM_DataType::Line;
10✔
450
    if (data_type_str == "analog") return DM_DataType::Analog;
4✔
451
    if (data_type_str == "digital_event") return DM_DataType::DigitalEvent;
2✔
452
    if (data_type_str == "digital_interval") return DM_DataType::DigitalInterval;
2✔
453
    if (data_type_str == "tensor") return DM_DataType::Tensor;
×
454
    if (data_type_str == "time") return DM_DataType::Time;
×
455
    return DM_DataType::Unknown;
×
456
}
457

458
std::vector<DataInfo> load_data_from_json_config(DataManager * dm, json const & j, std::filesystem::path const & base_path) {
71✔
459
    std::vector<DataInfo> data_info_list;
71✔
460
    // Create factory for plugin system
461
    ConcreteDataFactory factory;
71✔
462

463
    // Iterate through JSON array
464
    for (auto const & item: j) {
142✔
465

466
        // Skip transformation objects - they will be processed separately
467
        if (item.contains("transformations")) {
71✔
468
            continue;
59✔
469
        }
470

471
        if (!checkRequiredFields(item, {"data_type", "name", "filepath"})) {
36✔
472
            continue;// Exit if any required field is missing
×
473
        }
474

475
        std::string const data_type_str = item["data_type"];
12✔
476
        auto const data_type = stringToDataType(data_type_str);
12✔
477
        if (data_type == DM_DataType::Unknown) {
12✔
478
            std::cout << "Unknown data type: " << data_type_str << std::endl;
×
479
            continue;
×
480
        }
481

482
        std::string const name = item["name"];
12✔
483

484
        auto file_exists = processFilePath(item["filepath"], base_path);
12✔
485
        if (!file_exists) {
12✔
486
            std::cerr << "File does not exist: " << item["filepath"] << std::endl;
6✔
487
            continue;
6✔
488
        }
489

490
        std::string const file_path = file_exists.value();
6✔
491

492
        switch (data_type) {
6✔
493
            case DM_DataType::Video: {
×
494
                auto media_data = MediaDataFactory::loadMediaData(data_type, file_path, item);
×
495
                if (media_data) {
×
NEW
496
                    auto item_key = item.value("name", "media");
×
NEW
497
                    dm->setData<MediaData>(item_key, media_data, TimeKey("time"));
×
UNCOV
498
                    data_info_list.push_back({name, "VideoData", ""});
×
UNCOV
499
                } else {
×
500
                    std::cerr << "Failed to load video data: " << file_path << std::endl;
×
501
                }
502
                break;
×
503
            }
×
504
#ifdef ENABLE_OPENCV
505
            case DM_DataType::Images: {
×
506
                auto media_data = MediaDataFactory::loadMediaData(data_type, file_path, item);
×
507
                if (media_data) {
×
NEW
508
                    auto item_key = item.value("name", "media");
×
NEW
509
                    dm->setData<MediaData>(item_key, media_data, TimeKey("time"));
×
UNCOV
510
                    data_info_list.push_back({name, "ImageData", ""});
×
UNCOV
511
                } else {
×
512
                    std::cerr << "Failed to load image data: " << file_path << std::endl;
×
513
                }
514
                break;
×
515
            }
×
516
#endif
517
            case DM_DataType::Points: {
×
518

519
                auto point_data = load_into_PointData(file_path, item);
×
520

521
                // Attach identity context and generate EntityIds
522
                if (point_data) {
×
523
                    point_data->setIdentityContext(name, dm->getEntityRegistry());
×
524
                    point_data->rebuildAllEntityIds();
×
525
                }
526

527
                dm->setData<PointData>(name, point_data, TimeKey("time"));
×
528

529
                std::string const color = item.value("color", "#0000FF");
×
530
                data_info_list.push_back({name, "PointData", color});
×
531
                break;
×
532
            }
×
533
            case DM_DataType::Mask: {
1✔
534

535
                // Try registry system first, then fallback to legacy
536
                if (tryRegistryThenLegacyLoad(dm, file_path, data_type, item, name, data_info_list, &factory)) {
1✔
537
                    break;// Successfully loaded with plugin
1✔
538
                }
539

540
                // Legacy loading fallback
541
                auto mask_data = load_into_MaskData(file_path, item);
×
542

543
                std::string const color = item.value("color", "0000FF");
×
544
                dm->setData<MaskData>(name, mask_data, TimeKey("time"));
×
545

546
                data_info_list.push_back({name, "MaskData", color});
×
547

548
                break;
×
549
            }
×
550
            case DM_DataType::Line: {
3✔
551

552
                // Try registry system first, then fallback to legacy
553
                if (tryRegistryThenLegacyLoad(dm, file_path, data_type, item, name, data_info_list, &factory)) {
3✔
554
                    break;// Successfully loaded with plugin
2✔
555
                }
556

557
                // Legacy loading fallback
558
                auto line_data = load_into_LineData(file_path, item);
1✔
559

560
                // Attach identity context and generate EntityIds
561
                if (line_data) {
1✔
562
                    line_data->setIdentityContext(name, dm->getEntityRegistry());
1✔
563
                    line_data->rebuildAllEntityIds();
1✔
564
                }
565

566
                dm->setData<LineData>(name, line_data, TimeKey("time"));
1✔
567

568
                std::string const color = item.value("color", "0000FF");
1✔
569

570
                data_info_list.push_back({name, "LineData", color});
3✔
571

572
                break;
1✔
573
            }
1✔
574
            case DM_DataType::Analog: {
1✔
575

576
                auto analog_time_series = load_into_AnalogTimeSeries(file_path, item);
1✔
577

578
                for (size_t channel = 0; channel < analog_time_series.size(); channel++) {
2✔
579
                    std::string const channel_name = name + "_" + std::to_string(channel);
1✔
580

581
                    dm->setData<AnalogTimeSeries>(channel_name, analog_time_series[channel], TimeKey("time"));
1✔
582

583
                    if (item.contains("clock")) {
1✔
584
                        std::string const clock_str = item["clock"];
×
585
                        auto const clock = TimeKey(clock_str);
×
586
                        dm->setTimeKey(channel_name, clock);
×
587
                    }
×
588
                }
1✔
589
                break;
1✔
590
            }
1✔
591
            case DM_DataType::DigitalEvent: {
×
592

593
                auto digital_event_series = load_into_DigitalEventSeries(file_path, item);
×
594

595
                for (size_t channel = 0; channel < digital_event_series.size(); channel++) {
×
596
                    std::string const channel_name = name + "_" + std::to_string(channel);
×
597

598
                    // Attach identity context and generate EntityIds
599
                    if (digital_event_series[channel]) {
×
600
                        digital_event_series[channel]->setIdentityContext(channel_name, dm->getEntityRegistry());
×
601
                        digital_event_series[channel]->rebuildAllEntityIds();
×
602
                    }
603

604
                    dm->setData<DigitalEventSeries>(channel_name, digital_event_series[channel], TimeKey("time"));
×
605

606
                    if (item.contains("clock")) {
×
607
                        std::string const clock_str = item["clock"];
×
608
                        auto const clock = TimeKey(clock_str);
×
609
                        dm->setTimeKey(channel_name, clock);
×
610
                    }
×
611
                }
×
612
                break;
×
613
            }
×
614
            case DM_DataType::DigitalInterval: {
1✔
615

616
                auto digital_interval_series = load_into_DigitalIntervalSeries(file_path, item);
1✔
617
                if (digital_interval_series) {
1✔
618
                    digital_interval_series->setIdentityContext(name, dm->getEntityRegistry());
1✔
619
                    digital_interval_series->rebuildAllEntityIds();
1✔
620
                }
621
                dm->setData<DigitalIntervalSeries>(name, digital_interval_series, TimeKey("time"));
1✔
622

623
                break;
1✔
624
            }
1✔
625
            case DM_DataType::Tensor: {
×
626

627
                if (item["format"] == "numpy") {
×
628

629
                    TensorData tensor_data;
×
630
                    loadNpyToTensorData(file_path, tensor_data);
×
631

632
                    dm->setData<TensorData>(name, std::make_shared<TensorData>(tensor_data), TimeKey("time"));
×
633

634
                } else {
×
635
                    std::cout << "Format " << item["format"] << " not found for " << name << std::endl;
×
636
                }
637
                break;
×
638
            }
639
            case DM_DataType::Time: {
×
640

641
                if (item["format"] == "uint16") {
×
642

643
                    int const channel = item["channel"];
×
644
                    std::string const transition = item["transition"];
×
645

646
                    int const header_size = item.value("header_size", 0);
×
647

648
                    auto opts = Loader::BinaryAnalogOptions{.file_path = file_path,
×
649
                                                            .header_size_bytes = static_cast<size_t>(header_size)};
×
650
                    auto data = Loader::readBinaryFile<uint16_t>(opts);
×
651

652
                    auto digital_data = Loader::extractDigitalData(data, channel);
×
653
                    auto events = Loader::extractEvents(digital_data, transition);
×
654

655
                    // convert to int with std::transform
656
                    std::vector<int> events_int;
×
657
                    events_int.reserve(events.size());
×
658
                    for (auto e: events) {
×
659
                        events_int.push_back(static_cast<int>(e));
×
660
                    }
661
                    std::cout << "Loaded " << events_int.size() << " events for " << name << std::endl;
×
662

663
                    auto timeframe = std::make_shared<TimeFrame>(events_int);
×
664
                    dm->setTime(TimeKey(name), timeframe, true);
×
665
                }
×
666

667
                if (item["format"] == "uint16_length") {
×
668

669
                    int const header_size = item.value("header_size", 0);
×
670

671
                    auto opts = Loader::BinaryAnalogOptions{.file_path = file_path,
×
672
                                                            .header_size_bytes = static_cast<size_t>(header_size)};
×
673
                    auto data = Loader::readBinaryFile<uint16_t>(opts);
×
674

675
                    std::vector<int> t(data.size());
×
676
                    std::iota(std::begin(t), std::end(t), 0);
×
677

678
                    std::cout << "Total of " << t.size() << " timestamps for " << name << std::endl;
×
679

680
                    auto timeframe = std::make_shared<TimeFrame>(t);
×
681
                    dm->setTime(TimeKey(name), timeframe, true);
×
682
                }
×
683

684
                if (item["format"] == "filename") {
×
685

686
                    // Get required parameters
687
                    std::string const folder_path = file_path;// file path is required argument
×
688
                    std::string const regex_pattern = item["regex_pattern"];
×
689

690
                    // Get optional parameters with defaults
691
                    std::string const file_extension = item.value("file_extension", "");
×
692
                    std::string const mode_str = item.value("mode", "found_values");
×
693
                    bool const sort_ascending = item.value("sort_ascending", true);
×
694

695
                    // Convert mode string to enum
696
                    FilenameTimeFrameMode mode = FilenameTimeFrameMode::FOUND_VALUES;
×
697
                    if (mode_str == "zero_to_max") {
×
698
                        mode = FilenameTimeFrameMode::ZERO_TO_MAX;
×
699
                    } else if (mode_str == "min_to_max") {
×
700
                        mode = FilenameTimeFrameMode::MIN_TO_MAX;
×
701
                    }
702

703
                    // Create options
704
                    FilenameTimeFrameOptions options;
×
705
                    options.folder_path = folder_path;
×
706
                    options.file_extension = file_extension;
×
707
                    options.regex_pattern = regex_pattern;
×
708
                    options.mode = mode;
×
709
                    options.sort_ascending = sort_ascending;
×
710

711
                    // Create TimeFrame from filenames
712
                    auto timeframe = createTimeFrameFromFilenames(options);
×
713
                    if (timeframe) {
×
714
                        dm->setTime(TimeKey(name), timeframe, true);
×
715
                        std::cout << "Created TimeFrame '" << name << "' from filenames in "
716
                                  << folder_path << std::endl;
×
717
                    } else {
718
                        std::cerr << "Error: Failed to create TimeFrame from filenames for "
719
                                  << name << std::endl;
×
720
                    }
721
                }
×
722
                break;
×
723
            }
724
            default:
×
725
                std::cout << "Unsupported data type: " << data_type_str << std::endl;
×
726
                continue;
×
727
        }
×
728
        if (item.contains("clock")) {
6✔
729
            std::string clock_str = item["clock"];
×
730
            auto clock = TimeKey(clock_str);
×
731
            std::cout << "Setting time for " << name << " to " << clock << std::endl;
×
732
            dm->setTimeKey(name, clock);
×
733
        }
×
734
    }
24✔
735

736
    // Process all transformation objects found in the JSON array
737
    for (auto const & item: j) {
142✔
738
        if (item.contains("transformations")) {
71✔
739
            std::cout << "Found transformations section, executing pipeline..." << std::endl;
59✔
740

741
            try {
742
                // Create registry and pipeline with proper constructors
743
                auto registry = std::make_unique<TransformRegistry>();
59✔
744
                TransformPipeline pipeline(dm, registry.get());
59✔
745

746
                // Load the pipeline configuration from JSON
747
                if (!pipeline.loadFromJson(item["transformations"])) {
59✔
748
                    std::cerr << "Failed to load pipeline configuration from JSON" << std::endl;
×
749
                    continue;
×
750
                }
751

752
                // Execute the pipeline with a progress callback
753
                auto result = pipeline.execute([](int step_index, std::string const & step_name, int step_progress, int overall_progress) {
118✔
754
                    std::cout << "Step " << step_index << " ('" << step_name << "'): "
324✔
755
                              << step_progress << "% (Overall: " << overall_progress << "%)" << std::endl;
324✔
756
                });
118✔
757

758
                if (result.success) {
59✔
759
                    std::cout << "Pipeline executed successfully!" << std::endl;
59✔
760
                    std::cout << "Steps completed: " << result.steps_completed << "/" << result.total_steps << std::endl;
59✔
761
                    std::cout << "Total execution time: " << result.total_execution_time_ms << " ms" << std::endl;
59✔
762
                } else {
763
                    std::cerr << "Pipeline execution failed: " << result.error_message << std::endl;
×
764
                }
765

766
            } catch (std::exception const & e) {
59✔
767
                std::cerr << "Exception during pipeline execution: " << e.what() << std::endl;
×
768
            }
×
769
        }
770
    }
771

772
    return data_info_list;
71✔
773
}
74✔
774

775
std::vector<DataInfo> load_data_from_json_config(DataManager * dm, std::string const & json_filepath) {
71✔
776
    // Open JSON file
777
    std::ifstream ifs(json_filepath);
71✔
778
    if (!ifs.is_open()) {
71✔
779
        std::cerr << "Failed to open JSON file: " << json_filepath << std::endl;
×
780
        return {};
×
781
    }
782

783
    // Parse JSON
784
    json j;
71✔
785
    ifs >> j;
71✔
786

787
    // get base path of filepath
788
    std::filesystem::path const base_path = std::filesystem::path(json_filepath).parent_path();
71✔
789
    return load_data_from_json_config(dm, j, base_path);
71✔
790
}
71✔
791

792
DM_DataType DataManager::getType(std::string const & key) const {
25✔
793
    auto it = _data.find(key);
25✔
794
    if (it != _data.end()) {
25✔
795
        if (std::holds_alternative<std::shared_ptr<MediaData>>(it->second)) {
22✔
796
            auto media_data = std::get<std::shared_ptr<MediaData>>(it->second);
18✔
797
            switch (media_data->getMediaType()) {
18✔
798
                case MediaData::MediaType::Video:
11✔
799
                    return DM_DataType::Video;
11✔
800
                case MediaData::MediaType::Images:
7✔
801
                    return DM_DataType::Images;
7✔
802
                case MediaData::MediaType::HDF5:
×
803
                    // For HDF5, we might need additional logic to determine if it's video or images
804
                    // For now, defaulting to Video (old behavior)
805
                    return DM_DataType::Video;
×
806
                default:
×
807
                    return DM_DataType::Video;// Old behavior for unknown types
×
808
            }
809
        } else if (std::holds_alternative<std::shared_ptr<PointData>>(it->second)) {
22✔
810
            return DM_DataType::Points;
2✔
811
        } else if (std::holds_alternative<std::shared_ptr<LineData>>(it->second)) {
2✔
812
            return DM_DataType::Line;
1✔
813
        } else if (std::holds_alternative<std::shared_ptr<MaskData>>(it->second)) {
1✔
814
            return DM_DataType::Mask;
1✔
815
        } else if (std::holds_alternative<std::shared_ptr<AnalogTimeSeries>>(it->second)) {
×
816
            return DM_DataType::Analog;
×
817
        } else if (std::holds_alternative<std::shared_ptr<DigitalEventSeries>>(it->second)) {
×
818
            return DM_DataType::DigitalEvent;
×
819
        } else if (std::holds_alternative<std::shared_ptr<DigitalIntervalSeries>>(it->second)) {
×
820
            return DM_DataType::DigitalInterval;
×
821
        } else if (std::holds_alternative<std::shared_ptr<TensorData>>(it->second)) {
×
822
            return DM_DataType::Tensor;
×
823
        }
824
        return DM_DataType::Unknown;
×
825
    }
826
    return DM_DataType::Unknown;
3✔
827
}
828

829
std::string convert_data_type_to_string(DM_DataType type) {
×
830
    switch (type) {
×
831
        case DM_DataType::Video:
×
832
            return "video";
×
833
        case DM_DataType::Images:
×
834
            return "images";
×
835
        case DM_DataType::Points:
×
836
            return "points";
×
837
        case DM_DataType::Mask:
×
838
            return "mask";
×
839
        case DM_DataType::Line:
×
840
            return "line";
×
841
        case DM_DataType::Analog:
×
842
            return "analog";
×
843
        case DM_DataType::DigitalEvent:
×
844
            return "digital_event";
×
845
        case DM_DataType::DigitalInterval:
×
846
            return "digital_interval";
×
847
        case DM_DataType::Tensor:
×
848
            return "tensor";
×
849
        case DM_DataType::Time:
×
850
            return "time";
×
851
        default:
×
852
            return "unknown";
×
853
    }
854
}
855

856
template std::shared_ptr<AnalogTimeSeries> DataManager::getData<AnalogTimeSeries>(std::string const & key);
857
template void DataManager::setData<AnalogTimeSeries>(std::string const & key, TimeKey const & time_key);
858
template void DataManager::setData<AnalogTimeSeries>(std::string const & key, std::shared_ptr<AnalogTimeSeries> data, TimeKey const & time_key);
859

860
template std::shared_ptr<DigitalEventSeries> DataManager::getData<DigitalEventSeries>(std::string const & key);
861
template void DataManager::setData<DigitalEventSeries>(std::string const & key, TimeKey const & time_key);
862
template void DataManager::setData<DigitalEventSeries>(std::string const & key, std::shared_ptr<DigitalEventSeries> data, TimeKey const & time_key);
863

864
template std::shared_ptr<DigitalIntervalSeries> DataManager::getData<DigitalIntervalSeries>(std::string const & key);
865
template void DataManager::setData<DigitalIntervalSeries>(std::string const & key, TimeKey const & time_key);
866
template void DataManager::setData<DigitalIntervalSeries>(std::string const & key, std::shared_ptr<DigitalIntervalSeries> data, TimeKey const & time_key);
867

868
template std::shared_ptr<LineData> DataManager::getData<LineData>(std::string const & key);
869
template void DataManager::setData<LineData>(std::string const & key, TimeKey const & time_key);
870
template void DataManager::setData<LineData>(std::string const & key, std::shared_ptr<LineData> data, TimeKey const & time_key);
871

872
template std::shared_ptr<MaskData> DataManager::getData<MaskData>(std::string const & key);
873
template void DataManager::setData<MaskData>(std::string const & key, TimeKey const & time_key);
874
template void DataManager::setData<MaskData>(std::string const & key, std::shared_ptr<MaskData> data, TimeKey const & time_key);
875

876
template std::shared_ptr<MediaData> DataManager::getData<MediaData>(std::string const & key);
877

878
template std::shared_ptr<PointData> DataManager::getData<PointData>(std::string const & key);
879
template void DataManager::setData<PointData>(std::string const & key, TimeKey const & time_key);
880
template void DataManager::setData<PointData>(std::string const & key, std::shared_ptr<PointData> data, TimeKey const & time_key);
881

882
template std::shared_ptr<TensorData> DataManager::getData<TensorData>(std::string const & key);
883
template void DataManager::setData<TensorData>(std::string const & key, TimeKey const & time_key);
884
template void DataManager::setData<TensorData>(std::string const & key, std::shared_ptr<TensorData> data, TimeKey const & time_key);
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc