Fixed jogging, AVR state strings are now all caps, Retry i2c on fail
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Tue, 6 Sep 2016 08:58:34 +0000 (01:58 -0700)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Tue, 6 Sep 2016 08:58:34 +0000 (01:58 -0700)
src/jade/templates/control-view.jade
src/js/app.js
src/js/control-view.js
src/py/bbctrl/AVR.py
src/py/bbctrl/Jog.py
src/py/bbctrl/LCD.py
src/py/inevent/Event.py
src/py/inevent/EventStream.py
src/py/inevent/Format.py [deleted file]
src/py/inevent/InEvent.py

index 7c085e11dd9e4c9f354db03bb61e12d7492be9d2..3ba8b5ab01da4ec4fc362c50ea3128560d11838e 100644 (file)
@@ -75,25 +75,25 @@ script#control-view-template(type="text/x-template")
 
     .toolbar
       button.pure-button(title="Home the machine.", @click="home",
-        :disabled="state.x != 'ready'")
+        :disabled="state.x != 'READY'")
         .fa.fa-home
 
       button.pure-button(
-        title="{{state.x == 'running' ? 'Pause' : 'Start'}} program.",
+        title="{{state.x == 'RUNNING' ? 'Pause' : 'Start'}} program.",
         @click="start_pause",
-        :disabled="state.c == 'homing'")
-        .fa(:class="state.x == 'running' ? 'fa-pause' : 'fa-play'")
+        :disabled="state.c == 'HOMING'")
+        .fa(:class="state.x == 'RUNNING' ? 'fa-pause' : 'fa-play'")
 
       button.pure-button(title="Stop program.", @click="stop",
-        :disabled="state.x == 'ready'")
+        :disabled="state.x == 'READY'")
         .fa.fa-stop
 
       button.pure-button(title="Pause program at next optional stop (M1).",
-        @click="optional_pause", :disabled="state.c == 'homing'")
+        @click="optional_pause", :disabled="state.c == 'HOMING'")
         .fa.fa-stop-circle-o
 
       button.pure-button(title="Execute one program step.", @click="step",
-        :disabled="(state.x != 'ready' && state.x != 'holding') || !file")
+        :disabled="(state.x != 'READY' && state.x != 'HOLDING') || !file")
         .fa.fa-step-forward
 
     .tabs
@@ -109,7 +109,7 @@ script#control-view-template(type="text/x-template")
       section#content1.tab-content
         .toolbar
           button.pure-button(title="Upload a new program file.", @click="open",
-            :disabled="state.x == 'running' || state.x == 'stopping'")
+            :disabled="state.x == 'RUNNING' || state.x == 'STOPPING'")
             .fa.fa-folder-open
 
           input.gcode-file-input(type="file", @change="upload",
@@ -121,7 +121,7 @@ script#control-view-template(type="text/x-template")
 
           select(title="Select previously uploaded program files.",
             v-model="file", @change="load",
-            :disabled="state.x == 'running' || state.x == 'stopping'")
+            :disabled="state.x == 'RUNNING' || state.x == 'STOPPING'")
             option(v-for="file in files", :value="file") {{file}}
 
         .gcode(:class="{placeholder: !gcode}")
@@ -132,7 +132,7 @@ script#control-view-template(type="text/x-template")
           fieldset
             button.pure-button.pure-button-primary(
               title="Manually execute instructions.", @click="submit_mdi",
-              :disabled="state.x != 'ready'") MDI
+              :disabled="state.x != 'READY'") MDI
             input(v-model="mdi", @keyup.enter="submit_mdi")
 
         .history(:class="{placeholder: !history}")
index c92cd4b7266427cc4db2d98c4e59feb40a71184d..c676bcafaab147b87f25dc6fa75dd8cc67431f90 100644 (file)
@@ -58,7 +58,7 @@ module.exports = new Vue({
 
   methods: {
     estop: function () {
-      if (this.state.x == 'estopped') api.put('clear');
+      if (this.state.x == 'ESTOPPED') api.put('clear');
       else api.put('estop');
     },
 
index 3337c295c161c6ca7e8087d99fe7bb4c3b184700..f958b0a1b3a924bccfb3246d34d5406bc81a6613 100644 (file)
@@ -137,9 +137,9 @@ module.exports = {
 
 
     start_pause: function () {
-      if (this.state.x == 'running') this.pause();
+      if (this.state.x == 'RUNNING') this.pause();
 
-      else if (this.state.x == 'stopping' || this.state.x == 'holding')
+      else if (this.state.x == 'STOPPING' || this.state.x == 'HOLDING')
         this.unpause();
 
       else this.start();
index 4e9bc527a51912d35f52bb8ebb27e408e2f8b8ad..cb4a7992285b4742688780effbf3000084a72992 100644 (file)
@@ -1,6 +1,7 @@
 import re
 import serial
 import json
+import time
 import logging
 from collections import deque
 
@@ -44,10 +45,12 @@ class AVR():
             self.sp.nonblocking()
 
         except Exception as e:
+            self.sp = None
             log.warning('Failed to open serial port: %s', e)
-            return
 
-        ctrl.ioloop.add_handler(self.sp, self.serial_handler, ctrl.ioloop.READ)
+        if self.sp is not None:
+            ctrl.ioloop.add_handler(self.sp, self.serial_handler,
+                                    ctrl.ioloop.READ)
 
         try:
             self.i2c_bus = smbus.SMBus(ctrl.args.avr_port)
@@ -75,13 +78,29 @@ class AVR():
 
 
     def _i2c_command(self, cmd, word = None):
-        if not hasattr(self, 'i2c_bus'): return
+        if self.i2c_bus is None: return
 
         log.info('I2C: %d' % cmd)
+        retry = 5
 
-        if word is not None:
-            self.i2c_bus.write_word_data(self.i2c_addr, cmd, word)
-        self.i2c_bus.write_byte(self.i2c_addr, cmd)
+        while True:
+            try:
+                if word is not None:
+                    self.i2c_bus.write_word_data(self.i2c_addr, cmd, word)
+                self.i2c_bus.write_byte(self.i2c_addr, cmd)
+                break
+
+            except IOError as e:
+                retry -= 1
+
+                if retry:
+                    log.error('I2C communication failed, retrying: %s' % e)
+                    self.i2c_bus.close()
+                    time.sleep(0.1)
+                    self.i2c_bus = smbus.SMBus(self.ctrl.args.avr_port)
+                    continue
+
+                else: raise e
 
 
     def report(self): self._i2c_command(I2C_REPORT)
@@ -93,7 +112,7 @@ class AVR():
 
 
     def set_write(self, enable):
-        if not hasattr(self, 'sp'): return
+        if self.sp is None: return
 
         flags = self.ctrl.ioloop.READ
         if enable: flags |= self.ctrl.ioloop.WRITE
@@ -157,13 +176,14 @@ class AVR():
 
                 except Exception as e:
                     log.error('%s, data: %s', e, line)
+                    return
 
                 if 'firmware' in msg:
                     log.error('AVR rebooted')
                     self._stop_sending_gcode()
                     self.report()
 
-                if 'x' in msg and msg['x'] == 'estopped':
+                if 'x' in msg and msg['x'] == 'ESTOPPED':
                     self._stop_sending_gcode()
 
                 self.vars.update(msg)
index 6ca5db6f0e97e0929a5d743f4913ce7c2647fdbd..f4c48a6c940478c0b0cbe9367abedd9d0ac7b733 100644 (file)
@@ -19,16 +19,19 @@ class Jog(inevent.JogHandler):
 
         self.v = [0.0] * 4
         self.lastV = self.v
+        self.callback()
 
         self.processor = inevent.InEvent(ctrl.ioloop, self,
                                          types = "js kbd".split())
 
 
-    def processed_events(self):
+    def callback(self):
         if self.v != self.lastV:
             self.lastV = self.v
             self.ctrl.avr.jog(self.v)
 
+        self.ctrl.ioloop.call_later(0.25, self.callback)
+
 
     def changed(self):
         if self.speed == 1: scale = 1.0 / 128.0
index bc4e80f283e88ae5ba02aac7a01d1a24d9447877..1209e7ff8084659bccebe01ccb945c6db602da57 100644 (file)
@@ -15,17 +15,17 @@ class LCD:
 
         if has('x') or has('c'):
             v = self.ctrl.avr.vars
-            state = v.get('x', 'init')
-            if 'c' in v and state == 'running': state = v['c']
+            state = v.get('x', 'INIT')
+            if 'c' in v and state == 'RUNNING': state = v['c']
 
-            self.lcd.text('%-9s' % state.upper(), 0, 0)
+            self.lcd.text('%-9s' % state, 0, 0)
 
         if has('xp'): self.lcd.text('% 10.4fX' % msg['xp'], 9, 0)
         if has('yp'): self.lcd.text('% 10.4fY' % msg['yp'], 9, 1)
         if has('zp'): self.lcd.text('% 10.4fZ' % msg['zp'], 9, 2)
         if has('ap'): self.lcd.text('% 10.4fA' % msg['ap'], 9, 3)
         if has('t'):  self.lcd.text('%2uT'     % msg['t'],  6, 1)
-        if has('u'):  self.lcd.text('%s'       % msg['u'].upper(),  0, 1)
+        if has('u'):  self.lcd.text('%s'       % msg['u'],  0, 1)
         if has('f'):  self.lcd.text('%8uF'     % msg['f'],  0, 2)
         if has('s'):  self.lcd.text('%8dS'     % msg['s'],  0, 3)
 
index c14e9d413997c16341c72ccfbf54b1f4bfe7f6af..336ceb4895934938ed40d302b80eee0e166f2173 100644 (file)
 
 import struct
 
-from inevent import Format
 from inevent.Constants import *
 
 
+_format = 'llHHi'
+size = struct.calcsize(_format)
+
 
 class Event(object):
   """
@@ -97,8 +99,7 @@ class Event(object):
     tint = long(self.time)
     tfrac = long((self.time - tint) * 1000000)
 
-    return \
-        struct.pack(Format.Event, tsec, tfrac, self.type, self.code, self.value)
+    return struct.pack(_format, tsec, tfrac, self.type, self.code, self.value)
 
 
   def decode(self, s):
@@ -109,7 +110,6 @@ class Event(object):
       *s*
         A binary structure packed into a string.
     """
-    tsec, tfrac, self.type, self.code, self.value = \
-        struct.unpack(Format.Event, s)
+    tsec, tfrac, self.type, self.code, self.value = struct.unpack(_format, s)
 
     self.time = tsec + tfrac / 1000000.0
index 408e5ecb93f7aabc13b9d839fa46c2deda227a94..3a4cf6be5c8b13cf455b4b3acca5e89e2558db29 100644 (file)
@@ -34,7 +34,6 @@ from inevent.Constants import *
 from inevent import ioctl
 from inevent.AbsAxisScaling import AbsAxisScaling
 from inevent import Event
-from inevent import Format
 from inevent.EventState import EventState
 
 
@@ -155,7 +154,7 @@ class EventStream(object):
     Read and return the next waiting event.
     """
     try:
-      s = os.read(self.filehandle, Format.EventSize)
+      s = os.read(self.filehandle, Event.size)
       if s:
         event = Event.Event(self)
         event.decode(s)
diff --git a/src/py/inevent/Format.py b/src/py/inevent/Format.py
deleted file mode 100644 (file)
index d91665e..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-# The inevent Python module was adapted from pi3d.event from the pi3d
-# project.
-#
-# Copyright (c) 2016, Joseph Coffland, Cauldron Development LLC.
-# Copyright (c) 2015, Tim Skillman.
-# Copyright (c) 2015, Paddy Gaunt.
-# Copyright (c) 2015, Tom Ritchford.
-#
-# Permission is hereby granted, free of charge, to any person
-# obtaining a copy of this software and associated documentation files
-# (the "Software"), to deal in the Software without restriction,
-# including without limitation the rights to use, copy, modify, merge,
-# publish, distribute, sublicense, and/or sell copies of the Software,
-# and to permit persons to whom the Software is furnished to do so,
-# subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-
-import struct
-
-Event = 'llHHi'
-EventSize = struct.calcsize(Event)
index c98ae2400e2726b8ebd3d13f7ddd44ca348dc52b..261a5d62d03b958328f7b810210edeeb9bda241a 100644 (file)
@@ -157,7 +157,7 @@ class InEvent(object):
     for stream in self.streams:
       if stream.filehandle == fd:
         while True:
-          event = stream.read()
+          event = stream.next()
           if event: self.handler.event(event, self.cb)
           else: break