generated from KOLANICH/python_project_boilerplate.py
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit cc2b985
Showing
102 changed files
with
6,846 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
https://github.com/KOLANICH-physics/motorAccelerationPlanner.py.git | ||
https://github.com/KOLANICH-libs/SaneIO.py.git | ||
https://github.com/pyserial/pyserial-asyncio.git | ||
https://github.com/kaitai-io/kaitai_struct_python_runtime.git |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
root = true | ||
|
||
[*] | ||
charset = utf-8 | ||
indent_style = tab | ||
indent_size = 4 | ||
insert_final_newline = true | ||
end_of_line = lf | ||
|
||
[*.{yml,yaml,yug,ksy}] | ||
indent_style = space | ||
indent_size = 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
KOLANICH/python_project_boilerplate.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
version: 2 | ||
updates: | ||
- package-ecosystem: "pip" | ||
directory: "/" | ||
schedule: | ||
interval: "daily" | ||
allow: | ||
- dependency-type: "all" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
name: CI | ||
on: | ||
push: | ||
branches: [master] | ||
pull_request: | ||
branches: [master] | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-22.04 | ||
steps: | ||
- name: typical python workflow | ||
uses: KOLANICH-GHActions/typical-python-workflow@master | ||
with: | ||
github_token: ${{ secrets.GITHUB_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
__pycache__ | ||
*.pyc | ||
*.pyo | ||
/*.egg-info | ||
*.srctrlbm | ||
*.srctrldb | ||
build | ||
dist | ||
.eggs | ||
monkeytype.sqlite3 | ||
/.ipynb_checkpoints | ||
|
||
.ninja_log | ||
.ninja_deps | ||
/tests/lan_proto_test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
image: registry.gitlab.com/kolanich-subgroups/docker-images/fixed_python:latest | ||
|
||
variables: | ||
DOCKER_DRIVER: overlay2 | ||
SAST_ANALYZER_IMAGE_TAG: latest | ||
SAST_DISABLE_DIND: "true" | ||
SAST_CONFIDENCE_LEVEL: 5 | ||
CODECLIMATE_VERSION: latest | ||
|
||
include: | ||
- template: SAST.gitlab-ci.yml | ||
- template: Code-Quality.gitlab-ci.yml | ||
- template: License-Management.gitlab-ci.yml | ||
|
||
build: | ||
tags: | ||
- shared | ||
- linux | ||
stage: build | ||
variables: | ||
GIT_DEPTH: "1" | ||
PYTHONUSERBASE: ${CI_PROJECT_DIR}/python_user_packages | ||
|
||
before_script: | ||
- export PATH="$PATH:$PYTHONUSERBASE/bin" # don't move into `variables` | ||
- apt-get update | ||
# todo: | ||
#- apt-get -y install | ||
#- pip3 install --upgrade | ||
#- python3 ./fix_python_modules_paths.py | ||
|
||
script: | ||
- python3 -m build -nw bdist_wheel | ||
- mv ./dist/*.whl ./dist/SMSD-0.CI-py3-none-any.whl | ||
- pip3 install --upgrade ./dist/*.whl | ||
- coverage run --source=SMSD -m --branch pytest --junitxml=./rspec.xml ./tests/test.py | ||
- coverage report -m | ||
- coverage xml | ||
|
||
coverage: "/^TOTAL(?:\\s+\\d+){4}\\s+(\\d+%).+/" | ||
|
||
cache: | ||
paths: | ||
- $PYTHONUSERBASE | ||
|
||
artifacts: | ||
paths: | ||
- dist | ||
reports: | ||
junit: ./rspec.xml | ||
cobertura: ./coverage.xml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
No codes of conduct! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
include UNLICENSE | ||
include *.md | ||
include tests | ||
include .editorconfig |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
SMSD.py [![Unlicensed work](https://raw.githubusercontent.com/unlicense/unlicense.org/master/static/favicon.png)](https://unlicense.org/) | ||
======= | ||
[wheel (GHA via `nightly.link`)](https://nightly.link/KOLANICH-libs/SMSD.py/workflows/CI/master/SMSD-0.CI-py3-none-any.whl) | ||
[![GitHub Actions](https://github.com/KOLANICH-libs/SMSD.py/workflows/CI/badge.svg)](https://github.com/KOLANICH-libs/SMSD.py/actions/) | ||
[![Libraries.io Status](https://img.shields.io/librariesio/github/KOLANICH-libs/SMSD.py.svg)](https://libraries.io/github/KOLANICH-libs/SMSD.py) | ||
[![Code style: antiflash](https://img.shields.io/badge/code%20style-antiflash-FFF.svg)](https://github.com/KOLANICH-tools/antiflash.py) | ||
|
||
## Disclaimers | ||
* the author of this library is not affiliated with `Smart Motor Devices OÜ` or `НПФ Электропривод`; | ||
* | ||
|
||
|
||
A library implementing protocols for Smart Motor Devices SMSD controlling drivers for stepper motors. | ||
|
||
* SMSD-4.2LAN and SMSD-8.0LAN - implemented a protocol and a server under `.lan` submodule. | ||
* SMSD-1.5K, SMSD-3.0, SMC-3 - implemented a client and classes for commands under `.text` submodule. Also implemented parsing of the file formats where programs are stored. | ||
|
||
## SMSD-*LAN | ||
### Server | ||
0. Import the class and instantiate it | ||
|
||
```python | ||
from SMSD.lan.protocol import Server | ||
s = Server() | ||
``` | ||
|
||
1. Create the servr using one of the following ways. | ||
1.a. You can create a server listening on a serial port. | ||
|
||
```python | ||
await s.startUARTServer("/dev/ttyACM0") | ||
``` | ||
|
||
The serial port on Linux can be a `pty` - a virtual one, but it must be created by some other app, like `socat` or [`PyVirtualSerialPorts`](https://github.com/ezramorris/PyVirtualSerialPorts). But see the cavheats! | ||
|
||
Cavheats: | ||
* Wine COM port passthrough: symlinking to `~/.wine/dosdevices/com<number>` simply doesn't work at all. | ||
* VirtualBox COM port passthrough: | ||
a. when a VM is loaded, the virtual device must exist. You may need to restart the server while keeping the VM running. It is possible through use of the virtual serial ports tools mentioned. | ||
b. VirtualBox has the following modes of COM ports passthrough: | ||
* `Host Device` - results in an error when connected to a `pty`. OK when connected to a real device. | ||
* `Host Pipe` | ||
* `pty` - error | ||
* pipe - the communication is unidirectional. | ||
* `Raw File` - the communication is unidirectional. | ||
* **`TCP`** - **Works**! Can be set up as `socat TCP-LISTEN:14379 PTY,link=./host,rawer`. Bonus - wireshark can be used to listen to the communications. | ||
|
||
ToDo: Find a way to create a non-pty virtual serial port on Linux. | ||
|
||
1.b. You can create a TCP server. Address of listening is determined by `s.networkConfig.ip`, by default it is `localhost`. | ||
```python | ||
await s.startTCPServer() | ||
``` | ||
|
||
1.c You can create a TCP server listening for UART-escaped and framed messages. It will allow you to connect VirtualBox to the tool directly without `socat`. The IP is still determined by `s.networkConfig.ip`, but the port is determined by the argument passed to the func. | ||
|
||
```python | ||
await s.startUARTTCPServer(port=port) | ||
``` | ||
|
||
## SMSD-1.5K, SMSD-3.0, SMC-3 | ||
|
||
In my case commands for using in-memory operational buffer and `Direct Control` mode have never worked. Neither with vendor-supplied software, nor with my one. | ||
|
||
**Don't use, unfinished and API is very unstable** | ||
|
||
P.S. I'm a very newbie to AsyncIO, so it is very probably that I use it incorrectly. | ||
|
||
### File formats used | ||
The original `SMC_Program` software stores the user-created programs within pairs of files `<name>.smc` and `<name>._smc`. All the files are plain text files, where the data is stored in lines using `\r\n` separators and [`cp1251` encoding](https://en.wikipedia.org/wiki/Windows-1251). The files are scrambled by applying rotation `(byte[i] + 0x7E) & 0xFF`, to descramble you need `(byte[i] - 0x7E) & 0xFF`. **Both files are required** in order to allow `SMC_Program` to load the program, while technically only one should be enough. | ||
* `<name>.smc` contains a sequence of commands | ||
* `<name>._smc` contains human-readable description of what a command does. |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
from pathlib import Path | ||
|
||
from plumbum import cli | ||
|
||
|
||
class CLI(cli.Application): | ||
"""Tools to work with SMSD controllers.""" | ||
|
||
|
||
@CLI.subcommand("emulator") | ||
class CLIEmulator(cli.Application): | ||
"""Starts an emulator.""" | ||
|
||
|
||
@CLIEmulator.subcommand("lan") | ||
class CLIEmulatorLan(cli.Application): | ||
"""Starts an emulator for LAN models""" | ||
|
||
|
||
@CLIEmulatorLan.subcommand("net") | ||
class CLIEmulatorLanNet(cli.Application): | ||
def main(self): | ||
import asyncio | ||
|
||
from SMSD.lan.protocol import Server | ||
|
||
s = Server() | ||
l = asyncio.get_event_loop() | ||
l.run_until_complete(s.startTCPServer()) | ||
l.run_until_complete(s.tcpServer.server.serve_forever()) | ||
|
||
|
||
CLIEmulatorLanNet.__doc__ = CLIEmulatorLan.__doc__ + " as a TCP server, to which you can connect using SMC-PROGRAM-LAN running on host." | ||
|
||
|
||
@CLIEmulatorLan.subcommand("net-uart") | ||
class CLIEmulatorLanNetUart(cli.Application): | ||
def main(self): | ||
import asyncio | ||
|
||
from SMSD.lan.protocol import Server | ||
|
||
s = Server() | ||
l = asyncio.get_event_loop() | ||
l.run_until_complete(s.startUARTTCPServer()) | ||
l.run_until_complete(s.uartTCPServer.server.serve_forever()) | ||
|
||
|
||
CLIEmulatorLanNetUart.__doc__ = CLIEmulatorLan.__doc__ + " as a TCP server, to which you can connect VirtualBox by setting up providing a virtual COM port in TCP mode and using using SMC-PROGRAM-LAN in the guest to connect that virtual COM port." | ||
|
||
|
||
@CLIEmulatorLan.subcommand("uart") | ||
class CLIEmulatorLanUart(cli.Application): | ||
def main(self, port: Path): | ||
import asyncio | ||
|
||
from SMSD.lan.protocol import Server | ||
|
||
s = Server() | ||
l = asyncio.get_event_loop() | ||
l.run_until_complete(s.uartServer(port)) | ||
l.run_until_complete(s.uartServer.server.serve_forever()) | ||
|
||
|
||
CLIEmulatorLanUart.__doc__ = CLIEmulatorLan.__doc__ + " as a PTY device. Disclaimer: won't work with VirtualBox and Wine as it is." | ||
|
||
if __name__ == "__main__": | ||
CLI.run() |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
from pkg_resources import parse_version | ||
import kaitaistruct | ||
from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO | ||
if parse_version(kaitaistruct.__version__) < parse_version('0.9'): | ||
raise Exception('Incompatible Kaitai Struct Python API: 0.9 or later is required, but you have %s' % kaitaistruct.__version__) | ||
|
||
class ChecksumSimpleAdditiveU1(KaitaiStruct): | ||
"""assert calcChecksum(0xFF, b"abcde") == 238 | ||
assert calcChecksum(0xFF, b"abcdef") == 84 | ||
assert calcChecksum(0xFF, b"abcdefgh") == 35 | ||
assert calcChecksum(0xFF, bytes(range(256))) == 127 | ||
assert calcChecksum(0, b"abcde") == 239 | ||
assert calcChecksum(0, b"abcdef") == 85 | ||
assert calcChecksum(0, b"abcdefgh") == 36 | ||
assert calcChecksum(0, bytes(range(256))) == 128 | ||
""" | ||
|
||
def __init__(self, initial, data, _io, _parent=None, _root=None): | ||
self._io = _io | ||
self._parent = _parent | ||
self._root = _root if _root else self | ||
self.initial = initial | ||
self.data = data | ||
|
||
def _read(self): | ||
pass | ||
|
||
class Iteration(KaitaiStruct): | ||
|
||
def __init__(self, idx, _io, _parent=None, _root=None): | ||
self._io = _io | ||
self._parent = _parent | ||
self._root = _root if _root else self | ||
self.idx = idx | ||
|
||
def _read(self): | ||
pass | ||
|
||
@property | ||
def prev(self): | ||
if hasattr(self, '_m_prev'): | ||
return self._m_prev if hasattr(self, '_m_prev') else None | ||
self._m_prev = self._root.initial if self.idx == 0 else self._parent.reduction[self.idx - 1].res | ||
return self._m_prev if hasattr(self, '_m_prev') else None | ||
|
||
@property | ||
def res(self): | ||
if hasattr(self, '_m_res'): | ||
return self._m_res if hasattr(self, '_m_res') else None | ||
self._m_res = self.prev + KaitaiStream.byte_array_index(self._parent.data, self.idx) | ||
return self._m_res if hasattr(self, '_m_res') else None | ||
|
||
@property | ||
def reduction(self): | ||
if hasattr(self, '_m_reduction'): | ||
return self._m_reduction if hasattr(self, '_m_reduction') else None | ||
_pos = self._io.pos() | ||
self._io.seek(0) | ||
self._raw__m_reduction = [None] * len(self.data) | ||
self._m_reduction = [None] * len(self.data) | ||
for i in range(len(self.data)): | ||
self._raw__m_reduction[i] = self._io.read_bytes(0) | ||
_io__raw__m_reduction = KaitaiStream(BytesIO(self._raw__m_reduction[i])) | ||
_t__m_reduction = ChecksumSimpleAdditiveU1.Iteration(i, _io__raw__m_reduction, self, self._root) | ||
_t__m_reduction._read() | ||
self._m_reduction[i] = _t__m_reduction | ||
self._io.seek(_pos) | ||
return self._m_reduction if hasattr(self, '_m_reduction') else None | ||
|
||
@property | ||
def value(self): | ||
if hasattr(self, '_m_value'): | ||
return self._m_value if hasattr(self, '_m_value') else None | ||
self._m_value = self.reduction[len(self.data) - 1].res & 255 | ||
return self._m_value if hasattr(self, '_m_value') else None |
Oops, something went wrong.