Skip to content

API Reference: Dashboard

github-actions[bot] edited this page Jun 10, 2026 · 57 revisions

API Reference: Dashboard

DashboardEngine --- Top-level dashboard orchestrator.

Inherits from: handle

Constructor

obj = DashboardEngine(name, varargin)

Properties

Property Default Description
Name ''
Theme 'light'
LiveInterval 5
InfoFile ''
ProgressMode 'auto' 'auto' | 'on' | 'off' — render progress bar visibility
ShowTimePanel true hide the bottom time slider panel
EventMarkersVisible true global toggle for event markers across all widgets (runtime UI state, not serialized)
DebugPreview_ false 260508-das — opt-in: surface preview/marker pipeline failures as warnings
BannerHeight 0.035
EventStore []
WidgetHovers_ {}

Methods

pg = addPage(obj, name)

ADDPAGE Add a named page and make it the active page for addWidget. pg = d.addPage('Overview') creates a DashboardPage and appends it to Pages. Sets ActivePage to the last-added page index. When Pages is non-empty, addWidget routes to the active page.

setEventMarkersVisible(obj, tf)

SETEVENTMARKERSVISIBLE Globally show/hide event markers on every widget. Iterates every widget (across all pages in multi-page mode) and calls setEventMarkersVisible(tf) on any widget that implements it. Unsupported widgets are silently skipped. Also updates the toolbar indicator if a Toolbar is present. Default state on dashboard create is true so existing scripts are unaffected.

switchPage(obj, pageIdx)

SWITCHPAGE Switch the active page using panel visibility toggling. d.switchPage(2) sets ActivePage = 2 and toggles panel visibility.

w = addWidget(obj, type, varargin)

Accept a pre-constructed widget object directly

w = addCollapsible(obj, label, children, varargin)

ADDCOLLAPSIBLE Convenience: add a GroupWidget with Mode='collapsible'. w = d.addCollapsible('Sensors', {w1, w2}) w = d.addCollapsible('Sensors', {w1, w2}, 'Collapsed', true)

t = getCachedTheme(obj)

GETCACHEDTHEME Return cached theme struct, recomputing only when Theme changes.

render(obj)

startLive(obj)

stopLive(obj)

Clear IsLive FIRST so any in-flight onLiveTimerError callback does not re-start(obj.LiveTimer) on the timer we are about to delete (observed on CI as a runaway 500k+ stderr loop in testTimerContinuesAfterError). Then stop/delete the timer with isvalid + try/catch guards, matching LiveTagPipeline.stop().

store = attachPlantLog(obj, filePath, varargin)

ATTACHPLANTLOG Attach a plant log to this dashboard (PLOG-INT-01). store = engine.attachPlantLog(filePath) reads filePath using PlantLogReader.autoDetect for the column mapping, ingests every parseable row into a new PlantLogStore, starts a PlantLogLiveTail timer (default Interval=5s, StartTail=true), wires the slider + per-widget overlay refresh path, and returns the store handle.

detachPlantLog(obj)

DETACHPLANTLOG Remove the attached plant log + all overlays + live tail (PLOG-INT-02). Idempotent: calling on an engine with no plant log attached is a no-op.

save(obj, filepath)

cfg = stampPlantLogIntoConfig_(obj, cfg)

STAMPPLANTLOGINTOCONFIG_ Phase 1033 PLOG-INT-04: add plantLog key when attached. When no plant log is attached, cfg is returned unchanged (omit-when-empty contract -- byte-identical back-compat for v1.0-v3.0 dashboards).

exportScript(obj, filepath)

exportImage(obj, filepath, format)

EXPORTIMAGE Save the rendered dashboard figure as PNG or JPEG at 150 DPI. d.exportImage('out.png') % format inferred from extension d.exportImage('out.png', 'png') d.exportImage('out.jpg', 'jpeg')

preview(obj, varargin)

PREVIEW Print ASCII representation of the dashboard to console. d.preview() % default 120 chars wide d.preview('Width', 120) % custom width

showInfo(obj)

SHOWINFO Display the linked Markdown info file in a browser. When InfoFile is empty, displays a built-in placeholder page describing how to attach a custom info file.

writeAndOpenInfoHtml(obj, html)

WRITEANDOPENINFOHTML Write rendered HTML to the cached temp file and open it.

showInfoModal_(obj, html)

SHOWINFOMODAL_ Render the info HTML in an in-app modal uifigure (260508-n8h). Replaces the previous browser handoff via web(). Reuses an existing modal if still open so repeated Info-button clicks refocus rather than stack windows. Falls back silently on uifigure construction errors (older MATLAB releases without uihtml support keep the temp HTML file as the user-facing artifact).

onInfoModalResize_(~, src, hHtml)

ONINFOMODALRESIZE_ Keep the uihtml panel filling the modal figure.

onInfoModalClose_(obj, src)

ONINFOMODALCLOSE_ Clear the cached modal handle and dispose the figure.

md = buildPlaceholderInfoMarkdown(obj)

BUILDPLACEHOLDERINFOMARKDOWN Default info page shown when no InfoFile is set.

cleanupInfoTempFile(obj)

CLEANUPINFOTEMPFILE Delete the temporary HTML file if it exists.

removeWidget(obj, idx)

REMOVEWIDGET Remove widget at given index and re-layout.

removePage(obj, idx)

REMOVEPAGE Remove the page at index idx, keeping ActivePage valid. Mirror of removeWidget for pages. Throws DashboardEngine:invalidIndex on a bad index. Deletes the page's widgets and the page, adjusts ActivePage (decrements when removing a page before it; clamps when removing the active page; resets to 0 when no pages remain), and re-renders when a figure is live.

setWidgetPosition(obj, idx, pos)

SETWIDGETPOSITION Set the grid position of a widget by index. Clamps width to grid columns and resolves overlaps with other widgets. Operates on the active page in multi-page mode.

w = getWidgetByTitle(obj, title)

GETWIDGETBYTITLE Find a widget by its Title property. Searches every page in multi-page mode (active page or single-page Widgets otherwise). Returns the widget object, or empty if not found.

detachWidget(obj, widget)

DETACHWIDGET Pop a widget out as a standalone figure window.

removeDetached(obj)

REMOVEDETACHED Remove stale mirrors from the registry.

setContentArea(obj, contentArea)

SETCONTENTAREA Update the Layout content area. Provided so that DashboardBuilder can modify the layout without direct write-access to the Layout property (required for Octave compatibility).

[effToolbarH, effPageBarH, effTimeH] = applyChromeVisibility(obj, toolbarH, pageBarH)

APPLYCHROMEVISIBILITY Set chrome Visible state + return effective heights. Respects ShowToolbar and ShowTimePanel flags. Returns the heights that should be used for the content-area calculation (0 when the corresponding chrome element is hidden).

applyVisibilityAndRelayout(obj)

APPLYVISIBILITYANDRELAYOUT Re-apply ShowToolbar/ShowTimePanel + re-layout widgets.

applyThemeToChrome(obj)

APPLYTHEMETOCHROME Restyle figure + non-widget chrome using the current Theme. Widget panels are NOT touched here — call rerenderWidgets() after this method to recreate widget content with the new theme.

rerenderWidgets(obj)

RERENDERWIDGETS Delete all widget panels and recreate them. Mark in-flight so the SizeChangedFcn that fires during panel teardown/recreate doesn't schedule new resize-debounce timers — that would cause a recursive rerender cascade. Also cancel any timers that ARE currently scheduled — they are about to be invalidated by this rerender anyway. (260513-q7w fu2)

setTimeWindow(obj, t0, t1)

SETTIMEWINDOW Set the global load window and re-resolve all widgets. t0, t1 datenum scalars; both [] resets to full range. User-triggered (e.g. from the companion time bar) -- NOT called per live tick. Fans the window out to every widget via allPageWidgets(), then rebuilds widget panels once so they reload windowed data. Does NOT touch the scrubber's data range (no updateGlobalTimeRange call).

w = getTimeWindow(obj)

GETTIMEWINDOW Return the current stored load window. Returns [] when no window is set (full-range default). Used by tests and the companion to read back the stored window.

updateGlobalTimeRange(obj)

UPDATEGLOBALTIMERANGE Scan all widgets for data time bounds.

updateLiveTimeRange(obj)

UPDATELIVETIMERANGE Update DataTimeRange without resetting sliders. Called during live mode to expand the time range as data grows.

newTMax = updateLiveTimeRangeFrom(obj, ws)

UPDATELIVETIMERANGEFROM Update DataTimeRange from pre-fetched widget list. Like updateLiveTimeRange but accepts ws to avoid re-fetching activePageWidgets(). Returns the new tMax (or NaN when no widget has finite time data).

createStaleBanner(obj, theme, toolbarH)

CREATESTALEBANNER Create the hidden stale-data warning banner. Permanent reserved strip at the very TOP of the figure. Toolbar, page tabs, and content area all sit BELOW this strip — the banner is never an overlay (260508-jyh). Hidden by default; shown when staleness is detected and not previously dismissed by the user. toolbarH is retained for signature compat; banner now lives in the reserved top strip independent of chrome heights.

repositionStaleBanner_(obj)

REPOSITIONSTALEBANNER_ Park banner in the reserved top strip. Banner now lives in a permanent strip at the figure top; no chrome-height dependence (260508-jyh). Safe to call before render or after teardown — no-ops when the handle is empty/invalid.

showStaleBanner(obj, staleTitles)

SHOWSTALEBANNER Display the warning listing the widgets without new data. staleTitles is a cell array of widget Title strings whose tMax did not advance on the last live tick.

hideStaleBanner(obj)

HIDESTALEBANNER Clear the stale-data warning overlay.

onStaleBannerClose(obj)

ONSTALEBANNERCLOSE User dismissed the warning; stay hidden until data resumes.

msg = buildStaleMessage(obj, staleTitles, intervalStr)

BUILDSTALEMESSAGE Compose the banner text listing stale widgets.

staleTitles = detectStaleWidgets(obj, ws)

DETECTSTALEWIDGETS Return titles of widgets whose tMax did not advance. Updates LastTMaxPerWidget_ with the current observation.

broadcastTimeRange(obj, tStart, tEnd)

BROADCASTTIMERANGE Push time range to widgets across ALL pages (not just active). Time sync is a dashboard-wide control: dragging the slider, clicking "Sync all", or calling broadcastTimeRangeNow updates every page's widgets so switching tabs preserves the synced window. Per-widget UseGlobalTime=false (manually zoomed) widgets opt out via their own setTimeRange guard. (260508-llw — was activePageWidgets, caused a per-tab desync bug.)

resetGlobalTime(obj)

RESETGLOBALTIME Re-attach all widgets across ALL pages to global time and apply. (260508-llw — was activePageWidgets, leaving widgets on inactive pages still detached after a "Reset" toolbar action.)

realizeBatch(obj, batchSize)

REALIZEBATCH Render widgets in batches with drawnow between.

[idx, name] = activePageLabel(obj)

ACTIVEPAGELABEL Index and name of the active page, or (1, '') if single-page.

onScrollRealize(obj, topRow, bottomRow)

ONSCROLLREALIZE Realize widgets that scroll into view.

onLiveTick(obj)

markAllDirty(obj)

MARKALLDIRTY Flag all widgets as needing refresh. Called on theme change, figure resize, or other global state changes. Covers every page in multi-page mode.

onResize(obj)

ONRESIZE Handle figure resize: reposition all widget panels.

clearRerenderFlag_(obj)

CLEARRERENDERFLAG_ Reset IsRerendering_ via onCleanup so it always lands false even if rerenderWidgets throws. (260513-q7w fu2)

cancelResizeTimers_(obj)

CANCELRESIZETIMERS_ Stop + delete both resize-related debounce timers. Called from switchPage so a stale backstop scheduled for the previous page doesn't fire after the user has moved to a different tab; also called from rerenderWidgets so the spurious SizeChangedFcn that fires during panel teardown doesn't reschedule us into a cascade. (260513-q7w fu2)

scheduleResizeRefresh_(obj)

SCHEDULERESIZEREFRESH_ Coalesce rapid resize events into a single deferred refresh, mirroring the SliderDebounceTimer pattern. Drag-resize on macOS fires many SizeChangedFcn events per second; doing a full widget refresh on each would be expensive and visibly stutter. Instead, restart a 300 ms one-shot timer on every resize event — once the user stops dragging, the timer fires and refreshes all active-page widgets one time. (260513-q7w)

scheduleResizeFinalRedraw_(obj)

SCHEDULERESIZEFINALREDRAW_ Longer-period backstop debouncer that fires once the user has clearly stopped resizing for ~1.2 seconds, and unconditionally calls rerenderWidgets() — the same operation the user would have invoked manually via the toolbar's Reset button. Runs IN PARALLEL with the cheap scheduleResizeRefresh_ (300 ms): both timers restart on every resize event, so during continuous drag neither fires; the moment dragging stops, the 300 ms cheap pass runs first and handles most cases, then this 1.2 s backstop catches any residual failure mode (degenerate axes after holding at very small sizes, destroyed line handles, etc.). (260513-q7w fu)

finalRedrawAfterResize_(obj)

FINALREDRAWAFTERRESIZE_ Unconditional full rebuild of all panels on the active page after resize fully settles. Equivalent to the user pressing the toolbar's Reset button. Bulletproof catch-all for any failure mode the cheap two-pass refresh missed. (260513-q7w fu)

refreshActivePageWidgetsAfterResize_(obj)

REFRESHACTIVEPAGEWIDGETSAFTERRESIZE_ Re-push data through every realized widget on the active page after a resize, so any widget whose line data was wiped by a resize-race recovers without the user having to press Reset. (260513-q7w)

tf = isWidgetLineWhite_(~, w)

ISWIDGETLINEWHITE_ True if the FastSenseWidget's first line has no XData but its bound Tag clearly does — the visible manifestation of the resize-race bug. Defensive: any missing-handle / invalid-object case returns false to avoid false-positive escalations.

triggerTimeSlidersChangedForTest(obj)

TRIGGERTIMESLIDERSCHANGEDFORTEST Test-only hook to invoke the slider callback without going through UI events. Exposes the private onTimeSlidersChanged() debounce path to tests. (Hidden, not the narrower Access = {?matlab.unittest.TestCase}, so Octave parsing survives — Octave has no matlab.unittest.)

broadcastTimeRangeNow(obj, tStart, tEnd)

BROADCASTTIMERANGENOW Test-only synchronous broadcast bypassing the SliderDebounceTimer. Stock Octave 7 batch mode has unreliable timer scheduling; tests should use this entry point to drive the broadcast deterministically. Also updates the time labels (skipping the debounced onRangeSelectorChanged path).

env = computePreviewEnvelopeForTest(obj, nBuckets)

COMPUTEPREVIEWENVELOPEFORTEST Test-only wrapper around the private computePreviewEnvelopeReturning_. Runs the real aggregation and returns the envelope struct so tests can assert shape/monotonicity without scraping the selector's patch handles. When nBuckets is omitted, uses the method's own width-derived default.

setPlantLogStoreForTest_(obj, store)

SETPLANTLOGSTOREFORTEST_ Phase 1031 test seam — replaced by attachPlantLog in Phase 1033. Inject a PlantLogStore (or [] to detach) and immediately recompute plant-log slider markers so callers can assert on the slider state right after attach without waiting for a refresh hook.

setPlantLogLiveTailForTest_(obj, tail)

SETPLANTLOGLIVETAILFORTEST_ Phase 1031 test seam — wires PlantLogTailTick to refresh. Inject a PlantLogLiveTail (or [] to detach + tear down listener). When non-empty, installs an addlistener that calls computePlantLogMarkers on every PlantLogTailTick so the slider refreshes without a full dashboard re-render (PLOG-VIZ-08).

refreshPlantLogOverlayForWidgetForTest_(obj, widget)

REFRESHPLANTLOGOVERLAYFORWIDGETFORTEST_ Phase 1032 test seam. Routes to refreshPlantLogOverlayForWidget_ from function-style tests (which can't satisfy the {?FastSenseWidget, ?matlab.unittest.TestCase} access list). Hidden so it doesn't show up in methods(obj).

clearPlantLogOverlaysOnAllWidgetsForTest_(obj)

CLEARPLANTLOGOVERLAYSONALLWIDGETSFORTEST_ Phase 1032 test seam. Routes to clearPlantLogOverlaysOnAllWidgets_ from function-style tests. Hidden test seam mirroring the Phase 1031 idiom.

attachPlantLogXLimListenerForTest_(obj, widget)

ATTACHPLANTLOGXLIMLISTENERFORTEST_ Phase 1032 test seam. Routes to attachPlantLogXLimListener_ from function-style tests.

updateCurrentViewIndicatorForTest_(obj)

UPDATECURRENTVIEWINDICATORFORTEST_ Phase 1039 test seam. Routes to the private updateCurrentViewIndicator_ so class-based tests can assert the show/hide decision without depending on the Octave-skipped XLim PostSet listener. Mirrors the existing attachPlantLogXLimListenerForTest_ / setTimeRangeSelectorForTest_ idiom.

setTimeRangeSelectorForTest_(obj, sel)

SETTIMERANGESELECTORFORTEST_ Phase 1031 test seam — inject a TimeRangeSelector handle without going through render(). Used by TestPlantLogSliderOverlay to assert hPlantLogMarkers state without paying full-dashboard render cost. The TimeRangeSelector_ property is Access = private, so direct assignment from a test is impossible — this hidden setter is the documented seam. Phase 1033's review may remove it once render() pathways cover the new test cases.

ws = activePageWidgets(obj)

ACTIVEPAGEWIDGETS Return the widget list for the currently active page. Returns obj.Pages{obj.ActivePage}.Widgets in multi-page mode, or obj.Widgets in single-page mode.

ws = allPageWidgets(obj)

ALLPAGEWIDGETS Return concatenation of all pages' Widgets. Used for ReflowCallback injection and Follow toggle sweep. When Pages is empty, returns obj.Widgets.

linked = collectLinkedCrosshairs_(obj, widgets)

COLLECTLINKEDCROSSHAIRS_ Enumerate linked+rendered crosshairs on active page (260602-mri). linked = collectLinkedCrosshairs_(obj, widgets) flattens widgets via flattenWidgetsForPreview_ and returns a cell array of structs: {struct('widget', w, 'hc', hc), ...} for every flattened FastSenseWidget with CrosshairLinked=true AND a valid rendered HoverCrosshair_. Widgets failing any guard are silently skipped. PURE (no side effects) so it is unit-testable with a hand-built widget list. Made public (Access=public) so tests and DashboardLayout can call it.

rewireCrosshairLinks_(obj)

REWIRECEOSSHAIRLINKS_ Re-prime BroadcastFcn_ on active-page linked crosshairs (260602-mri).

  1. Clear BroadcastFcn_/BroadcastLeaveFcn_ on ALL active-page FastSense crosshairs (handles toggled-OFF widgets + previous-page crosshairs).
  2. For each currently-linked+rendered crosshair, install the engine broadcast callbacks. Must be called after rerenderWidgets (fresh HoverCrosshair_ handles), after switchPage, and after detachWidget. Wrapped in try/catch at call sites; inner per-handle errors are silently skipped so a single bad crosshair never breaks the whole sweep.

broadcastCrosshairX_(obj, sourceHc, xQuery)

BROADCASTCROSSHAIRX_ Mirror xQuery onto all OTHER linked crosshairs on active page (260602-mri). Fired at end of source crosshair's onMove (via BroadcastFcn_). Re-collects the linked set each call (cheap; active page only; upstream throttle limits call rate to ~40 Hz; N widgets small).

broadcastCrosshairLeave_(obj, sourceHc)

BROADCASTCROSSHAIRLEAVE_ Tell all OTHER linked crosshairs to hide (260602-mri). Fired at end of source crosshair's onLeave (via BroadcastLeaveFcn_).

onCrosshairLinkToggle(obj, widget)

ONCROSSHAIRLINKTOGGLE Called by DashboardLayout after widget.setCrosshairLink(tf) (260602-mri). Re-derives the whole active-page link set from current flags — idempotent. Wrapped in try/catch so a single toggle failure never crashes the bar.

notifyEventsChanged(obj)

NOTIFYEVENTSCHANGED Refresh all event-aware widgets after store mutation (260513-snt). Called after CreateEventDialog persists a new event. Walks the active page (recursing into GroupWidget children via getNestedWidgets) and refreshes every EventTimelineWidget and FastSenseWidget. Also re-aggregates the slider event-marker overlay via computeEventMarkers and the slider preview lines via computePreviewEnvelope so a freshly-added event becomes visible on the slider strip without waiting for the next live tick.

refreshPlantLogOverlayForWidget_(obj, widget)

REFRESHPLANTLOGOVERLAYFORWIDGET_ Recompute plant-log overlay for one widget (Phase 1032 PLOG-VIZ-04 + PLOG-VIZ-08). Idempotent: safe to call when widget.ShowPlantLog=false (clears markers), when the engine has no store (clears markers), or when the widget's FastSenseObj is not rendered (no-op).

clearPlantLogOverlaysOnAllWidgets_(obj)

CLEARPLANTLOGOVERLAYSONALLWIDGETS_ Wipe markers on every widget + every detached mirror (Phase 1032). Does NOT flip ShowPlantLog on any widget — user state is preserved for re-attach. Called from Phase 1033's detachPlantLog() entry point and from store swaps that need to nuke stale per-widget markers.

attachPlantLogXLimListener_(obj, widget)

ATTACHPLANTLOGXLIMLISTENER_ Wire an XLim PostSet listener on the widget's axes (Phase 1032). Stored in widget.PlantLogXLimListener_; deleted by setShowPlantLog(false) AND by widget.delete(). Idempotent: replaces any prior listener.

attachCurrentViewXLimListener_(obj, widget)

ATTACHCURRENTVIEWXLIMLISTENER_ XLim PostSet listener -> current-view box refresh (Phase 1039). Stored in widget.CurrentViewXLimListener_; released by widget.delete(). Idempotent: replaces any prior listener. Octave skips (its addlistener lacks the 4-arg PostSet form) — the live tick + post-broadcast + page-switch hooks still refresh the box there. Mirrors attachPlantLogXLimListener_.

attachPlantLogWidgetHover_(obj, widget)

ATTACHPLANTLOGWIDGETHOVER_ Lazy-construct a PlantLogWidgetHover for one widget (Phase 1032 PLOG-VIZ-07). Tears down any prior hover for this widget first (idempotent), then builds a new PlantLogWidgetHover parented to the widget's uifigure ancestor and storing the lookup closure that routes through obj.lookupPlantLogEntries_ (re-reads the store at call time so subsequent swaps are reflected immediately).

detachPlantLogWidgetHover_(obj, widget)

DETACHPLANTLOGWIDGETHOVER_ Tear down + remove a widget's hover (Phase 1032 PLOG-VIZ-07). Idempotent: safe when widget has no hover currently registered. Also sweeps stale-widget pairs (widget already destroyed) so the WidgetHovers_ list stays compact.

str = formatTimeVal(~, t)

FORMATTIMEVAL Format a numeric time value as a human-readable string. Supports three numeric ranges: posix epoch seconds (9e8 < t < 5e9) — fast arithmetic via datevec MATLAB datenum (t > 700000, not posix) — fast via datevec raw numeric (t <= 700000) — formats as s/m/h/d suffix via sprintf

Static Methods

DashboardEngine.types = widgetTypes()

WIDGETTYPES Supported widget types + descriptions, as an Nx2 cell. Derived from DashboardWidgetRegistry.types() (the single source of truth), so it can no longer drift from what addWidget accepts, and user-registered types (registerWidgetType) appear automatically with a generic description.

DashboardEngine.registerWidgetType(type, ctorHandle)

REGISTERWIDGETTYPE Register a custom widget type with the dashboard. DashboardEngine.registerWidgetType('mytype', @MyWidget) makes 'mytype' usable through addWidget, serialization, and detach — the documented extension point for third-party widgets. The widget class must subclass DashboardWidget and provide a static fromStruct. Errors DashboardWidgetRegistry:duplicateType on a name collision.

DashboardEngine.obj = load(filepath, varargin)


DashboardBuilder --- Edit mode overlay for dashboard GUI.

Inherits from: handle

Provides drag/resize overlays, a widget palette sidebar, and a properties panel. Activated via the Edit button in DashboardToolbar.

builder = DashboardBuilder(engine); builder.enterEditMode(); builder.exitEditMode();

Constructor

obj = DashboardBuilder(engine)

Properties

Property Default Description
IsActive false
MockCurrentPoint []

Methods

enterEditMode(obj)

exitEditMode(obj)

selectWidget(obj, idx)

w = addIconCard(obj, varargin)

ADDICONCARD Add an IconCardWidget via the builder.

w = addChipBar(obj, varargin)

ADDCHIPBAR Add a ChipBarWidget via the builder.

w = addSparkline(obj, varargin)

ADDSPARKLINE Add a SparklineCardWidget via the builder.

addWidget(obj, type)

deleteWidget(obj, idx)

deleteSelected(obj)

applyProperties(obj)

pos = findNextSlot(obj, type)

onDragStart(obj, widgetIdx)

onResizeStart(obj, widgetIdx)

onMouseMove(obj)

onMouseUp(obj)


DashboardWidget --- Abstract base class for all dashboard widgets.

Inherits from: handle

Subclasses must implement: render(parentPanel) — create graphics objects inside the panel refresh() — update data/display (called by live timer) getType() — return widget type string (e.g. 'fastsense')

Subclasses must also provide a static fromStruct(s) method.

Constructor

obj = DashboardWidget(varargin)

Map legacy 'Sensor' NV pair to 'Tag' for backward compat of serialized dashboards.

Properties

Property Default Description
Title '' Widget title displayed in header
Position [1 1 6 2] [col, row, width, height] in grid units
ThemeOverride struct() Per-widget theme overrides (merged on top of dashboard theme)
UseGlobalTime true false when user manually zooms this widget
Description '' Doc text shown in a popup when the widget's info (i) button is clicked
Tag [] v2.0 Tag API — any Tag subclass
ParentTheme [] Theme inherited from DashboardEngine
Dirty true true when widget needs refresh (data changed)
hPanel [] Handle to the panel where this widget's content lives.
hCellPanel [] Handle to the outer grid-cell uipanel that owns

Methods

t = get()

s = get()

GET.SENSOR Backward-compat alias for Tag (v1.x API).

set()

SET.SENSOR Backward-compat alias — maps to Tag property.

s = toStruct(obj)

markDirty(obj)

MARKDIRTY Flag this widget as needing a refresh.

markRealized(obj)

MARKREALIZED Mark this widget as having been rendered.

markUnrealized(obj)

MARKUNREALIZED Mark this widget as needing re-render.

setTimeRange(~, ~, ~)

Override in subclasses to respond to global time changes.

[tMin, tMax] = getTimeRange(~)

Override in subclasses to report data time range.

series = getPreviewSeries(~, ~)

GETPREVIEWSERIES Optional preview data for the time-range envelope. series = getPreviewSeries(obj, nBuckets) returns a struct with fields xCenters, yMin, yMax — each a 1xnBuckets row vector; yMin/yMax MUST be normalized to [0,1] within the widget's own y-range. Base returns [] to opt out of the preview envelope.

t = getEventTimes(~)

GETEVENTTIMES Optional list of event times for the time-slider overlay. t = getEventTimes(obj) returns a row vector of event start times in the dashboard's time axis. Override to expose events to the TimeRangeSelector event-marker overlay; base returns [] so widgets without events contribute nothing.

children = getNestedWidgets(~)

GETNESTEDWIDGETS Optional list of nested DashboardWidgets for engine traversal. children = getNestedWidgets(obj) returns a cell array of DashboardWidget subclasses that this widget logically contains (e.g., a GroupWidget's Children + Tabs widgets). The DashboardEngine uses this to flatten the active-page widget tree when collecting preview series and event markers so that data/events inside container widgets contribute to the slider overlay. Base returns {} — leaf widgets are not containers.

lines = asciiRender(obj, width, height)

ASCIIRENDER Return ASCII representation of this widget. lines = asciiRender(obj, width, height) returns a cell array of strings, each exactly WIDTH characters. HEIGHT is the available number of lines. Default implementation shows [type] Title; subclasses override for richer content.

render(~, ~)

refresh(~)

t = getType(~)


DashboardLayout --- Manages 24-column responsive grid positioning.

Inherits from: handle

Converts widget grid positions [col, row, width, height] to normalized canvas coordinates [x, y, w, h]. Handles overlap resolution, row calculation, and scrollable canvas when content exceeds the viewport.

Constructor

obj = DashboardLayout(varargin)

Properties

Property Default Description
Columns 24
TotalRows 4
ContentArea [0 0 1 1]
Padding [0 0 0 0]
GapH 0
GapV 0
RowHeight 0.22
ScrollbarWidth 0.015
OnScrollCallback [] function handle: @(topRow, bottomRow)
DetachCallback [] function handle: @(widget) — set by DashboardEngine
CreateEventCallback [] function handle: @(widget) — set by DashboardEngine
VisibleRows [1 Inf] [topRow bottomRow] currently visible
EngineRef [] Phase 1032 PLOG-VIZ-05 — back-reference to DashboardEngine for chrome callbacks (addPlantLogToggle)
hFigure [] Figure handle for popup dismiss callbacks
hInfoPopup [] Handle to active info popup uipanel (at most one)

Methods

cr = canvasRatio(obj)

CANVASRATIO Ratio of canvas height to viewport height. Returns 1 when content fits, >1 when scrolling is needed.

pos = computePosition(obj, gridPos)

COMPUTEPOSITION Convert grid position to canvas-normalized coords.

[stepW, stepH, cellW, cellH] = canvasStepSizes(obj)

CANVASSTEPSIZES Grid step sizes in canvas-normalized coords.

[dx_c, dy_c] = figureToCanvasDelta(obj, dx_fig, dy_fig)

FIGURETOCANVASDELTA Convert figure-normalized deltas to canvas deltas.

maxRow = calculateMaxRow(obj, widgets)

tf = overlaps(obj, posA, posB)

newPos = resolveOverlap(obj, pos, existingPositions)

ensureViewport(obj, hFigure, theme)

ENSUREVIEWPORT Create viewport/canvas/scrollbar only if they do not exist yet. Idempotent: if the viewport handle is already valid, returns immediately without deleting or recreating anything. On the first call the viewport, canvas, and (if needed) scrollbar are created and TotalRows is reset to 0 so that subsequent additive allocatePanels calls accumulate row counts.

resetViewport(obj)

RESETVIEWPORT Destroy the current viewport so the next ensureViewport call rebuilds it. Use when a full layout rebuild is required (e.g. single-page reflow).

allocatePanels(obj, hFigure, widgets, theme)

ALLOCATEPANELS Create placeholder panels for widgets (additive; no viewport destruction). Calls ensureViewport (idempotent) to guarantee hViewport/hCanvas exist, then accumulates TotalRows and appends widget panels to the shared canvas. Multiple calls for different page-widget sets are safe: earlier panels survive. Ensure viewport exists (idempotent — no-op if already live)

realizeWidget(obj, widget)

REALIZEWIDGET Render a single widget into its pre-allocated panel. Creates the chrome (full-width WidgetButtonBar + WidgetContentPanel sub-panel below the bar) BEFORE calling widget.render so the widget's own graphics children (titles, axes, status text, group headers) land in the visible content area, never under the bar.

createPanels(obj, hFigure, widgets, theme)

CREATEPANELS Create and render all widget panels (legacy path).

reflow(obj, hFigure, widgets, theme)

Re-run layout after dynamic changes (e.g., group collapse/expand). Tears down and recreates all panels, calling render() on each widget.

onScroll(obj, val)

ONSCROLL Adjust canvas position from scrollbar value. val=1 shows top, val=0 shows bottom.

rows = computeVisibleRows(obj, scrollVal)

COMPUTEVISIBLEROWS Derive visible row range from scroll position.

vis = isWidgetVisible(obj, gridPos, buffer)

ISWIDGETVISIBLE Check if widget rows overlap visible range + buffer.

openInfoPopup(obj, widget, theme)

OPENINFOPOPUP Open a modal figure window showing widget Description.

closeInfoPopup(obj)

CLOSEINFOPOPUP Close and delete the active info popup panel.

onFigureClickForDismiss(obj)

ONFIGURECLICKFORDISMISS Dismiss popup if click was outside the popup panel.

onKeyPressForDismiss(obj, eventData)

ONKEYPRESSFORDISMISS Dismiss popup when Escape is pressed.

addPlantLogToggle(obj, widget, engine)

ADDPLANTLOGTOGGLE Add the per-widget plant-log overlay toggle (Phase 1032 PLOG-VIZ-05). The toggle is always created (Decision B: always render, disable when no store); clicking it calls widget.setShowPlantLog(~widget.ShowPlantLog, engine). The engine handle is captured by the callback closure.

onPlantLogTogglePressed_(obj, src, widget, engine)

ONPLANTLOGTOGGLEPRESSED_ Toggle button callback — wraps setShowPlantLog with try/catch (Phase 1032 PLOG-VIZ-05). Programmatic force-call paths (tests, automation) need a software-level guard for Enable='off' because uicontrols only honor Enable natively for user-driven mouse clicks.

addCrosshairLinkToggle(obj, widget)

ADDCROSSHAIRLINKTOGGLE Inject crosshair-link 'X' button into WidgetButtonBar (260602-mri). Duck-typed: only called for widgets where ismethod(widget,'setCrosshairLink'). Idempotent: removes any prior CrosshairLinkButton before creating the new one. Glyph: 'X' (ASCII, Octave-safe — matches existing V/A/L/i/^ glyphs). Position: leftmost chrome button, placed to the LEFT of the V/A cluster; final position settled by reflowChrome_ (reflowChrome_ is called from both realizeWidget and from this method for callback-driven rebuilds). Active (linked) state highlighted via chooseYLimitActiveBg_ (same as V/A).

onCrosshairLinkTogglePressed_(obj, src, widget)

ONCROSSHAIRLINKTOGLEPRESSED_ CrosshairLink toggle callback (260602-mri). Flips widget.CrosshairLinked, notifies the engine, and rebuilds the button visual. All errors are caught and surfaced as namespaced warnings so no toggle failure can crash the dashboard refresh loop.

Static Methods

DashboardLayout.reflowChrome_(hCell, barH, inset)

REFLOWCHROME_ SizeChangedFcn handler — re-anchor the WidgetButtonBar AND resize the WidgetContentPanel after the parent cell panel resizes. Public so tests can drive a deterministic resize without relying on SizeChangedFcn firing under -batch. No-op when the cell has been deleted or chrome isn't there yet.

DashboardLayout.bg = chooseYLimitActiveBg_(theme)

CHOOSEYLIMITACTIVEBG_ Pick the highlight color for the active YLimit button. Tries PressedBg / SelectedBg / AccentColor in order, falling back to ToolbarBackground brightened by 0.15 per channel (capped at 1) when none are present. No new theme fields are introduced by 260513-sfp; future themes can opt into a dedicated PressedBg token without touching layout code.

DashboardLayout.syncYLimitButtonsState_(bar, mode)

SYNCYLIMITBUTTONSSTATE_ Visually highlight the YLimit button matching mode. The active button's BackgroundColor becomes the value stashed on bar.UserData.YLimitActiveBg by addYLimitButtons_; the other two revert to the theme's ToolbarBackground. Tolerates missing buttons (no-op if the bar's UserData was never primed).

DashboardLayout.reflowButtonBar_(hCell, barH, inset)

REFLOWBUTTONBAR_ Deprecated alias — forwards to reflowChrome_. Kept temporarily for any external callers that still reference the m52-era name.


DashboardPage --- Named page container within a multi-page dashboard.

Inherits from: handle

Each DashboardPage holds a list of widgets to be rendered when the page is active. DashboardEngine maintains a Pages cell array of DashboardPage objects and routes addWidget() to the active page.

Constructor

obj = DashboardPage(name)

DASHBOARDPAGE Construct a named page container. pg = DashboardPage() creates page with Name = '' pg = DashboardPage('Name') creates page with given Name

Properties

Property Default Description
Name ''
Widgets {}

Methods

w = addWidget(obj, w)

ADDWIDGET Append widget w to the Widgets list. pg.addWidget(w) appends w to obj.Widgets.

s = toStruct(obj)

TOSTRUCT Serialize the page to a struct with name and widgets fields. s = pg.toStruct() returns s.name (char) and s.widgets (cell).


DashboardSerializer --- JSON load/save and .m export for dashboard configs.

Static Methods

DashboardSerializer.save(config, filepath)

SAVE Write dashboard config as a MATLAB function file. The output is a function returning a DashboardEngine.

DashboardSerializer.saveJSON(config, filepath)

SAVEJSON Write dashboard config struct to JSON file. Handles both single-page (widgets field) and multi-page (pages field). Widgets/pages may have heterogeneous fields, so encode each entry individually and assemble the JSON array by hand.

DashboardSerializer.jsonStr = encodePlantLogBlock_(pl)

ENCODEPLANTLOGBLOCK_ Hand-encode the plantLog block as a JSON object. Used by saveJSON to preserve the metadataCols cell-array shape (jsonencode of {} is ambiguous across MATLAB versions). Returns a JSON object string with sourcePath, mapping, interval, and startTail keys in stable order.

DashboardSerializer.result = load(filepath)

LOAD Load dashboard config from file. For .m files: uses feval to execute the function and return the engine. For .json files: uses legacy JSON parsing.

DashboardSerializer.v = currentSchemaVersion()

CURRENTSCHEMAVERSION Supported dashboard config schema version. Writers stamp this into every config (widgetsToConfig / widgetsPagesToConfig). The loader warns (DashboardSerializer:schemaVersionNewer) when it reads a config whose schemaVersion exceeds this value. Bump only when the on-disk config shape changes in a way older loaders cannot read.

DashboardSerializer.checkSchemaVersion_(config, source)

CHECKSCHEMAVERSION_ Warn if a loaded config is newer than supported. A missing schemaVersion is treated as v1 (pre-versioning files) and loads silently.

DashboardSerializer.config = loadJSON(filepath)

LOADJSON Legacy: read dashboard config from JSON file.

DashboardSerializer.config = widgetsToConfig(name, theme, liveInterval, widgets, infoFile)

WIDGETSTOCONFIG Build a config struct from widget objects.

DashboardSerializer.config = widgetsPagesToConfig(name, theme, liveInterval, pages, activePage, infoFile)

WIDGETSPAGESTOCONFIG Build a multi-page config struct from page objects. pages is a cell array of DashboardPage objects. activePage is the Name string of the active page.

DashboardSerializer.widgets = configToWidgets(config, resolver)

CONFIGTOWIDGETS Create widget objects from config struct. configToWidgets(config) — no sensor resolution configToWidgets(config, resolver) — resolver is a function handle @(name) that returns a Sensor object by name.

DashboardSerializer.w = createWidgetFromStruct(ws)

CREATEWIDGETFROMSTRUCT Create a single widget from a struct. Dispatches through DashboardWidgetRegistry — the single source of truth for widget type->class. The deprecated 'kpi' resolves to NumberWidget via the registry alias. 'mock' is a test-only widget kept as a thin special-case here: MockDashboardWidget lives under tests/ and is intentionally NOT seeded into the library registry, so the library has no dependency on test code. Unknown types warn DashboardSerializer:unknownType and return [].

DashboardSerializer.exportScript(config, filepath)

EXPORTSCRIPT Generate a readable .m script from config.

DashboardSerializer.exportScriptPages(config, filepath)

EXPORTSCRIPTPAGES Generate a MATLAB function file from a multi-page config. The output is a function returning a DashboardEngine so that DashboardEngine.load() can use feval(funcname) to reconstruct it. Emits d.addPage('Name') + d.switchPage(N) before each page's widget block so that addWidget routes to the correct page.

DashboardSerializer.[childLines, varName, groupCount] = emitChildWidget(cw, groupCount)

EMITCHILDWIDGET Emit .m constructor lines for a child widget. Used by DashboardSerializer.save() to emit child code for GroupWidget children. Children are created by constructor, not d.addWidget(). Returns the generated code lines, the variable name assigned, and the updated groupCount (in case the child is itself a GroupWidget).


BarChartWidget

Inherits from: DashboardWidget

Constructor

obj = BarChartWidget(varargin)

Properties

Property Default Description
DataFcn [] @() struct('categories',{},'values',[])
Orientation 'vertical' 'vertical' or 'horizontal'
Stacked false

Methods

render(obj, parentPanel)

refresh(obj)

t = getType(~)

lines = asciiRender(obj, width, height)

s = toStruct(obj)

Static Methods

BarChartWidget.obj = fromStruct(s)


ChipBarWidget --- Horizontal row of mini status chips for system health summary.

Inherits from: DashboardWidget

Displays N colored circle icons with labels in a compact horizontal strip. Designed as a dense multi-sensor status overview at a glance.

Constructor

obj = ChipBarWidget(varargin)

CHIPBARWIDGET Construct a ChipBarWidget with optional name-value pairs.

Properties

Property Default Description
Chips {} Cell array of chip structs (label, statusFcn, sensor, iconColor)

Methods

render(obj, parentPanel)

RENDER Draw all chips in a single shared axes inside parentPanel. Re-entrancy guard: parenting an axes to parentPanel and toggling its Units below can synchronously fire the panel's SizeChangedFcn -> relayout_ -> render. Without this lock the nested call deletes the axes the outer render is populating and the outer render then crashes on text(obj.hAx, ...).

refresh(obj)

REFRESH Update chip circle colors from statusFcn or sensor state.

t = getType(~)

GETTYPE Return widget type string.

s = toStruct(obj)

TOSTRUCT Serialize widget to struct for JSON export.

Static Methods

ChipBarWidget.obj = fromStruct(s)

FROMSTRUCT Reconstruct ChipBarWidget from a saved struct.


CreateEventDialog --- Modal dialog to create a manual annotation Event (260513-snt).

Inherits from: handle

d = CreateEventDialog(fastSenseWidget, dashboardEngine)

Opens a modal figure pre-filled with the widget's current X view as the event time range and the widget's bound Tag.Key as the tag binding. On Save: appends an Event to engine.EventStore, registers per-tag EventBinding entries, calls EventStore.save() and finally engine.notifyEventsChanged() so EventTimelineWidget + FastSenseWidget instances and the slider's event-marker overlay refresh.

The dialog mirrors DashboardConfigDialog's pattern: classical figure (NOT uifigure) with WindowStyle='modal', styled from the engine's theme. All UI callbacks are wrapped in try/catch with non-blocking errordlg so a bad input never tears down the dialog.

Properties (SetAccess = private): Widget - bound FastSenseWidget Engine - bound DashboardEngine hFigure - modal figure handle

Methods (public): onSave - validate, persist, notify, close dialog on success onCancel - close dialog without writing delete - destructor, tears down figure

Methods (Static, public): persistEventStatic(engine, tStart, tEnd, label, sev, cat, notes, keys, primaryName) - mock-friendly persistence seam used by Task-3 tests; instance persistEvent_ delegates here.

Errors raised (all namespaced): CreateEventDialog:invalidWidget - widget is not a FastSenseWidget CreateEventDialog:invalidEngine - engine is not a DashboardEngine CreateEventDialog:noStore - engine.EventStore is empty CreateEventDialog:invalidTimeRange - EndTime < StartTime (or not finite) CreateEventDialog:emptyLabel - Label is empty after trim

Constructor

obj = CreateEventDialog(widget, engine)

CREATEEVENTDIALOG Construct + show modal dialog.

Methods

onSave(obj, ~, ~)

ONSAVE Validate inputs, persist Event, refresh dashboard, close dialog. Wraps the full pipeline in try/catch so any throw surfaces via errordlg without tearing the dialog down — the user can correct input and Save again. On success: deletes the modal figure.

onCancel(obj, ~, ~)

ONCANCEL Close the dialog without writing.

Static Methods

CreateEventDialog.persistEventStatic(engine, tStart, tEnd, label, sev, cat, notes, keys, primaryName)

PERSISTEVENTSTATIC Persist a manual annotation Event into engine.EventStore (260513-snt). Public static seam called by the instance persistEvent_ wrapper AND directly by Task-3 tests. Keeping the write-side logic free of any figure handles makes it trivially unit-testable.


DashboardConfigDialog --- Config editor for a DashboardEngine.

Inherits from: handle

Opens a figure listing every public DashboardEngine property with an editable control. Apply writes values back to the engine and propagates visible changes (figure title, theme re-render, live timer restart). Close dismisses without additional changes.

Enum-like properties get a popup menu: Theme — {'light', 'dark'} ProgressMode — {'auto', 'on', 'off'} Numeric properties get a numeric edit control. Everything else gets a plain text edit.

Usage (usually invoked by the toolbar Config button): dlg = DashboardConfigDialog(engine); % ...user edits fields, clicks Apply/Close...

Constructor

obj = DashboardConfigDialog(engine)

Methods

close(obj)

CLOSE Destroy the dialog figure.

apply(obj)

APPLY Write all control values back to the engine and propagate.


DashboardProgress --- Progress-bar helper for DashboardEngine render passes.

Inherits from: handle

Emits a self-updating progress line to stdout as widgets are realized during DashboardEngine.render() / rerenderWidgets(), and a final summary line on completion.

Silent outside interactive sessions so test / CI output stays clean.

Constructor

obj = DashboardProgress(name, totalWidgets, totalPages, mode)

Methods

tick(obj, widget, pageIdx, pageName)

finish(obj)


DashboardToolbar --- Global toolbar for dashboard controls.

Inherits from: handle

Provides buttons for: Sync, Live (toggle with blue border when active), Config (opens DashboardConfigDialog), Image, Export, and Info (always present — shows a placeholder page when no InfoFile is configured). Every button has a descriptive tooltip. Sits at the top of the dashboard figure.

Constructor

obj = DashboardToolbar(engine, hFigure, theme)

Properties

Property Default Description
Height 0.04

Methods

setLastUpdateTime(obj, t)

SETLASTUPDATETIME Update the last-update label with a timestamp. Hot-path note: called on every live tick. Uses datevec (no format string parsing) instead of datestr to avoid timefun/private overhead.

onNameEdit(obj, src)

onLiveToggle(obj, src)

setLiveActiveIndicator(obj, isActive)

SETLIVEACTIVEINDICATOR Show a blue surround when live mode is active.

onFollowToggle(obj, src)

ONFOLLOWTOGGLE Apply auto-pan to every FastSense widget in the dashboard. isOn=true: LiveViewMode='follow' on every FastSenseWidget's FastSenseObj AND snap each chart to its current data tail (one-shot jump-to-now). isOn=false: LiveViewMode='preserve' on every FastSenseWidget's FastSenseObj (the chart stops following).

setFollowActiveIndicator(obj, isActive)

SETFOLLOWACTIVEINDICATOR Show a blue surround when Follow is active.

applyFollowToWidgets_(obj, widgets, mode, snap)

APPLYFOLLOWTOWIDGETS_ Recursively apply LiveViewMode + optional snap. Walks the widget tree (descends into GroupWidget children), sets LiveViewMode on every FastSenseWidget's FastSenseObj, and — when snap is true — calls snapToTail() on each to immediately jump the view to the current data tail.

onEventsToggle(obj, src)

ONEVENTSTOGGLE Fire engine-level event-marker toggle from button state. Engine.setEventMarkersVisible already calls back into setEventsActiveIndicator, but call it directly here too in case the engine's call path skips the toolbar (e.g. tests that temporarily reassign Engine.Toolbar).

setEventsActiveIndicator(obj, isActive)

SETEVENTSACTIVEINDICATOR Blue border when event markers are visible. Matches the Live button's visual treatment so the toolbar reads consistently. Keeps the button label constant — the border colour is the active indicator; the tooltip explains the function.

onConfig(obj)

ONCONFIG Open the dashboard config dialog.

onReset(obj)

ONRESET Manual recovery — re-render all widgets on the active page. Delegates to DashboardEngine.rerenderWidgets which deletes every widget panel, marks widgets unrealized, then re-allocates and re-realizes them. Use when widgets get stuck (stale axes, zombie state, transient render error). Safe to call while Live mode is active — rerenderWidgets does not touch the Live timer state.

onExport(obj)

onImage(obj)

ONIMAGE Open save dialog and export dashboard figure as PNG/JPEG. Pops a uiputfile with PNG+JPEG filters, defaults to the sanitized dashboard name plus timestamp. On cancel, returns silently. On engine error, surfaces message via warndlg.

dispatchImageExport(obj, file, path, idx)

DISPATCHIMAGEEXPORT Post-dialog dispatcher — testable without uiputfile. file — filename string, or 0 on user-cancel path — directory path from uiputfile idx — filter index (1=PNG, 2=JPEG). Defaults to PNG.

fname = defaultImageFilename(obj)

DEFAULTIMAGEFILENAME Build sanitized default filename for the dialog. Pattern: {sanitized Engine.Name}{yyyymmdd_HHMMSS}.png Sanitization: replace [/:*?"<>|] and whitespace with ''. NOTE: datestr format 'yyyymmdd_HHMMSS' (lowercase mm=month here, HHMMSS=seconds). This differs from datetime/ISO notation — see libs/EventDetection/generateEventSnapshot.m:28 for the in-codebase precedent.

onInfo(obj)

contentArea = getContentArea(obj)

GETCONTENTAREA Compute the widget content area in normalized units. Subtracts the reserved banner strip at the top, the toolbar, and the time-panel height (260508-jyh). DashboardEngine computes ContentArea inline in render() and applyVisibilityAndRelayout(); this helper exists for consistency with consumers that read directly from the toolbar (e.g. DashboardBuilder canvas calc).


DashboardWidgetRegistry --- Single source of truth for dashboard widget types.

DashboardWidgetRegistry maps a widget type string (e.g. 'number') to the class that implements it, so that EVERY consumer — DashboardEngine.addWidget, DashboardEngine.widgetTypes, DashboardSerializer.createWidgetFromStruct and DetachedMirror.cloneWidget — dispatches through ONE table instead of four hand-maintained switch statements that drift out of sync.

It mirrors the TagRegistry static-singleton pattern (a classdef of static methods over a persistent containers.Map), with three intentional deltas:

1. The catalog is seeded NON-empty on first use with the built-in widget
   types (TagRegistry starts empty).
2. Type ALIASES (deprecated/renamed type strings) are a separate concern,
   resolved via resolveAlias() — e.g. the deprecated 'kpi' -> 'number'.
3. reset() RE-SEEDS the built-ins and built-in aliases rather than wiping
   to empty; it exists for test isolation after register()/registerAlias().

DashboardWidgetRegistry Methods (Static, public): types — sorted cellstr of all registered canonical type strings isRegistered — true if a canonical type is registered (aliases excluded) resolveAlias — map an alias to its canonical type (passthrough otherwise) constructorFor — the @ClassName constructor handle for a type (resolves alias) fromStruct — deserialize a widget struct via the type's static fromStruct register — add a NEW canonical type (hard error on collision) registerAlias — add an alias for an already-registered canonical type reset — restore the built-in catalog + aliases (test isolation)

Static Methods

DashboardWidgetRegistry.t = types()

TYPES Sorted cellstr of all registered canonical widget type strings. The single source of truth — DashboardEngine.widgetTypes() derives its list from this.

DashboardWidgetRegistry.tf = isRegistered(type)

ISREGISTERED True if TYPE is a registered canonical type. Aliases (e.g. 'kpi') are NOT counted — use resolveAlias() first if you need alias-aware membership.

DashboardWidgetRegistry.c = resolveAlias(type)

RESOLVEALIAS Map an alias to its canonical type. Returns TYPE unchanged when it is not an alias.

DashboardWidgetRegistry.h = constructorFor(type)

CONSTRUCTORFOR Constructor handle (@ClassName) for a widget type. Resolves aliases first. Throws DashboardWidgetRegistry:unknownType when the (resolved) type is not registered.

DashboardWidgetRegistry.w = fromStruct(type, s)

FROMSTRUCT Deserialize a widget struct via its class fromStruct. w = DashboardWidgetRegistry.fromStruct(type, s) resolves TYPE to a constructor handle, derives the class name, and calls .fromStruct(s). Throws DashboardWidgetRegistry:unknownType when TYPE (resolved) is not registered.

DashboardWidgetRegistry.register(type, ctorHandle)

REGISTER Add a NEW canonical widget type to the catalog. DashboardWidgetRegistry.register(type, @ClassName) registers a constructor handle under TYPE. Like TagRegistry.register, this HARD-ERRORS on collision (DashboardWidgetRegistry:duplicateType) so a custom widget cannot silently clobber a built-in. Call reset() to drop custom registrations (test isolation).

DashboardWidgetRegistry.registerAlias(alias, canonical)

REGISTERALIAS Map an alias type string to a registered canonical type. The canonical type must already be registered, else DashboardWidgetRegistry:unknownType is thrown.

DashboardWidgetRegistry.reset()

RESET Restore the built-in catalog and aliases (test isolation). Re-seeds the persistent maps in place so register()/registerAlias() side effects from a prior test do not leak. Mirrors TagRegistry.clear, but re-seeds the built-ins rather than wiping to empty.


DetachedMirror --- Standalone live-mirrored widget window for DashboardEngine.

Inherits from: handle

DetachedMirror wraps a cloned DashboardWidget in a standalone MATLAB figure window. The clone is produced via toStruct/fromStruct with post- clone live-reference restoration for FastSenseWidget and RawAxesWidget.

The mirror is NOT a DashboardWidget subclass — it wraps one. It belongs to DashboardEngine.DetachedMirrors and is ticked by the engine's existing LiveTimer via the engine's onLiveTick() loop.

Usage (called internally by DashboardEngine.detachWidget()): theme = DashboardTheme(obj.Theme); cb = @() obj.removeDetached(mirror); mirror = DetachedMirror(originalWidget, theme, cb);

Properties (SetAccess = private): hFigure — standalone MATLAB figure window handle hPanel — full-figure uipanel that hosts the cloned widget Widget — cloned DashboardWidget instance RemoveCallback — @() called by onFigureClose() before delete(hFigure)

Constructor

obj = DetachedMirror(originalWidget, themeStruct, removeCallback)

DETACHEDMIRROR Create a detached live-mirror window for originalWidget.

Methods

tick(obj)

TICK Refresh the cloned widget; no-op if figure is stale.

result = isStale(obj)

ISSTALE Return true when the mirror's figure has been closed or destroyed.


DividerWidget --- Horizontal divider line for visual section separation.

Inherits from: DashboardWidget

DividerWidget renders a horizontal colored line using the theme's WidgetBorderColor (or a custom Color override). It is a static widget with no data binding.

Constructor

obj = DividerWidget(varargin)

DIVIDERWIDGET Construct a DividerWidget. obj = DividerWidget() creates with defaults. obj = DividerWidget('Thickness', 2, 'Color', [1 0 0]) sets props.

Properties

Property Default Description
Thickness 1 Relative line thickness (1=thin, 2=medium, 3=thick)
Color [] RGB override; empty = use theme WidgetBorderColor

Methods

render(obj, parentPanel)

RENDER Create the divider line inside parentPanel. render(obj, parentPanel) creates a uipanel that acts as a horizontal colored line centered vertically in the panel.

refresh(~)

REFRESH No-op for static widget.

t = getType(~)

GETTYPE Return widget type string.

lines = asciiRender(obj, width, height)

ASCIIRENDER Return ASCII representation of the divider. First line is a row of dashes; remaining lines are blank.

s = toStruct(obj)

TOSTRUCT Serialize to struct. Omits 'thickness' at default (1) and 'color' when empty.

Static Methods

DividerWidget.obj = fromStruct(s)

FROMSTRUCT Reconstruct DividerWidget from serialized struct.


EventTimelineWidget --- Displays events as colored bars on a timeline.

Inherits from: DashboardWidget

Preferred: bind to an EventStore from the event detection system: w = EventTimelineWidget('Title', 'Events', 'EventStoreObj', store);

Legacy (still supported for backwards compatibility): w = EventTimelineWidget('Title', 'Events', 'EventFcn', @() getEvents()); w = EventTimelineWidget('Title', 'Events', 'Events', eventArray);

Events must be a struct array with fields: startTime, endTime, label, color (optional)

Constructor

obj = EventTimelineWidget(varargin)

Properties

Property Default Description
EventStoreObj [] EventStore handle — primary data source
Events [] struct array of events (legacy)
EventFcn [] function_handle returning events (legacy)
FilterSensors {} Cell array of Sensor names to filter
FilterTagKey '' Tag-key filter (MONITOR-05 carrier: SensorName OR ThresholdLabel match)
ColorSource 'event' 'event' or 'theme'

Methods

render(obj, parentPanel)

setTimeRange(obj, tStart, tEnd)

[tMin, tMax] = getTimeRange(obj)

t = getEventTimes(obj)

GETEVENTTIMES Event start times from resolveEvents (override). Mirrors the same filtering pipeline the widget uses to draw bars, so the time-slider overlay always matches what the widget itself renders.

m = getEventMarkers(obj)

GETEVENTMARKERS Per-event time + severity + color for slider markers. m = getEventMarkers(obj) returns a struct array with fields: m(k).Time — numeric timestamp (startTime) m(k).Severity — numeric severity (default 1 if absent) m(k).Color — 1x3 RGB triplet from severityColor(theme, sev)

refresh(obj)

t = getType(~)

lines = asciiRender(obj, width, height)

s = toStruct(obj)

evts = resolveEvents(obj)

RESOLVEEVENTS Get events from the best available source. Priority: EventStoreObj > TagRegistry default > EventFcn > Events (static / Event objects). When FilterTagKey is set AND an EventStore is bound (explicit or registry-default), events are pulled via EventStore.getEventsForTag(tagKey) using the dual-key pattern from Phase 1010 + the registry-default fallback from Phase 1017.

Static Methods

EventTimelineWidget.obj = fromStruct(s)


FastSenseWidget --- Dashboard widget wrapping a FastSense instance.

Inherits from: DashboardWidget

Supports data binding modes: Tag: w = FastSenseWidget('Tag', tagObj) DataStore: w = FastSenseWidget('DataStore', dsObj) Inline: w = FastSenseWidget('XData', x, 'YData', y) File: w = FastSenseWidget('File', 'path.mat', 'XVar', 'x', 'YVar', 'y')

Constructor

obj = FastSenseWidget(varargin)

Properties

Property Default Description
DataStoreObj []
XData []
YData []
File ''
XVar ''
YVar ''
Thresholds 'auto'
XLabel '' X-axis label (auto-set from Sensor if empty)
YLabel '' Y-axis label (auto-set from Sensor if empty)
YLimits [] Fixed Y-axis range [min max]; empty = auto-scale
ShowThresholdLabels false show inline name labels on threshold lines
ShowEventMarkers false Phase 1012 — toggle event round-marker overlay
EventStore [] Phase 1012 — EventStore handle forwarded to inner FastSense
ShowPlantLog false Phase 1032 PLOG-VIZ-03 — opt-in per-widget plant-log vertical-line overlay
LiveViewMode 'preserve'
YLimitMode 'auto-visible'
CrosshairLinked false
CurrentXLimOverrideForTest_ []
LastTickSkipped_ false

Methods

c = getRenderCacheForTest_(obj)

GETRENDERCACHEFORTEST_ 260610-ov3 test seam — return RenderDataCache_ value. Hidden (not public) so the DashboardWidget contract is unchanged. Used by test_fastsense_widget_render_cache.m and test_dashboard_load_perf.m to verify the cache lifecycle (cold on construction, warm after render(), cleared by live-tick entry).

setRenderCacheForTest_(obj, x, y)

SETRENDERCACHEFORTEST_ 260610-ov3 test seam — force-warm RenderDataCache_. Lets test_dashboard_load_perf.m call getPreviewSeries with a warm cache without going through render(), verifying the consume-once reuse. Passing empty x AND y clears the cache.

render(obj, parentPanel)

refresh(obj)

Re-render Tag-bound widgets so updated data shows. Uses incremental updateData() path when tag identity is unchanged (PERF2-01); falls back to full teardown/rebuild on first render, tag swap, or error. Zoom state (xlim) is preserved in both paths.

update(obj)

UPDATE Incrementally update Tag data without full axes rebuild. Uses FastSenseObj.updateData() to replace data and re-downsample, avoiding the expensive delete/recreate cycle of refresh(). Falls back to refresh() if FastSenseObj is not in a renderable state. (260513-ovt) Per-tick Y autoscale removed from this path so Live mode never silently mutates the user's Y view.

setEventMarkersVisible(obj, tf)

SETEVENTMARKERSVISIBLE Pass-through to FastSense event-marker toggle. No-op when no FastSense instance exists yet (pre-render). When rendered, delegates to FastSense.setShowEventMarkers which re-draws the overlay in place without disturbing zoom state or live refresh cadence.

setPlantLogMarkers(obj, times, entries)

SETPLANTLOGMARKERS Draw or clear per-widget plant-log vertical lines. Phase 1032 PLOG-VIZ-04. Draws one xline per finite timestamp on the widget's inner FastSense axes (Tag = 'WidgetPlantLogMarker', 1 px solid line with theme.MarkerPlantLog color, default [0 0 0]). Empty / no-arg input clears every existing marker via tag-based delete. Non-finite timestamps are silently dropped (mirrors TimeRangeSelector.setPlantLogMarkers shape).

setPlantLogXLimListenerForEngine_(obj, lis)

setCurrentViewXLimListenerForEngine_(obj, lis)

setShowPlantLog(obj, tf, engine)

SETSHOWPLANTLOG Toggle the per-widget plant-log overlay (Phase 1032 PLOG-VIZ-03). tf — boolean; true enables overlay + attaches XLim listener, false disables overlay + tears down listener + clears markers. engine — DashboardEngine handle; required so refresh + listener wiring can route through engine.refreshPlantLogOverlayForWidget_ and engine.attachPlantLogXLimListener_.

setYLimitMode(obj, mode)

SETYLIMITMODE Set the Y-axis rescale strategy and re-fit if rendered. mode is one of: 'auto-visible' - rescale to data inside the current X window 'auto-all' - rescale to all data the bound Tag exposes 'locked' - freeze YLim; no further rescale on tick/refresh

setTimeWindow(obj, t0, t1)

SETTIMEWINDOW Set the load window for this widget's data pulls. t0, t1 datenum scalars; both [] resets to full range. The DashboardEngine pushes this before re-rendering. Data is pulled via Tag.getXYRange when set, Tag.getXY when empty.

tf = isShowingEmptyState(obj)

ISSHOWINGEMPYSTATE Returns true when 'No data in selected range' is displayed. False when the widget has plotted data, or when no render has occurred.

setCrosshairLink(obj, tf)

SETCROSSHAIRLINK Set the crosshair-link flag (260602-mri). setCrosshairLink(obj, tf) sets CrosshairLinked to logical(tf). tf must be a logical scalar or a numeric 0/1 scalar. Does NOT touch graphics — the engine owns broadcast wiring. Throws FastSenseWidget:invalidCrosshairLink for invalid input.

autoScaleY_(obj, y)

AUTOSCALEY_ Rescale the Y axis to cover current data + thresholds. FastSense locks YLim to manual mode at first render, so new samples outside the initial range would fall off the chart. This helper recomputes the Y extent every tick (including any threshold values so MonitorTag lines stay visible) and updates the axes. Skipped when: - the widget has a user-pinned YLimits NV-pair, or - the user manually zoomed Y via mouse (UserZoomedY), or - the dashboard's Follow toggle is engaged (FastSenseObj.LiveViewMode == 'follow') — Follow is an explicit user intent to track the data tail in X only and keep the rest of the view (including Y) frozen. (260513-ovt) - YLimitMode == 'locked' — the user explicitly froze Y limits via the L button on the WidgetButtonBar (260513-sfp).

onYLimChanged(obj)

ONYLIMCHANGED Detach widget from automatic Y rescale after user zoom. Fired by the YLim PostSet listener. When the YLim change came from inside autoScaleY_ (IsSettingYLim==true) we ignore it; any other source — mouse scroll, drag, zoom toolbar, programmatic ylim() from user code — counts as a manual override and latches UserZoomedY so live ticks stop fighting the user.

setTimeRange(obj, tStart, tEnd)

onXLimChanged(obj)

If xlim changed by user zoom/pan (not by setTimeRange), detach this widget from global time.

[tMin, tMax] = getTimeRange(obj)

Return cached min/max in O(1). Cache is kept up to date by updateTimeRangeCache() which is called from render/refresh/update.

xl = getCurrentXLim(obj)

GETCURRENTXLIM Live x-limits of the wrapped FastSense axes (Phase 1039). Returns the 1x2 [xMin xMax] the plot is CURRENTLY showing — the actual view window, read live from the axes via get(ax,'XLim'). Returns [] when the widget is not rendered (no FastSenseObj, not IsRendered, or no valid axes).

series = getPreviewSeries(obj, nBuckets)

GETPREVIEWSERIES Per-bucket min/max preview for the dashboard envelope. series = getPreviewSeries(obj, nBuckets) returns a struct with fields xCenters, yMin, yMax — each a 1xnBucketsEff row vector; yMin and yMax are normalized into [0,1] across the widget's own current y-range. Returns [] only when no data is bound or when the sample count is genuinely too sparse (<4) to downsample.

t = getEventTimes(obj)

GETEVENTTIMES Event start times for the dashboard time-slider markers. Looks up events in this priority order: 1. obj.EventStore (widget-level — the modern attachment point) 2. obj.FastSenseObj.EventStore (legacy: events on inner FastSense) 3. obj.FastSenseObj.Events / .EventTimes (defensive: extra hooks)

m = getEventMarkers(obj)

GETEVENTMARKERS Per-event time + severity + color for slider markers. m = getEventMarkers(obj) returns a struct array with fields: m(k).Time — numeric timestamp (StartTime) m(k).Severity — numeric severity in {1,2,3} (default 1 if absent) m(k).Color — 1x3 RGB triplet from severityColor(theme, sev)

invalidatePreviewCache_(obj)

INVALIDATEPREVIEWCACHE_ Clear PreviewCache_ so getPreviewSeries recomputes. Called from refresh() / update() / rebuildForTag_() whenever the underlying data may have changed. Cheap (no graphics).

t = getType(~)

lines = asciiRender(obj, width, height)

s = toStruct(obj)

Static Methods

FastSenseWidget.obj = fromStruct(s)


GaugeWidget --- Gauge widget with arc, donut, bar, and thermometer styles.

Inherits from: DashboardWidget

w = GaugeWidget('Title', 'Pressure', 'ValueFcn', @() getPressure(), ... 'Range', [0 100], 'Units', 'bar'); w = GaugeWidget('Sensor', mySensor, 'Style', 'donut'); w = GaugeWidget('Threshold', t, 'StaticValue', 50);

Constructor

obj = GaugeWidget(varargin)

Properties

Property Default Description
ValueFcn []
Range [] Empty default for auto-derivation cascade
Units ''
StaticValue []
Style 'arc' 'arc', 'donut', 'bar', 'thermometer'
Threshold [] Threshold object or registry key string (per D-01)

Methods

render(obj, parentPanel)

refresh(obj)

t = getType(~)

lines = asciiRender(obj, width, height)

s = toStruct(obj)

Static Methods

GaugeWidget.obj = fromStruct(s)


GroupWidget --- Container widget that groups child widgets in one of three modes.

Inherits from: DashboardWidget

GroupWidget delivers the dashboard's nested-layout feature. Set Mode to: 'panel' — children laid out in a sub-grid inside a bordered panel 'collapsible' — like panel, with a header bar that collapses/expands 'tabbed' — children organised into named tabs with a tab strip

Children are added and removed through parallel APIs that depend on Mode: addChild(widget) — panel/collapsible (appends to Children) addChild(widget, tabName) — tabbed (appends to the named tab, creating it) removeChild(idx) — panel/collapsible removeChild(idx, tabName) — tabbed removeTab(tabName) — drop a whole tab Navigation / state: switchTab(tabName), collapse(), expand().

Groups may nest one level deep (a GroupWidget inside a GroupWidget); a maximum nesting depth of 2 is enforced by addChild (GroupWidget:maxDepth).

Key public properties: Mode — 'panel' | 'collapsible' | 'tabbed' Label — header bar title Children — cell of child widgets (panel/collapsible) Tabs — cell of struct('name', ..., 'widgets', {{...}}) (tabbed) ActiveTab — current tab name (tabbed) ChildColumns — sub-grid column count

Constructor

obj = GroupWidget(varargin)

Properties

Property Default Description
Mode 'panel' 'panel', 'collapsible', 'tabbed'
Label '' Title shown in header bar
Collapsed false Collapsed state (collapsible mode only)
Children {} Cell array of DashboardWidget (panel/collapsible)
Tabs {} Cell array of struct('name','...','widgets',{{}})
ActiveTab '' Current tab name (tabbed mode)
ChildColumns 24 Sub-grid column count
ChildAutoFlow true Auto-arrange children
ReflowCallback [] Callback invoked after collapse/expand (injected by DashboardEngine)

Methods

addChild(obj, widget, tabName)

Check nesting depth for GroupWidget children

removeChild(obj, idx, tabName)

REMOVECHILD Remove a child widget by index. removeChild(idx) removes obj.Children(idx) — panel/collapsible mode. removeChild(idx, tabName) removes the idx-th widget of the named tab (tabbed mode), mirroring addChild(widget, tabName). Throws GroupWidget:unknownTab for an unknown tab and GroupWidget:invalidIndex for an out-of-range index in either mode.

removeTab(obj, tabName)

REMOVETAB Remove an entire tab and its widgets (tabbed mode). Throws GroupWidget:unknownTab if the tab does not exist. When the removed tab was the active one, ActiveTab moves to the first remaining tab, or '' when none remain.

render(obj, parentPanel)

refresh(obj)

[tMin, tMax] = getTimeRange(obj)

GETTIMERANGE Aggregate time range from all children and tabs.

children = getNestedWidgets(obj)

GETNESTEDWIDGETS Return Children plus all Tabs widgets as a flat cell. Used by DashboardEngine.flattenWidgetsForPreview_ to surface nested data/event widgets to the time-slider preview and marker overlay. Order: Children first, then Tabs widgets in declaration order. Empty Group returns {}.

t = getType(obj)

lines = asciiRender(obj, width, height)

setTimeRange(obj, tStart, tEnd)

s = toStruct(obj)

collapse(obj)

expand(obj)

switchTab(obj, tabName)

Static Methods

GroupWidget.obj = fromStruct(s)


HeatmapWidget

Inherits from: DashboardWidget

Constructor

obj = HeatmapWidget(varargin)

Properties

Property Default Description
DataFcn [] function_handle returning matrix
Colormap 'parula' colormap name or Nx3 matrix
ShowColorbar true
XLabels {} cell array of axis labels
YLabels {} cell array of axis labels

Methods

render(obj, parentPanel)

refresh(obj)

t = getType(~)

lines = asciiRender(obj, width, height)

s = toStruct(obj)

Static Methods

HeatmapWidget.obj = fromStruct(s)


HistogramWidget

Inherits from: DashboardWidget

Constructor

obj = HistogramWidget(varargin)

Properties

Property Default Description
DataFcn []
NumBins [] empty = auto
ShowNormalFit false
EdgeColor [] RGB or empty for default

Methods

render(obj, parentPanel)

refresh(obj)

t = getType(~)

lines = asciiRender(obj, width, height)

s = toStruct(obj)

Static Methods

HistogramWidget.obj = fromStruct(s)


IconCardWidget --- Compact Mushroom Card-style widget with colored icon, value, and label.

Inherits from: DashboardWidget

Displays a state-colored circle icon at the left, a primary numeric value in the center, and a secondary label below the value. Icon color reflects the current threshold state (ok/warn/alarm/info/inactive).

Constructor

obj = IconCardWidget(varargin)

ICONCARDWIDGET Construct an IconCardWidget with optional name-value pairs.

Properties

Property Default Description
IconColor 'auto' RGB triplet or 'auto' (derive from state)
StaticValue [] Fixed static value (number)
ValueFcn [] Function handle returning scalar or struct
StaticState '' 'ok','warn','alarm','info','inactive',''
Units '' Display units string
Format '%.1f' sprintf format for numeric value
SecondaryLabel '' Subtitle text below primary value
Threshold [] Threshold object or registry key string (per D-01)

Methods

render(obj, parentPanel)

RENDER Create icon, value text, and label inside parentPanel.

refresh(obj)

REFRESH Update icon color, value display, and label.

t = getType(~)

GETTYPE Return widget type string.

s = toStruct(obj)

TOSTRUCT Serialize widget to struct for JSON export.

Static Methods

IconCardWidget.obj = fromStruct(s)

FROMSTRUCT Reconstruct IconCardWidget from a serialized struct.


ImageWidget

Inherits from: DashboardWidget

Constructor

obj = ImageWidget(varargin)

Properties

Property Default Description
File '' Path to image file (PNG, JPG)
ImageFcn [] function_handle returning image matrix
Scaling 'fit' 'fit', 'fill', 'stretch'
Caption ''

Methods

render(obj, parentPanel)

refresh(obj)

t = getType(~)

lines = asciiRender(obj, width, height)

s = toStruct(obj)

Static Methods

ImageWidget.obj = fromStruct(s)


MarkdownRenderer --- Lightweight Markdown-to-HTML converter.

html = MarkdownRenderer.render(mdText) html = MarkdownRenderer.render(mdText, themeName) html = MarkdownRenderer.render(mdText, themeName, basePath)

Converts a subset of Markdown to a self-contained HTML document. Supported: headings (#-###), bold, italic, inline code, fenced code blocks, [links](url), ![images](src), unordered/ordered lists, horizontal rules (---), tables (pipe-delimited), and paragraph breaks.

The optional themeName ('light', 'dark', etc.) controls the CSS color scheme. Unrecognized themes default to 'light'.

Static Methods

MarkdownRenderer.html = render(mdText, themeName, basePath)


MultiStatusWidget

Inherits from: DashboardWidget

Constructor

obj = MultiStatusWidget(varargin)

Properties

Property Default Description
Sensors {} Cell array of Sensor objects
Columns [] Grid columns (empty = auto)
ShowLabels true
IconStyle 'dot' 'dot', 'square', 'icon'

Methods

render(obj, parentPanel)

refresh(obj)

t = getType(~)

lines = asciiRender(obj, width, height)

s = toStruct(obj)

Fully override — does not use base Sensor property

Static Methods

MultiStatusWidget.obj = fromStruct(s)


NumberWidget --- Dashboard widget showing a big number with label and trend.

Inherits from: DashboardWidget

w = NumberWidget('Title', 'Temp', 'ValueFcn', @() readTemp(), 'Units', 'degC');

ValueFcn returns either: - A scalar (displayed as-is) - A struct with fields: value, unit, trend ('up'/'down'/'flat')

Constructor

obj = NumberWidget(varargin)

Properties

Property Default Description
ValueFcn [] function_handle returning scalar or struct
Units '' unit label string
Format '%.1f' sprintf format for value
StaticValue [] fixed value (no callback needed)

Methods

render(obj, parentPanel)

refresh(obj)

t = getType(~)

lines = asciiRender(obj, width, height)

s = toStruct(obj)

Static Methods

NumberWidget.obj = fromStruct(s)


RawAxesWidget --- User-supplied plot function on raw MATLAB axes.

Inherits from: DashboardWidget

w = RawAxesWidget('Title', 'Histogram', ... 'PlotFcn', @(ax) histogram(ax, randn(1,1000)));

When bound to a Sensor, the PlotFcn receives (ax, sensor) or (ax, sensor, timeRange) depending on its nargin.

Constructor

obj = RawAxesWidget(varargin)

Properties

Property Default Description
PlotFcn [] @(ax) or @(ax, sensor[, tRange]) or @(ax, tRange)
DataRangeFcn [] @() returning [tMin tMax] for global time range detection

Methods

render(obj, parentPanel)

refresh(obj)

setTimeRange(obj, tStart, tEnd)

[tMin, tMax] = getTimeRange(obj)

t = getType(~)

lines = asciiRender(obj, width, height)

s = toStruct(obj)

Static Methods

RawAxesWidget.obj = fromStruct(s)


ScatterWidget

Inherits from: DashboardWidget

Constructor

obj = ScatterWidget(varargin)

Properties

Property Default Description
SensorX [] Sensor for X axis
SensorY [] Sensor for Y axis
SensorColor [] Optional: color-code by third sensor
MarkerSize 6
Colormap 'parula'

Methods

render(obj, parentPanel)

refresh(obj)

t = getType(~)

lines = asciiRender(obj, width, height)

s = toStruct(obj)

Static Methods

ScatterWidget.obj = fromStruct(s)

ScatterWidget.t = resolveTag_(key, title)

RESOLVETAG_ Resolve a Tag key via TagRegistry; warn (no throw) if absent. Mirrors FastSenseWidget:tagNotFound semantics: a missing key yields [] and a namespaced warning rather than an error, so a dashboard saved against a different registry still loads.


SparklineCardWidget --- KPI card combining a big-number display with a mini sparkline chart and delta indicator.

Inherits from: DashboardWidget

w = SparklineCardWidget('Title', 'CPU', 'StaticValue', 42.0, ... 'SparkData', cpuHistory, 'Units', '%');

The card is divided into three zones: Top row — title (left) and delta indicator (right) Middle — large primary value Bottom — sparkline mini-chart (bottom 35% of card)

Data binding (three-path, resolved in priority order): 1. Sensor — uses Sensor.Y for both value and sparkline 2. ValueFcn — function_handle returning scalar or struct 3. StaticValue + SparkData — static numeric value with separate sparkline vector

Properties: StaticValue — fixed scalar value ValueFcn — function handle returning scalar or struct(.value, .unit) Units — display unit string Format — sprintf format for primary value (default '%.1f') NSparkPoints — number of tail points shown in sparkline (default 50) ShowDelta — show delta indicator (default true) DeltaFormat — sprintf format for delta (default '%+.1f') SparkColor — sparkline line color; empty => theme.DragHandleColor SparkData — numeric vector for sparkline (used when no Sensor)

Constructor

obj = SparklineCardWidget(varargin)

SPARKLINECARDWIDGET Construct a SparklineCardWidget. Accepts name-value pairs for any public property.

Properties

Property Default Description
StaticValue [] Fixed scalar value displayed in the card
ValueFcn [] Function handle returning scalar or struct
Units '' Unit label appended to primary value
Format '%.1f' sprintf format string for primary value
NSparkPoints 50 Number of tail data points in sparkline
ShowDelta true Whether to show the delta indicator
DeltaFormat '%+.1f' sprintf format string for delta value
SparkColor [] Sparkline line color (empty = theme default)
SparkData [] Numeric vector for sparkline (alternative to Sensor)

Methods

render(obj, parentPanel)

RENDER Create all graphics objects inside parentPanel.

refresh(obj)

REFRESH Update displayed value, sparkline, and delta indicator.

t = getType(~)

GETTYPE Return widget type string.

s = toStruct(obj)

TOSTRUCT Serialize widget to a struct for JSON export.

Static Methods

SparklineCardWidget.obj = fromStruct(s)

FROMSTRUCT Deserialize a SparklineCardWidget from a struct.


StatusWidget --- Colored dot indicator with sensor value.

Inherits from: DashboardWidget

Sensor-first: w = StatusWidget('Sensor', sensorObj);

Threshold-bound (no Sensor required): w = StatusWidget('Title', 'Temp', 'Threshold', t, 'Value', 85); w = StatusWidget('Title', 'Temp', 'Threshold', 'temp_hi', 'ValueFcn', @getTemp);

Legacy (still supported): w = StatusWidget('Title', 'Pump 1', 'StatusFcn', @() 'ok');

Constructor

obj = StatusWidget(varargin)

Properties

Property Default Description
StatusFcn [] function_handle returning 'ok'/'warning'/'alarm' (legacy)
StaticStatus '' fixed status string (legacy)
Threshold [] Threshold object or registry key string (per D-01)
Value [] Scalar numeric value for threshold comparison (per D-03)
ValueFcn [] Function handle returning scalar value (per D-03, D-09)

Methods

render(obj, parentPanel)

refresh(obj)

t = getType(~)

lines = asciiRender(obj, width, height)

s = toStruct(obj)

Static Methods

StatusWidget.obj = fromStruct(s)


TableWidget --- Tabular data display using uitable.

Inherits from: DashboardWidget

w = TableWidget('Title', 'Sensor Data', 'DataFcn', @() getData()); w = TableWidget('Title', 'Static', 'Data', {{'A',1;'B',2}}, ... 'ColumnNames', {'Name','Value'}); w = TableWidget('Sensor', sensorObj); % last N data rows w = TableWidget('Sensor', sensorObj, 'Mode', 'events', 'EventStoreObj', store);

Constructor

obj = TableWidget(varargin)

Properties

Property Default Description
DataFcn []
Data {}
ColumnNames {}
Mode 'data' 'data' or 'events'
N 10 number of rows to display
EventStoreObj [] EventStore for event mode

Methods

render(obj, parentPanel)

refresh(obj)

t = getType(~)

lines = asciiRender(obj, width, height)

s = toStruct(obj)

Static Methods

TableWidget.obj = fromStruct(s)


TextWidget --- Static text label or section header.

Inherits from: DashboardWidget

w = TextWidget('Title', 'Section A', 'Content', 'Sensor overview');

Constructor

obj = TextWidget(varargin)

Properties

Property Default Description
Content '' body text
FontSize 0 0 = use theme default
Alignment 'left' 'left', 'center', 'right'

Methods

render(obj, parentPanel)

refresh(~)

Static widget — nothing to refresh

t = getType(~)

lines = asciiRender(obj, width, height)

s = toStruct(obj)

Static Methods

TextWidget.obj = fromStruct(s)


TimeRangeSelector --- Single-window time-range selector with data-preview envelope.

Inherits from: handle

selector = TimeRangeSelector(hPanel) attaches a time-range selector to a uipanel. The selector owns its own axes inside the panel and draws:

  * an (optional) aggregate min/max envelope patch behind the selection,
  * a semi-transparent selection rectangle that can be panned by dragging
    its middle and resized by dragging either of its two edge handles,
  * two line handles at the left and right edges of the selection window.

Interaction uses figure-level WindowButton{Down,Motion,Up}Fcn. Any previously installed callbacks are saved on construction and restored on delete().

Usage (the contract plan 03 uses to wire this into DashboardEngine):

  selector = TimeRangeSelector(hPanel, ...
      'OnRangeChanged', @(tStart, tEnd) onRangeChanged(tStart, tEnd), ...
      'Theme',          themeStruct);
  selector.setDataRange(tMin, tMax);        % full extent user can scrub
  selector.setSelection(tStart, tEnd);      % fires OnRangeChanged
  selector.setEnvelope(xC, yMin, yMax);     % optional preview
  [tS, tE] = selector.getSelection();
  delete(selector);                         % restores figure callbacks

Properties (public, configurable): OnRangeChanged Function handle @(tStart, tEnd). May be []. Theme Theme struct (or []). MinWidthFrac Minimum selection width as fraction of DataRange span. EdgeTolPx Pixel tolerance for edge hit-testing.

Properties (read-only, set internally): hPanel, hFigure, hAxes, hEnvelope, hSelection, hEdgeLeft, hEdgeRight hCurrentViewBoxes, hCurrentViewEdgesL, hCurrentViewEdgesR (Phase 1039: a POOL of visually-distinct "current view" boxes — one per out-of-sync graph — each coloured to match that graph's slider preview line; empty unless setCurrentViews is called. See DashboardEngine.updateCurrentViewIndicator_) DataRange 1x2 [tMin tMax]. Selection 1x2 [tStart tEnd]. CurrentViews Nx2 [tStart tEnd] rows (one per box), or [] when hidden. DragState 'idle' | 'panning' | 'resizeLeft' | 'resizeRight'.

Methods: setDataRange(tMin, tMax) Set full extent; rescales selection. setSelection(tStart, tEnd) Set/clamp/reorder selection; fires callback. getSelection() Return [tStart, tEnd]. setCurrentViews(ranges, colorIdxs) Show one non-interactive current-view box per row of ranges (Nx2), each coloured from the shared preview palette by colorIdxs. setCurrentView(tStart, tEnd) Back-compat single-box wrapper. hideCurrentView() Hide/clear all current-view boxes. setEnvelope(xC, yMin, yMax) Update or hide aggregate envelope. delete() Restore saved figure callbacks.

Compatible with MATLAB R2020b+ and Octave 7+ (D-11): uses only axes, patch, line, uipanel primitives and WindowButton{Down,Motion,Up}Fcn — no matlab.graphics.*, no uifigure/uiaxes, no addlistener on primitive properties.

Constructor

obj = TimeRangeSelector(hPanel, varargin)

TimeRangeSelector Construct a selector attached to a uipanel.

Properties

Property Default Description
OnRangeChanged [] function handle @(tStart, tEnd)
Theme [] struct from DashboardTheme, or []
MinWidthFrac 0.005 minimum selection width as fraction of DataRange span
EdgeTolPx 10 pixel tolerance for edge hit-test

Methods

setDataRange(obj, tMin, tMax)

setDataRange Set the full extent the user can scrub over. When the new range fully contains the current selection, the selection is preserved verbatim (its absolute time values stay put). This is the "live mode pan-freeze" path — every live tick extends the data range by ~1 s, and we do not want that to shift the user's selected window. Otherwise (range contraction or selection falls outside), the selection is rescaled proportionally to keep its relative position. Programmatic in either branch — does NOT fire OnRangeChanged; only user drag interactions do. (260512-live-mode-companion-adhoc-tail-spike)

setSelection(obj, tStart, tEnd)

setSelection Update the selection window, clamping and reordering. Swapped inputs (tStart > tEnd) are reordered. Values outside DataRange are clamped. Widths smaller than MinWidthFrac * span are widened around the requested midpoint. Fires OnRangeChanged with the final [tStart, tEnd] (if the callback is set). Reorder swapped bounds (tStart < tEnd).

[tStart, tEnd] = getSelection(obj)

getSelection Return the current selection as [tStart, tEnd].

setCurrentView(obj, tStart, tEnd)

setCurrentView Back-compat single-box wrapper around setCurrentViews. Phase 1039. Shows ONE current-view box at [tStart, tEnd] coloured with preview-palette index 1. Prefer setCurrentViews for the per-graph multi-box path. No-throw on bad/empty input.

setCurrentViews(obj, ranges, colorIdxs)

setCurrentViews Show ONE current-view box per out-of-sync graph (Phase 1039). ranges Nx2 [tStart tEnd] rows in data-time (a flat 1x2 is accepted for the single-box case). colorIdxs 1xN preview-line indices (1-based). Each box is coloured from the shared preview palette (previewPalette_) by its index, so a box matches its graph's slider preview line. Defaults to 1:N when omitted. Each range is clamped to DataRange and reordered if swapped. Boxes are purely indicative (PickableParts/HitTest off) — only the Selection is interactive. Empty ranges hide everything. No-throw before render / after the figure is destroyed (handle-guarded).

hideCurrentView(obj)

hideCurrentView Hide and clear ALL current-view boxes. Phase 1039. Clears CurrentViews and deletes every pooled box + edge-line handle. Safe to call before render / after delete.

setRangeLabels(obj, leftText, rightText, middleText)

setRangeLabels Update the date/time labels shown BELOW the slider. Updates three labels: leftText — slider's LEFT selection-edge time rightText — slider's RIGHT selection-edge time middleText — (optional) selection duration string, shown centered between the edge labels. Omit or pass '' to leave the middle blank.

setEnvelope(obj, xC, yMin, yMax)

setEnvelope (Legacy) Draw the aggregate min/max preview envelope. Kept for backward compat with tests. New code should prefer setPreviewLines for per-widget line previews.

setPreviewLines(obj, lines)

setPreviewLines Draw one downsampled line per widget preview. lines is a cell array of structs, each with fields x and y (equal-length row vectors; y already normalized to [0,1]). Each line is rendered with a distinct color from a fixed palette, placed behind the selection rectangle so drag interactions remain unaffected.

setEventMarkers(obj, times, colors)

setEventMarkers Draw a faint full-height line per event time.

setEventBands(obj, starts, ends, colors)

setEventBands Draw a translucent rectangle per event spanning start→end. setEventBands(starts, ends) clears any existing bands and draws one semi-transparent rectangle per event, spanning the start time to the end time. Non-finite values (NaN, ±Inf) are silently dropped.

setPlantLogMarkers(obj, times)

setPlantLogMarkers Draw a 1px full-opacity vertical line per plant-log entry time. Phase 1031 PLOG-VIZ-01/02/09. Parallel to setEventMarkers but uses SEPARATE storage (hPlantLogMarkers) so plant-log markers and the sev1/2/3 event markers can coexist without clobbering each other.

reinstallCallbacks(obj)

reinstallCallbacks Re-install the figure WindowButton* handlers. Public wrapper around the private installCallbacks_ used by DashboardEngine.rerenderWidgets to force the selector back to the OUTERMOST position on the figure's WindowButton handlers after a Reset. Required because rerenderWidgets tears down widget panels in install-order, and each per-widget HoverCrosshair.delete() unconditionally restores its saved PrevWBMFcn_ — when sibling HCs delete in install-order (not reverse), the chain leaves a dangling closure on the figure's WindowButtonMotionFcn whose ~isvalid guard silently no-ops every motion event, so the slider's onButtonMotion_ is never reached and bracket drag/resize freezes. (260512-egv)

Clone this wiki locally