meerk40t 0.9.7030__py2.py3-none-any.whl → 0.9.7050__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 (85) hide show
  1. meerk40t/balormk/clone_loader.py +3 -2
  2. meerk40t/balormk/controller.py +38 -13
  3. meerk40t/balormk/cylindermod.py +1 -0
  4. meerk40t/balormk/device.py +13 -9
  5. meerk40t/balormk/driver.py +9 -2
  6. meerk40t/balormk/galvo_commands.py +3 -1
  7. meerk40t/balormk/gui/gui.py +6 -0
  8. meerk40t/balormk/livelightjob.py +338 -321
  9. meerk40t/balormk/mock_connection.py +4 -3
  10. meerk40t/balormk/usb_connection.py +11 -2
  11. meerk40t/camera/camera.py +19 -14
  12. meerk40t/camera/gui/camerapanel.py +6 -0
  13. meerk40t/core/cutplan.py +101 -78
  14. meerk40t/core/elements/element_treeops.py +435 -140
  15. meerk40t/core/elements/elements.py +100 -9
  16. meerk40t/core/elements/shapes.py +259 -72
  17. meerk40t/core/elements/tree_commands.py +10 -5
  18. meerk40t/core/node/blobnode.py +19 -4
  19. meerk40t/core/node/elem_ellipse.py +18 -8
  20. meerk40t/core/node/elem_image.py +51 -19
  21. meerk40t/core/node/elem_line.py +18 -8
  22. meerk40t/core/node/elem_path.py +18 -8
  23. meerk40t/core/node/elem_point.py +10 -4
  24. meerk40t/core/node/elem_polyline.py +19 -11
  25. meerk40t/core/node/elem_rect.py +18 -8
  26. meerk40t/core/node/elem_text.py +11 -5
  27. meerk40t/core/node/filenode.py +2 -8
  28. meerk40t/core/node/groupnode.py +11 -11
  29. meerk40t/core/node/image_processed.py +11 -5
  30. meerk40t/core/node/image_raster.py +11 -5
  31. meerk40t/core/node/node.py +64 -16
  32. meerk40t/core/node/refnode.py +2 -1
  33. meerk40t/core/planner.py +25 -11
  34. meerk40t/core/svg_io.py +91 -34
  35. meerk40t/device/dummydevice.py +7 -1
  36. meerk40t/extra/vtracer.py +222 -0
  37. meerk40t/grbl/device.py +96 -9
  38. meerk40t/grbl/driver.py +15 -5
  39. meerk40t/gui/about.py +20 -0
  40. meerk40t/gui/devicepanel.py +20 -16
  41. meerk40t/gui/gui_mixins.py +4 -0
  42. meerk40t/gui/icons.py +330 -253
  43. meerk40t/gui/laserpanel.py +27 -3
  44. meerk40t/gui/laserrender.py +41 -21
  45. meerk40t/gui/magnetoptions.py +158 -65
  46. meerk40t/gui/materialtest.py +569 -310
  47. meerk40t/gui/navigationpanels.py +229 -24
  48. meerk40t/gui/propertypanels/hatchproperty.py +2 -0
  49. meerk40t/gui/propertypanels/imageproperty.py +160 -106
  50. meerk40t/gui/propertypanels/wobbleproperty.py +6 -2
  51. meerk40t/gui/ribbon.py +6 -1
  52. meerk40t/gui/scenewidgets/gridwidget.py +29 -32
  53. meerk40t/gui/scenewidgets/rectselectwidget.py +190 -192
  54. meerk40t/gui/simulation.py +75 -77
  55. meerk40t/gui/spoolerpanel.py +27 -7
  56. meerk40t/gui/statusbarwidgets/defaultoperations.py +84 -48
  57. meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
  58. meerk40t/gui/tips.py +15 -1
  59. meerk40t/gui/toolwidgets/toolpointmove.py +3 -1
  60. meerk40t/gui/wxmmain.py +242 -114
  61. meerk40t/gui/wxmscene.py +107 -24
  62. meerk40t/gui/wxmtree.py +4 -2
  63. meerk40t/gui/wxutils.py +286 -15
  64. meerk40t/image/imagetools.py +129 -65
  65. meerk40t/internal_plugins.py +4 -0
  66. meerk40t/kernel/kernel.py +67 -18
  67. meerk40t/kernel/settings.py +28 -9
  68. meerk40t/lihuiyu/device.py +24 -12
  69. meerk40t/main.py +14 -9
  70. meerk40t/moshi/device.py +20 -6
  71. meerk40t/network/console_server.py +22 -6
  72. meerk40t/newly/device.py +10 -3
  73. meerk40t/newly/gui/gui.py +10 -0
  74. meerk40t/ruida/device.py +22 -2
  75. meerk40t/ruida/loader.py +9 -4
  76. meerk40t/ruida/rdjob.py +48 -8
  77. meerk40t/tools/geomstr.py +240 -123
  78. meerk40t/tools/rasterplotter.py +185 -94
  79. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/METADATA +1 -1
  80. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/RECORD +85 -84
  81. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/LICENSE +0 -0
  82. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/WHEEL +0 -0
  83. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/entry_points.txt +0 -0
  84. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/top_level.txt +0 -0
  85. {meerk40t-0.9.7030.dist-info → meerk40t-0.9.7050.dist-info}/zip-safe +0 -0
@@ -1,6 +1,6 @@
1
1
  import ast
2
- import os
3
2
  import configparser
3
+ import os
4
4
  from pathlib import Path
5
5
  from typing import Any, Dict, Generator, Optional, Union
6
6
 
@@ -22,7 +22,9 @@ class Settings:
22
22
 
23
23
  def __init__(self, directory, filename, ignore_settings=False, create_backup=False):
24
24
  if directory:
25
- self._config_file = Path(get_safe_path(directory, create=True)).joinpath(filename)
25
+ self._config_file = Path(get_safe_path(directory, create=True)).joinpath(
26
+ filename
27
+ )
26
28
  else:
27
29
  self._config_file = filename
28
30
  self._config_dict = {}
@@ -51,13 +53,20 @@ class Settings:
51
53
  PermissionError,
52
54
  configparser.NoSectionError,
53
55
  configparser.MissingSectionHeaderError,
56
+ configparser.ParsingError,
57
+ configparser.NoOptionError,
54
58
  FileNotFoundError,
55
59
  ):
56
60
  return
57
61
  except UnicodeDecodeError as e:
58
- print ("The config file contained unsupported characters, please share the file with the dev team")
59
- except (configparser.DuplicateOptionError, configparser.DuplicateSectionError) as e:
60
- print (f"We had a duplication error in the config, try to recover from {e}")
62
+ print(
63
+ "The config file contained unsupported characters, please share the file with the dev team"
64
+ )
65
+ except (
66
+ configparser.DuplicateOptionError,
67
+ configparser.DuplicateSectionError,
68
+ ) as e:
69
+ print(f"We had a duplication error in the config, try to recover from {e}")
61
70
  for section in parser.sections():
62
71
  for option in parser.options(section):
63
72
  try:
@@ -65,7 +74,12 @@ class Settings:
65
74
  except KeyError:
66
75
  config_section = {}
67
76
  self._config_dict[section] = config_section
68
- config_section[option] = parser.get(section, option)
77
+ try:
78
+ config_section[option] = parser.get(section, option)
79
+ except Exception as e:
80
+ print(
81
+ f"We had an error in the config, section {section}.{option}, try to recover from {e}"
82
+ )
69
83
 
70
84
  def write_configuration(self, targetfile=None):
71
85
  """
@@ -91,8 +105,13 @@ class Settings:
91
105
  except configparser.NoSectionError:
92
106
  parser.add_section(section_key)
93
107
  parser.set(section_key, key, value)
94
- except (configparser.DuplicateOptionError, configparser.DuplicateSectionError) as e:
95
- print (f"We had a duplication error in the config, try to recover from {e}")
108
+ except (
109
+ configparser.DuplicateOptionError,
110
+ configparser.DuplicateSectionError,
111
+ ) as e:
112
+ print(
113
+ f"We had a duplication error in the config, try to recover from {e}"
114
+ )
96
115
 
97
116
  if self.create_backup:
98
117
  VERSIONS = 5
@@ -125,7 +144,7 @@ class Settings:
125
144
  pass
126
145
  with open(targetfile, "w", encoding="utf-8") as fp:
127
146
  parser.write(fp)
128
- except (PermissionError, FileNotFoundError):
147
+ except (PermissionError, FileNotFoundError, OSError, RuntimeError):
129
148
  return
130
149
 
131
150
  def literal_dict(self):
@@ -5,21 +5,21 @@ Registers the Device service for M2 Nano (and family), registering the relevant
5
5
  the given device type.
6
6
  """
7
7
 
8
- from hashlib import md5
9
8
  import platform
9
+ from hashlib import md5
10
10
 
11
11
  import meerk40t.constants as mkconst
12
12
  from meerk40t.core.laserjob import LaserJob
13
13
  from meerk40t.core.spoolers import Spooler
14
+ from meerk40t.core.units import UNITS_PER_MIL, Length
14
15
  from meerk40t.core.view import View
16
+ from meerk40t.device.devicechoices import get_effect_choices
17
+ from meerk40t.device.mixins import Status
15
18
  from meerk40t.kernel import CommandSyntaxError, Service, signal_listener
16
19
 
17
- from meerk40t.core.units import UNITS_PER_MIL, Length
18
- from meerk40t.device.mixins import Status
19
20
  from .controller import LihuiyuController
20
21
  from .driver import LihuiyuDriver
21
22
  from .tcp_connection import TCPOutput
22
- from meerk40t.device.devicechoices import get_effect_choices
23
23
 
24
24
 
25
25
  class LihuiyuDevice(Service, Status):
@@ -149,9 +149,7 @@ class LihuiyuDevice(Service, Status):
149
149
  "label": _("Board"),
150
150
  "style": "combosmall",
151
151
  "choices": ["M2", "M3", "B2", "M", "M1", "A", "B", "B1"],
152
- "tip": _(
153
- "Select the board to use. This affects the speedcodes used."
154
- ),
152
+ "tip": _("Select the board to use. This affects the speedcodes used."),
155
153
  "section": "_10_" + _("Configuration"),
156
154
  "subsection": _("Board Setup"),
157
155
  },
@@ -374,8 +372,13 @@ class LihuiyuDevice(Service, Status):
374
372
  "type": bool,
375
373
  "label": _("Use legacy raster method"),
376
374
  "tip": (
377
- _("Active: Use legacy method (seems to work better at higher speeds, but has some artifacts)") + "\n" +
378
- _("Inactive: Use regular method (no artifacts but apparently more prone to stuttering at high speeds)")
375
+ _(
376
+ "Active: Use legacy method (seems to work better at higher speeds, but has some artifacts)"
377
+ )
378
+ + "\n"
379
+ + _(
380
+ "Inactive: Use regular method (no artifacts but apparently more prone to stuttering at high speeds)"
381
+ )
379
382
  ),
380
383
  "section": "_00_" + _("General Options"),
381
384
  "subsection": "_20_",
@@ -1022,10 +1025,12 @@ class LihuiyuDevice(Service, Status):
1022
1025
  return {
1023
1026
  "split_crossover": True,
1024
1027
  "unsupported_opt": (
1025
- mkconst.RASTER_GREEDY_H, mkconst.RASTER_GREEDY_V, mkconst.RASTER_SPIRAL,
1028
+ mkconst.RASTER_GREEDY_H,
1029
+ mkconst.RASTER_GREEDY_V,
1030
+ mkconst.RASTER_SPIRAL,
1026
1031
  ), # Greedy loses registration way too often to be reliable
1027
- "gantry" : True,
1028
- "legacy" : self.legacy_raster,
1032
+ "gantry": True,
1033
+ "legacy": self.legacy_raster,
1029
1034
  }
1030
1035
 
1031
1036
  @property
@@ -1185,3 +1190,10 @@ class LihuiyuDevice(Service, Status):
1185
1190
 
1186
1191
  def cool_helper(self, choice_dict):
1187
1192
  self.kernel.root.coolant.coolant_choice_helper(self)(choice_dict)
1193
+
1194
+ def location(self):
1195
+ if self.mock:
1196
+ return "mock"
1197
+ if self.networked:
1198
+ return f"tcp {self.address}:{self.port}"
1199
+ return f"usb {'auto' if self.usb_index < 0 else self.usb_index}"
meerk40t/main.py CHANGED
@@ -11,7 +11,7 @@ import os.path
11
11
  import sys
12
12
 
13
13
  APPLICATION_NAME = "MeerK40t"
14
- APPLICATION_VERSION = "0.9.7030"
14
+ APPLICATION_VERSION = "0.9.7050"
15
15
 
16
16
  if not getattr(sys, "frozen", False):
17
17
  # If .git directory does not exist we are running from a package like pypi
@@ -229,19 +229,24 @@ def _exe(restarted, args):
229
229
  server_mode = False
230
230
  if command:
231
231
  for c in command:
232
- server_mode = server_mode or any(substring in c for substring in ("lhyserver", "grblserver", "ruidacontrol", "grblcontrol", "webserver"))
233
- nogui = (
234
- (hasattr(kernel.args, "gui_suppress") and kernel.args.gui_suppress) or
235
- (hasattr(kernel.args, "no_gui") and kernel.args.no_gui)
232
+ server_mode = server_mode or any(
233
+ substring in c
234
+ for substring in (
235
+ "lhyserver",
236
+ "grblserver",
237
+ "ruidacontrol",
238
+ "grblcontrol",
239
+ "webserver",
240
+ )
241
+ )
242
+ nogui = (hasattr(kernel.args, "gui_suppress") and kernel.args.gui_suppress) or (
243
+ hasattr(kernel.args, "no_gui") and kernel.args.no_gui
236
244
  )
237
245
  for idx, attrib in enumerate(("mktablength", "mktabpositions")):
238
246
  kernel.register(f"registered_mk_svg_parameters/tabs{idx}", attrib)
239
247
 
240
248
  require_partial_mode = False
241
- if (
242
- (not console or nogui) and
243
- (auto or daemon or server_mode)
244
- ):
249
+ if (not console or nogui) and (auto or daemon or server_mode):
245
250
  require_partial_mode = True
246
251
  # print (f"Auto: {auto}, Command: {command}, Console: {console}, daemon: {daemon}, nogui:{nogui}, Server: {server_mode} -> {require_partial_mode}")
247
252
  kernel(partial=require_partial_mode)
meerk40t/moshi/device.py CHANGED
@@ -7,6 +7,7 @@ Registers relevant commands and options.
7
7
  """
8
8
  import meerk40t.constants as mkconst
9
9
  from meerk40t.core.view import View
10
+ from meerk40t.device.devicechoices import get_effect_choices
10
11
  from meerk40t.kernel import CommandSyntaxError, Service, signal_listener
11
12
 
12
13
  from ..core.laserjob import LaserJob
@@ -15,7 +16,6 @@ from ..core.units import Length
15
16
  from ..device.mixins import Status
16
17
  from .controller import MoshiController
17
18
  from .driver import MoshiDriver
18
- from meerk40t.device.devicechoices import get_effect_choices
19
19
 
20
20
 
21
21
  class MoshiDevice(Service, Status):
@@ -210,8 +210,13 @@ class MoshiDevice(Service, Status):
210
210
  "type": bool,
211
211
  "label": _("Use legacy raster method"),
212
212
  "tip": (
213
- _("Active: Use legacy method (seems to work better at higher speeds, but has some artifacts)") + "\n" +
214
- _("Inactive: Use regular method (no artifacts but apparently more prone to stuttering at high speeds)")
213
+ _(
214
+ "Active: Use legacy method (seems to work better at higher speeds, but has some artifacts)"
215
+ )
216
+ + "\n"
217
+ + _(
218
+ "Inactive: Use regular method (no artifacts but apparently more prone to stuttering at high speeds)"
219
+ )
215
220
  ),
216
221
  "section": "_20_Behaviour",
217
222
  },
@@ -485,11 +490,20 @@ class MoshiDevice(Service, Status):
485
490
  return {
486
491
  "split_crossover": True,
487
492
  "unsupported_opt": (
488
- mkconst.RASTER_GREEDY_H, mkconst.RASTER_GREEDY_V, mkconst.RASTER_SPIRAL,
493
+ mkconst.RASTER_GREEDY_H,
494
+ mkconst.RASTER_GREEDY_V,
495
+ mkconst.RASTER_SPIRAL,
489
496
  ), # Greedy loses registration way too often to be reliable
490
- "gantry" : True,
491
- "legacy" : self.legacy_raster,
497
+ "gantry": True,
498
+ "legacy": self.legacy_raster,
492
499
  }
493
500
 
494
501
  def cool_helper(self, choice_dict):
495
502
  self.kernel.root.coolant.coolant_choice_helper(self)(choice_dict)
503
+
504
+ def location(self):
505
+ return (
506
+ "mock"
507
+ if self.mock
508
+ else f"usb {'auto' if self.usb_index < 0 else self.usb_index}"
509
+ )
@@ -12,6 +12,13 @@ def plugin(kernel, lifecycle=None):
12
12
  action="store_true",
13
13
  help=_("do not watch server channels"),
14
14
  )
15
+ @kernel.console_option(
16
+ "suppress",
17
+ "u",
18
+ type=bool,
19
+ action="store_true",
20
+ help=_("suppress input prompt '>>'"),
21
+ )
15
22
  @kernel.console_option(
16
23
  "quit",
17
24
  "q",
@@ -23,8 +30,18 @@ def plugin(kernel, lifecycle=None):
23
30
  "consoleserver", help=_("starts a console_server on port 23 (telnet)")
24
31
  )
25
32
  def server_console(
26
- command, channel, _, port=23, silent=False, quit=False, **kwargs
33
+ command,
34
+ channel,
35
+ _,
36
+ port=23,
37
+ silent=False,
38
+ quit=False,
39
+ suppress=False,
40
+ **kwargs,
27
41
  ):
42
+ if suppress is None:
43
+ suppress = False
44
+ kernel.show_aio_prompt = not suppress
28
45
  root = kernel.root
29
46
  # Variable to store input
30
47
  root.__console_buffer = ""
@@ -42,9 +59,9 @@ def plugin(kernel, lifecycle=None):
42
59
  except (OSError, ValueError):
43
60
  channel(_("Server failed on port: {port}").format(port=port))
44
61
  return
45
-
62
+
46
63
  def exec_command(data: str) -> None:
47
- # We are in a different thread, so let's hand over stuff to the gui
64
+ # We are in a different thread, so let's hand over stuff to the gui
48
65
  if isinstance(data, bytes):
49
66
  try:
50
67
  data = data.decode()
@@ -60,7 +77,7 @@ def plugin(kernel, lifecycle=None):
60
77
  # No: we can split the command
61
78
  quotations = data.count('"', 0, idx)
62
79
  if quotations % 2 == 0:
63
- data = data[:idx].rstrip() + "\n" + data[idx+1:].lstrip()
80
+ data = data[:idx].rstrip() + "\n" + data[idx + 1 :].lstrip()
64
81
  start = idx + 1
65
82
  root.__console_buffer += data
66
83
  while "\n" in root.__console_buffer:
@@ -76,7 +93,7 @@ def plugin(kernel, lifecycle=None):
76
93
  for result in root.find("gui/handover"):
77
94
  # Do we have a thread handover routine?
78
95
  if result is not None:
79
- handover, _path, suffix_path = result
96
+ handover, _path, suffix_path = result
80
97
  break
81
98
  recv.watch(exec_command)
82
99
 
@@ -93,7 +110,6 @@ def plugin(kernel, lifecycle=None):
93
110
  console.watch(send)
94
111
  server.events_channel.watch(console)
95
112
 
96
-
97
113
  @kernel.console_option(
98
114
  "port", "p", type=int, default=2080, help=_("port to listen on.")
99
115
  )
meerk40t/newly/device.py CHANGED
@@ -5,10 +5,10 @@ from meerk40t.core.laserjob import LaserJob
5
5
  from meerk40t.core.spoolers import Spooler
6
6
  from meerk40t.core.units import Length
7
7
  from meerk40t.core.view import View
8
+ from meerk40t.device.devicechoices import get_effect_choices
8
9
  from meerk40t.device.mixins import Status
9
10
  from meerk40t.kernel import CommandSyntaxError, Service, signal_listener
10
11
  from meerk40t.newly.driver import NewlyDriver
11
- from meerk40t.device.devicechoices import get_effect_choices
12
12
 
13
13
 
14
14
  class NewlyDevice(Service, Status):
@@ -757,12 +757,16 @@ class NewlyDevice(Service, Status):
757
757
  help=_("Sets the default file index to use"),
758
758
  all_arguments_required=True,
759
759
  )
760
- def set_file_index(command, channel, _, file_index=None, data=None, remainder=None, **kwgs):
760
+ def set_file_index(
761
+ command, channel, _, file_index=None, data=None, remainder=None, **kwgs
762
+ ):
761
763
  old_value = self.file_index
762
764
  if file_index is None or file_index < 0 or file_index >= 10:
763
765
  file_index = 0
764
766
  self.file_index = file_index
765
- channel(f"File index was set to #{file_index} (previous value: {old_value})")
767
+ channel(
768
+ f"File index was set to #{file_index} (previous value: {old_value})"
769
+ )
766
770
  # Let propertypanels know that this value was updated
767
771
  self.signal("file_index", file_index, self)
768
772
 
@@ -856,6 +860,9 @@ class NewlyDevice(Service, Status):
856
860
  )
857
861
  self.signal("view;realized")
858
862
 
863
+ def location(self):
864
+ return "mock" if self.mock else "usb"
865
+
859
866
  @property
860
867
  def current(self):
861
868
  """
meerk40t/newly/gui/gui.py CHANGED
@@ -78,6 +78,7 @@ def plugin(service, lifecycle):
78
78
  "tip": _("File {index}").format(index=0),
79
79
  "help": "devicenewly",
80
80
  "action": lambda v: service("select_file 0\n"),
81
+ "multi_autoexec": True,
81
82
  },
82
83
  {
83
84
  "identifier": 1,
@@ -85,6 +86,7 @@ def plugin(service, lifecycle):
85
86
  "tip": _("File {index}").format(index=1),
86
87
  "help": "devicenewly",
87
88
  "action": lambda v: service("select_file 1\n"),
89
+ "multi_autoexec": True,
88
90
  },
89
91
  {
90
92
  "identifier": 2,
@@ -92,6 +94,7 @@ def plugin(service, lifecycle):
92
94
  "tip": _("File {index}").format(index=2),
93
95
  "help": "devicenewly",
94
96
  "action": lambda v: service("select_file 2\n"),
97
+ "multi_autoexec": True,
95
98
  },
96
99
  {
97
100
  "identifier": 3,
@@ -99,6 +102,7 @@ def plugin(service, lifecycle):
99
102
  "tip": _("File {index}").format(index=3),
100
103
  "help": "devicenewly",
101
104
  "action": lambda v: service("select_file 3\n"),
105
+ "multi_autoexec": True,
102
106
  },
103
107
  {
104
108
  "identifier": 4,
@@ -106,6 +110,7 @@ def plugin(service, lifecycle):
106
110
  "tip": _("File {index}").format(index=4),
107
111
  "help": "devicenewly",
108
112
  "action": lambda v: service("select_file 4\n"),
113
+ "multi_autoexec": True,
109
114
  },
110
115
  {
111
116
  "identifier": 5,
@@ -113,6 +118,7 @@ def plugin(service, lifecycle):
113
118
  "tip": _("File {index}").format(index=5),
114
119
  "help": "devicenewly",
115
120
  "action": lambda v: service("select_file 5\n"),
121
+ "multi_autoexec": True,
116
122
  },
117
123
  {
118
124
  "identifier": 6,
@@ -120,6 +126,7 @@ def plugin(service, lifecycle):
120
126
  "tip": _("File {index}").format(index=6),
121
127
  "help": "devicenewly",
122
128
  "action": lambda v: service("select_file 6\n"),
129
+ "multi_autoexec": True,
123
130
  },
124
131
  {
125
132
  "identifier": 7,
@@ -127,6 +134,7 @@ def plugin(service, lifecycle):
127
134
  "tip": _("File {index}").format(index=7),
128
135
  "help": "devicenewly",
129
136
  "action": lambda v: service("select_file 7\n"),
137
+ "multi_autoexec": True,
130
138
  },
131
139
  {
132
140
  "identifier": 8,
@@ -134,6 +142,7 @@ def plugin(service, lifecycle):
134
142
  "tip": _("File {index}").format(index=8),
135
143
  "help": "devicenewly",
136
144
  "action": lambda v: service("select_file 8\n"),
145
+ "multi_autoexec": True,
137
146
  },
138
147
  {
139
148
  "identifier": 9,
@@ -141,6 +150,7 @@ def plugin(service, lifecycle):
141
150
  "tip": _("File {index}").format(index=9),
142
151
  "help": "devicenewly",
143
152
  "action": lambda v: service("select_file 9\n"),
153
+ "multi_autoexec": True,
144
154
  },
145
155
  ],
146
156
  },
meerk40t/ruida/device.py CHANGED
@@ -5,6 +5,7 @@ Ruida device interfacing. We do not send or interpret ruida code, but we can emu
5
5
  ruida files (*.rd) and turn them likewise into cutcode.
6
6
  """
7
7
  from meerk40t.core.view import View
8
+ from meerk40t.device.devicechoices import get_effect_choices
8
9
  from meerk40t.kernel import CommandSyntaxError, Service, signal_listener
9
10
 
10
11
  from ..core.laserjob import LaserJob
@@ -16,7 +17,6 @@ from .mock_connection import MockConnection
16
17
  from .serial_connection import SerialConnection
17
18
  from .tcp_connection import TCPConnection
18
19
  from .udp_connection import UDPConnection
19
- from meerk40t.device.devicechoices import get_effect_choices
20
20
 
21
21
 
22
22
  class RuidaDevice(Service):
@@ -264,6 +264,7 @@ class RuidaDevice(Service):
264
264
  choice_dict["display"] = ["pyserial-not-installed"]
265
265
 
266
266
  from platform import system
267
+
267
268
  is_linux = system() == "Linux"
268
269
 
269
270
  choices = [
@@ -278,7 +279,7 @@ class RuidaDevice(Service):
278
279
  "section": "_10_Serial Interface",
279
280
  "subsection": "_00_",
280
281
  "dynamic": update,
281
- "exclusive": not is_linux,
282
+ "exclusive": not is_linux,
282
283
  },
283
284
  {
284
285
  "attr": "baud_rate",
@@ -581,6 +582,25 @@ class RuidaDevice(Service):
581
582
  name = self.label.replace(" ", "-")
582
583
  return name.replace("/", "-")
583
584
 
585
+ @property
586
+ def tcp_address(self):
587
+ return self.address
588
+
589
+ @property
590
+ def tcp_port(self):
591
+ return 5005
592
+
593
+ def location(self):
594
+ if self.interface == "mock":
595
+ return "mock"
596
+ elif self.interface == "udp":
597
+ return "udp, port 40200"
598
+ elif self.interface == "tcp":
599
+ return f"tcp {self.tcp_address}:{self.tcp_port}"
600
+ elif self.interface == "usb":
601
+ return f"usb: {self.serial_port}"
602
+ return f"undefined {self.interface}"
603
+
584
604
  @property
585
605
  def has_endstops(self):
586
606
  return True
meerk40t/ruida/loader.py CHANGED
@@ -15,9 +15,11 @@ def data_viewer(data, data_type):
15
15
 
16
16
  if not data:
17
17
  return ""
18
+ magic = determine_magic_via_histogram(data)
18
19
  return BlobNode.hex_view(
19
- data=decode_bytes(data, determine_magic_via_histogram(data)),
20
+ data=decode_bytes(data, magic),
20
21
  data_type=data_type,
22
+ info = f', Magic={magic} (0x{magic:02x})'
21
23
  )
22
24
 
23
25
 
@@ -26,10 +28,13 @@ def command_viewer(data, data_type):
26
28
 
27
29
  job = RDJob()
28
30
  job.write_blob(data)
29
- commands = list()
31
+ commands = []
30
32
  job.channel = commands.append
31
- while not job.execute(None):
32
- pass
33
+ try:
34
+ while not job.execute(None):
35
+ pass
36
+ except Exception as e:
37
+ commands.append(f"!! Error !!: {e}")
33
38
  return "\n".join(commands)
34
39
 
35
40
 
meerk40t/ruida/rdjob.py CHANGED
@@ -256,6 +256,8 @@ def encode_relcoord(coord):
256
256
 
257
257
 
258
258
  def encode_color(color):
259
+ # Scewed on RDC 22.01
260
+ # Maybe 16bit color is used?
259
261
  return encode32(int(color))
260
262
 
261
263
 
@@ -488,6 +490,7 @@ class RDJob:
488
490
  self._stopped = True
489
491
  self.enabled = True
490
492
  self._estimate = 0
493
+ self.offset = 0
491
494
 
492
495
  self.scale = UNITS_PER_uM
493
496
 
@@ -598,7 +601,8 @@ class RDJob:
598
601
  command = self.buffer.pop(0)
599
602
  try:
600
603
  array = list(command)
601
- self.process(array)
604
+ self.process(array, offset=self.offset)
605
+ self.offset += len(command)
602
606
  except IndexError as e:
603
607
  raise RuidaCommandError(
604
608
  f"Could not process Ruida buffer, {self.buffer[:25]} with magic: {self.magic:02}"
@@ -701,7 +705,7 @@ class RDJob:
701
705
  def set_color(self, color):
702
706
  self.color = color
703
707
 
704
- def process(self, array):
708
+ def process(self, array, offset=None):
705
709
  """
706
710
  Parses an individual unswizzled ruida command, updating the emulator state.
707
711
 
@@ -976,7 +980,7 @@ class RDJob:
976
980
  b = (c >> 16) & 0xFF
977
981
  c = Color(red=r, blue=b, green=g)
978
982
  self.set_color(c.hex)
979
- desc = f"{part}, Color {self.color}"
983
+ desc = f"Color Part {part}, {self.color}"
980
984
  elif array[1] == 0x10:
981
985
  value = array[2]
982
986
  desc = f"EnExIO Start {value}"
@@ -1012,12 +1016,44 @@ class RDJob:
1012
1016
  elif array[0] == 0xD8:
1013
1017
  if array[1] == 0x00:
1014
1018
  desc = "Start Process"
1015
- if array[1] == 0x10:
1019
+ elif array[1] == 0x10:
1016
1020
  desc = "Ref Point Mode 2, Machine Zero/Absolute Position"
1017
- if array[1] == 0x11:
1021
+ elif array[1] == 0x11:
1018
1022
  desc = "Ref Point Mode 1, Anchor Point"
1019
- if array[1] == 0x12:
1023
+ elif array[1] == 0x12:
1020
1024
  desc = "Ref Point Mode 0, Current Position"
1025
+ elif array[0] == 0xD9:
1026
+ if array[1] == 0x00:
1027
+ opts = array[2]
1028
+ value = abscoord(array[3:8])
1029
+ desc = f"Rapid move X ({value}μm)"
1030
+ elif array[1] == 0x01:
1031
+ opts = array[2]
1032
+ value = abscoord(array[3:8])
1033
+ desc = f"Rapid move Y ({value}μm)"
1034
+ elif array[1] == 0x02:
1035
+ opts = array[2]
1036
+ value = abscoord(array[3:8])
1037
+ desc = f"Rapid move Z ({value}μm)"
1038
+ elif array[1] == 0x03:
1039
+ opts = array[2]
1040
+ value = abscoord(array[3:8])
1041
+ desc = f"Rapid move U ({value}μm)"
1042
+ elif array[1] == 0x0F:
1043
+ opts = array[2]
1044
+ value = abscoord(array[3:8])
1045
+ desc = f"Rapid move Feed ({value}μm)"
1046
+ elif array[1] == 0x10:
1047
+ opts = array[2]
1048
+ x = abscoord(array[3:8])
1049
+ y = abscoord(array[8:13])
1050
+ desc = f"Rapid move XY ({x}μm, {y}μm)"
1051
+ elif array[1] == 0x30:
1052
+ opts = array[2]
1053
+ x = abscoord(array[3:7])
1054
+ y = abscoord(array[8:13])
1055
+ u = abscoord(array[13:18])
1056
+ desc = f"Rapid move XYU ({x}μm, {y}μm, {u}μm)"
1021
1057
  elif array[0] == 0xDA:
1022
1058
  mem = parse_mem(array[2:4])
1023
1059
  if array[1] == 0x01:
@@ -1240,7 +1276,8 @@ class RDJob:
1240
1276
  else:
1241
1277
  desc = "Unknown Command!"
1242
1278
  if self.channel:
1243
- self.channel(f"-**-> {str(bytes(array).hex())}\t({desc})")
1279
+ prefix = f"{offset:06x}" if offset is not None else ''
1280
+ self.channel(f"{prefix}-**-> {str(bytes(array).hex())}\t({desc})")
1244
1281
 
1245
1282
  def unswizzle(self, data):
1246
1283
  return bytes([self.lut_unswizzle[b] for b in data])
@@ -1325,6 +1362,9 @@ class RDJob:
1325
1362
  color = current_settings.get("line_color", 0)
1326
1363
  frequency = current_settings.get("frequency")
1327
1364
 
1365
+ if color == 0:
1366
+ color = current_settings.get("color", color)
1367
+
1328
1368
  self.speed_laser_1_part(part, speed)
1329
1369
  if frequency:
1330
1370
  self.frequency_part(0, part, frequency)
@@ -1670,7 +1710,7 @@ class RDJob:
1670
1710
  self(LAYER_COLOR, encode_color(color), output=output)
1671
1711
 
1672
1712
  def layer_color_part(self, part, color, output=None):
1673
- self(LAYER_COLOR, encode_part(part), encode_color(color), output=output)
1713
+ self(LAYER_COLOR_PART, encode_part(part), encode_color(color), output=output)
1674
1714
 
1675
1715
  def en_ex_io(self, value, output=None):
1676
1716
  """