meerk40t 0.9.2000__py2.py3-none-any.whl → 0.9.3001__py2.py3-none-any.whl

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.
Files changed (187) hide show
  1. meerk40t/balormk/balor_params.py +1 -43
  2. meerk40t/balormk/controller.py +1 -41
  3. meerk40t/balormk/device.py +16 -22
  4. meerk40t/balormk/driver.py +4 -4
  5. meerk40t/balormk/gui/balorconfig.py +2 -2
  6. meerk40t/balormk/gui/balorcontroller.py +13 -5
  7. meerk40t/balormk/gui/baloroperationproperties.py +0 -46
  8. meerk40t/balormk/gui/gui.py +17 -17
  9. meerk40t/camera/gui/camerapanel.py +18 -11
  10. meerk40t/core/cutcode/rastercut.py +3 -1
  11. meerk40t/core/cutplan.py +145 -14
  12. meerk40t/core/elements/clipboard.py +18 -9
  13. meerk40t/core/elements/element_treeops.py +320 -180
  14. meerk40t/core/elements/element_types.py +7 -2
  15. meerk40t/core/elements/elements.py +53 -27
  16. meerk40t/core/elements/geometry.py +8 -0
  17. meerk40t/core/elements/offset_clpr.py +129 -4
  18. meerk40t/core/elements/offset_mk.py +3 -1
  19. meerk40t/core/elements/shapes.py +28 -25
  20. meerk40t/core/laserjob.py +7 -0
  21. meerk40t/core/node/bootstrap.py +4 -0
  22. meerk40t/core/node/effect_hatch.py +85 -96
  23. meerk40t/core/node/effect_wobble.py +309 -0
  24. meerk40t/core/node/elem_image.py +49 -19
  25. meerk40t/core/node/elem_line.py +60 -0
  26. meerk40t/core/node/elem_rect.py +5 -3
  27. meerk40t/core/node/image_processed.py +766 -0
  28. meerk40t/core/node/image_raster.py +113 -0
  29. meerk40t/core/node/node.py +120 -1
  30. meerk40t/core/node/op_cut.py +2 -8
  31. meerk40t/core/node/op_dots.py +0 -8
  32. meerk40t/core/node/op_engrave.py +2 -18
  33. meerk40t/core/node/op_image.py +22 -35
  34. meerk40t/core/node/op_raster.py +0 -9
  35. meerk40t/core/planner.py +32 -2
  36. meerk40t/core/svg_io.py +699 -461
  37. meerk40t/core/treeop.py +191 -0
  38. meerk40t/core/undos.py +15 -1
  39. meerk40t/core/units.py +14 -4
  40. meerk40t/device/dummydevice.py +3 -2
  41. meerk40t/device/gui/defaultactions.py +43 -55
  42. meerk40t/device/gui/formatterpanel.py +58 -49
  43. meerk40t/device/gui/warningpanel.py +12 -12
  44. meerk40t/device/mixins.py +13 -0
  45. meerk40t/dxf/dxf_io.py +9 -5
  46. meerk40t/extra/ezd.py +28 -26
  47. meerk40t/extra/imageactions.py +300 -308
  48. meerk40t/extra/lbrn.py +19 -2
  49. meerk40t/fill/fills.py +6 -6
  50. meerk40t/fill/patternfill.py +1061 -1061
  51. meerk40t/fill/patterns.py +2 -6
  52. meerk40t/grbl/controller.py +168 -52
  53. meerk40t/grbl/device.py +23 -18
  54. meerk40t/grbl/driver.py +39 -0
  55. meerk40t/grbl/emulator.py +79 -19
  56. meerk40t/grbl/gcodejob.py +10 -0
  57. meerk40t/grbl/gui/grblconfiguration.py +2 -2
  58. meerk40t/grbl/gui/grblcontroller.py +24 -8
  59. meerk40t/grbl/gui/grblhardwareconfig.py +153 -0
  60. meerk40t/grbl/gui/gui.py +17 -14
  61. meerk40t/grbl/mock_connection.py +15 -34
  62. meerk40t/grbl/plugin.py +0 -4
  63. meerk40t/grbl/serial_connection.py +2 -1
  64. meerk40t/gui/about.py +8 -5
  65. meerk40t/gui/alignment.py +10 -6
  66. meerk40t/gui/basicops.py +27 -17
  67. meerk40t/gui/bufferview.py +2 -2
  68. meerk40t/gui/choicepropertypanel.py +101 -13
  69. meerk40t/gui/consolepanel.py +12 -9
  70. meerk40t/gui/devicepanel.py +38 -25
  71. meerk40t/gui/executejob.py +6 -4
  72. meerk40t/gui/help_assets/help_assets.py +13 -10
  73. meerk40t/gui/hersheymanager.py +8 -6
  74. meerk40t/gui/icons.py +1951 -3065
  75. meerk40t/gui/imagesplitter.py +14 -7
  76. meerk40t/gui/keymap.py +3 -3
  77. meerk40t/gui/laserpanel.py +151 -84
  78. meerk40t/gui/laserrender.py +61 -70
  79. meerk40t/gui/lasertoolpanel.py +8 -7
  80. meerk40t/gui/materialtest.py +3 -3
  81. meerk40t/gui/mkdebug.py +254 -1
  82. meerk40t/gui/navigationpanels.py +321 -180
  83. meerk40t/gui/notes.py +3 -3
  84. meerk40t/gui/opassignment.py +12 -12
  85. meerk40t/gui/operation_info.py +13 -13
  86. meerk40t/gui/plugin.py +5 -0
  87. meerk40t/gui/position.py +20 -18
  88. meerk40t/gui/preferences.py +21 -6
  89. meerk40t/gui/propertypanels/attributes.py +70 -22
  90. meerk40t/gui/propertypanels/blobproperty.py +2 -2
  91. meerk40t/gui/propertypanels/consoleproperty.py +2 -2
  92. meerk40t/gui/propertypanels/groupproperties.py +3 -3
  93. meerk40t/gui/propertypanels/hatchproperty.py +11 -18
  94. meerk40t/gui/propertypanels/imageproperty.py +4 -3
  95. meerk40t/gui/propertypanels/opbranchproperties.py +1 -1
  96. meerk40t/gui/propertypanels/pathproperty.py +2 -2
  97. meerk40t/gui/propertypanels/pointproperty.py +2 -2
  98. meerk40t/gui/propertypanels/propertywindow.py +4 -4
  99. meerk40t/gui/propertypanels/textproperty.py +3 -3
  100. meerk40t/gui/propertypanels/wobbleproperty.py +204 -0
  101. meerk40t/gui/ribbon.py +367 -259
  102. meerk40t/gui/scene/scene.py +31 -5
  103. meerk40t/gui/scenewidgets/elementswidget.py +12 -4
  104. meerk40t/gui/scenewidgets/gridwidget.py +2 -2
  105. meerk40t/gui/scenewidgets/laserpathwidget.py +7 -2
  106. meerk40t/gui/scenewidgets/machineoriginwidget.py +6 -2
  107. meerk40t/gui/scenewidgets/relocatewidget.py +1 -1
  108. meerk40t/gui/scenewidgets/reticlewidget.py +9 -0
  109. meerk40t/gui/scenewidgets/selectionwidget.py +12 -7
  110. meerk40t/gui/simpleui.py +95 -8
  111. meerk40t/gui/simulation.py +44 -36
  112. meerk40t/gui/spoolerpanel.py +124 -26
  113. meerk40t/gui/statusbarwidgets/defaultoperations.py +18 -6
  114. meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
  115. meerk40t/gui/statusbarwidgets/opassignwidget.py +12 -12
  116. meerk40t/gui/statusbarwidgets/shapepropwidget.py +45 -18
  117. meerk40t/gui/statusbarwidgets/statusbar.py +11 -4
  118. meerk40t/gui/themes.py +78 -0
  119. meerk40t/gui/toolwidgets/toolcircle.py +2 -1
  120. meerk40t/gui/toolwidgets/toolellipse.py +2 -1
  121. meerk40t/gui/toolwidgets/toolimagecut.py +132 -0
  122. meerk40t/gui/toolwidgets/toolline.py +144 -0
  123. meerk40t/gui/toolwidgets/toolnodeedit.py +72 -145
  124. meerk40t/gui/toolwidgets/toolpoint.py +1 -1
  125. meerk40t/gui/toolwidgets/toolpolygon.py +8 -55
  126. meerk40t/gui/toolwidgets/toolrect.py +2 -1
  127. meerk40t/gui/usbconnect.py +2 -2
  128. meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +2 -2
  129. meerk40t/gui/utilitywidgets/harmonograph.py +7 -7
  130. meerk40t/gui/utilitywidgets/scalewidget.py +1 -1
  131. meerk40t/gui/wordlisteditor.py +33 -18
  132. meerk40t/gui/wxmeerk40t.py +166 -66
  133. meerk40t/gui/wxmmain.py +236 -157
  134. meerk40t/gui/wxmribbon.py +49 -25
  135. meerk40t/gui/wxmscene.py +49 -38
  136. meerk40t/gui/wxmtree.py +216 -85
  137. meerk40t/gui/wxutils.py +62 -4
  138. meerk40t/image/imagetools.py +443 -15
  139. meerk40t/internal_plugins.py +2 -10
  140. meerk40t/kernel/kernel.py +12 -4
  141. meerk40t/lihuiyu/controller.py +7 -7
  142. meerk40t/lihuiyu/device.py +3 -1
  143. meerk40t/lihuiyu/driver.py +3 -0
  144. meerk40t/lihuiyu/gui/gui.py +8 -8
  145. meerk40t/lihuiyu/gui/lhyaccelgui.py +2 -2
  146. meerk40t/lihuiyu/gui/lhycontrollergui.py +73 -27
  147. meerk40t/lihuiyu/gui/lhydrivergui.py +2 -2
  148. meerk40t/lihuiyu/gui/tcpcontroller.py +22 -9
  149. meerk40t/main.py +6 -1
  150. meerk40t/moshi/controller.py +5 -5
  151. meerk40t/moshi/device.py +5 -2
  152. meerk40t/moshi/driver.py +4 -0
  153. meerk40t/moshi/gui/gui.py +8 -8
  154. meerk40t/moshi/gui/moshicontrollergui.py +24 -8
  155. meerk40t/moshi/gui/moshidrivergui.py +2 -2
  156. meerk40t/newly/controller.py +2 -0
  157. meerk40t/newly/device.py +9 -2
  158. meerk40t/newly/driver.py +4 -0
  159. meerk40t/newly/gui/gui.py +16 -17
  160. meerk40t/newly/gui/newlyconfig.py +2 -2
  161. meerk40t/newly/gui/newlycontroller.py +13 -5
  162. meerk40t/rotary/gui/gui.py +2 -2
  163. meerk40t/rotary/gui/rotarysettings.py +2 -2
  164. meerk40t/ruida/device.py +3 -0
  165. meerk40t/ruida/driver.py +4 -0
  166. meerk40t/ruida/gui/gui.py +6 -6
  167. meerk40t/ruida/gui/ruidaconfig.py +2 -2
  168. meerk40t/ruida/gui/ruidacontroller.py +13 -5
  169. meerk40t/svgelements.py +9 -9
  170. meerk40t/tools/geomstr.py +849 -153
  171. meerk40t/tools/kerftest.py +8 -4
  172. meerk40t/tools/livinghinges.py +15 -8
  173. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/METADATA +21 -16
  174. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/RECORD +185 -177
  175. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/entry_points.txt +0 -1
  176. test/test_core_elements.py +8 -24
  177. test/test_file_svg.py +88 -0
  178. test/test_fill.py +9 -9
  179. test/test_geomstr.py +258 -8
  180. test/test_kernel.py +4 -0
  181. test/test_tools_rasterplotter.py +29 -0
  182. meerk40t/extra/embroider.py +0 -56
  183. meerk40t/extra/pathoptimize.py +0 -249
  184. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/LICENSE +0 -0
  185. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/WHEEL +0 -0
  186. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/top_level.txt +0 -0
  187. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/zip-safe +0 -0
meerk40t/fill/patterns.py CHANGED
@@ -154,12 +154,8 @@ class LivingHinges:
154
154
  if outer_path is None:
155
155
  return
156
156
  self.path = Geomstr()
157
- clip = Geomstr()
158
- for sp in outer_path.as_subpaths():
159
- pts = list(sp.as_interpolated_points(interpolate=100))
160
- # pts = [Path(sp).point(i / 100.0, error=1e4) for i in range(101)]
161
- clip.polyline(pts)
162
- # clip.end()
157
+
158
+ clip = outer_path
163
159
 
164
160
  q = Clip(clip)
165
161
  subject = Geomstr()
@@ -2,6 +2,14 @@
2
2
  GRBL Controller
3
3
 
4
4
  Tasked with sending data to the different connection.
5
+
6
+ Validation Stages.
7
+ Stage 0, we are disconnected and invalid.
8
+ Stage 1, we are connected and need to check if we are GRBL send $
9
+ Stage 2, we parsed $ and need to try $$ $G
10
+ Stage 3, we successfully parsed $$
11
+ Stage 4, we successfully parsed $G, send ?
12
+ Stage 5, we successfully parsed ?
5
13
  """
6
14
  import re
7
15
  import threading
@@ -12,6 +20,83 @@ from meerk40t.kernel import signal_listener
12
20
  SETTINGS_MESSAGE = re.compile(r"^\$([0-9]+)=(.*)")
13
21
 
14
22
 
23
+ def hardware_settings(code):
24
+ """
25
+ Given a $# code returns the parameter and the units.
26
+
27
+ @param code: $$ code.
28
+ @return: parameter, units
29
+ """
30
+ if code == 0:
31
+ return 10, "step pulse time", "microseconds"
32
+ if code == 1:
33
+ return 25, "step idle delay", "milliseconds"
34
+ if code == 2:
35
+ return 0, "step pulse invert", "bitmask"
36
+ if code == 3:
37
+ return 0, "step direction invert", "bitmask"
38
+ if code == 4:
39
+ return 0, "invert step enable pin", "boolean"
40
+ if code == 5:
41
+ return 0, "invert limit pins", "boolean"
42
+ if code == 6:
43
+ return 0, "invert probe pin", "boolean"
44
+ if code == 10:
45
+ return 255, "status report options", "bitmask"
46
+ if code == 11:
47
+ return 0.010, "Junction deviation", "mm"
48
+ if code == 12:
49
+ return 0.002, "arc tolerance", "mm"
50
+ if code == 13:
51
+ return 0, "Report in inches", "boolean"
52
+ if code == 20:
53
+ return 0, "Soft limits enabled", "boolean"
54
+ if code == 21:
55
+ return 0, "hard limits enabled", "boolean"
56
+ if code == 22:
57
+ return 0, "Homing cycle enable", "boolean"
58
+ if code == 23:
59
+ return 0, "Homing direction invert", "bitmask"
60
+ if code == 24:
61
+ return 25.000, "Homing locate feed rate", "mm/min"
62
+ if code == 25:
63
+ return 500.000, "Homing search seek rate", "mm/min"
64
+ if code == 26:
65
+ return 250, "Homing switch debounce delay", "ms"
66
+ if code == 27:
67
+ return 1.000, "Homing switch pull-off distance", "mm"
68
+ if code == 30:
69
+ return 1000, "Maximum spindle speed", "RPM"
70
+ if code == 31:
71
+ return 0, "Minimum spindle speed", "RPM"
72
+ if code == 32:
73
+ return 1, "Laser mode enable", "boolean"
74
+ if code == 100:
75
+ return 250.000, "X-axis steps per millimeter", "steps"
76
+ if code == 101:
77
+ return 250.000, "Y-axis steps per millimeter", "steps"
78
+ if code == 102:
79
+ return 250.000, "Z-axis steps per millimeter", "steps"
80
+ if code == 110:
81
+ return 500.000, "X-axis max rate", "mm/min"
82
+ if code == 111:
83
+ return 500.000, "Y-axis max rate", "mm/min"
84
+ if code == 112:
85
+ return 500.000, "Z-axis max rate", "mm/min"
86
+ if code == 120:
87
+ return 10.000, "X-axis acceleration", "mm/s^2"
88
+ if code == 121:
89
+ return 10.000, "Y-axis acceleration", "mm/s^2"
90
+ if code == 122:
91
+ return 10.000, "Z-axis acceleration", "mm/s^2"
92
+ if code == 130:
93
+ return 200.000, "X-axis max travel", "mm"
94
+ if code == 131:
95
+ return 200.000, "Y-axis max travel", "mm"
96
+ if code == 132:
97
+ return 200.000, "Z-axis max travel", "mm"
98
+
99
+
15
100
  def grbl_error_code(code):
16
101
  long = ""
17
102
  short = f"Error #{code}"
@@ -156,53 +241,14 @@ class GrblController:
156
241
  def __init__(self, context):
157
242
  self.service = context
158
243
  self.connection = None
244
+ self._validation_stage = 0
159
245
 
160
246
  self.update_connection()
161
247
 
162
248
  self.driver = self.service.driver
163
- self.grbl_settings = {
164
- 0: 10, # step pulse microseconds
165
- 1: 25, # step idle delay
166
- 2: 0, # step pulse invert
167
- 3: 0, # step direction invert
168
- 4: 0, # invert step enable pin, boolean
169
- 5: 0, # invert limit pins, boolean
170
- 6: 0, # invert probe pin
171
- 10: 255, # status report options
172
- 11: 0.010, # Junction deviation, mm
173
- 12: 0.002, # arc tolerance, mm
174
- 13: 0, # Report in inches
175
- 20: 0, # Soft limits enabled.
176
- 21: 0, # hard limits enabled
177
- 22: 0, # Homing cycle enable
178
- 23: 0, # Homing direction invert
179
- 24: 25.000, # Homing locate feed rate, mm/min
180
- 25: 500.000, # Homing search seek rate, mm/min
181
- 26: 250, # Homing switch debounce delay, ms
182
- 27: 1.000, # Homing switch pull-off distance, mm
183
- 30: 1000, # Maximum spindle speed, RPM
184
- 31: 0, # Minimum spindle speed, RPM
185
- 32: 1, # Laser mode enable, boolean
186
- 100: 250.000, # X-axis steps per millimeter
187
- 101: 250.000, # Y-axis steps per millimeter
188
- 102: 250.000, # Z-axis steps per millimeter
189
- 110: 500.000, # X-axis max rate mm/min
190
- 111: 500.000, # Y-axis max rate mm/min
191
- 112: 500.000, # Z-axis max rate mm/min
192
- 120: 10.000, # X-axis acceleration, mm/s^2
193
- 121: 10.000, # Y-axis acceleration, mm/s^2
194
- 122: 10.000, # Z-axis acceleration, mm/s^2
195
- 130: 200.000, # X-axis max travel mm.
196
- 131: 200.000, # Y-axis max travel mm
197
- 132: 200.000, # Z-axis max travel mm.
198
- }
199
249
 
200
250
  # Welcome message into, indicates the device is initialized.
201
251
  self.welcome = self.service.setting(str, "welcome", "Grbl")
202
- self._requires_validation = self.service.setting(
203
- bool, "requires_validation", True
204
- )
205
- self._connection_validated = not self._requires_validation
206
252
 
207
253
  # Sending variables.
208
254
  self._sending_thread = None
@@ -321,6 +367,8 @@ class GrblController:
321
367
  self.log("Could not connect.", type="event")
322
368
  return
323
369
  self.log("Connecting to GRBL...", type="event")
370
+ self._validation_stage = 1
371
+ self.realtime("$\r\n")
324
372
 
325
373
  def close(self):
326
374
  """
@@ -331,8 +379,8 @@ class GrblController:
331
379
  if not self.connection.connected:
332
380
  return
333
381
  self.connection.disconnect()
334
- self._connection_validated = not self._requires_validation
335
382
  self.log("Disconnecting from GRBL...", type="event")
383
+ self._validation_stage = 0
336
384
 
337
385
  def write(self, data):
338
386
  """
@@ -386,6 +434,7 @@ class GrblController:
386
434
  self.add_watcher(self._channel_log)
387
435
 
388
436
  if self._sending_thread is None:
437
+ self._sending_thread = True # Avoid race condition.
389
438
  self._sending_thread = self.service.threaded(
390
439
  self._sending,
391
440
  thread_name=f"sender-{self.service.location()}",
@@ -393,6 +442,7 @@ class GrblController:
393
442
  daemon=True,
394
443
  )
395
444
  if self._recving_thread is None:
445
+ self._recving_thread = True # Avoid race condition.
396
446
  self._recving_thread = self.service.threaded(
397
447
  self._recving,
398
448
  thread_name=f"recver-{self.service.location()}",
@@ -454,6 +504,7 @@ class GrblController:
454
504
  if line is not None:
455
505
  self._send(line)
456
506
  if "\x18" in line:
507
+ self._paused = False
457
508
  with self._forward_lock:
458
509
  self._forward_buffer.clear()
459
510
 
@@ -476,9 +527,7 @@ class GrblController:
476
527
  @return:
477
528
  """
478
529
  with self._loop_cond:
479
- self.service.signal("pipe;running", False)
480
530
  self._loop_cond.wait()
481
- self.service.signal("pipe;running", True)
482
531
 
483
532
  def _send_resume(self):
484
533
  """
@@ -497,21 +546,24 @@ class GrblController:
497
546
  @return:
498
547
 
499
548
  """
500
- self.service.signal("pipe;running", True)
501
549
  while self.connection.connected:
502
550
  if self._realtime_queue:
503
551
  # Send realtime data.
504
552
  self._sending_realtime()
505
553
  continue
506
- if self._paused or not self._connection_validated:
507
- # We are paused. We do not send anything other than realtime commands.
554
+ if self._paused or not self.fully_validated():
555
+ # We are paused or invalid. We do not send anything other than realtime commands.
508
556
  time.sleep(0.05)
509
557
  continue
510
558
  if not self._sending_queue:
511
559
  # There is nothing to write/realtime
560
+ self.service.laser_status = "idle"
512
561
  self._send_halt()
513
562
  continue
514
563
  buffer = len(self._forward_buffer)
564
+ if buffer:
565
+ self.service.laser_status = "active"
566
+
515
567
  if self.service.buffer_mode == "sync":
516
568
  if buffer:
517
569
  # Any buffer is too much buffer. Halt.
@@ -525,7 +577,7 @@ class GrblController:
525
577
  continue
526
578
  # Go for send_line
527
579
  self._sending_single_line()
528
- self.service.signal("pipe;running", False)
580
+ self.service.laser_status = "idle"
529
581
 
530
582
  ####################
531
583
  # GRBL RECV ROUTINES
@@ -616,6 +668,9 @@ class GrblController:
616
668
  self._process_feedback_message(response)
617
669
  continue
618
670
  elif response.startswith("$"):
671
+ if self._validation_stage == 2:
672
+ self.log("Stage 3: $$ was successfully parsed.", type="event")
673
+ self._validation_stage = 3
619
674
  self._process_settings_message(response)
620
675
  continue
621
676
  elif response.startswith("ALARM"):
@@ -631,11 +686,15 @@ class GrblController:
631
686
  elif response.startswith(">"):
632
687
  self.log(f"STARTUP: {response}", type="event")
633
688
  elif response.startswith(self.welcome):
634
- self.log("Connection Confirmed.", type="event")
635
- self._connection_validated = True
689
+ self.log("Device Reset, revalidation required", type="event")
690
+ if self.fully_validated():
691
+ self._validation_stage = 1
692
+ self.realtime("$\r\n")
636
693
  else:
637
694
  self._assembled_response.append(response)
638
- self.service.signal("pipe;running", False)
695
+
696
+ def fully_validated(self):
697
+ return self._validation_stage == 5
639
698
 
640
699
  def _process_status_message(self, response):
641
700
  message = response[1:-1]
@@ -655,6 +714,39 @@ class GrblController:
655
714
  f, s = info.split(",")
656
715
  self.service.signal("grbl:speed", float(f))
657
716
  self.service.signal("grbl:power", float(s))
717
+ elif name == "MPos":
718
+ coords = info.split(",")
719
+ try:
720
+ nx = float(coords[0])
721
+ ny = float(coords[1])
722
+
723
+ if not self.fully_validated():
724
+ # During validation, we declare positions.
725
+ self.driver.declare_position(nx, ny)
726
+ ox = self.driver.mpos_x
727
+ oy = self.driver.mpos_y
728
+
729
+ x, y = self.service.view_mm.position(f"{nx}mm", f"{ny}mm")
730
+
731
+ (
732
+ self.driver.mpos_x,
733
+ self.driver.mpos_y,
734
+ ) = self.service.view_mm.scene_position(f"{x}mm", f"{y}mm")
735
+
736
+ if len(coords) >= 3:
737
+ self.driver.mpos_z = float(coords[2])
738
+ self.service.signal(
739
+ "status;position",
740
+ (ox, oy, self.driver.mpos_x, self.driver.mpos_y),
741
+ )
742
+ except ValueError:
743
+ pass
744
+ elif name == "WPos":
745
+ coords = info.split(",")
746
+ self.driver.wpos_x = coords[0]
747
+ self.driver.wpos_y = coords[1]
748
+ if len(coords) >= 3:
749
+ self.driver.wpos_z = coords[2]
658
750
  # See: https://github.com/grbl/grbl/blob/master/grbl/report.c#L421
659
751
  # MPos: Coord values. Machine Position.
660
752
  # WPos: MPos but with applied work coordinates. Work Position.
@@ -664,6 +756,9 @@ class GrblController:
664
756
  # Lim: limits states
665
757
  # Ctl: control pins and mask (binary).
666
758
  self.service.signal(f"grbl:status:{name}", info)
759
+ if self._validation_stage in (2, 3, 4):
760
+ self.log("Connection Confirmed.", type="event")
761
+ self._validation_stage = 5
667
762
 
668
763
  def _process_feedback_message(self, response):
669
764
  if response.startswith("[MSG:"):
@@ -671,11 +766,29 @@ class GrblController:
671
766
  self.log(message, type="event")
672
767
  self.service.channel("console")(message)
673
768
  elif response.startswith("[GC:"):
769
+ # Parsing $G
674
770
  message = response[4:-1]
771
+ states = list(message.split(" "))
772
+ if not self.fully_validated():
773
+ self.log("Stage 4: $G was successfully parsed.", type="event")
774
+ self.driver.declare_modals(states)
775
+ self._validation_stage = 4
776
+ self.realtime("?\r\n")
675
777
  self.log(message, type="event")
676
- self.service.signal("grbl:states", list(message.split(" ")))
778
+ self.service.signal("grbl:states", states)
677
779
  elif response.startswith("[HLP:"):
780
+ # Parsing $
678
781
  message = response[5:-1]
782
+ if self._validation_stage == 1:
783
+ self.log("Stage 2: $ was successfully parsed.", type="event")
784
+ self._validation_stage = 2
785
+ if "$$" in message:
786
+ self.realtime("$$\r\n")
787
+ if "$G" in message:
788
+ self.realtime("$G\r\n")
789
+ elif "?" in message:
790
+ # No $G just request status.
791
+ self.realtime("?\r\n")
679
792
  self.log(message, type="event")
680
793
  elif response.startswith("[G54:"):
681
794
  message = response[5:-1]
@@ -782,6 +895,9 @@ class GrblController:
782
895
  match = SETTINGS_MESSAGE.match(response)
783
896
  if match:
784
897
  try:
785
- self.grbl_settings[int(match.group(1))] = float(match.group(2))
898
+ key = int(match.group(1))
899
+ value = float(match.group(2))
900
+ self.service.hardware_config[key] = value
901
+ self.service.signal(f"grbl:hwsettings", key, value)
786
902
  except ValueError:
787
903
  pass
meerk40t/grbl/device.py CHANGED
@@ -10,22 +10,25 @@ from meerk40t.kernel import CommandSyntaxError, Service
10
10
 
11
11
  from ..core.laserjob import LaserJob
12
12
  from ..core.spoolers import Spooler
13
- from ..core.units import UNITS_PER_MIL, Length
13
+ from ..core.units import MM_PER_INCH, Length
14
14
  from ..core.view import View
15
+ from ..device.mixins import Status
15
16
  from .controller import GrblController
16
17
  from .driver import GRBLDriver
17
18
 
18
19
 
19
- class GRBLDevice(Service):
20
+ class GRBLDevice(Service, Status):
20
21
  """
21
22
  GRBLDevice is driver for the Gcode Controllers
22
23
  """
23
24
 
24
25
  def __init__(self, kernel, path, *args, choices=None, **kwargs):
26
+ self.hardware_config = {}
25
27
  self.permit_tcp = True
26
28
  self.permit_serial = True
27
29
 
28
30
  Service.__init__(self, kernel, path)
31
+ Status.__init__(self)
29
32
  self.name = "GRBLDevice"
30
33
  self.extension = "gcode"
31
34
  if choices is not None:
@@ -232,6 +235,12 @@ class GRBLDevice(Service):
232
235
  dpi_x=1000.0,
233
236
  dpi_y=1000.0,
234
237
  )
238
+ self.view_mm = View(
239
+ self.bedwidth,
240
+ self.bedheight,
241
+ dpi_x=MM_PER_INCH,
242
+ dpi_y=MM_PER_INCH,
243
+ )
235
244
  self.realize()
236
245
  self.settings = dict()
237
246
  self.state = 0
@@ -451,17 +460,6 @@ class GRBLDevice(Service):
451
460
  "conditional": (self, "use_red_dot"),
452
461
  "section": "_10_Red Dot",
453
462
  },
454
- {
455
- "attr": "requires_validation",
456
- "object": self,
457
- "default": True,
458
- "type": bool,
459
- "label": _("Require validation for device"),
460
- "tip": _(
461
- "Ensure device is completely initialized before sending data. This is usually known to be valid at the 'Grbl xx.x' version message."
462
- ),
463
- "section": "_40_Validation",
464
- },
465
463
  {
466
464
  "attr": "welcome",
467
465
  "object": self,
@@ -471,7 +469,6 @@ class GRBLDevice(Service):
471
469
  "tip": _(
472
470
  "If for some reason the device needs a different welcome validator than 'Grbl' (default), for example, somewhat custom grbl-like firmware"
473
471
  ),
474
- "conditional": (self, "requires_validation"),
475
472
  "section": "_40_Validation",
476
473
  },
477
474
  ]
@@ -523,7 +520,7 @@ class GRBLDevice(Service):
523
520
  )
524
521
  def soft_reset(command, channel, _, data=None, remainder=None, **kwgs):
525
522
  self.driver.reset()
526
- self.signal("pipe;running", False)
523
+ self.laser_status = "idle"
527
524
 
528
525
  @self.console_command(
529
526
  "estop",
@@ -532,8 +529,7 @@ class GRBLDevice(Service):
532
529
  )
533
530
  def estop(command, channel, _, data=None, remainder=None, **kwgs):
534
531
  self.driver.reset()
535
- self.signal("pipe;running", False)
536
- self.signal("pause")
532
+ self.laser_status = "idle"
537
533
 
538
534
  @self.console_command(
539
535
  "clear_alarm",
@@ -542,7 +538,7 @@ class GRBLDevice(Service):
542
538
  )
543
539
  def clear_alarm(command, channel, _, data=None, remainder=None, **kwgs):
544
540
  self.driver.clear_alarm()
545
- self.signal("pipe;running", False)
541
+ self.laser_status = "idle"
546
542
 
547
543
  @self.console_command(
548
544
  "pause",
@@ -755,6 +751,15 @@ class GRBLDevice(Service):
755
751
  flip_y=self.flip_y,
756
752
  swap_xy=self.swap_xy,
757
753
  )
754
+ self.view_mm.set_dims(self.bedwidth, self.bedheight)
755
+ self.view_mm.transform(
756
+ user_scale_x=self.scale_x,
757
+ user_scale_y=self.scale_y,
758
+ flip_x=self.flip_x,
759
+ flip_y=self.flip_y,
760
+ swap_xy=self.swap_xy,
761
+ )
762
+
758
763
  # rotary_active=self.rotary_active,
759
764
  # rotary_scale_x=self.rotary_scale_x,
760
765
  # rotary_scale_y=self.rotary_scale_y,
meerk40t/grbl/driver.py CHANGED
@@ -34,6 +34,15 @@ class GRBLDriver(Parameters):
34
34
  self.paused = False
35
35
  self.native_x = 0
36
36
  self.native_y = 0
37
+
38
+ self.mpos_x = 0
39
+ self.mpos_y = 0
40
+ self.mpos_z = 0
41
+
42
+ self.wpos_x = 0
43
+ self.wpos_y = 0
44
+ self.wpos_z = 0
45
+
37
46
  self.stepper_step_size = UNITS_PER_MIL
38
47
 
39
48
  self.plot_planner = PlotPlanner(
@@ -254,6 +263,8 @@ class GRBLDriver(Parameters):
254
263
  self(f"M4{self.line_end}")
255
264
  for q in self.queue:
256
265
  while self.hold_work(0):
266
+ if self.service.kernel.is_shutdown:
267
+ return
257
268
  time.sleep(0.05)
258
269
  x = self.native_x
259
270
  y = self.native_y
@@ -517,6 +528,7 @@ class GRBLDriver(Parameters):
517
528
  """
518
529
  self.paused = True
519
530
  self(f"!{self.line_end}", real=True)
531
+ self.service.signal("pause")
520
532
 
521
533
  def resume(self, *args):
522
534
  """
@@ -529,6 +541,7 @@ class GRBLDriver(Parameters):
529
541
  """
530
542
  self.paused = False
531
543
  self(f"~{self.line_end}", real=True)
544
+ self.service.signal("pause")
532
545
 
533
546
  def clear_states(self):
534
547
  self.power_dirty = True
@@ -562,6 +575,7 @@ class GRBLDriver(Parameters):
562
575
  self.units_dirty = True
563
576
 
564
577
  self.paused = False
578
+ self.service.signal("pause")
565
579
 
566
580
  def clear_alarm(self):
567
581
  """
@@ -571,6 +585,31 @@ class GRBLDriver(Parameters):
571
585
  """
572
586
  self(f"$X{self.line_end}", real=True)
573
587
 
588
+ def declare_modals(self, modals):
589
+ self.move_mode = 0 if "G0" in modals else 1
590
+ if "G90" in modals:
591
+ self._g90_absolute()
592
+ self.absolute_dirty = False
593
+ if "G91" in modals:
594
+ self._g91_relative()
595
+ self.absolute_dirty = False
596
+ if "G94" in modals:
597
+ self._g94_feedrate()
598
+ self.feedrate_dirty = False
599
+ if "G93" in modals:
600
+ self._g93_feedrate()
601
+ self.feedrate_dirty = False
602
+ if "G20" in modals:
603
+ self._g20_units_inch()
604
+ self.units_dirty = False
605
+ if "G21" in modals:
606
+ self._g21_units_mm()
607
+ self.units_dirty = False
608
+
609
+ def declare_position(self, x, y):
610
+ self.native_x = x * self.unit_scale
611
+ self.native_y = y * self.unit_scale
612
+
574
613
  ####################
575
614
  # PROTECTED DRIVER CODE
576
615
  ####################