/* * Copyright (C) 2012 Open Source Robotics Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #ifdef _WIN32 // Ensure that Winsock2.h is included before Windows.h, which can get // pulled in by anybody (e.g., Boost). #include #include // Seems like W_OK does not exists on Windows. // Reading access function documentation, the value should be 2 // http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx // Test for write permission. #define W_OK 2 #define access _access #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gazebo/common/CommonIface.hh" #include "gazebo/common/Assert.hh" #include "gazebo/common/Base64.hh" #include "gazebo/common/Console.hh" #include "gazebo/common/Events.hh" #include "gazebo/common/Exception.hh" #include "gazebo/common/Time.hh" #include "gazebo/common/SystemPaths.hh" #include "gazebo/gazebo_config.h" #include "gazebo/transport/transport.hh" #include "gazebo/util/LogRecordPrivate.hh" #include "gazebo/util/LogRecord.hh" using namespace gazebo; using namespace util; ////////////////////////////////////////////////// LogRecord::LogRecord() : dataPtr(new LogRecordPrivate) { this->dataPtr->pauseState = false; this->dataPtr->running = false; this->dataPtr->paused = false; this->dataPtr->initialized = false; this->dataPtr->stopThread = false; this->dataPtr->firstUpdate = true; this->dataPtr->readyToStart = false; // Get the user's home directory #ifndef _WIN32 const char *homePath = common::getEnv("HOME"); #else const char *homePath = common::getEnv("HOMEPATH"); #endif GZ_ASSERT(homePath, "HOME environment variable is missing"); if (!homePath) { common::SystemPaths *paths = common::SystemPaths::Instance(); this->dataPtr->logBasePath = paths->GetTmpPath() + "/gazebo"; } else { this->dataPtr->logBasePath = boost::filesystem::path(homePath); } this->dataPtr->logBasePath /= "/.gazebo/log/"; this->dataPtr->logsEnd = this->dataPtr->logs.end(); this->dataPtr->connections.push_back( event::Events::ConnectPause( std::bind(&LogRecord::OnPause, this, std::placeholders::_1))); } ////////////////////////////////////////////////// void LogRecord::OnPause(const bool _pause) { this->dataPtr->pauseState = _pause; } ////////////////////////////////////////////////// LogRecord::~LogRecord() { // Stop the write thread. this->Fini(); } ////////////////////////////////////////////////// bool LogRecord::Init(const std::string &_subdir) { if (_subdir.empty()) { gzerr << "LogRecord initialization directory is empty." << std::endl; return false; } this->dataPtr->logSubDir = _subdir; this->ClearLogs(); this->dataPtr->initialized = true; this->dataPtr->running = false; this->dataPtr->paused = false; this->dataPtr->stopThread = false; this->dataPtr->firstUpdate = true; this->dataPtr->readyToStart = true; return true; } ////////////////////////////////////////////////// bool LogRecord::Start(const LogRecordParams &_params) { this->dataPtr->period = _params.period; this->dataPtr->filter = _params.filter; return this->Start(_params.encoding, _params.path); } ////////////////////////////////////////////////// bool LogRecord::Start(const std::string &_encoding, const std::string &_path) { std::unique_lock lock(this->dataPtr->controlMutex); // Make sure ::Init has been called. if (!this->dataPtr->initialized) { gzerr << "LogRecord has not been initialized." << std::endl; return false; } // Check to see if the logger is already started. if (this->dataPtr->running) { /// \TODO replace this with gzlog gzerr << "LogRecord has already been started" << std::endl; return false; } // Get the current time as an ISO string. std::string logTimeDir = common::Time::GetWallTimeAsISOString(); // remove ":" if the dir is to be added to env path, e.g. for playback // with resources logTimeDir = common::replaceAll(logTimeDir, ":", ""); // Override the default path settings if the _path parameter is set. if (!_path.empty()) { this->dataPtr->logBasePath = boost::filesystem::path(_path); this->dataPtr->logCompletePath = this->dataPtr->logBasePath; } else { this->dataPtr->logCompletePath = this->dataPtr->logBasePath / logTimeDir / this->dataPtr->logSubDir; } // Create the log directory if necessary if (!boost::filesystem::exists(this->dataPtr->logCompletePath)) boost::filesystem::create_directories(this->dataPtr->logCompletePath); if (_encoding != "bz2" && _encoding != "txt" && _encoding != "zlib") gzthrow("Invalid log encoding[" + _encoding + "]. Must be one of [bz2, zlib, txt]"); this->dataPtr->encoding = _encoding; { std::unique_lock logLock(this->dataPtr->writeMutex); this->dataPtr->logsEnd = this->dataPtr->logs.end(); // Start all the logs for (LogRecordPrivate::Log_M::iterator iter = this->dataPtr->logs.begin(); iter != this->dataPtr->logsEnd; ++iter) iter->second->Start(this->dataPtr->logCompletePath); } this->dataPtr->running = true; this->dataPtr->paused = false; this->dataPtr->firstUpdate = true; this->dataPtr->stopThread = false; this->dataPtr->readyToStart = false; this->dataPtr->startTime = this->dataPtr->currTime = common::Time(); // Create a thread to cleanup recording. this->dataPtr->cleanupThread.reset(new std::thread( std::bind(&LogRecord::Cleanup, this))); // Wait for thread to start this->dataPtr->startThreadCondition.wait(lock); // Start the update thread if it has not already been started if (!this->dataPtr->updateThread) { std::unique_lock updateLock(this->dataPtr->updateMutex); this->dataPtr->updateThread.reset(new std::thread( std::bind(&LogRecord::RunUpdate, this))); this->dataPtr->startThreadCondition.wait(updateLock); } // Start the writing thread if it has not already been started if (!this->dataPtr->writeThread) { std::unique_lock writeLock(this->dataPtr->runWriteMutex); this->dataPtr->writeThread.reset(new std::thread( std::bind(&LogRecord::RunWrite, this))); this->dataPtr->startThreadCondition.wait(writeLock); } return true; } ////////////////////////////////////////////////// const std::string &LogRecord::GetEncoding() const { return this->Encoding(); } ////////////////////////////////////////////////// const std::string &LogRecord::Encoding() const { return this->dataPtr->encoding; } ////////////////////////////////////////////////// void LogRecord::Fini() { { std::unique_lock lock(this->dataPtr->controlMutex); this->dataPtr->cleanupCondition.notify_all(); } if (this->dataPtr->cleanupThread && this->dataPtr->cleanupThread->joinable()) this->dataPtr->cleanupThread->join(); this->dataPtr->cleanupThread.reset(); std::lock_guard lock(this->dataPtr->controlMutex); this->dataPtr->connections.clear(); // Remove all the logs. this->ClearLogs(); this->dataPtr->logControlSub.reset(); this->dataPtr->logStatusPub.reset(); this->dataPtr->node.reset(); } ////////////////////////////////////////////////// void LogRecord::Stop() { this->dataPtr->running = false; this->dataPtr->cleanupCondition.notify_all(); if (this->dataPtr->cleanupThread && this->dataPtr->cleanupThread->joinable()) this->dataPtr->cleanupThread->join(); this->dataPtr->cleanupThread.reset(); this->dataPtr->savedModels.clear(); this->dataPtr->savedFiles.clear(); } ////////////////////////////////////////////////// void LogRecord::ClearLogs() { std::lock_guard logLock(this->dataPtr->writeMutex); // Delete all the log objects for (LogRecordPrivate::Log_M::iterator iter = this->dataPtr->logs.begin(); iter != this->dataPtr->logs.end(); ++iter) { delete iter->second; } this->dataPtr->logs.clear(); this->dataPtr->logsEnd = this->dataPtr->logs.end(); } ////////////////////////////////////////////////// void LogRecord::SetPaused(const bool _paused) { this->dataPtr->paused = _paused; } ////////////////////////////////////////////////// bool LogRecord::GetPaused() const { return this->Paused(); } ////////////////////////////////////////////////// bool LogRecord::Paused() const { return this->dataPtr->paused; } ////////////////////////////////////////////////// double LogRecord::Period() const { return this->dataPtr->period; } ////////////////////////////////////////////////// void LogRecord::SetPeriod(const double _period) { this->dataPtr->period = _period; } ////////////////////////////////////////////////// std::string LogRecord::Filter() const { return this->dataPtr->filter; } ////////////////////////////////////////////////// void LogRecord::SetFilter(const std::string &_filter) { this->dataPtr->filter = _filter; } ////////////////////////////////////////////////// bool LogRecord::GetRunning() const { return this->Running(); } ////////////////////////////////////////////////// bool LogRecord::Running() const { return this->dataPtr->running; } ////////////////////////////////////////////////// bool LogRecord::RecordResources() const { return this->dataPtr->recordResources; } ////////////////////////////////////////////////// void LogRecord::SetRecordResources(const bool _record) { this->dataPtr->recordResources = _record; } ////////////////////////////////////////////////// void LogRecord::Add(const std::string &_name, const std::string &_filename, std::function _logCallback) { std::lock_guard logLock(this->dataPtr->writeMutex); // Check to see if the log has already been added. if (this->dataPtr->logs.find(_name) != this->dataPtr->logs.end()) { GZ_ASSERT(this->dataPtr->logs.find(_name)->second != NULL, "Unable to find log"); if (this->dataPtr->logs.find(_name)->second->RelativeFilename() != _filename) { gzerr << "Attempting to add a duplicate log object named[" << _name << "] with a filename of [" << _filename << "].\n"; return; } else { return; } } LogRecordPrivate::Log *newLog; // Create a new log object try { newLog = new LogRecordPrivate::Log(this, _filename, _logCallback); } catch(...) { gzthrow("Unable to create log. File permissions are probably bad."); } if (this->dataPtr->running) newLog->Start(this->dataPtr->logCompletePath); // Add the log to our map this->dataPtr->logs[_name] = newLog; // Update the pointer to the end of the log objects list. this->dataPtr->logsEnd = this->dataPtr->logs.end(); if (!this->dataPtr->node) { this->dataPtr->node = transport::NodePtr(new transport::Node()); this->dataPtr->node->Init(); this->dataPtr->logControlSub = this->dataPtr->node->Subscribe("~/log/control", &LogRecord::OnLogControl, this); this->dataPtr->logStatusPub = this->dataPtr->node->Advertise("~/log/status"); } } ////////////////////////////////////////////////// bool LogRecord::Remove(const std::string &_name) { std::lock_guard logLock(this->dataPtr->writeMutex); bool result = false; LogRecordPrivate::Log_M::iterator iter = this->dataPtr->logs.find(_name); if (iter != this->dataPtr->logs.end()) { delete iter->second; this->dataPtr->logs.erase(iter); // Update the pointer to the end of the log objects list. this->dataPtr->logsEnd = this->dataPtr->logs.end(); result = true; } return result; } ////////////////////////////////////////////////// std::string LogRecord::GetFilename(const std::string &_name) const { return this->Filename(_name); } ////////////////////////////////////////////////// std::string LogRecord::Filename(const std::string &_name) const { std::lock_guard logLock(this->dataPtr->writeMutex); std::string result; LogRecordPrivate::Log_M::const_iterator iter = this->dataPtr->logs.find(_name); if (iter != this->dataPtr->logs.end()) { GZ_ASSERT(iter->second, "Invalid log"); result = iter->second->CompleteFilename(); } else result = this->dataPtr->logs.begin()->second->CompleteFilename(); return result; } ////////////////////////////////////////////////// unsigned int LogRecord::GetFileSize(const std::string &_name) const { return this->FileSize(_name); } ////////////////////////////////////////////////// unsigned int LogRecord::FileSize(const std::string &_name) const { unsigned int result = 0; // Get the filename of the specified log object; std::string filename = this->Filename(_name); // Get the size of the log file on disk. if (!filename.empty()) { // Get the size of the file if (!filename.empty() && boost::filesystem::exists(filename)) result = boost::filesystem::file_size(filename); } // Add in the contents of the write buffer. This is the data that will be // written to disk soon. { std::lock_guard lock(this->dataPtr->writeMutex); LogRecordPrivate::Log_M::const_iterator iter = this->dataPtr->logs.find(_name); if (iter != this->dataPtr->logs.end()) { GZ_ASSERT(iter->second, "Log object is NULL"); result += iter->second->BufferSize(); } } return result; } ////////////////////////////////////////////////// void LogRecord::SetBasePath(const std::string &_path) { // Make sure the directory exists if (!boost::filesystem::exists(_path)) boost::filesystem::create_directories(_path); // Make sure we have a directory if (!boost::filesystem::is_directory(_path)) { gzerr << "Path " << _path << " is not a directory. Please only specify a " << "directory for data logging.\n"; return; } // Make sure the path is writable. // Note: This is not cross-platform compatible. if (access(_path.c_str(), W_OK) != 0) { gzerr << "You do no have permission to write into " << _path << "\n"; return; } this->dataPtr->logBasePath = _path; } ////////////////////////////////////////////////// std::string LogRecord::GetBasePath() const { return this->BasePath(); } ////////////////////////////////////////////////// std::string LogRecord::BasePath() const { return this->dataPtr->logBasePath.string(); } ////////////////////////////////////////////////// bool LogRecord::GetFirstUpdate() const { return this->FirstUpdate(); } ////////////////////////////////////////////////// bool LogRecord::FirstUpdate() const { return this->dataPtr->firstUpdate; } ////////////////////////////////////////////////// bool LogRecord::SaveModels(const std::set &_models) { std::set diff; std::set_difference(_models.begin(), _models.end(), this->dataPtr->savedModels.begin(), this->dataPtr->savedModels.end(), std::inserter(diff, diff.begin())); for (auto &model: diff) { if (model.empty()) continue; this->dataPtr->savedModels.insert(model); bool modelFound = false; for (const auto &path : common::SystemPaths::Instance()->GetModelPaths()) { boost::filesystem::path srcModelPath(path); srcModelPath /= model; if (boost::filesystem::exists(srcModelPath)) { modelFound = true; boost::filesystem::path destModelPath = this->dataPtr->logCompletePath / model; if (!gazebo::common::copyDir(srcModelPath, destModelPath)) { gzerr << "Failed to copy model from '" << srcModelPath.string() << "' to '" << destModelPath.string() << "'" << std::endl; } break; } } if (!modelFound) { gzwarn << "Model: " << model << " not found, " << "please check the value of env variable GAZEBO_MODEL_PATH\n"; } } return true; } ////////////////////////////////////////////////// bool LogRecord::SaveFiles(const std::set &_files) { if (_files.empty()) return false; bool saveError = false; std::set diff; std::set_difference(_files.begin(), _files.end(), this->dataPtr->savedFiles.begin(), this->dataPtr->savedFiles.end(), std::inserter(diff, diff.begin())); for (auto &file : diff) { if (file.empty()) continue; this->dataPtr->savedFiles.insert(file); bool fileFound = false; std::string prefix = "file://"; std::string fileName = file; boost::filesystem::path srcPath; if (fileName.compare(0, prefix.size(), prefix) == 0) { // strip prefix fileName = file.substr(prefix.size()); // search in gazebo path for (const auto &path : common::SystemPaths::Instance()->GetGazeboPaths()) { auto p = boost::filesystem::path(path) / fileName; if (common::exists(p.string())) { srcPath = path; fileFound = true; break; } } } // if not found in gazebo path or resource has abs path then check local // filesystem if (!fileFound || fileName[0] == '/') { fileFound = common::exists(fileName); } // copy resource // NOTE: if file is a mesh, e.g. box.dae, it could contain reference to // to texture files in other directories. A hacky workaround is to copy // entire model dir if (fileFound) { // HACK! copy entire model dir if mesh size_t meshIdx = fileName.find("/meshes/"); if (meshIdx != std::string::npos) { auto modelPath = boost::filesystem::path(fileName.substr(0, meshIdx)); srcPath = srcPath / modelPath; boost::filesystem::path destPath = this->dataPtr->logCompletePath / modelPath; boost::system::error_code errorCode; boost::filesystem::create_directories(destPath, errorCode); if (errorCode != boost::system::errc::success || !gazebo::common::copyDir(srcPath, destPath)) { gzerr << "Failed to copy model from '" << srcPath.string() << "' to '" << destPath.string() << "'" << std::endl; saveError = true; } } // else copy only the specified file else { srcPath = srcPath / fileName; boost::filesystem::path destPath = this->dataPtr->logCompletePath / fileName; boost::system::error_code errorCode; boost::filesystem::create_directories( destPath.parent_path(), errorCode); if (errorCode == boost::system::errc::success) boost::filesystem::copy_file(srcPath, destPath, errorCode); else { gzerr << "Failed to copy file from '" << srcPath.string() << "' to '" << destPath.string() << "'" << std::endl; saveError = true; } } } else { gzerr << "File: " << file << " not found!" << std::endl; saveError = true; } } return !saveError; } ////////////////////////////////////////////////// void LogRecord::Notify() { if (this->dataPtr->running) this->dataPtr->updateCondition.notify_all(); } ////////////////////////////////////////////////// void LogRecord::RunUpdate() { std::unique_lock updateLock(this->dataPtr->updateMutex); this->dataPtr->startThreadCondition.notify_all(); // This loop will write data to disk. while (!this->dataPtr->stopThread) { // Don't completely lock, just to be safe. this->dataPtr->updateCondition.wait(updateLock); if (!this->dataPtr->stopThread) this->Update(); } } ////////////////////////////////////////////////// void LogRecord::Update() { if (!this->dataPtr->paused) { unsigned int size = 0; { std::lock_guard lock(this->dataPtr->writeMutex); // Collect all the new log data. This will not write data to disk. for (this->dataPtr->updateIter = this->dataPtr->logs.begin(); this->dataPtr->updateIter != this->dataPtr->logsEnd; ++this->dataPtr->updateIter) { size += this->dataPtr->updateIter->second->Update(); } } if (this->dataPtr->firstUpdate) { this->dataPtr->firstUpdate = false; this->dataPtr->startTime = common::Time::GetWallTime(); } // Signal that new data is available. if (size > 0) this->dataPtr->dataAvailableCondition.notify_one(); this->dataPtr->currTime = common::Time::GetWallTime(); // Output the new log status this->PublishLogStatus(); } } ////////////////////////////////////////////////// void LogRecord::RunWrite() { // Wait for new data. std::unique_lock lock(this->dataPtr->runWriteMutex); this->dataPtr->startThreadCondition.notify_all(); // This loop will write data to disk. while (!this->dataPtr->stopThread) { this->dataPtr->dataAvailableCondition.wait(lock); this->Write(false); } } ////////////////////////////////////////////////// void LogRecord::Write(const bool /*_force*/) { std::lock_guard lock(this->dataPtr->writeMutex); // Collect all the new log data. for (this->dataPtr->updateIter = this->dataPtr->logs.begin(); this->dataPtr->updateIter != this->dataPtr->logsEnd; ++this->dataPtr->updateIter) { this->dataPtr->updateIter->second->Write(); } } ////////////////////////////////////////////////// common::Time LogRecord::GetRunTime() const { return this->RunTime(); } ////////////////////////////////////////////////// common::Time LogRecord::RunTime() const { return this->dataPtr->currTime - this->dataPtr->startTime; } ////////////////////////////////////////////////// LogRecordPrivate::Log::Log(LogRecord *_parent, const std::string &_relativeFilename, std::function _logCB) { this->parent = _parent; this->logCB = _logCB; this->relativeFilename = _relativeFilename; } ////////////////////////////////////////////////// LogRecordPrivate::Log::~Log() { this->Stop(); } ////////////////////////////////////////////////// unsigned int LogRecordPrivate::Log::Update() { std::ostringstream stream; // Get log data via the callback. if (this->logCB(stream)) { std::string data = stream.str(); if (!data.empty()) { const std::string &encodingLocal = this->parent->Encoding(); this->buffer.append("\n"); this->buffer.append("buffer); } else if (encodingLocal == "zlib") { std::string str; // Compress to zlib { boost::iostreams::filtering_ostream out; out.push(boost::iostreams::zlib_compressor()); out.push(std::back_inserter(str)); boost::iostreams::copy(boost::make_iterator_range(data), out); } // Encode in base64. Base64Encode(str.c_str(), str.size(), this->buffer); } else if (encodingLocal == "txt") this->buffer.append(data); else gzerr << "Unknown log file encoding[" << encodingLocal << "]\n"; this->buffer.append("]]>\n"); this->buffer.append("\n"); } } return this->buffer.size(); } ////////////////////////////////////////////////// void LogRecordPrivate::Log::ClearBuffer() { this->buffer.clear(); } ////////////////////////////////////////////////// unsigned int LogRecordPrivate::Log::BufferSize() { return this->buffer.size(); } ////////////////////////////////////////////////// std::string LogRecordPrivate::Log::RelativeFilename() const { return this->relativeFilename; } ////////////////////////////////////////////////// std::string LogRecordPrivate::Log::CompleteFilename() const { return this->completePath.string(); } ////////////////////////////////////////////////// void LogRecordPrivate::Log::Stop() { if (this->logFile.is_open()) { this->Update(); this->Write(); std::string xmlEnd = ""; this->logFile.write(xmlEnd.c_str(), xmlEnd.size()); this->logFile.close(); } this->completePath.clear(); } ////////////////////////////////////////////////// void LogRecordPrivate::Log::Start(const boost::filesystem::path &_path) { // Make the full path for the log file this->completePath = _path / this->relativeFilename; // Make sure the file does not exist if (boost::filesystem::exists(this->completePath)) gzlog << "Filename [" + this->completePath.string() + "], already exists." << " The log file will be overwritten.\n"; std::ostringstream stream; stream << "\n" << "\n" << "
\n" << "" << GZ_LOG_VERSION << "\n" << "" << GAZEBO_VERSION_FULL << "\n" << "" << ignition::math::Rand::Seed() << "\n" << "
\n"; this->buffer.append(stream.str()); } ////////////////////////////////////////////////// void LogRecordPrivate::Log::Write() { // Make sure the file is open for writing if (!this->logFile.is_open()) { // Try to open it... this->logFile.open(this->completePath.string().c_str(), std::fstream::out | std::ios::binary); // Throw an error if we couldn't open the file for writing. if (!this->logFile.is_open()) gzthrow("Unable to open file for logging:" + this->completePath.string() + "]"); } // Check to see if the log file still exists on disk. This will catch the // case when someone deletes a log file while recording. if (!boost::filesystem::exists(this->completePath.string().c_str())) { gzerr << "Log file[" << this->completePath << "] no longer exists. " << "Unable to write log data.\n"; // We have to clear the buffer, or else it may grow indefinitely. this->buffer.clear(); return; } // Write out the contents of the buffer. this->logFile.write(this->buffer.c_str(), this->buffer.size()); this->logFile.flush(); // Clear the buffer. this->buffer.clear(); } ////////////////////////////////////////////////// void LogRecord::OnLogControl(ConstLogControlPtr &_data) { if (_data->has_base_path() && !_data->base_path().empty()) this->SetBasePath(_data->base_path()); std::string msgEncoding = "zlib"; if (_data->has_encoding()) msgEncoding = _data->encoding(); if (_data->has_start() && _data->start()) { this->Start(msgEncoding); } else if (_data->has_stop() && _data->stop()) { this->Stop(); } else if (_data->has_paused()) { this->SetPaused(_data->paused()); } // Output the new log status this->PublishLogStatus(); } ////////////////////////////////////////////////// void LogRecord::PublishLogStatus() { if (this->dataPtr->logs.empty() || !this->dataPtr->logStatusPub || !this->dataPtr->logStatusPub->HasConnections()) return; /// \todo right now this function will only report on the first log. msgs::LogStatus msg; unsigned int size = 0; // Set the time of the status message msgs::Set(msg.mutable_sim_time(), this->RunTime()); // Set the log recording base path name msg.mutable_log_file()->set_base_path(this->BasePath()); // Get the full name of the log file msg.mutable_log_file()->set_full_path(this->Filename()); // Set the URI of th log file msg.mutable_log_file()->set_uri(transport::Connection::GetLocalHostname()); // Get the size of the log file size = this->FileSize(); if (size < 1000) msg.mutable_log_file()->set_size_units(msgs::LogStatus::LogFile::BYTES); else if (size < 1e6) { msg.mutable_log_file()->set_size(size / 1.0e3); msg.mutable_log_file()->set_size_units(msgs::LogStatus::LogFile::K_BYTES); } else if (size < 1e9) { msg.mutable_log_file()->set_size(size / 1.0e6); msg.mutable_log_file()->set_size_units(msgs::LogStatus::LogFile::M_BYTES); } else { msg.mutable_log_file()->set_size(size / 1.0e9); msg.mutable_log_file()->set_size_units(msgs::LogStatus::LogFile::G_BYTES); } this->dataPtr->logStatusPub->Publish(msg); } ////////////////////////////////////////////////// void LogRecord::Cleanup() { std::unique_lock lock(this->dataPtr->controlMutex); this->dataPtr->startThreadCondition.notify_all(); // Wait for the cleanup signal this->dataPtr->cleanupCondition.wait(lock); bool currentPauseState = this->dataPtr->pauseState; event::Events::pause(true); // Reset the flags this->dataPtr->paused = false; this->dataPtr->running = false; this->dataPtr->stopThread = true; // Kick the update thread { std::lock_guard updateLock(this->dataPtr->updateMutex); this->dataPtr->updateCondition.notify_all(); } // Wait for the write thread, if it exists if (this->dataPtr->updateThread) this->dataPtr->updateThread->join(); // Kick the write thread { std::lock_guard lock2(this->dataPtr->runWriteMutex); this->dataPtr->dataAvailableCondition.notify_all(); } // Wait for the write thread, if it exists if (this->dataPtr->writeThread) this->dataPtr->writeThread->join(); this->dataPtr->updateThread.reset(); this->dataPtr->writeThread.reset(); // Update and write one last time to make sure we log all data. this->Update(); this->Write(true); // Stop all the logs for (LogRecordPrivate::Log_M::iterator iter = this->dataPtr->logs.begin(); iter != this->dataPtr->logsEnd; ++iter) { iter->second->Stop(); } // Reset the times this->dataPtr->startTime = this->dataPtr->currTime = common::Time(); // Output the new log status this->PublishLogStatus(); event::Events::pause(currentPauseState); this->dataPtr->readyToStart = true; } ////////////////////////////////////////////////// bool LogRecord::IsReadyToStart() const { return this->dataPtr->readyToStart; } ////////////////////////////////////////////////// unsigned int LogRecord::GetBufferSize() const { return this->BufferSize(); } ////////////////////////////////////////////////// unsigned int LogRecord::BufferSize() const { std::lock_guard lock(this->dataPtr->writeMutex); unsigned int size = 0; for (LogRecordPrivate::Log_M::const_iterator iter = this->dataPtr->logs.begin(); iter != this->dataPtr->logs.end(); ++iter) { size += iter->second->BufferSize(); } return size; }