NTRT Simulator  Version: Master
 All Classes Namespaces Files Functions Variables Typedefs Friends Pages
tgDataLogger2.cpp
Go to the documentation of this file.
1 /*
2  * Copyright © 2012, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The NASA Tensegrity Robotics Toolkit (NTRT) v1 platform is licensed
7  * under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * http://www.apache.org/licenses/LICENSE-2.0.
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
15  * either express or implied. See the License for the specific language
16  * governing permissions and limitations under the License.
17 */
18 
26 // This module
27 #include "tgDataLogger2.h"
28 // This application
29 #include "tgSensor.h"
30 // The C++ Standard Library
31 #include <stdexcept>
32 #include <cassert>
33 #include <iostream>
34 #include <vector> // for managing descendants of tgSenseables.
35 #include <time.h> // for the file name of the log file
36 #include <sstream> // for converting a size_t to a string.
37 #include <cstdlib> // for getenv, converting ~ to $HOME.
38 
46 tgDataLogger2::tgDataLogger2(std::string fileNamePrefix, double timeInterval) :
47  tgDataManager(),
48  m_fileNamePrefix(fileNamePrefix),
49  m_timeInterval(timeInterval)
50 {
51  // A quick check on the passed-in string: it must not be the empty
52  // string. Must be a correct linux path.
53  // TO-DO: a better check on this.
54  if (m_fileNamePrefix == "") {
55  throw std::invalid_argument("File name cannot be the empty string. Please pass in a path to a file that can be opened.");
56  }
57  // Check: a negative time interval doesn't make sense.
58  if (m_timeInterval < 0.0 ) {
59  throw std::invalid_argument("Time interval must be nonnegative. Negative time intervals do not make sense.");
60  }
61 
62  // Additionally: we want to be able to expand the "~" string in fileNamePrefix
63  // to be the full home directory of the current user.
64  // It's a real pain to have to specify the complete directory structure
65  // for these log file names.
66  // Check if the first character of the string is a tilde:
67  if (m_fileNamePrefix.at(0) == '~') {
68  // Get the $HOME environment variable
69  std::string home = std::getenv("HOME");
70  // Remove the tilde (the first element) from the string
71  m_fileNamePrefix.erase(0,1);
72  // Concatenate the home directory.
74  }
75 
76  // Postcondition
77  assert(invariant());
78 }
79 
88 {
89  throw std::invalid_argument("Cannot create a tgDataLogger2 without a path to the log file! Please use the constructor that takes a string.");
90 }
91 
98 {
99  // TO-DO: should we double-check and close the tgOutput filestream here too?
100 }
101 
110 {
111  // Call the parent's setup method, which creates the sensors.
113  // Now, m_sensors should be populated! This is (2) above.
114 
115  // (1) Create the full filename of the log file.
116  // Credit to Brian Tietz Mirletz, via the original tgDataObserver.
117  // Adapted from: http://www.cplusplus.com/reference/clibrary/ctime/localtime/
118  // Also http://www.cplusplus.com/forum/unices/2259/
119  time_t rawtime;
120  tm* currentTime;
121  int fileTimeSize = 64;
122  char fileTime [fileTimeSize];
123 
124  time (&rawtime);
125  currentTime = localtime(&rawtime);
126  strftime(fileTime, fileTimeSize, "%m%d%Y_%H%M%S", currentTime);
127  // Result: fileTime is a string with the time information.
128  m_fileName = m_fileNamePrefix + "_" + fileTime + ".txt";
129 
130  // DEBUGGING output:
131  std::cout << "tgDataLogger2 will be saving data to the file: " << std::endl
132  << m_fileName << std::endl;
133 
134  // Attempt to open the log file
135  tgOutput.open(m_fileName.c_str());
136  if (!tgOutput.is_open()) {
137  throw std::runtime_error("Log file could not be opened. Usually, this is because the directory you specified does not exist. Check for spelling errors.");
138  }
139 
140  // Output a first line of the header.
141  tgOutput << "tgDataLogger2 started logging at time " << fileTime << ", with "
142  << m_sensors.size() << " sensors on " << m_senseables.size()
143  << " senseable objects." << std::endl;
144 
145  // The first column of data will be "time", the m_totalTime since beginning
146  // of the simulation.
147  tgOutput << "time,";
148 
149  // Iterate. For each sensor, output its header.
150  // Prepend each label with the sensor number, which we choose to be the index in
151  // the vector of sensors. NOTE that this means the sensors vector CANNOT
152  // BE CHANGED, otherwise the data will not be aligned properly.
153  for (std::size_t i=0; i < m_sensors.size(); i++) {
154  // Get the vector of sensor data headings from this sensor
155  std::vector<std::string> headings = m_sensors[i]->getSensorDataHeadings();
156  // Iterate and output each heading
157  for (std::size_t j=0; j < headings.size(); j++) {
158  // Prepend with the sensor number and an underscore.
159  // Also, end with a comma, since this is a comma-separated-value log file.
160  tgOutput << i << "_" << headings[j] << ",";
161  }
162  }
163  // End with a new line.
164  tgOutput << std::endl;
165 
166  // Done! Close the output for now, will be re-opened during step.
167  tgOutput.close();
168 
169  // Initialize/reset the values of the time variables.
170  m_totalTime = 0.0;
171  m_updateTime = 0.0;
172 
173  // Postcondition
174  assert(invariant());
175 }
176 
181 {
182  // Call the parent's teardown method! This is important!
184  // Close the log file.
185  tgOutput.close();
186  // Postcondition
187  assert(invariant());
188 }
189 
196 void tgDataLogger2::step(double dt)
197 {
198  if (dt <= 0.0)
199  {
200  throw std::invalid_argument("dt is not positive");
201  }
202  else
203  {
204  // For the timestamp: first, add dt to the total time
205  m_totalTime += dt;
206  // also, add to the current time between sensor readings.
207  m_updateTime += dt;
208  // Then, if enough time has elapsed between the previous sensor reading,
209  if (m_updateTime >= m_timeInterval) {
210  // Open the log file for writing, appending and not overwriting.
211  tgOutput.open(m_fileName.c_str(), std::ios::app);
212  // Then output the time.
213  tgOutput << m_totalTime << ",";
214  // Collect the data and output it to the file!
215  for (size_t i=0; i < m_sensors.size(); i++) {
216  // Get the vector of sensor data from this sensor
217  std::vector<std::string> sensordata = m_sensors[i]->getSensorData();
218  // Iterate and output each data sample
219  for (std::size_t j=0; j < sensordata.size(); j++) {
220  // Include a comma, since this is a comma-separated-value log file.
221  tgOutput << sensordata[j] << ",";
222  }
223  }
224  tgOutput << std::endl;
225  // Close the output, to be re-opened next step.
226  tgOutput.close();
227  // Now that the sensors have been read, reset the counter.
228  m_updateTime = 0.0;
229  }
230  }
231 
232  // Postcondition
233  assert(invariant());
234 }
235 
240 std::string tgDataLogger2::toString() const
241 {
242  std::string p = " ";
243  std::ostringstream os;
245  << "This tgDataManager is a tgDataLogger2. " << std::endl;
246 
247  return os.str();
248 }
249 
250 std::ostream&
251 operator<<(std::ostream& os, const tgDataLogger2& obj)
252 {
253  os << obj.toString() << std::endl;
254  return os;
255 }
Constains definition of abstract class tgSensor, which defines the methods that a sensor must impleme...
std::vector< tgSensor * > m_sensors
virtual void teardown()
std::ostream & operator<<(std::ostream &os, const tgModel &obj)
Definition: tgModel.cpp:219
Contains the definition of class tgDataLogger2.
virtual std::string toString() const
double m_timeInterval
std::string m_fileNamePrefix
std::vector< tgSenseable * > m_senseables
virtual void setup()
std::string m_fileName
virtual std::string toString() const
double m_totalTime
virtual void teardown()
std::ofstream tgOutput
double m_updateTime
virtual void step(double dt)
virtual void setup()