custom_function.cpp
Go to the documentation of this file.
1 #include "custom_function.h"
2 
3 #include <limits>
4 #include <QFile>
5 #include <QMessageBox>
6 #include <QElapsedTimer>
7 
8 CustomFunction::CustomFunction(const std::string &linkedPlot,
9  const SnippetData &snippet):
10  CustomFunction(linkedPlot,
11  snippet.name.toStdString(),
12  snippet.globalVars,
13  snippet.equation )
14 {
15 }
16 
17 QStringList CustomFunction::getChannelsFromFuntion(const QString& function)
18 {
19  QStringList output;
20  int offset = 0;
21  while(true)
22  {
23  int pos1 = function.indexOf("$$", offset);
24  if(pos1 == -1){
25  break;
26  }
27 
28  int pos2 = function.indexOf("$$", pos1+2);
29  if(pos2 == -1)
30  {
31  return {};
32  }
33  output.push_back( function.mid(pos1+2, pos2-pos1-2) );
34  offset = pos2+2;
35  }
36  return output;
37 }
38 
39 CustomFunction::CustomFunction(const std::string &linkedPlot,
40  const std::string &plotName,
41  const QString &globalVars,
42  const QString &function):
43  _linked_plot_name(linkedPlot),
44  _plot_name(plotName),
45  _global_vars(globalVars),
47  _last_updated_timestamp( - std::numeric_limits<double>::max() )
48 {
49 
50  QString qLinkedPlot = QString::fromStdString(_linked_plot_name);
51 
52  QString replaced_equation = _function;
53  while(true)
54  {
55  int pos1=replaced_equation.indexOf("$$");
56  if(pos1 == -1){
57  break;
58  }
59 
60  int pos2 = replaced_equation.indexOf("$$", pos1+2);
61  if(pos2 == -1)
62  {
63  throw std::runtime_error("syntax error : invalid use of $$ macro");
64  }
65 
66  QString channel_name = replaced_equation.mid(pos1+2, pos2-pos1-2);
67 
68  if(channel_name == qLinkedPlot)
69  {
70  // special case : user entered linkedPlot ; no need to add another channel
71  replaced_equation.replace(QStringLiteral("$$%1$$").arg(channel_name), QStringLiteral("value"));
72  }
73  else
74  {
75  QString jsExpression = QString("CHANNEL_VALUES[%1]").arg(_used_channels.size());
76  replaced_equation.replace(QStringLiteral("$$%1$$").arg(channel_name), jsExpression);
77  _used_channels.push_back(channel_name.toStdString());
78  }
79  }
80  _function_replaced = replaced_equation;
81 
82  //qDebug() << "final equation string : " << replaced_equation;
83  initJsEngine();
84 }
85 
87 {
88  bool newly_added = false;
89 
90  auto dst_data_it = plotData.numeric.find(_plot_name);
91  if(dst_data_it == plotData.numeric.end())
92  {
93  dst_data_it = plotData.addNumeric(_plot_name);
94  newly_added = true;
95  }
96 
97  PlotData& dst_data = dst_data_it->second;
98  dst_data.clear();
99 
100  try{
101  calculate(plotData, &dst_data);
102  }
103  catch(...)
104  {
105  if( newly_added )
106  {
107  plotData.numeric.erase( dst_data_it );
108  }
109  std::rethrow_exception( std::current_exception() );
110  }
111 }
112 
114 {
115  _jsEngine = std::unique_ptr<QJSEngine>( new QJSEngine() );
116 
117  QJSValue globalVarResult = _jsEngine->evaluate(_global_vars);
118  if(globalVarResult.isError())
119  {
120  throw std::runtime_error("JS Engine : " + globalVarResult.toString().toStdString());
121  }
122  QString calcMethodStr = QString("function calc(time, value, CHANNEL_VALUES){with (Math){\n%1\n}}").arg(_function_replaced);
123  _jsEngine->evaluate(calcMethodStr);
124 }
125 
126 PlotData::Point CustomFunction::calculatePoint(QJSValue& calcFct,
127  const PlotData& src_data,
128  const std::vector<const PlotData*>& channels_data,
129  QJSValue& chan_values,
130  size_t point_index)
131 {
132  const PlotData::Point &old_point = src_data.at(point_index);
133 
134  int chan_index = 0;
135  for(const PlotData* chan_data: channels_data)
136  {
137  double value;
138  int index = chan_data->getIndexFromX(old_point.x);
139  if(index != -1){
140  value = chan_data->at(index).y;
141  }
142  else{
143  value = std::numeric_limits<double>::quiet_NaN();
144  }
145  chan_values.setProperty(static_cast<quint32>(chan_index++), QJSValue(value));
146  }
147 
148  PlotData::Point new_point;
149  new_point.x = old_point.x;
150 
151  QJSValue jsData = calcFct.call({QJSValue(old_point.x), QJSValue(old_point.y), chan_values});
152  if(jsData.isError())
153  {
154  throw std::runtime_error("JS Engine : " + jsData.toString().toStdString());
155  }
156  new_point.y = jsData.toNumber();
157 
158  return new_point;
159 }
160 
161 void CustomFunction::calculate(const PlotDataMapRef &plotData, PlotData* dst_data)
162 {
163  QJSValue calcFct = _jsEngine->evaluate("calc");
164 
165  if(calcFct.isError())
166  {
167  throw std::runtime_error("JS Engine : " + calcFct.toString().toStdString());
168  }
169 
170  auto src_data_it = plotData.numeric.find(_linked_plot_name);
171  if(src_data_it == plotData.numeric.end())
172  {
173  // failed! keep it empty
174  return;
175  }
176  const PlotData& src_data = src_data_it->second;
177 
178  // clean up old data
179  dst_data->setMaximumRangeX( src_data.maximumRangeX() );
180 
181  std::vector<const PlotData*> channel_data;
182  channel_data.reserve(_used_channels.size());
183 
184  for(const auto& channel: _used_channels)
185  {
186  auto it = plotData.numeric.find(channel);
187  if(it == plotData.numeric.end())
188  {
189  throw std::runtime_error("Invalid channel name");
190  }
191  const PlotData* chan_data = &(it->second);
192  channel_data.push_back(chan_data);
193  }
194 
195  QJSValue chan_values = _jsEngine->newArray(static_cast<quint32>(_used_channels.size()));
196 
197  for(size_t i=0; i < src_data.size(); ++i)
198  {
199  if( src_data.at(i).x > _last_updated_timestamp)
200  {
201  dst_data->pushBack( calculatePoint(calcFct, src_data, channel_data, chan_values, i ) );
202  }
203  }
204  if (dst_data->size() != 0){
205  _last_updated_timestamp = dst_data->back().x;
206  }
207 }
208 
209 const std::string &CustomFunction::name() const
210 {
211  return _plot_name;
212 }
213 
214 const std::string &CustomFunction::linkedPlotName() const
215 {
216  return _linked_plot_name;
217 }
218 
219 const QString &CustomFunction::globalVars() const
220 {
221  return _global_vars;
222 }
223 
224 const QString &CustomFunction::function() const
225 {
226  return _function;
227 }
228 
229 
230 
231 QDomElement CustomFunction::xmlSaveState(QDomDocument &doc) const
232 {
233  QDomElement snippet = doc.createElement("snippet");
234  snippet.setAttribute("name", QString::fromStdString(_plot_name) );
235 
236  QDomElement linked = doc.createElement("linkedPlot");
237  linked.appendChild( doc.createTextNode( QString::fromStdString(_linked_plot_name)) );
238  snippet.appendChild(linked);
239 
240  QDomElement global = doc.createElement("global");
241  global.appendChild( doc.createTextNode(_global_vars) );
242  snippet.appendChild(global);
243 
244  QDomElement equation = doc.createElement("equation");
245  equation.appendChild( doc.createTextNode(_function) );
246  snippet.appendChild(equation);
247 
248  return snippet;
249 }
250 
252 {
253  auto name = element.attribute("name").toStdString();
254  auto linkedPlot = element.firstChildElement("linkedPlot").text().trimmed().toStdString();
255  auto globalVars = element.firstChildElement("global").text().trimmed();
256  auto calcEquation = element.firstChildElement("equation").text().trimmed();
257 
258  return std::make_shared<CustomFunction>(linkedPlot, name, globalVars, calcEquation );
259 }
260 
261 SnippetsMap GetSnippetsFromXML(const QString& xml_text)
262 {
263  if( xml_text.isEmpty() ) return {};
264 
265  QDomDocument doc;
266  QString parseErrorMsg;
267  int parseErrorLine;
268  if(!doc.setContent(xml_text, &parseErrorMsg, &parseErrorLine))
269  {
270  QMessageBox::critical(nullptr, "Error",
271  QString("Failed to parse snippets (xml), error %1 at line %2")
272  .arg(parseErrorMsg).arg(parseErrorLine));
273  return {};
274  }
275  else
276  {
277  QDomElement snippets_element = doc.documentElement();
278  return GetSnippetsFromXML(snippets_element);
279  }
280 }
281 
282 SnippetsMap GetSnippetsFromXML(const QDomElement &snippets_element)
283 {
284  SnippetsMap snippets;
285 
286  for (auto elem = snippets_element.firstChildElement("snippet");
287  !elem.isNull();
288  elem = elem.nextSiblingElement("snippet"))
289  {
290  SnippetData snippet;
291  snippet.name = elem.attribute("name");
292  snippet.globalVars = elem.firstChildElement("global").text().trimmed();
293  snippet.equation = elem.firstChildElement("equation").text().trimmed();
294  snippets.insert( {snippet.name, snippet } );
295  }
296  return snippets;
297 }
298 
299 QDomElement ExportSnippets(const SnippetsMap &snippets, QDomDocument &doc)
300 {
301  auto snippets_root = doc.createElement("snippets");
302 
303  for (const auto& it: snippets )
304  {
305  const auto& snippet = it.second;
306 
307  auto element = doc.createElement("snippet");
308  element.setAttribute("name", it.first);
309 
310  auto global_el = doc.createElement("global");
311  global_el.appendChild( doc.createTextNode( snippet.globalVars ) );
312 
313  auto equation_el = doc.createElement("equation");
314  equation_el.appendChild( doc.createTextNode( snippet.equation ) );
315 
316  element.appendChild( global_el );
317  element.appendChild( equation_el );
318  snippets_root.appendChild( element );
319  }
320  return snippets_root;
321 }
322 
const QString _function
std::unordered_map< std::string, PlotData > numeric
Definition: plotdata.h:144
void setMaximumRangeX(Time max_range)
Definition: plotdata.h:327
const Point & at(size_t index) const
Definition: plotdata.h:288
std::vector< std::string > _used_channels
QString globalVars
PlotData::Point calculatePoint(QJSValue &calcFct, const PlotData &src_data, const std::vector< const PlotData * > &channels_data, QJSValue &chan_values, size_t point_index)
void pushBack(Point p)
Definition: plotdata.h:214
std::map< QString, SnippetData > SnippetsMap
CustomFunction(const std::string &linkedPlot, const std::string &plotName, const QString &globalVars, const QString &function)
const QString _global_vars
QString equation
const std::string & name() const
std::unique_ptr< QJSEngine > _jsEngine
QDomElement ExportSnippets(const SnippetsMap &snippets, QDomDocument &doc)
const std::string & linkedPlotName() const
TFSIMD_FORCE_INLINE const tfScalar & x() const
static QStringList getChannelsFromFuntion(const QString &function)
const std::string _plot_name
void calculate(const PlotDataMapRef &plotData, PlotData *dst_data)
const QString & globalVars() const
T value
std::shared_ptr< CustomFunction > CustomPlotPtr
void clear()
Definition: plotdata.h:301
double _last_updated_timestamp
SnippetsMap GetSnippetsFromXML(const QString &xml_text)
virtual size_t size() const
Definition: plotdata.h:308
char name[1]
QString _function_replaced
static CustomPlotPtr createFromXML(QDomElement &element)
const std::string _linked_plot_name
void * arg
std::unordered_map< std::string, PlotData >::iterator addNumeric(const std::string &name)
Definition: plotdata.h:147
const Point & back() const
Definition: plotdata.h:114
const QString & function() const
int i
Time maximumRangeX() const
Definition: plotdata.h:110
QDomElement xmlSaveState(QDomDocument &doc) const
void calculateAndAdd(PlotDataMapRef &plotData)


plotjuggler
Author(s): Davide Faconti
autogenerated on Sat Jul 6 2019 03:44:17