Merge commit 'e1053513ced8'

This commit is contained in:
Markus Rosenstihl 2019-06-11 17:23:37 +02:00
commit ca018cbdc8
3 changed files with 210 additions and 11 deletions

View File

@ -90,6 +90,15 @@ class Experiment:
assert len(rf_gates) == len(rf_sources), "rf_sources and rf_gates must have equal number of entries"
self.rf_sources = rf_sources
self.rf_gates = rf_gates
#for tracking the experiment length:
#because loops are possible we need to track the length for each loop level
self.total_time=[]
self.total_time.append(0.0)
#and we need to know the number of iterations of the loops to multiply the state.
self.loop_iterations=[]
self.loop_iterations.append(1)
# Commands -------------------------------------------------------------------------------------
@ -118,6 +127,8 @@ class Experiment:
the_value=1<<channel
self.state_list.append(StateSimple(length, \
'<ttlout value="0x%06x"/>' % the_value))
self.total_time[-1] += length
## Same as ttl_pulse, but no *channel* keyword
def ttls(self, length = None, value = None):
@ -132,8 +143,10 @@ class Experiment:
s_content = '<ttlout value="0x%06x"/>' % the_value
if length is not None:
self.state_list.append(StateSimple(length, s_content))
self.total_time[-1] += length
else:
self.state_list.append(s_content)
def rf_pulse(self, length=None, phase=0, source=0):
"""
@ -157,6 +170,7 @@ class Experiment:
This must be closed with state_end()
"""
self.state_list.append('<state time="%s">\n' % repr(time))
self.total_time[-1] += time
## End of *state_start*
def state_end(self):
@ -184,6 +198,8 @@ class Experiment:
self.state_list.append(StateSimple(time,s_content))
else:
self.state_list.append(StateSimple(time))
self.total_time[-1] += time
## Records data with given number of samples, sampling-frequency frequency and sensitivity
# @param samples Number of samples to record
@ -247,6 +263,7 @@ class Experiment:
if timelength is None:
timelength = samples / float(frequency)#*1.01
self.state_list.append(StateSimple(timelength, s_content))
self.total_time[-1] += timelength
## Create a loop on the pulse programmer. Loop contents can not change inside the loop.
# @params iterations Number of loop iterations
@ -264,6 +281,9 @@ class Experiment:
# (These two lines could probably be guarded by a mutex)
self.list_stack.append(self.state_list)
self.state_list = l
self.total_time.append(0.0)
self.loop_iterations.append(iterations)
## End loop state
def loop_end(self):
@ -272,6 +292,10 @@ class Experiment:
"""
# (This line could probably be guarded by a mutex)
self.state_list = self.list_stack.pop(-1)
looptime=self.total_time.pop(-1)
loopiterations=self.loop_iterations.pop(-1)
self.total_time[-1] += looptime*loopiterations
## Set the frequency and phase of the frequency source.
## This state needs 2us.
@ -291,6 +315,8 @@ class Experiment:
if ttls != 0:
s_content += '<ttlout value="0x%06x"/>' % ttls
self.state_list.append(StateSimple(2e-6, s_content))
self.total_time[-1] += 2e-6
## Creates a, possibly shaped, pulsed gradient.
# @param dac_value DAC value to set
@ -331,10 +357,13 @@ class Experiment:
if form == 'rec': # shape==None --> rectangular gradients
s_content = '<ttlout value="%s"/><analogout id="1" dac_value="%i"/>' % (trigger, dac_value)
self.state_list.append(StateSimple(length, s_content))
self.total_time[-1] += length
if not is_seq:
s_content = '<analogout id="1" dac_value="0"/>'
self.state_list.append(StateSimple(42*9e-8, s_content))
self.total_time[-1] += 42*9e-8
elif form == 'sin2':
# sin**2 shape
@ -342,9 +371,12 @@ class Experiment:
dac = int (dac_value*numpy.sin(numpy.pi/length*t)**2)
s_content = '<ttlout value="%s"/><analogout id="1" dac_value="%i"/>' % (trigger, dac)
self.state_list.append(StateSimple(resolution, s_content))
# set it back to zero
s_content = '<ttlout value="%s"/><analogout id="1" dac_value="0"/>' % (trigger)
self.state_list.append(StateSimple(resolution, s_content))
self.total_time[-1] += resolution*(len(t_steps)+1)
elif form == 'sin':
# sin shape
@ -355,6 +387,8 @@ class Experiment:
# set it back to zero
s_content = '<ttlout value="%s"/><analogout id="1" dac_value="0"/>' % (trigger)
self.state_list.append(StateSimple(resolution, s_content))
self.total_time[-1] += resolution*(len(t_steps)+1)
else: # don't know what to do
raise SyntaxError , "form is unknown: %s"%form
@ -385,10 +419,15 @@ class Experiment:
s_content = '<analogout id="%d" dac_value="%i"/><ttlout value="0x%06x"/>' \
% (dac_id, dac_value, ttls)
self.state_list.append(StateSimple(length, s_content))
self.total_time[-1] += length
if not is_seq:
s_content = '<analogout id="%d" dac_value="0"/><ttlout value="0x%06x"/>' \
% (dac_id, ttls)
self.state_list.append(StateSimple(42*9e-8, s_content))
self.total_time[-1] += 42*9e-8
## sets the phase of the frequency source.
## This state needs 0.5us, though the phase switching time is dependent on the frequency source
@ -408,6 +447,8 @@ class Experiment:
if ttls!=0:
s_content += '<ttlout value="%d"/>' % ttls
self.state_list.append(StateSimple(0.5e-6, s_content))
self.total_time[-1] += 0.5e-6
## sets a description which is carried via the back end result
## file to the result script in the front end. In the result script
@ -434,6 +475,8 @@ class Experiment:
"""
self.state_list.append(StateSimple(1e-6, '<ttlout value="0xf000"/>'))
self.state_list.append(StateSimple(1e-6, '<ttlout value="0x8000"/>'))
self.total_time[-1] += 2e-6
# / Commands -----------------------------------------------------------------------------------
@ -508,7 +551,17 @@ class Experiment:
:returns: XML quit-job
"""
return '<?xml version="1.0" encoding="ISO-8859-1"?>\n<quit/>'
def get_length(self):
timelength = 0.0
#calculate the correct timelength also for unclosed loops, if there is no unclosed loop
#the timelength of this experiment is self.total_time[-1].
for i in range(len(self.total_time)):
timelength += self.total_time[i]
timelength *= self.loop_iterations[i]
return timelength
class Quit(Experiment):
def write_xml_string(self):
@ -529,8 +582,7 @@ def self_test():
e.ttl_pulse(1e-6/3, None, 7) # val = 7
if True:
e.loop_start(30)
e.set_pfg(dac_value=1024, is_seq = True)
e.set_pfg_wt(dac_value=2048)
e.set_pfg(dac_value=1024, length=5e-6, is_seq = True, shape=('rec', 42*9e-8))
e.loop_start(400)
e.set_phase(270, ttls = 32)
e.loop_end()
@ -550,6 +602,8 @@ def self_test():
raise AssertionError("An exception should happen")
e.set_pts_local()
print e.write_xml_string()
print e.get_length()
if __name__ == '__main__':
self_test()

View File

@ -53,9 +53,11 @@ matplotlib.rcParams[ "axes.formatter.limits" ] = "-3,3"
if matplotlib.rcParams[ "backend" ] == "GTK":
from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
max_points_to_display = 0 # no limit
elif matplotlib.rcParams[ "backend" ] == "GTKCairo":
from matplotlib.backends.backend_gtkcairo import FigureCanvasGTKCairo as FigureCanvas
max_points_to_display = 1 << 14 # cairo cannot render longer paths than 18???
else:
# default
@ -181,7 +183,7 @@ class DamarisGUI:
# my notebook
self.main_notebook = self.xml_gui.get_widget( "main_notebook" )
self.log = LogWindow( self.xml_gui )
self.log = LogWindow( self.xml_gui , self)
self.sw = ScriptWidgets( self.xml_gui )
@ -195,6 +197,9 @@ class DamarisGUI:
# lock file to prevent other DAMARIS to start immediatly
self.lockfile = LockFile() # = os.path.expanduser("~/.damaris.lock")
self.id = None
#to stop queued experiments
self.stop_experiment_flag = threading.Event()
exp_script = u""
if exp_script_filename is not None and exp_script_filename != "":
@ -277,11 +282,14 @@ class DamarisGUI:
self.toolbar_run_button.emit("clicked")
gtk.main( )
gtk.gdk.threads_leave( )
self.si = None
self.sw = None
self.config = None
self.xml_gui = None
# event handling: the real acitons in gui programming
# first global events
def quit_event( self, widget, data=None ):
@ -449,16 +457,32 @@ class DamarisGUI:
self.log.textbuffer.create_mark( "lastdumped", self.log.textbuffer.get_end_iter( ), left_gravity=True )
loop_run=0
#user shorter interval because this blocks user events from the GUI
interval = 0.1
self.stop_experiment_flag.clear()
self.id = self.lockfile.add_experiment()
while not self.lockfile.am_i_next():
time.sleep(interval)
self.stop_experiment_flag.wait(interval)
while gtk.events_pending():
gtk.main_iteration(False)
if loop_run > 1:
self.experiment_script_statusbar_label.set_text("Waiting for other experiment to finish")
loop_run = 0
loop_run += interval
if self.stop_experiment_flag.isSet():
#Experiment has been stopped, clean up and leave
self.experiment_script_statusbar_label.set_text("Experiment stopped")
self.state = DamarisGUI.Edit_State
self.sw.enable_editing( )
self.toolbar_run_button.set_sensitive( True )
self.toolbar_stop_button.set_sensitive( False )
self.toolbar_pause_button.set_sensitive( False )
self.toolbar_pause_button.set_active( False )
#delete experiment from queue so that next experiment may start
self.lockfile.del_experiment(self.id)
return
# start experiment
try:
@ -504,6 +528,8 @@ class DamarisGUI:
self.toolbar_stop_button.set_sensitive( False )
self.toolbar_pause_button.set_sensitive( False )
self.toolbar_pause_button.set_active( False )
#delete Experiment from queue
self.lockfile.del_experiment(self.id)
return
# switch to grapics
@ -541,6 +567,33 @@ class DamarisGUI:
r = self.si.data.get( "__recentresult", -1 ) + 1
b = self.si.data.get( "__resultsinadvance", -1 ) + 1
e = self.si.data.get( "__recentexperiment", -1 ) + 1
experimentstarttime = self.si.data.get( "__experimentstarted", 0 )
expectedexperimentruntime = self.si.data.get( "__totalexperimentlength", 0 )
experimentsfinishedtime = 0.0
for i in range(0, b):
experimentsfinishedtime += self.si.data.get("__experimentlengths", {}).get(i, 0.0)
experimentruntime = time.time() - experimentstarttime
experimenttimetext = ""
if experimentstarttime > 0 and expectedexperimentruntime > 0:
runtimemin, runtimesec = divmod(math.floor(experimentruntime), 60)
runtimehours, runtimemin = divmod(runtimemin, 60)
experimenttimetext += " ({:02d}:{:02d}:{:02d} / ".format(int(runtimehours), int(runtimemin), int(runtimesec))
expectedmin, expectedsec = divmod(math.ceil(expectedexperimentruntime), 60)
expectedhours, expectedmin = divmod(expectedmin, 60)
experimenttimetext += "{:02d}:{:02d}:{:02d})".format(int(expectedhours), int(expectedmin), int(expectedsec))
backendtimetext = ""
if experimentsfinishedtime > 0:
finexperimmin, finexperimsec = divmod(round(experimentsfinishedtime), 60)
finexperimhours, finexperimmin = divmod(finexperimmin, 60)
backendtimetext = " ({:02d}:{:02d}:{:02d})".format(int(finexperimhours), int(finexperimmin), int(finexperimsec))
e_text = None
r_text = None
b_text = None
@ -560,6 +613,7 @@ class DamarisGUI:
else:
#print self.si.back_driver.get_messages()
e_text = "Experiment Script Running (%d)" % e
e_text += experimenttimetext
if self.si.res_handling is not None:
if not self.si.res_handling.isAlive( ):
@ -588,6 +642,7 @@ class DamarisGUI:
b_text = "Backend Running"
if b != 0:
b_text += " (%d)" % b
b_text += backendtimetext
if self.dump_thread is not None:
if self.dump_thread.isAlive( ):
@ -656,7 +711,7 @@ class DamarisGUI:
# keep data to display but throw away everything else
self.si = None
# delete locak file so that other experiment can start
# delete lock file so that other experiment can start
self.lockfile.del_experiment(self.id)
return False
@ -904,6 +959,7 @@ class DamarisGUI:
def stop_experiment( self, widget, data=None ):
if self.state in [ DamarisGUI.Run_State, DamarisGUI.Pause_State ]:
self.stop_experiment_flag.set()
if self.si is None:
return
still_running = filter( None, [ self.si.exp_handling, self.si.res_handling, self.si.back_driver ] )
@ -990,7 +1046,8 @@ class DamarisGUI:
# check generic debian location
self.doc_urls[ "Python DAMARIS" ] = "file:///usr/share/doc/python-damaris/html/index.html"
else:
self.doc_urls[ "Python DAMARIS" ] = "https://element.fkp.physik.tu-darmstadt.de/damaris_cms/index.php?id=a-not-so-short-tutorial-on-damaris"
self.doc_urls[ "Python DAMARIS" ] = "https://element.fkp.physik.tu-darmstadt.de/damaris_cms/index.php?id=documentation"
self.doc_browser = None
@ -1060,10 +1117,12 @@ class LogWindow:
writes messages to the log window
"""
def __init__( self, xml_gui ):
def __init__( self, xml_gui, damaris_gui ):
self.xml_gui = xml_gui
self.damaris_gui = damaris_gui
self.textview = self.xml_gui.get_widget( "messages_textview" )
self.textview.connect( "key-press-event", self.textview_keypress)
self.textbuffer = self.textview.get_buffer( )
self.logstream = log
self.logstream.gui_log = self
@ -1086,6 +1145,14 @@ class LogWindow:
self.textbuffer.insert_at_cursor( date_tag + unicode( message ) )
self.textview.scroll_to_mark( self.textbuffer.get_insert( ), 0.1 )
gtk.gdk.threads_leave( )
def textview_keypress( self, widget, event, data=None ):
if event.state & gtk.gdk.CONTROL_MASK != 0:
if event.keyval == gtk.gdk.keyval_from_name("f"):
self.damaris_gui.sw.search(None, None)
return True
return False
def __del__( self ):
self.logstream.gui_log = None
@ -1125,13 +1192,22 @@ class ScriptWidgets:
self.data_handling_textbuffer.set_language(langpython)
self.data_handling_textbuffer.set_highlight_syntax(True)
fontdesc = pango.FontDescription("monospace")
self.experiment_script_textview.set_buffer( self.experiment_script_textbuffer )
self.experiment_script_textview.set_show_line_numbers(True)
self.experiment_script_textview.set_auto_indent(True)
self.experiment_script_textview.set_insert_spaces_instead_of_tabs(True)
self.experiment_script_textview.set_tab_width(4)
self.experiment_script_textview.set_smart_home_end(True)
self.experiment_script_textview.modify_font(fontdesc)
self.data_handling_textview.set_buffer( self.data_handling_textbuffer )
self.data_handling_textview.set_show_line_numbers(True)
self.data_handling_textview.set_auto_indent(True)
self.data_handling_textview.set_insert_spaces_instead_of_tabs(True)
self.data_handling_textview.set_tab_width(4)
self.data_handling_textview.set_smart_home_end(True)
self.data_handling_textview.modify_font(fontdesc)
keywords = """for if else elif in print try finally except global lambda not or pass def
class import from as return yield while continue break assert None True False AccumulatedValue
@ -1455,10 +1531,62 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
elif event.keyval == gtk.gdk.keyval_from_name("f"):
self.search(None, None)
return True
return 0
return False
#Handle Backspace (delete more than one space on line beginning)
if event.keyval == 0xFF08:
textbuffer = widget.get_buffer( )
if textbuffer.get_has_selection():
return False
cursor_mark = textbuffer.get_insert( )
cursor_iter = textbuffer.get_iter_at_mark( cursor_mark )
linestart_iter = cursor_iter.copy( )
linestart_iter.set_line_offset( 0 )
linebegin = textbuffer.get_text( linestart_iter, cursor_iter ).expandtabs(4)
if linebegin.isspace() and len(linebegin) > 0:
linebegin = u' ' * int((len( linebegin ) - 1) / 4) * 4
textbuffer.delete( linestart_iter, cursor_iter )
textbuffer.insert( linestart_iter, linebegin )
return True
elif event.keyval == 0xFF0D:
textbuffer = widget.get_buffer( )
if textbuffer.get_has_selection():
return False
cursor_mark = textbuffer.get_insert( )
cursor_iter = textbuffer.get_iter_at_mark( cursor_mark )
lastchar_iter = cursor_iter.copy()
lastchar = lastchar_iter.backward_char( )
if not lastchar_iter.get_char( ) == u":":
return False
linestart_iter = cursor_iter.copy( )
linestart_iter.set_line_offset( 0 )
spaceend_iter = linestart_iter.copy( )
while (not spaceend_iter.ends_line( ) and not spaceend_iter.is_end( ) and spaceend_iter.get_char( ).isspace( )):
spaceend_iter.forward_char( )
linebegin = textbuffer.get_text( linestart_iter, spaceend_iter ).expandtabs(4)
indent_length = int((int(len(linebegin)/4)+1)*4)
intext = u"\n" + (u" " * indent_length)
textbuffer.insert(cursor_iter, intext)
widget.scroll_to_mark( cursor_mark, 0.0, 0 )
return True
#self.textviews_moved(widget)
return 0
return False
def load_file_as_unicode( self, script_filename ):
script_file = file( script_filename, "rU" )
@ -3209,7 +3337,7 @@ class ScriptInterface:
self.exp_writer = self.res_reader = self.back_driver = None
if self.backend_executable is not None and self.backend_executable != "":
self.back_driver = BackendDriver.BackendDriver( self.backend_executable, spool_dir, clear_jobs,
clear_results)
clear_results )
if self.exp_script:
self.exp_writer = self.back_driver.get_exp_writer( )
if self.res_script:
@ -3229,6 +3357,7 @@ class ScriptInterface:
self.data = DataPool( )
def runScripts( self ):
try:
# get script engines
self.exp_handling = self.res_handling = None

View File

@ -66,6 +66,7 @@ class ExperimentHandling(threading.Thread):
self.traceback=traceback_file.getvalue()
traceback_file=None
return
while exp_iterator is not None and not self.quit_flag.isSet():
# get next experiment from script
try:
@ -86,6 +87,21 @@ class ExperimentHandling(threading.Thread):
if isinstance(job, Experiment):
if self.data is not None:
self.data["__recentexperiment"]=job.job_id+0
# track time when experiment is started. This is placed after executing the script
# and getting the iterator so that time for initializing devices etc. is not included
if "__experimentstarted" not in self.data:
self.data["__experimentstarted"] = time.time()
# track time of experiments in queue, the total time and for each experiment:
if "__totalexperimentlength" not in self.data:
self.data["__totalexperimentlength"] = 0.0
if "__experimentlengths" not in self.data:
self.data["__experimentlengths"] = {}
experimentlength = job.get_length()
self.data["__experimentlengths"][job.job_id+0] = experimentlength
self.data["__totalexperimentlength"] += experimentlength
# relax for a short time
if "__resultsinadvance" in self.data and self.data["__resultsinadvance"]+100<job.job_id:
self.quit_flag.wait(0.05)