diff options
author | rahulp13 | 2023-06-07 16:26:35 +0530 |
---|---|---|
committer | rahulp13 | 2023-06-07 16:26:35 +0530 |
commit | c99a4951df139de96e80a51805a3b4d677336653 (patch) | |
tree | 300cbfead2409e522482d4179c447d611341cac1 /src/ngspiceSimulation/NgspiceWidget.py | |
parent | abc410aac656e840940b2227f4d94f0780a614be (diff) | |
download | eSim-c99a4951df139de96e80a51805a3b4d677336653.tar.gz eSim-c99a4951df139de96e80a51805a3b4d677336653.tar.bz2 eSim-c99a4951df139de96e80a51805a3b4d677336653.zip |
sim. process switched to qt slot-signal mech. & report errors
Diffstat (limited to 'src/ngspiceSimulation/NgspiceWidget.py')
-rw-r--r-- | src/ngspiceSimulation/NgspiceWidget.py | 156 |
1 files changed, 88 insertions, 68 deletions
diff --git a/src/ngspiceSimulation/NgspiceWidget.py b/src/ngspiceSimulation/NgspiceWidget.py index d0ec7337..e940d995 100644 --- a/src/ngspiceSimulation/NgspiceWidget.py +++ b/src/ngspiceSimulation/NgspiceWidget.py @@ -1,13 +1,13 @@ +import os from PyQt5 import QtWidgets, QtCore from configuration.Appconfig import Appconfig from frontEnd import TerminalUi -import os # This Class creates NgSpice Window class NgspiceWidget(QtWidgets.QWidget): - def __init__(self, netlist, startSimulation): + def __init__(self, netlist, simEndSignal): """ - Creates constructor for NgspiceWidget class. - Creates NgspiceWindow and runs the process @@ -17,35 +17,33 @@ class NgspiceWidget(QtWidgets.QWidget): :param netlist: The file .cir.out file that contains the instructions. :type netlist: str - :param startSimulation: A function that disables - the toolbar buttons of connects the finishSimulation - function to finished.connect - :type startSimulation: function + :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.process = QtCore.QProcess(self) self.projDir = self.obj_appconfig.current_project["ProjectName"] - self.args = ['-b', '-r', netlist.replace(".cir.out", ".raw"), - netlist] + self.args = ['-b', '-r', netlist.replace(".cir.out", ".raw"), netlist] + print("Argument to ngspice: ", self.args) + + self.process = QtCore.QProcess(self) self.terminalUi = TerminalUi.TerminalUi(self.process, self.args) self.layout = QtWidgets.QVBoxLayout(self) self.layout.addWidget(self.terminalUi) -# print("Argument to ngspice command : ", netlist) - -# This variable makes sure that finished.connect is called exactly once - self.process.isFinishConnected = False - - self.process.\ - started.\ - connect(lambda: - startSimulation(process=self.process, - function=self.finishSimulation)) 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) + ) + self.process.errorOccurred.connect( + lambda: self.finishSimulation(None, None, simEndSignal)) self.process.start('ngspice', self.args) - self.process.readyReadStandardOutput.connect( - lambda: self.readyReadAll()) + self.obj_appconfig.process_obj.append(self.process) print(self.obj_appconfig.proc_dict) ( @@ -54,79 +52,101 @@ class NgspiceWidget(QtWidgets.QWidget): self.process.pid()) ) - if os.name != "nt": + 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) - def finishSimulation(self, exitCode, - exitStatus, checkNgspiceProcessFinished): - """This function is intended to run when the ngspice + @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): + """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 + :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 checkNgspiceProcessFinished: Takes the plotting function - as input and uses it to generate the plots. The reason - why this is passed in such a way is to minimize the no. - of functions passed through a chain of objects. - :type checkNgspiceProcessFinished: function + :param simEndSignal: A signal passed from constructor + for enabling simulation interaction and plotting data if the + simulation is successful + :type simEndSignal: PyQt Signal """ - -# To stop progressbar from running after simulation is completed + # 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() - if exitStatus == QtCore.QProcess.NormalExit: - checkNgspiceProcessFinished(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() + # Redo-simulation does not set correct exit status and code. + # So, need to check the error type: + # => UnknownError along with NormalExit seems successful simulation + if exitStatus == QtCore.QProcess.NormalExit and exitCode == 0 \ + and errorType == QtCore.QProcess.UnknownError: + 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>' - successFormat = '<span style="color:#00ff00; font-size:26px;"> \ - {} \ - </span>' - if exitCode == 0: - self.terminalUi.simulationConsole.append( - successFormat.format("Simulation Completed Successfully!")) + {} \ + </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: - self.terminalUi.simulationConsole.append( - failedFormat.format("Simulation Failed!")) + errMsg += ' could not complete. Try again later.' - self.terminalUi.simulationConsole.verticalScrollBar().setValue( - self.terminalUi.simulationConsole.verticalScrollBar().maximum() - ) - else: self.msg = QtWidgets.QErrorMessage() self.msg.setModal(True) self.msg.setWindowTitle("Error Message") - self.msg.showMessage( - 'Ngspice simulation did not complete successfully.' - ) + self.msg.showMessage(errMsg) self.msg.exec_() - @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') + self.terminalUi.simulationConsole.verticalScrollBar().setValue( + self.terminalUi.simulationConsole.verticalScrollBar().maximum() ) - stderror = str(self.process.readAllStandardError().data(), - encoding='utf-8') -# For suppressing the PrinterOnly error that batch mode throws - stderror = '\n'.join([line for line in stderror.split('\n') - if ('PrinterOnly' not in line and - 'viewport for graphics' not in line)]) - self.terminalUi.simulationConsole.insertPlainText( - stderror - ) + simEndSignal.emit(exitStatus, exitCode) |