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,1512 +1,1566 @@
1
- """
2
- Galvo Controller
3
-
4
- The balor controller takes low level lmc galvo commands and converts them into lists and shorts commands to send
5
- to the hardware controller.
6
- """
7
-
8
- import struct
9
- import threading
10
- import time
11
- from copy import copy
12
-
13
- from meerk40t.balormk.mock_connection import MockConnection
14
- from meerk40t.balormk.usb_connection import USBConnection
15
-
16
- DRIVER_STATE_RAPID = 0
17
- DRIVER_STATE_LIGHT = 1
18
- DRIVER_STATE_PROGRAM = 2
19
- DRIVER_STATE_RAW = 3
20
-
21
- nop = [0x02, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
22
- empty = bytearray(nop * 0x100)
23
-
24
- listJumpTo = 0x8001
25
- listEndOfList = 0x8002
26
- listLaserOnPoint = 0x8003
27
- listDelayTime = 0x8004
28
- listMarkTo = 0x8005
29
- listJumpSpeed = 0x8006
30
- listLaserOnDelay = 0x8007
31
- listLaserOffDelay = 0x8008
32
- listMarkFreq = 0x800A
33
- listMarkPowerRatio = 0x800B
34
- listMarkSpeed = 0x800C
35
- listJumpDelay = 0x800D
36
- listPolygonDelay = 0x800F
37
- listWritePort = 0x8011
38
- listMarkCurrent = 0x8012
39
- listMarkFreq2 = 0x8013
40
- listFlyEnable = 0x801A
41
- listQSwitchPeriod = 0x801B
42
- listDirectLaserSwitch = 0x801C
43
- listFlyDelay = 0x801D
44
- listSetCo2FPK = 0x801E
45
- listFlyWaitInput = 0x801F
46
- listFiberOpenMO = 0x8021
47
- listWaitForInput = 0x8022
48
- listChangeMarkCount = 0x8023
49
- listSetWeldPowerWave = 0x8024
50
- listEnableWeldPowerWave = 0x8025
51
- listFiberYLPMPulseWidth = 0x8026
52
- listFlyEncoderCount = 0x8028
53
- listSetDaZWord = 0x8029
54
- listJptSetParam = 0x8050
55
- listReadyMark = 0x8051
56
-
57
- DisableLaser = 0x0002
58
- EnableLaser = 0x0004
59
- ExecuteList = 0x0005
60
- SetPwmPulseWidth = 0x0006
61
- GetVersion = 0x0007
62
- GetSerialNo = 0x0009
63
- GetListStatus = 0x000A
64
- GetPositionXY = 0x000C
65
- GotoXY = 0x000D
66
- LaserSignalOff = 0x000E
67
- LaserSignalOn = 0x000F
68
- WriteCorLine = 0x0010
69
- ResetList = 0x0012
70
- RestartList = 0x0013
71
- WriteCorTable = 0x0015
72
- SetControlMode = 0x0016
73
- SetDelayMode = 0x0017
74
- SetMaxPolyDelay = 0x0018
75
- SetEndOfList = 0x0019
76
- SetFirstPulseKiller = 0x001A
77
- SetLaserMode = 0x001B
78
- SetTiming = 0x001C
79
- SetStandby = 0x001D
80
- SetPwmHalfPeriod = 0x001E
81
- StopExecute = 0x001F
82
- StopList = 0x0020
83
- WritePort = 0x0021
84
- WriteAnalogPort1 = 0x0022
85
- WriteAnalogPort2 = 0x0023
86
- WriteAnalogPortX = 0x0024
87
- ReadPort = 0x0025
88
- SetAxisMotionParam = 0x0026
89
- SetAxisOriginParam = 0x0027
90
- AxisGoOrigin = 0x0028
91
- MoveAxisTo = 0x0029
92
- GetAxisPos = 0x002A
93
- GetFlyWaitCount = 0x002B
94
- GetMarkCount = 0x002D
95
- SetFpkParam2 = 0x002E
96
- Fiber_SetMo = 0x0033 # open and close set by value
97
- Fiber_GetStMO_AP = 0x0034
98
- EnableZ = 0x003A
99
- DisableZ = 0x0039
100
- SetZData = 0x003B
101
- SetSPISimmerCurrent = 0x003C
102
- SetFpkParam = 0x0062
103
- Reset = 0x0040
104
- GetFlySpeed = 0x0038
105
- FiberPulseWidth = 0x002F
106
- FiberGetConfigExtend = 0x0030
107
- InputPort = 0x0031 # ClearLockInputPort calls 0x04, then if EnableLockInputPort 0x02 else 0x01, GetLockInputPort
108
- GetMarkTime = 0x0041
109
- GetUserData = 0x0036
110
- SetFlyRes = 0x0032
111
-
112
- list_command_lookup = {
113
- 0x8001: "listJumpTo",
114
- 0x8002: "listEndOfList",
115
- 0x8003: "listLaserOnPoint",
116
- 0x8004: "listDelayTime",
117
- 0x8005: "listMarkTo",
118
- 0x8006: "listJumpSpeed",
119
- 0x8007: "listLaserOnDelay",
120
- 0x8008: "listLaserOffDelay",
121
- 0x800A: "listMarkFreq",
122
- 0x800B: "listMarkPowerRatio",
123
- 0x800C: "listMarkSpeed",
124
- 0x800D: "listJumpDelay",
125
- 0x800F: "listPolygonDelay",
126
- 0x8011: "listWritePort",
127
- 0x8012: "listMarkCurrent",
128
- 0x8013: "listMarkFreq2",
129
- 0x801A: "listFlyEnable",
130
- 0x801B: "listQSwitchPeriod",
131
- 0x801C: "listDirectLaserSwitch",
132
- 0x801D: "listFlyDelay",
133
- 0x801E: "listSetCo2FPK",
134
- 0x801F: "listFlyWaitInput",
135
- 0x8021: "listFiberOpenMO",
136
- 0x8022: "listWaitForInput",
137
- 0x8023: "listChangeMarkCount",
138
- 0x8024: "listSetWeldPowerWave",
139
- 0x8025: "listEnableWeldPowerWave",
140
- 0x8026: "listFiberYLPMPulseWidth",
141
- 0x8028: "listFlyEncoderCount",
142
- 0x8029: "listSetDaZWord",
143
- 0x8050: "listJptSetParam",
144
- 0x8051: "listReadyMark",
145
- }
146
-
147
- single_command_lookup = {
148
- 0x0002: "DisableLaser",
149
- 0x0004: "EnableLaser",
150
- 0x0005: "ExecuteList",
151
- 0x0006: "SetPwmPulseWidth",
152
- 0x0007: "GetVersion",
153
- 0x0009: "GetSerialNo",
154
- 0x000A: "GetListStatus",
155
- 0x000C: "GetPositionXY",
156
- 0x000D: "GotoXY",
157
- 0x000E: "LaserSignalOff",
158
- 0x000F: "LaserSignalOn",
159
- 0x0010: "WriteCorLine",
160
- 0x0012: "ResetList",
161
- 0x0013: "RestartList",
162
- 0x0015: "WriteCorTable",
163
- 0x0016: "SetControlMode",
164
- 0x0017: "SetDelayMode",
165
- 0x0018: "SetMaxPolyDelay",
166
- 0x0019: "SetEndOfList",
167
- 0x001A: "SetFirstPulseKiller",
168
- 0x001B: "SetLaserMode",
169
- 0x001C: "SetTiming",
170
- 0x001D: "SetStandby",
171
- 0x001E: "SetPwmHalfPeriod",
172
- 0x001F: "StopExecute",
173
- 0x0020: "StopList",
174
- 0x0021: "WritePort",
175
- 0x0022: "WriteAnalogPort1",
176
- 0x0023: "WriteAnalogPort2",
177
- 0x0024: "WriteAnalogPortX",
178
- 0x0025: "ReadPort",
179
- 0x0026: "SetAxisMotionParam",
180
- 0x0027: "SetAxisOriginParam",
181
- 0x0028: "AxisGoOrigin",
182
- 0x0029: "MoveAxisTo",
183
- 0x002A: "GetAxisPos",
184
- 0x002B: "GetFlyWaitCount",
185
- 0x002D: "GetMarkCount",
186
- 0x002E: "SetFpkParam2",
187
- 0x0033: "Fiber_SetMo",
188
- 0x0034: "Fiber_GetStMO_AP",
189
- 0x003A: "EnableZ",
190
- 0x0039: "DisableZ",
191
- 0x003B: "SetZData",
192
- 0x003C: "SetSPISimmerCurrent",
193
- 0x0062: "SetFpkParam",
194
- 0x0040: "Reset",
195
- 0x0038: "GetFlySpeed",
196
- 0x002F: "FiberPulseWidth",
197
- 0x0030: "FiberGetConfigExtend",
198
- 0x0031: "InputPort",
199
- 0x0041: "GetMarkTime",
200
- 0x0036: "GetUserData",
201
- 0x0032: "SetFlyRes",
202
- }
203
-
204
- BUSY = 0x04
205
- READY = 0x20
206
- AXIS = 0x40
207
-
208
-
209
- def _bytes_to_words(r):
210
- b0 = r[1] << 8 | r[0]
211
- b1 = r[3] << 8 | r[2]
212
- b2 = r[5] << 8 | r[4]
213
- b3 = r[7] << 8 | r[6]
214
- return b0, b1, b2, b3
215
-
216
-
217
- class GalvoController:
218
- """
219
- Galvo controller is tasked with sending queued data to the controller board and ensuring that the connection to the
220
- controller board is established to perform these actions.
221
-
222
- This should serve as a next generation command sequencer written from scratch for galvo lasers. The goal is to
223
- provide all the given commands in a coherent queue structure which provides correct sequences between list and
224
- single commands.
225
- """
226
-
227
- def __init__(
228
- self,
229
- service,
230
- x=0x8000,
231
- y=0x8000,
232
- mark_speed=None,
233
- goto_speed=None,
234
- light_speed=None,
235
- dark_speed=None,
236
- force_mock=False,
237
- ):
238
- self.service = service
239
- self.force_mock = force_mock
240
- self.is_shutdown = False # Shutdown finished.
241
-
242
- name = self.service.label
243
- self.usb_log = service.channel(f"{name}/usb", buffer_size=500)
244
- self.usb_log.watch(lambda e: service.signal("pipe;usb_status", e))
245
-
246
- self.connection = None
247
- self._is_opening = False
248
- self._abort_open = False
249
- self._disable_connect = False
250
-
251
- self._light_bit = service.setting(int, "light_pin", 8)
252
- self._foot_bit = service.setting(int, "footpedal_pin", 15)
253
-
254
- self._last_x = x
255
- self._last_y = y
256
- self._mark_speed = mark_speed
257
- self._goto_speed = goto_speed
258
- self._light_speed = light_speed
259
- self._dark_speed = dark_speed
260
-
261
- self._ready = None
262
- self._speed = None
263
- self._travel_speed = None
264
- self._frequency = None
265
- self._power = None
266
- self._pulse_width = None
267
- self._fpk = None
268
-
269
- self._delay_jump = None
270
- self._delay_on = None
271
- self._delay_off = None
272
- self._delay_poly = None
273
- self._delay_end = None
274
-
275
- self._port_bits = 0
276
- self._machine_index = 0
277
-
278
- self.mode = DRIVER_STATE_RAPID
279
- self._list_lock = threading.RLock()
280
- self._active_list = None
281
- self._active_index = 0
282
- self._list_executing = False
283
- self._number_of_list_packets = 0
284
- self.paused = False
285
-
286
- @property
287
- def source(self):
288
- return self.service.source
289
-
290
- @property
291
- def state(self):
292
- if self.mode == DRIVER_STATE_RAPID:
293
- return "idle", "idle"
294
- if self.paused:
295
- return "hold", "paused"
296
- if self.mode == DRIVER_STATE_RAW:
297
- return "busy", "raw"
298
- if self.mode == DRIVER_STATE_LIGHT:
299
- return "busy", "light"
300
- if self.mode == DRIVER_STATE_PROGRAM:
301
- return "busy", "program"
302
-
303
- def set_disable_connect(self, status):
304
- self._disable_connect = status
305
-
306
- def added(self):
307
- pass
308
-
309
- def service_detach(self):
310
- pass
311
-
312
- def shutdown(self, *args, **kwargs):
313
- self.is_shutdown = True
314
-
315
- @property
316
- def connected(self):
317
- if self.connection is None:
318
- return False
319
- return self.connection.is_open(self._machine_index)
320
-
321
- @property
322
- def is_connecting(self):
323
- if self.connection is None:
324
- return False
325
- return self._is_opening
326
-
327
- def abort_connect(self):
328
- self._abort_open = True
329
- self.usb_log("Connect Attempts Aborted")
330
-
331
- def disconnect(self):
332
- try:
333
- self.connection.close(self._machine_index)
334
- except (ConnectionError, ConnectionRefusedError, AttributeError):
335
- pass
336
- self.connection = None
337
- # Reset error to allow another attempt
338
- self.set_disable_connect(False)
339
-
340
- def connect_if_needed(self):
341
- if self._disable_connect:
342
- # After many failures automatic connects are disabled. We require a manual connection.
343
- self.abort_connect()
344
- self.connection = None
345
- raise ConnectionRefusedError(
346
- "LMC was unreachable. Explicit connect required."
347
- )
348
- if self.connection is None:
349
- if self.service.setting(bool, "mock", False) or self.force_mock:
350
- self.connection = MockConnection(self.usb_log)
351
- name = self.service.label
352
- self.connection.send = self.service.channel(f"{name}/send")
353
- self.connection.recv = self.service.channel(f"{name}/recv")
354
- else:
355
- self.connection = USBConnection(self.usb_log)
356
- self._is_opening = True
357
- self._abort_open = False
358
- count = 0
359
- while not self.connection.is_open(self._machine_index):
360
- try:
361
- if self.connection.open(self._machine_index) < 0:
362
- raise ConnectionError
363
- self.init_laser()
364
- except (ConnectionError, ConnectionRefusedError):
365
- if count == 0:
366
- self.service("clone_init\n")
367
- time.sleep(0.3)
368
- count += 1
369
- # self.usb_log(f"Error-Routine pass #{count}")
370
- if self.is_shutdown or self._abort_open:
371
- self._is_opening = False
372
- self._abort_open = False
373
- return
374
- if self.connection.is_open(self._machine_index):
375
- self.connection.close(self._machine_index)
376
- if count >= 10:
377
- # We have failed too many times.
378
- self._is_opening = False
379
- self.set_disable_connect(True)
380
- self.usb_log("Could not connect to the LMC controller.")
381
- self.usb_log("Automatic connections disabled.")
382
- raise ConnectionRefusedError(
383
- "Could not connect to the LMC controller."
384
- )
385
- time.sleep(0.3)
386
- continue
387
- self._is_opening = False
388
- self._abort_open = False
389
-
390
- def send(self, data, read=True):
391
- if self.is_shutdown:
392
- return -1, -1, -1, -1
393
- self.connect_if_needed()
394
- try:
395
- self.connection.write(self._machine_index, data)
396
- except ConnectionError:
397
- return -1, -1, -1, -1
398
- if read:
399
- try:
400
- r = self.connection.read(self._machine_index)
401
- return struct.unpack("<4H", r)
402
- except ConnectionError:
403
- return -1, -1, -1, -1
404
-
405
- def status(self):
406
- b0, b1, b2, b3 = self.get_version()
407
- return b3
408
-
409
- #######################
410
- # MODE SHIFTS
411
- #######################
412
-
413
- def raw_mode(self):
414
- self.mode = DRIVER_STATE_RAW
415
-
416
- def rapid_mode(self):
417
- if self.mode == DRIVER_STATE_RAPID:
418
- return
419
- self.list_end_of_list() # Ensure at least one list_end_of_list
420
- self._list_end()
421
- if not self._list_executing and self._number_of_list_packets:
422
- # If we never ran the list, and we sent some lists.
423
- self.execute_list()
424
- self._list_executing = False
425
- self._number_of_list_packets = 0
426
- self.wait_idle()
427
- if self.source == "fiber":
428
- self.set_fiber_mo(0)
429
- self.port_off(bit=0)
430
- self.write_port()
431
- marktime = self.get_mark_time()
432
- self.service.signal("galvo;marktime", marktime)
433
- self.usb_log(f"Time taken for list execution: {marktime}")
434
- self.mode = DRIVER_STATE_RAPID
435
-
436
- def raster_mode(self):
437
- self.program_mode()
438
-
439
- def program_mode(self):
440
- if self.mode == DRIVER_STATE_PROGRAM:
441
- return
442
- if self.mode == DRIVER_STATE_LIGHT:
443
- self.mode = DRIVER_STATE_PROGRAM
444
- self.light_off()
445
- self.port_on(bit=0)
446
- self.write_port()
447
- if self.source == "fiber":
448
- self.set_fiber_mo(1)
449
- else:
450
- self.mode = DRIVER_STATE_PROGRAM
451
- self.reset_list()
452
- self.port_on(bit=0)
453
- self.write_port()
454
- if self.source == "fiber":
455
- self.set_fiber_mo(1)
456
- self._ready = None
457
- self._speed = None
458
- self._travel_speed = None
459
- self._frequency = None
460
- self._power = None
461
- self._pulse_width = None
462
-
463
- self._delay_jump = None
464
- self._delay_on = None
465
- self._delay_off = None
466
- self._delay_poly = None
467
- self._delay_end = None
468
- self.list_ready()
469
- if self.service.delay_openmo != 0 and self.source == "fiber":
470
- self.list_delay_time(int(self.service.delay_openmo * 100))
471
- self.list_write_port()
472
- self.list_jump_speed(self.service.default_rapid_speed)
473
-
474
- def light_mode(self):
475
- if self.mode == DRIVER_STATE_LIGHT:
476
- return
477
- if self.mode == DRIVER_STATE_PROGRAM:
478
- if self.source == "fiber":
479
- self.set_fiber_mo(0)
480
- self.port_off(bit=0)
481
- self.port_on(self._light_bit)
482
- self.write_port()
483
- else:
484
- self._ready = None
485
- self._speed = None
486
- self._travel_speed = None
487
- self._frequency = None
488
- self._power = None
489
- self._pulse_width = None
490
-
491
- self._delay_jump = None
492
- self._delay_on = None
493
- self._delay_off = None
494
- self._delay_poly = None
495
- self._delay_end = None
496
-
497
- self.reset_list()
498
- self.list_ready()
499
- self.port_off(bit=0)
500
- self.port_on(self._light_bit)
501
- self.list_write_port()
502
- self.mode = DRIVER_STATE_LIGHT
503
-
504
- #######################
505
- # LIST APPENDING OPERATIONS
506
- #######################
507
-
508
- def _list_end(self):
509
- if not self._active_list or not self._active_index:
510
- # Ensure there is a list to end.
511
- return
512
- with self._list_lock:
513
- if not self._active_list or not self._active_index:
514
- # Double-gated syntax, make sure there's still that list needing ending.
515
- return
516
- self.wait_ready()
517
- while self.paused:
518
- time.sleep(0.3)
519
- self.send(self._active_list, False)
520
- if self.mode != DRIVER_STATE_RAW:
521
- self.set_end_of_list(0)
522
- self._number_of_list_packets += 1
523
- self._active_list = None
524
- self._active_index = 0
525
- if self._number_of_list_packets > 2 and not self._list_executing:
526
- if self.mode != DRIVER_STATE_RAW:
527
- self.execute_list()
528
- self._list_executing = True
529
-
530
- def _list_new(self):
531
- with self._list_lock:
532
- self._active_list = copy(empty)
533
- self._active_index = 0
534
-
535
- def _list_write(self, command, v1=0, v2=0, v3=0, v4=0, v5=0):
536
- with self._list_lock:
537
- if self._active_index >= 0xC00:
538
- self._list_end()
539
- if self._active_list is None:
540
- self._list_new()
541
- index = self._active_index
542
- self._active_list[index : index + 12] = struct.pack(
543
- "<6H", int(command), int(v1), int(v2), int(v3), int(v4), int(v5)
544
- )
545
- self._active_index += 12
546
-
547
- def _command(self, command, v1=0, v2=0, v3=0, v4=0, v5=0, read=True):
548
- cmd = struct.pack(
549
- "<6H", int(command), int(v1), int(v2), int(v3), int(v4), int(v5)
550
- )
551
- return self.send(cmd, read=read)
552
-
553
- def raw_write(self, command, v1=0, v2=0, v3=0, v4=0, v5=0):
554
- """
555
- Write this raw command to value. Sends the correct way based on command value.
556
-
557
- @return:
558
- """
559
- if command >= 0x8000:
560
- self._list_write(command, v1, v2, v3, v4, v5)
561
- else:
562
- self._command(command, v1, v2, v3, v4, v5)
563
-
564
- def raw_clear(self):
565
- self._list_new()
566
-
567
- #######################
568
- # SETS FOR PLOTLIKES
569
- #######################
570
-
571
- def set_settings(self, settings):
572
- """
573
- Sets the primary settings. Rapid, frequency, speed, and timings.
574
-
575
- @param settings: The current settings dictionary
576
- @return:
577
- """
578
- if self.service.pulse_width_enabled and self.source == "fiber":
579
- # Global Pulse Width is enabled.
580
- if str(settings.get("pulse_width_enabled", False)).lower() == "true":
581
- # Local Pulse Width value is enabled.
582
- # OpFiberYLPMPulseWidth
583
-
584
- self.list_fiber_ylpm_pulse_width(
585
- int(settings.get("pulse_width", self.service.default_pulse_width))
586
- )
587
- else:
588
- # Only global is enabled, use global pulse width value.
589
- self.list_fiber_ylpm_pulse_width(self.service.default_pulse_width)
590
-
591
- if str(settings.get("rapid_enabled", False)).lower() == "true":
592
- self.list_jump_speed(
593
- float(settings.get("rapid_speed", self.service.default_rapid_speed))
594
- )
595
- else:
596
- self.list_jump_speed(self.service.default_rapid_speed)
597
-
598
- power = (
599
- float(settings.get("power", self.service.default_power)) / 10.0
600
- ) # Convert power, out of 1000
601
- frequency = float(settings.get("frequency", self.service.default_frequency))
602
- fpk = float(settings.get("fpk", self.service.default_fpk))
603
- if self.source == "fiber":
604
- self.power(power)
605
- self.frequency(frequency)
606
- elif self.source == "co2":
607
- self.frequency(frequency)
608
- self.fpk(fpk)
609
- self.power(power)
610
- self.list_mark_speed(float(settings.get("speed", self.service.default_speed)))
611
-
612
- if str(settings.get("timing_enabled", False)).lower() == "true":
613
- self.list_laser_on_delay(
614
- settings.get("delay_laser_on", self.service.delay_laser_on)
615
- )
616
- self.list_laser_off_delay(
617
- settings.get("delay_laser_off", self.service.delay_laser_off)
618
- )
619
- self.list_polygon_delay(
620
- settings.get("delay_laser_polygon", self.service.delay_polygon)
621
- )
622
- else:
623
- # Use globals
624
- self.list_laser_on_delay(self.service.delay_laser_on)
625
- self.list_laser_off_delay(self.service.delay_laser_off)
626
- self.list_polygon_delay(self.service.delay_polygon)
627
-
628
- #######################
629
- # PLOTLIKE SHORTCUTS
630
- #######################
631
-
632
- def mark(self, x, y):
633
- if x == self._last_x and y == self._last_y:
634
- return
635
- if x > 0xFFFF or x < 0 or y > 0xFFFF or y < 0:
636
- # Moves to out of range are not performed.
637
- return
638
- if self._mark_speed is not None:
639
- self.list_mark_speed(self._mark_speed)
640
- self.list_mark(x, y)
641
-
642
- def goto(self, x, y, long=None, short=None, distance_limit=None):
643
- if x == self._last_x and y == self._last_y:
644
- return
645
- if x > 0xFFFF or x < 0 or y > 0xFFFF or y < 0:
646
- # Moves to out of range are not performed.
647
- return
648
- if self._goto_speed is not None:
649
- self.list_jump_speed(self._goto_speed)
650
- self.list_jump(x, y, long=long, short=short, distance_limit=distance_limit)
651
-
652
- def light(self, x, y, long=None, short=None, distance_limit=None):
653
- if x == self._last_x and y == self._last_y:
654
- return
655
- if x > 0xFFFF or x < 0 or y > 0xFFFF or y < 0:
656
- # Moves to out of range are not performed.
657
- return
658
- if self.light_on():
659
- self.list_write_port()
660
- if self._light_speed is not None:
661
- self.list_jump_speed(self._light_speed)
662
- self.list_jump(x, y, long=long, short=short, distance_limit=distance_limit)
663
-
664
- def dark(self, x, y, long=None, short=None, distance_limit=None):
665
- if x == self._last_x and y == self._last_y:
666
- return
667
- if x > 0xFFFF or x < 0 or y > 0xFFFF or y < 0:
668
- # Moves to out of range are not performed.
669
- return
670
- if self.light_off():
671
- self.list_write_port()
672
- if self._dark_speed is not None:
673
- self.list_jump_speed(self._dark_speed)
674
- self.list_jump(x, y, long=long, short=short, distance_limit=distance_limit)
675
-
676
- def set_xy(self, x, y):
677
- distance = int(abs(complex(x, y) - complex(self._last_x, self._last_y)))
678
- if distance > 0xFFFF:
679
- distance = 0xFFFF
680
- self.goto_xy(x, y, distance=distance)
681
-
682
- def get_last_xy(self):
683
- return self._last_x, self._last_y
684
-
685
- #######################
686
- # Command Shortcuts
687
- #######################
688
-
689
- def is_busy(self):
690
- status = self.status()
691
- return bool(status & BUSY)
692
-
693
- def is_ready(self):
694
- status = self.status()
695
- return bool(status & READY)
696
-
697
- def is_axis(self):
698
- status = self.status()
699
- return bool(status & AXIS)
700
-
701
- def is_ready_and_not_busy(self):
702
- if self.mode == DRIVER_STATE_RAW:
703
- return True
704
- status = self.status()
705
- return bool(status & READY) and not bool(status & BUSY)
706
-
707
- def wait_finished(self):
708
- if self.mode == DRIVER_STATE_RAW:
709
- return
710
- while not self.is_ready_and_not_busy():
711
- time.sleep(0.01)
712
- if self.is_shutdown:
713
- return
714
-
715
- def wait_axis(self):
716
- if self.mode == DRIVER_STATE_RAW:
717
- return
718
- while self.is_axis():
719
- time.sleep(0.01)
720
- if self.is_shutdown:
721
- return
722
-
723
- def wait_ready(self):
724
- if self.mode == DRIVER_STATE_RAW:
725
- return
726
- while not self.is_ready():
727
- time.sleep(0.01)
728
- if self.is_shutdown:
729
- return
730
-
731
- def wait_idle(self):
732
- if self.mode == DRIVER_STATE_RAW:
733
- return
734
- while self.is_busy():
735
- time.sleep(0.01)
736
- if self.is_shutdown:
737
- return
738
-
739
- def abort(self, dummy_packet=True):
740
- if self.mode == DRIVER_STATE_RAW:
741
- return
742
- self.stop_execute()
743
- self.paused = False
744
- self.set_fiber_mo(0)
745
- self.reset_list()
746
- if dummy_packet:
747
- self._list_new()
748
- self.list_end_of_list() # Ensure packet is sent on end.
749
- self._list_end()
750
- if not self._list_executing:
751
- self.execute_list()
752
- self._list_executing = False
753
- self._number_of_list_packets = 0
754
- self.set_fiber_mo(0)
755
- self.port_off(bit=0)
756
- self.write_port()
757
- self.mode = DRIVER_STATE_RAPID
758
-
759
- def pause(self):
760
- if self.mode == DRIVER_STATE_RAW:
761
- return
762
- self.paused = True
763
- self.stop_list()
764
-
765
- def resume(self):
766
- if self.mode == DRIVER_STATE_RAW:
767
- return
768
- self.restart_list()
769
- self.paused = False
770
-
771
- def init_laser(self):
772
- if self.mode == DRIVER_STATE_RAW:
773
- return
774
- cor_file = self.service.corfile if self.service.corfile_enabled else None
775
- first_pulse_killer = self.service.first_pulse_killer
776
- pwm_pulse_width = self.service.pwm_pulse_width
777
- pwm_half_period = self.service.pwm_half_period
778
- standby_param_1 = self.service.standby_param_1
779
- standby_param_2 = self.service.standby_param_2
780
- timing_mode = self.service.timing_mode
781
- delay_mode = self.service.delay_mode
782
- laser_mode = self.service.laser_mode
783
- control_mode = self.service.control_mode
784
- fpk2_p1 = self.service.fpk2_p1
785
- fpk2_p2 = self.service.fpk2_p2
786
- fpk2_p3 = self.service.fpk2_p3
787
- fpk2_p4 = self.service.fpk2_p3
788
- fly_res_p1 = self.service.fly_res_p1
789
- fly_res_p2 = self.service.fly_res_p2
790
- fly_res_p3 = self.service.fly_res_p3
791
- fly_res_p4 = self.service.fly_res_p4
792
-
793
- self.usb_log("Initializing Laser")
794
- serial_number = self.get_serial_number()
795
- self.usb_log(f"Serial Number: {serial_number}")
796
- version = self.get_version()
797
- self.usb_log(f"Version: {version}")
798
-
799
- self.reset()
800
- self.usb_log("Reset")
801
- self.write_correction_file(cor_file)
802
- self.enable_laser()
803
- self.usb_log("Laser Enabled")
804
- self.set_control_mode(control_mode)
805
- self.usb_log("Control Mode")
806
- self.set_laser_mode(laser_mode)
807
- self.usb_log("Laser Mode")
808
- self.set_delay_mode(delay_mode)
809
- self.usb_log("Delay Mode")
810
- self.set_timing(timing_mode)
811
- self.usb_log("Timing Mode")
812
- self.set_standby(standby_param_1, standby_param_2)
813
- self.usb_log("Setting Standby")
814
- self.set_first_pulse_killer(first_pulse_killer)
815
- self.usb_log("Set First Pulse Killer")
816
- self.set_pwm_half_period(pwm_half_period)
817
- self.usb_log("Set PWM Half-Period")
818
- self.set_pwm_pulse_width(pwm_pulse_width)
819
- self.usb_log("Set PWM pulse width")
820
- self.set_fiber_mo(0) # Close
821
- self.usb_log("Set Fiber Mo (Closed)")
822
- self.set_pfk_param_2(fpk2_p1, fpk2_p2, fpk2_p3, fpk2_p4)
823
- self.usb_log("First Pulse Killer Parameters")
824
- self.set_fly_res(fly_res_p1, fly_res_p2, fly_res_p3, fly_res_p4)
825
- self.usb_log("On-The-Fly Res")
826
- self.enable_z()
827
- self.usb_log("Z-Enabled")
828
- self.write_analog_port_1(0x7FF)
829
- self.usb_log("Analog Port 1")
830
- self.enable_z()
831
- self.usb_log("Z-Enabled-part2")
832
- time.sleep(0.05)
833
- self.usb_log("Ready")
834
-
835
- def power(self, power):
836
- """
837
- Accepts power in percent, automatically converts to power_ratio
838
-
839
- @param power:
840
- @return:
841
- """
842
- if self._power == power:
843
- return
844
- self._power = power
845
- if self.source == "co2":
846
- power_ratio = int(round(200 * power / self._frequency))
847
- self.list_mark_power_ratio(power_ratio)
848
- if self.source == "fiber":
849
- self.list_mark_current(self._convert_power(power))
850
-
851
- def frequency(self, frequency):
852
- if self._frequency == frequency:
853
- return
854
- self._frequency = frequency
855
- if self.source == "fiber":
856
- self.list_qswitch_period(self._convert_frequency(frequency, base=20000.0))
857
- elif self.source == "co2":
858
- self.list_mark_frequency(self._convert_frequency(frequency, base=10000.0))
859
-
860
- def fpk(self, fpk):
861
- """
862
- Set First Pulse Killer
863
- @param fpk: first_pulse_killer value in percent.
864
- @return:
865
- """
866
- if self.source != "co2":
867
- # FPK only used for CO2 source.
868
- return
869
- if self._fpk == fpk or fpk is None:
870
- return
871
- self._fpk = fpk
872
- first_pulse_killer = int(round(2000.0 / self._frequency))
873
- self.list_set_co2_fpk(first_pulse_killer)
874
-
875
- def light_on(self):
876
- if not self.is_port(self._light_bit):
877
- self.port_on(self._light_bit)
878
- return True
879
- return False
880
-
881
- def light_off(self):
882
- if self.is_port(self._light_bit):
883
- self.port_off(self._light_bit)
884
- return True
885
- return False
886
-
887
- def is_port(self, bit):
888
- return bool((1 << bit) & self._port_bits)
889
-
890
- def port_on(self, bit):
891
- self._port_bits = self._port_bits | (1 << bit)
892
-
893
- def port_off(self, bit):
894
- self._port_bits = ~((~self._port_bits) | (1 << bit))
895
-
896
- def port_set(self, mask, values):
897
- self._port_bits &= ~mask # Unset mask.
898
- self._port_bits |= values & mask # Set masked bits.
899
-
900
- #######################
901
- # UNIT CONVERSIONS
902
- #######################
903
-
904
- def _convert_speed(self, speed):
905
- """
906
- Speed in the galvo is given in galvos/ms this means mm/s needs to multiply by galvos_per_mm
907
- and divide by 1000 (s/ms)
908
-
909
- @param speed:
910
- @return:
911
- """
912
- # return int(speed / 2)
913
- galvos_per_mm, _ = self.service.view.position("1mm", "1mm", vector=True)
914
- return abs(int(speed * galvos_per_mm / 1000.0))
915
-
916
- def _convert_frequency(self, frequency_khz, base=20000.0):
917
- """
918
- Converts frequency to period.
919
-
920
- 20000000.0 / frequency in hz
921
-
922
- @param frequency_khz: Frequency to convert
923
- @return:
924
- """
925
- return int(round(base / frequency_khz)) & 0xFFFF
926
-
927
- def _convert_power(self, power):
928
- """
929
- Converts power percent to int value
930
- @return:
931
- """
932
- return int(round(power * 0xFFF / 100.0))
933
-
934
- #######################
935
- # HIGH LEVEL OPERATIONS
936
- #######################
937
-
938
- def write_correction_file(self, filename):
939
- if filename is None:
940
- self.write_blank_correct_file()
941
- self.usb_log("Correction file set to blank.")
942
- return
943
- try:
944
- table = self._read_correction_file(filename)
945
- self._write_correction_table(table)
946
- self.usb_log("Correction File Sent")
947
- except OSError:
948
- self.write_blank_correct_file()
949
- self.usb_log("Correction file set to blank.")
950
- return
951
-
952
- @staticmethod
953
- def get_scale_from_correction_file(filename):
954
- with open(filename, "rb") as f:
955
- label = f.read(0x16)
956
- if label.decode("utf-16") == "LMC1COR_1.0":
957
- unk = f.read(2)
958
- return struct.unpack("63d", f.read(0x1F8))[43]
959
- else:
960
- unk = f.read(6)
961
- return struct.unpack("d", f.read(8))[0]
962
-
963
- def write_blank_correct_file(self):
964
- self.write_cor_table(False)
965
-
966
- def _read_float_correction_file(self, f):
967
- """
968
- Read table for cor files marked: LMC1COR_1.0
969
- @param f:
970
- @return:
971
- """
972
- table = []
973
- for j in range(65):
974
- for k in range(65):
975
- dx = int(round(struct.unpack("d", f.read(8))[0]))
976
- dx = dx if dx >= 0 else -dx + 0x8000
977
- dy = int(round(struct.unpack("d", f.read(8))[0]))
978
- dy = dy if dy >= 0 else -dy + 0x8000
979
- table.append([dx & 0xFFFF, dy & 0xFFFF])
980
- return table
981
-
982
- def _read_int_correction_file(self, f):
983
- table = []
984
- for j in range(65):
985
- for k in range(65):
986
- dx = int.from_bytes(f.read(4), "little", signed=True)
987
- dx = dx if dx >= 0 else -dx + 0x8000
988
- dy = int.from_bytes(f.read(4), "little", signed=True)
989
- dy = dy if dy >= 0 else -dy + 0x8000
990
- table.append([dx & 0xFFFF, dy & 0xFFFF])
991
- return table
992
-
993
- def _read_correction_file(self, filename):
994
- """
995
- Reads a standard .cor file and builds a table from that.
996
-
997
- @param filename:
998
- @return:
999
- """
1000
- with open(filename, "rb") as f:
1001
- label = f.read(0x16)
1002
- if label.decode("utf-16") == "LMC1COR_1.0":
1003
- header = f.read(0x1FA)
1004
- return self._read_float_correction_file(f)
1005
- else:
1006
- header = f.read(0xE)
1007
- return self._read_int_correction_file(f)
1008
-
1009
- def _write_correction_table(self, table):
1010
- assert len(table) == 65 * 65
1011
- self.write_cor_table(True)
1012
- first = True
1013
- for dx, dy in table:
1014
- self.write_cor_line(dx, dy, 0 if first else 1)
1015
- first = False
1016
-
1017
- #######################
1018
- # COMMAND LIST COMMAND
1019
- #######################
1020
-
1021
- def list_jump(self, x, y, short=None, long=None, distance_limit=None):
1022
- distance = int(abs(complex(x, y) - complex(self._last_x, self._last_y)))
1023
- if distance_limit and distance > distance_limit:
1024
- delay = long
1025
- else:
1026
- delay = short
1027
- if distance > 0xFFFF:
1028
- distance = 0xFFFF
1029
- angle = 0
1030
- if delay:
1031
- self.list_jump_delay(delay)
1032
- x = int(x)
1033
- y = int(y)
1034
- self._list_write(listJumpTo, x, y, angle, distance)
1035
- self._last_x = x
1036
- self._last_y = y
1037
-
1038
- def list_end_of_list(self):
1039
- self._list_write(listEndOfList)
1040
-
1041
- def list_laser_on_point(self, dwell_time):
1042
- self._list_write(listLaserOnPoint, dwell_time)
1043
-
1044
- def list_delay_time(self, time):
1045
- """
1046
- Delay time in 10 microseconds units
1047
-
1048
- @param time:
1049
- @return:
1050
- """
1051
- self._list_write(listDelayTime, abs(time))
1052
-
1053
- def list_mark(self, x, y, angle=0):
1054
- distance = int(abs(complex(x, y) - complex(self._last_x, self._last_y)))
1055
- if distance > 0xFFFF:
1056
- distance = 0xFFFF
1057
- x = int(x)
1058
- y = int(y)
1059
- self._list_write(listMarkTo, x, y, angle, distance)
1060
- self._last_x = x
1061
- self._last_y = y
1062
-
1063
- def list_jump_speed(self, speed):
1064
- if self._travel_speed == speed:
1065
- return
1066
- self._travel_speed = speed
1067
- c_speed = self._convert_speed(speed)
1068
- if c_speed > 0xFFFF:
1069
- c_speed = 0xFFFF
1070
- self._list_write(listJumpSpeed, c_speed)
1071
-
1072
- def list_laser_on_delay(self, delay):
1073
- """
1074
- Set laser on delay in microseconds
1075
- @param delay:
1076
- @return:
1077
- """
1078
- if self._delay_on == delay:
1079
- return
1080
- self._delay_on = delay
1081
- self._list_write(listLaserOnDelay, abs(delay), 0x0000 if delay > 0 else 0x8000)
1082
-
1083
- def list_laser_off_delay(self, delay):
1084
- """
1085
- Set laser off delay in microseconds
1086
- @param delay:
1087
- @return:
1088
- """
1089
- if self._delay_off == delay:
1090
- return
1091
- self._delay_off = delay
1092
- self._list_write(listLaserOffDelay, abs(delay), 0x0000 if delay > 0 else 0x8000)
1093
-
1094
- def list_mark_frequency(self, frequency):
1095
- """
1096
- This command is used in some machines but, it's not clear given the amount of reverse engineering how those
1097
- values are set. This is done for laser_type = 4.
1098
-
1099
- @param frequency:
1100
- @return:
1101
- """
1102
- self._list_write(listMarkFreq, frequency)
1103
-
1104
- def list_mark_power_ratio(self, power_ratio):
1105
- """
1106
- This command is used in some machines. Laser_type=4 and laser_type=0 (CO2), if 0x800A returned 0.
1107
-
1108
- @param power_ratio:
1109
- @return:
1110
- """
1111
- # listMarkPowerRatio
1112
- self._list_write(listMarkPowerRatio, power_ratio)
1113
-
1114
- def list_mark_speed(self, speed):
1115
- """
1116
- Sets the marking speed for the laser.
1117
-
1118
- @param speed:
1119
- @return:
1120
- """
1121
- if self._speed == speed:
1122
- return
1123
- self._speed = speed
1124
- c_speed = self._convert_speed(speed)
1125
- if c_speed > 0xFFFF:
1126
- c_speed = 0xFFFF
1127
- self._list_write(listMarkSpeed, c_speed)
1128
-
1129
- def list_jump_delay(self, delay):
1130
- """
1131
- Set laser jump delay in microseconds
1132
- @param delay:
1133
- @return:
1134
- """
1135
- if self._delay_jump == delay:
1136
- return
1137
- self._delay_jump = delay
1138
- self._list_write(listJumpDelay, abs(delay), 0x0000 if delay > 0 else 0x8000)
1139
-
1140
- def list_polygon_delay(self, delay):
1141
- """
1142
- Set polygon delay in microseconds
1143
- @param delay:
1144
- @return:
1145
- """
1146
- if self._delay_poly == delay:
1147
- return
1148
- self._delay_poly = delay
1149
- self._list_write(listPolygonDelay, abs(delay), 0x0000 if delay > 0 else 0x8000)
1150
-
1151
- def list_write_port(self):
1152
- """
1153
- Writes the set port values to the list.
1154
-
1155
- @return:
1156
- """
1157
- self._list_write(listWritePort, self._port_bits)
1158
-
1159
- def list_mark_current(self, current):
1160
- """
1161
- Also called as part of setting the power ratio. This is not correctly understood.
1162
- @param current:
1163
- @return:
1164
- """
1165
- # listMarkCurrent
1166
- self._list_write(listMarkCurrent, current)
1167
-
1168
- def list_mark_frequency_2(self, frequency):
1169
- """
1170
- Also called as part of setting frequency and is not correctly understood.
1171
-
1172
- @param frequency:
1173
- @return:
1174
- """
1175
- # listMarkFreq2
1176
- raise NotImplementedError
1177
-
1178
- def list_fly_enable(self, enabled=1):
1179
- """
1180
- On-The-Fly control enable/disable within list.
1181
-
1182
- @param enabled:
1183
- @return:
1184
- """
1185
- self._list_write(listFlyEnable, enabled)
1186
-
1187
- def list_qswitch_period(self, qswitch):
1188
- """
1189
- Sets the qswitch period, which in is the inversely related to frequency.
1190
-
1191
- @param qswitch:
1192
- @return:
1193
- """
1194
- self._list_write(listQSwitchPeriod, qswitch)
1195
-
1196
- def list_direct_laser_switch(self):
1197
- """
1198
- This is not understood.
1199
- @return:
1200
- """
1201
- # ListDirectLaserSwitch
1202
- raise NotImplementedError
1203
-
1204
- def list_fly_delay(self, delay):
1205
- """
1206
- On-the-fly control.
1207
-
1208
- @param delay:
1209
- @return:
1210
- """
1211
- self._list_write(listFlyDelay, abs(delay), 0x0000 if delay > 0 else 0x8000)
1212
-
1213
- def list_set_co2_fpk(self, fpk1, fpk2=None):
1214
- """
1215
- Set the CO2 Laser, First Pulse Killer.
1216
-
1217
- @return:
1218
- """
1219
- if fpk2 is None:
1220
- fpk2 = fpk1
1221
- self._list_write(listSetCo2FPK, fpk1, fpk2)
1222
-
1223
- def list_fly_wait_input(self):
1224
- """
1225
- Sets the On-the-fly to wait for input.
1226
- @return:
1227
- """
1228
- self._list_write(listFlyWaitInput)
1229
-
1230
- def list_fiber_open_mo(self, open_mo):
1231
- """
1232
- Sets motion operations, without MO set the laser does not automatically fire while moving.
1233
-
1234
- @param open_mo:
1235
- @return:
1236
- """
1237
- self._list_write(listFiberOpenMO, open_mo)
1238
-
1239
- def list_wait_for_input(self, wait_mask, wait_level):
1240
- """
1241
- Unknown.
1242
-
1243
- @return:
1244
- """
1245
- self._list_write(listWaitForInput, wait_mask, wait_level)
1246
-
1247
- def list_change_mark_count(self, count):
1248
- """
1249
- Unknown.
1250
-
1251
- @param count:
1252
- @return:
1253
- """
1254
- self._list_write(listChangeMarkCount, count)
1255
-
1256
- def list_set_weld_power_wave(self, weld_power_wave):
1257
- """
1258
- Unknown.
1259
-
1260
- @param weld_power_wave:
1261
- @return:
1262
- """
1263
- self._list_write(listSetWeldPowerWave, weld_power_wave)
1264
-
1265
- def list_enable_weld_power_wave(self, enabled):
1266
- """
1267
- Unknown.
1268
-
1269
- @param enabled:
1270
- @return:
1271
- """
1272
- self._list_write(listEnableWeldPowerWave, enabled)
1273
-
1274
- def list_fiber_ylpm_pulse_width(self, pulse_width):
1275
- """
1276
- Unknown.
1277
-
1278
- @param pulse_width:
1279
- @return:
1280
- """
1281
- if self._pulse_width == pulse_width:
1282
- return
1283
- self._pulse_width = pulse_width
1284
- self._list_write(listFiberYLPMPulseWidth, pulse_width)
1285
-
1286
- def list_fly_encoder_count(self, count):
1287
- """
1288
- Unknown.
1289
-
1290
- @param count:
1291
- @return:
1292
- """
1293
- self._list_write(listFlyEncoderCount, count)
1294
-
1295
- def list_set_da_z_word(self, word):
1296
- """
1297
- Unknown.
1298
-
1299
- @param word:
1300
- @return:
1301
- """
1302
- self._list_write(listSetDaZWord, word)
1303
-
1304
- def list_jpt_set_param(self, param):
1305
- """
1306
- Unknown.
1307
-
1308
- @param param:
1309
- @return:
1310
- """
1311
- self._list_write(listJptSetParam, param)
1312
-
1313
- def list_ready(self):
1314
- """
1315
- Seen at the start of any new command list.
1316
-
1317
- @return:
1318
- """
1319
- self._list_write(listReadyMark)
1320
-
1321
- #######################
1322
- # COMMAND LIST SHORTCUTS
1323
- #######################
1324
-
1325
- def disable_laser(self):
1326
- return self._command(DisableLaser)
1327
-
1328
- def enable_laser(self):
1329
- return self._command(EnableLaser)
1330
-
1331
- def execute_list(self):
1332
- return self._command(ExecuteList)
1333
-
1334
- def set_pwm_pulse_width(self, pulse_width):
1335
- return self._command(SetPwmPulseWidth, pulse_width)
1336
-
1337
- def get_version(self):
1338
- return self._command(GetVersion)
1339
-
1340
- def get_serial_number(self):
1341
- return self._command(GetSerialNo)
1342
-
1343
- def get_list_status(self):
1344
- return self._command(GetListStatus)
1345
-
1346
- def get_position_xy(self):
1347
- return self._command(GetPositionXY)
1348
-
1349
- def goto_xy(self, x, y, angle=0, distance=0):
1350
- self._last_x = x
1351
- self._last_y = y
1352
- return self._command(GotoXY, int(x), int(y), int(angle), int(distance))
1353
-
1354
- def laser_signal_off(self):
1355
- return self._command(LaserSignalOff)
1356
-
1357
- def laser_signal_on(self):
1358
- return self._command(LaserSignalOn)
1359
-
1360
- def write_cor_line(self, dx, dy, non_first):
1361
- self._command(WriteCorLine, dx, dy, non_first, read=False)
1362
-
1363
- def reset_list(self):
1364
- return self._command(ResetList)
1365
-
1366
- def restart_list(self):
1367
- return self._command(RestartList)
1368
-
1369
- def write_cor_table(self, table: bool = True):
1370
- return self._command(WriteCorTable, int(table))
1371
-
1372
- def set_control_mode(self, mode):
1373
- return self._command(SetControlMode, mode)
1374
-
1375
- def set_delay_mode(self, mode):
1376
- return self._command(SetDelayMode, mode)
1377
-
1378
- def set_max_poly_delay(self, delay):
1379
- return self._command(
1380
- SetMaxPolyDelay, abs(delay), 0x0000 if delay > 0 else 0x8000
1381
- )
1382
-
1383
- def set_end_of_list(self, end):
1384
- return self._command(SetEndOfList, end)
1385
-
1386
- def set_first_pulse_killer(self, fpk):
1387
- return self._command(SetFirstPulseKiller, fpk)
1388
-
1389
- def set_laser_mode(self, mode):
1390
- return self._command(SetLaserMode, mode)
1391
-
1392
- def set_timing(self, timing):
1393
- return self._command(SetTiming, timing)
1394
-
1395
- def set_standby(self, standby1, standby2):
1396
- return self._command(SetStandby, standby1, standby2)
1397
-
1398
- def set_pwm_half_period(self, pwm_half_period):
1399
- return self._command(SetPwmHalfPeriod, pwm_half_period)
1400
-
1401
- def stop_execute(self):
1402
- return self._command(StopExecute)
1403
-
1404
- def stop_list(self):
1405
- return self._command(StopList)
1406
-
1407
- def write_port(self):
1408
- return self._command(WritePort, self._port_bits)
1409
-
1410
- def write_analog_port_1(self, port):
1411
- return self._command(WriteAnalogPort1, port)
1412
-
1413
- def write_analog_port_2(self, port):
1414
- return self._command(WriteAnalogPort2, port)
1415
-
1416
- def write_analog_port_x(self, port):
1417
- return self._command(WriteAnalogPortX, port)
1418
-
1419
- def read_port(self):
1420
- return self._command(ReadPort)
1421
-
1422
- def set_axis_motion_param(self, *param):
1423
- return self._command(SetAxisMotionParam, *param)
1424
-
1425
- def set_axis_origin_param(self, *param):
1426
- return self._command(SetAxisOriginParam, *param)
1427
-
1428
- def axis_go_origin(self):
1429
- return self._command(AxisGoOrigin)
1430
-
1431
- def move_axis_to(self, position, invert):
1432
- return self._command(MoveAxisTo, position, invert)
1433
-
1434
- def get_axis_pos(self, index=0):
1435
- return self._command(GetAxisPos, index)
1436
-
1437
- def get_fly_wait_count(self):
1438
- return self._command(GetFlyWaitCount)
1439
-
1440
- def get_mark_count(self):
1441
- return self._command(GetMarkCount)
1442
-
1443
- def set_pfk_param_2(self, param1, param2, param3, param4):
1444
- return self._command(SetFpkParam2, param1, param2, param3, param4)
1445
-
1446
- def set_fiber_mo(self, mo):
1447
- """
1448
- mo == 0 close
1449
- mo == 1 open
1450
-
1451
- @param mo:
1452
- @return:
1453
- """
1454
- return self._command(Fiber_SetMo, mo)
1455
-
1456
- def get_fiber_st_mo_ap(self):
1457
- return self._command(Fiber_GetStMO_AP)
1458
-
1459
- def enable_z(self):
1460
- return self._command(EnableZ)
1461
-
1462
- def disable_z(self):
1463
- return self._command(DisableZ)
1464
-
1465
- def set_z_data(self, zdata):
1466
- return self._command(SetZData, zdata)
1467
-
1468
- def set_spi_simmer_current(self, current):
1469
- return self._command(SetSPISimmerCurrent, current)
1470
-
1471
- def set_fpk_param(self, param):
1472
- return self._command(SetFpkParam, param)
1473
-
1474
- def reset(self):
1475
- return self._command(Reset)
1476
-
1477
- def get_fly_speed(self):
1478
- return self._command(GetFlySpeed)
1479
-
1480
- def fiber_pulse_width(self):
1481
- return self._command(FiberPulseWidth)
1482
-
1483
- def get_fiber_config_extend(self):
1484
- return self._command(FiberGetConfigExtend)
1485
-
1486
- def input_port(self, port):
1487
- return self._command(InputPort, port)
1488
-
1489
- def clear_lock_input_port(self):
1490
- return self._command(InputPort, 0x04)
1491
-
1492
- def enable_lock_input_port(self):
1493
- return self._command(InputPort, 0x02)
1494
-
1495
- def disable_lock_input_port(self):
1496
- return self._command(InputPort, 0x01)
1497
-
1498
- def get_input_port(self):
1499
- return self._command(InputPort)
1500
-
1501
- def get_mark_time(self):
1502
- """
1503
- Get Mark Time is always called with data 3. With 0 it returns 0. It is unknown what the payload means.
1504
- @return:
1505
- """
1506
- return self._command(GetMarkTime, 3)
1507
-
1508
- def get_user_data(self):
1509
- return self._command(GetUserData)
1510
-
1511
- def set_fly_res(self, fly_res1, fly_res2, fly_res3, fly_res4):
1512
- return self._command(SetFlyRes, fly_res1, fly_res2, fly_res3, fly_res4)
1
+ """
2
+ Galvo Controller
3
+
4
+ The balor controller takes low level lmc galvo commands and converts them into lists and shorts commands to send
5
+ to the hardware controller.
6
+ """
7
+
8
+ import struct
9
+ import threading
10
+ import time
11
+ from copy import copy
12
+ from usb.core import NoBackendError
13
+
14
+ from meerk40t.balormk.mock_connection import MockConnection
15
+ from meerk40t.balormk.usb_connection import USBConnection
16
+
17
+ DRIVER_STATE_RAPID = 0
18
+ DRIVER_STATE_LIGHT = 1
19
+ DRIVER_STATE_PROGRAM = 2
20
+ DRIVER_STATE_RAW = 3
21
+
22
+ nop = [0x02, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
23
+ empty = bytearray(nop * 0x100)
24
+
25
+ listJumpTo = 0x8001
26
+ listEndOfList = 0x8002
27
+ listLaserOnPoint = 0x8003
28
+ listDelayTime = 0x8004
29
+ listMarkTo = 0x8005
30
+ listJumpSpeed = 0x8006
31
+ listLaserOnDelay = 0x8007
32
+ listLaserOffDelay = 0x8008
33
+ listMarkFreq = 0x800A
34
+ listMarkPowerRatio = 0x800B
35
+ listMarkSpeed = 0x800C
36
+ listJumpDelay = 0x800D
37
+ listPolygonDelay = 0x800F
38
+ listWritePort = 0x8011
39
+ listMarkCurrent = 0x8012
40
+ listMarkFreq2 = 0x8013
41
+ listFlyEnable = 0x801A
42
+ listQSwitchPeriod = 0x801B
43
+ listDirectLaserSwitch = 0x801C
44
+ listFlyDelay = 0x801D
45
+ listSetCo2FPK = 0x801E
46
+ listFlyWaitInput = 0x801F
47
+ listFiberOpenMO = 0x8021
48
+ listWaitForInput = 0x8022
49
+ listChangeMarkCount = 0x8023
50
+ listSetWeldPowerWave = 0x8024
51
+ listEnableWeldPowerWave = 0x8025
52
+ listFiberYLPMPulseWidth = 0x8026
53
+ listFlyEncoderCount = 0x8028
54
+ listSetDaZWord = 0x8029
55
+ listJptSetParam = 0x8050
56
+ listReadyMark = 0x8051
57
+
58
+ DisableLaser = 0x0002
59
+ EnableLaser = 0x0004
60
+ ExecuteList = 0x0005
61
+ SetPwmPulseWidth = 0x0006
62
+ GetVersion = 0x0007
63
+ GetSerialNo = 0x0009
64
+ GetListStatus = 0x000A
65
+ GetPositionXY = 0x000C
66
+ GotoXY = 0x000D
67
+ LaserSignalOff = 0x000E
68
+ LaserSignalOn = 0x000F
69
+ WriteCorLine = 0x0010
70
+ ResetList = 0x0012
71
+ RestartList = 0x0013
72
+ WriteCorTable = 0x0015
73
+ SetControlMode = 0x0016
74
+ SetDelayMode = 0x0017
75
+ SetMaxPolyDelay = 0x0018
76
+ SetEndOfList = 0x0019
77
+ SetFirstPulseKiller = 0x001A
78
+ SetLaserMode = 0x001B
79
+ SetTiming = 0x001C
80
+ SetStandby = 0x001D
81
+ SetPwmHalfPeriod = 0x001E
82
+ StopExecute = 0x001F
83
+ StopList = 0x0020
84
+ WritePort = 0x0021
85
+ WriteAnalogPort1 = 0x0022
86
+ WriteAnalogPort2 = 0x0023
87
+ WriteAnalogPortX = 0x0024
88
+ ReadPort = 0x0025
89
+ SetAxisMotionParam = 0x0026
90
+ SetAxisOriginParam = 0x0027
91
+ AxisGoOrigin = 0x0028
92
+ MoveAxisTo = 0x0029
93
+ GetAxisPos = 0x002A
94
+ GetFlyWaitCount = 0x002B
95
+ GetMarkCount = 0x002D
96
+ SetFpkParam2 = 0x002E
97
+ Fiber_SetMo = 0x0033 # open and close set by value
98
+ Fiber_GetStMO_AP = 0x0034
99
+ EnableZ = 0x003A
100
+ DisableZ = 0x0039
101
+ SetZData = 0x003B
102
+ SetSPISimmerCurrent = 0x003C
103
+ SetFpkParam = 0x0062
104
+ Reset = 0x0040
105
+ GetFlySpeed = 0x0038
106
+ FiberPulseWidth = 0x002F
107
+ FiberGetConfigExtend = 0x0030
108
+ InputPort = 0x0031 # ClearLockInputPort calls 0x04, then if EnableLockInputPort 0x02 else 0x01, GetLockInputPort
109
+ GetMarkTime = 0x0041
110
+ GetUserData = 0x0036
111
+ SetFlyRes = 0x0032
112
+
113
+ list_command_lookup = {
114
+ 0x8001: "listJumpTo",
115
+ 0x8002: "listEndOfList",
116
+ 0x8003: "listLaserOnPoint",
117
+ 0x8004: "listDelayTime",
118
+ 0x8005: "listMarkTo",
119
+ 0x8006: "listJumpSpeed",
120
+ 0x8007: "listLaserOnDelay",
121
+ 0x8008: "listLaserOffDelay",
122
+ 0x800A: "listMarkFreq",
123
+ 0x800B: "listMarkPowerRatio",
124
+ 0x800C: "listMarkSpeed",
125
+ 0x800D: "listJumpDelay",
126
+ 0x800F: "listPolygonDelay",
127
+ 0x8011: "listWritePort",
128
+ 0x8012: "listMarkCurrent",
129
+ 0x8013: "listMarkFreq2",
130
+ 0x801A: "listFlyEnable",
131
+ 0x801B: "listQSwitchPeriod",
132
+ 0x801C: "listDirectLaserSwitch",
133
+ 0x801D: "listFlyDelay",
134
+ 0x801E: "listSetCo2FPK",
135
+ 0x801F: "listFlyWaitInput",
136
+ 0x8021: "listFiberOpenMO",
137
+ 0x8022: "listWaitForInput",
138
+ 0x8023: "listChangeMarkCount",
139
+ 0x8024: "listSetWeldPowerWave",
140
+ 0x8025: "listEnableWeldPowerWave",
141
+ 0x8026: "listFiberYLPMPulseWidth",
142
+ 0x8028: "listFlyEncoderCount",
143
+ 0x8029: "listSetDaZWord",
144
+ 0x8050: "listJptSetParam",
145
+ 0x8051: "listReadyMark",
146
+ }
147
+
148
+ single_command_lookup = {
149
+ 0x0002: "DisableLaser",
150
+ 0x0004: "EnableLaser",
151
+ 0x0005: "ExecuteList",
152
+ 0x0006: "SetPwmPulseWidth",
153
+ 0x0007: "GetVersion",
154
+ 0x0009: "GetSerialNo",
155
+ 0x000A: "GetListStatus",
156
+ 0x000C: "GetPositionXY",
157
+ 0x000D: "GotoXY",
158
+ 0x000E: "LaserSignalOff",
159
+ 0x000F: "LaserSignalOn",
160
+ 0x0010: "WriteCorLine",
161
+ 0x0012: "ResetList",
162
+ 0x0013: "RestartList",
163
+ 0x0015: "WriteCorTable",
164
+ 0x0016: "SetControlMode",
165
+ 0x0017: "SetDelayMode",
166
+ 0x0018: "SetMaxPolyDelay",
167
+ 0x0019: "SetEndOfList",
168
+ 0x001A: "SetFirstPulseKiller",
169
+ 0x001B: "SetLaserMode",
170
+ 0x001C: "SetTiming",
171
+ 0x001D: "SetStandby",
172
+ 0x001E: "SetPwmHalfPeriod",
173
+ 0x001F: "StopExecute",
174
+ 0x0020: "StopList",
175
+ 0x0021: "WritePort",
176
+ 0x0022: "WriteAnalogPort1",
177
+ 0x0023: "WriteAnalogPort2",
178
+ 0x0024: "WriteAnalogPortX",
179
+ 0x0025: "ReadPort",
180
+ 0x0026: "SetAxisMotionParam",
181
+ 0x0027: "SetAxisOriginParam",
182
+ 0x0028: "AxisGoOrigin",
183
+ 0x0029: "MoveAxisTo",
184
+ 0x002A: "GetAxisPos",
185
+ 0x002B: "GetFlyWaitCount",
186
+ 0x002D: "GetMarkCount",
187
+ 0x002E: "SetFpkParam2",
188
+ 0x0033: "Fiber_SetMo",
189
+ 0x0034: "Fiber_GetStMO_AP",
190
+ 0x003A: "EnableZ",
191
+ 0x0039: "DisableZ",
192
+ 0x003B: "SetZData",
193
+ 0x003C: "SetSPISimmerCurrent",
194
+ 0x0062: "SetFpkParam",
195
+ 0x0040: "Reset",
196
+ 0x0038: "GetFlySpeed",
197
+ 0x002F: "FiberPulseWidth",
198
+ 0x0030: "FiberGetConfigExtend",
199
+ 0x0031: "InputPort",
200
+ 0x0041: "GetMarkTime",
201
+ 0x0036: "GetUserData",
202
+ 0x0032: "SetFlyRes",
203
+ }
204
+
205
+ BUSY = 0x04
206
+ READY = 0x20
207
+ AXIS = 0x40
208
+
209
+
210
+ def _bytes_to_words(r):
211
+ b0 = r[1] << 8 | r[0]
212
+ b1 = r[3] << 8 | r[2]
213
+ b2 = r[5] << 8 | r[4]
214
+ b3 = r[7] << 8 | r[6]
215
+ return b0, b1, b2, b3
216
+
217
+
218
+ class GalvoController:
219
+ """
220
+ Galvo controller is tasked with sending queued data to the controller board and ensuring that the connection to the
221
+ controller board is established to perform these actions.
222
+
223
+ This should serve as a next generation command sequencer written from scratch for galvo lasers. The goal is to
224
+ provide all the given commands in a coherent queue structure which provides correct sequences between list and
225
+ single commands.
226
+ """
227
+
228
+ def __init__(
229
+ self,
230
+ service,
231
+ x=0x8000,
232
+ y=0x8000,
233
+ mark_speed=None,
234
+ goto_speed=None,
235
+ light_speed=None,
236
+ dark_speed=None,
237
+ force_mock=False,
238
+ ):
239
+ self.service = service
240
+ self.force_mock = force_mock
241
+ self.is_shutdown = False # Shutdown finished.
242
+
243
+ self.usb_log = service.channel(
244
+ f"{self.service.safe_label}/usb", buffer_size=500
245
+ )
246
+ self.usb_log.watch(lambda e: service.signal("pipe;usb_status", e))
247
+
248
+ self.connection = None
249
+ self._is_opening = False
250
+ self._abort_open = False
251
+ self._disable_connect = False
252
+
253
+ self._light_bit = 8
254
+ self._foot_bit = 15
255
+ self.define_pins()
256
+
257
+ self._last_x = x
258
+ self._last_y = y
259
+ self._mark_speed = mark_speed
260
+ self._goto_speed = goto_speed
261
+ self._light_speed = light_speed
262
+ self._dark_speed = dark_speed
263
+ self.serial_confirmed = False
264
+ self.serial_number_found = None
265
+
266
+ self._ready = None
267
+ self._speed = None
268
+ self._travel_speed = None
269
+ self._frequency = None
270
+ self._power = None
271
+ self._pulse_width = None
272
+ self._fpk = None
273
+
274
+ self._delay_jump = None
275
+ self._delay_on = None
276
+ self._delay_off = None
277
+ self._delay_poly = None
278
+ self._delay_end = None
279
+
280
+ self._port_bits = 0
281
+ self._machine_index = 0
282
+
283
+ self.mode = DRIVER_STATE_RAPID
284
+ self._list_lock = threading.RLock()
285
+ self._active_list = None
286
+ self._active_index = 0
287
+ self._list_executing = False
288
+ self._number_of_list_packets = 0
289
+ self.paused = False
290
+ self._signal_updates = self.service.setting(bool, "signal_updates", True)
291
+
292
+ def define_pins(self):
293
+ self._light_bit = self.service.setting(int, "light_pin", 8)
294
+ self._foot_bit = self.service.setting(int, "footpedal_pin", 15)
295
+
296
+ @property
297
+ def source(self):
298
+ return self.service.source
299
+
300
+ @property
301
+ def state(self):
302
+ if self.mode == DRIVER_STATE_RAPID:
303
+ return "idle", "idle"
304
+ if self.paused:
305
+ return "hold", "paused"
306
+ if self.mode == DRIVER_STATE_RAW:
307
+ return "busy", "raw"
308
+ if self.mode == DRIVER_STATE_LIGHT:
309
+ return "busy", "light"
310
+ if self.mode == DRIVER_STATE_PROGRAM:
311
+ return "busy", "program"
312
+
313
+ def set_disable_connect(self, status):
314
+ self._disable_connect = status
315
+
316
+ def added(self):
317
+ pass
318
+
319
+ def service_detach(self):
320
+ pass
321
+
322
+ def shutdown(self, *args, **kwargs):
323
+ self.is_shutdown = True
324
+
325
+ @property
326
+ def connected(self):
327
+ if self.connection is None:
328
+ return False
329
+ return self.connection.is_open(self._machine_index)
330
+
331
+ @property
332
+ def is_connecting(self):
333
+ if self.connection is None:
334
+ return False
335
+ return self._is_opening
336
+
337
+ def abort_connect(self):
338
+ self._abort_open = True
339
+ self.usb_log("Connect Attempts Aborted")
340
+
341
+ def disconnect(self):
342
+ try:
343
+ self.connection.close(self._machine_index)
344
+ except (ConnectionError, ConnectionRefusedError, AttributeError):
345
+ pass
346
+ self.connection = None
347
+ self.serial_number_found = None
348
+ # Reset error to allow another attempt
349
+ self.set_disable_connect(False)
350
+
351
+ def connect_if_needed(self):
352
+ if self._disable_connect:
353
+ # After many failures automatic connects are disabled. We require a manual connection.
354
+ self.abort_connect()
355
+ self.connection = None
356
+ raise ConnectionRefusedError(
357
+ "LMC was unreachable. Explicit connect required."
358
+ )
359
+ if self.connection is None:
360
+ if self.service.setting(bool, "mock", False) or self.force_mock:
361
+ self.connection = MockConnection(self.usb_log)
362
+ name = self.service.safe_label
363
+ self.connection.send = self.service.channel(f"{name}/send")
364
+ self.connection.recv = self.service.channel(f"{name}/recv")
365
+ else:
366
+ self.connection = USBConnection(self.usb_log)
367
+ self._is_opening = True
368
+ self._abort_open = False
369
+ count = 0
370
+ while not self.connection.is_open(self._machine_index):
371
+ try:
372
+ if self.connection.open(self._machine_index) < 0:
373
+ raise ConnectionError
374
+ self.init_laser()
375
+ except (ConnectionError, ConnectionRefusedError):
376
+ if count == 0:
377
+ self.service("clone_init\n")
378
+ time.sleep(0.3)
379
+ count += 1
380
+ # self.usb_log(f"Error-Routine pass #{count}")
381
+ if self.is_shutdown or self._abort_open:
382
+ self._is_opening = False
383
+ self._abort_open = False
384
+ return
385
+ if self.connection.is_open(self._machine_index):
386
+ self.connection.close(self._machine_index)
387
+ if count >= 10:
388
+ # We have failed too many times.
389
+ self._is_opening = False
390
+ self.set_disable_connect(True)
391
+ self.usb_log("Could not connect to the LMC controller.")
392
+ self.usb_log("Automatic connections disabled.")
393
+ from platform import system
394
+ osname = system()
395
+ if osname == "Windows":
396
+ self.usb_log("Did you install the libusb driver via Zadig (https://zadig.akeo.ie/)?")
397
+ self.usb_log("Consult the wiki: https://github.com/meerk40t/meerk40t/wiki/Install%3A-Windows")
398
+ raise ConnectionRefusedError(
399
+ "Could not connect to the LMC controller."
400
+ )
401
+ time.sleep(0.3)
402
+ continue
403
+ self._is_opening = False
404
+ self._abort_open = False
405
+
406
+ def send(self, data, read=True):
407
+ if self.is_shutdown:
408
+ return -1, -1, -1, -1
409
+ try:
410
+ self.connect_if_needed()
411
+ except (ConnectionRefusedError, NoBackendError):
412
+ return -1, -1, -1, -1
413
+ try:
414
+ self.connection.write(self._machine_index, data)
415
+ except ConnectionError:
416
+ return -1, -1, -1, -1
417
+ if read:
418
+ try:
419
+ r = self.connection.read(self._machine_index)
420
+ return struct.unpack("<4H", r)
421
+ except ConnectionError:
422
+ return -1, -1, -1, -1
423
+
424
+ def status(self):
425
+ b0, b1, b2, b3 = self.get_version()
426
+ return b3
427
+
428
+ #######################
429
+ # MODE SHIFTS
430
+ #######################
431
+
432
+ def raw_mode(self):
433
+ self.mode = DRIVER_STATE_RAW
434
+
435
+ def rapid_mode(self):
436
+ if self.mode == DRIVER_STATE_RAPID:
437
+ return
438
+ self.list_end_of_list() # Ensure at least one list_end_of_list
439
+ self._list_end()
440
+ if not self._list_executing and self._number_of_list_packets:
441
+ # If we never ran the list, and we sent some lists.
442
+ self.execute_list()
443
+ self._list_executing = False
444
+ self._number_of_list_packets = 0
445
+ self.wait_idle()
446
+ if self.source == "fiber":
447
+ self.set_fiber_mo(0)
448
+ self.port_off(bit=0)
449
+ self.write_port()
450
+ marktime = self.get_mark_time()
451
+ self.service.signal("galvo;marktime", marktime)
452
+ self.usb_log(f"Time taken for list execution: {marktime}")
453
+ self.mode = DRIVER_STATE_RAPID
454
+
455
+ def raster_mode(self):
456
+ self.program_mode()
457
+
458
+ def program_mode(self):
459
+ if self.mode == DRIVER_STATE_PROGRAM:
460
+ return
461
+ if self.mode == DRIVER_STATE_LIGHT:
462
+ self.mode = DRIVER_STATE_PROGRAM
463
+ self.light_off()
464
+ self.port_on(bit=0)
465
+ self.write_port()
466
+ if self.source == "fiber":
467
+ self.set_fiber_mo(1)
468
+ else:
469
+ self.mode = DRIVER_STATE_PROGRAM
470
+ self.reset_list()
471
+ self.port_on(bit=0)
472
+ self.write_port()
473
+ if self.source == "fiber":
474
+ self.set_fiber_mo(1)
475
+ self._ready = None
476
+ self._speed = None
477
+ self._travel_speed = None
478
+ self._frequency = None
479
+ self._power = None
480
+ self._pulse_width = None
481
+
482
+ self._delay_jump = None
483
+ self._delay_on = None
484
+ self._delay_off = None
485
+ self._delay_poly = None
486
+ self._delay_end = None
487
+ self.list_ready()
488
+ if self.service.delay_openmo != 0 and self.source == "fiber":
489
+ self.list_delay_time(int(self.service.delay_openmo * 100))
490
+ self.list_write_port()
491
+ self.list_jump_speed(self.service.default_rapid_speed)
492
+
493
+ def light_mode(self):
494
+ if self.mode == DRIVER_STATE_LIGHT:
495
+ return
496
+ if self.mode == DRIVER_STATE_PROGRAM:
497
+ if self.source == "fiber":
498
+ self.set_fiber_mo(0)
499
+ self.port_off(bit=0)
500
+ self.port_on(self._light_bit)
501
+ self.write_port()
502
+ else:
503
+ self._ready = None
504
+ self._speed = None
505
+ self._travel_speed = None
506
+ self._frequency = None
507
+ self._power = None
508
+ self._pulse_width = None
509
+
510
+ self._delay_jump = None
511
+ self._delay_on = None
512
+ self._delay_off = None
513
+ self._delay_poly = None
514
+ self._delay_end = None
515
+
516
+ self.reset_list()
517
+ self.list_ready()
518
+ self.port_off(bit=0)
519
+ self.port_on(self._light_bit)
520
+ self.list_write_port()
521
+ self.mode = DRIVER_STATE_LIGHT
522
+
523
+ #######################
524
+ # LIST APPENDING OPERATIONS
525
+ #######################
526
+
527
+ def _list_end(self):
528
+ if not self._active_list or not self._active_index:
529
+ # Ensure there is a list to end.
530
+ return
531
+ with self._list_lock:
532
+ if not self._active_list or not self._active_index:
533
+ # Double-gated syntax, make sure there's still that list needing ending.
534
+ return
535
+ self.wait_ready()
536
+ while self.paused:
537
+ time.sleep(0.3)
538
+ self.send(self._active_list, False)
539
+ if self.mode != DRIVER_STATE_RAW:
540
+ self.set_end_of_list(0)
541
+ self._number_of_list_packets += 1
542
+ self._active_list = None
543
+ self._active_index = 0
544
+ if self._number_of_list_packets > 2 and not self._list_executing:
545
+ if self.mode != DRIVER_STATE_RAW:
546
+ self.execute_list()
547
+ self._list_executing = True
548
+
549
+ def _list_new(self):
550
+ with self._list_lock:
551
+ self._active_list = copy(empty)
552
+ self._active_index = 0
553
+
554
+ def _list_write(self, command, v1=0, v2=0, v3=0, v4=0, v5=0):
555
+ with self._list_lock:
556
+ if self._active_index >= 0xC00:
557
+ self._list_end()
558
+ if self._active_list is None:
559
+ self._list_new()
560
+ index = self._active_index
561
+ self._active_list[index : index + 12] = struct.pack(
562
+ "<6H", int(command), int(v1), int(v2), int(v3), int(v4), int(v5)
563
+ )
564
+ self._active_index += 12
565
+
566
+ def _command(self, command, v1=0, v2=0, v3=0, v4=0, v5=0, read=True):
567
+ cmd = struct.pack(
568
+ "<6H", int(command), int(v1), int(v2), int(v3), int(v4), int(v5)
569
+ )
570
+ return self.send(cmd, read=read)
571
+
572
+ def raw_write(self, command, v1=0, v2=0, v3=0, v4=0, v5=0):
573
+ """
574
+ Write this raw command to value. Sends the correct way based on command value.
575
+
576
+ @return:
577
+ """
578
+ if command >= 0x8000:
579
+ self._list_write(command, v1, v2, v3, v4, v5)
580
+ else:
581
+ self._command(command, v1, v2, v3, v4, v5)
582
+
583
+ def raw_clear(self):
584
+ self._list_new()
585
+
586
+ #######################
587
+ # SETS FOR PLOTLIKES
588
+ #######################
589
+
590
+ def set_settings(self, settings):
591
+ """
592
+ Sets the primary settings. Rapid, frequency, speed, and timings.
593
+
594
+ @param settings: The current settings dictionary
595
+ @return:
596
+ """
597
+ if self.service.pulse_width_enabled and self.source == "fiber":
598
+ # Global Pulse Width is enabled.
599
+ if str(settings.get("pulse_width_enabled", False)).lower() == "true":
600
+ # Local Pulse Width value is enabled.
601
+ # OpFiberYLPMPulseWidth
602
+
603
+ self.list_fiber_ylpm_pulse_width(
604
+ int(settings.get("pulse_width", self.service.default_pulse_width))
605
+ )
606
+ else:
607
+ # Only global is enabled, use global pulse width value.
608
+ self.list_fiber_ylpm_pulse_width(self.service.default_pulse_width)
609
+
610
+ if str(settings.get("rapid_enabled", False)).lower() == "true":
611
+ self.list_jump_speed(
612
+ float(settings.get("rapid_speed", self.service.default_rapid_speed))
613
+ )
614
+ else:
615
+ self.list_jump_speed(self.service.default_rapid_speed)
616
+
617
+ power = (
618
+ float(settings.get("power", self.service.default_power)) / 10.0
619
+ ) # Convert power, out of 1000
620
+ frequency = float(settings.get("frequency", self.service.default_frequency))
621
+ fpk = float(settings.get("fpk", self.service.default_fpk))
622
+ if self.source == "fiber":
623
+ self.power(power)
624
+ self.frequency(frequency)
625
+ elif self.source == "co2":
626
+ self.frequency(frequency)
627
+ self.fpk(fpk)
628
+ self.power(power)
629
+ self.list_mark_speed(float(settings.get("speed", self.service.default_speed)))
630
+
631
+ if str(settings.get("timing_enabled", False)).lower() == "true":
632
+ self.list_laser_on_delay(
633
+ settings.get("delay_laser_on", self.service.delay_laser_on)
634
+ )
635
+ self.list_laser_off_delay(
636
+ settings.get("delay_laser_off", self.service.delay_laser_off)
637
+ )
638
+ self.list_polygon_delay(
639
+ settings.get("delay_laser_polygon", self.service.delay_polygon)
640
+ )
641
+ else:
642
+ # Use globals
643
+ self.list_laser_on_delay(self.service.delay_laser_on)
644
+ self.list_laser_off_delay(self.service.delay_laser_off)
645
+ self.list_polygon_delay(self.service.delay_polygon)
646
+
647
+ #######################
648
+ # PLOTLIKE SHORTCUTS
649
+ #######################
650
+
651
+ def mark(self, x, y):
652
+ if x == self._last_x and y == self._last_y:
653
+ return
654
+ if x > 0xFFFF or x < 0 or y > 0xFFFF or y < 0:
655
+ # Moves to out of range are not performed.
656
+ return
657
+ if self._mark_speed is not None:
658
+ self.list_mark_speed(self._mark_speed)
659
+ self.list_mark(x, y)
660
+
661
+ def goto(self, x, y, long=None, short=None, distance_limit=None):
662
+ if x == self._last_x and y == self._last_y:
663
+ return
664
+ if x > 0xFFFF or x < 0 or y > 0xFFFF or y < 0:
665
+ # Moves to out of range are not performed.
666
+ return
667
+ if self._goto_speed is not None:
668
+ self.list_jump_speed(self._goto_speed)
669
+ self.list_jump(x, y, long=long, short=short, distance_limit=distance_limit)
670
+
671
+ def light(self, x, y, long=None, short=None, distance_limit=None):
672
+ if x == self._last_x and y == self._last_y:
673
+ return
674
+ if x > 0xFFFF or x < 0 or y > 0xFFFF or y < 0:
675
+ # Moves to out of range are not performed.
676
+ return
677
+ if self.light_on():
678
+ self.list_write_port()
679
+ if self._light_speed is not None:
680
+ self.list_jump_speed(self._light_speed)
681
+ self.list_jump(x, y, long=long, short=short, distance_limit=distance_limit)
682
+
683
+ def dark(self, x, y, long=None, short=None, distance_limit=None):
684
+ if x == self._last_x and y == self._last_y:
685
+ return
686
+ if x > 0xFFFF or x < 0 or y > 0xFFFF or y < 0:
687
+ # Moves to out of range are not performed.
688
+ return
689
+ if self.light_off():
690
+ self.list_write_port()
691
+ if self._dark_speed is not None:
692
+ self.list_jump_speed(self._dark_speed)
693
+ self.list_jump(x, y, long=long, short=short, distance_limit=distance_limit)
694
+
695
+ def set_xy(self, x, y):
696
+ distance = int(abs(complex(x, y) - complex(self._last_x, self._last_y)))
697
+ if distance > 0xFFFF:
698
+ distance = 0xFFFF
699
+ self.goto_xy(x, y, distance=distance)
700
+
701
+ def get_last_xy(self):
702
+ return self._last_x, self._last_y
703
+
704
+ #######################
705
+ # Command Shortcuts
706
+ #######################
707
+
708
+ def is_busy(self):
709
+ status = self.status()
710
+ return bool(status & BUSY)
711
+
712
+ def is_ready(self):
713
+ status = self.status()
714
+ return bool(status & READY)
715
+
716
+ def is_axis(self):
717
+ status = self.status()
718
+ return bool(status & AXIS)
719
+
720
+ def is_ready_and_not_busy(self):
721
+ if self.mode == DRIVER_STATE_RAW:
722
+ return True
723
+ status = self.status()
724
+ return bool(status & READY) and not bool(status & BUSY)
725
+
726
+ def wait_finished(self):
727
+ if self.mode == DRIVER_STATE_RAW:
728
+ return
729
+ while not self.is_ready_and_not_busy():
730
+ time.sleep(0.01)
731
+ if self.is_shutdown:
732
+ return
733
+
734
+ def wait_axis(self):
735
+ if self.mode == DRIVER_STATE_RAW:
736
+ return
737
+ while self.is_axis():
738
+ time.sleep(0.01)
739
+ if self.is_shutdown:
740
+ return
741
+
742
+ def wait_ready(self):
743
+ if self.mode == DRIVER_STATE_RAW:
744
+ return
745
+ while not self.is_ready():
746
+ time.sleep(0.01)
747
+ if self.is_shutdown:
748
+ return
749
+
750
+ def wait_idle(self):
751
+ if self.mode == DRIVER_STATE_RAW:
752
+ return
753
+ while self.is_busy():
754
+ time.sleep(0.01)
755
+ if self.is_shutdown:
756
+ return
757
+
758
+ def abort(self, dummy_packet=True):
759
+ if self.mode == DRIVER_STATE_RAW:
760
+ return
761
+ self.stop_execute()
762
+ self.paused = False
763
+ self.set_fiber_mo(0)
764
+ self.reset_list()
765
+ if dummy_packet:
766
+ self._list_new()
767
+ self.list_end_of_list() # Ensure packet is sent on end.
768
+ self._list_end()
769
+ if not self._list_executing:
770
+ self.execute_list()
771
+ self._list_executing = False
772
+ self._number_of_list_packets = 0
773
+ self.set_fiber_mo(0)
774
+ self.port_off(bit=0)
775
+ self.write_port()
776
+ self.mode = DRIVER_STATE_RAPID
777
+
778
+ def pause(self):
779
+ if self.mode == DRIVER_STATE_RAW:
780
+ return
781
+ self.paused = True
782
+ self.stop_list()
783
+
784
+ def resume(self):
785
+ if self.mode == DRIVER_STATE_RAW:
786
+ return
787
+ self.restart_list()
788
+ self.paused = False
789
+
790
+ def init_laser(self):
791
+ if self.mode == DRIVER_STATE_RAW:
792
+ return
793
+ cor_file = self.service.corfile if self.service.corfile_enabled else None
794
+ first_pulse_killer = self.service.first_pulse_killer
795
+ pwm_pulse_width = self.service.pwm_pulse_width
796
+ pwm_half_period = self.service.pwm_half_period
797
+ standby_param_1 = self.service.standby_param_1
798
+ standby_param_2 = self.service.standby_param_2
799
+ timing_mode = self.service.timing_mode
800
+ delay_mode = self.service.delay_mode
801
+ laser_mode = self.service.laser_mode
802
+ control_mode = self.service.control_mode
803
+ fpk2_p1 = self.service.fpk2_p1
804
+ fpk2_p2 = self.service.fpk2_p2
805
+ fpk2_p3 = self.service.fpk2_p3
806
+ fpk2_p4 = self.service.fpk2_p3
807
+ fly_res_p1 = self.service.fly_res_p1
808
+ fly_res_p2 = self.service.fly_res_p2
809
+ fly_res_p3 = self.service.fly_res_p3
810
+ fly_res_p4 = self.service.fly_res_p4
811
+
812
+ self.usb_log("Initializing Laser")
813
+ serial_number = self.get_serial_number()
814
+ content = "0x"
815
+ for nibble in serial_number:
816
+ content += f"{nibble:04x}"
817
+ self.usb_log(f"Serial Number: {serial_number} ({content})")
818
+ if self.service.serial_enable and self.service.serial and not self.serial_confirmed:
819
+ self.usb_log(f"Requires serial number confirmation against {self.service.serial}.")
820
+ if content == self.service.serial:
821
+ self.serial_confirmed = True
822
+
823
+ if not self.serial_confirmed:
824
+ self.disconnect()
825
+ raise ConnectionRefusedError("Serial number confirmation failed.")
826
+ else:
827
+ self.usb_log("Serial number confirmed.")
828
+
829
+ version = self.get_version()
830
+ self.usb_log(f"Version: {version}")
831
+
832
+ self.reset()
833
+ self.usb_log("Reset")
834
+ self.write_correction_file(cor_file)
835
+ self.enable_laser()
836
+ self.usb_log("Laser Enabled")
837
+ self.set_control_mode(control_mode)
838
+ self.usb_log("Control Mode")
839
+ self.set_laser_mode(laser_mode)
840
+ self.usb_log("Laser Mode")
841
+ self.set_delay_mode(delay_mode)
842
+ self.usb_log("Delay Mode")
843
+ self.set_timing(timing_mode)
844
+ self.usb_log("Timing Mode")
845
+ self.set_standby(standby_param_1, standby_param_2)
846
+ self.usb_log("Setting Standby")
847
+ self.set_first_pulse_killer(first_pulse_killer)
848
+ self.usb_log("Set First Pulse Killer")
849
+ self.set_pwm_half_period(pwm_half_period)
850
+ self.usb_log("Set PWM Half-Period")
851
+ self.set_pwm_pulse_width(pwm_pulse_width)
852
+ self.usb_log("Set PWM pulse width")
853
+ self.set_fiber_mo(0) # Close
854
+ self.usb_log("Set Fiber Mo (Closed)")
855
+ self.set_pfk_param_2(fpk2_p1, fpk2_p2, fpk2_p3, fpk2_p4)
856
+ self.usb_log("First Pulse Killer Parameters")
857
+ self.set_fly_res(fly_res_p1, fly_res_p2, fly_res_p3, fly_res_p4)
858
+ self.usb_log("On-The-Fly Res")
859
+ self.enable_z()
860
+ self.usb_log("Z-Enabled")
861
+ self.write_analog_port_1(0x7FF)
862
+ self.usb_log("Analog Port 1")
863
+ self.enable_z()
864
+ self.usb_log("Z-Enabled-part2")
865
+ time.sleep(0.05)
866
+ self.usb_log("Ready")
867
+
868
+ def power(self, power):
869
+ """
870
+ Accepts power in percent, automatically converts to power_ratio
871
+
872
+ @param power:
873
+ @return:
874
+ """
875
+ if self._power == power:
876
+ return
877
+ self._power = power
878
+ if self.source == "co2":
879
+ power_ratio = int(round(200 * power / self._frequency))
880
+ self.list_mark_power_ratio(power_ratio)
881
+ if self.source == "fiber":
882
+ self.list_mark_current(self._convert_power(power))
883
+
884
+ def frequency(self, frequency):
885
+ if self._frequency == frequency:
886
+ return
887
+ self._frequency = frequency
888
+ if self.source == "fiber":
889
+ self.list_qswitch_period(self._convert_frequency(frequency, base=20000.0))
890
+ elif self.source == "co2":
891
+ self.list_mark_frequency(self._convert_frequency(frequency, base=10000.0))
892
+
893
+ def fpk(self, fpk):
894
+ """
895
+ Set First Pulse Killer
896
+ @param fpk: first_pulse_killer value in percent.
897
+ @return:
898
+ """
899
+ if self.source != "co2":
900
+ # FPK only used for CO2 source.
901
+ return
902
+ if self._fpk == fpk or fpk is None:
903
+ return
904
+ self._fpk = fpk
905
+ first_pulse_killer = int(round(2000.0 / self._frequency))
906
+ self.list_set_co2_fpk(first_pulse_killer)
907
+
908
+ def light_on(self):
909
+ if not self.is_port(self._light_bit):
910
+ self.port_on(self._light_bit)
911
+ return True
912
+ return False
913
+
914
+ def light_off(self):
915
+ if self.is_port(self._light_bit):
916
+ self.port_off(self._light_bit)
917
+ return True
918
+ return False
919
+
920
+ def is_port(self, bit):
921
+ return bool((1 << bit) & self._port_bits)
922
+
923
+ def port_on(self, bit):
924
+ self._port_bits = self._port_bits | (1 << bit)
925
+
926
+ def port_off(self, bit):
927
+ self._port_bits = ~((~self._port_bits) | (1 << bit))
928
+
929
+ def port_set(self, mask, values):
930
+ self._port_bits &= ~mask # Unset mask.
931
+ self._port_bits |= values & mask # Set masked bits.
932
+
933
+ #######################
934
+ # UNIT CONVERSIONS
935
+ #######################
936
+
937
+ def _convert_speed(self, speed):
938
+ """
939
+ Speed in the galvo is given in galvos/ms this means mm/s needs to multiply by galvos_per_mm
940
+ and divide by 1000 (s/ms)
941
+
942
+ @param speed:
943
+ @return:
944
+ """
945
+ # return int(speed / 2)
946
+ galvos_per_mm, _ = self.service.view.position("1mm", "1mm", vector=True, margins=False)
947
+ return abs(int(speed * galvos_per_mm / 1000.0))
948
+
949
+ def _convert_frequency(self, frequency_khz, base=20000.0):
950
+ """
951
+ Converts frequency to period.
952
+
953
+ 20000000.0 / frequency in hz
954
+
955
+ @param frequency_khz: Frequency to convert
956
+ @return:
957
+ """
958
+ return int(round(base / frequency_khz)) & 0xFFFF
959
+
960
+ def _convert_power(self, power):
961
+ """
962
+ Converts power percent to int value
963
+ @return:
964
+ """
965
+ return int(round(power * 0xFFF / 100.0))
966
+
967
+ #######################
968
+ # HIGH LEVEL OPERATIONS
969
+ #######################
970
+
971
+ def write_correction_file(self, filename):
972
+ if filename is None:
973
+ self.write_blank_correct_file()
974
+ self.usb_log("Correction file set to blank.")
975
+ return
976
+ try:
977
+ table = self._read_correction_file(filename)
978
+ self._write_correction_table(table)
979
+ self.usb_log("Correction File Sent")
980
+ except OSError:
981
+ self.write_blank_correct_file()
982
+ self.usb_log("Correction file set to blank.")
983
+ return
984
+
985
+ @staticmethod
986
+ def get_scale_from_correction_file(filename):
987
+ with open(filename, "rb") as f:
988
+ label = f.read(0x16)
989
+ if label.decode("utf-16") == "LMC1COR_1.0":
990
+ unk = f.read(2)
991
+ return struct.unpack("63d", f.read(0x1F8))[43]
992
+ else:
993
+ unk = f.read(6)
994
+ return struct.unpack("d", f.read(8))[0]
995
+
996
+ def write_blank_correct_file(self):
997
+ self.write_cor_table(False)
998
+
999
+ def _read_float_correction_file(self, f):
1000
+ """
1001
+ Read table for cor files marked: LMC1COR_1.0
1002
+ @param f:
1003
+ @return:
1004
+ """
1005
+ table = []
1006
+ for j in range(65):
1007
+ for k in range(65):
1008
+ dx = int(round(struct.unpack("d", f.read(8))[0]))
1009
+ dx = dx if dx >= 0 else -dx + 0x8000
1010
+ dy = int(round(struct.unpack("d", f.read(8))[0]))
1011
+ dy = dy if dy >= 0 else -dy + 0x8000
1012
+ table.append([dx & 0xFFFF, dy & 0xFFFF])
1013
+ return table
1014
+
1015
+ def _read_int_correction_file(self, f):
1016
+ table = []
1017
+ for j in range(65):
1018
+ for k in range(65):
1019
+ dx = int.from_bytes(f.read(4), "little", signed=True)
1020
+ dx = dx if dx >= 0 else -dx + 0x8000
1021
+ dy = int.from_bytes(f.read(4), "little", signed=True)
1022
+ dy = dy if dy >= 0 else -dy + 0x8000
1023
+ table.append([dx & 0xFFFF, dy & 0xFFFF])
1024
+ return table
1025
+
1026
+ def _read_correction_file(self, filename):
1027
+ """
1028
+ Reads a standard .cor file and builds a table from that.
1029
+
1030
+ @param filename:
1031
+ @return:
1032
+ """
1033
+ with open(filename, "rb") as f:
1034
+ label = f.read(0x16)
1035
+ if label.decode("utf-16") == "LMC1COR_1.0":
1036
+ header = f.read(0x1FA)
1037
+ return self._read_float_correction_file(f)
1038
+ else:
1039
+ header = f.read(0xE)
1040
+ return self._read_int_correction_file(f)
1041
+
1042
+ def _write_correction_table(self, table):
1043
+ assert len(table) == 65 * 65
1044
+ self.write_cor_table(True)
1045
+ first = True
1046
+ for dx, dy in table:
1047
+ self.write_cor_line(dx, dy, 0 if first else 1)
1048
+ first = False
1049
+
1050
+ #######################
1051
+ # COMMAND LIST COMMAND
1052
+ #######################
1053
+
1054
+ def list_jump(self, x, y, short=None, long=None, distance_limit=None):
1055
+ distance = int(abs(complex(x, y) - complex(self._last_x, self._last_y)))
1056
+ if distance_limit and distance > distance_limit:
1057
+ delay = long
1058
+ else:
1059
+ delay = short
1060
+ if distance > 0xFFFF:
1061
+ distance = 0xFFFF
1062
+ angle = 0
1063
+ if delay:
1064
+ self.list_jump_delay(delay)
1065
+ x = int(x)
1066
+ y = int(y)
1067
+ self._list_write(listJumpTo, x, y, angle, distance)
1068
+ if self._signal_updates:
1069
+ view = self.service.view
1070
+ l_x, l_y = view.iposition(self._last_x, self._last_y)
1071
+ n_x, n_y = view.iposition(x, y)
1072
+ self.service.signal(
1073
+ "driver;position",
1074
+ (l_x, l_y, n_x, n_y),
1075
+ )
1076
+
1077
+ self._last_x = x
1078
+ self._last_y = y
1079
+
1080
+ def list_end_of_list(self):
1081
+ self._list_write(listEndOfList)
1082
+
1083
+ def list_laser_on_point(self, dwell_time):
1084
+ self._list_write(listLaserOnPoint, dwell_time)
1085
+
1086
+ def list_delay_time(self, delay_time):
1087
+ """
1088
+ Delay time in 10 microseconds units
1089
+
1090
+ @param delay_time:
1091
+ @return:
1092
+ """
1093
+ self._list_write(listDelayTime, abs(delay_time))
1094
+
1095
+ def list_mark(self, x, y, angle=0):
1096
+ distance = int(abs(complex(x, y) - complex(self._last_x, self._last_y)))
1097
+ if distance > 0xFFFF:
1098
+ distance = 0xFFFF
1099
+ x = int(x)
1100
+ y = int(y)
1101
+ self._list_write(listMarkTo, x, y, angle, distance)
1102
+
1103
+ if self._signal_updates:
1104
+ view = self.service.view
1105
+ l_x, l_y = view.iposition(self._last_x, self._last_y)
1106
+ n_x, n_y = view.iposition(x, y)
1107
+ self.service.signal(
1108
+ "driver;position",
1109
+ (l_x, l_y, n_x, n_y),
1110
+ )
1111
+
1112
+ self._last_x = x
1113
+ self._last_y = y
1114
+
1115
+ def list_jump_speed(self, speed):
1116
+ if self._travel_speed == speed:
1117
+ return
1118
+ self._travel_speed = speed
1119
+ c_speed = self._convert_speed(speed)
1120
+ if c_speed > 0xFFFF:
1121
+ c_speed = 0xFFFF
1122
+ self._list_write(listJumpSpeed, c_speed)
1123
+
1124
+ def list_laser_on_delay(self, delay):
1125
+ """
1126
+ Set laser on delay in microseconds
1127
+ @param delay:
1128
+ @return:
1129
+ """
1130
+ if self._delay_on == delay:
1131
+ return
1132
+ self._delay_on = delay
1133
+ self._list_write(listLaserOnDelay, abs(delay), 0x0000 if delay > 0 else 0x8000)
1134
+
1135
+ def list_laser_off_delay(self, delay):
1136
+ """
1137
+ Set laser off delay in microseconds
1138
+ @param delay:
1139
+ @return:
1140
+ """
1141
+ if self._delay_off == delay:
1142
+ return
1143
+ self._delay_off = delay
1144
+ self._list_write(listLaserOffDelay, abs(delay), 0x0000 if delay > 0 else 0x8000)
1145
+
1146
+ def list_mark_frequency(self, frequency):
1147
+ """
1148
+ This command is used in some machines but, it's not clear given the amount of reverse engineering how those
1149
+ values are set. This is done for laser_type = 4.
1150
+
1151
+ @param frequency:
1152
+ @return:
1153
+ """
1154
+ self._list_write(listMarkFreq, frequency)
1155
+
1156
+ def list_mark_power_ratio(self, power_ratio):
1157
+ """
1158
+ This command is used in some machines. Laser_type=4 and laser_type=0 (CO2), if 0x800A returned 0.
1159
+
1160
+ @param power_ratio:
1161
+ @return:
1162
+ """
1163
+ # listMarkPowerRatio
1164
+ self._list_write(listMarkPowerRatio, power_ratio)
1165
+
1166
+ def list_mark_speed(self, speed):
1167
+ """
1168
+ Sets the marking speed for the laser.
1169
+
1170
+ @param speed:
1171
+ @return:
1172
+ """
1173
+ if self._speed == speed:
1174
+ return
1175
+ self._speed = speed
1176
+ c_speed = self._convert_speed(speed)
1177
+ if c_speed > 0xFFFF:
1178
+ c_speed = 0xFFFF
1179
+ self._list_write(listMarkSpeed, c_speed)
1180
+
1181
+ def list_jump_delay(self, delay):
1182
+ """
1183
+ Set laser jump delay in microseconds
1184
+ @param delay:
1185
+ @return:
1186
+ """
1187
+ if self._delay_jump == delay:
1188
+ return
1189
+ self._delay_jump = delay
1190
+ self._list_write(listJumpDelay, abs(delay), 0x0000 if delay > 0 else 0x8000)
1191
+
1192
+ def list_polygon_delay(self, delay):
1193
+ """
1194
+ Set polygon delay in microseconds
1195
+ @param delay:
1196
+ @return:
1197
+ """
1198
+ if self._delay_poly == delay:
1199
+ return
1200
+ self._delay_poly = delay
1201
+ self._list_write(listPolygonDelay, abs(delay), 0x0000 if delay > 0 else 0x8000)
1202
+
1203
+ def list_write_port(self):
1204
+ """
1205
+ Writes the set port values to the list.
1206
+
1207
+ @return:
1208
+ """
1209
+ self._list_write(listWritePort, self._port_bits)
1210
+
1211
+ def list_mark_current(self, current):
1212
+ """
1213
+ Also called as part of setting the power ratio. This is not correctly understood.
1214
+ @param current:
1215
+ @return:
1216
+ """
1217
+ # listMarkCurrent
1218
+ self._list_write(listMarkCurrent, current)
1219
+
1220
+ def list_mark_frequency_2(self, frequency):
1221
+ """
1222
+ Also called as part of setting frequency and is not correctly understood.
1223
+
1224
+ @param frequency:
1225
+ @return:
1226
+ """
1227
+ # listMarkFreq2
1228
+ raise NotImplementedError
1229
+
1230
+ def list_fly_enable(self, enabled=1):
1231
+ """
1232
+ On-The-Fly control enable/disable within list.
1233
+
1234
+ @param enabled:
1235
+ @return:
1236
+ """
1237
+ self._list_write(listFlyEnable, enabled)
1238
+
1239
+ def list_qswitch_period(self, qswitch):
1240
+ """
1241
+ Sets the qswitch period, which in is the inversely related to frequency.
1242
+
1243
+ @param qswitch:
1244
+ @return:
1245
+ """
1246
+ self._list_write(listQSwitchPeriod, qswitch)
1247
+
1248
+ def list_direct_laser_switch(self):
1249
+ """
1250
+ This is not understood.
1251
+ @return:
1252
+ """
1253
+ # ListDirectLaserSwitch
1254
+ raise NotImplementedError
1255
+
1256
+ def list_fly_delay(self, delay):
1257
+ """
1258
+ On-the-fly control.
1259
+
1260
+ @param delay:
1261
+ @return:
1262
+ """
1263
+ self._list_write(listFlyDelay, abs(delay), 0x0000 if delay > 0 else 0x8000)
1264
+
1265
+ def list_set_co2_fpk(self, fpk1, fpk2=None):
1266
+ """
1267
+ Set the CO2 Laser, First Pulse Killer.
1268
+
1269
+ @return:
1270
+ """
1271
+ if fpk2 is None:
1272
+ fpk2 = fpk1
1273
+ self._list_write(listSetCo2FPK, fpk1, fpk2)
1274
+
1275
+ def list_fly_wait_input(self):
1276
+ """
1277
+ Sets the On-the-fly to wait for input.
1278
+ @return:
1279
+ """
1280
+ self._list_write(listFlyWaitInput)
1281
+
1282
+ def list_fiber_open_mo(self, open_mo):
1283
+ """
1284
+ Sets motion operations, without MO set the laser does not automatically fire while moving.
1285
+
1286
+ @param open_mo:
1287
+ @return:
1288
+ """
1289
+ self._list_write(listFiberOpenMO, open_mo)
1290
+
1291
+ def list_wait_for_input(self, wait_mask, wait_level):
1292
+ """
1293
+ Unknown.
1294
+
1295
+ @return:
1296
+ """
1297
+ self._list_write(listWaitForInput, wait_mask, wait_level)
1298
+
1299
+ def list_change_mark_count(self, count):
1300
+ """
1301
+ Unknown.
1302
+
1303
+ @param count:
1304
+ @return:
1305
+ """
1306
+ self._list_write(listChangeMarkCount, count)
1307
+
1308
+ def list_set_weld_power_wave(self, weld_power_wave):
1309
+ """
1310
+ Unknown.
1311
+
1312
+ @param weld_power_wave:
1313
+ @return:
1314
+ """
1315
+ self._list_write(listSetWeldPowerWave, weld_power_wave)
1316
+
1317
+ def list_enable_weld_power_wave(self, enabled):
1318
+ """
1319
+ Unknown.
1320
+
1321
+ @param enabled:
1322
+ @return:
1323
+ """
1324
+ self._list_write(listEnableWeldPowerWave, enabled)
1325
+
1326
+ def list_fiber_ylpm_pulse_width(self, pulse_width):
1327
+ """
1328
+ Unknown.
1329
+
1330
+ @param pulse_width:
1331
+ @return:
1332
+ """
1333
+ if self._pulse_width == pulse_width:
1334
+ return
1335
+ self._pulse_width = pulse_width
1336
+ self._list_write(listFiberYLPMPulseWidth, pulse_width)
1337
+
1338
+ def list_fly_encoder_count(self, count):
1339
+ """
1340
+ Unknown.
1341
+
1342
+ @param count:
1343
+ @return:
1344
+ """
1345
+ self._list_write(listFlyEncoderCount, count)
1346
+
1347
+ def list_set_da_z_word(self, word):
1348
+ """
1349
+ Unknown.
1350
+
1351
+ @param word:
1352
+ @return:
1353
+ """
1354
+ self._list_write(listSetDaZWord, word)
1355
+
1356
+ def list_jpt_set_param(self, param):
1357
+ """
1358
+ Unknown.
1359
+
1360
+ @param param:
1361
+ @return:
1362
+ """
1363
+ self._list_write(listJptSetParam, param)
1364
+
1365
+ def list_ready(self):
1366
+ """
1367
+ Seen at the start of any new command list.
1368
+
1369
+ @return:
1370
+ """
1371
+ self._list_write(listReadyMark)
1372
+
1373
+ #######################
1374
+ # COMMAND LIST SHORTCUTS
1375
+ #######################
1376
+
1377
+ def disable_laser(self):
1378
+ return self._command(DisableLaser)
1379
+
1380
+ def enable_laser(self):
1381
+ return self._command(EnableLaser)
1382
+
1383
+ def execute_list(self):
1384
+ return self._command(ExecuteList)
1385
+
1386
+ def set_pwm_pulse_width(self, pulse_width):
1387
+ return self._command(SetPwmPulseWidth, pulse_width)
1388
+
1389
+ def get_version(self):
1390
+ return self._command(GetVersion)
1391
+
1392
+ def get_serial_number(self):
1393
+ if self.serial_number_found is None:
1394
+ self.serial_number_found = self._command(GetSerialNo)
1395
+ return self.serial_number_found
1396
+
1397
+ def get_list_status(self):
1398
+ return self._command(GetListStatus)
1399
+
1400
+ def get_position_xy(self):
1401
+ return self._command(GetPositionXY)
1402
+
1403
+ def goto_xy(self, x, y, angle=0, distance=0):
1404
+ self._last_x = x
1405
+ self._last_y = y
1406
+ return self._command(GotoXY, int(x), int(y), int(angle), int(distance))
1407
+
1408
+ def laser_signal_off(self):
1409
+ return self._command(LaserSignalOff)
1410
+
1411
+ def laser_signal_on(self):
1412
+ return self._command(LaserSignalOn)
1413
+
1414
+ def write_cor_line(self, dx, dy, non_first):
1415
+ self._command(WriteCorLine, dx, dy, non_first, read=False)
1416
+
1417
+ def reset_list(self):
1418
+ return self._command(ResetList)
1419
+
1420
+ def restart_list(self):
1421
+ return self._command(RestartList)
1422
+
1423
+ def write_cor_table(self, table: bool = True):
1424
+ return self._command(WriteCorTable, int(table))
1425
+
1426
+ def set_control_mode(self, mode):
1427
+ return self._command(SetControlMode, mode)
1428
+
1429
+ def set_delay_mode(self, mode):
1430
+ return self._command(SetDelayMode, mode)
1431
+
1432
+ def set_max_poly_delay(self, delay):
1433
+ return self._command(
1434
+ SetMaxPolyDelay, abs(delay), 0x0000 if delay > 0 else 0x8000
1435
+ )
1436
+
1437
+ def set_end_of_list(self, end):
1438
+ return self._command(SetEndOfList, end)
1439
+
1440
+ def set_first_pulse_killer(self, fpk):
1441
+ return self._command(SetFirstPulseKiller, fpk)
1442
+
1443
+ def set_laser_mode(self, mode):
1444
+ return self._command(SetLaserMode, mode)
1445
+
1446
+ def set_timing(self, timing):
1447
+ return self._command(SetTiming, timing)
1448
+
1449
+ def set_standby(self, standby1, standby2):
1450
+ return self._command(SetStandby, standby1, standby2)
1451
+
1452
+ def set_pwm_half_period(self, pwm_half_period):
1453
+ return self._command(SetPwmHalfPeriod, pwm_half_period)
1454
+
1455
+ def stop_execute(self):
1456
+ return self._command(StopExecute)
1457
+
1458
+ def stop_list(self):
1459
+ return self._command(StopList)
1460
+
1461
+ def write_port(self):
1462
+ return self._command(WritePort, self._port_bits)
1463
+
1464
+ def write_analog_port_1(self, port):
1465
+ return self._command(WriteAnalogPort1, port)
1466
+
1467
+ def write_analog_port_2(self, port):
1468
+ return self._command(WriteAnalogPort2, port)
1469
+
1470
+ def write_analog_port_x(self, port):
1471
+ return self._command(WriteAnalogPortX, port)
1472
+
1473
+ def read_port(self):
1474
+ return self._command(ReadPort)
1475
+
1476
+ def set_axis_motion_param(self, *param):
1477
+ return self._command(SetAxisMotionParam, *param)
1478
+
1479
+ def set_axis_origin_param(self, *param):
1480
+ return self._command(SetAxisOriginParam, *param)
1481
+
1482
+ def axis_go_origin(self):
1483
+ return self._command(AxisGoOrigin)
1484
+
1485
+ def move_axis_to(self, position, invert):
1486
+ return self._command(MoveAxisTo, position, invert)
1487
+
1488
+ def get_axis_pos(self, index=0):
1489
+ return self._command(GetAxisPos, index)
1490
+
1491
+ def get_fly_wait_count(self):
1492
+ return self._command(GetFlyWaitCount)
1493
+
1494
+ def get_mark_count(self):
1495
+ return self._command(GetMarkCount)
1496
+
1497
+ def set_pfk_param_2(self, param1, param2, param3, param4):
1498
+ return self._command(SetFpkParam2, param1, param2, param3, param4)
1499
+
1500
+ def set_fiber_mo(self, mo):
1501
+ """
1502
+ mo == 0 close
1503
+ mo == 1 open
1504
+
1505
+ @param mo:
1506
+ @return:
1507
+ """
1508
+ return self._command(Fiber_SetMo, mo)
1509
+
1510
+ def get_fiber_st_mo_ap(self):
1511
+ return self._command(Fiber_GetStMO_AP)
1512
+
1513
+ def enable_z(self):
1514
+ return self._command(EnableZ)
1515
+
1516
+ def disable_z(self):
1517
+ return self._command(DisableZ)
1518
+
1519
+ def set_z_data(self, zdata):
1520
+ return self._command(SetZData, zdata)
1521
+
1522
+ def set_spi_simmer_current(self, current):
1523
+ return self._command(SetSPISimmerCurrent, current)
1524
+
1525
+ def set_fpk_param(self, param):
1526
+ return self._command(SetFpkParam, param)
1527
+
1528
+ def reset(self):
1529
+ return self._command(Reset)
1530
+
1531
+ def get_fly_speed(self):
1532
+ return self._command(GetFlySpeed)
1533
+
1534
+ def fiber_pulse_width(self):
1535
+ return self._command(FiberPulseWidth)
1536
+
1537
+ def get_fiber_config_extend(self):
1538
+ return self._command(FiberGetConfigExtend)
1539
+
1540
+ def input_port(self, port):
1541
+ return self._command(InputPort, port)
1542
+
1543
+ def clear_lock_input_port(self):
1544
+ return self._command(InputPort, 0x04)
1545
+
1546
+ def enable_lock_input_port(self):
1547
+ return self._command(InputPort, 0x02)
1548
+
1549
+ def disable_lock_input_port(self):
1550
+ return self._command(InputPort, 0x01)
1551
+
1552
+ def get_input_port(self):
1553
+ return self._command(InputPort)
1554
+
1555
+ def get_mark_time(self):
1556
+ """
1557
+ Get Mark Time is always called with data 3. With 0 it returns 0. It is unknown what the payload means.
1558
+ @return:
1559
+ """
1560
+ return self._command(GetMarkTime, 3)
1561
+
1562
+ def get_user_data(self):
1563
+ return self._command(GetUserData)
1564
+
1565
+ def set_fly_res(self, fly_res1, fly_res2, fly_res3, fly_res4):
1566
+ return self._command(SetFlyRes, fly_res1, fly_res2, fly_res3, fly_res4)