Merge branch 'develop' into feature/support

This commit is contained in:
casperlamboo 2018-01-24 12:50:47 +01:00
commit 1af76e6ef1
6 changed files with 210 additions and 37 deletions

21
package-lock.json generated
View File

@ -54,8 +54,8 @@
"react-notification-system-redux": "1.2.0",
"react-redux": "5.0.6",
"react-resize-detector": "1.1.0",
"react-svg-inline": "2.0.1",
"redux-form": "7.2.0",
"react-svg-inline": "2.1.0",
"redux-form": "7.2.1",
"redux-undo": "1.0.0-beta9-9-7",
"reselect": "3.0.1",
"semver": "5.4.1",
@ -6942,9 +6942,9 @@
}
},
"react-svg-inline": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/react-svg-inline/-/react-svg-inline-2.0.1.tgz",
"integrity": "sha512-9YVqJ80g1gPWAvD9CS/z4cKPD45ZSMjjzwxFAmQJiMEoAo1Ajhz92WirXag3ftltDN5lPNkVWx/KOnEWB/PaMQ==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/react-svg-inline/-/react-svg-inline-2.1.0.tgz",
"integrity": "sha512-GzRID5IcEQ8dnnaUtTb9MDTAbhuaOiVKKAVLgrCNuehHsg3DuZbe82bjc9JhmPv0zsDWhDrJwzADNgzEvE6VeQ==",
"requires": {
"classnames": "2.2.5",
"prop-types": "15.6.0"
@ -7058,9 +7058,9 @@
}
},
"redux-form": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/redux-form/-/redux-form-7.2.0.tgz",
"integrity": "sha512-qbgeI19drwnm9FeGAotDA1vsZO8q94XF7IxPDuJmSXxDYX2rqzhND6NROahCBJfBK5xM1cchvmgscO2rry1EEw==",
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/redux-form/-/redux-form-7.2.1.tgz",
"integrity": "sha512-KWV+rq+L1QGoRSKoJXbGS8Mw2q4ta5FVyGxW5ZYnAEjXZAukvUCkqDUzobBmOqiRHvrZ3/ssEA7kJFdu7rV8+w==",
"requires": {
"deep-equal": "1.0.1",
"es6-error": "4.1.1",
@ -8271,6 +8271,11 @@
"resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",
"integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA="
},
"validate-ip": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/validate-ip/-/validate-ip-1.0.1.tgz",
"integrity": "sha1-615PY+HRq8buRuGK4gaXv1vtBto="
},
"validate-npm-package-license": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",

View File

@ -33,6 +33,7 @@
"react-resize-detector": "^1.1.0",
"shortid": "^2.2.8",
"three": "^0.88.0",
"validate-ip": "^1.0.1",
"webpack-bundle-analyzer": "^2.9.2"
},
"devDependencies": {

View File

@ -18,6 +18,7 @@ import materialSettings from '../settings/material.yml';
import qualitySettings from '../settings/quality.yml';
import update from 'react-addons-update';
import SettingsIcon from 'material-ui-icons/Settings';
import validateIp from 'validate-ip';
const styles = {
textFieldRow: {
@ -79,6 +80,7 @@ class Settings extends React.Component {
open: false,
name: '',
printer: '',
ip: '',
error: null
},
managePrinter: {
@ -102,25 +104,22 @@ class Settings extends React.Component {
let state = _.cloneDeep(this.state);
const removeAddPrinterError = () => {
state = update(state, { addPrinter: { error: { $set: null } } });
};
switch (fieldName) {
case 'managePrinter.printer':
case 'managePrinter.name':
case 'managePrinter.ip':
state = _.set(state, fieldName, value);
state = update(state, { managePrinter: { error: { $set: null } } });
break;
case 'addPrinter.printer':
state = update(state, { addPrinter: { printer: { $set: value } } });
state = update(state, { addPrinter: { name: { $set: printerSettings[value].title } } });
removeAddPrinterError();
break;
case 'addPrinter.name':
state = update(state, { addPrinter: { name: { $set: value } } });
removeAddPrinterError();
case 'addPrinter.ip':
state = _.set(state, fieldName, value);
if (fieldName === 'addPrinter.printer') {
state = update(state, { addPrinter: { name: { $set: printerSettings[value].title } } });
}
state = update(state, { addPrinter: { error: { $set: null } } });
break;
case 'activePrinter':
@ -212,12 +211,13 @@ class Settings extends React.Component {
constructSettings(localStorage) {
if (!localStorage.active) return defaultSettings;
const { printer, material, quality, advanced } = localStorage.printers[localStorage.active].settings;
const { ip, settings: { printer, material, quality, advanced } } = localStorage.printers[localStorage.active];
let settings = {
...defaultSettings,
printer,
material,
quality
quality,
ip
};
settings = _.merge({}, settings, printerSettings[printer]);
@ -233,10 +233,14 @@ class Settings extends React.Component {
}
addPrinter = () => {
const { name, printer } = this.state.addPrinter;
const { name, printer, ip } = this.state.addPrinter;
if (!name || !printer) {
this.setState({ addPrinter: { ...this.state.addPrinter, error: 'Please enter a name and printer' } });
this.setState(update(this.state, { addPrinter: { error: { $set: 'Please enter a name and printer' } } }));
return;
}
if (printer === 'doodle3d_printer' && !validateIp(ip)) {
this.setState(update(this.state, { addPrinter: { error: { $set: 'Please enter a valid IP adress' } } }));
return;
}
@ -245,7 +249,7 @@ class Settings extends React.Component {
active: id,
printers: {
...this.state.localStorage.printers,
[id]: { name, settings: { printer, material: 'pla', quality: 'medium', advanced: {} } }
[id]: { name, ip, settings: { printer, material: 'pla', quality: 'medium', advanced: {} } }
}
};
this.setState({ localStorage });
@ -258,11 +262,22 @@ class Settings extends React.Component {
};
editPrinter = () => {
const { localStorage: { active, printers }, managePrinter: { printer, name } } = this.state;
const { localStorage: { active, printers }, managePrinter: { printer, name, ip } } = this.state;
if (!name) {
this.setState(update(this.state, { managePrinter: { error: { $set: 'Please enter a name' } } }));
return;
}
if (printer === 'doodle3d_printer' && !validateIp(ip)) {
this.setState(update(this.state, { managePrinter: { error: { $set: 'Please enter a valid IP adress' } } }));
return;
}
const localStorage = update(this.state.localStorage, {
printers: {
[active]: {
name: { $set: name },
ip: { $set: ip },
settings: {
printer: { $set: printer }
}
@ -296,7 +311,7 @@ class Settings extends React.Component {
closeAddPrinterDialog = () => this.setAddPrinterDialog(false);
openAddPrinterDialog = () => this.setAddPrinterDialog(true);
setAddPrinterDialog = (open) => this.setState({ addPrinter: { name: '', printer: '', error: null, open } });
setAddPrinterDialog = (open) => this.setState({ addPrinter: { ip: '', name: '', printer: '', error: null, open } });
closeManagePrinterDialog = () => this.setManagePrinterDialog(false);
openManagePrinterDialog = () => this.setManagePrinterDialog(true);
@ -307,7 +322,9 @@ class Settings extends React.Component {
managePrinter: {
open,
name: printers[active].name,
printer: printers[active].settings.printer
ip: printers[active].ip,
printer: printers[active].settings.printer,
error: null
}
});
}
@ -432,6 +449,7 @@ class Settings extends React.Component {
))}
</SelectField>
<TextField name="addPrinter.name" floatingLabelText="Name" fullWidth />
{(addPrinter.printer === 'doodle3d_printer') && <TextField name="addPrinter.ip" floatingLabelText="IP Adress" fullWidth />}
{addPrinter.error && <p className={classes.error}>{addPrinter.error}</p>}
</Dialog>
<Dialog
@ -461,6 +479,8 @@ class Settings extends React.Component {
))}
</SelectField>
<TextField name="managePrinter.name" floatingLabelText="Name" fullWidth />
{(managePrinter.printer === 'doodle3d_printer') && <TextField name="managePrinter.ip" floatingLabelText="IP Adress" fullWidth />}
{managePrinter.error && <p className={classes.error}>{managePrinter.error}</p>}
</Dialog>
</div>
);

View File

@ -209,6 +209,13 @@ class Interface extends React.Component {
const { name } = this.props;
if (isSlicing) return;
if (!settings) {
this.setState({ error: 'please select a printer first' });
}
if (target === 'WIFI' && !settings.ip) {
this.setState({ error: 'please connect to a WiFi enabled printer' });
return;
}
if (!mesh) {
this.setState({ error: 'there is no file to slice' });
return;
@ -311,7 +318,7 @@ class Interface extends React.Component {
render() {
const { classes, onCancel } = this.props;
const { isSlicing, progress, showFullScreen, error, objectDimensions, openUrlDialog } = this.state;
const { isSlicing, progress, showFullScreen, error, objectDimensions, openUrlDialog, settings } = this.state;
const style = { ...(showFullScreen ? {} : { maxWidth: 'inherit', width: '100%', height: '100%' }) };
@ -349,7 +356,7 @@ class Interface extends React.Component {
onRequestClose={this.closePopover}
>
<Menu>
<MenuItem primaryText="Send over WiFi" onTouchTap={() => this.slice('WIFI')} />
<MenuItem disabled={!Boolean(settings && settings.ip)} primaryText="Send over WiFi" onTouchTap={() => this.slice('WIFI')} />
<MenuItem primaryText="Download GCode" onTouchTap={() => this.slice('DOWNLOAD')} />
</Menu>
</Popover>

View File

@ -106,9 +106,10 @@ export function fetchProgress(url, data = {}, onProgress) {
if (xhr.upload && onProgress) xhr.upload.onprogress = onProgress;
if (xhr.responseType) xhr.responseType = 'blob';
request.headers.forEach((value, name) => {
xhr.setRequestHeader(name, value)
});
// Malyan printer doesn't like headers...
// request.headers.forEach((value, name) => {
// xhr.setRequestHeader(name, value)
// });
xhr.send(data.body);
});
@ -118,8 +119,6 @@ const GCODE_SERVER_URL = 'https://gcodeserver.doodle3d.com';
const CONNECT_URL = 'http://connect.doodle3d.com/';
export async function slice(target, name, mesh, settings, updateProgress) {
if (!settings) throw { message: 'please select a printer first', code: 0 };
let steps;
let currentStep = 0;
switch (target) {
@ -127,6 +126,7 @@ export async function slice(target, name, mesh, settings, updateProgress) {
steps = 1;
break;
case 'WIFI':
case 'DOODLE3D-WIFI-BOX':
steps = 2;
break;
default:
@ -151,12 +151,30 @@ export async function slice(target, name, mesh, settings, updateProgress) {
switch (target) {
case 'DOWNLOAD': {
const blob = new File([gcode], `${name}.gcode`, { type: 'text/plain;charset=utf-8' });
const blob = new File([gcode], `${name}.gcode`, { type: 'text/plain' });
fileSaver.saveAs(blob);
break;
}
case 'WIFI': {
const body = new FormData();
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}/upload`, { method: 'POST', body }, (progress) => {
updateProgress({
action: 'Uploading',
percentage: currentStep / steps + progress.loaded / progress.total / steps
});
});
currentStep ++;
await fetchProgress(`http://${settings.ip}/set?code=M566 ${name}.gcode`, { method: 'GET' });
await fetchProgress(`http://${settings.ip}/set?code=M565`, { method: 'GET' });
break;
}
case 'DOODLE3D-WIFI-BOX': {
// upload G-code file to AWS S3
const { data: { reservation, id } } = await fetch(`${GCODE_SERVER_URL}/upload`, { method: 'POST' })
.then(response => response.json());
@ -176,10 +194,10 @@ export async function slice(target, name, mesh, settings, updateProgress) {
}).trim()}\n${gcode}`;
body.append('file', file);
await fetchProgress(reservation.url, { method: 'POST', body }, (progess) => {
await fetchProgress(reservation.url, { method: 'POST', body }, (progress) => {
updateProgress({
action: 'Uploading',
percentage: currentStep / steps + progess.loaded / progess.total / steps
percentage: currentStep / steps + progress.loaded / progress.total / steps
});
});
currentStep ++;

122
test.js Normal file
View File

@ -0,0 +1,122 @@
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 (
<span>
<button onTouchTap={this.home} type="button">Home</button>
<button onTouchTap={this.status} type="button">Status</button>
<button onTouchTap={this.start} type="button">Start</button>
<button onTouchTap={this.stop} type="button">Stop</button>
<div>
<textarea ref="gcode" cols="80" rows="20" defaultValue={CIRCLE} />
<button onTouchTap={this.upload} type="button">Upload</button>
</div>
</span>
);
}
}
render((
<Print />
), document.getElementById('app'));