Commit 01a3a55f authored by vertighel's avatar vertighel
Browse files

Automatic widgets

parent 929e549a
Loading
Loading
Loading
Loading
Loading
+29 −2
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ import importlib
from pathlib import Path

# Third-party modules
from quart import Blueprint, request, abort
from quart import Blueprint, request, abort, jsonify

# Custom modules
from noctua import devices
@@ -63,9 +63,36 @@ def dynamic_import(url_path):
        log.info(f"Route registered: /api{url_path:<40} {full_resource_path:<48} {str(methods):<25}")
        
    except Exception as e:
        log.warning(f"Error loading route: {url_path<40} {e}")
        log.warning(f"Error loading route: {url_path:<40} {e}")
        
# Load all routes from ini
for section in ends.sections():
    if not section.startswith(("/blocks", "/templates", "/sequencer")):
        dynamic_import(section)

        
@api_blueprint.route('/')
async def api_catalog():
    """
    Restituisce l'elenco JSON piatto degli endpoint, dei metodi 
    e dei relativi metadati di input dichiarati tramite il decoratore @expects.
    """
    catalog = []
    
    for url_path, resource_instance in resource_registry.items():
        for m in ["GET", "POST", "PUT", "DELETE"]:
            # Recuperiamo la funzione del metodo (es. get, put, post, delete)
            method_fn = getattr(resource_instance, m.lower(), None)
            
            if method_fn:
                # Estraiamo i metadati del decoratore se presenti, altrimenti None
                schema = getattr(method_fn, "_input_schema", None)
                formatted_endpoint = f"/api/{url_path.strip('/')}/"
                
                catalog.append({
                    "endpoint": formatted_endpoint,
                    "method": m,
                    "expects": schema
                })
                
    return jsonify(catalog)
+19 −0
Original line number Diff line number Diff line
@@ -157,3 +157,22 @@ def register_error_handlers(app):
    @app.errorhandler(424)
    async def failed_dependency(e):
        return jsonify({"error": ["Failed Dependency"], "timestamp": datetime.utcnow().isoformat()}), 424



    
def expects(param_type="single", count=1, unit=None, placeholder=None):
    """Decoratore per dichiarare i requisiti di input dei metodi di
    scrittura (PUT, POST, DELETE).  Salva un dizionario
    '_input_schema' all'interno della funzione decorata.

    """
    def decorator(func):
        func._input_schema = {
            "type": param_type,       # "single", "array", "boolean" o None
            "count": count,           # numero di parametri attesi
            "unit": unit,             # unità di misura (es. "°C", "mm")
            "placeholder": placeholder # valore/i di esempio per l'interfaccia
        }
        return func
    return decorator
+3 −1
Original line number Diff line number Diff line
@@ -5,11 +5,12 @@

# Custom modules
from noctua.config import constants
from .baseresource import BaseResource
from .baseresource import BaseResource, expects

# "/dome/light"
# "/telescope/lamp"
# "/camera/power"

class State(BaseResource):
    '''Manage a switch state.'''

@@ -19,6 +20,7 @@ class State(BaseResource):
        raw = await self.run_blocking(lambda: self.dev.state)
        return self.make_response(constants.on_off.get(raw, "N/A"), raw_data=raw)

    @expects(param_type="boolean")
    async def put(self):
        '''Switch on or off.'''
        
+3 −1
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ from datetime import datetime
# Custom modules
from noctua.config import constants
from noctua.utils.coordinates import to_hms, to_hms_dms, to_radec
from .baseresource import BaseResource
from .baseresource import BaseResource, expects


class Cover(BaseResource):
@@ -115,6 +115,7 @@ class CoordinatesMovement(BaseResource):
class CoordinatesMovementRadec(BaseResource):
    '''Point the telescope in Ra, Dec.'''

    @expects(param_type="array", count=2, unit="hms ±dms / id", placeholder="Polaris")
    async def post(self):
        '''Set new Ra and Dec coordinates.'''
        
@@ -134,6 +135,7 @@ class CoordinatesMovementRadec(BaseResource):
class CoordinatesMovementAltaz(BaseResource):
    '''Point the telescope in Alt, Az.'''

    @expects(param_type="array", count=2, unit="°", placeholder="[45, 180]")
    async def post(self):
        '''Set new Alt and Az coordinates.'''
        
+6 −6
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ def to_radec(string):
                        unit=("hourangle", "deg"),
                        equinox=Time.now())

        return [coor.ra.hourangle, coor.dec.degree]
        return [coor.ra.hourangle.item(), coor.dec.degree.item()]

    except ValueError as e:
        log.warning("Cannot resolve coordinates, trying with name")
@@ -45,7 +45,7 @@ def to_radec(string):
            coorstr = coor.to_string(
                style='hmsdms', sep=' ', precision=1, pad=True)
            log.info(f"Correspond to {coorstr}")
            return [coor.ra.hourangle, coor.dec.degree]
            return [coor.ra.hourangle.item(), coor.dec.degree.item()]
        except NameResolveError as e:
            log.error("Cannot resolve name")
            log.error(e)
@@ -69,7 +69,7 @@ def to_radians(string, equinox=None):
                        unit=("hourangle", "deg"),
                        equinox=equinox)

        return [coor.ra.radian, coor.dec.radian]
        return [coor.ra.radian.item(), coor.dec.radian.item()]

    except ValueError as e:
        log.warning("Cannot resolve coordinates, trying with name")
@@ -77,7 +77,7 @@ def to_radians(string, equinox=None):
        try:
            coor = SkyCoord.from_name(string)
            log.info("Found catalog name")
            return [coor.ra.radian, coor.dec.radian]
            return [coor.ra.radian.item(), coor.dec.radian.item()]
        except NameResolveError as e:
            log.error("Cannot resolve name")
            log.error(e)
@@ -187,7 +187,7 @@ def calculate_offset(string="13 56 44 +27 28 01", header=None):
    log.info(f"Offset in arcsec (web): {dalt.arcsec}, {-daz.arcsec}]")
    log.debug(f"Offset in deg (astelos): {dalt.deg}, {-daz.deg}")

    return [dalt.deg, -daz.deg]
    return [dalt.deg.item(), -daz.deg.item()]


def apply_offset(fits_file=temp_fits, box=100, model="moffat", display=False):
@@ -243,7 +243,7 @@ def apply_offset(fits_file=temp_fits, box=100, model="moffat", display=False):
    wcs = WCS(header)

    coords = wcs.pixel_to_world(true_x, true_y)
    string_coords = to_hms_dms([coords.ra.deg, coords.dec.deg], join=True)
    string_coords = to_hms_dms([coords.ra.deg.item(), coords.dec.deg.item()], join=True)

    log.debug(f"True HMSDMS: {string_coords}")

Loading