ModelSpace
All Classes Namespaces Functions Variables Enumerations Pages
SimLogger.h
1/******************************************************************************
2* Copyright (c) ATTX LLC 2024. All Rights Reserved.
3*
4* This software and associated documentation (the "Software") are the
5* proprietary and confidential information of ATTX, LLC. The Software is
6* furnished under a license agreement between ATTX and the user organization
7* and may be used or copied only in accordance with the terms of the agreement.
8* Refer to 'license/attx_license.adoc' for standard license terms.
9*
10* EXPORT CONTROL NOTICE: THIS SOFTWARE MAY INCLUDE CONTENT CONTROLLED UNDER THE
11* INTERNATIONAL TRAFFIC IN ARMS REGULATIONS (ITAR) OR THE EXPORT ADMINISTRATION
12* REGULATIONS (EAR99). No part of the Software may be used, reproduced, or
13* transmitted in any form or by any means, for any purpose, without the express
14* written permission of ATTX, LLC.
15******************************************************************************/
16/*
17Logger header file
18
19Author: Alex Reynolds
20*/
21#ifndef SIM_LOGGER_H
22#define SIM_LOGGER_H
23
24#include <vector>
25#include <array>
26#include <string>
27
28#include "data_management/GraphTreeObject.h"
29#include "core/clockwerkerrors.h"
30#include "core/Matrix.hpp"
31#include "architecture/EventLogger.h"
32
33namespace clockwerk {
34
35 enum vector_select_e {
36 BOOL,
37 INT,
38 UINT,
39 FLOAT,
40 DOUBLE,
41 STRING
42 };
43
44 class Executive;
45
46 /// @brief Class for logging data to a file
47 ///
48 /// This function defines a base logger class with functions as outlined and
49 /// described in the header. It fully defines the updstream logging interface
50 /// (interface to logged objects and buffering), but does not define a downstream
51 /// interface to the actual output file. That interface is reserved for inherited
52 /// classes. With that in mind, this class is essentially useless until inherited,
53 /// and should not be used directly.
54
55 /// Current planned inherited file structures are CSV and HDF5.
56
57 /// The logger class is a generic class targeted at collecting, buffering,
58 /// and writing data to a file. It operates on the graph tree by interfacing with
59 /// objects inherited from it (graph tree by default cannot be logged). Desired
60 /// log parameters can be passed either by a direct handle to the class or by string
61 /// address, and may be passed as a single unit (just one data point) or a recursive
62 /// inheritence (log an entire branch of the tree).
63
64 /// Data may be buffered in the logger for speed and efficiency, but is not buffered
65 /// by default for simplicity. Regardless, the buffer class is used as the primary method
66 /// for collecting data off of graph tree objects for writing.
67 class SimLogger : public GraphTreeObject {
68 public:
69 /// Constructor for the SimLogger object -- has a default rate of every ms
70 /// @param exec Handle to the executive object
71 SimLogger(Executive &executive, const std::string &filename="output.csv", unsigned int buffer_size=1);
72 ~SimLogger() {close();}
73
74 /// Functions to get and set buffer size
75 unsigned int bufferSize() {return _buffer_size;}
76 int bufferSize(unsigned int buffer_size);
77
78 /// Functions to get and set filename
79 std::string filename() {return _filename;}
80 int filename(const std::string &name);
81
82 /// Functions to get and set output directory
83 std::string outDir() {return _output_directory;}
84 int outDir(const std::string &directory);
85
86 /// @brief Function to set up the SimLogger
87 /// @param file_name Name of the file to log to
88 /// @return Integer code indicating success/failure
89 virtual int setup(const std::string &file_name) {return NO_ERROR;}
90
91 /// @brief Function to add a logging parameter to the SimLogger.
92 /// Overloaded to add graph node by string or by object
93 /// @param parameter Graph tree node passed as object
94 /// @param param_name The name to which the parameter will be logged
95 /// @param recursive Flag indicating whether parameter should be added
96 /// recursively (include all lower items on its tree)
97 /// or not (the default)
98 int addParameter(GraphTreeObject &parameter, const std::string &param_name,
99 bool recursive = false);
100
101 /// @brief Function to add a logging parameter to the SimLogger.
102 /// Overloaded to add graph node by string or by object
103 /// @param parameter Graph tree node passed as address string
104 /// @param param_name The name to which the parameter will be logged
105 /// @param recursive Flag indicating whether parameter should be added
106 /// recursively (include all lower items on its tree)
107 /// or not (the default)
108 int addParameter(const std::string &parameter, const std::string &param_name,
109 bool recursive = false);
110
111 /// @brief Function to lock the file for logging once all parameters have
112 /// been defined. Generates headers and creates buffers for logging
113 /// based on parameters that have already been added above.
114 /// @note Should be called once, before sim starts
115 /// @note If anything changes after this point, it could potentially cause
116 /// problems in the SimLoggers
117 /// @note Should NOT be overridden in standard logging implementation.
118 virtual int lockForLogging();
119
120 /// @brief Function to log data to buffer. Flushes buffer to file when full
121 /// @note Should NOT be overridden in standard implementation. Virtual call
122 /// is for Python virtual implementation but should generally be disregarded
123 virtual int log();
124
125 /// @brief Function to close down the file -- logs remaining buffered data
126 /// and closes
127 virtual int close();
128
129 int writeHeaders(const int &val) {_group_idx.push_back(-1); _headers.push_back(_current_name); return NO_ERROR;}
130 int writeHeaders(const unsigned int &val) {_group_idx.push_back(-1); _headers.push_back(_current_name); return NO_ERROR;}
131 int writeHeaders(const double &val) {_group_idx.push_back(-1); _headers.push_back(_current_name); return NO_ERROR;}
132 int writeHeaders(const GraphTreeObject &val);
133 int writeHeaders(void* val) {_group_idx.push_back(-1); _headers.push_back(_current_name); return NO_ERROR;}
134 int writeHeaders(const std::string &val) {_group_idx.push_back(-1); _headers.push_back(_current_name); return NO_ERROR;}
135
136 template <typename T> int writeHeaders(const std::vector<T> &val);
137 template <typename T, long unsigned int N> int writeHeaders(const std::array<T, N> &val);
138
139 template <typename T, unsigned int R, unsigned int C> int writeHeaders(const Matrix<T, R, C> &val);
140
141 /// @brief Overloaded function to write data to the buffer. Should not be
142 /// called directly by the user -- is reserved for call within graph
143 /// object providing the logging
144 /// @note If init is true, pushes back element into buffer. Otherwise, writes
145 /// to appropriate buffer
146 int writeToBuffer(const bool &val);
147 int writeToBuffer(const int &val);
148 int writeToBuffer(const long unsigned int &val);
149 int writeToBuffer(const float &val);
150 int writeToBuffer(const double &val);
151 int writeToBuffer(const std::string &val);
152 int writeToBuffer(void* val);
153 int writeToBuffer(GraphTreeObject &val);
154
155 /// @brief Function to handle iteratable values -- note here that we only need to define one
156 /// templated instance because this function will be fully templated out to all DataIO
157 /// objects that are instantiated, and calls the above ^ under the hood
158 template <typename T> int writeToBuffer(const std::vector<T> &val);
159 template <typename T, long unsigned int N> int writeToBuffer(const std::array<T, N> &val);
160
161 template <typename T, unsigned int R, unsigned int C> int writeToBuffer(const Matrix<T, R, C> &val);
162
163 /// @brief Function to set the local model log level
164 /// @param new_level The new level to set logging to
165 void logLevel(log_level_e new_level) {_local_log_level = new_level;}
166
167 /// Handle to the executive object -- used for event logging, etc.
168 Executive *exc;
169 protected:
170 /// @brief Function to set all buffers to full size
171 void _fillBuffers();
172
173 /// @brief Function to create a file, in whatever format is chosen, for logging
174 virtual int _createSetupFile() {return NO_ERROR;}
175
176 /// @brief Function to write buffered data to file
177 virtual int _writeToFile() {return NO_ERROR;}
178
179 /// A vector to hold all graph tree object pointers
180 std::vector<GraphTreeObject*> _logged_nodes;
181 std::vector<std::string> _log_names;
182 std::string _current_name;
183
184 /// String for the filename
185 std::string _filename;
186 std::string _output_directory;
187
188 /// Ok, so this is important. At the end of the day, every variable resolves
189 /// somehow into a basic type, because it is mandated in our logging system.
190 /// The catch is that we need to resolve variables into headers and then data
191 /// streams, which can be difficult because C++ is a strongly typed language
192 /// and we're not really detecting our type until runtime. The solution here
193 /// is a little messy, but we're basically going to create a bunch of different
194 /// vectors to hold our different variables, then tie each element to its vector
195 /// via indices. Got it? God knows I sure don't. Here we go...
196 /// Vectors to hold defined/accepted data types and read/write indices
197 unsigned int _buffer_size = 10;
198 unsigned int _buffer_write_idx = 0;
199 unsigned int _buffer_read_idx = 0;
200 std::vector<std::vector<bool>> _bool_vector; /// 0
201 std::vector<std::vector<int>> _int_vector; /// 1
202 std::vector<std::vector<long unsigned int>> _uint_vector; /// 2
203 std::vector<std::vector<float>> _float_vector; /// 3
204 std::vector<std::vector<double>> _double_vector; /// 4
205 std::vector<std::vector<std::string>> _string_vector; /// 5
206
207 /// Header and indexing variables
208 bool _locked;
209 unsigned int _seq_idx = 0;
210 std::vector<std::string> _headers;
211 std::vector<unsigned int> _sequence;
212 std::vector<vector_select_e> _type;
213
214 /// Grouping methods -- for some variables, such as arrays and vectors,
215 /// we may want to lump all of the data elements together, even though
216 /// they are tracked/"logged" as separate native datatypes. The following
217 /// struct and associated tracking variables provide a mechanism for
218 /// doing that
219 struct Group {
220 long unsigned int size; /// The size of the group
221 std::string group_name; /// The name of the group object
222 };
223 std::vector<int> _group_idx; /// Set to -1 if variable is not part of group
224 std::vector<Group> _groups;
225
226 /// Our local log level -- allows a higher log level locally than the overall system
227 log_level_e _local_log_level = NONE;
228 };
229
230
231 template <typename T>
232 int SimLogger::writeToBuffer(const std::vector<T> &val) {
233 int err;
234 /// Loop through our vector and write each value individually to buffer
235 for(unsigned int i = 0; i < val.size(); i++) {
236 err = writeToBuffer(val[i]);
237 if(err) {
238 return err;
239 }
240 }
241 return NO_ERROR;
242 }
243
244 template <typename T, long unsigned int N>
245 int SimLogger::writeToBuffer(const std::array<T, N> &val) {
246 int err;
247 /// Loop through our vector and write each value individually to buffer
248 for(unsigned int i = 0; i < N; i++) {
249 err = writeToBuffer(val[i]);
250 if(err) {
251 return err;
252 }
253 }
254 return NO_ERROR;
255 }
256
257 template <typename T, unsigned int R, unsigned int C>
258 int SimLogger::writeToBuffer(const Matrix<T, R, C> &val) {
259 int err;
260 unsigned int i, j;
261 for(i = 0; i < R; i++) {
262 for(j = 0; j < C; j++) {
263 err = writeToBuffer(val.values[i][j]);
264 if(err) {
265 return err;
266 }
267 }
268 }
269 return NO_ERROR;
270 }
271
272 template <typename T>
273 int SimLogger::writeHeaders(const std::vector<T> &val) {
274 /// Vector is a group item -- create a group and record our index
275 /// to it so it can be (optionally) accessed down the line
276 _groups.push_back({val.size(), _current_name});
277
278 for(unsigned int i = 0; i < val.size(); i++) {
279 _headers.push_back(_current_name + "_" + std::to_string(i));
280 _group_idx.push_back(_groups.size() - 1);
281 }
282 return NO_ERROR;
283 }
284
285 template <typename T, long unsigned int N>
286 int SimLogger::writeHeaders(const std::array<T, N> &val) {
287 /// Array is a group item -- create a group and record our index
288 /// to it so it can be (optionally) accessed down the line
289 _groups.push_back({val.size(), _current_name});
290
291 for(unsigned int i = 0; i < N; i++) {
292 _headers.push_back(_current_name + "_" + std::to_string(i));
293 _group_idx.push_back(_groups.size() - 1);
294 }
295 return NO_ERROR;
296 }
297
298 template <typename T, unsigned int R, unsigned int C>
299 int SimLogger::writeHeaders(const Matrix<T, R, C> &val) {
300 /// Vector is a group item -- create a group and record our index
301 /// to it so it can be (optionally) accessed down the line
302 _groups.push_back({R*C, _current_name});
303
304 unsigned int i, j;
305 for(i = 0; i < R; i++) {
306 for(j = 0; j < C; j++) {
307 if(C == 1) {
308 /// This is a vector, so don't write out our column index
309 _headers.push_back(_current_name + "_" + std::to_string(i));
310 _group_idx.push_back(_groups.size() - 1);
311 } else {
312 /// This is a matrix, so write out both dimensions
313 _headers.push_back(_current_name + "_" + std::to_string(i) + "_" + std::to_string(j));
314 _group_idx.push_back(_groups.size() - 1);
315 }
316 }
317 }
318 return NO_ERROR;
319 }
320
321}
322
323#endif
Central control mechanism to run simulations and software.
Definition Executive.h:43
Base class for object organization.
Definition GraphTreeObject.h:87
Matrix math implementation.
Definition Matrix.hpp:54
Class for logging data to a file.
Definition SimLogger.h:67
std::vector< std::vector< long unsigned int > > _uint_vector
1
Definition SimLogger.h:202
virtual int _createSetupFile()
Function to create a file, in whatever format is chosen, for logging.
Definition SimLogger.h:174
std::vector< std::vector< int > > _int_vector
0
Definition SimLogger.h:201
virtual int lockForLogging()
Function to lock the file for logging once all parameters have been defined. Generates headers and cr...
Definition SimLogger.cpp:125
log_level_e _local_log_level
Our local log level – allows a higher log level locally than the overall system.
Definition SimLogger.h:227
Executive * exc
Handle to the executive object – used for event logging, etc.
Definition SimLogger.h:168
std::string outDir()
Functions to get and set output directory.
Definition SimLogger.h:83
std::string filename()
Functions to get and set filename.
Definition SimLogger.h:79
std::vector< std::vector< float > > _float_vector
2
Definition SimLogger.h:203
virtual int log()
Function to log data to buffer. Flushes buffer to file when full.
Definition SimLogger.cpp:178
int writeToBuffer(const bool &val)
Overloaded function to write data to the buffer. Should not be called directly by the user – is reser...
Definition SimLogger.cpp:277
std::vector< std::vector< std::string > > _string_vector
4
Definition SimLogger.h:205
virtual int close()
Function to close down the file – logs remaining buffered data and closes.
Definition SimLogger.cpp:216
std::vector< GraphTreeObject * > _logged_nodes
A vector to hold all graph tree object pointers.
Definition SimLogger.h:180
bool _locked
5
Definition SimLogger.h:208
std::string _filename
String for the filename.
Definition SimLogger.h:185
unsigned int bufferSize()
Functions to get and set buffer size.
Definition SimLogger.h:75
void logLevel(log_level_e new_level)
Function to set the local model log level.
Definition SimLogger.h:165
std::vector< Group > _groups
Set to -1 if variable is not part of group.
Definition SimLogger.h:224
unsigned int _buffer_size
Ok, so this is important. At the end of the day, every variable resolves somehow into a basic type,...
Definition SimLogger.h:197
virtual int _writeToFile()
Function to write buffered data to file.
Definition SimLogger.h:177
std::vector< std::vector< double > > _double_vector
3
Definition SimLogger.h:204
void _fillBuffers()
Function to set all buffers to full size.
Definition SimLogger.cpp:233
#define NO_ERROR
Definition clockwerkerrors.h:31
Grouping methods – for some variables, such as arrays and vectors, we may want to lump all of the dat...
Definition SimLogger.h:219
std::string group_name
The size of the group.
Definition SimLogger.h:221