g-coder 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in gcoder.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 32leaves
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,4 @@
1
+ gcoder
2
+ ======
3
+
4
+ GCoder is a Ruby library to deal with GCode in various flavours. It's designed to make scripting GCode filters easy, and to work with Opal to enable web-based GCode editors/viewers.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'gcoder/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "g-coder"
8
+ spec.version = GCoder::VERSION
9
+ spec.authors = ["32leaves"]
10
+ spec.email = ["info@32leav.es"]
11
+ spec.summary = %q{GCoder is a Ruby library to deal with GCode in various flavours.}
12
+ spec.description = %q{GCoder is designed to make scripting GCode filters easy, and to work with Opal to enable web-based GCode editors/viewers.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,4 @@
1
+ require 'gcoder/dialects/default'
2
+ require 'gcoder/version'
3
+ require 'gcoder/gcode'
4
+ require 'gcoder/parser'
@@ -0,0 +1,95 @@
1
+
2
+ module GCoder
3
+
4
+ module Dialects
5
+
6
+ def self.default
7
+ map = {}
8
+ map[:C] = {}
9
+ map[:G] = {}
10
+ map[:M] = {}
11
+ map[:G][0] = :MoveRapid
12
+ map[:G][1] = :MoveByFeedrate
13
+ map[:G][2] = :ClockwiseCircularArcAtFeedrate
14
+ map[:G][3] = :CounterClockwiseCircularArcAtFeedrate
15
+ map[:G][4] = :Dwell
16
+ map[:G][9] = :ExactStopCheck
17
+ map[:G][10] = :ProgrammableParameterInput
18
+ map[:G][15] = :TurnPolarCoordinatesOffReturnToCartesianCoordinates
19
+ map[:G][16] = :TurnPolarCoordinatesOn
20
+ map[:G][17] = :SelectXYPlane
21
+ map[:G][18] = :SelectXZPlane
22
+ map[:G][19] = :SelectYZPlane
23
+ map[:G][20] = :ProgramCoordinatesAreInches
24
+ map[:G][21] = :ProgramCoordinatesAreMm
25
+ map[:G][27] = :ReferencePointReturnCheck
26
+ map[:G][28] = :ReturnToHomePosition
27
+ map[:G][29] = :ReturnFromTheReferencePosition
28
+ map[:G][30] = :ReturnToThe2nd3rdAnd4thReferencePoint
29
+ map[:G][32] = :ConstantLeadThreading
30
+ map[:G][40] = :ToolCutterCompensationOff
31
+ map[:G][41] = :ToolCutterCompensationLeft
32
+ map[:G][42] = :ToolCutterCompensationRight
33
+ map[:G][43] = :ApplyToolLengthCompensationPlus
34
+ map[:G][44] = :ApplyToolLengthCompensationMinus
35
+ map[:G][49] = :ToolLengthCompensationCancel
36
+ map[:G][50] = :ResetAllScaleFactorsTo1
37
+ map[:G][51] = :TurnOnScaleFactors
38
+ map[:G][53] = :MachineCoordinateSystem
39
+ map[:G][54] = :WorkCoordinateSystem1stWorkpiece
40
+ map[:G][55] = :WorkCoordinateSystem2ndWorkpiece
41
+ map[:G][56] = :WorkCoordinateSystem3rdWorkpiece
42
+ map[:G][57] = :WorkCoordinateSystem4thWorkpiece
43
+ map[:G][58] = :WorkCoordinateSystem5thWorkpiece
44
+ map[:G][59] = :WorkCoordinateSystem6thWorkpiece
45
+ map[:G][61] = :ExactStopCheckMode
46
+ map[:G][62] = :AutomaticCornerOverride
47
+ map[:G][63] = :TappingMode
48
+ map[:G][64] = :BestSpeedPath
49
+ map[:G][65] = :CustomMacroSimpleCall
50
+ map[:G][68] = :CoordinateSystemRotation
51
+ map[:G][69] = :CancelCoordinateSystemRotation
52
+ map[:G][73] = :HighSpeedDrillingCycle
53
+ map[:G][74] = :LeftHandTappingCycle
54
+ map[:G][76] = :FineBoringCyle
55
+ map[:G][80] = :CancelCannedCycle
56
+ map[:G][81] = :SimpleDrillingCycle
57
+ map[:G][82] = :DrillingCycleWithDwell
58
+ map[:G][83] = :PeckDrillingCycle
59
+ map[:G][84] = :TappingCycle
60
+ map[:G][85] = :BoringCannedCycleNoDwellFeedOut
61
+ map[:G][86] = :BoringCannedCycleSpindleStopRapidOut
62
+ map[:G][87] = :BackBoringCannedCycle
63
+ map[:G][88] = :BoringCannedCycleSpindleStopManualOut
64
+ map[:G][89] = :BoringCannedCycleDwellFeedOut
65
+ map[:G][90] = :AbsoluteProgrammingOfXYZ
66
+ map[:G][91] = :IncrementalProgrammingOfXYZ
67
+ map[:G][92] = :OffsetCoordinateSystemAndSaveParameters
68
+ map[:G][921] = :CancelOffsetAndZeroParameters
69
+ map[:G][922] = :CancelOffsetAndRetainParameters
70
+ map[:G][923] = :OffsetCoordinateSystemWithSavedParameters
71
+ map[:G][94] = :UnitsPerMinuteFeedModeUnitsInInchesOrMm
72
+ map[:G][95] = :UnitsPerRevolutionFeedModeUnitsInInchesOrMm
73
+ map[:G][96] = :ConstantSurfaceSpeed
74
+ map[:G][97] = :CancelConstantSurfaceSpeed
75
+ map[:G][98] = :ReturnToInitialZPlaneAfterCannedCycle
76
+ map[:G][99] = :ReturnToInitialRPlaneAfterCannedCycle
77
+ map[:M][0] = :ProgramStop
78
+ map[:M][1] = :ProgramStopOptional
79
+ map[:M][2] = :EndOfProgram
80
+ map[:M][3] = :SpindleOnCwRotation
81
+ map[:M][4] = :SpindleOnCcwRotation
82
+ map[:M][5] = :SpindleStop
83
+ map[:M][6] = :ToolChange
84
+ map[:M][7] = :MistCoolantOn
85
+ map[:M][8] = :FloodCoolantOn
86
+ map[:M][9] = :CoolantOff
87
+ map[:M][30] = :EndOfProgramRewindAndResetModes
88
+ map[:M][98] = :SubprogramCall
89
+ map[:M][99] = :ReturnFromSubprogram
90
+ map
91
+ end
92
+
93
+ end
94
+
95
+ end
@@ -0,0 +1,321 @@
1
+ require 'delegate'
2
+
3
+ class Object
4
+
5
+ #
6
+ # A combination of to_i and to_f.
7
+ # If this object is nil?, nil is returned.
8
+ # If this object is an integer, to_i is returned.
9
+ # If this object is a floating point number, to_f is returned.
10
+ #
11
+ def to_nif
12
+ if nil?
13
+ nil
14
+ elsif to_i.to_s == self
15
+ to_i
16
+ else
17
+ to_f
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ module GCoder
24
+
25
+ module GCode
26
+
27
+ class ProgramContext < SimpleDelegator
28
+ attr_accessor :position, :feedrate, :units, :absolute
29
+
30
+ def initialize(position = [0,0,0], feedrate = 0, units = :mm, absolute = false)
31
+ super({})
32
+
33
+ @position = position
34
+ @feedrate = feedrate
35
+ @units = units
36
+ @absolute = absolute
37
+ end
38
+
39
+ def absolute?; absolute; end
40
+
41
+ def update_position(pos)
42
+ if absolute
43
+ @position = @position.each_with_index.map {|e, i| pos[i] || @position[i] }
44
+ else
45
+ @position = @position.each_with_index.map {|e, i| @position[i] + (pos[i] || 0) }
46
+ end
47
+ end
48
+
49
+ def update_feedrate(feedrate)
50
+ @feedrate = feedrate unless feedrate.nil?
51
+ end
52
+ end
53
+
54
+ class Program < SimpleDelegator
55
+ include GCoder::GCode
56
+
57
+ def initialize(commands)
58
+ super(commands)
59
+ end
60
+
61
+ #
62
+ # Interpretes aspects of the GCode program, namely position, feedrate, unit of measure
63
+ # and absolute/incremental positioning. This information is made available to the optional
64
+ # block as second parameter, through a ProgramContext instance.
65
+ #
66
+ # Returns an array with two elements, the mapping result and context.
67
+ #
68
+ #
69
+ def map_with_context(&block)
70
+ ctx = ProgramContext.new
71
+
72
+ map_result = self.map do |cmd|
73
+ if block_given?
74
+ r = yield(cmd, ctx)
75
+ else
76
+ r = cmd
77
+ end
78
+
79
+ if cmd.is_a? MoveRapid or cmd.is_a? MoveByFeedrate
80
+ ctx.update_position cmd.position
81
+ ctx.update_feedrate cmd.feedrate
82
+ elsif cmd.is_a? ProgramCoordinatesAreMm
83
+ ctx.units = :mm
84
+ elsif cmd.is_a? ProgramCoordinatesAreInches
85
+ ctx.units = :inch
86
+ elsif cmd.is_a? AbsoluteProgrammingOfXYZ
87
+ ctx.absolute = true
88
+ elsif cmd.is_a? IncrementalProgrammingOfXYZ
89
+ ctx.absolute = false
90
+ end
91
+
92
+ r
93
+ end
94
+
95
+ [map_result, ctx]
96
+ end
97
+
98
+
99
+ end
100
+
101
+ class Command
102
+ attr_reader :code, :args
103
+
104
+ def initialize(code, args)
105
+ @code = code
106
+ @args = args
107
+ end
108
+
109
+ def to_s
110
+ ( [code] + args.map {|e| e.join } ).join(" ")
111
+ end
112
+
113
+ end
114
+
115
+ class Comment < Command
116
+ # Returns the text of the comment without the parentheses
117
+ def text
118
+ code
119
+ end
120
+
121
+ def to_s
122
+ "(#{code})"
123
+ end
124
+ end
125
+
126
+ class CategoryCommand < Command
127
+ end
128
+ class MotionCommand < Command
129
+
130
+ def position
131
+ [ args[:X], args[:Y], args[:Z] ].map {|x| x.to_nif }
132
+ end
133
+
134
+ def feedrate
135
+ args[:F].to_nif
136
+ end
137
+
138
+ end
139
+ class CompensationCommand < Command
140
+ end
141
+ class CoordinateCommand < Command
142
+ end
143
+ class MotionCommand < Command
144
+ end
145
+ class CannedCommand < Command
146
+ end
147
+ class CompensationCommand < Command
148
+ end
149
+ class CoordinateCommand < Command
150
+ end
151
+ class OtherCommand < Command
152
+ end
153
+ class CannedCommand < Command
154
+ end
155
+ class MCodeCommand < Command
156
+ end
157
+
158
+ class MoveRapid < MotionCommand
159
+ end
160
+ class MoveByFeedrate < MotionCommand
161
+ end
162
+ class ClockwiseCircularArcAtFeedrate < MotionCommand
163
+ end
164
+ class CounterClockwiseCircularArcAtFeedrate < MotionCommand
165
+ end
166
+ class Dwell < MotionCommand
167
+ end
168
+ class ExactStopCheck < MotionCommand
169
+ end
170
+ class ProgrammableParameterInput < CompensationCommand
171
+ end
172
+ class TurnPolarCoordinatesOffReturnToCartesianCoordinates < CoordinateCommand
173
+ end
174
+ class TurnPolarCoordinatesOn < CoordinateCommand
175
+ end
176
+ class SelectXYPlane < CoordinateCommand
177
+ end
178
+ class SelectXZPlane < CoordinateCommand
179
+ end
180
+ class SelectYZPlane < CoordinateCommand
181
+ end
182
+ class ProgramCoordinatesAreInches < CoordinateCommand
183
+ end
184
+ class ProgramCoordinatesAreMm < CoordinateCommand
185
+ end
186
+ class AbsoluteProgrammingOfXYZ < CoordinateCommand
187
+ end
188
+ class IncrementalProgrammingOfXYZ < CoordinateCommand
189
+ end
190
+ class ReferencePointReturnCheck < MotionCommand
191
+ end
192
+ class ReturnToHomePosition < MotionCommand
193
+ end
194
+ class ReturnFromTheReferencePosition < MotionCommand
195
+ end
196
+ class ReturnToThe2nd3rdAnd4thReferencePoint < MotionCommand
197
+ end
198
+ class ConstantLeadThreading < CannedCommand
199
+ end
200
+ class ToolCutterCompensationOff < CompensationCommand
201
+ end
202
+ class ToolCutterCompensationLeft < CompensationCommand
203
+ end
204
+ class ToolCutterCompensationRight < CompensationCommand
205
+ end
206
+ class ApplyToolLengthCompensationPlus < CompensationCommand
207
+ end
208
+ class ApplyToolLengthCompensationMinus < CompensationCommand
209
+ end
210
+ class ToolLengthCompensationCancel < CompensationCommand
211
+ end
212
+ class ResetAllScaleFactorsTo1 < CompensationCommand
213
+ end
214
+ class TurnOnScaleFactors < CompensationCommand
215
+ end
216
+ class MachineCoordinateSystem < CoordinateCommand
217
+ end
218
+ class WorkCoordinateSystem1stWorkpiece < CoordinateCommand
219
+ end
220
+ class WorkCoordinateSystem2ndWorkpiece < CoordinateCommand
221
+ end
222
+ class WorkCoordinateSystem3rdWorkpiece < CoordinateCommand
223
+ end
224
+ class WorkCoordinateSystem4thWorkpiece < CoordinateCommand
225
+ end
226
+ class WorkCoordinateSystem5thWorkpiece < CoordinateCommand
227
+ end
228
+ class WorkCoordinateSystem6thWorkpiece < CoordinateCommand
229
+ end
230
+ class ExactStopCheckMode < OtherCommand
231
+ end
232
+ class AutomaticCornerOverride < OtherCommand
233
+ end
234
+ class TappingMode < OtherCommand
235
+ end
236
+ class BestSpeedPath < OtherCommand
237
+ end
238
+ class CustomMacroSimpleCall < OtherCommand
239
+ end
240
+ class CoordinateSystemRotation < CoordinateCommand
241
+ end
242
+ class CancelCoordinateSystemRotation < CoordinateCommand
243
+ end
244
+ class HighSpeedDrillingCycle < CannedCommand
245
+ end
246
+ class LeftHandTappingCycle < CannedCommand
247
+ end
248
+ class FineBoringCyle < CannedCommand
249
+ end
250
+ class CancelCannedCycle < CannedCommand
251
+ end
252
+ class SimpleDrillingCycle < CannedCommand
253
+ end
254
+ class DrillingCycleWithDwell < CannedCommand
255
+ end
256
+ class PeckDrillingCycle < CannedCommand
257
+ end
258
+ class TappingCycle < CannedCommand
259
+ end
260
+ class BoringCannedCycleNoDwellFeedOut < CannedCommand
261
+ end
262
+ class BoringCannedCycleSpindleStopRapidOut < CannedCommand
263
+ end
264
+ class BackBoringCannedCycle < CannedCommand
265
+ end
266
+ class BoringCannedCycleSpindleStopManualOut < CannedCommand
267
+ end
268
+ class BoringCannedCycleDwellFeedOut < CannedCommand
269
+ end
270
+ class OffsetCoordinateSystemAndSaveParameters < CoordinateCommand
271
+ end
272
+ class ClampOfMaximumSpindleSpeed < MotionCommand
273
+ end
274
+ class CancelOffsetAndZeroParameters < CoordinateCommand
275
+ end
276
+ class CancelOffsetAndRetainParameters < CoordinateCommand
277
+ end
278
+ class OffsetCoordinateSystemWithSavedParameters < CoordinateCommand
279
+ end
280
+ class UnitsPerMinuteFeedModeUnitsInInchesOrMm < MotionCommand
281
+ end
282
+ class UnitsPerRevolutionFeedModeUnitsInInchesOrMm < MotionCommand
283
+ end
284
+ class ConstantSurfaceSpeed < MotionCommand
285
+ end
286
+ class CancelConstantSurfaceSpeed < MotionCommand
287
+ end
288
+ class ReturnToInitialZPlaneAfterCannedCycle < CannedCommand
289
+ end
290
+ class ReturnToInitialRPlaneAfterCannedCycle < CannedCommand
291
+ end
292
+ class ProgramStop < MCodeCommand
293
+ end
294
+ class ProgramStopOptional < MCodeCommand
295
+ end
296
+ class EndOfProgram < MCodeCommand
297
+ end
298
+ class SpindleOnCwRotation < MCodeCommand
299
+ end
300
+ class SpindleOnCcwRotation < MCodeCommand
301
+ end
302
+ class SpindleStop < MCodeCommand
303
+ end
304
+ class ToolChange < MCodeCommand
305
+ end
306
+ class MistCoolantOn < MCodeCommand
307
+ end
308
+ class FloodCoolantOn < MCodeCommand
309
+ end
310
+ class CoolantOff < MCodeCommand
311
+ end
312
+ class EndOfProgramRewindAndResetModes < MCodeCommand
313
+ end
314
+ class SubprogramCall < MCodeCommand
315
+ end
316
+ class ReturnFromSubprogram < MCodeCommand
317
+ end
318
+
319
+ end
320
+
321
+ end
@@ -0,0 +1,45 @@
1
+
2
+ module GCoder
3
+
4
+ class Parser
5
+
6
+ #
7
+ # Rudimentary GCode parser that also maps the commands to their respective classes according to the dialect.
8
+ #
9
+ # Returns a GCode::Program containing the commands.
10
+ #
11
+ def parse(code, dialect = GCoder::Dialects::default)
12
+ code = code.split("\n") unless code.is_a? Array
13
+
14
+ commands = code.map do |caa|
15
+ r = nil
16
+ caa.strip!
17
+ unless caa.empty?
18
+ cmd, *args = caa.strip.split(" ")
19
+ root_code = cmd[0...1]
20
+ child_code = cmd[1..-1]
21
+ args = args.inject({}) {|m,e| m[e[0...1].to_sym] = e[1..-1]; m }
22
+
23
+ if root_code == '('
24
+ r = GCoder::GCode::Comment.new(caa.strip[1...-1], {})
25
+ else
26
+ commands = dialect[root_code.to_sym]
27
+ throw "Dialect does not support #{root_code} commands" if commands.nil?
28
+
29
+ unless child_code.nil? or child_code.empty?
30
+ cmd_class = commands[child_code.to_i]
31
+ throw "Dialect does not support command: #{child_code}" if cmd_class.nil?
32
+
33
+ r = eval("GCoder::GCode::#{cmd_class}").new(cmd, args)
34
+ end
35
+ end
36
+ end
37
+ r
38
+ end.compact
39
+
40
+ GCode::Program.new(commands)
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,6 @@
1
+
2
+ module GCoder
3
+
4
+ VERSION = 0.1
5
+
6
+ end
@@ -0,0 +1,13 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ describe 'Default dialect' do
5
+
6
+ it 'should use only classes that exist in GCoder' do
7
+ GCoder::Dialects::default.values.map {|e| e.values }.flatten.each do |classname|
8
+ GCoder::GCode.const_defined?(classname).should be_true, "Undefined class #{classname}"
9
+ GCoder::GCode.const_get(classname).new("Code", {})
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,56 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ describe 'GCode program' do
5
+
6
+ it 'should maintain the correct position (in absolute mode) in map_with_context' do
7
+ program = GCoder::Parser.new.parse("""
8
+ G90
9
+ G0 X0 Y0 Z0 F140
10
+ G1 X42 Y13 Z22.2 F100.001
11
+ G0 X11.1 Y0 Z102
12
+ M0
13
+ """)
14
+
15
+ program.map_with_context.last.absolute?.should eq true
16
+ program.map_with_context {|cmd, ctx| ctx.position }.first.should eq [
17
+ [ 0, 0, 0 ],
18
+ [ 0, 0, 0 ],
19
+ [ 0, 0, 0 ],
20
+ [ 42, 13, 22.2 ],
21
+ [ 11.1, 0, 102 ]
22
+ ]
23
+ end
24
+
25
+ it 'should maintain the correct position (in relative mode) in map_with_context' do
26
+ program = GCoder::Parser.new.parse("""
27
+ G91
28
+ G0 X0 Y0 Z0 F140
29
+ G1 X42 Y13 Z22.2 F100.001
30
+ G0 X11.1 Y0 Z102
31
+ M0
32
+ """)
33
+
34
+ program.map_with_context.last.absolute?.should eq false
35
+ program.map_with_context {|cmd, ctx| ctx.position }.first.should eq [
36
+ [ 0, 0, 0 ],
37
+ [ 0, 0, 0 ],
38
+ [ 0, 0, 0 ],
39
+ [ 42, 13, 22.2 ],
40
+ [ 53.1, 13, 124.2 ]
41
+ ]
42
+ end
43
+
44
+ it 'should maintain the correct feedrate in map_with_context' do
45
+ program = GCoder::Parser.new.parse("""
46
+ G90
47
+ G0 X0 Y0 Z0 F140
48
+ G1 X42 Y13 Z22.2 F100.001
49
+ G0 X11.1 Y0 Z102
50
+ M0
51
+ """)
52
+
53
+ program.map_with_context {|cmd, ctx| ctx.feedrate }.first.should eq [ 0, 0, 140, 100.001, 100.001 ]
54
+ end
55
+
56
+ end
@@ -0,0 +1,54 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ describe 'GCode parser' do
5
+
6
+ before do
7
+ @parser = GCoder::Parser.new
8
+ end
9
+
10
+ it 'should return a GCode program' do
11
+ @parser.parse("G0 X0 Y0 Z0 F1").should be_an_instance_of GCoder::GCode::Program
12
+ end
13
+
14
+ it 'should parse rapid motion GCode' do
15
+ cmd = @parser.parse("G0 X1 Y22.22 Z33.3 F12").first
16
+
17
+ cmd.should be_an_instance_of GCoder::GCode::MoveRapid
18
+ cmd.feedrate.should eq 12
19
+ cmd.position.should eq [ 1, 22.22, 33.3 ]
20
+ end
21
+
22
+ it 'should parse feedrate motion GCode' do
23
+ cmd = @parser.parse("G1 X1 Y22.22 Z33.3 F12").first
24
+
25
+ cmd.should be_an_instance_of GCoder::GCode::MoveByFeedrate
26
+ cmd.feedrate.should eq 12
27
+ cmd.position.should eq [ 1, 22.22, 33.3 ]
28
+ end
29
+
30
+ it 'should omit empty lines' do
31
+ prog = @parser.parse("""
32
+ G0 X1 Y2 Z3
33
+
34
+ M0
35
+ """)
36
+ prog.length.should eq 2
37
+ end
38
+
39
+ it 'should parse comments' do
40
+ comments = [
41
+ "Hello World, this is a comment",
42
+ "Let's try another comment"
43
+ ]
44
+
45
+ prog = @parser.parse(comments.map {|c| "(#{c})\n" }.join("\n"))
46
+
47
+ prog.length.should eq comments.length
48
+ prog.each_with_index do |cmd, idx|
49
+ cmd.should be_an_instance_of GCoder::GCode::Comment
50
+ cmd.text.should eq comments[idx]
51
+ end
52
+ end
53
+
54
+ end
@@ -0,0 +1,4 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
2
+
3
+ require 'gcoder'
4
+ require 'pry'
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: g-coder
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - 32leaves
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-02-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.5'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.5'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: GCoder is designed to make scripting GCode filters easy, and to work
47
+ with Opal to enable web-based GCode editors/viewers.
48
+ email:
49
+ - info@32leav.es
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - Gemfile
56
+ - LICENSE
57
+ - README.md
58
+ - Rakefile
59
+ - gcoder.gemspec
60
+ - lib/gcoder.rb
61
+ - lib/gcoder/dialects/default.rb
62
+ - lib/gcoder/gcode.rb
63
+ - lib/gcoder/parser.rb
64
+ - lib/gcoder/version.rb
65
+ - spec/default_dialect_spec.rb
66
+ - spec/gcode_program_spec.rb
67
+ - spec/parser_spec.rb
68
+ - spec/spec_helper.rb
69
+ homepage: ''
70
+ licenses:
71
+ - MIT
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 1.8.23
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: GCoder is a Ruby library to deal with GCode in various flavours.
94
+ test_files:
95
+ - spec/default_dialect_spec.rb
96
+ - spec/gcode_program_spec.rb
97
+ - spec/parser_spec.rb
98
+ - spec/spec_helper.rb