From 0fc661cd6d8fc0d528c905425d4d41e42a320fd7 Mon Sep 17 00:00:00 2001 From: hedi-tej <222785240+hedi-tej@users.noreply.github.com> Date: Wed, 10 Jun 2026 14:43:07 +0200 Subject: [PATCH 1/4] docs: add architecture guide to developer docs --- docs/source/dev/architecture.md | 745 ++++++++++++++++++++++++++++++++ docs/source/dev/index.rst | 1 + 2 files changed, 746 insertions(+) create mode 100644 docs/source/dev/architecture.md diff --git a/docs/source/dev/architecture.md b/docs/source/dev/architecture.md new file mode 100644 index 0000000000..6f863573db --- /dev/null +++ b/docs/source/dev/architecture.md @@ -0,0 +1,745 @@ +# MXCuBE Architecture + +**MXCuBE** (Macromolecular Xtallography Customized Beamline Environment) is a modular framework for controlling synchrotron beamlines for macromolecular crystallography experiments. + +## Table of Contents + +- [System Overview](#system-overview) +- [High-Level Architecture](#high-level-architecture) +- [MXCubeCore Architecture](#mxcubecore-architecture) +- [MXCubeWeb Architecture](#mxcubeweb-architecture) +- [Communication Flow](#communication-flow) +- [Data Collection Workflow](#data-collection-workflow) +- [Key Components](#key-components) + +--- + +## System Overview + +MXCuBE is split into two main repositories: + +- **mxcubecore**: Hardware abstraction layer and core business logic +- **mxcubeweb**: Web-based user interface and REST/WebSocket API server + +```mermaid +graph TB + subgraph "User Layer" + Browser[Web Browser] + end + + subgraph "Application Layer - mxcubeweb" + UI[React Frontend] + API[FastAPI Backend] + WS[WebSocket Server] + end + + subgraph "Hardware Abstraction Layer - mxcubecore" + HWR[Hardware Repository] + HWO[Hardware Objects] + QM[Queue Manager] + end + + subgraph "Control Systems" + TANGO[TANGO] + EPICS[EPICS] + SPEC[SPEC] + end + + subgraph "Physical Hardware" + Motors[Motors/Stages] + Detectors[Detectors] + Cameras[Cameras] + SC[Sample Changer] + end + + Browser <-->|HTTP/WS| UI + UI <-->|REST/Socket.IO| API + API <-->|Python API| HWR + API <-->|Events| WS + WS <-->|Socket.IO| UI + HWR --> HWO + HWO -->|Commands/Channels| TANGO + HWO -->|Commands/Channels| EPICS + HWO -->|Commands/Channels| SPEC + TANGO --> Motors + TANGO --> Detectors + EPICS --> Cameras + SPEC --> SC + + style Browser fill:#e1f5ff + style UI fill:#b3e5fc + style API fill:#81d4fa + style HWR fill:#ffcc80 + style HWO fill:#ffb74d + style TANGO fill:#c5e1a5 + style Motors fill:#f48fb1 +``` + +--- + +## High-Level Architecture + +### Three-Tier Architecture + +```mermaid +graph LR + subgraph "Presentation Tier" + A[React UI
Redux Store
Socket.IO Client] + end + + subgraph "Application Tier" + B[Flask/FastAPI Server
WebSocket Server
Business Logic
Queue Manager] + end + + subgraph "Hardware Tier" + C[Hardware Repository
Hardware Objects
Control Protocols
Device Drivers] + end + + A <-->|REST API
WebSocket| B + B <-->|Python API
Events| C + + style A fill:#bbdefb + style B fill:#ffccbc + style C fill:#c5e1a5 +``` + +--- + +## MXCubeCore Architecture + +### Core Component Hierarchy + +```mermaid +graph TD + subgraph "mxcubecore/" + HWR[HardwareRepository
Singleton Registry] + + subgraph "Hardware Objects" + BL[Beamline
Container Object] + HWO[Hardware Objects] + Abstract[Abstract Base Classes] + Concrete[Concrete Implementations] + end + + subgraph "Queue System" + QM[QueueManager] + QModel[QueueModel
Tree Structure] + QEntry[QueueEntry
Executors] + end + + subgraph "Data Models" + DMO[Queue Model Objects] + Config[Configuration Models] + Lims[LIMS Session Models] + end + + subgraph "Communication" + Dispatcher[Event Dispatcher] + Commands[Command Objects] + Channels[Channel Objects] + end + end + + HWR -->|manages| HWO + HWR -->|provides| BL + BL -->|contains| HWO + Abstract -.->|inheritance| Concrete + HWO -->|uses| Commands + HWO -->|uses| Channels + HWO -->|emits| Dispatcher + QM -->|manages| QModel + QModel -->|contains| QEntry + QEntry -->|executes| HWO + QEntry -->|uses| DMO + + style HWR fill:#ff9800 + style BL fill:#ffc107 + style QM fill:#8bc34a + style DMO fill:#64b5f6 + style Dispatcher fill:#ba68c8 +``` + +### Hardware Object Base Classes + +```mermaid +classDiagram + class HardwareObject { + +String name + +State state + +init() + +abort() + +stop() + +get_state() + +emit(signal, args) + +connect(sender, signal, slot) + } + + class AbstractActuator { + +get_value() + +set_value(value) + +get_limits() + +validate_value(value) + +update_value() + } + + class AbstractMotor { + +get_position() + +set_position(position) + +get_velocity() + +home() + } + + class AbstractSampleChanger { + +load_sample(sample) + +unload_sample() + +get_loaded_sample() + +get_sample_list() + } + + class AbstractCollect { + +collect(params) + +prepare_collection() + +data_collection_hook() + } + + class Beamline { + +diffractometer + +sample_changer + +detector + +energy + +collect + } + + HardwareObject <|-- AbstractActuator + AbstractActuator <|-- AbstractMotor + HardwareObject <|-- AbstractSampleChanger + HardwareObject <|-- AbstractCollect + HardwareObject <|-- Beamline +``` + +### Hardware Repository Pattern + +```mermaid +sequenceDiagram + participant App as Application + participant HWR as HardwareRepository + participant Parser as XMLParser + participant HWO as HardwareObject + participant Device as Control System + + App->>HWR: init_hardware_repository(path) + HWR->>HWR: scan XML/YAML files + + App->>HWR: get_hardware_object("motor") + HWR->>Parser: parse config file + Parser->>HWR: return class & properties + HWR->>HWO: instantiate & configure + HWO->>Device: connect to control system + HWO-->>HWR: return initialized object + HWR-->>App: return HardwareObject + + App->>HWO: set_value(10.5) + HWO->>Device: write command + Device-->>HWO: value changed + HWO->>HWO: emit('valueChanged') +``` + +### Queue Execution Model + +```mermaid +graph TD + subgraph "Queue Structure" + Root[Queue Root] + S1[Sample 1] + S2[Sample 2] + G1[TaskGroup] + G2[TaskGroup] + DC1[DataCollection] + DC2[DataCollection] + Char[Characterisation] + end + + subgraph "Queue Entries" + SEntry[SampleQueueEntry] + GEntry[TaskGroupQueueEntry] + DCEntry[DataCollectionQueueEntry] + CharEntry[CharacterisationQueueEntry] + end + + subgraph "Execution" + QM[QueueManager] + Executor[Entry Executor] + Collect[Beamline.collect] + end + + Root --> S1 + Root --> S2 + S1 --> G1 + S2 --> G2 + G1 --> DC1 + G1 --> Char + G2 --> DC2 + + S1 -.->|model| SEntry + G1 -.->|model| GEntry + DC1 -.->|model| DCEntry + + QM -->|traverse tree| SEntry + SEntry -->|execute children| GEntry + GEntry -->|execute children| DCEntry + DCEntry -->|collect data| Collect + + style Root fill:#ffeb3b + style S1 fill:#4caf50 + style G1 fill:#03a9f4 + style DC1 fill:#f44336 + style QM fill:#9c27b0 +``` + +--- + +## MXCubeWeb Architecture + +### Backend Architecture + +```mermaid +graph TB + Entry[mxcubeweb-server
Entry Point] + + Flask[Flask Application] + SocketIO[Flask-SocketIO] + Auth[Flask-Security] + Limiter[Rate Limiter] + + MainRoute[Main API Route
/mxcube/api/v0.1] + QueueRoute[Queue Routes] + LoginRoute[Login Routes] + RARoute[Remote Access] + WorkflowRoute[Workflow Routes] + HarvesterRoute[Harvester Routes] + + MXApp[MXCUBEApplication] + Queue[Queue Component] + Lims[LIMS Component] + UserMgr[User Manager] + Chat[Chat Component] + Workflow[Workflow Component] + Harvester[Harvester Component] + + AdapterMgr[HardwareObjectAdapterManager] + HWR[mxcubecore.HWR] + + StateStorage[UI State Storage] + UserDB[User Database] + + Entry --> Flask + Flask --> SocketIO + Flask --> Auth + Flask --> Limiter + Flask --> MainRoute + + MainRoute --> QueueRoute + MainRoute --> LoginRoute + MainRoute --> RARoute + MainRoute --> WorkflowRoute + MainRoute --> HarvesterRoute + + QueueRoute --> Queue + LoginRoute --> UserMgr + RARoute --> UserMgr + RARoute --> Chat + WorkflowRoute --> Workflow + HarvesterRoute --> Harvester + + MXApp --> Queue + MXApp --> Lims + MXApp --> UserMgr + MXApp --> Chat + MXApp --> Workflow + MXApp --> Harvester + MXApp --> AdapterMgr + + AdapterMgr --> HWR + Auth --> UserDB + SocketIO --> StateStorage + + style Entry fill:#ff9800 + style Flask fill:#2196f3 + style MXApp fill:#4caf50 + style AdapterMgr fill:#9c27b0 + style HWR fill:#ff5722 +``` + +### Frontend Architecture + +```mermaid +flowchart TB + Index[index.jsx Entry Point] + + subgraph State["State Management"] + Store[Redux Store] + Reducers[Reducers] + Actions[Action Creators] + end + + subgraph Components["Components"] + Login[Login] + SampleGrid[Sample Grid] + SampleQueue[Sample Queue] + SampleView[Sample View Video Stream] + TaskForms[Task Forms] + Equipment[Equipment Panel] + Logger[Logger] + RemoteAccess[Remote Access] + end + + subgraph Containers["Containers"] + SGContainer[SampleListViewContainer] + SQContainer[SampleQueueContainer] + SVContainer[SampleViewContainer] + end + + subgraph Communication["Communication"] + API[REST API Client wretch] + ServerIO[Socket.IO Client] + end + + Backend[Flask/SocketIO Server] + + Index --> Store + Store --> Reducers + Actions --> Reducers + + Index --> SGContainer + Index --> SQContainer + Index --> SVContainer + + SGContainer --> SampleGrid + SQContainer --> SampleQueue + SVContainer --> SampleView + + SampleGrid --> Actions + SampleQueue --> Actions + TaskForms --> Actions + + Actions --> API + Actions --> ServerIO + + API -->|REST| Backend + ServerIO -->|WebSocket| Backend + Backend -->|Events| ServerIO + + ServerIO --> Actions + Actions --> Store + + style Index fill:#ff9800 + style Store fill:#4caf50 + style API fill:#2196f3 + style ServerIO fill:#9c27b0 + style Backend fill:#f44336 +``` + +### Component Communication + +```mermaid +sequenceDiagram + participant UI as React Component + participant Action as Action Creator + participant API as API Client + participant Server as Flask Server + participant Queue as Queue Component + participant HWR as Hardware Repository + participant HWO as Hardware Object + + UI->>Action: User clicks "Start Queue" + Action->>API: sendStartQueue() + API->>Server: PUT /queue/start + Server->>Queue: queue_start() + Queue->>HWR: beamline.queue_manager + HWR->>HWO: execute queue entries + HWO->>HWO: collect data + HWO->>HWR: emit signals + HWR->>Queue: queue progress + Queue->>Server: emit('queue', state) + Server->>UI: WebSocket event + UI->>Action: dispatch(setStatus) + Action->>UI: update Redux store +``` + +--- + +## Communication Flow + +### REST API Flow + +```mermaid +sequenceDiagram + participant Browser + participant Redux + participant APIClient + participant Flask + participant Component + participant MXCubeCore + + Browser->>Redux: User Action + Redux->>APIClient: api.get('/queue') + APIClient->>Flask: HTTP GET /mxcube/api/v0.1/queue + Flask->>Flask: @restrict (auth check) + Flask->>Component: app.queue.queue_to_dict() + Component->>MXCubeCore: HWR.beamline.queue_manager + MXCubeCore-->>Component: queue data + Component-->>Flask: JSON response + Flask-->>APIClient: 200 OK + JSON + APIClient-->>Redux: dispatch(setQueue) + Redux-->>Browser: UI Update +``` + +### WebSocket Event Flow + +```mermaid +sequenceDiagram + participant HWO as Hardware Object + participant Signal as Signal Handler + participant SocketIO as Flask-SocketIO + participant Client as Browser Client + participant Redux as Redux Store + + HWO->>HWO: state changes + HWO->>Signal: emit('stateChanged', state) + Signal->>SocketIO: server.emit('hwobj_state', data) + SocketIO->>Client: WebSocket message + Client->>Redux: dispatch(updateHWObjState) + Redux->>Client: re-render UI + + Note over HWO,Redux: Real-time updates without polling +``` + +### Data Collection Signal Flow + +```mermaid +graph LR + subgraph "Hardware Layer" + Det[Detector] + Motor[Goniometer] + end + + subgraph "Control Layer" + Collect[CollectHWObj] + DC[DataCollectionEntry] + end + + subgraph "Queue Layer" + QM[QueueManager] + QE[QueueEntry] + end + + subgraph "Application Layer" + QComp[Queue Component] + Signal[Signal Handler] + end + + subgraph "Frontend" + Socket[Socket.IO] + UI[React UI] + end + + Det -->|frames collected| Collect + Motor -->|position reached| Collect + Collect -->|progress| DC + DC -->|status| QE + QE -->|emit| QM + QM -->|queue_entry_execute_started| QComp + QComp -->|format message| Signal + Signal -->|emit event| Socket + Socket -->|WebSocket| UI + + style Det fill:#c5e1a5 + style Collect fill:#ffcc80 + style QM fill:#81d4fa + style Signal fill:#ce93d8 + style UI fill:#90caf9 +``` + +--- + +## Data Collection Workflow + +### End-to-End Data Collection + +```mermaid +sequenceDiagram + participant User + participant UI as React UI + participant Queue as Queue Component + participant QM as QueueManager + participant SC as Sample Changer + participant Diff as Diffractometer + participant Collect as Collect HWObj + participant Det as Detector + participant LIMS + + User->>UI: Add sample + tasks + UI->>Queue: addTask() + Queue->>QM: add to queue model + + User->>UI: Click "Start Queue" + UI->>Queue: startQueue() + Queue->>QM: execute() + + QM->>SC: load_sample(location) + SC-->>QM: sample mounted + + QM->>Diff: center_sample() + Diff-->>QM: centred position + + QM->>Collect: collect(parameters) + Collect->>Diff: move to start position + Collect->>Det: prepare acquisition + Collect->>Det: start_acquisition() + + loop For each image + Det->>Det: collect frame + Det->>Collect: emit('imageCollected') + Collect->>QM: progress update + QM->>UI: WebSocket update + end + + Det-->>Collect: acquisition complete + Collect->>LIMS: store metadata + Collect-->>QM: collection finished + QM->>UI: emit('queue_entry_finished') + + QM->>SC: unload_sample() + SC-->>QM: sample unmounted +``` + +### Queue Tree Execution + +```mermaid +stateDiagram-v2 + [*] --> QueueIdle + QueueIdle --> Executing: start_queue() + + state Executing { + [*] --> MountSample + MountSample --> CenterSample + CenterSample --> TaskGroup + + state TaskGroup { + [*] --> DataCollection + DataCollection --> Characterisation + Characterisation --> [*] + } + + TaskGroup --> UnmountSample + UnmountSample --> NextSample + NextSample --> MountSample: more samples + NextSample --> [*]: done + } + + Executing --> QueueIdle: finished + Executing --> Paused: pause() + Paused --> Executing: resume() + Executing --> Stopped: stop() + Stopped --> QueueIdle +``` + +--- + +## Key Components + +### 1. Hardware Repository (mxcubecore) + +**Purpose**: Central registry and factory for hardware objects + +**Key Features**: +- Singleton pattern for global access (`HWR.beamline.*`) +- Dynamic loading from XML/YAML configurations +- Lazy initialization of hardware objects +- Event-driven communication via dispatcher + +**Example**: +```python +from mxcubecore import HardwareRepository as HWR + +# Access hardware objects +HWR.beamline.diffractometer.move_motors({'omega': 90}) +HWR.beamline.detector.prepare_acquisition() +``` + +### 2. Hardware Objects + +**Purpose**: Abstract hardware control behind unified interfaces + +**Key Base Classes**: +- `HardwareObject`: Base class with state management +- `AbstractActuator`: Values and limits (motors, energy, etc.) +- `AbstractSampleChanger`: Sample loading/unloading +- `AbstractCollect`: Data collection orchestration + +**Control System Adapters**: +- TANGO (Tango Controls) +- EPICS (Experimental Physics and Industrial Control System) +- SPEC (Python wrapper for Spec diffractometer control) + +### 3. Queue System + +**Purpose**: Manage and execute data collection workflows + +**Components**: +- `QueueModel`: Tree structure of samples and tasks +- `QueueEntry`: Executable nodes with `execute()` method +- `QueueManager`: Traversal and execution engine + +**Queue Entry Types**: +- `SampleQueueEntry`: Mount/unmount operations +- `DataCollectionQueueEntry`: Standard data collection +- `CharacterisationQueueEntry`: Crystal characterisation +- `WorkflowQueueEntry`: Multi-step workflows +- `TaskGroupQueueEntry`: Group related tasks + +### 4. MXCUBEApplication (mxcubeweb) + +**Purpose**: Application-level business logic and state + +**Components**: +- `Queue`: Queue management API +- `Lims`: LIMS integration (ISPyB, etc.) +- `UserManager`: Authentication and authorization +- `Chat`: Remote access collaboration +- `Workflow`: Advanced workflows (GPhL, etc.) +- `Harvester`: Crystal harvester integration + +### 5. Signal System + +**Purpose**: Decouple components via event-driven architecture + +**Pattern**: +```python +# Hardware object emits signal +self.emit('valueChanged', new_value) + +# Application listens +HWR.beamline.motor.connect('valueChanged', callback) +``` + +**Signal Flow**: +Hardware Object → Dispatcher → Signal Handler → Flask-SocketIO → Browser + +### 6. React Frontend + +**Purpose**: Modern web UI with real-time updates + +**Stack**: +- React 18 + Redux for state management +- Socket.IO for real-time communication +- Vite for fast builds +- Bootstrap for styling + +**Key Containers**: +- `SampleListViewContainer`: Sample management and LIMS +- `SampleQueueContainer`: Queue visualization and control +- `SampleViewContainer`: Video stream and sample centering +- `TaskParametersContainer`: Data collection forms + +--- diff --git a/docs/source/dev/index.rst b/docs/source/dev/index.rst index 7041a5bca1..7a88ea611d 100644 --- a/docs/source/dev/index.rst +++ b/docs/source/dev/index.rst @@ -17,6 +17,7 @@ Developer documentation configuration_files commands_channels queue + architecture abstract_classes api json-schema-generated-user-interface From a3ec27634b001990db9984f935255b3751bfaa30 Mon Sep 17 00:00:00 2001 From: hedi-tej <222785240+hedi-tej@users.noreply.github.com> Date: Fri, 12 Jun 2026 11:16:31 +0200 Subject: [PATCH 2/4] docs: enable Mermaid fenced blocks in Sphinx --- docs/conf.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index a9c43d0b4e..7db828d012 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -109,6 +109,12 @@ "fieldlist", ) +# Allow GitHub-style fenced blocks like ```mermaid to be parsed as +# ``{mermaid}`` directives so diagrams render in Sphinx/RTD. +myst_fence_as_directive = [ + "mermaid", +] + # -- Options for sphinxcontrib.mermaid # https://pypi.org/project/sphinxcontrib-mermaid/ From 9e3c1f87531196b1b9c276851d0500281f51ffd0 Mon Sep 17 00:00:00 2001 From: hedi-tej <222785240+hedi-tej@users.noreply.github.com> Date: Fri, 12 Jun 2026 11:51:37 +0200 Subject: [PATCH 3/4] docs: replace FastAPI mentions with Flask in architecture --- docs/source/dev/architecture.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/dev/architecture.md b/docs/source/dev/architecture.md index 6f863573db..0c12bac303 100644 --- a/docs/source/dev/architecture.md +++ b/docs/source/dev/architecture.md @@ -29,7 +29,7 @@ graph TB subgraph "Application Layer - mxcubeweb" UI[React Frontend] - API[FastAPI Backend] + API[Flask Backend] WS[WebSocket Server] end @@ -88,7 +88,7 @@ graph LR end subgraph "Application Tier" - B[Flask/FastAPI Server
WebSocket Server
Business Logic
Queue Manager] + B[Flask Server
WebSocket Server
Business Logic
Queue Manager] end subgraph "Hardware Tier" From cf55aa93add965b3d3df42616a1b344919584708 Mon Sep 17 00:00:00 2001 From: hedi-tej <222785240+hedi-tej@users.noreply.github.com> Date: Fri, 3 Jul 2026 15:58:13 +0200 Subject: [PATCH 4/4] docs: refine architecture guide after PR review --- docs/source/dev/architecture.md | 191 +++++++++++++++++--------------- 1 file changed, 100 insertions(+), 91 deletions(-) diff --git a/docs/source/dev/architecture.md b/docs/source/dev/architecture.md index 0c12bac303..3d55ff939f 100644 --- a/docs/source/dev/architecture.md +++ b/docs/source/dev/architecture.md @@ -2,6 +2,8 @@ **MXCuBE** (Macromolecular Xtallography Customized Beamline Environment) is a modular framework for controlling synchrotron beamlines for macromolecular crystallography experiments. +This documentation was partly generated automatically and is meant to provide an architectural overview of the concepts involved. It may miss implementation details. + ## Table of Contents - [System Overview](#system-overview) @@ -12,7 +14,7 @@ - [Data Collection Workflow](#data-collection-workflow) - [Key Components](#key-components) ---- +______________________________________________________________________ ## System Overview @@ -26,37 +28,34 @@ graph TB subgraph "User Layer" Browser[Web Browser] end - + subgraph "Application Layer - mxcubeweb" UI[React Frontend] - API[Flask Backend] - WS[WebSocket Server] + API[Flask Backend
WebSocket Server] end - + subgraph "Hardware Abstraction Layer - mxcubecore" HWR[Hardware Repository] HWO[Hardware Objects] QM[Queue Manager] end - + subgraph "Control Systems" TANGO[TANGO] EPICS[EPICS] SPEC[SPEC] end - + subgraph "Physical Hardware" Motors[Motors/Stages] Detectors[Detectors] Cameras[Cameras] SC[Sample Changer] end - + Browser <-->|HTTP/WS| UI UI <-->|REST/Socket.IO| API API <-->|Python API| HWR - API <-->|Events| WS - WS <-->|Socket.IO| UI HWR --> HWO HWO -->|Commands/Channels| TANGO HWO -->|Commands/Channels| EPICS @@ -75,7 +74,7 @@ graph TB style Motors fill:#f48fb1 ``` ---- +______________________________________________________________________ ## High-Level Architecture @@ -86,24 +85,24 @@ graph LR subgraph "Presentation Tier" A[React UI
Redux Store
Socket.IO Client] end - + subgraph "Application Tier" B[Flask Server
WebSocket Server
Business Logic
Queue Manager] end - + subgraph "Hardware Tier" C[Hardware Repository
Hardware Objects
Control Protocols
Device Drivers] end - + A <-->|REST API
WebSocket| B B <-->|Python API
Events| C - + style A fill:#bbdefb style B fill:#ffccbc style C fill:#c5e1a5 ``` ---- +______________________________________________________________________ ## MXCubeCore Architecture @@ -113,33 +112,33 @@ graph LR graph TD subgraph "mxcubecore/" HWR[HardwareRepository
Singleton Registry] - + subgraph "Hardware Objects" BL[Beamline
Container Object] HWO[Hardware Objects] Abstract[Abstract Base Classes] Concrete[Concrete Implementations] end - + subgraph "Queue System" QM[QueueManager] QModel[QueueModel
Tree Structure] QEntry[QueueEntry
Executors] end - + subgraph "Data Models" DMO[Queue Model Objects] Config[Configuration Models] Lims[LIMS Session Models] end - + subgraph "Communication" Dispatcher[Event Dispatcher] Commands[Command Objects] Channels[Channel Objects] end end - + HWR -->|manages| HWO HWR -->|provides| BL BL -->|contains| HWO @@ -151,7 +150,7 @@ graph TD QModel -->|contains| QEntry QEntry -->|executes| HWO QEntry -->|uses| DMO - + style HWR fill:#ff9800 style BL fill:#ffc107 style QM fill:#8bc34a @@ -173,7 +172,7 @@ classDiagram +emit(signal, args) +connect(sender, signal, slot) } - + class AbstractActuator { +get_value() +set_value(value) @@ -181,27 +180,27 @@ classDiagram +validate_value(value) +update_value() } - + class AbstractMotor { +get_position() +set_position(position) +get_velocity() +home() } - + class AbstractSampleChanger { +load_sample(sample) +unload_sample() +get_loaded_sample() +get_sample_list() } - + class AbstractCollect { +collect(params) +prepare_collection() +data_collection_hook() } - + class Beamline { +diffractometer +sample_changer @@ -209,7 +208,7 @@ classDiagram +energy +collect } - + HardwareObject <|-- AbstractActuator AbstractActuator <|-- AbstractMotor HardwareObject <|-- AbstractSampleChanger @@ -226,10 +225,10 @@ sequenceDiagram participant Parser as XMLParser participant HWO as HardwareObject participant Device as Control System - + App->>HWR: init_hardware_repository(path) HWR->>HWR: scan XML/YAML files - + App->>HWR: get_hardware_object("motor") HWR->>Parser: parse config file Parser->>HWR: return class & properties @@ -237,7 +236,7 @@ sequenceDiagram HWO->>Device: connect to control system HWO-->>HWR: return initialized object HWR-->>App: return HardwareObject - + App->>HWO: set_value(10.5) HWO->>Device: write command Device-->>HWO: value changed @@ -258,20 +257,20 @@ graph TD DC2[DataCollection] Char[Characterisation] end - + subgraph "Queue Entries" SEntry[SampleQueueEntry] GEntry[TaskGroupQueueEntry] DCEntry[DataCollectionQueueEntry] CharEntry[CharacterisationQueueEntry] end - + subgraph "Execution" QM[QueueManager] Executor[Entry Executor] Collect[Beamline.collect] end - + Root --> S1 Root --> S2 S1 --> G1 @@ -279,16 +278,16 @@ graph TD G1 --> DC1 G1 --> Char G2 --> DC2 - + S1 -.->|model| SEntry G1 -.->|model| GEntry DC1 -.->|model| DCEntry - + QM -->|traverse tree| SEntry SEntry -->|execute children| GEntry GEntry -->|execute children| DCEntry DCEntry -->|collect data| Collect - + style Root fill:#ffeb3b style S1 fill:#4caf50 style G1 fill:#03a9f4 @@ -296,7 +295,7 @@ graph TD style QM fill:#9c27b0 ``` ---- +______________________________________________________________________ ## MXCubeWeb Architecture @@ -305,19 +304,19 @@ graph TD ```mermaid graph TB Entry[mxcubeweb-server
Entry Point] - + Flask[Flask Application] SocketIO[Flask-SocketIO] Auth[Flask-Security] Limiter[Rate Limiter] - + MainRoute[Main API Route
/mxcube/api/v0.1] QueueRoute[Queue Routes] LoginRoute[Login Routes] RARoute[Remote Access] WorkflowRoute[Workflow Routes] HarvesterRoute[Harvester Routes] - + MXApp[MXCUBEApplication] Queue[Queue Component] Lims[LIMS Component] @@ -325,32 +324,32 @@ graph TB Chat[Chat Component] Workflow[Workflow Component] Harvester[Harvester Component] - + AdapterMgr[HardwareObjectAdapterManager] HWR[mxcubecore.HWR] - + StateStorage[UI State Storage] UserDB[User Database] - + Entry --> Flask Flask --> SocketIO Flask --> Auth Flask --> Limiter Flask --> MainRoute - + MainRoute --> QueueRoute MainRoute --> LoginRoute MainRoute --> RARoute MainRoute --> WorkflowRoute MainRoute --> HarvesterRoute - + QueueRoute --> Queue LoginRoute --> UserMgr RARoute --> UserMgr RARoute --> Chat WorkflowRoute --> Workflow HarvesterRoute --> Harvester - + MXApp --> Queue MXApp --> Lims MXApp --> UserMgr @@ -358,11 +357,11 @@ graph TB MXApp --> Workflow MXApp --> Harvester MXApp --> AdapterMgr - + AdapterMgr --> HWR Auth --> UserDB SocketIO --> StateStorage - + style Entry fill:#ff9800 style Flask fill:#2196f3 style MXApp fill:#4caf50 @@ -375,13 +374,13 @@ graph TB ```mermaid flowchart TB Index[index.jsx Entry Point] - + subgraph State["State Management"] Store[Redux Store] Reducers[Reducers] Actions[Action Creators] end - + subgraph Components["Components"] Login[Login] SampleGrid[Sample Grid] @@ -392,46 +391,46 @@ flowchart TB Logger[Logger] RemoteAccess[Remote Access] end - + subgraph Containers["Containers"] SGContainer[SampleListViewContainer] SQContainer[SampleQueueContainer] SVContainer[SampleViewContainer] end - + subgraph Communication["Communication"] API[REST API Client wretch] ServerIO[Socket.IO Client] end - + Backend[Flask/SocketIO Server] - + Index --> Store Store --> Reducers Actions --> Reducers - + Index --> SGContainer Index --> SQContainer Index --> SVContainer - + SGContainer --> SampleGrid SQContainer --> SampleQueue SVContainer --> SampleView - + SampleGrid --> Actions SampleQueue --> Actions TaskForms --> Actions - + Actions --> API Actions --> ServerIO - + API -->|REST| Backend ServerIO -->|WebSocket| Backend Backend -->|Events| ServerIO - + ServerIO --> Actions Actions --> Store - + style Index fill:#ff9800 style Store fill:#4caf50 style API fill:#2196f3 @@ -450,7 +449,7 @@ sequenceDiagram participant Queue as Queue Component participant HWR as Hardware Repository participant HWO as Hardware Object - + UI->>Action: User clicks "Start Queue" Action->>API: sendStartQueue() API->>Server: PUT /queue/start @@ -466,7 +465,7 @@ sequenceDiagram Action->>UI: update Redux store ``` ---- +______________________________________________________________________ ## Communication Flow @@ -480,7 +479,7 @@ sequenceDiagram participant Flask participant Component participant MXCubeCore - + Browser->>Redux: User Action Redux->>APIClient: api.get('/queue') APIClient->>Flask: HTTP GET /mxcube/api/v0.1/queue @@ -503,14 +502,14 @@ sequenceDiagram participant SocketIO as Flask-SocketIO participant Client as Browser Client participant Redux as Redux Store - + HWO->>HWO: state changes HWO->>Signal: emit('stateChanged', state) Signal->>SocketIO: server.emit('hwobj_state', data) SocketIO->>Client: WebSocket message Client->>Redux: dispatch(updateHWObjState) Redux->>Client: re-render UI - + Note over HWO,Redux: Real-time updates without polling ``` @@ -522,27 +521,27 @@ graph LR Det[Detector] Motor[Goniometer] end - + subgraph "Control Layer" Collect[CollectHWObj] DC[DataCollectionEntry] end - + subgraph "Queue Layer" QM[QueueManager] QE[QueueEntry] end - + subgraph "Application Layer" QComp[Queue Component] Signal[Signal Handler] end - + subgraph "Frontend" Socket[Socket.IO] UI[React UI] end - + Det -->|frames collected| Collect Motor -->|position reached| Collect Collect -->|progress| DC @@ -552,7 +551,7 @@ graph LR QComp -->|format message| Signal Signal -->|emit event| Socket Socket -->|WebSocket| UI - + style Det fill:#c5e1a5 style Collect fill:#ffcc80 style QM fill:#81d4fa @@ -560,7 +559,7 @@ graph LR style UI fill:#90caf9 ``` ---- +______________________________________________________________________ ## Data Collection Workflow @@ -577,38 +576,38 @@ sequenceDiagram participant Collect as Collect HWObj participant Det as Detector participant LIMS - + User->>UI: Add sample + tasks UI->>Queue: addTask() Queue->>QM: add to queue model - + User->>UI: Click "Start Queue" UI->>Queue: startQueue() Queue->>QM: execute() - + QM->>SC: load_sample(location) SC-->>QM: sample mounted - + QM->>Diff: center_sample() Diff-->>QM: centred position - + QM->>Collect: collect(parameters) Collect->>Diff: move to start position Collect->>Det: prepare acquisition Collect->>Det: start_acquisition() - + loop For each image Det->>Det: collect frame Det->>Collect: emit('imageCollected') Collect->>QM: progress update QM->>UI: WebSocket update end - + Det-->>Collect: acquisition complete Collect->>LIMS: store metadata Collect-->>QM: collection finished QM->>UI: emit('queue_entry_finished') - + QM->>SC: unload_sample() SC-->>QM: sample unmounted ``` @@ -619,24 +618,24 @@ sequenceDiagram stateDiagram-v2 [*] --> QueueIdle QueueIdle --> Executing: start_queue() - + state Executing { [*] --> MountSample MountSample --> CenterSample CenterSample --> TaskGroup - + state TaskGroup { [*] --> DataCollection DataCollection --> Characterisation Characterisation --> [*] } - + TaskGroup --> UnmountSample UnmountSample --> NextSample NextSample --> MountSample: more samples NextSample --> [*]: done } - + Executing --> QueueIdle: finished Executing --> Paused: pause() Paused --> Executing: resume() @@ -644,7 +643,7 @@ stateDiagram-v2 Stopped --> QueueIdle ``` ---- +______________________________________________________________________ ## Key Components @@ -653,17 +652,19 @@ stateDiagram-v2 **Purpose**: Central registry and factory for hardware objects **Key Features**: + - Singleton pattern for global access (`HWR.beamline.*`) - Dynamic loading from XML/YAML configurations - Lazy initialization of hardware objects - Event-driven communication via dispatcher **Example**: + ```python from mxcubecore import HardwareRepository as HWR # Access hardware objects -HWR.beamline.diffractometer.move_motors({'omega': 90}) +HWR.beamline.diffractometer.move_motors({"omega": 90}) HWR.beamline.detector.prepare_acquisition() ``` @@ -672,12 +673,14 @@ HWR.beamline.detector.prepare_acquisition() **Purpose**: Abstract hardware control behind unified interfaces **Key Base Classes**: + - `HardwareObject`: Base class with state management - `AbstractActuator`: Values and limits (motors, energy, etc.) - `AbstractSampleChanger`: Sample loading/unloading - `AbstractCollect`: Data collection orchestration **Control System Adapters**: + - TANGO (Tango Controls) - EPICS (Experimental Physics and Industrial Control System) - SPEC (Python wrapper for Spec diffractometer control) @@ -687,11 +690,13 @@ HWR.beamline.detector.prepare_acquisition() **Purpose**: Manage and execute data collection workflows **Components**: + - `QueueModel`: Tree structure of samples and tasks - `QueueEntry`: Executable nodes with `execute()` method - `QueueManager`: Traversal and execution engine **Queue Entry Types**: + - `SampleQueueEntry`: Mount/unmount operations - `DataCollectionQueueEntry`: Standard data collection - `CharacterisationQueueEntry`: Crystal characterisation @@ -703,6 +708,7 @@ HWR.beamline.detector.prepare_acquisition() **Purpose**: Application-level business logic and state **Components**: + - `Queue`: Queue management API - `Lims`: LIMS integration (ISPyB, etc.) - `UserManager`: Authentication and authorization @@ -715,12 +721,13 @@ HWR.beamline.detector.prepare_acquisition() **Purpose**: Decouple components via event-driven architecture **Pattern**: + ```python # Hardware object emits signal -self.emit('valueChanged', new_value) +self.emit("valueChanged", new_value) # Application listens -HWR.beamline.motor.connect('valueChanged', callback) +HWR.beamline.motor.connect("valueChanged", callback) ``` **Signal Flow**: @@ -731,15 +738,17 @@ Hardware Object → Dispatcher → Signal Handler → Flask-SocketIO → Browser **Purpose**: Modern web UI with real-time updates **Stack**: + - React 18 + Redux for state management - Socket.IO for real-time communication - Vite for fast builds - Bootstrap for styling **Key Containers**: + - `SampleListViewContainer`: Sample management and LIMS - `SampleQueueContainer`: Queue visualization and control - `SampleViewContainer`: Video stream and sample centering - `TaskParametersContainer`: Data collection forms ---- +______________________________________________________________________