Beispiel #1
0
    def build(self):
        Window.maximize()

        interface = FloatLayout()
        self.data = Data()

        self.frontpage = FrontPage(self.data, name='FrontPage')
        interface.add_widget(self.frontpage)

        self.nonVisibleWidgets = NonVisibleWidgets()
        '''
        Load User Settings
        '''

        self.data.comport = self.config.get('Makesmith Settings', 'COMport')
        self.data.gcodeFile = self.config.get('Makesmith Settings', 'openFile')
        self.data.config = self.config
        '''
        Initializations
        '''

        self.frontpage.setUpData(self.data)
        self.nonVisibleWidgets.setUpData(self.data)
        self.frontpage.gcodecanvas.initialzie()
        '''
        Scheduling
        '''

        Clock.schedule_interval(self.runPeriodically, .01)

        return interface
Beispiel #2
0
 def build(self):
     Window.maximize()
     
     interface       =  FloatLayout()
     self.data       =  Data()
     
     self.frontpage = FrontPage(self.data, name='FrontPage')
     interface.add_widget(self.frontpage)
     
     self.nonVisibleWidgets = NonVisibleWidgets()
     
     
     '''
     Load User Settings
     '''
     
     self.config.set('Advanced Settings', 'truncate', 0)
     self.config.set('Advanced Settings', 'digits', 4)
     self.config.write()
     
     self.data.comport = self.config.get('Maslow Settings', 'COMport')
     self.data.gcodeFile = self.config.get('Maslow Settings', 'openFile')
     offsetX = float(self.config.get('Advanced Settings', 'homeX'))
     offsetY = float(self.config.get('Advanced Settings', 'homeY'))
     self.data.gcodeShift = [offsetX,offsetY]
     self.data.config  = self.config
     
     
     '''
     Initializations
     '''
     
     self.frontpage.setUpData(self.data)
     self.nonVisibleWidgets.setUpData(self.data)
     self.frontpage.gcodecanvas.initialize()
     
     
     '''
     Scheduling
     '''
     
     Clock.schedule_interval(self.runPeriodically, .01)
     
     '''
     Push settings to machine
     '''
     self.data.bind(connectionStatus = self.push_settings_to_machine)
     self.data.pushSettings = self.push_settings_to_machine
     
     self.push_settings_to_machine()
     
     return interface
Beispiel #3
0
class GroundControlApp(App):

    json = '''
    [
        {
            "type": "string",
            "title": "Serial Connection",
            "desc": "Select the COM port to connect to machine",
            "section": "Makesmith Settings",
            "key": "COMport"
        },
        {
            "type": "string",
            "title": "X-Axis Pitch",
            "desc": "The number of mm moved per rotation",
            "section": "Makesmith Settings",
            "key": "xPitch"
        },
        {
            "type": "string",
            "title": "Open File",
            "desc": "The path to the open file",
            "section": "Makesmith Settings",
            "key": "openFile"
        }
    ]
    '''

    def build(self):
        Window.maximize()

        interface = FloatLayout()
        self.data = Data()

        self.frontpage = FrontPage(self.data, name='FrontPage')
        interface.add_widget(self.frontpage)

        self.nonVisibleWidgets = NonVisibleWidgets()
        '''
        Load User Settings
        '''

        self.data.comport = self.config.get('Makesmith Settings', 'COMport')
        self.data.gcodeFile = self.config.get('Makesmith Settings', 'openFile')
        self.data.config = self.config
        '''
        Initializations
        '''

        self.frontpage.setUpData(self.data)
        self.nonVisibleWidgets.setUpData(self.data)
        self.frontpage.gcodecanvas.initialzie()
        '''
        Scheduling
        '''

        Clock.schedule_interval(self.runPeriodically, .01)

        return interface

    def build_config(self, config):
        """
        Set the default values for the configs sections.
        """
        config.setdefaults('Makesmith Settings', {
            'COMport': 'COM5',
            'xPitch': 20,
            'openFile': " "
        })

    def build_settings(self, settings):
        """
        Add custom section to the default configuration object.
        """
        settings.add_json_panel('Makesmith Settings',
                                self.config,
                                data=self.json)

    def on_config_change(self, config, section, key, value):
        """
        Respond to changes in the configuration.
        """

        if section == "Makesmith Settings":
            if key == "COMport":
                self.data.comport = value
            elif key == 'xPitch':
                print "xPitch changed"

    def close_settings(self, settings):
        """
        Close settings panel
        """
        super(GroundControlApp, self).close_settings(settings)

    '''
    
    Update Functions
    
    '''

    def runPeriodically(self, *args):
        '''
        this block should be handled within the appropriate widget
        '''
        while not self.data.message_queue.empty(
        ):  #if there is new data to be read
            message = self.data.message_queue.get()
            if message[0:2] == "pz":
                self.setPosOnScreen(message)
            elif message[0:2] == "pt":
                self.setTargetOnScreen(message)
            elif message[0:8] == "Message:":
                self.data.uploadFlag = 0
                content = NotificationPopup(cancel=self.dismiss_popup,
                                            text=message[9:])
                self._popup = Popup(title="Notification: ",
                                    content=content,
                                    auto_dismiss=False,
                                    size_hint=(0.25, 0.25))
                self._popup.open()
            else:
                try:
                    newText = self.frontpage.consoleText[-3000:] + message
                    self.frontpage.consoleText = newText
                    self.frontpage.textconsole.gotToBottom()
                except:
                    self.frontpage.consoleText = "text not displayed correctly"

    def dismiss_popup(self):
        '''
        
        Close The Pop-up
        
        '''
        self._popup.dismiss()
        self.data.uploadFlag = 1

    def setPosOnScreen(self, message):
        '''
        
        This should be moved into the appropriate widget
        
        '''

        try:
            startpt = message.find('(')
            startpt = startpt + 1

            endpt = message.find(')')

            numz = message[startpt:endpt]
            units = message[endpt + 1:endpt + 3]

            valz = numz.split(",")

            xval = float(valz[0])
            yval = float(valz[1])
            zval = float(valz[2])
        except:
            print "bad data"
            return

        self.frontpage.setPosReadout(xval, yval, zval, units)
        self.frontpage.gcodecanvas.positionIndicator.setPos(
            xval, yval, self.data.units)

    def setTargetOnScreen(self, message):
        '''
        
        This should be moved into the appropriate widget
        
        '''
        try:
            startpt = message.find('(')
            startpt = startpt + 1

            endpt = message.find(')')

            numz = message[startpt:endpt]
            units = message[endpt + 1:endpt + 3]

            valz = numz.split(",")

            xval = float(valz[0])
            yval = float(valz[1])
            zval = float(valz[2])

            self.frontpage.gcodecanvas.targetIndicator.setPos(
                xval, yval, self.data.units)
        except:
            print "unable to convert to number"
Beispiel #4
0
class GroundControlApp(App):

    def get_application_config(self):
        return super(GroundControlApp, self).get_application_config(
            '~/%(appname)s.ini')

    json = '''
    [
        {
            "type": "string",
            "title": "Serial Connection",
            "desc": "Select the COM port to connect to machine",
            "section": "Maslow Settings",
            "key": "COMport"
        },
        {
            "type": "string",
            "title": "Distance Between Motors",
            "desc": "The horizontal distance between the center of the motor shafts in MM.",
            "section": "Maslow Settings",
            "key": "motorSpacingX"
            
        },
        {
            "type": "string",
            "title": "Work Area Width in MM",
            "desc": "The width of the machine working area (normally 8 feet).",
            "section": "Maslow Settings",
            "key": "bedWidth"
        },
        {
            "type": "string",
            "title": "Work Area Height in MM",
            "desc": "The Height of the machine working area (normally 4 feet).",
            "section": "Maslow Settings",
            "key": "bedHeight"
        },
        {
            "type": "string",
            "title": "Motor Offset Height in MM",
            "desc": "The vertical distance from the edge of the work area to the level of the motors.",
            "section": "Maslow Settings",
            "key": "motorOffsetY"
        },
        {
            "type": "string",
            "title": "Distance Between Sled Mounting Points",
            "desc": "The horizontal distance between the points where the chains mount to the sled.",
            "section": "Maslow Settings",
            "key": "sledWidth"
        },
        {
            "type": "string",
            "title": "Vertical Distance Sled Mounts to Cutter",
            "desc": "The vertical distance between where the chains mount on the sled to the cutting tool.",
            "section": "Maslow Settings",
            "key": "sledHeight"
        },
        {
            "type": "string",
            "title": "Center Of Gravity",
            "desc": "How far below the cutting bit is the center of gravity. This can be found by resting the sled on a round object and observing where it balances.",
            "section": "Maslow Settings",
            "key": "sledCG"
        },
        {
            "type": "bool",
            "title": "z-axis installed",
            "desc": "Does the machine have an automatic z-axis?",
            "section": "Maslow Settings",
            "key": "zAxis"
        },
        {
            "type": "string",
            "title": "Z-Axis Pitch",
            "desc": "The number of mm moved per rotation of the z-axis",
            "section": "Maslow Settings",
            "key": "zDistPerRot"
        },
        {
            "type": "string",
            "title": "Open File",
            "desc": "The path to the open file",
            "section": "Maslow Settings",
            "key": "openFile"
        },
        {
            "type": "string",
            "title": "Macro 1",
            "desc": "User defined gcode bound to the Macro 1 button",
            "section": "Maslow Settings",
            "key": "macro1"
        },
        {
            "type": "string",
            "title": "Macro 2",
            "desc": "User defined gcode bound to the Macro 2 button",
            "section": "Maslow Settings",
            "key": "macro2"
        }
    ]
    '''

    advanced = '''
    [
        {
            "type": "string",
            "title": "Encoder Steps per Revolution",
            "desc": "The number of encoder steps per revolution of the left or right motor",
            "section": "Advanced Settings",
            "key": "encoderSteps"
        },
        {
            "type": "string",
            "title": "Gear Teeth",
            "desc": "The number of teeth on the gear of the left or right motor",
            "section": "Advanced Settings",
            "key": "gearTeeth"
        },
        {
            "type": "string",
            "title": "Chain Pitch",
            "desc": "The distance between chain roller centers",
            "section": "Advanced Settings",
            "key": "chainPitch"
        },
        {
            "type": "string",
            "title": "Z-Axis Encoder Steps per Revolution",
            "desc": "The number of encoder steps per revolution of the z-axis",
            "section": "Advanced Settings",
            "key": "zEncoderSteps"
        },
        {
            "type": "string",
            "title": "Home Position X Coordinate",
            "desc": "The X coordinate of the home position",
            "section": "Advanced Settings",
            "key": "homeX"
        },
        {
            "type": "string",
            "title": "Home Position Y Coordinate",
            "desc": "The X coordinate of the home position",
            "section": "Advanced Settings",
            "key": "homeY"
        },
        {
            "type": "bool",
            "title": "Truncate Floating Point Numbers",
            "desc": "Truncate floating point numbers at the specified number of decimal places",
            "section": "Advanced Settings",
            "key": "truncate"
        },
        {
            "type": "string",
            "title": "Floating Point Precision",
            "desc": "If truncate floating point numbers is enabled, the number of digits after the decimal place to preserve",
            "section": "Advanced Settings",
            "key": "digits"
        },
        {
            "type": "options",
            "title": "Kinematics Type",
            "desc": "Switch between trapezoidal and triangular kinematics",
            "options": ["Quadrilateral", "Triangular"],
            "section": "Advanced Settings",
            "key": "kinematicsType"
        },
        {
            "type": "string",
            "title": "Rotation Radius for Triangular Kinematics",
            "desc": "The distance between where the chains attach and the center of the router bit in mm",
            "section": "Advanced Settings",
            "key": "rotationRadius"
        },
        {
            "type": "bool",
            "title": "Enable Custom Positional PID Values",
            "desc": "Enable using custom values for the positional PID controller. Turning this off will return to the default values",
            "section": "Advanced Settings",
            "key": "enablePosPIDValues"
        },
        {
            "type": "string",
            "title": "Kp Position",
            "desc": "The proportional constant for the position PID controller",
            "section": "Advanced Settings",
            "key": "KpPos"
        },
        {
            "type": "string",
            "title": "Ki Position",
            "desc": "The integral constant for the position PID controller",
            "section": "Advanced Settings",
            "key": "KiPos"
        },
        {
            "type": "string",
            "title": "Kd Position",
            "desc": "The derivative constant for the position PID controller",
            "section": "Advanced Settings",
            "key": "KdPos"
        },
        {
            "type": "bool",
            "title": "Enable Custom Velocity PID Values",
            "desc": "Enable using custom values for the Velocity PID controller. Turning this off will return to the default values",
            "section": "Advanced Settings",
            "key": "enableVPIDValues"
        },
        {
            "type": "string",
            "title": "Kp Velocity",
            "desc": "The proportional constant for the velocity PID controller",
            "section": "Advanced Settings",
            "key": "KpV"
        },
        {
            "type": "string",
            "title": "Ki Velocity",
            "desc": "The integral constant for the velocity PID controller",
            "section": "Advanced Settings",
            "key": "KiV"
        },
        {
            "type": "string",
            "title": "Kd Velocity",
            "desc": "The derivative constant for the velocity PID controller",
            "section": "Advanced Settings",
            "key": "KdV"
        }
    ]
    '''
    
    gcsettings = '''
    [
        {
            "type": "string",
            "title": "Zoom In",
            "desc": "Pressing this key will zoom in. Note combinations of keys like \'shift\' + \'=\' may not work as expected. Program must be restarted to take effect.",
            "section": "Ground Control Settings",
            "key": "zoomIn"
        },
        {
            "type": "string",
            "title": "Zoom Out",
            "desc": "Pressing this key will zoom in. Note combinations of keys like \'shift\' + \'=\' may not work as expected. Program must be restarted to take effect.",
            "section": "Ground Control Settings",
            "key": "zoomOut"
        },
        {
            "type": "string",
            "title": "Valid File Extensions",
            "desc": "Valid file extensions for Ground Control to open. Comma separated list.",
            "section": "Ground Control Settings",
            "disabled": "True",
            "key": "validExtensions"
        }
    ]
    '''
    
    def build(self):
        Window.maximize()
        
        interface       =  FloatLayout()
        self.data       =  Data()
        
        self.frontpage = FrontPage(self.data, name='FrontPage')
        interface.add_widget(self.frontpage)
        
        self.nonVisibleWidgets = NonVisibleWidgets()
        
        
        '''
        Load User Settings
        '''
        
        self.config.set('Advanced Settings', 'truncate', 0)
        self.config.set('Advanced Settings', 'digits', 4)
        self.config.write()
        
        self.data.comport = self.config.get('Maslow Settings', 'COMport')
        self.data.gcodeFile = self.config.get('Maslow Settings', 'openFile')
        offsetX = float(self.config.get('Advanced Settings', 'homeX'))
        offsetY = float(self.config.get('Advanced Settings', 'homeY'))
        self.data.gcodeShift = [offsetX,offsetY]
        self.data.config  = self.config
        
        
        '''
        Initializations
        '''
        
        self.frontpage.setUpData(self.data)
        self.nonVisibleWidgets.setUpData(self.data)
        self.frontpage.gcodecanvas.initialize()
        
        
        '''
        Scheduling
        '''
        
        Clock.schedule_interval(self.runPeriodically, .01)
        
        '''
        Push settings to machine
        '''
        self.data.bind(connectionStatus = self.push_settings_to_machine)
        self.data.pushSettings = self.push_settings_to_machine
        
        self.push_settings_to_machine()
        
        return interface
        
    def build_config(self, config):
        """
        Set the default values for the config sections.
        """
        config.setdefaults('Maslow Settings', {'COMport'         : '',
                                                 'zAxis'         : 0, 
                                                 'zDistPerRot'   : 3.17, 
                                                 'bedWidth'      : 2438.4, 
                                                 'bedHeight'     : 1219.2, 
                                                 'motorOffsetY'  : 463, 
                                                 'motorSpacingX' : 2978.4, 
                                                 'sledWidth'     : 310, 
                                                 'sledHeight'    : 139, 
                                                 'sledCG'        : 79, 
                                                 'openFile'      : " ",
                                                 'macro1'        : "",
                                                 'macro2'        : ""})

        config.setdefaults('Advanced Settings', {'encoderSteps'       : 8148.0,
                                                 'gearTeeth'          : 10, 
                                                 'chainPitch'         : 6.35,
                                                 'zEncoderSteps'      : 7560.0,
                                                 'homeX'              : 0.0,
                                                 'homeY'              : 0.0,
                                                 'truncate'           : 0,
                                                 'digits'             : 4,
                                                 'kinematicsType'     : 'Quadrilateral',
                                                 'rotationRadius'     : '100',
                                                 'enablePosPIDValues' : 0,
                                                 'KpPos'              : 400,
                                                 'KiPos'              : 5,
                                                 'KdPos'              : 10,
                                                 'enableVPIDValues'   : 0,
                                                 'KpV'                : 20,
                                                 'KiV'                : 1,
                                                 'KdV'                : 0})
        
        config.setdefaults('Ground Control Settings', {'zoomIn': "pageup",
                                                 'validExtensions':".nc, .ngc, .text, .gcode",
                                                 'zoomOut': "pagedown"})

    def build_settings(self, settings):
        """
        Add custom section to the default configuration object.
        """
        settings.add_json_panel('Maslow Settings', self.config, data=self.json)
        settings.add_json_panel('Advanced Settings', self.config, data=self.advanced)
        settings.add_json_panel('Ground Control Settings', self.config, data=self.gcsettings)

    def on_config_change(self, config, section, key, value):
        """
        Respond to changes in the configuration.
        """
        
        if section == "Maslow Settings":
            
            self.push_settings_to_machine()
            
            if key == "COMport":
                self.data.comport = value
            
            if (key == "bedHeight" or key == "bedWidth"):
                self.frontpage.gcodecanvas.drawWorkspace()

        if section == "Advanced Settings":
            
            self.push_settings_to_machine()
            
            if (key == "truncate") or (key == "digits"):
                self.frontpage.gcodecanvas.reloadGcode()
                
            

    def close_settings(self, settings):
        """
        Close settings panel
        """
        super(GroundControlApp, self).close_settings(settings)
    
    def push_settings_to_machine(self, *args):
        
        cmdString = ("B03" 
            +" A" + str(self.data.config.get('Maslow Settings', 'bedWidth'))
            +" C" + str(self.data.config.get('Maslow Settings', 'bedHeight'))
            +" Q" + str(self.data.config.get('Maslow Settings', 'motorSpacingX'))
            +" E" + str(self.data.config.get('Maslow Settings', 'motorOffsetY'))
            +" F" + str(self.data.config.get('Maslow Settings', 'sledWidth'))
            +" R" + str(self.data.config.get('Maslow Settings', 'sledHeight'))
            +" H" + str(self.data.config.get('Maslow Settings', 'sledCG'))
            +" I" + str(self.data.config.get('Maslow Settings', 'zAxis'))
            +" J" + str(self.data.config.get('Advanced Settings', 'encoderSteps'))
            +" K" + str(self.data.config.get('Advanced Settings', 'gearTeeth'))
            +" M" + str(self.data.config.get('Advanced Settings', 'chainPitch'))
            +" N" + str(self.data.config.get('Maslow Settings'  , 'zDistPerRot'))
            +" P" + str(self.data.config.get('Advanced Settings', 'zEncoderSteps'))
            + " "
        )
        
        self.data.gcode_queue.put(cmdString)
        
        #Split the settings push into two so that it doesn't exceed the maximum line length
        
        
        if int(self.data.config.get('Advanced Settings', 'enablePosPIDValues')) == 1:
            KpPos = float(self.data.config.get('Advanced Settings', 'KpPos'))
            KiPos = float(self.data.config.get('Advanced Settings', 'KiPos'))
            KdPos = float(self.data.config.get('Advanced Settings', 'KdPos'))
        else:
            KpPos = 400
            KiPos = 5
            KdPos = 10
        
        if int(self.data.config.get('Advanced Settings', 'enableVPIDValues')) == 1:
            KpV = float(self.data.config.get('Advanced Settings', 'KpV'))
            KiV = float(self.data.config.get('Advanced Settings', 'KiV'))
            KdV = float(self.data.config.get('Advanced Settings', 'KdV'))
        else:
            KpV = 20
            KiV = 1
            KdV = 0
        
        if self.data.config.get('Advanced Settings', 'kinematicsType') == 'Quadrilateral':
            kinematicsType = 1
            print "quadrilateral recognized"
        else:
            kinematicsType = 2
            print "triangular kinematics recognized"
        
        cmdString = ("B03" 
            +" S" + str(KpPos)
            +" T" + str(KiPos)
            +" U" + str(KdPos)
            +" V" + str(KpV)
            +" W" + str(KiV)
            +" X" + str(KdV)
            +" Y" + str(kinematicsType)
            +" Z" + str(self.data.config.get('Advanced Settings', 'rotationRadius'))
            + " "
        )
        
        self.data.gcode_queue.put(cmdString)
    
    '''
    
    Update Functions
    
    '''
    
    def writeToTextConsole(self, message):
        try:
            newText = self.frontpage.consoleText[-500:] + message
            self.frontpage.consoleText = newText
            self.frontpage.textconsole.gotToBottom()  
        except:
            self.frontpage.consoleText = "text not displayed correctly"
    
    def runPeriodically(self, *args):
        '''
        this block should be handled within the appropriate widget
        '''
        while not self.data.message_queue.empty(): #if there is new data to be read
            message = self.data.message_queue.get()
            
            self.data.logger.writeToLog(message)
            
            if message[0] == "<":
                self.setPosOnScreen(message)
            elif message[0] == "[":
                if message[1:4] == "PE:":
                    self.setErrorOnScreen(message)
                elif message[1:8] == "Measure":
                    print "measure seen"
                    print message
                    measuredDist = float(message[9:len(message)-3])
                    self.data.measureRequest(measuredDist)
            elif message[0:8] == "Message:":
                self.previousUploadStatus = self.data.uploadFlag 
                self.data.uploadFlag = 0
                try:
                    self._popup.dismiss()                                           #close any open popup
                except:
                    pass                                                            #there wasn't a popup to close
                content = NotificationPopup(continueOn = self.dismiss_popup_continue, text = message[9:])
                self._popup = Popup(title="Notification: ", content=content,
                            auto_dismiss=False, size_hint=(0.35, 0.35))
                self._popup.open()
            elif message[0:8] == "Firmware":
                 self.writeToTextConsole("Ground Control " + str(self.data.version) + "\r\n" + message + "\r\n")
            elif message == "ok\r\n":
                pass #displaying all the 'ok' messages clutters up the display
            else:
                self.writeToTextConsole(message)
    
    def dismiss_popup_continue(self):
        '''
        
        Close The Pop-up and continue cut
        
        '''
        self._popup.dismiss()
        self.data.quick_queue.put("~") #send cycle resume command to unpause the machine
        self.data.uploadFlag = self.previousUploadStatus #resume cutting if the machine was cutting before
    
    def dismiss_popup_hold(self):
        '''
        
        Close The Pop-up and continue cut
        
        '''
        self._popup.dismiss()
        self.data.uploadFlag = 0 #stop cutting
    
    def setPosOnScreen(self, message):
        '''
        
        This should be moved into the appropriate widget
        
        '''
        
        try:
            startpt = message.find('MPos:') + 5
            
            endpt = message.find('WPos:')
            
            numz  = message[startpt:endpt]
            units = "mm" #message[endpt+1:endpt+3]
            
            valz = numz.split(",")
            
            self.xval  = float(valz[0])
            self.yval  = float(valz[1])
            self.zval  = float(valz[2])
            
            if math.isnan(self.xval):
                self.writeToTextConsole("Unable to resolve x Kinematics.")
                self.xval = 0
            if math.isnan(self.yval):
                self.writeToTextConsole("Unable to resolve y Kinematics.")
                self.yval = 0
            if math.isnan(self.zval):
                self.writeToTextConsole("Unable to resolve z Kinematics.")
                self.zval = 0
            
            self.frontpage.setPosReadout(self.xval,self.yval,self.zval)
            self.frontpage.gcodecanvas.positionIndicator.setPos(self.xval,self.yval,self.data.units)
        except:
            print "One Machine Position Report Command Misread"
            return
        
        
    
    def setErrorOnScreen(self, message):
        
        try:
            startpt = message.find(':')+1 
            endpt = message.find(',', startpt)
            leftErrorValueAsString = message[startpt:endpt]
            leftErrorValueAsFloat  = float(leftErrorValueAsString)
            
            startpt = endpt + 1
            endpt = message.find(',', startpt)
            rightErrorValueAsString = message[startpt:endpt]
            
            rightErrorValueAsFloat  = float(rightErrorValueAsString)
            
            if self.data.units == "INCHES":
                rightErrorValueAsFloat = rightErrorValueAsFloat/25.4
                leftErrorValueAsFloat  = leftErrorValueAsFloat/25.4
            
            avgError = (abs(leftErrorValueAsFloat) + abs(rightErrorValueAsFloat))/2
            
            self.frontpage.gcodecanvas.positionIndicator.setError(0, self.data.units)
            self.data.logger.writeErrorValueToLog(avgError)
            
            self.frontpage.gcodecanvas.targetIndicator.setPos(self.xval - .5*rightErrorValueAsFloat + .5*leftErrorValueAsFloat, self.yval - .5*rightErrorValueAsFloat - .5*leftErrorValueAsFloat,self.data.units)
            
            
        except:
            print "Machine Position Report Command Misread Happened Once"
Beispiel #5
0
from flask_mobility.decorators import mobile_template
from werkzeug import secure_filename
from Background.UIProcessor import UIProcessor  # do this after socketio is declared
from Background.LogStreamer import LogStreamer  # do this after socketio is declared
from Background.WebMCPProcessor import WebMCPProcessor
from Background.WebMCPProcessor import ConsoleProcessor
from DataStructures.data import Data
from Connection.nonVisibleWidgets import NonVisibleWidgets
from WebPageProcessor.webPageProcessor import WebPageProcessor

from os import listdir
from os.path import isfile, join
import sys

app.data = Data()
app.nonVisibleWidgets = NonVisibleWidgets()
app.nonVisibleWidgets.setUpData(app.data)
app.data.config.computeSettings(None, None, None, True)
app.data.config.parseFirmwareVersions()
version = sys.version_info  # this is for python newer than 3.5
if version[:2] > (3, 5):
    app.data.pythonVersion35 = False  # set data flag
    print("Using routines for Python > 3.5")
else:
    app.data.pythonVersion35 = True  # set data flag
    print("Using routines for Python == 3.5")
app.data.units = app.data.config.getValue("Computed Settings", "units")
app.data.tolerance = app.data.config.getValue("Computed Settings", "tolerance")
app.data.distToMove = app.data.config.getValue("Computed Settings",
                                               "distToMove")
app.data.distToMoveZ = app.data.config.getValue("Computed Settings",
Beispiel #6
0
class GroundControlApp(App):
    def get_application_config(self):
        return super(GroundControlApp,
                     self).get_application_config('~/%(appname)s.ini')

    def build(self):

        interface = FloatLayout()
        self.data = Data()

        if self.config.get('Maslow Settings', 'colorScheme') == 'Light':
            self.data.iconPath = './Images/Icons/normal/'
            self.data.fontColor = '[color=7a7a7a]'
            self.data.drawingColor = [.47, .47, .47]
            Window.clearcolor = (1, 1, 1, 1)
            self.data.posIndicatorColor = [0, 0, 0]
            self.data.targetInicatorColor = [1, 0, 0]
        elif self.config.get('Maslow Settings', 'colorScheme') == 'Dark':
            self.data.iconPath = './Images/Icons/highvis/'
            self.data.fontColor = '[color=000000]'
            self.data.drawingColor = [1, 1, 1]
            Window.clearcolor = (0, 0, 0, 1)
            self.data.posIndicatorColor = [1, 1, 1]
            self.data.targetInicatorColor = [1, 0, 0]
        elif self.config.get('Maslow Settings',
                             'colorScheme') == 'DarkGreyBlue':
            self.data.iconPath = './Images/Icons/darkgreyblue/'
            self.data.fontColor = '[color=000000]'
            self.data.drawingColor = [1, 1, 1]
            Window.clearcolor = (0.06, 0.10, 0.2, 1)
            self.data.posIndicatorColor = [0.51, 0.93, 0.97]
            self.data.targetInicatorColor = [1, 0, 0]

        Window.maximize()

        self.frontpage = FrontPage(self.data, name='FrontPage')
        interface.add_widget(self.frontpage)

        self.nonVisibleWidgets = NonVisibleWidgets()
        '''
        Load User Settings
        '''

        # force create an ini no matter what.
        self.config.write()

        if self.config.get('Advanced Settings', 'encoderSteps') == '8148.0':
            self.data.message_queue.put(
                "Message: This update will adjust the the number of encoder pulses per rotation from 8,148 to 8,113 in your settings which improves the positional accuracy.\n\nPerforming a calibration will help you get the most out of this update."
            )
            self.config.set('Advanced Settings', 'encoderSteps', '8113.73')
        #up the maximum feedrate
        if self.config.get('Advanced Settings', 'maxFeedrate') == '700':
            self.data.message_queue.put(
                "Message: This update will increase the maximum feedrate of your machine. You can adjust this value under the Advanced settings."
            )
            self.config.set('Advanced Settings', 'maxFeedrate', '800')
            self.config.write()

        self.data.comport = self.config.get('Maslow Settings', 'COMport')
        self.data.baudRate = int(self.config.get('Maslow Settings',
                                                 'baudRate'))
        self.data.gcodeFile = self.config.get('Maslow Settings', 'openFile')
        offsetX = float(self.config.get('Advanced Settings', 'homeX'))
        offsetY = float(self.config.get('Advanced Settings', 'homeY'))
        self.data.gcodeShift = [offsetX, offsetY]
        self.data.config = self.config
        self.config.add_callback(self.configSettingChange)

        # Background image setup
        self.data.backgroundFile = self.config.get('Background Settings',
                                                   'backgroundFile')
        self.data.backgroundManualReg = json.loads(
            self.config.get('Background Settings', 'manualReg'))
        if self.data.backgroundFile != "":
            BackgroundMenu(self.data).processBackground()
        '''
        Initializations
        '''

        self.frontpage.setUpData(self.data)
        self.nonVisibleWidgets.setUpData(self.data)
        self.frontpage.gcodecanvas.initialize()
        '''
        Scheduling
        '''

        Clock.schedule_interval(self.runPeriodically, .01)
        '''
        Push settings to machine
        '''
        self.data.bind(connectionStatus=self.requestMachineSettings)
        self.data.pushSettings = self.requestMachineSettings

        return interface

    def build_config(self, config):
        """
        Set the default values for the config sections.
        """
        # Calculate computed settings on load
        config.add_callback(self.computeSettings)
        config.setdefaults(
            'Computed Settings',
            maslowSettings.getDefaultValueSection('Computed Settings'))
        config.setdefaults(
            'Maslow Settings',
            maslowSettings.getDefaultValueSection('Maslow Settings'))
        config.setdefaults(
            'Advanced Settings',
            maslowSettings.getDefaultValueSection('Advanced Settings'))
        config.setdefaults(
            'Ground Control Settings',
            maslowSettings.getDefaultValueSection('Ground Control Settings'))
        config.setdefaults(
            'Background Settings',
            maslowSettings.getDefaultValueSection('Background Settings'))
        config.remove_callback(self.computeSettings)

    def build_settings(self, settings):
        """
        Add custom section to the default configuration object.
        """

        settings.add_json_panel(
            'Maslow Settings',
            self.config,
            data=maslowSettings.getJSONSettingSection('Maslow Settings'))
        settings.add_json_panel(
            'Advanced Settings',
            self.config,
            data=maslowSettings.getJSONSettingSection('Advanced Settings'))
        settings.add_json_panel('Ground Control Settings',
                                self.config,
                                data=maslowSettings.getJSONSettingSection(
                                    "Ground Control Settings"))

    def computeSettings(self, section, key, value):
        # Update Computed settings
        if key == 'kinematicsType':
            if value == 'Quadrilateral':
                self.config.set('Computed Settings', 'kinematicsTypeComputed',
                                "1")
            else:
                self.config.set('Computed Settings', 'kinematicsTypeComputed',
                                "2")

        elif (key == 'gearTeeth'
              or key == 'chainPitch') and self.config.has_option(
                  'Advanced Settings', 'gearTeeth') and self.config.has_option(
                      'Advanced Settings', 'chainPitch'):
            distPerRot = float(
                self.config.get('Advanced Settings', 'gearTeeth')) * float(
                    self.config.get('Advanced Settings', 'chainPitch'))
            self.config.set('Computed Settings', "distPerRot", str(distPerRot))

        elif key == 'enablePosPIDValues':
            for key in ('KpPos', 'KiPos', 'KdPos', 'propWeight'):
                if int(
                        self.config.get('Advanced Settings',
                                        'enablePosPIDValues')) == 1:
                    value = float(self.config.get('Advanced Settings', key))
                else:
                    value = maslowSettings.getDefaultValue(
                        'Advanced Settings', key)
                self.config.set('Computed Settings', key + "Main", value)
            #updated computed values for z-axis
            for key in ('KpPosZ', 'KiPosZ', 'KdPosZ', 'propWeightZ'):
                if int(
                        self.config.get('Advanced Settings',
                                        'enablePosPIDValues')) == 1:
                    value = float(self.config.get('Advanced Settings', key))
                else:
                    value = maslowSettings.getDefaultValue(
                        'Advanced Settings', key)
                self.config.set('Computed Settings', key, value)

        elif key == 'enableVPIDValues':
            for key in ('KpV', 'KiV', 'KdV'):
                if int(
                        self.config.get('Advanced Settings',
                                        'enablePosPIDValues')) == 1:
                    value = float(self.config.get('Advanced Settings', key))
                else:
                    value = maslowSettings.getDefaultValue(
                        'Advanced Settings', key)
                self.config.set('Computed Settings', key + "Main", value)
            #updated computed values for z-axis
            for key in ('KpVZ', 'KiVZ', 'KdVZ'):
                if int(
                        self.config.get('Advanced Settings',
                                        'enablePosPIDValues')) == 1:
                    value = float(self.config.get('Advanced Settings', key))
                else:
                    value = maslowSettings.getDefaultValue(
                        'Advanced Settings', key)
                self.config.set('Computed Settings', key, value)

        elif key == 'chainOverSprocket':
            if value == 'Top':
                self.config.set('Computed Settings',
                                'chainOverSprocketComputed', 1)
            else:
                self.config.set('Computed Settings',
                                'chainOverSprocketComputed', 2)

        elif key == 'fPWM':
            if value == '31,000Hz':
                self.config.set('Computed Settings', 'fPWMComputed', 1)
            elif value == '4,100Hz':
                self.config.set('Computed Settings', 'fPWMComputed', 2)
            else:
                self.config.set('Computed Settings', 'fPWMComputed', 3)

    def configSettingChange(self, section, key, value):
        """
        
        Respond to changes in the configuration.
        
        """

        # Update GC things
        if section == "Maslow Settings":
            if key == "COMport":
                self.data.comport = value

            if key == "baudRate":
                self.data.baudRate = int(value)

            if (key == "bedHeight" or key == "bedWidth"):
                self.frontpage.gcodecanvas.drawWorkspace()

            if (key == "macro1_title") or (key == "macro2_title"):
                self.frontpage.update_macro_titles()

        if section == "Advanced Settings":
            if (key == "truncate") or (key == "digits"):
                self.frontpage.gcodecanvas.reloadGcode()
            if (key == "spindleAutomate"):
                if (value == "Servo"):
                    value = 1
                elif (value == "Relay_High"):
                    value = 2
                elif (value == "Relay_Low"):
                    value = 3
                else:
                    value = 0

        # Update Computed Settings
        self.computeSettings(section, key, value)

        # Write the settings change to the Disk
        self.data.config.write()

        # only run on live connection
        if self.data.connectionStatus != 1:
            return

        # Push settings that can be directly written to machine
        firmwareKey = maslowSettings.getFirmwareKey(section, key)
        if firmwareKey is not None:
            self.data.gcode_queue.put("$" + str(firmwareKey) + "=" +
                                      str(value))

    def requestMachineSettings(self, *args):
        ''' 
        Requests the machine to report all settings.  This will implicitly
        cause a sync of the machine settings because if GroundControl sees a
        reported setting which does match its expected value, GC will push the
        correct setting to the machine.
        '''
        if self.data.connectionStatus == 1:
            self.data.gcode_queue.put("$$")

    def receivedSetting(self, message):
        '''
        This parses a settings report from the machine, usually received in 
        response to a $$ request.  If the value received does not match the 
        expected value.
        '''
        parameter, position = self.parseFloat(message, 0)
        value, position = self.parseFloat(message, position)
        if (parameter is not None and value is not None):
            maslowSettings.syncFirmwareKey(int(parameter), value, self.data)

    def parseFloat(self, text, position=0):
        '''
        Takes a string and parses out the float found at position default to 0
        returning a list of the matched float and the ending
        position of the float
        '''
        # This regex comes from a python docs recommended
        regex = re.compile("[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?")
        match = regex.search(text[position:])
        if match:
            return (float(match.group(0)), match.end(0))
        else:
            return (None, position)

    '''
    
    Update Functions
    
    '''

    def writeToTextConsole(self, message):
        try:
            newText = self.frontpage.consoleText[-2000:] + message
            self.frontpage.consoleText = newText
            self.frontpage.textconsole.gotToBottom()
        except:
            self.frontpage.consoleText = "text not displayed correctly"

    def runPeriodically(self, *args):
        '''
        this block should be handled within the appropriate widget
        '''
        while not self.data.message_queue.empty(
        ):  #if there is new data to be read
            message = self.data.message_queue.get()

            if message[0] == "<":
                self.setPosOnScreen(message)
            elif message[0] == "$":
                self.receivedSetting(message)
            elif message[0] == "[":
                if message[1:4] == "PE:":
                    self.setErrorOnScreen(message)
                elif message[1:8] == "Measure":
                    measuredDist = float(message[9:len(message) - 3])
                    try:
                        self.data.measureRequest(measuredDist)
                    except:
                        print "No function has requested a measurement"
            elif message[0:13] == "Maslow Paused":
                self.data.uploadFlag = 0
                self.writeToTextConsole(message)
            elif message[0:8] == "Message:":
                if self.data.calibrationInProcess and message[
                        0:
                        15] == "Message: Unable":  #this suppresses the annoying messages about invalid chain lengths during the calibration process
                    break
                self.previousUploadStatus = self.data.uploadFlag
                self.data.uploadFlag = 0
                try:
                    self._popup.dismiss()  #close any open popup
                except:
                    pass  #there wasn't a popup to close
                content = NotificationPopup(
                    continueOn=self.dismiss_popup_continue, text=message[9:])
                if sys.platform.startswith('darwin'):
                    self._popup = Popup(title="Notification: ",
                                        content=content,
                                        auto_dismiss=False,
                                        size=(360, 240),
                                        size_hint=(.3, .3))
                else:
                    self._popup = Popup(title="Notification: ",
                                        content=content,
                                        auto_dismiss=False,
                                        size=(360, 240),
                                        size_hint=(None, None))
                self._popup.open()
                if global_variables._keyboard:
                    global_variables._keyboard.bind(
                        on_key_down=self.keydown_popup)
                    self._popup.bind(on_dismiss=self.ondismiss_popup)
            elif message[0:6] == "ALARM:":
                self.previousUploadStatus = self.data.uploadFlag
                self.data.uploadFlag = 0
                try:
                    self._popup.dismiss()  #close any open popup
                except:
                    pass  #there wasn't a popup to close
                content = NotificationPopup(
                    continueOn=self.dismiss_popup_continue, text=message[7:])
                if sys.platform.startswith('darwin'):
                    self._popup = Popup(title="Alarm Notification: ",
                                        content=content,
                                        auto_dismiss=False,
                                        size=(360, 240),
                                        size_hint=(.3, .3))
                else:
                    self._popup = Popup(title="Alarm Notification: ",
                                        content=content,
                                        auto_dismiss=False,
                                        size=(360, 240),
                                        size_hint=(None, None))
                self._popup.open()
                if global_variables._keyboard:
                    global_variables._keyboard.bind(
                        on_key_down=self.keydown_popup)
                    self._popup.bind(on_dismiss=self.ondismiss_popup)
            elif message[0:8] == "Firmware":
                self.data.logger.writeToLog("Ground Control Version " +
                                            str(self.data.version) + "\n")
                self.writeToTextConsole("Ground Control " +
                                        str(self.data.version) + "\r\n" +
                                        message + "\r\n")

                #Check that version numbers match
                if float(message[-7:]) < float(self.data.version):
                    self.data.message_queue.put(
                        "Message: Warning, your firmware is out of date and may not work correctly with this version of Ground Control\n\n"
                        + "Ground Control Version " + str(self.data.version) +
                        "\r\n" + message)
                if float(message[-7:]) > float(self.data.version):
                    self.data.message_queue.put(
                        "Message: Warning, your version of Ground Control is out of date and may not work with this firmware version\n\n"
                        + "Ground Control Version " + str(self.data.version) +
                        "\r\n" + message)
            elif message == "ok\r\n":
                pass  #displaying all the 'ok' messages clutters up the display
            else:
                self.writeToTextConsole(message)

    def ondismiss_popup(self, event):
        if global_variables._keyboard:
            global_variables._keyboard.unbind(on_key_down=self.keydown_popup)

    def keydown_popup(self, keyboard, keycode, text, modifiers):
        if (keycode[1] == 'enter') or (keycode[1]
                                       == 'numpadenter') or (keycode[1]
                                                             == 'escape'):
            self.dismiss_popup_continue()
        return True  # always swallow keypresses since this is a modal dialog

    def dismiss_popup_continue(self):
        '''
        
        Close The Pop-up and continue cut
        
        '''
        self._popup.dismiss()
        self.data.quick_queue.put(
            "~")  #send cycle resume command to unpause the machine
        self.data.uploadFlag = self.previousUploadStatus  #resume cutting if the machine was cutting before

    def dismiss_popup_hold(self):
        '''
        
        Close The Pop-up and continue cut
        
        '''
        self._popup.dismiss()
        self.data.uploadFlag = 0  #stop cutting

    def setPosOnScreen(self, message):
        '''
        
        This should be moved into the appropriate widget
        
        '''

        try:
            startpt = message.find('MPos:') + 5

            endpt = message.find('WPos:')

            numz = message[startpt:endpt]
            units = "mm"  #message[endpt+1:endpt+3]

            valz = numz.split(",")

            self.xval = float(valz[0])
            self.yval = float(valz[1])
            self.zval = float(valz[2])

            if math.isnan(self.xval):
                self.writeToTextConsole("Unable to resolve x Kinematics.")
                self.xval = 0
            if math.isnan(self.yval):
                self.writeToTextConsole("Unable to resolve y Kinematics.")
                self.yval = 0
            if math.isnan(self.zval):
                self.writeToTextConsole("Unable to resolve z Kinematics.")
                self.zval = 0
        except:
            print "One Machine Position Report Command Misread"
            return

        self.frontpage.setPosReadout(self.xval, self.yval, self.zval)
        self.frontpage.gcodecanvas.positionIndicator.setPos(
            self.xval, self.yval, self.data.units)

    def setErrorOnScreen(self, message):

        try:
            startpt = message.find(':') + 1
            endpt = message.find(',', startpt)
            leftErrorValueAsString = message[startpt:endpt]
            leftErrorValueAsFloat = float(leftErrorValueAsString)

            startpt = endpt + 1
            endpt = message.find(',', startpt)
            rightErrorValueAsString = message[startpt:endpt]

            rightErrorValueAsFloat = float(rightErrorValueAsString)

            if self.data.units == "INCHES":
                rightErrorValueAsFloat = rightErrorValueAsFloat / 25.4
                leftErrorValueAsFloat = leftErrorValueAsFloat / 25.4

            avgError = (abs(leftErrorValueAsFloat) +
                        abs(rightErrorValueAsFloat)) / 2

            self.frontpage.gcodecanvas.positionIndicator.setError(
                0, self.data.units)
            self.data.logger.writeErrorValueToLog(avgError)

            self.frontpage.gcodecanvas.targetIndicator.setPos(
                self.xval - .5 * rightErrorValueAsFloat +
                .5 * leftErrorValueAsFloat, self.yval -
                .5 * rightErrorValueAsFloat - .5 * leftErrorValueAsFloat,
                self.data.units)

        except:
            print "Machine Position Report Command Misread Happened Once"
Beispiel #7
0
    def build(self):

        interface = FloatLayout()
        self.data = Data()

        if self.config.get('Maslow Settings', 'colorScheme') == 'Light':
            self.data.iconPath = './Images/Icons/normal/'
            self.data.fontColor = '[color=7a7a7a]'
            self.data.drawingColor = [.47, .47, .47]
            Window.clearcolor = (1, 1, 1, 1)
            self.data.posIndicatorColor = [0, 0, 0]
            self.data.targetInicatorColor = [1, 0, 0]
        elif self.config.get('Maslow Settings', 'colorScheme') == 'Dark':
            self.data.iconPath = './Images/Icons/highvis/'
            self.data.fontColor = '[color=000000]'
            self.data.drawingColor = [1, 1, 1]
            Window.clearcolor = (0, 0, 0, 1)
            self.data.posIndicatorColor = [1, 1, 1]
            self.data.targetInicatorColor = [1, 0, 0]
        elif self.config.get('Maslow Settings',
                             'colorScheme') == 'DarkGreyBlue':
            self.data.iconPath = './Images/Icons/darkgreyblue/'
            self.data.fontColor = '[color=000000]'
            self.data.drawingColor = [1, 1, 1]
            Window.clearcolor = (0.06, 0.10, 0.2, 1)
            self.data.posIndicatorColor = [0.51, 0.93, 0.97]
            self.data.targetInicatorColor = [1, 0, 0]

        Window.maximize()

        self.frontpage = FrontPage(self.data, name='FrontPage')
        interface.add_widget(self.frontpage)

        self.nonVisibleWidgets = NonVisibleWidgets()
        '''
        Load User Settings
        '''

        # force create an ini no matter what.
        self.config.write()

        if self.config.get('Advanced Settings', 'encoderSteps') == '8148.0':
            self.data.message_queue.put(
                "Message: This update will adjust the the number of encoder pulses per rotation from 8,148 to 8,113 in your settings which improves the positional accuracy.\n\nPerforming a calibration will help you get the most out of this update."
            )
            self.config.set('Advanced Settings', 'encoderSteps', '8113.73')
        #up the maximum feedrate
        if self.config.get('Advanced Settings', 'maxFeedrate') == '700':
            self.data.message_queue.put(
                "Message: This update will increase the maximum feedrate of your machine. You can adjust this value under the Advanced settings."
            )
            self.config.set('Advanced Settings', 'maxFeedrate', '800')
            self.config.write()

        self.data.comport = self.config.get('Maslow Settings', 'COMport')
        self.data.baudRate = int(self.config.get('Maslow Settings',
                                                 'baudRate'))
        self.data.gcodeFile = self.config.get('Maslow Settings', 'openFile')
        offsetX = float(self.config.get('Advanced Settings', 'homeX'))
        offsetY = float(self.config.get('Advanced Settings', 'homeY'))
        self.data.gcodeShift = [offsetX, offsetY]
        self.data.config = self.config
        self.config.add_callback(self.configSettingChange)

        # Background image setup
        self.data.backgroundFile = self.config.get('Background Settings',
                                                   'backgroundFile')
        self.data.backgroundManualReg = json.loads(
            self.config.get('Background Settings', 'manualReg'))
        if self.data.backgroundFile != "":
            BackgroundMenu(self.data).processBackground()
        '''
        Initializations
        '''

        self.frontpage.setUpData(self.data)
        self.nonVisibleWidgets.setUpData(self.data)
        self.frontpage.gcodecanvas.initialize()
        '''
        Scheduling
        '''

        Clock.schedule_interval(self.runPeriodically, .01)
        '''
        Push settings to machine
        '''
        self.data.bind(connectionStatus=self.requestMachineSettings)
        self.data.pushSettings = self.requestMachineSettings

        return interface
    def build(self):
        
        interface       =  FloatLayout()
        self.data       =  Data()
        
        if self.config.get('Maslow Settings', 'colorScheme') == 'Light':
            self.data.iconPath               = './Images/Icons/normal/'
            self.data.fontColor              = '[color=7a7a7a]'
            self.data.drawingColor           = [.47,.47,.47]
            Window.clearcolor                = (1, 1, 1, 1)
            self.data.posIndicatorColor      =  [0,0,0]
            self.data.targetInicatorColor    =  [1,0,0]
        elif self.config.get('Maslow Settings', 'colorScheme') == 'Dark':
            self.data.iconPath               = './Images/Icons/highvis/'
            self.data.fontColor              = '[color=000000]'
            self.data.drawingColor           = [1,1,1]
            Window.clearcolor                = (0, 0, 0, 1)
            self.data.posIndicatorColor      =  [1,1,1]
            self.data.targetInicatorColor    =  [1,0,0]
        elif self.config.get('Maslow Settings', 'colorScheme') == 'DarkGreyBlue':
            self.data.iconPath               = './Images/Icons/darkgreyblue/'
            self.data.fontColor              = '[color=000000]'
            self.data.drawingColor           = [1,1,1]
            Window.clearcolor                = (0.06, 0.10, 0.2, 1)
            self.data.posIndicatorColor      =  [0.51,0.93,0.97]
            self.data.targetInicatorColor = [1,0,0]

        
        
        Window.maximize()
        
        
        self.frontpage = FrontPage(self.data, name='FrontPage')
        interface.add_widget(self.frontpage)
        
        self.nonVisibleWidgets = NonVisibleWidgets()
        
        
        '''
        Load User Settings
        '''
        
        # force create an ini no matter what.
        self.config.write()

        if self.config.get('Advanced Settings', 'encoderSteps') == '8148.0':
            self.data.message_queue.put("Message: This update will adjust the the number of encoder pulses per rotation from 8,148 to 8,113 in your settings which improves the positional accuracy.\n\nPerforming a calibration will help you get the most out of this update.")
            self.config.set('Advanced Settings', 'encoderSteps', '8113.73')
        #up the maximum feedrate
        if self.config.get('Advanced Settings', 'maxFeedrate') == '700':
            self.data.message_queue.put("Message: This update will increase the maximum feedrate of your machine. You can adjust this value under the Advanced settings.")
            self.config.set('Advanced Settings', 'maxFeedrate', '800')
            self.config.write()
        
        self.data.comport = self.config.get('Maslow Settings', 'COMport')
        self.data.gcodeFile = self.config.get('Maslow Settings', 'openFile')
        offsetX = float(self.config.get('Advanced Settings', 'homeX'))
        offsetY = float(self.config.get('Advanced Settings', 'homeY'))
        self.data.gcodeShift = [offsetX,offsetY]
        self.data.config  = self.config
        self.config.add_callback(self.configSettingChange)

        # Background image setup
        self.data.backgroundFile = self.config.get('Background Settings',
                                                   'backgroundFile')
        self.data.backgroundManualReg = json.loads(
                        self.config.get('Background Settings', 'manualReg'))
        if self.data.backgroundFile != "":
            BackgroundMenu(self.data).processBackground()
        
        '''
        Initializations
        '''
        
        self.frontpage.setUpData(self.data)
        self.nonVisibleWidgets.setUpData(self.data)
        self.frontpage.gcodecanvas.initialize()
        
        '''
        Scheduling
        '''
        
        Clock.schedule_interval(self.runPeriodically, .01)
        
        '''
        Push settings to machine
        '''
        self.data.bind(connectionStatus = self.requestMachineSettings)
        self.data.pushSettings = self.requestMachineSettings
        
        return interface
class GroundControlApp(App):

    def get_application_config(self):
        return super(GroundControlApp, self).get_application_config(
            '~/%(appname)s.ini')
    
    def build(self):
        
        interface       =  FloatLayout()
        self.data       =  Data()
        
        if self.config.get('Maslow Settings', 'colorScheme') == 'Light':
            self.data.iconPath               = './Images/Icons/normal/'
            self.data.fontColor              = '[color=7a7a7a]'
            self.data.drawingColor           = [.47,.47,.47]
            Window.clearcolor                = (1, 1, 1, 1)
            self.data.posIndicatorColor      =  [0,0,0]
            self.data.targetInicatorColor    =  [1,0,0]
        elif self.config.get('Maslow Settings', 'colorScheme') == 'Dark':
            self.data.iconPath               = './Images/Icons/highvis/'
            self.data.fontColor              = '[color=000000]'
            self.data.drawingColor           = [1,1,1]
            Window.clearcolor                = (0, 0, 0, 1)
            self.data.posIndicatorColor      =  [1,1,1]
            self.data.targetInicatorColor    =  [1,0,0]
        elif self.config.get('Maslow Settings', 'colorScheme') == 'DarkGreyBlue':
            self.data.iconPath               = './Images/Icons/darkgreyblue/'
            self.data.fontColor              = '[color=000000]'
            self.data.drawingColor           = [1,1,1]
            Window.clearcolor                = (0.06, 0.10, 0.2, 1)
            self.data.posIndicatorColor      =  [0.51,0.93,0.97]
            self.data.targetInicatorColor = [1,0,0]

        
        
        Window.maximize()
        
        
        self.frontpage = FrontPage(self.data, name='FrontPage')
        interface.add_widget(self.frontpage)
        
        self.nonVisibleWidgets = NonVisibleWidgets()
        
        
        '''
        Load User Settings
        '''
        
        # force create an ini no matter what.
        self.config.write()

        if self.config.get('Advanced Settings', 'encoderSteps') == '8148.0':
            self.data.message_queue.put("Message: This update will adjust the the number of encoder pulses per rotation from 8,148 to 8,113 in your settings which improves the positional accuracy.\n\nPerforming a calibration will help you get the most out of this update.")
            self.config.set('Advanced Settings', 'encoderSteps', '8113.73')
        #up the maximum feedrate
        if self.config.get('Advanced Settings', 'maxFeedrate') == '700':
            self.data.message_queue.put("Message: This update will increase the maximum feedrate of your machine. You can adjust this value under the Advanced settings.")
            self.config.set('Advanced Settings', 'maxFeedrate', '800')
            self.config.write()
        
        self.data.comport = self.config.get('Maslow Settings', 'COMport')
        self.data.gcodeFile = self.config.get('Maslow Settings', 'openFile')
        offsetX = float(self.config.get('Advanced Settings', 'homeX'))
        offsetY = float(self.config.get('Advanced Settings', 'homeY'))
        self.data.gcodeShift = [offsetX,offsetY]
        self.data.config  = self.config
        self.config.add_callback(self.configSettingChange)

        # Background image setup
        self.data.backgroundFile = self.config.get('Background Settings',
                                                   'backgroundFile')
        self.data.backgroundManualReg = json.loads(
                        self.config.get('Background Settings', 'manualReg'))
        if self.data.backgroundFile != "":
            BackgroundMenu(self.data).processBackground()
        
        '''
        Initializations
        '''
        
        self.frontpage.setUpData(self.data)
        self.nonVisibleWidgets.setUpData(self.data)
        self.frontpage.gcodecanvas.initialize()
        
        '''
        Scheduling
        '''
        
        Clock.schedule_interval(self.runPeriodically, .01)
        
        '''
        Push settings to machine
        '''
        self.data.bind(connectionStatus = self.requestMachineSettings)
        self.data.pushSettings = self.requestMachineSettings
        
        return interface
        
    def build_config(self, config):
        """
        Set the default values for the config sections.
        """
        # Calculate computed settings on load
        config.add_callback(self.computeSettings)
        config.setdefaults('Computed Settings', maslowSettings.getDefaultValueSection('Computed Settings'))
        config.setdefaults('Maslow Settings', maslowSettings.getDefaultValueSection('Maslow Settings'))
        config.setdefaults('Advanced Settings', maslowSettings.getDefaultValueSection('Advanced Settings'))
        config.setdefaults('Ground Control Settings', maslowSettings.getDefaultValueSection('Ground Control Settings'))
        config.setdefaults('Background Settings', maslowSettings.getDefaultValueSection('Background Settings'))
        config.remove_callback(self.computeSettings)
        
    def build_settings(self, settings):
        """
        Add custom section to the default configuration object.
        """
        
        settings.add_json_panel('Maslow Settings', self.config, data=maslowSettings.getJSONSettingSection('Maslow Settings'))
        settings.add_json_panel('Advanced Settings', self.config, data=maslowSettings.getJSONSettingSection('Advanced Settings'))
        settings.add_json_panel('Ground Control Settings', self.config, data=maslowSettings.getJSONSettingSection("Ground Control Settings"))
        

    def computeSettings(self, section, key, value):
        # Update Computed settings
        if key == 'kinematicsType':
            if value == 'Quadrilateral':
                self.config.set('Computed Settings', 'kinematicsTypeComputed', "1")
            else:
                self.config.set('Computed Settings', 'kinematicsTypeComputed', "2")

        elif (key == 'gearTeeth' or key == 'chainPitch') and self.config.has_option('Advanced Settings', 'gearTeeth') and self.config.has_option('Advanced Settings', 'chainPitch'):
            distPerRot = float(self.config.get('Advanced Settings', 'gearTeeth')) * float(self.config.get('Advanced Settings', 'chainPitch'))
            self.config.set('Computed Settings', "distPerRot", str(distPerRot))

        elif key == 'enablePosPIDValues':
            for key in ('KpPos', 'KiPos', 'KdPos', 'propWeight'):
                if int(self.config.get('Advanced Settings', 'enablePosPIDValues')) == 1:
                    value = float(self.config.get('Advanced Settings', key))
                else:
                    value = maslowSettings.getDefaultValue('Advanced Settings', key)
                self.config.set('Computed Settings', key + "Main", value)
            #updated computed values for z-axis
            for key in ('KpPosZ', 'KiPosZ', 'KdPosZ', 'propWeightZ'):
                if int(self.config.get('Advanced Settings', 'enablePosPIDValues')) == 1:
                    value = float(self.config.get('Advanced Settings', key))
                else:
                    value = maslowSettings.getDefaultValue('Advanced Settings', key)
                self.config.set('Computed Settings', key, value)

        elif key == 'enableVPIDValues':
            for key in ('KpV', 'KiV', 'KdV'):
                if int(self.config.get('Advanced Settings', 'enablePosPIDValues')) == 1:
                    value = float(self.config.get('Advanced Settings', key))
                else:
                    value = maslowSettings.getDefaultValue('Advanced Settings', key)
                self.config.set('Computed Settings', key + "Main", value)
            #updated computed values for z-axis
            for key in ('KpVZ', 'KiVZ', 'KdVZ'):
                if int(self.config.get('Advanced Settings', 'enablePosPIDValues')) == 1:
                    value = float(self.config.get('Advanced Settings', key))
                else:
                    value = maslowSettings.getDefaultValue('Advanced Settings', key)
                self.config.set('Computed Settings', key, value)
        
        elif key == 'chainOverSprocket':
            if value == 'Top':
                self.config.set('Computed Settings',  'chainOverSprocketComputed', 1)
            else:
                self.config.set('Computed Settings',  'chainOverSprocketComputed', 2)

        elif key == 'fPWM':
            if value == '31,000Hz':
                self.config.set('Computed Settings',  'fPWMComputed', 1)
            elif value == '4,100Hz':
                self.config.set('Computed Settings',  'fPWMComputed', 2)
            else: 
                self.config.set('Computed Settings',  'fPWMComputed', 3)

    def configSettingChange(self, section, key, value):
        """
        
        Respond to changes in the configuration.
        
        """
        
        # Update GC things
        if section == "Maslow Settings":
            if key == "COMport":
                self.data.comport = value
            
            if (key == "bedHeight" or key == "bedWidth"):
                self.frontpage.gcodecanvas.drawWorkspace()

            if (key == "macro1_title") or (key == "macro2_title"):
                self.frontpage.update_macro_titles()

        if section == "Advanced Settings":
            if (key == "truncate") or (key == "digits"):
                self.frontpage.gcodecanvas.reloadGcode()
            if (key == "spindleAutomate"):
                if (value == "Servo"):
                    value = 1
                elif (value == "Relay_High"):
                    value = 2
                elif (value == "Relay_Low"):
                    value = 3
                else:
                    value = 0
    
        
        # Update Computed Settings
        self.computeSettings(section, key, value)
        
        # Write the settings change to the Disk
        self.data.config.write()

        # only run on live connection
        if self.data.connectionStatus != 1:
            return
        
        # Push settings that can be directly written to machine
        firmwareKey = maslowSettings.getFirmwareKey(section, key)
        if firmwareKey is not None:
            self.data.gcode_queue.put("$" + str(firmwareKey) + "=" + str(value))
    
    def requestMachineSettings(self, *args):
        ''' 
        Requests the machine to report all settings.  This will implicitly
        cause a sync of the machine settings because if GroundControl sees a
        reported setting which does match its expected value, GC will push the
        correct setting to the machine.
        '''
        if self.data.connectionStatus == 1:
            self.data.gcode_queue.put("$$")
    
    def receivedSetting(self, message):
        '''
        This parses a settings report from the machine, usually received in 
        response to a $$ request.  If the value received does not match the 
        expected value.
        '''
        parameter, position = self.parseFloat(message, 0)
        value, position = self.parseFloat(message, position)
        if (parameter is not None and value is not None):
            maslowSettings.syncFirmwareKey(int(parameter), value, self.data)
    
    def parseFloat(self, text, position=0):
        '''
        Takes a string and parses out the float found at position default to 0
        returning a list of the matched float and the ending
        position of the float
        '''
        # This regex comes from a python docs recommended 
        regex = re.compile("[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?")
        match = regex.search(text[position:])
        if match:
            return (float(match.group(0)), match.end(0))
        else:
            return (None, position)
    
    '''
    
    Update Functions
    
    '''
    
    def writeToTextConsole(self, message):
        try:
            newText = self.frontpage.consoleText[-2000:] + message
            self.frontpage.consoleText = newText
            self.frontpage.textconsole.gotToBottom()  
        except:
            self.frontpage.consoleText = "text not displayed correctly"
    
    def runPeriodically(self, *args):
        '''
        this block should be handled within the appropriate widget
        '''
        while not self.data.message_queue.empty(): #if there is new data to be read
            message = self.data.message_queue.get()
            
            if message[0] == "<":
                self.setPosOnScreen(message)
            elif message[0] == "$":
                self.receivedSetting(message)
            elif message[0] == "[":
                if message[1:4] == "PE:":
                    self.setErrorOnScreen(message)
                elif message[1:8] == "Measure":
                    measuredDist = float(message[9:len(message)-3])
                    try:
                        self.data.measureRequest(measuredDist)
                    except:
                        print "No function has requested a measurement"
            elif message[0:13] == "Maslow Paused":
                self.data.uploadFlag = 0
                self.writeToTextConsole(message)
            elif message[0:8] == "Message:":
                if self.data.calibrationInProcess and message[0:15] == "Message: Unable":   #this suppresses the annoying messages about invalid chain lengths during the calibration process
                    break
                self.previousUploadStatus = self.data.uploadFlag 
                self.data.uploadFlag = 0
                try:
                    self._popup.dismiss()                                           #close any open popup
                except:
                    pass                                                            #there wasn't a popup to close
                content = NotificationPopup(continueOn = self.dismiss_popup_continue, text = message[9:])
                if sys.platform.startswith('darwin'):
                    self._popup = Popup(title="Notification: ", content=content,
                            auto_dismiss=False, size=(360,240), size_hint=(.3, .3))
                else:
                    self._popup = Popup(title="Notification: ", content=content,
                            auto_dismiss=False, size=(360,240), size_hint=(None, None))
                self._popup.open()
                if global_variables._keyboard:
                    global_variables._keyboard.bind(on_key_down=self.keydown_popup)
                    self._popup.bind(on_dismiss=self.ondismiss_popup)
            elif message[0:6] == "ALARM:":
                self.previousUploadStatus = self.data.uploadFlag 
                self.data.uploadFlag = 0
                try:
                    self._popup.dismiss()                                           #close any open popup
                except:
                    pass                                                            #there wasn't a popup to close
                content = NotificationPopup(continueOn = self.dismiss_popup_continue, text = message[7:])
                if sys.platform.startswith('darwin'):
                    self._popup = Popup(title="Alarm Notification: ", content=content,
                            auto_dismiss=False, size=(360,240), size_hint=(.3, .3))
                else:
                    self._popup = Popup(title="Alarm Notification: ", content=content,
                            auto_dismiss=False, size=(360,240), size_hint=(None, None))
                self._popup.open()
                if global_variables._keyboard:
                    global_variables._keyboard.bind(on_key_down=self.keydown_popup)
                    self._popup.bind(on_dismiss=self.ondismiss_popup)
            elif message[0:8] == "Firmware":
                self.data.logger.writeToLog("Ground Control Version " + str(self.data.version) + "\n")
                self.writeToTextConsole("Ground Control " + str(self.data.version) + "\r\n" + message + "\r\n")
                
                #Check that version numbers match
                if float(message[-7:]) < float(self.data.version):
                    self.data.message_queue.put("Message: Warning, your firmware is out of date and may not work correctly with this version of Ground Control\n\n" + "Ground Control Version " + str(self.data.version) + "\r\n" + message)
                if float(message[-7:]) > float(self.data.version):
                    self.data.message_queue.put("Message: Warning, your version of Ground Control is out of date and may not work with this firmware version\n\n" + "Ground Control Version " + str(self.data.version) + "\r\n" + message)
            elif message == "ok\r\n":
                pass #displaying all the 'ok' messages clutters up the display
            else:
                self.writeToTextConsole(message)

    def ondismiss_popup(self, event):
        if global_variables._keyboard:
            global_variables._keyboard.unbind(on_key_down=self.keydown_popup)

    def keydown_popup(self, keyboard, keycode, text, modifiers):
        if (keycode[1] == 'enter') or (keycode[1] =='numpadenter') or (keycode[1] == 'escape'):
            self.dismiss_popup_continue()
        return True     # always swallow keypresses since this is a modal dialog
        
    
    def dismiss_popup_continue(self):
        '''
        
        Close The Pop-up and continue cut
        
        '''
        self._popup.dismiss()
        self.data.quick_queue.put("~") #send cycle resume command to unpause the machine
        self.data.uploadFlag = self.previousUploadStatus #resume cutting if the machine was cutting before
    
    def dismiss_popup_hold(self):
        '''
        
        Close The Pop-up and continue cut
        
        '''
        self._popup.dismiss()
        self.data.uploadFlag = 0 #stop cutting
    
    def setPosOnScreen(self, message):
        '''
        
        This should be moved into the appropriate widget
        
        '''
        
        try:
            startpt = message.find('MPos:') + 5
            
            endpt = message.find('WPos:')
            
            numz  = message[startpt:endpt]
            units = "mm" #message[endpt+1:endpt+3]
            
            valz = numz.split(",")
            
            self.xval  = float(valz[0])
            self.yval  = float(valz[1])
            self.zval  = float(valz[2])

            if math.isnan(self.xval):
                self.writeToTextConsole("Unable to resolve x Kinematics.")
                self.xval = 0
            if math.isnan(self.yval):
                self.writeToTextConsole("Unable to resolve y Kinematics.")
                self.yval = 0
            if math.isnan(self.zval):
                self.writeToTextConsole("Unable to resolve z Kinematics.")
                self.zval = 0
        except:
            print "One Machine Position Report Command Misread"
            return

        self.frontpage.setPosReadout(self.xval, self.yval, self.zval)
        self.frontpage.gcodecanvas.positionIndicator.setPos(self.xval,self.yval,self.data.units)
    
    def setErrorOnScreen(self, message):
        
        try:
            startpt = message.find(':')+1 
            endpt = message.find(',', startpt)
            leftErrorValueAsString = message[startpt:endpt]
            leftErrorValueAsFloat  = float(leftErrorValueAsString)
            
            startpt = endpt + 1
            endpt = message.find(',', startpt)
            rightErrorValueAsString = message[startpt:endpt]
            
            rightErrorValueAsFloat  = float(rightErrorValueAsString)
            
            if self.data.units == "INCHES":
                rightErrorValueAsFloat = rightErrorValueAsFloat/25.4
                leftErrorValueAsFloat  = leftErrorValueAsFloat/25.4
            
            avgError = (abs(leftErrorValueAsFloat) + abs(rightErrorValueAsFloat))/2
            
            self.frontpage.gcodecanvas.positionIndicator.setError(0, self.data.units)
            self.data.logger.writeErrorValueToLog(avgError)
            
            self.frontpage.gcodecanvas.targetIndicator.setPos(self.xval - .5*rightErrorValueAsFloat + .5*leftErrorValueAsFloat, self.yval - .5*rightErrorValueAsFloat - .5*leftErrorValueAsFloat,self.data.units)
            
            
        except:
            print "Machine Position Report Command Misread Happened Once"
Beispiel #10
0
class GroundControlApp(App):
    def get_application_config(self):
        return super(GroundControlApp,
                     self).get_application_config('~/%(appname)s.ini')

    json = '''
    [
        {
            "type": "string",
            "title": "Serial Connection",
            "desc": "Select the COM port to connect to machine",
            "section": "Maslow Settings",
            "key": "COMport"
        },
        {
            "type": "string",
            "title": "Distance Between Motors",
            "desc": "The horizontal distance between the center of the motor shafts in MM.",
            "section": "Maslow Settings",
            "key": "motorSpacingX"
            
        },
        {
            "type": "string",
            "title": "Work Area Width in MM",
            "desc": "The width of the machine working area (normally 8 feet).",
            "section": "Maslow Settings",
            "key": "bedWidth"
        },
        {
            "type": "string",
            "title": "Work Area Height in MM",
            "desc": "The Height of the machine working area (normally 4 feet).",
            "section": "Maslow Settings",
            "key": "bedHeight"
        },
        {
            "type": "string",
            "title": "Motor Offset Height in MM",
            "desc": "The vertical distance from the edge of the work area to the level of the motors.",
            "section": "Maslow Settings",
            "key": "motorOffsetY"
        },
        {
            "type": "string",
            "title": "Distance Between Sled Mounting Points",
            "desc": "The horizontal distance between the points where the chains mount to the sled.",
            "section": "Maslow Settings",
            "key": "sledWidth"
        },
        {
            "type": "string",
            "title": "Vertical Distance Sled Mounts to Cutter",
            "desc": "The vertical distance between where the chains mount on the sled to the cutting tool.",
            "section": "Maslow Settings",
            "key": "sledHeight"
        },
        {
            "type": "string",
            "title": "Center Of Gravity",
            "desc": "How far below the cutting bit is the center of gravity. This can be found by resting the sled on a round object and observing where it balances.",
            "section": "Maslow Settings",
            "key": "sledCG"
        },
        {
            "type": "bool",
            "title": "z-axis installed",
            "desc": "Does the machine have an automatic z-axis?",
            "section": "Maslow Settings",
            "key": "zAxis"
        },
        {
            "type": "string",
            "title": "Z-Axis Pitch",
            "desc": "The number of mm moved per rotation of the z-axis",
            "section": "Maslow Settings",
            "key": "zDistPerRot"
        },
        {
            "type": "string",
            "title": "Open File",
            "desc": "The path to the open file",
            "section": "Maslow Settings",
            "key": "openFile"
        }
    ]
    '''

    advanced = '''
    [
        {
            "type": "string",
            "title": "Encoder Steps per Revolution",
            "desc": "The number of encoder steps per revolution of the left or right motor",
            "section": "Advanced Settings",
            "key": "encoderSteps"
        },
        {
            "type": "string",
            "title": "Gear Teeth",
            "desc": "The number of teeth on the gear of the left or right motor",
            "section": "Advanced Settings",
            "key": "gearTeeth"
        },
        {
            "type": "string",
            "title": "Chain Pitch",
            "desc": "The distance between chain roller centers",
            "section": "Advanced Settings",
            "key": "chainPitch"
        },
        {
            "type": "string",
            "title": "Z-Axis Encoder Steps per Revolution",
            "desc": "The number of encoder steps per revolution of the z-axis",
            "section": "Advanced Settings",
            "key": "zEncoderSteps"
        }
    ]
    '''

    gcsettings = '''
    [
        {
            "type": "string",
            "title": "Zoom In",
            "desc": "Pressing this key will zoom in. Note combinations of keys like \'shift\' + \'=\' may not work as expected. Program must be restarted to take effect.",
            "section": "Ground Control Settings",
            "key": "zoomIn"
        },
        {
            "type": "string",
            "title": "Zoom Out",
            "desc": "Pressing this key will zoom in. Note combinations of keys like \'shift\' + \'=\' may not work as expected. Program must be restarted to take effect.",
            "section": "Ground Control Settings",
            "key": "zoomOut"
        },
        {
            "type": "string",
            "title": "Valid File Extensions",
            "desc": "Valid file extensions for Ground Control to open. Comma separated list.",
            "section": "Ground Control Settings",
            "key": "validExtensions"
        }
    ]
    '''

    def build(self):
        Window.maximize()

        interface = FloatLayout()
        self.data = Data()

        self.frontpage = FrontPage(self.data, name='FrontPage')
        interface.add_widget(self.frontpage)

        self.nonVisibleWidgets = NonVisibleWidgets()
        '''
        Load User Settings
        '''

        self.data.comport = self.config.get('Maslow Settings', 'COMport')
        self.data.gcodeFile = self.config.get('Maslow Settings', 'openFile')
        self.data.config = self.config
        '''
        Initializations
        '''

        self.frontpage.setUpData(self.data)
        self.nonVisibleWidgets.setUpData(self.data)
        self.frontpage.gcodecanvas.initialize()
        '''
        Scheduling
        '''

        Clock.schedule_interval(self.runPeriodically, .01)
        '''
        Push settings to machine
        '''
        self.data.bind(connectionStatus=self.push_settings_to_machine)
        self.data.pushSettings = self.push_settings_to_machine

        return interface

    def build_config(self, config):
        """
        Set the default values for the config sections.
        """
        config.setdefaults(
            'Maslow Settings', {
                'COMport': '',
                'zAxis': 0,
                'zDistPerRot': 3.17,
                'bedWidth': 2438.4,
                'bedHeight': 1219.2,
                'motorOffsetY': 463,
                'motorSpacingX': 2978.4,
                'sledWidth': 310,
                'sledHeight': 139,
                'sledCG': 79,
                'openFile': " "
            })

        config.setdefaults(
            'Advanced Settings', {
                'encoderSteps': 8148.0,
                'gearTeeth': 10,
                'chainPitch': 6.35,
                'zEncoderSteps': 7560.0
            })

        config.setdefaults(
            'Ground Control Settings', {
                'zoomIn': "pageup",
                'validExtensions': ".nc, .ngc, .text, .gcode",
                'zoomOut': "pagedown"
            })

    def build_settings(self, settings):
        """
        Add custom section to the default configuration object.
        """
        settings.add_json_panel('Maslow Settings', self.config, data=self.json)
        settings.add_json_panel('Advanced Settings',
                                self.config,
                                data=self.advanced)
        settings.add_json_panel('Ground Control Settings',
                                self.config,
                                data=self.gcsettings)

    def on_config_change(self, config, section, key, value):
        """
        Respond to changes in the configuration.
        """

        if section == "Maslow Settings":
            if key == "COMport":
                self.data.comport = value
            self.push_settings_to_machine()

            if (key == "bedHeight" or key == "bedWidth"):
                self.frontpage.gcodecanvas.drawWorkspace()

    def close_settings(self, settings):
        """
        Close settings panel
        """
        super(GroundControlApp, self).close_settings(settings)

    def push_settings_to_machine(self, *args):

        cmdString = (
            "B03" + " A" +
            str(self.data.config.get('Maslow Settings', 'bedWidth')) + " C" +
            str(self.data.config.get('Maslow Settings', 'bedHeight')) + " Q" +
            str(self.data.config.get('Maslow Settings', 'motorSpacingX')) +
            " E" +
            str(self.data.config.get('Maslow Settings', 'motorOffsetY')) +
            " F" + str(self.data.config.get('Maslow Settings', 'sledWidth')) +
            " R" + str(self.data.config.get('Maslow Settings', 'sledHeight')) +
            " H" + str(self.data.config.get('Maslow Settings', 'sledCG')) +
            " I" + str(self.data.config.get('Maslow Settings', 'zAxis')) +
            " J" +
            str(self.data.config.get('Advanced Settings', 'encoderSteps')) +
            " K" +
            str(self.data.config.get('Advanced Settings', 'gearTeeth')) +
            " M" +
            str(self.data.config.get('Advanced Settings', 'chainPitch')) +
            " N" +
            str(self.data.config.get('Maslow Settings', 'zDistPerRot')) +
            " P" +
            str(self.data.config.get('Advanced Settings', 'zEncoderSteps')) +
            " ")

        self.data.gcode_queue.put(cmdString)

    '''
    
    Update Functions
    
    '''

    def writeToTextConsole(self, message):
        try:
            newText = self.frontpage.consoleText[-3000:] + message
            self.frontpage.consoleText = newText
            self.frontpage.textconsole.gotToBottom()
        except:
            self.frontpage.consoleText = "text not displayed correctly"

    def runPeriodically(self, *args):
        '''
        this block should be handled within the appropriate widget
        '''
        while not self.data.message_queue.empty(
        ):  #if there is new data to be read
            message = self.data.message_queue.get()

            self.data.logger.writeToLog(message)

            if message[0] == "<":
                self.setPosOnScreen(message)
            elif message[0] == "[":
                if message[1:10] == "PosError:":
                    self.setErrorOnScreen(message)
                elif message[1:8] == "Measure":
                    print "measure seen"
                    print message
                    measuredDist = float(message[9:len(message) - 3])
                    self.data.measureRequest(measuredDist)
            elif message[0:8] == "Message:":
                self.previousUploadStatus = self.data.uploadFlag
                self.data.uploadFlag = 0
                try:
                    self._popup.dismiss()  #close any open popup
                except:
                    pass  #there wasn't a popup to close
                content = NotificationPopup(
                    continueOn=self.dismiss_popup_continue,
                    hold=self.dismiss_popup_hold,
                    text=message[9:])
                self._popup = Popup(title="Notification: ",
                                    content=content,
                                    auto_dismiss=False,
                                    size_hint=(0.35, 0.35))
                self._popup.open()
            else:
                self.writeToTextConsole(message)

    def dismiss_popup_continue(self):
        '''
        
        Close The Pop-up and continue cut
        
        '''
        self._popup.dismiss()
        self.data.uploadFlag = self.previousUploadStatus  #resume cutting if the machine was cutting before

    def dismiss_popup_hold(self):
        '''
        
        Close The Pop-up and continue cut
        
        '''
        self._popup.dismiss()
        self.data.uploadFlag = 0  #stop cutting

    def setPosOnScreen(self, message):
        '''
        
        This should be moved into the appropriate widget
        
        '''

        try:
            startpt = message.find('MPos:') + 5

            endpt = message.find('WPos:')

            numz = message[startpt:endpt]
            units = "mm"  #message[endpt+1:endpt+3]

            valz = numz.split(",")

            xval = float(valz[0])
            yval = float(valz[1])
            zval = float(valz[2])

            if math.isnan(xval):
                self.writeToTextConsole("Unable to resolve x Kinematics.")
                xval = 0
            if math.isnan(yval):
                self.writeToTextConsole("Unable to resolve y Kinematics.")
                yval = 0
            if math.isnan(zval):
                self.writeToTextConsole("Unable to resolve z Kinematics.")
                zval = 0
        except:
            print "bad data"
            return

        self.frontpage.setPosReadout(xval, yval, zval)
        self.frontpage.gcodecanvas.positionIndicator.setPos(
            xval, yval, self.data.units)

    def setErrorOnScreen(self, message):

        try:
            startpt = message.find(':') + 1
            endpt = message.find(',', startpt)
            errorValueAsString = message[startpt:endpt]
            errorValueAsFloat = float(errorValueAsString)

            self.frontpage.gcodecanvas.positionIndicator.setError(
                errorValueAsFloat)
            self.data.logger.writeErrorValueToLog(errorValueAsFloat)
        except:
            print "unable to read error value"
Beispiel #11
0
class GroundControlApp(App):
    def get_application_config(self):
        return super(GroundControlApp,
                     self).get_application_config('~/%(appname)s.ini')

    json = '''
    [
        {
            "type": "string",
            "title": "Serial Connection",
            "desc": "Select the COM port to connect to machine",
            "section": "Maslow Settings",
            "key": "COMport"
        },
        {
            "type": "string",
            "title": "Distance Between Motors",
            "desc": "The horizontal distance between the center of the motor shafts in MM.\\ndefault setting: %s",
            "section": "Maslow Settings",
            "key": "motorSpacingX"
            
        },
        {
            "type": "string",
            "title": "Work Area Width in MM",
            "desc": "The width of the machine working area (normally 8 feet).\\ndefault setting: %s",
            "section": "Maslow Settings",
            "key": "bedWidth"
        },
        {
            "type": "string",
            "title": "Work Area Height in MM",
            "desc": "The Height of the machine working area (normally 4 feet).\\ndefault setting: %s",
            "section": "Maslow Settings",
            "key": "bedHeight"
        },
        {
            "type": "string",
            "title": "Motor Offset Height in MM",
            "desc": "The vertical distance from the edge of the work area to the level of the motors.\\ndefault setting: %s",
            "section": "Maslow Settings",
            "key": "motorOffsetY"
        },
        {
            "type": "string",
            "title": "Distance Between Sled Mounting Points",
            "desc": "The horizontal distance between the points where the chains mount to the sled.\\ndefault setting: %s",
            "section": "Maslow Settings",
            "key": "sledWidth"
        },
        {
            "type": "string",
            "title": "Vertical Distance Sled Mounts to Cutter",
            "desc": "The vertical distance between where the chains mount on the sled to the cutting tool.\\ndefault setting: %s",
            "section": "Maslow Settings",
            "key": "sledHeight"
        },
        {
            "type": "string",
            "title": "Center Of Gravity",
            "desc": "How far below the cutting bit is the center of gravity. This can be found by resting the sled on a round object and observing where it balances.\\ndefault setting: %s",
            "section": "Maslow Settings",
            "key": "sledCG"
        },
        {
            "type": "bool",
            "title": "z-axis installed",
            "desc": "Does the machine have an automatic z-axis?\\ndefault setting: %s",
            "section": "Maslow Settings",
            "key": "zAxis"
        },
        {
            "type": "string",
            "title": "Z-Axis Pitch",
            "desc": "The number of mm moved per rotation of the z-axis\\ndefault setting: %s",
            "section": "Maslow Settings",
            "key": "zDistPerRot"
        },
        {
            "type": "options",
            "title": "Color Scheme",
            "desc": "Switch between the light and dark color schemes. Restarting GC is needed for this change to take effect\\ndefault setting: %s",
            "options": ["Light", "Dark"],
            "section": "Maslow Settings",
            "key": "colorScheme"
        },
        {
            "type": "string",
            "title": "Open File",
            "desc": "The path to the open file\\ndefault setting: your home directory",
            "section": "Maslow Settings",
            "key": "openFile"
        },
        {
            "type": "string",
            "title": "Macro 1",
            "desc": "User defined gcode bound to the Macro 1 button",
            "section": "Maslow Settings",
            "key": "macro1"
        },
        {
            "type": "string",
            "title": "Macro 1 Title",
            "desc": "User defined title for the Macro 1 button",
            "section": "Maslow Settings",
            "key": "macro1_title"
        },
        {
            "type": "string",
            "title": "Macro 2",
            "desc": "User defined gcode bound to the Macro 2 button",
            "section": "Maslow Settings",
            "key": "macro2"
        },
        {
            "type": "string",
            "title": "Macro 2 Title",
            "desc": "User defined title for the Macro 2 button",
            "section": "Maslow Settings",
            "key": "macro2_title"
        }
    ]
    ''' % (
        # global_variables._COMport,
        global_variables._motorSpacingX,
        global_variables._bedWidth,
        global_variables._bedHeight,
        global_variables._motorOffsetY,
        global_variables._sledWidth,
        global_variables._sledHeight,
        global_variables._sledCG,
        global_variables._zAxis,
        global_variables._zDistPerRot,
        global_variables._colorScheme)

    advanced = '''
    [
        {
            "type": "string",
            "title": "Encoder Steps per Revolution",
            "desc": "The number of encoder steps per revolution of the left or right motor\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "encoderSteps"
        },
        {
            "type": "string",
            "title": "Gear Teeth",
            "desc": "The number of teeth on the gear of the left or right motor\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "gearTeeth"
        },
        {
            "type": "string",
            "title": "Chain Pitch",
            "desc": "The distance between chain roller centers\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "chainPitch"
        },
        {
            "type": "string",
            "title": "Z-Axis Encoder Steps per Revolution",
            "desc": "The number of encoder steps per revolution of the z-axis\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "zEncoderSteps"
        },
        {
            "type": "bool",
            "title": "Spindle Automation",
            "desc": "Should the spindle start and stop automatically based on gcode? Leave off for default stepper control.\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "zAxisAuto"
        },
        {
            "type": "string",
            "title": "Home Position X Coordinate",
            "desc": "The X coordinate of the home position\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "homeX"
        },
        {
            "type": "string",
            "title": "Home Position Y Coordinate",
            "desc": "The X coordinate of the home position\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "homeY"
        },
        {
            "type": "bool",
            "title": "Truncate Floating Point Numbers",
            "desc": "Truncate floating point numbers at the specified number of decimal places\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "truncate"
        },
        {
            "type": "string",
            "title": "Floating Point Precision",
            "desc": "If truncate floating point numbers is enabled, the number of digits after the decimal place to preserve\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "digits"
        },
        {
            "type": "options",
            "title": "Kinematics Type",
            "desc": "Switch between trapezoidal and triangular kinematics\\ndefault setting: %s",
            "options": ["Quadrilateral", "Triangular"],
            "section": "Advanced Settings",
            "key": "kinematicsType"
        },
        {
            "type": "string",
            "title": "Rotation Radius for Triangular Kinematics",
            "desc": "The distance between where the chains attach and the center of the router bit in mm\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "rotationRadius"
        },
        {
            "type": "bool",
            "title": "Enable Custom Positional PID Values",
            "desc": "Enable using custom values for the positional PID controller. Turning this off will return to the default values\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "enablePosPIDValues"
        },
        {
            "type": "string",
            "title": "Kp Position",
            "desc": "The proportional constant for the position PID controller\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "KpPos"
        },
        {
            "type": "string",
            "title": "Ki Position",
            "desc": "The integral constant for the position PID controller\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "KiPos"
        },
        {
            "type": "string",
            "title": "Kd Position",
            "desc": "The derivative constant for the position PID controller\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "KdPos"
        },
        {
            "type": "string",
            "title": "Proportional Weighting",
            "desc": "The ratio of Proportional on Error (1) to Proportional on Measure (0)\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "propWeight"
        },
        {
            "type": "bool",
            "title": "Enable Custom Velocity PID Values",
            "desc": "Enable using custom values for the Velocity PID controller. Turning this off will return to the default values\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "enableVPIDValues"
        },
        {
            "type": "string",
            "title": "Kp Velocity",
            "desc": "The proportional constant for the velocity PID controller\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "KpV"
        },
        {
            "type": "string",
            "title": "Ki Velocity",
            "desc": "The integral constant for the velocity PID controller\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "KiV"
        },
        {
            "type": "string",
            "title": "Kd Velocity",
            "desc": "The derivative constant for the velocity PID controller\\ndefault setting: %s",
            "section": "Advanced Settings",
            "key": "KdV"
        }
    ]
    ''' % (global_variables._encoderSteps, global_variables._gearTeeth,
           global_variables._chainPitch, global_variables._zEncoderSteps,
           global_variables._zAxisAuto, global_variables._homeX,
           global_variables._homeY, global_variables._truncate,
           global_variables._digits, global_variables._kinematicsType,
           global_variables._rotationRadius,
           global_variables._enablePosPIDValues, global_variables._KpPos,
           global_variables._KiPos, global_variables._KdPos,
           global_variables._propWeight, global_variables._enableVPIDValues,
           global_variables._KpV, global_variables._KiV, global_variables._KdV)

    gcsettings = '''
    [
        {
            "type": "bool",
            "title": "Center Canvas on Window Resize",
            "desc": "When resizing the window, automatically reset the Gcode canvas to be centered and zoomed out. Program must be restarted to take effect.\\ndefault setting: %s",
            "section": "Ground Control Settings",
            "key": "centerCanvasOnResize"
        },
        {
            "type": "string",
            "title": "Zoom In",
            "desc": "Pressing this key will zoom in. Note combinations of keys like \'shift\' + \'=\' may not work as expected. Program must be restarted to take effect.\\ndefault setting: %s",
            "section": "Ground Control Settings",
            "key": "zoomIn"
        },
        {
            "type": "string",
            "title": "Zoom Out",
            "desc": "Pressing this key will zoom in. Note combinations of keys like \'shift\' + \'=\' may not work as expected. Program must be restarted to take effect.\\ndefault setting: %s",
            "section": "Ground Control Settings",
            "key": "zoomOut"
        },
        {
            "type": "string",
            "title": "Valid File Extensions",
            "desc": "Valid file extensions for Ground Control to open. Comma separated list.\\ndefault setting: %s",
            "section": "Ground Control Settings",
            "key": "validExtensions"
        }
    ]
    ''' % (global_variables._centerCanvasOnResize, global_variables._zoomIn,
           global_variables._zoomOut, global_variables._validExtensions)

    def build(self):

        interface = FloatLayout()
        self.data = Data()

        if self.config.get('Maslow Settings', 'colorScheme') == 'Light':
            self.data.iconPath = './Images/Icons/normal/'
            self.data.fontColor = '[color=7a7a7a]'
            self.data.drawingColor = [.47, .47, .47]
            Window.clearcolor = (1, 1, 1, 1)
            self.data.posIndicatorColor = [0, 0, 0]
            self.data.targetInicatorColor = [1, 0, 0]
        elif self.config.get('Maslow Settings', 'colorScheme') == 'Dark':
            self.data.iconPath = './Images/Icons/highvis/'
            self.data.fontColor = '[color=000000]'
            self.data.drawingColor = [1, 1, 1]
            Window.clearcolor = (0, 0, 0, 1)
            self.data.posIndicatorColor = [1, 1, 1]
            self.data.targetInicatorColor = [1, 0, 0]

        Window.maximize()

        self.frontpage = FrontPage(self.data, name='FrontPage')
        interface.add_widget(self.frontpage)

        self.nonVisibleWidgets = NonVisibleWidgets()
        '''
        Load User Settings
        '''

        if self.config.get('Advanced Settings', 'encoderSteps') == '8148.0':
            self.data.message_queue.put(
                "Message: This update will adjust the the number of encoder pulses per rotation from 8,148 to 8,113 in your settings which improves the positional accuracy.\n\nPerforming a calibration will help you get the most out of this update."
            )
            self.config.set('Advanced Settings', 'encoderSteps', '8113.73')
            self.config.write()

        self.data.comport = self.config.get('Maslow Settings', 'COMport')
        self.data.gcodeFile = self.config.get('Maslow Settings', 'openFile')
        offsetX = float(self.config.get('Advanced Settings', 'homeX'))
        offsetY = float(self.config.get('Advanced Settings', 'homeY'))
        self.data.gcodeShift = [offsetX, offsetY]
        self.data.config = self.config
        '''
        Initializations
        '''

        self.frontpage.setUpData(self.data)
        self.nonVisibleWidgets.setUpData(self.data)
        self.frontpage.gcodecanvas.initialize()
        '''
        Scheduling
        '''

        Clock.schedule_interval(self.runPeriodically, .01)
        '''
        Push settings to machine
        '''
        self.data.bind(connectionStatus=self.push_settings_to_machine)
        self.data.pushSettings = self.push_settings_to_machine

        return interface

    def build_config(self, config):
        """
        Set the default values for the config sections.
        """
        config.setdefaults(
            'Maslow Settings', {
                'COMport': global_variables._COMport,
                'zAxis': global_variables._zAxis,
                'zDistPerRot': global_variables._zDistPerRot,
                'bedWidth': global_variables._bedWidth,
                'bedHeight': global_variables._bedHeight,
                'motorOffsetY': global_variables._motorOffsetY,
                'motorSpacingX': global_variables._motorSpacingX,
                'sledWidth': global_variables._sledWidth,
                'sledHeight': global_variables._sledHeight,
                'sledCG': global_variables._sledCG,
                'colorScheme': global_variables._colorScheme,
                'openFile': global_variables._openFile,
                'macro1': global_variables._macro1,
                'macro1_title': global_variables._macro1_title,
                'macro2': global_variables._macro2,
                'macro2_title': global_variables._macro2_title
            })

        config.setdefaults(
            'Advanced Settings', {
                'encoderSteps': global_variables._encoderSteps,
                'gearTeeth': global_variables._gearTeeth,
                'chainPitch': global_variables._chainPitch,
                'zEncoderSteps': global_variables._zEncoderSteps,
                'zAxisAuto': global_variables._zAxisAuto,
                'homeX': global_variables._homeX,
                'homeY': global_variables._homeY,
                'truncate': global_variables._truncate,
                'digits': global_variables._digits,
                'kinematicsType': global_variables._kinematicsType,
                'rotationRadius': global_variables._rotationRadius,
                'enablePosPIDValues': global_variables._enablePosPIDValues,
                'KpPos': global_variables._KpPos,
                'KiPos': global_variables._KiPos,
                'KdPos': global_variables._KdPos,
                'propWeight': global_variables._propWeight,
                'enableVPIDValues': global_variables._enableVPIDValues,
                'KpV': global_variables._KpV,
                'KiV': global_variables._KiV,
                'KdV': global_variables._KdV
            })

        config.setdefaults(
            'Ground Control Settings', {
                'centerCanvasOnResize': global_variables._centerCanvasOnResize,
                'zoomIn': global_variables._zoomIn,
                'zoomOut': global_variables._zoomOut,
                'validExtensions': global_variables._validExtensions,
            })

    def build_settings(self, settings):
        """
        Add custom section to the default configuration object.
        """
        settings.add_json_panel('Maslow Settings', self.config, data=self.json)
        settings.add_json_panel('Advanced Settings',
                                self.config,
                                data=self.advanced)
        settings.add_json_panel('Ground Control Settings',
                                self.config,
                                data=self.gcsettings)

    def on_config_change(self, config, section, key, value):
        """
        Respond to changes in the configuration.
        """

        if section == "Maslow Settings":

            self.push_settings_to_machine()

            if key == "COMport":
                self.data.comport = value

            if (key == "bedHeight" or key == "bedWidth"):
                self.frontpage.gcodecanvas.drawWorkspace()

            if (key == "macro1_title") or (key == "macro2_title"):
                self.frontpage.update_macro_titles()

        if section == "Advanced Settings":

            self.push_settings_to_machine()

            if (key == "truncate") or (key == "digits"):
                self.frontpage.gcodecanvas.reloadGcode()

    def push_settings_to_machine(self, *args):

        #Push motor configuration settings to machine

        if self.data.connectionStatus != 1:
            return  # only run on connection true

        if int(self.data.config.get('Advanced Settings',
                                    'enablePosPIDValues')) == 1:
            KpPos = float(self.data.config.get('Advanced Settings', 'KpPos'))
            KiPos = float(self.data.config.get('Advanced Settings', 'KiPos'))
            KdPos = float(self.data.config.get('Advanced Settings', 'KdPos'))
            propWeight = float(
                self.data.config.get('Advanced Settings', 'propWeight'))
        else:
            KpPos = 1300
            KiPos = 0
            KdPos = 34
            propWeight = 1

        if int(self.data.config.get('Advanced Settings',
                                    'enableVPIDValues')) == 1:
            KpV = float(self.data.config.get('Advanced Settings', 'KpV'))
            KiV = float(self.data.config.get('Advanced Settings', 'KiV'))
            KdV = float(self.data.config.get('Advanced Settings', 'KdV'))
        else:
            KpV = 7
            KiV = 0
            KdV = .28

        # Be pretty dumb about this, just push settings without checking to
        # see what machine has

        self.data.gcode_queue.put(
            "$16=" + str(self.data.config.get('Maslow Settings', 'zAxis')))
        self.data.gcode_queue.put(
            "$12=" +
            str(self.data.config.get('Advanced Settings', 'encoderSteps')))
        distPerRot = float(
            self.data.config.get('Advanced Settings', 'gearTeeth')) * float(
                self.data.config.get('Advanced Settings', 'chainPitch'))
        self.data.gcode_queue.put("$13=" + str(distPerRot))
        self.data.gcode_queue.put(
            "$19=" +
            str(self.data.config.get('Maslow Settings', 'zDistPerRot')))
        self.data.gcode_queue.put(
            "$20=" +
            str(self.data.config.get('Advanced Settings', 'zEncoderSteps')))

        #main axes
        self.data.gcode_queue.put("$21=" + str(KpPos))
        self.data.gcode_queue.put("$22=" + str(KiPos))
        self.data.gcode_queue.put("$23=" + str(KdPos))
        self.data.gcode_queue.put("$24=" + str(propWeight))
        self.data.gcode_queue.put("$25=" + str(KpV))
        self.data.gcode_queue.put("$26=" + str(KiV))
        self.data.gcode_queue.put("$27=" + str(KdV))
        self.data.gcode_queue.put("$28=1.0")

        #z axis
        self.data.gcode_queue.put("$29=" + str(KpPos))
        self.data.gcode_queue.put("$30=" + str(KiPos))
        self.data.gcode_queue.put("$31=" + str(KdPos))
        self.data.gcode_queue.put("$32=" + str(propWeight))
        self.data.gcode_queue.put("$33=" + str(KpV))
        self.data.gcode_queue.put("$34=" + str(KiV))
        self.data.gcode_queue.put("$35=" + str(KdV))
        self.data.gcode_queue.put("$36=1.0")
        self.data.gcode_queue.put(
            "$17=" +
            str(self.data.config.get('Advanced Settings', 'zAxisAuto')))

        #Push kinematics settings to machine
        if self.data.config.get('Advanced Settings',
                                'kinematicsType') == 'Quadrilateral':
            kinematicsType = 1
        else:
            kinematicsType = 2

        self.data.gcode_queue.put(
            "$0=" + str(self.data.config.get('Maslow Settings', 'bedWidth')))
        self.data.gcode_queue.put(
            "$1=" + str(self.data.config.get('Maslow Settings', 'bedHeight')))
        self.data.gcode_queue.put(
            "$2=" +
            str(self.data.config.get('Maslow Settings', 'motorSpacingX')))
        self.data.gcode_queue.put(
            "$3=" +
            str(self.data.config.get('Maslow Settings', 'motorOffsetY')))
        self.data.gcode_queue.put(
            "$4=" + str(self.data.config.get('Maslow Settings', 'sledWidth')))
        self.data.gcode_queue.put(
            "$5=" + str(self.data.config.get('Maslow Settings', 'sledHeight')))
        self.data.gcode_queue.put(
            "$6=" + str(self.data.config.get('Maslow Settings', 'sledCG')))
        self.data.gcode_queue.put("$7=" + str(kinematicsType))
        self.data.gcode_queue.put(
            "$8=" +
            str(self.data.config.get('Advanced Settings', 'rotationRadius')))

        # Force kinematics recalibration
        self.data.gcode_queue.put("$K")

    '''
    
    Update Functions
    
    '''

    def writeToTextConsole(self, message):
        try:
            newText = self.frontpage.consoleText[-500:] + message
            self.frontpage.consoleText = newText
            self.frontpage.textconsole.gotToBottom()
        except:
            self.frontpage.consoleText = "text not displayed correctly"

    def runPeriodically(self, *args):
        '''
        this block should be handled within the appropriate widget
        '''
        while not self.data.message_queue.empty(
        ):  #if there is new data to be read
            message = self.data.message_queue.get()

            if message[0] == "<":
                self.setPosOnScreen(message)
            elif message[0] == "[":
                if message[1:4] == "PE:":
                    self.setErrorOnScreen(message)
                elif message[1:8] == "Measure":
                    print "measure seen"
                    print message
                    measuredDist = float(message[9:len(message) - 3])
                    self.data.measureRequest(measuredDist)
            elif message[0:13] == "Maslow Paused":
                self.data.uploadFlag = 0
                self.writeToTextConsole(message)
            elif message[0:8] == "Message:":
                self.previousUploadStatus = self.data.uploadFlag
                self.data.uploadFlag = 0
                try:
                    self._popup.dismiss()  #close any open popup
                except:
                    pass  #there wasn't a popup to close
                content = NotificationPopup(
                    continueOn=self.dismiss_popup_continue, text=message[9:])
                if sys.platform.startswith('darwin'):
                    self._popup = Popup(title="Notification: ",
                                        content=content,
                                        auto_dismiss=False,
                                        size=(360, 240),
                                        size_hint=(.3, .3))
                else:
                    self._popup = Popup(title="Notification: ",
                                        content=content,
                                        auto_dismiss=False,
                                        size=(360, 240),
                                        size_hint=(None, None))
                self._popup.open()
                if global_variables._keyboard:
                    global_variables._keyboard.bind(
                        on_key_down=self.keydown_popup)
                    self._popup.bind(on_dismiss=self.ondismiss_popup)
            elif message[0:8] == "Firmware":
                self.data.logger.writeToLog("Ground Control Version " +
                                            str(self.data.version) + "\n")
                self.writeToTextConsole("Ground Control " +
                                        str(self.data.version) + "\r\n" +
                                        message + "\r\n")

                #Check that version numbers match
                if float(message[-7:]) < float(self.data.version):
                    self.data.message_queue.put(
                        "Message: Warning, your firmware is out of date and may not work correctly with this version of Ground Control\n\n"
                        + "Ground Control Version " + str(self.data.version) +
                        "\r\n" + message)
                if float(message[-7:]) > float(self.data.version):
                    self.data.message_queue.put(
                        "Message: Warning, your version of Ground Control is out of date and may not work with this firmware version\n\n"
                        + "Ground Control Version " + str(self.data.version) +
                        "\r\n" + message)
            elif message == "ok\r\n":
                pass  #displaying all the 'ok' messages clutters up the display
            else:
                self.writeToTextConsole(message)

    def ondismiss_popup(self, event):
        if global_variables._keyboard:
            global_variables._keyboard.unbind(on_key_down=self.keydown_popup)

    def keydown_popup(self, keyboard, keycode, text, modifiers):
        if (keycode[1] == 'enter') or (keycode[1]
                                       == 'numpadenter') or (keycode[1]
                                                             == 'escape'):
            self.dismiss_popup_continue()
        return True  # always swallow keypresses since this is a modal dialog

    def dismiss_popup_continue(self):
        '''
        
        Close The Pop-up and continue cut
        
        '''
        self._popup.dismiss()
        self.data.quick_queue.put(
            "~")  #send cycle resume command to unpause the machine
        self.data.uploadFlag = self.previousUploadStatus  #resume cutting if the machine was cutting before

    def dismiss_popup_hold(self):
        '''
        
        Close The Pop-up and continue cut
        
        '''
        self._popup.dismiss()
        self.data.uploadFlag = 0  #stop cutting

    def setPosOnScreen(self, message):
        '''
        
        This should be moved into the appropriate widget
        
        '''

        try:
            startpt = message.find('MPos:') + 5

            endpt = message.find('WPos:')

            numz = message[startpt:endpt]
            units = "mm"  #message[endpt+1:endpt+3]

            valz = numz.split(",")

            self.xval = float(valz[0])
            self.yval = float(valz[1])
            self.zval = float(valz[2])

            if math.isnan(self.xval):
                self.writeToTextConsole("Unable to resolve x Kinematics.")
                self.xval = 0
            if math.isnan(self.yval):
                self.writeToTextConsole("Unable to resolve y Kinematics.")
                self.yval = 0
            if math.isnan(self.zval):
                self.writeToTextConsole("Unable to resolve z Kinematics.")
                self.zval = 0

            self.frontpage.setPosReadout(self.xval, self.yval, self.zval)
            self.frontpage.gcodecanvas.positionIndicator.setPos(
                self.xval, self.yval, self.data.units)
        except:
            print "One Machine Position Report Command Misread"
            return

    def setErrorOnScreen(self, message):

        try:
            startpt = message.find(':') + 1
            endpt = message.find(',', startpt)
            leftErrorValueAsString = message[startpt:endpt]
            leftErrorValueAsFloat = float(leftErrorValueAsString)

            startpt = endpt + 1
            endpt = message.find(',', startpt)
            rightErrorValueAsString = message[startpt:endpt]

            rightErrorValueAsFloat = float(rightErrorValueAsString)

            if self.data.units == "INCHES":
                rightErrorValueAsFloat = rightErrorValueAsFloat / 25.4
                leftErrorValueAsFloat = leftErrorValueAsFloat / 25.4

            avgError = (abs(leftErrorValueAsFloat) +
                        abs(rightErrorValueAsFloat)) / 2

            self.frontpage.gcodecanvas.positionIndicator.setError(
                0, self.data.units)
            self.data.logger.writeErrorValueToLog(avgError)

            self.frontpage.gcodecanvas.targetIndicator.setPos(
                self.xval - .5 * rightErrorValueAsFloat +
                .5 * leftErrorValueAsFloat, self.yval -
                .5 * rightErrorValueAsFloat - .5 * leftErrorValueAsFloat,
                self.data.units)

        except:
            print "Machine Position Report Command Misread Happened Once"
Beispiel #12
0
    def build(self):

        interface = FloatLayout()
        self.data = Data()

        if self.config.get('Maslow Settings', 'colorScheme') == 'Light':
            self.data.iconPath = './Images/Icons/normal/'
            self.data.fontColor = '[color=7a7a7a]'
            self.data.drawingColor = [.47, .47, .47]
            Window.clearcolor = (1, 1, 1, 1)
            self.data.posIndicatorColor = [0, 0, 0]
            self.data.targetInicatorColor = [1, 0, 0]
        elif self.config.get('Maslow Settings', 'colorScheme') == 'Dark':
            self.data.iconPath = './Images/Icons/highvis/'
            self.data.fontColor = '[color=000000]'
            self.data.drawingColor = [1, 1, 1]
            Window.clearcolor = (0, 0, 0, 1)
            self.data.posIndicatorColor = [1, 1, 1]
            self.data.targetInicatorColor = [1, 0, 0]

        Window.maximize()

        self.frontpage = FrontPage(self.data, name='FrontPage')
        interface.add_widget(self.frontpage)

        self.nonVisibleWidgets = NonVisibleWidgets()
        '''
        Load User Settings
        '''

        if self.config.get('Advanced Settings', 'encoderSteps') == '8148.0':
            self.data.message_queue.put(
                "Message: This update will adjust the the number of encoder pulses per rotation from 8,148 to 8,113 in your settings which improves the positional accuracy.\n\nPerforming a calibration will help you get the most out of this update."
            )
            self.config.set('Advanced Settings', 'encoderSteps', '8113.73')
            self.config.write()

        self.data.comport = self.config.get('Maslow Settings', 'COMport')
        self.data.gcodeFile = self.config.get('Maslow Settings', 'openFile')
        offsetX = float(self.config.get('Advanced Settings', 'homeX'))
        offsetY = float(self.config.get('Advanced Settings', 'homeY'))
        self.data.gcodeShift = [offsetX, offsetY]
        self.data.config = self.config
        '''
        Initializations
        '''

        self.frontpage.setUpData(self.data)
        self.nonVisibleWidgets.setUpData(self.data)
        self.frontpage.gcodecanvas.initialize()
        '''
        Scheduling
        '''

        Clock.schedule_interval(self.runPeriodically, .01)
        '''
        Push settings to machine
        '''
        self.data.bind(connectionStatus=self.push_settings_to_machine)
        self.data.pushSettings = self.push_settings_to_machine

        return interface