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

paulmthompson / WhiskerToolbox / 15685147280

16 Jun 2025 03:31PM UTC coverage: 67.52% (+0.9%) from 66.58%
15685147280

push

github

paulmthompson
remove some unnecessary analog time series functions

11 of 11 new or added lines in 1 file covered. (100.0%)

72 existing lines in 6 files now uncovered.

8860 of 13122 relevant lines covered (67.52%)

1182.4 hits per line

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

39.86
/src/WhiskerToolbox/DataManager/DataManager.cpp
1
#include "DataManager.hpp"
2
#include "AnalogTimeSeries/Analog_Time_Series.hpp"
3
#include "DigitalTimeSeries/Digital_Event_Series.hpp"
4
#include "DigitalTimeSeries/Digital_Interval_Series.hpp"
5
#include "Lines/Line_Data.hpp"
6
#include "Masks/Mask_Data.hpp"
7
#include "Media/Media_Data.hpp"
8
#include "Media/Video_Data.hpp"
9
#include "Points/Point_Data.hpp"
10
#include "Tensors/Tensor_Data.hpp"
11

12
#include "AnalogTimeSeries/IO/JSON/Analog_Time_Series_JSON.hpp"
13
#include "DigitalTimeSeries/IO/CSV/Digital_Interval_Series_CSV.hpp"
14
#include "DigitalTimeSeries/IO/JSON/Digital_Event_Series_JSON.hpp"
15
#include "DigitalTimeSeries/IO/JSON/Digital_Interval_Series_JSON.hpp"
16
#include "Lines/IO/JSON/Line_Data_JSON.hpp"
17
#include "Masks/IO/JSON/Mask_Data_JSON.hpp"
18
#include "Media/Video_Data_Loader.hpp"
19
#include "Points/IO/JSON/Point_Data_JSON.hpp"
20

21
#include "loaders/binary_loaders.hpp"
22
#include "transforms/Masks/mask_area.hpp"
23

24
#include "TimeFrame.hpp"
25
#include "TimeFrame/TimeFrameV2.hpp"
26

27
#include "nlohmann/json.hpp"
28
#include "utils/string_manip.hpp"
29

30
#include <filesystem>
31
#include <fstream>
32
#include <iostream>
33
#include <optional>
34
#include <regex>
35

36
using namespace nlohmann;
37

38
DataManager::DataManager() {
56✔
39
    _times["time"] = std::make_shared<TimeFrame>();
168✔
40
    _data["media"] = std::make_shared<MediaData>();
168✔
41

42
    setTimeFrame("media", "time");
280✔
43
    _output_path = std::filesystem::current_path();
56✔
44
}
56✔
45

46
bool DataManager::setTime(std::string const & key, std::shared_ptr<TimeFrame> timeframe, bool overwrite) {
28✔
47

48
    if (!timeframe) {
28✔
49
        std::cerr << "Error: Cannot register a nullptr TimeFrame for key: " << key << std::endl;
1✔
50
        return false;
1✔
51
    }
52

53
    if (_times.find(key) != _times.end()) {
27✔
54
        if (overwrite) {
2✔
55
            _times[key] = std::move(timeframe);
×
56
            return true;
×
57
        } else {
58
            std::cerr << "Error: Time key already exists in DataManager: " << key << std::endl;
2✔
59
            return false;
2✔
60
        }
61
    }
62

63
    _times[key] = std::move(timeframe);
25✔
64
    return true;
25✔
65
}
66

67
std::shared_ptr<TimeFrame> DataManager::getTime() {
1✔
68
    return _times["time"];
3✔
69
};
70

71
std::shared_ptr<TimeFrame> DataManager::getTime(std::string const & key) {
11✔
72
    if (_times.find(key) != _times.end()) {
11✔
73
        return _times[key];
9✔
74
    }
75
    return nullptr;
2✔
76
};
77

78
bool DataManager::removeTime(std::string const & key) {
×
79
    if (_times.find(key) == _times.end()) {
×
80
        std::cerr << "Error: could not find time key in DataManager: " << key << std::endl;
×
81
        return false;
×
82
    }
83

84
    auto it = _times.find(key);
×
85
    _times.erase(it);
×
86
    return true;
×
87
}
88

89
bool DataManager::setTimeFrame(std::string const & data_key, std::string const & time_key) {
104✔
90
    if (_data.find(data_key) == _data.end()) {
104✔
91
        std::cerr << "Error: Data key not found in DataManager: " << data_key << std::endl;
1✔
92
        return false;
1✔
93
    }
94

95
    if (_times.find(time_key) == _times.end()) {
103✔
96
        std::cerr << "Error: Time key not found in DataManager: " << time_key << std::endl;
1✔
97
        return false;
1✔
98
    }
99

100
    _time_frames[data_key] = time_key;
102✔
101
    return true;
102✔
102
}
103

104
std::string DataManager::getTimeFrame(std::string const & data_key) {
9✔
105
    // check if data_key exists
106
    if (_data.find(data_key) == _data.end()) {
9✔
107
        std::cerr << "Error: Data key not found in DataManager: " << data_key << std::endl;
1✔
108
        return "";
3✔
109
    }
110

111
    // check if data key has time frame
112
    if (_time_frames.find(data_key) == _time_frames.end()) {
8✔
113
        std::cerr << "Error: Data key "
114
                  << data_key
115
                  << " exists, but not assigned to a TimeFrame" << std::endl;
×
116
        return "";
×
117
    }
118

119
    return _time_frames[data_key];
8✔
120
}
121

122
std::vector<std::string> DataManager::getTimeFrameKeys() {
9✔
123
    std::vector<std::string> keys;
9✔
124
    keys.reserve(_times.size());
9✔
125
    for (auto const & [key, value]: _times) {
27✔
126

127
        keys.push_back(key);
18✔
128
    }
129
    return keys;
9✔
130
}
×
131

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

134
    int id = -1;
7✔
135

136
    if (_data.find(key) != _data.end()) {
7✔
137
        auto data = _data[key];
6✔
138

139
        id = std::visit([callback](auto & x) {
12✔
140
            return x.get()->addObserver(callback);
6✔
141
        },
142
                        data);
143
    }
6✔
144

145
    return id;
7✔
146
}
147

148
bool DataManager::removeCallbackFromData(std::string const & key, int callback_id) {
4✔
149
    if (_data.find(key) != _data.end()) {
4✔
150
        auto data = _data[key];
3✔
151

152
        std::visit([callback_id](auto & x) {
6✔
153
            x.get()->removeObserver(callback_id);
3✔
154
        },
3✔
155
                   data);
156

157
        return true;
3✔
158
    }
3✔
159

160
    return false;
1✔
161
}
162

163
void DataManager::addObserver(ObserverCallback callback) {
5✔
164
    _observers.push_back(std::move(callback));
5✔
165
}
5✔
166

167
void DataManager::_notifyObservers() {
50✔
168
    for (auto & observer: _observers) {
58✔
169
        observer();
8✔
170
    }
171
}
50✔
172

173
std::vector<std::string> DataManager::getAllKeys() {
5✔
174
    std::vector<std::string> keys;
5✔
175
    keys.reserve(_data.size());
5✔
176
    for (auto const & [key, value]: _data) {
17✔
177

178
        keys.push_back(key);
12✔
179
    }
180
    return keys;
5✔
181
}
×
182

183
std::optional<DataTypeVariant> DataManager::getDataVariant(std::string const & key) {
×
184
    if (_data.find(key) != _data.end()) {
×
185
        return _data[key];
×
186
    }
187
    return std::nullopt;
×
188
}
189

190
void DataManager::setData(std::string const & key, DataTypeVariant data) {
1✔
191
    _data[key] = data;
1✔
192
    setTimeFrame(key, "time");
3✔
193
    _notifyObservers();
1✔
194
}
1✔
195

196
std::optional<std::string> processFilePath(
×
197
        std::string const & file_path,
198
        std::filesystem::path const & base_path) {
199
    std::filesystem::path full_path = file_path;
×
200

201
    // Check for wildcard character
202
    if (file_path.find('*') != std::string::npos) {
×
203
        // Convert wildcard pattern to regex
204
        std::string const pattern = std::regex_replace(full_path.string(), std::regex("\\*"), ".*");
×
205
        std::regex const regex_pattern(pattern);
×
206

207
        // Iterate through the directory to find matching files
208
        for (auto const & entry: std::filesystem::directory_iterator(base_path)) {
×
209
            std::cout << "Checking " << entry.path().string() << " with full path " << full_path << std::endl;
×
210
            if (std::regex_match(entry.path().string(), regex_pattern)) {
×
211
                std::cout << "Loading file " << entry.path().string() << std::endl;
×
212
                return entry.path().string();
×
213
            }
214
        }
×
215
        return std::nullopt;
×
216
    } else {
×
217
        // Check if the file path is relative
218
        if (!std::filesystem::path(file_path).is_absolute()) {
×
219
            full_path = base_path / file_path;
×
220
        }
221
        // Check for the presence of the file
222
        if (std::filesystem::exists(full_path)) {
×
223
            std::cout << "Loading file " << full_path.string() << std::endl;
×
224
            return full_path.string();
×
225
        } else {
226
            return std::nullopt;
×
227
        }
228
    }
229
}
×
230

231
bool checkRequiredFields(json const & item, std::vector<std::string> const & requiredFields) {
×
232
    for (auto const & field: requiredFields) {
×
233
        if (!item.contains(field)) {
×
234
            std::cerr << "Error: Missing required field \"" << field << "\" in JSON item." << std::endl;
×
235
            return false;
×
236
        }
237
    }
238
    return true;
×
239
}
240

241
void checkOptionalFields(json const & item, std::vector<std::string> const & optionalFields) {
×
242
    for (auto const & field: optionalFields) {
×
243
        if (!item.contains(field)) {
×
244
            std::cout << "Warning: Optional field \"" << field << "\" is missing in JSON item." << std::endl;
×
245
        }
246
    }
247
}
×
248

249
DM_DataType stringToDataType(std::string const & data_type_str) {
×
250
    if (data_type_str == "video") return DM_DataType::Video;
×
251
    if (data_type_str == "points") return DM_DataType::Points;
×
252
    if (data_type_str == "mask") return DM_DataType::Mask;
×
253
    if (data_type_str == "line") return DM_DataType::Line;
×
254
    if (data_type_str == "analog") return DM_DataType::Analog;
×
255
    if (data_type_str == "digital_event") return DM_DataType::DigitalEvent;
×
256
    if (data_type_str == "digital_interval") return DM_DataType::DigitalInterval;
×
257
    if (data_type_str == "tensor") return DM_DataType::Tensor;
×
258
    if (data_type_str == "time") return DM_DataType::Time;
×
259
    return DM_DataType::Unknown;
×
260
}
261

262
std::vector<DataInfo> load_data_from_json_config(DataManager * dm, std::string const & json_filepath) {
×
263
    std::vector<DataInfo> data_info_list;
×
264
    // Open JSON file
265
    std::ifstream ifs(json_filepath);
×
266
    if (!ifs.is_open()) {
×
267
        std::cerr << "Failed to open JSON file: " << json_filepath << std::endl;
×
268
        return data_info_list;
×
269
    }
270

271
    // Parse JSON
272
    json j;
×
273
    ifs >> j;
×
274

275
    // get base path of filepath
276
    std::filesystem::path const base_path = std::filesystem::path(json_filepath).parent_path();
×
277

278
    // Iterate through JSON array
279
    for (auto const & item: j) {
×
280

281
        if (!checkRequiredFields(item, {"data_type", "name", "filepath"})) {
×
282
            continue;// Exit if any required field is missing
×
283
        }
284

285
        std::string const data_type_str = item["data_type"];
×
286
        auto const data_type = stringToDataType(data_type_str);
×
287
        if (data_type == DM_DataType::Unknown) {
×
288
            std::cout << "Unknown data type: " << data_type_str << std::endl;
×
289
            continue;
×
290
        }
291

292
        std::string const name = item["name"];
×
293

294
        auto file_exists = processFilePath(item["filepath"], base_path);
×
295
        if (!file_exists) {
×
296
            std::cerr << "File does not exist: " << item["filepath"] << std::endl;
×
297
            continue;
×
298
        }
299

300
        std::string const file_path = file_exists.value();
×
301

302
        switch (data_type) {
×
303
            case DM_DataType::Video: {
×
304

305
                auto video_data = load_video_into_VideoData(file_path);
×
306
                dm->setData<VideoData>("media", video_data);
×
307

308
                data_info_list.push_back({name, "VideoData", ""});
×
309
                break;
×
310
            }
×
311
            case DM_DataType::Points: {
×
312

313
                auto point_data = load_into_PointData(file_path, item);
×
314

315
                dm->setData<PointData>(name, point_data);
×
316

317
                std::string const color = item.value("color", "#0000FF");
×
318
                data_info_list.push_back({name, "PointData", color});
×
319
                break;
×
320
            }
×
321
            case DM_DataType::Mask: {
×
322

323
                auto mask_data = load_into_MaskData(file_path, item);
×
324

325
                std::string const color = item.value("color", "0000FF");
×
326
                dm->setData<MaskData>(name, mask_data);
×
327

328
                data_info_list.push_back({name, "MaskData", color});
×
329

330
                if (item.contains("operations")) {
×
331

332
                    for (auto const & operation: item["operations"]) {
×
333

334
                        std::string const operation_type = operation["type"];
×
335

336
                        if (operation_type == "area") {
×
337
                            std::cout << "Calculating area for mask: " << name << std::endl;
×
338
                            auto area_data = area(dm->getData<MaskData>(name).get());
×
339
                            std::string const output_name = name + "_area";
×
340
                            dm->setData<AnalogTimeSeries>(output_name, area_data);
×
341
                        }
×
342
                    }
×
343
                }
344
                break;
×
345
            }
×
346
            case DM_DataType::Line: {
×
347

348
                auto line_data = load_into_LineData(file_path, item);
×
349

350
                dm->setData<LineData>(name, line_data);
×
351

352
                std::string const color = item.value("color", "0000FF");
×
353

354
                data_info_list.push_back({name, "LineData", color});
×
355

356
                break;
×
357
            }
×
358
            case DM_DataType::Analog: {
×
359

360
                auto analog_time_series = load_into_AnalogTimeSeries(file_path, item);
×
361

362
                for (int channel = 0; channel < analog_time_series.size(); channel++) {
×
363
                    std::string const channel_name = name + "_" + std::to_string(channel);
×
364

365
                    dm->setData<AnalogTimeSeries>(channel_name, analog_time_series[channel]);
×
366

367
                    if (item.contains("clock")) {
×
368
                        std::string const clock = item["clock"];
×
369
                        dm->setTimeFrame(channel_name, clock);
×
370
                    }
×
371
                }
×
372
                break;
×
373
            }
×
374
            case DM_DataType::DigitalEvent: {
×
375

376
                auto digital_event_series = load_into_DigitalEventSeries(file_path, item);
×
377

378
                for (int channel = 0; channel < digital_event_series.size(); channel++) {
×
379
                    std::string const channel_name = name + "_" + std::to_string(channel);
×
380

381
                    dm->setData<DigitalEventSeries>(channel_name, digital_event_series[channel]);
×
382

383
                    if (item.contains("clock")) {
×
384
                        std::string const clock = item["clock"];
×
385
                        dm->setTimeFrame(channel_name, clock);
×
386
                    }
×
387
                }
×
388
                break;
×
389
            }
×
390
            case DM_DataType::DigitalInterval: {
×
391

392
                auto digital_interval_series = load_into_DigitalIntervalSeries(file_path, item);
×
393
                dm->setData<DigitalIntervalSeries>(name, digital_interval_series);
×
394

395
                break;
×
396
            }
×
397
            case DM_DataType::Tensor: {
×
398

399
                if (item["format"] == "numpy") {
×
400

401
                    TensorData tensor_data;
×
402
                    loadNpyToTensorData(file_path, tensor_data);
×
403

404
                    dm->setData<TensorData>(name, std::make_shared<TensorData>(tensor_data));
×
405

406
                } else {
×
407
                    std::cout << "Format " << item["format"] << " not found for " << name << std::endl;
×
408
                }
409
                break;
×
410
            }
411
            case DM_DataType::Time: {
×
412

413
                if (item["format"] == "uint16") {
×
414

415
                    int const channel = item["channel"];
×
416
                    std::string const transition = item["transition"];
×
417

418
                    int const header_size = item.value("header_size", 0);
×
419

420
                    auto opts = Loader::BinaryAnalogOptions{.file_path = file_path,
×
421
                                                            .header_size_bytes = static_cast<size_t>(header_size)};
×
422
                    auto data = Loader::readBinaryFile<uint16_t>(opts);
×
423

424
                    auto digital_data = Loader::extractDigitalData(data, channel);
×
425
                    auto events = Loader::extractEvents(digital_data, transition);
×
426

427
                    // convert to int with std::transform
428
                    std::vector<int> events_int;
×
429
                    events_int.reserve(events.size());
×
430
                    for (auto e: events) {
×
431
                        events_int.push_back(static_cast<int>(e));
×
432
                    }
433
                    std::cout << "Loaded " << events_int.size() << " events for " << name << std::endl;
×
434

435
                    auto timeframe = std::make_shared<TimeFrame>(events_int);
×
436
                    dm->setTime(name, timeframe, true);
×
437
                }
×
438

439
                if (item["format"] == "uint16_length") {
×
440

441
                    int const header_size = item.value("header_size", 0);
×
442

443
                    auto opts = Loader::BinaryAnalogOptions{.file_path = file_path,
×
444
                                                            .header_size_bytes = static_cast<size_t>(header_size)};
×
445
                    auto data = Loader::readBinaryFile<uint16_t>(opts);
×
446

447
                    std::vector<int> t(data.size());
×
448
                    std::iota(std::begin(t), std::end(t), 0);
×
449

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

452
                    auto timeframe = std::make_shared<TimeFrame>(t);
×
453
                    dm->setTime(name, timeframe, true);
×
454
                }
×
455
                break;
×
456
            }
457
            default:
×
458
                std::cout << "Unsupported data type: " << data_type_str << std::endl;
×
459
                continue;
×
460
        }
×
461
        if (item.contains("clock")) {
×
462
            std::string const clock = item["clock"];
×
463
            std::cout << "Setting time for " << name << " to " << clock << std::endl;
×
464
            dm->setTimeFrame(name, clock);
×
465
        }
×
466
    }
×
467

468
    return data_info_list;
469
}
×
470

471
DM_DataType DataManager::getType(std::string const & key) const {
×
472
    auto it = _data.find(key);
×
473
    if (it != _data.end()) {
×
474
        if (std::holds_alternative<std::shared_ptr<MediaData>>(it->second)) {
×
475
            return DM_DataType::Video;
×
476
        } else if (std::holds_alternative<std::shared_ptr<PointData>>(it->second)) {
×
477
            return DM_DataType::Points;
×
478
        } else if (std::holds_alternative<std::shared_ptr<LineData>>(it->second)) {
×
479
            return DM_DataType::Line;
×
480
        } else if (std::holds_alternative<std::shared_ptr<MaskData>>(it->second)) {
×
481
            return DM_DataType::Mask;
×
482
        } else if (std::holds_alternative<std::shared_ptr<AnalogTimeSeries>>(it->second)) {
×
483
            return DM_DataType::Analog;
×
484
        } else if (std::holds_alternative<std::shared_ptr<DigitalEventSeries>>(it->second)) {
×
485
            return DM_DataType::DigitalEvent;
×
486
        } else if (std::holds_alternative<std::shared_ptr<DigitalIntervalSeries>>(it->second)) {
×
487
            return DM_DataType::DigitalInterval;
×
488
        } else if (std::holds_alternative<std::shared_ptr<TensorData>>(it->second)) {
×
489
            return DM_DataType::Tensor;
×
490
        }
491
        return DM_DataType::Unknown;
×
492
    }
493
    return DM_DataType::Unknown;
×
494
}
495

496
// ========== TimeFrameV2 Implementation ==========
497

498
bool DataManager::setTimeV2(std::string const & key, AnyTimeFrame timeframe, bool overwrite) {
18✔
499
    if (_times_v2.find(key) != _times_v2.end()) {
18✔
500
        if (overwrite) {
6✔
501
            _times_v2[key] = std::move(timeframe);
6✔
502
            return true;
6✔
503
        } else {
504
            std::cerr << "Error: TimeFrameV2 key already exists in DataManager: " << key << std::endl;
×
505
            return false;
×
506
        }
507
    }
508

509
    _times_v2[key] = std::move(timeframe);
12✔
510
    return true;
12✔
511
}
512

513
std::optional<AnyTimeFrame> DataManager::getTimeV2(std::string const & key) {
15✔
514
    if (_times_v2.find(key) != _times_v2.end()) {
15✔
515
        return _times_v2[key];
12✔
516
    }
517
    return std::nullopt;
3✔
518
}
519

520
bool DataManager::removeTimeV2(std::string const & key) {
2✔
521
    if (_times_v2.find(key) == _times_v2.end()) {
2✔
522
        std::cerr << "Error: could not find TimeFrameV2 key in DataManager: " << key << std::endl;
1✔
523
        return false;
1✔
524
    }
525

526
    auto it = _times_v2.find(key);
1✔
527
    _times_v2.erase(it);
1✔
528
    return true;
1✔
529
}
530

531
std::vector<std::string> DataManager::getTimeFrameV2Keys() {
3✔
532
    std::vector<std::string> keys;
3✔
533
    keys.reserve(_times_v2.size());
3✔
534
    for (auto const & [key, value]: _times_v2) {
7✔
535
        keys.push_back(key);
4✔
536
    }
537
    return keys;
3✔
538
}
×
539

540
bool DataManager::createClockTimeFrame(std::string const & key, int64_t start_tick,
4✔
541
                                       int64_t num_samples, double sampling_rate_hz,
542
                                       bool overwrite) {
543
    auto clock_frame = TimeFrameUtils::createDenseClockTimeFrame(start_tick, num_samples, sampling_rate_hz);
4✔
544
    AnyTimeFrame any_frame = clock_frame;
4✔
545
    return setTimeV2(key, std::move(any_frame), overwrite);
8✔
546
}
4✔
547

548
bool DataManager::createCameraTimeFrame(std::string const & key, std::vector<int64_t> frame_indices,
4✔
549
                                        bool overwrite) {
550
    auto camera_frame = TimeFrameUtils::createSparseCameraTimeFrame(std::move(frame_indices));
4✔
551
    AnyTimeFrame any_frame = camera_frame;
4✔
552
    return setTimeV2(key, std::move(any_frame), overwrite);
8✔
553
}
4✔
554

555
bool DataManager::createDenseCameraTimeFrame(std::string const & key, int64_t start_frame,
1✔
556
                                             int64_t num_frames, bool overwrite) {
557
    auto camera_frame = TimeFrameUtils::createDenseCameraTimeFrame(start_frame, num_frames);
1✔
558
    AnyTimeFrame any_frame = camera_frame;
1✔
559
    return setTimeV2(key, std::move(any_frame), overwrite);
2✔
560
}
1✔
561

562
std::string convert_data_type_to_string(DM_DataType type) {
×
563
    switch (type) {
×
564
        case DM_DataType::Video:
×
565
            return "video";
×
566
        case DM_DataType::Points:
×
567
            return "points";
×
568
        case DM_DataType::Mask:
×
569
            return "mask";
×
570
        case DM_DataType::Line:
×
571
            return "line";
×
572
        case DM_DataType::Analog:
×
573
            return "analog";
×
574
        case DM_DataType::DigitalEvent:
×
575
            return "digital_event";
×
576
        case DM_DataType::DigitalInterval:
×
577
            return "digital_interval";
×
578
        case DM_DataType::Tensor:
×
579
            return "tensor";
×
580
        case DM_DataType::Time:
×
581
            return "time";
×
582
        default:
×
583
            return "unknown";
×
584
    }
585
}
586

587
// ========== Enhanced AnalogTimeSeries Support Implementation ==========
588

589
bool DataManager::createAnalogTimeSeriesWithClock(std::string const & data_key,
3✔
590
                                                  std::string const & timeframe_key,
591
                                                  std::vector<float> analog_data,
592
                                                  int64_t start_tick,
593
                                                  double sampling_rate_hz,
594
                                                  bool overwrite) {
595
    // Create the clock timeframe
596
    if (!createClockTimeFrame(timeframe_key, start_tick, 
3✔
597
                             static_cast<int64_t>(analog_data.size()), 
3✔
598
                             sampling_rate_hz, overwrite)) {
599
        return false;
×
600
    }
601

602
    // Get the timeframe
603
    auto timeframe_opt = getTimeV2(timeframe_key);
3✔
604
    if (!timeframe_opt.has_value()) {
3✔
605
        return false;
×
606
    }
607

608
    // Create time vector with the actual tick values
609
    std::vector<TimeFrameIndex> time_vector;
3✔
610
    time_vector.reserve(analog_data.size());
3✔
611
    for (size_t i = 0; i < analog_data.size(); ++i) {
31,007✔
612
        time_vector.push_back(TimeFrameIndex(start_tick + static_cast<int64_t>(i)));
31,004✔
613
    }
614

615
    // Create the analog series
616
    auto series = std::make_shared<AnalogTimeSeries>(std::move(analog_data), std::move(time_vector));
3✔
617
    setDataV2(data_key, series, timeframe_opt.value(), timeframe_key);
3✔
618
    return true;
3✔
619
}
3✔
620

621
bool DataManager::createAnalogTimeSeriesWithCamera(std::string const & data_key,
2✔
622
                                                   std::string const & timeframe_key,
623
                                                   std::vector<float> analog_data,
624
                                                   std::vector<int64_t> frame_indices,
625
                                                   bool overwrite) {
626
    if (analog_data.size() != frame_indices.size()) {
2✔
627
        std::cerr << "Error: analog data and frame indices must have same size" << std::endl;
×
628
        return false;
×
629
    }
630

631
    // Save frame_indices before they're moved
632
    std::vector<int64_t> frame_indices_copy = frame_indices;
2✔
633

634
    // Create the camera timeframe
635
    if (!createCameraTimeFrame(timeframe_key, std::move(frame_indices), overwrite)) {
2✔
636
        return false;
×
637
    }
638

639
    // Get the timeframe
640
    auto timeframe_opt = getTimeV2(timeframe_key);
2✔
641
    if (!timeframe_opt.has_value()) {
2✔
642
        return false;
×
643
    }
644

645
    // Create time vector with the actual frame indices
646
    std::vector<TimeFrameIndex> time_vector;
2✔
647
    time_vector.reserve(analog_data.size());
2✔
648
    for (int64_t frame_idx : frame_indices_copy) {
107✔
649
        time_vector.push_back(TimeFrameIndex(frame_idx));
105✔
650
    }
651

652
    // Create the analog series
653
    auto series = std::make_shared<AnalogTimeSeries>(std::move(analog_data), std::move(time_vector));
2✔
654
    setDataV2(data_key, series, timeframe_opt.value(), timeframe_key);
2✔
655
    return true;
2✔
656
}
2✔
657

658
bool DataManager::createAnalogTimeSeriesWithDenseCamera(std::string const & data_key,
1✔
659
                                                        std::string const & timeframe_key,
660
                                                        std::vector<float> analog_data,
661
                                                        int64_t start_frame,
662
                                                        bool overwrite) {
663
    // Create the dense camera timeframe
664
    if (!createDenseCameraTimeFrame(timeframe_key, start_frame, 
1✔
665
                                   static_cast<int64_t>(analog_data.size()), overwrite)) {
1✔
666
        return false;
×
667
    }
668

669
    // Get the timeframe
670
    auto timeframe_opt = getTimeV2(timeframe_key);
1✔
671
    if (!timeframe_opt.has_value()) {
1✔
672
        return false;
×
673
    }
674

675
    // Create time vector with the actual frame indices
676
    std::vector<TimeFrameIndex> time_vector;
1✔
677
    time_vector.reserve(analog_data.size());
1✔
678
    for (size_t i = 0; i < analog_data.size(); ++i) {
7✔
679
        time_vector.push_back(TimeFrameIndex(start_frame + static_cast<int64_t>(i)));
6✔
680
    }
681

682
    // Create the analog series
683
    auto series = std::make_shared<AnalogTimeSeries>(std::move(analog_data), std::move(time_vector));
1✔
684
    setDataV2(data_key, series, timeframe_opt.value(), timeframe_key);
1✔
685
    return true;
1✔
686
}
1✔
687

688
std::string DataManager::getAnalogCoordinateType(std::string const & data_key) {
5✔
689
    // Check if the data exists and is an AnalogTimeSeries
690
    if (_data.find(data_key) == _data.end()) {
5✔
UNCOV
691
        return "not_found";
×
692
    }
693

694
    if (!std::holds_alternative<std::shared_ptr<AnalogTimeSeries>>(_data[data_key])) {
5✔
UNCOV
695
        return "not_analog_timeseries";
×
696
    }
697

698
    auto series = std::get<std::shared_ptr<AnalogTimeSeries>>(_data[data_key]);
5✔
699
    if (!series) {
5✔
UNCOV
700
        return "null_series";
×
701
    }
702

703
    return series->getCoordinateType();
5✔
704
}
5✔
705

706
bool DataManager::analogUsesCoordinateTypeImpl(std::string const & data_key, std::string const & type_name) {
3✔
707
    // Check if the data exists and is an AnalogTimeSeries
708
    if (_data.find(data_key) == _data.end()) {
3✔
UNCOV
709
        return false;
×
710
    }
711

712
    if (!std::holds_alternative<std::shared_ptr<AnalogTimeSeries>>(_data[data_key])) {
3✔
UNCOV
713
        return false;
×
714
    }
715

716
    auto series = std::get<std::shared_ptr<AnalogTimeSeries>>(_data[data_key]);
3✔
717
    if (!series) {
3✔
UNCOV
718
        return false;
×
719
    }
720

721
    return series->getCoordinateType() == type_name;
3✔
722
}
3✔
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