My Project  0.0.16
QUCS Mapping
 All Classes Namespaces 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 
26 #include <qregexp.h>
27 #include <qprocess.h>
28 #include <qstring.h>
29 #include <qstringlist.h>
30 #include <qmessagebox.h>
31 #include <qtextstream.h>
32 #include <qfile.h>
33 #include <qdir.h>
34 #include <qfileinfo.h>
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.contains(',') + 1;
91 
92  #define HALFWIDTH 17
93  int h = 30*((No-1)/2) + 15;
94  Lines.append(new Line(-HALFWIDTH, -h, HALFWIDTH, -h,QPen(QPen::darkBlue,2)));
95  Lines.append(new Line( HALFWIDTH, -h, HALFWIDTH, h,QPen(QPen::darkBlue,2)));
96  Lines.append(new Line(-HALFWIDTH, h, HALFWIDTH, h,QPen(QPen::darkBlue,2)));
97  Lines.append(new Line(-HALFWIDTH, -h,-HALFWIDTH, h,QPen(QPen::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(QPen::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(QPen::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(QPen::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  return properAbsFileName(FileName);
166 }
167 
168 // -------------------------------------------------------------------------
169 bool SpiceFile::createSubNetlist(QTextStream *stream)
170 {
171  // check file name
172  QString FileName = Props.first()->Value;
173  if(FileName.isEmpty()) {
174  ErrText += QObject::tr("ERROR: No file name in SPICE component \"%1\".").
175  arg(Name);
176  return false;
177  }
178 
179  // check input and output file
180  QFile SpiceFile, ConvFile;
181  FileName = getSubcircuitFile();
182  SpiceFile.setName(FileName);
183  if(!SpiceFile.open(IO_ReadOnly)) {
184  ErrText += QObject::tr("ERROR: Cannot open SPICE file \"%1\".").
185  arg(FileName);
186  return false;
187  }
188  SpiceFile.close();
189  QString ConvName = SpiceFile.name() + ".lst";
190  ConvFile.setName(ConvName);
191  QFileInfo Info(ConvName);
192 
193  // re-create converted file if necessary
194  if(changed || !ConvFile.exists() ||
195  (lastLoaded.isValid() && lastLoaded < Info.lastModified())) {
196  if(!ConvFile.open(IO_WriteOnly)) {
197  ErrText +=
198  QObject::tr("ERROR: Cannot save converted SPICE file \"%1\".").
199  arg(FileName + ".lst");
200  return false;
201  }
202  outstream = stream;
203  filstream = new QTextStream(&ConvFile);
204  QString SpiceName = SpiceFile.name();
205  bool ret = recreateSubNetlist(&SpiceName, &FileName);
206  ConvFile.close();
207  delete filstream;
208  return ret;
209  }
210 
211  // load old file and stuff into stream
212  if(!ConvFile.open(IO_ReadOnly)) {
213  ErrText +=
214  QObject::tr("ERROR: Cannot open converted SPICE file \"%1\".").
215  arg(FileName + ".lst");
216  return false;
217  }
218  QByteArray FileContent = ConvFile.readAll();
219  ConvFile.close();
220  stream->writeRawBytes(FileContent.data(), FileContent.size());
221  return true;
222 }
223 
224 // -------------------------------------------------------------------------
225 bool SpiceFile::recreateSubNetlist(QString *SpiceFile, QString *FileName)
226 {
227  // initialize collectors
228  ErrText = "";
229  NetText = "";
230  SimText = "";
231  NetLine = "";
232 
233  // evaluate properties
234  if(Props.at(1)->Value != "")
235  makeSubcircuit = true;
236  else
237  makeSubcircuit = false;
238  if(Props.at(2)->Value == "yes")
239  insertSim = true;
240  else
241  insertSim = false;
242 
243  // preprocessor run if necessary
244  QString preprocessor = Props.at(3)->Value;
245  if (preprocessor != "none") {
246  bool piping = true;
247  QString script;
248 #ifdef __MINGW32__
249  QString interpreter = "tinyperl.exe";
250 #else
251  QString interpreter = "perl";
252 #endif
253  if (preprocessor == "ps2sp") {
254  script = "ps2sp";
255  } else if (preprocessor == "spicepp") {
256  script = "spicepp.pl";
257  } else if (preprocessor == "spiceprm") {
258  script = "spiceprm";
259  piping = false;
260  }
261  script = QucsSettings.BinDir + script;
262  SpicePrep = new QProcess(this);
263  SpicePrep->addArgument(interpreter);
264  SpicePrep->addArgument(script);
265  SpicePrep->addArgument(*SpiceFile);
266 
267  QFile PrepFile;
268  QString PrepName = *SpiceFile + ".pre";
269 
270  if (!piping) {
271  SpicePrep->addArgument(PrepName);
272  connect(SpicePrep, SIGNAL(readyReadStdout()), SLOT(slotSkipOut()));
273  connect(SpicePrep, SIGNAL(readyReadStderr()), SLOT(slotGetPrepErr()));
274  } else {
275  connect(SpicePrep, SIGNAL(readyReadStdout()), SLOT(slotGetPrepOut()));
276  connect(SpicePrep, SIGNAL(readyReadStderr()), SLOT(slotGetPrepErr()));
277  }
278 
279  QMessageBox *MBox = new QMessageBox(QObject::tr("Info"),
280  QObject::tr("Preprocessing SPICE file \"%1\".").arg(*SpiceFile),
281  QMessageBox::NoIcon, QMessageBox::Abort,
282  QMessageBox::NoButton, QMessageBox::NoButton, 0, 0, true,
283  Qt::WStyle_DialogBorder | Qt::WDestructiveClose);
284  connect(SpicePrep, SIGNAL(processExited()), MBox, SLOT(close()));
285 
286  if (piping) {
287  PrepFile.setName(PrepName);
288  if(!PrepFile.open(IO_WriteOnly)) {
289  ErrText +=
290  QObject::tr("ERROR: Cannot save preprocessed SPICE file \"%1\".").
291  arg(PrepName);
292  return false;
293  }
294  prestream = new QTextStream(&PrepFile);
295  }
296 
297  if(!SpicePrep->start()) {
298  ErrText += QObject::tr("ERROR: Cannot execute \"%1\".").
299  arg(interpreter + " " + script + "\".");
300  if (piping) {
301  PrepFile.close();
302  delete prestream;
303  }
304  return false;
305  }
306  SpicePrep->closeStdin();
307 
308  MBox->exec();
309  delete SpicePrep;
310  if (piping) {
311  PrepFile.close();
312  delete prestream;
313  }
314  *SpiceFile = PrepName;
315  }
316 
317  // begin command line construction
318  QStringList com;
319  com << (QucsSettings.BinDir + "qucsconv");
320  if(makeSubcircuit) com << "-g" << "_ref";
321  com << "-if" << "spice" << "-of" << "qucs";
322  com << "-i" << *SpiceFile;
323 
324  // begin netlist text creation
325  if(makeSubcircuit) {
326  QString f = properFileName(*FileName);
327  NetText += "\n.Def:" + properName(f) + " ";
328  QString PortNames = Props.at(1)->Value;
329  PortNames.replace(',', ' ');
330  NetText += PortNames;
331  if(makeSubcircuit) NetText += " _ref";
332  }
333  NetText += "\n";
334 
335  // startup SPICE conversion process
336  QucsConv = new QProcess(this);
337  QucsConv->setArguments(com);
338  connect(QucsConv, SIGNAL(readyReadStdout()), SLOT(slotGetNetlist()));
339  connect(QucsConv, SIGNAL(readyReadStderr()), SLOT(slotGetError()));
340  connect(QucsConv, SIGNAL(processExited()), SLOT(slotExited()));
341  if(!QucsConv->start()) {
342  ErrText += QObject::tr("ERROR: Cannot start QucsConv!");
343  return false;
344  }
345  (*outstream) << NetText;
346  (*filstream) << NetText;
347  QucsConv->closeStdin();
348 
349  // waiting info dialog box
350  QMessageBox *MBox = new QMessageBox(QObject::tr("Info"),
351  QObject::tr("Converting SPICE file \"%1\".").arg(*SpiceFile),
352  QMessageBox::NoIcon, QMessageBox::Abort,
353  QMessageBox::NoButton, QMessageBox::NoButton, 0, 0, true,
354  Qt::WStyle_DialogBorder | Qt::WDestructiveClose);
355  connect(QucsConv, SIGNAL(processExited()), MBox, SLOT(close()));
356  MBox->exec();
357 
358  // finish
359  delete QucsConv;
360  lastLoaded = QDateTime::currentDateTime();
361  return true;
362 }
363 
364 // -------------------------------------------------------------------------
365 void SpiceFile::slotSkipErr()
366 {
367  SpicePrep->readStderr();
368 }
369 
370 // -------------------------------------------------------------------------
371 void SpiceFile::slotSkipOut()
372 {
373  SpicePrep->readStdout();
374 }
375 
376 // -------------------------------------------------------------------------
377 void SpiceFile::slotGetPrepErr()
378 {
379  ErrText += QString(SpicePrep->readStderr());
380 }
381 
382 // -------------------------------------------------------------------------
383 void SpiceFile::slotGetPrepOut()
384 {
385  (*prestream) << QString(SpicePrep->readStdout());
386 }
387 
388 // -------------------------------------------------------------------------
389 void SpiceFile::slotGetError()
390 {
391  ErrText += QString(QucsConv->readStderr());
392 }
393 
394 // -------------------------------------------------------------------------
395 void SpiceFile::slotGetNetlist()
396 {
397  int i;
398  QString s;
399  NetLine += QString(QucsConv->readStdout());
400 
401  while((i = NetLine.find('\n')) >= 0) {
402  s = NetLine.left(i);
403  NetLine.remove(0, i+1);
404  s = s.stripWhiteSpace();
405  if(s.at(0) == '#') {
406  continue;
407  } else if(s.isEmpty()) {
408  continue;
409  } else if(s.at(0) == '.') {
410  if(s.left(5) != ".Def:") {
411  if(insertSim) SimText += s + "\n";
412  continue;
413  }
414  }
415  if(makeSubcircuit) {
416  (*outstream) << " ";
417  (*filstream) << " ";
418  }
419  (*outstream) << s << "\n";
420  (*filstream) << s << "\n";
421  }
422 }
423 
424 // -------------------------------------------------------------------------
425 void SpiceFile::slotExited()
426 {
427  if (!QucsConv->normalExit()) {
428  NetText = "";
429  }
430  else {
431  if(makeSubcircuit) {
432  (*outstream) << ".Def:End\n\n";
433  (*filstream) << ".Def:End\n\n";
434  }
435  if(!SimText.isEmpty()) {
436  (*outstream) << SimText;
437  (*filstream) << SimText;
438  }
439  }
440 }