base-loom-server 0.2__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {base_loom_server-0.2 → base_loom_server-0.3.0}/.gitignore +2 -1
- {base_loom_server-0.2 → base_loom_server-0.3.0}/.pre-commit-config.yaml +4 -4
- base_loom_server-0.3.0/PKG-INFO +126 -0
- base_loom_server-0.3.0/README.md +104 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/pyproject.toml +10 -19
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/app_runner.py +44 -3
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/base_loom_server.py +14 -10
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/client_replies.py +2 -4
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/display.html_template +1 -1
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/display.js +7 -7
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/example_loom_server.py +1 -7
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/example_mock_loom.py +0 -2
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/pattern_database.py +68 -21
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/reduced_pattern.py +44 -40
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/testutils.py +19 -17
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/version.py +1 -1
- base_loom_server-0.3.0/src/base_loom_server.egg-info/PKG-INFO +126 -0
- base_loom_server-0.3.0/src/base_loom_server.egg-info/requires.txt +9 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/tests/test_pattern_database.py +28 -13
- {base_loom_server-0.2 → base_loom_server-0.3.0}/tests/test_reduced_pattern.py +7 -5
- base_loom_server-0.2/PKG-INFO +0 -79
- base_loom_server-0.2/README.md +0 -58
- base_loom_server-0.2/src/base_loom_server.egg-info/PKG-INFO +0 -79
- base_loom_server-0.2/src/base_loom_server.egg-info/requires.txt +0 -8
- {base_loom_server-0.2 → base_loom_server-0.3.0}/LICENSE.txt +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/setup.cfg +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/__init__.py +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/base_mock_loom.py +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/constants.py +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/display.css +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/favicon-32x32.png +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/locales/README.md +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/locales/default.json +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/locales/fr.json +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/main.py +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/mock_streams.py +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/py.typed +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/test_data/many color liftplan and zeros.dtx +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/test_data/many color liftplan and zeros.wif +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/test_data/many color multiple treadles and zeros.dtx +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/test_data/many color multiple treadles and zeros.wif +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/test_data/many color single treadles.dtx +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/test_data/many color single treadles.wif +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/test_data/two color liftplan.dtx +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/test_data/two color liftplan.wif +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/test_data/two color multiple treadles.dtx +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/test_data/two color multiple treadles.wif +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/test_data/two color single treadles.dtx +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server/test_data/two color single treadles.wif +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server.egg-info/SOURCES.txt +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server.egg-info/dependency_links.txt +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server.egg-info/entry_points.txt +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/src/base_loom_server.egg-info/top_level.txt +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/tests/test_loom_server.py +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/tests/test_mock_loom.py +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/tests/test_mock_streams.py +0 -0
- {base_loom_server-0.2 → base_loom_server-0.3.0}/tests/test_version.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
repos:
|
|
2
2
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
-
rev:
|
|
3
|
+
rev: v5.0.0
|
|
4
4
|
hooks:
|
|
5
5
|
- id: check-yaml
|
|
6
6
|
exclude: conda/meta.yaml
|
|
7
7
|
- id: check-xml
|
|
8
8
|
|
|
9
9
|
- repo: https://github.com/psf/black
|
|
10
|
-
rev:
|
|
10
|
+
rev: 25.1.0
|
|
11
11
|
hooks:
|
|
12
12
|
- id: black
|
|
13
13
|
|
|
@@ -17,12 +17,12 @@ repos:
|
|
|
17
17
|
- id: flake8
|
|
18
18
|
|
|
19
19
|
- repo: https://github.com/pycqa/isort
|
|
20
|
-
rev:
|
|
20
|
+
rev: 6.0.0
|
|
21
21
|
hooks:
|
|
22
22
|
- id: isort
|
|
23
23
|
name: isort (python)
|
|
24
24
|
|
|
25
25
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
26
|
-
rev: v1.
|
|
26
|
+
rev: v1.15.0
|
|
27
27
|
hooks:
|
|
28
28
|
- id: mypy
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: base_loom_server
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Base package for web servers that control dobby multi-shaft looms
|
|
5
|
+
Author-email: Russell Owen <r3owen@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/r-owen/base_loom_server
|
|
7
|
+
Project-URL: Issues, https://github.com/r-owen/base_loom_server/issues
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE.txt
|
|
14
|
+
Requires-Dist: aiosqlite>=0.20
|
|
15
|
+
Requires-Dist: dtx_to_wif~=3.0
|
|
16
|
+
Requires-Dist: fastapi[standard]~=0.115
|
|
17
|
+
Requires-Dist: pyserial-asyncio~=0.6
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: pre-commit~=4.0; extra == "dev"
|
|
20
|
+
Requires-Dist: pytest~=8.3; extra == "dev"
|
|
21
|
+
Requires-Dist: pytest-asyncio~=0.25; extra == "dev"
|
|
22
|
+
|
|
23
|
+
# Base package for web servers that control dobby multi-shaft looms
|
|
24
|
+
|
|
25
|
+
Such web servers are intended to allow you to control your loom from any phone, tablet or other device that has wifi and a web browser.
|
|
26
|
+
|
|
27
|
+
Used by [seguin_loom_server](<https://pypi.org/project/seguin-loom-server/)>)
|
|
28
|
+
and [toika_loom_server](https://pypi.org/project/toika-loom-server/).
|
|
29
|
+
|
|
30
|
+
## Installing this Package
|
|
31
|
+
|
|
32
|
+
You should only have to install this package if you want to examine or modify it.
|
|
33
|
+
If you are installing a loom driver such as
|
|
34
|
+
such as [seguin_loom_server](<https://pypi.org/project/seguin-loom-server/)>)
|
|
35
|
+
or [toika_loom_server](https://pypi.org/project/toika-loom-server/),
|
|
36
|
+
then you only need to install that package.
|
|
37
|
+
Doing so will pull in all dependencies.
|
|
38
|
+
|
|
39
|
+
Assuming you still want your own installation of base_loom_server:
|
|
40
|
+
|
|
41
|
+
* Install [Python](https://www.python.org/downloads/) 3.11 or later on the computer.
|
|
42
|
+
|
|
43
|
+
* If you are using python for other things, you may wish to make a virtual environment
|
|
44
|
+
dedicated to your loom server software. Look up "python virtual environment" on line.
|
|
45
|
+
|
|
46
|
+
* Install this [base_loom_server](https://pypi.org/project/base-loom-server/) package on the computer with command:
|
|
47
|
+
|
|
48
|
+
* On Raspberry Pi: **sudo pip install base_loom_server**
|
|
49
|
+
* On most other computers: **pip install base_loom_server**
|
|
50
|
+
|
|
51
|
+
If you have a Raspberry Pi then you have three choices for installation:
|
|
52
|
+
|
|
53
|
+
* Use sudo for all pip install commands. This is simplest.
|
|
54
|
+
* Add "~/.local/bin" to your PATH. You can read about that on line.
|
|
55
|
+
* Prefix the run command with ".local/bin", e.g. **.local/bin/run_example_loom**.
|
|
56
|
+
|
|
57
|
+
## Using this Package to Write a Loom Server
|
|
58
|
+
|
|
59
|
+
* Write a subclass of `BaseMockLoom` that emulates your loom.
|
|
60
|
+
Two examples are `ExampleMockLoom` in this package and `MockLoom`
|
|
61
|
+
in [toika_loom_server](https://pypi.org/project/toika-loom-server/).
|
|
62
|
+
|
|
63
|
+
* Write a subclass of `BaseLoomServer` that talks to the loom.
|
|
64
|
+
Two examples are `ExampleLoomServer` in this package and `LoomServer`
|
|
65
|
+
in [toika_loom_server](https://pypi.org/project/toika-loom-server/).
|
|
66
|
+
|
|
67
|
+
* Write a `main.py` like the one in this package, to run your loom server.
|
|
68
|
+
|
|
69
|
+
* Copy `tests/test_mock_loom.py` and modify it to suit your mock loom.
|
|
70
|
+
|
|
71
|
+
* The unit tests for your loom server should be able to use `testutils.BaseTestLoomServer`, as `tests/test_loom_server.py` does.
|
|
72
|
+
|
|
73
|
+
* Write a `pyproject.toml` like the one for [toika_loom_server](https://pypi.org/project/toika-loom-server/).
|
|
74
|
+
|
|
75
|
+
## Remembering Patterns
|
|
76
|
+
|
|
77
|
+
The web server keeps track of the most recent 25 patterns you have used in a database
|
|
78
|
+
(including the most recent pick number and number of repeats, which are restored when you select a pattern).
|
|
79
|
+
The patterns in the database are displayed in the pattern menu.
|
|
80
|
+
If you shut down the server or there is a power failure, all this information should be retained.
|
|
81
|
+
|
|
82
|
+
You can reset the database by starting the server with the **--reset-db** argument.
|
|
83
|
+
You must reset the database if you upgrade this base_loom_server package and the new database format is incompatible
|
|
84
|
+
(in which case the server will fail at startup).
|
|
85
|
+
You may also want to reset the database if you are weaving a new project and don't want to see any of the saved patterns.
|
|
86
|
+
|
|
87
|
+
## Developer Tips
|
|
88
|
+
|
|
89
|
+
* Download the source code from [github](https://github.com/r-owen/base_loom_server.git),
|
|
90
|
+
or make a fork and git clone that.
|
|
91
|
+
|
|
92
|
+
* Inside the directory, issue the following commands:
|
|
93
|
+
|
|
94
|
+
* **pip install -e .'[dev]'** (the single quotes are required in zsh, but not in bash)
|
|
95
|
+
to make an "editable installation" of the package.
|
|
96
|
+
An editable installation runs from the source code,
|
|
97
|
+
so changes you make to the source are used when you run or test the code,
|
|
98
|
+
without the need to reinstall the package.
|
|
99
|
+
**'[dev]'** installs development-related packages such as pytest
|
|
100
|
+
(see the file `pyproject.toml` for the full list).
|
|
101
|
+
|
|
102
|
+
* **pre-commit install** to activate the pre-commit hooks.
|
|
103
|
+
|
|
104
|
+
* **pytest** to test your installation.
|
|
105
|
+
|
|
106
|
+
* You may run an example loom server with: **run_example_loom mock**.
|
|
107
|
+
Please only specify the **mock** serial port; connecting it to a real loom will not work
|
|
108
|
+
(`ExampleMockLoom` is loosely based on a Séguin loom, but is not compatible).
|
|
109
|
+
|
|
110
|
+
**run_example_loom mock** also accepts these command-line arguments:
|
|
111
|
+
|
|
112
|
+
* **--reset-db** Reset the pattern database. Try this if you think the database is corrupted.
|
|
113
|
+
|
|
114
|
+
* **--verbose** Print more diagnostic information.
|
|
115
|
+
|
|
116
|
+
Note that the example loom server uses the same pattern database as
|
|
117
|
+
[seguin_loom_server](<https://pypi.org/project/seguin-loom-server/)>)
|
|
118
|
+
and [toika_loom_server](https://pypi.org/project/toika-loom-server/).
|
|
119
|
+
|
|
120
|
+
* To run mypy run: ***MYPYPATH=src mypy .***. This avoids complaints about module name ambiguity.
|
|
121
|
+
|
|
122
|
+
* In mock mode the web page shows a few extra controls for debugging.
|
|
123
|
+
|
|
124
|
+
* Warning: the web server's automatic reload feature, which reloads Python code whenever you save changes, *does not work* with this software.
|
|
125
|
+
Instead you have to kill the web server by typing control-C several times, until you get a terminal prompt, then run the server again.
|
|
126
|
+
This may be a bug in uvicorn; see [this discussion](https://github.com/encode/uvicorn/discussions/2075) for more information.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Base package for web servers that control dobby multi-shaft looms
|
|
2
|
+
|
|
3
|
+
Such web servers are intended to allow you to control your loom from any phone, tablet or other device that has wifi and a web browser.
|
|
4
|
+
|
|
5
|
+
Used by [seguin_loom_server](<https://pypi.org/project/seguin-loom-server/)>)
|
|
6
|
+
and [toika_loom_server](https://pypi.org/project/toika-loom-server/).
|
|
7
|
+
|
|
8
|
+
## Installing this Package
|
|
9
|
+
|
|
10
|
+
You should only have to install this package if you want to examine or modify it.
|
|
11
|
+
If you are installing a loom driver such as
|
|
12
|
+
such as [seguin_loom_server](<https://pypi.org/project/seguin-loom-server/)>)
|
|
13
|
+
or [toika_loom_server](https://pypi.org/project/toika-loom-server/),
|
|
14
|
+
then you only need to install that package.
|
|
15
|
+
Doing so will pull in all dependencies.
|
|
16
|
+
|
|
17
|
+
Assuming you still want your own installation of base_loom_server:
|
|
18
|
+
|
|
19
|
+
* Install [Python](https://www.python.org/downloads/) 3.11 or later on the computer.
|
|
20
|
+
|
|
21
|
+
* If you are using python for other things, you may wish to make a virtual environment
|
|
22
|
+
dedicated to your loom server software. Look up "python virtual environment" on line.
|
|
23
|
+
|
|
24
|
+
* Install this [base_loom_server](https://pypi.org/project/base-loom-server/) package on the computer with command:
|
|
25
|
+
|
|
26
|
+
* On Raspberry Pi: **sudo pip install base_loom_server**
|
|
27
|
+
* On most other computers: **pip install base_loom_server**
|
|
28
|
+
|
|
29
|
+
If you have a Raspberry Pi then you have three choices for installation:
|
|
30
|
+
|
|
31
|
+
* Use sudo for all pip install commands. This is simplest.
|
|
32
|
+
* Add "~/.local/bin" to your PATH. You can read about that on line.
|
|
33
|
+
* Prefix the run command with ".local/bin", e.g. **.local/bin/run_example_loom**.
|
|
34
|
+
|
|
35
|
+
## Using this Package to Write a Loom Server
|
|
36
|
+
|
|
37
|
+
* Write a subclass of `BaseMockLoom` that emulates your loom.
|
|
38
|
+
Two examples are `ExampleMockLoom` in this package and `MockLoom`
|
|
39
|
+
in [toika_loom_server](https://pypi.org/project/toika-loom-server/).
|
|
40
|
+
|
|
41
|
+
* Write a subclass of `BaseLoomServer` that talks to the loom.
|
|
42
|
+
Two examples are `ExampleLoomServer` in this package and `LoomServer`
|
|
43
|
+
in [toika_loom_server](https://pypi.org/project/toika-loom-server/).
|
|
44
|
+
|
|
45
|
+
* Write a `main.py` like the one in this package, to run your loom server.
|
|
46
|
+
|
|
47
|
+
* Copy `tests/test_mock_loom.py` and modify it to suit your mock loom.
|
|
48
|
+
|
|
49
|
+
* The unit tests for your loom server should be able to use `testutils.BaseTestLoomServer`, as `tests/test_loom_server.py` does.
|
|
50
|
+
|
|
51
|
+
* Write a `pyproject.toml` like the one for [toika_loom_server](https://pypi.org/project/toika-loom-server/).
|
|
52
|
+
|
|
53
|
+
## Remembering Patterns
|
|
54
|
+
|
|
55
|
+
The web server keeps track of the most recent 25 patterns you have used in a database
|
|
56
|
+
(including the most recent pick number and number of repeats, which are restored when you select a pattern).
|
|
57
|
+
The patterns in the database are displayed in the pattern menu.
|
|
58
|
+
If you shut down the server or there is a power failure, all this information should be retained.
|
|
59
|
+
|
|
60
|
+
You can reset the database by starting the server with the **--reset-db** argument.
|
|
61
|
+
You must reset the database if you upgrade this base_loom_server package and the new database format is incompatible
|
|
62
|
+
(in which case the server will fail at startup).
|
|
63
|
+
You may also want to reset the database if you are weaving a new project and don't want to see any of the saved patterns.
|
|
64
|
+
|
|
65
|
+
## Developer Tips
|
|
66
|
+
|
|
67
|
+
* Download the source code from [github](https://github.com/r-owen/base_loom_server.git),
|
|
68
|
+
or make a fork and git clone that.
|
|
69
|
+
|
|
70
|
+
* Inside the directory, issue the following commands:
|
|
71
|
+
|
|
72
|
+
* **pip install -e .'[dev]'** (the single quotes are required in zsh, but not in bash)
|
|
73
|
+
to make an "editable installation" of the package.
|
|
74
|
+
An editable installation runs from the source code,
|
|
75
|
+
so changes you make to the source are used when you run or test the code,
|
|
76
|
+
without the need to reinstall the package.
|
|
77
|
+
**'[dev]'** installs development-related packages such as pytest
|
|
78
|
+
(see the file `pyproject.toml` for the full list).
|
|
79
|
+
|
|
80
|
+
* **pre-commit install** to activate the pre-commit hooks.
|
|
81
|
+
|
|
82
|
+
* **pytest** to test your installation.
|
|
83
|
+
|
|
84
|
+
* You may run an example loom server with: **run_example_loom mock**.
|
|
85
|
+
Please only specify the **mock** serial port; connecting it to a real loom will not work
|
|
86
|
+
(`ExampleMockLoom` is loosely based on a Séguin loom, but is not compatible).
|
|
87
|
+
|
|
88
|
+
**run_example_loom mock** also accepts these command-line arguments:
|
|
89
|
+
|
|
90
|
+
* **--reset-db** Reset the pattern database. Try this if you think the database is corrupted.
|
|
91
|
+
|
|
92
|
+
* **--verbose** Print more diagnostic information.
|
|
93
|
+
|
|
94
|
+
Note that the example loom server uses the same pattern database as
|
|
95
|
+
[seguin_loom_server](<https://pypi.org/project/seguin-loom-server/)>)
|
|
96
|
+
and [toika_loom_server](https://pypi.org/project/toika-loom-server/).
|
|
97
|
+
|
|
98
|
+
* To run mypy run: ***MYPYPATH=src mypy .***. This avoids complaints about module name ambiguity.
|
|
99
|
+
|
|
100
|
+
* In mock mode the web page shows a few extra controls for debugging.
|
|
101
|
+
|
|
102
|
+
* Warning: the web server's automatic reload feature, which reloads Python code whenever you save changes, *does not work* with this software.
|
|
103
|
+
Instead you have to kill the web server by typing control-C several times, until you get a terminal prompt, then run the server again.
|
|
104
|
+
This may be a bug in uvicorn; see [this discussion](https://github.com/encode/uvicorn/discussions/2075) for more information.
|
|
@@ -11,9 +11,10 @@ dynamic = ["version"]
|
|
|
11
11
|
description = "Base package for web servers that control dobby multi-shaft looms"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
dependencies = [
|
|
14
|
-
"
|
|
15
|
-
"dtx_to_wif
|
|
16
|
-
"
|
|
14
|
+
"aiosqlite >= 0.20",
|
|
15
|
+
"dtx_to_wif ~= 3.0",
|
|
16
|
+
"fastapi[standard] ~= 0.115",
|
|
17
|
+
"pyserial-asyncio ~= 0.6",
|
|
17
18
|
]
|
|
18
19
|
authors = [
|
|
19
20
|
{ name="Russell Owen", email="r3owen@gmail.com" },
|
|
@@ -27,22 +28,9 @@ requires-python = ">=3.11"
|
|
|
27
28
|
|
|
28
29
|
[project.optional-dependencies]
|
|
29
30
|
dev = [
|
|
30
|
-
"pre-commit
|
|
31
|
-
"pytest
|
|
32
|
-
"pytest-asyncio
|
|
33
|
-
]
|
|
34
|
-
|
|
35
|
-
# Needed due to including package data below
|
|
36
|
-
[tool.setuptools.packages.find]
|
|
37
|
-
where = ["src"]
|
|
38
|
-
|
|
39
|
-
[tool.setuptools.package-data]
|
|
40
|
-
"base_loom_server" = [
|
|
41
|
-
"py.typed",
|
|
42
|
-
"display.css",
|
|
43
|
-
"display.html_template",
|
|
44
|
-
"display.js",
|
|
45
|
-
"favicon-32x32.png"
|
|
31
|
+
"pre-commit ~= 4.0",
|
|
32
|
+
"pytest ~= 8.3",
|
|
33
|
+
"pytest-asyncio ~= 0.25",
|
|
46
34
|
]
|
|
47
35
|
|
|
48
36
|
[project.scripts]
|
|
@@ -65,3 +53,6 @@ write_to_template = """
|
|
|
65
53
|
__all__ = ["__version__"]
|
|
66
54
|
__version__ = "{version}"
|
|
67
55
|
"""
|
|
56
|
+
|
|
57
|
+
[tool.mypy]
|
|
58
|
+
explicit_package_bases = "True"
|
|
@@ -19,6 +19,30 @@ LOCALE_FILES = PKG_FILES.joinpath("locales")
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class AppRunner:
|
|
22
|
+
"""Run the loom server application.
|
|
23
|
+
|
|
24
|
+
This contains the web server's endpoints,
|
|
25
|
+
the lifespan context manager,
|
|
26
|
+
and a method to create the argument parser.
|
|
27
|
+
|
|
28
|
+
In order to use this you *must* create an instance on import
|
|
29
|
+
(i.e. at the module level), typically in `main.py'.
|
|
30
|
+
If you defer creation, the web server will not see the endpoints!
|
|
31
|
+
See ``main.py`` for an example.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
app : FastAPI
|
|
36
|
+
The application, generated with ``app = FastAPI()``
|
|
37
|
+
server_class : Type[BaseLoomServer]
|
|
38
|
+
Your loom server class (NOT an instance, but the class itself).
|
|
39
|
+
favicon : bytes
|
|
40
|
+
A 32x32 or so favicon. None if empty.
|
|
41
|
+
app_package_name : the name of the python package for your loom server,
|
|
42
|
+
e.g. "toika_loom_server". This is used by the `run` method,
|
|
43
|
+
as an argument to `uvicorn.run`.
|
|
44
|
+
"""
|
|
45
|
+
|
|
22
46
|
def __init__(
|
|
23
47
|
self,
|
|
24
48
|
app: FastAPI,
|
|
@@ -52,9 +76,11 @@ class AppRunner:
|
|
|
52
76
|
async def get_wrapper():
|
|
53
77
|
return await self.get()
|
|
54
78
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
79
|
+
if self.favicon:
|
|
80
|
+
|
|
81
|
+
@router.get("/favicon.ico", include_in_schema=False)
|
|
82
|
+
async def get_favicon():
|
|
83
|
+
return await self.get_favicon()
|
|
58
84
|
|
|
59
85
|
@router.websocket("/ws")
|
|
60
86
|
async def websocket_endpoint_wrapper(websocket: WebSocket):
|
|
@@ -63,6 +89,10 @@ class AppRunner:
|
|
|
63
89
|
app.include_router(router)
|
|
64
90
|
|
|
65
91
|
def create_argument_parser(self) -> argparse.ArgumentParser:
|
|
92
|
+
"""Create the argument parser.
|
|
93
|
+
|
|
94
|
+
Subclasses may override this to add more options.
|
|
95
|
+
"""
|
|
66
96
|
parser = argparse.ArgumentParser()
|
|
67
97
|
parser.add_argument(
|
|
68
98
|
"serial_port",
|
|
@@ -98,6 +128,13 @@ class AppRunner:
|
|
|
98
128
|
|
|
99
129
|
@asynccontextmanager
|
|
100
130
|
async def lifespan(self, app: FastAPI) -> AsyncGenerator[None, FastAPI]:
|
|
131
|
+
"""Lifespan context manager for fastAPI.
|
|
132
|
+
|
|
133
|
+
Load the translation dict and create the sole instance of
|
|
134
|
+
the loom server class. That loom server instance persists
|
|
135
|
+
for the entire time the web server is running. This is because
|
|
136
|
+
the loom server speaks to one loom and serves at most one user.
|
|
137
|
+
"""
|
|
101
138
|
self.translation_dict = self.get_translation_dict()
|
|
102
139
|
|
|
103
140
|
parser = self.create_argument_parser()
|
|
@@ -138,6 +175,7 @@ class AppRunner:
|
|
|
138
175
|
return translation_dict
|
|
139
176
|
|
|
140
177
|
async def get(self) -> HTMLResponse:
|
|
178
|
+
"""Endpoint to get the main page."""
|
|
141
179
|
display_html_template = PKG_FILES.joinpath("display.html_template").read_text()
|
|
142
180
|
|
|
143
181
|
display_css = PKG_FILES.joinpath("display.css").read_text()
|
|
@@ -164,13 +202,16 @@ class AppRunner:
|
|
|
164
202
|
return HTMLResponse(display_html)
|
|
165
203
|
|
|
166
204
|
async def get_favicon(self) -> Response:
|
|
205
|
+
"""Endpoint to get the favicon"""
|
|
167
206
|
return Response(content=self.favicon, media_type="image/x-icon")
|
|
168
207
|
|
|
169
208
|
async def websocket_endpoint(self, websocket: WebSocket) -> None:
|
|
209
|
+
"""Websocket endpoint."""
|
|
170
210
|
assert self.loom_server is not None
|
|
171
211
|
await self.loom_server.run_client(websocket=websocket)
|
|
172
212
|
|
|
173
213
|
def run(self, host="0.0.0.0", port=8000, log_level="info", reload=True) -> None:
|
|
214
|
+
"""Parse command-line arguments and run the web server."""
|
|
174
215
|
# Handle the help argument and also catch parsing errors right away
|
|
175
216
|
arg_parser = self.create_argument_parser()
|
|
176
217
|
arg_parser.parse_args()
|
|
@@ -119,7 +119,7 @@ class BaseLoomServer:
|
|
|
119
119
|
self.done_task: asyncio.Future = asyncio.Future()
|
|
120
120
|
self.current_pattern: ReducedPattern | None = None
|
|
121
121
|
self.jump_pick = client_replies.JumpPickNumber(
|
|
122
|
-
pick_number=None,
|
|
122
|
+
pick_number=None, weaving_repeat_number=None
|
|
123
123
|
)
|
|
124
124
|
self.weave_forward = True
|
|
125
125
|
self.loom_error_flag = False
|
|
@@ -309,7 +309,7 @@ class BaseLoomServer:
|
|
|
309
309
|
If True then report JumpPickNumber, even if it has not changed.
|
|
310
310
|
"""
|
|
311
311
|
null_jump_pick = client_replies.JumpPickNumber(
|
|
312
|
-
pick_number=None,
|
|
312
|
+
pick_number=None, weaving_repeat_number=None
|
|
313
313
|
)
|
|
314
314
|
do_report = force_output or self.jump_pick != null_jump_pick
|
|
315
315
|
self.jump_pick = null_jump_pick
|
|
@@ -338,10 +338,12 @@ class BaseLoomServer:
|
|
|
338
338
|
def increment_pick_number(self) -> int:
|
|
339
339
|
"""Increment pick_number in the specified direction.
|
|
340
340
|
|
|
341
|
-
Increment
|
|
341
|
+
Increment weaving_repeat_number as well, if appropriate.
|
|
342
342
|
|
|
343
|
-
Return the new pick number. This will be 0 if
|
|
344
|
-
|
|
343
|
+
Return the new pick number. This will be 0 if
|
|
344
|
+
weaving_repeat_number changed,
|
|
345
|
+
or if unweaving and weaving_repeat_number
|
|
346
|
+
would be decremented to 0.
|
|
345
347
|
"""
|
|
346
348
|
if self.current_pattern is None:
|
|
347
349
|
return 0
|
|
@@ -405,7 +407,7 @@ class BaseLoomServer:
|
|
|
405
407
|
|
|
406
408
|
self.jump_pick = client_replies.JumpPickNumber(
|
|
407
409
|
pick_number=command.pick_number,
|
|
408
|
-
|
|
410
|
+
weaving_repeat_number=command.weaving_repeat_number,
|
|
409
411
|
)
|
|
410
412
|
await self.report_jump_pick_number()
|
|
411
413
|
|
|
@@ -530,8 +532,10 @@ class BaseLoomServer:
|
|
|
530
532
|
self.current_pattern.set_current_pick_number(new_pick_number)
|
|
531
533
|
else:
|
|
532
534
|
new_pick_number = self.increment_pick_number()
|
|
533
|
-
if self.jump_pick.
|
|
534
|
-
self.current_pattern.
|
|
535
|
+
if self.jump_pick.weaving_repeat_number is not None:
|
|
536
|
+
self.current_pattern.weaving_repeat_number = (
|
|
537
|
+
self.jump_pick.weaving_repeat_number
|
|
538
|
+
)
|
|
535
539
|
pick = self.current_pattern.get_current_pick()
|
|
536
540
|
await self.write_shafts_to_loom(pick)
|
|
537
541
|
await self.clear_jump_pick()
|
|
@@ -624,11 +628,11 @@ class BaseLoomServer:
|
|
|
624
628
|
await self.pattern_db.update_pick_number(
|
|
625
629
|
pattern_name=self.current_pattern.name,
|
|
626
630
|
pick_number=self.current_pattern.pick_number,
|
|
627
|
-
|
|
631
|
+
weaving_repeat_number=self.current_pattern.weaving_repeat_number,
|
|
628
632
|
)
|
|
629
633
|
reply = client_replies.CurrentPickNumber(
|
|
630
634
|
pick_number=self.current_pattern.pick_number,
|
|
631
|
-
|
|
635
|
+
weaving_repeat_number=self.current_pattern.weaving_repeat_number,
|
|
632
636
|
)
|
|
633
637
|
await self.write_to_client(reply)
|
|
634
638
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
import dataclasses
|
|
4
2
|
import enum
|
|
5
3
|
|
|
@@ -45,7 +43,7 @@ class CurrentPickNumber:
|
|
|
45
43
|
|
|
46
44
|
type: str = dataclasses.field(init=False, default="CurrentPickNumber")
|
|
47
45
|
pick_number: int
|
|
48
|
-
|
|
46
|
+
weaving_repeat_number: int
|
|
49
47
|
|
|
50
48
|
|
|
51
49
|
@dataclasses.dataclass
|
|
@@ -54,7 +52,7 @@ class JumpPickNumber:
|
|
|
54
52
|
|
|
55
53
|
type: str = dataclasses.field(init=False, default="JumpPickNumber")
|
|
56
54
|
pick_number: int | None
|
|
57
|
-
|
|
55
|
+
weaving_repeat_number: int | None
|
|
58
56
|
|
|
59
57
|
|
|
60
58
|
@dataclasses.dataclass
|
|
@@ -66,7 +66,7 @@ class ReducedPattern {
|
|
|
66
66
|
this.threading = datadict.threading
|
|
67
67
|
this.picks = []
|
|
68
68
|
this.pick_number = datadict.pick_number
|
|
69
|
-
this.
|
|
69
|
+
this.weaving_repeat_number = datadict.weaving_repeat_number
|
|
70
70
|
datadict.picks.forEach((pickdata) => {
|
|
71
71
|
this.picks.push(new Pick(pickdata))
|
|
72
72
|
})
|
|
@@ -209,12 +209,12 @@ class LoomClient {
|
|
|
209
209
|
console.log("Ignoring CurrentPickNumber: no pattern loaded")
|
|
210
210
|
}
|
|
211
211
|
this.currentPattern.pick_number = datadict.pick_number
|
|
212
|
-
this.currentPattern.
|
|
212
|
+
this.currentPattern.weaving_repeat_number = datadict.weaving_repeat_number
|
|
213
213
|
this.displayCurrentPattern()
|
|
214
214
|
this.displayPick()
|
|
215
215
|
} else if (datadict.type == "JumpPickNumber") {
|
|
216
216
|
this.jumpPickNumber = datadict.pick_number
|
|
217
|
-
this.jumpRepeatNumber = datadict.
|
|
217
|
+
this.jumpRepeatNumber = datadict.weaving_repeat_number
|
|
218
218
|
this.displayJumpPick()
|
|
219
219
|
} else if (datadict.type == "LoomConnectionState") {
|
|
220
220
|
this.loomConnectionState = datadict
|
|
@@ -505,7 +505,7 @@ class LoomClient {
|
|
|
505
505
|
Display the current pick and repeat.
|
|
506
506
|
*/
|
|
507
507
|
displayPick() {
|
|
508
|
-
var repeatNumberElt = document.getElementById("
|
|
508
|
+
var repeatNumberElt = document.getElementById("weaving_repeat_number")
|
|
509
509
|
var pickNumberElt = document.getElementById("pick_number")
|
|
510
510
|
var totalPicksElt = document.getElementById("total_picks")
|
|
511
511
|
var pickNumber = ""
|
|
@@ -513,7 +513,7 @@ class LoomClient {
|
|
|
513
513
|
var repeatNumber = ""
|
|
514
514
|
if (this.currentPattern) {
|
|
515
515
|
pickNumber = this.currentPattern.pick_number
|
|
516
|
-
repeatNumber = this.currentPattern.
|
|
516
|
+
repeatNumber = this.currentPattern.weaving_repeat_number
|
|
517
517
|
totalPicks = this.currentPattern.picks.length
|
|
518
518
|
}
|
|
519
519
|
pickNumberElt.textContent = pickNumber
|
|
@@ -644,7 +644,7 @@ class LoomClient {
|
|
|
644
644
|
// Handle blanks by using the current default, if any
|
|
645
645
|
var pickNumber = asNumberOrNull(jumpPickNumberElt.value)
|
|
646
646
|
var repeatNumber = asNumberOrNull(jumpRepeatNumberElt.value)
|
|
647
|
-
var command = { "type": "jump_to_pick", "pick_number": pickNumber, "
|
|
647
|
+
var command = { "type": "jump_to_pick", "pick_number": pickNumber, "weaving_repeat_number": repeatNumber }
|
|
648
648
|
await this.sendCommand(command)
|
|
649
649
|
event.preventDefault()
|
|
650
650
|
}
|
|
@@ -659,7 +659,7 @@ class LoomClient {
|
|
|
659
659
|
var jumpRepeatNumberElt = document.getElementById("jump_repeat_number")
|
|
660
660
|
jumpPickNumberElt.value = ""
|
|
661
661
|
jumpRepeatNumberElt.value = ""
|
|
662
|
-
var command = { "type": "jump_to_pick", "pick_number": null, "
|
|
662
|
+
var command = { "type": "jump_to_pick", "pick_number": null, "weaving_repeat_number": null }
|
|
663
663
|
await this.sendCommand(command)
|
|
664
664
|
event.preventDefault()
|
|
665
665
|
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
__all__ = ["BaseLoomServer"]
|
|
1
|
+
__all__ = ["ExampleLoomServer"]
|
|
4
2
|
|
|
5
3
|
import pathlib
|
|
6
4
|
|
|
@@ -10,10 +8,6 @@ from .example_mock_loom import ExampleMockLoom
|
|
|
10
8
|
from .reduced_pattern import Pick
|
|
11
9
|
|
|
12
10
|
|
|
13
|
-
class CommandError(Exception):
|
|
14
|
-
pass
|
|
15
|
-
|
|
16
|
-
|
|
17
11
|
class ExampleLoomServer(BaseLoomServer):
|
|
18
12
|
"""Example loom server.
|
|
19
13
|
|