Beispiel #1
0
class DSCUSBUILog(DSCUSBUI):
    """Same as :class:`.DSCUSBUI` except that it adds a logger facility. It 
    defines an :attr:`~.DSCUSBUILog.data` that hold the measured data.
    
    Examples
    --------
    
    For a display with a logger ...
    
    >>> dsc = DSCUSBUILog()
    >>> ok = dsc.configure_traits() %(skip)s
    
    """
    #: this holds the measured data
    data = Instance(StructArrayData, ())

    log_timer = Any

    _data = []
    #: set to False to stop writing to :attr:`~.DSCUSBUILog.data`
    do_log = Bool(True, desc='whether logging is activated or not')

    view = dscusbui_log_view

    def _data_default(self):
        return StructArrayData(width=420,
                               height=230,
                               dtype=[('time', 'float'), ('force', 'float'),
                                      ('temperature', 'float')])

    def _timer_function(self):
        value_temp = super(DSCUSBUILog, self)._timer_function()
        if self.do_log and value_temp is not None:
            value, temp = value_temp
            self._data.append((time.time(), value, temp))

    def _log_timer_function(self):
        if len(self._data) != 0:
            data, self._data = self._data, []
            self.data.append(data)

    def start_readout(self, interval=1.):
        """Starts readout process. GUI must be running for this to work...
        """
        super(DSCUSBUILog, self).start_readout(interval)
        try:
            self.log_timer.Start(1000)
        except AttributeError:
            #self._timer_function()
            self.log_timer = Timer(1000, self._log_timer_function)

    def stop_readout(self):
        """Stops readout process
        """
        super(DSCUSBUILog, self).stop_readout()
        self.log_timer.Stop()
Beispiel #2
0
class MainWindow(ApplicationWindow):
    """ The main application window. """

    # The pyface Timer.
    my_timer = Any()

    # Count each time the timer task executes.
    counter = Int

    def __init__(self, **traits):
        """ Creates a new application window. """

        # Base class constructor.
        super(MainWindow, self).__init__(**traits)

        # Add a menu bar.
        self.menu_bar_manager = MenuBarManager(
            MenuManager(
                Action(name='Start Timer', on_perform=self._start_timer),
                Action(name='Stop Timer', on_perform=self._stop_timer),
                Action(name='E&xit', on_perform=self.close),
                name = '&File',
            )
        )

        return

    def _start_timer(self):
        """Called when the user selects "Start Timer" from the menu."""

        if self.my_timer is None:
            # First call, so create a Timer.  It starts automatically.
            self.my_timer = Timer(500, self._timer_task)
        else:
            self.my_timer.Start()

    def _stop_timer(self):
        """Called when the user selecte "Stop Timer" from the menu."""

        if self.my_timer is not None:
            self.my_timer.Stop()


    def _timer_task(self):
        """The method run periodically by the timer."""

        self.counter += 1
        print "counter = %d" % self.counter
Beispiel #3
0
class Animator(HasTraits):
    """ Convenience class to manage a timer and present a convenient
        UI.  This is based on the code in `tvtk.tools.visual`.
        Here is a simple example of using this class::

            >>> from mayavi import mlab
            >>> def anim():
            ...     f = mlab.gcf()
            ...     while 1:
            ...         f.scene.camera.azimuth(10)
            ...         f.scene.render()
            ...         yield
            ...
            >>> anim = anim()
            >>> t = Animator(500, anim.next)
            >>> t.edit_traits()

        This makes it very easy to animate your visualizations and control
        it from a simple UI.

        **Notes**

        If you want to modify the data plotted by an `mlab` function call,
        please refer to the section on: :ref:`mlab-animating-data`
    """

    ########################################
    # Traits.

    start = Button('Start Animation')
    stop = Button('Stop Animation')
    delay = Range(10, 100000, 500,
                  desc='frequency with which timer is called')

    # The internal timer we manage.
    timer = Instance(Timer)

    ######################################################################
    # User interface view

    traits_view = View(Group(Item('start'),
                             Item('stop'),
                             show_labels=False),
                       Item('_'),
                       Item(name='delay'),
                       title='Animation Controller',
                       buttons=['OK'])

    ######################################################################
    # Initialize object
    def __init__(self, millisec, callable, *args, **kwargs):
        """Constructor.

        **Parameters**

          :millisec: int specifying the delay in milliseconds
                     between calls to the callable.

          :callable: callable function to call after the specified
                     delay.

          :\*args: optional arguments to be passed to the callable.

          :\*\*kwargs: optional keyword arguments to be passed to the callable.

        """
        HasTraits.__init__(self)
        self.delay = millisec
        self.timer = Timer(millisec, callable, *args, **kwargs)

    ######################################################################
    # Non-public methods, Event handlers
    def _start_fired(self):
        self.timer.Start(self.delay)

    def _stop_fired(self):
        self.timer.Stop()

    def _delay_changed(self, value):
        t = self.timer
        if t is None:
            return
        if t.IsRunning():
            t.Stop()
            t.Start(value)
Beispiel #4
0
class Animator(HasTraits):
    start = Button('Start Animation')
    stop = Button('Stop Animation')
    next_frame = Button('+')
    prev_frame = Button('-')
    delay = Range(10, 100000, 500)
    loop = Bool(True)

    current_frame = Int(-1)
    _last_frame = Int()

    # TODO use Range(high="trait name")
    render_from_frame = Int()
    render_to_frame = Int()
    render_animation = Button()
    is_rendering = Bool(False) # indicator bool is True when rendering
    is_rendering_animation = Bool(False)

    render_directory = Directory("/tmp", exists=False)
    render_name_pattern = String("frame_%05d.png")

    magnification = Range(1, 128)
    fix_image_size = Bool(False)
    image_size = Tuple(Int(1280), Int(720))

    render = Event()

    enable_cameraman = Bool(False)
    set_keyframe = Button()
    remove_keyframe = Button()

    timer = Instance(Timer)

    traits_view = View( Tabbed(
            Group(
                HGroup(
                    Item('start', show_label=False),
                    Item('stop', show_label=False),
                    Item('next_frame', show_label=False, enabled_when='current_frame < _last_frame'),
                    Item('prev_frame', show_label=False, enabled_when='current_frame > 0'),
                ),
                HGroup(
                    Item(name = 'loop'),
                    Item(name = 'delay'),
                ),
                Item(name = 'current_frame', 
                     editor=RangeEditor(is_float=False, high_name='_last_frame', mode='slider')),
                Group(
                    HGroup(
                        Item(name = 'enable_cameraman', label='enabled'),
                        Item(name = 'set_keyframe', show_label=False),
                        Item(name = 'remove_keyframe', show_label=False),
                        Item(name = 'interpolation_type', object='object._camera_interpolator'),
                    ),
                label = 'Cameraman',
                ),
            label = 'Timeline',
            ),
            Group(
                HGroup(
                    Item('fix_image_size', label="Set Image Size"),
                    Item('magnification', visible_when='not fix_image_size', label='Magnification'),
                    Item('image_size', visible_when='fix_image_size', show_label=False, editor=TupleEditor(cols=2, labels=['W', 'H'])),
                ),
                Item("_"),
                Item("render_directory", label="Target Dir"),
                Item("render_name_pattern", label="Filename Pattern"),
                Item("_"),
                HGroup(
                    Item("render_from_frame", label="from",
                         editor=RangeEditor(is_float=False, low=0, high_name='render_to_frame')),
                    Item("render_to_frame", label="to",
                         editor=RangeEditor(is_float=False, low_name='render_from_frame', high_name='_last_frame')),
                ),
                Item("render_animation", show_label=False),
                label = "Render",
            ),
        ),
        title = 'Animation Controller', 
        buttons = [])


    def __init__(self, num_frames, callable, millisec=40, figure=None, play=True, *args, **kwargs):
        HasTraits.__init__(self)
        self.delay = millisec
        self._last_frame = num_frames - 1
        self._callable = callable
        if figure is None:
            figure = mlab.gcf()
        self._figure = figure
        self._camera_interpolator = tvtk.CameraInterpolator(interpolation_type='spline')
        self._t_keyframes = {}
        self.render_to_frame = self._last_frame
        self.timer = Timer(millisec, self._on_timer, *args, **kwargs)
        if not play:
            self.stop = True
        self._internal_generator = None
        self.current_frame = 0
        self.on_trait_change(self._render, "render, current_frame", dispatch="ui")

    def _render(self):
        self.is_rendering = True
        if self._internal_generator is not None:
            try:
                next(self._internal_generator)
            except StopIteration: # is ok since generator should yield just once to render
                pass 
            except: # catch and re-raise other errors
                raise
            else:
                raise "The render function should be either a simple function or a generator that yields just once to render"
        # before we call the user function, we want to disallow rendering
        # this speeds up animations that use mlab functions
        scene = self._figure.scene
        scene.disable_render = True
        r = self._callable(self.current_frame)
        if isinstance(r, types.GeneratorType):
            next(r)
            # save away generator to yield when another frame has to be displayed
            self._internal_generator = r
        # render scene without dumb hourglass cursor, 
        # can be prevented by setting _interacting before calling render
        old_interacting = scene._interacting
        if self._camera_interpolator.number_of_cameras >= 2 and self.enable_cameraman:
            t = self.current_frame / float(self._last_frame)
            self._camera_interpolator.interpolate_camera(t, mlab.get_engine().current_scene.scene.camera)
            mlab.gcf().scene.renderer.reset_camera_clipping_range()
        scene._interacting = True
        scene.disable_render = False
        scene.render()
        scene._interacting = old_interacting
        self.is_rendering = False

    @on_trait_change('set_keyframe')
    def _set_keyframe(self):
        t = self.current_frame / float(self._last_frame)
        self._camera_interpolator.add_camera(t, mlab.get_engine().current_scene.scene.camera)
        self._t_keyframes[self.current_frame] = t

    def _next_frame_fired(self):
        self.current_frame += 1

    def _prev_frame_fired(self):
        self.current_frame -= 1

    @on_trait_change('remove_keyframe')
    def _remove_keyframe(self):
        if self.current_frame in self._t_keyframes:
            self._camera_interpolator.remove_last_keyframe(self._t_keyframes[self.current_frame])

    def _on_timer(self, *args, **kwargs):
        if self.loop or self.current_frame != self._last_frame:
            self.current_frame = (self.current_frame + 1) % (self._last_frame + 1)
        else:
            self.stop = True

    def _delay_changed(self, value):
        t = self.timer
        if t is None:
            return
        if t.IsRunning():
            t.Stop()
            t.Start(value)

    def _start_fired(self):
        if not self.loop and self.current_frame == self._last_frame:
            self.current_frame = 0
        self.timer.Start(self.delay) 

    def _stop_fired(self):
        self.timer.Stop()

    def _render_animation_fired(self):
        self.stop = True
        n_frames_render = self.render_to_frame - self.render_from_frame
        # prepare the render window
        renwin = self._figure.scene.render_window
        aa_frames = renwin.aa_frames
        renwin.aa_frames = 8
        renwin.alpha_bit_planes = 1
        # turn on off screen rendering
        #renwin.off_screen_rendering = True
        # set size of window
        if self.fix_image_size:
            orig_size = renwin.size
            renwin.size = self.image_size
        # render the frames
        progress = ProgressDialog(title="Rendering", max=n_frames_render, 
                                  show_time=True, can_cancel=True)
        progress.open()
        self.is_rendering_animation = True
        for frame in range(self.render_from_frame, self.render_to_frame + 1):
            # move animation to desired frame, this will also render the scene
            self.current_frame = frame
            # prepare window to image writer
            render = tvtk.WindowToImageFilter(input=renwin, magnification=1)#, input_buffer_type='rgba')
            if not self.fix_image_size:
                render.magnification = self.magnification
            exporter = tvtk.PNGWriter(file_name=path.join(self.render_directory, self.render_name_pattern % frame))

            configure_input(exporter,render)
            exporter.write()
            do_continue, skip = progress.update(frame - self.render_from_frame)
            if not do_continue:
                break
        # reset the render window to old values
        renwin.aa_frames = aa_frames
        if self.fix_image_size:
            renwin.size = orig_size
        #renwin.off_screen_rendering = False
        self.is_rendering_animation = False
        progress.close()
Beispiel #5
0
class Application(HasStrictTraits):
    """ Main GUI application which allows user to set global, Openfoam
    and Liggghts parameters of the computation and visualize it
    with Mayavi
    """
    #: The global settings for the calculation
    global_settings = Instance(GlobalParametersModel)

    #: The Liggghts settings for the calculation
    liggghts_settings = Instance(LiggghtsModel)

    #: The Openfoam settings for the calculation
    openfoam_settings = Instance(OpenfoamModel)

    # The mayavi sources, associated to the following datasets:
    # first element is the openfoam mesh, second and third are
    # the flow and walls particles, respectively.
    # It is an invariant. The cuds gets changed as we select different
    # frames.
    sources = Tuple(Instance(CUDSSource),
                    Instance(CUDSSource),
                    Instance(CUDSSource))

    # All the frames resulting from the execution of our computation.
    frames = List(Tuple(VTKMesh, VTKParticles, VTKParticles))

    # The frame to visualize
    current_frame_index = Int()

    # The physical current frame, for practicality
    _current_frame = Either(
        None,
        Tuple(VTKMesh, VTKParticles, VTKParticles)
    )

    #: The button on which the user will click to run the
    # calculation
    run_button = Button("Run")

    first_button = Button("First")
    previous_button = Button("Previous")
    play_stop_button = Button()
    play_stop_label = Str("Play")
    next_button = Button("Next")
    last_button = Button("Last")
    save_button = Button("Save...")

    play_timer = Instance(Timer)

    #: The pop up dialog which will show the status of the
    # calculation
    progress_dialog = Instance(ProgressDialog)

    #: Boolean representing if the application should allow
    # operations or not
    interactive = Bool(True)

    #: The Mayavi traits model which contains the scene and engine
    mlab_model = Instance(MlabSceneModel, ())

    #: Event object which will be useful for error dialog
    calculation_error_event = Event(Str)

    #: True if the calculation can be safely run, False otherwise
    valid = Bool(False)

    #: Dictionary of the namespace associated to the shell editor
    shell = Dict()

    # Private traits.
    #: Executor for the threaded action.
    _executor = Instance(futures.ThreadPoolExecutor)

    # Lock to synchronize the execution loop and the storage of the Datasets
    # from application specific cuds to VTK Datasets. We need to do so because
    # the computational engine can change the datasets, so we need to hold
    # the computational engine until we are done "copying and storing" each
    # frame
    _event_lock = Instance(threading.Event, ())

    traits_view = View(
        HSplit(
            VSplit(
                VGroup(
                    Tabbed(
                        UItem('global_settings', style='custom'),
                        UItem('liggghts_settings', style='custom'),
                        UItem('openfoam_settings',
                              label='OpenFOAM settings',
                              style="custom"),
                    ),
                    UItem(
                        name='run_button',
                        enabled_when='valid'
                    ),
                    enabled_when='interactive',
                ),
                UItem('shell', editor=ShellEditor())
            ),
            VGroup(
                UItem(
                    name='mlab_model',
                    editor=SceneEditor(scene_class=MayaviScene)
                ),
                HGroup(
                    UItem(
                        name="first_button",
                        enabled_when=(
                            "current_frame_index > 0 and play_timer is None"),
                    ),
                    UItem(
                        name="previous_button",
                        enabled_when=(
                            "current_frame_index > 0 and play_timer is None"),
                    ),
                    UItem(
                        name="play_stop_button",
                        editor=ButtonEditor(label_value="play_stop_label")
                    ),
                    UItem(
                        name="next_button",
                        enabled_when=(
                            "current_frame_index < len(frames) "
                            "and play_timer is None"),
                    ),
                    UItem(
                        name="last_button",
                        enabled_when=(
                            "current_frame_index < len(frames) "
                            "and play_timer is None"),
                    ),
                    Item(name="current_frame_index", style="readonly"),
                    UItem(name="save_button"),
                    enabled_when=(
                        'interactive and len(frames) > 0')
                )
            ),
        ),
        title='Simphony UI',
        resizable=True,
        width=1.0,
        height=1.0
    )

    @on_trait_change('calculation_error_event', dispatch='ui')
    def show_error(self, error_message):
        error(
            None,
            'Oups ! Something went bad...\n\n{}'.format(error_message),
            'Error'
        )

    @on_trait_change('openfoam_settings:valid,'
                     'liggghts_settings:valid')
    def update_valid(self):
        self.valid = (self.openfoam_settings.valid and
                      self.liggghts_settings.valid)

    @on_trait_change('run_button')
    def run_calc(self):
        """ Function which will start the calculation on a secondary
        thread on run button click

        Raises
        ------
        RuntimeError
            If the calculation is already running
        """
        if not self.interactive:
            raise RuntimeError('Unable to start calculation. Another '
                               'operation is already in progress')
        self.frames = []
        self.interactive = False
        self.progress_dialog.title = 'Calculation running...'
        self.progress_dialog.open()
        future = self._executor.submit(self._run_calc_threaded)
        future.add_done_callback(self._calculation_done)

    def _add_sources_to_scene(self):
        """Add the sources to the main scene."""
        mayavi_engine = self.mlab_model.engine
        mayavi_engine.add_source(self.sources[0])

        mayavi_engine.add_module(Surface())
        self._add_liggghts_source_to_scene(self.sources[1])
        self._add_liggghts_source_to_scene(self.sources[2])

    def _add_liggghts_source_to_scene(self, source):
        """ Function which add to liggghts source to the Mayavi scene

        Parameters
        ----------
        source :
            The mayavi source linked to the dataset
        """
        mayavi_engine = self.mlab_model.engine

        # Create Sphere glyph
        sphere_glyph_module = Glyph()

        # Add Liggghts sources
        mayavi_engine.add_source(source)

        try:
            source.point_vectors_name = 'VELOCITY'
        except TraitError:
            # The data is not available in the dataset for some reason.
            # Add the modules anyway, but don't select it.
            pass

        # Add sphere glyph module
        mayavi_engine.add_module(sphere_glyph_module)

        sphere_glyph_module.glyph.glyph_source.glyph_source = SphereSource()
        sphere_glyph_module.glyph.scale_mode = 'scale_by_scalar'
        sphere_glyph_module.glyph.glyph.range = [0.0, 1.0]
        sphere_glyph_module.glyph.glyph_source.glyph_source.radius = 1.0

        # Velocities are in meter/second, this scale factor makes
        # 1 graphical unit = 1 millimeter/sec
        arrow_scale_factor = 1.0

        arrow_glyph_module = Glyph()

        mayavi_engine.add_module(arrow_glyph_module)

        arrow_glyph_module.glyph.scale_mode = 'scale_by_vector'
        arrow_glyph_module.glyph.color_mode = 'color_by_vector'
        arrow_glyph_module.glyph.glyph.range = [0.0, 1.0]
        arrow_glyph_module.glyph.glyph.scale_factor = arrow_scale_factor

    def _remove_sources_from_scene(self):
        for source in self.sources:
            try:
                self.mlab_model.mayavi_scene.remove_child(source)
            except ValueError:
                pass

    def _run_calc_threaded(self):
        """ Function which will run the calculation. This function
        is only run by the secondary thread
        """
        try:
            return run_calc(
                self.global_settings,
                self.openfoam_settings,
                self.liggghts_settings,
                self.progress_callback,
                self._event_lock
            )
        except Exception:
            self.calculation_error_event = traceback.format_exc()
            log.exception('Error during the calculation')
            return None

    def _calculation_done(self, future):
        """ Function which will return the result of the computation to
        the main thread

        Parameters
        ----------
        future
            Object containing the result of the calculation
        """
        GUI.invoke_later(self._computation_done, future.result())

    def _computation_done(self, datasets):
        self.progress_dialog.update(100)
        if datasets is not None:
            self._append_frame(datasets)
            self._to_last_frame()
        self.interactive = True

    def _append_frame(self, datasets):
        self.frames.append(
            (
                VTKMesh.from_mesh(datasets[0]),
                VTKParticles.from_particles(datasets[1]),
                VTKParticles.from_particles(datasets[2])
            )
        )

    @on_trait_change("current_frame_index,frames[]")
    def _sync_current_frame(self):
        """Synchronizes the current frame with the index and the available
        frames."""
        try:
            self._current_frame = self.frames[self.current_frame_index]
        except IndexError:
            self._current_frame = None

    @on_trait_change("_current_frame")
    def _update_sources_with_current_frame(self, object, name, old, new):
        """Called when the current frame is changed, updates the sources
        with the new data. Parameters are from traits interface."""
        scene = self.mlab_model.mayavi_scene
        scene.scene.disable_render = True

        if new is None:
            self._remove_sources_from_scene()
        else:
            self._remove_sources_from_scene()
            for i in xrange(len(self._current_frame)):
                self.sources[i].cuds = self._current_frame[i]
            self._add_sources_to_scene()

        scene.scene.disable_render = False

    def progress_callback(self, datasets, current_iteration, total_iterations):
        """ Function called in the secondary thread. It will transfer the
        progress status of the calculation to the main thread

        Parameters
        ----------
        progress
            The progress of the calculation (Integer in the range [0, 100])
        """
        progress = current_iteration/total_iterations*100

        GUI.invoke_later(self._append_frame_and_continue, datasets)
        GUI.invoke_later(self.progress_dialog.update, progress)

    def _append_frame_and_continue(self, datasets):
        self._append_frame(datasets)
        self._event_lock.set()
        self.current_frame_index = len(self.frames) - 1

    def reset(self):
        """ Function which reset the Mayavi scene.
        """
        # Clear scene
        self._remove_sources_from_scene()

    @on_trait_change('first_button')
    def _to_first_frame(self):
        """Goes to the first frame"""
        self.current_frame_index = 0

    @on_trait_change('last_button')
    def _to_last_frame(self):
        """Goes to the last frame"""
        self.current_frame_index = len(self.frames) - 1

    @on_trait_change('previous_button')
    def _to_prev_frame(self):
        """Goes to the previous frame"""
        frame = self.current_frame_index - 1
        if frame < 0:
            frame = 0
        self.current_frame_index = frame

    @on_trait_change('next_button')
    def _to_next_frame(self):
        """Goes to the next frame"""
        frame = self.current_frame_index + 1
        if frame >= len(self.frames):
            frame = len(self.frames) - 1
        self.current_frame_index = frame

    @on_trait_change('play_stop_button')
    def _start_stop_video(self):
        """Starts the video playing"""
        if self.play_timer is None:
            self.play_timer = Timer(500, self._next_frame_looped)
        else:
            self.play_timer.Stop()
            self.play_timer = None

    @on_trait_change("save_button")
    def _save_images(self):
        """Saves the current frames in individual images."""
        dialog = DirectoryDialog()
        if dialog.open() != OK:
            return

        self.progress_dialog.title = 'Saving images...'
        self.progress_dialog.open()

        dirpath = dialog.path

        self.interactive = False
        try:
            for frame_index in xrange(len(self.frames)):
                self.current_frame_index = frame_index
                mlab.savefig(os.path.join(
                    dirpath, "frame-{}.png".format(frame_index)
                ))
                progress = 100*frame_index/len(self.frames)
                self.progress_dialog.update(progress)

            self.progress_dialog.update(100)
        except Exception:
            self.calculation_error_event = traceback.format_exc()
            log.exception('Error while saving')
        finally:
            self.interactive = True

    @on_trait_change('play_timer')
    def _change_play_button_label(self):
        """Changes the label from play to stop and vice-versa"""
        self.play_stop_label = "Stop" if self.play_timer else "Start"

    def _next_frame_looped(self):
        """Goes to the next frame, but loop back to the first when over."""
        if len(self.frames) == 0:
            self.current_frame_index = 0

        self.current_frame_index = (
            self.current_frame_index + 1
            ) % len(self.frames)

    def __executor_default(self):
        return futures.ThreadPoolExecutor(max_workers=1)

    def _progress_dialog_default(self):
        return ProgressDialog(
            min=0,
            max=100,
        )

    def _sources_default(self):
        return CUDSSource(), CUDSSource(), CUDSSource()

    def _global_settings_default(self):
        return GlobalParametersModel()

    def _liggghts_settings_default(self):
        return LiggghtsModel()

    def _openfoam_settings_default(self):
        return OpenfoamModel()
Beispiel #6
0
class BaseSimpleInstrumentUI(HasTraits):
    """This class serves as a base class for all custom instrument devices.
    It provides a basic interface with buttons and actions defined. When subclassing
    you need to define actions for the io_button and settings_button events and make
    sure that _initialized, _LED, _status_message attributes are in control.
    You also need to define the report method, which is responsible for
    a continuous device operation checking, like checking for status, motor positions.. etc.
    """
    #: logger from the logging module.
    logger = Any

    _initialized = Bool(False)
    _LED = Int(desc='device IO status')
    _status_message = Str(desc='status message')

    @property
    def initialized(self):
        """A bool that determines if the device is active or not"""
        return self._initialized

    @property
    def LED(self):
        """Determines device LED indicator status, an int of values 0(off),1(on),2(busy),-1(error)"""
        return self._LED

    @property
    def status_message(self):
        """"Returns device status message"""
        return self._status_message

    def _logger_default(self):
        return logger

    @display_exception('Could not initialize.')
    def _init_button_fired(self):
        if self.initialized:
            self.stop_timer()
            self.close()
        else:
            self.init()
            self.start_timer()

    def _io_button_fired(self):
        information(None, 'IO parameters not defined for this device.')

    def _settings_button_fired(self):
        information(None, 'Settings not defined for this device.')

    def __initialized_changed(self, value):
        if value == False:
            self._LED = 0
            self._status_message = ''

    def start_timer(self, interval=TIMER_INTERVAL):
        """Starts timer process. It updates status of the instrument with the
        interval specified, collects data, etc, depending on the defined
        update method
        """
        self.logger.info('Starting readout.')
        try:
            self.timer.Start(interval * 1000)
        except AttributeError:
            self.timer = Timer(interval * 1000, self._update)

    def stop_timer(self):
        """Stops timer process.
        """
        self.logger.info('Stopping readout.')
        try:
            self.timer.Stop()
        except AttributeError:
            pass

    def _update(self):
        self.logger.debug('Update called.')
        try:
            self.update()
        except Exception as e:
            self.logger.critical('Error in update. ' + e.message)
            raise e

    def update(self):
        """Does the status checking.. etc.
        """
        report = self.report()
        self.update_status_message(report)
        self.update_LED(report)

    def update_status_message(self, report):
        self._status_message = report.get('status_message', '')

    def update_LED(self, report):
        if report.get('error') == True:
            self._LED = -1
        else:
            self._LED = self._initialized

    def report(self):
        """Gives a device report. This function is called by the update methhod
        """
        return {'status_message': 'OK', 'error': False}

    def init(self, *args, **kw):
        """Needs to be defined in a subclass. Opens port, initializes..
        """
        self._initialized = True

    def close(self):
        """Needs to be defined in a subclass.
        """
        self._initialized = False
Beispiel #7
0
class TimeTrace(GetSetItemsMixin):

    number_of_points = Int(200,
                           desc='Length of Count Trace',
                           label='Number of points',
                           mode='text',
                           auto_set=False,
                           enter_set=True)
    seconds_per_point = Float(0.1,
                              desc='Seconds per point [s]',
                              label='Seconds per point [s]',
                              mode='text',
                              auto_set=False,
                              enter_set=True)

    # trace data
    count_rate = Array()
    time = Array()

    enable_0 = Bool(True, label='channel 0', desc='enable channel 0')
    enable_1 = Bool(False, label='channel 1', desc='enable channel 1')
    enable_2 = Bool(False, label='channel 2', desc='enable channel 2')
    enable_3 = Bool(False, label='channel 3', desc='enable channel 3')
    enable_4 = Bool(False, label='channel 4', desc='enable channel 4')
    enable_5 = Bool(False, label='channel 5', desc='enable channel 5')
    enable_6 = Bool(False, label='channel 6', desc='enable channel 6')
    enable_7 = Bool(False, label='channel 7', desc='enable channel 7')

    channels = Instance(list, factory=list)

    plot = Instance(Plot)
    plot_data = Instance(ArrayPlotData)

    start_button = Button(label='start', show_label=False)
    stop_button = Button(label='stop', show_label=False)
    clear_button = Button(label='clear', show_label=False)

    get_set_items = [
        'count_rate', 'time', 'channels', 'number_of_points',
        'seconds_per_point'
    ]

    def __init__(self, tagger, **kwargs):
        super(TimeTrace, self).__init__(**kwargs)
        self.tagger = tagger
        self._reset()

    def _channels_default(self):
        return list(
            np.arange(8)[np.array([
                self.enable_0, self.enable_1, self.enable_2, self.enable_3,
                self.enable_4, self.enable_5, self.enable_6, self.enable_7
            ])])

    @on_trait_change(
        'enable_0,enable_1,enable_2,enable_3,enable_4,enable_5,enable_6,enable_7'
    )
    def _update_channels(self):
        self.channels = self._channels_default()

    @on_trait_change('channels,number_of_points,seconds_per_point')
    def _reset(self):
        if hasattr(self, '_timer'):
            self._timer.Stop()
        self._create_plot()
        self._counter = Counter(self.tagger, self.channels,
                                int(self.seconds_per_point * 1e12),
                                self.number_of_points)
        self.time = self._counter.getIndex() * 1e-12
        self.count_rate = self._counter.getData() / self.seconds_per_point
        self._timer = Timer(200, self._refresh_data)
        self._timer.Start()

    def _refresh_data(self):
        self.count_rate = self._counter.getData() / self.seconds_per_point

    def _count_rate_changed(self, new):
        for i, line_i in enumerate(new):
            self.plot_data.set_data('channel_' + str(self.channels[i]), line_i)

    def _time_changed(self, new):
        self.plot_data.set_data('time', new)

    def _create_plot(self):
        kwargs = {}
        for i, channel_i in enumerate(self.channels):
            kwargs['channel_' + str(channel_i)] = np.array(())
        data = ArrayPlotData(time=np.array(()), **kwargs)
        plot = Plot(data,
                    width=100,
                    height=100,
                    resizable='hv',
                    padding_left=96,
                    padding_bottom=32)
        plot.index_axis.title = 'time [s]'
        plot.value_axis.title = 'count rate [counts/s]'

        color_map = {
            0: 'blue',
            1: 'red',
            2: 'green',
            3: 'black',
            4: 'brown',
            5: 'yellow',
            6: 'magenta',
            7: 'cyan'
        }

        for channel in self.channels:
            plot.plot(('time', 'channel_' + str(channel)),
                      type='line',
                      color=color_map[channel],
                      name='channel ' + str(channel))

        plot.legend.align = 'll'
        if len(self.channels) > 1:
            plot.legend.visible = True
        else:
            plot.legend.visible = False

        self.plot_data = data
        self.plot = plot

    def _clear_button_fired(self):
        self._counter.clear()
        self.count_rate = self._counter.getData()

    def _start_button_fired(self):
        self._counter.start()
        self._timer.Start()

    def _stop_button_fired(self):
        self._counter.stop()
        self._timer.Stop()

    def __del__(self):
        self._timer.Stop()

    traits_view = View(
        VGroup(
            HGroup(
                Item('clear_button', show_label=False),
                Item('start_button', show_label=False),
                Item('stop_button', show_label=False),
                Item('save_button', show_label=False),
            ),
            HGroup(
                Item('plot',
                     editor=ComponentEditor(size=(100, 100)),
                     show_label=False),
                VGroup(
                    Item('enable_0'),
                    Item('enable_1'),
                    Item('enable_2'),
                    Item('enable_3'),
                    Item('enable_4'),
                    Item('enable_5'),
                    Item('enable_6'),
                    Item('enable_7'),
                ),
            ),
            HGroup(
                Item('number_of_points'),
                Item('seconds_per_point'),
            ),
        ),
        title='Counter',
        width=780,
        height=520,
        buttons=[],
        resizable=True,
    )
class RemoteCommandServer(ConfigLoadable):
    '''
    '''
    simulation = False
    _server = None
    repeater = Instance(CommandRepeater)
    processor = Instance(CommandProcessor)

    host = Str(enter_set=True, auto_set=False)
    port = Int(enter_set=True, auto_set=False)
    klass = Str

    loaded_port = None
    loaded_host = None

    packets_received = Int
    packets_sent = Int
    repeater_fails = Int

    cur_rpacket = String
    cur_spacket = String

    server_button = Event
    server_label = Property(depends_on='_running')
    _running = Bool(False)
    _connected = Bool(False)

    save = Button
    _dirty = Bool(False)

    run_time = Str
    led = Instance(LED, ())

    use_ipc = True

    def _repeater_default(self):
        '''
        '''
        if globalv.use_ipc:
            c = CommandRepeater(logger_name='{}_repeater'.format(self.name),
                                name=self.name,
                                configuration_dir_name='servers')
            if c.bootstrap():
                return c

    def _repeater_fails_changed(self, old, new):
        if new != 0:
            self.repeater.led.state = 0

    def load(self, *args, **kw):
        '''
        '''

        config = self.get_configuration()
        if config:

            server_class = self.config_get(config, 'General', 'class')
            if server_class is None:
                return

            if server_class == 'IPCServer':
                path = self.config_get(config, 'General', 'path')
                if path is None:
                    self.warning('Path not set. use path config value')
                    return
                addr = path
                self.host = path
                if os.path.exists(path):
                    os.remove(path)
            else:
                if LOCAL:
                    host = 'localhost'
                else:
                    host = self.config_get(config,
                                           'General',
                                           'host',
                                           optional=True)
                port = self.config_get(config, 'General', 'port', cast='int')

                if host is None:
                    host = socket.gethostbyname(socket.gethostname())
                if port is None:
                    self.warning('Host or Port not set {}:{}'.format(
                        host, port))
                    return
                elif port < 1024:
                    self.warning('Port Numbers < 1024 not allowed')
                    return
                addr = (host, port)

                self.host = host
                self.port = port if port else 0

                self.loaded_host = host
                self.loaded_port = port
            self.klass = server_class[:3]

            ds = None
            if config.has_option('Requests', 'datasize'):
                ds = config.getint('Requests', 'datasize')

            ptype = self.config_get(config, 'Requests', 'type', optional=False)
            if ptype is None:
                return

            self.datasize = ds
            self.processor_type = ptype

            self._server = self.server_factory(server_class, addr, ptype, ds)

            # add links
            for link in self.config_get_options(config, 'Links'):
                # note links cannot be stopped
                self._server.add_link(link,
                                      self.config_get(config, 'Links', link))

            if self._server is not None and self._server.connected:
                addr = self._server.server_address
                #                saddr = '({})'.format(','.join(addr if isinstance(addr, tuple) else (addr,)))
                saddr = '({})'.format(addr)
                msg = '%s - %s' % (server_class, saddr)
                self.info(msg)
                self._connected = True
                return True
            else:
                self._connected = False
                self.warning('Cannot connect to {}'.format(addr))

    def server_factory(self, klass, addr, ptype, ds):
        '''
        '''
        # from tcp_server import TCPServer
        # from udp_server import UDPServer

        module = __import__('pychron.messaging.{}_server'.format(
            klass[:3].lower()),
                            fromlist=[klass])
        factory = getattr(module, klass)

        #        gdict = globals()
        #        if handler in gdict:
        #            handler_klass = gdict[handler]

        #        server = gdict[server_class]
        if ds is None:
            ds = 2**10
#        return server(self, ptype, ds, addr, handler_klass)
        return factory(self, ptype, ds, addr)

    def open(self):
        '''
        '''
        self._running = True
        # t = threading.Thread(target = self._server.serve_forever)
        t = threading.Thread(target=self.start_server)
        t.start()

        return True

    def start_server(self):
        SELECT_TIMEOUT = 1
        #        THREAD_LIMIT = 15
        while self._running:
            try:
                readySocket = select.select([self._server.socket], [], [],
                                            SELECT_TIMEOUT)
                if readySocket[0]:
                    self._server.handle_request()
#                    if threading.activeCount() < THREAD_LIMIT:
#                        self._server.handle_request()

            except:
                pass
#        self._server.socket.close()

    def shutdown(self):
        '''
        '''
        self._connected = False
        if self._server is not None:
            #            self._server.shutdown()
            self._server.socket.close()

            self._running = False

    def traits_view(self):
        '''
        '''
        cparams = VGroup(
            HGroup(
                Item('led', show_label=False, editor=LEDEditor()),
                Item('server_button',
                     show_label=False,
                     editor=ButtonEditor(label_value='server_label'),
                     enabled_when='_connected'),
            ),
            Item('host', visible_when='not _running'),
            Item('port', visible_when='not _running'),
            show_border=True,
            label='Connection',
        )
        stats = Group(Item('packets_received', style='readonly'),
                      Item('cur_rpacket', label='Received', style='readonly'),
                      Item('packets_sent', style='readonly'),
                      Item('cur_spacket', label='Sent', style='readonly'),
                      Item('repeater_fails', style='readonly'),
                      Item('run_time', style='readonly'),
                      show_border=True,
                      label='Statistics',
                      visible_when='_connected')

        buttons = HGroup(Item('save', show_label=False, enabled_when='_dirty'))
        v = View(
            VGroup(cparams, stats, buttons),
            handler=RCSHandler,
        )
        return v

    def _run_time_update(self):
        '''
        '''

        #        t = datetime.datetime.now() - self.start_time

        #        h = t.seconds / 3600
        #        m = (t.seconds % 3600) / 60
        #        s = (t.seconds % 3600) % 60

        t, h, m, s = diff_timestamp(datetime.datetime.now(), self.start_time)

        rt = '{:02n}:{:02n}:{:02n}'.format(h, m, s)
        if t.days:
            rt = '{} {:02n}:{:02n}:{:02n}'.format(t.days, h, m, s)
        self.run_time = rt

    def __running_changed(self):
        '''
        '''
        if self._running:
            self.start_time = datetime.datetime.now()
            self.timer = Timer(1000, self._run_time_update)
        else:
            self.timer.Stop()

    def _anytrait_changed(self, name, value):
        '''

        '''
        if name in ['host', 'port']:
            attr = 'loaded_{}'.format(name)
            a = getattr(self, attr)
            if value != a and a is not None:
                self._dirty = True

    def _save_fired(self):
        '''
        '''

        self.shutdown()
        config = self.get_configuration()
        for attr in ['host', 'port']:
            a = getattr(self, attr)
            setattr(self, 'loaded_{}'.format(attr), a)
            config.set('General', attr, a)
            self.write_configuration(config)
            self.load()
        self._dirty = False

    def _server_button_fired(self):
        '''
        '''
        if self._running:
            self.shutdown()
        else:
            # reset the stats
            self.packets_received = 0
            self.packets_sent = 0
            self.cur_rpacket = ''
            self.cur_spacket = ''
            self.repeater_fails = 0

            #            self._server = self.server_factory('TCPServer',
            #                                               (self.host, self.port),
            #                                               self.handler,
            #                                               self.processor_type,
            #                                               self.datasize
            #                                             )
            #            self.open()
            self.bootstrap()

    def _get_server_label(self):
        '''
        '''
        return 'Start' if not self._running else 'Stop'
Beispiel #9
0
class Progress(HasTraits):
    """Use this for progress tracking, estimating time to complete and to obtain
    progress messages.
    
    Examples
    --------

    >>> p = Progress()
    >>> p.start(9) #9 tasks to finish, timer is started
    >>> p.percentage
    0
    >>> p.task_add() # 10 tasks to finish
    >>> p.task_done() # 9 tasks to finish
    >>> p.tasks_all #10 tasks in total
    10
    >>> p.tasks_done #one completed
    1
    >>> p.percentage
    10
    >>> p.stop() #0 tasks to finish, timer is stopped
    >>> p.percentage
    100
    >>> p.task_add() # 1 task to finish, timer is started
    """
    #: integer describing how many tasks have completed in percentage
    percentage = Property(Int, depends_on='tasks_done,tasks_all')
    #: descirbes how many tasks have completed. This string is empty when all tasks done
    message = Property(Str, depends_on='percentage')
    #: specifies time when start is called
    start_time = Float()
    # specifies current time. This gets updated every second by the timer
    current_time = Float
    #: specifies total run time (a timedelta)
    run_time = Property(depends_on='current_time,start_time')
    #: specifies total run time (a timedelta)
    run_time_str = Property(depends_on='run_time')
    #: estimated time to complete (a timedelta)
    estimated_time = Property(depends_on='start_time,percentage,run_time')
    #: estimated time string to complete
    estimated_time_str = Property(Str, depends_on='estimated_time')
    #: number of all tasks to complete
    tasks_all = Range(low=0, value=0)
    #: number of completed tasks
    tasks_done = Range(low=0, value=0)
    #: calculated speed of task completion... needed for estimating time to complete
    speed = Property(depends_on='percentage')
    #: a timer that updates current time, should have a pyface Timer interface
    timer = Any

    def start(self, tasks):
        """This can be called to start timer and to define unfinished tasks
        """
        self.tasks_all = tasks
        self.start_time = time.time()
        try:
            self.timer.Start(1000)
        except AttributeError:
            self.timer = Timer(1000, self._timer_task)

    def task_done(self):
        """This must be called when task is finished
        """
        self.tasks_done += 1
        if self.percentage == 100:
            self.stop()

    def task_add(self):
        """This can be called either after start function to add a task
        It can be called without calling start first.
        """
        if self.tasks_all == 0:
            self.start(1)
        else:
            self.tasks_all += 1

    def stop(self):
        """This clears progress and puts everything to initial state
        """
        try:
            self.timer.Stop()
        except AttributeError:
            pass
        self.tasks_done = 0
        self.tasks_all = 0

    def _timer_task(self):
        self.current_time = time.time()

    @cached_property
    def _get_run_time(self):
        return datetime.timedelta(
            seconds=max(self.current_time - self.start_time, 0))

    @cached_property
    def _get_percentage(self):
        try:
            return min(int((100. * self.tasks_done) / self.tasks_all), 100)
        except ZeroDivisionError:
            return 100

    @cached_property
    def _get_speed(self):
        try:
            return 1.0 * self.percentage / (self.run_time.seconds)
        except ZeroDivisionError:
            return 0.

    @cached_property
    def _get_message(self):
        if self.percentage == 100:
            return ''
        else:
            return 'In progress ... %d%% completed.' % self.percentage

    @cached_property
    def _get_estimated_time(self):
        if self.percentage == 100:
            return
        else:
            try:
                return datetime.timedelta(seconds=int(100. / self.speed -
                                                      self.run_time.seconds))
            except ZeroDivisionError:
                return

    @cached_property
    def _get_estimated_time_str(self):
        if self.estimated_time:
            return 'Estimated time: %s' % self.estimated_time
        else:
            return ''

    @cached_property
    def _get_run_time_str(self):
        return 'Total time: %s' % self.run_time

    def __del__(self):
        self.stop()
class DACChannel(traits.HasTraits):
    def __init__(self, setchannelName, port, connection, rpiADC):
        self.channelName = setchannelName
        self.x = ad.ADEvalBC()
        self.port = port
        self.connection = connection
        self.rpiADC = rpiADC
        self.wmLockTimer = Timer(1000, self.wmLock2)
        self.wmLockTimer.Stop()
#        time.sleep(5)
#        self.start_timer()

    update = traits.Button()
    set = traits.Button()

    pinMode = '0'
    relockMode = traits.Enum("Manual Mode", "Doubling cavity Relock",
                             "Wavemeter Relock", "Wavemeter Lock")
    #set_Relock = traits.Button()

    #---- MANUAL MODE ----#

    voltage = traits.Float(desc="Voltage of the Channel")
    setVoltage = traits.Float(desc="set Voltage")
    powerModeMult = 0
    channelName = traits.Str(desc="Name")
    channelDescription = traits.Str(desc="Description")
    powerMode = traits.Enum(
        5,
        10,
        10.8,
        desc="power Mode",
    )
    bipolar = traits.Bool(desc="Bipolarity")
    channelMessage = traits.Str()
    bytecode = traits.Str()
    port = traits.Str()

    def pinSet(self, channel, mode):
        cmd = "pin=" + channel + mode
        self.connection.send(cmd)
        #print cmd

    def _update_fired(self):
        if self.bipolar == True:
            a = "bipolar"
            bip = True
            self.powerModeMult = -1
        else:
            a = "unipolar"
            bip = False
            self.powerModeMult = 0

        cmd = "cmd=" + self.x.setMaxValue(self.port, self.powerMode, bip)
        #print cmd
        self.connection.send(cmd)
        b = "Mode set to %.1f" % self.powerMode
        self.channelMessage = b + ' ' + a
        self._set_fired()

    def _set_fired(self):
        if ((self.setVoltage > self.powerMode) and (self.bipolar == False)):
            print "setVoltage out of bounds. Not sending."
        elif ((abs(self.setVoltage) > self.powerMode)
              and (self.bipolar == True)):
            print "setVoltage out of bounds. Not sending."
        else:
            cmd = "cmd=" + self.x.generate_voltage(
                self.port, self.powerMode, self.powerModeMult * self.powerMode,
                self.setVoltage)
            self.connection.send(cmd)
            self.bytecode = self.x.generate_voltage(
                self.port, self.powerMode, self.powerModeMult * self.powerMode,
                self.setVoltage)
            self.voltage = self.setVoltage

#---- MANUAL MODE GUI ----#

    voltageGroup = traitsui.HGroup(traitsui.VGroup(
        traitsui.Item('voltage',
                      label="Measured Voltage",
                      style_sheet='* { font-size: 18px;  }',
                      style="readonly"),
        traitsui.Item('setVoltage', label="Set Value"),
    ),
                                   traitsui.Item('set', show_label=False),
                                   show_border=True)

    powerGroup = traitsui.VGroup(traitsui.HGroup(
        traitsui.Item('powerMode', label="Power Mode"),
        traitsui.Item('bipolar'),
    ),
                                 traitsui.HGroup(
                                     traitsui.Item('update', show_label=False),
                                     traitsui.Item('channelMessage',
                                                   show_label=False,
                                                   style="readonly"),
                                 ),
                                 traitsui.Item('bytecode',
                                               show_label=False,
                                               style="readonly"),
                                 traitsui.Item('switch_Lock'),
                                 show_border=True)

    manualGroup = traitsui.VGroup(traitsui.Item('channelName',
                                                label="Channel Name",
                                                style="readonly"),
                                  voltageGroup,
                                  show_border=True,
                                  visible_when='relockMode == "Manual Mode"')

    #---- DOUBLING CAVITY MODE GUI----#

    adcChannel = traits.Enum(0,
                             1,
                             2,
                             3,
                             4,
                             5,
                             6,
                             7,
                             8,
                             9,
                             10,
                             11,
                             12,
                             desc="Channel of the rpiADC")
    adcVoltage = traits.Float(desc="Voltage on rpiADC Channel")
    DCscan_and_lock = traits.Button()
    switch_Lock = traits.Button()
    DCconnect = traits.Bool()
    DCautolock = traits.Button()
    DCadcVoltages = None
    DCadcVoltagesMean = None
    DCtolerance = 0.01  #Volt
    DCmistakeCounter = 0
    DCminBoundary = traits.Float()
    DCmaxBoundary = traits.Float()
    DCnoOfSteps = traits.Int()

    #Updates voltage of the selected channel"
    def _adcVoltage_update(self):
        self.adcVoltage = self._adcVoltage_get()

#Gets voltage of the selected channel via rpiADC Client

    def _adcVoltage_get(self):
        return self.rpiADC.getResults()[self.adcChannel]
#        print "latest results = %s " % self.rpiADC.latestResults
#        self.rpiADC.getResults()
#        print "latest results = %s " % self.rpiADC.latestResults
#        if self.adcChannel in self.rpiADC.latestResults:
#            return self.rpiADC.latestResults[self.adcChannel]
#        else:
#            return -999

#As soon as the connect button is checked, automatic updating of the adc voltage is initiated.
#When it is unchecked, the update stops

    def _DCconnect_changed(self):

        if self.DCconnect == True:
            self._start_PD_timer()
        else:
            self.PDtimer.stop()
            self.adcVoltage = -999
            print "PD timer stopped."

#Starts the timer, that only updates the displayed voltage

    def _start_PD_timer(self):
        self.PDtimer = Timer(1000.0, self._adcVoltage_update)

    #Starts a timer, that updates the displayed voltage, as well as does the "in lock" checking
    def _start_PD_lock_timer(self):
        self.PDtimer = Timer(1000.0, self.update_PD_and_Lock)

#Controls if everything is still in lock. Also updates displayed voltage. It counts still in lock, when the measured frequency
#is within DC tolerance of the mean of the last five measured frequencies.

    def update_PD_and_Lock(self):
        self._adcVoltage_update()  #still display Voltage
        pdVoltage = self._adcVoltage_get()
        #print "Updated Frequency"
        #mistakeCounter = 0
        if len(self.DCadcVoltages
               ) < 5:  #number of Measurements that will be compared
            print "Getting Data for Lock. Do not unlock!"
            self.DCadcVoltages = np.append(self.DCadcVoltages, pdVoltage)
        else:
            self.DCadcVoltagesMean = np.mean(
                self.DCadcVoltages)  #Mean Frequency to compare to
            if (abs(pdVoltage - self.DCadcVoltagesMean) < self.DCtolerance):
                self.DCadcVoltages = np.append(self.DCadcVoltages, pdVoltage)
                self.DCadcVoltages = np.delete(
                    self.DCadcVoltages, 0)  #keep Array at constant length
                print "Still locked."
                if self.DCmistakeCounter > 0:
                    self.DCmistakeCounter = 0
            else:
                self.DCmistakeCounter += 1
                if self.DCmistakeCounter > 5:
                    self.PDtimer.stop()
                    self._start_PD_timer()  #keep Frequency on display..
                    self._DCscan_and_lock_fired()
                    self._DCautolock_fired()
                else:
                    print self.DCmistakeCounter

    #This button is used, when everything is already locked. It prepares the voltage mean array, stops the PD timer and starts the update_PD_and_Lock timer routine
    def _DCautolock_fired(self):
        if self.DCconnect == True:
            self.DCadcVoltages = np.array([])
            self.PDtimer.stop()
            self._start_PD_lock_timer()
        else:
            print "No adcRPi connected."

#This function (button) scans the voltage and reads the RPiADC voltage of the selected channel. It subsequently attempts
#to lock the Cavity at the voltage, where the RPiADC voltage is highest. The Algorithm for the lock is as follows:
#1) It does a coarse DAC voltage scan with the parameters selected in the input (minBoundary etc). This scan is terminated when
#	the adc voltage goes above a threshold (3.2 V - hardcoded, maybe add input), or after scanning the whole range.
#2) A fine scan 0.3V(dac) around the maximum (or around the 3.2V dac voltage) is done. Currently the number of steps is hardcoded to 600,
#	which worked well. This scan terminated either by going to a 3.3V (adc) threshhold or after finishing.
#3) The Cavity is locked at a dac voltage that corresponds to either the maximum after the whole scan or the 3.3V threshold.

    def _DCscan_and_lock_fired(self):
        self.pinSet(self.port, '1')  #Unlock
        time.sleep(
            1
        )  #If the cavity was in lock before we can not directly start scanning, or else it will read 3.3V as first value
        voltages = np.linspace(
            self.DCminBoundary, self.DCmaxBoundary,
            self.DCnoOfSteps)  #Parameters for first scan set by GUI
        diodeVoltages = np.array([])
        for entry in voltages:  #First scan
            self.setVoltage = entry
            self._set_fired()  #update setVoltage
            diodeVoltages = np.append(diodeVoltages, self._adcVoltage_get())
            volt = self._adcVoltage_get()
            print volt
            if volt >= 3.2:  #Threshold for first scan
                break
            time.sleep(
                0.05
            )  #sleep time between every step for adc readout, maybe it is possible to make the scan faster
        self.setVoltage = voltages[diodeVoltages.argmax(
            axis=0)]  #DAC Voltage corresponding to the highest adc voltage
        print "Attempting to reduce scan Range"
        print self.setVoltage
        time.sleep(2.0)
        if self.setVoltage < 0.3:  #make sure we do not scan below zero, as the lock box does not like negative voltages.
            auxSetVolt = 0
        else:
            auxSetVolt = self.setVoltage - 0.3
        voltages = np.linspace(auxSetVolt, self.setVoltage + 0.3,
                               600)  #parameters for second scan
        diodeVoltages = np.array([])
        for entry in voltages:  #Second scan
            if entry > self.powerMode:  #Our voltage can not go above our maximum value
                break
            self.setVoltage = entry
            self._set_fired()
            diodeVoltages = np.append(diodeVoltages, self._adcVoltage_get())
            volt = self._adcVoltage_get()
            print volt
            if volt >= 3.3:  #threshold for second scan
                print self.setVoltage
                self.pinSet(self.port, '0')
                return
            time.sleep(0.1)
        self.setVoltage = voltages[diodeVoltages.argmax(axis=0)]
        print "DAC Voltage set to %f" % voltages[diodeVoltages.argmax(axis=0)]
        print ".. this corresponds to a diode Voltage of %f" % diodeVoltages[
            diodeVoltages.argmax(axis=0)]
        self._set_fired()
        time.sleep(0.2)
        self.pinSet(self.port, '0')  #Lock
        print "Voltage set to %f to attempt relock." % self.setVoltage
        return

    #Changes from lock to dither and other way around
    def _switch_Lock_fired(self):
        if self.pinMode == '0':
            self.pinMode = '1'
            self.pinSet(self.port, self.pinMode)
        else:
            self.pinMode = '0'
            self.pinSet(self.port, self.pinMode)

#Gui Stuff#

    DCgroup = traitsui.VGroup(
        traitsui.HGroup(
            traitsui.VGroup(
                traitsui.Item('adcChannel'),
                traitsui.Item('adcVoltage', style='readonly'),
            ),
            traitsui.VGroup(
                traitsui.Item('DCmaxBoundary'),
                traitsui.Item('DCminBoundary'),
                traitsui.Item('DCnoOfSteps'),
            ),
        ),
        #traitsui.Item('switch_Lock'),
        traitsui.HGroup(
            traitsui.VGroup(
                traitsui.Item('DCscan_and_lock'),
                traitsui.Item('DCautolock'),
            ), traitsui.Item('DCconnect')),
        visible_when='relockMode == "Doubling cavity Relock"',
        show_border=True,
    )

    #---- WAVEMETER GUI ----#
    wavemeter = traits.Enum("Humphry", "Ion Cavity")
    wmChannel = traits.Enum(1, 2, 3, 4, 5, 6, 7, 8)
    wmFrequency = traits.Float()
    wmConnected = traits.Bool()
    wmHWI = None
    wmIP = None
    wmPort = None
    wmReLock = traits.Button()
    wmFrequencyLog = np.array([])
    wmMeanFrequency = None
    wmTolerance = 0.0000006  #Frequency tolerance in THz, set to 60 MHz in accordance with wm accuracy
    mistakeCounter = 0
    #wmPolarity = traits.Enum("+", "-")
    wmEmptyMemory = traits.Button()

    #When hitting wmConnected, a connection to the wavemeter and readout is established
    def _wmConnected_changed(self):
        if self.wmConnected == True:
            if self.wavemeter == "Humphry":
                self.wmIP = '192.168.0.111'
                self.wmPort = 6101
            elif self.wavemeter == "Ion Cavity":
                self.wmIP = '192.168.32.2'
                self.wmPort = 6100

            self.wmHWI = PyHWI.DECADSClientConnection('WLM', self.wmIP,
                                                      self.wmPort)
            #frequencyArray = wmHWI.getFrequency(True, self.wmChannel)
            self.start_timer()
        else:
            self.wmFrequency = -999
            self.timer.Stop()  #stops either lock_timer or read_timer.
            print "Timer stopped"

    #GUI stuff
    wmGroup = traitsui.VGroup(traitsui.Item('wmFrequency', style='readonly'),
                              traitsui.Item('wmConnected'),
                              traitsui.Item('wmReLock'),
                              traitsui.HGroup(
                                  traitsui.Item('wavemeter'),
                                  traitsui.Item('wmChannel'),
                                  traitsui.Item('wmEmptyMemory',
                                                show_label=False)),
                              visible_when='relockMode == "Wavemeter Relock"')

    #Resets the memory of the lock (the array which saves the last read frequencies)
    def _wmEmptyMemory_fired(self):
        self.wmFrequencyLog = np.array([])
        print "Memory empty."

#starts readout-only timer

    def start_timer(self):
        print "Timer started"
        self.timer = Timer(1000.0, self.update_wm)

#starts readout-and-lock timer, analogue to the doubling cavity case

    def start_lock_timer(self):
        print "Lock timer started"
        self.timer = Timer(1000.0, self.update_wm_and_lock)

#updates the displayed frequency

    def update_wm(self):
        frequencyArray = self.wmHWI.getFrequency(True, self.wmChannel)
        self.wmFrequency = frequencyArray[1]

#updates frequency and checks if its still near the mean of the last five (hardcoded, see below) measured frequencies. If not, it attempts a relock.

    def update_wm_and_lock(self):
        self.update_wm()
        frequencyArray = self.wmHWI.getFrequency(True, self.wmChannel)
        #print "Updated Frequency"
        #mistakeCounter = 0
        if len(self.wmFrequencyLog
               ) < 5:  #number of Measurements that will be compared
            print "Getting Data for Lock. Do not unlock!"
            self.wmFrequencyLog = np.append(self.wmFrequencyLog,
                                            frequencyArray[1])
        else:
            self.wmMeanFrequency = np.mean(
                self.wmFrequencyLog)  #Mean Frequency to compare to
            if (abs(frequencyArray[1] - self.wmMeanFrequency) <
                    self.wmTolerance):
                self.wmFrequencyLog = np.append(self.wmFrequencyLog,
                                                frequencyArray[1])
                self.wmFrequencyLog = np.delete(
                    self.wmFrequencyLog, 0)  #keep Array at constant length
                print "Still locked."
                if self.mistakeCounter > 0:
                    self.mistakeCounter = 0
            else:
                self.mistakeCounter += 1
                if self.mistakeCounter > 5:  #number of measurements that still count as locked, though the frequency is not within boundaries
                    self.timer.stop()
                    self.start_timer()  #keep Frequency on display..
                    self.wmRelock(self.wmMeanFrequency)
                else:
                    print self.mistakeCounter

    #Relock procedure.
#For now this scans only one time, with a hardcoded number of steps. It might be worth modifying this to look like the doubling cavity relock procedure.

    def wmRelock(self, wantedFrequency):
        self.pinSet(self.port, '1')
        voltages = np.linspace(self.powerModeMult * self.powerMode,
                               self.powerMode, 10)
        wmRelockTry = 0
        try:
            while (wmRelockTry < 5):  #attempt relock five times
                for entry in voltages:
                    self.setVoltage = entry
                    self._set_fired()
                    time.sleep(1.0)
                    frequencyArray = self.wmHWI.getFrequency(
                        True, self.wmChannel)
                    if (abs(frequencyArray[1] - wantedFrequency) <
                            self.wmTolerance):
                        print "Relock_attempt!"
                        self.pinSet(self.port, '0')
                        self._wmLock_fired()
                        raise GetOutOfLoop  #Opens the function again (inductively). Maybe fix that by going back
                        #to the level above somehow.
                wmRelockTry += 1
                print "Relock try %f not succesful" % wmRelockTry
            print "Was not able to Relock."
        except GetOutOfLoop:
            print "gotOutOfLoop"
            pass

    def _wmReLock_fired(self):
        #self.wmFrequencyLog = np.array([])
        self.mistakeCounter = 0
        if self.wmConnected == True:
            self.timer.Stop(
            )  #Switch from read-only timer to read-and-log timer. If both run at the same time
            self.start_lock_timer()  #we run into timing problems.
        else:
            print "No Wavemeter connected!"

        print "hi"

#---- WAVEMETERLOCK - DE 17092018 GUI ----#

    wavemeter = traits.Enum("Humphry", "Ion Cavity")
    wmChannel = traits.Enum(1, 2, 3, 4, 5, 6, 7, 8)
    wmFrequency = traits.Float()
    wmConnected = traits.Bool()
    wmHWI = None
    wmIP = None
    wmPort = None
    isRunning = traits.Bool(False)
    wmVoltage = 0
    wmLockTimer = traits.Instance(Timer)
    wmLockStart = traits.Button()
    wmLockStop = traits.Button()
    wmFrequencyLog = np.array([])
    wmMeanFrequency = None
    wmTargetFrequency = traits.Float()  #frequency to lock
    wmGain = traits.Float()  #Gain for wavemeterlock
    wmTolerance = 0.0000006  #Frequency tolerance in THz, set to 60 MHz in accordance with wm accuracy
    mistakeCounter = 0
    #wmPolarity = traits.Enum("+", "-")
    wmEmptyMemory = traits.Button()

    #GUI stuff
    wmlockGroup = traitsui.VGroup(
        traitsui.HGroup(traitsui.Item('wavemeter'), traitsui.Item('wmChannel'),
                        traitsui.Item('wmEmptyMemory', show_label=False)),
        traitsui.Item('wmFrequency', style='readonly'),
        traitsui.Item('wmConnected'),
        traitsui.Item('wmTargetFrequency'),
        traitsui.Item('wmGain'),
        traitsui.HGroup(
            traitsui.Item('wmLockStart', visible_when="isRunning == False"),
            traitsui.Item('wmLockStop', visible_when="isRunning == True")),
        visible_when='relockMode == "Wavemeter Lock"')

    def _wmLockStart_fired(self):
        print "Start: Wavemeterlock"
        self.isRunning = True
        self.wmLockTimer.Start()

    def wmLock2(self):
        # Calculate error in MHz
        error = (self.wmFrequency - self.wmTargetFrequency) * 10**3
        if abs(error) < 5000:
            self.wmVoltage = self.wmVoltage + error * self.wmGain
            if self.wmVoltage > 10:
                self.wmVoltage = 10
            elif self.wmVoltage < -10:
                self.wmVoltage = -10

        cmd = "cmd=" + self.x.generate_voltage(
            self.port, self.powerMode, self.powerModeMult * self.powerMode,
            self.wmVoltage)
        self.connection.sendwithoutcomment(cmd)
        self.bytecode = self.x.generate_voltage(
            self.port, self.powerMode, self.powerModeMult * self.powerMode,
            self.wmVoltage)

        #self.saveToFile( "frequency_data.csv" )

    def _wmLockStop_fired(self):
        print "Stop: Wavemeterlock"
        self.isRunning = False

        cmd = "cmd=" + self.x.generate_voltage(
            self.port, self.powerMode, self.powerModeMult * self.powerMode,
            0)  #Spannung wieder auf 0
        self.connection.sendwithoutcomment(cmd)
        self.bytecode = self.x.generate_voltage(
            self.port, self.powerMode, self.powerModeMult * self.powerMode, 0)
        self.wmVoltage = 0

        self.wmLockTimer.Stop()

    def saveToFile(self, fileName):

        f = open(fileName, "a")

        f.write("%f \n" % self.wmFrequency)

        f.close()


#---- PUT TOGETHER CHANNEL ----#

    selectionGroup = traitsui.HGroup(traitsui.Item('relockMode'),
                                     #traitsui.Item('set_Relock'),
                                     )

    channelGroup = traitsui.VGroup(
        selectionGroup,
        powerGroup,
        wmGroup,
        wmlockGroup,
        DCgroup,
        manualGroup,
    )

    traits_view = traitsui.View(channelGroup)
Beispiel #11
0
class DSCUSBUI(DSCUSB, HasTraits):
    """An enhanced version of :class:`~.dscusb.DSCUSB`. It defines a traits
    based user interface. For function definition see :class:`~.dscusb.DSCUSB`
    
    .. note:: 
        
        HasTraits define set and get functions, so these are
        overridden by DSCUSB's set and get functions. 
        
    Examples
    --------
    
    For simple DSCUSB redout display do:
        
    >>> dsc = DSCUSBUI()
    >>> dsc.configure_traits() # doctest: +SKIP
    
    %s
    """
    #: DSCUSB settable parameters are defined in this class
    settings = Instance(DSCUSBSettings, ())
    #: station number
    STN = Range(0, 999, 1, desc='station number')
    #: serialUI instance
    serial = Instance(SerialUI, (), transient=True)
    _initialized = Bool(False, desc='device status')

    #: a string representing serial number of the device
    serial_number = Str(' Unknown ', desc='device serial number')
    #: a stringe representing firmware version of the device
    firmware_version = Str(' -.--  ', desc='firmware version')
    #: here the measured data is written for display
    output = Str(desc='measured output')
    #: here the measured temperature is written for display
    temp = Str('', desc='measured temperature')
    #: defines output mode SYS: Force [mN], MVV: Strain [mV/V], CELL: Cell output
    output_mode = Trait('Force [mN]', {
        'Force [mN]': 'SYS',
        'Strain [mV/V]': 'MVV',
        'Cell output': 'CELL'
    },
                        desc='different output modes. ')
    #: STAT flag is written here, for status messages
    stat = Int(desc='STAT flags.')
    #: status messages are written here
    status_message = Str
    #: a bool that determines if there is a problem with output data
    output_alarm = Bool(False)
    #: a bool that determines if there is a problem with temperature
    temp_alarm = Bool(False)

    timer = Any
    #: interval for data readout
    interval = Range(low=READOUT_INTERVAL_MIN,
                     high=READOUT_INTERVAL_MAX,
                     value=READOUT_INTERVAL,
                     desc='data acquisition interval')

    timer_fast = Any

    readout = List([])

    offset = Float(desc='force measurement offset')
    port_button = Button('Port', desc='port settings')
    settings_button = Button('Settings', desc='settings')
    init_button = Button('On/Off', desc='initialize action')
    set_offset_button = Button('Set', desc='set offset action')

    #    zero_button = Button('Set zero', desc = 'set force to zero')

    view = dscusbui_view

    def _serial_default(self):
        return DSCSerial(timeout=0.06, baudrate=BAUDRATE_DEFAULT)

    def __initialized_changed(self, value):
        if value == True:
            self.serial_number, self.firmware_version = self.get_info()
            self._stat_changed(self.stat)  #update message
            self.start_readout(self.interval)
        else:
            self.stop_readout()
            self._reset_output_data()
            self.status_message = ''

    def _interval_changed(self, value):
        if self._initialized:
            self.stop_readout()
            self.start_readout(value)

    def _timer_function(self):
        try:
            self.stat = self.get_stat()
            if self.readout != []:
                value, self.readout = np.median(self.readout), []
                self.output = '%.4f' % value
                temp = self.get_temp()
                self.temp = '%.2f' % temp
                return value, temp
        except:
            self.close()
            raise

    def _timer_fast_function(self):
        try:
            value = self.get(self.output_mode_)
            if self.output_mode_ == 'SYS':
                value -= self.offset
            self.readout.append(value)
        except:
            self.close()
            raise

    def start_readout(self, interval=1.):
        """Starts readout process. GUI must be running for this to work...
        """
        try:
            self.timer.Start(interval * 1000)
            self.timer_fast.Start(self._interval_min * 1000)
        except AttributeError:
            #self._timer_function()
            self.timer = Timer(interval * 1000, self._timer_function)
            self.timer_fast = Timer(self._interval_min * 1000,
                                    self._timer_fast_function)

    def stop_readout(self):
        """Stops readout process
        """
        self.timer.Stop()
        self.timer_fast.Stop()

    def _reset_output_data(self):
        self.status_message = ''
        self.output = ''
        self.temp = ''
        self.serial_number, self.firmware_version = ('Unknown', '-.--')

    def _stat_changed(self, stat):
        stat = stat_to_dict(stat)
        self.output_alarm = stat['ECOMUR'] or stat['ECOMOR'] or \
                            stat['SYSUR'] or stat['SYSOR'] or \
                            stat['CRAWUR'] or stat['CRAWOR'] or \
                            stat['SCALON'] or stat['LCINTEG']
        self.temp_alarm = stat['TEMPUR'] or stat['TEMPOR']
        self.status_message = get_status_message(stat)

    @display_on_exception(logger, 'Could not initialize')
    def _init_button_fired(self):
        if self._initialized:
            self.close()
        else:
            self.init()

    @display_on_exception(logger, 'Could not change settings')
    def _settings_button_fired(self):
        self.stop_readout()
        d = self.settings.get()
        d.pop('_poweruser')
        for key in d:
            value = self.get(key)
            setattr(self.settings, key, value)
            d[key] = getattr(self.settings, key)
        self.settings.edit_traits(kind='modal')
        reset = False
        STN = self.STN
        for key in d:
            value = getattr(self.settings, key)
            if d[key] != value:
                if key == 'BAUD':
                    self.set_baudrate(BAUDRATES[baudrate])
                else:
                    self.set(key, value)
                if key in RESET_SETTINGS:
                    reset = True
                if key == 'STN':
                    if STN != value:
                        STN = value

        if reset == True:
            self._initialized = False
            self.reset()
            self.STN = STN
        self.start_readout(self.interval)

    @display_on_exception(logger, 'Could not change port settings')
    def _port_button_fired(self):
        self.close()
        self.serial.edit_traits(kind='livemodal')

    @display_on_exception(logger, 'Could not change offset')
    def _set_offset_button_fired(self):
        self.calibrate()
Beispiel #12
0
class App(HasTraits):

    audio_stream = Instance(AudioStream, ())
    timer = Instance(Timer)

    decoder = Instance(SpeechDecoder)
    data_queue = Instance(Collapsible, ())
    results_queue = Instance(queue.Queue, ())
    decoder_thread = Instance(threading.Thread)

    spectrum_data = Instance(ArrayPlotData)
    time_data = Instance(ArrayPlotData)
    spectrogram_plotdata = Instance(ArrayPlotData)

    text = Str

    plot = Instance(Component)

    traits_view = View(
        VGroup(
            Group(Item('plot',
                       editor=ComponentEditor(size=(900, 500)),
                       show_label=False),
                  orientation="vertical"),
            UItem('text', style='custom'),
        ),
        resizable=True,
        title="Audio Spectrum",
        width=900,
        height=500,
    )

    def start(self):
        self.audio_stream.start()
        self.decoder_thread.start()

        self.timer = Timer(20, self.update)

    def stop(self):
        self.timer.Stop()

        self.data_queue.put(DONE)
        self.decoder_thread.join()

        self.audio_stream.stop()

    def update(self):
        audio_data = self.audio_stream.get_audio_data()

        normalized_time = audio_data / 32768.0
        spectrum = np.abs(scipy.fft(normalized_time))[:NUM_SAMPLES // 2]

        # Update plot data
        self.spectrum_data.set_data('amplitude', spectrum)
        self.time_data.set_data('amplitude', normalized_time)
        spectrogram_data = self.spectrogram_plotdata.get_data('imagedata')
        spectrogram_data = np.hstack(
            (spectrogram_data[:, 1:], np.transpose([spectrum])))
        self.spectrogram_plotdata.set_data('imagedata', spectrogram_data)

        # Update decoder
        self.data_queue.put(audio_data)
        try:
            self.text += self.results_queue.get_nowait()
        except queue.Empty:
            pass

    def _decoder_default(self):
        return SpeechDecoder.from_paths(
            model="deepspeech-0.5.1-models/output_graph.pbmm",
            alphabet="deepspeech-0.5.1-models/alphabet.txt",
            lm="deepspeech-0.5.1-models/lm.binary",
            trie="deepspeech-0.5.1-models/trie")

    def _decoder_thread_default(self):
        return threading.Thread(target=self._transcribe)

    def _transcribe(self):
        self.decoder.start()
        done = False
        while not done:
            for _ in range(8):
                data = self.data_queue.get()
                if data is DONE:
                    done = True
                    break
                self.decoder.update_content(data)
            result = self.decoder.decode()
            self.results_queue.put(result)
        self.decoder.stop()

    def _spectrum_data_default(self):
        frequencies = np.linspace(0.0, SAMPLING_RATE / 2, num=NUM_SAMPLES // 2)
        spectrum_data = ArrayPlotData(frequency=frequencies)
        empty_amplitude = np.zeros(NUM_SAMPLES // 2)
        spectrum_data.set_data('amplitude', empty_amplitude)
        return spectrum_data

    def _time_data_default(self):
        ts = np.linspace(0.0, NUM_SAMPLES / SAMPLING_RATE, num=NUM_SAMPLES)
        time_data = ArrayPlotData(time=ts)
        empty_amplitude = np.zeros(NUM_SAMPLES)
        time_data.set_data('amplitude', empty_amplitude)
        return time_data

    def _spectrogram_plotdata_default(self):
        spectrogram_data = np.zeros((NUM_SAMPLES // 2, SPECTROGRAM_LENGTH))
        spectrogram_plotdata = ArrayPlotData()
        spectrogram_plotdata.set_data('imagedata', spectrogram_data)
        return spectrogram_plotdata

    def _plot_default(self):
        # Set up the spectrum plot
        spectrum_plot = Plot(self.spectrum_data)
        spectrum_plot.plot(("frequency", "amplitude"),
                           name="Spectrum",
                           color="red")
        spectrum_plot.padding = 50
        spectrum_plot.title = "Spectrum"
        spec_range = list(
            spectrum_plot.plots.values())[0][0].value_mapper.range  # noqa
        spec_range.low = 0.0
        spec_range.high = 5.0
        spectrum_plot.index_axis.title = 'Frequency (Hz)'
        spectrum_plot.value_axis.title = 'Amplitude'

        # Time series plot
        time_plot = Plot(self.time_data)
        time_plot.plot(("time", "amplitude"), name="Time", color="blue")
        time_plot.padding = 50
        time_plot.title = "Time"
        time_plot.index_axis.title = 'Time (seconds)'
        time_plot.value_axis.title = 'Amplitude'
        time_range = list(time_plot.plots.values())[0][0].value_mapper.range
        time_range.low = -0.2
        time_range.high = 0.2

        # Spectrogram plot
        spectrogram_plot = Plot(self.spectrogram_plotdata)
        max_time = SPECTROGRAM_LENGTH * NUM_SAMPLES / SAMPLING_RATE
        max_freq = SAMPLING_RATE / 2
        spectrogram_plot.img_plot(
            'imagedata',
            name='Spectrogram',
            xbounds=(0, max_time),
            ybounds=(0, max_freq),
            colormap=hot,
        )
        range_obj = spectrogram_plot.plots['Spectrogram'][0].value_mapper.range
        range_obj.high = 5
        range_obj.low = 0.0
        spectrogram_plot.title = 'Spectrogram'

        container = HPlotContainer()
        container.add(spectrum_plot)
        container.add(time_plot)
        container.add(spectrogram_plot)

        return container