|
8 | 8 | #include "../HAL.h"
|
9 | 9 | #include <src/MarlinCore.h>
|
10 | 10 | #include <src/pins/pinsDebug.h>
|
| 11 | +#include <fstream> |
| 12 | +#include <vcd_writer.h> |
| 13 | +#include <regex> |
11 | 14 |
|
12 | 15 | Application::Application() {
|
13 | 16 | sim.vis.create();
|
@@ -72,67 +75,157 @@ Application::Application() {
|
72 | 75 | });
|
73 | 76 |
|
74 | 77 | user_interface.addElement<UiWindow>("Signal Analyser", [this](UiWindow* window){
|
75 |
| - struct ScrollingData { |
76 |
| - int MaxSize; |
77 |
| - int Offset; |
78 |
| - ImVector<ImPlotPoint> Data; |
79 |
| - ScrollingData() { |
80 |
| - MaxSize = 100000; |
81 |
| - Offset = 0; |
82 |
| - Data.reserve(MaxSize); |
| 78 | + if (!Gpio::isLoggingEnabled()) { |
| 79 | + if (ImGui::Button("Enable Pin Logging")) { |
| 80 | + Gpio::setLoggingEnabled(true); |
83 | 81 | }
|
84 |
| - void AddPoint(double x, double y) { |
85 |
| - if (Data.size() < MaxSize) |
86 |
| - Data.push_back(ImPlotPoint(x,y)); |
87 |
| - else { |
88 |
| - Data[Offset] = ImPlotPoint(x,y); |
89 |
| - Offset = (Offset + 1) % MaxSize; |
90 |
| - } |
| 82 | + } |
| 83 | + else { |
| 84 | + if (ImGui::Button("Disable Pin Logging")) { |
| 85 | + Gpio::setLoggingEnabled(false); |
| 86 | + } |
| 87 | + ImGui::SameLine(); |
| 88 | + if (ImGui::Button("Reset logs")) { |
| 89 | + Gpio::resetLogs(); |
91 | 90 | }
|
92 |
| - void Erase() { |
93 |
| - if (Data.size() > 0) { |
94 |
| - Data.shrink(0); |
95 |
| - Offset = 0; |
| 91 | + |
| 92 | + struct ScrollingData { |
| 93 | + int MaxSize; |
| 94 | + int Offset; |
| 95 | + ImVector<ImPlotPoint> Data; |
| 96 | + ScrollingData() { |
| 97 | + MaxSize = 100000; |
| 98 | + Offset = 0; |
| 99 | + Data.reserve(MaxSize); |
| 100 | + } |
| 101 | + void AddPoint(double x, double y) { |
| 102 | + if (Data.size() < MaxSize) |
| 103 | + Data.push_back(ImPlotPoint(x,y)); |
| 104 | + else { |
| 105 | + Data[Offset] = ImPlotPoint(x,y); |
| 106 | + Offset = (Offset + 1) % MaxSize; |
| 107 | + } |
| 108 | + } |
| 109 | + void Erase() { |
| 110 | + if (Data.size() > 0) { |
| 111 | + Data.shrink(0); |
| 112 | + Offset = 0; |
| 113 | + } |
| 114 | + } |
| 115 | + }; |
| 116 | + |
| 117 | + static pin_type monitor_pin = X_STEP_PIN; |
| 118 | + static const char* label = "Select Pin"; |
| 119 | + static char* active_label = (char *)label; |
| 120 | + if(ImGui::BeginCombo("##Select Pin", active_label)) { |
| 121 | + for (auto p : pin_array) { |
| 122 | + if (ImGui::Selectable(p.name, p.pin == monitor_pin)) { |
| 123 | + monitor_pin = p.pin; |
| 124 | + active_label = (char *)p.name; |
96 | 125 | }
|
| 126 | + if (p.pin == monitor_pin) ImGui::SetItemDefaultFocus(); |
| 127 | + } |
| 128 | + ImGui::EndCombo(); |
97 | 129 | }
|
98 |
| - }; |
99 |
| - |
100 |
| - static pin_type monitor_pin = X_STEP_PIN; |
101 |
| - static const char* label = "Select Pin"; |
102 |
| - static char* active_label = (char *)label; |
103 |
| - if(ImGui::BeginCombo("##Select Pin", active_label)) { |
104 |
| - for (auto p : pin_array) { |
105 |
| - if (ImGui::Selectable(p.name, p.pin == monitor_pin)) { |
106 |
| - monitor_pin = p.pin; |
107 |
| - active_label = (char *)p.name; |
| 130 | + |
| 131 | + if (Gpio::pin_map[monitor_pin].event_log.size()) { |
| 132 | + ScrollingData sdata; |
| 133 | + |
| 134 | + pin_log_data last{}; |
| 135 | + for (auto v : Gpio::pin_map[monitor_pin].event_log) { |
| 136 | + if (last.timestamp) sdata.AddPoint(v.timestamp, last.value); |
| 137 | + sdata.AddPoint(v.timestamp, v.value); |
| 138 | + last = v; |
| 139 | + } |
| 140 | + sdata.AddPoint(Kernel::SimulationRuntime::nanos(), last.value); |
| 141 | + |
| 142 | + static float window = 10000000000.0f; |
| 143 | + ImGui::SliderFloat("Window", &window, 10.f, 100000000000.f,"%.0f ns", ImGuiSliderFlags_Logarithmic); |
| 144 | + static float offset = 0.0f; |
| 145 | + ImGui::SliderFloat("X offset", &offset, 0.f, 10000000000.f,"%.0f ns"); |
| 146 | + ImGui::SliderFloat("X offset", &offset, 0.f, 100000000000.f,"%.0f ns"); |
| 147 | + if (!ImPlot::GetCurrentContext()) ImPlot::CreateContext(); |
| 148 | + ImPlot::SetNextPlotLimitsX(Kernel::SimulationRuntime::nanos() - window - offset, Kernel::SimulationRuntime::nanos() - offset, ImGuiCond_Always); |
| 149 | + ImPlot::SetNextPlotLimitsY(0.0f, 1.2f, ImGuiCond_Always); |
| 150 | + static int rt_axis = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_LockMin; |
| 151 | + if (ImPlot::BeginPlot("##Scrolling", "Time (ns)", NULL, ImVec2(-1,150), ImPlotAxisFlags_NoTickLabels | ImPlotFlags_Query, rt_axis, rt_axis)) { |
| 152 | + ImPlot::PlotLine("pin", &sdata.Data[0].x, &sdata.Data[0].y, sdata.Data.size(), sdata.Offset, sizeof(ImPlotPoint)); |
| 153 | + ImPlot::EndPlot(); |
108 | 154 | }
|
109 |
| - if (p.pin == monitor_pin) ImGui::SetItemDefaultFocus(); |
110 | 155 | }
|
111 |
| - ImGui::EndCombo(); |
112 |
| - } |
113 | 156 |
|
114 |
| - if (Gpio::pin_map[monitor_pin].event_log.size()) { |
115 |
| - ScrollingData sdata; |
| 157 | + static bool export_single_pin = false; |
| 158 | + if (ImGui::Button("Export selected pin to file")) { |
| 159 | + export_single_pin = true; |
| 160 | + ImGuiFileDialog::Instance()->OpenDialog("PulseExportDlgKey", "Choose File", "Value Change Dump (*.vcd){.vcd},.*", "."); |
| 161 | + } |
| 162 | + |
116 | 163 |
|
117 |
| - pin_log_data last{}; |
118 |
| - for (auto v : Gpio::pin_map[monitor_pin].event_log) { |
119 |
| - if (last.timestamp) sdata.AddPoint(v.timestamp, last.value); |
120 |
| - sdata.AddPoint(v.timestamp, v.value); |
121 |
| - last = v; |
| 164 | + if (ImGui::Button("Export pins matching regex to file")) { |
| 165 | + export_single_pin = false; |
| 166 | + ImGuiFileDialog::Instance()->OpenDialog("PulseExportDlgKey", "Choose File", "Value Change Dump (*.vcd){.vcd},.*", "."); |
| 167 | + } |
| 168 | + |
| 169 | + static char export_regex[128] = ""; |
| 170 | + ImGui::SameLine(); |
| 171 | + ImGui::InputText("Pin regex", export_regex, sizeof(export_regex)); |
| 172 | + |
| 173 | + if (ImGuiFileDialog::Instance()->Display("PulseExportDlgKey", ImGuiWindowFlags_NoDocking)) { |
| 174 | + try{ |
| 175 | + if (ImGuiFileDialog::Instance()->IsOk()) { |
| 176 | + std::string image_filename = ImGuiFileDialog::Instance()->GetFilePathName(); |
| 177 | + |
| 178 | + using namespace vcd; |
| 179 | + |
| 180 | + HeadPtr head = makeVCDHeader(static_cast<TimeScale>(50), TimeScaleUnit::ns, utils::now()); |
| 181 | + VCDWriter writer{image_filename, std::move(head)}; |
| 182 | + |
| 183 | + if (export_single_pin) { |
| 184 | + std::string pin_name(active_label); |
| 185 | + auto scope = pin_name.substr(0, pin_name.find_first_of('_')); |
| 186 | + auto var = writer.register_var(scope, pin_name, VariableType::wire, 1); |
| 187 | + |
| 188 | + if (Gpio::pin_map[monitor_pin].event_log.size() && pin_array[monitor_pin].is_digital) { |
| 189 | + for (const auto &value : Gpio::pin_map[monitor_pin].event_log) { |
| 190 | + writer.change(var, value.timestamp / 50, utils::format("%u", value.value)); |
| 191 | + } |
| 192 | + } |
| 193 | + } |
| 194 | + else { |
| 195 | + std::map<size_t, VarPtr> pin_to_var_map; |
| 196 | + std::regex expression(export_regex); |
| 197 | + |
| 198 | + for (auto pin : pin_array) { |
| 199 | + std::string pin_name(pin.name); |
| 200 | + bool regex_match = strlen(export_regex) == 0 || std::regex_search(pin_name, expression); |
| 201 | + auto scope = pin_name.substr(0, pin_name.find_first_of('_')); |
| 202 | + if (pin.is_digital && regex_match) |
| 203 | + pin_to_var_map[pin.pin] = writer.register_var(scope, pin_name, VariableType::wire, 1); |
| 204 | + } |
| 205 | + |
| 206 | + std::multimap<uint64_t, std::pair<pin_t, uint16_t> > timestamp_pin_change_map; |
| 207 | + |
| 208 | + for (auto pin : pin_array) { |
| 209 | + if (pin.is_digital && pin_to_var_map.find(pin.pin) != pin_to_var_map.end()) |
| 210 | + for (const auto &data : Gpio::pin_map[pin.pin].event_log) |
| 211 | + { |
| 212 | + timestamp_pin_change_map.emplace(std::make_pair(data.timestamp, std::make_pair(pin.pin, data.value))); |
| 213 | + } |
| 214 | + } |
| 215 | + |
| 216 | + auto timestamp_offset = timestamp_pin_change_map.begin()->first; |
| 217 | + for (const auto ×tamp : timestamp_pin_change_map) { |
| 218 | + writer.change(pin_to_var_map[timestamp.second.first], (timestamp.first - timestamp_offset) / 50, utils::format("%u", timestamp.second.second)); |
| 219 | + } |
| 220 | + } |
| 221 | + } |
| 222 | + } |
| 223 | + catch (const std::exception& e) |
| 224 | + { |
| 225 | + auto test = e.what(); |
| 226 | + } |
| 227 | + ImGuiFileDialog::Instance()->Close(); |
122 | 228 | }
|
123 |
| - sdata.AddPoint(Kernel::SimulationRuntime::nanos(), last.value); |
124 |
| - |
125 |
| - static float window = 10000000000.0f; |
126 |
| - ImGui::SliderFloat("Window", &window, 10.f, 100000000000.f,"%.0f ns"); |
127 |
| - static float offset = 0.0f; |
128 |
| - ImGui::SliderFloat("X offset", &offset, 0.f, 10000000000.f,"%.0f ns"); |
129 |
| - // ImPlot::SetNextPlotLimitsX(Kernel::SimulationRuntime::nanos() - window - offset, Kernel::SimulationRuntime::nanos() - offset, ImGuiCond_Always); |
130 |
| - // ImPlot::SetNextPlotLimitsY(0.0f, 1.2f, ImGuiCond_Always); |
131 |
| - // static int rt_axis = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_LockMin; |
132 |
| - // if (ImPlot::BeginPlot("##Scrolling", "Time (ns)", NULL, ImVec2(-1,150), ImPlotAxisFlags_NoTickLabels | ImPlotFlags_Query, rt_axis, rt_axis)) { |
133 |
| - // ImPlot::PlotLine("pin", &sdata.Data[0].x, &sdata.Data[0].y, sdata.Data.size(), sdata.Offset, sizeof(ImPlotPoint)); |
134 |
| - // ImPlot::EndPlot(); |
135 |
| - // } |
136 | 229 | }
|
137 | 230 | });
|
138 | 231 |
|
|
0 commit comments