meerk40t 0.9.7051__py2.py3-none-any.whl → 0.9.7900__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.
- meerk40t/balormk/controller.py +3 -3
- meerk40t/balormk/device.py +7 -0
- meerk40t/balormk/driver.py +23 -14
- meerk40t/balormk/galvo_commands.py +18 -3
- meerk40t/balormk/gui/balorconfig.py +6 -0
- meerk40t/balormk/livelightjob.py +36 -14
- meerk40t/camera/camera.py +1 -0
- meerk40t/camera/gui/camerapanel.py +154 -58
- meerk40t/camera/plugin.py +46 -5
- meerk40t/core/elements/branches.py +90 -20
- meerk40t/core/elements/elements.py +59 -37
- meerk40t/core/elements/trace.py +10 -6
- meerk40t/core/node/node.py +2 -0
- meerk40t/core/plotplanner.py +7 -4
- meerk40t/device/gui/defaultactions.py +78 -14
- meerk40t/dxf/dxf_io.py +42 -0
- meerk40t/grbl/controller.py +245 -35
- meerk40t/grbl/device.py +102 -26
- meerk40t/grbl/driver.py +8 -2
- meerk40t/grbl/gui/grblconfiguration.py +6 -0
- meerk40t/grbl/gui/grblcontroller.py +1 -1
- meerk40t/gui/about.py +7 -0
- meerk40t/gui/choicepropertypanel.py +20 -30
- meerk40t/gui/devicepanel.py +27 -16
- meerk40t/gui/icons.py +15 -0
- meerk40t/gui/laserpanel.py +102 -54
- meerk40t/gui/materialtest.py +10 -0
- meerk40t/gui/mkdebug.py +268 -9
- meerk40t/gui/navigationpanels.py +65 -7
- meerk40t/gui/propertypanels/operationpropertymain.py +185 -91
- meerk40t/gui/scenewidgets/elementswidget.py +7 -1
- meerk40t/gui/scenewidgets/selectionwidget.py +24 -9
- meerk40t/gui/simulation.py +1 -1
- meerk40t/gui/statusbarwidgets/shapepropwidget.py +50 -40
- meerk40t/gui/statusbarwidgets/statusbar.py +2 -2
- meerk40t/gui/toolwidgets/toolmeasure.py +1 -1
- meerk40t/gui/toolwidgets/toolnodeedit.py +4 -1
- meerk40t/gui/toolwidgets/tooltabedit.py +9 -7
- meerk40t/gui/wxmeerk40t.py +2 -0
- meerk40t/gui/wxmmain.py +23 -9
- meerk40t/gui/wxmribbon.py +36 -0
- meerk40t/gui/wxutils.py +66 -42
- meerk40t/kernel/inhibitor.py +120 -0
- meerk40t/kernel/kernel.py +38 -0
- meerk40t/lihuiyu/controller.py +33 -3
- meerk40t/lihuiyu/device.py +99 -4
- meerk40t/lihuiyu/driver.py +62 -5
- meerk40t/lihuiyu/gui/lhycontrollergui.py +69 -24
- meerk40t/lihuiyu/gui/lhydrivergui.py +6 -0
- meerk40t/lihuiyu/laserspeed.py +17 -10
- meerk40t/lihuiyu/parser.py +23 -0
- meerk40t/main.py +1 -1
- meerk40t/moshi/gui/moshidrivergui.py +7 -0
- meerk40t/newly/controller.py +3 -2
- meerk40t/newly/device.py +23 -2
- meerk40t/newly/driver.py +8 -3
- meerk40t/newly/gui/newlyconfig.py +7 -0
- meerk40t/ruida/gui/ruidaconfig.py +7 -0
- meerk40t/tools/geomstr.py +68 -48
- meerk40t/tools/rasterplotter.py +0 -5
- meerk40t/tools/ttfparser.py +155 -82
- {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/METADATA +1 -1
- {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/RECORD +68 -67
- {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/LICENSE +0 -0
- {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/WHEEL +0 -0
- {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/entry_points.txt +0 -0
- {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/top_level.txt +0 -0
- {meerk40t-0.9.7051.dist-info → meerk40t-0.9.7900.dist-info}/zip-safe +0 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
"""
|
2
|
+
This modules prevents the OS from sleeping / hibernating.
|
3
|
+
It is used to ensure that the system remains active during long-running operations.
|
4
|
+
It is not intended to be used for general-purpose tasks.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import ctypes
|
8
|
+
import platform
|
9
|
+
import subprocess
|
10
|
+
|
11
|
+
_ES_CONTINUOUS = 0x80000000
|
12
|
+
_ES_SYSTEM_REQUIRED = 0x00000001
|
13
|
+
|
14
|
+
# Extract common target list up top
|
15
|
+
_SYSTEMCTL_TARGETS = [
|
16
|
+
"sleep.target",
|
17
|
+
"suspend.target",
|
18
|
+
"hibernate.target",
|
19
|
+
"hybrid-sleep.target",
|
20
|
+
]
|
21
|
+
|
22
|
+
|
23
|
+
def _run_systemctl(action: str):
|
24
|
+
try:
|
25
|
+
# Check if systemctl is available
|
26
|
+
proc = subprocess.run(["systemctl", action] + _SYSTEMCTL_TARGETS)
|
27
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
28
|
+
print("systemctl is not available on this system.")
|
29
|
+
return False
|
30
|
+
except Exception as e:
|
31
|
+
print(f"An unexpected error occurred: {e}")
|
32
|
+
return False
|
33
|
+
# print(f"systemctl {action} returned {proc.returncode} [{proc}]")
|
34
|
+
return proc.returncode == 0
|
35
|
+
|
36
|
+
|
37
|
+
def _darwin_inhibit():
|
38
|
+
try:
|
39
|
+
proc = subprocess.run(["caffeinate", "-i"])
|
40
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
41
|
+
print("caffeinate is not available on this system.")
|
42
|
+
return False
|
43
|
+
except Exception as e:
|
44
|
+
print(f"An unexpected error occurred: {e}")
|
45
|
+
return False
|
46
|
+
return proc.returncode == 0
|
47
|
+
|
48
|
+
|
49
|
+
def _darwin_release():
|
50
|
+
try:
|
51
|
+
# Use killall to stop caffeinate
|
52
|
+
proc = subprocess.run(["killall", "caffeinate"])
|
53
|
+
except Exception as e:
|
54
|
+
print(f"An unexpected error occurred: {e}")
|
55
|
+
return False
|
56
|
+
return proc.returncode == 0
|
57
|
+
|
58
|
+
|
59
|
+
def _linux_inhibit():
|
60
|
+
return _run_systemctl("mask")
|
61
|
+
|
62
|
+
|
63
|
+
def _linux_release():
|
64
|
+
return _run_systemctl("unmask")
|
65
|
+
|
66
|
+
|
67
|
+
def _windows_inhibit():
|
68
|
+
try:
|
69
|
+
# Set the thread execution state to prevent sleep
|
70
|
+
ctypes.windll.kernel32.SetThreadExecutionState(
|
71
|
+
_ES_CONTINUOUS | _ES_SYSTEM_REQUIRED
|
72
|
+
)
|
73
|
+
except Exception as e:
|
74
|
+
print(f"An error occurred while setting thread execution state: {e}")
|
75
|
+
return False
|
76
|
+
return True
|
77
|
+
|
78
|
+
|
79
|
+
def _windows_release():
|
80
|
+
try:
|
81
|
+
# Reset the thread execution state to allow sleep
|
82
|
+
ctypes.windll.kernel32.SetThreadExecutionState(_ES_CONTINUOUS)
|
83
|
+
except Exception as e:
|
84
|
+
print(f"An error occurred while resetting thread execution state: {e}")
|
85
|
+
return False
|
86
|
+
return True
|
87
|
+
|
88
|
+
|
89
|
+
# Darwin does not have a systemctl, but uses caffeinate
|
90
|
+
# Linux uses systemctl to mask/unmask sleep targets
|
91
|
+
# Windows uses SetThreadExecutionState to prevent sleep
|
92
|
+
# NB: Darwin does not to work relaibly on my testsystem, so has been disabled
|
93
|
+
_ACTIONS = {
|
94
|
+
# "Darwin": {"inhibit": _darwin_inhibit, "release": _darwin_release},
|
95
|
+
"Linux": {"inhibit": _linux_inhibit, "release": _linux_release},
|
96
|
+
"Windows": {"inhibit": _windows_inhibit, "release": _windows_release},
|
97
|
+
}
|
98
|
+
|
99
|
+
|
100
|
+
class Inhibitor:
|
101
|
+
def __init__(self):
|
102
|
+
self._os = platform.system()
|
103
|
+
self.active = False
|
104
|
+
self._actions = _ACTIONS.get(self._os)
|
105
|
+
|
106
|
+
@property
|
107
|
+
def available(self) -> bool:
|
108
|
+
return self._actions is not None
|
109
|
+
|
110
|
+
def inhibit(self):
|
111
|
+
if not self.available or self.active:
|
112
|
+
return
|
113
|
+
if self._actions["inhibit"]():
|
114
|
+
self.active = True
|
115
|
+
|
116
|
+
def release(self):
|
117
|
+
if not self.available or not self.active:
|
118
|
+
return
|
119
|
+
if self._actions["release"]():
|
120
|
+
self.active = False
|
meerk40t/kernel/kernel.py
CHANGED
@@ -19,6 +19,7 @@ from .functions import (
|
|
19
19
|
console_option,
|
20
20
|
get_safe_path,
|
21
21
|
)
|
22
|
+
from .inhibitor import Inhibitor
|
22
23
|
from .jobs import ConsoleFunction, Job
|
23
24
|
from .lifecycles import *
|
24
25
|
from .module import Module
|
@@ -183,6 +184,7 @@ class Kernel(Settings):
|
|
183
184
|
self.os_information = self._get_environment()
|
184
185
|
self.show_aio_prompt = True
|
185
186
|
self.silent_mode = False
|
187
|
+
self.inhibitor = Inhibitor()
|
186
188
|
|
187
189
|
def __str__(self):
|
188
190
|
return f"Kernel({self.name}, {self.profile}, {self.version})"
|
@@ -2855,6 +2857,42 @@ class Kernel(Settings):
|
|
2855
2857
|
channel(_("Syntax Error: timer<name> <times> <interval> <command>"))
|
2856
2858
|
return
|
2857
2859
|
|
2860
|
+
# ==========
|
2861
|
+
# Sleep Commands
|
2862
|
+
# ==========
|
2863
|
+
@self.console_argument(
|
2864
|
+
"mode", type=str, help=_("Mode to set: prevent/allow"), default=None
|
2865
|
+
)
|
2866
|
+
@self.console_command(
|
2867
|
+
"system_hibernate", _("Prevent/allow system hibernation.")
|
2868
|
+
)
|
2869
|
+
def system_hibernate(channel, _, mode=None, **kwargs):
|
2870
|
+
"""
|
2871
|
+
Prevent or allow system hibernation. This is a toggle command.
|
2872
|
+
If no mode is specified, it will print the current state.
|
2873
|
+
"""
|
2874
|
+
if not self.inhibitor.available:
|
2875
|
+
channel(_("Inhibitor is not available on this system."))
|
2876
|
+
return
|
2877
|
+
if mode is not None:
|
2878
|
+
if mode.lower() not in ("prevent", "allow"):
|
2879
|
+
channel(_("Please specify 'prevent' or 'allow'."))
|
2880
|
+
return
|
2881
|
+
if mode.lower() == "prevent":
|
2882
|
+
self.inhibitor.inhibit()
|
2883
|
+
else:
|
2884
|
+
self.inhibitor.release()
|
2885
|
+
sudo_msg = _("You might need system administrator priviliges.")
|
2886
|
+
if self.inhibitor.active:
|
2887
|
+
channel(_("System hibernation is prevented."))
|
2888
|
+
else:
|
2889
|
+
channel(_("System hibernation is allowed."))
|
2890
|
+
if self.os_information["OS_NAME"] == "Linux" and (
|
2891
|
+
(mode == "prevent" and not self.inhibitor.active)
|
2892
|
+
or (mode == "allow" and self.inhibitor.active)
|
2893
|
+
):
|
2894
|
+
channel(sudo_msg)
|
2895
|
+
|
2858
2896
|
# ==========
|
2859
2897
|
# CORE OBJECTS COMMANDS
|
2860
2898
|
# ==========
|
meerk40t/lihuiyu/controller.py
CHANGED
@@ -124,10 +124,21 @@ def onewire_crc_lookup(line):
|
|
124
124
|
@param line: line to be CRC'd
|
125
125
|
@return: 8 bit crc of line.
|
126
126
|
"""
|
127
|
+
|
127
128
|
crc = 0
|
128
129
|
for i in range(0, 30):
|
129
130
|
crc = line[i] ^ crc
|
130
131
|
crc = crc_table[crc & 0x0F] ^ crc_table[16 + ((crc >> 4) & 0x0F)]
|
132
|
+
|
133
|
+
""" Print the line in hex and ascii format for debugging purposes.
|
134
|
+
def hex_repr(data):
|
135
|
+
return " ".join(f"{x:02x}" for x in data)
|
136
|
+
|
137
|
+
def ascii_repr(data):
|
138
|
+
return "".join(chr(x) if 32 <= x < 127 else "." for x in data)
|
139
|
+
|
140
|
+
print (f"Line ({len(line)} bytes): {hex_repr(line)} {ascii_repr(line)} CRC: {hex(crc)}")
|
141
|
+
"""
|
131
142
|
return crc
|
132
143
|
|
133
144
|
|
@@ -618,6 +629,16 @@ class LihuiyuController:
|
|
618
629
|
self._preempt.clear()
|
619
630
|
self.update_buffer()
|
620
631
|
|
632
|
+
def debug_packet(self, packet):
|
633
|
+
"""
|
634
|
+
Debugging function to print the packet in a readable format.
|
635
|
+
We will output both hex and ascii representation of the packet.
|
636
|
+
@param packet: Packet to debug.
|
637
|
+
"""
|
638
|
+
hex_packet = " ".join(f"{b:02x}" for b in packet)
|
639
|
+
ascii_packet = "".join(chr(b) if 32 <= b < 127 else "." for b in packet)
|
640
|
+
print(f"Packet: {hex_packet} | ASCII: {ascii_packet} (len={len(packet)})")
|
641
|
+
|
621
642
|
def process_queue(self):
|
622
643
|
"""
|
623
644
|
Attempts to process the buffer/queue
|
@@ -662,6 +683,13 @@ class LihuiyuController:
|
|
662
683
|
post_send_command = None
|
663
684
|
default_checksum = True
|
664
685
|
|
686
|
+
if packet.startswith(b"AT"):
|
687
|
+
# This is as special case for the M3 only:
|
688
|
+
# AT command packages are padded with 0x00 and not 'F' as usal
|
689
|
+
if packet.endswith(b"\n"):
|
690
|
+
packet = packet[:-1]
|
691
|
+
c = b"\x00"
|
692
|
+
packet += c * (30 - len(packet)) # Padding with 0 character
|
665
693
|
# find pipe commands.
|
666
694
|
if packet.endswith(b"\n"):
|
667
695
|
packet = packet[:-1]
|
@@ -691,7 +719,7 @@ class LihuiyuController:
|
|
691
719
|
self.update_state("terminate")
|
692
720
|
self.is_shutdown = True
|
693
721
|
packet = packet[:-1]
|
694
|
-
if packet.startswith(b"A"):
|
722
|
+
if packet.startswith(b"A") and not packet.startswith(b"AT"):
|
695
723
|
# This is a challenge code. A is only used for serial challenges.
|
696
724
|
post_send_command = self._confirm_serial
|
697
725
|
if len(packet) != 0:
|
@@ -703,13 +731,14 @@ class LihuiyuController:
|
|
703
731
|
c = b"F" # Packet was simply #. We can do nothing.
|
704
732
|
packet += bytes([c]) * (30 - len(packet)) # Padding. '\n'
|
705
733
|
else:
|
706
|
-
|
734
|
+
padder = b"\x00" if packet.startswith(b"AT") else b"F"
|
735
|
+
packet += padder * (30 - len(packet)) # Padding. '\n'
|
707
736
|
if not realtime and self.state in ("pause", "busy"):
|
708
737
|
return False # Processing normal queue, PAUSE and BUSY apply.
|
709
738
|
|
710
739
|
# Packet is prepared and ready to send. Open Channel.
|
711
740
|
self.open()
|
712
|
-
|
741
|
+
# print (f"Packet: {packet!r} (len={len(packet)})" )
|
713
742
|
if len(packet) == 30:
|
714
743
|
# We have a sendable packet.
|
715
744
|
if not self.pre_ok:
|
@@ -718,6 +747,7 @@ class LihuiyuController:
|
|
718
747
|
packet = b"\x00" + packet + bytes([onewire_crc_lookup(packet)])
|
719
748
|
else:
|
720
749
|
packet = b"\x00" + packet + bytes([onewire_crc_lookup(packet) ^ 0xFF])
|
750
|
+
# self.debug_packet(packet)
|
721
751
|
self.connection.write(packet)
|
722
752
|
self.pre_ok = False
|
723
753
|
|
meerk40t/lihuiyu/device.py
CHANGED
@@ -129,6 +129,13 @@ class LihuiyuDevice(Service, Status):
|
|
129
129
|
]
|
130
130
|
self.register_choices("bed_dim", choices)
|
131
131
|
|
132
|
+
def get_max_range():
|
133
|
+
"""
|
134
|
+
Returns the maximum range of the device.
|
135
|
+
"""
|
136
|
+
dev_mode = getattr(self.kernel.root, "developer_mode", False)
|
137
|
+
return 100 if dev_mode else 50
|
138
|
+
|
132
139
|
choices = [
|
133
140
|
{
|
134
141
|
"attr": "label",
|
@@ -153,6 +160,52 @@ class LihuiyuDevice(Service, Status):
|
|
153
160
|
"section": "_10_" + _("Configuration"),
|
154
161
|
"subsection": _("Board Setup"),
|
155
162
|
},
|
163
|
+
{
|
164
|
+
"attr": "supports_pwm",
|
165
|
+
"object": self,
|
166
|
+
"default": False,
|
167
|
+
"type": bool,
|
168
|
+
"label": _("Hardware-PWM"),
|
169
|
+
"tip": _(
|
170
|
+
"Does the board support Hardware-PWM. Only M3 and fireware >= 2024.01.18g support PWM. Earlier M3 revisions are just M2+."
|
171
|
+
),
|
172
|
+
"section": "_10_" + _("Configuration"),
|
173
|
+
"subsection": _("Hardware-Laser-Power"),
|
174
|
+
"conditional": (self, "board", "M3"),
|
175
|
+
"signals": "pwm_mode_changed",
|
176
|
+
},
|
177
|
+
{
|
178
|
+
"attr": "pwm_speedcode",
|
179
|
+
"object": self,
|
180
|
+
"default": False,
|
181
|
+
"type": bool,
|
182
|
+
"label": _("Use PWM-Speedcode"),
|
183
|
+
"tip": _("PWM Method: set power as well in LHY-speedcodes."),
|
184
|
+
"section": "_10_" + _("Configuration"),
|
185
|
+
"subsection": _("Hardware-Laser-Power"),
|
186
|
+
"conditional": (self, "supports_pwm"),
|
187
|
+
"hidden": True,
|
188
|
+
},
|
189
|
+
{
|
190
|
+
"attr": "power_scale",
|
191
|
+
"object": self,
|
192
|
+
"default": 30,
|
193
|
+
"type": int,
|
194
|
+
"style": "slider",
|
195
|
+
"min": 1,
|
196
|
+
"max": get_max_range,
|
197
|
+
"label": _("Maximum Laser strength"),
|
198
|
+
"trailer": "%",
|
199
|
+
"tip": _(
|
200
|
+
"Set the maximum laser power level, any operation power will be a fraction of this"
|
201
|
+
)
|
202
|
+
+ "\n"
|
203
|
+
+ _("Setting this too high may cause damage to your laser tube!"),
|
204
|
+
"section": "_10_" + _("Configuration"),
|
205
|
+
"subsection": _("Hardware-Laser-Power"),
|
206
|
+
"conditional": (self, "supports_pwm"),
|
207
|
+
"signals": "pwm_mode_changed",
|
208
|
+
},
|
156
209
|
{
|
157
210
|
"attr": "flip_x",
|
158
211
|
"object": self,
|
@@ -547,12 +600,15 @@ class LihuiyuDevice(Service, Status):
|
|
547
600
|
action="store_true",
|
548
601
|
help=_("override one second laser fire pulse duration"),
|
549
602
|
)
|
603
|
+
@self.console_option("power", "p", type=str, help=_("Power level"))
|
550
604
|
@self.console_argument("time", type=float, help=_("laser fire pulse duration"))
|
551
605
|
@self.console_command(
|
552
606
|
"pulse",
|
553
607
|
help=_("pulse <time>: Pulse the laser in place."),
|
554
608
|
)
|
555
|
-
def pulse(
|
609
|
+
def pulse(
|
610
|
+
command, channel, _, time=None, power=None, idonotlovemyhouse=False, **kwgs
|
611
|
+
):
|
556
612
|
if time is None:
|
557
613
|
channel(_("Must specify a pulse time in milliseconds."))
|
558
614
|
return
|
@@ -568,16 +624,33 @@ class LihuiyuDevice(Service, Status):
|
|
568
624
|
except IndexError:
|
569
625
|
return
|
570
626
|
|
571
|
-
def timed_fire():
|
627
|
+
def timed_fire(withpower=None):
|
572
628
|
yield "wait_finish"
|
573
|
-
|
629
|
+
if withpower is not None:
|
630
|
+
yield "laser_on", withpower
|
631
|
+
else:
|
632
|
+
yield "laser_on"
|
574
633
|
yield "wait", time
|
575
634
|
yield "laser_off"
|
576
635
|
|
636
|
+
if power is not None:
|
637
|
+
try:
|
638
|
+
if power.endswith("%"):
|
639
|
+
power = power[:-1]
|
640
|
+
power = float(power) * 10
|
641
|
+
else:
|
642
|
+
power = float(power)
|
643
|
+
except ValueError:
|
644
|
+
channel(_("Power must be valid value."))
|
645
|
+
return
|
646
|
+
if not (0 <= power <= 1000):
|
647
|
+
channel(_("Power must be between 0 and 1000."))
|
648
|
+
return
|
649
|
+
|
577
650
|
if self.spooler.is_idle:
|
578
651
|
label = _("Pulse laser for {time}ms").format(time=time)
|
579
652
|
self.spooler.laserjob(
|
580
|
-
list(timed_fire()),
|
653
|
+
list(timed_fire(withpower=power)),
|
581
654
|
label=label,
|
582
655
|
helper=True,
|
583
656
|
outline=[self.native] * 4,
|
@@ -892,6 +965,27 @@ class LihuiyuDevice(Service, Status):
|
|
892
965
|
code = b"A%s\n" % challenge
|
893
966
|
self.output.write(code)
|
894
967
|
|
968
|
+
def _validate_board(channel, board):
|
969
|
+
"""
|
970
|
+
Validates the board type
|
971
|
+
"""
|
972
|
+
if self.board != board:
|
973
|
+
channel(
|
974
|
+
_(
|
975
|
+
"This command is only available for {target} boards. This is a {board}."
|
976
|
+
).format(target=board, board=self.board)
|
977
|
+
)
|
978
|
+
return False
|
979
|
+
return True
|
980
|
+
|
981
|
+
@self.console_command(
|
982
|
+
"get_m3nano_info",
|
983
|
+
help=_("Request M3Nano+ board info"),
|
984
|
+
)
|
985
|
+
def get_m3nano_info(command, channel, _, remainder=None, **kwgs):
|
986
|
+
if _validate_board(channel, "M3"):
|
987
|
+
self.driver.get_m3_hardware_info()
|
988
|
+
|
895
989
|
@self.console_command("start", help=_("Start Pipe to Controller"))
|
896
990
|
def pipe_start(command, channel, _, **kwgs):
|
897
991
|
self.controller.update_state("active")
|
@@ -1050,6 +1144,7 @@ class LihuiyuDevice(Service, Status):
|
|
1050
1144
|
@signal_listener("plot_shift")
|
1051
1145
|
@signal_listener("plot_phase_type")
|
1052
1146
|
@signal_listener("plot_phase_value")
|
1147
|
+
@signal_listener("supports_pwm")
|
1053
1148
|
def plot_attributes_update(self, origin=None, *args):
|
1054
1149
|
self.driver.plot_attribute_update()
|
1055
1150
|
|
meerk40t/lihuiyu/driver.py
CHANGED
@@ -194,6 +194,18 @@ class LihuiyuDriver(Parameters):
|
|
194
194
|
def __call__(self, e):
|
195
195
|
self.out_pipe.write(e)
|
196
196
|
|
197
|
+
@property
|
198
|
+
def has_adjustable_maximum_power(self):
|
199
|
+
return self.service.supports_pwm
|
200
|
+
|
201
|
+
@property
|
202
|
+
def max_power_scale(self):
|
203
|
+
return self.service.power_scale
|
204
|
+
|
205
|
+
@max_power_scale.setter
|
206
|
+
def max_power_scale(self, value):
|
207
|
+
self.service.power_scale = value
|
208
|
+
|
197
209
|
def get_internal_queue_status(self):
|
198
210
|
return self._queue_current, self._queue_total
|
199
211
|
|
@@ -205,6 +217,7 @@ class LihuiyuDriver(Parameters):
|
|
205
217
|
self.plot_planner.force_shift = self.service.plot_shift
|
206
218
|
self.plot_planner.phase_type = self.service.plot_phase_type
|
207
219
|
self.plot_planner.phase_value = self.service.plot_phase_value
|
220
|
+
self.plot_planner.set_ppi(not self.service.supports_pwm)
|
208
221
|
|
209
222
|
def hold_work(self, priority):
|
210
223
|
"""
|
@@ -384,7 +397,7 @@ class LihuiyuDriver(Parameters):
|
|
384
397
|
self.rapid_mode()
|
385
398
|
self._move_relative(unit_dx, unit_dy)
|
386
399
|
|
387
|
-
def dwell(self, time_in_ms):
|
400
|
+
def dwell(self, time_in_ms, settings=None):
|
388
401
|
"""
|
389
402
|
Requests that the laser fire in place for the given time period. This could be done in a series of commands,
|
390
403
|
move to a location, turn laser on, wait, turn laser off. However, some drivers have specific laser-in-place
|
@@ -393,9 +406,12 @@ class LihuiyuDriver(Parameters):
|
|
393
406
|
@param time_in_ms:
|
394
407
|
@return:
|
395
408
|
"""
|
409
|
+
power = settings.get("power", None) if settings else None
|
396
410
|
self.rapid_mode()
|
397
411
|
self.wait_finish()
|
398
|
-
self.laser_on(
|
412
|
+
self.laser_on(
|
413
|
+
power=power
|
414
|
+
) # This can't be sent early since these are timed operations.
|
399
415
|
self.wait(time_in_ms)
|
400
416
|
self.laser_off()
|
401
417
|
|
@@ -421,7 +437,7 @@ class LihuiyuDriver(Parameters):
|
|
421
437
|
self.laser = False
|
422
438
|
return True
|
423
439
|
|
424
|
-
def laser_on(self):
|
440
|
+
def laser_on(self, power=None):
|
425
441
|
"""
|
426
442
|
Turn laser on in place.
|
427
443
|
|
@@ -429,6 +445,9 @@ class LihuiyuDriver(Parameters):
|
|
429
445
|
"""
|
430
446
|
if self.laser:
|
431
447
|
return False
|
448
|
+
if power is not None and self.service.supports_pwm:
|
449
|
+
self.send_at_pwm_code(power, "laser_on")
|
450
|
+
|
432
451
|
if self.state == DRIVER_STATE_RAPID:
|
433
452
|
self(b"I")
|
434
453
|
self(self.CODE_LASER_ON)
|
@@ -443,6 +462,18 @@ class LihuiyuDriver(Parameters):
|
|
443
462
|
self.laser = True
|
444
463
|
return True
|
445
464
|
|
465
|
+
def send_at_pwm_code(self, power: float = 1000.0):
|
466
|
+
if len(self.out_pipe) > 0:
|
467
|
+
self(b"\n")
|
468
|
+
self.wait_finish()
|
469
|
+
self.rapid_mode()
|
470
|
+
power = max(0.0, min(power, 1000.0)) * self.service.power_scale / 100.0
|
471
|
+
m = int(power / 254)
|
472
|
+
n = int(power % 254)
|
473
|
+
# AT commands will be flushed out immediately
|
474
|
+
packet = bytes((ord("A"), ord("T"), ord("1"), m, n, ord("\n")))
|
475
|
+
self(packet)
|
476
|
+
|
446
477
|
def rapid_mode(self, *values):
|
447
478
|
"""
|
448
479
|
Rapid mode sets the laser to rapid state. This is usually moving the laser around without it executing a large
|
@@ -537,6 +568,8 @@ class LihuiyuDriver(Parameters):
|
|
537
568
|
else:
|
538
569
|
# Unidirectional (step on forward swing - rasters only going forward)
|
539
570
|
raster_step_value = self._raster_step_g_value, 0
|
571
|
+
# We don't allow in situ power changes in raster mode.
|
572
|
+
power_val = None
|
540
573
|
speed_code = LaserSpeed(
|
541
574
|
self.service.board,
|
542
575
|
self.speed,
|
@@ -548,6 +581,7 @@ class LihuiyuDriver(Parameters):
|
|
548
581
|
suffix_c=False,
|
549
582
|
fix_speeds=self.service.fix_speeds,
|
550
583
|
raster_horizontal=horizontal,
|
584
|
+
power_value=power_val,
|
551
585
|
).speedcode
|
552
586
|
speed_code = bytes(speed_code, "utf8")
|
553
587
|
self(speed_code)
|
@@ -593,7 +627,12 @@ class LihuiyuDriver(Parameters):
|
|
593
627
|
self._leftward = False
|
594
628
|
self._topward = False
|
595
629
|
self._horizontal_major = False
|
596
|
-
|
630
|
+
if self.service.supports_pwm and self.service.pwm_speedcode:
|
631
|
+
# If we are using PWM speedcode, we need to set the power value.
|
632
|
+
power_val = self.power * self.service.power_scale / 100.0
|
633
|
+
else:
|
634
|
+
# If we are not using PWM speedcode, we do not set the power value.
|
635
|
+
power_val = None
|
597
636
|
speed_code = LaserSpeed(
|
598
637
|
self.service.board,
|
599
638
|
self.speed,
|
@@ -605,6 +644,7 @@ class LihuiyuDriver(Parameters):
|
|
605
644
|
suffix_c=suffix_c,
|
606
645
|
fix_speeds=self.service.fix_speeds,
|
607
646
|
raster_horizontal=self._horizontal_major,
|
647
|
+
power_value=power_val,
|
608
648
|
).speedcode
|
609
649
|
speed_code = bytes(speed_code, "utf8")
|
610
650
|
self(speed_code)
|
@@ -702,6 +742,7 @@ class LihuiyuDriver(Parameters):
|
|
702
742
|
self.rapid_mode()
|
703
743
|
self._move_absolute(start.real, start.imag)
|
704
744
|
self.wait_finish()
|
745
|
+
self._set_power(sets.get("power", 1000.0))
|
705
746
|
self.dwell(sets.get("dwell_time"))
|
706
747
|
elif function == "wait":
|
707
748
|
self.plot_start()
|
@@ -1029,6 +1070,8 @@ class LihuiyuDriver(Parameters):
|
|
1029
1070
|
self.power = 1000.0
|
1030
1071
|
if self.power <= 0:
|
1031
1072
|
self.power = 0.0
|
1073
|
+
if self.service.supports_pwm:
|
1074
|
+
self.send_at_pwm_code(self.power)
|
1032
1075
|
|
1033
1076
|
def _set_ppi(self, power=1000.0):
|
1034
1077
|
self.power = power
|
@@ -1463,4 +1506,18 @@ class LihuiyuDriver(Parameters):
|
|
1463
1506
|
self._x_engaged = False
|
1464
1507
|
self._y_engaged = True
|
1465
1508
|
return x_dir + y_dir
|
1466
|
-
|
1509
|
+
|
1510
|
+
# def get_board_info(self):
|
1511
|
+
# try:
|
1512
|
+
# self.out_pipe.write_raw(b"\xac\x2e", 73 - 27)
|
1513
|
+
# except AttributeError:
|
1514
|
+
# pass
|
1515
|
+
|
1516
|
+
# def get_param_info(self):
|
1517
|
+
# try:
|
1518
|
+
# self.out_pipe.write_raw(b"\xac\xe0", 251 - 27)
|
1519
|
+
# except AttributeError:
|
1520
|
+
# pass
|
1521
|
+
|
1522
|
+
def get_m3_hardware_info(self):
|
1523
|
+
self(b"AT01")
|