Code source de sigima.io.signal.base
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
"""
Base I/O registry
"""
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
from __future__ import annotations
import abc
import os.path as osp
from typing import Sequence
import numpy as np
from sigima.config import _
from sigima.io.base import BaseIORegistry, FormatBase, UnsupportedFormatOperationError
from sigima.objects.signal import SignalObj, create_signal
from sigima.worker import CallbackWorkerProtocol
class SignalIORegistry(BaseIORegistry):
"""Metaclass for registering signal I/O handler classes"""
REGISTRY_INFO: str = _("Signal I/O formats")
_io_format_instances: list[SignalFormatBase] = []
class SignalFormatBaseMeta(SignalIORegistry, abc.ABCMeta):
"""Mixed metaclass to avoid conflicts"""
[docs]
class SignalFormatBase(abc.ABC, FormatBase, metaclass=SignalFormatBaseMeta):
"""Class representing a signal file type"""
HEADER_KEY = "header"
[docs]
@staticmethod
def create_object(filename: str, index: int | None = None) -> SignalObj:
"""Create empty object
Args:
filename: File name
index: Index of object in file
Returns:
Signal object
"""
name = osp.basename(filename)
if index is not None:
name += f" {index:02d}"
return create_signal(name, metadata={"source": filename})
[docs]
def create_signal(
self, xydata: np.ndarray, filename: str, index: int | None = None
) -> SignalObj:
"""Create signal object from xydata and filename.
Args:
xydata: XY data
filename: File name
index: Index of object in file
Returns:
Signal object
"""
obj = self.create_object(filename, index=index)
obj.set_xydata(xydata[:, 0], xydata[:, index or 1])
return obj
[docs]
def create_signals_from(self, xydata: np.ndarray, filename: str) -> list[SignalObj]:
"""Create signal objects from xydata and filename
Args:
xydata: XY data
filename: File name
Returns:
List of signal objects
"""
assert isinstance(xydata, np.ndarray), "Data type not supported"
assert len(xydata.shape) in (1, 2), "Data not supported"
if len(xydata.shape) == 1:
# 1D data
obj = self.create_object(filename)
obj.set_xydata(np.arange(xydata.size), xydata)
return [obj]
# 2D data: x, y1, y2, ...
# Eventually transpose data:
if xydata.shape[1] > xydata.shape[0]:
xydata = xydata.T
# If only data contains one x and y columns, return single object without index
# in title
if xydata.shape[1] == 2:
return [self.create_signal(xydata, filename)]
objs = []
# Create objects for each y column
for i in range(1, xydata.shape[1]):
objs.append(self.create_signal(xydata, filename, i))
return objs
[docs]
def read(
self, filename: str, worker: CallbackWorkerProtocol | None = None
) -> Sequence[SignalObj]:
"""Read list of signal objects from file
Args:
filename: File name
worker: Callback worker object
Returns:
List of signal objects
"""
xydata = self.read_xydata(filename)
return self.create_signals_from(xydata, filename)
[docs]
def read_xydata(self, filename: str) -> np.ndarray:
"""Read data and metadata from file, write metadata to object, return xydata
Args:
filename: File name
Returns:
XY data
"""
raise UnsupportedFormatOperationError(
f"Reading from {self.info.name} is not supported"
)