rintcore 0.0.1 → 0.0.2
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.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/CONTRIBUTING.md +10 -6
- data/README.md +24 -4
- data/bin/rintcore +5 -0
- data/lib/rint_core/cli.rb +87 -0
- data/lib/rint_core/driver.rb +22 -0
- data/lib/rint_core/driver/operating_system.rb +30 -0
- data/lib/rint_core/driver/operations.rb +209 -0
- data/lib/rint_core/driver/parsing.rb +39 -0
- data/lib/rint_core/driver/queueing.rb +73 -0
- data/lib/rint_core/driver/state.rb +71 -0
- data/lib/rint_core/g_code.rb +3 -2
- data/lib/rint_core/g_code/codes.rb +2 -2
- data/lib/rint_core/g_code/line.rb +83 -0
- data/lib/rint_core/g_code/object.rb +173 -0
- data/lib/rint_core/printer.rb +59 -0
- data/lib/rint_core/version.rb +1 -1
- data/rintcore.gemspec +3 -1
- metadata +53 -28
- data/lib/rint_core/printer_driver.rb +0 -312
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3a9b723838919efffb3f912ecefcbbbea54b9ab8
|
4
|
+
data.tar.gz: ce1a9e323a978ae0e7452c722aadb13f259e436b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9fa795c99cd6f8783942d2a1fe206f5c0b4994acd0447b3e67f05984a89e6eed2e7e8e31633b0bd5be667e3634b09859086b09c8225e5dbaccf0024a821b3576
|
7
|
+
data.tar.gz: dfd942aa41baaf1fe91ffaff69aa3731cee4c8ae88dd697d5eae5bb4dd32339bb274ecdb438879e7d121080081ed2d9da6d929d4dc3491c03ded7b0f6f98c6ec
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p0
|
data/CONTRIBUTING.md
CHANGED
@@ -16,15 +16,19 @@ you can share.
|
|
16
16
|
|
17
17
|
If you'd like to submit a pull request please adhere to the following:
|
18
18
|
|
19
|
-
1. Your code *must* be tested. Please TDD your code!
|
20
|
-
2.
|
21
|
-
3.
|
22
|
-
4.
|
19
|
+
1. Your code *must* be tested. Please TDD your code! (where ever possible)
|
20
|
+
2. do modifications on an appropriately named branch.
|
21
|
+
3. No single-character variables
|
22
|
+
4. Two-spaces instead of tabs
|
23
|
+
5. Single-quotes instead of double-quotes unless you are using string
|
23
24
|
interpolation or escapes.
|
24
|
-
|
25
|
+
6. General Rails/Ruby naming conventions for files and classes
|
26
|
+
7. Have your commit message prefixed with the class it impacts Eg.: [PrinterDriver] Made it more awesome.
|
27
|
+
8. A single commit can only impact one class. If your changes impact multiple classes, then
|
28
|
+
your pull request must have multiple commits.
|
25
29
|
|
26
30
|
Plase note that you must adhere to each of the above mentioned rules.
|
27
31
|
Failure to do so will result in an immediate closing of the pull
|
28
32
|
request. If you update and rebase the pull request to follow the
|
29
33
|
guidelines your pull request will be re-opened and considered for
|
30
|
-
inclusion.
|
34
|
+
inclusion.
|
data/README.md
CHANGED
@@ -1,10 +1,30 @@
|
|
1
|
-
#
|
1
|
+
# RintCore
|
2
|
+
|
3
|
+
[](https://codeclimate.com/github/KazW/RintCore)
|
2
4
|
|
3
5
|
A RepRap/GCode parsing and sending utility written in Ruby.
|
4
6
|
|
5
|
-
###
|
7
|
+
### Usage
|
8
|
+
Install: ```gem install rintcore```
|
9
|
+
|
10
|
+
Get stats for a GCode file: ```rintcore analyze my_print.gcode```
|
11
|
+
Print a GCode file: ```rintcore print my_print.gcode```
|
12
|
+
See more options: ```rintcore help```
|
13
|
+
|
14
|
+
Use it somewhere else:
|
15
|
+
```ruby
|
16
|
+
require 'rint_core/printer'
|
17
|
+
require 'rint_core/g_code/object'
|
18
|
+
gcode = RintCore::GCode::Object.new('my_print.gcode')
|
19
|
+
printer = RintCore::Printer.new
|
20
|
+
printer.port = '/dev/ttyUSB0' # Set to /dev/ttyACM0 by default
|
21
|
+
printer.baud = 250000 # Set to 115200 by default
|
22
|
+
printer.callbacks[:temperature] = Proc.new { |line| puts(line) }
|
23
|
+
printer.connect!
|
24
|
+
printer.start_print gcode```
|
6
25
|
|
7
|
-
|
26
|
+
### TODO
|
27
|
+
Documentation and spec writing.
|
8
28
|
|
9
29
|
### Contributing
|
10
30
|
See CONTRIBUTING.md
|
@@ -13,7 +33,7 @@ See CONTRIBUTING.md
|
|
13
33
|
|
14
34
|
Copyright (C) 2013 Kaz Walker
|
15
35
|
|
16
|
-
The
|
36
|
+
The Driver modules are based on printcore.py by Kliment Yanev and various contributors.
|
17
37
|
|
18
38
|
This program is free software: you can redistribute it and/or modify
|
19
39
|
it under the terms of the GNU General Public License as
|
data/bin/rintcore
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'rint_core/g_code/object'
|
2
|
+
require 'rint_core/printer'
|
3
|
+
require 'rint_core/g_code/codes'
|
4
|
+
require 'thor'
|
5
|
+
require 'active_support/core_ext/object/blank'
|
6
|
+
|
7
|
+
module RintCore
|
8
|
+
class Cli < Thor
|
9
|
+
map '-a' => :analyze, '-p' => :print
|
10
|
+
|
11
|
+
desc 'analyze FILE', 'Get statistics about the given GCode file.'
|
12
|
+
method_option :decimals, default: 2, aliases: '-d', type: :numeric, desc: 'The number of decimal places given for measurements.'
|
13
|
+
def analyze(file)
|
14
|
+
unless RintCore::GCode::Object.is_file?(file)
|
15
|
+
puts "Non-exsitant file: #{file}"
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
@object = RintCore::GCode::Object.new(RintCore::GCode::Object.get_file(file))
|
19
|
+
decimals = options[:decimals]
|
20
|
+
decimals ||= 2
|
21
|
+
output = <<EOS
|
22
|
+
Dimensions:
|
23
|
+
\tX: #{@object.x_min.round(decimals)} - #{@object.x_max.round(decimals)} (#{@object.width.round(decimals)}mm)
|
24
|
+
\tY: #{@object.y_min.round(decimals)} - #{@object.y_max.round(decimals)} (#{@object.depth.round(decimals)}mm)
|
25
|
+
\tZ: #{@object.z_min.round(decimals)} - #{@object.z_max.round(decimals)} (#{@object.height.round(decimals)}mm)
|
26
|
+
Total Travel:
|
27
|
+
\tX: #{@object.x_travel.round(decimals)}mm
|
28
|
+
\tY: #{@object.y_travel.round(decimals)}mm
|
29
|
+
\tZ: #{@object.z_travel.round(decimals)}mm
|
30
|
+
Filament used: #{@object.filament_used.round(decimals)}mm
|
31
|
+
Number of layers: #{@object.layers}
|
32
|
+
#{@object.raw_data.length} lines / #{@object.lines.length} commands
|
33
|
+
EOS
|
34
|
+
puts output
|
35
|
+
end
|
36
|
+
|
37
|
+
desc 'print FILE', 'Print the given GCode file.'
|
38
|
+
method_option :port, aliases: '-p', type: :string, desc: 'The port that the printer is connected to.'
|
39
|
+
method_option :baud, aliases: '-b', type: :numeric, desc: 'The baud rate at which the printer communicates at.'
|
40
|
+
method_option :loud, aliases: '-l', default: false, type: :boolean, desc: 'Output additional info (temperature, progress, etc.)'
|
41
|
+
def print(file)
|
42
|
+
analyze(file)
|
43
|
+
port = options[:port]
|
44
|
+
baud = options[:baud]
|
45
|
+
baud = baud.to_i unless baud.blank?
|
46
|
+
baud = nil unless RintCore::Printer.baud_rates.include?(baud)
|
47
|
+
port = nil unless RintCore::Printer.is_port?(port)
|
48
|
+
while port.blank?
|
49
|
+
puts "Please enter the port and press enter:"
|
50
|
+
port = $stdin.gets.strip
|
51
|
+
port = nil unless RintCore::Printer.is_port?(port)
|
52
|
+
end
|
53
|
+
while baud.blank?
|
54
|
+
puts "Please enter the baud rate and press enter:"
|
55
|
+
baud = $stdin.gets.strip
|
56
|
+
baud = baud.to_i unless baud.blank?
|
57
|
+
baud = nil unless RintCore::Printer.baud_rates.include?(baud)
|
58
|
+
end
|
59
|
+
printer = RintCore::Printer.new
|
60
|
+
printer.port = port
|
61
|
+
printer.baud = baud
|
62
|
+
printer.callbacks[:online] = Proc.new { puts "Printer online!" }
|
63
|
+
printer.callbacks[:start] = Proc.new { puts "Started printing!" }
|
64
|
+
printer.callbacks[:finish] = Proc.new { puts "Print took: "+printer.time_from_start }
|
65
|
+
printer.callbacks[:temperature] = Proc.new { |line| puts line }
|
66
|
+
if options[:loud]
|
67
|
+
printer.callbacks[:receive] = Proc.new { |line| puts "Got: "+line }
|
68
|
+
printer.callbacks[:send] = Proc.new { |line| puts "Sent: "+line }
|
69
|
+
end
|
70
|
+
printer.callbacks[:disconnect] = Proc.new { puts "Printer disconnected!" }
|
71
|
+
printer.connect!
|
72
|
+
until printer.online?
|
73
|
+
sleep(printer.long_sleep)
|
74
|
+
end
|
75
|
+
start_time = Time.now
|
76
|
+
printer.start_print(@object)
|
77
|
+
while printer.printing?
|
78
|
+
printer.send_now(RintCore::GCode::Codes::GET_EXT_TEMP)
|
79
|
+
sleep 4.20
|
80
|
+
puts "Printed "+((Float(printer.queue_index) / Float(printer.main_queue.length))*100).round(2).to_s+"% in "+printer.time_from_start
|
81
|
+
sleep 1
|
82
|
+
end
|
83
|
+
printer.disconnect!
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rint_core/driver/operating_system'
|
2
|
+
require 'rint_core/driver/state'
|
3
|
+
require 'rint_core/driver/parsing'
|
4
|
+
require 'rint_core/driver/queueing'
|
5
|
+
require 'rint_core/driver/operations'
|
6
|
+
|
7
|
+
module RintCore
|
8
|
+
module Driver
|
9
|
+
include RintCore::Driver::OperatingSystem
|
10
|
+
include RintCore::Driver::State
|
11
|
+
include RintCore::Driver::Parsing
|
12
|
+
include RintCore::Driver::Queueing
|
13
|
+
include RintCore::Driver::Operations
|
14
|
+
|
15
|
+
def initialize_driver
|
16
|
+
initialize_state
|
17
|
+
initialize_queueing
|
18
|
+
initialize_operations
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RintCore
|
2
|
+
module Driver
|
3
|
+
module OperatingSystem
|
4
|
+
def control_ttyhup(port, disable_hup)
|
5
|
+
if get_os == :linux
|
6
|
+
if disable_hup
|
7
|
+
`stty -F #{port} -hup`
|
8
|
+
else
|
9
|
+
`stty -F #{port} hup`
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def enable_hup(port)
|
15
|
+
control_ttyhup(port, true)
|
16
|
+
end
|
17
|
+
|
18
|
+
def disable_hup(port)
|
19
|
+
control_ttyhup(port, false)
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_os
|
23
|
+
return :linux if /linux/ =~ RUBY_PLATFORM
|
24
|
+
return :mac if /darwin/ =~ RUBY_PLATFORM
|
25
|
+
return :windows if /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
|
26
|
+
return :unknown
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'rint_core/g_code/codes'
|
2
|
+
require 'rint_core/g_code/object'
|
3
|
+
require 'serialport'
|
4
|
+
require 'active_support/core_ext/object/blank'
|
5
|
+
|
6
|
+
module RintCore
|
7
|
+
module Driver
|
8
|
+
module Operations
|
9
|
+
|
10
|
+
def connect!
|
11
|
+
return false if connected?
|
12
|
+
if config.port.present? && config.baud.present?
|
13
|
+
disable_hup(config.port)
|
14
|
+
@connection = SerialPort.new(config.port, config.baud)
|
15
|
+
@connection.read_timeout = config.read_timeout
|
16
|
+
@stop_listening = false
|
17
|
+
sleep(config.long_sleep)
|
18
|
+
@listening_thread = Thread.new{listen()}
|
19
|
+
config.callbacks[:connect].call if config.callbacks[:connect].present?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def disconnect!
|
24
|
+
if connected?
|
25
|
+
if @listening_thread
|
26
|
+
@stop_listening = true
|
27
|
+
send!(RintCore::GCode::Codes::GET_EXT_TEMP)
|
28
|
+
@listening_thread.join
|
29
|
+
@listening_thread = nil
|
30
|
+
end
|
31
|
+
@connection.close
|
32
|
+
end
|
33
|
+
@connection = nil
|
34
|
+
offline!
|
35
|
+
not_printing!
|
36
|
+
config.callbacks[:disconnect].call if config.callbacks[:disconnect].present?
|
37
|
+
end
|
38
|
+
|
39
|
+
def reset!
|
40
|
+
@connection.dtr = 0
|
41
|
+
sleep(config.long_sleep)
|
42
|
+
@connection.dtr = 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def pause!
|
46
|
+
return false unless printing?
|
47
|
+
@paused = true
|
48
|
+
not_printing!
|
49
|
+
@print_thread.join
|
50
|
+
@print_thread = nil
|
51
|
+
config.callbacks[:pause].call if config.callbacks[:pause].present?
|
52
|
+
end
|
53
|
+
|
54
|
+
def resume!
|
55
|
+
return false unless paused?
|
56
|
+
paused!
|
57
|
+
printing!
|
58
|
+
@print_thread = Thread.new{print!()}
|
59
|
+
config.callbacks[:resume].call if config.callbacks[:resume].present?
|
60
|
+
end
|
61
|
+
|
62
|
+
def send(command, wait = 0, priority = false)
|
63
|
+
if online?
|
64
|
+
if printing?
|
65
|
+
priority ? @priority_queue.push(command) : @main_queue.push(command)
|
66
|
+
else
|
67
|
+
until clear_to_send? do
|
68
|
+
sleep(config.sleep_time)
|
69
|
+
end
|
70
|
+
wait = config.wait_period if wait == 0 && config.wait_period > 0
|
71
|
+
not_clear_to_send!
|
72
|
+
send!(command)
|
73
|
+
while wait > 0 && !clear_to_send? do
|
74
|
+
sleep config.sleep_time
|
75
|
+
wait -= 1
|
76
|
+
end
|
77
|
+
end
|
78
|
+
else
|
79
|
+
# TODO: log something about not being connected to printer
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def send_now(command, wait = 0)
|
84
|
+
send(command, wait, true)
|
85
|
+
end
|
86
|
+
|
87
|
+
def start_print(data, start_index = 0)
|
88
|
+
return false unless can_print?
|
89
|
+
data = data.lines if data.class == RintCore::GCode::Object
|
90
|
+
printing!
|
91
|
+
@main_queue = [] + data
|
92
|
+
@line_number = 0
|
93
|
+
@queue_index = start_index
|
94
|
+
@resend_from = -1
|
95
|
+
send!(RintCore::GCode::Codes::SET_LINE_NUM, -1, true)
|
96
|
+
return true unless data.present?
|
97
|
+
@print_thread = Thread.new{print!()}
|
98
|
+
@start_time = Time.now
|
99
|
+
return true
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def initialize_operations
|
105
|
+
@connection = nil
|
106
|
+
@listening_thread = nil
|
107
|
+
@printing_thread = nil
|
108
|
+
@full_history = []
|
109
|
+
end
|
110
|
+
|
111
|
+
def readline!
|
112
|
+
begin
|
113
|
+
line = @connection.readline.strip
|
114
|
+
rescue EOFError, Errno::ENODEV => e
|
115
|
+
config.callbacks[:critcal_error].call(e) if config.callbacks[:critcal_error].present?
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def print!
|
120
|
+
@machine_history = []
|
121
|
+
config.callbacks[:start].call if config.callbacks[:start].present?
|
122
|
+
while online? && printing? do
|
123
|
+
advance_queue
|
124
|
+
end
|
125
|
+
@print_thread.join
|
126
|
+
@print_thread = nil
|
127
|
+
config.callbacks[:finish].call if config.callbacks[:finish].present?
|
128
|
+
return true
|
129
|
+
end
|
130
|
+
|
131
|
+
def listen
|
132
|
+
clear_to_send!
|
133
|
+
listen_until_online
|
134
|
+
while listen_can_continue? do
|
135
|
+
line = readline!
|
136
|
+
@last_line_received = line
|
137
|
+
case get_response_type(line)
|
138
|
+
when :valid
|
139
|
+
config.callbacks[:receive].call(line) if config.callbacks[:receive].present?
|
140
|
+
clear_to_send!
|
141
|
+
when :online
|
142
|
+
config.callbacks[:receive].call(line) if config.callbacks[:receive].present?
|
143
|
+
when :temperature
|
144
|
+
config.callbacks[:temperature].call(line) if config.callbacks[:temperature].present?
|
145
|
+
when :temperature_response
|
146
|
+
config.callbacks[:temperature].call(line) if config.callbacks[:temperature].present?
|
147
|
+
clear_to_send!
|
148
|
+
when :error
|
149
|
+
config.callbacks[:printer_error] if config.callbacks[:printer_error].present?
|
150
|
+
# TODO: Figure out if an error should be raised here or if it should be left to the callback
|
151
|
+
when :resend
|
152
|
+
@resend_from = get_resend_number(line)
|
153
|
+
config.callbacks[:resend] if config.callbacks[:resend].present?
|
154
|
+
clear_to_send!
|
155
|
+
when :debug
|
156
|
+
config.callbacks[:debug] if config.callbacks[:debug].present?
|
157
|
+
when :invalid
|
158
|
+
config.callbacks[:invalid_response] if config.callbacks[:invalid_response].present?
|
159
|
+
#break
|
160
|
+
end
|
161
|
+
# clear_to_send!
|
162
|
+
end
|
163
|
+
#clear_to_send!
|
164
|
+
end
|
165
|
+
|
166
|
+
def listen_until_online
|
167
|
+
begin
|
168
|
+
empty_lines = 0
|
169
|
+
accepted_reponses = [:online,:temperature,:valid]
|
170
|
+
while listen_can_continue? do
|
171
|
+
line = readline!
|
172
|
+
if line.present?
|
173
|
+
empty_lines = 0
|
174
|
+
else
|
175
|
+
empty_lines += 1
|
176
|
+
not_clear_to_send!
|
177
|
+
send!(RintCore::GCode::Codes::GET_EXT_TEMP)
|
178
|
+
end
|
179
|
+
break if empty_lines == 5
|
180
|
+
if accepted_reponses.include?(get_response_type(line))
|
181
|
+
config.callbacks[:online].call if config.callbacks[:online].present?
|
182
|
+
online!
|
183
|
+
return true
|
184
|
+
end
|
185
|
+
sleep(config.long_sleep)
|
186
|
+
end
|
187
|
+
raise "Printer could not be brought online."
|
188
|
+
rescue RuntimeError => e
|
189
|
+
config.callbacks[:critcal_error].call(e) if config.callbacks[:critcal_error].present?
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def send!(command, line_number = 0, calc_checksum = false)
|
194
|
+
if calc_checksum
|
195
|
+
command = prefix_command(command, line_number)
|
196
|
+
@machine_history[line_number] = command unless command.include?(RintCore::GCode::Codes::SET_LINE_NUM)
|
197
|
+
end
|
198
|
+
if connected?
|
199
|
+
config.callbacks[:send].call(command) if online? && config.callbacks[:send].present?
|
200
|
+
command = format_command(command)
|
201
|
+
@connection.write(command)
|
202
|
+
@full_history << command
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rint_core/g_code/codes'
|
2
|
+
require 'active_support/core_ext/object/blank'
|
3
|
+
|
4
|
+
module RintCore
|
5
|
+
module Driver
|
6
|
+
module Parsing
|
7
|
+
|
8
|
+
def format_command(command)
|
9
|
+
(command.strip + "\n").split(RintCore::GCode::Codes::COMMENT_SYMBOL).first.encode(config.encoding)
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_checksum(command)
|
13
|
+
command.bytes.inject{|a,b| a^b}.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
def prefix_command(command, line_number)
|
17
|
+
prefix = ('N' + line_number.to_s + ' ' + command.strip).encode(config.encoding)
|
18
|
+
prefix+'*'+get_checksum(prefix)
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_response_type(line)
|
22
|
+
return :invalid unless line.present? || line.class == String
|
23
|
+
return :error if line.include?(config.error_response)
|
24
|
+
return :debug if line.include?(config.debug_response)
|
25
|
+
return :online if line.start_with?(*config.online_response)
|
26
|
+
return :valid if line.start_with?(*config.good_response) && !line.include?(config.temperature_response)
|
27
|
+
return :temperature_response if line.start_with?(*config.good_response) && line.include?(config.temperature_response)
|
28
|
+
return :temperature if line.include?(config.temperature_response)
|
29
|
+
return :resend if line.start_with?(*config.resend_response)
|
30
|
+
return :invalid
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_resend_number(line)
|
34
|
+
line.sub('N:', '').sub('N', '').sub(':', '').strip.split.first.to_i
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|