diff --git a/src/interface/MalyanControl.js b/src/interface/MalyanControl.js new file mode 100644 index 0000000..fd5f6e4 --- /dev/null +++ b/src/interface/MalyanControl.js @@ -0,0 +1,61 @@ +import React from 'react'; +import PropTypes from 'proptypes'; +import muiThemeable from 'material-ui/styles/muiThemeable'; +import injectSheet from 'react-jss'; +import FlatButton from 'material-ui/FlatButton'; +import { sleep, getMalyanStatus } from './utils.js'; + +const styles = { + +}; + +class MalyanControl extends React.Component { + static propTypes = { + ip: PropTypes.string.isRequired + }; + + state = { + status: null, + mounted: true + }; + + componentDidMount = async () => { + const { ip } = this.props; + while (this.state.mounted) { + const status = await getMalyanStatus(ip).catch(() => null); + this.setState({ status }); + await sleep(1000); + } + }; + + home = () => { + const { ip } = this.props; + fetch(`http://${ip}/set?code=G28`, { method: 'GET' }); + }; + + stop = () => { + const { ip } = this.props; + fetch(`http://${ip}/set?cmd={P:X}`, { method: 'GET' }); + }; + + componentWillUnmount() { + this.setState({ mounted: false }); + } + + render() { + const { status } = this.state; + return ( +
+ {status && +

Nozzle temperature: {status.nozzleTemperature}/{status.nozzleTargetTemperature}

+

Bed temperature: {status.bedTemperature}/{status.bedTargetTemperature}

+ {status.state === 'printing' &&

Progress: {status.progress}%

} +
} + + +
+ ); + } +} + +export default muiThemeable()(injectSheet(styles)(MalyanControl)); diff --git a/src/interface/index.js b/src/interface/index.js index 7f8f9b2..37e4d36 100644 --- a/src/interface/index.js +++ b/src/interface/index.js @@ -14,6 +14,7 @@ import Menu from 'material-ui/Menu'; import MenuItem from 'material-ui/MenuItem'; import { Tabs, Tab } from 'material-ui/Tabs'; import Settings from './Settings.js'; +import MalyanControl from './MalyanControl.js'; import ReactResizeDetector from 'react-resize-detector'; import JSONToSketchData from 'doodle3d-core/shape/JSONToSketchData'; import createSceneData from 'doodle3d-core/d3/createSceneData.js'; @@ -334,6 +335,7 @@ class Interface extends React.Component { {isSlicing &&

{progress.action}

} {isSlicing && } + {settings && settings.printer === 'doodle3d_printer' && }
{onCancel && response.text()) + .then(statusText => { + const [nozzleTemperature, nozzleTargetTemperature, bedTemperature, bedTargetTemperature, progress] = statusText.match(/\d+/g); + const status = { nozzleTemperature, nozzleTargetTemperature, bedTemperature, bedTargetTemperature, progress }; + + switch (statusText.charAt(statusText.length - 1)) { + case 'I': + status.state = 'idle'; + break; + case 'P': + status.state = 'printing'; + break; + default: + status.state = 'unknown'; + break; + } + return status; + }) +} + +export function sleep(time) { + return new Promise(resolve => setTimeout(resolve, time)); +} + const GCODE_SERVER_URL = 'https://gcodeserver.doodle3d.com'; const CONNECT_URL = 'http://connect.doodle3d.com/'; @@ -127,6 +153,10 @@ export async function slice(target, name, mesh, settings, updateProgress) { break; case 'WIFI': case 'DOODLE3D-WIFI-BOX': + if (settings.printer === 'doodle3d_printer') { + const { state } = await getMalyanStatus(settings.ip); + if (state !== 'idle') throw { message: 'printer must be idle before starting a print', code: 1 }; + } steps = 2; break; default: @@ -161,7 +191,7 @@ export async function slice(target, name, mesh, settings, updateProgress) { const file = new File([gcode], 'doodle.gcode', { type: 'plain/text' }); body.append('file', file); - await fetchProgress(`http://${settings.ip}/set?code=M563 S4`, { method: 'GET' }); + // await fetchProgress(`http://${settings.ip}/set?code=M563 S4`, { method: 'GET' }); await fetchProgress(`http://${settings.ip}/upload`, { method: 'POST', body }, (progress) => { updateProgress({ action: 'Uploading', diff --git a/test.js b/test.js deleted file mode 100644 index d448d41..0000000 --- a/test.js +++ /dev/null @@ -1,122 +0,0 @@ -import 'babel-polyfill' -import React from 'react'; -import { render } from 'react-dom'; -import injectTapEventPlugin from 'react-tap-event-plugin'; -// import './fetch.js'; - -injectTapEventPlugin(); - -const IP = 'http://10.0.0.109'; -const DEFAULT_GCODE = `; Generated with Doodle3D Slicer V0.0.18 -G28 -G1 X30 Y30 -G1 X90 Y30 -G1 X30 Y30 -G1 X90 Y30 -G1 X30 Y30 -G1 X90 Y30 -G1 X30 Y30 -G1 X90 Y30 -G1 X30 Y30 -G1 X90 Y30 -G1 X30 Y30 -G1 X90 Y30 -; test -`; - -const CIRCLE = `; Generated with Doodle3D Slicer V0.0.18 -G28 -G1 X50 Y70 -G1 X56.180339887498945 Y69.02113032590307 -G1 X61.75570504584947 Y66.18033988749895 -G1 X66.18033988749895 Y61.75570504584947 -G1 X69.02113032590307 Y56.180339887498945 -G1 X70 Y50 -G1 X69.02113032590307 Y43.819660112501055 -G1 X66.18033988749895 Y38.24429495415054 -G1 X61.75570504584947 Y33.819660112501055 -G1 X56.18033988749895 Y30.97886967409693 -G1 X50 Y30 -G1 X43.819660112501055 Y30.978869674096927 -G1 X38.24429495415054 Y33.819660112501055 -G1 X33.819660112501055 Y38.24429495415053 -G1 X30.97886967409693 Y43.81966011250105 -G1 X30 Y49.99999999999999 -G1 X30.978869674096927 Y56.180339887498945 -G1 X33.81966011250105 Y61.75570504584946 -G1 X38.24429495415053 Y66.18033988749895 -G1 X43.81966011250105 Y69.02113032590307 -; test -`; - -// export function fetch(url, data = {}, onProgress) { -// return new Promise((resolve, reject) => { -// const request = new Request(url, data); -// const xhr = new XMLHttpRequest(); -// -// xhr.onload = () => { -// const { status, statusText, responseURL: url } = xhr; -// resolve(new Response(xhr.response, { status, statusText, url })); -// } -// xhr.onerror = () => reject(new TypeError('Network request failed')); -// xhr.ontimeout = () => reject(new TypeError('Network request failed')); -// -// xhr.open(request.method, url); -// -// if (request.credentials === 'include') { -// xhr.withCredentials = true -// } else if (request.credentials === 'omit') { -// xhr.withCredentials = false -// } -// if (xhr.upload && onProgress) xhr.upload.onprogress = onProgress; -// if (xhr.responseType) xhr.responseType = 'blob'; -// -// request.headers.forEach((value, name) => { -// xhr.setRequestHeader(name, value) -// }); -// -// xhr.send(data.body); -// }); -// } - -class Print extends React.Component { - home = () => fetch(`${IP}/set?code=G28`, { method: 'GET', mode: 'no-cors' }); - status = () => fetch(`${IP}/inquiry`, { method: 'GET', mode: 'no-cors' }) - .then(response => response.text()) - .then(result => console.log('result: ', result)); - start = () => fetch(`${IP}/set?code=M565`, { method: 'GET', mode: 'no-cors' }); - stop = () => fetch(`${IP}/set?cmd={P:X}`, { method: 'GET', mode: 'no-cors' }); - upload = async () => { - const gcode = this.refs.gcode.value; - - const headers = new Headers(); - headers.append('Content-Disposition', 'form-data; name="file"; filename="doodle.gcode"'); - headers.append('Content-Type', 'application/octet-stream'); - headers.append('Accept', 'application/json'); - - const body = new FormData(); - const file = new File([gcode], 'doodle.gcode', { type: 'application/octet-stream' }); - body.append('file', file); - - const result = await fetch(`${IP}/upload`, { method: 'POST', mode: 'no-cors', headers, body }); - }; - - render() { - return ( - - - - - -
-