@@ -75,7 +75,8 @@ class file_observer::slave_value_writer
7575 std::lock_guard<std::mutex> lock (mutex_);
7676 if (recording_) {
7777 if (!fsw_.is_open ()) {
78- create_log_file ();
78+ auto dataFileName = create_log_file ();
79+ create_metadata_file (dataFileName);
7980 }
8081 if (timeStep % decimationFactor_ == 0 ) {
8182
@@ -130,6 +131,8 @@ class file_observer::slave_value_writer
130131 }
131132
132133private:
134+ int keyWidth_ = 14 ;
135+
133136 template <typename T>
134137 void write (const std::vector<T>& values, std::stringstream& ss)
135138 {
@@ -160,7 +163,7 @@ class file_observer::slave_value_writer
160163 }
161164 }
162165
163- /* * Default constructor initialization, all variables are logged. */
166+ /* * Default constructor initialization, all variables - except those with causality local - are logged. */
164167 void initialize_default ()
165168 {
166169 if (!timeStampedFileNames_) {
@@ -188,14 +191,16 @@ class file_observer::slave_value_writer
188191 }
189192 }
190193
191- void create_log_file ()
194+ std::string create_log_file ()
192195 {
193196 std::string filename;
194197 std::stringstream ss;
198+ std::string time_str;
199+
195200 if (!timeStampedFileNames_) {
196201 filename = observable_->name ().append (" .csv" );
197202 } else {
198- auto time_str = format_time (boost::posix_time::microsec_clock::local_time ());
203+ time_str = format_time (boost::posix_time::microsec_clock::local_time ());
199204 filename = observable_->name ().append (" _" ).append (time_str).append (" .csv" );
200205 }
201206
@@ -204,29 +209,93 @@ class file_observer::slave_value_writer
204209 fsw_.open (filePath, std::ios_base::out | std::ios_base::app);
205210
206211 if (fsw_.fail ()) {
207- throw std::runtime_error (" Failed to open file stream for logging" );
212+ std::stringstream error;
213+ error << " Failed to open log file stream: " << filePath.c_str ();
214+ throw std::runtime_error (error.str ());
208215 }
209216
210217 ss << " Time,StepCount" ;
211218
219+ // Add variable names
212220 for (const auto & vd : realVars_) {
213- ss << " ," << vd.name << " [ " << vd. reference << " " << vd. type << " " << vd. causality << " ] " ;
221+ ss << " ," << vd.name ;
214222 }
215223 for (const auto & vd : intVars_) {
216- ss << " ," << vd.name << " [ " << vd. reference << " " << vd. type << " " << vd. causality << " ] " ;
224+ ss << " ," << vd.name ;
217225 }
218226 for (const auto & vd : boolVars_) {
219- ss << " ," << vd.name << " [ " << vd. reference << " " << vd. type << " " << vd. causality << " ] " ;
227+ ss << " ," << vd.name ;
220228 }
221229 for (const auto & vd : stringVars_) {
222- ss << " ," << vd.name << " [ " << vd. reference << " " << vd. type << " " << vd. causality << " ] " ;
230+ ss << " ," << vd.name ;
223231 }
224232
225233 ss << std::endl;
226234
227235 if (fsw_.is_open ()) {
228236 fsw_ << ss.rdbuf ();
229237 }
238+
239+ return time_str;
240+ }
241+
242+ void write_variable_metadata (std::stringstream& ss, std::vector<variable_description>& variables) const
243+ {
244+ for (const auto & v : variables) {
245+ ss << " - " << std::setw (keyWidth_) << " name:" << v.name << std::endl
246+ << " " << std::setw (keyWidth_) << " reference:" << v.reference << std::endl
247+ << " " << std::setw (keyWidth_) << " type:" << v.type << std::endl
248+ << " " << std::setw (keyWidth_) << " causality:" << v.causality << std::endl
249+ << " " << std::setw (keyWidth_) << " variability:" << v.variability << std::endl;
250+
251+ if (v.start .has_value ()) {
252+ ss << " " << std::setw (keyWidth_) << " start value:" ;
253+ std::visit ([&](const auto & val) { ss << val << std::endl; }, v.start .value ());
254+ }
255+ }
256+ }
257+
258+ void create_metadata_file (const std::string& time_str)
259+ {
260+ std::ofstream metadata_fw;
261+ std::string filename;
262+ std::stringstream ss;
263+
264+ if (!timeStampedFileNames_) {
265+ filename = observable_->name ().append (" _metadata.yaml" );
266+ } else {
267+ filename = observable_->name ().append (" _" ).append (time_str).append (" _metadata.yaml" );
268+ }
269+
270+ const auto filePath = logDir_ / filename;
271+ metadata_fw.open (filePath, std::ios_base::out | std::ios_base::app);
272+
273+ if (fsw_.fail ()) {
274+ std::stringstream error;
275+ error << " Failed to open log metadata file stream: " << filePath.c_str ();
276+ throw std::runtime_error (error.str ());
277+ }
278+
279+ auto md = observable_->model_description ();
280+
281+ ss << std::left
282+ << std::setw (keyWidth_) << " name:" << md.name << std::endl
283+ << std::setw (keyWidth_) << " uuid:" << md.uuid << std::endl
284+ << std::setw (keyWidth_) << " description:" << md.description << std::endl
285+ << std::setw (keyWidth_) << " author:" << md.description << std::endl
286+ << std::setw (keyWidth_) << " version:" << md.version << std::endl;
287+
288+ ss << " variables:" << std::endl;
289+
290+ write_variable_metadata (ss, realVars_);
291+ write_variable_metadata (ss, intVars_);
292+ write_variable_metadata (ss, boolVars_);
293+ write_variable_metadata (ss, stringVars_);
294+
295+ if (metadata_fw.is_open ()) {
296+ metadata_fw << ss.rdbuf ();
297+ }
298+ metadata_fw.close ();
230299 }
231300
232301 void persist ()
0 commit comments