Commit 0658217f authored by vertighel's avatar vertighel
Browse files

Fase 0: chiusi i due punti sospesi delle convenzioni; fix rename...


Fase 0: chiusi i due punti sospesi delle convenzioni; fix rename camera->scicam in synoptic.svg; loggati 2 bug hardware atik.py

javascript.md: confermato che i transform inline in synoptic.js restano
così (dati live), documentato il rename fatto in synoptic.svg.

python.md: decisa la strategia per il SystemExit nei fallback di
check.py — flag esplicito _RECOVERABLE settato solo da noctua.app:run(),
non inferito dall'ambiente; noctua-sequencer/noctua-guider/ipython
restano a fallire duro. Implementazione rimandata alla fase devices.

synoptic.svg: id e data-status camera/camera2/camera3 (residuo pre-
split scicam/teccam) rinominati in scicam1/scicam2/scicam3, allineati
ai veri nomi di subsystem della telemetria (prefissi delle route in
api.ini, vedi noctua/web/stream.py) — prima del fix questi campi
dell'SVG non potevano mai ricevere un aggiornamento.

PLAN.md: aggiunta sezione con i due bug hardware trovati testando
atik.py su fork dopo il fix iniziale (corruzione CCD-TEMP ancora
presente con cooling attivo; download() che si blocca all'infinito se
il cooling viene azionato a metà) — aperti, non ancora risolti.

Co-Authored-By: default avatarClaude Sonnet 5 <noreply@anthropic.com>
parent 70aeebcd
Loading
Loading
Loading
Loading
+17 −3
Original line number Diff line number Diff line
@@ -56,12 +56,26 @@ set of states — the actual point of the "no injected HTML" rule):

## No injected CSS except genuinely runtime-computed values

**Synoptic panel**: `synoptic.js` fetches and injects an Inkscape SVG
(`web/static/img/synoptic.svg`) whose elements it drives purely through
`data-status` attributes (matched against telemetry subsystem names by
`parseStatusKey()` in `ui-core.js`), not hardcoded element IDs.

**Keep inline** (confirmed live-data-driven, no static class could
express it): `synoptic.js:47,79,111,127,139-140,154-157`
(`transform`/`transformOrigin` from dome azimuth, mirror offset, cover
position telemetry), `viewer/fits-viewer.js:652` (aspect ratio from
FITS shape), `:1408-1409` (pan/zoom transform), `:1450-1452` (crosshair
overlay position from live pixel coords).
FITS shape), `:1408-1409` (pan/zoom transform), `:1450-1452`
(crosshair overlay position from live pixel coords).

`synoptic.svg`'s `camera`/`camera2`/`camera3` ids and `data-status`
values (leftover from before the scicam/teccam split) have been renamed
to `scicam1`/`scicam2`/`scicam3` to match the real telemetry subsystem
names (`/scicam1/...`, `/scicam2/...`, ... in `api.ini`) — before this
they could never match a broadcast subsystem, so those SVG fields were
silently dead.

**Custom class names** should start with `noctua-`.

**Convert to a class**:
- `control.js:320``dot.style.cssText` (badge sizing) → a CSS class.
@@ -71,7 +85,7 @@ overlay position from live pixel coords).
  sides should become `.d-none` toggling instead of inline style both
  ways.
- `viewer/fits-viewer.js:1041,1138``cursor` grabbing/crosshair,
  low-value inline case, could be `.cursor-grabbing`/`.cursor-crosshair`
  low-value inline case, could be `.noctua-cursor-grabbing`/`.noctua-cursor-crosshair`
  classes (minor, not urgent).

## Click safety — disable during in-flight requests
+40 −13
Original line number Diff line number Diff line
@@ -33,14 +33,15 @@ from noctua.utils.logger import log
```

No underscore aliases unless there's a genuine name conflict (`import
time`, not `import time as _time`; `devices`, not `_dev`). Deferred
imports inside a method only when strictly necessary (circular
imports, optional dependency).
time`, not `import time as _time`; `devices`, not `_dev`). 
No imports without a method.
No third-party modules except the ones listed in `pyproject.toml`

## Naming

`snake_case` everywhere (PEP 8) — methods, attributes, variables,
functions. No camelCase, even for names that mirror a JS-side concept.
Use declarative names, maximize auto-documentable variable names.

## Private names — minimize

@@ -62,11 +63,9 @@ explicit project preference, not just an observation.
- Private methods: no docstring unless the logic is non-obvious.
- **Always in English** — comments too. This is new as of this
  cleanup; older Italian comments/docstrings get translated as their
  file comes up for review. Scope is comments/docstrings only — log
  and user-facing error strings are a separate, not-yet-decided
  question (today's codebase mixes Italian/English there, e.g.
  `mako.py`: `"Mako: binning {b} non nel range [{lo}, {hi}]"` vs
  `"Mako connection failed"" — leave as-is until asked).
  file comes up for review. Translate also eventual italian log
  and user-facing error strings, e.g.
  `mako.py`: `"Mako: binning {b} non nel range [{lo}, {hi}]"`.

## Blank lines

@@ -78,6 +77,10 @@ explicit project preference, not just an observation.
  before an early one-line guard clause (`if self._looping: return`
  stays compact, very common in this codebase).

## Columns

- Fit into maximum 85 columns.

## Alignment

Vertically align `=` in `__init__` assignment blocks and dict
@@ -131,11 +134,35 @@ Fix: design new decorators for the ctypes/SDK family (one for
Atik/STL-style ctypes calls, one for VmbPy) instead of reusing the
existing HTTP/telnet-specific ones, which don't fit.

**Open question, not decided**: several `check.py` fallbacks do `raise
SystemExit(e)` on a fully unhandled exception — this kills the entire
`noctua-api` process for an unexpected error on a single device.
Decide during the devices phase whether new decorators should replicate
this or contain the failure to just that device/subsystem.
**Decided**: several `check.py` fallbacks do `raise SystemExit(e)` on a
fully unhandled exception — this kills the entire `noctua-app` process
for an unexpected error on a single device. `pyproject.toml` defines
three separate entry points (`noctua-app`, `noctua-sequencer`,
`noctua-guider`), plus ad hoc usage (ipython, scripts) — only
`noctua-app` is the long-lived process where recovering from a single
device's error is worth it; everywhere else (ipython, the other two
entry points) should keep failing loudly and immediately, as today.

Don't infer this from the environment (`sys.argv[0]`, env vars — both
fragile across systemd/tmux/`python -m`/pip-wrapper invocations).
Instead, an explicit opt-in flag in `check.py`, default `False` (today's
hard-exit behavior, unchanged everywhere):

```python
_RECOVERABLE = False

def set_recoverable(value=True):
    global _RECOVERABLE
    _RECOVERABLE = value
```

New decorators check `_RECOVERABLE` in the unhandled-exception fallback
(`if _RECOVERABLE: log.error(...); return` instead of `raise
SystemExit(e)`). Only `noctua.app:run()` calls `set_recoverable(True)`,
before starting the server — `noctua.sequencer:cli()`,
`noctua.guider:cli()`, and ipython never call it, so they keep the
current hard-exit behavior unchanged. Implementation is devices-phase
work, not done yet.

**Also found by audit**: `domotics.py`'s `get`/`put` (lines 25-26,
58-59) start with a bare `return` before any body — dead code, every
+51 −8
Original line number Diff line number Diff line
@@ -11,14 +11,22 @@ read those for the *how*; this file tracks the *what/status*.
## Testing constraints (see also the environment notes in Claude's
memory, not duplicated here)

Work happens mostly on **snoopy**. From home (remote): everything
testable except USB devices physically on **fork** (Atik, STL) and the
two Mako GigE cameras (on the telescope's own LAN). At the telescope:
everything testable, USB included. **fork** is always the deploy
target regardless of where code is written. → batch Atik/STL/Mako
device-layer changes for on-site sessions; do STX/telescope/dome/
stage/api/jinja/js work (excluding Atik/STL/Mako specifics) when
working remotely for faster iteration.
Work happens in two environments:
- **snoopy** laptop (Ubuntu 26.04). It can be connected wifi from home to the telescope network using a VPN, or wifi connected in the telescope LAN.
- **fork** 10 years old fanless pc (Ubuntu 22.04), ethernet connected
  to the telescope lan. 

**snoopy** from home can test all hardware except USB devices (Atik, STL) and LAN-only devices (the two Mako)
**snoopy** from wifi LAN can test all hardware except USB devices. Eventually, USB devices can be connected.
**fork** can test all hardware.

The main work happens on **snoopy**, where the Claude session operates.

**fork** is always the deploy target regardless of where
code is written. → batch Atik/STL/Mako device-layer changes for
on-site sessions; do STX/telescope/dome/ stage/api/jinja/js work
(excluding Atik/STL/Mako specifics) when working remotely for faster
iteration.

## Order (least → most risky)

@@ -85,6 +93,41 @@ just style)
- **`noctua/api/.#webcam.py`** — a dangling Emacs lock symlink, not
  real source. Remove, add the pattern to `.gitignore`.

## Bugs found during hardware testing on `main` (post-Phase-0, atik.py)

Found and partially addressed directly on `main` (not this branch) while
testing on `fork`, ahead of reaching the devices phase — noted here so
they aren't lost and get properly closed out when `atik.py` comes up.
Commits so far: `e662296` (indentation crash fix, `RLock` serializing all
Artemis SDK calls, uint16 FITS in `stl.py`), `9768f56` (check
`ArtemisImageFailed()` + `ArtemisTemperatureSensorInfo` return code),
`c4e04ca`/"atik dev" (settle `time.sleep(0.2)` before reading CCD-TEMP on
binned frames). None of the below are closed.

- **CCD-TEMP still corrupts, now correlated with cooling being on**: on
  `fork`, with cooling active, `CCD-TEMP` intermittently reads a large
  implausible sentinel again (`399.03`, `397.11`) — same shape as the
  original `0.31`/`399.03` symptom, surviving the `RLock`, the
  `ArtemisImageFailed()` check, the return-code retry, *and* the 0.2s
  settle sleep before the binned-frame read. Bit-shift/truncation of a
  real reading was checked and ruled out (no bitwise op maps varying real
  readings to a constant). Since it persists through serialization, error
  checking, and a settle delay, binning may not be the whole story —
  cooling state look like another variable at play, and the true cause is
  still unknown; needs investigation with the physical camera, not
  further blind guesses from the host side. Do not attempt another fix
  without hardware in hand.
- **`download()` hangs forever if cooling is toggled mid-download**: the
  `while True: ... ArtemisImageReady(h) ...` poll loop in `download()`
  (and the equivalent wait in `_run_loop`) has no timeout — if toggling
  `cooler` while an exposure/readout is in flight leaves
  `ArtemisImageReady()` never returning true, the loop spins
  indefinitely with no way out (no abort, no error surfaced). Pre-dates
  the recent fixes (the original code had the same unbounded loop) but
  was only now triggered/observed. Needs a bounded timeout with an
  explicit error path, matching `stop_looping()`'s bounded-wait pattern
  elsewhere in the same file.

## Phase checklists

### 1. Devices
+24 −24
Original line number Diff line number Diff line
@@ -396,19 +396,19 @@
   sodipodi:nodetypes="ssccssscscsscscsscscsssccs"
   data-status="stage-connection" /><path
   d="m 365.06325,202.08714 c 0.13195,0.49233 0.6392,0.78519 1.13155,0.6533 l 5.1697,-1.38482 0.98966,3.69249 -5.16966,1.38483 c -0.49236,0.13188 -0.78519,0.63907 -0.65322,1.1314 0.13195,0.49233 0.63921,0.7852 1.13155,0.6533 l 5.16969,-1.38482 0.65978,2.46166 c 0.13197,0.49234 0.67824,0.80773 1.13157,0.65331 l 4.67734,-1.25293 c 1.8463,-0.49459 3.43315,-1.71118 4.4017,-3.38878 0.8109,-1.40449 1.10851,-3.00129 0.91979,-4.56676 l 4.18498,-1.12105 c 0.49235,-0.13189 0.78517,-0.63907 0.65322,-1.1314 -0.0885,-0.20714 -0.23854,-0.39782 -0.47266,-0.53298 -0.23412,-0.13518 -0.47425,-0.16977 -0.72044,-0.10384 l -4.18499,1.12105 c -0.59699,-1.48908 -1.67588,-2.68418 -3.08057,-3.49518 -1.67783,-0.9687 -3.62152,-1.20658 -5.50687,-0.73453 l -4.67733,1.25295 c -0.24619,0.0659 -0.43685,0.21595 -0.57199,0.45003 -0.11263,0.19507 -0.16967,0.47426 -0.10375,0.72038 l 0.64329,2.40012 -5.16968,1.38483 c -0.3918,0.13793 -0.68462,0.6451 -0.55258,1.13748 z"
   id="icon:camera3-power-status"
   id="icon:scicam3-power-status"
   style="fill:#008080;fill-opacity:0.298039;stroke:#cccccc;stroke-width:1;stroke-dasharray:none"
   data-status="camera3-power" /><path
   data-status="scicam3-power" /><path
   d="m 11.862962,343.2085 c 0.13195,0.49233 0.6392,0.78519 1.13155,0.6533 l 5.1697,-1.38482 0.98966,3.69249 -5.16966,1.38483 c -0.49236,0.13188 -0.78519,0.63907 -0.65322,1.1314 0.13195,0.49233 0.63921,0.7852 1.13155,0.6533 l 5.16969,-1.38482 0.65978,2.46166 c 0.13197,0.49234 0.67824,0.80773 1.13157,0.65331 l 4.67734,-1.25293 c 1.8463,-0.49459 3.43315,-1.71118 4.4017,-3.38878 0.8109,-1.40449 1.10851,-3.00129 0.91979,-4.56676 l 4.18498,-1.12105 c 0.49235,-0.13189 0.78517,-0.63907 0.65322,-1.1314 -0.0885,-0.20714 -0.23854,-0.39782 -0.47266,-0.53298 -0.23412,-0.13518 -0.47425,-0.16977 -0.72044,-0.10384 l -4.18499,1.12105 c -0.59699,-1.48908 -1.67588,-2.68418 -3.08057,-3.49518 -1.67783,-0.9687 -3.62152,-1.20658 -5.50687,-0.73453 l -4.67733,1.25295 c -0.24619,0.0659 -0.43685,0.21595 -0.57199,0.45003 -0.11263,0.19507 -0.16967,0.47426 -0.10375,0.72038 l 0.64329,2.40012 -5.16968,1.38483 c -0.3918,0.13793 -0.68462,0.6451 -0.55258,1.13748 z"
   id="icon:camera2-power-status"
   id="icon:scicam2-power-status"
   style="fill:#008080;fill-opacity:0.298039;stroke:#cccccc;stroke-width:1;stroke-dasharray:none"
   sodipodi:nodetypes="ssccssscscsscscsscscsssccs"
   data-status="camera2-power" /><path
   data-status="scicam2-power" /><path
   d="m 364.1034,343.2085 c 0.13195,0.49233 0.6392,0.78519 1.13155,0.6533 l 5.1697,-1.38482 0.98966,3.69249 -5.16966,1.38483 c -0.49236,0.13188 -0.78519,0.63907 -0.65322,1.1314 0.13195,0.49233 0.63921,0.7852 1.13155,0.6533 l 5.16969,-1.38482 0.65978,2.46166 c 0.13197,0.49234 0.67824,0.80773 1.13157,0.65331 l 4.67734,-1.25293 c 1.8463,-0.49459 3.43315,-1.71118 4.4017,-3.38878 0.8109,-1.40449 1.10851,-3.00129 0.91979,-4.56676 l 4.18498,-1.12105 c 0.49235,-0.13189 0.78517,-0.63907 0.65322,-1.1314 -0.0885,-0.20714 -0.23854,-0.39782 -0.47266,-0.53298 -0.23412,-0.13518 -0.47425,-0.16977 -0.72044,-0.10384 l -4.18499,1.12105 c -0.59699,-1.48908 -1.67588,-2.68418 -3.08057,-3.49518 -1.67783,-0.9687 -3.62152,-1.20658 -5.50687,-0.73453 l -4.67733,1.25295 c -0.24619,0.0659 -0.43685,0.21595 -0.57199,0.45003 -0.11263,0.19507 -0.16967,0.47426 -0.10375,0.72038 l 0.64329,2.40012 -5.16968,1.38483 c -0.3918,0.13793 -0.68462,0.6451 -0.55258,1.13748 z"
   id="icon:camera-power-status"
   id="icon:scicam1-power-status"
   style="display:inline;fill:#008080;fill-opacity:0.298039;stroke:#cccccc;stroke-width:1;stroke-dasharray:none"
   sodipodi:nodetypes="ssccssscscsscscsscscsssccs"
   data-status="camera-power" /><path
   data-status="scicam1-power" /><path
   d="m 158.02597,239.08505 h -1.18121 c -0.75154,0 -1.68327,-0.53358 -2.168,-1.25547 l -1.41221,-0.73364 -3.23794,10e-6 -0.83433,0.48885 c -0.38616,0.85502 -1.27284,1.50023 -2.06173,1.49976 h -1.93584 c -0.42774,4.9e-4 -0.83123,-0.20739 -1.13578,-0.5863 v 0 c -0.30416,-0.37841 -0.47164,-0.87993 -0.47164,-1.41258 l 3.9e-4,-8.78252 c 3.7e-4,-1.10219 0.72113,-1.99889 1.60703,-1.99937 l 12.83126,4.8e-4 c 0.8863,6.8e-4 1.60704,0.89861 1.60742,1.99984 v 8.78108 c 0,0.53266 -0.16749,1.03418 -0.47203,1.41307 -0.30415,0.37841 -0.70725,0.58678 -1.1354,0.58679 z"
   id="path1-9"
   style="fill:#008080;fill-opacity:0.298039;stroke:#e6e6e6;stroke-width:1"
@@ -667,15 +667,15 @@
   style="fill:#008080;fill-opacity:0.298039;stroke:#cccccc;stroke-width:0.999998;stroke-dasharray:none"
   d="m 186.17786,44.062272 c 0.0241,0.729645 -0.7628,3.424269 -2.9302,3.424269 -2.1674,0 -2.9302,-2.49921 -2.9302,-3.424269 z m 3.90751,-10.42066 c 10e-5,-0.907814 -0.1805,-1.806613 -0.53144,-2.643868 -0.35094,-0.837246 -0.86499,-1.596196 -1.51237,-2.232645 -0.64738,-0.636439 -1.41499,-1.13759 -2.25811,-1.474211 -0.84301,-0.336631 -1.74473,-0.501963 -2.65241,-0.486403 -1.74249,0.05182 -3.40159,0.757963 -4.64688,1.977902 -1.24529,1.219949 -1.98543,2.86419 -2.07305,4.605265 -0.0417,1.161813 0.21363,2.315042 0.74173,3.350769 0.5281,1.035629 1.31158,1.919572 2.27636,2.56822 0.27192,0.176789 0.49553,0.418432 0.6507,0.70315 0.15518,0.284815 0.23703,0.603718 0.23814,0.927993 v 1.494401 h 5.8604 v -1.494401 c 0,-0.323396 0.0804,-0.641713 0.23383,-0.92643 0.15345,-0.284718 0.37517,-0.52685 0.64523,-0.704713 0.93005,-0.624034 1.69268,-1.467052 2.2206,-2.454822 0.52803,-0.987769 0.80522,-2.090207 0.80727,-3.210207 z"
   data-status="telescope-lamp-status" /><g
   id="g-camera2"
   id="g-scicam2"
   transform="translate(-194.78915,238.29897)" /><g
   id="g-camera3"
   id="g-scicam3"
   transform="translate(109.21085,104.8576)"><text
     x="206.91849"
     y="125.27547"
     style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3333px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ececec;fill-opacity:1"
     id="text135-3">state:</text><text
     id="telemetry:camera3:state:response"
     id="telemetry:scicam3:state:response"
     x="246.91849"
     y="125.27547"
     style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3333px;font-family:'Courier New';-inkscape-font-specification:'Courier New, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#00ff00">[???]</text><text
@@ -683,7 +683,7 @@
     y="139.27547"
     style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3333px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ececec;fill-opacity:1"
     id="text137-0">temp:</text><text
     id="telemetry:camera3:cooler:temperature:response-4"
     id="telemetry:scicam3:cooler:temperature:response-4"
     x="246.91849"
     y="139.27547"
     style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3333px;font-family:'Courier New';-inkscape-font-specification:'Courier New, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#00ff00">[???]</text></g><text
@@ -797,35 +797,35 @@
   x="304.87238"
   y="104.36085"
   style="font-size:13px;font-family:'Ubuntu Mono';fill:#cccccc">[???]</text><text
   id="label:camera-cooler"
   id="label:scicam1-cooler"
   x="270.13333"
   y="367.72519"
   style="font-size:13px;fill:#cccccc">   cooler:</text><text
   id="value:camera-cooler"
   data-status="camera-cooler"
   id="value:scicam1-cooler"
   data-status="scicam1-cooler"
   x="354.13333"
   y="367.72519"
   style="font-size:13px;font-family:'Ubuntu Mono';fill:#cccccc">[???]</text><text
   id="value:camera-filter"
   data-status="camera-filter"
   id="value:scicam1-filter"
   data-status="scicam1-filter"
   x="215.76125"
   y="328.03528"
   style="font-size:13px;font-family:'Ubuntu Mono';fill:#cccccc">[???]</text><text
   id="label:camera-settings-temperature"
   id="label:scicam1-settings-temperature"
   x="270.13333"
   y="381.72519"
   style="font-size:13px;fill:#cccccc">   temp:</text><text
   id="value:camera-settings-temperature"
   data-status="camera-settings-temperature"
   id="value:scicam1-settings-temperature"
   data-status="scicam1-settings-temperature"
   x="354.13333"
   y="381.72519"
   style="font-size:13px;font-family:'Ubuntu Mono';fill:#cccccc">[???]</text><text
   id="label:camera-snapshot-state"
   id="label:scicam1-snapshot-state"
   x="270.13333"
   y="395.72519"
   style="font-size:13px;fill:#cccccc">   state:</text><text
   id="value:camera-snapshot-state"
   data-status="camera-snapshot-state"
   id="value:scicam1-snapshot-state"
   data-status="scicam1-snapshot-state"
   x="354.13333"
   y="395.72519"
   style="font-size:13px;font-family:'Ubuntu Mono';fill:#cccccc">[???]</text><text
@@ -843,7 +843,7 @@
   y="367.28592"
   style="font-size:13px;fill:#cccccc">   cooler:</text><text
   id="text7"
   data-status="camera2-cooler"
   data-status="scicam2-cooler"
   x="108.51318"
   y="367.28592"
   style="font-size:13px;font-family:'Ubuntu Mono';fill:#cccccc">[???]</text><text
@@ -852,7 +852,7 @@
   y="381.28592"
   style="font-size:13px;fill:#cccccc">   temp:</text><text
   id="text11"
   data-status="camera2-settings-temperature"
   data-status="scicam2-settings-temperature"
   x="108.51318"
   y="381.28592"
   style="font-size:13px;font-family:'Ubuntu Mono';fill:#cccccc">[???]</text><text
@@ -861,7 +861,7 @@
   y="395.49265"
   style="font-size:13px;fill:#cccccc">   state:</text><text
   id="text21"
   data-status="camera2-snapshot-state"
   data-status="scicam2-snapshot-state"
   x="108.29355"
   y="395.49265"
   style="font-size:13px;font-family:'Ubuntu Mono';fill:#cccccc">[???]</text><text