My Project  0.0.16
QUCS Mapping
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
simmessage.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  simmessage.cpp
3  ----------------
4  begin : Sat Sep 6 2003
5  copyright : (C) 2003 by Michael Margraf
6  email : michael.margraf@alumni.tu-berlin.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include <stdlib.h>
19 
20 #include <qlabel.h>
21 #include <qlayout.h>
22 #include <qvgroupbox.h>
23 #include <qhgroupbox.h>
24 #include <qhbox.h>
25 #include <qtimer.h>
26 #include <qpushbutton.h>
27 #include <qprogressbar.h>
28 #include <qtextedit.h>
29 #include <qdatetime.h>
30 #include <qregexp.h>
31 
32 #include "simmessage.h"
33 #include "main.h"
34 #include "qucs.h"
35 #include "textdoc.h"
36 #include "schematic.h"
37 #include "components/opt_sim.h"
38 #include "components/vhdlfile.h"
39 
40 
41 SimMessage::SimMessage(QWidget *w, QWidget *parent)
42  : QDialog(parent, 0, FALSE, Qt::WDestructiveClose)
43 {
44  setCaption(tr("Qucs Simulation Messages"));
45  QucsDoc *Doc;
46  DocWidget = w;
47  if(DocWidget->inherits("QTextEdit"))
48  Doc = (QucsDoc*) ((TextDoc*)DocWidget);
49  else
50  Doc = (QucsDoc*) ((Schematic*)DocWidget);
51 
52  DocName = Doc->DocName;
53  DataDisplay = Doc->DataDisplay;
54  Script = Doc->Script;
55  QFileInfo Info(DocName);
56  DataSet = QDir::convertSeparators(Info.dirPath()) +
57  QDir::separator() + Doc->DataSet;
58  showBias = Doc->showBias; // save some settings as the document...
59  SimOpenDpl = Doc->SimOpenDpl; // ...could be closed during the simulation.
61 
62  all = new QVBoxLayout(this);
63  all->setSpacing(5);
64  all->setMargin(5);
65  QVGroupBox *Group1 = new QVGroupBox(tr("Progress:"),this);
66  all->addWidget(Group1);
67 
68  ProgText = new QTextEdit(Group1);
69  ProgText->setTextFormat(Qt::PlainText);
70  ProgText->setReadOnly(true);
71  ProgText->setWordWrap(QTextEdit::NoWrap);
72  ProgText->setMinimumSize(400,80);
73  wasLF = false;
74 
75  QHGroupBox *HGroup = new QHGroupBox(this);
76  HGroup->setInsideMargin(5);
77  HGroup->setInsideSpacing(5);
78  all->addWidget(HGroup);
79  new QLabel(tr("Progress:"), HGroup);
80  SimProgress = new QProgressBar(HGroup);
81 // SimProgress->setPercentageVisible(false);
82 
83  QVGroupBox *Group2 = new QVGroupBox(tr("Errors and Warnings:"),this);
84  all->addWidget(Group2);
85 
86  ErrText = new QTextEdit(Group2);
87  ErrText->setTextFormat(Qt::PlainText);
88  ErrText->setReadOnly(true);
89  ErrText->setWordWrap(QTextEdit::NoWrap);
90  ErrText->setMinimumSize(400,80);
91 
92  QHBox *Butts = new QHBox(this);
93  all->addWidget(Butts);
94 
95  Display = new QPushButton(tr("Goto display page"), Butts);
96  Display->setDisabled(true);
97  connect(Display,SIGNAL(clicked()),SLOT(slotDisplayButton()));
98 
99  Abort = new QPushButton(tr("Abort simulation"), Butts);
100  connect(Abort,SIGNAL(clicked()),SLOT(reject()));
101 }
102 
104 {
105  if(SimProcess.isRunning()) SimProcess.kill();
106  delete all;
107 }
108 
109 // ------------------------------------------------------------------------
111 {
112  Abort->setText(tr("Abort simulation"));
113  Display->setDisabled(true);
114 
115  QString txt = tr("Starting new simulation on %1 at %2").
116  arg(QDate::currentDate().toString("ddd dd. MMM yyyy")).
117  arg(QTime::currentTime().toString("hh:mm:ss"));
118  ProgText->insert(txt + "\n\n");
119 
120  SimProcess.blockSignals(false);
121  if(SimProcess.isRunning()) {
122  ErrText->insert(tr("ERROR: Simulator is still running!"));
123  FinishSimulation(-1);
124  return false;
125  }
126 
127  Collect.clear(); // clear list for NodeSets, SPICE components etc.
128  ProgText->insert(tr("creating netlist... "));
129  NetlistFile.setName(QucsHomeDir.filePath("netlist.txt"));
130  if(!NetlistFile.open(IO_WriteOnly)) {
131  ErrText->insert(tr("ERROR: Cannot write netlist file!"));
132  FinishSimulation(-1);
133  return false;
134  }
135 
136  Stream.setDevice(&NetlistFile);
137 
138  if(!DocWidget->inherits("QTextEdit")) {
139  SimPorts =
140  ((Schematic*)DocWidget)->prepareNetlist(Stream, Collect, ErrText);
141  if(SimPorts < -5) {
142  NetlistFile.close();
143  FinishSimulation(-1);
144  return false;
145  }
146  }
147  Collect.append("*"); // mark the end
148 
149 
150  disconnect(&SimProcess, 0, 0, 0);
151  connect(&SimProcess, SIGNAL(readyReadStderr()), SLOT(slotDisplayErr()));
152  connect(&SimProcess, SIGNAL(readyReadStdout()),
153  SLOT(slotReadSpiceNetlist()));
154  connect(&SimProcess, SIGNAL(processExited()),
155  SLOT(slotFinishSpiceNetlist()));
156 
157  nextSPICE();
158  return true;
159  // Since now, the Doc pointer may be obsolete, as the user could have
160  // closed the schematic !!!
161 }
162 
163 // ---------------------------------------------------
164 // Converts a spice netlist into Qucs format and outputs it.
165 void SimMessage::nextSPICE()
166 {
167  QString Line;
168  for(;;) { // search for next SPICE component
169  Line = *(Collect.begin());
170  Collect.remove(Collect.begin());
171  if(Line == "*") { // worked on all components ?
172  startSimulator(); // <<<<<================== go on ===
173  return;
174  }
175  if(Line.left(5) == "SPICE") {
176  if(Line.at(5) != 'o') insertSim = true;
177  else insertSim = false;
178  break;
179  }
180  Collect.append(Line);
181  }
182 
183 
184  QString FileName = Line.section('"', 1,1);
185  Line = Line.section('"', 2); // port nodes
186  if(Line.isEmpty()) makeSubcircuit = false;
187  else makeSubcircuit = true;
188 
189  QStringList com;
190  com << (QucsSettings.BinDir + "qucsconv");
191  if(makeSubcircuit)
192  com << "-g" << "_ref";
193  com << "-if" << "spice" << "-of" << "qucs";
194  SimProcess.setArguments(com);
195 
196  QFile SpiceFile;
197  if(FileName.find(QDir::separator()) < 0) // add path ?
198  SpiceFile.setName(QucsWorkDir.path() + QDir::separator() + FileName);
199  else
200  SpiceFile.setName(FileName);
201  if(!SpiceFile.open(IO_ReadOnly)) {
202  ErrText->insert(tr("ERROR: Cannot open SPICE file \"%1\".").arg(FileName));
203  FinishSimulation(-1);
204  return;
205  }
206 
207 
208  if(makeSubcircuit) {
209  Stream << "\n.Def:" << properName(FileName) << " ";
210 
211  Line.replace(',', ' ');
212  Stream << Line;
213  if(!Line.isEmpty()) Stream << " _ref";
214  }
215  Stream << "\n";
216 
217 
218  ProgressText = "";
219  if(!SimProcess.start()) {
220  ErrText->insert(tr("ERROR: Cannot start QucsConv!"));
221  FinishSimulation(-1);
222  return;
223  }
224 
225  QByteArray SpiceContent = SpiceFile.readAll();
226  SpiceFile.close();
227  SimProcess.writeToStdin(SpiceContent);
228  connect(&SimProcess, SIGNAL(wroteToStdin()), SLOT(slotCloseStdin()));
229 }
230 
231 // ------------------------------------------------------------------------
232 void SimMessage::slotCloseStdin()
233 {
234  SimProcess.closeStdin();
235  disconnect(&SimProcess, SIGNAL(wroteToStdin()), 0, 0);
236 }
237 
238 // ------------------------------------------------------------------------
239 void SimMessage::slotReadSpiceNetlist()
240 {
241  int i;
242  QString s;
243  ProgressText += QString(SimProcess.readStdout());
244 
245  while((i = ProgressText.find('\n')) >= 0) {
246 
247  s = ProgressText.left(i);
248  ProgressText.remove(0, i+1);
249 
250 
251  s = s.stripWhiteSpace();
252  if(s.isEmpty()) continue;
253  if(s.at(0) == '#') continue;
254  if(s.at(0) == '.') if(s.left(5) != ".Def:") { // insert simulations later
255  if(insertSim) Collect.append(s);
256  continue;
257  }
258  Stream << " " << s << '\n';
259  }
260 }
261 
262 // ------------------------------------------------------------------------
263 void SimMessage::slotFinishSpiceNetlist()
264 {
265  if(makeSubcircuit)
266  Stream << ".Def:End\n\n";
267 
268  nextSPICE();
269 }
270 
271 // ------------------------------------------------------------------------
272 #ifdef __MINGW32__
273 #include <windows.h>
274 static QString pathName(QString longpath) {
275  const char * lpath = QDir::convertSeparators(longpath).ascii();
276  char spath[2048];
277  int len = GetShortPathNameA(lpath,spath,sizeof(spath)-1);
278  spath[len] = '\0';
279  return QString(spath);
280 }
281 #else
282 static QString pathName(QString longpath) {
283  return longpath;
284 }
285 #endif
286 
287 // ------------------------------------------------------------------------
288 void SimMessage::startSimulator()
289 {
290  // Using the Doc pointer here is risky as the user may have closed
291  // the schematic, but converting the SPICE netlists is (hopefully)
292  // faster than the user (I have no other idea).
293 
294  QString SimTime;
295  QStringList CommandLine;
296  QString SimPath = QDir::convertSeparators (QucsHomeDir.absPath());
297 #ifdef __MINGW32__
298  QString QucsDigiLib = "qucsdigilib.bat";
299  QString QucsDigi = "qucsdigi.bat";
300  QString QucsVeri = "qucsveri.bat";
301 #else
302  QString QucsDigiLib = "qucsdigilib";
303  QString QucsDigi = "qucsdigi";
304  QString QucsVeri = "qucsveri";
305 #endif
306  SimOpt = NULL;
307  bool isVerilog = false;
308 
309  // Simulate text window.
310  if(DocWidget->inherits("QTextEdit")) {
311 
312  TextDoc * Doc = (TextDoc*)DocWidget;
313 
314  // Take VHDL file in memory as it could contain unsaved changes.
315  Stream << Doc->text();
316  NetlistFile.close();
317  ProgText->insert(tr("done.")+"\n"); // of "creating netlist...
318 
319  // Simulation.
320  if (Doc->simulation) {
321  SimTime = Doc->SimTime;
322  QString libs = Doc->Libraries.lower();
323  if(libs.isEmpty()) {
324  libs = "-Wl";
325  } else {
326  libs.replace(" ",",-l");
327  libs = "-Wl,-l" + libs;
328  }
329  CommandLine << pathName(QucsSettings.BinDir + QucsDigi)
330  << "netlist.txt" << DataSet << SimTime << pathName(SimPath)
331  << pathName(QucsSettings.BinDir) << libs;
332  }
333  // Module.
334  else {
335  QString text = Doc->text();
336  VHDL_File_Info VInfo (text);
337  QString entity = VInfo.EntityName.lower();
338  QString lib = Doc->Library.lower();
339  if (lib.isEmpty()) lib = "work";
340  QString dir = QDir::convertSeparators (QucsHomeDir.path());
341  QDir vhdlDir(dir);
342  if(!vhdlDir.exists("vhdl"))
343  if(!vhdlDir.mkdir("vhdl")) {
344  ErrText->insert(tr("ERROR: Cannot create VHDL directory \"%1\"!")
345  .arg(vhdlDir.path()+"/vhdl"));
346  return;
347  }
348  vhdlDir.setPath(vhdlDir.path()+"/vhdl");
349  if(!vhdlDir.exists(lib))
350  if(!vhdlDir.mkdir(lib)) {
351  ErrText->insert(tr("ERROR: Cannot create VHDL directory \"%1\"!")
352  .arg(vhdlDir.path()+"/"+lib));
353  return;
354  }
355  vhdlDir.setPath(vhdlDir.path()+"/"+lib);
356  QFile destFile;
357  destFile.setName(vhdlDir.filePath(entity+".vhdl"));
358  if(!destFile.open(IO_WriteOnly)) {
359  ErrText->insert(tr("ERROR: Cannot create \"%1\"!")
360  .arg(destFile.name()));
361  return;
362  }
363  destFile.writeBlock(text.ascii(), text.length());
364  destFile.close();
365  CommandLine << pathName(QucsSettings.BinDir + QucsDigiLib)
366  << "netlist.txt" << pathName(SimPath) << entity << lib;
367  }
368  }
369  // Simulate schematic window.
370  else {
371  // output NodeSets, SPICE simulations etc.
372  for(QStringList::Iterator it = Collect.begin();
373  it != Collect.end(); ++it) {
374  // don't put library includes into netlist...
375  if ((*it).right(4) != ".lst" &&
376  (*it).right(5) != ".vhdl" &&
377  (*it).right(4) != ".vhd" &&
378  (*it).right(2) != ".v") {
379  Stream << *it << '\n';
380  }
381  }
382  Stream << '\n';
383 
384  isVerilog = ((Schematic*)DocWidget)->isVerilog;
385  SimTime = ((Schematic*)DocWidget)->createNetlist(Stream, SimPorts);
386  if(SimTime.at(0) == '§') {
387  NetlistFile.close();
388  ErrText->insert(SimTime.mid(1));
389  FinishSimulation(-1);
390  return;
391  }
392  if (isVerilog) {
393  Stream << "\n"
394  << " initial begin\n"
395  << " $dumpfile(\"digi.vcd\");\n"
396  << " $dumpvars();\n"
397  << " #" << SimTime << " $finish;\n"
398  << " end\n\n"
399  << "endmodule // TestBench\n";
400  }
401  NetlistFile.close();
402  ProgText->insert(tr("done.")+"\n"); // of "creating netlist...
403 
404  if(SimPorts < 0) {
405  if((SimOpt = findOptimization((Schematic*)DocWidget))) {
406  ((Optimize_Sim*)SimOpt)->createASCOnetlist();
407  CommandLine << QucsSettings.AscoDir + "asco" << "-qucs" <<
408  QucsHomeDir.filePath("asco_netlist.txt") << "-o" << "asco_out";
409  }
410  else {
411  CommandLine << QucsSettings.BinDir + "qucsator" << "-b" << "-g"
412  << "-i" << QucsHomeDir.filePath("netlist.txt") << "-o" << DataSet;
413  }
414  } else {
415  if (isVerilog) {
416  CommandLine << pathName(QucsSettings.BinDir + QucsVeri)
417  << "netlist.txt" << DataSet << SimTime << pathName(SimPath)
418  << pathName(QucsSettings.BinDir) << "-c";
419  } else {
420  CommandLine << pathName(QucsSettings.BinDir + QucsDigi)
421  << "netlist.txt" << DataSet << SimTime << pathName(SimPath)
422  << pathName(QucsSettings.BinDir) << "-Wl" << "-c";
423  }
424  }
425  }
426 
427  SimProcess.setArguments(CommandLine);
428 
429  disconnect(&SimProcess, 0, 0, 0);
430  connect(&SimProcess, SIGNAL(readyReadStderr()), SLOT(slotDisplayErr()));
431  connect(&SimProcess, SIGNAL(readyReadStdout()), SLOT(slotDisplayMsg()));
432  connect(&SimProcess, SIGNAL(processExited()), SLOT(slotSimEnded()));
433 
434 #ifdef SPEEDUP_PROGRESSBAR
435  waitForUpdate = false;
436 #endif
437  wasLF = false;
438  ProgressText = "";
439  if(!SimProcess.start()) {
440  ErrText->insert(tr("ERROR: Cannot start simulator!"));
441  FinishSimulation(-1);
442  return;
443  }
444 }
445 
446 // ------------------------------------------------------------------------
447 Component * SimMessage::findOptimization(Schematic *Doc) {
448  Component *pc;
449  for(pc=Doc->Components->first(); pc!=0; pc=Doc->Components->next())
450  if(pc->isActive)
451  if(pc->Model == ".Opt")
452  return pc;
453  return NULL;
454 }
455 
456 
457 // ------------------------------------------------------------------------
458 // Is called when the process sends an output to stdout.
459 void SimMessage::slotDisplayMsg()
460 {
461  int i;
462  ProgressText += QString(SimProcess.readStdout());
463  if(wasLF) {
464  i = ProgressText.findRev('\r');
465 #ifdef __MINGW32__
466  while (i > 1 && ProgressText.at(i+1) == '\n') {
467  i = ProgressText.findRev('\r', i-1);
468  }
469 #endif /* __MINGW32__ */
470  if(i > 1) {
471 #ifdef SPEEDUP_PROGRESSBAR
472  iProgress = 10*int(ProgressText.at(i-2).latin1()-'0') +
473  int(ProgressText.at(i-1).latin1()-'0');
474  if(!waitForUpdate) {
475  QTimer::singleShot(20, this, SLOT(slotUpdateProgressBar()));
476  waitForUpdate = true;
477  }
478 #else
479  SimProgress->setProgress(
480  10*int(ProgressText.at(i-2).latin1()-'0') +
481  int(ProgressText.at(i-1).latin1()-'0'), 100);
482 #endif
483  ProgressText.remove(0, i+1);
484  }
485 
486  if(ProgressText.at(0).latin1() <= '\t')
487  return;
488  }
489  else {
490  i = ProgressText.find('\t');
491  if(i >= 0) {
492  wasLF = true;
493  ProgText->insert(ProgressText.left(i));
494  ProgressText.remove(0, i+1);
495  return;
496  }
497  }
498 
499  ProgText->insert(ProgressText);
500  ProgressText = "";
501  wasLF = false;
502 }
503 
504 #ifdef SPEEDUP_PROGRESSBAR
505 // ------------------------------------------------------------------------
506 void SimMessage::slotUpdateProgressBar()
507 {
508  SimProgress->setProgress(iProgress, 100);
509  waitForUpdate = false;
510 }
511 #endif
512 
513 // ------------------------------------------------------------------------
514 // Is called when the process sends an output to stderr.
515 void SimMessage::slotDisplayErr()
516 {
517  int par = ErrText->paragraphs();
518  int idx = ErrText->paragraphLength(par-1);
519  ErrText->setCursorPosition(par-1,idx);
520  ErrText->insert(QString(SimProcess.readStderr()));
521 }
522 
523 // ------------------------------------------------------------------------
524 // Is called when the simulation process terminates.
525 void SimMessage::slotSimEnded()
526 {
527  int stat = (!SimProcess.normalExit()) ? -1 : SimProcess.exitStatus();
528  FinishSimulation(stat);
529 }
530 
531 // ------------------------------------------------------------------------
532 // Is called when the simulation ended with errors before starting simulator
533 // process.
534 void SimMessage::FinishSimulation(int Status)
535 {
536  Abort->setText(tr("Close window"));
537  Display->setDisabled(false);
538  SimProgress->setProgress(100, 100); // progress bar to 100%
539 
540  QDate d = QDate::currentDate(); // get date of today
541  QTime t = QTime::currentTime(); // get time
542 
543  if(Status == 0) {
544  QString txt = tr("Simulation ended on %1 at %2").
545  arg(d.toString("ddd dd. MMM yyyy")).
546  arg(t.toString("hh:mm:ss"));
547  ProgText->insert("\n" + txt + "\n" + tr("Ready.") + "\n");
548  }
549  else {
550  QString txt = tr("Errors occurred during simulation on %1 at %2").
551  arg(d.toString("ddd dd. MMM yyyy")).
552  arg(t.toString("hh:mm:ss"));
553  ProgText->insert("\n" + txt + "\n" + tr("Aborted.") + "\n");
554  }
555 
556  QFile file(QucsHomeDir.filePath("log.txt")); // save simulator messages
557  if(file.open(IO_WriteOnly)) {
558  int z;
559  QTextStream stream(&file);
560  stream << tr("Output:\n-------") << "\n\n";
561  for(z=0; z<=ProgText->paragraphs(); z++)
562  stream << ProgText->text(z) << "\n";
563  stream << "\n\n\n" << tr("Errors:\n-------") << "\n\n";
564  for(z=0; z<ErrText->paragraphs(); z++)
565  stream << ErrText->text(z) << "\n";
566  file.close();
567  }
568 
569  if(Status == 0) {
570  if(SimOpt) { // save optimization data
571  QFile ifile(QucsHomeDir.filePath("asco_out.dat"));
572  QFile ofile(DataSet);
573  if(ifile.open(IO_ReadOnly)) {
574  if(ofile.open(IO_WriteOnly)) {
575  QByteArray data = ifile.readAll();
576  ofile.writeBlock(data);
577  ofile.close();
578  }
579  ifile.close();
580  }
581  if(((Optimize_Sim*)SimOpt)->loadASCOout())
582  ((Schematic*)DocWidget)->setChanged(true,true);
583  }
584  }
585 
586  emit SimulationEnded(Status, this);
587 }
588 
589 // ------------------------------------------------------------------------
590 // To call accept(), which is protected, from the outside.
592 {
593  accept();
594 }
595 
596 // ------------------------------------------------------------------------
597 void SimMessage::slotDisplayButton()
598 {
600  accept();
601 }