class BrownianMotionVisualization(HasTraits):

    scene = Instance(MlabSceneModel, ())
    plot = Instance(PipelineBase)
    x = Array()
    y = Array()
    z = Array()

    timer = Any()
    timer_button = Button()

    @on_trait_change('scene.activated')
    def create_plot(self):
        """ Wait before the scene is activated before trying to add data,
        modules, filers into it.
        """
        x, y, z = compute_positions()
        self.plot = self.scene.mlab.points3d(x, y, z)

    def _timer_button_fired(self):
        if self.timer is None:
            self.timer = Timer(100, self.update_plot)
        else:
            self.timer.stop()
            self.timer = None

    def update_plot(self):
        """ Recompute the ball positions and redraw
        """
        x, y, z = compute_positions()
        self.plot.mlab_source.set(x=x, y=y, z=z)


    # the layout of the dialog created
    view = View(Item('scene', editor=SceneEditor(scene_class=MayaviScene),
                    height=250, width=300, show_label=False),
                Item('timer_button', label = "Start/Stop animation",
                      show_label = False),
                )
class PhysiologyController(Controller):

    buffer_                 = Any
    iface_physiology        = Any
    buffer_raw              = Any
    buffer_proc             = Any
    buffer_ts               = Any
    buffer_ttl              = Any
    physiology_ttl_pipeline = Any
    buffer_spikes           = List(Any)
    state                   = Enum('master', 'client')
    process                 = Instance('tdt.DSPProject')
    timer                   = Instance(Timer)
    parent                  = Any

    shell_variables         = Dict

    # These define what variables will be available in the Python shell.  Right
    # now we can't add various stuff such as the data and interface classes
    # because they have not been created yet.  I'm not sure how we can update
    # the Python shell with new instances once the experiment has started
    # running.
    def _shell_variables_default(self):
        return dict(controller=self, c=self)

    def init(self, info):
        self.setup_physiology()
        self.model = info.object
        if self.state == 'master':
            self.process.start()
            self.iface_physiology.trigger('A', 'high')
            self.start()

    def close(self, info, is_ok, from_parent=False):
        return True
        if self.state == 'client':
            return from_parent
        else:
            # The function confirm returns an integer that represents the
            # response that the user requested.  YES is a constant (also
            # imported from the same module as confirm) corresponding to the
            # return value of confirm when the user presses the "yes" button on
            # the dialog.  If any other button (e.g. "no", "abort", etc.) is
            # pressed, the return value will be something other than YES and we
            # will assume that the user has requested not to quit the
            # experiment.
            if confirm(info.ui.control, 'OK to stop?') == YES:
                self.stop(info)
                return True
            else:
                return False

    def setup_physiology(self):
        # Load the circuit
        circuit = join(get_config('RCX_ROOT'), 'physiology')
        self.iface_physiology = self.process.load_circuit(circuit, 'RZ5')

        # Initialize the buffers that will be spooling the data
        self.buffer_raw = self.iface_physiology.get_buffer('craw',
                'r', src_type='float32', dest_type='float32', channels=CHANNELS,
                block_size=1048)
        self.buffer_filt = self.iface_physiology.get_buffer('cfilt',
                'r', src_type='int16', dest_type='float32', channels=CHANNELS,
                block_size=1048)
        self.buffer_ts = self.iface_physiology.get_buffer('trig/',
                'r', src_type='int32', dest_type='int32', block_size=1)
        self.buffer_ts_start = self.iface_physiology.get_buffer('trig/', 
                'r', src_type='int32', dest_type='int32', block_size=1)
        self.buffer_ts_end = self.iface_physiology.get_buffer('trig\\', 
                'r', src_type='int32', dest_type='int32', block_size=1)
        self.buffer_ttl = self.iface_physiology.get_buffer('TTL',
                'r', src_type='int8', dest_type='int8', block_size=1)

        for i in range(CHANNELS):
            name = 'spike{}'.format(i+1)
            buffer = self.iface_physiology.get_buffer(name, 'r',
                    block_size=SPIKE_SNIPPET_SIZE+2)
            self.buffer_spikes.append(buffer)

    @on_trait_change('model.data')
    def update_data(self):
        # Ensure that the data store has the correct sampling frequency
        for i in range(CHANNELS):
            data_spikes = self.model.data.spikes
            for src, dest in zip(self.buffer_spikes, data_spikes):
                dest.fs = src.fs
                dest.snippet_size = SPIKE_SNIPPET_SIZE
        self.model.data.raw.fs = self.buffer_raw.fs
        self.model.data.processed.fs = self.buffer_filt.fs
        self.model.data.ts.fs = self.iface_physiology.fs
        self.model.data.epoch.fs = self.iface_physiology.fs
        self.model.data.sweep.fs = self.buffer_ttl.fs

        # Setup the pipeline
        targets = [self.model.data.sweep]
        self.physiology_ttl_pipeline = deinterleave_bits(targets)

    def start(self):
        self.timer = Timer(100, self.monitor_physiology)

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

    def monitor_physiology(self):
        # Acquire raw physiology data
        waveform = self.buffer_raw.read()
        self.model.data.raw.send(waveform)

        # Acquire filtered physiology data
        waveform = self.buffer_filt.read()
        self.model.data.processed.send(waveform)

        # Acquire sweep data
        ttl = self.buffer_ttl.read()
        self.physiology_ttl_pipeline.send(ttl)

        # Get the timestamps
        ts = self.buffer_ts.read().ravel()
        self.model.data.ts.send(ts)

        ends = self.buffer_ts_end.read().ravel()
        starts = self.buffer_ts_start.read(len(ends)).ravel()
        self.model.data.epoch.send(zip(starts, ends))

        # Get the spikes.  Each channel has a separate buffer for the spikes
        # detected online.  Need to add 2 to the snippet size to compensate for
        # the extra samples provided with the snippet (the timestamp and the
        # classifier).
        snippet_shape = (-1, SPIKE_SNIPPET_SIZE+2)
        for i in range(CHANNELS):
            data = self.buffer_spikes[i].read().reshape(snippet_shape)

            # First sample of each snippet is the timestamp (as a 32 bit
            # integer) and last sample is the classifier (also as a 32 bit
            # integer).  The bits in between should be interpreted as 32-bit
            # floating point values.
            snip = data[:,1:-1]
            ts = data[:,0].view('int32')
            cl = data[:,-1].view('int32')
            self.model.data.spikes[i].send(snip, ts, cl)

    @on_trait_change('model.settings.spike_signs')
    def set_spike_signs(self, value):
        for ch, sign in enumerate(value):
            name = 's_spike{}'.format(ch+1)
            self.iface_physiology.set_tag(name, sign)

    @on_trait_change('model.settings.spike_thresholds')
    def set_spike_thresholds(self, value):
        for ch, threshold in enumerate(value):
            name = 'a_spike{}'.format(ch+1)
            self.iface_physiology.set_tag(name, threshold)
            
    @on_trait_change('model.settings.monitor_fc_highpass')
    def set_monitor_fc_highpass(self, value):
        self.iface_physiology.set_tag('FiltHP', value)

    @on_trait_change('model.settings.monitor_fc_lowpass')
    def set_monitor_fc_lowpass(self, value):
        self.iface_physiology.set_tag('FiltLP', value)

    @on_trait_change('model.settings.monitor_ch_1')
    def set_monitor_ch_1(self, value):
        self.iface_physiology.set_tag('ch1_out', value)

    @on_trait_change('model.settings.monitor_ch_2')
    def set_monitor_ch_2(self, value):
        self.iface_physiology.set_tag('ch2_out', value)

    @on_trait_change('model.settings.monitor_ch_3')
    def set_monitor_ch_3(self, value):
        self.iface_physiology.set_tag('ch3_out', value)

    @on_trait_change('model.settings.monitor_gain_1')
    def set_monitor_gain_1(self, value):
        self.iface_physiology.set_tag('ch1_out_sf', value*1e3)

    @on_trait_change('model.settings.monitor_gain_2')
    def set_monitor_gain_2(self, value):
        self.iface_physiology.set_tag('ch2_out_sf', value*1e3)

    @on_trait_change('model.settings.monitor_gain_3')
    def set_monitor_gain_3(self, value):
        self.iface_physiology.set_tag('ch3_out_sf', value*1e3)

    @on_trait_change('model.settings.diff_matrix')
    def set_diff_matrix(self, value):
        self.iface_physiology.set_coefficients('diff_map', value.ravel())

    def load_settings(self, info):
        instance = load_instance(PHYSIOLOGY_ROOT, PHYSIOLOGY_WILDCARD)
        if instance is not None:
            self.model.settings.copy_traits(instance)

    def saveas_settings(self, info):
        dump_instance(self.model.settings, PHYSIOLOGY_ROOT, PHYSIOLOGY_WILDCARD)

    @on_trait_change('model.channel_mode')
    def _channel_mode_changed(self, new):
        if new == 'TDT':
            offset = 0
        elif new =='TBSI':
            offset = 1
        else:
            offset = 2
        self.iface_physiology.set_tag('ch_offset', offset*16+1)
class LogFilePlots(traits.HasTraits):
    """just a several tabbed view of several log file plot objects"""

    lfps = traits.List(
        plotObjects.logFilePlot.LogFilePlot)  #list of possible fits
    selectedLFP = traits.Instance(plotObjects.logFilePlot.LogFilePlot)
    #logFilePlotGroup = traitsui.Group(traitsui.Item("logFilePlotObject", editor = traitsui.InstanceEditor(), style="custom", show_label=False),label="Log File Plotter")
    logFilePlotsGroup = traitsui.Group(traitsui.Item(
        'lfps',
        style="custom",
        editor=traitsui.ListEditor(use_notebook=True,
                                   deletable=True,
                                   selected="selectedLFP",
                                   export='DockWindowShell',
                                   page_name=".logFilePlotsTabName"),
        label="logFilePlots",
        show_label=False),
                                       springy=True)

    autoRefreshTimer = traits.Instance(Timer)
    autoRefreshDialog = traits.Instance(
        plotObjects.autoRefreshDialog.AutoRefreshDialog)

    #for saving and loading
    default_directory = os.path.join('N:', os.sep, 'Data', 'eagleLogs')
    file_wildcard = traits.String("CSV Master Settings (*.csv)|All files|*")

    menubar = traitsmenu.MenuBar(
        traitsmenu.Menu(traitsui.Action(name='Add Log File Plot',
                                        action='_add_lfp'),
                        traitsui.Action(name='Clone selected',
                                        action='_add_with_current_lfp'),
                        traitsui.Action(name='Print current',
                                        action='_print_current'),
                        traitsui.Action(name='Auto refresh',
                                        action='_autoRefresh_dialog'),
                        traitsui.Action(name='Matplotlibify',
                                        action='_matplotlibify_dialog'),
                        traitsui.Action(name='Save as',
                                        action='_save_as_settings'),
                        traitsui.Action(name='Load as',
                                        action='_open_settings'),
                        name="Menu"), )
    statusBarString = traits.String()

    traits_view = traitsui.View(logFilePlotsGroup,
                                title="Log File Plots",
                                statusbar="statusBarString",
                                icon=pyface.image_resource.ImageResource(
                                    os.path.join('icons', 'eagles.ico')),
                                resizable=True,
                                menubar=menubar)

    def __init__(self, N, **traitsDict):
        """N is initial number of logFilePlots """
        super(traits.HasTraits, self).__init__(**traitsDict)
        self.lfps = [
            plotObjects.logFilePlot.LogFilePlot() for c in range(0, N)
        ]
        self.selectedLFP = self.lfps[0]
        for lfp, counter in zip(self.lfps, range(0, N)):
            lfp.logFilePlotsBool = True
            lfp.logFilePlotsTabName = "log file plot " + str(counter)
            lfp.logFilePlotsReference = self

    def _add_lfp(self):
        """called from menu. adds a new logfileplot to list and hence to gui in case you run out """
        new = plotObjects.logFilePlot.LogFilePlot()
        new.logFilePlotsBool = True
        new.logFilePlotsReference = self
        self.lfps.append(new)

    def _add_with_current_lfp(self):
        """called from menu. adds a new logfileplot to list and hence to gui in case you run out """
        cloneTraits = [
            "logFile", "mode", "masterList", "xAxis", "yAxis", "aggregateAxis",
            "series", "xLogScale", "yLogScale", "interpretAsTimeAxis",
            "filterYs", "filterMinYs", "filterMaxYs", "filterXs",
            "filterMinXs", "filterMaxXs", "filterNaN", "logFilePlotsBool"
        ]
        new = self.selectedLFP.clone_traits(traits=cloneTraits, copy="deep")
        new.logFilePlotsReference = self
        new.__init__()
        self.lfps.append(new)

    def _print_current(self):
        """opens matplotlibify that allows user to save to one note or print the image """
        self.selectedLFP._savePlotButton_fired()

    def _autoRefresh_dialog(self):
        """when user clicks autorefresh in the menu this function calls the dialog 
        after user hits ok, it makes the choices of how to setup or stop the timer"""
        logger.info("auto refresh dialog called")
        if self.autoRefreshDialog is None:
            self.autoRefreshDialog = plotObjects.autoRefreshDialog.AutoRefreshDialog(
            )
        self.autoRefreshDialog.configure_traits()
        logger.info("dialog edit traits finished")
        if self.autoRefreshDialog.autoRefreshBool:
            if self.autoRefreshTimer is not None:
                self.autoRefreshTimer.stop()
            self.selectedLFP.autoRefreshObject = self.autoRefreshDialog  #this gives it all the info it needs
            self.autoRefreshTimer = Timer(
                self.autoRefreshDialog.minutes * 60.0 * 1000.0,
                self.selectedLFP.autoRefresh)
            logger.info("started auto refresh timer to autorefresh")
        else:
            self.selectedLFP.autoRefreshObject = None
            logger.info("stopping auto refresh")
            if self.autoRefreshTimer is not None:
                self.autoRefreshTimer.stop()

    def _matplotlibify_dialog(self):
        import matplotlibify.matplotlibify
        dialog = matplotlibify.matplotlibify.Matplotlibify(
            logFilePlotReference=self.selectedLFP, logFilePlotsReference=self)
        dialog.configure_traits()

    def _save_settings(self, settingsFile):
        logger.debug("_save_settings call saving settings")
        with open(settingsFile, 'wb') as sfile:
            writer = csv.writer(sfile)
            writer.writerow([-1, 'N', len(self.lfps)])
            c = 0
            lfp_traits = [
                'mode', 'logFilePlotBool', 'fitLogFileBool',
                'autoFitWithRefresh', 'softRefresh', 'errorBarMode', 'logFile',
                'xAxis', 'yAxis', 'aggregateAxis', 'masterList', 'series',
                'xLogScale', 'yLogScale', 'interpretAsTimeAxis', 'filterYs',
                'filterMinYs', 'filterMaxYs', 'filterXs', 'filterMinXs',
                'filterMaxXs', 'filterNaN', 'filterSpecific',
                'filterSpecificString', 'filterVariableSelector',
                'logFilePlotsBool', 'logFilePlotsTabName'
            ]

            for lfp in self.lfps:
                for trait in lfp_traits:
                    try:
                        writer.writerow([
                            c, trait,
                            getattr(lfp, trait),
                            type(getattr(lfp, trait))
                        ])
                    except Exception as e:
                        logger.error(e.message)
                c += 1

    def _load_settings(self, settingsFile):
        load_df = pandas.read_csv(
            settingsFile,
            na_filter=False,
            header=None,
            names=['lfp_idx', 'variable', 'value', 'type'])
        global_settings_df = load_df[load_df['lfp_idx'] == -1]
        N = int(
            global_settings_df[global_settings_df['variable'] == 'N'].value[0])
        self.__init__(N)
        for idx in range(0, N):
            df = load_df[load_df['lfp_idx'] == idx]
            traits = list(df.variable.values)
            values = list(df.value.values)
            types = list(df.type.values)
            traitsDict = {
                trait: (value, type_val)
                for (trait, value, type_val) in zip(traits, values, types)
            }
            logger.info("loading lfp %G" % idx)
            logger.info("dictionary %s" % traitsDict)
            self.lfps[idx].load_settings(traitsDict)

    def _save_as_settings(self):
        """Called when user presses save as in Menu bar """
        dialog = FileDialog(action="save as",
                            default_directory=self.default_directory,
                            wildcard=self.file_wildcard)
        dialog.open()
        if dialog.return_code == OK:
            self.settingsFile = dialog.path
            if self.settingsFile[-4:] != ".csv":
                self.settingsFile += ".csv"
            self._save_settings(self.settingsFile)

    def _open_settings(self):
        logger.debug("_open_settings call . Opening a settings file")
        dialog = FileDialog(action="open",
                            default_directory=self.default_directory,
                            wildcard=self.file_wildcard)
        dialog.open()
        if dialog.return_code == OK:
            self.settingsFile = dialog.path
            self._load_settings(self.settingsFile)
class EagleHandler(traitsui.Handler):

    #---------------------------------------------------------------------------
    # State traits
    #---------------------------------------------------------------------------
    model = traits.Instance(CameraImage)
    view = traits.Any
    watchFolderTimer = traits.Instance(Timer)

    #---------------------------------------------------------------------------
    # Handler interface
    #---------------------------------------------------------------------------
    def closed(self, info, is_ok):
        """ Handles a dialog-based user interface being closed by the user.
        Overridden here to stop the timer once the window is destroyed.
        """
        try:
            #stop any previous timer, should only have 1 timer at a time
            logger.info("closing image plot inspector")
        except Exception as e:
            logger.error("couldn't close: error: %s " % e.message)
        return

    def init(self, info):
        self.view = info.object
        self.model = info.object.model
        self.model.on_trait_change(self._model_changed, "model_changed")
        self.view.boxSelection2D.on_trait_change(self._box_selection_complete,
                                                 "selection_complete")
        self.view.lineInspectorX.on_trait_change(self._setCentreGuess,
                                                 "mouseClickEvent")
        self.view.on_trait_change(self._watchFolderModeChanged,
                                  "watchFolderBool")

        #self.start_timer()

    def _model_changed(self):
        if self.view is not None:
            self.view.update(self.model)

    def _box_selection_complete(self):
        logger.critical("Box selection complete")
        [
            self.view.selectedFit.startX, self.view.selectedFit.startY,
            self.view.selectedFit.endX, self.view.selectedFit.endY
        ] = map(int, self.view.boxSelection2D._get_coordinate_box())
        logger.debug("box selection results %s " % [
            self.view.selectedFit.startX, self.view.selectedFit.startY,
            self.view.selectedFit.endX, self.view.selectedFit.endY
        ])

    def _setCentreGuess(self):
        x_ndx, y_ndx = self.view._image_index.metadata["selections"]
        logger.debug("selected fit %s " % self.view.selectedFit)
        #x0=self.model.xs[x_ndx]
        #y0=self.model.ys[y_ndx]
        #print x0,y0
        #for variable in self.view.selectedFit
        self.view.selectedFit.x0.initialValue = self.model.xs[x_ndx]
        self.view.selectedFit.y0.initialValue = self.model.ys[y_ndx]

    def _watchFolderModeChanged(self):
        eagle = self.view
        eagle.oldFiles = []
        if eagle.watchFolderBool:
            if eagle.watchFolder == '' and os.path.isfile(eagle.selectedFile):
                eagle.watchFolder = os.path.dirname(eagle.selectedFile)
            eagle.oldFiles = self.getPNGFiles()
            self.watchFolderTimer = Timer(2000., self.checkForNewFiles)
        else:
            if self.watchFolderTimer is not None:
                self.watchFolderTimer.stop()
                self.watchFolderTimer = None

    def getPNGFiles(self):
        """return set of all pngs in watched folder """
        eagle = self.view
        return set([
            f for f in os.listdir(eagle.watchFolder)
            if os.path.splitext(f)[1] == ".png"
        ])

    def parseFiles(self, filesSet):
        """given a set of files, this file will check how many match the search string
        and return the LIST of all those that do"""
        searchString = self.view.searchString
        return [fileName for fileName in filesSet if searchString in fileName]

    def checkForNewFiles(self):
        """function called by timer thread. Checks directory for new files
        and then checks if they match searchString (calls parseFiles)"""
        eagle = self.view
        if not os.path.isdir(eagle.watchFolder):
            logger.warning(
                "watchFolder is not a valid directory. Will not check for new files"
            )
            return
        currentFiles = self.getPNGFiles()
        newFiles = currentFiles - eagle.oldFiles
        if len(newFiles) == 0:
            #logger.debug("No new files detected in watch Directory")
            return
        else:
            logger.debug("new files = %s" % list(newFiles))
            newFiles = self.parseFiles(newFiles)
            logger.debug("new files after checking for sub-string = %s" %
                         list(newFiles))
            if len(newFiles) == 0:
                logger.info(
                    "new files were found but none matched search string %s" %
                    eagle.searchString)
                return
            if len(newFiles) > 1:
                logger.warning(
                    "more than one new file found %s only analysing first one"
                    % newFiles)
            newFile = newFiles[0]
            logger.debug("new file is %s" % newFile)
            logger.debug("new full path file is %s" %
                         os.path.join(eagle.watchFolder, newFile))
            eagle.selectedFile = os.path.join(eagle.watchFolder, newFile)
            eagle.oldFiles = currentFiles
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)
class TemperatureControlPanel(HasTraits):
    TIMING_UPDATE_INTERVAL = 500.0

    pane_name = Str('Temperature control ')
    pane_id = Str('sensorscience.unimeas.temperatur_control_pane')
    output_channels = Dict({0:'temp_controller'})
    y_units = Dict({0 : 'temp'})
    enable = Bool(False)
    loop_sequence = Bool(True)
    poll_timer = Instance(Timer)
    timing_timer = Instance(Timer)
    table_entries = List(TableEntry)
    current_row = Int(0)
    current_temp = Float
    actual_temp = Float
    process_value = Float
    adjusted_pv = Float
    PID_out = Float
    pwm_value = Int
    max31865_ctrl = Int
    max31865_error = Int
    setpoint = Float
    supply_voltage = Float
    RT_resistance = Float
    set_temp = Float
    current_time = Float
    row_start_time = Float
    start_time = Float
    running = False
    connected = Bool(False)
    save_detailed_log = Bool(False)

    pid_P = Float(0)
    pid_I = Float(0)
    pid_D = Float(0)
    pid_N = Float(0)
    pwm_table_index = Int(0)
    use_direct_pwm = Bool(False)
    pwm_set_direct = Int(0)

    use_direct_pv = Bool(False)
    pv_set_direct = Int(0)


    temp_setpoint = Float
    resistance_setpoint = Float
    supply_voltage_setpoint = Float(15.0)
    RT_resistance_setpoint = Float(109.71)


    temperature_table = Array
    selected_com_port = Str
    selected_pid_configuration = Str
    available_pid_configurations = List(['Normal TO-8', 'M18 ceramic', 'L-A Cap'])
    com_ports_list = List(Str)
    connect = Button
    connect_button_string = Str('Connect')
    update_PID_values = Int(0)
    set_parameters = Button
    update_pid = Button
    save = Button
    load = Button
    filename = File

    set_parameters_queue = Instance(Queue.Queue)
    get_parameters_queue = Instance(Queue.Queue)
    serial_handler = Instance(SerialHandler)

    traits_view = View(
        HGroup(Item('selected_com_port',  label='Com port',
                    editor=EnumEditor(name='com_ports_list'),
                    enabled_when='not running'),
               Item('connect', editor=ButtonEditor(label_value='connect_button_string'),
                    show_label=False,  enabled_when='selected_com_port != ""')),
        Item('enable', label='Enable temp program', enabled_when='connected'),
        Item('loop_sequence', label='Loop temperature sequence'),
        Group(Item('table_entries', show_label=False, editor=table_editor,
                   enabled_when='True'
                   ),
              HGroup(Item('filename'), spring, Item('save', show_label=False),
                     Item('load', show_label=False)),
              show_border=True,
              ),
        Group(
            HGroup(
                VGroup(Item('actual_temp', style='readonly', format_str='%.2f'),
                       Item('process_value', style='readonly', format_str='%.2f'),
                       Item('adjusted_pv', style='readonly', format_str='%.2f'),
                    Item('setpoint', style='readonly', format_str='%.2f')),
                spring,
                VGroup(Item('pwm_value', style='readonly', format_str='%d'),
                       Item('max31865_ctrl', style='readonly', format_str='%#04x'),
                       Item('max31865_error', style='readonly', format_str='%#04x')),
                spring,
                VGroup(Item('PID_out', style='readonly', format_str='%.2f'),
                       Item('supply_voltage', style='readonly', format_str='%.2f'),
                       Item('RT_resistance', style='readonly', format_str='%.2f'))),
            label='Diagnostic parameters', show_border=True,
        ),
       HGroup(
            Group(
                VGroup(Item('temp_setpoint', format_str='%.1f')),
                VGroup(Item('resistance_setpoint', format_str='%.1f')),
                VGroup(Item('supply_voltage_setpoint', format_str='%.1f')),
                VGroup(Item('RT_resistance_setpoint', format_str='%.1f')),
                Item('set_parameters', enabled_when='connected'),
                label='Adjustable parameters', show_border=True,
                ),
            Group(
                Item('selected_pid_configuration', label='PID adaption',
                     editor=EnumEditor(name='available_pid_configurations'),
                     enabled_when='True'),
                VGroup(Item('pid_P', label='PID P', format_str='%.7f')),
                VGroup(Item('pid_I', label='PID I', format_str='%.7f')),
                VGroup(Item('pid_D', label='PID D',  format_str='%.7f')),
                VGroup(Item('pid_N', label='PID Nd',  format_str='%.7f')),
                VGroup(Item('pwm_table_index', label='tbl_idx')),
                HGroup(Item('use_direct_pwm'), Item('pwm_set_direct', show_label=False)),
                HGroup(Item('use_direct_pv'), Item('pv_set_direct', show_label=False)),
                Item('update_pid', enabled_when='connected'),
                Item('save_detailed_log', enabled_when='connected'),
                label='PID parameters - Don\'t touch', show_border=True,
                ),
        ),
        resizable=True, kind='live', handler=TemperatureControlHandler
    )

    def _set_parameters_queue_default(self):
        return Queue.Queue(QUEUE_LENGHT)

    def _get_parameters_queue_default(self):
        return Queue.Queue(QUEUE_LENGHT)

    def _temp_setpoint_changed(self, new):
        self.resistance_setpoint = temp_to_resistance(new, self.RT_resistance_setpoint)

    def _connected_changed(self, new):
        if new is True:
            self.connect_button_string = 'Disconnect'
            self._selected_pid_configuration_changed(self.selected_pid_configuration)
            self.update_PID_values = 3
        else:
            self.connect_button_string = 'Connect'

    def _save_detailed_log_changed(self, new):
        if new:
            self.fh = open('templog_' + strftime("%y%m%d_%H%M%S") + '.csv', 'w')
            self.csv_writer = csv.writer(self.fh,quoting=csv.QUOTE_NONNUMERIC)
            self.csv_writer.writerow(['pv','pid_out','pwm','max31ctrl','max31err','sp', 'sv','RT_ref','pid_p','pid_i','pid_d','pid_n','time'])
            self.log_start_time = time()
        else:
            self.fh.close()

    def _RT_resistance_setpoint_changed(self, new):
        self.resistance_setpoint = temp_to_resistance(self.temp_setpoint, new)

    def _selected_pid_configuration_changed(self, new):
        self.pid_P = PID_configurations[new]['P']
        self.pid_I = PID_configurations[new]['I']
        self.pid_D = PID_configurations[new]['D']
        self.pid_N = PID_configurations[new]['N']
        self.pwm_table_index = PID_configurations[new]['pwm_table_index']

    def _selected_pid_configuration_default(self):
        return 'Normal TO-8'

    def _available_pid_configurations_default(self):
        return PID_configurations.keys()

    def _onTimer(self):
        self.current_time = time() - self.start_time
        index = int(np.floor(self.current_time))
        if index >= len(self.temperature_table):
            return
        if self.temperature_table[index] is not self.current_temp:
            self.current_temp = self.temperature_table[index]
        if self.current_row >= len(self.table_entries):
            return
        time_left = self.table_entries[self.current_row].time + self.row_start_time - self.current_time
        self.table_entries[self.current_row].remaining = int(time_left)
        if self.table_entries[self.current_row].remaining < 1:
            self.current_row += 1
            self.row_changed(time_left)

    def _poll_queue(self):
        while not self.get_parameters_queue.empty():
            try:
                values = self.get_parameters_queue.get_nowait()
            except Queue.Empty:
                logger.error('No temp recieved from SerialHandler - Strange')
                pass
            self.get_parameters_queue.task_done()
            self.actual_temp = resistance_to_temp(values[0], values[7])
            self.process_value = values[0]
            self.adjusted_pv = values[0]*(109.7/values[7])
            self.PID_out = values[1]
            self.pwm_value = values[2]
            self.max31856_ctrl = values[3]
            self.max31856_error = values[4]
            self.setpoint = values[5]
            self.supply_voltage = values[6]
            self.RT_resistance = values[7]
            if self.update_PID_values > 0:
                self.update_PID_values -= 1
                self.pid_P = values[8]
                self.pid_I = values[9]
                self.pid_D = values[10]
                self.pid_N = values[11]
            if self.save_detailed_log:
                self.csv_writer.writerow(values)

    def _table_entries_default(self):
        return [TableEntry(time = 15, start_temp = 150, end_temp = 150, remaining = -1),
                TableEntry(time = 15, start_temp = 350, end_temp = 350, remaining = -1),
                TableEntry(time = 15, start_temp = 250, end_temp = 250, remaining = -1)]
        #        return [TableEntry(time = 700, start_temp = 50, end_temp = 650, remaining = -1),
        #        TableEntry(time = 200, start_temp = 650, end_temp = 650, remaining = -1),
        #        TableEntry(time = 1200, start_temp = 650, end_temp = 200, remaining = -1),
        #        TableEntry(time = 7300, start_temp = 200, end_temp = 200, remaining = -1),
        #        TableEntry(time = 1200, start_temp = 200, end_temp = 150, remaining = -1),
        #        TableEntry(time = 1200, start_temp = 150, end_temp = 150, remaining = -1),
        #        TableEntry(time = 750, start_temp = 150, end_temp = 650, remaining = -1),
        #        TableEntry(time = 120, start_temp = 650, end_temp = 50, remaining = -1),
        #        TableEntry(time = 700, start_temp = 50, end_temp = 650, remaining = -1),
        #        TableEntry(time = 200, start_temp = 650, end_temp = 650, remaining = -1),
        #        TableEntry(time = 1200, start_temp = 650, end_temp = 300, remaining = -1),
        #        TableEntry(time = 7300, start_temp = 300, end_temp = 300, remaining = -1),
        #        TableEntry(time = 1200, start_temp = 300, end_temp = 150, remaining = -1),
        #        TableEntry(time = 1200, start_temp = 150, end_temp = 150, remaining = -1),
        #        TableEntry(time = 750, start_temp = 150, end_temp = 650, remaining = -1),
        #        TableEntry(time = 120, start_temp = 650, end_temp = 50, remaining = -1),
        #        TableEntry(time = 700, start_temp = 50, end_temp = 650, remaining = -1),
        #        TableEntry(time = 200, start_temp = 650, end_temp = 650, remaining = -1),
        #        TableEntry(time = 1200, start_temp = 650, end_temp = 400, remaining = -1),
        #        TableEntry(time = 7300, start_temp = 400, end_temp = 400, remaining = -1),
        #        TableEntry(time = 1200, start_temp = 400, end_temp = 150, remaining = -1),
        #        TableEntry(time = 1200, start_temp = 150, end_temp = 150, remaining = -1),
        #        TableEntry(time = 750, start_temp = 150, end_temp = 650, remaining = -1),
        #        TableEntry(time = 120, start_temp = 650, end_temp = 50, remaining = -1),
        #        TableEntry(time = 700, start_temp = 50, end_temp = 650, remaining = -1),
        #        TableEntry(time = 200, start_temp = 650, end_temp = 650, remaining = -1),
        #        TableEntry(time = 1200, start_temp = 650, end_temp = 500, remaining = -1),
        #        TableEntry(time = 7300, start_temp = 500, end_temp = 500, remaining = -1),
        #        TableEntry(time = 1200, start_temp = 500, end_temp = 150, remaining = -1),
        #        TableEntry(time = 1200, start_temp = 150, end_temp = 150, remaining = -1),
        #        TableEntry(time = 750, start_temp = 150, end_temp = 650, remaining = -1),
        #        TableEntry(time = 120, start_temp = 650, end_temp = 50, remaining = -1),]

    def start_stop(self, running):
        if not self.enable:
            return

        if running:
            if self.running:
                return

            self.start_time = time()
            self.calculate_temperature_table()
            for row in self.table_entries:
                row.remaining = row.time
            self.current_time = 0
            self.current_row = 0
            self.row_changed(time() - self.start_time)
            self.current_temp = self.temperature_table[0]
            self.timing_timer = Timer(self.TIMING_UPDATE_INTERVAL, self._onTimer)

        else:
            if not self.running:
                return
            if self.timing_timer is not None:
                self.timing_timer.Stop()
        self.running = running

    def add_data(self, data):
        pass

    def get_data(self):
        return {'time': 0}, {'temp': self.actual_temp}

    def calculate_temperature_table(self):
        self.temperature_table = np.array([])
        for row in self.table_entries:
            self.temperature_table = np.hstack((self.temperature_table,
                                                np.linspace(row.start_temp, row.end_temp, row.time)))

    def row_changed(self, remainder):
        self.row_start_time = self.current_time + remainder
        if self.current_row >= len(self.table_entries):
            if self.loop_sequence:
                self.start_time = time()
                for row in self.table_entries:
                    row.remaining = row.time
                self.current_time = 0
                self.current_row = 0
                self.row_changed(time() - self.start_time)

    def _current_temp_changed(self, new):
        self.temp_setpoint = new
        if self.use_direct_pwm:
            self.pwm_set_direct = int(new)
            self._update_pid_fired()
            return
        self._set_parameters_fired()

    def _com_ports_list_default(self):
        import serial.tools.list_ports as lp
        l = []
        for p in lp.grep('Arduino'):
            l.append(p[0])
        return l

    def _set_parameters_fired(self):
        self.set_parameters_queue.put_nowait(struct.pack('<c5B5f', 's', 0, 0, 0, 0, self.pwm_table_index,
            self.resistance_setpoint, self.supply_voltage_setpoint, 
            self.RT_resistance_setpoint, 0, 0))

    def _update_pid_fired(self):
        self.set_parameters_queue.put_nowait(struct.pack('<c4f', 'p', self.pid_P, self.pid_I, self.pid_D, self.pid_N))
        self.set_parameters_queue.put_nowait(struct.pack('<c5B5f', 's', 0, 
            int(self.use_direct_pwm), int(self.use_direct_pv), self.pwm_set_direct, self.pwm_table_index, self.resistance_setpoint,
            self.supply_voltage_setpoint, self.RT_resistance_setpoint, 0, self.pv_set_direct))
        self.update_PID_values = 3

    def _connect_fired(self):
        if self.serial_handler is None:
            self.serial_handler = SerialHandler(self.set_parameters_queue, self.get_parameters_queue,
                    self.selected_com_port)
            if not self.serial_handler.open_port():
                return
            self.poll_timer = Timer(POLL_INTERVAL, self._poll_queue)
            self.serial_handler.start()
            self.connected = True
        else:
            self.start_stop(False)
            self.serial_handler.exit_flag = True
            self.serial_handler.join()
            self.serial_handler = None
            if self.poll_timer is not None:
                self.poll_timer.stop()
            self.connected = False

    def _save_fired(self):
        if self.filename is '':
            GenericPopupMessage(message='No filename given').edit_traits()
            return
        filehandle = open(self.filename, "w", 1)
        fieldnames = self.table_entries[0].trait_get().keys()
        csv_writer = csv.DictWriter(filehandle, fieldnames=fieldnames, delimiter=',',
                                    quotechar='"', quoting=csv.QUOTE_NONNUMERIC,
                                    lineterminator='\n')
        csv_writer.writeheader()
        for row in self.table_entries:
            csv_writer.writerow(row.trait_get())
        filehandle.close()

    def _load_fired(self):
        if self.filename is '':
            GenericPopupMessage(message='No filename given').edit_traits()
            return
        filehandle = open(self.filename, 'rU', 1)
        reader = csv.DictReader(filehandle, lineterminator='\n')
        self.table_entries = []
        for row in reader:
            self.table_entries.append(TableEntry(time = int(row['time']),
                                                start_temp=int(row['start_temp']),
                                                end_temp=int(row['end_temp']),
                                                remaining=int(row['time'])))
        filehandle.close()

    def _set_temp_table_for_calibration(self):
        pwms = [int(a) for a in np.hstack((np.linspace(0, 255, 256), np.linspace(255, 0, 256)))]
        self.table_entries = [TableEntry(time = 60, start_temp = t, end_temp = t, remaining = -1) for t in pwms]
        self.temp_setpoint = self.table_entries[0].start_temp - 1 # Make sure they are unequal