Commit 6ce0f52f authored by vertighel's avatar vertighel
Browse files

refactor: UI macros + JS — scicam1/2, panels da /api/cameras; v0.6



control.html: data-subsystem scicam1/2, prefissi macro scicam1/2,
  tuple guider cam1/tec1/2, panels da fetch /api/cameras (passo 11+12).
control_panel.html: fits_viewer_section nuova firma camera_panel,
  data-telemetry-init scicam1/2/3.
init.html: endpoint e status-key /camera→/scicam1, /camera2→/scicam2.
guider-panel.js: PASSIVE_CAMERAS cam1, default tec1, telemetry all-scicam1.
control.js: cam→cam1 in MODES e postExpose.
pyproject.toml: version 0.5.1 → 0.6.

Co-Authored-By: default avatarClaude Sonnet 4.6 <noreply@anthropic.com>
parent c5c1c80c
Loading
Loading
Loading
Loading
+33 −33
Original line number Diff line number Diff line
@@ -145,40 +145,40 @@
    
    {{ ctrl.observing_mode_selector() }}

    <section id="mode-panel-imaging" class="mode-panel" data-subsystem="camera">
    <section id="mode-panel-imaging" class="mode-panel" data-subsystem="scicam1">

      {{ ctrl.widget_frame("imaging", "camera",
      {{ ctrl.widget_frame("imaging", "scicam1",
                          [("Full","full"),("Half","half"),("2'×2'","small")]) }}

      {{ ctrl.imaging_panel() }}

      {{ ctrl.expose_widget("imaging", "camera", has_filter=True) }}
      {{ ctrl.expose_widget("imaging", "scicam1", has_filter=True) }}

      <h4 class="mt-2 mb-2">Guider</h4>

      {{ ctrl.guider_panel("imaging",
                           [("cam","scicam","ctrl-combo1-sci"),
                            ("teccam","teccam","ctrl-combo1-tec")],
                           "camera",
                           [("cam1","scicam1","ctrl-combo1-sci"),
                            ("tec1","teccam1","ctrl-combo1-tec")],
                           "scicam1",
                           has_actuator_select=True) }}

    </section>

    <section id="mode-panel-spectro" class="mode-panel d-none" data-subsystem="camera2">
    <section id="mode-panel-spectro" class="mode-panel d-none" data-subsystem="scicam2">

      {{ ctrl.widget_frame("spectro", "camera2",
      {{ ctrl.widget_frame("spectro", "scicam2",
                          [("Full","full"),("Half","half"),("Stripe","small")],
                          binning_max=2) }}

      {{ ctrl.spectro_panel() }}

      {{ ctrl.expose_widget("spectro", "camera2", has_filter=False) }}
      {{ ctrl.expose_widget("spectro", "scicam2", has_filter=False) }}

      <h4 class="mt-2 mb-2">Guider</h4>

      {{ ctrl.guider_panel("spectro",
                           [("teccam2","teccam2","ctrl-combo2-tec")],
                           "camera2") }}
                           [("tec2","teccam2","ctrl-combo2-tec")],
                           "scicam2") }}

    </section>

@@ -235,11 +235,11 @@
    import { FitsViewer }  from "{{ url_for('web.static', filename='js/viewer/fits-viewer.js') }}";
    import { register as registerViewer } from "{{ url_for('web.static', filename='js/viewer/viewer-registry.js') }}";

    function initViewer(panelId, station, camera, hasLoop, apiBase) {
    function initViewer(panelId, cam_id, hasLoop, apiBase) {
        const panel = document.getElementById(`viewer-panel-${panelId}`);
        if (!panel) return null;

        const viewer = new FitsViewer(station, panel, apiBase, [camera]);
        const viewer = new FitsViewer(panel, apiBase, [cam_id]);
        viewer.refresh();

        panel.querySelector(`.btn-refresh-${panelId}`)
@@ -263,7 +263,7 @@
            }

            async function loopPost(action, extra = {}) {
                return fetch(`${apiBase}/viewer/${station}/${camera}/loop`, {
                return fetch(`${apiBase}/viewer/${cam_id}/loop`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ action, ...extra }),
@@ -281,7 +281,7 @@
                setLoopUI(false);
            });

            fetch(`${apiBase}/viewer/${station}/${camera}/loop`)
            fetch(`${apiBase}/viewer/${cam_id}/loop`)
                .then(r => r.json())
                .then(d => setLoopUI(d.running))
                .catch(() => {});
@@ -290,19 +290,19 @@
        return viewer;
    }

    const panels = [
        { id: 'ctrl-combo1-sci', station: 'station1', camera: 'scicam',  hasLoop: false },
        { id: 'ctrl-combo1-tec', station: 'station1', camera: 'teccam',  hasLoop: true  },
        { id: 'ctrl-combo2-sci', station: 'station2', camera: 'scicam',  hasLoop: false },
        { id: 'ctrl-combo2-tec', station: 'station2', camera: 'teccam',  hasLoop: true  },
        { id: 'ctrl-combo3-sci', station: 'station3', camera: 'scicam',  hasLoop: false },
        { id: 'ctrl-combo3-tec', station: 'station3', camera: 'teccam',  hasLoop: true  },
    ];

    for (const p of panels) {
        const viewer = initViewer(p.id, p.station, p.camera, p.hasLoop, '/api');
        if (viewer) registerViewer(p.id, viewer);
    fetch('/api/cameras')
        .then(r => r.json())
        .then(cameras => {
            for (const [cam_id, cfg] of Object.entries(cameras)) {
                const station = cfg.station;
                const role    = cfg.role === 'sci' ? 'sci' : 'tec';
                const id      = `ctrl-combo${station}-${role}`;
                const hasLoop = 'device' in cfg;
                const viewer  = initViewer(id, cam_id, hasLoop, '/api');
                if (viewer) registerViewer(id, viewer);
            }
        })
        .catch(err => console.warn('cameras fetch failed:', err));
    </script>
    <script src="{{ url_for('web.static', filename='js/webcam.js') }}"></script>
    <script>window.SYNOPTIC_SVG_URL = "{{ url_for('web.static', filename='img/synoptic.svg') }}";</script>
+19 −19
Original line number Diff line number Diff line
@@ -183,21 +183,21 @@

            {{ w.widget_standard({
                "label": "Power",
                "info": "camera-power",
                "info": "scicam1-power",
                "map": "bool_yesno",
                "buttons": [
                    {"label": "On",  "endpoint": "/camera/power", "val": true,  "method": "PUT"},
                    {"label": "Off", "endpoint": "/camera/power", "val": false, "method": "PUT"}
                    {"label": "On",  "endpoint": "/scicam1/power", "val": true,  "method": "PUT"},
                    {"label": "Off", "endpoint": "/scicam1/power", "val": false, "method": "PUT"}
                ],
            }) }}

            {{ w.widget_standard({
                "label": "Cooling",
                "info": "camera-cooler",
                "info": "scicam1-cooler",
                "map": "bool_yesno",
                "buttons": [
                    {"label": "On",  "endpoint": "/camera/cooler", "val": true,  "method": "PUT"},
                    {"label": "Off", "endpoint": "/camera/cooler", "val": false, "method": "PUT"}
                    {"label": "On",  "endpoint": "/scicam1/cooler", "val": true,  "method": "PUT"},
                    {"label": "Off", "endpoint": "/scicam1/cooler", "val": false, "method": "PUT"}
                ],
            }) }}

@@ -205,11 +205,11 @@
                "label": "Temp_1",
                "inputs": [{"value": -10}],
                "unit": "°C",
                "buttons": [{"label": "Set", "endpoint": "/camera/cooler/temperature/setpoint", "method": "PUT"}],
                "buttons": [{"label": "Set", "endpoint": "/scicam1/cooler/temperature/setpoint", "method": "PUT"}],
                "info_list": [
                    {"label": "fan", "status": "camera-settings-fan",         "display": "pill", "map": "bool_yesno"},
                    {"label": "set", "status": "camera-settings-setpoint",    "transform": "round_1"},
                    {"label": "T",   "status": "camera-settings-temperature", "transform": "round_1"}
                    {"label": "fan", "status": "scicam1-settings-fan",         "display": "pill", "map": "bool_yesno"},
                    {"label": "set", "status": "scicam1-settings-setpoint",    "transform": "round_1"},
                    {"label": "T",   "status": "scicam1-settings-temperature", "transform": "round_1"}
                ]
            }) }}
        </section>
@@ -219,21 +219,21 @@

            {{ w.widget_standard({
                "label": "Power",
                "info": "camera2-power",
                "info": "scicam2-power",
                "map": "bool_yesno",
                "buttons": [
                    {"label": "On",  "endpoint": "/camera2/power", "val": true,  "method": "PUT"},
                    {"label": "Off", "endpoint": "/camera2/power", "val": false, "method": "PUT"}
                    {"label": "On",  "endpoint": "/scicam2/power", "val": true,  "method": "PUT"},
                    {"label": "Off", "endpoint": "/scicam2/power", "val": false, "method": "PUT"}
                ],
            }) }}

            {{ w.widget_standard({
                "label": "Cooling",
                "info": "camera2-cooler",
                "info": "scicam2-cooler",
                "map": "bool_yesno",
                "buttons": [
                    {"label": "On",  "endpoint": "/camera2/cooler", "val": true,  "method": "PUT"},
                    {"label": "Off", "endpoint": "/camera2/cooler", "val": false, "method": "PUT"}
                    {"label": "On",  "endpoint": "/scicam2/cooler", "val": true,  "method": "PUT"},
                    {"label": "Off", "endpoint": "/scicam2/cooler", "val": false, "method": "PUT"}
                ],
            }) }}

@@ -241,11 +241,11 @@
                "label": "Temp_2",
                "inputs": [{"value": -10}],
                "unit": "°C",
                "buttons": [{"label": "Set", "endpoint": "/camera2/cooler/temperature/setpoint", "method": "PUT"}],
                "buttons": [{"label": "Set", "endpoint": "/scicam2/cooler/temperature/setpoint", "method": "PUT"}],
                "info_list": [
                    {"label": null},
                    {"label": "set", "status": "camera2-settings-setpoint", "transform":"round_1"},
                    {"label": "T",   "status": "camera2-settings-temperature", "transform":"round_1"}
                    {"label": "set", "status": "scicam2-settings-setpoint", "transform":"round_1"},
                    {"label": "T",   "status": "scicam2-settings-temperature", "transform":"round_1"}
                ]
            }) }}
        </section>
+13 −13
Original line number Diff line number Diff line
@@ -63,7 +63,7 @@
  panel_id         — unique prefix for element IDs ("imaging" or "spectro")
  cameras          — list of (value, label) pairs; length > 1 renders a radio selector,
                     length == 1 renders a disabled static field
  camera_prefix    — telemetry key prefix ("camera" or "camera2")
  camera_prefix    — telemetry key prefix ("scicam1" or "scicam2")
  has_actuator_select — True: full Tel / AO / AO+offload radio group
                        False: disabled static field showing "Telescope"
  -->
@@ -223,7 +223,7 @@
    <label class="col-md-2 col-form-label">Type</label>
    <div class="col-md">
      <select class="form-select form-select-sm bg-black" name="imagetype"
              data-telemetry-init="camera-imagetype">
              data-telemetry-init="scicam1-imagetype">
        <option value="Light">Light</option>
        <option value="Bias">Bias</option>
        <option value="Flat">Flat</option>
@@ -234,7 +234,7 @@
    <label class="col-md-2 col-form-label">Filter</label>
    <div class="col-md">
      <select class="form-select form-select-sm bg-black" name="filter"
              data-telemetry-init="camera-filter">
              data-telemetry-init="scicam1-filter">
        <option value="SDSS u">SDSS u</option>
        <option value="SDSS g">SDSS g</option>
        <option value="SDSS r">SDSS r</option>
@@ -254,7 +254,7 @@
    <label class="col-md-2 col-form-label">Type</label>
    <div class="col-md">
      <select class="form-select form-select-sm bg-black" name="imagetype"
              data-telemetry-init="camera2-imagetype">
              data-telemetry-init="scicam2-imagetype">
        <option value="Light">Light</option>
        <option value="Bias">Bias</option>
      </select>
@@ -270,7 +270,7 @@
    <label class="col-md-2 col-form-label">Type</label>
    <div class="col-md">
      <select class="form-select form-select-sm bg-black" name="imagetype"
              data-telemetry-init="camera3-imagetype">
              data-telemetry-init="scicam3-imagetype">
        <option value="Light">Light</option>
        <option value="Bias">Bias</option>
        <option value="Flat">Flat</option>
@@ -281,7 +281,7 @@
    <label class="col-md-2 col-form-label">Filter</label>
    <div class="col-md">
      <select class="form-select form-select-sm bg-black" name="filter"
              data-telemetry-init="camera3-filter">
              data-telemetry-init="scicam3-filter">
        <option value="FREE">Free</option>
      </select>
    </div>
@@ -295,7 +295,7 @@
  widget_frame(camera_id, camera_prefix, frames, binning_max)

  camera_id     — form ID prefix ("imaging" or "spectro")
  camera_prefix — telemetry key prefix ("camera" or "camera2")
  camera_prefix — telemetry key prefix ("scicam1" or "scicam2")
  frames        — list of (label, value) pairs; value = preset name for the API endpoint
  binning_max   — highest binning integer (generates 1×1 … N×N options)
  -->
@@ -434,22 +434,22 @@

{# combo1 — station1 — visible by default #}
<div data-combo-view="combo1">
{% call camera_panel('ctrl-combo1-sci', 'station1', 'scicam', false, api_base, layout='stacked') %}
    {{ camera_panel('ctrl-combo1-tec', 'station1', 'teccam', true, api_base, layout='compact') }}
{% call camera_panel('ctrl-combo1-sci', 'scicam1', false, api_base, layout='stacked') %}
    {{ camera_panel('ctrl-combo1-tec', 'teccam1', true, api_base, layout='compact') }}
{% endcall %}
</div>

{# combo2 — station2 — hidden #}
<div data-combo-view="combo2" class="d-none">
{% call camera_panel('ctrl-combo2-sci', 'station2', 'scicam', false, api_base, layout='stacked') %}
    {{ camera_panel('ctrl-combo2-tec', 'station2', 'teccam', true, api_base, layout='compact') }}
{% call camera_panel('ctrl-combo2-sci', 'scicam2', false, api_base, layout='stacked') %}
    {{ camera_panel('ctrl-combo2-tec', 'teccam2', true, api_base, layout='compact') }}
{% endcall %}
</div>

{# combo3 — station3 — hidden #}
<div data-combo-view="combo3" class="d-none">
{% call camera_panel('ctrl-combo3-sci', 'station3', 'scicam', false, api_base, layout='stacked') %}
    {{ camera_panel('ctrl-combo3-tec', 'station3', 'teccam', true, api_base, layout='compact') }}
{% call camera_panel('ctrl-combo3-sci', 'scicam3', false, api_base, layout='stacked') %}
    {{ camera_panel('ctrl-combo3-tec', 'teccam3', true, api_base, layout='compact') }}
{% endcall %}
</div>
{% endmacro %}
+2 −2
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@ import { showToast, setInputState } from './ui.js';

// Map station → { panel id suffix, FITS viewer combo, sequencer template, camera device }
const MODES = {
    station1: { panel: 'imaging', combo: 'combo1', template: 'snapshot_imaging', camera: 'cam'  },
    station1: { panel: 'imaging', combo: 'combo1', template: 'snapshot_imaging', camera: 'cam1' },
    station2: { panel: 'spectro', combo: 'combo2', template: 'snapshot_spectro', camera: 'cam2' },
    station3: { panel: 'echelle', combo: 'combo3', template: null,               camera: null, disabled: true },
};
@@ -150,7 +150,7 @@ document.addEventListener('DOMContentLoaded', () => {

    document.getElementById('btn-imaging-expose')?.addEventListener('click', () => {
        const form = document.getElementById('form-imaging');
        if (form) postExpose('snapshot', 'cam', readForm(form));
        if (form) postExpose('snapshot', 'cam1', readForm(form));
    });

    document.getElementById('btn-spectro-expose')?.addEventListener('click', () => {
+4 −4
Original line number Diff line number Diff line
@@ -12,7 +12,7 @@ import { resolveValue } from './ui-core.js';
// --- Constants ---

/** Camera device names that acquire passively (read FITS from disk, no trigger). */
const PASSIVE_CAMERAS = new Set(['cam', 'cam2']);
const PASSIVE_CAMERAS = new Set(['cam1', 'cam2']);

/**
 * Maps the three-way UI actuator choice to the API params sent to /guider/.
@@ -118,7 +118,7 @@ function onTargetChange(panelId, target) {
function collectParams(panelId) {
    const get = id => document.getElementById(id);

    const camera      = get(`guider-camera-${panelId}`)?.value ?? 'teccam';
    const camera      = get(`guider-camera-${panelId}`)?.value ?? 'tec1';
    const actuatorRaw = get(`guider-actuator-sel-${panelId}`)?.value ?? 'telescope';
    const interval    = parseFloat(get(`guider-interval-val-${panelId}`)?.value) || 5.0;
    const box         = parseInt(get(`guider-box-val-${panelId}`)?.value)        || 256;
@@ -550,8 +550,8 @@ document.addEventListener('DOMContentLoaded', () => {

    document.addEventListener('noctua-telemetry', e => {
        const msg = e.detail;
        if (msg?.name !== 'all-camera') return;
        const exposing = resolveValue(msg.data, 'camera-snapshot-state')?.value === 2;
        if (msg?.name !== 'all-scicam1') return;
        const exposing = resolveValue(msg.data, 'scicam1-snapshot-state')?.value === 2;
        document.querySelectorAll('[id^="guider-exptime-val-"]')
            .forEach(el => { el.disabled = exposing; });
    });
Loading