Qucs-GUI  0.0.19
/home/travis/build/Qucs/qucs/qucs/qucs/components/spicefile.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                               spicefile.cpp
00003                              ---------------
00004     begin                : Tue Dez 28 2004
00005     copyright            : (C) 2004 by Michael Margraf
00006     email                : michael.margraf@alumni.tu-berlin.de
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #if HAVE_UNISTD_H
00019 # include <unistd.h>
00020 #endif
00021 #include <QRegExp>
00022 #include <QProcess>
00023 #include <QString>
00024 #include <QStringList>
00025 #include <QMessageBox>
00026 #include <QTextStream>
00027 #include <QFile>
00028 #include <QDir>
00029 #include <QFileInfo>
00030 #include <QMutex>
00031 #include <QDebug>
00032 #include <QStatusBar>
00033 
00034 #include "spicefile.h"
00035 #include "schematic.h"
00036 #include "main.h"
00037 #include "qucs.h"
00038 #include "misc.h"
00039 
00040 
00041 SpiceFile::SpiceFile()
00042 {
00043   Description = QObject::tr("SPICE netlist file");
00044   // Property descriptions not needed, but must not be empty !
00045   Props.append(new Property("File", "", true, QString("x")));
00046   Props.append(new Property("Ports", "", false, QString("x")));
00047   Props.append(new Property("Sim", "yes", false, QString("x")));
00048   Props.append(new Property("Preprocessor", "none", false, QString("x")));
00049   withSim = false;
00050 
00051   Model = "SPICE";
00052   Name  = "X";
00053   changed = false;
00054 
00055   // Do NOT call createSymbol() here. But create port to let it rotate.
00056   Ports.append(new Port(0, 0));
00057 }
00058 
00059 // -------------------------------------------------------
00060 Component* SpiceFile::newOne()
00061 {
00062   SpiceFile *p = new SpiceFile();
00063   p->recreate(0);   // createSymbol() is NOT called in constructor !!!
00064   return p;
00065 }
00066 
00067 // -------------------------------------------------------
00068 Element* SpiceFile::info(QString& Name, char* &BitmapFile, bool getNewOne)
00069 {
00070   Name = QObject::tr("SPICE netlist");
00071   BitmapFile = (char *) "spicefile";
00072 
00073   if(getNewOne) {
00074     SpiceFile *p = new SpiceFile();
00075     p->recreate(0);   // createSymbol() is NOT called in constructor !!!
00076     return p;
00077   }
00078   return 0;
00079 }
00080 
00081 // -------------------------------------------------------
00082 void SpiceFile::createSymbol()
00083 {
00084   QFont f = QucsSettings.font; // get the basic font
00085   // symbol text is smaller (10 pt default)
00086   f.setPointSize(10);
00087   // use the screen-compatible metric
00088   QFontMetrics  smallmetrics(f, 0);   // get size of text
00089   int fHeight = smallmetrics.lineSpacing();
00090 
00091   int No = 0;
00092   QString tmp, PortNames = Props.at(1)->Value;
00093   if(!PortNames.isEmpty())  No = PortNames.count(',') + 1;
00094   
00095   // draw symbol outline
00096   #define HALFWIDTH  17
00097   int h = 30*((No-1)/2) + 15;
00098   Lines.append(new Line(-HALFWIDTH, -h, HALFWIDTH, -h,QPen(Qt::darkBlue,2)));
00099   Lines.append(new Line( HALFWIDTH, -h, HALFWIDTH,  h,QPen(Qt::darkBlue,2)));
00100   Lines.append(new Line(-HALFWIDTH,  h, HALFWIDTH,  h,QPen(Qt::darkBlue,2)));
00101   Lines.append(new Line(-HALFWIDTH, -h,-HALFWIDTH,  h,QPen(Qt::darkBlue,2)));
00102 
00103   int w, i = fHeight/2;
00104   if(withSim) {
00105     i = fHeight - 2;
00106     tmp = QObject::tr("sim");
00107     w = smallmetrics.width(tmp);
00108     Texts.append(new Text(w/-2, 0, tmp, Qt::red));
00109   }
00110   tmp = QObject::tr("spice");
00111   w = smallmetrics.boundingRect(tmp).width();
00112   Texts.append(new Text(w/-2, -i, tmp));
00113 
00114   i = 0;
00115   int y = 15-h;
00116   while(i<No) { // add ports lines and numbers
00117     Lines.append(new Line(-30,  y,-HALFWIDTH,  y,QPen(Qt::darkBlue,2)));
00118     Ports.append(new Port(-30,  y));
00119     tmp = PortNames.section(',', i, i).mid(4);
00120     w = smallmetrics.width(tmp);
00121     Texts.append(new Text(-20-w, y-fHeight-2, tmp)); // text right-aligned
00122     i++;
00123 
00124     if(i == No) break; // if odd number of ports there will be one port less on the right side
00125     Lines.append(new Line(HALFWIDTH,  y, 30,  y,QPen(Qt::darkBlue,2)));
00126     Ports.append(new Port( 30,  y));
00127     tmp = PortNames.section(',', i, i).mid(4);
00128     Texts.append(new Text( 20, y-fHeight-2, tmp)); // text left-aligned
00129     y += 60;
00130     i++;
00131   }
00132 
00133   if(No > 0) {
00134     Lines.append(new Line( 0, h, 0,h+15,QPen(Qt::darkBlue,2)));
00135     Texts.append(new Text( 4, h,"Ref"));
00136     Ports.append(new Port( 0, h+15));    // 'Ref' port
00137   }
00138 
00139   x1 = -30; y1 = -h-2;
00140   x2 =  30; y2 =  h+15;
00141 
00142   // compute component name text position - normal size font
00143   QFontMetrics  metrics(QucsSettings.font, 0);   // use the screen-compatible metric
00144   fHeight = metrics.lineSpacing();
00145   tx = x1+4;
00146   ty = y1 - fHeight - 4;
00147   if(Props.first()->display) ty -= fHeight;
00148   changed = true;
00149 }
00150 
00151 // ---------------------------------------------------
00152 QString SpiceFile::netlist()
00153 {
00154   if(Props.at(1)->Value.isEmpty())
00155     return QString("");  // no ports, no subcircuit instance
00156 
00157   QString s = "Sub:"+Name;   // SPICE netlist is subcircuit
00158   foreach(Port *pp, Ports)
00159     s += " "+pp->Connection->Name;   // output all node names
00160 
00161   QString f = misc::properFileName(Props.first()->Value);
00162   s += " Type=\""+misc::properName(f)+"\"\n";
00163   return s;
00164 }
00165 
00166 // -------------------------------------------------------
00167 QString SpiceFile::getSubcircuitFile()
00168 {
00169   // construct full filename
00170   QString FileName = Props.getFirst()->Value;
00171 
00172   if (FileName.isEmpty())
00173   {
00174       return misc::properAbsFileName(FileName);
00175   }
00176 
00177   QFileInfo FileInfo(FileName);
00178 
00179   if (FileInfo.exists())
00180   {
00181       // the file must be an absolute path to a schematic file
00182      return FileInfo.absoluteFilePath();
00183   }
00184   else
00185   {
00186     // get the complete base name (everything except the last '.'
00187     // and whatever follows
00188     QString baseName = FileInfo.completeBaseName();
00189 
00190     // if only a file name is supplied, first check if it is in the
00191     // same directory as the schematic file it is a part of
00192     if (FileInfo.fileName () == FileName)
00193     {
00194         // the file has no path information, just the file name
00195         if (containingSchematic)
00196         {
00197             // check if a file of the same name is in the same directory
00198             // as the schematic file, if we have a pointer to it, in
00199             // which case we use this one
00200             QFileInfo schematicFileInfo = containingSchematic->getFileInfo ();
00201 
00202             for (int i = 0; i < QucsSettings.spiceExtensions.count (); i++)
00203             {
00204                 QString extension = QucsSettings.spiceExtensions[i];
00205                 extension.remove(0, 1); // take leading '*' out, issue with exits()
00206 
00207                 QFileInfo localFileInfo (schematicFileInfo.canonicalPath ()
00208                                          + "/" + baseName + extension);
00209 
00210                 if (localFileInfo.exists ())
00211                 {
00212                     // return the subcircuit saved in the same directory
00213                     // as the schematic file
00214                     return localFileInfo.absoluteFilePath();
00215                 }
00216                 else
00217                 {
00219                     qCritical() << "Spice file not found:" << localFileInfo.absFilePath();
00220                 }
00221             }
00222         }
00223     }
00224 
00225     // look up the hash table for the schematic file as
00226     // it does not seem to be an absolute path, this will also
00227     // search the home directory which is always hashed
00228     QMutex mutex;
00229     mutex.lock();
00230     QString hashsearchresult = "";
00231     // if GUI is running and has something in the hash
00232     if ( (QucsMain != 0) && !QucsMain->spiceNameHash.isEmpty() )
00233       hashsearchresult = QucsMain->spiceNameHash.value(baseName);
00234     mutex.unlock();
00235 
00236     if (hashsearchresult.isEmpty())
00237     {
00238         // the schematic was not found in the hash table, return
00239         // what would always have been returned in this case
00240         return misc::properAbsFileName(FileName);
00241     }
00242     else
00243     {
00244         // we found an entry in the hash table, check it actually still exists
00245         FileInfo.setFile(hashsearchresult);
00246 
00247         if (FileInfo.exists())
00248         {
00249             // it does exist so return the absolute file path
00250             return FileInfo.absoluteFilePath();
00251         }
00252         else
00253         {
00254             // the schematic file does not actually exist, return
00255             // what would always have been returned in this case
00256             return misc::properAbsFileName(FileName);
00257         }
00258     }
00259 
00260   }
00261 
00262 }
00263 
00264 // -------------------------------------------------------------------------
00265 bool SpiceFile::createSubNetlist(QTextStream *stream)
00266 {
00267   // check file name
00268   QString FileName = Props.first()->Value;
00269   if(FileName.isEmpty()) {
00270     ErrText += QObject::tr("ERROR: No file name in SPICE component \"%1\".").
00271       arg(Name);
00272     return false;
00273   }
00274 
00275   // check input and output file
00276   QFile SpiceFile, ConvFile;
00277   FileName = getSubcircuitFile();
00278   SpiceFile.setFileName(FileName);
00279   if(!SpiceFile.open(QIODevice::ReadOnly)) {
00280     ErrText += QObject::tr("ERROR: Cannot open SPICE file \"%1\".").
00281       arg(FileName);
00282     return false;
00283   }
00284   SpiceFile.close();
00285   QString ConvName = SpiceFile.fileName() + ".lst";
00286   ConvFile.setFileName(ConvName);
00287   QFileInfo Info(ConvName);
00288 
00289   // re-create converted file if necessary
00290   if(changed || !ConvFile.exists() ||
00291      (lastLoaded.isValid() && lastLoaded < Info.lastModified())) {
00292     if(!ConvFile.open(QIODevice::WriteOnly)) {
00293       ErrText += QObject::tr("ERROR: Cannot save converted SPICE file \"%1\".").
00294                           arg(FileName + ".lst");
00295       return false;
00296     }
00297     outstream = stream;
00298     filstream = new QTextStream(&ConvFile);
00299     QString SpiceName = SpiceFile.fileName();
00300     bool ret = recreateSubNetlist(&SpiceName, &FileName);
00301     ConvFile.close();
00302     delete filstream;
00303     return ret;
00304   }
00305 
00306   // load old file and stuff into stream
00307   if(!ConvFile.open(QIODevice::ReadOnly)) {
00308     ErrText += QObject::tr("ERROR: Cannot open converted SPICE file \"%1\".").
00309                         arg(FileName + ".lst");
00310     return false;
00311   }
00312   QByteArray FileContent = ConvFile.readAll();
00313   ConvFile.close();
00314   //? stream->writeRawBytes(FileContent.data(), FileContent.size());
00315   (*stream) << FileContent.data();
00316   return true;
00317 }
00318 
00319 // -------------------------------------------------------------------------
00320 bool SpiceFile::recreateSubNetlist(QString *SpiceFile, QString *FileName)
00321 {
00322   // initialize collectors
00323   ErrText = "";
00324   NetText = "";
00325   SimText = "";
00326   NetLine = "";
00327 
00328   // evaluate properties
00329   if(Props.at(1)->Value != "")
00330     makeSubcircuit = true;
00331   else
00332     makeSubcircuit = false;
00333   if(Props.at(2)->Value == "yes")
00334     insertSim = true;
00335   else
00336     insertSim = false;
00337 
00338   // preprocessor run if necessary
00339   QString preprocessor = Props.at(3)->Value;
00340   if (preprocessor != "none") {
00341     bool piping = true;
00342     QStringList script;
00343 #ifdef __MINGW32__
00344     QString interpreter = "tinyperl.exe";
00345 #else
00346     QString interpreter = "perl";
00347 #endif
00348     if (preprocessor == "ps2sp") {
00349       script << "ps2sp";
00350     } else if (preprocessor == "spicepp") {
00351       script << "spicepp.pl";
00352     } else if (preprocessor == "spiceprm") {
00353       script << "spiceprm";
00354       piping = false;
00355     }
00356     SpicePrep = new QProcess(this);
00357     script << interpreter;
00358     script << script;
00359     script << *SpiceFile;
00360 
00361     QFile PrepFile;
00362     QString PrepName = *SpiceFile + ".pre";
00363 
00364     if (!piping) {
00365       script << PrepName;
00366       connect(SpicePrep, SIGNAL(readyReadStandardOutput()), SLOT(slotSkipOut()));
00367       connect(SpicePrep, SIGNAL(readyReadStandardError()), SLOT(slotGetPrepErr()));
00368     } else {
00369       connect(SpicePrep, SIGNAL(readyReadStandardOutput()), SLOT(slotGetPrepOut()));
00370       connect(SpicePrep, SIGNAL(readyReadStandardError()), SLOT(slotGetPrepErr()));
00371     }
00372 
00373     QMessageBox *MBox = 
00374       new QMessageBox(QMessageBox::NoIcon,
00375                       QObject::tr("Info"),
00376                       QObject::tr("Preprocessing SPICE file \"%1\".").arg(*SpiceFile),
00377                       QMessageBox::Abort);
00378     MBox->setAttribute(Qt::WA_DeleteOnClose);
00379     connect(SpicePrep, SIGNAL(finished(int)), MBox, SLOT(close()));
00380 
00381     if (piping) {
00382       PrepFile.setFileName(PrepName);
00383       if(!PrepFile.open(QIODevice::WriteOnly)) {
00384         ErrText +=
00385           QObject::tr("ERROR: Cannot save preprocessed SPICE file \"%1\".").
00386           arg(PrepName);
00387         return false;
00388       }
00389       prestream = new QTextStream(&PrepFile);
00390     }
00391 
00392     QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
00393     env.insert("PATH", env.value("PATH") );
00394     SpicePrep->setProcessEnvironment(env);
00395     SpicePrep->start(script.join(" "));
00396     //QucsHelp->setCommunication(0);
00397 
00398     if(SpicePrep->state()!=QProcess::Running&&
00399             SpicePrep->state()!=QProcess::Starting) {
00400       ErrText += QObject::tr("ERROR: Cannot execute \"%1\".").
00401               arg(interpreter + " " + script.join(" ") + "\".");
00402       if (piping) {
00403         PrepFile.close();
00404         delete prestream;
00405       }
00406       return false;
00407     }
00408     //SpicePrep->closeStdin();
00409 
00410     MBox->exec();
00411     delete SpicePrep;
00412     if (piping) {
00413       PrepFile.close();
00414       delete prestream;
00415     }
00416     *SpiceFile = PrepName;
00417   }
00418 
00419   // begin command line construction
00420   QString prog;
00421   QStringList com;
00422   //prog =  QucsSettings.BinDir + "qucsconv"  + executableSuffix;
00423   prog =  QucsSettings.Qucsconv;
00424 
00425   if(makeSubcircuit) com << "-g" << "_ref";
00426   com << "-if" << "spice" << "-of" << "qucs";
00427   com << "-i" << *SpiceFile;
00428 
00429   // begin netlist text creation
00430   if(makeSubcircuit) {
00431     QString f = misc::properFileName(*FileName);
00432     NetText += "\n.Def:" + misc::properName(f) + " ";
00433     QString PortNames = Props.at(1)->Value;
00434     PortNames.replace(',', ' ');
00435     NetText += PortNames;
00436     if(makeSubcircuit) NetText += " _ref";
00437   }
00438   NetText += "\n";
00439 
00440   // startup SPICE conversion process
00441   QucsConv = new QProcess(this);
00442   QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
00443   env.insert("PATH", env.value("PATH") );
00444   QucsConv->setProcessEnvironment(env);
00445 
00446   qDebug() << "SpiceFile::recreateSubNetlist :Command:" << prog << com.join(" ");
00447 //  QucsConv->start(com.join(" "));
00448   QucsConv->start(prog, com);
00449 
00451   connect(QucsConv, SIGNAL(readyReadStandardOutput()), SLOT(slotGetNetlist()));
00452   connect(QucsConv, SIGNAL(readyReadStandardError()), SLOT(slotGetError()));
00453   connect(QucsConv, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(slotExited()));
00454 
00455   if(QucsConv->state()!=QProcess::Running&&
00456           QucsConv->state()!=QProcess::Starting) {
00457     ErrText += QObject::tr("COMP ERROR: Cannot start QucsConv!");
00458     return false;
00459   }
00460   (*outstream) << NetText;
00461   (*filstream) << NetText;
00462 
00463   // only interact with the GUI if it was launched
00464   if (QucsMain) {
00465     QucsMain->statusBar()->showMessage(tr("Converting SPICE file \"%1\".").arg(*SpiceFile), 2000);
00466   }
00467   else
00468     qDebug() << QObject::tr("Converting SPICE file \"%1\".").arg(*SpiceFile);
00469 
00470   // finish
00471   QucsConv->waitForFinished();
00472   delete QucsConv;
00473   lastLoaded = QDateTime::currentDateTime();
00474   return true;
00475 }
00476 
00477 // -------------------------------------------------------------------------
00478 void SpiceFile::slotSkipErr()
00479 {
00480   SpicePrep->readAllStandardError();
00481 }
00482 
00483 // -------------------------------------------------------------------------
00484 void SpiceFile::slotSkipOut()
00485 {
00486     SpicePrep->readAllStandardOutput();
00487 }
00488 
00489 // -------------------------------------------------------------------------
00490 void SpiceFile::slotGetPrepErr()
00491 {
00492   ErrText += QString(SpicePrep->readAllStandardError());
00493 }
00494 
00495 // -------------------------------------------------------------------------
00496 void SpiceFile::slotGetPrepOut()
00497 {
00498   (*prestream) << QString(SpicePrep->readAllStandardOutput());
00499 }
00500 
00501 // -------------------------------------------------------------------------
00502 void SpiceFile::slotGetError()
00503 {
00504   ErrText += QString(QucsConv->readAllStandardError());
00505 }
00506 
00507 // -------------------------------------------------------------------------
00508 void SpiceFile::slotGetNetlist()
00509 {
00510   int i;
00511   QString s;
00512   NetLine += QString(QucsConv->readAllStandardOutput());
00513 
00514   while((i = NetLine.indexOf('\n')) >= 0) {
00515     s = NetLine.left(i);
00516     NetLine.remove(0, i+1);
00517     s = s.trimmed();
00518     if(s.size()>0&&s.at(0) == '#') {
00519       continue;
00520     } else if(s.isEmpty()) {
00521       continue;
00522     } else if(s.size()>0&&s.at(0) == '.') {
00523       if(s.left(5) != ".Def:") {
00524         if(insertSim) SimText += s + "\n";
00525         continue;
00526       }
00527     }
00528     if(makeSubcircuit) {
00529       (*outstream) << "  ";
00530       (*filstream) << "  ";
00531     }
00532     (*outstream) << s << "\n";
00533     (*filstream) << s << "\n";
00534   }
00535 }
00536 
00537 // -------------------------------------------------------------------------
00538 void SpiceFile::slotExited()
00539 {
00540   if (QucsConv->exitStatus() != QProcess::NormalExit) {
00541     NetText = "";
00542   }
00543   else {
00544     if(makeSubcircuit) {
00545       (*outstream) << ".Def:End\n\n";
00546       (*filstream) << ".Def:End\n\n";
00547     }
00548     if(!SimText.isEmpty()) {
00549       (*outstream) << SimText;
00550       (*filstream) << SimText;
00551     }
00552   }
00553 }
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines