/* * Copyright (C) 2020 James Turner * * This file is part of the program FlightGear. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "TestDataLogger.hxx" #include #include #include #include #include #include "Main/globals.hxx" namespace FGTestApi { using DoubleVec = std::vector; static std::unique_ptr static_instance; class DataLogger::DataLoggerPrivate { public: sg_ofstream _stream; struct SampleInfo { int column; std::string name; // range / units info, later SGPropertyNode_ptr property; }; double _currentTimeBase; std::vector _samples; DoubleVec _openRow; bool _didHeader = false; void writeCurrentRow() { if (!_didHeader) { writeHeader(); _didHeader = true; } // capture property values into the open row data std::for_each(_samples.begin(), _samples.end(), [this](const SampleInfo& info) { if (info.property) { _openRow[info.column] = info.property->getDoubleValue(); } }); // write time base _stream << globals->get_sim_time_sec() << ","; for (const auto v : _openRow) { if (std::isnan(v)) { _stream << ","; // skip this data point } else { _stream << v << ","; } } _stream << "\n"; std::fill(_openRow.begin(), _openRow.end(), std::numeric_limits::quiet_NaN()); } void writeHeader() { _stream << "sim-time, "; std::for_each(_samples.begin(), _samples.end(), [this](const SampleInfo& info) { _stream << info.name << ", "; }); _stream << "\n"; } }; DataLogger::DataLogger() { d.reset(new DataLoggerPrivate); } DataLogger::~DataLogger() { d->_stream.close(); } bool DataLogger::isActive() { return static_instance != nullptr; } DataLogger* DataLogger::instance() { if (!static_instance) { static_instance.reset(new DataLogger); } return static_instance.get(); } void DataLogger::initTest(const std::string& testName) { d->_stream = sg_ofstream(testName + "_trace.csv"); } void DataLogger::tearDown() { if (static_instance) { static_instance.reset(); } } void DataLogger::writeRecord() { d->writeCurrentRow(); } void DataLogger::recordProperty(const std::string& name, SGPropertyNode_ptr prop) { int index = static_cast(d->_samples.size()); DataLoggerPrivate::SampleInfo info{index, name, prop}; d->_samples.push_back(info); if (d->_openRow.size() <= index) { d->_openRow.resize(index + 1, std::numeric_limits::quiet_NaN()); } } void DataLogger::recordSamplePoint(const std::string& name, double value) { auto it = std::find_if(d->_samples.begin(), d->_samples.end(), [&name](const DataLoggerPrivate::SampleInfo& sample) { return name == sample.name; }); int index = 0; if (it == d->_samples.end()) { index = static_cast(d->_samples.size()); DataLoggerPrivate::SampleInfo info{index, name}; d->_samples.push_back(info); } else { index = it->column; } // grow _openRow as required if (d->_openRow.size() <= index) { d->_openRow.resize(index + 1, std::numeric_limits::quiet_NaN()); } d->_openRow[index] = value; } } // namespace FGTestApi