rintcore 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,81 +1,176 @@
1
1
  require 'rint_core/g_code/codes'
2
- require 'active_support/core_ext/object/blank'
3
2
 
4
3
  module RintCore
5
4
  module GCode
5
+ # Represents a single line in a GCode file, parse expression tester: {http://rubular.com/}
6
6
  class Line
7
7
  include RintCore::GCode::Codes
8
8
 
9
- attr_accessor :imperial, :relative, :f
10
- attr_reader :raw
11
- attr_writer :x, :y, :z, :e
12
-
9
+ # @!macro attr_accessor
10
+ # @!attribute [rw] $1
11
+ # @param speed_multiplier [Float] number speed (F) will be multiplied by.
12
+ # @return [nil] if the speed multiplier is not set.
13
+ # @return [Float] the speed multiplier (print moves only).
14
+ # @!attribute [rw] $2
15
+ # @param extrusion_multiplier [Float] number extrusions (E) will be multiplied by.
16
+ # @return [nil] if the extrusion multiplier is not set.
17
+ # @return [Float] the extrusion multiplier.
18
+ # @!attribute [rw] $3
19
+ # @param travel_multiplier [Float] number travel move speeds (F) will be multiplied by.
20
+ # @return [nil] if the travel multiplier is not set.
21
+ # @return [Float] the travel multiplier.
22
+ # @!attribute [rw] $4
23
+ # @param tool_number [Fixnum] the tool used in the command.
24
+ # @return [Fixnum] the tool used in the command.
25
+ # @!attribute [rw] $5
26
+ # @param f [Float] speed of the command (in mm/minute).
27
+ # @return [Float] the speed of the command (in mm/minute).
28
+ attr_accessor :speed_multiplier, :extrusion_multiplier,
29
+ :travel_multiplier, :tool_number, :f
30
+
31
+ # @!macro attr_reader
32
+ # @!attribute [r] $1
33
+ # @return [String] the line, upcased and stripped of whitespace.
34
+ # @!attribute [r] $2
35
+ # @return [nil] if the line wasn't valid GCode.
36
+ # @return [MatchData] the raw matches from the regular evaluation expression.
37
+ # @!attribute [r] $3
38
+ # @return [String] ready to send version of the original line (no multipliers, call {#to_s} to apply multipliers).
39
+ # @!attribute [r] $4
40
+ # @return [String] command portion of the line.
41
+ # @!attribute [r] $5
42
+ # @return [String] command letter of the line (G,M,T).
43
+ # @!attribute [r] $6
44
+ # @return [Fixnum] command number of the line (ex. 28 for "G28").
45
+ # @!attribute [r] $7
46
+ # @return [nil] if there's no S data for the command.
47
+ # @return [Fixnum] S data for the command.
48
+ # @!attribute [r] $8
49
+ # @return [nil] if there's no P data for the command.
50
+ # @return [Fixnum] P data for the command.
51
+ # @!attribute [r] $9
52
+ # @return [nil] if there's no X coordinate of the command.
53
+ # @return [Float] X coordinate of the command.
54
+ # @!attribute [r] $10
55
+ # @return [nil] if there's no Y coordinate of the command.
56
+ # @return [Float] Y coordinate of the command.
57
+ # @!attribute [r] $11
58
+ # @return [nil] if there's no Z coordinate of the command.
59
+ # @return [Float] Z coordinate of the command.
60
+ # @!attribute [r] $12
61
+ # @return [nil] if there's no E parameter of the command.
62
+ # @return [Float] E parameter of the command.
63
+ attr_reader :raw, :matches, :line, :command, :command_letter, :command_number,
64
+ :s_data, :p_data, :x, :y, :z, :e
65
+
66
+ # Creates a {Line}
67
+ # @param line [String] a line of GCode.
68
+ # @return [false] if line is empty or doesn't match the evaluation expression.
69
+ # @return [Line]
13
70
  def initialize(line)
14
- @coordinates = ['X','Y','Z','E','F']
15
- @number_pattern = /[-]?\d+[.]?\d*/
16
- @raw = line.upcase.strip
17
- @raw = @raw.split(COMMENT_SYMBOL).first.strip if line.include?(COMMENT_SYMBOL)
18
-
19
- parse_coordinates
71
+ return false if line.nil? || line.empty?
72
+ @raw = line
73
+ @gcode_pattern = /^(?<line>(?<command>((?<command_letter>[G|M|T])(?<command_number>\d{1,3}))) ?(?<regular_data>([S](?<s_data>\d*))? ?([P](?<p_data>\d*))? ?([X](?<x_data>[-]?\d+\.?\d*))? ?([Y](?<y_data>[-]?\d+\.?\d*))? ?([Z](?<z_data>[-]?\d+\.?\d*))? ?([F](?<f_data>\d+\.?\d*))? ?([E](?<e_data>[-]?\d+\.?\d*))?)? (?<string_data>[^;]*)?)? ?;?(?<comment>.*)$/
74
+ @matches = @raw.match(@gcode_pattern)
75
+ return false if @matches.nil?
76
+ assign_values unless @matches.nil?
20
77
  end
21
78
 
22
- def to_mm(number)
23
- number *= 25.4 if number.present? && @imperial
24
- number
79
+ # Checks if the given line is more than just a comment.
80
+ # @return [Boolean] true if empty/invalid
81
+ def empty?
82
+ @command.nil?
25
83
  end
26
84
 
27
- def x
28
- to_mm @x
85
+ # Checks if the command in the line causes movement.
86
+ # @return [Boolean] true if command moves printer, false otherwise.
87
+ def is_move?
88
+ @command == RAPID_MOVE || @command == CONTROLLED_MOVE
29
89
  end
30
90
 
31
- def y
32
- to_mm @y
91
+ # Checks whether the line is a travel move or not.
92
+ # @return [Boolean] true if line is a travel move, false otherwise.
93
+ def travel_move?
94
+ is_move? && @e.nil?
33
95
  end
34
96
 
35
- def z
36
- to_mm @z
97
+ # Checks whether the line is as extrusion move or not.
98
+ # @return [Boolean] true if line is an extrusion move, false otherwise.
99
+ def extrusion_move?
100
+ is_move? && !@e.nil? && @e > 0
37
101
  end
38
102
 
39
- def e
40
- to_mm @e
103
+ # Checks wether the line is a full home or not.
104
+ # @return [Boolean] true if line is full home, false otherwise.
105
+ def full_home?
106
+ @command == HOME && !@x.nil? && !@y.nil? && !@z.nil?
41
107
  end
42
108
 
43
- def command
44
- if @raw.present?
45
- @raw.split(' ').first
46
- else
47
- ''
48
- end
49
- end
109
+ # Returns the line, modified if multipliers are set.
110
+ # @return [String] the line.
111
+ def to_s
112
+ return @line if @extrusion_multiplier.nil? && @speed_multiplier.nil?
113
+
114
+ new_f = multiplied_speed unless is_move?
115
+ new_e = multiplied_extrusion unless @e.nil?
116
+
117
+ x_string = !@x.nil? ? " X#{@x}" : ''
118
+ y_string = !@y.nil? ? " Y#{@y}" : ''
119
+ z_string = !@z.nil? ? " Z#{@z}" : ''
120
+ e_string = !@e.nil? ? " E#{new_e}" : ''
121
+ f_string = is_move? ? " F#{new_f}" : ''
122
+ string = !@string_data.nil? ? " #{@string_data}" : ''
50
123
 
51
- def get_float(axis)
52
- @raw.split(axis).last.scan(@number_pattern).first.to_f
124
+ "#{@command}#{x_string}#{y_string}#{z_string}#{f_string}#{e_string}#{string}"
53
125
  end
54
126
 
55
- def parse_coordinates
56
- @coordinates.each do |axis|
57
- send(axis.downcase+'=', get_float(axis)) if @raw.include?(axis)
58
- end
127
+ private
128
+
129
+ def assign_values
130
+ command_assignments
131
+ coordinate_assignments
132
+ @s = @matches[:s_data].to_i unless @matches[:s_data].nil?
133
+ @p = @matches[:p_data].to_i unless @matches[:p_data].nil?
134
+ @string_data = @matches[:string_data]
135
+ @comment = @matches[:comment].strip unless @matches[:comment].nil?
59
136
  end
60
137
 
61
- def is_move?
62
- @raw.start_with?(RAPID_MOVE) || @raw.start_with?(CONTROLLED_MOVE)
138
+ def command_assignments
139
+ @command = @matches[:command]
140
+ @line = @matches[:line] unless @matches[:line].nil?
141
+ @command_letter = @matches[:command_letter]
142
+ @command_number = @matches[:command_number].to_i unless @matches[:command_number].nil?
143
+ @tool_number = @command_number if !@matches[:command_letter].nil? && @matches[:command_letter] == 'T'
63
144
  end
64
145
 
65
- def travel_move?
66
- is_move? && !@e.present?
146
+ def coordinate_assignments
147
+ @x = @matches[:x_data].to_f unless @matches[:x_data].nil?
148
+ @y = @matches[:y_data].to_f unless @matches[:y_data].nil?
149
+ @z = @matches[:z_data].to_f unless @matches[:z_data].nil?
150
+ @f = @matches[:f_data].to_f unless @matches[:f_data].nil?
151
+ @e = @matches[:e_data].to_f unless @matches[:e_data].nil?
67
152
  end
68
153
 
69
- def extrusion_move?
70
- is_move? && @e.present? && @e > 0
154
+ def multiplied_extrusion
155
+ if valid_multiplier?(@extrusion_multiplier)
156
+ return @e * @extrusion_multiplier
157
+ else
158
+ @e
159
+ end
71
160
  end
72
161
 
73
- def full_home?
74
- command == HOME && @x.blank? && @y.blank? && @z.blank?
162
+ def multiplied_speed
163
+ if travel_move? && valid_multiplier?(@travel_multiplier)
164
+ return @f * @travel_multiplier
165
+ elsif extrusion_move? && valid_multiplier?(@speed_multiplier)
166
+ return @f * @speed_multiplier
167
+ else
168
+ return @f
169
+ end
75
170
  end
76
171
 
77
- def to_s
78
- @raw
172
+ def valid_multiplier?(multiplier)
173
+ !multiplier.nil? && (multiplier.class == Fixnum || multiplier.class == Float) && multiplier > 0
79
174
  end
80
175
 
81
176
  end
@@ -1,45 +1,121 @@
1
1
  require 'rint_core/g_code/codes'
2
2
  require 'rint_core/g_code/line'
3
- require 'active_support/core_ext/object/blank'
4
3
 
5
4
  module RintCore
6
5
  module GCode
6
+ # A class that represents a processed GCode file.
7
7
  class Object
8
8
  include RintCore::GCode::Codes
9
9
 
10
- attr_accessor :raw_data, :layers
10
+ # An array of the raw Gcode with each line as an element.
11
+ # @return [Array] of raw GCode without the comments stripped out.
12
+ attr_accessor :raw_data
13
+ # @!macro attr_reader
14
+ # @!attribute [r] $1
15
+ # @return [Array<Line>] an array of {Line}s.
16
+ # @!attribute [r] $2
17
+ # @return [Float] the smallest X coordinate of an extrusion line.
18
+ # @!attribute [r] $3
19
+ # @return [Float] the biggest X coordinate of an extrusion line.
20
+ # @!attribute [r] $4
21
+ # @return [Float] the smallest Y coordinate of an extrusion line.
22
+ # @!attribute [r] $5
23
+ # @return [Float] the biggest Y coordinate of an extrusion line.
24
+ # @!attribute [r] $6
25
+ # @return [Float] the smallest Z coordinate.
26
+ # @!attribute [r] $7
27
+ # @return [Float] the biggest Z coordinate.
28
+ # @!attribute [r] $8
29
+ # @return [Array<Float>] the amount in mm of fliament extruded with the index representing the extruder.
30
+ # @!attribute [r] $9
31
+ # @return [Float] the distance in total that the X axis will travel in mm.
32
+ # @!attribute [r] $10
33
+ # @return [Float] the distance in total that the Y axis will travel in mm.
34
+ # @!attribute [r] $11
35
+ # @return [Float] the distance in total that the Z axis will travel in mm.
36
+ # @!attribute [r] $12
37
+ # @return [Float] the distance in total that the E axis will travel in mm.
38
+ # @todo implement this
39
+ # @!attribute [r] $13
40
+ # @return [Float] the width of the print.
41
+ # @!attribute [r] $14
42
+ # @return [Float] the depth of the print.
43
+ # @!attribute [r] $15
44
+ # @return [Float] the height of the print.
45
+ # @!attribute [r] $16
46
+ # @return [Fixnum] the number of layers in the print.
11
47
  attr_reader :lines, :x_min, :x_max, :y_min, :y_max, :z_min, :z_max,
12
48
  :filament_used, :x_travel, :y_travel, :z_travel, :e_travel,
13
- :width, :depth, :height
49
+ :width, :depth, :height, :layers
14
50
 
15
- def initialize(data = nil)
51
+ # Creates a GCode {Object}.
52
+ # @param data [String] path to a GCode file on the system.
53
+ # @param data [Array] with each element being a line of GCode.
54
+ # @param default_speed [Float] the default speed (in mm/minute) for moves that don't have one declared.
55
+ # @param auto_process [Boolean] enable/disable auto processing.
56
+ # @return [Object] if data is valid, returns a GCode {Object}.
57
+ # @return [false] if data is not an array, path, didn't contain GCode or default_speed wasn't a number grater than 0.
58
+ def initialize(data = nil, default_speed = 2400, auto_process = true)
59
+ return false if default_speed.nil? && (default_speed.class == Fixnum || default_speed.class == Float) && default_speed <= 0
16
60
  if data.class == String && self.class.is_file?(data)
17
61
  data = self.class.get_file(data)
18
62
  end
19
- return false if data.blank? || data.class != Array
20
- @raw_data = data
21
- @imperial = false
22
- @relative = false
23
- @lines = []
63
+ return false if data.nil? || data.class != Array
64
+ set_variables(data, default_speed)
24
65
  data.each do |line|
25
66
  line = RintCore::GCode::Line.new(line)
26
- @lines << line if line.raw.present?
67
+ @lines << set_line_properties(line) if line && !line.command.nil?
27
68
  end
28
- process
69
+ process if auto_process
70
+ return false if empty?
29
71
  end
30
72
 
73
+ # Checks if the given string is a file and if it exists.
74
+ # @param file [String] path to a file on the system.
75
+ # @return [Boolean] true if is a file that exists on the system, false otherwise.
31
76
  def self.is_file?(file)
32
- file.present? && File.exist?(file) && File.file?(file)
77
+ !file.nil? && !file.empty? && File.exist?(file) && File.file?(file)
33
78
  end
34
79
 
80
+ # Returns an array of the lines of the file if it exists.
81
+ # @param file [String] path to a file on the system.
82
+ # @return [Array] containting the lines of the given file as elements.
83
+ # @return [false] if given string isn't a file or doesn't exist.
35
84
  def self.get_file(file)
85
+ return false unless self.is_file?(file)
36
86
  IO.readlines(file)
37
87
  end
38
88
 
89
+ # Checks if there are any {Line}s in {#lines}.
90
+ # @return [Boolean] true if no lines, false otherwise.
91
+ def blank?
92
+ empty?
93
+ end
94
+
95
+ # Checks if there are any {Line}s in {#lines}.
96
+ # @return [Boolean] true if no lines, false otherwise.
97
+ def empty?
98
+ @lines.empty?
99
+ end
100
+
101
+ # Opposite of {#empty?}.
102
+ # @see #empty?
103
+ def present?
104
+ !empty?
105
+ end
106
+
107
+ # Checks if the GCode object contains multiple materials.
108
+ # @return [nil] if processing hasn't been done.
109
+ # @return [Boolean] true if multiple extruders used, false otherwise.
110
+ def multi_material?
111
+ return nil unless @width
112
+ @filament_used.length > 1
113
+ end
114
+
39
115
  private
40
116
 
41
117
  def process
42
- set_variables
118
+ set_processing_variables
43
119
 
44
120
  @lines.each do |line|
45
121
  case line.command
@@ -63,94 +139,105 @@ private
63
139
  end
64
140
  end
65
141
 
142
+ set_dimensions
143
+ end
144
+
145
+ def set_dimensions
66
146
  @width = @x_max - @x_min
67
147
  @depth = @y_max - @y_min
68
148
  @height = @z_max - @z_min
69
149
  end
70
150
 
71
151
  def count_layers(line)
72
- if line.z.present? && line.z > @current_z
152
+ if !line.z.nil? && line.z > @current_z
73
153
  @layers += 1
74
154
  end
75
155
  end
76
156
 
77
157
  def movement_line(line)
78
- line.imperial = @imperial
79
- line.relative = @relative
80
158
  measure_travel(line)
81
159
  set_current_position(line)
82
160
  set_limits(line)
83
161
  end
84
162
 
85
163
  def measure_travel(line)
86
- if line.relative
87
- @x_travel += line.x.abs if line.x.present?
88
- @y_travel += line.y.abs if line.y.present?
89
- @z_travel += line.z.abs if line.z.present?
164
+ if @relative
165
+ @x_travel += to_mm(line.x).abs unless line.x.nil?
166
+ @y_travel += to_mm(line.y).abs unless line.y.nil?
167
+ @z_travel += to_mm(line.z).abs unless line.z.nil?
90
168
  else
91
- @x_travel += (@current_x - line.x).abs if line.x.present?
92
- @y_travel += (@current_y - line.y).abs if line.y.present?
93
- @z_travel += (@current_z - line.z).abs if line.z.present?
169
+ @x_travel += (@current_x - to_mm(line.x)).abs unless line.x.nil?
170
+ @y_travel += (@current_y - to_mm(line.y)).abs unless line.y.nil?
171
+ @z_travel += (@current_z - to_mm(line.z)).abs unless line.z.nil?
94
172
  end
95
173
  end
96
174
 
97
175
  def home_axes(line)
98
- if line.x.present? || line.full_home?
176
+ if !line.x.nil? || line.full_home?
99
177
  @x_travel += @current_x
100
178
  @current_x = 0
101
179
  end
102
- if line.y.present? || line.full_home?
180
+ if !line.y.nil? || line.full_home?
103
181
  @y_travel += @current_y
104
182
  @current_y = 0
105
183
  end
106
- if line.z.present? || line.full_home?
184
+ if !line.z.nil? || line.full_home?
107
185
  @z_travel += @current_z
108
186
  @current_z = 0
109
187
  end
110
188
  end
111
189
 
112
190
  def set_positions(line)
113
- @current_x = line.x if line.x.present?
114
- @current_y = line.y if line.y.present?
115
- @current_z = line.z if line.z.present?
116
- if line.e.present?
117
- @filament_used += @current_e
118
- @current_e = line.e
191
+ @current_x = to_mm(line.x) unless line.x.nil?
192
+ @current_y = to_mm(line.y) unless line.y.nil?
193
+ @current_z = to_mm(line.z) unless line.z.nil?
194
+ unless line.e.nil?
195
+ @filament_used[line.tool_number] = 0 if @filament_used[line.tool_number].nil?
196
+ @filament_used[line.tool_number] += @current_e
197
+ @current_e = to_mm(line.e)
119
198
  end
120
199
  end
121
200
 
122
201
  def set_current_position(line)
123
- if line.relative
124
- @current_x += line.x if line.x.present?
125
- @current_y += line.y if line.y.present?
126
- @current_z += line.z if line.z.present?
127
- @current_e += line.e if line.e.present?
202
+ if @relative
203
+ @current_x += to_mm(line.x) unless line.x.nil?
204
+ @current_y += to_mm(line.y) unless line.y.nil?
205
+ @current_z += to_mm(line.z) unless line.z.nil?
206
+ @current_e += to_mm(line.e) unless line.e.nil?
128
207
  else
129
- @current_x = line.x if line.x.present?
130
- @current_y = line.y if line.y.present?
131
- @current_z = line.z if line.z.present?
132
- @current_e = line.e if line.e.present?
208
+ @current_x = to_mm(line.x) unless line.x.nil?
209
+ @current_y = to_mm(line.y) unless to_mm(line.y).nil?
210
+ @current_z = to_mm(line.z) unless line.z.nil?
211
+ @current_e = to_mm(line.e) unless line.e.nil?
133
212
  end
134
213
  end
135
214
 
136
215
  def set_limits(line)
137
216
  if line.extrusion_move?
138
- if line.x.present? && !line.x.zero?
217
+ unless line.x.nil?
139
218
  @x_min = @current_x if @current_x < @x_min
140
219
  @x_max = @current_x if @current_x > @x_max
141
220
  end
142
- if line.y.present? && !line.y.zero?
221
+ unless line.y.nil?
143
222
  @y_min = @current_y if @current_y < @y_min
144
223
  @y_max = @current_y if @current_y > @y_max
145
224
  end
146
225
  end
147
- if line.z.present?
226
+ unless line.z.nil?
148
227
  @z_min = @current_z if @current_z < @z_min
149
228
  @z_max = @current_z if @current_z > @z_max
150
229
  end
151
230
  end
152
231
 
153
- def set_variables
232
+ def set_line_properties(line)
233
+ @tool_number = line.tool_number unless line.tool_number.nil?
234
+ line.tool_number = @tool_number if line.tool_number.nil?
235
+ @speed = line.f unless line.f.nil?
236
+ line.f = @speed if line.f.nil?
237
+ line
238
+ end
239
+
240
+ def set_processing_variables
154
241
  @x_travel = 0
155
242
  @y_travel = 0
156
243
  @z_travel = 0
@@ -164,10 +251,24 @@ private
164
251
  @x_max = -999999999
165
252
  @y_max = -999999999
166
253
  @z_max = -999999999
167
- @filament_used = 0
254
+ @filament_used = []
168
255
  @layers = 0
169
256
  end
170
257
 
258
+ def set_variables(data, default_speed)
259
+ @raw_data = data
260
+ @imperial = false
261
+ @relative = false
262
+ @tool_number = 0
263
+ @speed = default_speed.to_f
264
+ @lines = []
265
+ end
266
+
267
+ def to_mm(number)
268
+ return number unless @imperial
269
+ number *= 25.4 if !number.nil?
270
+ end
271
+
171
272
  end
172
273
  end
173
274
  end