Architecture of the TiledWriter Callback#

To ingest Bluesky data into Tiled, TiledWriter callback is subscribed to the Bluesky Run Engine.

Structurally, TiledWriter consists of two main parts: RunNormalizer and _RunWriter.

The former is responsible for converting legacy document schemas to their latest version; this ensures that existing Bluesky code that relies on older versions of the Bluesky Event Model can still function correctly with TiledWriter. For example, while TiledWriter natively supports the modern StreamResource and StreamDatum documents (commonly used in asynchronous plans), the Resource and Datum documents are automatically converted to their modern counterparts prior to being written to the Tiled catalog. The schema normalization is mostly done by renaming and restructuring certain document fields, but subclassing RunNormalizer also allows the user to invoke use-case-specific patches for each type of document and achieve high flexibility.

The simplified flowchart of the RunNormalizer logic is shown below. It illustrates how the input documents (top) are processed and emitted as output documents (bottom) after specific transformations or caching operations.

        flowchart TD
    %% Input documents
    subgraph Input [ ]
        style Input fill:#ffffff,stroke-width:0
        StartIn["Start"]
        DescriptorIn["Descriptor"]
        ResourceIn["Resource"]
        DatumIn["Datum"]
        EventIn["Event"]
        StopIn["Stop"]
    end

    %% Emitted documents
    subgraph Output [ ]
        style Output fill:#ffffff,stroke-width:0
        StartOut["Start"]
        DescriptorOut["Descriptor"]
        EventOut["Event"]
        StreamResourceOut["StreamResource"]
        StreamDatumOut["StreamDatum"]
        StopOut["Stop"]
    end

    %% Processing steps
    StartIn --> P1["start():<br/>patch → emit"]
    P1 --> StartOut

    DescriptorIn --> P2["descriptor():<br/>patch → rename fields →<br/>track internal/external keys → emit"]
    P2 --> DescriptorOut

    ResourceIn --> P3["resource():<br/>patch → convert to StreamResource → cache"]
    P3 --> SResCache[(SRes Cache)]

    DatumIn --> P4["datum():<br/>patch → cache"]
    P4 --> DatumCache[(Datum Cache)]

    EventIn --> P5["event():<br/>patch → split internal/external keys → emit"]
    P5 -->|internal data| EventOut
    P5 -->|external data| P6["convert_datum_to_stream_datum()<br/>move datum_kwargs to parameters on SRes"]
    P6 --> StreamDatumOut
    P6 --> |only before first SDatum| StreamResourceOut

    StopIn --> P7["stop():<br/>patch → flush cached StreamDatum"]
    P7 --> StopOut
    P7 --> StreamDatumOut
    P7 --> |if not emitted<br/>already| StreamResourceOut

    %% Extra connections
    SResCache --> P6
    DatumCache --> P6

    %% Styling
    classDef doc fill:#e0f7fa,stroke:#00796b,stroke-width:1px;
    classDef emit fill:#f1f8e9,stroke:#33691e,stroke-width:1px;
    classDef proc fill:#fff3e0,stroke:#e65100,stroke-width:1px;

    class StartIn,DescriptorIn,ResourceIn,DatumIn,EventIn,StopIn doc;
    class StartOut,DescriptorOut,EventOut,StreamResourceOut,StreamDatumOut,StopOut emit;
    class P1,P2,P3,P4,P5,P6,P7 proc;
    

The second component, _RunWriter, is the callback that directly communicates with the Tiled server. It uses the RunRouter to manage the routing of documents from multiple runs, ensuring that each Bluesky run is handled separately.

Furthermore, TiledWriter implements a backup mechanism that allows saving the documents to a local file system in case the Tiled server is not available or any other error occurs during the writing process. This ensures that no data is lost and the writing can be retried later.