meerk40t 0.9.3001__py2.py3-none-any.whl → 0.9.7020__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 (446) hide show
  1. meerk40t/__init__.py +1 -1
  2. meerk40t/balormk/balor_params.py +167 -167
  3. meerk40t/balormk/clone_loader.py +457 -457
  4. meerk40t/balormk/controller.py +1566 -1512
  5. meerk40t/balormk/cylindermod.py +64 -0
  6. meerk40t/balormk/device.py +966 -1959
  7. meerk40t/balormk/driver.py +778 -591
  8. meerk40t/balormk/galvo_commands.py +1194 -0
  9. meerk40t/balormk/gui/balorconfig.py +237 -111
  10. meerk40t/balormk/gui/balorcontroller.py +191 -184
  11. meerk40t/balormk/gui/baloroperationproperties.py +116 -115
  12. meerk40t/balormk/gui/corscene.py +845 -0
  13. meerk40t/balormk/gui/gui.py +179 -147
  14. meerk40t/balormk/livelightjob.py +466 -382
  15. meerk40t/balormk/mock_connection.py +131 -109
  16. meerk40t/balormk/plugin.py +133 -135
  17. meerk40t/balormk/usb_connection.py +306 -301
  18. meerk40t/camera/__init__.py +1 -1
  19. meerk40t/camera/camera.py +514 -397
  20. meerk40t/camera/gui/camerapanel.py +1241 -1095
  21. meerk40t/camera/gui/gui.py +58 -58
  22. meerk40t/camera/plugin.py +441 -399
  23. meerk40t/ch341/__init__.py +27 -27
  24. meerk40t/ch341/ch341device.py +628 -628
  25. meerk40t/ch341/libusb.py +595 -589
  26. meerk40t/ch341/mock.py +171 -171
  27. meerk40t/ch341/windriver.py +157 -157
  28. meerk40t/constants.py +13 -0
  29. meerk40t/core/__init__.py +1 -1
  30. meerk40t/core/bindalias.py +550 -539
  31. meerk40t/core/core.py +47 -47
  32. meerk40t/core/cutcode/cubiccut.py +73 -73
  33. meerk40t/core/cutcode/cutcode.py +315 -312
  34. meerk40t/core/cutcode/cutgroup.py +141 -137
  35. meerk40t/core/cutcode/cutobject.py +192 -185
  36. meerk40t/core/cutcode/dwellcut.py +37 -37
  37. meerk40t/core/cutcode/gotocut.py +29 -29
  38. meerk40t/core/cutcode/homecut.py +29 -29
  39. meerk40t/core/cutcode/inputcut.py +34 -34
  40. meerk40t/core/cutcode/linecut.py +33 -33
  41. meerk40t/core/cutcode/outputcut.py +34 -34
  42. meerk40t/core/cutcode/plotcut.py +335 -335
  43. meerk40t/core/cutcode/quadcut.py +61 -61
  44. meerk40t/core/cutcode/rastercut.py +168 -148
  45. meerk40t/core/cutcode/waitcut.py +34 -34
  46. meerk40t/core/cutplan.py +1843 -1316
  47. meerk40t/core/drivers.py +330 -329
  48. meerk40t/core/elements/align.py +801 -669
  49. meerk40t/core/elements/branches.py +1858 -1507
  50. meerk40t/core/elements/clipboard.py +229 -219
  51. meerk40t/core/elements/element_treeops.py +4595 -2837
  52. meerk40t/core/elements/element_types.py +125 -105
  53. meerk40t/core/elements/elements.py +4315 -3617
  54. meerk40t/core/elements/files.py +117 -64
  55. meerk40t/core/elements/geometry.py +473 -224
  56. meerk40t/core/elements/grid.py +467 -316
  57. meerk40t/core/elements/materials.py +158 -94
  58. meerk40t/core/elements/notes.py +50 -38
  59. meerk40t/core/elements/offset_clpr.py +934 -912
  60. meerk40t/core/elements/offset_mk.py +963 -955
  61. meerk40t/core/elements/penbox.py +339 -267
  62. meerk40t/core/elements/placements.py +300 -83
  63. meerk40t/core/elements/render.py +785 -687
  64. meerk40t/core/elements/shapes.py +2618 -2092
  65. meerk40t/core/elements/testcases.py +105 -0
  66. meerk40t/core/elements/trace.py +651 -563
  67. meerk40t/core/elements/tree_commands.py +415 -409
  68. meerk40t/core/elements/undo_redo.py +116 -58
  69. meerk40t/core/elements/wordlist.py +319 -200
  70. meerk40t/core/exceptions.py +9 -9
  71. meerk40t/core/laserjob.py +220 -220
  72. meerk40t/core/logging.py +63 -63
  73. meerk40t/core/node/blobnode.py +83 -86
  74. meerk40t/core/node/bootstrap.py +105 -103
  75. meerk40t/core/node/branch_elems.py +40 -31
  76. meerk40t/core/node/branch_ops.py +45 -38
  77. meerk40t/core/node/branch_regmark.py +48 -41
  78. meerk40t/core/node/cutnode.py +29 -32
  79. meerk40t/core/node/effect_hatch.py +375 -257
  80. meerk40t/core/node/effect_warp.py +398 -0
  81. meerk40t/core/node/effect_wobble.py +441 -309
  82. meerk40t/core/node/elem_ellipse.py +404 -309
  83. meerk40t/core/node/elem_image.py +1082 -801
  84. meerk40t/core/node/elem_line.py +358 -292
  85. meerk40t/core/node/elem_path.py +259 -201
  86. meerk40t/core/node/elem_point.py +129 -102
  87. meerk40t/core/node/elem_polyline.py +310 -246
  88. meerk40t/core/node/elem_rect.py +376 -286
  89. meerk40t/core/node/elem_text.py +445 -418
  90. meerk40t/core/node/filenode.py +59 -40
  91. meerk40t/core/node/groupnode.py +138 -74
  92. meerk40t/core/node/image_processed.py +777 -766
  93. meerk40t/core/node/image_raster.py +156 -113
  94. meerk40t/core/node/layernode.py +31 -31
  95. meerk40t/core/node/mixins.py +135 -107
  96. meerk40t/core/node/node.py +1427 -1304
  97. meerk40t/core/node/nutils.py +117 -114
  98. meerk40t/core/node/op_cut.py +463 -335
  99. meerk40t/core/node/op_dots.py +296 -251
  100. meerk40t/core/node/op_engrave.py +414 -311
  101. meerk40t/core/node/op_image.py +755 -369
  102. meerk40t/core/node/op_raster.py +787 -522
  103. meerk40t/core/node/place_current.py +37 -40
  104. meerk40t/core/node/place_point.py +329 -126
  105. meerk40t/core/node/refnode.py +58 -47
  106. meerk40t/core/node/rootnode.py +225 -219
  107. meerk40t/core/node/util_console.py +48 -48
  108. meerk40t/core/node/util_goto.py +84 -65
  109. meerk40t/core/node/util_home.py +61 -61
  110. meerk40t/core/node/util_input.py +102 -102
  111. meerk40t/core/node/util_output.py +102 -102
  112. meerk40t/core/node/util_wait.py +65 -65
  113. meerk40t/core/parameters.py +709 -707
  114. meerk40t/core/planner.py +875 -785
  115. meerk40t/core/plotplanner.py +656 -652
  116. meerk40t/core/space.py +120 -113
  117. meerk40t/core/spoolers.py +706 -705
  118. meerk40t/core/svg_io.py +1836 -1549
  119. meerk40t/core/treeop.py +534 -445
  120. meerk40t/core/undos.py +278 -124
  121. meerk40t/core/units.py +784 -680
  122. meerk40t/core/view.py +393 -322
  123. meerk40t/core/webhelp.py +62 -62
  124. meerk40t/core/wordlist.py +513 -504
  125. meerk40t/cylinder/cylinder.py +247 -0
  126. meerk40t/cylinder/gui/cylindersettings.py +41 -0
  127. meerk40t/cylinder/gui/gui.py +24 -0
  128. meerk40t/device/__init__.py +1 -1
  129. meerk40t/device/basedevice.py +322 -123
  130. meerk40t/device/devicechoices.py +50 -0
  131. meerk40t/device/dummydevice.py +163 -128
  132. meerk40t/device/gui/defaultactions.py +618 -602
  133. meerk40t/device/gui/effectspanel.py +114 -0
  134. meerk40t/device/gui/formatterpanel.py +253 -290
  135. meerk40t/device/gui/warningpanel.py +337 -260
  136. meerk40t/device/mixins.py +13 -13
  137. meerk40t/dxf/__init__.py +1 -1
  138. meerk40t/dxf/dxf_io.py +766 -554
  139. meerk40t/dxf/plugin.py +47 -35
  140. meerk40t/external_plugins.py +79 -79
  141. meerk40t/external_plugins_build.py +28 -28
  142. meerk40t/extra/cag.py +112 -116
  143. meerk40t/extra/coolant.py +403 -0
  144. meerk40t/extra/encode_detect.py +204 -0
  145. meerk40t/extra/ezd.py +1165 -1165
  146. meerk40t/extra/hershey.py +834 -340
  147. meerk40t/extra/imageactions.py +322 -316
  148. meerk40t/extra/inkscape.py +628 -622
  149. meerk40t/extra/lbrn.py +424 -424
  150. meerk40t/extra/outerworld.py +283 -0
  151. meerk40t/extra/param_functions.py +1542 -1556
  152. meerk40t/extra/potrace.py +257 -253
  153. meerk40t/extra/serial_exchange.py +118 -0
  154. meerk40t/extra/updater.py +602 -453
  155. meerk40t/extra/vectrace.py +147 -146
  156. meerk40t/extra/winsleep.py +83 -83
  157. meerk40t/extra/xcs_reader.py +597 -0
  158. meerk40t/fill/fills.py +781 -335
  159. meerk40t/fill/patternfill.py +1061 -1061
  160. meerk40t/fill/patterns.py +614 -567
  161. meerk40t/grbl/control.py +87 -87
  162. meerk40t/grbl/controller.py +990 -903
  163. meerk40t/grbl/device.py +1084 -768
  164. meerk40t/grbl/driver.py +989 -771
  165. meerk40t/grbl/emulator.py +532 -497
  166. meerk40t/grbl/gcodejob.py +783 -767
  167. meerk40t/grbl/gui/grblconfiguration.py +373 -298
  168. meerk40t/grbl/gui/grblcontroller.py +485 -271
  169. meerk40t/grbl/gui/grblhardwareconfig.py +269 -153
  170. meerk40t/grbl/gui/grbloperationconfig.py +105 -0
  171. meerk40t/grbl/gui/gui.py +147 -116
  172. meerk40t/grbl/interpreter.py +44 -44
  173. meerk40t/grbl/loader.py +22 -22
  174. meerk40t/grbl/mock_connection.py +56 -56
  175. meerk40t/grbl/plugin.py +294 -264
  176. meerk40t/grbl/serial_connection.py +93 -88
  177. meerk40t/grbl/tcp_connection.py +81 -79
  178. meerk40t/grbl/ws_connection.py +112 -0
  179. meerk40t/gui/__init__.py +1 -1
  180. meerk40t/gui/about.py +2042 -296
  181. meerk40t/gui/alignment.py +1644 -1608
  182. meerk40t/gui/autoexec.py +199 -0
  183. meerk40t/gui/basicops.py +791 -670
  184. meerk40t/gui/bufferview.py +77 -71
  185. meerk40t/gui/busy.py +232 -133
  186. meerk40t/gui/choicepropertypanel.py +1662 -1469
  187. meerk40t/gui/consolepanel.py +706 -542
  188. meerk40t/gui/devicepanel.py +687 -581
  189. meerk40t/gui/dialogoptions.py +110 -107
  190. meerk40t/gui/executejob.py +316 -306
  191. meerk40t/gui/fonts.py +90 -90
  192. meerk40t/gui/functionwrapper.py +252 -0
  193. meerk40t/gui/gui_mixins.py +729 -0
  194. meerk40t/gui/guicolors.py +205 -182
  195. meerk40t/gui/help_assets/help_assets.py +218 -201
  196. meerk40t/gui/helper.py +154 -0
  197. meerk40t/gui/hersheymanager.py +1440 -846
  198. meerk40t/gui/icons.py +3422 -2747
  199. meerk40t/gui/imagesplitter.py +555 -508
  200. meerk40t/gui/keymap.py +354 -344
  201. meerk40t/gui/laserpanel.py +897 -806
  202. meerk40t/gui/laserrender.py +1470 -1232
  203. meerk40t/gui/lasertoolpanel.py +805 -793
  204. meerk40t/gui/magnetoptions.py +436 -0
  205. meerk40t/gui/materialmanager.py +2944 -0
  206. meerk40t/gui/materialtest.py +1722 -1694
  207. meerk40t/gui/mkdebug.py +646 -359
  208. meerk40t/gui/mwindow.py +163 -140
  209. meerk40t/gui/navigationpanels.py +2605 -2467
  210. meerk40t/gui/notes.py +143 -142
  211. meerk40t/gui/opassignment.py +414 -410
  212. meerk40t/gui/operation_info.py +310 -299
  213. meerk40t/gui/plugin.py +500 -328
  214. meerk40t/gui/position.py +714 -669
  215. meerk40t/gui/preferences.py +901 -650
  216. meerk40t/gui/propertypanels/attributes.py +1461 -1131
  217. meerk40t/gui/propertypanels/blobproperty.py +117 -114
  218. meerk40t/gui/propertypanels/consoleproperty.py +83 -80
  219. meerk40t/gui/propertypanels/gotoproperty.py +77 -0
  220. meerk40t/gui/propertypanels/groupproperties.py +223 -217
  221. meerk40t/gui/propertypanels/hatchproperty.py +489 -469
  222. meerk40t/gui/propertypanels/imageproperty.py +2244 -1384
  223. meerk40t/gui/propertypanels/inputproperty.py +59 -58
  224. meerk40t/gui/propertypanels/opbranchproperties.py +82 -80
  225. meerk40t/gui/propertypanels/operationpropertymain.py +1890 -1638
  226. meerk40t/gui/propertypanels/outputproperty.py +59 -58
  227. meerk40t/gui/propertypanels/pathproperty.py +389 -380
  228. meerk40t/gui/propertypanels/placementproperty.py +1214 -383
  229. meerk40t/gui/propertypanels/pointproperty.py +140 -136
  230. meerk40t/gui/propertypanels/propertywindow.py +313 -181
  231. meerk40t/gui/propertypanels/rasterwizardpanels.py +996 -912
  232. meerk40t/gui/propertypanels/regbranchproperties.py +76 -0
  233. meerk40t/gui/propertypanels/textproperty.py +770 -755
  234. meerk40t/gui/propertypanels/waitproperty.py +56 -55
  235. meerk40t/gui/propertypanels/warpproperty.py +121 -0
  236. meerk40t/gui/propertypanels/wobbleproperty.py +255 -204
  237. meerk40t/gui/ribbon.py +2471 -2210
  238. meerk40t/gui/scene/scene.py +1100 -1051
  239. meerk40t/gui/scene/sceneconst.py +22 -22
  240. meerk40t/gui/scene/scenepanel.py +439 -349
  241. meerk40t/gui/scene/scenespacewidget.py +365 -365
  242. meerk40t/gui/scene/widget.py +518 -505
  243. meerk40t/gui/scenewidgets/affinemover.py +215 -215
  244. meerk40t/gui/scenewidgets/attractionwidget.py +315 -309
  245. meerk40t/gui/scenewidgets/bedwidget.py +120 -97
  246. meerk40t/gui/scenewidgets/elementswidget.py +137 -107
  247. meerk40t/gui/scenewidgets/gridwidget.py +785 -745
  248. meerk40t/gui/scenewidgets/guidewidget.py +765 -765
  249. meerk40t/gui/scenewidgets/laserpathwidget.py +66 -66
  250. meerk40t/gui/scenewidgets/machineoriginwidget.py +86 -86
  251. meerk40t/gui/scenewidgets/nodeselector.py +28 -28
  252. meerk40t/gui/scenewidgets/rectselectwidget.py +592 -346
  253. meerk40t/gui/scenewidgets/relocatewidget.py +33 -33
  254. meerk40t/gui/scenewidgets/reticlewidget.py +83 -83
  255. meerk40t/gui/scenewidgets/selectionwidget.py +2958 -2756
  256. meerk40t/gui/simpleui.py +362 -333
  257. meerk40t/gui/simulation.py +2451 -2094
  258. meerk40t/gui/snapoptions.py +208 -203
  259. meerk40t/gui/spoolerpanel.py +1227 -1180
  260. meerk40t/gui/statusbarwidgets/defaultoperations.py +480 -353
  261. meerk40t/gui/statusbarwidgets/infowidget.py +520 -483
  262. meerk40t/gui/statusbarwidgets/opassignwidget.py +356 -355
  263. meerk40t/gui/statusbarwidgets/selectionwidget.py +172 -171
  264. meerk40t/gui/statusbarwidgets/shapepropwidget.py +754 -236
  265. meerk40t/gui/statusbarwidgets/statusbar.py +272 -260
  266. meerk40t/gui/statusbarwidgets/statusbarwidget.py +268 -270
  267. meerk40t/gui/statusbarwidgets/strokewidget.py +267 -251
  268. meerk40t/gui/themes.py +200 -78
  269. meerk40t/gui/tips.py +590 -0
  270. meerk40t/gui/toolwidgets/circlebrush.py +35 -35
  271. meerk40t/gui/toolwidgets/toolcircle.py +248 -242
  272. meerk40t/gui/toolwidgets/toolcontainer.py +82 -77
  273. meerk40t/gui/toolwidgets/tooldraw.py +97 -90
  274. meerk40t/gui/toolwidgets/toolellipse.py +219 -212
  275. meerk40t/gui/toolwidgets/toolimagecut.py +25 -132
  276. meerk40t/gui/toolwidgets/toolline.py +39 -144
  277. meerk40t/gui/toolwidgets/toollinetext.py +79 -236
  278. meerk40t/gui/toolwidgets/toollinetext_inline.py +296 -0
  279. meerk40t/gui/toolwidgets/toolmeasure.py +163 -216
  280. meerk40t/gui/toolwidgets/toolnodeedit.py +2088 -2074
  281. meerk40t/gui/toolwidgets/toolnodemove.py +92 -94
  282. meerk40t/gui/toolwidgets/toolparameter.py +754 -668
  283. meerk40t/gui/toolwidgets/toolplacement.py +108 -108
  284. meerk40t/gui/toolwidgets/toolpoint.py +68 -59
  285. meerk40t/gui/toolwidgets/toolpointlistbuilder.py +294 -0
  286. meerk40t/gui/toolwidgets/toolpointmove.py +183 -0
  287. meerk40t/gui/toolwidgets/toolpolygon.py +288 -403
  288. meerk40t/gui/toolwidgets/toolpolyline.py +38 -196
  289. meerk40t/gui/toolwidgets/toolrect.py +211 -207
  290. meerk40t/gui/toolwidgets/toolrelocate.py +72 -72
  291. meerk40t/gui/toolwidgets/toolribbon.py +598 -113
  292. meerk40t/gui/toolwidgets/tooltabedit.py +546 -0
  293. meerk40t/gui/toolwidgets/tooltext.py +98 -89
  294. meerk40t/gui/toolwidgets/toolvector.py +213 -204
  295. meerk40t/gui/toolwidgets/toolwidget.py +39 -39
  296. meerk40t/gui/usbconnect.py +98 -91
  297. meerk40t/gui/utilitywidgets/buttonwidget.py +18 -18
  298. meerk40t/gui/utilitywidgets/checkboxwidget.py +90 -90
  299. meerk40t/gui/utilitywidgets/controlwidget.py +14 -14
  300. meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +343 -340
  301. meerk40t/gui/utilitywidgets/debugwidgets.py +148 -0
  302. meerk40t/gui/utilitywidgets/handlewidget.py +27 -27
  303. meerk40t/gui/utilitywidgets/harmonograph.py +450 -447
  304. meerk40t/gui/utilitywidgets/openclosewidget.py +40 -40
  305. meerk40t/gui/utilitywidgets/rotationwidget.py +54 -54
  306. meerk40t/gui/utilitywidgets/scalewidget.py +75 -75
  307. meerk40t/gui/utilitywidgets/seekbarwidget.py +183 -183
  308. meerk40t/gui/utilitywidgets/togglewidget.py +142 -142
  309. meerk40t/gui/utilitywidgets/toolbarwidget.py +8 -8
  310. meerk40t/gui/wordlisteditor.py +985 -931
  311. meerk40t/gui/wxmeerk40t.py +1447 -1169
  312. meerk40t/gui/wxmmain.py +5644 -4112
  313. meerk40t/gui/wxmribbon.py +1591 -1076
  314. meerk40t/gui/wxmscene.py +1631 -1453
  315. meerk40t/gui/wxmtree.py +2416 -2089
  316. meerk40t/gui/wxutils.py +1769 -1099
  317. meerk40t/gui/zmatrix.py +102 -102
  318. meerk40t/image/__init__.py +1 -1
  319. meerk40t/image/dither.py +429 -0
  320. meerk40t/image/imagetools.py +2793 -2269
  321. meerk40t/internal_plugins.py +150 -130
  322. meerk40t/kernel/__init__.py +63 -12
  323. meerk40t/kernel/channel.py +259 -212
  324. meerk40t/kernel/context.py +538 -538
  325. meerk40t/kernel/exceptions.py +41 -41
  326. meerk40t/kernel/functions.py +463 -414
  327. meerk40t/kernel/jobs.py +100 -100
  328. meerk40t/kernel/kernel.py +3828 -3571
  329. meerk40t/kernel/lifecycles.py +71 -71
  330. meerk40t/kernel/module.py +49 -49
  331. meerk40t/kernel/service.py +147 -147
  332. meerk40t/kernel/settings.py +383 -343
  333. meerk40t/lihuiyu/controller.py +883 -876
  334. meerk40t/lihuiyu/device.py +1181 -1069
  335. meerk40t/lihuiyu/driver.py +1466 -1372
  336. meerk40t/lihuiyu/gui/gui.py +127 -106
  337. meerk40t/lihuiyu/gui/lhyaccelgui.py +377 -363
  338. meerk40t/lihuiyu/gui/lhycontrollergui.py +741 -651
  339. meerk40t/lihuiyu/gui/lhydrivergui.py +470 -446
  340. meerk40t/lihuiyu/gui/lhyoperationproperties.py +238 -237
  341. meerk40t/lihuiyu/gui/tcpcontroller.py +226 -190
  342. meerk40t/lihuiyu/interpreter.py +53 -53
  343. meerk40t/lihuiyu/laserspeed.py +450 -450
  344. meerk40t/lihuiyu/loader.py +90 -90
  345. meerk40t/lihuiyu/parser.py +404 -404
  346. meerk40t/lihuiyu/plugin.py +101 -102
  347. meerk40t/lihuiyu/tcp_connection.py +111 -109
  348. meerk40t/main.py +231 -165
  349. meerk40t/moshi/builder.py +788 -781
  350. meerk40t/moshi/controller.py +505 -499
  351. meerk40t/moshi/device.py +495 -442
  352. meerk40t/moshi/driver.py +862 -696
  353. meerk40t/moshi/gui/gui.py +78 -76
  354. meerk40t/moshi/gui/moshicontrollergui.py +538 -522
  355. meerk40t/moshi/gui/moshidrivergui.py +87 -75
  356. meerk40t/moshi/plugin.py +43 -43
  357. meerk40t/network/console_server.py +140 -57
  358. meerk40t/network/kernelserver.py +10 -9
  359. meerk40t/network/tcp_server.py +142 -140
  360. meerk40t/network/udp_server.py +103 -77
  361. meerk40t/network/web_server.py +404 -0
  362. meerk40t/newly/controller.py +1158 -1144
  363. meerk40t/newly/device.py +874 -732
  364. meerk40t/newly/driver.py +540 -412
  365. meerk40t/newly/gui/gui.py +219 -188
  366. meerk40t/newly/gui/newlyconfig.py +116 -101
  367. meerk40t/newly/gui/newlycontroller.py +193 -186
  368. meerk40t/newly/gui/operationproperties.py +51 -51
  369. meerk40t/newly/mock_connection.py +82 -82
  370. meerk40t/newly/newly_params.py +56 -56
  371. meerk40t/newly/plugin.py +1214 -1246
  372. meerk40t/newly/usb_connection.py +322 -322
  373. meerk40t/rotary/gui/gui.py +52 -46
  374. meerk40t/rotary/gui/rotarysettings.py +240 -232
  375. meerk40t/rotary/rotary.py +202 -98
  376. meerk40t/ruida/control.py +291 -91
  377. meerk40t/ruida/controller.py +138 -1088
  378. meerk40t/ruida/device.py +676 -231
  379. meerk40t/ruida/driver.py +534 -472
  380. meerk40t/ruida/emulator.py +1494 -1491
  381. meerk40t/ruida/exceptions.py +4 -4
  382. meerk40t/ruida/gui/gui.py +71 -76
  383. meerk40t/ruida/gui/ruidaconfig.py +239 -72
  384. meerk40t/ruida/gui/ruidacontroller.py +187 -184
  385. meerk40t/ruida/gui/ruidaoperationproperties.py +48 -47
  386. meerk40t/ruida/loader.py +54 -52
  387. meerk40t/ruida/mock_connection.py +57 -109
  388. meerk40t/ruida/plugin.py +124 -87
  389. meerk40t/ruida/rdjob.py +2084 -945
  390. meerk40t/ruida/serial_connection.py +116 -0
  391. meerk40t/ruida/tcp_connection.py +146 -0
  392. meerk40t/ruida/udp_connection.py +73 -0
  393. meerk40t/svgelements.py +9671 -9669
  394. meerk40t/tools/driver_to_path.py +584 -579
  395. meerk40t/tools/geomstr.py +5583 -4680
  396. meerk40t/tools/jhfparser.py +357 -292
  397. meerk40t/tools/kerftest.py +904 -890
  398. meerk40t/tools/livinghinges.py +1168 -1033
  399. meerk40t/tools/pathtools.py +987 -949
  400. meerk40t/tools/pmatrix.py +234 -0
  401. meerk40t/tools/pointfinder.py +942 -942
  402. meerk40t/tools/polybool.py +941 -940
  403. meerk40t/tools/rasterplotter.py +1660 -547
  404. meerk40t/tools/shxparser.py +1047 -901
  405. meerk40t/tools/ttfparser.py +726 -446
  406. meerk40t/tools/zinglplotter.py +595 -593
  407. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/LICENSE +21 -21
  408. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/METADATA +150 -139
  409. meerk40t-0.9.7020.dist-info/RECORD +446 -0
  410. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/WHEEL +1 -1
  411. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/top_level.txt +0 -1
  412. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/zip-safe +1 -1
  413. meerk40t/balormk/elementlightjob.py +0 -159
  414. meerk40t-0.9.3001.dist-info/RECORD +0 -437
  415. test/bootstrap.py +0 -63
  416. test/test_cli.py +0 -12
  417. test/test_core_cutcode.py +0 -418
  418. test/test_core_elements.py +0 -144
  419. test/test_core_plotplanner.py +0 -397
  420. test/test_core_viewports.py +0 -312
  421. test/test_drivers_grbl.py +0 -108
  422. test/test_drivers_lihuiyu.py +0 -443
  423. test/test_drivers_newly.py +0 -113
  424. test/test_element_degenerate_points.py +0 -43
  425. test/test_elements_classify.py +0 -97
  426. test/test_elements_penbox.py +0 -22
  427. test/test_file_svg.py +0 -176
  428. test/test_fill.py +0 -155
  429. test/test_geomstr.py +0 -1523
  430. test/test_geomstr_nodes.py +0 -18
  431. test/test_imagetools_actualize.py +0 -306
  432. test/test_imagetools_wizard.py +0 -258
  433. test/test_kernel.py +0 -200
  434. test/test_laser_speeds.py +0 -3303
  435. test/test_length.py +0 -57
  436. test/test_lifecycle.py +0 -66
  437. test/test_operations.py +0 -251
  438. test/test_operations_hatch.py +0 -57
  439. test/test_ruida.py +0 -19
  440. test/test_spooler.py +0 -22
  441. test/test_tools_rasterplotter.py +0 -29
  442. test/test_wobble.py +0 -133
  443. test/test_zingl.py +0 -124
  444. {test → meerk40t/cylinder}/__init__.py +0 -0
  445. /meerk40t/{core/element_commands.py → cylinder/gui/__init__.py} +0 -0
  446. {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/entry_points.txt +0 -0
@@ -1,1144 +1,1158 @@
1
- """
2
- Newly Controller
3
- """
4
- import math
5
- import struct
6
- import time
7
-
8
- from meerk40t.core.cutcode.rastercut import RasterCut
9
- from meerk40t.newly.mock_connection import MockConnection
10
- from meerk40t.newly.usb_connection import USBConnection
11
-
12
-
13
- class NewlyController:
14
- """
15
- Newly Controller
16
- """
17
-
18
- def __init__(
19
- self,
20
- service,
21
- x=0,
22
- y=0,
23
- force_mock=False,
24
- ):
25
- self._machine_index = 0
26
- self.service = service
27
- self.force_mock = force_mock
28
- self.is_shutdown = False # Shutdown finished.
29
-
30
- name = self.service.label
31
- self.usb_log = service.channel(f"{name}/usb", buffer_size=500)
32
- self.usb_log.watch(lambda e: service.signal("pipe;usb_status", e))
33
-
34
- # Load Primary Pens
35
- self.sp0 = self.service.setting(int, "sp0", 0)
36
- self.sp1 = self.service.setting(int, "sp1", 1)
37
- self.sp2 = self.service.setting(int, "sp2", 2)
38
-
39
- self.connection = None
40
- self._is_opening = False
41
- self._abort_open = False
42
- self._disable_connect = False
43
-
44
- self._job_x = None
45
- self._job_y = None
46
-
47
- self._last_x = x
48
- self._last_y = y
49
-
50
- self._last_updated_x = x
51
- self._last_updated_y = y
52
-
53
- #######################
54
- # Preset Modes.
55
- #######################
56
-
57
- self._set_pen = None
58
- self._set_cut_dc = None
59
- self._set_move_dc = None
60
- self._set_mode = None
61
- self._set_speed = None
62
- self._set_power = None
63
- self._set_pwm_freq = None
64
- self._set_relative = None
65
- self._set_bit_depth = None
66
- self._set_bit_width = None
67
- self._set_backlash = None
68
- self._set_corner_speed = None
69
- self._set_acceleration_length = None
70
-
71
- #######################
72
- # Current Set Modes.
73
- #######################
74
-
75
- self._pen = None
76
- self._cut_dc = None
77
- self._move_dc = None
78
- self._mode = None
79
- self._speed = None
80
- self._power = None
81
- self._pwm_frequency = None
82
- self._relative = None
83
- self._bit_depth = None
84
- self._bit_width = None
85
- self._backlash = None
86
- self._corner_speed = None
87
- self._acceleration_length = None
88
-
89
- self._realtime = False
90
-
91
- self.mode = "init"
92
- self.paused = False
93
- self._command_buffer = []
94
-
95
- def __call__(self, cmd, *args, **kwargs):
96
- if isinstance(cmd, str):
97
- # Any string data sent is latin-1 encoded.
98
- self._command_buffer.append(cmd.encode("latin-1"))
99
- else:
100
- self._command_buffer.append(cmd)
101
-
102
- def set_disable_connect(self, status):
103
- self._disable_connect = status
104
-
105
- def added(self):
106
- pass
107
-
108
- def service_detach(self):
109
- pass
110
-
111
- def shutdown(self, *args, **kwargs):
112
- self.is_shutdown = True
113
-
114
- @property
115
- def connected(self):
116
- if self.connection is None:
117
- return False
118
- return self.connection.is_open(self._machine_index)
119
-
120
- @property
121
- def is_connecting(self):
122
- if self.connection is None:
123
- return False
124
- return self._is_opening
125
-
126
- def abort_connect(self):
127
- self._abort_open = True
128
- self.usb_log("Connect Attempts Aborted")
129
-
130
- def disconnect(self):
131
- try:
132
- self.connection.close(self._machine_index)
133
- except (ConnectionError, ConnectionRefusedError, AttributeError):
134
- pass
135
- self.connection = None
136
- # Reset error to allow another attempt
137
- self.set_disable_connect(False)
138
-
139
- def connect_if_needed(self):
140
- if self._disable_connect:
141
- # After many failures automatic connects are disabled. We require a manual connection.
142
- self.abort_connect()
143
- self.connection = None
144
- raise ConnectionRefusedError(
145
- "NewlyController was unreachable. Explicit connect required."
146
- )
147
- if self.connection is None:
148
- if self.service.setting(bool, "mock", False) or self.force_mock:
149
- self.connection = MockConnection(self.usb_log)
150
- name = self.service.label
151
- self.connection.send = self.service.channel(f"{name}/send")
152
- self.connection.recv = self.service.channel(f"{name}/recv")
153
- else:
154
- self.connection = USBConnection(self.usb_log)
155
- self._is_opening = True
156
- self._abort_open = False
157
- count = 0
158
- while not self.connection.is_open(self._machine_index):
159
- try:
160
- if self.connection.open(self._machine_index) < 0:
161
- raise ConnectionError
162
- self.init_laser()
163
- except (ConnectionError, ConnectionRefusedError):
164
- time.sleep(0.3)
165
- count += 1
166
- # self.usb_log(f"Error-Routine pass #{count}")
167
- if self.is_shutdown or self._abort_open:
168
- self._is_opening = False
169
- self._abort_open = False
170
- return
171
- if self.connection.is_open(self._machine_index):
172
- self.connection.close(self._machine_index)
173
- if count >= 10:
174
- # We have failed too many times.
175
- self._is_opening = False
176
- self.set_disable_connect(True)
177
- self.usb_log("Could not connect to the controller.")
178
- self.usb_log("Automatic connections disabled.")
179
- raise ConnectionRefusedError("Could not connect to the controller.")
180
- time.sleep(0.3)
181
- continue
182
- self._is_opening = False
183
- self._abort_open = False
184
-
185
- def sync(self):
186
- self._last_updated_x, self._last_updated_y = self._last_x, self._last_y
187
-
188
- def update(self):
189
- last_x, last_y = self.service.view.iposition(
190
- self._last_updated_x, self._last_updated_y
191
- )
192
- x, y = self.service.view.iposition(self._last_x, self._last_y)
193
- self.sync()
194
- self.service.signal("driver;position", (last_x, last_y, x, y))
195
-
196
- #######################
197
- # MODE SHIFTS
198
- #######################
199
-
200
- def realtime_job(self, job=None):
201
- """
202
- Starts a realtime job, which runs on file0
203
- @return:
204
- """
205
- if self.mode != "init":
206
- return
207
- self._realtime = True
208
- self.mode = "realtime"
209
- self(f"ZZZFile0")
210
- self._clear_settings()
211
-
212
- def _clear_settings(self):
213
- self._set_pen = None
214
- self._set_cut_dc = None
215
- self._set_move_dc = None
216
- self._set_mode = None
217
- self._set_speed = None
218
- self._set_power = None
219
- self._set_pwm_freq = None
220
- self._set_relative = None
221
- self._set_bit_depth = None
222
- self._set_bit_width = None
223
- self._set_backlash = None
224
- self._set_corner_speed = None
225
- self._set_acceleration_length = None
226
-
227
- self._pen = None
228
- self._cut_dc = None
229
- self._move_dc = None
230
- self._mode = None
231
- self._speed = None
232
- self._power = None
233
- self._pwm_frequency = None
234
- self._relative = None
235
- self._bit_depth = None
236
- self._bit_width = None
237
- self._backlash = None
238
- self._corner_speed = None
239
- self._acceleration_length = None
240
-
241
- def _set_move_mode(self):
242
- """
243
- Move mode is done for any major movements usually starting out an execution.
244
-
245
- eg.
246
- VP100;VK100;SP2;SP2;VQ15;VJ24;VS10;DA0;
247
- @return:
248
- """
249
- self.mode = "move"
250
- self._set_pen = self.sp2
251
- self._set_cut_dc = self.service.cut_dc
252
- self._set_move_dc = self.service.move_dc
253
- self._set_mode = "move"
254
- self._set_speed = self.service.moving_speed
255
- self._set_power = None
256
- self._set_pwm_freq = None
257
- self._set_relative = True
258
- self._set_bit_depth = None
259
- self._set_bit_width = None
260
- self._set_backlash = None
261
- self._set_corner_speed = None
262
- self._set_acceleration_length = None
263
-
264
- def _set_goto_mode(self):
265
- """
266
- Goto mode is done for minor between movements, where we don't need to set the power to 0, since we will be
267
- using PU; commands.
268
-
269
- eg.
270
- SP2;SP2;VQ15;VJ24;VS10;PR;PU2083,-5494;
271
-
272
- @return:
273
- """
274
- self._set_pen = self.sp2
275
- self._set_mode = "goto"
276
- self._set_speed = self.service.moving_speed
277
- self._set_relative = True
278
-
279
- def _set_frame_mode(self):
280
- """
281
- Frame mode is the standard framing operation settings.
282
- eg.
283
- SP0;VS20;PR;PD9891,0;PD0,-19704;PD-9891,0;PD0,19704;ZED;
284
-
285
- @return:
286
- """
287
-
288
- self._set_cut_dc = self.service.cut_dc
289
- self._set_move_dc = self.service.move_dc
290
- self._set_pen = self.sp0
291
- self._set_power = 0
292
- self._set_relative = True
293
- self._set_mode = "frame"
294
- self._set_speed = self.service.moving_speed
295
- if self.service.pwm_enabled:
296
- self._set_pwm_freq = self.service.pwm_frequency
297
-
298
- def _set_vector_mode(self):
299
- """
300
- Vector mode typically is just the PD commands for a vector.
301
-
302
- eg.
303
- PR;SP1;DA65;VS187;PD0,-2534;PD1099,0;PD0,2534;PD-1099,0;
304
- @return:
305
- """
306
- self._set_pen = self.sp1
307
- self._set_cut_dc = self.service.cut_dc
308
- self._set_move_dc = self.service.move_dc
309
- self._set_mode = "vector"
310
- self._set_speed = self.service.default_cut_speed
311
- self._set_power = self.service.default_cut_power
312
- if self.service.pwm_enabled:
313
- self._set_pwm_freq = self.service.pwm_frequency
314
- self._set_relative = True
315
- self._set_bit_depth = None
316
- self._set_bit_width = None
317
- self._set_backlash = None
318
-
319
- def _set_raster_mode(self):
320
- """
321
- Raster mode is the typical preamble and required settings for running a raster. This usually consists of
322
- YF, YZ, XF, XZ, and small PU commands.
323
-
324
- EG.
325
- BT1;DA77;BC0;BD3;SP0;VQ20;VJ10;VS18;YF...
326
- @return:
327
- """
328
- self._set_pen = self.sp0
329
- self._set_cut_dc = self.service.cut_dc
330
- self._set_move_dc = self.service.move_dc
331
- self._set_mode = "raster"
332
- self._corner_speed = None
333
- self._acceleration_length = None
334
- if self.service.pwm_enabled:
335
- self._set_pwm_freq = self.service.pwm_frequency
336
- self._set_relative = True
337
- self._set_bit_depth = 1
338
- self._set_bit_width = 1
339
- self._set_speed = self.service.default_raster_speed
340
- self._set_power = self.service.default_raster_power
341
-
342
- def _write_frame(self, outline):
343
- self.mode = "frame"
344
- if outline is not None:
345
- x, y = self._last_x, self._last_y
346
- self("DW")
347
- for pt in outline:
348
- self.frame(*pt)
349
- self.frame(*outline[0])
350
- self.goto(x, y) # Return to initial x, y if different than outline.
351
- self._clear_settings()
352
- self("ZED")
353
-
354
- def _execute_job(self):
355
- self.service.laser_status = "active"
356
- self("ZED")
357
- cmd = b";".join(self._command_buffer) + b";"
358
- self.connect_if_needed()
359
- self.connection.write(index=self._machine_index, data=cmd)
360
- self._command_buffer.clear()
361
- self._clear_settings()
362
- self.service.laser_status = "idle"
363
-
364
- def open_job(self, job=None):
365
- """
366
- Opens a job at the declared file_index
367
-
368
- @return:
369
- """
370
-
371
- outline = None
372
- try:
373
- outline = job.outline
374
- except AttributeError:
375
- pass
376
- if outline is not None:
377
- self.set_xy(*outline[0])
378
- self._job_x, self._job_y = outline[0]
379
- self._realtime = False
380
- self._clear_settings()
381
- self(f"ZZZFile{self.service.file_index}")
382
- self._write_frame(outline)
383
- self("GZ")
384
- self._clear_settings()
385
-
386
- def close_job(self, job=None):
387
- """
388
- Closes the file and sends.
389
- @return:
390
- """
391
- if not self._command_buffer:
392
- return
393
- if self.mode in ("realtime", "init"):
394
- # Job contains no instructions.
395
- self.mode = "init"
396
- self._command_buffer.clear()
397
- return
398
- if not self._realtime and self._job_x is not None and self._job_y is not None:
399
- self.goto_rel(self._job_x, self._job_y)
400
-
401
- self._execute_job()
402
- self.mode = "init"
403
- if self.service.autoplay and not self._realtime:
404
- self.replay(self.service.file_index)
405
-
406
- def program_mode(self):
407
- self._set_vector_mode()
408
-
409
- def rapid_mode(self):
410
- pass
411
-
412
- #######################
413
- # Raster Events
414
- #######################
415
-
416
- def scanline(self, bits, right=False, left=False, top=False, bottom=False):
417
- """
418
- Send a scanline movement.
419
-
420
- @param bits: list of bits.
421
- @param right: Moving right?
422
- @param left: Moving left?
423
- @param top: Moving top?
424
- @param bottom: Moving bottom?
425
- @return:
426
- """
427
- self._commit_settings()
428
- cmd = None
429
- if left: # left movement
430
- cmd = bytearray(b"YF")
431
- bits = bits[::-1]
432
- elif right:
433
- cmd = bytearray(b"YZ")
434
- bits = bits[::-1]
435
- elif top:
436
- cmd = bytearray(b"XF")
437
- bits = bits[::-1]
438
- elif bottom:
439
- cmd = bytearray(b"XZ")
440
- bits = bits[::-1]
441
- if cmd is None:
442
- return # 0,0 goes nowhere.
443
- count = len(bits)
444
- byte_length = int(math.ceil(count / 8))
445
- cmd += struct.pack(">i", count)[1:]
446
- binary = "".join([str(b) for b in bits])
447
- cmd += int(binary, 2).to_bytes(byte_length, "little")
448
- self(cmd)
449
- if left:
450
- self._last_x -= count
451
- elif right:
452
- self._last_x += count
453
- elif top:
454
- self._last_y -= count
455
- elif bottom:
456
- self._last_y += count
457
-
458
- def _raster_jog(self, x, y, raster_cut):
459
- self.goto(x, y)
460
- self._set_raster_mode()
461
- self.set_settings(raster_cut.settings)
462
- self._set_mode = "raster"
463
- self._commit_settings()
464
-
465
- def raster(self, raster_cut: RasterCut):
466
- """
467
- Run a raster cut with scanlines and default actions.
468
-
469
- @param raster_cut:
470
- @return:
471
- """
472
-
473
- scanline = []
474
- increasing = True
475
-
476
- def commit_scanline():
477
- if scanline:
478
- # If there is a scanline commit the scanline.
479
- if raster_cut.horizontal:
480
- # Horizontal Raster.
481
- if increasing:
482
- self.scanline(scanline, right=True)
483
- scanline.clear()
484
- else:
485
- self.scanline(scanline, left=True)
486
- scanline.clear()
487
- else:
488
- # Vertical raster.
489
- if increasing:
490
- self.scanline(scanline, bottom=True)
491
- scanline.clear()
492
- else:
493
- self.scanline(scanline, top=True)
494
- scanline.clear()
495
-
496
- self("IN")
497
- self._clear_settings()
498
-
499
- previous_x, previous_y = raster_cut.plot.initial_position_in_scene()
500
-
501
- self._raster_jog(previous_x, previous_y, raster_cut)
502
-
503
- if raster_cut.horizontal:
504
- self.mode = "raster_horizontal"
505
- for x, y, on in raster_cut.plot.plot():
506
- dx = x - previous_x
507
- dy = y - previous_y
508
- if dx < 0 and increasing or dx > 0 and not increasing:
509
- # X direction has switched.
510
- commit_scanline()
511
- increasing = not increasing
512
- if dy != 0:
513
- # We are moving in the Y direction.
514
- commit_scanline()
515
- if abs(dy) > self.service.max_raster_jog:
516
- self._raster_jog(x, y, raster_cut)
517
- else:
518
- self._relative = True
519
- self("PR")
520
- self._goto(x, y) # remain standard rastermode
521
- if dx != 0:
522
- # Normal move, extend bytes.
523
- scanline.extend([int(on)] * abs(dx))
524
- previous_x, previous_y = x, y
525
- else:
526
- self.mode = "raster_vertical"
527
- for x, y, on in raster_cut.plot.plot():
528
- dx = x - previous_x
529
- dy = y - previous_y
530
- if dy < 0 and increasing or dy > 0 and not increasing:
531
- # Y direction has switched.
532
- commit_scanline()
533
- increasing = not increasing
534
- if dx != 0:
535
- # We are moving in the X direction.
536
- commit_scanline()
537
- if abs(dx) > self.service.max_raster_jog:
538
- self._raster_jog(x, y, raster_cut)
539
- else:
540
- self._relative = True
541
- self("PR")
542
- self._goto(x, y) # remain standard rastermode
543
- if dy != 0:
544
- # Normal move, extend bytes
545
- scanline.extend([int(on)] * abs(dy))
546
- previous_x, previous_y = x, y
547
- commit_scanline()
548
-
549
- #######################
550
- # SETS FOR PLOTLIKES
551
- #######################
552
-
553
- def set_settings(self, settings):
554
- """
555
- Sets the primary settings. Rapid, frequency, speed, and timings.
556
-
557
- @param settings: The current settings dictionary
558
- @return:
559
- """
560
- if "speed" in settings:
561
- self._set_speed = settings.get("speed")
562
- if "power" in settings:
563
- self._set_power = settings.get("power")
564
- if "pwm_frequency" in settings:
565
- self._set_pwm_freq = settings.get("pwm_frequency")
566
-
567
- #######################
568
- # PLOTLIKE SHORTCUTS
569
- #######################
570
-
571
- def raw(self, data):
572
- data = bytes(data, "latin1")
573
- self.connect_if_needed()
574
- self.connection.write(index=self._machine_index, data=data)
575
-
576
- def frame(self, x, y):
577
- self._set_frame_mode()
578
- self._mark(x, y)
579
-
580
- def mark(self, x, y, settings=None, power=None, speed=None):
581
- """
582
- Mark either sets default vector settings or sets the settings based on the settings object provided.
583
- @param x:
584
- @param y:
585
- @param settings:
586
- @param power: power during switch
587
- @param speed: speed for marking
588
- @return:
589
- """
590
- self._set_vector_mode()
591
- if settings is not None:
592
- self.set_settings(settings)
593
- if power is not None:
594
- self._set_power = power
595
- if speed is not None:
596
- self._set_speed = speed
597
- self._mark(x, y)
598
-
599
- def _mark(self, x, y):
600
- dx = int(round(x - self._last_x))
601
- dy = int(round(y - self._last_y))
602
- if dx == 0 and dy == 0:
603
- return
604
- self._commit_settings()
605
- if self._relative:
606
- self(f"PD{dy},{dx}")
607
- self._last_x += dx
608
- self._last_y += dy
609
- else:
610
- x = int(round(x))
611
- y = int(round(y))
612
- self(f"PD{y},{x}")
613
- self._last_x, self._last_y = x, y
614
-
615
- def goto(self, x, y):
616
- """
617
- Goto position.
618
- @param x:
619
- @param y:
620
- @return:
621
- """
622
- self.goto_rel(x, y)
623
-
624
- def goto_abs(self, x, y):
625
- """
626
- Goto given absolute coords value.
627
- @param x:
628
- @param y:
629
- @return:
630
- """
631
- self._set_move_mode()
632
- self._relative = False
633
- self._goto(x, y)
634
-
635
- def goto_rel(self, x, y):
636
- """
637
- Positions are given in absolute coords, but the motion sent to the laser is relative.
638
- @param x:
639
- @param y:
640
- @return:
641
- """
642
- self._set_move_mode()
643
- self._goto(x, y)
644
-
645
- def _goto(self, x, y):
646
- dx = int(round(x - self._last_x))
647
- dy = int(round(y - self._last_y))
648
- if dx == 0 and dy == 0:
649
- return
650
- self._commit_settings()
651
- if self._relative:
652
- self(f"PU{dy},{dx}")
653
- self._last_x += dx
654
- self._last_y += dy
655
- else:
656
- x = int(round(x))
657
- y = int(round(y))
658
- self(f"PU{y},{x}")
659
- self._last_x, self._last_y = x, y
660
-
661
- def set_xy(self, x, y):
662
- self.realtime_job()
663
- self.mode = "jog"
664
- self.goto_rel(x, y)
665
- self.close_job()
666
-
667
- def get_last_xy(self):
668
- return self._last_x, self._last_y
669
-
670
- #######################
671
- # Command Shortcuts
672
- #######################
673
-
674
- def move_frame(self, file_index):
675
- self.realtime_job()
676
- self.mode = "move_frame"
677
- self(f"ZK{file_index}")
678
- self.close_job()
679
-
680
- def draw_frame(self, file_index):
681
- self.realtime_job()
682
- self.mode = "draw_frame"
683
- self(f"ZH{file_index}")
684
- self.close_job()
685
-
686
- def replay(self, file_index):
687
- self.realtime_job()
688
- self.mode = "replay"
689
- self(f"ZG{file_index}")
690
- self.close_job()
691
-
692
- def home_speeds(self, x_speed, y_speed, m_speed):
693
- self.realtime_job()
694
- self.mode = "home_speeds"
695
- self(f"VX{int(round(x_speed / 10))}")
696
- self(f"VY{int(round(y_speed / 10))}")
697
- self(f"VM{int(round(m_speed / 10))}")
698
- self.close_job()
699
-
700
- def z_relative(self, amount, speed=100):
701
- self.realtime_job()
702
- self.mode = "zmove"
703
- self(f"CV{int(round(speed / 10))}")
704
- self(f"CR{int(round(amount))}")
705
- self.close_job()
706
-
707
- def z_absolute(self, z_position, speed=100):
708
- self.realtime_job()
709
- self.mode = "zmove"
710
- self(f"CV{int(round(speed / 10))}")
711
- self(f"CU{int(round(z_position))}")
712
- self.close_job()
713
-
714
- def w_relative(self, amount, speed=100):
715
- self.realtime_job()
716
- self.mode = "wmove"
717
- self(f"WV{int(round(speed / 10))}")
718
- self(f"WR{int(round(amount))}")
719
- self.close_job()
720
-
721
- def w_absolute(self, w_position, speed=100):
722
- self.realtime_job()
723
- self.mode = "wmove"
724
- self(f"WV{int(round(speed / 10))}")
725
- self(f"WU{int(round(w_position))}")
726
- self.close_job()
727
-
728
- def pulse(self, pulse_time_ms):
729
- self.realtime_job()
730
- self.dwell(pulse_time_ms)
731
- self.close_job()
732
-
733
- def home(self):
734
- self.realtime_job()
735
- self.mode = "home"
736
- self("RS")
737
- self._last_x = 0
738
- self._last_y = 0
739
- self.close_job()
740
-
741
- def origin(self):
742
- self.realtime_job()
743
- self.mode = "origin"
744
- self.goto(0, 0)
745
- self.close_job()
746
-
747
- def abort(self):
748
- self.realtime_job()
749
- self.mode = "abort"
750
- self("ZQ")
751
- self.close_job()
752
-
753
- def wait(self, time_in_ms):
754
- while time_in_ms > 255:
755
- time_in_ms -= 255
756
- self("TX255")
757
- if time_in_ms > 0:
758
- self(f"TX{int(round(time_in_ms))}")
759
-
760
- def dwell(self, time_in_ms, settings=None):
761
- self.mode = "pulse"
762
- if self.service.pwm_enabled:
763
- self._set_pwm_freq = self.service.pwm_frequency
764
- if settings is not None:
765
- # Settings based speed, power, pwm_freq
766
- if "power" in settings:
767
- self._set_power = settings.get("power")
768
- if "pwm_frequency" in settings:
769
- self._set_pwm_freq = settings.get("pwm_frequency")
770
- # self.set_settings(settings)
771
- else:
772
- self._set_power = 1000.0
773
- self._set_power *= self.service.max_pulse_power / 100.0
774
- self._commit_pwmfreq()
775
- self._commit_power()
776
- while time_in_ms > 255:
777
- time_in_ms -= 255
778
- self("TO255")
779
- if time_in_ms > 0:
780
- self(f"TO{int(round(time_in_ms))}")
781
-
782
- def pause(self):
783
- self.realtime_job()
784
- self.mode = "pause"
785
- self("ZT")
786
- self.close_job()
787
- self.paused = True
788
-
789
- def resume(self):
790
- self.realtime_job()
791
- self.mode = "resume"
792
- self("ZG")
793
- self.close_job()
794
- self.paused = False
795
-
796
- def wait_finished(self):
797
- # No known method to force the laser to single state.
798
- pass
799
-
800
- def init_laser(self):
801
- self.usb_log("Ready")
802
-
803
- def power(self, power):
804
- """
805
- Accepts power in percent, automatically converts to power_ratio
806
-
807
- @param power:
808
- @return:
809
- """
810
- self._set_power = power
811
-
812
- #######################
813
- # Commit settings
814
- #######################
815
-
816
- def _commit_settings(self):
817
- if self._set_speed is not None:
818
- # If set speed is set, we set all the various speed chart lookup properties.
819
- settings = self._get_chart_settings_for_speed(self._set_speed)
820
- self._set_corner_speed = int(round(settings["corner_speed"]))
821
- self._set_acceleration_length = int(round(settings["acceleration_length"]))
822
- self._set_backlash = int(round(settings["backlash"]))
823
-
824
- self.commit_mode()
825
- self._commit_pwmfreq()
826
- self._commit_dc()
827
- if self._mode != "raster":
828
- self._commit_pen()
829
- self._commit_power()
830
- if self._mode == "raster":
831
- self._commit_raster()
832
- self._commit_pen()
833
- if self._mode != "vector":
834
- self._commit_corner_speed()
835
- self._commit_acceleration_length()
836
- self._commit_speed()
837
- self._commit_relative_mode()
838
-
839
- def commit_mode(self):
840
- if self._set_mode is None:
841
- # Quick fail.
842
- return
843
- new_mode = self._set_mode
844
- self._set_mode = None
845
- if new_mode != self._mode:
846
- self._mode = new_mode
847
-
848
- # Old speed and power are void on mode change.
849
- self._speed = None
850
- self._power = None
851
- self._corner_speed = None
852
- self._acceleration_length = None
853
- self._backlash = None
854
-
855
- #######################
856
- # Commit DC Info
857
- #######################
858
-
859
- def _commit_cut_dc(self):
860
- if self._set_cut_dc is None and self._cut_dc is not None:
861
- # Quick Fail.
862
- return
863
-
864
- # Fetch Requested.
865
- new_dc = self._set_cut_dc
866
- self._set_cut_dc = None
867
- if new_dc is None:
868
- # Nothing set, set default.
869
- new_dc = self.service.cut_dc
870
- if new_dc != self._cut_dc:
871
- # DC is different
872
- self._cut_dc = new_dc
873
- self(f"VP{self._cut_dc}")
874
-
875
- def _commit_move_dc(self):
876
- if self._set_move_dc is None and self._move_dc is not None:
877
- # Quick Fail.
878
- return
879
-
880
- # Fetch Requested.
881
- new_dc = self._set_move_dc
882
- self._set_move_dc = None
883
- if new_dc is None:
884
- # Nothing set, set default.
885
- new_dc = self.service.move_dc
886
- if new_dc != self._move_dc:
887
- # DC is different
888
- self._move_dc = new_dc
889
- self(f"VK{self._move_dc}")
890
-
891
- def _commit_dc(self):
892
- self._commit_cut_dc()
893
- self._commit_move_dc()
894
-
895
- #######################
896
- # Commit Pen
897
- #######################
898
-
899
- def _commit_pen(self):
900
- if self._set_pen is None and self._pen is not None:
901
- # Quick Fail.
902
- return
903
-
904
- # Fetch Requested.
905
- new_pen = self._set_pen
906
- self._set_pen = None
907
- if new_pen is None:
908
- # Nothing set, set default.
909
- new_pen = 0
910
- if new_pen != self._pen:
911
- # PEN is different
912
- self._pen = new_pen
913
- self(f"SP{self._pen}")
914
-
915
- #######################
916
- # Commit Power
917
- #######################
918
-
919
- def _map_power(self, power):
920
- if self.mode == "pulse":
921
- power /= 10.0
922
- return int(round(power))
923
- power /= 1000.0 # Scale to 0-1
924
- power *= self.service.max_power # Scale by max power %
925
- power *= 255.0 / 100.0 # range between 000 and 255
926
- if power > 255:
927
- return 255
928
- if power <= 0:
929
- return 0
930
- return int(round(power))
931
-
932
- def _commit_power(self):
933
- """
934
- Write power information. If the _set_power is set then it takes priority. Otherwise, the power remains set to
935
- what it was previously set to. If no power is set, then power is set to the default cut power.
936
-
937
- @return:
938
- """
939
- if self._set_power is None and self._power is not None:
940
- return # quick fail.
941
-
942
- new_power = self._set_power
943
- self._set_power = None
944
-
945
- if new_power is None:
946
- return
947
-
948
- # Premap the power setting.
949
- new_power = self._map_power(new_power)
950
-
951
- if new_power != self._power:
952
- # Already set power is not the new_power setting.
953
- self._power = new_power
954
- self(f"DA{new_power}")
955
-
956
- def _commit_pwmfreq(self):
957
- """
958
- Write pwm frequency information.
959
- @return:
960
- """
961
- if self._set_pwm_freq is None and self._pwm_frequency is not None:
962
- # Quick Fail.
963
- return
964
-
965
- # Fetch Requested.
966
- new_freq = self._set_pwm_freq
967
- self._set_pwm_freq = None
968
- if new_freq is None:
969
- return
970
- if new_freq != self._pwm_frequency:
971
- # Frequency is needed, and different
972
- self._pwm_frequency = new_freq
973
- self(f"PL{self._pwm_frequency}")
974
-
975
- #######################
976
- # Commit speed settings
977
- #######################
978
-
979
- def _commit_speed(self):
980
- if self._set_speed is None and self._speed is not None:
981
- return
982
- new_speed = self._set_speed
983
- self._set_speed = None
984
- if new_speed is not None and new_speed != self._speed:
985
- self._speed = new_speed
986
- if self._mode == "vector":
987
- self(f"VS{self._map_vector_speed(new_speed)}")
988
- else:
989
- self(f"VS{self._map_raster_speed(new_speed)}")
990
-
991
- def _get_chart_settings_for_speed(self, speed):
992
- """
993
- Get charted settings for a particular speed. Given a speed this provides other required settings for this
994
- same speed.
995
-
996
- @param speed:
997
- @return:
998
- """
999
- chart = self.service.speedchart
1000
- smallest_difference = float("inf")
1001
- closest_index = None
1002
- for i, c in enumerate(chart):
1003
- chart_speed = c.get("speed", 0)
1004
- delta_speed = chart_speed - speed
1005
- if chart_speed > speed and smallest_difference > delta_speed:
1006
- smallest_difference = delta_speed
1007
- closest_index = i
1008
- if closest_index is not None:
1009
- return chart[closest_index]
1010
- return chart[-1]
1011
-
1012
- def _map_raster_speed(self, speed):
1013
- v = int(round(speed / 10))
1014
- if v == 0:
1015
- v = 1
1016
- return v
1017
-
1018
- def _map_vector_speed(self, speed):
1019
- if speed >= 93:
1020
- return int(round(speed / 10))
1021
- if speed >= 15:
1022
- return 162 + int(round(speed))
1023
- if speed >= 5:
1024
- return 147 + int(round(speed * 2))
1025
- if speed >= 1:
1026
- return 132 + int(round(speed * 5))
1027
- else:
1028
- return 127 + int(round(speed * 10))
1029
-
1030
- #######################
1031
- # Commit Speed Chart
1032
- #######################
1033
-
1034
- def _commit_corner_speed(self):
1035
- if self._set_corner_speed is None and self._corner_speed is not None:
1036
- # Quick Fail.
1037
- return
1038
-
1039
- # Fetch Requested.
1040
- new_corner_speed = self._set_corner_speed
1041
- self._set_corner_speed = None
1042
- if new_corner_speed is None:
1043
- # Nothing set, set default.
1044
- return
1045
- if new_corner_speed != self._corner_speed:
1046
- # Corner_speed is different
1047
- self._corner_speed = new_corner_speed
1048
- self(f"VQ{int(round(self._corner_speed))}")
1049
-
1050
- def _commit_acceleration_length(self):
1051
- if (
1052
- self._set_acceleration_length is None
1053
- and self._acceleration_length is not None
1054
- ):
1055
- # Quick Fail.
1056
- return
1057
-
1058
- # Fetch Requested.
1059
- new_acceleration_length = self._set_acceleration_length
1060
- self._set_acceleration_length = None
1061
- if new_acceleration_length is None:
1062
- # Nothing set, set default.
1063
- return
1064
- if new_acceleration_length != self._acceleration_length:
1065
- # Acceleration Length is different
1066
- self._acceleration_length = new_acceleration_length
1067
- self(f"VJ{int(round(self._acceleration_length))}")
1068
-
1069
- #######################
1070
- # Commit Relative Mode
1071
- #######################
1072
-
1073
- def _commit_relative_mode(self):
1074
- if self._set_relative is None and self._relative is not None:
1075
- return
1076
- new_relative = self._set_relative
1077
- self._set_relative = None
1078
-
1079
- if new_relative is None:
1080
- new_relative = True
1081
-
1082
- if new_relative != self._relative:
1083
- self._relative = new_relative
1084
- if self._relative:
1085
- self("PR")
1086
- else:
1087
- self("PA")
1088
-
1089
- #######################
1090
- # Commit Raster
1091
- #######################
1092
-
1093
- def _commit_raster_bitdepth(self):
1094
- if self._set_bit_depth is None and self._bit_depth is not None:
1095
- # Quick Fail.
1096
- return
1097
-
1098
- # Fetch Requested.
1099
- new_bitdepth = self._set_bit_depth
1100
- self._set_bit_depth = None
1101
- if new_bitdepth is None:
1102
- # Nothing set, set default.
1103
- return
1104
- if new_bitdepth != self._bit_depth:
1105
- # Bitdepth is different
1106
- self._bit_depth = new_bitdepth
1107
- self(f"BT{self._bit_depth}")
1108
-
1109
- def _commit_raster_bitwidth(self):
1110
- if self._set_bit_width is None and self._bit_width is not None:
1111
- # Quick Fail.
1112
- return
1113
-
1114
- # Fetch Requested.
1115
- new_bitwidth = self._set_bit_width
1116
- self._set_bit_width = None
1117
- if new_bitwidth is None:
1118
- # Nothing set, set default.
1119
- return
1120
- if new_bitwidth != self._bit_width:
1121
- # Bitdepth is different
1122
- self._bit_width = new_bitwidth
1123
- self(f"BD{self._bit_width}")
1124
-
1125
- def _commit_raster_backlash(self):
1126
- if self._set_backlash is None and self._backlash is not None:
1127
- # Quick Fail.
1128
- return
1129
-
1130
- # Fetch Requested.
1131
- new_backlash = self._set_backlash
1132
- self._set_backlash = None
1133
- if new_backlash is None:
1134
- # Nothing set, set default.
1135
- return
1136
- if new_backlash != self._backlash:
1137
- # backlash is different
1138
- self._backlash = new_backlash
1139
- self(f"BC{self._backlash}")
1140
-
1141
- def _commit_raster(self):
1142
- self._commit_raster_bitdepth()
1143
- self._commit_raster_backlash()
1144
- self._commit_raster_bitwidth()
1
+ """
2
+ Newly Controller
3
+ """
4
+ import math
5
+ import struct
6
+ import time
7
+
8
+ from meerk40t.core.cutcode.rastercut import RasterCut
9
+ from meerk40t.newly.mock_connection import MockConnection
10
+ from meerk40t.newly.usb_connection import USBConnection
11
+
12
+
13
+ class NewlyController:
14
+ """
15
+ Newly Controller
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ service,
21
+ x=0,
22
+ y=0,
23
+ force_mock=False,
24
+ ):
25
+ self._machine_index = 0
26
+ self.service = service
27
+ self.force_mock = force_mock
28
+ self.is_shutdown = False # Shutdown finished.
29
+
30
+ self.usb_log = service.channel(f"{service.safe_label}/usb", buffer_size=500)
31
+ self.usb_log.watch(lambda e: service.signal("pipe;usb_status", e))
32
+
33
+ # Load Primary Pens
34
+ self.sp0 = self.service.setting(int, "sp0", 0)
35
+ self.sp1 = self.service.setting(int, "sp1", 1)
36
+ self.sp2 = self.service.setting(int, "sp2", 2)
37
+
38
+ self.connection = None
39
+ self._is_opening = False
40
+ self._abort_open = False
41
+ self._disable_connect = False
42
+
43
+ self._job_x = None
44
+ self._job_y = None
45
+
46
+ self._last_x = x
47
+ self._last_y = y
48
+
49
+ self._last_updated_x = x
50
+ self._last_updated_y = y
51
+
52
+ #######################
53
+ # Preset Modes.
54
+ #######################
55
+
56
+ self._set_pen = None
57
+ self._set_cut_dc = None
58
+ self._set_move_dc = None
59
+ self._set_mode = None
60
+ self._set_speed = None
61
+ self._set_power = None
62
+ self._set_pwm_freq = None
63
+ self._set_relative = None
64
+ self._set_bit_depth = None
65
+ self._set_bit_width = None
66
+ self._set_backlash = None
67
+ self._set_corner_speed = None
68
+ self._set_acceleration_length = None
69
+
70
+ #######################
71
+ # Current Set Modes.
72
+ #######################
73
+
74
+ self._pen = None
75
+ self._cut_dc = None
76
+ self._move_dc = None
77
+ self._mode = None
78
+ self._speed = None
79
+ self._power = None
80
+ self._pwm_frequency = None
81
+ self._relative = None
82
+ self._bit_depth = None
83
+ self._bit_width = None
84
+ self._backlash = None
85
+ self._corner_speed = None
86
+ self._acceleration_length = None
87
+
88
+ self._realtime = False
89
+
90
+ self.mode = "init"
91
+ self.paused = False
92
+ self._command_buffer = []
93
+ self._signal_updates = self.service.setting(bool, "signal_updates", True)
94
+
95
+ def __call__(self, cmd, *args, **kwargs):
96
+ if isinstance(cmd, str):
97
+ # Any string data sent is latin-1 encoded.
98
+ self._command_buffer.append(cmd.encode("latin-1"))
99
+ else:
100
+ self._command_buffer.append(cmd)
101
+
102
+ def set_disable_connect(self, status):
103
+ self._disable_connect = status
104
+
105
+ def added(self):
106
+ pass
107
+
108
+ def service_detach(self):
109
+ pass
110
+
111
+ def shutdown(self, *args, **kwargs):
112
+ self.is_shutdown = True
113
+
114
+ @property
115
+ def connected(self):
116
+ if self.connection is None:
117
+ return False
118
+ return self.connection.is_open(self._machine_index)
119
+
120
+ @property
121
+ def is_connecting(self):
122
+ if self.connection is None:
123
+ return False
124
+ return self._is_opening
125
+
126
+ def abort_connect(self):
127
+ self._abort_open = True
128
+ self.usb_log("Connect Attempts Aborted")
129
+
130
+ def disconnect(self):
131
+ try:
132
+ self.connection.close(self._machine_index)
133
+ except (ConnectionError, ConnectionRefusedError, AttributeError):
134
+ pass
135
+ self.connection = None
136
+ # Reset error to allow another attempt
137
+ self.set_disable_connect(False)
138
+
139
+ def connect_if_needed(self):
140
+ if self._disable_connect:
141
+ # After many failures automatic connects are disabled. We require a manual connection.
142
+ self.abort_connect()
143
+ self.connection = None
144
+ raise ConnectionRefusedError(
145
+ "NewlyController was unreachable. Explicit connect required."
146
+ )
147
+ if self.connection is None:
148
+ if self.service.setting(bool, "mock", False) or self.force_mock:
149
+ self.connection = MockConnection(self.usb_log)
150
+ name = self.service.safe_label
151
+ self.connection.send = self.service.channel(f"{name}/send")
152
+ self.connection.recv = self.service.channel(f"{name}/recv")
153
+ else:
154
+ self.connection = USBConnection(self.usb_log)
155
+ if self.connection is None:
156
+ self._is_opening = False
157
+ self.set_disable_connect(True)
158
+ self.usb_log("Could not connect to the controller.")
159
+ self.usb_log("Automatic connections disabled.")
160
+ raise ConnectionRefusedError("Could not connect to the controller.")
161
+
162
+ self._is_opening = True
163
+ self._abort_open = False
164
+ count = 0
165
+ while not self.connection.is_open(self._machine_index):
166
+ try:
167
+ if self.connection.open(self._machine_index) < 0:
168
+ raise ConnectionError
169
+ self.init_laser()
170
+ except (ConnectionError, ConnectionRefusedError):
171
+ time.sleep(0.3)
172
+ count += 1
173
+ # self.usb_log(f"Error-Routine pass #{count}")
174
+ if self.is_shutdown or self._abort_open:
175
+ self._is_opening = False
176
+ self._abort_open = False
177
+ return
178
+ if self.connection.is_open(self._machine_index):
179
+ self.connection.close(self._machine_index)
180
+ if count >= 10:
181
+ # We have failed too many times.
182
+ self._is_opening = False
183
+ self.set_disable_connect(True)
184
+ self.usb_log("Could not connect to the controller.")
185
+ self.usb_log("Automatic connections disabled.")
186
+ raise ConnectionRefusedError("Could not connect to the controller.")
187
+ time.sleep(0.3)
188
+ continue
189
+ self._is_opening = False
190
+ self._abort_open = False
191
+
192
+ def sync(self):
193
+ self._last_updated_x, self._last_updated_y = self._last_x, self._last_y
194
+
195
+ def update(self):
196
+ last_x, last_y = self.service.view.iposition(
197
+ self._last_updated_x, self._last_updated_y
198
+ )
199
+ x, y = self.service.view.iposition(self._last_x, self._last_y)
200
+ self.sync()
201
+ if self._signal_updates:
202
+ self.service.signal("driver;position", (last_x, last_y, x, y))
203
+
204
+ #######################
205
+ # MODE SHIFTS
206
+ #######################
207
+
208
+ def realtime_job(self, job=None):
209
+ """
210
+ Starts a realtime job, which runs on file0
211
+ @return:
212
+ """
213
+ if self.mode != "init":
214
+ return
215
+ self._realtime = True
216
+ self.mode = "realtime"
217
+ self(f"ZZZFile{0}")
218
+ self._clear_settings()
219
+
220
+ def _clear_settings(self):
221
+ self._set_pen = None
222
+ self._set_cut_dc = None
223
+ self._set_move_dc = None
224
+ self._set_mode = None
225
+ self._set_speed = None
226
+ self._set_power = None
227
+ self._set_pwm_freq = None
228
+ self._set_relative = None
229
+ self._set_bit_depth = None
230
+ self._set_bit_width = None
231
+ self._set_backlash = None
232
+ self._set_corner_speed = None
233
+ self._set_acceleration_length = None
234
+
235
+ self._pen = None
236
+ self._cut_dc = None
237
+ self._move_dc = None
238
+ self._mode = None
239
+ self._speed = None
240
+ self._power = None
241
+ self._pwm_frequency = None
242
+ self._relative = None
243
+ self._bit_depth = None
244
+ self._bit_width = None
245
+ self._backlash = None
246
+ self._corner_speed = None
247
+ self._acceleration_length = None
248
+
249
+ def _set_move_mode(self):
250
+ """
251
+ Move mode is done for any major movements usually starting out an execution.
252
+
253
+ e.g.
254
+ VP100;VK100;SP2;SP2;VQ15;VJ24;VS10;DA0;
255
+ @return:
256
+ """
257
+ self.mode = "move"
258
+ self._set_pen = self.sp2
259
+ self._set_cut_dc = self.service.cut_dc
260
+ self._set_move_dc = self.service.move_dc
261
+ self._set_mode = "move"
262
+ self._set_speed = self.service.moving_speed
263
+ self._set_power = None
264
+ self._set_pwm_freq = None
265
+ self._set_relative = True
266
+ self._set_bit_depth = None
267
+ self._set_bit_width = None
268
+ self._set_backlash = None
269
+ self._set_corner_speed = None
270
+ self._set_acceleration_length = None
271
+
272
+ def _set_goto_mode(self):
273
+ """
274
+ Goto mode is done for minor between movements, where we don't need to set the power to 0, since we will be
275
+ using PU; commands.
276
+
277
+ e.g.
278
+ SP2;SP2;VQ15;VJ24;VS10;PR;PU2083,-5494;
279
+
280
+ @return:
281
+ """
282
+ self._set_pen = self.sp2
283
+ self._set_mode = "goto"
284
+ self._set_speed = self.service.moving_speed
285
+ self._set_relative = True
286
+
287
+ def _set_frame_mode(self):
288
+ """
289
+ Frame mode is the standard framing operation settings.
290
+ e.g.
291
+ SP0;VS20;PR;PD9891,0;PD0,-19704;PD-9891,0;PD0,19704;ZED;
292
+
293
+ @return:
294
+ """
295
+
296
+ self._set_cut_dc = self.service.cut_dc
297
+ self._set_move_dc = self.service.move_dc
298
+ self._set_pen = self.sp0
299
+ self._set_power = 0
300
+ self._set_relative = True
301
+ self._set_mode = "frame"
302
+ self._set_speed = self.service.moving_speed
303
+ if self.service.pwm_enabled:
304
+ self._set_pwm_freq = self.service.pwm_frequency
305
+
306
+ def _set_vector_mode(self):
307
+ """
308
+ Vector mode typically is just the PD commands for a vector.
309
+
310
+ e.g.
311
+ PR;SP1;DA65;VS187;PD0,-2534;PD1099,0;PD0,2534;PD-1099,0;
312
+ @return:
313
+ """
314
+ self._set_pen = self.sp1
315
+ self._set_cut_dc = self.service.cut_dc
316
+ self._set_move_dc = self.service.move_dc
317
+ self._set_mode = "vector"
318
+ self._set_speed = self.service.default_cut_speed
319
+ self._set_power = self.service.default_cut_power
320
+ if self.service.pwm_enabled:
321
+ self._set_pwm_freq = self.service.pwm_frequency
322
+ self._set_relative = True
323
+ self._set_bit_depth = None
324
+ self._set_bit_width = None
325
+ self._set_backlash = None
326
+
327
+ def _set_raster_mode(self):
328
+ """
329
+ Raster mode is the typical preamble and required settings for running a raster. This usually consists of
330
+ YF, YZ, XF, XZ, and small PU commands.
331
+
332
+ EG.
333
+ BT1;DA77;BC0;BD3;SP0;VQ20;VJ10;VS18;YF...
334
+ @return:
335
+ """
336
+ self._set_pen = self.sp0
337
+ self._set_cut_dc = self.service.cut_dc
338
+ self._set_move_dc = self.service.move_dc
339
+ self._set_mode = "raster"
340
+ self._corner_speed = None
341
+ self._acceleration_length = None
342
+ if self.service.pwm_enabled:
343
+ self._set_pwm_freq = self.service.pwm_frequency
344
+ self._set_relative = True
345
+ self._set_bit_depth = 1
346
+ self._set_bit_width = 1
347
+ self._set_speed = self.service.default_raster_speed
348
+ self._set_power = self.service.default_raster_power
349
+
350
+ def _write_frame(self, outline):
351
+ self.mode = "frame"
352
+ if outline is not None:
353
+ x, y = self._last_x, self._last_y
354
+ self("DW")
355
+ for pt in outline:
356
+ self.frame(*pt)
357
+ self.frame(*outline[0])
358
+ self.goto(x, y) # Return to initial x, y if different from outline.
359
+ self._clear_settings()
360
+ self("ZED")
361
+
362
+ def _execute_job(self):
363
+ self.service.laser_status = "active"
364
+ self("ZED")
365
+ cmd = b";".join(self._command_buffer) + b";"
366
+ try:
367
+ self.connect_if_needed()
368
+ self.connection.write(index=self._machine_index, data=cmd)
369
+ except ConnectionError:
370
+ pass
371
+ self._command_buffer.clear()
372
+ self._clear_settings()
373
+ self.service.laser_status = "idle"
374
+
375
+ def open_job(self, job=None):
376
+ """
377
+ Opens a job at the declared file_index
378
+
379
+ @return:
380
+ """
381
+
382
+ outline = None
383
+ try:
384
+ outline = job.outline
385
+ except AttributeError:
386
+ pass
387
+ if outline is not None:
388
+ self.set_xy(*outline[0])
389
+ self._job_x, self._job_y = outline[0]
390
+ self._realtime = False
391
+ self._clear_settings()
392
+ self(f"ZZZFile{self.service.file_index}")
393
+ self._write_frame(outline)
394
+ self("GZ")
395
+ self._clear_settings()
396
+
397
+ def close_job(self, job=None):
398
+ """
399
+ Closes the file and sends.
400
+ @return:
401
+ """
402
+ if not self._command_buffer:
403
+ return
404
+ if self.mode in ("realtime", "init"):
405
+ # Job contains no instructions.
406
+ self.mode = "init"
407
+ self._command_buffer.clear()
408
+ return
409
+ if not self._realtime and self._job_x is not None and self._job_y is not None:
410
+ self.goto_rel(self._job_x, self._job_y)
411
+
412
+ self._execute_job()
413
+ self.mode = "init"
414
+ if self.service.autoplay and not self._realtime:
415
+ self.replay(self.service.file_index)
416
+
417
+ def program_mode(self):
418
+ self._set_vector_mode()
419
+
420
+ def rapid_mode(self):
421
+ pass
422
+
423
+ #######################
424
+ # Raster Events
425
+ #######################
426
+
427
+ def scanline(self, bits, right=False, left=False, top=False, bottom=False):
428
+ """
429
+ Send a scanline movement.
430
+
431
+ @param bits: list of bits.
432
+ @param right: Moving right?
433
+ @param left: Moving left?
434
+ @param top: Moving top?
435
+ @param bottom: Moving bottom?
436
+ @return:
437
+ """
438
+ self._commit_settings()
439
+ cmd = None
440
+ if left: # left movement
441
+ cmd = bytearray(b"YF")
442
+ bits = bits[::-1]
443
+ elif right:
444
+ cmd = bytearray(b"YZ")
445
+ bits = bits[::-1]
446
+ elif top:
447
+ cmd = bytearray(b"XF")
448
+ bits = bits[::-1]
449
+ elif bottom:
450
+ cmd = bytearray(b"XZ")
451
+ bits = bits[::-1]
452
+ if cmd is None:
453
+ return # 0,0 goes nowhere.
454
+ count = len(bits)
455
+ byte_length = int(math.ceil(count / 8))
456
+ cmd += struct.pack(">i", count)[1:]
457
+ binary = "".join([str(b) for b in bits])
458
+ cmd += int(binary, 2).to_bytes(byte_length, "little")
459
+ self(cmd)
460
+ if left:
461
+ self._last_x -= count
462
+ elif right:
463
+ self._last_x += count
464
+ elif top:
465
+ self._last_y -= count
466
+ elif bottom:
467
+ self._last_y += count
468
+
469
+ def _raster_jog(self, x, y, raster_cut):
470
+ self.goto(x, y)
471
+ self._set_raster_mode()
472
+ self.set_settings(raster_cut.settings)
473
+ self._set_mode = "raster"
474
+ self._commit_settings()
475
+
476
+ def raster(self, raster_cut: RasterCut):
477
+ """
478
+ Run a raster cut with scanlines and default actions.
479
+
480
+ @param raster_cut:
481
+ @return:
482
+ """
483
+
484
+ scanline = []
485
+ increasing = True
486
+
487
+ def commit_scanline():
488
+ if scanline:
489
+ # If there is a scanline commit the scanline.
490
+ if raster_cut.horizontal:
491
+ # Horizontal Raster.
492
+ if increasing:
493
+ self.scanline(scanline, right=True)
494
+ scanline.clear()
495
+ else:
496
+ self.scanline(scanline, left=True)
497
+ scanline.clear()
498
+ else:
499
+ # Vertical raster.
500
+ if increasing:
501
+ self.scanline(scanline, bottom=True)
502
+ scanline.clear()
503
+ else:
504
+ self.scanline(scanline, top=True)
505
+ scanline.clear()
506
+
507
+ self("IN")
508
+ self._clear_settings()
509
+
510
+ previous_x, previous_y = raster_cut.plot.initial_position_in_scene()
511
+
512
+ self._raster_jog(previous_x, previous_y, raster_cut)
513
+
514
+ if raster_cut.horizontal:
515
+ self.mode = "raster_horizontal"
516
+ for x, y, on in raster_cut.plot.plot():
517
+ dx = x - previous_x
518
+ dy = y - previous_y
519
+ if dx < 0 and increasing or dx > 0 and not increasing:
520
+ # X direction has switched.
521
+ commit_scanline()
522
+ increasing = not increasing
523
+ if dy != 0:
524
+ # We are moving in the Y direction.
525
+ commit_scanline()
526
+ if abs(dy) > self.service.max_raster_jog:
527
+ self._raster_jog(x, y, raster_cut)
528
+ else:
529
+ self._relative = True
530
+ self("PR")
531
+ self._goto(x, y) # remain standard rastermode
532
+ if dx != 0:
533
+ # Normal move, extend bytes.
534
+ scanline.extend([int(on)] * abs(dx))
535
+ previous_x, previous_y = x, y
536
+ else:
537
+ self.mode = "raster_vertical"
538
+ for x, y, on in raster_cut.plot.plot():
539
+ dx = x - previous_x
540
+ dy = y - previous_y
541
+ if dy < 0 and increasing or dy > 0 and not increasing:
542
+ # Y direction has switched.
543
+ commit_scanline()
544
+ increasing = not increasing
545
+ if dx != 0:
546
+ # We are moving in the X direction.
547
+ commit_scanline()
548
+ if abs(dx) > self.service.max_raster_jog:
549
+ self._raster_jog(x, y, raster_cut)
550
+ else:
551
+ self._relative = True
552
+ self("PR")
553
+ self._goto(x, y) # remain standard rastermode
554
+ if dy != 0:
555
+ # Normal move, extend bytes
556
+ scanline.extend([int(on)] * abs(dy))
557
+ previous_x, previous_y = x, y
558
+ commit_scanline()
559
+
560
+ #######################
561
+ # SETS FOR PLOTLIKES
562
+ #######################
563
+
564
+ def set_settings(self, settings):
565
+ """
566
+ Sets the primary settings. Rapid, frequency, speed, and timings.
567
+
568
+ @param settings: The current settings dictionary
569
+ @return:
570
+ """
571
+ if "speed" in settings:
572
+ self._set_speed = settings.get("speed")
573
+ if "power" in settings:
574
+ self._set_power = settings.get("power")
575
+ if "pwm_frequency" in settings:
576
+ self._set_pwm_freq = settings.get("pwm_frequency")
577
+
578
+ #######################
579
+ # PLOTLIKE SHORTCUTS
580
+ #######################
581
+
582
+ def raw(self, data):
583
+ data = bytes(data, "latin1")
584
+ try:
585
+ self.connect_if_needed()
586
+ self.connection.write(index=self._machine_index, data=data)
587
+ except ConnectionError:
588
+ return
589
+
590
+ def frame(self, x, y):
591
+ self._set_frame_mode()
592
+ self._mark(x, y)
593
+
594
+ def mark(self, x, y, settings=None, power=None, speed=None):
595
+ """
596
+ Mark either sets default vector settings or sets the settings based on the settings object provided.
597
+ @param x:
598
+ @param y:
599
+ @param settings:
600
+ @param power: power during switch
601
+ @param speed: speed for marking
602
+ @return:
603
+ """
604
+ self._set_vector_mode()
605
+ if settings is not None:
606
+ self.set_settings(settings)
607
+ if power is not None:
608
+ self._set_power = power
609
+ if speed is not None:
610
+ self._set_speed = speed
611
+ self._mark(x, y)
612
+
613
+ def _mark(self, x, y):
614
+ dx = int(round(x - self._last_x))
615
+ dy = int(round(y - self._last_y))
616
+ if dx == 0 and dy == 0:
617
+ return
618
+ self._commit_settings()
619
+ if self._relative:
620
+ self(f"PD{dy},{dx}")
621
+ self._last_x += dx
622
+ self._last_y += dy
623
+ else:
624
+ x = int(round(x))
625
+ y = int(round(y))
626
+ self(f"PD{y},{x}")
627
+ self._last_x, self._last_y = x, y
628
+
629
+ def goto(self, x, y):
630
+ """
631
+ Goto position.
632
+ @param x:
633
+ @param y:
634
+ @return:
635
+ """
636
+ self.goto_rel(x, y)
637
+
638
+ def goto_abs(self, x, y):
639
+ """
640
+ Goto given absolute coords value.
641
+ @param x:
642
+ @param y:
643
+ @return:
644
+ """
645
+ self._set_move_mode()
646
+ self._relative = False
647
+ self._goto(x, y)
648
+
649
+ def goto_rel(self, x, y):
650
+ """
651
+ Positions are given in absolute coords, but the motion sent to the laser is relative.
652
+ @param x:
653
+ @param y:
654
+ @return:
655
+ """
656
+ self._set_move_mode()
657
+ self._goto(x, y)
658
+
659
+ def _goto(self, x, y):
660
+ dx = int(round(x - self._last_x))
661
+ dy = int(round(y - self._last_y))
662
+ if dx == 0 and dy == 0:
663
+ return
664
+ self._commit_settings()
665
+ if self._relative:
666
+ self(f"PU{dy},{dx}")
667
+ self._last_x += dx
668
+ self._last_y += dy
669
+ else:
670
+ x = int(round(x))
671
+ y = int(round(y))
672
+ self(f"PU{y},{x}")
673
+ self._last_x, self._last_y = x, y
674
+
675
+ def set_xy(self, x, y):
676
+ self.realtime_job()
677
+ self.mode = "jog"
678
+ self.goto_rel(x, y)
679
+ self.close_job()
680
+
681
+ def get_last_xy(self):
682
+ return self._last_x, self._last_y
683
+
684
+ #######################
685
+ # Command Shortcuts
686
+ #######################
687
+
688
+ def move_frame(self, file_index):
689
+ self.realtime_job()
690
+ self.mode = "move_frame"
691
+ self(f"ZK{file_index}")
692
+ self.close_job()
693
+
694
+ def draw_frame(self, file_index):
695
+ self.realtime_job()
696
+ self.mode = "draw_frame"
697
+ self(f"ZH{file_index}")
698
+ self.close_job()
699
+
700
+ def replay(self, file_index):
701
+ self.realtime_job()
702
+ self.mode = "replay"
703
+ self(f"ZG{file_index}")
704
+ self.close_job()
705
+
706
+ def home_speeds(self, x_speed, y_speed, m_speed):
707
+ self.realtime_job()
708
+ self.mode = "home_speeds"
709
+ self(f"VX{int(round(x_speed / 10))}")
710
+ self(f"VY{int(round(y_speed / 10))}")
711
+ self(f"VM{int(round(m_speed / 10))}")
712
+ self.close_job()
713
+
714
+ def z_relative(self, amount, speed=100):
715
+ self.realtime_job()
716
+ self.mode = "zmove"
717
+ self(f"CV{int(round(speed / 10))}")
718
+ self(f"CR{int(round(amount))}")
719
+ self.close_job()
720
+
721
+ def z_absolute(self, z_position, speed=100):
722
+ self.realtime_job()
723
+ self.mode = "zmove"
724
+ self(f"CV{int(round(speed / 10))}")
725
+ self(f"CU{int(round(z_position))}")
726
+ self.close_job()
727
+
728
+ def w_relative(self, amount, speed=100):
729
+ self.realtime_job()
730
+ self.mode = "wmove"
731
+ self(f"WV{int(round(speed / 10))}")
732
+ self(f"WR{int(round(amount))}")
733
+ self.close_job()
734
+
735
+ def w_absolute(self, w_position, speed=100):
736
+ self.realtime_job()
737
+ self.mode = "wmove"
738
+ self(f"WV{int(round(speed / 10))}")
739
+ self(f"WU{int(round(w_position))}")
740
+ self.close_job()
741
+
742
+ def pulse(self, pulse_time_ms):
743
+ self.realtime_job()
744
+ self.dwell(pulse_time_ms)
745
+ self.close_job()
746
+
747
+ def home(self):
748
+ self.realtime_job()
749
+ self.mode = "home"
750
+ self("RS")
751
+ self._last_x = 0
752
+ self._last_y = 0
753
+ self.close_job()
754
+
755
+ def origin(self):
756
+ self.realtime_job()
757
+ self.mode = "origin"
758
+ self.goto(0, 0)
759
+ self.close_job()
760
+
761
+ def abort(self):
762
+ self.realtime_job()
763
+ self.mode = "abort"
764
+ self("ZQ")
765
+ self.close_job()
766
+
767
+ def wait(self, time_in_ms):
768
+ while time_in_ms > 255:
769
+ time_in_ms -= 255
770
+ self("TX255")
771
+ if time_in_ms > 0:
772
+ self(f"TX{int(round(time_in_ms))}")
773
+
774
+ def dwell(self, time_in_ms, settings=None):
775
+ self.mode = "pulse"
776
+ if self.service.pwm_enabled:
777
+ self._set_pwm_freq = self.service.pwm_frequency
778
+ if settings is not None:
779
+ # Settings based speed, power, pwm_freq
780
+ if "power" in settings:
781
+ self._set_power = settings.get("power")
782
+ if "pwm_frequency" in settings:
783
+ self._set_pwm_freq = settings.get("pwm_frequency")
784
+ # self.set_settings(settings)
785
+ else:
786
+ self._set_power = 1000.0
787
+ self._set_power *= self.service.max_pulse_power / 100.0
788
+ self._commit_pwmfreq()
789
+ self._commit_power()
790
+ while time_in_ms > 255:
791
+ time_in_ms -= 255
792
+ self("TO255")
793
+ if time_in_ms > 0:
794
+ self(f"TO{int(round(time_in_ms))}")
795
+
796
+ def pause(self):
797
+ self.realtime_job()
798
+ self.mode = "pause"
799
+ self("ZT")
800
+ self.close_job()
801
+ self.paused = True
802
+
803
+ def resume(self):
804
+ self.realtime_job()
805
+ self.mode = "resume"
806
+ self("ZG")
807
+ self.close_job()
808
+ self.paused = False
809
+
810
+ def wait_finished(self):
811
+ # No known method to force the laser to single state.
812
+ pass
813
+
814
+ def init_laser(self):
815
+ self.usb_log("Ready")
816
+
817
+ def power(self, power):
818
+ """
819
+ Accepts power in percent, automatically converts to power_ratio
820
+
821
+ @param power:
822
+ @return:
823
+ """
824
+ self._set_power = power
825
+
826
+ #######################
827
+ # Commit settings
828
+ #######################
829
+
830
+ def _commit_settings(self):
831
+ if self._set_speed is not None:
832
+ # If set speed is set, we set all the various speed chart lookup properties.
833
+ settings = self._get_chart_settings_for_speed(self._set_speed)
834
+ self._set_corner_speed = int(round(settings["corner_speed"]))
835
+ self._set_acceleration_length = int(round(settings["acceleration_length"]))
836
+ self._set_backlash = int(round(settings["backlash"]))
837
+
838
+ self.commit_mode()
839
+ self._commit_pwmfreq()
840
+ self._commit_dc()
841
+ if self._mode != "raster":
842
+ self._commit_pen()
843
+ self._commit_power()
844
+ if self._mode == "raster":
845
+ self._commit_raster()
846
+ self._commit_pen()
847
+ if self._mode != "vector":
848
+ self._commit_corner_speed()
849
+ self._commit_acceleration_length()
850
+ self._commit_speed()
851
+ self._commit_relative_mode()
852
+
853
+ def commit_mode(self):
854
+ if self._set_mode is None:
855
+ # Quick fail.
856
+ return
857
+ new_mode = self._set_mode
858
+ self._set_mode = None
859
+ if new_mode != self._mode:
860
+ self._mode = new_mode
861
+
862
+ # Old speed and power are void on mode change.
863
+ self._speed = None
864
+ self._power = None
865
+ self._corner_speed = None
866
+ self._acceleration_length = None
867
+ self._backlash = None
868
+
869
+ #######################
870
+ # Commit DC Info
871
+ #######################
872
+
873
+ def _commit_cut_dc(self):
874
+ if self._set_cut_dc is None and self._cut_dc is not None:
875
+ # Quick Fail.
876
+ return
877
+
878
+ # Fetch Requested.
879
+ new_dc = self._set_cut_dc
880
+ self._set_cut_dc = None
881
+ if new_dc is None:
882
+ # Nothing set, set default.
883
+ new_dc = self.service.cut_dc
884
+ if new_dc != self._cut_dc:
885
+ # DC is different
886
+ self._cut_dc = new_dc
887
+ self(f"VP{self._cut_dc}")
888
+
889
+ def _commit_move_dc(self):
890
+ if self._set_move_dc is None and self._move_dc is not None:
891
+ # Quick Fail.
892
+ return
893
+
894
+ # Fetch Requested.
895
+ new_dc = self._set_move_dc
896
+ self._set_move_dc = None
897
+ if new_dc is None:
898
+ # Nothing set, set default.
899
+ new_dc = self.service.move_dc
900
+ if new_dc != self._move_dc:
901
+ # DC is different
902
+ self._move_dc = new_dc
903
+ self(f"VK{self._move_dc}")
904
+
905
+ def _commit_dc(self):
906
+ self._commit_cut_dc()
907
+ self._commit_move_dc()
908
+
909
+ #######################
910
+ # Commit Pen
911
+ #######################
912
+
913
+ def _commit_pen(self):
914
+ if self._set_pen is None and self._pen is not None:
915
+ # Quick Fail.
916
+ return
917
+
918
+ # Fetch Requested.
919
+ new_pen = self._set_pen
920
+ self._set_pen = None
921
+ if new_pen is None:
922
+ # Nothing set, set default.
923
+ new_pen = 0
924
+ if new_pen != self._pen:
925
+ # PEN is different
926
+ self._pen = new_pen
927
+ self(f"SP{self._pen}")
928
+
929
+ #######################
930
+ # Commit Power
931
+ #######################
932
+
933
+ def _map_power(self, power):
934
+ if self.mode == "pulse":
935
+ power /= 10.0
936
+ return int(round(power))
937
+ power /= 1000.0 # Scale to 0-1
938
+ power *= self.service.max_power # Scale by max power %
939
+ power *= 255.0 / 100.0 # range between 000 and 255
940
+ if power > 255:
941
+ return 255
942
+ if power <= 0:
943
+ return 0
944
+ return int(round(power))
945
+
946
+ def _commit_power(self):
947
+ """
948
+ Write power information. If the _set_power is set then it takes priority. Otherwise, the power remains set to
949
+ what it was previously set to. If no power is set, then power is set to the default cut power.
950
+
951
+ @return:
952
+ """
953
+ if self._set_power is None and self._power is not None:
954
+ return # quick fail.
955
+
956
+ new_power = self._set_power
957
+ self._set_power = None
958
+
959
+ if new_power is None:
960
+ return
961
+
962
+ # Premap the power setting.
963
+ new_power = self._map_power(new_power)
964
+
965
+ if new_power != self._power:
966
+ # Already set power is not the new_power setting.
967
+ self._power = new_power
968
+ self(f"DA{new_power}")
969
+
970
+ def _commit_pwmfreq(self):
971
+ """
972
+ Write pwm frequency information.
973
+ @return:
974
+ """
975
+ if self._set_pwm_freq is None and self._pwm_frequency is not None:
976
+ # Quick Fail.
977
+ return
978
+
979
+ # Fetch Requested.
980
+ new_freq = self._set_pwm_freq
981
+ self._set_pwm_freq = None
982
+ if new_freq is None:
983
+ return
984
+ if new_freq != self._pwm_frequency:
985
+ # Frequency is needed, and different
986
+ self._pwm_frequency = new_freq
987
+ self(f"PL{self._pwm_frequency}")
988
+
989
+ #######################
990
+ # Commit speed settings
991
+ #######################
992
+
993
+ def _commit_speed(self):
994
+ if self._set_speed is None and self._speed is not None:
995
+ return
996
+ new_speed = self._set_speed
997
+ self._set_speed = None
998
+ if new_speed is not None and new_speed != self._speed:
999
+ self._speed = new_speed
1000
+ if self._mode == "vector":
1001
+ self(f"VS{self._map_vector_speed(new_speed)}")
1002
+ else:
1003
+ self(f"VS{self._map_raster_speed(new_speed)}")
1004
+
1005
+ def _get_chart_settings_for_speed(self, speed):
1006
+ """
1007
+ Get charted settings for a particular speed. Given a speed this provides other required settings for this
1008
+ same speed.
1009
+
1010
+ @param speed:
1011
+ @return:
1012
+ """
1013
+ chart = self.service.speedchart
1014
+ smallest_difference = float("inf")
1015
+ closest_index = None
1016
+ for i, c in enumerate(chart):
1017
+ chart_speed = c.get("speed", 0)
1018
+ delta_speed = chart_speed - speed
1019
+ if chart_speed > speed and smallest_difference > delta_speed:
1020
+ smallest_difference = delta_speed
1021
+ closest_index = i
1022
+ if closest_index is not None:
1023
+ return chart[closest_index]
1024
+ return chart[-1]
1025
+
1026
+ def _map_raster_speed(self, speed):
1027
+ v = int(round(speed / 10))
1028
+ if v == 0:
1029
+ v = 1
1030
+ return v
1031
+
1032
+ def _map_vector_speed(self, speed):
1033
+ if speed >= 93:
1034
+ return int(round(speed / 10))
1035
+ if speed >= 15:
1036
+ return 162 + int(round(speed))
1037
+ if speed >= 5:
1038
+ return 147 + int(round(speed * 2))
1039
+ if speed >= 1:
1040
+ return 132 + int(round(speed * 5))
1041
+ else:
1042
+ return 127 + int(round(speed * 10))
1043
+
1044
+ #######################
1045
+ # Commit Speed Chart
1046
+ #######################
1047
+
1048
+ def _commit_corner_speed(self):
1049
+ if self._set_corner_speed is None and self._corner_speed is not None:
1050
+ # Quick Fail.
1051
+ return
1052
+
1053
+ # Fetch Requested.
1054
+ new_corner_speed = self._set_corner_speed
1055
+ self._set_corner_speed = None
1056
+ if new_corner_speed is None:
1057
+ # Nothing set, set default.
1058
+ return
1059
+ if new_corner_speed != self._corner_speed:
1060
+ # Corner_speed is different
1061
+ self._corner_speed = new_corner_speed
1062
+ self(f"VQ{int(round(self._corner_speed))}")
1063
+
1064
+ def _commit_acceleration_length(self):
1065
+ if (
1066
+ self._set_acceleration_length is None
1067
+ and self._acceleration_length is not None
1068
+ ):
1069
+ # Quick Fail.
1070
+ return
1071
+
1072
+ # Fetch Requested.
1073
+ new_acceleration_length = self._set_acceleration_length
1074
+ self._set_acceleration_length = None
1075
+ if new_acceleration_length is None:
1076
+ # Nothing set, set default.
1077
+ return
1078
+ if new_acceleration_length != self._acceleration_length:
1079
+ # Acceleration Length is different
1080
+ self._acceleration_length = new_acceleration_length
1081
+ self(f"VJ{int(round(self._acceleration_length))}")
1082
+
1083
+ #######################
1084
+ # Commit Relative Mode
1085
+ #######################
1086
+
1087
+ def _commit_relative_mode(self):
1088
+ if self._set_relative is None and self._relative is not None:
1089
+ return
1090
+ new_relative = self._set_relative
1091
+ self._set_relative = None
1092
+
1093
+ if new_relative is None:
1094
+ new_relative = True
1095
+
1096
+ if new_relative != self._relative:
1097
+ self._relative = new_relative
1098
+ if self._relative:
1099
+ self("PR")
1100
+ else:
1101
+ self("PA")
1102
+
1103
+ #######################
1104
+ # Commit Raster
1105
+ #######################
1106
+
1107
+ def _commit_raster_bitdepth(self):
1108
+ if self._set_bit_depth is None and self._bit_depth is not None:
1109
+ # Quick Fail.
1110
+ return
1111
+
1112
+ # Fetch Requested.
1113
+ new_bitdepth = self._set_bit_depth
1114
+ self._set_bit_depth = None
1115
+ if new_bitdepth is None:
1116
+ # Nothing set, set default.
1117
+ return
1118
+ if new_bitdepth != self._bit_depth:
1119
+ # Bitdepth is different
1120
+ self._bit_depth = new_bitdepth
1121
+ self(f"BT{self._bit_depth}")
1122
+
1123
+ def _commit_raster_bitwidth(self):
1124
+ if self._set_bit_width is None and self._bit_width is not None:
1125
+ # Quick Fail.
1126
+ return
1127
+
1128
+ # Fetch Requested.
1129
+ new_bitwidth = self._set_bit_width
1130
+ self._set_bit_width = None
1131
+ if new_bitwidth is None:
1132
+ # Nothing set, set default.
1133
+ return
1134
+ if new_bitwidth != self._bit_width:
1135
+ # Bitdepth is different
1136
+ self._bit_width = new_bitwidth
1137
+ self(f"BD{self._bit_width}")
1138
+
1139
+ def _commit_raster_backlash(self):
1140
+ if self._set_backlash is None and self._backlash is not None:
1141
+ # Quick Fail.
1142
+ return
1143
+
1144
+ # Fetch Requested.
1145
+ new_backlash = self._set_backlash
1146
+ self._set_backlash = None
1147
+ if new_backlash is None:
1148
+ # Nothing set, set default.
1149
+ return
1150
+ if new_backlash != self._backlash:
1151
+ # backlash is different
1152
+ self._backlash = new_backlash
1153
+ self(f"BC{self._backlash}")
1154
+
1155
+ def _commit_raster(self):
1156
+ self._commit_raster_bitdepth()
1157
+ self._commit_raster_backlash()
1158
+ self._commit_raster_bitwidth()