Skip to content

Commit

Permalink
Threaded circuit simulation
Browse files Browse the repository at this point in the history
This prevents the blocking of GUI main thread
  • Loading branch information
rafael1193 committed Dec 8, 2014
1 parent 4105f62 commit 74a84c4
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 15 deletions.
23 changes: 16 additions & 7 deletions spicegui/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import os.path

import ngspice_simulation
import running_dialog


class MainWindow(Gtk.ApplicationWindow):
Expand Down Expand Up @@ -526,20 +527,28 @@ def on_gear_button_clicked(self, button):
self.figure.axes[0].legend(loc='lower left')

def on_simulate_button_clicked(self, button):
# Dismiss infobar messages (if they exists)
self.dismiss_error()
simulator = ngspice_simulation.Ngspice_async()
dialog = running_dialog.RunningDialog(self,simulator.end_event)
try:
#First, save changes on disk
self.on_save_button_clicked_overview(None)

# Dismiss infobar messages (if they exists)

ngspice_simulation.Ngspice.simulatefile(self.netlist_file_path)
self.simulation_output = ngspice_simulation.NgspiceOutput.parse_file(self.netlist_file_path + ".out")
self.figure = self.simulation_output.get_figure()
self._update_canvas(self.figure)
self.simulation_view()
simulator.simulatefile("/home/rafael/Documentos/Convertidor DC-AC.sch.net")

if dialog.run() == 1: # Not cancelled
self.simulation_output = ngspice_simulation.NgspiceOutput.parse_file(self.netlist_file_path + ".out")
self.figure = self.simulation_output.get_figure()
self._update_canvas(self.figure)
self.simulation_view()
else:
simulator.terminate()
self.set_error(title="Simulation failed", message=simulator.error.message)
except Exception as e:
self.set_error(title="Simulation failed", message=e.message)
finally:
dialog.destroy()

def start_file_monitor(self):
if self.schematic_file_path is not None:
Expand Down
59 changes: 51 additions & 8 deletions spicegui/ngspice_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import csv
import os.path

from multiprocessing import Pool
from threading import Event, Thread, Lock
from gi.repository import Gio
from matplotlib.figure import Figure

Expand Down Expand Up @@ -300,14 +300,57 @@ def simulatefile(cls, netlist_path):
print error_lines
raise Exception(error_lines)

@classmethod
def simulate_async(cls, netlist_path):
def simulation_completed():
return

pool = Pool(processes=4) # start 4 worker processes
result = pool.apply_async(Ngspice.simulatefile, [netlist_path]) # evaluate "f(10)" asynchronously
print result.get(timeout=1)
class Ngspice_async():

def __init__(self):
self.thread = None
self.result = None
self.errors = None
self.end_event = Event()
self._lock_result = Lock()
self._lock_errors = Lock()

def simulatefile(self, netlist_path):
"""
simulate asyncrhonously netlist_path file with ngspice
set self.result with (stout, stderr)
"""
self.result = None
self.error = None
self.end_event.clear()
self.thread = Thread(group=None, name="ngspice-thread",
target=self._run_simulation, args=(netlist_path,))
self.thread.start()

def _run_simulation(self, netlist_path):
self.process = subprocess.Popen(["ngspice", "-b", "-o",
str(netlist_path) + ".out",
str(netlist_path)],
shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = self.process.communicate()
with self._lock_result:
self.result = (stdout, stderr)
if stderr:
stderr_l = stderr.split("\n")
error_lines=""
for line in stderr_l:
if line.startswith("Error:"):
if len(error_lines) > 0:
error_lines += "\n"
error_lines += line
if error_lines != "":
with self._lock_errors:
self.errors = Exception(error_lines)
self.end_event.set()

def terminate(self):
if self.process is not None:
if self.process.poll() is None:
self.process.terminate()


class Gnetlist():
Expand Down
58 changes: 58 additions & 0 deletions spicegui/running_dialog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-

from gi.repository import Gtk, GObject

class RunningDialog(Gtk.Dialog):

def __init__(self, parent, event):
Gtk.Dialog.__init__(self, "Simulation", parent, Gtk.DialogFlags.MODAL|Gtk.DialogFlags.DESTROY_WITH_PARENT|Gtk.DialogFlags.USE_HEADER_BAR,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL))

self.event = event

self.set_default_size(150, 100)
self.set_default_response(Gtk.ResponseType.CANCEL)

self.progress_bar = Gtk.ProgressBar()
self.progress_bar.activity_mode = True
self.progress_bar.pulse()
self.progress_bar.set_text("Running simulation")
self.progress_bar.set_show_text(True)

self.label = Gtk.Label(label="Running simulation")
self.label.set_vexpand(True)

self.spinner = Gtk.Spinner()
self.spinner.start()
self.spinner.set_vexpand(True)

self.hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.hbox.pack_start(self.spinner, True, True, 18)
self.hbox.pack_start(self.label, True, True, 18)


box = self.get_content_area()
box.add(self.hbox)

self.timeout_id = GObject.timeout_add(50, self.on_timeout, None)

self.show_all()

def on_timeout(self, user_data):
"""
Update value on the progress bar
"""
self.progress_bar.pulse()
if self.event.is_set(): # if thread finished
self.response(1) # 1 is an application-defined response type
return True


if __name__ == "__main__":
win = Gtk.Window()
dialog = RunningDialog(win)
dialog.run()
dialog.destroy()
win.destroy()


0 comments on commit 74a84c4

Please sign in to comment.