Repository navigation
Conversation
54bd651 to
bba5a78
Compare
bba5a78 to
757dcfe
Compare
|
Hi. I am reviewing the code. It builds, runs, and produces the csv. The timestamp looks a bit odd, for instance, row 569215 looks like this 1704,7.0916666666666668e+00,Genrou,5281_1_genrou,omega,2.2408284627934353e-05 This effectively means that t = 7.09 something. Is this thing going to merge on t if I need to left join? Thank you. |
|
@tsybinae Thanks! Join on Let me know if you have any othr issues! |
|
This functionality is not specific to the csv task, so the merge can go ahead. There may be more precision related questions within a few month. |
tsybinae
left a comment
There was a problem hiding this comment.
Builds and runs cleanly.
superwhiskers
left a comment
There was a problem hiding this comment.
core idea seems like the right approach. just some comments on implementation of formatting that should be addressed before this goes further.
| void append(std::string& out, Json json) const override | ||
| void append(std::string& out, Json json) const | ||
| { | ||
| std::ostringstream os; | ||
| os.precision(std::numeric_limits<RealT>::digits10 + 1); | ||
| os << std::scientific; | ||
|
|
||
| std::string indent = " "; | ||
| if (json.after_first) | ||
| { | ||
| os << indent << ",\n"; | ||
| out += " ,\n"; | ||
| } | ||
| os << indent << "{\n"; | ||
| indent.append(2, ' '); | ||
| out += " {\n \"t\": "; | ||
| VariableMonitorDetail::formatReal(out, *time_); | ||
|
|
||
| os << indent << std::quoted("t") << ": " << *time_ << ",\n"; | ||
| for (auto&& var : variables_) | ||
| const Entry* previous = nullptr; | ||
| for (auto&& entry : entries_) | ||
| { | ||
| os << indent << std::quoted(var.label) << ": " << *var.value << ",\n"; | ||
| if (previous && previous->device_class == entry.device_class && previous->id == entry.id) | ||
| { | ||
| out += ",\n \""; | ||
| } | ||
| else | ||
| { | ||
| if (previous) | ||
| { | ||
| out += "\n }"; | ||
| } | ||
| out += ",\n \""; | ||
| out += entry.device_class; | ||
| out += '_'; | ||
| out += entry.id; | ||
| out += "\": {\n \""; | ||
| } | ||
| out += entry.variable; | ||
| out += "\": "; | ||
| VariableMonitorDetail::formatReal(out, entry.value()); | ||
|
|
||
| previous = &entry; | ||
| } | ||
| if (previous) | ||
| { | ||
| out += "\n }"; | ||
| } | ||
| out += "\n }"; | ||
| } |
There was a problem hiding this comment.
since we are already pulling in nlohmann/json, i would recommend using its json serialization capabilities here. that would make this easier to read
There was a problem hiding this comment.
I don't think it is a good idea to pull third-party dependency in the GridKit code beyond the JSON parser. Variable monitoring and JSON formatting should be cleanly separated so that we can replace parser dependencies (if needed) with other library.
There was a problem hiding this comment.
i disagree. with how we currently write the data, when using the json output format for monitored variables, if a component has its id set to a string which contains an escaped double quote, it will be unescaped in the output, creating malformed json output. if we used an existing json writer, this problem is solved. presumably, this would also apply to csv output and unescaped commas, or something similar for yaml
example modified case file:
example malformed json output:
There was a problem hiding this comment.
the same also applies to newlines and tabs. i.e. \n\t in the id of a component will output a literal newline and tab, so it's not just a matter of escaping a " in the id of a component. you need to handle a lot more
| out += " - t: "; | ||
| VariableMonitorDetail::formatReal(out, *time_); | ||
| out += '\n'; | ||
|
|
||
| auto after_first = false; | ||
| for (auto* mon : monitors_) | ||
| const Entry* previous = nullptr; | ||
| for (auto&& entry : entries_) | ||
| { | ||
| if (after_first) | ||
| if (!previous || previous->device_class != entry.device_class || previous->id != entry.id) | ||
| { | ||
| os << ",\n"; | ||
| out += " "; | ||
| out += entry.device_class; | ||
| out += '_'; | ||
| out += entry.id; | ||
| out += ":\n"; | ||
| } | ||
| std::string monitor_out; | ||
| mon->append(monitor_out, Json()); | ||
| os << monitor_out; | ||
| after_first = true; | ||
| } | ||
|
|
||
| indent.erase(indent.size() - 2); | ||
| os << '\n' | ||
| << indent << "}"; | ||
| out += " "; | ||
| out += entry.variable; | ||
| out += ": "; | ||
| VariableMonitorDetail::formatReal(out, entry.value()); | ||
| out += '\n'; | ||
|
|
||
| out += os.str(); | ||
| previous = &entry; | ||
| } |
There was a problem hiding this comment.
we don't have a yaml library so i understand doing this manually, but it could be improved by using <format> to avoid repeatedly concatenating new strings to the output
There was a problem hiding this comment.
Each += appends into one reused buffer, so no new strings are created after the first sample. I tried std::format_to for YAML, but 1.9× slower for wide and 2.2× slower for long layout
| */ | ||
| virtual void appendHeader(std::string&, Csv) const = 0; | ||
| /// Returns the current value of a monitored variable | ||
| using GetterT = std::function<double()>; |
There was a problem hiding this comment.
Everything is now converted to double before printing, including float, int, bool, etc.
There was a problem hiding this comment.
Attempted to resolve, but I might be misunderstanding. Please advise
|
@nkoukpaizan @superwhiskers feedback greatly appreciated! the example I hope is much better. And hopfully the template parameter approach is cleaner, please LMK |
Description
Adds an
output_layoutsolver option for monitor output. With"output_layout": "long", each row ofoutput_fileholds one variable at one sample instead of one column per variable, so results load straight into SQL tables no matter how many variables are monitored.Closes #597.
Wide output is unchanged, and printing is faster:
developPrint time only, 2000 monitors × 4 variables over 500 samples.
Proposed changes
VariableMonitorLayout(WIDE,LONG) toSinkSpec, set byoutput_layoutnext tooutput_filein the solver file. It defaults towide, andlongis not supported withreference_file.sample, t, class, id, variable, valuerather than thet, id, valuesketched in the issue:classandidare the device'sclassandidfrom the case file, since anidalone can repeat across classes.variablegets its own column, so queries can filter or group by variable without parsing names.sampleis needed for a unique key, since the samples just before and after an event share the samet.appendEntries), and the controller writes every format and layout from that list. This replaces the per-format virtuals inVariableMonitorBaseand the formatting code inVariableMonitor.Sink<FormatT, Layout>), resolved at compile time.static_assertinset()); every existing getter does.VariableMonitorController::addVariable.DynamicSimulation/PostgreSQLexample that runs an ACTIVSg2000 bus fault and loads the long output into PostgreSQL with Docker Compose or Podman.monitorsfor removal in a follow-up PR.Checklist
-Wall -Wpedantic -Wconversion -Wextra.Further comments
@pelesh Feedback on PR length/scope
@tsybinae Thank you for the suggestion and guidance.