RSYNC_EXCLUDE := \*.pyc __pycache__ \*.egg-info \\\#* \*~ .\\\#\*
RSYNC_EXCLUDE := $(patsubst %,--exclude %,$(RSYNC_EXCLUDE))
-RSYNC_OPTS := $(RSYNC_EXCLUDE) -rLv --no-g
+RSYNC_OPTS := $(RSYNC_EXCLUDE) -rLv --no-g --delete --force
ifndef DEST
DEST=mnt
html: templates $(HTML)
css: $(CSS_ASSETS) $(CSS_ASSETS).sha256
+ rm -f $(TARGET)/css/style-*.css
install -D $< $(TARGET)/css/style-$(shell cat $(CSS_ASSETS).sha256).css
js: $(JS_ASSETS) $(JS_ASSETS).sha256
+ rm -f $(TARGET)/js/assets-*.js
install -D $< $(TARGET)/js/assets-$(shell cat $(JS_ASSETS).sha256).js
static: $(STATIC)
td.position {{state.#{axis}p || 0 | fixed 4}}
td.offset {{state.#{axis}o || 0 | fixed 4}}
td.errors
- .fa.fa-hot(v-if="state.#{axis}t", title="Driver overtemp")
- .fa.fa-ban(v-if="state.#{axis}s", title="Motor stalled")
+ .fa.fa-hot(v-if="state.#{axis}t", title="Driver overtemp.")
+ .fa.fa-ban(v-if="state.#{axis}s", title="Motor stalled.")
th.actions
button.pure-button(title="Zero #{axis} axis.",
@click="zero('#{axis}')")
| Override:
.override
label Feed
- input(type="range", min="-1", max="1", step="0.01",
- v-model="feed_override", @change="override_feed")
+ input(title="Feed rate override.", type="range", min="-1", max="1",
+ step="0.01", v-model="feed_override", @change="override_feed")
span.percent {{feed_override | percent 0}}
.override
- label Spindle
- input(type="range", min="-1", max="1", step="0.01",
- v-model="speed_override", @change="override_speed")
+ label Speed
+ input(title="Speed override.", type="range", min="-1", max="1",
+ step="0.01", v-model="speed_override", @change="override_speed")
span.percent {{speed_override | percent 0}}
.mdi.pure-form
fieldset
button.pure-button.pure-button-primary(
- @click="submit_mdi", :disabled="running") MDI
+ title="Manually execute instructions.", @click="submit_mdi",
+ :disabled="running") MDI
input(v-model="mdi", @keyup.enter="submit_mdi")
.toolbar
- button.pure-button(@click="home", :disabled="running")
+ button.pure-button(title="Home the machine.", @click="home",
+ :disabled="running")
.fa.fa-home
- button.pure-button(@click="play_pause()", :disabled="!file")
+ button.pure-button(title="{{running ? 'Pause' : 'Start'}} program.",
+ @click="play_pause()", :disabled="!file")
.fa(:class="running ? 'fa-pause' : 'fa-play'")
- button.pure-button(@click="stop", :disabled="!running")
+ button.pure-button(title="Stop program.", @click="stop",
+ :disabled="!running")
.fa.fa-stop
- button.pure-button(@click="optional_stop", :disabled="!file")
+ button.pure-button(title="Pause program at next optional stop (M1).",
+ @click="optional_stop", :disabled="!file")
.fa.fa-stop-circle-o
- button.pure-button(@click="step", :disabled="running || !file")
+ button.pure-button(title="Execute one program step.", @click="step",
+ :disabled="running || !file")
.fa.fa-step-forward
.spacer
- button.pure-button(@click="open", :disabled="running")
+ button.pure-button(title="Upload a new program file.", @click="open",
+ :disabled="running")
.fa.fa-folder-open
input.gcode-file-input(type="file", @change="upload",
style="display:none", accept=".nc,.gcode,.gc,.ngc")
- button.pure-button(@click="delete", :disabled="!file")
+ button.pure-button(title="Delete current program file.", @click="delete",
+ :disabled="!file")
.fa.fa-trash
- select(v-model="file", @change="load", :disabled="running")
+ select(title="Select previously uploaded program files.", v-model="file",
+ @change="load", :disabled="running")
option(v-for="file in files", :value="file") {{file}}
api.get('load').done(function (data) {
this.config = data;
-
this.parse_hash();
}.bind(this))
}.bind(this))
--- /dev/null
+import json
+import tornado.web
+
+
+class APIHandler(tornado.web.RequestHandler):
+ def prepare(self):
+ self.json = {}
+
+ if self.request.body:
+ try:
+ self.json = tornado.escape.json_decode(self.request.body)
+ except ValueError:
+ self.send_error(400, message = 'Unable to parse JSON.')
+
+
+ def set_default_headers(self):
+ self.set_header('Content-Type', 'application/json')
+
+
+ def write_error(self, status_code, **kwargs):
+ e = {}
+ e['message'] = str(kwargs['exc_info'][1])
+ e['code'] = status_code
+
+ self.write_json(e)
+
+
+ def write_json(self, data):
+ self.write(json.dumps(data))
--- /dev/null
+import os
+import bbctrl
+
+
+class FileHandler(bbctrl.APIHandler):
+ def prepare(self): pass
+
+
+ def delete(self, path):
+ path = 'upload' + path
+ if os.path.exists(path): os.unlink(path)
+ self.write_json('ok')
+
+
+ def put(self, path):
+ path = 'upload' + path
+ if not os.path.exists(path): return
+
+ with open(path, 'r') as f:
+ for line in f:
+ self.application.input_queue.put(line)
+
+
+ def get(self, path):
+ if path:
+ with open('upload/' + path, 'r') as f:
+ self.write_json(f.read())
+ return
+
+ files = []
+
+ if os.path.exists('upload'):
+ for path in os.listdir('upload'):
+ if os.path.isfile('upload/' + path):
+ files.append(path)
+
+ self.write_json(files)
+
+
+ def post(self, path):
+ gcode = self.request.files['gcode'][0]
+
+ if not os.path.exists('upload'): os.mkdir('upload')
+
+ with open('upload/' + gcode['filename'], 'wb') as f:
+ f.write(gcode['body'])
+
+ self.write_json('ok')
log = logging.getLogger('Web')
-class APIHandler(tornado.web.RequestHandler):
- def prepare(self):
- self.json = {}
- if self.request.body:
- try:
- self.json = tornado.escape.json_decode(self.request.body)
- except ValueError:
- self.send_error(400, message = 'Unable to parse JSON.')
-
-
- def set_default_headers(self):
- self.set_header('Content-Type', 'application/json')
-
-
- def write_error(self, status_code, **kwargs):
- e = {}
- e['message'] = str(kwargs['exc_info'][1])
- e['code'] = status_code
-
- self.write_json(e)
-
-
- def write_json(self, data):
- self.write(json.dumps(data))
-
-
-
-class LoadHandler(APIHandler):
+class LoadHandler(bbctrl.APIHandler):
def send_file(self, path):
with open(path, 'r') as f:
self.write_json(json.load(f))
-class SaveHandler(APIHandler):
+class SaveHandler(bbctrl.APIHandler):
def post(self):
with open('config.json', 'w') as f:
json.dump(self.json, f)
-class FileHandler(APIHandler):
- def prepare(self): pass
-
-
- def delete(self, path):
- path = 'upload' + path
- if os.path.exists(path): os.unlink(path)
- self.write_json('ok')
-
-
- def put(self, path):
- path = 'upload' + path
- if not os.path.exists(path): return
-
- with open(path, 'r') as f:
- for line in f:
- self.application.input_queue.put(line)
-
-
- def get(self, path):
- if path:
- with open('upload/' + path, 'r') as f:
- self.write_json(f.read())
- return
-
- files = []
-
- if os.path.exists('upload'):
- for path in os.listdir('upload'):
- if os.path.isfile('upload/' + path):
- files.append(path)
-
- self.write_json(files)
-
-
- def post(self, path):
- gcode = self.request.files['gcode'][0]
-
- if not os.path.exists('upload'): os.mkdir('upload')
-
- with open('upload/' + gcode['filename'], 'wb') as f:
- f.write(gcode['body'])
-
- self.write_json('ok')
-
-
-
class Connection(sockjs.tornado.SockJSConnection):
def heartbeat(self):
self.timer = self.app.ioloop.call_later(3, self.heartbeat)
handlers = [
(r'/api/load', LoadHandler),
(r'/api/save', SaveHandler),
- (r'/api/file(/.*)?', FileHandler),
+ (r'/api/file(/.*)?', bbctrl.FileHandler),
(r'/(.*)', tornado.web.StaticFileHandler,
{'path': bbctrl.get_resource('http/'),
"default_filename": "index.html"}),
from pkg_resources import Requirement, resource_filename
+from bbctrl.APIHandler import APIHandler
+from bbctrl.FileHandler import FileHandler
from bbctrl.LCD import LCD
from bbctrl.AVR import AVR
from bbctrl.Web import Web