Commit e9ee88c7 authored by vertighel's avatar vertighel
Browse files

camera.py: rimuovi la pipeline di broadcast in-memory duplicata

_broadcast_loop trasmetteva via WebSocket il PNG generato in memoria
da device.image(), su un task separato dal loop di acquisizione del
device e senza mai scrivere su disco. Ora che mako.py/stx.py scrivono
il FITS ad ogni frame, il broadcast per tec1/2/3 arriva gratis dal
poll mtime generico (image_loop in noctua/web/stream.py), gia' attivo
per tutte le cam_id di cameras.ini.

Loop.post/delete si riducono a un puro toggle di device.looping.
noctua/api/guider.py reimporta questa stessa classe Loop (non e' una
pipeline parallela), quindi il fix si applica anche a teccam1/2/3.
parent 3f228143
Loading
Loading
Loading
Loading
+6 −39
Original line number Diff line number Diff line
@@ -4,38 +4,11 @@

'''REST API for Camera related operations'''

# System modules
import asyncio
import base64

# Third-party modules
from quart import request

# This module imports
from .baseresource import BaseResource, expects
from noctua.config import constants
from noctua.api.sequencer_instance import seq

# Broadcast tasks keyed by device id — one per looping camera
_broadcast_tasks: dict[int, asyncio.Task] = {}


async def _broadcast_loop(device, cam_id):
    """Broadcast PNG frames via WebSocket while device.looping is True."""
    from noctua.web import streamer
    ev = asyncio.get_running_loop()
    try:
        while device.looping:
            png = await ev.run_in_executor(None, device.image)
            if png:
                encoded = base64.b64encode(png).decode('utf-8')
                await streamer.broadcaster.broadcast(
                    'fits-preview',
                    {'cam_id': cam_id, 'png': encoded},
                )
            await asyncio.sleep(0.5)
    except asyncio.CancelledError:
        pass

class FrameBinning(BaseResource):
    """Binning of the camera."""
@@ -243,7 +216,12 @@ class Settings(BaseResource):
        return self.make_response(res)

class Loop(BaseResource):
    """Continuous acquisition loop with WebSocket broadcast."""
    """Continuous acquisition loop.

    Frame persistence and WebSocket broadcast are handled by the device's own
    background loop (writes to disk on every frame) and by the generic
    mtime-poll broadcaster in noctua.web.stream, shared with every camera.
    """

    async def get(self):
        """Return whether the acquisition loop is running."""
@@ -257,23 +235,12 @@ class Loop(BaseResource):
        """
        body     = await self.get_payload()
        exposure = float(body.get('exptime', 1.0))
        cam_id   = request.path.split('/')[2]

        self.dev.loop_exposure = exposure
        self.dev.looping = True

        existing = _broadcast_tasks.pop(id(self.dev), None)
        if existing and not existing.done():
            existing.cancel()
        _broadcast_tasks[id(self.dev)] = asyncio.get_running_loop().create_task(
            _broadcast_loop(self.dev, cam_id)
        )
        return self.make_response({'running': True, 'exposure': exposure})

    async def delete(self):
        """Stop the acquisition loop."""
        self.dev.looping = False
        task = _broadcast_tasks.pop(id(self.dev), None)
        if task and not task.done():
            task.cancel()
        return self.make_response({'running': False})