diff --git a/wiki/Architecture.md b/wiki/Architecture.md index 3f90ff9b..08886b32 100644 --- a/wiki/Architecture.md +++ b/wiki/Architecture.md @@ -4,80 +4,97 @@ ## Overview -FastPlot uses a render-once, re-downsample-on-zoom architecture. Instead of pushing millions of points to the GPU, it maintains a lightweight cache and re-downsamples only the visible range on every interaction. +FastPlot uses a render-once, re-downsample‑on‑zoom architecture. Instead of pushing millions of points to the GPU, it maintains a lightweight cache and re‑downsamples only the visible range on every interaction. The library is built around a **Tag‑based domain model** (v2.0) for sensor data, thresholds, and derived signals, and a **widget–based dashboard engine** that composes interactive displays from those tags. ## Project Structure ``` FastPlot/ -├── install.m # Path install + MEX compilation +├── install.m # Path install + MEX compilation ├── libs/ -│ ├── FastSense/ # Core plotting engine -│ │ ├── FastSense.m # Main class -│ │ ├── FastSenseGrid.m # Dashboard layout -│ │ ├── FastSenseDock.m # Tabbed container -│ │ ├── FastSenseToolbar.m # Interactive toolbar -│ │ ├── FastSenseTheme.m # Theme system -│ │ ├── FastSenseDataStore.m # SQLite-backed chunked storage -│ │ ├── SensorDetailPlot.m # Sensor detail view with state bands -│ │ ├── NavigatorOverlay.m # Minimap zoom navigator -│ │ ├── ConsoleProgressBar.m # Progress indication -│ │ ├── binary_search.m # Binary search utility -│ │ ├── build_mex.m # MEX compilation script -│ │ └── private/ # Internal algorithms + MEX sources -│ ├── SensorThreshold/ # Sensor and threshold system -│ │ ├── Sensor.m -│ │ ├── StateChannel.m -│ │ ├── ThresholdRule.m -│ │ ├── SensorRegistry.m -│ │ ├── ExternalSensorRegistry.m -│ │ └── private/ # Resolution algorithms -│ ├── EventDetection/ # Event detection and viewer -│ │ ├── Event.m -│ │ ├── EventDetector.m -│ │ ├── EventViewer.m -│ │ ├── LiveEventPipeline.m -│ │ ├── NotificationService.m -│ │ ├── EventStore.m -│ │ ├── EventConfig.m -│ │ ├── IncrementalEventDetector.m -│ │ ├── DataSource.m # Abstract data source -│ │ ├── MatFileDataSource.m # File-based data source -│ │ ├── MockDataSource.m # Test data generation -│ │ ├── NotificationRule.m # Email notification rules -│ │ └── private/ # Event grouping algorithms -│ ├── Dashboard/ # Dashboard engine (serializable) -│ │ ├── DashboardEngine.m -│ │ ├── DashboardBuilder.m -│ │ ├── DashboardLayout.m -│ │ ├── DashboardSerializer.m -│ │ ├── DashboardTheme.m -│ │ ├── DashboardToolbar.m -│ │ ├── DashboardWidget.m # Abstract widget base -│ │ ├── FastSenseWidget.m -│ │ ├── GaugeWidget.m -│ │ ├── NumberWidget.m -│ │ ├── StatusWidget.m -│ │ ├── TextWidget.m -│ │ ├── TableWidget.m -│ │ ├── RawAxesWidget.m -│ │ ├── EventTimelineWidget.m -│ │ ├── GroupWidget.m # Collapsible/tabbed widget groups -│ │ ├── MultiStatusWidget.m # Grid of status indicators -│ │ ├── BarChartWidget.m -│ │ ├── ScatterWidget.m -│ │ ├── HeatmapWidget.m -│ │ ├── HistogramWidget.m -│ │ ├── ImageWidget.m -│ │ └── MarkdownRenderer.m # HTML conversion for info panels -│ └── WebBridge/ # TCP server for web visualization +│ ├── FastSense/ # Core plotting engine +│ │ ├── FastSense.m # Main class +│ │ ├── FastSenseGrid.m # Dashboard layout +│ │ ├── FastSenseDock.m # Tabbed container +│ │ ├── FastSenseToolbar.m # Interactive toolbar +│ │ ├── FastSenseTheme.m # Theme system +│ │ ├── FastSenseDataStore.m # SQLite‑backed chunked storage +│ │ ├── FastSenseDefaults.m # Global default settings +│ │ ├── SensorDetailPlot.m # Sensor detail view with state bands +│ │ ├── NavigatorOverlay.m # Minimap zoom navigator +│ │ ├── ConsoleProgressBar.m # Progress indication +│ │ ├── binary_search.m # Binary search utility +│ │ ├── build_mex.m # MEX compilation script +│ │ ├── mex_stamp.m # Deterministic build stamp +│ │ └── private/ # Internal algorithms + MEX sources +│ ├── SensorThreshold/ # Tag‑based domain model (v2.0) +│ │ ├── Tag.m # Abstract base +│ │ ├── SensorTag.m # Sensor time‑series data +│ │ ├── StateTag.m # Discrete state signals +│ │ ├── MonitorTag.m # Threshold monitor (0/1 output) +│ │ ├── CompositeTag.m # Boolean aggregation of monitors +│ │ ├── TagRegistry.m # Singleton catalog of tags +│ │ ├── BatchTagPipeline.m # Off‑line raw‑data → per‑tag .mat +│ │ ├── LiveTagPipeline.m # Timer‑driven raw‑data → .mat +│ │ └── readRawDelimitedForTest_.m # Test shim for private parser +│ ├── EventDetection/ # Event detection and viewer +│ │ ├── Event.m # Event handle +│ │ ├── EventDetector.m # Off‑line threshold‑based detector +│ │ ├── EventViewer.m # Gantt chart + filterable table +│ │ ├── LiveEventPipeline.m # Streaming event pipeline (MonitorTag) +│ │ ├── NotificationService.m # Email alerts +│ │ ├── EventStore.m # Persistence (.mat) +│ │ ├── EventConfig.m # Legacy configuration (deprecated) +│ │ ├── IncrementalEventDetector.m # Legacy wrapper (no‑op) +│ │ ├── EventBinding.m # Many‑to‑many Event ↔ Tag registry +│ │ ├── DataSource.m # Abstract data source +│ │ ├── DataSourceMap.m # Key → DataSource map +│ │ ├── MatFileDataSource.m # .mat‑file data source +│ │ ├── MockDataSource.m # Test signal generator +│ │ ├── NotificationRule.m # Per‑sensor/threshold notification rules +│ │ ├── printEventSummary.m # Console table helper +│ │ ├── eventLogger.m # Factory for simple event logger +│ │ └── generateEventSnapshot.m # PNG snapshot generation +│ ├── Dashboard/ # Dashboard engine (serializable) +│ │ ├── DashboardEngine.m # Top‑level orchestrator +│ │ ├── DashboardBuilder.m # Edit mode overlay (drag/resize, palette, properties) +│ │ ├── DashboardLayout.m # 24‑column grid + scrollable canvas +│ │ ├── DashboardSerializer.m # JSON load/save + .m script export +│ │ ├── DashboardTheme.m # FastSenseTheme + dashboard‑specific fields +│ │ ├── DashboardToolbar.m # Toolbar (live, edit, config, export, info) +│ │ ├── DashboardWidget.m # Abstract widget base +│ │ ├── DashboardPage.m # Named page container (multi‑page) +│ │ ├── DashboardConfigDialog.m # Config editor popup +│ │ ├── DashboardProgress.m # Render‑progress helper +│ │ ├── TimeRangeSelector.m # Dual‑slider time‑range control with envelope +│ │ ├── FastSenseWidget.m # FastSense instance wrapper +│ │ ├── GaugeWidget.m # Arc/donut/bar/thermometer gauge +│ │ ├── NumberWidget.m # Big number with trend arrow +│ │ ├── StatusWidget.m # Colored dot indicator +│ │ ├── TextWidget.m # Static label or header +│ │ ├── TableWidget.m # uitable display +│ │ ├── RawAxesWidget.m # User‑supplied plot function +│ │ ├── EventTimelineWidget.m # Colored event bars on timeline +│ │ ├── GroupWidget.m # Collapsible panels, tabbed containers +│ │ ├── MultiStatusWidget.m # Grid of sensor status dots +│ │ ├── BarChartWidget.m # Bar chart +│ │ ├── ScatterWidget.m # Scatter plot +│ │ ├── HeatmapWidget.m # Heat map +│ │ ├── HistogramWidget.m # Histogram +│ │ ├── ImageWidget.m # Image display +│ │ ├── ChipBarWidget.m # Compact horizontal chip strip +│ │ ├── IconCardWidget.m # Mushroom‑style card (icon + value) +│ │ ├── SparklineCardWidget.m # KPI card with sparkline + delta +│ │ ├── DividerWidget.m # Visual section divider +│ │ └── MarkdownRenderer.m # Markdown‑to‑HTML for info pages +│ └── WebBridge/ # TCP server for web visualization │ ├── WebBridge.m │ └── WebBridgeProtocol.m -├── examples/ # 40+ runnable examples -└── tests/ # 30+ test suites +├── examples/ # 40+ runnable examples +└── tests/ # 30+ test suites ``` -## Render Pipeline +## Render Pipeline (FastSense) 1. User calls `render()` 2. Create figure/axes if not parented @@ -87,7 +104,7 @@ FastPlot/ 6. For each line: initial downsample of full range, create graphics object 7. Create threshold, band, shading, marker objects 8. Install XLim PostSet listener for zoom/pan events -9. Set axis limits, disable auto-limits +9. Set axis limits, disable auto‑limits 10. `drawnow` to display ## Zoom/Pan Callback @@ -114,52 +131,83 @@ For each pixel bucket, keep the minimum and maximum Y values. Preserves signal e ### LTTB (Largest Triangle Three Buckets) Visually optimal downsampling that preserves signal shape by maximizing triangle area between consecutive buckets. Better visual fidelity but slightly slower. -Both algorithms handle NaN gaps by segmenting contiguous non-NaN regions independently. +Both algorithms handle NaN gaps by segmenting contiguous non‑NaN regions independently. -## Lazy Multi-Resolution Pyramid +## Lazy Multi‑Resolution Pyramid -Problem: At full zoom-out with 50M+ points, scanning all data is O(N). +Problem: At full zoom‑out with 50M+ points, scanning all data is O(N). -Solution: Pre-computed MinMax pyramid with configurable reduction factor (default 100x per level): +Solution: Pre‑computed MinMax pyramid with configurable reduction factor (default 100× per level): ``` Level 0: Raw data (50,000,000 points) -Level 1: 100x reduction ( 500,000 points) -Level 2: 100x reduction ( 5,000 points) +Level 1: 100× reduction ( 500,000 points) +Level 2: 100× reduction ( 5,000 points) ``` -On zoom, the coarsest level with sufficient resolution is selected. Full zoom-out reads level 2 (5K points) and downsamples to ~4K in under 1ms. +On zoom, the coarsest level with sufficient resolution is selected. Full zoom‑out reads level 2 (5K points) and downsamples to ~4K in under 1ms. -Levels are built lazily on first access — the first zoom-out pays a one-time build cost (~70ms with MEX), subsequent queries are instant. +Levels are built lazily on first access — the first zoom‑out pays a one‑time build cost (~70 ms with MEX), subsequent queries are instant. ## MEX Acceleration -Optional C MEX functions with SIMD intrinsics (AVX2 on x86_64, NEON on arm64): +Optional C MEX functions with SIMD intrinsics (AVX2 on x86_64, NEON on arm64). For details, see [[MEX Acceleration]]. | Function | Speedup | Description | |----------|---------|-------------| -| binary_search_mex | 10-20x | O(log n) visible range lookup | -| minmax_core_mex | 3-10x | Per-pixel MinMax reduction | -| lttb_core_mex | 10-50x | Triangle area computation | -| violation_cull_mex | significant | Fused detection + pixel culling | -| compute_violations_mex | significant | Batch violation detection for resolve() | -| resolve_disk_mex | significant | SQLite disk-based sensor resolution | -| build_store_mex | 2-3x | Bulk SQLite writer for DataStore init | -| to_step_function_mex | significant | SIMD step-function conversion for thresholds | +| `binary_search_mex` | 10‑20× | O(log n) visible range lookup | +| `minmax_core_mex` | 3‑10× | Per‑pixel MinMax reduction | +| `lttb_core_mex` | 10‑50× | Triangle area computation | +| `violation_cull_mex` | significant | Fused detection + pixel culling | +| `compute_violations_mex` | significant | Batch violation detection (legacy `resolve`) | +| `resolve_disk_mex` | significant | SQLite disk‑based sensor resolution (legacy) | +| `build_store_mex` | 2‑3× | Bulk SQLite writer for `FastSenseDataStore` init | +| `to_step_function_mex` | significant | SIMD step‑function conversion for thresholds | -All share a common `simd_utils.h` abstraction layer. If MEX is unavailable, pure-MATLAB implementations are used with identical behavior. +All share a common `simd_utils.h` abstraction layer. If MEX is unavailable, pure‑MATLAB implementations are used with identical behavior. ## Data Flow Architecture -### Core Data Path +### Tag‑Based Data Model (v2.0) + +FastPlot v2.0 introduces a unified **Tag** abstraction that replaces the old Sensor/Threshold/StateChannel pipeline. Tags are registered in a global `TagRegistry` and serve as the single source of truth for all time‑series data, states, and derived signals. + +- **`SensorTag`** — holds raw X/Y sensor data. Data may reside in memory or be backed by `FastSenseDataStore` (disk). +- **`StateTag`** — models discrete piecewise‑constant states (e.g., machine mode) with zero‑order‑hold lookup. +- **`MonitorTag`** — wraps a parent Tag and a condition function to produce a binary 0/1 signal (alarm/ok) on the parent’s native time grid. Supports hysteresis, minimum duration, and callback‑based event emission (via `EventStore`). Streaming tail extension is done through `appendData()`. +- **`CompositeTag`** — aggregates multiple `MonitorTag` or `CompositeTag` children using boolean logic (AND, OR, WORST, COUNT, MAJORITY, SEVERITY) or a user‑supplied function to produce a combined derived signal. + +Tags are lazy‑evaluated — a `SensorTag` provides data by reference, and derived tags (`MonitorTag`, `CompositeTag`) recompute only when invalidated. Invalidation cascades through listener chains, keeping downstream consumers in sync. + +### Core Tag Pipeline + +``` +SensorTag (X, Y) + ↓ +MonitorTag (condition → 0/1) ←─ hysteresis, MinDuration + ↓ +CompositeTag (AND/OR aggregation of multiple monitors) + ↓ +EventStore ←─ events triggered by MonitorTag/CompositeTag edges + ↓ +Dashboard Widgets (FastSenseWidget, EventTimelineWidget, …) +``` + +### Data Ingestion + +- **`BatchTagPipeline`** — offline bulk processing: enumerates `TagRegistry` for tags with `RawSource` bindings, de‑duplicates file reads, parses raw CSV/TXT files, and writes per‑tag `.mat` files. +- **`LiveTagPipeline`** — timer‑driven analogue of the batch pipeline: polls raw files, detects new rows, and appends to the per‑tag `.mat` in streaming fashion. + +### FastSense Data Path + ``` -Raw Data (X, Y arrays) +Raw Data (X, Y from SensorTag.getXY()) ↓ FastSenseDataStore (optional, for large datasets) ↓ Downsampling Engine (MinMax/LTTB) ↓ -Pyramid Cache (lazy multi-resolution) +Pyramid Cache (lazy multi‑resolution) ↓ Graphics Objects (line handles) ↓ @@ -169,31 +217,34 @@ Interactive Display ### Storage Modes - **Memory mode**: X/Y arrays held in MATLAB workspace - **Disk mode**: Data chunked into SQLite database via `FastSenseDataStore` -- **Auto mode**: Switches to disk when data exceeds `MemoryLimit` (default 500MB) +- **Auto mode**: Switches to disk when data exceeds `MemoryLimit` (default 500 MB) + +## Tag‑Based Threshold Monitoring + +`MonitorTag` is the primary mechanism for threshold‑based event detection in the v2.0 model. -## Sensor Threshold Resolution +1. A `MonitorTag` is created with a **parent Tag** (e.g., a `SensorTag`) and a **condition function** `(x, y) -> logical`. +2. On first `getXY()`, the parent’s full (X, Y) grid is fetched and the condition evaluated, producing a 0/1 vector. The result is cached. +3. The condition can be augmented with an `AlarmOffConditionFn` for hysteresis (e.g., rising edge at 50, falling edge at 48). +4. A minimum duration `MinDuration` (in parent X units) can be specified; runs shorter than this are filtered out. +5. On subsequent `updateData()` calls to the parent, all registered listeners (including the `MonitorTag`) are invalidated. The `MonitorTag` then recomputes lazily on the next `getXY()`. -The `Sensor.resolve()` algorithm is segment-based: +For **live streaming**, `LiveEventPipeline` periodically fetches new data from a `DataSource`, calls `parent.updateData(newX, newY)` to update the parent’s data **first**, then calls `monitor.appendData(newX, newY)`. The `appendData` method extends the cached binary vector using the condition function only on the new samples, preserving hysteresis state and `MinDuration` bookkeeping across the append boundary. Open events at the tail are carried over; events that complete within the new data fire callbacks and are persisted via `EventStore`. -1. Collect all state-change timestamps from all StateChannels -2. For each segment between state changes: - - Evaluate which ThresholdRules match the current state - - Group rules with identical conditions -3. Assign threshold values per segment -4. Detect violations using SIMD-accelerated comparison +**`CompositeTag`** extends this to combine multiple monitors with Boolean algebra, enabling complex alarm logic (e.g., “alarm if pump A OR pump B exceeds threshold”). -Complexity: O(S × R) where S = state segments and R = rules, instead of O(N × R) per-point evaluation. +The legacy `Sensor.resolve()` algorithm (which evaluated threshold rules per state segment) has been removed in Phase 1011. All threshold logic now lives in the Tag layer. -## Disk-Backed Data Storage +## Disk‑Backed Data Storage -For datasets exceeding available memory (100M+ points), `FastSenseDataStore` provides SQLite-backed chunked storage: +For datasets exceeding available memory (100M+ points), `FastSenseDataStore` provides SQLite‑backed chunked storage: -1. Data is split into chunks (~10K-500K points each, auto-tuned) -2. Each chunk stored as a pair of typed BLOBs (X and Y) with X range metadata +1. Data is split into chunks (~10K‑500K points each, auto‑tuned) +2. Each chunk stored as a pair of typed BLOBs (X and Y) with X‑range metadata 3. On zoom/pan, only chunks overlapping the visible range are loaded -4. Pre-computed L1 MinMax pyramid for instant zoom-out +4. Pre‑computed L1 MinMax pyramid for instant zoom‑out -The bulk write path uses `build_store_mex` — a single C call that writes all chunks with SIMD-accelerated Y min/max computation, replacing ~20K mksqlite round-trips. +The bulk write path uses `build_store_mex` — a single C call that writes all chunks with SIMD‑accelerated Y min/max computation, replacing ~20K mksqlite round‑trips. If SQLite is unavailable, a binary file fallback is used automatically. @@ -203,85 +254,106 @@ If SQLite is unavailable, a binary file fallback is used automatically. Element override > Tile theme > Figure theme > 'default' preset ``` -Each level fills in only the fields it specifies; unspecified fields cascade from the next level. +Each level fills in only the fields it specifies; unspecified fields cascade from the next level. (See [[API Reference: Themes]]). ## Dashboard Architecture ### FastSenseGrid vs DashboardEngine -- **[[Dashboard|FastSenseGrid]]**: Simple tiled grid of FastSense instances with synchronized live mode -- **[[Dashboard Engine Guide|DashboardEngine]]**: Full widget-based dashboard with gauges, numbers, status indicators, tables, timelines, and edit mode +- **[[Dashboard|FastSenseGrid]]**: Simple tiled grid of FastSense instances with synchronized live mode. +- **[[Dashboard Engine Guide|DashboardEngine]]**: Full widget‑based dashboard with gauges, numbers, status indicators, tables, timelines, edit mode, multi‑page support, and a time‑range selector. ### DashboardEngine Components ``` DashboardEngine -├── DashboardToolbar — Top toolbar (Live, Edit, Save, Export, Sync) -├── DashboardLayout — 24-column responsive grid with scrollable canvas -├── DashboardTheme — FastSenseTheme + dashboard-specific fields -├── DashboardBuilder — Edit mode overlay (drag/resize, palette, properties) -├── DashboardSerializer — JSON save/load and .m script export +├── DashboardToolbar — Top toolbar (Live, Edit, Config, Export, Info) +├── DashboardLayout — 24‑column responsive grid with scrollable canvas +├── DashboardTheme — FastSenseTheme + dashboard‑specific fields +├── DashboardBuilder — Edit mode overlay (drag/resize, palette, properties) +├── DashboardSerializer — JSON save/load and .m script export +├── DashboardProgress — Console progress bar during batch render +├── DashboardConfigDialog — Figure‑based config editor +├── TimeRangeSelector — Dual‑slider time range with aggregate envelope preview +├── DashboardPage — Named page container (multi‑page mode) └── Widgets (DashboardWidget subclasses) - ├── FastSenseWidget — FastSense instance (Sensor/DataStore/inline) + ├── FastSenseWidget — FastSense instance (Tag/DataStore/inline) ├── GaugeWidget — Arc/donut/bar/thermometer gauge ├── NumberWidget — Big number with trend arrow ├── StatusWidget — Colored dot indicator ├── TextWidget — Static label or header ├── TableWidget — uitable display - ├── RawAxesWidget — User-supplied plot function + ├── RawAxesWidget — User‑supplied plot function ├── EventTimelineWidget — Colored event bars on timeline ├── GroupWidget — Collapsible panels, tabbed containers - └── MultiStatusWidget — Grid of sensor status dots + ├── MultiStatusWidget — Grid of sensor status dots + ├── BarChartWidget — Bar chart + ├── ScatterWidget — Scatter plot + ├── HeatmapWidget — Heat map + ├── HistogramWidget — Histogram + ├── ImageWidget — Image display + ├── ChipBarWidget — Compact horizontal chip strip (system health) + ├── IconCardWidget — Mushroom card (icon + primary value) + ├── SparklineCardWidget — KPI card with sparkline + delta + └── DividerWidget — Horizontal section divider ``` ### Render Flow -1. `DashboardEngine.render()` creates the figure -2. `DashboardTheme(preset)` generates the full theme struct -3. `DashboardToolbar` creates the top toolbar panel -4. Time control panel (dual sliders) is created at the bottom -5. `DashboardLayout.createPanels()` computes grid positions, creates viewport/canvas/scrollbar, and creates a uipanel per widget -6. Each widget's `render(parentPanel)` is called to populate its panel -7. `updateGlobalTimeRange()` scans widgets for data bounds and configures the time sliders +1. `DashboardEngine.render()` creates the figure. +2. Theme is resolved via `DashboardTheme(preset)`. +3. `DashboardToolbar` renders the top toolbar. +4. If `ShowTimePanel` is true, the `TimeRangeSelector` panel (dual sliders with envelope preview) is created at the bottom. +5. `DashboardLayout.createPanels()` computes grid positions, creates a viewport/canvas with optional scrollbar, and allocates one `uipanel` per widget. +6. Each widget’s `render(parentPanel)` is called to populate its panel. +7. `updateGlobalTimeRange()` scans widgets for data bounds and configures the time sliders. ### Live Mode When `startLive()` is called, a timer fires at `LiveInterval` seconds: -1. `updateLiveTimeRange()` expands time bounds from new data -2. Each widget's `refresh()` is called (sensor-bound widgets re-read `Sensor.Y(end)`) -3. The toolbar timestamp label is updated -4. Current slider positions are re-applied to the updated time range +1. `updateLiveTimeRange()` expands time bounds from new data. +2. Each widget’s `refresh()` is called (sensor‑bound widgets re‑read `SensorTag.Y(end)`, monitor‑bound widgets re‑evaluate conditions, etc.). +3. The time‑range selector is updated, and the toolbar timestamp label shows last‑update time. +4. A stale‑data banner warns if any widget’s data did not advance. ### Edit Mode -Clicking "Edit" in the toolbar creates a `DashboardBuilder` instance: -1. A palette sidebar (left) shows widget type buttons -2. A properties panel (right) shows selected widget settings -3. Drag/resize overlays are added on top of each widget panel -4. The content area narrows to accommodate sidebars -5. Mouse move/up callbacks handle drag and resize interactions -6. Grid snap rounds positions to the nearest column/row +Clicking “Edit” in the toolbar creates a `DashboardBuilder` instance: +1. A palette sidebar (left) shows widget type buttons. +2. A properties panel (right) shows selected widget settings. +3. Drag/resize overlays are added on top of each widget panel. +4. The content area narrows to accommodate sidebars. +5. Mouse move/up callbacks handle drag and resize interactions. +6. Grid snap rounds positions to the nearest column/row. + +### Multi‑Page Dashboards + +`DashboardEngine` supports multiple named pages. Each `DashboardPage` holds its own list of widgets. `addPage('name')` creates a new page and makes it active; `switchPage(n)` toggles visibility of competing pages. Page state is fully serializable in JSON. + +### Time Range Selector + +`TimeRangeSelector` provides a dual‑slider with an aggregate min‑max envelope (downsampled from all `FastSenseWidget`s and event markers) that allows panning and resize of the visible time window. The selector owns its own axes, uses figure‑level mouse callbacks, and is compatible with both MATLAB and Octave. ### JSON Persistence -`DashboardSerializer` handles round-trip serialization: -- **Save:** each widget's `toStruct()` produces a plain struct with type, title, position, and source. The struct is encoded to JSON with heterogeneous widget arrays assembled manually (MATLAB's `jsonencode` cannot handle cell arrays of mixed structs). -- **Load:** JSON is decoded, widgets array is normalized to cell, and `configToWidgets()` dispatches to each widget class's `fromStruct()` static method. An optional `SensorResolver` function handle re-binds Sensor objects by name. -- **Export script:** generates a `.m` file with `DashboardEngine` constructor calls and `addWidget` calls for each widget. +`DashboardSerializer` handles round‑trip serialization: +- **Save:** each widget’s `toStruct()` produces a plain struct with type, title, position, and source. The struct is encoded to JSON with heterogeneous widget arrays assembled manually (MATLAB’s `jsonencode` cannot handle cell arrays of mixed structs). +- **Load:** JSON is decoded, widgets array is normalized to cell, and `configToWidgets()` dispatches to each widget class’s `fromStruct()` static method. An optional `SensorResolver` function handle re‑binds `SensorTag` objects by key. Multi‑page dashboards are stored in a `pages` array, each page containing its own `widgets`. +- **Export script:** generates a `.m` file with `DashboardEngine` constructor calls and `addWidget` / `addPage` calls for each widget. ## Event Detection Architecture -The event detection system provides real-time threshold violation monitoring with configurable notifications and data persistence. +The event detection system is built on top of the Tag model. The primary live pipeline is `LiveEventPipeline`, which uses `MonitorTag` to detect threshold crossings in streaming data. ### Core Components ``` LiveEventPipeline -├── DataSourceMap — Maps sensor keys to data sources -├── IncrementalEventDetector — Tracks per-sensor state and open events -├── EventStore — Thread-safe .mat file persistence -├── NotificationService — Rule-based email alerts -└── EventViewer — Interactive Gantt chart + filterable table +├── MonitorTargets — containers.Map: tag key → MonitorTag +├── DataSourceMap — maps parent tag keys to DataSource instances +├── EventStore — thread‑safe .mat file persistence +├── NotificationService — rule‑based email alerts +└── EventViewer — interactive Gantt chart + filterable table ``` ### Data Sources @@ -290,36 +362,48 @@ LiveEventPipeline - **MockDataSource**: Generates realistic test signals with violations - **Custom sources**: Implement `DataSource.fetchNew()` interface -### Event Detection Flow +### Event Detection Flow (Live) + +1. `LiveEventPipeline.runCycle()` polls each parent tag’s data source. +2. New data for a parent (e.g., a `SensorTag`) is passed to `parent.updateData(newX, newY)` to update the parent’s grid. +3. The corresponding `MonitorTag`’s `appendData(newX, newY)` is called, which extends the cached binary time series and fires events for closed violation runs. +4. New events are sent to `EventStore.append()`, which persists them atomically. +5. `NotificationService` applies rule‑based email alerts with optional PNG snapshots. +6. Any active `EventViewer` instances auto‑refresh to show new events. + +### Off‑line Detection -1. `LiveEventPipeline.runCycle()` polls all data sources -2. New data is passed to `IncrementalEventDetector.process()` -3. Sensor state is evaluated via `Sensor.resolve()` -4. Violations are grouped into events with debouncing (`MinDuration`) -5. Events are stored via `EventStore.append()` (atomic .mat writes) -6. `NotificationService` sends rule-based email alerts with plot snapshots -7. Active `EventViewer` instances auto-refresh to show new events +`EventDetector.detect(tag, threshold)` can be used for one‑shot, full‑history detection on any Tag. It evaluates the tag’s full (X, Y) grid against the threshold rules and returns an array of `Event` objects. This is useful for batch analysis or after loading a new dataset. ### Escalation Logic When `EscalateSeverity` is enabled, events are promoted to the highest violated threshold: -- A violation starts at "Warning" level -- If "Alarm" threshold is also crossed, the event is escalated to "Alarm" +- A violation starts at “Warning” level +- If “Alarm” threshold is also crossed, the event is escalated to “Alarm” - The event retains the highest severity level encountered +### Event‑Tag Binding + +`EventBinding` provides a many‑to‑many registry that binds `Event.Id` to `Tag.Key`. This enables widgets like `EventTimelineWidget` to filter events by tag and supports the query method `EventStore.getEventsForTag(tagKey)`. + ## Progress Indication `ConsoleProgressBar` provides hierarchical progress feedback: -- Single-line ASCII/Unicode bars with backspace-based updates +- Single‑line ASCII/Unicode bars with backspace‑based updates - Indentation support for nested operations (e.g., dock → tabs → tiles) - Freeze/finish modes for permanent status lines +`DashboardProgress` is a thin wrapper used during dashboard render passes. ## Interactive Features ### Toolbars and Navigation - **[[API Reference: FastPlot|FastSenseToolbar]]**: Data cursor, crosshair, grid toggle, autoscale, export, live mode -- **DashboardToolbar**: Live toggle, edit mode, save/export, name editing +- **DashboardToolbar**: Live toggle, edit mode, save/export, name editing, config dialog, info button - **NavigatorOverlay**: Minimap with draggable zoom rectangle for `SensorDetailPlot` +- **TimeRangeSelector**: Dual‑slider time range with data envelope and event markers on the dashboard ### Link Groups -Multiple FastSense instances can share synchronized zoom/pan via `LinkGroup` strings. When one plot's XLim changes, all plots in the same group update automatically. +Multiple FastSense instances can share synchronized zoom/pan via `LinkGroup` strings. When one plot’s XLim changes, all plots in the same group update automatically. + +### Loupe +`openLoupe()` creates a standalone magnified copy of any tile, preserving the current zoom and adding its own toolbar. diff --git a/wiki/Dashboard-Engine-Guide.md b/wiki/Dashboard-Engine-Guide.md index 2fff83a1..76dfeb87 100644 --- a/wiki/Dashboard-Engine-Guide.md +++ b/wiki/Dashboard-Engine-Guide.md @@ -2,7 +2,7 @@ # Dashboard Engine Guide -Build rich, interactive dashboards with mixed widget types, sensor bindings, JSON persistence, and a visual editor. +Build rich, interactive dashboards with mixed widget types, sensor bindings, JSON persistence, a visual editor, and multi‑page support. --- @@ -12,18 +12,20 @@ FastSense provides two dashboard systems: | Feature | FastSenseGrid | DashboardEngine | |---------|---------------|-----------------| -| Grid | Fixed rows x cols | 24-column responsive | -| Tile content | FastSense instances only | 8 widget types (plots, gauges, numbers, tables, etc.) | +| Grid | Fixed rows x cols | 24‑column responsive | +| Tile content | FastSense instances only | 15+ widget types (plots, gauges, numbers, tables, images, etc.) | | Persistence | None | JSON save/load + .m script export | | Visual editor | No | Yes (drag/resize, palette, properties panel) | -| Scrolling | No | Auto-scrollbar when content overflows | -| Global time | No | Dual sliders controlling all widgets | -| Sensor binding | Via addSensor per tile | Direct widget property (auto-title, auto-units) | -| Live mode | Per-figure timer | Engine-level timer refreshing all widgets | +| Scrolling | No | Auto‑scrollbar when content overflows | +| Global time | No | Dual sliders controlling all widgets + data‑preview envelope | +| Sensor binding | Via addSensor per tile | Direct widget property (auto‑title, auto‑units) | +| Live mode | Per‑figure timer | Engine‑level timer refreshing all widgets + stale‑data detection | +| Multi‑page | No | Yes – addPage / switchPage | +| Chrome customisation | Toolbar only | ShowTimePanel, EventMarkersVisible, Config dialog | **When to use FastSenseGrid:** You need a simple tiled grid of FastSense time series plots with linked axes and a toolbar. -**When to use DashboardEngine:** You need mixed widget types (gauges, KPIs, tables, timelines), JSON persistence, or the visual editor. +**When to use DashboardEngine:** You need mixed widget types (gauges, KPIs, tables, timelines, images), JSON persistence, multi‑page dashboards, or the visual editor. --- @@ -55,16 +57,16 @@ d.render(); ## Grid System -DashboardEngine uses a **24-column grid**. Widget positions are specified as: +DashboardEngine uses a **24‑column grid**. Widget positions are specified as: ``` Position = [col, row, width, height] ``` -- `col`: column (1-24), left to right +- `col`: column (1–24), left to right - `row`: row (1+), top to bottom -- `width`: number of columns to span (1-24) -- `height`: number of rows to span +- `width`: number of columns to span (1–24) +- `height`: number of rows to span (minimum ~0.22 per row in height) Examples: ```matlab @@ -74,7 +76,7 @@ Examples: [1 5 8 2] % Left third, row 5 ``` -If a new widget overlaps an existing one, it is automatically pushed down to the next free row. +If a new widget overlaps an existing one, it is automatically pushed down to the next free row. The layout supports scrolling when the content height exceeds the viewport. --- @@ -83,7 +85,7 @@ If a new widget overlaps an existing one, it is automatically pushed down to the ### FastSense (time series) ```matlab -% Sensor-bound (recommended) +% Sensor‑bound (recommended) d.addWidget('fastsense', 'Position', [1 1 12 8], 'Sensor', mySensor); % Inline data @@ -99,7 +101,7 @@ d.addWidget('fastsense', 'Title', 'Store', 'Position', [1 15 24 6], ... 'DataStore', myDataStore); ``` -When bound to a Sensor, threshold rules apply automatically (resolved violations are shown). The widget title, X-axis label (`'Time'`), and Y-axis label (sensor Units or Name) are auto-derived. +When bound to a Sensor, threshold rules apply automatically (resolved violations are shown). Title, X‑axis label (`'Time'`), and Y‑axis label (sensor Units or Name) are auto‑derived. You can also show event markers by setting `ShowEventMarkers = true` and providing an `EventStore`. ### Number (big value display) @@ -108,12 +110,12 @@ d.addWidget('number', 'Title', 'Temperature', ... 'Position', [1 1 6 2], ... 'Sensor', sTemp, 'Units', 'degF', 'Format', '%.1f'); -% Or with static value +% Static value d.addWidget('number', 'Title', 'Total Count', ... 'Position', [7 1 6 2], ... 'StaticValue', 1234, 'Units', 'pcs', 'Format', '%d'); -% Or with function callback +% Function callback d.addWidget('number', 'Title', 'CPU Load', ... 'Position', [13 1 6 2], ... 'ValueFcn', @() getCpuLoad(), 'Units', '%', 'Format', '%.0f'); @@ -132,9 +134,22 @@ d.addWidget('status', 'Title', 'Pump', ... d.addWidget('status', 'Title', 'System', ... 'Position', [12 1 5 2], ... 'StaticStatus', 'ok'); % 'ok', 'warning', 'alarm' + +% Threshold‑bound (no sensor) +d.addWidget('status', 'Title', 'Pressure', ... + 'Position', [1 3 5 2], ... + 'Threshold', t, 'ValueFcn', @() getPressure()); ``` -Shows a colored dot (green/amber/red) and the sensor's latest value. Status is derived automatically from threshold rules. +Shows a colored dot (green/amber/red) and the sensor’s latest value. Status is derived automatically from threshold rules. + +### MultiStatus (grid of sensor status dots) + +```matlab +d.addWidget('multistatus', 'Title', 'Vessels', ... + 'Position', [1 3 8 4], ... + 'Sensors', {s1, s2, s3}, 'Columns', 2, 'IconStyle', 'dot'); +``` ### Gauge (arc/donut/bar/thermometer) @@ -153,17 +168,25 @@ d.addWidget('gauge', 'Title', 'Efficiency', ... Styles: `'arc'` (default), `'donut'`, `'bar'`, `'thermometer'`. -When Sensor-bound, range and units are auto-derived from threshold rules and sensor properties. +When Sensor‑bound, range and units are auto‑derived from threshold rules and sensor properties. ### Text (labels and headers) ```matlab d.addWidget('text', 'Title', 'Plant Overview', ... 'Position', [1 1 6 1], ... - 'Content', 'Line 4 - Shift A', 'FontSize', 16, ... + 'Content', 'Line 4 – Shift A', 'FontSize', 16, ... 'Alignment', 'center'); ``` +### Divider (horizontal rule) + +```matlab +d.addWidget('divider', 'Position', [1 3 24 1], 'Thickness', 2); +``` + +Thickness levels: 1 (thin), 2 (medium), 3 (thick). Color can be overridden with the `Color` property. + ### Table (data display) ```matlab @@ -199,19 +222,19 @@ d.addWidget('rawaxes', 'Title', 'Temperature Distribution', ... 'PlotFcn', @(ax) histogram(ax, tempData, 50, ... 'FaceColor', [0.31 0.80 0.64], 'EdgeColor', 'none')); -% Sensor-bound with time range +% Sensor‑bound with time range d.addWidget('rawaxes', 'Title', 'Custom Analysis', ... 'Position', [9 5 8 4], ... 'Sensor', mySensor, ... 'PlotFcn', @(ax, sensor, tRange) plotCustom(ax, sensor, tRange)); ``` -The `PlotFcn` receives MATLAB axes as the first argument. When Sensor-bound, it also receives the Sensor object and optionally a time range. +PlotFcn receives MATLAB axes as the first argument. When Sensor‑bound it also receives the Sensor object and optionally a time range (`tRange`). Provide `DataRangeFcn` to contribute to the global time range. ### Event Timeline ```matlab -% From event structs +% From event structs (legacy) events = struct('startTime', {0, 3600}, 'endTime', {3600, 7200}, ... 'label', {'Idle', 'Running'}, 'color', {[0.6 0.6 0.6], [0.2 0.7 0.3]}); @@ -231,6 +254,113 @@ d.addWidget('timeline', 'Title', 'Temp Events', ... 'FilterSensors', {'T-401', 'T-402'}); ``` +### ChipBar (compact multi‑status strip) + +```matlab +d.addWidget('chipbar', 'Title', 'System Health', ... + 'Position', [1 1 24 1], ... + 'Chips', { ... + struct('label', 'Pump', 'statusFcn', @() 'ok'), ... + struct('label', 'Tank', 'statusFcn', @() 'warn'), ... + struct('label', 'Fan', 'statusFcn', @() 'alarm') ... + }); +``` + +Each chip has a colored dot and a label. Status can be driven by sensors. + +### IconCard (mushroom‑style KPI card) + +```matlab +d.addWidget('iconcard', 'Title', 'Temperature', ... + 'Position', [1 1 6 3], ... + 'StaticValue', 23.5, 'Units', '°C', ... + 'StaticState', 'ok'); +``` + +Supports `Sensor`, `ValueFcn`, threshold‑derived icon color. + +### SparklineCard (KPI with mini‑chart) + +```matlab +d.addWidget('sparkline', 'Title', 'CPU Load', ... + 'Position', [1 1 6 3], ... + 'StaticValue', 42, 'SparkData', cpuHistory, 'Units', '%'); +``` + +Displays a big number, a delta indicator, and a small sparkline of the last N points (default 50). Supports `Sensor` or inline `SparkData`. + +### BarChart and Histogram + +```matlab +d.addWidget('barchart', 'Title', 'Throughput', ... + 'Position', [1 1 6 3], ... + 'DataFcn', @() struct('categories', {{'A','B','C'}}, 'values', [30 45 20]), ... + 'Orientation', 'vertical'); + +d.addWidget('histogram', 'Title', 'Distribution', ... + 'Position', [7 1 6 3], ... + 'DataFcn', @() randn(1,1000), ... + 'NumBins', 50, 'ShowNormalFit', true); +``` + +### Heatmap + +```matlab +d.addWidget('heatmap', 'Title', 'Correlation', ... + 'Position', [1 1 8 6], ... + 'DataFcn', @() corr(rand(10,5)), ... + 'XLabels', {'V1','V2','V3','V4','V5'}, ... + 'YLabels', {'V1','V2','V3','V4','V5'}, ... + 'ShowColorbar', true); +``` + +### Image + +```matlab +d.addWidget('image', 'Title', 'Floor Plan', ... + 'Position', [1 1 12 8], ... + 'File', 'floorplan.png', ... + 'Scaling', 'fit', 'Caption', 'Level 1'); +``` + +Supports `ImageFcn` returning an image matrix as an alternative to a file. + +### Scatter (sensor cross‑plots) + +```matlab +d.addWidget('scatter', 'Title', 'P vs T', ... + 'Position', [1 1 6 6], ... + 'SensorX', sensorPressure, 'SensorY', sensorTemp, ... + 'SensorColor', sensorFlow, 'MarkerSize', 8); +``` + +### GroupWidget (panel, collapsible, tabbed) + +GroupWidgets organise children in sub‑grids. + +```matlab +% Collapsible group +w = GroupWidget('Mode', 'collapsible', 'Label', 'Sensors', ... + 'Collapsed', false); +w.addChild(NumberWidget('Title', 'T1', 'StaticValue', 100)); +w.addChild(StatusWidget('Title', 'Pump', 'StaticStatus', 'ok')); +d.addWidget(w); + +% Tabbed group +tabs = GroupWidget('Mode', 'tabbed', 'Label', 'Views'); +tabs.addChild(NumberWidget('Title', 'Tab A', 'StaticValue', 1), 'Overview'); +tabs.addChild(NumberWidget('Title', 'Tab B', 'StaticValue', 2), 'Details'); +d.addWidget(tabs); +``` + +Simplify with convenience methods: +```matlab +d.addWidget('group', 'Mode', 'collapsible', 'Label', 'Controls', ... + 'Children', {w1, w2}); +d.addWidget('group', 'Mode', 'tabbed', 'Label', 'Views', ... + 'Tabs', {{'Overview', {w1}}, {'Details', {w2}}}); +``` + --- ## Sensor Binding @@ -254,7 +384,7 @@ sTemp.addThresholdRule(struct('machine', 1), 85, ... 'Direction', 'upper', 'Label', 'Hi Alarm'); sTemp.resolve(); -% All of these auto-derive from the Sensor: +% Bind to multiple widgets d.addWidget('fastsense', 'Sensor', sTemp, 'Position', [1 1 12 8]); d.addWidget('number', 'Sensor', sTemp, 'Position', [13 1 6 2], 'Units', 'degF'); d.addWidget('status', 'Sensor', sTemp, 'Position', [19 1 6 2]); @@ -262,12 +392,14 @@ d.addWidget('gauge', 'Sensor', sTemp, 'Position', [13 3 12 6]); ``` Benefits of Sensor binding: -- **Title:** auto-derived from `Sensor.Name` or `Sensor.Key` -- **Units:** auto-derived from `Sensor.Units` -- **Value:** uses `Sensor.Y(end)` for number, gauge, and status widgets -- **Thresholds:** FastSenseWidget renders resolved thresholds and violations -- **Status:** StatusWidget checks the latest value against all threshold rules -- **Live refresh:** calling `refresh()` re-reads the sensor data + +- **Title:** auto‑derived from `Sensor.Name` or `Sensor.Key` +- **Units:** auto‑derived from `Sensor.Units` +- **Value:** uses `Sensor.Y(end)` for number, gauge, status, iconcard, sparkline widgets +- **Thresholds:** FastSenseWidget renders resolved thresholds and violations; StatusWidget and IconCardWidget derive colour from threshold rules +- **Live refresh:** calling `refresh()` rereads updated sensor data + +The `Sensor` property is a backward‑compat alias for the internal `Tag` property (v2.0 Tag API). Any widget that accepts `'Sensor'` also works with `'Tag'`. --- @@ -279,7 +411,7 @@ Benefits of Sensor binding: d.save('dashboard.json'); ``` -The JSON file contains the dashboard name, theme, live interval, grid settings, and each widget's type, title, position, and data source. +The JSON file contains the dashboard name, theme, live interval, grid settings, and each widget’s type, title, position, and data source. Multi‑page dashboards are fully serialised. ### Load from JSON @@ -288,7 +420,7 @@ d2 = DashboardEngine.load('dashboard.json'); d2.render(); ``` -To re-bind Sensor objects on load, provide a resolver function: +To re‑bind Sensor objects on load, provide a resolver function: ```matlab d2 = DashboardEngine.load('dashboard.json', ... @@ -302,29 +434,31 @@ d2.render(); d.exportScript('rebuild_dashboard.m'); ``` -Generates a readable `.m` file with `DashboardEngine` constructor and `addWidget` calls that recreates the dashboard. +Generates a readable `.m` file (a function returning a `DashboardEngine`) that recreates the dashboard. Multi‑page dashboards are emitted with `addPage()` and `switchPage()` calls. --- ## Theming -DashboardEngine uses `DashboardTheme`, which extends `FastSenseTheme` with dashboard-specific fields (widget backgrounds, border colors, status indicator colors, etc.). +DashboardEngine uses `DashboardTheme`, which extends `FastSenseTheme` with dashboard‑specific fields (widget backgrounds, border colours, group headers, status indicator colours, etc.). ```matlab d = DashboardEngine('My Dashboard'); -d.Theme = 'dark'; % or 'light', 'industrial', 'scientific', 'ocean' +d.Theme = 'dark'; % or 'light' d.render(); ``` -Available presets: `'default'`, `'dark'`, `'light'`, `'industrial'`, `'scientific'`, `'ocean'`. +Available presets: `'dark'` and `'light'`. Older presets (`'industrial'`, `'scientific'`, `'ocean'`) alias to `'light'`. -You can also override specific theme properties: +Override specific properties: ```matlab theme = DashboardTheme('dark', 'WidgetBackground', [0.1 0.1 0.2]); d.Theme = theme; ``` +Relevant dashboard‑specific theme fields: `DashboardBackground`, `WidgetBackground`, `WidgetBorderColor`, `ToolbarBackground`, `ToolbarFontColor`, `DragHandleColor`, `DropZoneColor`, `GridLineColor`, `GroupHeaderBg`, `GroupHeaderFg`, `GroupBorderColor`, `TabActiveBg`, `TabInactiveBg`, `StatusOkColor`, `StatusWarnColor`, `StatusAlarmColor`, `KpiFontSize`, `WidgetTitleFontSize`, etc. + --- ## Live Mode @@ -334,30 +468,41 @@ DashboardEngine supports live data updates via a timer that periodically calls ` ```matlab d = DashboardEngine('Live Monitor'); d.Theme = 'dark'; -d.LiveInterval = 2; % refresh every 2 seconds +d.LiveInterval = 2; % seconds d.addWidget('fastsense', 'Sensor', sTemp, 'Position', [1 1 24 8]); d.addWidget('number', 'Sensor', sTemp, 'Position', [1 9 12 2]); d.render(); -d.startLive(); % start periodic refresh +d.startLive(); % start periodic refresh % ... later -d.stopLive(); % stop +d.stopLive(); % stop ``` -You can also toggle live mode from the toolbar's Live button. The toolbar shows the last update timestamp when live mode is active. +Toggle live mode from the toolbar’s **Live** button (blue border when active). The toolbar shows the last‑update timestamp. + +### Stale‑Data Detection + +During live mode, the engine tracks the maximum timestamp of each widget. If a widget’s `tMax` does not advance for multiple ticks, a stale‑data banner appears below the toolbar listing the stalled widget titles. --- ## Global Time Controls -The time panel at the bottom of the dashboard has two sliders that control the visible time range across all widgets. Moving the sliders calls `setTimeRange(tStart, tEnd)` on each widget. +The time panel at the bottom (visible when `ShowTimePanel = true`) contains: + +- Two time‑range sliders +- An aggregate **envelope** (data‑preview) showing the min/max extents of all contributed widgets +- Event‑marker lines (if widgets expose events) +- Per‑widget down‑sampled line previews + +Moving the sliders or dragging the selection window broadcasts the time range to all widgets using `setTimeRange(tStart, tEnd)`. - **FastSenseWidget:** sets xlim on the FastSense axes - **EventTimelineWidget:** sets xlim on the timeline axes - **RawAxesWidget:** passes the time range to the PlotFcn -If a user manually zooms a specific widget, that widget detaches from global time (`UseGlobalTime = false`). Click the **Sync** button in the toolbar to re-attach all widgets. +If a user manually zooms a specific widget, that widget detaches from global time (`UseGlobalTime = false`). Click the **Sync** toolbar button to re‑attach all widgets. --- @@ -366,19 +511,19 @@ If a user manually zooms a specific widget, that widget detaches from global tim Click the **Edit** button in the toolbar to enter edit mode: 1. A **palette sidebar** appears on the left with buttons for each widget type -2. A **properties panel** appears on the right showing the selected widget's settings +2. A **properties panel** appears on the right showing the selected widget’s settings 3. **Drag handles** let you reposition widgets on the grid 4. **Resize handles** let you change widget dimensions 5. Click **Apply** to save property changes 6. Click **Done** to exit edit mode -The editor snaps to the 24-column grid. You can change the widget's title, position, axis labels, and data source directly in the properties panel. +The editor snaps to the 24‑column grid. You can change the widget’s title, position, axis labels, and data source directly in the properties panel. Widget management functions: -- `addWidget(type)` - add a new widget of the specified type -- `deleteWidget(idx)` - remove widget by index -- `selectWidget(idx)` - select a widget for property editing -- `setWidgetPosition(idx, pos)` - move/resize widget programmatically +- `addWidget(type)` – add a new widget of the specified type +- `deleteWidget(idx)` – remove widget by index +- `selectWidget(idx)` – select a widget for property editing +- `setWidgetPosition(idx, pos)` – move/resize widget programmatically --- @@ -392,18 +537,91 @@ d.InfoFile = 'dashboard_help.md'; % path to Markdown file d.render(); ``` -An **Info** button appears in the toolbar. Clicking it renders the Markdown file as HTML and opens it in the system browser. Supports basic Markdown syntax including headers, lists, code blocks, and tables. +An **Info** button appears in the toolbar. Clicking it renders the Markdown file as HTML (using the built‑in `MarkdownRenderer`) and opens it in the system browser. When no file is set, a placeholder page is shown. --- -## Complete Example +## Multi‑Page Dashboards + +Create tabbed pages within the same dashboard figure: + +```matlab +d = DashboardEngine('Multi‑Page'); +d.Theme = 'dark'; + +% Page 1 (default) +d.addWidget('number', 'Title', 'Page 1 Value', 'Position', [1 1 6 2], 'StaticValue', 1); + +% Page 2 +d.addPage('Details'); +d.addWidget('number', 'Title', 'Page 2 Value', 'Position', [1 1 6 2], 'StaticValue', 2); + +d.render(); +``` + +Pages are switched with `d.switchPage(idx)` or via the tab bar that appears automatically at the top. + +When calling `addWidget`, the widget goes to the currently active page (the one last added or set by `switchPage`). Serialisation exports and imports the entire page list. + +--- + +## Other Features + +### Detached Mirrors + +Pop any widget out as a standalone live mirror window: + +```matlab +d.detachWidget(widget); +``` + +The mirror window updates on every live tick and closes when the original dashboard is closed. + +### Image Export + +Save the dashboard figure as a PNG or JPEG: -This example creates a process monitoring dashboard with sensor-bound widgets: +```matlab +d.exportImage('output.png'); +% or via toolbar Image button +``` + +### ASCII Preview + +Print a text‑based approximation of the dashboard layout to the console: + +```matlab +d.preview(); +d.preview('Width', 120); +``` + +### Config Dialog + +Open a property editor for all DashboardEngine public properties (name, theme, live interval, progress mode, etc.) via the toolbar Config button. + +### Progress Bar + +By default Dashboards show a progress bar in the console during render: + +```matlab +d.ProgressMode = 'off'; % suppress +``` + +### Event Markers Toggle + +Hide or show event markers across all widgets globally: + +```matlab +d.EventMarkersVisible = false; % also togglable from toolbar +``` + +--- + +## Complete Example ```matlab install; -%% Generate data rng(42); N = 10000; t = linspace(0, 86400, N); % 24 hours @@ -419,10 +637,8 @@ sTemp.Units = 'degF'; sTemp.X = t; sTemp.Y = 74 + 3*sin(2*pi*t/3600) + randn(1,N)*1.2; sTemp.addStateChannel(scMode); -sTemp.addThresholdRule(struct('machine', 1), 78, ... - 'Direction', 'upper', 'Label', 'Hi Warn'); -sTemp.addThresholdRule(struct('machine', 1), 85, ... - 'Direction', 'upper', 'Label', 'Hi Alarm'); +sTemp.addThresholdRule(struct('machine', 1), 78, 'Direction', 'upper', 'Label', 'Hi Warn'); +sTemp.addThresholdRule(struct('machine', 1), 85, 'Direction', 'upper', 'Label', 'Hi Alarm'); sTemp.resolve(); % Pressure sensor @@ -441,7 +657,7 @@ d.LiveInterval = 5; % Header row: text + numbers + status d.addWidget('text', 'Title', 'Overview', 'Position', [1 1 4 2], ... - 'Content', 'Line 4 — Shift A', 'FontSize', 16); + 'Content', 'Line 4 – Shift A', 'FontSize', 16); d.addWidget('number', 'Title', 'Temperature', 'Position', [5 1 5 2], ... 'Sensor', sTemp, 'Format', '%.1f'); d.addWidget('number', 'Title', 'Pressure', 'Position', [10 1 5 2], ... @@ -451,7 +667,7 @@ d.addWidget('status', 'Title', 'Temp', 'Position', [15 1 5 2], ... d.addWidget('status', 'Title', 'Press', 'Position', [20 1 5 2], ... 'Sensor', sPress); -% Plot row: sensor-bound FastSense widgets +% Plot row: sensor‑bound FastSense widgets d.addWidget('fastsense', 'Position', [1 3 12 8], 'Sensor', sTemp); d.addWidget('fastsense', 'Position', [13 3 12 8], 'Sensor', sPress); @@ -472,7 +688,7 @@ d.save(fullfile(tempdir, 'process_dashboard.json')); ## See Also -- [[API Reference: Dashboard]] -- Full API reference for all dashboard classes -- [[API Reference: Sensors]] -- Sensor, StateChannel, ThresholdRule -- [[Live Mode Guide]] -- Live data polling +- [[API Reference: Dashboard]] -- Full API reference for all dashboard classes +- [[API Reference: Sensors]] -- Sensor, StateChannel, ThresholdRule +- [[Live Mode Guide]] -- Live data polling - [[Examples]] -- `example_dashboard_engine`, `example_dashboard_all_widgets` diff --git a/wiki/Home.md b/wiki/Home.md index 13a0f418..5c80ff9c 100644 --- a/wiki/Home.md +++ b/wiki/Home.md @@ -19,25 +19,26 @@ FastPlot consists of five integrated libraries: | Library | Description | |---------|-------------| -| **FastSense** | Core plotting engine with dynamic downsampling, dashboard layouts (FastSenseGrid, FastSenseDock), interactive toolbar, themes, and disk-backed storage via FastSenseDataStore | -| **Dashboard** | Widget-based dashboard engine with 8 widget types, 24-column responsive grid, edit mode, and JSON persistence | -| **SensorThreshold** | Sensor data containers with state-dependent threshold rules, violation detection, and SensorRegistry catalog | -| **EventDetection** | Event detection from threshold violations, EventViewer with Gantt timeline, live pipeline with notifications | -| **WebBridge** | TCP server for web-based visualization with NDJSON protocol | +| **FastSense** | Core plotting engine with dynamic downsampling, tiled (`FastSenseGrid`) and tabbed (`FastSenseDock`) layouts, interactive toolbar, theme‑based styling, disk‑backed storage via `FastSenseDataStore`, and live file‑polling. | +| **Dashboard** | Widget‑based dashboard engine with edit mode, JSON persistence, a 24‑column responsive grid, and 20+ widget types (`FastSenseWidget`, `GaugeWidget`, `StatusWidget`, `NumberWidget`, `SparklineCardWidget`, etc.). | +| **SensorThreshold** | Tag‑based domain model for sensor data (`SensorTag`), discrete states (`StateTag`), derived binary monitors (`MonitorTag`), composite aggregations (`CompositeTag`), and a centralised `TagRegistry` catalogue. | +| **EventDetection** | Event detection from threshold violations, persistent `EventStore`, `EventViewer` with Gantt timeline, incremental streaming via `MonitorTag.appendData`, and live pipeline with notifications. | +| **WebBridge** | TCP server for web‑based data relay using NDJSON protocol, enabling direct MATLAB → external clients communication. | ## Features -- **Smart downsampling** — per-pixel MinMax and LTTB algorithms, auto-selected per zoom level -- **Pyramid cache** — multi-resolution pre-computation for instant zoom-out on 50M+ datasets -- **MEX acceleration** — optional C with SIMD (AVX2/NEON), auto-fallback to pure MATLAB -- **Dashboard layouts** — tiled grids (FastSenseGrid) and tabbed containers (FastSenseDock) -- **Interactive toolbar** — data cursor, crosshair, grid/legend toggle, autoscale, PNG export -- **6 built-in themes** — default, dark, light, industrial, scientific, ocean -- **Linked axes** — synchronized zoom/pan across subplots -- **Sensor system** — state-dependent thresholds with condition-based rules and violation markers -- **Event detection** — group violations into events with statistics, Gantt viewer, click-to-plot -- **Live mode** — file polling with auto-refresh (preserve/follow/reset view modes) -- **Disk-backed storage** — SQLite-backed chunked DataStore for 100M+ point datasets +- **Smart downsampling** — per‑pixel MinMax and LTTB algorithms, user‑selectable per line +- **Pyramid cache** — multi‑resolution pre‑computation for instant zoom‑out on datasets of 50M+ points +- **MEX acceleration** — optional C with SIMD (AVX2/NEON), automatic fallback to pure MATLAB +- **Dashboard layouts** — tiled grids (`FastSenseGrid`) and tabbed containers (`FastSenseDock`) +- **Interactive toolbar** — data cursor, crosshair, grid/legend toggle, Y‑autoscale, PNG export, live mode controls +- **2 built‑in themes** — `light` and `dark` with 4 colour palettes (vibrant, muted, colorblind, ocean) + Legacy preset names (`default`, `industrial`, `scientific`, `ocean`) are automatically aliased to `light`. +- **Linked axes** — synchronised zoom/pan across subplots via `LinkGroup` +- **Tag‑based threshold monitoring** — `MonitorTag` with hysteresis, debounce (MinDuration), and streaming tail append +- **Event detection** — groups violations into events with statistics, Gantt viewer, click‑to‑detail plot +- **Live mode** — file polling with auto‑refresh (preserve/follow/reset view modes) +- **Disk‑backed storage** — SQLite‑backed chunked `FastSenseDataStore` for 100M+ point datasets ## Quick Start @@ -71,19 +72,15 @@ fig.renderAll(); ``` ```matlab -% Sensor with state-dependent thresholds -s = Sensor('pressure', 'Name', 'Chamber Pressure'); -s.X = linspace(0, 100, 1e6); -s.Y = randn(1, 1e6) * 10 + 50; - -sc = StateChannel('machine'); -sc.X = [0 30 60 80]; sc.Y = [0 1 2 1]; -s.addStateChannel(sc); -s.addThresholdRule(struct('machine', 1), 70, 'Direction', 'upper', 'Label', 'Run HI'); -s.resolve(); - -fp = FastSense('Theme', 'industrial'); -fp.addSensor(s, 'ShowThresholds', true); +% Using the Tag API: create a sensor and render it +st = SensorTag('pressure', ... + 'X', linspace(0, 100, 1e6), ... + 'Y', randn(1, 1e6)*10 + 50, ... + 'Units', 'bar'); +TagRegistry.register('pressure', st); + +fp = FastSense('Theme', 'dark'); +fp.addTag(st); fp.render(); ``` @@ -95,20 +92,20 @@ fp.render(); ## Getting Started -Start with the [[Installation]] guide to set up FastPlot and compile MEX acceleration. Then follow the [[Getting Started]] tutorial for step-by-step examples covering basic plotting, dashboards, sensors, and live mode. +Start with the [[Installation]] guide to set up FastPlot and compile MEX acceleration. Then follow the [[Getting Started]] tutorial for step‑by‑step examples covering basic plotting, dashboards, sensors, and live mode. ## API Reference **Core Classes** - [[API Reference: FastPlot]] — main plotting engine with dynamic downsampling -- [[API Reference: Dashboard]] — FastSenseGrid, FastSenseDock, FastSenseToolbar -- [[API Reference: Sensors]] — Sensor, StateChannel, ThresholdRule, SensorRegistry -- [[API Reference: Event Detection]] — EventDetector, EventViewer, LiveEventPipeline -- [[API Reference: Themes]] — theme presets, customization, color palettes -- [[API Reference: Utilities]] — ConsoleProgressBar, FastSenseDefaults +- [[API Reference: Dashboard]] — `FastSenseGrid`, `FastSenseDock`, `FastSenseToolbar`, `DashboardEngine` and all widget types +- [[API Reference: Sensors]] — `Tag`, `SensorTag`, `StateTag`, `MonitorTag`, `CompositeTag`, `TagRegistry`, pipeline pipelines +- [[API Reference: Event Detection]] — `EventDetector`, `EventStore`, `EventViewer`, `LiveEventPipeline` +- [[API Reference: Themes]] — theme presets, customisation, colour palettes +- [[API Reference: Utilities]] — `ConsoleProgressBar`, `FastSenseDefaults` **Specialized Guides** - [[Live Mode Guide]] — file polling, view modes, live dashboards -- [[Dashboard Engine Guide]] — DashboardEngine with widget-based dashboards +- [[Dashboard Engine Guide]] — widget‑based dashboards with edit mode and persistence - [[Datetime Guide]] — working with time series data - [[Examples]] — 40+ categorized runnable examples