From 9c8418a03d9c30b9f4c8e12fc9f0f0743a748c89 Mon Sep 17 00:00:00 2001 From: d3m1g0d Date: Wed, 23 Jan 2019 20:59:51 +0100 Subject: [PATCH] sensors module now in early working stage --- backend/__pycache__/sensors.cpython-36.pyc | Bin 0 -> 3110 bytes backend/sensors.py | 76 +- .../bin/__pycache__/miniterm.cpython-36.pyc | Bin 0 -> 27896 bytes backend/venv/bin/miniterm.py | 976 ++++++++++++ backend/venv/bin/miniterm.pyc | Bin 0 -> 28907 bytes .../__pycache__/easy_install.cpython-36.pyc | Bin 277 -> 0 bytes .../__pycache__/six.cpython-36.pyc | Bin 26450 -> 26483 bytes .../pyserial-3.4.dist-info/DESCRIPTION.rst | 13 + .../INSTALLER | 0 .../pyserial-3.4.dist-info/METADATA | 44 + .../pyserial-3.4.dist-info/RECORD | 91 ++ .../WHEEL | 2 +- .../pyserial-3.4.dist-info/metadata.json | 1 + .../top_level.txt | 0 .../serial-0.0.91.dist-info/METADATA | 26 - .../serial-0.0.91.dist-info/RECORD | 33 - .../site-packages/serial/__init__.py | 95 +- .../site-packages/serial/__init__.pyc | Bin 0 -> 2322 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 770 -> 2073 bytes .../serial/__pycache__/aio.cpython-36.pyc | Bin 0 -> 3687 bytes .../serial/__pycache__/errors.cpython-36.pyc | Bin 4500 -> 0 bytes .../serial/__pycache__/hooks.cpython-36.pyc | Bin 4857 -> 0 bytes .../serial/__pycache__/marshal.cpython-36.pyc | Bin 12467 -> 0 bytes .../serial/__pycache__/meta.cpython-36.pyc | Bin 17338 -> 0 bytes .../serial/__pycache__/model.cpython-36.pyc | Bin 23620 -> 0 bytes .../__pycache__/properties.cpython-36.pyc | Bin 22460 -> 0 bytes .../serial/__pycache__/request.cpython-36.pyc | Bin 12480 -> 0 bytes .../serial/__pycache__/rfc2217.cpython-36.pyc | Bin 0 -> 32682 bytes .../serial/__pycache__/rs485.cpython-36.pyc | Bin 0 -> 2812 bytes .../__pycache__/serialcli.cpython-36.pyc | Bin 0 -> 6627 bytes .../__pycache__/serialjava.cpython-36.pyc | Bin 0 -> 7276 bytes .../__pycache__/serialposix.cpython-36.pyc | Bin 0 -> 20214 bytes .../__pycache__/serialutil.cpython-36.pyc | Bin 0 -> 18385 bytes .../__pycache__/serialwin32.cpython-36.pyc | Bin 0 -> 12952 bytes .../serial/__pycache__/test.cpython-36.pyc | Bin 5857 -> 0 bytes .../serial/__pycache__/win32.cpython-36.pyc | Bin 0 -> 6386 bytes .../site-packages/serial/abc/__init__.py | 9 - .../abc/__pycache__/__init__.cpython-36.pyc | Bin 454 -> 0 bytes .../abc/__pycache__/model.cpython-36.pyc | Bin 5863 -> 0 bytes .../abc/__pycache__/properties.cpython-36.pyc | Bin 2506 -> 0 bytes .../site-packages/serial/abc/model.py | 250 --- .../site-packages/serial/abc/properties.py | 108 -- .../lib/python3.6/site-packages/serial/aio.py | 115 ++ .../python3.6/site-packages/serial/aio.pyc | Bin 0 -> 4496 bytes .../python3.6/site-packages/serial/errors.py | 214 --- .../python3.6/site-packages/serial/hooks.py | 258 ---- .../python3.6/site-packages/serial/marshal.py | 704 --------- .../python3.6/site-packages/serial/meta.py | 812 ---------- .../python3.6/site-packages/serial/model.py | 1306 ---------------- .../site-packages/serial/properties.py | 941 ------------ .../python3.6/site-packages/serial/request.py | 451 ------ .../python3.6/site-packages/serial/rfc2217.py | 1346 +++++++++++++++++ .../site-packages/serial/rfc2217.pyc | Bin 0 -> 37632 bytes .../python3.6/site-packages/serial/rs485.py | 92 ++ .../python3.6/site-packages/serial/rs485.pyc | Bin 0 -> 3266 bytes .../site-packages/serial/serialcli.py | 251 +++ .../site-packages/serial/serialcli.pyc | Bin 0 -> 8308 bytes .../site-packages/serial/serialjava.py | 249 +++ .../site-packages/serial/serialjava.pyc | Bin 0 -> 9577 bytes .../site-packages/serial/serialposix.py | 811 ++++++++++ .../site-packages/serial/serialposix.pyc | Bin 0 -> 24286 bytes .../site-packages/serial/serialutil.py | 693 +++++++++ .../site-packages/serial/serialutil.pyc | Bin 0 -> 20027 bytes .../site-packages/serial/serialwin32.py | 475 ++++++ .../site-packages/serial/serialwin32.pyc | Bin 0 -> 14471 bytes .../python3.6/site-packages/serial/test.py | 304 ---- .../site-packages/serial/threaded/__init__.py | 295 ++++ .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 9490 bytes .../site-packages/serial/tools/__init__.py | 0 .../site-packages/serial/tools/__init__.pyc | Bin 0 -> 140 bytes .../tools/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 142 bytes .../__pycache__/hexlify_codec.cpython-36.pyc | Bin 0 -> 4839 bytes .../__pycache__/list_ports.cpython-36.pyc | Bin 0 -> 2448 bytes .../list_ports_common.cpython-36.pyc | Bin 0 -> 3111 bytes .../list_ports_linux.cpython-36.pyc | Bin 0 -> 2907 bytes .../__pycache__/list_ports_osx.cpython-36.pyc | Bin 0 -> 5633 bytes .../list_ports_posix.cpython-36.pyc | Bin 0 -> 4446 bytes .../list_ports_windows.cpython-36.pyc | Bin 0 -> 5764 bytes .../tools/__pycache__/miniterm.cpython-36.pyc | Bin 0 -> 27883 bytes .../serial/tools/hexlify_codec.py | 124 ++ .../serial/tools/hexlify_codec.pyc | Bin 0 -> 4872 bytes .../site-packages/serial/tools/list_ports.py | 108 ++ .../site-packages/serial/tools/list_ports.pyc | Bin 0 -> 2815 bytes .../serial/tools/list_ports_common.py | 108 ++ .../serial/tools/list_ports_common.pyc | Bin 0 -> 3315 bytes .../serial/tools/list_ports_linux.py | 107 ++ .../serial/tools/list_ports_linux.pyc | Bin 0 -> 3038 bytes .../serial/tools/list_ports_osx.py | 260 ++++ .../serial/tools/list_ports_osx.pyc | Bin 0 -> 7164 bytes .../serial/tools/list_ports_posix.py | 117 ++ .../serial/tools/list_ports_posix.pyc | Bin 0 -> 3966 bytes .../serial/tools/list_ports_windows.py | 305 ++++ .../serial/tools/list_ports_windows.pyc | Bin 0 -> 6912 bytes .../site-packages/serial/tools/miniterm.py | 976 ++++++++++++ .../site-packages/serial/tools/miniterm.pyc | Bin 0 -> 30469 bytes .../serial/urlhandler/__init__.py | 0 .../serial/urlhandler/__init__.pyc | Bin 0 -> 145 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 147 bytes .../__pycache__/protocol_alt.cpython-36.pyc | Bin 0 -> 1309 bytes .../protocol_hwgrep.cpython-36.pyc | Bin 0 -> 1781 bytes .../__pycache__/protocol_loop.cpython-36.pyc | Bin 0 -> 8061 bytes .../protocol_rfc2217.cpython-36.pyc | Bin 0 -> 200 bytes .../protocol_serve-rfc2217.cpython-36.pyc | Bin 0 -> 206 bytes .../protocol_socket.cpython-36.pyc | Bin 0 -> 9007 bytes .../__pycache__/protocol_spy.cpython-36.pyc | Bin 0 -> 9019 bytes .../serial/urlhandler/protocol_alt.py | 55 + .../serial/urlhandler/protocol_hwgrep.py | 89 ++ .../serial/urlhandler/protocol_hwgrep.pyc | Bin 0 -> 1627 bytes .../serial/urlhandler/protocol_loop.py | 295 ++++ .../serial/urlhandler/protocol_loop.pyc | Bin 0 -> 9394 bytes .../serial/urlhandler/protocol_rfc2217.py | 10 + .../serial/urlhandler/protocol_rfc2217.pyc | Bin 0 -> 214 bytes .../urlhandler/protocol_serve-rfc2217.py | 10 + .../serial/urlhandler/protocol_socket.py | 356 +++++ .../serial/urlhandler/protocol_socket.pyc | Bin 0 -> 9803 bytes .../serial/urlhandler/protocol_spy.py | 286 ++++ .../serial/urlhandler/protocol_spy.pyc | Bin 0 -> 10984 bytes .../serial/utilities/__init__.py | 494 ------ .../__pycache__/__init__.cpython-36.pyc | Bin 10246 -> 0 bytes .../__pycache__/compatibility.cpython-36.pyc | Bin 1013 -> 0 bytes .../serial/utilities/compatibility.py | 29 - .../python3.6/site-packages/serial/win32.py | 354 +++++ .../python3.6/site-packages/serial/win32.pyc | Bin 0 -> 7725 bytes 123 files changed, 9170 insertions(+), 5954 deletions(-) create mode 100644 backend/__pycache__/sensors.cpython-36.pyc create mode 100644 backend/venv/bin/__pycache__/miniterm.cpython-36.pyc create mode 100755 backend/venv/bin/miniterm.py create mode 100755 backend/venv/bin/miniterm.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/__pycache__/easy_install.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/DESCRIPTION.rst rename backend/venv/lib/python3.6/site-packages/{serial-0.0.91.dist-info => pyserial-3.4.dist-info}/INSTALLER (100%) create mode 100644 backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/METADATA create mode 100644 backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/RECORD rename backend/venv/lib/python3.6/site-packages/{serial-0.0.91.dist-info => pyserial-3.4.dist-info}/WHEEL (70%) create mode 100644 backend/venv/lib/python3.6/site-packages/pyserial-3.4.dist-info/metadata.json rename backend/venv/lib/python3.6/site-packages/{serial-0.0.91.dist-info => pyserial-3.4.dist-info}/top_level.txt (100%) delete mode 100644 backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/METADATA delete mode 100644 backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/RECORD create mode 100644 backend/venv/lib/python3.6/site-packages/serial/__init__.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/aio.cpython-36.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/errors.cpython-36.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/hooks.cpython-36.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/marshal.cpython-36.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/meta.cpython-36.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/model.cpython-36.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/properties.cpython-36.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/request.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/rfc2217.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/rs485.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialcli.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialjava.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialposix.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialutil.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialwin32.cpython-36.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/test.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/__pycache__/win32.cpython-36.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/abc/__init__.py delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/__init__.cpython-36.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/model.cpython-36.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/properties.cpython-36.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/abc/model.py delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/abc/properties.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/aio.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/aio.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/errors.py delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/hooks.py delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/marshal.py delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/meta.py delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/model.py delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/properties.py delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/request.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/rfc2217.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/rfc2217.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/rs485.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/rs485.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/serialcli.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/serialcli.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/serialjava.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/serialjava.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/serialposix.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/serialposix.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/serialutil.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/serialutil.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/serialwin32.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/serialwin32.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/test.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/threaded/__init__.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/threaded/__pycache__/__init__.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/__init__.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/__init__.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/__init__.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/hexlify_codec.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_common.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_linux.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_osx.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_posix.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_windows.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/miniterm.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/hexlify_codec.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/hexlify_codec.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/list_ports.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/list_ports.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_common.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_common.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_linux.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_linux.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_osx.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_osx.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_posix.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_posix.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_windows.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_windows.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/miniterm.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/tools/miniterm.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/__init__.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/__init__.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/__init__.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_alt.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_hwgrep.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_loop.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_rfc2217.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_serve-rfc2217.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_socket.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_spy.cpython-36.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_alt.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_hwgrep.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_hwgrep.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_loop.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_loop.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_rfc2217.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_rfc2217.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_serve-rfc2217.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_socket.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_socket.pyc create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_spy.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_spy.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/utilities/__init__.py delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/utilities/__pycache__/__init__.cpython-36.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/utilities/__pycache__/compatibility.cpython-36.pyc delete mode 100644 backend/venv/lib/python3.6/site-packages/serial/utilities/compatibility.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/win32.py create mode 100644 backend/venv/lib/python3.6/site-packages/serial/win32.pyc diff --git a/backend/__pycache__/sensors.cpython-36.pyc b/backend/__pycache__/sensors.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9cc6e42bb282ffc891f56effa79e350876cbd40a GIT binary patch literal 3110 zcma)8&u<&Y6`t8&TvDX0D2dxRaj|HCG;CcdXacloUBi^6Sc+{q5SiLtv?bP@k+{@y zmzf#W79#;g>E4<{|ApS#Kcko4dgyVlJ?URCP`KY4lCr26ZAr|`n;-k$%zNK_Z$54` zYFGch{j0M&WB+0oelFzOD0%~xV1j2X=C*ZW7p;?dLqGO8&AC}Hti+*}c~*_9cbV{n z|C|Yb;>0z<+pHO!K@Zz)^3w05L#ZHm@>yznG(7+L5N@OBZB)`4j2*#+^PIt`E4-BN zbK#5NIgdS25h2EXQ57|`fmjlCnDtV&?~8_5#+r(_0@EvHy*-jWql%$4QuR2L)6YSv zqTE5z8LHzA!B6=K8?XUvPB>>)b|!4#P8epm;D?U7VZ7gy>_Ywot%vqDjXh%ddz{hV z_&7SgH-QMK{Uytt$E2+?!K|oYSK;_UugLdPHH=JOMq1{&P*JKQCH1JtMVcQ(NiL$1 zDtc0i=xJ*Dk-l(_D9uxoCRsX8Oj_h|^(Yr|Kh34k6uefa7CR5`#s1db-ow4|&supT zpY`O3)<#9|NGVd5A(X89Lh8D*lsf{>sl&UeE4Jlcb5KBl^R)w zN+u$M-7*leR|r{Fx8{aQf27jfl#YB>HncRKCn~W)&`qcGw9NM7iqZya4*tT_6_|PP z;kqgcvyNjA&K)4z)_cG()7MZj$Kf8Y^JN~Q z4V@5efU?ZT*Dpe0?Xrf_&7@ES!yci7e$??$EK2?fK_K>rf!Z{T@bC_s@Kfi+5zZl3 zk#HdcJp)I01J`&bK+%K`{0UZBH1R=T5#U=Lf3!M$tHV8Up@Zo@+ThSr8FaZbU6lc5 zU#lmydj4T5*h^^jcqg9vE57w|PdKOEOhXkKyw_@|0gFrHjbt>+QmHK#N9R$Bvh+|! z_qR479Q2I^H{ot@oIXXnAz&%ZjYl^%V2M(7;Z zTAjPCon0DMtFr;v`IrU>YA#@-0#Z~#RR)C6#FW7lEM*{bf|?3x(tA=AS<_d`=)|>G zyH{`0_is@}CM>?D)e3bRsIH^v4^S~T1Yig**LV$psq+=cS7)-v1BZI!YYXsMK=e7U z5Mq{4EMoo);rKg<0&xnkaG6ssa0cRKIl~u-wdIV*kLN@hY+9Uw^N3bM+jC7YsDS-2 zg6K1Gy?fK}BK52$J!aIyR~Fx9PLKT9ZevLhIA3x)sPs?HmtOlqOh5?E z+D9c%FNNtv?Piv0(<_FfTN|W?E@r^SpDacKmdz&Y-@)cUP9V9pn_1CIGJOleTtU3L z*Y14ri${02cRss!zqN5^bL-RdxRK)jd$a0YtXA()MYMGRRs<;<`sqgF)v1}g+j~3h z&&c?_`wJ$CT&#b-4Z-?n<&+aKOc2y}+0;)>et=oVe%HE9DRe(864P}4`zAKAe&#Lp zHslxl27Q|%O*|V{jvlS|hQIj{lth>-E4?B+8s_@!-!EUjY&Kq9J$uCzkSR76Ul9EfbQ=-cv5{mnT46dTf4Q8Dh|+OAHS-dP6Qf56}3TB~_H%$BFuvPNRr-=%u3x z0*tcL+lzahIdEKG7xK63#vFgq-uNhGf8(r2)JQ*S&&g&V9oli1ozRX!ICkGi0$um` zs__O$JD5SDu{02Xe7-w-uvCOQKr*3pg=b53N>zBatlDQwjZRIZaejJl*WPphszL`Z zj?DPS-{g$>6QdZ(oG4@Z(xEp*G%6JGiHW{CZPrU~l$d@QK1q%QK}CHZ2T|WcRk{Gd z6lzH}1*Jvdw`c)DQN2MGMW7j2Z>t~BzztNfXRs}+CJt;?(3c`mhTSeb0lM7>SU&?% z007b70pAA*$}+OSrNGR;qC^E1IY8g-mbGqoSPkCJRUGusovP{6Qw)B#u!QT5 hwIV{Zj)FHRH 3: + gain = 1 + self.serialObject.write('ATGAIN={}\n'.format(gain).encode()) + self.serialObject.readline() + + if 'led' in parameters: + led = bool(parameters['led']) + if led: + led=1 + else: + led=0 + self.serialObject.write('ATLED3={}\n'.format(led).encode()) + self.serialObject.readline() + # except: + # print('An exception occured during spectrometer initialization') + # ex(1) + + def startDataCollection(self): + try: + self.serialObject.write(b'ATCDATA\n') + rawresp = self.serialObject.readline().decode() + except: + print('An exception occurred when polling for spectrometer data') + ex(1) + else: + responseorder = [i for i in 'RSTUVWGHIJKLABCDEF'] + realorder = [i for i in 'ABCDEFGHRISJTUVWKL'] + response = pd.Series([float(i)/35.0 for i in rawresp[:-3].split(',')], index=responseorder) + self.data = pd.DataFrame(response, index=realorder, columns = ['uW/cm^2']) + self.schedule(self.rrate) + + def schedule(self, refresh): + self.timerObject = Timer(refresh, self.startDataCollection) + self.timerObject.start() + + + def __init__(self, path='/dev/ttyUSB0', baudrate=115200, tout=1, rrate=1, params={}): self.path=path self.baudrate=baudrate self.timeout=1 + self.rrate=rrate try: - self.serialObject = ser.Serial(path, baudrate, timeout) + self.serialObject = ser.Serial(path, baudrate, timeout=tout) except: print('An exception occured when opening the serial port at {}'.format(path)) ex(1) else: - initialiseSensor() - startDataCollection() + self.initializeSensor() + self.startDataCollection() diff --git a/backend/venv/bin/__pycache__/miniterm.cpython-36.pyc b/backend/venv/bin/__pycache__/miniterm.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..15c52d225723db7c338e5ee37e609b6b9e850cd0 GIT binary patch literal 27896 zcmb__32+=&dfs%;Js2DWK@vPfHctT*0ZQVoR!eKOq(D%jmjt;0R@Ba}JRVLrz!?l? zhOZkW&;qoID97=}vgCEVb~cK=K4Qm5e5@1OaU7Q`>)7ifPV90Lr{ma;U8ypw?8H}6 zE|znA-~YOM4ndT5ok8{Mcc1_J-+#TvnZd!r{N}Tt9QlSs;tvyDzhU^#;c@@eNF?lp zZ8Q=k{WeO5gw2vE?_?<{?^G#;x7kRqW=a`@d6SLoYOa(s5>BF?w^MfdwZvV+NNnVs zf|0nMXdXx~cK)@5op(m=CQCaU zqdsaER!sFJv|u!LmUhZ}tTcw419lNPi;{B}!h`k@!b8pul-%9CAZ3S9_OGSPp3)w9 zkC(>fy|=U%Wk!0+j5z!39rgW=SwC>sxNE#>Ajhb^6SeM?Rvtol%-)6YF6SY%auDI& z_8x@yNca%KbA%q`t4x{`Lgb&(>5I!X7M-jH{!w4Ui z@G*ps*hdjQD&gbKLFbTT*~i{WmQFaR!#Uy{b&fg5?c;A{N)Ov7?1wRn6DT!dPomVM z^m@{swjZ%iy=Ih7I*-^>E9RSq`eWzhU95s_EF>mR-y*+eCn;H}2#_J-@ITBYNeIy3#g zX}jW8PPZUkGyoYx^>?;H#eCKk}YKi8RS~6ctJ`zZ8d2I`RdJzs#GzI zQla8jYc-j3nH}xqP**UFTFYH+9YUnbl!Tcxa>gju0RAc?q8|r*4v+f+94EoOjD2ii z51V!pkdo5-w3H6_UqyJ%(x76%5R@ znr^G%JX3KU|H-QhR^^84aYWISRCT&FQERr>ywj~U4=+m~>y*`OSx)t4YcgIZ!dMc| z47+|34q?kquB6p&_{@58Gf_@8bM(^}zS9DEskwVB3M`WV7^>0Op9zxgh8q-bJIY13 z%C+WFE6BK>jm`v_8*58TjtT~DY&95mRoBs1IA(qapU=#lMYQ9YA?sg zt<(i9Y1i@A+Gh|c`@rAP-6KhR*X~&Jj_b7);d`mF*64H%#Xa=OeP9Q&)rM1PuC=H;4X3_BXboae0*H<`zq8@dJaMDM zK-}S1S*W{FK~d7^ek5VS2@650qL$qtvvP;;zIhISW}J1RnR!+wfciEBqchA+4l;MM zvg(w}L7`k;ZP{xL`itfAt+h%cOv#qZcB@)0D+0J;M`Q}rL-bj61OkrGca+WvIvoTf zJ{~-32b@VfLI~uHl$p!slK4yI()i2fQj#Mun_d;U;vWZb4v#wl=e-?ESgU-Hyt?N3 z$ChdhXR1+KajZM4(gwr;8B{!L&BZ2cxzif(WS&qv-+W|47vR}6aAasAh3gQQOLhq6 zY6{6+!;}$nieAJMO?(kucrO@T1DDjcJ!r`W``WIW^Q>7=M9*d>C`spelJOh_{F=Z= zCW&qh!O6gExq8hwglN|q{h%aSxDe4`q)C~txyh`07%7S?qPV1r=dOkjq5@t0b}+!x zuY^ZVK!E{3fMP70ihmpjfA0I>L~H)_i6u~8w)t8PBuUavfwTbQU}x}7+gUq@cjoI8 zsYEF&>z@sGUA)gCLV=aLpMm2gJWSACvo2`bOV(5MbUkwyo4uS3LpcfM!%)E%)EFInuwA$LZ|FgM5p2@5bNSv|Fr z{URj|dKFu)*^<*iNG9|;$jLc}(<_8;>?L5!YNO)1KrI~{!5y@W$0YzKjG_4NfO&wZ zbR_KE_PZ+HyT?$N;IGNQdWr{f7*1ohGYm%?BCF0Iw<4se_tSZj4k-~uLO^{0&i1=9 zAZ~OTFTlO7hmoGUH;DS~5oZZ8dv6Fna1fbQ_jXl*WaV;IZw8sQ zX03V?Y=k~b48I?R5v||Byrdo6c0lm{*$?WWF{xwId)49Io1#ihR_Q70}I}11d#Dz<`n$T z&AEgp#BRkR&bESV*&y9+xwZ8w65}875P$AdaK0QQXXot#kX=@gT@J`@K-!4cqX*I5 zK;_#ZIzen1FI&&)4FPnO_X_m^go?6Vl0a)o@VU=>L-k=nc_V;DAW87MiSiC`RPfUm zBCam(shg7;Rga?w9lRx&0@%#6YfZ(G%Sjq!$(GkzKrCJr;8pQF1@^ZA_80s!D4HM# zaX`&QE=_(=n4r$W5llo{8Yxdp7^tQ!)yKF-FHtX+c^!E}ARhvV^C=kP<^wQH&-T^R zst?#mD6p3}COMoq$9F1xr}-xEKLrUG-bu-oK`0f6vQM!D7sA9lj{`dyZj$tzOzS-^ z>Aegby;BP6GW}QR@DvX+muAk-U7ZV3vop_K4N{jDEhzXMOm zO&0}Gb#`|jlHv@;$agmam}w!82*&7WG~?#nUob1=H{ijlO|MZ(*IHGtp$R@tdyrkc zbaiH7@zVCEf;xgc--{SyD-k~q#R=yuk)R*{d3R&vO{I2(!!zO~Sjn>gs8>mvY7 zun~YY{DWj}A#15RHBeJpx}c?w7Db^lKM~9xMzPL>$-W9qwf+-`*2`h~#khylYW+Pn z4q49)$8s^36#INxjeF}qwSD8>yEi@|BkyYb24r$}&BY3CU?w2VtZE6%yUsz1^1FZ0 z)sdmjEHLSv*b9gA$>UWm(}_V*`vfJ}NceF)5qn|a08S~!ZVyo0b=w0J04N=#X67$n z^mok8EzZt!#%*2>V^UmdwL;Q*aacejbm|JLIqwW40f@g_P}wPlXHxy%-#HsqA5tv%=hn#2hFuKqgLKqTb#as)7KU!JG1R% zcOY-O8Ly+BUt(PuIGU$bjO&?mB9n7?T#DU$=bVrdm;kh%Ilg>xZZfrPZ?@}01GWAJ zt0%6^8H(Ya_KCyJ;c=gV)7`$4(&Bw5O)ldCnAf{TDdS}7S@PC)_AQ*;AVx&dR?0a! zi1!QV@jy_RYb}yyYgQWmqsm!r-F7T+q_t|LVF@vZ1mXlwwfKfELZeF&>c@v5`^&zu zv+fBvn~4_>l53z8)lRHr6^SO$3wshv=JQ|}Zh<@EjQOd-LM~rR97|9c6%q@9IsIs0 ztasrCqJ{hUr>KcpZ>y(&o6YDp{jtsfrsE8XM_BDJYONiIgDv|aBJTYII6W}WI%e=_ zfgN@|4s7%oJ;PxQB@GlBO&5oo6AaJKUb}pLX7&npQ2EOFYY)N#1hn5{t*ky__=8{`_e(r9rR>IxJDo7cv{<|qN&WyEyNh=32LMXkd5_jrU1CgASIV;TjD zWSG_iKxssrV`j3)Uk(7-xwpW?0)+CIoC3iHG$et-^^ebMu;D@611YDhTC>`i4zVrw z4u5*xLRcY_23Q#teH@^r3qH6CriKg|H)gzv+^R*VvwLI?+KBrjJY7H$b*~>NjuI$3 zkdYKn5%5sAP{Z8Y8Of7u(4(_PhfAYw)9G}gvl`^O{~I2GsLpDbhGI(3T5uxHF)PWt z?zMgvo?1hl|-n-qdz_SbkKFgp~l3XZtxry4C_P50>z#QCY0&e~f+Vlo2; z7-DP4H7=sKv=U@MgED3iF+r-{sx@&^P&c4Rat4OlmR%w)_)9#YXe|YH*AkX#+&l>8 z&M$U%>47KVGMm~Cgx8VqXKW%q35$wx+XL?eQnmx$x2u`4pYuU(rqyVvn(z3h&bQl* z4G^Ey!jcZIG6lyA)afy4PT(aq880-k2se&^=?FXkObYiYsKrTxHj;CGdjBWRd|*{x zkF5fa56Ec+4LceL3an52apK5?cVMcoV~0#L z5gEE7TA#a!d$-eKfUQZ5h7ZMh*j67i@xWGIdzV(Y+CyEfKm-uB@*rZJGx09R&#TCi zWW}61p_|e;co@4qyjVziFuY*mvO)HOb7O7!(p6vYG3yTXJvCQzOd6$W?kz@OoE{=E zfeavdVZ`f@$JUb`gfv7xA`!z0Ur&oRP)?XRKW}|v{)MwIZM|rLaNLQ4>O*uyA;!(A zC`9d6j>-n@+ByO>+?cyK58O2gV>dRYUkC@$PXK?V-CX>ecXJ7y+xJf0{7Px(uS2Dy zQKcFN6yrbd&4R5Gh7FdIw6gLVy@+Y7Rxg+VFbY=uNakZ(taTnSc58a#uvO+biY ziL*O^M^^dg;GwvMtIV~f%~mE5A`2=gcF8K?z$ zFv+2XO`GL0gX8JQ^~AdEwU$H*;mXNf zaaY9fff93l&f{!v)vC_4wb-&&D^Q8TPz5sz4SIn!ITg2ttl;Ef`rOZU+&>!rhLtKc7H^ei>$k{$J0;l;4vJWUG7hCO^S_@^ZlSG2rf8&a z^zO!?OZAG0L-+um48j@M1F6pHikNb;o`Z3^AVhOrX@aqP&q1rGq9dc`# z&@d|-8fN9}N5wSD01u~4vkIYU)`0z(z02N>I*Rt=_8xm2zCnAhy$@1=A$z}l0PkV@ zA^RZSBlaQN!h45(*gk^ysD0EvhWAeUxP1ceG5cYA0`Fb+qEOBCg3jcNpr1= zhP>y1>XGEOLGFUug}y4}Kf8>9M1V$`7?Q~~%kiqZ=tcFnG11jD9RBzwxCr9736BSp z5E2by9e@o9KdD?8mkBYE7B{m=!?WxrRV0+`=fxC=aRo^J6XgM$bri)N6n9A6VR1)< zAW6?%3^l>`D2%;FrN`tBtx(E_g9k*5)uRZ-f+XNL7}7*bq;d_0TcQPqaSGUQNI-+6 z(`p1+=xMRV++eq67R$o2Qd%l=?*>C+C=%O}1w)*rawnX|uDk6(Gp4mKWgQ$2DKJ&` zZZaV#sC8&bDFA@Mq8Ixi*AjDIA)Lr4MdP4x}rq$AE60|1tb^OAhW2aC!@=&?_+w#u7D$MQay&MwKuk*;UMDanP~Sp&kDOtW>TrC@>)j@MljkuJ#hA3!7O2jNhB^SuoG7@Z%Y z^TTjpz(I>Y6^zOTT&;NsgquhG2y^`?ozKysUS|7gkxXlx4KkvD1tTZWfEgKzXGHqh zGZisjBmbeNA{4J3f`-ppYE+g5Npj75#|QDGA;a0eKd^#DnNP7pYy*~*gI(QdwR?!~ z7#gW6#zcpUv1z!|G^xgJgb9;6Mjy0dSSDEvS+KCT$70~??Ik;p>0E`B5--9RLGPd8 zEg+`8KwmHHg&bFDFj|f>!Mh(1y@G;wIXX^U!YtrO7x3$hl6-m3TqC#MbL0v-qm1lO zOzB6Et~Oe(v;Cy1pF+vM8_#L?(n_hgIZk;zspk-&?RwqVn^+brjMo?feM{lS7F5!< zi5QXn0^tb_jC4@9qUq0s=}^mwAKGCqQC0*g1Rka=CA8Dn9&x64u{#ET`B%nnRG-aqM6e=WFk3xDf~s_r>=E@pqv^ z-^32-^J4@c4Ev=+?=n@Q%c^od_~SdqBINqD&7_>81R$_L1gg+#{UEVQM2GYWY)Iu% z37!@+c-%RJB92&_AK2#W9k~bjUNK6jr`0Jt)ilA;3PGgKc7?L)IUUlx z@y6wdw++kY)XlEkd3C!hR8X&Vg$C4T!kWNM%ayR*;EGfABKYDj#C$O>gM}?*!xjeB zuY~2g`<@F^htwZ!lbV;*;VvnUX%{387EiI{$Fv6|ZbyBT5`wPyA`Ym*It@C<7|#@Y z2*lE`f9$&0{8!hM$SDCLau-65)D3qUR!RU}|0BX9FNBHUh1=7l8n-O^ZdBH6-XYpL z#Q-l4Km;m*b_JDhh?i&Wjhg3fLDd`>ZT|%M;`zk|^3_kGo(qffk@uPT+4CRKfdXX9 zVE;kG=WsrX&qu*2Yo@E@LR@nGQdGyxg|GtuAhWxzB@YG_&?ZnK5Xxkb1FXEH6lkxzAFv>bWpAb|HaMHAtfDV0b~gGP_=N+RPGU*IUi?*3wdt zK{d#(v6t*}V(}ooMw@d@YkVFN+n?*fsJ*t@7BN9Nf|2j!W}ZM15*H*bXbU?AX@T|) z8RN!CGGiRX-x&VJ4S#=((cWHLyyuDkVky{nBe3DEHvzc`)rM<`t7%}fo@!f^j`5gN zU|F&ApMnd`FlC~UcQFo>FhfxIj3ZQl%xq{YxtW9=ArSwTdF$GB(i)k1F_e*|uJd~5 z1opC7AH-gMOYdc2nRU(~5_qpB!mFN1VLBdav_OE8xTsPfpaEv;>5v5$VG`)kVNG6> zvCuSK8NO1X)-01c1cFp=F-a(Ci$0zt;JGw&`tyu&=p3Z;>vWF533V%w_uzoi8kV8n zbF~jItAB!0Kh5Q%dM*Kt>X11G`Vayh+{&S;;TVqD44IYTc*!&u7%x)>1$W>P)O;Tv z8_TfqJL~@elbKMzvoVA>I8_+f!UVWEj_FKdGT#?X=9a0akGb8aui;Eq^YEwiBCxJo zIm#{SX&B|*u#<5(gYY`yh0(c|V6LsCENxDfHno|;f`c^NO5S=+mNqmQtq(GmV1d#@ zisjOM<%TxXD3`wVvr^6gh#(9w$_Yq}g?$&LOuYP>!TM1v{9Wz=gK+t$o(>VnTGOjF zDAZhz^X| zR|&3X;Ynmtp;VIirT{QC0lIwa2QvC3Ssem#MwlM$14Y#VX3fyXEFN(V9|7iHNNN(X zo>(#6F%pY)T82K2cgTCwRy+nJDu@A&IC%xQ-b^-Mr9B8r5TJ2A!$=^mi8$KFH@$pa zpWFjHqa|&gX12)xebPd)NB}jmKzGo z<-&49>Nk7Jnaw{yxx8GP-X|r8!;-^c$wF9i7#FLD`pSME$__}`Ybcvx$#NzvhpQxv zL#YdWrP?SpD5aK=V?^e}Y?cr^B(Y!ec6g)2#!!%lREj+e&JTFn+eMVHl18ncvqumE zy~bX)Iu^FNLs}iH?~Z65w6I57Fq(gb7DlB7&e3=n%kl4&*uO;V-Z1vp5IeS#dDB$T ze$@Acu|J8}T@q^{c7GWAdBpCP*e4KsAdLMqV)vk46Bg-a{UM~7<%4je7V1bJm-O36 zKlHZruSWV_sr9o+XM1M(a98=0NZ%*vKZ*1sZ%Z#AeZQpt7SfOQrZ@i>sd^-Tj#P>& zx@vF!I#LgyY!Wx$&HC}a)E`6YL+E)@LEKS4A*m0;m60_+gVckPdKsw`eW@N&4@qhr zsgr%F&m+~6c0Uc@;T58MvwSkH`w7Gzk@O#g?`W9*NSwYqgo0!BLGP5_mE0Znah%F0 z{3Dklt6r;uJGTsIN&0EvQ%&jKH+K1{+|B1ceBT)Lhe@4*Dg|X|!CO#Y0#Z=F4ClU) z(y1U+qtyF`>F;0(p;$YyX@56k1?h^0zmt@?Zxn?X(ApuP21D9V8oX~9_lXnk&gxTg}f$?}?oC(bJ7Xssj60l8R%=?Gg@LF@F z*}BuzBaNlux)V@7O?6@LUX-o=6NkG@hetv%u-0z0Dt4Lor8@L!FbJ-~o9HTv}WxYH3e*uJX03 zmtb6X@zV3N>fdtA|BeoAJR-vw(o;biS6m%e&rdHat!NkcXFLPBG6{1G1X~6~T6Fm- zhx6_TLUc)gAA?&EKPmGZat_HUIWCksE)D2XuftjBi}q;*Auly-D|l z$MX6UI7kXMsW}`Wt_}J!Q6GTb1Qejqe_)h}w8N63*uJ$$D$mY-G%=runf8!*U@1_s zPk@Rgd;hyeVhfkV{C#2hJX_vO-9`VH@8hV&&Tge%PQ4&sgXz>NYViWhgP{42wcgxa zO|sL%KG2BU0;4QkDx^xTze||!i^xT;TyM#<7Ol3y_1*Btx#bs73hV$Tdb6tnvZjKNCI>qkU$Y&Duwt4+`3QUz^HuCd zt?7Wl zKfE5co2zHNAyPZKJ}@`yU_{7nP(3@`1+e+Iw;qDlL5(nl4XB-lY9YE{qE)8;c%QWc zfC32Gg}no9;CrTt1hLp0lB9>=dpAu@woDrw8#)CoTxf47rbhl=!V=^`XP#)BZ6TlD5Xc zMX^x2B32j^?xghzSUGCP(@K{}uF1y-w$+;lQGT7AP9d zKhIdld zvk%DpfS~dm;5|b%TDL;AXSU+y7kZ5H_lKX;VtuJ2+*~08Br-u zS39Y}(bj^uogrWl7$Q>89~MY>CKOELu+#=ya?*u_f1Ei!>gFbg`d6-V)`=uG7d4>X zz=WuHuPYD(3H8m46FQirYq-zxDW*yl>#6<^Q~PMTzd+o}Tq~N*M28#gt@hy{8#VTI zQ&ZtOMLmeuiPRLkDO6Z=A%J>I$A*+z+L1CblVJ`A6xRmcC&&jc9YXF39Xd zf4!0fv&|z%o03wejYALygD^j&XjPjYh!7Q0v=DV9DRnF7Q7T!7m3$cw4?%&9Bvv?C zE`~5bq3Umk6qLoQ+w~F9ZaY9fX%vy9`XZ#GAV>-6s0l$MVVYPjCBw35l+}9o2*vDV zdC})({I7ZrUzM;bB&Wpoib8R1Hb{sOB8+xga=Wr0qs%QrSutR@%D9+WCRNi7M-j!u zBZufGh9my~51DI-3ahn2x5cTGWBQ=@EY?i@M>@iTeG7d*0jD$!?kdu>T;R2tA&uk2 zPH#;$N;_zOw$YtM2@59N*L~I_ELc9)0rW$)z$g{IVtua$VF^%sX!#7TizCS z{|wo`k+6FT9)7Y-%l*gj;kF#G3`NSCXCP0ap_%fz-DVeo7Q!@VFNSAQ9psAyp8pp- zgy#^BM4pTT3NTuS*$d2EN%?1^gJR2A|8|MVxxWzR0p!BS6=Hvwxu&$T5TOB*W+cCu z0s$Ho3xZv)C7K|>>@);*FWbOo*Ak%|TSWfX6St;;!-fIBKM(j#0*8%+cQxG!3m8)y2YPxG_wijWy8Y#W!M)%NK8&8?wMvJ@Yy+*Rey*zUGzn5*2{XZ z*KwnBny-=maQt2UnH-gkqW6t}@lI6PZ`e+7E@f-3>|5cqwai+MV9L6+^jh}I0MR!A zT5kw_RRw0HxJn^-K?Dx!|1!gNX#CHS@PqMI$mDRu;+~qY%p>ALB#mR3hcJ)f5o3a> zNq%=LrgFPQ8}VL1w-Q5mWG|H90nFph4?a-rJ{0eDNRFwD1FYFwXRwV))gzl zSXwe?P0Y2L&M9m5w$nUiUA65~)8BIv=whx6bGWSUqKN7OS@=9y?_{ ze#&}m`u%a<8&OB%rbta$v|WgLvu?c*Wx|&Ocqj3+)8uP=6N0FivN-ed>npacWkDgx zK0iCZ05dUvIATV_J*ECLwz>KpI{$^v@6!1LIsu*kO6T|J{63t?Vvvo>4FXvNDK1)v z8Ym?J3)<{DrN0lVAy&9i`3x3?v){>@GD5iqS)3IeYgeAb8f$(J^K5^kj1s(GCiWAC zWB(8^U9XKZ?Vr0$etU$+kPTc!y^rbY<+XRa8^rp{Z9m5TkVw)7r=os*g+Vz5zKgyu z(h-h{M7Sur71f6#KUIH19~o;!Hc9;j9l`rV;ff>aBGH9dr`?(QHaN6C5?UvhN|__0 zxg5SAQplwWIeZ^5TSynuxxKlaxf8j?+-|%_aznX;xkFO&-Ydr;Ss`7+Ul9*yN^tsH zh~>u^8jvSKZ%so-P$^ruiUWD3TyEdQEz#G(UPw&d&H!^$%`@TYZLb9a@yW$kKv2SyAZcq@-hdmP4LQuy?=bqSUdtR2s&}$Kzh$o6Sg_kW}`OtxVb{dBuX)E8@PK##N8UaFyfCa>9P!>$s9A zIp1$TVSm7Waye;#Q2Kw?ezL3oUxk{V>S^~wal22Wmmjvz+2`$N;x=X^?t(oV#<8at z;}Xx>&!N6cJw5$u#C)Wy1#m!H*-cV+**a9M;ER3+E0MD=3)hpa=b`L7U|&IMUKDR* z{6JN`XwNY{^k2nis;=1|eJ!ECzt<;D)cLQ5`FGg!QO#SqX2xDvHnwta8tbp2g;6Mw zb8njN)8b!5Z%p;+jiLu`XER$K1$T7{)alqt0r9s(flOEJ((jM4cJO{<;HFR`YWuXr zKhOA$FL=A^yBUvX51w&6d-3eEuiG!Yp4rNK`#1CU$M2$L_vh^S&3yR)QXfLUI1hgX z|3ReuX*li@RGdgf${*REfa3a-A%BNBlza$jyltI_Qt@#qnRHKvC0~@17E+G%m9$V2 zPyMjw5SuneysLf$V@|5igC@tf+5HP)EnibV><5F7_GW=_aH_clot|z<`n|YLb7|%p~60@GO%de-l2E3Dq z0iCruP<}-GpuFHeh1e;)@zkf`KP@XeP=7S`bG}mU#jw9$i%}2MciENDs8O%o4UFP3 z#6JRhjqg+4Z@Y>)74t)+JkB`*zcK)LyUw-%5j068M5Lnc)Q46#y5+wUwrqzz6CMcV z&#)#DygB~CnbztmnF`AIK~}XIU^MvAbCCbAIuSkQod3aykOdidKGL+}Z~=7)iZP`A zKuqxRL66XGS~%>^7srJSy>EQjpXie8i7eNeczEhz(siV#H19z><4Ih^OgH6=vndjw z>KlMuRTkLVKT3)qKH^VBAAUY-9iIqqXimCNzd>Y=larsSRD0o-3j9IRFtL)@&rMBf zvLrB|dz}r8tAVi)7*~2obrjYH*gP~ql7nppVQoa9ex4cKgqG^E}Vkw(;*Z0QcOE-bqx+iyg^pqqjVLo!*-1d!_ZcMK>?JK za$uId=J=B#i34i#Gz=?yj3ljSs?gxl@ViL!6EZLA4rdU+Zgm;6tIQ`TjucbcGh<)f^V zUC_`4>fuE_fCn#z;+pa*XuaDN)wPo5)Dl5mb0YfQX=?8Lm05pJ#fB7%W@Wt!C2XG~ z1hM12aE3Vrhauk>k`MjpFFbL7@W(fw1w3Nd|7Go!ua5W12vw!n2eQc2oC`=*?)Z}z zTKuG&$jL-55ly9gy0`moDSNZUFNM1P0J;o@a0)DpDkbMWcE!K`F?@*>wX%umk|t9D z|8j{+U)qNRk@7t)eBrWn-45M;9n`mD+?Y7on|%|9UXX;BUijsx1Mj^TkOj%tjAE{Q zg4MkPK&Zcuj^Ljk)GEtOd_1LAbpg`1{Or`!t+krt`TOxfU)U77u6%q2oC44{Zddb* zrml}ok1N+sqt*5g3P!%R;oYQC2J_=k=1mQal<}v#rq-%fny3zV8HWjv0?~WB5D~@W z7I^e`c5Y;AaQy$EqulxK+DR?tA$a!V={6?oCBhzf9cn1|^kM2qAF0iIcnaM&ru-u! zVXoZ3P6wRC!Z}!OauSwd+TQec2(sypS_F9fC2G)SB4RL-XP15)ar#O*j?yai_1YY% z%TTE|nfR1e?)$5)wT3NicBF|f@m4zw@{@fxTK#Jjw!|ko*70v(?;i^P)Gx8?Pq5-p zkVqlo6#%D*{5GxUaCVEgO9&JhA>21|{*dt6BD0~yV)A`K4u?KYMAr$1&Z}iypKp5C z7*x2{Zrf#~N}T9wg3<1%^0I0{)mD(w%;=W#1V6bt3_Dcd0r3WD0bsS0bH9`8@gX_~ z>AXTG)&?t(TTuSc`4%ShN%ujPYMP{fRlk7D__Azm)q!AEQGy?&W%Fr8lF0Q%MEUEi zm9jv7T|%~9ki>~43})BzL9dq3-{JXg7Wf`IVp9G^`i?SHm}BAWf0S`#ogqgkh7^&A zI&p(u|BsAvq;3M12xCA$bOJbY)g&qvb1Ke!T4q)qW6htZ^N;CJXj;leIH_-AL1=+P z_kU7?@HMC^k<6Kpmg1qb6rUf6ee@S3#7_})r*!hXn~&vn&7GXOrr!1_!RT#4xBO0h z>aQ~gB?yd@p%$S%Jry5g^3LH3bjln@-Mq+#2RW!LVXvaUoLNLJe$)COp74#Z*8QVb z_@^x6sGohITv@jvuGFS&WF%hHGL$&eN`N(~C%9VpuL_*=a+x2v-G!LBxye(ahE~&D zF9G!*qK~?%(jHB&Oq2L`r%7}Rl(_PVic+yVRsJ`Uy2hg41SiPP0l&}k>bW=lD9q&Rw5eDIGRf-)dIGCYzUDU75>Mn;MwqX_L3 ZpigN)6F($NBcDp}?_}XqiBA-a{|BYnIn)3E literal 0 HcmV?d00001 diff --git a/backend/venv/bin/miniterm.py b/backend/venv/bin/miniterm.py new file mode 100755 index 0000000..9731c3d --- /dev/null +++ b/backend/venv/bin/miniterm.py @@ -0,0 +1,976 @@ +#!/root/projekti/TeraHz/backend/venv/bin/python3 +# +# Very simple serial terminal +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C)2002-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +import codecs +import os +import sys +import threading + +import serial +from serial.tools.list_ports import comports +from serial.tools import hexlify_codec + +# pylint: disable=wrong-import-order,wrong-import-position + +codecs.register(lambda c: hexlify_codec.getregentry() if c == 'hexlify' else None) + +try: + raw_input +except NameError: + # pylint: disable=redefined-builtin,invalid-name + raw_input = input # in python3 it's "raw" + unichr = chr + + +def key_description(character): + """generate a readable description for a key""" + ascii_code = ord(character) + if ascii_code < 32: + return 'Ctrl+{:c}'.format(ord('@') + ascii_code) + else: + return repr(character) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +class ConsoleBase(object): + """OS abstraction for console (input/output codec, no echo)""" + + def __init__(self): + if sys.version_info >= (3, 0): + self.byte_output = sys.stdout.buffer + else: + self.byte_output = sys.stdout + self.output = sys.stdout + + def setup(self): + """Set console to read single characters, no echo""" + + def cleanup(self): + """Restore default console settings""" + + def getkey(self): + """Read a single key from the console""" + return None + + def write_bytes(self, byte_string): + """Write bytes (already encoded)""" + self.byte_output.write(byte_string) + self.byte_output.flush() + + def write(self, text): + """Write string""" + self.output.write(text) + self.output.flush() + + def cancel(self): + """Cancel getkey operation""" + + # - - - - - - - - - - - - - - - - - - - - - - - - + # context manager: + # switch terminal temporary to normal mode (e.g. to get user input) + + def __enter__(self): + self.cleanup() + return self + + def __exit__(self, *args, **kwargs): + self.setup() + + +if os.name == 'nt': # noqa + import msvcrt + import ctypes + + class Out(object): + """file-like wrapper that uses os.write""" + + def __init__(self, fd): + self.fd = fd + + def flush(self): + pass + + def write(self, s): + os.write(self.fd, s) + + class Console(ConsoleBase): + def __init__(self): + super(Console, self).__init__() + self._saved_ocp = ctypes.windll.kernel32.GetConsoleOutputCP() + self._saved_icp = ctypes.windll.kernel32.GetConsoleCP() + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + ctypes.windll.kernel32.SetConsoleCP(65001) + self.output = codecs.getwriter('UTF-8')(Out(sys.stdout.fileno()), 'replace') + # the change of the code page is not propagated to Python, manually fix it + sys.stderr = codecs.getwriter('UTF-8')(Out(sys.stderr.fileno()), 'replace') + sys.stdout = self.output + self.output.encoding = 'UTF-8' # needed for input + + def __del__(self): + ctypes.windll.kernel32.SetConsoleOutputCP(self._saved_ocp) + ctypes.windll.kernel32.SetConsoleCP(self._saved_icp) + + def getkey(self): + while True: + z = msvcrt.getwch() + if z == unichr(13): + return unichr(10) + elif z in (unichr(0), unichr(0x0e)): # functions keys, ignore + msvcrt.getwch() + else: + return z + + def cancel(self): + # CancelIo, CancelSynchronousIo do not seem to work when using + # getwch, so instead, send a key to the window with the console + hwnd = ctypes.windll.kernel32.GetConsoleWindow() + ctypes.windll.user32.PostMessageA(hwnd, 0x100, 0x0d, 0) + +elif os.name == 'posix': + import atexit + import termios + import fcntl + + class Console(ConsoleBase): + def __init__(self): + super(Console, self).__init__() + self.fd = sys.stdin.fileno() + self.old = termios.tcgetattr(self.fd) + atexit.register(self.cleanup) + if sys.version_info < (3, 0): + self.enc_stdin = codecs.getreader(sys.stdin.encoding)(sys.stdin) + else: + self.enc_stdin = sys.stdin + + def setup(self): + new = termios.tcgetattr(self.fd) + new[3] = new[3] & ~termios.ICANON & ~termios.ECHO & ~termios.ISIG + new[6][termios.VMIN] = 1 + new[6][termios.VTIME] = 0 + termios.tcsetattr(self.fd, termios.TCSANOW, new) + + def getkey(self): + c = self.enc_stdin.read(1) + if c == unichr(0x7f): + c = unichr(8) # map the BS key (which yields DEL) to backspace + return c + + def cancel(self): + fcntl.ioctl(self.fd, termios.TIOCSTI, b'\0') + + def cleanup(self): + termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old) + +else: + raise NotImplementedError( + 'Sorry no implementation for your platform ({}) available.'.format(sys.platform)) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +class Transform(object): + """do-nothing: forward all data unchanged""" + def rx(self, text): + """text received from serial port""" + return text + + def tx(self, text): + """text to be sent to serial port""" + return text + + def echo(self, text): + """text to be sent but displayed on console""" + return text + + +class CRLF(Transform): + """ENTER sends CR+LF""" + + def tx(self, text): + return text.replace('\n', '\r\n') + + +class CR(Transform): + """ENTER sends CR""" + + def rx(self, text): + return text.replace('\r', '\n') + + def tx(self, text): + return text.replace('\n', '\r') + + +class LF(Transform): + """ENTER sends LF""" + + +class NoTerminal(Transform): + """remove typical terminal control codes from input""" + + REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32) if unichr(x) not in '\r\n\b\t') + REPLACEMENT_MAP.update( + { + 0x7F: 0x2421, # DEL + 0x9B: 0x2425, # CSI + }) + + def rx(self, text): + return text.translate(self.REPLACEMENT_MAP) + + echo = rx + + +class NoControls(NoTerminal): + """Remove all control codes, incl. CR+LF""" + + REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32)) + REPLACEMENT_MAP.update( + { + 0x20: 0x2423, # visual space + 0x7F: 0x2421, # DEL + 0x9B: 0x2425, # CSI + }) + + +class Printable(Transform): + """Show decimal code for all non-ASCII characters and replace most control codes""" + + def rx(self, text): + r = [] + for c in text: + if ' ' <= c < '\x7f' or c in '\r\n\b\t': + r.append(c) + elif c < ' ': + r.append(unichr(0x2400 + ord(c))) + else: + r.extend(unichr(0x2080 + ord(d) - 48) for d in '{:d}'.format(ord(c))) + r.append(' ') + return ''.join(r) + + echo = rx + + +class Colorize(Transform): + """Apply different colors for received and echo""" + + def __init__(self): + # XXX make it configurable, use colorama? + self.input_color = '\x1b[37m' + self.echo_color = '\x1b[31m' + + def rx(self, text): + return self.input_color + text + + def echo(self, text): + return self.echo_color + text + + +class DebugIO(Transform): + """Print what is sent and received""" + + def rx(self, text): + sys.stderr.write(' [RX:{}] '.format(repr(text))) + sys.stderr.flush() + return text + + def tx(self, text): + sys.stderr.write(' [TX:{}] '.format(repr(text))) + sys.stderr.flush() + return text + + +# other ideas: +# - add date/time for each newline +# - insert newline after: a) timeout b) packet end character + +EOL_TRANSFORMATIONS = { + 'crlf': CRLF, + 'cr': CR, + 'lf': LF, +} + +TRANSFORMATIONS = { + 'direct': Transform, # no transformation + 'default': NoTerminal, + 'nocontrol': NoControls, + 'printable': Printable, + 'colorize': Colorize, + 'debug': DebugIO, +} + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def ask_for_port(): + """\ + Show a list of ports and ask the user for a choice. To make selection + easier on systems with long device names, also allow the input of an + index. + """ + sys.stderr.write('\n--- Available ports:\n') + ports = [] + for n, (port, desc, hwid) in enumerate(sorted(comports()), 1): + sys.stderr.write('--- {:2}: {:20} {!r}\n'.format(n, port, desc)) + ports.append(port) + while True: + port = raw_input('--- Enter port index or full name: ') + try: + index = int(port) - 1 + if not 0 <= index < len(ports): + sys.stderr.write('--- Invalid index!\n') + continue + except ValueError: + pass + else: + port = ports[index] + return port + + +class Miniterm(object): + """\ + Terminal application. Copy data from serial port to console and vice versa. + Handle special keys from the console to show menu etc. + """ + + def __init__(self, serial_instance, echo=False, eol='crlf', filters=()): + self.console = Console() + self.serial = serial_instance + self.echo = echo + self.raw = False + self.input_encoding = 'UTF-8' + self.output_encoding = 'UTF-8' + self.eol = eol + self.filters = filters + self.update_transformations() + self.exit_character = 0x1d # GS/CTRL+] + self.menu_character = 0x14 # Menu: CTRL+T + self.alive = None + self._reader_alive = None + self.receiver_thread = None + self.rx_decoder = None + self.tx_decoder = None + + def _start_reader(self): + """Start reader thread""" + self._reader_alive = True + # start serial->console thread + self.receiver_thread = threading.Thread(target=self.reader, name='rx') + self.receiver_thread.daemon = True + self.receiver_thread.start() + + def _stop_reader(self): + """Stop reader thread only, wait for clean exit of thread""" + self._reader_alive = False + if hasattr(self.serial, 'cancel_read'): + self.serial.cancel_read() + self.receiver_thread.join() + + def start(self): + """start worker threads""" + self.alive = True + self._start_reader() + # enter console->serial loop + self.transmitter_thread = threading.Thread(target=self.writer, name='tx') + self.transmitter_thread.daemon = True + self.transmitter_thread.start() + self.console.setup() + + def stop(self): + """set flag to stop worker threads""" + self.alive = False + + def join(self, transmit_only=False): + """wait for worker threads to terminate""" + self.transmitter_thread.join() + if not transmit_only: + if hasattr(self.serial, 'cancel_read'): + self.serial.cancel_read() + self.receiver_thread.join() + + def close(self): + self.serial.close() + + def update_transformations(self): + """take list of transformation classes and instantiate them for rx and tx""" + transformations = [EOL_TRANSFORMATIONS[self.eol]] + [TRANSFORMATIONS[f] + for f in self.filters] + self.tx_transformations = [t() for t in transformations] + self.rx_transformations = list(reversed(self.tx_transformations)) + + def set_rx_encoding(self, encoding, errors='replace'): + """set encoding for received data""" + self.input_encoding = encoding + self.rx_decoder = codecs.getincrementaldecoder(encoding)(errors) + + def set_tx_encoding(self, encoding, errors='replace'): + """set encoding for transmitted data""" + self.output_encoding = encoding + self.tx_encoder = codecs.getincrementalencoder(encoding)(errors) + + def dump_port_settings(self): + """Write current settings to sys.stderr""" + sys.stderr.write("\n--- Settings: {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits}\n".format( + p=self.serial)) + sys.stderr.write('--- RTS: {:8} DTR: {:8} BREAK: {:8}\n'.format( + ('active' if self.serial.rts else 'inactive'), + ('active' if self.serial.dtr else 'inactive'), + ('active' if self.serial.break_condition else 'inactive'))) + try: + sys.stderr.write('--- CTS: {:8} DSR: {:8} RI: {:8} CD: {:8}\n'.format( + ('active' if self.serial.cts else 'inactive'), + ('active' if self.serial.dsr else 'inactive'), + ('active' if self.serial.ri else 'inactive'), + ('active' if self.serial.cd else 'inactive'))) + except serial.SerialException: + # on RFC 2217 ports, it can happen if no modem state notification was + # yet received. ignore this error. + pass + sys.stderr.write('--- software flow control: {}\n'.format('active' if self.serial.xonxoff else 'inactive')) + sys.stderr.write('--- hardware flow control: {}\n'.format('active' if self.serial.rtscts else 'inactive')) + sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding)) + sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding)) + sys.stderr.write('--- EOL: {}\n'.format(self.eol.upper())) + sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters))) + + def reader(self): + """loop and copy serial->console""" + try: + while self.alive and self._reader_alive: + # read all that is there or wait for one byte + data = self.serial.read(self.serial.in_waiting or 1) + if data: + if self.raw: + self.console.write_bytes(data) + else: + text = self.rx_decoder.decode(data) + for transformation in self.rx_transformations: + text = transformation.rx(text) + self.console.write(text) + except serial.SerialException: + self.alive = False + self.console.cancel() + raise # XXX handle instead of re-raise? + + def writer(self): + """\ + Loop and copy console->serial until self.exit_character character is + found. When self.menu_character is found, interpret the next key + locally. + """ + menu_active = False + try: + while self.alive: + try: + c = self.console.getkey() + except KeyboardInterrupt: + c = '\x03' + if not self.alive: + break + if menu_active: + self.handle_menu_key(c) + menu_active = False + elif c == self.menu_character: + menu_active = True # next char will be for menu + elif c == self.exit_character: + self.stop() # exit app + break + else: + #~ if self.raw: + text = c + for transformation in self.tx_transformations: + text = transformation.tx(text) + self.serial.write(self.tx_encoder.encode(text)) + if self.echo: + echo_text = c + for transformation in self.tx_transformations: + echo_text = transformation.echo(echo_text) + self.console.write(echo_text) + except: + self.alive = False + raise + + def handle_menu_key(self, c): + """Implement a simple menu / settings""" + if c == self.menu_character or c == self.exit_character: + # Menu/exit character again -> send itself + self.serial.write(self.tx_encoder.encode(c)) + if self.echo: + self.console.write(c) + elif c == '\x15': # CTRL+U -> upload file + self.upload_file() + elif c in '\x08hH?': # CTRL+H, h, H, ? -> Show help + sys.stderr.write(self.get_help_text()) + elif c == '\x12': # CTRL+R -> Toggle RTS + self.serial.rts = not self.serial.rts + sys.stderr.write('--- RTS {} ---\n'.format('active' if self.serial.rts else 'inactive')) + elif c == '\x04': # CTRL+D -> Toggle DTR + self.serial.dtr = not self.serial.dtr + sys.stderr.write('--- DTR {} ---\n'.format('active' if self.serial.dtr else 'inactive')) + elif c == '\x02': # CTRL+B -> toggle BREAK condition + self.serial.break_condition = not self.serial.break_condition + sys.stderr.write('--- BREAK {} ---\n'.format('active' if self.serial.break_condition else 'inactive')) + elif c == '\x05': # CTRL+E -> toggle local echo + self.echo = not self.echo + sys.stderr.write('--- local echo {} ---\n'.format('active' if self.echo else 'inactive')) + elif c == '\x06': # CTRL+F -> edit filters + self.change_filter() + elif c == '\x0c': # CTRL+L -> EOL mode + modes = list(EOL_TRANSFORMATIONS) # keys + eol = modes.index(self.eol) + 1 + if eol >= len(modes): + eol = 0 + self.eol = modes[eol] + sys.stderr.write('--- EOL: {} ---\n'.format(self.eol.upper())) + self.update_transformations() + elif c == '\x01': # CTRL+A -> set encoding + self.change_encoding() + elif c == '\x09': # CTRL+I -> info + self.dump_port_settings() + #~ elif c == '\x01': # CTRL+A -> cycle escape mode + #~ elif c == '\x0c': # CTRL+L -> cycle linefeed mode + elif c in 'pP': # P -> change port + self.change_port() + elif c in 'sS': # S -> suspend / open port temporarily + self.suspend_port() + elif c in 'bB': # B -> change baudrate + self.change_baudrate() + elif c == '8': # 8 -> change to 8 bits + self.serial.bytesize = serial.EIGHTBITS + self.dump_port_settings() + elif c == '7': # 7 -> change to 8 bits + self.serial.bytesize = serial.SEVENBITS + self.dump_port_settings() + elif c in 'eE': # E -> change to even parity + self.serial.parity = serial.PARITY_EVEN + self.dump_port_settings() + elif c in 'oO': # O -> change to odd parity + self.serial.parity = serial.PARITY_ODD + self.dump_port_settings() + elif c in 'mM': # M -> change to mark parity + self.serial.parity = serial.PARITY_MARK + self.dump_port_settings() + elif c in 'sS': # S -> change to space parity + self.serial.parity = serial.PARITY_SPACE + self.dump_port_settings() + elif c in 'nN': # N -> change to no parity + self.serial.parity = serial.PARITY_NONE + self.dump_port_settings() + elif c == '1': # 1 -> change to 1 stop bits + self.serial.stopbits = serial.STOPBITS_ONE + self.dump_port_settings() + elif c == '2': # 2 -> change to 2 stop bits + self.serial.stopbits = serial.STOPBITS_TWO + self.dump_port_settings() + elif c == '3': # 3 -> change to 1.5 stop bits + self.serial.stopbits = serial.STOPBITS_ONE_POINT_FIVE + self.dump_port_settings() + elif c in 'xX': # X -> change software flow control + self.serial.xonxoff = (c == 'X') + self.dump_port_settings() + elif c in 'rR': # R -> change hardware flow control + self.serial.rtscts = (c == 'R') + self.dump_port_settings() + else: + sys.stderr.write('--- unknown menu character {} --\n'.format(key_description(c))) + + def upload_file(self): + """Ask user for filenname and send its contents""" + sys.stderr.write('\n--- File to upload: ') + sys.stderr.flush() + with self.console: + filename = sys.stdin.readline().rstrip('\r\n') + if filename: + try: + with open(filename, 'rb') as f: + sys.stderr.write('--- Sending file {} ---\n'.format(filename)) + while True: + block = f.read(1024) + if not block: + break + self.serial.write(block) + # Wait for output buffer to drain. + self.serial.flush() + sys.stderr.write('.') # Progress indicator. + sys.stderr.write('\n--- File {} sent ---\n'.format(filename)) + except IOError as e: + sys.stderr.write('--- ERROR opening file {}: {} ---\n'.format(filename, e)) + + def change_filter(self): + """change the i/o transformations""" + sys.stderr.write('\n--- Available Filters:\n') + sys.stderr.write('\n'.join( + '--- {:<10} = {.__doc__}'.format(k, v) + for k, v in sorted(TRANSFORMATIONS.items()))) + sys.stderr.write('\n--- Enter new filter name(s) [{}]: '.format(' '.join(self.filters))) + with self.console: + new_filters = sys.stdin.readline().lower().split() + if new_filters: + for f in new_filters: + if f not in TRANSFORMATIONS: + sys.stderr.write('--- unknown filter: {}\n'.format(repr(f))) + break + else: + self.filters = new_filters + self.update_transformations() + sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters))) + + def change_encoding(self): + """change encoding on the serial port""" + sys.stderr.write('\n--- Enter new encoding name [{}]: '.format(self.input_encoding)) + with self.console: + new_encoding = sys.stdin.readline().strip() + if new_encoding: + try: + codecs.lookup(new_encoding) + except LookupError: + sys.stderr.write('--- invalid encoding name: {}\n'.format(new_encoding)) + else: + self.set_rx_encoding(new_encoding) + self.set_tx_encoding(new_encoding) + sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding)) + sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding)) + + def change_baudrate(self): + """change the baudrate""" + sys.stderr.write('\n--- Baudrate: ') + sys.stderr.flush() + with self.console: + backup = self.serial.baudrate + try: + self.serial.baudrate = int(sys.stdin.readline().strip()) + except ValueError as e: + sys.stderr.write('--- ERROR setting baudrate: {} ---\n'.format(e)) + self.serial.baudrate = backup + else: + self.dump_port_settings() + + def change_port(self): + """Have a conversation with the user to change the serial port""" + with self.console: + try: + port = ask_for_port() + except KeyboardInterrupt: + port = None + if port and port != self.serial.port: + # reader thread needs to be shut down + self._stop_reader() + # save settings + settings = self.serial.getSettingsDict() + try: + new_serial = serial.serial_for_url(port, do_not_open=True) + # restore settings and open + new_serial.applySettingsDict(settings) + new_serial.rts = self.serial.rts + new_serial.dtr = self.serial.dtr + new_serial.open() + new_serial.break_condition = self.serial.break_condition + except Exception as e: + sys.stderr.write('--- ERROR opening new port: {} ---\n'.format(e)) + new_serial.close() + else: + self.serial.close() + self.serial = new_serial + sys.stderr.write('--- Port changed to: {} ---\n'.format(self.serial.port)) + # and restart the reader thread + self._start_reader() + + def suspend_port(self): + """\ + open port temporarily, allow reconnect, exit and port change to get + out of the loop + """ + # reader thread needs to be shut down + self._stop_reader() + self.serial.close() + sys.stderr.write('\n--- Port closed: {} ---\n'.format(self.serial.port)) + do_change_port = False + while not self.serial.is_open: + sys.stderr.write('--- Quit: {exit} | p: port change | any other key to reconnect ---\n'.format( + exit=key_description(self.exit_character))) + k = self.console.getkey() + if k == self.exit_character: + self.stop() # exit app + break + elif k in 'pP': + do_change_port = True + break + try: + self.serial.open() + except Exception as e: + sys.stderr.write('--- ERROR opening port: {} ---\n'.format(e)) + if do_change_port: + self.change_port() + else: + # and restart the reader thread + self._start_reader() + sys.stderr.write('--- Port opened: {} ---\n'.format(self.serial.port)) + + def get_help_text(self): + """return the help text""" + # help text, starts with blank line! + return """ +--- pySerial ({version}) - miniterm - help +--- +--- {exit:8} Exit program +--- {menu:8} Menu escape key, followed by: +--- Menu keys: +--- {menu:7} Send the menu character itself to remote +--- {exit:7} Send the exit character itself to remote +--- {info:7} Show info +--- {upload:7} Upload file (prompt will be shown) +--- {repr:7} encoding +--- {filter:7} edit filters +--- Toggles: +--- {rts:7} RTS {dtr:7} DTR {brk:7} BREAK +--- {echo:7} echo {eol:7} EOL +--- +--- Port settings ({menu} followed by the following): +--- p change port +--- 7 8 set data bits +--- N E O S M change parity (None, Even, Odd, Space, Mark) +--- 1 2 3 set stop bits (1, 2, 1.5) +--- b change baud rate +--- x X disable/enable software flow control +--- r R disable/enable hardware flow control +""".format(version=getattr(serial, 'VERSION', 'unknown version'), + exit=key_description(self.exit_character), + menu=key_description(self.menu_character), + rts=key_description('\x12'), + dtr=key_description('\x04'), + brk=key_description('\x02'), + echo=key_description('\x05'), + info=key_description('\x09'), + upload=key_description('\x15'), + repr=key_description('\x01'), + filter=key_description('\x06'), + eol=key_description('\x0c')) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# default args can be used to override when calling main() from an other script +# e.g to create a miniterm-my-device.py +def main(default_port=None, default_baudrate=9600, default_rts=None, default_dtr=None): + """Command line tool, entry point""" + + import argparse + + parser = argparse.ArgumentParser( + description="Miniterm - A simple terminal program for the serial port.") + + parser.add_argument( + "port", + nargs='?', + help="serial port name ('-' to show port list)", + default=default_port) + + parser.add_argument( + "baudrate", + nargs='?', + type=int, + help="set baud rate, default: %(default)s", + default=default_baudrate) + + group = parser.add_argument_group("port settings") + + group.add_argument( + "--parity", + choices=['N', 'E', 'O', 'S', 'M'], + type=lambda c: c.upper(), + help="set parity, one of {N E O S M}, default: N", + default='N') + + group.add_argument( + "--rtscts", + action="store_true", + help="enable RTS/CTS flow control (default off)", + default=False) + + group.add_argument( + "--xonxoff", + action="store_true", + help="enable software flow control (default off)", + default=False) + + group.add_argument( + "--rts", + type=int, + help="set initial RTS line state (possible values: 0, 1)", + default=default_rts) + + group.add_argument( + "--dtr", + type=int, + help="set initial DTR line state (possible values: 0, 1)", + default=default_dtr) + + group.add_argument( + "--ask", + action="store_true", + help="ask again for port when open fails", + default=False) + + group = parser.add_argument_group("data handling") + + group.add_argument( + "-e", "--echo", + action="store_true", + help="enable local echo (default off)", + default=False) + + group.add_argument( + "--encoding", + dest="serial_port_encoding", + metavar="CODEC", + help="set the encoding for the serial port (e.g. hexlify, Latin1, UTF-8), default: %(default)s", + default='UTF-8') + + group.add_argument( + "-f", "--filter", + action="append", + metavar="NAME", + help="add text transformation", + default=[]) + + group.add_argument( + "--eol", + choices=['CR', 'LF', 'CRLF'], + type=lambda c: c.upper(), + help="end of line mode", + default='CRLF') + + group.add_argument( + "--raw", + action="store_true", + help="Do no apply any encodings/transformations", + default=False) + + group = parser.add_argument_group("hotkeys") + + group.add_argument( + "--exit-char", + type=int, + metavar='NUM', + help="Unicode of special character that is used to exit the application, default: %(default)s", + default=0x1d) # GS/CTRL+] + + group.add_argument( + "--menu-char", + type=int, + metavar='NUM', + help="Unicode code of special character that is used to control miniterm (menu), default: %(default)s", + default=0x14) # Menu: CTRL+T + + group = parser.add_argument_group("diagnostics") + + group.add_argument( + "-q", "--quiet", + action="store_true", + help="suppress non-error messages", + default=False) + + group.add_argument( + "--develop", + action="store_true", + help="show Python traceback on error", + default=False) + + args = parser.parse_args() + + if args.menu_char == args.exit_char: + parser.error('--exit-char can not be the same as --menu-char') + + if args.filter: + if 'help' in args.filter: + sys.stderr.write('Available filters:\n') + sys.stderr.write('\n'.join( + '{:<10} = {.__doc__}'.format(k, v) + for k, v in sorted(TRANSFORMATIONS.items()))) + sys.stderr.write('\n') + sys.exit(1) + filters = args.filter + else: + filters = ['default'] + + while True: + # no port given on command line -> ask user now + if args.port is None or args.port == '-': + try: + args.port = ask_for_port() + except KeyboardInterrupt: + sys.stderr.write('\n') + parser.error('user aborted and port is not given') + else: + if not args.port: + parser.error('port is not given') + try: + serial_instance = serial.serial_for_url( + args.port, + args.baudrate, + parity=args.parity, + rtscts=args.rtscts, + xonxoff=args.xonxoff, + do_not_open=True) + + if not hasattr(serial_instance, 'cancel_read'): + # enable timeout for alive flag polling if cancel_read is not available + serial_instance.timeout = 1 + + if args.dtr is not None: + if not args.quiet: + sys.stderr.write('--- forcing DTR {}\n'.format('active' if args.dtr else 'inactive')) + serial_instance.dtr = args.dtr + if args.rts is not None: + if not args.quiet: + sys.stderr.write('--- forcing RTS {}\n'.format('active' if args.rts else 'inactive')) + serial_instance.rts = args.rts + + serial_instance.open() + except serial.SerialException as e: + sys.stderr.write('could not open port {}: {}\n'.format(repr(args.port), e)) + if args.develop: + raise + if not args.ask: + sys.exit(1) + else: + args.port = '-' + else: + break + + miniterm = Miniterm( + serial_instance, + echo=args.echo, + eol=args.eol.lower(), + filters=filters) + miniterm.exit_character = unichr(args.exit_char) + miniterm.menu_character = unichr(args.menu_char) + miniterm.raw = args.raw + miniterm.set_rx_encoding(args.serial_port_encoding) + miniterm.set_tx_encoding(args.serial_port_encoding) + + if not args.quiet: + sys.stderr.write('--- Miniterm on {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits} ---\n'.format( + p=miniterm.serial)) + sys.stderr.write('--- Quit: {} | Menu: {} | Help: {} followed by {} ---\n'.format( + key_description(miniterm.exit_character), + key_description(miniterm.menu_character), + key_description(miniterm.menu_character), + key_description('\x08'))) + + miniterm.start() + try: + miniterm.join(True) + except KeyboardInterrupt: + pass + if not args.quiet: + sys.stderr.write("\n--- exit ---\n") + miniterm.join() + miniterm.close() + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +if __name__ == '__main__': + main() diff --git a/backend/venv/bin/miniterm.pyc b/backend/venv/bin/miniterm.pyc new file mode 100755 index 0000000000000000000000000000000000000000..07aff4e78edaeabe52fc80414be46e7ef643856f GIT binary patch literal 28907 zcmchAeQ+GfbzjdeK9>Lq5+wL}5j#J9-_v@M21&BKVILRcZx2Hc|cfWr9`t^IShuJ^w%>C0BJ6X8e~u{|Gm*v}GvOrZ88eYd;#o71P2x>vqDk?LDL0$h7BkUe9Ky<)a;uqbGZSsb z35=O(H-3|O)0l^jaTeQ5kTcHB%44P}*ktbHOzqRgah#yTOyo?^X(l$Cieqm1&8#u8 z#rQ2I=rU92O{-uS^S+6#CUDHmHsiOMI~h}JSF0F7x0&cpdbiz7Y&U*;QaP7a-eLSr zCfF*F9cgNhsf;C1I|X$lfx6R7>`b8UG84NR>ThnW-)(~3#@}LsjG5VEdNA4#qt{(Y z54NUd_Zokj3HGW7-D&DR<8Mz=cciJ08NVk<-I=EDH~y|9b$6P2!1#Mia6oYPrm3#+ z_a$W?OH&UTe}9sCAWc1Fd^brwn5G^!rK}0izeDC+fDRqT4|;IKoSQgeN|+xE=qS?A z{^R&TgU9fL7JBgmkjL?J*!U+<{;2U!67Iy~#_vlo)1S6<%mls0A7IGHDf1?X?6?U| z82<@P$~Q8>ar4j^rvf^2%vkSf5OL{8_%qt;AR4tHC{}0Zs81smb|L**_H)Yo)m;>_0Q`)T!B0r4$9V*@3x5jo^H_ zFgxiNp4*1<3#`pPxnq{Y1AxXiJWH9kk|{JI2(2tPHcT4v~fQojqd_A}TrN zH(^9v{^4;1=ygylM1fmyYeB&;OqK)J4{X;;)rvb+ts(19uvl_gg*X_BYUPthi+~6g zGAiUm>{YeqYxK2Xu4Yr)ky^Z6s1=GBTa?3FAuN_knuT>DWeX~!7x{+1?L|`9hQMSx zoDOGOCgNNSRV!h&99%4fL6Ld0E@7qM1b%!?-4jW^cIE~F$yL=3U1XAxL30=zrTrgl)TU=3N$M83&XI^q=j%EB3C zp=DB?C^o_>|r46qwY=F7pVic|aZq)R4Ga&P>_thg?SYKBKW)Rit{o`yUkNUS62^Nb) zAz}#%HLp9_reYf)VRFfQ9=k_S%jYlRCGJMf5^@$P1bv-3%e&DqlWM#NMeLli8)C+V z+H@!maOXaw>qCsTB6Mwm3;QzL=X@Ebm#jR_tQ2O0d|u2wpP#Mz^JTnxJ5a!5cRZrT z+r?lX1I^fDjJXKv-BD~U+oQ+r{3xEV3qgz1-rAnk-=_Aq_NIs(tVH6A*XN@m-t>p) z5o!-3@?&=ZFm+=6Q7UEISW=k&{4y3s@M?O>Z=gi>vOdB!4UTxzL|a7vRC%Wsj)w*l zw#hd6aO^`&=iG5y(ulb^ftzF=VIUfKHMCQ!F%yUw$3_?9OKjbuK%$CwkPr_s5M^=6 ztGzmU*G@o3Pu zHZv{c$|R{)?&oo0Tawt`keH*iLVjK^w<%5PFf*MLVvrwgHqjO{(`9D15)4v&h=NwQ z*k&LstSs5RvLveKyT$NO9`P!}qs6(va;E#GieD~^o!kj(m7sj~OvF9ug&>Nj1S=WR%FqqV zWx~R}z|U8UbCP=MrIzQHkWVOMk3ylaR8K}V5`s-ar@Cbgsh-eQ}2TcfRrlJ8{G+no%aE7$c`JQ+u4D?-S~SVa{>}>$_=iDOd_011sVS$l69U>Zy#m z(_(6`)}c6eA#LNBh@YDpJV|x5L_S{PCMLjtu1rBHXmqeXTy8Nnw6hP62;p} zov)ONx9tI^E*$-Aoe`~x&vQd$+uG2L)6VmYlXLbvotae`9uIrkMeFUXo#Aoi-C|=g z!|T94Nj%I|!_q<#5&cQo9Z3OGw#Gve_DarVN}E}{Xj(sk$lLL~hnDHJ7?8?LTb)SB z3BmMoW@eKTJ1jkeGlIS0Cn$Lj&q75;PMffbjA`fGM>hup<6t#57=%j(48voKh=y z5vS`M4q4L2@WhArZ5Ys&`j*4Bqrtd{G*XHs4h|12IGIxRchoYn=MnX;GSKvq+&KZ4 zuf8;P#k+{N4HGz}(@)_^1VvKDN z7CwjO>;z`k+_R|aP6p^vC2|pc>z;WYfN!8z(Za8+y;p=Ud^PlHaz1kXQi%Cq1pc5! zEn21I3e0V!f1IqKc)R+gdJlV-6MiCjRf{->kD-`1&VWa%IxZAj%QSiKMZM4BiRm9J zpb6rG*nxNT@}tDdRkcQ};Z_90qvOLKXV4GbA@3yMFpEzXkEoIYH>eU!6MGD~ zWCj$<+|iJfn-kG=<}CN%a2mMZ`sZCZ`ZYjFmUDOu+t*)CxtT9<7d3VyzU)DdmyOFR~cX)!4&82QScE0E38> z3YQ?Z;NL{E38b_rNm_xdcMj$e7)H(^)3G+%iB=P|(oBMJwh=l{lC@yAdM|Ks?k^Rw zlSp_*SI8TqT9v=B?bywxRKan-NDL97>?1oI@^}hCM-_oG6!B#!9f179dUpP{yF^!#A#87#h9? z29UpY;l?}Td|U+I!jmi%j%Wqv6TA0I#Yl2Bx-uPj??*9v9-f1M5!jt(qnpeGfc-=E zfeVFeX&;Wntm`WO@KqNv1oJBa|q$PLve2II%G;A{OsmuOQ~V%2%Z^SU(MZ zhqVa4C8oi{q`XxKv6p9|6%*~{nf3t&oL{eipxzn9*3u(y67~Kip0EQ!eLhRI;dC;I!;36W%Ru8~VnM%&eGlFTa6HlO$DxJS5RBcf-iN8WSeg|R@dInC#$;8h zmHrE3Ln9+@YJ3jeLdAzjE6t=ki$nYJDb=cM0@OA`<7TJ~cx$0!ql}U(=HIcwROslCc4??uuGUIF zyUL2{Gk0rZChS9SVQ#Lxh(iqB26)gavH@(*a0f{bhFnrSi-?vI zdeY+Q0R-;F)=p*(589~{5uQ}H_m#6x&B}Iw*y&l(j64*y{liHFEsFcI4f|?0?(L|& zK8lhu?7yR$RYPam>*6UXOMTvi2s+GF3xZE}U=GWxAb97S?DqjB-eeyY0^X+e>N{?o z)b_{q)>j-DC1VOh?`#vFMGN0!D{TlQ5I4t`wM)U|{Pf6m60iOcsbcpoB5zNepvf!> ziEU`#aPQwPL~bdx=YPx9t)e1DDv4NAO4bBc0htOm1<)h3m|y~!Ua&Iz&xx~}%|o17 zkP|z5i3Pc@c((>0EWPGhgi$u}&N0}{pu|AJ60MQ%oIL&%P_(R#a{zqgr!boFKc~@* zV1_Yieq1Pt~i-5&>0Z|n8XNWXX7^upj)BuatD>P4_OQ;Uo zLI#gf8ORYA%&Qshv9D$+T|lN-LZOsOVYFx=iDDn8U|2^RW@W2s zf_;_lx!WWIZ- z>C*8e?1P3gtWwPhVUR6o?_lEv3kbI5U|~QoL+(ns{{DXVLZXVbT^!8G@DO$*pVM@+oaoA+ONL-lAS0D~g>uQaB@gCCduemD zw&awDi0FdK{H$CsMW-S5yuhbe5_1?(lkuo-R^eLVKK$(Hx|c-~#AyA(BCU<`%$U!u3W+4|H!jl7UYs%ytEwO8{+iQ^(_=l`pI|DVajXQ(YNBCR0ir&;& zz)!>$&_Csu)!Z~$3tr>UT40&9sTh1H5QjH765%|7_$I|Wj4x;K30N6BRdTcO<=#yn z^esw(7etpLTPZ7TLl;9DAV{!@X2mT)aDsw^l#CN%bq+!l_3tZ{=+s<;_0UySM-!1W zJva-NSg&AbLsmG40}jqNaQcgNBP8;qBDLQ<1;O?(?*>uPR`u?pPesl>AM(HW4yAqDEJFN)f!C2~-l|i~{QRHQ;?m#3C!iUY5BoE?;vz+y` zkT%3Ru-4PEC>B)90uFBttl2Q)(w~>2A|EA&7tK*9sPb@24~t47PV;G!-M`p-HEC@Q zSczd&s1yTr*!v`MJ<`MamXIW`K`tA1(qO5yFZ-LrTagkIAI_*|^AaMMO(Gt;FneIs zWZ|@k3Bgo6G#TbL$Hj0o%wuE0tU-I%Y9}uB1SjVkiwZSbeQ1XUD`iA;f@GMs#VN?= zBqNSWt!LR1`C8sq%Y)(xsxw2X0|(;;402 zTOU>esw*i{Tv}g7M%_&mTB|KF0v(jSZVX#eLx>P@Rj%c%5S;55*Yk<4cJ~ z2l9ky?D#l1EHF}yB_FK*M)HAshAKcPfqxq7Kp`RLG&vQwr%WJS^4?$Lus*|J96>Kn zHkMM@4^fEsd8YjW1DcjZBG%(dE?Zimv+$dML##!e_e(7M%M3osfGcIAlp!$VL==@% z^u5W693XO&*7&3ZK_W^CLR^j6v9?1K`8V*s(Vl2carRzgZ`i1*i~Vd!E1iszc*j4S zT2KVSAw&{8eQ^>bwuatiQN#Np0}_|WtbRPv!bmWjC)uuVVF~63*kKC72D^ESMI6a4 zr8LSyG2u97_q0P*n(gtW*Se%&lD>ZT`!nY5PO}ptB@0kG-a?6(lCLq$e3I30SppmZ zWI_;)f zVm*o%9vpxdbpqTAuf&v8f=q`vRL2)b=lvxHXAwj@5P;D`wGB%R1j<4g%2n88tPTbP zj$f^XiUYOYN+bh#KtemTzLCWnX_N=(WIqQvC!T`fUq6`+1=(V(6e>dL$86vYC2fe6(DwG&Bp|E*TDOg2~rDTQ6-_9z3^dUwb4GXT*Q< zNa7|_`-6JoL#mV<$#q0N&U;qtsqN#0g^K0nmid3g!=QUmXuX2yC;Z;#=#kd_){*3s}q zggSY`?HDU;I>hmEZh+#{lFQg+VczF0yrn+A@VX524tkj@;8M<_qTDGaOHsI#6Nj0T z(+|(9-uM`gt4{;grExEbUi5}9yl7u?+F@}EkJ4Ei`T`rUUc!F75X4Xeh_MueHB^53Gb8e=-X3@;ReIo06tW}#YHs7_fW6t1KKfaSSjOFS}2)}4n1qh_~j zogl~>HX`-%OzO|i&gm?k$Jt5LRs%T8o#E8i3AaA_mG8rm+{Sh|uG5uma!xuYoAx37 z&?+1Hw*h$ZD+ZoXig*~X296V~bR10hdvZP&z zcfX({EN1H!DoxgctHd1S0a#J7JRzY%ccpy{vclcS++}38u?RW5-8#L$&BQSV(jfmM#wbNL$PV0;sW6al zm=2=LWr+3dL*6d}+$T8uJldE{*4Y7B7d~80cV-)uG2PBygzecP>5Uyt1-SD_0awQS zE$(v`c1YOAM7qwRWpv5O2(xjsvQtc*_`X1VW9pGiOmptvuc=H7lib7r^seU3&LN#& z?@<@9EZyGh|FkN%OYXg4D}fxAlLs^s*ik?p5E{{P*4+KFB@f+mVH3`>O=;ZU%pa!Iv2PO$Or(w1{>y_Uj0=OsqI2ZHhIyY-bJ+kb-IV zERQs*<>7sc+3g5qAzZ%?s`mxd{UVpgIYdlL3s@pGmEB;HIV_V-ER-#Ja!ymWMGJPA6;y`|Wzm+H#jeKwH!4~Q zR&9c$WIPzF?Nl8uG*AF{_upd?K{5N@TlE}_sIkSHci(WDaONU+dEIfaAd_)o+>qT= zmdn!O0HtrC7Zzyc&svEud}Ra}{d&C~anl`(b57+CR0t9&kotec9MIqGrh}z2yzW-- z8{|B+r*xF4{VxX^V#?jASXueFfOQGj6)NC}j2$Vk9Sy)BQ>MUnnA$U$M}R#mU>G}k z_L&4&PYMj*sYodRH~3OuJ*M{2M}d96g}B%*V1FoJErJBCU>(ek1Pl=N?MGq!9l_`k zj2~v})plw&>+ieF48EA650gZ@OthQdL$T6x?7hOtuNU9LR{g!KM2zhvH2Y7}(P}>B zt{~%mBId^!E@9Uf3B8U!e>b}@W)Qjb+LN)|fX3+__@Rd0?qhQiR;4zKxS<<&Ka|D4 zo2ZOF^ZyuVXJpdp9d5u;-u9s_bm0jFnR25w)XY zEP3~2GYy9`CzGmt=}TUgq4ww_J*-20Jjv@%dS5xvL~I|^KtI?_umg6wT9nbNRsWq9 zW`KhF@adsW6P?oFa8%eP1y2TwG(H& zOAt4i#n+^Vhr$XbrsHIj55cY=WIzl#XI|eVyv234GZmaAe%+y0g^*Q9a&bnFGJ4S4 zb3pe~>g`8cRxrU6LXE@~kpkL#T0l@qs7LQ@0vZKin0)GZJ(=KDLqvUnz>wc95O=ZZ zd?Q0V_X(YEwAQO?CX$I?w&^@b2QX-sI?duy6Fs9g(``sJhNkQ|^Me@S(-`4-jj-Ih zVuS-LMhF)u`k3uEtH-BA8Goylr-5ga8qX;b6Uu*}E;atJ(05htJ*$@c zEtNYh4E#{#o=?hMXoU9(l>?=K6S-|{_u{JM0+luTqC zM^-I2uX68J|DKTuGsrseB>qfe(|AD_pHa~%6$MSCMPF{;>!PJ$wAF1B06uX`*D`Zbfa~ru%V?VDqk~ADsP~~dM|=aRJmvCsB9>cPK=3Coijp<_ zi?NmzEAen#cUW!23)oH6sDtj&PzV%3Jgx94feVK9Q@yUA*=za<$MjRN?$W1K{EWi0 z%2>Fims@(Nd3y1}gK-y<3Fi^D;jpfyo9n%XQuZLMGw||5ulLWH`w|14uQxNsqp1Bf z%X<*RcFU_X1;1RX*sl`6n}Oer zMy~e_2D}_(6^;)W`w9YSBJl~R*-&l_dWCWq_7NVrah(_*DiUVem-? ze}Td8GN4__R%36i{e=o9Xxr{UU%)dx6e5VKrydn0-fu9FUi9}E`#A>xi~+~$eVDZSETt`S&9mJ<18*KNOeJ7L(72`%hfQDNm|HGh{#()p7%in zQZWpVyl`dw;>h@zEfnkMl{Gf}^6;p#IIJ5Nypi$u<(Vd>hSlIpQy6DmzjR3mh+nT= z@LrT9KYky(fxA781#s7WSOvz$uis!hc_xrB5|hC4-0_>&qn*fYES|q{eFV3KE|0uC z?EPcpdB4MeI*VRW9W^b|tOzbRRUl`NUqiB{aS}U%<*$LdhUJ`(`I++Z<)&kQ^b&J~BSV*-$KQYpo(e_MiQ6oHrC!|UaIwkDK z&?AAlfzA2QX+V905N3C49g{W(`kwez#|`+_;T2*Qq%C;TF^+Qp;+quLmpfn^V>*sC zNY^Jj;6ukaPLxQ8RSofO#brG6p&~(gkJ5K4zKijPremy^g28C`UPu?(5azJ7buGR3 zLHzNeDn|w?YQF1;ORq+SZtkO(2OuwADT6MZsn6)`%-psisPr9&Jmwg9a?XxJ4M0Nr4j>(fCH`6K|8XJA9MGnF6QMR;}U}W#F>MH^@IMp;D3z z66xIS6{43Kr$pGcQv|vBcseC>oEQ7Y3KkMQl9r!xpH6ZC-1=wIu$ttLy2I{ucg(%! z#$t(UdAQrl54-lc!}o$ppL^Z+``ocPeCevsy;i8*Nzr-QJ>#CW4G=tev`g@A@994G zOrLvt;K{V?WYUqeDQ-V5ZNW)z7TjA&A$(JS*XB-vH}gBKtD0bwYBiUb*Pj*DRyE7! zCKsNvo1VNbM3VB-3t_c%f($7BbDKcl4H#prdHB>XB~s z+O7@2QmztqNX$^wQC_^$kZrYSlTluWH4^Y7pdLde;1_tTWr16pwL8!Zu~8Q-??0jP zZ3w#7fVQU{Jd|9U{J-(9-M46c^$s#P#NcHHA7{WLrFD9v0An55zQh=quJI&J)g_?c zW7;n=_yPm&aq32WZ&Yke8|2TU^1FaGoef%=S~A-@@tMTt_V)H{M{|2~M^i^r``-5L z?MK?bTzcauWFgKhQAeRbyThxH+ORwL5Z$0_wYE0AYL9?l3iR<%zC%HV{Q{eXy-AB5 z0owtN_Ybj!#o17b*=)Nvz;g?J!m-J>NreEQlbjw3T_|A*5+2yl3&ZOgmIi$68=v;x zZl(bRuEzR6HufKE6!hWdmI5fY8dT5tMmIjijqE+zn7CV^CpH@&t4_1=O>691%d+u( z>$Hpb$Thl1%R*Wk-?&b*U1d{?Y=?;vScPvofl^v&MgynZ`Z}R6S|l#1sPX zIRS>gvW}{!8anrM(z*I*pag83kHKW@=<72Ad0rr&Z3Ow;BZc0hLeJYmAQqtM!g9bD zjX%`TlS@fY>S!8n$iAFp#~p^8K<w3k4xuFP_$$J&S8u19xA2SgwE}7;f ztO-=X;s7FH9`Kv^dccG)HPw4RZf4xLSnW!a|B~!vFSA<>a{(JnyUFj54IVQG6NtZ^ zK>TV0#G47kgQoVmw}N<35Dy9>EQyDRF<2{ZVkR--!_qJBX?CFMcj|q;Mab3?LqjFB zQV6X*ks~{5YPn_{KY`BW=%we{<807}O&0~RiYFA2sRS+iuh5DE_OJw=aY9)VmSGLZ zpG;C@{$UENA^_4)TR~nSwAxI8%>v|$)R`w5-;o2HXItvyc$MSuUn2mR(h{%1ltSLG zVV+ht*^7y6yl<7bpJ(DjYr`cS0puAW6Q&@(;Jct82K38b6|>9abPJY=96xO5MjJZ! z0hNwH$(hc2$Q7{DjOq*iI#@P*;9XzYO0Eb&n7@$dZJ~qHXG{_q9wH&HEI=Zvf2C@` z9y@u!ujnJl*agjdBzW*pAa|&S;#QH?d#wK$<#gSup|(nQ*^YbjsOxW!nv!>|+O1SyeYbqArpKUNF0BHLbt3#C`GI;dgAuPfxI`;EC) zWSBhkA<}2(s6q!zs5|wtgfuW#;^FY?a>JTL2JH9dYZ<`DuL(~uz%Xrh`*6=8;Ejd{ zsf@hTFz(TKY}U_Bk{f+C;5(Nin5bF{;A1@>=vxD^5R3D|DR>_)b0c&U{Gq9-Rf z_s5QO>e1T?z$+>f1O5HzQFtaE3h$ipP8Clo)T%*3hq&$b;&Yf`iE=eA?xBx^?h}yJ zkLw0ge?QJMj{xA|W(@#H0lEVw{}`N8q+sq3U}vHh(i=lu0Ayp zEIy%&je%3dbFlULOG}WpW+4;b!#x_JVd(m$;i2%Bid%P}r08l3$n6aVrU!6>*8iN* z=U&BqsR~Xoy3KXGVb&X|oz19#9ph9?5LSNoAgtIGG6v*l2PMkY%u&x|16I$6{air21}_JS~|I_ z|5&8I|L%M#h{C<-3%(f*r?oKD$6VlJ1_qDozF}b+0adtIk+_dhuFmOXBYu5jF}jTl z!$5a2pf-`WhgCBiP=yrBZn01ST<~s~_qc1o58Ex!VvyoPJhxw`Olzdm&E>)*wgljt z-lF2^aI+(vY4}xFWLJAMwHs~M?;FPl*}cJRIP&>hkk z`nr^aar6pA2}K+9ge=7RqMkN=(n?AhDfgJsdloRI5QkubjTQUrW~5xGP2(nbCAz_s zn$+lEk9m|)vg{GMeAJklpRQHs=cHxOCM$5PV?Rqwg4Qio>m~_4W8cWTv^K0+g?mYZ zy@kE8b|@-bz0D9RR+x&s|ICbbX2_)VKln-kX$47G_s?%2LpzYxx3yTw67@%{Avb8Q zY0*!-ti7LSmP})s<^RIh^9*E1qfW z1pcte-`4DEFk3SnbTZHG$1$B2jV@;^#?p;v3*y$g(uEjCh;u$+B^RF&gN>veb{Oi~ z^qbiX&D&A+Xsb$F&s=_`nD&w`K&6c%(ad#cX>p+?rU`Yr>7@k6r~E8%o6q;2gxT%r&Ntfq~&M5W@izkmUfx#Y#XTg&~R|l`(}eo2f`Hg(-zOhbfmiikXoi zmARRLks*a8m_d`ZN;;@CPoX3uRlzl}xY9GPxFj(rMsH#ada5BUncq`xdr+KnFYE~CA!&Jg+_+vx#ju@v-FY8&?~6C#bJ}1pHiBW SYR3pPs2F4#3lj$;4$W4qOu&}UP+;EqxfV&7)vzt|rJXT9 z`lmCEt$xi+631!uPdn32+Ua)k!;Dk=CyiEZV>+GolscU{-ePREHcgsn?E8Mp0^%?{ zyytnZ-}U{tevLf7Mt;$qxzppKs;axfRDD&M)dQ|^h06U)F0bsHRYHE(F(q`&6%2S? z;YEE$4K-|Uc4+}!sSSf(o>sKzk<33O7m7)0&@auYelaPe%yYqtjI8TajE-#ihd@Nu zSol_6yUApt1prPnO-0!WAhMfjE21SF2rHV%mdn2n`k@QT9@_|Q=cSX5CC=&mRQoUW(?e5(R2uT^vdW>gLU9;{pjY<5-v9&t7Ro^rMV zUg4NnRa7jd>a&56u24zWXN?4ar8u)4J|IC=Jvhu)6$8Fm)eiVaj+xb1N+ZX~>L!p+ zRAZX=s$T}YQ@tC|Rf7fjYbuEteZ3}=WSxVF=p`^C0-q6jF8XfmS0w9w_zJG0-;bVm zRg$iYK)<$*#&07jLW62p2`#0A0Go6>RgOj!O&8MxAx)QqK?$`A6tA`!o<27UL$Vy|Sjn zKQb`Lu&inGD)kReL+l%MI9<=`3i8DqRDyND*Avla;x5cppSM*k=<#Ygm3_K90LMyy z&OolubBK97>5JM_47wWYO#PPaVoKi{ld((o%_!@L^!=gPkUAe)b-7hv(=I0OAKkk$ zS?PUp$hWxmod$Qq!lL9|48yR*jIc~KMVis|uoMiyTu1jdB$K`mVLg7eZhi1uOp^V6 zFHFu-#_IZLVKt;FVj5_CK~+;`U~0KtAT*_l(bJ7Kl6wh!FRhD(-(vJa(>+2?vmdui zyZV7JlBK|0Sf#LCV+ZqhK+-D+R}rouoJMHD16X_+#J^(DOc3w@nn_ChN1&HYEV9E( z2HBr?GzlM>SVrr+C3s|M3Bp+b!zxMsfKQhsir0#h&;oujJ!&lzJ~pxZwuizE6H9Ji z6t0`tciMj_+%mBTyQ-MGqfNMDVy|^f3HOlMe5y0kYQ!SkW{`*r8b zn?AvW_fapDN?Da0Yuxw(#>~fK19NDG^U{wc)D1MM0m{{7*cZA0# zR?=Oc`!CQMiO1w191IBg2t)pxS8naj7e3=gIo!ws_xjuho2BkN;qx^c(Sl39!RCH< zUiKFl_94Q55dI6m)SepKV-VJ|!e}kXOt6bR&9=`r>ady%J*~FSH!3X1e3Lt2qUQn% zUznm=?*b9N#@VeVPx;tt-08wI6YFqq&NVi{wpFM;DhD)08&#&2g|NW_?!#b;(+_^j z?ml8LyT`FYfv4n|BPr~v$3?8{si%?HSbg6aVq$;jJCc|I+lXc|(v?jTSpV+H^@N$7 z-+hhbv+wp_CE4u2zyY!~`tyPF1g?Nr_tcZZ=$SpW!a)y=!briR0cSoCnl=*PFWrW5 z69EjX67s2jP|zB%q)*V?GEZTFoPuHY%eqX9!OlqxVJaMJec1JAIkWG(n2OPP9BOC( z+_%*ps~3Zp1Bhw!a!8jVp#Y>%XeIJaRy$N+OG6R+C~WQuHa0Xx8rb=v!@XFSVVMd> zw3$9U~|K7lWArh`F3vy(rpOs z2%QK#bHX~78}V_qfW9(erF)u$OfzkXelTJs!d+OA(H!BCiQOK3!*Lgj!q)}u0teV8 zJ27r|JUHdCdfC~r;j$hejZ|L{_Qsq7FB?C_UKFb#?#FV+9o=rA#SAzZaM#FmQQ|HT z`X;8qDDWcT8F((V-Vo$l;|}6sSv@)Iz40fl*TKz5m85w^o~6nZ+$MM*Ia4TCn0;z^ zkv3rvzKC{|aDwRoyTAV>9IkbuG;=jeY($-`ZDQ{xPOeQPp>Fo(M7eDcyuY+W^r_k% zv?O|YqLZ}p-IuH@3%aB)hLza%O7#XpI6ls7$CNli7~E4FXc-d4tb4MFj6{!3?j@bP zz9FQC5mr5ngyDnrJS45eu_pQz+-DmCJv-P;#@XeGNp?ass(CCPW(;HTsH*`tc(Owc zWPkLxhn^7fg#E0gfc@>Tr*ahC_zb2@;7#YDRHZZ>E1y8s0RY(4f9H3@;UoAGId$Y@ z(LU6bpiZDK%B)A)`qDJU7{!5Dp*>O=o(r+P|A~n$NL5)pGY(WV4qktuy_}BMdK4@B+bbST3|^LlLmVx#|A_XErmi1b5MorAbA&V%yC?tL{|Qa z(t)qa*m!=A5^qgSDAghqA@F_AXOcfw&mxC!ERlWiO0fl>RU*3&(nT(1%B7YMxzr-t zExf}rNp>8^Hc3B1_!zY*5}poeDiZR=WlafAi8fBbVNkUQRX~Ln5-zlu9+UmblpG1_ zTHJIxsCwm~Mwc=64FDr~pFFp+Qn!%218|(TKKdzs-$TIWW~AW%5pe53uNSl7FE2{< z^F(?;4T%}+NM2tK5?B+eSIf+Lp2cc&pQHQ}@v5H>5HB8^7sb9su_IAzMn+of{_w$b z6P_afL;g#IuMoaQfOhg1FFw$pdGn5^;3_9UR_wOa249qS-(>ivvHdZGPQw63QZH_p zfgzfXc3B9UxIa9?^ne_shtSlDya^!z!HiIYiZO((_?eGT2#`b@P;6$WeN}F}R;dR8 vZwWerFpjVvK}6`sGT^>i`F~H6p!SuO8*1&q??{Ia?Vh delta 3796 zcmai1TTola8D;(oCZAZlaGO^A!R8;mg^3>abzG8Y-+*vALyAY>#RvF9j&Ck|w$ zY1}wOOR)c4pLz)x&_Ns^14Z>~sLGI$Ho^&fS2Y3Ut(zKzmT2TGI^e zi#4TyZ`AAo{HwsGT8ua)aHX~l+Ba&^+plXI0aNSt1G?)l?lX1OgvYMe<&wPHFyB4` z6PLuq>FwBi_5UDwn2X>h`oq}WhHBFNG0FPY>m0O?}2+wIe_Eu{au~|Xy)+Rgb^VkP%e<0*0 z`%$~UVGLJ3O%8-28Z}fJ--`vHMGFy%5bOvyL6NrOCX;N0Eq83izi)KRl1cXWjy7p4 zVU4@5@4)>>cOd*29jD5&FW@y~nc|V*B-AcWnJt|aQVC%loqv{`gw^a>k+um_U&B?x?hru zlj$n3_M_W92t5cc0CwJ0SJ@4X^{6O$AQdS0u;00MmG%A~O&IA1uHBV*f_#xfls_&U z;*h>bv!BFX=vyYzW4L~`WLV_nV1KrBfUslzMYfqOQ0*!;#+87s>f@?kU5?DK=lYM? zhr#oM6L}1{GZ5FxtOJc?hV>6r6+B80oUaXp$OQZ2Km$3%N(NhrhfNH=LPpqUgU3sz z(e;r>_&*5Tw}k(-{jTEwsW!^)?!QA$uS{h?Ck^f z(vcA;GoC&R#~2(zfv}$^!#@pR*f9W}s)oH9Y*Ih1!-5<_&b&mSKAaLa`4mH;CxLSk zF3m+Y6}HCuN8ian@4^oav7E83jSpU zA~VM3$Z2+W?3f3~#m#fUs6IdFOyS9J+tNaev4jRspfp8}z&s546m;N0u@P|*Rgisf zy-u@p?w80Sa~^uK?<*+(GQu+m&nDF2iLt>7C9!><26=Vzy&F79$~DoaV;>z#B~lAv zpN;2BU4&UCUaS(^7+*N_3>ZKqeii*bmvBynujkpj6Yh$$K=KT45DG4&8uOZuKA%uq zw`02|tIX$6ktvhb>Yb>7b}&fcQ+P*tJcWsH?DAw)%6Xu@aQqeKv-cIWPhurQ*upGRH_P;V31{i_l;y66icjQOcGNSnMUWf$ zBlH}5!&8}c5v)HqM~%7q4!RKg#Iu*|M%O&eP?rtaSc$0d8q1gsgcZ6HXO=M}P7`K0 zjO{SON!X@sZpka!sBXkVu5deWaxow&pM8su4`@y}}bo*TsCfd3ME6b&*hR z2`S5Z;FQWO2VK(>!!rXEUu)bH)WZImw0&5!lemCmZ19BIg^%9&G>dU6;<9Z+t{$NT zL2L<;W%0DeTbSMk;OwIlrDl8%a+ax-nm-n;CZ)`rjRD!TQf?NH1Gc7At=iE2R)i9i zrpvf-ASYpOqOGgJIi4jb8DAfeED)iFjD^6n<87Zhr$mE>o>27%wOJ*oQwREXqVKd( zC3J5A8#LmyZOF3B~Kf z%)6h((c12#e4fOp?{|H~8m|uzF5O{VCQBa{%=0.17.1) -Requires-Dist: pyyaml (>=3.13) -Requires-Dist: iso8601 (>=0.1.12) - -UNKNOWN - - diff --git a/backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/RECORD b/backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/RECORD deleted file mode 100644 index 9b6151d..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial-0.0.91.dist-info/RECORD +++ /dev/null @@ -1,33 +0,0 @@ -serial-0.0.91.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -serial-0.0.91.dist-info/METADATA,sha256=HIgxcDdfyCfsjlnzIWs9nmFaeMKOHvkxTjv6cBHRSTM,889 -serial-0.0.91.dist-info/RECORD,, -serial-0.0.91.dist-info/WHEEL,sha256=8T8fxefr_r-A79qbOJ9d_AaEgkpCGmEPHc-gpCq5BRg,110 -serial-0.0.91.dist-info/top_level.txt,sha256=FSjfWHWw-VjPiEqOhttbiP-F8OHn-liixq1wKL2fWOA,7 -serial/__init__.py,sha256=93wvMGEaUuBYuXcFk9knZsuJVrU6UxUVAfGyAXBgJ94,472 -serial/__pycache__/__init__.cpython-36.pyc,, -serial/__pycache__/errors.cpython-36.pyc,, -serial/__pycache__/hooks.cpython-36.pyc,, -serial/__pycache__/marshal.cpython-36.pyc,, -serial/__pycache__/meta.cpython-36.pyc,, -serial/__pycache__/model.cpython-36.pyc,, -serial/__pycache__/properties.cpython-36.pyc,, -serial/__pycache__/request.cpython-36.pyc,, -serial/__pycache__/test.cpython-36.pyc,, -serial/abc/__init__.py,sha256=kPltSkxwc2LSzjvmCLdRkou3fk6ND85H0Rq0pR3Sbcc,257 -serial/abc/__pycache__/__init__.cpython-36.pyc,, -serial/abc/__pycache__/model.cpython-36.pyc,, -serial/abc/__pycache__/properties.cpython-36.pyc,, -serial/abc/model.py,sha256=lI-7DDWvFjYHJ03VeZSNDxODdCCQeLia0ixZG5uiymU,6116 -serial/abc/properties.py,sha256=ho1HkWayE4m8SHfqM4O-RvcLjzRql4mQxBm65iOiwcc,3036 -serial/errors.py,sha256=aagqxWtjMLuyvHojOhSK4jspVDcXXKUQ-WavH9iXXmM,5217 -serial/hooks.py,sha256=PH28AQ5VtFbT5IuHS8IVMSjYtEWHR0FlBgREqDESi_U,8749 -serial/marshal.py,sha256=b61GMVRP19h_-DW1yqeu82676ti0ermlsvZEVb09pso,22125 -serial/meta.py,sha256=qXDFjkznp7fB_t4KDTVhLSGKnFfafQEAr9YeO9wtkX4,27529 -serial/model.py,sha256=NzdXq9CvcYNYnn-7wZvmwv1j2c8j7gR8gS6CWMtasq0,41874 -serial/properties.py,sha256=XdZlkjaeyc_7rs6x2BXtVl6pKx5cEaZuuzA9vi0RJuc,29488 -serial/request.py,sha256=hqn0CGzGP9pn5eErrHdiPUllIcVqYe-6YhOgGaT9J1M,14226 -serial/test.py,sha256=xNmDsOCeX7NYqmfxs1aQeB1P_0Fnfd88hyfEWhMPH4E,9273 -serial/utilities/__init__.py,sha256=_k3k8_wm9sZ-jWRNS9Bfi0zaHZWY9by-Ykj2qMmUvNQ,13250 -serial/utilities/__pycache__/__init__.cpython-36.pyc,, -serial/utilities/__pycache__/compatibility.cpython-36.pyc,, -serial/utilities/compatibility.py,sha256=rfAYMsSPUyHP8H2rURgbKW7mCsttBJ0R_NFXjXaZZLg,845 diff --git a/backend/venv/lib/python3.6/site-packages/serial/__init__.py b/backend/venv/lib/python3.6/site-packages/serial/__init__.py index 860b2fe..c24ced8 100644 --- a/backend/venv/lib/python3.6/site-packages/serial/__init__.py +++ b/backend/venv/lib/python3.6/site-packages/serial/__init__.py @@ -1,10 +1,89 @@ -""" -`serial` is an object serialization/deserialization library intended to facilitate authoring of API models which are -readable and introspective, and to expedite code and data validation and testing. `serial` supports JSON, YAML, and -XML. -""" +#!/usr/bin/env python +# +# This is a wrapper module for different platform implementations +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2001-2017 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals +import sys +import importlib -from . import utilities, abc, model, marshal, errors, properties, meta, hooks, test, request +from serial.serialutil import * +#~ SerialBase, SerialException, to_bytes, iterbytes + +__version__ = '3.4' + +VERSION = __version__ + +# pylint: disable=wrong-import-position +if sys.platform == 'cli': + from serial.serialcli import Serial +else: + import os + # chose an implementation, depending on os + if os.name == 'nt': # sys.platform == 'win32': + from serial.serialwin32 import Serial + elif os.name == 'posix': + from serial.serialposix import Serial, PosixPollSerial, VTIMESerial # noqa + elif os.name == 'java': + from serial.serialjava import Serial + else: + raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name)) + + +protocol_handler_packages = [ + 'serial.urlhandler', +] + + +def serial_for_url(url, *args, **kwargs): + """\ + Get an instance of the Serial class, depending on port/url. The port is not + opened when the keyword parameter 'do_not_open' is true, by default it + is. All other parameters are directly passed to the __init__ method when + the port is instantiated. + + The list of package names that is searched for protocol handlers is kept in + ``protocol_handler_packages``. + + e.g. we want to support a URL ``foobar://``. A module + ``my_handlers.protocol_foobar`` is provided by the user. Then + ``protocol_handler_packages.append("my_handlers")`` would extend the search + path so that ``serial_for_url("foobar://"))`` would work. + """ + # check and remove extra parameter to not confuse the Serial class + do_open = not kwargs.pop('do_not_open', False) + # the default is to use the native implementation + klass = Serial + try: + url_lowercase = url.lower() + except AttributeError: + # it's not a string, use default + pass + else: + # if it is an URL, try to import the handler module from the list of possible packages + if '://' in url_lowercase: + protocol = url_lowercase.split('://', 1)[0] + module_name = '.protocol_{}'.format(protocol) + for package_name in protocol_handler_packages: + try: + importlib.import_module(package_name) + handler_module = importlib.import_module(module_name, package_name) + except ImportError: + continue + else: + if hasattr(handler_module, 'serial_class_for_url'): + url, klass = handler_module.serial_class_for_url(url) + else: + klass = handler_module.Serial + break + else: + raise ValueError('invalid URL, protocol {!r} not known'.format(protocol)) + # instantiate and open when desired + instance = klass(None, *args, **kwargs) + instance.port = url + if do_open: + instance.open() + return instance diff --git a/backend/venv/lib/python3.6/site-packages/serial/__init__.pyc b/backend/venv/lib/python3.6/site-packages/serial/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f1122a6a2dc07b5fe5e8acde461e686cc060ea0 GIT binary patch literal 2322 zcmbVNUvC>l5T8B&i~poaTNIQRSVfe$(4MQdDpmO-Dp8>#lvYkjt4Qd)@onO@*LTP6 zx{f9Lh4R7!{S5sYyzS+P8&U6;c1>^hTZz;;L)rql4soln zJ7%!;g29g%e;ZDa$OVWy@DsAzAe#H#b)>BR#%Hg~P;@kWWNSFy3U7!Tj&d{9DqpPQ zWM3L3viarf)1Bb_?dz@-rm{(CZ7SDzK6oviNn8iJwq+CriEvGVZ z!l^Xj$Eo3qbY^&_g~Q2|cXzMY-K!jTDlLw*v=?!|^&x~aqemi%wKPMDx`S^6-$!IY z@gl!v0Fwq}?~s7g?ar!Fs_S5`7wXRwj2d87g~cW;F;E6Ca9V?>$wjCl)kzD?0Wijr zSMV$jU|E9+?*Eg7F(M*(1zzA-h3!=x;uCyK9uOpRslvpRc9kBH=2rP9#Ig(_`*NlIL~gLoKYm z#$%buBv#1@$EsziaXl)xvQ7ynC_ zY{}Q!!qPQ_#1$QMg)dk-?&nOeSi3_@$#bDqO!>NYUXv?^x{*(kbe`;WX=8p-(dDJn zxiszqO81>J>S*TV17omy(Y(P zq5Xl-v%(EsjC*M!@s6OI)HpBDQSgEK`xi6jwZfcOM|bdK?nS!oFiB+W!=2FK^daDm z$TJ%XEAh5CC*b4BLyM@N9i^k_#9TqBg~I2hWL)qeu<~-~q6~hpJ(R-U(M;*MhlvQa zN@l0mPw(D|?%ck9GyJ5NcR;T^k72g(=>Kkv4-v)QCe+P$S%t6d>yjAGKmRes0pS4SGTFzB+>!y24?Z1tE>ZP}1q3>azIXL+%{pk}l0;m~27pjD{{8|-q>p%L@Ajql}PtJ6951J literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/__init__.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/__init__.cpython-36.pyc index 99cd0ee8a060d3915c3db9eba542a030ccdddde4..a95229a525858c030bd644bc23091c2e3c36a3dc 100644 GIT binary patch literal 2073 zcmah~O^@6(7`Bs4CY$-_v{aSK$t;z22emV(1qlj5RHBqh&{o~0-A0Qt-m!NlGoB>1 zJv%$w%!T2?1@*w0I}#^;1V18IB&6~aIPp5kwA*qZk?q*;*YiHF?c8X!{D;|{ebz+i zPju$7;rnxF>UZcEVpyP{9C3hQZV77_F*~ri9XK3~Ys{Hg@*|McggdPVb&TRsVdn)N z_r@E{1^Hef_bd5V#d;mqPhi~>4YNLN2F+RP`^TMuO*>(6i=8ro@y22~` zJ!Xv~9GquO*7^Yj7Z~0_-S!K>jqY`Ej^8`AZ}dLM?V*TZ>h5qEQIT7*&TDC+qLZ#I z9nj6sJutHei4c`anE{xzd+yZExpCtx<2+YZ-p@`zUuI8EjPgTCGfogzMq%A=lf64)BQc9{cMBP zSC;%O+ZZ=8w8Hz=5-pwBg*o7R09uccZZfZoW(t!z%+K&IR%ZQ#mwtvaKWt^T{44Xv z?G@n8d?;0e{%9dY;JL#!p)rYKrD;6mBsnB{#7S8JG89y)tAz2C$1IAE2(X?eQul$+ z9@zy&Gb53L>S*8iOF)w|G9U4{a5UkIc_JA}siae`xg=XG2|+tFnp?(H%Nf5)4i*sR zkj@0SD_upZM{Ws05>UOP~PN={fL`A~}mtSbcpby9>2!zhk)7!rszN=h6BE|b(6 zYniN$sOGHa7h;o&h?F+@r*t@>N1Vg}7|@Mqk)YyK4o48l?7CDYIvFN{RGUhfJSIE^ z&A9M47_3#oN+qn^s==U&$$LjVGUsGYV{P!OSy~{ZZO7(~+Qv+aYFep!52vmizQ?@r>rn1?6D)C}3f1}Lmju6A%6tQEBsc&fI>=k)XIPdgZ`>^; zrNjD&Dhe^AxpQ*=5f!s4lw-_u$DD<3Be#L9+@bPFu;Qul1fa%>-nF*LM+l1tFqR>yIje*yala>)Py literal 770 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 L?Z&U!<$33S2L>HI diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/hooks.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/hooks.cpython-36.pyc deleted file mode 100644 index b481dd5b8d37af11aaa02d003b56a4bd9a138fda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4857 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 JcTQ{i{{o&E?co3b diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/meta.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/meta.cpython-36.pyc deleted file mode 100644 index 88b2500d659c71ce93c8aa2953d36fcaeed32b32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17338 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 M^32@KQ#j82Uvr1^8UO$Q diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/request.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/request.cpython-36.pyc deleted file mode 100644 index bf36e90587dcbfbcb95d59d9566798911c3d1c43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12480 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 CT29CS diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/rfc2217.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/rfc2217.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..02318a7c3281c7c0f1d7512c0eadf70c8d5e191e GIT binary patch literal 32682 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 RJ?XB=+hNf1TeXAR{{m^2)KdTe literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialcli.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialcli.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b12804c572e2eb0d5b7fa443da876e02833662b7 GIT binary patch literal 6627 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialjava.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialjava.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a7ef15c95b832de3f3431dafcc1e5ddca985ca7 GIT binary patch literal 7276 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialposix.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialposix.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f40016fe9ab933ef8a0d7cef30a212a7706b965b GIT binary patch literal 20214 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialutil.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialutil.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..decafc4956ecd5ff5cab9de7d1e7bcc92dcf5dea GIT binary patch literal 18385 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialwin32.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/serialwin32.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03889682f5c2860a516cbc86d3447f40bc89d3e2 GIT binary patch literal 12952 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 diff --git a/backend/venv/lib/python3.6/site-packages/serial/__pycache__/win32.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/__pycache__/win32.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e5142b355bf0349a08f3d2470d0d19da60def7c7 GIT binary patch literal 6386 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 VoJSGMN8dgrqQx*%($Yg?`#+=$F6jUO literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/abc/__init__.py b/backend/venv/lib/python3.6/site-packages/serial/abc/__init__.py deleted file mode 100644 index fcf351e..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/abc/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# region Backwards Compatibility -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals -from ..utilities.compatibility import backport - -backport() - -from . import model, properties - diff --git a/backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/__init__.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index e041bd14b116b92c362b688e18be8c786a9badab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 454 zcmYk1y-vh149C;Y>-Ac=jTd0)aMyu>1tAs|1{SsmDe6Z!BH}b8DF-u8!y}Ou3F%&e z31>heQhv4*`)~WKuFKEw>-#7o zx`@aakBKZ`NtY4f%Xfk=>6pn9R&)gwtuW5-NV7Pg9vWXOZXF+)9VX4KFLzvV)49o5 zUyR~KSTQMo-1T;nv*S#Fo{aNHd*L3Lb)DnD6~@{K<=Dfl1{Vs}vl7EJawdgCIcX1J zktXs*-wjVe;((iyCiWR%n)IbM*xxw8?QK*0lA)HH4QI@RN|OYn0a-vEum~s+^kjAd zJ7Mv5m_T<<^dWvTCH{J1s*qVDK8RTdKj7!Qb)Z`<^hPLLMas>q0S?c*eQUW9o%}Oh eKeUVqB^+a0{pO1c9D0`gGK^-!ET?f5=kYI|!hgL0 diff --git a/backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/model.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/model.cpython-36.pyc deleted file mode 100644 index 9e75de17443e42a05096b6cc4a5ea0731c4500ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5863 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 diff --git a/backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/properties.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/abc/__pycache__/properties.cpython-36.pyc deleted file mode 100644 index b35072e14e7e92cf067c0462bb7b441b5d8d3a5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2506 zcmbtWOOG5i5Vrd@Jw4Bz&5H<$1|q~9l09$&AruH%gaj+32tryQtEcVF_HI9BZBLR} z4X0fxSN;YU{tp&6B&7TWPE@&PW+$uUu-b09T^T3CdQ|qJ%r#EyA*$v?&qoQvxG|dqq@s zl8)v5qFeToUfECjl+1@94Pa!D3@CX_s#_ir!=p%^P*@2XwvxdUQaL}9ZwPPk@Qm{C=)#um5`~fr7|JUpXDmAD>FzZsxD?)u)Lhs zQk(wsTpuu{Q!PqS!S__=m1g_1D$}@NIIHrk=7JTu26csc<)Arg!bzGPp$g!mD%JTj z0czyfF|9AZdhor_sp*3;Ez?YwLLbx|Cb}TX(BIYLFB$bGFoGnMlf)5j;t8MM!Ep9g1Q(h?!aEDonAYm4?_1S4%kW;ud0BHmekcSOg|=x&VoyvO?}Pr5L7 zz=xLa0l&pZmhS_<&97O00Q_}+!}7zI#3RWTAb-n*KS;n@>iOPow%Q5y2+;#*>SG|B zEC?eD3Y0D!pw7Yt>e_K{;Q{Y+0{ZCQI4}Xza}1x~bY7K~!h}*B&vGfaX|rcSVk9A` zD{kx?Pl;k*Zhnzyb0ZZpYG^#x--qEck>E@T@-g;$WoNYPoM87A@j6Y zb?GGAnKrx1g}OJLn~<@*$~9x}fFgy-K*E5o-jl5r2KQF;GTc5sE;Mi1TPR-K=E2wG z*U@i|22d8xsiOk!KO)D$W1{IPIdm2fQh=g$@>3Kqp9cpbJBrgPww_m+i0l10jn!-|s_zaq|(eY0DYdiL&t%HW*x9fJS z?qu7pVYOuE7{AHj{d0p?avqQJ?zygwL$^wTOZjh8ub<9XN2vNX_%$SK7D{DS0 zkOytYv$SX=5=*4%ff(+A#S+RSauHh~fvZrA2mzK^p$K=L7yJ4ikP zf-0@LOccecpkRJO!oP=;Y;hR?hbP(A+tBeG$Axr;x7+TH9NPCiXv;qA`pqkG)!uj; zH?=?0P)k8S76OSSTw;Zhc(2I-Nef8`B)K`$Is6ykMUmCz6n+9G_&t{qO1q7!dpJo+ z_^Tquh3m!QBP}3(9P4~*|3n^2SxXbtlS7ed>(EBb)@96upEsy~fleu`;dh*_6F?if L4js}yjokkMiA)~v diff --git a/backend/venv/lib/python3.6/site-packages/serial/abc/model.py b/backend/venv/lib/python3.6/site-packages/serial/abc/model.py deleted file mode 100644 index e5ee35c..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/abc/model.py +++ /dev/null @@ -1,250 +0,0 @@ -# Tell the linters what's up: -# pylint:disable=wrong-import-position,consider-using-enumerate,useless-object-inheritance -# mccabe:options:max-complexity=999 -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals - -from serial.utilities.compatibility import backport - -backport() - -from abc import ABCMeta, abstractmethod - -# We need ABCs to inherit from `ABC` in python 3x, but in python 2x ABC is absent and we need classes to inherit from -# `object` in order to be new-style classes -try: - from abc import ABC -except ImportError: - ABC = object - -from ..utilities import collections - - -class Model(ABC): - - __metaclass__ = ABCMeta - - _format = None # type: Optional[str] - _meta = None # type: Optional[meta.Object] - _hooks = None # type: Optional[hooks.Object] - - @abstractmethod - def __init__(self): - self._format = None # type: Optional[str] - self._meta = None # type: Optional[meta.Meta] - self._hooks = None # type: Optional[hooks.Hooks] - self._url = None # type: Optional[str] - self._xpath = None # type: Optional[str] - self._pointer = None # type: Optional[str] - - @classmethod - def __subclasscheck__(cls, subclass): - # type: (object) -> bool - """ - Check a subclass to ensure it has required properties - """ - - if cls is subclass or type.__subclasscheck__(cls, subclass): - return True - - for attribute in ( - '_format', - '_meta', - '_hooks' - ): - if not hasattr(subclass, attribute): - return False - - return True - - def __instancecheck__(self, instance): - # type: (object) -> bool - """ - Check an instance of a subclass to ensure it has required properties - """ - - for attribute in ( - '_format', - '_meta', - '_hooks', - '_url', - '_xpath', - '_pointer' - ): - if not hasattr(self, attribute): - return False - - # Perform any instance checks needed for our superclass(es) - try: - return super().__instancecheck__(instance) - except AttributeError: # There is no further instance-checking to perform - return True - - @abstractmethod - def _marshal(self): - # type: (...) -> Union[collections.OrderedDict, list] - pass - - @abstractmethod - def _validate(self, raise_errors=True): - # type: (bool) -> None - pass - - @abstractmethod - def __str__(self): - # type: (...) -> str - pass - - @abstractmethod - def __repr__(self): - # type: (...) -> str - pass - - @abstractmethod - def __copy__(self): - # type: (...) -> Object - pass - - @abstractmethod - def __deepcopy__(self, memo): - # type: (Optional[dict]) -> Object - pass - - @abstractmethod - def __eq__(self, other): - # type: (Any) -> bool - pass - - @abstractmethod - def __ne__(self, other): - # type: (Any) -> bool - pass - - -class Object(Model): - - @abstractmethod - def _marshal(self): - # type: (...) -> collections.OrderedDict - pass - - @abstractmethod - def __setitem__(self, key, value): - # type: (str, Any) -> None - pass - - @abstractmethod - def __getitem__(self, key): - # type: (str, Any) -> None - pass - - @abstractmethod - def __setattr__(self, property_name, value): - # type: (Object, str, Any) -> None - pass - - @abstractmethod - def __getattr__(self, key): - # type: (str) -> Any - pass - - @abstractmethod - def __delattr__(self, key): - # type: (str) -> None - pass - - -class Dictionary(Model): - - @classmethod - def __subclasscheck__(cls, subclass): - # type: (object) -> bool - """ - Verify inheritance - """ - if cls is subclass or type.__subclasscheck__(cls, subclass): - return True - - if not issubclass(subclass, collections.OrderedDict): - return False - - # Perform any subclass checks needed for our superclass(es) - return super().__subclasscheck__(subclass) - - def __instancecheck__(self, instance): - # type: (object) -> bool - """ - Check an instance of a subclass to ensure it has required properties - """ - - if not isinstance(self, collections.OrderedDict): - return False - - # Perform any instance checks needed for our superclass(es) - return super().__instancecheck__(instance) - - @abstractmethod - def _marshal(self): - # type: (...) -> collections.OrderedDict - pass - - @abstractmethod - def __setitem__(self, key, value): - # type: (str, Any) -> None - pass - - def keys(self): - # type: (...) -> Iterable[str] - pass - - def values(self): - # type: (...) -> Iterable[Any] - pass - - -class Array(Model): - - @classmethod - def __subclasscheck__(cls, subclass): - # type: (object) -> bool - """ - Verify inheritance - """ - - if cls is subclass or type.__subclasscheck__(cls, subclass): - return True - - if not issubclass(subclass, list): - return False - - # Perform any subclass checks needed for our superclass(es) - return super().__subclasscheck__(subclass) - - def __instancecheck__(self, instance): - # type: (object) -> bool - """ - Check an instance of a subclass to ensure it has required properties - """ - - if not isinstance(self, list): - return False - - # Perform any instance checks needed for our superclass(es) - return super().__instancecheck__(instance) - - @abstractmethod - def _marshal(self): - # type: (...) -> list - pass - - @abstractmethod - def __setitem__(self, key, value): - # type: (str, Any) -> None - pass - - @abstractmethod - def append(self, value): - # type: (Any) -> None - pass - - diff --git a/backend/venv/lib/python3.6/site-packages/serial/abc/properties.py b/backend/venv/lib/python3.6/site-packages/serial/abc/properties.py deleted file mode 100644 index 0db386c..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/abc/properties.py +++ /dev/null @@ -1,108 +0,0 @@ -# Tell the linters what's up: -# pylint:disable=wrong-import-position,consider-using-enumerate,useless-object-inheritance -# mccabe:options:max-complexity=999 -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals -from ..utilities.compatibility import backport - -backport() - -from future.utils import native_str # noqa - -from abc import ABCMeta, abstractmethod - -# We need to inherit from `ABC` in python 3x, but in python 2x ABC is absent -try: - from abc import ABC -except ImportError: - ABC = object - - -class Property(ABC): - - __metaclass__ = ABCMeta - - @abstractmethod - def __init__( - self, - types=None, # type: Sequence[Union[type, Property]] - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Sequence[Union[str, serial.meta.Version]]] - ): - self._types = None # type: Optional[Sequence[Union[type, Property]]] - self.types = types - self.name = name - self.required = required - self._versions = None # type: Optional[Union[Mapping[str, Optional[Property]], Set[Union[str, Number]]]] - self.versions = versions # type: Optional[Union[Mapping[str, Optional[Property]], Set[Union[str, Number]]]] - - def __instancecheck__(self, instance): - # type: (object) -> bool - """ - Check an instance of a subclass to ensure it has required properties - """ - - for attribute in ( - '_types', - '_versions', - 'name', - 'required' - ): - if not hasattr(self, attribute): - return False - - # Perform any instance checks needed for our superclass(es) - return super().__instancecheck__(instance) - - @property - @abstractmethod - def types(self): - # type: (...) -> Optional[Sequence[Union[type, Property, Model]]] - pass - - @types.setter - @abstractmethod - def types(self, types_or_properties): - # type: (Optional[Sequence[Union[type, Property, Model]]]) -> None - pass - - @property - @abstractmethod - def versions(self): - # type: () -> Optional[Sequence[meta.Version]] - pass - - @versions.setter - @abstractmethod - def versions( - self, - versions # type: Optional[Sequence[Union[str, collections_abc.Iterable, meta.Version]]] - ): - # type: (...) -> Optional[Union[Mapping[str, Optional[Property]], Set[Union[str, Number]]]] - pass - - @abstractmethod - def unmarshal(self, data): - # type: (Any) -> Any - pass - - @abstractmethod - def marshal(self, data): - # type: (Any) -> Any - pass - - @abstractmethod - def __repr__(self): - # type: (...) -> str - pass - - @abstractmethod - def __copy__(self): - # type: (...) -> Property - pass - - @abstractmethod - def __deepcopy__(self, memo): - # type: (dict) -> Property - pass diff --git a/backend/venv/lib/python3.6/site-packages/serial/aio.py b/backend/venv/lib/python3.6/site-packages/serial/aio.py new file mode 100644 index 0000000..257c47c --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/aio.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +# +# Python Serial Port Extension for Win32, Linux, BSD, Jython +# module for serial IO for POSIX compatible systems, like Linux +# see __init__.py +# +# (C) 2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +"""\ +Support asyncio with serial ports. EXPERIMENTAL + +Posix platforms only, Python 3.4+ only. + +Windows event loops can not wait for serial ports with the current +implementation. It should be possible to get that working though. +""" +import asyncio +import serial +import logger + + +class SerialTransport(asyncio.Transport): + def __init__(self, loop, protocol, serial_instance): + self._loop = loop + self._protocol = protocol + self.serial = serial_instance + self._closing = False + self._paused = False + # XXX how to support url handlers too + self.serial.timeout = 0 + self.serial.nonblocking() + loop.call_soon(protocol.connection_made, self) + # only start reading when connection_made() has been called + loop.call_soon(loop.add_reader, self.serial.fd, self._read_ready) + + def __repr__(self): + return '{self.__class__.__name__}({self._loop}, {self._protocol}, {self.serial})'.format(self=self) + + def close(self): + if self._closing: + return + self._closing = True + self._loop.remove_reader(self.serial.fd) + self.serial.close() + self._loop.call_soon(self._protocol.connection_lost, None) + + def _read_ready(self): + data = self.serial.read(1024) + if data: + self._protocol.data_received(data) + + def write(self, data): + self.serial.write(data) + + def can_write_eof(self): + return False + + def pause_reading(self): + if self._closing: + raise RuntimeError('Cannot pause_reading() when closing') + if self._paused: + raise RuntimeError('Already paused') + self._paused = True + self._loop.remove_reader(self._sock_fd) + if self._loop.get_debug(): + logger.debug("%r pauses reading", self) + + def resume_reading(self): + if not self._paused: + raise RuntimeError('Not paused') + self._paused = False + if self._closing: + return + self._loop.add_reader(self._sock_fd, self._read_ready) + if self._loop.get_debug(): + logger.debug("%r resumes reading", self) + + #~ def set_write_buffer_limits(self, high=None, low=None): + #~ def get_write_buffer_size(self): + #~ def writelines(self, list_of_data): + #~ def write_eof(self): + #~ def abort(self): + + +@asyncio.coroutine +def create_serial_connection(loop, protocol_factory, *args, **kwargs): + ser = serial.Serial(*args, **kwargs) + protocol = protocol_factory() + transport = SerialTransport(loop, protocol, ser) + return (transport, protocol) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + class Output(asyncio.Protocol): + def connection_made(self, transport): + self.transport = transport + print('port opened', transport) + transport.serial.rts = False + transport.write(b'hello world\n') + + def data_received(self, data): + print('data received', repr(data)) + self.transport.close() + + def connection_lost(self, exc): + print('port closed') + asyncio.get_event_loop().stop() + + loop = asyncio.get_event_loop() + coro = create_serial_connection(loop, Output, '/dev/ttyUSB0', baudrate=115200) + loop.run_until_complete(coro) + loop.run_forever() + loop.close() diff --git a/backend/venv/lib/python3.6/site-packages/serial/aio.pyc b/backend/venv/lib/python3.6/site-packages/serial/aio.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3e720250fe9f256e1fb53e9c39823031ca4fc812 GIT binary patch literal 4496 zcmb_fTXP)674F%awQC83xYKRMKv;z=w&jRX{|<{o!;v1 zv8;;qg7cgo#Y6r^p7Wd^kbK|i*^*9aTl@07RfW>Y%fvyXv5;imuX;I_aq+Z0M=KDfK|9KOgjkJ5WVm zSUBR6(i^H6s5>2HzgGnw@!C*c+gQCe67FT5aj>a0HenvAVyFtt;CG{gEizA@QF==~ zh*ZRfD||tyXN9UEdq?e$H>uA*G3_^tg|R-)+*zHMCO$3waqP4$vnu9-o5Z_++S^S& z|8jSF`@=6rqdnuwd-0;m{E@M97n`~|dnevI^T(!+-<{lgTR4-^=-aX`%&Cj@U0wUQ zGG^i8Jgei{`1mv{eT?@X`cn9|Kh|;HSc{iN<$O`;IjGE+rk=!~``8_urYhn?4e6XK z4=Wve6VJ4d{c(najlEOWGd!4Pc03uC|H04HU!kU=)NPy9j*6|^8)5Aunqv=>hI!#G zMY{)ol7KTi!s*eg5viYQ=(gF8?IJqPa#VIEnJOYTp!h?>AS{4>Y%e|L{*aS*cRKOJD z4g{E%nACU+lka0n=_5ZRwXnvU+*F>}4``4rpJbKOo;sy@1t8Qje@XUdjnjqDzMN~* zcz+rE+SG@Y$>|X}H_xgnb;i`e?sYO-_KlvS})m4}n6tebl`?tSoH`rfS@ zzn;9m6JFiPN;6rU1#NLo>C^No58mcc(Qx_O*^IWL=Q}xZ#L>kNCqD}UemjwnqinE4UEP8vp?MP~H-RfLsuLnQq3n;M4y#^H zoSIrc1&bvw<0HhFqXdLV591}kLu^J7I0PI!AcDm+-HHB+>;4vXg619100fzlq_P7F4ty4WfLh)k?D$ zu(Z;w)!>3Jxi3ggho0Kf>$Qx@Pr!R}`F@JGe}nNTQVABZI?d+SxFSCx?SPa zsi>4{bMyfik4i#(iX;|9u#&y15HCm+fWUW=g0sm-FK|=%oi)EL3sNpZG;t@w2*sWh zVoHjFtrfBsBS`{}!L)=Bm7TiRC`DGY5-*s!C3=vg$8W`_$GVQ&-0qlJKCFnYv#_yn z&w+K_h9?(uiSIFWhX5t=lcr`B*tOQ!giuLd#Y-WUivJ(l_ z)*F4{hKdc(ur>!bZ7}uOQbBnXX*3t$Uvv{cJ$1C|bm^l)>JChAwQJ^jZF@ zzra~s5$r9iF&Bu>j#>}Sv$9T8k?gC+FPi_+Q4?btE%E>DsL>=-C9(Umn0yR| z`xPeC4}=E7iw$r@n|}Hky9=~c=!K<1BJCl7m;(!DcAhnLguUEr%tF_?kb-=ys|pt! zW2<5`W^zexaH5}sbNDK5JP8qZp(b(H6Pq?+5N8;{gfQUcXmedP`AJchF$o=kvrsZc zQ)Ua%^Z~MzXh#E09YdLw9J!#a@;s+)1r|2wCCv!;OZESemGu>zGsAGSmWrNRYe0mH z2G>A91NcXzhRATyBZ9BoBAred6SbAG%B5N>-s}NJ!W(zY?=OoA#D@#&0yFQK<*U!?vD3oU+;f( zQ-bO+YYK}XEYs4Bq9NF<*Fa)WR!6o)r*I7v?!pe=s{j02XGU0yeB!};{ zcRCR#xy3a}JTw8{@l3w9sZ&<(3JHLhZ?EM7Mm8=M@QPhExfiVRm None - """ - Generate a comprehensible error message for data which could not be un-marshalled according to spec, and raise - the appropriate exception - """ - - self._message = None # type: Optional[str] - self._parameter = None # type: Optional[str] - self._index = None # type: Optional[int] - self._key = None # type: Optional[str] - - error_message_lines = [''] - - # Identify which parameter is being used for type validation - - types_label = None - - if types: - types_label = 'types' - elif item_types: - types_label = 'item_types' - types = item_types - elif value_types: - types_label = 'value_types' - types = value_types - - # Assemble the error message - - # Assemble a text representation of the `data` - - data_lines = [] - - lines = repr(data).strip().split('\n') - - if len(lines) == 1: - - data_lines.append(lines[0]) - - else: - - data_lines.append('') - - for line in lines: - data_lines.append( - ' ' + line - ) - - # Assemble a text representation of the `types`, `item_types`, or `value_types`. - - if types is None: - - error_message_lines.append('The data provided is not an instance of an un-marshallable type:') - - else: - - error_message_lines.append( - 'The data provided does not match any of the expected types and/or property definitions:' - ) - - error_message_lines.append( - ' - data: %s' % '\n'.join(data_lines) - ) - - if types is None: - - types = UNMARSHALLABLE_TYPES - types_label = 'un-marshallable types' - - types_lines = ['('] - - for type_ in types: - - if isinstance(type_, type): - lines = (qualified_name(type_),) - else: - lines = repr(type_).split('\n') - - for line in lines: - types_lines.append( - ' ' + line - ) - - types_lines[-1] += ',' - - types_lines.append(' )') - - error_message_lines.append( - ' - %s: %s' % (types_label, '\n'.join(types_lines)) - ) - - self.message = '\n'.join(error_message_lines) - - @property - def paramater(self): - # type: (...) -> Optional[str] - return self._parameter - - @paramater.setter - def paramater(self, paramater_name): - # type: (str) -> None - if paramater_name != self.paramater: - self._parameter = paramater_name - self.assemble_message() - - @property - def message(self): - # type: (...) -> Optional[str] - return self._message - - @message.setter - def message(self, message_text): - # type: (str) -> None - if message_text != self.message: - self._message = message_text - self.assemble_message() - - @property - def index(self): - # type: (...) -> Optional[int] - return self._message - - @index.setter - def index(self, index_or_key): - # type: (Union[str, int]) -> None - if index_or_key != self.index: - self._index = index_or_key - self.assemble_message() - - def assemble_message(self): - - messages = [] - - if self.paramater: - messages.append( - 'Errors encountered in attempting to un-marshal %s:' % self.paramater - ) - - if self.index is not None: - messages.append( - 'Errors encountered in attempting to un-marshal the value at index %s:' % repr(self.index) - ) - - if self.message: - messages.append(self.message) - - super().__init__('\n'.join(messages)) - - -class UnmarshalTypeError(UnmarshalError, TypeError): - - pass - - -class UnmarshalValueError(UnmarshalError, ValueError): - - pass diff --git a/backend/venv/lib/python3.6/site-packages/serial/hooks.py b/backend/venv/lib/python3.6/site-packages/serial/hooks.py deleted file mode 100644 index 231a800..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/hooks.py +++ /dev/null @@ -1,258 +0,0 @@ -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals -from .utilities.compatibility import backport - -backport() - -import serial.abc - -from copy import deepcopy - -import serial -from .utilities import qualified_name -from .abc.model import Model - -try: - import typing -except ImportError as e: - typing = None - - -class Hooks(object): - - def __init__( - self, - before_marshal=None, # Optional[Callable] - after_marshal=None, # Optional[Callable] - before_unmarshal=None, # Optional[Callable] - after_unmarshal=None, # Optional[Callable] - before_serialize=None, # Optional[Callable] - after_serialize=None, # Optional[Callable] - before_deserialize=None, # Optional[Callable] - after_deserialize=None, # Optional[Callable] - before_validate=None, # Optional[Callable] - after_validate=None, # Optional[Callable] - ): - self.before_marshal = before_marshal - self.after_marshal = after_marshal - self.before_unmarshal = before_unmarshal - self.after_unmarshal = after_unmarshal - self.before_serialize = before_serialize - self.after_serialize = after_serialize - self.before_deserialize = before_deserialize - self.after_deserialize = after_deserialize - self.before_validate = before_validate - self.after_validate = after_validate - - def __copy__(self): - return self.__class__(**vars(self)) - - def __deepcopy__(self, memo=None): - # type: (dict) -> Memo - return self.__class__(**{ - k: deepcopy(v, memo=memo) - for k, v in vars(self).items() - }) - - def __bool__(self): - return True - - -class Object(Hooks): - - def __init__( - self, - before_marshal=None, # Optional[Callable] - after_marshal=None, # Optional[Callable] - before_unmarshal=None, # Optional[Callable] - after_unmarshal=None, # Optional[Callable] - before_serialize=None, # Optional[Callable] - after_serialize=None, # Optional[Callable] - before_deserialize=None, # Optional[Callable] - after_deserialize=None, # Optional[Callable] - before_validate=None, # Optional[Callable] - after_validate=None, # Optional[Callable] - before_setattr=None, # Optional[Callable] - after_setattr=None, # Optional[Callable] - before_setitem=None, # Optional[Callable] - after_setitem=None, # Optional[Callable] - ): - self.before_marshal = before_marshal - self.after_marshal = after_marshal - self.before_unmarshal = before_unmarshal - self.after_unmarshal = after_unmarshal - self.before_serialize = before_serialize - self.after_serialize = after_serialize - self.before_deserialize = before_deserialize - self.after_deserialize = after_deserialize - self.before_validate = before_validate - self.after_validate = after_validate - self.before_setattr = before_setattr - self.after_setattr = after_setattr - self.before_setitem = before_setitem - self.after_setitem = after_setitem - - -class Array(Hooks): - - def __init__( - self, - before_marshal=None, # Optional[Callable] - after_marshal=None, # Optional[Callable] - before_unmarshal=None, # Optional[Callable] - after_unmarshal=None, # Optional[Callable] - before_serialize=None, # Optional[Callable] - after_serialize=None, # Optional[Callable] - before_deserialize=None, # Optional[Callable] - after_deserialize=None, # Optional[Callable] - before_validate=None, # Optional[Callable] - after_validate=None, # Optional[Callable] - before_setitem=None, # Optional[Callable] - after_setitem=None, # Optional[Callable] - before_append=None, # Optional[Callable] - after_append=None, # Optional[Callable] - ): - self.before_marshal = before_marshal - self.after_marshal = after_marshal - self.before_unmarshal = before_unmarshal - self.after_unmarshal = after_unmarshal - self.before_serialize = before_serialize - self.after_serialize = after_serialize - self.before_deserialize = before_deserialize - self.after_deserialize = after_deserialize - self.before_validate = before_validate - self.after_validate = after_validate - self.before_setitem = before_setitem - self.after_setitem = after_setitem - self.before_append = before_append - self.after_append = after_append - - -class Dictionary(Hooks): - - def __init__( - self, - before_marshal=None, # Optional[Callable] - after_marshal=None, # Optional[Callable] - before_unmarshal=None, # Optional[Callable] - after_unmarshal=None, # Optional[Callable] - before_serialize=None, # Optional[Callable] - after_serialize=None, # Optional[Callable] - before_deserialize=None, # Optional[Callable] - after_deserialize=None, # Optional[Callable] - before_validate=None, # Optional[Callable] - after_validate=None, # Optional[Callable] - before_setitem=None, # Optional[Callable] - after_setitem=None, # Optional[Callable] - ): - self.before_marshal = before_marshal - self.after_marshal = after_marshal - self.before_unmarshal = before_unmarshal - self.after_unmarshal = after_unmarshal - self.before_serialize = before_serialize - self.after_serialize = after_serialize - self.before_deserialize = before_deserialize - self.after_deserialize = after_deserialize - self.before_validate = before_validate - self.after_validate = after_validate - self.before_setitem = before_setitem - self.after_setitem = after_setitem - - -def read( - model_instance # type: Union[type, serial.abc.model.Model] -): - # type: (...) -> Hooks - """ - Read metadata from a model instance (the returned metadata may be inherited, and therefore should not be written to) - """ - - if isinstance(model_instance, type): - return model_instance._hooks - elif isinstance(model_instance, Model): - return model_instance._hooks or read(type(model_instance)) - - -def writable( - model_instance # type: Union[type, serial.abc.model.Model] -): - # type: (...) -> Hooks - """ - Retrieve a metadata instance. If the instance currently inherits its metadata from a class or superclass, - this funtion will copy that metadata and assign it directly to the model instance. - """ - - if isinstance(model_instance, type): - - if model_instance._hooks is None: - - model_instance._hooks = ( - Object() - if issubclass(model_instance, serial.model.Object) else - Array() - if issubclass(model_instance, serial.model.Array) else - Dictionary() - if issubclass(model_instance, serial.model.Dictionary) - else None - ) - - else: - - for b in model_instance.__bases__: - - if hasattr(b, '_hooks') and (model_instance._hooks is b._hooks): - model_instance._hooks = deepcopy(model_instance._hooks) - break - - elif isinstance(model_instance, Model): - - if model_instance._hooks is None: - model_instance._hooks = deepcopy(writable(type(model_instance))) - - return model_instance._hooks - - -def write( - model_instance, # type: Union[type, serial.abc.model.Model] - meta # type: Hooks -): - # type: (...) -> None - """ - Write metadata to a class or instance - """ - - if isinstance(model_instance, type): - - t = model_instance - mt = ( - Object - if issubclass(model_instance, serial.model.Object) else - Array - if issubclass(model_instance, serial.model.Array) else - Dictionary - if issubclass(model_instance, serial.model.Dictionary) - else None - ) - - elif isinstance(model_instance, Model): - - t = type(model_instance) - mt = ( - Object - if isinstance(model_instance, serial.model.Object) else - Array - if isinstance(model_instance, serial.model.Array) else - Dictionary - if isinstance(model_instance, serial.model.Dictionary) - else None - ) - - if not isinstance(meta, mt): - raise ValueError( - 'Hooks assigned to `%s` must be of type `%s`' % ( - qualified_name(t), - qualified_name(mt) - ) - ) - - model_instance._hooks = meta diff --git a/backend/venv/lib/python3.6/site-packages/serial/marshal.py b/backend/venv/lib/python3.6/site-packages/serial/marshal.py deleted file mode 100644 index 15ecdce..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/marshal.py +++ /dev/null @@ -1,704 +0,0 @@ -# Tell the linters what's up: -# pylint:disable=wrong-import-position,consider-using-enumerate,useless-object-inheritance -# mccabe:options:max-complexity=999 -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals - -from .utilities.compatibility import backport - -backport() # noqa - -from future.utils import native_str - -from copy import deepcopy - -import json -from decimal import Decimal -from base64 import b64encode -from numbers import Number - -import yaml -from datetime import date, datetime - -from itertools import chain - -import serial -from . import utilities, abc, properties, errors, meta, hooks -from .utilities import Generator, qualified_name, read, collections, collections_abc - - -try: - from typing import Union, Optional, Sequence, Any, Callable -except ImportError: - Union = Optional = Sequence = Any = Callable = None - -try: - from abc import ABC -except ImportError: - ABC = None - - -UNMARSHALLABLE_TYPES = tuple({ - str, bytes, native_str, Number, Decimal, date, datetime, bool, - dict, collections.OrderedDict, - collections_abc.Set, collections_abc.Sequence, Generator, - abc.model.Model, properties.Null, properties.NoneType -}) - - -def ab2c(abc_or_property): - # type: (ABC) -> Union[model.Object, model.Dict, model.Array, properties.Property] - """ - Get the corresponding model class from an abstract base class, or if none exists--return the original class or type - """ - - class_or_property = abc_or_property # type: (type) - - if isinstance(abc_or_property, type): - - if abc_or_property in {abc.model.Dictionary, abc.model.Array, abc.model.Object}: - - class_or_property = getattr( - serial.model, - abc_or_property.__name__.split('.')[-1] - ) - - elif isinstance(abc_or_property, properties.Property): - - class_or_property = deepcopy(abc_or_property) - - for types_attribute in ('types', 'value_types', 'item_types'): - - if hasattr(class_or_property, types_attribute): - - setattr( - class_or_property, - types_attribute, - tuple( - ab2c(type_) for type_ in getattr(class_or_property, types_attribute) - ) - ) - - return class_or_property - - -def ab2cs(abcs_or_properties): - # type: (Sequence[Union[ABC, properties.Property]]) -> Sequence[model.Model, properties.Property] - for abc_or_property in abcs_or_properties: - yield ab2c(abc_or_property) - - -class _Marshal(object): - - pass - - -def marshal( - data, # type: Any - types=None, # type: Optional[Sequence[Union[type, properties.Property, Callable]]] - value_types=None, # type: Optional[Sequence[Union[type, properties.Property]]] - item_types=None, # type: Optional[Sequence[Union[type, properties.Property]]] -): - # type: (...) -> Any - - """ - Recursively converts instances of `serial.abc.model.Model` into JSON/YAML/XML serializable objects. - """ - - if hasattr(data, '_marshal'): - return data._marshal() # noqa - this is *our* protected member, so linters can buzz off - - if data is None: - return data - - if callable(types): - types = types(data) - - # If data types have been provided, validate the un-marshalled data by attempting to initialize the provided type(s) - # with `data` - - if types is not None: - - if (str in types) and (native_str is not str) and (native_str not in types): - - types = tuple(chain(*( - ((type_, native_str) if (type_ is str) else (type_,)) - for type_ in types - ))) - - matched = False - - for type_ in types: - - if isinstance(type_, properties.Property): - - try: - data = type_.marshal(data) - matched = True - break - except TypeError: - pass - - elif isinstance(type_, type) and isinstance(data, type_): - - matched = True - - break - - if not matched: - raise TypeError( - '%s cannot be interpreted as any of the designated types: %s' % ( - repr(data), - repr(types) - ) - ) - - if value_types is not None: - for k, v in data.items(): - data[k] = marshal(v, types=value_types) - - if item_types is not None: - - for i in range(len(data)): - data[i] = marshal(data[i], types=item_types) - - if isinstance(data, Decimal): - return float(data) - - if isinstance(data, (date, datetime)): - return data.isoformat() - - if isinstance(data, native_str): - return data - - if isinstance(data, (bytes, bytearray)): - return str(b64encode(data), 'ascii') - - if hasattr(data, '__bytes__'): - return str(b64encode(bytes(data)), 'ascii') - - return data - - -class _Unmarshal(object): - """ - This class should be used exclusively by `serial.marshal.unmarshal`. - """ - - def __init__( - self, - data, # type: Any - types=None, # type: Optional[Sequence[Union[type, properties.Property]]] - value_types=None, # type: Optional[Sequence[Union[type, properties.Property]]] - item_types=None # type: Optional[Sequence[Union[type, properties.Property]]] - ): - # type: (...) -> None - - if not isinstance( - data, - UNMARSHALLABLE_TYPES - ): - # Verify that the data can be parsed before attempting to un-marshall it - - raise errors.UnmarshalTypeError( - '%s, an instance of `%s`, cannot be un-marshalled. ' % (repr(data), type(data).__name__) + - 'Acceptable types are: ' + ', '.join(( - qualified_name(data_type) - for data_type in UNMARSHALLABLE_TYPES - )) - ) - - # If only one type was passed for any of the following parameters--we convert it to a tuple - # If any parameters are abstract base classes--we convert them to the corresponding models - - if types is not None: - - if not isinstance(types, collections_abc.Sequence): - types = (types,) - - if value_types is not None: - - if not isinstance(value_types, collections_abc.Sequence): - value_types = (value_types,) - - if item_types is not None: - - if not isinstance(item_types, collections_abc.Sequence): - item_types = (item_types,) - - # Member data - - self.data = data # type: Any - self.types = types # type: Optional[Sequence[Union[type, properties.Property]]] - self.value_types = value_types # type: Optional[Sequence[Union[type, properties.Property]]] - self.item_types = item_types # type: Optional[Sequence[Union[type, properties.Property]]] - self.meta = None # type: Optional[meta.Meta] - - def __call__(self): - # type: (...) -> Any - """ - Return `self.data` unmarshalled - """ - - unmarshalled_data = self.data - - if ( - (self.data is not None) and - (self.data is not properties.NULL) - ): - # If the data is a serial `Model`, get it's metadata - if isinstance(self.data, abc.model.Model): - - self.meta = meta.read(self.data) - - if self.meta is None: # Only un-marshall models if they have no metadata yet (are generic) - - # If `types` is a function, it should be one which expects to receive marshalled data and returns a list - # of types which are applicable - if callable(self.types): - self.types = self.types(self.data) - - # If the data provided is a `Generator`, make it static by casting the data into a tuple - if isinstance(self.data, Generator): - self.data = tuple(self.data) - - if self.types is None: - - # If no types are provided, we unmarshal the data into one of serial's generic container types - unmarshalled_data = self.as_container_or_simple_type - - else: - - self.backport_types() - - unmarshalled_data = None - successfully_unmarshalled = False - - first_error = None # type: Optional[Exception] - - # Attempt to un-marshal the data as each type, in the order provided - for type_ in self.types: - - error = None # type: Optional[Union[AttributeError, KeyError, TypeError, ValueError]] - - try: - - unmarshalled_data = self.as_type(type_) - - # if (self.data is not None) and (unmarshalled_data is None): - # raise RuntimeError(self.data) - - # If the data is un-marshalled successfully, we do not need to try any further types - successfully_unmarshalled = True - break - - except (AttributeError, KeyError, TypeError, ValueError) as e: - - error = e - - if (first_error is None) and (error is not None): - first_error = error - - if not successfully_unmarshalled: - - if (first_error is None) or isinstance(first_error, TypeError): - - raise errors.UnmarshalTypeError( - self.data, - types=self.types, - value_types=self.value_types, - item_types=self.item_types - ) - - elif isinstance(first_error, ValueError): - - raise errors.UnmarshalValueError( - self.data, - types=self.types, - value_types=self.value_types, - item_types=self.item_types - ) - - else: - - raise first_error # noqa - pylint erroneously identifies this as raising `None` - - return unmarshalled_data - - @property - def as_container_or_simple_type(self): - # type: (...) -> Any - - """ - This function unmarshalls and returns the data into one of serial's container types, or if the data is of a - simple data type--it returns that data unmodified - """ - - unmarshalled_data = self.data - - if isinstance(self.data, abc.model.Dictionary): - - type_ = type(self.data()) - - if self.value_types is not None: - unmarshalled_data = type_(self.data, value_types=self.value_types) - - elif isinstance(self.data, abc.model.Array): - - if self.item_types is not None: - unmarshalled_data = serial.model.Array(self.data, item_types=self.item_types) - - elif isinstance(self.data, (dict, collections.OrderedDict)): - - unmarshalled_data = serial.model.Dictionary(self.data, value_types=self.value_types) - - elif ( - isinstance(self.data, (collections_abc.Set, collections_abc.Sequence)) - ) and ( - not isinstance(self.data, (str, bytes, native_str)) - ): - - unmarshalled_data = serial.model.Array(self.data, item_types=self.item_types) - - elif not isinstance(self.data, (str, bytes, native_str, Number, Decimal, date, datetime, bool, abc.model.Model)): - - raise errors.UnmarshalValueError( - '%s cannot be un-marshalled' % repr(self.data) - ) - - return unmarshalled_data - - def backport_types(self): - # type: (...) -> None - """ - This examines a set of types passed to `unmarshal`, and resolves any compatibility issues with the python - version being utilized - """ - - if (str in self.types) and (native_str is not str) and (native_str not in self.types): - - self.types = tuple(chain(*( - ((type_, native_str) if (type_ is str) else (type_,)) - for type_ in self.types - ))) # type: Tuple[Union[type, properties.Property], ...] - - def as_type( - self, - type_, # type: Union[type, properties.Property] - ): - # type: (...) -> bool - - unmarshalled_data = None # type: Union[abc.model.Model, Number, str, bytes, date, datetime] - - if isinstance( - type_, - properties.Property - ): - - unmarshalled_data = type_.unmarshal(self.data) - - elif isinstance(type_, type): - - if isinstance( - self.data, - (dict, collections.OrderedDict, abc.model.Model) - ): - - if issubclass(type_, abc.model.Object): - - unmarshalled_data = type_(self.data) - - elif issubclass( - type_, - abc.model.Dictionary - ): - - unmarshalled_data = type_(self.data, value_types=self.value_types) - - elif issubclass( - type_, - (dict, collections.OrderedDict) - ): - - unmarshalled_data = serial.model.Dictionary(self.data, value_types=self.value_types) - - else: - - raise TypeError(self.data) - - elif ( - isinstance(self.data, (collections_abc.Set, collections_abc.Sequence, abc.model.Array)) and - (not isinstance(self.data, (str, bytes, native_str))) - ): - - if issubclass(type_, abc.model.Array): - - unmarshalled_data = type_(self.data, item_types=self.item_types) - - elif issubclass( - type_, - (collections_abc.Set, collections_abc.Sequence) - ) and not issubclass( - type_, - (str, bytes, native_str) - ): - - unmarshalled_data = serial.model.Array(self.data, item_types=self.item_types) - - else: - - raise TypeError('%s is not of type `%s`' % (repr(self.data), repr(type_))) - - elif isinstance(self.data, type_): - - if isinstance(self.data, Decimal): - unmarshalled_data = float(self.data) - else: - unmarshalled_data = self.data - - else: - - raise TypeError(self.data) - - return unmarshalled_data - - -def unmarshal( - data, # type: Any - types=None, # type: Optional[Union[Sequence[Union[type, properties.Property]], type, properties.Property]] - value_types=None, # type: Optional[Union[Sequence[Union[type, properties.Property]], type, properties.Property]] - item_types=None, # type: Optional[Union[Sequence[Union[type, properties.Property]], type, properties.Property]] -): - # type: (...) -> Optional[Union[abc.model., str, Number, date, datetime]] - """ - Converts `data` into an instance of a serial model, and recursively does the same for all member data. - - Parameters: - - - data ([type|serial.properties.Property]): One or more data types. Each type - - This is done by attempting to cast that data into a series of `types`. - - to "un-marshal" data which has been deserialized from bytes or text, but is still represented - by generic containers - """ - - unmarshalled_data = _Unmarshal( - data, - types=types, - value_types=value_types, - item_types=item_types - )() - - return unmarshalled_data - - -def serialize(data, format_='json'): - # type: (Union[abc.model.Model, str, Number], Optional[str]) -> str - """ - Serializes instances of `serial.model.Object` as JSON or YAML. - """ - instance_hooks = None - - if isinstance(data, abc.model.Model): - - instance_hooks = hooks.read(data) - - if (instance_hooks is not None) and (instance_hooks.before_serialize is not None): - data = instance_hooks.before_serialize(data) - - if format_ not in ('json', 'yaml'): # , 'xml' - - format_ = format_.lower() - - if format_ not in ('json', 'yaml'): - - raise ValueError( - 'Supported `serial.model.serialize()` `format_` values include "json" and "yaml" (not "%s").' % - format_ - ) - - if format_ == 'json': - data = json.dumps(marshal(data)) - elif format_ == 'yaml': - data = yaml.dump(marshal(data)) - - if (instance_hooks is not None) and (instance_hooks.after_serialize is not None): - data = instance_hooks.after_serialize(data) - - if not isinstance(data, str): - if isinstance(data, native_str): - data = str(data) - - return data - - -def deserialize(data, format_): - # type: (Optional[Union[str, IOBase, addbase]], str) -> Any - """ - Parameters: - - - data (str|io.IOBase|io.addbase): - - This can be a string or file-like object containing JSON, YAML, or XML serialized inforation. - - - format_ (str): - - This can be "json", "yaml" or "xml". - - Returns: - - A deserialized representation of the information you provided. - """ - if format_ not in ('json', 'yaml'): # , 'xml' - raise NotImplementedError( - 'Deserialization of data in the format %s is not currently supported.' % repr(format_) - ) - if not isinstance(data, (str, bytes)): - data = read(data) - if isinstance(data, bytes): - data = str(data, encoding='utf-8') - if isinstance(data, str): - if format_ == 'json': - data = json.loads( - data, - object_hook=collections.OrderedDict, - object_pairs_hook=collections.OrderedDict - ) - elif format_ == 'yaml': - data = yaml.load(data) - return data - - -def detect_format(data): - # type: (Optional[Union[str, IOBase, addbase]]) -> Tuple[Any, str] - """ - Parameters: - - - data (str|io.IOBase|io.addbase): - - This can be a string or file-like object containing JSON, YAML, or XML serialized inforation. - - Returns: - - A tuple containing the deserialized information and a string indicating the format of that information. - """ - if not isinstance(data, str): - try: - data = utilities.read(data) - except TypeError: - return data, None - formats = ('json', 'yaml') # , 'xml' - format_ = None - for potential_format in formats: - try: - data = deserialize(data, potential_format) - format_ = potential_format - break - except (ValueError, yaml.YAMLError): - pass - if format is None: - raise ValueError( - 'The data provided could not be parsed:\n' + repr(data) - ) - return data, format_ - - -def validate( - data, # type: Optional[abc.model.Model] - types=None, # type: Optional[Union[type, properties.Property, model.Object, Callable]] - raise_errors=True # type: bool -): - # type: (...) -> Sequence[str] - """ - This function verifies that all properties/items/values in an instance of `serial.abc.model.Model` are of the - correct data type(s), and that all required attributes are present (if applicable). If `raise_errors` is `True` - (this is the default)--violations will result in a validation error. If `raise_errors` is `False`--a list of error - messages will be returned if invalid/missing information is found, or an empty list otherwise. - """ - - if isinstance(data, Generator): - data = tuple(data) - - error_messages = [] - - error_message = None - - if types is not None: - - if callable(types): - types = types(data) - - if (str in types) and (native_str is not str) and (native_str not in types): - - types = tuple(chain(*( - ((type_, native_str) if (type_ is str) else (type_,)) - for type_ in types - ))) - - valid = False - - for type_ in types: - - if isinstance(type_, type) and isinstance(data, type_): - - valid = True - break - - elif isinstance(type_, properties.Property): - - if type_.types is None: - - valid = True - break - - try: - - validate(data, type_.types, raise_errors=True) - valid = True - break - - except errors.ValidationError: - - pass - - if not valid: - - error_message = ( - 'Invalid data:\n\n%s\n\nThe data must be one of the following types:\n\n%s' % ( - '\n'.join( - ' ' + line - for line in repr(data).split('\n') - ), - '\n'.join(chain( - (' (',), - ( - ' %s,' % '\n'.join( - ' ' + line - for line in repr(type_).split('\n') - ).strip() - for type_ in types - ), - (' )',) - )) - ) - ) - - if error_message is not None: - - if (not error_messages) or (error_message not in error_messages): - - error_messages.append(error_message) - - if ('_validate' in dir(data)) and callable(data._validate): - - error_messages.extend( - error_message for error_message in - data._validate(raise_errors=False) - if error_message not in error_messages - ) - - if raise_errors and error_messages: - raise errors.ValidationError('\n' + '\n\n'.join(error_messages)) - - return error_messages diff --git a/backend/venv/lib/python3.6/site-packages/serial/meta.py b/backend/venv/lib/python3.6/site-packages/serial/meta.py deleted file mode 100644 index 51e1ebd..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/meta.py +++ /dev/null @@ -1,812 +0,0 @@ -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals -from .utilities.compatibility import backport - -backport() # noqa - -from future.utils import native_str - -import numbers -import operator -import re -import collections -from collections import OrderedDict -from copy import copy, deepcopy -from itertools import chain -from numbers import Number - -try: - from typing import Optional, Dict, Sequence, Tuple, Mapping, Union, Any, List -except ImportError: - Optional = Sequence = Dict = Tuple = Mapping = Union = Any = List = None - -import serial -from serial.utilities import qualified_name, properties_values, collections_abc -from serial.abc.model import Model -from serial.abc.properties import Property - -_DOT_SYNTAX_RE = re.compile( - r'^\d+(\.\d+)*$' -) - - -class Meta(object): - - def __copy__(self): - new_instance = self.__class__() - for a in dir(self): - if a[0] != '_': - v = getattr(self, a) - if not isinstance(v, collections.Callable): - setattr(new_instance, a, v) - return new_instance - - def __deepcopy__(self, memo=None): - # type: (Optional[dict]) -> Meta - new_instance = self.__class__() - for a, v in properties_values(self): - setattr(new_instance, a, deepcopy(v, memo=memo)) - return new_instance - - def __bool__(self): - return True - - def __repr__(self): - return ('\n'.join( - ['%s(' % qualified_name(type(self))] + - [ - ' %s=%s,' % (p, repr(v)) - for p, v in properties_values(self) - ] + - [')'] - )) - - -class Version(Meta): - - def __init__( - self, - version_number=None, # type: Optional[str] - specification=None, # type: Optional[Sequence[str]] - equals=None, # type: Optional[Sequence[Union[str, Number]]] - not_equals=None, # type: Optional[Sequence[Union[str, Number]]] - less_than=None, # type: Optional[Sequence[Union[str, Number]]] - less_than_or_equal_to=None, # type: Optional[Sequence[Union[str, Number]]] - greater_than=None, # type: Optional[Sequence[Union[str, Number]]] - greater_than_or_equal_to=None, # type: Optional[Sequence[Union[str, Number]]] - ): - if isinstance(version_number, str) and ( - (specification is None) and - (equals is None) and - (not_equals is None) and - (less_than is None) and - (less_than_or_equal_to is None) and - (greater_than is None) and - (greater_than_or_equal_to is None) - ): - specification = None - for s in version_number.split('&'): - if '==' in s: - s, equals = s.split('==') - elif '<=' in s: - s, less_than_or_equal_to = s.split('<=') - elif '>=' in s: - s, greater_than_or_equal_to = s.split('>=') - elif '<' in s: - s, less_than = s.split('<') - elif '>' in s: - s, greater_than = s.split('>') - elif '!=' in s: - s, not_equals = s.split('!=') - elif '=' in s: - s, equals = s.split('=') - if specification: - if s != specification: - raise ValueError( - 'Multiple specifications cannot be associated with an instance of ' + - '`serial.meta.Version`: ' + repr(version_number) - ) - elif s: - specification = s - self.specification = specification - self.equals = equals - self.not_equals = not_equals - self.less_than = less_than - self.less_than_or_equal_to = less_than_or_equal_to - self.greater_than = greater_than - self.greater_than_or_equal_to = greater_than_or_equal_to - - def __eq__(self, other): - # type: (Any) -> bool - - compare_properties_functions = ( - ('equals', operator.eq), - ('not_equals', operator.ne), - ('less_than', operator.lt), - ('less_than_or_equal_to', operator.le), - ('greater_than', operator.gt), - ('greater_than_or_equal_to', operator.ge), - ) - - if ( - (isinstance(other, str) and _DOT_SYNTAX_RE.match(other)) or - isinstance(other, (collections_abc.Sequence, int)) - ): - if isinstance(other, (native_str, bytes, numbers.Number)): - other = str(other) - if isinstance(other, str): - - other = other.rstrip('.0') - if other == '': - other_components = (0,) - else: - other_components = tuple(int(other_component) for other_component in other.split('.')) - else: - other_components = tuple(other) - for compare_property, compare_function in compare_properties_functions: - compare_value = getattr(self, compare_property) - if compare_value is not None: - compare_values = tuple(int(n) for n in compare_value.split('.')) - other_values = copy(other_components) - ld = len(other_values) - len(compare_values) - if ld < 0: - other_values = tuple(chain(other_values, [0] * (-ld))) - elif ld > 0: - compare_values = tuple(chain(compare_values, [0] * ld)) - if not compare_function(other_values, compare_values): - return False - else: - for compare_property, compare_function in compare_properties_functions: - compare_value = getattr(self, compare_property) - if (compare_value is not None) and not compare_function(other, compare_value): - return False - return True - - def __str__(self): - representation = [] - for property, operator in ( - ('equals', '=='), - ('not_equals', '!='), - ('greater_than', '>'), - ('greater_than_or_equal_to', '>='), - ('less_than', '<'), - ('less_than_or_equal_to', '<='), - ): - v = getattr(self, property) - if v is not None: - representation.append( - self.specification + operator + v - ) - return '&'.join(representation) - - -class Object(Meta): - - def __init__( - self, - properties=None, # type: Optional[Properties] - ): - self._properties = None # type: Optional[Properties] - self.properties = properties - - @property - def properties(self): - # type: () -> Optional[Properties] - return self._properties - - @properties.setter - def properties( - self, - properties_ - # type: Optional[Union[Dict[str, Property], Sequence[Tuple[str, Property]]]] - ): - # type: (...) -> None - self._properties = Properties(properties_) - - -class Dictionary(Meta): - - def __init__( - self, - value_types=None, # type: Optional[Sequence[Property, type]] - ): - self._value_types = None # type: Optional[Tuple] - self.value_types = value_types - - @property - def value_types(self): - # type: () -> Optional[Dict[str, Union[type, Property, serial.model.Object]]] - return self._value_types - - @value_types.setter - def value_types(self, value_types): - # type: (Optional[Sequence[Union[type, Property, serial.model.Object]]]) -> None - - if value_types is not None: - - if isinstance(value_types, (type, Property)): - value_types = (value_types,) - - if native_str is not str: - - if isinstance(value_types, collections.Callable): - - _types = value_types - - def value_types(d): - # type: (Any) -> Any - - ts = _types(d) - - if (ts is not None) and (str in ts) and (native_str not in ts): - ts = tuple(chain(*( - ((t, native_str) if (t is str) else (t,)) - for t in ts - ))) - - return ts - - elif (str in value_types) and (native_str is not str) and (native_str not in value_types): - - value_types = chain(*( - ((t, native_str) if (t is str) else (t,)) - for t in value_types - )) - - if not isinstance(value_types, collections_abc.Callable): - value_types = tuple(value_types) - - self._value_types = value_types - - -class Array(Meta): - - def __init__( - self, - item_types=None, # type: Optional[Sequence[Property, type]] - ): - self._item_types = None # type: Optional[Tuple] - self.item_types = item_types - - @property - def item_types(self): - return self._item_types - - @item_types.setter - def item_types(self, item_types): - # type: (Optional[Sequence[Union[type, Property, serial.model.Object]]]) -> None - if item_types is not None: - if isinstance(item_types, (type, Property)): - item_types = (item_types,) - if native_str is not str: - if isinstance(item_types, collections.Callable): - _types = item_types - - def item_types(d): - # type: (Any) -> Any - ts = _types(d) - if (ts is not None) and (str in ts) and (native_str not in ts): - ts = tuple(chain(*( - ((t, native_str) if (t is str) else (t,)) - for t in ts - ))) - return ts - elif (str in item_types) and (native_str is not str) and (native_str not in item_types): - item_types = chain(*( - ((t, native_str) if (t is str) else (t,)) - for t in item_types - )) - if not callable(item_types): - item_types = tuple(item_types) - self._item_types = item_types - - -class Properties(OrderedDict): - - def __init__( - self, - items=( - None - ) # type: Optional[Union[Dict[str, Property], List[Tuple[str, Property]]]] - ): - if items is None: - super().__init__() - else: - if isinstance(items, OrderedDict): - items = items.items() - elif isinstance(items, dict): - items = sorted(items.items()) - super().__init__(items) - - def __setitem__(self, key, value): - # type: (str, Property) -> None - if not isinstance(value, Property): - raise ValueError(value) - super().__setitem__(key, value) - - def __copy__(self): - # type: () -> Properties - return self.__class__(self) - - def __deepcopy__(self, memo=None): - # type: (dict) -> Properties - return self.__class__( - tuple( - (k, deepcopy(v, memo=memo)) - for k, v in self.items() - ) - ) - - def __repr__(self): - representation = [ - qualified_name(type(self)) + '(' - ] - items = tuple(self.items()) - if len(items) > 0: - representation[0] += '[' - for k, v in items: - rv = ( - qualified_name(v) if isinstance(v, type) else - repr(v) - ) - rvls = rv.split('\n') - if len(rvls) > 1: - rvs = [rvls[0]] - for rvl in rvls[1:]: - rvs.append(' ' + rvl) - rv = '\n'.join(rvs) - representation += [ - ' (', - ' %s,' % repr(k), - ' %s' % rv, - ' ),' - ] - else: - representation.append( - ' (%s, %s),' % (repr(k), rv) - ) - representation[-1] = representation[-1][:-1] - representation.append( - ']' - ) - representation[-1] += ')' - if len(representation) > 2: - return '\n'.join(representation) - else: - return ''.join(representation) - - -def read( - model # type: Union[type, serial.abc.model.Model] -): - # type: (...) -> Optional[Meta] - - if isinstance( - model, - Model - ): - - return model._meta or read(type(model)) - - elif isinstance(model, type) and issubclass(model, Model): - - return model._meta - - else: - - try: - - repr_model = repr(model) - - except: - - repr_model = object.__repr__(model) - - raise TypeError( - '%s requires a parameter which is an instance or sub-class of `%s`, not%s' % ( - serial.utilities.calling_function_qualified_name(), - qualified_name(Model), - ( - ':\n' + repr_model - if '\n' in repr_model else - ' `%s`' % repr_model - ) - ) - ) - - -def writable( - model # type: Union[type, serial.abc.model.Model] -): - # type: (...) -> Optional[Meta] - if isinstance(model, Model): - if model._meta is None: - model._meta = deepcopy(writable(type(model))) - elif isinstance(model, type) and issubclass(model, Model): - if model._meta is None: - model._meta = ( - Object() - if issubclass(model, serial.model.Object) else - Array() - if issubclass(model, serial.model.Array) else - Dictionary() - if issubclass(model, serial.model.Dictionary) - else None - ) - else: - for b in model.__bases__: - if hasattr(b, '_meta') and (model._meta is b._meta): - model._meta = deepcopy(model._meta) - break - else: - repr_model = repr(model) - raise TypeError( - '%s requires a parameter which is an instance or sub-class of `%s`, not%s' % ( - serial.utilities.calling_function_qualified_name(), - qualified_name(Model), - ( - ':\n' + repr_model - if '\n' in repr_model else - ' `%s`' % repr_model - ) - ) - ) - return model._meta - - -def write( - model, # type: Union[type, serial.model.Object] - meta # type: Meta -): - # type: (...) -> None - if isinstance(model, Model): - model_type = type(model) - elif isinstance(model, type) and issubclass(model, Model): - model_type = model - else: - repr_model = repr(model) - raise TypeError( - '%s requires a value for the parameter `model` which is an instance or sub-class of `%s`, not%s' % ( - serial.utilities.calling_function_qualified_name(), - qualified_name(Model), - ( - ':\n' + repr_model - if '\n' in repr_model else - ' `%s`' % repr_model - ) - ) - ) - metadata_type = ( - Object - if issubclass(model_type, serial.model.Object) else - Array - if issubclass(model_type, serial.model.Array) else - Dictionary - if issubclass(model_type, serial.model.Dictionary) - else None - ) - if not isinstance(meta, metadata_type): - raise ValueError( - 'Metadata assigned to `%s` must be of type `%s`' % ( - qualified_name(model_type), - qualified_name(metadata_type) - ) - ) - model._meta = meta - - -_UNIDENTIFIED = None - - -def xpath(model, xpath_=_UNIDENTIFIED): - # type: (serial.abc.model.Model, Optional[str]) -> Optional[str] - """ - Return the xpath at which the element represented by this object was found, relative to the root document. If - the parameter `xpath_` is provided--set the value - """ - - if not isinstance(model, Model): - - raise TypeError( - '`model` must be an instance of `%s`, not %s.' % (qualified_name(Model), repr(model)) - ) - - if xpath_ is not _UNIDENTIFIED: - - if not isinstance(xpath_, str): - - if isinstance(xpath_, native_str): - xpath_ = str(xpath_) - else: - raise TypeError( - '`xpath_` must be a `str`, not %s.' % repr(xpath_) - ) - - model._xpath = xpath_ - - if isinstance(model, serial.model.Dictionary): - for k, v in model.items(): - if isinstance(v, Model): - xpath(v, '%s/%s' % (xpath_, k)) - elif isinstance(model, serial.model.Object): - for pn, p in read(model).properties.items(): - k = p.name or pn - v = getattr(model, pn) - if isinstance(v, Model): - xpath(v, '%s/%s' % (xpath_, k)) - elif isinstance(model, serial.model.Array): - for i in range(len(model)): - v = model[i] - if isinstance(v, Model): - xpath(v, '%s[%s]' % (xpath_, str(i))) - return model._xpath - - -def pointer(model, pointer_=_UNIDENTIFIED): - # type: (serial.abc.model.Model, Optional[str]) -> Optional[str] - - if not isinstance(model, Model): - raise TypeError( - '`model` must be an instance of `%s`, not %s.' % (qualified_name(Model), repr(model)) - ) - - if pointer_ is not _UNIDENTIFIED: - - if not isinstance(pointer_, (str, native_str)): - raise TypeError( - '`pointer_` must be a `str`, not %s (of type `%s`).' % (repr(pointer_), type(pointer_).__name__) - ) - - model._pointer = pointer_ - - if isinstance(model, serial.model.Dictionary): - - for k, v in model.items(): - if isinstance(v, Model): - pointer(v, '%s/%s' % (pointer_, k.replace('~', '~0').replace('/', '~1'))) - - elif isinstance(model, serial.model.Object): - - for pn, property in read(model).properties.items(): - - k = property.name or pn - v = getattr(model, pn) - - if isinstance(v, Model): - pointer(v, '%s/%s' % (pointer_, k.replace('~', '~0').replace('/', '~1'))) - - elif isinstance(model, serial.model.Array): - - for i in range(len(model)): - - v = model[i] - - if isinstance(v, Model): - pointer(v, '%s[%s]' % (pointer_, str(i))) - - return model._pointer - - -def url(model, url_=_UNIDENTIFIED): - # type: (serial.abc.model.Model, Optional[str]) -> Optional[str] - if not isinstance(model, serial.abc.model.Model): - raise TypeError( - '`model` must be an instance of `%s`, not %s.' % (qualified_name(Model), repr(model)) - ) - if url_ is not _UNIDENTIFIED: - if not isinstance(url_, str): - raise TypeError( - '`url_` must be a `str`, not %s.' % repr(url_) - ) - model._url = url_ - if isinstance(model, serial.model.Dictionary): - for v in model.values(): - if isinstance(v, Model): - url(v, url_) - elif isinstance(model, serial.model.Object): - for pn in read(model).properties.keys(): - v = getattr(model, pn) - if isinstance(v, Model): - url(v, url_) - elif isinstance(model, serial.model.Array): - for v in model: - if isinstance(v, Model): - url(v, url_) - return model._url - - -def format_(model, serialization_format=_UNIDENTIFIED): - - # type: (serial.abc.model.Model, Optional[str]) -> Optional[str] - if not isinstance(model, Model): - - raise TypeError( - '`model` must be an instance of `%s`, not %s.' % (qualified_name(Model), repr(model)) - ) - - if serialization_format is not _UNIDENTIFIED: - - if not isinstance(serialization_format, str): - - if isinstance(serialization_format, native_str): - serialization_format = str(serialization_format) - else: - raise TypeError( - '`serialization_format` must be a `str`, not %s.' % repr(serialization_format) - ) - - model._format = serialization_format - - if isinstance(model, serial.model.Dictionary): - for v in model.values(): - if isinstance(v, Model): - format_(v, serialization_format) - elif isinstance(model, serial.model.Object): - for pn in read(model).properties.keys(): - v = getattr(model, pn) - if isinstance(v, Model): - format_(v, serialization_format) - elif isinstance(model, serial.model.Array): - for v in model: - if isinstance(v, Model): - format_(v, serialization_format) - - return model._format - - -def version(data, specification, version_number): - # type: (serial.abc.model.Model, str, Union[str, int, Sequence[int]]) -> None - """ - Recursively alters model class or instance metadata based on version number metadata associated with an - object's properties. This allows one data model to represent multiple versions of a specification and dynamically - change based on the version of a specification represented. - - Arguments: - - - data (serial.abc.model.Model) - - - specification (str): - - The specification to which the `version_number` argument applies. - - - version_number (str|int|[int]): - - A version number represented as text (in the form of integers separated by periods), an integer, or a - sequence of integers. - """ - if not isinstance(data, serial.abc.model.Model): - raise TypeError( - 'The data provided is not an instance of serial.abc.model.Model: ' + repr(data) - ) - - def version_match(property_): - # type: (serial.properties.Property) -> bool - if property_.versions is not None: - version_matched = False - specification_matched = False - for applicable_version in property_.versions: - if applicable_version.specification == specification: - specification_matched = True - if applicable_version == version_number: - version_matched = True - break - if specification_matched and (not version_matched): - return False - return True - - def version_properties(properties_): - # type: (Sequence[serial.properties.Property]) -> Optional[Sequence[Meta]] - changed = False - nps = [] - - for property in properties_: - - if isinstance(property, serial.properties.Property): - if version_match(property): - np = version_property(property) - if np is not property: - changed = True - nps.append(np) - else: - changed = True - else: - nps.append(property) - if changed: - return tuple(nps) - else: - return None - - def version_property(property): - # type: (serial.properties.Property) -> Meta - changed = False - if isinstance(property, serial.properties.Array) and (property.item_types is not None): - item_types = version_properties(property.item_types) - if item_types is not None: - if not changed: - property = deepcopy(property) - property.item_types = item_types - changed = True - elif isinstance(property, serial.properties.Dictionary) and (property.value_types is not None): - value_types = version_properties(property.value_types) - if value_types is not None: - if not changed: - property = deepcopy(property) - property.value_types = value_types - changed = True - if property.types is not None: - types = version_properties(property.types) - if types is not None: - if not changed: - property = deepcopy(property) - property.types = types - return property - - instance_meta = read(data) - class_meta = read(type(data)) - - if isinstance(data, serial.abc.model.Object): - - for property_name in tuple(instance_meta.properties.keys()): - - property = instance_meta.properties[property_name] - - if version_match(property): - np = version_property(property) - if np is not property: - if instance_meta is class_meta: - instance_meta = writable(data) - instance_meta.properties[property_name] = np - else: - if instance_meta is class_meta: - instance_meta = writable(data) - del instance_meta.properties[property_name] - version_ = getattr(data, property_name) - if version_ is not None: - raise serial.errors.VersionError( - '%s - the property `%s` is not applicable in %s version %s:\n%s' % ( - qualified_name(type(data)), - property_name, - specification, - version_number, - str(data) - ) - ) - - value = getattr(data, property_name) - - if isinstance(value, serial.abc.model.Model): - version(value, specification, version_number) - - elif isinstance(data, serial.abc.model.Dictionary): - - if instance_meta and instance_meta.value_types: - - new_value_types = version_properties(instance_meta.value_types) - - if new_value_types: - - if instance_meta is class_meta: - instance_meta = writable(data) - - instance_meta.value_types = new_value_types - - for value in data.values(): - if isinstance(value, serial.abc.model.Model): - version(value, specification, version_number) - - elif isinstance(data, serial.abc.model.Array): - - if instance_meta and instance_meta.item_types: - - new_item_types = version_properties(instance_meta.item_types) - - if new_item_types: - - if instance_meta is class_meta: - instance_meta = writable(data) - - instance_meta.item_types = new_item_types - - for item in data: - if isinstance(item, serial.abc.model.Model): - version(item, specification, version_number) diff --git a/backend/venv/lib/python3.6/site-packages/serial/model.py b/backend/venv/lib/python3.6/site-packages/serial/model.py deleted file mode 100644 index 4b7e8e1..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/model.py +++ /dev/null @@ -1,1306 +0,0 @@ -""" -This module defines the building blocks of a `serial` based data model. -""" - -# Tell the linters what's up: -# pylint:disable=wrong-import-position,consider-using-enumerate,useless-object-inheritance -# mccabe:options:max-complexity=999 -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals - -from .utilities.compatibility import backport, BACKWARDS_COMPATIBILITY_IMPORTS - -backport() # noqa - -from future.utils import native_str - -# region Built-In Imports - -import re -import sys - -from urllib.parse import urljoin - -from copy import deepcopy -from io import IOBase -from itertools import chain -from numbers import Number - -# endregion - -# region 3rd-Party Maintained Package Imports - -# endregion - -# region Serial Imports - -from .utilities import qualified_name, collections, Generator -from . import properties, meta, errors, hooks, abc -from .marshal import marshal, unmarshal, serialize, detect_format, validate, UNMARSHALLABLE_TYPES - - -# endregion - - -# region Compatibility Conditionals - -# The following detects the presence of the typing library, and utilizes typing classes if possible. -# All typing classes in this package are referenced in a backwards-compatible fashion, so if this library -# is not present, the package will still function. - -try: - from typing import Union, Dict, Any, AnyStr, IO, Sequence, Mapping, Callable, Tuple, Optional, Set # noqa -except ImportError: - Union = Dict = Any = AnyStr = IO = Sequence = Mapping = Callable = Tuple = Optional = Set = None - -# endregion - - -class Object(object): - - _format = None # type: Optional[str] - _meta = None # type: Optional[meta.Object] - _hooks = None # type: Optional[hooks.Object] - - def __init__( - self, - _=None, # type: Optional[Union[str, bytes, dict, Sequence, IO]] - ): - - self._meta = None # type: Optional[meta.Object] - self._hooks = None # type: Optional[hooks.Object] - self._url = None # type: Optional[str] - self._xpath = None # type: Optional[str] - self._pointer = None # type: Optional[str] - - url = None - - if _ is not None: - - if isinstance(_, Object): - - instance_meta = meta.read(_) - - if meta.read(self) is not instance_meta: - meta.write(self, deepcopy(instance_meta)) - - instance_hooks = hooks.read(_) - - if hooks.read(self) is not instance_hooks: - hooks.write(self, deepcopy(instance_hooks)) - - for property_name in instance_meta.properties.keys(): - - try: - - setattr(self, property_name, getattr(_, property_name)) - - except TypeError as error: - - label = '\n - %s.%s: ' % (qualified_name(type(self)), property_name) - - if error.args: - error.args = tuple( - chain( - (label + error.args[0],), - error.args[1:] - ) - ) - else: - error.args = (label + serialize(_),) - raise error - else: - - if isinstance(_, IOBase): - - if hasattr(_, 'url'): - url = _.url - elif hasattr(_, 'name'): - url = urljoin('file:', _.name) - - _, format_ = detect_format(_) - - if isinstance(_, dict): - - for property_name, value in _.items(): - - if value is None: - value = properties.NULL - - try: - - self[property_name] = value - - except KeyError as error: - - if error.args and len(error.args) == 1: - - error.args = ( - r'%s.%s: %s' % (qualified_name(type(self)), error.args[0], repr(_)), - ) - - raise error - - else: - if format_ is None: - _dir = tuple(property_name for property_name in dir(_) if property_name[0] != '_') - for property_name in meta.writable(self.__class__).properties.keys(): - if property_name in _dir: - setattr(self, getattr(_, property_name)) - else: - raise TypeError( - 'The `_` parameter must be a string, file-like object, or dictionary, not `%s`' % - repr(_) - ) - if format_ is not None: - meta.format_(self, format_) - if url is not None: - meta.url(self, url) - if meta.pointer(self) is None: - meta.pointer(self, '#') - if meta.xpath(self) is None: - meta.xpath(self, '') - - def __hash__(self): - return id(self) - - def __setattr__(self, property_name, value): - # type: (Object, str, Any) -> None - - instance_hooks = None - - unmarshalled_value = value - - if property_name[0] != '_': - - instance_hooks = hooks.read(self) # type: hooks.Object - - if instance_hooks and instance_hooks.before_setattr: - property_name, value = instance_hooks.before_setattr(self, property_name, value) - - try: - - property_definition = meta.read(self).properties[property_name] - - except KeyError: - - raise KeyError( - '`%s` has no attribute "%s".' % ( - qualified_name(type(self)), - property_name - ) - ) - - if value is not None: - - if isinstance(value, Generator): - value = tuple(value) - - try: - - unmarshalled_value = property_definition.unmarshal(value) - - except (TypeError, ValueError) as error: - - message = '\n - %s.%s: ' % ( - qualified_name(type(self)), - property_name - ) - - if error.args and isinstance(error.args[0], str): - - error.args = tuple( - chain( - (message + error.args[0],), - error.args[1:] - ) - ) - - else: - - error.args = (message + repr(value),) - - raise error - - super().__setattr__(property_name, unmarshalled_value) - - if instance_hooks and instance_hooks.after_setattr: - instance_hooks.after_setattr(self, property_name, value) - - def __setitem__(self, key, value): - # type: (str, Any) -> None - - instance_hooks = hooks.read(self) # type: hooks.Object - - if instance_hooks and instance_hooks.before_setitem: - key, value = instance_hooks.before_setitem(self, key, value) - - instance_meta = meta.read(self) - - if key in instance_meta.properties: - - property_name = key - - else: - - property_name = None - - for potential_property_name, property in instance_meta.properties.items(): - if key == property.name: - property_name = potential_property_name - break - - if property_name is None: - raise KeyError( - '`%s` has no property mapped to the name "%s"' % ( - qualified_name(type(self)), - key - ) - ) - - setattr(self, property_name, value) - - if instance_hooks and instance_hooks.after_setitem: - instance_hooks.after_setitem(self, key, value) - - def __delattr__(self, key): - # type: (str) -> None - - instance_meta = meta.read(self) - - if key in instance_meta.properties: - setattr(self, key, None) - else: - super().__delattr__(key) - - def __getitem__(self, key): - # type: (str, Any) -> None - - instance_meta = meta.read(self) - - if key in instance_meta.properties: - - property_name = key - - else: - - property_definition = None - property_name = None - - for pn, pd in instance_meta.properties.items(): - if key == pd.name: - property_name = pn - property_definition = pd - break - - if property_definition is None: - raise KeyError( - '`%s` has no property mapped to the name "%s"' % ( - qualified_name(type(self)), - key - ) - ) - - return getattr(self, property_name) - - def __copy__(self): - # type: () -> Object - return self.__class__(self) - - def __deepcopy__(self, memo): - # type: (Optional[dict]) -> Object - - new_instance = self.__class__() - - instance_meta = meta.read(self) - class_meta = meta.read(type(self)) - - if instance_meta is class_meta: - meta_ = class_meta # type: meta.Object - else: - meta.write(new_instance, deepcopy(instance_meta, memo)) - meta_ = instance_meta # type: meta.Object - - instance_hooks = hooks.read(self) - class_hooks = hooks.read(type(self)) - - if instance_hooks is not class_hooks: - hooks.write(new_instance, deepcopy(instance_hooks, memo)) - - if meta_ is not None: - - for property_name in meta_.properties.keys(): - - try: - - value = getattr(self, property_name) - - if isinstance(value, Generator): - value = tuple(value) - - if value is not None: - - if not callable(value): - value = deepcopy(value, memo) - - setattr(new_instance, property_name, value) - - except TypeError as error: - - label = '%s.%s: ' % (qualified_name(type(self)), property_name) - - if error.args: - error.args = tuple( - chain( - (label + error.args[0],), - error.args[1:] - ) - ) - else: - error.args = (label + serialize(self),) - - raise error - - return new_instance - - def _marshal(self): - # type: (...) -> collections.OrderedDict - object_ = self - instance_hooks = hooks.read(object_) - if (instance_hooks is not None) and (instance_hooks.before_marshal is not None): - object_ = instance_hooks.before_marshal(object_) - data = collections.OrderedDict() - instance_meta = meta.read(object_) - for property_name, property in instance_meta.properties.items(): - value = getattr(object_, property_name) - if value is not None: - key = property.name or property_name - data[key] = property.marshal(value) - if (instance_hooks is not None) and (instance_hooks.after_marshal is not None): - data = instance_hooks.after_marshal(data) - return data - - def __str__(self): - # type: (...) -> str - return serialize(self) - - def __repr__(self): - # type: (...) -> str - representation = [ - '%s(' % qualified_name(type(self)) - ] - instance_meta = meta.read(self) - for property_name in instance_meta.properties.keys(): - value = getattr(self, property_name) - if value is not None: - repr_value = ( - qualified_name(value) - if isinstance(value, type) else - repr(value) - ) - repr_value_lines = repr_value.split('\n') - if len(repr_value_lines) > 2: - rvs = [repr_value_lines[0]] - for rvl in repr_value_lines[1:]: - rvs.append(' ' + rvl) - repr_value = '\n'.join(rvs) - representation.append( - ' %s=%s,' % (property_name, repr_value) - ) - representation.append(')') - if len(representation) > 2: - return '\n'.join(representation) - else: - return ''.join(representation) - - def __eq__(self, other): - # type: (Any) -> bool - if type(self) is not type(other): - return False - instance_meta = meta.read(self) - om = meta.read(other) - self_properties = set(instance_meta.properties.keys()) - other_properties = set(om.properties.keys()) - if self_properties != other_properties: - return False - for property_name in (self_properties & other_properties): - value = getattr(self, property_name) - ov = getattr(other, property_name) - if value != ov: - return False - return True - - def __ne__(self, other): - # type: (Any) -> bool - return False if self == other else True - - def __iter__(self): - instance_meta = meta.read(self) - for property_name, property in instance_meta.properties.items(): - yield property.name or property_name - - def _validate(self, raise_errors=True): - # type: (bool) -> None - - validation_errors = [] - object_ = self - - instance_hooks = hooks.read(self) - - if (instance_hooks is not None) and (instance_hooks.before_validate is not None): - object_ = instance_hooks.before_validate(object_) - - instance_meta = meta.read(object_) - - for property_name, property in instance_meta.properties.items(): - - value = getattr(object_, property_name) - - if value is None: - - if callable(property.required): - required = property.required(object_) - else: - required = property.required - - if required: - - validation_errors.append( - 'The property `%s` is required for `%s`:\n%s' % ( - property_name, - qualified_name(type(object_)), - str(object_) - ) - ) - else: - - if value is properties.NULL: - - types = property.types - - if callable(types): - types = types(value) - - if types is not None: - - if (str in types) and (native_str is not str) and (native_str not in types): - types = tuple(chain(*( - ((type_, native_str) if (type_ is str) else (type_,)) - for type_ in types - ))) - - if properties.Null not in types: - - validation_errors.append( - 'Null values are not allowed in `%s.%s`, ' % ( - qualified_name(type(object_)), property_name - ) + - 'permitted types include: %s.' % ', '.join( - '`%s`' % qualified_name(type_) for type_ in types - ) - ) - else: - - try: - value_validation_error_messages = validate(value, property.types, raise_errors=False) - - if value_validation_error_messages: - - index = 0 - - for error_message in value_validation_error_messages: - value_validation_error_messages[index] = ( - 'Error encountered ' + - 'while attempting to validate property `%s`:\n\n' % property_name + - error_message - ) - - validation_errors.extend(value_validation_error_messages) - - except errors.ValidationError as error: - - message = '%s.%s:\n' % (qualified_name(type(object_)), property_name) - - if error.args: - error.args = tuple(chain( - (error.args[0] + message,), - error.args[1:] - )) - else: - error.args = ( - message, - ) - - if (instance_hooks is not None) and (instance_hooks.after_validate is not None): - instance_hooks.after_validate(object_) - if raise_errors and validation_errors: - raise errors.ValidationError('\n'.join(validation_errors)) - return validation_errors - - -abc.model.Object.register(Object) - - -class Array(list): - - _format = None # type: Optional[str] - _hooks = None # type: Optional[hooks.Array] - _meta = None # type: Optional[meta.Array] - - def __init__( - self, - items=None, # type: Optional[Union[Sequence, Set]] - item_types=( - None - ), # type: Optional[Union[Sequence[Union[type, properties.Property]], type, properties.Property]] - ): - self._meta = None # type: Optional[meta.Array] - self._hooks = None # type: Optional[hooks.Array] - self._url = None # type: Optional[str] - self._xpath = None # type: Optional[str] - self._pointer = None # type: Optional[str] - url = None - if isinstance(items, IOBase): - if hasattr(items, 'url'): - url = items.url - elif hasattr(items, 'name'): - url = urljoin('file:', items.name) - items, format_ = detect_format(items) - if item_types is None: - if isinstance(items, Array): - m = meta.read(items) - if meta.read(self) is not m: - meta.write(self, deepcopy(m)) - else: - meta.writable(self).item_types = item_types - if items is not None: - for item in items: - self.append(item) - if meta.pointer(self) is None: - meta.pointer(self, '#') - if meta.xpath(self) is None: - meta.xpath(self, '') - if url is not None: - meta.url(self, url) - if format_ is not None: - meta.format_(self, format_) - - def __hash__(self): - return id(self) - - def __setitem__( - self, - index, # type: int - value, # type: Any - ): - instance_hooks = hooks.read(self) # type: hooks.Object - - if instance_hooks and instance_hooks.before_setitem: - index, value = instance_hooks.before_setitem(self, index, value) - - m = meta.read(self) # type: Optional[meta.Array] - - if m is None: - item_types = None - else: - item_types = m.item_types - - value = unmarshal(value, types=item_types) - super().__setitem__(index, value) - - if instance_hooks and instance_hooks.after_setitem: - instance_hooks.after_setitem(self, index, value) - - def append(self, value): - # type: (Any) -> None - if not isinstance(value, UNMARSHALLABLE_TYPES): - raise errors.UnmarshalTypeError(value) - - instance_hooks = hooks.read(self) # type: hooks.Array - - if instance_hooks and instance_hooks.before_append: - value = instance_hooks.before_append(self, value) - - instance_meta = meta.read(self) # type: Optional[meta.Array] - - if instance_meta is None: - item_types = None - else: - item_types = instance_meta.item_types - - value = unmarshal(value, types=item_types) - - super().append(value) - - if instance_hooks and instance_hooks.after_append: - instance_hooks.after_append(self, value) - - def __copy__(self): - # type: () -> Array - return self.__class__(self) - - def __deepcopy__(self, memo=None): - # type: (Optional[dict]) -> Array - new_instance = self.__class__() - im = meta.read(self) - cm = meta.read(type(self)) - if im is not cm: - meta.write(new_instance, deepcopy(im, memo=memo)) - ih = hooks.read(self) - ch = hooks.read(type(self)) - if ih is not ch: - hooks.write(new_instance, deepcopy(ih, memo=memo)) - for i in self: - new_instance.append(deepcopy(i, memo=memo)) - return new_instance - - def _marshal(self): - a = self - h = hooks.read(a) - if (h is not None) and (h.before_marshal is not None): - a = h.before_marshal(a) - m = meta.read(a) - a = tuple( - marshal( - i, - types=None if m is None else m.item_types - ) for i in a - ) - if (h is not None) and (h.after_marshal is not None): - a = h.after_marshal(a) - return a - - def _validate( - self, - raise_errors=True - ): - # type: (bool) -> None - validation_errors = [] - a = self - h = hooks.read(a) - - if (h is not None) and (h.before_validate is not None): - a = h.before_validate(a) - - m = meta.read(a) - - if m.item_types is not None: - - for i in a: - - validation_errors.extend(validate(i, m.item_types, raise_errors=False)) - - if (h is not None) and (h.after_validate is not None): - h.after_validate(a) - - if raise_errors and validation_errors: - raise errors.ValidationError('\n'.join(validation_errors)) - - return validation_errors - - def __repr__(self): - representation = [ - qualified_name(type(self)) + '(' - ] - if len(self) > 0: - representation.append(' [') - for i in self: - ri = ( - qualified_name(i) if isinstance(i, type) else - repr(i) - ) - rils = ri.split('\n') - if len(rils) > 1: - ris = [rils[0]] - ris += [ - ' ' + rvl - for rvl in rils[1:] - ] - ri = '\n'.join(ris) - representation.append( - ' %s,' % ri - ) - im = meta.read(self) - cm = meta.read(type(self)) - m = None if (im is cm) else im # type: Optional[meta.Array] - representation.append( - ' ]' + ('' - if m is None or m.item_types is None - else ',') - ) - im = meta.read(self) - cm = meta.read(type(self)) - if im is not cm: - if im.item_types: - representation.append( - ' item_types=(', - ) - for it in im.item_types: - ri = ( - qualified_name(it) if isinstance(it, type) else - repr(it) - ) - rils = ri.split('\n') - if len(rils) > 2: - ris = [rils[0]] - ris += [ - ' ' + rvl - for rvl in rils[1:-1] - ] - ris.append(' ' + rils[-1]) - ri = '\n'.join(ris) - representation.append(' %s,' % ri) - m = meta.read(self) - if len(m.item_types) > 1: - representation[-1] = representation[-1][:-1] - representation.append(' )') - representation.append(')') - if len(representation) > 2: - return '\n'.join(representation) - else: - return ''.join(representation) - - def __eq__(self, other): - # type: (Any) -> bool - if type(self) is not type(other): - return False - length = len(self) - if length != len(other): - return False - for i in range(length): - if self[i] != other[i]: - return False - return True - - def __ne__(self, other): - # type: (Any) -> bool - if self == other: - return False - else: - return True - - def __str__(self): - return serialize(self) - - -abc.model.Array.register(Array) - - -class Dictionary(collections.OrderedDict): - - _format = None # type: Optional[str] - _hooks = None # type: Optional[hooks.Dictionary] - _meta = None # type: Optional[meta.Dictionary] - - def __init__( - self, - items=None, # type: Optional[Mapping] - value_types=( - None - ), # type: Optional[Union[Sequence[Union[type, properties.Property]], type, properties.Property]] - ): - self._meta = None # type: Optional[meta.Dictionary] - self._hooks = None # type: Optional[hooks.Dictionary] - self._url = None # type: Optional[str] - self._xpath = None # type: Optional[str] - self._pointer = None # type: Optional[str] - - url = None - - if isinstance(items, IOBase): - - if hasattr(items, 'url'): - url = items.url - elif hasattr(items, 'name'): - url = urljoin('file:', items.name) - - items, format_ = detect_format(items) - - if value_types is None: - - if isinstance(items, Dictionary): - - m = meta.read(items) - - if meta.read(self) is not m: - meta.write(self, deepcopy(m)) - else: - - meta.writable(self).value_types = value_types - - if items is None: - - super().__init__() - - else: - - if isinstance(items, (collections.OrderedDict, Dictionary)): - items = items.items() - elif isinstance(items, dict): - items = sorted(items.items(), key=lambda kv: kv) - - super().__init__(items) - - if meta.pointer(self) is None: - meta.pointer(self, '#') - - if meta.xpath(self) is None: - meta.xpath(self, '') - - if url is not None: - meta.url(self, url) - - if format_ is not None: - meta.format_(self, format_) - - def __hash__(self): - return id(self) - - def __setitem__( - self, - key, # type: int - value # type: Any - ): - instance_hooks = hooks.read(self) # type: hooks.Dictionary - - if instance_hooks and instance_hooks.before_setitem: - key, value = instance_hooks.before_setitem(self, key, value) - - instance_meta = meta.read(self) # type: Optional[meta.Dictionary] - - if instance_meta is None: - value_types = None - else: - value_types = instance_meta.value_types - - try: - - unmarshalled_value = unmarshal( - value, - types=value_types - ) - - except TypeError as error: - - message = "\n - %s['%s']: " % ( - qualified_name(type(self)), - key - ) - - if error.args and isinstance(error.args[0], str): - - error.args = tuple( - chain( - (message + error.args[0],), - error.args[1:] - ) - ) - - else: - - error.args = (message + repr(value),) - - raise error - - if value is None: - raise RuntimeError(key) - - super().__setitem__( - key, - unmarshalled_value - ) - - if instance_hooks and instance_hooks.after_setitem: - instance_hooks.after_setitem(self, key, unmarshalled_value) - - def __copy__(self): - # type: (Dictionary) -> Dictionary - new_instance = self.__class__() - im = meta.read(self) - cm = meta.read(type(self)) - if im is not cm: - meta.write(new_instance, im) - ih = hooks.read(self) - ch = hooks.read(type(self)) - if ih is not ch: - hooks.write(new_instance, ih) - for k, v in self.items(): - new_instance[k] = v - return new_instance - - def __deepcopy__(self, memo=None): - # type: (dict) -> Dictionary - new_instance = self.__class__() - im = meta.read(self) - cm = meta.read(type(self)) - if im is not cm: - meta.write(new_instance, deepcopy(im, memo=memo)) - ih = hooks.read(self) - ch = hooks.read(type(self)) - if ih is not ch: - hooks.write(new_instance, deepcopy(ih, memo=memo)) - for k, v in self.items(): - new_instance[k] = deepcopy(v, memo=memo) - return new_instance - - def _marshal(self): - """ - This method marshals an instance of `Dictionary` as built-in type `OrderedDict` which can be serialized into - JSON/YAML/XML. - """ - - # This variable is needed because before-marshal hooks are permitted to return altered *copies* of `self`, so - # prior to marshalling--this variable may no longer point to `self` - data = self # type: Union[Dictionary, collections.OrderedDict] - - # Check for hooks - instance_hooks = hooks.read(data) - - # Execute before-marshal hooks, if applicable - if (instance_hooks is not None) and (instance_hooks.before_marshal is not None): - data = instance_hooks.before_marshal(data) - - # Get the metadata, if any has been assigned - instance_meta = meta.read(data) # type: Optional[meta.Dictionary] - - # Check to see if value types are defined in the metadata - if instance_meta is None: - value_types = None - else: - value_types = instance_meta.value_types - - # Recursively convert the data to generic, serializable, data types - unmarshalled_data = collections.OrderedDict( - [ - ( - k, - marshal(v, types=value_types) - ) for k, v in data.items() - ] - ) # type: collections.OrderedDict - - # Execute after-marshal hooks, if applicable - if (instance_hooks is not None) and (instance_hooks.after_marshal is not None): - unmarshalled_data = instance_hooks.after_marshal(unmarshalled_data) - - return unmarshalled_data - - def _validate(self, raise_errors=True): - # type: (Callable) -> None - """ - Recursively validate - """ - - validation_errors = [] - d = self - h = d._hooks or type(d)._hooks - - if (h is not None) and (h.before_validate is not None): - d = h.before_validate(d) - - m = meta.read(d) # type: Optional[meta.Dictionary] - - if m is None: - value_types = None - else: - value_types = m.value_types - - if value_types is not None: - - for k, v in d.items(): - - value_validation_errors = validate(v, value_types, raise_errors=False)\ - - validation_errors.extend(value_validation_errors) - - if (h is not None) and (h.after_validate is not None): - h.after_validate(d) - - if raise_errors and validation_errors: - raise errors.ValidationError('\n'.join(validation_errors)) - - return validation_errors - - def __repr__(self): - representation = [ - qualified_name(type(self)) + '(' - ] - items = tuple(self.items()) - if len(items) > 0: - representation.append(' [') - for k, v in items: - rv = ( - qualified_name(v) if isinstance(v, type) else - repr(v) - ) - rvls = rv.split('\n') - if len(rvls) > 1: - rvs = [rvls[0]] - for rvl in rvls[1:]: - rvs.append(' ' + rvl) - # rvs.append(' ' + rvs[-1]) - rv = '\n'.join(rvs) - representation += [ - ' (', - ' %s,' % repr(k), - ' %s' % rv, - ' ),' - ] - else: - representation.append( - ' (%s, %s),' % (repr(k), rv) - ) - representation[-1] = representation[-1][:-1] - representation.append( - ' ]' - if self._meta is None or self._meta.value_types is None else - ' ],' - ) - cm = meta.read(type(self)) - im = meta.read(self) - if cm is not im: - if self._meta.value_types: - representation.append( - ' value_types=(', - ) - for vt in im.value_types: - rv = ( - qualified_name(vt) if isinstance(vt, type) else - repr(vt) - ) - rvls = rv.split('\n') - if len(rvls) > 1: - rvs = [rvls[0]] - rvs += [ - ' ' + rvl - for rvl in rvls[1:] - ] - rv = '\n'.join(rvs) - representation.append(' %s,' % rv) - if len(self._meta.value_types) > 1: - representation[-1] = representation[-1][:-1] - representation.append(' )') - representation.append(')') - if len(representation) > 2: - return '\n'.join(representation) - else: - return ''.join(representation) - - def __eq__(self, other): - # type: (Any) -> bool - if type(self) is not type(other): - return False - keys = tuple(self.keys()) - other_keys = tuple(other.keys()) - if keys != other_keys: - return False - for k in keys: - if self[k] != other[k]: - return False - return True - - def __ne__(self, other): - # type: (Any) -> bool - if self == other: - return False - else: - return True - - def __str__(self): - return serialize(self) - - -abc.model.Dictionary.register(Dictionary) - - -def from_meta(name, metadata, module=None, docstring=None): - # type: (meta.Meta, str, Optional[str]) -> type - """ - Constructs an `Object`, `Array`, or `Dictionary` sub-class from an instance of `serial.meta.Meta`. - - Arguments: - - - name (str): The name of the class. - - - class_meta (serial.meta.Meta) - - - module (str): Specify the value for the class definition's `__module__` property. The invoking module will be - used if this is not specified (if possible). - - - docstring (str): A docstring to associate with the class definition. - """ - - def typing_from_property(p): - # type: (properties.Property) -> str - if isinstance(p, type): - if p in ( - Union, Dict, Any, Sequence, IO - ): - type_hint = p.__name__ - else: - type_hint = qualified_name(p) - elif isinstance(p, properties.DateTime): - type_hint = 'datetime' - elif isinstance(p, properties.Date): - type_hint = 'date' - elif isinstance(p, properties.Bytes): - type_hint = 'bytes' - elif isinstance(p, properties.Integer): - type_hint = 'int' - elif isinstance(p, properties.Number): - type_hint = qualified_name(Number) - elif isinstance(p, properties.Boolean): - type_hint = 'bool' - elif isinstance(p, properties.String): - type_hint = 'str' - elif isinstance(p, properties.Array): - item_types = None - if p.item_types: - if len(p.item_types) > 1: - item_types = 'Union[%s]' % ( - ', '.join( - typing_from_property(it) - for it in p.item_types - ) - ) - else: - item_types = typing_from_property(p.item_types[0]) - type_hint = 'Sequence' + ( - '[%s]' % item_types - if item_types else - '' - ) - elif isinstance(p, properties.Dictionary): - value_types = None - if p.value_types: - if len(p.value_types) > 1: - value_types = 'Union[%s]' % ( - ', '.join( - typing_from_property(vt) - for vt in p.value_types - ) - ) - else: - value_types = typing_from_property(p.value_types[0]) - type_hint = ( - 'Dict[str, %s]' % value_types - if value_types else - 'dict' - ) - elif p.types: - if len(p.types) > 1: - type_hint = 'Union[%s]' % ', '.join( - typing_from_property(t) for t in p.types - ) - else: - type_hint = typing_from_property(p.types[0]) - else: - type_hint = 'Any' - return type_hint - if docstring is not None: - if '\t' in docstring: - docstring = docstring.replace('\t', ' ') - lines = docstring.split('\n') - indentation_length = float('inf') - for line in lines: - match = re.match(r'^[ ]+', line) - if match: - indentation_length = min( - indentation_length, - len(match.group()) - ) - else: - indentation_length = 0 - break - wrapped_lines = [] - for line in lines: - line = ' ' + line[indentation_length:] - if len(line) > 120: - indent = re.match(r'^[ ]*', line).group() - li = len(indent) - words = re.split(r'([\w]*[\w,/"\'.;\-?`])', line[li:]) - wrapped_line = '' - for word in words: - if (len(wrapped_line) + len(word) + li) <= 120: - wrapped_line += word - else: - wrapped_lines.append(indent + wrapped_line) - wrapped_line = '' if not word.strip() else word - if wrapped_line: - wrapped_lines.append(indent + wrapped_line) - else: - wrapped_lines.append(line) - docstring = '\n'.join( - [' """'] + - wrapped_lines + - [' """'] - ) - if isinstance(metadata, meta.Dictionary): - out = [ - 'class %s(serial.model.Dictionary):' % name - ] - if docstring is not None: - out.append(docstring) - out.append('\n pass') - elif isinstance(metadata, meta.Array): - out = [ - 'class %s(serial.model.Array):' % name - ] - if docstring is not None: - out.append(docstring) - out.append('\n pass') - elif isinstance(metadata, meta.Object): - out = [ - 'class %s(serial.model.Object):' % name - ] - if docstring is not None: - out.append(docstring) - out += [ - '', - ' def __init__(', - ' self,', - ' _=None, # type: Optional[Union[str, bytes, dict, Sequence, IO]]' - ] - for n, p in metadata.properties.items(): - out.append( - ' %s=None, # type: Optional[%s]' % (n, typing_from_property(p)) - ) - out.append( - ' ):' - ) - for n in metadata.properties.keys(): - out.append( - ' self.%s = %s' % (n, n) - ) - out.append(' super().__init__(_)\n\n') - else: - raise ValueError(metadata) - class_definition = '\n'.join(out) - namespace = dict(__name__='from_meta_%s' % name) - imports = '\n'.join([ - 'import serial', - '', - 'serial.utilities.compatibility.backport()', - '' - 'try:', - ' from typing import Union, Dict, Any, Sequence, IO', - 'except ImportError:', - ' Union = Dict = Any = Sequence = IO = None', - ]) - source = '%s\n\n\n%s' % (imports, class_definition) - exec(source, namespace) - result = namespace[name] - result._source = source - - if module is None: - try: - module = sys._getframe(1).f_globals.get('__name__', '__main__') - except (AttributeError, ValueError): - pass - - if module is not None: - result.__module__ = module - - result._meta = metadata - - return result diff --git a/backend/venv/lib/python3.6/site-packages/serial/properties.py b/backend/venv/lib/python3.6/site-packages/serial/properties.py deleted file mode 100644 index 88999e2..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/properties.py +++ /dev/null @@ -1,941 +0,0 @@ -# Tell the linters what's up: -# pylint:disable=wrong-import-position,consider-using-enumerate,useless-object-inheritance -# mccabe:options:max-complexity=999 -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals - -from .utilities.compatibility import backport - -backport() # noqa - -from future.utils import native_str - -import numbers # noqa - -from base64 import b64decode, b64encode # noqa -from copy import deepcopy # noqa -from datetime import date, datetime # noqa - -try: - from typing import Union, Optional, Sequence, Mapping, Set, Sequence, Callable, Dict, Any, Hashable, Collection,\ - Tuple -except ImportError: - Union = Optional = Sequence = Mapping = Set = Sequence = Callable = Dict = Any = Hashable = Collection = Tuple =\ - Iterable = None - -import iso8601 # noqa - -from .utilities import collections, collections_abc, qualified_name, properties_values, parameters_defaults,\ - calling_function_qualified_name - -from serial import abc, errors, meta -import serial - - -NoneType = type(None) - - -NULL = None - - -class Null(object): # noqa - inheriting from object is intentional, as this is needed for python 2x compatibility - """ - Instances of this class represent an *explicit* null value, rather than the absence of a - property/attribute/element, as would be inferred from a value of `None`. - """ - - def __init__(self): - if NULL is not None: - raise errors.DefinitionExistsError( - '%s may only be defined once.' % repr(self) - ) - - def __bool__(self): - # type: (...) -> bool - return False - - def __eq__(self, other): - # type: (Any) -> bool - return id(other) == id(self) - - def __hash__(self): - # type: (...) -> int - return 0 - - def __str__(self): - # type: (...) -> str - return 'null' - - def _marshal(self): - # type: (...) -> None - return None - - def __repr__(self): - # type: (...) -> str - return ( - 'NULL' - if self.__module__ in ('__main__', 'builtins', '__builtin__') else - '%s.NULL' % self.__module__ - ) - - def __copy__(self): - # type: (...) -> Null - return self - - def __deepcopy__(self, memo): - # type: (Dict[Hashable, Any]) -> Null - return self - - -NULL = Null() - - -def _validate_type_or_property(type_or_property): - # type: (Union[type, Property]) -> (Union[type, Property]) - - if not isinstance(type_or_property, (type, Property)): - raise TypeError(type_or_property) - - if not ( - (type_or_property is Null) or - ( - isinstance(type_or_property, type) and - issubclass( - type_or_property, - ( - abc.model.Model, - str, - native_str, - bytes, - numbers.Number, - date, - datetime, - Null, - collections_abc.Iterable, - dict, - collections.OrderedDict, - bool - ) - ) - ) or - isinstance(type_or_property, Property) - ): - raise TypeError(type_or_property) - - return type_or_property - - -class Types(list): - """ - Instances of this class are lists which will only take values which are valid types for describing a property - definition. - """ - - def __init__( - self, - property_, # type: Property - items=None # type: Optional[Union[Sequence[Union[type, Property], Types], type, Property]] - ): - # (...) -> None - if not isinstance(property_, Property): - raise TypeError( - 'The parameter `property` must be a `type`, or an instance of `%s`.' % qualified_name(Property) - ) - - self.property_ = property_ - - if isinstance(items, (type, Property)): - items = (items,) - - if items is None: - super().__init__() - else: - super().__init__(items) - - def __setitem__(self, index, value): - # type: (int, Union[type, Property]) -> None - super().__setitem__(index, _validate_type_or_property(value)) - if value is str and (native_str is not str) and (native_str not in self): - super().append(native_str) - - def append(self, value): - # type: (Union[type, Property]) -> None - super().append(_validate_type_or_property(value)) - if value is str and (native_str is not str) and (native_str not in self): - super().append(native_str) - - def __delitem__(self, index): - # type: (int) -> None - value = self[index] - super().__delitem__(index) - if (value is str) and (native_str in self): - self.remove(native_str) - - def pop(self, index=-1): - # type: (int) -> Union[type, Property] - value = super().pop(index) - if (value is str) and (native_str in self): - self.remove(native_str) - return value - - def __copy__(self): - # type: () -> Types - return self.__class__(self.property_, self) - - def __deepcopy__(self, memo=None): - # type: (dict) -> Types - return self.__class__( - self.property_, - tuple( - deepcopy(v, memo=memo) - for v in self - ) - ) - - def __repr__(self): - - representation = [ - qualified_name(type(self)) + '(' - ] - - if self: - - representation[0] += '[' - - for v in self: - - rv = ( - qualified_name(v) if isinstance(v, type) else - repr(v) - ) - rvls = rv.split('\n') - - if len(rvls) > 1: - - rvs = [rvls[0]] - - for rvl in rvls[1:]: - rvs.append(' ' + rvl) - - rv = '\n'.join(rvs) - representation += [ - ' %s' % rv, - ] - - else: - - representation.append( - ' %s,' % rv - ) - - representation[-1] = representation[-1][:-1] - representation.append( - ']' - ) - - representation[-1] += ')' - - return '\n'.join(representation) if len(representation) > 2 else ''.join(representation) - - -class Property(object): - """ - This is the base class for defining a property. - - Properties - - - value_types ([type|Property]): One or more expected value_types or `Property` instances. Values are checked, - sequentially, against each type or `Property` instance, and the first appropriate match is used. - - - required (bool|collections.Callable): If `True`--dumping the json_object will throw an error if this value - is `None`. - - - versions ([str]|{str:Property}): The property should be one of the following: - - - A set/tuple/list of version numbers to which this property applies. - - A mapping of version numbers to an instance of `Property` applicable to that version. - - Version numbers prefixed by "<" indicate any version less than the one specified, so "<3.0" indicates that - this property is available in versions prior to 3.0. The inverse is true for version numbers prefixed by ">". - ">=" and "<=" have similar meanings, but are inclusive. - - Versioning can be applied to an json_object by calling `serial.meta.set_version` in the `__init__` method of - an `serial.model.Object` sub-class. For an example, see `oapi.model.OpenAPI.__init__`. - - - name (str): The name of the property when loaded from or dumped into a JSON/YAML/XML json_object. Specifying a - `name` facilitates mapping of PEP8 compliant property to JSON or YAML attribute names, or XML element names, - which are either camelCased, are python keywords, or otherwise not appropriate for usage in python code. - - """ - - def __init__( - self, - types=None, # type: Sequence[Union[type, Property]] - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Sequence[Union[str, meta.Version]]] - ): - self._types = None # type: Optional[Sequence[Union[type, Property]]] - self.types = types - self.name = name - self.required = required - self._versions = None # type: Optional[Union[Mapping[str, Optional[Property]], Set[Union[str, Number]]]] - self.versions = versions # type: Optional[Union[Mapping[str, Optional[Property]], Set[Union[str, Number]]]] - - @property - def types(self): - return self._types - - @types.setter - def types(self, types_or_properties): - # type: (Optional[Sequence[Union[type, Property, abc.model.Model]]]) -> None - - if types_or_properties is not None: - - if callable(types_or_properties): - - if native_str is not str: - - _types_or_properties = types_or_properties - - def types_or_properties(d): - # type: (Sequence[Union[type, Property, abc.model.Model]]) -> Types - return Types(self, _types_or_properties(d)) - - else: - - types_or_properties = Types(self, types_or_properties) - - self._types = types_or_properties - - @property - def versions(self): - # type: () -> Optional[Sequence[meta.Version]] - return self._versions - - @versions.setter - def versions( - self, - versions # type: Optional[Sequence[Union[str, collections_abc.Iterable, meta.Version]]] - ): - # type: (...) -> Optional[Union[Mapping[str, Optional[Property]], Set[Union[str, Number]]]] - if versions is not None: - - if isinstance(versions, (str, Number, meta.Version)): - versions = (versions,) - - if isinstance(versions, collections_abc.Iterable): - versions = tuple( - (v if isinstance(v, meta.Version) else meta.Version(v)) - for v in versions - ) - - else: - - repr_versions = repr(versions) - - raise TypeError( - ( - '`%s` requires a sequence of version strings or ' % - calling_function_qualified_name() - ) + ( - '`%s` instances, not' % qualified_name(meta.Version) - ) + ( - ':\n' + repr_versions - if '\n' in repr_versions else - ' `%s`.' % repr_versions - ) - ) - - self._versions = versions - - def unmarshal(self, data): - # type: (Any) -> Any - # return data if self.types is None else unmarshal(data, types=self.types) - - if isinstance( - data, - collections_abc.Iterable - ) and not isinstance( - data, - (str, bytes, bytearray, native_str) - ) and not isinstance( - data, - abc.model.Model - ): - - if isinstance(data, (dict, collections.OrderedDict)): - - for k, v in data.items(): - if v is None: - data[k] = NULL - - else: - - data = tuple((NULL if i is None else i) for i in data) - - return serial.marshal.unmarshal(data, types=self.types) - - def marshal(self, data): - # type: (Any) -> Any - return serial.marshal.marshal(data, types=self.types) #, types=self.types, value_types=self.value_types) - - def __repr__(self): - representation = [qualified_name(type(self)) + '('] - pd = parameters_defaults(self.__init__) - for p, v in properties_values(self): - if (p not in pd) or pd[p] == v: - continue - if (v is not None) and (v is not NULL): - if isinstance(v, collections_abc.Sequence) and not isinstance(v, (str, bytes)): - rvs = ['('] - for i in v: - ri = ( - qualified_name(i) - if isinstance(i, type) else - "'%s'" % str(i) - if isinstance(i, meta.Version) else - repr(i) - ) - rils = ri.split('\n') - if len(rils) > 1: - ris = [rils[0]] - for ril in rils[1:]: - ris.append(' ' + ril) - ri = '\n'.join(ris) - rvs.append(' %s,' % ri) - if len(v) > 1: - rvs[-1] = rvs[-1][:-1] - rvs.append(' )') - rv = '\n'.join(rvs) - else: - rv = ( - qualified_name(v) - if isinstance(v, type) else - "'%s'" % str(v) - if isinstance(v, meta.Version) else - repr(v) - ) - rvls = rv.split('\n') - if len(rvls) > 2: - rvs = [rvls[0]] - for rvl in rvls[1:]: - rvs.append(' ' + rvl) - rv = '\n'.join(rvs) - representation.append( - ' %s=%s,' % (p, rv) - ) - representation.append(')') - if len(representation) > 2: - return '\n'.join(representation) - else: - return ''.join(representation) - - def __copy__(self): - - new_instance = self.__class__() - - for a in dir(self): - - if a[0] != '_' and a != 'data': - - v = getattr(self, a) - - if not callable(v): - setattr(new_instance, a, v) - - return new_instance - - def __deepcopy__(self, memo): - # type: (dict) -> Property - - new_instance = self.__class__() - - for a, v in properties_values(self): - setattr(new_instance, a, deepcopy(v, memo)) - - return new_instance - - -abc.properties.Property.register(Property) - - -class String(Property): - """ - See `serial.properties.Property` - """ - - def __init__( - self, - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - ): - super().__init__( - types=(str,), - name=name, - required=required, - versions=versions, - ) - - -class Date(Property): - """ - See `serial.properties.Property` - - Additional Properties: - - - marshal (collections.Callable): A function, taking one argument (a python `date` json_object), and returning - a date string in the desired format. The default is `date.isoformat`--returning an iso8601 compliant date - string. - - - unmarshal (collections.Callable): A function, taking one argument (a date string), and returning a python - `date` json_object. By default, this is `iso8601.parse_date`. - """ - - def __init__( - self, - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - date2str=date.isoformat, # type: Optional[Callable] - str2date=iso8601.parse_date # type: Callable - ): - super().__init__( - types=(date,), - name=name, - required=required, - versions=versions, - ) - self.date2str = date2str - self.str2date = str2date - - def unmarshal(self, data): - # type: (Optional[str]) -> Union[date, NoneType] - if data is None: - return data - else: - if isinstance(data, date): - date_ = data - elif isinstance(data, str): - date_ = self.str2date(data) - else: - raise TypeError( - '%s is not a `str`.' % repr(data) - ) - if isinstance(date_, date): - return date_ - else: - raise TypeError( - '"%s" is not a properly formatted date string.' % data - ) - - def marshal(self, data): - # type: (Optional[date]) -> Optional[str] - if data is None: - return data - else: - ds = self.date2str(data) - if not isinstance(ds, str): - if isinstance(ds, native_str): - ds = str(ds) - else: - raise TypeError( - 'The date2str function should return a `str`, not a `%s`: %s' % ( - type(ds).__name__, - repr(ds) - ) - ) - return ds - - -class DateTime(Property): - """ - See `serial.properties.Property` - - Additional Properties: - - - marshal (collections.Callable): A function, taking one argument (a python `datetime` json_object), and - returning a date-time string in the desired format. The default is `datetime.isoformat`--returning an - iso8601 compliant date-time string. - - - unmarshal (collections.Callable): A function, taking one argument (a datetime string), and returning a python - `datetime` json_object. By default, this is `iso8601.parse_date`. - """ - - def __init__( - self, - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - datetime2str=datetime.isoformat, # type: Optional[Callable] - str2datetime=iso8601.parse_date # type: Callable - ): - self.datetime2str = datetime2str - self.str2datetime = str2datetime - super().__init__( - types=(datetime,), - name=name, - required=required, - versions=versions, - ) - - def unmarshal(self, data): - # type: (Optional[str]) -> Union[datetime, NoneType] - if data is None: - return data - else: - if isinstance(data, datetime): - datetime_ = data - elif isinstance(data, str): - datetime_ = self.str2datetime(data) - else: - raise TypeError( - '%s is not a `str`.' % repr(data) - ) - if isinstance(datetime_, datetime): - return datetime_ - else: - raise TypeError( - '"%s" is not a properly formatted date-time string.' % data - ) - - def marshal(self, data): - # type: (Optional[datetime]) -> Optional[str] - if data is None: - return data - else: - datetime_string = self.datetime2str(data) - if not isinstance(datetime_string, str): - if isinstance(datetime_string, native_str): - datetime_string = str(datetime_string) - else: - repr_datetime_string = repr(datetime_string).strip() - raise TypeError( - 'The datetime2str function should return a `str`, not:' + ( - '\n' - if '\n' in repr_datetime_string else - ' ' - ) + repr_datetime_string - ) - return datetime_string - - -class Bytes(Property): - """ - See `serial.properties.Property` - """ - - def __init__( - self, - name=None, # type: Optional[str] - required=False, # type: bool - versions=None, # type: Optional[Collection] - ): - super().__init__( - types=(bytes, bytearray), - name=name, - required=required, - versions=versions, - ) - - def unmarshal(self, data): - # type: (str) -> Optional[bytes] - """ - Un-marshal a base-64 encoded string into bytes - """ - if data is None: - return data - elif isinstance(data, str): - return b64decode(data) - elif isinstance(data, bytes): - return data - else: - raise TypeError( - '`data` must be a base64 encoded `str` or `bytes`--not `%s`' % qualified_name(type(data)) - ) - - def marshal(self, data): - # type: (bytes) -> str - """ - Marshal bytes into a base-64 encoded string - """ - if (data is None) or isinstance(data, str): - return data - elif isinstance(data, bytes): - return str(b64encode(data), 'ascii') - else: - raise TypeError( - '`data` must be a base64 encoded `str` or `bytes`--not `%s`' % qualified_name(type(data)) - ) - - -class Enumerated(Property): - """ - See `serial.properties.Property`... - - + Properties: - - - values ([Any]): A list of possible values. If the parameter `types` is specified--typing is - applied prior to validation. - """ - - def __init__( - self, - types=None, # type: Optional[Sequence[Union[type, Property]]] - values=None, # type: Optional[Union[Sequence, Set]] - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - ): - self._values = None - - super().__init__( - types=types, - name=name, - required=required, - versions=versions - ) - - self.values = values # type: Optional[Sequence] - - @property - def values(self): - # type: () -> Optional[Union[Tuple, Callable]] - return self._values - - @values.setter - def values(self, values): - # type: (Iterable) -> None - - if not ((values is None) or callable(values)): - - if (values is not None) and (not isinstance(values, (collections_abc.Sequence, collections_abc.Set))): - raise TypeError( - '`values` must be a finite set or sequence, not `%s`.' % qualified_name(type(values)) - ) - - if values is not None: - values = [ - serial.marshal.unmarshal(v, types=self.types) - for v in values - ] - - self._values = values - - def unmarshal(self, data): - # type: (Any) -> Any - - if self.types is not None: - data = serial.marshal.unmarshal(data, types=self.types) - - if ( - (data is not None) and - (self.values is not None) and - (data not in self.values) - ): - raise ValueError( - 'The value provided is not a valid option:\n%s\n\n' % repr(data) + - 'Valid options include:\n%s' % ( - ', '.join(repr(t) for t in self.values) - ) - ) - return data - - -class Number(Property): - """ - See `serial.properties.Property` - """ - - def __init__( - self, - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - ): - # type: (...) -> None - super().__init__( - types=(numbers.Number,), - name=name, - required=required, - versions=versions, - ) - - -class Integer(Property): - """ - See `serial.properties.Property` - """ - - def __init__( - self, - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - ): - super().__init__( - types=(int,), - name=name, - required=required, - versions=versions, - ) - - # def unmarshal(self, data): - # # type: (Any) -> Any - # if data is None: - # return data - # else: - # return int(data) - # - # def marshal(self, data): - # # type: (Any) -> Any - # if data is None: - # return data - # else: - # return int(data) - - -class Boolean(Property): - """ - See `serial.properties.Property` - """ - - def __init__( - self, - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - ): - # type: (...) -> None - super().__init__( - types=(bool,), - name=name, - required=required, - versions=versions, - ) - - -class Array(Property): - """ - See `serial.properties.Property`... - - + Properties: - - - item_types (type|Property|[type|Property]): The type(s) of values/objects contained in the array. Similar to - `serial.properties.Property().value_types`, but applied to items in the array, not the array itself. - """ - - def __init__( - self, - item_types=None, # type: Optional[Union[type, Sequence[Union[type, Property]]]] - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - ): - self._item_types = None - self.item_types = item_types - super().__init__( - types=(serial.model.Array,), - name=name, - required=required, - versions=versions, - ) - - def unmarshal(self, data): - # type: (Any) -> Any - return serial.marshal.unmarshal(data, types=self.types, item_types=self.item_types) - - def marshal(self, data): - # type: (Any) -> Any - return serial.marshal.marshal(data, types=self.types, item_types=self.item_types) - - @property - def item_types(self): - return self._item_types - - @item_types.setter - def item_types(self, item_types): - # type: (Optional[Sequence[Union[type, Property, abc.model.Object]]]) -> None - if item_types is not None: - if callable(item_types): - if native_str is not str: - _item_types = item_types - - def item_types(d): - # type: (Sequence[Union[type, Property, abc.model.Object]]) -> Types - return Types(self, _item_types(d)) - else: - item_types = Types(self, item_types) - self._item_types = item_types - - -class Dictionary(Property): - """ - See `serial.properties.Property`... - - + Properties: - - - value_types (type|Property|[type|Property]): The type(s) of values/objects comprising the mapped - values. Similar to `serial.properties.Property.types`, but applies to *values* in the dictionary - object, not the dictionary itself. - """ - - def __init__( - self, - value_types=None, # type: Optional[Union[type, Sequence[Union[type, Property]]]] - name=None, # type: Optional[str] - required=False, # type: Union[bool, collections.Callable] - versions=None, # type: Optional[Collection] - ): - self._value_types = None - self.value_types = value_types - super().__init__( - types=(serial.model.Dictionary,), - name=name, - required=required, - versions=versions, - ) - - def unmarshal(self, data): - # type: (Any) -> Any - return serial.marshal.unmarshal(data, types=self.types, value_types=self.value_types) - - @property - def value_types(self): - return self._value_types - - @value_types.setter - def value_types(self, value_types_): - # type: (Optional[Sequence[Union[type, Property, abc.model.Object]]]) -> None - """ - The `types` can be either: - - - A sequence of types and/or `serial.properties.Property` instances. - - - A function which accepts exactly one argument (a dictionary), and which returns a sequence of types and/or - `serial.properties.Property` instances. - - If more than one type or property definition is provided, un-marshalling is attempted using each `value_type`, - in sequential order. If a value could be cast into more than one of the `types` without throwing a - `ValueError`, `TypeError`, or `serial.errors.ValidationError`, the value type occuring *first* in the sequence - will be used. - """ - - if value_types_ is not None: - - if callable(value_types_): - - if native_str is not str: - - original_value_types_ = value_types_ - - def value_types_(data): - # type: (Sequence[Union[type, Property, abc.model.Object]]) -> Types - return Types(self, original_value_types_(data)) - - else: - - value_types_ = Types(self, value_types_) - - self._value_types = value_types_ diff --git a/backend/venv/lib/python3.6/site-packages/serial/request.py b/backend/venv/lib/python3.6/site-packages/serial/request.py deleted file mode 100644 index 90ef336..0000000 --- a/backend/venv/lib/python3.6/site-packages/serial/request.py +++ /dev/null @@ -1,451 +0,0 @@ -""" -This module extends the functionality of `urllib.request.Request` to support multipart requests, to support passing -instances of serial models to the `data` parameter/property for `urllib.request.Request`, and to -support casting requests as `str` or `bytes` (typically for debugging purposes and/or to aid in producing -non-language-specific API documentation). -""" -# region Backwards Compatibility -from __future__ import nested_scopes, generators, division, absolute_import, with_statement, \ - print_function, unicode_literals -from .utilities.compatibility import backport - -backport() # noqa - -from future.utils import native_str - -import random -import re -import string -import urllib.request - -try: - from typing import Dict, Sequence, Set, Iterable -except ImportError: - Dict = Sequence = Set = None - -from serial.marshal import serialize -from .abc.model import Model -from .utilities import collections_abc - - -class Headers(object): - """ - A dictionary of headers for a `Request`, `Part`, or `MultipartRequest` instance. - """ - - def __init__(self, items, request): - # type: (Dict[str, str], Union[Part, Request]) -> None - self._dict = {} - self.request = request # type: Data - self.update(items) - - def pop(self, key, default=None): - # type: (str, Optional[str]) -> str - key = key.capitalize() - if hasattr(self.request, '_boundary'): - self.request._boundary = None - if hasattr(self.request, '_bytes'): - self.request._bytes = None - return self._dict.pop(key, default=default) - - def popitem(self): - # type: (str, Optional[str]) -> str - if hasattr(self.request, '_boundary'): - self.request._boundary = None - if hasattr(self.request, '_bytes'): - self.request._bytes = None - return self._dict.popitem() - - def setdefault(self, key, default=None): - # type: (str, Optional[str]) -> str - key = key.capitalize() - if hasattr(self.request, '_boundary'): - self.request._boundary = None - if hasattr(self.request, '_bytes'): - self.request._bytes = None - return self._dict.setdefault(key, default=default) - - def update(self, iterable=None, **kwargs): - # type: (Union[Dict[str, str], Sequence[Tuple[str, str]]], Union[Dict[str, str]]) -> None - cd = {} - if iterable is None: - d = kwargs - else: - d = dict(iterable, **kwargs) - for k, v in d.items(): - cd[k.capitalize()] = v - if hasattr(self.request, '_boundary'): - self.request._boundary = None - if hasattr(self.request, '_bytes'): - self.request._bytes = None - return self._dict.update(cd) - - def __delitem__(self, key): - # type: (str) -> None - key = key.capitalize() - if hasattr(self.request, '_boundary'): - self.request._boundary = None - if hasattr(self.request, '_bytes'): - self.request._bytes = None - del self._dict[key] - - def __setitem__(self, key, value): - # type: (str, str) -> None - key = key.capitalize() - if key != 'Content-length': - if hasattr(self.request, '_boundary'): - self.request._boundary = None - if hasattr(self.request, '_bytes'): - self.request._bytes = None - return self._dict.__setitem__(key, value) - - def __getitem__(self, key): - # type: (str) -> None - key = key.capitalize() - if key == 'Content-length': - data = self.request.data - if data is None: - content_length = 0 - else: - content_length = len(data) - value = str(content_length) - else: - try: - value = self._dict.__getitem__(key) - except KeyError as e: - if key == 'Content-type': - if hasattr(self.request, 'parts') and self.request.parts: - value = 'multipart/form-data' - if ( - (value is not None) and - value.strip().lower()[:9] == 'multipart' and - hasattr(self.request, 'boundary') - ): - value += '; boundary=' + str(self.request.boundary, encoding='utf-8') - return value - - def keys(self): - # type: (...) -> Iterable[str] - return (k for k in self) - - def values(self): - return (self[k] for k in self) - - def __len__(self): - return len(tuple(self)) - - def __iter__(self): - # type: (...) -> Iterable[str] - keys = set() - for k in self._dict.keys(): - keys.add(k) - yield k - if type(self.request) is not Part: - # *Always* include "Content-length" - if 'Content-length' not in keys: - yield 'Content-length' - if ( - hasattr(self.request, 'parts') and - self.request.parts and - ('Content-type' not in keys) - ): - yield 'Content-type' - - def __contains__(self, key): - # type: (str) -> bool - return True if key in self.keys() else False - - def items(self): - # type: (...) -> Iterable[Tuple[str, str]] - for k in self: - yield k, self[k] - - def copy(self): - # type: (...) -> Headers - return self.__class__( - self._dict, - request=self.request - ) - - def __copy__(self): - # type: (...) -> Headers - return self.copy() - - -class Data(object): - """ - One of a multipart request's parts. - """ - - def __init__( - self, - data=None, # type: Optional[Union[bytes, str, Sequence, Set, dict, Model]] - headers=None # type: Optional[Dict[str, str]] - ): - """ - Parameters: - - - data (bytes|str|collections.Sequence|collections.Set|dict|serial.abc.Model): The payload. - - - headers ({str: str}): A dictionary of headers (for this part of the request body, not the main request). - This should (almost) always include values for "Content-Disposition" and "Content-Type". - """ - self._bytes = None # type: Optional[bytes] - self._headers = None - self._data = None - self.headers = headers # type: Dict[str, str] - self.data = data # type: Optional[bytes] - - @property - def headers(self): - return self._headers - - @headers.setter - def headers(self, headers): - self._bytes = None - if headers is None: - headers = Headers({}, self) - elif isinstance(headers, Headers): - headers.request = self - else: - headers = Headers(headers, self) - self._headers = headers - - @property - def data(self): - return self._data - - @data.setter - def data(self, data): - # type: (Optional[Union[bytes, str, Sequence, Set, dict, Model]]) -> None - self._bytes = None - if data is not None: - serialize_type = None - if 'Content-type' in self.headers: - ct = self.headers['Content-type'] - if re.search(r'/json\b', ct) is not None: - serialize_type = 'json' - if re.search(r'/xml\b', ct) is not None: - serialize_type = 'xml' - if re.search(r'/yaml\b', ct) is not None: - serialize_type = 'yaml' - if isinstance(data, (Model, dict)) or ( - isinstance(data, (collections_abc.Sequence, collections_abc.Set)) and not - isinstance(data, (str, bytes)) - ): - data = serialize(data, serialize_type or 'json') - if isinstance(data, str): - data = bytes(data, encoding='utf-8') - self._data = data - - def __bytes__(self): - if self._bytes is None: - lines = [] - for k, v in self.headers.items(): - lines.append(bytes( - '%s: %s' % (k, v), - encoding='utf-8' - )) - lines.append(b'') - data = self.data - if data: - lines.append(self.data) - self._bytes = b'\r\n'.join(lines) + b'\r\n' - return self._bytes - - def __str__(self): - b = self.__bytes__() - if not isinstance(b, native_str): - b = repr(b)[2:-1].replace('\\r\\n', '\r\n').replace('\\n', '\n') - return b - - -class Part(Data): - - def __init__( - self, - data=None, # type: Optional[Union[bytes, str, Sequence, Set, dict, Model]] - headers=None, # type: Optional[Dict[str, str]] - parts=None # type: Optional[Sequence[Part]] - ): - """ - Parameters: - - - data (bytes|str|collections.Sequence|collections.Set|dict|serial.abc.Model): The payload. - - - headers ({str: str}): A dictionary of headers (for this part of the request body, not the main request). - This should (almost) always include values for "Content-Disposition" and "Content-Type". - """ - self._boundary = None # type: Optional[bytes] - self._parts = None # type: Optional[Parts] - self.parts = parts - Data.__init__(self, data=data, headers=headers) - - @property - def boundary(self): - """ - Calculates a boundary which is not contained in any of the request parts. - """ - if self._boundary is None: - data = b'\r\n'.join( - [self._data or b''] + - [bytes(p) for p in self.parts] - ) - boundary = b''.join( - bytes( - random.choice(string.digits + string.ascii_letters), - encoding='utf-8' - ) - for i in range(16) - ) - while boundary in data: - boundary += bytes( - random.choice(string.digits + string.ascii_letters), - encoding='utf-8' - ) - self._boundary = boundary - return self._boundary - - @property - def data(self): - # type: (bytes) -> None - if self.parts: - data = (b'\r\n--' + self.boundary + b'\r\n').join( - [self._data or b''] + - [bytes(p).rstrip() for p in self.parts] - ) + (b'\r\n--' + self.boundary + b'--') - else: - data = self._data - return data - - @data.setter - def data(self, data): - return Data.data.__set__(self, data) - - @property - def parts(self): - # type: (...) -> Parts - return self._parts - - @parts.setter - def parts(self, parts): - # type: (Optional[Sequence[Part]]) -> None - if parts is None: - parts = Parts([], request=self) - elif isinstance(parts, Parts): - parts.request = self - else: - parts = Parts(parts, request=self) - self._boundary = None - self._parts = parts - - -class Parts(list): - - def __init__(self, items, request): - # type: (typing.Sequence[Part], MultipartRequest) -> None - self.request = request - super().__init__(items) - - def append(self, item): - # type: (Part) -> None - self.request._boundary = None - self.request._bytes = None - super().append(item) - - def clear(self): - # type: (...) -> None - self.request._boundary = None - self.request._bytes = None - super().clear() - - def extend(self, items): - # type: (Iterable[Part]) -> None - self.request._boundary = None - self.request._bytes = None - super().extend(items) - - def reverse(self): - # type: (...) -> None - self.request._boundary = None - self.request._bytes = None - super().reverse() - - def __delitem__(self, key): - # type: (str) -> None - self.request._boundary = None - self.request._bytes = None - super().__delitem__(key) - - def __setitem__(self, key, value): - # type: (str) -> None - self.request._boundary = None - self.request._bytes = None - super().__setitem__(key, value) - - -class Request(Data, urllib.request.Request): - """ - A sub-class of `urllib.request.Request` which accommodates additional data types, and serializes `data` in - accordance with what is indicated by the request's "Content-Type" header. - """ - - def __init__( - self, - url, - data=None, # type: Optional[Union[bytes, str, Sequence, Set, dict, Model]] - headers=None, # type: Optional[Dict[str, str]] - origin_req_host=None, # type: Optional[str] - unverifiable=False, # type: bool - method=None # type: Optional[str] - ): - # type: (...) -> None - self._bytes = None # type: Optional[bytes] - self._headers = None - self._data = None - self.headers = headers - urllib.request.Request.__init__( - self, - url, - data=data, - headers=headers, - origin_req_host=origin_req_host, - unverifiable=unverifiable, - method=method - ) - - -class MultipartRequest(Part, Request): - """ - A sub-class of `Request` which adds a property (and initialization parameter) to hold the `parts` of a - multipart request. - - https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html - """ - - def __init__( - self, - url, - data=None, # type: Optional[Union[bytes, str, Sequence, Set, dict, Model]] - headers=None, # type: Optional[Dict[str, str]] - origin_req_host=None, # type: Optional[str] - unverifiable=False, # type: bool - method=None, # type: Optional[str] - parts=None # type: Optional[Sequence[Part]] - ): - # type: (...) -> None - Part.__init__( - self, - data=data, - headers=headers, - parts=parts - ) - Request.__init__( - self, - url, - data=data, - headers=headers, - origin_req_host=origin_req_host, - unverifiable=unverifiable, - method=method - ) diff --git a/backend/venv/lib/python3.6/site-packages/serial/rfc2217.py b/backend/venv/lib/python3.6/site-packages/serial/rfc2217.py new file mode 100644 index 0000000..419947d --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/rfc2217.py @@ -0,0 +1,1346 @@ +#! python +# +# This module implements a RFC2217 compatible client. RF2217 descibes a +# protocol to access serial ports over TCP/IP and allows setting the baud rate, +# modem control lines etc. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2001-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +# TODO: +# - setting control line -> answer is not checked (had problems with one of the +# severs). consider implementing a compatibility mode flag to make check +# conditional +# - write timeout not implemented at all + +# ########################################################################### +# observations and issues with servers +# =========================================================================== +# sredird V2.2.1 +# - http://www.ibiblio.org/pub/Linux/system/serial/ sredird-2.2.2.tar.gz +# - does not acknowledge SET_CONTROL (RTS/DTR) correctly, always responding +# [105 1] instead of the actual value. +# - SET_BAUDRATE answer contains 4 extra null bytes -> probably for larger +# numbers than 2**32? +# - To get the signature [COM_PORT_OPTION 0] has to be sent. +# - run a server: while true; do nc -l -p 7000 -c "sredird debug /dev/ttyUSB0 /var/lock/sredir"; done +# =========================================================================== +# telnetcpcd (untested) +# - http://ftp.wayne.edu/kermit/sredird/telnetcpcd-1.09.tar.gz +# - To get the signature [COM_PORT_OPTION] w/o data has to be sent. +# =========================================================================== +# ser2net +# - does not negotiate BINARY or COM_PORT_OPTION for his side but at least +# acknowledges that the client activates these options +# - The configuration may be that the server prints a banner. As this client +# implementation does a flushInput on connect, this banner is hidden from +# the user application. +# - NOTIFY_MODEMSTATE: the poll interval of the server seems to be one +# second. +# - To get the signature [COM_PORT_OPTION 0] has to be sent. +# - run a server: run ser2net daemon, in /etc/ser2net.conf: +# 2000:telnet:0:/dev/ttyS0:9600 remctl banner +# ########################################################################### + +# How to identify ports? pySerial might want to support other protocols in the +# future, so lets use an URL scheme. +# for RFC2217 compliant servers we will use this: +# rfc2217://:[?option[&option...]] +# +# options: +# - "logging" set log level print diagnostic messages (e.g. "logging=debug") +# - "ign_set_control": do not look at the answers to SET_CONTROL +# - "poll_modem": issue NOTIFY_MODEMSTATE requests when CTS/DTR/RI/CD is read. +# Without this option it expects that the server sends notifications +# automatically on change (which most servers do and is according to the +# RFC). +# the order of the options is not relevant + +import logging +import socket +import struct +import threading +import time +try: + import urlparse +except ImportError: + import urllib.parse as urlparse +try: + import Queue +except ImportError: + import queue as Queue + +import serial +from serial.serialutil import SerialBase, SerialException, to_bytes, \ + iterbytes, portNotOpenError, Timeout + +# port string is expected to be something like this: +# rfc2217://host:port +# host may be an IP or including domain, whatever. +# port is 0...65535 + +# map log level names to constants. used in from_url() +LOGGER_LEVELS = { + 'debug': logging.DEBUG, + 'info': logging.INFO, + 'warning': logging.WARNING, + 'error': logging.ERROR, +} + + +# telnet protocol characters +SE = b'\xf0' # Subnegotiation End +NOP = b'\xf1' # No Operation +DM = b'\xf2' # Data Mark +BRK = b'\xf3' # Break +IP = b'\xf4' # Interrupt process +AO = b'\xf5' # Abort output +AYT = b'\xf6' # Are You There +EC = b'\xf7' # Erase Character +EL = b'\xf8' # Erase Line +GA = b'\xf9' # Go Ahead +SB = b'\xfa' # Subnegotiation Begin +WILL = b'\xfb' +WONT = b'\xfc' +DO = b'\xfd' +DONT = b'\xfe' +IAC = b'\xff' # Interpret As Command +IAC_DOUBLED = b'\xff\xff' + +# selected telnet options +BINARY = b'\x00' # 8-bit data path +ECHO = b'\x01' # echo +SGA = b'\x03' # suppress go ahead + +# RFC2217 +COM_PORT_OPTION = b'\x2c' + +# Client to Access Server +SET_BAUDRATE = b'\x01' +SET_DATASIZE = b'\x02' +SET_PARITY = b'\x03' +SET_STOPSIZE = b'\x04' +SET_CONTROL = b'\x05' +NOTIFY_LINESTATE = b'\x06' +NOTIFY_MODEMSTATE = b'\x07' +FLOWCONTROL_SUSPEND = b'\x08' +FLOWCONTROL_RESUME = b'\x09' +SET_LINESTATE_MASK = b'\x0a' +SET_MODEMSTATE_MASK = b'\x0b' +PURGE_DATA = b'\x0c' + +SERVER_SET_BAUDRATE = b'\x65' +SERVER_SET_DATASIZE = b'\x66' +SERVER_SET_PARITY = b'\x67' +SERVER_SET_STOPSIZE = b'\x68' +SERVER_SET_CONTROL = b'\x69' +SERVER_NOTIFY_LINESTATE = b'\x6a' +SERVER_NOTIFY_MODEMSTATE = b'\x6b' +SERVER_FLOWCONTROL_SUSPEND = b'\x6c' +SERVER_FLOWCONTROL_RESUME = b'\x6d' +SERVER_SET_LINESTATE_MASK = b'\x6e' +SERVER_SET_MODEMSTATE_MASK = b'\x6f' +SERVER_PURGE_DATA = b'\x70' + +RFC2217_ANSWER_MAP = { + SET_BAUDRATE: SERVER_SET_BAUDRATE, + SET_DATASIZE: SERVER_SET_DATASIZE, + SET_PARITY: SERVER_SET_PARITY, + SET_STOPSIZE: SERVER_SET_STOPSIZE, + SET_CONTROL: SERVER_SET_CONTROL, + NOTIFY_LINESTATE: SERVER_NOTIFY_LINESTATE, + NOTIFY_MODEMSTATE: SERVER_NOTIFY_MODEMSTATE, + FLOWCONTROL_SUSPEND: SERVER_FLOWCONTROL_SUSPEND, + FLOWCONTROL_RESUME: SERVER_FLOWCONTROL_RESUME, + SET_LINESTATE_MASK: SERVER_SET_LINESTATE_MASK, + SET_MODEMSTATE_MASK: SERVER_SET_MODEMSTATE_MASK, + PURGE_DATA: SERVER_PURGE_DATA, +} + +SET_CONTROL_REQ_FLOW_SETTING = b'\x00' # Request Com Port Flow Control Setting (outbound/both) +SET_CONTROL_USE_NO_FLOW_CONTROL = b'\x01' # Use No Flow Control (outbound/both) +SET_CONTROL_USE_SW_FLOW_CONTROL = b'\x02' # Use XON/XOFF Flow Control (outbound/both) +SET_CONTROL_USE_HW_FLOW_CONTROL = b'\x03' # Use HARDWARE Flow Control (outbound/both) +SET_CONTROL_REQ_BREAK_STATE = b'\x04' # Request BREAK State +SET_CONTROL_BREAK_ON = b'\x05' # Set BREAK State ON +SET_CONTROL_BREAK_OFF = b'\x06' # Set BREAK State OFF +SET_CONTROL_REQ_DTR = b'\x07' # Request DTR Signal State +SET_CONTROL_DTR_ON = b'\x08' # Set DTR Signal State ON +SET_CONTROL_DTR_OFF = b'\x09' # Set DTR Signal State OFF +SET_CONTROL_REQ_RTS = b'\x0a' # Request RTS Signal State +SET_CONTROL_RTS_ON = b'\x0b' # Set RTS Signal State ON +SET_CONTROL_RTS_OFF = b'\x0c' # Set RTS Signal State OFF +SET_CONTROL_REQ_FLOW_SETTING_IN = b'\x0d' # Request Com Port Flow Control Setting (inbound) +SET_CONTROL_USE_NO_FLOW_CONTROL_IN = b'\x0e' # Use No Flow Control (inbound) +SET_CONTROL_USE_SW_FLOW_CONTOL_IN = b'\x0f' # Use XON/XOFF Flow Control (inbound) +SET_CONTROL_USE_HW_FLOW_CONTOL_IN = b'\x10' # Use HARDWARE Flow Control (inbound) +SET_CONTROL_USE_DCD_FLOW_CONTROL = b'\x11' # Use DCD Flow Control (outbound/both) +SET_CONTROL_USE_DTR_FLOW_CONTROL = b'\x12' # Use DTR Flow Control (inbound) +SET_CONTROL_USE_DSR_FLOW_CONTROL = b'\x13' # Use DSR Flow Control (outbound/both) + +LINESTATE_MASK_TIMEOUT = 128 # Time-out Error +LINESTATE_MASK_SHIFTREG_EMPTY = 64 # Transfer Shift Register Empty +LINESTATE_MASK_TRANSREG_EMPTY = 32 # Transfer Holding Register Empty +LINESTATE_MASK_BREAK_DETECT = 16 # Break-detect Error +LINESTATE_MASK_FRAMING_ERROR = 8 # Framing Error +LINESTATE_MASK_PARTIY_ERROR = 4 # Parity Error +LINESTATE_MASK_OVERRUN_ERROR = 2 # Overrun Error +LINESTATE_MASK_DATA_READY = 1 # Data Ready + +MODEMSTATE_MASK_CD = 128 # Receive Line Signal Detect (also known as Carrier Detect) +MODEMSTATE_MASK_RI = 64 # Ring Indicator +MODEMSTATE_MASK_DSR = 32 # Data-Set-Ready Signal State +MODEMSTATE_MASK_CTS = 16 # Clear-To-Send Signal State +MODEMSTATE_MASK_CD_CHANGE = 8 # Delta Receive Line Signal Detect +MODEMSTATE_MASK_RI_CHANGE = 4 # Trailing-edge Ring Detector +MODEMSTATE_MASK_DSR_CHANGE = 2 # Delta Data-Set-Ready +MODEMSTATE_MASK_CTS_CHANGE = 1 # Delta Clear-To-Send + +PURGE_RECEIVE_BUFFER = b'\x01' # Purge access server receive data buffer +PURGE_TRANSMIT_BUFFER = b'\x02' # Purge access server transmit data buffer +PURGE_BOTH_BUFFERS = b'\x03' # Purge both the access server receive data + # buffer and the access server transmit data buffer + + +RFC2217_PARITY_MAP = { + serial.PARITY_NONE: 1, + serial.PARITY_ODD: 2, + serial.PARITY_EVEN: 3, + serial.PARITY_MARK: 4, + serial.PARITY_SPACE: 5, +} +RFC2217_REVERSE_PARITY_MAP = dict((v, k) for k, v in RFC2217_PARITY_MAP.items()) + +RFC2217_STOPBIT_MAP = { + serial.STOPBITS_ONE: 1, + serial.STOPBITS_ONE_POINT_FIVE: 3, + serial.STOPBITS_TWO: 2, +} +RFC2217_REVERSE_STOPBIT_MAP = dict((v, k) for k, v in RFC2217_STOPBIT_MAP.items()) + +# Telnet filter states +M_NORMAL = 0 +M_IAC_SEEN = 1 +M_NEGOTIATE = 2 + +# TelnetOption and TelnetSubnegotiation states +REQUESTED = 'REQUESTED' +ACTIVE = 'ACTIVE' +INACTIVE = 'INACTIVE' +REALLY_INACTIVE = 'REALLY_INACTIVE' + + +class TelnetOption(object): + """Manage a single telnet option, keeps track of DO/DONT WILL/WONT.""" + + def __init__(self, connection, name, option, send_yes, send_no, ack_yes, + ack_no, initial_state, activation_callback=None): + """\ + Initialize option. + :param connection: connection used to transmit answers + :param name: a readable name for debug outputs + :param send_yes: what to send when option is to be enabled. + :param send_no: what to send when option is to be disabled. + :param ack_yes: what to expect when remote agrees on option. + :param ack_no: what to expect when remote disagrees on option. + :param initial_state: options initialized with REQUESTED are tried to + be enabled on startup. use INACTIVE for all others. + """ + self.connection = connection + self.name = name + self.option = option + self.send_yes = send_yes + self.send_no = send_no + self.ack_yes = ack_yes + self.ack_no = ack_no + self.state = initial_state + self.active = False + self.activation_callback = activation_callback + + def __repr__(self): + """String for debug outputs""" + return "{o.name}:{o.active}({o.state})".format(o=self) + + def process_incoming(self, command): + """\ + A DO/DONT/WILL/WONT was received for this option, update state and + answer when needed. + """ + if command == self.ack_yes: + if self.state is REQUESTED: + self.state = ACTIVE + self.active = True + if self.activation_callback is not None: + self.activation_callback() + elif self.state is ACTIVE: + pass + elif self.state is INACTIVE: + self.state = ACTIVE + self.connection.telnet_send_option(self.send_yes, self.option) + self.active = True + if self.activation_callback is not None: + self.activation_callback() + elif self.state is REALLY_INACTIVE: + self.connection.telnet_send_option(self.send_no, self.option) + else: + raise ValueError('option in illegal state {!r}'.format(self)) + elif command == self.ack_no: + if self.state is REQUESTED: + self.state = INACTIVE + self.active = False + elif self.state is ACTIVE: + self.state = INACTIVE + self.connection.telnet_send_option(self.send_no, self.option) + self.active = False + elif self.state is INACTIVE: + pass + elif self.state is REALLY_INACTIVE: + pass + else: + raise ValueError('option in illegal state {!r}'.format(self)) + + +class TelnetSubnegotiation(object): + """\ + A object to handle subnegotiation of options. In this case actually + sub-sub options for RFC 2217. It is used to track com port options. + """ + + def __init__(self, connection, name, option, ack_option=None): + if ack_option is None: + ack_option = option + self.connection = connection + self.name = name + self.option = option + self.value = None + self.ack_option = ack_option + self.state = INACTIVE + + def __repr__(self): + """String for debug outputs.""" + return "{sn.name}:{sn.state}".format(sn=self) + + def set(self, value): + """\ + Request a change of the value. a request is sent to the server. if + the client needs to know if the change is performed he has to check the + state of this object. + """ + self.value = value + self.state = REQUESTED + self.connection.rfc2217_send_subnegotiation(self.option, self.value) + if self.connection.logger: + self.connection.logger.debug("SB Requesting {} -> {!r}".format(self.name, self.value)) + + def is_ready(self): + """\ + Check if answer from server has been received. when server rejects + the change, raise a ValueError. + """ + if self.state == REALLY_INACTIVE: + raise ValueError("remote rejected value for option {!r}".format(self.name)) + return self.state == ACTIVE + # add property to have a similar interface as TelnetOption + active = property(is_ready) + + def wait(self, timeout=3): + """\ + Wait until the subnegotiation has been acknowledged or timeout. It + can also throw a value error when the answer from the server does not + match the value sent. + """ + timeout_timer = Timeout(timeout) + while not timeout_timer.expired(): + time.sleep(0.05) # prevent 100% CPU load + if self.is_ready(): + break + else: + raise SerialException("timeout while waiting for option {!r}".format(self.name)) + + def check_answer(self, suboption): + """\ + Check an incoming subnegotiation block. The parameter already has + cut off the header like sub option number and com port option value. + """ + if self.value == suboption[:len(self.value)]: + self.state = ACTIVE + else: + # error propagation done in is_ready + self.state = REALLY_INACTIVE + if self.connection.logger: + self.connection.logger.debug("SB Answer {} -> {!r} -> {}".format(self.name, suboption, self.state)) + + +class Serial(SerialBase): + """Serial port implementation for RFC 2217 remote serial ports.""" + + BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200) + + def __init__(self, *args, **kwargs): + self._thread = None + self._socket = None + self._linestate = 0 + self._modemstate = None + self._modemstate_timeout = Timeout(-1) + self._remote_suspend_flow = False + self._write_lock = None + self.logger = None + self._ignore_set_control_answer = False + self._poll_modem_state = False + self._network_timeout = 3 + self._telnet_options = None + self._rfc2217_port_settings = None + self._rfc2217_options = None + self._read_buffer = None + super(Serial, self).__init__(*args, **kwargs) # must be last call in case of auto-open + + def open(self): + """\ + Open port with current settings. This may throw a SerialException + if the port cannot be opened. + """ + self.logger = None + self._ignore_set_control_answer = False + self._poll_modem_state = False + self._network_timeout = 3 + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if self.is_open: + raise SerialException("Port is already open.") + try: + self._socket = socket.create_connection(self.from_url(self.portstr), timeout=5) # XXX good value? + self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + except Exception as msg: + self._socket = None + raise SerialException("Could not open port {}: {}".format(self.portstr, msg)) + + # use a thread save queue as buffer. it also simplifies implementing + # the read timeout + self._read_buffer = Queue.Queue() + # to ensure that user writes does not interfere with internal + # telnet/rfc2217 options establish a lock + self._write_lock = threading.Lock() + # name the following separately so that, below, a check can be easily done + mandadory_options = [ + TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE), + TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED), + ] + # all supported telnet options + self._telnet_options = [ + TelnetOption(self, 'ECHO', ECHO, DO, DONT, WILL, WONT, REQUESTED), + TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED), + TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, REQUESTED), + TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, INACTIVE), + TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, REQUESTED), + ] + mandadory_options + # RFC 2217 specific states + # COM port settings + self._rfc2217_port_settings = { + 'baudrate': TelnetSubnegotiation(self, 'baudrate', SET_BAUDRATE, SERVER_SET_BAUDRATE), + 'datasize': TelnetSubnegotiation(self, 'datasize', SET_DATASIZE, SERVER_SET_DATASIZE), + 'parity': TelnetSubnegotiation(self, 'parity', SET_PARITY, SERVER_SET_PARITY), + 'stopsize': TelnetSubnegotiation(self, 'stopsize', SET_STOPSIZE, SERVER_SET_STOPSIZE), + } + # There are more subnegotiation objects, combine all in one dictionary + # for easy access + self._rfc2217_options = { + 'purge': TelnetSubnegotiation(self, 'purge', PURGE_DATA, SERVER_PURGE_DATA), + 'control': TelnetSubnegotiation(self, 'control', SET_CONTROL, SERVER_SET_CONTROL), + } + self._rfc2217_options.update(self._rfc2217_port_settings) + # cache for line and modem states that the server sends to us + self._linestate = 0 + self._modemstate = None + self._modemstate_timeout = Timeout(-1) + # RFC 2217 flow control between server and client + self._remote_suspend_flow = False + + self.is_open = True + self._thread = threading.Thread(target=self._telnet_read_loop) + self._thread.setDaemon(True) + self._thread.setName('pySerial RFC 2217 reader thread for {}'.format(self._port)) + self._thread.start() + + try: # must clean-up if open fails + # negotiate Telnet/RFC 2217 -> send initial requests + for option in self._telnet_options: + if option.state is REQUESTED: + self.telnet_send_option(option.send_yes, option.option) + # now wait until important options are negotiated + timeout = Timeout(self._network_timeout) + while not timeout.expired(): + time.sleep(0.05) # prevent 100% CPU load + if sum(o.active for o in mandadory_options) == sum(o.state != INACTIVE for o in mandadory_options): + break + else: + raise SerialException( + "Remote does not seem to support RFC2217 or BINARY mode {!r}".format(mandadory_options)) + if self.logger: + self.logger.info("Negotiated options: {}".format(self._telnet_options)) + + # fine, go on, set RFC 2271 specific things + self._reconfigure_port() + # all things set up get, now a clean start + if not self._dsrdtr: + self._update_dtr_state() + if not self._rtscts: + self._update_rts_state() + self.reset_input_buffer() + self.reset_output_buffer() + except: + self.close() + raise + + def _reconfigure_port(self): + """Set communication parameters on opened port.""" + if self._socket is None: + raise SerialException("Can only operate on open ports") + + # if self._timeout != 0 and self._interCharTimeout is not None: + # XXX + + if self._write_timeout is not None: + raise NotImplementedError('write_timeout is currently not supported') + # XXX + + # Setup the connection + # to get good performance, all parameter changes are sent first... + if not 0 < self._baudrate < 2 ** 32: + raise ValueError("invalid baudrate: {!r}".format(self._baudrate)) + self._rfc2217_port_settings['baudrate'].set(struct.pack(b'!I', self._baudrate)) + self._rfc2217_port_settings['datasize'].set(struct.pack(b'!B', self._bytesize)) + self._rfc2217_port_settings['parity'].set(struct.pack(b'!B', RFC2217_PARITY_MAP[self._parity])) + self._rfc2217_port_settings['stopsize'].set(struct.pack(b'!B', RFC2217_STOPBIT_MAP[self._stopbits])) + + # and now wait until parameters are active + items = self._rfc2217_port_settings.values() + if self.logger: + self.logger.debug("Negotiating settings: {}".format(items)) + timeout = Timeout(self._network_timeout) + while not timeout.expired(): + time.sleep(0.05) # prevent 100% CPU load + if sum(o.active for o in items) == len(items): + break + else: + raise SerialException("Remote does not accept parameter change (RFC2217): {!r}".format(items)) + if self.logger: + self.logger.info("Negotiated settings: {}".format(items)) + + if self._rtscts and self._xonxoff: + raise ValueError('xonxoff and rtscts together are not supported') + elif self._rtscts: + self.rfc2217_set_control(SET_CONTROL_USE_HW_FLOW_CONTROL) + elif self._xonxoff: + self.rfc2217_set_control(SET_CONTROL_USE_SW_FLOW_CONTROL) + else: + self.rfc2217_set_control(SET_CONTROL_USE_NO_FLOW_CONTROL) + + def close(self): + """Close port""" + self.is_open = False + if self._socket: + try: + self._socket.shutdown(socket.SHUT_RDWR) + self._socket.close() + except: + # ignore errors. + pass + if self._thread: + self._thread.join(7) # XXX more than socket timeout + self._thread = None + # in case of quick reconnects, give the server some time + time.sleep(0.3) + self._socket = None + + def from_url(self, url): + """\ + extract host and port from an URL string, other settings are extracted + an stored in instance + """ + parts = urlparse.urlsplit(url) + if parts.scheme != "rfc2217": + raise SerialException( + 'expected a string in the form ' + '"rfc2217://:[?option[&option...]]": ' + 'not starting with rfc2217:// ({!r})'.format(parts.scheme)) + try: + # process options now, directly altering self + for option, values in urlparse.parse_qs(parts.query, True).items(): + if option == 'logging': + logging.basicConfig() # XXX is that good to call it here? + self.logger = logging.getLogger('pySerial.rfc2217') + self.logger.setLevel(LOGGER_LEVELS[values[0]]) + self.logger.debug('enabled logging') + elif option == 'ign_set_control': + self._ignore_set_control_answer = True + elif option == 'poll_modem': + self._poll_modem_state = True + elif option == 'timeout': + self._network_timeout = float(values[0]) + else: + raise ValueError('unknown option: {!r}'.format(option)) + if not 0 <= parts.port < 65536: + raise ValueError("port not in range 0...65535") + except ValueError as e: + raise SerialException( + 'expected a string in the form ' + '"rfc2217://:[?option[&option...]]": {}'.format(e)) + return (parts.hostname, parts.port) + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def in_waiting(self): + """Return the number of bytes currently in the input buffer.""" + if not self.is_open: + raise portNotOpenError + return self._read_buffer.qsize() + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + data = bytearray() + try: + timeout = Timeout(self._timeout) + while len(data) < size: + if self._thread is None: + raise SerialException('connection failed (reader thread died)') + data += self._read_buffer.get(True, timeout.time_left()) + if timeout.expired(): + break + except Queue.Empty: # -> timeout + pass + return bytes(data) + + def write(self, data): + """\ + Output the given byte string over the serial port. Can block if the + connection is blocked. May raise SerialException if the connection is + closed. + """ + if not self.is_open: + raise portNotOpenError + # XXX use protocol_socket's write + with self._write_lock: + try: + self._socket.sendall(to_bytes(data).replace(IAC, IAC_DOUBLED)) + except socket.error as e: + raise SerialException("connection failed (socket error): {}".format(e)) + return len(data) + + def reset_input_buffer(self): + """Clear input buffer, discarding all that is in the buffer.""" + if not self.is_open: + raise portNotOpenError + self.rfc2217_send_purge(PURGE_RECEIVE_BUFFER) + # empty read buffer + while self._read_buffer.qsize(): + self._read_buffer.get(False) + + def reset_output_buffer(self): + """\ + Clear output buffer, aborting the current output and + discarding all that is in the buffer. + """ + if not self.is_open: + raise portNotOpenError + self.rfc2217_send_purge(PURGE_TRANSMIT_BUFFER) + + def _update_break_state(self): + """\ + Set break: Controls TXD. When active, to transmitting is + possible. + """ + if not self.is_open: + raise portNotOpenError + if self.logger: + self.logger.info('set BREAK to {}'.format('active' if self._break_state else 'inactive')) + if self._break_state: + self.rfc2217_set_control(SET_CONTROL_BREAK_ON) + else: + self.rfc2217_set_control(SET_CONTROL_BREAK_OFF) + + def _update_rts_state(self): + """Set terminal status line: Request To Send.""" + if not self.is_open: + raise portNotOpenError + if self.logger: + self.logger.info('set RTS to {}'.format('active' if self._rts_state else 'inactive')) + if self._rts_state: + self.rfc2217_set_control(SET_CONTROL_RTS_ON) + else: + self.rfc2217_set_control(SET_CONTROL_RTS_OFF) + + def _update_dtr_state(self): + """Set terminal status line: Data Terminal Ready.""" + if not self.is_open: + raise portNotOpenError + if self.logger: + self.logger.info('set DTR to {}'.format('active' if self._dtr_state else 'inactive')) + if self._dtr_state: + self.rfc2217_set_control(SET_CONTROL_DTR_ON) + else: + self.rfc2217_set_control(SET_CONTROL_DTR_OFF) + + @property + def cts(self): + """Read terminal status line: Clear To Send.""" + if not self.is_open: + raise portNotOpenError + return bool(self.get_modem_state() & MODEMSTATE_MASK_CTS) + + @property + def dsr(self): + """Read terminal status line: Data Set Ready.""" + if not self.is_open: + raise portNotOpenError + return bool(self.get_modem_state() & MODEMSTATE_MASK_DSR) + + @property + def ri(self): + """Read terminal status line: Ring Indicator.""" + if not self.is_open: + raise portNotOpenError + return bool(self.get_modem_state() & MODEMSTATE_MASK_RI) + + @property + def cd(self): + """Read terminal status line: Carrier Detect.""" + if not self.is_open: + raise portNotOpenError + return bool(self.get_modem_state() & MODEMSTATE_MASK_CD) + + # - - - platform specific - - - + # None so far + + # - - - RFC2217 specific - - - + + def _telnet_read_loop(self): + """Read loop for the socket.""" + mode = M_NORMAL + suboption = None + try: + while self.is_open: + try: + data = self._socket.recv(1024) + except socket.timeout: + # just need to get out of recv form time to time to check if + # still alive + continue + except socket.error as e: + # connection fails -> terminate loop + if self.logger: + self.logger.debug("socket error in reader thread: {}".format(e)) + break + if not data: + break # lost connection + for byte in iterbytes(data): + if mode == M_NORMAL: + # interpret as command or as data + if byte == IAC: + mode = M_IAC_SEEN + else: + # store data in read buffer or sub option buffer + # depending on state + if suboption is not None: + suboption += byte + else: + self._read_buffer.put(byte) + elif mode == M_IAC_SEEN: + if byte == IAC: + # interpret as command doubled -> insert character + # itself + if suboption is not None: + suboption += IAC + else: + self._read_buffer.put(IAC) + mode = M_NORMAL + elif byte == SB: + # sub option start + suboption = bytearray() + mode = M_NORMAL + elif byte == SE: + # sub option end -> process it now + self._telnet_process_subnegotiation(bytes(suboption)) + suboption = None + mode = M_NORMAL + elif byte in (DO, DONT, WILL, WONT): + # negotiation + telnet_command = byte + mode = M_NEGOTIATE + else: + # other telnet commands + self._telnet_process_command(byte) + mode = M_NORMAL + elif mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following + self._telnet_negotiate_option(telnet_command, byte) + mode = M_NORMAL + finally: + self._thread = None + if self.logger: + self.logger.debug("read thread terminated") + + # - incoming telnet commands and options + + def _telnet_process_command(self, command): + """Process commands other than DO, DONT, WILL, WONT.""" + # Currently none. RFC2217 only uses negotiation and subnegotiation. + if self.logger: + self.logger.warning("ignoring Telnet command: {!r}".format(command)) + + def _telnet_negotiate_option(self, command, option): + """Process incoming DO, DONT, WILL, WONT.""" + # check our registered telnet options and forward command to them + # they know themselves if they have to answer or not + known = False + for item in self._telnet_options: + # can have more than one match! as some options are duplicated for + # 'us' and 'them' + if item.option == option: + item.process_incoming(command) + known = True + if not known: + # handle unknown options + # only answer to positive requests and deny them + if command == WILL or command == DO: + self.telnet_send_option((DONT if command == WILL else WONT), option) + if self.logger: + self.logger.warning("rejected Telnet option: {!r}".format(option)) + + def _telnet_process_subnegotiation(self, suboption): + """Process subnegotiation, the data between IAC SB and IAC SE.""" + if suboption[0:1] == COM_PORT_OPTION: + if suboption[1:2] == SERVER_NOTIFY_LINESTATE and len(suboption) >= 3: + self._linestate = ord(suboption[2:3]) # ensure it is a number + if self.logger: + self.logger.info("NOTIFY_LINESTATE: {}".format(self._linestate)) + elif suboption[1:2] == SERVER_NOTIFY_MODEMSTATE and len(suboption) >= 3: + self._modemstate = ord(suboption[2:3]) # ensure it is a number + if self.logger: + self.logger.info("NOTIFY_MODEMSTATE: {}".format(self._modemstate)) + # update time when we think that a poll would make sense + self._modemstate_timeout.restart(0.3) + elif suboption[1:2] == FLOWCONTROL_SUSPEND: + self._remote_suspend_flow = True + elif suboption[1:2] == FLOWCONTROL_RESUME: + self._remote_suspend_flow = False + else: + for item in self._rfc2217_options.values(): + if item.ack_option == suboption[1:2]: + #~ print "processing COM_PORT_OPTION: %r" % list(suboption[1:]) + item.check_answer(bytes(suboption[2:])) + break + else: + if self.logger: + self.logger.warning("ignoring COM_PORT_OPTION: {!r}".format(suboption)) + else: + if self.logger: + self.logger.warning("ignoring subnegotiation: {!r}".format(suboption)) + + # - outgoing telnet commands and options + + def _internal_raw_write(self, data): + """internal socket write with no data escaping. used to send telnet stuff.""" + with self._write_lock: + self._socket.sendall(data) + + def telnet_send_option(self, action, option): + """Send DO, DONT, WILL, WONT.""" + self._internal_raw_write(IAC + action + option) + + def rfc2217_send_subnegotiation(self, option, value=b''): + """Subnegotiation of RFC2217 parameters.""" + value = value.replace(IAC, IAC_DOUBLED) + self._internal_raw_write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE) + + def rfc2217_send_purge(self, value): + """\ + Send purge request to the remote. + (PURGE_RECEIVE_BUFFER / PURGE_TRANSMIT_BUFFER / PURGE_BOTH_BUFFERS) + """ + item = self._rfc2217_options['purge'] + item.set(value) # transmit desired purge type + item.wait(self._network_timeout) # wait for acknowledge from the server + + def rfc2217_set_control(self, value): + """transmit change of control line to remote""" + item = self._rfc2217_options['control'] + item.set(value) # transmit desired control type + if self._ignore_set_control_answer: + # answers are ignored when option is set. compatibility mode for + # servers that answer, but not the expected one... (or no answer + # at all) i.e. sredird + time.sleep(0.1) # this helps getting the unit tests passed + else: + item.wait(self._network_timeout) # wait for acknowledge from the server + + def rfc2217_flow_server_ready(self): + """\ + check if server is ready to receive data. block for some time when + not. + """ + #~ if self._remote_suspend_flow: + #~ wait--- + + def get_modem_state(self): + """\ + get last modem state (cached value. If value is "old", request a new + one. This cache helps that we don't issue to many requests when e.g. all + status lines, one after the other is queried by the user (CTS, DSR + etc.) + """ + # active modem state polling enabled? is the value fresh enough? + if self._poll_modem_state and self._modemstate_timeout.expired(): + if self.logger: + self.logger.debug('polling modem state') + # when it is older, request an update + self.rfc2217_send_subnegotiation(NOTIFY_MODEMSTATE) + timeout = Timeout(self._network_timeout) + while not timeout.expired(): + time.sleep(0.05) # prevent 100% CPU load + # when expiration time is updated, it means that there is a new + # value + if not self._modemstate_timeout.expired(): + break + else: + if self.logger: + self.logger.warning('poll for modem state failed') + # even when there is a timeout, do not generate an error just + # return the last known value. this way we can support buggy + # servers that do not respond to polls, but send automatic + # updates. + if self._modemstate is not None: + if self.logger: + self.logger.debug('using cached modem state') + return self._modemstate + else: + # never received a notification from the server + raise SerialException("remote sends no NOTIFY_MODEMSTATE") + + +############################################################################# +# The following is code that helps implementing an RFC 2217 server. + +class PortManager(object): + """\ + This class manages the state of Telnet and RFC 2217. It needs a serial + instance and a connection to work with. Connection is expected to implement + a (thread safe) write function, that writes the string to the network. + """ + + def __init__(self, serial_port, connection, logger=None): + self.serial = serial_port + self.connection = connection + self.logger = logger + self._client_is_rfc2217 = False + + # filter state machine + self.mode = M_NORMAL + self.suboption = None + self.telnet_command = None + + # states for modem/line control events + self.modemstate_mask = 255 + self.last_modemstate = None + self.linstate_mask = 0 + + # all supported telnet options + self._telnet_options = [ + TelnetOption(self, 'ECHO', ECHO, WILL, WONT, DO, DONT, REQUESTED), + TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED), + TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, INACTIVE), + TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE), + TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, REQUESTED), + TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED, self._client_ok), + TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, INACTIVE, self._client_ok), + ] + + # negotiate Telnet/RFC2217 -> send initial requests + if self.logger: + self.logger.debug("requesting initial Telnet/RFC 2217 options") + for option in self._telnet_options: + if option.state is REQUESTED: + self.telnet_send_option(option.send_yes, option.option) + # issue 1st modem state notification + + def _client_ok(self): + """\ + callback of telnet option. It gets called when option is activated. + This one here is used to detect when the client agrees on RFC 2217. A + flag is set so that other functions like check_modem_lines know if the + client is OK. + """ + # The callback is used for we and they so if one party agrees, we're + # already happy. it seems not all servers do the negotiation correctly + # and i guess there are incorrect clients too.. so be happy if client + # answers one or the other positively. + self._client_is_rfc2217 = True + if self.logger: + self.logger.info("client accepts RFC 2217") + # this is to ensure that the client gets a notification, even if there + # was no change + self.check_modem_lines(force_notification=True) + + # - outgoing telnet commands and options + + def telnet_send_option(self, action, option): + """Send DO, DONT, WILL, WONT.""" + self.connection.write(IAC + action + option) + + def rfc2217_send_subnegotiation(self, option, value=b''): + """Subnegotiation of RFC 2217 parameters.""" + value = value.replace(IAC, IAC_DOUBLED) + self.connection.write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE) + + # - check modem lines, needs to be called periodically from user to + # establish polling + + def check_modem_lines(self, force_notification=False): + """\ + read control lines from serial port and compare the last value sent to remote. + send updates on changes. + """ + modemstate = ( + (self.serial.cts and MODEMSTATE_MASK_CTS) | + (self.serial.dsr and MODEMSTATE_MASK_DSR) | + (self.serial.ri and MODEMSTATE_MASK_RI) | + (self.serial.cd and MODEMSTATE_MASK_CD)) + # check what has changed + deltas = modemstate ^ (self.last_modemstate or 0) # when last is None -> 0 + if deltas & MODEMSTATE_MASK_CTS: + modemstate |= MODEMSTATE_MASK_CTS_CHANGE + if deltas & MODEMSTATE_MASK_DSR: + modemstate |= MODEMSTATE_MASK_DSR_CHANGE + if deltas & MODEMSTATE_MASK_RI: + modemstate |= MODEMSTATE_MASK_RI_CHANGE + if deltas & MODEMSTATE_MASK_CD: + modemstate |= MODEMSTATE_MASK_CD_CHANGE + # if new state is different and the mask allows this change, send + # notification. suppress notifications when client is not rfc2217 + if modemstate != self.last_modemstate or force_notification: + if (self._client_is_rfc2217 and (modemstate & self.modemstate_mask)) or force_notification: + self.rfc2217_send_subnegotiation( + SERVER_NOTIFY_MODEMSTATE, + to_bytes([modemstate & self.modemstate_mask])) + if self.logger: + self.logger.info("NOTIFY_MODEMSTATE: {}".format(modemstate)) + # save last state, but forget about deltas. + # otherwise it would also notify about changing deltas which is + # probably not very useful + self.last_modemstate = modemstate & 0xf0 + + # - outgoing data escaping + + def escape(self, data): + """\ + This generator function is for the user. All outgoing data has to be + properly escaped, so that no IAC character in the data stream messes up + the Telnet state machine in the server. + + socket.sendall(escape(data)) + """ + for byte in iterbytes(data): + if byte == IAC: + yield IAC + yield IAC + else: + yield byte + + # - incoming data filter + + def filter(self, data): + """\ + Handle a bunch of incoming bytes. This is a generator. It will yield + all characters not of interest for Telnet/RFC 2217. + + The idea is that the reader thread pushes data from the socket through + this filter: + + for byte in filter(socket.recv(1024)): + # do things like CR/LF conversion/whatever + # and write data to the serial port + serial.write(byte) + + (socket error handling code left as exercise for the reader) + """ + for byte in iterbytes(data): + if self.mode == M_NORMAL: + # interpret as command or as data + if byte == IAC: + self.mode = M_IAC_SEEN + else: + # store data in sub option buffer or pass it to our + # consumer depending on state + if self.suboption is not None: + self.suboption += byte + else: + yield byte + elif self.mode == M_IAC_SEEN: + if byte == IAC: + # interpret as command doubled -> insert character + # itself + if self.suboption is not None: + self.suboption += byte + else: + yield byte + self.mode = M_NORMAL + elif byte == SB: + # sub option start + self.suboption = bytearray() + self.mode = M_NORMAL + elif byte == SE: + # sub option end -> process it now + self._telnet_process_subnegotiation(bytes(self.suboption)) + self.suboption = None + self.mode = M_NORMAL + elif byte in (DO, DONT, WILL, WONT): + # negotiation + self.telnet_command = byte + self.mode = M_NEGOTIATE + else: + # other telnet commands + self._telnet_process_command(byte) + self.mode = M_NORMAL + elif self.mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following + self._telnet_negotiate_option(self.telnet_command, byte) + self.mode = M_NORMAL + + # - incoming telnet commands and options + + def _telnet_process_command(self, command): + """Process commands other than DO, DONT, WILL, WONT.""" + # Currently none. RFC2217 only uses negotiation and subnegotiation. + if self.logger: + self.logger.warning("ignoring Telnet command: {!r}".format(command)) + + def _telnet_negotiate_option(self, command, option): + """Process incoming DO, DONT, WILL, WONT.""" + # check our registered telnet options and forward command to them + # they know themselves if they have to answer or not + known = False + for item in self._telnet_options: + # can have more than one match! as some options are duplicated for + # 'us' and 'them' + if item.option == option: + item.process_incoming(command) + known = True + if not known: + # handle unknown options + # only answer to positive requests and deny them + if command == WILL or command == DO: + self.telnet_send_option((DONT if command == WILL else WONT), option) + if self.logger: + self.logger.warning("rejected Telnet option: {!r}".format(option)) + + def _telnet_process_subnegotiation(self, suboption): + """Process subnegotiation, the data between IAC SB and IAC SE.""" + if suboption[0:1] == COM_PORT_OPTION: + if self.logger: + self.logger.debug('received COM_PORT_OPTION: {!r}'.format(suboption)) + if suboption[1:2] == SET_BAUDRATE: + backup = self.serial.baudrate + try: + (baudrate,) = struct.unpack(b"!I", suboption[2:6]) + if baudrate != 0: + self.serial.baudrate = baudrate + except ValueError as e: + if self.logger: + self.logger.error("failed to set baud rate: {}".format(e)) + self.serial.baudrate = backup + else: + if self.logger: + self.logger.info("{} baud rate: {}".format('set' if baudrate else 'get', self.serial.baudrate)) + self.rfc2217_send_subnegotiation(SERVER_SET_BAUDRATE, struct.pack(b"!I", self.serial.baudrate)) + elif suboption[1:2] == SET_DATASIZE: + backup = self.serial.bytesize + try: + (datasize,) = struct.unpack(b"!B", suboption[2:3]) + if datasize != 0: + self.serial.bytesize = datasize + except ValueError as e: + if self.logger: + self.logger.error("failed to set data size: {}".format(e)) + self.serial.bytesize = backup + else: + if self.logger: + self.logger.info("{} data size: {}".format('set' if datasize else 'get', self.serial.bytesize)) + self.rfc2217_send_subnegotiation(SERVER_SET_DATASIZE, struct.pack(b"!B", self.serial.bytesize)) + elif suboption[1:2] == SET_PARITY: + backup = self.serial.parity + try: + parity = struct.unpack(b"!B", suboption[2:3])[0] + if parity != 0: + self.serial.parity = RFC2217_REVERSE_PARITY_MAP[parity] + except ValueError as e: + if self.logger: + self.logger.error("failed to set parity: {}".format(e)) + self.serial.parity = backup + else: + if self.logger: + self.logger.info("{} parity: {}".format('set' if parity else 'get', self.serial.parity)) + self.rfc2217_send_subnegotiation( + SERVER_SET_PARITY, + struct.pack(b"!B", RFC2217_PARITY_MAP[self.serial.parity])) + elif suboption[1:2] == SET_STOPSIZE: + backup = self.serial.stopbits + try: + stopbits = struct.unpack(b"!B", suboption[2:3])[0] + if stopbits != 0: + self.serial.stopbits = RFC2217_REVERSE_STOPBIT_MAP[stopbits] + except ValueError as e: + if self.logger: + self.logger.error("failed to set stop bits: {}".format(e)) + self.serial.stopbits = backup + else: + if self.logger: + self.logger.info("{} stop bits: {}".format('set' if stopbits else 'get', self.serial.stopbits)) + self.rfc2217_send_subnegotiation( + SERVER_SET_STOPSIZE, + struct.pack(b"!B", RFC2217_STOPBIT_MAP[self.serial.stopbits])) + elif suboption[1:2] == SET_CONTROL: + if suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING: + if self.serial.xonxoff: + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL) + elif self.serial.rtscts: + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL) + else: + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL) + elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL: + self.serial.xonxoff = False + self.serial.rtscts = False + if self.logger: + self.logger.info("changed flow control to None") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL) + elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTROL: + self.serial.xonxoff = True + if self.logger: + self.logger.info("changed flow control to XON/XOFF") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL) + elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTROL: + self.serial.rtscts = True + if self.logger: + self.logger.info("changed flow control to RTS/CTS") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL) + elif suboption[2:3] == SET_CONTROL_REQ_BREAK_STATE: + if self.logger: + self.logger.warning("requested break state - not implemented") + pass # XXX needs cached value + elif suboption[2:3] == SET_CONTROL_BREAK_ON: + self.serial.break_condition = True + if self.logger: + self.logger.info("changed BREAK to active") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_ON) + elif suboption[2:3] == SET_CONTROL_BREAK_OFF: + self.serial.break_condition = False + if self.logger: + self.logger.info("changed BREAK to inactive") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_OFF) + elif suboption[2:3] == SET_CONTROL_REQ_DTR: + if self.logger: + self.logger.warning("requested DTR state - not implemented") + pass # XXX needs cached value + elif suboption[2:3] == SET_CONTROL_DTR_ON: + self.serial.dtr = True + if self.logger: + self.logger.info("changed DTR to active") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_ON) + elif suboption[2:3] == SET_CONTROL_DTR_OFF: + self.serial.dtr = False + if self.logger: + self.logger.info("changed DTR to inactive") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_OFF) + elif suboption[2:3] == SET_CONTROL_REQ_RTS: + if self.logger: + self.logger.warning("requested RTS state - not implemented") + pass # XXX needs cached value + #~ self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON) + elif suboption[2:3] == SET_CONTROL_RTS_ON: + self.serial.rts = True + if self.logger: + self.logger.info("changed RTS to active") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON) + elif suboption[2:3] == SET_CONTROL_RTS_OFF: + self.serial.rts = False + if self.logger: + self.logger.info("changed RTS to inactive") + self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_OFF) + #~ elif suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING_IN: + #~ elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL_IN: + #~ elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTOL_IN: + #~ elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTOL_IN: + #~ elif suboption[2:3] == SET_CONTROL_USE_DCD_FLOW_CONTROL: + #~ elif suboption[2:3] == SET_CONTROL_USE_DTR_FLOW_CONTROL: + #~ elif suboption[2:3] == SET_CONTROL_USE_DSR_FLOW_CONTROL: + elif suboption[1:2] == NOTIFY_LINESTATE: + # client polls for current state + self.rfc2217_send_subnegotiation( + SERVER_NOTIFY_LINESTATE, + to_bytes([0])) # sorry, nothing like that implemented + elif suboption[1:2] == NOTIFY_MODEMSTATE: + if self.logger: + self.logger.info("request for modem state") + # client polls for current state + self.check_modem_lines(force_notification=True) + elif suboption[1:2] == FLOWCONTROL_SUSPEND: + if self.logger: + self.logger.info("suspend") + self._remote_suspend_flow = True + elif suboption[1:2] == FLOWCONTROL_RESUME: + if self.logger: + self.logger.info("resume") + self._remote_suspend_flow = False + elif suboption[1:2] == SET_LINESTATE_MASK: + self.linstate_mask = ord(suboption[2:3]) # ensure it is a number + if self.logger: + self.logger.info("line state mask: 0x{:02x}".format(self.linstate_mask)) + elif suboption[1:2] == SET_MODEMSTATE_MASK: + self.modemstate_mask = ord(suboption[2:3]) # ensure it is a number + if self.logger: + self.logger.info("modem state mask: 0x{:02x}".format(self.modemstate_mask)) + elif suboption[1:2] == PURGE_DATA: + if suboption[2:3] == PURGE_RECEIVE_BUFFER: + self.serial.reset_input_buffer() + if self.logger: + self.logger.info("purge in") + self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_RECEIVE_BUFFER) + elif suboption[2:3] == PURGE_TRANSMIT_BUFFER: + self.serial.reset_output_buffer() + if self.logger: + self.logger.info("purge out") + self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_TRANSMIT_BUFFER) + elif suboption[2:3] == PURGE_BOTH_BUFFERS: + self.serial.reset_input_buffer() + self.serial.reset_output_buffer() + if self.logger: + self.logger.info("purge both") + self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_BOTH_BUFFERS) + else: + if self.logger: + self.logger.error("undefined PURGE_DATA: {!r}".format(list(suboption[2:]))) + else: + if self.logger: + self.logger.error("undefined COM_PORT_OPTION: {!r}".format(list(suboption[1:]))) + else: + if self.logger: + self.logger.warning("unknown subnegotiation: {!r}".format(suboption)) + + +# simple client test +if __name__ == '__main__': + import sys + s = Serial('rfc2217://localhost:7000', 115200) + sys.stdout.write('{}\n'.format(s)) + + sys.stdout.write("write...\n") + s.write(b"hello\n") + s.flush() + sys.stdout.write("read: {}\n".format(s.read(5))) + s.close() diff --git a/backend/venv/lib/python3.6/site-packages/serial/rfc2217.pyc b/backend/venv/lib/python3.6/site-packages/serial/rfc2217.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9d4727e4b93a66e141d98bc89a320dc1f90cd786 GIT binary patch literal 37632 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 Yea*aPx)6RFM+et7;|dSDRqaguA7HP;p#T5? literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/rs485.py b/backend/venv/lib/python3.6/site-packages/serial/rs485.py new file mode 100644 index 0000000..2939350 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/rs485.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python + +# RS485 support +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +"""\ +The settings for RS485 are stored in a dedicated object that can be applied to +serial ports (where supported). +NOTE: Some implementations may only support a subset of the settings. +""" + +import time +import serial + + +class RS485Settings(object): + def __init__( + self, + rts_level_for_tx=True, + rts_level_for_rx=False, + loopback=False, + delay_before_tx=None, + delay_before_rx=None): + self.rts_level_for_tx = rts_level_for_tx + self.rts_level_for_rx = rts_level_for_rx + self.loopback = loopback + self.delay_before_tx = delay_before_tx + self.delay_before_rx = delay_before_rx + + +class RS485(serial.Serial): + """\ + A subclass that replaces the write method with one that toggles RTS + according to the RS485 settings. + + NOTE: This may work unreliably on some serial ports (control signals not + synchronized or delayed compared to data). Using delays may be + unreliable (varying times, larger than expected) as the OS may not + support very fine grained delays (no smaller than in the order of + tens of milliseconds). + + NOTE: Some implementations support this natively. Better performance + can be expected when the native version is used. + + NOTE: The loopback property is ignored by this implementation. The actual + behavior depends on the used hardware. + + Usage: + + ser = RS485(...) + ser.rs485_mode = RS485Settings(...) + ser.write(b'hello') + """ + + def __init__(self, *args, **kwargs): + super(RS485, self).__init__(*args, **kwargs) + self._alternate_rs485_settings = None + + def write(self, b): + """Write to port, controlling RTS before and after transmitting.""" + if self._alternate_rs485_settings is not None: + # apply level for TX and optional delay + self.setRTS(self._alternate_rs485_settings.rts_level_for_tx) + if self._alternate_rs485_settings.delay_before_tx is not None: + time.sleep(self._alternate_rs485_settings.delay_before_tx) + # write and wait for data to be written + super(RS485, self).write(b) + super(RS485, self).flush() + # optional delay and apply level for RX + if self._alternate_rs485_settings.delay_before_rx is not None: + time.sleep(self._alternate_rs485_settings.delay_before_rx) + self.setRTS(self._alternate_rs485_settings.rts_level_for_rx) + else: + super(RS485, self).write(b) + + # redirect where the property stores the settings so that underlying Serial + # instance does not see them + @property + def rs485_mode(self): + """\ + Enable RS485 mode and apply new settings, set to None to disable. + See serial.rs485.RS485Settings for more info about the value. + """ + return self._alternate_rs485_settings + + @rs485_mode.setter + def rs485_mode(self, rs485_settings): + self._alternate_rs485_settings = rs485_settings diff --git a/backend/venv/lib/python3.6/site-packages/serial/rs485.pyc b/backend/venv/lib/python3.6/site-packages/serial/rs485.pyc new file mode 100644 index 0000000000000000000000000000000000000000..09650af2bd23c1a969f0d383b7e878386b173413 GIT binary patch literal 3266 zcmbtW&2Aev5azCa94k&;)UDCq3^5RI<=TnJbT>9Zq%brJpD-b5+Tq zmTEpXC4Ta@=X6~vBj>jEPWDeHn)EE@G}gto+1%)vuDtT4t(=^x zrL>h43IF6e z{0s4_BaS*y(2ubwr!CZI@C40$iS9)tUI>vvh*`j}#R7(H7BK9v0FMheHo~rheh0n( z5FOl*8GWKn2H$4>>@Ab^nZJUS#@hK%<;Q*-gF+j%%!V49H0^q0u0HFNvA&JYX){Xc z+>~rjTh!5fN>=pILg)4%CHuogX^MSQ4ii&Wi?ds2_wHtQZ{PYV`FcMDZ@+fPfMmX8 zv$CwLN}pvPW3%g#vW?9s{2c@n$oMR))J$iYrxjT?v&F*Dkjyx&7uu64-zkG=($s;i zbV@0BZXHYSY+?!>Vh4>)>$X}^_~Y|9HP$IXzIjY5AE65=Q6Mn@pNPP5gv5whXbasT zOkwgRu+|m2E2bL_xOfIRLl<&);A@G`Bg)j=DCYt$YCSh9*EG`dv@X4tGwmm~kf)`e zAj@@F=k0iGuq-_s^hl4&b6XdHBA~>30~i~8COuviFmyO617=QbeJmGMtxc(hfRC-D zBYa(kVs0y6TO(aLt`G-VS7l2#&Y%*MlIu;ns)CXzO)+%J^5aOt6*sFRZ8IoG%_ip+U zjrIND5Kal)??HGC`bm=9T&qQ1B}@?nbKR_IDEQ5_od5mdjfpnK-UxbfqAMaQYR?Et zU!e2&XTp7nhYJh~ITpVN5do+~!*5b7qi_KyrTi8UrKDS=={mYh83YMV*IBT)!BL-1 zP1pR1ymX#M$8g8kopaK=(-9ePAI-JViRgNi6YWr=BcGtTKhfbbQ-05nH%0wB5BYt; zn_wn;9dA1=-Is3K?-UTqZqc3I)o zMj;)%wq+~K?*7Lyuq`~E?g1|KBl8@Hf*$)lPzMtZ9+@%m8|nml%%rLcsYV1IU#rT^ zN-jN;gFXk4-nkG!iU;C_o#AOChw|Iu!6r=a&S*VPKcF47m*+UwSQ?pyoA3+BuOh$k zA>@>L!}kgDCs6PZ&C#1E;_Y}R`Y76sccYyc-;g-F=@@Dpbjphm(oi9|Uok-_qtDX{ zysZP&Bx8!5graIGD}A~y&-O?nCZ^|;8M$<{Gg(7(pc~o`2u&_kM_k^`CVNgBm2oa*Y1_uq?%>~pq^%OZWSVT)h z94Ydz;Tkjlv(`yJ#nzwDf@yL83gnc#O-KL~)is69vy2Oul#+l8q=uwf1Jel6jIPf; z#?rfU3Hjo4v{2gYcH_*e6IoCxz9&ka<2zg(N_`2VO T+U_A)=g2?Zcq`g!?QZ`IPWSw) literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialcli.py b/backend/venv/lib/python3.6/site-packages/serial/serialcli.py new file mode 100644 index 0000000..0727a52 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/serialcli.py @@ -0,0 +1,251 @@ +#! python +# +# Backend for .NET/Mono (IronPython), .NET >= 2 +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2008-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +import System +import System.IO.Ports +from serial.serialutil import * + +# must invoke function with byte array, make a helper to convert strings +# to byte arrays +sab = System.Array[System.Byte] + + +def as_byte_array(string): + return sab([ord(x) for x in string]) # XXX will require adaption when run with a 3.x compatible IronPython + + +class Serial(SerialBase): + """Serial port implementation for .NET/Mono.""" + + BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200) + + def open(self): + """\ + Open port with current settings. This may throw a SerialException + if the port cannot be opened. + """ + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if self.is_open: + raise SerialException("Port is already open.") + try: + self._port_handle = System.IO.Ports.SerialPort(self.portstr) + except Exception as msg: + self._port_handle = None + raise SerialException("could not open port %s: %s" % (self.portstr, msg)) + + # if RTS and/or DTR are not set before open, they default to True + if self._rts_state is None: + self._rts_state = True + if self._dtr_state is None: + self._dtr_state = True + + self._reconfigure_port() + self._port_handle.Open() + self.is_open = True + if not self._dsrdtr: + self._update_dtr_state() + if not self._rtscts: + self._update_rts_state() + self.reset_input_buffer() + + def _reconfigure_port(self): + """Set communication parameters on opened port.""" + if not self._port_handle: + raise SerialException("Can only operate on a valid port handle") + + #~ self._port_handle.ReceivedBytesThreshold = 1 + + if self._timeout is None: + self._port_handle.ReadTimeout = System.IO.Ports.SerialPort.InfiniteTimeout + else: + self._port_handle.ReadTimeout = int(self._timeout * 1000) + + # if self._timeout != 0 and self._interCharTimeout is not None: + # timeouts = (int(self._interCharTimeout * 1000),) + timeouts[1:] + + if self._write_timeout is None: + self._port_handle.WriteTimeout = System.IO.Ports.SerialPort.InfiniteTimeout + else: + self._port_handle.WriteTimeout = int(self._write_timeout * 1000) + + # Setup the connection info. + try: + self._port_handle.BaudRate = self._baudrate + except IOError as e: + # catch errors from illegal baudrate settings + raise ValueError(str(e)) + + if self._bytesize == FIVEBITS: + self._port_handle.DataBits = 5 + elif self._bytesize == SIXBITS: + self._port_handle.DataBits = 6 + elif self._bytesize == SEVENBITS: + self._port_handle.DataBits = 7 + elif self._bytesize == EIGHTBITS: + self._port_handle.DataBits = 8 + else: + raise ValueError("Unsupported number of data bits: %r" % self._bytesize) + + if self._parity == PARITY_NONE: + self._port_handle.Parity = getattr(System.IO.Ports.Parity, 'None') # reserved keyword in Py3k + elif self._parity == PARITY_EVEN: + self._port_handle.Parity = System.IO.Ports.Parity.Even + elif self._parity == PARITY_ODD: + self._port_handle.Parity = System.IO.Ports.Parity.Odd + elif self._parity == PARITY_MARK: + self._port_handle.Parity = System.IO.Ports.Parity.Mark + elif self._parity == PARITY_SPACE: + self._port_handle.Parity = System.IO.Ports.Parity.Space + else: + raise ValueError("Unsupported parity mode: %r" % self._parity) + + if self._stopbits == STOPBITS_ONE: + self._port_handle.StopBits = System.IO.Ports.StopBits.One + elif self._stopbits == STOPBITS_ONE_POINT_FIVE: + self._port_handle.StopBits = System.IO.Ports.StopBits.OnePointFive + elif self._stopbits == STOPBITS_TWO: + self._port_handle.StopBits = System.IO.Ports.StopBits.Two + else: + raise ValueError("Unsupported number of stop bits: %r" % self._stopbits) + + if self._rtscts and self._xonxoff: + self._port_handle.Handshake = System.IO.Ports.Handshake.RequestToSendXOnXOff + elif self._rtscts: + self._port_handle.Handshake = System.IO.Ports.Handshake.RequestToSend + elif self._xonxoff: + self._port_handle.Handshake = System.IO.Ports.Handshake.XOnXOff + else: + self._port_handle.Handshake = getattr(System.IO.Ports.Handshake, 'None') # reserved keyword in Py3k + + #~ def __del__(self): + #~ self.close() + + def close(self): + """Close port""" + if self.is_open: + if self._port_handle: + try: + self._port_handle.Close() + except System.IO.Ports.InvalidOperationException: + # ignore errors. can happen for unplugged USB serial devices + pass + self._port_handle = None + self.is_open = False + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def in_waiting(self): + """Return the number of characters currently in the input buffer.""" + if not self.is_open: + raise portNotOpenError + return self._port_handle.BytesToRead + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + # must use single byte reads as this is the only way to read + # without applying encodings + data = bytearray() + while size: + try: + data.append(self._port_handle.ReadByte()) + except System.TimeoutException: + break + else: + size -= 1 + return bytes(data) + + def write(self, data): + """Output the given string over the serial port.""" + if not self.is_open: + raise portNotOpenError + #~ if not isinstance(data, (bytes, bytearray)): + #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data))) + try: + # must call overloaded method with byte array argument + # as this is the only one not applying encodings + self._port_handle.Write(as_byte_array(data), 0, len(data)) + except System.TimeoutException: + raise writeTimeoutError + return len(data) + + def reset_input_buffer(self): + """Clear input buffer, discarding all that is in the buffer.""" + if not self.is_open: + raise portNotOpenError + self._port_handle.DiscardInBuffer() + + def reset_output_buffer(self): + """\ + Clear output buffer, aborting the current output and + discarding all that is in the buffer. + """ + if not self.is_open: + raise portNotOpenError + self._port_handle.DiscardOutBuffer() + + def _update_break_state(self): + """ + Set break: Controls TXD. When active, to transmitting is possible. + """ + if not self.is_open: + raise portNotOpenError + self._port_handle.BreakState = bool(self._break_state) + + def _update_rts_state(self): + """Set terminal status line: Request To Send""" + if not self.is_open: + raise portNotOpenError + self._port_handle.RtsEnable = bool(self._rts_state) + + def _update_dtr_state(self): + """Set terminal status line: Data Terminal Ready""" + if not self.is_open: + raise portNotOpenError + self._port_handle.DtrEnable = bool(self._dtr_state) + + @property + def cts(self): + """Read terminal status line: Clear To Send""" + if not self.is_open: + raise portNotOpenError + return self._port_handle.CtsHolding + + @property + def dsr(self): + """Read terminal status line: Data Set Ready""" + if not self.is_open: + raise portNotOpenError + return self._port_handle.DsrHolding + + @property + def ri(self): + """Read terminal status line: Ring Indicator""" + if not self.is_open: + raise portNotOpenError + #~ return self._port_handle.XXX + return False # XXX an error would be better + + @property + def cd(self): + """Read terminal status line: Carrier Detect""" + if not self.is_open: + raise portNotOpenError + return self._port_handle.CDHolding + + # - - platform specific - - - - + # none diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialcli.pyc b/backend/venv/lib/python3.6/site-packages/serial/serialcli.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1caa60ecf94fe9fe208506ad039925047930aad GIT binary patch literal 8308 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 +# +# SPDX-License-Identifier: BSD-3-Clause + +from serial.serialutil import * + + +def my_import(name): + mod = __import__(name) + components = name.split('.') + for comp in components[1:]: + mod = getattr(mod, comp) + return mod + + +def detect_java_comm(names): + """try given list of modules and return that imports""" + for name in names: + try: + mod = my_import(name) + mod.SerialPort + return mod + except (ImportError, AttributeError): + pass + raise ImportError("No Java Communications API implementation found") + + +# Java Communications API implementations +# http://mho.republika.pl/java/comm/ + +comm = detect_java_comm([ + 'javax.comm', # Sun/IBM + 'gnu.io', # RXTX +]) + + +def device(portnumber): + """Turn a port number into a device name""" + enum = comm.CommPortIdentifier.getPortIdentifiers() + ports = [] + while enum.hasMoreElements(): + el = enum.nextElement() + if el.getPortType() == comm.CommPortIdentifier.PORT_SERIAL: + ports.append(el) + return ports[portnumber].getName() + + +class Serial(SerialBase): + """\ + Serial port class, implemented with Java Communications API and + thus usable with jython and the appropriate java extension. + """ + + def open(self): + """\ + Open port with current settings. This may throw a SerialException + if the port cannot be opened. + """ + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if self.is_open: + raise SerialException("Port is already open.") + if type(self._port) == type(''): # strings are taken directly + portId = comm.CommPortIdentifier.getPortIdentifier(self._port) + else: + portId = comm.CommPortIdentifier.getPortIdentifier(device(self._port)) # numbers are transformed to a comport id obj + try: + self.sPort = portId.open("python serial module", 10) + except Exception as msg: + self.sPort = None + raise SerialException("Could not open port: %s" % msg) + self._reconfigurePort() + self._instream = self.sPort.getInputStream() + self._outstream = self.sPort.getOutputStream() + self.is_open = True + + def _reconfigurePort(self): + """Set communication parameters on opened port.""" + if not self.sPort: + raise SerialException("Can only operate on a valid port handle") + + self.sPort.enableReceiveTimeout(30) + if self._bytesize == FIVEBITS: + jdatabits = comm.SerialPort.DATABITS_5 + elif self._bytesize == SIXBITS: + jdatabits = comm.SerialPort.DATABITS_6 + elif self._bytesize == SEVENBITS: + jdatabits = comm.SerialPort.DATABITS_7 + elif self._bytesize == EIGHTBITS: + jdatabits = comm.SerialPort.DATABITS_8 + else: + raise ValueError("unsupported bytesize: %r" % self._bytesize) + + if self._stopbits == STOPBITS_ONE: + jstopbits = comm.SerialPort.STOPBITS_1 + elif self._stopbits == STOPBITS_ONE_POINT_FIVE: + jstopbits = comm.SerialPort.STOPBITS_1_5 + elif self._stopbits == STOPBITS_TWO: + jstopbits = comm.SerialPort.STOPBITS_2 + else: + raise ValueError("unsupported number of stopbits: %r" % self._stopbits) + + if self._parity == PARITY_NONE: + jparity = comm.SerialPort.PARITY_NONE + elif self._parity == PARITY_EVEN: + jparity = comm.SerialPort.PARITY_EVEN + elif self._parity == PARITY_ODD: + jparity = comm.SerialPort.PARITY_ODD + elif self._parity == PARITY_MARK: + jparity = comm.SerialPort.PARITY_MARK + elif self._parity == PARITY_SPACE: + jparity = comm.SerialPort.PARITY_SPACE + else: + raise ValueError("unsupported parity type: %r" % self._parity) + + jflowin = jflowout = 0 + if self._rtscts: + jflowin |= comm.SerialPort.FLOWCONTROL_RTSCTS_IN + jflowout |= comm.SerialPort.FLOWCONTROL_RTSCTS_OUT + if self._xonxoff: + jflowin |= comm.SerialPort.FLOWCONTROL_XONXOFF_IN + jflowout |= comm.SerialPort.FLOWCONTROL_XONXOFF_OUT + + self.sPort.setSerialPortParams(self._baudrate, jdatabits, jstopbits, jparity) + self.sPort.setFlowControlMode(jflowin | jflowout) + + if self._timeout >= 0: + self.sPort.enableReceiveTimeout(int(self._timeout*1000)) + else: + self.sPort.disableReceiveTimeout() + + def close(self): + """Close port""" + if self.is_open: + if self.sPort: + self._instream.close() + self._outstream.close() + self.sPort.close() + self.sPort = None + self.is_open = False + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def in_waiting(self): + """Return the number of characters currently in the input buffer.""" + if not self.sPort: + raise portNotOpenError + return self._instream.available() + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.sPort: + raise portNotOpenError + read = bytearray() + if size > 0: + while len(read) < size: + x = self._instream.read() + if x == -1: + if self.timeout >= 0: + break + else: + read.append(x) + return bytes(read) + + def write(self, data): + """Output the given string over the serial port.""" + if not self.sPort: + raise portNotOpenError + if not isinstance(data, (bytes, bytearray)): + raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data))) + self._outstream.write(data) + return len(data) + + def reset_input_buffer(self): + """Clear input buffer, discarding all that is in the buffer.""" + if not self.sPort: + raise portNotOpenError + self._instream.skip(self._instream.available()) + + def reset_output_buffer(self): + """\ + Clear output buffer, aborting the current output and + discarding all that is in the buffer. + """ + if not self.sPort: + raise portNotOpenError + self._outstream.flush() + + def send_break(self, duration=0.25): + """Send break condition. Timed, returns to idle state after given duration.""" + if not self.sPort: + raise portNotOpenError + self.sPort.sendBreak(duration*1000.0) + + def _update_break_state(self): + """Set break: Controls TXD. When active, to transmitting is possible.""" + if self.fd is None: + raise portNotOpenError + raise SerialException("The _update_break_state function is not implemented in java.") + + def _update_rts_state(self): + """Set terminal status line: Request To Send""" + if not self.sPort: + raise portNotOpenError + self.sPort.setRTS(self._rts_state) + + def _update_dtr_state(self): + """Set terminal status line: Data Terminal Ready""" + if not self.sPort: + raise portNotOpenError + self.sPort.setDTR(self._dtr_state) + + @property + def cts(self): + """Read terminal status line: Clear To Send""" + if not self.sPort: + raise portNotOpenError + self.sPort.isCTS() + + @property + def dsr(self): + """Read terminal status line: Data Set Ready""" + if not self.sPort: + raise portNotOpenError + self.sPort.isDSR() + + @property + def ri(self): + """Read terminal status line: Ring Indicator""" + if not self.sPort: + raise portNotOpenError + self.sPort.isRI() + + @property + def cd(self): + """Read terminal status line: Carrier Detect""" + if not self.sPort: + raise portNotOpenError + self.sPort.isCD() diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialjava.pyc b/backend/venv/lib/python3.6/site-packages/serial/serialjava.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7db4a583dd771da9bec41356d541f83245da4cb3 GIT binary patch literal 9577 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialposix.py b/backend/venv/lib/python3.6/site-packages/serial/serialposix.py new file mode 100644 index 0000000..afe5062 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/serialposix.py @@ -0,0 +1,811 @@ +#!/usr/bin/env python +# +# backend for serial IO for POSIX compatible systems, like Linux, OSX +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2001-2016 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +# +# parts based on code from Grant B. Edwards : +# ftp://ftp.visi.com/users/grante/python/PosixSerial.py +# +# references: http://www.easysw.com/~mike/serial/serial.html + +# Collection of port names (was previously used by number_to_device which was +# removed. +# - Linux /dev/ttyS%d (confirmed) +# - cygwin/win32 /dev/com%d (confirmed) +# - openbsd (OpenBSD) /dev/cua%02d +# - bsd*, freebsd* /dev/cuad%d +# - darwin (OS X) /dev/cuad%d +# - netbsd /dev/dty%02d (NetBSD 1.6 testing by Erk) +# - irix (IRIX) /dev/ttyf%d (partially tested) names depending on flow control +# - hp (HP-UX) /dev/tty%dp0 (not tested) +# - sunos (Solaris/SunOS) /dev/tty%c (letters, 'a'..'z') (confirmed) +# - aix (AIX) /dev/tty%d + + +# pylint: disable=abstract-method +import errno +import fcntl +import os +import select +import struct +import sys +import termios + +import serial +from serial.serialutil import SerialBase, SerialException, to_bytes, \ + portNotOpenError, writeTimeoutError, Timeout + + +class PlatformSpecificBase(object): + BAUDRATE_CONSTANTS = {} + + def _set_special_baudrate(self, baudrate): + raise NotImplementedError('non-standard baudrates are not supported on this platform') + + def _set_rs485_mode(self, rs485_settings): + raise NotImplementedError('RS485 not supported on this platform') + + +# some systems support an extra flag to enable the two in POSIX unsupported +# paritiy settings for MARK and SPACE +CMSPAR = 0 # default, for unsupported platforms, override below + +# try to detect the OS so that a device can be selected... +# this code block should supply a device() and set_special_baudrate() function +# for the platform +plat = sys.platform.lower() + +if plat[:5] == 'linux': # Linux (confirmed) # noqa + import array + + # extra termios flags + CMSPAR = 0o10000000000 # Use "stick" (mark/space) parity + + # baudrate ioctls + TCGETS2 = 0x802C542A + TCSETS2 = 0x402C542B + BOTHER = 0o010000 + + # RS485 ioctls + TIOCGRS485 = 0x542E + TIOCSRS485 = 0x542F + SER_RS485_ENABLED = 0b00000001 + SER_RS485_RTS_ON_SEND = 0b00000010 + SER_RS485_RTS_AFTER_SEND = 0b00000100 + SER_RS485_RX_DURING_TX = 0b00010000 + + class PlatformSpecific(PlatformSpecificBase): + BAUDRATE_CONSTANTS = { + 0: 0o000000, # hang up + 50: 0o000001, + 75: 0o000002, + 110: 0o000003, + 134: 0o000004, + 150: 0o000005, + 200: 0o000006, + 300: 0o000007, + 600: 0o000010, + 1200: 0o000011, + 1800: 0o000012, + 2400: 0o000013, + 4800: 0o000014, + 9600: 0o000015, + 19200: 0o000016, + 38400: 0o000017, + 57600: 0o010001, + 115200: 0o010002, + 230400: 0o010003, + 460800: 0o010004, + 500000: 0o010005, + 576000: 0o010006, + 921600: 0o010007, + 1000000: 0o010010, + 1152000: 0o010011, + 1500000: 0o010012, + 2000000: 0o010013, + 2500000: 0o010014, + 3000000: 0o010015, + 3500000: 0o010016, + 4000000: 0o010017 + } + + def _set_special_baudrate(self, baudrate): + # right size is 44 on x86_64, allow for some growth + buf = array.array('i', [0] * 64) + try: + # get serial_struct + fcntl.ioctl(self.fd, TCGETS2, buf) + # set custom speed + buf[2] &= ~termios.CBAUD + buf[2] |= BOTHER + buf[9] = buf[10] = baudrate + + # set serial_struct + fcntl.ioctl(self.fd, TCSETS2, buf) + except IOError as e: + raise ValueError('Failed to set custom baud rate ({}): {}'.format(baudrate, e)) + + def _set_rs485_mode(self, rs485_settings): + buf = array.array('i', [0] * 8) # flags, delaytx, delayrx, padding + try: + fcntl.ioctl(self.fd, TIOCGRS485, buf) + buf[0] |= SER_RS485_ENABLED + if rs485_settings is not None: + if rs485_settings.loopback: + buf[0] |= SER_RS485_RX_DURING_TX + else: + buf[0] &= ~SER_RS485_RX_DURING_TX + if rs485_settings.rts_level_for_tx: + buf[0] |= SER_RS485_RTS_ON_SEND + else: + buf[0] &= ~SER_RS485_RTS_ON_SEND + if rs485_settings.rts_level_for_rx: + buf[0] |= SER_RS485_RTS_AFTER_SEND + else: + buf[0] &= ~SER_RS485_RTS_AFTER_SEND + if rs485_settings.delay_before_tx is not None: + buf[1] = int(rs485_settings.delay_before_tx * 1000) + if rs485_settings.delay_before_rx is not None: + buf[2] = int(rs485_settings.delay_before_rx * 1000) + else: + buf[0] = 0 # clear SER_RS485_ENABLED + fcntl.ioctl(self.fd, TIOCSRS485, buf) + except IOError as e: + raise ValueError('Failed to set RS485 mode: {}'.format(e)) + + +elif plat == 'cygwin': # cygwin/win32 (confirmed) + + class PlatformSpecific(PlatformSpecificBase): + BAUDRATE_CONSTANTS = { + 128000: 0x01003, + 256000: 0x01005, + 500000: 0x01007, + 576000: 0x01008, + 921600: 0x01009, + 1000000: 0x0100a, + 1152000: 0x0100b, + 1500000: 0x0100c, + 2000000: 0x0100d, + 2500000: 0x0100e, + 3000000: 0x0100f + } + + +elif plat[:6] == 'darwin': # OS X + import array + IOSSIOSPEED = 0x80045402 # _IOW('T', 2, speed_t) + + class PlatformSpecific(PlatformSpecificBase): + osx_version = os.uname()[2].split('.') + # Tiger or above can support arbitrary serial speeds + if int(osx_version[0]) >= 8: + def _set_special_baudrate(self, baudrate): + # use IOKit-specific call to set up high speeds + buf = array.array('i', [baudrate]) + fcntl.ioctl(self.fd, IOSSIOSPEED, buf, 1) + +elif plat[:3] == 'bsd' or \ + plat[:7] == 'freebsd' or \ + plat[:6] == 'netbsd' or \ + plat[:7] == 'openbsd': + + class ReturnBaudrate(object): + def __getitem__(self, key): + return key + + class PlatformSpecific(PlatformSpecificBase): + # Only tested on FreeBSD: + # The baud rate may be passed in as + # a literal value. + BAUDRATE_CONSTANTS = ReturnBaudrate() + +else: + class PlatformSpecific(PlatformSpecificBase): + pass + + +# load some constants for later use. +# try to use values from termios, use defaults from linux otherwise +TIOCMGET = getattr(termios, 'TIOCMGET', 0x5415) +TIOCMBIS = getattr(termios, 'TIOCMBIS', 0x5416) +TIOCMBIC = getattr(termios, 'TIOCMBIC', 0x5417) +TIOCMSET = getattr(termios, 'TIOCMSET', 0x5418) + +# TIOCM_LE = getattr(termios, 'TIOCM_LE', 0x001) +TIOCM_DTR = getattr(termios, 'TIOCM_DTR', 0x002) +TIOCM_RTS = getattr(termios, 'TIOCM_RTS', 0x004) +# TIOCM_ST = getattr(termios, 'TIOCM_ST', 0x008) +# TIOCM_SR = getattr(termios, 'TIOCM_SR', 0x010) + +TIOCM_CTS = getattr(termios, 'TIOCM_CTS', 0x020) +TIOCM_CAR = getattr(termios, 'TIOCM_CAR', 0x040) +TIOCM_RNG = getattr(termios, 'TIOCM_RNG', 0x080) +TIOCM_DSR = getattr(termios, 'TIOCM_DSR', 0x100) +TIOCM_CD = getattr(termios, 'TIOCM_CD', TIOCM_CAR) +TIOCM_RI = getattr(termios, 'TIOCM_RI', TIOCM_RNG) +# TIOCM_OUT1 = getattr(termios, 'TIOCM_OUT1', 0x2000) +# TIOCM_OUT2 = getattr(termios, 'TIOCM_OUT2', 0x4000) +if hasattr(termios, 'TIOCINQ'): + TIOCINQ = termios.TIOCINQ +else: + TIOCINQ = getattr(termios, 'FIONREAD', 0x541B) +TIOCOUTQ = getattr(termios, 'TIOCOUTQ', 0x5411) + +TIOCM_zero_str = struct.pack('I', 0) +TIOCM_RTS_str = struct.pack('I', TIOCM_RTS) +TIOCM_DTR_str = struct.pack('I', TIOCM_DTR) + +TIOCSBRK = getattr(termios, 'TIOCSBRK', 0x5427) +TIOCCBRK = getattr(termios, 'TIOCCBRK', 0x5428) + + +class Serial(SerialBase, PlatformSpecific): + """\ + Serial port class POSIX implementation. Serial port configuration is + done with termios and fcntl. Runs on Linux and many other Un*x like + systems. + """ + + def open(self): + """\ + Open port with current settings. This may throw a SerialException + if the port cannot be opened.""" + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if self.is_open: + raise SerialException("Port is already open.") + self.fd = None + # open + try: + self.fd = os.open(self.portstr, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK) + except OSError as msg: + self.fd = None + raise SerialException(msg.errno, "could not open port {}: {}".format(self._port, msg)) + #~ fcntl.fcntl(self.fd, fcntl.F_SETFL, 0) # set blocking + + try: + self._reconfigure_port(force_update=True) + except: + try: + os.close(self.fd) + except: + # ignore any exception when closing the port + # also to keep original exception that happened when setting up + pass + self.fd = None + raise + else: + self.is_open = True + try: + if not self._dsrdtr: + self._update_dtr_state() + if not self._rtscts: + self._update_rts_state() + except IOError as e: + if e.errno in (errno.EINVAL, errno.ENOTTY): + # ignore Invalid argument and Inappropriate ioctl + pass + else: + raise + self.reset_input_buffer() + self.pipe_abort_read_r, self.pipe_abort_read_w = os.pipe() + self.pipe_abort_write_r, self.pipe_abort_write_w = os.pipe() + fcntl.fcntl(self.pipe_abort_read_r, fcntl.F_SETFL, os.O_NONBLOCK) + fcntl.fcntl(self.pipe_abort_write_r, fcntl.F_SETFL, os.O_NONBLOCK) + + def _reconfigure_port(self, force_update=False): + """Set communication parameters on opened port.""" + if self.fd is None: + raise SerialException("Can only operate on a valid file descriptor") + + # if exclusive lock is requested, create it before we modify anything else + if self._exclusive is not None: + if self._exclusive: + try: + fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError as msg: + raise SerialException(msg.errno, "Could not exclusively lock port {}: {}".format(self._port, msg)) + else: + fcntl.flock(self.fd, fcntl.LOCK_UN) + + custom_baud = None + + vmin = vtime = 0 # timeout is done via select + if self._inter_byte_timeout is not None: + vmin = 1 + vtime = int(self._inter_byte_timeout * 10) + try: + orig_attr = termios.tcgetattr(self.fd) + iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr + except termios.error as msg: # if a port is nonexistent but has a /dev file, it'll fail here + raise SerialException("Could not configure port: {}".format(msg)) + # set up raw mode / no echo / binary + cflag |= (termios.CLOCAL | termios.CREAD) + lflag &= ~(termios.ICANON | termios.ECHO | termios.ECHOE | + termios.ECHOK | termios.ECHONL | + termios.ISIG | termios.IEXTEN) # |termios.ECHOPRT + for flag in ('ECHOCTL', 'ECHOKE'): # netbsd workaround for Erk + if hasattr(termios, flag): + lflag &= ~getattr(termios, flag) + + oflag &= ~(termios.OPOST | termios.ONLCR | termios.OCRNL) + iflag &= ~(termios.INLCR | termios.IGNCR | termios.ICRNL | termios.IGNBRK) + if hasattr(termios, 'IUCLC'): + iflag &= ~termios.IUCLC + if hasattr(termios, 'PARMRK'): + iflag &= ~termios.PARMRK + + # setup baud rate + try: + ispeed = ospeed = getattr(termios, 'B{}'.format(self._baudrate)) + except AttributeError: + try: + ispeed = ospeed = self.BAUDRATE_CONSTANTS[self._baudrate] + except KeyError: + #~ raise ValueError('Invalid baud rate: %r' % self._baudrate) + # may need custom baud rate, it isn't in our list. + ispeed = ospeed = getattr(termios, 'B38400') + try: + custom_baud = int(self._baudrate) # store for later + except ValueError: + raise ValueError('Invalid baud rate: {!r}'.format(self._baudrate)) + else: + if custom_baud < 0: + raise ValueError('Invalid baud rate: {!r}'.format(self._baudrate)) + + # setup char len + cflag &= ~termios.CSIZE + if self._bytesize == 8: + cflag |= termios.CS8 + elif self._bytesize == 7: + cflag |= termios.CS7 + elif self._bytesize == 6: + cflag |= termios.CS6 + elif self._bytesize == 5: + cflag |= termios.CS5 + else: + raise ValueError('Invalid char len: {!r}'.format(self._bytesize)) + # setup stop bits + if self._stopbits == serial.STOPBITS_ONE: + cflag &= ~(termios.CSTOPB) + elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE: + cflag |= (termios.CSTOPB) # XXX same as TWO.. there is no POSIX support for 1.5 + elif self._stopbits == serial.STOPBITS_TWO: + cflag |= (termios.CSTOPB) + else: + raise ValueError('Invalid stop bit specification: {!r}'.format(self._stopbits)) + # setup parity + iflag &= ~(termios.INPCK | termios.ISTRIP) + if self._parity == serial.PARITY_NONE: + cflag &= ~(termios.PARENB | termios.PARODD | CMSPAR) + elif self._parity == serial.PARITY_EVEN: + cflag &= ~(termios.PARODD | CMSPAR) + cflag |= (termios.PARENB) + elif self._parity == serial.PARITY_ODD: + cflag &= ~CMSPAR + cflag |= (termios.PARENB | termios.PARODD) + elif self._parity == serial.PARITY_MARK and CMSPAR: + cflag |= (termios.PARENB | CMSPAR | termios.PARODD) + elif self._parity == serial.PARITY_SPACE and CMSPAR: + cflag |= (termios.PARENB | CMSPAR) + cflag &= ~(termios.PARODD) + else: + raise ValueError('Invalid parity: {!r}'.format(self._parity)) + # setup flow control + # xonxoff + if hasattr(termios, 'IXANY'): + if self._xonxoff: + iflag |= (termios.IXON | termios.IXOFF) # |termios.IXANY) + else: + iflag &= ~(termios.IXON | termios.IXOFF | termios.IXANY) + else: + if self._xonxoff: + iflag |= (termios.IXON | termios.IXOFF) + else: + iflag &= ~(termios.IXON | termios.IXOFF) + # rtscts + if hasattr(termios, 'CRTSCTS'): + if self._rtscts: + cflag |= (termios.CRTSCTS) + else: + cflag &= ~(termios.CRTSCTS) + elif hasattr(termios, 'CNEW_RTSCTS'): # try it with alternate constant name + if self._rtscts: + cflag |= (termios.CNEW_RTSCTS) + else: + cflag &= ~(termios.CNEW_RTSCTS) + # XXX should there be a warning if setting up rtscts (and xonxoff etc) fails?? + + # buffer + # vmin "minimal number of characters to be read. 0 for non blocking" + if vmin < 0 or vmin > 255: + raise ValueError('Invalid vmin: {!r}'.format(vmin)) + cc[termios.VMIN] = vmin + # vtime + if vtime < 0 or vtime > 255: + raise ValueError('Invalid vtime: {!r}'.format(vtime)) + cc[termios.VTIME] = vtime + # activate settings + if force_update or [iflag, oflag, cflag, lflag, ispeed, ospeed, cc] != orig_attr: + termios.tcsetattr( + self.fd, + termios.TCSANOW, + [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]) + + # apply custom baud rate, if any + if custom_baud is not None: + self._set_special_baudrate(custom_baud) + + if self._rs485_mode is not None: + self._set_rs485_mode(self._rs485_mode) + + def close(self): + """Close port""" + if self.is_open: + if self.fd is not None: + os.close(self.fd) + self.fd = None + os.close(self.pipe_abort_read_w) + os.close(self.pipe_abort_read_r) + os.close(self.pipe_abort_write_w) + os.close(self.pipe_abort_write_r) + self.pipe_abort_read_r, self.pipe_abort_read_w = None, None + self.pipe_abort_write_r, self.pipe_abort_write_w = None, None + self.is_open = False + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def in_waiting(self): + """Return the number of bytes currently in the input buffer.""" + #~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str) + s = fcntl.ioctl(self.fd, TIOCINQ, TIOCM_zero_str) + return struct.unpack('I', s)[0] + + # select based implementation, proved to work on many systems + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + read = bytearray() + timeout = Timeout(self._timeout) + while len(read) < size: + try: + ready, _, _ = select.select([self.fd, self.pipe_abort_read_r], [], [], timeout.time_left()) + if self.pipe_abort_read_r in ready: + os.read(self.pipe_abort_read_r, 1000) + break + # If select was used with a timeout, and the timeout occurs, it + # returns with empty lists -> thus abort read operation. + # For timeout == 0 (non-blocking operation) also abort when + # there is nothing to read. + if not ready: + break # timeout + buf = os.read(self.fd, size - len(read)) + # read should always return some data as select reported it was + # ready to read when we get to this point. + if not buf: + # Disconnected devices, at least on Linux, show the + # behavior that they are always ready to read immediately + # but reading returns nothing. + raise SerialException( + 'device reports readiness to read but returned no data ' + '(device disconnected or multiple access on port?)') + read.extend(buf) + except OSError as e: + # this is for Python 3.x where select.error is a subclass of + # OSError ignore BlockingIOErrors and EINTR. other errors are shown + # https://www.python.org/dev/peps/pep-0475. + if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): + raise SerialException('read failed: {}'.format(e)) + except select.error as e: + # this is for Python 2.x + # ignore BlockingIOErrors and EINTR. all errors are shown + # see also http://www.python.org/dev/peps/pep-3151/#select + if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): + raise SerialException('read failed: {}'.format(e)) + if timeout.expired(): + break + return bytes(read) + + def cancel_read(self): + if self.is_open: + os.write(self.pipe_abort_read_w, b"x") + + def cancel_write(self): + if self.is_open: + os.write(self.pipe_abort_write_w, b"x") + + def write(self, data): + """Output the given byte string over the serial port.""" + if not self.is_open: + raise portNotOpenError + d = to_bytes(data) + tx_len = length = len(d) + timeout = Timeout(self._write_timeout) + while tx_len > 0: + try: + n = os.write(self.fd, d) + if timeout.is_non_blocking: + # Zero timeout indicates non-blocking - simply return the + # number of bytes of data actually written + return n + elif not timeout.is_infinite: + # when timeout is set, use select to wait for being ready + # with the time left as timeout + if timeout.expired(): + raise writeTimeoutError + abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], timeout.time_left()) + if abort: + os.read(self.pipe_abort_write_r, 1000) + break + if not ready: + raise writeTimeoutError + else: + assert timeout.time_left() is None + # wait for write operation + abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], None) + if abort: + os.read(self.pipe_abort_write_r, 1) + break + if not ready: + raise SerialException('write failed (select)') + d = d[n:] + tx_len -= n + except SerialException: + raise + except OSError as e: + # this is for Python 3.x where select.error is a subclass of + # OSError ignore BlockingIOErrors and EINTR. other errors are shown + # https://www.python.org/dev/peps/pep-0475. + if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): + raise SerialException('write failed: {}'.format(e)) + except select.error as e: + # this is for Python 2.x + # ignore BlockingIOErrors and EINTR. all errors are shown + # see also http://www.python.org/dev/peps/pep-3151/#select + if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): + raise SerialException('write failed: {}'.format(e)) + if not timeout.is_non_blocking and timeout.expired(): + raise writeTimeoutError + return length - len(d) + + def flush(self): + """\ + Flush of file like objects. In this case, wait until all data + is written. + """ + if not self.is_open: + raise portNotOpenError + termios.tcdrain(self.fd) + + def reset_input_buffer(self): + """Clear input buffer, discarding all that is in the buffer.""" + if not self.is_open: + raise portNotOpenError + termios.tcflush(self.fd, termios.TCIFLUSH) + + def reset_output_buffer(self): + """\ + Clear output buffer, aborting the current output and discarding all + that is in the buffer. + """ + if not self.is_open: + raise portNotOpenError + termios.tcflush(self.fd, termios.TCOFLUSH) + + def send_break(self, duration=0.25): + """\ + Send break condition. Timed, returns to idle state after given + duration. + """ + if not self.is_open: + raise portNotOpenError + termios.tcsendbreak(self.fd, int(duration / 0.25)) + + def _update_break_state(self): + """\ + Set break: Controls TXD. When active, no transmitting is possible. + """ + if self._break_state: + fcntl.ioctl(self.fd, TIOCSBRK) + else: + fcntl.ioctl(self.fd, TIOCCBRK) + + def _update_rts_state(self): + """Set terminal status line: Request To Send""" + if self._rts_state: + fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_RTS_str) + else: + fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_RTS_str) + + def _update_dtr_state(self): + """Set terminal status line: Data Terminal Ready""" + if self._dtr_state: + fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str) + else: + fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_DTR_str) + + @property + def cts(self): + """Read terminal status line: Clear To Send""" + if not self.is_open: + raise portNotOpenError + s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) + return struct.unpack('I', s)[0] & TIOCM_CTS != 0 + + @property + def dsr(self): + """Read terminal status line: Data Set Ready""" + if not self.is_open: + raise portNotOpenError + s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) + return struct.unpack('I', s)[0] & TIOCM_DSR != 0 + + @property + def ri(self): + """Read terminal status line: Ring Indicator""" + if not self.is_open: + raise portNotOpenError + s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) + return struct.unpack('I', s)[0] & TIOCM_RI != 0 + + @property + def cd(self): + """Read terminal status line: Carrier Detect""" + if not self.is_open: + raise portNotOpenError + s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) + return struct.unpack('I', s)[0] & TIOCM_CD != 0 + + # - - platform specific - - - - + + @property + def out_waiting(self): + """Return the number of bytes currently in the output buffer.""" + #~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str) + s = fcntl.ioctl(self.fd, TIOCOUTQ, TIOCM_zero_str) + return struct.unpack('I', s)[0] + + def fileno(self): + """\ + For easier use of the serial port instance with select. + WARNING: this function is not portable to different platforms! + """ + if not self.is_open: + raise portNotOpenError + return self.fd + + def set_input_flow_control(self, enable=True): + """\ + Manually control flow - when software flow control is enabled. + This will send XON (true) or XOFF (false) to the other device. + WARNING: this function is not portable to different platforms! + """ + if not self.is_open: + raise portNotOpenError + if enable: + termios.tcflow(self.fd, termios.TCION) + else: + termios.tcflow(self.fd, termios.TCIOFF) + + def set_output_flow_control(self, enable=True): + """\ + Manually control flow of outgoing data - when hardware or software flow + control is enabled. + WARNING: this function is not portable to different platforms! + """ + if not self.is_open: + raise portNotOpenError + if enable: + termios.tcflow(self.fd, termios.TCOON) + else: + termios.tcflow(self.fd, termios.TCOOFF) + + def nonblocking(self): + """DEPRECATED - has no use""" + import warnings + warnings.warn("nonblocking() has no effect, already nonblocking", DeprecationWarning) + + +class PosixPollSerial(Serial): + """\ + Poll based read implementation. Not all systems support poll properly. + However this one has better handling of errors, such as a device + disconnecting while it's in use (e.g. USB-serial unplugged). + """ + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + read = bytearray() + poll = select.poll() + poll.register(self.fd, select.POLLIN | select.POLLERR | select.POLLHUP | select.POLLNVAL) + if size > 0: + while len(read) < size: + # print "\tread(): size",size, "have", len(read) #debug + # wait until device becomes ready to read (or something fails) + for fd, event in poll.poll(self._timeout * 1000): + if event & (select.POLLERR | select.POLLHUP | select.POLLNVAL): + raise SerialException('device reports error (poll)') + # we don't care if it is select.POLLIN or timeout, that's + # handled below + buf = os.read(self.fd, size - len(read)) + read.extend(buf) + if ((self._timeout is not None and self._timeout >= 0) or + (self._inter_byte_timeout is not None and self._inter_byte_timeout > 0)) and not buf: + break # early abort on timeout + return bytes(read) + + +class VTIMESerial(Serial): + """\ + Implement timeout using vtime of tty device instead of using select. + This means that no inter character timeout can be specified and that + the error handling is degraded. + + Overall timeout is disabled when inter-character timeout is used. + """ + + def _reconfigure_port(self, force_update=True): + """Set communication parameters on opened port.""" + super(VTIMESerial, self)._reconfigure_port() + fcntl.fcntl(self.fd, fcntl.F_SETFL, 0) # clear O_NONBLOCK + + if self._inter_byte_timeout is not None: + vmin = 1 + vtime = int(self._inter_byte_timeout * 10) + elif self._timeout is None: + vmin = 1 + vtime = 0 + else: + vmin = 0 + vtime = int(self._timeout * 10) + try: + orig_attr = termios.tcgetattr(self.fd) + iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr + except termios.error as msg: # if a port is nonexistent but has a /dev file, it'll fail here + raise serial.SerialException("Could not configure port: {}".format(msg)) + + if vtime < 0 or vtime > 255: + raise ValueError('Invalid vtime: {!r}'.format(vtime)) + cc[termios.VTIME] = vtime + cc[termios.VMIN] = vmin + + termios.tcsetattr( + self.fd, + termios.TCSANOW, + [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]) + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + read = bytearray() + while len(read) < size: + buf = os.read(self.fd, size - len(read)) + if not buf: + break + read.extend(buf) + return bytes(read) + + # hack to make hasattr return false + cancel_read = property() diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialposix.pyc b/backend/venv/lib/python3.6/site-packages/serial/serialposix.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f008f2bbc49b8a4a5688d6d583ad2d0a56787dac GIT binary patch literal 24286 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialutil.py b/backend/venv/lib/python3.6/site-packages/serial/serialutil.py new file mode 100644 index 0000000..7d51752 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/serialutil.py @@ -0,0 +1,693 @@ +#! python +# +# Base class and support functions used by various backends. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2001-2016 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +import io +import time + +# ``memoryview`` was introduced in Python 2.7 and ``bytes(some_memoryview)`` +# isn't returning the contents (very unfortunate). Therefore we need special +# cases and test for it. Ensure that there is a ``memoryview`` object for older +# Python versions. This is easier than making every test dependent on its +# existence. +try: + memoryview +except (NameError, AttributeError): + # implementation does not matter as we do not really use it. + # it just must not inherit from something else we might care for. + class memoryview(object): # pylint: disable=redefined-builtin,invalid-name + pass + +try: + unicode +except (NameError, AttributeError): + unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name + +try: + basestring +except (NameError, AttributeError): + basestring = (str,) # for Python 3, pylint: disable=redefined-builtin,invalid-name + + +# "for byte in data" fails for python3 as it returns ints instead of bytes +def iterbytes(b): + """Iterate over bytes, returning bytes instead of ints (python3)""" + if isinstance(b, memoryview): + b = b.tobytes() + i = 0 + while True: + a = b[i:i + 1] + i += 1 + if a: + yield a + else: + break + + +# all Python versions prior 3.x convert ``str([17])`` to '[17]' instead of '\x11' +# so a simple ``bytes(sequence)`` doesn't work for all versions +def to_bytes(seq): + """convert a sequence to a bytes type""" + if isinstance(seq, bytes): + return seq + elif isinstance(seq, bytearray): + return bytes(seq) + elif isinstance(seq, memoryview): + return seq.tobytes() + elif isinstance(seq, unicode): + raise TypeError('unicode strings are not supported, please encode to bytes: {!r}'.format(seq)) + else: + # handle list of integers and bytes (one or more items) for Python 2 and 3 + return bytes(bytearray(seq)) + + +# create control bytes +XON = to_bytes([17]) +XOFF = to_bytes([19]) + +CR = to_bytes([13]) +LF = to_bytes([10]) + + +PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = 'N', 'E', 'O', 'M', 'S' +STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO = (1, 1.5, 2) +FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5, 6, 7, 8) + +PARITY_NAMES = { + PARITY_NONE: 'None', + PARITY_EVEN: 'Even', + PARITY_ODD: 'Odd', + PARITY_MARK: 'Mark', + PARITY_SPACE: 'Space', +} + + +class SerialException(IOError): + """Base class for serial port related exceptions.""" + + +class SerialTimeoutException(SerialException): + """Write timeouts give an exception""" + + +writeTimeoutError = SerialTimeoutException('Write timeout') +portNotOpenError = SerialException('Attempting to use a port that is not open') + + +class Timeout(object): + """\ + Abstraction for timeout operations. Using time.monotonic() if available + or time.time() in all other cases. + + The class can also be initialized with 0 or None, in order to support + non-blocking and fully blocking I/O operations. The attributes + is_non_blocking and is_infinite are set accordingly. + """ + if hasattr(time, 'monotonic'): + # Timeout implementation with time.monotonic(). This function is only + # supported by Python 3.3 and above. It returns a time in seconds + # (float) just as time.time(), but is not affected by system clock + # adjustments. + TIME = time.monotonic + else: + # Timeout implementation with time.time(). This is compatible with all + # Python versions but has issues if the clock is adjusted while the + # timeout is running. + TIME = time.time + + def __init__(self, duration): + """Initialize a timeout with given duration""" + self.is_infinite = (duration is None) + self.is_non_blocking = (duration == 0) + self.duration = duration + if duration is not None: + self.target_time = self.TIME() + duration + else: + self.target_time = None + + def expired(self): + """Return a boolean, telling if the timeout has expired""" + return self.target_time is not None and self.time_left() <= 0 + + def time_left(self): + """Return how many seconds are left until the timeout expires""" + if self.is_non_blocking: + return 0 + elif self.is_infinite: + return None + else: + delta = self.target_time - self.TIME() + if delta > self.duration: + # clock jumped, recalculate + self.target_time = self.TIME() + self.duration + return self.duration + else: + return max(0, delta) + + def restart(self, duration): + """\ + Restart a timeout, only supported if a timeout was already set up + before. + """ + self.duration = duration + self.target_time = self.TIME() + duration + + +class SerialBase(io.RawIOBase): + """\ + Serial port base class. Provides __init__ function and properties to + get/set port settings. + """ + + # default values, may be overridden in subclasses that do not support all values + BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, + 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, + 3000000, 3500000, 4000000) + BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS) + PARITIES = (PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE) + STOPBITS = (STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO) + + def __init__(self, + port=None, + baudrate=9600, + bytesize=EIGHTBITS, + parity=PARITY_NONE, + stopbits=STOPBITS_ONE, + timeout=None, + xonxoff=False, + rtscts=False, + write_timeout=None, + dsrdtr=False, + inter_byte_timeout=None, + exclusive=None, + **kwargs): + """\ + Initialize comm port object. If a "port" is given, then the port will be + opened immediately. Otherwise a Serial port object in closed state + is returned. + """ + + self.is_open = False + self.portstr = None + self.name = None + # correct values are assigned below through properties + self._port = None + self._baudrate = None + self._bytesize = None + self._parity = None + self._stopbits = None + self._timeout = None + self._write_timeout = None + self._xonxoff = None + self._rtscts = None + self._dsrdtr = None + self._inter_byte_timeout = None + self._rs485_mode = None # disabled by default + self._rts_state = True + self._dtr_state = True + self._break_state = False + self._exclusive = None + + # assign values using get/set methods using the properties feature + self.port = port + self.baudrate = baudrate + self.bytesize = bytesize + self.parity = parity + self.stopbits = stopbits + self.timeout = timeout + self.write_timeout = write_timeout + self.xonxoff = xonxoff + self.rtscts = rtscts + self.dsrdtr = dsrdtr + self.inter_byte_timeout = inter_byte_timeout + self.exclusive = exclusive + + # watch for backward compatible kwargs + if 'writeTimeout' in kwargs: + self.write_timeout = kwargs.pop('writeTimeout') + if 'interCharTimeout' in kwargs: + self.inter_byte_timeout = kwargs.pop('interCharTimeout') + if kwargs: + raise ValueError('unexpected keyword arguments: {!r}'.format(kwargs)) + + if port is not None: + self.open() + + # - - - - - - - - - - - - - - - - - - - - - - - - + + # to be implemented by subclasses: + # def open(self): + # def close(self): + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def port(self): + """\ + Get the current port setting. The value that was passed on init or using + setPort() is passed back. + """ + return self._port + + @port.setter + def port(self, port): + """\ + Change the port. + """ + if port is not None and not isinstance(port, basestring): + raise ValueError('"port" must be None or a string, not {}'.format(type(port))) + was_open = self.is_open + if was_open: + self.close() + self.portstr = port + self._port = port + self.name = self.portstr + if was_open: + self.open() + + @property + def baudrate(self): + """Get the current baud rate setting.""" + return self._baudrate + + @baudrate.setter + def baudrate(self, baudrate): + """\ + Change baud rate. It raises a ValueError if the port is open and the + baud rate is not possible. If the port is closed, then the value is + accepted and the exception is raised when the port is opened. + """ + try: + b = int(baudrate) + except TypeError: + raise ValueError("Not a valid baudrate: {!r}".format(baudrate)) + else: + if b < 0: + raise ValueError("Not a valid baudrate: {!r}".format(baudrate)) + self._baudrate = b + if self.is_open: + self._reconfigure_port() + + @property + def bytesize(self): + """Get the current byte size setting.""" + return self._bytesize + + @bytesize.setter + def bytesize(self, bytesize): + """Change byte size.""" + if bytesize not in self.BYTESIZES: + raise ValueError("Not a valid byte size: {!r}".format(bytesize)) + self._bytesize = bytesize + if self.is_open: + self._reconfigure_port() + + @property + def exclusive(self): + """Get the current exclusive access setting.""" + return self._exclusive + + @exclusive.setter + def exclusive(self, exclusive): + """Change the exclusive access setting.""" + self._exclusive = exclusive + if self.is_open: + self._reconfigure_port() + + @property + def parity(self): + """Get the current parity setting.""" + return self._parity + + @parity.setter + def parity(self, parity): + """Change parity setting.""" + if parity not in self.PARITIES: + raise ValueError("Not a valid parity: {!r}".format(parity)) + self._parity = parity + if self.is_open: + self._reconfigure_port() + + @property + def stopbits(self): + """Get the current stop bits setting.""" + return self._stopbits + + @stopbits.setter + def stopbits(self, stopbits): + """Change stop bits size.""" + if stopbits not in self.STOPBITS: + raise ValueError("Not a valid stop bit size: {!r}".format(stopbits)) + self._stopbits = stopbits + if self.is_open: + self._reconfigure_port() + + @property + def timeout(self): + """Get the current timeout setting.""" + return self._timeout + + @timeout.setter + def timeout(self, timeout): + """Change timeout setting.""" + if timeout is not None: + try: + timeout + 1 # test if it's a number, will throw a TypeError if not... + except TypeError: + raise ValueError("Not a valid timeout: {!r}".format(timeout)) + if timeout < 0: + raise ValueError("Not a valid timeout: {!r}".format(timeout)) + self._timeout = timeout + if self.is_open: + self._reconfigure_port() + + @property + def write_timeout(self): + """Get the current timeout setting.""" + return self._write_timeout + + @write_timeout.setter + def write_timeout(self, timeout): + """Change timeout setting.""" + if timeout is not None: + if timeout < 0: + raise ValueError("Not a valid timeout: {!r}".format(timeout)) + try: + timeout + 1 # test if it's a number, will throw a TypeError if not... + except TypeError: + raise ValueError("Not a valid timeout: {!r}".format(timeout)) + + self._write_timeout = timeout + if self.is_open: + self._reconfigure_port() + + @property + def inter_byte_timeout(self): + """Get the current inter-character timeout setting.""" + return self._inter_byte_timeout + + @inter_byte_timeout.setter + def inter_byte_timeout(self, ic_timeout): + """Change inter-byte timeout setting.""" + if ic_timeout is not None: + if ic_timeout < 0: + raise ValueError("Not a valid timeout: {!r}".format(ic_timeout)) + try: + ic_timeout + 1 # test if it's a number, will throw a TypeError if not... + except TypeError: + raise ValueError("Not a valid timeout: {!r}".format(ic_timeout)) + + self._inter_byte_timeout = ic_timeout + if self.is_open: + self._reconfigure_port() + + @property + def xonxoff(self): + """Get the current XON/XOFF setting.""" + return self._xonxoff + + @xonxoff.setter + def xonxoff(self, xonxoff): + """Change XON/XOFF setting.""" + self._xonxoff = xonxoff + if self.is_open: + self._reconfigure_port() + + @property + def rtscts(self): + """Get the current RTS/CTS flow control setting.""" + return self._rtscts + + @rtscts.setter + def rtscts(self, rtscts): + """Change RTS/CTS flow control setting.""" + self._rtscts = rtscts + if self.is_open: + self._reconfigure_port() + + @property + def dsrdtr(self): + """Get the current DSR/DTR flow control setting.""" + return self._dsrdtr + + @dsrdtr.setter + def dsrdtr(self, dsrdtr=None): + """Change DsrDtr flow control setting.""" + if dsrdtr is None: + # if not set, keep backwards compatibility and follow rtscts setting + self._dsrdtr = self._rtscts + else: + # if defined independently, follow its value + self._dsrdtr = dsrdtr + if self.is_open: + self._reconfigure_port() + + @property + def rts(self): + return self._rts_state + + @rts.setter + def rts(self, value): + self._rts_state = value + if self.is_open: + self._update_rts_state() + + @property + def dtr(self): + return self._dtr_state + + @dtr.setter + def dtr(self, value): + self._dtr_state = value + if self.is_open: + self._update_dtr_state() + + @property + def break_condition(self): + return self._break_state + + @break_condition.setter + def break_condition(self, value): + self._break_state = value + if self.is_open: + self._update_break_state() + + # - - - - - - - - - - - - - - - - - - - - - - - - + # functions useful for RS-485 adapters + + @property + def rs485_mode(self): + """\ + Enable RS485 mode and apply new settings, set to None to disable. + See serial.rs485.RS485Settings for more info about the value. + """ + return self._rs485_mode + + @rs485_mode.setter + def rs485_mode(self, rs485_settings): + self._rs485_mode = rs485_settings + if self.is_open: + self._reconfigure_port() + + # - - - - - - - - - - - - - - - - - - - - - - - - + + _SAVED_SETTINGS = ('baudrate', 'bytesize', 'parity', 'stopbits', 'xonxoff', + 'dsrdtr', 'rtscts', 'timeout', 'write_timeout', + 'inter_byte_timeout') + + def get_settings(self): + """\ + Get current port settings as a dictionary. For use with + apply_settings(). + """ + return dict([(key, getattr(self, '_' + key)) for key in self._SAVED_SETTINGS]) + + def apply_settings(self, d): + """\ + Apply stored settings from a dictionary returned from + get_settings(). It's allowed to delete keys from the dictionary. These + values will simply left unchanged. + """ + for key in self._SAVED_SETTINGS: + if key in d and d[key] != getattr(self, '_' + key): # check against internal "_" value + setattr(self, key, d[key]) # set non "_" value to use properties write function + + # - - - - - - - - - - - - - - - - - - - - - - - - + + def __repr__(self): + """String representation of the current port settings and its state.""" + return '{name}(port={p.portstr!r}, ' \ + 'baudrate={p.baudrate!r}, bytesize={p.bytesize!r}, parity={p.parity!r}, ' \ + 'stopbits={p.stopbits!r}, timeout={p.timeout!r}, xonxoff={p.xonxoff!r}, ' \ + 'rtscts={p.rtscts!r}, dsrdtr={p.dsrdtr!r})'.format( + name=self.__class__.__name__, id=id(self), p=self) + + # - - - - - - - - - - - - - - - - - - - - - - - - + # compatibility with io library + # pylint: disable=invalid-name,missing-docstring + + def readable(self): + return True + + def writable(self): + return True + + def seekable(self): + return False + + def readinto(self, b): + data = self.read(len(b)) + n = len(data) + try: + b[:n] = data + except TypeError as err: + import array + if not isinstance(b, array.array): + raise err + b[:n] = array.array('b', data) + return n + + # - - - - - - - - - - - - - - - - - - - - - - - - + # context manager + + def __enter__(self): + if not self.is_open: + self.open() + return self + + def __exit__(self, *args, **kwargs): + self.close() + + # - - - - - - - - - - - - - - - - - - - - - - - - + + def send_break(self, duration=0.25): + """\ + Send break condition. Timed, returns to idle state after given + duration. + """ + if not self.is_open: + raise portNotOpenError + self.break_condition = True + time.sleep(duration) + self.break_condition = False + + # - - - - - - - - - - - - - - - - - - - - - - - - + # backwards compatibility / deprecated functions + + def flushInput(self): + self.reset_input_buffer() + + def flushOutput(self): + self.reset_output_buffer() + + def inWaiting(self): + return self.in_waiting + + def sendBreak(self, duration=0.25): + self.send_break(duration) + + def setRTS(self, value=1): + self.rts = value + + def setDTR(self, value=1): + self.dtr = value + + def getCTS(self): + return self.cts + + def getDSR(self): + return self.dsr + + def getRI(self): + return self.ri + + def getCD(self): + return self.cd + + def setPort(self, port): + self.port = port + + @property + def writeTimeout(self): + return self.write_timeout + + @writeTimeout.setter + def writeTimeout(self, timeout): + self.write_timeout = timeout + + @property + def interCharTimeout(self): + return self.inter_byte_timeout + + @interCharTimeout.setter + def interCharTimeout(self, interCharTimeout): + self.inter_byte_timeout = interCharTimeout + + def getSettingsDict(self): + return self.get_settings() + + def applySettingsDict(self, d): + self.apply_settings(d) + + def isOpen(self): + return self.is_open + + # - - - - - - - - - - - - - - - - - - - - - - - - + # additional functionality + + def read_all(self): + """\ + Read all bytes currently available in the buffer of the OS. + """ + return self.read(self.in_waiting) + + def read_until(self, terminator=LF, size=None): + """\ + Read until a termination sequence is found ('\n' by default), the size + is exceeded or until timeout occurs. + """ + lenterm = len(terminator) + line = bytearray() + timeout = Timeout(self._timeout) + while True: + c = self.read(1) + if c: + line += c + if line[-lenterm:] == terminator: + break + if size is not None and len(line) >= size: + break + else: + break + if timeout.expired(): + break + return bytes(line) + + def iread_until(self, *args, **kwargs): + """\ + Read lines, implemented as generator. It will raise StopIteration on + timeout (empty read). + """ + while True: + line = self.read_until(*args, **kwargs) + if not line: + break + yield line + + +# - - - - - - - - - - - - - - - - - - - - - - - - - +if __name__ == '__main__': + import sys + s = SerialBase() + sys.stdout.write('port name: {}\n'.format(s.name)) + sys.stdout.write('baud rates: {}\n'.format(s.BAUDRATES)) + sys.stdout.write('byte sizes: {}\n'.format(s.BYTESIZES)) + sys.stdout.write('parities: {}\n'.format(s.PARITIES)) + sys.stdout.write('stop bits: {}\n'.format(s.STOPBITS)) + sys.stdout.write('{}\n'.format(s)) diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialutil.pyc b/backend/venv/lib/python3.6/site-packages/serial/serialutil.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3800ba7b1a41101aa13accc5538c7c820e81c163 GIT binary patch literal 20027 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 DjnVz> literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialwin32.py b/backend/venv/lib/python3.6/site-packages/serial/serialwin32.py new file mode 100644 index 0000000..7b88999 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/serialwin32.py @@ -0,0 +1,475 @@ +#! python +# +# backend for Windows ("win32" incl. 32/64 bit support) +# +# (C) 2001-2015 Chris Liechti +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# SPDX-License-Identifier: BSD-3-Clause +# +# Initial patch to use ctypes by Giovanni Bajo + +# pylint: disable=invalid-name,too-few-public-methods +import ctypes +import time +from serial import win32 + +import serial +from serial.serialutil import SerialBase, SerialException, to_bytes, portNotOpenError, writeTimeoutError + + +class Serial(SerialBase): + """Serial port implementation for Win32 based on ctypes.""" + + BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200) + + def __init__(self, *args, **kwargs): + self._port_handle = None + self._overlapped_read = None + self._overlapped_write = None + super(Serial, self).__init__(*args, **kwargs) + + def open(self): + """\ + Open port with current settings. This may throw a SerialException + if the port cannot be opened. + """ + if self._port is None: + raise SerialException("Port must be configured before it can be used.") + if self.is_open: + raise SerialException("Port is already open.") + # the "\\.\COMx" format is required for devices other than COM1-COM8 + # not all versions of windows seem to support this properly + # so that the first few ports are used with the DOS device name + port = self.name + try: + if port.upper().startswith('COM') and int(port[3:]) > 8: + port = '\\\\.\\' + port + except ValueError: + # for like COMnotanumber + pass + self._port_handle = win32.CreateFile( + port, + win32.GENERIC_READ | win32.GENERIC_WRITE, + 0, # exclusive access + None, # no security + win32.OPEN_EXISTING, + win32.FILE_ATTRIBUTE_NORMAL | win32.FILE_FLAG_OVERLAPPED, + 0) + if self._port_handle == win32.INVALID_HANDLE_VALUE: + self._port_handle = None # 'cause __del__ is called anyway + raise SerialException("could not open port {!r}: {!r}".format(self.portstr, ctypes.WinError())) + + try: + self._overlapped_read = win32.OVERLAPPED() + self._overlapped_read.hEvent = win32.CreateEvent(None, 1, 0, None) + self._overlapped_write = win32.OVERLAPPED() + #~ self._overlapped_write.hEvent = win32.CreateEvent(None, 1, 0, None) + self._overlapped_write.hEvent = win32.CreateEvent(None, 0, 0, None) + + # Setup a 4k buffer + win32.SetupComm(self._port_handle, 4096, 4096) + + # Save original timeout values: + self._orgTimeouts = win32.COMMTIMEOUTS() + win32.GetCommTimeouts(self._port_handle, ctypes.byref(self._orgTimeouts)) + + self._reconfigure_port() + + # Clear buffers: + # Remove anything that was there + win32.PurgeComm( + self._port_handle, + win32.PURGE_TXCLEAR | win32.PURGE_TXABORT | + win32.PURGE_RXCLEAR | win32.PURGE_RXABORT) + except: + try: + self._close() + except: + # ignore any exception when closing the port + # also to keep original exception that happened when setting up + pass + self._port_handle = None + raise + else: + self.is_open = True + + def _reconfigure_port(self): + """Set communication parameters on opened port.""" + if not self._port_handle: + raise SerialException("Can only operate on a valid port handle") + + # Set Windows timeout values + # timeouts is a tuple with the following items: + # (ReadIntervalTimeout,ReadTotalTimeoutMultiplier, + # ReadTotalTimeoutConstant,WriteTotalTimeoutMultiplier, + # WriteTotalTimeoutConstant) + timeouts = win32.COMMTIMEOUTS() + if self._timeout is None: + pass # default of all zeros is OK + elif self._timeout == 0: + timeouts.ReadIntervalTimeout = win32.MAXDWORD + else: + timeouts.ReadTotalTimeoutConstant = max(int(self._timeout * 1000), 1) + if self._timeout != 0 and self._inter_byte_timeout is not None: + timeouts.ReadIntervalTimeout = max(int(self._inter_byte_timeout * 1000), 1) + + if self._write_timeout is None: + pass + elif self._write_timeout == 0: + timeouts.WriteTotalTimeoutConstant = win32.MAXDWORD + else: + timeouts.WriteTotalTimeoutConstant = max(int(self._write_timeout * 1000), 1) + win32.SetCommTimeouts(self._port_handle, ctypes.byref(timeouts)) + + win32.SetCommMask(self._port_handle, win32.EV_ERR) + + # Setup the connection info. + # Get state and modify it: + comDCB = win32.DCB() + win32.GetCommState(self._port_handle, ctypes.byref(comDCB)) + comDCB.BaudRate = self._baudrate + + if self._bytesize == serial.FIVEBITS: + comDCB.ByteSize = 5 + elif self._bytesize == serial.SIXBITS: + comDCB.ByteSize = 6 + elif self._bytesize == serial.SEVENBITS: + comDCB.ByteSize = 7 + elif self._bytesize == serial.EIGHTBITS: + comDCB.ByteSize = 8 + else: + raise ValueError("Unsupported number of data bits: {!r}".format(self._bytesize)) + + if self._parity == serial.PARITY_NONE: + comDCB.Parity = win32.NOPARITY + comDCB.fParity = 0 # Disable Parity Check + elif self._parity == serial.PARITY_EVEN: + comDCB.Parity = win32.EVENPARITY + comDCB.fParity = 1 # Enable Parity Check + elif self._parity == serial.PARITY_ODD: + comDCB.Parity = win32.ODDPARITY + comDCB.fParity = 1 # Enable Parity Check + elif self._parity == serial.PARITY_MARK: + comDCB.Parity = win32.MARKPARITY + comDCB.fParity = 1 # Enable Parity Check + elif self._parity == serial.PARITY_SPACE: + comDCB.Parity = win32.SPACEPARITY + comDCB.fParity = 1 # Enable Parity Check + else: + raise ValueError("Unsupported parity mode: {!r}".format(self._parity)) + + if self._stopbits == serial.STOPBITS_ONE: + comDCB.StopBits = win32.ONESTOPBIT + elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE: + comDCB.StopBits = win32.ONE5STOPBITS + elif self._stopbits == serial.STOPBITS_TWO: + comDCB.StopBits = win32.TWOSTOPBITS + else: + raise ValueError("Unsupported number of stop bits: {!r}".format(self._stopbits)) + + comDCB.fBinary = 1 # Enable Binary Transmission + # Char. w/ Parity-Err are replaced with 0xff (if fErrorChar is set to TRUE) + if self._rs485_mode is None: + if self._rtscts: + comDCB.fRtsControl = win32.RTS_CONTROL_HANDSHAKE + else: + comDCB.fRtsControl = win32.RTS_CONTROL_ENABLE if self._rts_state else win32.RTS_CONTROL_DISABLE + comDCB.fOutxCtsFlow = self._rtscts + else: + # checks for unsupported settings + # XXX verify if platform really does not have a setting for those + if not self._rs485_mode.rts_level_for_tx: + raise ValueError( + 'Unsupported value for RS485Settings.rts_level_for_tx: {!r}'.format( + self._rs485_mode.rts_level_for_tx,)) + if self._rs485_mode.rts_level_for_rx: + raise ValueError( + 'Unsupported value for RS485Settings.rts_level_for_rx: {!r}'.format( + self._rs485_mode.rts_level_for_rx,)) + if self._rs485_mode.delay_before_tx is not None: + raise ValueError( + 'Unsupported value for RS485Settings.delay_before_tx: {!r}'.format( + self._rs485_mode.delay_before_tx,)) + if self._rs485_mode.delay_before_rx is not None: + raise ValueError( + 'Unsupported value for RS485Settings.delay_before_rx: {!r}'.format( + self._rs485_mode.delay_before_rx,)) + if self._rs485_mode.loopback: + raise ValueError( + 'Unsupported value for RS485Settings.loopback: {!r}'.format( + self._rs485_mode.loopback,)) + comDCB.fRtsControl = win32.RTS_CONTROL_TOGGLE + comDCB.fOutxCtsFlow = 0 + + if self._dsrdtr: + comDCB.fDtrControl = win32.DTR_CONTROL_HANDSHAKE + else: + comDCB.fDtrControl = win32.DTR_CONTROL_ENABLE if self._dtr_state else win32.DTR_CONTROL_DISABLE + comDCB.fOutxDsrFlow = self._dsrdtr + comDCB.fOutX = self._xonxoff + comDCB.fInX = self._xonxoff + comDCB.fNull = 0 + comDCB.fErrorChar = 0 + comDCB.fAbortOnError = 0 + comDCB.XonChar = serial.XON + comDCB.XoffChar = serial.XOFF + + if not win32.SetCommState(self._port_handle, ctypes.byref(comDCB)): + raise SerialException( + 'Cannot configure port, something went wrong. ' + 'Original message: {!r}'.format(ctypes.WinError())) + + #~ def __del__(self): + #~ self.close() + + def _close(self): + """internal close port helper""" + if self._port_handle is not None: + # Restore original timeout values: + win32.SetCommTimeouts(self._port_handle, self._orgTimeouts) + if self._overlapped_read is not None: + self.cancel_read() + win32.CloseHandle(self._overlapped_read.hEvent) + self._overlapped_read = None + if self._overlapped_write is not None: + self.cancel_write() + win32.CloseHandle(self._overlapped_write.hEvent) + self._overlapped_write = None + win32.CloseHandle(self._port_handle) + self._port_handle = None + + def close(self): + """Close port""" + if self.is_open: + self._close() + self.is_open = False + + # - - - - - - - - - - - - - - - - - - - - - - - - + + @property + def in_waiting(self): + """Return the number of bytes currently in the input buffer.""" + flags = win32.DWORD() + comstat = win32.COMSTAT() + if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)): + raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError())) + return comstat.cbInQue + + def read(self, size=1): + """\ + Read size bytes from the serial port. If a timeout is set it may + return less characters as requested. With no timeout it will block + until the requested number of bytes is read. + """ + if not self.is_open: + raise portNotOpenError + if size > 0: + win32.ResetEvent(self._overlapped_read.hEvent) + flags = win32.DWORD() + comstat = win32.COMSTAT() + if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)): + raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError())) + n = min(comstat.cbInQue, size) if self.timeout == 0 else size + if n > 0: + buf = ctypes.create_string_buffer(n) + rc = win32.DWORD() + read_ok = win32.ReadFile( + self._port_handle, + buf, + n, + ctypes.byref(rc), + ctypes.byref(self._overlapped_read)) + if not read_ok and win32.GetLastError() not in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING): + raise SerialException("ReadFile failed ({!r})".format(ctypes.WinError())) + result_ok = win32.GetOverlappedResult( + self._port_handle, + ctypes.byref(self._overlapped_read), + ctypes.byref(rc), + True) + if not result_ok: + if win32.GetLastError() != win32.ERROR_OPERATION_ABORTED: + raise SerialException("GetOverlappedResult failed ({!r})".format(ctypes.WinError())) + read = buf.raw[:rc.value] + else: + read = bytes() + else: + read = bytes() + return bytes(read) + + def write(self, data): + """Output the given byte string over the serial port.""" + if not self.is_open: + raise portNotOpenError + #~ if not isinstance(data, (bytes, bytearray)): + #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data))) + # convert data (needed in case of memoryview instance: Py 3.1 io lib), ctypes doesn't like memoryview + data = to_bytes(data) + if data: + #~ win32event.ResetEvent(self._overlapped_write.hEvent) + n = win32.DWORD() + success = win32.WriteFile(self._port_handle, data, len(data), ctypes.byref(n), self._overlapped_write) + if self._write_timeout != 0: # if blocking (None) or w/ write timeout (>0) + if not success and win32.GetLastError() not in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING): + raise SerialException("WriteFile failed ({!r})".format(ctypes.WinError())) + + # Wait for the write to complete. + #~ win32.WaitForSingleObject(self._overlapped_write.hEvent, win32.INFINITE) + win32.GetOverlappedResult(self._port_handle, self._overlapped_write, ctypes.byref(n), True) + if win32.GetLastError() == win32.ERROR_OPERATION_ABORTED: + return n.value # canceled IO is no error + if n.value != len(data): + raise writeTimeoutError + return n.value + else: + errorcode = win32.ERROR_SUCCESS if success else win32.GetLastError() + if errorcode in (win32.ERROR_INVALID_USER_BUFFER, win32.ERROR_NOT_ENOUGH_MEMORY, + win32.ERROR_OPERATION_ABORTED): + return 0 + elif errorcode in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING): + # no info on true length provided by OS function in async mode + return len(data) + else: + raise SerialException("WriteFile failed ({!r})".format(ctypes.WinError())) + else: + return 0 + + def flush(self): + """\ + Flush of file like objects. In this case, wait until all data + is written. + """ + while self.out_waiting: + time.sleep(0.05) + # XXX could also use WaitCommEvent with mask EV_TXEMPTY, but it would + # require overlapped IO and it's also only possible to set a single mask + # on the port--- + + def reset_input_buffer(self): + """Clear input buffer, discarding all that is in the buffer.""" + if not self.is_open: + raise portNotOpenError + win32.PurgeComm(self._port_handle, win32.PURGE_RXCLEAR | win32.PURGE_RXABORT) + + def reset_output_buffer(self): + """\ + Clear output buffer, aborting the current output and discarding all + that is in the buffer. + """ + if not self.is_open: + raise portNotOpenError + win32.PurgeComm(self._port_handle, win32.PURGE_TXCLEAR | win32.PURGE_TXABORT) + + def _update_break_state(self): + """Set break: Controls TXD. When active, to transmitting is possible.""" + if not self.is_open: + raise portNotOpenError + if self._break_state: + win32.SetCommBreak(self._port_handle) + else: + win32.ClearCommBreak(self._port_handle) + + def _update_rts_state(self): + """Set terminal status line: Request To Send""" + if self._rts_state: + win32.EscapeCommFunction(self._port_handle, win32.SETRTS) + else: + win32.EscapeCommFunction(self._port_handle, win32.CLRRTS) + + def _update_dtr_state(self): + """Set terminal status line: Data Terminal Ready""" + if self._dtr_state: + win32.EscapeCommFunction(self._port_handle, win32.SETDTR) + else: + win32.EscapeCommFunction(self._port_handle, win32.CLRDTR) + + def _GetCommModemStatus(self): + if not self.is_open: + raise portNotOpenError + stat = win32.DWORD() + win32.GetCommModemStatus(self._port_handle, ctypes.byref(stat)) + return stat.value + + @property + def cts(self): + """Read terminal status line: Clear To Send""" + return win32.MS_CTS_ON & self._GetCommModemStatus() != 0 + + @property + def dsr(self): + """Read terminal status line: Data Set Ready""" + return win32.MS_DSR_ON & self._GetCommModemStatus() != 0 + + @property + def ri(self): + """Read terminal status line: Ring Indicator""" + return win32.MS_RING_ON & self._GetCommModemStatus() != 0 + + @property + def cd(self): + """Read terminal status line: Carrier Detect""" + return win32.MS_RLSD_ON & self._GetCommModemStatus() != 0 + + # - - platform specific - - - - + + def set_buffer_size(self, rx_size=4096, tx_size=None): + """\ + Recommend a buffer size to the driver (device driver can ignore this + value). Must be called before the port is opened. + """ + if tx_size is None: + tx_size = rx_size + win32.SetupComm(self._port_handle, rx_size, tx_size) + + def set_output_flow_control(self, enable=True): + """\ + Manually control flow - when software flow control is enabled. + This will do the same as if XON (true) or XOFF (false) are received + from the other device and control the transmission accordingly. + WARNING: this function is not portable to different platforms! + """ + if not self.is_open: + raise portNotOpenError + if enable: + win32.EscapeCommFunction(self._port_handle, win32.SETXON) + else: + win32.EscapeCommFunction(self._port_handle, win32.SETXOFF) + + @property + def out_waiting(self): + """Return how many bytes the in the outgoing buffer""" + flags = win32.DWORD() + comstat = win32.COMSTAT() + if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)): + raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError())) + return comstat.cbOutQue + + def _cancel_overlapped_io(self, overlapped): + """Cancel a blocking read operation, may be called from other thread""" + # check if read operation is pending + rc = win32.DWORD() + err = win32.GetOverlappedResult( + self._port_handle, + ctypes.byref(overlapped), + ctypes.byref(rc), + False) + if not err and win32.GetLastError() in (win32.ERROR_IO_PENDING, win32.ERROR_IO_INCOMPLETE): + # cancel, ignoring any errors (e.g. it may just have finished on its own) + win32.CancelIoEx(self._port_handle, overlapped) + + def cancel_read(self): + """Cancel a blocking read operation, may be called from other thread""" + self._cancel_overlapped_io(self._overlapped_read) + + def cancel_write(self): + """Cancel a blocking write operation, may be called from other thread""" + self._cancel_overlapped_io(self._overlapped_write) + + @SerialBase.exclusive.setter + def exclusive(self, exclusive): + """Change the exclusive access setting.""" + if exclusive is not None and not exclusive: + raise ValueError('win32 only supports exclusive access (not: {})'.format(exclusive)) + else: + serial.SerialBase.exclusive.__set__(self, exclusive) diff --git a/backend/venv/lib/python3.6/site-packages/serial/serialwin32.pyc b/backend/venv/lib/python3.6/site-packages/serial/serialwin32.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f994e45d0f20623b8322f654f19b39493f08b30e GIT binary patch literal 14471 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 dict - discrepancies = {} - a_properties = set(serial.meta.read(a).properties.keys()) - b_properties = set(serial.meta.read(b).properties.keys()) - for property_ in a_properties | b_properties: - try: - a_value = getattr(a, property_) - except AttributeError: - a_value = None - try: - b_value = getattr(b, property_) - except AttributeError: - b_value = None - if a_value != b_value: - discrepancies[property_] = (a_value, b_value) - return discrepancies - - -def json( - model_instance, # type: serial.model.Model - raise_validation_errors=True, # type: bool -): - # type: (...) -> None - model( - model_instance=model_instance, - format_='json', - raise_validation_errors=raise_validation_errors - ) - - -def yaml( - model_instance, # type: serial.model.Model - raise_validation_errors=True, # type: bool -): - # type: (...) -> None - model( - model_instance=model_instance, - format_='yaml', - raise_validation_errors=raise_validation_errors - ) - - -def xml( - model_instance, # type: serial.model.Model - raise_validation_errors=True, # type: bool -): - # type: (...) -> None - raise NotImplementedError() - # model( - # model_instance=model_instance, - # format_='xml', - # raise_validation_errors=raise_validation_errors - # ) - - -def model( - model_instance, # type: serial.model.Model - format_, # type: str - raise_validation_errors=True, # type: bool -): - # type: (...) -> None - """ - Tests an instance of a `serial.model.Model` sub-class. - - Parameters: - - - model_instance (serial.model.Model): - - An instance of a `serial.model.Model` sub-class. - - - format_ (str): - - The serialization format being tested: 'json', 'yaml' or 'xml'. - - - raise_validation_errors (bool): - - The function `serial.model.validate` verifies that all required attributes are present, as well as any - additional validations implemented using the model's validation hooks `after_validate` and/or - `before_validate`. - - - If `True`, errors resulting from `serial.model.validate` are raised. - - - If `False`, errors resulting from `serial.model.validate` are expressed only as warnings. - """ - if not isinstance(model_instance, Model): - - value_representation = repr(model_instance) - - raise TypeError( - '`%s` requires an instance of `%s` for the parameter `model_instance`, not%s' % ( - calling_function_qualified_name(), - qualified_name(Model), - ( - (':\n%s' if '\n' in value_representation else ' `%s`') % - value_representation - ) - ) - ) - - serial.meta.format_(model_instance, format_) - - if isinstance(model_instance, serial.model.Object): - - errors = serial.marshal.validate(model_instance, raise_errors=raise_validation_errors) - - if errors: - warn('\n' + '\n'.join(errors)) - - model_type = type(model_instance) - - string = str(model_instance) - - assert string != '' - - reloaded_model_instance = model_type(string) - qualified_model_name = qualified_name(type(model_instance)) - - try: - - assert model_instance == reloaded_model_instance - - except AssertionError as e: - - sa = serial.marshal.serialize(model_instance) - sb = serial.marshal.serialize(reloaded_model_instance) - - ra = ''.join(l.strip() for l in repr(model_instance).split('\n')) - rb = ''.join(l.strip() for l in repr(reloaded_model_instance).split('\n')) - - message = [ - 'Discrepancies were found between the instance of `%s` provided and ' % qualified_model_name + - 'a serialized/deserialized clone:' - ] - - if sa != sb: - - message.append( - '\n :\n\n %s\n !=\n %s' % ( - sa, - sb - ) - ) - - if ra != rb: - - message.append( - '\n %s\n !=\n %s\n' % ( - ra, - rb - ) - ) - - for k, a_b in _object_discrepancies(model_instance, reloaded_model_instance).items(): - - a, b = a_b - - assert a != b - - sa = serial.marshal.serialize(a) - sb = serial.marshal.serialize(b) - - if sa != sb: - - message.append( - '\n %s().%s:\n\n %s\n %s\n %s' % ( - qualified_model_name, - k, - sa, - '==' if sa == sb else '!=', - sb - ) - ) - - ra = ''.join(l.strip() for l in repr(a).split('\n')) - rb = ''.join(l.strip() for l in repr(b).split('\n')) - - if ra != rb: - - message.append( - '\n %s\n %s\n %s' % ( - ra, - '==' if ra == rb else '!=', - rb - ) - ) - - e.args = tuple( - chain( - (e.args[0] + '\n' + '\n'.join(message) if e.args else '\n'.join(message),), - e.args[1:] if e.args else tuple() - ) - ) - - raise e - - reloaded_string = str(reloaded_model_instance) - - try: - assert string == reloaded_string - except AssertionError as e: - m = '\n%s\n!=\n%s' % (string, reloaded_string) - if e.args: - e.args = tuple(chain( - (e.args[0] + '\n' + m,), - e.args[1:] - )) - else: - e.args = (m,) - raise e - - if format_ == 'json': - reloaded_marshalled_data = _json.loads( - string, - object_hook=collections.OrderedDict, - object_pairs_hook=collections.OrderedDict - ) - elif format_ == 'yaml': - reloaded_marshalled_data = _yaml.load(string) - elif format_ == 'xml': - raise NotImplementedError() - else: - format_representation = repr(format_) - raise ValueError( - 'Valid serialization types for parameter `format_` are "json", "yaml", or "xml'", not" + ( - ( - ':\n%s' if '\n' in format_representation else ' %s.' - ) % format_representation - ) - ) - - keys = set() - - for property_name, property in serial.meta.read(model_instance).properties.items(): - - keys.add(property.name or property_name) - property_value = getattr(model_instance, property_name) - - if isinstance(property_value, Model) or ( - hasattr(property_value, '__iter__') and - (not isinstance(property_value, (str, native_str, bytes))) - ): - model(property_value, format_=format_, raise_validation_errors=raise_validation_errors) - - for k in reloaded_marshalled_data.keys(): - - if k not in keys: - raise KeyError( - '"%s" not found in serialized/re-deserialized data: %s' % ( - k, - string - ) - ) - - elif isinstance(model_instance, serial.model.Array): - - serial.marshal.validate(model_instance) - - for item in model_instance: - - if isinstance(item, Model) or ( - hasattr(item, '__iter__') and - (not isinstance(item, (str, native_str, bytes))) - ): - model(item, format_=format_, raise_validation_errors=raise_validation_errors) - - elif isinstance(model_instance, serial.model.Dictionary): - - serial.marshal.validate(model_instance) - - for key, value in model_instance.items(): - - if isinstance(value, Model) or ( - hasattr(value, '__iter__') and - (not isinstance(value, (str, native_str, bytes))) - ): - model(value, format_=format_, raise_validation_errors=raise_validation_errors) diff --git a/backend/venv/lib/python3.6/site-packages/serial/threaded/__init__.py b/backend/venv/lib/python3.6/site-packages/serial/threaded/__init__.py new file mode 100644 index 0000000..74b6924 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/threaded/__init__.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python3 +# +# Working with threading and pySerial +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2015-2016 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +"""\ +Support threading with serial ports. +""" +import serial +import threading + + +class Protocol(object): + """\ + Protocol as used by the ReaderThread. This base class provides empty + implementations of all methods. + """ + + def connection_made(self, transport): + """Called when reader thread is started""" + + def data_received(self, data): + """Called with snippets received from the serial port""" + + def connection_lost(self, exc): + """\ + Called when the serial port is closed or the reader loop terminated + otherwise. + """ + if isinstance(exc, Exception): + raise exc + + +class Packetizer(Protocol): + """ + Read binary packets from serial port. Packets are expected to be terminated + with a TERMINATOR byte (null byte by default). + + The class also keeps track of the transport. + """ + + TERMINATOR = b'\0' + + def __init__(self): + self.buffer = bytearray() + self.transport = None + + def connection_made(self, transport): + """Store transport""" + self.transport = transport + + def connection_lost(self, exc): + """Forget transport""" + self.transport = None + super(Packetizer, self).connection_lost(exc) + + def data_received(self, data): + """Buffer received data, find TERMINATOR, call handle_packet""" + self.buffer.extend(data) + while self.TERMINATOR in self.buffer: + packet, self.buffer = self.buffer.split(self.TERMINATOR, 1) + self.handle_packet(packet) + + def handle_packet(self, packet): + """Process packets - to be overridden by subclassing""" + raise NotImplementedError('please implement functionality in handle_packet') + + +class FramedPacket(Protocol): + """ + Read binary packets. Packets are expected to have a start and stop marker. + + The class also keeps track of the transport. + """ + + START = b'(' + STOP = b')' + + def __init__(self): + self.packet = bytearray() + self.in_packet = False + self.transport = None + + def connection_made(self, transport): + """Store transport""" + self.transport = transport + + def connection_lost(self, exc): + """Forget transport""" + self.transport = None + self.in_packet = False + del self.packet[:] + super(FramedPacket, self).connection_lost(exc) + + def data_received(self, data): + """Find data enclosed in START/STOP, call handle_packet""" + for byte in serial.iterbytes(data): + if byte == self.START: + self.in_packet = True + elif byte == self.STOP: + self.in_packet = False + self.handle_packet(bytes(self.packet)) # make read-only copy + del self.packet[:] + elif self.in_packet: + self.packet.extend(byte) + else: + self.handle_out_of_packet_data(byte) + + def handle_packet(self, packet): + """Process packets - to be overridden by subclassing""" + raise NotImplementedError('please implement functionality in handle_packet') + + def handle_out_of_packet_data(self, data): + """Process data that is received outside of packets""" + pass + + +class LineReader(Packetizer): + """ + Read and write (Unicode) lines from/to serial port. + The encoding is applied. + """ + + TERMINATOR = b'\r\n' + ENCODING = 'utf-8' + UNICODE_HANDLING = 'replace' + + def handle_packet(self, packet): + self.handle_line(packet.decode(self.ENCODING, self.UNICODE_HANDLING)) + + def handle_line(self, line): + """Process one line - to be overridden by subclassing""" + raise NotImplementedError('please implement functionality in handle_line') + + def write_line(self, text): + """ + Write text to the transport. ``text`` is a Unicode string and the encoding + is applied before sending ans also the newline is append. + """ + # + is not the best choice but bytes does not support % or .format in py3 and we want a single write call + self.transport.write(text.encode(self.ENCODING, self.UNICODE_HANDLING) + self.TERMINATOR) + + +class ReaderThread(threading.Thread): + """\ + Implement a serial port read loop and dispatch to a Protocol instance (like + the asyncio.Protocol) but do it with threads. + + Calls to close() will close the serial port but it is also possible to just + stop() this thread and continue the serial port instance otherwise. + """ + + def __init__(self, serial_instance, protocol_factory): + """\ + Initialize thread. + + Note that the serial_instance' timeout is set to one second! + Other settings are not changed. + """ + super(ReaderThread, self).__init__() + self.daemon = True + self.serial = serial_instance + self.protocol_factory = protocol_factory + self.alive = True + self._lock = threading.Lock() + self._connection_made = threading.Event() + self.protocol = None + + def stop(self): + """Stop the reader thread""" + self.alive = False + if hasattr(self.serial, 'cancel_read'): + self.serial.cancel_read() + self.join(2) + + def run(self): + """Reader loop""" + if not hasattr(self.serial, 'cancel_read'): + self.serial.timeout = 1 + self.protocol = self.protocol_factory() + try: + self.protocol.connection_made(self) + except Exception as e: + self.alive = False + self.protocol.connection_lost(e) + self._connection_made.set() + return + error = None + self._connection_made.set() + while self.alive and self.serial.is_open: + try: + # read all that is there or wait for one byte (blocking) + data = self.serial.read(self.serial.in_waiting or 1) + except serial.SerialException as e: + # probably some I/O problem such as disconnected USB serial + # adapters -> exit + error = e + break + else: + if data: + # make a separated try-except for called used code + try: + self.protocol.data_received(data) + except Exception as e: + error = e + break + self.alive = False + self.protocol.connection_lost(error) + self.protocol = None + + def write(self, data): + """Thread safe writing (uses lock)""" + with self._lock: + self.serial.write(data) + + def close(self): + """Close the serial port and exit reader thread (uses lock)""" + # use the lock to let other threads finish writing + with self._lock: + # first stop reading, so that closing can be done on idle port + self.stop() + self.serial.close() + + def connect(self): + """ + Wait until connection is set up and return the transport and protocol + instances. + """ + if self.alive: + self._connection_made.wait() + if not self.alive: + raise RuntimeError('connection_lost already called') + return (self, self.protocol) + else: + raise RuntimeError('already stopped') + + # - - context manager, returns protocol + + def __enter__(self): + """\ + Enter context handler. May raise RuntimeError in case the connection + could not be created. + """ + self.start() + self._connection_made.wait() + if not self.alive: + raise RuntimeError('connection_lost already called') + return self.protocol + + def __exit__(self, exc_type, exc_val, exc_tb): + """Leave context: close port""" + self.close() + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + # pylint: disable=wrong-import-position + import sys + import time + import traceback + + #~ PORT = 'spy:///dev/ttyUSB0' + PORT = 'loop://' + + class PrintLines(LineReader): + def connection_made(self, transport): + super(PrintLines, self).connection_made(transport) + sys.stdout.write('port opened\n') + self.write_line('hello world') + + def handle_line(self, data): + sys.stdout.write('line received: {!r}\n'.format(data)) + + def connection_lost(self, exc): + if exc: + traceback.print_exc(exc) + sys.stdout.write('port closed\n') + + ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1) + with ReaderThread(ser, PrintLines) as protocol: + protocol.write_line('hello') + time.sleep(2) + + # alternative usage + ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1) + t = ReaderThread(ser, PrintLines) + t.start() + transport, protocol = t.connect() + protocol.write_line('hello') + time.sleep(2) + t.close() diff --git a/backend/venv/lib/python3.6/site-packages/serial/threaded/__pycache__/__init__.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/threaded/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..daa3e324396b7f0bab73043c5ea3c1d1e78458d6 GIT binary patch literal 9490 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 L?Lg)i12F>tkJ}uS literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/__init__.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..563632cf8eebfad311e3feb79bcdad59b1659a24 GIT binary patch literal 142 zcmXr!<>d;hagSjDg2x~N1{i@12OutH0TL+;!3>&=ek&P@K*9*(m$iOLZh?M5W`S;I zUU5lcPL6JTa%rZ4WnqqfL1l4jQD$O}K9nlS&(A5=kB`sH%PfhH*DI*J#bJ}1pHiBW LY6mj37>F4F!muK2 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/hexlify_codec.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/hexlify_codec.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a4b9fa11a0feadb732668f07c15cff91fc134a27 GIT binary patch literal 4839 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 ABLDyZ literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_common.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_common.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..162df21f7656871fb1f2bbb430963701d5fa33cf GIT binary patch literal 3111 zcmZ`*TXWmS6~^L5h@xa#R$3=<+HGzV`X*1}nI@XVNo{3PO*OQ~&aIlX7zn$PK!O0g z3)&V$zJ&fW`Ac~1lYc>H`qb|%C`Q!Sz~I{}cK4j~owMh&wYB!+$#=i}w_{oVvCcg% z>fhkgj{w3FEVcHVlI=6ow)bse3+JV^?|f$oS9mWi;YoMq?0do&E%f`cC4!-?_GBPE zu`**b#<0>8tJi*oRjq!^sKDdk!wIYh_;ep|WKG$NozfaG!6w$s-m(65cWO^;wQjQVU8{74ZMDrTdjDx<_RrRY4P2VRtfw14p@seID&}||T+O0G+lWJf&7 z%A7|$O?1igLte@krRGs4cs3p#NTo4>=SoP$F;9<+BFDc`n^wxAa;&11Yjk0$+orSf zKKVu5==HqXR@ig5kPP9}>ly1$7GS5@JMN))xLLJ8WXq0ES zi<3HlB<2{GNU<9L%X8TVbD7OLY@PY|6e&KYp|gjq@eMxx7$7aOH*C%J9dSiC!hOm1 zVQt~V!k&PI$sF^M!0@#<52WaNhf`PPiKG$m=c1cK*O34XGYyI zJ+48|pl`5cFfh1cuswmK!xc3sR|lQJ+5~)617Q!=P5TnHXY&;yh10K`Y%8i-d%OLA ze04hQb!%V9XGtt;H;YEHUKLWuDk(^$+C6@rh}wBZPyp5%p~8l0b&%$9L=*QrqbM66 zMltv$RoyC7F2-?LuYg8Up44t7z6i7$#X#hT;39xpx2-h+tlOUd~&ata|IbrEZ`7xo})^;Dr(HqA{@saB(Q{ z9ViaU8rak`gW0KWMy^xMw4EnSIB=@Zm4v_Gqww zKTo%t;&ggCRb)4)jz9VKF{oQxRfq3AeDJsLcOUkN%3h;I3K;4N!6w05fH&wfAoY+v zzQmllB3%~Xy?T*DXET>HNX~3Dcoh|B#hL*_!yi*~%(Y=xy>THRIHX*-mWkNpUHnkx zBcmNmiTdEfpRt(g68weWGT;s8Uq?_*Pr|da?O>*+E0E|=y}Lw&<(W(2XdwlKRFVQ$ zku{`{$NJcqoUvycKGt3iPOFQw^utg-4Z~eb)U=qzsw+#IH{(m2F5Hi7R2u5d7)mxk zv>IB?=C+CQ=TD$M!1UMbpCT~qVdzc!)A*9<7WLi?{w zcsR0O{dKpLBOb-E)Os5+CmueJl+d`MMu@J-K`QyJd;Al+kZu|(-sD8Uo^h*J3jr=7kM0p>Q7j`aUSD0?^E+bf{zG3 zCb&WHdr&nsx?R*7fc(y68v%3e_T^wD2m&u~=r!XD{t$N-{JD>Y$@@M>-UlYl7_z?W zQ@dmXd*Dz;r@E$Yvb%{j^i>xu_vX1?caiG}%FwioRD8^lIq@yT#Ccq7^1C$8hfQX2 zGNKDqQ-U|l^5D6O z0=gJ_4$>l%7wFnEyS?^SZqq)Y8UHu_YGNpowJy6xN!q9ABO{KYB!l5RF&>RhdMk=} zrtT1!{VDL&ets}OCS_D@epEN9+bfXumaj8f9emkP;vU)2u!}-B?A7gl9jpcH|FG!X ACIA2c literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_linux.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_linux.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7981fca032da0d04be38634863b28a5db5ac54ae GIT binary patch literal 2907 zcmZ7&O_STU6#yxUqO@9hz1g^q-C-Laq8(exw4Jo+xSlv}*LC7-9B&%CY&;w;0ojx& zk_M>Q(n=@oG`BXr_Yd^kztA7i!L=v<1)usJD9MVY!6V)$9`HVZU+(O959fEk{rmzU z|07#J2j-u^Pz3-^I88}VeQ7`eXDJ(7fknw(!Yyu}6K)G@XfLgx#y#%v`Z*08QRnW6 z$@Jx+aD}rZOZt2AV4Mcl_hdjhgQ>4If8XA5Q|>PVfF(B%YR!_Yi#4V!Z^ zbeE|8PeXIIX&~9Ev@4#0JD}0x_L89%NVKz-v01@3aNE2FA^DGy-bHC6*U2s$o&&u9 zA{BHynPYT2fOl{|KhQ0`j?rsO56TO`--FTMbzFC0JtEm)@{Wy)3>X+rTf41mX`KKFd zatgdsj9+^bb$UYynMA3!U>>wjk0;}MSt;Zog1mi2AuR(p;opdi=kiqt(MG}_$ed5& zQadZ%);P*0pixewkh)&1*o|auDX@yH&@ADB9gR<;nd{%bcl`P9&K6xJu|LQffbLIq zO--PNBvylV!!XH`G7L4#m9~qhe5Rd5rAao@t`t#fDDG*bL>7&Owh;!qu#K?VX;`U< zmr(agJ5ZS8s0^ChOnaConZzR0b)HCLVGk{AJg4?dT4#`Ja7o27DNt71&t4?F zYinDHbPza|w?REoxbi4)z<8Jppr19u(1bS(|3zNk>6hc8UnIqiBvWOSrZ>WPl6?5_ z^R!<~E4TWqPnqYb>TfkeuseC#E2eT6oVy4^eGP!n4%??K>M)zOsYCba0lmf?z&*N0 zJ?b%swLpdey#v@m^`(2P!#Y6e0M~=zf;7So=+4@vS@zbEjWNIQhY`Tl@4`?=0H)Wm z5f0hVn$y2QxBvUzg3TG+3S?MY5^M~denlRV$7F7ufO_eSJo(A0K|tSsTy=jo`?tUP z{ZXW*Jx{V2&)F|?ALHpqzDlyEsqitd{y^qqAPtgK^t`p|m%xCoKF+1^i%3G6L4iO| z2NX^^ep0d!Ur80tO6c`FQ`Poi|isfQuY<1jRtW6bNF zRaV9#O@XoTpiAY?VNq!uv0#Q+jqX$#=f|{#^eBB9*48$TJQFQ!yoM#3*iL!d>Oz~xF??MnH|G} zX=F)zPXEE4+u#S)ZjnYM_B1$I)ao+LdLL>VpBHbA-JJJp?eL#$3tp$Vuub#>!vnKe0QG>4j3;1Zt-||HM3dkIW zLYof3KKC@uPqianmT<>NBnO^pPLRPP)n&m>lEvwS3)BBpP_H_oddsqQ*INnDJbFo> zOPNk&pbgNW@;5-o+#W|s7KXFt+2XhU*~0(J+2To;9(CLDBM>lk7qqH|*)t8Z@~m|q z4(9q3eCA7Sy|SpWb4 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_osx.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_osx.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d95fc87025ff1caec196c15d4b78d54f98cda2ac GIT binary patch literal 5633 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_posix.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_posix.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..623c95e9703606b9ba42fe1021fd3c470c6312e4 GIT binary patch literal 4446 zcmd5<&u<(x6duoRb~lqvx=Gu#p`|!LNjIdENUa2fQVRVUgj$p!Doq=0CSxZ%o7wTy z_H?uPabb(Z3Ccf#gv5cr0&(tv!(2J#!iiH)JbO0V-4vvjc14qnJ@IEhKY!2myzkub zu(h~z{qFaphVh5d^^=45OYn%_kzo+SF$f}NfE=W2(=m0OaWYV6f^2=j89)X#Yq?s! zWjF;|ASTIvV@?=kV8i5#&?^@V)-A_E#zPd|(0E?s1;Dw+hdTHfzz3oITkt*PnxNfXt6fh%CrzLJVqv=9okx`g}z4gFnLp3dbBx1 zk5H>+w9pk}`PAKvbJQ7kj?v@fE$Dxgj?-hQg@@?gFSiY1E*a$+cNBvAWtHNU6_3>; z#-&(U!E15oNuPz-7np8F)RUB8$uOrf<{@?gkd(Wt0mZWFO5F5mKm?X?6i{KYHGB>W z$^CF$%wuH}hi;wD;A&ilp`5xz4<%H1+>i7`Zb-0;t1c%Ems7lO#kQ+EH*W?eb)BWaY=H-T4dbtA9)=Gw@u7N6bJWjT#)N7TQHS20yifYDk*3%nr1uAj8qgJTP`3kSzpZ%HG_v zW(mDND`j(Oy2of1Y%M9H<4~*{JI3G^l2ls;6s3|D--C(v|NyY*(+r>}B zguiJ=&BxXS)vAcOSXQ2?sdL8G$$hHq3jqV(#YK4Obj&8%^?DIkGG&p${(Qs9e9vWXL zHiJ4gUhF=1XteL`=x__XgZCk*cu^IdxTFgkfwnn#o{>;~NZBZBq%!tG`5lc97^~K*S*vJ?r|PxAU~u<4r$UzTIDz7oTD|CqqtaiBor4s+=ar0P;grZ%Dib9*Fj21P!h5!FlY{0g$!Rbg`{=cn#2pZ z$>OddI@?!?s7~00O6~QRjerPH5z>++qEpnVHtLbnpFD$f(Hw)N@1X$ zMd^}mPr<#0FRR$atG>iR>}}|^sSh~_8LP`J%;PWwbGB_;TOTel&YSZ}0{L}NSalFC z>DtuiAK2lzo2C!n{gF zr#(pq*hx$#oMO5ZTc`bLc3#+@732_WP@ZH}gY$ZKPg`g3sesa&NhjjYiGS{sW(N;)Kk3+b-7sB&d-35u*)K8Fq#$(7JZ4x=K} U#r#b5oSZEym4XiN*_W%F@ literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_windows.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/__pycache__/list_ports_windows.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e999bf3019a720a96405a0ea03ea7d59979815e GIT binary patch literal 5764 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 SSsM9Nf`6w9pGth9VEjMGlQoI} literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/hexlify_codec.py b/backend/venv/lib/python3.6/site-packages/serial/tools/hexlify_codec.py new file mode 100644 index 0000000..1371da2 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/tools/hexlify_codec.py @@ -0,0 +1,124 @@ +#! python +# +# This is a codec to create and decode hexdumps with spaces between characters. used by miniterm. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2015-2016 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +"""\ +Python 'hex' Codec - 2-digit hex with spaces content transfer encoding. + +Encode and decode may be a bit missleading at first sight... + +The textual representation is a hex dump: e.g. "40 41" +The "encoded" data of this is the binary form, e.g. b"@A" + +Therefore decoding is binary to text and thus converting binary data to hex dump. + +""" + +import codecs +import serial + + +try: + unicode +except (NameError, AttributeError): + unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name + + +HEXDIGITS = '0123456789ABCDEF' + + +# Codec APIs + +def hex_encode(data, errors='strict'): + """'40 41 42' -> b'@ab'""" + return (serial.to_bytes([int(h, 16) for h in data.split()]), len(data)) + + +def hex_decode(data, errors='strict'): + """b'@ab' -> '40 41 42'""" + return (unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))), len(data)) + + +class Codec(codecs.Codec): + def encode(self, data, errors='strict'): + """'40 41 42' -> b'@ab'""" + return serial.to_bytes([int(h, 16) for h in data.split()]) + + def decode(self, data, errors='strict'): + """b'@ab' -> '40 41 42'""" + return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))) + + +class IncrementalEncoder(codecs.IncrementalEncoder): + """Incremental hex encoder""" + + def __init__(self, errors='strict'): + self.errors = errors + self.state = 0 + + def reset(self): + self.state = 0 + + def getstate(self): + return self.state + + def setstate(self, state): + self.state = state + + def encode(self, data, final=False): + """\ + Incremental encode, keep track of digits and emit a byte when a pair + of hex digits is found. The space is optional unless the error + handling is defined to be 'strict'. + """ + state = self.state + encoded = [] + for c in data.upper(): + if c in HEXDIGITS: + z = HEXDIGITS.index(c) + if state: + encoded.append(z + (state & 0xf0)) + state = 0 + else: + state = 0x100 + (z << 4) + elif c == ' ': # allow spaces to separate values + if state and self.errors == 'strict': + raise UnicodeError('odd number of hex digits') + state = 0 + else: + if self.errors == 'strict': + raise UnicodeError('non-hex digit found: {!r}'.format(c)) + self.state = state + return serial.to_bytes(encoded) + + +class IncrementalDecoder(codecs.IncrementalDecoder): + """Incremental decoder""" + def decode(self, data, final=False): + return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))) + + +class StreamWriter(Codec, codecs.StreamWriter): + """Combination of hexlify codec and StreamWriter""" + + +class StreamReader(Codec, codecs.StreamReader): + """Combination of hexlify codec and StreamReader""" + + +def getregentry(): + """encodings module API""" + return codecs.CodecInfo( + name='hexlify', + encode=hex_encode, + decode=hex_decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + #~ _is_text_encoding=True, + ) diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/hexlify_codec.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/hexlify_codec.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8cfc1153acbcd77a9ecac028411c679d85e3982 GIT binary patch literal 4872 zcmd^D-BKJ?5boJO7P1M62|*Nf%T&y22}I1FF_nq|l2BGrsoBIts^Zi#a~6gyGi2s$ z1r%we{bPQf58xlEMs&e$VtnQWe^#iljWQQd0Ea&P>`S~`{07WZ%OH*1VdsRSuaTw zUn$GJ$ezg8%Owehl`*7@ksf16f@8`kE2Gk5lqEPWrlh=KX=2|KJ>IYcqg~#J@=o@6 zBNCkAWce7?uh-t-sqSI8zirpzsCr?|Y+b0%#ewmwQ`On2AY2V?6^qs9pibiEPy-jVNuJq-r8R4#H@4dL;Y{pCx+|A}@Y2`{||2SFT?B?2FrT^9y%uiJRFZ z^sS!*Nvm>cj?158up;Y@tcvV_$k8VWVZsoX6hEwl%3GGu>>37BWS`DEr$oQH^tDhtX!+ssNKDagtgG zoV6MT(ILGKYI^lbK8T3 zj6c1(ZAU21YGX@ zA=v36IJ3=?dkk+35VdXv@-m<>wA>S%;u%Qnz6{p4AXN_zu+qiuD7R5>5XXJf4{U!8 z$qb|u-Oa_xJYL3v=S7VT<9P~b&)bNDb_-MYG}SqSp|6QjM@(fdBV`OvO1PDrO5XC| zi;OGt9CDtg*dw7a+6^D@I|!l8vXWpM)?}zr*OAe;)e+6470C0#D72pU9we)|#2I!J z_uY)G$`iAQmyYt%t87^Afb7j4MNJ2%By&e%Qe)47=*Ba3>prR@HC6FL2<@6K51Q7ZT~)>3 zL1d=e&9lqdEy&i-L@Jt|J@z_3$!<}0ih@G>v?NJFVjZndkp+LZBs+OY8nRQ6b+kS@ z1yXK}gXy3>fY4{ZGH}2O3UYIt3zPgd;^t{ik{?yT3)KOOC0%^MGLNYY9=Fk6~28+aoMv}dp^4-cb|X=#D&UB86Z=&cMPq*O*Tj2f+ME3Rb)FVGgq*YVqd z1)wJBkFiPmJ_crol5^UrIHUMG<_zaffgZzbI5+BygIi;4xXivTGoH(W;IbsSZ*!Q$ zphvjVXr)Gl_f=Kuh*`~LX@3V#%7(U-s}zRw{CTwm%xh0|NC{v)%1VIIwu$|d_DqDx z|ErUjrx}@`{;iWZ2Y%m$NA(`9uW5TX0JrgEr*$+O>ozfsjYmmn z4|ECyfxcJnQx}0^Yi8>hnNMI{Cl?7GYFN<-IWVEHZX>F~)xrG|y`S zXSViJ&GC8}Ta22qOGRDY_%#!`^u9~)xwMcT;6c(hgN29XZ3PWUVsK-%$@YCrQ#KV@ zzAhnGcYkWEG-^K23t}H#_$(H(a>FL@fc`xFVTW)pgE8O(lp*p)b}_h_2c5EbAbUHN UT&Y+oR0 +# +# SPDX-License-Identifier: BSD-3-Clause + +"""\ +This module will provide a function called comports that returns an +iterable (generator or list) that will enumerate available com ports. Note that +on some systems non-existent ports may be listed. + +Additionally a grep function is supplied that can be used to search for ports +based on their descriptions or hardware ID. +""" + +import sys +import os +import re + +# chose an implementation, depending on os +#~ if sys.platform == 'cli': +#~ else: +if os.name == 'nt': # sys.platform == 'win32': + from serial.tools.list_ports_windows import comports +elif os.name == 'posix': + from serial.tools.list_ports_posix import comports +#~ elif os.name == 'java': +else: + raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name)) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +def grep(regexp, include_links=False): + """\ + Search for ports using a regular expression. Port name, description and + hardware ID are searched. The function returns an iterable that returns the + same tuples as comport() would do. + """ + r = re.compile(regexp, re.I) + for info in comports(include_links): + port, desc, hwid = info + if r.search(port) or r.search(desc) or r.search(hwid): + yield info + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def main(): + import argparse + + parser = argparse.ArgumentParser(description='Serial port enumeration') + + parser.add_argument( + 'regexp', + nargs='?', + help='only show ports that match this regex') + + parser.add_argument( + '-v', '--verbose', + action='store_true', + help='show more messages') + + parser.add_argument( + '-q', '--quiet', + action='store_true', + help='suppress all messages') + + parser.add_argument( + '-n', + type=int, + help='only output the N-th entry') + + parser.add_argument( + '-s', '--include-links', + action='store_true', + help='include entries that are symlinks to real devices') + + args = parser.parse_args() + + hits = 0 + # get iteraror w/ or w/o filter + if args.regexp: + if not args.quiet: + sys.stderr.write("Filtered list with regexp: {!r}\n".format(args.regexp)) + iterator = sorted(grep(args.regexp, include_links=args.include_links)) + else: + iterator = sorted(comports(include_links=args.include_links)) + # list them + for n, (port, desc, hwid) in enumerate(iterator, 1): + if args.n is None or args.n == n: + sys.stdout.write("{:20}\n".format(port)) + if args.verbose: + sys.stdout.write(" desc: {}\n".format(desc)) + sys.stdout.write(" hwid: {}\n".format(hwid)) + hits += 1 + if not args.quiet: + if hits: + sys.stderr.write("{} ports found\n".format(hits)) + else: + sys.stderr.write("no ports found\n") + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + main() diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7532ec75ab67cdcacd3f576e686c3bf53d9c7971 GIT binary patch literal 2815 zcmb_e&2k$>5bjx7vZdH@;v~)wA)bqgQiWtHP)W*AK#Gv6RDmlc2SS;oRBP?nT4%K@ z&&*m@#Xbb{4!i;f4xD)bPC0Pk2{`ZoaEGsZC0qEwDbns}rn{%7r~8|4RQhYK_GkN# zuR1jPl<@m0UiUMah-d??q9~w5(FQt2KcJ0Z+%M5aiA+E{QxuixML_loQB6@&re1|M zDx}hTV~es_q+^TeV~a&HGZa-uc3X-Tj}(1}<)-O-gi)nvMmjYzXDF(Qo1e(k=!oDS zQ3kYLuW{t}715J!>~t@S^2F#toFuw$v%NSnI@DWv+VOFg>Q0y>Ceod(*UzkX+IK^* zt?{`{oetAl?2QdK;h?^4QgnT0HJ&7P{#Icuo=uwfSPS2KVVuYe_|)RGsn;@S*mVs{ zx~ylk8#-@#PN!MgF#GUkQeRl>UO3d7MwT#9vsSwwMKQ;UNQVf0+nWAK{Mez(`~4)w zCM5PwnDXr0u^`jVgtpVwTZma=scnX=K^VSkVyh$LIyUaJf5#i^hBg|6*62qMnzi^J zeAaw`gr>fZjFO*0GftK-W6;lByzf3lZ#}bixQZR=xYtii4?7Aa$GrG3%Mn2mdJKBH zeskH~ymgYR4*Oj9< z*eP<~j)yRv98U4jF0t7`i4LY{t3+R`-6g{F6u4fm^Jd+zXf!_Sr?Q8%#_2YSV9j=( zgjSpVzBSGvdrkcq0McpLGw)7j6(EXg>}w*kniE`91{Ky%y5^*yC)m@+*!vGCQ8)4D zu#)yUG7X@*F@o#2^dQTVNJm*y%+~6hSE(UfPovM}ixcBH=Z{)aRZe>mPhrhtY!SN? zE9<8qVEjmjF(aMsAdc$LkTmWtdd-#1JWisOB;IT$ahmTp_V3+k-}#_%yLorT85@Vm ziqEpd!NU1=aX$J(amUDh2RrJ9H~H!6yT8>MPQIw(aix`$GWo=%}?mgP3q25w7}?NhZl|= z0#k?s>s@3d0$Dp{vcHqB(9SgNfX^kE z^JFJ2u876-*y3dBRZ(yPW$NxLW?Gycgmo7i-u;`N^0Eu$UMq<0FS2a0^=mR$4k0hQ z`+7q6mbhcq26xo2qqJ*6ILHBH6TEa8<8^_KaF`yGElypFQ6W5(hIZQtT0beUX_CHM zhBiwdz%}# zz(e-ggGl#40pT_p@v`eUs77NqkBxVi*b)rQG@(J^r;Jkx)(`u}U4x|ro8`WrdnRbT z*6>~I*V|zXEx8+L9>xhSC6G9uAY5#)D9-AtUbeNE547XRU#{MM@37|P&}IJNjIQdJ z6B0hYg=C)14@U{w%5qSaV;Q(yPAKjVmensnCRYHG-488NbQY5Pb{n@}>L0Vj0<*|k z6t!U^VjLKvIQ5c4nZ+tsl#X?L7jnSPN5)#I=D>pd3L#j>L@jn&a21zmjDUi~iX5(G z>1c+JkU^Qumrdw}^~N=2Hk8WL*-`5v8Zni?3Zmoa3Jk^G$##W_QT-O@zaEkT)Rka9SXMVcnN_??>a5sb4quq|OPP^T@$^u4h=2~0|**4!{ehH(3ubaZxVDm9ww+C?=Wdm2# +# +# SPDX-License-Identifier: BSD-3-Clause +import re +import glob +import os + + +def numsplit(text): + """\ + Convert string into a list of texts and numbers in order to support a + natural sorting. + """ + result = [] + for group in re.split(r'(\d+)', text): + if group: + try: + group = int(group) + except ValueError: + pass + result.append(group) + return result + + +class ListPortInfo(object): + """Info collection base class for serial ports""" + + def __init__(self, device=None): + self.device = device + self.name = None + self.description = 'n/a' + self.hwid = 'n/a' + # USB specific data + self.vid = None + self.pid = None + self.serial_number = None + self.location = None + self.manufacturer = None + self.product = None + self.interface = None + # special handling for links + if device is not None and os.path.islink(device): + self.hwid = 'LINK={}'.format(os.path.realpath(device)) + + def usb_description(self): + """return a short string to name the port based on USB info""" + if self.interface is not None: + return '{} - {}'.format(self.product, self.interface) + elif self.product is not None: + return self.product + else: + return self.name + + def usb_info(self): + """return a string with USB related information about device""" + return 'USB VID:PID={:04X}:{:04X}{}{}'.format( + self.vid or 0, + self.pid or 0, + ' SER={}'.format(self.serial_number) if self.serial_number is not None else '', + ' LOCATION={}'.format(self.location) if self.location is not None else '') + + def apply_usb_info(self): + """update description and hwid from USB data""" + self.description = self.usb_description() + self.hwid = self.usb_info() + + def __eq__(self, other): + return self.device == other.device + + def __lt__(self, other): + return numsplit(self.device) < numsplit(other.device) + + def __str__(self): + return '{} - {}'.format(self.device, self.description) + + def __getitem__(self, index): + """Item access: backwards compatible -> (port, desc, hwid)""" + if index == 0: + return self.device + elif index == 1: + return self.description + elif index == 2: + return self.hwid + else: + raise IndexError('{} > 2'.format(index)) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def list_links(devices): + """\ + search all /dev devices and look for symlinks to known ports already + listed in devices. + """ + links = [] + for device in glob.glob('/dev/*'): + if os.path.islink(device) and os.path.realpath(device) in devices: + links.append(device) + return links + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + print(ListPortInfo('dummy')) diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_common.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_common.pyc new file mode 100644 index 0000000000000000000000000000000000000000..67c57f41db06c018bbfa0e3315e6b3d21f3d144e GIT binary patch literal 3315 zcmcImZFAE`5WbV`Bn|;WOVeihc66X)29hw8LYpZWO2~kxjA3NTOQ!9OES-ZASu(m) zNGAA8!;kIv{($z`Jv-3yHIc2=YPDMJ%d>k|`}_3lU)|}au^1mUT<_qtZIA?iDKa58 z!35$h7%7sPFoEcUsgvYTh!>j>)kV?}JAo)SgsF*SN*qdY|3S_cx~*wgOaH;qi6vP2 z$`sCZoHhd45@IC9-V@=3*oJ8--oSJq?)`p8i~{inb{EC|yl~9JG=$IcbE58QVrN#A z4}_F5m*V*wDYomR4;}exQQ-d!vTiG&PsJb#$9j?Pn$juj$~50nY3>RYsVucl6@BH* zp0g^-6O~tko+&L9R8c0TRM4~4a9H5oXqGJHk*mrmQx+naT4teb;5)6~k}H>6#KCDH zO5=#P!z^`h8tKTLo5l5MlvQSO|lSKPZi@GRH=?%Ay*!@K7+Z zYv7hwdR3YwD_PoG&eFWvTiW~KroMS&>H6}m6>G{g%2r%aWEL9MX$ryWxEKtIe0jL< zW?%x*vU_L1+mAp*V@fW{DOr>A^0chW^U}>h`Va;lgqR+7}?kBLy@N zNt4R?R*FqP#Dl0oAR;tPjX|_IP3R%X3_*t&yfw|ttVm{Loz0XW%c?@Cg2T2L5^m z{(1)fdItV_2L5{H6fqun9ZU7hSE3f>kIR{u-842_;!cr!CXWWjoq|SUY+R;83XC;g zzD^Sc&F=U*#Pu{3eVE$gl#7@Zam4S@mj+Q@^`jUN1=^_xhh>pev2#uE0H7)H)NpU` zcdW_!A*J~E!KHPYr%vk&ux=MXglzg>Fuw8~kB7;K;96aSDvGeph1zL~@)Ff&8-oDC z+V~MhMc3bV-%C10d@xc=>R{BWgAYF;LJ8&)04IaUh39eKqECk8*6~Ju78Wbp)9)2@ z1tLqGF63Fc5F7=3G!Os4BZ|<6%}_jw7Qs;pzN1bWE7WMz6a&bsFz< z4(q@qO`?wd67<*ZUG=nmZ}oBe-tB|cYd4;cR{iB*bTG1%K-Jv{Zy$^>nBg~pN&^bI;m?f91ae!NgzhHf=XB4^ZlrQDJoT#I-oq^1l%Cqh&7 zq+%!ASSYf6J;}grNGxGo1ZU-k1CQ$BW2XfYAVR;ea~#RrM+`Me_!W__iCiFZ5#(Js%pJr2 zFr*CweTnyln6SazndVe;rrBuL=*QTF=TVx&Z(3fGYB1RMHNl+-DNbH>i{3W2Y-}*R b1mW>J%g+4dv$#gZu=Me{T62D3bx!^Z4=}F) literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_linux.py b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_linux.py new file mode 100644 index 0000000..4be27cd --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_linux.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# +# This is a module that gathers a list of serial ports including details on +# GNU/Linux systems. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2011-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +import glob +import os +from serial.tools import list_ports_common + + +class SysFS(list_ports_common.ListPortInfo): + """Wrapper for easy sysfs access and device info""" + + def __init__(self, device): + super(SysFS, self).__init__(device) + # special handling for links + if device is not None and os.path.islink(device): + device = os.path.realpath(device) + is_link = True + else: + is_link = False + self.name = os.path.basename(device) + self.usb_device_path = None + if os.path.exists('/sys/class/tty/{}/device'.format(self.name)): + self.device_path = os.path.realpath('/sys/class/tty/{}/device'.format(self.name)) + self.subsystem = os.path.basename(os.path.realpath(os.path.join(self.device_path, 'subsystem'))) + else: + self.device_path = None + self.subsystem = None + # check device type + if self.subsystem == 'usb-serial': + self.usb_interface_path = os.path.dirname(self.device_path) + elif self.subsystem == 'usb': + self.usb_interface_path = self.device_path + else: + self.usb_interface_path = None + # fill-in info for USB devices + if self.usb_interface_path is not None: + self.usb_device_path = os.path.dirname(self.usb_interface_path) + + try: + num_if = int(self.read_line(self.usb_device_path, 'bNumInterfaces')) + except ValueError: + num_if = 1 + + self.vid = int(self.read_line(self.usb_device_path, 'idVendor'), 16) + self.pid = int(self.read_line(self.usb_device_path, 'idProduct'), 16) + self.serial_number = self.read_line(self.usb_device_path, 'serial') + if num_if > 1: # multi interface devices like FT4232 + self.location = os.path.basename(self.usb_interface_path) + else: + self.location = os.path.basename(self.usb_device_path) + + self.manufacturer = self.read_line(self.usb_device_path, 'manufacturer') + self.product = self.read_line(self.usb_device_path, 'product') + self.interface = self.read_line(self.device_path, 'interface') + + if self.subsystem in ('usb', 'usb-serial'): + self.apply_usb_info() + #~ elif self.subsystem in ('pnp', 'amba'): # PCI based devices, raspi + elif self.subsystem == 'pnp': # PCI based devices + self.description = self.name + self.hwid = self.read_line(self.device_path, 'id') + elif self.subsystem == 'amba': # raspi + self.description = self.name + self.hwid = os.path.basename(self.device_path) + + if is_link: + self.hwid += ' LINK={}'.format(device) + + def read_line(self, *args): + """\ + Helper function to read a single line from a file. + One or more parameters are allowed, they are joined with os.path.join. + Returns None on errors.. + """ + try: + with open(os.path.join(*args)) as f: + line = f.readline().strip() + return line + except IOError: + return None + + +def comports(include_links=False): + devices = glob.glob('/dev/ttyS*') # built-in serial ports + devices.extend(glob.glob('/dev/ttyUSB*')) # usb-serial with own driver + devices.extend(glob.glob('/dev/ttyACM*')) # usb-serial with CDC-ACM profile + devices.extend(glob.glob('/dev/ttyAMA*')) # ARM internal port (raspi) + devices.extend(glob.glob('/dev/rfcomm*')) # BT serial devices + devices.extend(glob.glob('/dev/ttyAP*')) # Advantech multi-port serial controllers + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [info + for info in [SysFS(d) for d in devices] + if info.subsystem != "platform"] # hide non-present internal serial ports + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + for port, desc, hwid in sorted(comports()): + print("{}: {} [{}]".format(port, desc, hwid)) diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_linux.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_linux.pyc new file mode 100644 index 0000000000000000000000000000000000000000..08ffc258c5904b84d635518b52e03049c6219fa0 GIT binary patch literal 3038 zcmb_e?`|7K5TErWj_t%r|IjogNL;BRtg5jQ(12Q~=pUe!Xj;zD6hk?kee2|s^PPLU zZeywWRHS}EAn^|H0=x~+0H1gO_|2RziOWZ7-|p@Fy_wmW*>(P|*ZyeFJ?PQoH;eZt z80HsDAs$6BQRi^2sG|g5qPRo@hdK_a&>`*6?hN%oo1q_wo)dl3nWb=+flit99ED}t zD^d0n?O!HcAzfys=ZbDUI#1t0Pn9|~>daF(N4iS7MtYw54lRFv`*EMP5;3LSsKBh~ zAN)2O3QKzuQydxV57W#Vzn2aMX=2%n_Sk&ZKJ<&3Z(tY~lZH*=MW@6LvWX6bC0Wdn z*KRn>xsGWo3x-3Tx&1VAbzsJ>8JoUwgI-S?ypqrj^^>Tl-6-j&y%HpN@aJ!0n0-JN zkD_PrwM4rmdZyTEWhGv_vlAF(zszyvMTI8RDq9E4ZkZ#y47u#rf>!$vjYa1R@@%oF z6^jMYg!q=&66nIiB6Uhm6<-E5H=!=fb;8c_ru|hYSv*>>UGvBER493`keU&xvxS$7 zlTS~+2u^rb_{w@J4DMAa`%~E^VLl-<@Uv_!atAY8dy-yen44xeH7#B#1Wy;1BjobM zi7~HD^H&T0Gt>B5fnPp`N4OAy+_1BR6lKi_+7j)RDXWwiT;ViVNnFqtBsABAR%WDs zZdzl#(D*tf8->)ONZBc=qZGntWG7e~q@-BejT6YgO|yczg{*D$;=q`ewd2;BY2{gK ztDK~xF3467ObyV;bk~i}q9BHP0j-o=_&_INnnkQs#G>$imWHFAwPjY4i!5L{2$E4h z=-E-GGdl;waKgtQixR6dP_<|uCPT~h5`}gKuVBy(8Vq|tF-0lW+kWA)dteUOv zD2l^Y9Ce$paJ0X^|KWze@&5X|%@10+Wi6Ygu>r?na}~zsaO|-`%NXVgCQ{4ljH;?P z)SGIfR8`f|l6qS$snrs%&f~u_`L6=5V=Smu&=*vX_mD%${ql1R^A)D?)k(`lv)pyK zKmI7^#LwsG0Hp@x43`vIC*}i(w$LNLeE7_v_7)o141C_JQFf1%QlPXS@+4s~zeBq_ z`Q6sB^z~8F;{dugb$Pqoz%@~_9c!2O&FyFD0H}Tx>t?MGy$1r0gF%{UcNk=xT{^>I z05FK-^r;Rnxpqg71;bgQL-%Q9cihx8Ih&e{J5uLqluBY;PD&S+=qyV!(>!9>ltWK8 zH67~2)XPoE4lyckX@4%DmH9H#pGV1x%tT$hHlRC8EB7Ec{AS zgsb%`hT)b#Wd~cnqAIGc8f>B%?fYCYC|oXd-yfix#0;|Kz8|JN-qA#m*sk+v~iX4o?1N=7tIKX%1_JI@QAn)<2Ko$q7;#Pl%btcE+G!|)_pZ!z3 zNaGy4O@s%uTaQlB0dy62h3`xB_%X1nMgHTif+Tk+d%@+vmaPIEh=-U@1 z`UUuP`^Gfl`pvs9A?{v3hRFJSo4P1_HH-s`JJ3L~NHS(SPP@`y^uEQ>ApKa{pkDL0EX*=&|Q`%e@9(VlWZFVQR4fCTj!d&;;tF@ z+cop3!3pZU!xI;lWiW4mO=*B0;2iq}I^liXGVj2N+}+C{d6$?jXCP=g_ml0BD23Ye kytlw{> 24)] + while locationID & 0xf00000: + if len(loc) > 1: + loc.append('.') + loc.append('{}'.format((locationID >> 20) & 0xf)) + locationID <<= 4 + return ''.join(loc) + + +class SuitableSerialInterface(object): + pass + + +def scan_interfaces(): + """ + helper function to scan USB interfaces + returns a list of SuitableSerialInterface objects with name and id attributes + """ + interfaces = [] + for service in GetIOServicesByType('IOSerialBSDClient'): + device = get_string_property(service, "IOCalloutDevice") + if device: + usb_device = GetParentDeviceByType(service, "IOUSBInterface") + if usb_device: + name = get_string_property(usb_device, "USB Interface Name") or None + locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type) or '' + i = SuitableSerialInterface() + i.id = locationID + i.name = name + interfaces.append(i) + return interfaces + + +def search_for_locationID_in_interfaces(serial_interfaces, locationID): + for interface in serial_interfaces: + if (interface.id == locationID): + return interface.name + return None + + +def comports(include_links=False): + # XXX include_links is currently ignored. are links in /dev even supported here? + # Scan for all iokit serial ports + services = GetIOServicesByType('IOSerialBSDClient') + ports = [] + serial_interfaces = scan_interfaces() + for service in services: + # First, add the callout device file. + device = get_string_property(service, "IOCalloutDevice") + if device: + info = list_ports_common.ListPortInfo(device) + # If the serial port is implemented by IOUSBDevice + usb_device = GetParentDeviceByType(service, "IOUSBDevice") + if usb_device: + # fetch some useful informations from properties + info.vid = get_int_property(usb_device, "idVendor", kCFNumberSInt16Type) + info.pid = get_int_property(usb_device, "idProduct", kCFNumberSInt16Type) + info.serial_number = get_string_property(usb_device, "USB Serial Number") + info.product = get_string_property(usb_device, "USB Product Name") or 'n/a' + info.manufacturer = get_string_property(usb_device, "USB Vendor Name") + locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type) + info.location = location_to_string(locationID) + info.interface = search_for_locationID_in_interfaces(serial_interfaces, locationID) + info.apply_usb_info() + ports.append(info) + return ports + +# test +if __name__ == '__main__': + for port, desc, hwid in sorted(comports()): + print("{}: {} [{}]".format(port, desc, hwid)) diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_osx.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_osx.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a5c2c2b76f768c7d73dc538581a841460e4f266c GIT binary patch literal 7164 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_posix.py b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_posix.py new file mode 100644 index 0000000..0d580b0 --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_posix.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# +# This is a module that gathers a list of serial ports on POSIXy systems. +# For some specific implementations, see also list_ports_linux, list_ports_osx +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2011-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +"""\ +The ``comports`` function is expected to return an iterable that yields tuples +of 3 strings: port name, human readable description and a hardware ID. + +As currently no method is known to get the second two strings easily, they are +currently just identical to the port name. +""" + +import glob +import sys +import os +from serial.tools import list_ports_common + +# try to detect the OS so that a device can be selected... +plat = sys.platform.lower() + +if plat[:5] == 'linux': # Linux (confirmed) # noqa + from serial.tools.list_ports_linux import comports + +elif plat[:6] == 'darwin': # OS X (confirmed) + from serial.tools.list_ports_osx import comports + +elif plat == 'cygwin': # cygwin/win32 + # cygwin accepts /dev/com* in many contexts + # (such as 'open' call, explicit 'ls'), but 'glob.glob' + # and bare 'ls' do not; so use /dev/ttyS* instead + def comports(include_links=False): + devices = glob.glob('/dev/ttyS*') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:7] == 'openbsd': # OpenBSD + def comports(include_links=False): + devices = glob.glob('/dev/cua*') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:3] == 'bsd' or plat[:7] == 'freebsd': + def comports(include_links=False): + devices = glob.glob('/dev/cua*[!.init][!.lock]') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:6] == 'netbsd': # NetBSD + def comports(include_links=False): + """scan for available ports. return a list of device names.""" + devices = glob.glob('/dev/dty*') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:4] == 'irix': # IRIX + def comports(include_links=False): + """scan for available ports. return a list of device names.""" + devices = glob.glob('/dev/ttyf*') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:2] == 'hp': # HP-UX (not tested) + def comports(include_links=False): + """scan for available ports. return a list of device names.""" + devices = glob.glob('/dev/tty*p0') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:5] == 'sunos': # Solaris/SunOS + def comports(include_links=False): + """scan for available ports. return a list of device names.""" + devices = glob.glob('/dev/tty*c') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +elif plat[:3] == 'aix': # AIX + def comports(include_links=False): + """scan for available ports. return a list of device names.""" + devices = glob.glob('/dev/tty*') + if include_links: + devices.extend(list_ports_common.list_links(devices)) + return [list_ports_common.ListPortInfo(d) for d in devices] + +else: + # platform detection has failed... + import serial + sys.stderr.write("""\ +don't know how to enumerate ttys on this system. +! I you know how the serial ports are named send this information to +! the author of this module: + +sys.platform = {!r} +os.name = {!r} +pySerial version = {} + +also add the naming scheme of the serial ports and with a bit luck you can get +this module running... +""".format(sys.platform, os.name, serial.VERSION)) + raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name)) + +# test +if __name__ == '__main__': + for port, desc, hwid in sorted(comports()): + print("{}: {} [{}]".format(port, desc, hwid)) diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_posix.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_posix.pyc new file mode 100644 index 0000000000000000000000000000000000000000..184c3f3e167120f4615a6f7d473d6f73f6ef5f7a GIT binary patch literal 3966 zcmd5}X8CBFLweD}>~-~9@{```!g!4II_z1(rU_(IBXS3BFg-P69mj6By9{Z(o zFM(&jNxa|0%gvSmBtQe(0AvZ$02(M1atRuxpb z!|!LYYn64sfjlzQVIb)CDC*`&=l{{p!%wEdlR6v8pq%?9%LQ1>hAPM~g) zb*q6~8tQ&x-AUBF#=2X9JT=sP$2!XVb=EZld3vamtfSb@ua!nb2D23n=k37_{;y=r{srx zi~@~9{SW@`t|2tw7qCgS^Gyb)i70hVrYA^6O{%kezp$Lf$03NCcAck2#3VFTQ_8z@D! zom{7~mg=2|GrY8a?P~Mtl}m3$@7A2MI!J-~~yc~rB!Cate zJRa2aRJP;fL1Hk2z8Qlgj|&LuG%=ihoo0Yju2TqgnxX?wHA1J)Q{_3J2Git}TUMz9 zPWcqNAEo?YIntT-TX;`RvbV*n;D3hT$~XkP)-&Y~G--E@hDfk}w2T$;qgbb0M0i(5 zqsk;`JqoNq#p>q5<03a&Bhn8JWsSTy0;q)Bf%XOOQ*HJB!R>{Y!)Uygv^>K{&1af(1+^d4n_#(QH90ow2z5oyv;`r6sW1~8R z=k=j%u`kEbahLJzmnK{F%&0`$^ob0qEbk(_^GNRezzL**zKx{I4V+iqC|njBVqo&I zE=<6P6EKHz#J0HbO5|n4%BZ(S?qRzzlRa0rmDfr?Ac(c JTn)|z{{rbjkvjkY literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_windows.py b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_windows.py new file mode 100644 index 0000000..f28047b --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_windows.py @@ -0,0 +1,305 @@ +#! python +# +# Enumerate serial ports on Windows including a human readable description +# and hardware information. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2001-2016 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause + +# pylint: disable=invalid-name,too-few-public-methods +import re +import ctypes +from ctypes.wintypes import BOOL +from ctypes.wintypes import HWND +from ctypes.wintypes import DWORD +from ctypes.wintypes import WORD +from ctypes.wintypes import LONG +from ctypes.wintypes import ULONG +from ctypes.wintypes import HKEY +from ctypes.wintypes import BYTE +import serial +from serial.win32 import ULONG_PTR +from serial.tools import list_ports_common + + +def ValidHandle(value, func, arguments): + if value == 0: + raise ctypes.WinError() + return value + + +NULL = 0 +HDEVINFO = ctypes.c_void_p +LPCTSTR = ctypes.c_wchar_p +PCTSTR = ctypes.c_wchar_p +PTSTR = ctypes.c_wchar_p +LPDWORD = PDWORD = ctypes.POINTER(DWORD) +#~ LPBYTE = PBYTE = ctypes.POINTER(BYTE) +LPBYTE = PBYTE = ctypes.c_void_p # XXX avoids error about types + +ACCESS_MASK = DWORD +REGSAM = ACCESS_MASK + + +class GUID(ctypes.Structure): + _fields_ = [ + ('Data1', DWORD), + ('Data2', WORD), + ('Data3', WORD), + ('Data4', BYTE * 8), + ] + + def __str__(self): + return "{{{:08x}-{:04x}-{:04x}-{}-{}}}".format( + self.Data1, + self.Data2, + self.Data3, + ''.join(["{:02x}".format(d) for d in self.Data4[:2]]), + ''.join(["{:02x}".format(d) for d in self.Data4[2:]]), + ) + + +class SP_DEVINFO_DATA(ctypes.Structure): + _fields_ = [ + ('cbSize', DWORD), + ('ClassGuid', GUID), + ('DevInst', DWORD), + ('Reserved', ULONG_PTR), + ] + + def __str__(self): + return "ClassGuid:{} DevInst:{}".format(self.ClassGuid, self.DevInst) + + +PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA) + +PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p + +setupapi = ctypes.windll.LoadLibrary("setupapi") +SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList +SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO] +SetupDiDestroyDeviceInfoList.restype = BOOL + +SetupDiClassGuidsFromName = setupapi.SetupDiClassGuidsFromNameW +SetupDiClassGuidsFromName.argtypes = [PCTSTR, ctypes.POINTER(GUID), DWORD, PDWORD] +SetupDiClassGuidsFromName.restype = BOOL + +SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo +SetupDiEnumDeviceInfo.argtypes = [HDEVINFO, DWORD, PSP_DEVINFO_DATA] +SetupDiEnumDeviceInfo.restype = BOOL + +SetupDiGetClassDevs = setupapi.SetupDiGetClassDevsW +SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD] +SetupDiGetClassDevs.restype = HDEVINFO +SetupDiGetClassDevs.errcheck = ValidHandle + +SetupDiGetDeviceRegistryProperty = setupapi.SetupDiGetDeviceRegistryPropertyW +SetupDiGetDeviceRegistryProperty.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD] +SetupDiGetDeviceRegistryProperty.restype = BOOL + +SetupDiGetDeviceInstanceId = setupapi.SetupDiGetDeviceInstanceIdW +SetupDiGetDeviceInstanceId.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, PTSTR, DWORD, PDWORD] +SetupDiGetDeviceInstanceId.restype = BOOL + +SetupDiOpenDevRegKey = setupapi.SetupDiOpenDevRegKey +SetupDiOpenDevRegKey.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM] +SetupDiOpenDevRegKey.restype = HKEY + +advapi32 = ctypes.windll.LoadLibrary("Advapi32") +RegCloseKey = advapi32.RegCloseKey +RegCloseKey.argtypes = [HKEY] +RegCloseKey.restype = LONG + +RegQueryValueEx = advapi32.RegQueryValueExW +RegQueryValueEx.argtypes = [HKEY, LPCTSTR , LPDWORD, LPDWORD, LPBYTE, LPDWORD] +RegQueryValueEx.restype = LONG + + +DIGCF_PRESENT = 2 +DIGCF_DEVICEINTERFACE = 16 +INVALID_HANDLE_VALUE = 0 +ERROR_INSUFFICIENT_BUFFER = 122 +SPDRP_HARDWAREID = 1 +SPDRP_FRIENDLYNAME = 12 +SPDRP_LOCATION_PATHS = 35 +SPDRP_MFG = 11 +DICS_FLAG_GLOBAL = 1 +DIREG_DEV = 0x00000001 +KEY_READ = 0x20019 + + +def iterate_comports(): + """Return a generator that yields descriptions for serial ports""" + GUIDs = (GUID * 8)() # so far only seen one used, so hope 8 are enough... + guids_size = DWORD() + if not SetupDiClassGuidsFromName( + "Ports", + GUIDs, + ctypes.sizeof(GUIDs), + ctypes.byref(guids_size)): + raise ctypes.WinError() + + # repeat for all possible GUIDs + for index in range(guids_size.value): + bInterfaceNumber = None + g_hdi = SetupDiGetClassDevs( + ctypes.byref(GUIDs[index]), + None, + NULL, + DIGCF_PRESENT) # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports + + devinfo = SP_DEVINFO_DATA() + devinfo.cbSize = ctypes.sizeof(devinfo) + index = 0 + while SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)): + index += 1 + + # get the real com port name + hkey = SetupDiOpenDevRegKey( + g_hdi, + ctypes.byref(devinfo), + DICS_FLAG_GLOBAL, + 0, + DIREG_DEV, # DIREG_DRV for SW info + KEY_READ) + port_name_buffer = ctypes.create_unicode_buffer(250) + port_name_length = ULONG(ctypes.sizeof(port_name_buffer)) + RegQueryValueEx( + hkey, + "PortName", + None, + None, + ctypes.byref(port_name_buffer), + ctypes.byref(port_name_length)) + RegCloseKey(hkey) + + # unfortunately does this method also include parallel ports. + # we could check for names starting with COM or just exclude LPT + # and hope that other "unknown" names are serial ports... + if port_name_buffer.value.startswith('LPT'): + continue + + # hardware ID + szHardwareID = ctypes.create_unicode_buffer(250) + # try to get ID that includes serial number + if not SetupDiGetDeviceInstanceId( + g_hdi, + ctypes.byref(devinfo), + #~ ctypes.byref(szHardwareID), + szHardwareID, + ctypes.sizeof(szHardwareID) - 1, + None): + # fall back to more generic hardware ID if that would fail + if not SetupDiGetDeviceRegistryProperty( + g_hdi, + ctypes.byref(devinfo), + SPDRP_HARDWAREID, + None, + ctypes.byref(szHardwareID), + ctypes.sizeof(szHardwareID) - 1, + None): + # Ignore ERROR_INSUFFICIENT_BUFFER + if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: + raise ctypes.WinError() + # stringify + szHardwareID_str = szHardwareID.value + + info = list_ports_common.ListPortInfo(port_name_buffer.value) + + # in case of USB, make a more readable string, similar to that form + # that we also generate on other platforms + if szHardwareID_str.startswith('USB'): + m = re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(\w+))?', szHardwareID_str, re.I) + if m: + info.vid = int(m.group(1), 16) + if m.group(3): + info.pid = int(m.group(3), 16) + if m.group(5): + bInterfaceNumber = int(m.group(5)) + if m.group(7): + info.serial_number = m.group(7) + # calculate a location string + loc_path_str = ctypes.create_unicode_buffer(250) + if SetupDiGetDeviceRegistryProperty( + g_hdi, + ctypes.byref(devinfo), + SPDRP_LOCATION_PATHS, + None, + ctypes.byref(loc_path_str), + ctypes.sizeof(loc_path_str) - 1, + None): + m = re.finditer(r'USBROOT\((\w+)\)|#USB\((\w+)\)', loc_path_str.value) + location = [] + for g in m: + if g.group(1): + location.append('{:d}'.format(int(g.group(1)) + 1)) + else: + if len(location) > 1: + location.append('.') + else: + location.append('-') + location.append(g.group(2)) + if bInterfaceNumber is not None: + location.append(':{}.{}'.format( + 'x', # XXX how to determine correct bConfigurationValue? + bInterfaceNumber)) + if location: + info.location = ''.join(location) + info.hwid = info.usb_info() + elif szHardwareID_str.startswith('FTDIBUS'): + m = re.search(r'VID_([0-9a-f]{4})\+PID_([0-9a-f]{4})(\+(\w+))?', szHardwareID_str, re.I) + if m: + info.vid = int(m.group(1), 16) + info.pid = int(m.group(2), 16) + if m.group(4): + info.serial_number = m.group(4) + # USB location is hidden by FDTI driver :( + info.hwid = info.usb_info() + else: + info.hwid = szHardwareID_str + + # friendly name + szFriendlyName = ctypes.create_unicode_buffer(250) + if SetupDiGetDeviceRegistryProperty( + g_hdi, + ctypes.byref(devinfo), + SPDRP_FRIENDLYNAME, + #~ SPDRP_DEVICEDESC, + None, + ctypes.byref(szFriendlyName), + ctypes.sizeof(szFriendlyName) - 1, + None): + info.description = szFriendlyName.value + #~ else: + # Ignore ERROR_INSUFFICIENT_BUFFER + #~ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: + #~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value)) + # ignore errors and still include the port in the list, friendly name will be same as port name + + # manufacturer + szManufacturer = ctypes.create_unicode_buffer(250) + if SetupDiGetDeviceRegistryProperty( + g_hdi, + ctypes.byref(devinfo), + SPDRP_MFG, + #~ SPDRP_DEVICEDESC, + None, + ctypes.byref(szManufacturer), + ctypes.sizeof(szManufacturer) - 1, + None): + info.manufacturer = szManufacturer.value + yield info + SetupDiDestroyDeviceInfoList(g_hdi) + + +def comports(include_links=False): + """Return a list of info objects about serial ports""" + return list(iterate_comports()) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# test +if __name__ == '__main__': + for port, desc, hwid in sorted(comports()): + print("{}: {} [{}]".format(port, desc, hwid)) diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_windows.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/list_ports_windows.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fa5f4b67d67057b2ffcadc11348f3912d671db13 GIT binary patch literal 6912 zcmcIoUsn@Z60c5&U?2#hqKJPO9W>~Gh>ripnK32-GRBbUARK#i`h@fibPNe}cNAve zoZWRFcAxj0ecBJOAHa{W^A&c#$L`xc?Ch`V<`3%ZJ~==aRrl7dTmQOl-8TOO$Bg&rV#K+^+450X8oY3P|CJ0ZFaQhkWFj*@*; z&n2inOj{#lkANPc-@~9|`8KmMf@k^jtFv*++)IhiK=kiC=b3NdS-u0?(sttC%O|;o&BbG zKl^ffFZ+rhNvchgds3#ZlDbN@Yh+&|b%HhwQrF1&jFc(JjLgoGeVvrH3O0Po3!$$% zOB+#A*E{?`H2)mgb1aQ{F4G1?&2@NyXr3En&v%s60wEvL`b6fu@IwL+@WXl_H$|CS zWPdKgVU*~g#CfbtKdH|<8v)T9-6s1^had6KY6ITsa01bscgeon;RJ*Jo1~2xsk_2F zAPATdZ|%b|@!>sE_o#NC?E9n!#E18VANl|RKW4um`vK3YIH?KwJI9cPKj<(6nIZQS z*uYky$$m)g8M0I4B6A**dzS39jAR66$wd+^kV;T(k=*m-ULY5Fgbcb! z8%)Duz#Qp;2|X>4(6=kMlP1Fy6}rQ}uA0`F)16p{weFbeQ!c{$&smevXx zP4!_ev$kxtNoIYQ$}bldd#q1;WDKnNV!E_q9g_Pv`>aPjdbX0)6jvx&>J&Td$1qg) zg3#G+`eEQynp<1VM#aP^KM_1>Ji!HkeX)xjGeIFy&0w$m&LG%}sZx@e3myOo8nR}U zaBo`$;Sr!~UL))KO+U$kLpJzTx!zLZmTId}30b?cztP%KjW9@pAOXSGz-F(nx4gQ0 zz3#2g*1bk+cV_qQZRhr_nfci}*8}By<@)uo*{lcP=o<6dYq-tV!R+>4I0zZ5<+|rC zl^bqdodFrJeqt*sDx54&uD7aHg@<4+?u}s)ECE19%oswbqsD|GDlIwUX*4C`U(3`~t}TOHiG%lAyihjP{a~1?jrHUh;lWQgYMv zau6)GJU3*2Wz?%&BM8M8mO^EHrCiBSeqY&DlA$MnyqBTa4|&8MEd-~SuOs&UR500= z3RE~OcBeHBpbO?CV|YGZa!B}AAW>IEoeS0_3|OBskjQf=EIxItDF(k*#4kg59?v@w zA2!9sfeKsOB*)7Ve_oL(t za|MHl-M^UO2<<~7h|^jK&l{8cbd--UlkV_V$C&9bM>xtN!s8U4pf@o(=%er?mq~1J z5TkIcvt^7&c(9+sQ?w7Z)2t;cs`nFyHV%`)jB8jp&a5De@C@w>OKpPB0Sx);#?D{4 zE9z}{gu=5QD5%9t|Hy@bPJ z5#Fa0ww+jHCmvxnVSxxbh*S6p)h^0N*DnLKA15|WM*M%6;U%h}eeUOR#&Ov)>0prd z2g!dM2`@8JyTXxyGh9eU>o7o0l6q5;CB3QX&XNJ~-`hxAf{?uSD($=#Rl6ZX5`E~J zYlmEuU{P(q3f}Q+Klwk24WE%aD5lCZw1f>fTMve4_m8Yl=EZO6;3(}M?OEf0Z|)CK z4c8Uy4?_=4v8^VZ8pD5M=b{SlDWV^CVcd~rqoKP^29W-z##%Q(sJ z=4S4dXR0rLymc^rx%mD_^7-@R^Vip=r@z)EkxRE11qis~fFNsmdF6Rhc%D!1U&2V2 zlIlO@O8YS@a7H?rr1mhqKkd$yyv!Sm(g{S#E>LE_v++upcDO%Z5 zwyIJ(y3WK2m#a$Gm52qm#_cnj8=r^~OA8Hdy#f(*SmQq$` zEoEhM8Jrw)_sW-pP_B{^x2$C?TTZS}db+TXOXuJ`=Mhj0bB85s@{r9Ih}SrOd?j5_ zpvrz_Q}QAwY3VgW3F3y|Y;DWPEA&MsR&3)xM@io`9Ry+>dP)RD%lvXWwUS#dIK|Y; zQb~MP#r4w*m2WYZ2*FiQI%JuyH!J1PYc_;v1?!H-G3DIYe2vXGCfWfD7L3Z|pA}M1 zvT~7iRZ#K0ZCT9ycRR;2xM=&$y^g=DO*UKJN3n^oJb{F21I=FG1l#~}(4nT<)n+@J z(3smm7g^R-=Vd#k_!EKdq}z8m&a>X2zUYd>gCCa4zWciDD@2XC4i9k4E_TWe+}v!* zA~?j^E{B^uZ>?Zh0<(c0f!8ni9`2R(JJ{SAO{VT>E5{3eW1 zW7r%6J|c7+ZTl$h80U=vlQ|MHYJxvuoNVKIe8d>&J88tsL`2gGNQk%b4o5sPYMhKD zA}6E6#%V~Mz%MXUOYJ~j$FoD2yMh%Xks;#@=+nk%B;MnNY}vyYjl~VI4ELR~*T6-I zU)`zT{v`VG@1}wm$s%Yg$3V&&zxw)f%`85g$afnHLu7G;4qcTvL#0?)TnH`B7JYq^ zx+tGwbPfp0vTn{xmwk9tzSYPJYH@m4cL8)9oL5cHb+(0AYTt(1GnLJ>U_4lHm_t%4jjADuTwKl-R07zwGZ>*C5$?#^R3v;3Jv-tj50g@z&!T+u@@7;SrXy_)~~=pCueR zH4dLjDT~iUk*|DIMOAr;X0Tgax`q7xN{#Bw>-a9!JL@_%s8jP(RvPmbUthHa?VFC3 z;t|P!cMqME#b5obdpyktpgtTq9n#sZ_r#Q3VKtRU&#;t2jm$bAo@T8@UYupH#^759 z4;XyO;41)8BWybS5iPLhdH5j%Zd&A?DB%cD5|k?$#nnL&uD;eP*m_k+u#fUhgTwnk zf1mh@kFVg*010`eM^HKr|MF`-V#fPo@&!S@B1DEF`1FCXczk?xd~i6zrG<+U7p(IF iL2JD8AJl2VCjfigpiKQC +# +# SPDX-License-Identifier: BSD-3-Clause + +import codecs +import os +import sys +import threading + +import serial +from serial.tools.list_ports import comports +from serial.tools import hexlify_codec + +# pylint: disable=wrong-import-order,wrong-import-position + +codecs.register(lambda c: hexlify_codec.getregentry() if c == 'hexlify' else None) + +try: + raw_input +except NameError: + # pylint: disable=redefined-builtin,invalid-name + raw_input = input # in python3 it's "raw" + unichr = chr + + +def key_description(character): + """generate a readable description for a key""" + ascii_code = ord(character) + if ascii_code < 32: + return 'Ctrl+{:c}'.format(ord('@') + ascii_code) + else: + return repr(character) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +class ConsoleBase(object): + """OS abstraction for console (input/output codec, no echo)""" + + def __init__(self): + if sys.version_info >= (3, 0): + self.byte_output = sys.stdout.buffer + else: + self.byte_output = sys.stdout + self.output = sys.stdout + + def setup(self): + """Set console to read single characters, no echo""" + + def cleanup(self): + """Restore default console settings""" + + def getkey(self): + """Read a single key from the console""" + return None + + def write_bytes(self, byte_string): + """Write bytes (already encoded)""" + self.byte_output.write(byte_string) + self.byte_output.flush() + + def write(self, text): + """Write string""" + self.output.write(text) + self.output.flush() + + def cancel(self): + """Cancel getkey operation""" + + # - - - - - - - - - - - - - - - - - - - - - - - - + # context manager: + # switch terminal temporary to normal mode (e.g. to get user input) + + def __enter__(self): + self.cleanup() + return self + + def __exit__(self, *args, **kwargs): + self.setup() + + +if os.name == 'nt': # noqa + import msvcrt + import ctypes + + class Out(object): + """file-like wrapper that uses os.write""" + + def __init__(self, fd): + self.fd = fd + + def flush(self): + pass + + def write(self, s): + os.write(self.fd, s) + + class Console(ConsoleBase): + def __init__(self): + super(Console, self).__init__() + self._saved_ocp = ctypes.windll.kernel32.GetConsoleOutputCP() + self._saved_icp = ctypes.windll.kernel32.GetConsoleCP() + ctypes.windll.kernel32.SetConsoleOutputCP(65001) + ctypes.windll.kernel32.SetConsoleCP(65001) + self.output = codecs.getwriter('UTF-8')(Out(sys.stdout.fileno()), 'replace') + # the change of the code page is not propagated to Python, manually fix it + sys.stderr = codecs.getwriter('UTF-8')(Out(sys.stderr.fileno()), 'replace') + sys.stdout = self.output + self.output.encoding = 'UTF-8' # needed for input + + def __del__(self): + ctypes.windll.kernel32.SetConsoleOutputCP(self._saved_ocp) + ctypes.windll.kernel32.SetConsoleCP(self._saved_icp) + + def getkey(self): + while True: + z = msvcrt.getwch() + if z == unichr(13): + return unichr(10) + elif z in (unichr(0), unichr(0x0e)): # functions keys, ignore + msvcrt.getwch() + else: + return z + + def cancel(self): + # CancelIo, CancelSynchronousIo do not seem to work when using + # getwch, so instead, send a key to the window with the console + hwnd = ctypes.windll.kernel32.GetConsoleWindow() + ctypes.windll.user32.PostMessageA(hwnd, 0x100, 0x0d, 0) + +elif os.name == 'posix': + import atexit + import termios + import fcntl + + class Console(ConsoleBase): + def __init__(self): + super(Console, self).__init__() + self.fd = sys.stdin.fileno() + self.old = termios.tcgetattr(self.fd) + atexit.register(self.cleanup) + if sys.version_info < (3, 0): + self.enc_stdin = codecs.getreader(sys.stdin.encoding)(sys.stdin) + else: + self.enc_stdin = sys.stdin + + def setup(self): + new = termios.tcgetattr(self.fd) + new[3] = new[3] & ~termios.ICANON & ~termios.ECHO & ~termios.ISIG + new[6][termios.VMIN] = 1 + new[6][termios.VTIME] = 0 + termios.tcsetattr(self.fd, termios.TCSANOW, new) + + def getkey(self): + c = self.enc_stdin.read(1) + if c == unichr(0x7f): + c = unichr(8) # map the BS key (which yields DEL) to backspace + return c + + def cancel(self): + fcntl.ioctl(self.fd, termios.TIOCSTI, b'\0') + + def cleanup(self): + termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old) + +else: + raise NotImplementedError( + 'Sorry no implementation for your platform ({}) available.'.format(sys.platform)) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +class Transform(object): + """do-nothing: forward all data unchanged""" + def rx(self, text): + """text received from serial port""" + return text + + def tx(self, text): + """text to be sent to serial port""" + return text + + def echo(self, text): + """text to be sent but displayed on console""" + return text + + +class CRLF(Transform): + """ENTER sends CR+LF""" + + def tx(self, text): + return text.replace('\n', '\r\n') + + +class CR(Transform): + """ENTER sends CR""" + + def rx(self, text): + return text.replace('\r', '\n') + + def tx(self, text): + return text.replace('\n', '\r') + + +class LF(Transform): + """ENTER sends LF""" + + +class NoTerminal(Transform): + """remove typical terminal control codes from input""" + + REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32) if unichr(x) not in '\r\n\b\t') + REPLACEMENT_MAP.update( + { + 0x7F: 0x2421, # DEL + 0x9B: 0x2425, # CSI + }) + + def rx(self, text): + return text.translate(self.REPLACEMENT_MAP) + + echo = rx + + +class NoControls(NoTerminal): + """Remove all control codes, incl. CR+LF""" + + REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32)) + REPLACEMENT_MAP.update( + { + 0x20: 0x2423, # visual space + 0x7F: 0x2421, # DEL + 0x9B: 0x2425, # CSI + }) + + +class Printable(Transform): + """Show decimal code for all non-ASCII characters and replace most control codes""" + + def rx(self, text): + r = [] + for c in text: + if ' ' <= c < '\x7f' or c in '\r\n\b\t': + r.append(c) + elif c < ' ': + r.append(unichr(0x2400 + ord(c))) + else: + r.extend(unichr(0x2080 + ord(d) - 48) for d in '{:d}'.format(ord(c))) + r.append(' ') + return ''.join(r) + + echo = rx + + +class Colorize(Transform): + """Apply different colors for received and echo""" + + def __init__(self): + # XXX make it configurable, use colorama? + self.input_color = '\x1b[37m' + self.echo_color = '\x1b[31m' + + def rx(self, text): + return self.input_color + text + + def echo(self, text): + return self.echo_color + text + + +class DebugIO(Transform): + """Print what is sent and received""" + + def rx(self, text): + sys.stderr.write(' [RX:{}] '.format(repr(text))) + sys.stderr.flush() + return text + + def tx(self, text): + sys.stderr.write(' [TX:{}] '.format(repr(text))) + sys.stderr.flush() + return text + + +# other ideas: +# - add date/time for each newline +# - insert newline after: a) timeout b) packet end character + +EOL_TRANSFORMATIONS = { + 'crlf': CRLF, + 'cr': CR, + 'lf': LF, +} + +TRANSFORMATIONS = { + 'direct': Transform, # no transformation + 'default': NoTerminal, + 'nocontrol': NoControls, + 'printable': Printable, + 'colorize': Colorize, + 'debug': DebugIO, +} + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def ask_for_port(): + """\ + Show a list of ports and ask the user for a choice. To make selection + easier on systems with long device names, also allow the input of an + index. + """ + sys.stderr.write('\n--- Available ports:\n') + ports = [] + for n, (port, desc, hwid) in enumerate(sorted(comports()), 1): + sys.stderr.write('--- {:2}: {:20} {!r}\n'.format(n, port, desc)) + ports.append(port) + while True: + port = raw_input('--- Enter port index or full name: ') + try: + index = int(port) - 1 + if not 0 <= index < len(ports): + sys.stderr.write('--- Invalid index!\n') + continue + except ValueError: + pass + else: + port = ports[index] + return port + + +class Miniterm(object): + """\ + Terminal application. Copy data from serial port to console and vice versa. + Handle special keys from the console to show menu etc. + """ + + def __init__(self, serial_instance, echo=False, eol='crlf', filters=()): + self.console = Console() + self.serial = serial_instance + self.echo = echo + self.raw = False + self.input_encoding = 'UTF-8' + self.output_encoding = 'UTF-8' + self.eol = eol + self.filters = filters + self.update_transformations() + self.exit_character = 0x1d # GS/CTRL+] + self.menu_character = 0x14 # Menu: CTRL+T + self.alive = None + self._reader_alive = None + self.receiver_thread = None + self.rx_decoder = None + self.tx_decoder = None + + def _start_reader(self): + """Start reader thread""" + self._reader_alive = True + # start serial->console thread + self.receiver_thread = threading.Thread(target=self.reader, name='rx') + self.receiver_thread.daemon = True + self.receiver_thread.start() + + def _stop_reader(self): + """Stop reader thread only, wait for clean exit of thread""" + self._reader_alive = False + if hasattr(self.serial, 'cancel_read'): + self.serial.cancel_read() + self.receiver_thread.join() + + def start(self): + """start worker threads""" + self.alive = True + self._start_reader() + # enter console->serial loop + self.transmitter_thread = threading.Thread(target=self.writer, name='tx') + self.transmitter_thread.daemon = True + self.transmitter_thread.start() + self.console.setup() + + def stop(self): + """set flag to stop worker threads""" + self.alive = False + + def join(self, transmit_only=False): + """wait for worker threads to terminate""" + self.transmitter_thread.join() + if not transmit_only: + if hasattr(self.serial, 'cancel_read'): + self.serial.cancel_read() + self.receiver_thread.join() + + def close(self): + self.serial.close() + + def update_transformations(self): + """take list of transformation classes and instantiate them for rx and tx""" + transformations = [EOL_TRANSFORMATIONS[self.eol]] + [TRANSFORMATIONS[f] + for f in self.filters] + self.tx_transformations = [t() for t in transformations] + self.rx_transformations = list(reversed(self.tx_transformations)) + + def set_rx_encoding(self, encoding, errors='replace'): + """set encoding for received data""" + self.input_encoding = encoding + self.rx_decoder = codecs.getincrementaldecoder(encoding)(errors) + + def set_tx_encoding(self, encoding, errors='replace'): + """set encoding for transmitted data""" + self.output_encoding = encoding + self.tx_encoder = codecs.getincrementalencoder(encoding)(errors) + + def dump_port_settings(self): + """Write current settings to sys.stderr""" + sys.stderr.write("\n--- Settings: {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits}\n".format( + p=self.serial)) + sys.stderr.write('--- RTS: {:8} DTR: {:8} BREAK: {:8}\n'.format( + ('active' if self.serial.rts else 'inactive'), + ('active' if self.serial.dtr else 'inactive'), + ('active' if self.serial.break_condition else 'inactive'))) + try: + sys.stderr.write('--- CTS: {:8} DSR: {:8} RI: {:8} CD: {:8}\n'.format( + ('active' if self.serial.cts else 'inactive'), + ('active' if self.serial.dsr else 'inactive'), + ('active' if self.serial.ri else 'inactive'), + ('active' if self.serial.cd else 'inactive'))) + except serial.SerialException: + # on RFC 2217 ports, it can happen if no modem state notification was + # yet received. ignore this error. + pass + sys.stderr.write('--- software flow control: {}\n'.format('active' if self.serial.xonxoff else 'inactive')) + sys.stderr.write('--- hardware flow control: {}\n'.format('active' if self.serial.rtscts else 'inactive')) + sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding)) + sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding)) + sys.stderr.write('--- EOL: {}\n'.format(self.eol.upper())) + sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters))) + + def reader(self): + """loop and copy serial->console""" + try: + while self.alive and self._reader_alive: + # read all that is there or wait for one byte + data = self.serial.read(self.serial.in_waiting or 1) + if data: + if self.raw: + self.console.write_bytes(data) + else: + text = self.rx_decoder.decode(data) + for transformation in self.rx_transformations: + text = transformation.rx(text) + self.console.write(text) + except serial.SerialException: + self.alive = False + self.console.cancel() + raise # XXX handle instead of re-raise? + + def writer(self): + """\ + Loop and copy console->serial until self.exit_character character is + found. When self.menu_character is found, interpret the next key + locally. + """ + menu_active = False + try: + while self.alive: + try: + c = self.console.getkey() + except KeyboardInterrupt: + c = '\x03' + if not self.alive: + break + if menu_active: + self.handle_menu_key(c) + menu_active = False + elif c == self.menu_character: + menu_active = True # next char will be for menu + elif c == self.exit_character: + self.stop() # exit app + break + else: + #~ if self.raw: + text = c + for transformation in self.tx_transformations: + text = transformation.tx(text) + self.serial.write(self.tx_encoder.encode(text)) + if self.echo: + echo_text = c + for transformation in self.tx_transformations: + echo_text = transformation.echo(echo_text) + self.console.write(echo_text) + except: + self.alive = False + raise + + def handle_menu_key(self, c): + """Implement a simple menu / settings""" + if c == self.menu_character or c == self.exit_character: + # Menu/exit character again -> send itself + self.serial.write(self.tx_encoder.encode(c)) + if self.echo: + self.console.write(c) + elif c == '\x15': # CTRL+U -> upload file + self.upload_file() + elif c in '\x08hH?': # CTRL+H, h, H, ? -> Show help + sys.stderr.write(self.get_help_text()) + elif c == '\x12': # CTRL+R -> Toggle RTS + self.serial.rts = not self.serial.rts + sys.stderr.write('--- RTS {} ---\n'.format('active' if self.serial.rts else 'inactive')) + elif c == '\x04': # CTRL+D -> Toggle DTR + self.serial.dtr = not self.serial.dtr + sys.stderr.write('--- DTR {} ---\n'.format('active' if self.serial.dtr else 'inactive')) + elif c == '\x02': # CTRL+B -> toggle BREAK condition + self.serial.break_condition = not self.serial.break_condition + sys.stderr.write('--- BREAK {} ---\n'.format('active' if self.serial.break_condition else 'inactive')) + elif c == '\x05': # CTRL+E -> toggle local echo + self.echo = not self.echo + sys.stderr.write('--- local echo {} ---\n'.format('active' if self.echo else 'inactive')) + elif c == '\x06': # CTRL+F -> edit filters + self.change_filter() + elif c == '\x0c': # CTRL+L -> EOL mode + modes = list(EOL_TRANSFORMATIONS) # keys + eol = modes.index(self.eol) + 1 + if eol >= len(modes): + eol = 0 + self.eol = modes[eol] + sys.stderr.write('--- EOL: {} ---\n'.format(self.eol.upper())) + self.update_transformations() + elif c == '\x01': # CTRL+A -> set encoding + self.change_encoding() + elif c == '\x09': # CTRL+I -> info + self.dump_port_settings() + #~ elif c == '\x01': # CTRL+A -> cycle escape mode + #~ elif c == '\x0c': # CTRL+L -> cycle linefeed mode + elif c in 'pP': # P -> change port + self.change_port() + elif c in 'sS': # S -> suspend / open port temporarily + self.suspend_port() + elif c in 'bB': # B -> change baudrate + self.change_baudrate() + elif c == '8': # 8 -> change to 8 bits + self.serial.bytesize = serial.EIGHTBITS + self.dump_port_settings() + elif c == '7': # 7 -> change to 8 bits + self.serial.bytesize = serial.SEVENBITS + self.dump_port_settings() + elif c in 'eE': # E -> change to even parity + self.serial.parity = serial.PARITY_EVEN + self.dump_port_settings() + elif c in 'oO': # O -> change to odd parity + self.serial.parity = serial.PARITY_ODD + self.dump_port_settings() + elif c in 'mM': # M -> change to mark parity + self.serial.parity = serial.PARITY_MARK + self.dump_port_settings() + elif c in 'sS': # S -> change to space parity + self.serial.parity = serial.PARITY_SPACE + self.dump_port_settings() + elif c in 'nN': # N -> change to no parity + self.serial.parity = serial.PARITY_NONE + self.dump_port_settings() + elif c == '1': # 1 -> change to 1 stop bits + self.serial.stopbits = serial.STOPBITS_ONE + self.dump_port_settings() + elif c == '2': # 2 -> change to 2 stop bits + self.serial.stopbits = serial.STOPBITS_TWO + self.dump_port_settings() + elif c == '3': # 3 -> change to 1.5 stop bits + self.serial.stopbits = serial.STOPBITS_ONE_POINT_FIVE + self.dump_port_settings() + elif c in 'xX': # X -> change software flow control + self.serial.xonxoff = (c == 'X') + self.dump_port_settings() + elif c in 'rR': # R -> change hardware flow control + self.serial.rtscts = (c == 'R') + self.dump_port_settings() + else: + sys.stderr.write('--- unknown menu character {} --\n'.format(key_description(c))) + + def upload_file(self): + """Ask user for filenname and send its contents""" + sys.stderr.write('\n--- File to upload: ') + sys.stderr.flush() + with self.console: + filename = sys.stdin.readline().rstrip('\r\n') + if filename: + try: + with open(filename, 'rb') as f: + sys.stderr.write('--- Sending file {} ---\n'.format(filename)) + while True: + block = f.read(1024) + if not block: + break + self.serial.write(block) + # Wait for output buffer to drain. + self.serial.flush() + sys.stderr.write('.') # Progress indicator. + sys.stderr.write('\n--- File {} sent ---\n'.format(filename)) + except IOError as e: + sys.stderr.write('--- ERROR opening file {}: {} ---\n'.format(filename, e)) + + def change_filter(self): + """change the i/o transformations""" + sys.stderr.write('\n--- Available Filters:\n') + sys.stderr.write('\n'.join( + '--- {:<10} = {.__doc__}'.format(k, v) + for k, v in sorted(TRANSFORMATIONS.items()))) + sys.stderr.write('\n--- Enter new filter name(s) [{}]: '.format(' '.join(self.filters))) + with self.console: + new_filters = sys.stdin.readline().lower().split() + if new_filters: + for f in new_filters: + if f not in TRANSFORMATIONS: + sys.stderr.write('--- unknown filter: {}\n'.format(repr(f))) + break + else: + self.filters = new_filters + self.update_transformations() + sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters))) + + def change_encoding(self): + """change encoding on the serial port""" + sys.stderr.write('\n--- Enter new encoding name [{}]: '.format(self.input_encoding)) + with self.console: + new_encoding = sys.stdin.readline().strip() + if new_encoding: + try: + codecs.lookup(new_encoding) + except LookupError: + sys.stderr.write('--- invalid encoding name: {}\n'.format(new_encoding)) + else: + self.set_rx_encoding(new_encoding) + self.set_tx_encoding(new_encoding) + sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding)) + sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding)) + + def change_baudrate(self): + """change the baudrate""" + sys.stderr.write('\n--- Baudrate: ') + sys.stderr.flush() + with self.console: + backup = self.serial.baudrate + try: + self.serial.baudrate = int(sys.stdin.readline().strip()) + except ValueError as e: + sys.stderr.write('--- ERROR setting baudrate: {} ---\n'.format(e)) + self.serial.baudrate = backup + else: + self.dump_port_settings() + + def change_port(self): + """Have a conversation with the user to change the serial port""" + with self.console: + try: + port = ask_for_port() + except KeyboardInterrupt: + port = None + if port and port != self.serial.port: + # reader thread needs to be shut down + self._stop_reader() + # save settings + settings = self.serial.getSettingsDict() + try: + new_serial = serial.serial_for_url(port, do_not_open=True) + # restore settings and open + new_serial.applySettingsDict(settings) + new_serial.rts = self.serial.rts + new_serial.dtr = self.serial.dtr + new_serial.open() + new_serial.break_condition = self.serial.break_condition + except Exception as e: + sys.stderr.write('--- ERROR opening new port: {} ---\n'.format(e)) + new_serial.close() + else: + self.serial.close() + self.serial = new_serial + sys.stderr.write('--- Port changed to: {} ---\n'.format(self.serial.port)) + # and restart the reader thread + self._start_reader() + + def suspend_port(self): + """\ + open port temporarily, allow reconnect, exit and port change to get + out of the loop + """ + # reader thread needs to be shut down + self._stop_reader() + self.serial.close() + sys.stderr.write('\n--- Port closed: {} ---\n'.format(self.serial.port)) + do_change_port = False + while not self.serial.is_open: + sys.stderr.write('--- Quit: {exit} | p: port change | any other key to reconnect ---\n'.format( + exit=key_description(self.exit_character))) + k = self.console.getkey() + if k == self.exit_character: + self.stop() # exit app + break + elif k in 'pP': + do_change_port = True + break + try: + self.serial.open() + except Exception as e: + sys.stderr.write('--- ERROR opening port: {} ---\n'.format(e)) + if do_change_port: + self.change_port() + else: + # and restart the reader thread + self._start_reader() + sys.stderr.write('--- Port opened: {} ---\n'.format(self.serial.port)) + + def get_help_text(self): + """return the help text""" + # help text, starts with blank line! + return """ +--- pySerial ({version}) - miniterm - help +--- +--- {exit:8} Exit program +--- {menu:8} Menu escape key, followed by: +--- Menu keys: +--- {menu:7} Send the menu character itself to remote +--- {exit:7} Send the exit character itself to remote +--- {info:7} Show info +--- {upload:7} Upload file (prompt will be shown) +--- {repr:7} encoding +--- {filter:7} edit filters +--- Toggles: +--- {rts:7} RTS {dtr:7} DTR {brk:7} BREAK +--- {echo:7} echo {eol:7} EOL +--- +--- Port settings ({menu} followed by the following): +--- p change port +--- 7 8 set data bits +--- N E O S M change parity (None, Even, Odd, Space, Mark) +--- 1 2 3 set stop bits (1, 2, 1.5) +--- b change baud rate +--- x X disable/enable software flow control +--- r R disable/enable hardware flow control +""".format(version=getattr(serial, 'VERSION', 'unknown version'), + exit=key_description(self.exit_character), + menu=key_description(self.menu_character), + rts=key_description('\x12'), + dtr=key_description('\x04'), + brk=key_description('\x02'), + echo=key_description('\x05'), + info=key_description('\x09'), + upload=key_description('\x15'), + repr=key_description('\x01'), + filter=key_description('\x06'), + eol=key_description('\x0c')) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# default args can be used to override when calling main() from an other script +# e.g to create a miniterm-my-device.py +def main(default_port=None, default_baudrate=9600, default_rts=None, default_dtr=None): + """Command line tool, entry point""" + + import argparse + + parser = argparse.ArgumentParser( + description="Miniterm - A simple terminal program for the serial port.") + + parser.add_argument( + "port", + nargs='?', + help="serial port name ('-' to show port list)", + default=default_port) + + parser.add_argument( + "baudrate", + nargs='?', + type=int, + help="set baud rate, default: %(default)s", + default=default_baudrate) + + group = parser.add_argument_group("port settings") + + group.add_argument( + "--parity", + choices=['N', 'E', 'O', 'S', 'M'], + type=lambda c: c.upper(), + help="set parity, one of {N E O S M}, default: N", + default='N') + + group.add_argument( + "--rtscts", + action="store_true", + help="enable RTS/CTS flow control (default off)", + default=False) + + group.add_argument( + "--xonxoff", + action="store_true", + help="enable software flow control (default off)", + default=False) + + group.add_argument( + "--rts", + type=int, + help="set initial RTS line state (possible values: 0, 1)", + default=default_rts) + + group.add_argument( + "--dtr", + type=int, + help="set initial DTR line state (possible values: 0, 1)", + default=default_dtr) + + group.add_argument( + "--ask", + action="store_true", + help="ask again for port when open fails", + default=False) + + group = parser.add_argument_group("data handling") + + group.add_argument( + "-e", "--echo", + action="store_true", + help="enable local echo (default off)", + default=False) + + group.add_argument( + "--encoding", + dest="serial_port_encoding", + metavar="CODEC", + help="set the encoding for the serial port (e.g. hexlify, Latin1, UTF-8), default: %(default)s", + default='UTF-8') + + group.add_argument( + "-f", "--filter", + action="append", + metavar="NAME", + help="add text transformation", + default=[]) + + group.add_argument( + "--eol", + choices=['CR', 'LF', 'CRLF'], + type=lambda c: c.upper(), + help="end of line mode", + default='CRLF') + + group.add_argument( + "--raw", + action="store_true", + help="Do no apply any encodings/transformations", + default=False) + + group = parser.add_argument_group("hotkeys") + + group.add_argument( + "--exit-char", + type=int, + metavar='NUM', + help="Unicode of special character that is used to exit the application, default: %(default)s", + default=0x1d) # GS/CTRL+] + + group.add_argument( + "--menu-char", + type=int, + metavar='NUM', + help="Unicode code of special character that is used to control miniterm (menu), default: %(default)s", + default=0x14) # Menu: CTRL+T + + group = parser.add_argument_group("diagnostics") + + group.add_argument( + "-q", "--quiet", + action="store_true", + help="suppress non-error messages", + default=False) + + group.add_argument( + "--develop", + action="store_true", + help="show Python traceback on error", + default=False) + + args = parser.parse_args() + + if args.menu_char == args.exit_char: + parser.error('--exit-char can not be the same as --menu-char') + + if args.filter: + if 'help' in args.filter: + sys.stderr.write('Available filters:\n') + sys.stderr.write('\n'.join( + '{:<10} = {.__doc__}'.format(k, v) + for k, v in sorted(TRANSFORMATIONS.items()))) + sys.stderr.write('\n') + sys.exit(1) + filters = args.filter + else: + filters = ['default'] + + while True: + # no port given on command line -> ask user now + if args.port is None or args.port == '-': + try: + args.port = ask_for_port() + except KeyboardInterrupt: + sys.stderr.write('\n') + parser.error('user aborted and port is not given') + else: + if not args.port: + parser.error('port is not given') + try: + serial_instance = serial.serial_for_url( + args.port, + args.baudrate, + parity=args.parity, + rtscts=args.rtscts, + xonxoff=args.xonxoff, + do_not_open=True) + + if not hasattr(serial_instance, 'cancel_read'): + # enable timeout for alive flag polling if cancel_read is not available + serial_instance.timeout = 1 + + if args.dtr is not None: + if not args.quiet: + sys.stderr.write('--- forcing DTR {}\n'.format('active' if args.dtr else 'inactive')) + serial_instance.dtr = args.dtr + if args.rts is not None: + if not args.quiet: + sys.stderr.write('--- forcing RTS {}\n'.format('active' if args.rts else 'inactive')) + serial_instance.rts = args.rts + + serial_instance.open() + except serial.SerialException as e: + sys.stderr.write('could not open port {}: {}\n'.format(repr(args.port), e)) + if args.develop: + raise + if not args.ask: + sys.exit(1) + else: + args.port = '-' + else: + break + + miniterm = Miniterm( + serial_instance, + echo=args.echo, + eol=args.eol.lower(), + filters=filters) + miniterm.exit_character = unichr(args.exit_char) + miniterm.menu_character = unichr(args.menu_char) + miniterm.raw = args.raw + miniterm.set_rx_encoding(args.serial_port_encoding) + miniterm.set_tx_encoding(args.serial_port_encoding) + + if not args.quiet: + sys.stderr.write('--- Miniterm on {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits} ---\n'.format( + p=miniterm.serial)) + sys.stderr.write('--- Quit: {} | Menu: {} | Help: {} followed by {} ---\n'.format( + key_description(miniterm.exit_character), + key_description(miniterm.menu_character), + key_description(miniterm.menu_character), + key_description('\x08'))) + + miniterm.start() + try: + miniterm.join(True) + except KeyboardInterrupt: + pass + if not args.quiet: + sys.stderr.write("\n--- exit ---\n") + miniterm.join() + miniterm.close() + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +if __name__ == '__main__': + main() diff --git a/backend/venv/lib/python3.6/site-packages/serial/tools/miniterm.pyc b/backend/venv/lib/python3.6/site-packages/serial/tools/miniterm.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3e6e5cbad94cc5ac3114af02788fd93d6947d8ab GIT binary patch literal 30469 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__init__.py b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__init__.pyc b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..17d71fe01ef09a8825cacd00df3b4851b9a8a8c8 GIT binary patch literal 145 zcmZSn%*(|&d0%KU0~9aVbdB`P^@~%BG81$3ON(+c67y1WQj7HC<1_OzOXB183MxxDfZA06;GunE(I) literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/__init__.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ca49b051b8dc9dcfa08ba41c73c5ce08e0dc449 GIT binary patch literal 147 zcmXr!<>d;hagSjDg2x~N1{i@12OutH0TL+;!3>&=ek&P@K*9*(mxF#uZh?M5W`S;I zUU5lcPL6JTa%rZ4WnqqfL1l4jQD$O}K9njg%E?H~OUX$s(vOeN%*!l^kJl@xyv1RY Qo1apelWGStxfqBU0Ej*$aR2}S literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_alt.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_alt.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..feb1675861af5962b421e4b73f06f2e01053222e GIT binary patch literal 1309 zcmb_bO>f&a7$)^)S#i=7Eefpb2Cj!JF3_rIPYc#Iz)}nYhN4Kerc)XSs>sArXi+BV z*7mF&=Khdg`xp8T_7`~AWvAVD+G(#SZ|m-pz*pq!htK=G59#}xo6+I&>leTD5c&;$ z@NB?*ABIXG%m)-v%u#}lQT3^Z7`}+)EsWaN{^FEwf_^}i^SoPoiANpkzCnr40_t5j z@)`5W9`#o^30a?oD-1jF5gG=+13!9_yuv?Y>Nd{&VD=Eu0c3mBTRCO8LI>z{_l48Ajkk2E4?2>iM}v=0<4pE- zpZ0*m?HDHe$8C4_Jf!>3-CK+rAEU)JTPp+{Xm2YWL!Y!y`;GsT|J-)0BkeAom80GR zV+6>xCcV++n?Mhi0q|@B=2yUM{EMN3atricg0X!L`O65Xt`}oAt68oYCD}Spii+qFBWFU6$xXoQ#c@k<@<`OW z5Y@?pR~etO)03V5u1`;I?vYAppvk0$0_1g}N2DVlyFY#=o1qDgm@G2>o$+&?DJ9#} zTU?!17nOKjk&bYWwAjVr|EsL!e6qOmcPU$bUFmbXuvJN+Y!Bd6Q9&bB&WJc8ooa)I zmrXd8yw0RzcBX1xXcMS>#KsK1w!7p~hkrcFnYGLWt(loNq1~BIl<_B1Cg;X4G#e`u zJ!>s}ETxd9H_B9|HB8Q#wv(t(YWlJ>%mKrUSBj!CLB%u|x$){ukA^+tf->Vn1r3B2 zomN?^sOn6++yN2TR2MUbf zMsX(NS_&<4!BaTjJN4XL?I3Aa7urn$-14hi5Ren$2z&6h(ZM%xh$H73-nO~xkllhY zfG9r!v?5W_qvw9dwzuma;&A7d*os$EQ zz>Al6y5nk{0g)lV{wdP>lcvemTKrnJ!Ein`{71BP=!0;>iu-ab=#;aES74>KAvjk0 MwiCMHb!ZI#1%g_Ih5!Hn literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_hwgrep.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_hwgrep.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0b8a764df81ee1352823813c0d56d7b3fff9a93 GIT binary patch literal 1781 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 literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_rfc2217.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_rfc2217.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..35c1926af4505636b51ef62a03b81b8b48f0f74f GIT binary patch literal 200 zcmXr!<>d;hagPyYWMFvAfCQL;YzH7N761|{3@MB`47rR^jEoE^Ou-DA%rAkW44RC$ z*n(4wG81$BG?}9Kios01qO@cqBSZ5dkO?apikN{EnE2(PUy@s(Uyxa#o0(T!l9-dD z8=qX7X<%8HqhC-7(X0=pN{ezb67y1WQj7Epitd;hagPyYWMFvAfCQL;YzH7N761|{3@MB`47rR^jEoE^Ou-DA%rAkW44RC$ z*n(4wG81$BG?}9Kios01qO@cqBSZ5dkO?apikN{EnE2(VUy@s(Uyxa#o0(T!l9-dD z8=qX7X<%8HqhC-7(X0=pN{ezb67y1WQj7Epitg3My}L*yQG? Pl;)(`aRIF;24V&P^$Ime literal 0 HcmV?d00001 diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_socket.cpython-36.pyc b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/__pycache__/protocol_socket.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..445392fdb4fed5c5862fc59066fee86fc4d325dc GIT binary patch literal 9007 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 +# +# SPDX-License-Identifier: BSD-3-Clause +# +# URL format: alt://port[?option[=value][&option[=value]]] +# options: +# - class=X used class named X instead of Serial +# +# example: +# use poll based implementation on Posix (Linux): +# python -m serial.tools.miniterm alt:///dev/ttyUSB0?class=PosixPollSerial + +try: + import urlparse +except ImportError: + import urllib.parse as urlparse + +import serial + + +def serial_class_for_url(url): + """extract host and port from an URL string""" + parts = urlparse.urlsplit(url) + if parts.scheme != 'alt': + raise serial.SerialException( + 'expected a string in the form "alt://port[?option[=value][&option[=value]]]": ' + 'not starting with alt:// ({!r})'.format(parts.scheme)) + class_name = 'Serial' + try: + for option, values in urlparse.parse_qs(parts.query, True).items(): + if option == 'class': + class_name = values[0] + else: + raise ValueError('unknown option: {!r}'.format(option)) + except ValueError as e: + raise serial.SerialException( + 'expected a string in the form ' + '"alt://port[?option[=value][&option[=value]]]": {!r}'.format(e)) + if not hasattr(serial, class_name): + raise ValueError('unknown class: {!r}'.format(class_name)) + cls = getattr(serial, class_name) + if not issubclass(cls, serial.Serial): + raise ValueError('class {!r} is not an instance of Serial'.format(class_name)) + return (''.join([parts.netloc, parts.path]), cls) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +if __name__ == '__main__': + s = serial.serial_for_url('alt:///dev/ttyS0?class=PosixPollSerial') + print(s) diff --git a/backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_hwgrep.py b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_hwgrep.py new file mode 100644 index 0000000..49bbebe --- /dev/null +++ b/backend/venv/lib/python3.6/site-packages/serial/urlhandler/protocol_hwgrep.py @@ -0,0 +1,89 @@ +#! python +# +# This module implements a special URL handler that uses the port listing to +# find ports by searching the string descriptions. +# +# This file is part of pySerial. https://github.com/pyserial/pyserial +# (C) 2011-2015 Chris Liechti +# +# SPDX-License-Identifier: BSD-3-Clause +# +# URL format: hwgrep://&