Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
P
Printrun
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
machinery
Printrun
Commits
b4f620a0
Commit
b4f620a0
authored
Apr 08, 2013
by
kliment
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #352 from fsantini/experimental
Recovery after pause
parents
630d1178
025a5a7b
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
329 additions
and
7 deletions
+329
-7
GCodeAnalyzer.py
GCodeAnalyzer.py
+213
-0
printcore.py
printcore.py
+77
-3
pronterface.py
pronterface.py
+39
-4
No files found.
GCodeAnalyzer.py
0 → 100644
View file @
b4f620a0
# This file is part of the Printrun suite.
#
# Copyright 2013 Francesco Santini francesco.santini@gmail.com
#
# Printrun is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Printrun is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Printrun. If not, see <http://www.gnu.org/licenses/>.
#
# This code is imported from RepetierHost - Original copyright and license:
# Copyright 2011 repetier repetierdev@gmail.com
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import
re
class
GCodeAnalyzer
():
def
__init__
(
self
):
self
.
x
=
0
self
.
y
=
0
self
.
z
=
0
self
.
e
=
0
self
.
emax
=
0
self
.
f
=
1000
self
.
lastX
=
0
self
.
lastY
=
0
self
.
lastZ
=
0
self
.
lastE
=
0
self
.
xOffset
=
0
self
.
yOffset
=
0
self
.
zOffset
=
0
self
.
eOffset
=
0
self
.
lastZPrint
=
0
self
.
layerZ
=
0
self
.
relative
=
False
self
.
eRelative
=
False
self
.
homeX
=
0
self
.
homeY
=
0
self
.
homeZ
=
0
self
.
maxX
=
150
self
.
maxY
=
150
self
.
maxZ
=
150
self
.
minX
=
0
self
.
minY
=
0
self
.
minZ
=
0
self
.
hasHomeX
=
False
self
.
hasHomeY
=
False
self
.
hasHomeZ
=
False
# find a code in a gstring line
def
findCode
(
self
,
gcode
,
codeStr
):
pattern
=
re
.
compile
(
codeStr
+
"
\\
s*(-?[
\
d.]*)"
,
re
.
I
)
m
=
re
.
search
(
pattern
,
gcode
)
if
m
==
None
:
return
None
else
:
return
m
.
group
(
1
)
def
Analyze
(
self
,
gcode
):
gcode
=
gcode
[:
gcode
.
find
(
";"
)]
.
lstrip
()
# remove comments
if
gcode
.
startswith
(
"@"
):
return
# code is a host command
code_g
=
self
.
findCode
(
gcode
,
"G"
)
code_m
=
self
.
findCode
(
gcode
,
"M"
)
# we have a g_code
if
code_g
!=
None
:
code_g
=
int
(
code_g
)
#get movement codes
if
code_g
==
0
or
code_g
==
1
or
code_g
==
2
or
code_g
==
3
:
self
.
lastX
=
self
.
x
self
.
lastY
=
self
.
y
self
.
lastZ
=
self
.
z
self
.
lastE
=
self
.
e
eChanged
=
False
;
code_f
=
self
.
findCode
(
gcode
,
"F"
)
if
code_f
!=
None
:
self
.
f
=
float
(
code_f
)
code_x
=
self
.
findCode
(
gcode
,
"X"
)
code_y
=
self
.
findCode
(
gcode
,
"Y"
)
code_z
=
self
.
findCode
(
gcode
,
"Z"
)
code_e
=
self
.
findCode
(
gcode
,
"E"
)
if
self
.
relative
:
if
code_x
!=
None
:
self
.
x
+=
float
(
code_x
)
if
code_y
!=
None
:
self
.
y
+=
float
(
code_y
)
if
code_z
!=
None
:
self
.
z
+=
float
(
code_z
)
if
code_e
!=
None
:
e
=
float
(
code_e
)
if
e
!=
0
:
eChanged
=
True
self
.
e
+=
e
else
:
#absolute coordinates
if
code_x
!=
None
:
self
.
x
=
self
.
xOffset
+
float
(
code_x
)
if
code_y
!=
None
:
self
.
y
=
self
.
yOffset
+
float
(
code_y
)
if
code_z
!=
None
:
self
.
z
=
self
.
zOffset
+
float
(
code_z
)
if
code_e
!=
None
:
e
=
float
(
code_e
)
if
self
.
eRelative
:
if
e
!=
0
:
eChanged
=
True
self
.
e
+=
e
else
:
# e is absolute. Is it changed?
if
self
.
e
!=
self
.
eOffset
+
e
:
eChanged
=
True
self
.
e
=
self
.
eOffset
+
e
#limit checking
if
self
.
x
<
self
.
minX
:
self
.
x
=
self
.
minX
if
self
.
y
<
self
.
minY
:
self
.
y
=
self
.
minY
if
self
.
z
<
self
.
minZ
:
self
.
z
=
self
.
minZ
if
self
.
x
>
self
.
maxX
:
self
.
x
=
self
.
maxX
if
self
.
y
>
self
.
maxY
:
self
.
y
=
self
.
maxY
if
self
.
z
>
self
.
maxZ
:
self
.
z
=
self
.
maxZ
#Repetier has a bunch of limit-checking code here and time calculations: we are leaving them for now
elif
code_g
==
28
or
code_g
==
161
:
self
.
lastX
=
self
.
x
self
.
lastY
=
self
.
y
self
.
lastZ
=
self
.
z
self
.
lastE
=
self
.
e
code_x
=
self
.
findCode
(
gcode
,
"X"
)
code_y
=
self
.
findCode
(
gcode
,
"Y"
)
code_z
=
self
.
findCode
(
gcode
,
"Z"
)
code_e
=
self
.
findCode
(
gcode
,
"E"
)
homeAll
=
False
if
code_x
==
None
and
code_y
==
None
and
code_z
==
None
:
homeAll
=
True
if
code_x
!=
None
or
homeAll
:
self
.
hasHomeX
=
True
self
.
xOffset
=
0
self
.
x
=
self
.
homeX
if
code_y
!=
None
or
homeAll
:
self
.
hasHomeY
=
True
self
.
yOffset
=
0
self
.
y
=
self
.
homeY
if
code_z
!=
None
or
homeAll
:
self
.
hasHomeZ
=
True
self
.
zOffset
=
0
self
.
z
=
self
.
homeZ
if
code_e
!=
None
:
self
.
eOffset
=
0
self
.
e
=
0
elif
code_g
==
162
:
self
.
lastX
=
self
.
x
self
.
lastY
=
self
.
y
self
.
lastZ
=
self
.
z
self
.
lastE
=
self
.
e
code_x
=
self
.
findCode
(
gcode
,
"X"
)
code_y
=
self
.
findCode
(
gcode
,
"Y"
)
code_z
=
self
.
findCode
(
gcode
,
"Z"
)
homeAll
=
False
if
code_x
==
None
and
code_y
==
None
and
code_z
==
None
:
homeAll
=
True
if
code_x
!=
None
or
homeAll
:
self
.
hasHomeX
=
True
self
.
xOffset
=
0
self
.
x
=
self
.
maxX
if
code_y
!=
None
or
homeAll
:
self
.
hasHomeY
=
True
self
.
yOffset
=
0
self
.
y
=
self
.
maxY
if
code_z
!=
None
or
homeAll
:
self
.
hasHomeZ
=
True
self
.
zOffset
=
0
self
.
z
=
self
.
maxZ
elif
code_g
==
90
:
self
.
relative
=
False
elif
code_g
==
91
:
self
.
relative
=
True
elif
code_g
==
92
:
code_x
=
self
.
findCode
(
gcode
,
"X"
)
code_y
=
self
.
findCode
(
gcode
,
"Y"
)
code_z
=
self
.
findCode
(
gcode
,
"Z"
)
code_e
=
self
.
findCode
(
gcode
,
"E"
)
if
code_x
!=
None
:
self
.
xOffset
=
self
.
x
-
float
(
code_x
)
self
.
x
=
self
.
xOffset
if
code_y
!=
None
:
self
.
yOffset
=
self
.
y
-
float
(
code_y
)
self
.
y
=
self
.
yOffset
if
code_z
!=
None
:
self
.
zOffset
=
self
.
z
-
float
(
code_z
)
self
.
z
=
self
.
zOffset
if
code_e
!=
None
:
self
.
xOffset
=
self
.
e
-
float
(
code_e
)
self
.
e
=
self
.
eOffset
#End code_g != None
if
code_m
!=
None
:
code_m
=
int
(
code_m
)
if
code_m
==
82
:
self
.
eRelative
=
False
elif
code_m
==
83
:
self
.
eRelative
=
True
def
print_status
(
self
):
attrs
=
vars
(
self
)
print
'
\n
'
.
join
(
"
%
s:
%
s"
%
item
for
item
in
attrs
.
items
())
\ No newline at end of file
printcore.py
View file @
b4f620a0
...
...
@@ -20,6 +20,7 @@ from threading import Thread
from
select
import
error
as
SelectError
import
time
,
getopt
,
sys
import
platform
,
os
from
GCodeAnalyzer
import
GCodeAnalyzer
def
control_ttyhup
(
port
,
disable_hup
):
"""Controls the HUPCL"""
...
...
@@ -69,7 +70,11 @@ class printcore():
self
.
print_thread
=
None
if
port
is
not
None
and
baud
is
not
None
:
self
.
connect
(
port
,
baud
)
self
.
analyzer
=
GCodeAnalyzer
()
self
.
xy_feedrate
=
None
self
.
z_feedrate
=
None
self
.
pronterface
=
None
def
disconnect
(
self
):
"""Disconnects from printer and pauses the print
"""
...
...
@@ -219,19 +224,69 @@ class printcore():
self
.
print_thread
.
start
()
return
True
# run a simple script if it exists, no multithreading
def
runSmallScript
(
self
,
filename
):
if
filename
==
None
:
return
f
=
None
try
:
f
=
open
(
filename
)
except
:
pass
if
f
!=
None
:
for
i
in
f
:
l
=
i
.
replace
(
"
\n
"
,
""
)
l
=
l
[:
l
.
find
(
";"
)]
#remove comment
self
.
send_now
(
l
)
f
.
close
()
def
pause
(
self
):
"""Pauses the print, saving the current position.
"""
if
not
self
.
printing
:
return
False
self
.
paused
=
True
self
.
printing
=
False
self
.
print_thread
.
join
()
# try joining the print thread: enclose it in try/except because we might be calling it from the thread itself
try
:
self
.
print_thread
.
join
()
except
:
pass
self
.
print_thread
=
None
# saves the status
self
.
pauseX
=
self
.
analyzer
.
x
-
self
.
analyzer
.
xOffset
;
self
.
pauseY
=
self
.
analyzer
.
y
-
self
.
analyzer
.
yOffset
;
self
.
pauseZ
=
self
.
analyzer
.
z
-
self
.
analyzer
.
zOffset
;
self
.
pauseE
=
self
.
analyzer
.
e
-
self
.
analyzer
.
eOffset
;
self
.
pauseF
=
self
.
analyzer
.
f
;
self
.
pauseRelative
=
self
.
analyzer
.
relative
;
def
resume
(
self
):
"""Resumes a paused print.
"""
if
not
self
.
paused
:
return
False
if
self
.
paused
:
#restores the status
self
.
send_now
(
"G90"
)
# go to absolute coordinates
xyFeedString
=
""
zFeedString
=
""
if
self
.
xy_feedrate
!=
None
:
xyFeedString
=
" F"
+
str
(
self
.
xy_feedrate
)
if
self
.
z_feedrate
!=
None
:
zFeedString
=
" F"
+
str
(
self
.
z_feedrate
)
self
.
send_now
(
"G1 X"
+
str
(
self
.
pauseX
)
+
" Y"
+
str
(
self
.
pauseY
)
+
xyFeedString
)
self
.
send_now
(
"G1 Z"
+
str
(
self
.
pauseZ
)
+
zFeedString
)
self
.
send_now
(
"G92 E"
+
str
(
self
.
pauseE
))
if
self
.
pauseRelative
:
self
.
send_now
(
"G91"
)
# go back to relative if needed
#reset old feed rate
self
.
send_now
(
"G1 F"
+
str
(
self
.
pauseF
))
self
.
paused
=
False
self
.
printing
=
True
self
.
print_thread
=
Thread
(
target
=
self
.
_print
)
...
...
@@ -289,13 +344,24 @@ class printcore():
self
.
sentlines
=
{}
self
.
log
=
[]
self
.
sent
=
[]
self
.
print_thread
.
join
()
try
:
self
.
print_thread
.
join
()
except
:
pass
self
.
print_thread
=
None
if
self
.
endcb
:
#callback for printing done
try
:
self
.
endcb
()
except
:
pass
#now only "pause" is implemented as host command
def
processHostCommand
(
self
,
command
):
command
=
command
.
lstrip
()
if
command
.
startswith
(
";@pause"
):
if
self
.
pronterface
!=
None
:
self
.
pronterface
.
pause
(
None
)
else
:
self
.
pause
()
def
_sendnext
(
self
):
if
not
self
.
printer
:
return
...
...
@@ -316,6 +382,13 @@ class printcore():
return
if
self
.
printing
and
self
.
queueindex
<
len
(
self
.
mainqueue
):
tline
=
self
.
mainqueue
[
self
.
queueindex
]
#check for host command
if
tline
.
lstrip
()
.
startswith
(
";@"
):
#it is a host command: pop it from the list
self
.
mainqueue
.
pop
(
self
.
queueindex
)
self
.
processHostCommand
(
tline
)
return
tline
=
tline
.
split
(
";"
)[
0
]
if
len
(
tline
)
>
0
:
self
.
_send
(
tline
,
self
.
lineno
,
True
)
...
...
@@ -339,6 +412,7 @@ class printcore():
self
.
sentlines
[
lineno
]
=
command
if
self
.
printer
:
self
.
sent
.
append
(
command
)
self
.
analyzer
.
Analyze
(
command
)
# run the command through the analyzer
if
self
.
loud
:
print
"SENT: "
,
command
if
self
.
sendcb
:
...
...
pronterface.py
View file @
b4f620a0
...
...
@@ -85,7 +85,7 @@ class Tee(object):
class
PronterWindow
(
MainWindow
,
pronsole
.
pronsole
):
def
__init__
(
self
,
filename
=
None
,
size
=
winsize
):
pronsole
.
pronsole
.
__init__
(
self
)
self
.
settings
.
build_dimensions
=
'200x200x100+0+0+0'
#default build dimensions are 200x200x100 with 0, 0, 0 in the corner of the bed
self
.
settings
.
build_dimensions
=
'200x200x100+0+0+0
+0+0+0
'
#default build dimensions are 200x200x100 with 0, 0, 0 in the corner of the bed
self
.
settings
.
last_bed_temperature
=
0.0
self
.
settings
.
last_file_path
=
""
self
.
settings
.
last_temperature
=
0.0
...
...
@@ -93,7 +93,11 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self
.
settings
.
preview_grid_step1
=
10.
self
.
settings
.
preview_grid_step2
=
50.
self
.
settings
.
bgcolor
=
"#FFFFFF"
self
.
helpdict
[
"build_dimensions"
]
=
_
(
"Dimensions of Build Platform
\n
& optional offset of origin
\n\n
Examples:
\n
XXXxYYY
\n
XXX,YYY,ZZZ
\n
XXXxYYYxZZZ+OffX+OffY+OffZ"
)
self
.
pauseScript
=
"pause.gcode"
self
.
endScript
=
"end.gcode"
self
.
helpdict
[
"build_dimensions"
]
=
_
(
"Dimensions of Build Platform
\n
& optional offset of origin
\n
& optional switch position
\n\n
Examples:
\n
XXXxYYY
\n
XXX,YYY,ZZZ
\n
XXXxYYYxZZZ+OffX+OffY+OffZ
\n
XXXxYYYxZZZ+OffX+OffY+OffZ+HomeX+HomeY+HomeZ"
)
self
.
helpdict
[
"last_bed_temperature"
]
=
_
(
"Last Set Temperature for the Heated Print Bed"
)
self
.
helpdict
[
"last_file_path"
]
=
_
(
"Folder of last opened file"
)
self
.
helpdict
[
"last_temperature"
]
=
_
(
"Last Temperature of the Hot End"
)
...
...
@@ -128,6 +132,30 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self
.
btndict
=
{}
self
.
parse_cmdline
(
sys
.
argv
[
1
:])
self
.
build_dimensions_list
=
self
.
get_build_dimensions
(
self
.
settings
.
build_dimensions
)
#initialize the code analyzer with the correct sizes. There must be a more general way to do so
# minimum = offset
self
.
p
.
analyzer
.
minX
=
self
.
build_dimensions_list
[
3
]
self
.
p
.
analyzer
.
minY
=
self
.
build_dimensions_list
[
4
]
self
.
p
.
analyzer
.
minZ
=
self
.
build_dimensions_list
[
5
]
#max = offset + bedsize
self
.
p
.
analyzer
.
maxX
=
self
.
build_dimensions_list
[
3
]
+
self
.
build_dimensions_list
[
0
]
self
.
p
.
analyzer
.
maxY
=
self
.
build_dimensions_list
[
4
]
+
self
.
build_dimensions_list
[
1
]
self
.
p
.
analyzer
.
maxZ
=
self
.
build_dimensions_list
[
5
]
+
self
.
build_dimensions_list
[
2
]
self
.
p
.
analyzer
.
homeX
=
self
.
build_dimensions_list
[
6
]
self
.
p
.
analyzer
.
homeY
=
self
.
build_dimensions_list
[
7
]
self
.
p
.
analyzer
.
homeZ
=
self
.
build_dimensions_list
[
8
]
#set feedrates in printcore for pause/resume
self
.
p
.
xy_feedrate
=
self
.
settings
.
xy_feedrate
self
.
p
.
z_feedrate
=
self
.
settings
.
z_feedrate
#make printcore aware of me
self
.
p
.
pronterface
=
self
self
.
panel
.
SetBackgroundColour
(
self
.
settings
.
bgcolor
)
customdict
=
{}
try
:
...
...
@@ -198,6 +226,8 @@ class PronterWindow(MainWindow, pronsole.pronsole):
wx
.
CallAfter
(
self
.
pausebtn
.
Disable
)
wx
.
CallAfter
(
self
.
printbtn
.
SetLabel
,
_
(
"Print"
))
self
.
p
.
runSmallScript
(
self
.
endScript
)
param
=
self
.
settings
.
final_command
if
not
param
:
return
...
...
@@ -1388,7 +1418,9 @@ class PronterWindow(MainWindow, pronsole.pronsole):
#print "Not printing, cannot pause."
return
self
.
p
.
pause
()
self
.
p
.
runSmallScript
(
self
.
pauseScript
)
self
.
paused
=
True
#self.p.runSmallScript(self.pauseScript)
self
.
extra_print_time
+=
int
(
time
.
time
()
-
self
.
starttime
)
wx
.
CallAfter
(
self
.
pausebtn
.
SetLabel
,
_
(
"Resume"
))
else
:
...
...
@@ -1527,9 +1559,12 @@ class PronterWindow(MainWindow, pronsole.pronsole):
"[^
\
d+-]*(
\
d+)?"
+
# Z build size
"[^
\
d+-]*([+-]
\
d+)?"
+
# X corner coordinate
"[^
\
d+-]*([+-]
\
d+)?"
+
# Y corner coordinate
"[^
\
d+-]*([+-]
\
d+)?"
# Z corner coordinate
"[^
\
d+-]*([+-]
\
d+)?"
+
# Z corner coordinate
"[^
\
d+-]*([+-]
\
d+)?"
+
# X endstop
"[^
\
d+-]*([+-]
\
d+)?"
+
# Y endstop
"[^
\
d+-]*([+-]
\
d+)?"
# Z endstop
,
bdim
)
.
groups
()
defaults
=
[
200
,
200
,
100
,
0
,
0
,
0
]
defaults
=
[
200
,
200
,
100
,
0
,
0
,
0
,
0
,
0
,
0
]
bdl_float
=
[
float
(
value
)
if
value
else
defaults
[
i
]
for
i
,
value
in
enumerate
(
bdl
)]
return
bdl_float
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment