Work with Table and Waveform Data#

This guide shows how to use Waveform and Table datatypes for array-based data.

Waveform - Homogeneous Arrays#

Use Waveform for numpy arrays of a single data type (spectra, time series, images).

Basic 1D Waveform#

import numpy as np

from fastcs.attributes import AttrR, AttrRW
from fastcs.controllers import Controller
from fastcs.datatypes import Waveform

class SpectrumController(Controller):
    # 1D array of 1000 float64 values
    spectrum: AttrR[np.ndarray] = AttrR(Waveform(np.float64, shape=(1000,)))

    # Writable waveform
    setpoints: AttrRW[np.ndarray] = AttrRW(Waveform(np.float64, shape=(100,)))

2D Waveform (Images)#

class CameraController(Controller):
    # 2D array for images (max 1024x1024 uint16)
    image: AttrR[np.ndarray] = AttrR(Waveform(np.uint16, shape=(1024, 1024)))

    # Smaller region of interest
    roi: AttrRW[np.ndarray] = AttrRW(Waveform(np.uint16, shape=(256, 256)))

Waveform Parameters#

Parameter

Type

Default

Description

array_dtype

DTypeLike

(required)

Numpy dtype (np.float64, np.int32, etc.)

shape

tuple[int, ...]

(2000,)

Maximum array dimensions

Updating Waveforms#

from fastcs.methods import scan

class SpectrumController(Controller):
    spectrum: AttrR[np.ndarray] = AttrR(Waveform(np.float64, shape=(1000,)))

    @scan(period=0.1)
    async def read_spectrum(self):
        # Get data from device (e.g., numpy array)
        data = await self.device.get_spectrum()

        # Update the attribute
        await self.spectrum.update(data)

Shape Validation#

Waveforms validate that data fits within the declared shape:

wave = Waveform(np.float64, shape=(100,))

# OK - fits within shape
wave.validate(np.array([1.0, 2.0, 3.0]))

# Error - exceeds maximum shape
wave.validate(np.arange(200))  # ValueError: shape (200,) exceeds maximum (100,)

Table - Structured Arrays#

Use Table for tabular data with named columns of different types.

Basic Table#

import numpy as np

from fastcs.attributes import AttrR
from fastcs.controllers import Controller
from fastcs.datatypes import Table

class MeasurementController(Controller):
    # Table with columns: name (string), value (float), valid (bool)
    results: AttrR[np.ndarray] = AttrR(Table([
        ("name", "S32"),       # 32-character string
        ("value", np.float64),
        ("valid", np.bool_),
    ]))

Table Parameters#

Parameter

Type

Description

structured_dtype

list[tuple[str, DTypeLike]]

List of (name, dtype) tuples

Creating Table Data#

from fastcs.attributes import AttrR
from fastcs.controllers import Controller
from fastcs.datatypes import Table

class ChannelController(Controller):
    channel_data: AttrR[np.ndarray] = AttrR(Table([
        ("channel", np.int32),
        ("temperature", np.float64),
        ("status", "S10"),
    ]))

# Create data using numpy structured array
data = np.array([
    (0, 25.5, "OK"),
    (1, 26.2, "OK"),
    (2, 30.1, "WARN"),
], dtype=[("channel", np.int32), ("temperature", np.float64), ("status", "S10")])

# Update the attribute
await controller.channel_data.update(data)

Accessing Table Data#

# Get the table
table = controller.results.get()

# Access by column name
names = table["name"]
values = table["value"]

# Access by row index
first_row = table[0]

# Access specific cell
first_name = table[0]["name"]

Common String Types in Tables#

# Fixed-length byte strings (ASCII)
("name", "S32")      # 32-byte string
("status", "S10")    # 10-byte string

# Unicode strings
("label", "U32")     # 32-character unicode string