Subversion Repositories Projects

Rev

Rev 615 | Rev 619 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
# generated by wxGlade 0.6.3 on Thu Sep 24 15:46:36 2009

import sys
import os
import time
import thread
import ConfigParser

import wx
import wx.lib
import wx.lib.plot
import wx.lib.newevent

import mkProto



CHANNEL_NAMES = ["GyroYaw", "GyroRoll", "GyroNick", "Pressure", "Batt", "AccTop", "AccRoll", "AccNick"]
FS = 11111

# Needs Numeric or numarray or NumPy
try:
    import numpy.oldnumeric as _Numeric
except:
    try:
        import numarray as _Numeric  #if numarray is used it is renamed Numeric
    except:
        try:
            import Numeric as _Numeric
        except:
            msg= """
            This module requires the Numeric/numarray or NumPy module,
            which could not be imported.  It probably is not installed
            (it's not part of the standard Python distribution). See the
            Numeric Python site (http://numpy.scipy.org) for information on
            downloading source or binaries."""

            raise ImportError, "Numeric,numarray or NumPy not found. \n" + msg

# begin wxGlade: extracode
# end wxGlade


# This creates a new Event class and a EVT binder function
(MeasStatusUpdateEvent, EVT_MEAS_STATUS_UPDATE) = wx.lib.newevent.NewEvent()  
(MeasDataEvent, EVT_MEAS_DATA) = wx.lib.newevent.NewEvent()  

class MeasureDialog(wx.Dialog):
    def __init__(self, *args, **kwds):
        # begin wxGlade: MeasureDialog.__init__
        kwds["style"] = wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.THICK_FRAME
        wx.Dialog.__init__(self, *args, **kwds)
        self.text_ctrl_1 = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
        self.button = wx.Button(self, -1, "STOP")

        self.__set_properties()
        self.__do_layout()

        self.Bind(wx.EVT_BUTTON, self.onButton, self.button)
        # end wxGlade
       
        self.running = True
        self.Bind(EVT_MEAS_STATUS_UPDATE, self.OnUpdate)
        self.Bind(EVT_MEAS_DATA, self.OnData)
        # The first argument that is passed to the constructor is the parent
        self.app = args[0].app
        self.error = False

    def __set_properties(self):
        # begin wxGlade: MeasureDialog.__set_properties
        self.SetTitle("Measuring Status")
        self.text_ctrl_1.SetMinSize((400,300))
        # end wxGlade

    def __do_layout(self):
        # begin wxGlade: MeasureDialog.__do_layout
        sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_2 = wx.BoxSizer(wx.VERTICAL)
        sizer_1.Add((20, 20), 0, 0, 0)
        sizer_2.Add((20, 20), 0, 0, 0)
        sizer_2.Add(self.text_ctrl_1, 1, wx.EXPAND, 0)
        sizer_2.Add((20, 20), 0, 0, 0)
        sizer_2.Add(self.button, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
        sizer_2.Add((20, 20), 0, 0, 0)
        sizer_1.Add(sizer_2, 1, wx.EXPAND, 0)
        sizer_1.Add((20, 20), 0, 0, 0)
        self.SetSizer(sizer_1)
        sizer_1.Fit(self)
        self.Layout()
        # end wxGlade
       
    def OnData(self, evt):
        print "Received Data"
        self.app.AddTest2(evt.vibTest)

    def OnUpdate(self, evt):
        print "Status update"
        self.running = evt.running
        if evt.error:
            self.error = True;
            self.text_ctrl_1.WriteText("ERROR: ")
            self.text_ctrl_1.SetBackgroundColour("Red")  
        self.text_ctrl_1.WriteText("%s\n"%evt.msg)
        if (not self.running):
            if (not self.error):
                self.text_ctrl_1.SetBackgroundColour("Green")
                self.text_ctrl_1.write(" ") # so that the background is redrawn
            self.button.SetLabel("Close")  
           
       
    def onButton(self, event): # wxGlade: MeasureDialog.<event_handler>
        if (not self.running):
            self.Destroy()
        else:
            self.app.cancelMeasurement()

# end of class MeasureDialog


class SettingsDialog(wx.Dialog):
    def __init__(self, *args, **kwds):
        # begin wxGlade: SettingsDialog.__init__
        kwds["style"] = wx.DEFAULT_DIALOG_STYLE
        wx.Dialog.__init__(self, *args, **kwds)
        self.button_5 = wx.Button(self, wx.ID_CANCEL, "")
        self.button_6 = wx.Button(self, wx.ID_OK, "")

        self.__set_properties()
        self.__do_layout()

        self.Bind(wx.EVT_BUTTON, self.onOK, self.button_6)
        # end wxGlade

        # The first argument that is passed to the constructor is the parent
        self.settings = args[0].app.settings
        # Add text-boxes for all settings
        self.tb = []
        self.grid_sizer_2.SetRows(len(self.settings))
        for setting in self.settings.iteritems():
            lb = wx.StaticText(self, -1, setting[1].descr, style=wx.ALIGN_RIGHT)
            tb = wx.TextCtrl(self, -1, str(setting[1].value))
            self.grid_sizer_2.Add(lb, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
            self.grid_sizer_2.Add(tb, 0, 0, 0)
            self.tb.append(tb)
        self.sizer_5.Fit(self)
        self.Layout()

    def __set_properties(self):
        # begin wxGlade: SettingsDialog.__set_properties
        self.SetTitle("Settings")
        # end wxGlade

    def __do_layout(self):
        # begin wxGlade: SettingsDialog.__do_layout
        sizer_5 = wx.BoxSizer(wx.VERTICAL)
        grid_sizer_3 = wx.GridSizer(1, 2, 0, 0)
        sizer_6 = wx.BoxSizer(wx.HORIZONTAL)
        grid_sizer_2 = wx.GridSizer(1, 2, 4, 4)
        sizer_5.Add((20, 20), 0, 0, 0)
        sizer_6.Add((20, 20), 0, 0, 0)
        sizer_6.Add(grid_sizer_2, 0, 0, 0)
        sizer_6.Add((20, 20), 0, 0, 0)
        sizer_5.Add(sizer_6, 1, wx.EXPAND, 0)
        sizer_5.Add((20, 20), 0, 0, 0)
        grid_sizer_3.Add(self.button_5, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 0)
        grid_sizer_3.Add(self.button_6, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_5.Add(grid_sizer_3, 0, wx.EXPAND, 0)
        sizer_5.Add((20, 20), 0, 0, 0)
        self.SetSizer(sizer_5)
        sizer_5.Fit(self)
        self.Layout()
        # end wxGlade

        # Store some of the items, we will need them later
        self.grid_sizer_2 = grid_sizer_2  
        self.sizer_5 = sizer_5

       
    def onOK(self, event): # wxGlade: SettingsDialog.<event_handler>
        print "Updating parameters"
        try:
            i=0
            for setting in self.settings.iteritems():
              print setting[0], self.tb[i].GetValue()
              setting[1].set(self.tb[i].GetValue())
              i += 1
            event.Skip()
        except:
            wx.MessageBox("Invalid format for \"%s\" setting." % setting[1].descr)
       
# end of class SettingsDialog



class MainFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        # begin wxGlade: MainFrame.__init__
        kwds["style"] = wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)
       
        # Menu Bar
        self.frame_1_menubar = wx.MenuBar()
        wxglade_tmp_menu = wx.Menu()
        wxglade_tmp_menu.Append(101, "Settings", "", wx.ITEM_NORMAL)
        wxglade_tmp_menu.AppendSeparator()
        wxglade_tmp_menu.Append(150, "Exit", "", wx.ITEM_NORMAL)
        self.frame_1_menubar.Append(wxglade_tmp_menu, "File")
        wxglade_tmp_menu = wx.Menu()
        wxglade_tmp_menu.Append(301, "Clear All", "", wx.ITEM_NORMAL)
        wxglade_tmp_menu.Append(302, "Clear Selected", "", wx.ITEM_NORMAL)
        wxglade_tmp_menu.AppendSeparator()
        wxglade_tmp_menu.Append(303, "Load", "", wx.ITEM_NORMAL)
        wxglade_tmp_menu.Append(304, "Save", "", wx.ITEM_NORMAL)
        self.frame_1_menubar.Append(wxglade_tmp_menu, "TestSet")
        wxglade_tmp_menu = wx.Menu()
        self.frame_1_menubar.Append(wxglade_tmp_menu, "MK")
        wxglade_tmp_menu = wx.Menu()
        self.frame_1_menubar.Append(wxglade_tmp_menu, "Help")
        self.SetMenuBar(self.frame_1_menubar)
        # Menu Bar end
        self.Description = wx.StaticText(self, -1, "Description")
        self.tcDescr = wx.TextCtrl(self, -1, "Test")
        self.label_37 = wx.StaticText(self, -1, "Speed(s)")
        self.tcSpeeds = wx.TextCtrl(self, -1, "100-200:10")
        self.label_35 = wx.StaticText(self, -1, "Motor(s)")
        self.tcMotors = wx.TextCtrl(self, -1, "1")
        self.label_38 = wx.StaticText(self, -1, "")
        self.text_ctrl_10 = wx.TextCtrl(self, -1, "")
        self.label_36 = wx.StaticText(self, -1, "Channel(s)")
        self.tcChannels = wx.TextCtrl(self, -1, "6")
        self.label_39 = wx.StaticText(self, -1, "")
        self.text_ctrl_11 = wx.TextCtrl(self, -1, "")
        self.button_4 = wx.Button(self, -1, "Start")
        self.GraphPanel = wx.Panel(self, -1)
        self.label_40 = wx.StaticText(self, -1, "Graph Type ")
        self.graphTypeChoice = wx.Choice(self, -1, choices=["Raw Signal", "Filtered Signal", "Spectrum"])
        self.label_41 = wx.StaticText(self, -1, "Y Axis Range ")
        self.yAxisChoice = wx.Choice(self, -1, choices=["25", "50", "100", "200"])
        self.TestListCtrl = wx.ListCtrl(self, -1, style=wx.LC_REPORT|wx.SUNKEN_BORDER)

        self.__set_properties()
        self.__do_layout()

        self.Bind(wx.EVT_MENU, self.OnSettings, id=101)
        self.Bind(wx.EVT_MENU, self.onClear, id=301)
        self.Bind(wx.EVT_MENU, self.onClearSelected, id=302)
        self.Bind(wx.EVT_MENU, self.OnImport, id=303)
        self.Bind(wx.EVT_MENU, self.onExport, id=304)
        self.Bind(wx.EVT_BUTTON, self.onStartMeasure, self.button_4)
        self.Bind(wx.EVT_CHOICE, self.onGraphTypeChange, self.graphTypeChoice)
        self.Bind(wx.EVT_CHOICE, self.onYAxisChange, self.yAxisChoice)
        # end wxGlade

    def setApp(self, app):
        self.app = app

    def __set_properties(self):
        # begin wxGlade: MainFrame.__set_properties
        self.SetTitle("VibrationTest")
        self.SetSize((850, 700))
        self.Description.SetMinSize((53, 13))
        self.button_4.SetMinSize((80, 80))
        self.GraphPanel.SetMinSize((800,300))
        self.graphTypeChoice.SetSelection(0)
        self.yAxisChoice.SetSelection(1)
        self.TestListCtrl.SetMinSize((800,300))
        # end wxGlade

    def __do_layout(self):
        # begin wxGlade: MainFrame.__do_layout
        sizer_3 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_8 = wx.BoxSizer(wx.VERTICAL)
        sizer_11 = wx.BoxSizer(wx.VERTICAL)
        sizer_12 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_9 = wx.BoxSizer(wx.HORIZONTAL)
        sizer_10 = wx.BoxSizer(wx.HORIZONTAL)
        grid_sizer_1 = wx.GridSizer(3, 4, 4, 5)
        sizer_3.Add((20, 20), 0, 0, 0)
        sizer_8.Add((20, 20), 0, 0, 0)
        grid_sizer_1.Add(self.Description, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        grid_sizer_1.Add(self.tcDescr, 0, 0, 0)
        grid_sizer_1.Add(self.label_37, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        grid_sizer_1.Add(self.tcSpeeds, 0, 0, 0)
        grid_sizer_1.Add(self.label_35, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        grid_sizer_1.Add(self.tcMotors, 0, 0, 0)
        grid_sizer_1.Add(self.label_38, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        grid_sizer_1.Add(self.text_ctrl_10, 0, 0, 0)
        grid_sizer_1.Add(self.label_36, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        grid_sizer_1.Add(self.tcChannels, 0, 0, 0)
        grid_sizer_1.Add(self.label_39, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL, 0)
        grid_sizer_1.Add(self.text_ctrl_11, 0, 0, 0)
        sizer_9.Add(grid_sizer_1, 0, 0, 0)
        sizer_10.Add((50, 20), 0, 0, 0)
        sizer_10.Add(self.button_4, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_9.Add(sizer_10, 1, wx.EXPAND, 0)
        sizer_8.Add(sizer_9, 0, 0, 0)
        sizer_8.Add((20, 30), 0, 0, 0)
        sizer_11.Add(self.GraphPanel, 1, wx.EXPAND, 0)
        sizer_11.Add((20, 5), 0, 0, 0)
        sizer_12.Add(self.label_40, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_12.Add(self.graphTypeChoice, 0, 0, 0)
        sizer_12.Add((40, 20), 0, 0, 0)
        sizer_12.Add(self.label_41, 0, wx.ALIGN_CENTER_VERTICAL, 0)
        sizer_12.Add(self.yAxisChoice, 0, 0, 0)
        sizer_11.Add(sizer_12, 0, 0, 0)
        sizer_8.Add(sizer_11, 0, 0, 0)
        sizer_8.Add((20, 30), 0, 0, 0)
        sizer_8.Add(self.TestListCtrl, 1, 0, 0)
        sizer_8.Add((20, 20), 0, 0, 0)
        sizer_3.Add(sizer_8, 1, wx.EXPAND, 0)
        self.SetSizer(sizer_3)
        self.Layout()
        self.SetSize((850, 700))
        # end wxGlade

        # List events
        self.TestListCtrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnTestSelected, self.TestListCtrl)

        # Configure Graph
        self.client = wx.lib.plot.PlotCanvas(self.GraphPanel, size=(800,300))
       
        self.client.SetPointLabelFunc(self.DrawPointLabel)
       
        self.client.SetFont(wx.Font(10,wx.SWISS,wx.NORMAL,wx.NORMAL))
        self.client.SetFontSizeAxis(10)
        self.client.SetFontSizeLegend(7)
        self.client.setLogScale((False,False))


        # Configure TestListCtrl
        self.TestListCtrl.InsertColumn(0, "Description")
        self.TestListCtrl.InsertColumn(1, "Speed")
        self.TestListCtrl.InsertColumn(2, "Channel")
        self.TestListCtrl.InsertColumn(3, "Vibration Value")
        self.TestListCtrl.SetColumnWidth(3, 500)

    def DrawPointLabel(self, dc, mDataDict):
        """This is the fuction that defines how the pointLabels are plotted
            dc - DC that will be passed
            mDataDict - Dictionary of data that you want to use for the pointLabel

            As an example I have decided I want a box at the curve point
            with some text information about the curve plotted below.
            Any wxDC method can be used.
        """

        # ----------
        dc.SetPen(wx.Pen(wx.BLACK))
        dc.SetBrush(wx.Brush( wx.BLACK, wx.SOLID ) )
       
        sx, sy = mDataDict["scaledXY"] #scaled x,y of closest point
        dc.DrawRectangle( sx-5,sy-5, 10, 10)  #10by10 square centered on point
        px,py = mDataDict["pointXY"]
        cNum = mDataDict["curveNum"]
        pntIn = mDataDict["pIndex"]
        legend = mDataDict["legend"]
        #make a string to display
        s = "Crv# %i, '%s', Pt. (%.2f,%.2f), PtInd %i" %(cNum, legend, px, py, pntIn)
        dc.DrawText(s, sx , sy+1)
        # -----------


    def onNewTest(self, test):
        index = self.TestListCtrl.InsertStringItem(sys.maxint, test.descr)
        self.TestListCtrl.SetStringItem(index, 1, str(test.speed))
        self.TestListCtrl.SetStringItem(index, 2, test.channel)

        vv = int(test.getVibValue(self.app.settings["hpf"].value, self.app.settings["lpf"].value))
        vvs = "|%s| (%d)" % ("----------------------------------------------------------------------------------------------------"[0:min(vv,100)], vv)
        self.TestListCtrl.SetStringItem(index, 3, vvs)
        self.TestListCtrl.Select(index)


    def OnTestSelected(self, event):
         testId = event.m_itemIndex
         print "Test Selected id=%d" % (testId)
         self.activeTestId = testId
         self.drawGraph()

    def drawGraph(self):
         
         y = int(self.yAxisChoice.GetStringSelection())

         nbSelected = self.TestListCtrl.SelectedItemCount

         if nbSelected == 0:
              self.client.Clear()
         
         elif nbSelected > 1:
             self.graphTypeChoice.Disable()
             x = 1
             data = []
             idx = self.TestListCtrl.GetFirstSelected()
             while idx != -1:
                 data.append([x,self.app.getTest(idx).getVibValue(self.app.settings["hpf"].value, self.app.settings["lpf"].value)])
                 x += 1
                 idx = self.TestListCtrl.GetNextSelected(idx)
             line = wx.lib.plot.PolyLine(data, legend= 'Vibrations', colour='red', width=2)
             markers = wx.lib.plot.PolyMarker(data, legend= '', colour='red', marker='circle',size=2)
             title = "Comparing tests"
             self.client.Draw(wx.lib.plot.PlotGraphics([line, markers], title, "Test", "Vibration Value"), xAxis=(1,max(x,10)), yAxis=(0,y))
             self.client.SetEnableGrid('Horizontal')

         else:
             self.graphTypeChoice.Enable()
             vibTest = self.app.getTest(self.activeTestId)
             nb = vibTest.getDataLen()

             if self.graphTypeChoice.GetSelection() == 0:
                 xydata = _Numeric.linspace(0,0.09*nb,2*nb)
                 xydata.shape = (nb, 2)
                 xydata[:,1] = vibTest.getRawData()
                 line = wx.lib.plot.PolyLine(xydata, legend= 'Raw Data', colour='red', width=2)
       
                 title = "Raw Signal: %s %s %d" %(vibTest.descr, vibTest.channel, vibTest.speed)
                 self.client.Draw(wx.lib.plot.PlotGraphics([line], title, "Time (ms)", "Acc"), yAxis= (-y,y))
                 self.client.SetEnableGrid('Horizontal')
   
             if self.graphTypeChoice.GetSelection() == 1:
                 xydata = _Numeric.linspace(0,0.09*nb,2*nb)
                 xydata.shape = (nb, 2)
                 xydata[:,1] = vibTest.getFilteredData(self.app.settings["hpf"].value, self.app.settings["lpf"].value)
                 line = wx.lib.plot.PolyLine(xydata, legend= 'Raw Data', colour='red', width=2)
       
                 title = "Filtered Signal: %s %s %d" %(vibTest.descr, vibTest.channel, vibTest.speed)
                 self.client.Draw(wx.lib.plot.PlotGraphics([line], title, "Time (ms)", "Acc"), yAxis= (-y,y))
                 self.client.SetEnableGrid('Horizontal')
   
             elif self.graphTypeChoice.GetSelection() == 2:
                 xydata = _Numeric.linspace(0,FS/2,nb)
                 xydata.shape = (nb/2, 2)
                 
                 xydata[:,1] = vibTest.getSpectrum()
   
                 line = wx.lib.plot.PolyLine(xydata, legend= 'Spectrum', colour='red')
                 markers = wx.lib.plot.PolyMarker(xydata, legend= '', colour='red', marker='circle',size=2)
                 
                 fc = self.app.settings["hpf"].value
                 filterLine1 = wx.lib.plot.PolyLine(((fc,0),(fc,y)), legend='HP Filter', colour='Black', width=4)
                 fc = self.app.settings["lpf"].value
                 filterLine2 = wx.lib.plot.PolyLine(((fc,0),(fc,y)), legend='HP Filter', colour='Black', width=4)
       
                 title = "Spectrum: %s %s %d" %(vibTest.descr, vibTest.channel, vibTest.speed)
                 self.client.Draw(wx.lib.plot.PlotGraphics([line,markers, filterLine1, filterLine2], title, "Freq (Hz)", "Acc"), xAxis=(0,200), yAxis= (-0,y))
                 self.client.SetEnableGrid(True)


    def OnImport(self, event): # wxGlade: MainFrame.<event_handler>
        dlg = wx.FileDialog(
            self, message="Choose a file",
            defaultDir=os.getcwd(),
            defaultFile="*.txt",
            wildcard="",
            style=wx.OPEN | wx.CHANGE_DIR
            )
        if dlg.ShowModal() == wx.ID_OK:
            paths = dlg.GetPaths();
            self.app.Import(paths[0])
        dlg.Destroy()

    def onYAxisChange(self, event): # wxGlade: MainFrame.<event_handler>
        self.drawGraph()

    def onGraphTypeChange(self, event): # wxGlade: MainFrame.<event_handler>
        self.drawGraph()

    def OnSettings(self, event): # wxGlade: MainFrame.<event_handler>
        dlg = SettingsDialog(self, -1, "Sample Dialog", size=(350, 200),
                         #style=wx.CAPTION | wx.SYSTEM_MENU | wx.THICK_FRAME,
                         style=wx.DEFAULT_DIALOG_STYLE, # & ~wx.CLOSE_BOX
                         )
        dlg.CenterOnScreen()
        val = dlg.ShowModal()  # this does not return until the dialog is closed.
        dlg.Destroy()
        self.app.onSettingsChanged()

    def onStartMeasure(self, event): # wxGlade: MainFrame.<event_handler>
        # Collect measure parameters
        mp = MeasureParameters()
        mp.descr = self.tcDescr.GetValue()
        mp.motors = map(int,self.tcMotors.GetValue().split(','))
        mp.channels = map(int,self.tcChannels.GetValue().split(','))
        s = self.tcSpeeds.GetValue()
        if s.count("-") == 1:
            # assume from-to:step format
            s = s.split("-")
            if len(s) != 2: raise Exception("Invalid format")
            s[1] = s[1].split(":")
            if len(s[1]) != 2: raise Exception("Invalid format")
            mp.speeds = range(int(s[0]),int(s[1][0])+int(s[1][1]),int(s[1][1]))
        else:
            mp.speeds = map(int,s.split(','))
           
       
        # create the dialog that will show the satus
        dlg = MeasureDialog(self)
        dlg.CenterOnScreen()
       
        # Signal the application
        self.app.startMeasure(mp, dlg)
       
        # Show the dialog
        val = dlg.ShowModal()  # this does not return until the dialog is closed.
        dlg.Destroy()

    def _removeTest(self, idx):
        print "Deleting test %d" % idx
        self.app.removeTest(idx)
        self.TestListCtrl.DeleteItem(idx)
       
           
    def onClear(self, event): # wxGlade: MainFrame.<event_handler>
        print "Clear all tests"
        for i in range(len(self.app.VibTests)-1, -1, -1):
            self._removeTest(i)
        self.drawGraph()
           
       
    def onClearSelected(self, event): # wxGlade: MainFrame.<event_handler>
        while True:
            idx = self.TestListCtrl.GetFirstSelected()
            if idx == -1: break
            self._removeTest(idx)
           
    def onExport(self, event): # wxGlade: MainFrame.<event_handler>
        print "Event handler `onExport' not implemented"
        event.Skip()

# end of class MainFrame

class Setting:
    def __init__(self, descr, defaultValue):
        self.descr = descr
        self.value = defaultValue
       
    def set(self, newValue):
        if isinstance(self.value, int):
            self.value = int(newValue)
        else:
            self.value = str(newValue)

class MeasureParameters:
      pass

class MeasureThread:
    def __init__(self, measureParameters, evtConsumer):
        self.mk = mkProto.MkComm()
        self.param = measureParameters
        self.evtConsumer = evtConsumer
        self.cancel = False
        self.running = False
       
    def start(self):
        thread.start_new_thread(self._run, ())
       
    def stop(self):
        self.cancel = True

    def _testCancel(self):
        if self.cancel:
            raise Exception("Operation cancelled")
               
    def _sendEvent(self, msg, error=False):
        evt = MeasStatusUpdateEvent(running=self.running, msg=msg, error=error)
        wx.PostEvent(self.evtConsumer, evt)

    def _setMotorSpeed(self, speed, settlingTime):
        speeds = [0,0,0,0]
        for motor in self.param.motors:
            speeds[motor-1] = speed
        for i in range(int(settlingTime*10)):
          self._testCancel()
          self.mk.setMotorTest(speeds)
          time.sleep(.1)
       
                   
    def _run(self):
        self.running = True
        self._sendEvent("Starting test \"%s\"" % self.param.descr)    

        try:
            self._sendEvent("Opening SerialPort \"%s\"" % self.param.serialPort)
            self.mk.open(comPort=self.param.serialPort)
               
            msg = self.mk.getVersionMsg()
            version = msg.getVersion()
            self._sendEvent("Version: %d.%d" % version)
           
            msg = self.mk.getDebugMsg()
            voltage = msg.getVoltage()
            if (voltage == 0):
              minVoltage = 0
            else:
              if (voltage > 4.2*3):
                minVoltage = 4*3.5
              else:
                minVoltage = 3*3.5

            self._sendEvent("Voltage: %2.1fV" % voltage)
            self._sendEvent("Minimum Voltage: %2.1fV" % minVoltage)
         
            self._sendEvent("Starting motor(s) (speed=%d)... " % self.param.motorStartupSpeed)
            self._setMotorSpeed(self.param.motorStartupSpeed, self.param.motorStartupSettlingTime)
         
            for speed in self.param.speeds:
                self._sendEvent("Changing motor speed to %d... " % speed)
                self._setMotorSpeed(speed, 1)
               
                for channel in self.param.channels:
                    self._setMotorSpeed(speed, .1)
                    self._sendEvent("Getting data from channel %s" % CHANNEL_NAMES[channel])
                    data = self.mk.doVibrationTest(1000, channel)
                   
                    vt = VibTest(self.param.descr, self.param.motors, speed, CHANNEL_NAMES[channel], data)
                    evt = MeasDataEvent(vibTest = vt)
                    wx.PostEvent(self.evtConsumer, evt)
                   
            self._sendEvent("Done !")            
       
        except Exception, e:
            self._sendEvent("Exception \"%s\"" % e, error=True)  
               
        self.running = False
        self._sendEvent("")    
       

class VibTest:
    def __init__(self, descr, motor, speed, channel, rawData):
        self.descr = descr
        self.motor = motor
        self.speed = speed
        self.channel = channel

        self.dataLen = len(rawData)

        self.rawData = _Numeric.array(rawData)
        self.dc = self.rawData.mean()
        self.rawData -= self.dc

        self.fft = _Numeric.fft.rfft(self.rawData)

        self.spectrum = None
        self.filteredData = None
        self.fc1 = None
        self.fc2 = None
       
        self.vibValue = None
       
    def getDescr(self):
        return self.Descr

    def getRawData(self):
        return self.rawData

    def getDataLen(self):
        return self.dataLen

    def getSpectrum(self):
        if self.spectrum == None:
            self.spectrum = _Numeric.absolute(self.fft[1:self.dataLen/2+1]) / (self.dataLen/2)
        return self.spectrum

    def getFilteredData(self, fc1, fc2):
        if self.fc1 != fc1 or self.fc2 != fc2:
            self.filteredData = None  
           
        if self.filteredData == None:
            tmpfft = self.fft.copy()
            fc = (float(fc1))/(FS/2)*len(tmpfft)
            print "fc1=%d => fc=%d" % (fc1,fc)
            for i in range(0,int(fc)):
                tmpfft[i] = 0
            fc = (float(fc2))/(FS/2)*len(tmpfft)
            print "fc2=%d => fc=%d" % (fc2,fc)
            for i in range(int(fc), len(tmpfft)):
                tmpfft[i] = 0
            self.filteredData = _Numeric.fft.irfft(tmpfft)
            self.fc1 = fc1
            self.fc2 = fc2
           
        return self.filteredData
       
    def getVibValue(self, fc1, fc2):
      if self.fc1 != fc1 or self.fc2 != fc2:
        self.vibValue = None  
      if self.vibValue == None:
        fd = self.getFilteredData(fc1, fc2)[100:-100];
        self.vibValue = max(fd)-min(fd)
      return self.vibValue



class App(wx.App):

    SETTINGSFILE = "settings.cfg"

    def __init__(self, par):
        self.VibTests = []
        wx.App.__init__(self, par)

        # Init settings
        self.settings={}
        self.settings["serialport"] = Setting("Serial Port", "COM1")
        self.settings["startupspeed"] = Setting("Motor Startup Speed", 25)
        self.settings["startupsettling"] = Setting("Motor Startup Setting time (s)", 3)
        self.settings["serialport"] = Setting("Serial Port", "COM1")
        self.settings["hpf"] = Setting("HP Filter cutoff (Hz)", 50)
        self.settings["lpf"] = Setting("LP Filter cutoff (Hz)", 180)

        self.readSettings()

        if len(sys.argv)>1:
            self.Import(sys.argv[1])


    def readSettings(self):
        print "Reading settings"
        cp = ConfigParser.ConfigParser()

        try:
            cp.read(App.SETTINGSFILE)
            for setting in cp.items("DEFAULT"):
                print " ",setting
                try:
                    self.settings[setting[0]].set(setting[1])
                except:
                    print "WARNING, unknown setting"
        except:
            print "ERROR reading settingsfile"


    def storeSettings(self):
        print "Storing settings"

        cp = ConfigParser.ConfigParser()
        for setting in self.settings.iteritems():
            cp.set("", setting[0], setting[1].value)

        file = open(App.SETTINGSFILE, "w")
        cp.write(file)
        file.close()


    def onSettingsChanged(self):
        self.storeSettings()
     
    def AddTest2(self, vibTest):
        self.VibTests.append(vibTest)
        self.frame_1.onNewTest(vibTest)
         
    def AddTest(self, descr, motor, speed, channel, rawData):
        test = VibTest(descr, motor, speed, channel, rawData)
        self.AddTest2(test)

    def removeTest(self, idx):
        del(self.VibTests[idx])
   
    def getTest(self, testId):
        return self.VibTests[testId]

    def OnInit(self):
        wx.InitAllImageHandlers()
        self.frame_1 = MainFrame(None, -1, "")
        self.frame_1.setApp(self);
        self.SetTopWindow(self.frame_1)

        self.frame_1.CenterOnScreen()
        self.frame_1.Show()
        return 1

    def Import(self, filePath):
       
        print "Importing file \"%s\"" % filePath

        logfile = open(filePath, "r")
        data = None

        headers = (logfile.readline()).split(',')
        nbCols = len(headers)
        print "NbCols =", nbCols

        data = []
        descr = []
        speed = []
        channel = []
        for c in range(nbCols):
            data.append([])
            h = headers[c].split(' ')
            descr.append(h[0]);
            speed.append(h[1]);
            channel.append(h[2]);

        for line in logfile:
            values = line.split(',')
            for i in range(nbCols):
                data[i].append(int(values[i]))
        logfile.close()

        for c in range(nbCols):
            if (len(data[c]) % 2) != 0:
                data[c].append(data[c][-1])
            self.AddTest(descr[c], 0, int(speed[c]), channel[c], data[c])
           
    def startMeasure(self, measureParams, dialog):
        print "Start measuring"
       
        measureParams.serialPort = self.settings["serialport"].value
        measureParams.motorStartupSpeed = self.settings["startupspeed"].value
        measureParams.motorStartupSettlingTime = self.settings["startupsettling"].value
       
        self.measureThread = MeasureThread(measureParams, dialog)
        self.measureThread.start()
       
    def cancelMeasurement(self):
        print "Measuring CANCEL"
       
        self.measureThread.stop()
       
       
       
       
       
           

       
# end of class App

if __name__ == "__main__":
    VibrationTestGui = App(0)
    VibrationTestGui.MainLoop()