-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgrblUtils.py
181 lines (145 loc) · 5.81 KB
/
grblUtils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import serialUtils
import re
import datetime
import os
import config
import logging
import grbl2image.grbl2image as G2I
from PIL import Image
from werkzeug.utils import secure_filename
from flask import flash, escape
REGEX_LIGHTBURN_BOUNDS=";\s+Bounds: X([0-9\.]+) Y([0-9\.]+) to X([0-9\.]+) Y([0-9\.]+)"
REGEX_DEVICE_STATUS = "<(?P<state>[^:|]+)(?::||).*"
#Try to extract the wellknown comment about boundaries to get the frame details.
#Ok it's ugly and risky but hey it's here so no need to redo what was done by professionals
def __getFrameFromComments (fileFullPath):
with open(fileFullPath, "r") as f:
for l in f:
m = re.search(REGEX_LIGHTBURN_BOUNDS, l.strip())
if None != m:
#got it !
grp = m.groups()
grp = [float(x) for x in grp]
return [[grp[0], grp[1]], [grp[2], grp[3]]]
return None
#Used for cancellation, to force the device to stop and terminate nicely
def sendOutroOnly (port:str):
logging.warn("Sending outro only")
outro = """; Outro, turn everything off nicely
G90
M9
G1 S0
M5
G90
; return to user-defined finish pos
G0 X0 Y0
M2
"""
try:
for l in outro.split("\n"):
l = l.strip()
if l == '':
continue
#If you send outro, you don't care about the busy status (most likely it IS busy and you cancel a job)
res = serialUtils.sendCommand(port, l, ignoreBusyStatus=True)
logging.debug(f"Outro line '{ l }' result: { res }")
except Exception as ex:
logging.error("Error at outro : " + str(ex))
#Attempts to frame tne file content (just more around where the action will be)
def generateFrame(port, fileFullPath, pauseAtCornersInSec:float = 0.0, framingSpeendInMMPerSec:int = 20):
fromto = __getFrameFromComments(fileFullPath)
logging.info (f"Extracted from file: { str(fromto) }")
#make a temp file
tempfile = os.path.join(config.myconfig['upload folder'],
"/tmp/frame_" + datetime.datetime.now().strftime("%Y%m%d%H%M%S") + ".nc")
try:
logging.info(f"Generating temporary framing file at { tempfile }")
with open(tempfile, "w+") as f:
f.write(f"""; Framing temporary file autogenerated
; { tempfile }
; Assuming creator was a chad and uses metric system
; Using minmax coordinates : { fromto }
;
G00 G17 G40 G21 G54
G90
M4
M8
; Go to origin, 1 percent laser, {framingSpeendInMMPerSec} mm /sec ({framingSpeendInMMPerSec * 60} mm/min)
G0 X0Y0S010F{framingSpeendInMMPerSec * 60}
; Now move to corner and trace
""")
f.write(f"G0 X{ fromto[0][0] }Y{ fromto[0][1] }\n")
if pauseAtCornersInSec > 0.0: f.write(f"G4 P{ pauseAtCornersInSec:0.1f}\n")
f.write(f"G1 X{ fromto[1][0] }\n")
if pauseAtCornersInSec > 0.0: f.write(f"G4 P{ pauseAtCornersInSec:0.1f}\n")
f.write(f"G1 Y{ fromto[1][1] }\n")
if pauseAtCornersInSec > 0.0: f.write(f"G4 P{ pauseAtCornersInSec:0.1f}\n")
f.write(f"G1 X{ fromto[0][0] }\n")
if pauseAtCornersInSec > 0.0: f.write(f"G4 P{ pauseAtCornersInSec:0.1f}\n")
f.write(f"G1 Y{ fromto[0][1] }\n")
if pauseAtCornersInSec > 0.0: f.write(f"G4 P{ pauseAtCornersInSec:0.1f}\n")
f.write("""
; Outro, turn everything off nicely
G90
M9
G1 S0
M5
G90
; return to user-defined finish pos
G0 X0 Y0
M2
""")
#debug
#with open(tempfile, "r") as f: print("".join(f.readlines()))
#not taking risks
serialUtils.simulateFile(port, tempfile)
finally:
try:
os.remove(tempfile)
except:
logging.error(f"Couldn't delete temp frame file { tempfile }")
#From a filename, returns the path to the thumbnail
def pathToThumbnail (filename:str):
dir_path = os.path.dirname(os.path.realpath(__file__))
return os.path.join(dir_path, "static", "thumbnails", os.path.basename(filename) + ".png")
#Creates an image PNG of the job stored in same folder, same name, with PNG suffix
def createThumbnailForJob(fileFullPath:str) -> G2I.JobStats:
#Generate the PIL Image object based on sample code
img, stats = G2I.processFile(fileFullPath, color="crimson")
#final flip because the image 0,0 is top left and for us human it's at the bottom left
img = img.transpose(Image.FLIP_TOP_BOTTOM)
#make sure it is saved in the subfolder of current file
thumbnail = pathToThumbnail(fileFullPath)
img.save(thumbnail)
#return the stats
return stats
#Delete thumbnail
def deleteThumbnailForJob(filename:str):
thumbnail = pathToThumbnail(filename)
os.remove(thumbnail)
#Returns the DEVICE status (Idle, Hold, ...)
def getDeviceStatus():
deviceStatus = serialUtils.serialStatusEnum()
if deviceStatus == serialUtils.ConnectionStatus.READY:
#device is not communicating but ready to receive order
res = serialUtils.sendCommand(config.myconfig["device port"], serialUtils.CMD_STATUS )
m = re.search(REGEX_DEVICE_STATUS, res)
if m != None:
return m.group("state")
else:
#Shouldn't happen but just in case
return "Unknown"
else:
return "Not Ready or Busy?"
############################ Thumbnail generation ###############################
#Makes a thumnail for a GRBL file
def genThumbnail(fileFullPath):
#try to make a thumbnail img
filename = secure_filename(os.path.basename(fileFullPath))
try:
stats = createThumbnailForJob(fileFullPath)
flash(f'Successfully created thumbnail for [{escape(filename)}]', "success")
return stats
except Exception as ex:
flash(f'Failed creating thumbnail for [{escape(filename)}] with message "{str(ex)}"', "error")
logging.error ("error on making thumbnail: " + str(ex))