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

paulmthompson / WhiskerToolbox / 17733471381

15 Sep 2025 12:43PM UTC coverage: 72.1% (+0.4%) from 71.744%
17733471381

push

github

paulmthompson
fix optional missing include on windows

37727 of 52326 relevant lines covered (72.1%)

1297.48 hits per line

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

61.61
/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
#include "Entity/EntityGroupManager.hpp"
45

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

52
using namespace nlohmann;
53

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

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

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

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

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

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

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

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

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

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

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

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

130
    setTimeKey("media", TimeKey("time"));
900✔
131
    _output_path = std::filesystem::current_path();
300✔
132

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

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

139
    // Initialize EntityGroupManager
140
    _entity_group_manager = std::make_unique<EntityGroupManager>();
300✔
141

142
    // Register all available loaders
143
    static bool loaders_registered = false;
144
    if (!loaders_registered) {
300✔
145
        registerAllLoaders();
177✔
146
        loaders_registered = true;
177✔
147
    }
148
}
300✔
149

150
DataManager::~DataManager() = default;
300✔
151

152
void DataManager::reset() {
1✔
153
    std::cout << "DataManager: Resetting to initial state..." << std::endl;
1✔
154

155
    // Clear all data objects except media (which we'll reset)
156
    _data.clear();
1✔
157

158
    // Reset media to a fresh empty MediaData object
159
    _data["media"] = std::make_shared<EmptyMediaData>();
3✔
160

161
    // Clear all TimeFrame objects except the default "time" frame
162
    _times.clear();
1✔
163

164
    // Recreate the default "time" TimeFrame
165
    _times[TimeKey("time")] = std::make_shared<TimeFrame>();
1✔
166

167
    // Clear all data-to-timeframe mappings and recreate the default media mapping
168
    _time_frames.clear();
1✔
169
    setTimeKey("media", TimeKey("time"));
3✔
170

171

172
    // Reset current time
173
    _current_time = 0;
1✔
174

175
    // Notify observers that the state has changed
176
    _notifyObservers();
1✔
177

178
    std::cout << "DataManager: Reset complete. Default 'time' frame and 'media' data restored." << std::endl;
1✔
179

180
    // Reset entity registry for a new session context
181
    if (_entity_registry) {
1✔
182
        _entity_registry->clear();
1✔
183
    }
184

185
    // Reset entity group manager for a new session context
186
    if (_entity_group_manager) {
1✔
187
        _entity_group_manager->clear();
1✔
188
    }
189
}
1✔
190

191
bool DataManager::setTime(TimeKey const & key, std::shared_ptr<TimeFrame> timeframe, bool overwrite) {
349✔
192

193
    if (!timeframe) {
349✔
194
        std::cerr << "Error: Cannot register a nullptr TimeFrame for key: " << key << std::endl;
1✔
195
        return false;
1✔
196
    }
197

198
    if (_times.find(key) != _times.end()) {
348✔
199
        if (!overwrite) {
25✔
200
            std::cerr << "Error: Time key already exists in DataManager: " << key << std::endl;
20✔
201
            return false;
20✔
202
        }
203
    }
204

205
    _times[key] = std::move(timeframe);
328✔
206

207
    // Move ptr to new time frame to all data that hold
208
    for (auto const & [data_key, data]: _data) {
674✔
209
        if (_time_frames.find(data_key) != _time_frames.end()) {
346✔
210
            auto time_key = _time_frames[data_key];
346✔
211
            if (time_key == key) {
346✔
212
                std::visit([this, timeframe](auto & x) {
40✔
213
                    x->setTimeFrame(timeframe);
20✔
214
                },
20✔
215
                           data);
216
            }
217
        }
346✔
218
    }
219

220
    return true;
328✔
221
}
222

223
std::shared_ptr<TimeFrame> DataManager::getTime() {
8✔
224
    return _times[TimeKey("time")];
8✔
225
};
226

227
std::shared_ptr<TimeFrame> DataManager::getTime(TimeKey const & key) {
367✔
228
    if (_times.find(key) != _times.end()) {
367✔
229
        return _times[key];
365✔
230
    }
231
    return nullptr;
2✔
232
};
233

234
TimeIndexAndFrame DataManager::getCurrentIndexAndFrame(TimeKey const & key) {
×
235
    if (_times.find(key) != _times.end()) {
×
236
        return {TimeFrameIndex(_current_time), _times[key]};
×
237
    }
238
    return {TimeFrameIndex(_current_time), nullptr};
×
239
}
×
240

241
bool DataManager::removeTime(TimeKey const & key) {
15✔
242
    if (_times.find(key) == _times.end()) {
15✔
243
        std::cerr << "Error: could not find time key in DataManager: " << key << std::endl;
×
244
        return false;
×
245
    }
246

247
    auto it = _times.find(key);
15✔
248
    _times.erase(it);
15✔
249
    return true;
15✔
250
}
251

252
bool DataManager::setTimeKey(std::string const & data_key, TimeKey const & time_key) {
1,158✔
253
    if (_data.find(data_key) == _data.end()) {
1,158✔
254
        std::cerr << "Error: Data key not found in DataManager: " << data_key << std::endl;
3✔
255
        return false;
3✔
256
    }
257

258
    if (_times.find(time_key) == _times.end()) {
1,155✔
259
        std::cerr << "Error: Time key not found in DataManager: " << time_key << std::endl;
1✔
260
        return false;
1✔
261
    }
262

263
    _time_frames[data_key] = time_key;
1,154✔
264

265
    if (_data.find(data_key) != _data.end()) {
1,154✔
266
        auto data = _data[data_key];
1,154✔
267
        std::visit([this, time_key](auto & x) {
2,308✔
268
            x->setTimeFrame(this->_times[time_key]);
1,154✔
269
        },
1,154✔
270
                   data);
271
    }
1,154✔
272
    return true;
1,154✔
273
}
274

275
TimeKey DataManager::getTimeKey(std::string const & data_key) {
801✔
276
    // check if data_key exists
277
    if (_data.find(data_key) == _data.end()) {
801✔
278
        std::cerr << "Error: Data key not found in DataManager: " << data_key << std::endl;
1✔
279
        return TimeKey("");
1✔
280
    }
281

282
    // check if data key has time frame
283
    if (_time_frames.find(data_key) == _time_frames.end()) {
800✔
284
        std::cerr << "Error: Data key "
285
                  << data_key
286
                  << " exists, but not assigned to a TimeFrame" << std::endl;
×
287
        return TimeKey("");
×
288
    }
289

290
    return _time_frames[data_key];
800✔
291
}
292

293
std::vector<TimeKey> DataManager::getTimeFrameKeys() {
120✔
294
    std::vector<TimeKey> keys;
120✔
295
    keys.reserve(_times.size());
120✔
296
    for (auto const & [key, value]: _times) {
545✔
297

298
        keys.push_back(key);
425✔
299
    }
300
    return keys;
120✔
301
}
×
302

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

305
    int id = -1;
7✔
306

307
    if (_data.find(key) != _data.end()) {
7✔
308
        auto data = _data[key];
6✔
309

310
        id = std::visit([callback](auto & x) {
12✔
311
            return x.get()->addObserver(callback);
6✔
312
        },
313
                        data);
314
    }
6✔
315

316
    return id;
7✔
317
}
318

319
bool DataManager::removeCallbackFromData(std::string const & key, int callback_id) {
4✔
320
    if (_data.find(key) != _data.end()) {
4✔
321
        auto data = _data[key];
3✔
322

323
        std::visit([callback_id](auto & x) {
6✔
324
            x.get()->removeObserver(callback_id);
3✔
325
        },
3✔
326
                   data);
327

328
        return true;
3✔
329
    }
3✔
330

331
    return false;
1✔
332
}
333

334
void DataManager::addObserver(ObserverCallback callback) {
70✔
335
    _observers.push_back(std::move(callback));
70✔
336
}
70✔
337

338
void DataManager::_notifyObservers() {
867✔
339
    for (auto & observer: _observers) {
920✔
340
        observer();
53✔
341
    }
342
}
867✔
343

344
// ===== Table Registry accessors =====
345
TableRegistry * DataManager::getTableRegistry() {
422✔
346
    return _table_registry.get();
422✔
347
}
348

349
TableRegistry const * DataManager::getTableRegistry() const {
×
350
    return _table_registry.get();
×
351
}
352

353
// ===== Table observer channel =====
354
int DataManager::addTableObserver(TableObserver callback) {
16✔
355
    if (!callback) return -1;
16✔
356
    int id = _next_table_observer_id++;
16✔
357
    _table_observers[id] = std::move(callback);
16✔
358
    return id;
16✔
359
}
360

361
bool DataManager::removeTableObserver(int callback_id) {
×
362
    return _table_observers.erase(callback_id) > 0;
×
363
}
364

365
void DataManager::notifyTableObservers(TableEvent const & ev) {
71✔
366
    for (auto const & [id, cb]: _table_observers) {
97✔
367
        (void) id;
368
        cb(ev);
26✔
369
    }
370
}
71✔
371

372
// Provide C-style bridge for TableRegistry to call
373
void DataManager__NotifyTableObservers(DataManager & dm, TableEvent const & ev) {
71✔
374
    dm.notifyTableObservers(ev);
71✔
375
}
71✔
376

377
std::vector<std::string> DataManager::getAllKeys() {
96✔
378
    std::vector<std::string> keys;
96✔
379
    keys.reserve(_data.size());
96✔
380
    for (auto const & [key, value]: _data) {
607✔
381

382
        keys.push_back(key);
511✔
383
    }
384
    return keys;
96✔
385
}
×
386

387
std::optional<DataTypeVariant> DataManager::getDataVariant(std::string const & key) {
74✔
388
    if (_data.find(key) != _data.end()) {
74✔
389
        return _data[key];
74✔
390
    }
391
    return std::nullopt;
×
392
}
393

394
void DataManager::setData(std::string const & key, DataTypeVariant data, TimeKey const & time_key) {
75✔
395
    _data[key] = data;
75✔
396
    setTimeKey(key, time_key);
75✔
397
    _notifyObservers();
75✔
398
}
75✔
399

400
bool DataManager::deleteData(std::string const & key) {
15✔
401
    // Check if the key exists
402
    if (_data.find(key) == _data.end()) {
15✔
403
        std::cerr << "Error: Data key not found in DataManager: " << key << std::endl;
×
404
        return false;
×
405
    }
406

407
    // Remove the time frame mapping if it exists
408
    _time_frames.erase(key);
15✔
409
    
410
    // Remove the data from storage
411
    _data.erase(key);
15✔
412
    
413
    // Notify all observers that data has changed
414
    _notifyObservers();
15✔
415
    
416
    std::cout << "DataManager: Successfully deleted data with key: " << key << std::endl;
15✔
417
    return true;
15✔
418
}
419

420
std::optional<std::string> processFilePath(
12✔
421
        std::string const & file_path,
422
        std::filesystem::path const & base_path) {
423
    std::filesystem::path full_path = file_path;
12✔
424

425
    // Check for wildcard character
426
    if (file_path.find('*') != std::string::npos) {
12✔
427
        // Convert wildcard pattern to regex
428
        std::string const pattern = std::regex_replace(full_path.string(), std::regex("\\*"), ".*");
×
429
        std::regex const regex_pattern(pattern);
×
430

431
        // Iterate through the directory to find matching files
432
        for (auto const & entry: std::filesystem::directory_iterator(base_path)) {
×
433
            std::cout << "Checking " << entry.path().string() << " with full path " << full_path << std::endl;
×
434
            if (std::regex_match(entry.path().string(), regex_pattern)) {
×
435
                std::cout << "Loading file " << entry.path().string() << std::endl;
×
436
                return entry.path().string();
×
437
            }
438
        }
×
439
        return std::nullopt;
×
440
    } else {
×
441
        // Check if the file path is relative
442
        if (!std::filesystem::path(file_path).is_absolute()) {
12✔
443
            full_path = base_path / file_path;
×
444
        }
445
        // Check for the presence of the file
446
        if (std::filesystem::exists(full_path)) {
12✔
447
            std::cout << "Loading file " << full_path.string() << std::endl;
6✔
448
            return full_path.string();
6✔
449
        } else {
450
            return std::nullopt;
6✔
451
        }
452
    }
453
}
12✔
454

455
bool checkRequiredFields(json const & item, std::vector<std::string> const & requiredFields) {
12✔
456
    for (auto const & field: requiredFields) {
48✔
457
        if (!item.contains(field)) {
36✔
458
            std::cerr << "Error: Missing required field \"" << field << "\" in JSON item." << std::endl;
×
459
            return false;
×
460
        }
461
    }
462
    return true;
12✔
463
}
464

465
void checkOptionalFields(json const & item, std::vector<std::string> const & optionalFields) {
×
466
    for (auto const & field: optionalFields) {
×
467
        if (!item.contains(field)) {
×
468
            std::cout << "Warning: Optional field \"" << field << "\" is missing in JSON item." << std::endl;
×
469
        }
470
    }
471
}
×
472

473
DM_DataType stringToDataType(std::string const & data_type_str) {
12✔
474
    if (data_type_str == "video") return DM_DataType::Video;
12✔
475
    if (data_type_str == "images") return DM_DataType::Images;
12✔
476
    if (data_type_str == "points") return DM_DataType::Points;
12✔
477
    if (data_type_str == "mask") return DM_DataType::Mask;
12✔
478
    if (data_type_str == "line") return DM_DataType::Line;
10✔
479
    if (data_type_str == "analog") return DM_DataType::Analog;
4✔
480
    if (data_type_str == "digital_event") return DM_DataType::DigitalEvent;
2✔
481
    if (data_type_str == "digital_interval") return DM_DataType::DigitalInterval;
2✔
482
    if (data_type_str == "tensor") return DM_DataType::Tensor;
×
483
    if (data_type_str == "time") return DM_DataType::Time;
×
484
    return DM_DataType::Unknown;
×
485
}
486

487
std::vector<DataInfo> load_data_from_json_config(DataManager * dm, json const & j, std::filesystem::path const & base_path) {
71✔
488
    std::vector<DataInfo> data_info_list;
71✔
489
    // Create factory for plugin system
490
    ConcreteDataFactory factory;
71✔
491

492
    // Iterate through JSON array
493
    for (auto const & item: j) {
142✔
494

495
        // Skip transformation objects - they will be processed separately
496
        if (item.contains("transformations")) {
71✔
497
            continue;
59✔
498
        }
499

500
        if (!checkRequiredFields(item, {"data_type", "name", "filepath"})) {
36✔
501
            continue;// Exit if any required field is missing
×
502
        }
503

504
        std::string const data_type_str = item["data_type"];
12✔
505
        auto const data_type = stringToDataType(data_type_str);
12✔
506
        if (data_type == DM_DataType::Unknown) {
12✔
507
            std::cout << "Unknown data type: " << data_type_str << std::endl;
×
508
            continue;
×
509
        }
510

511
        std::string const name = item["name"];
12✔
512

513
        auto file_exists = processFilePath(item["filepath"], base_path);
12✔
514
        if (!file_exists) {
12✔
515
            std::cerr << "File does not exist: " << item["filepath"] << std::endl;
6✔
516
            continue;
6✔
517
        }
518

519
        std::string const file_path = file_exists.value();
6✔
520

521
        switch (data_type) {
6✔
522
            case DM_DataType::Video: {
×
523
                auto media_data = MediaDataFactory::loadMediaData(data_type, file_path, item);
×
524
                if (media_data) {
×
525
                    auto item_key = item.value("name", "media");
×
526
                    dm->setData<MediaData>(item_key, media_data, TimeKey("time"));
×
527
                    data_info_list.push_back({name, "VideoData", ""});
×
528
                } else {
×
529
                    std::cerr << "Failed to load video data: " << file_path << std::endl;
×
530
                }
531
                break;
×
532
            }
×
533
#ifdef ENABLE_OPENCV
534
            case DM_DataType::Images: {
×
535
                auto media_data = MediaDataFactory::loadMediaData(data_type, file_path, item);
×
536
                if (media_data) {
×
537
                    auto item_key = item.value("name", "media");
×
538
                    dm->setData<MediaData>(item_key, media_data, TimeKey("time"));
×
539
                    data_info_list.push_back({name, "ImageData", ""});
×
540
                } else {
×
541
                    std::cerr << "Failed to load image data: " << file_path << std::endl;
×
542
                }
543
                break;
×
544
            }
×
545
#endif
546
            case DM_DataType::Points: {
×
547

548
                auto point_data = load_into_PointData(file_path, item);
×
549

550
                // Attach identity context and generate EntityIds
551
                if (point_data) {
×
552
                    point_data->setIdentityContext(name, dm->getEntityRegistry());
×
553
                    point_data->rebuildAllEntityIds();
×
554
                }
555

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

558
                std::string const color = item.value("color", "#0000FF");
×
559
                data_info_list.push_back({name, "PointData", color});
×
560
                break;
×
561
            }
×
562
            case DM_DataType::Mask: {
1✔
563

564
                // Try registry system first, then fallback to legacy
565
                if (tryRegistryThenLegacyLoad(dm, file_path, data_type, item, name, data_info_list, &factory)) {
1✔
566
                    break;// Successfully loaded with plugin
1✔
567
                }
568

569
                // Legacy loading fallback
570
                auto mask_data = load_into_MaskData(file_path, item);
×
571

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

575
                data_info_list.push_back({name, "MaskData", color});
×
576

577
                break;
×
578
            }
×
579
            case DM_DataType::Line: {
3✔
580

581
                // Try registry system first, then fallback to legacy
582
                if (tryRegistryThenLegacyLoad(dm, file_path, data_type, item, name, data_info_list, &factory)) {
3✔
583
                    break;// Successfully loaded with plugin
2✔
584
                }
585

586
                // Legacy loading fallback
587
                auto line_data = load_into_LineData(file_path, item);
1✔
588

589
                // Attach identity context and generate EntityIds
590
                if (line_data) {
1✔
591
                    line_data->setIdentityContext(name, dm->getEntityRegistry());
1✔
592
                    line_data->rebuildAllEntityIds();
1✔
593
                }
594

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

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

599
                data_info_list.push_back({name, "LineData", color});
3✔
600

601
                break;
1✔
602
            }
1✔
603
            case DM_DataType::Analog: {
1✔
604

605
                auto analog_time_series = load_into_AnalogTimeSeries(file_path, item);
1✔
606

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

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

612
                    if (item.contains("clock")) {
1✔
613
                        std::string const clock_str = item["clock"];
×
614
                        auto const clock = TimeKey(clock_str);
×
615
                        dm->setTimeKey(channel_name, clock);
×
616
                    }
×
617
                }
1✔
618
                break;
1✔
619
            }
1✔
620
            case DM_DataType::DigitalEvent: {
×
621

622
                auto digital_event_series = load_into_DigitalEventSeries(file_path, item);
×
623

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

627
                    // Attach identity context and generate EntityIds
628
                    if (digital_event_series[channel]) {
×
629
                        digital_event_series[channel]->setIdentityContext(channel_name, dm->getEntityRegistry());
×
630
                        digital_event_series[channel]->rebuildAllEntityIds();
×
631
                    }
632

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

635
                    if (item.contains("clock")) {
×
636
                        std::string const clock_str = item["clock"];
×
637
                        auto const clock = TimeKey(clock_str);
×
638
                        dm->setTimeKey(channel_name, clock);
×
639
                    }
×
640
                }
×
641
                break;
×
642
            }
×
643
            case DM_DataType::DigitalInterval: {
1✔
644

645
                auto digital_interval_series = load_into_DigitalIntervalSeries(file_path, item);
1✔
646
                if (digital_interval_series) {
1✔
647
                    digital_interval_series->setIdentityContext(name, dm->getEntityRegistry());
1✔
648
                    digital_interval_series->rebuildAllEntityIds();
1✔
649
                }
650
                dm->setData<DigitalIntervalSeries>(name, digital_interval_series, TimeKey("time"));
1✔
651

652
                break;
1✔
653
            }
1✔
654
            case DM_DataType::Tensor: {
×
655

656
                if (item["format"] == "numpy") {
×
657

658
                    TensorData tensor_data;
×
659
                    loadNpyToTensorData(file_path, tensor_data);
×
660

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

663
                } else {
×
664
                    std::cout << "Format " << item["format"] << " not found for " << name << std::endl;
×
665
                }
666
                break;
×
667
            }
668
            case DM_DataType::Time: {
×
669

670
                if (item["format"] == "uint16") {
×
671

672
                    int const channel = item["channel"];
×
673
                    std::string const transition = item["transition"];
×
674

675
                    int const header_size = item.value("header_size", 0);
×
676

677
                    auto opts = Loader::BinaryAnalogOptions{.file_path = file_path,
×
678
                                                            .header_size_bytes = static_cast<size_t>(header_size)};
×
679
                    auto data = Loader::readBinaryFile<uint16_t>(opts);
×
680

681
                    auto digital_data = Loader::extractDigitalData(data, channel);
×
682
                    auto events = Loader::extractEvents(digital_data, transition);
×
683

684
                    // convert to int with std::transform
685
                    std::vector<int> events_int;
×
686
                    events_int.reserve(events.size());
×
687
                    for (auto e: events) {
×
688
                        events_int.push_back(static_cast<int>(e));
×
689
                    }
690
                    std::cout << "Loaded " << events_int.size() << " events for " << name << std::endl;
×
691

692
                    auto timeframe = std::make_shared<TimeFrame>(events_int);
×
693
                    dm->setTime(TimeKey(name), timeframe, true);
×
694
                }
×
695

696
                if (item["format"] == "uint16_length") {
×
697

698
                    int const header_size = item.value("header_size", 0);
×
699

700
                    auto opts = Loader::BinaryAnalogOptions{.file_path = file_path,
×
701
                                                            .header_size_bytes = static_cast<size_t>(header_size)};
×
702
                    auto data = Loader::readBinaryFile<uint16_t>(opts);
×
703

704
                    std::vector<int> t(data.size());
×
705
                    std::iota(std::begin(t), std::end(t), 0);
×
706

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

709
                    auto timeframe = std::make_shared<TimeFrame>(t);
×
710
                    dm->setTime(TimeKey(name), timeframe, true);
×
711
                }
×
712

713
                if (item["format"] == "filename") {
×
714

715
                    // Get required parameters
716
                    std::string const folder_path = file_path;// file path is required argument
×
717
                    std::string const regex_pattern = item["regex_pattern"];
×
718

719
                    // Get optional parameters with defaults
720
                    std::string const file_extension = item.value("file_extension", "");
×
721
                    std::string const mode_str = item.value("mode", "found_values");
×
722
                    bool const sort_ascending = item.value("sort_ascending", true);
×
723

724
                    // Convert mode string to enum
725
                    FilenameTimeFrameMode mode = FilenameTimeFrameMode::FOUND_VALUES;
×
726
                    if (mode_str == "zero_to_max") {
×
727
                        mode = FilenameTimeFrameMode::ZERO_TO_MAX;
×
728
                    } else if (mode_str == "min_to_max") {
×
729
                        mode = FilenameTimeFrameMode::MIN_TO_MAX;
×
730
                    }
731

732
                    // Create options
733
                    FilenameTimeFrameOptions options;
×
734
                    options.folder_path = folder_path;
×
735
                    options.file_extension = file_extension;
×
736
                    options.regex_pattern = regex_pattern;
×
737
                    options.mode = mode;
×
738
                    options.sort_ascending = sort_ascending;
×
739

740
                    // Create TimeFrame from filenames
741
                    auto timeframe = createTimeFrameFromFilenames(options);
×
742
                    if (timeframe) {
×
743
                        dm->setTime(TimeKey(name), timeframe, true);
×
744
                        std::cout << "Created TimeFrame '" << name << "' from filenames in "
745
                                  << folder_path << std::endl;
×
746
                    } else {
747
                        std::cerr << "Error: Failed to create TimeFrame from filenames for "
748
                                  << name << std::endl;
×
749
                    }
750
                }
×
751
                break;
×
752
            }
753
            default:
×
754
                std::cout << "Unsupported data type: " << data_type_str << std::endl;
×
755
                continue;
×
756
        }
×
757
        if (item.contains("clock")) {
6✔
758
            std::string clock_str = item["clock"];
×
759
            auto clock = TimeKey(clock_str);
×
760
            std::cout << "Setting time for " << name << " to " << clock << std::endl;
×
761
            dm->setTimeKey(name, clock);
×
762
        }
×
763
    }
24✔
764

765
    // Process all transformation objects found in the JSON array
766
    for (auto const & item: j) {
142✔
767
        if (item.contains("transformations")) {
71✔
768
            std::cout << "Found transformations section, executing pipeline..." << std::endl;
59✔
769

770
            try {
771
                // Create registry and pipeline with proper constructors
772
                auto registry = std::make_unique<TransformRegistry>();
59✔
773
                TransformPipeline pipeline(dm, registry.get());
59✔
774

775
                // Load the pipeline configuration from JSON
776
                if (!pipeline.loadFromJson(item["transformations"])) {
59✔
777
                    std::cerr << "Failed to load pipeline configuration from JSON" << std::endl;
×
778
                    continue;
×
779
                }
780

781
                // Execute the pipeline with a progress callback
782
                auto result = pipeline.execute([](int step_index, std::string const & step_name, int step_progress, int overall_progress) {
118✔
783
                    std::cout << "Step " << step_index << " ('" << step_name << "'): "
324✔
784
                              << step_progress << "% (Overall: " << overall_progress << "%)" << std::endl;
324✔
785
                });
118✔
786

787
                if (result.success) {
59✔
788
                    std::cout << "Pipeline executed successfully!" << std::endl;
59✔
789
                    std::cout << "Steps completed: " << result.steps_completed << "/" << result.total_steps << std::endl;
59✔
790
                    std::cout << "Total execution time: " << result.total_execution_time_ms << " ms" << std::endl;
59✔
791
                } else {
792
                    std::cerr << "Pipeline execution failed: " << result.error_message << std::endl;
×
793
                }
794

795
            } catch (std::exception const & e) {
59✔
796
                std::cerr << "Exception during pipeline execution: " << e.what() << std::endl;
×
797
            }
×
798
        }
799
    }
800

801
    return data_info_list;
71✔
802
}
74✔
803

804
std::vector<DataInfo> load_data_from_json_config(DataManager * dm, std::string const & json_filepath) {
71✔
805
    // Open JSON file
806
    std::ifstream ifs(json_filepath);
71✔
807
    if (!ifs.is_open()) {
71✔
808
        std::cerr << "Failed to open JSON file: " << json_filepath << std::endl;
×
809
        return {};
×
810
    }
811

812
    // Parse JSON
813
    json j;
71✔
814
    ifs >> j;
71✔
815

816
    // get base path of filepath
817
    std::filesystem::path const base_path = std::filesystem::path(json_filepath).parent_path();
71✔
818
    return load_data_from_json_config(dm, j, base_path);
71✔
819
}
71✔
820

821
DM_DataType DataManager::getType(std::string const & key) const {
689✔
822
    auto it = _data.find(key);
689✔
823
    if (it != _data.end()) {
689✔
824
        if (std::holds_alternative<std::shared_ptr<MediaData>>(it->second)) {
686✔
825
            auto media_data = std::get<std::shared_ptr<MediaData>>(it->second);
109✔
826
            switch (media_data->getMediaType()) {
109✔
827
                case MediaData::MediaType::Video:
102✔
828
                    return DM_DataType::Video;
102✔
829
                case MediaData::MediaType::Images:
7✔
830
                    return DM_DataType::Images;
7✔
831
                case MediaData::MediaType::HDF5:
×
832
                    // For HDF5, we might need additional logic to determine if it's video or images
833
                    // For now, defaulting to Video (old behavior)
834
                    return DM_DataType::Video;
×
835
                default:
×
836
                    return DM_DataType::Video;// Old behavior for unknown types
×
837
            }
838
        } else if (std::holds_alternative<std::shared_ptr<PointData>>(it->second)) {
686✔
839
            return DM_DataType::Points;
94✔
840
        } else if (std::holds_alternative<std::shared_ptr<LineData>>(it->second)) {
483✔
841
            return DM_DataType::Line;
82✔
842
        } else if (std::holds_alternative<std::shared_ptr<MaskData>>(it->second)) {
401✔
843
            return DM_DataType::Mask;
1✔
844
        } else if (std::holds_alternative<std::shared_ptr<AnalogTimeSeries>>(it->second)) {
400✔
845
            return DM_DataType::Analog;
299✔
846
        } else if (std::holds_alternative<std::shared_ptr<DigitalEventSeries>>(it->second)) {
101✔
847
            return DM_DataType::DigitalEvent;
72✔
848
        } else if (std::holds_alternative<std::shared_ptr<DigitalIntervalSeries>>(it->second)) {
29✔
849
            return DM_DataType::DigitalInterval;
29✔
850
        } else if (std::holds_alternative<std::shared_ptr<TensorData>>(it->second)) {
×
851
            return DM_DataType::Tensor;
×
852
        }
853
        return DM_DataType::Unknown;
×
854
    }
855
    return DM_DataType::Unknown;
3✔
856
}
857

858
std::string convert_data_type_to_string(DM_DataType type) {
734✔
859
    switch (type) {
734✔
860
        case DM_DataType::Video:
81✔
861
            return "video";
243✔
862
        case DM_DataType::Images:
×
863
            return "images";
×
864
        case DM_DataType::Points:
128✔
865
            return "points";
384✔
866
        case DM_DataType::Mask:
×
867
            return "mask";
×
868
        case DM_DataType::Line:
117✔
869
            return "line";
351✔
870
        case DM_DataType::Analog:
218✔
871
            return "analog";
654✔
872
        case DM_DataType::DigitalEvent:
132✔
873
            return "digital_event";
396✔
874
        case DM_DataType::DigitalInterval:
58✔
875
            return "digital_interval";
174✔
876
        case DM_DataType::Tensor:
×
877
            return "tensor";
×
878
        case DM_DataType::Time:
×
879
            return "time";
×
880
        default:
×
881
            return "unknown";
×
882
    }
883
}
884

885
template std::shared_ptr<AnalogTimeSeries> DataManager::getData<AnalogTimeSeries>(std::string const & key);
886
template void DataManager::setData<AnalogTimeSeries>(std::string const & key, TimeKey const & time_key);
887
template void DataManager::setData<AnalogTimeSeries>(std::string const & key, std::shared_ptr<AnalogTimeSeries> data, TimeKey const & time_key);
888

889
template std::shared_ptr<DigitalEventSeries> DataManager::getData<DigitalEventSeries>(std::string const & key);
890
template void DataManager::setData<DigitalEventSeries>(std::string const & key, TimeKey const & time_key);
891
template void DataManager::setData<DigitalEventSeries>(std::string const & key, std::shared_ptr<DigitalEventSeries> data, TimeKey const & time_key);
892

893
template std::shared_ptr<DigitalIntervalSeries> DataManager::getData<DigitalIntervalSeries>(std::string const & key);
894
template void DataManager::setData<DigitalIntervalSeries>(std::string const & key, TimeKey const & time_key);
895
template void DataManager::setData<DigitalIntervalSeries>(std::string const & key, std::shared_ptr<DigitalIntervalSeries> data, TimeKey const & time_key);
896

897
template std::shared_ptr<LineData> DataManager::getData<LineData>(std::string const & key);
898
template void DataManager::setData<LineData>(std::string const & key, TimeKey const & time_key);
899
template void DataManager::setData<LineData>(std::string const & key, std::shared_ptr<LineData> data, TimeKey const & time_key);
900

901
template std::shared_ptr<MaskData> DataManager::getData<MaskData>(std::string const & key);
902
template void DataManager::setData<MaskData>(std::string const & key, TimeKey const & time_key);
903
template void DataManager::setData<MaskData>(std::string const & key, std::shared_ptr<MaskData> data, TimeKey const & time_key);
904

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

907
template std::shared_ptr<PointData> DataManager::getData<PointData>(std::string const & key);
908
template void DataManager::setData<PointData>(std::string const & key, TimeKey const & time_key);
909
template void DataManager::setData<PointData>(std::string const & key, std::shared_ptr<PointData> data, TimeKey const & time_key);
910

911
template std::shared_ptr<TensorData> DataManager::getData<TensorData>(std::string const & key);
912
template void DataManager::setData<TensorData>(std::string const & key, TimeKey const & time_key);
913
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