#main
.header
- img(src="/images/buildbotics_logo.png")
- .logo
- span.left Build
- span.right botics
- h2 Machine Controller
+ .header-content
+ .estop(:class="{active: state.es}")
+ estop(@click="estop")
+
+ .banner
+ img(src="/images/buildbotics_logo.png")
+ .title
+ span.left Build
+ span.right botics
+ .subtitle Machine Controller
.content(class="{{currentView}}-view")
component(:is="currentView + '-view'", :index="index",
script#control-view-template(type="text/x-template")
#control
- .jog
- axis-control(axes="XY", :colors="['red', 'green']",
- :enabled="[enabled('x'), enabled('y')]",
- v-if="enabled('x') || enabled('y')")
- axis-control(axes="AZ", :colors="['orange', 'blue']",
- :enabled="[enabled('a'), enabled('z')]",
- v-if="enabled('a') || enabled('z')")
- axis-control(axes="BC", :colors="['cyan', 'purple']",
- :enabled="[enabled('b'), enabled('c')]",
- v-if="enabled('b') || enabled('c')")
-
-
- .estop(:class="{active: state.es}")
- estop(@click="estop")
-
-
- table.info
- tr
- th Tool
- td {{state.t || 0}}
- tr
- th Velocity
- td {{state.v || 0 | fixed 0}} mm/min
- tr
- th Feed
- td {{state.f || 0}} mm/min
- tr
- th Speed
- td {{state.s || 0}} RPM
- tr
- th Direction
- td {{state.sd || 'Clockwise'}}
- tr
- th Mist
- td {{state.mist || 'Off'}}
- tr
- th Coolant
- td {{state.coolant || 'Off'}}
- tr
- th State
- td {{state.x || ''}}
- tr
- th Cycle
- td {{state.c || ''}}
-
-
table.axes
tr
- th Axis
- th Position
- th Offset
- th Errors
- th Actions
+ th.name Axis
+ th.position Position
+ th.offset Offset
+ th.errors Errors
+ th.actions Actions
each axis in 'xyzabc'
tr.axis(class="axis-#{axis}", v-if="enabled('#{axis}')")
.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.",
+ button.pure-button(title="Zero {{'#{axis}' | upper}} axis.",
@click="zero('#{axis}')")
| ∅
- button.pure-button(title="Home #{axis} axis.",
+ button.pure-button(title="Home {{'#{axis}' | upper}} axis.",
@click="home('#{axis}')")
.fa.fa-home
+ table.info
+ tr
+ th State
+ td {{get_state()}}
+ td
+ tr
+ th Feed
+ td {{state.f || 0}}
+ td mm/min
+ tr
+ th Speed
+ td {{state.s || 0}}
+ td RPM
+ tr
+ th Direction
+ td {{state.sd || 'Clockwise'}}
+ td
- .overrides
- | Override:
- .override
- label Feed
- input(title="Feed rate override.", type="range", min="0", max="2",
- step="0.01", v-model="feed_override", @change="override_feed")
- span.percent {{feed_override | percent 0}}
-
- .override
- label Speed
- input(title="Speed override.", type="range", min="0", max="2",
- step="0.01", v-model="speed_override", @change="override_speed")
- span.percent {{speed_override | percent 0}}
+ table.info
+ tr
+ th Velocity
+ td {{state.v || 0 | fixed 0}}
+ td mm/min
+ tr
+ th Tool
+ td {{state.t || 0}}
+ td
+ tr
+ th Mist
+ td {{state.mist || 'Off'}}
+ td
+ tr
+ th Coolant
+ td {{state.coolant || 'Off'}}
+ td
+ .override(title="Feed rate override.")
+ label Feed
+ input(type="range", min="0", max="2", step="0.01",
+ v-model="feed_override", @change="override_feed")
+ span.percent {{feed_override | percent 0}}
- .mdi.pure-form
- fieldset
- button.pure-button.pure-button-primary(
- title="Manually execute instructions.", @click="submit_mdi",
- :disabled="state.x != 'ready'") MDI
- input(v-model="mdi", @keyup.enter="submit_mdi")
+ .override(title="Spindle speed override.")
+ label Speed
+ input(type="range", min="0", max="2", step="0.01",
+ v-model="speed_override", @change="override_speed")
+ span.percent {{speed_override | percent 0}}
.toolbar
:disabled="(state.x != 'ready' && state.x != 'holding') || !file")
.fa.fa-step-forward
- .spacer
-
- button.pure-button(title="Upload a new program file.", @click="open",
- :disabled="state.x != 'ready'")
- .fa.fa-folder-open
-
- input.gcode-file-input(type="file", @change="upload",
- style="display:none", accept=".nc,.gcode,.gc,.ngc")
-
- button.pure-button(title="Delete current program file.", @click="delete",
- :disabled="!file")
- .fa.fa-trash
-
- select(title="Select previously uploaded program files.", v-model="file",
- @change="load", :disabled="state.x != 'ready'")
- option(v-for="file in files", :value="file") {{file}}
-
-
- .gcode {{gcode || 'GCode displays here.'}}
+ .tabs
+ input#tab1(type="radio", name="tabs" checked)
+ label(for="tab1") Auto
+
+ input#tab2(type="radio", name="tabs")
+ label(for="tab2") MDI
+
+ input#tab3(type="radio", name="tabs")
+ label(for="tab3") Manual
+
+ section#content1.tab-content
+ .toolbar
+ button.pure-button(title="Upload a new program file.", @click="open",
+ :disabled="state.x == 'running' || state.x == 'stopping'")
+ .fa.fa-folder-open
+
+ input.gcode-file-input(type="file", @change="upload",
+ style="display:none", accept=".nc,.gcode,.gc,.ngc")
+
+ button.pure-button(title="Delete current program file.",
+ @click="delete", :disabled="!file")
+ .fa.fa-trash
+
+ select(title="Select previously uploaded program files.",
+ v-model="file", @change="load",
+ :disabled="state.x == 'running' || state.x == 'stopping'")
+ option(v-for="file in files", :value="file") {{file}}
+
+ .gcode(:class="{placeholder: !gcode}")
+ {{gcode || 'GCode displays here.'}}
+
+ section#content2.tab-content
+ .mdi.pure-form
+ fieldset
+ button.pure-button.pure-button-primary(
+ title="Manually execute instructions.", @click="submit_mdi",
+ :disabled="state.x != 'ready'") MDI
+ input(v-model="mdi", @keyup.enter="submit_mdi")
+
+ .history(:class="{placeholder: !history}")
+ {{history || 'MDI history displays here.'}}
+
+ section#content3.tab-content
+ .jog
+ axis-control(axes="XY", :colors="['red', 'green']",
+ :enabled="[enabled('x'), enabled('y')]",
+ v-if="enabled('x') || enabled('y')")
+ axis-control(axes="AZ", :colors="['orange', 'blue']",
+ :enabled="[enabled('a'), enabled('z')]",
+ v-if="enabled('a') || enabled('z')")
+ axis-control(axes="BC", :colors="['cyan', 'purple']",
+ :enabled="[enabled('b'), enabled('c')]",
+ v-if="enabled('b') || enabled('c')")
svg(version="1.1", xmlns:svg="http://www.w3.org/2000/svg",
xmlns="http://www.w3.org/2000/svg",
xmlns:xlink="http://www.w3.org/1999/xlink",
- width="200", height="200")
+ width="130", height="130")
defs
path#text-path-1(style="fill:none;stroke:none", d="m 73.735867,673.1299 c 0,55.10749 44.673453,99.78094 99.780973,99.78094 55.10748,0 99.78093,-44.67345 99.78093,-99.78094 0,-55.10749 -44.67345,-99.78094 -99.78093,-99.78094 -55.10752,0 -99.780973,44.67345 -99.780973,99.78094 z")
fecomposite(in="offset", in2="SourceGraphic", operator="atop")
- g(transform="scale(0.92, 0.92),translate(-65, -526)")
+ g(transform="scale(0.6, 0.6),translate(-65, -526)")
// Yellow ring
circle.ring(style="fill:#f5e138;filter:url(#filter5266)",
cx="173", cy="633", r="100")
components: {
+ 'estop': {template: '#estop-template'},
'loading-view': {template: '<h1>Loading...</h1>'},
'control-view': require('./control-view'),
'axis-view': require('./axis-view'),
methods: {
+ estop: function () {
+ if (this.state.x == 'estopped') api.put('clear');
+ else api.put('estop');
+ },
+
+
update: function () {
$.get('/config-template.json', {cache: false})
.success(function (data, status, xhr) {
files: [],
axes: 'xyzabc',
gcode: '',
+ history: '',
speed_override: 1,
feed_override: 1
}
components: {
- 'axis-control': require('./axis-control'),
- 'estop': {template: '#estop-template'}
+ 'axis-control': require('./axis-control')
},
events: {
+ // TODO These should all be implemented via the API
jog: function (axis, move) {this.send('g91 g0' + axis + move)},
home: function (axis) {this.send('$home ' + axis)},
zero: function (axis) {this.send('$zero ' + axis)}
methods: {
+ get_state: function () {
+ var state = this.state.x || '';
+ if (state == 'running' && this.state.c) state = this.state.c;
+ return state.toUpperCase();
+ },
+
+
send: function (msg) {
this.$dispatch('send', msg);
},
},
- estop: function () {
- if (this.state.x == 'estopped') api.put('clear').done(this.update);
- else api.put('estop').done(this.update);
- },
-
-
update: function () {
api.get('file')
.done(function (files) {
submit_mdi: function () {
this.send(this.mdi);
+ this.history = this.mdi + '\n' + this.history;
},
},
- fixed: function (value, precision) {
- return value.toFixed(precision);
- }
+ fixed: function (value, precision) {return value.toFixed(precision)},
+ upper: function (value) {return value.toUpperCase()}
}
}
try:
msg = json.loads(line)
- if 'firmware' in msg:
- log.error('AVR rebooted')
- self._stop_sending_gcode()
- self.report()
+ except Exception as e:
+ log.error('%s, data: %s', e, line)
- if 'x' in msg and msg['x'] == 'estopped':
- self._stop_sending_gcode()
+ if 'firmware' in msg:
+ log.error('AVR rebooted')
+ self._stop_sending_gcode()
+ self.report()
- self.vars.update(msg)
- self.ctrl.web.broadcast(msg)
- log.debug(line)
+ if 'x' in msg and msg['x'] == 'estopped':
+ self._stop_sending_gcode()
+
+ self.vars.update(msg)
+ self.ctrl.lcd.update(msg)
+ self.ctrl.web.broadcast(msg)
+
+ log.debug(line)
- except Exception as e:
- log.error('%s, data: %s', e, line)
def queue_command(self, cmd):
class LCD:
def __init__(self, ctrl):
+ self.ctrl = ctrl
+
self.lcd = lcd.LCD(ctrl.args.lcd_port, ctrl.args.lcd_addr)
- self.splash()
atexit.register(self.goodbye)
- def splash(self):
- self.lcd.clear()
- self.lcd.display(0, 'Buildbotics', lcd.JUSTIFY_CENTER)
- self.lcd.display(1, 'Controller', lcd.JUSTIFY_CENTER)
- self.lcd.display(3, '*Ready*', lcd.JUSTIFY_CENTER)
+ def update(self, msg, force = False):
+ def has(name): return force or name in msg
+
+ 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']
+
+ self.lcd.text('%-9s' % state.upper(), 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('f'): self.lcd.text('%8uF' % msg['f'], 0, 2)
+ if has('s'): self.lcd.text('%8dS' % msg['s'], 0, 3)
def goodbye(self):
self.write(LCD_SET_DDRAM_ADDR | (0, 64, 20, 84)[y] + int(x))
- def text(self, msg):
+ def text(self, msg, x = None, y = None):
+ if x is not None and y is not None: self.goto(x, y)
+
for c in msg:
self.write(ord(c), REG_SELECT_BIT)
if x < 0: x = 0
- self.goto(x, line)
- self.text(msg)
+ self.text(msg, x, line)
def shift(self, count = 1, right = True, display = True):
.button-success:not([disabled])
background rgb(28, 184, 65)
-.header img
- vertical-align top
+.header, .content
+ padding 0
-.logo
- font-size 30pt
- font-family Audiowide
- display inline
- margin-right 0.5em
+.header
+ height 140px
+ padding 0
- .left
- color #444
- .right
- color #e5aa3d
+ .header-content
+ max-width 800px
+ margin auto
+ text-align left
+
+ .estop
+ float right
+ margin 5px
+
+ .banner
+ padding-top 40px
+ white-space nowrap
+
+ img
+ vertical-align top
+
+ .title
+ font-size 30pt
+ font-family Audiowide
+ display inline
+ margin-right 0.5em
+
+ .left
+ color #444
+ .right
+ color #e5aa3d
+
+ .subtitle
+ font-size 18pt
+ font-weight 100
+ color #aaa
.error
background red
50%
fill #ff9d00
+.estop
+ width 130px
+ transition 250ms
+
+ &.active .ring
+ animation blink 2s step-start 0s infinite
+
+ svg
+ cursor pointer
+
+ .button:hover
+ filter brightness(120%)
+
.control-view
table
margin 0.5em 0
border 1px solid #ddd
.axes
+ width 100%
+
.axis-x .name
color #f00
td, th
padding 2px
+ white-space nowrap
th
text-align center
.name, .position
font-size 36pt
- line-height 36pt
+ line-height 30pt
- .estop
- display inline-block
- width 190px
- transition 250ms
-
- &.active .ring
- animation blink 2s step-start 0s infinite
-
- svg
- cursor pointer
+ .position
+ width 99%
- .button:hover
- filter brightness(120%)
+ .offset
+ min-width 8em
- .jog
- float right
+ .errors
+ min-width 6em
+ white-space normal
.jog svg
text
text-anchor middle
.info
- float right
- clear right
+ empty-cells show
th, td
padding 3px
text-align right
- td
+ th
+ width 5.25em
+
+ td:nth-child(2)
min-width 8em
- .overrides
- clear both
+ td:nth-child(3)
+ text-align left
- .override
- margin 0.5em
- display inline-block
+ .info:nth-of-type(2)
+ float left
+ clear left
- .percent
- display inline-block
- width 3em
-
- input
- border-radius 0
- margin -0.4em 0.5em
+ .info:nth-of-type(3)
+ float right
+ clear right
- .mdi
- clear both
- white-space nowrap
+ .override
margin 0.5em 0
+ white-space nowrap
+
+ label
+ font-weight bold
+ min-width 3.5em
+ display inline-block
+
+ .percent
+ display inline-block
+ width 3em
input
- width 90%
+ border-radius 0
+ margin -0.4em 0.5em
+
+ .override:nth-of-type(1)
+ clear left
+ float left
+
+ .override:nth-of-type(2)
+ clear right
+ float right
.toolbar
clear both
- margin 0.5em 0
- .spacer
- display inline-block
- width 1px
- height 1px
- margin 0 1em
+ > *
+ margin 0.5em 0
- .gcode
+ .gcode, .history
clear both
- border 2px inset #ccc
- border-radius 5px
overflow auto
width 100%
- max-width 100%
- min-width 100%
+ max-width 99%
+ min-width 99%
height 200px
padding 2px
white-space pre
+
+ &.placeholder
+ color #aaa
+
+ .mdi
+ clear both
+ white-space nowrap
+
+ input
+ width 90%
+
+ .jog
+ text-align center
+
+ > svg
+ margin 1em
+
+
+.tabs
+ clear both
+
+ > input
+ display none
+
+ > label
+ display block
+ float left
+ width 5em
+ font-weight bold
+ cursor pointer
+ text-decoration none
+ text-align center
+ background #f6f6f6
+ border-top-left-radius 5px
+ border-top-right-radius 5px
+ border-top 2px solid #f6f6f6
+ border-left 1px solid #f6f6f6
+ border-right 1px solid #f6f6f6
+ margin-right 2px
+
+ > section
+ display none
+ clear both
+
+ > #tab1:checked ~ #content1,
+ > #tab2:checked ~ #content2,
+ > #tab3:checked ~ #content3
+ display block
+
+ [id^="tab"]:checked + label
+ background #fff
+ border-top 2px solid #ddd
+ border-left 1px solid #ddd
+ border-right 1px solid #ddd
+ border-bottom 1px solid #fff
+ margin-bottom -1px
+
+ .tab-content
+ border 1px solid #ddd
+ padding 0.5em
+
+
+@media only screen and (max-width 48em)
+ .header
+ height auto
+
+ .header-content > .banner
+ padding-top 0
+ clear both
+
+ .control-view #control
+ .axes
+ .axis
+ .name, .position
+ font-size 24pt
+ line-height 24pt
+
+ .offset, .errors
+ display none
+
+ > *:nth-of-type(n)
+ float none
+ clear both
+ width 99%
+
+ .tab_container
+ width 98%