arcsecond 3.11.0__tar.gz → 3.13.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.
- {arcsecond-3.11.0 → arcsecond-3.13.0}/.github/workflows/pythonpublish.yml +5 -7
- {arcsecond-3.11.0 → arcsecond-3.13.0}/.github/workflows/tests.yml +15 -23
- {arcsecond-3.11.0 → arcsecond-3.13.0}/PKG-INFO +11 -11
- {arcsecond-3.11.0 → arcsecond-3.13.0}/README.md +10 -11
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/__init__.py +0 -4
- arcsecond-3.13.0/arcsecond/alpaca/commands.py +180 -0
- arcsecond-3.13.0/arcsecond/alpaca/dome_probe.py +431 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/api/main.py +0 -4
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cli.py +4 -4
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/__init__.py +1 -3
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/resources.py +0 -35
- arcsecond-3.13.0/arcsecond/cloud/uploader/__init__.py +8 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/imagesources/commands.py +1 -1
- {arcsecond-3.11.0 → arcsecond-3.13.0}/docs/.vitepress/config.js +2 -0
- arcsecond-3.13.0/docs/install-windows.md +215 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/docs/install.md +7 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/docs/resources.md +0 -1
- {arcsecond-3.11.0 → arcsecond-3.13.0}/docs/upload.md +2 -40
- {arcsecond-3.11.0 → arcsecond-3.13.0}/pyproject.toml +12 -1
- arcsecond-3.13.0/tests/test_alpaca_probe.py +407 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/utils.py +0 -27
- arcsecond-3.13.0/uv.lock +2488 -0
- arcsecond-3.11.0/arcsecond/cloud/uploader/__init__.py +0 -13
- arcsecond-3.11.0/arcsecond/cloud/uploader/allskycameraimages/context.py +0 -62
- arcsecond-3.11.0/arcsecond/cloud/uploader/allskycameraimages/errors.py +0 -19
- arcsecond-3.11.0/arcsecond/cloud/uploader/allskycameraimages/uploader.py +0 -29
- arcsecond-3.11.0/arcsecond/cloud/uploader/allskycameraimages/utils.py +0 -54
- arcsecond-3.11.0/examples/example_upload_images.py +0 -24
- arcsecond-3.11.0/poetry.lock +0 -471
- arcsecond-3.11.0/tests/cloud/uploader/allskycameraimages/test_context.py +0 -115
- arcsecond-3.11.0/tests/cloud/uploader/allskycameraimages/test_uploader_full_process.py +0 -85
- arcsecond-3.11.0/tests/cloud/uploader/datafiles/__init__.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/.docker/Dockerfile_postgres +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/.docker/Dockerfile_redis +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/.github/dependabot.yml +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/.github/workflows/docsdeploy.yml +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/.gitignore +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/LICENSE +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/Makefile +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/__version__.py +0 -0
- {arcsecond-3.11.0/arcsecond/cloud/uploader/allskycameraimages → arcsecond-3.13.0/arcsecond/alpaca}/__init__.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/api/__init__.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/api/config.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/api/constants.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/api/endpoint.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/api/resources.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/auth.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/constants.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/context.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/datafiles/__init__.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/datafiles/context.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/datafiles/errors.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/datafiles/uploader.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/datafiles/utils.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/errors.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/logger.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/uploader.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/utils.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/uploader/walker.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/cloud/uploads.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/errors.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/__init__.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/backups.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/checks.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/constants.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/__init__.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/constants.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/containers.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/docker-compose.yml +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/images.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/docker/utils.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/keygen/__init__.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/keygen/client.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/keygen/utils.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/local.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/main.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/postgres/init-db.sh +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/setup.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/utils.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/hosting/validation.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/imagesources/__init__.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/imagesources/proxy.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/imagesources/registry.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/imagesources/sources/__init__.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/imagesources/sources/base.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/imagesources/sources/filewatch.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/imagesources/sources/opencv.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/options.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/arcsecond/targets.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/deploy.sh +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/docs/.vitepress/theme/custom.css +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/docs/.vitepress/theme/index.js +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/docs/api-basics.md +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/docs/backups.md +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/docs/img/icon-logo.png +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/docs/index.md +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/docs/public/img/icon-logo.png +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/docs/rotate-postgres-password.md +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/docs/webcam.md +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/examples/example_upload_files.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/package-lock.json +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/package.json +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/requirements.txt +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/setup.cfg +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/setup.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/sonar-project.properties +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/__init__.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/api/__init__.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/api/test_api.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/api/test_api_endpoint.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/api/test_config.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/api/test_targets.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/cloud/__init__.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/cloud/uploader/__init__.py +0 -0
- {arcsecond-3.11.0/tests/cloud/uploader/allskycameraimages → arcsecond-3.13.0/tests/cloud/uploader/datafiles}/__init__.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/cloud/uploader/datafiles/test_uploader_errors.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/cloud/uploader/datafiles/test_uploader_full_process.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/cloud/uploader/datafiles/test_uploader_init.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/cloud/uploader/datafiles/test_uploader_prepare.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/cloud/uploader/datafiles/test_uploader_upload.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/conftest.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/fixtures/file1.fits +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/test_cli.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/test_hosting_local.py +0 -0
- {arcsecond-3.11.0 → arcsecond-3.13.0}/tests/test_targets_planning.py +0 -0
|
@@ -25,14 +25,15 @@ jobs:
|
|
|
25
25
|
|
|
26
26
|
- uses: actions/checkout@v5
|
|
27
27
|
|
|
28
|
-
- name:
|
|
29
|
-
uses:
|
|
28
|
+
- name: Install uv
|
|
29
|
+
uses: astral-sh/setup-uv@v6
|
|
30
30
|
with:
|
|
31
31
|
python-version: '3.12'
|
|
32
|
+
enable-cache: true
|
|
32
33
|
|
|
33
34
|
- name: Validate pyproject.toml version matches tag
|
|
34
35
|
run: |
|
|
35
|
-
python - <<'PY'
|
|
36
|
+
uv run python - <<'PY'
|
|
36
37
|
import os, pathlib, tomllib
|
|
37
38
|
|
|
38
39
|
tag = os.environ["GITHUB_REF_NAME"]
|
|
@@ -45,10 +46,7 @@ jobs:
|
|
|
45
46
|
PY
|
|
46
47
|
|
|
47
48
|
- name: Build distributions
|
|
48
|
-
run:
|
|
49
|
-
python -m pip install --upgrade pip
|
|
50
|
-
pip install build
|
|
51
|
-
python -m build
|
|
49
|
+
run: uv build
|
|
52
50
|
|
|
53
51
|
- name: Publish to PyPI (OIDC)
|
|
54
52
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -16,26 +16,22 @@ jobs:
|
|
|
16
16
|
steps:
|
|
17
17
|
- uses: actions/checkout@v5
|
|
18
18
|
|
|
19
|
-
- name:
|
|
20
|
-
uses:
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v6
|
|
21
21
|
with:
|
|
22
22
|
python-version: ${{ matrix.python-version }}
|
|
23
|
+
enable-cache: true
|
|
23
24
|
|
|
24
25
|
- name: Install dependencies
|
|
25
|
-
run:
|
|
26
|
-
python -m pip install --upgrade pip
|
|
27
|
-
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
|
28
|
-
pip install pytest pytest-cov respx
|
|
29
|
-
pip install -e .
|
|
26
|
+
run: uv sync --extra webcam --group dev
|
|
30
27
|
|
|
31
28
|
- name: Test with coverage (only on 3.12)
|
|
32
29
|
if: matrix.python-version == '3.12'
|
|
33
|
-
run:
|
|
34
|
-
pytest -vvs tests/ --cov=arcsecond --cov-report=xml:coverage.xml
|
|
30
|
+
run: uv run pytest -vvs tests/ --cov=arcsecond --cov-report=xml:coverage.xml
|
|
35
31
|
|
|
36
32
|
- name: Test without coverage
|
|
37
33
|
if: matrix.python-version != '3.12'
|
|
38
|
-
run: pytest -vvs tests/
|
|
34
|
+
run: uv run pytest -vvs tests/
|
|
39
35
|
|
|
40
36
|
- name: Upload coverage artifact (only on 3.12)
|
|
41
37
|
if: matrix.python-version == '3.12'
|
|
@@ -58,7 +54,7 @@ jobs:
|
|
|
58
54
|
path: .
|
|
59
55
|
|
|
60
56
|
- name: SonarQube Scan
|
|
61
|
-
uses: SonarSource/sonarqube-scan-action@
|
|
57
|
+
uses: SonarSource/sonarqube-scan-action@v6
|
|
62
58
|
env:
|
|
63
59
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
64
60
|
|
|
@@ -67,24 +63,20 @@ jobs:
|
|
|
67
63
|
steps:
|
|
68
64
|
- uses: actions/checkout@v5
|
|
69
65
|
|
|
70
|
-
- name:
|
|
71
|
-
uses:
|
|
66
|
+
- name: Install uv
|
|
67
|
+
uses: astral-sh/setup-uv@v6
|
|
72
68
|
with:
|
|
73
69
|
python-version: '3.12'
|
|
70
|
+
enable-cache: true
|
|
74
71
|
|
|
75
|
-
- name: Install dependencies
|
|
76
|
-
run:
|
|
77
|
-
python -m pip install --upgrade pip
|
|
78
|
-
pip install flake8 black isort
|
|
72
|
+
- name: Install dev dependencies
|
|
73
|
+
run: uv sync --group dev
|
|
79
74
|
|
|
80
75
|
- name: Lint with flake8
|
|
81
|
-
run:
|
|
82
|
-
flake8 arcsecond tests --count --select=E9,F63,F7,F82 --show-source --statistics
|
|
76
|
+
run: uv run flake8 arcsecond tests --count --select=E9,F63,F7,F82 --show-source --statistics
|
|
83
77
|
|
|
84
78
|
- name: Check formatting with black
|
|
85
|
-
run:
|
|
86
|
-
black --check arcsecond tests
|
|
79
|
+
run: uv run black --check arcsecond tests
|
|
87
80
|
|
|
88
81
|
- name: Check imports with isort
|
|
89
|
-
run:
|
|
90
|
-
isort --check-only --profile black arcsecond tests
|
|
82
|
+
run: uv run isort --check-only --profile black arcsecond tests
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arcsecond
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.13.0
|
|
4
4
|
Summary: CLI for arcsecond.io
|
|
5
5
|
Project-URL: Homepage, https://github.com/arcsecond-io/cli
|
|
6
6
|
Project-URL: Issues, https://github.com/arcsecond-io/cli/issues
|
|
@@ -36,6 +36,7 @@ Classifier: Operating System :: OS Independent
|
|
|
36
36
|
Classifier: Programming Language :: Python :: 3
|
|
37
37
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
38
38
|
Requires-Python: >=3.9
|
|
39
|
+
Requires-Dist: alpyca==3.1.2
|
|
39
40
|
Requires-Dist: click>=8
|
|
40
41
|
Requires-Dist: configparser
|
|
41
42
|
Requires-Dist: docker
|
|
@@ -61,20 +62,19 @@ public and private ones.
|
|
|
61
62
|
|
|
62
63
|
# Development
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
(assuming [virtualenv](https://virtualenv.pypa.io/en/stable/) is installed):
|
|
65
|
+
The project uses [uv](https://docs.astral.sh/uv/) for environment and
|
|
66
|
+
dependency management. After forking and cloning:
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
69
|
$ cd ~/arcsecond-cli
|
|
70
|
-
$
|
|
71
|
-
$
|
|
72
|
-
$
|
|
73
|
-
```
|
|
70
|
+
$ uv sync --extra webcam --group dev
|
|
71
|
+
$ uv run arcsecond --help
|
|
72
|
+
$ uv run pytest
|
|
73
|
+
```
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
`uv sync` creates `.venv/` and installs the project with its `webcam` extra
|
|
76
|
+
and the `dev` dependency group (pytest, black, flake8, isort). Prefix
|
|
77
|
+
commands with `uv run` or activate the venv with `source .venv/bin/activate`.
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
## License
|
|
@@ -11,20 +11,19 @@ public and private ones.
|
|
|
11
11
|
|
|
12
12
|
# Development
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
(assuming [virtualenv](https://virtualenv.pypa.io/en/stable/) is installed):
|
|
14
|
+
The project uses [uv](https://docs.astral.sh/uv/) for environment and
|
|
15
|
+
dependency management. After forking and cloning:
|
|
17
16
|
|
|
18
17
|
```bash
|
|
19
18
|
$ cd ~/arcsecond-cli
|
|
20
|
-
$
|
|
21
|
-
$
|
|
22
|
-
$
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
$ uv sync --extra webcam --group dev
|
|
20
|
+
$ uv run arcsecond --help
|
|
21
|
+
$ uv run pytest
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`uv sync` creates `.venv/` and installs the project with its `webcam` extra
|
|
25
|
+
and the `dev` dependency group (pytest, black, flake8, isort). Prefix
|
|
26
|
+
commands with `uv run` or activate the venv with `source .venv/bin/activate`.
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
## License
|
|
@@ -5,8 +5,6 @@ from .api import (
|
|
|
5
5
|
ArcsecondTargetListsResource,
|
|
6
6
|
)
|
|
7
7
|
from .cloud.uploader import (
|
|
8
|
-
AllSkyCameraImageFileUploader,
|
|
9
|
-
AllSkyCameraImageUploadContext,
|
|
10
8
|
DatasetFileUploader,
|
|
11
9
|
DatasetUploadContext,
|
|
12
10
|
)
|
|
@@ -25,8 +23,6 @@ __all__ = [
|
|
|
25
23
|
"ArcsecondTargetPayloadPlan",
|
|
26
24
|
"DatasetUploadContext",
|
|
27
25
|
"DatasetFileUploader",
|
|
28
|
-
"AllSkyCameraImageFileUploader",
|
|
29
|
-
"AllSkyCameraImageUploadContext",
|
|
30
26
|
"plan_target_payload",
|
|
31
27
|
"walk_folder_and_upload_files",
|
|
32
28
|
]
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Click command group: ``arcsecond alpaca``.
|
|
3
|
+
|
|
4
|
+
First citizen: ``arcsecond alpaca probe dome`` — a read-only diagnostic that
|
|
5
|
+
captures what surface a given Alpaca dome server exposes (device metadata,
|
|
6
|
+
``SupportedActions``, ``CommandString`` / ``CommandBool`` / ``CommandBlind``
|
|
7
|
+
passthrough behaviour, and optional local host hints).
|
|
8
|
+
|
|
9
|
+
Structure leaves room for ``arcsecond alpaca probe telescope`` /
|
|
10
|
+
``probe camera`` / ``probe focuser`` and ``arcsecond alpaca discover`` later
|
|
11
|
+
under the same group.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
from datetime import datetime, timezone
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
import click
|
|
22
|
+
|
|
23
|
+
from ..errors import ArcsecondError
|
|
24
|
+
from .dome_probe import ProbeProgress, probe_dome
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@click.group(name="alpaca", help="Diagnostics for local ASCOM Alpaca devices.")
|
|
28
|
+
def alpaca_group() -> None:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@alpaca_group.group(name="probe", help="Inspect an Alpaca device read-only.")
|
|
33
|
+
def probe_group() -> None:
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
_COMMON_OPTIONS = [
|
|
38
|
+
click.option(
|
|
39
|
+
"--host",
|
|
40
|
+
required=True,
|
|
41
|
+
metavar="HOST",
|
|
42
|
+
help="Alpaca server hostname or IP (without scheme).",
|
|
43
|
+
),
|
|
44
|
+
click.option(
|
|
45
|
+
"--port",
|
|
46
|
+
type=int,
|
|
47
|
+
required=True,
|
|
48
|
+
metavar="PORT",
|
|
49
|
+
help="Alpaca server TCP port (e.g. 11111).",
|
|
50
|
+
),
|
|
51
|
+
click.option(
|
|
52
|
+
"--device-number",
|
|
53
|
+
type=int,
|
|
54
|
+
default=0,
|
|
55
|
+
show_default=True,
|
|
56
|
+
help="Alpaca device number on the server.",
|
|
57
|
+
),
|
|
58
|
+
click.option(
|
|
59
|
+
"--protocol",
|
|
60
|
+
type=click.Choice(["http", "https"], case_sensitive=False),
|
|
61
|
+
default="http",
|
|
62
|
+
show_default=True,
|
|
63
|
+
help="Protocol to reach the Alpaca server with.",
|
|
64
|
+
),
|
|
65
|
+
click.option(
|
|
66
|
+
"--allow-active",
|
|
67
|
+
is_flag=True,
|
|
68
|
+
default=False,
|
|
69
|
+
help=(
|
|
70
|
+
"Also send CommandBlind and active-class probes. OFF by default "
|
|
71
|
+
"to keep the run safe on real hardware."
|
|
72
|
+
),
|
|
73
|
+
),
|
|
74
|
+
click.option(
|
|
75
|
+
"--collect-host-info",
|
|
76
|
+
is_flag=True,
|
|
77
|
+
default=False,
|
|
78
|
+
help=(
|
|
79
|
+
"Additionally collect best-effort local OS hints (open ports, "
|
|
80
|
+
"COM ProgIDs matching TCS/Galil). Read-only."
|
|
81
|
+
),
|
|
82
|
+
),
|
|
83
|
+
click.option(
|
|
84
|
+
"--output",
|
|
85
|
+
"output_path",
|
|
86
|
+
type=click.Path(dir_okay=False, writable=True, resolve_path=True),
|
|
87
|
+
default=None,
|
|
88
|
+
help="Where to write the JSON report. Defaults to cwd with a UTC timestamp.",
|
|
89
|
+
),
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _add_options(options):
|
|
94
|
+
def _wrap(fn):
|
|
95
|
+
for opt in reversed(options):
|
|
96
|
+
fn = opt(fn)
|
|
97
|
+
return fn
|
|
98
|
+
|
|
99
|
+
return _wrap
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _default_output_path(kind: str) -> str:
|
|
103
|
+
stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
|
|
104
|
+
return str(Path.cwd() / f"alpaca_{kind}_probe_{stamp}.json")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _format_progress(label: str, ok: bool, detail: str | None) -> str:
|
|
108
|
+
tag = click.style("[OK ]", fg="green") if ok else click.style("[ERR]", fg="red")
|
|
109
|
+
suffix = f" — {detail}" if detail else ""
|
|
110
|
+
return f" {tag} {label}{suffix}"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@probe_group.command(
|
|
114
|
+
name="dome",
|
|
115
|
+
help=(
|
|
116
|
+
"Probe a local Alpaca dome device read-only and write a JSON report.\n\n"
|
|
117
|
+
"Captures device metadata, SupportedActions, and the behaviour of the "
|
|
118
|
+
"legacy CommandString / CommandBool / CommandBlind passthroughs. "
|
|
119
|
+
"Useful for figuring out whether a proprietary driver (e.g. TCSGalil) "
|
|
120
|
+
"exposes any vendor-specific extension surface beyond the standard "
|
|
121
|
+
"ASCOM IDome interface."
|
|
122
|
+
),
|
|
123
|
+
)
|
|
124
|
+
@_add_options(_COMMON_OPTIONS)
|
|
125
|
+
def probe_dome_cmd(
|
|
126
|
+
host: str,
|
|
127
|
+
port: int,
|
|
128
|
+
device_number: int,
|
|
129
|
+
protocol: str,
|
|
130
|
+
allow_active: bool,
|
|
131
|
+
collect_host_info: bool,
|
|
132
|
+
output_path: str | None,
|
|
133
|
+
) -> None:
|
|
134
|
+
output_path = output_path or _default_output_path("dome")
|
|
135
|
+
|
|
136
|
+
click.echo(
|
|
137
|
+
click.style("Alpaca dome probe", bold=True)
|
|
138
|
+
+ f" → {protocol}://{host}:{port} (device {device_number})"
|
|
139
|
+
)
|
|
140
|
+
if allow_active:
|
|
141
|
+
click.echo(
|
|
142
|
+
click.style(" ⚠ ", fg="yellow")
|
|
143
|
+
+ "--allow-active is ON: CommandBlind and active-class probes will be sent."
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
progress = ProbeProgress(
|
|
147
|
+
on_probe=lambda label, ok, detail: click.echo(
|
|
148
|
+
_format_progress(label, ok, detail)
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
result = probe_dome(
|
|
154
|
+
host=host,
|
|
155
|
+
port=port,
|
|
156
|
+
device_number=device_number,
|
|
157
|
+
protocol=protocol.lower(),
|
|
158
|
+
allow_active=allow_active,
|
|
159
|
+
collect_host_info=collect_host_info,
|
|
160
|
+
progress=progress,
|
|
161
|
+
)
|
|
162
|
+
except RuntimeError as exc:
|
|
163
|
+
raise ArcsecondError(str(exc)) from exc
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
with open(output_path, "w", encoding="utf-8") as fp:
|
|
167
|
+
json.dump(result.report, fp, indent=2, sort_keys=False)
|
|
168
|
+
fp.write("\n")
|
|
169
|
+
except OSError as exc:
|
|
170
|
+
raise ArcsecondError(f"Could not write report to {output_path}: {exc}") from exc
|
|
171
|
+
|
|
172
|
+
counts = result.counts
|
|
173
|
+
supported = result.report.get("supported_actions", {})
|
|
174
|
+
supported_n = len(supported.get("value") or []) if supported.get("ok") else 0
|
|
175
|
+
|
|
176
|
+
click.echo("")
|
|
177
|
+
click.echo(click.style("Summary", bold=True))
|
|
178
|
+
click.echo(f" Probes: {counts.ok}/{counts.total} OK")
|
|
179
|
+
click.echo(f" SupportedActions: {supported_n} entries")
|
|
180
|
+
click.echo(f" Report written to {click.style(os.fspath(output_path), fg='cyan')}")
|