DynExp
Highly flexible laboratory automation for dynamically changing experiments.
Loading...
Searching...
No Matches
SignalPlotter.cpp
Go to the documentation of this file.
1// This file is part of DynExp.
2
3#include "stdafx.h"
4#include "SignalPlotter.h"
5
7
9{
14
16 {
17 Running = true;
18 RollingView = false;
19 Autoscale = true;
20
22 SampleDataList.clear();
23
24 UIInitialized = false;
25 }
26
28 {
29 std::vector<DynExpInstr::DataStreamBase::BasicSampleListType> BasicSamplesSeries;
30 decltype(SignalPlotterData::SampleDataList) ProcessedSamples;
31 bool Running{};
33
34 try
35 {
36 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance.ModuleDataGetter());
37 ProcessedSamples.resize(ModuleData->GetDataStreamInstrCount());
38 Running = ModuleData->Running && !IsSavingData;
39 PlotInfo = ModuleData->PlotInfo;
40
41 if (Running)
42 {
44
45 for (size_t i = 0; i < ProcessedSamples.size(); ++i)
46 {
47 ProcessedSamples[i].Visible = ModuleData->SampleDataList.size() == ProcessedSamples.size() ? ModuleData->SampleDataList[i].Visible : true;
48 if (!ProcessedSamples[i].Visible)
49 {
50 BasicSamplesSeries.push_back({});
51 continue;
52 }
53
54 ModuleData->GetDataStreamInstr(i)->ReadData();
55
56 auto InstrData = DynExp::dynamic_InstrumentData_cast<DynExpInstr::DataStreamInstrument>(ModuleData->GetDataStreamInstr(i)->GetInstrumentData());
57 auto SampleStream = InstrData->GetSampleStream();
58
59 if (!ModuleData->RollingView || !SampleStream->SeekEqual(std::ios_base::in))
60 SampleStream->SeekBeg(std::ios_base::in);
61
62 BasicSamplesSeries.emplace_back(SampleStream->ReadBasicSamples(SampleStream->GetStreamSizeRead()));
63 PlotInfo.XUnit = (PlotInfo.XUnit == DynExp::Units::UnitType::Time_s && SampleStream->IsBasicSampleTimeUsed()) ? DynExp::Units::UnitType::Time_s : DynExp::Units::UnitType::Index;
64 }
65 }
66
68 } // ModuleData unlocked here.
69 catch (const Util::TimeoutException& e)
70 {
71 if (NumFailedUpdateAttempts++ >= 3)
72 Instance.GetOwner().SetWarning(e);
73
75 }
76
77 if (Running)
78 {
79 PlotInfo.Reset();
80 PlotInfo.GenerateSampleTimingInfo(BasicSamplesSeries);
81 for (size_t i = 0; i < ProcessedSamples.size(); ++i)
82 PlotInfo.ProcessBasicSamples(std::move(BasicSamplesSeries[i]), ProcessedSamples[i].Samples, i);
83 PlotInfo.AdjustAxesLimits();
84
85 {
86 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance.ModuleDataGetter());
87
88 ModuleData->SampleDataList = std::move(ProcessedSamples);
89 ModuleData->PlotInfo = std::move(PlotInfo);
90 } // ModuleData unlocked here.
91 }
92 else
93 {
94 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance.ModuleDataGetter());
95
96 PlotInfo.ResetHoveredSample();
97 for (size_t i = 0; i < ProcessedSamples.size(); ++i)
98 PlotInfo.ReprocessSamples(ModuleData->SampleDataList[i].Samples, i);
99
100 ModuleData->PlotInfo = std::move(PlotInfo);
101 }
102
104 }
105
111
121
122 void SignalPlotter::UpdateUIChild(const ModuleBase::ModuleDataGetterType& ModuleDataGetter)
123 {
124 auto Backend = GetBackend<SignalPlotterBackend>();
125 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(ModuleDataGetter());
126
127 // Initialize UI
128 if (!ModuleData->IsUIInitialized())
129 {
130 Backend->SetRollingView(ModuleData->RollingView);
131 Backend->SetAutoscale(ModuleData->Autoscale);
132
133 for (const auto& InstrLabel : ModuleData->GetDataStreamInstrLabels())
134 Backend->GetGraph()->InsertSeries(QString::fromStdString(InstrLabel));
135
136 ModuleData->SetUIInitialized();
137 }
138
139 // Update plotted data
140 ModuleData->Running = Backend->IsRunning();
141
142 for (size_t i = 0; i < ModuleData->GetDataStreamInstrCount(); ++i)
143 {
144 auto& SampleData = ModuleData->SampleDataList.at(i);
145 auto& Series = Backend->GetGraph()->UpdateSeries(i, SampleData.Samples, ModuleData->PlotInfo, ModuleData->Running);
146
147 SampleData.Visible = Series.Visible;
148 }
149
150 // Update axes and hovered point
151 Backend->GetGraph()->UpdateData(ModuleData->PlotInfo, ModuleData->Autoscale, ModuleData->Running);
152 }
153
155 {
156 auto ModuleParams = DynExp::dynamic_Params_cast<SignalPlotter>(Instance->ParamsGetter());
157 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->ModuleDataGetter());
158
159 ModuleData->LockInstruments(Instance, ModuleParams->DataStreamInstr);
160
161 ModuleData->PlotInfo.YUnit = ModuleData->GetDataStreamInstr(0)->GetValueUnit();
162 for (size_t i = 1; i < ModuleData->GetDataStreamInstrCount(); ++i)
163 {
164 if (ModuleData->GetDataStreamInstr(0)->GetValueUnit() != ModuleData->PlotInfo.YUnit)
165 {
167 break;
168 }
169 }
170
171 ModuleData->RollingView = ModuleParams->RollingView.Get();
172 ModuleData->Autoscale = ModuleParams->Autoscale.Get();
173 }
174
176 {
177 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->ModuleDataGetter());
178
179 ModuleData->UnlockInstruments(Instance);
180 }
181
183 {
184 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->ModuleDataGetter());
185
186 if (State)
187 {
188 for (size_t i = 0; i < ModuleData->SampleDataList.size(); ++i)
189 {
190 if (ModuleData->SampleDataList[i].Visible)
191 ModuleData->GetDataStreamInstr(i)->ResetStreamSize();
192 }
193 }
194 }
195
197 {
198 auto ModuleParams = DynExp::dynamic_Params_cast<SignalPlotter>(GetNonConstParams());
199 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->ModuleDataGetter());
200
201 ModuleParams->RollingView = State;
202 ModuleData->RollingView = State;
203 }
204
206 {
207 auto ModuleParams = DynExp::dynamic_Params_cast<SignalPlotter>(GetNonConstParams());
208 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->ModuleDataGetter());
209
210 ModuleParams->Autoscale = State;
211 ModuleData->Autoscale = State;
212 }
213
215 {
216 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(Instance->ModuleDataGetter());
217
218 for (size_t i = 0; i < ModuleData->SampleDataList.size(); ++i)
219 {
220 if (ModuleData->SampleDataList[i].Visible)
221 {
222 auto InstrData = DynExp::dynamic_InstrumentData_cast<DynExpInstr::DataStreamInstrument>(ModuleData->GetDataStreamInstr(i)->GetInstrumentData());
223
224 InstrData->GetSampleStream()->Clear();
225 }
226 }
227 }
228
230 {
232
233 // As soon as FinishedSavingDataGuard is destroyed, IsSavingData is set back to false.
234 FinishedSavingDataGuardType FinishedSavingDataGuard(*this, &SignalPlotter::FinishedSavingData);
235 IsSavingData = true;
236
237 auto Filename = Util::PromptSaveFilePathModule(GetWidget(), "Save data", ".csv", " Comma-separated values file (*.csv)");
238 if (Filename.isEmpty())
239 return;
240
241 std::stringstream CSVData;
242 {
243 auto ModuleData = DynExp::dynamic_ModuleData_cast<SignalPlotter>(GetModuleData());
244
245 using SampleIteratorType = decltype(SignalPlotterData::SampleDataType::Samples)::const_iterator;
246 std::vector<std::pair<SampleIteratorType, SampleIteratorType>> SeriesIterators;
247 auto HeaderIterator = ModuleData->GetDataStreamInstrLabels().cbegin();
248 const auto XUnit = std::string("_") + DynExp::Units::UnitTypeToStr(ModuleData->PlotInfo.XUnit);
249 const auto YUnit = std::string("_") + DynExp::Units::UnitTypeToStr(ModuleData->PlotInfo.YUnit);
250
251 for (const auto& Series : ModuleData->SampleDataList)
252 {
253 std::string Header(*HeaderIterator++);
254
255 if (!Series.Visible)
256 continue;
257
258 SeriesIterators.emplace_back(Series.Samples.cbegin(), Series.Samples.cend());
259
260 Header = Object::RemoveCategoryAndName(Header);
261 std::erase_if(Header, [](char c) { return c == ' ' || c == '_' || c == ';'; });
262 CSVData << "X_" << Header << XUnit << ";Y_" << Header << YUnit << ";";
263 }
264
265 CSVData << "\n";
266 while (std::any_of(SeriesIterators.cbegin(), SeriesIterators.cend(), [](auto& IteratorPair) { return IteratorPair.first != IteratorPair.second; }))
267 {
268 for (auto& IteratorPair : SeriesIterators)
269 {
270 if (IteratorPair.first != IteratorPair.second)
271 {
272 CSVData << IteratorPair.first->x() / std::pow(10.0, ModuleData->PlotInfo.Multiplier) << ";" << IteratorPair.first->y() << ";";
273 ++IteratorPair.first;
274 }
275 else
276 CSVData << ";;";
277 }
278
279 CSVData << "\n";
280 }
281 } // ModuleData unlocked here.
282
283 if (!Util::SaveToFile(Filename, CSVData.str()))
284 QMessageBox::warning(GetWidget(), "DynExp - Error", "Error writing data to file.");
285 }
286}
C++ backend of the SignalPlotter QML module.
Implementation of a module to plot the samples stored in data stream instruments.
void ResetImpl(dispatch_tag< QMLModuleDataBase >) override final
QList< QPointF > Samples
Data points of a single data series to plot.
std::vector< SampleDataType > SampleDataList
void OnRollingViewChanged(DynExp::ModuleInstance *Instance, bool State) const
void OnInit(DynExp::ModuleInstance *Instance) const override final
This event is triggered right before the module thread starts. Override it to lock instruments this m...
void OnExit(DynExp::ModuleInstance *Instance) const override final
This event is triggered right before the module thread terminates (not due to an exception,...
void OnAutoscaleChanged(DynExp::ModuleInstance *Instance, bool State) const
void OnRunningChanged(DynExp::ModuleInstance *Instance, bool State) const
std::atomic_bool IsSavingData
If data is currently being saved to file, do not update internal data.
Util::DynExpErrorCodes::DynExpErrorCodes ModuleMainLoop(DynExp::ModuleInstance &Instance) override final
Module main loop. The function is executed periodically by the module thread. Also refer to GetMainLo...
void MakeConnections(QObject *Backend) override final
Use QModuleBase::Connect() to connect Qt QML signals to the module's event functions.
void UpdateUIChild(const ModuleBase::ModuleDataGetterType &ModuleDataGetter) override final
void ResetImpl(dispatch_tag< QMLModuleBase >) override final
QObject SignalContext
Does nothing but ensures that this module's Qt connections are disconnected when SignalContext is des...
void OnClearStream(DynExp::ModuleInstance *Instance) const
ModuleDataTypeSyncPtrType GetModuleData(const std::chrono::milliseconds Timeout=GetModuleDataTimeoutDefault)
Locks the mutex of the module data class instance ModuleData assigned to this ModuleBase instance and...
Definition Module.cpp:219
const std::unique_ptr< ModuleDataType > ModuleData
Module data belonging to this ModuleBase instance.
Definition Module.h:789
Refer to ParamsBase::dispatch_tag.
Definition Module.h:192
Defines data for a thread belonging to a ModuleBase instance. Refer to RunnableInstance.
Definition Module.h:841
const ModuleBase::ModuleDataGetterType ModuleDataGetter
Getter for module's data. Refer to ModuleBase::ModuleDataGetterType.
Definition Module.h:873
ParamsTypeSyncPtrType GetNonConstParams(const std::chrono::milliseconds Timeout=GetParamsTimeoutDefault) const
Allows derived Objects to edit their own parameters - even in const task functions (for instruments) ...
Definition Object.cpp:480
void EnsureCallFromOwningThread() const
Asserts that the call to this function is performed from the thread which constructed this Object ins...
Definition Object.cpp:473
Refer to ParamsBase::dispatch_tag.
Definition Object.h:2026
WidgetType * GetWidget() const
Getter for Widget.
Definition Module.h:1860
void Connect(SenderType *Sender, SignalType Signal, ReceiverType *Receiver, EventType Event)
Uses Qt's connect mechanism to connect a QObject's signal to a DynExp module's event....
Definition Module.h:1825
const Object::ParamsGetterType ParamsGetter
Invoke to obtain the parameters (derived from ParamsBase) of Owner.
Definition Object.h:3718
const auto & GetOwner() const noexcept
Returns Owner.
Definition Object.h:3564
std::atomic< bool > Running
Indicates whether the RunnableObject instance is running.
Definition Object.h:2700
Holds a CallableMemberWrapper and invokes its callable when being destroyed.
Definition Util.h:495
Thrown when an operation timed out before it could be completed, especially used for locking shared d...
Definition Exception.h:262
@ Arbitrary
Arbitrary units (a.u.)
@ Index
Sample index.
const char * UnitTypeToStr(UnitType Unit)
Returns a descriptive string of the given unit's type (e.g. 'dBm').
Definition Units.cpp:104
DynExpErrorCodes
DynExp's error codes
Definition Exception.h:22
bool SaveToFile(const QString &Filename, std::string_view Text)
Saves a std::string_view to a file (using QFile). Creates a new file or truncates an existing file's ...
Definition QtUtil.cpp:236
QString PromptSaveFilePathModule(DynExp::QModuleWidget *Parent, const QString &Title, const QString &DefaultSuffix, const QString &NameFilter)
Works as PromptOpenFilePath() but asks the user to select a single file which does not need to exist....
Definition QtUtil.cpp:143
Accumulates include statements to provide a precompiled header.
DynExp::Units::UnitType XUnit
Joint unit of the plot's x axis.
Definition GraphUtil.h:104
void AdjustAxesLimits()
Adjusts MinValues and MaxValues to best display all data series.
void ReprocessSamples(const QList< QPointF > &Samples, const size_t SeriesIndex)
If data plotting is not enabled (running), the available and already plotted samples have to be repro...
void ResetHoveredSample()
Removes the stored hovered sample.
void Reset()
Resets the instance to generate information from new sample series.
Definition GraphUtil.cpp:20
bool ProcessBasicSamples(DynExpInstr::DataStreamBase::BasicSampleListType BasicSamples, QList< QPointF > &Samples, const size_t SeriesIndex)
Converts BasicSamples to displayable format and stores their minimal and maximal values in this DynEx...
void GenerateSampleTimingInfo(std::vector< DynExpInstr::DataStreamBase::BasicSampleListType > &BasicSamplesSeries)
Extracts sample timing information for x axis scaling from BasicSamples and stores the results in thi...
Definition GraphUtil.cpp:38