meerk40t 0.9.3001__py2.py3-none-any.whl → 0.9.7010__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meerk40t/__init__.py +1 -1
- meerk40t/balormk/balor_params.py +167 -167
- meerk40t/balormk/clone_loader.py +457 -457
- meerk40t/balormk/controller.py +1566 -1512
- meerk40t/balormk/cylindermod.py +64 -0
- meerk40t/balormk/device.py +966 -1959
- meerk40t/balormk/driver.py +778 -591
- meerk40t/balormk/galvo_commands.py +1195 -0
- meerk40t/balormk/gui/balorconfig.py +237 -111
- meerk40t/balormk/gui/balorcontroller.py +191 -184
- meerk40t/balormk/gui/baloroperationproperties.py +116 -115
- meerk40t/balormk/gui/corscene.py +845 -0
- meerk40t/balormk/gui/gui.py +179 -147
- meerk40t/balormk/livelightjob.py +466 -382
- meerk40t/balormk/mock_connection.py +131 -109
- meerk40t/balormk/plugin.py +133 -135
- meerk40t/balormk/usb_connection.py +306 -301
- meerk40t/camera/__init__.py +1 -1
- meerk40t/camera/camera.py +514 -397
- meerk40t/camera/gui/camerapanel.py +1241 -1095
- meerk40t/camera/gui/gui.py +58 -58
- meerk40t/camera/plugin.py +441 -399
- meerk40t/ch341/__init__.py +27 -27
- meerk40t/ch341/ch341device.py +628 -628
- meerk40t/ch341/libusb.py +595 -589
- meerk40t/ch341/mock.py +171 -171
- meerk40t/ch341/windriver.py +157 -157
- meerk40t/constants.py +13 -0
- meerk40t/core/__init__.py +1 -1
- meerk40t/core/bindalias.py +550 -539
- meerk40t/core/core.py +47 -47
- meerk40t/core/cutcode/cubiccut.py +73 -73
- meerk40t/core/cutcode/cutcode.py +315 -312
- meerk40t/core/cutcode/cutgroup.py +141 -137
- meerk40t/core/cutcode/cutobject.py +192 -185
- meerk40t/core/cutcode/dwellcut.py +37 -37
- meerk40t/core/cutcode/gotocut.py +29 -29
- meerk40t/core/cutcode/homecut.py +29 -29
- meerk40t/core/cutcode/inputcut.py +34 -34
- meerk40t/core/cutcode/linecut.py +33 -33
- meerk40t/core/cutcode/outputcut.py +34 -34
- meerk40t/core/cutcode/plotcut.py +335 -335
- meerk40t/core/cutcode/quadcut.py +61 -61
- meerk40t/core/cutcode/rastercut.py +168 -148
- meerk40t/core/cutcode/waitcut.py +34 -34
- meerk40t/core/cutplan.py +1843 -1316
- meerk40t/core/drivers.py +330 -329
- meerk40t/core/elements/align.py +801 -669
- meerk40t/core/elements/branches.py +1844 -1507
- meerk40t/core/elements/clipboard.py +229 -219
- meerk40t/core/elements/element_treeops.py +4561 -2837
- meerk40t/core/elements/element_types.py +125 -105
- meerk40t/core/elements/elements.py +4329 -3617
- meerk40t/core/elements/files.py +117 -64
- meerk40t/core/elements/geometry.py +473 -224
- meerk40t/core/elements/grid.py +467 -316
- meerk40t/core/elements/materials.py +158 -94
- meerk40t/core/elements/notes.py +50 -38
- meerk40t/core/elements/offset_clpr.py +933 -912
- meerk40t/core/elements/offset_mk.py +963 -955
- meerk40t/core/elements/penbox.py +339 -267
- meerk40t/core/elements/placements.py +300 -83
- meerk40t/core/elements/render.py +785 -687
- meerk40t/core/elements/shapes.py +2618 -2092
- meerk40t/core/elements/trace.py +651 -563
- meerk40t/core/elements/tree_commands.py +415 -409
- meerk40t/core/elements/undo_redo.py +116 -58
- meerk40t/core/elements/wordlist.py +319 -200
- meerk40t/core/exceptions.py +9 -9
- meerk40t/core/laserjob.py +220 -220
- meerk40t/core/logging.py +63 -63
- meerk40t/core/node/blobnode.py +83 -86
- meerk40t/core/node/bootstrap.py +105 -103
- meerk40t/core/node/branch_elems.py +40 -31
- meerk40t/core/node/branch_ops.py +45 -38
- meerk40t/core/node/branch_regmark.py +48 -41
- meerk40t/core/node/cutnode.py +29 -32
- meerk40t/core/node/effect_hatch.py +375 -257
- meerk40t/core/node/effect_warp.py +398 -0
- meerk40t/core/node/effect_wobble.py +441 -309
- meerk40t/core/node/elem_ellipse.py +404 -309
- meerk40t/core/node/elem_image.py +1082 -801
- meerk40t/core/node/elem_line.py +358 -292
- meerk40t/core/node/elem_path.py +259 -201
- meerk40t/core/node/elem_point.py +129 -102
- meerk40t/core/node/elem_polyline.py +310 -246
- meerk40t/core/node/elem_rect.py +376 -286
- meerk40t/core/node/elem_text.py +445 -418
- meerk40t/core/node/filenode.py +59 -40
- meerk40t/core/node/groupnode.py +138 -74
- meerk40t/core/node/image_processed.py +777 -766
- meerk40t/core/node/image_raster.py +156 -113
- meerk40t/core/node/layernode.py +31 -31
- meerk40t/core/node/mixins.py +135 -107
- meerk40t/core/node/node.py +1427 -1304
- meerk40t/core/node/nutils.py +117 -114
- meerk40t/core/node/op_cut.py +462 -335
- meerk40t/core/node/op_dots.py +296 -251
- meerk40t/core/node/op_engrave.py +414 -311
- meerk40t/core/node/op_image.py +755 -369
- meerk40t/core/node/op_raster.py +787 -522
- meerk40t/core/node/place_current.py +37 -40
- meerk40t/core/node/place_point.py +329 -126
- meerk40t/core/node/refnode.py +58 -47
- meerk40t/core/node/rootnode.py +225 -219
- meerk40t/core/node/util_console.py +48 -48
- meerk40t/core/node/util_goto.py +84 -65
- meerk40t/core/node/util_home.py +61 -61
- meerk40t/core/node/util_input.py +102 -102
- meerk40t/core/node/util_output.py +102 -102
- meerk40t/core/node/util_wait.py +65 -65
- meerk40t/core/parameters.py +709 -707
- meerk40t/core/planner.py +875 -785
- meerk40t/core/plotplanner.py +656 -652
- meerk40t/core/space.py +120 -113
- meerk40t/core/spoolers.py +706 -705
- meerk40t/core/svg_io.py +1836 -1549
- meerk40t/core/treeop.py +534 -445
- meerk40t/core/undos.py +278 -124
- meerk40t/core/units.py +784 -680
- meerk40t/core/view.py +393 -322
- meerk40t/core/webhelp.py +62 -62
- meerk40t/core/wordlist.py +513 -504
- meerk40t/cylinder/cylinder.py +247 -0
- meerk40t/cylinder/gui/cylindersettings.py +41 -0
- meerk40t/cylinder/gui/gui.py +24 -0
- meerk40t/device/__init__.py +1 -1
- meerk40t/device/basedevice.py +322 -123
- meerk40t/device/devicechoices.py +50 -0
- meerk40t/device/dummydevice.py +163 -128
- meerk40t/device/gui/defaultactions.py +618 -602
- meerk40t/device/gui/effectspanel.py +114 -0
- meerk40t/device/gui/formatterpanel.py +253 -290
- meerk40t/device/gui/warningpanel.py +337 -260
- meerk40t/device/mixins.py +13 -13
- meerk40t/dxf/__init__.py +1 -1
- meerk40t/dxf/dxf_io.py +766 -554
- meerk40t/dxf/plugin.py +47 -35
- meerk40t/external_plugins.py +79 -79
- meerk40t/external_plugins_build.py +28 -28
- meerk40t/extra/cag.py +112 -116
- meerk40t/extra/coolant.py +403 -0
- meerk40t/extra/encode_detect.py +198 -0
- meerk40t/extra/ezd.py +1165 -1165
- meerk40t/extra/hershey.py +835 -340
- meerk40t/extra/imageactions.py +322 -316
- meerk40t/extra/inkscape.py +630 -622
- meerk40t/extra/lbrn.py +424 -424
- meerk40t/extra/outerworld.py +284 -0
- meerk40t/extra/param_functions.py +1542 -1556
- meerk40t/extra/potrace.py +257 -253
- meerk40t/extra/serial_exchange.py +118 -0
- meerk40t/extra/updater.py +602 -453
- meerk40t/extra/vectrace.py +147 -146
- meerk40t/extra/winsleep.py +83 -83
- meerk40t/extra/xcs_reader.py +597 -0
- meerk40t/fill/fills.py +781 -335
- meerk40t/fill/patternfill.py +1061 -1061
- meerk40t/fill/patterns.py +614 -567
- meerk40t/grbl/control.py +87 -87
- meerk40t/grbl/controller.py +990 -903
- meerk40t/grbl/device.py +1081 -768
- meerk40t/grbl/driver.py +989 -771
- meerk40t/grbl/emulator.py +532 -497
- meerk40t/grbl/gcodejob.py +783 -767
- meerk40t/grbl/gui/grblconfiguration.py +373 -298
- meerk40t/grbl/gui/grblcontroller.py +485 -271
- meerk40t/grbl/gui/grblhardwareconfig.py +269 -153
- meerk40t/grbl/gui/grbloperationconfig.py +105 -0
- meerk40t/grbl/gui/gui.py +147 -116
- meerk40t/grbl/interpreter.py +44 -44
- meerk40t/grbl/loader.py +22 -22
- meerk40t/grbl/mock_connection.py +56 -56
- meerk40t/grbl/plugin.py +294 -264
- meerk40t/grbl/serial_connection.py +93 -88
- meerk40t/grbl/tcp_connection.py +81 -79
- meerk40t/grbl/ws_connection.py +112 -0
- meerk40t/gui/__init__.py +1 -1
- meerk40t/gui/about.py +2042 -296
- meerk40t/gui/alignment.py +1644 -1608
- meerk40t/gui/autoexec.py +199 -0
- meerk40t/gui/basicops.py +791 -670
- meerk40t/gui/bufferview.py +77 -71
- meerk40t/gui/busy.py +170 -133
- meerk40t/gui/choicepropertypanel.py +1673 -1469
- meerk40t/gui/consolepanel.py +706 -542
- meerk40t/gui/devicepanel.py +687 -581
- meerk40t/gui/dialogoptions.py +110 -107
- meerk40t/gui/executejob.py +316 -306
- meerk40t/gui/fonts.py +90 -90
- meerk40t/gui/functionwrapper.py +252 -0
- meerk40t/gui/gui_mixins.py +729 -0
- meerk40t/gui/guicolors.py +205 -182
- meerk40t/gui/help_assets/help_assets.py +218 -201
- meerk40t/gui/helper.py +154 -0
- meerk40t/gui/hersheymanager.py +1430 -846
- meerk40t/gui/icons.py +3422 -2747
- meerk40t/gui/imagesplitter.py +555 -508
- meerk40t/gui/keymap.py +354 -344
- meerk40t/gui/laserpanel.py +892 -806
- meerk40t/gui/laserrender.py +1470 -1232
- meerk40t/gui/lasertoolpanel.py +805 -793
- meerk40t/gui/magnetoptions.py +436 -0
- meerk40t/gui/materialmanager.py +2917 -0
- meerk40t/gui/materialtest.py +1722 -1694
- meerk40t/gui/mkdebug.py +646 -359
- meerk40t/gui/mwindow.py +163 -140
- meerk40t/gui/navigationpanels.py +2605 -2467
- meerk40t/gui/notes.py +143 -142
- meerk40t/gui/opassignment.py +414 -410
- meerk40t/gui/operation_info.py +310 -299
- meerk40t/gui/plugin.py +494 -328
- meerk40t/gui/position.py +714 -669
- meerk40t/gui/preferences.py +901 -650
- meerk40t/gui/propertypanels/attributes.py +1461 -1131
- meerk40t/gui/propertypanels/blobproperty.py +117 -114
- meerk40t/gui/propertypanels/consoleproperty.py +83 -80
- meerk40t/gui/propertypanels/gotoproperty.py +77 -0
- meerk40t/gui/propertypanels/groupproperties.py +223 -217
- meerk40t/gui/propertypanels/hatchproperty.py +489 -469
- meerk40t/gui/propertypanels/imageproperty.py +2244 -1384
- meerk40t/gui/propertypanels/inputproperty.py +59 -58
- meerk40t/gui/propertypanels/opbranchproperties.py +82 -80
- meerk40t/gui/propertypanels/operationpropertymain.py +1890 -1638
- meerk40t/gui/propertypanels/outputproperty.py +59 -58
- meerk40t/gui/propertypanels/pathproperty.py +389 -380
- meerk40t/gui/propertypanels/placementproperty.py +1214 -383
- meerk40t/gui/propertypanels/pointproperty.py +140 -136
- meerk40t/gui/propertypanels/propertywindow.py +313 -181
- meerk40t/gui/propertypanels/rasterwizardpanels.py +996 -912
- meerk40t/gui/propertypanels/regbranchproperties.py +76 -0
- meerk40t/gui/propertypanels/textproperty.py +770 -755
- meerk40t/gui/propertypanels/waitproperty.py +56 -55
- meerk40t/gui/propertypanels/warpproperty.py +121 -0
- meerk40t/gui/propertypanels/wobbleproperty.py +255 -204
- meerk40t/gui/ribbon.py +2468 -2210
- meerk40t/gui/scene/scene.py +1100 -1051
- meerk40t/gui/scene/sceneconst.py +22 -22
- meerk40t/gui/scene/scenepanel.py +439 -349
- meerk40t/gui/scene/scenespacewidget.py +365 -365
- meerk40t/gui/scene/widget.py +518 -505
- meerk40t/gui/scenewidgets/affinemover.py +215 -215
- meerk40t/gui/scenewidgets/attractionwidget.py +315 -309
- meerk40t/gui/scenewidgets/bedwidget.py +120 -97
- meerk40t/gui/scenewidgets/elementswidget.py +137 -107
- meerk40t/gui/scenewidgets/gridwidget.py +785 -745
- meerk40t/gui/scenewidgets/guidewidget.py +765 -765
- meerk40t/gui/scenewidgets/laserpathwidget.py +66 -66
- meerk40t/gui/scenewidgets/machineoriginwidget.py +86 -86
- meerk40t/gui/scenewidgets/nodeselector.py +28 -28
- meerk40t/gui/scenewidgets/rectselectwidget.py +589 -346
- meerk40t/gui/scenewidgets/relocatewidget.py +33 -33
- meerk40t/gui/scenewidgets/reticlewidget.py +83 -83
- meerk40t/gui/scenewidgets/selectionwidget.py +2952 -2756
- meerk40t/gui/simpleui.py +357 -333
- meerk40t/gui/simulation.py +2431 -2094
- meerk40t/gui/snapoptions.py +208 -203
- meerk40t/gui/spoolerpanel.py +1227 -1180
- meerk40t/gui/statusbarwidgets/defaultoperations.py +480 -353
- meerk40t/gui/statusbarwidgets/infowidget.py +520 -483
- meerk40t/gui/statusbarwidgets/opassignwidget.py +356 -355
- meerk40t/gui/statusbarwidgets/selectionwidget.py +172 -171
- meerk40t/gui/statusbarwidgets/shapepropwidget.py +754 -236
- meerk40t/gui/statusbarwidgets/statusbar.py +272 -260
- meerk40t/gui/statusbarwidgets/statusbarwidget.py +268 -270
- meerk40t/gui/statusbarwidgets/strokewidget.py +267 -251
- meerk40t/gui/themes.py +200 -78
- meerk40t/gui/tips.py +591 -0
- meerk40t/gui/toolwidgets/circlebrush.py +35 -35
- meerk40t/gui/toolwidgets/toolcircle.py +248 -242
- meerk40t/gui/toolwidgets/toolcontainer.py +82 -77
- meerk40t/gui/toolwidgets/tooldraw.py +97 -90
- meerk40t/gui/toolwidgets/toolellipse.py +219 -212
- meerk40t/gui/toolwidgets/toolimagecut.py +25 -132
- meerk40t/gui/toolwidgets/toolline.py +39 -144
- meerk40t/gui/toolwidgets/toollinetext.py +79 -236
- meerk40t/gui/toolwidgets/toollinetext_inline.py +296 -0
- meerk40t/gui/toolwidgets/toolmeasure.py +160 -216
- meerk40t/gui/toolwidgets/toolnodeedit.py +2088 -2074
- meerk40t/gui/toolwidgets/toolnodemove.py +92 -94
- meerk40t/gui/toolwidgets/toolparameter.py +754 -668
- meerk40t/gui/toolwidgets/toolplacement.py +108 -108
- meerk40t/gui/toolwidgets/toolpoint.py +68 -59
- meerk40t/gui/toolwidgets/toolpointlistbuilder.py +294 -0
- meerk40t/gui/toolwidgets/toolpointmove.py +183 -0
- meerk40t/gui/toolwidgets/toolpolygon.py +288 -403
- meerk40t/gui/toolwidgets/toolpolyline.py +38 -196
- meerk40t/gui/toolwidgets/toolrect.py +211 -207
- meerk40t/gui/toolwidgets/toolrelocate.py +72 -72
- meerk40t/gui/toolwidgets/toolribbon.py +598 -113
- meerk40t/gui/toolwidgets/tooltabedit.py +546 -0
- meerk40t/gui/toolwidgets/tooltext.py +98 -89
- meerk40t/gui/toolwidgets/toolvector.py +213 -204
- meerk40t/gui/toolwidgets/toolwidget.py +39 -39
- meerk40t/gui/usbconnect.py +98 -91
- meerk40t/gui/utilitywidgets/buttonwidget.py +18 -18
- meerk40t/gui/utilitywidgets/checkboxwidget.py +90 -90
- meerk40t/gui/utilitywidgets/controlwidget.py +14 -14
- meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +343 -340
- meerk40t/gui/utilitywidgets/debugwidgets.py +148 -0
- meerk40t/gui/utilitywidgets/handlewidget.py +27 -27
- meerk40t/gui/utilitywidgets/harmonograph.py +450 -447
- meerk40t/gui/utilitywidgets/openclosewidget.py +40 -40
- meerk40t/gui/utilitywidgets/rotationwidget.py +54 -54
- meerk40t/gui/utilitywidgets/scalewidget.py +75 -75
- meerk40t/gui/utilitywidgets/seekbarwidget.py +183 -183
- meerk40t/gui/utilitywidgets/togglewidget.py +142 -142
- meerk40t/gui/utilitywidgets/toolbarwidget.py +8 -8
- meerk40t/gui/wordlisteditor.py +985 -931
- meerk40t/gui/wxmeerk40t.py +1444 -1169
- meerk40t/gui/wxmmain.py +5578 -4112
- meerk40t/gui/wxmribbon.py +1591 -1076
- meerk40t/gui/wxmscene.py +1635 -1453
- meerk40t/gui/wxmtree.py +2410 -2089
- meerk40t/gui/wxutils.py +1769 -1099
- meerk40t/gui/zmatrix.py +102 -102
- meerk40t/image/__init__.py +1 -1
- meerk40t/image/dither.py +429 -0
- meerk40t/image/imagetools.py +2778 -2269
- meerk40t/internal_plugins.py +150 -130
- meerk40t/kernel/__init__.py +63 -12
- meerk40t/kernel/channel.py +259 -212
- meerk40t/kernel/context.py +538 -538
- meerk40t/kernel/exceptions.py +41 -41
- meerk40t/kernel/functions.py +463 -414
- meerk40t/kernel/jobs.py +100 -100
- meerk40t/kernel/kernel.py +3809 -3571
- meerk40t/kernel/lifecycles.py +71 -71
- meerk40t/kernel/module.py +49 -49
- meerk40t/kernel/service.py +147 -147
- meerk40t/kernel/settings.py +383 -343
- meerk40t/lihuiyu/controller.py +883 -876
- meerk40t/lihuiyu/device.py +1181 -1069
- meerk40t/lihuiyu/driver.py +1466 -1372
- meerk40t/lihuiyu/gui/gui.py +127 -106
- meerk40t/lihuiyu/gui/lhyaccelgui.py +377 -363
- meerk40t/lihuiyu/gui/lhycontrollergui.py +741 -651
- meerk40t/lihuiyu/gui/lhydrivergui.py +470 -446
- meerk40t/lihuiyu/gui/lhyoperationproperties.py +238 -237
- meerk40t/lihuiyu/gui/tcpcontroller.py +226 -190
- meerk40t/lihuiyu/interpreter.py +53 -53
- meerk40t/lihuiyu/laserspeed.py +450 -450
- meerk40t/lihuiyu/loader.py +90 -90
- meerk40t/lihuiyu/parser.py +404 -404
- meerk40t/lihuiyu/plugin.py +101 -102
- meerk40t/lihuiyu/tcp_connection.py +111 -109
- meerk40t/main.py +231 -165
- meerk40t/moshi/builder.py +788 -781
- meerk40t/moshi/controller.py +505 -499
- meerk40t/moshi/device.py +495 -442
- meerk40t/moshi/driver.py +862 -696
- meerk40t/moshi/gui/gui.py +78 -76
- meerk40t/moshi/gui/moshicontrollergui.py +538 -522
- meerk40t/moshi/gui/moshidrivergui.py +87 -75
- meerk40t/moshi/plugin.py +43 -43
- meerk40t/network/console_server.py +102 -57
- meerk40t/network/kernelserver.py +10 -9
- meerk40t/network/tcp_server.py +142 -140
- meerk40t/network/udp_server.py +103 -77
- meerk40t/network/web_server.py +390 -0
- meerk40t/newly/controller.py +1158 -1144
- meerk40t/newly/device.py +874 -732
- meerk40t/newly/driver.py +540 -412
- meerk40t/newly/gui/gui.py +219 -188
- meerk40t/newly/gui/newlyconfig.py +116 -101
- meerk40t/newly/gui/newlycontroller.py +193 -186
- meerk40t/newly/gui/operationproperties.py +51 -51
- meerk40t/newly/mock_connection.py +82 -82
- meerk40t/newly/newly_params.py +56 -56
- meerk40t/newly/plugin.py +1214 -1246
- meerk40t/newly/usb_connection.py +322 -322
- meerk40t/rotary/gui/gui.py +52 -46
- meerk40t/rotary/gui/rotarysettings.py +240 -232
- meerk40t/rotary/rotary.py +202 -98
- meerk40t/ruida/control.py +291 -91
- meerk40t/ruida/controller.py +138 -1088
- meerk40t/ruida/device.py +672 -231
- meerk40t/ruida/driver.py +534 -472
- meerk40t/ruida/emulator.py +1494 -1491
- meerk40t/ruida/exceptions.py +4 -4
- meerk40t/ruida/gui/gui.py +71 -76
- meerk40t/ruida/gui/ruidaconfig.py +239 -72
- meerk40t/ruida/gui/ruidacontroller.py +187 -184
- meerk40t/ruida/gui/ruidaoperationproperties.py +48 -47
- meerk40t/ruida/loader.py +54 -52
- meerk40t/ruida/mock_connection.py +57 -109
- meerk40t/ruida/plugin.py +124 -87
- meerk40t/ruida/rdjob.py +2084 -945
- meerk40t/ruida/serial_connection.py +116 -0
- meerk40t/ruida/tcp_connection.py +146 -0
- meerk40t/ruida/udp_connection.py +73 -0
- meerk40t/svgelements.py +9671 -9669
- meerk40t/tools/driver_to_path.py +584 -579
- meerk40t/tools/geomstr.py +5583 -4680
- meerk40t/tools/jhfparser.py +357 -292
- meerk40t/tools/kerftest.py +904 -890
- meerk40t/tools/livinghinges.py +1168 -1033
- meerk40t/tools/pathtools.py +987 -949
- meerk40t/tools/pmatrix.py +234 -0
- meerk40t/tools/pointfinder.py +942 -942
- meerk40t/tools/polybool.py +940 -940
- meerk40t/tools/rasterplotter.py +1660 -547
- meerk40t/tools/shxparser.py +989 -901
- meerk40t/tools/ttfparser.py +726 -446
- meerk40t/tools/zinglplotter.py +595 -593
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/LICENSE +21 -21
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/METADATA +150 -139
- meerk40t-0.9.7010.dist-info/RECORD +445 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/WHEEL +1 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/top_level.txt +0 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/zip-safe +1 -1
- meerk40t/balormk/elementlightjob.py +0 -159
- meerk40t-0.9.3001.dist-info/RECORD +0 -437
- test/bootstrap.py +0 -63
- test/test_cli.py +0 -12
- test/test_core_cutcode.py +0 -418
- test/test_core_elements.py +0 -144
- test/test_core_plotplanner.py +0 -397
- test/test_core_viewports.py +0 -312
- test/test_drivers_grbl.py +0 -108
- test/test_drivers_lihuiyu.py +0 -443
- test/test_drivers_newly.py +0 -113
- test/test_element_degenerate_points.py +0 -43
- test/test_elements_classify.py +0 -97
- test/test_elements_penbox.py +0 -22
- test/test_file_svg.py +0 -176
- test/test_fill.py +0 -155
- test/test_geomstr.py +0 -1523
- test/test_geomstr_nodes.py +0 -18
- test/test_imagetools_actualize.py +0 -306
- test/test_imagetools_wizard.py +0 -258
- test/test_kernel.py +0 -200
- test/test_laser_speeds.py +0 -3303
- test/test_length.py +0 -57
- test/test_lifecycle.py +0 -66
- test/test_operations.py +0 -251
- test/test_operations_hatch.py +0 -57
- test/test_ruida.py +0 -19
- test/test_spooler.py +0 -22
- test/test_tools_rasterplotter.py +0 -29
- test/test_wobble.py +0 -133
- test/test_zingl.py +0 -124
- {test → meerk40t/cylinder}/__init__.py +0 -0
- /meerk40t/{core/element_commands.py → cylinder/gui/__init__.py} +0 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7010.dist-info}/entry_points.txt +0 -0
meerk40t/ch341/ch341device.py
CHANGED
@@ -1,628 +1,628 @@
|
|
1
|
-
"""
|
2
|
-
There are 2 primary parts of this code, the first is divided into three sections.
|
3
|
-
|
4
|
-
Kernel is needed to open files namely the usb device.
|
5
|
-
Ole32 is needed briefly to convert the GUID name.
|
6
|
-
Setupaapi is needed in places to query the devices.
|
7
|
-
|
8
|
-
Most of these are taken from pySerial, since that's a lot safer. It is very easy to mess this up and lose compatibility
|
9
|
-
with various versions of windows.
|
10
|
-
|
11
|
-
---
|
12
|
-
https://github.com/pyserial/pyserial
|
13
|
-
(C) 2001-2016 Chris Liechti <cliechti@gmx.net>
|
14
|
-
|
15
|
-
SPDX-License-Identifier: BSD-3-Clause
|
16
|
-
---
|
17
|
-
|
18
|
-
The gwangyi library was also highly useful, and largely gave rise to the Ole32 functions.
|
19
|
-
--
|
20
|
-
https://github.com/gwangyi/pysetupdi
|
21
|
-
MIT License
|
22
|
-
Copyright (c) 2016 gwangyi
|
23
|
-
---
|
24
|
-
|
25
|
-
The second part is largely just mimicking the functionality of the CH341DLL.dll driver. The inclusion of that file
|
26
|
-
was always a bit iffy and error prone.
|
27
|
-
"""
|
28
|
-
|
29
|
-
|
30
|
-
import ctypes
|
31
|
-
import struct
|
32
|
-
from ctypes import POINTER, Structure, WinDLL, c_int64, c_ulong, c_void_p, sizeof
|
33
|
-
from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, HWND, LPCWSTR, WORD
|
34
|
-
|
35
|
-
_stdcall_libraries = {}
|
36
|
-
_stdcall_libraries["kernel32"] = WinDLL("kernel32")
|
37
|
-
|
38
|
-
|
39
|
-
# some details of the
|
40
|
-
def is_64bit():
|
41
|
-
"""Returns true when running on a 64
|
42
|
-
return sizeof(c_ulong) != sizeof(c_void_p)
|
43
|
-
|
44
|
-
|
45
|
-
# ULONG_PTR is
|
46
|
-
# is either 32 or 64 bits, depending on the type of windows...
|
47
|
-
# so test if this a 32
|
48
|
-
if is_64bit():
|
49
|
-
ULONG_PTR = c_int64
|
50
|
-
else:
|
51
|
-
ULONG_PTR = c_ulong
|
52
|
-
|
53
|
-
|
54
|
-
class _SECURITY_ATTRIBUTES(Structure):
|
55
|
-
pass
|
56
|
-
|
57
|
-
|
58
|
-
LPSECURITY_ATTRIBUTES = POINTER(_SECURITY_ATTRIBUTES)
|
59
|
-
|
60
|
-
|
61
|
-
class _OVERLAPPED(Structure):
|
62
|
-
pass
|
63
|
-
|
64
|
-
|
65
|
-
OVERLAPPED = _OVERLAPPED
|
66
|
-
LPOVERLAPPED = POINTER(_OVERLAPPED)
|
67
|
-
LPDWORD = POINTER(DWORD)
|
68
|
-
LPVOID = c_void_p
|
69
|
-
|
70
|
-
|
71
|
-
try:
|
72
|
-
CreateEventW = _stdcall_libraries["kernel32"].CreateEventW
|
73
|
-
except AttributeError:
|
74
|
-
# Fallback to non-wide char version for old OS...
|
75
|
-
from ctypes.wintypes import LPCSTR
|
76
|
-
|
77
|
-
CreateEventA = _stdcall_libraries["kernel32"].CreateEventA
|
78
|
-
CreateEventA.restype = HANDLE
|
79
|
-
CreateEventA.argtypes = [LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCSTR]
|
80
|
-
CreateEvent = CreateEventA
|
81
|
-
|
82
|
-
CreateFileA = _stdcall_libraries["kernel32"].CreateFileA
|
83
|
-
CreateFileA.restype = HANDLE
|
84
|
-
CreateFileA.argtypes = [
|
85
|
-
LPCSTR,
|
86
|
-
DWORD,
|
87
|
-
DWORD,
|
88
|
-
LPSECURITY_ATTRIBUTES,
|
89
|
-
DWORD,
|
90
|
-
DWORD,
|
91
|
-
HANDLE,
|
92
|
-
]
|
93
|
-
CreateFile = CreateFileA
|
94
|
-
else:
|
95
|
-
CreateEventW.restype = HANDLE
|
96
|
-
CreateEventW.argtypes = [LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCWSTR]
|
97
|
-
CreateEvent = CreateEventW # alias
|
98
|
-
|
99
|
-
CreateFileW = _stdcall_libraries["kernel32"].CreateFileW
|
100
|
-
CreateFileW.restype = HANDLE
|
101
|
-
CreateFileW.argtypes = [
|
102
|
-
LPCWSTR,
|
103
|
-
DWORD,
|
104
|
-
DWORD,
|
105
|
-
LPSECURITY_ATTRIBUTES,
|
106
|
-
DWORD,
|
107
|
-
DWORD,
|
108
|
-
HANDLE,
|
109
|
-
]
|
110
|
-
CreateFile = CreateFileW # alias
|
111
|
-
|
112
|
-
|
113
|
-
def validate_handle(handle, function, args):
|
114
|
-
handle = HANDLE(handle)
|
115
|
-
if handle.value == HANDLE(-1).value:
|
116
|
-
raise ConnectionError(f"Error {GetLastError()}. Failed to open.")
|
117
|
-
return handle
|
118
|
-
|
119
|
-
|
120
|
-
CreateFile.errcheck = validate_handle
|
121
|
-
|
122
|
-
|
123
|
-
GetLastError = _stdcall_libraries["kernel32"].GetLastError
|
124
|
-
GetLastError.restype = DWORD
|
125
|
-
GetLastError.argtypes = []
|
126
|
-
|
127
|
-
CloseHandle = _stdcall_libraries["kernel32"].CloseHandle
|
128
|
-
CloseHandle.restype = BOOL
|
129
|
-
CloseHandle.argtypes = [HANDLE]
|
130
|
-
|
131
|
-
DeviceIoControl = _stdcall_libraries["kernel32"].DeviceIoControl
|
132
|
-
DeviceIoControl.argtypes = [
|
133
|
-
HANDLE,
|
134
|
-
DWORD,
|
135
|
-
LPVOID,
|
136
|
-
DWORD,
|
137
|
-
LPVOID,
|
138
|
-
DWORD,
|
139
|
-
LPDWORD,
|
140
|
-
LPOVERLAPPED,
|
141
|
-
]
|
142
|
-
DeviceIoControl.restype = BOOL
|
143
|
-
dwBytesReturned = DWORD(0)
|
144
|
-
lpBytesReturned = ctypes.byref(dwBytesReturned)
|
145
|
-
|
146
|
-
|
147
|
-
ERROR_SUCCESS = 0
|
148
|
-
ERROR_INSUFFICIENT_BUFFER = 122
|
149
|
-
ERROR_STILL_ACTIVE = 259
|
150
|
-
|
151
|
-
GENERIC_READ = 0x80000000
|
152
|
-
GENERIC_WRITE = 0x40000000
|
153
|
-
GENERIC_EXECUTE = 0x20000000
|
154
|
-
GENERIC_ALL = 0x10000000
|
155
|
-
|
156
|
-
FILE_ATTRIBUTE_NORMAL = 0x00000080
|
157
|
-
|
158
|
-
CREATE_NEW = 1
|
159
|
-
CREATE_ALWAYS = 2
|
160
|
-
OPEN_EXISTING = 3
|
161
|
-
OPEN_ALWAYS = 4
|
162
|
-
TRUNCATE_EXISTING = 5
|
163
|
-
|
164
|
-
|
165
|
-
################################
|
166
|
-
# ole32 Section.
|
167
|
-
# Allows creations of GUID structures from strings.
|
168
|
-
################################
|
169
|
-
|
170
|
-
ole32 = ctypes.windll.LoadLibrary("ole32")
|
171
|
-
CLSIDFromString = ole32.CLSIDFromString
|
172
|
-
|
173
|
-
################################
|
174
|
-
# SetupApi Section.
|
175
|
-
################################
|
176
|
-
|
177
|
-
setupapi = ctypes.windll.LoadLibrary("setupapi")
|
178
|
-
|
179
|
-
|
180
|
-
class GUID(ctypes.Structure):
|
181
|
-
_fields_ = [
|
182
|
-
("Data1", DWORD),
|
183
|
-
("Data2", WORD),
|
184
|
-
("Data3", WORD),
|
185
|
-
("Data4", BYTE * 8),
|
186
|
-
]
|
187
|
-
|
188
|
-
def __init__(self, guid):
|
189
|
-
super().__init__()
|
190
|
-
ret = CLSIDFromString(ctypes.create_unicode_buffer(guid), ctypes.byref(self))
|
191
|
-
if ret < 0:
|
192
|
-
err_no = GetLastError()
|
193
|
-
raise OSError(err_no, ctypes.FormatError(err_no), guid)
|
194
|
-
|
195
|
-
def __str__(self):
|
196
|
-
return "{{{:08x}-{:04x}-{:04x}-{}-{}}}".format(
|
197
|
-
self.Data1,
|
198
|
-
self.Data2,
|
199
|
-
self.Data3,
|
200
|
-
"".join(["{:02x}".format(d) for d in self.Data4[:2]]),
|
201
|
-
"".join(["{:02x}".format(d) for d in self.Data4[2:]]),
|
202
|
-
)
|
203
|
-
|
204
|
-
|
205
|
-
class SP_DEVINFO_DATA(ctypes.Structure):
|
206
|
-
_fields_ = [
|
207
|
-
("cbSize", DWORD),
|
208
|
-
("ClassGuid", GUID),
|
209
|
-
("DevInst", DWORD),
|
210
|
-
("Reserved", ULONG_PTR),
|
211
|
-
]
|
212
|
-
|
213
|
-
def __str__(self):
|
214
|
-
return "ClassGuid:{} DevInst:{}".format(self.ClassGuid, self.DevInst)
|
215
|
-
|
216
|
-
|
217
|
-
class DEVPROP_KEY(ctypes.Structure):
|
218
|
-
_fields_ = [("fmtid", GUID), ("pid", ctypes.c_ulong)]
|
219
|
-
|
220
|
-
def __init__(self, guid, pid):
|
221
|
-
super().__init__()
|
222
|
-
self.fmtid.__init__(guid)
|
223
|
-
self.pid = pid
|
224
|
-
|
225
|
-
|
226
|
-
HDEVINFO = ctypes.c_void_p
|
227
|
-
PCTSTR = ctypes.c_wchar_p
|
228
|
-
|
229
|
-
PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA)
|
230
|
-
|
231
|
-
PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p
|
232
|
-
|
233
|
-
SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList
|
234
|
-
SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO]
|
235
|
-
SetupDiDestroyDeviceInfoList.restype = BOOL
|
236
|
-
|
237
|
-
SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo
|
238
|
-
SetupDiEnumDeviceInfo.argtypes = [HDEVINFO, DWORD, PSP_DEVINFO_DATA]
|
239
|
-
SetupDiEnumDeviceInfo.restype = BOOL
|
240
|
-
|
241
|
-
SetupDiGetClassDevs = setupapi.SetupDiGetClassDevsW
|
242
|
-
SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD]
|
243
|
-
SetupDiGetClassDevs.restype = HDEVINFO
|
244
|
-
|
245
|
-
|
246
|
-
def valid_hdevinfo(value, func, arguments):
|
247
|
-
if value in (-1, 0):
|
248
|
-
err_no = GetLastError()
|
249
|
-
raise OSError(err_no, ctypes.FormatError(err_no))
|
250
|
-
return value
|
251
|
-
|
252
|
-
|
253
|
-
SetupDiGetClassDevs.errcheck = valid_hdevinfo
|
254
|
-
|
255
|
-
SetupDiGetDeviceProperty = setupapi.SetupDiGetDevicePropertyW
|
256
|
-
SetupDiGetDeviceProperty.argtypes = [
|
257
|
-
ctypes.c_void_p,
|
258
|
-
ctypes.c_void_p,
|
259
|
-
ctypes.c_void_p,
|
260
|
-
ctypes.c_void_p,
|
261
|
-
ctypes.c_void_p,
|
262
|
-
ctypes.c_uint,
|
263
|
-
ctypes.c_void_p,
|
264
|
-
ctypes.c_uint,
|
265
|
-
]
|
266
|
-
|
267
|
-
|
268
|
-
def valid_property(value, func, arguments):
|
269
|
-
err_no = GetLastError()
|
270
|
-
if err_no in (ERROR_SUCCESS, ERROR_INSUFFICIENT_BUFFER):
|
271
|
-
return value
|
272
|
-
raise OSError(err_no, ctypes.FormatError(err_no))
|
273
|
-
|
274
|
-
|
275
|
-
SetupDiGetDeviceProperty.errcheck = valid_property
|
276
|
-
|
277
|
-
DIGCF_PRESENT = 2
|
278
|
-
|
279
|
-
################
|
280
|
-
# CH341 Section
|
281
|
-
################
|
282
|
-
|
283
|
-
USB_LOCK_VENDOR = 0x1A86 # Dev : (1a86) QinHeng Electronics
|
284
|
-
USB_LOCK_PRODUCT = 0x5512 # (5512) CH341A
|
285
|
-
BULK_WRITE_ENDPOINT = 0x02 # usb.util.ENDPOINT_OUT|usb.util.ENDPOINT_TYPE_BULK
|
286
|
-
BULK_READ_ENDPOINT = 0x82 # usb.util.ENDPOINT_IN|usb.util.ENDPOINT_TYPE_BULK
|
287
|
-
CH341_PARA_MODE_EPP19 = 0x01
|
288
|
-
|
289
|
-
mCH341_PARA_CMD_R0 = 0xAC # 10101100
|
290
|
-
mCH341_PARA_CMD_R1 = 0xAD # 10101101
|
291
|
-
mCH341_PARA_CMD_W0 = 0xA6 # 10100110
|
292
|
-
mCH341_PARA_CMD_W1 = 0xA7 # 10100111
|
293
|
-
mCH341_PARA_CMD_STS = 0xA0 # 10100000
|
294
|
-
|
295
|
-
mCH341_PACKET_LENGTH = 32
|
296
|
-
mCH341_PKT_LEN_SHORT = 8
|
297
|
-
mCH341_SET_PARA_MODE = 0x9A
|
298
|
-
mCH341_PARA_INIT = 0xB1
|
299
|
-
mCH341_VENDOR_READ = 0xC0
|
300
|
-
mCH341_VENDOR_WRITE = 0x40
|
301
|
-
mCH341A_BUF_CLEAR = 0xB2
|
302
|
-
mCH341A_DELAY_MS = 0x5E
|
303
|
-
mCH341A_GET_VER = 0x5F
|
304
|
-
mCH341A_STATUS = 0x52
|
305
|
-
|
306
|
-
CH341_DEVICE_IO = 0x223CD0
|
307
|
-
|
308
|
-
|
309
|
-
class CONTROL_TRANSFER(ctypes.Structure):
|
310
|
-
"""
|
311
|
-
Control Transfer governs the control transfer routines for sending single control transfer commands to the CH341
|
312
|
-
Kernel-side driver.
|
313
|
-
"""
|
314
|
-
|
315
|
-
_fields_ = [
|
316
|
-
("command", ctypes.c_int),
|
317
|
-
("size", ctypes.c_int),
|
318
|
-
("bmRequestType", ctypes.c_byte),
|
319
|
-
("bRequest", ctypes.c_byte),
|
320
|
-
("wValue", ctypes.c_ushort),
|
321
|
-
("wIndex", ctypes.c_ushort),
|
322
|
-
("wLength", ctypes.c_byte),
|
323
|
-
]
|
324
|
-
|
325
|
-
def __init__(self, bmRequestType, bRequest, wValue, wIndex, wLength):
|
326
|
-
super().__init__()
|
327
|
-
self.command = 0x4
|
328
|
-
self.size = 0
|
329
|
-
self.bmRequestType = bmRequestType
|
330
|
-
self.bRequest = bRequest
|
331
|
-
self.wValue = wValue
|
332
|
-
self.wIndex = wIndex
|
333
|
-
self.wLength = wLength
|
334
|
-
|
335
|
-
|
336
|
-
class BULK_OUT(ctypes.Structure):
|
337
|
-
"""
|
338
|
-
Governs the USB Bulk-Out usb device commands with optional command override. The command tends to govern the type
|
339
|
-
of output write. WriteData(), WriteEppData(), WriteEppAddr() are all bulk out commands with different commands.
|
340
|
-
"""
|
341
|
-
|
342
|
-
_fields_ = [
|
343
|
-
("command", ctypes.c_int),
|
344
|
-
("size", ctypes.c_int),
|
345
|
-
("packet", ctypes.c_byte * 258),
|
346
|
-
]
|
347
|
-
|
348
|
-
def __init__(self, packet: bytes, cmd=0x07):
|
349
|
-
self.command = cmd
|
350
|
-
self.size = len(packet)
|
351
|
-
ctypes.memmove(ctypes.addressof(self.packet), packet, self.size)
|
352
|
-
|
353
|
-
|
354
|
-
class CH341_DEFAULT(ctypes.Structure):
|
355
|
-
"""
|
356
|
-
Default CH341 device command for other types of commands. We primarily support the EPP commands but other commands
|
357
|
-
can be sent using the defined types.
|
358
|
-
"""
|
359
|
-
|
360
|
-
_fields_ = [
|
361
|
-
("command", ctypes.c_int),
|
362
|
-
("data", ctypes.c_int),
|
363
|
-
]
|
364
|
-
|
365
|
-
def __init__(self, command, data):
|
366
|
-
self.command = command
|
367
|
-
self.data = data
|
368
|
-
|
369
|
-
|
370
|
-
def _get_required_size(handle, key, dev_info):
|
371
|
-
"""
|
372
|
-
Requests the property with a 0 size, this is expected to fail and return the required buffer size for the property.
|
373
|
-
|
374
|
-
@param handle:
|
375
|
-
@param key:
|
376
|
-
@param dev_info:
|
377
|
-
@return:
|
378
|
-
"""
|
379
|
-
prop_type = ctypes.c_ulong()
|
380
|
-
required_size = ctypes.c_ulong()
|
381
|
-
|
382
|
-
if SetupDiGetDeviceProperty(
|
383
|
-
handle,
|
384
|
-
ctypes.byref(dev_info),
|
385
|
-
ctypes.byref(key),
|
386
|
-
ctypes.byref(prop_type),
|
387
|
-
None,
|
388
|
-
0,
|
389
|
-
ctypes.byref(required_size),
|
390
|
-
0,
|
391
|
-
):
|
392
|
-
raise OSError()
|
393
|
-
return required_size
|
394
|
-
|
395
|
-
|
396
|
-
def _get_prop(handle, key, dev_info):
|
397
|
-
"""
|
398
|
-
Get property associated with the given key.
|
399
|
-
|
400
|
-
@param handle:
|
401
|
-
@param key:
|
402
|
-
@param dev_info:
|
403
|
-
@return:
|
404
|
-
"""
|
405
|
-
prop_type = ctypes.c_ulong()
|
406
|
-
required_size = _get_required_size(handle, key, dev_info)
|
407
|
-
value_buffer = ctypes.create_string_buffer(required_size.value)
|
408
|
-
if SetupDiGetDeviceProperty(
|
409
|
-
handle,
|
410
|
-
ctypes.byref(dev_info),
|
411
|
-
ctypes.byref(key),
|
412
|
-
ctypes.byref(prop_type),
|
413
|
-
ctypes.byref(value_buffer),
|
414
|
-
required_size.value,
|
415
|
-
ctypes.byref(required_size),
|
416
|
-
0,
|
417
|
-
):
|
418
|
-
return bytes(value_buffer).decode("utf-16").split("\0", 1)[0]
|
419
|
-
|
420
|
-
|
421
|
-
class CH341Device:
|
422
|
-
def __init__(self, pdo_name, desc):
|
423
|
-
self._handle = None
|
424
|
-
self._buffer = (ctypes.c_char * 0x28)()
|
425
|
-
self._pointer_buffer = ctypes.pointer(self._buffer)
|
426
|
-
self.path = r"\\?\GLOBALROOT" + pdo_name
|
427
|
-
self.name = desc
|
428
|
-
self.success = True
|
429
|
-
|
430
|
-
@property
|
431
|
-
def bytes_returned(self):
|
432
|
-
return dwBytesReturned.value
|
433
|
-
|
434
|
-
@property
|
435
|
-
def buffer(self):
|
436
|
-
return self._buffer[8 : self.bytes_returned]
|
437
|
-
|
438
|
-
@staticmethod
|
439
|
-
def enumerate_devices():
|
440
|
-
handle = SetupDiGetClassDevs(
|
441
|
-
GUID("{77989adf-06db-4025-92e8-40d902c03b0a}"), None, None, DIGCF_PRESENT
|
442
|
-
)
|
443
|
-
try:
|
444
|
-
devinfo = SP_DEVINFO_DATA()
|
445
|
-
devinfo.cbSize = ctypes.sizeof(devinfo)
|
446
|
-
index = 0
|
447
|
-
while SetupDiEnumDeviceInfo(handle, index, ctypes.byref(devinfo)):
|
448
|
-
index += 1
|
449
|
-
pdo_name = _get_prop(
|
450
|
-
handle,
|
451
|
-
DEVPROP_KEY("{a45c254e-df1c-4efd-8020-67d146a850e0}", 16),
|
452
|
-
devinfo,
|
453
|
-
)
|
454
|
-
desc = _get_prop(
|
455
|
-
handle,
|
456
|
-
DEVPROP_KEY("{a45c254e-df1c-4efd-8020-67d146a850e0}", 2),
|
457
|
-
devinfo,
|
458
|
-
)
|
459
|
-
yield CH341Device(pdo_name, desc)
|
460
|
-
|
461
|
-
err_no = GetLastError()
|
462
|
-
if err_no not in (ERROR_STILL_ACTIVE, ERROR_SUCCESS):
|
463
|
-
raise OSError(err_no, ctypes.FormatError(err_no))
|
464
|
-
|
465
|
-
finally:
|
466
|
-
SetupDiDestroyDeviceInfoList(handle)
|
467
|
-
|
468
|
-
def ioctl(
|
469
|
-
self, io_control_code, in_buffer, in_buffer_size, out_buffer, out_buffer_size
|
470
|
-
):
|
471
|
-
self.success = DeviceIoControl(
|
472
|
-
self._handle,
|
473
|
-
io_control_code,
|
474
|
-
in_buffer,
|
475
|
-
in_buffer_size,
|
476
|
-
out_buffer,
|
477
|
-
out_buffer_size,
|
478
|
-
lpBytesReturned,
|
479
|
-
None,
|
480
|
-
)
|
481
|
-
return self.success
|
482
|
-
|
483
|
-
def open(self):
|
484
|
-
self._handle = CreateFile(
|
485
|
-
self.path,
|
486
|
-
GENERIC_READ | GENERIC_WRITE,
|
487
|
-
3,
|
488
|
-
None,
|
489
|
-
OPEN_EXISTING,
|
490
|
-
FILE_ATTRIBUTE_NORMAL,
|
491
|
-
0,
|
492
|
-
)
|
493
|
-
|
494
|
-
def close(self):
|
495
|
-
if self._handle is not None:
|
496
|
-
CloseHandle(self._handle)
|
497
|
-
self._handle = None
|
498
|
-
|
499
|
-
def is_connected(self):
|
500
|
-
return self._handle is not None
|
501
|
-
|
502
|
-
def __enter__(self):
|
503
|
-
self.open()
|
504
|
-
return self
|
505
|
-
|
506
|
-
def __exit__(self, typ, val, tb):
|
507
|
-
self.close()
|
508
|
-
|
509
|
-
def CH341ReadData(self, length, cmd=0x06):
|
510
|
-
self.ioctl(
|
511
|
-
CH341_DEVICE_IO,
|
512
|
-
ctypes.pointer(CH341_DEFAULT(command=cmd, data=length)),
|
513
|
-
0x8,
|
514
|
-
self._pointer_buffer,
|
515
|
-
length,
|
516
|
-
)
|
517
|
-
return self._buffer[8 : self.bytes_returned]
|
518
|
-
|
519
|
-
def CH341ReadData0(self, length):
|
520
|
-
self.CH341ReadData(length, cmd=0x10)
|
521
|
-
|
522
|
-
def CH341ReadData1(self, length):
|
523
|
-
self.CH341ReadData(length, cmd=0x11)
|
524
|
-
|
525
|
-
def CH341WriteData(self, buffer, cmd=0x07):
|
526
|
-
if buffer is None:
|
527
|
-
return True
|
528
|
-
return self.ioctl(
|
529
|
-
CH341_DEVICE_IO,
|
530
|
-
ctypes.pointer(BULK_OUT(bytes(buffer), cmd=cmd)),
|
531
|
-
len(buffer) + 8,
|
532
|
-
self._pointer_buffer,
|
533
|
-
0x8,
|
534
|
-
)
|
535
|
-
|
536
|
-
def CH341EppWriteData(self, buffer):
|
537
|
-
return self.CH341WriteData(buffer, cmd=0x12)
|
538
|
-
|
539
|
-
def CH341EppWriteAddr(self, buffer):
|
540
|
-
return self.CH341WriteData(buffer, cmd=0x13)
|
541
|
-
|
542
|
-
def CH341InitParallel(self, mode=CH341_PARA_MODE_EPP19):
|
543
|
-
value = mode << 8
|
544
|
-
if mode < 256:
|
545
|
-
value |= 2
|
546
|
-
return self.ioctl(
|
547
|
-
CH341_DEVICE_IO,
|
548
|
-
ctypes.pointer(
|
549
|
-
CONTROL_TRANSFER(mCH341_VENDOR_WRITE, mCH341_PARA_INIT, value, 0, 0x0)
|
550
|
-
),
|
551
|
-
0x28,
|
552
|
-
self._pointer_buffer,
|
553
|
-
0x28,
|
554
|
-
)
|
555
|
-
|
556
|
-
def CH341ResetDevice(self):
|
557
|
-
return self.ioctl(
|
558
|
-
CH341_DEVICE_IO,
|
559
|
-
ctypes.pointer(CH341_DEFAULT(command=0xC, data=0)),
|
560
|
-
0x28,
|
561
|
-
self._pointer_buffer,
|
562
|
-
0x28,
|
563
|
-
)
|
564
|
-
|
565
|
-
def CH341SetDelayMS(self, delay):
|
566
|
-
if delay > 0x0F:
|
567
|
-
delay = 0x0F
|
568
|
-
return self.CH341WriteData(bytes([0xAA, 0x50 | delay, 0x00]))
|
569
|
-
|
570
|
-
def CH341GetStatus(self):
|
571
|
-
"""D7-0, 8: err, 9: pEmp, 10: Int, 11: SLCT, 12: SDA, 13: Busy, 14: data, 15: addrs"""
|
572
|
-
self.ioctl(
|
573
|
-
CH341_DEVICE_IO,
|
574
|
-
ctypes.pointer(
|
575
|
-
CONTROL_TRANSFER(mCH341_VENDOR_READ, mCH341A_STATUS, 0, 0, 0x8)
|
576
|
-
),
|
577
|
-
0x28,
|
578
|
-
self._pointer_buffer,
|
579
|
-
0x28,
|
580
|
-
)
|
581
|
-
return tuple(self.buffer)
|
582
|
-
|
583
|
-
def CH341GetVerIC(self):
|
584
|
-
self.ioctl(
|
585
|
-
CH341_DEVICE_IO,
|
586
|
-
ctypes.pointer(
|
587
|
-
CONTROL_TRANSFER(mCH341_VENDOR_READ, mCH341A_GET_VER, 0, 0, 0x2)
|
588
|
-
),
|
589
|
-
0x28,
|
590
|
-
self._pointer_buffer,
|
591
|
-
0x28,
|
592
|
-
)
|
593
|
-
return struct.unpack("<h", self.buffer)[0]
|
594
|
-
|
595
|
-
def CH341SetParaMode(self, index, mode=CH341_PARA_MODE_EPP19):
|
596
|
-
value = 0x2525
|
597
|
-
return self.ioctl(
|
598
|
-
CH341_DEVICE_IO,
|
599
|
-
ctypes.pointer(
|
600
|
-
CONTROL_TRANSFER(
|
601
|
-
mCH341_VENDOR_WRITE,
|
602
|
-
mCH341_SET_PARA_MODE,
|
603
|
-
value,
|
604
|
-
index,
|
605
|
-
mode << 8 | mode,
|
606
|
-
)
|
607
|
-
),
|
608
|
-
0x28,
|
609
|
-
self._pointer_buffer,
|
610
|
-
0x28,
|
611
|
-
)
|
612
|
-
|
613
|
-
|
614
|
-
if __name__ == "__main__":
|
615
|
-
for device in CH341Device.enumerate_devices():
|
616
|
-
print(device)
|
617
|
-
print(device.name)
|
618
|
-
# device.open()
|
619
|
-
with device:
|
620
|
-
device.CH341WriteData(b"\xA0")
|
621
|
-
data = device.CH341ReadData(8)
|
622
|
-
print(data)
|
623
|
-
device.CH341InitParallel()
|
624
|
-
status = device.CH341GetStatus()
|
625
|
-
print(status)
|
626
|
-
status = device.CH341GetVerIC()
|
627
|
-
print(status)
|
628
|
-
device.CH341EppWriteData(b"\x00IPPFFFFFFFFFFFFFFFFFFFFFFFFFFF\xe4")
|
1
|
+
"""
|
2
|
+
There are 2 primary parts of this code, the first is divided into three sections.
|
3
|
+
|
4
|
+
Kernel is needed to open files namely the usb device.
|
5
|
+
Ole32 is needed briefly to convert the GUID name.
|
6
|
+
Setupaapi is needed in places to query the devices.
|
7
|
+
|
8
|
+
Most of these are taken from pySerial, since that's a lot safer. It is very easy to mess this up and lose compatibility
|
9
|
+
with various versions of windows.
|
10
|
+
|
11
|
+
---
|
12
|
+
https://github.com/pyserial/pyserial
|
13
|
+
(C) 2001-2016 Chris Liechti <cliechti@gmx.net>
|
14
|
+
|
15
|
+
SPDX-License-Identifier: BSD-3-Clause
|
16
|
+
---
|
17
|
+
|
18
|
+
The gwangyi library was also highly useful, and largely gave rise to the Ole32 functions.
|
19
|
+
--
|
20
|
+
https://github.com/gwangyi/pysetupdi
|
21
|
+
MIT License
|
22
|
+
Copyright (c) 2016 gwangyi
|
23
|
+
---
|
24
|
+
|
25
|
+
The second part is largely just mimicking the functionality of the CH341DLL.dll driver. The inclusion of that file
|
26
|
+
was always a bit iffy and error prone.
|
27
|
+
"""
|
28
|
+
|
29
|
+
|
30
|
+
import ctypes
|
31
|
+
import struct
|
32
|
+
from ctypes import POINTER, Structure, WinDLL, c_int64, c_ulong, c_void_p, sizeof
|
33
|
+
from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, HWND, LPCWSTR, WORD
|
34
|
+
|
35
|
+
_stdcall_libraries = {}
|
36
|
+
_stdcall_libraries["kernel32"] = WinDLL("kernel32")
|
37
|
+
|
38
|
+
|
39
|
+
# some details of the Windows API differ between 32 and 64-bit systems...
|
40
|
+
def is_64bit():
|
41
|
+
"""Returns true when running on a 64-bit system"""
|
42
|
+
return sizeof(c_ulong) != sizeof(c_void_p)
|
43
|
+
|
44
|
+
|
45
|
+
# ULONG_PTR is an ordinary number, not a pointer and contrary to the name it
|
46
|
+
# is either 32 or 64 bits, depending on the type of windows...
|
47
|
+
# so test if this a 32-bit windows...
|
48
|
+
if is_64bit():
|
49
|
+
ULONG_PTR = c_int64
|
50
|
+
else:
|
51
|
+
ULONG_PTR = c_ulong
|
52
|
+
|
53
|
+
|
54
|
+
class _SECURITY_ATTRIBUTES(Structure):
|
55
|
+
pass
|
56
|
+
|
57
|
+
|
58
|
+
LPSECURITY_ATTRIBUTES = POINTER(_SECURITY_ATTRIBUTES)
|
59
|
+
|
60
|
+
|
61
|
+
class _OVERLAPPED(Structure):
|
62
|
+
pass
|
63
|
+
|
64
|
+
|
65
|
+
OVERLAPPED = _OVERLAPPED
|
66
|
+
LPOVERLAPPED = POINTER(_OVERLAPPED)
|
67
|
+
LPDWORD = POINTER(DWORD)
|
68
|
+
LPVOID = c_void_p
|
69
|
+
|
70
|
+
|
71
|
+
try:
|
72
|
+
CreateEventW = _stdcall_libraries["kernel32"].CreateEventW
|
73
|
+
except AttributeError:
|
74
|
+
# Fallback to non-wide char version for old OS...
|
75
|
+
from ctypes.wintypes import LPCSTR
|
76
|
+
|
77
|
+
CreateEventA = _stdcall_libraries["kernel32"].CreateEventA
|
78
|
+
CreateEventA.restype = HANDLE
|
79
|
+
CreateEventA.argtypes = [LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCSTR]
|
80
|
+
CreateEvent = CreateEventA
|
81
|
+
|
82
|
+
CreateFileA = _stdcall_libraries["kernel32"].CreateFileA
|
83
|
+
CreateFileA.restype = HANDLE
|
84
|
+
CreateFileA.argtypes = [
|
85
|
+
LPCSTR,
|
86
|
+
DWORD,
|
87
|
+
DWORD,
|
88
|
+
LPSECURITY_ATTRIBUTES,
|
89
|
+
DWORD,
|
90
|
+
DWORD,
|
91
|
+
HANDLE,
|
92
|
+
]
|
93
|
+
CreateFile = CreateFileA
|
94
|
+
else:
|
95
|
+
CreateEventW.restype = HANDLE
|
96
|
+
CreateEventW.argtypes = [LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCWSTR]
|
97
|
+
CreateEvent = CreateEventW # alias
|
98
|
+
|
99
|
+
CreateFileW = _stdcall_libraries["kernel32"].CreateFileW
|
100
|
+
CreateFileW.restype = HANDLE
|
101
|
+
CreateFileW.argtypes = [
|
102
|
+
LPCWSTR,
|
103
|
+
DWORD,
|
104
|
+
DWORD,
|
105
|
+
LPSECURITY_ATTRIBUTES,
|
106
|
+
DWORD,
|
107
|
+
DWORD,
|
108
|
+
HANDLE,
|
109
|
+
]
|
110
|
+
CreateFile = CreateFileW # alias
|
111
|
+
|
112
|
+
|
113
|
+
def validate_handle(handle, function, args):
|
114
|
+
handle = HANDLE(handle)
|
115
|
+
if handle.value == HANDLE(-1).value:
|
116
|
+
raise ConnectionError(f"Error {GetLastError()}. Failed to open.")
|
117
|
+
return handle
|
118
|
+
|
119
|
+
|
120
|
+
CreateFile.errcheck = validate_handle
|
121
|
+
|
122
|
+
|
123
|
+
GetLastError = _stdcall_libraries["kernel32"].GetLastError
|
124
|
+
GetLastError.restype = DWORD
|
125
|
+
GetLastError.argtypes = []
|
126
|
+
|
127
|
+
CloseHandle = _stdcall_libraries["kernel32"].CloseHandle
|
128
|
+
CloseHandle.restype = BOOL
|
129
|
+
CloseHandle.argtypes = [HANDLE]
|
130
|
+
|
131
|
+
DeviceIoControl = _stdcall_libraries["kernel32"].DeviceIoControl
|
132
|
+
DeviceIoControl.argtypes = [
|
133
|
+
HANDLE,
|
134
|
+
DWORD,
|
135
|
+
LPVOID,
|
136
|
+
DWORD,
|
137
|
+
LPVOID,
|
138
|
+
DWORD,
|
139
|
+
LPDWORD,
|
140
|
+
LPOVERLAPPED,
|
141
|
+
]
|
142
|
+
DeviceIoControl.restype = BOOL
|
143
|
+
dwBytesReturned = DWORD(0)
|
144
|
+
lpBytesReturned = ctypes.byref(dwBytesReturned)
|
145
|
+
|
146
|
+
|
147
|
+
ERROR_SUCCESS = 0
|
148
|
+
ERROR_INSUFFICIENT_BUFFER = 122
|
149
|
+
ERROR_STILL_ACTIVE = 259
|
150
|
+
|
151
|
+
GENERIC_READ = 0x80000000
|
152
|
+
GENERIC_WRITE = 0x40000000
|
153
|
+
GENERIC_EXECUTE = 0x20000000
|
154
|
+
GENERIC_ALL = 0x10000000
|
155
|
+
|
156
|
+
FILE_ATTRIBUTE_NORMAL = 0x00000080
|
157
|
+
|
158
|
+
CREATE_NEW = 1
|
159
|
+
CREATE_ALWAYS = 2
|
160
|
+
OPEN_EXISTING = 3
|
161
|
+
OPEN_ALWAYS = 4
|
162
|
+
TRUNCATE_EXISTING = 5
|
163
|
+
|
164
|
+
|
165
|
+
################################
|
166
|
+
# ole32 Section.
|
167
|
+
# Allows creations of GUID structures from strings.
|
168
|
+
################################
|
169
|
+
|
170
|
+
ole32 = ctypes.windll.LoadLibrary("ole32")
|
171
|
+
CLSIDFromString = ole32.CLSIDFromString
|
172
|
+
|
173
|
+
################################
|
174
|
+
# SetupApi Section.
|
175
|
+
################################
|
176
|
+
|
177
|
+
setupapi = ctypes.windll.LoadLibrary("setupapi")
|
178
|
+
|
179
|
+
|
180
|
+
class GUID(ctypes.Structure):
|
181
|
+
_fields_ = [
|
182
|
+
("Data1", DWORD),
|
183
|
+
("Data2", WORD),
|
184
|
+
("Data3", WORD),
|
185
|
+
("Data4", BYTE * 8),
|
186
|
+
]
|
187
|
+
|
188
|
+
def __init__(self, guid):
|
189
|
+
super().__init__()
|
190
|
+
ret = CLSIDFromString(ctypes.create_unicode_buffer(guid), ctypes.byref(self))
|
191
|
+
if ret < 0:
|
192
|
+
err_no = GetLastError()
|
193
|
+
raise OSError(err_no, ctypes.FormatError(err_no), guid)
|
194
|
+
|
195
|
+
def __str__(self):
|
196
|
+
return "{{{:08x}-{:04x}-{:04x}-{}-{}}}".format(
|
197
|
+
self.Data1,
|
198
|
+
self.Data2,
|
199
|
+
self.Data3,
|
200
|
+
"".join(["{:02x}".format(d) for d in self.Data4[:2]]),
|
201
|
+
"".join(["{:02x}".format(d) for d in self.Data4[2:]]),
|
202
|
+
)
|
203
|
+
|
204
|
+
|
205
|
+
class SP_DEVINFO_DATA(ctypes.Structure):
|
206
|
+
_fields_ = [
|
207
|
+
("cbSize", DWORD),
|
208
|
+
("ClassGuid", GUID),
|
209
|
+
("DevInst", DWORD),
|
210
|
+
("Reserved", ULONG_PTR),
|
211
|
+
]
|
212
|
+
|
213
|
+
def __str__(self):
|
214
|
+
return "ClassGuid:{} DevInst:{}".format(self.ClassGuid, self.DevInst)
|
215
|
+
|
216
|
+
|
217
|
+
class DEVPROP_KEY(ctypes.Structure):
|
218
|
+
_fields_ = [("fmtid", GUID), ("pid", ctypes.c_ulong)]
|
219
|
+
|
220
|
+
def __init__(self, guid, pid):
|
221
|
+
super().__init__()
|
222
|
+
self.fmtid.__init__(guid)
|
223
|
+
self.pid = pid
|
224
|
+
|
225
|
+
|
226
|
+
HDEVINFO = ctypes.c_void_p
|
227
|
+
PCTSTR = ctypes.c_wchar_p
|
228
|
+
|
229
|
+
PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA)
|
230
|
+
|
231
|
+
PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p
|
232
|
+
|
233
|
+
SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList
|
234
|
+
SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO]
|
235
|
+
SetupDiDestroyDeviceInfoList.restype = BOOL
|
236
|
+
|
237
|
+
SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo
|
238
|
+
SetupDiEnumDeviceInfo.argtypes = [HDEVINFO, DWORD, PSP_DEVINFO_DATA]
|
239
|
+
SetupDiEnumDeviceInfo.restype = BOOL
|
240
|
+
|
241
|
+
SetupDiGetClassDevs = setupapi.SetupDiGetClassDevsW
|
242
|
+
SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD]
|
243
|
+
SetupDiGetClassDevs.restype = HDEVINFO
|
244
|
+
|
245
|
+
|
246
|
+
def valid_hdevinfo(value, func, arguments):
|
247
|
+
if value in (-1, 0):
|
248
|
+
err_no = GetLastError()
|
249
|
+
raise OSError(err_no, ctypes.FormatError(err_no))
|
250
|
+
return value
|
251
|
+
|
252
|
+
|
253
|
+
SetupDiGetClassDevs.errcheck = valid_hdevinfo
|
254
|
+
|
255
|
+
SetupDiGetDeviceProperty = setupapi.SetupDiGetDevicePropertyW
|
256
|
+
SetupDiGetDeviceProperty.argtypes = [
|
257
|
+
ctypes.c_void_p,
|
258
|
+
ctypes.c_void_p,
|
259
|
+
ctypes.c_void_p,
|
260
|
+
ctypes.c_void_p,
|
261
|
+
ctypes.c_void_p,
|
262
|
+
ctypes.c_uint,
|
263
|
+
ctypes.c_void_p,
|
264
|
+
ctypes.c_uint,
|
265
|
+
]
|
266
|
+
|
267
|
+
|
268
|
+
def valid_property(value, func, arguments):
|
269
|
+
err_no = GetLastError()
|
270
|
+
if err_no in (ERROR_SUCCESS, ERROR_INSUFFICIENT_BUFFER):
|
271
|
+
return value
|
272
|
+
raise OSError(err_no, ctypes.FormatError(err_no))
|
273
|
+
|
274
|
+
|
275
|
+
SetupDiGetDeviceProperty.errcheck = valid_property
|
276
|
+
|
277
|
+
DIGCF_PRESENT = 2
|
278
|
+
|
279
|
+
################
|
280
|
+
# CH341 Section
|
281
|
+
################
|
282
|
+
|
283
|
+
USB_LOCK_VENDOR = 0x1A86 # Dev : (1a86) QinHeng Electronics
|
284
|
+
USB_LOCK_PRODUCT = 0x5512 # (5512) CH341A
|
285
|
+
BULK_WRITE_ENDPOINT = 0x02 # usb.util.ENDPOINT_OUT|usb.util.ENDPOINT_TYPE_BULK
|
286
|
+
BULK_READ_ENDPOINT = 0x82 # usb.util.ENDPOINT_IN|usb.util.ENDPOINT_TYPE_BULK
|
287
|
+
CH341_PARA_MODE_EPP19 = 0x01
|
288
|
+
|
289
|
+
mCH341_PARA_CMD_R0 = 0xAC # 10101100
|
290
|
+
mCH341_PARA_CMD_R1 = 0xAD # 10101101
|
291
|
+
mCH341_PARA_CMD_W0 = 0xA6 # 10100110
|
292
|
+
mCH341_PARA_CMD_W1 = 0xA7 # 10100111
|
293
|
+
mCH341_PARA_CMD_STS = 0xA0 # 10100000
|
294
|
+
|
295
|
+
mCH341_PACKET_LENGTH = 32
|
296
|
+
mCH341_PKT_LEN_SHORT = 8
|
297
|
+
mCH341_SET_PARA_MODE = 0x9A
|
298
|
+
mCH341_PARA_INIT = 0xB1
|
299
|
+
mCH341_VENDOR_READ = 0xC0
|
300
|
+
mCH341_VENDOR_WRITE = 0x40
|
301
|
+
mCH341A_BUF_CLEAR = 0xB2
|
302
|
+
mCH341A_DELAY_MS = 0x5E
|
303
|
+
mCH341A_GET_VER = 0x5F
|
304
|
+
mCH341A_STATUS = 0x52
|
305
|
+
|
306
|
+
CH341_DEVICE_IO = 0x223CD0
|
307
|
+
|
308
|
+
|
309
|
+
class CONTROL_TRANSFER(ctypes.Structure):
|
310
|
+
"""
|
311
|
+
Control Transfer governs the control transfer routines for sending single control transfer commands to the CH341
|
312
|
+
Kernel-side driver.
|
313
|
+
"""
|
314
|
+
|
315
|
+
_fields_ = [
|
316
|
+
("command", ctypes.c_int),
|
317
|
+
("size", ctypes.c_int),
|
318
|
+
("bmRequestType", ctypes.c_byte),
|
319
|
+
("bRequest", ctypes.c_byte),
|
320
|
+
("wValue", ctypes.c_ushort),
|
321
|
+
("wIndex", ctypes.c_ushort),
|
322
|
+
("wLength", ctypes.c_byte),
|
323
|
+
]
|
324
|
+
|
325
|
+
def __init__(self, bmRequestType, bRequest, wValue, wIndex, wLength):
|
326
|
+
super().__init__()
|
327
|
+
self.command = 0x4
|
328
|
+
self.size = 0
|
329
|
+
self.bmRequestType = bmRequestType
|
330
|
+
self.bRequest = bRequest
|
331
|
+
self.wValue = wValue
|
332
|
+
self.wIndex = wIndex
|
333
|
+
self.wLength = wLength
|
334
|
+
|
335
|
+
|
336
|
+
class BULK_OUT(ctypes.Structure):
|
337
|
+
"""
|
338
|
+
Governs the USB Bulk-Out usb device commands with optional command override. The command tends to govern the type
|
339
|
+
of output write. WriteData(), WriteEppData(), WriteEppAddr() are all bulk out commands with different commands.
|
340
|
+
"""
|
341
|
+
|
342
|
+
_fields_ = [
|
343
|
+
("command", ctypes.c_int),
|
344
|
+
("size", ctypes.c_int),
|
345
|
+
("packet", ctypes.c_byte * 258),
|
346
|
+
]
|
347
|
+
|
348
|
+
def __init__(self, packet: bytes, cmd=0x07):
|
349
|
+
self.command = cmd
|
350
|
+
self.size = len(packet)
|
351
|
+
ctypes.memmove(ctypes.addressof(self.packet), packet, self.size)
|
352
|
+
|
353
|
+
|
354
|
+
class CH341_DEFAULT(ctypes.Structure):
|
355
|
+
"""
|
356
|
+
Default CH341 device command for other types of commands. We primarily support the EPP commands but other commands
|
357
|
+
can be sent using the defined types.
|
358
|
+
"""
|
359
|
+
|
360
|
+
_fields_ = [
|
361
|
+
("command", ctypes.c_int),
|
362
|
+
("data", ctypes.c_int),
|
363
|
+
]
|
364
|
+
|
365
|
+
def __init__(self, command, data):
|
366
|
+
self.command = command
|
367
|
+
self.data = data
|
368
|
+
|
369
|
+
|
370
|
+
def _get_required_size(handle, key, dev_info):
|
371
|
+
"""
|
372
|
+
Requests the property with a 0 size, this is expected to fail and return the required buffer size for the property.
|
373
|
+
|
374
|
+
@param handle:
|
375
|
+
@param key:
|
376
|
+
@param dev_info:
|
377
|
+
@return:
|
378
|
+
"""
|
379
|
+
prop_type = ctypes.c_ulong()
|
380
|
+
required_size = ctypes.c_ulong()
|
381
|
+
|
382
|
+
if SetupDiGetDeviceProperty(
|
383
|
+
handle,
|
384
|
+
ctypes.byref(dev_info),
|
385
|
+
ctypes.byref(key),
|
386
|
+
ctypes.byref(prop_type),
|
387
|
+
None,
|
388
|
+
0,
|
389
|
+
ctypes.byref(required_size),
|
390
|
+
0,
|
391
|
+
):
|
392
|
+
raise OSError()
|
393
|
+
return required_size
|
394
|
+
|
395
|
+
|
396
|
+
def _get_prop(handle, key, dev_info):
|
397
|
+
"""
|
398
|
+
Get property associated with the given key.
|
399
|
+
|
400
|
+
@param handle:
|
401
|
+
@param key:
|
402
|
+
@param dev_info:
|
403
|
+
@return:
|
404
|
+
"""
|
405
|
+
prop_type = ctypes.c_ulong()
|
406
|
+
required_size = _get_required_size(handle, key, dev_info)
|
407
|
+
value_buffer = ctypes.create_string_buffer(required_size.value)
|
408
|
+
if SetupDiGetDeviceProperty(
|
409
|
+
handle,
|
410
|
+
ctypes.byref(dev_info),
|
411
|
+
ctypes.byref(key),
|
412
|
+
ctypes.byref(prop_type),
|
413
|
+
ctypes.byref(value_buffer),
|
414
|
+
required_size.value,
|
415
|
+
ctypes.byref(required_size),
|
416
|
+
0,
|
417
|
+
):
|
418
|
+
return bytes(value_buffer).decode("utf-16").split("\0", 1)[0]
|
419
|
+
|
420
|
+
|
421
|
+
class CH341Device:
|
422
|
+
def __init__(self, pdo_name, desc):
|
423
|
+
self._handle = None
|
424
|
+
self._buffer = (ctypes.c_char * 0x28)()
|
425
|
+
self._pointer_buffer = ctypes.pointer(self._buffer)
|
426
|
+
self.path = r"\\?\GLOBALROOT" + pdo_name
|
427
|
+
self.name = desc
|
428
|
+
self.success = True
|
429
|
+
|
430
|
+
@property
|
431
|
+
def bytes_returned(self):
|
432
|
+
return dwBytesReturned.value
|
433
|
+
|
434
|
+
@property
|
435
|
+
def buffer(self):
|
436
|
+
return self._buffer[8 : self.bytes_returned]
|
437
|
+
|
438
|
+
@staticmethod
|
439
|
+
def enumerate_devices():
|
440
|
+
handle = SetupDiGetClassDevs(
|
441
|
+
GUID("{77989adf-06db-4025-92e8-40d902c03b0a}"), None, None, DIGCF_PRESENT
|
442
|
+
)
|
443
|
+
try:
|
444
|
+
devinfo = SP_DEVINFO_DATA()
|
445
|
+
devinfo.cbSize = ctypes.sizeof(devinfo)
|
446
|
+
index = 0
|
447
|
+
while SetupDiEnumDeviceInfo(handle, index, ctypes.byref(devinfo)):
|
448
|
+
index += 1
|
449
|
+
pdo_name = _get_prop(
|
450
|
+
handle,
|
451
|
+
DEVPROP_KEY("{a45c254e-df1c-4efd-8020-67d146a850e0}", 16),
|
452
|
+
devinfo,
|
453
|
+
)
|
454
|
+
desc = _get_prop(
|
455
|
+
handle,
|
456
|
+
DEVPROP_KEY("{a45c254e-df1c-4efd-8020-67d146a850e0}", 2),
|
457
|
+
devinfo,
|
458
|
+
)
|
459
|
+
yield CH341Device(pdo_name, desc)
|
460
|
+
|
461
|
+
err_no = GetLastError()
|
462
|
+
if err_no not in (ERROR_STILL_ACTIVE, ERROR_SUCCESS):
|
463
|
+
raise OSError(err_no, ctypes.FormatError(err_no))
|
464
|
+
|
465
|
+
finally:
|
466
|
+
SetupDiDestroyDeviceInfoList(handle)
|
467
|
+
|
468
|
+
def ioctl(
|
469
|
+
self, io_control_code, in_buffer, in_buffer_size, out_buffer, out_buffer_size
|
470
|
+
):
|
471
|
+
self.success = DeviceIoControl(
|
472
|
+
self._handle,
|
473
|
+
io_control_code,
|
474
|
+
in_buffer,
|
475
|
+
in_buffer_size,
|
476
|
+
out_buffer,
|
477
|
+
out_buffer_size,
|
478
|
+
lpBytesReturned,
|
479
|
+
None,
|
480
|
+
)
|
481
|
+
return self.success
|
482
|
+
|
483
|
+
def open(self):
|
484
|
+
self._handle = CreateFile(
|
485
|
+
self.path,
|
486
|
+
GENERIC_READ | GENERIC_WRITE,
|
487
|
+
3,
|
488
|
+
None,
|
489
|
+
OPEN_EXISTING,
|
490
|
+
FILE_ATTRIBUTE_NORMAL,
|
491
|
+
0,
|
492
|
+
)
|
493
|
+
|
494
|
+
def close(self):
|
495
|
+
if self._handle is not None:
|
496
|
+
CloseHandle(self._handle)
|
497
|
+
self._handle = None
|
498
|
+
|
499
|
+
def is_connected(self):
|
500
|
+
return self._handle is not None
|
501
|
+
|
502
|
+
def __enter__(self):
|
503
|
+
self.open()
|
504
|
+
return self
|
505
|
+
|
506
|
+
def __exit__(self, typ, val, tb):
|
507
|
+
self.close()
|
508
|
+
|
509
|
+
def CH341ReadData(self, length, cmd=0x06):
|
510
|
+
self.ioctl(
|
511
|
+
CH341_DEVICE_IO,
|
512
|
+
ctypes.pointer(CH341_DEFAULT(command=cmd, data=length)),
|
513
|
+
0x8,
|
514
|
+
self._pointer_buffer,
|
515
|
+
length,
|
516
|
+
)
|
517
|
+
return self._buffer[8 : self.bytes_returned]
|
518
|
+
|
519
|
+
def CH341ReadData0(self, length):
|
520
|
+
self.CH341ReadData(length, cmd=0x10)
|
521
|
+
|
522
|
+
def CH341ReadData1(self, length):
|
523
|
+
self.CH341ReadData(length, cmd=0x11)
|
524
|
+
|
525
|
+
def CH341WriteData(self, buffer, cmd=0x07):
|
526
|
+
if buffer is None:
|
527
|
+
return True
|
528
|
+
return self.ioctl(
|
529
|
+
CH341_DEVICE_IO,
|
530
|
+
ctypes.pointer(BULK_OUT(bytes(buffer), cmd=cmd)),
|
531
|
+
len(buffer) + 8,
|
532
|
+
self._pointer_buffer,
|
533
|
+
0x8,
|
534
|
+
)
|
535
|
+
|
536
|
+
def CH341EppWriteData(self, buffer):
|
537
|
+
return self.CH341WriteData(buffer, cmd=0x12)
|
538
|
+
|
539
|
+
def CH341EppWriteAddr(self, buffer):
|
540
|
+
return self.CH341WriteData(buffer, cmd=0x13)
|
541
|
+
|
542
|
+
def CH341InitParallel(self, mode=CH341_PARA_MODE_EPP19):
|
543
|
+
value = mode << 8
|
544
|
+
if mode < 256:
|
545
|
+
value |= 2
|
546
|
+
return self.ioctl(
|
547
|
+
CH341_DEVICE_IO,
|
548
|
+
ctypes.pointer(
|
549
|
+
CONTROL_TRANSFER(mCH341_VENDOR_WRITE, mCH341_PARA_INIT, value, 0, 0x0)
|
550
|
+
),
|
551
|
+
0x28,
|
552
|
+
self._pointer_buffer,
|
553
|
+
0x28,
|
554
|
+
)
|
555
|
+
|
556
|
+
def CH341ResetDevice(self):
|
557
|
+
return self.ioctl(
|
558
|
+
CH341_DEVICE_IO,
|
559
|
+
ctypes.pointer(CH341_DEFAULT(command=0xC, data=0)),
|
560
|
+
0x28,
|
561
|
+
self._pointer_buffer,
|
562
|
+
0x28,
|
563
|
+
)
|
564
|
+
|
565
|
+
def CH341SetDelayMS(self, delay):
|
566
|
+
if delay > 0x0F:
|
567
|
+
delay = 0x0F
|
568
|
+
return self.CH341WriteData(bytes([0xAA, 0x50 | delay, 0x00]))
|
569
|
+
|
570
|
+
def CH341GetStatus(self):
|
571
|
+
"""D7-0, 8: err, 9: pEmp, 10: Int, 11: SLCT, 12: SDA, 13: Busy, 14: data, 15: addrs"""
|
572
|
+
self.ioctl(
|
573
|
+
CH341_DEVICE_IO,
|
574
|
+
ctypes.pointer(
|
575
|
+
CONTROL_TRANSFER(mCH341_VENDOR_READ, mCH341A_STATUS, 0, 0, 0x8)
|
576
|
+
),
|
577
|
+
0x28,
|
578
|
+
self._pointer_buffer,
|
579
|
+
0x28,
|
580
|
+
)
|
581
|
+
return tuple(self.buffer)
|
582
|
+
|
583
|
+
def CH341GetVerIC(self):
|
584
|
+
self.ioctl(
|
585
|
+
CH341_DEVICE_IO,
|
586
|
+
ctypes.pointer(
|
587
|
+
CONTROL_TRANSFER(mCH341_VENDOR_READ, mCH341A_GET_VER, 0, 0, 0x2)
|
588
|
+
),
|
589
|
+
0x28,
|
590
|
+
self._pointer_buffer,
|
591
|
+
0x28,
|
592
|
+
)
|
593
|
+
return struct.unpack("<h", self.buffer)[0]
|
594
|
+
|
595
|
+
def CH341SetParaMode(self, index, mode=CH341_PARA_MODE_EPP19):
|
596
|
+
value = 0x2525
|
597
|
+
return self.ioctl(
|
598
|
+
CH341_DEVICE_IO,
|
599
|
+
ctypes.pointer(
|
600
|
+
CONTROL_TRANSFER(
|
601
|
+
mCH341_VENDOR_WRITE,
|
602
|
+
mCH341_SET_PARA_MODE,
|
603
|
+
value,
|
604
|
+
index,
|
605
|
+
mode << 8 | mode,
|
606
|
+
)
|
607
|
+
),
|
608
|
+
0x28,
|
609
|
+
self._pointer_buffer,
|
610
|
+
0x28,
|
611
|
+
)
|
612
|
+
|
613
|
+
|
614
|
+
if __name__ == "__main__":
|
615
|
+
for device in CH341Device.enumerate_devices():
|
616
|
+
print(device)
|
617
|
+
print(device.name)
|
618
|
+
# device.open()
|
619
|
+
with device:
|
620
|
+
device.CH341WriteData(b"\xA0")
|
621
|
+
data = device.CH341ReadData(8)
|
622
|
+
print(data)
|
623
|
+
device.CH341InitParallel()
|
624
|
+
status = device.CH341GetStatus()
|
625
|
+
print(status)
|
626
|
+
status = device.CH341GetVerIC()
|
627
|
+
print(status)
|
628
|
+
device.CH341EppWriteData(b"\x00IPPFFFFFFFFFFFFFFFFFFFFFFFFFFF\xe4")
|