Qucs-GUI  0.0.18
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
spicefile.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  spicefile.cpp
3  ---------------
4  begin : Tue Dez 28 2004
5  copyright : (C) 2004 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 #if HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 
22 #if HAVE_UNISTD_H
23 # include <unistd.h>
24 #endif
25 #include <QtGui>
26 #include <QRegExp>
27 #include <QProcess>
28 #include <QString>
29 #include <QStringList>
30 #include <QMessageBox>
31 #include <QTextStream>
32 #include <QFile>
33 #include <QDir>
34 #include <QFileInfo>
35 
36 #include "spicefile.h"
37 #include "schematic.h"
38 #include "main.h"
39 #include "qucs.h"
40 
41 
43 {
44  Description = QObject::tr("SPICE netlist file");
45  // Property descriptions not needed, but must not be empty !
46  Props.append(new Property("File", "", true, QString("x")));
47  Props.append(new Property("Ports", "", false, QString("x")));
48  Props.append(new Property("Sim", "yes", false, QString("x")));
49  Props.append(new Property("Preprocessor", "none", false, QString("x")));
50  withSim = false;
51 
52  Model = "SPICE";
53  Name = "X";
54  changed = false;
55 
56  // Do NOT call createSymbol() here. But create port to let it rotate.
57  Ports.append(new Port(0, 0));
58 }
59 
60 // -------------------------------------------------------
62 {
63  SpiceFile *p = new SpiceFile();
64  p->recreate(0); // createSymbol() is NOT called in constructor !!!
65  return p;
66 }
67 
68 // -------------------------------------------------------
69 Element* SpiceFile::info(QString& Name, char* &BitmapFile, bool getNewOne)
70 {
71  Name = QObject::tr("SPICE netlist");
72  BitmapFile = (char *) "spicefile";
73 
74  if(getNewOne) {
75  SpiceFile *p = new SpiceFile();
76  p->recreate(0); // createSymbol() is NOT called in constructor !!!
77  return p;
78  }
79  return 0;
80 }
81 
82 // -------------------------------------------------------
84 {
85  QFontMetrics metrics(QucsSettings.font); // get size of text
86  int fHeight = metrics.lineSpacing();
87 
88  int No = 0;
89  QString tmp, PortNames = Props.at(1)->Value;
90  if(!PortNames.isEmpty()) No = PortNames.count(',') + 1;
91 
92  #define HALFWIDTH 17
93  int h = 30*((No-1)/2) + 15;
94  Lines.append(new Line(-HALFWIDTH, -h, HALFWIDTH, -h,QPen(Qt::darkBlue,2)));
95  Lines.append(new Line( HALFWIDTH, -h, HALFWIDTH, h,QPen(Qt::darkBlue,2)));
96  Lines.append(new Line(-HALFWIDTH, h, HALFWIDTH, h,QPen(Qt::darkBlue,2)));
97  Lines.append(new Line(-HALFWIDTH, -h,-HALFWIDTH, h,QPen(Qt::darkBlue,2)));
98 
99  int w, i = fHeight/2;
100  if(withSim) {
101  i = fHeight - 2;
102  tmp = QObject::tr("sim");
103  w = metrics.width(tmp);
104  Texts.append(new Text(w/-2, 0, tmp, Qt::red));
105  }
106  tmp = QObject::tr("spice");
107  w = metrics.width(tmp);
108  Texts.append(new Text(w/-2, -i, tmp));
109 
110 
111  i = 0;
112  int y = 15-h;
113  while(i<No) {
114  Lines.append(new Line(-30, y,-HALFWIDTH, y,QPen(Qt::darkBlue,2)));
115  Ports.append(new Port(-30, y));
116  tmp = PortNames.section(',', i, i).mid(4);
117  w = metrics.width(tmp);
118  Texts.append(new Text(-20-w, y-fHeight-2, tmp));
119  i++;
120 
121  if(i == No) break;
122  Lines.append(new Line(HALFWIDTH, y, 30, y,QPen(Qt::darkBlue,2)));
123  Ports.append(new Port( 30, y));
124  tmp = PortNames.section(',', i, i).mid(4);
125  Texts.append(new Text( 20, y-fHeight-2, tmp));
126  y += 60;
127  i++;
128  }
129 
130  if(No > 0) {
131  Lines.append(new Line( 0, h, 0,h+15,QPen(Qt::darkBlue,2)));
132  Texts.append(new Text( 4, h,"Ref"));
133  Ports.append(new Port( 0, h+15)); // 'Ref' port
134  }
135 
136  x1 = -30; y1 = -h-2;
137  x2 = 30; y2 = h+15;
138 
139  tx = x1+4;
140  ty = y1 - fHeight - 4;
141  if(Props.first()->display) ty -= fHeight;
142  changed = true;
143 }
144 
145 // ---------------------------------------------------
147 {
148  if(Props.at(1)->Value.isEmpty())
149  return QString(""); // no ports, no subcircuit instance
150 
151  QString s = "Sub:"+Name; // SPICE netlist is subcircuit
152  for(Port *pp = Ports.first(); pp != 0; pp = Ports.next())
153  s += " "+pp->Connection->Name; // output all node names
154 
155  QString f = properFileName(Props.first()->Value);
156  s += " Type=\""+properName(f)+"\"\n";
157  return s;
158 }
159 
160 // -------------------------------------------------------
162 {
163  // construct full filename
164  QString FileName = Props.getFirst()->Value;
165 
166  if (FileName.isEmpty())
167  {
168  return properAbsFileName(FileName);
169  }
170 
171  QFileInfo FileInfo(FileName);
172 
173  if (FileInfo.exists())
174  {
175  // the file must be an absolute path to a schematic file
176  return FileInfo.absoluteFilePath();
177  }
178  else
179  {
180  // get the complete base name (everything except the last '.'
181  // and whatever follows
182  QString baseName = FileInfo.completeBaseName();
183 
184  // if only a file name is supplied, first check if it is in the
185  // same directory as the schematic file it is a part of
186  if (FileInfo.fileName () == FileName)
187  {
188  // the file has no path information, just the file name
190  {
191  // check if a file of the same name is in the same directory
192  // as the schematic file, if we have a pointer to it, in
193  // which case we use this one
194  QFileInfo schematicFileInfo = containingSchematic->getFileInfo ();
195 
196  for (int i = 0; i < QucsMain->spiceExtensions.count (); i++)
197  {
198  QFileInfo localFIleInfo (schematicFileInfo.canonicalPath ()
199  + "/" + baseName + QucsMain->spiceExtensions[i]);
200  if (localFIleInfo.exists ())
201  {
202  // return the subcircuit saved in the same directory
203  // as the schematic file
204  return localFIleInfo.absoluteFilePath();
205  }
206  }
207  }
208  }
209 
210  // look up the hash table for the schematic file as
211  // it does not seem to be an absolute path, this will also
212  // search the home directory which is always hashed
213  QMutex mutex;
214  mutex.lock();
215  QString hashsearchresult = QucsMain->spiceNameHash.value(baseName);
216  mutex.unlock();
217 
218  if (hashsearchresult.isEmpty())
219  {
220  // the schematic was not found in the hash table, return
221  // what would always have been returned in this case
222  return properAbsFileName(FileName);
223  }
224  else
225  {
226  // we found an entry in the hash table, check it actually still exists
227  FileInfo.setFile(hashsearchresult);
228 
229  if (FileInfo.exists())
230  {
231  // it does exist so return the absolute file path
232  return FileInfo.absoluteFilePath();
233  }
234  else
235  {
236  // the schematic file does not actually exist, return
237  // what would always have been returned in this case
238  return properAbsFileName(FileName);
239  }
240  }
241 
242  }
243 
244 }
245 
246 // -------------------------------------------------------------------------
247 bool SpiceFile::createSubNetlist(QTextStream *stream)
248 {
249  // check file name
250  QString FileName = Props.first()->Value;
251  if(FileName.isEmpty()) {
252  ErrText += QObject::tr("ERROR: No file name in SPICE component \"%1\".").
253  arg(Name);
254  return false;
255  }
256 
257  // check input and output file
258  QFile SpiceFile, ConvFile;
259  FileName = getSubcircuitFile();
260  SpiceFile.setName(FileName);
261  if(!SpiceFile.open(QIODevice::ReadOnly)) {
262  ErrText += QObject::tr("ERROR: Cannot open SPICE file \"%1\".").
263  arg(FileName);
264  return false;
265  }
266  SpiceFile.close();
267  QString ConvName = SpiceFile.name() + ".lst";
268  ConvFile.setName(ConvName);
269  QFileInfo Info(ConvName);
270 
271  // re-create converted file if necessary
272  if(changed || !ConvFile.exists() ||
273  (lastLoaded.isValid() && lastLoaded < Info.lastModified())) {
274  if(!ConvFile.open(QIODevice::WriteOnly)) {
275  ErrText += QObject::tr("ERROR: Cannot save converted SPICE file \"%1\".").
276  arg(FileName + ".lst");
277  return false;
278  }
279  outstream = stream;
280  filstream = new QTextStream(&ConvFile);
281  QString SpiceName = SpiceFile.name();
282  bool ret = recreateSubNetlist(&SpiceName, &FileName);
283  ConvFile.close();
284  delete filstream;
285  return ret;
286  }
287 
288  // load old file and stuff into stream
289  if(!ConvFile.open(QIODevice::ReadOnly)) {
290  ErrText += QObject::tr("ERROR: Cannot open converted SPICE file \"%1\".").
291  arg(FileName + ".lst");
292  return false;
293  }
294  QByteArray FileContent = ConvFile.readAll();
295  ConvFile.close();
296  //? stream->writeRawBytes(FileContent.data(), FileContent.size());
297  (*stream) << FileContent.data();
298  return true;
299 }
300 
301 // -------------------------------------------------------------------------
302 bool SpiceFile::recreateSubNetlist(QString *SpiceFile, QString *FileName)
303 {
304  // initialize collectors
305  ErrText = "";
306  NetText = "";
307  SimText = "";
308  NetLine = "";
309 
310  // evaluate properties
311  if(Props.at(1)->Value != "")
312  makeSubcircuit = true;
313  else
314  makeSubcircuit = false;
315  if(Props.at(2)->Value == "yes")
316  insertSim = true;
317  else
318  insertSim = false;
319 
320  // preprocessor run if necessary
321  QString preprocessor = Props.at(3)->Value;
322  if (preprocessor != "none") {
323  bool piping = true;
324  QStringList script;
325 #ifdef __MINGW32__
326  QString interpreter = "tinyperl.exe";
327 #else
328  QString interpreter = "perl";
329 #endif
330  if (preprocessor == "ps2sp") {
331  script << "ps2sp";
332  } else if (preprocessor == "spicepp") {
333  script << "spicepp.pl";
334  } else if (preprocessor == "spiceprm") {
335  script << "spiceprm";
336  piping = false;
337  }
338  SpicePrep = new QProcess(this);
339  script << interpreter;
340  script << script;
341  script << *SpiceFile;
342 
343  QFile PrepFile;
344  QString PrepName = *SpiceFile + ".pre";
345 
346  if (!piping) {
347  script << PrepName;
348  connect(SpicePrep, SIGNAL(readyReadStandardOutput()), SLOT(slotSkipOut()));
349  connect(SpicePrep, SIGNAL(readyReadStandardError()), SLOT(slotGetPrepErr()));
350  } else {
351  connect(SpicePrep, SIGNAL(readyReadStandardOutput()), SLOT(slotGetPrepOut()));
352  connect(SpicePrep, SIGNAL(readyReadStandardError()), SLOT(slotGetPrepErr()));
353  }
354 
355  QMessageBox *MBox = new QMessageBox(QObject::tr("Info"),
356  QObject::tr("Preprocessing SPICE file \"%1\".").arg(*SpiceFile),
357  QMessageBox::NoIcon, QMessageBox::Abort,
358  QMessageBox::NoButton, QMessageBox::NoButton, 0, 0, true,
359  Qt::WStyle_DialogBorder | Qt::WDestructiveClose);
360  connect(SpicePrep, SIGNAL(finished(int)), MBox, SLOT(close()));
361 
362  if (piping) {
363  PrepFile.setName(PrepName);
364  if(!PrepFile.open(QIODevice::WriteOnly)) {
365  ErrText +=
366  QObject::tr("ERROR: Cannot save preprocessed SPICE file \"%1\".").
367  arg(PrepName);
368  return false;
369  }
370  prestream = new QTextStream(&PrepFile);
371  }
372 
373  QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
374  env.insert("PATH", env.value("PATH") );
375  SpicePrep->setProcessEnvironment(env);
376  SpicePrep->start(script.join(" "));
377  //QucsHelp->setCommunication(0);
378 
379  if(SpicePrep->state()!=QProcess::Running&&
380  SpicePrep->state()!=QProcess::Starting) {
381  ErrText += QObject::tr("ERROR: Cannot execute \"%1\".").
382  arg(interpreter + " " + script.join(" ") + "\".");
383  if (piping) {
384  PrepFile.close();
385  delete prestream;
386  }
387  return false;
388  }
389  //SpicePrep->closeStdin();
390 
391  MBox->exec();
392  delete SpicePrep;
393  if (piping) {
394  PrepFile.close();
395  delete prestream;
396  }
397  *SpiceFile = PrepName;
398  }
399 
400  QString executableSuffix = "";
401 #ifdef __MINGW32__
402  executableSuffix = ".exe";
403 #endif
404 
405  // begin command line construction
406  QString prog;
407  QStringList com;
408  prog = QucsSettings.BinDir + "qucsconv" + executableSuffix;
409 
410  if(makeSubcircuit) com << "-g" << "_ref";
411  com << "-if" << "spice" << "-of" << "qucs";
412  com << "-i" << *SpiceFile;
413 
414  // begin netlist text creation
415  if(makeSubcircuit) {
416  QString f = properFileName(*FileName);
417  NetText += "\n.Def:" + properName(f) + " ";
418  QString PortNames = Props.at(1)->Value;
419  PortNames.replace(',', ' ');
420  NetText += PortNames;
421  if(makeSubcircuit) NetText += " _ref";
422  }
423  NetText += "\n";
424 
425  // startup SPICE conversion process
426  QucsConv = new QProcess(this);
427  QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
428  env.insert("PATH", env.value("PATH") );
429  QucsConv->setProcessEnvironment(env);
430 
431  qDebug() << "Command:" << prog << com.join(" ");
432 // QucsConv->start(com.join(" "));
433  QucsConv->start(prog, com);
434 
436  connect(QucsConv, SIGNAL(readyReadStandardOutput()), SLOT(slotGetNetlist()));
437  connect(QucsConv, SIGNAL(readyReadStandardError()), SLOT(slotGetError()));
438  connect(QucsConv, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(slotExited()));
439 
440  if(QucsConv->state()!=QProcess::Running&&
441  QucsConv->state()!=QProcess::Starting) {
442  ErrText += QObject::tr("COMP ERROR: Cannot start QucsConv!");
443  return false;
444  }
445  (*outstream) << NetText;
446  (*filstream) << NetText;
447 
448  // waiting info dialog box
449  QMessageBox *MBox = new QMessageBox(QObject::tr("Info"),
450  QObject::tr("Converting SPICE file \"%1\".").arg(*SpiceFile),
451  QMessageBox::NoIcon, QMessageBox::Abort,
452  QMessageBox::NoButton, QMessageBox::NoButton, 0, 0, true,
453  Qt::WStyle_DialogBorder | Qt::WDestructiveClose);
454  connect(QucsConv, SIGNAL(finished(int)), MBox, SLOT(close()));
455  MBox->exec();
456 
457  // finish
458  delete QucsConv;
459  lastLoaded = QDateTime::currentDateTime();
460  return true;
461 }
462 
463 // -------------------------------------------------------------------------
465 {
466  SpicePrep->readAllStandardError();
467 }
468 
469 // -------------------------------------------------------------------------
471 {
472  SpicePrep->readAllStandardOutput();
473 }
474 
475 // -------------------------------------------------------------------------
477 {
478  ErrText += QString(SpicePrep->readAllStandardError());
479 }
480 
481 // -------------------------------------------------------------------------
483 {
484  (*prestream) << QString(SpicePrep->readAllStandardOutput());
485 }
486 
487 // -------------------------------------------------------------------------
489 {
490  ErrText += QString(QucsConv->readAllStandardError());
491 }
492 
493 // -------------------------------------------------------------------------
495 {
496  int i;
497  QString s;
498  NetLine += QString(QucsConv->readAllStandardOutput());
499 
500  while((i = NetLine.find('\n')) >= 0) {
501  s = NetLine.left(i);
502  NetLine.remove(0, i+1);
503  s = s.stripWhiteSpace();
504  if(s.size()>0&&s.at(0) == '#') {
505  continue;
506  } else if(s.isEmpty()) {
507  continue;
508  } else if(s.size()>0&&s.at(0) == '.') {
509  if(s.left(5) != ".Def:") {
510  if(insertSim) SimText += s + "\n";
511  continue;
512  }
513  }
514  if(makeSubcircuit) {
515  (*outstream) << " ";
516  (*filstream) << " ";
517  }
518  (*outstream) << s << "\n";
519  (*filstream) << s << "\n";
520  }
521 }
522 
523 // -------------------------------------------------------------------------
525 {
526  if (QucsConv->exitStatus() != QProcess::NormalExit) {
527  NetText = "";
528  }
529  else {
530  if(makeSubcircuit) {
531  (*outstream) << ".Def:End\n\n";
532  (*filstream) << ".Def:End\n\n";
533  }
534  if(!SimText.isEmpty()) {
535  (*outstream) << SimText;
536  (*filstream) << SimText;
537  }
538  }
539 }
Q3PtrList< Line > Lines
Definition: component.h:67
bool insertSim
Definition: spicefile.h:47
QTextStream * prestream
Definition: spicefile.h:51
void slotExited()
Definition: spicefile.cpp:524
QString properFileName(const QString &Name)
Definition: main.cpp:468
int y1
Definition: element.h:153
bool withSim
Definition: spicefile.h:40
bool createSubNetlist(QTextStream *)
Definition: spicefile.cpp:247
QFont font
Definition: main.h:46
QString netlist()
Definition: spicefile.cpp:146
QDateTime lastLoaded
Definition: spicefile.h:52
tQucsSettings QucsSettings
Definition: main.cpp:52
int tx
Definition: component.h:78
int y2
Definition: element.h:153
Definition: element.h:72
int x1
Definition: element.h:153
QFileInfo getFileInfo(void)
Definition: schematic.h:147
QTextStream * outstream
Definition: spicefile.h:51
QString BinDir
Definition: main.h:57
bool changed
Definition: spicefile.h:48
void slotSkipOut()
Definition: spicefile.cpp:470
void slotGetNetlist()
Definition: spicefile.cpp:494
Definitions and declarations for the main application.
int ty
Definition: component.h:78
QucsApp * QucsMain
Definition: main.cpp:54
QString properAbsFileName(const QString &Name)
Definition: main.cpp:452
QStringList spiceExtensions
Definition: qucs.h:77
Q3PtrList< Property > Props
Definition: component.h:72
void slotGetError()
Definition: spicefile.cpp:488
QString SimText
Definition: spicefile.h:50
#define HALFWIDTH
QHash< QString, QString > spiceNameHash
Definition: qucs.h:79
void createSymbol()
Definition: spicefile.cpp:83
QProcess * SpicePrep
Definition: spicefile.h:49
void slotGetPrepErr()
Definition: spicefile.cpp:476
Definition: element.h:82
Definition: element.h:48
Superclass of all schematic drawing elements.
Definition: element.h:142
void recreate(Schematic *)
Definition: component.cpp:1250
QProcess * QucsConv
Definition: spicefile.h:49
Q3PtrList< Port > Ports
Definition: component.h:70
QString NetLine
Definition: spicefile.h:50
QString ErrText
Definition: spicefile.h:50
QString Name
Definition: component.h:80
Q3PtrList< Text > Texts
Definition: component.h:71
#define executableSuffix
Definition: simmessage.cpp:45
Component * newOne()
Definition: spicefile.cpp:61
QString Model
Definition: component.h:80
Schematic * containingSchematic
Definition: component.h:96
QString Description
Definition: component.h:81
void slotGetPrepOut()
Definition: spicefile.cpp:482
QString properName(const QString &Name)
Definition: main.cpp:476
bool recreateSubNetlist(QString *, QString *)
Definition: spicefile.cpp:302
QString NetText
Definition: spicefile.h:50
QTextStream * filstream
Definition: spicefile.h:51
static Element * info(QString &, char *&, bool getNewOne=false)
Definition: spicefile.cpp:69
int x2
Definition: element.h:153
bool makeSubcircuit
Definition: spicefile.h:46
QString getSubcircuitFile()
Definition: spicefile.cpp:161
void slotSkipErr()
Definition: spicefile.cpp:464