summaryrefslogtreecommitdiff
path: root/src/ngspiceSimulation/NgspiceWidget.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/ngspiceSimulation/NgspiceWidget.py')
-rw-r--r--src/ngspiceSimulation/NgspiceWidget.py199
1 files changed, 155 insertions, 44 deletions
diff --git a/src/ngspiceSimulation/NgspiceWidget.py b/src/ngspiceSimulation/NgspiceWidget.py
index 8c63a22a..94368cdd 100644
--- a/src/ngspiceSimulation/NgspiceWidget.py
+++ b/src/ngspiceSimulation/NgspiceWidget.py
@@ -1,58 +1,169 @@
+import os
from PyQt5 import QtWidgets, QtCore
from configuration.Appconfig import Appconfig
-from configparser import ConfigParser
-import os
+from frontEnd import TerminalUi
# This Class creates NgSpice Window
class NgspiceWidget(QtWidgets.QWidget):
- def __init__(self, command, projPath):
+ def __init__(self, netlist, simEndSignal):
"""
- Creates constructor for NgspiceWidget class.
- - Checks whether OS is Linux or Windows and
- creates Ngspice window accordingly.
+ - Creates NgspiceWindow and runs the process
+ - Calls the logs the ngspice process, returns
+ it's simulation status and calls the plotter
+ - Checks whether it is Linux and runs gaw
+ :param netlist: The file .cir.out file that
+ contains the instructions.
+ :type netlist: str
+ :param simEndSignal: A signal that will be emitted to Application class
+ for enabling simulation interaction and plotting data if the
+ simulation is successful
+ :type simEndSignal: PyQt Signal
"""
QtWidgets.QWidget.__init__(self)
self.obj_appconfig = Appconfig()
+ self.projDir = self.obj_appconfig.current_project["ProjectName"]
+ self.args = ['-b', '-r', netlist.replace(".cir.out", ".raw"), netlist]
+ print("Argument to ngspice: ", self.args)
+
self.process = QtCore.QProcess(self)
- self.terminal = QtWidgets.QWidget(self)
+ self.terminalUi = TerminalUi.TerminalUi(self.process, self.args)
self.layout = QtWidgets.QVBoxLayout(self)
- self.layout.addWidget(self.terminal)
-
- print("Argument to ngspice command : ", command)
-
- if os.name == 'nt': # For Windows OS
- parser_nghdl = ConfigParser()
- parser_nghdl.read(
- os.path.join('library', 'config', '.nghdl', 'config.ini')
- )
-
- msys_home = parser_nghdl.get('COMPILER', 'MSYS_HOME')
-
- tempdir = os.getcwd()
- projPath = self.obj_appconfig.current_project["ProjectName"]
- os.chdir(projPath)
- self.command = 'cmd /c '+'"start /min ' + \
- msys_home + "/usr/bin/mintty.exe ngspice -p " + command + '"'
- self.process.start(self.command)
- os.chdir(tempdir)
-
- else: # For Linux OS
- self.command = "cd " + projPath + \
- ";ngspice -r " + command.replace(".cir.out", ".raw") + \
- " " + command
- # Creating argument for process
- self.args = ['-hold', '-e', self.command]
- self.process.start('xterm', self.args)
- self.obj_appconfig.process_obj.append(self.process)
- print(self.obj_appconfig.proc_dict)
- (
- self.obj_appconfig.proc_dict
- [self.obj_appconfig.current_project['ProjectName']].append(
- self.process.pid())
- )
- self.process = QtCore.QProcess(self)
- self.command = "gaw " + command.replace(".cir.out", ".raw")
- self.process.start('sh', ['-c', self.command])
- print(self.command)
+ self.layout.addWidget(self.terminalUi)
+
+ self.process.setWorkingDirectory(self.projDir)
+ self.process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
+ self.process.readyRead.connect(self.readyReadAll)
+ self.process.finished.connect(
+ lambda exitCode, exitStatus:
+ self.finishSimulation(exitCode, exitStatus, simEndSignal, False)
+ )
+ self.process.errorOccurred.connect(
+ lambda: self.finishSimulation(None, None, simEndSignal, True))
+ self.process.start('ngspice', self.args)
+
+ self.obj_appconfig.process_obj.append(self.process)
+ print(self.obj_appconfig.proc_dict)
+ (
+ self.obj_appconfig.proc_dict
+ [self.obj_appconfig.current_project['ProjectName']].append(
+ self.process.pid())
+ )
+
+ if os.name != "nt": # Linux OS
+ self.gawProcess = QtCore.QProcess(self)
+ self.gawCommand = "gaw " + netlist.replace(".cir.out", ".raw")
+ self.gawProcess.start('sh', ['-c', self.gawCommand])
+ print(self.gawCommand)
+
+ @QtCore.pyqtSlot()
+ def readyReadAll(self):
+ """Outputs the ngspice process standard output and standard error
+ to :class:`TerminalUi.TerminalUi` console
+ """
+ self.terminalUi.simulationConsole.insertPlainText(
+ str(self.process.readAllStandardOutput().data(), encoding='utf-8')
+ )
+
+ stderror = str(self.process.readAllStandardError().data(),
+ encoding='utf-8')
+
+ # Suppressing the Ngspice PrinterOnly error that batch mode throws
+ stderror = '\n'.join([errLine for errLine in stderror.split('\n')
+ if ('PrinterOnly' not in errLine and
+ 'viewport for graphics' not in errLine)])
+
+ self.terminalUi.simulationConsole.insertPlainText(stderror)
+
+ def finishSimulation(self, exitCode, exitStatus,
+ simEndSignal, hasErrorOccurred):
+ """This function is intended to run when the Ngspice
+ simulation finishes. It singals to the function that generates
+ the plots and also writes in the appropriate status of the
+ simulation (Whether it was a success or not).
+
+ :param exitCode: The exit code signal of the QProcess
+ that runs ngspice
+ :type exitCode: int
+ :param exitStatus: The exit status signal of the
+ qprocess that runs ngspice
+ :type exitStatus: class:`QtCore.QProcess.ExitStatus`
+ :param simEndSignal: A signal passed from constructor
+ for enabling simulation interaction and plotting data if the
+ simulation is successful
+ :type simEndSignal: PyQt Signal
+ """
+
+ # Canceling simulation triggers both finished and
+ # errorOccurred signals...need to skip finished signal in this case.
+ if not hasErrorOccurred and self.terminalUi.simulationCancelled:
+ return
+
+ # Stop progressbar from running after simulation is completed
+ self.terminalUi.progressBar.setMaximum(100)
+ self.terminalUi.progressBar.setProperty("value", 100)
+ self.terminalUi.cancelSimulationButton.setEnabled(False)
+ self.terminalUi.redoSimulationButton.setEnabled(True)
+
+ if exitCode is None:
+ exitCode = self.process.exitCode()
+
+ errorType = self.process.error()
+ if errorType < 3: # 0, 1, 2 ==> failed to start, crashed, timedout
+ exitStatus = QtCore.QProcess.CrashExit
+ elif exitStatus is None:
+ exitStatus = self.process.exitStatus()
+
+ if self.terminalUi.simulationCancelled:
+ msg = QtWidgets.QMessageBox()
+ msg.setModal(True)
+ msg.setIcon(QtWidgets.QMessageBox.Warning)
+ msg.setWindowTitle("Warning Message")
+ msg.setText("Simulation was cancelled.")
+ msg.setStandardButtons(QtWidgets.QMessageBox.Ok)
+ msg.exec()
+
+ elif exitStatus == QtCore.QProcess.NormalExit and exitCode == 0 \
+ and errorType == QtCore.QProcess.UnknownError:
+ # Redo-simulation does not set correct exit status and code.
+ # So, need to check the error type ==>
+ # UnknownError along with NormalExit seems successful simulation
+
+ successFormat = '<span style="color:#00ff00; font-size:26px;">\
+ {} \
+ </span>'
+ self.terminalUi.simulationConsole.append(
+ successFormat.format("Simulation Completed Successfully!"))
+
+ else:
+ failedFormat = '<span style="color:#ff3333; font-size:26px;"> \
+ {} \
+ </span>'
+ self.terminalUi.simulationConsole.append(
+ failedFormat.format("Simulation Failed!"))
+
+ errMsg = 'Simulation '
+ if errorType == QtCore.QProcess.FailedToStart:
+ errMsg += 'failed to start. ' + \
+ 'Ensure that eSim is installed correctly.'
+ elif errorType == QtCore.QProcess.Crashed:
+ errMsg += 'crashed. Try again later.'
+ elif errorType == QtCore.QProcess.Timedout:
+ errMsg += ' has timed out. Try to reduce the ' + \
+ ' simulation time or the simulation step interval.'
+ else:
+ errMsg += ' could not complete. Try again later.'
+
+ msg = QtWidgets.QErrorMessage()
+ msg.setModal(True)
+ msg.setWindowTitle("Error Message")
+ msg.showMessage(errMsg)
+ msg.exec()
+
+ self.terminalUi.simulationConsole.verticalScrollBar().setValue(
+ self.terminalUi.simulationConsole.verticalScrollBar().maximum()
+ )
+
+ simEndSignal.emit(exitStatus, exitCode)