Skip to content

Commit ee47f16

Browse files
author
Cheton Wu (RD-TW)
committed
Initial commit
1 parent 1661c4a commit ee47f16

File tree

15 files changed

+866
-2
lines changed

15 files changed

+866
-2
lines changed

.babelrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
presets: ["es2015", "stage-0"]
3+
}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
/coverage

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/coverage

.travis.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
language: node_js
2+
node_js:
3+
- '4.1'
4+
- '4.0'
5+
- '0.12'
6+
- '0.10'
7+
- 'iojs'
8+
after_success:
9+
- npm run coveralls
10+
- npm run coverage-clean

README.md

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,115 @@
1-
# gcode-interpreter
2-
A G-code interpreter for Node.js
1+
# gcode-interpreter [![build status](https://travis-ci.org/cheton/gcode-interpreter.svg?branch=master)](https://travis-ci.org/cheton/gcode-interpreter) [![Coverage Status](https://coveralls.io/repos/cheton/gcode-interpreter/badge.svg?branch=master&service=github)](https://coveralls.io/github/cheton/gcode-interpreter?branch=master)
2+
[![NPM](https://nodei.co/npm/gcode-interpreter.png?downloads=true&stars=true)](https://nodei.co/npm/gcode-interpreter/)
3+
4+
## Install
5+
6+
`npm install --save gcode-interpreter`
7+
8+
## Usage
9+
10+
```js
11+
var fs = require('fs');
12+
var util = require('util');
13+
var GCodeInterpreter = require('gcode-interpreter').GCodeInterpreter;
14+
15+
var GCodeRunner = function() {
16+
GCodeInterpreter.call(this);
17+
};
18+
util.inherits(GCodeRunner, GCodeInterpreter);
19+
20+
GCodeRunner.prototype.G0 = function(args) {
21+
console.log('G0', args);
22+
};
23+
24+
GCodeRunner.prototype.G1 = function(args) {
25+
console.log('G1', args);
26+
};
27+
28+
var runner = new GCodeRunner();
29+
30+
// Interpret G-code from file
31+
var file = 'example.nc';
32+
runner.interpretFile(file, function(err, data) {
33+
});
34+
35+
// Interpret G-code from stream
36+
var stream = fs.createReadStream(file, { encoding: 'utf8' });
37+
runner.interpretStream(stream, function(err, data) {
38+
});
39+
40+
// Interpret G-code from text string
41+
var text = fs.readFileSync(file, 'utf8');
42+
runner.interpretText(text, function(err, data) {
43+
});
44+
```
45+
46+
## Examples
47+
48+
You can run this example with babel-node:
49+
```js
50+
import { GCodeInterpreter } from 'gcode-interpreter';
51+
52+
const GCODE_TEXT = [
53+
'N1 G17 G20 G90 G94 G54',
54+
'N2 G0 Z0.25',
55+
'N3 X-0.5 Y0.',
56+
'N4 Z0.1',
57+
'N5 G01 Z0. F5.',
58+
'N6 G02 X0. Y0.5 I0.5 J0. F2.5',
59+
'N7 X0.5 Y0. I0. J-0.5',
60+
'N8 X0. Y-0.5 I-0.5 J0.',
61+
'N9 X-0.5 Y0. I0. J0.5',
62+
'N10 G01 Z0.1 F5.',
63+
'N11 G00 X0. Y0. Z0.25'
64+
].join('\n');
65+
66+
class GCodeRunner extends GCodeInterpreter {
67+
G17() {
68+
console.log('G17');
69+
}
70+
G20() {
71+
console.log('G20');
72+
}
73+
G90() {
74+
console.log('G90');
75+
}
76+
G94() {
77+
console.log('G94');
78+
}
79+
G54() {
80+
console.log('G54');
81+
}
82+
G0(args) {
83+
console.log('G0', args);
84+
}
85+
G1(args) {
86+
console.log('G1', args);
87+
}
88+
G2(args) {
89+
console.log('G2', args);
90+
}
91+
}
92+
93+
let runner = new GCodeRunner();
94+
runner.interpretText(GCODE_TEXT, (err, results) => {
95+
});
96+
```
97+
98+
and you will see the output as below:
99+
```
100+
G17
101+
G20
102+
G90
103+
G94
104+
G54
105+
G0 { Z: 0.25 }
106+
G0 { X: -0.5, Y: 0 }
107+
G0 { Z: 0.1 }
108+
G1 { Z: 0, F: 5 }
109+
G2 { X: 0, Y: 0.5, I: 0.5, J: 0, F: 2.5 }
110+
G2 { X: 0.5, Y: 0, I: 0, J: -0.5 }
111+
G2 { X: 0, Y: -0.5, I: -0.5, J: 0 }
112+
G2 { X: -0.5, Y: 0, I: 0, J: 0.5 }
113+
G1 { Z: 0.1, F: 5 }
114+
G0 { X: 0, Y: 0, Z: 0.25 }
115+
```

dist/gcode-interpreter.js

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
'use strict';
2+
3+
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
4+
5+
Object.defineProperty(exports, "__esModule", {
6+
value: true
7+
});
8+
exports.GCodeInterpreter = undefined;
9+
10+
var _lodash = require('lodash');
11+
12+
var _lodash2 = _interopRequireDefault(_lodash);
13+
14+
var _byline = require('byline');
15+
16+
var _byline2 = _interopRequireDefault(_byline);
17+
18+
var _fs = require('fs');
19+
20+
var _fs2 = _interopRequireDefault(_fs);
21+
22+
var _stream = require('stream');
23+
24+
var _stream2 = _interopRequireDefault(_stream);
25+
26+
var _gcodeParser = require('./gcode-parser');
27+
28+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
29+
30+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
31+
32+
var streamify = function streamify(text) {
33+
var s = new _stream2.default.Readable();
34+
s.push(text);
35+
s.push(null);
36+
};
37+
38+
var partitionWordsByGroup = function partitionWordsByGroup(words) {
39+
var groups = [];
40+
41+
_lodash2.default.each(words, function (word) {
42+
var letter = word[0];
43+
var argument = word[1];
44+
45+
if (_lodash2.default.includes(['G', 'M'], letter)) {
46+
groups.push([word]);
47+
return;
48+
}
49+
50+
if (_lodash2.default.size(groups) > 0) {
51+
groups[groups.length - 1].push(word);
52+
} else {
53+
groups.push([word]);
54+
}
55+
});
56+
57+
return groups;
58+
};
59+
60+
var GCodeInterpreter = (function () {
61+
function GCodeInterpreter() {
62+
_classCallCheck(this, GCodeInterpreter);
63+
64+
this.cmd = '';
65+
}
66+
67+
_createClass(GCodeInterpreter, [{
68+
key: '_interpret',
69+
value: function _interpret(data) {
70+
var _this = this;
71+
72+
var groups = partitionWordsByGroup(data.words);
73+
_lodash2.default.each(groups, function (words) {
74+
var word = words[0] || [];
75+
var letter = word[0];
76+
var arg = word[1];
77+
var cmd = (letter + arg).replace('.', '_');
78+
var args = {};
79+
80+
if (_lodash2.default.includes(['G', 'M'], letter)) {
81+
_this.cmd = cmd;
82+
args = _lodash2.default.zipObject(words.slice(1));
83+
} else {
84+
// Use the same command if the line does not start with Gxx or Mxx.
85+
// For example:
86+
// G0 Z0.25
87+
// X-0.5 Y0.
88+
// Z0.1
89+
// G01 Z0. F5.
90+
// X0.5 Y0. I0. J-0.5
91+
// X0. Y-0.5 I-0.5 J0.
92+
// X-0.5 Y0. I0. J0.5
93+
cmd = _this.cmd;
94+
args = _lodash2.default.zipObject(words);
95+
}
96+
97+
if (typeof _this[cmd] === 'function') {
98+
var func = _this[cmd].bind(_this);
99+
func(args);
100+
}
101+
console.log('#####', data['N'], cmd, args);
102+
});
103+
}
104+
}, {
105+
key: 'interpretStream',
106+
value: function interpretStream(stream, callback) {
107+
var _this2 = this;
108+
109+
var results = [];
110+
callback = callback || function () {};
111+
return (0, _byline2.default)(stream).pipe(new _gcodeParser.GCodeParser()).on('data', function (data) {
112+
results.push(data);
113+
_this2._interpret(data);
114+
}).on('end', function () {
115+
callback(null, results);
116+
}).on('error', callback);
117+
}
118+
}, {
119+
key: 'interpretFile',
120+
value: function interpretFile(file, callback) {
121+
var s = _fs2.default.createReadStream(file, { encoding: 'utf8' });
122+
return this.interpretStream(s, callback);
123+
}
124+
}, {
125+
key: 'interpretText',
126+
value: function interpretText(text, callback) {
127+
var s = streamify(text);
128+
return this.interpretStream(s, callback);
129+
}
130+
}]);
131+
132+
return GCodeInterpreter;
133+
})();
134+
135+
exports.GCodeInterpreter = GCodeInterpreter;

dist/gcode-parser.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
'use strict';
2+
3+
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
4+
5+
Object.defineProperty(exports, "__esModule", {
6+
value: true
7+
});
8+
exports.parseText = exports.parseFile = exports.parseStream = exports.GCodeParser = undefined;
9+
10+
var _lodash = require('lodash');
11+
12+
var _lodash2 = _interopRequireDefault(_lodash);
13+
14+
var _byline = require('byline');
15+
16+
var _byline2 = _interopRequireDefault(_byline);
17+
18+
var _fs = require('fs');
19+
20+
var _fs2 = _interopRequireDefault(_fs);
21+
22+
var _stream = require('stream');
23+
24+
var _stream2 = _interopRequireDefault(_stream);
25+
26+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
27+
28+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
29+
30+
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
31+
32+
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
33+
34+
var streamify = function streamify(text) {
35+
var s = new _stream2.default.Readable();
36+
s.push(text);
37+
s.push(null);
38+
};
39+
40+
var stripComments = function stripComments(s) {
41+
var re1 = /^\s+|\s+$/g; // Strip leading and trailing spaces
42+
var re2 = /\s*[#;].*$/g; // Strip everything after # or ; to the end of the line, including preceding spaces
43+
return s.replace(re1, '').replace(re2, '');
44+
};
45+
46+
var GCodeParser = (function (_Transform) {
47+
_inherits(GCodeParser, _Transform);
48+
49+
function GCodeParser(options) {
50+
_classCallCheck(this, GCodeParser);
51+
52+
var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(GCodeParser).call(this, _lodash2.default.extend({}, options, { objectMode: true })));
53+
54+
_this.options = options || {};
55+
return _this;
56+
}
57+
58+
_createClass(GCodeParser, [{
59+
key: '_transform',
60+
value: function _transform(chunk, encoding, next) {
61+
if (encoding === 'buffer') {
62+
chunk = chunk.toString('utf8');
63+
}
64+
65+
chunk = _lodash2.default.trim(stripComments(chunk));
66+
var list = chunk.match(/([a-zA-Z][^\s]*)/igm) || [];
67+
var n = undefined;
68+
var words = [];
69+
_lodash2.default.each(list, function (word) {
70+
var r = word.match(/([a-zA-Z])([^\s]*)/) || [];
71+
var letter = (r[1] || '').toUpperCase();
72+
var argument = _lodash2.default.isNaN(parseFloat(r[2])) ? r[2] : Number(r[2]);
73+
74+
if (letter === 'N' && typeof n === 'undefined') {
75+
// Line (block) number in program
76+
n = Number(argument);
77+
return;
78+
}
79+
80+
words.push([letter, argument]);
81+
});
82+
83+
this.push({
84+
'N': n,
85+
'words': words
86+
});
87+
88+
next();
89+
}
90+
}, {
91+
key: '_flush',
92+
value: function _flush(done) {
93+
done();
94+
}
95+
}]);
96+
97+
return GCodeParser;
98+
})(_stream.Transform);
99+
100+
var parseStream = function parseStream(stream, callback) {
101+
var results = [];
102+
callback = callback || function () {};
103+
return (0, _byline2.default)(stream).pipe(new GCodeParser()).on('data', function (data) {
104+
results.push(data);
105+
}).on('end', function () {
106+
callback(null, results);
107+
}).on('error', callback);
108+
};
109+
110+
var parseFile = function parseFile(file, callback) {
111+
var s = _fs2.default.createReadStream(file, { encoding: 'utf8' });
112+
return parseStream(s, callback);
113+
};
114+
115+
var parseText = function parseText(text, callback) {
116+
var s = streamify(text);
117+
return parseStream(s, callback);
118+
};
119+
120+
exports.GCodeParser = GCodeParser;
121+
exports.parseStream = parseStream;
122+
exports.parseFile = parseFile;
123+
exports.parseText = parseText;

0 commit comments

Comments
 (0)