Ejemplo n.º 1
0
def navigation_sliders(data_axes, title=None):
    """Raises a windows with sliders to control the index of DataAxis
    
    Parameters
    ----------
    data_axes : list of DataAxis instances
    
    """

    class NavigationSliders(t.HasTraits):
        pass

    nav = NavigationSliders()
    view_tuple = ()
    for axis in data_axes:
        name = str(axis).replace(" ", "_")
        nav.add_class_trait(name, axis)
        nav.trait_set([name, axis])
        view_tuple += (
                        tui.Item(name,
                             style = "custom",
                             editor = tui.InstanceEditor(
                                 view=tui.View(
                                     tui.Item(
                                         "index",
                                         show_label=False,
                                         # The following is commented out
                                         # due to a traits ui bug
                                         #editor=tui.RangeEditor(mode="slider"),
                                          ),
                                     ),
                                 ),
                             ),
                         )
                               
                                  



    view = tui.View(tui.VSplit(view_tuple), title="Navigation sliders"
                                            if title is None
                                            else title)

    nav.edit_traits(view=view)
Ejemplo n.º 2
0
class ADC(traits.HasTraits):

    refreshTime = traits.Float(
        0.1, desc="how often to update the frequencies in seconds")
    logFile = traits.File
    rpiADCLogFolder = traits.String
    averageN = traits.Int(100)
    VMax = traits.Enum(3.3, 5.0)
    channelList = traits.List(
        [1, 3, 5, 6],
        desc="list of channels to show frequencies for and query")
    channelValues = [(0, 'Ch 0'), (1, 'Ch 1'), (2, 'Ch 2'), (3, 'Ch 3'),
                     (4, 'Ch 4'), (5, 'Ch 5'), (6, 'Ch 6'), (7, 'Ch 7')]
    #THIS ONLY WORKS IF PyHWI can be found! it is in the python path manager for lab-Monitoring-0
    connection = rpiADCClient.Connection()
    #if there are problems check the server is running on 192.168.0.111
    icon_trait = pyface.image_resource.ImageResource('icons/wavemeter.png')
    #oscilloscope = None

    currentLocalTime = time.localtime()
    currentYear = currentLocalTime.tm_year
    currentMonth = currentLocalTime.tm_mon
    currentDay = currentLocalTime.tm_mday

    channel0 = ADCChannel(channelNumber=0,
                          connection=connection,
                          channelName="Na Cavity Lock",
                          channelMessageHigh="Na Cavity Locked",
                          channelMessageLow="Na Cavity Out of Lock",
                          criticalValue=1.5,
                          highIsGood=True,
                          highSoundFile="NaLocked.wav",
                          lowSoundFile="NaOutOfLock.wav")

    channel1 = ADCChannel(channelNumber=1,
                          connection=connection,
                          channelName="Li MOT Fluorescence",
                          channelMessageHigh="MOT Loading",
                          channelMessageLow="No MOT",
                          criticalValue=0.1)
    channel2 = ADCChannel(
        channelNumber=2,
        connection=connection,
        channelName="Li MOT Power (stable)")  #changed by Martin
    channel3 = ADCChannel(channelNumber=3,
                          connection=connection,
                          channelName="Li MOT (unstable)")
    channel4 = ADCChannel(channelNumber=4,
                          connection=connection,
                          channelName="Na MOT Power (stable)")
    channel5 = ADCChannel(channelNumber=5,
                          connection=connection,
                          channelName="Na MOT Flourescence")
    channel6 = ADCChannel(channelNumber=6,
                          connection=connection,
                          channelName="ZS light Power")
    channel7 = ADCChannel(channelNumber=7,
                          connection=connection,
                          channelName="disconnected")

    channels = {
        0: channel0,
        1: channel1,
        2: channel2,
        3: channel3,
        4: channel4,
        5: channel5,
        6: channel6,
        7: channel7
    }

    def __init__(self, **traitsDict):
        """Called when object initialises. Starts timer etc. """
        print "Instantiating GUI.."
        super(ADC, self).__init__(**traitsDict)
        self.connection.connect()
        self.oscilloscope = Oscilloscope(connection=self.connection,
                                         resolution=self.refreshTime,
                                         visibleChannels=self.channelList)
        self.start_timer()

    def start_timer(self):
        """Called in init if user selected live update mode, otherwise called from menu action.
        Every self.wizard.updateRateSeconds, self._refresh_data_action will be called"""
        print "Timer Object Started. Will update ADC Information every %s seconds" % self.refreshTime
        self.timer = Timer(
            float(self.refreshTime) * 1000, self._refresh_Visible_channels)

    def _refreshTime_changed(self):
        self.timer.setInterval(float(self.refreshTime) * 1000)
        print "will update ADC every %s seconds" % (float(self.refreshTime))
        self.oscilloscope.resolution = self.refreshTime  #use refresh time to set resolution of oscilloscope

    def _logFile_changed(self):
        self._create_log_file()

    def _channelList_changed(self):
        """push changes to visible channels in oscilloscope """
        self.oscilloscope.visibleChannels = self.channelList

    def _logFile_default(self):
        """default log file has date stamp. log file is changed once a day """
        print "choosing default log file"
        return os.path.join(
            self.rpiADCLogFolder,
            time.strftime("rpiADC-%Y-%m-%d.csv", self.currentLocalTime))

    def getGroupFolder(self):
        """returns the location of the group folder. supports both
         linux and windows. assumes it is mounted to /media/ursa/AQOGroupFolder
         for linux"""
        if platform.system() == "Windows":
            groupFolder = os.path.join("\\\\ursa", "AQOGroupFolder")
        if platform.system() == "Linux":
            groupFolder = os.path.join("/media", "ursa", "AQOGroupFolder")
        return groupFolder

    def _rpiADCLogFolder_default(self):
        return os.path.join(self.getGroupFolder(), "Experiment Humphry",
                            "Experiment Control And Software", "rpiADC",
                            "data")

    def _create_log_file(self):
        if not os.path.exists(os.path.join(self.rpiADCLogFolder,
                                           self.logFile)):
            with open(self.logFile, 'a+') as csvFile:
                csvWriter = csv.writer(csvFile)
                csvWriter.writerow([
                    "epochSeconds", "Channel 0", "Channel 1", "Channel 2",
                    "Channel 3", "Channel 4", "Channel 5", "Channel 6",
                    "Channel 7"
                ])

    def checkDateForFileName(self):
        """gets current date and time and checks if we should change file name
        if we should it creates the new file and the name"""
        #self.currentLocalTime was already changed in log Temperatures
        if self.currentLocalTime.tm_mday != self.currentDay:
            #the day has changed we should start a new log file!
            self.logFile = self._logFile_default()
            self._create_log_file()

    def _log_channels(self):
        self.currentLocalTime = time.localtime()
        self.checkDateForFileName()
        self.currentMonth = self.currentLocalTime.tm_mon
        self.currentDay = self.currentLocalTime.tm_mday

        if not os.path.exists(os.path.join(self.rpiADCLogFolder,
                                           self.logFile)):
            self._create_log_file()

        voltages = [self.channels[i]._voltage_get() for i in range(0, 8)]
        with open(self.logFile, 'a+') as csvFile:
            csvWriter = csv.writer(csvFile)
            csvWriter.writerow([time.time()] + voltages)

    def _refresh_Visible_channels(self):
        self.connection.getResults()  #updates dictionary in connection object
        for channelNumber in self.channelList:
            channel = self.channels[channelNumber]
            channel._voltage_update()
        self.oscilloscope.updateArrays()
        self.oscilloscope.updateArrayPlotData()
        self._log_channels()

    settingsGroup = traitsui.VGroup(
        traitsui.Item("logFile", label="Log File"),
        traitsui.HGroup(
            traitsui.Item('refreshTime', label='refreshTime'),
            traitsui.Item(
                'averageN',
                label='averaging Number',
                tooltip=
                "Number of measurements taken and averaged for value shown"),
            traitsui.Item('VMax',
                          label='Maximum Voltage Setting',
                          tooltip="Whether the box is set to 3.3V or 5V max")),
    )

    selectionGroup = traitsui.Group(
        traitsui.Item('channelList',
                      editor=traitsui.CheckListEditor(values=channelValues,
                                                      cols=4),
                      style='custom',
                      label='Show'))

    groupLeft = traitsui.VGroup(
        traitsui.Item('channel0',
                      editor=traitsui.InstanceEditor(),
                      style='custom',
                      show_label=False,
                      visible_when="(0 in channelList)"),
        traitsui.Item('channel1',
                      editor=traitsui.InstanceEditor(),
                      style='custom',
                      show_label=False,
                      visible_when="(1 in channelList)"),
        traitsui.Item('channel2',
                      editor=traitsui.InstanceEditor(),
                      style='custom',
                      show_label=False,
                      visible_when="(2 in channelList)"),
        traitsui.Item('channel3',
                      editor=traitsui.InstanceEditor(),
                      style='custom',
                      show_label=False,
                      visible_when="(3 in channelList)"))

    groupRight = traitsui.VGroup(
        traitsui.Item('channel4',
                      editor=traitsui.InstanceEditor(),
                      style='custom',
                      show_label=False,
                      visible_when="(4 in channelList)"),
        traitsui.Item('channel5',
                      editor=traitsui.InstanceEditor(),
                      style='custom',
                      show_label=False,
                      visible_when="(5 in channelList)"),
        traitsui.Item('channel6',
                      editor=traitsui.InstanceEditor(),
                      style='custom',
                      show_label=False,
                      visible_when="(6 in channelList)"),
        traitsui.Item('channel7',
                      editor=traitsui.InstanceEditor(),
                      style='custom',
                      show_label=False,
                      visible_when="(7 in channelList)"))

    groupOscilloscope = traitsui.Group(
        traitsui.Item('oscilloscope',
                      editor=traitsui.InstanceEditor(),
                      style='custom',
                      show_label=False))

    groupAll = traitsui.VGroup(
        settingsGroup, selectionGroup,
        traitsui.VSplit(traitsui.HGroup(groupLeft, groupRight),
                        groupOscilloscope))

    traits_view = traitsui.View(groupAll,
                                resizable=True,
                                title="ADC Monitor",
                                handler=ADCHandler(),
                                icon=icon_trait)
Ejemplo n.º 3
0
class CSVFile(tr.HasStrictTraits):
    path = tr.Str
    count_lines = tr.Button
    _lines_number = tr.Int  # _ is the private field convention
    show_lines_number = tr.Bool
    num_of_first_lines_to_show = tr.Int(10)
    num_of_last_lines_to_show = tr.Int(10)

    first_lines = tr.Property(
        depends_on='path, num_of_first_lines_to_show, first_lines_to_skip')
    last_lines = tr.Property(
        depends_on='path, num_of_last_lines_to_show, last_lines_to_skip')
    first_lines_to_skip = tr.Range(low=0, high=10**9, mode='spinner')
    last_lines_to_skip = tr.Range(low=0, high=10**9, mode='spinner')

    def _count_lines_fired(self):
        #         return sum(1 for line in open(self.path))
        self.show_lines_number = True
        self._lines_number = self._count_lines_in_file(self.path)

    def get_lines_number(self):
        # If it was not yet calculated, calc it first
        if self._lines_number == 0:
            self._lines_number = self._count_lines_in_file(self.path)
        return self._lines_number

    def _count_lines_in_file(self, file_name):
        ''' This method will count the number of lines in a huge file pretty
        quickly using custom buffering'''
        f = open(file_name, 'rb')
        bufgen = takewhile(lambda x: x,
                           (f.raw.read(1024 * 1024) for i in repeat(None)))
        return sum(buf.count(b'\n') for buf in bufgen) + 1

    @tr.cached_property
    def _get_first_lines(self):
        first_lines_list = []
        with open(self.path) as myfile:
            for i in range(self.num_of_first_lines_to_show):
                try:
                    # Get next line if it exists!
                    line = next(myfile)
                    # The following will not executed if an exception was thrown
                    first_lines_list.append(line)
                except StopIteration:
                    # If last line ends with \n then a new empty line is
                    # actually there
                    if len(first_lines_list) != 0:
                        if first_lines_list[-1].endswith('\n'):
                            first_lines_list.append('')
                    break
        first_lines_list = self.add_line_numbers(first_lines_list)
        first_lines_list = first_lines_list[self.first_lines_to_skip:]
        first_lines_str = ''.join(first_lines_list)
        return first_lines_str

    def add_line_numbers(self, lines_list):
        new_list = []
        for line_num, line in zip(range(1, len(lines_list) + 1), lines_list):
            new_list.append('(' + str(line_num) + ')--> ' + str(line))
        return new_list

    def add_reverse_line_numbers(self, lines_list):
        new_list = []
        for line_num, line in zip(range(len(lines_list), 0, -1), lines_list):
            new_list.append('(' + str(line_num) + ')--> ' + str(line))
        return new_list

    @tr.cached_property
    def _get_last_lines(self):
        last_lines_list = self.get_last_n_lines(self.path,
                                                self.num_of_last_lines_to_show,
                                                False)
        last_lines_list = self.add_reverse_line_numbers(last_lines_list)
        last_lines_list = last_lines_list[0:len(last_lines_list) -
                                          self.last_lines_to_skip]
        last_lines_str = ''.join(last_lines_list)
        return last_lines_str

    def get_last_n_lines(self, file_name, N, skip_empty_lines=False):
        # Create an empty list to keep the track of last N lines
        list_of_lines = []
        # Open file for reading in binary mode
        with open(file_name, 'rb') as read_obj:
            # Move the cursor to the end of the file
            read_obj.seek(0, os.SEEK_END)
            # Create a buffer to keep the last read line
            buffer = bytearray()
            # Get the current position of pointer i.e eof
            pointer_location = read_obj.tell()
            # Loop till pointer reaches the top of the file
            while pointer_location >= 0:
                # Move the file pointer to the location pointed by pointer_location
                read_obj.seek(pointer_location)
                # Shift pointer location by -1
                pointer_location = pointer_location - 1
                # read that byte / character
                new_byte = read_obj.read(1)
                # If the read byte is new line character then it means one line is read
                if new_byte == b'\n':
                    # Save the line in list of lines
                    line = buffer.decode()[::-1]
                    if (skip_empty_lines):
                        line_is_empty = line.isspace()
                        if (line_is_empty == False):
                            list_of_lines.append(line)
                    else:
                        list_of_lines.append(line)
                    # If the size of list reaches N, then return the reversed list
                    if len(list_of_lines) == N:
                        return list(reversed(list_of_lines))
                    # Reinitialize the byte array to save next line
                    buffer = bytearray()
                else:
                    # If last read character is not eol then add it in buffer
                    buffer.extend(new_byte)

            # As file is read completely, if there is still data in buffer, then its first line.
            if len(buffer) > 0:
                list_of_lines.append(buffer.decode()[::-1])
        # return the reversed list
        return list(reversed(list_of_lines))

    traits_view = ui.View(
        ui.Item('path', style='readonly', label='File'),
        ui.HGroup(
            ui.UItem('count_lines'),
            ui.Item('_lines_number',
                    style='readonly',
                    visible_when='show_lines_number == True')),
        ui.VSplit(
            ui.HGroup(ui.Item('first_lines', style='custom'),
                      'first_lines_to_skip',
                      label='First lines in the file'),
            ui.HGroup(ui.Item('last_lines', style='custom'),
                      'last_lines_to_skip',
                      label='Last lines in the file')))
Ejemplo n.º 4
0
class HCFF(tr.HasStrictTraits):
    '''High-Cycle Fatigue Filter
    '''

    #=========================================================================
    # Traits definitions
    #=========================================================================
    decimal = tr.Enum(',', '.')
    delimiter = tr.Str(';')
    file_csv = tr.File
    open_file_csv = tr.Button('Input file')
    skip_rows = tr.Int(4, auto_set=False, enter_set=True)
    columns_headers_list = tr.List([])
    x_axis = tr.Enum(values='columns_headers_list')
    y_axis = tr.Enum(values='columns_headers_list')
    x_axis_multiplier = tr.Enum(1, -1)
    y_axis_multiplier = tr.Enum(-1, 1)
    npy_folder_path = tr.Str
    file_name = tr.Str
    apply_filters = tr.Bool
    force_name = tr.Str('Kraft')
    peak_force_before_cycles = tr.Float(30)
    plots_num = tr.Enum(1, 2, 3, 4, 6, 9)
    plot_list = tr.List()
    plot = tr.Button
    add_plot = tr.Button
    add_creep_plot = tr.Button
    parse_csv_to_npy = tr.Button
    generate_filtered_npy = tr.Button
    add_columns_average = tr.Button
    force_max = tr.Float(100)
    force_min = tr.Float(40)

    figure = tr.Instance(Figure)

#     plots_list = tr.List(editor=ui.SetEditor(
#         values=['kumquats', 'pomegranates', 'kiwi'],
#         can_move_all=False,
#         left_column_title='List'))

    #=========================================================================
    # File management
    #=========================================================================

    def _open_file_csv_fired(self):
        """ Handles the user clicking the 'Open...' button.
        """
        extns = ['*.csv', ]  # seems to handle only one extension...
        wildcard = '|'.join(extns)

        dialog = FileDialog(title='Select text file',
                            action='open', wildcard=wildcard,
                            default_path=self.file_csv)
        dialog.open()
        self.file_csv = dialog.path

        """ Filling x_axis and y_axis with values """
        headers_array = np.array(
            pd.read_csv(
                self.file_csv, delimiter=self.delimiter, decimal=self.decimal,
                nrows=1, header=None
            )
        )[0]
        for i in range(len(headers_array)):
            headers_array[i] = self.get_valid_file_name(headers_array[i])
        self.columns_headers_list = list(headers_array)

        """ Saving file name and path and creating NPY folder """
        dir_path = os.path.dirname(self.file_csv)
        self.npy_folder_path = os.path.join(dir_path, 'NPY')
        if os.path.exists(self.npy_folder_path) == False:
            os.makedirs(self.npy_folder_path)

        self.file_name = os.path.splitext(os.path.basename(self.file_csv))[0]

    #=========================================================================
    # Parameters of the filter algorithm
    #=========================================================================

    def _figure_default(self):
        figure = Figure(facecolor='white')
        figure.set_tight_layout(True)
        return figure

    def _parse_csv_to_npy_fired(self):
        print('Parsing csv into npy files...')

        for i in range(len(self.columns_headers_list)):
            column_array = np.array(pd.read_csv(
                self.file_csv, delimiter=self.delimiter, decimal=self.decimal, skiprows=self.skip_rows, usecols=[i]))
            np.save(os.path.join(self.npy_folder_path, self.file_name +
                                 '_' + self.columns_headers_list[i] + '.npy'), column_array)

        print('Finsihed parsing csv into npy files.')

    def get_valid_file_name(self, original_file_name):
        valid_chars = "-_.() %s%s" % (string.ascii_letters, string.digits)
        new_valid_file_name = ''.join(
            c for c in original_file_name if c in valid_chars)
        return new_valid_file_name

#     def _add_columns_average_fired(self):
#         columns_average = ColumnsAverage(
#             columns_names=self.columns_headers_list)
#         # columns_average.set_columns_headers_list(self.columns_headers_list)
#         columns_average.configure_traits()

    def _generate_filtered_npy_fired(self):

        # 1- Export filtered force
        force = np.load(os.path.join(self.npy_folder_path,
                                     self.file_name + '_' + self.force_name + '.npy')).flatten()
        peak_force_before_cycles_index = np.where(
            abs((force)) > abs(self.peak_force_before_cycles))[0][0]
        force_ascending = force[0:peak_force_before_cycles_index]
        force_rest = force[peak_force_before_cycles_index:]

        force_max_indices, force_min_indices = self.get_array_max_and_min_indices(
            force_rest)

        force_max_min_indices = np.concatenate(
            (force_min_indices, force_max_indices))
        force_max_min_indices.sort()

        force_rest_filtered = force_rest[force_max_min_indices]
        force_filtered = np.concatenate((force_ascending, force_rest_filtered))
        np.save(os.path.join(self.npy_folder_path, self.file_name +
                             '_' + self.force_name + '_filtered.npy'), force_filtered)

        # 2- Export filtered displacements
        # TODO I skipped time with presuming it's the first column
        for i in range(1, len(self.columns_headers_list)):
            if self.columns_headers_list[i] != str(self.force_name):

                disp = np.load(os.path.join(self.npy_folder_path, self.file_name +
                                            '_' + self.columns_headers_list[i] + '.npy')).flatten()
                disp_ascending = disp[0:peak_force_before_cycles_index]
                disp_rest = disp[peak_force_before_cycles_index:]
                disp_ascending = savgol_filter(
                    disp_ascending, window_length=51, polyorder=2)
                disp_rest = disp_rest[force_max_min_indices]
                filtered_disp = np.concatenate((disp_ascending, disp_rest))
                np.save(os.path.join(self.npy_folder_path, self.file_name + '_' +
                                     self.columns_headers_list[i] + '_filtered.npy'), filtered_disp)

        # 3- Export creep for displacements
        # Cutting unwanted max min values to get correct full cycles and remove
        # false min/max values caused by noise
        force_max_indices_cutted, force_min_indices_cutted = self.cut_indices_in_range(force_rest,
                                                                                       force_max_indices,
                                                                                       force_min_indices,
                                                                                       self.force_max,
                                                                                       self.force_min)

        print("Cycles number= ", len(force_min_indices))
        print("Cycles number after cutting unwanted max-min range= ",
              len(force_min_indices_cutted))

        # TODO I skipped time with presuming it's the first column
        for i in range(1, len(self.columns_headers_list)):
            if self.columns_headers_list[i] != str(self.force_name):
                disp_rest_maxima = disp_rest[force_max_indices_cutted]
                disp_rest_minima = disp_rest[force_min_indices_cutted]
                np.save(os.path.join(self.npy_folder_path, self.file_name + '_' +
                                     self.columns_headers_list[i] + '_max.npy'), disp_rest_maxima)
                np.save(os.path.join(self.npy_folder_path, self.file_name + '_' +
                                     self.columns_headers_list[i] + '_min.npy'), disp_rest_minima)

        print('Filtered npy files are generated.')

    def cut_indices_in_range(self, array, max_indices, min_indices, range_upper_value, range_lower_value):
        cutted_max_indices = []
        cutted_min_indices = []

        for max_index in max_indices:
            if abs(array[max_index]) > abs(range_upper_value):
                cutted_max_indices.append(max_index)
        for min_index in min_indices:
            if abs(array[min_index]) < abs(range_lower_value):
                cutted_min_indices.append(min_index)
        return cutted_max_indices, cutted_min_indices

    def get_array_max_and_min_indices(self, input_array):

        # Checking dominant sign
        positive_values_count = np.sum(np.array(input_array) >= 0)
        negative_values_count = input_array.size - positive_values_count

        # Getting max and min indices
        if (positive_values_count > negative_values_count):
            force_max_indices = argrelextrema(input_array, np.greater_equal)[0]
            force_min_indices = argrelextrema(input_array, np.less_equal)[0]
        else:
            force_max_indices = argrelextrema(input_array, np.less_equal)[0]
            force_min_indices = argrelextrema(input_array, np.greater_equal)[0]

        # Remove subsequent max/min indices (np.greater_equal will give 1,2 for
        # [4, 8, 8, 1])
        force_max_indices = self.remove_subsequent_max_values(
            force_max_indices)
        force_min_indices = self.remove_subsequent_min_values(
            force_min_indices)

        # If size is not equal remove the last element from the big one
        if force_max_indices.size > force_min_indices.size:
            force_max_indices = force_max_indices[:-1]
        elif force_max_indices.size < force_min_indices.size:
            force_min_indices = force_min_indices[:-1]

        return force_max_indices, force_min_indices

    def remove_subsequent_max_values(self, force_max_indices):
        to_delete_from_maxima = []
        for i in range(force_max_indices.size - 1):
            if force_max_indices[i + 1] - force_max_indices[i] == 1:
                to_delete_from_maxima.append(i)

        force_max_indices = np.delete(force_max_indices, to_delete_from_maxima)
        return force_max_indices

    def remove_subsequent_min_values(self, force_min_indices):
        to_delete_from_minima = []
        for i in range(force_min_indices.size - 1):
            if force_min_indices[i + 1] - force_min_indices[i] == 1:
                to_delete_from_minima.append(i)
        force_min_indices = np.delete(force_min_indices, to_delete_from_minima)
        return force_min_indices

    #=========================================================================
    # Plotting
    #=========================================================================
    plot_figure_num = tr.Int(0)

    def _plot_fired(self):
        ax = self.figure.add_subplot()

    def x_plot_fired(self):
        self.plot_figure_num += 1
        plt.draw()
        plt.show()

    data_changed = tr.Event

    def _add_plot_fired(self):

        if False:  # (len(self.plot_list) >= self.plots_num):
            dialog = MessageDialog(
                title='Attention!', message='Max plots number is {}'.format(self.plots_num))
            dialog.open()
            return

        print('Loading npy files...')

        if self.apply_filters:
            x_axis_name = self.x_axis + '_filtered'
            y_axis_name = self.y_axis + '_filtered'
            x_axis_array = self.x_axis_multiplier * \
                np.load(os.path.join(self.npy_folder_path,
                                     self.file_name + '_' + self.x_axis + '_filtered.npy'))
            y_axis_array = self.y_axis_multiplier * \
                np.load(os.path.join(self.npy_folder_path,
                                     self.file_name + '_' + self.y_axis + '_filtered.npy'))
        else:
            x_axis_name = self.x_axis
            y_axis_name = self.y_axis
            x_axis_array = self.x_axis_multiplier * \
                np.load(os.path.join(self.npy_folder_path,
                                     self.file_name + '_' + self.x_axis + '.npy'))
            y_axis_array = self.y_axis_multiplier * \
                np.load(os.path.join(self.npy_folder_path,
                                     self.file_name + '_' + self.y_axis + '.npy'))

        print('Adding Plot...')
        mpl.rcParams['agg.path.chunksize'] = 50000

#        plt.figure(self.plot_figure_num)
        ax = self.figure.add_subplot(1, 1, 1)

        ax.set_xlabel('Displacement [mm]')
        ax.set_ylabel('kN')
        ax.set_title('Original data', fontsize=20)
        ax.plot(x_axis_array, y_axis_array, 'k', linewidth=0.8)

        self.plot_list.append('{}, {}'.format(x_axis_name, y_axis_name))
        self.data_changed = True
        print('Finished adding plot!')

    def apply_new_subplot(self):
        plt = self.figure
        if (self.plots_num == 1):
            plt.add_subplot(1, 1, 1)
        elif (self.plots_num == 2):
            plot_location = int('12' + str(len(self.plot_list) + 1))
            plt.add_subplot(plot_location)
        elif (self.plots_num == 3):
            plot_location = int('13' + str(len(self.plot_list) + 1))
            plt.add_subplot(plot_location)
        elif (self.plots_num == 4):
            plot_location = int('22' + str(len(self.plot_list) + 1))
            plt.add_subplot(plot_location)
        elif (self.plots_num == 6):
            plot_location = int('23' + str(len(self.plot_list) + 1))
            plt.add_subplot(plot_location)
        elif (self.plots_num == 9):
            plot_location = int('33' + str(len(self.plot_list) + 1))
            plt.add_subplot(plot_location)

    def _add_creep_plot_fired(self):

        plt = self.figure
        if (len(self.plot_list) >= self.plots_num):
            dialog = MessageDialog(
                title='Attention!', message='Max plots number is {}'.format(self.plots_num))
            dialog.open()
            return

        disp_max = self.x_axis_multiplier * \
            np.load(os.path.join(self.npy_folder_path,
                                 self.file_name + '_' + self.x_axis + '_max.npy'))
        disp_min = self.x_axis_multiplier * \
            np.load(os.path.join(self.npy_folder_path,
                                 self.file_name + '_' + self.x_axis + '_min.npy'))

        print('Adding creep plot...')
        mpl.rcParams['agg.path.chunksize'] = 50000

        self.apply_new_subplot()
        plt.xlabel('Cycles number')
        plt.ylabel('mm')
        plt.title('Fatigue creep curve', fontsize=20)
        plt.plot(np.arange(0, disp_max.size), disp_max,
                 'k', linewidth=0.8, color='red')
        plt.plot(np.arange(0, disp_min.size), disp_min,
                 'k', linewidth=0.8, color='green')

        self.plot_list.append('Plot {}'.format(len(self.plot_list) + 1))

        print('Finished adding creep plot!')

    #=========================================================================
    # Configuration of the view
    #=========================================================================

    traits_view = ui.View(
        ui.HSplit(
            ui.VSplit(
                ui.HGroup(
                    ui.UItem('open_file_csv'),
                    ui.UItem('file_csv', style='readonly'),
                    label='Input data'
                ),
                ui.Item('add_columns_average', show_label=False),
                ui.VGroup(
                    ui.Item('skip_rows'),
                    ui.Item('decimal'),
                    ui.Item('delimiter'),
                    ui.Item('parse_csv_to_npy', show_label=False),
                    label='Filter parameters'
                ),
                ui.VGroup(
                    ui.Item('plots_num'),
                    ui.HGroup(ui.Item('x_axis'), ui.Item('x_axis_multiplier')),
                    ui.HGroup(ui.Item('y_axis'), ui.Item('y_axis_multiplier')),
                    ui.HGroup(ui.Item('add_plot', show_label=False),
                              ui.Item('apply_filters')),
                    ui.HGroup(ui.Item('add_creep_plot', show_label=False)),
                    ui.Item('plot_list'),
                    ui.Item('plot', show_label=False),
                    show_border=True,
                    label='Plotting settings'),
            ),
            ui.VGroup(
                ui.Item('force_name'),
                ui.HGroup(ui.Item('peak_force_before_cycles'),
                          show_border=True, label='Skip noise of ascending branch:'),
                #                     ui.Item('plots_list'),
                ui.VGroup(ui.Item('force_max'),
                          ui.Item('force_min'),
                          show_border=True,
                          label='Cut fake cycles for creep:'),
                ui.Item('generate_filtered_npy', show_label=False),
                show_border=True,
                label='Filters'
            ),
            ui.UItem('figure', editor=MPLFigureEditor(),
                     resizable=True,
                     springy=True,
                     width=0.3,
                     label='2d plots'),
        ),
        title='HCFF Filter',
        resizable=True,
        width=0.6,
        height=0.6

    )
class HCFF(tr.HasStrictTraits):
    '''High-Cycle Fatigue Filter
    '''

    #=========================================================================
    # Traits definitions
    #=========================================================================
    decimal = tr.Enum(',', '.')
    delimiter = tr.Str(';')
    records_per_second = tr.Float(100)
    take_time_from_first_column = tr.Bool
    file_csv = tr.File
    open_file_csv = tr.Button('Input file')
    skip_first_rows = tr.Int(3, auto_set=False, enter_set=True)
    columns_headers_list = tr.List([])
    x_axis = tr.Enum(values='columns_headers_list')
    y_axis = tr.Enum(values='columns_headers_list')
    x_axis_multiplier = tr.Enum(1, -1)
    y_axis_multiplier = tr.Enum(-1, 1)
    npy_folder_path = tr.Str
    file_name = tr.Str
    apply_filters = tr.Bool
    normalize_cycles = tr.Bool
    smooth = tr.Bool
    plot_every_nth_point = tr.Range(low=1, high=1000000, mode='spinner')
    force_name = tr.Str('Kraft')
    old_peak_force_before_cycles = tr.Float
    peak_force_before_cycles = tr.Float
    window_length = tr.Int(31)
    polynomial_order = tr.Int(2)
    activate = tr.Bool(False)
    plots_num = tr.Enum(1, 2, 3, 4, 6, 9)
    plot_list = tr.List()
    add_plot = tr.Button
    add_creep_plot = tr.Button(desc='Creep plot of X axis array')
    clear_plot = tr.Button
    parse_csv_to_npy = tr.Button
    generate_filtered_and_creep_npy = tr.Button
    add_columns_average = tr.Button
    force_max = tr.Float(100)
    force_min = tr.Float(40)
    min_cycle_force_range = tr.Float(50)
    cutting_method = tr.Enum('Define min cycle range(force difference)',
                             'Define Max, Min')
    columns_to_be_averaged = tr.List

    figure = tr.Instance(Figure)

    def _figure_default(self):
        figure = Figure(facecolor='white')
        figure.set_tight_layout(True)
        return figure

    #=========================================================================
    # File management
    #=========================================================================

    def _open_file_csv_fired(self):

        self.reset()
        """ Handles the user clicking the 'Open...' button.
        """
        extns = [
            '*.csv',
        ]  # seems to handle only one extension...
        wildcard = '|'.join(extns)

        dialog = FileDialog(title='Select text file',
                            action='open',
                            wildcard=wildcard,
                            default_path=self.file_csv)

        result = dialog.open()
        """ Test if the user opened a file to avoid throwing an exception if he 
        doesn't """
        if result == OK:
            self.file_csv = dialog.path
        else:
            return
        """ Filling x_axis and y_axis with values """
        headers_array = np.array(
            pd.read_csv(self.file_csv,
                        delimiter=self.delimiter,
                        decimal=self.decimal,
                        nrows=1,
                        header=None))[0]
        for i in range(len(headers_array)):
            headers_array[i] = self.get_valid_file_name(headers_array[i])
        self.columns_headers_list = list(headers_array)
        """ Saving file name and path and creating NPY folder """
        dir_path = os.path.dirname(self.file_csv)
        self.npy_folder_path = os.path.join(dir_path, 'NPY')
        if os.path.exists(self.npy_folder_path) == False:
            os.makedirs(self.npy_folder_path)

        self.file_name = os.path.splitext(os.path.basename(self.file_csv))[0]

    def _parse_csv_to_npy_fired(self):
        print('Parsing csv into npy files...')

        for i in range(
                len(self.columns_headers_list) -
                len(self.columns_to_be_averaged)):
            column_array = np.array(
                pd.read_csv(self.file_csv,
                            delimiter=self.delimiter,
                            decimal=self.decimal,
                            skiprows=self.skip_first_rows,
                            usecols=[i]))
            """ TODO! Create time array supposing it's column is the
            first one in the file and that we have 100 reads in 1 second """
            if i == 0 and self.take_time_from_first_column == False:
                column_array = np.arange(start=0.0,
                                         stop=len(column_array) /
                                         self.records_per_second,
                                         step=1.0 / self.records_per_second)

            np.save(
                os.path.join(
                    self.npy_folder_path, self.file_name + '_' +
                    self.columns_headers_list[i] + '.npy'), column_array)
        """ Exporting npy arrays of averaged columns """
        for columns_names in self.columns_to_be_averaged:
            temp = np.zeros((1))
            for column_name in columns_names:
                temp = temp + np.load(
                    os.path.join(self.npy_folder_path, self.file_name + '_' +
                                 column_name + '.npy')).flatten()
            avg = temp / len(columns_names)

            avg_file_suffex = self.get_suffex_for_columns_to_be_averaged(
                columns_names)
            np.save(
                os.path.join(self.npy_folder_path,
                             self.file_name + '_' + avg_file_suffex + '.npy'),
                avg)

        print('Finsihed parsing csv into npy files.')

    def get_suffex_for_columns_to_be_averaged(self, columns_names):
        suffex_for_saved_file_name = 'avg_' + '_'.join(columns_names)
        return suffex_for_saved_file_name

    def get_valid_file_name(self, original_file_name):
        valid_chars = "-_.() %s%s" % (string.ascii_letters, string.digits)
        new_valid_file_name = ''.join(c for c in original_file_name
                                      if c in valid_chars)
        return new_valid_file_name

    def _clear_plot_fired(self):
        self.figure.clear()
        self.plot_list = []
        self.data_changed = True

    def _add_columns_average_fired(self):
        columns_average = ColumnsAverage()
        for name in self.columns_headers_list:
            columns_average.columns.append(Column(column_name=name))

        # kind='modal' pauses the implementation until the window is closed
        columns_average.configure_traits(kind='modal')

        columns_to_be_averaged_temp = []
        for i in columns_average.columns:
            if i.selected:
                columns_to_be_averaged_temp.append(i.column_name)

        if columns_to_be_averaged_temp:  # If it's not empty
            self.columns_to_be_averaged.append(columns_to_be_averaged_temp)

            avg_file_suffex = self.get_suffex_for_columns_to_be_averaged(
                columns_to_be_averaged_temp)
            self.columns_headers_list.append(avg_file_suffex)

    def _generate_filtered_and_creep_npy_fired(self):

        if self.npy_files_exist(
                os.path.join(self.npy_folder_path, self.file_name + '_' +
                             self.force_name + '.npy')) == False:
            return

        # 1- Export filtered force
        force = np.load(
            os.path.join(self.npy_folder_path, self.file_name + '_' +
                         self.force_name + '.npy')).flatten()
        peak_force_before_cycles_index = np.where(
            abs((force)) > abs(self.peak_force_before_cycles))[0][0]
        force_ascending = force[0:peak_force_before_cycles_index]
        force_rest = force[peak_force_before_cycles_index:]

        force_max_indices, force_min_indices = self.get_array_max_and_min_indices(
            force_rest)

        force_max_min_indices = np.concatenate(
            (force_min_indices, force_max_indices))
        force_max_min_indices.sort()

        force_rest_filtered = force_rest[force_max_min_indices]
        force_filtered = np.concatenate((force_ascending, force_rest_filtered))
        np.save(
            os.path.join(
                self.npy_folder_path,
                self.file_name + '_' + self.force_name + '_filtered.npy'),
            force_filtered)

        # 2- Export filtered displacements
        # TODO I skipped time presuming it's the first column
        for i in range(1, len(self.columns_headers_list)):
            if self.columns_headers_list[i] != str(self.force_name):

                disp = np.load(
                    os.path.join(
                        self.npy_folder_path, self.file_name + '_' +
                        self.columns_headers_list[i] + '.npy')).flatten()
                disp_ascending = disp[0:peak_force_before_cycles_index]
                disp_rest = disp[peak_force_before_cycles_index:]

                if self.activate == True:
                    disp_ascending = savgol_filter(
                        disp_ascending,
                        window_length=self.window_length,
                        polyorder=self.polynomial_order)

                disp_rest_filtered = disp_rest[force_max_min_indices]
                filtered_disp = np.concatenate(
                    (disp_ascending, disp_rest_filtered))
                np.save(
                    os.path.join(
                        self.npy_folder_path, self.file_name + '_' +
                        self.columns_headers_list[i] + '_filtered.npy'),
                    filtered_disp)

        # 3- Export creep for displacements
        # Cutting unwanted max min values to get correct full cycles and remove
        # false min/max values caused by noise
        if self.cutting_method == "Define Max, Min":
            force_max_indices_cutted, force_min_indices_cutted = \
                self.cut_indices_of_min_max_range(force_rest,
                                                  force_max_indices,
                                                  force_min_indices,
                                                  self.force_max,
                                                  self.force_min)
        elif self.cutting_method == "Define min cycle range(force difference)":
            force_max_indices_cutted, force_min_indices_cutted = \
                self.cut_indices_of_defined_range(force_rest,
                                                  force_max_indices,
                                                  force_min_indices,
                                                  self.min_cycle_force_range)

        print("Cycles number= ", len(force_min_indices))
        print("Cycles number after cutting fake cycles because of noise= ",
              len(force_min_indices_cutted))

        # TODO I skipped time with presuming it's the first column
        for i in range(1, len(self.columns_headers_list)):
            array = np.load(
                os.path.join(
                    self.npy_folder_path, self.file_name + '_' +
                    self.columns_headers_list[i] + '.npy')).flatten()
            array_rest = array[peak_force_before_cycles_index:]
            array_rest_maxima = array_rest[force_max_indices_cutted]
            array_rest_minima = array_rest[force_min_indices_cutted]
            np.save(
                os.path.join(
                    self.npy_folder_path, self.file_name + '_' +
                    self.columns_headers_list[i] + '_max.npy'),
                array_rest_maxima)
            np.save(
                os.path.join(
                    self.npy_folder_path, self.file_name + '_' +
                    self.columns_headers_list[i] + '_min.npy'),
                array_rest_minima)

        print('Filtered and creep npy files are generated.')

    def cut_indices_of_min_max_range(self, array, max_indices, min_indices,
                                     range_upper_value, range_lower_value):
        cutted_max_indices = []
        cutted_min_indices = []

        for max_index in max_indices:
            if abs(array[max_index]) > abs(range_upper_value):
                cutted_max_indices.append(max_index)
        for min_index in min_indices:
            if abs(array[min_index]) < abs(range_lower_value):
                cutted_min_indices.append(min_index)
        return cutted_max_indices, cutted_min_indices

    def cut_indices_of_defined_range(self, array, max_indices, min_indices,
                                     range_):
        cutted_max_indices = []
        cutted_min_indices = []

        for max_index, min_index in zip(max_indices, min_indices):
            if abs(array[max_index] - array[min_index]) > range_:
                cutted_max_indices.append(max_index)
                cutted_min_indices.append(min_index)

        return cutted_max_indices, cutted_min_indices

    def get_array_max_and_min_indices(self, input_array):

        # Checking dominant sign
        positive_values_count = np.sum(np.array(input_array) >= 0)
        negative_values_count = input_array.size - positive_values_count

        # Getting max and min indices
        if (positive_values_count > negative_values_count):
            force_max_indices = argrelextrema(input_array, np.greater_equal)[0]
            force_min_indices = argrelextrema(input_array, np.less_equal)[0]
        else:
            force_max_indices = argrelextrema(input_array, np.less_equal)[0]
            force_min_indices = argrelextrema(input_array, np.greater_equal)[0]

        # Remove subsequent max/min indices (np.greater_equal will give 1,2 for
        # [4, 8, 8, 1])
        force_max_indices = self.remove_subsequent_max_values(
            force_max_indices)
        force_min_indices = self.remove_subsequent_min_values(
            force_min_indices)

        # If size is not equal remove the last element from the big one
        if force_max_indices.size > force_min_indices.size:
            force_max_indices = force_max_indices[:-1]
        elif force_max_indices.size < force_min_indices.size:
            force_min_indices = force_min_indices[:-1]

        return force_max_indices, force_min_indices

    def remove_subsequent_max_values(self, force_max_indices):
        to_delete_from_maxima = []
        for i in range(force_max_indices.size - 1):
            if force_max_indices[i + 1] - force_max_indices[i] == 1:
                to_delete_from_maxima.append(i)

        force_max_indices = np.delete(force_max_indices, to_delete_from_maxima)
        return force_max_indices

    def remove_subsequent_min_values(self, force_min_indices):
        to_delete_from_minima = []
        for i in range(force_min_indices.size - 1):
            if force_min_indices[i + 1] - force_min_indices[i] == 1:
                to_delete_from_minima.append(i)
        force_min_indices = np.delete(force_min_indices, to_delete_from_minima)
        return force_min_indices

    def _activate_changed(self):
        if self.activate == False:
            self.old_peak_force_before_cycles = self.peak_force_before_cycles
            self.peak_force_before_cycles = 0
        else:
            self.peak_force_before_cycles = self.old_peak_force_before_cycles

    def _window_length_changed(self, new):

        if new <= self.polynomial_order:
            dialog = MessageDialog(
                title='Attention!',
                message='Window length must be bigger than polynomial order.')
            dialog.open()

        if new % 2 == 0 or new <= 0:
            dialog = MessageDialog(
                title='Attention!',
                message='Window length must be odd positive integer.')
            dialog.open()

    def _polynomial_order_changed(self, new):

        if new >= self.window_length:
            dialog = MessageDialog(
                title='Attention!',
                message='Polynomial order must be less than window length.')
            dialog.open()

    #=========================================================================
    # Plotting
    #=========================================================================

    plot_list_current_elements_num = tr.Int(0)

    def npy_files_exist(self, path):
        if os.path.exists(path) == True:
            return True
        else:
            dialog = MessageDialog(
                title='Attention!',
                message='Please parse csv file to generate npy files first.'.
                format(self.plots_num))
            dialog.open()
            return False

    def filtered_and_creep_npy_files_exist(self, path):
        if os.path.exists(path) == True:
            return True
        else:
            dialog = MessageDialog(
                title='Attention!',
                message='Please generate filtered and creep npy files first.'.
                format(self.plots_num))
            dialog.open()
            return False

    def max_plots_number_is_reached(self):
        if len(self.plot_list) >= self.plots_num:
            dialog = MessageDialog(title='Attention!',
                                   message='Max plots number is {}'.format(
                                       self.plots_num))
            dialog.open()
            return True
        else:
            return False

    def _plot_list_changed(self):
        if len(self.plot_list) > self.plot_list_current_elements_num:
            self.plot_list_current_elements_num = len(self.plot_list)

    data_changed = tr.Event

    def _add_plot_fired(self):

        if self.max_plots_number_is_reached() == True:
            return

        if self.apply_filters:

            if self.filtered_and_creep_npy_files_exist(
                    os.path.join(
                        self.npy_folder_path, self.file_name + '_' +
                        self.x_axis + '_filtered.npy')) == False:
                return

            x_axis_name = self.x_axis + '_filtered'
            y_axis_name = self.y_axis + '_filtered'

            print('Loading npy files...')

            x_axis_array = self.x_axis_multiplier * \
                np.load(os.path.join(self.npy_folder_path,
                                     self.file_name + '_' + self.x_axis
                                     + '_filtered.npy'))
            y_axis_array = self.y_axis_multiplier * \
                np.load(os.path.join(self.npy_folder_path,
                                     self.file_name + '_' + self.y_axis
                                     + '_filtered.npy'))
        else:

            if self.npy_files_exist(
                    os.path.join(self.npy_folder_path, self.file_name + '_' +
                                 self.x_axis + '.npy')) == False:
                return

            x_axis_name = self.x_axis
            y_axis_name = self.y_axis

            print('Loading npy files...')

            x_axis_array = self.x_axis_multiplier * \
                np.load(os.path.join(self.npy_folder_path,
                                     self.file_name + '_' + self.x_axis
                                     + '.npy'))
            y_axis_array = self.y_axis_multiplier * \
                np.load(os.path.join(self.npy_folder_path,
                                     self.file_name + '_' + self.y_axis
                                     + '.npy'))

        print('Adding Plot...')
        mpl.rcParams['agg.path.chunksize'] = 50000

        ax = self.apply_new_subplot()

        ax.set_xlabel(x_axis_name)
        ax.set_ylabel(y_axis_name)
        ax.plot(x_axis_array,
                y_axis_array,
                'k',
                linewidth=1.2,
                color=np.random.rand(3, ),
                label=self.file_name + ', ' + x_axis_name)

        ax.legend()

        self.plot_list.append('{}, {}'.format(x_axis_name, y_axis_name))
        self.data_changed = True
        print('Finished adding plot!')

    def apply_new_subplot(self):
        plt = self.figure
        if (self.plots_num == 1):
            return plt.add_subplot(1, 1, 1)
        elif (self.plots_num == 2):
            plot_location = int('12' + str(len(self.plot_list) + 1))
            return plt.add_subplot(plot_location)
        elif (self.plots_num == 3):
            plot_location = int('13' + str(len(self.plot_list) + 1))
            return plt.add_subplot(plot_location)
        elif (self.plots_num == 4):
            plot_location = int('22' + str(len(self.plot_list) + 1))
            return plt.add_subplot(plot_location)
        elif (self.plots_num == 6):
            plot_location = int('23' + str(len(self.plot_list) + 1))
            return plt.add_subplot(plot_location)
        elif (self.plots_num == 9):
            plot_location = int('33' + str(len(self.plot_list) + 1))
            return plt.add_subplot(plot_location)

    def _add_creep_plot_fired(self):

        if self.filtered_and_creep_npy_files_exist(
                os.path.join(self.npy_folder_path, self.file_name + '_' +
                             self.x_axis + '_max.npy')) == False:
            return

        if self.max_plots_number_is_reached() == True:
            return

        disp_max = self.x_axis_multiplier * \
            np.load(os.path.join(self.npy_folder_path,
                                 self.file_name + '_' + self.x_axis + '_max.npy'))
        disp_min = self.x_axis_multiplier * \
            np.load(os.path.join(self.npy_folder_path,
                                 self.file_name + '_' + self.x_axis + '_min.npy'))
        complete_cycles_number = disp_max.size

        print('Adding creep-fatigue plot...')
        mpl.rcParams['agg.path.chunksize'] = 50000

        ax = self.apply_new_subplot()

        ax.set_xlabel('Cycles number')
        ax.set_ylabel(self.x_axis)

        if self.plot_every_nth_point > 1:
            disp_max = disp_max[0::self.plot_every_nth_point]
            disp_min = disp_min[0::self.plot_every_nth_point]

        if self.smooth:
            # Keeping the first item of the array and filtering the rest
            disp_max = np.concatenate(
                (np.array([disp_max[0]]),
                 savgol_filter(disp_max[1:],
                               window_length=self.window_length,
                               polyorder=self.polynomial_order)))
            disp_min = np.concatenate(
                (np.array([disp_min[0]]),
                 savgol_filter(disp_min[1:],
                               window_length=self.window_length,
                               polyorder=self.polynomial_order)))

        if self.normalize_cycles:
            ax.plot(np.linspace(0, 1., disp_max.size),
                    disp_max,
                    'k',
                    linewidth=1.2,
                    color='red',
                    label='Max' + ', ' + self.file_name + ', ' + self.x_axis)
            ax.plot(np.linspace(0, 1., disp_max.size),
                    disp_min,
                    'k',
                    linewidth=1.2,
                    color='green',
                    label='Min' + ', ' + self.file_name + ', ' + self.x_axis)
        else:
            ax.plot(np.linspace(0, complete_cycles_number, disp_max.size),
                    disp_max,
                    'k',
                    linewidth=1.2,
                    color='red',
                    label='Max' + ', ' + self.file_name + ', ' + self.x_axis)
            ax.plot(np.linspace(0, complete_cycles_number, disp_max.size),
                    disp_min,
                    'k',
                    linewidth=1.2,
                    color='green',
                    label='Min' + ', ' + self.file_name + ', ' + self.x_axis)

        ax.legend()

        self.plot_list.append('Creep-fatigue: {}, {}'.format(
            self.x_axis, self.y_axis))
        self.data_changed = True

        print('Finished adding creep-fatigue plot!')

    def reset(self):
        self.delimiter = ';'
        self.skip_first_rows = 3
        self.columns_headers_list = []
        self.npy_folder_path = ''
        self.file_name = ''
        self.apply_filters = False
        self.force_name = 'Kraft'
        self.plot_list = []
        self.columns_to_be_averaged = []

    #=========================================================================
    # Configuration of the view
    #=========================================================================

    traits_view = ui.View(ui.HSplit(
        ui.VSplit(
            ui.HGroup(ui.UItem('open_file_csv'),
                      ui.UItem('file_csv', style='readonly', width=0.1),
                      label='Input data'),
            ui.Item('add_columns_average', show_label=False),
            ui.VGroup(
                ui.VGroup(ui.Item(
                    'records_per_second',
                    enabled_when='take_time_from_first_column == False'),
                          ui.Item('take_time_from_first_column'),
                          label='Time calculation',
                          show_border=True),
                ui.VGroup(ui.Item('skip_first_rows'),
                          ui.Item('decimal'),
                          ui.Item('delimiter'),
                          ui.Item('parse_csv_to_npy', show_label=False),
                          label='Processing csv file',
                          show_border=True),
                ui.VGroup(ui.HGroup(ui.Item('plots_num'),
                                    ui.Item('clear_plot')),
                          ui.HGroup(ui.Item('x_axis'),
                                    ui.Item('x_axis_multiplier')),
                          ui.HGroup(ui.Item('y_axis'),
                                    ui.Item('y_axis_multiplier')),
                          ui.VGroup(ui.HGroup(
                              ui.Item('add_plot', show_label=False),
                              ui.Item('apply_filters')),
                                    show_border=True,
                                    label='Plotting X axis with Y axis'),
                          ui.VGroup(ui.HGroup(
                              ui.Item('add_creep_plot', show_label=False),
                              ui.VGroup(ui.Item('normalize_cycles'),
                                        ui.Item('smooth'),
                                        ui.Item('plot_every_nth_point'))),
                                    show_border=True,
                                    label='Plotting Creep-fatigue of x-axis'),
                          ui.Item('plot_list'),
                          show_border=True,
                          label='Plotting'))),
        ui.VGroup(
            ui.Item('force_name'),
            ui.VGroup(ui.VGroup(
                ui.Item('window_length'),
                ui.Item('polynomial_order'),
                enabled_when='activate == True or smooth == True'),
                      show_border=True,
                      label='Smoothing parameters (Savitzky-Golay filter):'),
            ui.VGroup(ui.VGroup(
                ui.Item('activate'),
                ui.Item('peak_force_before_cycles',
                        enabled_when='activate == True')),
                      show_border=True,
                      label='Smooth ascending branch for all displacements:'),
            ui.VGroup(
                ui.Item('cutting_method'),
                ui.VGroup(ui.Item('force_max'),
                          ui.Item('force_min'),
                          label='Max, Min:',
                          show_border=True,
                          enabled_when='cutting_method == "Define Max, Min"'),
                ui.VGroup(
                    ui.Item('min_cycle_force_range'),
                    label='Min cycle force range:',
                    show_border=True,
                    enabled_when=
                    'cutting_method == "Define min cycle range(force difference)"'
                ),
                show_border=True,
                label='Cut fake cycles for creep:'),
            ui.Item('generate_filtered_and_creep_npy', show_label=False),
            show_border=True,
            label='Filters'),
        ui.UItem('figure',
                 editor=MPLFigureEditor(),
                 resizable=True,
                 springy=True,
                 width=0.8,
                 label='2d plots')),
                          title='HCFF Filter',
                          resizable=True,
                          width=0.85,
                          height=0.7)
Ejemplo n.º 6
0
class MATSXDEval(MATSEval):
    '''Base class for elastic models.
    '''

    n_dims = Constant(Float)
    '''Number of spatial dimensions of an integration 
    cell for the material model
    '''

    E = Float(34e+3,
              label="E",
              desc="Young's Modulus",
              auto_set=False,
              input=True)

    nu = Float(0.2,
               label='nu',
               desc="Poison's ratio",
               auto_set=False,
               input=True)

    def _get_lame_params(self):
        # First Lame parameter (bulk modulus)
        la = self.E * self.nu / ((1. + self.nu) * (1. - 2. * self.nu))
        # second Lame parameter (shear modulus)
        mu = self.E / (2. + 2. * self.nu)
        return la, mu

    D_abef = Property(Array, depends_on='+input')
    '''Material stiffness - rank 4 tensor
    '''

    @cached_property
    def _get_D_abef(self):
        la, mu = self._get_lame_params()
        delta = np.identity(self.n_dims)
        return (np.einsum(',ij,kl->ijkl', la, delta, delta) +
                np.einsum(',ik,jl->ijkl', mu, delta, delta) +
                np.einsum(',il,jk->ijkl', mu, delta, delta))

    def get_corr_pred(self, eps_Emab, tn1):
        '''
        Corrector predictor computation.
        @param eps_Emab input variable - strain tensor
        '''
        sigma_Emab = np.einsum('abcd,...cd->...ab', self.D_abef, eps_Emab)
        Em_len = len(eps_Emab.shape) - 2
        new_shape = tuple([1 for _ in range(Em_len)]) + self.D_abef.shape
        D_abef = self.D_abef.reshape(*new_shape)
        return sigma_Emab, D_abef

    #=========================================================================
    # Response variables
    #=========================================================================
    def get_eps_ab(self, eps_ab, tn1, **state):
        return eps_ab

    def get_sig_ab(self, eps_ab, tn1, **state):
        return self.get_sig(eps_ab, tn1, **state)

    var_dict = Property(Dict(Str, Callable))
    '''Dictionary of response variables
    '''

    @cached_property
    def _get_var_dict(self):
        return dict(eps_ab=self.get_eps_ab, sig_ab=self.get_sig_ab)

    def x_var_dict_default(self):
        return {
            'sig_app': self.get_sig_ab,
            'eps_app': self.get_eps_ab,
            'max_principle_sig': self.get_max_principle_sig,
            'strain_energy': self.get_strain_energy
        }

    view_traits = ui.View(ui.VSplit(ui.Group(
        ui.Item('E'),
        ui.Item('nu'),
    ), ),
                          resizable=True)
class Comparator(HasTraits):
    """ The main application.
    """

    #### Configuration traits ##################################################

    # The root directory of the test suite.
    suitedir = Str()

    # Mapping of SVG basenames to their reference PNGs. Use None if there is no
    # reference PNG.
    svg_png = Dict()

    # The list of SVG file names.
    svg_files = List()

    # The name of the default PNG file to display when no reference PNG exists.
    default_png = Str(os.path.join(this_dir, 'images/default.png'))

    #### State traits ##########################################################

    # The currently selected SVG file.
    current_file = Str()
    abs_current_file = Property(depends_on=['current_file'])

    # The current XML ElementTree root Element and its XMLTree view model.
    current_xml = Any()
    current_xml_view = Any()

    # The profilers.
    profile_this = Instance(ProfileThis, args=())

    #### GUI traits ############################################################

    # The text showing the current mouse coordinates over any of the components.
    mouse_coords = Property(Str, depends_on=['ch_controller.svg_coords'])

    # Move forward and backward through the list of SVG files.
    move_forward = Button('>>')
    move_backward = Button('<<')

    # The description of the test.
    description = HTML()

    document = Instance(document.SVGDocument)

    # The components to view.
    kiva_component = ComponentTrait(klass=SVGComponent)
    ref_component = ComponentTrait(klass=ImageComponent, args=())
    ch_controller = Instance(MultiController)

    # The profiler views.
    parsing_sike = Instance(Sike, args=())
    drawing_sike = Instance(Sike, args=())
    wx_doc_sike = Instance(Sike, args=())
    kiva_doc_sike = Instance(Sike, args=())

    traits_view = tui.View(
        tui.Tabbed(
            tui.VGroup(
                tui.HGroup(
                    tui.Item('current_file',
                             editor=tui.EnumEditor(name='svg_files'),
                             style='simple',
                             width=1.0,
                             show_label=False),
                    tui.Item(
                        'move_backward',
                        show_label=False,
                        enabled_when="svg_files.index(current_file) != 0"),
                    tui.Item(
                        'move_forward',
                        show_label=False,
                        enabled_when=
                        "svg_files.index(current_file) != len(svg_files)-1"),
                ),
                tui.VSplit(
                    tui.HSplit(
                        tui.Item('description',
                                 label='Description',
                                 show_label=False),
                        tui.Item('current_xml_view',
                                 editor=xml_tree_editor,
                                 show_label=False),
                    ),
                    tui.HSplit(
                        tui.Item('document',
                                 editor=SVGEditor(),
                                 show_label=False),
                        tui.Item('kiva_component', show_label=False),
                        tui.Item('ref_component', show_label=False),
                        # TODO: tui.Item('agg_component', show_label=False),
                    ),
                ),
                label='SVG',
            ),
            tui.Item('parsing_sike',
                     style='custom',
                     show_label=False,
                     label='Parsing Profile'),
            tui.Item('drawing_sike',
                     style='custom',
                     show_label=False,
                     label='Kiva Drawing Profile'),
            tui.Item('wx_doc_sike',
                     style='custom',
                     show_label=False,
                     label='Creating WX document'),
            tui.Item('kiva_doc_sike',
                     style='custom',
                     show_label=False,
                     label='Creating WX document'),
        ),
        width=1280,
        height=768,
        resizable=True,
        statusbar='mouse_coords',
        title='SVG Comparator',
    )

    def __init__(self, **traits):
        super(Comparator, self).__init__(**traits)
        kiva_ch = activate_tool(self.kiva_component,
                                Crosshair(self.kiva_component))
        ref_ch = activate_tool(self.ref_component,
                               Crosshair(self.ref_component))
        self.ch_controller = MultiController(kiva_ch, ref_ch)

    @classmethod
    def fromsuitedir(cls, dirname, **traits):
        """ Find all SVG files and their related reference PNG files under
        a directory.

        This assumes that the SVGs are located under <dirname>/svg/ and the
        related PNGs under <dirname>/png/ and that there are no subdirectories.
        """
        dirname = os.path.abspath(dirname)
        svgs = glob.glob(os.path.join(dirname, 'svg', '*.svg'))
        pngdir = os.path.join(dirname, 'png')
        d = {}
        for svg in svgs:
            png = None
            base = os.path.splitext(os.path.basename(svg))[0]
            for prefix in ('full-', 'basic-', 'tiny-', ''):
                fn = os.path.join(pngdir, prefix + base + '.png')
                if os.path.exists(fn):
                    png = os.path.basename(fn)
                    break
            d[os.path.basename(svg)] = png
        svgs = sorted(d)
        x = cls(suitedir=dirname, svg_png=d, svg_files=svgs, **traits)
        x.current_file = svgs[0]
        return x

    def display_reference_png(self, filename):
        """ Read the image file and shove its data into the display component.
        """
        img = Image.open(filename)
        arr = np.array(img)
        self.ref_component.image = arr

    def display_test_description(self):
        """ Extract the test description for display.
        """
        html = ET.Element('html')

        title = self.current_xml.find('.//{http://www.w3.org/2000/svg}title')
        if title is not None:
            title_text = title.text
        else:
            title_text = os.path.splitext(self.current_file)[0]
        p = ET.SubElement(html, 'p')
        b = ET.SubElement(p, 'b')
        b.text = 'Title: '
        b.tail = title_text

        desc_text = None
        version_text = None
        desc = self.current_xml.find('.//{http://www.w3.org/2000/svg}desc')
        if desc is not None:
            desc_text = desc.text
        else:
            testcase = self.current_xml.find(
                './/{http://www.w3.org/2000/02/svg/testsuite/description/}SVGTestCase'
            )
            if testcase is not None:
                desc_text = testcase.get('desc', None)
                version_text = testcase.get('version', None)
        if desc_text is not None:
            p = ET.SubElement(html, 'p')
            b = ET.SubElement(p, 'b')
            b.text = 'Description: '
            b.tail = normalize_text(desc_text)

        if version_text is None:
            script = self.current_xml.find(
                './/{http://www.w3.org/2000/02/svg/testsuite/description/}OperatorScript'
            )
            if script is not None:
                version_text = script.get('version', None)
        if version_text is not None:
            p = ET.SubElement(html, 'p')
            b = ET.SubElement(p, 'b')
            b.text = 'Version: '
            b.tail = version_text

        paras = self.current_xml.findall(
            './/{http://www.w3.org/2000/02/svg/testsuite/description/}Paragraph'
        )
        if len(paras) > 0:
            div = ET.SubElement(html, 'div')
            for para in paras:
                p = ET.SubElement(div, 'p')
                p.text = normalize_text(para.text)
                # Copy over any children elements like <a>.
                p[:] = para[:]

        tree = ET.ElementTree(html)
        f = StringIO()
        tree.write(f)
        text = f.getvalue()
        self.description = text

    def locate_file(self, name, kind):
        """ Find the location of the given file in the suite.

        Parameters
        ----------
        name : str
            Path of the file relative to the suitedir.
        kind : either 'svg' or 'png'
            The kind of file.

        Returns
        -------
        path : str
            The full path to the file.
        """
        return os.path.join(self.suitedir, kind, name)

    def _kiva_component_default(self):
        return SVGComponent(profile_this=self.profile_this)

    def _move_backward_fired(self):
        idx = self.svg_files.index(self.current_file)
        idx = max(idx - 1, 0)
        self.current_file = self.svg_files[idx]

    def _move_forward_fired(self):
        idx = self.svg_files.index(self.current_file)
        idx = min(idx + 1, len(self.svg_files) - 1)
        self.current_file = self.svg_files[idx]

    def _get_abs_current_file(self):
        return self.locate_file(self.current_file, 'svg')

    def _current_file_changed(self, new):
        # Reset the warnings filters. While it's good to only get 1 warning per
        # file, we want to get the same warning again if a new file issues it.
        warnings.resetwarnings()

        self.profile_this.start('Parsing')
        self.current_xml = ET.parse(self.abs_current_file).getroot()
        self.current_xml_view = xml_to_tree(self.current_xml)
        resources = document.ResourceGetter.fromfilename(self.abs_current_file)
        self.profile_this.stop()
        try:
            self.profile_this.start('Creating WX document')
            self.document = document.SVGDocument(self.current_xml,
                                                 resources=resources,
                                                 renderer=WxRenderer)
        except:
            logger.exception('Error parsing document %s', new)
            self.document = None

        self.profile_this.stop()

        try:
            self.profile_this.start('Creating Kiva document')
            self.kiva_component.document = document.SVGDocument(
                self.current_xml, resources=resources, renderer=KivaRenderer)
        except Exception as e:
            logger.exception('Error parsing document %s', new)
            self.kiva_component.document

        self.profile_this.stop()

        png_file = self.svg_png.get(new, None)
        if png_file is None:
            png_file = self.default_png
        else:
            png_file = self.locate_file(png_file, 'png')
        self.display_test_description()
        self.display_reference_png(png_file)

    def _get_mouse_coords(self):
        if self.ch_controller is None:
            return ''
        else:
            return '%1.3g %1.3g' % self.ch_controller.svg_coords

    @on_trait_change('profile_this:profile_ended')
    def _update_profiling(self, new):
        if new is not None:
            name, p = new
            stats = pstats.Stats(p)
            if name == 'Parsing':
                self.parsing_sike.stats = stats
            elif name == 'Drawing':
                self.drawing_sike.stats = stats
            elif name == 'Creating WX document':
                self.wx_doc_sike.stats = stats
            elif name == 'Creating Kiva document':
                self.kiva_doc_sike.stats = stats
Ejemplo n.º 8
0
class ExperimentSnake(traits.HasTraits):
    """Main Experiment Snake GUI that sends arbitrary actions based on the 
    experiment runner sequence and actions that have been set up."""

    #mainLog = utilities.TextDisplay()
    mainLog = outputStream.OutputStream()
    statusString = traits.String("Press Start Snake to begin...")
    isRunning = traits.Bool(False)  # true when the snake is running
    sequenceStarted = traits.Bool(
        False)  # flashes true for ~1ms when sequence starts
    queue = traits.Int(0)

    variables = traits.Dict(
        key_trait=traits.Str, value_trait=traits.Float
    )  #dictionary mapping variable names in Exp control to their values in this sequence
    timingEdges = traits.Dict(
        key_trait=traits.Str, value_trait=traits.Float
    )  #dictionary mapping timing Edge names in Exp control to their values in this sequence
    statusList = [
    ]  #eventually will contain the information gathered from experiment Runner each time we poll

    startAction = traitsui.Action(name='start',
                                  action='_startSnake',
                                  image=pyface.image_resource.ImageResource(
                                      os.path.join('icons', 'start.png')))
    stopAction = traitsui.Action(name='stop',
                                 action='_stopSnakeToolbar',
                                 image=pyface.image_resource.ImageResource(
                                     os.path.join('icons', 'stop.png')))
    reloadHWAsAction = traitsui.Action(
        name='reload',
        action='_reloadHWAsToolbar',
        image=pyface.image_resource.ImageResource(
            os.path.join('icons', 'reload.png')))

    connectionTimer = traits.Instance(
        Timer
    )  # polls the experiment runner and starts off callbacks at appropriate times
    statusStringTimer = traits.Instance(
        Timer)  #updates status bar at regular times (less freque)
    getCurrentTimer = traits.Instance(
        Timer
    )  #waits for get current to return which marks the beginning of a sequence

    getCurrentThread = traits.Instance(SocketThread)

    connectionPollFrequency = traits.Float(
        1000.0)  #milliseconds defines accuracy you will perform callbacks at
    statusStringFrequency = traits.Float(2000.0)  #milliseconds
    getCurrentFrequency = traits.Float(
        1000.0)  #milliseconds should be shorter than the sequence

    timeRunning = traits.Float(0.0)  #how long the sequence has been running
    totalTime = traits.Float(0.0)  # total length of sequence
    runnerHalted = traits.Bool(True)  # true if runner is halted
    haltedCount = 0
    progress = traits.Float(0.0)  # % of cycle complete
    #progressBar = ProgressDialog()
    hardwareActions = traits.List(hardwareAction.hardwareAction.HardwareAction)

    examineVariablesDictionary = traits.Instance(
        variableDictionary.ExamineVariablesDictionary)
    xmlString = ""  # STRING that will contain entire XML File

    menubar = traitsui.MenuBar(
        traitsui.Menu(
            traitsui.Action(name='Start Snake', action='_startSnake'),
            traitsui.Action(name='Stop Snake', action='_stopSnake'),
            traitsui.Action(name='Reload', action='_reloadHWAs'),
            traitsui.Menu(traitsui.Action(name='DEBUG',
                                          action='_changeLoggingLevelDebug'),
                          traitsui.Action(name='INFO',
                                          action='_changeLoggingLevelInfo'),
                          traitsui.Action(name='WARNING',
                                          action='_changeLoggingLevelWarning'),
                          traitsui.Action(name='ERROR',
                                          action='_changeLoggingLevelError'),
                          name="Log Level"),
            name='Menu'))

    toolbar = traitsui.ToolBar(startAction, stopAction, reloadHWAsAction)

    mainSnakeGroup = traitsui.VGroup(
        traitsui.Item('statusString', show_label=False, style='readonly'),
        traitsui.Item('mainLog',
                      show_label=False,
                      springy=True,
                      style='custom',
                      editor=traitsui.InstanceEditor()))

    hardwareActionsGroup = traitsui.Group(traitsui.Item(
        'hardwareActions',
        show_label=False,
        style='custom',
        editor=traitsui.ListEditor(style="custom")),
                                          label="Hardware Actions",
                                          show_border=True)

    variableExaminerGroup = traitsui.Group(traitsui.Item(
        "examineVariablesDictionary",
        editor=traitsui.InstanceEditor(),
        style="custom",
        show_label=False),
                                           label="Variable Examiner")

    sidePanelGroup = traitsui.VSplit(hardwareActionsGroup,
                                     variableExaminerGroup)

    traits_view = traitsui.View(traitsui.HSplit(sidePanelGroup,
                                                mainSnakeGroup,
                                                show_labels=True),
                                resizable=True,
                                menubar=menubar,
                                toolbar=toolbar,
                                width=0.5,
                                height=0.75,
                                title="Experiment Snake",
                                icon=pyface.image_resource.ImageResource(
                                    os.path.join('icons', 'snakeIcon.ico')))

    def __init__(self, **traits):
        """ takes no  arguments to construct the snake. Everything is done through GUI.
        Snake construction makes a ExperimentSnakeConnection object and writes to the 
        main log window"""

        super(ExperimentSnake, self).__init__(**traits)
        self.connection = experimentRunnerConnection.Connection(
        )  #can override default ports and IP
        self.hardwareActions = [
            hardwareAction.sequenceLoggerHWA.SequenceLogger(
                0.0, snakeReference=self),
            hardwareAction.experimentTablesHWA.ExperimentTables(
                0.0, snakeReference=self, enabled=False),
            hardwareAction.dlicEvapHWA.EvaporationRamp(1.0,
                                                       snakeReference=self),
            #hardwareAction.dlicRFSweepHWA.DLICRFSweep(1.0, snakeReference = self,enabled=False),
            hardwareAction.dlicRFSweepLZHWA.DLICRFSweep(1.0,
                                                        snakeReference=self,
                                                        enabled=False),
            hardwareAction.dlicRFSweepLZWithPowerCtrlHWA.DLICRFSweep(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.dlicRFSweepLZWithPowerCtrl13PreparationHWA.
            DLICRFSweep(1.0, snakeReference=self, enabled=True),
            hardwareAction.dlicPiPulseHWA.DLICPiPulse(1.0,
                                                      snakeReference=self,
                                                      enabled=False),
            hardwareAction.evapAttenuationHWA.EvapAttenuation(
                1.0, snakeReference=self),
            hardwareAction.greyMollassesOffsetFreqHWA.GreyMollassesOffset(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.evapAttenuation2HWA.EvapAttenuation(
                "EvapSnakeAttenuationTimeFinal",
                snakeReference=self,
                enabled=False),
            hardwareAction.picomotorPlugHWA.PicomotorPlug(1.0,
                                                          snakeReference=self,
                                                          enabled=False),
            hardwareAction.windFreakOffsetLockHWA.WindFreak(
                0.0, snakeReference=self, enabled=False),
            hardwareAction.windFreakOffsetLockHighFieldImagingHWA.WindFreak(
                0.0, snakeReference=self, enabled=True),
            hardwareAction.windFreakOffsetLock6ImagingHWA.WindFreak(
                2.0, snakeReference=self, enabled=False),
            hardwareAction.windFreak6To1HWA.WindFreak(2.0,
                                                      snakeReference=self,
                                                      enabled=False),
            hardwareAction.windFreakOffsetLockLaser3.WindFreak(
                3.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelNaZSFreq(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelNaZSAtten(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelNaZSEOMFreq(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelNaZSEOMAtten(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelNaSpecFreq(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelLiImaging(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelLiImagingDetuning(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelLiPushPulseAttenuation(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelLiPushPulseDetuning(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelNaDarkSpotAOMFreq(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelNaDarkSpotAOMAtten(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelNaMOTFreq(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelNaMOTAtten(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelNaMOTEOMAtten(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelNaImagingDP(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelLiMOTRep(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelLiMOTCool(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelLiOpticalPump(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelNa2to2OpticalPumpingFreq(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelNa2to2OpticalPumpingAtt(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelNaHighFieldImagingFreq(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.AOMChannelHWAs.AOMChannelNaHighFieldImagingAtt(
                1.0, snakeReference=self, enabled=False),
            hardwareAction.digitalMultimeterCurrentMeasureHWA.
            DigitalMultimeterMeasurement(1.0,
                                         snakeReference=self,
                                         enabled=True),
            hardwareAction.MXGPiPulseHWA.PiPulse(1.0,
                                                 snakeReference=self,
                                                 enabled=False),
            hardwareAction.variableExplorerHWA.VariableExplorer(
                2.0, snakeReference=self, enabled=False),
            hardwareAction.jds6600HWA.JDS6600HWA(1.0,
                                                 snakeReference=self,
                                                 enabled=False),
            hardwareAction.watchdogHWA.WatchdogHWA(18.0,
                                                   snakeReference=self,
                                                   enabled=True)
        ]
        introString = """Welcome to experiment snake."""

        self.mainLog.addLine(introString, 1)

    def initialiseHardwareActions(self):
        for hdwAct in self.hardwareActions:
            if hdwAct.enabled:
                returnString = hdwAct.init()
                hdwAct.variablesReference = self.variables
                self.mainLog.addLine(returnString)

    def closeHardwareActions(self):
        """ this function is called when the user presses stop key. it should cleanly close or 
        shutdown all hardware. user must appropriately implement the hardware action close function"""
        for hdwAct in self.hardwareActions:
            if hdwAct.initialised:
                returnString = hdwAct.close()
                self.mainLog.addLine(returnString)

    def _startSnake(self):
        """action call back from menu or toolbar. Simply starts the timer that
        polls the runner and makes the isRunning bool true  """
        self.mainLog.addLine("Experiment Snake Started", 1)
        self.isRunning = True
        self.getCurrentBlocking()
        self.initialiseHardwareActions()
        self.startTimers()

    def newSequenceStarted(self):
        """called by GetCurrent Thread at the beginning of every sequence """
        if self.isRunning:  #otherwise we have already stopped before new sequence began again
            self.getStatusUpdate()
            self.mainLog.addLine("New cycle started: %s" % self.statusList[0],
                                 1)
            self.refreshExamineVariablesDictionary(
            )  # update the examine variables dictionary to reflect the latest values
            self.refreshVariableDependentCallbackTimes(
            )  # if a callback time is a timing edge name or variable name we must pull the value here
        else:
            self.mainLog.addLine("final connection closed")
        for hdwAct in self.hardwareActions:
            hdwAct.awaitingCallback = True

    def _stopSnakeToolbar(self):
        """if snake is stopped, addLine to main log and then run stopSnake """
        self.mainLog.addLine(
            "Experiment Snake Stopped (you should still wait till the end of this sequence before continuing)",
            1)
        self._stopSnake()

    def _reloadHWAsToolbar(self):
        """if snake is stopped, addLine to main log and then run stopSnake """
        self.mainLog.addLine(
            "Experiment Snake Stopped (you should still wait till the end of this sequence before continuing)",
            1)
        self._reloadHWAs()

    def _reloadHWAs(self):
        """if snake is stopped, addLine to main log and then run stopSnake """
        self.mainLog.addLine("Reloading hardware actions (advanced feature)",
                             3)
        reload(hardwareAction.hardwareAction)
        reload(hardwareAction.sequenceLoggerHWA)
        reload(hardwareAction.dlicEvapHWA)
        reload(hardwareAction.dlicRFSweepHWA)
        reload(hardwareAction.dlicRFSweepHWA)
        reload(hardwareAction.evapAttenuationHWA)
        reload(hardwareAction.evapAttenuation2HWA)
        reload(hardwareAction.picomotorPlugHWA)
        reload(hardwareAction.windFreakOffsetLockHWA)
        #reload( hardwareAction.AOMChannelHWAs)#CAUSES REFERENCING PROBLEMS!
        reload(hardwareAction.experimentTablesHWA)
        reload(hardwareAction.windFreakOffsetLockHighFieldImagingHWA)
        reload(hardwareAction.greyMollassesOffsetFreqHWA)
        reload(hardwareAction.dlicRFSweepLZHWA)
        reload(hardwareAction.digitalMultimeterCurrentMeasureHWA)
        self.__init__()

    def stopTimers(self):
        """stops all timers with error catching """
        try:
            #stop any previous timer, should only have 1 timer at a time
            if self.connectionTimer is not None:
                self.connectionTimer.stop()
        except Exception as e:
            logger.error(
                "couldn't stop current timer before starting new one: %s" %
                e.message)
        try:
            #stop any previous timer, should only have 1 timer at a time
            if self.statusStringTimer is not None:
                self.statusStringTimer.stop()
        except Exception as e:
            logger.error(
                "couldn't stop current timer before starting new one: %s" %
                e.message)
        try:
            #stop any previous timer, should only have 1 timer at a time
            if self.getCurrentTimer is not None:
                self.getCurrentTimer.stop()
        except Exception as e:
            logger.error(
                "couldn't stop current timer before starting new one: %s" %
                e.message)

    def _stopSnake(self):
        """Simply stops the timers, shuts down hardware and sets isRunning bool false  """
        self.stopTimers()
        self.closeHardwareActions()
        self.isRunning = False

    def startTimers(self):
        """This timer object polls the experiment runner regularly polling at any time"""
        #stop any previous timers
        self.stopTimers()
        #start timer
        self.connectionTimer = Timer(self.connectionPollFrequency,
                                     self.getStatus)
        time.sleep(0.1)
        self.statusStringTimer = Timer(self.statusStringFrequency,
                                       self.updateStatusString)
        time.sleep(0.1)
        self.getCurrentTimer = Timer(self.getCurrentFrequency, self.getCurrent)
        """Menu action function to change logger level """
        logger.info("timers started")

    def getStatus(self):
        """calls the connection objects get status function and updates the statusList """
        logger.debug("starting getStatus")
        try:
            self.getStatusUpdate()
            self.checkForCallback()
        except Exception as e:
            logger.error("error in getStatus Function")
            logger.error("error: %s " % e.message)
            self.mainLog.addLine(
                "error in getStatus Function. Error: %s" % e.message, 4)

    def getStatusUpdate(self):
        """Calls get status and updates times """
        try:
            statusString = self.connection.getStatus()
        except socket.error as e:
            logger.error(
                "failed to get status . message=%s . errno=%s . errstring=%s "
                % (e.message, e.errno, e.strerror))
            self.mainLog.addLine(
                "Failed to get status from Experiment Runner. message=%s . errno=%s . errstring=%s"
                % (e.message, e.errno, e.strerror), 3)
            self.mainLog.addLine(
                "Cannot update timeRunning - callbacks could be wrong this sequence!",
                4)
            return
        self.statusList = statusString.split("\n")
        timeFormat = '%d/%m/%Y %H:%M:%S'
        timeBegin = datetime.datetime.strptime(self.statusList[2], timeFormat)
        timeCurrent = datetime.datetime.strptime(self.statusList[3],
                                                 timeFormat)
        self.timeRunning = (timeCurrent - timeBegin).total_seconds()
        logger.debug("time Running = %s " % self.timeRunning)

    def checkForCallback(self):
        """if we've received a sequence, we check through all callback times and
        send off a callback on a hardware action if appropriate"""
        try:
            for hdwAct in [
                    hdwA for hdwA in self.hardwareActions if hdwA.enabled
            ]:  #only iterate through enable hardware actions
                if hdwAct.awaitingCallback and self.timeRunning >= hdwAct.callbackTime:  #callback should be started!
                    try:
                        logger.debug("attempting to callback %s " %
                                     hdwAct.hardwareActionName)
                        hdwAct.setVariablesDictionary(self.variables)
                        logger.debug("vars dictionary set to %s " %
                                     self.variables)
                        callbackReturnString = hdwAct.callback()
                        self.mainLog.addLine(
                            "%s @ %s secs : %s" %
                            (hdwAct.hardwareActionName, self.timeRunning,
                             callbackReturnString), 2)
                        hdwAct.awaitingCallback = False
                        hdwAct.callbackCounter += 1
                    except Exception as e:
                        logger.error(
                            "error while performing callback on %s. see error message below"
                            % (hdwAct.hardwareActionName))
                        logger.error("error: %s " % e.message)
                        self.mainLog.addLine(
                            "error while performing callback on %s. Error: %s"
                            % (hdwAct.hardwareActionName, e.message), 4)
        except Exception as e:
            logger.error("error in checkForCallbackFunction")
            logger.error("error: %s " % e.message)
            self.mainLog.addLine(
                "error in checkForCallbackFunction. Error: %s" % e.message, 4)

    def getCurrent(self):
        """calls the connection objects get status function and updates the variables dictionary """
        if self.getCurrentThread and self.getCurrentThread.isAlive():
            #logger.debug( "getCurrent - already waiting - will not start new thread")
            #removed the above as it fills the log without any useful information
            self.sequenceStarted = False
            return
        else:
            logger.info("starting getCurrent Thread")
            self.getCurrentThread = SocketThread()
            self.getCurrentThread.snakeReference = self  # for calling functions of the snake
            self.getCurrentThread.start()

    def getCurrentBlocking(self):
        """calls getCurrent and won't return until XML parsed. unlike above threaded function
        This is useful when starting up the snake so that we don't start looking for hardware events
        until a sequence has started and we have received XML"""
        self.mainLog.addLine("Waiting for next sequence to start")
        self.xmlString = self.connection.getCurrent(
        )  # only returns at the beginning of a sequence! Experiment runner then returns the entire XML file
        logger.debug("length of xml string = %s " % len(self.xmlString))
        logger.debug("end of xml file is like [-30:]= %s" %
                     self.xmlString[-30:])
        try:
            root = ET.fromstring(self.xmlString)
            variables = root.find("variables")
            self.variables = {
                child[0].text: float(child[1].text)
                for child in variables
            }
            #timing edges dictionary : name--> value
            self.timingEdges = {
                timingEdge.find("name").text:
                float(timingEdge.find("value").text)
                for timingEdge in root.find("timing")
            }
            self.newSequenceStarted()
        except ET.ParseError as e:
            self.mainLog.addLine("Error. Could not parse XML: %s" % e.message,
                                 3)
            self.mainLog.addLine(
                "Possible cause is that buffer is full. is XML length %s>= limit %s ????"
                % (len(self.xmlString), self.connection.BUFFER_SIZE_XML), 3)
            logger.error("could not parse XML: %s " % self.xmlString)
            logger.error(e.message)

    def updateStatusString(self):
        """update the status string with first element of return of GETSTATUS. 
        similiar to experiment control and camera control. It also does the analysis
        of progress that doesn't need to be as accurate (e.g. progress bar)"""
        logger.info("starting update status string")
        self.statusString = self.statusList[
            0] + "- Time Running = %s " % self.timeRunning
        self.queue = int(self.statusList[1])
        timeFormat = '%d/%m/%Y %H:%M:%S'
        timeBegin = datetime.datetime.strptime(self.statusList[2], timeFormat)
        timeEnd = datetime.datetime.strptime(self.statusList[4], timeFormat)
        self.timeTotal = (timeEnd - timeBegin).total_seconds()
        if self.timeRunning > self.timeTotal:
            self.haltedCount += 1
            self.runnerHalted = True
            if self.haltedCount == 0:
                logger.critical("runner was stopped.")
                self.mainLog.addLine("Runner stopped!", 3)
                self.closeHardwareActions()
        else:
            if self.haltedCount > 0:
                self.initialiseHardwareActions()
            self.haltedCount = 0
            self.runnerHalted = False
        self.progress = 100.0 * self.timeRunning / self.timeTotal

    def _examineVariablesDictionary_default(self):
        if len(self.hardwareActions) > 0:
            logger.debug(
                "returning first hardware action %s for examineVariablesDictionary default"
                % self.hardwareActions[0].hardwareActionName)
            return variableDictionary.ExamineVariablesDictionary(
                hdwA=self.hardwareActions[0]
            )  #default is the first in the list
        else:
            logger.warning(
                "hardwareActions list was empty. how should I populate variable examiner...?!."
            )
            return None

    def updateExamineVariablesDictionary(self, hdwA):
        """Populates the examineVariablesDictionary Pane appropriately. It is passed the 
        hdwA so that it can find the necessary variables"""
        self.examineVariablesDictionary.hdwA = hdwA
        self.examineVariablesDictionary.hardwareActionName = hdwA.hardwareActionName
        self.examineVariablesDictionary.updateDisplayList()
        logger.critical("examineVariablesDictionary changed")

    def refreshExamineVariablesDictionary(self):
        """calls the updateDisplayList function of examineVariables Dictionary 
        this updates the values in the display list to the latest values in variables
        dictionary. useful for refereshing at the beginning of a sequence"""
        self.examineVariablesDictionary.updateDisplayList()
        logger.info("refreshed examine variables dictionary")

    def refreshVariableDependentCallbackTimes(self):
        """if a HWA is variable dependent call back time, we refresh the value 
        using this function. THis should be called in each sequence"""
        [
            hdwA.parseCallbackTime() for hdwA in self.hardwareActions
            if hdwA.callbackTimeVariableDependent
        ]

    def _changeLoggingLevelDebug(self):
        """Menu action function to change logger level """
        logger.setLevel(logging.DEBUG)

    def _changeLoggingLevelInfo(self):
        """Menu action function to change logger level """
        logger.setLevel(logging.INFO)

    def _changeLoggingLevelWarning(self):
        """Menu action function to change logger level """
        logger.setLevel(logging.WARNING)

    def _changeLoggingLevelError(self):
        """Menu action function to change logger level """
        logger.setLevel(logging.ERROR)
Ejemplo n.º 9
0
class HCFT(tr.HasStrictTraits):
    '''High-Cycle Fatigue Tool
    '''
    #=========================================================================
    # Traits definitions
    #=========================================================================
    decimal = tr.Enum(',', '.')
    delimiter = tr.Str(';')
    records_per_second = tr.Float(100)
    take_time_from_time_column = tr.Bool(True)
    file_csv = tr.File
    open_file_csv = tr.Button('Input file')
    skip_first_rows = tr.Range(low=1, high=10**9, mode='spinner')
    columns_headers_list = tr.List([])
    x_axis = tr.Enum(values='columns_headers_list')
    y_axis = tr.Enum(values='columns_headers_list')
    force_column = tr.Enum(values='columns_headers_list')
    time_column = tr.Enum(values='columns_headers_list')
    x_axis_multiplier = tr.Enum(1, -1)
    y_axis_multiplier = tr.Enum(-1, 1)
    npy_folder_path = tr.Str
    file_name = tr.Str
    apply_filters = tr.Bool
    plot_settings_btn = tr.Button
    plot_settings = PlotSettings()
    plot_settings_active = tr.Bool
    normalize_cycles = tr.Bool
    smooth = tr.Bool
    plot_every_nth_point = tr.Range(low=1, high=1000000, mode='spinner')
    old_peak_force_before_cycles = tr.Float
    peak_force_before_cycles = tr.Float
    window_length = tr.Range(low=1, high=10**9 - 1, value=31, mode='spinner')
    polynomial_order = tr.Range(low=1, high=10**9, value=2, mode='spinner')
    activate = tr.Bool(False)
    add_plot = tr.Button
    add_creep_plot = tr.Button(desc='Creep plot of X axis array')
    clear_plot = tr.Button
    parse_csv_to_npy = tr.Button
    generate_filtered_and_creep_npy = tr.Button
    add_columns_average = tr.Button
    force_max = tr.Float(100)
    force_min = tr.Float(40)
    min_cycle_force_range = tr.Float(50)
    cutting_method = tr.Enum(
        'Define min cycle range(force difference)', 'Define Max, Min')
    columns_to_be_averaged = tr.List
    figure = tr.Instance(mpl.figure.Figure)
    log = tr.Str('')
    clear_log = tr.Button

    def _figure_default(self):
        figure = mpl.figure.Figure(facecolor='white')
        figure.set_tight_layout(True)
        return figure

    #=========================================================================
    # File management
    #=========================================================================

    def _open_file_csv_fired(self):
        try:

            self.reset()

            """ Handles the user clicking the 'Open...' button.
            """
            extns = ['*.csv', ]  # seems to handle only one extension...
            wildcard = '|'.join(extns)

            dialog = FileDialog(title='Select text file',
                                action='open', wildcard=wildcard,
                                default_path=self.file_csv)

            result = dialog.open()

            """ Test if the user opened a file to avoid throwing an exception if he 
            doesn't """
            if result == OK:
                self.file_csv = dialog.path
            else:
                return

            """ Filling x_axis and y_axis with values """
            headers_array = np.array(
                pd.read_csv(
                    self.file_csv, delimiter=self.delimiter, decimal=self.decimal,
                    nrows=1, header=None
                )
            )[0]
            for i in range(len(headers_array)):
                headers_array[i] = self.get_valid_file_name(headers_array[i])
            self.columns_headers_list = list(headers_array)

            """ Saving file name and path and creating NPY folder """
            dir_path = os.path.dirname(self.file_csv)
            self.npy_folder_path = os.path.join(dir_path, 'NPY')
            if os.path.exists(self.npy_folder_path) == False:
                os.makedirs(self.npy_folder_path)

            self.file_name = os.path.splitext(
                os.path.basename(self.file_csv))[0]

        except Exception as e:
            self.deal_with_exception(e)

    def _parse_csv_to_npy_fired(self):
        # Run method on different thread so GUI doesn't freeze
        #thread = Thread(target = threaded_function, function_args = (10,))
        thread = Thread(target=self.parse_csv_to_npy_fired)
        thread.start()

    def parse_csv_to_npy_fired(self):
        try:
            self.print_custom('Parsing csv into npy files...')

            for i in range(len(self.columns_headers_list) -
                           len(self.columns_to_be_averaged)):
                current_column_name = self.columns_headers_list[i]
                column_array = np.array(pd.read_csv(
                    self.file_csv, delimiter=self.delimiter, decimal=self.decimal,
                    skiprows=self.skip_first_rows, usecols=[i]))

                if current_column_name == self.time_column and \
                        self.take_time_from_time_column == False:
                    column_array = np.arange(start=0.0,
                                             stop=len(column_array) /
                                             self.records_per_second,
                                             step=1.0 / self.records_per_second)

                np.save(os.path.join(self.npy_folder_path, self.file_name +
                                     '_' + current_column_name + '.npy'),
                        column_array)

            """ Exporting npy arrays of averaged columns """
            for columns_names in self.columns_to_be_averaged:
                temp = np.zeros((1))
                for column_name in columns_names:
                    temp = temp + np.load(os.path.join(self.npy_folder_path,
                                                       self.file_name +
                                                       '_' + column_name +
                                                       '.npy')).flatten()
                avg = temp / len(columns_names)

                avg_file_suffex = self.get_suffex_for_columns_to_be_averaged(
                    columns_names)
                np.save(os.path.join(self.npy_folder_path, self.file_name +
                                     '_' + avg_file_suffex + '.npy'), avg)

            self.print_custom('Finsihed parsing csv into npy files.')
        except Exception as e:
            self.deal_with_exception(e)

    def get_suffex_for_columns_to_be_averaged(self, columns_names):
        suffex_for_saved_file_name = 'avg_' + '_'.join(columns_names)
        return suffex_for_saved_file_name

    def get_valid_file_name(self, original_file_name):
        valid_chars = "-_.() %s%s" % (string.ascii_letters, string.digits)
        new_valid_file_name = ''.join(
            c for c in original_file_name if c in valid_chars)
        return new_valid_file_name

    def _clear_plot_fired(self):
        self.figure.clear()
        self.data_changed = True

    def _add_columns_average_fired(self):
        try:
            columns_average = ColumnsAverage()
            for name in self.columns_headers_list:
                columns_average.columns.append(Column(column_name=name))

            # kind='modal' pauses the implementation until the window is closed
            columns_average.configure_traits(kind='modal')

            columns_to_be_averaged_temp = []
            for i in columns_average.columns:
                if i.selected:
                    columns_to_be_averaged_temp.append(i.column_name)

            if columns_to_be_averaged_temp:  # If it's not empty
                self.columns_to_be_averaged.append(columns_to_be_averaged_temp)

                avg_file_suffex = self.get_suffex_for_columns_to_be_averaged(
                    columns_to_be_averaged_temp)
                self.columns_headers_list.append(avg_file_suffex)
        except Exception as e:
            self.deal_with_exception(e)

    def _generate_filtered_and_creep_npy_fired(self):
        # Run method on different thread so GUI doesn't freeze
        #thread = Thread(target = threaded_function, function_args = (10,))
        thread = Thread(target=self.generate_filtered_and_creep_npy_fired)
        thread.start()

    def generate_filtered_and_creep_npy_fired(self):
        try:
            if self.npy_files_exist(os.path.join(
                    self.npy_folder_path, self.file_name + '_' + self.force_column
                    + '.npy')) == False:
                return

            self.print_custom('Generating filtered and creep files...')

            # 1- Export filtered force
            force = np.load(os.path.join(self.npy_folder_path,
                                         self.file_name + '_' + self.force_column
                                         + '.npy')).flatten()
            peak_force_before_cycles_index = np.where(
                abs((force)) > abs(self.peak_force_before_cycles))[0][0]
            force_ascending = force[0:peak_force_before_cycles_index]
            force_rest = force[peak_force_before_cycles_index:]

            force_max_indices, force_min_indices = self.get_array_max_and_min_indices(
                force_rest)

            force_max_min_indices = np.concatenate(
                (force_min_indices, force_max_indices))
            force_max_min_indices.sort()

            force_rest_filtered = force_rest[force_max_min_indices]
            force_filtered = np.concatenate(
                (force_ascending, force_rest_filtered))
            np.save(os.path.join(self.npy_folder_path, self.file_name +
                                 '_' + self.force_column + '_filtered.npy'),
                    force_filtered)

            # 2- Export filtered displacements
            for i in range(0, len(self.columns_headers_list)):
                if self.columns_headers_list[i] != self.force_column and \
                        self.columns_headers_list[i] != self.time_column:

                    disp = np.load(os.path.join(self.npy_folder_path, self.file_name
                                                + '_' +
                                                self.columns_headers_list[i]
                                                + '.npy')).flatten()
                    disp_ascending = disp[0:peak_force_before_cycles_index]
                    disp_rest = disp[peak_force_before_cycles_index:]

                    if self.activate == True:
                        disp_ascending = savgol_filter(
                            disp_ascending, window_length=self.window_length,
                            polyorder=self.polynomial_order)

                    disp_rest_filtered = disp_rest[force_max_min_indices]
                    filtered_disp = np.concatenate(
                        (disp_ascending, disp_rest_filtered))
                    np.save(os.path.join(self.npy_folder_path, self.file_name + '_'
                                         + self.columns_headers_list[i] +
                                         '_filtered.npy'), filtered_disp)

            # 3- Export creep for displacements
            # Cutting unwanted max min values to get correct full cycles and remove
            # false min/max values caused by noise
            if self.cutting_method == "Define Max, Min":
                force_max_indices_cutted, force_min_indices_cutted = \
                    self.cut_indices_of_min_max_range(force_rest,
                                                      force_max_indices,
                                                      force_min_indices,
                                                      self.force_max,
                                                      self.force_min)
            elif self.cutting_method == "Define min cycle range(force difference)":
                force_max_indices_cutted, force_min_indices_cutted = \
                    self.cut_indices_of_defined_range(force_rest,
                                                      force_max_indices,
                                                      force_min_indices,
                                                      self.min_cycle_force_range)

            self.print_custom("Cycles number= ", len(force_min_indices))
            self.print_custom("Cycles number after cutting fake cycles = ",
                              len(force_min_indices_cutted))

            for i in range(0, len(self.columns_headers_list)):
                if self.columns_headers_list[i] != self.time_column:
                    array = np.load(os.path.join(self.npy_folder_path, self.file_name +
                                                 '_' +
                                                 self.columns_headers_list[i]
                                                 + '.npy')).flatten()
                    array_rest = array[peak_force_before_cycles_index:]
                    array_rest_maxima = array_rest[force_max_indices_cutted]
                    array_rest_minima = array_rest[force_min_indices_cutted]
                    np.save(os.path.join(self.npy_folder_path, self.file_name + '_' +
                                         self.columns_headers_list[i] + '_max.npy'), array_rest_maxima)
                    np.save(os.path.join(self.npy_folder_path, self.file_name + '_' +
                                         self.columns_headers_list[i] + '_min.npy'), array_rest_minima)

            self.print_custom('Filtered and creep npy files are generated.')
        except Exception as e:
            self.deal_with_exception(e)

    def cut_indices_of_min_max_range(self, array, max_indices, min_indices,
                                     range_upper_value, range_lower_value):
        cutted_max_indices = []
        cutted_min_indices = []

        for max_index in max_indices:
            if abs(array[max_index]) > abs(range_upper_value):
                cutted_max_indices.append(max_index)
        for min_index in min_indices:
            if abs(array[min_index]) < abs(range_lower_value):
                cutted_min_indices.append(min_index)
        return cutted_max_indices, cutted_min_indices

    def cut_indices_of_defined_range(self, array, max_indices, min_indices, range_):
        cutted_max_indices = []
        cutted_min_indices = []

        for max_index, min_index in zip(max_indices, min_indices):
            if abs(array[max_index] - array[min_index]) > range_:
                cutted_max_indices.append(max_index)
                cutted_min_indices.append(min_index)

        if max_indices.size > min_indices.size:
            cutted_max_indices.append(max_indices[-1])
        elif min_indices.size > max_indices.size:
            cutted_min_indices.append(min_indices[-1])

        return cutted_max_indices, cutted_min_indices

    def get_array_max_and_min_indices(self, input_array):

        # Checking dominant sign
        positive_values_count = np.sum(np.array(input_array) >= 0)
        negative_values_count = input_array.size - positive_values_count

        # Getting max and min indices
        if (positive_values_count > negative_values_count):
            force_max_indices = self.get_max_indices(input_array)
            force_min_indices = self.get_min_indices(input_array)
        else:
            force_max_indices = self.get_min_indices(input_array)
            force_min_indices = self.get_max_indices(input_array)

        return force_max_indices, force_min_indices

    def get_max_indices(self, a):
        # This method doesn't qualify first and last elements as max
        max_indices = []
        i = 1
        while i < a.size - 1:
            previous_element = a[i - 1]

            # Skip repeated elements and record previous element value
            first_repeated_element = True

            while a[i] == a[i + 1] and i < a.size - 1:
                if first_repeated_element:
                    previous_element = a[i - 1]
                    first_repeated_element = False
                if i < a.size - 2:
                    i += 1
                else:
                    break

            if a[i] > a[i + 1] and a[i] > previous_element:
                max_indices.append(i)
            i += 1
        return np.array(max_indices)

    def get_min_indices(self, a):
        # This method doesn't qualify first and last elements as min
        min_indices = []
        i = 1
        while i < a.size - 1:
            previous_element = a[i - 1]

            # Skip repeated elements and record previous element value
            first_repeated_element = True
            while a[i] == a[i + 1]:
                if first_repeated_element:
                    previous_element = a[i - 1]
                    first_repeated_element = False
                if i < a.size - 2:
                    i += 1
                else:
                    break

            if a[i] < a[i + 1] and a[i] < previous_element:
                min_indices.append(i)
            i += 1
        return np.array(min_indices)

    def _activate_changed(self):
        if self.activate == False:
            self.old_peak_force_before_cycles = self.peak_force_before_cycles
            self.peak_force_before_cycles = 0
        else:
            self.peak_force_before_cycles = self.old_peak_force_before_cycles

    def _window_length_changed(self, new):

        if new <= self.polynomial_order:
            dialog = MessageDialog(
                title='Attention!',
                message='Window length must be bigger than polynomial order.')
            dialog.open()

        if new % 2 == 0 or new <= 0:
            dialog = MessageDialog(
                title='Attention!',
                message='Window length must be odd positive integer.')
            dialog.open()

    def _polynomial_order_changed(self, new):
        if new >= self.window_length:
            dialog = MessageDialog(
                title='Attention!',
                message='Polynomial order must be smaller than window length.')
            dialog.open()

    #=========================================================================
    # Plotting
    #=========================================================================

    def _plot_settings_btn_fired(self):
        try:
            self.plot_settings.configure_traits(kind='modal')
        except Exception as e:
            self.deal_with_exception(e)

    def npy_files_exist(self, path):
        if os.path.exists(path) == True:
            return True
        else:
            # TODO fix this
            self.print_custom(
                'Please parse csv file to generate npy files first.')
#             dialog = MessageDialog(
#                 title='Attention!',
#                 message='Please parse csv file to generate npy files first.')
#             dialog.open()
            return False

    def filtered_and_creep_npy_files_exist(self, path):
        if os.path.exists(path) == True:
            return True
        else:
            # TODO fix this
            self.print_custom(
                'Please generate filtered and creep npy files first.')
#             dialog = MessageDialog(
#                 title='Attention!',
#                 message='Please generate filtered and creep npy files first.')
#             dialog.open()
            return False

    data_changed = tr.Event

    def _add_plot_fired(self):
        # Run method on different thread so GUI doesn't freeze
        #thread = Thread(target = threaded_function, function_args = (10,))
        thread = Thread(target=self.add_plot_fired)
        thread.start()

    def add_plot_fired(self):
        try:
            if self.apply_filters:
                if self.filtered_and_creep_npy_files_exist(os.path.join(
                        self.npy_folder_path, self.file_name + '_' + self.x_axis
                        + '_filtered.npy')) == False:
                    return
                x_axis_name = self.x_axis + '_filtered'
                y_axis_name = self.y_axis + '_filtered'
                self.print_custom('Loading npy files...')
                # when mmap_mode!=None, the array will be loaded as 'numpy.memmap'
                # object which doesn't load the array to memory until it's
                # indexed
                x_axis_array = np.load(os.path.join(self.npy_folder_path,
                                                    self.file_name + '_' + self.x_axis
                                                    + '_filtered.npy'), mmap_mode='r')
                y_axis_array = np.load(os.path.join(self.npy_folder_path,
                                                    self.file_name + '_' + self.y_axis
                                                    + '_filtered.npy'), mmap_mode='r')
            else:
                if self.npy_files_exist(os.path.join(
                        self.npy_folder_path, self.file_name + '_' + self.x_axis
                        + '.npy')) == False:
                    return

                x_axis_name = self.x_axis
                y_axis_name = self.y_axis
                self.print_custom('Loading npy files...')
                # when mmap_mode!=None, the array will be loaded as 'numpy.memmap'
                # object which doesn't load the array to memory until it's
                # indexed
                x_axis_array = np.load(os.path.join(self.npy_folder_path,
                                                    self.file_name + '_' + self.x_axis
                                                    + '.npy'), mmap_mode='r')
                y_axis_array = np.load(os.path.join(self.npy_folder_path,
                                                    self.file_name + '_' + self.y_axis
                                                    + '.npy'), mmap_mode='r')

            if self.plot_settings_active:
                print(self.plot_settings.first_rows)
                print(self.plot_settings.distance)
                print(self.plot_settings.num_of_rows_after_each_distance)
                print(np.size(x_axis_array))
                indices = self.get_indices_array(np.size(x_axis_array),
                                                 self.plot_settings.first_rows,
                                                 self.plot_settings.distance,
                                                 self.plot_settings.num_of_rows_after_each_distance)
                x_axis_array = self.x_axis_multiplier * x_axis_array[indices]
                y_axis_array = self.y_axis_multiplier * y_axis_array[indices]
            else:
                x_axis_array = self.x_axis_multiplier * x_axis_array
                y_axis_array = self.y_axis_multiplier * y_axis_array

            self.print_custom('Adding Plot...')
            mpl.rcParams['agg.path.chunksize'] = 10000

            ax = self.figure.add_subplot(1, 1, 1)

            ax.set_xlabel(x_axis_name)
            ax.set_ylabel(y_axis_name)
            ax.plot(x_axis_array, y_axis_array, 'k',
                    linewidth=1.2, color=np.random.rand(3), label=self.file_name +
                    ', ' + x_axis_name)

            ax.legend()
            self.data_changed = True
            self.print_custom('Finished adding plot.')

        except Exception as e:
            self.deal_with_exception(e)

    def _add_creep_plot_fired(self):
        # Run method on different thread so GUI doesn't freeze
        #thread = Thread(target = threaded_function, function_args = (10,))
        thread = Thread(target=self.add_creep_plot_fired)
        thread.start()

    def add_creep_plot_fired(self):
        try:
            if self.filtered_and_creep_npy_files_exist(os.path.join(
                    self.npy_folder_path, self.file_name + '_' + self.x_axis
                    + '_max.npy')) == False:
                return

            self.print_custom('Loading npy files...')
            disp_max = self.x_axis_multiplier * \
                np.load(os.path.join(self.npy_folder_path,
                                     self.file_name + '_' + self.x_axis + '_max.npy'))
            disp_min = self.x_axis_multiplier * \
                np.load(os.path.join(self.npy_folder_path,
                                     self.file_name + '_' + self.x_axis + '_min.npy'))
            complete_cycles_number = disp_max.size

            self.print_custom('Adding creep-fatigue plot...')
            mpl.rcParams['agg.path.chunksize'] = 10000

            ax = self.figure.add_subplot(1, 1, 1)

            ax.set_xlabel('Cycles number')
            ax.set_ylabel(self.x_axis)

            if self.plot_every_nth_point > 1:
                disp_max = disp_max[0::self.plot_every_nth_point]
                disp_min = disp_min[0::self.plot_every_nth_point]

            if self.smooth:
                # Keeping the first item of the array and filtering the rest
                disp_max = np.concatenate((
                    np.array([disp_max[0]]),
                    savgol_filter(disp_max[1:],
                                  window_length=self.window_length,
                                  polyorder=self.polynomial_order)
                ))
                disp_min = np.concatenate((
                    np.array([disp_min[0]]),
                    savgol_filter(disp_min[1:],
                                  window_length=self.window_length,
                                  polyorder=self.polynomial_order)
                ))

            if self.normalize_cycles:
                ax.plot(np.linspace(0, 1., disp_max.size), disp_max,
                        'k', linewidth=1.2, color=np.random.rand(3), label='Max'
                        + ', ' + self.file_name + ', ' + self.x_axis)
                ax.plot(np.linspace(0, 1., disp_min.size), disp_min,
                        'k', linewidth=1.2, color=np.random.rand(3), label='Min'
                        + ', ' + self.file_name + ', ' + self.x_axis)
            else:
                ax.plot(np.linspace(0, complete_cycles_number,
                                    disp_max.size), disp_max,
                        'k', linewidth=1.2, color=np.random.rand(3), label='Max'
                        + ', ' + self.file_name + ', ' + self.x_axis)
                ax.plot(np.linspace(0, complete_cycles_number,
                                    disp_min.size), disp_min,
                        'k', linewidth=1.2, color=np.random.rand(3), label='Min'
                        + ', ' + self.file_name + ', ' + self.x_axis)

            ax.legend()
            self.data_changed = True
            self.print_custom('Finished adding creep-fatigue plot.')

        except Exception as e:
            self.deal_with_exception(e)

    def get_indices_array(self,
                          array_size,
                          first_rows,
                          distance,
                          num_of_rows_after_each_distance):
        result_1 = np.arange(first_rows)
        result_2 = np.arange(start=first_rows, stop=array_size,
                             step=distance + num_of_rows_after_each_distance)
        result_2_updated = np.array([], dtype=np.int_)

        for result_2_value in result_2:
            data_slice = np.arange(result_2_value, result_2_value +
                                   num_of_rows_after_each_distance)
            result_2_updated = np.concatenate((result_2_updated, data_slice))

        result = np.concatenate((result_1, result_2_updated))
        return result

    def reset(self):
        self.columns_to_be_averaged = []
        self.log = ''

    def print_custom(self, *input_args):
        print(*input_args)
        if self.log == '':
            self.log = ''.join(str(e) for e in list(input_args))
        else:
            self.log = self.log + '\n' + \
                ''.join(str(e) for e in list(input_args))

    def deal_with_exception(self, e):
        self.print_custom('SOMETHING WENT WRONG!')
        self.print_custom('--------- Error message: ---------')
        self.print_custom(traceback.format_exc())
        self.print_custom('----------------------------------')

    def _clear_log_fired(self):
        self.log = ''

    #=========================================================================
    # Configuration of the view
    #=========================================================================
    traits_view = ui.View(
        ui.HSplit(
            ui.VSplit(
                ui.VGroup(
                    ui.VGroup(
                        ui.Item('decimal'),
                        ui.Item('delimiter'),
                        ui.HGroup(
                            ui.UItem('open_file_csv', has_focus=True),
                            ui.UItem('file_csv', style='readonly', width=0.1)),
                        label='Importing csv file',
                        show_border=True)),
                ui.VGroup(
                    ui.VGroup(
                        ui.VGroup(
                            ui.Item('take_time_from_time_column'),
                            ui.Item('time_column',
                                    enabled_when='take_time_from_time_column == True'),
                            ui.Item('records_per_second',
                                    enabled_when='take_time_from_time_column == False'),
                            label='Time calculation',
                            show_border=True),
                        ui.UItem('add_columns_average'),
                        ui.Item('skip_first_rows'),
                        ui.UItem('parse_csv_to_npy', resizable=True),
                        label='Processing csv file',
                        show_border=True)),
                ui.VGroup(
                    ui.VGroup(
                        ui.HGroup(ui.Item('x_axis'), ui.Item(
                            'x_axis_multiplier')),
                        ui.HGroup(ui.Item('y_axis'), ui.Item(
                            'y_axis_multiplier')),
                        ui.VGroup(
                            ui.HGroup(ui.UItem('add_plot'),
                                      ui.Item('apply_filters'),
                                      ui.Item('plot_settings_btn',
                                              label='Settings',
                                              show_label=False,
                                              enabled_when='plot_settings_active == True'),
                                      ui.Item('plot_settings_active',
                                              show_label=False)
                                      ),
                            show_border=True,
                            label='Plotting X axis with Y axis'
                        ),
                        ui.VGroup(
                            ui.HGroup(ui.UItem('add_creep_plot'),
                                      ui.VGroup(
                                          ui.Item('normalize_cycles'),
                                          ui.Item('smooth'),
                                          ui.Item('plot_every_nth_point'))
                                      ),
                            show_border=True,
                            label='Plotting Creep-fatigue of X axis variable'
                        ),
                        ui.UItem('clear_plot', resizable=True),
                        show_border=True,
                        label='Plotting'))
            ),
            ui.VGroup(
                ui.Item('force_column'),
                ui.VGroup(ui.VGroup(
                    ui.Item('window_length'),
                    ui.Item('polynomial_order'),
                    enabled_when='activate == True or smooth == True'),
                    show_border=True,
                    label='Smoothing parameters (Savitzky-Golay filter):'
                ),
                ui.VGroup(ui.VGroup(
                    ui.Item('activate'),
                    ui.Item('peak_force_before_cycles',
                            enabled_when='activate == True')
                ),
                    show_border=True,
                    label='Smooth ascending branch for all displacements:'
                ),
                ui.VGroup(ui.Item('cutting_method'),
                          ui.VGroup(ui.Item('force_max'),
                                    ui.Item('force_min'),
                                    label='Max, Min:',
                                    show_border=True,
                                    enabled_when='cutting_method == "Define Max, Min"'),
                          ui.VGroup(ui.Item('min_cycle_force_range'),
                                    label='Min cycle force range:',
                                    show_border=True,
                                    enabled_when='cutting_method == "Define min cycle range(force difference)"'),
                          show_border=True,
                          label='Cut fake cycles for creep:'),

                ui.VSplit(
                    ui.UItem('generate_filtered_and_creep_npy'),
                    ui.VGroup(
                        ui.Item('log',
                                width=0.1, style='custom'),
                        ui.UItem('clear_log'))),
                show_border=True,
                label='Filters'
            ),
            ui.UItem('figure', editor=MPLFigureEditor(),
                     resizable=True,
                     springy=True,
                     width=0.8,
                     label='2d plots')
        ),
        title='High-cycle fatigue tool',
        resizable=True,
        width=0.85,
        height=0.7
    )
Ejemplo n.º 10
0
class HCFF(tr.HasStrictTraits):
    '''High-Cycle Fatigue Filter
    '''

    something = tr.Instance(Something)
    decimal = tr.Enum(',', '.')
    delimiter = tr.Str(';')

    path_hdf5 = tr.Str('')

    def _something_default(self):
        return Something()

    #=========================================================================
    # File management
    #=========================================================================
    file_csv = tr.File

    open_file_csv = tr.Button('Input file')

    def _open_file_csv_fired(self):
        """ Handles the user clicking the 'Open...' button.
        """
        extns = [
            '*.csv',
        ]  # seems to handle only one extension...
        wildcard = '|'.join(extns)

        dialog = FileDialog(title='Select text file',
                            action='open',
                            wildcard=wildcard,
                            default_path=self.file_csv)
        dialog.open()
        self.file_csv = dialog.path
        """ Filling x_axis and y_axis with values """
        headers_array = np.array(
            pd.read_csv(self.file_csv,
                        delimiter=self.delimiter,
                        decimal=self.decimal,
                        nrows=1,
                        header=None))[0]
        for i in range(len(headers_array)):
            headers_array[i] = self.get_valid_file_name(headers_array[i])
        self.columns_headers_list = list(headers_array)

    #=========================================================================
    # Parameters of the filter algorithm
    #=========================================================================

    chunk_size = tr.Int(10000, auto_set=False, enter_set=True)

    skip_rows = tr.Int(4, auto_set=False, enter_set=True)

    # 1) use the decorator
    @tr.on_trait_change('chunk_size, skip_rows')
    def whatever_name_size_changed(self):
        print('chunk-size changed')

    # 2) use the _changed or _fired extension
    def _chunk_size_changed(self):
        print('chunk_size changed - calling the named function')

    data = tr.Array(dtype=np.float_)

    read_loadtxt_button = tr.Button()

    def _read_loadtxt_button_fired(self):
        self.data = np.loadtxt(self.file_csv,
                               skiprows=self.skip_rows,
                               delimiter=self.delimiter)
        print(self.data.shape)

    read_csv_button = tr.Button
    read_hdf5_button = tr.Button

    def _read_csv_button_fired(self):
        self.read_csv()

    def _read_hdf5_button_fired(self):
        self.read_hdf5_no_filter()

    def read_csv(self):
        '''Read the csv file and transform it to the hdf5 format.
        The output file has the same name as the input csv file
        with an extension hdf5
        '''
        path_csv = self.file_csv
        # Following splitext splits the path into a pair (root, extension)
        self.path_hdf5 = os.path.splitext(path_csv)[0] + '.hdf5'

        for i, chunk in enumerate(
                pd.read_csv(path_csv,
                            delimiter=self.delimiter,
                            decimal=self.decimal,
                            skiprows=self.skip_rows,
                            chunksize=self.chunk_size)):
            chunk_array = np.array(chunk)
            chunk_data_frame = pd.DataFrame(
                chunk_array, columns=['a', 'b', 'c', 'd', 'e', 'f'])
            if i == 0:
                chunk_data_frame.to_hdf(self.path_hdf5,
                                        'all_data',
                                        mode='w',
                                        format='table')
            else:
                chunk_data_frame.to_hdf(self.path_hdf5,
                                        'all_data',
                                        append=True)

    def read_hdf5_no_filter(self):

        # reading hdf files is really memory-expensive!
        force = np.array(pd.read_hdf(self.path_hdf5, columns=['b']))
        weg = np.array(pd.read_hdf(self.path_hdf5, columns=['c']))
        disp1 = np.array(pd.read_hdf(self.path_hdf5, columns=['d']))
        disp2 = np.array(pd.read_hdf(self.path_hdf5, columns=['e']))
        disp3 = np.array(pd.read_hdf(self.path_hdf5, columns=['f']))

        force = np.concatenate((np.zeros((1, 1)), force))
        weg = np.concatenate((np.zeros((1, 1)), weg))
        disp1 = np.concatenate((np.zeros((1, 1)), disp1))
        disp2 = np.concatenate((np.zeros((1, 1)), disp2))
        disp3 = np.concatenate((np.zeros((1, 1)), disp3))

        dir_path = os.path.dirname(self.file_csv)
        npy_folder_path = os.path.join(dir_path, 'NPY')
        if os.path.exists(npy_folder_path) == False:
            os.makedirs(npy_folder_path)

        file_name = os.path.splitext(os.path.basename(self.file_csv))[0]

        np.save(
            os.path.join(npy_folder_path, file_name + '_Force_nofilter.npy'),
            force)
        np.save(
            os.path.join(npy_folder_path,
                         file_name + '_Displacement_machine_nofilter.npy'),
            weg)
        np.save(
            os.path.join(npy_folder_path,
                         file_name + '_Displacement_sliding1_nofilter.npy'),
            disp1)
        np.save(
            os.path.join(npy_folder_path,
                         file_name + '_Displacement_sliding2_nofilter.npy'),
            disp2)
        np.save(
            os.path.join(npy_folder_path,
                         file_name + '_Displacement_crack1_nofilter.npy'),
            disp3)

        # Defining chunk size for matplotlib points visualization
        mpl.rcParams['agg.path.chunksize'] = 50000

        plt.subplot(111)
        plt.xlabel('Displacement [mm]')
        plt.ylabel('kN')
        plt.title('original data', fontsize=20)
        plt.plot(disp2, force, 'k')
        plt.show()

    figure = tr.Instance(Figure)

    def _figure_default(self):
        figure = Figure(facecolor='white')
        figure.set_tight_layout(True)
        return figure

    columns_headers_list = tr.List([])
    x_axis = tr.Enum(values='columns_headers_list')
    y_axis = tr.Enum(values='columns_headers_list')
    npy_folder_path = tr.Str
    file_name = tr.Str

    plot = tr.Button

    def _plot_fired(self):
        ax = self.figure.add_subplot(111)
        print('plotting figure')
        print(type(self.x_axis), type(self.y_axis))
        print(self.data[:, 1])
        print(self.data[:, self.x_axis])
        print(self.data[:, self.y_axis])
        ax.plot(self.data[:, self.x_axis], self.data[:, self.y_axis])

    traits_view = ui.View(ui.HSplit(
        ui.VSplit(
            ui.HGroup(ui.UItem('open_file_csv'),
                      ui.UItem('file_csv', style='readonly'),
                      label='Input data'),
            ui.VGroup(ui.Item('chunk_size'),
                      ui.Item('skip_rows'),
                      ui.Item('decimal'),
                      ui.Item('delimiter'),
                      label='Filter parameters'),
            ui.VGroup(
                ui.HGroup(ui.Item('read_loadtxt_button', show_label=False),
                          ui.Item('plot', show_label=False),
                          show_border=True),
                ui.HGroup(ui.Item('read_csv_button', show_label=False),
                          ui.Item('read_hdf5_button', show_label=False),
                          show_border=True))),
        ui.UItem('figure',
                 editor=MPLFigureEditor(),
                 resizable=True,
                 springy=True,
                 label='2d plots'),
    ),
                          resizable=True,
                          width=0.8,
                          height=0.6)