Commit 44d728ee authored by vertighel's avatar vertighel
Browse files

html + js code review

parent 19cdb3da
Loading
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -414,8 +414,9 @@

  <div class="col-md">
    <div class="input-group input-group-sm">
      <button class="btn btn-primary flex-fill" type="button"
              id="btn-{{ camera_id }}-expose">Expose</button>
      <button class="btn btn-primary flex-fill btn-universal" type="button"
              id="btn-{{ camera_id }}-expose"
              data-method="POST" data-url="/sequencer/run" data-timeout-ms="0">Expose</button>
      <button class="btn btn-danger btn-universal" type="button"
              id="btn-{{ camera_id }}-expose-stop"
              data-method="DELETE" data-url="/sequencer/run">Stop</button>
+5 −1
Original line number Diff line number Diff line
@@ -21,7 +21,11 @@

<!-- WIDGET: Standard  -->
{% macro widget_standard(config) %}
{% set safe_id = config.label | replace(' ', '-') | lower %}
{# config.info is unique per widget (it's the status key); config.label often
   isn't (e.g. "Power"/"Cooling"/"Parked" repeat across devices) — prefer info
   for the id, falling back to label only for widgets that have none. #}
{% set _id_src = config.info.strip('/') | replace('/', '-') if config.info else config.label %}
{% set safe_id = _id_src | replace(' ', '-') | lower %}
<fieldset class="row mt-1 widget-universal align-items-center" id="input-{{ safe_id }}">
  <label class="col-md-2 col-form-label">
    {{ config.label }}
+3 −3
Original line number Diff line number Diff line
@@ -15,9 +15,9 @@ document.addEventListener('DOMContentLoaded', () => {
        const endpoint = button.dataset.url;
        const safeId = button.dataset.safeId;
        // data-timeout-ms="0" opts out of the client-side abort timeout for
        // endpoints that legitimately block server-side for a long time
        // (e.g. /sequencer/run, which runs synchronously until the whole OB
        // finishes — see control.js's postExpose() for the same exception).
        // endpoints that legitimately keep a request open for a long time
        // (e.g. /sequencer/run, which doesn't respond until the whole OB
        // finishes — used by both the sequencer's RUN button and Expose).
        const noTimeout = button.dataset.timeoutMs === '0';

        await withBusyButton(button, async () => {
+20 −41
Original line number Diff line number Diff line
// control.js — Control page: mode switching, stage movement, expose dispatch.

import { showToast, setInputState, withBusyButton, fetchWithTimeout, extractErrorMessage } from './ui.js';
import { setInputState, withBusyButton, fetchWithTimeout } from './ui.js';
import { wireLoopToggle } from './loop-toggle.js';

// Map station → { panel id suffix, FITS viewer combo, sequencer template,
@@ -161,51 +161,29 @@ document.addEventListener('DOMContentLoaded', () => {
    }

    /**
     * Starts an exposure via POST /api/sequencer/run.
     * Keeps a station's Expose button's data-payload in sync with its
     * form, read right before the shared .btn-universal click handler
     * (actions.js) picks it up and POSTs to /api/sequencer/run — the
     * same declarative data-timeout-ms="0" path as the sequencer's RUN
     * button, since both hit this endpoint for the same reason (see
     * actions.js's noTimeout comment).
     *
     * @param {HTMLButtonElement} btn - the station's Expose button.
     * @param {string} template - template name (e.g. "snapshot").
     * @param {string} camera - device name (e.g. "cam1").
     * @param {object} params - readForm()'s output.
     * @param {string} station - key into MODES (e.g. "station1").
     */
    async function postExpose(btn, template, camera, params) {
        await withBusyButton(btn, async () => {
            try {
                // No fetchWithTimeout here: /api/sequencer/run blocks server-side
                // until the whole OB finishes executing (sequencer.py's execute()
                // runs every template synchronously), which for a real exposure
                // can legitimately take far longer than the ~12-15s timeout used
                // everywhere else — aborting this one would kill real exposures.
                const res  = await fetch('/api/sequencer/run', {
                    method:  'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body:    JSON.stringify({ template, params: { camera, ...params } }),
    function wireExpose(station) {
        const mode = MODES[station];
        const btn  = document.getElementById(`btn-${mode.panel}-expose`);
        const form = document.getElementById(`form-${mode.panel}`);
        if (!btn || !form) return;

        btn.addEventListener('click', () => {
            btn.dataset.payload = JSON.stringify({
                template: mode.template,
                params: { camera: mode.camera, ...readForm(form) },
            });
                if (!res.ok) {
                    const detail = await extractErrorMessage(res, `HTTP ${res.status}`);
                    showToast(`Sequencer error: ${detail}`, 'danger');
                }
            } catch {
                showToast('Sequencer POST failed', 'danger');
            }
        });
    }

    document.getElementById('btn-imaging-expose')?.addEventListener('click', (e) => {
        const form = document.getElementById('form-imaging');
        if (form) postExpose(e.currentTarget, MODES.station1.template, MODES.station1.camera, readForm(form));
    });

    document.getElementById('btn-spectro-expose')?.addEventListener('click', (e) => {
        const form = document.getElementById('form-spectro');
        if (form) postExpose(e.currentTarget, MODES.station2.template, MODES.station2.camera, readForm(form));
    });

    document.getElementById('btn-echelle-expose')?.addEventListener('click', (e) => {
        const form = document.getElementById('form-echelle');
        if (form) postExpose(e.currentTarget, MODES.station3.template, MODES.station3.camera, readForm(form));
    });

    // Loop switches — one per camera in the whole page, each the sole owner
    // of that camera's continuous acquisition:
    //   - scicam's switch (in the Expose widget) additionally gates Expose/
@@ -268,6 +246,7 @@ document.addEventListener('DOMContentLoaded', () => {
    }

    for (const station of Object.keys(MODES)) {
        wireExpose(station);
        wireScicamLoop(station);
        wireTeccamLoop(station);
    }